extern cvar_t cl_playermodel;
extern cvar_t cl_playerskin;
+extern cvar_t rcon_password;
+extern cvar_t rcon_address;
+
extern cvar_t cl_upspeed;
extern cvar_t cl_forwardspeed;
extern cvar_t cl_backspeed;
qboolean con_initialized;
+// used for server replies to rcon command
+qboolean rcon_redirect = false;
+int rcon_redirect_bufferpos = 0;
+char rcon_redirect_buffer[1400];
+
/*
==============================================================================
for (;*msg;msg++)
{
+ // if this print is in response to an rcon command, add the character
+ // to the rcon redirect buffer
+ if (rcon_redirect && rcon_redirect_bufferpos < (int)sizeof(rcon_redirect_buffer) - 1)
+ rcon_redirect_buffer[rcon_redirect_bufferpos++] = *msg;
+ // if this is the beginning of a new line, print timestamp
if (index == 0)
{
- // if this is the beginning of a new line, print timestamp
const char *timestamp = timestamps.integer ? Sys_TimeString(timeformat.string) : "";
// reset the color
// FIXME: 1. perhaps we should use a terminal system 2. use a constant instead of 7!
extern int con_backscroll;
extern qboolean con_initialized;
+extern qboolean rcon_redirect;
+extern int rcon_redirect_bufferpos;
+extern char rcon_redirect_buffer[1400];
+
void Con_CheckResize (void);
void Con_Init (void);
void Con_Init_Commands (void);
static qboolean hosterror = false;
va_list argptr;
+ // turn off rcon redirect if it was active when the crash occurred
+ rcon_redirect = false;
+
va_start (argptr,error);
dpvsnprintf (hosterrorstring1,sizeof(hosterrorstring1),error,argptr);
va_end (argptr);
// set the time and clear the general datagram
SV_ClearDatagram();
- // check for network packets to the server each world step incase they
- // come in midframe (particularly if host is running really slow)
- NetConn_ServerFrame();
-
// move things around and think unless paused
if (sv.frametime)
SV_Physics();
//
//-------------------
+ // receive server packets now, which might contain rcon commands, which
+ // may change level or other such things we don't want to have happen in
+ // the middle of Host_Frame
+ NetConn_ServerFrame();
+
// check for commands typed to the host
Host_GetConsoleCommands();
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;
/*
host_client->nametime = sv.time + 5;
// point the string back at updateclient->name to keep it safe
+ SV_VM_Begin();
strlcpy (host_client->name, newName, sizeof (host_client->name));
host_client->edict->fields.server->netname = PRVM_SetEngineString(host_client->name);
if (strcmp(host_client->old_name, host_client->name))
MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
MSG_WriteString (&sv.reliable_datagram, host_client->name);
}
+ SV_VM_End();
}
/*
host_client->nametime = sv.time + 5;
*/
+ SV_VM_Begin();
// point the string back at updateclient->name to keep it safe
strlcpy (host_client->playermodel, newPath, sizeof (host_client->playermodel));
if( eval_playermodel )
MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
MSG_WriteString (&sv.reliable_datagram, host_client->playermodel);*/
}
+ SV_VM_End();
}
/*
host_client->nametime = sv.time + 5;
*/
+ SV_VM_Begin();
// point the string back at updateclient->name to keep it safe
strlcpy (host_client->playerskin, newPath, sizeof (host_client->playerskin));
if( eval_playerskin )
MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
MSG_WriteString (&sv.reliable_datagram, host_client->playerskin);*/
}
+ SV_VM_End();
}
void Host_Version_f (void)
return;
}
+ SV_VM_Begin();
if (host_client->edict && (f = PRVM_ED_FindFunction ("SV_ChangeTeam")) && (SV_ChangeTeam = (func_t)(f - prog->functions)))
{
Con_DPrint("Calling SV_ChangeTeam\n");
MSG_WriteByte (&sv.reliable_datagram, host_client->colors);
}
}
+ SV_VM_End();
}
cvar_t cl_rate = {CVAR_SAVE, "_cl_rate", "10000", "internal storage cvar for current rate (changed by rate command)"};
return;
}
+ SV_VM_Begin();
prog->globals.server->time = sv.time;
prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
PRVM_ExecuteProgram (prog->globals.server->ClientKill, "QC function ClientKill is missing");
+ SV_VM_End();
}
return;
}
+ SV_VM_Begin();
if (host_client->edict && (val = PRVM_GETEDICTFIELDVALUE(host_client->edict, eval_pmodel)))
val->_float = i;
+ SV_VM_End();
}
//===========================================================================
// SZ_Clear (&host_client->netconnection->message);
// run the entrance script
+ SV_VM_Begin();
if (sv.loadgame)
{
// loaded games are fully initialized already
PRVM_ExecuteProgram (prog->globals.server->PutClientInServer, "QC function PutClientInServer is missing");
}
+ SV_VM_End();
if (!host_client->netconnection)
return;
+ SV_VM_Begin();
// send time of update
MSG_WriteByte (&host_client->netconnection->message, svc_time);
MSG_WriteFloat (&host_client->netconnection->message, sv.time);
MSG_WriteByte (&host_client->netconnection->message, svc_signonnum);
MSG_WriteByte (&host_client->netconnection->message, 3);
+ SV_VM_End();
}
/*
t = Cmd_Argv(1);
v = atoi (Cmd_Argv(2));
+ SV_VM_Begin();
switch (t[0])
{
case '0':
}
break;
}
+ SV_VM_End();
}
prvm_edict_t *FindViewthing (void)
//=============================================================================
+// QuakeWorld commands
+
+char emodel_name[] =
+ { 'e' ^ 0xff, 'm' ^ 0xff, 'o' ^ 0xff, 'd' ^ 0xff, 'e' ^ 0xff, 'l' ^ 0xff, 0 };
+char pmodel_name[] =
+ { 'p' ^ 0xff, 'm' ^ 0xff, 'o' ^ 0xff, 'd' ^ 0xff, 'e' ^ 0xff, 'l' ^ 0xff, 0 };
+char prespawn_name[] =
+ { 'p'^0xff, 'r'^0xff, 'e'^0xff, 's'^0xff, 'p'^0xff, 'a'^0xff, 'w'^0xff, 'n'^0xff,
+ ' '^0xff, '%'^0xff, 'i'^0xff, ' '^0xff, '0'^0xff, ' '^0xff, '%'^0xff, 'i'^0xff, 0 };
+char modellist_name[] =
+ { 'm'^0xff, 'o'^0xff, 'd'^0xff, 'e'^0xff, 'l'^0xff, 'l'^0xff, 'i'^0xff, 's'^0xff, 't'^0xff,
+ ' '^0xff, '%'^0xff, 'i'^0xff, ' '^0xff, '%'^0xff, 'i'^0xff, 0 };
+char soundlist_name[] =
+ { 's'^0xff, 'o'^0xff, 'u'^0xff, 'n'^0xff, 'd'^0xff, 'l'^0xff, 'i'^0xff, 's'^0xff, 't'^0xff,
+ ' '^0xff, '%'^0xff, 'i'^0xff, ' '^0xff, '%'^0xff, 'i'^0xff, 0 };
+
+/*
+=====================
+Host_Rcon_f
+
+ Send the rest of the command line over as
+ an unconnected command.
+=====================
+*/
+void Host_Rcon_f (void) // credit: taken from QuakeWorld
+{
+ lhnetaddress_t to;
+ lhnetsocket_t *mysocket;
+
+ if (!rcon_password.string)
+ {
+ Con_Printf ("You must set rcon_password before issuing an rcon command.\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_Packet_f
+
+packet <destination> <contents>
+
+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 <destination> <contents>\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 = strlen (in);
+ for (i=0 ; i<l ; i++)
+ {
+ if (out >= send + sizeof(send) - 1)
+ break;
+ if (in[i] == '\\' && in[i+1] == 'n')
+ {
+ *out++ = '\n';
+ i++;
+ }
+ else
+ *out++ = in[i];
+ }
+ *out = 0;
+
+ mysocket = NetConn_ChooseClientSocketForAddress(&address);
+ if (mysocket)
+ NetConn_WriteString(mysocket, send, &address);
+}
+
+//=============================================================================
+
/*
==================
Host_InitCommands
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 ("packet", Host_Packet_f, "send a packet to the specified address:port containing a text string");
+
Cvar_RegisterVariable(&sv_cheats);
}
stringbuf[length] = 0;
string = stringbuf;
+ LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
+
if (developer.integer)
{
- LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
Con_Printf("NetConn_ClientParsePacket: %s sent us a command:\n", addressstring2);
Com_HexDumpToConsole(data, length);
}
{
char protocolnames[1400];
Protocol_Names(protocolnames, sizeof(protocolnames));
- LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
Con_Printf("\"%s\" received, sending connect request back to %s\n", string, addressstring2);
M_Update_Return_Reason("Got challenge response");
NetConn_WriteString(mysocket, va("\377\377\377\377connect\\protocol\\darkplaces 3\\protocols\\%s\\challenge\\%s", protocolnames, string + 10), peeraddress);
if (!strncmp(string, "ack", 3))
return true;
*/
+ if (string[0] == 'n')
+ {
+ // qw print command
+ Con_Printf("QW print command from server at %s:\n", addressstring2, string + 1);
+ }
// we may not have liked the packet, but it was a command packet, so
// we're done processing this packet now
return true;
double besttime;
client_t *client;
netconn_t *conn;
- char *s, *string, response[512], addressstring2[128], stringbuf[16384];
+ char *s, *string, response[1400], addressstring2[128], stringbuf[16384];
+
+ // see if we can identify the sender as a local player
+ // (this is necessary for rcon to send a reliable reply if the client is
+ // actually on the server, not sending remotely)
+ for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
+ if (host_client->netconnection && host_client->netconnection->mysocket == mysocket && !LHNETADDRESS_Compare(&host_client->netconnection->peeraddress, peeraddress))
+ break;
+ if (i == svs.maxclients)
+ host_client = NULL;
if (sv.active)
{
Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", conn->address);
NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
// now set up the client
+ SV_VM_Begin();
SV_ConnectClient(clientnum, conn);
+ SV_VM_End();
NetConn_Heartbeat(1);
}
}
}
return true;
}
+ if (length >= 5 && !memcmp(string, "rcon ", 5))
+ {
+ int i;
+ char *s = string + 5;
+ char password[64];
+ for (i = 0;*s > ' ';s++)
+ if (i < (int)sizeof(password) - 1)
+ password[i++] = *s;
+ password[i] = 0;
+ if (!strcmp(rcon_password.string, password))
+ {
+ // looks like a legitimate rcon command with the correct password
+ Con_Printf("server received rcon command from %s:\n%s\n", host_client ? host_client->name : addressstring2, s);
+ rcon_redirect = true;
+ rcon_redirect_bufferpos = 0;
+ Cmd_ExecuteString(s, src_command);
+ rcon_redirect_buffer[rcon_redirect_bufferpos] = 0;
+ rcon_redirect = false;
+ // print resulting text to client
+ // if client is playing, send a reliable reply instead of
+ // a command packet
+ if (host_client)
+ {
+ // if the netconnection is loop, then this is the
+ // local player on a listen mode server, and it would
+ // result in duplicate printing to the console
+ // (not that the local player should be using rcon
+ // when they have the console)
+ if (host_client->netconnection && LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) != LHNETADDRESSTYPE_LOOP)
+ SV_ClientPrintf("%s", rcon_redirect_buffer);
+ }
+ else
+ {
+ // qw print command
+ dpsnprintf(response, sizeof(response), "\377\377\377\377n%s", rcon_redirect_buffer);
+ NetConn_WriteString(mysocket, response, peeraddress);
+ }
+ }
+ return true;
+ }
/*
if (!strncmp(string, "ping", 4))
{
NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
SZ_Clear(&net_message);
// now set up the client struct
+ SV_VM_Begin();
SV_ConnectClient(clientnum, conn);
+ SV_VM_End();
NetConn_Heartbeat(1);
}
else
}
}
#endif
- for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
+ if (host_client)
{
- if (host_client->netconnection && host_client->netconnection->mysocket == mysocket && !LHNETADDRESS_Compare(&host_client->netconnection->peeraddress, peeraddress))
+ if ((ret = NetConn_ReceivedMessage(host_client->netconnection, data, length)) == 2)
{
- if ((ret = NetConn_ReceivedMessage(host_client->netconnection, data, length)) == 2)
- SV_ReadClientMessage();
+ SV_ReadClientMessage();
return ret;
}
}
extern cvar_t cl_netlocalping;
+extern cvar_t cl_netport;
+extern cvar_t sv_netport;
+extern cvar_t net_address;
+//extern cvar_t net_netaddress_ipv6;
+
int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data);
void NetConn_CloseClientPorts(void);
void NetConn_OpenClientPorts(void);
netconn_t *NetConn_Open(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress);
void NetConn_Close(netconn_t *conn);
void NetConn_Listen(qboolean state);
+int NetConn_Read(lhnetsocket_t *mysocket, void *data, int maxlength, lhnetaddress_t *peeraddress);
+int NetConn_Write(lhnetsocket_t *mysocket, const void *data, int length, const lhnetaddress_t *peeraddress);
+int NetConn_WriteString(lhnetsocket_t *mysocket, const char *string, const lhnetaddress_t *peeraddress);
int NetConn_IsLocalGame(void);
void NetConn_ClientFrame(void);
void NetConn_ServerFrame(void);
*/
qboolean SV_ReadClientMove (void)
{
+ qboolean kickplayer = false;
int i;
double oldmovetime;
usercmd_t *move = &host_client->cmd;
+ SV_VM_Begin();
+
oldmovetime = move->time;
// if this move has been applied, clear it, and start accumulating new data
// this fixes the timestamp to prevent a speed cheat from working
move->time = sv.time;
// but we kick the player for good measure
- return true;
+ kickplayer = true;
}
else
{
prog->globals.server->frametime = oldframetime;
}
}
- return false;
+ SV_VM_End();
+ return kickplayer;
}
void SV_ApplyClientMove (void)