openmohaa/code/cgame/cg_servercmds.c

459 lines
14 KiB
C

/*
===========================================================================
Copyright (C) 2008-2024 the OpenMoHAA team
Copyright (C) 1999-2005 Id Software, Inc.
This file is part of OpenMoHAA source code.
OpenMoHAA source code 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.
OpenMoHAA source code 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 OpenMoHAA source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
// DESCRIPTION:
// cg_servercmds.c -- text commands sent by the server
#include "cg_local.h"
#include "../fgame/bg_voteoptions.h"
#include "cg_servercmds_filter.h"
/*
================
IsWeaponAllowed
Returns true if the weapon is allowed
================
*/
static const char *IsWeaponAllowed(int dmFlags, int flags)
{
return (dmFlags & flags) ? "0" : "1";
}
/*
================
QueryLandminesAllowed2
Returns true if landmines is allowed by the map or by a dm flag
================
*/
static qboolean QueryLandminesAllowed2(const char *mapname, int dmflags)
{
if (dmflags & DF_WEAPON_NO_LANDMINE) {
return qfalse;
}
if (dmflags & DF_WEAPON_LANDMINE_ALWAYS) {
return qtrue;
}
if (!Q_stricmpn(mapname, "obj/obj_", 8u)) {
return qfalse;
}
if (!Q_stricmpn(mapname, "dm/mohdm", 8u)) {
return qfalse;
}
if (!Q_stricmp(mapname, "DM/MP_Bahnhof_DM")) {
return qfalse;
}
if (!Q_stricmp(mapname, "obj/MP_Ardennes_TOW")) {
return qfalse;
}
if (!Q_stricmp(mapname, "DM/MP_Bazaar_DM")) {
return qfalse;
}
if (!Q_stricmp(mapname, "obj/MP_Berlin_TOW")) {
return qfalse;
}
if (!Q_stricmp(mapname, "DM/MP_Brest_DM")) {
return qfalse;
}
if (!Q_stricmp(mapname, "obj/MP_Druckkammern_TOW")) {
return qfalse;
}
if (!Q_stricmp(mapname, "DM/MP_Gewitter_DM")) {
return qfalse;
}
if (!Q_stricmp(mapname, "obj/MP_Flughafen_TOW")) {
return qfalse;
}
if (!Q_stricmp(mapname, "DM/MP_Holland_DM")) {
return qfalse;
}
if (!Q_stricmp(mapname, "DM/MP_Malta_DM")) {
return qfalse;
}
if (!Q_stricmp(mapname, "DM/MP_Stadt_DM")) {
return qfalse;
}
if (!Q_stricmp(mapname, "DM/MP_Unterseite_DM")) {
return qfalse;
}
if (!Q_stricmp(mapname, "DM/MP_Verschneit_DM")) {
return qfalse;
}
if (!Q_stricmp(mapname, "lib/mp_ship_lib")) {
return qfalse;
}
if (!Q_stricmp(mapname, "DM/MP_Verschneit_DM")) {
return qfalse;
}
if (!Q_stricmp(mapname, "lib/mp_ship_lib")) {
return qfalse;
}
return qtrue;
}
/*
================
CG_ParseServerinfo
This is called explicitly when the gamestate is first received,
and whenever the server updates any serverinfo flagged cvars
================
*/
void CG_ParseServerinfo(void)
{
const char *info;
const char *mapname;
char map[MAX_QPATH];
char *spawnpos;
const char *version;
const char *mapChecksumStr;
info = CG_ConfigString(CS_SERVERINFO);
cgs.gametype = atoi(Info_ValueForKey(info, "g_gametype"));
cgs.dmflags = atoi(Info_ValueForKey(info, "dmflags"));
cgs.teamflags = atoi(Info_ValueForKey(info, "teamflags"));
cgs.fraglimit = atoi(Info_ValueForKey(info, "fraglimit"));
cgs.timelimit = atoi(Info_ValueForKey(info, "timelimit"));
cgs.maxclients = atoi(Info_ValueForKey(info, "sv_maxclients"));
version = Info_ValueForKey(info, "version");
if (strstr(version, "Spearhead")) {
cgi.Cvar_Set("g_servertype", "1");
} else {
cgi.Cvar_Set("g_servertype", "2");
}
cgi.Cvar_Set("cg_gametype", Info_ValueForKey(info, "g_gametype"));
cgi.Cvar_Set("cg_fraglimit", Info_ValueForKey(info, "fraglimit"));
cgi.Cvar_Set("cg_timelimit", Info_ValueForKey(info, "timelimit"));
cgi.Cvar_Set("cg_maxclients", Info_ValueForKey(info, "sv_gametype"));
cgi.Cvar_Set("cg_allowvote", Info_ValueForKey(info, "g_allowvote"));
cgi.Cvar_Set("cg_obj_alliedtext1", Info_ValueForKey(info, "g_obj_alliedtext1"));
cgi.Cvar_Set("cg_obj_alliedtext2", Info_ValueForKey(info, "g_obj_alliedtext2"));
cgi.Cvar_Set("cg_obj_alliedtext3", Info_ValueForKey(info, "g_obj_alliedtext3"));
cgi.Cvar_Set("cg_obj_alliedtext4", Info_ValueForKey(info, "g_obj_alliedtext4"));
cgi.Cvar_Set("cg_obj_alliedtext5", Info_ValueForKey(info, "g_obj_alliedtext5"));
cgi.Cvar_Set("cg_obj_axistext1", Info_ValueForKey(info, "g_obj_axistext1"));
cgi.Cvar_Set("cg_obj_axistext2", Info_ValueForKey(info, "g_obj_axistext2"));
cgi.Cvar_Set("cg_obj_axistext3", Info_ValueForKey(info, "g_obj_axistext3"));
cgi.Cvar_Set("cg_obj_axistext4", Info_ValueForKey(info, "g_obj_axistext4"));
cgi.Cvar_Set("cg_obj_axistext5", Info_ValueForKey(info, "g_obj_axistext5"));
cgi.Cvar_Set("cg_scoreboardpic", Info_ValueForKey(info, "g_scoreboardpic"));
cgi.Cvar_Set("cg_scoreboardpicover", Info_ValueForKey(info, "g_scoreboardpicover"));
mapChecksumStr = Info_ValueForKey(info, "sv_mapChecksum");
if (mapChecksumStr && mapChecksumStr[0]) {
cgs.mapChecksum = atoi(mapChecksumStr);
cgs.useMapChecksum = qtrue;
} else {
cgs.mapChecksum = 0;
cgs.useMapChecksum = qfalse;
}
mapname = Info_ValueForKey(info, "mapname");
cgi.Cvar_Set("cg_weapon_rifle", IsWeaponAllowed(cgs.dmflags, DF_WEAPON_NO_RIFLE));
cgi.Cvar_Set("cg_weapon_sniper", IsWeaponAllowed(cgs.dmflags, DF_WEAPON_NO_SNIPER));
cgi.Cvar_Set("cg_weapon_mg", IsWeaponAllowed(cgs.dmflags, DF_WEAPON_NO_MG));
cgi.Cvar_Set("cg_weapon_smg", IsWeaponAllowed(cgs.dmflags, DF_WEAPON_NO_SMG));
cgi.Cvar_Set("cg_weapon_rocket", IsWeaponAllowed(cgs.dmflags, DF_WEAPON_NO_ROCKET));
cgi.Cvar_Set("cg_weapon_shotgun", IsWeaponAllowed(cgs.dmflags, DF_WEAPON_NO_SHOTGUN));
cgi.Cvar_Set("cg_weapon_landmine", QueryLandminesAllowed2(mapname, cgs.dmflags) ? "1" : "0");
spawnpos = strchr(mapname, '$');
if (spawnpos) {
Q_strncpyz(map, mapname, spawnpos - mapname + 1);
} else {
Q_strncpyz(map, mapname, sizeof(map));
}
if (CG_UseLargeLightmaps(mapname)) {
Com_sprintf(cgs.mapname, sizeof(cgs.mapname), "maps/%s.bsp", map);
} else {
Com_sprintf(cgs.mapname, sizeof(cgs.mapname), "maps/%s_sml.bsp", map);
}
// hide/show huds
if (cgs.gametype) {
cgi.Cmd_Execute(EXEC_NOW, "ui_addhud hud_timelimit\n");
if (cgs.fraglimit) {
cgi.Cmd_Execute(EXEC_NOW, "ui_addhud hud_fraglimit\n");
cgi.Cmd_Execute(EXEC_NOW, "ui_removehud hud_score\n");
} else {
cgi.Cmd_Execute(EXEC_NOW, "ui_addhud hud_score\n");
cgi.Cmd_Execute(EXEC_NOW, "ui_removehud hud_fraglimit\n");
}
} else {
cgi.Cmd_Execute(EXEC_NOW, "ui_removehud hud_timelimit\n");
cgi.Cmd_Execute(EXEC_NOW, "ui_removehud hud_fraglimit\n");
cgi.Cmd_Execute(EXEC_NOW, "ui_removehud hud_score\n");
}
}
/*
================
CG_ConfigStringModified
================
*/
static void CG_ConfigStringModified(int num, qboolean modelOnly)
{
// get the gamestate from the client system, which will have the
// new configstring already integrated
cgi.GetGameState(&cgs.gameState);
CG_ProcessConfigString(num, modelOnly);
}
/*
================
CG_ParseStats
================
*/
static void CG_ParseStats()
{
cgi.Cvar_Set("ui_NumObjectives", cgi.Argv(1));
cgi.Cvar_Set("ui_NumComplete", cgi.Argv(2));
cgi.Cvar_Set("ui_NumShotsFired", cgi.Argv(3));
cgi.Cvar_Set("ui_NumHits", cgi.Argv(4));
cgi.Cvar_Set("ui_Accuracy", cgi.Argv(5));
cgi.Cvar_Set("ui_PreferredWeapon", cgi.Argv(6));
cgi.Cvar_Set("ui_NumHitsTaken", cgi.Argv(7));
cgi.Cvar_Set("ui_NumObjectsDestroyed", cgi.Argv(8));
cgi.Cvar_Set("ui_NumEnemysKilled", cgi.Argv(9));
cgi.Cvar_Set("ui_HeadShots", cgi.Argv(10));
cgi.Cvar_Set("ui_TorsoShots", cgi.Argv(11));
cgi.Cvar_Set("ui_LeftLegShots", cgi.Argv(12));
cgi.Cvar_Set("ui_RightLegShots", cgi.Argv(13));
cgi.Cvar_Set("ui_GroinShots", cgi.Argv(14));
cgi.Cvar_Set("ui_LeftArmShots", cgi.Argv(15));
cgi.Cvar_Set("ui_RightArmShots", cgi.Argv(16));
cgi.Cvar_Set("ui_GunneryEvaluation", cgi.Argv(17));
cgi.Cvar_Set("ui_gotmedal", cgi.Argv(18));
cgi.Cvar_Set("ui_success", cgi.Argv(19));
cgi.Cvar_Set("ui_failed", cgi.Argv(20));
}
/*
================
CG_Stopwatch_f
================
*/
static void CG_Stopwatch_f()
{
if (cgi.Argc() < 3) {
Com_Error(ERR_DROP, "stopwatch didn't have 2 parameters");
}
if (cg_protocol >= PROTOCOL_MOHTA_MIN) {
cgi.stopWatch->iStartTime = atoi(cgi.Argv(1));
if (cgi.Argc() > 3) {
cgi.stopWatch->eType = atoi(cgi.Argv(3));
} else {
// Normal stop watch
cgi.stopWatch->eType = SWT_NORMAL;
}
} else {
// The base game has it wrong
cgi.stopWatch->iStartTime = 1000 * atoi(cgi.Argv(1));
}
cgi.stopWatch->iEndTime = cgi.stopWatch->iStartTime + 1000 * atoi(cgi.Argv(2));
}
/*
================
CG_ServerLag_f
================
*/
static void CG_ServerLag_f()
{
cgs.serverLagTime = cg.time;
}
/*
=================
CG_ServerCommand
The string has been tokenized and can be retrieved with
Cmd_Argc() / Cmd_Argv()
=================
*/
static void CG_ServerCommand(qboolean modelOnly)
{
const char *cmd;
cmd = cgi.Argv(0);
if (!cmd[0]) {
// server claimed the command
return;
}
if (!strcmp(cmd, "cs")) {
CG_ConfigStringModified(cgi.getConfigStringIdNormalized(atoi(cgi.Argv(1))), modelOnly);
return;
}
if (modelOnly) {
return;
}
if (!strcmp(cmd, "print") || !strcmp(cmd, "hudprint")) {
cgi.Printf("%s", cgi.Argv(1));
if (!strcmp(cmd, "hudprint")) {
CG_HudPrint_f();
}
return;
} else if (!strcmp(cmd, "printdeathmsg")) {
const char *s1, *s2, *attackerName, *victimName, *type;
const char *result1, *result2;
int hudColor;
result1 = NULL;
result2 = NULL;
s1 = cgi.Argv(1);
s2 = cgi.Argv(2);
attackerName = cgi.Argv(3);
victimName = cgi.Argv(4);
type = cgi.Argv(5);
if (*type == tolower(*type)) {
hudColor = 4;
} else {
hudColor = 5;
}
if (*s1 != 'x') {
result1 = cgi.LV_ConvertString(s1);
}
if (*s2 != 'x') {
result2 = cgi.LV_ConvertString(s2);
}
if (tolower(*type) == 's') {
cgi.Printf("%c%s %s\n", hudColor, victimName, result1);
} else if (tolower(*type) == 'p') {
if (*s2 == 'x') {
cgi.Printf("%c%s %s %s\n", hudColor, victimName, result1, attackerName);
} else {
cgi.Printf("%c%s %s %s%s\n", hudColor, victimName, result1, attackerName, result2);
}
} else if (tolower(*type) == 'w') {
cgi.Printf("%c%s %s\n", hudColor, victimName, result1);
} else {
cgi.Printf("%s", cgi.Argv(1));
}
return;
}
if (!strcmp(cmd, "stufftext")) {
char *cmd = cgi.Argv(1);
if (CG_IsStatementFiltered(cmd)) {
// Added in OPM
// Don't execute filtered commands
return;
}
cgi.Cmd_Stuff(cmd);
cgi.Cmd_Stuff("\n");
return;
}
if (!strcmp(cmd, "scores")) {
CG_ParseScores();
return;
}
if (!strcmp(cmd, "stats")) {
CG_ParseStats();
return;
}
if (!strcmp(cmd, "stopwatch")) {
CG_Stopwatch_f();
return;
}
if (!strcmp(cmd, "svlag")) {
CG_ServerLag_f();
return;
}
if (!strcmp(cmd, "voteresult")) {
cmd = cgi.Argv(1);
if (*cmd) {
strcmp(cmd, "passed");
}
}
if (!strcmp(cmd, "vo0")) {
CG_VoteOptions_StartReadFromServer(cgi.Argv(1));
return;
}
if (!strcmp(cmd, "vo1")) {
CG_VoteOptions_ContinueReadFromServer(cgi.Argv(1));
return;
}
if (!strcmp(cmd, "vo2")) {
CG_VoteOptions_FinishReadFromServer(cgi.Argv(1));
return;
}
cgi.Printf("Unknown client game command: %s\n", cmd);
}
/*
====================
CG_ExecuteNewServerCommands
Execute all of the server commands that were received along
with this this snapshot.
====================
*/
void CG_ExecuteNewServerCommands(int latestSequence, qboolean differentServer)
{
int lastServerCommandSequence;
lastServerCommandSequence = cgs.serverCommandSequence;
while (cgs.serverCommandSequence < latestSequence) {
if (cgi.GetServerCommand(++cgs.serverCommandSequence, differentServer)) {
CG_ServerCommand(qtrue);
}
}
cgs.serverCommandSequence = lastServerCommandSequence;
while (cgs.serverCommandSequence < latestSequence) {
if (cgi.GetServerCommand(++cgs.serverCommandSequence, differentServer)) {
CG_ServerCommand(qfalse);
}
}
}