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