1 //**************************************************************************
2 //**
3 //**	##   ##    ##    ##   ##   ####     ####   ###     ###
4 //**	##   ##  ##  ##  ##   ##  ##  ##   ##  ##  ####   ####
5 //**	 ## ##  ##    ##  ## ##  ##    ## ##    ## ## ## ## ##
6 //**	 ## ##  ########  ## ##  ##    ## ##    ## ##  ###  ##
7 //**	  ###   ##    ##   ###    ##  ##   ##  ##  ##       ##
8 //**	   #    ##    ##    #      ####     ####   ##       ##
9 //**
10 //**	$Id: sv_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 "sv_local.h"
31 #include "cl_local.h"
32 
33 // MACROS ------------------------------------------------------------------
34 
35 // TYPES -------------------------------------------------------------------
36 
37 // EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
38 
39 // PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
40 
41 // PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
42 
43 static void G_DoReborn(int playernum);
44 static void G_DoCompleted();
45 
46 // EXTERNAL DATA DECLARATIONS ----------------------------------------------
47 
48 // PUBLIC DATA DEFINITIONS -------------------------------------------------
49 
50 VCvarI			real_time("real_time", "1");
51 
52 server_t		sv;
53 server_static_t	svs;
54 
55 // increment every time a check is made
56 int				validcount = 1;
57 
58 bool			sv_loading = false;
59 bool			sv_map_travel = false;
60 int				sv_load_num_players;
61 bool			run_open_scripts;
62 
63 VBasePlayer*	GPlayersBase[MAXPLAYERS];
64 
65 vuint8			deathmatch = false;   	// only if started as net death
66 
67 int 			TimerGame;
68 
69 VLevelInfo*		GLevelInfo;
70 
71 int 			LeavePosition;
72 
73 bool			completed;
74 
75 VNetContext*	GDemoRecordingContext;
76 
77 // PRIVATE DATA DEFINITIONS ------------------------------------------------
78 
79 static int		RebornPosition;	// Position indicator for cooperative net-play reborn
80 
81 static bool		mapteleport_issued;
82 
83 static VCvarI	TimeLimit("TimeLimit", "0");
84 static VCvarI	DeathMatch("DeathMatch", "0", CVAR_ServerInfo);
85 static VCvarI	NoMonsters("NoMonsters", "0");
86 static VCvarI	Skill("Skill", "2");
87 static VCvarI	sv_cheats("sv_cheats", "0", CVAR_ServerInfo | CVAR_Latch);
88 static VCvarI	split_frame("split_frame", "1", CVAR_Archive);
89 static VCvarI	sv_maxmove("sv_maxmove", "400", CVAR_Archive);
90 static VCvarF	master_heartbeat_time("master_heartbeat_time", "300", CVAR_Archive);
91 
92 static VServerNetContext*	ServerNetContext;
93 
94 static double	LastMasterUpdate;
95 
96 // CODE --------------------------------------------------------------------
97 
98 //==========================================================================
99 //
100 //	SV_Init
101 //
102 //==========================================================================
103 
SV_Init()104 void SV_Init()
105 {
106 	guard(SV_Init);
107 	int		i;
108 
109 	svs.max_clients = 1;
110 
111 	VMemberBase::StaticLoadPackage(NAME_game, TLocation());
112 
113 	GGameInfo = (VGameInfo*)VObject::StaticSpawnObject(
114 		VClass::FindClass("MainGameInfo"));
115 	GGameInfo->eventInit();
116 
117 	ProcessDecorateScripts();
118 
119 	ProcessDehackedFiles();
120 
121 	for (int i = 0; i < VClass::GSpriteNames.Num(); i++)
122 	{
123 		R_InstallSprite(*VClass::GSpriteNames[i], i);
124 	}
125 
126 	ServerNetContext = new VServerNetContext();
127 
128 	VClass* PlayerClass = VClass::FindClass("Player");
129 	for (i = 0; i < MAXPLAYERS; i++)
130 	{
131 		GPlayersBase[i] = (VBasePlayer*)VObject::StaticSpawnObject(
132 			PlayerClass);
133 	}
134 
135 	GGameInfo->validcount = &validcount;
136 	GGameInfo->skyflatnum = skyflatnum;
137 	VEntity::InitFuncIndexes();
138 
139 	P_InitSwitchList();
140 	P_InitTerrainTypes();
141 	InitLockDefs();
142 	unguard;
143 }
144 
145 //==========================================================================
146 //
147 //	P_InitThinkers
148 //
149 //==========================================================================
150 
P_InitThinkers()151 void P_InitThinkers()
152 {
153 	VThinker::FIndex_Tick = VThinker::StaticClass()->GetMethodIndex(NAME_Tick);
154 }
155 
156 //==========================================================================
157 //
158 //	SV_Shutdown
159 //
160 //==========================================================================
161 
SV_Shutdown()162 void SV_Shutdown()
163 {
164 	guard(SV_Shutdown);
165 	if (GGameInfo)
166 	{
167 		SV_ShutdownGame();
168 		GGameInfo->ConditionalDestroy();
169 	}
170 	for (int i = 0; i < MAXPLAYERS; i++)
171 	{
172 		if (GPlayersBase[i])
173 		{
174 			delete GPlayersBase[i]->Net;
175 			GPlayersBase[i]->Net = NULL;
176 			GPlayersBase[i]->ConditionalDestroy();
177 		}
178 	}
179 
180 	P_FreeTerrainTypes();
181 	ShutdownLockDefs();
182 	svs.serverinfo.Clean();
183 
184 	delete ServerNetContext;
185 	ServerNetContext = NULL;
186 	unguard;
187 }
188 
189 //==========================================================================
190 //
191 //	SV_Clear
192 //
193 //==========================================================================
194 
SV_Clear()195 void SV_Clear()
196 {
197 	guard(SV_Clear);
198 	if (GLevel)
199 	{
200 		GLevel->ConditionalDestroy();
201 		GLevel = NULL;
202 		VObject::CollectGarbage();
203 	}
204 	memset(&sv, 0, sizeof(sv));
205 #ifdef CLIENT
206 	// Make sure all sounds are stopped.
207 	GAudio->StopAllSound();
208 #endif
209 	unguard;
210 }
211 
212 //==========================================================================
213 //
214 //	SV_SendClientMessages
215 //
216 //==========================================================================
217 
SV_SendClientMessages()218 void SV_SendClientMessages()
219 {
220 	guard(SV_SendClientMessages);
221 	//	Update player replication infos.
222 	for (int i = 0; i < svs.max_clients; i++)
223 	{
224 		if (!GGameInfo->Players[i])
225 		{
226 			continue;
227 		}
228 
229 		VBasePlayer* Player = GGameInfo->Players[i];
230 
231 		VPlayerReplicationInfo* RepInfo = Player->PlayerReplicationInfo;
232 		RepInfo->PlayerName = Player->PlayerName;
233 		RepInfo->UserInfo = Player->UserInfo;
234 		RepInfo->TranslStart = Player->TranslStart;
235 		RepInfo->TranslEnd = Player->TranslEnd;
236 		RepInfo->Colour = Player->Colour;
237 		RepInfo->Frags = Player->Frags;
238 		RepInfo->Deaths = Player->Deaths;
239 		RepInfo->KillCount = Player->KillCount;
240 		RepInfo->ItemCount = Player->ItemCount;
241 		RepInfo->SecretCount = Player->SecretCount;
242 
243 		//	Update view angle if needed.
244 		if (Player->PlayerFlags & VBasePlayer::PF_Spawned)
245 		{
246 			Player->WriteViewData();
247 		}
248 	}
249 
250 	ServerNetContext->Tick();
251 
252 	if (GDemoRecordingContext)
253 	{
254 		for (int i = 0; i < GDemoRecordingContext->ClientConnections.Num(); i++)
255 		{
256 			GDemoRecordingContext->ClientConnections[i]->NeedsUpdate = true;
257 		}
258 		GDemoRecordingContext->Tick();
259 	}
260 	unguard;
261 }
262 
263 //========================================================================
264 //
265 //	CheckForSkip
266 //
267 //	Check to see if any player hit a key
268 //
269 //========================================================================
270 
CheckForSkip()271 static void CheckForSkip()
272 {
273 	int   			i;
274 	VBasePlayer		*player;
275 	static bool		triedToSkip;
276 	bool			skip = false;
277 
278 	for (i = 0; i < MAXPLAYERS; i++)
279 	{
280 		player = GGameInfo->Players[i];
281 		if (player)
282 		{
283 			if (player->Buttons & BT_ATTACK)
284 			{
285 				if (!(player->PlayerFlags & VBasePlayer::PF_AttackDown))
286 				{
287 					skip = true;
288 				}
289 				player->PlayerFlags |= VBasePlayer::PF_AttackDown;
290 			}
291 			else
292 			{
293 				player->PlayerFlags &= ~VBasePlayer::PF_AttackDown;
294 			}
295 			if (player->Buttons & BT_USE)
296 			{
297 				if (!(player->PlayerFlags & VBasePlayer::PF_UseDown))
298 				{
299 					skip = true;
300 				}
301 				player->PlayerFlags |= VBasePlayer::PF_UseDown;
302 			}
303 			else
304 			{
305 				player->PlayerFlags &= ~VBasePlayer::PF_UseDown;
306 			}
307 		}
308 	}
309 
310 	if (deathmatch && sv.intertime < 140)
311 	{
312 		// wait for 4 seconds before allowing a skip
313 		if (skip)
314 		{
315 			triedToSkip = true;
316 			skip = false;
317 		}
318 	}
319 	else
320 	{
321 		if (triedToSkip)
322 		{
323 			skip = true;
324 			triedToSkip = false;
325 		}
326 	}
327 	if (skip)
328 	{
329 		for (int i = 0; i < svs.max_clients; i++)
330 			if (GGameInfo->Players[i])
331 				GGameInfo->Players[i]->eventClientSkipIntermission();
332 	}
333 }
334 
335 //==========================================================================
336 //
337 //	SV_RunClients
338 //
339 //==========================================================================
340 
SV_RunClients()341 void SV_RunClients()
342 {
343 	guard(SV_RunClients);
344 	// get commands
345 	for (int i = 0; i < MAXPLAYERS; i++)
346 	{
347 		VBasePlayer* Player = GGameInfo->Players[i];
348 		if (!Player)
349 		{
350 			continue;
351 		}
352 
353 		if ((Player->PlayerFlags & VBasePlayer::PF_IsBot) &&
354 			!(Player->PlayerFlags & VBasePlayer::PF_Spawned))
355 		{
356 			Player->SpawnClient();
357 		}
358 
359 		// do player reborns if needed
360 		if (Player->PlayerState == PST_REBORN)
361 		{
362 			G_DoReborn(i);
363 		}
364 
365 		if (Player->Net)
366 		{
367 			Player->Net->NeedsUpdate = false;
368 			Player->Net->GetMessages();
369 		}
370 
371 		// pause if in menu or console and at least one tic has been run
372 		if (Player->PlayerFlags & VBasePlayer::PF_Spawned &&
373 			!sv.intermission && !GGameInfo->IsPaused())
374 		{
375 			Player->ForwardMove = Player->ClientForwardMove;
376 			Player->SideMove = Player->ClientSideMove;
377 			// Don't move faster than maxmove
378 			if (Player->ForwardMove > sv_maxmove)
379 			{
380 				Player->ForwardMove = sv_maxmove;
381 			}
382 			else if (Player->ForwardMove < -sv_maxmove)
383 			{
384 				Player->ForwardMove = -sv_maxmove;
385 			}
386 			if (Player->SideMove > sv_maxmove)
387 			{
388 				Player->SideMove = sv_maxmove;
389 			}
390 			else if (Player->SideMove < -sv_maxmove)
391 			{
392 				Player->SideMove = -sv_maxmove;
393 			}
394 			//	Check for disabled freelook and jumping
395 			if (GLevelInfo->LevelInfoFlags & VLevelInfo::LIF_NoFreelook)
396 			{
397 				Player->ViewAngles.pitch = 0;
398 			}
399 			if (GLevelInfo->LevelInfoFlags & VLevelInfo::LIF_NoJump)
400 			{
401 				Player->Buttons &= ~BT_JUMP;
402 			}
403 			Player->OldViewAngles = Player->ViewAngles;
404 			Player->eventPlayerTick(host_frametime);
405 			Player->OldButtons = Player->Buttons;
406 		}
407 	}
408 
409 	if (sv.intermission)
410 	{
411 		CheckForSkip();
412 		sv.intertime++;
413 	}
414 	unguard;
415 }
416 
417 //==========================================================================
418 //
419 //	SV_Ticker
420 //
421 //==========================================================================
422 
SV_Ticker()423 void SV_Ticker()
424 {
425 	guard(SV_Ticker);
426 	float	saved_frametime;
427 	int		exec_times;
428 
429 	if (GGameInfo->NetMode >= NM_DedicatedServer && (!LastMasterUpdate ||
430 		host_time - LastMasterUpdate > master_heartbeat_time))
431 	{
432 		GNet->UpdateMaster();
433 		LastMasterUpdate = host_time;
434 	}
435 
436 	saved_frametime = host_frametime;
437 	exec_times = 1;
438 	if (!real_time)
439 	{
440 		// Rounded a little bit up to prevent "slow motion"
441 		host_frametime = 0.028572f;//1.0 / 35.0;
442 	}
443 	else if (split_frame)
444 	{
445 		while (host_frametime / exec_times > 0.028572f/*1.0 / 35.0*/)
446 			exec_times++;
447 	}
448 
449 	GGameInfo->frametime = host_frametime;
450 	SV_RunClients();
451 
452 	if (sv_loading)
453 		return;
454 
455 	// do main actions
456 	if (!sv.intermission)
457 	{
458 		host_frametime /= exec_times;
459 		GGameInfo->frametime = host_frametime;
460 		for (int i = 0; i < exec_times && !completed; i++)
461 		{
462 			if (!GGameInfo->IsPaused())
463 			{
464 				//	LEVEL TIMER
465 				if (TimerGame)
466 				{
467 					if (!--TimerGame)
468 					{
469 						LeavePosition = 0;
470 						completed = true;
471 					}
472 				}
473 				if (i)
474 				{
475 					VObject::CollectGarbage();
476 				}
477 				GLevel->TickWorld(host_frametime);
478 			}
479 		}
480 	}
481 
482 	if (completed)
483 	{
484 		G_DoCompleted();
485 	}
486 
487 	host_frametime = saved_frametime;
488 	unguard;
489 }
490 
491 //==========================================================================
492 //
493 //	CheckRedirects
494 //
495 //==========================================================================
496 
CheckRedirects(VName Map)497 static VName CheckRedirects(VName Map)
498 {
499 	guard(CheckRedirects);
500 	const mapInfo_t& Info = P_GetMapInfo(Map);
501 	if (Info.RedirectType == NAME_None || Info.RedirectMap == NAME_None)
502 	{
503 		//	No redirect for this map.
504 		return Map;
505 	}
506 
507 	//	Check all players.
508 	for (int i = 0; i < MAXPLAYERS; i++)
509 	{
510 		VBasePlayer* P = GGameInfo->Players[i];
511 		if (!P || !(P->PlayerFlags & VBasePlayer::PF_Spawned))
512 		{
513 			continue;
514 		}
515 		if (P->MO->eventCheckInventory(Info.RedirectType) > 0)
516 		{
517 			return CheckRedirects(Info.RedirectMap);
518 		}
519 	}
520 
521 	//	None of the players have required item, no redirect.
522 	return Map;
523 	unguard;
524 }
525 
526 //==========================================================================
527 //
528 //	G_DoCompleted
529 //
530 //==========================================================================
531 
G_DoCompleted()532 static void G_DoCompleted()
533 {
534 	int			i;
535 
536 	completed = false;
537 	if (sv.intermission)
538 	{
539 		return;
540 	}
541 	if ((GGameInfo->NetMode < NM_DedicatedServer) && (!GGameInfo->Players[0] ||
542 		!(GGameInfo->Players[0]->PlayerFlags & VBasePlayer::PF_Spawned)))
543 	{
544 		//FIXME Some ACS left from previous visit of the level
545 		return;
546 	}
547 	sv.intermission = 1;
548 	sv.intertime = 0;
549 	GLevelInfo->CompletitionTime = GLevel->Time;
550 
551 	GLevel->Acs->StartTypedACScripts(SCRIPT_Unloading, 0, 0, 0, NULL, false,
552 		true);
553 
554 	GLevelInfo->NextMap = CheckRedirects(GLevelInfo->NextMap);
555 
556 	const mapInfo_t& old_info = P_GetMapInfo(GLevel->MapName);
557 	const mapInfo_t& new_info = P_GetMapInfo(GLevelInfo->NextMap);
558 	const VClusterDef* ClusterD = P_GetClusterDef(old_info.Cluster);
559 	bool HubChange = !old_info.Cluster || !(ClusterD->Flags & CLUSTERF_Hub) ||
560 		old_info.Cluster != new_info.Cluster;
561 
562 	for (i = 0; i < MAXPLAYERS; i++)
563 	{
564 		if (GGameInfo->Players[i])
565 		{
566 			GGameInfo->Players[i]->eventPlayerExitMap(HubChange);
567 			if (deathmatch || HubChange)
568 			{
569 				GGameInfo->Players[i]->eventClientIntermission(
570 					GLevelInfo->NextMap);
571 			}
572 		}
573 	}
574 
575 	if (!deathmatch && !HubChange)
576 	{
577 		GCmdBuf << "TeleportNewMap\n";
578 	}
579 }
580 
581 //==========================================================================
582 //
583 //	COMMAND	TeleportNewMap
584 //
585 //==========================================================================
586 
COMMAND(TeleportNewMap)587 COMMAND(TeleportNewMap)
588 {
589 	guard(COMMAND TeleportNewMap);
590 	if (Source == SRC_Command)
591 	{
592 		ForwardToServer();
593 		return;
594 	}
595 
596 	if (GGameInfo->NetMode == NM_None ||
597 		GGameInfo->NetMode == NM_Client)
598 	{
599 		return;
600 	}
601 
602 	if (Args.Num() == 3)
603 	{
604 		GLevelInfo->NextMap = VName(*Args[1], VName::AddLower8);
605 		LeavePosition = atoi(*Args[2]);
606 	}
607 	else if (sv.intermission != 1)
608 	{
609 		return;
610 	}
611 
612 	if (!deathmatch)
613 	{
614 		if (VStr(GLevelInfo->NextMap).StartsWith("EndGame"))
615 		{
616 			for (int i = 0; i < svs.max_clients; i++)
617 				if (GGameInfo->Players[i])
618 					GGameInfo->Players[i]->eventClientFinale(*GLevelInfo->NextMap);
619 			sv.intermission = 2;
620 			return;
621 		}
622 	}
623 
624 #ifdef CLIENT
625 	Draw_TeleportIcon();
626 #endif
627 	RebornPosition = LeavePosition;
628 	GGameInfo->RebornPosition = RebornPosition;
629 	mapteleport_issued = true;
630 	unguard;
631 }
632 
633 //==========================================================================
634 //
635 //	G_DoReborn
636 //
637 //==========================================================================
638 
G_DoReborn(int playernum)639 static void G_DoReborn(int playernum)
640 {
641 	if (!GGameInfo->Players[playernum] ||
642 		!(GGameInfo->Players[playernum]->PlayerFlags & VBasePlayer::PF_Spawned))
643 	{
644 		return;
645 	}
646 	if (GGameInfo->NetMode == NM_Standalone)
647 	{
648 		GCmdBuf << "Restart\n";
649 		GGameInfo->Players[playernum]->PlayerState = PST_LIVE;
650 	}
651 	else
652 	{
653 		GGameInfo->Players[playernum]->eventNetGameReborn();
654 	}
655 }
656 
657 //==========================================================================
658 //
659 //	NET_SendToAll
660 //
661 //==========================================================================
662 
NET_SendToAll(int blocktime)663 int NET_SendToAll(int blocktime)
664 {
665 	guard(NET_SendToAll);
666 	double		start;
667 	int			i;
668 	int			count = 0;
669 	bool		state1[MAXPLAYERS];
670 	bool		state2[MAXPLAYERS];
671 
672 	for (i = 0; i < svs.max_clients; i++)
673 	{
674 		VBasePlayer* Player = GGameInfo->Players[i];
675 		if (Player && Player->Net)
676 		{
677 			if (Player->Net->IsLocalConnection())
678 			{
679 				state1[i] = false;
680 				state2[i] = true;
681 				continue;
682 			}
683 			count++;
684 			state1[i] = false;
685 			state2[i] = false;
686 		}
687 		else
688 		{
689 			state1[i] = true;
690 			state2[i] = true;
691 		}
692 	}
693 
694 	start = Sys_Time();
695 	while (count)
696 	{
697 		count = 0;
698 		for (i = 0; i < svs.max_clients; i++)
699 		{
700 			VBasePlayer* Player = GGameInfo->Players[i];
701 			if (!state1[i])
702 			{
703 				state1[i] = true;
704 				Player->Net->Channels[0]->Close();
705 				count++;
706 				continue;
707 			}
708 
709 			if (!state2[i])
710 			{
711 				if (Player->Net->State == NETCON_Closed)
712 				{
713 					state2[i] = true;
714 				}
715 				else
716 				{
717 					Player->Net->GetMessages();
718 					Player->Net->Tick();
719 				}
720 				count++;
721 				continue;
722 			}
723 		}
724 		if ((Sys_Time() - start) > blocktime)
725 			break;
726 	}
727 	return count;
728 	unguard;
729 }
730 
731 //==========================================================================
732 //
733 //	SV_SendServerInfoToClients
734 //
735 //==========================================================================
736 
SV_SendServerInfoToClients()737 void SV_SendServerInfoToClients()
738 {
739 	guard(SV_SendServerInfoToClients);
740 	for (int i = 0; i < svs.max_clients; i++)
741 	{
742 		VBasePlayer* Player = GGameInfo->Players[i];
743 		if (!Player)
744 		{
745 			continue;
746 		}
747 		Player->Level = GLevelInfo;
748 		if (Player->Net)
749 		{
750 			Player->Net->SendServerInfo();
751 		}
752 	}
753 	unguard;
754 }
755 
756 //==========================================================================
757 //
758 //	SV_SpawnServer
759 //
760 //==========================================================================
761 
SV_SpawnServer(const char * mapname,bool spawn_thinkers,bool titlemap)762 void SV_SpawnServer(const char *mapname, bool spawn_thinkers, bool titlemap)
763 {
764 	guard(SV_SpawnServer);
765 	int			i;
766 
767 	GCon->Logf(NAME_Dev, "Spawning server %s", mapname);
768 	GGameInfo->Flags &= ~VGameInfo::GIF_Paused;
769 	mapteleport_issued = false;
770 	run_open_scripts = spawn_thinkers;
771 
772 	if (GGameInfo->NetMode != NM_None)
773 	{
774 		//	Level change
775 		for (i = 0; i < MAXPLAYERS; i++)
776 		{
777 			if (!GGameInfo->Players[i])
778 				continue;
779 
780 			GGameInfo->Players[i]->KillCount = 0;
781 			GGameInfo->Players[i]->SecretCount = 0;
782 			GGameInfo->Players[i]->ItemCount = 0;
783 
784 			GGameInfo->Players[i]->PlayerFlags &= ~VBasePlayer::PF_Spawned;
785 			GGameInfo->Players[i]->MO = NULL;
786 			GGameInfo->Players[i]->Frags = 0;
787 			GGameInfo->Players[i]->Deaths = 0;
788 			if (GGameInfo->Players[i]->PlayerState == PST_DEAD)
789 				GGameInfo->Players[i]->PlayerState = PST_REBORN;
790 		}
791 	}
792 	else
793 	{
794 		//	New game
795 		deathmatch = DeathMatch;
796 		GGameInfo->deathmatch = deathmatch;
797 
798 		P_InitThinkers();
799 
800 #ifdef CLIENT
801 		GGameInfo->NetMode = titlemap ? NM_TitleMap :
802 			svs.max_clients == 1 ? NM_Standalone : NM_ListenServer;
803 #else
804 		GGameInfo->NetMode = NM_DedicatedServer;
805 #endif
806 
807 		GGameInfo->WorldInfo = GGameInfo->eventCreateWorldInfo();
808 
809 		GGameInfo->WorldInfo->SetSkill(Skill);
810 		GGameInfo->eventInitNewGame(GGameInfo->WorldInfo->GameSkill);
811 	}
812 
813 	SV_Clear();
814 	VCvar::Unlatch();
815 
816 	//	Load it
817 	SV_LoadLevel(VName(mapname, VName::AddLower8));
818 	GLevel->NetContext = ServerNetContext;
819 	GLevel->WorldInfo = GGameInfo->WorldInfo;
820 
821 	const mapInfo_t& info = P_GetMapInfo(GLevel->MapName);
822 
823 	if (spawn_thinkers)
824 	{
825 		//	Create level info.
826 		GLevelInfo = (VLevelInfo*)GLevel->SpawnThinker(
827 			GGameInfo->LevelInfoClass);
828 		GLevelInfo->Level = GLevelInfo;
829 		GLevelInfo->Game = GGameInfo;
830 		GLevelInfo->World = GGameInfo->WorldInfo;
831 		GLevel->LevelInfo = GLevelInfo;
832 		GLevelInfo->SetMapInfo(info);
833 
834 		//	Spawn things.
835 		for (i = 0; i < GLevel->NumThings; i++)
836 		{
837 			GLevelInfo->eventSpawnMapThing(&GLevel->Things[i]);
838 		}
839 		if (deathmatch && GLevelInfo->DeathmatchStarts.Num() < 4)
840 		{
841 			Host_Error("Level needs more deathmatch start spots");
842 		}
843 	}
844 
845 	if (deathmatch)
846 	{
847 		TimerGame = TimeLimit * 35 * 60;
848 	}
849 	else
850 	{
851 		TimerGame = 0;
852 	}
853 
854 	// set up world state
855 
856 	//
857 	//	P_SpawnSpecials
858 	//	After the map has been loaded, scan for specials that spawn thinkers
859 	//
860 	if (spawn_thinkers)
861 	{
862 		GLevelInfo->eventSpawnSpecials();
863 	}
864 
865 	if (!spawn_thinkers)
866 	{
867 //		if (level.thinkerHead)
868 //		{
869 //			Sys_Error("Spawned a thinker when it's not allowed");
870 //		}
871 		return;
872 	}
873 
874 	SV_SendServerInfoToClients();
875 
876 	//	Call BeginPlay events.
877 	for (TThinkerIterator<VEntity> Ent(GLevel); Ent; ++Ent)
878 	{
879 		Ent->eventBeginPlay();
880 	}
881 	GLevelInfo->LevelInfoFlags2 |= VLevelInfo::LIF2_BegunPlay;
882 
883 	if (GGameInfo->NetMode != NM_TitleMap &&
884 		GGameInfo->NetMode != NM_Standalone)
885 	{
886 		GLevel->TickWorld(host_frametime);
887 		GLevel->TickWorld(host_frametime);
888 
889 		//	Start open scripts.
890 		GLevel->Acs->StartTypedACScripts(SCRIPT_Open, 0, 0, 0, NULL, false,
891 			false);
892 	}
893 
894 	GCon->Log(NAME_Dev, "Server spawned");
895 	unguard;
896 }
897 
898 //==========================================================================
899 //
900 //	COMMAND PreSpawn
901 //
902 //==========================================================================
903 
COMMAND(PreSpawn)904 COMMAND(PreSpawn)
905 {
906 	guard(COMMAND PreSpawn);
907 	if (Source == SRC_Command)
908 	{
909 		GCon->Log("PreSpawn is not valid from console");
910 		return;
911 	}
912 
913 	//	Make sure level info is spawned on client side, since there
914 	// could be some RPCs that depend on it.
915 	VThinkerChannel* Chan = Player->Net->ThinkerChannels.FindPtr(GLevelInfo);
916 	if (!Chan)
917 	{
918 		Chan = (VThinkerChannel*)Player->Net->CreateChannel(CHANNEL_Thinker,
919 			-1);
920 		if (Chan)
921 		{
922 			Chan->SetThinker(GLevelInfo);
923 			Chan->Update();
924 		}
925 	}
926 	unguard;
927 }
928 
929 //==========================================================================
930 //
931 //	COMMAND Spawn
932 //
933 //==========================================================================
934 
COMMAND(Spawn)935 COMMAND(Spawn)
936 {
937 	guard(COMMAND Spawn);
938 	if (Source == SRC_Command)
939 	{
940 		GCon->Log("Spawn is not valid from console");
941 		return;
942 	}
943 
944 	Player->SpawnClient();
945 	unguard;
946 }
947 
948 //==========================================================================
949 //
950 //	SV_DropClient
951 //
952 //==========================================================================
953 
SV_DropClient(VBasePlayer * Player,bool)954 void SV_DropClient(VBasePlayer* Player, bool)
955 {
956 	guard(SV_DropClient);
957 	if (GLevel && GLevel->Acs)
958 	{
959 		GLevel->Acs->StartTypedACScripts(SCRIPT_Disconnect,
960 			SV_GetPlayerNum(Player), 0, 0, NULL, true, false);
961 	}
962 	if (Player->PlayerFlags & VBasePlayer::PF_Spawned)
963 	{
964 		Player->eventDisconnectClient();
965 	}
966 	Player->PlayerFlags &= ~VBasePlayer::PF_Active;
967 	GGameInfo->Players[SV_GetPlayerNum(Player)] = NULL;
968 	Player->PlayerFlags &= ~VBasePlayer::PF_Spawned;
969 
970 	Player->PlayerReplicationInfo->DestroyThinker();
971 
972 	delete Player->Net;
973 	Player->Net = NULL;
974 
975 	svs.num_connected--;
976 	Player->UserInfo = VStr();
977 	unguard;
978 }
979 
980 //==========================================================================
981 //
982 //	SV_ShutdownGame
983 //
984 //	This only happens at the end of a game, not between levels
985 //	This is also called on Host_Error, so it shouldn't cause any errors
986 //
987 //==========================================================================
988 
SV_ShutdownGame()989 void SV_ShutdownGame()
990 {
991 	guard(SV_ShutdownGame);
992 	if (GGameInfo->NetMode == NM_None)
993 	{
994 		return;
995 	}
996 
997 #ifdef CLIENT
998 	if (GGameInfo->Flags & VGameInfo::GIF_Paused)
999 	{
1000 		GGameInfo->Flags &= ~VGameInfo::GIF_Paused;
1001 		GAudio->ResumeSound();
1002 	}
1003 
1004 	// stop sounds (especially looping!)
1005 	GAudio->StopAllSound();
1006 
1007 	if (cls.demorecording)
1008 	{
1009 		CL_StopRecording();
1010 	}
1011 #endif
1012 
1013 	if (GGameInfo->NetMode == NM_Client)
1014 	{
1015 #ifdef CLIENT
1016 		if (cls.demoplayback)
1017 		{
1018 			GClGame->eventDemoPlaybackStopped();
1019 		}
1020 
1021 		//	Sends a disconnect message to the server
1022 		if (!cls.demoplayback)
1023 		{
1024 			GCon->Log(NAME_Dev, "Sending clc_disconnect");
1025 			cl->Net->Channels[0]->Close();
1026 			cl->Net->Flush();
1027 		}
1028 
1029 		delete cl->Net;
1030 		cl->Net = NULL;
1031 		cl->ConditionalDestroy();
1032 
1033 		if (GClLevel)
1034 		{
1035 			delete GClLevel;
1036 			GClLevel = NULL;
1037 		}
1038 #endif
1039 	}
1040 	else
1041 	{
1042 		sv_loading = false;
1043 		sv_map_travel = false;
1044 
1045 		// make sure all the clients know we're disconnecting
1046 		int count = NET_SendToAll(5);
1047 		if (count)
1048 		{
1049 			GCon->Logf("Shutdown server failed for %d clients", count);
1050 		}
1051 
1052 		for (int i = 0; i < svs.max_clients; i++)
1053 		{
1054 			if (GGameInfo->Players[i])
1055 			{
1056 				SV_DropClient(GGameInfo->Players[i], false);
1057 			}
1058 		}
1059 
1060 		//
1061 		// clear structures
1062 		//
1063 		if (GLevel)
1064 		{
1065 			delete GLevel;
1066 			GLevel = NULL;
1067 		}
1068 		if (GGameInfo->WorldInfo)
1069 		{
1070 			delete GGameInfo->WorldInfo;
1071 			GGameInfo->WorldInfo = NULL;
1072 		}
1073 		for (int i = 0; i < MAXPLAYERS; i++)
1074 		{
1075 			//	Save net pointer
1076 			VNetConnection* OldNet = GPlayersBase[i]->Net;
1077 			GPlayersBase[i]->GetClass()->DestructObject(GPlayersBase[i]);
1078 			memset((vuint8*)GPlayersBase[i] + sizeof(VObject), 0,
1079 				GPlayersBase[i]->GetClass()->ClassSize - sizeof(VObject));
1080 			//	Restore pointer
1081 			GPlayersBase[i]->Net = OldNet;
1082 		}
1083 		memset(GGameInfo->Players, 0, sizeof(GGameInfo->Players));
1084 		memset(&sv, 0, sizeof(sv));
1085 
1086 		//	Tell master server that this server is gone.
1087 		if (GGameInfo->NetMode >= NM_DedicatedServer)
1088 		{
1089 			GNet->QuitMaster();
1090 			LastMasterUpdate = 0;
1091 		}
1092 	}
1093 
1094 #ifdef CLIENT
1095 	GClLevel = NULL;
1096 	cl = NULL;
1097 	cls.demoplayback = false;
1098 	cls.signon = 0;
1099 
1100 	if (GGameInfo->NetMode != NM_DedicatedServer)
1101 	{
1102 		GClGame->eventDisconnected();
1103 	}
1104 #endif
1105 
1106 	SV_InitBaseSlot();
1107 
1108 	GGameInfo->NetMode = NM_None;
1109 	unguard;
1110 }
1111 
1112 #ifdef CLIENT
1113 
1114 //==========================================================================
1115 //
1116 //	G_DoSingleReborn
1117 //
1118 //	Called by G_Ticker based on gameaction.  Loads a game from the reborn
1119 // save slot.
1120 //
1121 //==========================================================================
1122 
COMMAND(Restart)1123 COMMAND(Restart)
1124 {
1125 	guard(COMMAND Restart);
1126 	if (GGameInfo->NetMode != NM_Standalone)
1127 	{
1128 		return;
1129 	}
1130 
1131 	if (SV_RebornSlotAvailable())
1132 	{
1133 		// Use the reborn code if the slot is available
1134 		SV_LoadGame(SV_GetRebornSlot());
1135 	}
1136 	else
1137 	{
1138 		// reload the level from scratch
1139 		SV_SpawnServer(*GLevel->MapName, true, false);
1140 	}
1141 	unguard;
1142 }
1143 
1144 #endif
1145 
1146 //==========================================================================
1147 //
1148 //	COMMAND Pause
1149 //
1150 //==========================================================================
1151 
COMMAND(Pause)1152 COMMAND(Pause)
1153 {
1154 	guard(COMMAND Pause);
1155 	if (Source == SRC_Command)
1156 	{
1157 		ForwardToServer();
1158 		return;
1159 	}
1160 
1161 	GGameInfo->Flags ^= VGameInfo::GIF_Paused;
1162 	for (int i = 0; i < svs.max_clients; i++)
1163 	{
1164 		if (GGameInfo->Players[i])
1165 		{
1166 			GGameInfo->Players[i]->eventClientPause(
1167 				!!(GGameInfo->Flags & VGameInfo::GIF_Paused));
1168 		}
1169 	}
1170 	unguard;
1171 }
1172 
1173 //==========================================================================
1174 //
1175 //  Stats_f
1176 //
1177 //==========================================================================
1178 
COMMAND(Stats)1179 COMMAND(Stats)
1180 {
1181 	guard(COMMAND Stats);
1182 	if (Source == SRC_Command)
1183 	{
1184 		ForwardToServer();
1185 		return;
1186 	}
1187 
1188 	Player->Printf("Kills: %d of %d", Player->KillCount, GLevelInfo->TotalKills);
1189 	Player->Printf("Items: %d of %d", Player->ItemCount, GLevelInfo->TotalItems);
1190 	Player->Printf("Secrets: %d of %d", Player->SecretCount, GLevelInfo->TotalSecret);
1191 	unguard;
1192 }
1193 
1194 //==========================================================================
1195 //
1196 //	SV_ConnectClient
1197 //
1198 //	Initialises a client_t for a new net connection.  This will only be
1199 // called once for a player each game, not once for each level change.
1200 //
1201 //==========================================================================
1202 
SV_ConnectClient(VBasePlayer * player)1203 void SV_ConnectClient(VBasePlayer *player)
1204 {
1205 	guard(SV_ConnectClient);
1206 	if (player->Net)
1207 	{
1208 		GCon->Logf(NAME_Dev, "Client %s connected", *player->Net->GetAddress());
1209 
1210 		ServerNetContext->ClientConnections.Append(player->Net);
1211 		player->Net->NeedsUpdate = false;
1212 	}
1213 
1214 	GGameInfo->Players[SV_GetPlayerNum(player)] = player;
1215 	player->ClientNum = SV_GetPlayerNum(player);
1216 	player->PlayerFlags |= VBasePlayer::PF_Active;
1217 
1218 	player->PlayerFlags &= ~VBasePlayer::PF_Spawned;
1219 	player->Level = GLevelInfo;
1220 	if (!sv_loading)
1221 	{
1222 		player->MO = NULL;
1223 		player->PlayerState = PST_REBORN;
1224 		player->eventPutClientIntoServer();
1225 	}
1226 	player->Frags = 0;
1227 	player->Deaths = 0;
1228 
1229 	player->PlayerReplicationInfo =
1230 		(VPlayerReplicationInfo*)GLevel->SpawnThinker(GGameInfo->PlayerReplicationInfoClass);
1231 	player->PlayerReplicationInfo->Player = player;
1232 	player->PlayerReplicationInfo->PlayerNum = SV_GetPlayerNum(player);
1233 	unguard;
1234 }
1235 
1236 //==========================================================================
1237 //
1238 //	SV_CheckForNewClients
1239 //
1240 //==========================================================================
1241 
SV_CheckForNewClients()1242 void SV_CheckForNewClients()
1243 {
1244 	guard(SV_CheckForNewClients);
1245 	VSocketPublic*	sock;
1246 	int				i;
1247 
1248 	//
1249 	// check for new connections
1250 	//
1251 	while (1)
1252 	{
1253 		sock = GNet->CheckNewConnections();
1254 		if (!sock)
1255 		{
1256 			break;
1257 		}
1258 
1259 		//
1260 		// init a new client structure
1261 		//
1262 		for (i = 0; i < svs.max_clients; i++)
1263 		{
1264 			if (!GGameInfo->Players[i])
1265 			{
1266 				break;
1267 			}
1268 		}
1269 		if (i == svs.max_clients)
1270 		{
1271 			Sys_Error("Host_CheckForNewClients: no free clients");
1272 		}
1273 
1274 		VBasePlayer* Player = GPlayersBase[i];
1275 		Player->Net = new VNetConnection(sock, ServerNetContext, Player);
1276 		Player->Net->ObjMap->SetUpClassLookup();
1277 		((VPlayerChannel*)Player->Net->Channels[CHANIDX_Player])->SetPlayer(Player);
1278 		Player->Net->CreateChannel(CHANNEL_ObjectMap, -1);
1279 		SV_ConnectClient(Player);
1280 		svs.num_connected++;
1281 	}
1282 	unguard;
1283 }
1284 
1285 //==========================================================================
1286 //
1287 //	SV_ConnectBot
1288 //
1289 //==========================================================================
1290 
SV_ConnectBot(const char * name)1291 void SV_ConnectBot(const char *name)
1292 {
1293 	guard(SV_ConnectBot);
1294 	int				i;
1295 
1296 	if (GGameInfo->NetMode == NM_None || GGameInfo->NetMode == NM_Client)
1297 	{
1298 		GCon->Log("Game is not running");
1299 		return;
1300 	}
1301 
1302 	if (svs.num_connected >= svs.max_clients)
1303 	{
1304 		GCon->Log("Server is full");
1305 		return;
1306 	}
1307 
1308 	//
1309 	// init a new client structure
1310 	//
1311 	for (i = 0; i < svs.max_clients; i++)
1312 		if (!GGameInfo->Players[i])
1313 			break;
1314 	if (i == svs.max_clients)
1315 		Sys_Error("SV_ConnectBot: no free clients");
1316 
1317 	VBasePlayer* Player = GPlayersBase[i];
1318 	Player->PlayerFlags |= VBasePlayer::PF_IsBot;
1319 	Player->PlayerName = name;
1320 	SV_ConnectClient(Player);
1321 	svs.num_connected++;
1322 	Player->SetUserInfo(Player->UserInfo);
1323 	Player->SpawnClient();
1324 	unguard;
1325 }
1326 
1327 //==========================================================================
1328 //
1329 //	COMMAND AddBot
1330 //
1331 //==========================================================================
1332 
COMMAND(AddBot)1333 COMMAND(AddBot)
1334 {
1335 	SV_ConnectBot(Args.Num() > 1 ? *Args[1] : "");
1336 }
1337 
1338 //==========================================================================
1339 //
1340 //  Map
1341 //
1342 //==========================================================================
1343 
COMMAND(Map)1344 COMMAND(Map)
1345 {
1346 	guard(COMMAND Map);
1347 	VStr	mapname;
1348 
1349 	if (Args.Num() != 2)
1350 	{
1351 		GCon->Log("map <mapname> : change level");
1352 		return;
1353 	}
1354 	mapname = Args[1];
1355 
1356 	SV_ShutdownGame();
1357 
1358 	// Default the player start spot group to 0
1359 	RebornPosition = 0;
1360 	GGameInfo->RebornPosition = RebornPosition;
1361 
1362 	if ((int)Skill < 0)
1363 	{
1364 		Skill = 0;
1365 	}
1366 	else if ((int)Skill >= P_GetNumSkills())
1367 	{
1368 		Skill = P_GetNumSkills() - 1;
1369 	}
1370 
1371 	SV_SpawnServer(*mapname, true, false);
1372 #ifdef CLIENT
1373 	if (GGameInfo->NetMode != NM_DedicatedServer)
1374 	{
1375 		CL_SetUpLocalPlayer();
1376 	}
1377 #endif
1378 	unguard;
1379 }
1380 
1381 //==========================================================================
1382 //
1383 //	Host_StartTitleMap
1384 //
1385 //==========================================================================
1386 
Host_StartTitleMap()1387 bool Host_StartTitleMap()
1388 {
1389 	guard(Host_StartTitleMap);
1390 	if (!FL_FileExists("maps/titlemap.wad") &&
1391 		W_CheckNumForName(NAME_titlemap) < 0)
1392 	{
1393 		return false;
1394 	}
1395 
1396 	// Default the player start spot group to 0
1397 	RebornPosition = 0;
1398 	GGameInfo->RebornPosition = RebornPosition;
1399 
1400 	SV_SpawnServer("titlemap", true, true);
1401 #ifdef CLIENT
1402 	CL_SetUpLocalPlayer();
1403 #endif
1404 	return true;
1405 	unguard;
1406 }
1407 
1408 //==========================================================================
1409 //
1410 //	COMMAND MaxPlayers
1411 //
1412 //==========================================================================
1413 
COMMAND(MaxPlayers)1414 COMMAND(MaxPlayers)
1415 {
1416 	guard(COMMAND MaxPlayers);
1417 	int 	n;
1418 
1419 	if (Args.Num() != 2)
1420 	{
1421 		GCon->Logf("maxplayers is %d", svs.max_clients);
1422 		return;
1423 	}
1424 
1425 	if (GGameInfo->NetMode != NM_None && GGameInfo->NetMode != NM_Client)
1426 	{
1427 		GCon->Log("maxplayers can not be changed while a server is running.");
1428 		return;
1429 	}
1430 
1431 	n = atoi(*Args[1]);
1432 	if (n < 1)
1433 		n = 1;
1434 	if (n > MAXPLAYERS)
1435 	{
1436 		n = MAXPLAYERS;
1437 		GCon->Logf("maxplayers set to %d", n);
1438 	}
1439 	svs.max_clients = n;
1440 
1441 	if (n == 1)
1442 	{
1443 #ifdef CLIENT
1444 		GCmdBuf << "listen 0\n";
1445 #endif
1446 		DeathMatch = 0;
1447 		NoMonsters = 0;
1448 	}
1449 	else
1450 	{
1451 #ifdef CLIENT
1452 		GCmdBuf << "listen 1\n";
1453 #endif
1454 		DeathMatch = 2;
1455 		NoMonsters = 1;
1456 	}
1457 	unguard;
1458 }
1459 
1460 //==========================================================================
1461 //
1462 //	ServerFrame
1463 //
1464 //==========================================================================
1465 
ServerFrame(int realtics)1466 void ServerFrame(int realtics)
1467 {
1468 	guard(ServerFrame);
1469 	SV_CheckForNewClients();
1470 
1471 	if (real_time)
1472 	{
1473 		SV_Ticker();
1474 	}
1475 	else
1476 	{
1477 		// run the count dics
1478 		while (realtics--)
1479 		{
1480 			SV_Ticker();
1481 		}
1482 	}
1483 
1484 	if (mapteleport_issued)
1485 	{
1486 		SV_MapTeleport(GLevelInfo->NextMap);
1487 	}
1488 
1489 	SV_SendClientMessages();
1490 	unguard;
1491 }
1492 
1493 //==========================================================================
1494 //
1495 //	SV_FindClassFromEditorId
1496 //
1497 //==========================================================================
1498 
SV_FindClassFromEditorId(int Id,int GameFilter)1499 VClass* SV_FindClassFromEditorId(int Id, int GameFilter)
1500 {
1501 	guard(SV_FindClassFromEditorId);
1502 	for (int i = VClass::GMobjInfos.Num() - 1; i >= 0; i--)
1503 	{
1504 		if ((!VClass::GMobjInfos[i].GameFilter ||
1505 			(VClass::GMobjInfos[i].GameFilter & GameFilter)) &&
1506 			Id == VClass::GMobjInfos[i].DoomEdNum)
1507 		{
1508 			return VClass::GMobjInfos[i].Class;
1509 		}
1510 	}
1511 	return NULL;
1512 	unguard;
1513 }
1514 
1515 //==========================================================================
1516 //
1517 //	SV_FindClassFromScriptId
1518 //
1519 //==========================================================================
1520 
SV_FindClassFromScriptId(int Id,int GameFilter)1521 VClass* SV_FindClassFromScriptId(int Id, int GameFilter)
1522 {
1523 	guard(SV_FindClassFromScriptId);
1524 	for (int i = VClass::GScriptIds.Num() - 1; i >= 0; i--)
1525 	{
1526 		if ((!VClass::GScriptIds[i].GameFilter ||
1527 			(VClass::GScriptIds[i].GameFilter & GameFilter)) &&
1528 			Id == VClass::GScriptIds[i].DoomEdNum)
1529 		{
1530 			return VClass::GScriptIds[i].Class;
1531 		}
1532 	}
1533 	return NULL;
1534 	unguard;
1535 }
1536 
1537 //==========================================================================
1538 //
1539 //	COMMAND Say
1540 //
1541 //==========================================================================
1542 
COMMAND(Say)1543 COMMAND(Say)
1544 {
1545 	guard(COMMAND Say);
1546 	if (Source == SRC_Command)
1547 	{
1548 		ForwardToServer();
1549 		return;
1550 	}
1551 	if (Args.Num() < 2)
1552 		return;
1553 
1554 	VStr Text = Player->PlayerName;
1555 	Text += ":";
1556 	for (int i = 1; i < Args.Num(); i++)
1557 	{
1558 		Text += " ";
1559 		Text += Args[i];
1560 	}
1561 	GLevelInfo->BroadcastPrint(*Text);
1562 	GLevelInfo->StartSound(TVec(0, 0, 0), 0,
1563 		GSoundManager->GetSoundID("misc/chat"), 0, 1.0, 0, false);
1564 	unguard;
1565 }
1566 
1567 //==========================================================================
1568 //
1569 //	VServerNetContext::GetLevel
1570 //
1571 //==========================================================================
1572 
GetLevel()1573 VLevel* VServerNetContext::GetLevel()
1574 {
1575 	return GLevel;
1576 }
1577 
1578 //**************************************************************************
1579 //
1580 //	Dedicated server console streams
1581 //
1582 //**************************************************************************
1583 
1584 #ifndef CLIENT
1585 
1586 class FConsoleDevice : public FOutputDevice
1587 {
1588 public:
Serialise(const char * V,EName)1589 	void Serialise(const char* V, EName)
1590 	{
1591 		printf("%s\n", V);
1592 	}
1593 };
1594 
1595 FConsoleDevice			Console;
1596 
1597 FOutputDevice			*GCon = &Console;
1598 
1599 #endif
1600