1 /*-------------------------------------------------------------------------------
2 
3 	BARONY
4 	File: net.cpp
5 	Desc: support functions for networking
6 
7 	Copyright 2013-2016 (c) Turning Wheel LLC, all rights reserved.
8 	See LICENSE for details.
9 
10 -------------------------------------------------------------------------------*/
11 
12 #include "main.hpp"
13 #include "draw.hpp"
14 #include "game.hpp"
15 #include "stat.hpp"
16 #include "net.hpp"
17 #include "messages.hpp"
18 #include "entity.hpp"
19 #include "files.hpp"
20 #include "monster.hpp"
21 #include "interface/interface.hpp"
22 #include "magic/magic.hpp"
23 #include "sound.hpp"
24 #include "items.hpp"
25 #include "shops.hpp"
26 #include "menu.hpp"
27 #include "scores.hpp"
28 #include "collision.hpp"
29 #include "paths.hpp"
30 #ifdef STEAMWORKS
31 #include <steam/steam_api.h>
32 #include "steam.hpp"
33 #endif
34 #include "player.hpp"
35 #include "scores.hpp"
36 #include "colors.hpp"
37 #include "mod_tools.hpp"
38 #include "lobbies.hpp"
39 
40 NetHandler* net_handler = nullptr;
41 
42 char last_ip[64] = "";
43 char last_port[64] = "";
44 char lobbyChatbox[LOBBY_CHATBOX_LENGTH];
45 list_t lobbyChatboxMessages;
46 bool disableMultithreadedSteamNetworking = true;
47 bool disableFPSLimitOnNetworkMessages = false;
48 
49 // uncomment this to have the game log packet info
50 //#define PACKETINFO
51 
packetDeconstructor(void * data)52 void packetDeconstructor(void* data)
53 {
54 	packetsend_t* packetsend = (packetsend_t*)data;
55 	SDLNet_FreePacket(packetsend->packet);
56 	free(data);
57 }
58 
59 /*-------------------------------------------------------------------------------
60 
61 	sendPacket
62 
63 	when STEAMWORKS is undefined, works like SDLNet_UDP_Send and last argument
64 	is ignored. Otherwise, the first two arguments are ignored and the packet
65 	is sent with SteamNetworking()->SendP2PPacket, using the hostnum variable
66 	to get the steam ID of the same player number and the first two arguments
67 	are ignored.
68 
69 -------------------------------------------------------------------------------*/
70 
sendPacket(UDPsocket sock,int channel,UDPpacket * packet,int hostnum,bool tryReliable)71 int sendPacket(UDPsocket sock, int channel, UDPpacket* packet, int hostnum, bool tryReliable)
72 {
73 	if ( directConnect )
74 	{
75 		return SDLNet_UDP_Send(sock, channel, packet);
76 	}
77 	else
78 	{
79 		if ( LobbyHandler.getP2PType() == LobbyHandler_t::LobbyServiceType::LOBBY_STEAM )
80 		{
81 #ifdef STEAMWORKS
82 			if ( steamIDRemote[hostnum] && !client_disconnected[hostnum] )
83 			{
84 				return SteamNetworking()->SendP2PPacket(*static_cast<CSteamID* >(steamIDRemote[hostnum]), packet->data, packet->len, tryReliable? k_EP2PSendReliable : k_EP2PSendUnreliable, 0);
85 			}
86 			else
87 			{
88 				return 0;
89 			}
90 #endif
91 		}
92 		else if ( LobbyHandler.getP2PType() == LobbyHandler_t::LobbyServiceType::LOBBY_CROSSPLAY )
93 		{
94 #if defined USE_EOS
95 			EOS.SendMessageP2P(EOS.P2PConnectionInfo.getPeerIdFromIndex(hostnum), (char*)packet->data, packet->len);
96 			return 0;
97 #endif
98 		}
99 		return 0;
100 	}
101 }
102 
103 /*-------------------------------------------------------------------------------
104 
105 	sendPacketSafe
106 
107 	works like sendPacket, but adds an additional layer of insurance to
108 	increase the chance of a successful transmission. When STEAMWORKS is
109 	undefined, the game uses its own system to increase the transmission
110 	success rate of a packet. Otherwise it just sends the packet with
111 	SendP2PPacket's k_EP2PSendReliable flag
112 
113 -------------------------------------------------------------------------------*/
114 
115 Uint32 packetnum = 0;
sendPacketSafe(UDPsocket sock,int channel,UDPpacket * packet,int hostnum)116 int sendPacketSafe(UDPsocket sock, int channel, UDPpacket* packet, int hostnum)
117 {
118 	if ( hostnum < 0 )
119 	{
120 		printlog("[NET]: Error - attempt to send to negative hostnum: %d", hostnum);
121 		return 0;
122 	}
123 
124 	if ( !directConnect )
125 	{
126 		if ( LobbyHandler.getP2PType() == LobbyHandler_t::LobbyServiceType::LOBBY_STEAM )
127 		{
128 #ifdef STEAMWORKS
129 			if ( !steamIDRemote[hostnum] )
130 			{
131 				return 0;
132 			}
133 #endif
134 		}
135 		else if ( LobbyHandler.getP2PType() == LobbyHandler_t::LobbyServiceType::LOBBY_CROSSPLAY )
136 		{
137 #if defined USE_EOS
138 			if ( !EOS.P2PConnectionInfo.getPeerIdFromIndex(hostnum) )
139 			{
140 				return 0;
141 			}
142 #endif
143 		}
144 	}
145 
146 	packetsend_t* packetsend = (packetsend_t*) malloc(sizeof(packetsend_t));
147 	if (!(packetsend->packet = SDLNet_AllocPacket(NET_PACKET_SIZE)))
148 	{
149 		printlog("warning: packet allocation failed: %s\n", SDLNet_GetError());
150 		free(packetsend);
151 		return 0;
152 	}
153 
154 	packetsend->hostnum = hostnum;
155 	packetsend->sock = sock;
156 	packetsend->channel = channel;
157 	packetsend->packet->channel = channel;
158 	memcpy(packetsend->packet->data + 9, packet->data, NET_PACKET_SIZE - 9);
159 	packetsend->packet->len = packet->len + 9;
160 	packetsend->packet->address.host = packet->address.host;
161 	packetsend->packet->address.port = packet->address.port;
162 	strcpy((char*)packetsend->packet->data, "SAFE");
163 	if ( receivedclientnum || multiplayer != CLIENT )
164 	{
165 		packetsend->packet->data[4] = clientnum;
166 	}
167 	else
168 	{
169 		packetsend->packet->data[4] = MAXPLAYERS;
170 	}
171 	SDLNet_Write32(packetnum, &packetsend->packet->data[5]);
172 	packetsend->num = packetnum;
173 	packetsend->tries = 0;
174 	packetnum++;
175 
176 	node_t* node = list_AddNodeFirst(&safePacketsSent);
177 	node->element = packetsend;
178 	node->deconstructor = &packetDeconstructor;
179 
180 	if ( directConnect )
181 	{
182 		return SDLNet_UDP_Send(sock, channel, packetsend->packet);
183 	}
184 	else
185 	{
186 		if ( LobbyHandler.getP2PType() == LobbyHandler_t::LobbyServiceType::LOBBY_STEAM )
187 		{
188 #ifdef STEAMWORKS
189 			if ( steamIDRemote[hostnum] && !client_disconnected[hostnum] )
190 			{
191 				return SteamNetworking()->SendP2PPacket(*static_cast<CSteamID* >(steamIDRemote[hostnum]), packetsend->packet->data, packetsend->packet->len, k_EP2PSendReliable, 0);
192 			}
193 			else
194 			{
195 				return 0;
196 			}
197 #endif
198 		}
199 		else if ( LobbyHandler.getP2PType() == LobbyHandler_t::LobbyServiceType::LOBBY_CROSSPLAY )
200 		{
201 #if defined USE_EOS
202 			EOS.SendMessageP2P(EOS.P2PConnectionInfo.getPeerIdFromIndex(hostnum), packetsend->packet->data, packetsend->packet->len);
203 			return 0;
204 #endif
205 		}
206 		return 0;
207 	}
208 }
209 
210 /*-------------------------------------------------------------------------------
211 
212 	power
213 
214 	A simple power function designed to work only with integers.
215 
216 -------------------------------------------------------------------------------*/
217 
power(int a,int b)218 int power(int a, int b)
219 {
220 	int c, result = 1;
221 	for ( c = 0; c < b; c++ )
222 	{
223 		result *= a;
224 	}
225 	return result;
226 }
227 
228 /*-------------------------------------------------------------------------------
229 
230 	messagePlayer
231 
232 	Support function, messages the player number given by "player" with the
233 	message "message"
234 
235 -------------------------------------------------------------------------------*/
236 
messagePlayer(int player,char const * const message,...)237 void messagePlayer(int player, char const * const message, ...)
238 {
239 	if ( player < 0 || player >= MAXPLAYERS )
240 	{
241 		return;
242 	}
243 	char str[256] = { 0 };
244 
245 	va_list argptr;
246 	va_start( argptr, message );
247 	vsnprintf( str, 255, message, argptr );
248 	va_end( argptr );
249 
250 	messagePlayerColor(player, 0xFFFFFFFF, str);
251 }
252 
253 /*-------------------------------------------------------------------------------
254 
255 	messagePlayerColor
256 
257 	Messages the player number given by "player" with the message "message"
258 	and color "color"
259 
260 -------------------------------------------------------------------------------*/
261 
messagePlayerColor(int player,Uint32 color,char const * const message,...)262 void messagePlayerColor(int player, Uint32 color, char const * const message, ...)
263 {
264 	char str[ADD_MESSAGE_BUFFER_LENGTH] = { 0 };
265 	va_list argptr;
266 
267 	if ( message == NULL )
268 	{
269 		return;
270 	}
271 	if ( player < 0 || player >= MAXPLAYERS )
272 	{
273 		return;
274 	}
275 
276 	// format the content
277 	va_start( argptr, message );
278 	vsnprintf( str, 255, message, argptr );
279 	va_end( argptr );
280 
281 	// fixes crash when reading config at start of game
282 	if (!initialized)
283 	{
284 		printlog("%s\n", str);
285 		return;
286 	}
287 
288 	if ( player == clientnum )
289 	{
290 		printlog("%s\n", str);
291 		strncpy(str, messageSanitizePercentSign(str, nullptr).c_str(), ADD_MESSAGE_BUFFER_LENGTH - 1);
292 		newString(&messages, color, str);
293 		while ( list_Size(&messages) > MESSAGE_LIST_SIZE_CAP )
294 		{
295 			list_RemoveNode(messages.first);
296 		}
297 		if ( !disable_messages )
298 		{
299 			addMessage(color, str);
300 		}
301 	}
302 	else if ( multiplayer == SERVER && player > 0 )
303 	{
304 		strcpy((char*)net_packet->data, "MSGS");
305 		SDLNet_Write32(color, &net_packet->data[4]);
306 		strcpy((char*)(&net_packet->data[8]), str);
307 		net_packet->address.host = net_clients[player - 1].host;
308 		net_packet->address.port = net_clients[player - 1].port;
309 		net_packet->len = 8 + strlen(str) + 1;
310 		sendPacketSafe(net_sock, -1, net_packet, player - 1);
311 	}
312 
313 	int c;
314 	char tempstr[256];
315 	for ( c = 0; c < MAXPLAYERS; c++ )
316 	{
317 		if ( client_disconnected[c] )
318 		{
319 			continue;
320 		}
321 		snprintf(tempstr, 256, language[697], stats[c]->name);
322 		if ( !strcmp(str, tempstr) )
323 		{
324 			steamAchievementClient(player, "BARONY_ACH_NOT_A_TEAM_PLAYER");
325 		}
326 	}
327 }
328 
329 /*-------------------------------------------------------------------------------
330 
331 	sendEntityUDP / sendEntityTCP
332 
333 	Updates given entity data for given client. Server -> client functions
334 
335 -------------------------------------------------------------------------------*/
336 
sendEntityTCP(Entity * entity,int c)337 void sendEntityTCP(Entity* entity, int c)
338 {
339 	int j;
340 
341 	if ( entity == NULL )
342 	{
343 		return;
344 	}
345 	if ( client_disconnected[c] == true )
346 	{
347 		return;
348 	}
349 
350 	// send entity data to the client
351 	strcpy((char*)net_packet->data, "ENTU");
352 	SDLNet_Write32((Uint32)entity->getUID(), &net_packet->data[4]);
353 	SDLNet_Write16((Uint16)entity->sprite, &net_packet->data[8]);
354 	SDLNet_Write16((Sint16)(entity->x * 32), &net_packet->data[10]);
355 	SDLNet_Write16((Sint16)(entity->y * 32), &net_packet->data[12]);
356 	SDLNet_Write16((Sint16)(entity->z * 32), &net_packet->data[14]);
357 	net_packet->data[16] = (Sint8)entity->sizex;
358 	net_packet->data[17] = (Sint8)entity->sizey;
359 	net_packet->data[18] = (Uint8)(entity->scalex * 128);
360 	net_packet->data[19] = (Uint8)(entity->scaley * 128);
361 	net_packet->data[20] = (Uint8)(entity->scalez * 128);
362 	SDLNet_Write16((Sint16)(entity->yaw * 256), &net_packet->data[21]);
363 	SDLNet_Write16((Sint16)(entity->pitch * 256), &net_packet->data[23]);
364 	SDLNet_Write16((Sint16)(entity->roll * 256), &net_packet->data[25]);
365 	net_packet->data[27] = (char)(entity->focalx * 8);
366 	net_packet->data[28] = (char)(entity->focaly * 8);
367 	net_packet->data[29] = (char)(entity->focalz * 8);
368 	SDLNet_Write32(entity->skill[2], &net_packet->data[30]);
369 	net_packet->data[34] = 0;
370 	net_packet->data[35] = 0;
371 	for (j = 0; j < 16; j++)
372 	{
373 		if ( entity->flags[j] )
374 		{
375 			net_packet->data[34 + j / 8] |= power(2, j - (j / 8) * 8);
376 		}
377 	}
378 	SDLNet_Write32((Uint32)ticks, &net_packet->data[36]);
379 	SDLNet_Write16((Sint16)(entity->vel_x * 32), &net_packet->data[40]);
380 	SDLNet_Write16((Sint16)(entity->vel_y * 32), &net_packet->data[42]);
381 	SDLNet_Write16((Sint16)(entity->vel_z * 32), &net_packet->data[44]);
382 	net_packet->address.host = net_clients[c - 1].host;
383 	net_packet->address.port = net_clients[c - 1].port;
384 	net_packet->len = ENTITY_PACKET_LENGTH;
385 	if ( directConnect )
386 	{
387 		SDLNet_TCP_Send(net_tcpclients[c - 1], net_packet->data, net_packet->len);
388 	}
389 	else
390 	{
391 #ifdef STEAMWORKS
392 		SteamNetworking()->SendP2PPacket(*static_cast<CSteamID* >(steamIDRemote[c - 1]), net_packet->data, net_packet->len, k_EP2PSendReliable, 0);
393 #endif
394 	}
395 	SDL_Delay(50);
396 	if ( entity->clientsHaveItsStats )
397 	{
398 		entity->serverUpdateEffectsForEntity(false);
399 	}
400 }
401 
sendEntityUDP(Entity * entity,int c,bool guarantee)402 void sendEntityUDP(Entity* entity, int c, bool guarantee)
403 {
404 	int j;
405 
406 	if ( entity == NULL )
407 	{
408 		return;
409 	}
410 	if ( client_disconnected[c] == true )
411 	{
412 		return;
413 	}
414 	if ( c <= 0 )
415 	{
416 		return;
417 	}
418 
419 	// send entity data to the client
420 	strcpy((char*)net_packet->data, "ENTU");
421 	SDLNet_Write32((Uint32)entity->getUID(), &net_packet->data[4]);
422 	SDLNet_Write16((Uint16)entity->sprite, &net_packet->data[8]);
423 	SDLNet_Write16((Sint16)(entity->x * 32), &net_packet->data[10]);
424 	SDLNet_Write16((Sint16)(entity->y * 32), &net_packet->data[12]);
425 	SDLNet_Write16((Sint16)(entity->z * 32), &net_packet->data[14]);
426 	net_packet->data[16] = (Sint8)entity->sizex;
427 	net_packet->data[17] = (Sint8)entity->sizey;
428 	net_packet->data[18] = (Uint8)(entity->scalex * 128);
429 	net_packet->data[19] = (Uint8)(entity->scaley * 128);
430 	net_packet->data[20] = (Uint8)(entity->scalez * 128);
431 	SDLNet_Write16((Sint16)(entity->yaw * 256), &net_packet->data[21]);
432 	SDLNet_Write16((Sint16)(entity->pitch * 256), &net_packet->data[23]);
433 	SDLNet_Write16((Sint16)(entity->roll * 256), &net_packet->data[25]);
434 	net_packet->data[27] = (char)(entity->focalx * 8);
435 	net_packet->data[28] = (char)(entity->focaly * 8);
436 	net_packet->data[29] = (char)(entity->focalz * 8);
437 	SDLNet_Write32(entity->skill[2], &net_packet->data[30]);
438 	net_packet->data[34] = 0;
439 	net_packet->data[35] = 0;
440 	for (j = 0; j < 16; j++)
441 	{
442 		if ( entity->flags[j] )
443 		{
444 			net_packet->data[34 + j / 8] |= power(2, j - (j / 8) * 8);
445 		}
446 	}
447 	SDLNet_Write32((Uint32)ticks, &net_packet->data[36]);
448 	SDLNet_Write16((Sint16)(entity->vel_x * 32), &net_packet->data[40]);
449 	SDLNet_Write16((Sint16)(entity->vel_y * 32), &net_packet->data[42]);
450 	SDLNet_Write16((Sint16)(entity->vel_z * 32), &net_packet->data[44]);
451 	net_packet->address.host = net_clients[c - 1].host;
452 	net_packet->address.port = net_clients[c - 1].port;
453 	net_packet->len = ENTITY_PACKET_LENGTH;
454 
455 	// sometimes you want more insurance that the entity update arrives
456 	if ( guarantee )
457 	{
458 		sendPacketSafe(net_sock, -1, net_packet, c - 1);
459 	}
460 	else
461 	{
462 		sendPacket(net_sock, -1, net_packet, c - 1);
463 	}
464 	if ( entity->clientsHaveItsStats )
465 	{
466 		entity->serverUpdateEffectsForEntity(false);
467 	}
468 }
469 
470 /*-------------------------------------------------------------------------------
471 
472 	sendMapSeedTCP
473 
474 	Sends the seed necessary to generate the next map
475 
476 -------------------------------------------------------------------------------*/
477 
sendMapSeedTCP(int c)478 void sendMapSeedTCP(int c)
479 {
480 	if ( client_disconnected[c] == true )
481 	{
482 		return;
483 	}
484 
485 	SDLNet_Write32(mapseed, &net_packet->data[0]);
486 	net_packet->address.host = net_clients[c - 1].host;
487 	net_packet->address.port = net_clients[c - 1].port;
488 	net_packet->len = 4;
489 	if ( directConnect )
490 	{
491 		SDLNet_TCP_Send(net_tcpclients[c - 1], net_packet->data, net_packet->len);
492 	}
493 	else
494 	{
495 #ifdef STEAMWORKS
496 		SteamNetworking()->SendP2PPacket(*static_cast<CSteamID* >(steamIDRemote[c - 1 ]), net_packet->data, net_packet->len, k_EP2PSendReliable, 0);
497 #endif
498 	}
499 	SDL_Delay(50);
500 }
501 
502 /*-------------------------------------------------------------------------------
503 
504 	sendMapTCP
505 
506 	Sends all map data to clients
507 
508 -------------------------------------------------------------------------------*/
509 
sendMapTCP(int c)510 void sendMapTCP(int c)
511 {
512 	Uint32 x, y, z;
513 
514 	if ( client_disconnected[c] == true )
515 	{
516 		return;
517 	}
518 
519 	// send all the map data to the client
520 	for ( z = 0; z < MAPLAYERS; z++ )
521 	{
522 		for ( y = 0; y < map.height; y++ )
523 		{
524 			for ( x = 0; x < map.width; x++ )
525 			{
526 				net_packet->data[x] = map.tiles[z + y * MAPLAYERS + x * MAPLAYERS * map.height];
527 			}
528 			net_packet->address.host = net_clients[c - 1].host;
529 			net_packet->address.port = net_clients[c - 1].port;
530 			net_packet->len = map.width;
531 			if ( directConnect )
532 			{
533 				SDLNet_TCP_Send(net_tcpclients[c - 1], net_packet->data, net_packet->len);
534 			}
535 			else
536 			{
537 #ifdef STEAMWORKS
538 				SteamNetworking()->SendP2PPacket(*static_cast<CSteamID* >(steamIDRemote[c - 1]), net_packet->data, net_packet->len, k_EP2PSendReliable, 0);
539 #endif
540 			}
541 			SDL_Delay(50);
542 		}
543 	}
544 }
545 
546 /*-------------------------------------------------------------------------------
547 
548 	serverUpdateBodypartIDs
549 
550 	Updates the uid numbers of all the given bodyparts for the given entity
551 
552 -------------------------------------------------------------------------------*/
553 
serverUpdateBodypartIDs(Entity * entity)554 void serverUpdateBodypartIDs(Entity* entity)
555 {
556 	int c;
557 	if ( multiplayer != SERVER )
558 	{
559 		return;
560 	}
561 	for ( c = 1; c < MAXPLAYERS; c++ )
562 	{
563 		if ( !client_disconnected[c] )
564 		{
565 			strcpy((char*)net_packet->data, "BDYI");
566 			SDLNet_Write32(entity->getUID(), &net_packet->data[4]);
567 			node_t* node;
568 			int i;
569 			for ( i = 0, node = entity->children.first; node != NULL; node = node->next, i++ )
570 			{
571 				if ( i < 1 || (i < 2 && entity->behavior == &actMonster) )
572 				{
573 					continue;
574 				}
575 				Entity* tempEntity = (Entity*)node->element;
576 				if ( entity->behavior == &actMonster )
577 				{
578 					SDLNet_Write32(tempEntity->getUID(), &net_packet->data[8 + 4 * (i - 2)]);
579 				}
580 				else
581 				{
582 					SDLNet_Write32(tempEntity->getUID(), &net_packet->data[8 + 4 * (i - 1)]);
583 				}
584 			}
585 			net_packet->address.host = net_clients[c - 1].host;
586 			net_packet->address.port = net_clients[c - 1].port;
587 			net_packet->len = 8 + (list_Size(&entity->children) - 2) * 4;
588 			sendPacketSafe(net_sock, -1, net_packet, c - 1);
589 		}
590 	}
591 }
592 
593 /*-------------------------------------------------------------------------------
594 
595 	serverUpdateEntityBodypart
596 
597 	Updates the given bodypart of the given entity for all clients
598 
599 -------------------------------------------------------------------------------*/
600 
601 //int numPlayerBodypartUpdates = 0;
602 //int numMonsterBodypartUpdates = 0;
603 //Uint32 lastbodypartTick = 0;
604 
serverUpdateEntityBodypart(Entity * entity,int bodypart)605 void serverUpdateEntityBodypart(Entity* entity, int bodypart)
606 {
607 	int c;
608 	if ( multiplayer != SERVER )
609 	{
610 		return;
611 	}
612 	for ( c = 1; c < MAXPLAYERS; c++ )
613 	{
614 		if ( !client_disconnected[c] )
615 		{
616 			strcpy((char*)net_packet->data, "ENTB");
617 			SDLNet_Write32(entity->getUID(), &net_packet->data[4]);
618 			net_packet->data[8] = bodypart;
619 			node_t* node = list_Node(&entity->children, bodypart);
620 			if ( node )
621 			{
622 				Entity* tempEntity = (Entity*)node->element;
623 				SDLNet_Write32(tempEntity->sprite, &net_packet->data[9]);
624 				net_packet->data[13] = tempEntity->flags[INVISIBLE];
625 				net_packet->address.host = net_clients[c - 1].host;
626 				net_packet->address.port = net_clients[c - 1].port;
627 				net_packet->len = 14;
628 				sendPacketSafe(net_sock, -1, net_packet, c - 1);
629 			}
630 		}
631 	}
632 	//if ( entity->behavior == &actPlayer )
633 	//{
634 	//	++numPlayerBodypartUpdates;
635 	//}
636 	//else if ( entity->behavior == &actMonster )
637 	//{
638 	//	++numMonsterBodypartUpdates;
639 	//}
640 	//if ( lastbodypartTick == 0 )
641 	//{
642 	//	lastbodypartTick = ticks;
643 	//}
644 	//if ( ticks - lastbodypartTick >= 250 )
645 	//{
646 	//	messagePlayer(0, "Bodypart updates (%ds) players: %d, monster: %d", (ticks - lastbodypartTick) / 50, numPlayerBodypartUpdates, numMonsterBodypartUpdates);
647 	//	lastbodypartTick = 0;
648 	//	numMonsterBodypartUpdates = 0;
649 	//	numPlayerBodypartUpdates = 0;
650 	//}
651 }
652 
653 /*-------------------------------------------------------------------------------
654 
655 	serverUpdateEntitySprite
656 
657 	Updates the given entity's sprite for all clients
658 
659 -------------------------------------------------------------------------------*/
660 
serverUpdateEntitySprite(Entity * entity)661 void serverUpdateEntitySprite(Entity* entity)
662 {
663 	int c;
664 	if ( multiplayer != SERVER )
665 	{
666 		return;
667 	}
668 	for ( c = 1; c < MAXPLAYERS; c++ )
669 	{
670 		if ( !client_disconnected[c] )
671 		{
672 			strcpy((char*)net_packet->data, "ENTA");
673 			SDLNet_Write32(entity->getUID(), &net_packet->data[4]);
674 			SDLNet_Write32(entity->sprite, &net_packet->data[8]);
675 			net_packet->address.host = net_clients[c - 1].host;
676 			net_packet->address.port = net_clients[c - 1].port;
677 			net_packet->len = 12;
678 			sendPacketSafe(net_sock, -1, net_packet, c - 1);
679 		}
680 	}
681 }
682 
683 /*-------------------------------------------------------------------------------
684 
685 	serverUpdateEntitySkill
686 
687 	Updates a specific entity skill for all clients
688 
689 -------------------------------------------------------------------------------*/
690 
serverUpdateEntitySkill(Entity * entity,int skill)691 void serverUpdateEntitySkill(Entity* entity, int skill)
692 {
693 	int c;
694 	if ( multiplayer != SERVER )
695 	{
696 		return;
697 	}
698 	for ( c = 1; c < MAXPLAYERS; c++ )
699 	{
700 		if ( !client_disconnected[c] )
701 		{
702 			strcpy((char*)net_packet->data, "ENTS");
703 			SDLNet_Write32(entity->getUID(), &net_packet->data[4]);
704 			net_packet->data[8] = skill;
705 			SDLNet_Write32(entity->skill[skill], &net_packet->data[9]);
706 			net_packet->address.host = net_clients[c - 1].host;
707 			net_packet->address.port = net_clients[c - 1].port;
708 			net_packet->len = 13;
709 			sendPacketSafe(net_sock, -1, net_packet, c - 1);
710 		}
711 	}
712 }
713 
714 /*-------------------------------------------------------------------------------
715 
716 serverUpdateEntitySkill
717 
718 Updates a specific entity skill for all clients
719 
720 -------------------------------------------------------------------------------*/
721 
serverUpdateEntityStatFlag(Entity * entity,int flag)722 void serverUpdateEntityStatFlag(Entity* entity, int flag)
723 {
724 	int c;
725 	if ( multiplayer != SERVER )
726 	{
727 		return;
728 	}
729 	if ( !entity->getStats() )
730 	{
731 		return;
732 	}
733 	for ( c = 1; c < MAXPLAYERS; c++ )
734 	{
735 		if ( !client_disconnected[c] )
736 		{
737 			strcpy((char*)net_packet->data, "ENSF");
738 			SDLNet_Write32(entity->getUID(), &net_packet->data[4]);
739 			net_packet->data[8] = flag;
740 			SDLNet_Write32(entity->getStats()->MISC_FLAGS[flag], &net_packet->data[9]);
741 			net_packet->address.host = net_clients[c - 1].host;
742 			net_packet->address.port = net_clients[c - 1].port;
743 			net_packet->len = 13;
744 			sendPacketSafe(net_sock, -1, net_packet, c - 1);
745 		}
746 	}
747 }
748 
749 /*-------------------------------------------------------------------------------
750 
751 serverUpdateEntityFSkill
752 
753 Updates a specific entity fskill for all clients
754 
755 -------------------------------------------------------------------------------*/
756 
serverUpdateEntityFSkill(Entity * entity,int fskill)757 void serverUpdateEntityFSkill(Entity* entity, int fskill)
758 {
759 	int c;
760 	if ( multiplayer != SERVER )
761 	{
762 		return;
763 	}
764 	for ( c = 1; c < MAXPLAYERS; c++ )
765 	{
766 		if ( !client_disconnected[c] )
767 		{
768 			strcpy((char*)net_packet->data, "ENFS");
769 			SDLNet_Write32(entity->getUID(), &net_packet->data[4]);
770 			net_packet->data[8] = fskill;
771 			SDLNet_Write16(static_cast<Sint16>(entity->fskill[fskill] * 256), &net_packet->data[9]);
772 			net_packet->address.host = net_clients[c - 1].host;
773 			net_packet->address.port = net_clients[c - 1].port;
774 			net_packet->len = 11;
775 			sendPacketSafe(net_sock, -1, net_packet, c - 1);
776 		}
777 	}
778 }
779 
780 /*-------------------------------------------------------------------------------
781 
782 serverSpawnMiscParticles
783 
784 Spawns misc particle effects for all clients
785 
786 -------------------------------------------------------------------------------*/
787 
serverSpawnMiscParticles(Entity * entity,int particleType,int particleSprite,Uint32 optionalUid)788 void serverSpawnMiscParticles(Entity* entity, int particleType, int particleSprite, Uint32 optionalUid)
789 {
790 	int c;
791 	if ( multiplayer != SERVER )
792 	{
793 		return;
794 	}
795 	for ( c = 1; c < MAXPLAYERS; c++ )
796 	{
797 		if ( !client_disconnected[c] )
798 		{
799 			strcpy((char*)net_packet->data, "SPPE");
800 			SDLNet_Write32(entity->getUID(), &net_packet->data[4]);
801 			net_packet->data[8] = particleType;
802 			SDLNet_Write16(particleSprite, &net_packet->data[9]);
803 			SDLNet_Write32(optionalUid, &net_packet->data[11]);
804 			net_packet->address.host = net_clients[c - 1].host;
805 			net_packet->address.port = net_clients[c - 1].port;
806 			net_packet->len = 16;
807 			sendPacketSafe(net_sock, -1, net_packet, c - 1);
808 		}
809 	}
810 }
811 
812 /*-------------------------------------------------------------------------------
813 
814 serverSpawnMiscParticlesAtLocation
815 
816 Spawns misc particle effects for all clients at given coordinates.
817 
818 -------------------------------------------------------------------------------*/
819 
serverSpawnMiscParticlesAtLocation(Sint16 x,Sint16 y,Sint16 z,int particleType,int particleSprite)820 void serverSpawnMiscParticlesAtLocation(Sint16 x, Sint16 y, Sint16 z, int particleType, int particleSprite)
821 {
822 	int c;
823 	if ( multiplayer != SERVER )
824 	{
825 		return;
826 	}
827 	for ( c = 1; c < MAXPLAYERS; c++ )
828 	{
829 		if ( !client_disconnected[c] )
830 		{
831 			strcpy((char*)net_packet->data, "SPPL");
832 			SDLNet_Write16(x, &net_packet->data[4]);
833 			SDLNet_Write16(y, &net_packet->data[6]);
834 			SDLNet_Write16(z, &net_packet->data[8]);
835 			net_packet->data[10] = particleType;
836 			SDLNet_Write16(particleSprite, &net_packet->data[11]);
837 			net_packet->address.host = net_clients[c - 1].host;
838 			net_packet->address.port = net_clients[c - 1].port;
839 			net_packet->len = 14;
840 			sendPacketSafe(net_sock, -1, net_packet, c - 1);
841 		}
842 	}
843 }
844 
845 /*-------------------------------------------------------------------------------
846 
847 	serverUpdateEntityFlag
848 
849 	Updates a specific entity flag for all clients
850 
851 -------------------------------------------------------------------------------*/
852 
serverUpdateEntityFlag(Entity * entity,int flag)853 void serverUpdateEntityFlag(Entity* entity, int flag)
854 {
855 	int c;
856 	if ( multiplayer != SERVER )
857 	{
858 		return;
859 	}
860 	for ( c = 1; c < MAXPLAYERS; c++ )
861 	{
862 		if ( !client_disconnected[c] )
863 		{
864 			strcpy((char*)net_packet->data, "ENTF");
865 			SDLNet_Write32(entity->getUID(), &net_packet->data[4]);
866 			net_packet->data[8] = flag;
867 			net_packet->data[9] = entity->flags[flag];
868 			net_packet->address.host = net_clients[c - 1].host;
869 			net_packet->address.port = net_clients[c - 1].port;
870 			net_packet->len = 10;
871 			sendPacketSafe(net_sock, -1, net_packet, c - 1);
872 		}
873 	}
874 }
875 
876 /*-------------------------------------------------------------------------------
877 
878 	serverUpdateEffects
879 
880 	Updates the status of the EFFECTS variables (blindness, drunkenness, etc.)
881 	for the specified client.
882 
883 -------------------------------------------------------------------------------*/
884 
serverUpdateEffects(int player)885 void serverUpdateEffects(int player)
886 {
887 	int j;
888 
889 	if ( multiplayer != SERVER || clientnum == player )
890 	{
891 		return;
892 	}
893 	if ( player <= 0 || player >= MAXPLAYERS )
894 	{
895 		return;
896 	}
897 	if ( client_disconnected[player] == true )
898 	{
899 		return;
900 	}
901 
902 	strcpy((char*)net_packet->data, "UPEF");
903 	net_packet->data[4] = 0;
904 	net_packet->data[5] = 0;
905 	net_packet->data[6] = 0;
906 	net_packet->data[7] = 0;
907 	net_packet->data[8] = 0;
908 	net_packet->data[9] = 0;
909 	net_packet->data[10] = 0;
910 	net_packet->data[11] = 0;
911 	net_packet->data[12] = 0;
912 	net_packet->data[13] = 0;
913 	for (j = 0; j < NUMEFFECTS; j++)
914 	{
915 		if ( stats[player]->EFFECTS[j] == true )
916 		{
917 			net_packet->data[4 + j / 8] |= power(2, j - (j / 8) * 8);
918 		}
919 		if ( stats[player]->EFFECTS_TIMERS[j] < TICKS_PER_SECOND * 5 && stats[player]->EFFECTS_TIMERS[j] > 0 )
920 		{
921 			// use these bits to denote if duration is low.
922 			net_packet->data[9 + j / 8] |= power(2, j - (j / 8) * 8);
923 		}
924 	}
925 	net_packet->address.host = net_clients[player - 1].host;
926 	net_packet->address.port = net_clients[player - 1].port;
927 	net_packet->len = 14;
928 	sendPacketSafe(net_sock, -1, net_packet, player - 1);
929 }
930 
931 /*-------------------------------------------------------------------------------
932 
933 	serverUpdateHunger
934 
935 	Updates the HUNGER variable for the specified client
936 
937 -------------------------------------------------------------------------------*/
938 
serverUpdateHunger(int player)939 void serverUpdateHunger(int player)
940 {
941 	if ( multiplayer != SERVER || clientnum == player )
942 	{
943 		return;
944 	}
945 	if ( player <= 0 || player >= MAXPLAYERS )
946 	{
947 		return;
948 	}
949 	if ( client_disconnected[player] == true )
950 	{
951 		return;
952 	}
953 
954 	strcpy((char*)net_packet->data, "HNGR");
955 	SDLNet_Write32(stats[player]->HUNGER, &net_packet->data[4]);
956 	net_packet->address.host = net_clients[player - 1].host;
957 	net_packet->address.port = net_clients[player - 1].port;
958 	net_packet->len = 8;
959 	sendPacketSafe(net_sock, -1, net_packet, player - 1);
960 }
961 
962 /*-------------------------------------------------------------------------------
963 
964 serverUpdatePlayerStats
965 
966 Updates all player current HP/MP for clients
967 
968 -------------------------------------------------------------------------------*/
969 
serverUpdatePlayerStats()970 void serverUpdatePlayerStats()
971 {
972 	int c;
973 	if ( multiplayer != SERVER )
974 	{
975 		return;
976 	}
977 	for ( c = 1; c < MAXPLAYERS; c++ )
978 	{
979 		if ( !client_disconnected[c] )
980 		{
981 			strcpy((char*)net_packet->data, "STAT");
982 			Sint32 playerHP = 0;
983 			Sint32 playerMP = 0;
984 			for ( int i = 0; i < MAXPLAYERS; ++i )
985 			{
986 				if ( stats[i] )
987 				{
988 					playerHP = static_cast<Sint16>(stats[i]->MAXHP);
989 					playerHP |= static_cast<Sint16>(stats[i]->HP) << 16;
990 					playerMP = static_cast<Sint16>(stats[i]->MAXMP);
991 					playerMP |= static_cast<Sint16>(stats[i]->MP) << 16;
992 				}
993 				SDLNet_Write32(playerHP, &net_packet->data[4 + i * 8]); // 4/12/20/28 data
994 				SDLNet_Write32(playerMP, &net_packet->data[8 + i * 8]); // 8/16/24/32 data
995 				playerHP = 0;
996 				playerMP = 0;
997 			}
998 			net_packet->address.host = net_clients[c - 1].host;
999 			net_packet->address.port = net_clients[c - 1].port;
1000 			net_packet->len = 4 + 8 * MAXPLAYERS;
1001 			sendPacketSafe(net_sock, -1, net_packet, c - 1);
1002 		}
1003 	}
1004 }
1005 
1006 /*-------------------------------------------------------------------------------
1007 
1008 serverUpdatePlayerGameplayStats
1009 
1010 Updates given players gameplayStatistics value by given increment.
1011 
1012 -------------------------------------------------------------------------------*/
serverUpdatePlayerGameplayStats(int player,int gameplayStat,int changeval)1013 void serverUpdatePlayerGameplayStats(int player, int gameplayStat, int changeval)
1014 {
1015 	if ( player < 0 || player >= MAXPLAYERS )
1016 	{
1017 		return;
1018 	}
1019 	if ( client_disconnected[player] )
1020 	{
1021 		return;
1022 	}
1023 	if ( player == 0 )
1024 	{
1025 		if ( gameplayStat == STATISTICS_TEMPT_FATE )
1026 		{
1027 			if ( gameStatistics[STATISTICS_TEMPT_FATE] == -1 )
1028 			{
1029 				// don't change, completed task.
1030 			}
1031 			else
1032 			{
1033 				if ( changeval == 5 )
1034 				{
1035 					gameStatistics[gameplayStat] = changeval;
1036 				}
1037 				else if ( changeval == 1 && gameStatistics[gameplayStat] > 0 )
1038 				{
1039 					gameStatistics[gameplayStat] = -1;
1040 				}
1041 			}
1042 		}
1043 		else if ( gameplayStat == STATISTICS_FORUM_TROLL )
1044 		{
1045 			if ( changeval == AchievementObserver::FORUM_TROLL_BREAK_WALL )
1046 			{
1047 				int walls = gameStatistics[gameplayStat] & 0xFF;
1048 				walls = std::min(walls + 1, 3);
1049 				gameStatistics[gameplayStat] = gameStatistics[gameplayStat] & 0xFFFFFF00;
1050 				gameStatistics[gameplayStat] |= walls;
1051 			}
1052 			else if ( changeval == AchievementObserver::FORUM_TROLL_RECRUIT_TROLL )
1053 			{
1054 				int trolls = (gameStatistics[gameplayStat] >> 8) & 0xFF;
1055 				trolls = std::min(trolls + 1, 3);
1056 				gameStatistics[gameplayStat] = gameStatistics[gameplayStat] & 0xFFFF00FF;
1057 				gameStatistics[gameplayStat] |= (trolls << 8);
1058 			}
1059 			else if ( changeval == AchievementObserver::FORUM_TROLL_FEAR )
1060 			{
1061 				int fears = (gameStatistics[gameplayStat] >> 16) & 0xFF;
1062 				fears = std::min(fears + 1, 3);
1063 				gameStatistics[gameplayStat] = gameStatistics[gameplayStat] & 0xFF00FFFF;
1064 				gameStatistics[gameplayStat] |= (fears << 16);
1065 			}
1066 		}
1067 		else if ( gameplayStat == STATISTICS_POP_QUIZ_1 || gameplayStat == STATISTICS_POP_QUIZ_2 )
1068 		{
1069 			int spellID = changeval;
1070 			if ( spellID >= 30 )
1071 			{
1072 				spellID -= 30;
1073 				int shifted = (1 << spellID);
1074 				gameStatistics[gameplayStat] |= shifted;
1075 			}
1076 			else
1077 			{
1078 				int shifted = (1 << spellID);
1079 				gameStatistics[gameplayStat] |= shifted;
1080 			}
1081 		}
1082 		else
1083 		{
1084 			gameStatistics[gameplayStat] += changeval;
1085 		}
1086 	}
1087 	else
1088 	{
1089 		strcpy((char*)net_packet->data, "GPST");
1090 		SDLNet_Write32(gameplayStat, &net_packet->data[4]);
1091 		SDLNet_Write32(changeval, &net_packet->data[8]);
1092 		net_packet->address.host = net_clients[player - 1].host;
1093 		net_packet->address.port = net_clients[player - 1].port;
1094 		net_packet->len = 12;
1095 		sendPacketSafe(net_sock, -1, net_packet, player - 1);
1096 	}
1097 	//messagePlayer(clientnum, "[DEBUG]: sent: %d, %d: val %d", gameplayStat, changeval, gameStatistics[gameplayStat]);
1098 }
1099 
serverUpdatePlayerConduct(int player,int conduct,int value)1100 void serverUpdatePlayerConduct(int player, int conduct, int value)
1101 {
1102 	if ( player <= 0 || player >= MAXPLAYERS )
1103 	{
1104 		return;
1105 	}
1106 	if ( client_disconnected[player] )
1107 	{
1108 		return;
1109 	}
1110 	strcpy((char*)net_packet->data, "COND");
1111 	SDLNet_Write16(conduct, &net_packet->data[4]);
1112 	SDLNet_Write16(value, &net_packet->data[6]);
1113 	net_packet->address.host = net_clients[player - 1].host;
1114 	net_packet->address.port = net_clients[player - 1].port;
1115 	net_packet->len = 8;
1116 	sendPacketSafe(net_sock, -1, net_packet, player - 1);
1117 }
1118 
1119 /*-------------------------------------------------------------------------------
1120 
1121 serverUpdatePlayerLVL
1122 
1123 Updates all player current LVL for clients
1124 
1125 -------------------------------------------------------------------------------*/
1126 
serverUpdatePlayerLVL()1127 void serverUpdatePlayerLVL()
1128 {
1129 	int c;
1130 	if ( multiplayer != SERVER )
1131 	{
1132 		return;
1133 	}
1134 	for ( c = 1; c < MAXPLAYERS; c++ )
1135 	{
1136 		if ( !client_disconnected[c] )
1137 		{
1138 			strcpy((char*)net_packet->data, "UPLV");
1139 			Sint32 playerLevels = 0;
1140 			for ( int i = 0; i < MAXPLAYERS; ++i )
1141 			{
1142 				if ( stats[i] )
1143 				{
1144 					playerLevels |= static_cast<Uint8>(stats[i]->LVL) << (8 * i); // store uint8 in data, highest bits for player 4.
1145 				}
1146 			}
1147 			SDLNet_Write32(playerLevels, &net_packet->data[4]);
1148 			net_packet->address.host = net_clients[c - 1].host;
1149 			net_packet->address.port = net_clients[c - 1].port;
1150 			net_packet->len = 8;
1151 			sendPacketSafe(net_sock, -1, net_packet, c - 1);
1152 		}
1153 	}
1154 }
1155 
serverRemoveClientFollower(int player,Uint32 uidToRemove)1156 void serverRemoveClientFollower(int player, Uint32 uidToRemove)
1157 {
1158 	if ( multiplayer != SERVER || player <= 0 )
1159 	{
1160 		return;
1161 	}
1162 
1163 	if ( !client_disconnected[player] )
1164 	{
1165 		strcpy((char*)net_packet->data, "LDEL");
1166 		SDLNet_Write32(uidToRemove, &net_packet->data[4]);
1167 		net_packet->address.host = net_clients[player - 1].host;
1168 		net_packet->address.port = net_clients[player - 1].port;
1169 		net_packet->len = 8;
1170 		sendPacketSafe(net_sock, -1, net_packet, player - 1);
1171 	}
1172 }
1173 
serverSendItemToPickupAndEquip(int player,Item * item)1174 void serverSendItemToPickupAndEquip(int player, Item* item)
1175 {
1176 	if ( multiplayer != SERVER || player <= 0 )
1177 	{
1178 		return;
1179 	}
1180 
1181 	if ( !client_disconnected[player] )
1182 	{
1183 		// send the client info on the item it just picked up
1184 		strcpy((char*)net_packet->data, "ITEQ");
1185 		SDLNet_Write32((Uint32)item->type, &net_packet->data[4]);
1186 		SDLNet_Write32((Uint32)item->status, &net_packet->data[8]);
1187 		SDLNet_Write32((Uint32)item->beatitude, &net_packet->data[12]);
1188 		SDLNet_Write32((Uint32)item->count, &net_packet->data[16]);
1189 		SDLNet_Write32((Uint32)item->appearance, &net_packet->data[20]);
1190 		SDLNet_Write32((Uint32)item->ownerUid, &net_packet->data[24]);
1191 		net_packet->data[28] = item->identified;
1192 		net_packet->address.host = net_clients[player - 1].host;
1193 		net_packet->address.port = net_clients[player - 1].port;
1194 		net_packet->len = 29;
1195 		sendPacketSafe(net_sock, -1, net_packet, player - 1);
1196 	}
1197 }
1198 
serverUpdateAllyStat(int player,Uint32 uidToUpdate,int LVL,int HP,int MAXHP,int type)1199 void serverUpdateAllyStat(int player, Uint32 uidToUpdate, int LVL, int HP, int MAXHP, int type)
1200 {
1201 	if ( multiplayer != SERVER || player <= 0 )
1202 	{
1203 		return;
1204 	}
1205 
1206 	if ( !client_disconnected[player] )
1207 	{
1208 		strcpy((char*)net_packet->data, "NPCI");
1209 		SDLNet_Write32(uidToUpdate, &net_packet->data[4]);
1210 		net_packet->data[8] = static_cast<Uint8>(LVL);
1211 		SDLNet_Write16(HP, &net_packet->data[9]);
1212 		SDLNet_Write16(MAXHP, &net_packet->data[11]);
1213 		net_packet->data[13] = static_cast<Uint8>(type);
1214 		net_packet->address.host = net_clients[player - 1].host;
1215 		net_packet->address.port = net_clients[player - 1].port;
1216 		net_packet->len = 14;
1217 		sendPacketSafe(net_sock, -1, net_packet, player - 1);
1218 	}
1219 }
1220 
serverUpdatePlayerSummonStrength(int player)1221 void serverUpdatePlayerSummonStrength(int player)
1222 {
1223 	if ( multiplayer != SERVER )
1224 	{
1225 		return;
1226 	}
1227 	if ( player <= 0 || player > MAXPLAYERS )
1228 	{
1229 		return;
1230 	}
1231 
1232 	if ( !client_disconnected[player] && stats[player] )
1233 	{
1234 		strcpy((char*)net_packet->data, "SUMS");
1235 		SDLNet_Write32(stats[player]->playerSummonLVLHP, &net_packet->data[4]);
1236 		SDLNet_Write32(stats[player]->playerSummonSTRDEXCONINT, &net_packet->data[8]);
1237 		SDLNet_Write32(stats[player]->playerSummonPERCHR, &net_packet->data[12]);
1238 		SDLNet_Write32(stats[player]->playerSummon2LVLHP, &net_packet->data[16]);
1239 		SDLNet_Write32(stats[player]->playerSummon2STRDEXCONINT, &net_packet->data[20]);
1240 		SDLNet_Write32(stats[player]->playerSummon2PERCHR, &net_packet->data[24]);
1241 		net_packet->address.host = net_clients[player - 1].host;
1242 		net_packet->address.port = net_clients[player - 1].port;
1243 		net_packet->len = 28;
1244 		sendPacketSafe(net_sock, -1, net_packet, player - 1);
1245 	}
1246 }
1247 
serverUpdateAllyHP(int player,Uint32 uidToUpdate,int HP,int MAXHP,bool guarantee)1248 void serverUpdateAllyHP(int player, Uint32 uidToUpdate, int HP, int MAXHP, bool guarantee)
1249 {
1250 	if ( multiplayer != SERVER )
1251 	{
1252 		return;
1253 	}
1254 	if ( player <= 0 )
1255 	{
1256 		return;
1257 	}
1258 
1259 	if ( !client_disconnected[player] )
1260 	{
1261 		strcpy((char*)net_packet->data, "NPCU");
1262 		SDLNet_Write32(uidToUpdate, &net_packet->data[4]);
1263 		SDLNet_Write16(HP, &net_packet->data[8]);
1264 		SDLNet_Write16(MAXHP, &net_packet->data[10]);
1265 		net_packet->address.host = net_clients[player - 1].host;
1266 		net_packet->address.port = net_clients[player - 1].port;
1267 		net_packet->len = 12;
1268 		if ( !guarantee )
1269 		{
1270 			sendPacket(net_sock, -1, net_packet, player - 1);
1271 		}
1272 		else
1273 		{
1274 			sendPacketSafe(net_sock, -1, net_packet, player - 1);
1275 		}
1276 	}
1277 }
1278 
sendMinimapPing(Uint8 player,Uint8 x,Uint8 y)1279 void sendMinimapPing(Uint8 player, Uint8 x, Uint8 y)
1280 {
1281 	if ( multiplayer == SINGLE )
1282 	{
1283 		return;
1284 	}
1285 
1286 	if ( multiplayer == CLIENT )
1287 	{
1288 		// send to host to relay info.
1289 		strcpy((char*)net_packet->data, "PMAP");
1290 		net_packet->data[4] = player;
1291 		net_packet->data[5] = x;
1292 		net_packet->data[6] = y;
1293 
1294 		net_packet->address.host = net_server.host;
1295 		net_packet->address.port = net_server.port;
1296 		net_packet->len = 7;
1297 		sendPacket(net_sock, -1, net_packet, 0);
1298 	}
1299 	else
1300 	{
1301 		for ( int c = 1; c < MAXPLAYERS; c++ )
1302 		{
1303 			if ( !client_disconnected[c] )
1304 			{
1305 				// send to all clients.
1306 				strcpy((char*)net_packet->data, "PMAP");
1307 				net_packet->data[4] = player;
1308 				net_packet->data[5] = x;
1309 				net_packet->data[6] = y;
1310 
1311 				net_packet->address.host = net_clients[c - 1].host;
1312 				net_packet->address.port = net_clients[c - 1].port;
1313 				net_packet->len = 7;
1314 				sendPacket(net_sock, -1, net_packet, c - 1);
1315 			}
1316 		}
1317 	}
1318 }
1319 
sendAllyCommandClient(int player,Uint32 uid,int command,Uint8 x,Uint8 y,Uint32 targetUid)1320 void sendAllyCommandClient(int player, Uint32 uid, int command, Uint8 x, Uint8 y, Uint32 targetUid)
1321 {
1322 	if ( multiplayer != CLIENT )
1323 	{
1324 		return;
1325 	}
1326 	//messagePlayer(clientnum, "%d", targetUid);
1327 
1328 	// send to host.
1329 	strcpy((char*)net_packet->data, "ALLY");
1330 	net_packet->data[4] = player;
1331 	net_packet->data[5] = command;
1332 	net_packet->data[6] = x;
1333 	net_packet->data[7] = y;
1334 	SDLNet_Write32(uid, &net_packet->data[8]);
1335 	net_packet->len = 12;
1336 	if ( targetUid != 0 )
1337 	{
1338 		SDLNet_Write32(targetUid, &net_packet->data[12]);
1339 		net_packet->len = 16;
1340 	}
1341 	net_packet->address.host = net_server.host;
1342 	net_packet->address.port = net_server.port;
1343 	sendPacket(net_sock, -1, net_packet, 0);
1344 }
1345 
lobbyPlayerJoinRequest(int & outResult)1346 NetworkingLobbyJoinRequestResult lobbyPlayerJoinRequest(int& outResult)
1347 {
1348 	int c = MAXPLAYERS;
1349 	if ( strcmp(VERSION, (char*)net_packet->data + 54) )
1350 	{
1351 		c = MAXPLAYERS + 1; // wrong version number
1352 	}
1353 	else
1354 	{
1355 		Uint32 clientlsg = SDLNet_Read32(&net_packet->data[68]);
1356 		Uint32 clientms = SDLNet_Read32(&net_packet->data[64]);
1357 		if ( net_packet->data[63] == 0 )
1358 		{
1359 			// client will enter any player spot
1360 			for ( c = 0; c < MAXPLAYERS; c++ )
1361 			{
1362 				if ( client_disconnected[c] == true )
1363 				{
1364 					break;    // no more player slots
1365 				}
1366 			}
1367 		}
1368 		else
1369 		{
1370 			// client is joining a particular player spot
1371 			c = net_packet->data[63];
1372 			if ( !client_disconnected[c] )
1373 			{
1374 				c = MAXPLAYERS;  // client wants to fill a space that is already filled
1375 			}
1376 		}
1377 		if ( clientlsg != loadingsavegame && loadingsavegame == 0 )
1378 		{
1379 			c = MAXPLAYERS + 2;  // client shouldn't load save game
1380 		}
1381 		else if ( clientlsg == 0 && loadingsavegame != 0 )
1382 		{
1383 			c = MAXPLAYERS + 3;  // client is trying to join a save game without a save of their own
1384 		}
1385 		else if ( clientlsg != loadingsavegame )
1386 		{
1387 			c = MAXPLAYERS + 4;  // client is trying to join the game with an incompatible save
1388 		}
1389 		else if ( loadingsavegame && getSaveGameMapSeed(false) != clientms )
1390 		{
1391 			c = MAXPLAYERS + 5;  // client is trying to join the game with a slightly incompatible save (wrong level)
1392 		}
1393 	}
1394 	outResult = c;
1395 	if ( c >= MAXPLAYERS )
1396 	{
1397 		// on error, client gets a player number that is invalid (to be interpreted as an error code)
1398 		net_clients[MAXPLAYERS - 1].host = net_packet->address.host;
1399 		net_clients[MAXPLAYERS - 1].port = net_packet->address.port;
1400 		if ( directConnect )
1401 			while ( (net_tcpclients[MAXPLAYERS - 1] = SDLNet_TCP_Accept(net_tcpsock)) == NULL );
1402 		net_packet->address.host = net_clients[MAXPLAYERS - 1].host;
1403 		net_packet->address.port = net_clients[MAXPLAYERS - 1].port;
1404 		net_packet->len = 4;
1405 		SDLNet_Write32(c, &net_packet->data[0]); // error code for client to interpret
1406 		printlog("sending error code %d to client.\n", c);
1407 		if ( directConnect )
1408 		{
1409 			SDLNet_TCP_Send(net_tcpclients[MAXPLAYERS - 1], net_packet->data, net_packet->len);
1410 			SDLNet_TCP_Close(net_tcpclients[MAXPLAYERS - 1]);
1411 			return NET_LOBBY_JOIN_DIRECTIP_FAILURE;
1412 		}
1413 		else
1414 		{
1415 			return NET_LOBBY_JOIN_P2P_FAILURE;
1416 		}
1417 	}
1418 	else
1419 	{
1420 		// on success, client gets legit player number
1421 		strcpy(stats[c]->name, (char*)(&net_packet->data[19]));
1422 		client_disconnected[c] = false;
1423 		client_classes[c] = (int)SDLNet_Read32(&net_packet->data[42]);
1424 		stats[c]->sex = static_cast<sex_t>((int)SDLNet_Read32(&net_packet->data[46]));
1425 		Uint32 raceAndAppearance = (Uint32)SDLNet_Read32(&net_packet->data[50]);
1426 		stats[c]->appearance = (raceAndAppearance & 0xFF00) >> 8;
1427 		stats[c]->playerRace = (raceAndAppearance & 0xFF);
1428 		net_clients[c - 1].host = net_packet->address.host;
1429 		net_clients[c - 1].port = net_packet->address.port;
1430 		if ( directConnect )
1431 		{
1432 			while ( (net_tcpclients[c - 1] = SDLNet_TCP_Accept(net_tcpsock)) == NULL );
1433 			const char* clientaddr = SDLNet_ResolveIP(&net_packet->address);
1434 			printlog("client %d connected from %s:%d\n", c, clientaddr, net_packet->address.port);
1435 		}
1436 		else
1437 		{
1438 			printlog("client %d connected.\n", c);
1439 		}
1440 		client_keepalive[c] = ticks;
1441 
1442 		// send existing clients info on new client
1443 		for ( int x = 1; x < MAXPLAYERS; x++ )
1444 		{
1445 			if ( client_disconnected[x] || c == x )
1446 			{
1447 				continue;
1448 			}
1449 			strcpy((char*)(&net_packet->data[0]), "NEWPLAYER");
1450 			net_packet->data[9] = c; // clientnum
1451 			net_packet->data[10] = client_classes[c]; // class
1452 			net_packet->data[11] = stats[c]->sex; // sex
1453 			net_packet->data[12] = (Uint8)stats[c]->appearance; // appearance
1454 			net_packet->data[13] = (Uint8)stats[c]->playerRace; // player race
1455 			char shortname[32] = "";
1456 			strncpy(shortname, stats[c]->name, 22);
1457 			strcpy((char*)(&net_packet->data[14]), shortname);  // name
1458 			net_packet->address.host = net_clients[x - 1].host;
1459 			net_packet->address.port = net_clients[x - 1].port;
1460 			net_packet->len = 14 + strlen(stats[c]->name) + 1;
1461 			sendPacketSafe(net_sock, -1, net_packet, x - 1);
1462 		}
1463 		char shortname[32] = { 0 };
1464 		strncpy(shortname, stats[c]->name, 22);
1465 
1466 		newString(&lobbyChatboxMessages, 0xFFFFFFFF, "\n***   %s has joined the game   ***\n", shortname);
1467 
1468 		// send new client their id number + info on other clients
1469 		SDLNet_Write32(c, &net_packet->data[0]);
1470 		for ( int x = 0; x < MAXPLAYERS; x++ )
1471 		{
1472 			net_packet->data[4 + x * (5 + 23)] = client_classes[x]; // class
1473 			net_packet->data[5 + x * (5 + 23)] = stats[x]->sex; // sex
1474 			net_packet->data[6 + x * (5 + 23)] = client_disconnected[x]; // connectedness :p
1475 			net_packet->data[7 + x * (5 + 23)] = (Uint8)stats[x]->appearance; // appearance
1476 			net_packet->data[8 + x * (5 + 23)] = (Uint8)stats[x]->playerRace; // player race
1477 			char shortname[32] = "";
1478 			strncpy(shortname, stats[x]->name, 22);
1479 			strcpy((char*)(&net_packet->data[9 + x * (5 + 23)]), shortname);  // name
1480 		}
1481 		net_packet->address.host = net_clients[c - 1].host;
1482 		net_packet->address.port = net_clients[c - 1].port;
1483 		net_packet->len = 4 + MAXPLAYERS * (5 + 23);
1484 		if ( directConnect )
1485 		{
1486 			SDLNet_TCP_Send(net_tcpclients[c - 1], net_packet->data, net_packet->len);
1487 			return NET_LOBBY_JOIN_DIRECTIP_SUCCESS;
1488 		}
1489 		else
1490 		{
1491 			return NET_LOBBY_JOIN_P2P_SUCCESS;
1492 		}
1493 	}
1494 }
1495 
1496 /*-------------------------------------------------------------------------------
1497 
1498 	receiveEntity
1499 
1500 	receives entity data from server
1501 
1502 -------------------------------------------------------------------------------*/
1503 
receiveEntity(Entity * entity)1504 Entity* receiveEntity(Entity* entity)
1505 {
1506 	bool newentity = false;
1507 	int c;
1508 
1509 	//TODO: Find out if this is needed.
1510 	/*bool oldeffects[NUMEFFECTS];
1511 	Stat* entityStats = entity->getStats();
1512 
1513 	for ( int i = 0; i < NUMEFFECTS; ++i )
1514 	{
1515 		if ( !entityStats )
1516 		{
1517 			oldeffects[i] = 0;
1518 		}
1519 		else
1520 		{
1521 			oldeffects[i] = entityStats->EFFECTS[i];
1522 		}
1523 	}
1524 	//Yes, it is necessary. I don't think I like this solution though, will try something else.
1525 	*/
1526 
1527 	if ( entity == nullptr )
1528 	{
1529 		newentity = true;
1530 		entity = newEntity((int)SDLNet_Read16(&net_packet->data[8]), 0, map.entities, nullptr);
1531 	}
1532 	else
1533 	{
1534 		entity->sprite = (int)SDLNet_Read16(&net_packet->data[8]);
1535 	}
1536 	entity->lastupdate = ticks;
1537 	entity->lastupdateserver = (Uint32)SDLNet_Read32(&net_packet->data[36]);
1538 	entity->setUID((int)SDLNet_Read32(&net_packet->data[4])); // remember who I am
1539 	entity->new_x = ((Sint16)SDLNet_Read16(&net_packet->data[10])) / 32.0;
1540 	entity->new_y = ((Sint16)SDLNet_Read16(&net_packet->data[12])) / 32.0;
1541 	entity->new_z = ((Sint16)SDLNet_Read16(&net_packet->data[14])) / 32.0;
1542 	entity->sizex = (Sint8)net_packet->data[16];
1543 	entity->sizey = (Sint8)net_packet->data[17];
1544 	entity->scalex = ((Uint8)net_packet->data[18]) / 128.f;
1545 	entity->scaley = ((Uint8)net_packet->data[19]) / 128.f;
1546 	entity->scalez = ((Uint8)net_packet->data[20]) / 128.f;
1547 	entity->new_yaw = ((Sint16)SDLNet_Read16(&net_packet->data[21])) / 256.0;
1548 	entity->new_pitch = ((Sint16)SDLNet_Read16(&net_packet->data[23])) / 256.0;
1549 	entity->new_roll = ((Sint16)SDLNet_Read16(&net_packet->data[25])) / 256.0;
1550 	if ( newentity )
1551 	{
1552 		entity->x = entity->new_x;
1553 		entity->y = entity->new_y;
1554 		entity->z = entity->new_z;
1555 		entity->yaw = entity->new_yaw;
1556 		entity->pitch = entity->new_pitch;
1557 		entity->roll = entity->new_roll;
1558 	}
1559 	entity->focalx = ((char)net_packet->data[27]) / 8.0;
1560 	entity->focaly = ((char)net_packet->data[28]) / 8.0;
1561 	entity->focalz = ((char)net_packet->data[29]) / 8.0;
1562 	for (c = 0; c < 16; ++c)
1563 	{
1564 		if ( net_packet->data[34 + c / 8]&power(2, c - (c / 8) * 8) )
1565 		{
1566 			entity->flags[c] = true;
1567 		}
1568 	}
1569 	entity->vel_x = ((Sint16)SDLNet_Read16(&net_packet->data[40])) / 32.0;
1570 	entity->vel_y = ((Sint16)SDLNet_Read16(&net_packet->data[42])) / 32.0;
1571 	entity->vel_z = ((Sint16)SDLNet_Read16(&net_packet->data[44])) / 32.0;
1572 
1573 	return entity;
1574 }
1575 
1576 /*-------------------------------------------------------------------------------
1577 
1578 	clientActions
1579 
1580 	Assigns an action to a given entity based mainly on its sprite
1581 
1582 -------------------------------------------------------------------------------*/
1583 
clientActions(Entity * entity)1584 void clientActions(Entity* entity)
1585 {
1586 	int playernum;
1587 
1588 	// this code assigns behaviors based on the sprite (model) number
1589 	switch ( entity->sprite )
1590 	{
1591 		case 1:
1592 			entity->behavior = &actDoorFrame;
1593 			break;
1594 		case 2:
1595 			entity->behavior = &actDoor;
1596 			break;
1597 		case 3:
1598 			entity->behavior = &actTorch;
1599 			entity->flags[NOUPDATE] = 1;
1600 			break;
1601 		case 160:
1602 		case 203:
1603 		case 212:
1604 		case 213:
1605 		case 214:
1606 		case 682:
1607 		case 681:
1608 			entity->flags[NOUPDATE] = true;
1609 			break;
1610 		case 162:
1611 			entity->behavior = &actCampfire;
1612 			entity->flags[NOUPDATE] = true;
1613 			break;
1614 		case 163:
1615 			entity->skill[2] = (int)SDLNet_Read32(&net_packet->data[30]);
1616 			entity->behavior = &actFountain;
1617 			break;
1618 		case 174:
1619 			if (SDLNet_Read32(&net_packet->data[30]) != 0)
1620 			{
1621 				entity->behavior = &actMagiclightBall; //TODO: Finish this here. I think this gets reassigned every time the entity is recieved? Make sure.
1622 			}
1623 			break;
1624 		case 185:
1625 			entity->behavior = &actSwitch;
1626 			break;
1627 		case 186:
1628 			entity->behavior = &actGate;
1629 			break;
1630 		case 216:
1631 			entity->behavior = &actChestLid;
1632 			break;
1633 		case 254:
1634 		case 255:
1635 		case 256:
1636 		case 257:
1637 			entity->behavior = &actPortal;
1638 			break;
1639 		case 273:
1640 			entity->behavior = &actMCaxe;
1641 			break;
1642 		case 278:
1643 		case 279:
1644 		case 280:
1645 		case 281:
1646 			entity->behavior = &actWinningPortal;
1647 			break;
1648 		case 282:
1649 			entity->behavior = &actSpearTrap;
1650 			break;
1651 		case 578:
1652 			entity->behavior = &actPowerCrystal;
1653 			break;
1654 		case 586:
1655 			entity->behavior = &actSwitchWithTimer;
1656 			break;
1657 		case 601:
1658 			entity->behavior = &actPedestalBase;
1659 			break;
1660 		case 602:
1661 		case 603:
1662 		case 604:
1663 		case 605:
1664 			entity->behavior = &actPedestalOrb;
1665 			break;
1666 		case 667:
1667 		case 668:
1668 			entity->behavior = &actBeartrap;
1669 			break;
1670 		case 674:
1671 		case 675:
1672 		case 676:
1673 		case 677:
1674 			entity->behavior = &actCeilingTile;
1675 			entity->flags[NOUPDATE] = true;
1676 			break;
1677 		case 629:
1678 			entity->behavior = &actColumn;
1679 			entity->flags[NOUPDATE] = true;
1680 			break;
1681 		case 632:
1682 		case 633:
1683 			entity->behavior = &actPistonCam;
1684 			entity->flags[NOUPDATE] = true;
1685 			break;
1686 		case 130:
1687 			entity->behavior = &actGoldBag;
1688 			break;
1689 		default:
1690 			if ( entity->isPlayerHeadSprite() )
1691 			{
1692 				// these are all player heads
1693 				playernum = SDLNet_Read32(&net_packet->data[30]);
1694 				if ( playernum >= 0 && playernum < MAXPLAYERS )
1695 				{
1696 					if ( players[playernum] && players[playernum]->entity )
1697 					{
1698 						players[playernum]->entity = entity;
1699 					}
1700 					entity->skill[2] = playernum;
1701 					entity->behavior = &actPlayer;
1702 				}
1703 			}
1704 			break;
1705 	}
1706 
1707 	// if the above method failed, we check the value of skill[2] (stored in net_packet->data[30]) and assign an action based on that
1708 	if ( entity->behavior == NULL )
1709 	{
1710 		int c = (int)SDLNet_Read32(&net_packet->data[30]);
1711 		if ( c < 0 )
1712 		{
1713 			switch ( c )
1714 			{
1715 				case -4:
1716 					entity->behavior = &actMonster;
1717 					break;
1718 				case -5:
1719 					entity->behavior = &actItem;
1720 					break;
1721 				case -6:
1722 					entity->behavior = &actGib;
1723 					break;
1724 				case -7:
1725 					entity->behavior = &actEmpty;
1726 					if ( entity->sprite == 989 ) // boulder_lava.vox
1727 					{
1728 						entity->flags[BURNABLE] = true;
1729 					}
1730 					break;
1731 				case -8:
1732 					entity->behavior = &actThrown;
1733 					break;
1734 				case -9:
1735 					entity->behavior = &actLiquid;
1736 					break;
1737 				case -10:
1738 					entity->behavior = &actMagiclightBall;
1739 					break;
1740 				case -11:
1741 					entity->behavior = &actMagicClient;
1742 					break;
1743 				case -12:
1744 					entity->behavior = &actMagicClientNoLight;
1745 					break;
1746 				case -13:
1747 					entity->behavior = &actParticleSapCenter;
1748 					break;
1749 				case -14:
1750 					entity->behavior = &actDecoyBox;
1751 					break;
1752 				case -15:
1753 					entity->behavior = &actBomb;
1754 					break;
1755 				case -16:
1756 					entity->behavior = &actBoulder;
1757 					break;
1758 				default:
1759 					if ( c < -1000 && c > -2000 )
1760 					{
1761 						entity->arrowShotByWeapon = -(c + 1000);
1762 						entity->behavior = &actArrow;
1763 					}
1764 					break;
1765 			}
1766 		}
1767 	}
1768 }
1769 
1770 /*-------------------------------------------------------------------------------
1771 
1772 	clientHandlePacket
1773 
1774 	Called by clientHandleMessages. Does the actual handling of a packet.
1775 
1776 -------------------------------------------------------------------------------*/
1777 
clientHandlePacket()1778 void clientHandlePacket()
1779 {
1780 	if (handleSafePacket())
1781 	{
1782 		return;
1783 	}
1784 
1785 	Uint32 x, y;
1786 	node_t* node;
1787 	node_t* nextnode;
1788 	Entity* entity, *entity2;
1789 	int c = 0;
1790 	Uint32 i = 0, j;
1791 	Item* item = NULL;
1792 	FILE* fp;
1793 
1794 #ifdef PACKETINFO
1795 	char packetinfo[NET_PACKET_SIZE];
1796 	strncpy( packetinfo, (char*)net_packet->data, net_packet->len );
1797 	packetinfo[net_packet->len] = 0;
1798 	printlog("info: client packet: %s\n", packetinfo);
1799 #endif
1800 	if ( logCheckMainLoopTimers )
1801 	{
1802 		char packetinfo[NET_PACKET_SIZE];
1803 		strncpy(packetinfo, (char*)net_packet->data, net_packet->len);
1804 		packetinfo[net_packet->len] = 0;
1805 
1806 		char packetHeader[5];
1807 		strncpy(packetHeader, packetinfo, 4);
1808 		packetHeader[4] = 0;
1809 
1810 		std::string tmp = packetHeader;
1811 		unsigned long hash = djb2Hash(packetHeader);
1812 		auto find = DebugStats.networkPackets.find(hash);
1813 		if ( find != DebugStats.networkPackets.end() )
1814 		{
1815 			++DebugStats.networkPackets[hash].second;
1816 		}
1817 		else
1818 		{
1819 			DebugStats.networkPackets.insert(std::make_pair(hash, std::make_pair(tmp, 0)));
1820 			messagePlayer(clientnum, "%s", tmp.c_str());
1821 		}
1822 		if ( !strcmp(packetinfo, "ENTU") )
1823 		{
1824 			int sprite = 0;
1825 			Uint32 uidpacket = static_cast<Uint32>(SDLNet_Read32(&net_packet->data[4]));
1826 			if ( uidToEntity(uidpacket) )
1827 			{
1828 				sprite = uidToEntity(uidpacket)->sprite;
1829 				auto find = DebugStats.entityUpdatePackets.find(sprite);
1830 				if ( find != DebugStats.entityUpdatePackets.end() )
1831 				{
1832 					++DebugStats.entityUpdatePackets[sprite];
1833 				}
1834 				else
1835 				{
1836 					DebugStats.entityUpdatePackets.insert(std::make_pair(sprite, 1));
1837 				}
1838 			}
1839 		}
1840 	}
1841 
1842 	// keep alive
1843 	if (!strncmp((char*)net_packet->data, "KPAL", 4))
1844 	{
1845 		client_keepalive[0] = ticks;
1846 		return;
1847 	}
1848 
1849 	// entity update
1850 	else if ( !strncmp((char*)net_packet->data, "ENTU", 4) )
1851 	{
1852 		client_keepalive[0] = ticks; // don't timeout
1853 		Entity *entity = uidToEntity((int)SDLNet_Read32(&net_packet->data[4]));
1854 		if ( entity )
1855 		{
1856 			if ( (Uint32)SDLNet_Read32(&net_packet->data[36]) < (Uint32)entity->lastupdateserver )
1857 			{
1858 				// old packet, not used
1859 			}
1860 			else if ( entity->behavior == &actPlayer && entity->skill[2] == clientnum )
1861 			{
1862 				// don't update my player
1863 			}
1864 			else if ( entity->flags[NOUPDATE] )
1865 			{
1866 				// inform the server that it tried to update a no-update entity
1867 				strcpy((char*)net_packet->data, "NOUP");
1868 				net_packet->data[4] = clientnum;
1869 				SDLNet_Write32(entity->getUID(), &net_packet->data[5]);
1870 				net_packet->address.host = net_server.host;
1871 				net_packet->address.port = net_server.port;
1872 				net_packet->len = 9;
1873 				sendPacket(net_sock, -1, net_packet, 0);
1874 			}
1875 			else
1876 			{
1877 				// receive the entity
1878 				receiveEntity(entity);
1879 				entity->behavior = NULL;
1880 				clientActions(entity);
1881 			}
1882 			return;
1883 		}
1884 
1885 		for ( node = removedEntities.first; node != NULL; node = node->next )
1886 		{
1887 			entity2 = (Entity*)node->element;
1888 			if ( entity2->getUID() == (int)SDLNet_Read32(&net_packet->data[4]) )
1889 			{
1890 				return;
1891 			}
1892 		}
1893 
1894 		entity = receiveEntity(NULL);
1895 		// IMPORTANT! Assign actions to the objects the client has control over
1896 		clientActions(entity);
1897 		return;
1898 	}
1899 
1900 	else if ( !strncmp((char*)net_packet->data, "EFFE", 4) )
1901 	{
1902 		/*
1903 		* Packet breakdown:
1904 		* [0][1][2][3]: "EFFE"
1905 		* [4][5][6][7]: Entity's UID.
1906 		* [8][9][10][11]: Entity's effects.
1907 		*/
1908 
1909 		Uint32 uid = static_cast<int>(SDLNet_Read32(&net_packet->data[4]));
1910 
1911 		Entity* entity = uidToEntity(uid);
1912 
1913 		if ( entity )
1914 		{
1915 			if ( entity->behavior == &actPlayer && entity->skill[2] == clientnum )
1916 			{
1917 				//Don't update this client's entity! Use the dedicated function for that.
1918 				return;
1919 			}
1920 
1921 			Stat *stats = entity->getStats();
1922 			if ( !stats )
1923 			{
1924 				entity->giveClientStats();
1925 				stats = entity->getStats();
1926 				if ( !stats )
1927 				{
1928 					return;
1929 				}
1930 			}
1931 
1932 			for ( int i = 0; i < NUMEFFECTS; ++i )
1933 			{
1934 				if ( net_packet->data[8 + i / 8] & power(2, i - (i / 8) * 8) )
1935 				{
1936 					stats->EFFECTS[i] = true;
1937 				}
1938 				else
1939 				{
1940 					stats->EFFECTS[i] = false;
1941 				}
1942 			}
1943 		}
1944 		return;
1945 	}
1946 
1947 	// update entity skill
1948 	else if ( !strncmp((char*)net_packet->data, "ENTS", 4) )
1949 	{
1950 		Entity *entity = uidToEntity((int)SDLNet_Read32(&net_packet->data[4]));
1951 		if ( entity )
1952 		{
1953 			entity->skill[net_packet->data[8]] = SDLNet_Read32(&net_packet->data[9]);
1954 		}
1955 		return;
1956 	}
1957 
1958 	// update entity fskill
1959 	else if ( !strncmp((char*)net_packet->data, "ENFS", 4) )
1960 	{
1961 		Entity *entity = uidToEntity((int)SDLNet_Read32(&net_packet->data[4]));
1962 		if ( entity )
1963 		{
1964 			entity->fskill[net_packet->data[8]] = (SDLNet_Read16(&net_packet->data[9]) / 256.0);
1965 		}
1966 		return;
1967 	}
1968 
1969 	// update entity bodypart
1970 	else if ( !strncmp((char*)net_packet->data, "ENTB", 4) )
1971 	{
1972 		Entity *entity = uidToEntity((int)SDLNet_Read32(&net_packet->data[4]));
1973 		if ( entity )
1974 		{
1975 			node_t* childNode = list_Node(&entity->children, net_packet->data[8]);
1976 			if ( childNode )
1977 			{
1978 				Entity* tempEntity = (Entity*)childNode->element;
1979 				tempEntity->sprite = SDLNet_Read32(&net_packet->data[9]);
1980 				tempEntity->skill[7] = tempEntity->sprite;
1981 				tempEntity->flags[INVISIBLE] = net_packet->data[13];
1982 			}
1983 		}
1984 		return;
1985 	}
1986 
1987 	// bodypart ids
1988 	else if ( !strncmp((char*)net_packet->data, "BDYI", 4) )
1989 	{
1990 		Entity *entity = uidToEntity((int)SDLNet_Read32(&net_packet->data[4]));
1991 		if ( entity )
1992 		{
1993 			node_t* childNode;
1994 			int c;
1995 			for ( c = 0, childNode = entity->children.first; childNode != nullptr; childNode = childNode->next, c++ )
1996 			{
1997 				if ( c < 1 || (c < 2 && entity->behavior == &actMonster) )
1998 				{
1999 					continue;
2000 				}
2001 				Entity* tempEntity = (Entity*)childNode->element;
2002 				if ( entity->behavior == &actMonster )
2003 				{
2004 					tempEntity->setUID(SDLNet_Read32(&net_packet->data[8 + 4 * (c - 2)]));
2005 				}
2006 				else
2007 				{
2008 					tempEntity->setUID(SDLNet_Read32(&net_packet->data[8 + 4 * (c - 1)]));
2009 				}
2010 			}
2011 		}
2012 		return;
2013 	}
2014 
2015 	// update entity flag
2016 	else if ( !strncmp((char*)net_packet->data, "ENTF", 4) )
2017 	{
2018 		Entity *entity = uidToEntity((int)SDLNet_Read32(&net_packet->data[4]));
2019 		if ( entity )
2020 		{
2021 			entity->flags[net_packet->data[8]] = net_packet->data[9];
2022 			if ( entity->behavior == &actMonster && net_packet->data[8] == USERFLAG2 )
2023 			{
2024 				// we should update the flags for all bodyparts (except for human and automaton heads, don't update the other bodyparts).
2025 				if ( !(entity->isPlayerHeadSprite() || entity->sprite == 467 || !monsterChangesColorWhenAlly(nullptr, entity)) )
2026 				{
2027 					int bodypart = 0;
2028 					for ( node_t* node = entity->children.first; node != nullptr; node = node->next )
2029 					{
2030 						if ( bodypart >= LIMB_HUMANOID_TORSO )
2031 						{
2032 							Entity* tmp = (Entity*)node->element;
2033 							if ( tmp )
2034 							{
2035 								tmp->flags[USERFLAG2] = entity->flags[net_packet->data[8]];
2036 							}
2037 						}
2038 						++bodypart;
2039 					}
2040 				}
2041 			}
2042 		}
2043 		return;
2044 	}
2045 
2046 	// player movement correction
2047 	else if ( !strncmp((char*)net_packet->data, "PMOV", 4) )
2048 	{
2049 		if ( players[clientnum] == nullptr || players[clientnum]->entity == nullptr )
2050 		{
2051 			return;
2052 		}
2053 		players[clientnum]->entity->x = ((Sint16)SDLNet_Read16(&net_packet->data[4])) / 32.0;
2054 		players[clientnum]->entity->y = ((Sint16)SDLNet_Read16(&net_packet->data[6])) / 32.0;
2055 		return;
2056 	}
2057 
2058 	// update health
2059 	else if ( !strncmp((char*)net_packet->data, "UPHP", 4) )
2060 	{
2061 		if ( (Monster)SDLNet_Read32(&net_packet->data[8]) != NOTHING )
2062 		{
2063 			if ( SDLNet_Read32(&net_packet->data[4]) < stats[clientnum]->HP )
2064 			{
2065 				cameravars[clientnum].shakex += .1;
2066 				cameravars[clientnum].shakey += 10;
2067 			}
2068 			else
2069 			{
2070 				cameravars[clientnum].shakex += .05;
2071 				cameravars[clientnum].shakey += 5;
2072 			}
2073 		}
2074 		stats[clientnum]->HP = SDLNet_Read32(&net_packet->data[4]);
2075 		return;
2076 	}
2077 
2078 	// server sent item details.
2079 	else if ( !strncmp((char*)net_packet->data, "ITMU", 4) )
2080 	{
2081 		Uint32 uid = SDLNet_Read32(&net_packet->data[4]);
2082 		Entity* entity = uidToEntity(uid);
2083 		if ( entity )
2084 		{
2085 			entity->skill[10] = SDLNet_Read32(&net_packet->data[8]); // item type
2086 			entity->skill[11] = SDLNet_Read32(&net_packet->data[12]); // status
2087 			entity->skill[12] = SDLNet_Read32(&net_packet->data[16]); // beatitude
2088 			entity->itemReceivedDetailsFromServer = 1;
2089 		}
2090 		return;
2091 	}
2092 
2093 
2094 	// spawn an explosion
2095 	else if ( !strncmp((char*)net_packet->data, "EXPL", 4) )
2096 	{
2097 		Sint16 x = (Sint16)SDLNet_Read16(&net_packet->data[4]);
2098 		Sint16 y = (Sint16)SDLNet_Read16(&net_packet->data[6]);
2099 		Sint16 z = (Sint16)SDLNet_Read16(&net_packet->data[8]);
2100 		spawnExplosion(x, y, z);
2101 		return;
2102 	}
2103 
2104 	// spawn an explosion, custom sprite
2105 	else if ( !strncmp((char*)net_packet->data, "EXPS", 4) )
2106 	{
2107 		Uint16 sprite = (Uint16)SDLNet_Read16(&net_packet->data[4]);
2108 		Sint16 x = (Sint16)SDLNet_Read16(&net_packet->data[6]);
2109 		Sint16 y = (Sint16)SDLNet_Read16(&net_packet->data[8]);
2110 		Sint16 z = (Sint16)SDLNet_Read16(&net_packet->data[10]);
2111 		spawnExplosionFromSprite(sprite, x, y, z);
2112 		return;
2113 	}
2114 
2115 	// spawn a bang sprite
2116 	else if ( !strncmp((char*)net_packet->data, "BANG", 4) )
2117 	{
2118 		Sint16 x = (Sint16)SDLNet_Read16(&net_packet->data[4]);
2119 		Sint16 y = (Sint16)SDLNet_Read16(&net_packet->data[6]);
2120 		Sint16 z = (Sint16)SDLNet_Read16(&net_packet->data[8]);
2121 		spawnBang(x, y, z);
2122 		return;
2123 	}
2124 
2125 	// spawn a gib
2126 	else if ( !strncmp((char*)net_packet->data, "SPGB", 4) )
2127 	{
2128 		Sint16 x = (Sint16)SDLNet_Read16(&net_packet->data[4]);
2129 		Sint16 y = (Sint16)SDLNet_Read16(&net_packet->data[6]);
2130 		Sint16 z = (Sint16)SDLNet_Read16(&net_packet->data[8]);
2131 		Sint16 sprite = (Sint16)SDLNet_Read16(&net_packet->data[10]);
2132 		Entity* gib = spawnGibClient(x, y, z, sprite);
2133 		gib->flags[SPRITE] = net_packet->data[12];
2134 		if ( !spawn_blood && (!gib->flags[SPRITE] || gib->sprite != 29) )
2135 		{
2136 			gib->flags[INVISIBLE] = true;
2137 		}
2138 		return;
2139 	}
2140 
2141 	// spawn a sleep Z
2142 	else if ( !strncmp((char*)net_packet->data, "SLEZ", 4) )
2143 	{
2144 		Sint16 x = (Sint16)SDLNet_Read16(&net_packet->data[4]);
2145 		Sint16 y = (Sint16)SDLNet_Read16(&net_packet->data[6]);
2146 		Sint16 z = (Sint16)SDLNet_Read16(&net_packet->data[8]);
2147 		spawnSleepZ(x, y, z);
2148 		return;
2149 	}
2150 
2151 	// spawn a misc sprite like the sleep Z
2152 	else if ( !strncmp((char*)net_packet->data, "SLEM", 4) )
2153 	{
2154 		Sint16 x = (Sint16)SDLNet_Read16(&net_packet->data[4]);
2155 		Sint16 y = (Sint16)SDLNet_Read16(&net_packet->data[6]);
2156 		Sint16 z = (Sint16)SDLNet_Read16(&net_packet->data[8]);
2157 		Sint16 sprite = (Sint16)SDLNet_Read16(&net_packet->data[10]);
2158 		spawnFloatingSpriteMisc(sprite, x, y, z);
2159 		return;
2160 	}
2161 
2162 	// spawn magical effect particles
2163 	else if ( !strncmp((char*)net_packet->data, "MAGE", 4) )
2164 	{
2165 		Sint16 x = (Sint16)SDLNet_Read16(&net_packet->data[4]);
2166 		Sint16 y = (Sint16)SDLNet_Read16(&net_packet->data[6]);
2167 		Sint16 z = (Sint16)SDLNet_Read16(&net_packet->data[8]);
2168 		Uint32 sprite = (Uint32)SDLNet_Read32(&net_packet->data[10]);
2169 		spawnMagicEffectParticles(x, y, z, sprite);
2170 		return;
2171 	}
2172 
2173 	// spawn misc particle effect
2174 	else if ( !strncmp((char*)net_packet->data, "SPPE", 4) )
2175 	{
2176 		Entity *entity = uidToEntity((int)SDLNet_Read32(&net_packet->data[4]));
2177 		if ( entity )
2178 		{
2179 			int particleType = static_cast<int>(net_packet->data[8]);
2180 			int sprite = static_cast<int>(SDLNet_Read16(&net_packet->data[9]));
2181 			switch ( particleType )
2182 			{
2183 				case PARTICLE_EFFECT_ABILITY_PURPLE:
2184 					createParticleDot(entity);
2185 					break;
2186 				case PARTICLE_EFFECT_ABILITY_ROCK:
2187 					createParticleRock(entity);
2188 					break;
2189 				case PARTICLE_EFFECT_SHADOW_INVIS:
2190 					createParticleDropRising(entity, sprite, 1.0);
2191 					break;
2192 				case PARTICLE_EFFECT_INCUBUS_TELEPORT_STEAL:
2193 				{
2194 					Entity* spellTimer = createParticleTimer(entity, 80, sprite);
2195 					spellTimer->particleTimerCountdownAction = PARTICLE_TIMER_ACTION_SHOOT_PARTICLES;
2196 					spellTimer->particleTimerCountdownSprite = sprite;
2197 					spellTimer->particleTimerPreDelay = 40;
2198 				}
2199 				break;
2200 				case PARTICLE_EFFECT_INCUBUS_TELEPORT_TARGET:
2201 				{
2202 					Entity* spellTimer = createParticleTimer(entity, 40, sprite);
2203 					spellTimer->particleTimerCountdownAction = PARTICLE_TIMER_ACTION_SHOOT_PARTICLES;
2204 					spellTimer->particleTimerCountdownSprite = sprite;
2205 				}
2206 				break;
2207 				case PARTICLE_EFFECT_SHADOW_TELEPORT:
2208 				{
2209 					Entity* spellTimer = createParticleTimer(entity, 40, sprite);
2210 					spellTimer->particleTimerCountdownAction = PARTICLE_TIMER_ACTION_SHOOT_PARTICLES;
2211 					spellTimer->particleTimerCountdownSprite = sprite;
2212 				}
2213 				break;
2214 				case PARTICLE_EFFECT_TELEPORT_PULL:
2215 				{
2216 					Entity* spellTimer = createParticleTimer(entity, 40, sprite);
2217 					spellTimer->particleTimerCountdownAction = PARTICLE_TIMER_ACTION_SHOOT_PARTICLES;
2218 					spellTimer->particleTimerCountdownSprite = sprite;
2219 				}
2220 				break;
2221 				case PARTICLE_EFFECT_ERUPT:
2222 					createParticleErupt(entity, sprite);
2223 					break;
2224 				case PARTICLE_EFFECT_VAMPIRIC_AURA:
2225 					createParticleDropRising(entity, sprite, 0.5);
2226 					break;
2227 				case PARTICLE_EFFECT_RISING_DROP:
2228 					createParticleDropRising(entity, sprite, 1.0);
2229 					break;
2230 				case PARTICLE_EFFECT_CHARM_MONSTER:
2231 					createParticleCharmMonster(entity);
2232 					break;
2233 				case PARTICLE_EFFECT_SHADOW_TAG:
2234 				{
2235 					Uint32 uid = SDLNet_Read32(&net_packet->data[11]);
2236 					createParticleShadowTag(entity, uid, 60 * TICKS_PER_SECOND);
2237 					break;
2238 				}
2239 				case PARTICLE_EFFECT_SPELL_WEB_ORBIT:
2240 					createParticleAestheticOrbit(entity, 863, 400, PARTICLE_EFFECT_SPELL_WEB_ORBIT);
2241 					break;
2242 				case PARTICLE_EFFECT_PORTAL_SPAWN:
2243 				{
2244 					Entity* spellTimer = createParticleTimer(entity, 100, sprite);
2245 					spellTimer->particleTimerCountdownAction = PARTICLE_TIMER_ACTION_SPAWN_PORTAL;
2246 					spellTimer->particleTimerCountdownSprite = 174;
2247 					spellTimer->particleTimerEndAction = PARTICLE_EFFECT_PORTAL_SPAWN;
2248 				}
2249 				break;
2250 				case PARTICLE_EFFECT_LICHFIRE_TELEPORT_STATIONARY:
2251 				case PARTICLE_EFFECT_LICHICE_TELEPORT_STATIONARY:
2252 				case PARTICLE_EFFECT_LICH_TELEPORT_ROAMING:
2253 				{
2254 					Entity* spellTimer = createParticleTimer(entity, 40, sprite);
2255 					spellTimer->particleTimerCountdownAction = PARTICLE_TIMER_ACTION_SHOOT_PARTICLES;
2256 					spellTimer->particleTimerCountdownSprite = sprite;
2257 				}
2258 				break;
2259 				case PARTICLE_EFFECT_PLAYER_AUTOMATON_DEATH:
2260 					createParticleExplosionCharge(entity, 174, 100, 0.25);
2261 					if ( entity && entity->behavior == &actPlayer )
2262 					{
2263 						if ( entity->getMonsterTypeFromSprite() == AUTOMATON )
2264 						{
2265 							entity->playerAutomatonDeathCounter = 1;
2266 							if ( entity->skill[2] == clientnum )
2267 							{
2268 								// this is me dying, setup the deathcam.
2269 								entity->playerCreatedDeathCam = 1;
2270 								Entity* entity = newEntity(-1, 1, map.entities, nullptr);
2271 								entity->x = cameras[clientnum].x * 16;
2272 								entity->y = cameras[clientnum].y * 16;
2273 								entity->z = -2;
2274 								entity->flags[NOUPDATE] = true;
2275 								entity->flags[PASSABLE] = true;
2276 								entity->flags[INVISIBLE] = true;
2277 								entity->behavior = &actDeathCam;
2278 								entity->skill[2] = clientnum;
2279 								entity->yaw = cameras[clientnum].ang;
2280 								entity->pitch = PI / 8;
2281 							}
2282 						}
2283 					}
2284 					break;
2285 				default:
2286 					break;
2287 			}
2288 		}
2289 		return;
2290 	}
2291 
2292 	// spawn misc particle effect at fixed location
2293 	else if ( !strncmp((char*)net_packet->data, "SPPL", 4) )
2294 	{
2295 		Sint16 particle_x = static_cast<Sint16>(SDLNet_Read16(&net_packet->data[4]));
2296 		Sint16 particle_y = static_cast<Sint16>(SDLNet_Read16(&net_packet->data[6]));
2297 		Sint16 particle_z = static_cast<Sint16>(SDLNet_Read16(&net_packet->data[8]));
2298 		int particleType = static_cast<int>(net_packet->data[10]);
2299 		int sprite = static_cast<int>(SDLNet_Read16(&net_packet->data[11]));
2300 		//messagePlayer(1, "recv, %d, %d, %d, type: %d", particle_x, particle_y, particle_z, particleType);
2301 		switch ( particleType )
2302 		{
2303 			case PARTICLE_EFFECT_SUMMON_MONSTER:
2304 			{
2305 				Entity* spellTimer = createParticleTimer(nullptr, 70, sprite);
2306 				spellTimer->particleTimerCountdownAction = PARTICLE_TIMER_ACTION_SUMMON_MONSTER;
2307 				spellTimer->particleTimerCountdownSprite = 174;
2308 				spellTimer->particleTimerEndAction = PARTICLE_EFFECT_SUMMON_MONSTER;
2309 				spellTimer->x = particle_x * 16.0 + 8;
2310 				spellTimer->y = particle_y * 16.0 + 8;
2311 				spellTimer->z = particle_z;
2312 			}
2313 			break;
2314 			case PARTICLE_EFFECT_DEVIL_SUMMON_MONSTER:
2315 			{
2316 				Entity* spellTimer = createParticleTimer(nullptr, 70, sprite);
2317 				spellTimer->particleTimerCountdownAction = PARTICLE_TIMER_ACTION_DEVIL_SUMMON_MONSTER;
2318 				spellTimer->particleTimerCountdownSprite = 174;
2319 				spellTimer->particleTimerEndAction = PARTICLE_EFFECT_SUMMON_MONSTER;
2320 				spellTimer->x = particle_x * 16.0 + 8;
2321 				spellTimer->y = particle_y * 16.0 + 8;
2322 				spellTimer->z = particle_z;
2323 			}
2324 			break;
2325 			case PARTICLE_EFFECT_SPELL_SUMMON:
2326 			{
2327 				Entity* spellTimer = createParticleTimer(nullptr, 55, sprite);
2328 				spellTimer->particleTimerCountdownSprite = 791;
2329 				spellTimer->particleTimerCountdownAction = PARTICLE_TIMER_ACTION_SPELL_SUMMON;
2330 				spellTimer->particleTimerPreDelay = 40;
2331 				spellTimer->particleTimerEndAction = PARTICLE_EFFECT_SPELL_SUMMON;
2332 				spellTimer->x = particle_x * 16.0 + 8;
2333 				spellTimer->y = particle_y * 16.0 + 8;
2334 				spellTimer->z = particle_z;
2335 			}
2336 			break;
2337 			case PARTICLE_EFFECT_TELEPORT_PULL_TARGET_LOCATION:
2338 			{
2339 				Entity* spellTimer = createParticleTimer(nullptr, 40, 593);
2340 				spellTimer->particleTimerCountdownAction = PARTICLE_EFFECT_TELEPORT_PULL_TARGET_LOCATION;
2341 				spellTimer->particleTimerCountdownSprite = 593;
2342 				spellTimer->x = particle_x * 16.0 + 8;
2343 				spellTimer->y = particle_y * 16.0 + 8;
2344 				spellTimer->z = particle_z;
2345 				spellTimer->flags[PASSABLE] = false;
2346 				spellTimer->sizex = 4;
2347 				spellTimer->sizey = 4;
2348 			}
2349 			break;
2350 			default:
2351 				break;
2352 		}
2353 		return;
2354 	}
2355 
2356 	// enemy hp bar
2357 	else if ( !strncmp((char*)net_packet->data, "ENHP", 4) )
2358 	{
2359 		Sint32 enemy_hp = SDLNet_Read32(&net_packet->data[4]);
2360 		Sint32 enemy_maxhp = SDLNet_Read32(&net_packet->data[8]);
2361 		Uint32 enemy_bar_color = SDLNet_Read32(&net_packet->data[12]); // receive color enemy bar data for my client.
2362 		Sint32 oldhp = SDLNet_Read32(&net_packet->data[16]);
2363 		Uint32 uid = SDLNet_Read32(&net_packet->data[20]);
2364 		bool lowPriorityTick = false;
2365 		if ( net_packet->data[24] == 1 )
2366 		{
2367 			lowPriorityTick = true;
2368 		}
2369 		char enemy_name[128] = "";
2370 		strcpy(enemy_name, (char*)(&net_packet->data[25]));
2371 		enemyHPDamageBarHandler.addEnemyToList(enemy_hp, enemy_maxhp, oldhp, enemy_bar_color, uid, enemy_name, lowPriorityTick);
2372 		return;
2373 	}
2374 
2375 	// ping
2376 	else if (!strncmp((char*)net_packet->data, "PING", 4))
2377 	{
2378 		messagePlayer(clientnum, language[1117], (SDL_GetTicks() - pingtime));
2379 		return;
2380 	}
2381 
2382 	// unlock steam achievement
2383 	else if (!strncmp((char*)net_packet->data, "SACH", 4))
2384 	{
2385 		steamAchievement((char*)(&net_packet->data[4]));
2386 		return;
2387 	}
2388 
2389 	// update steam statistic
2390 	else if ( !strncmp((char*)net_packet->data, "SSTA", 4) )
2391 	{
2392 		int value = static_cast<int>(SDLNet_Read16(&net_packet->data[6]));
2393 		steamStatisticUpdate(static_cast<int>(net_packet->data[4]),
2394 			static_cast<ESteamStatTypes>(net_packet->data[5]), value);
2395 		return;
2396 	}
2397 
2398 	// pause game
2399 	else if (!strncmp((char*)net_packet->data, "PAUS", 4))
2400 	{
2401 		messagePlayer(clientnum, language[1118], stats[net_packet->data[4]]->name);
2402 		pauseGame(2, 0);
2403 		return;
2404 	}
2405 
2406 	// unpause game
2407 	else if (!strncmp((char*)net_packet->data, "UNPS", 4))
2408 	{
2409 		messagePlayer(clientnum, language[1119], stats[net_packet->data[4]]->name);
2410 		pauseGame(1, 0);
2411 		return;
2412 	}
2413 
2414 	// server or player shut down
2415 	else if (!strncmp((char*)net_packet->data, "DISCONNECT", 10))
2416 	{
2417 		if ( net_packet->data[10] == 0 )
2418 		{
2419 			// server shutdown
2420 			if ( !victory )
2421 			{
2422 				button_t* button;
2423 
2424 				printlog("The remote server has shut down.\n");
2425 				pauseGame(2, 0);
2426 
2427 				// close current window
2428 				buttonCloseSubwindow(NULL);
2429 				for ( node = button_l.first; node != NULL; node = nextnode )
2430 				{
2431 					nextnode = node->next;
2432 					button = (button_t*)node->element;
2433 					if ( button->focused )
2434 					{
2435 						list_RemoveNode(button->node);
2436 					}
2437 				}
2438 
2439 				// create new window
2440 				subwindow = 1;
2441 				subx1 = xres / 2 - 256;
2442 				subx2 = xres / 2 + 256;
2443 				suby1 = yres / 2 - 56;
2444 				suby2 = yres / 2 + 56;
2445 				strcpy(subtext, language[1126]);
2446 
2447 				// close button
2448 				button = newButton();
2449 				strcpy(button->label, "x");
2450 				button->x = subx2 - 20;
2451 				button->y = suby1;
2452 				button->sizex = 20;
2453 				button->sizey = 20;
2454 				button->action = &buttonCloseAndEndGameConfirm;
2455 				button->visible = 1;
2456 				button->focused = 1;
2457 
2458 				// okay button
2459 				button = newButton();
2460 				strcpy(button->label, language[732]);
2461 				button->x = subx2 - (subx2 - subx1) / 2 - 28;
2462 				button->y = suby2 - 28;
2463 				button->sizex = 56;
2464 				button->sizey = 20;
2465 				button->action = &buttonCloseAndEndGameConfirm;
2466 				button->visible = 1;
2467 				button->focused = 1;
2468 				button->key = SDL_SCANCODE_RETURN;
2469 			}
2470 		}
2471 		client_disconnected[net_packet->data[10]] = true;
2472 		return;
2473 	}
2474 
2475 	// teleport player
2476 	else if (!strncmp((char*)net_packet->data, "TELE", 4))
2477 	{
2478 		if (players[clientnum] == nullptr || players[clientnum]->entity == nullptr)
2479 		{
2480 			return;
2481 		}
2482 		int tele_x = net_packet->data[4];
2483 		int tele_y = net_packet->data[5];
2484 		Sint16 degrees = (Sint16)SDLNet_Read16(&net_packet->data[6]);
2485 		players[clientnum]->entity->yaw = degrees * PI / 180;
2486 		players[clientnum]->entity->x = (tele_x << 4) + 8;
2487 		players[clientnum]->entity->y = (tele_y << 4) + 8;
2488 		return;
2489 	}
2490 
2491 	// teleport player
2492 	else if ( !strncmp((char*)net_packet->data, "TELM", 4) )
2493 	{
2494 		if ( players[clientnum] == nullptr || players[clientnum]->entity == nullptr )
2495 		{
2496 			return;
2497 		}
2498 		int tele_x = net_packet->data[4];
2499 		int tele_y = net_packet->data[5];
2500 		int type = net_packet->data[6];
2501 		players[clientnum]->entity->x = (tele_x << 4) + 8;
2502 		players[clientnum]->entity->y = (tele_y << 4) + 8;
2503 		// play sound effect
2504 		if ( type == 0 || type == 1 )
2505 		{
2506 			playSoundEntityLocal(players[clientnum]->entity, 96, 64);
2507 		}
2508 		else if ( type == 2 )
2509 		{
2510 			playSoundEntityLocal(players[clientnum]->entity, 154, 64);
2511 		}
2512 		return;
2513 	}
2514 
2515 	// delete entity
2516 	else if (!strncmp((char*)net_packet->data, "ENTD", 4))
2517 	{
2518 		Entity *entity = uidToEntity((int)SDLNet_Read32(&net_packet->data[4]));
2519 		if ( entity )
2520 		{
2521 			entity2 = newEntity(entity->sprite, 1, &removedEntities, nullptr);
2522 			if ( entity2 )
2523 			{
2524 				entity2->setUID(entity->getUID());
2525 				for ( j = 0; j < MAXPLAYERS; ++j )
2526 				{
2527 					if (entity == players[j]->entity )
2528 					{
2529 						if ( stats[j] )
2530 						{
2531 							for ( int effect = 0; effect < NUMEFFECTS; ++effect )
2532 							{
2533 								if ( effect != EFF_VAMPIRICAURA && effect != EFF_WITHDRAWAL && effect != EFF_SHAPESHIFT )
2534 								{
2535 									stats[j]->EFFECTS[effect] = false;
2536 									stats[j]->EFFECTS_TIMERS[effect] = 0;
2537 								}
2538 							}
2539 						}
2540 						players[j]->entity = nullptr;
2541 					}
2542 				}
2543 				if ( entity->light )
2544 				{
2545 					list_RemoveNode(entity->light->node);
2546 					entity->light = nullptr;
2547 				}
2548 				list_RemoveNode(entity->mynode);
2549 
2550 				// inform the server that we deleted the entity
2551 				strcpy((char*)net_packet->data, "ENTD");
2552 				net_packet->data[4] = clientnum;
2553 				SDLNet_Write32(entity2->getUID(), &net_packet->data[5]);
2554 				net_packet->address.host = net_server.host;
2555 				net_packet->address.port = net_server.port;
2556 				net_packet->len = 9;
2557 				sendPacket(net_sock, -1, net_packet, 0);
2558 			}
2559 		}
2560 		return;
2561 	}
2562 
2563 	// shake screen
2564 	else if (!strncmp((char*)net_packet->data, "SHAK", 4))
2565 	{
2566 		cameravars[clientnum].shakex += ((char)(net_packet->data[4])) / 100.f;
2567 		cameravars[clientnum].shakey += ((char)(net_packet->data[5]));
2568 		return;
2569 	}
2570 
2571 	// update armor quality
2572 	else if (!strncmp((char*)net_packet->data, "ARMR", 4))
2573 	{
2574 		switch ( net_packet->data[4] )
2575 		{
2576 			case 0:
2577 				item = stats[clientnum]->helmet;
2578 				break;
2579 			case 1:
2580 				item = stats[clientnum]->breastplate;
2581 				break;
2582 			case 2:
2583 				item = stats[clientnum]->gloves;
2584 				break;
2585 			case 3:
2586 				item = stats[clientnum]->shoes;
2587 				break;
2588 			case 4:
2589 				item = stats[clientnum]->shield;
2590 				break;
2591 			case 5:
2592 				item = stats[clientnum]->weapon;
2593 				break;
2594 			case 6:
2595 				item = stats[clientnum]->cloak;
2596 				break;
2597 			case 7:
2598 				item = stats[clientnum]->amulet;
2599 				break;
2600 			case 8:
2601 				item = stats[clientnum]->ring;
2602 				break;
2603 			case 9:
2604 				item = stats[clientnum]->mask;
2605 				break;
2606 			default:
2607 				item = NULL;
2608 				break;
2609 		}
2610 		if ( item != NULL )
2611 		{
2612 			if ( item->count > 1 )
2613 			{
2614 				newItem(item->type, item->status, item->beatitude, item->count - 1, item->appearance, item->identified, &stats[clientnum]->inventory);
2615 				item->count = 1;
2616 			}
2617 			if ( static_cast<int>(net_packet->data[5]) > EXCELLENT )
2618 			{
2619 				item->status = EXCELLENT;
2620 			}
2621 			else if ( static_cast<int>(net_packet->data[5]) < BROKEN )
2622 			{
2623 				item->status = BROKEN;
2624 				if ( net_packet->data[4] == 5 )
2625 				{
2626 					if ( client_classes[clientnum] == CLASS_MESMER )
2627 					{
2628 						if ( stats[clientnum]->weapon->type == MAGICSTAFF_CHARM )
2629 						{
2630 							bool foundCharmSpell = false;
2631 							for ( node_t* spellnode = stats[clientnum]->inventory.first; spellnode != nullptr; spellnode = spellnode->next )
2632 							{
2633 								Item* item = (Item*)spellnode->element;
2634 								if ( item && itemCategory(item) == SPELL_CAT )
2635 								{
2636 									spell_t* spell = getSpellFromItem(item);
2637 									if ( spell && spell->ID == SPELL_CHARM_MONSTER )
2638 									{
2639 										foundCharmSpell = true;
2640 										break;
2641 									}
2642 								}
2643 							}
2644 							if ( !foundCharmSpell )
2645 							{
2646 								steamAchievement("BARONY_ACH_WHAT_NOW");
2647 							}
2648 						}
2649 					}
2650 				}
2651 			}
2652 			else
2653 			{
2654 				item->status = static_cast<Status>(net_packet->data[5]);
2655 			}
2656 
2657 			// spellbooks in hand crumble to nothing.
2658 			if ( item->status == BROKEN && net_packet->data[4] == 4 && itemCategory(item) == SPELLBOOK )
2659 			{
2660 				consumeItem(item, clientnum);
2661 			}
2662 		}
2663 		return;
2664 	}
2665 
2666 	// steal armor (destroy it)
2667 	else if (!strncmp((char*)net_packet->data, "STLA", 4))
2668 	{
2669 		switch ( net_packet->data[4] )
2670 		{
2671 			case 0:
2672 				item = stats[clientnum]->helmet;
2673 				break;
2674 			case 1:
2675 				item = stats[clientnum]->breastplate;
2676 				break;
2677 			case 2:
2678 				item = stats[clientnum]->gloves;
2679 				break;
2680 			case 3:
2681 				item = stats[clientnum]->shoes;
2682 				break;
2683 			case 4:
2684 				item = stats[clientnum]->shield;
2685 				break;
2686 			case 5:
2687 				item = stats[clientnum]->weapon;
2688 				break;
2689 			case 6:
2690 				item = stats[clientnum]->cloak;
2691 				break;
2692 			case 7:
2693 				item = stats[clientnum]->amulet;
2694 				break;
2695 			case 8:
2696 				item = stats[clientnum]->ring;
2697 				break;
2698 			case 9:
2699 				item = stats[clientnum]->mask;
2700 				break;
2701 			default:
2702 				item = NULL;
2703 				break;
2704 		}
2705 		Item** slot = itemSlot(stats[clientnum], item);
2706 		if ( slot != NULL )
2707 		{
2708 			*slot = NULL;
2709 		}
2710 		if ( item )
2711 		{
2712 			list_RemoveNode(item->node);
2713 		}
2714 		return;
2715 	}
2716 
2717 	// damage indicator
2718 	else if (!strncmp((char*)net_packet->data, "DAMI", 4))
2719 	{
2720 		newDamageIndicator(SDLNet_Read32(&net_packet->data[4]), SDLNet_Read32(&net_packet->data[8]));
2721 		return;
2722 	}
2723 
2724 	// play sound position
2725 	else if (!strncmp((char*)net_packet->data, "SNDP", 4))
2726 	{
2727 		playSoundPos(SDLNet_Read32(&net_packet->data[4]), SDLNet_Read32(&net_packet->data[8]), SDLNet_Read32(&net_packet->data[12]), SDLNet_Read32(&net_packet->data[16]));
2728 		return;
2729 	}
2730 
2731 	// play sound global
2732 	else if (!strncmp((char*)net_packet->data, "SNDG", 4))
2733 	{
2734 		playSound(SDLNet_Read32(&net_packet->data[4]), SDLNet_Read32(&net_packet->data[8]));
2735 		return;
2736 	}
2737 
2738 	// play sound entity local
2739 	else if ( !strncmp((char*)net_packet->data, "SNEL", 4) )
2740 	{
2741 		Entity* tmp = uidToEntity(SDLNet_Read32(&net_packet->data[6]));
2742 		int sfx = SDLNet_Read16(&net_packet->data[4]);
2743 		if ( tmp )
2744 		{
2745 			if ( tmp->behavior == &actPlayer && mute_player_monster_sounds )
2746 			{
2747 				switch ( sfx )
2748 				{
2749 					case 95:
2750 					case 70:
2751 					case 322:
2752 					case 323:
2753 					case 324:
2754 					case 329:
2755 					case 332:
2756 					case 333:
2757 					case 291:
2758 					case 292:
2759 					case 293:
2760 					case 294:
2761 					case 60:
2762 					case 61:
2763 					case 62:
2764 					case 257:
2765 					case 258:
2766 					case 276:
2767 					case 277:
2768 					case 278:
2769 						// return early, don't play monster noises from players.
2770 						return;
2771 					default:
2772 						break;
2773 				}
2774 			}
2775 			playSoundEntityLocal(tmp, sfx, SDLNet_Read16(&net_packet->data[10]));
2776 		}
2777 		return;
2778 	}
2779 
2780 	// new light, shadowed
2781 	else if (!strncmp((char*)net_packet->data, "LITS", 4))
2782 	{
2783 		lightSphereShadow(SDLNet_Read16(&net_packet->data[4]), SDLNet_Read16(&net_packet->data[6]), SDLNet_Read16(&net_packet->data[8]), SDLNet_Read16(&net_packet->data[10]));
2784 		return;
2785 	}
2786 
2787 	// new light, unshadowed
2788 	else if (!strncmp((char*)net_packet->data, "LITU", 4))
2789 	{
2790 		lightSphere(SDLNet_Read16(&net_packet->data[4]), SDLNet_Read16(&net_packet->data[6]), SDLNet_Read16(&net_packet->data[8]), SDLNet_Read16(&net_packet->data[10]));
2791 		return;
2792 	}
2793 
2794 	// create wall
2795 	else if (!strncmp((char*)net_packet->data, "WALC", 4))
2796 	{
2797 		int y = SDLNet_Read16(&net_packet->data[6]);
2798 		int x = SDLNet_Read16(&net_packet->data[4]);
2799 		if ( x >= 0 && x < map.width && y >= 0 && y < map.height )
2800 		{
2801 			map.tiles[OBSTACLELAYER + y * MAPLAYERS + x * MAPLAYERS * map.height] = map.tiles[y * MAPLAYERS + x * MAPLAYERS * map.height];
2802 		}
2803 		return;
2804 	}
2805 
2806 	// destroy wall
2807 	else if (!strncmp((char*)net_packet->data, "WALD", 4))
2808 	{
2809 		int y = SDLNet_Read16(&net_packet->data[6]);
2810 		int x = SDLNet_Read16(&net_packet->data[4]);
2811 		if ( x >= 0 && x < map.width && y >= 0 && y < map.height )
2812 		{
2813 			map.tiles[OBSTACLELAYER + y * MAPLAYERS + x * MAPLAYERS * map.height] = 0;
2814 		}
2815 		return;
2816 	}
2817 
2818 	// destroy wall + ceiling
2819 	else if (!strncmp((char*)net_packet->data, "WACD", 4))
2820 	{
2821 		int y = SDLNet_Read16(&net_packet->data[6]);
2822 		int x = SDLNet_Read16(&net_packet->data[4]);
2823 		if ( x >= 0 && x < map.width && y >= 0 && y < map.height )
2824 		{
2825 			map.tiles[OBSTACLELAYER + y * MAPLAYERS + x * MAPLAYERS * map.height] = 0;
2826 			map.tiles[(MAPLAYERS - 1) + y * MAPLAYERS + x * MAPLAYERS * map.height] = 0;
2827 		}
2828 		return;
2829 	}
2830 
2831 	// monster music
2832 	else if (!strncmp((char*)net_packet->data, "MUSM", 3))
2833 	{
2834 		combat = (bool)net_packet->data[3];
2835 		return;
2836 	}
2837 
2838 	// get item
2839 	else if (!strncmp((char*)net_packet->data, "ITEM", 4))
2840 	{
2841 		item = newItem(static_cast<ItemType>(SDLNet_Read32(&net_packet->data[4])), static_cast<Status>(SDLNet_Read32(&net_packet->data[8])), SDLNet_Read32(&net_packet->data[12]), SDLNet_Read32(&net_packet->data[16]), SDLNet_Read32(&net_packet->data[20]), net_packet->data[28], NULL);
2842 		item->ownerUid = SDLNet_Read32(&net_packet->data[24]);
2843 		Item* pickedUp = itemPickup(clientnum, item);
2844 		free(item);
2845 		if ( players[clientnum] && players[clientnum]->entity )
2846 		{
2847 			if ( pickedUp && pickedUp->type == BOOMERANG && !stats[clientnum]->weapon && item->ownerUid == players[clientnum]->entity->getUID() )
2848 			{
2849 				useItem(pickedUp, clientnum);
2850 				if ( magicBoomerangHotbarSlot >= 0 )
2851 				{
2852 					hotbar[magicBoomerangHotbarSlot].item = pickedUp->uid;
2853 					for ( int i = 0; i < NUM_HOTBAR_SLOTS; ++i )
2854 					{
2855 						if ( i != magicBoomerangHotbarSlot && hotbar[i].item == pickedUp->uid )
2856 						{
2857 							hotbar[i].item = 0;
2858 						}
2859 					}
2860 				}
2861 			}
2862 		}
2863 		return;
2864 	}
2865 
2866 	// unequip and remove item
2867 	else if (!strncmp((char*)net_packet->data, "DROP", 4))
2868 	{
2869 		Item** armor = NULL;
2870 		switch ( net_packet->data[4] )
2871 		{
2872 			case 0:
2873 				armor = &stats[clientnum]->helmet;
2874 				break;
2875 			case 1:
2876 				armor = &stats[clientnum]->breastplate;
2877 				break;
2878 			case 2:
2879 				armor = &stats[clientnum]->gloves;
2880 				break;
2881 			case 3:
2882 				armor = &stats[clientnum]->shoes;
2883 				break;
2884 			case 4:
2885 				armor = &stats[clientnum]->shield;
2886 				break;
2887 			case 5:
2888 				armor = &stats[clientnum]->weapon;
2889 				break;
2890 			case 6:
2891 				armor = &stats[clientnum]->cloak;
2892 				break;
2893 			case 7:
2894 				armor = &stats[clientnum]->amulet;
2895 				break;
2896 			case 8:
2897 				armor = &stats[clientnum]->ring;
2898 				break;
2899 			case 9:
2900 				armor = &stats[clientnum]->mask;
2901 				break;
2902 		}
2903 		if ( !armor )
2904 		{
2905 			return;
2906 		}
2907 		if ( !(*armor) )
2908 		{
2909 			return;
2910 		}
2911 
2912 		if ( *armor == selectedItem )
2913 		{
2914 			selectedItem = nullptr;
2915 		}
2916 
2917 		if ( (*armor)->count > 1 )
2918 		{
2919 			(*armor)->count--;
2920 		}
2921 		else
2922 		{
2923 			if ( (*armor)->node )
2924 			{
2925 				list_RemoveNode((*armor)->node);
2926 			}
2927 			else
2928 			{
2929 				free(*armor);
2930 			}
2931 		}
2932 		*armor = NULL;
2933 		return;
2934 	}
2935 
2936 	// get gold
2937 	else if (!strncmp((char*)net_packet->data, "GOLD", 4))
2938 	{
2939 		stats[clientnum]->GOLD = SDLNet_Read32(&net_packet->data[4]);
2940 		return;
2941 	}
2942 
2943 	// open shop
2944 	else if (!strncmp((char*)net_packet->data, "SHOP", 4))
2945 	{
2946 		closeAllGUIs(DONT_CHANGE_SHOOTMODE, CLOSEGUI_DONT_CLOSE_SHOP);
2947 		openStatusScreen(GUI_MODE_SHOP, INVENTORY_MODE_ITEM);
2948 		shopkeeper = (Uint32)SDLNet_Read32(&net_packet->data[4]);
2949 		shopkeepertype = net_packet->data[8];
2950 		strcpy( shopkeepername_client, (char*)(&net_packet->data[9]) );
2951 		shopkeepername = shopkeepername_client;
2952 		shoptimer = ticks - 1;
2953 		shopspeech = language[194 + rand() % 3];
2954 		shopinventorycategory = 7;
2955 		sellitem = NULL;
2956 		shopitemscroll = 0;
2957 		//Initialize shop gamepad code here.
2958 		if ( shopinvitems[0] != nullptr )
2959 		{
2960 			selectedShopSlot = 0;
2961 			warpMouseToSelectedShopSlot();
2962 		}
2963 		else
2964 		{
2965 			selectedShopSlot = -1;
2966 		}
2967 		return;
2968 	}
2969 
2970 	// shop item
2971 	else if (!strncmp((char*)net_packet->data, "SHPI", 4))
2972 	{
2973 		if ( !shopInv )
2974 		{
2975 			return;
2976 		}
2977 		newItem(static_cast<ItemType>(SDLNet_Read32(&net_packet->data[4])), static_cast<Status>((char)net_packet->data[8]), (char)net_packet->data[9], (unsigned char)net_packet->data[10], SDLNet_Read32(&net_packet->data[11]), (bool)net_packet->data[15], shopInv);
2978 	}
2979 
2980 	// close shop
2981 	else if (!strncmp((char*)net_packet->data, "SHPC", 4))
2982 	{
2983 		shopkeeper = 0;
2984 		closeAllGUIs(CLOSEGUI_ENABLE_SHOOTMODE, CLOSEGUI_CLOSE_ALL);
2985 		list_FreeAll(shopInv);
2986 		//Clean up shop gamepad code here.
2987 		selectedShopSlot = -1;
2988 		return;
2989 	}
2990 
2991 	// textbox message
2992 	else if (!strncmp((char*)net_packet->data, "MSGS", 4))
2993 	{
2994 		if ( ticks != 1 )
2995 		{
2996 			Uint32 color = SDLNet_Read32(&net_packet->data[4]);
2997 			messagePlayerColor(clientnum, color, (char*)(&net_packet->data[8]));
2998 		}
2999 		for ( c = 0; c < MAXPLAYERS; c++ )
3000 		{
3001 			if ( !strncmp( (char*)(&net_packet->data[8]), stats[c]->name, std::min<size_t>(strlen(stats[c]->name), 10) ) )    //TODO: Why are size_t and int being compared?
3002 			{
3003 				if ( net_packet->data[8 + std::min<size_t>(strlen(stats[c]->name), 10)] == ':' )   //TODO: Why are size_t and int being compared?
3004 				{
3005 					playSound(238, 64);
3006 				}
3007 			}
3008 		}
3009 		if ( !strcmp((char*)(&net_packet->data[8]), language[577]) )    //TODO: Replace with a UDIE packet.
3010 		{
3011 			// this is how the client knows it died...
3012 			if ( players[clientnum] && players[clientnum]->entity && players[clientnum]->entity->playerCreatedDeathCam != 0 )
3013 			{
3014 				// don't spawn deathcam
3015 			}
3016 			else
3017 			{
3018 				Entity* entity = newEntity(-1, 1, map.entities, nullptr);
3019 				entity->x = cameras[clientnum].x * 16;
3020 				entity->y = cameras[clientnum].y * 16;
3021 				entity->z = -2;
3022 				entity->flags[NOUPDATE] = true;
3023 				entity->flags[PASSABLE] = true;
3024 				entity->flags[INVISIBLE] = true;
3025 				entity->behavior = &actDeathCam;
3026 				entity->skill[2] = clientnum;
3027 				entity->yaw = cameras[clientnum].ang;
3028 				entity->pitch = PI / 8;
3029 			}
3030 
3031 			//deleteSaveGame(multiplayer); // stops save scumming c: //Not here, because it'll make the game unresumable if the game crashes but not all players have died.
3032 
3033 			closeBookGUI();
3034 
3035 #ifdef SOUND
3036 			levelmusicplaying = true;
3037 			combatmusicplaying = false;
3038 			fadein_increment = default_fadein_increment * 4;
3039 			fadeout_increment = default_fadeout_increment * 4;
3040 			playmusic( sounds[209], false, true, false );
3041 #endif
3042 			combat = false;
3043 			assailant[clientnum] = false;
3044 			assailantTimer[clientnum] = 0;
3045 
3046 			if ( !(svFlags & SV_FLAG_KEEPINVENTORY) )
3047 			{
3048 				for ( node = stats[clientnum]->inventory.first; node != NULL; node = nextnode )
3049 				{
3050 					nextnode = node->next;
3051 					Item* item = (Item*)node->element;
3052 					if ( itemCategory(item) == SPELL_CAT )
3053 					{
3054 						continue;    // don't drop spells on death, stupid!
3055 					}
3056 					if ( itemIsEquipped(item, clientnum) )
3057 					{
3058 						Item** slot = itemSlot(stats[clientnum], item);
3059 						if ( slot != NULL )
3060 						{
3061 							*slot = NULL;
3062 						}
3063 						list_RemoveNode(node);
3064 						continue;
3065 					}
3066 					strcpy((char*)net_packet->data, "DIEI");
3067 					SDLNet_Write32((Uint32)item->type, &net_packet->data[4]);
3068 					SDLNet_Write32((Uint32)item->status, &net_packet->data[8]);
3069 					SDLNet_Write32((Uint32)item->beatitude, &net_packet->data[12]);
3070 					SDLNet_Write32((Uint32)item->count, &net_packet->data[16]);
3071 					SDLNet_Write32((Uint32)item->appearance, &net_packet->data[20]);
3072 					net_packet->data[24] = item->identified;
3073 					net_packet->data[25] = clientnum;
3074 					net_packet->data[26] = (Uint8)cameras[clientnum].x;
3075 					net_packet->data[27] = (Uint8)cameras[clientnum].y;
3076 					net_packet->address.host = net_server.host;
3077 					net_packet->address.port = net_server.port;
3078 					net_packet->len = 28;
3079 					sendPacketSafe(net_sock, -1, net_packet, 0);
3080 					list_RemoveNode(node);
3081 				}
3082 				stats[clientnum]->helmet = NULL;
3083 				stats[clientnum]->breastplate = NULL;
3084 				stats[clientnum]->gloves = NULL;
3085 				stats[clientnum]->shoes = NULL;
3086 				stats[clientnum]->shield = NULL;
3087 				stats[clientnum]->weapon = NULL;
3088 				stats[clientnum]->cloak = NULL;
3089 				stats[clientnum]->amulet = NULL;
3090 				stats[clientnum]->ring = NULL;
3091 				stats[clientnum]->mask = NULL;
3092 			}
3093 			else
3094 			{
3095 				// to not soft lock at Herx
3096 				for ( node = stats[clientnum]->inventory.first; node != NULL; node = nextnode )
3097 				{
3098 					nextnode = node->next;
3099 					Item* item = (Item*)node->element;
3100 					if ( item->type == ARTIFACT_ORB_PURPLE )
3101 					{
3102 						strcpy((char*)net_packet->data, "DIEI");
3103 						SDLNet_Write32((Uint32)item->type, &net_packet->data[4]);
3104 						SDLNet_Write32((Uint32)item->status, &net_packet->data[8]);
3105 						SDLNet_Write32((Uint32)item->beatitude, &net_packet->data[12]);
3106 						SDLNet_Write32((Uint32)item->count, &net_packet->data[16]);
3107 						SDLNet_Write32((Uint32)item->appearance, &net_packet->data[20]);
3108 						net_packet->data[24] = item->identified;
3109 						net_packet->data[25] = clientnum;
3110 						net_packet->data[26] = (Uint8)cameras[clientnum].x;
3111 						net_packet->data[27] = (Uint8)cameras[clientnum].y;
3112 						net_packet->address.host = net_server.host;
3113 						net_packet->address.port = net_server.port;
3114 						net_packet->len = 28;
3115 						sendPacketSafe(net_sock, -1, net_packet, 0);
3116 						list_RemoveNode(node);
3117 						break;
3118 					}
3119 				}
3120 			}
3121 
3122 			for ( node_t* mapNode = map.creatures->first; mapNode != nullptr; mapNode = mapNode->next )
3123 			{
3124 				Entity* mapCreature = (Entity*)mapNode->element;
3125 				if ( mapCreature )
3126 				{
3127 					mapCreature->monsterEntityRenderAsTelepath = 0; // do a final pass to undo any telepath rendering.
3128 				}
3129 			}
3130 		}
3131 		else if ( !strcmp((char*)(&net_packet->data[8]), language[1109]) )
3132 		{
3133 			// ... or lived
3134 			stats[clientnum]->HP = stats[clientnum]->MAXHP * 0.5;
3135 			stats[clientnum]->MP = stats[clientnum]->MAXMP * 0.5;
3136 			stats[clientnum]->HUNGER = 500;
3137 			for ( c = 0; c < NUMEFFECTS; c++ )
3138 			{
3139 				if ( !(c == EFF_VAMPIRICAURA && stats[clientnum]->EFFECTS_TIMERS[c] == -2)
3140 					&& c != EFF_WITHDRAWAL && c != EFF_SHAPESHIFT )
3141 				{
3142 					stats[clientnum]->EFFECTS[c] = false;
3143 					stats[clientnum]->EFFECTS_TIMERS[c] = 0;
3144 				}
3145 			}
3146 		}
3147 		else if ( !strncmp((char*)(&net_packet->data[8]), language[1114], 28) )
3148 		{
3149 #ifdef MUSIC
3150 			fadein_increment = default_fadein_increment * 20;
3151 			fadeout_increment = default_fadeout_increment * 5;
3152 			playmusic( sounds[175], false, true, false );
3153 #endif
3154 		}
3155 		else if ( (strstr((char*)(&net_packet->data[8]), language[1160])) != NULL )
3156 		{
3157 			for ( c = 0; c < MAXPLAYERS; c++ )
3158 			{
3159 				if ( !strncmp(stats[c]->name, (char*)(&net_packet->data[8]), strlen(stats[c]->name)) )
3160 				{
3161 					if (players[clientnum] && players[clientnum]->entity && players[c] && players[c]->entity)
3162 					{
3163 						double tangent = atan2(players[clientnum]->entity->y - players[c]->entity->y, players[clientnum]->entity->x - players[c]->entity->x);
3164 						players[clientnum]->entity->vel_x += cos(tangent);
3165 						players[clientnum]->entity->vel_y += sin(tangent);
3166 					}
3167 					break;
3168 				}
3169 			}
3170 		}
3171 		return;
3172 	}
3173 
3174 	// update magic
3175 	else if (!strncmp((char*)net_packet->data, "UPMP", 4))
3176 	{
3177 		stats[clientnum]->MP = SDLNet_Read32(&net_packet->data[4]);
3178 		return;
3179 	}
3180 
3181 	// update effects flags
3182 	else if (!strncmp((char*)net_packet->data, "UPEF", 4))
3183 	{
3184 		for (c = 0; c < NUMEFFECTS; c++)
3185 		{
3186 			if ( net_packet->data[4 + c / 8]&power(2, c - (c / 8) * 8) )
3187 			{
3188 				stats[clientnum]->EFFECTS[c] = true;
3189 				if ( net_packet->data[9 + c / 8] & power(2, c - (c / 8) * 8) ) // use these bits to denote if duration is low.
3190 				{
3191 					stats[clientnum]->EFFECTS_TIMERS[c] = 1;
3192 				}
3193 			}
3194 			else
3195 			{
3196 				stats[clientnum]->EFFECTS[c] = false;
3197 				if ( stats[clientnum]->EFFECTS_TIMERS[c] > 0 )
3198 				{
3199 					stats[clientnum]->EFFECTS_TIMERS[c] = 0;
3200 				}
3201 			}
3202 		}
3203 		return;
3204 	}
3205 
3206 	// update entity stat flag
3207 	else if ( !strncmp((char*)net_packet->data, "ENSF", 4) )
3208 	{
3209 		Entity *entity = uidToEntity((int)SDLNet_Read32(&net_packet->data[4]));
3210 		if ( entity )
3211 		{
3212 			if ( entity->getStats() )
3213 			{
3214 				entity->getStats()->MISC_FLAGS[net_packet->data[8]] = SDLNet_Read32(&net_packet->data[9]);
3215 			}
3216 		}
3217 		return;
3218 	}
3219 
3220 	// update attributes
3221 	else if (!strncmp((char*)net_packet->data, "ATTR", 4))
3222 	{
3223 		stats[clientnum]->STR = (Sint8)net_packet->data[5];
3224 		stats[clientnum]->DEX = (Sint8)net_packet->data[6];
3225 		stats[clientnum]->CON = (Sint8)net_packet->data[7];
3226 		stats[clientnum]->INT = (Sint8)net_packet->data[8];
3227 		stats[clientnum]->PER = (Sint8)net_packet->data[9];
3228 		stats[clientnum]->CHR = (Sint8)net_packet->data[10];
3229 		stats[clientnum]->EXP = (Sint8)net_packet->data[11];
3230 		stats[clientnum]->LVL = (Sint8)net_packet->data[12];
3231 		stats[clientnum]->HP = (Sint16)SDLNet_Read16(&net_packet->data[13]);
3232 		stats[clientnum]->MAXHP = (Sint16)SDLNet_Read16(&net_packet->data[15]);
3233 		stats[clientnum]->MP = (Sint16)SDLNet_Read16(&net_packet->data[17]);
3234 		stats[clientnum]->MAXMP = (Sint16)SDLNet_Read16(&net_packet->data[19]);
3235 		return;
3236 	}
3237 
3238 	// level up icon timers, sets second row of icons if double stat gain is rolled.
3239 	else if (!strncmp((char*)net_packet->data, "LVLI", 4))
3240 	{
3241 		// Note - set to 250 ticks, higher values will require resending/using 16 bit data.
3242 		stats[clientnum]->PLAYER_LVL_STAT_TIMER[STAT_STR] = (Uint8)net_packet->data[5];
3243 		stats[clientnum]->PLAYER_LVL_STAT_TIMER[STAT_DEX] = (Uint8)net_packet->data[6];
3244 		stats[clientnum]->PLAYER_LVL_STAT_TIMER[STAT_CON] = (Uint8)net_packet->data[7];
3245 		stats[clientnum]->PLAYER_LVL_STAT_TIMER[STAT_INT] = (Uint8)net_packet->data[8];
3246 		stats[clientnum]->PLAYER_LVL_STAT_TIMER[STAT_PER] = (Uint8)net_packet->data[9];
3247 		stats[clientnum]->PLAYER_LVL_STAT_TIMER[STAT_CHR] = (Uint8)net_packet->data[10];
3248 		stats[clientnum]->PLAYER_LVL_STAT_TIMER[STAT_STR + NUMSTATS] = (Uint8)net_packet->data[11];
3249 		stats[clientnum]->PLAYER_LVL_STAT_TIMER[STAT_DEX + NUMSTATS] = (Uint8)net_packet->data[12];
3250 		stats[clientnum]->PLAYER_LVL_STAT_TIMER[STAT_CON + NUMSTATS] = (Uint8)net_packet->data[13];
3251 		stats[clientnum]->PLAYER_LVL_STAT_TIMER[STAT_INT + NUMSTATS] = (Uint8)net_packet->data[14];
3252 		stats[clientnum]->PLAYER_LVL_STAT_TIMER[STAT_PER + NUMSTATS] = (Uint8)net_packet->data[15];
3253 		stats[clientnum]->PLAYER_LVL_STAT_TIMER[STAT_CHR + NUMSTATS] = (Uint8)net_packet->data[16];
3254 		return;
3255 	}
3256 
3257 	// killed a monster
3258 	else if (!strncmp((char*)net_packet->data, "MKIL", 4))
3259 	{
3260 		kills[net_packet->data[4]]++;
3261 		return;
3262 	}
3263 
3264 	// update skill
3265 	else if (!strncmp((char*)net_packet->data, "SKIL", 4))
3266 	{
3267 		stats[clientnum]->PROFICIENCIES[net_packet->data[5]] = net_packet->data[6];
3268 
3269 		int statBonusSkill = getStatForProficiency(net_packet->data[5]);
3270 
3271 		if ( statBonusSkill >= STAT_STR )
3272 		{
3273 			// stat has chance for bonus point if the relevant proficiency has been trained.
3274 			// write the last proficiency that effected the skill.
3275 			stats[clientnum]->PLAYER_LVL_STAT_BONUS[statBonusSkill] = net_packet->data[5];
3276 		}
3277 
3278 		if ( net_packet->data[5] == PRO_ALCHEMY )
3279 		{
3280 			GenericGUI.alchemyLearnRecipeOnLevelUp(stats[clientnum]->PROFICIENCIES[net_packet->data[5]]);
3281 		}
3282 		return;
3283 	}
3284 
3285 	//Add spell.
3286 	else if ( !strncmp((char*)net_packet->data, "ASPL", 4) )
3287 	{
3288 		if ( net_packet->len != 6 ) //Need to get the actual length, not reported...Should be a generic check at the top of the function, if len != actual len, then abort.
3289 		{
3290 			printlog("Received malformed ASPL packet.");
3291 			return;
3292 		}
3293 
3294 		addSpell(net_packet->data[5], clientnum);
3295 
3296 		return;
3297 	}
3298 
3299 	// update hunger
3300 	else if (!strncmp((char*)net_packet->data, "HNGR", 4))
3301 	{
3302 		stats[clientnum]->HUNGER = (Sint32)SDLNet_Read32(&net_packet->data[4]);
3303 		return;
3304 	}
3305 
3306 	// update player stat values
3307 	else if ( !strncmp((char*)net_packet->data, "STAT", 4) )
3308 	{
3309 		Sint32 buffer = 0;
3310 		for ( int i = 0; i < MAXPLAYERS; ++i )
3311 		{
3312 			buffer = (Sint32)SDLNet_Read32(&net_packet->data[4 + i * 8]);
3313 			stats[i]->MAXHP = buffer & 0xFFFF;
3314 			stats[i]->HP = (buffer >> 16) & 0xFFFF;
3315 			buffer = (Sint32)SDLNet_Read32(&net_packet->data[8 + i * 8]);
3316 			stats[i]->MAXMP = buffer & 0xFFFF;
3317 			stats[i]->MP = (buffer >> 16) & 0xFFFF;
3318 		}
3319 	}
3320 
3321 	else if ( !strncmp((char*)net_packet->data, "COND", 4) )
3322 	{
3323 		int conduct = SDLNet_Read16(&net_packet->data[4]);
3324 		int value = SDLNet_Read16(&net_packet->data[6]);
3325 		conductGameChallenges[conduct] = value;
3326 		//messagePlayer(clientnum, "received %d %d, set to %d", conduct, value, conductGameChallenges[conduct]);
3327 		return;
3328 	}
3329 
3330 	// update player statistics
3331 	else if ( !strncmp((char*)net_packet->data, "GPST", 4) )
3332 	{
3333 		int gameplayStat = SDLNet_Read32(&net_packet->data[4]);
3334 		int changeval = SDLNet_Read32(&net_packet->data[8]);
3335 		if ( gameplayStat == STATISTICS_TEMPT_FATE )
3336 		{
3337 			if ( gameStatistics[STATISTICS_TEMPT_FATE] == -1 )
3338 			{
3339 				// don't change, completed task.
3340 			}
3341 			else
3342 			{
3343 				if ( changeval == 5 )
3344 				{
3345 					gameStatistics[gameplayStat] = changeval;
3346 				}
3347 				else if ( changeval == 1 && gameStatistics[gameplayStat] > 0 )
3348 				{
3349 					gameStatistics[gameplayStat] = -1;
3350 				}
3351 			}
3352 		}
3353 		else if ( gameplayStat == STATISTICS_FORUM_TROLL )
3354 		{
3355 			if ( changeval == AchievementObserver::FORUM_TROLL_BREAK_WALL )
3356 			{
3357 				int walls = gameStatistics[gameplayStat] & 0xFF;
3358 				walls = std::min(walls + 1, 3);
3359 				gameStatistics[gameplayStat] = gameStatistics[gameplayStat] & 0xFFFFFF00;
3360 				gameStatistics[gameplayStat] |= walls;
3361 			}
3362 			else if ( changeval == AchievementObserver::FORUM_TROLL_RECRUIT_TROLL )
3363 			{
3364 				int trolls = (gameStatistics[gameplayStat] >> 8) & 0xFF;
3365 				trolls = std::min(trolls + 1, 3);
3366 				gameStatistics[gameplayStat] = gameStatistics[gameplayStat] & 0xFFFF00FF;
3367 				gameStatistics[gameplayStat] |= (trolls << 8);
3368 			}
3369 			else if ( changeval == AchievementObserver::FORUM_TROLL_FEAR )
3370 			{
3371 				int fears = (gameStatistics[gameplayStat] >> 16) & 0xFF;
3372 				fears = std::min(fears + 1, 3);
3373 				gameStatistics[gameplayStat] = gameStatistics[gameplayStat] & 0xFF00FFFF;
3374 				gameStatistics[gameplayStat] |= (fears << 16);
3375 			}
3376 		}
3377 		else if ( gameplayStat == STATISTICS_POP_QUIZ_1 || gameplayStat == STATISTICS_POP_QUIZ_2 )
3378 		{
3379 			int spellID = changeval;
3380 			if ( spellID >= 32 )
3381 			{
3382 				spellID -= 32;
3383 				int shifted = (1 << spellID);
3384 				gameStatistics[gameplayStat] |= shifted;
3385 			}
3386 			else
3387 			{
3388 				int shifted = (1 << spellID);
3389 				gameStatistics[gameplayStat] |= shifted;
3390 			}
3391 		}
3392 		else
3393 		{
3394 			gameStatistics[gameplayStat] += changeval;
3395 		}
3396 		//messagePlayer(clientnum, "received: %d, %d, val: %d", gameplayStat, changeval, gameStatistics[gameplayStat]);
3397 	}
3398 
3399 	// update player levels
3400 	else if ( !strncmp((char*)net_packet->data, "UPLV", 4) )
3401 	{
3402 		Sint32 buffer = SDLNet_Read32(&net_packet->data[4]);
3403 		for ( int i = 0; i < MAXPLAYERS; ++i )
3404 		{
3405 			stats[i]->LVL = static_cast<Sint32>((buffer >> (i * 8) ) & 0xFF);
3406 		}
3407 	}
3408 
3409 	// current game level
3410 	else if (!strncmp((char*)net_packet->data, "LVLC", 4) || !strncmp((char*)net_packet->data, "LVLR", 4) )
3411 	{
3412 		if ( strncmp((char*)net_packet->data, "LVLR", 4) )
3413 		{
3414 			if ( currentlevel == net_packet->data[13] && secretlevel == net_packet->data[4] )
3415 			{
3416 				// the server's just doing a routine check
3417 				return;
3418 			}
3419 		}
3420 
3421 		if ( net_packet->data[14] != 0 )
3422 		{
3423 			// loading a custom map name.
3424 			char buf[128] = "";
3425 			strcpy(buf, (char*)&net_packet->data[14]);
3426 			loadCustomNextMap = buf;
3427 		}
3428 
3429 		if ( introstage == 9
3430 			|| introstage == 11 + MOVIE_MIDGAME_BAPHOMET_HUMAN_AUTOMATON
3431 			|| introstage == 11 + MOVIE_MIDGAME_BAPHOMET_MONSTERS
3432 			|| introstage == 11 + MOVIE_MIDGAME_HERX_MONSTERS )
3433 		{
3434 			thirdendmovietime = 0;
3435 			thirdendmoviestage = 0;
3436 			DLCendmovieStageAndTime[MOVIE_MIDGAME_HERX_MONSTERS][MOVIE_STAGE] = 0;
3437 			DLCendmovieStageAndTime[MOVIE_MIDGAME_HERX_MONSTERS][MOVIE_TIME] = 0;
3438 			DLCendmovieStageAndTime[MOVIE_MIDGAME_BAPHOMET_MONSTERS][MOVIE_STAGE] = 0;
3439 			DLCendmovieStageAndTime[MOVIE_MIDGAME_BAPHOMET_MONSTERS][MOVIE_TIME] = 0;
3440 			DLCendmovieStageAndTime[MOVIE_MIDGAME_BAPHOMET_HUMAN_AUTOMATON][MOVIE_STAGE] = 0;
3441 			DLCendmovieStageAndTime[MOVIE_MIDGAME_BAPHOMET_HUMAN_AUTOMATON][MOVIE_TIME] = 0;
3442 
3443 			movie = false; // allow normal pause screen.
3444 			introstage = 1; // return to normal game functionality
3445 			pauseGame(1, false); // unpause game
3446 		}
3447 
3448 		// hack to fix these things from breaking everything...
3449 		hudarm = nullptr;
3450 		hudweapon = nullptr;
3451 		magicLeftHand = nullptr;
3452 		magicRightHand = nullptr;
3453 
3454 		// stop all sounds
3455 #ifdef USE_FMOD
3456 		if ( sound_group )
3457 		{
3458 			FMOD_ChannelGroup_Stop(sound_group);
3459 		}
3460 		if ( soundAmbient_group )
3461 		{
3462 			FMOD_ChannelGroup_Stop(soundAmbient_group);
3463 		}
3464 		if ( soundEnvironment_group )
3465 		{
3466 			FMOD_ChannelGroup_Stop(soundEnvironment_group);
3467 		}
3468 #elif defined USE_OPENAL
3469 		if ( sound_group )
3470 		{
3471 			OPENAL_ChannelGroup_Stop(sound_group);
3472 		}
3473 		if ( soundAmbient_group )
3474 		{
3475 			OPENAL_ChannelGroup_Stop(soundAmbient_group);
3476 		}
3477 		if ( soundEnvironment_group )
3478 		{
3479 			OPENAL_ChannelGroup_Stop(soundEnvironment_group);
3480 		}
3481 #endif
3482 		if ( openedChest[clientnum] )
3483 		{
3484 			closeChestClientside();
3485 		}
3486 
3487 		// show loading message
3488 #define LOADSTR language[709]
3489 		loading = true;
3490 		drawClearBuffers();
3491 		int w, h;
3492 		TTF_SizeUTF8(ttf16, LOADSTR, &w, &h);
3493 		ttfPrintText(ttf16, (xres - w) / 2, (yres - h) / 2, LOADSTR);
3494 
3495 		GO_SwapBuffers(screen);
3496 
3497 		// unlock some steam achievements
3498 		if ( !secretlevel )
3499 		{
3500 			switch ( currentlevel )
3501 			{
3502 			case 0:
3503 				steamAchievement("BARONY_ACH_ENTER_THE_DUNGEON");
3504 				break;
3505 			default:
3506 				break;
3507 			}
3508 		}
3509 
3510 		// setup level change
3511 		printlog("Received order to change level.\n");
3512 		currentlevel = static_cast<Sint8>(net_packet->data[13]);
3513 
3514 		if ( !secretlevel )
3515 		{
3516 			switch ( currentlevel )
3517 			{
3518 				case 5:
3519 					steamAchievement("BARONY_ACH_TWISTY_PASSAGES");
3520 					break;
3521 				case 10:
3522 					steamAchievement("BARONY_ACH_JUNGLE_FEVER");
3523 					break;
3524 				case 15:
3525 					steamAchievement("BARONY_ACH_SANDMAN");
3526 					break;
3527 				case 30:
3528 					steamAchievement("BARONY_ACH_SPELUNKY");
3529 					break;
3530 				case 35:
3531 					if ( ((completionTime / TICKS_PER_SECOND) / 60) <= 45 )
3532 					{
3533 						conductGameChallenges[CONDUCT_BLESSED_BOOTS_SPEED] = 1;
3534 					}
3535 					break;
3536 				default:
3537 					break;
3538 			}
3539 		}
3540 
3541 		list_FreeAll(&removedEntities);
3542 		for ( node = map.entities->first; node != nullptr; node = node->next )
3543 		{
3544 			entity = (Entity*)node->element;
3545 			entity2 = newEntity(entity->sprite, 1, &removedEntities, nullptr);
3546 			entity2->setUID(entity->getUID());
3547 		}
3548 		for ( i = 0; i < MAXPLAYERS; ++i )
3549 		{
3550 			list_FreeAll(&stats[i]->FOLLOWERS);
3551 		}
3552 
3553 		// load next level
3554 		darkmap = false;
3555 		secretlevel = net_packet->data[4];
3556 		mapseed = SDLNet_Read32(&net_packet->data[5]);
3557 		/*Uint32 oldtime = SDL_GetTicks();
3558 		while( SDLNet_TCP_Recv(net_tcpsock, net_packet->data, 4)!=4 ) {
3559 			if( SDL_GetTicks()-oldtime>10000 )
3560 				printlog("warning: game has taken more than 10 seconds to receive map seed\n");
3561 		}*/
3562 		numplayers = 0;
3563 		entity_uids = (Uint32)SDLNet_Read32(&net_packet->data[9]);
3564 		printlog("Received map seed: %d. Entity UID start: %d\n", mapseed, entity_uids);
3565 
3566 		gameplayCustomManager.readFromFile();
3567 
3568 		int checkMapHash = -1;
3569 		physfsLoadMapFile(currentlevel, mapseed, false, &checkMapHash);
3570 		if ( checkMapHash == 0 )
3571 		{
3572 			conductGameChallenges[CONDUCT_MODDED] = 1;
3573 		}
3574 
3575 		minimapPings.clear(); // clear minimap pings
3576 		globalLightModifierActive = GLOBAL_LIGHT_MODIFIER_STOPPED;
3577 
3578 		// clear follower menu entities.
3579 		FollowerMenu.closeFollowerMenuGUI(true);
3580 
3581 		numplayers = 0;
3582 		assignActions(&map);
3583 		generatePathMaps();
3584 		for ( node = map.entities->first; node != nullptr; node = nextnode )
3585 		{
3586 			nextnode = node->next;
3587 			Entity* entity = (Entity*)node->element;
3588 			if ( entity->flags[NOUPDATE] )
3589 			{
3590 				list_RemoveNode(entity->mynode);    // we're anticipating this entity data from server
3591 			}
3592 		}
3593 
3594 		saveGame();
3595 
3596 		// (special) unlock temple achievement
3597 		if ( secretlevel && currentlevel == 8 )
3598 		{
3599 			steamAchievement("BARONY_ACH_TRICKS_AND_TRAPS");
3600 		}
3601 
3602 		printlog("Done.\n");
3603 
3604 		if ( !secretlevel )
3605 		{
3606 			messagePlayer(clientnum, language[710], currentlevel);
3607 		}
3608 		else
3609 		{
3610 			messagePlayer(clientnum, language[711], map.name);
3611 		}
3612 		if ( !secretlevel )
3613 		{
3614 			switch ( currentlevel )
3615 			{
3616 				case 2:
3617 					messagePlayer(clientnum, language[712]);
3618 					break;
3619 				case 3:
3620 					messagePlayer(clientnum, language[713]);
3621 					break;
3622 				case 7:
3623 					messagePlayer(clientnum, language[714]);
3624 					break;
3625 				case 8:
3626 					messagePlayer(clientnum, language[715]);
3627 					break;
3628 				case 11:
3629 					messagePlayer(clientnum, language[716]);
3630 					break;
3631 				case 13:
3632 					messagePlayer(clientnum, language[717]);
3633 					break;
3634 				case 16:
3635 					messagePlayer(clientnum, language[718]);
3636 					break;
3637 				case 18:
3638 					messagePlayer(clientnum, language[719]);
3639 					break;
3640 				default:
3641 					break;
3642 			}
3643 		}
3644 		if ( MFLAG_DISABLETELEPORT || MFLAG_DISABLEOPENING )
3645 		{
3646 			messagePlayer(clientnum, language[2382]);
3647 		}
3648 		if ( MFLAG_DISABLELEVITATION )
3649 		{
3650 			messagePlayer(clientnum, language[2383]);
3651 		}
3652 		if ( MFLAG_DISABLEDIGGING )
3653 		{
3654 			messagePlayer(clientnum, language[2450]);
3655 		}
3656 		if ( !strncmp(map.name, "Mages Guild", 11) )
3657 		{
3658 			messagePlayer(clientnum, language[2599]);
3659 		}
3660 		loading = false;
3661 		fadeout = false;
3662 		fadealpha = 255;
3663 		return;
3664 	}
3665 
3666 	// lead a monster
3667 	else if (!strncmp((char*)net_packet->data, "LEAD", 4))
3668 	{
3669 		Uint32* uidnum = (Uint32*) malloc(sizeof(Uint32));
3670 		*uidnum = (Uint32)SDLNet_Read32(&net_packet->data[4]);
3671 		node_t* node = list_AddNodeLast(&stats[clientnum]->FOLLOWERS);
3672 		node->element = uidnum;
3673 		node->deconstructor = &defaultDeconstructor;
3674 		node->size = sizeof(Uint32);
3675 
3676 		Entity* monster = uidToEntity(*uidnum);
3677 		if ( monster )
3678 		{
3679 			if ( !monster->clientsHaveItsStats )
3680 			{
3681 				monster->giveClientStats();
3682 			}
3683 			if ( monster->clientStats )
3684 			{
3685 				strcpy(monster->clientStats->name, (char*)&net_packet->data[8]);
3686 			}
3687 			if ( !FollowerMenu.recentEntity )
3688 			{
3689 				FollowerMenu.recentEntity = monster;
3690 			}
3691 		}
3692 		return;
3693 	}
3694 
3695 	// remove a monster from followers list
3696 	else if ( !strncmp((char*)net_packet->data, "LDEL", 4) )
3697 	{
3698 		Uint32 uidnum = (Uint32)SDLNet_Read32(&net_packet->data[4]);
3699 		if ( stats[clientnum] )
3700 		{
3701 			for ( node_t* allyNode = stats[clientnum]->FOLLOWERS.first; allyNode != nullptr; allyNode = allyNode->next )
3702 			{
3703 				if ( (Uint32*)allyNode->element && *((Uint32*)allyNode->element) == uidnum )
3704 				{
3705 					if ( FollowerMenu.recentEntity && (FollowerMenu.recentEntity->getUID() == 0
3706 						|| FollowerMenu.recentEntity->getUID() == uidnum) )
3707 					{
3708 						FollowerMenu.recentEntity = nullptr;
3709 					}
3710 					if ( FollowerMenu.followerToCommand == uidToEntity(uidnum) )
3711 					{
3712 						FollowerMenu.closeFollowerMenuGUI();
3713 					}
3714 					list_RemoveNode(allyNode);
3715 					break;
3716 				}
3717 			}
3718 		}
3719 	}
3720 
3721 	// update client's follower data on level up or initial follow.
3722 	else if ( !strncmp((char*)net_packet->data, "NPCI", 4) )
3723 	{
3724 		Uint32 uidnum = (Uint32)SDLNet_Read32(&net_packet->data[4]);
3725 		Entity* monster = uidToEntity(uidnum);
3726 		if ( monster )
3727 		{
3728 			if ( !monster->clientsHaveItsStats )
3729 			{
3730 				monster->giveClientStats();
3731 			}
3732 			if ( monster->clientStats )
3733 			{
3734 				monster->clientStats->LVL = net_packet->data[8];
3735 				monster->clientStats->HP = SDLNet_Read16(&net_packet->data[9]);
3736 				monster->clientStats->MAXHP = SDLNet_Read16(&net_packet->data[11]);
3737 				monster->clientStats->type = static_cast<Monster>(net_packet->data[13]);
3738 			}
3739 		}
3740 	}
3741 
3742 	// update client's follower hp/maxhp data at intervals
3743 	else if ( !strncmp((char*)net_packet->data, "NPCU", 4) )
3744 	{
3745 		Uint32 uidnum = (Uint32)SDLNet_Read32(&net_packet->data[4]);
3746 		Entity* monster = uidToEntity(uidnum);
3747 		if ( monster )
3748 		{
3749 			if ( !monster->clientsHaveItsStats )
3750 			{
3751 				monster->giveClientStats();
3752 			}
3753 			if ( monster->clientStats )
3754 			{
3755 				monster->clientStats->HP = SDLNet_Read16(&net_packet->data[8]);
3756 				monster->clientStats->MAXHP = SDLNet_Read16(&net_packet->data[10]);
3757 			}
3758 		}
3759 	}
3760 
3761 	// bless my equipment
3762 	else if (!strncmp((char*)net_packet->data, "BLES", 4))
3763 	{
3764 		if ( stats[clientnum]->helmet )
3765 		{
3766 			stats[clientnum]->helmet->beatitude++;
3767 		}
3768 		if ( stats[clientnum]->breastplate )
3769 		{
3770 			stats[clientnum]->breastplate->beatitude++;
3771 		}
3772 		if ( stats[clientnum]->gloves )
3773 		{
3774 			stats[clientnum]->gloves->beatitude++;
3775 		}
3776 		if ( stats[clientnum]->shoes )
3777 		{
3778 			stats[clientnum]->shoes->beatitude++;
3779 		}
3780 		if ( stats[clientnum]->shield )
3781 		{
3782 			stats[clientnum]->shield->beatitude++;
3783 		}
3784 		if ( stats[clientnum]->weapon )
3785 		{
3786 			stats[clientnum]->weapon->beatitude++;
3787 		}
3788 		if ( stats[clientnum]->cloak )
3789 		{
3790 			stats[clientnum]->cloak->beatitude++;
3791 		}
3792 		if ( stats[clientnum]->amulet )
3793 		{
3794 			stats[clientnum]->amulet->beatitude++;
3795 		}
3796 		if ( stats[clientnum]->ring )
3797 		{
3798 			stats[clientnum]->ring->beatitude++;
3799 		}
3800 		if ( stats[clientnum]->mask )
3801 		{
3802 			stats[clientnum]->mask->beatitude++;
3803 		}
3804 		return;
3805 	}
3806 
3807 	// bless one piece of my equipment
3808 	else if (!strncmp((char*)net_packet->data, "BLE1", 4))
3809 	{
3810 		Uint32 chosen = static_cast<Uint32>(SDLNet_Read32(&net_packet->data[4]));
3811 		switch ( chosen )
3812 		{
3813 			case 0:
3814 				if ( stats[clientnum]->helmet )
3815 				{
3816 					stats[clientnum]->helmet->beatitude++;
3817 				}
3818 				break;
3819 			case 1:
3820 				if ( stats[clientnum]->breastplate )
3821 				{
3822 					stats[clientnum]->breastplate->beatitude++;
3823 				}
3824 				break;
3825 			case 2:
3826 				if ( stats[clientnum]->gloves )
3827 				{
3828 					stats[clientnum]->gloves->beatitude++;
3829 				}
3830 				break;
3831 			case 3:
3832 				if ( stats[clientnum]->shoes )
3833 				{
3834 					stats[clientnum]->shoes->beatitude++;
3835 				}
3836 				break;
3837 			case 4:
3838 				if ( stats[clientnum]->shield )
3839 				{
3840 					stats[clientnum]->shield->beatitude++;
3841 				}
3842 				break;
3843 			case 5:
3844 				if ( stats[clientnum]->weapon )
3845 				{
3846 					stats[clientnum]->weapon->beatitude++;
3847 				}
3848 				break;
3849 			case 6:
3850 				if ( stats[clientnum]->cloak )
3851 				{
3852 					stats[clientnum]->cloak->beatitude++;
3853 				}
3854 				break;
3855 			case 7:
3856 				if ( stats[clientnum]->amulet )
3857 				{
3858 					stats[clientnum]->amulet->beatitude++;
3859 				}
3860 				break;
3861 			case 8:
3862 				if ( stats[clientnum]->ring )
3863 				{
3864 					stats[clientnum]->ring->beatitude++;
3865 				}
3866 				break;
3867 			case 9:
3868 				if ( stats[clientnum]->mask )
3869 				{
3870 					stats[clientnum]->mask->beatitude++;
3871 				}
3872 				break;
3873 			default:
3874 				break;
3875 		}
3876 		return;
3877 	}
3878 
3879 	// update entity appearance (sprite)
3880 	else if (!strncmp((char*)net_packet->data, "ENTA", 4))
3881 	{
3882 		Entity *entity = uidToEntity((int)SDLNet_Read32(&net_packet->data[4]));
3883 		if ( entity )
3884 		{
3885 			entity->sprite = SDLNet_Read32(&net_packet->data[8]);
3886 		}
3887 		return;
3888 	}
3889 
3890 	// monster summon
3891 	else if (!strncmp((char*)net_packet->data, "SUMM", 4))
3892 	{
3893 		Monster monster = (Monster)SDLNet_Read32(&net_packet->data[4]);
3894 		Sint32 x = (Sint32)SDLNet_Read32(&net_packet->data[8]);
3895 		Sint32 y = (Sint32)SDLNet_Read32(&net_packet->data[12]);
3896 		Uint32 uid = SDLNet_Read32(&net_packet->data[16]);
3897 		summonMonsterClient(monster, x, y, uid);
3898 		return;
3899 	}
3900 
3901 	// monster summon
3902 	else if ( !strncmp((char*)net_packet->data, "SUMS", 4) )
3903 	{
3904 		if ( stats[clientnum] )
3905 		{
3906 			stats[clientnum]->playerSummonLVLHP = (Sint32)SDLNet_Read32(&net_packet->data[4]);
3907 			stats[clientnum]->playerSummonSTRDEXCONINT = (Sint32)SDLNet_Read32(&net_packet->data[8]);
3908 			stats[clientnum]->playerSummonPERCHR = (Sint32)SDLNet_Read32(&net_packet->data[12]);
3909 			stats[clientnum]->playerSummon2LVLHP = (Sint32)SDLNet_Read32(&net_packet->data[16]);
3910 			stats[clientnum]->playerSummon2STRDEXCONINT = (Sint32)SDLNet_Read32(&net_packet->data[20]);
3911 			stats[clientnum]->playerSummon2PERCHR = (Sint32)SDLNet_Read32(&net_packet->data[24]);
3912 		}
3913 		return;
3914 	}
3915 
3916 	//Multiplayer chest code (client).
3917 	else if ( !strncmp((char*)net_packet->data, "CHST", 4) )
3918 	{
3919 		if ( openedChest[clientnum] )
3920 		{
3921 			//Close the chest.
3922 			closeChestClientside();
3923 		}
3924 
3925 		Entity *entity = uidToEntity((int)SDLNet_Read32(&net_packet->data[4]));
3926 		if ( entity )
3927 		{
3928 			openedChest[clientnum] = entity; //Set the opened chest to this.
3929 			if ( removecursegui_active )
3930 			{
3931 				closeRemoveCurseGUI();
3932 			}
3933 			GenericGUI.closeGUI();
3934 			identifygui_active = false;
3935 			list_FreeAll(&chestInv);
3936 			chestInv.first = nullptr;
3937 			chestInv.last = nullptr;
3938 			openStatusScreen(GUI_MODE_INVENTORY, INVENTORY_MODE_ITEM);
3939 		}
3940 		return;
3941 	}
3942 
3943 	//Add an item to the chest.
3944 	else if (!strncmp((char*)net_packet->data, "CITM", 4))
3945 	{
3946 		Item* newitem = NULL;
3947 		if ( (newitem = (Item*) malloc(sizeof(Item))) == NULL)
3948 		{
3949 			printlog( "failed to allocate memory for new item!\n" );
3950 			return; //TODO: Error or something.
3951 		}
3952 		newitem->node = NULL;
3953 		newitem->type = static_cast<ItemType>(SDLNet_Read32(&net_packet->data[4]));
3954 		newitem->status = static_cast<Status>(SDLNet_Read32(&net_packet->data[8]));
3955 		newitem->beatitude = SDLNet_Read32(&net_packet->data[12]);
3956 		newitem->count = SDLNet_Read32(&net_packet->data[16]);
3957 		newitem->appearance = SDLNet_Read32(&net_packet->data[20]);
3958 		if ( net_packet->data[24])   //TODO: Is this right?
3959 		{
3960 			newitem->identified = true;
3961 		}
3962 		else
3963 		{
3964 			newitem->identified = false;
3965 		}
3966 
3967 		addItemToChestClientside(newitem);
3968 		return;
3969 	}
3970 
3971 	//Close the chest.
3972 	else if (!strncmp((char*)net_packet->data, "CCLS", 4))
3973 	{
3974 		closeChestClientside();
3975 		return;
3976 	}
3977 
3978 	//Open up the GUI to identify an item.
3979 	else if (!strncmp((char*)net_packet->data, "IDEN", 4))
3980 	{
3981 		//identifygui_mode = true;
3982 		identifygui_active = true;
3983 		identifygui_appraising = false;
3984 		shootmode = false;
3985 		openStatusScreen(GUI_MODE_INVENTORY, INVENTORY_MODE_ITEM); // Reset the GUI to the inventory.
3986 		if ( removecursegui_active )
3987 		{
3988 			closeRemoveCurseGUI();
3989 		}
3990 		GenericGUI.closeGUI();
3991 		if ( openedChest[clientnum] )
3992 		{
3993 			openedChest[clientnum]->closeChest();
3994 		}
3995 
3996 		//Initialize Identify GUI game controller code here.
3997 		initIdentifyGUIControllerCode();
3998 
3999 		return;
4000 	}
4001 
4002 	// Open up the Remove Curse GUI
4003 	else if ( !strncmp((char*)net_packet->data, "CRCU", 4) )
4004 	{
4005 		removecursegui_active = true;
4006 		shootmode = false;
4007 		openStatusScreen(GUI_MODE_INVENTORY, INVENTORY_MODE_ITEM); // Reset the GUI to the inventory.
4008 
4009 		if ( identifygui_active )
4010 		{
4011 			CloseIdentifyGUI();
4012 		}
4013 		GenericGUI.closeGUI();
4014 
4015 		if ( openedChest[clientnum] )
4016 		{
4017 			openedChest[clientnum]->closeChest();
4018 		}
4019 
4020 		initRemoveCurseGUIControllerCode();
4021 
4022 		return;
4023 	}
4024 
4025 	//Add a spell to the channeled spells list.
4026 	else if (!strncmp((char*)net_packet->data, "CHAN", 4))
4027 	{
4028 		spell_t* thespell = getSpellFromID(SDLNet_Read32(&net_packet->data[5]));
4029 		node = list_AddNodeLast(&channeledSpells[clientnum]);
4030 		node->element = thespell;
4031 		node->size = sizeof(spell_t);
4032 		//node->deconstructor = &spellDeconstructor_Channeled;
4033 		node->deconstructor = &emptyDeconstructor;
4034 		((spell_t*)(node->element))->sustain_node = node;
4035 		return;
4036 	}
4037 
4038 	//Remove a spell from the channeled spells list.
4039 	else if (!strncmp((char*)net_packet->data, "UNCH", 4))
4040 	{
4041 		spell_t* thespell = getSpellFromID(SDLNet_Read32(&net_packet->data[5]));
4042 		if (spellInList(&channeledSpells[clientnum], thespell))
4043 		{
4044 			node_t* nextnode;
4045 			for (node = channeledSpells[clientnum].first; node; node = nextnode)
4046 			{
4047 				nextnode = node->next;
4048 				spell_t* spell_search = (spell_t*)node->element;
4049 				if (spell_search->ID == thespell->ID)
4050 				{
4051 					list_RemoveNode(node);
4052 					node = NULL;
4053 				}
4054 			}
4055 		}
4056 		return;
4057 	}
4058 
4059 	//Map the magic. I mean magic the map. I mean magically map the level (client).
4060 	else if (!strncmp((char*)net_packet->data, "MMAP", 4))
4061 	{
4062 		spell_magicMap(clientnum);
4063 		return;
4064 	}
4065 
4066 	else if ( !strncmp((char*)net_packet->data, "MFOD", 4) )
4067 	{
4068 		mapFoodOnLevel(clientnum);
4069 		return;
4070 	}
4071 
4072 	else if ( !strncmp((char*)net_packet->data, "TKIT", 4) )
4073 	{
4074 		GenericGUI.tinkeringKitDegradeOnUse(clientnum);
4075 		return;
4076 	}
4077 
4078 	// boss death
4079 	else if ( !strncmp((char*)net_packet->data, "BDTH", 4) )
4080 	{
4081 		for ( node = map.entities->first; node != nullptr; node = node->next )
4082 		{
4083 			Entity* entity = (Entity*)node->element;
4084 			if ( strstr(map.name, "Hell") )
4085 			{
4086 				if ( entity->behavior == &actWinningPortal )
4087 				{
4088 					//entity->flags[INVISIBLE] = false;
4089 				}
4090 			}
4091 			else if ( strstr(map.name, "Boss") )
4092 			{
4093 				if ( entity->behavior == &actPedestalBase )
4094 				{
4095 					entity->pedestalInit = 1;
4096 				}
4097 			}
4098 		}
4099 		if ( strstr(map.name, "Hell") )
4100 		{
4101 			int x, y;
4102 			for ( y = map.height / 2 - 1; y < map.height / 2 + 2; y++ )
4103 			{
4104 				for ( x = 3; x < map.width / 2; x++ )
4105 				{
4106 					if ( !map.tiles[y * MAPLAYERS + x * MAPLAYERS * map.height] )
4107 					{
4108 						map.tiles[y * MAPLAYERS + x * MAPLAYERS * map.height] = 72;
4109 					}
4110 				}
4111 			}
4112 		}
4113 		return;
4114 	}
4115 
4116 	// update svFlags
4117 	else if (!strncmp((char*)net_packet->data, "SVFL", 4))
4118 	{
4119 		svFlags = SDLNet_Read32(&net_packet->data[4]);
4120 		return;
4121 	}
4122 
4123 	// kick
4124 	else if (!strncmp((char*)net_packet->data, "KICK", 4))
4125 	{
4126 		button_t* button;
4127 
4128 		printlog("kicked from server.\n");
4129 		pauseGame(2, 0);
4130 
4131 		// close current window
4132 		buttonCloseSubwindow(NULL);
4133 		for ( node = button_l.first; node != NULL; node = nextnode )
4134 		{
4135 			nextnode = node->next;
4136 			button = (button_t*)node->element;
4137 			if ( button->focused )
4138 			{
4139 				list_RemoveNode(button->node);
4140 			}
4141 		}
4142 
4143 		// create new window
4144 		subwindow = 1;
4145 		subx1 = xres / 2 - 256;
4146 		subx2 = xres / 2 + 256;
4147 		suby1 = yres / 2 - 56;
4148 		suby2 = yres / 2 + 56;
4149 		strcpy(subtext, language[1127]);
4150 
4151 		// close button
4152 		button = newButton();
4153 		strcpy(button->label, "x");
4154 		button->x = subx2 - 20;
4155 		button->y = suby1;
4156 		button->sizex = 20;
4157 		button->sizey = 20;
4158 		button->action = &buttonCloseAndEndGameConfirm;
4159 		button->visible = 1;
4160 		button->focused = 1;
4161 
4162 		// okay button
4163 		button = newButton();
4164 		strcpy(button->label, language[732]);
4165 		button->x = subx2 - (subx2 - subx1) / 2 - 28;
4166 		button->y = suby2 - 28;
4167 		button->sizex = 56;
4168 		button->sizey = 20;
4169 		button->action = &buttonCloseAndEndGameConfirm;
4170 		button->visible = 1;
4171 		button->focused = 1;
4172 		button->key = SDL_SCANCODE_RETURN;
4173 
4174 		client_disconnected[0] = true;
4175 		return;
4176 	}
4177 
4178 	// win the game
4179 	else if (!strncmp((char*)net_packet->data, "WING", 4))
4180 	{
4181 		victory = net_packet->data[4];
4182 		subwindow = 0;
4183 		introstage = 5; // prepares win game sequence
4184 		fadeout = true;
4185 		if ( !intro )
4186 		{
4187 			pauseGame(2, false);
4188 		}
4189 		return;
4190 	}
4191 
4192 	// mid game movie
4193 	else if ( !strncmp((char*)net_packet->data, "MIDG", 4) )
4194 	{
4195 		subwindow = 0;
4196 		fadeout = true;
4197 		if ( !intro )
4198 		{
4199 			pauseGame(2, false);
4200 		}
4201 		introstage = net_packet->data[4]; // prepares mid game sequence
4202 		return;
4203 	}
4204 
4205 	else if ( !strncmp((char*)net_packet->data, "PMAP", 4) )
4206 	{
4207 		MinimapPing newPing(ticks, net_packet->data[4], net_packet->data[5], net_packet->data[6]);
4208 		minimapPingAdd(newPing);
4209 	}
4210 	else if ( !strncmp((char*)net_packet->data, "DASH", 4) )
4211 	{
4212 		if ( players[clientnum] && players[clientnum]->entity && stats[clientnum] )
4213 		{
4214 			real_t vel = sqrt(pow(players[clientnum]->entity->vel_y, 2) + pow(players[clientnum]->entity->vel_x, 2));
4215 			players[clientnum]->entity->monsterKnockbackVelocity = std::min(2.25, std::max(1.0, vel));
4216 			players[clientnum]->entity->monsterKnockbackTangentDir = atan2(players[clientnum]->entity->vel_y, players[clientnum]->entity->vel_x);
4217 			if ( vel < 0.01 )
4218 			{
4219 				players[clientnum]->entity->monsterKnockbackTangentDir = players[clientnum]->entity->yaw + PI;
4220 			}
4221 		}
4222 	}
4223 
4224 	// get item
4225 	else if ( !strncmp((char*)net_packet->data, "ITEQ", 4) )
4226 	{
4227 		item = newItem(static_cast<ItemType>(SDLNet_Read32(&net_packet->data[4])), static_cast<Status>(SDLNet_Read32(&net_packet->data[8])), SDLNet_Read32(&net_packet->data[12]), SDLNet_Read32(&net_packet->data[16]), SDLNet_Read32(&net_packet->data[20]), net_packet->data[28], NULL);
4228 		item->ownerUid = SDLNet_Read32(&net_packet->data[24]);
4229 		Item* pickedUp = itemPickup(clientnum, item);
4230 		free(item);
4231 		if ( players[clientnum] && players[clientnum]->entity && pickedUp )
4232 		{
4233 			bool oldIntro = intro;
4234 			intro = true;
4235 			useItem(pickedUp, clientnum);
4236 			intro = oldIntro;
4237 		}
4238 		return;
4239 	}
4240 
4241 	// update attributes from script
4242 	else if ( !strncmp((char*)net_packet->data, "SCRU", 4) )
4243 	{
4244 		if ( net_packet->data[25] )
4245 		{
4246 			bool clearStats = false;
4247 			if ( net_packet->data[26] )
4248 			{
4249 				clearStats = true;
4250 			}
4251 			textSourceScript.playerClearInventory(clearStats);
4252 		}
4253 		stats[clientnum]->STR = (Sint8)net_packet->data[5];
4254 		stats[clientnum]->DEX = (Sint8)net_packet->data[6];
4255 		stats[clientnum]->CON = (Sint8)net_packet->data[7];
4256 		stats[clientnum]->INT = (Sint8)net_packet->data[8];
4257 		stats[clientnum]->PER = (Sint8)net_packet->data[9];
4258 		stats[clientnum]->CHR = (Sint8)net_packet->data[10];
4259 		stats[clientnum]->EXP = (Sint8)net_packet->data[11];
4260 		stats[clientnum]->LVL = (Sint8)net_packet->data[12];
4261 		stats[clientnum]->HP = (Sint16)SDLNet_Read16(&net_packet->data[13]);
4262 		stats[clientnum]->MAXHP = (Sint16)SDLNet_Read16(&net_packet->data[15]);
4263 		stats[clientnum]->MP = (Sint16)SDLNet_Read16(&net_packet->data[17]);
4264 		stats[clientnum]->MAXMP = (Sint16)SDLNet_Read16(&net_packet->data[19]);
4265 		stats[clientnum]->GOLD = (Sint32)SDLNet_Read32(&net_packet->data[21]);
4266 		for ( int i = 0; i < NUMPROFICIENCIES; ++i )
4267 		{
4268 			stats[clientnum]->PROFICIENCIES[i] = (Sint8)net_packet->data[27 + i];
4269 		}
4270 		return;
4271 	}
4272 
4273 	// update class from script
4274 	else if ( !strncmp((char*)net_packet->data, "SCRC", 4) )
4275 	{
4276 		for ( int c = 0; c < MAXPLAYERS; ++c )
4277 		{
4278 			client_classes[c] = net_packet->data[4 + c];
4279 			if ( c == clientnum )
4280 			{
4281 				bool oldIntro = intro;
4282 				intro = true;
4283 				initClass(clientnum);
4284 				intro = oldIntro;
4285 			}
4286 		}
4287 		return;
4288 	}
4289 
4290 	// game restart
4291 	if (!strncmp((char*)net_packet->data, "BARONY_GAME_START", 17))
4292 	{
4293 		if ( !intro )
4294 		{
4295 			// intro is true if starting from main menu, otherwise we're restarting the game.
4296 			// set the main menu camera to the player camera coordinates if restarting midgame.
4297 			menucam.x = cameras[clientnum].x;
4298 			menucam.y = cameras[clientnum].y;
4299 			menucam.z = cameras[clientnum].z;
4300 			menucam.ang = cameras[clientnum].ang;
4301 			menucam.vang = cameras[clientnum].vang;
4302 		}
4303 		intro = true;
4304 		client_disconnected[0] = true;
4305 		svFlags = SDLNet_Read32(&net_packet->data[17]);
4306 		uniqueGameKey = SDLNet_Read32(&net_packet->data[21]);
4307 		buttonCloseSubwindow(NULL);
4308 		numplayers = 0;
4309 		introstage = 3;
4310 		if ( net_packet->data[25] == 0 )
4311 		{
4312 			loadingsavegame = 0; // the server said we're not loading a saved game.
4313 		}
4314 		fadeout = true;
4315 		return;
4316 	}
4317 
4318 	// delete multiplayer save
4319 	if (!strncmp((char*)net_packet->data, "DSAV", 4))
4320 	{
4321 		if ( multiplayer == CLIENT )
4322 		{
4323 			deleteSaveGame(multiplayer);
4324 		}
4325 		return;
4326 	}
4327 }
4328 
4329 /*-------------------------------------------------------------------------------
4330 
4331 	clientHandleMessages
4332 
4333 	Parses messages received from the server
4334 
4335 -------------------------------------------------------------------------------*/
4336 
clientHandleMessages(Uint32 framerateBreakInterval)4337 void clientHandleMessages(Uint32 framerateBreakInterval)
4338 {
4339 #ifdef STEAMWORKS
4340 	if (!directConnect && !net_handler)
4341 	{
4342 		net_handler = new NetHandler();
4343 		if ( !disableMultithreadedSteamNetworking )
4344 		{
4345 			net_handler->initializeMultithreadedPacketHandling();
4346 		}
4347 	}
4348 #elif defined USE_EOS
4349 	if ( !directConnect && !net_handler )
4350 	{
4351 		net_handler = new NetHandler();
4352 	}
4353 #endif
4354 
4355 	if (!directConnect)
4356 	{
4357 #if defined(STEAMWORKS) || defined(USE_EOS)
4358 		if ( LobbyHandler.getP2PType() == LobbyHandler_t::LobbyServiceType::LOBBY_STEAM )
4359 		{
4360 #ifdef STEAMWORKS
4361 			//Steam stuff goes here.
4362 			if ( disableMultithreadedSteamNetworking )
4363 			{
4364 				steamPacketThread(static_cast<void*>(net_handler));
4365 			}
4366 #endif
4367 		}
4368 		else if ( LobbyHandler.getP2PType() == LobbyHandler_t::LobbyServiceType::LOBBY_CROSSPLAY )
4369 		{
4370 #if defined USE_EOS
4371 			EOSPacketThread(static_cast<void*>(net_handler));
4372 #endif
4373 		}
4374 		SteamPacketWrapper* packet = nullptr;
4375 
4376 		if ( logCheckMainLoopTimers )
4377 		{
4378 			DebugStats.messagesT1 = std::chrono::high_resolution_clock::now();
4379 			DebugStats.handlePacketStartLoop = true;
4380 		}
4381 
4382 		while (packet = net_handler->getGamePacket())
4383 		{
4384 			memcpy(net_packet->data, packet->data(), packet->len());
4385 			net_packet->len = packet->len();
4386 
4387 			clientHandlePacket(); //Uses net_packet.
4388 
4389 			if ( logCheckMainLoopTimers )
4390 			{
4391 				DebugStats.messagesT2WhileLoop = std::chrono::high_resolution_clock::now();
4392 				DebugStats.handlePacketStartLoop = false;
4393 			}
4394 			delete packet;
4395 
4396 			if ( !disableFPSLimitOnNetworkMessages && !frameRateLimit(framerateBreakInterval, false) )
4397 			{
4398 				if ( logCheckMainLoopTimers )
4399 				{
4400 					printlog("[NETWORK]: Incoming messages exceeded given cycle time, packets remaining: %d", net_handler->game_packets.size());
4401 				}
4402 				break;
4403 			}
4404 		}
4405 #endif
4406 	}
4407 	else
4408 	{
4409 		//Direct-connect goes here.
4410 
4411 		// receive packets from server
4412 		while (SDLNet_UDP_Recv(net_sock, net_packet))
4413 		{
4414 			// filter out broken packets
4415 			if ( !net_packet->data[0] )
4416 			{
4417 				continue;
4418 			}
4419 
4420 			clientHandlePacket();
4421 		}
4422 	}
4423 }
4424 
4425 /*-------------------------------------------------------------------------------
4426 
4427 	serverHandlePacket
4428 
4429 	Called by serverHandleMessages. Does the actual handling of a packet.
4430 
4431 -------------------------------------------------------------------------------*/
4432 
serverHandlePacket()4433 void serverHandlePacket()
4434 {
4435 	if (handleSafePacket())
4436 	{
4437 		return;
4438 	}
4439 
4440 	node_t* node;
4441 	Entity* entity;
4442 	int c = 0;
4443 	Uint32 j;
4444 	Item* item;
4445 	double dx, dy, velx, vely, yaw, pitch, dist;
4446 	deleteent_t* deleteent;
4447 
4448 #ifdef PACKETINFO
4449 	char packetinfo[NET_PACKET_SIZE];
4450 	strncpy( packetinfo, (char*)net_packet->data, net_packet->len );
4451 	packetinfo[net_packet->len] = 0;
4452 	printlog("info: server packet: %s\n", packetinfo);
4453 #endif
4454 
4455 	// keep alive
4456 	if (!strncmp((char*)net_packet->data, "KPAL", 4))
4457 	{
4458 		client_keepalive[net_packet->data[4]] = ticks;
4459 		return;
4460 	}
4461 
4462 	// ping
4463 	else if (!strncmp((char*)net_packet->data, "PING", 4))
4464 	{
4465 		j = net_packet->data[4];
4466 		if ( j <= 0 )
4467 		{
4468 			return;
4469 		}
4470 		if ( client_disconnected[j] )
4471 		{
4472 			return;
4473 		}
4474 		strcpy((char*)net_packet->data, "PING");
4475 		net_packet->address.host = net_clients[j - 1].host;
4476 		net_packet->address.port = net_clients[j - 1].port;
4477 		net_packet->len = 5;
4478 		sendPacketSafe(net_sock, -1, net_packet, j - 1);
4479 		return;
4480 	}
4481 
4482 	// pause game
4483 	else if (!strncmp((char*)net_packet->data, "PAUS", 4))
4484 	{
4485 		messagePlayer(clientnum, language[1118], stats[net_packet->data[4]]->name);
4486 		j = net_packet->data[4];
4487 		pauseGame(2, j);
4488 		return;
4489 	}
4490 
4491 	// unpause game
4492 	else if (!strncmp((char*)net_packet->data, "UNPS", 4))
4493 	{
4494 		messagePlayer(clientnum, language[1119], stats[net_packet->data[4]]->name);
4495 		j = net_packet->data[4];
4496 		pauseGame(1, j);
4497 		return;
4498 	}
4499 
4500 	// check entity existence
4501 	else if (!strncmp((char*)net_packet->data, "ENTE", 4))
4502 	{
4503 		int x = net_packet->data[4];
4504 		if ( x <= 0 )
4505 		{
4506 			return;
4507 		}
4508 		Uint32 uid = SDLNet_Read32(&net_packet->data[5]);
4509 		Entity* entity = uidToEntity(uid);
4510 		if ( entity )
4511 		{
4512 			return; // found entity.
4513 		}
4514 		// else reply with entity deleted.
4515 		strcpy((char*)net_packet->data, "ENTD");
4516 		SDLNet_Write32(uid, &net_packet->data[4]);
4517 		net_packet->address.host = net_clients[x - 1].host;
4518 		net_packet->address.port = net_clients[x - 1].port;
4519 		net_packet->len = 8;
4520 		sendPacketSafe(net_sock, -1, net_packet, x - 1);
4521 		return;
4522 	}
4523 
4524 	// client request item details.
4525 	else if ( !strncmp((char*)net_packet->data, "ITMU", 4) )
4526 	{
4527 		int x = net_packet->data[4];
4528 		if ( x <= 0 )
4529 		{
4530 			return;
4531 		}
4532 		Uint32 uid = SDLNet_Read32(&net_packet->data[5]);
4533 		Entity* entity = uidToEntity(uid);
4534 		if ( entity )
4535 		{
4536 			strcpy((char*)net_packet->data, "ITMU");
4537 			SDLNet_Write32(uid, &net_packet->data[4]);
4538 			SDLNet_Write32(entity->skill[10], &net_packet->data[8]); // item type
4539 			SDLNet_Write32(entity->skill[11], &net_packet->data[12]); // status
4540 			SDLNet_Write32(entity->skill[12], &net_packet->data[16]); // beatitude
4541 			net_packet->address.host = net_clients[x - 1].host;
4542 			net_packet->address.port = net_clients[x - 1].port;
4543 			net_packet->len = 20;
4544 			sendPacketSafe(net_sock, -1, net_packet, x - 1);
4545 			return; // found entity.
4546 		}
4547 	}
4548 
4549 	// player move
4550 	else if (!strncmp((char*)net_packet->data, "PMOV", 4))
4551 	{
4552 		int player = net_packet->data[4];
4553 		if ( player < 0 || player >= MAXPLAYERS )
4554 		{
4555 			return;
4556 		}
4557 		client_keepalive[player] = ticks;
4558 		if (players[player] == nullptr || players[player]->entity == nullptr)
4559 		{
4560 			return;
4561 		}
4562 
4563 		// check if the info is outdated
4564 		if ( net_packet->data[5] != currentlevel || net_packet->data[18] != secretlevel )
4565 		{
4566 			return;
4567 		}
4568 
4569 		// get info from client
4570 		dx = ((Sint16)SDLNet_Read16(&net_packet->data[6])) / 32.0;
4571 		dy = ((Sint16)SDLNet_Read16(&net_packet->data[8])) / 32.0;
4572 		velx = ((Sint16)SDLNet_Read16(&net_packet->data[10])) / 128.0;
4573 		vely = ((Sint16)SDLNet_Read16(&net_packet->data[12])) / 128.0;
4574 		yaw = ((Sint16)SDLNet_Read16(&net_packet->data[14])) / 128.0;
4575 		pitch = ((Sint16)SDLNet_Read16(&net_packet->data[16])) / 128.0;
4576 
4577 		// update rotation
4578 		players[player]->entity->yaw = yaw;
4579 		players[player]->entity->pitch = pitch;
4580 
4581 		// update player's internal velocity variables
4582 		players[player]->entity->vel_x = velx; // PLAYER_VELX
4583 		players[player]->entity->vel_y = vely; // PLAYER_VELY
4584 
4585 		// store old coordinates
4586 		// since this function runs more often than actPlayer runs, we need to keep track of the accumulated position in new_x/new_y
4587 		real_t ox = players[player]->entity->x;
4588 		real_t oy = players[player]->entity->y;
4589 		players[player]->entity->x = players[player]->entity->new_x;
4590 		players[player]->entity->y = players[player]->entity->new_y;
4591 
4592 		// calculate distance
4593 		dx -= players[player]->entity->x;
4594 		dy -= players[player]->entity->y;
4595 		dist = sqrt( dx * dx + dy * dy );
4596 
4597 		// move player with collision detection
4598 		real_t result = clipMove(&players[player]->entity->x, &players[player]->entity->y, dx, dy, players[player]->entity);
4599 		if ( result < dist - .025 )
4600 		{
4601 			// player encountered obstacle on path
4602 			// stop updating position on server side and send client corrected position
4603 			j = net_packet->data[4];
4604 			if ( j > 0 )
4605 			{
4606 				strcpy((char*)net_packet->data, "PMOV");
4607 				SDLNet_Write16((Sint16)(players[j]->entity->x * 32), &net_packet->data[4]);
4608 				SDLNet_Write16((Sint16)(players[j]->entity->y * 32), &net_packet->data[6]);
4609 				net_packet->address.host = net_clients[j - 1].host;
4610 				net_packet->address.port = net_clients[j - 1].port;
4611 				net_packet->len = 8;
4612 				sendPacket(net_sock, -1, net_packet, j - 1);
4613 			}
4614 		}
4615 
4616 		// clipMove sent any corrections to the client, now let's save the updated coordinates.
4617 		players[player]->entity->new_x = players[player]->entity->x;
4618 		players[player]->entity->new_y = players[player]->entity->y;
4619 		// return x/y to their original state as this can update more than actPlayer and causes stuttering. use new_x/new_y in actPlayer.
4620 		players[player]->entity->x = ox;
4621 		players[player]->entity->y = oy;
4622 
4623 		// update the players' head and mask as these will otherwise wait until actPlayer to update their rotation. stops clipping.
4624 		node_t* tmpNode = nullptr;
4625 		int bodypartNum = 0;
4626 		for ( bodypartNum = 0, tmpNode = players[player]->entity->children.first; tmpNode; tmpNode = tmpNode->next, bodypartNum++ )
4627 		{
4628 			if ( bodypartNum == 0 )
4629 			{
4630 				// hudweapon case
4631 				continue;
4632 			}
4633 
4634 			Entity* limb = (Entity*)tmpNode->element;
4635 			if ( limb )
4636 			{
4637 				// adjust headgear/mask yaw/pitch variations as these do not update always.
4638 				if ( bodypartNum == 9 || bodypartNum == 10 )
4639 				{
4640 					limb->x = players[player]->entity->x;
4641 					limb->y = players[player]->entity->y;
4642 					limb->pitch = players[player]->entity->pitch;
4643 					limb->yaw = players[player]->entity->yaw;
4644 				}
4645 			}
4646 		}
4647 
4648 		return;
4649 	}
4650 
4651 	// tried to update
4652 	else if (!strncmp((char*)net_packet->data, "NOUP", 4))
4653 	{
4654 		Uint32 uid = SDLNet_Read32(&net_packet->data[5]);
4655 		Entity* entity = uidToEntity(uid);
4656 		if ( entity )
4657 		{
4658 			entity->flags[UPDATENEEDED] = false;
4659 		}
4660 		return;
4661 	}
4662 
4663 	// client deleted entity
4664 	else if (!strncmp((char*)net_packet->data, "ENTD", 4))
4665 	{
4666 		for ( node = entitiesToDelete[net_packet->data[4]].first; node != NULL; node = node->next )
4667 		{
4668 			deleteent = (deleteent_t*)node->element;
4669 			if ( deleteent->uid == SDLNet_Read32(&net_packet->data[5]) )
4670 			{
4671 				list_RemoveNode(node);
4672 				break;
4673 			}
4674 		}
4675 		return;
4676 	}
4677 
4678 	// clicked entity in range
4679 	else if (!strncmp((char*)net_packet->data, "CKIR", 4)
4680 		|| !strncmp((char*)net_packet->data, "SALV", 4)
4681 		|| !strncmp((char*)net_packet->data, "RATF", 4) )
4682 	{
4683 		client_keepalive[net_packet->data[4]] = ticks;
4684 		Uint32 uid = SDLNet_Read32(&net_packet->data[5]);
4685 		Entity* entity = uidToEntity(uid);
4686 		if ( entity )
4687 		{
4688 			if ( (entity->behavior == &actItem || entity->behavior == &actTorch || entity->behavior == &actCrystalShard)
4689 				&& !strncmp((char*)net_packet->data, "SALV", 4) )
4690 			{
4691 				// auto salvage this item.
4692 				if ( players[net_packet->data[4]] && players[net_packet->data[4]]->entity )
4693 				{
4694 					entity->itemAutoSalvageByPlayer = static_cast<Sint32>(players[net_packet->data[4]]->entity->getUID());
4695 				}
4696 			}
4697 			else if ( entity->behavior == &actItem && !strncmp((char*)net_packet->data, "RATF", 4) )
4698 			{
4699 				achievementObserver.playerAchievements[net_packet->data[4]].rat5000secondRule.insert(uid);
4700 			}
4701 			client_selected[net_packet->data[4]] = entity;
4702 			inrange[net_packet->data[4]] = true;
4703 		}
4704 		return;
4705 	}
4706 
4707 	// clicked entity out of range
4708 	else if (!strncmp((char*)net_packet->data, "CKOR", 4))
4709 	{
4710 		Uint32 uid = SDLNet_Read32(&net_packet->data[5]);
4711 		Entity* entity = uidToEntity(uid);
4712 		if ( entity )
4713 		{
4714 			client_selected[net_packet->data[4]] = entity;
4715 			inrange[net_packet->data[4]] = false;
4716 		}
4717 		return;
4718 	}
4719 
4720 	// disconnect
4721 	else if (!strncmp((char*)net_packet->data, "DISCONNECT", 10))
4722 	{
4723 		int playerDisconnected = net_packet->data[10];
4724 		char shortname[32] = { 0 };
4725 		strncpy(shortname, stats[playerDisconnected]->name, 22);
4726 		client_disconnected[playerDisconnected] = true;
4727 		for ( c = 1; c < MAXPLAYERS; c++ )
4728 		{
4729 			if ( client_disconnected[c] == true )
4730 			{
4731 				continue;
4732 			}
4733 			strncpy((char*)net_packet->data, "DISCONNECT", 10);
4734 			net_packet->data[10] = playerDisconnected;
4735 			net_packet->address.host = net_clients[c - 1].host;
4736 			net_packet->address.port = net_clients[c - 1].port;
4737 			net_packet->len = 11;
4738 			sendPacketSafe(net_sock, -1, net_packet, c - 1);
4739 			messagePlayer(c, language[1120], shortname);
4740 		}
4741 		messagePlayer(clientnum, language[1120], shortname);
4742 		return;
4743 	}
4744 
4745 	// message
4746 	else if (!strncmp((char*)net_packet->data, "MSGS", 4))
4747 	{
4748 		char tempstr[1024];
4749 
4750 		int pnum = net_packet->data[4];
4751 		client_keepalive[pnum] = ticks;
4752 		Uint32 color = SDLNet_Read32(&net_packet->data[5]);
4753 
4754 		// strncpy() does not copy N bytes if a terminating null is encountered first
4755 		// see http://www.cplusplus.com/reference/cstring/strncpy/
4756 		// see https://en.cppreference.com/w/c/string/byte/strncpy
4757 		// GCC throws a warning (intended) when the length argument to strncpy() in
4758 		// any way depends on strlen(src) to discourage this (and related) construct(s).
4759 		strncpy(tempstr, stats[pnum]->name, 10);
4760 		tempstr[std::min<size_t>(strlen(stats[pnum]->name), 10)] = 0; //TODO: Why are size_t and int being compared?
4761 		strcat(tempstr, ": ");
4762 		strcat(tempstr, (char*)(&net_packet->data[9]));
4763 		messagePlayerColor(clientnum, color, tempstr);
4764 
4765 		playSound(238, 64);
4766 
4767 		// relay message to all clients
4768 		for ( c = 1; c < MAXPLAYERS; c++ )
4769 		{
4770 			if ( c == pnum || client_disconnected[c] == true )
4771 			{
4772 				continue;
4773 			}
4774 			strcpy((char*)net_packet->data, "MSGS");
4775 			SDLNet_Write32(color, &net_packet->data[4]);
4776 			strcpy((char*)(&net_packet->data[8]), tempstr);
4777 			net_packet->address.host = net_clients[c - 1].host;
4778 			net_packet->address.port = net_clients[c - 1].port;
4779 			net_packet->len = 8 + strlen(tempstr) + 1;
4780 			sendPacketSafe(net_sock, -1, net_packet, c - 1);
4781 		}
4782 		return;
4783 	}
4784 
4785 	// spotting (examining)
4786 	else if (!strncmp((char*)net_packet->data, "SPOT", 4))
4787 	{
4788 		client_keepalive[net_packet->data[4]] = ticks;
4789 		j = net_packet->data[4]; // player number
4790 		Uint32 uid = SDLNet_Read32(&net_packet->data[5]);
4791 		Entity* entity = uidToEntity(uid);
4792 		if ( entity )
4793 		{
4794 			clickDescription(j, entity);
4795 		}
4796 		return;
4797 	}
4798 
4799 	// item drop
4800 	else if (!strncmp((char*)net_packet->data, "DROP", 4))
4801 	{
4802 		client_keepalive[net_packet->data[25]] = ticks;
4803 		item = newItem(static_cast<ItemType>(SDLNet_Read32(&net_packet->data[4])), static_cast<Status>(SDLNet_Read32(&net_packet->data[8])), SDLNet_Read32(&net_packet->data[12]), SDLNet_Read32(&net_packet->data[16]), SDLNet_Read32(&net_packet->data[20]), net_packet->data[24], &stats[net_packet->data[25]]->inventory);
4804 		dropItem(item, net_packet->data[25]);
4805 		return;
4806 	}
4807 
4808 	// item drop (on death)
4809 	else if (!strncmp((char*)net_packet->data, "DIEI", 4))
4810 	{
4811 		item = newItem(static_cast<ItemType>(SDLNet_Read32(&net_packet->data[4])), static_cast<Status>(SDLNet_Read32(&net_packet->data[8])), SDLNet_Read32(&net_packet->data[12]), SDLNet_Read32(&net_packet->data[16]), SDLNet_Read32(&net_packet->data[20]), net_packet->data[24], &stats[net_packet->data[25]]->inventory);
4812 		entity = newEntity(-1, 1, map.entities, nullptr); //Item entity.
4813 		entity->x = net_packet->data[26];
4814 		entity->x = entity->x * 16 + 8;
4815 		entity->y = net_packet->data[27];
4816 		entity->y = entity->y * 16 + 8;
4817 		entity->flags[NOUPDATE] = true;
4818 		entity->flags[PASSABLE] = true;
4819 		entity->flags[INVISIBLE] = true;
4820 		for ( c = item->count; c > 0; c-- )
4821 		{
4822 			int qtyToDrop = 1;
4823 			if ( c >= 10 && (item->type == TOOL_METAL_SCRAP || item->type == TOOL_MAGIC_SCRAP) )
4824 			{
4825 				qtyToDrop = 10;
4826 				c -= 9;
4827 			}
4828 			else if ( itemTypeIsQuiver(item->type) )
4829 			{
4830 				qtyToDrop = item->count;
4831 				c -= item->count;
4832 			}
4833 			dropItemMonster(item, entity, stats[net_packet->data[25]], qtyToDrop);
4834 		}
4835 		list_RemoveNode(entity->mynode);
4836 		return;
4837 	}
4838 
4839 	// raise/lower shield
4840 	else if (!strncmp((char*)net_packet->data, "SHLD", 4))
4841 	{
4842 		stats[net_packet->data[4]]->defending = net_packet->data[5];
4843 		return;
4844 	}
4845 
4846 	// sneaking
4847 	else if ( !strncmp((char*)net_packet->data, "SNEK", 4) )
4848 	{
4849 		stats[net_packet->data[4]]->sneaking = net_packet->data[5];
4850 		return;
4851 	}
4852 
4853 	// close shop
4854 	else if (!strncmp((char*)net_packet->data, "SHPC", 4))
4855 	{
4856 		Entity* entity = uidToEntity((Uint32)SDLNet_Read32(&net_packet->data[4]));
4857 		if ( entity )
4858 		{
4859 			entity->skill[0] = 0;
4860 			monsterMoveAside(entity, uidToEntity(entity->skill[1]));
4861 			entity->skill[1] = 0;
4862 		}
4863 		return;
4864 	}
4865 
4866 	// buy item from shop
4867 	else if (!strncmp((char*)net_packet->data, "SHPB", 4))
4868 	{
4869 		Uint32 uidnum = (Uint32)SDLNet_Read32(&net_packet->data[4]);
4870 		int client = net_packet->data[29];
4871 		Entity* entity = uidToEntity(uidnum);
4872 		if ( !entity )
4873 		{
4874 			printlog("warning: client %d bought item from non-existent shop! (uid=%d)\n", client, uidnum);
4875 			return;
4876 		}
4877 		Stat* entitystats = entity->getStats();
4878 		if ( !entitystats )
4879 		{
4880 			printlog("warning: client %d bought item from a \"shop\" that has no stats! (uid=%d)\n", client, uidnum);
4881 			return;
4882 		}
4883 		Item* item = (Item*) malloc(sizeof(Item));
4884 		item->type = static_cast<ItemType>(SDLNet_Read32(&net_packet->data[8]));
4885 		item->status = static_cast<Status>(SDLNet_Read32(&net_packet->data[12]));
4886 		item->beatitude = SDLNet_Read32(&net_packet->data[16]);
4887 		item->appearance = SDLNet_Read32(&net_packet->data[20]);
4888 		item->count = SDLNet_Read32(&net_packet->data[24]);
4889 		if ( net_packet->data[28] )
4890 		{
4891 			item->identified = true;
4892 		}
4893 		else
4894 		{
4895 			item->identified = false;
4896 		}
4897 		node_t* nextnode;
4898 		for ( node = entitystats->inventory.first; node != NULL; node = nextnode )
4899 		{
4900 			nextnode = node->next;
4901 			Item* item2 = (Item*)node->element;
4902 			if (!itemCompare(item, item2, false))
4903 			{
4904 				printlog("client %d bought item from shop (uid=%d)\n", client, uidnum);
4905 				if ( shopIsMysteriousShopkeeper(entity) )
4906 				{
4907 					buyItemFromMysteriousShopkeepConsumeOrb(*entity, *item2);
4908 				}
4909 				consumeItem(item2, client);
4910 				break;
4911 			}
4912 		}
4913 		entitystats->GOLD += item->buyValue(client);
4914 		stats[client]->GOLD -= item->buyValue(client);
4915 		if ( players[client] && players[client]->entity )
4916 		{
4917 			if ( rand() % 2 )
4918 			{
4919 				if ( item->buyValue(client) <= 1 )
4920 				{
4921 					// buying cheap items does not increase trading past basic
4922 					if ( stats[client]->PROFICIENCIES[PRO_TRADING] < SKILL_LEVEL_SKILLED )
4923 					{
4924 						players[client]->entity->increaseSkill(PRO_TRADING);
4925 					}
4926 				}
4927 				else
4928 				{
4929 					players[client]->entity->increaseSkill(PRO_TRADING);
4930 				}
4931 			}
4932 			else if ( item->buyValue(client) >= 150 )
4933 			{
4934 				if ( item->buyValue(client) >= 300 || rand() % 2 )
4935 				{
4936 					players[client]->entity->increaseSkill(PRO_TRADING);
4937 				}
4938 			}
4939 		}
4940 		free(item);
4941 		return;
4942 	}
4943 
4944 	//Remove a spell from the channeled spells list.
4945 	else if (!strncmp((char*)net_packet->data, "UNCH", 4))
4946 	{
4947 		int client = net_packet->data[4];
4948 		spell_t* thespell = getSpellFromID(SDLNet_Read32(&net_packet->data[5]));
4949 		if (spellInList(&channeledSpells[client], thespell))
4950 		{
4951 			node_t* nextnode;
4952 			for (node = channeledSpells[client].first; node; node = nextnode )
4953 			{
4954 				nextnode = node->next;
4955 				spell_t* spell_search = (spell_t*)node->element;
4956 				if (spell_search->ID == thespell->ID)
4957 				{
4958 					spell_search->sustain = false;
4959 				}
4960 			}
4961 		}
4962 		return;
4963 	}
4964 
4965 	// sell item to shop
4966 	else if (!strncmp((char*)net_packet->data, "SHPS", 4))
4967 	{
4968 		Uint32 uidnum = (Uint32)SDLNet_Read32(&net_packet->data[4]);
4969 		int client = net_packet->data[29];
4970 		Entity* entity = uidToEntity(uidnum);
4971 		if ( !entity )
4972 		{
4973 			printlog("warning: client %d sold item to non-existent shop! (uid=%d)\n", client, uidnum);
4974 			return;
4975 		}
4976 		Stat* entitystats = entity->getStats();
4977 		if ( !entitystats )
4978 		{
4979 			printlog("warning: client %d sold item to a \"shop\" that has no stats! (uid=%d)\n", client, uidnum);
4980 			return;
4981 		}
4982 		if ( net_packet->data[28] )
4983 		{
4984 			item = newItem(static_cast<ItemType>(SDLNet_Read32(&net_packet->data[8])), static_cast<Status>(SDLNet_Read32(&net_packet->data[12])),
4985 				SDLNet_Read32(&net_packet->data[16]), SDLNet_Read32(&net_packet->data[24]), SDLNet_Read32(&net_packet->data[20]), true, &entitystats->inventory);
4986 		}
4987 		else
4988 		{
4989 			item = newItem(static_cast<ItemType>(SDLNet_Read32(&net_packet->data[8])), static_cast<Status>(SDLNet_Read32(&net_packet->data[12])),
4990 				SDLNet_Read32(&net_packet->data[16]), SDLNet_Read32(&net_packet->data[24]), SDLNet_Read32(&net_packet->data[20]), false, &entitystats->inventory);
4991 		}
4992 		printlog("client %d sold item to shop (uid=%d)\n", client, uidnum);
4993 		stats[client]->GOLD += item->sellValue(client);
4994 		if (rand() % 2 && item->type != GEM_GLASS )
4995 		{
4996 			if ( players[client] && players[client]->entity )
4997 			{
4998 				players[client]->entity->increaseSkill(PRO_TRADING);
4999 			}
5000 		}
5001 		return;
5002 	}
5003 
5004 	// use item
5005 	else if (!strncmp((char*)net_packet->data, "USEI", 4))
5006 	{
5007 		item = newItem(static_cast<ItemType>(SDLNet_Read32(&net_packet->data[4])), static_cast<Status>(SDLNet_Read32(&net_packet->data[8])), SDLNet_Read32(&net_packet->data[12]), SDLNet_Read32(&net_packet->data[16]), SDLNet_Read32(&net_packet->data[20]), net_packet->data[24], &stats[net_packet->data[25]]->inventory);
5008 		useItem(item, net_packet->data[25]);
5009 		return;
5010 	}
5011 
5012 	// equip item (as a weapon)
5013 	else if (!strncmp((char*)net_packet->data, "EQUI", 4))
5014 	{
5015 		item = newItem(static_cast<ItemType>(SDLNet_Read32(&net_packet->data[4])), static_cast<Status>(SDLNet_Read32(&net_packet->data[8])), SDLNet_Read32(&net_packet->data[12]), SDLNet_Read32(&net_packet->data[16]), SDLNet_Read32(&net_packet->data[20]), net_packet->data[24], &stats[net_packet->data[25]]->inventory);
5016 		equipItem(item, &stats[net_packet->data[25]]->weapon, net_packet->data[25]);
5017 		return;
5018 	}
5019 
5020 	// equip item (as a shield)
5021 	else if ( !strncmp((char*)net_packet->data, "EQUS", 4) )
5022 	{
5023 		item = newItem(static_cast<ItemType>(SDLNet_Read32(&net_packet->data[4])), static_cast<Status>(SDLNet_Read32(&net_packet->data[8])), SDLNet_Read32(&net_packet->data[12]), SDLNet_Read32(&net_packet->data[16]), SDLNet_Read32(&net_packet->data[20]), net_packet->data[24], &stats[net_packet->data[25]]->inventory);
5024 		equipItem(item, &stats[net_packet->data[25]]->shield, net_packet->data[25]);
5025 		return;
5026 	}
5027 
5028 	// equip item (any other slot)
5029 	else if ( !strncmp((char*)net_packet->data, "EQUM", 4) )
5030 	{
5031 		item = newItem(static_cast<ItemType>(SDLNet_Read32(&net_packet->data[4])), static_cast<Status>(SDLNet_Read32(&net_packet->data[8])), SDLNet_Read32(&net_packet->data[12]), SDLNet_Read32(&net_packet->data[16]), SDLNet_Read32(&net_packet->data[20]), net_packet->data[24], &stats[net_packet->data[25]]->inventory);
5032 
5033 		switch ( net_packet->data[27] )
5034 		{
5035 			case EQUIP_ITEM_SLOT_WEAPON:
5036 				equipItem(item, &stats[net_packet->data[25]]->weapon, net_packet->data[25]);
5037 				break;
5038 			case EQUIP_ITEM_SLOT_SHIELD:
5039 				equipItem(item, &stats[net_packet->data[25]]->shield, net_packet->data[25]);
5040 				break;
5041 			case EQUIP_ITEM_SLOT_MASK:
5042 				equipItem(item, &stats[net_packet->data[25]]->mask, net_packet->data[25]);
5043 				break;
5044 			case EQUIP_ITEM_SLOT_HELM:
5045 				equipItem(item, &stats[net_packet->data[25]]->helmet, net_packet->data[25]);
5046 				break;
5047 			case EQUIP_ITEM_SLOT_GLOVES:
5048 				equipItem(item, &stats[net_packet->data[25]]->gloves, net_packet->data[25]);
5049 				break;
5050 			case EQUIP_ITEM_SLOT_BOOTS:
5051 				equipItem(item, &stats[net_packet->data[25]]->shoes, net_packet->data[25]);
5052 				break;
5053 			case EQUIP_ITEM_SLOT_BREASTPLATE:
5054 				equipItem(item, &stats[net_packet->data[25]]->breastplate, net_packet->data[25]);
5055 				break;
5056 			case EQUIP_ITEM_SLOT_CLOAK:
5057 				equipItem(item, &stats[net_packet->data[25]]->cloak, net_packet->data[25]);
5058 				break;
5059 			case EQUIP_ITEM_SLOT_AMULET:
5060 				equipItem(item, &stats[net_packet->data[25]]->amulet, net_packet->data[25]);
5061 				break;
5062 			case EQUIP_ITEM_SLOT_RING:
5063 				equipItem(item, &stats[net_packet->data[25]]->ring, net_packet->data[25]);
5064 				break;
5065 			default:
5066 				break;
5067 		}
5068 		return;
5069 	}
5070 
5071 	// apply item to entity
5072 	else if (!strncmp((char*)net_packet->data, "APIT", 4))
5073 	{
5074 		item = newItem(static_cast<ItemType>(SDLNet_Read32(&net_packet->data[4])), static_cast<Status>(SDLNet_Read32(&net_packet->data[8])), SDLNet_Read32(&net_packet->data[12]), SDLNet_Read32(&net_packet->data[16]), SDLNet_Read32(&net_packet->data[20]), net_packet->data[24], NULL);
5075 		Entity* entity = uidToEntity(SDLNet_Read32(&net_packet->data[26]));
5076 		if ( entity )
5077 		{
5078 			item->apply(net_packet->data[25], entity);
5079 		}
5080 		else
5081 		{
5082 			printlog("warning: client applied item to entity that does not exist\n");
5083 		}
5084 		free(item);
5085 		return;
5086 	}
5087 
5088 	// apply item to entity
5089 	else if ( !strncmp((char*)net_packet->data, "APIW", 4) )
5090 	{
5091 		item = newItem(static_cast<ItemType>(SDLNet_Read32(&net_packet->data[4])), static_cast<Status>(SDLNet_Read32(&net_packet->data[8])), SDLNet_Read32(&net_packet->data[12]), SDLNet_Read32(&net_packet->data[16]), SDLNet_Read32(&net_packet->data[20]), net_packet->data[24], NULL);
5092 		int wallx = (SDLNet_Read16(&net_packet->data[26]));
5093 		int wally = (SDLNet_Read16(&net_packet->data[28]));
5094 		item->applyLockpickToWall(net_packet->data[25], wallx, wally);
5095 		free(item);
5096 		return;
5097 	}
5098 
5099 	// attacking
5100 	else if (!strncmp((char*)net_packet->data, "ATAK", 4))
5101 	{
5102 		if (players[net_packet->data[4]] && players[net_packet->data[4]]->entity)
5103 		{
5104 			players[net_packet->data[4]]->entity->attack(net_packet->data[5], net_packet->data[6], nullptr);
5105 		}
5106 		return;
5107 	}
5108 
5109 	//Multiplayer chest code (server).
5110 	else if (!strncmp((char*)net_packet->data, "CCLS", 4))    //Close the chest.
5111 	{
5112 		int the_client = net_packet->data[4];
5113 		if (openedChest[the_client])
5114 		{
5115 			openedChest[the_client]->closeChestServer();
5116 		}
5117 	}
5118 
5119 	//The client failed some alchemy.
5120 	else if ( !strncmp((char*)net_packet->data, "BOOM", 4) )
5121 	{
5122 		int the_client = net_packet->data[4];
5123 		if ( players[the_client] && players[the_client]->entity )
5124 		{
5125 			spawnMagicTower(nullptr, players[the_client]->entity->x, players[the_client]->entity->y, SPELL_FIREBALL, nullptr);
5126 			players[the_client]->entity->setObituary(language[3350]);
5127 		}
5128 		return;
5129 	}
5130 
5131 	//The client cast a spell.
5132 	else if (!strncmp((char*)net_packet->data, "SPEL", 4))
5133 	{
5134 		int the_client = net_packet->data[4];
5135 
5136 		spell_t* thespell = getSpellFromID(SDLNet_Read32(&net_packet->data[5]));
5137 		if ( players[the_client] && players[the_client]->entity )
5138 		{
5139 			if ( net_packet->data[9] == 1 )
5140 			{
5141 				castSpell(players[the_client]->entity->getUID(), thespell, false, false, true);
5142 			}
5143 			else
5144 			{
5145 				castSpell(players[the_client]->entity->getUID(), thespell, false, false);
5146 			}
5147 		}
5148 		return;
5149 	}
5150 
5151 	//The client added an item to the chest.
5152 	else if (!strncmp((char*)net_packet->data, "CITM", 4))
5153 	{
5154 		int the_client = net_packet->data[4];
5155 		if (!openedChest[the_client])
5156 		{
5157 			return;
5158 		}
5159 
5160 		Item* newitem = NULL;
5161 		if ( (newitem = (Item*) malloc(sizeof(Item))) == NULL)
5162 		{
5163 			printlog( "failed to allocate memory for new item!\n" );
5164 			return; //Should error instead or something?
5165 		}
5166 		newitem->node = NULL;
5167 		newitem->type = static_cast<ItemType>(SDLNet_Read32(&net_packet->data[5]));
5168 		newitem->status = static_cast<Status>(SDLNet_Read32(&net_packet->data[9]));
5169 		newitem->beatitude = SDLNet_Read32(&net_packet->data[13]);
5170 		newitem->count = SDLNet_Read32(&net_packet->data[17]);
5171 		newitem->appearance = SDLNet_Read32(&net_packet->data[21]);
5172 		if ( net_packet->data[25])
5173 		{
5174 			newitem->identified = true;
5175 		}
5176 		else
5177 		{
5178 			newitem->identified = false;
5179 		}
5180 
5181 		openedChest[the_client]->addItemToChestServer(newitem);
5182 
5183 		return;
5184 	}
5185 
5186 	//The client removed an item from the chest.
5187 	else if (!strncmp((char*)net_packet->data, "RCIT", 4))
5188 	{
5189 		int the_client = net_packet->data[4];
5190 		if (!openedChest[the_client])
5191 		{
5192 			return;
5193 		}
5194 
5195 		Item* theitem = NULL;
5196 		if ( (theitem = (Item*) malloc(sizeof(Item))) == NULL)
5197 		{
5198 			printlog( "failed to allocate memory for new item!\n" );
5199 			return; //Should it error instead or somesuch?
5200 		}
5201 		theitem->node = NULL;
5202 		theitem->type = static_cast<ItemType>(SDLNet_Read32(&net_packet->data[5]));
5203 		theitem->status = static_cast<Status>(SDLNet_Read32(&net_packet->data[9]));
5204 		theitem->beatitude = SDLNet_Read32(&net_packet->data[13]);
5205 		theitem->count = SDLNet_Read32(&net_packet->data[17]);
5206 		theitem->appearance = SDLNet_Read32(&net_packet->data[21]);
5207 		if ( net_packet->data[25])   //HNGH NUMBERS.
5208 		{
5209 			theitem->identified = true;
5210 		}
5211 		else
5212 		{
5213 			theitem->identified = false;
5214 		}
5215 
5216 		openedChest[the_client]->removeItemFromChestServer(theitem, theitem->count);
5217 		return;
5218 	}
5219 
5220 	// the client removed a curse on his equipment
5221 	else if (!strncmp((char*)net_packet->data, "RCUR", 4))
5222 	{
5223 		int player = net_packet->data[4];
5224 		switch ( net_packet->data[5] )
5225 		{
5226 			case 0:
5227 				item = stats[player]->helmet;
5228 				break;
5229 			case 1:
5230 				item = stats[player]->breastplate;
5231 				break;
5232 			case 2:
5233 				item = stats[player]->gloves;
5234 				break;
5235 			case 3:
5236 				item = stats[player]->shoes;
5237 				break;
5238 			case 4:
5239 				item = stats[player]->shield;
5240 				break;
5241 			case 5:
5242 				item = stats[player]->weapon;
5243 				break;
5244 			case 6:
5245 				item = stats[player]->cloak;
5246 				break;
5247 			case 7:
5248 				item = stats[player]->amulet;
5249 				break;
5250 			case 8:
5251 				item = stats[player]->ring;
5252 				break;
5253 			case 9:
5254 				item = stats[player]->mask;
5255 				break;
5256 			default:
5257 				item = nullptr;
5258 				break;
5259 		}
5260 		if ( item != nullptr )
5261 		{
5262 			item->beatitude = 0;
5263 		}
5264 		return;
5265 	}
5266 
5267 	// the client repaired equipment or otherwise modified status of equipment.
5268 	else if ( !strncmp((char*)net_packet->data, "REPA", 4) )
5269 	{
5270 		int player = net_packet->data[4];
5271 		Item* equipment = nullptr;
5272 
5273 		switch ( net_packet->data[5] )
5274 		{
5275 			case 0:
5276 				equipment = stats[player]->weapon;
5277 				break;
5278 			case 1:
5279 				equipment = stats[player]->helmet;
5280 				break;
5281 			case 2:
5282 				equipment = stats[player]->breastplate;
5283 				break;
5284 			case 3:
5285 				equipment = stats[player]->gloves;
5286 				break;
5287 			case 4:
5288 				equipment = stats[player]->shoes;
5289 				break;
5290 			case 5:
5291 				equipment = stats[player]->shield;
5292 				break;
5293 			case 6:
5294 				equipment = stats[player]->cloak;
5295 				break;
5296 			case 7:
5297 				equipment = stats[player]->mask;
5298 				break;
5299 			default:
5300 				equipment = nullptr;
5301 				break;
5302 		}
5303 
5304 		if ( !equipment )
5305 		{
5306 			return;
5307 		}
5308 
5309 		if ( static_cast<int>(net_packet->data[6]) > EXCELLENT )
5310 		{
5311 			equipment->status = EXCELLENT;
5312 		}
5313 		else if ( static_cast<int>(net_packet->data[6]) < BROKEN )
5314 		{
5315 			equipment->status = BROKEN;
5316 		}
5317 		equipment->status = static_cast<Status>(net_packet->data[6]);
5318 		return;
5319 	}
5320 
5321 	// the client changed beatitude of equipment.
5322 	else if ( !strncmp((char*)net_packet->data, "BEAT", 4) )
5323 	{
5324 		int player = net_packet->data[4];
5325 		Item* equipment = nullptr;
5326 		//messagePlayer(0, "client: %d, armornum: %d, status %d", player, net_packet->data[5], net_packet->data[6]);
5327 
5328 		switch ( net_packet->data[5] )
5329 		{
5330 			case 0:
5331 				equipment = stats[player]->weapon;
5332 				break;
5333 			case 1:
5334 				equipment = stats[player]->helmet;
5335 				break;
5336 			case 2:
5337 				equipment = stats[player]->breastplate;
5338 				break;
5339 			case 3:
5340 				equipment = stats[player]->gloves;
5341 				break;
5342 			case 4:
5343 				equipment = stats[player]->shoes;
5344 				break;
5345 			case 5:
5346 				equipment = stats[player]->shield;
5347 				break;
5348 			case 6:
5349 				equipment = stats[player]->cloak;
5350 				break;
5351 			case 7:
5352 				equipment = stats[player]->mask;
5353 				break;
5354 			default:
5355 				equipment = nullptr;
5356 				break;
5357 		}
5358 
5359 		if ( !equipment )
5360 		{
5361 			return;
5362 		}
5363 
5364 		equipment->beatitude = net_packet->data[6] - 100; // we sent the data beatitude + 100
5365 		//messagePlayer(0, "%d", equipment->beatitude);
5366 		return;
5367 	}
5368 
5369 	// client dropped gold
5370 	else if (!strncmp((char*)net_packet->data, "DGLD", 4))
5371 	{
5372 		int player = net_packet->data[4];
5373 		int amount = SDLNet_Read32(&net_packet->data[5]);
5374 		if ( players[player] && players[player]->entity )
5375 		{
5376 			stats[player]->GOLD -= amount;
5377 			playSoundEntity(players[player]->entity, 242 + rand() % 4, 64);
5378 			//Drop gold.
5379 			int x = std::min<int>(std::max(0, (int)(players[player]->entity->x / 16)), map.width - 1);
5380 			int y = std::min<int>(std::max(0, (int)(players[player]->entity->y / 16)), map.height - 1);
5381 			if ( map.tiles[y * MAPLAYERS + x * MAPLAYERS * map.height] )
5382 			{
5383 				entity = newEntity(130, 0, map.entities, nullptr); // 130 = goldbag model
5384 				entity->sizex = 4;
5385 				entity->sizey = 4;
5386 				entity->x = players[player]->entity->x;
5387 				entity->y = players[player]->entity->y;
5388 				entity->z = 6;
5389 				entity->yaw = (rand() % 360) * PI / 180.0;
5390 				entity->flags[PASSABLE] = true;
5391 				entity->flags[UPDATENEEDED] = true;
5392 				entity->behavior = &actGoldBag;
5393 				entity->goldAmount = amount; // amount
5394 			}
5395 
5396 		}
5397 		return;
5398 	}
5399 
5400 	// client played a sound
5401 	else if ( !strncmp((char*)net_packet->data, "EMOT", 4) )
5402 	{
5403 		int player = net_packet->data[4];
5404 		int sfx = SDLNet_Read16(&net_packet->data[5]);
5405 		if ( players[player] && players[player]->entity )
5406 		{
5407 			playSoundEntityLocal(players[player]->entity, sfx, 92);
5408 			for ( int c = 1; c < MAXPLAYERS; ++c )
5409 			{
5410 				// send to all other players
5411 				if ( c != player && !client_disconnected[c] )
5412 				{
5413 					strcpy((char*)net_packet->data, "SNEL");
5414 					SDLNet_Write16(sfx, &net_packet->data[4]);
5415 					SDLNet_Write32((Uint32)players[player]->entity->getUID(), &net_packet->data[6]);
5416 					SDLNet_Write16(92, &net_packet->data[10]);
5417 					net_packet->address.host = net_clients[c - 1].host;
5418 					net_packet->address.port = net_clients[c - 1].port;
5419 					net_packet->len = 12;
5420 					sendPacketSafe(net_sock, -1, net_packet, c - 1);
5421 				}
5422 			}
5423 		}
5424 		return;
5425 	}
5426 
5427 	// the client asked for a level up
5428 	else if ( !strncmp((char*)net_packet->data, "CLVL", 4) )
5429 	{
5430 		int player = net_packet->data[4];
5431 		if ( players[player] && players[player]->entity )
5432 		{
5433 			players[player]->entity->getStats()->EXP += 100;
5434 		}
5435 		return;
5436 	}
5437 
5438 	// the client asked for a level up
5439 	else if ( !strncmp((char*)net_packet->data, "CSKL", 4) )
5440 	{
5441 		int player = net_packet->data[4];
5442 		int skill = net_packet->data[5];
5443 		if ( player > 0 && player < MAXPLAYERS && players[player] && players[player]->entity )
5444 		{
5445 			if ( skill >= 0 && skill < NUMPROFICIENCIES )
5446 			{
5447 				players[player]->entity->increaseSkill(skill);
5448 			}
5449 		}
5450 		return;
5451 	}
5452 
5453 	// the client sent a minimap ping packet.
5454 	else if ( !strncmp((char*)net_packet->data, "PMAP", 4) )
5455 	{
5456 		MinimapPing newPing(ticks, net_packet->data[4], net_packet->data[5], net_packet->data[6]);
5457 		minimapPingAdd(newPing);
5458 		sendMinimapPing(net_packet->data[4], newPing.x, newPing.y); // relay to other clients.
5459 		return;
5460 	}
5461 
5462 	//Remove vampiric aura
5463 	else if ( !strncmp((char*)net_packet->data, "VAMP", 4) )
5464 	{
5465 		int player = net_packet->data[4];
5466 		int spellID = SDLNet_Read32(&net_packet->data[5]);
5467 		if ( players[player] && players[player]->entity && stats[player] )
5468 		{
5469 			if ( client_classes[player] == CLASS_ACCURSED &&
5470 				stats[player]->EFFECTS[EFF_VAMPIRICAURA] && players[player]->entity->playerVampireCurse == 1 )
5471 			{
5472 				players[player]->entity->setEffect(EFF_VAMPIRICAURA, true, 1, true);
5473 				messagePlayerColor(player, uint32ColorGreen(*mainsurface), language[3241]);
5474 				messagePlayerColor(player, uint32ColorGreen(*mainsurface), language[3242]);
5475 				players[player]->entity->playerVampireCurse = 2; // cured.
5476 				serverUpdateEntitySkill(players[player]->entity, 51);
5477 				steamAchievementClient(player, "BARONY_ACH_REVERSE_THIS_CURSE");
5478 				playSoundEntity(players[player]->entity, 402, 128);
5479 				createParticleDropRising(players[player]->entity, 174, 1.0);
5480 				serverSpawnMiscParticles(players[player]->entity, PARTICLE_EFFECT_RISING_DROP, 174);
5481 			}
5482 		}
5483 		return;
5484 	}
5485 
5486 	// the client sent a monster command.
5487 	else if ( !strncmp((char*)net_packet->data, "ALLY", 4) )
5488 	{
5489 		int player = net_packet->data[4];
5490 		int allyCmd = net_packet->data[5];
5491 		Uint32 uid = SDLNet_Read32(&net_packet->data[8]);
5492 		//messagePlayer(0, " received %d, %d, %d, %d, %d", player, allyCmd, net_packet->data[6], net_packet->data[7], uid);
5493 		Entity* entity = uidToEntity(uid);
5494 		if ( entity )
5495 		{
5496 			if ( net_packet->len > 12 )
5497 			{
5498 				Uint32 interactUid = SDLNet_Read32(&net_packet->data[12]);
5499 				entity->monsterAllySendCommand(allyCmd, net_packet->data[6], net_packet->data[7], interactUid);
5500 				//messagePlayer(0, "received UID of target: %d, applying...", uid);
5501 				entity->monsterAllyInteractTarget = interactUid;
5502 			}
5503 			else
5504 			{
5505 				entity->monsterAllySendCommand(allyCmd, net_packet->data[6], net_packet->data[7]);
5506 			}
5507 		}
5508 	}
5509 
5510 	else if ( !strncmp((char*)net_packet->data, "IDIE", 4) )
5511 	{
5512 		int playerDie = net_packet->data[4];
5513 		if ( playerDie >= 1 && playerDie < MAXPLAYERS )
5514 		{
5515 			if ( players[playerDie] && players[playerDie]->entity )
5516 			{
5517 				players[playerDie]->entity->setHP(0);
5518 			}
5519 		}
5520 	}
5521 
5522 	// use automaton food item
5523 	else if ( !strncmp((char*)net_packet->data, "FODA", 4) )
5524 	{
5525 		item = newItem(static_cast<ItemType>(SDLNet_Read32(&net_packet->data[4])), static_cast<Status>(SDLNet_Read32(&net_packet->data[8])), SDLNet_Read32(&net_packet->data[12]), SDLNet_Read32(&net_packet->data[16]), SDLNet_Read32(&net_packet->data[20]), net_packet->data[24], &stats[net_packet->data[25]]->inventory);
5526 		item_FoodAutomaton(item, net_packet->data[25]);
5527 		return;
5528 	}
5529 
5530 	else if ( !strncmp((char*)net_packet->data, "BARONY_JOIN_PROGRES", 19) )
5531 	{
5532 		if ( directConnect )
5533 		{
5534 			return;
5535 		}
5536 	}
5537 }
5538 
5539 /*-------------------------------------------------------------------------------
5540 
5541 	serverHandleMessages
5542 
5543 	Parses messages received from clients
5544 
5545 -------------------------------------------------------------------------------*/
5546 
serverHandleMessages(Uint32 framerateBreakInterval)5547 void serverHandleMessages(Uint32 framerateBreakInterval)
5548 {
5549 #ifdef STEAMWORKS
5550 	if (!directConnect && !net_handler)
5551 	{
5552 		net_handler = new NetHandler();
5553 		if ( !disableMultithreadedSteamNetworking )
5554 		{
5555 			net_handler->initializeMultithreadedPacketHandling();
5556 		}
5557 	}
5558 #elif defined USE_EOS
5559 	if ( !directConnect && !net_handler )
5560 	{
5561 		net_handler = new NetHandler();
5562 	}
5563 #endif
5564 
5565 	if (!directConnect)
5566 	{
5567 #if defined(STEAMWORKS) || defined(USE_EOS)
5568 		if ( LobbyHandler.getP2PType() == LobbyHandler_t::LobbyServiceType::LOBBY_STEAM )
5569 		{
5570 #ifdef STEAMWORKS
5571 			//Steam stuff goes here.
5572 			if ( disableMultithreadedSteamNetworking )
5573 			{
5574 				steamPacketThread(static_cast<void*>(net_handler));
5575 			}
5576 #endif
5577 		}
5578 		else if ( LobbyHandler.getP2PType() == LobbyHandler_t::LobbyServiceType::LOBBY_CROSSPLAY )
5579 		{
5580 #if defined USE_EOS
5581 			EOSPacketThread(static_cast<void*>(net_handler));
5582 #endif // USE_EOS
5583 		}
5584 		SteamPacketWrapper* packet = nullptr;
5585 
5586 		if ( logCheckMainLoopTimers )
5587 		{
5588 			DebugStats.messagesT1 = std::chrono::high_resolution_clock::now();
5589 			DebugStats.handlePacketStartLoop = true;
5590 		}
5591 
5592 		while (packet = net_handler->getGamePacket())
5593 		{
5594 			memcpy(net_packet->data, packet->data(), packet->len());
5595 			net_packet->len = packet->len();
5596 
5597 			serverHandlePacket(); //Uses net_packet;
5598 
5599 			if ( logCheckMainLoopTimers )
5600 			{
5601 				DebugStats.messagesT2WhileLoop = std::chrono::high_resolution_clock::now();
5602 				DebugStats.handlePacketStartLoop = false;
5603 			}
5604 			delete packet;
5605 
5606 			if ( !disableFPSLimitOnNetworkMessages && !frameRateLimit(framerateBreakInterval, false) )
5607 			{
5608 				if ( logCheckMainLoopTimers )
5609 				{
5610 					printlog("[NETWORK]: Incoming messages exceeded given cycle time, packets remaining: %d", net_handler->game_packets.size());
5611 				}
5612 				break;
5613 			}
5614 		}
5615 #endif
5616 	}
5617 	else
5618 	{
5619 		//Direct-connect goes here.
5620 
5621 		while (SDLNet_UDP_Recv(net_sock, net_packet))
5622 		{
5623 			// filter out broken packets
5624 			if ( !net_packet->data[0] )
5625 			{
5626 				continue;
5627 			}
5628 
5629 			serverHandlePacket(); //Uses net_packet.
5630 		}
5631 	}
5632 }
5633 
5634 /*-------------------------------------------------------------------------------
5635 
5636 	handleSafePacket()
5637 
5638 	Handles potentially safe packets. Returns true if this is not a packet to
5639 	be handled.
5640 
5641 -------------------------------------------------------------------------------*/
5642 
handleSafePacket()5643 bool handleSafePacket()
5644 {
5645 	packetsend_t* packet;
5646 	node_t* node;
5647 	int c, j;
5648 
5649 	// safe packet
5650 	if (!strncmp((char*)net_packet->data, "SAFE", 4))
5651 	{
5652 		if ( net_packet->data[4] != MAXPLAYERS )
5653 		{
5654 			int receivedPacketNum = SDLNet_Read32(&net_packet->data[5]);
5655 			Uint8 fromClientnum = net_packet->data[4];
5656 
5657 			if ( ticks > (60 * TICKS_PER_SECOND) && (ticks % (TICKS_PER_SECOND / 2) == 0) )
5658 			{
5659 				// clear old packets > 60 secs
5660 				for ( auto it = safePacketsReceivedMap[fromClientnum].begin(); it != safePacketsReceivedMap[fromClientnum].end(); )
5661 				{
5662 					if ( it->second < (ticks - 60 * TICKS_PER_SECOND) )
5663 					{
5664 						it = safePacketsReceivedMap[fromClientnum].erase(it);
5665 					}
5666 					else
5667 					{
5668 						++it;
5669 					}
5670 				}
5671 			}
5672 
5673 			//auto tmp1 = std::chrono::high_resolution_clock::now();
5674 			auto find = safePacketsReceivedMap[fromClientnum].find(receivedPacketNum);
5675 			if ( find != safePacketsReceivedMap[fromClientnum].end() )
5676 			{
5677 				return true;
5678 			}
5679 
5680 			/*auto tmp2 = std::chrono::high_resolution_clock::now();
5681 			std::chrono::duration<double> time_span =
5682 				std::chrono::duration_cast<std::chrono::duration<double>>(tmp2 - tmp1);
5683 			double timer = time_span.count() * 1000;*/
5684 
5685 			safePacketsReceivedMap[fromClientnum].insert(std::make_pair(receivedPacketNum, ticks));
5686 
5687 			// send an ack
5688 			j = net_packet->data[4];
5689 			net_packet->data[4] = clientnum;
5690 			strcpy((char*)net_packet->data, "GOTP");
5691 			if ( multiplayer == CLIENT )
5692 			{
5693 				net_packet->address.host = net_server.host;
5694 				net_packet->address.port = net_server.port;
5695 			}
5696 			else
5697 			{
5698 				if ( j > 0 )
5699 				{
5700 					net_packet->address.host = net_clients[j - 1].host;
5701 					net_packet->address.port = net_clients[j - 1].port;
5702 				}
5703 			}
5704 			c = net_packet->len;
5705 			net_packet->len = 9;
5706 			if ( multiplayer == CLIENT )
5707 			{
5708 				sendPacket(net_sock, -1, net_packet, 0);
5709 			}
5710 			else
5711 			{
5712 				if ( j > 0 )
5713 				{
5714 					sendPacket(net_sock, -1, net_packet, j - 1);
5715 				}
5716 			}
5717 			net_packet->len = c - 9;
5718 			Uint8 bytedata[NET_PACKET_SIZE];
5719 			memcpy(&bytedata, net_packet->data + 9, net_packet->len);
5720 			memcpy(net_packet->data, &bytedata, net_packet->len);
5721 
5722 			/*int sprite = -9999;
5723 			char chr[5];
5724 			chr[0] = '\0';
5725 			strncpy(chr, (char*)net_packet->data, 4);
5726 			chr[4] = '\0';
5727 			if ( !strncmp((char*)net_packet->data, "ENTU", 4) )
5728 			{
5729 				Entity *entity = uidToEntity((int)SDLNet_Read32(&net_packet->data[4]));
5730 				if ( entity )
5731 				{
5732 					sprite = entity->sprite;
5733 				}
5734 			}
5735 
5736 			if ( !strncmp((char*)net_packet->data, "ENTS", 4) )
5737 			{
5738 				Entity *entity = uidToEntity((int)SDLNet_Read32(&net_packet->data[4]));
5739 				if ( entity )
5740 				{
5741 					messagePlayer(clientnum, "%s | %d : %d - %d | %d %.8f", chr, entity->sprite, net_packet->data[8],
5742 						SDLNet_Read32(&net_packet->data[9]), safePacketsReceivedMap[fromClientnum].size(), timer);
5743 				}
5744 			}
5745 			else
5746 			{
5747 				messagePlayer(clientnum, "%s | %d %.8f", chr, safePacketsReceivedMap[fromClientnum].size(), timer);
5748 			}*/
5749 		}
5750 	}
5751 
5752 	// they got the safe packet
5753 	else if (!strncmp((char*)net_packet->data, "GOTP", 4))
5754 	{
5755 		for ( node = safePacketsSent.first; node != NULL; node = node->next )
5756 		{
5757 			packetsend_t* packet = (packetsend_t*)node->element;
5758 			if ( packet->num == SDLNet_Read32(&net_packet->data[5]) )
5759 			{
5760 				list_RemoveNode(node);
5761 				break;
5762 			}
5763 		}
5764 		return true;
5765 	}
5766 
5767 	return false;
5768 }
5769 
5770 /*-------------------------------------------------------------------------------
5771 
5772 	closeNetworkInterfaces()
5773 
5774 	Consolidating all of the network interfaces close code into one place.
5775 
5776 -------------------------------------------------------------------------------*/
5777 
closeNetworkInterfaces()5778 void closeNetworkInterfaces()
5779 {
5780 	printlog("closing network interfaces...\n");
5781 
5782 	if (net_handler)
5783 	{
5784 		delete net_handler; //Close steam multithreading and stuff.
5785 		net_handler = nullptr;
5786 	}
5787 
5788 	if (net_packet != nullptr)
5789 	{
5790 		SDLNet_FreePacket(net_packet);
5791 		net_packet = nullptr;
5792 	}
5793 	if (net_clients != nullptr)
5794 	{
5795 		free(net_clients);
5796 		net_clients = nullptr;
5797 	}
5798 	if (net_sock != nullptr)
5799 	{
5800 		SDLNet_UDP_Close(net_sock);
5801 		net_sock = nullptr;
5802 	}
5803 	if (net_tcpclients != nullptr)
5804 	{
5805 		for (int c = 0; c < MAXPLAYERS; c++)
5806 		{
5807 			if (net_tcpclients[c] != nullptr)
5808 			{
5809 				SDLNet_TCP_Close(net_tcpclients[c]);
5810 			}
5811 		}
5812 		free(net_tcpclients);
5813 		net_tcpclients = nullptr;
5814 	}
5815 	if (net_tcpsock != nullptr)
5816 	{
5817 		SDLNet_TCP_Close(net_tcpsock);
5818 		net_tcpsock = nullptr;
5819 	}
5820 	if (tcpset)
5821 	{
5822 		SDLNet_FreeSocketSet(tcpset);
5823 		tcpset = nullptr;
5824 	}
5825 }
5826 
5827 
5828 
5829 
5830 
5831 /* ***** MULTITHREADED STEAM PACKET HANDLING ***** */
5832 
SteamPacketWrapper(Uint8 * data,int len)5833 SteamPacketWrapper::SteamPacketWrapper(Uint8* data, int len)
5834 {
5835 	_data = data;
5836 	_len = len;
5837 }
5838 
~SteamPacketWrapper()5839 SteamPacketWrapper::~SteamPacketWrapper()
5840 {
5841 	free(_data);
5842 }
5843 
data()5844 Uint8*& SteamPacketWrapper::data()
5845 {
5846 	return _data;
5847 }
5848 
len()5849 int& SteamPacketWrapper::len()
5850 {
5851 	return _len;
5852 }
5853 
NetHandler()5854 NetHandler::NetHandler()
5855 {
5856 	steam_packet_thread = nullptr;
5857 	continue_multithreading_steam_packets = false;
5858 	game_packets_lock = SDL_CreateMutex();
5859 	continue_multithreading_steam_packets_lock = SDL_CreateMutex();
5860 }
5861 
~NetHandler()5862 NetHandler::~NetHandler()
5863 {
5864 	//First, must join with the worker thread.
5865 	printlog("Waiting for steam_packet_thread to finish...");
5866 	stopMultithreadedPacketHandling();
5867 	if ( steam_packet_thread )
5868 	{
5869 		SDL_WaitThread(steam_packet_thread, NULL); //Wait for the thread to finish.
5870 	}
5871 	printlog("Done.\n");
5872 
5873 	SDL_DestroyMutex(game_packets_lock);
5874 	game_packets_lock = nullptr;
5875 	SDL_DestroyMutex(continue_multithreading_steam_packets_lock);
5876 	continue_multithreading_steam_packets_lock = nullptr;
5877 
5878 	//Free up packet memory.
5879 	while (!game_packets.empty())
5880 	{
5881 		SteamPacketWrapper* packet = game_packets.front();
5882 		delete packet;
5883 		game_packets.pop();
5884 	}
5885 }
5886 
toggleMultithreading(bool disableMultithreading)5887 void NetHandler::toggleMultithreading(bool disableMultithreading)
5888 {
5889 	if ( disableMultithreading )
5890 	{
5891 		// stop the old thread...
5892 		if ( steam_packet_thread )
5893 		{
5894 			printlog("Waiting for steam_packet_thread to finish...");
5895 			stopMultithreadedPacketHandling();
5896 			if ( steam_packet_thread )
5897 			{
5898 				SDL_WaitThread(steam_packet_thread, NULL); //Wait for the thread to finish.
5899 			}
5900 			printlog("Done.\n");
5901 			SDL_DestroyMutex(game_packets_lock);
5902 			game_packets_lock = nullptr;
5903 			SDL_DestroyMutex(continue_multithreading_steam_packets_lock);
5904 			continue_multithreading_steam_packets_lock = nullptr;
5905 			steam_packet_thread = nullptr;
5906 		}
5907 	}
5908 	else
5909 	{
5910 		// create the new thread...
5911 		steam_packet_thread = nullptr;
5912 		continue_multithreading_steam_packets = false;
5913 		game_packets_lock = SDL_CreateMutex();
5914 		continue_multithreading_steam_packets_lock = SDL_CreateMutex();
5915 		initializeMultithreadedPacketHandling();
5916 	}
5917 }
5918 
initializeMultithreadedPacketHandling()5919 void NetHandler::initializeMultithreadedPacketHandling()
5920 {
5921 #ifdef STEAMWORKS
5922 
5923 	printlog("Initializing multithreaded packet handling.");
5924 
5925 	steam_packet_thread = SDL_CreateThread(steamPacketThread, "steamPacketThread", static_cast<void* >(this));
5926 	continue_multithreading_steam_packets = true;
5927 
5928 #endif
5929 }
5930 
stopMultithreadedPacketHandling()5931 void NetHandler::stopMultithreadedPacketHandling()
5932 {
5933 	SDL_LockMutex(continue_multithreading_steam_packets_lock); //NOTE: Will block.
5934 	continue_multithreading_steam_packets = false;
5935 	SDL_UnlockMutex(continue_multithreading_steam_packets_lock);
5936 }
5937 
getContinueMultithreadingSteamPackets()5938 bool NetHandler::getContinueMultithreadingSteamPackets()
5939 {
5940 	return continue_multithreading_steam_packets;
5941 	//SDL_UnlockMutex(continue_multithreading_steam_packets_lock);
5942 }
5943 
addGamePacket(SteamPacketWrapper * packet)5944 void NetHandler::addGamePacket(SteamPacketWrapper* packet)
5945 {
5946 	if ( !disableMultithreadedSteamNetworking )
5947 	{
5948 		SDL_LockMutex(game_packets_lock);
5949 		game_packets.push(packet);
5950 		SDL_UnlockMutex(game_packets_lock);
5951 	}
5952 	else
5953 	{
5954 		game_packets.push(packet);
5955 	}
5956 }
5957 
getGamePacket()5958 SteamPacketWrapper* NetHandler::getGamePacket()
5959 {
5960 	SteamPacketWrapper* packet = nullptr;
5961 	if ( !disableMultithreadedSteamNetworking )
5962 	{
5963 		SDL_LockMutex(game_packets_lock);
5964 		if (!game_packets.empty())
5965 		{
5966 			packet = game_packets.front();
5967 			game_packets.pop();
5968 		}
5969 		SDL_UnlockMutex(game_packets_lock);
5970 	}
5971 	else
5972 	{
5973 		if ( !game_packets.empty() )
5974 		{
5975 			packet = game_packets.front();
5976 			game_packets.pop();
5977 		}
5978 	}
5979 	return packet;
5980 }
5981 
EOSPacketThread(void * data)5982 int EOSPacketThread(void* data)
5983 {
5984 #ifdef USE_EOS
5985 	if ( !EOS.CurrentUserInfo.isValid() )
5986 	{
5987 		//logError("EOSPacketThread: Invalid local user Id: %s", CurrentUserInfo.getProductUserIdStr());
5988 		return -1;
5989 	}
5990 
5991 	if ( !data )
5992 	{
5993 		return -1;    //Some generic error?
5994 	}
5995 
5996 	NetHandler& handler = *static_cast<NetHandler*>(data); //Basically, our this.
5997 	EOS_ProductUserId remoteId = nullptr;
5998 	Uint32 packetlen = 0;
5999 	Uint32 bytes_read = 0;
6000 	Uint8* packet = nullptr;
6001 	bool run = true;
6002 	std::queue<SteamPacketWrapper* > packets; //TODO: Expose to game? Use lock-free packets?
6003 
6004 	while ( run )   //1. Check if thread is supposed to be running.
6005 	{
6006 		//2. Game not over. Grab/poll for packet.
6007 
6008 		//while (handler.getContinueMultithreadingSteamPackets() && SteamNetworking()->IsP2PPacketAvailable(&packetlen)) //Burst read in a bunch of packets.
6009 		while (EOS.HandleReceivedMessages(&remoteId) )
6010 		{
6011 			packetlen = std::min<uint32_t>(net_packet->len, NET_PACKET_SIZE - 1);
6012 			//Read packets and push into queue.
6013 			packet = static_cast<Uint8*>(malloc(packetlen));
6014 			memcpy(packet, net_packet->data, packetlen);
6015 			if ( !EOSFuncs::Helpers_t::isMatchingProductIds(remoteId, EOS.CurrentUserInfo.getProductUserIdHandle())
6016 				&& net_packet->data[0] )
6017 			{
6018 				//Push packet into queue.
6019 				//TODO: Use lock-free queues?
6020 				packets.push(new SteamPacketWrapper(packet, packetlen));
6021 				packet = nullptr;
6022 			}
6023 			if ( packet )
6024 			{
6025 				free(packet);
6026 			}
6027 		}
6028 
6029 
6030 		//3. Now push our local packetstack onto the game's network stack.
6031 		//Well, that is: analyze packet.
6032 		//If packet is good, push into queue.
6033 		//If packet is bad, loop back to start of function.
6034 
6035 		while ( !packets.empty() )
6036 		{
6037 			//Copy over the packets read in so far, and expose them to the game.
6038 			SteamPacketWrapper* packet = packets.front();
6039 			packets.pop();
6040 			handler.addGamePacket(packet);
6041 		}
6042 
6043 		run = false; // only run thread once if multithreading disabled.
6044 	}
6045 #endif // USE_EOS
6046 
6047 	return 0;
6048 }
6049 
steamPacketThread(void * data)6050 int steamPacketThread(void* data)
6051 {
6052 #ifdef STEAMWORKS
6053 
6054 	if (!data)
6055 	{
6056 		return -1;    //Some generic error?
6057 	}
6058 
6059 	NetHandler& handler = *static_cast<NetHandler* >(data); //Basically, our this.
6060 
6061 	Uint32 packetlen = 0;
6062 	Uint32 bytes_read = 0;
6063 	CSteamID steam_id_remote;
6064 	Uint8* packet = nullptr;
6065 	CSteamID mySteamID = SteamUser()->GetSteamID();
6066 	bool run = true;
6067 	std::queue<SteamPacketWrapper* > packets; //TODO: Expose to game? Use lock-free packets?
6068 
6069 	while (run)   //1. Check if thread is supposed to be running.
6070 	{
6071 		//2. Game not over. Grab/poll for packet.
6072 
6073 		//while (handler.getContinueMultithreadingSteamPackets() && SteamNetworking()->IsP2PPacketAvailable(&packetlen)) //Burst read in a bunch of packets.
6074 		while (SteamNetworking()->IsP2PPacketAvailable(&packetlen))
6075 		{
6076 			packetlen = std::min<uint32_t>(packetlen, NET_PACKET_SIZE - 1);
6077 			//Read packets and push into queue.
6078 			packet = static_cast<Uint8* >(malloc(packetlen));
6079 			if (SteamNetworking()->ReadP2PPacket(packet, packetlen, &bytes_read, &steam_id_remote, 0))
6080 			{
6081 				if (packetlen > sizeof(DWORD) && mySteamID.ConvertToUint64() != steam_id_remote.ConvertToUint64() && net_packet->data[0])
6082 				{
6083 					//Push packet into queue.
6084 					//TODO: Use lock-free queues?
6085 					packets.push(new SteamPacketWrapper(packet, packetlen));
6086 					packet = nullptr;
6087 				}
6088 			}
6089 			if (packet)
6090 			{
6091 				free(packet);
6092 			}
6093 		}
6094 
6095 
6096 		//3. Now push our local packetstack onto the game's network stack.
6097 		//Well, that is: analyze packet.
6098 		//If packet is good, push into queue.
6099 		//If packet is bad, loop back to start of function.
6100 
6101 		while (!packets.empty())
6102 		{
6103 			//Copy over the packets read in so far, and expose them to the game.
6104 			SteamPacketWrapper* packet = packets.front();
6105 			packets.pop();
6106 			handler.addGamePacket(packet);
6107 		}
6108 
6109 		if ( !disableMultithreadedSteamNetworking )
6110 		{
6111 			SDL_LockMutex(handler.continue_multithreading_steam_packets_lock);
6112 			run = handler.getContinueMultithreadingSteamPackets();
6113 			SDL_UnlockMutex(handler.continue_multithreading_steam_packets_lock);
6114 		}
6115 		else
6116 		{
6117 			run = false; // only run thread once if multithreading disabled.
6118 		}
6119 	}
6120 
6121 #endif
6122 
6123 	return 0; //If it isn't supposed to be running anymore, exit.
6124 
6125 	//NOTE: This thread is to be created when the gameplay starts. NOT in the steam lobby.
6126 	//If it's desired that it be created right when the network interfaces are opened, menu.c would need to be modified to support this, and the packet wrapper would need to include CSteamID.
6127 }
6128 
6129 /* ***** END MULTITHREADED STEAM PACKET HANDLING ***** */
6130 
deleteMultiplayerSaveGames()6131 void deleteMultiplayerSaveGames()
6132 {
6133 	if ( multiplayer != SERVER )
6134 	{
6135 		return;
6136 	}
6137 
6138 	//Only delete saves if no players are left alive.
6139 	bool lastAlive = true;
6140 
6141 	//const int playersAtStartOfMap = numplayers;
6142 	//int currentPlayers = 0;
6143 	//for ( int i = 0; i < MAXPLAYERS; ++i )
6144 	//{
6145 	//	if ( !client_disconnected[i] )
6146 	//	{
6147 	//		++currentPlayers;
6148 	//	}
6149 	//}
6150 
6151 	//if ( currentPlayers != playersAtStartOfMap )
6152 	//{
6153 	//	return;
6154 	//}
6155 
6156 	for ( int i = 0; i < MAXPLAYERS; ++i )
6157 	{
6158 		Stat* stat = nullptr;
6159 		if ( players[i] && players[i]->entity && (stat = players[i]->entity->getStats()) && stat->HP > 0)
6160 		{
6161 			lastAlive = false;
6162 		}
6163 	}
6164 	if ( !lastAlive )
6165 	{
6166 		return;
6167 	}
6168 
6169 	deleteSaveGame(multiplayer); // stops save scumming c:
6170 
6171 	for ( int i = 1; i < MAXPLAYERS; ++i )
6172 	{
6173 		if ( client_disconnected[i] )
6174 		{
6175 			continue;
6176 		}
6177 		strcpy((char *)net_packet->data,"DSAV"); //Delete save game.
6178 		net_packet->address.host = net_clients[i - 1].host;
6179 		net_packet->address.port = net_clients[i - 1].port;
6180 		net_packet->len = 4;
6181 		sendPacketSafe(net_sock, -1, net_packet, i - 1);
6182 	}
6183 }
6184