1 //**************************************************************************
2 //**
3 //**	##   ##    ##    ##   ##   ####     ####   ###     ###
4 //**	##   ##  ##  ##  ##   ##  ##  ##   ##  ##  ####   ####
5 //**	 ## ##  ##    ##  ## ##  ##    ## ##    ## ## ## ## ##
6 //**	 ## ##  ########  ## ##  ##    ## ##    ## ##  ###  ##
7 //**	  ###   ##    ##   ###    ##  ##   ##  ##  ##       ##
8 //**	   #    ##    ##    #      ####     ####   ##       ##
9 //**
10 //**	$Id: cl_main.cpp 4297 2010-06-03 22:49:00Z firebrand_kh $
11 //**
12 //**	Copyright (C) 1999-2006 Jānis Legzdiņš
13 //**
14 //**	This program is free software; you can redistribute it and/or
15 //**  modify it under the terms of the GNU General Public License
16 //**  as published by the Free Software Foundation; either version 2
17 //**  of the License, or (at your option) any later version.
18 //**
19 //**	This program is distributed in the hope that it will be useful,
20 //**  but WITHOUT ANY WARRANTY; without even the implied warranty of
21 //**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22 //**  GNU General Public License for more details.
23 //**
24 //**************************************************************************
25 
26 // HEADER FILES ------------------------------------------------------------
27 
28 #include "gamedefs.h"
29 #include "network.h"
30 #include "cl_local.h"
31 #include "ui.h"
32 #include "sv_local.h"
33 
34 // MACROS ------------------------------------------------------------------
35 
36 // TYPES -------------------------------------------------------------------
37 
38 // EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
39 
40 void CL_SetUpNetClient(VSocketPublic*);
41 void SV_ConnectClient(VBasePlayer*);
42 void CL_Clear();
43 void CL_ReadFromServerInfo();
44 
45 // PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
46 
47 // PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
48 
49 // EXTERNAL DATA DECLARATIONS ----------------------------------------------
50 
51 // PUBLIC DATA DEFINITIONS -------------------------------------------------
52 
53 client_static_t		cls;
54 VBasePlayer*		cl;
55 VClientNetContext*	ClientNetContext;
56 
57 VClientGameBase*	GClGame;
58 
59 bool				UserInfoSent;
60 
61 VCvarS			cl_name("name", "PLAYER", CVAR_Archive | CVAR_UserInfo);
62 VCvarI			cl_colour("colour", "0", CVAR_Archive | CVAR_UserInfo);
63 VCvarI			cl_class("class", "0", CVAR_Archive | CVAR_UserInfo);
64 VCvarS			cl_model("model", "", CVAR_Archive | CVAR_UserInfo);
65 
66 // PRIVATE DATA DEFINITIONS ------------------------------------------------
67 
68 IMPLEMENT_CLASS(V, ClientGameBase);
69 
70 static VName				CurrentSongLump;
71 static int					CurrentCDTrack;
72 
73 // CODE --------------------------------------------------------------------
74 
75 //==========================================================================
76 //
77 //	CL_Init
78 //
79 //==========================================================================
80 
CL_Init()81 void CL_Init()
82 {
83 	guard(CL_Init);
84 	VMemberBase::StaticLoadPackage(NAME_cgame, TLocation());
85 	TLocation::ClearSourceFiles();
86 
87 	ClientNetContext = new VClientNetContext();
88 	GClGame = (VClientGameBase*)VObject::StaticSpawnObject(
89 		VClass::FindClass("ClientGame"));
90 	GClGame->Game = GGameInfo;
91 	unguard;
92 }
93 
94 //==========================================================================
95 //
96 //	CL_Ticker
97 //
98 //==========================================================================
99 
CL_Ticker()100 void CL_Ticker()
101 {
102 	guard(CL_Ticker);
103 	// do main actions
104 	switch (GClGame->intermission)
105 	{
106 	case 0:
107 		SB_Ticker();
108 		AM_Ticker();
109 		break;
110 	}
111 	R_AnimateSurfaces();
112 	unguard;
113 }
114 
115 //==========================================================================
116 //
117 //	CL_Shutdown
118 //
119 //==========================================================================
120 
CL_Shutdown()121 void CL_Shutdown()
122 {
123 	guard(CL_Shutdown);
124 	if (cl)
125 	{
126 		//	Disconnect.
127 		SV_ShutdownGame();
128 	}
129 
130 	//	Free up memory.
131 	if (GClGame)
132 		GClGame->ConditionalDestroy();
133 	if (GRoot)
134 		GRoot->ConditionalDestroy();
135 	cls.userinfo.Clean();
136 	delete ClientNetContext;
137 	ClientNetContext = NULL;
138 	unguard;
139 }
140 
141 //==========================================================================
142 //
143 //	CL_DecayLights
144 //
145 //==========================================================================
146 
CL_DecayLights()147 void CL_DecayLights()
148 {
149 	guard(CL_DecayLights);
150 	if (GClLevel)
151 	{
152 		GClLevel->RenderData->DecayLights(host_frametime);
153 	}
154 	unguard;
155 }
156 
157 //==========================================================================
158 //
159 //	CL_UpdateMobjs
160 //
161 //==========================================================================
162 
CL_UpdateMobjs()163 void CL_UpdateMobjs()
164 {
165 	guard(CL_UpdateMobjs);
166 	for (TThinkerIterator<VThinker> Th(GClLevel); Th; ++Th)
167 	{
168 		Th->eventClientTick(host_frametime);
169 	}
170 	unguard;
171 }
172 
173 //==========================================================================
174 //
175 //	CL_ReadFromServer
176 //
177 //	Read all incoming data from the server
178 //
179 //==========================================================================
180 
CL_ReadFromServer()181 void CL_ReadFromServer()
182 {
183 	guard(CL_ReadFromServer);
184 	if (!cl)
185 	{
186 		return;
187 	}
188 
189 	if (cl->Net)
190 	{
191 		cl->Net->GetMessages();
192 		if (cl->Net->State == NETCON_Closed)
193 		{
194 			Host_EndGame("Server disconnected");
195 		}
196 	}
197 
198 	if (cls.signon)
199 	{
200 		if (GGameInfo->NetMode == NM_Client)
201 		{
202 			GClLevel->Time += host_frametime;
203 			GClLevel->TicTime = (int)(GClLevel->Time * 35.0);
204 		}
205 
206 		CL_UpdateMobjs();
207 		cl->eventClientTick(host_frametime);
208 		CL_Ticker();
209 	}
210 
211 	if (GClLevel && GClLevel->LevelInfo)
212 	{
213 		if (CurrentSongLump != GClLevel->LevelInfo->SongLump ||
214 			CurrentCDTrack != GClLevel->LevelInfo->CDTrack)
215 		{
216 			CurrentSongLump = GClLevel->LevelInfo->SongLump;
217 			CurrentCDTrack = GClLevel->LevelInfo->CDTrack;
218 			GAudio->MusicChanged();
219 		}
220 	}
221 	unguard;
222 }
223 
224 //==========================================================================
225 //
226 //	CL_KeepaliveMessage
227 //
228 //	When the client is taking a long time to load stuff, send keepalive
229 // messages so the server doesn't disconnect.
230 //
231 //==========================================================================
232 
CL_KeepaliveMessage()233 void CL_KeepaliveMessage()
234 {
235 	guard(CL_KeepaliveMessage);
236 	if (GGameInfo->NetMode != NM_Client)
237 		return;		// no need if server is local
238 	if (cls.demoplayback)
239 		return;
240 	// write out a nop
241 	cl->Net->Flush();
242 	unguard;
243 }
244 
245 //==========================================================================
246 //
247 //	CL_EstablishConnection
248 //
249 //	Host should be a net address to be passed on
250 //
251 //==========================================================================
252 
CL_EstablishConnection(const char * host)253 void CL_EstablishConnection(const char* host)
254 {
255 	guard(CL_EstablishConnection);
256 	if (GGameInfo->NetMode == NM_DedicatedServer)
257 	{
258 		return;
259 	}
260 
261 	SV_ShutdownGame();
262 
263 	VSocketPublic* Sock = GNet->Connect(host);
264 	if (!Sock)
265 	{
266 		GCon->Log("Failed to connect to the server");
267 		return;
268 	}
269 
270 	CL_SetUpNetClient(Sock);
271 	GCon->Logf(NAME_Dev, "CL_EstablishConnection: connected to %s", host);
272 	GGameInfo->NetMode = NM_Client;
273 
274 	UserInfoSent = false;
275 
276 	GClGame->eventConnected();
277 	cls.signon = 0;				// need all the signon messages before playing
278 
279 	MN_DeactivateMenu();
280 	unguard;
281 }
282 
283 //==========================================================================
284 //
285 //	CL_SetUpLocalPlayer
286 //
287 //==========================================================================
288 
CL_SetUpLocalPlayer()289 void CL_SetUpLocalPlayer()
290 {
291 	guard(CL_SetUpLocalPlayer);
292 	if (GGameInfo->NetMode == NM_DedicatedServer)
293 	{
294 		return;
295 	}
296 
297 	VBasePlayer* Player = GPlayersBase[0];
298 	SV_ConnectClient(Player);
299 	svs.num_connected++;
300 
301 	cl = Player;
302 	cl->ClGame = GClGame;
303 	GClGame->cl = cl;
304 
305 	cl->eventServerSetUserInfo(cls.userinfo);
306 
307 	GClGame->eventConnected();
308 	cls.signon = 0;				// need all the signon messages before playing
309 
310 	MN_DeactivateMenu();
311 
312 	CL_SetUpStandaloneClient();
313 	unguard;
314 }
315 
316 //==========================================================================
317 //
318 //	CL_SetUpStandaloneClient
319 //
320 //==========================================================================
321 
CL_SetUpStandaloneClient()322 void CL_SetUpStandaloneClient()
323 {
324 	guard(CL_SetUpStandaloneClient);
325 	CL_Clear();
326 
327 	GClGame->serverinfo = svs.serverinfo;
328 	CL_ReadFromServerInfo();
329 
330 	GClGame->maxclients = svs.max_clients;
331 	GClGame->deathmatch = deathmatch;
332 
333 	const mapInfo_t& LInfo = P_GetMapInfo(*GLevel->MapName);
334 	GCon->Log("---------------------------------------");
335 	GCon->Log(LInfo.GetName());
336 	GCon->Log("");
337 
338 	GClLevel = GLevel;
339 	GClGame->GLevel = GClLevel;
340 
341 	R_Start(GClLevel);
342 	GAudio->Start();
343 
344 	SB_Start();
345 
346 	for (int i = 0; i < GClLevel->NumStaticLights; i++)
347 	{
348 		rep_light_t& L = GClLevel->StaticLights[i];
349 		GClLevel->RenderData->AddStaticLight(L.Origin, L.Radius, L.Colour);
350 	}
351 	GClLevel->RenderData->PreRender();
352 
353 	cl->SpawnClient();
354 	cls.signon = 1;
355 
356 	GCon->Log(NAME_Dev, "Client level loaded");
357 	GCmdBuf << "HideConsole\n";
358 	unguard;
359 }
360 
361 //==========================================================================
362 //
363 //	CL_SendMove
364 //
365 //==========================================================================
366 
CL_SendMove()367 void CL_SendMove()
368 {
369 	guard(CL_SendMove);
370 	if (!cl)
371 	{
372 		return;
373 	}
374 
375 	if (cls.demoplayback || GGameInfo->NetMode == NM_TitleMap)
376 	{
377 		return;
378 	}
379 
380 	if (cls.signon)
381 	{
382 		if (!GGameInfo->IsPaused())
383 		{
384 			cl->HandleInput();
385 		}
386 		if (cl->Net)
387 		{
388 			((VPlayerChannel*)cl->Net->Channels[CHANIDX_Player])->Update();
389 		}
390 	}
391 
392 	if (cl->Net)
393 	{
394 		cl->Net->Tick();
395 	}
396 	unguard;
397 }
398 
399 //==========================================================================
400 //
401 //	CL_Responder
402 //
403 //	Get info needed to make ticcmd_ts for the players.
404 //
405 //==========================================================================
406 
CL_Responder(event_t * ev)407 bool CL_Responder(event_t* ev)
408 {
409 	guard(CL_Responder);
410 	if (GGameInfo->NetMode == NM_TitleMap)
411 	{
412 		return false;
413 	}
414 
415 	if (cl)
416 	{
417 		return cl->Responder(ev);
418 	}
419 	return false;
420 	unguard;
421 }
422 
423 //==========================================================================
424 //
425 //	CL_Clear
426 //
427 //==========================================================================
428 
CL_Clear()429 void CL_Clear()
430 {
431 	guard(CL_Clear);
432 	GClGame->serverinfo.Clean();
433 	GClGame->intermission = 0;
434 	if (cl)
435 	{
436 		cl->ClearInput();
437 	}
438 	if (GGameInfo->NetMode == NM_None || GGameInfo->NetMode == NM_Client)
439 	{
440 		// Make sure all sounds are stopped.
441 		GAudio->StopAllSound();
442 	}
443 	cls.signon = 0;
444 	unguard;
445 }
446 
447 //==========================================================================
448 //
449 //	CL_ReadFromServerInfo
450 //
451 //==========================================================================
452 
CL_ReadFromServerInfo()453 void CL_ReadFromServerInfo()
454 {
455 	guard(CL_ReadFromServerInfo);
456 	VCvar::SetCheating(!!atoi(*Info_ValueForKey(GClGame->serverinfo, "sv_cheats")));
457 	unguard;
458 }
459 
460 //==========================================================================
461 //
462 //	CL_DoLoadLevel
463 //
464 //==========================================================================
465 
CL_ParseServerInfo(VMessageIn & msg)466 void CL_ParseServerInfo(VMessageIn& msg)
467 {
468 	guard(CL_ParseServerInfo);
469 	CL_Clear();
470 
471 	msg << GClGame->serverinfo;
472 	CL_ReadFromServerInfo();
473 
474 	VStr TmpStr;
475 	msg << TmpStr;
476 	VName MapName = *TmpStr;
477 
478 	GClGame->maxclients = msg.ReadInt(MAXPLAYERS + 1);
479 	GClGame->deathmatch = msg.ReadInt(256);
480 
481 	const mapInfo_t& LInfo = P_GetMapInfo(MapName);
482 	GCon->Log("---------------------------------------");
483 	GCon->Log(LInfo.GetName());
484 	GCon->Log("");
485 
486 	CL_LoadLevel(MapName);
487 	GClLevel->NetContext = ClientNetContext;
488 
489 	((VLevelChannel*)cl->Net->Channels[CHANIDX_Level])->SetLevel(GClLevel);
490 
491 	R_Start(GClLevel);
492 	GAudio->Start();
493 
494 	SB_Start();
495 
496 	GCon->Log(NAME_Dev, "Client level loaded");
497 	unguard;
498 }
499 
500 //==========================================================================
501 //
502 //	VClientNetContext::GetLevel
503 //
504 //==========================================================================
505 
GetLevel()506 VLevel* VClientNetContext::GetLevel()
507 {
508 	return GClLevel;
509 }
510 
511 //==========================================================================
512 //
513 //	CL_SetUpNetClient
514 //
515 //==========================================================================
516 
CL_SetUpNetClient(VSocketPublic * Sock)517 void CL_SetUpNetClient(VSocketPublic* Sock)
518 {
519 	guard(CL_SetUpNetClient);
520 	//	Create player structure.
521 	cl = (VBasePlayer*)VObject::StaticSpawnObject(
522 		VClass::FindClass("Player"));
523 	cl->PlayerFlags |= VBasePlayer::PF_IsClient;
524 	cl->ClGame = GClGame;
525 	GClGame->cl = cl;
526 
527 	if (cls.demorecording)
528 	{
529 		cl->Net = new VDemoRecordingNetConnection(Sock, ClientNetContext, cl);
530 	}
531 	else
532 	{
533 		cl->Net = new VNetConnection(Sock, ClientNetContext, cl);
534 	}
535 	ClientNetContext->ServerConnection = cl->Net;
536 	((VPlayerChannel*)cl->Net->Channels[CHANIDX_Player])->SetPlayer(cl);
537 	unguard;
538 }
539 
540 //==========================================================================
541 //
542 //	CL_PlayDemo
543 //
544 //==========================================================================
545 
CL_PlayDemo(const VStr & DemoName,bool IsTimeDemo)546 void CL_PlayDemo(const VStr& DemoName, bool IsTimeDemo)
547 {
548 	guard(CL_PlayDemo);
549 	char	magic[8];
550 
551 	//
552 	// open the demo file
553 	//
554 	VStr name = VStr("demos/") + DemoName.DefaultExtension(".dem");
555 
556 	GCon->Logf("Playing demo from %s.", *name);
557 	VStream* Strm = FL_OpenFileRead(name);
558 	if (!Strm)
559 	{
560 		GCon->Log("ERROR: couldn't open.");
561 		return;
562 	}
563 
564 	Strm->Serialise(magic, 4);
565 	magic[4] = 0;
566 	if (VStr::Cmp(magic, "VDEM"))
567 	{
568 		delete Strm;
569 		Strm = NULL;
570 		GCon->Log("ERROR: not a Vavoom demo.");
571 		return;
572 	}
573 
574 	//
575 	// disconnect from server
576 	//
577 	SV_ShutdownGame();
578 
579 	cls.demoplayback = true;
580 
581 	//	Create player structure.
582 	cl = (VBasePlayer*)VObject::StaticSpawnObject(
583 		VClass::FindClass("Player"));
584 	cl->PlayerFlags |= VBasePlayer::PF_IsClient;
585 	cl->ClGame = GClGame;
586 	GClGame->cl = cl;
587 
588 	cl->Net = new VDemoPlaybackNetConnection(ClientNetContext, cl,
589 		Strm, IsTimeDemo);
590 	ClientNetContext->ServerConnection = cl->Net;
591 	((VPlayerChannel*)cl->Net->Channels[CHANIDX_Player])->SetPlayer(cl);
592 
593 	GGameInfo->NetMode = NM_Client;
594 	GClGame->eventDemoPlaybackStarted();
595 	unguard;
596 }
597 
598 //==========================================================================
599 //
600 //	CL_StopRecording
601 //
602 //==========================================================================
603 
CL_StopRecording()604 void CL_StopRecording()
605 {
606 	guard(CL_StopRecording);
607 	// finish up
608 	delete cls.demofile;
609 	cls.demofile = NULL;
610 	cls.demorecording = false;
611 	if (GDemoRecordingContext)
612 	{
613 		delete GDemoRecordingContext;
614 		GDemoRecordingContext = NULL;
615 	}
616 	GCon->Log("Completed demo");
617 	unguard;
618 }
619 
620 //==========================================================================
621 //
622 //	COMMAND Connect
623 //
624 //==========================================================================
625 
COMMAND(Connect)626 COMMAND(Connect)
627 {
628 	CL_EstablishConnection(Args.Num() > 1 ? *Args[1] : "");
629 }
630 
631 //==========================================================================
632 //
633 //	COMMAND Disconnect
634 //
635 //==========================================================================
636 
COMMAND(Disconnect)637 COMMAND(Disconnect)
638 {
639 	SV_ShutdownGame();
640 }
641 
642 //==========================================================================
643 //
644 //	COMMAND StopDemo
645 //
646 //	stop recording a demo
647 //
648 //==========================================================================
649 
COMMAND(StopDemo)650 COMMAND(StopDemo)
651 {
652 	guard(COMMAND StopDemo);
653 	if (Source != SRC_Command)
654 	{
655 		return;
656 	}
657 
658 	if (!cls.demorecording)
659 	{
660 		GCon->Log("Not recording a demo.");
661 		return;
662 	}
663 	CL_StopRecording();
664 	unguard;
665 }
666 
667 //==========================================================================
668 //
669 //	COMMAND Record
670 //
671 //	record <demoname> <map>
672 //
673 //==========================================================================
674 
COMMAND(Record)675 COMMAND(Record)
676 {
677 	guard(COMMAND Record);
678 	if (Source != SRC_Command)
679 	{
680 		return;
681 	}
682 
683 	int c = Args.Num();
684 	if (c != 2 && c != 3)
685 	{
686 		GCon->Log("record <demoname> [<map>]");
687 		return;
688 	}
689 
690 	if (strstr(*Args[1], ".."))
691 	{
692 		GCon->Log("Relative pathnames are not allowed.");
693 		return;
694 	}
695 
696 	if (c == 2 && GGameInfo->NetMode == NM_Client)
697 	{
698 		GCon->Log("Can not record - already connected to server");
699 		GCon->Log("Client demo recording must be started before connecting");
700 		return;
701 	}
702 
703 	if (cls.demorecording)
704 	{
705 		GCon->Log("Already recording a demo");
706 		return;
707 	}
708 
709 	VStr name = VStr("demos/") + Args[1].DefaultExtension(".dem");
710 
711 	//
712 	// start the map up
713 	//
714 	if (c > 2)
715 	{
716 		VCommand::ExecuteString(VStr("map ") + Args[2], SRC_Command, NULL);
717 	}
718 
719 	//
720 	// open the demo file
721 	//
722 
723 	GCon->Logf("recording to %s.", *name);
724 	cls.demofile = FL_OpenFileWrite(name);
725 	if (!cls.demofile)
726 	{
727 		GCon->Log("ERROR: couldn't open.");
728 		return;
729 	}
730 
731 	cls.demofile->Serialise(const_cast<char*>("VDEM"), 4);
732 
733 	cls.demorecording = true;
734 
735 	if (GGameInfo->NetMode == NM_Standalone ||
736 		GGameInfo->NetMode == NM_ListenServer)
737 	{
738 		GDemoRecordingContext = new VServerNetContext();
739 		VSocketPublic* Sock = new VDemoRecordingSocket();
740 		VNetConnection* Conn = new VNetConnection(Sock,
741 			GDemoRecordingContext, cl);
742 		Conn->AutoAck = true;
743 		GDemoRecordingContext->ClientConnections.Append(Conn);
744 		Conn->ObjMap->SetUpClassLookup();
745 		VObjectMapChannel* Chan = (VObjectMapChannel*)Conn->CreateChannel(
746 			CHANNEL_ObjectMap, -1);
747 		while (!Conn->ObjMapSent)
748 		{
749 			Conn->Tick();
750 		}
751 		Conn->SendServerInfo();
752 		((VPlayerChannel*)Conn->Channels[CHANIDX_Player])->SetPlayer(cl);
753 	}
754 	unguard;
755 }
756 
757 //==========================================================================
758 //
759 //	COMMAND PlayDemo
760 //
761 //	play [demoname]
762 //
763 //==========================================================================
764 
COMMAND(PlayDemo)765 COMMAND(PlayDemo)
766 {
767 	guard(COMMAND PlayDemo);
768 	if (Source != SRC_Command)
769 	{
770 		return;
771 	}
772 
773 	if (Args.Num() != 2)
774 	{
775 		GCon->Log("play <demoname> : plays a demo");
776 		return;
777 	}
778 
779 	CL_PlayDemo(Args[1], false);
780 	unguard;
781 }
782 
783 //==========================================================================
784 //
785 //	COMMAND TimeDemo
786 //
787 //	timedemo [demoname]
788 //
789 //==========================================================================
790 
COMMAND(TimeDemo)791 COMMAND(TimeDemo)
792 {
793 	guard(COMMAND TimeDemo);
794 	if (Source != SRC_Command)
795 	{
796 		return;
797 	}
798 
799 	if (Args.Num() != 2)
800 	{
801 		GCon->Log("timedemo <demoname> : gets demo speeds");
802 		return;
803 	}
804 
805 	CL_PlayDemo(Args[1], true);
806 	unguard;
807 }
808