1772: Remove all checking for the gametype in the game r=Learath2 a=heinrich5991

OK, maybe not actually remove because it is kept for fallback when the
new method isn't available.

The whole gametype parsing business had the same downsides as user agent
parsing on the web, hence I removed it while keeping behavior the same.

This allows servers to explicitly opt in or out of certain bug
workarounds and other client behavior. This increases the complexity of
different configurations that are available in the client (which is a
bad thing).

Co-authored-by: heinrich5991 <heinrich5991@gmail.com>
This commit is contained in:
bors[bot] 2019-06-13 22:43:55 +00:00
commit f7861563e2
29 changed files with 354 additions and 162 deletions

View file

@ -198,7 +198,7 @@ class Flags:
self.values = values
class NetObject:
def __init__(self, name, variables, ex=None):
def __init__(self, name, variables, ex=None, validate_size=True):
l = name.split(":")
self.name = l[0]
self.base = ""
@ -209,6 +209,7 @@ class NetObject:
self.enum_name = "NETOBJTYPE_%s" % self.name.upper()
self.variables = variables
self.ex = ex
self.validate_size = validate_size
def emit_declaration(self):
if self.base:
lines = ["struct %s : public %s"%(self.struct_name,self.base_struct_name), "{"]
@ -221,10 +222,14 @@ class NetObject:
def emit_validate(self):
lines = ["case %s:" % self.enum_name]
lines += ["{"]
lines += ["\t%s *pObj = (%s *)pData;"%(self.struct_name, self.struct_name)]
lines += ["\tif((int)sizeof(*pObj) > Size) return -1;"]
if self.validate_size:
lines += ["\t%s *pObj = (%s *)pData;"%(self.struct_name, self.struct_name)]
lines += ["\tif((int)sizeof(*pObj) > Size) return -1;"]
prev_len = len(lines)
for v in self.variables:
lines += ["\t"+line for line in v.emit_validate()]
if not self.validate_size and prev_len != len(lines):
raise ValueError("Can't use members that need validation in a struct whose size isn't validated")
lines += ["\treturn 0;"]
lines += ["}"]
return lines
@ -273,8 +278,8 @@ class NetMessage(NetObject):
return lines
class NetObjectEx(NetObject):
def __init__(self, name, ex, variables):
NetObject.__init__(self, name, variables, ex=ex)
def __init__(self, name, ex, variables, validate_size=True):
NetObject.__init__(self, name, variables, ex=ex, validate_size=validate_size)
class NetEventEx(NetEvent):
def __init__(self, name, ex, variables):

View file

@ -5,10 +5,20 @@ PlayerFlags = ["PLAYING", "IN_MENU", "CHATTING", "SCOREBOARD", "AIM"]
GameFlags = ["TEAMS", "FLAGS"]
GameStateFlags = ["GAMEOVER", "SUDDENDEATH", "PAUSED", "RACETIME"]
CharacterFlags = ["SOLO", "JETPACK", "NO_COLLISION", "ENDLESS_HOOK", "ENDLESS_JUMP", "SUPER",
"NO_HAMMER_HIT", "NO_SHOTGUN_HIT", "NO_GRENADE_HIT", "NO_RIFLE_HIT", "NO_HOOK",
"TELEGUN_GUN", "TELEGUN_GRENADE", "TELEGUN_LASER",
"WEAPON_HAMMER", "WEAPON_GUN", "WEAPON_SHOTGUN", "WEAPON_GRENADE", "WEAPON_LASER", "WEAPON_NINJA"]
GameInfoFlags = ["TIMESCORE"]
"NO_HAMMER_HIT", "NO_SHOTGUN_HIT", "NO_GRENADE_HIT", "NO_RIFLE_HIT", "NO_HOOK",
"TELEGUN_GUN", "TELEGUN_GRENADE", "TELEGUN_LASER",
"WEAPON_HAMMER", "WEAPON_GUN", "WEAPON_SHOTGUN", "WEAPON_GRENADE", "WEAPON_LASER", "WEAPON_NINJA"]
GameInfoFlags = [
"TIMESCORE", "GAMETYPE_RACE", "GAMETYPE_FASTCAP", "GAMETYPE_FNG",
"GAMETYPE_DDRACE", "GAMETYPE_DDNET", "GAMETYPE_BLOCK_WORLDS",
"GAMETYPE_VANILLA", "GAMETYPE_PLUS", "FLAG_STARTS_RACE", "RACE",
"UNLIMITED_AMMO", "DDRACE_RECORD_MESSAGE", "RACE_RECORD_MESSAGE",
"ALLOW_EYE_WHEEL", "ALLOW_HOOK_COLL", "ALLOW_ZOOM", "BUG_DDRACE_GHOST",
"BUG_DDRACE_INPUT", "BUG_FNG_LASER_RANGE", "BUG_VANILLA_BOUNCE",
"PREDICT_FNG", "PREDICT_DDRACE", "PREDICT_DDRACE_TILES", "PREDICT_VANILLA",
"ENTITIES_DDNET", "ENTITIES_DDRACE", "ENTITIES_RACE", "ENTITIES_FNG",
"ENTITIES_VANILLA",
]
Emoticons = ["OOP", "EXCLAMATION", "HEARTS", "DROP", "DOTDOT", "MUSIC", "SORRY", "GHOST", "SUSHI", "SPLATTEE", "DEVILTEE", "ZOMG", "ZZZ", "WTF", "EYES", "QUESTION"]
@ -38,6 +48,11 @@ enum
SPEC_FREEVIEW=-1,
SPEC_FOLLOW=-2,
};
enum
{
GAMEINFO_CURVERSION=2,
};
'''
RawSource = '''
@ -216,9 +231,10 @@ Objects = [
NetIntRange("m_AuthLevel", "AUTHED_NO", "AUTHED_ADMIN"),
]),
NetObjectEx("DDNetGameInfo", "gameinfo@netobj.ddnet.tw", [
NetObjectEx("GameInfoEx", "gameinfo@netobj.ddnet.tw", [
NetIntAny("m_Flags"),
]),
NetIntAny("m_Version"),
], validate_size=False),
## Events

View file

@ -153,6 +153,7 @@ public:
virtual int SnapNumItems(int SnapID) = 0;
virtual void *SnapFindItem(int SnapID, int Type, int ID) = 0;
virtual void *SnapGetItem(int SnapID, int Index, CSnapItem *pItem) = 0;
virtual int SnapItemSize(int SnapID, int Index) = 0;
virtual void SnapInvalidateItem(int SnapID, int Index) = 0;
virtual void SnapSetStaticsize(int ItemType, int Size) = 0;

View file

