about summary refs log tree commit diff
diff options
context:
space:
mode:
authoroy <Tom_Adams@web.de>2011-03-04 17:08:10 +0100
committeroy <Tom_Adams@web.de>2011-03-04 17:08:10 +0100
commit9811c2396baa86e3401caef295649eaf3e12a4a5 (patch)
tree15232e5f180a844e8702f6ef99cd97e9e55a6365
parent4bede550be18cfc9bb4df669c147032917160066 (diff)
downloadzcatch-9811c2396baa86e3401caef295649eaf3e12a4a5.tar.gz
zcatch-9811c2396baa86e3401caef295649eaf3e12a4a5.zip
made network clip flags. Closes #131
-rw-r--r--datasrc/network.py31
-rw-r--r--src/game/client/components/chat.cpp2
-rw-r--r--src/game/client/components/controls.cpp4
-rw-r--r--src/game/client/components/hud.cpp47
-rw-r--r--src/game/client/components/items.cpp29
-rw-r--r--src/game/client/components/items.h2
-rw-r--r--src/game/client/components/killmessages.cpp4
-rw-r--r--src/game/client/components/menus_ingame.cpp43
-rw-r--r--src/game/client/components/nameplates.cpp2
-rw-r--r--src/game/client/components/players.cpp12
-rw-r--r--src/game/client/components/scoreboard.cpp79
-rw-r--r--src/game/client/gameclient.cpp25
-rw-r--r--src/game/client/gameclient.h4
-rw-r--r--src/game/server/entities/flag.cpp9
-rw-r--r--src/game/server/gamecontroller.cpp47
-rw-r--r--src/game/server/gamemodes/ctf.cpp25
-rw-r--r--src/game/server/gamemodes/ctf.h1
-rw-r--r--src/game/server/gamemodes/tdm.cpp15
-rw-r--r--src/game/server/gamemodes/tdm.h1
19 files changed, 226 insertions, 156 deletions
diff --git a/datasrc/network.py b/datasrc/network.py
index 2489b131..3b1afa77 100644
--- a/datasrc/network.py
+++ b/datasrc/network.py
@@ -3,6 +3,7 @@ from datatypes import *
 Emotes = ["NORMAL", "PAIN", "HAPPY", "SURPRISE", "ANGRY", "BLINK"]
 PlayerFlags = ["PLAYING", "IN_MENU", "CHATTING", "SCOREBOARD"]
 GameFlags = ["TEAMS", "FLAGS"]
+GameStateFlags = ["GAMEOVER", "SUDDENDEATH", "PAUSED"]
 
 Emoticons = [str(x) for x in range(0,16)]
 
