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