diff --git a/fteqtv/control.c b/fteqtv/control.c
index 5f0ccde88..2dc70cac6 100644
--- a/fteqtv/control.c
+++ b/fteqtv/control.c
@@ -165,8 +165,10 @@ unsigned char *FS_ReadFile(char *gamedir, char *filename, unsigned int *size)
return data;
}
-
-int SortFilesByDate(const void *a, const void *b)
+#ifndef _WIN32
+#define _cdecl
+#endif
+int _cdecl SortFilesByDate(const void *a, const void *b)
{
if (((availdemo_t*)a)->time < ((availdemo_t*)b)->time)
return 1;
@@ -483,6 +485,7 @@ void DoCommandLine(cluster_t *cluster, int argc, char **argv)
}
}
+#ifndef LIBQTV
int main(int argc, char **argv)
{
cluster_t *cluster;
@@ -569,6 +572,7 @@ int main(int argc, char **argv)
return 0;
}
+#endif
void QTV_Printf(sv_t *qtv, char *fmt, ...)
{
@@ -586,6 +590,13 @@ void QTV_Printf(sv_t *qtv, char *fmt, ...)
Sys_Printf(qtv->cluster, "%s", string);
}
+//#ifdef LIBQTV
+//#ifndef _WIN32
+//#define _cdecl
+//#endif
+//void _cdecl Con_Printf(char *fmt, ...);
+//#endif
+
void Sys_Printf(cluster_t *cluster, char *fmt, ...)
{
va_list argptr;
@@ -597,6 +608,10 @@ void Sys_Printf(cluster_t *cluster, char *fmt, ...)
string[sizeof(string)-1] = 0;
va_end (argptr);
+//#ifdef LIBQTV
+// Con_Printf("QTV: %s", string);
+//#endif
+
for (t = (unsigned char*)string; *t; t++)
{
if (*t >= 146 && *t < 156)
diff --git a/fteqtv/dotnet2005/libqtv.vcproj b/fteqtv/dotnet2005/libqtv.vcproj
new file mode 100644
index 000000000..a07f0e94e
--- /dev/null
+++ b/fteqtv/dotnet2005/libqtv.vcproj
@@ -0,0 +1,282 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/fteqtv/dotnet2005/qtvprox.sln b/fteqtv/dotnet2005/qtvprox.sln
index d24f85e11..5aff43b80 100644
--- a/fteqtv/dotnet2005/qtvprox.sln
+++ b/fteqtv/dotnet2005/qtvprox.sln
@@ -3,6 +3,8 @@ Microsoft Visual Studio Solution File, Format Version 9.00
# Visual Studio 2005
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qtvprox", "qtvprox.vcproj", "{62669E6C-7E18-4E4D-BA54-DFBE29E7D24E}"
EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libqtv", "libqtv.vcproj", "{EDBDDC82-6DEE-4BF1-B0BC-BBBCCFE65D4C}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
@@ -13,6 +15,10 @@ Global
{62669E6C-7E18-4E4D-BA54-DFBE29E7D24E}.Debug|Win32.Build.0 = Debug|Win32
{62669E6C-7E18-4E4D-BA54-DFBE29E7D24E}.Release|Win32.ActiveCfg = Release|Win32
{62669E6C-7E18-4E4D-BA54-DFBE29E7D24E}.Release|Win32.Build.0 = Release|Win32
+ {EDBDDC82-6DEE-4BF1-B0BC-BBBCCFE65D4C}.Debug|Win32.ActiveCfg = Debug|Win32
+ {EDBDDC82-6DEE-4BF1-B0BC-BBBCCFE65D4C}.Debug|Win32.Build.0 = Debug|Win32
+ {EDBDDC82-6DEE-4BF1-B0BC-BBBCCFE65D4C}.Release|Win32.ActiveCfg = Release|Win32
+ {EDBDDC82-6DEE-4BF1-B0BC-BBBCCFE65D4C}.Release|Win32.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/fteqtv/menu.c b/fteqtv/menu.c
index f42101843..303ddc187 100644
--- a/fteqtv/menu.c
+++ b/fteqtv/menu.c
@@ -95,7 +95,7 @@ void Menu_Enter(cluster_t *cluster, viewer_t *viewer, int buttonnum)
}
if (i++ == viewer->menuop)
{ //disconnect
- QTV_Shutdown(viewer->server);
+ QTV_ShutdownStream(viewer->server);
}
if (i++ == viewer->menuop)
{
diff --git a/fteqtv/netchan.c b/fteqtv/netchan.c
index 8e67a320a..eb01e67f3 100644
--- a/fteqtv/netchan.c
+++ b/fteqtv/netchan.c
@@ -122,11 +122,21 @@ SOCKET NET_ChooseSocket(SOCKET sock[2], netadr_t *adr)
return sock[0];
}
+#ifdef LIBQTV
+void QTV_DoReceive(void *data, int length);
+#endif
void NET_SendPacket(cluster_t *cluster, SOCKET sock, int length, void *data, netadr_t adr)
{
int ret;
int alen;
+#ifdef LIBQTV
+ if (((struct sockaddr *)&adr.sockaddr)->sa_family == AF_UNSPEC)
+ {
+ QTV_DoReceive(data, length);
+ return;
+ }
+#endif
#ifdef AF_INET6
if (((struct sockaddr *)&adr.sockaddr)->sa_family == AF_INET6)
alen = sizeof(struct sockaddr_in6);
@@ -403,7 +413,7 @@ void Netchan_Setup (SOCKET sock, netchan_t *chan, netadr_t adr, int qport, qbool
chan->message.allowoverflow = true;
- chan->rate = 1000.0f/2500;
+ chan->rate = 10000*1000;
}
@@ -485,11 +495,11 @@ void Netchan_Transmit (cluster_t *cluster, netchan_t *chan, int length, const vo
i = MAX_NQDATAGRAM;
WriteData (&send, chan->reliable_buf+chan->reliable_start, i);
- if (length && send.cursize + length < send.maxsize)
- { //throw the unreliable packet into the same one as the reliable (but not sent reliably)
- WriteData (&send, data, length);
- length = 0;
- }
+// if (length && send.cursize + length < send.maxsize)
+// { //throw the unreliable packet into the same one as the reliable (but not sent reliably)
+// WriteData (&send, data, length);
+// length = 0;
+// }
if (chan->reliable_start+i == chan->reliable_length)
@@ -497,12 +507,18 @@ void Netchan_Transmit (cluster_t *cluster, netchan_t *chan, int length, const vo
else
*(int*)send_buf = BigLong(NETFLAG_DATA | send.cursize);
NET_SendPacket(cluster, chan->sock, send.cursize, send.data, chan->remote_address);
+ send.cursize = 0;
if (chan->cleartime < curtime)
chan->cleartime = curtime + (int)(send.cursize*chan->rate);
else
chan->cleartime += (int)(send.cursize*chan->rate);
}
+// else if (!length)
+// {
+// length = 1;
+// data = "\x01";
+// }
//send out the unreliable (if still unsent)
if (length)
diff --git a/fteqtv/nq_api.c b/fteqtv/nq_api.c
new file mode 100644
index 000000000..4d75307b0
--- /dev/null
+++ b/fteqtv/nq_api.c
@@ -0,0 +1,230 @@
+/*
+This file is intended as a set of exports for an NQ-based engine.
+This is supported _purely_ for clients, and will not work for servers.
+
+
+
+[EndUser] how to use:
+to join a qw server: connect "udp:127.0.0.1:27500"
+to watch a qtv stream: connect "tcp:3@127.0.0.1:27599" (where '3' is the streamid)
+to watch an mvd demo: connect "demo:blahblah.mvd" - the demo will be loaded from $WORKINGDIR/qw/demos/ - note that $WORKINGDIR is NOT always the same dir
+ as your engine is running from. The -basedir argument will break it, or engines that hunt down a 'proper' installation of quake instead.
+
+
+[Developer] how to incorporate into an nq engine:
+load up net_win.c
+find the #include "net_wins.h" line.
+dupe it, call it net_qtv.h
+dupe the header itself too, changing all WINS_foo to QTV_foo.
+find the net_landrivers array. Dupe the first block, then edit the first block to be all QTV_foo functions.
+bump net_numlandrivers.
+For non-window operating systems, you'll need to do the same, just figure out which net_win.c equivelent function it uses first. :P
+certain engines may do weird things with the port. probably its best to just use Cmd_Args() for the connect command instead of Cmd_Argv(1), and to add
+ port parsing to XXXX_GetAddrFromName instead of messing around with port cvars etc and ruining server configs.
+ If your engine already has weird port behaviour, then its entirely your problem to fix. :P
+You probably want to tweak your menus a little to clean up the nq/qw/qtv connection distinctions.
+If you do want to make changes to libqtv, please consider joining the FTE team (or at least the irc channel) in order to contribute without forking.
+
+[Developer] how to compile libqtv:
+cflags MUST define 'LIBQTV' or it won't compile properly.
+The relevent exports are all tagged as 'EXPORT void PUBLIC fname(...)' (dllexport+cdecl in windows), feel free to define those properly if you're making a linux shared object without exporting all (potentially conflicting) internals.
+This means you can compile it as a dll without any issues, one with a standardized interface. Any libqtv-specific bugfixes can be released independantly from engine(s).
+Compiling a dll with msvc will generally automatically produce a .lib which you can directly link against. Alternatively, include both projects in your workspace and set up dependancies properly and it'll be automatically imported.
+
+[PowerUser] issues:
+its a full qtv proxy, but you can't get admin/rcon access to it.
+it doesn't read any configs, and has no console, thus you cannot set an rcon password/port.
+without console/rcon, you cannot enable any listening ports for other users.
+if you need a public qtv proxy, use a standalone version.
+*/
+
+
+
+#include "qtv.h"
+int build_number(void);
+
+static cluster_t *cluster;
+
+//note that a qsockaddr is only 16 bytes.
+//this is not enough for ipv6 etc.
+struct qsockaddr
+{
+ int ipid;
+};
+char resolvedadrstring[128];
+int lastadrid;
+
+#ifdef _WIN32
+#define EXPORT __declspec(dllexport)
+#define PUBLIC __cdecl
+#endif
+#ifndef EXPORT
+#define EXPORT
+#endif
+#ifndef PUBLIC
+#define PUBLIC
+#endif
+
+EXPORT int PUBLIC QTV_Init (void)
+{
+ cluster = malloc(sizeof(*cluster));
+ if (cluster)
+ {
+ memset(cluster, 0, sizeof(*cluster));
+
+ cluster->qwdsocket[0] = INVALID_SOCKET;
+ cluster->qwdsocket[1] = INVALID_SOCKET;
+ cluster->tcpsocket[0] = INVALID_SOCKET;
+ cluster->tcpsocket[1] = INVALID_SOCKET;
+ cluster->anticheattime = 1*1000;
+ cluster->tooslowdelay = 100;
+ cluster->qwlistenportnum = 0;
+ cluster->allownqclients = true;
+ strcpy(cluster->hostname, DEFAULT_HOSTNAME);
+ cluster->buildnumber = build_number();
+ cluster->maxproxies = -1;
+
+ strcpy(cluster->demodir, "qw/demos/");
+ return 0;
+ }
+
+ return -1;
+}
+EXPORT void PUBLIC QTV_Shutdown (void)
+{
+}
+EXPORT void PUBLIC QTV_Listen (qboolean state)
+{
+}
+EXPORT int PUBLIC QTV_OpenSocket (int port)
+{
+ return 0;
+}
+EXPORT int PUBLIC QTV_CloseSocket (int socket)
+{
+ //give it a chance to close any server connections from us disconnecting (should have already send disconnect message, but won't have run the server so not noticed the lack of viewers)
+ Cluster_Run(cluster, false);
+ return 0;
+}
+EXPORT int PUBLIC QTV_Connect (int socket, struct qsockaddr *addr)
+{
+ if (addr->ipid == lastadrid)
+ {
+ strlcpy(cluster->autojoinadr, resolvedadrstring, sizeof(cluster->autojoinadr));
+ return 0;
+ }
+ else
+ {
+ cluster->autojoinadr[0] = 0;
+ return -1;
+ }
+ return 0;
+}
+EXPORT int PUBLIC QTV_CheckNewConnections (void)
+{
+ return -1;
+}
+
+static byte pendingbuf[8][1032];
+static int pendinglen[8];
+static unsigned int pendingin, pendingout;
+void QTV_DoReceive(void *data, int length)
+{
+ int idx;
+ if (length > sizeof(pendingbuf[0]))
+ return;
+ idx = pendingout++;
+ idx &= 7;
+ memcpy(pendingbuf[idx], data, length);
+ pendinglen[idx] = length;
+}
+EXPORT int PUBLIC QTV_Read (int socket, byte *buf, int len, struct qsockaddr *addr)
+{
+ if (pendingout == pendingin)
+ {
+ Cluster_Run(cluster, false);
+ Cluster_Run(cluster, false);
+ }
+
+ while (pendingin != pendingout)
+ {
+ int idx = pendingin++;
+ idx &= 7;
+ if (pendinglen[idx] > len)
+ continue; //error
+ memcpy(buf, pendingbuf[idx], pendinglen[idx]);
+ return pendinglen[idx];
+ }
+ return 0;
+}
+EXPORT int PUBLIC QTV_Write (int socket, byte *buf, int len, struct qsockaddr *addr)
+{
+ netmsg_t m;
+ netadr_t from;
+ from.tcpcon = NULL;
+ ((struct sockaddr*)from.sockaddr)->sa_family = AF_UNSPEC;
+
+ m.cursize = len;
+ m.data = buf;
+ m.readpos = 0;
+
+ QW_ProcessUDPPacket(cluster, &m, from);
+
+ if (pendingout == pendingin)
+ Cluster_Run(cluster, false);
+
+ return 0;
+}
+EXPORT int PUBLIC QTV_Broadcast (int socket, byte *buf, int len)
+{
+ netmsg_t m;
+ netadr_t from;
+ from.tcpcon = NULL;
+ ((struct sockaddr*)from.sockaddr)->sa_family = AF_UNSPEC;
+
+ m.cursize = len;
+ m.data = buf;
+ m.readpos = 0;
+
+ QW_ProcessUDPPacket(cluster, &m, from);
+
+ return 0;
+}
+EXPORT char *PUBLIC QTV_AddrToString (struct qsockaddr *addr)
+{
+ return 0;
+}
+EXPORT int PUBLIC QTV_StringToAddr (char *string, struct qsockaddr *addr)
+{
+ if (!strncmp(string, "udp:", 4) || !strncmp(string, "tcp:", 4) || !strncmp(string, "file:", 5))
+ {
+ snprintf(resolvedadrstring, sizeof(resolvedadrstring), "%s", string);
+ addr->ipid = ++lastadrid;
+ return 0;
+ }
+ return -1;
+}
+EXPORT int PUBLIC QTV_GetSocketAddr (int socket, struct qsockaddr *addr)
+{
+ return 0;
+}
+EXPORT int PUBLIC QTV_GetNameFromAddr (struct qsockaddr *addr, char *name)
+{
+ return 0;
+}
+EXPORT int PUBLIC QTV_GetAddrFromName (char *name, struct qsockaddr *addr)
+{
+ return QTV_StringToAddr(name, addr);
+}
+EXPORT int PUBLIC QTV_AddrCompare (struct qsockaddr *addr1, struct qsockaddr *addr2)
+{
+ return 0;
+}
+EXPORT int PUBLIC QTV_GetSocketPort (struct qsockaddr *addr)
+{
+ return 0;
+}
+EXPORT int PUBLIC QTV_SetSocketPort (struct qsockaddr *addr, int port)
+{
+ return 0;
+}
diff --git a/fteqtv/parse.c b/fteqtv/parse.c
index eb72f66a6..26cbd76b5 100644
--- a/fteqtv/parse.c
+++ b/fteqtv/parse.c
@@ -127,6 +127,7 @@ static void ParseServerData(sv_t *tv, netmsg_t *m, int to, unsigned int playerma
return;
}
+ tv->mapstarttime = tv->parsetime;
tv->parsingconnectiondata = true;
tv->clservercount = ReadLong(m); //we don't care about server's servercount, it's all reliable data anyway.
@@ -169,7 +170,7 @@ static void ParseServerData(sv_t *tv, netmsg_t *m, int to, unsigned int playerma
v->thinksitsconnected = false;
}
- if (!tv->controller && tv->usequakeworldprotocols)
+ if ((!tv->controller || tv->controller->netchan.isnqprotocol) && tv->usequakeworldprotocols)
{
tv->netchan.message.cursize = 0; //mvdsv sucks
SendClientCommand(tv, "soundlist %i 0\n", tv->clservercount);
@@ -178,7 +179,10 @@ static void ParseServerData(sv_t *tv, netmsg_t *m, int to, unsigned int playerma
ConnectionData(tv, (void*)((char*)m->data+m->startpos), m->readpos - m->startpos, to, dem_read, QW);
if (tv->controller)
+ {
QW_ClearViewerState(tv->controller);
+ tv->controller->trackplayer = tv->map.thisplayer;
+ }
strcpy(tv->status, "Receiving soundlist\n");
}
@@ -273,14 +277,14 @@ static void ParseStufftext(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
for (v = tv->cluster->viewers; v; v = v->next)
{
- if (v->server == tv && v != tv->controller)
+ if (v->server == tv && (v != tv->controller || v->netchan.isnqprotocol))
{
v->servercount++;
SendBufferToViewer(v, newcmd, sizeof(newcmd), true);
}
}
- if (tv->controller)
+ if (tv->controller && !tv->controller->netchan.isnqprotocol)
SendBufferToViewer(tv->controller, (char*)m->data+m->startpos, m->readpos - m->startpos, true);
else if (tv->usequakeworldprotocols)
SendClientCommand(tv, "begin %i\n", tv->clservercount);
@@ -304,6 +308,19 @@ static void ParseStufftext(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
SendBufferToViewer(tv->controller, (char*)m->data+m->startpos, m->readpos - m->startpos, true);
return;
}
+ else if (!strncmp(text, "cmd prespawn ", 13))
+ {
+ if (tv->usequakeworldprotocols)
+ SendClientCommand(tv, "%s", text+4);
+ return; //commands the game server asked for are pointless.
+ }
+ else if (!strncmp(text, "cmd spawn ", 10))
+ {
+ if (tv->usequakeworldprotocols)
+ SendClientCommand(tv, "%s", text+4);
+
+ return; //commands the game server asked for are pointless.
+ }
else if (!strncmp(text, "cmd ", 4))
{
if (tv->controller)
@@ -822,7 +839,7 @@ static void ParsePacketEntities(sv_t *tv, netmsg_t *m, int deltaframe)
tv->map.nailcount = 0;
- tv->physicstime = tv->parsetime;
+ tv->physicstime = tv->curtime;
if (tv->cluster->chokeonnotupdated)
for (v = tv->cluster->viewers; v; v = v->next)
@@ -1710,7 +1727,7 @@ void ParseMessage(sv_t *tv, void *buffer, int length, int to, int mask)
strcpy(tv->status, "Prespawning\n");
}
ConnectionData(tv, (void*)((char*)buf.data+buf.startpos), buf.readpos - buf.startpos, to, mask, QW);
- if (tv->usequakeworldprotocols && !tv->controller)
+ if ((!tv->controller || tv->controller->netchan.isnqprotocol) && tv->usequakeworldprotocols)
{
if (i)
SendClientCommand(tv, "modellist %i %i\n", tv->clservercount, i);
@@ -1755,7 +1772,7 @@ void ParseMessage(sv_t *tv, void *buffer, int length, int to, int mask)
if (!i)
strcpy(tv->status, "Receiving modellist\n");
ConnectionData(tv, (void*)((char*)buf.data+buf.startpos), buf.readpos - buf.startpos, to, mask, QW);
- if (tv->usequakeworldprotocols && !tv->controller)
+ if ((!tv->controller || tv->controller->netchan.isnqprotocol) && tv->usequakeworldprotocols)
{
if (i)
SendClientCommand(tv, "soundlist %i %i\n", tv->clservercount, i);
diff --git a/fteqtv/qtv.h b/fteqtv/qtv.h
index 0bed03e39..0925fa1fd 100644
--- a/fteqtv/qtv.h
+++ b/fteqtv/qtv.h
@@ -226,6 +226,11 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
size_t strlcpy(char *dst, const char *src, size_t siz);
+
+#ifdef LIBQTV
+//#define Sys_Printf QTVSys_Printf
+#endif
+
#define VERSION "0.01" //this will be added to the serverinfo
#define PROX_DEFAULTSERVERPORT 27500
@@ -441,6 +446,7 @@ typedef struct viewer_s {
int lost; //packets
usercmd_t ucmds[3];
+ unsigned int lasttime;
int settime; //the time that we last told the client.
@@ -628,6 +634,7 @@ struct sv_s { //details about a server connection (also known as stream)
qboolean parsingconnectiondata; //so reject any new connects for now
+ unsigned int mapstarttime;
unsigned int physicstime; //the last time all the ents moved.
unsigned int simtime;
unsigned int curtime;
@@ -725,6 +732,7 @@ struct cluster_s {
sv_t *viewserver;
//options
+ char autojoinadr[128]; //new clients automatically .join this server
int qwlistenportnum;
int tcplistenportnum;
char adminpassword[256];//password required for rcon etc
@@ -834,8 +842,8 @@ void BuildNQServerData(sv_t *tv, netmsg_t *msg, qboolean mvd, int servercount);
void QW_UpdateUDPStuff(cluster_t *qtv);
unsigned int Sys_Milliseconds(void);
void Prox_SendInitialEnts(sv_t *qtv, oproxy_t *prox, netmsg_t *msg);
-qboolean QTV_Connect(sv_t *qtv, char *serverurl);
-void QTV_Shutdown(sv_t *qtv);
+qboolean QTV_ConnectStream(sv_t *qtv, char *serverurl);
+void QTV_ShutdownStream(sv_t *qtv);
qboolean NET_StringToAddr (char *s, netadr_t *sadr, int defaultport);
void QTV_Printf(sv_t *qtv, char *format, ...) PRINTFWARNING(2);
diff --git a/fteqtv/qw.c b/fteqtv/qw.c
index d6f6d4548..58b4d0da5 100644
--- a/fteqtv/qw.c
+++ b/fteqtv/qw.c
@@ -266,7 +266,7 @@ void BuildNQServerData(sv_t *tv, netmsg_t *msg, qboolean mvd, int playernum)
WriteByte(msg, 0);
WriteByte(msg, svc_nqsetview);
- WriteShort(msg, playernum);
+ WriteShort(msg, playernum+1);
WriteByte(msg, svc_nqsignonnum);
WriteByte(msg, 1);
@@ -274,7 +274,7 @@ void BuildNQServerData(sv_t *tv, netmsg_t *msg, qboolean mvd, int playernum)
else
{
//dummy connection, for choosing a game to watch.
- WriteString(msg, "FTEQTV Proxy");
+ WriteString(msg, tv->map.mapname);
//modellist
@@ -296,7 +296,7 @@ void BuildNQServerData(sv_t *tv, netmsg_t *msg, qboolean mvd, int playernum)
WriteByte(msg, tv->map.cdtrack);
WriteByte(msg, svc_nqsetview);
- WriteShort(msg, 15);
+ WriteShort(msg, playernum+1);
WriteByte(msg, svc_nqsignonnum);
WriteByte(msg, 1);
@@ -328,7 +328,7 @@ void SendServerData(sv_t *tv, viewer_t *viewer)
SendBufferToViewer(viewer, msg.data, msg.cursize, true);
viewer->thinksitsconnected = false;
- if (tv && (tv->controller == viewer))
+ if (tv && (tv->controller == viewer) && !viewer->netchan.isnqprotocol)
viewer->thinksitsconnected = true;
QW_ClearViewerState(viewer);
@@ -341,7 +341,7 @@ void SendNQSpawnInfoToViewer(cluster_t *cluster, viewer_t *viewer, netmsg_t *msg
int colours;
sv_t *tv = viewer->server;
WriteByte(msg, svc_nqtime);
- WriteFloat(msg, cluster->curtime/1000.0f);
+ WriteFloat(msg, (cluster->curtime - (tv?tv->mapstarttime:0))/1000.0f);
if (tv)
{
@@ -661,7 +661,7 @@ void QW_SetViewersServer(cluster_t *cluster, viewer_t *viewer, sv_t *sv)
if (sv != oldserver)
{
- if (sv)
+ if (sv && oldserver)
{
snprintf(buffer, sizeof(buffer), "%cQTV%c%s leaves to watch %s (%i)\n", 91+128, 93+128, viewer->name, *sv->map.hostname?sv->map.hostname:sv->server, sv->streamid);
QW_StreamPrint(cluster, oldserver, viewer, buffer);
@@ -685,6 +685,23 @@ qboolean ChallengePasses(netadr_t *addr, int challenge)
void NewClient(cluster_t *cluster, viewer_t *viewer)
{
+ sv_t *initialserver;
+ initialserver = NULL;
+ if (*cluster->autojoinadr)
+ {
+ initialserver = QTV_NewServerConnection(cluster, 0, cluster->autojoinadr, "", false, AD_WHENEMPTY, true, false);
+ if (initialserver && initialserver->sourcetype == SRC_UDP)
+ initialserver->controller = viewer;
+ }
+ else if (cluster->nouserconnects && cluster->numservers == 1)
+ {
+ initialserver = cluster->servers;
+ if (!initialserver->map.modellist[1].name[0])
+ initialserver = NULL; //damn, that server isn't ready
+ }
+
+ QW_SetViewersServer(cluster, viewer, initialserver);
+
viewer->userid = ++cluster->nextuserid;
viewer->timeout = cluster->curtime + 15*1000;
viewer->trackplayer = -1;
@@ -693,6 +710,7 @@ void NewClient(cluster_t *cluster, viewer_t *viewer)
QW_SetMenu(viewer, MENU_NONE);
+#ifndef LIBQTV
QW_PrintfToViewer(viewer, "Welcome to FTEQTV build %i\n", cluster->buildnumber);
QW_StuffcmdToViewer(viewer, "alias admin \"cmd admin\"\n");
@@ -719,6 +737,7 @@ void NewClient(cluster_t *cluster, viewer_t *viewer)
// QW_StuffcmdToViewer(viewer, "alias \".observe\" \"say .observe\"\n");
QW_PrintfToViewer(viewer, "Type admin for the admin menu\n");
+#endif
}
void ParseUserInfo(cluster_t *cluster, viewer_t *viewer)
@@ -760,7 +779,8 @@ void ParseUserInfo(cluster_t *cluster, viewer_t *viewer)
}
- QW_StreamPrint(cluster, viewer->server, NULL, buf);
+ if (!viewer->server || viewer->server->controller != viewer)
+ QW_StreamPrint(cluster, viewer->server, NULL, buf);
}
strlcpy(viewer->name, temp, sizeof(viewer->name));
@@ -839,18 +859,6 @@ void NewNQClient(cluster_t *cluster, netadr_t *addr)
for (i = 0; i < ENTITY_FRAMES; i++)
viewer->delta_frames[i] = -1;
- initialserver = NULL;
- if (cluster->numservers == 1)
- {
- initialserver = cluster->servers;
- if (!initialserver->map.modellist[1].name[0])
- initialserver = NULL; //damn, that server isn't ready
- }
-
- viewer->server = initialserver;
- if (viewer->server)
- viewer->server->numviewers++;
-
cluster->numviewers++;
sprintf(viewer->userinfo, "\\name\\%s", "unnamed");
@@ -859,9 +867,8 @@ void NewNQClient(cluster_t *cluster, netadr_t *addr)
NewClient(cluster, viewer);
- QW_StuffcmdToViewer(viewer, "cmd new\n");
-
- Sys_Printf(cluster, "New NQ client connected\n");
+ if (!viewer->server)
+ QW_StuffcmdToViewer(viewer, "cmd new\n");
}
void NewQWClient(cluster_t *cluster, netadr_t *addr, char *connectmessage)
@@ -907,18 +914,6 @@ void NewQWClient(cluster_t *cluster, netadr_t *addr, char *connectmessage)
for (i = 0; i < ENTITY_FRAMES; i++)
viewer->delta_frames[i] = -1;
- initialserver = NULL;
- if (cluster->nouserconnects && cluster->numservers == 1)
- {
- initialserver = cluster->servers;
- if (!initialserver->map.modellist[1].name[0])
- initialserver = NULL; //damn, that server isn't ready
- }
-
- viewer->server = initialserver;
- if (viewer->server)
- viewer->server->numviewers++;
-
cluster->numviewers++;
strlcpy(viewer->userinfo, infostring, sizeof(viewer->userinfo));
@@ -1642,23 +1637,20 @@ void SendNQPlayerStates(cluster_t *cluster, sv_t *tv, viewer_t *v, netmsg_t *msg
if (tv)
{
WriteByte(msg, svc_nqtime);
- WriteFloat(msg, tv->physicstime/1000.0f);
+ WriteFloat(msg, (tv->physicstime - tv->mapstarttime)/1000.0f);
BSP_SetupForPosition(tv->map.bsp, v->origin[0], v->origin[1], v->origin[2]);
lerp = ((tv->simtime - tv->oldpackettime)/1000.0f) / ((tv->nextpackettime - tv->oldpackettime)/1000.0f);
- if (lerp < 0)
- lerp = 0;
- if (lerp > 1)
- lerp = 1;
+ lerp = 1;
- if (tv->controller == v)
- lerp = 1;
+// if (tv->controller == v)
+// lerp = 1;
}
else
{
WriteByte(msg, svc_nqtime);
- WriteFloat(msg, cluster->curtime/1000.0f);
+ WriteFloat(msg, (cluster->curtime)/1000.0f);
lerp = 1;
}
@@ -1667,23 +1659,24 @@ void SendNQPlayerStates(cluster_t *cluster, sv_t *tv, viewer_t *v, netmsg_t *msg
if (tv)
{
-
- if (v->trackplayer >= 0)
+ if (v != tv->controller)
{
- WriteByte(msg, svc_nqsetview);
- WriteShort(msg, v->trackplayer+1);
+ if (v->trackplayer >= 0)
+ {
+ WriteByte(msg, svc_nqsetview);
+ WriteShort(msg, v->trackplayer+1);
- WriteByte(msg, svc_setangle);
- WriteByte(msg, (int)InterpolateAngle(tv->map.players[v->trackplayer].old.angles[0], tv->map.players[v->trackplayer].current.angles[0], lerp)>>8);
- WriteByte(msg, (int)InterpolateAngle(tv->map.players[v->trackplayer].old.angles[1], tv->map.players[v->trackplayer].current.angles[1], lerp)>>8);
- WriteByte(msg, (int)InterpolateAngle(tv->map.players[v->trackplayer].old.angles[2], tv->map.players[v->trackplayer].current.angles[2], lerp)>>8);
+ WriteByte(msg, svc_setangle);
+ WriteByte(msg, (int)InterpolateAngle(tv->map.players[v->trackplayer].old.angles[0], tv->map.players[v->trackplayer].current.angles[0], lerp)>>8);
+ WriteByte(msg, (int)InterpolateAngle(tv->map.players[v->trackplayer].old.angles[1], tv->map.players[v->trackplayer].current.angles[1], lerp)>>8);
+ WriteByte(msg, (int)InterpolateAngle(tv->map.players[v->trackplayer].old.angles[2], tv->map.players[v->trackplayer].current.angles[2], lerp)>>8);
+ }
+ else
+ {
+ WriteByte(msg, svc_nqsetview);
+ WriteShort(msg, v->thisplayer+1);
+ }
}
- else
- {
- WriteByte(msg, svc_nqsetview);
- WriteShort(msg, v->thisplayer+1);
- }
-
for (e = 0; e < MAX_CLIENTS; e++)
{
@@ -1736,8 +1729,9 @@ void SendNQPlayerStates(cluster_t *cluster, sv_t *tv, viewer_t *v, netmsg_t *msg
if (!pl->active)
continue;
- if (pl->current.modelindex >= tv->map.numinlines && !BSP_Visible(tv->map.bsp, pl->leafcount, pl->leafs))
- continue;
+ if (v != tv->controller)
+ if (pl->current.modelindex >= tv->map.numinlines && !BSP_Visible(tv->map.bsp, pl->leafcount, pl->leafs))
+ continue;
pl->current.modelindex = 8;
@@ -1839,8 +1833,9 @@ void SendNQPlayerStates(cluster_t *cluster, sv_t *tv, viewer_t *v, netmsg_t *msg
//pvs cull everything else
newstate = &topacket->ents[newindex];
newnum = topacket->entnums[newindex];
- if (newstate->modelindex >= tv->map.numinlines && !BSP_Visible(tv->map.bsp, tv->map.entity[newnum].leafcount, tv->map.entity[newnum].leafs))
- continue;
+ if (v != tv->controller)
+ if (newstate->modelindex >= tv->map.numinlines && !BSP_Visible(tv->map.bsp, tv->map.entity[newnum].leafcount, tv->map.entity[newnum].leafs))
+ continue;
if (msg->cursize + 128 > msg->maxsize)
break;
@@ -1851,7 +1846,7 @@ void SendNQPlayerStates(cluster_t *cluster, sv_t *tv, viewer_t *v, netmsg_t *msg
for (i=0 ; i<3 ; i++)
{
miss = (int)(newstate->origin[i]) - ent->baseline.origin[i];
- if ( miss < -1 || miss > 1 )
+ if ( miss <= -1 || miss >= 1 )
bits |= UNQ_ORIGIN1<server, v, ".menu", false);
}
+ else if (!strncmp(buf, "say \".", 6))
+ QTV_Say(cluster, qtv, v, buf+5, false);
+ else if (!strncmp(buf, "say .", 5))
+ QTV_Say(cluster, qtv, v, buf+4, false);
+
+ else if (v->server && v == v->server->controller)
+ SendClientCommand(v->server, "%s", buf);
+
// else if (!strcmp(buf, "pause"))
// qtv->errored = ERR_PAUSED;
@@ -3495,12 +3498,16 @@ void ParseNQC(cluster_t *cluster, sv_t *qtv, viewer_t *v, netmsg_t *m)
v->ucmds[2].upmove = ReadShort(m);
//one button
- v->ucmds[1].buttons = v->ucmds[2].buttons;
v->ucmds[2].buttons = ReadByte(m);
//one impulse
v->ucmds[2].impulse = ReadByte(m);
- v->ucmds[2].msec = 1000/NQ_PACKETS_PER_SECOND;
+ v->ucmds[2].msec = cluster->curtime - v->lasttime;
+ v->lasttime = cluster->curtime;
+
+ if (v->server && v->server->controller == v)
+ return;
+
PMove(v, &v->ucmds[2]);
if ((v->ucmds[1].buttons&1) != (v->ucmds[2].buttons&1) && (v->ucmds[2].buttons&1))
diff --git a/fteqtv/rcon.c b/fteqtv/rcon.c
index b4f770335..342d49800 100644
--- a/fteqtv/rcon.c
+++ b/fteqtv/rcon.c
@@ -914,7 +914,7 @@ void Cmd_DemoSpeed(cmdctxt_t *ctx)
void Cmd_Disconnect(cmdctxt_t *ctx)
{
- QTV_Shutdown(ctx->qtv);
+ QTV_ShutdownStream(ctx->qtv);
Cmd_Printf(ctx, "Disconnected\n");
}
@@ -1001,7 +1001,7 @@ void Cmd_Reconnect(cmdctxt_t *ctx)
Cmd_Printf(ctx, "Stream is a reverse connection (command rejected)\n");
// else if (ctx->qtv->autodisconnect == AD_STATUSPOLL && !ctx->qtv->numviewers && !ctx->qtv->proxies)
// Cmd_Printf(ctx, "Not reconnecting to idle server\n");
- else if (QTV_Connect(ctx->qtv, ctx->qtv->server))
+ else if (QTV_ConnectStream(ctx->qtv, ctx->qtv->server))
Cmd_Printf(ctx, "Reconnected\n");
else
Cmd_Printf(ctx, "Failed to reconnect (will keep trying)\n");
diff --git a/fteqtv/source.c b/fteqtv/source.c
index 49795a80d..ada557969 100644
--- a/fteqtv/source.c
+++ b/fteqtv/source.c
@@ -206,6 +206,8 @@ qboolean Net_CompareAddress(netadr_t *s1, netadr_t *s2, int qp1, int qp2)
return false;
switch(g1->sa_family)
{
+ default:
+ return true;
case AF_INET:
{
struct sockaddr_in *i1=(void*)s1->sockaddr, *i2=(void*)s2->sockaddr;
@@ -1063,7 +1065,7 @@ void Trim(char *s)
*s = '\0';
}
-qboolean QTV_Connect(sv_t *qtv, char *serverurl)
+qboolean QTV_ConnectStream(sv_t *qtv, char *serverurl)
{
if (qtv->sourcesock != INVALID_SOCKET)
{
@@ -1222,7 +1224,7 @@ void QTV_Cleanup(sv_t *qtv, qboolean leaveadmins)
}
}
-void QTV_Shutdown(sv_t *qtv)
+void QTV_ShutdownStream(sv_t *qtv)
{
sv_t *peer;
cluster_t *cluster;
@@ -1608,7 +1610,7 @@ void QTV_Run(sv_t *qtv)
}
else if (qtv->errored == ERR_DROP)
{
- QTV_Shutdown(qtv); //destroys the stream
+ QTV_ShutdownStream(qtv); //destroys the stream
return;
}
}
@@ -1679,7 +1681,7 @@ void QTV_Run(sv_t *qtv)
strcpy(qtv->status, "Attemping challenge\n");
if (qtv->sourcesock == INVALID_SOCKET && !qtv->sourcefile)
{
- if (!QTV_Connect(qtv, qtv->server)) //reconnect it
+ if (!QTV_ConnectStream(qtv, qtv->server)) //reconnect it
{
qtv->errored = ERR_PERMANENT;
}
@@ -1735,6 +1737,10 @@ void QTV_Run(sv_t *qtv)
}
ChooseFavoriteTrack(qtv);
+ //if we froze somehow, don't speedcheat by a burst of 10000+ packets while we were frozen in a debugger or disk spinup or whatever
+ if (qtv->packetratelimiter < qtv->curtime - UDPPACKETINTERVAL*2)
+ qtv->packetratelimiter = qtv->curtime;
+
if (qtv->map.trackplayer >= 0)
{
qtv->packetratelimiter += UDPPACKETINTERVAL;
@@ -1748,6 +1754,13 @@ void QTV_Run(sv_t *qtv)
{
qtv->packetratelimiter += UDPPACKETINTERVAL;
+ if (qtv->controller->netchan.isnqprotocol)
+ {
+ memcpy(&qtv->controller->ucmds[0], &qtv->controller->ucmds[1], sizeof(qtv->controller->ucmds[0]));
+ memcpy(&qtv->controller->ucmds[1], &qtv->controller->ucmds[2], sizeof(qtv->controller->ucmds[0]));
+ qtv->controller->ucmds[2].msec = UDPPACKETINTERVAL;
+ }
+
WriteByte(&msg, clc_tmove);
WriteShort(&msg, qtv->controller->origin[0]);
WriteShort(&msg, qtv->controller->origin[1]);
@@ -1836,7 +1849,7 @@ void QTV_Run(sv_t *qtv)
qtv->errored = ERR_DROP;
return;
}
- if (!QTV_Connect(qtv, qtv->server)) //reconnect it
+ if (!QTV_ConnectStream(qtv, qtv->server)) //reconnect it
{
qtv->errored = ERR_PERMANENT;
return;
@@ -2205,7 +2218,7 @@ sv_t *QTV_NewServerConnection(cluster_t *cluster, int newstreamid, char *server,
//warning review this logic
if (qtv->errored == ERR_DISABLED)
{
- if (!(!QTV_Connect(qtv, server) && !force)) //try and wake it up
+ if (!(!QTV_ConnectStream(qtv, server) && !force)) //try and wake it up
qtv->errored = ERR_NONE;
}
return qtv;
@@ -2256,7 +2269,7 @@ sv_t *QTV_NewServerConnection(cluster_t *cluster, int newstreamid, char *server,
if (autoclose != AD_REVERSECONNECT) //2 means reverse connection (don't ever try reconnecting)
{
- if (!QTV_Connect(qtv, server) && !force)
+ if (!QTV_ConnectStream(qtv, server) && !force)
{
QTV_Cleanup(qtv, false);
free(qtv);