2
0
mirror of https://github.com/rehlds/rehlds.git synced 2025-01-01 01:25:38 +03:00
rehlds/rehlds/engine/sv_user.cpp
Very Strange Karaulov f23498bef7
sv_user.cpp: Small code refactoring (#810)
* Small code style fix in sv_user.cpp

* SV_EstablishTimeBase_internal: code refactoring

Co-authored-by: Sergey Shorokhov <wopox1337@ya.ru>
2021-06-19 21:44:18 +03:00

1983 lines
53 KiB
C++

/*
*
* 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;
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;
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 (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<int *>((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 (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;
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)
{
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
Con_Printf("SV_ReadClientMessage: badread on %s\n", host_client->name);
if (host_client->active)
SV_ClientPrintf("Badread\n");
#else // REHLDS_FIXES
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
}