/* * * 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" /* ../engine/sv_user.c:21 */ typedef struct command_s { char *command; } command_t; 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; /* * Globals initialization */ #ifndef HOOK_ENGINE #if defined(SWDS) && defined(REHLDS_FIXES) command_t clcommands[] = { "status", "name", "kill", "pause", "spawn", "new", "sendres", "dropclient", "kick", "ping", "dlfile", "setinfo", "sendents", "fullupdate", "setpause", "unpause", NULL }; #else command_t 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", "200", 0, 0.0f, NULL }; cvar_t sv_rollangle = { "sv_rollangle", "2.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", NULL }, { clc_nop, "clc_nop", NULL }, { 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", NULL }, { 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", NULL }, }; #else //HOOK_ENGINE command_t clcommands[23]; cvar_t sv_edgefriction; cvar_t sv_maxspeed; cvar_t sv_accelerate; cvar_t sv_footsteps; cvar_t sv_rollspeed; cvar_t sv_rollangle; cvar_t sv_unlag; cvar_t sv_maxunlag; cvar_t sv_unlagpush; cvar_t sv_unlagsamples; cvar_t mp_consistency; cvar_t sv_voiceenable; clc_func_t sv_clcfuncs[12]; #endif //HOOK_ENGINE bool EXT_FUNC SV_CheckConsistencyResponse_API(IGameClient *client, resource_t *res, uint32 hash) { return (hash != *(uint32 *)&res->rgucMD5_hash[0]); } /* ../engine/sv_user.c:94 */ 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(); 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. } #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); } #endif // REHLDS_FIXES return; } host_client->has_force_unmodified = 0; } /* ../engine/sv_user.c:267 */ 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; } /* ../engine/sv_user.c:298 */ 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("Server unable to get bounds for %s\n", 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; } /* ../engine/sv_user.c:374 */ void SV_SendConsistencyList(sizebuf_t *msg) { host_client->has_force_unmodified = FALSE; if (g_psvs.maxclients == 1 || mp_consistency.value == 0.0f || g_psv.num_consistency == 0 || host_client->proxy) { 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); } /* ../engine/sv_user.c:439 */ void SV_PreRunCmd(void) { } /* ../engine/sv_user.c:450 */ 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->info = e; pe->origin[2] = check->v.origin[2]; 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 = 0; 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, 0x20u); pe->name[31] = 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, 0x20u); pe->name[31] = 0; } else { pe->model = 0; } } else { pe->model = NULL; 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], 0x20u); pe->name[31] = 0; } else { Q_sprintf(pe->name, "?"); } } else { if (check->v.modelindex) { pModel = g_psv.models[check->v.modelindex]; if (pModel) { if (pModel->flags & 0x200) pe->studiomodel = pModel; Q_strncpy(pe->name, pModel->name, 0x20u); pe->name[31] = 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; Q_memcpy(pe->controller, check->v.controller, 4); Q_memcpy(pe->blending, check->v.blending, 2); pe->movetype = check->v.movetype; 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]; } /* ../engine/sv_user.c:579 */ 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 || 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); } } /* ../engine/sv_user.c:712 */ 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, 0x20u); pmove->physents[0].name[31] = 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); } /* ../engine/sv_user.c:758 */ void SV_PlayerRunPreThink(edict_t *player, float time) { gGlobalVariables.time = time; gEntityInterface.pfnPlayerPreThink(player); } /* ../engine/sv_user.c:774 */ 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; } /* ../engine/sv_user.c:814 */ 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; } /* ../engine/sv_user.c:852 */ 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; } /* ../engine/sv_user.c:873 */ 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); } /* ../engine/sv_user.c:918 */ 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); 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 = 1; pmove->multiplayer = (qboolean)(g_psvs.maxclients > 1); pmove->time = float(1000.0 * host_client->svtimebase); pmove->usehull = (sv_player->v.flags & 0x4000) != 0; 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, 0xFFu); pmove->physinfo[255] = 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 = 1; pmove->PM_PlaySound = PM_SV_PlaySound; pmove->PM_TraceTexture = PM_SV_TraceTexture; pmove->PM_PlaybackEventFull = PM_SV_PlaybackEventFull; gEntityInterface.pfnPM_Move(pmove, 1); 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, 1); 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); } /* ../engine/sv_user.c:1197 */ int SV_ValidateClientCommand(char *pszCommand) { char *p; int i = 0; COM_Parse(pszCommand); while ((p = clcommands[i].command) != NULL) { if (!Q_stricmp(com_token, p)) { return 1; } i++; } return 0; } /* ../engine/sv_user.c:1226 */ 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; } /* ../engine/sv_user.c:1297 */ void SV_ComputeLatency(client_t *cl) { cl->latency = SV_CalcClientTime(cl); } /* ../engine/sv_user.c:1339 */ 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; } /* ../engine/sv_user.c:1361 */ 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]; } } /* ../engine/sv_user.c:1383 */ 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; } } /* ../engine/sv_user.c:1406 */ 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; } /* ../engine/sv_user.c:1426 */ 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, 0xB00u); 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, 0); pos->needrelink = 1; } } } /* ../engine/sv_user.c:1670 */ 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, 0); } } } /* ../engine/sv_user.c:1736 */ 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; } } /* ../engine/sv_user.c:1777 */ 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); } /* ../engine/sv_user.c:1790 */ void SV_EstablishTimeBase_internal(client_t *cl, usercmd_t *cmds, int dropped, int numbackup, int numcmds) { int cmdnum; double runcmd_time; runcmd_time = 0.0; cmdnum = dropped; if (dropped < 24) { if (dropped > numbackup) { cmdnum = dropped - (dropped - numbackup); runcmd_time = (double)cl->lastcmd.msec * (dropped - numbackup) / 1000.0; } for (; cmdnum > 0; cmdnum--) { runcmd_time += cmds[cmdnum - 1 + numcmds].msec / 1000.0; } } for (; numcmds > 0; numcmds--) { runcmd_time += cmds[numcmds - 1].msec / 1000.0; } cl->svtimebase = host_frametime + g_psv.time - runcmd_time; } /* ../engine/sv_user.c:1835 */ 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(); 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); } /* ../engine/sv_user.c:2022 */ 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); } } } /* ../engine/sv_user.c:2091 */ void SV_IgnoreHLTV(client_t *cl) { } /* ../engine/sv_user.c:2096 */ 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); } /* ../engine/sv_user.c:2107 */ 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, int8 opcode) { client_t* cl = client->GetClient(); if (opcode < clc_bad || opcode > clc_cvarvalue2) { Con_Printf("SV_ReadClientMessage: unknown command char (%d)\n", opcode); SV_DropClient(cl, FALSE, "Bad command character in client command"); return; } void(*func)(client_t *) = sv_clcfuncs[opcode].pfnParse; if (func) func(cl); } /* ../engine/sv_user.c:2155 */ 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); 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); } } /* ../engine/sv_user.c:2233 */ 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; } /* ../engine/sv_user.c:2260 */ 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 } } /* ../engine/sv_user.c:2277 */ 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 = 1; #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 } } } } /* ../engine/sv_user.c:2302 */ 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 }