1 // Emacs style mode select -*- C++ -*-
2 //-----------------------------------------------------------------------------
3 //
4 // $Id: cl_main.cpp 4688 2014-03-25 16:55:35Z dr_sean $
5 //
6 // Copyright (C) 1998-2006 by Randy Heit (ZDoom).
7 // Copyright (C) 2000-2006 by Sergey Makovkin (CSDoom .62).
8 // Copyright (C) 2006-2014 by The Odamex Team.
9 //
10 // This program is free software; you can redistribute it and/or
11 // modify it under the terms of the GNU General Public License
12 // as published by the Free Software Foundation; either version 2
13 // of the License, or (at your option) any later version.
14 //
15 // This program is distributed in the hope that it will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU General Public License for more details.
19 //
20 // DESCRIPTION:
21 // CL_MAIN
22 //
23 //-----------------------------------------------------------------------------
24
25
26 #include "doomtype.h"
27 #include "doomstat.h"
28 #include "gstrings.h"
29 #include "d_player.h"
30 #include "g_game.h"
31 #include "d_net.h"
32 #include "p_local.h"
33 #include "p_tick.h"
34 #include "s_sound.h"
35 #include "gi.h"
36 #include "i_net.h"
37 #include "i_system.h"
38 #include "i_video.h"
39 #include "c_dispatch.h"
40 #include "st_stuff.h"
41 #include "m_argv.h"
42 #include "cl_main.h"
43 #include "c_effect.h"
44 #include "c_console.h"
45 #include "d_main.h"
46 #include "p_ctf.h"
47 #include "m_random.h"
48 #include "w_wad.h"
49 #include "md5.h"
50 #include "m_fileio.h"
51 #include "r_sky.h"
52 #include "cl_demo.h"
53 #include "cl_download.h"
54 #include "p_local.h"
55 #include "cl_maplist.h"
56 #include "cl_vote.h"
57 #include "p_mobj.h"
58 #include "p_snapshot.h"
59 #include "p_lnspec.h"
60 #include "cl_netgraph.h"
61 #include "cl_maplist.h"
62 #include "cl_vote.h"
63 #include "p_mobj.h"
64 #include "p_pspr.h"
65 #include "d_netcmd.h"
66 #include "g_warmup.h"
67 #include "c_level.h"
68 #include "v_text.h"
69
70 #include <string>
71 #include <vector>
72 #include <map>
73 #include <set>
74 #include <sstream>
75
76 #ifdef _XBOX
77 #include "i_xbox.h"
78 #endif
79
80 #if _MSC_VER == 1310
81 #pragma optimize("",off)
82 #endif
83
84 // denis - fancy gfx, but no game manipulation
85 bool clientside = true, serverside = false;
86 baseapp_t baseapp = client;
87
88 extern bool step_mode;
89
90 // denis - client version (VERSION or other supported)
91 short version = 0;
92 int gameversion = 0; // GhostlyDeath -- Bigger Game Version
93 int gameversiontosend = 0; // If the server is 0.4, let's fake our client info
94
95 buf_t net_buffer(MAX_UDP_PACKET);
96
97 bool noservermsgs;
98 int last_received;
99
100 // [SL] 2012-03-17 - world_index is the gametic on the server that the client
101 // is currently simulating. world_index_accum is a continuous accumulator that
102 // is used to advance world_index if appropriate.
103 int world_index = 0;
104 float world_index_accum = 0.0f;
105
106 int last_svgametic = 0;
107 int last_player_update = 0;
108
109 bool recv_full_update = false;
110
111 std::string connectpasshash = "";
112
113 BOOL connected;
114 netadr_t serveraddr; // address of a server
115 netadr_t lastconaddr;
116
117 int packetseq[256];
118 byte packetnum;
119
120 // denis - unique session key provided by the server
121 std::string digest;
122
123 // denis - clientside compressor, used for decompression
124 huffman_client compressor;
125
126 std::string server_host = ""; // hostname of server
127
128 // [SL] 2011-06-27 - Class to record and playback network recordings
129 NetDemo netdemo;
130 // [SL] 2011-07-06 - not really connected (playing back a netdemo)
131 bool simulated_connection = false;
132 bool forcenetdemosplit = false; // need to split demo due to svc_reconnect
133
134 NetCommand localcmds[MAXSAVETICS];
135
136 extern NetGraph netgraph;
137
138 // [SL] 2012-03-07 - Players that were teleported during the current gametic
139 std::set<byte> teleported_players;
140
141 // [SL] 2012-04-06 - moving sector snapshots received from the server
142 std::map<unsigned short, SectorSnapshotManager> sector_snaps;
143
144 EXTERN_CVAR (sv_weaponstay)
145
146 EXTERN_CVAR (cl_name)
147 EXTERN_CVAR (cl_color)
148 EXTERN_CVAR (cl_gender)
149 EXTERN_CVAR (cl_predictsectors)
150
151 EXTERN_CVAR (mute_spectators)
152 EXTERN_CVAR (mute_enemies)
153
154 EXTERN_CVAR(cl_autoaim)
155
156 EXTERN_CVAR(cl_updaterate)
157 EXTERN_CVAR(cl_interp)
158
159 // [SL] Force enemies to have the specified color
160 EXTERN_CVAR (r_forceenemycolor)
161 EXTERN_CVAR (r_forceteamcolor)
162 static int enemycolor = 0, teamcolor = 0;
163
CL_GetPlayerColor(player_t * player)164 int CL_GetPlayerColor(player_t *player)
165 {
166 if (!player)
167 return 0;
168
169 int color = player->userinfo.color;
170
171 // Adjust the shade of color for team games
172 if (sv_gametype == GM_TEAMDM || sv_gametype == GM_CTF)
173 {
174 int red = RPART(player->userinfo.color);
175 int green = GPART(player->userinfo.color);
176 int blue = BPART(player->userinfo.color);
177
178 int intensity = MAX(MAX(red, green), blue) / 3;
179
180 if (player->userinfo.team == TEAM_BLUE)
181 color = MAKERGB(0, 0, 0xAA + intensity);
182 else if (player->userinfo.team == TEAM_RED)
183 color = MAKERGB(0xAA + intensity, 0, 0);
184 }
185
186 // apply r_teamcolor & r_enemycolor overrides
187 if (!consoleplayer().spectator)
188 {
189 if (sv_gametype == GM_COOP)
190 {
191 if (r_forceteamcolor && player->id != consoleplayer_id)
192 color = teamcolor;
193 }
194 else if (sv_gametype == GM_DM)
195 {
196 if (r_forceenemycolor && player->id != consoleplayer_id)
197 color = enemycolor;
198 }
199 else if (sv_gametype == GM_TEAMDM || sv_gametype == GM_CTF)
200 {
201 if (r_forceteamcolor &&
202 (P_AreTeammates(consoleplayer(), *player) || player->id == consoleplayer_id))
203 color = teamcolor;
204 if (r_forceenemycolor && !P_AreTeammates(consoleplayer(), *player) &&
205 player->id != consoleplayer_id)
206 color = enemycolor;
207 }
208 }
209
210 return color;
211 }
212
CL_RebuildAllPlayerTranslations()213 static void CL_RebuildAllPlayerTranslations()
214 {
215 // [SL] vanilla demo colors override
216 if (demoplayback)
217 return;
218
219 for (Players::iterator it = players.begin(); it != players.end(); ++it)
220 {
221 int color = CL_GetPlayerColor(&*it);
222 R_BuildPlayerTranslation(it->id, color);
223 }
224 }
225
CVAR_FUNC_IMPL(r_enemycolor)226 CVAR_FUNC_IMPL (r_enemycolor)
227 {
228 // cache the color whenever the user changes it
229 enemycolor = V_GetColorFromString(NULL, var.cstring());
230 CL_RebuildAllPlayerTranslations();
231 }
232
CVAR_FUNC_IMPL(r_teamcolor)233 CVAR_FUNC_IMPL (r_teamcolor)
234 {
235 // cache the color whenever the user changes it
236 teamcolor = V_GetColorFromString(NULL, var.cstring());
237 CL_RebuildAllPlayerTranslations();
238 }
239
CVAR_FUNC_IMPL(r_forceenemycolor)240 CVAR_FUNC_IMPL (r_forceenemycolor)
241 {
242 CL_RebuildAllPlayerTranslations();
243 }
244
CVAR_FUNC_IMPL(r_forceteamcolor)245 CVAR_FUNC_IMPL (r_forceteamcolor)
246 {
247 CL_RebuildAllPlayerTranslations();
248 }
249
CVAR_FUNC_IMPL(cl_team)250 CVAR_FUNC_IMPL (cl_team)
251 {
252 CL_RebuildAllPlayerTranslations();
253 }
254
255 EXTERN_CVAR (sv_maxplayers)
256 EXTERN_CVAR (sv_maxclients)
257 EXTERN_CVAR (sv_infiniteammo)
258 EXTERN_CVAR (sv_fraglimit)
259 EXTERN_CVAR (sv_timelimit)
260 EXTERN_CVAR (sv_nomonsters)
261 EXTERN_CVAR (sv_fastmonsters)
262 EXTERN_CVAR (sv_allowexit)
263 EXTERN_CVAR (sv_fragexitswitch)
264 EXTERN_CVAR (sv_allowjump)
265 EXTERN_CVAR (sv_allowredscreen)
266 EXTERN_CVAR (sv_scorelimit)
267 EXTERN_CVAR (sv_monstersrespawn)
268 EXTERN_CVAR (sv_itemsrespawn)
269 EXTERN_CVAR (sv_allowcheats)
270 EXTERN_CVAR (sv_allowtargetnames)
271 EXTERN_CVAR (sv_keepkeys)
272 EXTERN_CVAR (cl_mouselook)
273 EXTERN_CVAR (sv_freelook)
274 EXTERN_CVAR (cl_connectalert)
275 EXTERN_CVAR (cl_disconnectalert)
276 EXTERN_CVAR (waddirs)
277 EXTERN_CVAR (cl_autorecord)
278 EXTERN_CVAR (cl_splitnetdemos)
279 EXTERN_CVAR (st_scale)
280
281 void CL_PlayerTimes (void);
282 void CL_GetServerSettings(void);
283 void CL_RequestDownload(std::string filename, std::string filehash = "");
284 void CL_TryToConnect(DWORD server_token);
285 void CL_Decompress(int sequence);
286
287 void CL_LocalDemoTic(void);
288 void CL_NetDemoStop(void);
289 void CL_NetDemoSnapshot(void);
290 bool M_FindFreeName(std::string &filename, const std::string &extension);
291
292 void CL_SimulateWorld();
293
294 // [Toke - CTF]
295 void CalcTeamFrags (void);
296
297 // some doom functions (not csDoom)
298 void D_Display(void);
299 void D_DoAdvanceDemo(void);
300 void M_Ticker(void);
301
302 void R_InterpolationTicker();
303
304 size_t P_NumPlayersInGame();
305 void G_PlayerReborn (player_t &player);
306 void CL_SpawnPlayer ();
307 void P_KillMobj (AActor *source, AActor *target, AActor *inflictor, bool joinkill);
308 void P_SetPsprite (player_t *player, int position, statenum_t stnum);
309 void P_ExplodeMissile (AActor* mo);
310 void G_SetDefaultTurbo (void);
311 void P_CalcHeight (player_t *player);
312 bool P_CheckMissileSpawn (AActor* th);
313 void CL_SetMobjSpeedAndAngle(void);
314
315 void P_PlayerLookUpDown (player_t *p);
316 team_t D_TeamByName (const char *team);
317 gender_t D_GenderByName (const char *gender);
318 int V_GetColorFromString (const DWORD *palette, const char *colorstring);
319 void AM_Stop();
320
321 void ST_AdjustStatusBarScale(bool scale);
322
323 //
324 // CL_CalculateWorldIndexSync
325 //
326 // Calculates world_index based on the most recently received gametic from the
327 // server and the number of tics the client wants to withold for interpolation.
328 //
CL_CalculateWorldIndexSync()329 static int CL_CalculateWorldIndexSync()
330 {
331 return last_svgametic ? last_svgametic - cl_interp : 0;
332 }
333
334 //
335 // CL_CalculateWorldIndexDriftCorrection
336 //
337 // [SL] 2012-03-17 - Try to maintain sync with the server by gradually
338 // slowing down or speeding up world_index
339 //
CL_CalculateWorldIndexDriftCorrection()340 static int CL_CalculateWorldIndexDriftCorrection()
341 {
342 static const float CORRECTION_PERIOD = 1.0f / 16.0f;
343
344 int delta = CL_CalculateWorldIndexSync() - world_index;
345 if (delta == 0)
346 world_index_accum = 0.0f;
347 else
348 world_index_accum += CORRECTION_PERIOD * delta;
349
350 // truncate the decimal portion of world_index_accum
351 int correction = int(world_index_accum);
352
353 // reset world_index_accum if our correction will affect world_index
354 if (correction != 0)
355 world_index_accum = 0.0f;
356
357 return correction;
358 }
359
360 //
361 // CL_ResyncWorldIndex
362 //
363 // Recalculate world_index based and resets world_index_accum, which keeps
364 // track of how much the sync has drifted.
365 //
CL_ResyncWorldIndex()366 static void CL_ResyncWorldIndex()
367 {
368 world_index = CL_CalculateWorldIndexSync();
369 world_index_accum = 0.0f;
370 }
371
Host_EndGame(const char * msg)372 void Host_EndGame(const char *msg)
373 {
374 Printf(PRINT_HIGH, "%s", msg);
375 CL_QuitNetGame();
376 }
377
CL_QuitNetGame(void)378 void CL_QuitNetGame(void)
379 {
380 if(connected)
381 {
382 MSG_WriteMarker(&net_buffer, clc_disconnect);
383 NET_SendPacket(net_buffer, serveraddr);
384 SZ_Clear(&net_buffer);
385 sv_gametype = GM_COOP;
386 }
387
388 if (paused)
389 {
390 paused = false;
391 S_ResumeSound ();
392 }
393
394 memset (&serveraddr, 0, sizeof(serveraddr));
395 connected = false;
396 gameaction = ga_fullconsole;
397 noservermsgs = false;
398 AM_Stop();
399
400 serverside = clientside = true;
401 network_game = false;
402
403 sv_freelook = 1;
404 sv_allowjump = 1;
405 sv_allowexit = 1;
406 sv_allowredscreen = 1;
407
408 mute_spectators = 0.f;
409 mute_enemies = 0.f;
410
411 P_ClearAllNetIds();
412 players.clear();
413
414 recv_full_update = false;
415
416 if (netdemo.isRecording())
417 netdemo.stopRecording();
418
419 if (netdemo.isPlaying())
420 netdemo.stopPlaying();
421
422 // Reset the palette to default
423 if (I_HardwareInitialized())
424 {
425 int lu_palette = W_GetNumForName("PLAYPAL");
426 if (lu_palette != -1)
427 {
428 byte *pal = (byte *)W_CacheLumpNum(lu_palette, PU_CACHE);
429 if (pal)
430 {
431 I_SetOldPalette(pal);
432 }
433 }
434 }
435
436 // Reset the palette to default
437 if (I_HardwareInitialized())
438 {
439 int lu_palette = W_GetNumForName("PLAYPAL");
440 if (lu_palette != -1)
441 {
442 byte *pal = (byte *)W_CacheLumpNum(lu_palette, PU_CACHE);
443 if (pal)
444 {
445 I_SetOldPalette(pal);
446 }
447 }
448 }
449
450 cvar_t::C_RestoreCVars();
451 }
452
453
CL_Reconnect(void)454 void CL_Reconnect(void)
455 {
456 recv_full_update = false;
457
458 if (netdemo.isRecording())
459 forcenetdemosplit = true;
460
461 if (connected)
462 {
463 MSG_WriteMarker(&net_buffer, clc_disconnect);
464 NET_SendPacket(net_buffer, serveraddr);
465 SZ_Clear(&net_buffer);
466 connected = false;
467 gameaction = ga_fullconsole;
468
469 P_ClearAllNetIds();
470 }
471 else if (lastconaddr.ip[0])
472 {
473 serveraddr = lastconaddr;
474 }
475
476 connecttimeout = 0;
477 }
478
479 //
480 // CL_ConnectClient
481 //
CL_ConnectClient(void)482 void CL_ConnectClient(void)
483 {
484 player_t &player = idplayer(MSG_ReadByte());
485
486 if (!cl_connectalert)
487 return;
488
489 // GhostlyDeath <August 1, 2008> -- Play connect sound
490 if (&player == &consoleplayer())
491 return;
492
493 S_Sound (CHAN_INTERFACE, "misc/pljoin", 1, ATTN_NONE);
494 }
495
496 //
497 // CL_CheckDisplayPlayer
498 //
499 // Perfoms validation on the value of displayplayer_id based on the current
500 // game state and status of the consoleplayer.
501 //
CL_CheckDisplayPlayer()502 void CL_CheckDisplayPlayer()
503 {
504 static byte previd = consoleplayer_id;
505 byte newid = 0;
506
507 if (displayplayer_id != previd)
508 newid = displayplayer_id;
509
510 if (!validplayer(displayplayer()) || !displayplayer().mo)
511 newid = consoleplayer_id;
512
513 if (!(P_CanSpy(consoleplayer(), displayplayer()) ||
514 netdemo.isPlaying() || netdemo.isPaused()))
515 newid = consoleplayer_id;
516
517 if (displayplayer().spectator)
518 newid = consoleplayer_id;
519
520 if (newid)
521 {
522 // Request information about this player from the server
523 // (weapons, ammo, health, etc)
524 MSG_WriteMarker(&net_buffer, clc_spy);
525 MSG_WriteByte(&net_buffer, newid);
526 displayplayer_id = newid;
527
528 ST_AdjustStatusBarScale(st_scale != 0);
529 }
530
531 previd = newid;
532 }
533
534 //
535 // CL_SpyCycle
536 //
537 // Cycles through the point-of-view of players in the game. Checks
538 // are made to ensure only spectators can view enemy players.
539 //
540 template<class Iterator>
CL_SpyCycle(Iterator begin,Iterator end)541 void CL_SpyCycle(Iterator begin, Iterator end)
542 {
543 extern bool st_firsttime;
544
545 // Make sure we have players to iterate over
546 if (players.empty())
547 return;
548
549 if (!validplayer(displayplayer()))
550 {
551 CL_CheckDisplayPlayer();
552 return;
553 }
554
555 // set the sentinal iterator to point to displayplayer
556 Iterator sentinal = begin;
557 while (sentinal != end && sentinal->id != displayplayer_id)
558 ++sentinal;
559
560 // We can't find the displayplayer. This is bad.
561 if (sentinal == end)
562 return;
563
564 // iterate through all of the players until we reach sentinal again
565 Iterator it = sentinal;
566
567 do
568 {
569 // Increment iterator and wrap around if we hit end.
570 // The sentinal will stop the lop.
571 if (++it == end)
572 it = begin;
573
574 player_t& self = consoleplayer();
575 player_t& player = *it;
576
577 // spectators only cycle between active players
578 if (P_CanSpy(self, player) ||
579 (player.id == consoleplayer_id && !self.spectator) ||
580 demoplayback || netdemo.isPlaying() || netdemo.isPaused())
581 {
582 displayplayer_id = player.id;
583 CL_CheckDisplayPlayer();
584
585 if (demoplayback)
586 {
587 consoleplayer_id = player.id;
588 st_firsttime = true;
589 }
590
591 return;
592 }
593 } while (it != sentinal);
594 }
595
596 //
597 // CL_DisconnectClient
598 //
CL_DisconnectClient(void)599 void CL_DisconnectClient(void)
600 {
601 player_t &player = idplayer(MSG_ReadByte());
602 if (players.empty() || !validplayer(player))
603 return;
604
605 if (player.mo)
606 {
607 P_DisconnectEffect(player.mo);
608
609 // [AM] Destroying the player mobj is not our responsibility. However, we do want to
610 // make sure that the mobj->player doesn't point to an invalid player.
611 player.mo->player = NULL;
612 }
613
614 // Remove the player from the players list.
615 for (Players::iterator it = players.begin();it != players.end();++it)
616 {
617 if (it->id == player.id)
618 {
619 if (cl_disconnectalert && &player != &consoleplayer())
620 S_Sound(CHAN_INTERFACE, "misc/plpart", 1, ATTN_NONE);
621 players.erase(it);
622 break;
623 }
624 }
625
626 // if this was our displayplayer, update camera
627 CL_CheckDisplayPlayer();
628 }
629
630 extern BOOL advancedemo;
631 QWORD nextstep = 0;
632 int canceltics = 0;
633
CL_StepTics(unsigned int count)634 void CL_StepTics(unsigned int count)
635 {
636 DObject::BeginFrame ();
637
638 // run the realtics tics
639 while (count--)
640 {
641 if (canceltics && canceltics--)
642 continue;
643
644 NetUpdate();
645
646 if (advancedemo)
647 D_DoAdvanceDemo();
648
649 C_Ticker ();
650 M_Ticker ();
651
652 if (P_AtInterval(TICRATE))
653 CL_PlayerTimes();
654
655 if (sv_gametype == GM_CTF)
656 CTF_RunTics ();
657
658 Maplist_Runtic();
659
660 R_InterpolationTicker();
661
662 G_Ticker ();
663 gametic++;
664 if (netdemo.isPlaying() && !netdemo.isPaused())
665 netdemo.ticker();
666 }
667
668 DObject::EndFrame ();
669 }
670
671 //
672 // CL_DisplayTics
673 //
CL_DisplayTics()674 void CL_DisplayTics()
675 {
676 D_Display();
677 }
678
679 //
680 // CL_RunTics
681 //
CL_RunTics()682 void CL_RunTics()
683 {
684 std::string cmd = I_ConsoleInput();
685 if (cmd.length())
686 AddCommandString(cmd);
687
688 if (CON.is_open())
689 {
690 CON.clear();
691 if (!CON.eof())
692 {
693 std::getline(CON, cmd);
694 AddCommandString(cmd);
695 }
696 }
697
698 if (step_mode)
699 {
700 NetUpdate();
701
702 if (nextstep)
703 {
704 canceltics = 0;
705 CL_StepTics(nextstep);
706 nextstep = 0;
707
708 // debugging output
709 extern unsigned char prndindex;
710 if (!(players.empty()) && players.begin()->mo)
711 Printf(PRINT_HIGH, "level.time %d, prndindex %d, %d %d %d\n",
712 level.time, prndindex, players.begin()->mo->x, players.begin()->mo->y, players.begin()->mo->z);
713 else
714 Printf(PRINT_HIGH, "level.time %d, prndindex %d\n", level.time, prndindex);
715 }
716 }
717 else
718 {
719 CL_StepTics(1);
720 }
721
722 if (!connected)
723 CL_RequestConnectInfo();
724
725 // [RH] Use the consoleplayer's camera to update sounds
726 S_UpdateSounds(listenplayer().camera); // move positional sounds
727 S_UpdateMusic(); // play another chunk of music
728
729 D_DisplayTicker();
730 }
731
732 /////// CONSOLE COMMANDS ///////
733
BEGIN_COMMAND(stepmode)734 BEGIN_COMMAND (stepmode)
735 {
736 step_mode = !step_mode;
737 }
738 END_COMMAND (stepmode)
739
BEGIN_COMMAND(step)740 BEGIN_COMMAND (step)
741 {
742 nextstep = argc > 1 ? atoi(argv[1]) : 1;
743 }
744 END_COMMAND (step)
745
BEGIN_COMMAND(connect)746 BEGIN_COMMAND (connect)
747 {
748 if (argc == 1)
749 {
750 Printf(PRINT_HIGH, "Usage: connect ip[:port] [password]\n");
751 Printf(PRINT_HIGH, "\n");
752 Printf(PRINT_HIGH, "Connect to a server, with optional port number");
753 Printf(PRINT_HIGH, " and/or password\n");
754 Printf(PRINT_HIGH, "eg: connect 127.0.0.1\n");
755 Printf(PRINT_HIGH, "eg: connect 192.168.0.1:12345 secretpass\n");
756
757 return;
758 }
759
760 C_FullConsole();
761 gamestate = GS_CONNECTING;
762
763 CL_QuitNetGame();
764
765 if (argc > 1)
766 {
767 std::string target = argv[1];
768
769 // [Russell] - Passworded servers
770 if(argc > 2)
771 {
772 connectpasshash = MD5SUM(argv[2]);
773 }
774 else
775 {
776 connectpasshash = "";
777 }
778
779 if(NET_StringToAdr (target.c_str(), &serveraddr))
780 {
781 if (!serveraddr.port)
782 I_SetPort(serveraddr, SERVERPORT);
783
784 lastconaddr = serveraddr;
785 }
786 else
787 {
788 Printf(PRINT_HIGH, "Could not resolve host %s\n", target.c_str());
789 memset(&serveraddr, 0, sizeof(serveraddr));
790 }
791 }
792
793 connecttimeout = 0;
794 }
795 END_COMMAND (connect)
796
797
BEGIN_COMMAND(disconnect)798 BEGIN_COMMAND (disconnect)
799 {
800 CL_QuitNetGame();
801 }
802 END_COMMAND (disconnect)
803
804
BEGIN_COMMAND(reconnect)805 BEGIN_COMMAND (reconnect)
806 {
807 CL_Reconnect();
808 }
809 END_COMMAND (reconnect)
810
BEGIN_COMMAND(players)811 BEGIN_COMMAND (players)
812 {
813 // Gather all ingame players
814 std::map<int, std::string> mplayers;
815 for (Players::const_iterator it = players.begin();it != players.end();++it) {
816 if (it->ingame()) {
817 mplayers[it->id] = it->userinfo.netname;
818 }
819 }
820
821 // Print them, ordered by player id.
822 Printf(PRINT_HIGH, " PLAYERS IN GAME:\n");
823 for (std::map<int, std::string>::iterator it = mplayers.begin();it != mplayers.end();++it) {
824 Printf(PRINT_HIGH, "%d. %s\n", (*it).first, (*it).second.c_str());
825 }
826 }
827 END_COMMAND (players)
828
829
BEGIN_COMMAND(playerinfo)830 BEGIN_COMMAND (playerinfo)
831 {
832 player_t *player = &consoleplayer();
833
834 if(argc > 1)
835 {
836 size_t who = atoi(argv[1]);
837 player_t &p = idplayer(who);
838
839 if(!validplayer(p))
840 {
841 Printf (PRINT_HIGH, "Bad player number\n");
842 return;
843 }
844 else
845 player = &p;
846 }
847
848 if(!validplayer(*player))
849 {
850 Printf (PRINT_HIGH, "Not a valid player\n");
851 return;
852 }
853
854 Printf (PRINT_HIGH, "---------------[player info]----------- \n");
855 Printf (PRINT_HIGH, " userinfo.netname - %s \n", player->userinfo.netname.c_str());
856 Printf (PRINT_HIGH, " userinfo.team - %d \n", player->userinfo.team);
857 Printf (PRINT_HIGH, " userinfo.color - #%06x \n", player->userinfo.color);
858 Printf (PRINT_HIGH, " userinfo.gender - %d \n", player->userinfo.gender);
859 Printf (PRINT_HIGH, " spectator - %d \n", player->spectator);
860 Printf (PRINT_HIGH, " time - %d \n", player->GameTime);
861 Printf (PRINT_HIGH, "--------------------------------------- \n");
862 }
863 END_COMMAND (playerinfo)
864
865
BEGIN_COMMAND(kill)866 BEGIN_COMMAND (kill)
867 {
868 if (sv_allowcheats || (sv_gametype == GM_COOP && !sv_keepkeys))
869 MSG_WriteMarker(&net_buffer, clc_kill);
870 else
871 Printf (PRINT_HIGH, "You must run the server with '+set sv_allowcheats 1' or disable sv_keepkeys to enable this command.\n");
872 }
873 END_COMMAND (kill)
874
875
BEGIN_COMMAND(serverinfo)876 BEGIN_COMMAND (serverinfo)
877 {
878 std::vector<std::string> server_cvars;
879
880 cvar_t *Cvar = GetFirstCvar();
881 size_t MaxFieldLength = 0;
882
883 // [Russell] - Find the largest cvar name, used for formatting
884 while (Cvar)
885 {
886 if (Cvar->flags() & CVAR_SERVERINFO)
887 {
888 size_t FieldLength = strlen(Cvar->name());
889
890 if (FieldLength > MaxFieldLength)
891 MaxFieldLength = FieldLength;
892
893 // store this cvar name in our vector to be sorted later
894 server_cvars.push_back(Cvar->name());
895 }
896
897 Cvar = Cvar->GetNext();
898 }
899
900 // sort the list of cvars
901 std::sort(server_cvars.begin(), server_cvars.end());
902
903 // Heading
904 Printf (PRINT_HIGH, "\n%*s - Value\n", MaxFieldLength, "Name");
905
906 // Data
907 for (size_t i = 0; i < server_cvars.size(); i++)
908 {
909 cvar_t *dummy;
910 Cvar = cvar_t::FindCVar(server_cvars[i].c_str(), &dummy);
911
912 Printf(PRINT_HIGH,
913 "%*s - %s\n",
914 MaxFieldLength,
915 Cvar->name(),
916 Cvar->cstring());
917 }
918
919 Printf (PRINT_HIGH, "\n");
920 }
921 END_COMMAND (serverinfo)
922
923 // rate: takes a kbps value
CVAR_FUNC_IMPL(rate)924 CVAR_FUNC_IMPL (rate)
925 {
926 if (connected)
927 {
928 MSG_WriteMarker(&net_buffer, clc_rate);
929 MSG_WriteLong(&net_buffer, (int)var);
930 }
931 }
932
933
BEGIN_COMMAND(rcon)934 BEGIN_COMMAND (rcon)
935 {
936 if (connected && argc > 1)
937 {
938 char command[256];
939
940 if (argc == 2)
941 sprintf(command, "%s", argv[1]);
942 if (argc == 3)
943 sprintf(command, "%s %s", argv[1], argv[2]);
944 if (argc == 4)
945 sprintf(command, "%s %s %s", argv[1], argv[2], argv[3]);
946
947 MSG_WriteMarker(&net_buffer, clc_rcon);
948 MSG_WriteString(&net_buffer, command);
949 }
950 }
951 END_COMMAND (rcon)
952
953
BEGIN_COMMAND(rcon_password)954 BEGIN_COMMAND (rcon_password)
955 {
956 if (connected && argc > 1)
957 {
958 bool login = true;
959
960 MSG_WriteMarker(&net_buffer, clc_rcon_password);
961 MSG_WriteByte(&net_buffer, login);
962
963 std::string password = argv[1];
964 MSG_WriteString(&net_buffer, MD5SUM(password + digest).c_str());
965 }
966 }
967 END_COMMAND (rcon_password)
968
BEGIN_COMMAND(rcon_logout)969 BEGIN_COMMAND (rcon_logout)
970 {
971 if (connected)
972 {
973 bool login = false;
974
975 MSG_WriteMarker(&net_buffer, clc_rcon_password);
976 MSG_WriteByte(&net_buffer, login);
977 MSG_WriteString(&net_buffer, "");
978 }
979 }
980 END_COMMAND (rcon_logout)
981
982
BEGIN_COMMAND(playerteam)983 BEGIN_COMMAND (playerteam)
984 {
985 Printf (PRINT_MEDIUM, "Your Team is %d \n", consoleplayer().userinfo.team);
986 }
987 END_COMMAND (playerteam)
988
BEGIN_COMMAND(changeteams)989 BEGIN_COMMAND (changeteams)
990 {
991 if (consoleplayer().userinfo.team == TEAM_BLUE)
992 cl_team.Set("RED");
993 else if (consoleplayer().userinfo.team == TEAM_RED)
994 cl_team.Set("BLUE");
995 }
996 END_COMMAND (changeteams)
997
BEGIN_COMMAND(spectate)998 BEGIN_COMMAND (spectate)
999 {
1000 if (consoleplayer().spectator)
1001 {
1002 // reset camera to self, do not send any messages
1003 displayplayer_id = consoleplayer_id;
1004 CL_CheckDisplayPlayer();
1005 return;
1006 }
1007
1008 MSG_WriteMarker(&net_buffer, clc_spectate);
1009 MSG_WriteByte(&net_buffer, true);
1010 }
1011 END_COMMAND (spectate)
1012
BEGIN_COMMAND(ready)1013 BEGIN_COMMAND (ready) {
1014 MSG_WriteMarker(&net_buffer, clc_ready);
1015 } END_COMMAND (ready)
1016
BEGIN_COMMAND(join)1017 BEGIN_COMMAND (join)
1018 {
1019 if (P_NumPlayersInGame() >= sv_maxplayers)
1020 {
1021 C_MidPrint("The game is currently full", NULL);
1022 return;
1023 }
1024
1025 MSG_WriteMarker(&net_buffer, clc_spectate);
1026 MSG_WriteByte(&net_buffer, false);
1027 }
1028 END_COMMAND (join)
1029
BEGIN_COMMAND(flagnext)1030 BEGIN_COMMAND (flagnext)
1031 {
1032 if (sv_gametype == GM_CTF && (consoleplayer().spectator || netdemo.isPlaying()))
1033 {
1034 for (int i = 0; i < NUMTEAMS; i++)
1035 {
1036 byte id = CTFdata[i].flagger;
1037 if (id != 0 && displayplayer_id != id)
1038 {
1039 displayplayer_id = id;
1040 CL_CheckDisplayPlayer();
1041 return;
1042 }
1043 }
1044 }
1045 }
1046 END_COMMAND (flagnext)
1047
BEGIN_COMMAND(spynext)1048 BEGIN_COMMAND (spynext)
1049 {
1050 CL_SpyCycle(players.begin(), players.end());
1051 }
1052 END_COMMAND (spynext)
1053
BEGIN_COMMAND(spyprev)1054 BEGIN_COMMAND (spyprev)
1055 {
1056 CL_SpyCycle(players.rbegin(), players.rend());
1057 }
1058 END_COMMAND (spyprev)
1059
BEGIN_COMMAND(spy)1060 BEGIN_COMMAND (spy)
1061 {
1062 byte id = consoleplayer_id;
1063
1064 if (argc > 1)
1065 id = atoi(argv[1]);
1066
1067 if (id == 0)
1068 {
1069 Printf(PRINT_HIGH, "Expecting player ID. Try 'players' to list all of the player IDs.\n");
1070 return;
1071 }
1072
1073 displayplayer_id = id;
1074 CL_CheckDisplayPlayer();
1075
1076 if (displayplayer_id != id)
1077 Printf(PRINT_HIGH, "Unable to spy player ID %i!\n", id);
1078 }
1079 END_COMMAND (spy)
1080
1081 void STACK_ARGS call_terms (void);
1082
CL_QuitCommand()1083 void CL_QuitCommand()
1084 {
1085 call_terms();
1086 exit(EXIT_SUCCESS);
1087 }
1088
BEGIN_COMMAND(quit)1089 BEGIN_COMMAND (quit)
1090 {
1091 CL_QuitCommand();
1092 }
1093 END_COMMAND (quit)
1094
1095 // An alias for 'quit'
BEGIN_COMMAND(exit)1096 BEGIN_COMMAND (exit)
1097 {
1098 CL_QuitCommand();
1099 }
1100 END_COMMAND (exit)
1101
1102 //
1103 // NetDemo related functions
1104 //
1105
CVAR_FUNC_IMPL(cl_netdemoname)1106 CVAR_FUNC_IMPL (cl_netdemoname)
1107 {
1108 // No empty format strings allowed.
1109 if (strlen(var.cstring()) == 0)
1110 var.RestoreDefault();
1111 }
1112
1113 //
1114 // CL_GenerateNetDemoFileName
1115 //
1116 //
CL_GenerateNetDemoFileName(const std::string & filename=cl_netdemoname.cstring ())1117 std::string CL_GenerateNetDemoFileName(const std::string &filename = cl_netdemoname.cstring())
1118 {
1119 const std::string expanded_filename(M_ExpandTokens(filename));
1120 std::string newfilename(expanded_filename);
1121 newfilename = I_GetUserFileName(newfilename.c_str());
1122
1123 // keep trying to find a filename that doesn't yet exist
1124 if (!M_FindFreeName(newfilename, "odd"))
1125 {
1126 //I_Error("Unable to generate netdemo file name. Please delete some netdemos.");
1127 I_Warning("Unable to generate netdemo file name.");
1128 return std::string();
1129 }
1130
1131 return newfilename;
1132 }
1133
CL_NetDemoStop()1134 void CL_NetDemoStop()
1135 {
1136 netdemo.stopPlaying();
1137 }
1138
CL_NetDemoRecord(const std::string & filename)1139 void CL_NetDemoRecord(const std::string &filename)
1140 {
1141 netdemo.startRecording(filename);
1142 }
1143
CL_NetDemoPlay(const std::string & filename)1144 void CL_NetDemoPlay(const std::string &filename)
1145 {
1146 std::string newfilename;
1147
1148 std::string dir;
1149 M_ExtractFilePath(filename, dir);
1150
1151 // if no path is supplied, check the default path
1152 if (dir.empty())
1153 newfilename = I_GetUserFileName(filename.c_str());
1154 else
1155 newfilename = filename;
1156
1157 if (!M_FileExists(newfilename))
1158 {
1159 // try adding .odd to the end of the file name
1160 std::string ext;
1161 M_ExtractFileExtension(newfilename, ext);
1162 if (!iequals(ext, ".odd"))
1163 M_AppendExtension(newfilename, ".odd", false);
1164 }
1165
1166 netdemo.startPlaying(newfilename);
1167 }
1168
BEGIN_COMMAND(stopnetdemo)1169 BEGIN_COMMAND(stopnetdemo)
1170 {
1171 if (netdemo.isRecording())
1172 {
1173 netdemo.stopRecording();
1174 }
1175 else if (netdemo.isPlaying())
1176 {
1177 netdemo.stopPlaying();
1178 }
1179 }
1180 END_COMMAND(stopnetdemo)
1181
BEGIN_COMMAND(netrecord)1182 BEGIN_COMMAND(netrecord)
1183 {
1184 if (netdemo.isRecording())
1185 {
1186 Printf(PRINT_HIGH, "Already recording a netdemo. Please stop recording before "\
1187 "beginning a new netdemo recording.\n");
1188 return;
1189 }
1190
1191 if (!connected || simulated_connection)
1192 {
1193 Printf(PRINT_HIGH, "You must be connected to a server to record a netdemo.\n");
1194 return;
1195 }
1196
1197 std::string filename;
1198 if (argc > 1 && strlen(argv[1]) > 0)
1199 filename = CL_GenerateNetDemoFileName(argv[1]);
1200 else
1201 filename = CL_GenerateNetDemoFileName();
1202
1203 CL_NetDemoRecord(filename);
1204 netdemo.writeMapChange();
1205 }
1206 END_COMMAND(netrecord)
1207
BEGIN_COMMAND(netpause)1208 BEGIN_COMMAND(netpause)
1209 {
1210 if (netdemo.isPaused())
1211 {
1212 netdemo.resume();
1213 paused = false;
1214 Printf(PRINT_HIGH, "Demo resumed.\n");
1215 }
1216 else if (netdemo.isPlaying())
1217 {
1218 netdemo.pause();
1219 paused = true;
1220 Printf(PRINT_HIGH, "Demo paused.\n");
1221 }
1222 }
1223 END_COMMAND(netpause)
1224
BEGIN_COMMAND(netplay)1225 BEGIN_COMMAND(netplay)
1226 {
1227 if(argc <= 1)
1228 {
1229 Printf(PRINT_HIGH, "Usage: netplay <demoname>\n");
1230 return;
1231 }
1232
1233 if (!connected)
1234 {
1235 G_CheckDemoStatus(); // cleans up vanilla demo or single player game
1236 }
1237
1238 CL_QuitNetGame();
1239 connected = false;
1240
1241 std::string filename = argv[1];
1242 CL_NetDemoPlay(filename);
1243 }
1244 END_COMMAND(netplay)
1245
BEGIN_COMMAND(netdemostats)1246 BEGIN_COMMAND(netdemostats)
1247 {
1248 if (!netdemo.isPlaying() && !netdemo.isPaused())
1249 return;
1250
1251 std::vector<int> maptimes = netdemo.getMapChangeTimes();
1252 int curtime = netdemo.calculateTimeElapsed();
1253 int totaltime = netdemo.calculateTotalTime();
1254
1255 Printf(PRINT_HIGH, "\n%s\n", netdemo.getFileName().c_str());
1256 Printf(PRINT_HIGH, "============================================\n");
1257 Printf(PRINT_HIGH, "Total time: %i seconds\n", totaltime);
1258 Printf(PRINT_HIGH, "Current position: %i seconds (%i%%)\n",
1259 curtime, curtime * 100 / totaltime);
1260 Printf(PRINT_HIGH, "Number of maps: %i\n", maptimes.size());
1261 for (size_t i = 0; i < maptimes.size(); i++)
1262 {
1263 Printf(PRINT_HIGH, "> %02i Starting time: %i seconds\n",
1264 i + 1, maptimes[i]);
1265 }
1266 }
1267 END_COMMAND(netdemostats)
1268
BEGIN_COMMAND(netff)1269 BEGIN_COMMAND(netff)
1270 {
1271 if (netdemo.isPlaying())
1272 netdemo.nextSnapshot();
1273 }
1274 END_COMMAND(netff)
1275
BEGIN_COMMAND(netrew)1276 BEGIN_COMMAND(netrew)
1277 {
1278 if (netdemo.isPlaying())
1279 netdemo.prevSnapshot();
1280 }
1281 END_COMMAND(netrew)
1282
BEGIN_COMMAND(netnextmap)1283 BEGIN_COMMAND(netnextmap)
1284 {
1285 if (netdemo.isPlaying())
1286 netdemo.nextMap();
1287 }
1288 END_COMMAND(netnextmap)
1289
BEGIN_COMMAND(netprevmap)1290 BEGIN_COMMAND(netprevmap)
1291 {
1292 if (netdemo.isPlaying())
1293 netdemo.prevMap();
1294 }
END_COMMAND(netprevmap)1295 END_COMMAND(netprevmap)
1296
1297 void CL_NetDemoLoadSnap()
1298 {
1299 AddCommandString("netprevmap");
1300 }
1301
1302 //
1303 // CL_MoveThing
1304 //
CL_MoveThing(AActor * mobj,fixed_t x,fixed_t y,fixed_t z)1305 void CL_MoveThing(AActor *mobj, fixed_t x, fixed_t y, fixed_t z)
1306 {
1307 if (!mobj)
1308 return;
1309
1310 // [SL] 2011-11-06 - Return before setting the thing's floorz value if
1311 // the thing hasn't moved. This ensures the floorz value is correct for
1312 // things that have spawned (too close to a ledge) but have not yet moved.
1313 if (mobj->x == x && mobj->y == y && mobj->z == z)
1314 return;
1315
1316 P_CheckPosition(mobj, x, y);
1317 mobj->UnlinkFromWorld ();
1318
1319 mobj->x = x;
1320 mobj->y = y;
1321 mobj->z = z;
1322 mobj->floorz = tmfloorz;
1323 mobj->ceilingz = tmceilingz;
1324 mobj->dropoffz = tmdropoffz;
1325 mobj->floorsector = tmfloorsector;
1326 mobj->LinkToWorld ();
1327 }
1328
1329 //
1330 // CL_SendUserInfo
1331 //
CL_SendUserInfo(void)1332 void CL_SendUserInfo(void)
1333 {
1334 UserInfo* coninfo = &consoleplayer().userinfo;
1335 D_SetupUserInfo();
1336
1337 MSG_WriteMarker (&net_buffer, clc_userinfo);
1338 MSG_WriteString (&net_buffer, coninfo->netname.c_str());
1339 MSG_WriteByte (&net_buffer, coninfo->team); // [Toke]
1340 MSG_WriteLong (&net_buffer, coninfo->gender);
1341 MSG_WriteLong (&net_buffer, coninfo->color);
1342
1343 // [SL] place holder for deprecated skins
1344 MSG_WriteString (&net_buffer, "");
1345
1346 MSG_WriteLong (&net_buffer, coninfo->aimdist);
1347 MSG_WriteBool (&net_buffer, coninfo->unlag); // [SL] 2011-05-11
1348 MSG_WriteBool (&net_buffer, coninfo->predict_weapons);
1349 MSG_WriteByte (&net_buffer, (char)coninfo->update_rate);
1350 MSG_WriteByte (&net_buffer, (char)coninfo->switchweapon);
1351 for (size_t i = 0; i < NUMWEAPONS; i++)
1352 {
1353 MSG_WriteByte (&net_buffer, coninfo->weapon_prefs[i]);
1354 }
1355 }
1356
1357 //
1358 // CL_FindPlayer
1359 //
CL_FindPlayer(size_t id)1360 player_t &CL_FindPlayer(size_t id)
1361 {
1362 player_t *p = &idplayer(id);
1363
1364 // Totally new player?
1365 if(!validplayer(*p))
1366 {
1367 if (players.size() >= MAXPLAYERS)
1368 return *p;
1369
1370 players.push_back(player_s());
1371
1372 p = &players.back();
1373 p->id = id;
1374 }
1375
1376 return *p;
1377 }
1378
1379 //
1380 // CL_SetupUserInfo
1381 //
CL_SetupUserInfo(void)1382 void CL_SetupUserInfo(void)
1383 {
1384 byte who = MSG_ReadByte();
1385 player_t *p = &CL_FindPlayer(who);
1386
1387 p->userinfo.netname = MSG_ReadString();
1388 p->userinfo.team = (team_t)MSG_ReadByte();
1389 p->userinfo.gender = (gender_t)MSG_ReadLong();
1390 p->userinfo.color = MSG_ReadLong();
1391
1392 // [SL] place holder for deprecated skins
1393 MSG_ReadString();
1394
1395 p->GameTime = MSG_ReadShort();
1396
1397 if(p->userinfo.gender >= NUMGENDER)
1398 p->userinfo.gender = GENDER_NEUTER;
1399
1400 // [SL] 2012-04-30 - Were we looking through a teammate's POV who changed
1401 // to the other team?
1402 // [SL] 2012-05-24 - Were we spectating a teammate before we changed teams?
1403 CL_CheckDisplayPlayer();
1404
1405 int color = CL_GetPlayerColor(p);
1406 R_BuildPlayerTranslation (p->id, color);
1407
1408 extern bool st_firsttime;
1409 st_firsttime = true;
1410 }
1411
1412
1413 //
1414 // CL_UpdateFrags
1415 //
CL_UpdateFrags(void)1416 void CL_UpdateFrags(void)
1417 {
1418 player_t &p = CL_FindPlayer(MSG_ReadByte());
1419
1420 if(sv_gametype != GM_COOP)
1421 p.fragcount = MSG_ReadShort();
1422 else
1423 p.killcount = MSG_ReadShort();
1424 p.deathcount = MSG_ReadShort();
1425 p.points = MSG_ReadShort();
1426 }
1427
1428 //
1429 // [deathz0r] Receive team frags/captures
1430 //
CL_TeamPoints(void)1431 void CL_TeamPoints (void)
1432 {
1433 for(size_t i = 0; i < NUMTEAMS; i++)
1434 TEAMpoints[i] = MSG_ReadShort();
1435 }
1436
1437 //
1438 // CL_MoveMobj
1439 //
CL_MoveMobj(void)1440 void CL_MoveMobj(void)
1441 {
1442 AActor *mo;
1443 int netid;
1444 fixed_t x, y, z;
1445
1446 netid = MSG_ReadShort();
1447 mo = P_FindThingById (netid);
1448
1449 byte rndindex = MSG_ReadByte();
1450 x = MSG_ReadLong();
1451 y = MSG_ReadLong();
1452 z = MSG_ReadLong();
1453
1454 if (!mo)
1455 return;
1456
1457 if (mo->player)
1458 {
1459 // [SL] 2013-07-21 - Save the position information to a snapshot
1460 int snaptime = last_svgametic;
1461 PlayerSnapshot newsnap(snaptime);
1462 newsnap.setAuthoritative(true);
1463
1464 newsnap.setX(x);
1465 newsnap.setY(y);
1466 newsnap.setZ(z);
1467
1468 mo->player->snapshots.addSnapshot(newsnap);
1469 }
1470 else
1471 {
1472 CL_MoveThing (mo, x, y, z);
1473 mo->rndindex = rndindex;
1474 }
1475 }
1476
1477 //
1478 // CL_DamageMobj
1479 //
CL_DamageMobj()1480 void CL_DamageMobj()
1481 {
1482 AActor *mo;
1483 int netid, health, pain;
1484
1485 netid = MSG_ReadShort();
1486 health = MSG_ReadShort();
1487 pain = MSG_ReadByte();
1488
1489 mo = P_FindThingById (netid);
1490
1491 if (!mo)
1492 return;
1493
1494 mo->health = health;
1495
1496 if(pain < mo->info->painchance)
1497 P_SetMobjState(mo, mo->info->painstate);
1498 }
1499
1500 int connecttimeout = 0;
1501
1502 //
1503 // [denis] CL_RequestConnectInfo
1504 // Do what a launcher does...
1505 //
CL_RequestConnectInfo(void)1506 void CL_RequestConnectInfo(void)
1507 {
1508 if (!serveraddr.ip[0])
1509 return;
1510
1511 if(gamestate != GS_DOWNLOAD)
1512 gamestate = GS_CONNECTING;
1513
1514 if(!connecttimeout)
1515 {
1516 connecttimeout = 140;
1517
1518 Printf(PRINT_HIGH, "connecting to %s\n", NET_AdrToString(serveraddr));
1519
1520 SZ_Clear(&net_buffer);
1521 MSG_WriteLong(&net_buffer, LAUNCHER_CHALLENGE);
1522 NET_SendPacket(net_buffer, serveraddr);
1523 }
1524
1525 connecttimeout--;
1526 }
1527
1528 //
1529 // [denis] CL_PrepareConnect
1530 // Process server info and switch to the right wads...
1531 //
1532 std::string missing_file, missing_hash;
CL_PrepareConnect(void)1533 bool CL_PrepareConnect(void)
1534 {
1535 G_CleanupDemo(); // stop dmeos from playing before D_DoomWadReboot wipes out Zone memory
1536
1537 cvar_t::C_BackupCVars(CVAR_SERVERINFO);
1538
1539 size_t i;
1540 DWORD server_token = MSG_ReadLong();
1541 server_host = MSG_ReadString();
1542
1543 bool recv_teamplay_stats = 0;
1544 gameversiontosend = 0;
1545
1546 byte playercount = MSG_ReadByte(); // players
1547 MSG_ReadByte(); // max_players
1548
1549 std::string server_map = MSG_ReadString();
1550 byte server_wads = MSG_ReadByte();
1551
1552 Printf(PRINT_HIGH, "\n");
1553 Printf(PRINT_HIGH, "> Server: %s\n", server_host.c_str());
1554 Printf(PRINT_HIGH, "> Map: %s\n", server_map.c_str());
1555
1556 std::vector<std::string> newwadfiles(server_wads);
1557 for(i = 0; i < server_wads; i++)
1558 newwadfiles[i] = MSG_ReadString();
1559
1560 MSG_ReadBool(); // deathmatch
1561 MSG_ReadByte(); // skill
1562 recv_teamplay_stats |= MSG_ReadBool(); // teamplay
1563 recv_teamplay_stats |= MSG_ReadBool(); // ctf
1564
1565 for(i = 0; i < playercount; i++)
1566 {
1567 MSG_ReadString();
1568 MSG_ReadShort();
1569 MSG_ReadLong();
1570 MSG_ReadByte();
1571 }
1572
1573 std::vector<std::string> newwadhashes(server_wads);
1574 for(i = 0; i < server_wads; i++)
1575 {
1576 newwadhashes[i] = MSG_ReadString();
1577 Printf(PRINT_HIGH, "> %s\n %s\n", newwadfiles[i].c_str(), newwadhashes[i].c_str());
1578 }
1579
1580 MSG_ReadString();
1581
1582 // Receive conditional teamplay information
1583 if (recv_teamplay_stats)
1584 {
1585 MSG_ReadLong();
1586
1587 for(size_t i = 0; i < NUMTEAMS; i++)
1588 {
1589 bool enabled = MSG_ReadBool();
1590
1591 if (enabled)
1592 MSG_ReadLong();
1593 }
1594 }
1595
1596 version = MSG_ReadShort();
1597
1598 Printf(PRINT_HIGH, "> Server protocol version: %i\n", version);
1599
1600 if(version > VERSION)
1601 version = VERSION;
1602 if(version < 62)
1603 version = 62;
1604
1605 /* GhostlyDeath -- Need the actual version info */
1606 if (version == 65)
1607 {
1608 size_t l;
1609 MSG_ReadString();
1610
1611 for (l = 0; l < 3; l++)
1612 MSG_ReadShort();
1613 for (l = 0; l < 14; l++)
1614 MSG_ReadBool();
1615 for (l = 0; l < playercount; l++)
1616 {
1617 MSG_ReadShort();
1618 MSG_ReadShort();
1619 MSG_ReadShort();
1620 }
1621
1622 MSG_ReadLong();
1623 MSG_ReadShort();
1624
1625 for (l = 0; l < playercount; l++)
1626 MSG_ReadBool();
1627
1628 MSG_ReadLong();
1629 MSG_ReadShort();
1630
1631 gameversion = MSG_ReadLong();
1632
1633 // GhostlyDeath -- Assume 40 for compatibility and fake it
1634 if (((gameversion % 256) % 10) == -1)
1635 {
1636 gameversion = 40;
1637 gameversiontosend = 40;
1638 }
1639
1640 Printf(PRINT_HIGH, "> Server Version %i.%i.%i\n", gameversion / 256, (gameversion % 256) / 10, (gameversion % 256) % 10);
1641 }
1642
1643 Printf(PRINT_HIGH, "\n");
1644
1645 // DEH/BEX Patch files
1646 size_t patch_count = MSG_ReadByte();
1647 std::vector<std::string> newpatchfiles(patch_count);
1648
1649 for (i = 0; i < patch_count; ++i)
1650 newpatchfiles[i] = MSG_ReadString();
1651
1652 // TODO: Allow deh/bex file downloads
1653 D_DoomWadReboot(newwadfiles, newpatchfiles, newwadhashes);
1654
1655 if (!missingfiles.empty())
1656 {
1657 // denis - download files
1658 missing_file = missingfiles[0];
1659 missing_hash = missinghashes[0];
1660
1661 if (netdemo.isPlaying())
1662 {
1663 // Playing a netdemo and unable to download from the server
1664 Printf(PRINT_HIGH, "Unable to find \"%s\". Cannot download while playing a netdemo.\n", missing_file.c_str());
1665 CL_QuitNetGame();
1666 return false;
1667 }
1668
1669 gamestate = GS_DOWNLOAD;
1670 Printf(PRINT_HIGH, "Will download \"%s\" from server\n", missing_file.c_str());
1671 }
1672
1673 recv_full_update = false;
1674
1675 connecttimeout = 0;
1676 CL_TryToConnect(server_token);
1677
1678 return true;
1679 }
1680
1681 //
1682 // Connecting to a server...
1683 //
CL_Connect(void)1684 bool CL_Connect(void)
1685 {
1686 players.clear();
1687
1688 memset(packetseq, -1, sizeof(packetseq) );
1689 packetnum = 0;
1690
1691 MSG_WriteMarker(&net_buffer, clc_ack);
1692 MSG_WriteLong(&net_buffer, 0);
1693
1694 if(gamestate == GS_DOWNLOAD && missing_file.length())
1695 {
1696 // denis - do not download commercial wads
1697 if(W_IsIWAD(missing_file, missing_hash))
1698 {
1699 Printf(PRINT_HIGH, "This is a commercial wad and will not be downloaded.\n");
1700 CL_QuitNetGame();
1701 return false;
1702 }
1703
1704 CL_RequestDownload(missing_file, missing_hash);
1705 }
1706
1707 compressor.reset();
1708
1709 connected = true;
1710 multiplayer = true;
1711 network_game = true;
1712 serverside = false;
1713 simulated_connection = netdemo.isPlaying();
1714
1715 CL_Decompress(0);
1716 CL_ParseCommands();
1717
1718 if (gameaction == ga_fullconsole) // Host_EndGame was called
1719 return false;
1720
1721 D_SetupUserInfo();
1722
1723 //raise the weapon
1724 if(validplayer(consoleplayer()))
1725 consoleplayer().psprites[ps_weapon].sy = 32*FRACUNIT+0x6000;
1726
1727 noservermsgs = false;
1728 last_received = gametic;
1729
1730 if (gamestate != GS_DOWNLOAD)
1731 gamestate = GS_CONNECTED;
1732
1733 return true;
1734 }
1735
1736
1737 //
1738 // CL_InitNetwork
1739 //
CL_InitNetwork(void)1740 void CL_InitNetwork (void)
1741 {
1742 netgame = false; // for old network code
1743
1744 const char *v = Args.CheckValue ("-port");
1745 if (v)
1746 {
1747 localport = atoi (v);
1748 Printf (PRINT_HIGH, "using alternate port %i\n", localport);
1749 }
1750 else
1751 localport = CLIENTPORT;
1752
1753 // set up a socket and net_message buffer
1754 InitNetCommon();
1755
1756 SZ_Clear(&net_buffer);
1757
1758 size_t ParamIndex = Args.CheckParm ("-connect");
1759
1760 if (ParamIndex)
1761 {
1762 const char *ipaddress = Args.GetArg(ParamIndex + 1);
1763
1764 if (ipaddress && ipaddress[0] != '-' && ipaddress[0] != '+')
1765 {
1766 NET_StringToAdr (ipaddress, &serveraddr);
1767
1768 const char *passhash = Args.GetArg(ParamIndex + 2);
1769
1770 if (passhash && passhash[0] != '-' && passhash[0] != '+')
1771 {
1772 connectpasshash = MD5SUM(passhash);
1773 }
1774
1775 if (!serveraddr.port)
1776 I_SetPort(serveraddr, SERVERPORT);
1777
1778 lastconaddr = serveraddr;
1779 gamestate = GS_CONNECTING;
1780 }
1781 }
1782
1783 G_SetDefaultTurbo ();
1784
1785 connected = false;
1786 }
1787
CL_TryToConnect(DWORD server_token)1788 void CL_TryToConnect(DWORD server_token)
1789 {
1790 if (!serveraddr.ip[0])
1791 return;
1792
1793 if (!connecttimeout)
1794 {
1795 connecttimeout = 140; // 140 tics = 4 seconds
1796
1797 Printf(PRINT_HIGH, "challenging %s\n", NET_AdrToString(serveraddr));
1798
1799 SZ_Clear(&net_buffer);
1800 MSG_WriteLong(&net_buffer, CHALLENGE); // send challenge
1801 MSG_WriteLong(&net_buffer, server_token); // confirm server token
1802 MSG_WriteShort(&net_buffer, version); // send client version
1803
1804 if(gamestate == GS_DOWNLOAD)
1805 MSG_WriteByte(&net_buffer, 1); // send type of connection (play/spectate/rcon/download)
1806 else
1807 MSG_WriteByte(&net_buffer, 0); // send type of connection (play/spectate/rcon/download)
1808
1809 // GhostlyDeath -- Send more version info
1810 if (gameversiontosend)
1811 MSG_WriteLong(&net_buffer, gameversiontosend);
1812 else
1813 MSG_WriteLong(&net_buffer, GAMEVER);
1814
1815 CL_SendUserInfo(); // send userinfo
1816
1817 MSG_WriteLong(&net_buffer, (int)rate);
1818
1819 MSG_WriteString(&net_buffer, (char *)connectpasshash.c_str());
1820
1821 NET_SendPacket(net_buffer, serveraddr);
1822 SZ_Clear(&net_buffer);
1823 }
1824
1825 connecttimeout--;
1826 }
1827
EXTERN_CVAR(show_messages)1828 EXTERN_CVAR (show_messages)
1829
1830 //
1831 // CL_Print
1832 //
1833 void CL_Print (void)
1834 {
1835 byte level = MSG_ReadByte();
1836 const char *str = MSG_ReadString();
1837
1838 Printf (level, "%s", str);
1839
1840 if (show_messages)
1841 {
1842 if (level == PRINT_CHAT)
1843 S_Sound (CHAN_INTERFACE, gameinfo.chatSound, 1, ATTN_NONE);
1844 else if (level == PRINT_TEAMCHAT)
1845 S_Sound (CHAN_INTERFACE, "misc/teamchat", 1, ATTN_NONE);
1846 }
1847 }
1848
1849 // Print a message in the middle of the screen
CL_MidPrint(void)1850 void CL_MidPrint (void)
1851 {
1852 const char *str = MSG_ReadString();
1853 int msgtime = MSG_ReadShort();
1854
1855 C_MidPrint(str,NULL,msgtime);
1856 }
1857
1858 /**
1859 * Handle the svc_say server message, which contains a message from another
1860 * client with a player id attached to it.
1861 */
CL_Say()1862 void CL_Say()
1863 {
1864 byte who = MSG_ReadByte();
1865 byte player_id = MSG_ReadByte();
1866 const char* message = MSG_ReadString();
1867
1868 player_t &player = idplayer(player_id);
1869 const char* name;
1870
1871 if (!validplayer(player))
1872 return;
1873
1874 if (consoleplayer().id != player.id)
1875 {
1876 if (mute_spectators && player.spectator)
1877 return;
1878
1879 if (mute_enemies && !player.spectator &&
1880 (sv_gametype == GM_DM ||
1881 ((sv_gametype == GM_TEAMDM || sv_gametype == GM_CTF) &&
1882 player.userinfo.team != consoleplayer().userinfo.team)))
1883 return;
1884 }
1885
1886 name = player.userinfo.netname.c_str();
1887
1888 switch (who)
1889 {
1890 case 0:
1891 if (strnicmp(message, "/me ", 4) == 0)
1892 Printf(PRINT_CHAT, "* %s %s\n", name, &message[4]);
1893 else
1894 Printf(PRINT_CHAT, "%s: %s\n", name, message);
1895 if (show_messages)
1896 S_Sound(CHAN_INTERFACE, gameinfo.chatSound, 1, ATTN_NONE);
1897 break;
1898 case 1:
1899 if (strnicmp(message, "/me ", 4) == 0)
1900 Printf(PRINT_TEAMCHAT, "* %s %s\n", name, &message[4]);
1901 else
1902 Printf(PRINT_TEAMCHAT, "%s: %s\n", name, message);
1903 if (show_messages)
1904 S_Sound(CHAN_INTERFACE, "misc/teamchat", 1, ATTN_NONE);
1905 break;
1906 }
1907
1908 }
1909
1910 //
1911 // CL_PlayerJustTeleported
1912 //
1913 // Returns true if we have received a svc_activateline message from the server
1914 // involving this player and teleportation
1915 //
CL_PlayerJustTeleported(player_t * player)1916 bool CL_PlayerJustTeleported(player_t *player)
1917 {
1918 if (player && teleported_players.find(player->id) != teleported_players.end())
1919 return true;
1920
1921 return false;
1922 }
1923
1924 //
1925 // CL_ClearPlayerJustTeleported
1926 //
CL_ClearPlayerJustTeleported(player_t * player)1927 void CL_ClearPlayerJustTeleported(player_t *player)
1928 {
1929 if (player)
1930 teleported_players.erase(player->id);
1931 }
1932
CL_UpdatePlayer()1933 void CL_UpdatePlayer()
1934 {
1935 byte who = MSG_ReadByte();
1936 player_t *p = &idplayer(who);
1937
1938 MSG_ReadLong(); // Read and ignore for now
1939
1940 fixed_t x = MSG_ReadLong();
1941 fixed_t y = MSG_ReadLong();
1942 fixed_t z = MSG_ReadLong();
1943
1944 angle_t angle = MSG_ReadShort() << FRACBITS;
1945 angle_t pitch = MSG_ReadShort() << FRACBITS;
1946
1947 int frame = MSG_ReadByte();
1948 fixed_t momx = MSG_ReadLong();
1949 fixed_t momy = MSG_ReadLong();
1950 fixed_t momz = MSG_ReadLong();
1951
1952 int invisibility = MSG_ReadByte();
1953
1954 if (!validplayer(*p) || !p->mo)
1955 return;
1956
1957 // Mark the gametic this update arrived in for prediction code
1958 p->tic = gametic;
1959
1960 // GhostlyDeath -- Servers will never send updates on spectators
1961 if (p->spectator && (p != &consoleplayer()))
1962 p->spectator = 0;
1963
1964 // [Russell] - hack, read and set invisibility flag
1965 p->powers[pw_invisibility] = invisibility;
1966 if (p->powers[pw_invisibility])
1967 p->mo->flags |= MF_SHADOW;
1968 else
1969 p->mo->flags &= ~MF_SHADOW;
1970
1971 // This is a very bright frame. Looks cool :)
1972 if (frame == PLAYER_FULLBRIGHTFRAME)
1973 frame = 32773;
1974
1975 // denis - fixme - security
1976 if(!p->mo->sprite || (p->mo->frame&FF_FRAMEMASK) >= sprites[p->mo->sprite].numframes)
1977 return;
1978
1979 p->last_received = gametic;
1980 last_player_update = gametic;
1981
1982 // [SL] 2012-02-21 - Save the position information to a snapshot
1983 int snaptime = last_svgametic;
1984 PlayerSnapshot newsnap(snaptime);
1985 newsnap.setAuthoritative(true);
1986
1987 newsnap.setX(x);
1988 newsnap.setY(y);
1989 newsnap.setZ(z);
1990 newsnap.setMomX(momx);
1991 newsnap.setMomY(momy);
1992 newsnap.setMomZ(momz);
1993 newsnap.setAngle(angle);
1994 newsnap.setPitch(pitch);
1995 newsnap.setFrame(frame);
1996
1997 // Mark the snapshot as continuous unless the player just teleported
1998 // and lerping should be disabled
1999 newsnap.setContinuous(!CL_PlayerJustTeleported(p));
2000 CL_ClearPlayerJustTeleported(p);
2001
2002 p->snapshots.addSnapshot(newsnap);
2003 }
2004
2005 BOOL P_GiveWeapon(player_t *player, weapontype_t weapon, BOOL dropped);
2006
CL_UpdatePlayerState(void)2007 void CL_UpdatePlayerState(void)
2008 {
2009 byte id = MSG_ReadByte();
2010 short health = MSG_ReadShort();
2011 byte armortype = MSG_ReadByte();
2012 short armorpoints = MSG_ReadShort();
2013
2014 weapontype_t weap = static_cast<weapontype_t>(MSG_ReadByte());
2015
2016 short ammo[NUMAMMO];
2017 for (int i = 0; i < NUMAMMO; i++)
2018 ammo[i] = MSG_ReadShort();
2019
2020 statenum_t stnum[NUMPSPRITES];
2021 for (int i = 0; i < NUMPSPRITES; i++)
2022 {
2023 int n = MSG_ReadByte();
2024 if (n == 0xFF)
2025 stnum[i] = S_NULL;
2026 else
2027 stnum[i] = static_cast<statenum_t>(n);
2028 }
2029
2030 player_t &player = idplayer(id);
2031 if (!validplayer(player) || !player.mo)
2032 return;
2033
2034 player.health = player.mo->health = health;
2035 player.armortype = armortype;
2036 player.armorpoints = armorpoints;
2037
2038 player.readyweapon = weap;
2039 player.pendingweapon = wp_nochange;
2040
2041 if (!player.weaponowned[weap])
2042 P_GiveWeapon(&player, weap, false);
2043
2044 for (int i = 0; i < NUMAMMO; i++)
2045 player.ammo[i] = ammo[i];
2046
2047 for (int i = 0; i < NUMPSPRITES; i++)
2048 P_SetPsprite(&player, i, stnum[i]);
2049 }
2050
2051 //
2052 // CL_UpdateLocalPlayer
2053 //
CL_UpdateLocalPlayer(void)2054 void CL_UpdateLocalPlayer(void)
2055 {
2056 player_t &p = consoleplayer();
2057
2058 // The server has processed the ticcmd that the local client sent
2059 // during the the tic referenced below
2060 p.tic = MSG_ReadLong();
2061
2062 fixed_t x = MSG_ReadLong();
2063 fixed_t y = MSG_ReadLong();
2064 fixed_t z = MSG_ReadLong();
2065
2066 fixed_t momx = MSG_ReadLong();
2067 fixed_t momy = MSG_ReadLong();
2068 fixed_t momz = MSG_ReadLong();
2069
2070 byte waterlevel = MSG_ReadByte();
2071
2072 int snaptime = last_svgametic;
2073 PlayerSnapshot newsnapshot(snaptime);
2074 newsnapshot.setAuthoritative(true);
2075 newsnapshot.setX(x);
2076 newsnapshot.setY(y);
2077 newsnapshot.setZ(z);
2078 newsnapshot.setMomX(momx);
2079 newsnapshot.setMomY(momy);
2080 newsnapshot.setMomZ(momz);
2081 newsnapshot.setWaterLevel(waterlevel);
2082
2083 // Mark the snapshot as continuous unless the player just teleported
2084 // and lerping should be disabled
2085 newsnapshot.setContinuous(!CL_PlayerJustTeleported(&p));
2086 CL_ClearPlayerJustTeleported(&p);
2087
2088 consoleplayer().snapshots.addSnapshot(newsnapshot);
2089 }
2090
2091
2092 //
2093 // CL_SaveSvGametic
2094 //
2095 // Receives the server's gametic at the time the packet was sent. It will be
2096 // sent back to the server with the next cmd.
2097 //
2098 // [SL] 2011-05-11
CL_SaveSvGametic(void)2099 void CL_SaveSvGametic(void)
2100 {
2101 byte t = MSG_ReadByte();
2102
2103 int newtic = (last_svgametic & 0xFFFFFF00) + t;
2104
2105 if (last_svgametic > newtic + 127)
2106 newtic += 256;
2107
2108 last_svgametic = newtic;
2109
2110 #ifdef _WORLD_INDEX_DEBUG_
2111 Printf(PRINT_HIGH, "Gametic %i, received world index %i\n", gametic, last_svgametic);
2112 #endif // _WORLD_INDEX_DEBUG_
2113 }
2114
2115 //
2116 // CL_SendPingReply
2117 //
2118 // Replies to a server's ping request
2119 //
2120 // [SL] 2011-05-11 - Changed from CL_ResendSvGametic to CL_SendPingReply
2121 // for clarity since it sends timestamps, not gametics.
2122 //
CL_SendPingReply(void)2123 void CL_SendPingReply(void)
2124 {
2125 int svtimestamp = MSG_ReadLong();
2126 MSG_WriteMarker (&net_buffer, clc_pingreply);
2127 MSG_WriteLong (&net_buffer, svtimestamp);
2128 }
2129
2130 //
2131 // CL_UpdatePing
2132 // Update ping value
2133 //
CL_UpdatePing(void)2134 void CL_UpdatePing(void)
2135 {
2136 player_t &p = idplayer(MSG_ReadByte());
2137 p.ping = MSG_ReadLong();
2138 }
2139
2140
2141 //
2142 // CL_UpdateTimeLeft
2143 // Changes the value of level.timeleft
2144 //
CL_UpdateTimeLeft(void)2145 void CL_UpdateTimeLeft(void)
2146 {
2147 level.timeleft = MSG_ReadShort() * TICRATE; // convert from seconds to tics
2148 }
2149
2150 //
2151 // CL_UpdateIntTimeLeft
2152 // Changes the value of level.inttimeleft
2153 //
CL_UpdateIntTimeLeft(void)2154 void CL_UpdateIntTimeLeft(void)
2155 {
2156 level.inttimeleft = MSG_ReadShort(); // convert from seconds to tics
2157 }
2158
2159
2160 //
2161 // CL_SpawnMobj
2162 //
CL_SpawnMobj()2163 void CL_SpawnMobj()
2164 {
2165 AActor *mo;
2166
2167 fixed_t x = MSG_ReadLong();
2168 fixed_t y = MSG_ReadLong();
2169 fixed_t z = MSG_ReadLong();
2170 angle_t angle = MSG_ReadLong();
2171
2172 unsigned short type = MSG_ReadShort();
2173 unsigned short netid = MSG_ReadShort();
2174 byte rndindex = MSG_ReadByte();
2175 SWORD state = MSG_ReadShort();
2176
2177 if(type >= NUMMOBJTYPES)
2178 return;
2179
2180 P_ClearId(netid);
2181
2182 mo = new AActor (x, y, z, (mobjtype_t)type);
2183
2184 // denis - puff hack
2185 if(mo->type == MT_PUFF)
2186 {
2187 mo->momz = FRACUNIT;
2188 mo->tics -= M_Random () & 3;
2189 if (mo->tics < 1)
2190 mo->tics = 1;
2191 }
2192
2193 mo->angle = angle;
2194 P_SetThingId(mo, netid);
2195 mo->rndindex = rndindex;
2196
2197 if (state < NUMSTATES)
2198 P_SetMobjState(mo, (statenum_t)state);
2199
2200 if(mo->flags & MF_MISSILE)
2201 {
2202 AActor *target = P_FindThingById(MSG_ReadShort());
2203 if(target)
2204 mo->target = target->ptr();
2205 CL_SetMobjSpeedAndAngle();
2206 }
2207
2208 if (mo->flags & MF_COUNTKILL)
2209 level.total_monsters++;
2210
2211 if (connected && (mo->flags & MF_MISSILE ) && mo->info->seesound)
2212 S_Sound (mo, CHAN_VOICE, mo->info->seesound, 1, ATTN_NORM);
2213
2214 if (mo->type == MT_IFOG)
2215 S_Sound (mo, CHAN_VOICE, "misc/spawn", 1, ATTN_IDLE);
2216
2217 if (mo->type == MT_TFOG)
2218 {
2219 if (level.time) // don't play sound on first tic of the level
2220 S_Sound (mo, CHAN_VOICE, "misc/teleport", 1, ATTN_NORM);
2221 }
2222
2223 if (type == MT_FOUNTAIN)
2224 {
2225 mo->effects = int(MSG_ReadByte()) << FX_FOUNTAINSHIFT;
2226 }
2227
2228 if (type == MT_ZDOOMBRIDGE)
2229 {
2230 mo->radius = int(MSG_ReadByte()) << FRACBITS;
2231 mo->height = int(MSG_ReadByte()) << FRACBITS;
2232 }
2233 }
2234
2235 //
2236 // CL_Corpse
2237 // Called after killed thing is created.
2238 //
CL_Corpse(void)2239 void CL_Corpse(void)
2240 {
2241 AActor *mo = P_FindThingById(MSG_ReadShort());
2242 int frame = MSG_ReadByte();
2243 int tics = MSG_ReadByte();
2244
2245 if(tics == 0xFF)
2246 tics = -1;
2247
2248 // already spawned as gibs?
2249 if (!mo || mo->state - states == S_GIBS)
2250 return;
2251
2252 if((frame&FF_FRAMEMASK) >= sprites[mo->sprite].numframes)
2253 return;
2254
2255 mo->frame = frame;
2256 mo->tics = tics;
2257
2258 // from P_KillMobj
2259 mo->flags &= ~(MF_SHOOTABLE|MF_FLOAT|MF_SKULLFLY);
2260 mo->flags |= MF_CORPSE|MF_DROPOFF;
2261 mo->height >>= 2;
2262 mo->flags &= ~MF_SOLID;
2263
2264 if (mo->player)
2265 mo->player->playerstate = PST_DEAD;
2266
2267 if (mo->flags & MF_COUNTKILL)
2268 level.killed_monsters++;
2269 }
2270
2271 //
2272 // CL_TouchSpecialThing
2273 //
CL_TouchSpecialThing(void)2274 void CL_TouchSpecialThing (void)
2275 {
2276 AActor *mo = P_FindThingById(MSG_ReadShort());
2277
2278 if(!consoleplayer().mo || !mo)
2279 return;
2280
2281 P_GiveSpecial(&consoleplayer(), mo);
2282 }
2283
2284
2285 //
2286 // CL_SpawnPlayer
2287 //
CL_SpawnPlayer()2288 void CL_SpawnPlayer()
2289 {
2290 byte playernum;
2291 player_t *p;
2292 AActor *mobj;
2293 fixed_t x = 0, y = 0, z = 0;
2294 unsigned short netid;
2295 angle_t angle = 0;
2296 int i;
2297
2298 playernum = MSG_ReadByte();
2299 netid = MSG_ReadShort();
2300 p = &CL_FindPlayer(playernum);
2301
2302 angle = MSG_ReadLong();
2303 x = MSG_ReadLong();
2304 y = MSG_ReadLong();
2305 z = MSG_ReadLong();
2306
2307 P_ClearId(netid);
2308
2309 // first disassociate the corpse
2310 if (p->mo)
2311 {
2312 p->mo->player = NULL;
2313 p->mo->health = 0;
2314 }
2315
2316 G_PlayerReborn (*p);
2317
2318 mobj = new AActor (x, y, z, MT_PLAYER);
2319
2320 mobj->momx = mobj->momy = mobj->momz = 0;
2321
2322 // set color translations for player sprites
2323 mobj->translation = translationref_t(translationtables + 256*playernum, playernum);
2324 mobj->angle = angle;
2325 mobj->pitch = 0;
2326 mobj->player = p;
2327 mobj->health = p->health;
2328 P_SetThingId(mobj, netid);
2329
2330 p->mo = p->camera = mobj->ptr();
2331 p->fov = 90.0f;
2332 p->playerstate = PST_LIVE;
2333 p->refire = 0;
2334 p->damagecount = 0;
2335 p->bonuscount = 0;
2336 p->extralight = 0;
2337 p->fixedcolormap = 0;
2338
2339 p->xviewshift = 0;
2340 p->viewheight = VIEWHEIGHT;
2341
2342 p->attacker = AActor::AActorPtr();
2343 p->viewz = z + VIEWHEIGHT;
2344
2345 // spawn a teleport fog
2346 // tfog = new AActor (x, y, z, MT_TFOG);
2347
2348 // setup gun psprite
2349 P_SetupPsprites (p);
2350
2351 // give all cards in death match mode
2352 if(sv_gametype != GM_COOP)
2353 for (i = 0; i < NUMCARDS; i++)
2354 p->cards[i] = true;
2355
2356 if(p->id == consoleplayer_id)
2357 {
2358 // denis - if this concerns the local player, restart the status bar
2359 ST_Start ();
2360
2361 // [SL] 2012-04-23 - Clear predicted sectors
2362 movingsectors.clear();
2363 }
2364
2365 if (p->id == displayplayer().id)
2366 {
2367 // [SL] 2012-03-08 - Resync with the server's incoming tic since we don't care
2368 // about players/sectors jumping to new positions when the displayplayer spawns
2369 CL_ResyncWorldIndex();
2370 }
2371
2372 int snaptime = last_svgametic;
2373 PlayerSnapshot newsnap(snaptime, p);
2374 newsnap.setAuthoritative(true);
2375 newsnap.setContinuous(false);
2376 p->snapshots.clearSnapshots();
2377 p->snapshots.addSnapshot(newsnap);
2378 }
2379
2380 //
2381 // CL_PlayerInfo
2382 // denis - your personal arsenal, as supplied by the server
2383 //
CL_PlayerInfo(void)2384 void CL_PlayerInfo(void)
2385 {
2386 player_t *p = &consoleplayer();
2387
2388 uint16_t booleans = MSG_ReadShort();
2389
2390 for (int i = 0; i < NUMWEAPONS; i++)
2391 p->weaponowned[i] = booleans & 1 << i;
2392 for (int i = 0; i < NUMCARDS; i++)
2393 p->cards[i] = booleans & 1 << (i + NUMWEAPONS);
2394 p->backpack = booleans & 1 << (NUMWEAPONS + NUMCARDS);
2395
2396 for (int i = 0; i < NUMAMMO; i++)
2397 {
2398 p->maxammo[i] = MSG_ReadShort();
2399 p->ammo[i] = MSG_ReadShort();
2400 }
2401
2402 p->health = MSG_ReadByte();
2403 p->armorpoints = MSG_ReadByte();
2404 p->armortype = MSG_ReadByte();
2405
2406 weapontype_t newweapon = static_cast<weapontype_t>(MSG_ReadByte());
2407 if (newweapon >= NUMWEAPONS) // bad weapon number, choose something else
2408 newweapon = wp_fist;
2409
2410 if (newweapon != p->readyweapon)
2411 p->pendingweapon = newweapon;
2412
2413 for (int i = 0; i < NUMPOWERS; i++)
2414 p->powers[i] = MSG_ReadShort();
2415 }
2416
2417 //
2418 // CL_SetMobjSpeedAndAngle
2419 //
CL_SetMobjSpeedAndAngle(void)2420 void CL_SetMobjSpeedAndAngle(void)
2421 {
2422 AActor *mo;
2423 int netid;
2424
2425 netid = MSG_ReadShort();
2426 mo = P_FindThingById(netid);
2427
2428 angle_t angle = MSG_ReadLong();
2429 fixed_t momx = MSG_ReadLong();
2430 fixed_t momy = MSG_ReadLong();
2431 fixed_t momz = MSG_ReadLong();
2432
2433 if (!mo)
2434 return;
2435
2436 if (mo->player)
2437 {
2438 // [SL] 2013-07-21 - Save the position information to a snapshot
2439 int snaptime = last_svgametic;
2440 PlayerSnapshot newsnap(snaptime);
2441 newsnap.setAuthoritative(true);
2442
2443 newsnap.setMomX(momx);
2444 newsnap.setMomY(momy);
2445 newsnap.setMomZ(momz);
2446
2447 mo->player->snapshots.addSnapshot(newsnap);
2448 }
2449 else
2450 {
2451 mo->angle = angle;
2452 mo->momx = momx;
2453 mo->momy = momy;
2454 mo->momz = momz;
2455 }
2456 }
2457
2458 //
2459 // CL_ExplodeMissile
2460 //
CL_ExplodeMissile(void)2461 void CL_ExplodeMissile(void)
2462 {
2463 AActor *mo;
2464 int netid;
2465
2466 netid = MSG_ReadShort();
2467 mo = P_FindThingById(netid);
2468
2469 if (!mo)
2470 return;
2471
2472 P_ExplodeMissile(mo);
2473 }
2474
2475
2476 //
2477 // CL_RailTrail
2478 //
CL_RailTrail()2479 void CL_RailTrail()
2480 {
2481 v3double_t start, end;
2482
2483 start.x = double(MSG_ReadShort());
2484 start.y = double(MSG_ReadShort());
2485 start.z = double(MSG_ReadShort());
2486 end.x = double(MSG_ReadShort());
2487 end.y = double(MSG_ReadShort());
2488 end.z = double(MSG_ReadShort());
2489
2490 P_DrawRailTrail(start, end);
2491 }
2492
2493 //
2494 // CL_UpdateMobjInfo
2495 //
CL_UpdateMobjInfo(void)2496 void CL_UpdateMobjInfo(void)
2497 {
2498 int netid = MSG_ReadShort();
2499 int flags = MSG_ReadLong();
2500 //int flags2 = MSG_ReadLong();
2501
2502 AActor *mo = P_FindThingById(netid);
2503
2504 if (!mo)
2505 return;
2506
2507 mo->flags = flags;
2508 //mo->flags2 = flags2;
2509 }
2510
2511
2512 //
2513 // CL_RemoveMobj
2514 //
CL_RemoveMobj(void)2515 void CL_RemoveMobj(void)
2516 {
2517 int netid = MSG_ReadShort();
2518
2519 AActor *mo = P_FindThingById(netid);
2520 if (mo && mo->player && mo->player->id == displayplayer_id)
2521 displayplayer_id = consoleplayer_id;
2522
2523 P_ClearId(netid);
2524 }
2525
2526
2527 //
2528 // CL_DamagePlayer
2529 //
CL_DamagePlayer(void)2530 void CL_DamagePlayer(void)
2531 {
2532 player_t *p;
2533 int health;
2534 int damage;
2535
2536 p = &idplayer(MSG_ReadByte());
2537
2538 p->armorpoints = MSG_ReadByte();
2539 health = MSG_ReadShort();
2540
2541 if(!p->mo)
2542 return;
2543
2544 damage = p->health - health;
2545 p->mo->health = p->health = health;
2546
2547 if (p->health < 0)
2548 p->health = 0;
2549
2550 if (damage < 0) // can't be!
2551 return;
2552
2553 if (damage > 0) {
2554 p->damagecount += damage;
2555
2556 if (p->damagecount > 100)
2557 p->damagecount = 100;
2558
2559 if(p->mo->info->painstate)
2560 P_SetMobjState(p->mo, p->mo->info->painstate);
2561 }
2562 }
2563
2564 extern int MeansOfDeath;
2565
2566 //
2567 // CL_KillMobj
2568 //
CL_KillMobj(void)2569 void CL_KillMobj(void)
2570 {
2571 AActor *source = P_FindThingById (MSG_ReadShort() );
2572 AActor *target = P_FindThingById (MSG_ReadShort() );
2573 AActor *inflictor = P_FindThingById (MSG_ReadShort() );
2574 int health = MSG_ReadShort();
2575
2576 MeansOfDeath = MSG_ReadLong();
2577
2578 bool joinkill = ((MSG_ReadByte()) != 0);
2579
2580 if (!target)
2581 return;
2582
2583 target->health = health;
2584
2585 if (!serverside && target->flags & MF_COUNTKILL)
2586 level.killed_monsters++;
2587
2588 if (target->player == &consoleplayer())
2589 for (size_t i = 0; i < MAXSAVETICS; i++)
2590 localcmds[i].clear();
2591
2592 P_KillMobj (source, target, inflictor, joinkill);
2593 }
2594
2595
2596 ///////////////////////////////////////////////////////////
2597 ///// CL_Fire* called when someone uses a weapon /////////
2598 ///////////////////////////////////////////////////////////
2599
2600 // [tm512] attempt at squashing weapon desyncs.
2601 // The server will send us what weapon we fired, and if that
2602 // doesn't match the weapon we have up at the moment, fix it
2603 // and request that we get a full update of playerinfo - apr 14 2012
CL_FireWeapon(void)2604 void CL_FireWeapon (void)
2605 {
2606 player_t *p = &consoleplayer ();
2607 weapontype_t firedweap = (weapontype_t) MSG_ReadByte ();
2608 int servertic = MSG_ReadLong ();
2609
2610 if (firedweap != p->readyweapon)
2611 {
2612 DPrintf("CL_FireWeapon: weapon misprediction\n");
2613 A_ForceWeaponFire(p->mo, firedweap, servertic);
2614
2615 // Request the player's ammo status from the server
2616 MSG_WriteMarker (&net_buffer, clc_getplayerinfo);
2617 }
2618
2619 }
2620
2621 //
2622 // CL_FirePistol
2623 //
CL_FirePistol(void)2624 void CL_FirePistol(void)
2625 {
2626 player_t &p = idplayer(MSG_ReadByte());
2627
2628 if(!validplayer(p))
2629 return;
2630
2631 if(!p.mo)
2632 return;
2633
2634 if (&p != &consoleplayer())
2635 S_Sound (p.mo, CHAN_WEAPON, "weapons/pistol", 1, ATTN_NORM);
2636 }
2637
2638 //
2639 // CL_FireShotgun
2640 //
CL_FireShotgun(void)2641 void CL_FireShotgun(void)
2642 {
2643 player_t &p = idplayer(MSG_ReadByte());
2644
2645 if(!validplayer(p))
2646 return;
2647
2648 if(!p.mo)
2649 return;
2650
2651 if (&p != &consoleplayer())
2652 S_Sound (p.mo, CHAN_WEAPON, "weapons/shotgf", 1, ATTN_NORM);
2653 }
2654
2655 //
2656 // CL_FireSSG
2657 //
CL_FireSSG(void)2658 void CL_FireSSG(void)
2659 {
2660 player_t &p = idplayer(MSG_ReadByte());
2661
2662 if(!validplayer(p))
2663 return;
2664
2665 if(!p.mo)
2666 return;
2667
2668 if (&p != &consoleplayer())
2669 S_Sound (p.mo, CHAN_WEAPON, "weapons/sshotf", 1, ATTN_NORM);
2670 }
2671
2672
2673 //
2674 // CL_FireChainGun
2675 //
CL_FireChainGun(void)2676 void CL_FireChainGun(void)
2677 {
2678 player_t &p = idplayer(MSG_ReadByte());
2679
2680 if(!validplayer(p))
2681 return;
2682
2683 if(!p.mo)
2684 return;
2685
2686 if (&p != &consoleplayer())
2687 S_Sound (p.mo, CHAN_WEAPON, "weapons/chngun", 1, ATTN_NORM);
2688 }
2689
2690 //
2691 // CL_ChangeWeapon
2692 // [ML] From Zdaemon .99
2693 //
CL_ChangeWeapon(void)2694 void CL_ChangeWeapon (void)
2695 {
2696 player_t *player = &consoleplayer();
2697 weapontype_t newweapon = (weapontype_t)MSG_ReadByte();
2698
2699 // ensure that the client has the weapon
2700 player->weaponowned[newweapon] = true;
2701
2702 // [SL] 2011-09-22 - Only change the weapon if the client doesn't already
2703 // have that weapon up.
2704 if (player->readyweapon != newweapon)
2705 player->pendingweapon = newweapon;
2706 }
2707
2708
2709 //
2710 // CL_Sound
2711 //
CL_Sound(void)2712 void CL_Sound(void)
2713 {
2714 int netid = MSG_ReadShort();
2715 int x = MSG_ReadLong();
2716 int y = MSG_ReadLong();
2717 byte channel = MSG_ReadByte();
2718 byte sfx_id = MSG_ReadByte();
2719 byte attenuation = MSG_ReadByte();
2720 byte vol = MSG_ReadByte();
2721
2722 AActor *mo = P_FindThingById (netid);
2723
2724 float volume = vol/(float)255;
2725
2726 if(mo)
2727 S_SoundID (mo, channel, sfx_id, volume, attenuation); // play at thing location
2728 else
2729 S_SoundID (x, y, channel, sfx_id, volume, attenuation); // play at approximate thing location
2730 }
2731
CL_SoundOrigin(void)2732 void CL_SoundOrigin(void)
2733 {
2734 int x = MSG_ReadLong();
2735 int y = MSG_ReadLong();
2736 byte channel = MSG_ReadByte();
2737 byte sfx_id = MSG_ReadByte();
2738 byte attenuation = MSG_ReadByte();
2739 byte vol = MSG_ReadByte();
2740
2741 float volume = vol/(float)255;
2742
2743 AActor *mo = consoleplayer().mo;
2744
2745 if(!mo)
2746 return;
2747
2748 S_SoundID (x, y, channel, sfx_id, volume, attenuation);
2749 }
2750
2751 //
2752 // CL_ClearSectorSnapshots
2753 //
2754 // Removes all sector snapshots at the start of a map, etc
2755 //
CL_ClearSectorSnapshots()2756 void CL_ClearSectorSnapshots()
2757 {
2758 sector_snaps.clear();
2759 }
2760
2761 //
2762 // CL_UpdateSector
2763 // Updates floorheight and ceilingheight of a sector.
2764 //
CL_UpdateSector(void)2765 void CL_UpdateSector(void)
2766 {
2767 unsigned short sectornum = (unsigned short)MSG_ReadShort();
2768 unsigned short floorheight = MSG_ReadShort();
2769 unsigned short ceilingheight = MSG_ReadShort();
2770
2771 unsigned short fp = MSG_ReadShort();
2772 unsigned short cp = MSG_ReadShort();
2773
2774 if (!sectors || sectornum >= numsectors)
2775 return;
2776
2777 sector_t *sector = §ors[sectornum];
2778 P_SetCeilingHeight(sector, ceilingheight << FRACBITS);
2779 P_SetFloorHeight(sector, floorheight << FRACBITS);
2780
2781 if(fp >= numflats)
2782 fp = numflats;
2783
2784 sector->floorpic = fp;
2785
2786 if(cp >= numflats)
2787 cp = numflats;
2788
2789 sector->ceilingpic = cp;
2790 sector->moveable = true;
2791
2792 P_ChangeSector(sector, false);
2793
2794 SectorSnapshot snap(last_svgametic, sector);
2795 sector_snaps[sectornum].addSnapshot(snap);
2796 }
2797
2798 //
2799 // CL_UpdateMovingSector
2800 // Updates floorheight and ceilingheight of a sector.
2801 //
CL_UpdateMovingSector(void)2802 void CL_UpdateMovingSector(void)
2803 {
2804 unsigned short sectornum = (unsigned short)MSG_ReadShort();
2805
2806 fixed_t ceilingheight = MSG_ReadShort() << FRACBITS;
2807 fixed_t floorheight = MSG_ReadShort() << FRACBITS;
2808
2809 byte movers = MSG_ReadByte();
2810 movertype_t ceiling_mover = static_cast<movertype_t>(movers & 0x0F);
2811 movertype_t floor_mover = static_cast<movertype_t>((movers & 0xF0) >> 4);
2812
2813 if (ceiling_mover == SEC_ELEVATOR)
2814 floor_mover = SEC_INVALID;
2815 if (ceiling_mover == SEC_PILLAR)
2816 floor_mover = SEC_INVALID;
2817
2818 SectorSnapshot snap(last_svgametic);
2819
2820 snap.setCeilingHeight(ceilingheight);
2821 snap.setFloorHeight(floorheight);
2822
2823 if (floor_mover == SEC_FLOOR)
2824 {
2825 // Floors/Stairbuilders
2826 snap.setFloorMoverType(SEC_FLOOR);
2827 snap.setFloorType(static_cast<DFloor::EFloor>(MSG_ReadByte()));
2828 snap.setFloorStatus(MSG_ReadByte());
2829 snap.setFloorCrush(MSG_ReadBool());
2830 snap.setFloorDirection(char(MSG_ReadByte()));
2831 snap.setFloorSpecial(MSG_ReadShort());
2832 snap.setFloorTexture(MSG_ReadShort());
2833 snap.setFloorDestination(MSG_ReadShort() << FRACBITS);
2834 snap.setFloorSpeed(MSG_ReadShort() << FRACBITS);
2835 snap.setResetCounter(MSG_ReadLong());
2836 snap.setOrgHeight(MSG_ReadShort() << FRACBITS);
2837 snap.setDelay(MSG_ReadLong());
2838 snap.setPauseTime(MSG_ReadLong());
2839 snap.setStepTime(MSG_ReadLong());
2840 snap.setPerStepTime(MSG_ReadLong());
2841 snap.setFloorOffset(MSG_ReadShort() << FRACBITS);
2842 snap.setFloorChange(MSG_ReadByte());
2843
2844 int LineIndex = MSG_ReadLong();
2845
2846 if (!lines || LineIndex >= numlines || LineIndex < 0)
2847 snap.setFloorLine(NULL);
2848 else
2849 snap.setFloorLine(&lines[LineIndex]);
2850 }
2851
2852 if (floor_mover == SEC_PLAT)
2853 {
2854 // Platforms/Lifts
2855 snap.setFloorMoverType(SEC_PLAT);
2856 snap.setFloorSpeed(MSG_ReadShort() << FRACBITS);
2857 snap.setFloorLow(MSG_ReadShort() << FRACBITS);
2858 snap.setFloorHigh(MSG_ReadShort() << FRACBITS);
2859 snap.setFloorWait(MSG_ReadLong());
2860 snap.setFloorCounter(MSG_ReadLong());
2861 snap.setFloorStatus(MSG_ReadByte());
2862 snap.setOldFloorStatus(MSG_ReadByte());
2863 snap.setFloorCrush(MSG_ReadBool());
2864 snap.setFloorTag(MSG_ReadShort());
2865 snap.setFloorType(MSG_ReadByte());
2866 snap.setFloorOffset(MSG_ReadShort() << FRACBITS);
2867 snap.setFloorLip(MSG_ReadShort() << FRACBITS);
2868 }
2869
2870 if (ceiling_mover == SEC_CEILING)
2871 {
2872 // Ceilings / Crushers
2873 snap.setCeilingMoverType(SEC_CEILING);
2874 snap.setCeilingType(MSG_ReadByte());
2875 snap.setCeilingLow(MSG_ReadShort() << FRACBITS);
2876 snap.setCeilingHigh(MSG_ReadShort() << FRACBITS);
2877 snap.setCeilingSpeed(MSG_ReadShort() << FRACBITS);
2878 snap.setCrusherSpeed1(MSG_ReadShort() << FRACBITS);
2879 snap.setCrusherSpeed2(MSG_ReadShort() << FRACBITS);
2880 snap.setCeilingCrush(MSG_ReadBool());
2881 snap.setSilent(MSG_ReadBool());
2882 snap.setCeilingDirection(char(MSG_ReadByte()));
2883 snap.setCeilingTexture(MSG_ReadShort());
2884 snap.setCeilingSpecial(MSG_ReadShort());
2885 snap.setCeilingTag(MSG_ReadShort());
2886 snap.setCeilingOldDirection(char(MSG_ReadByte()));
2887 }
2888
2889 if (ceiling_mover == SEC_DOOR)
2890 {
2891 // Doors
2892 snap.setCeilingMoverType(SEC_DOOR);
2893 snap.setCeilingType(static_cast<DDoor::EVlDoor>(MSG_ReadByte()));
2894 snap.setCeilingHigh(MSG_ReadShort() << FRACBITS);
2895 snap.setCeilingSpeed(MSG_ReadShort() << FRACBITS);
2896 snap.setCeilingWait(MSG_ReadLong());
2897 snap.setCeilingCounter(MSG_ReadLong());
2898 snap.setCeilingStatus(MSG_ReadByte());
2899
2900 int LineIndex = MSG_ReadLong();
2901
2902 // If the moving sector's line is -1, it is likely a type 666 door
2903 if (!lines || LineIndex >= numlines || LineIndex < 0)
2904 snap.setCeilingLine(NULL);
2905 else
2906 snap.setCeilingLine(&lines[LineIndex]);
2907 }
2908
2909 if (ceiling_mover == SEC_ELEVATOR)
2910 {
2911 // Elevators
2912 snap.setCeilingMoverType(SEC_ELEVATOR);
2913 snap.setFloorMoverType(SEC_ELEVATOR);
2914 snap.setCeilingType(static_cast<DElevator::EElevator>(MSG_ReadByte()));
2915 snap.setFloorType(snap.getCeilingType());
2916 snap.setCeilingStatus(MSG_ReadByte());
2917 snap.setFloorStatus(snap.getCeilingStatus());
2918 snap.setCeilingDirection(char(MSG_ReadByte()));
2919 snap.setFloorDirection(snap.getCeilingDirection());
2920 snap.setFloorDestination(MSG_ReadShort() << FRACBITS);
2921 snap.setCeilingDestination(MSG_ReadShort() << FRACBITS);
2922 snap.setCeilingSpeed(MSG_ReadShort() << FRACBITS);
2923 snap.setFloorSpeed(snap.getCeilingSpeed());
2924 }
2925
2926 if (ceiling_mover == SEC_PILLAR)
2927 {
2928 // Pillars
2929 snap.setCeilingMoverType(SEC_PILLAR);
2930 snap.setFloorMoverType(SEC_PILLAR);
2931 snap.setCeilingType(static_cast<DPillar::EPillar>(MSG_ReadByte()));
2932 snap.setFloorType(snap.getCeilingType());
2933 snap.setCeilingStatus(MSG_ReadByte());
2934 snap.setFloorStatus(snap.getCeilingStatus());
2935 snap.setFloorSpeed(MSG_ReadShort() << FRACBITS);
2936 snap.setCeilingSpeed(MSG_ReadShort() << FRACBITS);
2937 snap.setFloorDestination(MSG_ReadShort() << FRACBITS);
2938 snap.setCeilingDestination(MSG_ReadShort() << FRACBITS);
2939 snap.setCeilingCrush(MSG_ReadBool());
2940 snap.setFloorCrush(snap.getCeilingCrush());
2941 }
2942
2943 if (!sectors || sectornum >= numsectors)
2944 return;
2945
2946 snap.setSector(§ors[sectornum]);
2947
2948 sector_snaps[sectornum].addSnapshot(snap);
2949 }
2950
2951
2952 //
2953 // CL_CheckMissedPacket
2954 //
CL_CheckMissedPacket(void)2955 void CL_CheckMissedPacket(void)
2956 {
2957 int sequence;
2958 short size;
2959
2960 sequence = MSG_ReadLong();
2961 size = MSG_ReadShort();
2962
2963 for (int n=0; n<256; n++)
2964 {
2965 // skip a duplicated packet
2966 if (packetseq[n] == sequence)
2967 {
2968 MSG_ReadChunk(size);
2969
2970 #ifdef _DEBUG
2971 Printf (PRINT_LOW, "warning: duplicate packet\n");
2972 #endif
2973 return;
2974 }
2975 }
2976 }
2977
2978 // Decompress the packet sequence
2979 // [Russell] - reason this was failing is because of huffman routines, so just
2980 // use minilzo for now (cuts a packet size down by roughly 45%), huffman is the
2981 // if 0'd sections
CL_Decompress(int sequence)2982 void CL_Decompress(int sequence)
2983 {
2984 if(!MSG_BytesLeft() || MSG_NextByte() != svc_compressed)
2985 return;
2986 else
2987 MSG_ReadByte();
2988
2989 byte method = MSG_ReadByte();
2990
2991 #if 0
2992 if(method & adaptive_mask)
2993 MSG_DecompressAdaptive(compressor.codec_for_received(method & adaptive_select_mask ? 1 : 0));
2994 else
2995 {
2996 // otherwise compressed packets can still contain codec updates
2997 compressor.codec_for_received(method & adaptive_select_mask ? 1 : 0);
2998 }
2999 #endif
3000
3001 if(method & minilzo_mask)
3002 MSG_DecompressMinilzo();
3003 #if 0
3004 if(method & adaptive_record_mask)
3005 compressor.ack_sent(net_message.ptr(), MSG_BytesLeft());
3006 #endif
3007 }
3008
3009 //
3010 // CL_ReadPacketHeader
3011 //
CL_ReadPacketHeader(void)3012 void CL_ReadPacketHeader(void)
3013 {
3014 unsigned int sequence = MSG_ReadLong();
3015
3016 MSG_WriteMarker(&net_buffer, clc_ack);
3017 MSG_WriteLong(&net_buffer, sequence);
3018
3019 CL_Decompress(sequence);
3020
3021 packetseq[packetnum] = sequence;
3022 packetnum++;
3023 }
3024
CL_GetServerSettings(void)3025 void CL_GetServerSettings(void)
3026 {
3027 cvar_t *var = NULL, *prev = NULL;
3028
3029 // TODO: REMOVE IN 0.7 - We don't need this loop anymore
3030 while (MSG_ReadByte() != 2)
3031 {
3032 std::string CvarName = MSG_ReadString();
3033 std::string CvarValue = MSG_ReadString();
3034
3035 var = cvar_t::FindCVar (CvarName.c_str(), &prev);
3036
3037 // GhostlyDeath <June 19, 2008> -- Read CVAR or dump it
3038 if (var)
3039 {
3040 if (var->flags() & CVAR_SERVERINFO)
3041 var->Set(CvarValue.c_str());
3042 }
3043 else
3044 {
3045 // [Russell] - create a new "temporary" cvar, CVAR_AUTO marks it
3046 // for cleanup on program termination
3047 // [AM] We have no way of telling of cvars are CVAR_NOENABLEDISABLE,
3048 // so let's set it on all cvars.
3049 var = new cvar_t(CvarName.c_str(), NULL, "", CVARTYPE_NONE,
3050 CVAR_SERVERINFO | CVAR_AUTO | CVAR_UNSETTABLE |
3051 CVAR_NOENABLEDISABLE);
3052 var->Set(CvarValue.c_str());
3053 }
3054 }
3055
3056 // Nes - update the skies in case sv_freelook is changed.
3057 R_InitSkyMap ();
3058
3059 // [AM] - Adhere to sv_allowwidescreen setting.
3060 ST_AdjustStatusBarScale(st_scale != 0);
3061 setsizeneeded = true;
3062 }
3063
3064 //
3065 // CL_FinishedFullUpdate
3066 //
3067 // Takes care of any business that needs to be done once the client has a full
3068 // view of the game world.
3069 //
CL_FinishedFullUpdate()3070 void CL_FinishedFullUpdate()
3071 {
3072 recv_full_update = true;
3073
3074 // Write the first map snapshot to a netdemo
3075 if (netdemo.isRecording())
3076 netdemo.writeMapChange();
3077 }
3078
3079 //
3080 // CL_SetMobjState
3081 //
CL_SetMobjState()3082 void CL_SetMobjState()
3083 {
3084 AActor *mo = P_FindThingById (MSG_ReadShort() );
3085 SWORD s = MSG_ReadShort();
3086
3087 if (!mo || s >= NUMSTATES)
3088 return;
3089
3090 P_SetMobjState (mo, (statenum_t)s);
3091 }
3092
3093 // ---------------------------------------------------------------------------------------------------------
3094 // CL_ForceSetTeam
3095 // Allows server to force set a players team setting
3096 // ---------------------------------------------------------------------------------------------------------
3097
CL_ForceSetTeam(void)3098 void CL_ForceSetTeam (void)
3099 {
3100 unsigned int t = MSG_ReadShort ();
3101
3102 if(t < NUMTEAMS || t == TEAM_NONE)
3103 consoleplayer().userinfo.team = (team_t)t;
3104
3105 // Setting the cl_team will send a playerinfo packet back to the server.
3106 // Unfortunately, this is unavoidable until we rework the team system.
3107 switch (consoleplayer().userinfo.team) {
3108 case TEAM_BLUE:
3109 cl_team.Set("BLUE");
3110 break;
3111 case TEAM_RED:
3112 cl_team.Set("RED");
3113 break;
3114 default:
3115 cl_team.Set("NONE");
3116 break;
3117 }
3118 }
3119
3120 //
3121 // CL_Actor_Movedir
3122 //
CL_Actor_Movedir()3123 void CL_Actor_Movedir()
3124 {
3125 AActor *actor = P_FindThingById (MSG_ReadShort());
3126 BYTE movedir = MSG_ReadByte();
3127 SDWORD movecount = MSG_ReadLong();
3128
3129 if (!actor || movedir >= 8)
3130 return;
3131
3132 actor->movedir = movedir;
3133 actor->movecount = movecount;
3134 }
3135
3136 //
3137 // CL_Actor_Target
3138 //
CL_Actor_Target()3139 void CL_Actor_Target()
3140 {
3141 AActor *actor = P_FindThingById (MSG_ReadShort());
3142 AActor *target = P_FindThingById (MSG_ReadShort());
3143
3144 if (!actor || !target)
3145 return;
3146
3147 actor->target = target->ptr();
3148 }
3149
3150 //
3151 // CL_Actor_Tracer
3152 //
CL_Actor_Tracer()3153 void CL_Actor_Tracer()
3154 {
3155 AActor *actor = P_FindThingById (MSG_ReadShort());
3156 AActor *tracer = P_FindThingById (MSG_ReadShort());
3157
3158 if (!actor || !tracer)
3159 return;
3160
3161 actor->tracer = tracer->ptr();
3162 }
3163
3164 //
3165 // CL_MobjTranslation
3166 //
CL_MobjTranslation()3167 void CL_MobjTranslation()
3168 {
3169 AActor *mo = P_FindThingById(MSG_ReadShort());
3170 byte table = MSG_ReadByte();
3171
3172 if (!mo)
3173 return;
3174
3175 if (table <= MAXPLAYERS)
3176 mo->translation = translationref_t(translationtables + 256 * table, table);
3177 else
3178 mo->translation = translationref_t(translationtables + 256 * table);
3179 }
3180
3181
3182 //
3183 // CL_Switch
3184 // denis - switch state and timing
3185 //
CL_Switch()3186 void CL_Switch()
3187 {
3188 unsigned l = MSG_ReadLong();
3189 byte wastoggled = MSG_ReadByte();
3190 byte state = MSG_ReadByte();
3191 unsigned time = MSG_ReadLong();
3192
3193 if (!lines || l >= (unsigned)numlines || state >= 3)
3194 return;
3195
3196 if(!P_SetButtonInfo(&lines[l], state, time)) // denis - fixme - security
3197 if(wastoggled)
3198 P_ChangeSwitchTexture(&lines[l], lines[l].flags & ML_REPEAT_SPECIAL); // denis - fixme - security
3199
3200 if(wastoggled && !(lines[l].flags & ML_REPEAT_SPECIAL)) // non repeat special
3201 lines[l].special = 0;
3202 }
3203
CL_ActivateLine(void)3204 void CL_ActivateLine(void)
3205 {
3206 unsigned l = MSG_ReadLong();
3207 AActor *mo = P_FindThingById(MSG_ReadShort());
3208 byte side = MSG_ReadByte();
3209 byte activationType = MSG_ReadByte();
3210
3211 if (!lines || l >= (unsigned)numlines)
3212 return;
3213
3214 // [SL] 2012-03-07 - If this is a player teleporting, add this player to
3215 // the set of recently teleported players. This is used to flush past
3216 // positions since they cannot be used for interpolation.
3217 if ((mo && mo->player) &&
3218 (lines[l].special == Teleport || lines[l].special == Teleport_NoFog ||
3219 lines[l].special == Teleport_Line))
3220 {
3221 teleported_players.insert(mo->player->id);
3222
3223 // [SL] 2012-03-21 - Server takes care of moving players that teleport.
3224 // Don't allow client to process it since it screws up interpolation.
3225 return;
3226 }
3227
3228 // [SL] 2012-04-25 - Clients will receive updates for sectors so they do not
3229 // need to create moving sectors on their own in response to svc_activateline
3230 if (P_LineSpecialMovesSector(&lines[l]))
3231 return;
3232
3233 switch (activationType)
3234 {
3235 case 0:
3236 P_CrossSpecialLine(l, side, mo, true);
3237 break;
3238 case 1:
3239 P_UseSpecialLine(mo, &lines[l], side, true);
3240 break;
3241 case 2:
3242 P_ShootSpecialLine(mo, &lines[l], true);
3243 break;
3244 case 3:
3245 P_PushSpecialLine(mo, &lines[l], side, true);
3246 break;
3247 }
3248 }
3249
CL_ConsolePlayer(void)3250 void CL_ConsolePlayer(void)
3251 {
3252 displayplayer_id = consoleplayer_id = MSG_ReadByte();
3253 digest = MSG_ReadString();
3254 }
3255
3256 //
3257 // CL_LoadMap
3258 //
3259 // Read wad & deh filenames and map name from the server and loads
3260 // the appropriate wads & map.
3261 //
CL_LoadMap(void)3262 void CL_LoadMap(void)
3263 {
3264 bool splitnetdemo = (netdemo.isRecording() && cl_splitnetdemos) || forcenetdemosplit;
3265 forcenetdemosplit = false;
3266
3267 if (splitnetdemo)
3268 netdemo.stopRecording();
3269
3270 std::vector<std::string> newwadfiles, newwadhashes;
3271 std::vector<std::string> newpatchfiles, newpatchhashes;
3272
3273 int wadcount = (byte)MSG_ReadByte();
3274 while (wadcount--)
3275 {
3276 newwadfiles.push_back(MSG_ReadString());
3277 newwadhashes.push_back(MSG_ReadString());
3278 }
3279
3280 int patchcount = (byte)MSG_ReadByte();
3281 while (patchcount--)
3282 {
3283 newpatchfiles.push_back(MSG_ReadString());
3284 newpatchhashes.push_back(MSG_ReadString());
3285 }
3286
3287 const char *mapname = MSG_ReadString ();
3288
3289 if (gamestate == GS_DOWNLOAD)
3290 {
3291 CL_Reconnect();
3292 return;
3293 }
3294
3295 // Load the specified WAD and DEH files and change the level.
3296 // if any WADs are missing, reconnect to begin downloading.
3297 G_LoadWad(newwadfiles, newpatchfiles, newwadhashes, newpatchhashes);
3298
3299 if (!missingfiles.empty())
3300 {
3301 missing_file = missingfiles[0];
3302 missing_hash = missinghashes[0];
3303
3304 CL_Reconnect();
3305 return;
3306 }
3307
3308 // [SL] 2012-12-02 - Force the music to stop when the new map uses
3309 // the same music lump name that is currently playing. Otherwise,
3310 // the music from the old wad continues to play...
3311 S_StopMusic();
3312
3313 G_InitNew (mapname);
3314
3315 movingsectors.clear();
3316 teleported_players.clear();
3317
3318 CL_ClearSectorSnapshots();
3319 for (Players::iterator it = players.begin();it != players.end();++it)
3320 it->snapshots.clearSnapshots();
3321
3322 // reset the world_index (force it to sync)
3323 CL_ResyncWorldIndex();
3324 last_svgametic = 0;
3325
3326 CTF_CheckFlags(consoleplayer());
3327
3328 gameaction = ga_nothing;
3329
3330 // Autorecord netdemo or continue recording in a new file
3331 if (!(netdemo.isPlaying() || netdemo.isRecording() || netdemo.isPaused()))
3332 {
3333 std::string filename;
3334
3335 size_t param = Args.CheckParm("-netrecord");
3336 if (param && Args.GetArg(param + 1))
3337 filename = Args.GetArg(param + 1);
3338
3339 if (splitnetdemo || cl_autorecord || param)
3340 {
3341 if (filename.empty())
3342 filename = CL_GenerateNetDemoFileName();
3343 else
3344 filename = CL_GenerateNetDemoFileName(filename);
3345
3346 // NOTE(jsd): Presumably a warning is already printed.
3347 if (filename.empty())
3348 {
3349 netdemo.stopRecording();
3350 return;
3351 }
3352
3353 netdemo.startRecording(filename);
3354 }
3355 }
3356
3357 // write the map index to the netdemo
3358 if (netdemo.isRecording() && recv_full_update)
3359 netdemo.writeMapChange();
3360 }
3361
CL_ResetMap()3362 void CL_ResetMap()
3363 {
3364 // Destroy every actor with a netid that isn't a player. We're going to
3365 // get the contents of the map with a full update later on anyway.
3366 AActor* mo;
3367 TThinkerIterator<AActor> iterator;
3368 while ((mo = iterator.Next()))
3369 {
3370 if (mo->netid && mo->type != MT_PLAYER)
3371 {
3372 mo->Destroy();
3373 }
3374 }
3375
3376 // write the map index to the netdemo
3377 if (netdemo.isRecording() && recv_full_update)
3378 netdemo.writeMapChange();
3379 }
3380
CL_EndGame()3381 void CL_EndGame()
3382 {
3383 Host_EndGame ("\nServer disconnected\n");
3384 }
3385
CL_FullGame()3386 void CL_FullGame()
3387 {
3388 Host_EndGame ("Server is full\n");
3389 }
3390
CL_ExitLevel()3391 void CL_ExitLevel()
3392 {
3393 if(gamestate != GS_DOWNLOAD) {
3394 gameaction = ga_completed;
3395
3396 if (netdemo.isRecording())
3397 netdemo.writeIntermission();
3398 }
3399 }
3400
CL_Noop()3401 void CL_Noop()
3402 {
3403 // Nothing to see here. Move along.
3404 }
3405
CL_Clear()3406 void CL_Clear()
3407 {
3408 size_t left = MSG_BytesLeft();
3409 MSG_ReadChunk(left);
3410 }
3411
EXTERN_CVAR(st_scale)3412 EXTERN_CVAR (st_scale)
3413
3414 void CL_Spectate()
3415 {
3416 player_t &player = CL_FindPlayer(MSG_ReadByte());
3417
3418 bool wasalive = !player.spectator && player.mo && player.mo->health > 0;
3419 player.spectator = ((MSG_ReadByte()) != 0);
3420
3421 if (player.spectator && wasalive)
3422 P_DisconnectEffect(player.mo);
3423
3424 if (&player == &consoleplayer()) {
3425 st_scale.Callback (); // refresh status bar size
3426 if (player.spectator) {
3427 player.playerstate = PST_LIVE; // resurrect dead spectators
3428 // GhostlyDeath -- Sometimes if the player spectates while he is falling down he squats
3429 player.deltaviewheight = 1000 << FRACBITS;
3430 } else {
3431 displayplayer_id = consoleplayer_id; // get out of spynext
3432 player.cheats &= ~CF_FLY; // remove flying ability
3433 }
3434
3435 CL_RebuildAllPlayerTranslations();
3436 }
3437 else
3438 {
3439 R_BuildPlayerTranslation(player.id, CL_GetPlayerColor(&player));
3440 }
3441
3442 // GhostlyDeath -- If the player matches our display player...
3443 CL_CheckDisplayPlayer();
3444 }
3445
CL_ReadyState()3446 void CL_ReadyState() {
3447 player_t &player = CL_FindPlayer(MSG_ReadByte());
3448 player.ready = MSG_ReadBool();
3449 }
3450
3451 // Set local warmup state.
CL_WarmupState()3452 void CL_WarmupState()
3453 {
3454 warmup.set_client_status(static_cast<Warmup::status_t>(MSG_ReadByte()));
3455 if (warmup.get_status() == Warmup::COUNTDOWN ||
3456 warmup.get_status() == Warmup::FORCE_COUNTDOWN)
3457 {
3458 // Read an extra countdown number off the wire
3459 short count = MSG_ReadShort();
3460 std::ostringstream buffer;
3461 buffer << "Match begins in " << count << "...";
3462 C_GMidPrint(buffer.str().c_str(), CR_GREEN, 0);
3463 }
3464 else
3465 {
3466 // Clear the midprint in other cases.
3467 C_GMidPrint("", CR_GREY, 0);
3468 }
3469 }
3470
3471 // client source (once)
3472 typedef void (*client_callback)();
3473 typedef std::map<svc_t, client_callback> cmdmap;
3474 cmdmap cmds;
3475
3476 //
3477 // CL_AllowPackets
3478 //
CL_InitCommands(void)3479 void CL_InitCommands(void)
3480 {
3481 cmds[svc_abort] = &CL_EndGame;
3482 cmds[svc_loadmap] = &CL_LoadMap;
3483 cmds[svc_resetmap] = &CL_ResetMap;
3484 cmds[svc_playerinfo] = &CL_PlayerInfo;
3485 cmds[svc_consoleplayer] = &CL_ConsolePlayer;
3486 cmds[svc_updatefrags] = &CL_UpdateFrags;
3487 cmds[svc_moveplayer] = &CL_UpdatePlayer;
3488 cmds[svc_updatelocalplayer] = &CL_UpdateLocalPlayer;
3489 cmds[svc_userinfo] = &CL_SetupUserInfo;
3490 cmds[svc_teampoints] = &CL_TeamPoints;
3491 cmds[svc_playerstate] = &CL_UpdatePlayerState;
3492
3493 cmds[svc_updateping] = &CL_UpdatePing;
3494 cmds[svc_spawnmobj] = &CL_SpawnMobj;
3495 cmds[svc_mobjspeedangle] = &CL_SetMobjSpeedAndAngle;
3496 cmds[svc_mobjinfo] = &CL_UpdateMobjInfo;
3497 cmds[svc_explodemissile] = &CL_ExplodeMissile;
3498 cmds[svc_removemobj] = &CL_RemoveMobj;
3499
3500 cmds[svc_killmobj] = &CL_KillMobj;
3501 cmds[svc_movemobj] = &CL_MoveMobj;
3502 cmds[svc_damagemobj] = &CL_DamageMobj;
3503 cmds[svc_corpse] = &CL_Corpse;
3504 cmds[svc_spawnplayer] = &CL_SpawnPlayer;
3505 // cmds[svc_spawnhiddenplayer] = &CL_SpawnHiddenPlayer;
3506 cmds[svc_damageplayer] = &CL_DamagePlayer;
3507 cmds[svc_firepistol] = &CL_FirePistol;
3508 cmds[svc_fireweapon] = &CL_FireWeapon;
3509
3510 cmds[svc_fireshotgun] = &CL_FireShotgun;
3511 cmds[svc_firessg] = &CL_FireSSG;
3512 cmds[svc_firechaingun] = &CL_FireChainGun;
3513 cmds[svc_changeweapon] = &CL_ChangeWeapon;
3514 cmds[svc_railtrail] = &CL_RailTrail;
3515 cmds[svc_connectclient] = &CL_ConnectClient;
3516 cmds[svc_disconnectclient] = &CL_DisconnectClient;
3517 cmds[svc_activateline] = &CL_ActivateLine;
3518 cmds[svc_sector] = &CL_UpdateSector;
3519 cmds[svc_movingsector] = &CL_UpdateMovingSector;
3520 cmds[svc_switch] = &CL_Switch;
3521 cmds[svc_print] = &CL_Print;
3522 cmds[svc_midprint] = &CL_MidPrint;
3523 cmds[svc_say] = &CL_Say;
3524 cmds[svc_pingrequest] = &CL_SendPingReply;
3525 cmds[svc_svgametic] = &CL_SaveSvGametic;
3526 cmds[svc_mobjtranslation] = &CL_MobjTranslation;
3527 cmds[svc_timeleft] = &CL_UpdateTimeLeft;
3528 cmds[svc_inttimeleft] = &CL_UpdateIntTimeLeft;
3529
3530 cmds[svc_startsound] = &CL_Sound;
3531 cmds[svc_soundorigin] = &CL_SoundOrigin;
3532 cmds[svc_mobjstate] = &CL_SetMobjState;
3533 cmds[svc_actor_movedir] = &CL_Actor_Movedir;
3534 cmds[svc_actor_target] = &CL_Actor_Target;
3535 cmds[svc_actor_tracer] = &CL_Actor_Tracer;
3536 cmds[svc_missedpacket] = &CL_CheckMissedPacket;
3537 cmds[svc_forceteam] = &CL_ForceSetTeam;
3538
3539 cmds[svc_ctfevent] = &CL_CTFEvent;
3540 cmds[svc_serversettings] = &CL_GetServerSettings;
3541 cmds[svc_disconnect] = &CL_EndGame;
3542 cmds[svc_full] = &CL_FullGame;
3543 cmds[svc_reconnect] = &CL_Reconnect;
3544 cmds[svc_exitlevel] = &CL_ExitLevel;
3545
3546 cmds[svc_wadinfo] = &CL_DownloadStart;
3547 cmds[svc_wadchunk] = &CL_Download;
3548
3549 cmds[svc_challenge] = &CL_Clear;
3550 cmds[svc_launcher_challenge]= &CL_Clear;
3551
3552 cmds[svc_spectate] = &CL_Spectate;
3553 cmds[svc_readystate] = &CL_ReadyState;
3554 cmds[svc_warmupstate] = &CL_WarmupState;
3555
3556 cmds[svc_touchspecial] = &CL_TouchSpecialThing;
3557
3558 cmds[svc_netdemocap] = &CL_LocalDemoTic;
3559 cmds[svc_netdemostop] = &CL_NetDemoStop;
3560 cmds[svc_netdemoloadsnap] = &CL_NetDemoLoadSnap;
3561 cmds[svc_fullupdatedone] = &CL_FinishedFullUpdate;
3562
3563 cmds[svc_vote_update] = &CL_VoteUpdate;
3564 cmds[svc_maplist] = &CL_Maplist;
3565 cmds[svc_maplist_update] = &CL_MaplistUpdate;
3566 cmds[svc_maplist_index] = &CL_MaplistIndex;
3567 }
3568
3569 //
3570 // CL_ParseCommands
3571 //
CL_ParseCommands(void)3572 void CL_ParseCommands(void)
3573 {
3574 std::vector<svc_t> history;
3575 svc_t cmd = svc_abort;
3576
3577 static bool once = true;
3578 if(once)CL_InitCommands();
3579 once = false;
3580
3581 while(connected)
3582 {
3583 cmd = (svc_t)MSG_ReadByte();
3584 history.push_back(cmd);
3585
3586 if(cmd == (svc_t)-1)
3587 break;
3588
3589 cmdmap::iterator i = cmds.find(cmd);
3590 if(i == cmds.end())
3591 {
3592 CL_QuitNetGame();
3593 Printf(PRINT_HIGH, "CL_ParseCommands: Unknown server message %d following: \n", (int)cmd);
3594
3595 for(size_t j = 0; j < history.size(); j++)
3596 Printf(PRINT_HIGH, "CL_ParseCommands: message #%d [%d %s]\n", j, history[j], svc_info[history[j]].getName());
3597 Printf(PRINT_HIGH, "\n");
3598 break;
3599 }
3600
3601 i->second();
3602
3603 if (net_message.overflowed)
3604 {
3605 CL_QuitNetGame();
3606 Printf(PRINT_HIGH, "CL_ParseCommands: Bad server message\n");
3607 Printf(PRINT_HIGH, "CL_ParseCommands: %d(%s) overflowed\n",
3608 (int)cmd,
3609 svc_info[cmd].getName());
3610 Printf(PRINT_HIGH, "CL_ParseCommands: It was command number %d in the packet\n",
3611 (int)history.back());
3612 for(size_t j = 0; j < history.size(); j++)
3613 Printf(PRINT_HIGH, "CL_ParseCommands: message #%d [%d %s]\n", j, history[j], svc_info[history[j]].getName());
3614 }
3615
3616 }
3617 }
3618
3619
CL_SaveCmd(void)3620 void CL_SaveCmd(void)
3621 {
3622 NetCommand *netcmd = &localcmds[gametic % MAXSAVETICS];
3623 netcmd->fromPlayer(&consoleplayer());
3624 netcmd->setTic(gametic);
3625 netcmd->setWorldIndex(world_index);
3626 }
3627
3628 extern int outrate;
3629
3630 //
3631 // CL_SendCmd
3632 //
CL_SendCmd(void)3633 void CL_SendCmd(void)
3634 {
3635 player_t *p = &consoleplayer();
3636
3637 if (netdemo.isPlaying()) // we're not really connected to a server
3638 return;
3639
3640 if (!p->mo || gametic < 1 )
3641 return;
3642
3643 // GhostlyDeath -- If we are spectating, tell the server of our new position
3644 if (p->spectator)
3645 {
3646 MSG_WriteMarker(&net_buffer, clc_spectate);
3647 MSG_WriteByte(&net_buffer, 5);
3648 MSG_WriteLong(&net_buffer, p->mo->x);
3649 MSG_WriteLong(&net_buffer, p->mo->y);
3650 MSG_WriteLong(&net_buffer, p->mo->z);
3651 }
3652
3653 MSG_WriteMarker(&net_buffer, clc_move);
3654
3655 // Write current client-tic. Server later sends this back to client
3656 // when sending svc_updatelocalplayer so the client knows which ticcmds
3657 // need to be used for client's positional prediction.
3658 MSG_WriteLong(&net_buffer, gametic);
3659
3660 NetCommand *netcmd;
3661 for (int i = 9; i >= 0; i--)
3662 {
3663 netcmd = &localcmds[(gametic - i) % MAXSAVETICS];
3664 netcmd->write(&net_buffer);
3665 }
3666
3667 NET_SendPacket(net_buffer, serveraddr);
3668 outrate += net_buffer.size();
3669 SZ_Clear(&net_buffer);
3670 }
3671
3672 //
3673 // CL_PlayerTimes
3674 //
CL_PlayerTimes()3675 void CL_PlayerTimes()
3676 {
3677 for (Players::iterator it = players.begin();it != players.end();++it)
3678 {
3679 if (it->ingame())
3680 it->GameTime++;
3681 }
3682 }
3683
PickupMessage(AActor * toucher,const char * message)3684 void PickupMessage (AActor *toucher, const char *message)
3685 {
3686 // Some maps have multiple items stacked on top of each other.
3687 // It looks odd to display pickup messages for all of them.
3688 static int lastmessagetic;
3689 static const char *lastmessage = NULL;
3690
3691 if (toucher == consoleplayer().camera
3692 && (lastmessagetic != gametic || lastmessage != message))
3693 {
3694 lastmessagetic = gametic;
3695 lastmessage = message;
3696 Printf (PRINT_LOW, "%s\n", message);
3697 }
3698 }
3699
3700 //
3701 // void WeaponPickupMessage (weapontype_t &Weapon)
3702 //
3703 // This is used for displaying weaponstay messages, it is inevitably a hack
3704 // because weaponstay is a hack
WeaponPickupMessage(AActor * toucher,weapontype_t & Weapon)3705 void WeaponPickupMessage (AActor *toucher, weapontype_t &Weapon)
3706 {
3707 switch (Weapon)
3708 {
3709 case wp_shotgun:
3710 {
3711 PickupMessage(toucher, GStrings(GOTSHOTGUN));
3712 }
3713 break;
3714
3715 case wp_chaingun:
3716 {
3717 PickupMessage(toucher, GStrings(GOTCHAINGUN));
3718 }
3719 break;
3720
3721 case wp_missile:
3722 {
3723 PickupMessage(toucher, GStrings(GOTLAUNCHER));
3724 }
3725 break;
3726
3727 case wp_plasma:
3728 {
3729 PickupMessage(toucher, GStrings(GOTPLASMA));
3730 }
3731 break;
3732
3733 case wp_bfg:
3734 {
3735 PickupMessage(toucher, GStrings(GOTBFG9000));
3736 }
3737 break;
3738
3739 case wp_chainsaw:
3740 {
3741 PickupMessage(toucher, GStrings(GOTCHAINSAW));
3742 }
3743 break;
3744
3745 case wp_supershotgun:
3746 {
3747 PickupMessage(toucher, GStrings(GOTSHOTGUN2));
3748 }
3749 break;
3750
3751 default:
3752 break;
3753 }
3754 }
3755
CL_LocalDemoTic()3756 void CL_LocalDemoTic()
3757 {
3758 player_t* clientPlayer = &consoleplayer();
3759 fixed_t x, y, z;
3760 fixed_t momx, momy, momz;
3761 fixed_t pitch, viewheight, deltaviewheight;
3762 angle_t angle;
3763 int jumpTics, reactiontime;
3764 byte waterlevel;
3765
3766 clientPlayer->cmd.clear();
3767 clientPlayer->cmd.buttons = MSG_ReadByte();
3768 clientPlayer->cmd.impulse = MSG_ReadByte();
3769 clientPlayer->cmd.yaw = MSG_ReadShort();
3770 clientPlayer->cmd.forwardmove = MSG_ReadShort();
3771 clientPlayer->cmd.sidemove = MSG_ReadShort();
3772 clientPlayer->cmd.upmove = MSG_ReadShort();
3773 clientPlayer->cmd.pitch = MSG_ReadShort();
3774
3775 waterlevel = MSG_ReadByte();
3776 x = MSG_ReadLong();
3777 y = MSG_ReadLong();
3778 z = MSG_ReadLong();
3779 momx = MSG_ReadLong();
3780 momy = MSG_ReadLong();
3781 momz = MSG_ReadLong();
3782 angle = MSG_ReadLong();
3783 pitch = MSG_ReadLong();
3784 viewheight = MSG_ReadLong();
3785 deltaviewheight = MSG_ReadLong();
3786 jumpTics = MSG_ReadLong();
3787 reactiontime = MSG_ReadLong();
3788 clientPlayer->readyweapon = static_cast<weapontype_t>(MSG_ReadByte());
3789 clientPlayer->pendingweapon = static_cast<weapontype_t>(MSG_ReadByte());
3790
3791 if(clientPlayer->mo)
3792 {
3793 clientPlayer->mo->x = x;
3794 clientPlayer->mo->y = y;
3795 clientPlayer->mo->z = z;
3796 clientPlayer->mo->momx = momx;
3797 clientPlayer->mo->momy = momy;
3798 clientPlayer->mo->momz = momz;
3799 clientPlayer->mo->angle = angle;
3800 clientPlayer->mo->pitch = pitch;
3801 clientPlayer->viewheight = viewheight;
3802 clientPlayer->deltaviewheight = deltaviewheight;
3803 clientPlayer->jumpTics = jumpTics;
3804 clientPlayer->mo->reactiontime = reactiontime;
3805 clientPlayer->mo->waterlevel = waterlevel;
3806 }
3807
3808 }
3809
CL_RemoveCompletedMovingSectors()3810 void CL_RemoveCompletedMovingSectors()
3811 {
3812 std::map<unsigned short, SectorSnapshotManager>::iterator itr;
3813 itr = sector_snaps.begin();
3814
3815 while (itr != sector_snaps.end())
3816 {
3817 SectorSnapshotManager *mgr = &(itr->second);
3818 int time = mgr->getMostRecentTime();
3819
3820 // are all the snapshots in the container invalid or too old?
3821 if (world_index - time > NUM_SNAPSHOTS || mgr->empty())
3822 sector_snaps.erase(itr++);
3823 else
3824 ++itr;
3825 }
3826 }
3827
CVAR_FUNC_IMPL(cl_interp)3828 CVAR_FUNC_IMPL (cl_interp)
3829 {
3830 // Resync the world index since the sync offset has changed
3831 CL_ResyncWorldIndex();
3832 netgraph.setInterpolation(var);
3833 }
3834
3835 //
3836 // CL_SimulateSectors
3837 //
3838 // Iterates through the list of moving sector snapshot containers
3839 // and loads the world_index snapshot for each sector that is not
3840 // currently being predicted. Predicted sectors are handled elsewhere.
3841 //
CL_SimulateSectors()3842 void CL_SimulateSectors()
3843 {
3844 // Get rid of snapshots for sectors that are done moving
3845 CL_RemoveCompletedMovingSectors();
3846
3847 // Move sectors
3848 std::map<unsigned short, SectorSnapshotManager>::iterator itr;
3849 for (itr = sector_snaps.begin(); itr != sector_snaps.end(); ++itr)
3850 {
3851 unsigned short sectornum = itr->first;
3852 if (sectornum >= numsectors)
3853 continue;
3854
3855 sector_t *sector = §ors[sectornum];
3856
3857 // will this sector be handled when predicting sectors?
3858 if (cl_predictsectors && CL_SectorIsPredicting(sector))
3859 continue;
3860
3861 // Fetch the snapshot for this world_index and run the sector's
3862 // thinkers to play any sector sounds
3863 SectorSnapshot snap = itr->second.getSnapshot(world_index);
3864 if (snap.isValid())
3865 {
3866 snap.toSector(sector);
3867
3868 if (sector->ceilingdata)
3869 sector->ceilingdata->RunThink();
3870 if (sector->floordata && sector->ceilingdata != sector->floordata)
3871 sector->floordata->RunThink();
3872
3873 snap.toSector(sector);
3874 }
3875 }
3876 }
3877
3878 //
3879 // CL_SimulatePlayers()
3880 //
3881 // Iterates through the players vector and loads the world_index snapshot
3882 // for all players except consoleplayer, as this is handled by the prediction
3883 // functions.
3884 //
CL_SimulatePlayers()3885 void CL_SimulatePlayers()
3886 {
3887 for (Players::iterator it = players.begin();it != players.end();++it)
3888 {
3889 player_t *player = &*it;
3890 if (!player || !player->mo || player->spectator)
3891 continue;
3892
3893 // Consoleplayer is handled in CL_PredictWorld
3894 if (player->id == consoleplayer_id)
3895 continue;
3896
3897 PlayerSnapshot snap = player->snapshots.getSnapshot(world_index);
3898 if (snap.isValid())
3899 {
3900 // Examine the old position. If it doesn't match the snapshot for the
3901 // previous world_index, then old position was probably extrapolated
3902 // and should be smoothly moved towards the corrected position instead
3903 // of snapping to it.
3904
3905 if (snap.isContinuous())
3906 {
3907 // [SL] Save the position prior to the new update so it can be
3908 // used for rendering interpolation
3909 player->mo->prevx = player->mo->x;
3910 player->mo->prevy = player->mo->y;
3911 player->mo->prevz = player->mo->z;
3912 player->mo->prevangle = player->mo->angle;
3913 player->mo->prevpitch = player->mo->pitch;
3914
3915 PlayerSnapshot prevsnap = player->snapshots.getSnapshot(world_index - 1);
3916
3917 v3fixed_t offset;
3918 M_SetVec3Fixed(&offset, prevsnap.getX() - player->mo->x,
3919 prevsnap.getY() - player->mo->y,
3920 prevsnap.getZ() - player->mo->z);
3921
3922 fixed_t dist = M_LengthVec3Fixed(&offset);
3923 if (dist > 2 * FRACUNIT)
3924 {
3925 #ifdef _SNAPSHOT_DEBUG_
3926 Printf(PRINT_HIGH, "Snapshot %i, Correcting extrapolation error of %i\n",
3927 world_index, dist >> FRACBITS);
3928 #endif // _SNAPSHOT_DEBUG_
3929
3930 static const fixed_t correction_amount = FRACUNIT * 0.80f;
3931 M_ScaleVec3Fixed(&offset, &offset, correction_amount);
3932
3933 // Apply a smoothing offset to the current snapshot
3934 snap.setX(snap.getX() - offset.x);
3935 snap.setY(snap.getY() - offset.y);
3936 snap.setZ(snap.getZ() - offset.z);
3937 }
3938 }
3939
3940 int oldframe = player->mo->frame;
3941 snap.toPlayer(player);
3942
3943 if (player->playerstate != PST_LIVE)
3944 player->mo->frame = oldframe;
3945
3946 if (!snap.isContinuous())
3947 {
3948 // [SL] Save the position after to the new update so this position
3949 // won't be interpolated.
3950 player->mo->prevx = player->mo->x;
3951 player->mo->prevy = player->mo->y;
3952 player->mo->prevz = player->mo->z;
3953 player->mo->prevangle = player->mo->angle;
3954 player->mo->prevpitch = player->mo->pitch;
3955 }
3956 }
3957 }
3958 }
3959
3960
3961 //
3962 // CL_SimulateWorld
3963 //
3964 // Maintains synchronization with the server by manipulating world_index.
3965 // Loads snapshots for all moving sectors and players for the server gametic
3966 // denoted by world_index.
3967 //
CL_SimulateWorld()3968 void CL_SimulateWorld()
3969 {
3970 if (gamestate != GS_LEVEL || netdemo.isPaused())
3971 return;
3972
3973 // if the world_index falls outside this range, resync it
3974 static const int MAX_BEHIND = 16;
3975 static const int MAX_AHEAD = 16;
3976
3977 int lower_sync_limit = CL_CalculateWorldIndexSync() - MAX_BEHIND;
3978 int upper_sync_limit = CL_CalculateWorldIndexSync() + MAX_AHEAD;
3979
3980 // Was the displayplayer just teleported?
3981 bool continuous = displayplayer().snapshots.getSnapshot(world_index).isContinuous();
3982
3983 // Reset the synchronization with the server if needed
3984 if (world_index <= 0 || !continuous ||
3985 world_index > upper_sync_limit || world_index < lower_sync_limit)
3986 {
3987 #ifdef _WORLD_INDEX_DEBUG_
3988 std::string reason;
3989 if (!continuous)
3990 reason = "discontinuous";
3991 else if (world_index > upper_sync_limit)
3992 reason = "too far ahead of server";
3993 else if (world_index < lower_sync_limit)
3994 reason = "too far behind server";
3995 else
3996 reason = "invalid world_index";
3997
3998 Printf(PRINT_HIGH, "Gametic %i, world_index %i, Resynching world index (%s).\n",
3999 gametic, world_index, reason.c_str());
4000 #endif // _WORLD_INDEX_DEBUG_
4001
4002 CL_ResyncWorldIndex();
4003 }
4004
4005 // Not using interpolation? Use the last update always
4006 if (!cl_interp)
4007 world_index = last_svgametic;
4008
4009 #ifdef _WORLD_INDEX_DEBUG_
4010 Printf(PRINT_HIGH, "Gametic %i, simulating world_index %i\n",
4011 gametic, world_index);
4012 #endif // _WORLD_INDEX_DEBUG_
4013
4014 // [SL] 2012-03-29 - Add sync information to the netgraph
4015 netgraph.setWorldIndexSync(world_index - (last_svgametic - cl_interp));
4016
4017 CL_SimulateSectors();
4018 CL_SimulatePlayers();
4019
4020 // [SL] 2012-03-17 - Try to maintain sync with the server by gradually
4021 // slowing down or speeding up world_index
4022 int drift_correction = CL_CalculateWorldIndexDriftCorrection();
4023
4024 #ifdef _WORLD_INDEX_DEBUG_
4025 if (drift_correction != 0)
4026 Printf(PRINT_HIGH, "Gametic %i, increasing world index by %i.\n",
4027 gametic, drift_correction);
4028 #endif // _WORLD_INDEX_DEBUG_
4029
4030 world_index = world_index + 1 + drift_correction;
4031 }
4032
OnChangedSwitchTexture(line_t * line,int useAgain)4033 void OnChangedSwitchTexture (line_t *line, int useAgain) {}
OnActivatedLine(line_t * line,AActor * mo,int side,int activationType)4034 void OnActivatedLine (line_t *line, AActor *mo, int side, int activationType) {}
4035
4036 VERSION_CONTROL (cl_main_cpp, "$Id: cl_main.cpp 4688 2014-03-25 16:55:35Z dr_sean $")
4037