@ -740,6 +740,8 @@ void CClient::DisconnectWithReason(const char *pReason)
//
m_RconAuthed[0] = 0;
m_CanReceiveServerCapabilities = true;
m_ServerSentCapabilities = false;
m_UseTempRconCommands = 0;
m_pConsole->DeregisterTempAll();
m_NetClient[0].Disconnect(pReason);
@ -899,6 +901,12 @@ void *CClient::SnapGetItem(int SnapID, int Index, CSnapItem *pItem)
return (void *)i->Data();
}
int CClient::SnapItemSize(int SnapID, int Index)
{
dbg_assert(SnapID >= 0 && SnapID < NUM_SNAPSHOT_TYPES, "invalid SnapID");
return m_aSnapshots[g_Config.m_ClDummy][SnapID]->m_pAltSnap->GetItemSize(Index);
}
void CClient::SnapInvalidateItem(int SnapID, int Index)
{
CSnapshotItem *i;
@ -1480,6 +1488,15 @@ void CClient::ProcessServerInfo(int RawType, NETADDR *pFrom, const void *pData,
#undef GET_INT
}
bool CClient::ShouldSendChatTimeoutCodeHeuristic()
{
if(m_ServerSentCapabilities)
{
return false;
}
return IsDDNet(&m_CurrentServerInfo);
}
static void FormatMapDownloadFilename(const char *pName, SHA256_DIGEST *pSha256, int Crc, bool Temp, char *pBuffer, int BufferSize)
{
char aSha256[SHA256_MAXSTRSIZE + 1];
@ -1497,6 +1514,22 @@ static void FormatMapDownloadFilename(const char *pName, SHA256_DIGEST *pSha256,
Temp ? ".tmp" : "");
}
static CServerCapabilities GetServerCapabilities(int Version, int Flags)
{
CServerCapabilities Result;
bool DDNet = false;
if(Version >= 1)
{
DDNet = Flags&SERVERCAPFLAG_DDNET;
}
Result.m_ChatTimeoutCode = DDNet;
if(Version >= 1)
{
Result.m_ChatTimeoutCode = Flags&SERVERCAPFLAG_CHATTIMEOUTCODE;
}
return Result;
}
void CClient::ProcessServerPacket(CNetChunk *pPacket)
{
CUnpacker Unpacker;
@ -1537,8 +1570,29 @@ void CClient::ProcessServerPacket(CNetChunk *pPacket)
m_MapDetailsSha256 = *pMapSha256;
m_MapDetailsCrc = MapCrc;
}
else if((pPacket->m_Flags&NET_CHUNKFLAG_VITAL) != 0 && Msg == NETMSG_CAPABILITIES)
{
if(!m_CanReceiveServerCapabilities)
{
return;
}
int Version = Unpacker.GetInt();
int Flags = Unpacker.GetInt();
if(Version <= 0)
{
return;
}
m_ServerCapabilities = GetServerCapabilities(Version, Flags);
m_CanReceiveServerCapabilities = false;
m_ServerSentCapabilities = true;
}
else if((pPacket->m_Flags&NET_CHUNKFLAG_VITAL) != 0 && Msg == NETMSG_MAP_CHANGE)
{
if(m_CanReceiveServerCapabilities)
{
m_ServerCapabilities = GetServerCapabilities(0, 0);
m_CanReceiveServerCapabilities = false;
}
bool MapDetailsWerePresent = m_MapDetailsPresent;
m_MapDetailsPresent = false;
@ -1923,7 +1977,7 @@ void CClient::ProcessServerPacket(CNetChunk *pPacket)
if(m_ReceivedSnapshots[g_Config.m_ClDummy] > 50 && !m_aTimeoutCodeSent[g_Config.m_ClDummy])
{
if(IsDDNet(&m_CurrentServerInfo))
if(m_ServerCapabilities.m_ChatTimeoutCode || ShouldSendChatTimeoutCodeHeuristic())
{
m_aTimeoutCodeSent[g_Config.m_ClDummy] = true;
CNetMsg_Cl_Say Msg;

View file

@ -55,6 +55,12 @@ public:
void Update(CGraph *pGraph, int64 Target, int TimeLeft, int AdjustDirection);
};
class CServerCapabilities
{
public:
bool m_ChatTimeoutCode;
};
class CClient : public IClient, public CDemoPlayer::IListener
{
// needed interfaces
@ -191,6 +197,12 @@ class CClient : public IClient, public CDemoPlayer::IListener
std::list<std::shared_ptr<CDemoEdit>> m_EditJobs;
//
bool m_CanReceiveServerCapabilities;
bool m_ServerSentCapabilities;
CServerCapabilities m_ServerCapabilities;
bool ShouldSendChatTimeoutCodeHeuristic();
class CServerInfo m_CurrentServerInfo;
int64 m_CurrentServerInfoRequestTime; // >= 0 should request, == -1 got info
@ -285,6 +297,7 @@ public:
int GetPredictionTime();
void *SnapGetItem(int SnapID, int Index, CSnapItem *pItem);
int SnapItemSize(int SnapID, int Index);
void SnapInvalidateItem(int SnapID, int Index);
void *SnapFindItem(int SnapID, int Type, int ID);
int SnapNumItems(int SnapID);

View file

@ -849,6 +849,7 @@ int CServer::NewClientNoAuthCallback(int ClientID, void *pUser)
pThis->m_aClients[ClientID].m_ShowIps = false;
pThis->m_aClients[ClientID].Reset();
pThis->SendCapabilities(ClientID);
pThis->SendMap(ClientID);
#if defined(CONF_FAMILY_UNIX)
pThis->SendConnLoggingCommand(OPEN_SESSION, pThis->m_NetServer.ClientAddr(ClientID));
@ -977,6 +978,14 @@ void CServer::GetMapInfo(char *pMapName, int MapNameSize, int *pMapSize, SHA256_
*pMapCrc = m_CurrentMapCrc;
}
void CServer::SendCapabilities(int ClientID)
{
CMsgPacker Msg(NETMSG_CAPABILITIES);
Msg.AddInt(SERVERCAP_CURVERSION); // version
Msg.AddInt(SERVERCAPFLAG_CHATTIMEOUTCODE); // flags
SendMsgEx(&Msg, MSGFLAG_VITAL, ClientID, true);
}
void CServer::SendMap(int ClientID)
{
{
@ -1202,6 +1211,7 @@ void CServer::ProcessClientPacket(CNetChunk *pPacket)
m_aClients[ClientID].m_State = CClient::STATE_CONNECTING;
SendRconType(ClientID, m_AuthManager.NumNonDefaultKeys() > 0);
SendCapabilities(ClientID);
SendMap(ClientID);
}
}
@ -1556,10 +1566,7 @@ void CServer::SendServerInfo(const NETADDR *pAddr, int Token, int Type, bool Sen
p.AddString(GameServer()->GameType(), 16);
// flags
int Flags = SERVER_FLAG_ISDDNET;
if(g_Config.m_Password[0])
Flags |= SERVER_FLAG_PASSWORD;
ADD_INT(p, Flags);
ADD_INT(p, g_Config.m_Password[0] ? SERVER_FLAG_PASSWORD : 0);
int MaxClients = m_NetServer.MaxClients();
if(Type == SERVERINFO_VANILLA || Type == SERVERINFO_INGAME)

View file

@ -278,6 +278,7 @@ public:
static int ClientRejoinCallback(int ClientID, void *pUser);
void SendRconType(int ClientID, bool UsernameReq);
void SendCapabilities(int ClientID);
void SendMap(int ClientID);
void SendMapData(int ClientID, int Chunk);
void SendConnectionReady(int ClientID);

View file

@ -79,7 +79,6 @@ enum
{
SERVER_TICK_SPEED=50,
SERVER_FLAG_PASSWORD = 0x1,
SERVER_FLAG_ISDDNET = 0x2,
MAX_CLIENTS=64,
VANILLA_MAX_CLIENTS=16,

View file

@ -19,6 +19,10 @@ enum
UNPACKMESSAGE_ERROR=0,
UNPACKMESSAGE_OK,
UNPACKMESSAGE_ANSWER,
SERVERCAP_CURVERSION=1,
SERVERCAPFLAG_DDNET=1<<0,
SERVERCAPFLAG_CHATTIMEOUTCODE=1<<1,
};
void RegisterUuids(class CUuidManager *pManager);

View file

@ -25,3 +25,4 @@ UUID(NETMSG_IDONTKNOW, "i-dont-know@ddnet.tw")
UUID(NETMSG_RCONTYPE, "rcon-type@ddnet.tw")
UUID(NETMSG_MAP_DETAILS, "map-details@ddnet.tw")
UUID(NETMSG_CAPABILITIES, "capabilities@ddnet.tw")

View file

@ -60,8 +60,7 @@ bool IsBlockWorlds(const CServerInfo *pInfo)
bool IsDDNet(const CServerInfo *pInfo)
{
return (pInfo->m_Flags & SERVER_FLAG_ISDDNET
|| str_find_nocase(pInfo->m_aGameType, "ddracenet")
return (str_find_nocase(pInfo->m_aGameType, "ddracenet")
|| str_find_nocase(pInfo->m_aGameType, "ddnet"))
&& !IsBlockInfectionZ(pInfo);
}

View file

@ -24,10 +24,7 @@ CCamera::CCamera()
void CCamera::OnRender()
{
CServerInfo Info;
Client()->GetServerInfo(&Info);
if(!(m_pClient->m_Snap.m_SpecInfo.m_Active || IsRace(&Info) || IsBlockWorlds(&Info) || Client()->State() == IClient::STATE_DEMOPLAYBACK))
if(!(m_pClient->m_Snap.m_SpecInfo.m_Active || GameClient()->m_GameInfo.m_AllowZoom || Client()->State() == IClient::STATE_DEMOPLAYBACK))
{
if(!Client()->DummyConnected() && !Client()->DummyConnecting())
{
@ -112,17 +109,13 @@ void CCamera::OnReset()
void CCamera::ConZoomPlus(IConsole::IResult *pResult, void *pUserData)
{
CCamera *pSelf = (CCamera *)pUserData;
CServerInfo Info;
pSelf->Client()->GetServerInfo(&Info);
if(pSelf->m_pClient->m_Snap.m_SpecInfo.m_Active || IsRace(&Info) || IsBlockWorlds(&Info) || pSelf->Client()->State() == IClient::STATE_DEMOPLAYBACK)
if(pSelf->m_pClient->m_Snap.m_SpecInfo.m_Active || pSelf->GameClient()->m_GameInfo.m_AllowZoom || pSelf->Client()->State() == IClient::STATE_DEMOPLAYBACK)
((CCamera *)pUserData)->m_Zoom *= ZoomStep;
}
void CCamera::ConZoomMinus(IConsole::IResult *pResult, void *pUserData)
{
CCamera *pSelf = (CCamera *)pUserData;
CServerInfo Info;
pSelf->Client()->GetServerInfo(&Info);
if(pSelf->m_pClient->m_Snap.m_SpecInfo.m_Active || IsRace(&Info) || IsBlockWorlds(&Info) || pSelf->Client()->State() == IClient::STATE_DEMOPLAYBACK)
if(pSelf->m_pClient->m_Snap.m_SpecInfo.m_Active || pSelf->GameClient()->m_GameInfo.m_AllowZoom || pSelf->Client()->State() == IClient::STATE_DEMOPLAYBACK)
{
if(((CCamera *)pUserData)->m_Zoom < 500.0f/ZoomStep)
{
@ -132,8 +125,5 @@ void CCamera::ConZoomMinus(IConsole::IResult *pResult, void *pUserData)
}
void CCamera::ConZoomReset(IConsole::IResult *pResult, void *pUserData)
{
CCamera *pSelf = (CCamera *)pUserData;
CServerInfo Info;
pSelf->Client()->GetServerInfo(&Info);
((CCamera *)pUserData)->OnReset();
}

View file

@ -110,10 +110,7 @@ static void ConKeyInputState(IConsole::IResult *pResult, void *pUserData)
{
CInputState *pState = (CInputState *)pUserData;
CServerInfo Info;
pState->m_pControls->GameClient()->Client()->GetServerInfo(&Info);
if ((IsRace(&Info) || IsDDRace(&Info)) && pState->m_pControls->GameClient()->m_Snap.m_SpecInfo.m_Active)
if(pState->m_pControls->GameClient()->m_GameInfo.m_BugDDRaceInput && pState->m_pControls->GameClient()->m_Snap.m_SpecInfo.m_Active)
return;
if (g_Config.m_ClDummy)
@ -126,10 +123,7 @@ static void ConKeyInputCounter(IConsole::IResult *pResult, void *pUserData)
{
CInputState *pState = (CInputState *)pUserData;
CServerInfo Info;
pState->m_pControls->GameClient()->Client()->GetServerInfo(&Info);
if ((IsRace(&Info) || IsDDRace(&Info)) && pState->m_pControls->GameClient()->m_Snap.m_SpecInfo.m_Active)
if(pState->m_pControls->GameClient()->m_GameInfo.m_BugDDRaceInput && pState->m_pControls->GameClient()->m_Snap.m_SpecInfo.m_Active)
return;
int *v;
@ -216,9 +210,7 @@ int CControls::SnapInput(int *pData)
{
if(m_InputData[g_Config.m_ClDummy].m_PlayerFlags == PLAYERFLAG_CHATTING)
{
CServerInfo Info;
GameClient()->Client()->GetServerInfo(&Info);
if(IsDDNet(&Info))
if(GameClient()->m_GameInfo.m_BugDDRaceInput)
ResetInput(g_Config.m_ClDummy);
}
m_InputData[g_Config.m_ClDummy].m_PlayerFlags = PLAYERFLAG_PLAYING;
@ -241,9 +233,7 @@ int CControls::SnapInput(int *pData)
// we freeze the input if chat or menu is activated
if(!(m_InputData[g_Config.m_ClDummy].m_PlayerFlags&PLAYERFLAG_PLAYING))
{
CServerInfo Info;
GameClient()->Client()->GetServerInfo(&Info);
if(!IsDDNet(&Info))
if(!GameClient()->m_GameInfo.m_BugDDRaceInput)
ResetInput(g_Config.m_ClDummy);
mem_copy(pData, &m_InputData[g_Config.m_ClDummy], sizeof(m_InputData[0]));
@ -456,10 +446,7 @@ void CControls::OnRender()
}
}
CServerInfo Info;
GameClient()->Client()->GetServerInfo(&Info);
if( g_Config.m_ClAutoswitchWeaponsOutOfAmmo && !IsRace(&Info) && !IsDDRace(&Info) && m_pClient->m_Snap.m_pLocalCharacter )
if(g_Config.m_ClAutoswitchWeaponsOutOfAmmo && !GameClient()->m_GameInfo.m_UnlimitedAmmo && m_pClient->m_Snap.m_pLocalCharacter)
{
// Keep track of ammo count, we know weapon ammo only when we switch to that weapon, this is tracked on server and protocol does not track that
m_AmmoCount[m_pClient->m_Snap.m_pLocalCharacter->m_Weapon%NUM_WEAPONS] = m_pClient->m_Snap.m_pLocalCharacter->m_AmmoCount;

View file

@ -135,9 +135,7 @@ void CEmoticon::OnRender()
Graphics()->QuadsEnd();
CServerInfo pServerInfo;
Client()->GetServerInfo(&pServerInfo);
if((IsDDRace(&pServerInfo) || IsDDNet(&pServerInfo) || IsBlockWorlds(&pServerInfo) || IsPlus(&pServerInfo)) && g_Config.m_ClEyeWheel)
if(GameClient()->m_GameInfo.m_AllowEyeWheel && g_Config.m_ClEyeWheel)
{
Graphics()->TextureSet(-1);
Graphics()->QuadsBegin();

View file

@ -187,9 +187,7 @@ void CGhost::CheckStart()
GhostRecorder()->Stop(0, -1);
int StartTick = RaceTick;
CServerInfo ServerInfo;
Client()->GetServerInfo(&ServerInfo);
if(IsDDRace(&ServerInfo)) // the client recognizes the start one tick earlier than ddrace servers
if(GameClient()->m_GameInfo.m_BugDDRaceGhost) // the client recognizes the start one tick earlier than ddrace servers
StartTick--;
StartRecord(StartTick);
RenderTick = StartTick;
@ -261,9 +259,7 @@ void CGhost::TryRenderStart(int Tick, bool ServerControl)
void CGhost::OnNewSnapshot()
{
CServerInfo ServerInfo;
Client()->GetServerInfo(&ServerInfo);
if(!IsRace(&ServerInfo) || Client()->State() != IClient::STATE_ONLINE)
if(!GameClient()->m_GameInfo.m_Race || Client()->State() != IClient::STATE_ONLINE)
return;
if(!m_pClient->m_Snap.m_pGameInfoObj || m_pClient->m_Snap.m_SpecInfo.m_Active || !m_pClient->m_Snap.m_pLocalCharacter || !m_pClient->m_Snap.m_pLocalPrevCharacter)
return;
@ -289,9 +285,7 @@ void CGhost::OnNewSnapshot()
void CGhost::OnNewPredictedSnapshot()
{
CServerInfo ServerInfo;
Client()->GetServerInfo(&ServerInfo);
if(!IsRace(&ServerInfo) || !g_Config.m_ClRaceGhost || Client()->State() != IClient::STATE_ONLINE)
if(!GameClient()->m_GameInfo.m_Race || !g_Config.m_ClRaceGhost || Client()->State() != IClient::STATE_ONLINE)
return;
if(!m_pClient->m_Snap.m_pGameInfoObj || m_pClient->m_Snap.m_SpecInfo.m_Active || !m_pClient->m_Snap.m_pLocalCharacter || !m_pClient->m_Snap.m_pLocalPrevCharacter)
return;

View file

@ -105,7 +105,7 @@ void CHud::RenderGameTimer()
if(!(m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_SUDDENDEATH))
{
char Buf[32];
char aBuf[32];
int Time = 0;
if(m_pClient->m_Snap.m_pGameInfoObj->m_TimeLimit && (m_pClient->m_Snap.m_pGameInfoObj->m_WarmupTimer <= 0))
{
@ -122,19 +122,16 @@ void CHud::RenderGameTimer()
else
Time = (Client()->GameTick()-m_pClient->m_Snap.m_pGameInfoObj->m_RoundStartTick)/Client()->GameTickSpeed();
CServerInfo Info;
Client()->GetServerInfo(&Info);
if(Time <= 0 && g_Config.m_ClShowDecisecs)
str_format(Buf, sizeof(Buf), "00:00.0");
str_format(aBuf, sizeof(aBuf), "00:00.0");
else if(Time <= 0)
str_format(Buf, sizeof(Buf), "00:00");
else if(IsRace(&Info) && !IsDDRace(&Info) && m_ServerRecord >= 0)
str_format(Buf, sizeof(Buf), "%02d:%02d", (int)(m_ServerRecord*100)/60, ((int)(m_ServerRecord*100)%60));
str_format(aBuf, sizeof(aBuf), "00:00");
else if(GameClient()->m_GameInfo.m_RaceRecordMessage && m_ServerRecord >= 0)
str_format(aBuf, sizeof(aBuf), "%02d:%02d", (int)(m_ServerRecord*100)/60, ((int)(m_ServerRecord*100)%60));
else if(g_Config.m_ClShowDecisecs)
str_format(Buf, sizeof(Buf), "%02d:%02d.%d", Time/60, Time%60, m_DDRaceTick/10);
str_format(aBuf, sizeof(aBuf), "%02d:%02d.%d", Time/60, Time%60, m_DDRaceTick/10);
else
str_format(Buf, sizeof(Buf), "%02d:%02d", Time/60, Time%60);
str_format(aBuf, sizeof(aBuf), "%02d:%02d", Time/60, Time%60);
float FontSize = 10.0f;
float w;
if(g_Config.m_ClShowDecisecs)
@ -147,7 +144,7 @@ void CHud::RenderGameTimer()
float Alpha = Time <= 10 && (2*time_get()/time_freq()) % 2 ? 0.5f : 1.0f;
TextRender()->TextColor(1.0f, 0.25f, 0.25f, Alpha);
}
TextRender()->Text(0, Half-w/2, 2, FontSize, Buf, -1);
TextRender()->Text(0, Half-w/2, 2, FontSize, aBuf, -1);
TextRender()->TextColor(1.0f, 1.0f, 1.0f, 1.0f);
}
}
@ -338,9 +335,7 @@ void CHud::RenderScoreHud()
{
if(apPlayerInfo[t])
{
CServerInfo Info;
Client()->GetServerInfo(&Info);
if(m_pClient->TimeScore() && g_Config.m_ClDDRaceScoreBoard)
if(m_pClient->m_GameInfo.m_TimeScore && g_Config.m_ClDDRaceScoreBoard)
{
if(apPlayerInfo[t]->m_Score != -9999)
str_format(aScore[t], sizeof(aScore[t]), "%02d:%02d", abs(apPlayerInfo[t]->m_Score)/60, abs(apPlayerInfo[t]->m_Score)%60);
@ -869,13 +864,10 @@ void CHud::OnMessage(int MsgType, void *pRawMsg)
}
else if(MsgType == NETMSGTYPE_SV_RECORD)
{
CServerInfo Info;
Client()->GetServerInfo(&Info);
CNetMsg_Sv_Record *pMsg = (CNetMsg_Sv_Record *)pRawMsg;
// NETMSGTYPE_SV_RACETIME on old race servers
if(!IsDDRace(&Info) && IsRace(&Info))
if(GameClient()->m_GameInfo.m_DDRaceRecordMessage)
{
m_DDRaceTimeReceived = true;
@ -890,7 +882,7 @@ void CHud::OnMessage(int MsgType, void *pRawMsg)
m_CheckpointTick = Client()->GameTick();
}
}
else
else if(GameClient()->m_GameInfo.m_RaceRecordMessage)
{
m_ServerRecord = (float)pMsg->m_ServerTimeBest/100;
m_PlayerRecord = (float)pMsg->m_PlayerTimeBest/100;

View file

@ -103,32 +103,29 @@ void CMapImages::LoadBackground(class IMap *pMap)
int CMapImages::GetEntities()
{
CServerInfo Info;
Client()->GetServerInfo(&Info);
if(m_EntitiesTextures == -1 || str_comp(m_aEntitiesGameType, Info.m_aGameType))
{
// DDNet default to prevent delay in seeing entities
char file[64] = "ddnet";
if(IsDDNet(&Info))
str_copy(file, "ddnet", sizeof(file));
else if(IsDDRace(&Info))
str_copy(file, "ddrace", sizeof(file));
else if(IsRace(&Info))
str_copy(file, "race", sizeof(file));
else if(IsFNG(&Info))
str_copy(file, "fng", sizeof(file));
else if(IsVanilla(&Info))
str_copy(file, "vanilla", sizeof(file));
// DDNet default to prevent delay in seeing entities
const char *pEntities = "ddnet";
if(GameClient()->m_GameInfo.m_EntitiesDDNet)
pEntities = "ddnet";
else if(GameClient()->m_GameInfo.m_EntitiesDDRace)
pEntities = "ddrace";
else if(GameClient()->m_GameInfo.m_EntitiesRace)
pEntities = "race";
else if(GameClient()->m_GameInfo.m_EntitiesFNG)
pEntities = "fng";
else if(GameClient()->m_GameInfo.m_EntitiesVanilla)
pEntities = "vanilla";
if(m_EntitiesTextures == -1 || m_pEntitiesGameType != pEntities)
{
char aPath[64];
str_format(aPath, sizeof(aPath), "editor/entities_clear/%s.png", pEntities);
char path[64];
str_format(path, sizeof(path), "editor/entities_clear/%s.png", file);
if(m_EntitiesTextures >= 0)
Graphics()->UnloadTexture(m_EntitiesTextures);
m_EntitiesTextures = Graphics()->LoadTexture(path, IStorage::TYPE_ALL, CImageInfo::FORMAT_AUTO, 0);
m_EntitiesTextures = Graphics()->LoadTexture(aPath, IStorage::TYPE_ALL, CImageInfo::FORMAT_AUTO, 0);
str_copy(m_aEntitiesGameType, Info.m_aGameType, sizeof(m_aEntitiesGameType));
m_pEntitiesGameType = pEntities;
}
return m_EntitiesTextures;
}

View file

@ -11,7 +11,7 @@ class CMapImages : public CComponent
int m_aTextures[64];
int m_Count;
char m_aEntitiesGameType[16];
const char *m_pEntitiesGameType;
public:
CMapImages();
CMapImages(int ImageSize);

View file

@ -450,9 +450,6 @@ void CMapLayers::OnMapLoad()
m_QuadLayerVisuals.clear();
}
CServerInfo Info;
Client()->GetServerInfo(&Info);
bool PassedGameLayer = false;
//prepare all visuals for all tile layers
std::vector<STmpTile> tmpTiles;
@ -623,7 +620,7 @@ void CMapLayers::OnMapLoad()
{
Index = ((CTile*)pTiles)[y*pTMap->m_Width+x].m_Index;
Flags = ((CTile*)pTiles)[y*pTMap->m_Width+x].m_Flags;
if(IsDDNet(&Info) && !IsValidGameTile(Index))
if(GameClient()->m_GameInfo.m_EntitiesDDNet && !IsValidGameTile(Index))
Index = 0;
}
if(IsFrontLayer)

View file

@ -773,10 +773,8 @@ int CMenus::RenderMenubar(CUIRect r)
NewPage = PAGE_NETWORK;
{
CServerInfo Info;
Client()->GetServerInfo(&Info);
static int s_GhostButton=0;
if(IsRace(&Info) || IsDDNet(&Info))
if(GameClient()->m_GameInfo.m_Race)
{
Box.VSplitLeft(70.0f, &Button, &Box);
if(DoButton_MenuTab(&s_GhostButton, Localize("Ghost"), m_ActivePage==PAGE_GHOST, &Button, 0))

View file

@ -294,9 +294,7 @@ void CPlayers::RenderPlayer(
// draw gun
{
CServerInfo Info;
Client()->GetServerInfo(&Info);
if(ClientID >= 0 && (((IsDDRace(&Info) || IsDDNet(&Info)) && g_Config.m_ClShowHookCollAlways) || (Player.m_PlayerFlags&PLAYERFLAG_AIM && ((!Local && g_Config.m_ClShowHookCollOther) || (Local && g_Config.m_ClShowHookCollOwn)))))
if(ClientID >= 0 && ((GameClient()->m_GameInfo.m_AllowHookColl && g_Config.m_ClShowHookCollAlways) || (Player.m_PlayerFlags&PLAYERFLAG_AIM && ((!Local && g_Config.m_ClShowHookCollOther) || (Local && g_Config.m_ClShowHookCollOwn)))))
{
vec2 ExDirection = Direction;

View file

@ -51,9 +51,7 @@ void CRaceDemo::OnStateChange(int NewState, int OldState)
void CRaceDemo::OnNewSnapshot()
{
CServerInfo ServerInfo;
Client()->GetServerInfo(&ServerInfo);
if(!IsRace(&ServerInfo) || !g_Config.m_ClAutoRaceRecord || Client()->State() != IClient::STATE_ONLINE)
if(!GameClient()->m_GameInfo.m_Race || !g_Config.m_ClAutoRaceRecord || Client()->State() != IClient::STATE_ONLINE)
return;
if(!m_pClient->m_Snap.m_pGameInfoObj || m_pClient->m_Snap.m_SpecInfo.m_Active || !m_pClient->m_Snap.m_pLocalCharacter || !m_pClient->m_Snap.m_pLocalPrevCharacter)

View file

@ -30,10 +30,6 @@ CScoreboard::CScoreboard()
void CScoreboard::ConKeyScoreboard(IConsole::IResult *pResult, void *pUserData)
{
CScoreboard *pSelf = (CScoreboard *)pUserData;
CServerInfo Info;
pSelf->Client()->GetServerInfo(&Info);
pSelf->m_IsGameTypeRace = IsRace(&Info);
pSelf->m_Active = pResult->GetInteger(0) != 0;
}
@ -231,7 +227,7 @@ void CScoreboard::RenderScoreboard(float x, float y, float w, int Team, const ch
}
}
if(m_pClient->TimeScore() && g_Config.m_ClDDRaceScoreBoard)
if(m_pClient->m_GameInfo.m_TimeScore && g_Config.m_ClDDRaceScoreBoard)
{
if (m_ServerRecord > 0)
{
@ -294,7 +290,7 @@ void CScoreboard::RenderScoreboard(float x, float y, float w, int Team, const ch
// render headlines
y += 50.0f;
float HeadlineFontsize = 22.0f;
const char *pScore = (m_pClient->TimeScore() && g_Config.m_ClDDRaceScoreBoard) ? Localize("Time") : Localize("Score");
const char *pScore = (m_pClient->m_GameInfo.m_TimeScore && g_Config.m_ClDDRaceScoreBoard) ? Localize("Time") : Localize("Score");
float ScoreWidth = TextRender()->TextWidth(0, HeadlineFontsize, pScore, -1);
tw = ScoreLength > ScoreWidth ? ScoreLength : ScoreWidth;
TextRender()->Text(0, ScoreOffset+ScoreLength-tw, y + (HeadlineFontsize * 2.f - HeadlineFontsize) / 2.f, HeadlineFontsize, pScore, -1);
@ -414,7 +410,7 @@ void CScoreboard::RenderScoreboard(float x, float y, float w, int Team, const ch
}
// score
if(m_pClient->TimeScore() && g_Config.m_ClDDRaceScoreBoard)
if(m_pClient->m_GameInfo.m_TimeScore && g_Config.m_ClDDRaceScoreBoard)
{
if (pInfo->m_Score == -9999)
aBuf[0] = 0;

View file

@ -32,7 +32,6 @@ public:
private:
bool m_IsGameTypeRace;
float m_ServerRecord;
};

View file

@ -473,9 +473,6 @@ void CGameClient::OnConnected()
m_All.m_paComponents[i]->OnReset();
}
CServerInfo CurrentServerInfo;
Client()->GetServerInfo(&CurrentServerInfo);
m_ServerMode = SERVERMODE_PURE;
// send the initial info
@ -953,6 +950,110 @@ void CGameClient::ProcessEvents()
}
}
static CGameInfo GetGameInfo(const CNetObj_GameInfoEx *pInfoEx, int InfoExSize, CServerInfo *pFallbackServerInfo)
{
int Version = -1;
if(InfoExSize >= 8)
{
Version = pInfoEx->m_Version;
}
else if(InfoExSize >= 4)
{
Version = 0;
}
int Flags = 0;
if(Version >= 0)
{
Flags = pInfoEx->m_Flags;
}
bool Race;
bool FastCap;
bool FNG;
bool DDRace;
bool DDNet;
bool BlockWorlds;
bool Vanilla;
bool Plus;
if(Version < 1)
{
Race = IsRace(pFallbackServerInfo);
FastCap = IsFastCap(pFallbackServerInfo);
FNG = IsFNG(pFallbackServerInfo);
DDRace = IsDDRace(pFallbackServerInfo);
DDNet = IsDDNet(pFallbackServerInfo);
BlockWorlds = IsBlockWorlds(pFallbackServerInfo);
Vanilla = IsVanilla(pFallbackServerInfo);
Plus = IsPlus(pFallbackServerInfo);
}
else
{
Race = Flags&GAMEINFOFLAG_GAMETYPE_RACE;
FastCap = Flags&GAMEINFOFLAG_GAMETYPE_FASTCAP;
FNG = Flags&GAMEINFOFLAG_GAMETYPE_FNG;
DDRace = Flags&GAMEINFOFLAG_GAMETYPE_DDRACE;
DDNet = Flags&GAMEINFOFLAG_GAMETYPE_DDNET;
BlockWorlds = Flags&GAMEINFOFLAG_GAMETYPE_BLOCK_WORLDS;
Vanilla = Flags&GAMEINFOFLAG_GAMETYPE_VANILLA;
Plus = Flags&GAMEINFOFLAG_GAMETYPE_PLUS;
// Ensure invariants upheld by the server info parsing business.
DDRace = DDRace || DDNet;
Race = Race || FastCap || DDRace;
}
CGameInfo Info;
Info.m_FlagStartsRace = FastCap;
Info.m_TimeScore = Race;
Info.m_UnlimitedAmmo = Race;
Info.m_DDRaceRecordMessage = DDRace;
Info.m_RaceRecordMessage = Race && !DDRace;
Info.m_AllowEyeWheel = DDRace || BlockWorlds || Plus;
Info.m_AllowHookColl = DDRace;
Info.m_AllowZoom = Race || BlockWorlds;
Info.m_BugDDRaceGhost = DDRace;
Info.m_BugDDRaceInput = DDRace;
Info.m_BugFNGLaserRange = FNG;
Info.m_BugVanillaBounce = Vanilla;
Info.m_PredictFNG = FNG;
Info.m_PredictDDRace = DDRace;
Info.m_PredictDDRaceTiles = DDRace && !BlockWorlds;
Info.m_PredictVanilla = Vanilla || FastCap;
Info.m_EntitiesDDNet = DDNet;
Info.m_EntitiesDDRace = DDRace;
Info.m_EntitiesRace = Race;
Info.m_EntitiesFNG = FNG;
Info.m_EntitiesVanilla = Vanilla;
if(Version >= 0)
{
Info.m_TimeScore = Flags&GAMEINFOFLAG_TIMESCORE;
}
if(Version >= 2)
{
Info.m_FlagStartsRace = Flags&GAMEINFOFLAG_FLAG_STARTS_RACE;
Info.m_UnlimitedAmmo = Flags&GAMEINFOFLAG_UNLIMITED_AMMO;
Info.m_DDRaceRecordMessage = Flags&GAMEINFOFLAG_DDRACE_RECORD_MESSAGE;
Info.m_RaceRecordMessage = Flags&GAMEINFOFLAG_RACE_RECORD_MESSAGE;
Info.m_AllowEyeWheel = Flags&GAMEINFOFLAG_ALLOW_EYE_WHEEL;
Info.m_AllowHookColl = Flags&GAMEINFOFLAG_ALLOW_HOOK_COLL;
Info.m_AllowZoom = Flags&GAMEINFOFLAG_ALLOW_ZOOM;
Info.m_BugDDRaceGhost = Flags&GAMEINFOFLAG_BUG_DDRACE_GHOST;
Info.m_BugDDRaceInput = Flags&GAMEINFOFLAG_BUG_DDRACE_INPUT;
Info.m_BugFNGLaserRange = Flags&GAMEINFOFLAG_BUG_FNG_LASER_RANGE;
Info.m_BugVanillaBounce = Flags&GAMEINFOFLAG_BUG_VANILLA_BOUNCE;
Info.m_PredictFNG = Flags&GAMEINFOFLAG_PREDICT_FNG;
Info.m_PredictDDRace = Flags&GAMEINFOFLAG_PREDICT_DDRACE;
Info.m_PredictDDRaceTiles = Flags&GAMEINFOFLAG_PREDICT_DDRACE_TILES;
Info.m_PredictVanilla = Flags&GAMEINFOFLAG_PREDICT_VANILLA;
Info.m_EntitiesDDNet = Flags&GAMEINFOFLAG_ENTITIES_DDNET;
Info.m_EntitiesDDRace = Flags&GAMEINFOFLAG_ENTITIES_DDRACE;
Info.m_EntitiesRace = Flags&GAMEINFOFLAG_ENTITIES_RACE;
Info.m_EntitiesFNG = Flags&GAMEINFOFLAG_ENTITIES_FNG;
Info.m_EntitiesVanilla = Flags&GAMEINFOFLAG_ENTITIES_VANILLA;
}
return Info;
}
void CGameClient::OnNewSnapshot()
{
m_NewTick = true;
@ -1002,6 +1103,8 @@ void CGameClient::OnNewSnapshot()
}
#endif
bool FoundGameInfoEx = false;
// go trough all the items in the snapshot and gather the info we want
{
m_Snap.m_aTeamSize[TEAM_RED] = m_Snap.m_aTeamSize[TEAM_BLUE] = 0;
@ -1174,9 +1277,16 @@ void CGameClient::OnNewSnapshot()
s_GameOver = CurrentTickGameOver;
s_GamePaused = (bool)(m_Snap.m_pGameInfoObj->m_GameStateFlags & GAMESTATEFLAG_PAUSED);
}
else if(Item.m_Type == NETOBJTYPE_DDNETGAMEINFO)
else if(Item.m_Type == NETOBJTYPE_GAMEINFOEX)
{
m_Snap.m_pGameInfoEx = (const CNetObj_DDNetGameInfo *)pData;
if(FoundGameInfoEx)
{
continue;
}
FoundGameInfoEx = true;
CServerInfo ServerInfo;
Client()->GetServerInfo(&ServerInfo);
m_GameInfo = GetGameInfo((const CNetObj_GameInfoEx *)pData, Client()->SnapItemSize(IClient::SNAP_CURRENT, i), &ServerInfo);
}
else if(Item.m_Type == NETOBJTYPE_GAMEDATA)
{
@ -1209,6 +1319,13 @@ void CGameClient::OnNewSnapshot()
}
}
if(!FoundGameInfoEx)
{
CServerInfo ServerInfo;
Client()->GetServerInfo(&ServerInfo);
m_GameInfo = GetGameInfo(0, 0, &ServerInfo);
}
// setup local pointers
if(m_Snap.m_LocalClientID >= 0)
{
@ -1276,21 +1393,19 @@ void CGameClient::OnNewSnapshot()
return str_comp_nocase(m_aClients[p1->m_ClientID].m_aName, m_aClients[p2->m_ClientID].m_aName) < 0;
});
CServerInfo CurrentServerInfo;
Client()->GetServerInfo(&CurrentServerInfo);
bool IsGameTypeRace = IsRace(&CurrentServerInfo);
bool TimeScore = m_GameInfo.m_TimeScore;
// sort player infos by score
mem_copy(m_Snap.m_paInfoByScore, m_Snap.m_paInfoByName, sizeof(m_Snap.m_paInfoByScore));
std::stable_sort(m_Snap.m_paInfoByScore, m_Snap.m_paInfoByScore + MAX_CLIENTS,
[IsGameTypeRace](const CNetObj_PlayerInfo* p1, const CNetObj_PlayerInfo* p2) -> bool
[TimeScore](const CNetObj_PlayerInfo* p1, const CNetObj_PlayerInfo* p2) -> bool
{
if (!p2)
return static_cast<bool>(p1);
if (!p1)
return false;
return (((IsGameTypeRace && p1->m_Score == -9999) ? std::numeric_limits<int>::min() : p1->m_Score) >
((IsGameTypeRace && p2->m_Score == -9999) ? std::numeric_limits<int>::min() : p2->m_Score));
return (((TimeScore && p1->m_Score == -9999) ? std::numeric_limits<int>::min() : p1->m_Score) >
((TimeScore && p2->m_Score == -9999) ? std::numeric_limits<int>::min() : p2->m_Score));
});
// sort player infos by DDRace Team (and score between)
@ -1304,6 +1419,8 @@ void CGameClient::OnNewSnapshot()
}
}
CServerInfo CurrentServerInfo;
Client()->GetServerInfo(&CurrentServerInfo);
CTuningParams StandardTuning;
if(CurrentServerInfo.m_aGameType[0] != '0')
{
@ -1875,14 +1992,11 @@ void CGameClient::UpdatePrediction()
m_TeamsPredicted = m_Teams;
CServerInfo CurrentServerInfo;
Client()->GetServerInfo(&CurrentServerInfo);
m_GameWorld.m_WorldConfig.m_IsVanilla = IsVanilla(&CurrentServerInfo) || IsFastCap(&CurrentServerInfo);
m_GameWorld.m_WorldConfig.m_IsDDRace = IsDDRace(&CurrentServerInfo);
m_GameWorld.m_WorldConfig.m_IsFNG = IsFNG(&CurrentServerInfo);
m_GameWorld.m_WorldConfig.m_IsVanilla = m_GameInfo.m_PredictVanilla;
m_GameWorld.m_WorldConfig.m_IsDDRace = m_GameInfo.m_PredictDDRace;
m_GameWorld.m_WorldConfig.m_IsFNG = m_GameInfo.m_PredictFNG;
m_GameWorld.m_WorldConfig.m_PredictDDRace = g_Config.m_ClPredictDDRace;
m_GameWorld.m_WorldConfig.m_PredictTiles = g_Config.m_ClPredictDDRace && m_GameWorld.m_WorldConfig.m_IsDDRace && !IsBlockWorlds(&CurrentServerInfo);
m_GameWorld.m_WorldConfig.m_PredictTiles = g_Config.m_ClPredictDDRace && m_GameInfo.m_PredictDDRaceTiles;
m_GameWorld.m_WorldConfig.m_PredictFreeze = g_Config.m_ClPredictFreeze;
m_GameWorld.m_WorldConfig.m_PredictWeapons = AntiPingWeapons();
if(m_Snap.m_pLocalCharacter->m_AmmoCount > 0 && m_Snap.m_pLocalCharacter->m_Weapon != WEAPON_NINJA)
@ -1984,13 +2098,6 @@ void CGameClient::UpdatePrediction()
}
}
bool CGameClient::TimeScore()
{
CServerInfo Info;
Client()->GetServerInfo(&Info);
return m_Snap.m_pGameInfoEx ? m_Snap.m_pGameInfoEx->m_Flags & GAMEINFOFLAG_TIMESCORE : IsRace(&Info);
}
void CGameClient::UpdateRenderedCharacters()
{
for(int i = 0; i < MAX_CLIENTS; i++)

View file

@ -59,6 +59,37 @@ public:
void Deactivate() { m_Active = 0; }
};
class CGameInfo
{
public:
bool m_FlagStartsRace;
bool m_TimeScore;
bool m_Race;
bool m_UnlimitedAmmo;
bool m_DDRaceRecordMessage;
bool m_RaceRecordMessage;
bool m_AllowEyeWheel;
bool m_AllowHookColl;
bool m_AllowZoom;
bool m_BugDDRaceGhost;
bool m_BugDDRaceInput;
bool m_BugFNGLaserRange;
bool m_BugVanillaBounce;
bool m_PredictFNG;
bool m_PredictDDRace;
bool m_PredictDDRaceTiles;
bool m_PredictVanilla;
bool m_EntitiesDDNet;
bool m_EntitiesDDRace;
bool m_EntitiesRace;
bool m_EntitiesFNG;
bool m_EntitiesVanilla;
};
class CGameClient : public IGameClient
{
class CStack
@ -164,6 +195,7 @@ public:
SERVERMODE_PUREMOD,
};
int m_ServerMode;
CGameInfo m_GameInfo;
int m_DemoSpecID;
@ -192,8 +224,6 @@ public:
//const CNetObj_PlayerInfo *m_paInfoByTeam[MAX_CLIENTS];
const CNetObj_PlayerInfo *m_paInfoByDDTeam[MAX_CLIENTS];
const CNetObj_DDNetGameInfo *m_pGameInfoEx;
int m_LocalClientID;
int m_NumPlayers;
int m_aTeamSize[2];
@ -228,8 +258,6 @@ public:
CSnapState m_Snap;
bool TimeScore();
// client data
struct CClientData
{

View file

@ -70,12 +70,8 @@ int CRaceHelper::TimeFromFinishMessage(const char *pStr, char *pNameBuf, int Nam
bool CRaceHelper::IsStart(CGameClient *pClient, vec2 Prev, vec2 Pos)
{
CServerInfo ServerInfo;
pClient->Client()->GetServerInfo(&ServerInfo);
CCollision *pCollision = pClient->Collision();
if(IsFastCap(&ServerInfo))
if(pClient->m_GameInfo.m_FlagStartsRace)
{
int EnemyTeam = pClient->m_aClients[pClient->m_Snap.m_LocalClientID].m_Team ^ 1;
return ms_aFlagIndex[EnemyTeam] != -1 && distance(Pos, pCollision->GetPos(ms_aFlagIndex[EnemyTeam])) < 32;

View file

@ -1257,7 +1257,7 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID)
}
else
{
if(g_Config.m_SvSpamprotection && str_comp_nocase_num(pMsg->m_pMessage+1, "timeout ", 8) != 0
if(g_Config.m_SvSpamprotection && !str_startswith(pMsg->m_pMessage+1, "timeout ")
&& pPlayer->m_LastCommands[0] && pPlayer->m_LastCommands[0]+Server()->TickSpeed() > Server()->Tick()
&& pPlayer->m_LastCommands[1] && pPlayer->m_LastCommands[1]+Server()->TickSpeed() > Server()->Tick()
&& pPlayer->m_LastCommands[2] && pPlayer->m_LastCommands[2]+Server()->TickSpeed() > Server()->Tick()

View file

@ -535,11 +535,28 @@ void IGameController::Snap(int SnappingClient)
}
}
CNetObj_DDNetGameInfo *pGameInfoEx = (CNetObj_DDNetGameInfo *)Server()->SnapNewItem(NETOBJTYPE_DDNETGAMEINFO, 0, sizeof(CNetObj_DDNetGameInfo));
CNetObj_GameInfoEx *pGameInfoEx = (CNetObj_GameInfoEx *)Server()->SnapNewItem(NETOBJTYPE_GAMEINFOEX, 0, sizeof(CNetObj_GameInfoEx));
if(!pGameInfoEx)
return;
pGameInfoEx->m_Flags = GAMEINFOFLAG_TIMESCORE;
pGameInfoEx->m_Flags = 0
| GAMEINFOFLAG_TIMESCORE
| GAMEINFOFLAG_GAMETYPE_RACE
| GAMEINFOFLAG_GAMETYPE_DDRACE
| GAMEINFOFLAG_GAMETYPE_DDNET
| GAMEINFOFLAG_UNLIMITED_AMMO
| GAMEINFOFLAG_DDRACE_RECORD_MESSAGE
| GAMEINFOFLAG_ALLOW_EYE_WHEEL
| GAMEINFOFLAG_ALLOW_HOOK_COLL
| GAMEINFOFLAG_ALLOW_ZOOM
| GAMEINFOFLAG_BUG_DDRACE_GHOST
| GAMEINFOFLAG_BUG_DDRACE_INPUT
| GAMEINFOFLAG_PREDICT_DDRACE
| GAMEINFOFLAG_PREDICT_DDRACE_TILES
| GAMEINFOFLAG_ENTITIES_DDNET
| GAMEINFOFLAG_ENTITIES_DDRACE
| GAMEINFOFLAG_ENTITIES_RACE;
pGameInfoEx->m_Version = GAMEINFO_CURVERSION;
}
int IGameController::GetAutoTeam(int NotThisID)