Knocked up some commands and stuff to make qtv not forget about streams that failed. Finally implemented the commands command. Added commands to enable/disable streams.
git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@2812 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
parent
81b91a422d
commit
6006580d8a
5 changed files with 303 additions and 141 deletions
|
@ -344,7 +344,7 @@ static void ParseStufftext(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
|
|||
SendClientCommand(tv, "new\n");
|
||||
return;
|
||||
}
|
||||
tv->drop = true; //this shouldn't ever happen
|
||||
Sys_Printf(tv->cluster, "packet stuffcmd in an mvd\n"); //shouldn't ever happen, try ignoring it.
|
||||
return;
|
||||
}
|
||||
else if (tv->usequakeworldprotocols && !strncmp(text, "setinfo ", 8))
|
||||
|
@ -1380,6 +1380,7 @@ void ParseNails(sv_t *tv, netmsg_t *m, qboolean nails2)
|
|||
|
||||
void ParseDownload(sv_t *tv, netmsg_t *m)
|
||||
{
|
||||
#warning this needs looking at (controller downloads)
|
||||
int size, b;
|
||||
unsigned int percent;
|
||||
char buffer[2048];
|
||||
|
@ -1393,7 +1394,7 @@ void ParseDownload(sv_t *tv, netmsg_t *m)
|
|||
if (tv->downloadfile)
|
||||
fclose(tv->downloadfile);
|
||||
tv->downloadfile = NULL;
|
||||
tv->drop = true;
|
||||
tv->errored = ERR_PERMANENT;
|
||||
QW_StreamPrint(tv->cluster, tv, NULL, "Map download failed\n");
|
||||
return;
|
||||
}
|
||||
|
@ -1404,7 +1405,7 @@ void ParseDownload(sv_t *tv, netmsg_t *m)
|
|||
if (!tv->downloadfile)
|
||||
{
|
||||
Sys_Printf(tv->cluster, "Not downloading anything\n");
|
||||
tv->drop = true;
|
||||
tv->errored = ERR_PERMANENT;
|
||||
return;
|
||||
}
|
||||
fwrite(buffer, 1, size, tv->downloadfile);
|
||||
|
@ -1423,7 +1424,7 @@ void ParseDownload(sv_t *tv, netmsg_t *m)
|
|||
if (!tv->bsp)
|
||||
{
|
||||
Sys_Printf(tv->cluster, "Failed to read BSP\n");
|
||||
tv->drop = true;
|
||||
tv->errored = ERR_PERMANENT;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
10
fteqtv/qtv.h
10
fteqtv/qtv.h
|
@ -478,6 +478,14 @@ typedef enum {
|
|||
SRC_TCP
|
||||
} sourcetype_t;
|
||||
|
||||
typedef enum {
|
||||
ERR_NONE, //stream is fine
|
||||
ERR_RECONNECT, //stream needs to reconnect
|
||||
ERR_PERMANENT, //permanent error, transitioning to disabled next frame
|
||||
ERR_DISABLED, //stream is disabled, can be set to reconnect by admin
|
||||
ERR_DROP //stream _will_ be forgotten about next frame
|
||||
} errorstate_t;
|
||||
|
||||
struct sv_s { //details about a server connection (also known as stream)
|
||||
char connectpassword[64]; //password given to server
|
||||
netadr_t serveraddress;
|
||||
|
@ -580,7 +588,7 @@ struct sv_s { //details about a server connection (also known as stream)
|
|||
|
||||
|
||||
|
||||
qboolean drop;
|
||||
errorstate_t errored;
|
||||
qboolean disconnectwhennooneiswatching;
|
||||
unsigned int numviewers;
|
||||
|
||||
|
|
11
fteqtv/qw.c
11
fteqtv/qw.c
|
@ -3644,13 +3644,14 @@ void ParseQWC(cluster_t *cluster, sv_t *qtv, viewer_t *v, netmsg_t *m)
|
|||
{
|
||||
if (!qtv->bsp)
|
||||
{
|
||||
QW_PrintfToViewer(v, "Proxy was unable to check your map version\n");
|
||||
qtv->drop = true;
|
||||
#warning do we still actually need to do this ourselves? Or can we just forward what the user stated?
|
||||
QW_PrintfToViewer(v, "QTV doesn't have that map (%s), sorry.\n", qtv->modellist[1].name);
|
||||
qtv->errored = ERR_DROP;
|
||||
}
|
||||
else if (crc != BSP_Checksum(qtv->bsp))
|
||||
{
|
||||
QW_PrintfToViewer(v, "Your map (%s) does not match the servers\n", qtv->modellist[1].name);
|
||||
qtv->drop = true;
|
||||
QW_PrintfToViewer(v, "QTV's map (%s) does not match the servers\n", qtv->modellist[1].name);
|
||||
qtv->errored = ERR_DROP;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4484,7 +4485,7 @@ void QW_FreeViewer(cluster_t *cluster, viewer_t *viewer)
|
|||
if (viewer->server->controller == viewer)
|
||||
{
|
||||
if (viewer->server->disconnectwhennooneiswatching)
|
||||
viewer->server->drop = true;
|
||||
viewer->server->errored = ERR_DROP;
|
||||
else
|
||||
viewer->server->controller = NULL;
|
||||
}
|
||||
|
|
213
fteqtv/rcon.c
213
fteqtv/rcon.c
|
@ -351,7 +351,7 @@ void Cmd_Hostname(cmdctxt_t *ctx)
|
|||
if (Cmd_Argc(ctx) < 2)
|
||||
{
|
||||
if (*ctx->cluster->hostname)
|
||||
Cmd_Printf(ctx, "Current hostname is %s\n", ctx->cluster->hostname);
|
||||
Cmd_Printf(ctx, "Current hostname is \"%s\"\n", ctx->cluster->hostname);
|
||||
else
|
||||
Cmd_Printf(ctx, "No master server is currently set.\n");
|
||||
}
|
||||
|
@ -429,7 +429,7 @@ void Cmd_AdminPassword(cmdctxt_t *ctx)
|
|||
if (*ctx->cluster->adminpassword)
|
||||
Cmd_Printf(ctx, "An admin password is currently set\n");
|
||||
else
|
||||
Cmd_Printf(ctx, "No admin passsword is currently set\n");
|
||||
Cmd_Printf(ctx, "No admin password is currently set\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -472,6 +472,7 @@ void Cmd_QTVDemoList(cmdctxt_t *ctx)
|
|||
|
||||
void Cmd_GenericConnect(cmdctxt_t *ctx, char *method)
|
||||
{
|
||||
sv_t *sv;
|
||||
char *address, *password;
|
||||
if (Cmd_Argc(ctx) < 2)
|
||||
{
|
||||
|
@ -488,10 +489,11 @@ void Cmd_GenericConnect(cmdctxt_t *ctx, char *method)
|
|||
memmove(address+strlen(method), address, ARG_LEN-(1+strlen(method)));
|
||||
strncpy(address, method, strlen(method));
|
||||
|
||||
if (!QTV_NewServerConnection(ctx->cluster, address, password, false, false, false, false))
|
||||
sv = QTV_NewServerConnection(ctx->cluster, address, password, false, false, false, false);
|
||||
if (!sv)
|
||||
Cmd_Printf(ctx, "Failed to connect to \"%s\", connection aborted\n", address);
|
||||
|
||||
Cmd_Printf(ctx, "Source registered \"%s\"\n", address);
|
||||
else
|
||||
Cmd_Printf(ctx, "Source registered \"%s\" as stream %i\n", address, sv->streamid);
|
||||
}
|
||||
|
||||
void Cmd_QTVConnect(cmdctxt_t *ctx)
|
||||
|
@ -591,11 +593,12 @@ void Cmd_Say(cmdctxt_t *ctx)
|
|||
|
||||
void Cmd_Status(cmdctxt_t *ctx)
|
||||
{
|
||||
Cmd_Printf(ctx, "%i sources\n", ctx->cluster->numservers);
|
||||
Cmd_Printf(ctx, "%i viewers\n", ctx->cluster->numviewers);
|
||||
Cmd_Printf(ctx, "%i proxies\n", ctx->cluster->numproxies);
|
||||
Cmd_Printf(ctx, "QTV Status:\n");
|
||||
Cmd_Printf(ctx, " %i sources\n", ctx->cluster->numservers);
|
||||
Cmd_Printf(ctx, " %i viewers\n", ctx->cluster->numviewers);
|
||||
Cmd_Printf(ctx, " %i proxies\n", ctx->cluster->numproxies);
|
||||
|
||||
Cmd_Printf(ctx, "Options:\n");
|
||||
Cmd_Printf(ctx, "Common Options:\n");
|
||||
Cmd_Printf(ctx, " Hostname %s\n", ctx->cluster->hostname);
|
||||
|
||||
if (ctx->cluster->chokeonnotupdated)
|
||||
|
@ -622,41 +625,44 @@ void Cmd_Status(cmdctxt_t *ctx)
|
|||
{
|
||||
Cmd_Printf(ctx, "Selected server: %s\n", ctx->qtv->server);
|
||||
if (ctx->qtv->sourcefile)
|
||||
Cmd_Printf(ctx, "Playing from file\n");
|
||||
Cmd_Printf(ctx, " Playing from file\n");
|
||||
if (ctx->qtv->sourcesock != INVALID_SOCKET)
|
||||
Cmd_Printf(ctx, "Connected\n");
|
||||
Cmd_Printf(ctx, " Connected\n");
|
||||
if (ctx->qtv->parsingqtvheader || ctx->qtv->parsingconnectiondata)
|
||||
Cmd_Printf(ctx, "Waiting for gamestate\n");
|
||||
Cmd_Printf(ctx, " Waiting for gamestate\n");
|
||||
if (ctx->qtv->usequakeworldprotocols)
|
||||
{
|
||||
Cmd_Printf(ctx, "QuakeWorld protocols\n");
|
||||
Cmd_Printf(ctx, " QuakeWorld protocols\n");
|
||||
if (ctx->qtv->controller)
|
||||
{
|
||||
Cmd_Printf(ctx, "Controlled by %s\n", ctx->qtv->controller->name);
|
||||
Cmd_Printf(ctx, " Controlled by %s\n", ctx->qtv->controller->name);
|
||||
}
|
||||
}
|
||||
else if (ctx->qtv->sourcesock == INVALID_SOCKET && !ctx->qtv->sourcefile)
|
||||
Cmd_Printf(ctx, "Connection not established\n");
|
||||
Cmd_Printf(ctx, " Connection not established\n");
|
||||
|
||||
if (*ctx->qtv->modellist[1].name)
|
||||
{
|
||||
Cmd_Printf(ctx, "Map name %s\n", ctx->qtv->modellist[1].name);
|
||||
Cmd_Printf(ctx, " Map name %s\n", ctx->qtv->modellist[1].name);
|
||||
}
|
||||
if (*ctx->qtv->connectpassword)
|
||||
Cmd_Printf(ctx, "Using a password\n");
|
||||
Cmd_Printf(ctx, " Using a password\n");
|
||||
|
||||
if (ctx->qtv->errored == ERR_DISABLED)
|
||||
Cmd_Printf(ctx, " Stream is disabled\n");
|
||||
|
||||
if (ctx->qtv->disconnectwhennooneiswatching)
|
||||
Cmd_Printf(ctx, "Stream is temporary\n");
|
||||
Cmd_Printf(ctx, " Stream is temporary\n");
|
||||
|
||||
/* if (ctx->qtv->tcpsocket != INVALID_SOCKET)
|
||||
{
|
||||
Cmd_Printf(ctx, "Listening for proxies (%i)\n", ctx->qtv->tcplistenportnum);
|
||||
Cmd_Printf(ctx, " Listening for proxies (%i)\n", ctx->qtv->tcplistenportnum);
|
||||
}
|
||||
*/
|
||||
|
||||
if (ctx->qtv->bsp)
|
||||
{
|
||||
Cmd_Printf(ctx, "BSP (%s) is loaded\n", ctx->qtv->mapname);
|
||||
Cmd_Printf(ctx, " BSP (%s) is loaded\n", ctx->qtv->mapname);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -826,11 +832,38 @@ void Cmd_Quit(cmdctxt_t *ctx)
|
|||
void Cmd_Streams(cmdctxt_t *ctx)
|
||||
{
|
||||
sv_t *qtv;
|
||||
char *status;
|
||||
Cmd_Printf(ctx, "Streams:\n");
|
||||
|
||||
for (qtv = ctx->cluster->servers; qtv; qtv = qtv->next)
|
||||
{
|
||||
Cmd_Printf(ctx, "%i: %s\n", qtv->streamid, qtv->server);
|
||||
switch (qtv->errored)
|
||||
{
|
||||
case ERR_NONE:
|
||||
if (qtv->controller)
|
||||
status = " (player controlled)";
|
||||
else if (qtv->parsingconnectiondata)
|
||||
status = " (connecting)";
|
||||
else
|
||||
status = "";
|
||||
break;
|
||||
case ERR_DISABLED:
|
||||
status = " (disabled)";
|
||||
break;
|
||||
case ERR_DROP: //a user should never normally see this, but there is a chance
|
||||
status = " (dropping)";
|
||||
break;
|
||||
case ERR_RECONNECT: //again, rare
|
||||
status = " (reconnecting)";
|
||||
break;
|
||||
default: //some other kind of error, transitioning
|
||||
status = " (errored)";
|
||||
break;
|
||||
}
|
||||
Cmd_Printf(ctx, "%i: %s%s\n", qtv->streamid, qtv->server, status);
|
||||
|
||||
if (qtv->upstreamacceptschat)
|
||||
Cmd_Printf(ctx, " (dbg) can chat!\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -855,6 +888,27 @@ void Cmd_Disconnect(cmdctxt_t *ctx)
|
|||
Cmd_Printf(ctx, "Disconnected\n");
|
||||
}
|
||||
|
||||
void Cmd_Halt(cmdctxt_t *ctx)
|
||||
{
|
||||
if (ctx->qtv->errored == ERR_DISABLED || ctx->qtv->errored == ERR_PERMANENT)
|
||||
{
|
||||
Cmd_Printf(ctx, "Stream is already halted\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
ctx->qtv->errored = ERR_PERMANENT;
|
||||
Cmd_Printf(ctx, "Stream will disconnect\n");
|
||||
}
|
||||
}
|
||||
void Cmd_Resume(cmdctxt_t *ctx)
|
||||
{
|
||||
if (ctx->qtv->errored == ERR_NONE)
|
||||
Cmd_Printf(ctx, "Stream is already functional\n");
|
||||
|
||||
ctx->qtv->errored = ERR_RECONNECT;
|
||||
Cmd_Printf(ctx, "Stream will attempt to reconnect\n");
|
||||
}
|
||||
|
||||
void Cmd_Record(cmdctxt_t *ctx)
|
||||
{
|
||||
char *fname = Cmd_Argv(ctx, 1);
|
||||
|
@ -1000,10 +1054,10 @@ void Cmd_MuteStream(cmdctxt_t *ctx)
|
|||
if (*val)
|
||||
{
|
||||
ctx->qtv->silentstream = atoi(val);
|
||||
Cmd_Printf(ctx, "Stream is now %smuted\n", ctx->qtv->silentstream?"un":"");
|
||||
Cmd_Printf(ctx, "Stream is now %smuted\n", ctx->qtv->silentstream?"":"un");
|
||||
}
|
||||
else
|
||||
Cmd_Printf(ctx, "Stream is currently %smuted\n", ctx->qtv->silentstream?"un":"");
|
||||
Cmd_Printf(ctx, "Stream is currently %smuted\n", ctx->qtv->silentstream?"":"un");
|
||||
}
|
||||
|
||||
#ifdef VIEWER
|
||||
|
@ -1028,71 +1082,91 @@ void Cmd_Watch(cmdctxt_t *ctx)
|
|||
#endif
|
||||
|
||||
|
||||
void Cmd_Commands(cmdctxt_t *ctx)
|
||||
{
|
||||
Cmd_Printf(ctx, "fixme\n");
|
||||
}
|
||||
|
||||
typedef struct rconcommands_s {
|
||||
char *name;
|
||||
qboolean serverspecific; //works within a qtv context
|
||||
qboolean clusterspecific; //works without a qtv context (ignores context)
|
||||
consolecommand_t func;
|
||||
char *description;
|
||||
} rconcommands_t;
|
||||
|
||||
extern const rconcommands_t rconcommands[];
|
||||
|
||||
void Cmd_Commands(cmdctxt_t *ctx)
|
||||
{
|
||||
rconcommands_t *cmd;
|
||||
consolecommand_t lastfunc = NULL;
|
||||
|
||||
Cmd_Printf(ctx, "Commands:\n");
|
||||
for (cmd = rconcommands; cmd->name; cmd = cmd++)
|
||||
{
|
||||
if (cmd->func == lastfunc)
|
||||
continue; //no spamming alternative command names
|
||||
|
||||
Cmd_Printf(ctx, "%s: %s\n", cmd->name, cmd->description?cmd->description:"no description available");
|
||||
lastfunc = cmd->func;
|
||||
}
|
||||
}
|
||||
|
||||
const rconcommands_t rconcommands[] =
|
||||
{
|
||||
{"exec", 1, 1, Cmd_Exec},
|
||||
{"status", 1, 1, Cmd_Status},
|
||||
{"say", 1, 1, Cmd_Say},
|
||||
{"exec", 1, 1, Cmd_Exec, "executes a config file"},
|
||||
{"status", 1, 1, Cmd_Status, "prints proxy/stream status" },
|
||||
{"say", 1, 1, Cmd_Say, "says to a stream"},
|
||||
|
||||
{"help", 0, 1, Cmd_Help},
|
||||
{"commands", 0, 1, Cmd_Commands},
|
||||
{"hostname", 0, 1, Cmd_Hostname},
|
||||
{"master", 0, 1, Cmd_Master},
|
||||
{"udpport", 0, 1, Cmd_UDPPort},
|
||||
{"help", 0, 1, Cmd_Help, "shows the brief intro help text"},
|
||||
{"commands", 0, 1, Cmd_Commands, "prints the list of commands"},
|
||||
{"hostname", 0, 1, Cmd_Hostname, "changes the hostname seen in server browsers"},
|
||||
{"master", 0, 1, Cmd_Master, "specifies which master server to use"},
|
||||
{"udpport", 0, 1, Cmd_UDPPort, "specifies to listen on a provided udp port for regular qw clients"},
|
||||
{"port", 0, 1, Cmd_UDPPort},
|
||||
{"adminpassword",0, 1, Cmd_AdminPassword},
|
||||
{"rconpassword",0, 1, Cmd_AdminPassword},
|
||||
{"qtvlist", 0, 1, Cmd_QTVList},
|
||||
{"qtvdemolist", 0, 1, Cmd_QTVDemoList},
|
||||
{"qtv", 0, 1, Cmd_QTVConnect},
|
||||
{"addserver", 0, 1, Cmd_QTVConnect},
|
||||
{"connect", 0, 1, Cmd_QTVConnect},
|
||||
{"qw", 0, 1, Cmd_QWConnect},
|
||||
{"observe", 0, 1, Cmd_QWConnect},
|
||||
{"demos", 0, 1, Cmd_DemoList},
|
||||
{"demo", 0, 1, Cmd_MVDConnect},
|
||||
{"playdemo", 0, 1, Cmd_MVDConnect},
|
||||
{"choke", 0, 1, Cmd_Choke},
|
||||
{"late", 0, 1, Cmd_Late},
|
||||
{"talking", 0, 1, Cmd_Talking},
|
||||
{"nobsp", 0, 1, Cmd_NoBSP},
|
||||
{"userconnects", 0, 1, Cmd_UserConnects},
|
||||
{"maxviewers", 0, 1, Cmd_MaxViewers},
|
||||
{"maxproxies", 0, 1, Cmd_MaxProxies},
|
||||
{"demodir", 0, 1, Cmd_DemoDir},
|
||||
{"basedir", 0, 1, Cmd_BaseDir},
|
||||
{"ping", 0, 1, Cmd_Ping},
|
||||
{"reconnect", 0, 1, Cmd_Reconnect},
|
||||
{"echo", 0, 1, Cmd_Echo},
|
||||
{"quit", 0, 1, Cmd_Quit},
|
||||
{"adminpassword", 0, 1, Cmd_AdminPassword,"specifies the password for qtv administrators"},
|
||||
{"rconpassword", 0, 1, Cmd_AdminPassword},
|
||||
{"qtvlist", 0, 1, Cmd_QTVList, "queries a seperate proxy for a list of available streams"},
|
||||
{"qtvdemolist", 0, 1, Cmd_QTVDemoList, "queries a seperate proxy for a list of available demos"},
|
||||
{"qtv", 0, 1, Cmd_QTVConnect, "adds a new tcp/qtv stream"},
|
||||
{"addserver", 0, 1, Cmd_QTVConnect},
|
||||
{"connect", 0, 1, Cmd_QTVConnect},
|
||||
{"qw", 0, 1, Cmd_QWConnect, "adds a new udp/qw stream"},
|
||||
{"observe", 0, 1, Cmd_QWConnect},
|
||||
{"demos", 0, 1, Cmd_DemoList, "shows the list of demos available on this proxy"},
|
||||
{"demo", 0, 1, Cmd_MVDConnect, "adds a demo as a new stream"},
|
||||
{"playdemo", 0, 1, Cmd_MVDConnect},
|
||||
{"choke", 0, 1, Cmd_Choke, "chokes packets to the data rate in the stream, disables proxy-side interpolation"},
|
||||
{"late", 0, 1, Cmd_Late, "enforces a time delay on packets sent through this proxy"},
|
||||
{"talking", 0, 1, Cmd_Talking, "permits viewers to talk to each other"},
|
||||
{"nobsp", 0, 1, Cmd_NoBSP, "disables loading of bsp files"},
|
||||
{"userconnects", 0, 1, Cmd_UserConnects, "prevents users from creating thier own streams"},
|
||||
{"maxviewers", 0, 1, Cmd_MaxViewers, "sets a limit on udp/qw client connections"},
|
||||
{"maxproxies", 0, 1, Cmd_MaxProxies, "sets a limit on tcp/qtv client connections"},
|
||||
{"demodir", 0, 1, Cmd_DemoDir, "specifies where to get the demo list from"},
|
||||
{"basedir", 0, 1, Cmd_BaseDir, "specifies where to get any files required by the game. this is prefixed to the server-specified game dir."},
|
||||
{"ping", 0, 1, Cmd_Ping, "sends a udp ping to a qtv proxy or server"},
|
||||
{"reconnect", 0, 1, Cmd_Reconnect, "forces a stream to reconnect to its server (restarts demos)"},
|
||||
{"echo", 0, 1, Cmd_Echo, "a useless command that echos a string"},
|
||||
{"quit", 0, 1, Cmd_Quit, "closes the qtv"},
|
||||
{"exit", 0, 1, Cmd_Quit},
|
||||
{"streams", 0, 1, Cmd_Streams},
|
||||
{"allownq", 0, 1, Cmd_AllowNQ},
|
||||
{"streams", 0, 1, Cmd_Streams, "shows a list of active streams"},
|
||||
{"allownq", 0, 1, Cmd_AllowNQ, "permits nq clients to connect. This can be disabled as this code is less tested than the rest"},
|
||||
|
||||
|
||||
|
||||
{"halt", 1, 0, Cmd_Halt, "disables a stream, preventing it from reconnecting until someone tries watching it anew"},
|
||||
{"disable", 1, 0, Cmd_Halt},
|
||||
{"resume", 1, 0, Cmd_Resume, "reactivates a stream, allowing it to reconnect"},
|
||||
{"enable", 1, 0, Cmd_Resume},
|
||||
{"mute", 1, 0, Cmd_MuteStream, "hides prints that come from the game server"},
|
||||
{"mutestream", 1, 0, Cmd_MuteStream},
|
||||
{"disconnect", 1, 0, Cmd_Disconnect},
|
||||
{"record", 1, 0, Cmd_Record},
|
||||
{"stop", 1, 0, Cmd_Stop},
|
||||
{"demospeed", 1, 0, Cmd_DemoSpeed},
|
||||
{"tcpport", 0, 1, Cmd_MVDPort},
|
||||
{"mvdport", 0, 1, Cmd_MVDPort},
|
||||
{"disconnect", 1, 0, Cmd_Disconnect, "fully closes a stream"},
|
||||
{"record", 1, 0, Cmd_Record, "records a stream to a demo"},
|
||||
{"stop", 1, 0, Cmd_Stop, "stops recording of a demo"},
|
||||
{"demospeed", 1, 0, Cmd_DemoSpeed, "changes the rate the demo is played at"},
|
||||
{"tcpport", 0, 1, Cmd_MVDPort, "specifies which port to listen on for tcp/qtv connections"},
|
||||
{"mvdport", 0, 1, Cmd_MVDPort},
|
||||
|
||||
#ifdef VIEWER
|
||||
{"watch", 1, 0, Cmd_Watch},
|
||||
{"watch", 1, 0, Cmd_Watch, "specifies to watch that stream in the built-in viewer"},
|
||||
#endif
|
||||
|
||||
{NULL}
|
||||
|
@ -1137,6 +1211,7 @@ void Cmd_ExecuteNow(cmdctxt_t *ctx, char *command)
|
|||
|
||||
cmdname = Cmd_Argv(ctx, 0);
|
||||
|
||||
//if there's only one stream, set that as the selected stream
|
||||
if (!ctx->qtv && ctx->cluster->numservers==1)
|
||||
ctx->qtv = ctx->cluster->servers;
|
||||
|
||||
|
|
201
fteqtv/source.c
201
fteqtv/source.c
|
@ -364,7 +364,7 @@ void Net_SendQTVConnectionRequest(sv_t *qtv, char *authmethod, char *challenge)
|
|||
}
|
||||
else
|
||||
{
|
||||
qtv->drop = true;
|
||||
qtv->errored = ERR_PERMANENT;
|
||||
qtv->upstreambuffersize = 0;
|
||||
Sys_Printf(qtv->cluster, "Auth method %s was not usable\n", authmethod);
|
||||
return;
|
||||
|
@ -424,6 +424,11 @@ qboolean Net_ConnectToTCPServer(sv_t *qtv, char *ip)
|
|||
}
|
||||
}
|
||||
|
||||
//make sure the buffers are empty. we could have disconnected prematurly
|
||||
qtv->upstreambuffersize = 0;
|
||||
qtv->buffersize = 0;
|
||||
qtv->forwardpoint = 0;
|
||||
|
||||
//read the notes at the start of this file for what these text strings mean
|
||||
Net_SendQTVConnectionRequest(qtv, NULL, NULL);
|
||||
return true;
|
||||
|
@ -615,7 +620,7 @@ void Net_QueueUpstream(sv_t *qtv, int size, char *buffer)
|
|||
if (qtv->upstreambuffersize + size > sizeof(qtv->upstreambuffer))
|
||||
{
|
||||
Sys_Printf(qtv->cluster, "Stream %i: Upstream queue overflowed for %s\n", qtv->streamid, qtv->server);
|
||||
qtv->drop = true;
|
||||
qtv->errored = ERR_RECONNECT;
|
||||
return;
|
||||
}
|
||||
memcpy(qtv->upstreambuffer + qtv->upstreambuffersize, buffer, size);
|
||||
|
@ -642,7 +647,7 @@ qboolean Net_WriteUpstream(sv_t *qtv)
|
|||
Sys_Printf(qtv->cluster, "Stream %i: Error: source socket error %i\n", qtv->streamid, qerrno);
|
||||
else
|
||||
Sys_Printf(qtv->cluster, "Stream %i: Error: server %s disconnected\n", qtv->streamid, qtv->server);
|
||||
qtv->drop = true;
|
||||
qtv->errored = ERR_RECONNECT; //if the server is down, we'll detect it on reconnect
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -1034,16 +1039,32 @@ qboolean QTV_Connect(sv_t *qtv, char *serverurl)
|
|||
return true;
|
||||
}
|
||||
|
||||
void QTV_Shutdown(sv_t *qtv)
|
||||
{
|
||||
oproxy_t *prox;
|
||||
oproxy_t *old;
|
||||
void QTV_Cleanup(sv_t *qtv, qboolean leaveadmins)
|
||||
{ //disconnects the stream
|
||||
viewer_t *v;
|
||||
sv_t *peer;
|
||||
cluster_t *cluster;
|
||||
int i;
|
||||
Sys_Printf(qtv->cluster, "Stream %i: Closing source %s\n", qtv->streamid, qtv->server);
|
||||
oproxy_t *prox;
|
||||
oproxy_t *old;
|
||||
|
||||
cluster = qtv->cluster;
|
||||
|
||||
//set connected viewers to a different stream
|
||||
if (cluster->viewserver == qtv)
|
||||
cluster->viewserver = NULL;
|
||||
for (v = cluster->viewers; v; v = v->next)
|
||||
{
|
||||
#warning fixme: honour leaveadmins
|
||||
if (v->server == qtv)
|
||||
{ //they were watching this one
|
||||
QW_SetViewersServer(qtv->cluster, v, NULL);
|
||||
QW_SetMenu(v, MENU_NONE);
|
||||
QTV_SayCommand(cluster, v->server, v, "menu");
|
||||
QW_PrintfToViewer(v, "Stream %s is closing\n", qtv->server);
|
||||
}
|
||||
}
|
||||
|
||||
// close the source handle
|
||||
if (qtv->sourcesock != INVALID_SOCKET)
|
||||
{
|
||||
if (qtv->usequakeworldprotocols)
|
||||
|
@ -1061,17 +1082,57 @@ void QTV_Shutdown(sv_t *qtv)
|
|||
fclose(qtv->sourcefile);
|
||||
qtv->sourcefile = NULL;
|
||||
}
|
||||
|
||||
//cancel downloads
|
||||
if (qtv->downloadfile)
|
||||
{
|
||||
fclose(qtv->downloadfile);
|
||||
qtv->downloadfile = NULL;
|
||||
unlink(qtv->downloadname);
|
||||
*qtv->downloadname = '\0';
|
||||
}
|
||||
// if (qtv->tcpsocket != INVALID_SOCKET)
|
||||
// closesocket(qtv->tcpsocket);
|
||||
//free the bsp
|
||||
BSP_Free(qtv->bsp);
|
||||
qtv->bsp = NULL;
|
||||
|
||||
//clean up entity state
|
||||
for (i = 0; i < ENTITY_FRAMES; i++)
|
||||
{
|
||||
if (qtv->frame[i].ents)
|
||||
{
|
||||
free(qtv->frame[i].ents);
|
||||
qtv->frame[i].ents = NULL;
|
||||
}
|
||||
if (qtv->frame[i].entnums)
|
||||
{
|
||||
free(qtv->frame[i].entnums);
|
||||
qtv->frame[i].entnums = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
//boot connected downstream proxies
|
||||
for (prox = qtv->proxies; prox; )
|
||||
{
|
||||
if (prox->file)
|
||||
fclose(prox->file);
|
||||
if (prox->sock != INVALID_SOCKET)
|
||||
closesocket(prox->sock);
|
||||
old = prox;
|
||||
prox = prox->next;
|
||||
free(old);
|
||||
cluster->numproxies--;
|
||||
}
|
||||
}
|
||||
|
||||
void QTV_Shutdown(sv_t *qtv)
|
||||
{
|
||||
sv_t *peer;
|
||||
cluster_t *cluster;
|
||||
Sys_Printf(qtv->cluster, "Stream %i: Closing source %s\n", qtv->streamid, qtv->server);
|
||||
|
||||
QTV_Cleanup(qtv, false);
|
||||
|
||||
//unlink it
|
||||
cluster = qtv->cluster;
|
||||
if (cluster->servers == qtv)
|
||||
cluster->servers = qtv->next;
|
||||
|
@ -1087,41 +1148,6 @@ void QTV_Shutdown(sv_t *qtv)
|
|||
}
|
||||
}
|
||||
|
||||
if (cluster->viewserver == qtv)
|
||||
cluster->viewserver = NULL;
|
||||
|
||||
for (v = cluster->viewers; v; v = v->next)
|
||||
{
|
||||
if (v->server == qtv)
|
||||
{
|
||||
QW_SetViewersServer(qtv->cluster, v, NULL);
|
||||
QW_SetMenu(v, MENU_NONE);
|
||||
QTV_SayCommand(cluster, v->server, v, "menu");
|
||||
QW_PrintfToViewer(v, "Stream %s is closing\n", qtv->server);
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < ENTITY_FRAMES; i++)
|
||||
{
|
||||
if (qtv->frame[i].ents)
|
||||
free(qtv->frame[i].ents);
|
||||
if (qtv->frame[i].entnums)
|
||||
free(qtv->frame[i].entnums);
|
||||
}
|
||||
|
||||
for (prox = qtv->proxies; prox; )
|
||||
{
|
||||
if (prox->file)
|
||||
fclose(prox->file);
|
||||
if (prox->sock != INVALID_SOCKET)
|
||||
closesocket(prox->sock);
|
||||
old = prox;
|
||||
prox = prox->next;
|
||||
free(old);
|
||||
cluster->numproxies--;
|
||||
}
|
||||
|
||||
|
||||
free(qtv);
|
||||
cluster->numservers--;
|
||||
}
|
||||
|
@ -1457,15 +1483,32 @@ void QTV_Run(sv_t *qtv)
|
|||
if (qtv->disconnectwhennooneiswatching == 1 && qtv->numviewers == 0 && qtv->proxies == NULL)
|
||||
{
|
||||
Sys_Printf(qtv->cluster, "Stream %i: %s became inactive\n", qtv->streamid, qtv->server);
|
||||
qtv->drop = true;
|
||||
qtv->errored = ERR_DROP;
|
||||
}
|
||||
if (qtv->drop)
|
||||
if (qtv->errored)
|
||||
{
|
||||
QTV_Shutdown(qtv);
|
||||
return;
|
||||
if (qtv->errored == ERR_DISABLED)
|
||||
{
|
||||
//this keeps any connected proxies ticking over.
|
||||
//probably we should drop them instead - the connection will only be revived if one of them reconnects.
|
||||
SV_ForwardStream(qtv, NULL, 0);
|
||||
return;
|
||||
}
|
||||
else if (qtv->errored == ERR_PERMANENT)
|
||||
{
|
||||
QTV_Cleanup(qtv, false); //frees various pieces of context
|
||||
qtv->errored = ERR_DISABLED;
|
||||
return;
|
||||
}
|
||||
else if (qtv->errored == ERR_DROP)
|
||||
{
|
||||
QTV_Shutdown(qtv); //destroys the stream
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//we will read out as many packets as we can until we're up to date
|
||||
//note: this can cause real issues when we're overloaded for any length of time
|
||||
//each new packet comes with a leading msec byte (msecs from last packet)
|
||||
|
@ -1488,6 +1531,11 @@ void QTV_Run(sv_t *qtv)
|
|||
}
|
||||
|
||||
|
||||
if (qtv->errored == ERR_RECONNECT)
|
||||
{
|
||||
qtv->errored = ERR_NONE;
|
||||
qtv->nextconnectattempt = qtv->curtime; //make the reconnect happen _now_
|
||||
}
|
||||
|
||||
|
||||
if (qtv->sourcetype == SRC_UDP)
|
||||
|
@ -1499,10 +1547,26 @@ void QTV_Run(sv_t *qtv)
|
|||
|
||||
if (!qtv->isconnected && (qtv->curtime >= qtv->nextconnectattempt || qtv->curtime < qtv->nextconnectattempt - UDPRECONNECT_TIME*2))
|
||||
{
|
||||
strcpy(qtv->status, "Attemping challenge\n");
|
||||
Netchan_OutOfBand(qtv->cluster, qtv->sourcesock, qtv->serveraddress, 13, "getchallenge\n");
|
||||
if (qtv->errored == ERR_DISABLED)
|
||||
{
|
||||
strcpy(qtv->status, "Given up connecting\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
strcpy(qtv->status, "Attemping challenge\n");
|
||||
if (qtv->sourcesock == INVALID_SOCKET && !qtv->sourcefile)
|
||||
{
|
||||
if (!QTV_Connect(qtv, qtv->server)) //reconnect it
|
||||
qtv->errored = ERR_PERMANENT;
|
||||
}
|
||||
if (qtv->errored == ERR_NONE)
|
||||
Netchan_OutOfBand(qtv->cluster, qtv->sourcesock, qtv->serveraddress, 13, "getchallenge\n");
|
||||
}
|
||||
qtv->nextconnectattempt = qtv->curtime + UDPRECONNECT_TIME;
|
||||
}
|
||||
if (qtv->sourcesock == INVALID_SOCKET && !qtv->sourcefile)
|
||||
return;
|
||||
|
||||
QTV_ParseQWStream(qtv);
|
||||
|
||||
if (qtv->isconnected)
|
||||
|
@ -1637,15 +1701,19 @@ void QTV_Run(sv_t *qtv)
|
|||
|
||||
if (qtv->sourcesock == INVALID_SOCKET && !qtv->sourcefile)
|
||||
{
|
||||
if (qtv->errored == ERR_DISABLED)
|
||||
return;
|
||||
|
||||
if (qtv->curtime >= qtv->nextconnectattempt || qtv->curtime < qtv->nextconnectattempt - RECONNECT_TIME*2)
|
||||
{
|
||||
if (qtv->disconnectwhennooneiswatching == 2)
|
||||
if (qtv->disconnectwhennooneiswatching == 2) //2 means a reverse connection
|
||||
{
|
||||
qtv->drop = true;
|
||||
qtv->errored = ERR_DROP;
|
||||
return;
|
||||
}
|
||||
if (!QTV_Connect(qtv, qtv->server))
|
||||
if (!QTV_Connect(qtv, qtv->server)) //reconnect it
|
||||
{
|
||||
qtv->errored = ERR_PERMANENT;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -1692,7 +1760,7 @@ void QTV_Run(sv_t *qtv)
|
|||
{
|
||||
Sys_Printf(qtv->cluster, "Stream %i: Server is not a QTV server (or is incompatible)\n", qtv->streamid);
|
||||
//printf("%i, %s\n", qtv->buffersize, qtv->buffer);
|
||||
qtv->drop = true;
|
||||
qtv->errored = ERR_PERMANENT;
|
||||
return;
|
||||
}
|
||||
if (length < 6)
|
||||
|
@ -1712,7 +1780,7 @@ void QTV_Run(sv_t *qtv)
|
|||
if ((int)svversion != 1)
|
||||
{
|
||||
Sys_Printf(qtv->cluster, "Stream %i: QTV server doesn't support a compatible protocol version (returned %i)\n", qtv->streamid, atoi((char*)qtv->buffer + 6));
|
||||
qtv->drop = true;
|
||||
qtv->errored = ERR_PERMANENT;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1752,13 +1820,13 @@ void QTV_Run(sv_t *qtv)
|
|||
else if (!strcmp(start, "COMPRESSION"))
|
||||
{ //we don't support compression, we didn't ask for it.
|
||||
Sys_Printf(qtv->cluster, "Stream %i: QTV server wrongly used compression\n", qtv->streamid);
|
||||
qtv->drop = true;
|
||||
qtv->errored = ERR_PERMANENT;
|
||||
return;
|
||||
}
|
||||
else if (!strcmp(start, "PERROR"))
|
||||
{
|
||||
Sys_Printf(qtv->cluster, "\nStream %i: Server PERROR from %s: %s\n\n", qtv->streamid, qtv->server, colon);
|
||||
qtv->drop = true;
|
||||
qtv->errored = ERR_PERMANENT;
|
||||
qtv->buffersize = 0;
|
||||
qtv->forwardpoint = 0;
|
||||
return;
|
||||
|
@ -1770,7 +1838,7 @@ void QTV_Run(sv_t *qtv)
|
|||
qtv->forwardpoint = 0;
|
||||
|
||||
if (qtv->disconnectwhennooneiswatching)
|
||||
qtv->drop = true; //if its a user registered stream, drop it immediatly
|
||||
qtv->errored = ERR_DROP; //if its a user registered stream, drop it immediatly
|
||||
else
|
||||
{ //otherwise close the socket (this will result in a timeout and reconnect)
|
||||
if (qtv->sourcesock != INVALID_SOCKET)
|
||||
|
@ -1823,7 +1891,7 @@ void QTV_Run(sv_t *qtv)
|
|||
if (qtv->serverquery)
|
||||
{
|
||||
Sys_Printf(qtv->cluster, "End of list\n");
|
||||
qtv->drop = true;
|
||||
qtv->errored = ERR_DROP;
|
||||
qtv->buffersize = 0;
|
||||
qtv->forwardpoint = 0;
|
||||
return;
|
||||
|
@ -1836,7 +1904,7 @@ void QTV_Run(sv_t *qtv)
|
|||
else if (qtv->parsingqtvheader)
|
||||
{
|
||||
Sys_Printf(qtv->cluster, "Stream %i: QTV server sent no begin command - assuming incompatible\n\n", qtv->streamid);
|
||||
qtv->drop = true;
|
||||
qtv->errored = ERR_PERMANENT;
|
||||
qtv->buffersize = 0;
|
||||
qtv->forwardpoint = 0;
|
||||
return;
|
||||
|
@ -2008,7 +2076,15 @@ sv_t *QTV_NewServerConnection(cluster_t *cluster, char *server, char *password,
|
|||
for (qtv = cluster->servers; qtv; qtv = qtv->next)
|
||||
{
|
||||
if (!strcmp(qtv->server, server))
|
||||
{ //if the stream detected some permanent/config error, try reconnecting again (of course this only happens when someone tries using the stream)
|
||||
#warning review this logic
|
||||
if (qtv->errored == ERR_DISABLED)
|
||||
{
|
||||
if (!(!QTV_Connect(qtv, server) && !force)) //try and wake it up
|
||||
qtv->errored = ERR_NONE;
|
||||
}
|
||||
return qtv;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (autoclose)
|
||||
|
@ -2043,6 +2119,7 @@ sv_t *QTV_NewServerConnection(cluster_t *cluster, char *server, char *password,
|
|||
{
|
||||
if (!QTV_Connect(qtv, server) && !force)
|
||||
{
|
||||
QTV_Cleanup(qtv, false);
|
||||
free(qtv);
|
||||
return NULL;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue