diff --git a/README.md b/README.md index 2f41d25..15439cd 100644 --- a/README.md +++ b/README.md @@ -15,8 +15,16 @@ You can try playing on one of many servers that are using ReHLDS: [Game Tracker] ## How can use it? -ReHLDS is fully compatible with latest official HLDS downloaded by steamcmd. All you have to do is to download rehlds binaries and replace original swds.dll/engine_i486.so. For windows you can also copy a swds.pdb file with a debug information. -
Warning! ReHLDS is not compatible with an old 5xxx or below platforms downloaded by hldsupdatetool. +ReHLDS is fully compatible with the official pre-anniversary edition of HLDS (engine version <= 8684) downloaded by steamcmd. All you have to do is to download rehlds binaries and replace original swds.dll/engine_i486.so. For windows you can also copy a swds.pdb file with a debug information. + +Warning! ReHLDS is not compatible with an old 5xxx or below platforms downloaded by hldsupdatetool. + +#### Downloading HLDS via steamcmd + +``` +app_set_config 90 mod cstrike +app_update 90 -beta steam_legacy validate +``` ## Downloads * [Release builds](https://github.com/dreamstalker/rehlds/releases) @@ -31,20 +39,20 @@ This means that plugins that do binary code analysis (Orpheu for example) probab
Click to expand
@@ -62,6 +71,9 @@ This means that plugins that do binary code analysis (Orpheu for example) probab ## Build instructions diff --git a/rehlds/engine/common.cpp b/rehlds/engine/common.cpp index e70d03c..3ce8aa4 100644 --- a/rehlds/engine/common.cpp +++ b/rehlds/engine/common.cpp @@ -586,9 +586,9 @@ void MSG_WriteBitData(void *src, int length) void MSG_WriteBitAngle(float fAngle, int numbits) { - if (numbits >= 32) + if (numbits > 22) { - Sys_Error("%s: Can't write bit angle with 32 bits precision\n", __func__); + Sys_Error("%s: Can't write bit angle with more than 22 bits precision\n", __func__); } uint32 shift = (1 << numbits); diff --git a/rehlds/engine/filter.h b/rehlds/engine/filter.h index b2a080b..84c7afe 100644 --- a/rehlds/engine/filter.h +++ b/rehlds/engine/filter.h @@ -42,9 +42,7 @@ typedef struct ipfilter_s } compare; float banEndTime; float banTime; -#ifdef REHLDS_FIXES int cidr; -#endif // REHLDS_FIXES } ipfilter_t; typedef struct userfilter_s diff --git a/rehlds/engine/host_cmd.cpp b/rehlds/engine/host_cmd.cpp index 993e56a..79996ab 100644 --- a/rehlds/engine/host_cmd.cpp +++ b/rehlds/engine/host_cmd.cpp @@ -610,7 +610,6 @@ void Host_Status_f(void) Host_Status_Printf(conprint, log, "players : %i active (%i max)\n\n", nClients, g_psvs.maxclients); Host_Status_Printf(conprint, log, "# name userid uniqueid frag time ping loss adr\n"); - int count = 1; client = g_psvs.clients; for (j = 0; j < g_psvs.maxclients; j++, client++) { @@ -634,7 +633,7 @@ void Host_Status_f(void) val = SV_GetClientIDString(client); else val = "BOT"; - Host_Status_Printf(conprint, log, "#%2i %8s %i %s", count++, va("\"%s\"", client->name), client->userid, val); + Host_Status_Printf(conprint, log, "#%2i %8s %i %s", j + 1, va("\"%s\"", client->name), client->userid, val); if (client->proxy) { const char *userInfo = Info_ValueForKey(client->userinfo, "hspecs"); @@ -724,7 +723,6 @@ void Host_Status_Formatted_f(void) Host_Status_Printf(conprint, log, "players : %i active (%i max)\n\n", nClients, g_psvs.maxclients); Host_Status_Printf(conprint, log, "%-2.2s\t%-9.9s\t%-7.7s\t%-20.20s\t%-4.4s\t%-8.8s\t%-4.4s\t%-4.4s\t%-21.21s\n","# ","name","userid ","uniqueid ","frag","time ","ping","loss","adr"); - int count = 1; char *szRemoteAddr; client = g_psvs.clients; for (j = 0; j < g_psvs.maxclients; j++, client++) @@ -755,7 +753,7 @@ void Host_Status_Formatted_f(void) #endif // REHLDS_FIXES szIDString = SV_GetClientIDString(client); Host_Status_Printf(conprint, log, "%-2.2s\t%-9.9s\t%-7.7s\t%-20.20s\t%-4.4s\t%-8.8s\t%-4.4s\t%-4.4s\t%-21.21s\n", - va("%-2i", count++),va("\"%s\"", client->name),va("%-7i", client->userid),szIDString, + va("%-2i", j + 1),va("\"%s\"", client->name),va("%-7i", client->userid),szIDString, va("%-4i", (int)client->edict->v.frags),sz,va("%-4i", SV_CalcPing(client)),va("%-4i", (int)client->packet_loss),szRemoteAddr); } Host_Status_Printf(conprint, log, "%i users\n", nClients); diff --git a/rehlds/engine/server.h b/rehlds/engine/server.h index 5fda2c1..c59332e 100644 --- a/rehlds/engine/server.h +++ b/rehlds/engine/server.h @@ -366,6 +366,7 @@ extern cvar_t sv_visiblemaxplayers; extern cvar_t sv_downloadurl; extern cvar_t sv_allow_dlfile; extern cvar_t sv_version; +extern cvar_t sv_tags; #ifdef REHLDS_FIXES extern cvar_t sv_echo_unknown_cmd; extern cvar_t sv_auto_precache_sounds_in_models; @@ -587,6 +588,9 @@ void SV_ClearEntities(void); int RegUserMsg(const char *pszName, int iSize); qboolean StringToFilter(const char *s, ipfilter_t *f); USERID_t *SV_StringToUserID(const char *str); +bool CanBeWrittenWithoutCIDR(const ipfilter_t &f); +void FilterToString(const ipfilter_t &f, char *s); +bool IsFilterIncludesAnotherFilter(const ipfilter_t &f, const ipfilter_t &f2); void SV_BanId_f(void); void Host_Kick_f(void); void SV_RemoveId_f(void); diff --git a/rehlds/engine/sv_main.cpp b/rehlds/engine/sv_main.cpp index 6d0387d..870a5fa 100644 --- a/rehlds/engine/sv_main.cpp +++ b/rehlds/engine/sv_main.cpp @@ -193,6 +193,8 @@ cvar_t sv_version = { "sv_version", "", FCVAR_SERVER, 0.0f, NULL }; cvar_t sv_version = {"sv_version", "", 0, 0.0f, NULL}; #endif +cvar_t sv_tags = { "sv_tags", "", 0, 0.0f, NULL }; + cvar_t sv_rcon_minfailures = { "sv_rcon_minfailures", "5", 0, 0.0f, NULL }; cvar_t sv_rcon_maxfailures = { "sv_rcon_maxfailures", "10", 0, 0.0f, NULL }; cvar_t sv_rcon_minfailuretime = { "sv_rcon_minfailuretime", "30", 0, 0.0f, NULL }; @@ -3274,6 +3276,159 @@ void SV_ResetRcon_f(void) Q_memset(g_rgRconFailures, 0, sizeof(g_rgRconFailures)); } +const int MAX_RCON_USERS = 128; +ipfilter_t rconusers[MAX_RCON_USERS]; +int numrconusers = 0; + +qboolean SV_CheckRconAllowed(const netadr_t *adr) +{ + if (numrconusers <= 0) + return TRUE; // Rcon user list empty so assume allowed it for all + + for (int i = numrconusers - 1; i >= 0; i--) + { + ipfilter_t *curFilter = &rconusers[i]; + if (curFilter->compare.u32 == 0xFFFFFFFF || (*(uint32*)adr->ip & curFilter->mask) == curFilter->compare.u32) + return TRUE; + } + + return FALSE; +} + +void SV_RconAddUser_f(void) +{ + if (Cmd_Argc() != 2) + { + Con_Printf("Usage: rcon_adduser \n" + "ipaddress A.B.C.D/24 is equivalent to A.B.C.0 and A.B.C\n"); + return; + } + + ipfilter_t tempFilter; + if (!StringToFilter(Cmd_Argv(1), &tempFilter)) + { + Con_Printf("Invalid IP address!\nUsage: rcon_adduser \n"); + return; + } + + int i = 0; + for (; i < numrconusers; i++) + { + if (rconusers[i].mask == tempFilter.mask && rconusers[i].compare.u32 == tempFilter.compare.u32) + { + rconusers[i].cidr = tempFilter.cidr; + return; + } + } + + if (numrconusers >= MAX_RCON_USERS) + { + Con_Printf("IP rcon users is full\n"); + return; + } + + numrconusers++; + rconusers[i].compare = tempFilter.compare; + rconusers[i].mask = tempFilter.mask; + rconusers[i].cidr = tempFilter.cidr; +} + +void SV_RconDelUser_f(void) +{ + int argCount = Cmd_Argc(); + if (argCount != 2 && argCount != 3) + { + Con_Printf("Usage: rcon_deluser {removeAll}\n" + "removeip {removeAll}\n" + "Use removeAll to delete all Rcon ip users which ipaddress or ipaddress/CIDR includes\n"); + + return; + } + + ipfilter_t f; + + if (!StringToFilter(Cmd_Argv(1), &f)) + { + Con_Printf("Invalid IP address\n" + "Usage: rcon_deluser {removeAll}\n" + " rcon_deluser {removeAll}\n" + "Use removeAll to delete all Rcon ip users which ipaddress or ipaddress/CIDR includes\n"); + return; + } + + bool found = false; + for (int i = 0; i < numrconusers; i++) + { + if ((argCount == 2 && rconusers[i].mask == f.mask && rconusers[i].compare.u32 == f.compare.u32) || + (argCount == 3 && IsFilterIncludesAnotherFilter(f, rconusers[i]))) + { + if (i + 1 < numrconusers) + Q_memmove(&rconusers[i], &rconusers[i + 1], (numrconusers - (i + 1)) * sizeof(ipfilter_t)); + numrconusers--; + rconusers[numrconusers].banTime = 0.0f; + rconusers[numrconusers].banEndTime = 0.0f; + rconusers[numrconusers].compare.u32 = 0; + rconusers[numrconusers].mask = 0; + found = true; + --i; + + if (argCount == 2) + break; + } + } + + if (found) + Con_Printf("Rcon user IP removed.\n"); + + else + { + Con_Printf("rcon_deluser: couldn't find %s.\n", Cmd_Argv(1)); + } +} + +void SV_RconUsers_f(void) +{ + if (numrconusers <= 0) + { + Con_Printf("Rcon user IP list: empty\n"); + return; + } + + bool isNew = Cmd_Argc() == 2; + bool searchByFilter = isNew && isdigit(Cmd_Argv(1)[0]); + ipfilter_t filter; + + if (searchByFilter) + { + if (!StringToFilter(Cmd_Argv(1), &filter)) + return; + + Con_Printf("Rcon user IP list for %s:\n", Cmd_Argv(1)); + } + else + { + Con_Printf("Rcon user IP list:\n"); + } + + for (int i = 0; i < numrconusers; i++) + { + uint8 *b = rconusers[i].compare.octets; + if (isNew) + { + if (!searchByFilter || IsFilterIncludesAnotherFilter(filter, rconusers[i])) + { + char strFilter[32]; + FilterToString(rconusers[i], strFilter); + Con_Printf("%-18s\n", strFilter); + } + } + else if (CanBeWrittenWithoutCIDR(rconusers[i])) + { + Con_Printf("%3i.%3i.%3i.%3i\n", b[0], b[1], b[2], b[3]); + } + } +} + void SV_AddFailedRcon(netadr_t *adr) { int i; @@ -3402,10 +3557,21 @@ qboolean SV_CheckRconFailure(netadr_t *adr) return FALSE; } +#define RCON_RESULT_SUCCESS 0 // allow the rcon +#define RCON_RESULT_BADPASSWORD 1 // reject it, bad password +#define RCON_RESULT_BADCHALLENGE 2 // bad challenge +#define RCON_RESULT_BANNING 3 // decline it, banning for rcon hacking attempts +#define RCON_RESULT_NOSETPASSWORD 4 // rcon password is not set +#define RCON_RESULT_NOPRIVILEGE 5 // user attempt with valid password but is not privileged + int SV_Rcon_Validate(void) { - if (Cmd_Argc() < 3 || Q_strlen(rcon_password.string) == 0) - return 1; + if (Cmd_Argc() < 3) + return RCON_RESULT_BADPASSWORD; + + // Must have a password set to allow any rconning + if (Q_strlen(rcon_password.string) == 0) + return RCON_RESULT_NOSETPASSWORD; if (sv_rcon_banpenalty.value < 0.0f) Cvar_SetValue("sv_rcon_banpenalty", 0.0); @@ -3414,78 +3580,105 @@ int SV_Rcon_Validate(void) { Con_Printf("Banning %s for rcon hacking attempts\n", NET_AdrToString(net_from)); Cbuf_AddText(va("addip %i %s\n", (int)sv_rcon_banpenalty.value, NET_BaseAdrToString(net_from))); - return 3; + return RCON_RESULT_BANNING; } if (!SV_CheckChallenge(&net_from, Q_atoi(Cmd_Argv(1)))) - return 2; + return RCON_RESULT_BADCHALLENGE; // The client is spoofing... + // If the pw does not match, then disallow command if (Q_strcmp(Cmd_Argv(2), rcon_password.string)) { SV_AddFailedRcon(&net_from); - return 1; + return RCON_RESULT_BADPASSWORD; } - return 0; + + if (!SV_CheckRconAllowed(&net_from)) + { + Con_Printf("Banning %s for rcon attempts without privileged\n", NET_AdrToString(net_from)); + Cbuf_AddText(va("addip %i %s\n", (int)sv_rcon_banpenalty.value, NET_BaseAdrToString(net_from))); + return RCON_RESULT_NOPRIVILEGE; + } + + // Otherwise it's ok + return RCON_RESULT_SUCCESS; } +// A client issued an rcom command +// Shift down the remaining args and redirect all Con_Printf void SV_Rcon(netadr_t *net_from_) { - char remaining[512]; - char rcon_buff[1024]; + int invalid; + char remaining[1024]; + char rcon_buff[512]; + int len; - int invalid = SV_Rcon_Validate(); - int len = net_message.cursize - Q_strlen("rcon"); - if (len <= 0 || len >= sizeof(remaining)) + // Verify this user has access rights + invalid = SV_Rcon_Validate(); + + len = net_message.cursize - Q_strlen("rcon"); + if (len <= 0 || len >= sizeof(rcon_buff)) return; - Q_memcpy(remaining, &net_message.data[Q_strlen("rcon")], len); - remaining[len] = 0; + Q_memcpy(rcon_buff, &net_message.data[Q_strlen("rcon")], len); + rcon_buff[len] = 0; #ifdef REHLDS_FIXES if (sv_rcon_condebug.value > 0.0f) #endif { - if (invalid) + if (invalid != RCON_RESULT_SUCCESS) { - Con_Printf("Bad Rcon from %s:\n%s\n", NET_AdrToString(*net_from_), remaining); - Log_Printf("Bad Rcon: \"%s\" from \"%s\"\n", remaining, NET_AdrToString(*net_from_)); + Con_Printf("Bad Rcon from %s:\n%s\n", NET_AdrToString(*net_from_), rcon_buff); + Log_Printf("Bad Rcon: \"%s\" from \"%s\"\n", rcon_buff, NET_AdrToString(*net_from_)); } else { - Con_Printf("Rcon from %s:\n%s\n", NET_AdrToString(*net_from_), remaining); - Log_Printf("Rcon: \"%s\" from \"%s\"\n", remaining, NET_AdrToString(*net_from_)); + Con_Printf("Rcon from %s:\n%s\n", NET_AdrToString(*net_from_), rcon_buff); + Log_Printf("Rcon: \"%s\" from \"%s\"\n", rcon_buff, NET_AdrToString(*net_from_)); } } SV_BeginRedirect(RD_PACKET, net_from_); - if (invalid) + switch (invalid) { - if (invalid == 2) - Con_Printf("Bad rcon_password.\n"); - else if (Q_strlen(rcon_password.string) == 0) - Con_Printf("Bad rcon_password.\nNo password set for this server.\n"); + case RCON_RESULT_SUCCESS: + { + char *data; + data = COM_Parse(rcon_buff); + data = COM_Parse(data); + data = COM_Parse(data); + + if (data) + { + Q_strncpy(remaining, data, sizeof(remaining) - 1); + remaining[sizeof(remaining) - 1] = 0; + + Cmd_ExecuteString(remaining, src_command); + } else - Con_Printf("Bad rcon_password.\n"); + { + Con_Printf("Empty rcon\n"); + } - SV_EndRedirect(); - return; + break; } - char *data = COM_Parse(COM_Parse(COM_Parse(remaining))); - if (!data) - { - Con_Printf("Empty rcon\n"); - -#ifdef REHLDS_FIXES - //missing SV_EndRedirect() - SV_EndRedirect(); -#endif // REHLDS_FIXES - return; + case RCON_RESULT_BANNING: + case RCON_RESULT_BADPASSWORD: + Con_Printf("Bad rcon_password.\n"); + break; + case RCON_RESULT_NOPRIVILEGE: + Con_Printf("Bad rcon_password.\nNo privilege.\n"); + break; + case RCON_RESULT_NOSETPASSWORD: + Con_Printf("Bad rcon_password.\nNo password set for this server.\n"); + break; + case RCON_RESULT_BADCHALLENGE: + Con_Printf("Bad rcon_password.\nBad challenge.\n"); + break; } - Q_strncpy(rcon_buff, data, sizeof(rcon_buff) - 1); - rcon_buff[sizeof(rcon_buff) - 1] = 0; - Cmd_ExecuteString(rcon_buff, src_command); SV_EndRedirect(); } @@ -4698,30 +4891,38 @@ void SV_WriteEntitiesToClient(client_t *client, sizebuf_t *msg) auto &entityState = curPack->entities[i]; if (entityState.number > MAX_CLIENTS) { - if (entityState.movetype == MOVETYPE_FOLLOW && entityState.aiment > 0) + if (entityState.movetype == MOVETYPE_FOLLOW) { - if (sv_rehlds_attachedentities_playeranimationspeed_fix.string[0] == '1' && - entityState.aiment <= MAX_CLIENTS) + if (entityState.aiment > 0 && entityState.aiment < g_psv.num_edicts) { - attachedEntCount[entityState.aiment]++; - } + if (sv_rehlds_attachedentities_playeranimationspeed_fix.string[0] == '1' && + entityState.aiment <= MAX_CLIENTS) + { + attachedEntCount[entityState.aiment]++; + } - // Prevent crash "Cache_UnlinkLRU: NULL link" on client-side - // if aiment with sprite model will be to render as a studio model - if (entityState.aiment < g_psv.num_edicts) - { + // Prevent crash "Cache_UnlinkLRU: NULL link" on client-side + // if aiment with sprite model will be to render as a studio model edict_t *ent = &g_psv.edicts[entityState.aiment]; - if ((ent->v.modelindex >= 0 && ent->v.modelindex < MAX_MODELS) - && g_psv.models[ent->v.modelindex]->type != mod_studio) + if (ent->v.modelindex >= 0 && ent->v.modelindex < MAX_MODELS + && (!g_psv.models[ent->v.modelindex] + || g_psv.models[ent->v.modelindex]->type != mod_studio)) { entityState.aiment = 0; + entityState.movetype = MOVETYPE_NONE; } } + else + { + entityState.aiment = 0; + entityState.movetype = MOVETYPE_NONE; + } } // Prevent spam "Non-sprite set to glow!" in console on client-side if (entityState.rendermode == kRenderGlow && (entityState.modelindex >= 0 && entityState.modelindex < MAX_MODELS) + && g_psv.models[entityState.modelindex] && g_psv.models[entityState.modelindex]->type != mod_sprite) { entityState.rendermode = kRenderNormal; @@ -6370,7 +6571,6 @@ int EXT_FUNC RegUserMsg(const char *pszName, int iSize) return pNewMsg->iMsg; } -#ifdef REHLDS_FIXES uint32_t CIDRToMask(int cidr) { return htonl(0xFFFFFFFFull << (32 - cidr)); @@ -6520,44 +6720,6 @@ qboolean StringToFilter(const char *s, ipfilter_t *f) return true; } -#else // REHLDS_FIXES -qboolean StringToFilter(const char *s, ipfilter_t *f) -{ - char num[128]; - unsigned char b[4] = { 0, 0, 0, 0 }; - unsigned char m[4] = { 0, 0, 0, 0 }; - - const char* cc = s; - int i = 0; - while (1) - { - if (*cc < '0' || *cc > '9') - break; - - int j = 0; - while (*cc >= '0' && *cc <= '9') - num[j++] = *(cc++); - - num[j] = 0; - b[i] = Q_atoi(num); - if (b[i]) - m[i] = -1; - - if (*cc) - { - ++cc; - ++i; - if (i < 4) - continue; - } - f->mask = *(uint32 *)m; - f->compare.u32 = *(uint32 *)b; - return TRUE; - } - Con_Printf("Bad filter address: %s\n", cc); - return FALSE; -} -#endif // REHLDS_FIXES USERID_t *SV_StringToUserID(const char *str) { @@ -7995,6 +8157,11 @@ void SV_Init(void) Cmd_AddCommand("listid", SV_ListId_f); Cmd_AddCommand("writeid", SV_WriteId_f); Cmd_AddCommand("resetrcon", SV_ResetRcon_f); +#ifdef REHLDS_FIXES + Cmd_AddCommand("rcon_adduser", SV_RconAddUser_f); + Cmd_AddCommand("rcon_deluser", SV_RconDelUser_f); + Cmd_AddCommand("rcon_users", SV_RconUsers_f); +#endif Cmd_AddCommand("logaddress", SV_SetLogAddress_f); Cmd_AddCommand("logaddress_add", SV_AddLogAddress_f); Cmd_AddCommand("logaddress_del", SV_DelLogAddress_f); @@ -8109,6 +8276,7 @@ void SV_Init(void) Cvar_RegisterVariable(&sv_version); Cvar_RegisterVariable(&sv_allow_dlfile); #ifdef REHLDS_FIXES + Cvar_RegisterVariable(&sv_tags); Cvar_RegisterVariable(&sv_force_ent_intersection); Cvar_RegisterVariable(&sv_echo_unknown_cmd); Cvar_RegisterVariable(&sv_auto_precache_sounds_in_models); diff --git a/rehlds/engine/sv_steam3.cpp b/rehlds/engine/sv_steam3.cpp index ad288fa..c35b293 100644 --- a/rehlds/engine/sv_steam3.cpp +++ b/rehlds/engine/sv_steam3.cpp @@ -232,6 +232,10 @@ CSteam3Server::CSteam3Server() : m_CallbackLogonFailure(this, &CSteam3Server::OnLogonFailure), m_SteamIDGS(1, 0, k_EUniverseInvalid, k_EAccountTypeInvalid) { +#ifdef REHLDS_FIXES + m_GameTagsData[0] = '\0'; +#endif + m_bHasActivePlayers = false; m_bWantToBeSecure = false; m_bLanOnly = false; @@ -499,6 +503,21 @@ void CSteam3Server::RunFrame() } } +void CSteam3Server::UpdateGameTags() +{ +#ifdef REHLDS_FIXES + if (!m_GameTagsData[0] && !sv_tags.string[0]) + return; + + if (m_GameTagsData[0] && !Q_stricmp(m_GameTagsData, sv_tags.string)) + return; + + Q_strlcpy(m_GameTagsData, sv_tags.string); + Q_strlwr(m_GameTagsData); + CRehldsPlatformHolder::get()->SteamGameServer()->SetGameTags(m_GameTagsData); +#endif +} + void CSteam3Server::SendUpdatedServerDetails() { int botCount = 0; @@ -521,6 +540,8 @@ void CSteam3Server::SendUpdatedServerDetails() CRehldsPlatformHolder::get()->SteamGameServer()->SetBotPlayerCount(botCount); CRehldsPlatformHolder::get()->SteamGameServer()->SetServerName(Cvar_VariableString("hostname")); CRehldsPlatformHolder::get()->SteamGameServer()->SetMapName(g_psv.name); + + UpdateGameTags(); } void CSteam3Client::Shutdown() diff --git a/rehlds/engine/sv_steam3.h b/rehlds/engine/sv_steam3.h index 1aa2e51..3238dc8 100644 --- a/rehlds/engine/sv_steam3.h +++ b/rehlds/engine/sv_steam3.h @@ -54,6 +54,8 @@ protected: bool InitModule(); }; +#define MAX_STEAM_TAGS_LENGTH 128 // Steam doesn't send tags string more than 128 bytes + class CSteam3Server: public CSteam3 { public: @@ -71,6 +73,10 @@ protected: bool m_bLanOnly; CSteamID m_SteamIDGS; +#ifdef REHLDS_FIXES + char m_GameTagsData[MAX_STEAM_TAGS_LENGTH]; +#endif + public: NOBODY void SetServerType(); @@ -96,6 +102,7 @@ public: void NotifyOfLevelChange(bool bForce); void RunFrame(); void SendUpdatedServerDetails(); + void UpdateGameTags(); }; class CSteam3Client: public CSteam3 diff --git a/rehlds/engine/textures.cpp b/rehlds/engine/textures.cpp index 17b65b3..4c72dfd 100644 --- a/rehlds/engine/textures.cpp +++ b/rehlds/engine/textures.cpp @@ -109,10 +109,10 @@ qboolean TEX_InitFromWad(char *path) #endif // REHLDS_FIXES texfile = FS_Open(wadPath, "rb"); - texfiles[nTexFiles++] = texfile; if (!texfile) Sys_Error("%s: couldn't open %s\n", __func__, wadPath); + texfiles[nTexFiles++] = texfile; Con_DPrintf("Using WAD File: %s\n", wadPath); SafeRead(texfile, &header, 12); if (Q_strncmp(header.identification, "WAD2", 4) && Q_strncmp(header.identification, "WAD3", 4)) @@ -147,8 +147,9 @@ void TEX_CleanupWadInfo(void) for (int i = 0; i < nTexFiles; i++) { - FS_Close(texfiles[i]); - texfiles[i] = 0; + if (texfiles[i]) + FS_Close(texfiles[i]); + texfiles[i] = NULL; } nTexLumps = 0; diff --git a/rehlds/rehlds/structSizeCheck.cpp b/rehlds/rehlds/structSizeCheck.cpp index 18cb3db..259787c 100644 --- a/rehlds/rehlds/structSizeCheck.cpp +++ b/rehlds/rehlds/structSizeCheck.cpp @@ -15,5 +15,7 @@ void check_size() { void checkSizesStatic() { CHECK_TYPE_SIZE(client_t, 0x5018, 0x4EF4); CHECK_TYPE_SIZE(userfilter_t, 0x20, 0x18); +#ifndef REHLDS_FIXES CHECK_TYPE_SIZE(CSteam3Server, 0x90, 0xA8); +#endif }