diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 29a64b6..d76a2c6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -25,12 +25,12 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: fetch-depth: 0 - name: Setup MSBuild - uses: microsoft/setup-msbuild@v1.0.2 + uses: microsoft/setup-msbuild@v1.1.3 - name: Build and Run unittests run: | @@ -70,7 +70,7 @@ jobs: move msvc\${{ env.buildRelease }}\director.pdb publish\debug\director.pdb - name: Deploy artifacts - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3.1.1 with: name: win32 path: publish/* @@ -92,7 +92,7 @@ jobs: steps: - name: Deploying windows artifacts - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v3 with: name: win32 @@ -155,7 +155,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: fetch-depth: 0 @@ -228,7 +228,7 @@ jobs: shell: bash - name: Deploy artifacts - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3.1.1 id: upload-job with: name: linux32 @@ -247,12 +247,12 @@ jobs: steps: - name: Deploying linux artifacts - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v3 with: name: linux32 - name: Deploying windows artifacts - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v3 with: name: win32 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/cmd.cpp b/rehlds/engine/cmd.cpp index 7dcf76c..a49df84 100644 --- a/rehlds/engine/cmd.cpp +++ b/rehlds/engine/cmd.cpp @@ -235,7 +235,8 @@ void Cbuf_Execute(void) } // execute the command line - Cmd_ExecuteString(line, src_command); + if (line[0]) + Cmd_ExecuteString(line, src_command); if (cmd_wait) { @@ -406,17 +407,12 @@ void Cmd_Exec_f(void) else { char *pszDataPtr = configContents; - while (true) + while (pszDataPtr && *pszDataPtr) { Cbuf_Execute(); // TODO: This doesn't obey the rule to first execute commands from the file, and then the others in the buffer pszDataPtr = COM_ParseLine(pszDataPtr); // TODO: COM_ParseLine can be const char* - - if (com_token[0] == 0) - { - break; - } - - Cbuf_InsertTextLines(com_token); + if (com_token[0]) + Cbuf_InsertTextLines(com_token); } } 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/decals.cpp b/rehlds/engine/decals.cpp index 975d7f9..1705b7f 100644 --- a/rehlds/engine/decals.cpp +++ b/rehlds/engine/decals.cpp @@ -717,14 +717,9 @@ NOXREF qboolean Draw_CacheReload(cachewad_t *wad, int i, lumpinfo_t *pLump, cach qboolean Draw_ValidateCustomLogo(cachewad_t *wad, unsigned char *data, lumpinfo_t *lump) { texture_t tex; - miptex_t *mip; - miptex_t tmp; - int pix; - int pixoffset; - int paloffset; - int palettesize; - int nPalleteCount; - int nSize; + miptex_t *mip, tmp; + int i, pix, paloffset, palettesize; + int size; if (wad->cacheExtra != DECAL_EXTRASIZE) { @@ -734,58 +729,54 @@ qboolean Draw_ValidateCustomLogo(cachewad_t *wad, unsigned char *data, lumpinfo_ tex = *(texture_t *)data; mip = (miptex_t *)(data + wad->cacheExtra); - tmp = *mip; + // Copy mip texture data + tmp = *mip; tex.width = LittleLong(tmp.width); tex.height = LittleLong(tmp.height); - tex.anim_max = 0; - tex.anim_min = 0; - tex.anim_total = 0; - tex.alternate_anims = NULL; - tex.anim_next = NULL; + tex.anim_total = tex.anim_min = tex.anim_max = 0; + tex.alternate_anims = tex.anim_next = NULL; - if (!tex.width || tex.width > 256 || tex.height > 256) - { - Con_Printf("%s: Bad wad dimensions %s\n", __func__, wad->name); - return FALSE; - } - - for (int i = 0; i < MIPLEVELS; i++) + for (i = 0; i < MIPLEVELS; i++) tex.offsets[i] = wad->cacheExtra + LittleLong(tmp.offsets[i]); + if (tex.width <= 0 || tex.height <= 0 || + // Check if texture dimensions exceed limits + tex.width > 256 || tex.height > 256) + { + Con_Printf("%s: Bad cached wad tex size %ux%u on %s\n", __func__, tex.width, tex.height, wad->name); + return FALSE; + } + pix = tex.width * tex.height; - pixoffset = pix + (pix >> 2) + (pix >> 4) + (pix >> 6); + size = pix + (pix >> 2) + (pix >> 4) + (pix >> 6); -#ifdef REHLDS_FIXES - // Ensure that pixoffset won't be exceed the pre allocated buffer - // This can happen when there are no color palettes in payload - if ((pixoffset + sizeof(texture_t)) >= (unsigned)(wad->cacheExtra + lump->size)) + if ((unsigned)(size + sizeof(miptex_t)) >= (unsigned)(lump->size + wad->cacheExtra)) { - Con_Printf("%s: Bad wad payload size %s\n", __func__, wad->name); - return FALSE; + Con_Printf("%s: Bad cached wad size %i/%i on %s\n", __func__, size + sizeof(miptex_t), lump->size + wad->cacheExtra, wad->name); } -#endif - paloffset = (pix >> 2) + tmp.offsets[0] + pix; - palettesize = (pix >> 4) + paloffset; + paloffset = size + sizeof(lumpinfo_t) + sizeof(miptex_t); + palettesize = *(u_short *)(data + paloffset); // Get palette size - if ((tmp.offsets[0] + pix != tmp.offsets[1]) - || paloffset != tmp.offsets[2] - || palettesize != tmp.offsets[3]) + for (i = 0; i < 3; i++) { - Con_Printf("%s: Bad cached wad %s\n", __func__, wad->name); + // Check if offsets are valid for mip levels + if (pix + tmp.offsets[i] != tmp.offsets[i + 1]) + { + Con_Printf("%s: Bad cached wad %s\n", __func__, wad->name); + return FALSE; + } + pix >>= 2; + } + + if (palettesize > 256) + { + Con_Printf("%s: Bad cached wad palette size %i on %s\n", __func__, palettesize, wad->name); return FALSE; } - nPalleteCount = *(u_short *)(data + pixoffset + sizeof(texture_t)); - if (nPalleteCount > 256) - { - Con_Printf("%s: Bad cached wad palette size %i on %s\n", __func__, nPalleteCount, wad->name); - return FALSE; - } - - nSize = pixoffset + LittleLong(tmp.offsets[0]) + 3 * nPalleteCount + 2; - if (nSize > lump->disksize) + if ((palettesize + 2 * (palettesize + 1) + size + LittleLong(tmp.offsets[0])) > lump->disksize) { Con_Printf("%s: Bad cached wad %s\n", __func__, wad->name); return FALSE; 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/hashpak.cpp b/rehlds/engine/hashpak.cpp index 3e76012..2befee6 100644 --- a/rehlds/engine/hashpak.cpp +++ b/rehlds/engine/hashpak.cpp @@ -620,7 +620,9 @@ qboolean HPAK_ResourceForHash(char *pakname, unsigned char *hash, struct resourc fp = FS_Open(name, "rb"); if (!fp) { +#ifndef REHLDS_FIXES Con_Printf("ERROR: couldn't open %s.\n", name); +#endif return FALSE; } FS_Read(&header, sizeof(hash_pack_header_t), 1, fp); diff --git a/rehlds/engine/host.cpp b/rehlds/engine/host.cpp index 31560b7..673b329 100644 --- a/rehlds/engine/host.cpp +++ b/rehlds/engine/host.cpp @@ -1197,7 +1197,11 @@ int Host_Init(quakeparms_t *parms) else { Cvar_RegisterVariable(&suitvolume); +#ifdef REHLDS_FIXES + Cvar_RegisterVariable(&r_cachestudio); +#endif } + Cbuf_InsertText("exec valve.rc\n"); Hunk_AllocName(0, "-HOST_HUNKLEVEL-"); host_hunklevel = Hunk_LowMark(); diff --git a/rehlds/engine/host_cmd.cpp b/rehlds/engine/host_cmd.cpp index 3d6558c..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,12 +753,117 @@ 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); } +// Sets client to godmode +void Host_God_f(void) +{ + if (cmd_source == src_command) + { + Cmd_ForwardToServer(); + return; + } + + if (!sv_cheats.value) + return; + + sv_player->v.flags = (int)sv_player->v.flags ^ FL_GODMODE; + if (!((int)sv_player->v.flags & FL_GODMODE)) + SV_ClientPrintf("godmode OFF\n"); + else + SV_ClientPrintf("godmode ON\n"); +} + +// Sets client to notarget mode +void Host_Notarget_f(void) +{ + if (cmd_source == src_command) + { + Cmd_ForwardToServer(); + return; + } + + if (!sv_cheats.value) + return; + + sv_player->v.flags = (int)sv_player->v.flags ^ FL_NOTARGET; + if (!((int)sv_player->v.flags & FL_NOTARGET)) + SV_ClientPrintf("notarget OFF\n"); + else + SV_ClientPrintf("notarget ON\n"); +} + +// Searches along the direction ray in steps of "step" to see if +// the entity position is passible +// Used for putting the player in valid space when toggling off noclip mode +int FindPassableSpace(edict_t *pEdict, vec_t *direction, float step) +{ + int i; + + for (i = 0; i < 100; i++) + { + VectorMA(pEdict->v.origin, step, direction, pEdict->v.origin); + + if (!SV_TestEntityPosition(pEdict)) + { + // Store old origin + VectorCopy(pEdict->v.origin, pEdict->v.oldorigin); + return TRUE; + } + } + + return FALSE; +} + +void Host_Noclip_f(void) +{ + if (cmd_source == src_command) + { + Cmd_ForwardToServer(); + return; + } + + if (!sv_cheats.value) + return; + + if (sv_player->v.movetype != MOVETYPE_NOCLIP) + { + sv_player->v.movetype = MOVETYPE_NOCLIP; + SV_ClientPrintf("noclip ON\n"); + } + else + { + sv_player->v.movetype = MOVETYPE_WALK; + + // Store old origin + VectorCopy(sv_player->v.origin, sv_player->v.oldorigin); + + SV_ClientPrintf("noclip OFF\n"); + + if (SV_TestEntityPosition(sv_player)) + { + vec3_t forward, right, up; + AngleVectors(sv_player->v.v_angle, forward, right, up); + + if (!FindPassableSpace(sv_player, forward, 1.0) + && !FindPassableSpace(sv_player, right, 1.0) + && !FindPassableSpace(sv_player, right, -1.0) // left + && !FindPassableSpace(sv_player, up, 1.0) // up + && !FindPassableSpace(sv_player, up, -1.0) // down + && !FindPassableSpace(sv_player, forward, -1.0)) // back + { + Con_DPrintf("Can't find the world\n"); + } + + VectorCopy(sv_player->v.oldorigin, sv_player->v.origin); + } + } +} + void Host_Ping_f(void) { int i; @@ -3146,11 +3249,12 @@ void Host_InitCommands(void) Cmd_AddCommand("setinfo", Host_SetInfo_f); Cmd_AddCommand("fullinfo", Host_FullInfo_f); -#ifndef SWDS Cmd_AddCommand("god", Host_God_f); Cmd_AddCommand("notarget", Host_Notarget_f); - Cmd_AddCommand("fly", Host_Fly_f); Cmd_AddCommand("noclip", Host_Noclip_f); + +#ifndef SWDS + Cmd_AddCommand("fly", Host_Fly_f); Cmd_AddCommand("viewmodel", Host_Viewmodel_f); Cmd_AddCommand("viewframe", Host_Viewframe_f); Cmd_AddCommand("viewnext", Host_Viewnext_f); diff --git a/rehlds/engine/pr_cmds.cpp b/rehlds/engine/pr_cmds.cpp index e83cbb4..dfd2846 100644 --- a/rehlds/engine/pr_cmds.cpp +++ b/rehlds/engine/pr_cmds.cpp @@ -1802,7 +1802,14 @@ void EXT_FUNC PF_aim_I(edict_t *ent, float speed, float *rgflReturn) bestdir[1] = dir[1]; bestdir[2] = dir[2]; bestdir[0] = dir[0]; - bestdist = sv_aim.value; + if (sv_allow_autoaim.value) + { + bestdist = sv_aim.value; + } + else + { + bestdist = 0.0f; + } for (int i = 1; i < g_psv.num_edicts; i++) { diff --git a/rehlds/engine/server.h b/rehlds/engine/server.h index 24e2518..8f16f3a 100644 --- a/rehlds/engine/server.h +++ b/rehlds/engine/server.h @@ -283,6 +283,7 @@ extern rehlds_server_t g_rehlds_sv; extern cvar_t sv_lan; extern cvar_t sv_lan_rate; extern cvar_t sv_aim; +extern cvar_t sv_allow_autoaim; extern cvar_t sv_skycolor_r; extern cvar_t sv_skycolor_g; @@ -365,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; @@ -588,6 +590,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 c176ee8..95077e9 100644 --- a/rehlds/engine/sv_main.cpp +++ b/rehlds/engine/sv_main.cpp @@ -114,6 +114,7 @@ int giNextUserMsg = 64; cvar_t sv_lan = { "sv_lan", "0", 0, 0.0f, NULL }; cvar_t sv_lan_rate = { "sv_lan_rate", "20000.0", 0, 0.0f, NULL }; cvar_t sv_aim = { "sv_aim", "1", FCVAR_SERVER | FCVAR_ARCHIVE , 0.0f, NULL }; +cvar_t sv_allow_autoaim = { "sv_allow_autoaim", "1", FCVAR_SERVER | FCVAR_ARCHIVE, 0.0f, NULL }; cvar_t sv_skycolor_r = { "sv_skycolor_r", "0", 0, 0.0f, NULL }; cvar_t sv_skycolor_g = { "sv_skycolor_g", "0", 0, 0.0f, NULL }; @@ -192,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 }; @@ -3275,6 +3278,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; @@ -3403,10 +3559,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); @@ -3415,78 +3582,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(); } @@ -4699,16 +4893,38 @@ void SV_WriteEntitiesToClient(client_t *client, sizebuf_t *msg) auto &entityState = curPack->entities[i]; if (entityState.number > MAX_CLIENTS) { - if (sv_rehlds_attachedentities_playeranimationspeed_fix.string[0] == '1' - && entityState.movetype == MOVETYPE_FOLLOW - && 1 <= entityState.aiment && entityState.aiment <= MAX_CLIENTS) + if (entityState.movetype == MOVETYPE_FOLLOW) { - attachedEntCount[entityState.aiment]++; + if (entityState.aiment > 0 && entityState.aiment < g_psv.num_edicts) + { + 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 + edict_t *ent = &g_psv.edicts[entityState.aiment]; + 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; @@ -5534,6 +5750,12 @@ void SV_PropagateCustomizations(void) if (pCust->bInUse) { pResource = &pCust->resource; + +#ifdef REHLDS_FIXES + if ((pResource->ucFlags & RES_CUSTOM) && !sv_send_logos.value) + continue; +#endif + MSG_WriteByte(&host_client->netchan.message, svc_customization); MSG_WriteByte(&host_client->netchan.message, i); MSG_WriteByte(&host_client->netchan.message, pResource->type); @@ -6357,7 +6579,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)); @@ -6507,44 +6728,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) { @@ -7982,6 +8165,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); @@ -8026,6 +8214,9 @@ void SV_Init(void) Cvar_RegisterVariable(&sv_visiblemaxplayers); Cvar_RegisterVariable(&sv_password); Cvar_RegisterVariable(&sv_aim); +#ifdef REHLDS_FIXES + Cvar_RegisterVariable(&sv_allow_autoaim); +#endif Cvar_RegisterVariable(&violence_hblood); Cvar_RegisterVariable(&violence_ablood); Cvar_RegisterVariable(&violence_hgibs); @@ -8093,6 +8284,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/sv_upld.cpp b/rehlds/engine/sv_upld.cpp index e28a0e3..425e439 100644 --- a/rehlds/engine/sv_upld.cpp +++ b/rehlds/engine/sv_upld.cpp @@ -138,13 +138,16 @@ void SV_CreateCustomizationList(client_t *pHost) { pCust->nUserData2 = nLumps; gEntityInterface.pfnPlayerCustomization(pHost->edict, pCust); +#ifdef REHLDS_FIXES + SV_Customization(pHost, pResource, TRUE); +#endif } else { if (sv_allow_upload.value == 0.0f) - Con_Printf("Ignoring custom decal from %s\n", pHost->name); + Con_DPrintf("Ignoring custom decal from %s\n", pHost->name); else - Con_Printf("Ignoring invalid custom decal from %s\n", pHost->name); + Con_DPrintf("Ignoring invalid custom decal from %s\n", pHost->name); } } } @@ -157,6 +160,11 @@ void SV_Customization(client_t *pPlayer, resource_t *pResource, qboolean bSkipPl int nPlayerNumber; client_t *pHost; +#ifdef REHLDS_FIXES + if ((pResource->ucFlags & RES_CUSTOM) && !sv_send_logos.value) + return; +#endif + // Get originating player id for (i = 0, pHost = g_psvs.clients; i < g_psvs.maxclients; i++, pHost++) { @@ -205,10 +213,6 @@ void SV_RegisterResources(void) pHost->uploading = FALSE; #ifdef REHLDS_FIXES SV_CreateCustomizationList(pHost); // FIXED: Call this function only once. It was crazy to call it for each resource available. - for (pResource = pHost->resourcesonhand.pNext; pResource != &pHost->resourcesonhand; pResource = pResource->pNext) - { - SV_Customization(pHost, pResource, TRUE); - } #else // REHLDS_FIXES for (pResource = pHost->resourcesonhand.pNext; pResource != &pHost->resourcesonhand; pResource = pResource->pNext) { @@ -509,8 +513,13 @@ void SV_ParseResourceList(client_t *pSenderClient) } } - host_client->uploading = TRUE; - host_client->uploaddoneregistering = FALSE; +#ifdef REHLDS_FIXES + if (sv_allow_upload.value != 0.0f) +#endif //REHLDS_FIXES + { + host_client->uploading = TRUE; + host_client->uploaddoneregistering = FALSE; - SV_BatchUploadRequest(host_client); + SV_BatchUploadRequest(host_client); + } } diff --git a/rehlds/engine/sv_user.cpp b/rehlds/engine/sv_user.cpp index 64e388e..84c226e 100644 --- a/rehlds/engine/sv_user.cpp +++ b/rehlds/engine/sv_user.cpp @@ -42,7 +42,7 @@ edict_t *sv_player; qboolean nofind; #if defined(SWDS) && defined(REHLDS_FIXES) -const char *clcommands[] = { "status", "name", "kill", "pause", "spawn", "new", "sendres", "dropclient", "kick", "ping", "dlfile", "setinfo", "sendents", "fullupdate", "setpause", "unpause", NULL }; +const char *clcommands[] = { "status", "name", "kill", "pause", "spawn", "new", "sendres", "dropclient", "kick", "ping", "dlfile", "setinfo", "sendents", "fullupdate", "setpause", "unpause", "noclip", "god", "notarget", NULL }; #else const char *clcommands[23] = { "status", "god", "notarget", "fly", "name", "noclip", "kill", "pause", "spawn", "new", "sendres", "dropclient", "kick", "ping", "dlfile", "nextdl", "setinfo", "showinfo", "sendents", "fullupdate", "setpause", "unpause", NULL }; #endif diff --git a/rehlds/engine/sys_dll.cpp b/rehlds/engine/sys_dll.cpp index 293119d..08431a4 100644 --- a/rehlds/engine/sys_dll.cpp +++ b/rehlds/engine/sys_dll.cpp @@ -1150,46 +1150,47 @@ void EXT_FUNC EngineFprintf(void *pfile, const char *szFmt, ...) void EXT_FUNC AlertMessage(ALERT_TYPE atype, const char *szFmt, ...) { + char szOut[2048]; va_list argptr; - static char szOut[1024]; - va_start(argptr, szFmt); if (atype == at_logged && g_psvs.maxclients > 1) { + va_start(argptr, szFmt); Q_vsnprintf(szOut, sizeof(szOut), szFmt, argptr); + va_end(argptr); + Log_Printf("%s", szOut); + return; } - else if (developer.value != 0.0f) - { - switch (atype) - { - case at_notice: - Q_strcpy(szOut, "NOTE: "); - break; - case at_console: - szOut[0] = 0; - break; - case at_aiconsole: - if (developer.value < 2.0f) - return; - szOut[0] = 0; - break; - case at_warning: - Q_strcpy(szOut, "WARNING: "); - break; - case at_error: - Q_strcpy(szOut, "ERROR: "); - break; - case at_logged: - break; - default: - break; - } - int iLen = Q_strlen(szOut); - Q_vsnprintf(&szOut[iLen], sizeof(szOut) - iLen, szFmt, argptr); - Con_Printf("%s", szOut); - } + + if (!developer.value) + return; + + if (atype == at_aiconsole && developer.value < 2) + return; + + va_start(argptr, szFmt); + Q_vsnprintf(szOut, sizeof(szOut), szFmt, argptr); va_end(argptr); + + switch (atype) + { + case at_notice: + Con_Printf("NOTE: %s", szOut); + break; + case at_console: + case at_aiconsole: + Con_Printf("%s", szOut); + break; + case at_warning: + Con_Printf("WARNING: %s", szOut); + break; + case at_error: + Con_Printf("ERROR: %s", szOut); + break; + default: + break; + } } NOXREF void Sys_SplitPath(const char *path, char *drive, char *dir, char *fname, char *ext) @@ -1326,7 +1327,7 @@ void Con_Printf(const char *fmt, ...) va_start(va, fmt); Q_vsnprintf(Dest, sizeof(Dest), fmt, va); va_end(va); - + g_RehldsHookchains.m_Con_Printf.callChain(Con_Printf_internal, Dest); } 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 }