@@ -21,7 +22,10 @@ enum
 {
 	TEAM_SPECTATORS=-1,
 	TEAM_RED,
-	TEAM_BLUE
+	TEAM_BLUE,
+
+	FLAG_ATSTAND=-2,
+	FLAG_TAKEN,
 };
 '''
 
@@ -37,8 +41,9 @@ Enums = [
 ]
 
 Flags = [
-	Enum("PLAYERFLAG", PlayerFlags),
-	Flags("GAMEFLAG", GameFlags)
+	Flags("PLAYERFLAG", PlayerFlags),
+	Flags("GAMEFLAG", GameFlags),
+	Flags("GAMESTATEFLAG", GameStateFlags)
 ]
 
 Objects = [
@@ -90,28 +95,28 @@ Objects = [
 		NetIntAny("m_X"),
 		NetIntAny("m_Y"),
 		
-		NetIntRange("m_Team", 'TEAM_RED', 'TEAM_BLUE'),
-		NetIntRange("m_CarriedBy", -2, 'MAX_CLIENTS-1')
+		NetIntRange("m_Team", 'TEAM_RED', 'TEAM_BLUE')
 	]),
 
-	NetObject("Game", [
-		NetIntRange("m_Flags", 0, 256),
+	NetObject("GameInfo", [
+		NetIntRange("m_GameFlags", 0, 256),
+		NetIntRange("m_GameStateFlags", 0, 256),
 		NetTick("m_RoundStartTick"),
-		
-		NetIntRange("m_GameOver", 0, 1),
-		NetIntRange("m_SuddenDeath", 0, 1),
-		NetIntRange("m_Paused", 0, 1),
+		NetIntRange("m_WarmupTimer", 0, 'max_int'),
 		
 		NetIntRange("m_ScoreLimit", 0, 'max_int'),
 		NetIntRange("m_TimeLimit", 0, 'max_int'),
 		
-		NetIntRange("m_Warmup", 0, 'max_int'),
-		
 		NetIntRange("m_RoundNum", 0, 'max_int'),
 		NetIntRange("m_RoundCurrent", 0, 'max_int'),
+	]),
 
+	NetObject("GameData", [
 		NetIntAny("m_TeamscoreRed"),
 		NetIntAny("m_TeamscoreBlue"),
+
+		NetIntRange("m_FlagCarrierRed", 'FLAG_ATSTAND', 'MAX_CLIENTS-1'),
+		NetIntRange("m_FlagCarrierBlue", 'FLAG_ATSTAND', 'MAX_CLIENTS-1'),
 	]),
 
 	NetObject("CharacterCore", [
diff --git a/src/game/client/components/chat.cpp b/src/game/client/components/chat.cpp
index 79b9720e..e90e8579 100644
--- a/src/game/client/components/chat.cpp
+++ b/src/game/client/components/chat.cpp
@@ -235,7 +235,7 @@ void CChat::AddLine(int ClientID, int Team, const char *pLine)
 			if(m_pClient->m_aClients[ClientID].m_Team == TEAM_SPECTATORS)
 				m_aLines[m_CurrentLine].m_NameColor = TEAM_SPECTATORS;
 
-			if(m_pClient->m_Snap.m_pGameobj && m_pClient->m_Snap.m_pGameobj->m_Flags&GAMEFLAG_TEAMS)
+			if(m_pClient->m_Snap.m_pGameInfoObj && m_pClient->m_Snap.m_pGameInfoObj->m_GameFlags&GAMEFLAG_TEAMS)
 			{
 				if(m_pClient->m_aClients[ClientID].m_Team == TEAM_RED)
 					m_aLines[m_CurrentLine].m_NameColor = TEAM_RED;
diff --git a/src/game/client/components/controls.cpp b/src/game/client/components/controls.cpp
index 479bba22..df9a6bf0 100644
--- a/src/game/client/components/controls.cpp
+++ b/src/game/client/components/controls.cpp
@@ -199,13 +199,13 @@ int CControls::SnapInput(int *pData)
 void CControls::OnRender()
 {
 	// update target pos
-	if(m_pClient->m_Snap.m_pGameobj && !(m_pClient->m_Snap.m_pGameobj->m_Paused || m_pClient->m_Snap.m_Spectate))
+	if(m_pClient->m_Snap.m_pGameInfoObj && !(m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_PAUSED || m_pClient->m_Snap.m_Spectate))
 		m_TargetPos = m_pClient->m_LocalCharacterPos + m_MousePos;
 }
 
 bool CControls::OnMouseMove(float x, float y)
 {
-	if((m_pClient->m_Snap.m_pGameobj && m_pClient->m_Snap.m_pGameobj->m_Paused) || (m_pClient->m_Snap.m_Spectate && m_pClient->m_pChat->IsActive()))
+	if((m_pClient->m_Snap.m_pGameInfoObj && m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_PAUSED) || (m_pClient->m_Snap.m_Spectate && m_pClient->m_pChat->IsActive()))
 		return false;
 	m_MousePos += vec2(x, y); // TODO: ugly
 
diff --git a/src/game/client/components/hud.cpp b/src/game/client/components/hud.cpp
index b87cf650..ca100f9d 100644
--- a/src/game/client/components/hud.cpp
+++ b/src/game/client/components/hud.cpp
@@ -32,25 +32,25 @@ void CHud::RenderGameTimer()
 	float Half = 300.0f*Graphics()->ScreenAspect()/2.0f;
 	Graphics()->MapScreen(0, 0, 300.0f*Graphics()->ScreenAspect(), 300.0f);
 	
-	if(!m_pClient->m_Snap.m_pGameobj->m_SuddenDeath)
+	if(!(m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_SUDDENDEATH))
 	{
 		char Buf[32];
 		int Time = 0;
-		if(m_pClient->m_Snap.m_pGameobj->m_TimeLimit)
+		if(m_pClient->m_Snap.m_pGameInfoObj->m_TimeLimit)
 		{
-			Time = m_pClient->m_Snap.m_pGameobj->m_TimeLimit*60 - ((Client()->GameTick()-m_pClient->m_Snap.m_pGameobj->m_RoundStartTick)/Client()->GameTickSpeed());
+			Time = m_pClient->m_Snap.m_pGameInfoObj->m_TimeLimit*60 - ((Client()->GameTick()-m_pClient->m_Snap.m_pGameInfoObj->m_RoundStartTick)/Client()->GameTickSpeed());
 
-			if(m_pClient->m_Snap.m_pGameobj->m_GameOver)
+			if(m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_GAMEOVER)
 				Time  = 0;
 		}
 		else
-			Time = (Client()->GameTick()-m_pClient->m_Snap.m_pGameobj->m_RoundStartTick)/Client()->GameTickSpeed();
+			Time = (Client()->GameTick()-m_pClient->m_Snap.m_pGameInfoObj->m_RoundStartTick)/Client()->GameTickSpeed();
 
 		str_format(Buf, sizeof(Buf), "%d:%02d", Time/60, Time%60);
 		float FontSize = 10.0f;
 		float w = TextRender()->TextWidth(0, FontSize, Buf, -1);
 		// last 60 sec red, last 10 sec blink
-		if(m_pClient->m_Snap.m_pGameobj->m_TimeLimit && Time <= 60)
+		if(m_pClient->m_Snap.m_pGameInfoObj->m_TimeLimit && Time <= 60)
 		{
 			float Alpha = Time <= 10 && (2*time_get()/time_freq()) % 2 ? 0.5f : 1.0f;
 			TextRender()->TextColor(1.0f, 0.25f, 0.25f, Alpha);
@@ -62,7 +62,7 @@ void CHud::RenderGameTimer()
 
 void CHud::RenderSuddenDeath()
 {
-	if(m_pClient->m_Snap.m_pGameobj->m_SuddenDeath)
+	if(m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_SUDDENDEATH)
 	{
 		float Half = 300.0f*Graphics()->ScreenAspect()/2.0f;
 		const char *pText = Localize("Sudden Death");
@@ -75,17 +75,18 @@ void CHud::RenderSuddenDeath()
 void CHud::RenderScoreHud()
 {	
 	// render small score hud
-	if(!(m_pClient->m_Snap.m_pGameobj && m_pClient->m_Snap.m_pGameobj->m_GameOver))
+	if(!(m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_GAMEOVER))
 	{
-		int GameFlags = m_pClient->m_Snap.m_pGameobj->m_Flags;
+		int GameFlags = m_pClient->m_Snap.m_pGameInfoObj->m_GameFlags;
 		float Whole = 300*Graphics()->ScreenAspect();
 
-		if(GameFlags&GAMEFLAG_TEAMS)
+		if(GameFlags&GAMEFLAG_TEAMS && m_pClient->m_Snap.m_pGameDataObj)
 		{
 			char aScoreTeam[2][32];
-			str_format(aScoreTeam[TEAM_RED], sizeof(aScoreTeam)/2, "%d", m_pClient->m_Snap.m_pGameobj->m_TeamscoreRed);
-			str_format(aScoreTeam[TEAM_BLUE], sizeof(aScoreTeam)/2, "%d", m_pClient->m_Snap.m_pGameobj->m_TeamscoreBlue);
-			float aScoreTeamWidth[2] = {TextRender()->TextWidth(0, 14.0f, aScoreTeam[TEAM_RED], -1), TextRender()->TextWidth(0, 14.0f, aScoreTeam[TEAM_BLUE], -1)};
+			str_format(aScoreTeam[TEAM_RED], sizeof(aScoreTeam)/2, "%d", m_pClient->m_Snap.m_pGameDataObj->m_TeamscoreRed);
+			str_format(aScoreTeam[TEAM_BLUE], sizeof(aScoreTeam)/2, "%d", m_pClient->m_Snap.m_pGameDataObj->m_TeamscoreBlue);
+			float aScoreTeamWidth[2] = { TextRender()->TextWidth(0, 14.0f, aScoreTeam[TEAM_RED], -1), TextRender()->TextWidth(0, 14.0f, aScoreTeam[TEAM_BLUE], -1) };
+			int FlagCarrier[2] = { m_pClient->m_Snap.m_pGameDataObj->m_FlagCarrierRed, m_pClient->m_Snap.m_pGameDataObj->m_FlagCarrierBlue };
 			float ScoreWidthMax = max(max(aScoreTeamWidth[TEAM_RED], aScoreTeamWidth[TEAM_BLUE]), TextRender()->TextWidth(0, 14.0f, "100", -1));
 			float Split = 3.0f;
 			float ImageSize = GameFlags&GAMEFLAG_FLAGS ? 16.0f : Split;
@@ -106,9 +107,9 @@ void CHud::RenderScoreHud()
 				// draw score
 				TextRender()->Text(0, Whole-ScoreWidthMax+(ScoreWidthMax-aScoreTeamWidth[t])/2-Split, 245.0f+t*20, 14.0f, aScoreTeam[t], -1);
 
-				if(GameFlags&GAMEFLAG_FLAGS && m_pClient->m_Snap.m_paFlags[t])
+				if(GameFlags&GAMEFLAG_FLAGS)
 				{
-					if(m_pClient->m_Snap.m_paFlags[t]->m_CarriedBy == -2 || (m_pClient->m_Snap.m_paFlags[t]->m_CarriedBy == -1 && ((Client()->GameTick()/10)&1)))
+					if(FlagCarrier[t] == FLAG_ATSTAND || (FlagCarrier[t] == FLAG_TAKEN && ((Client()->GameTick()/10)&1)))
 					{
 						// draw flag
 						Graphics()->BlendNormal();
@@ -119,10 +120,10 @@ void CHud::RenderScoreHud()
 						Graphics()->QuadsDrawTL(&QuadItem, 1);
 						Graphics()->QuadsEnd();
 					}
-					else if(m_pClient->m_Snap.m_paFlags[t]->m_CarriedBy >= 0)
+					else if(FlagCarrier[t] >= 0)
 					{
 						// draw name of the flag holder
-						int ID = m_pClient->m_Snap.m_paFlags[t]->m_CarriedBy%MAX_CLIENTS;
+						int ID = FlagCarrier[t]%MAX_CLIENTS;
 						const char *pName = m_pClient->m_aClients[ID].m_aName;
 						float w = TextRender()->TextWidth(0, 10.0f, pName, -1);
 						TextRender()->Text(0, Whole-ScoreWidthMax-ImageSize-3*Split-w, 247.0f+t*20, 10.0f, pName, -1);
@@ -216,16 +217,16 @@ void CHud::RenderScoreHud()
 void CHud::RenderWarmupTimer()
 {
 	// render warmup timer
-	if(m_pClient->m_Snap.m_pGameobj->m_Warmup)
+	if(m_pClient->m_Snap.m_pGameInfoObj->m_WarmupTimer)
 	{
 		char Buf[256];
 		float FontSize = 20.0f;
 		float w = TextRender()->TextWidth(0, FontSize, Localize("Warmup"), -1);
 		TextRender()->Text(0, 150*Graphics()->ScreenAspect()+-w/2, 50, FontSize, Localize("Warmup"), -1);
 
-		int Seconds = m_pClient->m_Snap.m_pGameobj->m_Warmup/SERVER_TICK_SPEED;
+		int Seconds = m_pClient->m_Snap.m_pGameInfoObj->m_WarmupTimer/SERVER_TICK_SPEED;
 		if(Seconds < 5)
-			str_format(Buf, sizeof(Buf), "%d.%d", Seconds, (m_pClient->m_Snap.m_pGameobj->m_Warmup*10/SERVER_TICK_SPEED)%10);
+			str_format(Buf, sizeof(Buf), "%d.%d", Seconds, (m_pClient->m_Snap.m_pGameInfoObj->m_WarmupTimer*10/SERVER_TICK_SPEED)%10);
 		else
 			str_format(Buf, sizeof(Buf), "%d", Seconds);
 		w = TextRender()->TextWidth(0, FontSize, Buf, -1);
@@ -268,7 +269,7 @@ void CHud::RenderTeambalanceWarning()
 {
 	// render prompt about team-balance
 	bool Flash = time_get()/(time_freq()/2)%2 == 0;
-	if (m_pClient->m_Snap.m_pGameobj && (m_pClient->m_Snap.m_pGameobj->m_Flags&GAMEFLAG_TEAMS) != 0)
+	if(m_pClient->m_Snap.m_pGameInfoObj->m_GameFlags&GAMEFLAG_TEAMS)
 	{	
 		int TeamDiff = m_pClient->m_Snap.m_aTeamSize[TEAM_RED]-m_pClient->m_Snap.m_aTeamSize[TEAM_BLUE];
 		if (g_Config.m_ClWarningTeambalance && (TeamDiff >= 2 || TeamDiff <= -2))
@@ -395,7 +396,7 @@ void CHud::RenderHealthAndAmmo()
 
 void CHud::OnRender()
 {
-	if(!m_pClient->m_Snap.m_pGameobj)
+	if(!m_pClient->m_Snap.m_pGameInfoObj)
 		return;
 		
 	m_Width = 300*Graphics()->ScreenAspect();
@@ -404,7 +405,7 @@ void CHud::OnRender()
 	if(m_pClient->m_Snap.m_pLocalInfo && m_pClient->m_Snap.m_pLocalInfo->m_Team == TEAM_SPECTATORS)
 		Spectate = true;
 	
-	if(m_pClient->m_Snap.m_pLocalCharacter && !Spectate && !(m_pClient->m_Snap.m_pGameobj && m_pClient->m_Snap.m_pGameobj->m_GameOver))
+	if(m_pClient->m_Snap.m_pLocalCharacter && !Spectate && !(m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_GAMEOVER))
 		RenderHealthAndAmmo();
 
 	RenderGameTimer();
diff --git a/src/game/client/components/items.cpp b/src/game/client/components/items.cpp
index fe14a339..526c94d6 100644
--- a/src/game/client/components/items.cpp
+++ b/src/game/client/components/items.cpp
@@ -152,7 +152,7 @@ void CItems::RenderPickup(const CNetObj_Pickup *pPrev, const CNetObj_Pickup *pCu
 	Graphics()->QuadsEnd();
 }
 
-void CItems::RenderFlag(const CNetObj_Flag *pPrev, const CNetObj_Flag *pCurrent)
+void CItems::RenderFlag(const CNetObj_Flag *pPrev, const CNetObj_Flag *pCurrent, const CNetObj_GameData *pPrevGameData, const CNetObj_GameData *pCurGameData)
 {
 	float Angle = 0.0f;
 	float Size = 42.0f;
@@ -170,13 +170,20 @@ void CItems::RenderFlag(const CNetObj_Flag *pPrev, const CNetObj_Flag *pCurrent)
 
 	vec2 Pos = mix(vec2(pPrev->m_X, pPrev->m_Y), vec2(pCurrent->m_X, pCurrent->m_Y), Client()->IntraGameTick());
 	
-	// make sure that the flag isn't interpolated between capture and return
-	if(pPrev->m_CarriedBy != pCurrent->m_CarriedBy)
-		Pos = vec2(pCurrent->m_X, pCurrent->m_Y);
-
-	// make sure to use predicted position if we are the carrier
-	if(m_pClient->m_Snap.m_pLocalInfo && pCurrent->m_CarriedBy == m_pClient->m_Snap.m_pLocalInfo->m_ClientID)
-		Pos = m_pClient->m_LocalCharacterPos;
+	if(pCurGameData)
+	{
+		// make sure that the flag isn't interpolated between capture and return
+		if(pPrevGameData &&
+			((pCurrent->m_Team == TEAM_RED && pPrevGameData->m_FlagCarrierRed != pCurGameData->m_FlagCarrierRed) ||
+			(pCurrent->m_Team == TEAM_BLUE && pPrevGameData->m_FlagCarrierBlue != pCurGameData->m_FlagCarrierBlue)))
+			Pos = vec2(pCurrent->m_X, pCurrent->m_Y);
+
+		// make sure to use predicted position if we are the carrier
+		if(m_pClient->m_Snap.m_pLocalInfo &&
+			((pCurrent->m_Team == TEAM_RED && pCurGameData->m_FlagCarrierRed == m_pClient->m_Snap.m_LocalClientID) ||
+			(pCurrent->m_Team == TEAM_BLUE && pCurGameData->m_FlagCarrierBlue == m_pClient->m_Snap.m_LocalClientID)))
+			Pos = m_pClient->m_LocalCharacterPos;
+	}
 
 	IGraphics::CQuadItem QuadItem(Pos.x, Pos.y-Size*0.75f, Size, Size*2);
 	Graphics()->QuadsDraw(&QuadItem, 1);
@@ -286,7 +293,11 @@ void CItems::OnRender()
 		{
 			const void *pPrev = Client()->SnapFindItem(IClient::SNAP_PREV, Item.m_Type, Item.m_ID);
 			if (pPrev)
-				RenderFlag((const CNetObj_Flag *)pPrev, (const CNetObj_Flag *)pData);
+			{
+				const void *pPrevGameData =  Client()->SnapFindItem(IClient::SNAP_PREV, NETOBJTYPE_GAMEDATA, m_pClient->m_Snap.m_GameDataSnapID);
+				RenderFlag(static_cast<const CNetObj_Flag *>(pPrev), static_cast<const CNetObj_Flag *>(pData),
+							static_cast<const CNetObj_GameData *>(pPrevGameData), m_pClient->m_Snap.m_pGameDataObj);
+			}
 		}
 	}
 
diff --git a/src/game/client/components/items.h b/src/game/client/components/items.h
index 2fd188eb..17702394 100644
--- a/src/game/client/components/items.h
+++ b/src/game/client/components/items.h
@@ -8,7 +8,7 @@ class CItems : public CComponent
 {	
 	void RenderProjectile(const CNetObj_Projectile *pCurrent, int ItemID);
 	void RenderPickup(const CNetObj_Pickup *pPrev, const CNetObj_Pickup *pCurrent);
-	void RenderFlag(const CNetObj_Flag *pPrev, const CNetObj_Flag *pCurrent);
+	void RenderFlag(const CNetObj_Flag *pPrev, const CNetObj_Flag *pCurrent, const CNetObj_GameData *pPrevGameData, const CNetObj_GameData *pCurGameData);
 	void RenderLaser(const struct CNetObj_Laser *pCurrent);
 	
 public:
diff --git a/src/game/client/components/killmessages.cpp b/src/game/client/components/killmessages.cpp
index d57d7fd3..7745bcb2 100644
--- a/src/game/client/components/killmessages.cpp
+++ b/src/game/client/components/killmessages.cpp
@@ -70,7 +70,7 @@ void CKillMessages::OnRender()
 		// render victim tee
 		x -= 24.0f;
 		
-		if(m_pClient->m_Snap.m_pGameobj && m_pClient->m_Snap.m_pGameobj->m_Flags&GAMEFLAG_FLAGS)
+		if(m_pClient->m_Snap.m_pGameInfoObj && m_pClient->m_Snap.m_pGameInfoObj->m_GameFlags&GAMEFLAG_FLAGS)
 		{
 			if(m_aKillmsgs[r].m_ModeSpecial&1)
 			{
@@ -107,7 +107,7 @@ void CKillMessages::OnRender()
 
 		if(m_aKillmsgs[r].m_VictimID != m_aKillmsgs[r].m_KillerID)
 		{
-			if(m_pClient->m_Snap.m_pGameobj && m_pClient->m_Snap.m_pGameobj->m_Flags&GAMEFLAG_FLAGS)
+			if(m_pClient->m_Snap.m_pGameInfoObj && m_pClient->m_Snap.m_pGameInfoObj->m_GameFlags&GAMEFLAG_FLAGS)
 			{
 				if(m_aKillmsgs[r].m_ModeSpecial&2)
 				{
diff --git a/src/game/client/components/menus_ingame.cpp b/src/game/client/components/menus_ingame.cpp
index ffb4bdfe..9a4ee5dc 100644
--- a/src/game/client/components/menus_ingame.cpp
+++ b/src/game/client/components/menus_ingame.cpp
@@ -35,7 +35,7 @@ void CMenus::RenderGame(CUIRect MainView)
 	if(DoButton_Menu(&s_DisconnectButton, Localize("Disconnect"), 0, &Button))
 		Client()->Disconnect();
 
-	if(m_pClient->m_Snap.m_pLocalInfo && m_pClient->m_Snap.m_pGameobj)
+	if(m_pClient->m_Snap.m_pLocalInfo && m_pClient->m_Snap.m_pGameInfoObj)
 	{
 		if(m_pClient->m_Snap.m_pLocalInfo->m_Team != TEAM_SPECTATORS)
 		{
@@ -49,7 +49,7 @@ void CMenus::RenderGame(CUIRect MainView)
 			}
 		}
 		
-		if(m_pClient->m_Snap.m_pGameobj->m_Flags & GAMEFLAG_TEAMS)
+		if(m_pClient->m_Snap.m_pGameInfoObj->m_GameFlags & GAMEFLAG_TEAMS)
 		{
 			if(m_pClient->m_Snap.m_pLocalInfo->m_Team != TEAM_RED)
 			{
@@ -248,24 +248,27 @@ void CMenus::RenderServerInfo(CUIRect MainView)
 	TextRender()->Text(0, GameInfo.x+x, GameInfo.y+y, 32, Localize("Game info"), 250);
 	y += 32.0f+5.0f;
 	
-	mem_zero(aBuf, sizeof(aBuf));
-	str_format(
-		aBuf,
-		sizeof(aBuf),
-		"\n\n"
-		"%s: %s\n"
-		"%s: %s\n"
-		"%s: %d\n"
-		"%s: %d\n"
-		"\n"
-		"%s: %d/%d\n",
-		Localize("Game type"), CurrentServerInfo.m_aGameType,
-		Localize("Map"), CurrentServerInfo.m_aMap,
-		Localize("Score limit"), m_pClient->m_Snap.m_pGameobj->m_ScoreLimit,
-		Localize("Time limit"), m_pClient->m_Snap.m_pGameobj->m_TimeLimit,
-		Localize("Players"), m_pClient->m_Snap.m_NumPlayers, CurrentServerInfo.m_MaxPlayers
-	);
-	TextRender()->Text(0, GameInfo.x+x, GameInfo.y+y, 20, aBuf, 250);
+	if(m_pClient->m_Snap.m_pGameInfoObj)
+	{
+		mem_zero(aBuf, sizeof(aBuf));
+		str_format(
+			aBuf,
+			sizeof(aBuf),
+			"\n\n"
+			"%s: %s\n"
+			"%s: %s\n"
+			"%s: %d\n"
+			"%s: %d\n"
+			"\n"
+			"%s: %d/%d\n",
+			Localize("Game type"), CurrentServerInfo.m_aGameType,
+			Localize("Map"), CurrentServerInfo.m_aMap,
+			Localize("Score limit"), m_pClient->m_Snap.m_pGameInfoObj->m_ScoreLimit,
+			Localize("Time limit"), m_pClient->m_Snap.m_pGameInfoObj->m_TimeLimit,
+			Localize("Players"), m_pClient->m_Snap.m_NumPlayers, CurrentServerInfo.m_MaxPlayers
+		);
+		TextRender()->Text(0, GameInfo.x+x, GameInfo.y+y, 20, aBuf, 250);
+	}
 	
 	// motd
 	Motd.HSplitTop(10.0f, 0, &Motd);
diff --git a/src/game/client/components/nameplates.cpp b/src/game/client/components/nameplates.cpp
index ee67eb82..5664cb4e 100644
--- a/src/game/client/components/nameplates.cpp
+++ b/src/game/client/components/nameplates.cpp
@@ -33,7 +33,7 @@ void CNamePlates::RenderNameplate(
 		float tw = TextRender()->TextWidth(0, FontSize, pName, -1);
 		
 		TextRender()->TextColor(1.0f, 1.0f, 1.0f, a);
-		if(m_pClient->m_Snap.m_pGameobj && m_pClient->m_Snap.m_pGameobj->m_Flags&GAMEFLAG_TEAMS)
+		if(m_pClient->m_Snap.m_pGameInfoObj && m_pClient->m_Snap.m_pGameInfoObj->m_GameFlags&GAMEFLAG_TEAMS)
 		{
 			if(pPlayerInfo->m_Team == TEAM_RED)
 				TextRender()->TextColor(1.0f, 0.5f, 0.5f, a);
diff --git a/src/game/client/components/players.cpp b/src/game/client/components/players.cpp
index cbbc8d99..74a48503 100644
--- a/src/game/client/components/players.cpp
+++ b/src/game/client/components/players.cpp
@@ -99,8 +99,8 @@ void CPlayers::RenderHook(
 
 	// check for teamplay modes
 	bool IsTeamplay = false;
-	if(m_pClient->m_Snap.m_pGameobj)
-		IsTeamplay = (m_pClient->m_Snap.m_pGameobj->m_Flags&GAMEFLAG_TEAMS) != 0;
+	if(m_pClient->m_Snap.m_pGameInfoObj)
+		IsTeamplay = (m_pClient->m_Snap.m_pGameInfoObj->m_GameFlags&GAMEFLAG_TEAMS) != 0;
 
 	// check for ninja	
 	if (Player.m_Weapon == WEAPON_NINJA)
@@ -129,7 +129,7 @@ void CPlayers::RenderHook(
 	// use preditect players if needed
 	if(pInfo.m_Local && g_Config.m_ClPredict && Client()->State() != IClient::STATE_DEMOPLAYBACK)
 	{
-		if(!m_pClient->m_Snap.m_pLocalCharacter || (m_pClient->m_Snap.m_pGameobj && m_pClient->m_Snap.m_pGameobj->m_GameOver))
+		if(!m_pClient->m_Snap.m_pLocalCharacter || (m_pClient->m_Snap.m_pGameInfoObj && m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_GAMEOVER))
 		{
 		}
 		else
@@ -223,8 +223,8 @@ void CPlayers::RenderPlayer(
 	// check for teamplay modes
 	bool IsTeamplay = false;
 	bool NewTick = m_pClient->m_NewTick;
-	if(m_pClient->m_Snap.m_pGameobj)
-		IsTeamplay = (m_pClient->m_Snap.m_pGameobj->m_Flags&GAMEFLAG_TEAMS) != 0;
+	if(m_pClient->m_Snap.m_pGameInfoObj)
+		IsTeamplay = (m_pClient->m_Snap.m_pGameInfoObj->m_GameFlags&GAMEFLAG_TEAMS) != 0;
 
 	// check for ninja	
 	if (Player.m_Weapon == WEAPON_NINJA)
@@ -284,7 +284,7 @@ void CPlayers::RenderPlayer(
 	// use preditect players if needed
 	if(pInfo.m_Local && g_Config.m_ClPredict && Client()->State() != IClient::STATE_DEMOPLAYBACK)
 	{
-		if(!m_pClient->m_Snap.m_pLocalCharacter || (m_pClient->m_Snap.m_pGameobj && m_pClient->m_Snap.m_pGameobj->m_GameOver))
+		if(!m_pClient->m_Snap.m_pLocalCharacter || (m_pClient->m_Snap.m_pGameInfoObj && m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_GAMEOVER))
 		{
 		}
 		else
diff --git a/src/game/client/components/scoreboard.cpp b/src/game/client/components/scoreboard.cpp
index d5588c10..a9b249ed 100644
--- a/src/game/client/components/scoreboard.cpp
+++ b/src/game/client/components/scoreboard.cpp
@@ -51,24 +51,24 @@ void CScoreboard::RenderGoals(float x, float y, float w)
 	Graphics()->QuadsEnd();
 
 	// render goals
-	if(m_pClient->m_Snap.m_pGameobj)
+	if(m_pClient->m_Snap.m_pGameInfoObj)
 	{
-		if(m_pClient->m_Snap.m_pGameobj->m_ScoreLimit)
+		if(m_pClient->m_Snap.m_pGameInfoObj->m_ScoreLimit)
 		{
 			char aBuf[64];
-			str_format(aBuf, sizeof(aBuf), "%s: %d", Localize("Score limit"), m_pClient->m_Snap.m_pGameobj->m_ScoreLimit);
+			str_format(aBuf, sizeof(aBuf), "%s: %d", Localize("Score limit"), m_pClient->m_Snap.m_pGameInfoObj->m_ScoreLimit);
 			TextRender()->Text(0, x, y, 20.0f, aBuf, -1);
 		}
-		if(m_pClient->m_Snap.m_pGameobj->m_TimeLimit)
+		if(m_pClient->m_Snap.m_pGameInfoObj->m_TimeLimit)
 		{
 			char aBuf[64];
-			str_format(aBuf, sizeof(aBuf), Localize("Time limit: %d min"), m_pClient->m_Snap.m_pGameobj->m_TimeLimit);
+			str_format(aBuf, sizeof(aBuf), Localize("Time limit: %d min"), m_pClient->m_Snap.m_pGameInfoObj->m_TimeLimit);
 			TextRender()->Text(0, x+220.0f, y, 20.0f, aBuf, -1);
 		}
-		if(m_pClient->m_Snap.m_pGameobj->m_RoundNum && m_pClient->m_Snap.m_pGameobj->m_RoundCurrent)
+		if(m_pClient->m_Snap.m_pGameInfoObj->m_RoundNum && m_pClient->m_Snap.m_pGameInfoObj->m_RoundCurrent)
 		{
 			char aBuf[64];
-			str_format(aBuf, sizeof(aBuf), "%s %d/%d", Localize("Round"), m_pClient->m_Snap.m_pGameobj->m_RoundCurrent, m_pClient->m_Snap.m_pGameobj->m_RoundNum);
+			str_format(aBuf, sizeof(aBuf), "%s %d/%d", Localize("Round"), m_pClient->m_Snap.m_pGameInfoObj->m_RoundCurrent, m_pClient->m_Snap.m_pGameInfoObj->m_RoundNum);
 			float tw = TextRender()->TextWidth(0, 20.0f, aBuf, -1);
 			TextRender()->Text(0, x+w-tw-20.0f, y, 20.0f, aBuf, -1);
 		}
@@ -129,7 +129,7 @@ void CScoreboard::RenderScoreboard(float x, float y, float w, int Team, const ch
 	// render title
 	if(!pTitle)
 	{
-		if(m_pClient->m_Snap.m_pGameobj->m_GameOver)
+		if(m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_GAMEOVER)
 			pTitle = Localize("Game over");
 		else
 			pTitle = Localize("Score board");
@@ -138,10 +138,21 @@ void CScoreboard::RenderScoreboard(float x, float y, float w, int Team, const ch
 	float tw = TextRender()->TextWidth(0, 48, pTitle, -1);
 	TextRender()->Text(0, x+10, y, 48, pTitle, -1);
 
-	if(m_pClient->m_Snap.m_pGameobj)
+	if(m_pClient->m_Snap.m_pGameInfoObj->m_GameFlags&GAMEFLAG_TEAMS)
+	{
+		if(m_pClient->m_Snap.m_pGameDataObj)
+		{
+			char aBuf[128];
+			int Score = Team == TEAM_RED ? m_pClient->m_Snap.m_pGameDataObj->m_TeamscoreRed : m_pClient->m_Snap.m_pGameDataObj->m_TeamscoreBlue;
+			str_format(aBuf, sizeof(aBuf), "%d", Score);
+			tw = TextRender()->TextWidth(0, 48, aBuf, -1);
+			TextRender()->Text(0, x+w-tw-30, y, 48, aBuf, -1);
+		}
+	}
+	else
 	{
 		char aBuf[128];
-		int Score = Team == TEAM_RED ? m_pClient->m_Snap.m_pGameobj->m_TeamscoreRed : m_pClient->m_Snap.m_pGameobj->m_TeamscoreBlue;
+		int Score = m_pClient->m_Snap.m_pLocalInfo->m_Score;
 		str_format(aBuf, sizeof(aBuf), "%d", Score);
 		tw = TextRender()->TextWidth(0, 48, aBuf, -1);
 		TextRender()->Text(0, x+w-tw-30, y, 48, aBuf, -1);
@@ -208,16 +219,16 @@ void CScoreboard::RenderScoreboard(float x, float y, float w, int Team, const ch
 			--FontSizeResize;
 		TextRender()->Text(0, x+w-35.0f-Width, y+(FontSize-FontSizeResize)/2, FontSizeResize, aBuf, -1);
 
-		// render avatar
-		if((m_pClient->m_Snap.m_paFlags[0] && m_pClient->m_Snap.m_paFlags[0]->m_CarriedBy == pInfo->m_ClientID) ||
-			(m_pClient->m_Snap.m_paFlags[1] && m_pClient->m_Snap.m_paFlags[1]->m_CarriedBy == pInfo->m_ClientID))
+		// render flag
+		if(m_pClient->m_Snap.m_pGameInfoObj->m_GameFlags&GAMEFLAG_TEAMS &&
+			m_pClient->m_Snap.m_pGameDataObj && (m_pClient->m_Snap.m_pGameDataObj->m_FlagCarrierRed == pInfo->m_ClientID ||
+			m_pClient->m_Snap.m_pGameDataObj->m_FlagCarrierBlue == pInfo->m_ClientID))
 		{
 			Graphics()->BlendNormal();
 			Graphics()->TextureSet(g_pData->m_aImages[IMAGE_GAME].m_Id);
 			Graphics()->QuadsBegin();
 
-			if(pInfo->m_Team == TEAM_RED) RenderTools()->SelectSprite(SPRITE_FLAG_BLUE, SPRITE_FLAG_FLIP_X);
-			else RenderTools()->SelectSprite(SPRITE_FLAG_RED, SPRITE_FLAG_FLIP_X);
+			RenderTools()->SelectSprite(pInfo->m_Team==TEAM_RED ? SPRITE_FLAG_BLUE : SPRITE_FLAG_RED, SPRITE_FLAG_FLIP_X);
 			
 			float size = 64.0f;
 			IGraphics::CQuadItem QuadItem(x+55, y-15, size/2, size);
@@ -225,6 +236,7 @@ void CScoreboard::RenderScoreboard(float x, float y, float w, int Team, const ch
 			Graphics()->QuadsEnd();
 		}
 		
+		// render avatar
 		CTeeRenderInfo TeeInfo = m_pClient->m_aClients[pInfo->m_ClientID].m_RenderInfo;
 		TeeInfo.m_Size *= TeeSizeMod;
 		RenderTools()->RenderTee(CAnimState::GetIdle(), &TeeInfo, EMOTE_NORMAL, vec2(1,0), vec2(x+90, y+28+TeeOffset));
@@ -274,25 +286,28 @@ void CScoreboard::OnRender()
 
 	float w = 650.0f;
 
-	if(m_pClient->m_Snap.m_pGameobj && !(m_pClient->m_Snap.m_pGameobj->m_Flags&GAMEFLAG_TEAMS))
-		RenderScoreboard(Width/2-w/2, 150.0f, w, 0, 0);
-	else
+	if(m_pClient->m_Snap.m_pGameInfoObj)
 	{
-			
-		if(m_pClient->m_Snap.m_pGameobj && m_pClient->m_Snap.m_pGameobj->m_GameOver)
+		if(!(m_pClient->m_Snap.m_pGameInfoObj->m_GameFlags&GAMEFLAG_TEAMS))
+			RenderScoreboard(Width/2-w/2, 150.0f, w, 0, 0);
+		else
 		{
-			const char *pText = Localize("Draw!");
-			if(m_pClient->m_Snap.m_pGameobj->m_TeamscoreRed > m_pClient->m_Snap.m_pGameobj->m_TeamscoreBlue)
-				pText = Localize("Red team wins!");
-			else if(m_pClient->m_Snap.m_pGameobj->m_TeamscoreBlue > m_pClient->m_Snap.m_pGameobj->m_TeamscoreRed)
-				pText = Localize("Blue team wins!");
-				
-			float w = TextRender()->TextWidth(0, 86.0f, pText, -1);
-			TextRender()->Text(0, Width/2-w/2, 39, 86.0f, pText, -1);
-		}
 		
-		RenderScoreboard(Width/2-w-20, 150.0f, w, TEAM_RED, Localize("Red team"));
-		RenderScoreboard(Width/2 + 20, 150.0f, w, TEAM_BLUE, Localize("Blue team"));
+			if(m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_GAMEOVER && m_pClient->m_Snap.m_pGameDataObj)
+			{
+				const char *pText = Localize("Draw!");
+				if(m_pClient->m_Snap.m_pGameDataObj->m_TeamscoreRed > m_pClient->m_Snap.m_pGameDataObj->m_TeamscoreBlue)
+					pText = Localize("Red team wins!");
+				else if(m_pClient->m_Snap.m_pGameDataObj->m_TeamscoreBlue > m_pClient->m_Snap.m_pGameDataObj->m_TeamscoreRed)
+					pText = Localize("Blue team wins!");
+			
+				float w = TextRender()->TextWidth(0, 86.0f, pText, -1);
+				TextRender()->Text(0, Width/2-w/2, 39, 86.0f, pText, -1);
+			}
+		
+			RenderScoreboard(Width/2-w-20, 150.0f, w, TEAM_RED, Localize("Red team"));
+			RenderScoreboard(Width/2 + 20, 150.0f, w, TEAM_BLUE, Localize("Blue team"));
+		}
 	}
 
 	RenderGoals(Width/2-w/2, 150+750+25, w);
@@ -314,7 +329,7 @@ bool CScoreboard::Active()
 	}
 
 	// if the game is over
-	if(m_pClient->m_Snap.m_pGameobj && m_pClient->m_Snap.m_pGameobj->m_GameOver)
+	if(m_pClient->m_Snap.m_pGameInfoObj && m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_GAMEOVER)
 		return true;
 
 	return false;
diff --git a/src/game/client/gameclient.cpp b/src/game/client/gameclient.cpp
index 0aaf40b4..f80d0266 100644
--- a/src/game/client/gameclient.cpp
+++ b/src/game/client/gameclient.cpp
@@ -340,7 +340,7 @@ void CGameClient::UpdateLocalCharacterPos()
 {
 	if(g_Config.m_ClPredict && Client()->State() != IClient::STATE_DEMOPLAYBACK)
 	{
-		if(!m_Snap.m_pLocalCharacter || (m_Snap.m_pGameobj && m_Snap.m_pGameobj->m_GameOver))
+		if(!m_Snap.m_pLocalCharacter || (m_Snap.m_pGameInfoObj && m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_GAMEOVER))
 		{
 			// don't use predicted
 		}
@@ -413,7 +413,7 @@ void CGameClient::OnRender()
 		// resend if client info differs
 		if(str_comp(g_Config.m_PlayerName, m_aClients[m_Snap.m_LocalClientID].m_aName) ||
 			str_comp(g_Config.m_PlayerSkin, m_aClients[m_Snap.m_LocalClientID].m_aSkinName) ||
-			(g_GameClient.m_Snap.m_pGameobj && !(g_GameClient.m_Snap.m_pGameobj->m_Flags&GAMEFLAG_TEAMS) &&	// no teamgame?
+			(m_Snap.m_pGameInfoObj && !(m_Snap.m_pGameInfoObj->m_GameFlags&GAMEFLAG_TEAMS) &&	// no teamgame?
 			(g_Config.m_PlayerUseCustomColor != m_aClients[m_Snap.m_LocalClientID].m_UseCustomColor ||
 			g_Config.m_PlayerColorBody != m_aClients[m_Snap.m_LocalClientID].m_ColorBody ||
 			g_Config.m_PlayerColorFeet != m_aClients[m_Snap.m_LocalClientID].m_ColorFeet)))
@@ -728,15 +728,20 @@ void CGameClient::OnNewSnapshot()
 						Evolve(&m_Snap.m_aCharacters[Item.m_ID].m_Cur, Client()->GameTick());
 				}
 			}
-			else if(Item.m_Type == NETOBJTYPE_GAME)
+			else if(Item.m_Type == NETOBJTYPE_GAMEINFO)
 			{
-				static int s_GameOver = 0;
-				m_Snap.m_pGameobj = (CNetObj_Game *)pData;
-				if(s_GameOver == 0 && m_Snap.m_pGameobj->m_GameOver != 0)
+				static bool s_GameOver = 0;
+				m_Snap.m_pGameInfoObj = (const CNetObj_GameInfo *)pData;
+				if(!s_GameOver && m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_GAMEOVER)
 					OnGameOver();
-				else if(s_GameOver != 0 && m_Snap.m_pGameobj->m_GameOver == 0)
+				else if(s_GameOver && !(m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_GAMEOVER))
 					OnStartGame();
-				s_GameOver = m_Snap.m_pGameobj->m_GameOver;
+				s_GameOver = m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_GAMEOVER;
+			}
+			else if(Item.m_Type == NETOBJTYPE_GAMEDATA)
+			{
+				m_Snap.m_pGameDataObj = (const CNetObj_GameData *)pData;
+				m_Snap.m_GameDataSnapID = Item.m_ID;
 			}
 			else if(Item.m_Type == NETOBJTYPE_FLAG)
 				m_Snap.m_paFlags[Item.m_ID%2] = (const CNetObj_Flag *)pData;
@@ -803,7 +808,7 @@ void CGameClient::OnPredict()
 		return;
 	
 	// don't predict anything if we are paused
-	if(m_Snap.m_pGameobj && m_Snap.m_pGameobj->m_Paused)
+	if(m_Snap.m_pGameInfoObj && m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_PAUSED)
 	{
 		if(m_Snap.m_pLocalCharacter)
 			m_PredictedChar.Read(m_Snap.m_pLocalCharacter);
@@ -928,7 +933,7 @@ void CGameClient::CClientData::UpdateRenderInfo()
 	m_RenderInfo = m_SkinInfo;
 
 	// force team colors
-	if(g_GameClient.m_Snap.m_pGameobj && g_GameClient.m_Snap.m_pGameobj->m_Flags&GAMEFLAG_TEAMS)
+	if(g_GameClient.m_Snap.m_pGameInfoObj && g_GameClient.m_Snap.m_pGameInfoObj->m_GameFlags&GAMEFLAG_TEAMS)
 	{
 		const int TeamColors[2] = {65387, 10223467};
 		if(m_Team >= TEAM_RED && m_Team <= TEAM_BLUE)
diff --git a/src/game/client/gameclient.h b/src/game/client/gameclient.h
index e6650d44..3681b301 100644
--- a/src/game/client/gameclient.h
+++ b/src/game/client/gameclient.h
@@ -110,7 +110,9 @@ public:
 		const CNetObj_Character *m_pLocalPrevCharacter;
 		const CNetObj_PlayerInfo *m_pLocalInfo;
 		const CNetObj_Flag *m_paFlags[2];
-		const CNetObj_Game *m_pGameobj;
+		const CNetObj_GameInfo *m_pGameInfoObj;
+		const CNetObj_GameData *m_pGameDataObj;
+		int m_GameDataSnapID;
 
 		const CNetObj_PlayerInfo *m_paPlayerInfos[MAX_CLIENTS];
 		const CNetObj_PlayerInfo *m_paInfoByScore[MAX_CLIENTS];
diff --git a/src/game/server/entities/flag.cpp b/src/game/server/entities/flag.cpp
index 13109b07..5ac3de47 100644
--- a/src/game/server/entities/flag.cpp
+++ b/src/game/server/entities/flag.cpp
@@ -25,6 +25,9 @@ void CFlag::Reset()
 
 void CFlag::Snap(int SnappingClient)
 {
+	if(NetworkClipped(SnappingClient))
+		return;
+
 	CNetObj_Flag *pFlag = (CNetObj_Flag *)Server()->SnapNewItem(NETOBJTYPE_FLAG, m_Team, sizeof(CNetObj_Flag));
 	if(!pFlag)
 		return;
@@ -32,10 +35,4 @@ void CFlag::Snap(int SnappingClient)
 	pFlag->m_X = (int)m_Pos.x;
 	pFlag->m_Y = (int)m_Pos.y;
 	pFlag->m_Team = m_Team;
-	pFlag->m_CarriedBy = -1;
-	
-	if(m_AtStand)
-		pFlag->m_CarriedBy = -2;
-	else if(m_pCarryingCharacter && m_pCarryingCharacter->GetPlayer())
-		pFlag->m_CarriedBy = m_pCarryingCharacter->GetPlayer()->GetCID();
 }
diff --git a/src/game/server/gamecontroller.cpp b/src/game/server/gamecontroller.cpp
index e51b503c..e37bde65 100644
--- a/src/game/server/gamecontroller.cpp
+++ b/src/game/server/gamecontroller.cpp
@@ -571,37 +571,26 @@ bool IGameController::IsTeamplay() const
 
 void IGameController::Snap(int SnappingClient)
 {
-	CNetObj_Game *pGameObj = (CNetObj_Game *)Server()->SnapNewItem(NETOBJTYPE_GAME, 0, sizeof(CNetObj_Game));
-	if(!pGameObj)
+	CNetObj_GameInfo *pGameInfoObj = (CNetObj_GameInfo *)Server()->SnapNewItem(NETOBJTYPE_GAMEINFO, 0, sizeof(CNetObj_GameInfo));
+	if(!pGameInfoObj)
 		return;
 
-	pGameObj->m_Paused = GameServer()->m_World.m_Paused;
-	pGameObj->m_GameOver = m_GameOverTick==-1?0:1;
-	pGameObj->m_SuddenDeath = m_SuddenDeath;
-	
-	pGameObj->m_ScoreLimit = g_Config.m_SvScorelimit;
-	pGameObj->m_TimeLimit = g_Config.m_SvTimelimit;
-	pGameObj->m_RoundStartTick = m_RoundStartTick;
-	pGameObj->m_Flags = m_GameFlags;
-	
-	pGameObj->m_Warmup = m_Warmup;
-	
-	pGameObj->m_RoundNum = (str_length(g_Config.m_SvMaprotation) && g_Config.m_SvRoundsPerMap) ? g_Config.m_SvRoundsPerMap : 0;
-	pGameObj->m_RoundCurrent = m_RoundCount+1;
-	
-	
-	if(SnappingClient == -1)
-	{
-		// we are recording a demo, just set the scores
-		pGameObj->m_TeamscoreRed = m_aTeamscore[TEAM_RED];
-		pGameObj->m_TeamscoreBlue = m_aTeamscore[TEAM_BLUE];
-	}
-	else
-	{
-		// TODO: this little hack should be removed
-		pGameObj->m_TeamscoreRed = IsTeamplay() ? m_aTeamscore[TEAM_RED] : GameServer()->m_apPlayers[SnappingClient]->m_Score;
-		pGameObj->m_TeamscoreBlue = m_aTeamscore[TEAM_BLUE];
-	}
+	pGameInfoObj->m_GameFlags = m_GameFlags;
+	pGameInfoObj->m_GameStateFlags = 0;
+	if(m_GameOverTick != -1)
+		pGameInfoObj->m_GameStateFlags |= GAMESTATEFLAG_GAMEOVER;
+	if(m_SuddenDeath)
+		pGameInfoObj->m_GameStateFlags |= GAMESTATEFLAG_SUDDENDEATH;
+	if(GameServer()->m_World.m_Paused)
+		pGameInfoObj->m_GameStateFlags |= GAMESTATEFLAG_PAUSED;
+	pGameInfoObj->m_RoundStartTick = m_RoundStartTick;
+	pGameInfoObj->m_WarmupTimer = m_Warmup;
+
+	pGameInfoObj->m_ScoreLimit = g_Config.m_SvScorelimit;
+	pGameInfoObj->m_TimeLimit = g_Config.m_SvTimelimit;
+	
+	pGameInfoObj->m_RoundNum = (str_length(g_Config.m_SvMaprotation) && g_Config.m_SvRoundsPerMap) ? g_Config.m_SvRoundsPerMap : 0;
+	pGameInfoObj->m_RoundCurrent = m_RoundCount+1;
 }
 
 int IGameController::GetAutoTeam(int NotThisID)
diff --git a/src/game/server/gamemodes/ctf.cpp b/src/game/server/gamemodes/ctf.cpp
index 92fc90c9..490067dd 100644
--- a/src/game/server/gamemodes/ctf.cpp
+++ b/src/game/server/gamemodes/ctf.cpp
@@ -78,6 +78,31 @@ bool CGameControllerCTF::CanBeMovedOnBalance(int ClientID)
 	return true;
 }
 
+void CGameControllerCTF::Snap(int SnappingClient)
+{
+	IGameController::Snap(SnappingClient);
+
+	CNetObj_GameData *pGameDataObj = (CNetObj_GameData *)Server()->SnapNewItem(NETOBJTYPE_GAMEDATA, 0, sizeof(CNetObj_GameData));
+	if(!pGameDataObj)
+		return;
+
+	pGameDataObj->m_TeamscoreRed = m_aTeamscore[TEAM_RED];
+	pGameDataObj->m_TeamscoreBlue = m_aTeamscore[TEAM_BLUE];
+
+	if(m_apFlags[TEAM_RED]->m_AtStand)
+		pGameDataObj->m_FlagCarrierRed = FLAG_ATSTAND;
+	else if(m_apFlags[TEAM_RED]->m_pCarryingCharacter && m_apFlags[TEAM_RED]->m_pCarryingCharacter->GetPlayer())
+		pGameDataObj->m_FlagCarrierRed = m_apFlags[TEAM_RED]->m_pCarryingCharacter->GetPlayer()->GetCID();
+	else
+		pGameDataObj->m_FlagCarrierRed = FLAG_TAKEN;
+	if(m_apFlags[TEAM_BLUE]->m_AtStand)
+		pGameDataObj->m_FlagCarrierBlue = FLAG_ATSTAND;
+	else if(m_apFlags[TEAM_BLUE]->m_pCarryingCharacter && m_apFlags[TEAM_BLUE]->m_pCarryingCharacter->GetPlayer())
+		pGameDataObj->m_FlagCarrierBlue = m_apFlags[TEAM_BLUE]->m_pCarryingCharacter->GetPlayer()->GetCID();
+	else
+		pGameDataObj->m_FlagCarrierBlue = FLAG_TAKEN;
+}
+
 void CGameControllerCTF::Tick()
 {
 	IGameController::Tick();
diff --git a/src/game/server/gamemodes/ctf.h b/src/game/server/gamemodes/ctf.h
index e6fb9c89..b9868e70 100644
--- a/src/game/server/gamemodes/ctf.h
+++ b/src/game/server/gamemodes/ctf.h
@@ -12,6 +12,7 @@ public:
 	
 	CGameControllerCTF(class CGameContext *pGameServer);
 	virtual bool CanBeMovedOnBalance(int ClientID);
+	virtual void Snap(int SnappingClient);
 	virtual void Tick();
 	
 	virtual bool OnEntity(int Index, vec2 Pos);
diff --git a/src/game/server/gamemodes/tdm.cpp b/src/game/server/gamemodes/tdm.cpp
index 581fca2f..ef5302c6 100644
--- a/src/game/server/gamemodes/tdm.cpp
+++ b/src/game/server/gamemodes/tdm.cpp
@@ -27,6 +27,21 @@ int CGameControllerTDM::OnCharacterDeath(class CCharacter *pVictim, class CPlaye
 	return 0;
 }
 
+void CGameControllerTDM::Snap(int SnappingClient)
+{
+	IGameController::Snap(SnappingClient);
+
+	CNetObj_GameData *pGameDataObj = (CNetObj_GameData *)Server()->SnapNewItem(NETOBJTYPE_GAMEDATA, 0, sizeof(CNetObj_GameData));
+	if(!pGameDataObj)
+		return;
+
+	pGameDataObj->m_TeamscoreRed = m_aTeamscore[TEAM_RED];
+	pGameDataObj->m_TeamscoreBlue = m_aTeamscore[TEAM_BLUE];
+
+	pGameDataObj->m_FlagCarrierRed = 0;
+	pGameDataObj->m_FlagCarrierBlue = 0;
+}
+
 void CGameControllerTDM::Tick()
 {
 	DoTeamScoreWincheck();
diff --git a/src/game/server/gamemodes/tdm.h b/src/game/server/gamemodes/tdm.h
index 806e799a..c8d3f328 100644
--- a/src/game/server/gamemodes/tdm.h
+++ b/src/game/server/gamemodes/tdm.h
@@ -10,6 +10,7 @@ public:
 	CGameControllerTDM(class CGameContext *pGameServer);
 	
 	int OnCharacterDeath(class CCharacter *pVictim, class CPlayer *pKiller, int Weapon);
+	virtual void Snap(int SnappingClient);
 	virtual void Tick();
 };
 #endif