From d6b35fad804d9d6bf1dbebd6ae5f98a0782893fe Mon Sep 17 00:00:00 2001 From: pepepepito0147 <112137590+pepepepito0147@users.noreply.github.com> Date: Mon, 5 Sep 2022 13:32:02 -0400 Subject: [PATCH] Update sv_user.cpp Added new cvars: Fix bug to fake players sv_invalid_length = 0 Disable message flooding in console sv_msg_badread = 1 --- rehlds/engine/sv_user.cpp | 2001 +++++++++++++++++++++++++++++++++++++ 1 file changed, 2001 insertions(+) diff --git a/rehlds/engine/sv_user.cpp b/rehlds/engine/sv_user.cpp index 5e2c001..e122bde 100644 --- a/rehlds/engine/sv_user.cpp +++ b/rehlds/engine/sv_user.cpp @@ -33,6 +33,42 @@ qboolean g_balreadymoved; float s_LastFullUpdate[33]; +//cvar_t sv_voicecodec; +//cvar_t sv_voicequality;/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#include "precompiled.h" + +sv_adjusted_positions_t truepositions[MAX_CLIENTS]; +qboolean g_balreadymoved; + +float s_LastFullUpdate[33]; + //cvar_t sv_voicecodec; //cvar_t sv_voicequality; @@ -80,6 +116,1971 @@ bool EXT_FUNC SV_CheckConsistencyResponse_API(IGameClient *client, resource_t *r return (hash != *(uint32 *)&res->rgucMD5_hash[0]); } +void SV_ParseConsistencyResponse(client_t *pSenderClient) +{ + vec3_t mins; + vec3_t maxs; + vec3_t cmins; + vec3_t cmaxs; + unsigned char nullbuffer[32]; + unsigned char resbuffer[32]; + + int length = 0; + int c = 0; + Q_memset(nullbuffer, 0, sizeof(nullbuffer)); + int value = MSG_ReadShort(); + + if (sv_invalid_length.value == 1) + { + if (value <= 0 || !SZ_HasSomethingToRead(&net_message, value)) + { + msg_badread = TRUE; + Con_DPrintf("%s: %s:%s invalid length: %d\n", __func__, host_client->name, NET_AdrToString(host_client->netchan.remote_address), value); + SV_DropClient(host_client, FALSE, "Invalid length"); + return; + } + } + + COM_UnMunge(&net_message.data[msg_readcount], value, g_psvs.spawncount); + MSG_StartBitReading(&net_message); + + while (MSG_ReadBits(1)) + { + int idx = MSG_ReadBits(12); + if (idx < 0 || idx >= g_psv.num_resources) + { + c = -1; + break; + } + +#ifdef REHLDS_FIXES + resource_t *r = &g_rehlds_sv.resources[idx]; +#else // REHLDS_FIXES + resource_t *r = &g_psv.resourcelist[idx]; +#endif // REHLDS_FIXES + if (!(r->ucFlags & RES_CHECKFILE)) + { + c = -1; + break; + } + + Q_memcpy(resbuffer, r->rguc_reserved, sizeof(resbuffer)); + if (!Q_memcmp(resbuffer, nullbuffer, sizeof(resbuffer))) + { + uint32 hash = MSG_ReadBits(32); + if (g_RehldsHookchains.m_SV_CheckConsistencyResponse.callChain(SV_CheckConsistencyResponse_API, GetRehldsApiClient(pSenderClient), r, hash)) + c = idx + 1; + } + else + { + MSG_ReadBitData(cmins, 12); + MSG_ReadBitData(cmaxs, 12); + Q_memcpy(resbuffer, r->rguc_reserved, sizeof(resbuffer)); + COM_UnMunge(resbuffer, sizeof(resbuffer), g_psvs.spawncount); + FORCE_TYPE ft = (FORCE_TYPE)resbuffer[0]; + if (ft == force_model_samebounds) + { + Q_memcpy(mins, &resbuffer[1], 12); + Q_memcpy(maxs, &resbuffer[13], 12); + if (!VectorCompare(cmins, mins) || !VectorCompare(cmaxs, maxs)) + c = idx + 1; + } + else if (ft == force_model_specifybounds) + { + Q_memcpy(mins, &resbuffer[1], 12); + Q_memcpy(maxs, &resbuffer[13], 12); + for (int i = 0; i < 3; i++) + { + if (cmins[i] < mins[i] || cmaxs[i] > maxs[i]) + { + c = idx + 1; + break; + } + } + } + else if (ft == force_model_specifybounds_if_avail) + { + Q_memcpy(mins, &resbuffer[1], 12); + Q_memcpy(maxs, &resbuffer[13], 12); + if (cmins[0] != -1.0 || cmins[1] != -1.0 || cmins[2] != -1.0 || cmaxs[0] != -1.0 || cmaxs[1] != -1.0 || cmaxs[2] != -1.0) + { + for (int i = 0; i < 3; i++) + { + if (cmins[i] < mins[i] || cmaxs[i] > maxs[i]) + { + c = idx + 1; + break; + } + } + } + } + else + { + msg_badread = 1; + c = idx + 1; + break; + } + + } + + if (msg_badread) + break; + + ++length; + } + + MSG_EndBitReading(&net_message); + if (c < 0 || length != g_psv.num_consistency) + { + msg_badread = 1; + Con_Printf("SV_ParseConsistencyResponse: %s:%s sent bad file data\n", host_client->name, NET_AdrToString(host_client->netchan.remote_address)); + SV_DropClient(host_client, FALSE, "Bad file data"); + return; + } + + if (c > 0) + { + char dropmessage[256]; +#ifdef REHLDS_FIXES + dropmessage[0] = '\0'; + + if (gEntityInterface.pfnInconsistentFile(host_client->edict, g_rehlds_sv.resources[c - 1].szFileName, dropmessage)) + { + if (dropmessage[0]) + SV_ClientPrintf("%s", dropmessage); + + SV_DropClient(host_client, FALSE, "Bad file %s", g_rehlds_sv.resources[c - 1].szFileName); // only filename. reason was printed in console if exists. + return; + } +#else // REHLDS_FIXES + if (gEntityInterface.pfnInconsistentFile(host_client->edict, g_psv.resourcelist[c - 1].szFileName, dropmessage)) + { + if (Q_strlen(dropmessage) > 0) + SV_ClientPrintf("%s", dropmessage); + + SV_DropClient(host_client, FALSE, "Bad file %s", dropmessage); + } + return; +#endif // REHLDS_FIXES + } + + // Reset has_force_unmodified if we have received response from the client + host_client->has_force_unmodified = FALSE; +} + +qboolean EXT_FUNC SV_FileInConsistencyList(const char *filename, consistency_t **ppconsist) +{ + for (int i = 0; i < ARRAYSIZE(g_psv.consistency_list); i++) + { + if (!g_psv.consistency_list[i].filename) + return 0; + + if (!Q_stricmp(filename, g_psv.consistency_list[i].filename)) + { + if (ppconsist) + *ppconsist = &g_psv.consistency_list[i]; + return 1; + } + } + + return 0; +} + +int SV_TransferConsistencyInfo(void) +{ + return g_RehldsHookchains.m_SV_TransferConsistencyInfo.callChain(SV_TransferConsistencyInfo_internal); +} + +int EXT_FUNC SV_TransferConsistencyInfo_internal(void) +{ + consistency_t *pc; + + int c = 0; + for (int i = 0; i < g_psv.num_resources; i++) + { +#ifdef REHLDS_FIXES + resource_t *r = &g_rehlds_sv.resources[i]; +#else // REHLDS_FIXES + resource_t *r = &g_psv.resourcelist[i]; +#endif // REHLDS_FIXES + if (r->ucFlags == (RES_CUSTOM | RES_REQUESTED | RES_UNK_6) || (r->ucFlags & RES_CHECKFILE)) + continue; + + if (!SV_FileInConsistencyList(r->szFileName, &pc)) + continue; + + r->ucFlags |= RES_CHECKFILE; + + char filename[MAX_PATH]; + if (r->type != t_sound) + { + Q_strncpy(filename, r->szFileName, MAX_PATH - 1); + filename[MAX_PATH - 1] = 0; + } + else + { + Q_snprintf(filename, MAX_PATH, "sound/%s", r->szFileName); + } + MD5_Hash_File(r->rgucMD5_hash, filename, FALSE, FALSE, NULL); + + if (r->type == t_model) + { + if (pc->check_type == force_model_samebounds) + { + vec3_t mins; + vec3_t maxs; + + if (!R_GetStudioBounds(filename, mins, maxs)) + Host_Error("%s: Server unable to get bounds for %s\n", __func__, filename); + + Q_memcpy(&r->rguc_reserved[1], mins, sizeof(mins)); + Q_memcpy(&r->rguc_reserved[13], maxs, sizeof(maxs)); + } + else if (pc->check_type == force_model_specifybounds || pc->check_type == force_model_specifybounds_if_avail) + { + Q_memcpy(&r->rguc_reserved[1], pc->mins, sizeof(pc->mins)); + Q_memcpy(&r->rguc_reserved[13], pc->maxs, sizeof(pc->maxs)); + } + else + { + ++c; + continue; + } + r->rguc_reserved[0] = pc->check_type; + COM_Munge(r->rguc_reserved, 32, g_psvs.spawncount); + } + ++c; + } + return c; +} + +bool EXT_FUNC SV_ShouldSendConsistencyList_mod(IGameClient *cl, bool forceConsistency) +{ + if (g_psvs.maxclients == 1 || g_psv.num_consistency == 0 || cl->IsProxy()) + return false; + + if (!forceConsistency && mp_consistency.value == 0.0f) + return false; + + return true; +} + +bool SV_ShouldSendConsistencyList(client_t *client, bool forceConsistency) +{ + return g_RehldsHookchains.m_SV_ShouldSendConsistencyList.callChain(SV_ShouldSendConsistencyList_mod, GetRehldsApiClient(client), forceConsistency); +} + +void SV_SendConsistencyList(sizebuf_t *msg) +{ + host_client->has_force_unmodified = FALSE; + + if (!SV_ShouldSendConsistencyList(host_client, false)) + { + MSG_WriteBits(0, 1); + return; + } + + host_client->has_force_unmodified = TRUE; + + int delta = 0; + int lastcheck = 0; +#ifdef REHLDS_FIXES + resource_t *r = g_rehlds_sv.resources; +#else // REHLDS_FIXES + resource_t *r = g_psv.resourcelist; +#endif // REHLDS_FIXES + + MSG_WriteBits(1, 1); + + for (int i = 0; i < g_psv.num_resources; i++, r++) + { + if (r && (r->ucFlags & RES_CHECKFILE) != 0) + { + MSG_WriteBits(1, 1); + delta = i - lastcheck; + + if (delta > 31) + { + MSG_WriteBits(0, 1); + MSG_WriteBits(i, 10); // LIMIT: Here it write index, not a diff, with resolution of 10 bits. So, it limits not adjacent index to 1023 max. + } + else + { + // Write 5 bits index delta, so it is up to 31 + MSG_WriteBits(1, 1); + MSG_WriteBits(delta, 5); + } + + lastcheck = i; + } + } + + MSG_WriteBits(0, 1); +} + +void SV_PreRunCmd(void) +{ +} + +void SV_CopyEdictToPhysent(physent_t *pe, int e, edict_t *check) +{ + model_t *pModel; + + pe->origin[0] = check->v.origin[0]; + pe->origin[1] = check->v.origin[1]; + pe->origin[2] = check->v.origin[2]; + + pe->info = e; + + if (e < 1 || e > g_psvs.maxclients) + { + pe->player = 0; + } + else + { + SV_GetTrueOrigin(e - 1, pe->origin); + pe->player = pe->info; + } + + pe->angles[0] = check->v.angles[0]; + pe->angles[1] = check->v.angles[1]; + pe->angles[2] = check->v.angles[2]; + + pe->studiomodel = nullptr; + pe->rendermode = check->v.rendermode; + + if (check->v.solid == SOLID_BSP) + { + pe->model = g_psv.models[check->v.modelindex]; + Q_strncpy(pe->name, pe->model->name, sizeof(pe->name) - 1); + pe->name[sizeof(pe->name) - 1] = 0; + } + else if (check->v.solid == SOLID_NOT) + { + if (check->v.modelindex) + { + pe->model = g_psv.models[check->v.modelindex]; + Q_strncpy(pe->name, pe->model->name, sizeof(pe->name) - 1); + pe->name[sizeof(pe->name) - 1] = 0; + } + else + { + pe->model = nullptr; + } + } + else + { + pe->model = nullptr; + if (check->v.solid != SOLID_BBOX) + { + pe->mins[0] = check->v.mins[0]; + pe->mins[1] = check->v.mins[1]; + pe->maxs[0] = check->v.maxs[0]; + pe->mins[2] = check->v.mins[2]; + pe->maxs[1] = check->v.maxs[1]; + pe->maxs[2] = check->v.maxs[2]; + + if (check->v.classname) + { + Q_strncpy(pe->name, &pr_strings[check->v.classname], sizeof(pe->name) - 1); + pe->name[sizeof(pe->name) - 1] = 0; + } + else + { + Q_sprintf(pe->name, "?"); + } + } + else + { + if (check->v.modelindex) + { + pModel = g_psv.models[check->v.modelindex]; + if (pModel) + { + if (pModel->flags & STUDIO_TRACE_HITBOX) + pe->studiomodel = pModel; + + Q_strncpy(pe->name, pModel->name, sizeof(pe->name) - 1); + pe->name[sizeof(pe->name) - 1] = 0; + } + } + + pe->mins[0] = check->v.mins[0]; + pe->mins[1] = check->v.mins[1]; + pe->mins[2] = check->v.mins[2]; + pe->maxs[0] = check->v.maxs[0]; + pe->maxs[1] = check->v.maxs[1]; + pe->maxs[2] = check->v.maxs[2]; + } + } + + pe->skin = check->v.skin; + pe->frame = check->v.frame; + pe->solid = check->v.solid; + pe->sequence = check->v.sequence; + pe->movetype = check->v.movetype; + + Q_memcpy(&pe->controller[0], &check->v.controller[0], 4 * sizeof(byte)); + Q_memcpy(&pe->blending[0], &check->v.blending[0], 2 * sizeof(byte)); + + pe->iuser1 = check->v.iuser1; + pe->iuser2 = check->v.iuser2; + pe->iuser3 = check->v.iuser3; + pe->iuser4 = check->v.iuser4; + pe->fuser1 = check->v.fuser1; + pe->fuser2 = check->v.fuser2; + pe->fuser3 = check->v.fuser3; + pe->fuser4 = check->v.fuser4; + + pe->vuser1[0] = check->v.vuser1[0]; + pe->vuser1[1] = check->v.vuser1[1]; + pe->vuser1[2] = check->v.vuser1[2]; + pe->vuser2[0] = check->v.vuser2[0]; + pe->vuser2[1] = check->v.vuser2[1]; + pe->vuser2[2] = check->v.vuser2[2]; + pe->vuser3[0] = check->v.vuser3[0]; + pe->vuser3[1] = check->v.vuser3[1]; + pe->vuser3[2] = check->v.vuser3[2]; + + pe->takedamage = 0; + pe->blooddecal = 0; + + pe->vuser4[0] = check->v.vuser4[0]; + pe->vuser4[1] = check->v.vuser4[1]; + pe->vuser4[2] = check->v.vuser4[2]; +} + +void SV_AddLinksToPM_(areanode_t *node, float *pmove_mins, float *pmove_maxs) +{ + struct link_s *l; + edict_t *check; + int e; + physent_t *ve; + int i; + link_t *next; + float *fmax; + float *fmin; + physent_t *pe; + + for (l = node->solid_edicts.next; l != &node->solid_edicts; l = next) + { + check = (edict_t *)&l[-1]; + next = l->next; + if (check->v.groupinfo) + { + if (g_groupop) + { + if (g_groupop == GROUP_OP_NAND && (check->v.groupinfo & sv_player->v.groupinfo)) + continue; + } + else + { + if (!(check->v.groupinfo & sv_player->v.groupinfo)) + continue; + } + } + + if (check->v.owner == sv_player) + continue; + + if (check->v.solid != SOLID_BSP && check->v.solid != SOLID_BBOX && check->v.solid != SOLID_SLIDEBOX && check->v.solid != SOLID_NOT) + continue; + + e = NUM_FOR_EDICT(check); + ve = &pmove->visents[pmove->numvisent]; + pmove->numvisent = pmove->numvisent + 1; + SV_CopyEdictToPhysent(ve, e, check); + + if ((check->v.solid == SOLID_NOT) && (!check->v.skin || !check->v.modelindex)) + continue; + + if ((check->v.flags & FL_MONSTERCLIP) && check->v.solid == SOLID_BSP) + continue; + + if (check == sv_player) + continue; + + if ((check->v.flags & FL_CLIENT) && check->v.health <= 0.0) + continue; + + if (check->v.mins[2] == 0.0 && check->v.maxs[2] == 1.0) + continue; + + if (Length(check->v.size) == 0.0) + continue; + + fmin = check->v.absmin; + fmax = check->v.absmax; + if (check->v.flags & FL_CLIENT) + SV_GetTrueMinMax(e - 1, &fmin, &fmax); + + for (i = 0; i < 3; i++) + { + if (fmin[i] > pmove_maxs[i] || fmax[i] < pmove_mins[i]) + break; + } + + if (i != 3) + continue; + + if (check->v.solid || check->v.skin != -16) + { + if (pmove->numphysent >= MAX_PHYSENTS) + { + Con_DPrintf("SV_AddLinksToPM: pmove->numphysent >= MAX_PHYSENTS\n"); + return; + } + pe = &pmove->physents[pmove->numphysent++]; + } + else + { + if (pmove->nummoveent >= MAX_MOVEENTS) + { + Con_DPrintf("SV_AddLinksToPM: pmove->nummoveent >= MAX_MOVEENTS\n"); + continue; + } + pe = &pmove->moveents[pmove->nummoveent++]; + } + + Q_memcpy(pe, ve, sizeof(physent_t)); + } + + if (node->axis != -1) + { + if (pmove_maxs[node->axis] > node->dist) + SV_AddLinksToPM_(node->children[0], pmove_mins, pmove_maxs); + + if (pmove_mins[node->axis] < node->dist) + SV_AddLinksToPM_(node->children[1], pmove_mins, pmove_maxs); + } +} + +void SV_AddLinksToPM(areanode_t *node, vec_t *origin) +{ + vec3_t pmove_mins; + vec3_t pmove_maxs; + + Q_memset(&pmove->physents[0], 0, sizeof(physent_t)); + Q_memset(&pmove->visents[0], 0, sizeof(physent_t)); + + pmove->physents[0].model = g_psv.worldmodel; + if (g_psv.worldmodel != NULL) + { + Q_strncpy(pmove->physents[0].name, g_psv.worldmodel->name, sizeof(pmove->physents[0].name) - 1); + pmove->physents[0].name[sizeof(pmove->physents[0].name) - 1] = 0; + } + pmove->physents[0].origin[0] = vec3_origin[0]; + pmove->physents[0].origin[1] = vec3_origin[1]; + pmove->physents[0].origin[2] = vec3_origin[2]; + pmove->physents[0].info = 0; + pmove->physents[0].solid = 4; + pmove->physents[0].movetype = 0; + pmove->physents[0].takedamage = 1; + pmove->physents[0].blooddecal = 0; + pmove->numphysent = 1; + pmove->numvisent = 1; + pmove->visents[0] = pmove->physents[0]; + pmove->nummoveent = 0; + for (int i = 0; i < 3; i++) + { + pmove_mins[i] = origin[i] - 256.0f; + pmove_maxs[i] = origin[i] + 256.0f; + } + SV_AddLinksToPM_(node, pmove_mins, pmove_maxs); +} + +void SV_PlayerRunPreThink(edict_t *player, float time) +{ + gGlobalVariables.time = time; + gEntityInterface.pfnPlayerPreThink(player); +} + +qboolean SV_PlayerRunThink(edict_t *ent, float frametime, double clienttimebase) +{ + float thinktime; + + if (!(ent->v.flags & (FL_DORMANT | FL_KILLME))) + { + thinktime = ent->v.nextthink; + if (thinktime <= 0.0 || frametime + clienttimebase < thinktime) + return 1; + + if (thinktime < clienttimebase) + thinktime = (float)clienttimebase; + + ent->v.nextthink = 0; + gGlobalVariables.time = thinktime; + gEntityInterface.pfnThink(ent); + } + + if (ent->v.flags & FL_KILLME) + ED_Free(ent); + + return ent->free == 0; +} + +void SV_CheckMovingGround(edict_t *player, float frametime) +{ + edict_t *groundentity; + + if (player->v.flags & FL_ONGROUND) + { + groundentity = player->v.groundentity; + if (groundentity) + { + if (groundentity->v.flags & FL_CONVEYOR) + { + if (player->v.flags & FL_BASEVELOCITY) + VectorMA(player->v.basevelocity, groundentity->v.speed, groundentity->v.movedir, player->v.basevelocity); + else + VectorScale(groundentity->v.movedir, groundentity->v.speed, player->v.basevelocity); + player->v.flags |= FL_BASEVELOCITY; + } + } + } + + if (!(player->v.flags & FL_BASEVELOCITY)) + { + VectorMA(player->v.velocity, frametime * 0.5f + 1.0f, player->v.basevelocity, player->v.velocity); + player->v.basevelocity[0] = 0; + player->v.basevelocity[1] = 0; + player->v.basevelocity[2] = 0; + } + + player->v.flags &= ~FL_BASEVELOCITY; +} + +void SV_ConvertPMTrace(trace_t *dest, pmtrace_t *src, edict_t *ent) +{ + dest->allsolid = src->allsolid; + dest->startsolid = src->startsolid; + dest->inopen = src->inopen; + dest->inwater = src->inwater; + dest->fraction = src->fraction; + dest->endpos[0] = src->endpos[0]; + dest->endpos[1] = src->endpos[1]; + dest->endpos[2] = src->endpos[2]; + dest->plane.normal[0] = src->plane.normal[0]; + dest->plane.normal[1] = src->plane.normal[1]; + dest->plane.normal[2] = src->plane.normal[2]; + dest->plane.dist = src->plane.dist; + dest->hitgroup = src->hitgroup; + dest->ent = ent; +} + +void SV_ForceFullClientsUpdate(void) +{ + byte data[9216]; + sizebuf_t msg; + + Q_memset(&msg, 0, sizeof(msg)); + msg.buffername = "Force Update"; + msg.data = data; + msg.cursize = 0; + msg.maxsize = sizeof(data); + + for (int i = 0; i < g_psvs.maxclients; ++i) + { + client_t * client = &g_psvs.clients[i]; + if (client == host_client || client->active || client->connected || client->spawned) + SV_FullClientUpdate(client, &msg); + } + + Con_DPrintf("Client %s started recording. Send full update.\n", host_client->name); + Netchan_CreateFragments(1, &host_client->netchan, &msg); + Netchan_FragSend(&host_client->netchan); +} + +void SV_RunCmd(usercmd_t *ucmd, int random_seed) +{ + usercmd_t cmd = *ucmd; + int i; + edict_t *ent; + trace_t trace; + float frametime; + + if (host_client->ignorecmdtime > realtime) + { + host_client->cmdtime = (double)ucmd->msec / 1000.0 + host_client->cmdtime; + return; + } + + + host_client->ignorecmdtime = 0; + if (cmd.msec > 50) + { + cmd.msec = (byte)(ucmd->msec / 2.0); + SV_RunCmd(&cmd, random_seed); + cmd.msec = (byte)(ucmd->msec / 2.0); + cmd.impulse = 0; + SV_RunCmd(&cmd, random_seed); + return; + } + + + if (!host_client->fakeclient) + SV_SetupMove(host_client); + +#ifdef REHLDS_FIXES + if (sv_usercmd_custom_random_seed.value) + { + float fltTimeNow = float(Sys_FloatTime() * 1000.0); + random_seed = *reinterpret_cast((char *)&fltTimeNow); + } +#endif + + gEntityInterface.pfnCmdStart(sv_player, ucmd, random_seed); + frametime = float(ucmd->msec * 0.001); + host_client->svtimebase = frametime + host_client->svtimebase; + host_client->cmdtime = ucmd->msec / 1000.0 + host_client->cmdtime; + if (ucmd->impulse) + { + sv_player->v.impulse = ucmd->impulse; + + // Disable fullupdate via impulse 204 +#ifndef REHLDS_FIXES + if (ucmd->impulse == 204) + SV_ForceFullClientsUpdate(); +#endif // REHLDS_FIXES + } + sv_player->v.clbasevelocity[0] = 0; + sv_player->v.clbasevelocity[1] = 0; + sv_player->v.clbasevelocity[2] = 0; + sv_player->v.button = ucmd->buttons; +#ifdef REHLDS_FIXES + sv_player->v.light_level = ucmd->lightlevel; +#endif + SV_CheckMovingGround(sv_player, frametime); + pmove->oldangles[0] = sv_player->v.v_angle[0]; + pmove->oldangles[1] = sv_player->v.v_angle[1]; + pmove->oldangles[2] = sv_player->v.v_angle[2]; + if (!sv_player->v.fixangle) + { + sv_player->v.v_angle[0] = ucmd->viewangles[0]; + sv_player->v.v_angle[1] = ucmd->viewangles[1]; + sv_player->v.v_angle[2] = ucmd->viewangles[2]; + } + SV_PlayerRunPreThink(sv_player, (float)host_client->svtimebase); + SV_PlayerRunThink(sv_player, frametime, host_client->svtimebase); + if (Length(sv_player->v.basevelocity) > 0.0) + { + sv_player->v.clbasevelocity[0] = sv_player->v.basevelocity[0]; + sv_player->v.clbasevelocity[1] = sv_player->v.basevelocity[1]; + sv_player->v.clbasevelocity[2] = sv_player->v.basevelocity[2]; + } + + pmove->server = TRUE; + pmove->multiplayer = (g_psvs.maxclients > 1) ? TRUE : FALSE; + pmove->time = float(1000.0 * host_client->svtimebase); + pmove->usehull = (sv_player->v.flags & FL_DUCKING) == FL_DUCKING; + pmove->maxspeed = sv_maxspeed.value; + pmove->clientmaxspeed = sv_player->v.maxspeed; + pmove->flDuckTime = (float)sv_player->v.flDuckTime; + pmove->bInDuck = sv_player->v.bInDuck; + pmove->flTimeStepSound = sv_player->v.flTimeStepSound; + pmove->iStepLeft = sv_player->v.iStepLeft; + pmove->flFallVelocity = sv_player->v.flFallVelocity; + pmove->flSwimTime = (float)sv_player->v.flSwimTime; + pmove->oldbuttons = sv_player->v.oldbuttons; + + Q_strncpy(pmove->physinfo, host_client->physinfo, sizeof(pmove->physinfo) - 1); + pmove->physinfo[sizeof(pmove->physinfo) - 1] = 0; + + pmove->velocity[0] = sv_player->v.velocity[0]; + pmove->velocity[1] = sv_player->v.velocity[1]; + pmove->velocity[2] = sv_player->v.velocity[2]; + + pmove->movedir[0] = sv_player->v.movedir[0]; + pmove->movedir[1] = sv_player->v.movedir[1]; + pmove->movedir[2] = sv_player->v.movedir[2]; + + pmove->angles[0] = sv_player->v.v_angle[0]; + pmove->angles[1] = sv_player->v.v_angle[1]; + pmove->angles[2] = sv_player->v.v_angle[2]; + + pmove->basevelocity[0] = sv_player->v.basevelocity[0]; + pmove->basevelocity[1] = sv_player->v.basevelocity[1]; + pmove->basevelocity[2] = sv_player->v.basevelocity[2]; + + pmove->view_ofs[0] = sv_player->v.view_ofs[0]; + pmove->view_ofs[1] = sv_player->v.view_ofs[1]; + pmove->view_ofs[2] = sv_player->v.view_ofs[2]; + + pmove->punchangle[0] = sv_player->v.punchangle[0]; + pmove->punchangle[1] = sv_player->v.punchangle[1]; + pmove->punchangle[2] = sv_player->v.punchangle[2]; + + pmove->deadflag = sv_player->v.deadflag; + pmove->effects = sv_player->v.effects; + pmove->gravity = sv_player->v.gravity; + pmove->friction = sv_player->v.friction; + pmove->spectator = 0; + pmove->waterjumptime = sv_player->v.teleport_time; + + Q_memcpy(&pmove->cmd, &cmd, sizeof(pmove->cmd)); + + pmove->dead = sv_player->v.health <= 0.0; + pmove->movetype = sv_player->v.movetype; + pmove->flags = sv_player->v.flags; + pmove->player_index = NUM_FOR_EDICT(sv_player) - 1; + + pmove->iuser1 = sv_player->v.iuser1; + pmove->iuser2 = sv_player->v.iuser2; + pmove->iuser3 = sv_player->v.iuser3; + pmove->iuser4 = sv_player->v.iuser4; + pmove->fuser1 = sv_player->v.fuser1; + pmove->fuser2 = sv_player->v.fuser2; + pmove->fuser3 = sv_player->v.fuser3; + pmove->fuser4 = sv_player->v.fuser4; + + pmove->vuser1[0] = sv_player->v.vuser1[0]; + pmove->vuser1[1] = sv_player->v.vuser1[1]; + pmove->vuser1[2] = sv_player->v.vuser1[2]; + + pmove->vuser2[0] = sv_player->v.vuser2[0]; + pmove->vuser2[1] = sv_player->v.vuser2[1]; + pmove->vuser2[2] = sv_player->v.vuser2[2]; + + pmove->vuser3[0] = sv_player->v.vuser3[0]; + pmove->vuser3[1] = sv_player->v.vuser3[1]; + pmove->vuser3[2] = sv_player->v.vuser3[2]; + + pmove->vuser4[0] = sv_player->v.vuser4[0]; + pmove->vuser4[1] = sv_player->v.vuser4[1]; + pmove->vuser4[2] = sv_player->v.vuser4[2]; + + pmove->origin[0] = sv_player->v.origin[0]; + pmove->origin[1] = sv_player->v.origin[1]; + pmove->origin[2] = sv_player->v.origin[2]; + + SV_AddLinksToPM(sv_areanodes, pmove->origin); + + pmove->frametime = frametime; + pmove->runfuncs = TRUE; + pmove->PM_PlaySound = PM_SV_PlaySound; + pmove->PM_TraceTexture = PM_SV_TraceTexture; + pmove->PM_PlaybackEventFull = PM_SV_PlaybackEventFull; + + gEntityInterface.pfnPM_Move(pmove, TRUE); + + sv_player->v.deadflag = pmove->deadflag; + sv_player->v.effects = pmove->effects; + sv_player->v.teleport_time = pmove->waterjumptime; + sv_player->v.waterlevel = pmove->waterlevel; + sv_player->v.watertype = pmove->watertype; + sv_player->v.flags = pmove->flags; + sv_player->v.friction = pmove->friction; + sv_player->v.movetype = pmove->movetype; + sv_player->v.maxspeed = pmove->clientmaxspeed; + sv_player->v.iStepLeft = pmove->iStepLeft; + + sv_player->v.view_ofs[0] = pmove->view_ofs[0]; + sv_player->v.view_ofs[1] = pmove->view_ofs[1]; + sv_player->v.view_ofs[2] = pmove->view_ofs[2]; + + sv_player->v.movedir[0] = pmove->movedir[0]; + sv_player->v.movedir[1] = pmove->movedir[1]; + sv_player->v.movedir[2] = pmove->movedir[2]; + + sv_player->v.punchangle[0] = pmove->punchangle[0]; + sv_player->v.punchangle[1] = pmove->punchangle[1]; + sv_player->v.punchangle[2] = pmove->punchangle[2]; + + if (pmove->onground == -1) + { + sv_player->v.flags &= ~FL_ONGROUND; + } + else + { + sv_player->v.flags |= FL_ONGROUND; + sv_player->v.groundentity = EDICT_NUM(pmove->physents[pmove->onground].info); + } + + sv_player->v.origin[0] = pmove->origin[0]; + sv_player->v.origin[1] = pmove->origin[1]; + sv_player->v.origin[2] = pmove->origin[2]; + + sv_player->v.velocity[0] = pmove->velocity[0]; + sv_player->v.velocity[1] = pmove->velocity[1]; + sv_player->v.velocity[2] = pmove->velocity[2]; + + sv_player->v.basevelocity[0] = pmove->basevelocity[0]; + sv_player->v.basevelocity[1] = pmove->basevelocity[1]; + sv_player->v.basevelocity[2] = pmove->basevelocity[2]; + + if (!sv_player->v.fixangle) + { + sv_player->v.v_angle[0] = pmove->angles[0]; + sv_player->v.v_angle[1] = pmove->angles[1]; + sv_player->v.v_angle[2] = pmove->angles[2]; + sv_player->v.angles[0] = float(-pmove->angles[0] / 3.0); + sv_player->v.angles[1] = pmove->angles[1]; + sv_player->v.angles[2] = pmove->angles[2]; + } + + sv_player->v.bInDuck = pmove->bInDuck; + sv_player->v.flDuckTime = (int)pmove->flDuckTime; + sv_player->v.flTimeStepSound = pmove->flTimeStepSound; + sv_player->v.flFallVelocity = pmove->flFallVelocity; + sv_player->v.flSwimTime = (int)pmove->flSwimTime; + sv_player->v.oldbuttons = pmove->cmd.buttons; + + sv_player->v.iuser1 = pmove->iuser1; + sv_player->v.iuser2 = pmove->iuser2; + sv_player->v.iuser3 = pmove->iuser3; + sv_player->v.iuser4 = pmove->iuser4; + sv_player->v.fuser1 = pmove->fuser1; + sv_player->v.fuser2 = pmove->fuser2; + sv_player->v.fuser3 = pmove->fuser3; + sv_player->v.fuser4 = pmove->fuser4; + + sv_player->v.vuser1[0] = pmove->vuser1[0]; + sv_player->v.vuser1[1] = pmove->vuser1[1]; + sv_player->v.vuser1[2] = pmove->vuser1[2]; + + sv_player->v.vuser2[0] = pmove->vuser2[0]; + sv_player->v.vuser2[1] = pmove->vuser2[1]; + sv_player->v.vuser2[2] = pmove->vuser2[2]; + + sv_player->v.vuser3[0] = pmove->vuser3[0]; + sv_player->v.vuser3[1] = pmove->vuser3[1]; + sv_player->v.vuser3[2] = pmove->vuser3[2]; + + sv_player->v.vuser4[0] = pmove->vuser4[0]; + sv_player->v.vuser4[1] = pmove->vuser4[1]; + sv_player->v.vuser4[2] = pmove->vuser4[2]; + + SetMinMaxSize(sv_player, player_mins[pmove->usehull], player_maxs[pmove->usehull], 0); + if (host_client->edict->v.solid) + { + SV_LinkEdict(sv_player, TRUE); + vec3_t vel; + + vel[0] = sv_player->v.velocity[0]; + vel[1] = sv_player->v.velocity[1]; + vel[2] = sv_player->v.velocity[2]; + for (i = 0; i < pmove->numtouch; ++i) + { + pmtrace_t *tr = &pmove->touchindex[i]; + ent = EDICT_NUM(pmove->physents[tr->ent].info); + SV_ConvertPMTrace(&trace, tr, ent); + sv_player->v.velocity[0] = tr->deltavelocity[0]; + sv_player->v.velocity[1] = tr->deltavelocity[1]; + sv_player->v.velocity[2] = tr->deltavelocity[2]; + SV_Impact(ent, sv_player, &trace); + } + sv_player->v.velocity[0] = vel[0]; + sv_player->v.velocity[1] = vel[1]; + sv_player->v.velocity[2] = vel[2]; + } + + gGlobalVariables.time = (float)host_client->svtimebase; + gGlobalVariables.frametime = frametime; + gEntityInterface.pfnPlayerPostThink(sv_player); + gEntityInterface.pfnCmdEnd(sv_player); + + if (!host_client->fakeclient) + SV_RestoreMove(host_client); +} + +int SV_ValidateClientCommand(char *pszCommand) +{ + const char *p; + int i = 0; + + COM_Parse(pszCommand); + while ((p = clcommands[i]) != NULL) + { + if (!Q_stricmp(com_token, p)) + { + return 1; + } + i++; + } + return 0; +} + +float SV_CalcClientTime(client_t *cl) +{ + float minping; + float maxping; + int backtrack; + + float ping = 0.0; + int count = 0; + backtrack = (int)sv_unlagsamples.value; + + if (backtrack < 1) + backtrack = 1; + + if (backtrack >= (SV_UPDATE_BACKUP <= 16 ? SV_UPDATE_BACKUP : 16)) + backtrack = (SV_UPDATE_BACKUP <= 16 ? SV_UPDATE_BACKUP : 16); + + if (backtrack <= 0) + return 0.0f; + + for (int i = 0; i < backtrack; i++) + { + client_frame_t *frame = &cl->frames[SV_UPDATE_MASK & (cl->netchan.incoming_acknowledged - i)]; + if (frame->ping_time <= 0.0f) + continue; + + ++count; + ping += frame->ping_time; + } + + if (!count) + return 0.0f; + + minping = 9999.0; + maxping = -9999.0; + ping /= count; + + for (int i = 0; i < (SV_UPDATE_BACKUP <= 4 ? SV_UPDATE_BACKUP : 4); i++) + { + client_frame_t *frame = &cl->frames[SV_UPDATE_MASK & (cl->netchan.incoming_acknowledged - i)]; + if (frame->ping_time <= 0.0f) + continue; + + if (frame->ping_time < minping) + minping = frame->ping_time; + + if (frame->ping_time > maxping) + maxping = frame->ping_time; + } + + if (maxping < minping || fabs(maxping - minping) <= 0.2) + return ping; + + return 0.0f; +} + +void SV_ComputeLatency(client_t *cl) +{ + cl->latency = SV_CalcClientTime(cl); +} + +int SV_UnlagCheckTeleport(vec_t *v1, vec_t *v2) +{ + for (int i = 0; i < 3; i++) + { + if (fabs(v1[i] - v2[i]) > 128) + return 1; + } + + return 0; +} + +void SV_GetTrueOrigin(int player, vec_t *origin) +{ + if (!host_client->lw || !host_client->lc) + return; + + if (sv_unlag.value == 0 || g_psvs.maxclients <= 1 || !host_client->active) + return; + + if (player < 0 || player >= g_psvs.maxclients) + return; + + if (truepositions[player].active && truepositions[player].needrelink) + { + origin[0] = truepositions[player].oldorg[0]; + origin[1] = truepositions[player].oldorg[1]; + origin[2] = truepositions[player].oldorg[2]; + } +} + +void SV_GetTrueMinMax(int player, float **fmin, float **fmax) +{ + if (!host_client->lw || !host_client->lc) + return; + + if (sv_unlag.value == 0.0f || g_psvs.maxclients <= 1) + return; + + if (!host_client->active || player < 0 || player >= g_psvs.maxclients) + return; + + if (truepositions[player].active && truepositions[player].needrelink) + { + *fmin = truepositions[player].oldabsmin; + *fmax = truepositions[player].oldabsmax; + } +} + +entity_state_t *SV_FindEntInPack(int index, packet_entities_t *pack) +{ + if (pack->num_entities <= 0) + return NULL; + + for (int i = 0; i < pack->num_entities; i++) + { + if (pack->entities[i].number == index) + return &pack->entities[i]; + } + + return NULL; +} + +void SV_SetupMove(client_t *_host_client) +{ + struct client_s *cl; + float cl_interptime; + client_frame_t *nextFrame; + entity_state_t *state; + sv_adjusted_positions_t *pos; + float frac; + entity_state_t *pnextstate; + int i; + client_frame_t *frame; + vec3_t origin; + vec3_t delta; + + +#ifdef REHLDS_FIXES + double targettime; // FP precision fix +#else + float targettime; +#endif // REHLDS_FIXES + + Q_memset(truepositions, 0, sizeof(truepositions)); + nofind = 1; + if (!gEntityInterface.pfnAllowLagCompensation()) + return; + + if (sv_unlag.value == 0.0f || !_host_client->lw || !_host_client->lc) + return; + + if (g_psvs.maxclients <= 1 || !_host_client->active) + return; + + nofind = 0; + for (int i = 0; i < g_psvs.maxclients; i++) + { + cl = &g_psvs.clients[i]; + if (cl == _host_client || !cl->active) + continue; + + truepositions[i].oldorg[0] = cl->edict->v.origin[0]; + truepositions[i].oldorg[1] = cl->edict->v.origin[1]; + truepositions[i].oldorg[2] = cl->edict->v.origin[2]; + truepositions[i].oldabsmin[0] = cl->edict->v.absmin[0]; + truepositions[i].oldabsmin[1] = cl->edict->v.absmin[1]; + truepositions[i].oldabsmin[2] = cl->edict->v.absmin[2]; + truepositions[i].oldabsmax[0] = cl->edict->v.absmax[0]; + truepositions[i].oldabsmax[1] = cl->edict->v.absmax[1]; + truepositions[i].active = 1; + truepositions[i].oldabsmax[2] = cl->edict->v.absmax[2]; + } + + float clientLatency = _host_client->latency; + if (clientLatency > 1.5) + clientLatency = 1.5f; + + if (sv_maxunlag.value != 0.0f) + { + if (sv_maxunlag.value < 0.0) + Cvar_SetValue("sv_maxunlag", 0.0); + + if (clientLatency >= sv_maxunlag.value) + clientLatency = sv_maxunlag.value; + } + + cl_interptime = _host_client->lastcmd.lerp_msec / 1000.0f; + + if (cl_interptime > 0.1) + cl_interptime = 0.1f; + + if (_host_client->next_messageinterval > cl_interptime) + cl_interptime = (float) _host_client->next_messageinterval; + +#ifdef REHLDS_FIXES + // FP Precision fix (targettime is double there, not float) + targettime = realtime - clientLatency - cl_interptime + sv_unlagpush.value; + if (targettime > realtime) + targettime = realtime; +#else + targettime = float(realtime - clientLatency - cl_interptime + sv_unlagpush.value); + if (targettime > realtime) + targettime = float(realtime); +#endif // REHLDS_FIXES + + if (SV_UPDATE_BACKUP <= 0) + { + Q_memset(truepositions, 0, sizeof(truepositions)); + nofind = 1; + return; + } + + frame = nextFrame = NULL; + for (i = 0; i < SV_UPDATE_BACKUP; i++, frame = nextFrame) + { + nextFrame = &_host_client->frames[SV_UPDATE_MASK & (_host_client->netchan.outgoing_sequence + ~i)]; + for (int j = 0; j < nextFrame->entities.num_entities; j++) + { + state = &nextFrame->entities.entities[j]; + +#ifdef REHLDS_OPT_PEDANTIC + if (state->number <= 0) + continue; + + if (state->number > g_psvs.maxclients) + break; // players are always in the beginning of the list, no need to look more +#else + if (state->number <= 0 || state->number > g_psvs.maxclients) + continue; +#endif + + pos = &truepositions[state->number - 1]; + if (pos->deadflag) + continue; + + + if (state->health <= 0) + pos->deadflag = 1; + + if (state->effects & EF_NOINTERP) + pos->deadflag = 1; + + if (pos->temp_org_setflag) + { + if (SV_UnlagCheckTeleport(state->origin, pos->temp_org)) + pos->deadflag = 1; + } + else + { + pos->temp_org_setflag = 1; + } + + pos->temp_org[0] = state->origin[0]; + pos->temp_org[1] = state->origin[1]; + pos->temp_org[2] = state->origin[2]; + } + + if (targettime > nextFrame->senttime) + break; + } + + if ( i >= SV_UPDATE_BACKUP || targettime - nextFrame->senttime > 1.0) + { + Q_memset(truepositions, 0, sizeof(truepositions)); + nofind = 1; + return; + } + + if (frame) + { + float timeDiff = float(frame->senttime - nextFrame->senttime); + if (timeDiff == 0.0) + frac = 0.0; + else + { + frac = float((targettime - nextFrame->senttime) / timeDiff); + if (frac <= 1.0) + { + if (frac < 0.0) + frac = 0.0; + } + else + frac = 1.0; + } + } + else + { + frame = nextFrame; + frac = 0.0; + } + + for (i = 0; i < nextFrame->entities.num_entities; i++) + { + state = &nextFrame->entities.entities[i]; + if (state->number <= 0 || state->number > g_psvs.maxclients) + continue; + + cl = &g_psvs.clients[state->number - 1]; + if (cl == _host_client || !cl->active) + continue; + + pos = &truepositions[state->number - 1]; + if (pos->deadflag) + continue; + + if (!pos->active) + { + Con_DPrintf("tried to store off position of bogus player %i/%s\n", i, cl->name); + continue; + } + + pnextstate = SV_FindEntInPack(state->number, &frame->entities); + + if (pnextstate) + { + delta[0] = pnextstate->origin[0] - state->origin[0]; + delta[1] = pnextstate->origin[1] - state->origin[1]; + delta[2] = pnextstate->origin[2] - state->origin[2]; + VectorMA(state->origin, frac, delta, origin); + } + else + { + origin[0] = state->origin[0]; + origin[1] = state->origin[1]; + origin[2] = state->origin[2]; + } + pos->neworg[0] = origin[0]; + pos->neworg[1] = origin[1]; + pos->neworg[2] = origin[2]; + pos->initial_correction_org[0] = origin[0]; + pos->initial_correction_org[1] = origin[1]; + pos->initial_correction_org[2] = origin[2]; + if (!VectorCompare(origin, cl->edict->v.origin)) + { + cl->edict->v.origin[0] = origin[0]; + cl->edict->v.origin[1] = origin[1]; + cl->edict->v.origin[2] = origin[2]; + SV_LinkEdict(cl->edict, FALSE); + pos->needrelink = 1; + } + } +} + +void SV_RestoreMove(client_t *_host_client) +{ + sv_adjusted_positions_t *pos; + client_t *cli; + + if (nofind) + { + nofind = 0; + return; + } + + if (!gEntityInterface.pfnAllowLagCompensation()) + return; + + if (g_psvs.maxclients <= 1 || sv_unlag.value == 0.0) + return; + + if (!_host_client->lw || !_host_client->lc || !_host_client->active) + return; + + for (int i = 0; i < g_psvs.maxclients; i++) + { + cli = &g_psvs.clients[i]; + pos = &truepositions[i]; + + if (cli == _host_client ||! cli->active) + continue; + + if (VectorCompare(pos->neworg, pos->oldorg) || !pos->needrelink) + continue; + + if (!pos->active) + { + Con_DPrintf("SV_RestoreMove: Tried to restore 'inactive' player %i/%s\n", i, &cli->name[4]); + continue; + } + + if (VectorCompare(pos->initial_correction_org, cli->edict->v.origin)) + { + cli->edict->v.origin[0] = pos->oldorg[0]; + cli->edict->v.origin[1] = pos->oldorg[1]; + cli->edict->v.origin[2] = pos->oldorg[2]; + SV_LinkEdict(cli->edict, FALSE); + } + } +} + +void SV_ParseStringCommand(client_t *pSenderClient) +{ + //check string commands rate for this player +#ifdef REHLDS_FIXES + g_StringCommandsRateLimiter.StringCommandIssued(pSenderClient - g_psvs.clients); + + if (!pSenderClient->connected) { + return; //return if player was kicked + } +#endif + + char *s = MSG_ReadString(); + int ret = SV_ValidateClientCommand(s); + switch (ret) + { + case 0: +#ifndef REHLDS_OPT_PEDANTIC + if (Q_strlen(s) > 127) +#endif + { + s[127] = 0; + } + Cmd_TokenizeString(s); + gEntityInterface.pfnClientCommand(sv_player); + break; + case 1: + Cmd_ExecuteString(s, src_client); + break; + case 2: // TODO: Check not used path + Cbuf_InsertText(s); + break; + } +} + +void SV_ParseDelta(client_t *pSenderClient) +{ + host_client->delta_sequence = MSG_ReadByte(); +} + +void EXT_FUNC SV_EstablishTimeBase_mod(IGameClient *cl, usercmd_t *cmds, int dropped, int numbackup, int numcmds) +{ + SV_EstablishTimeBase_internal(cl->GetClient(), cmds, dropped, numbackup, numcmds); +} + +void SV_EstablishTimeBase(client_t *cl, usercmd_t *cmds, int dropped, int numbackup, int numcmds) +{ + return g_RehldsHookchains.m_SV_EstablishTimeBase.callChain(SV_EstablishTimeBase_mod, GetRehldsApiClient(cl), cmds, dropped, numbackup, numcmds); +} + +void SV_EstablishTimeBase_internal(client_t *cl, usercmd_t *cmds, int dropped, int numbackup, int numcmds) +{ + int i; + double runcmd_time = 0.0; + double time_at_end = 0.0; + constexpr int MAX_DROPPED_CMDS = 24; + + // If we haven't dropped too many packets, then run some commands + if (dropped < MAX_DROPPED_CMDS) + { + if (dropped > numbackup) + { + // Con_Printf("%s: lost %i cmds\n", __func__, dropped - numbackup); + } + + int droppedcmds = dropped; + + // Run the last known cmd for each dropped cmd we don't have a backup for + while (droppedcmds > numbackup) + { + runcmd_time += cl->lastcmd.msec / 1000.0; + droppedcmds--; + } + + // Now run the "history" commands if we still have dropped packets + while (droppedcmds > 0) + { + int cmdnum = numcmds + droppedcmds - 1; + runcmd_time += cmds[cmdnum].msec / 1000.0; + droppedcmds--; + } + } + + // Now run any new command(s). Go backward because the most recent command is at index 0 + for (i = numcmds - 1; i >= 0; i--) + { + time_at_end += cmds[i].msec / 1000.0; + } + + cl->svtimebase = host_frametime + g_psv.time - (time_at_end + runcmd_time); +} + +void SV_ParseMove(client_t *pSenderClient) +{ + client_frame_t *frame; + int placeholder; + int mlen; + unsigned int packetLossByte; + int numcmds; + int totalcmds; + byte cbpktchecksum; + usercmd_t *cmd; + usercmd_t cmds[64]; + usercmd_t cmdNull; + float packet_loss; + byte cbchecksum; + int numbackup; + + if (g_balreadymoved) + { + msg_badread = 1; + return; + } + g_balreadymoved = 1; + + frame = &host_client->frames[SV_UPDATE_MASK & host_client->netchan.incoming_acknowledged]; + Q_memset(&cmdNull, 0, sizeof(cmdNull)); + + placeholder = msg_readcount + 1; + mlen = MSG_ReadByte(); + cbchecksum = MSG_ReadByte(); + + if (sv_invalid_length.value == 1) + { + if (mlen <= 0 || !SZ_HasSpaceToRead(&net_message, mlen)) + { + msg_badread = TRUE; + Con_DPrintf("%s: %s:%s invalid length: %d\n", __func__, host_client->name, NET_AdrToString(host_client->netchan.remote_address), mlen); + SV_DropClient(host_client, FALSE, "Invalid length"); + return; + } + } + + COM_UnMunge(&net_message.data[placeholder + 1], mlen, host_client->netchan.incoming_sequence); + + packetLossByte = MSG_ReadByte(); + numbackup = MSG_ReadByte(); + numcmds = MSG_ReadByte(); + + packet_loss = float(packetLossByte & 0x7F); + pSenderClient->m_bLoopback = (packetLossByte >> 7) & 1; + totalcmds = numcmds + numbackup; + net_drop += 1 - numcmds; + if (totalcmds < 0 || totalcmds >= CMD_MAXBACKUP - 1) + { + Con_Printf("SV_ReadClientMessage: too many cmds %i sent for %s/%s\n", totalcmds, host_client->name, NET_AdrToString(host_client->netchan.remote_address)); + SV_DropClient(host_client, FALSE, "CMD_MAXBACKUP hit"); + msg_badread = 1; + return; + } + + usercmd_t* from = &cmdNull; + for (int i = totalcmds - 1; i >= 0; i--) + { + MSG_ReadUsercmd(&cmds[i], from); + from = &cmds[i]; + } + + if (!g_psv.active || !(host_client->active || host_client->spawned)) + return; + + if (msg_badread) + { + Con_Printf("Client %s:%s sent a bogus command packet\n", host_client->name, NET_AdrToString(host_client->netchan.remote_address)); + return; + } + + cbpktchecksum = COM_BlockSequenceCRCByte(&net_message.data[placeholder + 1], msg_readcount - placeholder - 1, host_client->netchan.incoming_sequence); + if (cbpktchecksum != cbchecksum) + { + Con_DPrintf("Failed command checksum for %s:%s\n", host_client->name, NET_AdrToString(host_client->netchan.remote_address)); + msg_badread = 1; + return; + } + + host_client->packet_loss = packet_loss; + if (!g_psv.paused && (g_psvs.maxclients > 1 || !key_dest) && !(sv_player->v.flags & FL_FROZEN)) + { + sv_player->v.v_angle[0] = cmds[0].viewangles[0]; + sv_player->v.v_angle[1] = cmds[0].viewangles[1]; + sv_player->v.v_angle[2] = cmds[0].viewangles[2]; + } + else + { + for (int i = 0; i < numcmds; i++) + { + cmd = &cmds[i]; + cmd->msec = 0; + cmd->forwardmove = 0; + cmd->sidemove = 0; + cmd->upmove = 0; + cmd->buttons = 0; + + if (sv_player->v.flags & FL_FROZEN) + cmd->impulse = 0; + + cmd->viewangles[0] = sv_player->v.v_angle[0]; + cmd->viewangles[1] = sv_player->v.v_angle[1]; + cmd->viewangles[2] = sv_player->v.v_angle[2]; + } + + net_drop = 0; + } + + //check move commands rate for this player +#ifdef REHLDS_FIXES + int numCmdsToIssue = numcmds; + if (net_drop > 0) { + numCmdsToIssue += net_drop; + } + g_MoveCommandRateLimiter.MoveCommandsIssued(host_client - g_psvs.clients, numCmdsToIssue); + + if (!host_client->connected) { + return; //return if player was kicked + } +#endif + +#ifndef REHLDS_FIXES + // dup and more correct in SV_RunCmd + sv_player->v.button = cmds[0].buttons; + sv_player->v.light_level = cmds[0].lightlevel; +#endif + SV_EstablishTimeBase(host_client, cmds, net_drop, numbackup, numcmds); + if (net_drop < 24) + { + while (net_drop > numbackup) + { + SV_RunCmd(&host_client->lastcmd, 0); + net_drop--; + } + + while (net_drop > 0) + { + SV_RunCmd(&cmds[numcmds + net_drop - 1], host_client->netchan.incoming_sequence - (numcmds + net_drop - 1)); + net_drop--; + } + + } + + for (int i = numcmds - 1; i >= 0; i--) + { + SV_RunCmd(&cmds[i], host_client->netchan.incoming_sequence - i); + } + +#ifdef REHLDS_FIXES + if (numcmds) + host_client->lastcmd = cmds[numcmds - 1]; + else if (numbackup) + host_client->lastcmd = cmds[0]; +#else + host_client->lastcmd = cmds[0]; +#endif + + frame->ping_time -= float(host_client->lastcmd.msec * 0.5 / 1000.0); + if (frame->ping_time < 0.0) + frame->ping_time = 0; + + if (sv_player->v.animtime > host_frametime + g_psv.time) + sv_player->v.animtime = float(host_frametime + g_psv.time); +} + +void SV_ParseVoiceData(client_t *cl) +{ + char chReceived[4096]; + int iClient = cl - g_psvs.clients; + unsigned int nDataLength = MSG_ReadShort(); + if (nDataLength > sizeof(chReceived)) + { + Con_DPrintf("SV_ParseVoiceData: invalid incoming packet.\n"); + SV_DropClient(cl, FALSE, "Invalid voice data\n"); + return; + } + + MSG_ReadBuf(nDataLength, chReceived); + cl->m_lastvoicetime = g_psv.time; + + if (sv_voiceenable.value == 0.0f) + return; + + for (int i = 0; i < g_psvs.maxclients; i++) + { + client_t *pDestClient = &g_psvs.clients[i]; + if (!((1 << (i & 0x1F)) & cl->m_VoiceStreams[i >> 5]) && i != iClient) + continue; + + if (!pDestClient->active && !pDestClient->connected && i != iClient) + continue; + + int nSendLength = nDataLength; + if (i == iClient && !pDestClient->m_bLoopback) + nSendLength = 0; + + if (pDestClient->datagram.cursize + nSendLength + 6 < pDestClient->datagram.maxsize) + { + MSG_WriteByte(&pDestClient->datagram, svc_voicedata); + MSG_WriteByte(&pDestClient->datagram, iClient); + MSG_WriteShort(&pDestClient->datagram, nSendLength); + MSG_WriteBuf(&pDestClient->datagram, nSendLength, chReceived); + } + } +} + +void SV_IgnoreHLTV(client_t *cl) +{ +} + +void SV_ParseCvarValue(client_t *cl) +{ + char *value; + value = MSG_ReadString(); + if (gNewDLLFunctions.pfnCvarValue) + gNewDLLFunctions.pfnCvarValue(cl->edict, value); + + Con_DPrintf("Cvar query response: name:%s, value:%s\n", cl->name, value); +} + +void SV_ParseCvarValue2(client_t *cl) +{ + int requestID = MSG_ReadLong(); + + char cvarName[255]; + Q_strncpy(cvarName, MSG_ReadString(), sizeof(cvarName)); + cvarName[sizeof(cvarName) - 1] = 0; + + char* value = MSG_ReadString(); + + if (gNewDLLFunctions.pfnCvarValue2) + gNewDLLFunctions.pfnCvarValue2(cl->edict, requestID, cvarName, value); + + Con_DPrintf("Cvar query response: name:%s, request ID %d, cvar:%s, value:%s\n", cl->name, requestID, cvarName, value); +} + +void EXT_FUNC SV_HandleClientMessage_api(IGameClient* client, uint8 opcode) { + client_t* cl = client->GetClient(); + if (opcode < clc_bad || opcode > clc_cvarvalue2) + { + // TODO: Are we forced to use msg_badread for break the loop. + static_assert(REHLDS_API_VERSION_MAJOR <= 3, "Bump major API DETECTED!! You shall rework the hookchain, make function returnable"); + msg_badread = 1; + + if (sv_msg_badread.value == 1) + { + Con_Printf("SV_ReadClientMessage: unknown command char (%d)\n", opcode); + } + + SV_DropClient(cl, FALSE, "Bad command character in client command"); + return; + } + +#ifdef REHLDS_FIXES + // Save current name of the client before a possible kick + char name[32]; + Q_strlcpy(name, host_client->name); +#endif + + void(*func)(client_t *) = sv_clcfuncs[opcode].pfnParse; + if (func) + func(cl); + +#ifdef REHLDS_FIXES + if (msg_badread) + { + if (sv_msg_badread.value == 1) + { + Con_Printf("SV_ReadClientMessage: badread on %s, opcode %s\n", name, sv_clcfuncs[opcode].pszname); + } + } +#endif + +} + +void SV_ExecuteClientMessage(client_t *cl) +{ + g_balreadymoved = 0; + client_frame_t * frame = &cl->frames[SV_UPDATE_MASK & cl->netchan.incoming_acknowledged]; + frame->ping_time = realtime - frame->senttime - cl->next_messageinterval; + if (frame->senttime == 0.0) + frame->ping_time = 0; + + if (realtime - cl->connection_started < 2.0 && frame->ping_time > 0.0) + frame->ping_time = 0; + + SV_ComputeLatency(cl); + host_client = cl; + sv_player = cl->edict; + cl->delta_sequence = -1; + pmove = &g_svmove; + IGameClient* apiClient = GetRehldsApiClient(cl); + + while (1) + { + if (msg_badread) + { +#ifdef REHLDS_FIXES + if (sv_msg_badread.value == 1) + { + Con_Printf("SV_ReadClientMessage: badread on %s\n", host_client->name); + } + + if (host_client->active) + SV_ClientPrintf("Badread\n"); +#else // REHLDS_FIXES + if (sv_msg_badread.value == 1) + { + Con_Printf("SV_ReadClientMessage: badread\n"); + } +#endif // REHLDS_FIXES + return; + } + + int c = MSG_ReadByte(); + if (c == -1) + return; + + g_RehldsHookchains.m_HandleNetCommand.callChain(SV_HandleClientMessage_api, apiClient, c); + +#ifdef REHLDS_FIXES + // FIXED: Don't handle remaining packets if got dropclient above + if (!cl->connected && !cl->active && !cl->spawned) + break; +#endif // REHLDS_FIXES + } +} + +qboolean SV_SetPlayer(int idnum) +{ + for (int i = 0; i < g_psvs.maxclients; i++) + { + client_t *cl = &g_psvs.clients[i]; + if (!cl->spawned || !cl->active || !cl->connected) + continue; + + if (cl->userid == idnum) + { + host_client = cl; + sv_player = cl->edict; + return 1; + } + } + + Con_Printf("Userid %i is not on the server\n", idnum); + return 0; +} + +void SV_ShowServerinfo_f(void) +{ + if (cmd_source == src_command) + { + Cmd_ForwardToServer(); + } + else + { +#ifdef REHLDS_FIXES + // fix print message client console + SV_ClientPrintf(Info_Serverinfo()); +#else + Info_Print(Info_Serverinfo()); +#endif // REHLDS_FIXES + } +} + +void SV_SendEnts_f(void) +{ + if (cmd_source == src_command) + { + Cmd_ForwardToServer(); + } + else + { + if (host_client->active && host_client->spawned) + { + if (host_client->connected) + { + host_client->fully_connected = TRUE; + +#ifdef REHLDS_FIXES + // See SV_CheckFile function + if (sv_delayed_spray_upload.value) + { + resource_t *res = host_client->resourcesneeded.pNext; + if (res != &host_client->resourcesneeded) + { + // TODO: all this is already checked earlier + if (res->ucFlags & RES_WASMISSING && res->type == t_decal && res->ucFlags & RES_CUSTOM) + { + if (sv_rehlds_force_dlmax.value) + { + MSG_WriteByte(&host_client->netchan.message, svc_stufftext); + MSG_WriteString(&host_client->netchan.message, va("cl_dlmax %i\n", FRAGMENT_MAX_SIZE)); + } + + MSG_WriteByte(&host_client->netchan.message, svc_stufftext); + MSG_WriteString(&host_client->netchan.message, va("upload !MD5%s\n", MD5_Print(res->rgucMD5_hash))); + } + } + } +#endif // REHLDS_FIXES + } + } + } +} + +void SV_FullUpdate_f(void) +{ + int entIndex; + float ltime; + + if (cmd_source == src_command) + { + Cmd_ForwardToServer(); + return; + } + + if (host_client->active) + { + entIndex = IndexOfEdict(host_client->edict); + if (s_LastFullUpdate[entIndex] > g_psv.time) + s_LastFullUpdate[entIndex] = 0; + + ltime = g_psv.time - s_LastFullUpdate[entIndex]; + if (ltime <= 0.0) + ltime = 0.0; + + if (ltime < 0.45 && g_psv.time > 0.45) + { + Con_DPrintf( + "%s is spamming fullupdate: (%f) (%f) (%f)\n", + host_client->name, + g_psv.time, + s_LastFullUpdate[entIndex], + ltime); + return; + } + s_LastFullUpdate[entIndex] = g_psv.time; + +#ifdef REHLDS_FIXES + // it's not need until not active + SV_ForceFullClientsUpdate(); + gEntityInterface.pfnClientCommand( sv_player ); +#endif // REHLDS_FIXES + } + +#ifndef REHLDS_FIXES + SV_ForceFullClientsUpdate(); + gEntityInterface.pfnClientCommand(sv_player); +#endif // REHLDS_FIXES +} + +edict_t *sv_player; + +//int giSkip; +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 }; +#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 + +cvar_t sv_edgefriction = { "edgefriction", "2", FCVAR_SERVER, 0.0f, NULL }; +cvar_t sv_maxspeed = { "sv_maxspeed", "320", FCVAR_SERVER, 0.0f, NULL }; +cvar_t sv_accelerate = { "sv_accelerate", "10", FCVAR_SERVER, 0.0f, NULL }; +cvar_t sv_footsteps = { "mp_footsteps", "1", FCVAR_SERVER, 0.0f, NULL }; +cvar_t sv_rollspeed = { "sv_rollspeed", "0.0", 0, 0.0f, NULL }; +cvar_t sv_rollangle = { "sv_rollangle", "0.0", 0, 0.0f, NULL }; +cvar_t sv_unlag = { "sv_unlag", "1", 0, 0.0f, NULL }; +cvar_t sv_maxunlag = { "sv_maxunlag", "0.5", 0, 0.0f, NULL }; +cvar_t sv_unlagpush = { "sv_unlagpush", "0.0", 0, 0.0f, NULL }; +cvar_t sv_unlagsamples = { "sv_unlagsamples", "1", 0, 0.0f, NULL }; +cvar_t mp_consistency = { "mp_consistency", "1", FCVAR_SERVER, 0.0f, NULL }; +cvar_t sv_voiceenable = { "sv_voiceenable", "1", FCVAR_SERVER | FCVAR_ARCHIVE, 0.0f, NULL }; + +clc_func_t sv_clcfuncs[] = { + { clc_bad, "clc_bad", nullptr }, + { clc_nop, "clc_nop", nullptr }, + { clc_move, "clc_move", &SV_ParseMove }, + { clc_stringcmd, "clc_stringcmd", &SV_ParseStringCommand }, + { clc_delta, "clc_delta", &SV_ParseDelta }, + { clc_resourcelist, "clc_resourcelist", &SV_ParseResourceList }, + { clc_tmove, "clc_tmove", nullptr }, + { clc_fileconsistency, "clc_fileconsistency", &SV_ParseConsistencyResponse }, + { clc_voicedata, "clc_voicedata", &SV_ParseVoiceData }, + { clc_hltv, "clc_hltv", &SV_IgnoreHLTV }, + { clc_cvarvalue, "clc_cvarvalue", &SV_ParseCvarValue }, + { clc_cvarvalue2, "clc_cvarvalue2", &SV_ParseCvarValue2 }, + { clc_endoflist, "End of List", nullptr }, +}; + +bool EXT_FUNC SV_CheckConsistencyResponse_API(IGameClient *client, resource_t *res, uint32 hash) { + return (hash != *(uint32 *)&res->rgucMD5_hash[0]); +} + void SV_ParseConsistencyResponse(client_t *pSenderClient) { vec3_t mins;