X-Git-Url: http://de.git.xonotic.org/?a=blobdiff_plain;f=host_cmd.c;h=bed286c7a54eb11c6b6fc0ea9b64d457d9d8da5f;hb=eacd06327dd5ac3be3800d102e5b56a1efd051b0;hp=f2db17f3b8f76d812264ced228c01d9db1fb541a;hpb=d159736a199e4043ed98cde48f6d5716ec40845c;p=xonotic%2Fdarkplaces.git diff --git a/host_cmd.c b/host_cmd.c index f2db17f3..bed286c7 100644 --- a/host_cmd.c +++ b/host_cmd.c @@ -22,6 +22,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. int current_skill; cvar_t sv_cheats = {0, "sv_cheats", "0", "enables cheat commands in any game, and cheat impulses in dpmod"}; +cvar_t rcon_password = {0, "rcon_password", "", "password to authenticate rcon commands"}; +cvar_t rcon_address = {0, "rcon_address", "", "server address to send rcon commands to (when not connected to a server)"}; qboolean allowcheats = false; /* @@ -267,7 +269,7 @@ void Host_Map_f (void) cls.demonum = -1; // stop demo loop in case this fails CL_Disconnect (); - Host_ShutdownServer(false); + Host_ShutdownServer(); // remove console or menu key_dest = key_game; @@ -278,11 +280,7 @@ void Host_Map_f (void) strcpy(level, Cmd_Argv(1)); SV_SpawnServer(level); if (sv.active && cls.state == ca_disconnected) - { - SV_VM_Begin(); CL_EstablishConnection("local:1"); - SV_VM_End(); - } } /* @@ -301,16 +299,16 @@ void Host_Changelevel_f (void) Con_Print("changelevel : continue game on a new level\n"); return; } - // HACKHACKHACK - if (!sv.active) { - Host_Map_f(); - return; - } if (cls.demoplayback) { Con_Print("Only the server may changelevel\n"); return; } + // HACKHACKHACK + if (!sv.active) { + Host_Map_f(); + return; + } if (cmd_source != src_command) return; @@ -325,11 +323,7 @@ void Host_Changelevel_f (void) strcpy(level, Cmd_Argv(1)); SV_SpawnServer(level); if (sv.active && cls.state == ca_disconnected) - { - SV_VM_Begin(); CL_EstablishConnection("local:1"); - SV_VM_End(); - } } /* @@ -348,7 +342,7 @@ void Host_Restart_f (void) Con_Print("restart : restart current level\n"); return; } - if (!sv.active || cls.demoplayback) + if (!sv.active) { Con_Print("Only the server may restart\n"); return; @@ -364,11 +358,7 @@ void Host_Restart_f (void) strcpy(mapname, sv.name); SV_SpawnServer(mapname); if (sv.active && cls.state == ca_disconnected) - { - SV_VM_Begin(); CL_EstablishConnection("local:1"); - SV_VM_End(); - } } /* @@ -381,22 +371,39 @@ This is sent just before a server changes levels */ void Host_Reconnect_f (void) { - if (cmd_source == src_command) - { - Con_Print("reconnect is not valid from the console\n"); - return; - } - if (Cmd_Argc() != 1) + if (cls.protocol == PROTOCOL_QUAKEWORLD) { - Con_Print("reconnect : wait for signon messages again\n"); - return; + if (cls.qw_downloadmemory) // don't change when downloading + return; + + S_StopAllSounds(); + + if (cls.netcon) + { + if (cls.state == ca_connected && cls.signon < SIGNONS) + { + Con_Printf("reconnecting...\n"); + MSG_WriteChar(&cls.netcon->message, qw_clc_stringcmd); + MSG_WriteString(&cls.netcon->message, "new"); + } + else + Con_Printf("Please use connect instead (reconnect not implemented)\n"); + } } - if (!cls.signon) + else { - //Con_Print("reconnect: no signon, ignoring reconnect\n"); - return; + if (Cmd_Argc() != 1) + { + Con_Print("reconnect : wait for signon messages again\n"); + return; + } + if (!cls.signon) + { + Con_Print("reconnect: no signon, ignoring reconnect\n"); + return; + } + cls.signon = 0; // need new connection messages } - cls.signon = 0; // need new connection messages } /* @@ -413,13 +420,7 @@ void Host_Connect_f (void) Con_Print("connect : connect to a multiplayer game\n"); return; } - if( sv.active ) { - SV_VM_Begin(); - CL_EstablishConnection(Cmd_Argv(1)); - SV_VM_End(); - } else { - CL_EstablishConnection(Cmd_Argv(1)); - } + CL_EstablishConnection(Cmd_Argv(1)); } @@ -570,6 +571,7 @@ void Host_Loadgame_f (void) float time; const char *start; const char *t; + const char *oldt; char *text; prvm_edict_t *ent; int i; @@ -651,10 +653,33 @@ void Host_Loadgame_f (void) for (i = 0;i < MAX_LIGHTSTYLES;i++) { // light style + oldt = t; COM_ParseToken(&t, false); + // if this is a 64 lightstyle savegame produced by Quake, stop now + // we have to check this because darkplaces saves 256 lightstyle savegames + if (com_token[0] == '{') + { + t = oldt; + break; + } strlcpy(sv.lightstyles[i], com_token, sizeof(sv.lightstyles[i])); } + // now skip everything before the first opening brace + // (this is for forward compatibility, so that older versions (at + // least ones with this fix) can load savegames with extra data before the + // first brace, as might be produced by a later engine version) + for(;;) + { + oldt = t; + COM_ParseToken(&t, false); + if (com_token[0] == '{') + { + t = oldt; + break; + } + } + // load the edicts out of the savegame file SV_VM_Begin(); // -1 is the globals @@ -711,11 +736,11 @@ void Host_Loadgame_f (void) for (i = 0;i < NUM_SPAWN_PARMS;i++) svs.clients[0].spawn_parms[i] = spawn_parms[i]; + SV_VM_End(); + // make sure we're connected to loopback if (cls.state == ca_disconnected || !(cls.state == ca_connected && cls.netcon != NULL && LHNETADDRESS_GetAddressType(&cls.netcon->peeraddress) == LHNETADDRESSTYPE_LOOP)) CL_EstablishConnection("local:1"); - - SV_VM_End(); } //============================================================================ @@ -750,6 +775,7 @@ void Host_Name_f (void) if (cmd_source == src_command) { Cvar_Set ("_cl_name", newName); + InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "name", newName); if (cls.state == ca_connected) Cmd_ForwardToServer (); return; @@ -809,6 +835,7 @@ void Host_Playermodel_f (void) if (cmd_source == src_command) { Cvar_Set ("_cl_playermodel", newPath); + InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "playermodel", newPath); if (cls.state == ca_connected) Cmd_ForwardToServer (); return; @@ -868,6 +895,7 @@ void Host_Playerskin_f (void) if (cmd_source == src_command) { Cvar_Set ("_cl_playerskin", newPath); + InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "playerskin", newPath); if (cls.state == ca_connected) Cmd_ForwardToServer (); return; @@ -1085,11 +1113,16 @@ void Host_Color_f(void) if (cmd_source == src_command) { Cvar_SetValue ("_cl_color", playercolor); - if (cls.state == ca_connected) + InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "topcolor", va("%i", top)); + InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "bottomcolor", va("%i", bottom)); + if (cls.state == ca_connected && cls.protocol != PROTOCOL_QUAKEWORLD) Cmd_ForwardToServer (); return; } + if (cls.protocol == PROTOCOL_QUAKEWORLD) + return; + if (host_client->edict && (f = PRVM_ED_FindFunction ("SV_ChangeTeam")) && (SV_ChangeTeam = (func_t)(f - prog->functions))) { Con_DPrint("Calling SV_ChangeTeam\n"); @@ -1136,6 +1169,7 @@ void Host_Rate_f(void) if (cmd_source == src_command) { Cvar_SetValue ("_cl_rate", bound(NET_MINRATE, rate, NET_MAXRATE)); + InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "rate", va("%i", rate)); if (cls.state == ca_connected) Cmd_ForwardToServer (); return; @@ -1250,10 +1284,12 @@ void Host_PreSpawn_f (void) return; } - SZ_Write (&host_client->message, sv.signon.data, sv.signon.cursize); - MSG_WriteByte (&host_client->message, svc_signonnum); - MSG_WriteByte (&host_client->message, 2); - host_client->sendsignon = true; + if (host_client->netconnection) + { + SZ_Write (&host_client->netconnection->message, sv.signon.data, sv.signon.cursize); + MSG_WriteByte (&host_client->netconnection->message, svc_signonnum); + MSG_WriteByte (&host_client->netconnection->message, 2); + } // reset the name change timer because the client will send name soon host_client->nametime = 0; @@ -1289,8 +1325,9 @@ void Host_Spawn_f (void) host_client->nametime = 0; // LordHavoc: moved this above the QC calls at FrikaC's request - // send all current names, colors, and frag counts - SZ_Clear (&host_client->message); + // LordHavoc: commented this out + //if (host_client->netconnection) + // SZ_Clear (&host_client->netconnection->message); // run the entrance script if (sv.loadgame) @@ -1328,24 +1365,27 @@ void Host_Spawn_f (void) PRVM_ExecuteProgram (prog->globals.server->PutClientInServer, "QC function PutClientInServer is missing"); } + if (!host_client->netconnection) + return; // send time of update - MSG_WriteByte (&host_client->message, svc_time); - MSG_WriteFloat (&host_client->message, sv.time); + MSG_WriteByte (&host_client->netconnection->message, svc_time); + MSG_WriteFloat (&host_client->netconnection->message, sv.time); + // send all current names, colors, and frag counts for (i = 0, client = svs.clients;i < svs.maxclients;i++, client++) { if (!client->active) continue; - MSG_WriteByte (&host_client->message, svc_updatename); - MSG_WriteByte (&host_client->message, i); - MSG_WriteString (&host_client->message, client->name); - MSG_WriteByte (&host_client->message, svc_updatefrags); - MSG_WriteByte (&host_client->message, i); - MSG_WriteShort (&host_client->message, client->frags); - MSG_WriteByte (&host_client->message, svc_updatecolors); - MSG_WriteByte (&host_client->message, i); - MSG_WriteByte (&host_client->message, client->colors); + MSG_WriteByte (&host_client->netconnection->message, svc_updatename); + MSG_WriteByte (&host_client->netconnection->message, i); + MSG_WriteString (&host_client->netconnection->message, client->name); + MSG_WriteByte (&host_client->netconnection->message, svc_updatefrags); + MSG_WriteByte (&host_client->netconnection->message, i); + MSG_WriteShort (&host_client->netconnection->message, client->frags); + MSG_WriteByte (&host_client->netconnection->message, svc_updatecolors); + MSG_WriteByte (&host_client->netconnection->message, i); + MSG_WriteByte (&host_client->netconnection->message, client->colors); } // send all current light styles @@ -1353,44 +1393,54 @@ void Host_Spawn_f (void) { if (sv.lightstyles[i][0]) { - MSG_WriteByte (&host_client->message, svc_lightstyle); - MSG_WriteByte (&host_client->message, (char)i); - MSG_WriteString (&host_client->message, sv.lightstyles[i]); + MSG_WriteByte (&host_client->netconnection->message, svc_lightstyle); + MSG_WriteByte (&host_client->netconnection->message, (char)i); + MSG_WriteString (&host_client->netconnection->message, sv.lightstyles[i]); } } // send some stats - MSG_WriteByte (&host_client->message, svc_updatestat); - MSG_WriteByte (&host_client->message, STAT_TOTALSECRETS); - MSG_WriteLong (&host_client->message, prog->globals.server->total_secrets); + MSG_WriteByte (&host_client->netconnection->message, svc_updatestat); + MSG_WriteByte (&host_client->netconnection->message, STAT_TOTALSECRETS); + MSG_WriteLong (&host_client->netconnection->message, prog->globals.server->total_secrets); - MSG_WriteByte (&host_client->message, svc_updatestat); - MSG_WriteByte (&host_client->message, STAT_TOTALMONSTERS); - MSG_WriteLong (&host_client->message, prog->globals.server->total_monsters); + MSG_WriteByte (&host_client->netconnection->message, svc_updatestat); + MSG_WriteByte (&host_client->netconnection->message, STAT_TOTALMONSTERS); + MSG_WriteLong (&host_client->netconnection->message, prog->globals.server->total_monsters); - MSG_WriteByte (&host_client->message, svc_updatestat); - MSG_WriteByte (&host_client->message, STAT_SECRETS); - MSG_WriteLong (&host_client->message, prog->globals.server->found_secrets); + MSG_WriteByte (&host_client->netconnection->message, svc_updatestat); + MSG_WriteByte (&host_client->netconnection->message, STAT_SECRETS); + MSG_WriteLong (&host_client->netconnection->message, prog->globals.server->found_secrets); - MSG_WriteByte (&host_client->message, svc_updatestat); - MSG_WriteByte (&host_client->message, STAT_MONSTERS); - MSG_WriteLong (&host_client->message, prog->globals.server->killed_monsters); + MSG_WriteByte (&host_client->netconnection->message, svc_updatestat); + MSG_WriteByte (&host_client->netconnection->message, STAT_MONSTERS); + MSG_WriteLong (&host_client->netconnection->message, prog->globals.server->killed_monsters); // send a fixangle // Never send a roll angle, because savegames can catch the server // in a state where it is expecting the client to correct the angle // and it won't happen if the game was just loaded, so you wind up // with a permanent head tilt - MSG_WriteByte (&host_client->message, svc_setangle); - MSG_WriteAngle (&host_client->message, host_client->edict->fields.server->angles[0], sv.protocol); - MSG_WriteAngle (&host_client->message, host_client->edict->fields.server->angles[1], sv.protocol); - MSG_WriteAngle (&host_client->message, 0, sv.protocol); + if (sv.loadgame) + { + MSG_WriteByte (&host_client->netconnection->message, svc_setangle); + MSG_WriteAngle (&host_client->netconnection->message, host_client->edict->fields.server->v_angle[0], sv.protocol); + MSG_WriteAngle (&host_client->netconnection->message, host_client->edict->fields.server->v_angle[1], sv.protocol); + MSG_WriteAngle (&host_client->netconnection->message, 0, sv.protocol); + sv.loadgame = false; // we're basically done with loading now + } + else + { + MSG_WriteByte (&host_client->netconnection->message, svc_setangle); + MSG_WriteAngle (&host_client->netconnection->message, host_client->edict->fields.server->angles[0], sv.protocol); + MSG_WriteAngle (&host_client->netconnection->message, host_client->edict->fields.server->angles[1], sv.protocol); + MSG_WriteAngle (&host_client->netconnection->message, 0, sv.protocol); + } - SV_WriteClientdataToMessage (host_client, host_client->edict, &host_client->message, stats); + SV_WriteClientdataToMessage (host_client, host_client->edict, &host_client->netconnection->message, stats); - MSG_WriteByte (&host_client->message, svc_signonnum); - MSG_WriteByte (&host_client->message, 3); - host_client->sendsignon = true; + MSG_WriteByte (&host_client->netconnection->message, svc_signonnum); + MSG_WriteByte (&host_client->netconnection->message, 3); } /* @@ -1869,7 +1919,7 @@ void Host_Stopdemo_f (void) if (!cls.demoplayback) return; CL_Disconnect (); - Host_ShutdownServer (false); + Host_ShutdownServer (); } void Host_SendCvar_f (void) @@ -1933,6 +1983,305 @@ static void MaxPlayers_f(void) //============================================================================= +// QuakeWorld commands + +/* +===================== +Host_Rcon_f + + Send the rest of the command line over as + an unconnected command. +===================== +*/ +void Host_Rcon_f (void) // credit: taken from QuakeWorld +{ + int i; + lhnetaddress_t to; + lhnetsocket_t *mysocket; + + if (!rcon_password.string || !rcon_password.string[0]) + { + Con_Printf ("You must set rcon_password before issuing an rcon command.\n"); + return; + } + + for (i = 0;rcon_password.string[i];i++) + { + if (rcon_password.string[i] <= ' ') + { + Con_Printf("rcon_password is not allowed to have any whitespace.\n"); + return; + } + } + + if (cls.netcon) + to = cls.netcon->peeraddress; + else + { + if (!rcon_address.integer || !rcon_address.string[0]) + { + Con_Printf ("You must either be connected, or set the rcon_address cvar to issue rcon commands\n"); + return; + } + LHNETADDRESS_FromString(&to, rcon_address.string, sv_netport.integer); + } + mysocket = NetConn_ChooseClientSocketForAddress(&to); + if (mysocket) + { + // simply put together the rcon packet and send it + NetConn_WriteString(mysocket, va("\377\377\377\377rcon %s %s", rcon_password.string, Cmd_Args()), &to); + } +} + +/* +==================== +Host_User_f + +user + +Dump userdata / masterdata for a user +==================== +*/ +void Host_User_f (void) // credit: taken from QuakeWorld +{ + int uid; + int i; + + if (Cmd_Argc() != 2) + { + Con_Printf ("Usage: user \n"); + return; + } + + uid = atoi(Cmd_Argv(1)); + + for (i = 0;i < cl.maxclients;i++) + { + if (!cl.scores[i].name[0]) + continue; + if (cl.scores[i].qw_userid == uid || !strcasecmp(cl.scores[i].name, Cmd_Argv(1))) + { + InfoString_Print(cl.scores[i].qw_userinfo); + return; + } + } + Con_Printf ("User not in server.\n"); +} + +/* +==================== +Host_Users_f + +Dump userids for all current players +==================== +*/ +void Host_Users_f (void) // credit: taken from QuakeWorld +{ + int i; + int c; + + c = 0; + Con_Printf ("userid frags name\n"); + Con_Printf ("------ ----- ----\n"); + for (i = 0;i < cl.maxclients;i++) + { + if (cl.scores[i].name[0]) + { + Con_Printf ("%6i %4i %s\n", cl.scores[i].qw_userid, cl.scores[i].frags, cl.scores[i].name); + c++; + } + } + + Con_Printf ("%i total users\n", c); +} + +/* +================== +Host_FullServerinfo_f + +Sent by server when serverinfo changes +================== +*/ +// TODO: shouldn't this be a cvar instead? +void Host_FullServerinfo_f (void) // credit: taken from QuakeWorld +{ + char temp[512]; + if (Cmd_Argc() != 2) + { + Con_Printf ("usage: fullserverinfo \n"); + return; + } + + strlcpy (cl.qw_serverinfo, Cmd_Argv(1), sizeof(cl.qw_serverinfo)); + InfoString_GetValue(cl.qw_serverinfo, "teamplay", temp, sizeof(temp)); + cl.qw_teamplay = atoi(temp); +} + +/* +================== +Host_FullInfo_f + +Allow clients to change userinfo +================== +Casey was here :) +*/ +void Host_FullInfo_f (void) // credit: taken from QuakeWorld +{ + char key[512]; + char value[512]; + char *o; + const char *s; + + if (Cmd_Argc() != 2) + { + Con_Printf ("fullinfo \n"); + return; + } + + s = Cmd_Argv(1); + if (*s == '\\') + s++; + while (*s) + { + o = key; + while (*s && *s != '\\') + *o++ = *s++; + *o = 0; + + if (!*s) + { + Con_Printf ("MISSING VALUE\n"); + return; + } + + o = value; + s++; + while (*s && *s != '\\') + *o++ = *s++; + *o = 0; + + if (*s) + s++; + + if (!strcasecmp(key, "pmodel") || !strcasecmp(key, "emodel")) + continue; + + if (key[0] == '*') + { + Con_Printf("Can't set star-key \"%s\" to \"%s\"\n", key, value); + continue; + } + + InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), key, value); + } +} + +/* +================== +CL_SetInfo_f + +Allow clients to change userinfo +================== +*/ +void Host_SetInfo_f (void) // credit: taken from QuakeWorld +{ + if (Cmd_Argc() == 1) + { + InfoString_Print(cls.userinfo); + return; + } + if (Cmd_Argc() != 3) + { + Con_Printf ("usage: setinfo [ ]\n"); + return; + } + if (!strcasecmp(Cmd_Argv(1), "pmodel") || !strcasecmp(Cmd_Argv(1), "emodel")) + return; + if (Cmd_Argv(1)[0] == '*') + { + Con_Printf("Can't set star-key \"%s\" to \"%s\"\n", Cmd_Argv(1), Cmd_Argv(2)); + return; + } + InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), Cmd_Argv(1), Cmd_Argv(2)); + if (cls.state == ca_connected) + Cmd_ForwardToServer (); +} + +/* +==================== +Host_Packet_f + +packet + +Contents allows \n escape character +==================== +*/ +void Host_Packet_f (void) // credit: taken from QuakeWorld +{ + char send[2048]; + int i, l; + const char *in; + char *out; + lhnetaddress_t address; + lhnetsocket_t *mysocket; + + if (Cmd_Argc() != 3) + { + Con_Printf ("packet \n"); + return; + } + + if (!LHNETADDRESS_FromString (&address, Cmd_Argv(1), sv_netport.integer)) + { + Con_Printf ("Bad address\n"); + return; + } + + in = Cmd_Argv(2); + out = send+4; + send[0] = send[1] = send[2] = send[3] = 0xff; + + l = (int)strlen (in); + for (i=0 ; i= send + sizeof(send) - 1) + break; + if (in[i] == '\\' && in[i+1] == 'n') + { + *out++ = '\n'; + i++; + } + else if (in[i] == '\\' && in[i+1] == '0') + { + *out++ = '\0'; + i++; + } + else if (in[i] == '\\' && in[i+1] == 't') + { + *out++ = '\t'; + i++; + } + else if (in[i] == '\\' && in[i+1] == 'r') + { + *out++ = '\r'; + i++; + } + else if (in[i] == '\\' && in[i+1] == '"') + { + *out++ = '\"'; + i++; + } + else + *out++ = in[i]; + } + + mysocket = NetConn_ChooseClientSocketForAddress(&address); + if (mysocket) + NetConn_Write(mysocket, send, out - send, &address); +} + +//============================================================================= + /* ================== Host_InitCommands @@ -1940,6 +2289,8 @@ Host_InitCommands */ void Host_InitCommands (void) { + strcpy(cls.userinfo, "\\name\\player\\team\\none\\topcolor\\0\\bottomcolor\\0\\rate\\10000\\msg\\1\\*ver\\dp"); + Cmd_AddCommand ("status", Host_Status_f, "print server status information"); Cmd_AddCommand ("quit", Host_Quit_f, "quit the game"); if (gamemode == GAME_NEHAHRA) @@ -2008,6 +2359,16 @@ void Host_InitCommands (void) Cmd_AddCommand ("sendcvar", Host_SendCvar_f, "sends the value of a cvar to the server as a sentcvar command, for use by QuakeC"); // By [515] + Cvar_RegisterVariable (&rcon_password); + Cvar_RegisterVariable (&rcon_address); + Cmd_AddCommand ("rcon", Host_Rcon_f, "sends a command to the server console (if your rcon_password matches the server's rcon_password), or to the address specified by rcon_address when not connected (again rcon_password must match the server's)"); + Cmd_AddCommand ("user", Host_User_f, "prints additional information about a player number or name on the scoreboard"); + Cmd_AddCommand ("users", Host_Users_f, "prints additional information about all players on the scoreboard"); + Cmd_AddCommand ("fullserverinfo", Host_FullServerinfo_f, "internal use only, sent by server to client to update client's local copy of serverinfo string"); + Cmd_AddCommand ("fullinfo", Host_FullInfo_f, "allows client to modify their userinfo"); + Cmd_AddCommand ("setinfo", Host_SetInfo_f, "modifies your userinfo"); + Cmd_AddCommand ("packet", Host_Packet_f, "send a packet to the specified address:port containing a text string"); + Cvar_RegisterVariable(&sv_cheats); }