]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - sv_ccmds.c
host: Implement a hook struct in host_t. Use a hook to connect to local server
[xonotic/darkplaces.git] / sv_ccmds.c
index f6bb0a67230eb2050f61a05d3a7c0695b9f3b253..b5b55bf38d129f2fd7a33e80ad130cfc8de6a525 100644 (file)
@@ -18,10 +18,10 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
 */
 
-#include <quakedef.h>
-#include <utf8lib.h>
-#include <server.h>
-#include <sv_demo.h>
+#include "quakedef.h"
+#include "utf8lib.h"
+#include "server.h"
+#include "sv_demo.h"
 
 int current_skill;
 cvar_t sv_cheats = {CVAR_SERVER | CVAR_NOTIFY, "sv_cheats", "0", "enables cheat commands in any game, and cheat impulses in dpmod"};
@@ -84,8 +84,9 @@ static void SV_Map_f(cmd_state_t *cmd)
        svs.serverflags = 0;                    // haven't completed an episode yet
        strlcpy(level, Cmd_Argv(cmd, 1), sizeof(level));
        SV_SpawnServer(level);
-       if (sv.active && cls.state == ca_disconnected)
-               CL_EstablishConnection("local:1", -2);
+
+       if(sv.active && host.hook.ConnectLocal != NULL)
+               host.hook.ConnectLocal();
 }
 
 /*
@@ -104,9 +105,10 @@ static void SV_Changelevel_f(cmd_state_t *cmd)
                Con_Print("changelevel <levelname> : continue game on a new level\n");
                return;
        }
-       // HACKHACKHACK
-       if (!sv.active) {
-               SV_Map_f(cmd);
+
+       if (!sv.active)
+       {
+               Con_Printf("You must be running a server to changelevel. Use 'map %s' instead\n", Cmd_Argv(cmd, 1));
                return;
        }
 
@@ -120,8 +122,9 @@ static void SV_Changelevel_f(cmd_state_t *cmd)
        SV_SaveSpawnparms ();
        strlcpy(level, Cmd_Argv(cmd, 1), sizeof(level));
        SV_SpawnServer(level);
-       if (sv.active && cls.state == ca_disconnected)
-               CL_EstablishConnection("local:1", -2);
+       
+       if(sv.active && host.hook.ConnectLocal != NULL)
+               host.hook.ConnectLocal();
 }
 
 /*
@@ -155,19 +158,20 @@ static void SV_Restart_f(cmd_state_t *cmd)
 
        strlcpy(mapname, sv.name, sizeof(mapname));
        SV_SpawnServer(mapname);
-       if (sv.active && cls.state == ca_disconnected)
-               CL_EstablishConnection("local:1", -2);
+       
+       if(sv.active && host.hook.ConnectLocal != NULL)
+               host.hook.ConnectLocal();
 }
 
 //===========================================================================
 
 // Disable cheats if sv_cheats is turned off
-static void SV_DisableCheats_c(char *value)
+static void SV_DisableCheats_c(cvar_t *var)
 {
        prvm_prog_t *prog = SVVM_prog;
        int i = 0;
 
-       if (value[0] == '0' && !value[1])
+       if (var->value == 0)
        {
                while (svs.clients[i].edict)
                {
@@ -408,15 +412,7 @@ static void SV_Pause_f(cmd_state_t *cmd)
 {
        void (*print) (const char *fmt, ...);
        if (cmd->source == src_command)
-       {
-               // if running a client, try to send over network so the pause is handled by the server
-               if (cls.state == ca_connected)
-               {
-                       Cmd_ForwardToServer_f(cmd);
-                       return;
-               }
                print = Con_Printf;
-       }
        else
                print = SV_ClientPrintf;
 
@@ -464,7 +460,7 @@ static void SV_Say(cmd_state_t *cmd, qboolean teamonly)
                }
                else
                {
-                       Cmd_ForwardToServer_f(cmd);
+                       CL_ForwardToServer_f(cmd);
                        return;
                }
        }
@@ -539,7 +535,7 @@ static void SV_Tell_f(cmd_state_t *cmd)
                        fromServer = true;
                else
                {
-                       Cmd_ForwardToServer_f(cmd);
+                       CL_ForwardToServer_f(cmd);
                        return;
                }
        }
@@ -667,7 +663,7 @@ static void SV_Ping_f(cmd_state_t *cmd)
                // if running a client, try to send over network so the client's ping report parser will see the report
                if (cls.state == ca_connected)
                {
-                       Cmd_ForwardToServer_f(cmd);
+                       CL_ForwardToServer_f(cmd);
                        return;
                }
                print = Con_Printf;
@@ -825,15 +821,7 @@ static void SV_Status_f(cmd_state_t *cmd)
        char vabuf[1024];
 
        if (cmd->source == src_command)
-       {
-               // if running a client, try to send over network so the client's status report parser will see the report
-               if (cls.state == ca_connected)
-               {
-                       Cmd_ForwardToServer_f(cmd);
-                       return;
-               }
                print = Con_Printf;
-       }
        else
                print = SV_ClientPrintf;
 
@@ -894,7 +882,7 @@ static void SV_Status_f(cmd_state_t *cmd)
                        ping = bound(0, (int)floor(client->ping*1000+0.5), 9999);
                }
 
-               if(sv_status_privacy.integer && cmd->source != src_command)
+               if(sv_status_privacy.integer && cmd->source != src_command && LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) != LHNETADDRESSTYPE_LOOP)
                        strlcpy(ip, client->netconnection ? "hidden" : "botclient", 48);
                else
                        strlcpy(ip, (client->netconnection && *client->netconnection->address) ? client->netconnection->address : "botclient", 48);
@@ -945,6 +933,23 @@ static void SV_Status_f(cmd_state_t *cmd)
        }
 }
 
+void SV_Name(int clientnum)
+{
+       prvm_prog_t *prog = SVVM_prog;
+       PRVM_serveredictstring(host_client->edict, netname) = PRVM_SetEngineString(prog, host_client->name);
+       if (strcmp(host_client->old_name, host_client->name))
+       {
+               if (host_client->begun)
+                       SV_BroadcastPrintf("\003%s ^7changed name to ^3%s\n", host_client->old_name, host_client->name);
+               strlcpy(host_client->old_name, host_client->name, sizeof(host_client->old_name));
+               // send notification to all clients
+               MSG_WriteByte (&sv.reliable_datagram, svc_updatename);
+               MSG_WriteByte (&sv.reliable_datagram, clientnum);
+               MSG_WriteString (&sv.reliable_datagram, host_client->name);
+               SV_WriteNetnameIntoDemo(host_client);
+       }       
+}
+
 /*
 ======================
 SV_Name_f
@@ -952,7 +957,6 @@ SV_Name_f
 */
 static void SV_Name_f(cmd_state_t *cmd)
 {
-       prvm_prog_t *prog = SVVM_prog;
        int i, j;
        qboolean valid_colors;
        const char *newNameSource;
@@ -1047,18 +1051,7 @@ static void SV_Name_f(cmd_state_t *cmd)
        if (j >= 0 && strlen(host_client->name) < sizeof(host_client->name) - 2)
                memcpy(host_client->name + strlen(host_client->name), STRING_COLOR_DEFAULT_STR, strlen(STRING_COLOR_DEFAULT_STR) + 1);
 
-       PRVM_serveredictstring(host_client->edict, netname) = PRVM_SetEngineString(prog, host_client->name);
-       if (strcmp(host_client->old_name, host_client->name))
-       {
-               if (host_client->begun)
-                       SV_BroadcastPrintf("%s ^7changed name to %s\n", host_client->old_name, host_client->name);
-               strlcpy(host_client->old_name, host_client->name, sizeof(host_client->old_name));
-               // send notification to all clients
-               MSG_WriteByte (&sv.reliable_datagram, svc_updatename);
-               MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
-               MSG_WriteString (&sv.reliable_datagram, host_client->name);
-               SV_WriteNetnameIntoDemo(host_client);
-       }
+       SV_Name(host_client - svs.clients);
 }
 
 static void SV_Rate_f(cmd_state_t *cmd)
