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 = &sectors[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(&sectors[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 = &sectors[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