@@ -1094,6 +1087,9 @@ static void SV_Color_f(cmd_state_t *cmd)
        top = atoi(Cmd_Argv(cmd, 1));
        bottom = atoi(Cmd_Argv(cmd, 2));
 
+       top &= 15;
+       bottom &= 15;
+
        playercolor = top*16 + bottom;
 
        if (host_client->edict && PRVM_serverfunction(SV_ChangeTeam))
@@ -1168,7 +1164,7 @@ static void SV_Kick_f(cmd_state_t *cmd)
                        if (cls.state == ca_dedicated)
                                who = "Console";
                        else
-                               who = name.string;
+                               who = cl_name.string;
                }
                else
                        who = save->name;
@@ -1228,6 +1224,120 @@ static void SV_MaxPlayers_f(cmd_state_t *cmd)
                Cvar_Set (&cvars_all, "deathmatch", "1");
 }
 
+/*
+======================
+SV_Playermodel_f
+======================
+*/
+// the old playermodel in cl_main has been renamed to __cl_playermodel
+static void SV_Playermodel_f(cmd_state_t *cmd)
+{
+       prvm_prog_t *prog = SVVM_prog;
+       int i, j;
+       char newPath[sizeof(host_client->playermodel)];
+
+       if (Cmd_Argc (cmd) == 1)
+               return;
+
+       if (Cmd_Argc (cmd) == 2)
+               strlcpy (newPath, Cmd_Argv(cmd, 1), sizeof (newPath));
+       else
+               strlcpy (newPath, Cmd_Args(cmd), sizeof (newPath));
+
+       for (i = 0, j = 0;newPath[i];i++)
+               if (newPath[i] != '\r' && newPath[i] != '\n')
+                       newPath[j++] = newPath[i];
+       newPath[j] = 0;
+
+       /*
+       if (host.realtime < host_client->nametime)
+       {
+               SV_ClientPrintf("You can't change playermodel more than once every 5 seconds!\n");
+               return;
+       }
+
+       host_client->nametime = host.realtime + 5;
+       */
+
+       // point the string back at updateclient->name to keep it safe
+       strlcpy (host_client->playermodel, newPath, sizeof (host_client->playermodel));
+       PRVM_serveredictstring(host_client->edict, playermodel) = PRVM_SetEngineString(prog, host_client->playermodel);
+       if (strcmp(host_client->old_model, host_client->playermodel))
+       {
+               strlcpy(host_client->old_model, host_client->playermodel, sizeof(host_client->old_model));
+               /*// send notification to all clients
+               MSG_WriteByte (&sv.reliable_datagram, svc_updatepmodel);
+               MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
+               MSG_WriteString (&sv.reliable_datagram, host_client->playermodel);*/
+       }
+}
+
+/*
+======================
+SV_Playerskin_f
+======================
+*/
+static void SV_Playerskin_f(cmd_state_t *cmd)
+{
+       prvm_prog_t *prog = SVVM_prog;
+       int i, j;
+       char newPath[sizeof(host_client->playerskin)];
+
+       if (Cmd_Argc (cmd) == 1)
+               return;
+
+       if (Cmd_Argc (cmd) == 2)
+               strlcpy (newPath, Cmd_Argv(cmd, 1), sizeof (newPath));
+       else
+               strlcpy (newPath, Cmd_Args(cmd), sizeof (newPath));
+
+       for (i = 0, j = 0;newPath[i];i++)
+               if (newPath[i] != '\r' && newPath[i] != '\n')
+                       newPath[j++] = newPath[i];
+       newPath[j] = 0;
+
+       /*
+       if (host.realtime < host_client->nametime)
+       {
+               SV_ClientPrintf("You can't change playermodel more than once every 5 seconds!\n");
+               return;
+       }
+
+       host_client->nametime = host.realtime + 5;
+       */
+
+       // point the string back at updateclient->name to keep it safe
+       strlcpy (host_client->playerskin, newPath, sizeof (host_client->playerskin));
+       PRVM_serveredictstring(host_client->edict, playerskin) = PRVM_SetEngineString(prog, host_client->playerskin);
+       if (strcmp(host_client->old_skin, host_client->playerskin))
+       {
+               //if (host_client->begun)
+               //      SV_BroadcastPrintf("%s changed skin to %s\n", host_client->name, host_client->playerskin);
+               strlcpy(host_client->old_skin, host_client->playerskin, sizeof(host_client->old_skin));
+               /*// send notification to all clients
+               MSG_WriteByte (&sv.reliable_datagram, svc_updatepskin);
+               MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
+               MSG_WriteString (&sv.reliable_datagram, host_client->playerskin);*/
+       }
+}
+
+/*
+======================
+SV_PModel_f
+LadyHavoc: only supported for Nehahra, I personally think this is dumb, but Mindcrime won't listen.
+LadyHavoc: correction, Mindcrime will be removing pmodel in the future, but it's still stuck here for compatibility.
+======================
+*/
+static void SV_PModel_f(cmd_state_t *cmd)
+{
+       prvm_prog_t *prog = SVVM_prog;
+
+       if (Cmd_Argc (cmd) == 1)
+               return;
+
+       PRVM_serveredictfloat(host_client->edict, pmodel) = atoi(Cmd_Argv(cmd, 1));
+}
+
 /*
 ===============================================================================
 
@@ -1369,6 +1479,238 @@ static void SV_Viewprev_f(cmd_state_t *cmd)
        }
 }
 
+static void SV_SendCvar_f(cmd_state_t *cmd)
+{
+       int i;  
+       const char *cvarname;
+       client_t *old;
+       
+       if(Cmd_Argc(cmd) != 2)
+               return;
+
+       if(!sv.active)// || !PRVM_serverfunction(SV_ParseClientCommand))
+               return;
+
+       cvarname = Cmd_Argv(cmd, 1);
+
+       old = host_client;
+       if (cls.state != ca_dedicated)
+               i = 1;
+       else
+               i = 0;
+       for(;i<svs.maxclients;i++)
+               if(svs.clients[i].active && svs.clients[i].netconnection)
+               {
+                       host_client = &svs.clients[i];
+                       SV_ClientCommands("sendcvar %s\n", cvarname);
+               }
+       host_client = old;
+}
+
+static void SV_Ent_Create_f(cmd_state_t *cmd)
+{
+       prvm_prog_t *prog = SVVM_prog;
+       prvm_edict_t *ed;
+       ddef_t *key;
+       int i;
+       qboolean haveorigin;
+
+       qboolean expectval = false;
+       void (*print)(const char *, ...) = (cmd->source == src_client ? SV_ClientPrintf : Con_Printf);
+
+       if(!Cmd_Argc(cmd))
+               return;
+
+       ed = PRVM_ED_Alloc(SVVM_prog);
+
+       PRVM_ED_ParseEpair(prog, ed, PRVM_ED_FindField(prog, "classname"), Cmd_Argv(cmd, 1), false);
+
+       // Spawn where the player is aiming. We need a view matrix first.
+       if(cmd->source == src_client)
+       {
+               vec3_t org, temp, dest;
+               matrix4x4_t view;
+               trace_t trace;
+               char buf[128];
+
+               SV_GetEntityMatrix(prog, host_client->edict, &view, true);
+
+               Matrix4x4_OriginFromMatrix(&view, org);
+               VectorSet(temp, 65536, 0, 0);
+               Matrix4x4_Transform(&view, temp, dest);         
+
+               trace = SV_TraceLine(org, dest, MOVE_NORMAL, NULL, SUPERCONTENTS_SOLID, 0, 0, collision_extendmovelength.value);
+
+               dpsnprintf(buf, sizeof(buf), "%g %g %g", trace.endpos[0], trace.endpos[1], trace.endpos[2]);
+               PRVM_ED_ParseEpair(prog, ed, PRVM_ED_FindField(prog, "origin"), buf, false);
+
+               haveorigin = true;
+       }
+       // Or spawn at a specified origin.
+       else
+       {
+               print = Con_Printf;
+               haveorigin = false;
+       }
+
+       // Allow more than one key/value pair by cycling between expecting either one.
+       for(i = 2; i < Cmd_Argc(cmd); i++)
+       {
+               if(!expectval)
+               {
+                       if(!(key = PRVM_ED_FindField(prog, Cmd_Argv(cmd, i))))
+                       {
+                               print("Key %s not found!\n", Cmd_Argv(cmd, i));
+                               PRVM_ED_Free(prog, ed);
+                               return;
+                       }
+
+                       /*
+                        * This is mostly for dedicated server console, but if the
+                        * player gave a custom origin, we can ignore the traceline.
+                        */
+                       if(!strcmp(Cmd_Argv(cmd, i), "origin"))
+                               haveorigin = true;
+
+                       expectval = true;
+               }
+               else
+               {
+                       PRVM_ED_ParseEpair(prog, ed, key, Cmd_Argv(cmd, i), false);
+                       expectval = false;
+               }
+       }
+
+       if(!haveorigin)
+       {
+               print("Missing origin\n");
+               PRVM_ED_Free(prog, ed);
+               return;
+       }
+
+       // Spawn it
+       PRVM_ED_CallPrespawnFunction(prog, ed);
+       
+       if(!PRVM_ED_CallSpawnFunction(prog, ed, NULL, NULL))
+       {
+               print("Could not spawn a \"%s\". No such entity or it has no spawn function\n", Cmd_Argv(cmd, 1));
+               if(cmd->source == src_client)
+                       Con_Printf("%s tried to spawn a \"%s\"\n", host_client->name, Cmd_Argv(cmd, 1));
+               // CallSpawnFunction already freed the edict for us.
+               return;
+       }
+
+       PRVM_ED_CallPostspawnFunction(prog, ed);        
+
+       // Make it appear in the world
+       SV_LinkEdict(ed);
+
+       if(cmd->source == src_client)
+               Con_Printf("%s spawned a \"%s\"\n", host_client->name, Cmd_Argv(cmd, 1));
+}
+
+static void SV_Ent_Remove_f(cmd_state_t *cmd)
+{
+       prvm_prog_t *prog = SVVM_prog;
+       prvm_edict_t *ed;
+       int i, ednum;
+       void (*print)(const char *, ...) = (cmd->source == src_client ? SV_ClientPrintf : Con_Printf);
+
+       if(!Cmd_Argc(cmd))
+               return;
+
+       // Allow specifying edict by number
+       if(Cmd_Argc(cmd) > 1 && Cmd_Argv(cmd, 1))
+       {
+               ednum = atoi(Cmd_Argv(cmd, 1));
+               if(!ednum)
+               {
+                       print("Cannot remove the world\n");
+                       return;
+               }
+       }
+       // Or trace a line if it's a client who didn't specify one.
+       else if(cmd->source == src_client)
+       {
+               vec3_t org, temp, dest;
+               matrix4x4_t view;
+               trace_t trace;
+
+               SV_GetEntityMatrix(prog, host_client->edict, &view, true);
+
+               Matrix4x4_OriginFromMatrix(&view, org);
+               VectorSet(temp, 65536, 0, 0);
+               Matrix4x4_Transform(&view, temp, dest);         
+
+               trace = SV_TraceLine(org, dest, MOVE_NORMAL, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY, 0, 0, collision_extendmovelength.value);
+               
+               if(trace.ent)
+                       ednum = (int)PRVM_EDICT_TO_PROG(trace.ent);
+               if(!trace.ent || !ednum)
+                       // Don't remove the world, but don't annoy players with a print if they miss
+                       return;
+       }
+       else
+       {
+               // Only a dedicated server console should be able to reach this.
+               print("No edict given\n");
+               return;
+       }
+
+       ed = PRVM_EDICT_NUM(ednum);
+
+       if(ed)
+       {
+               // Skip players
+               for (i = 0; i < svs.maxclients; i++)
+               {
+                       if(ed == svs.clients[i].edict)
+                               return;
+               }
+
+               if(!ed->priv.required->free)
+               {
+                       print("Removed a \"%s\"\n", PRVM_GetString(prog, PRVM_serveredictstring(ed, classname)));
+                       PRVM_ED_ClearEdict(prog, ed);
+                       PRVM_ED_Free(prog, ed);
+               }
+       }
+       else
+       {
+               // This should only be reachable if an invalid edict number was given
+               print("No such entity\n");
+               return;
+       }
+}
+
+static void SV_Ent_Remove_All_f(cmd_state_t *cmd)
+{
+       prvm_prog_t *prog = SVVM_prog;
+       int i, rmcount;
+       prvm_edict_t *ed;
+       void (*print)(const char *, ...) = (cmd->source == src_client ? SV_ClientPrintf : Con_Printf);
+
+       for (i = 0, rmcount = 0, ed = PRVM_EDICT_NUM(i); i < prog->num_edicts; i++, ed = PRVM_NEXT_EDICT(ed))
+       {
+               if(!ed->priv.required->free && !strcmp(PRVM_GetString(prog, PRVM_serveredictstring(ed, classname)), Cmd_Argv(cmd, 1)))
+               {
+                       if(!i)
+                       {
+                               print("Cannot remove the world\n");
+                               return;
+                       }
+                       PRVM_ED_ClearEdict(prog, ed);
+                       PRVM_ED_Free(prog, ed);
+                       rmcount++;
+               }
+       }
+
+       if(!rmcount)
+               print("No \"%s\" found\n", Cmd_Argv(cmd, 1));
+       else
+               print("Removed %i of \"%s\"\n", rmcount, Cmd_Argv(cmd, 1));
+}
+
 void SV_InitOperatorCommands(void)
 {
        Cvar_RegisterVariable(&sv_cheats);
@@ -1397,6 +1739,7 @@ void SV_InitOperatorCommands(void)
        Cmd_AddCommand(CMD_SHARED, "maxplayers", SV_MaxPlayers_f, "sets limit on how many players (or bots) may be connected to the server at once");
        Cmd_AddCommand(CMD_SHARED, "user", SV_User_f, "prints additional information about a player number or name on the scoreboard");
        Cmd_AddCommand(CMD_SHARED, "users", SV_Users_f, "prints additional information about all players on the scoreboard");
+       Cmd_AddCommand(CMD_SERVER, "sendcvar", SV_SendCvar_f, "sends the value of a cvar to the server as a sentcvar command, for use by QuakeC");
 
        // commands that do not have automatic forwarding from cmd_client, these are internal details of the network protocol and not of interest to users (if they know what they are doing they can still use a generic "cmd prespawn" or similar)
        Cmd_AddCommand(CMD_SERVER_FROM_CLIENT, "prespawn", SV_PreSpawn_f, "internal use - signon 1 (client acknowledges that server information has been received)");
@@ -1415,4 +1758,11 @@ void SV_InitOperatorCommands(void)
        Cmd_AddCommand(CMD_USERINFO, "name", SV_Name_f, "change your player name");
        Cmd_AddCommand(CMD_USERINFO, "rate", SV_Rate_f, "change your network connection speed");
        Cmd_AddCommand(CMD_USERINFO, "rate_burstsize", SV_Rate_BurstSize_f, "change your network connection speed");
-}
\ No newline at end of file
+       Cmd_AddCommand(CMD_USERINFO, "pmodel", SV_PModel_f, "(Nehahra-only) change your player model choice");
+       Cmd_AddCommand(CMD_USERINFO, "playermodel", SV_Playermodel_f, "change your player model");
+       Cmd_AddCommand(CMD_USERINFO, "playerskin", SV_Playerskin_f, "change your player skin number");
+
+       Cmd_AddCommand(CMD_CHEAT | CMD_SERVER_FROM_CLIENT, "ent_create", SV_Ent_Create_f, "Creates an entity at the specified coordinate, of the specified classname. If executed from a server, origin has to be specified manually.");
+       Cmd_AddCommand(CMD_CHEAT | CMD_SERVER_FROM_CLIENT, "ent_remove_all", SV_Ent_Remove_All_f, "Removes all entities of the specified classname");
+       Cmd_AddCommand(CMD_CHEAT | CMD_SERVER_FROM_CLIENT, "ent_remove", SV_Ent_Remove_f, "Removes an entity by number, or the entity you're aiming at");
+}