NQ-related bugfixes.

Added new project/target for easy integration into NQ engines - read nq_api.c for guidelines.

git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@4024 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
Spoike 2012-04-23 04:37:33 +00:00
parent 810d28dd84
commit 3da350a3ed
11 changed files with 684 additions and 90 deletions

View File

@ -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)

View File

@ -0,0 +1,282 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="8.00"
Name="libqtv"
ProjectGUID="{EDBDDC82-6DEE-4BF1-B0BC-BBBCCFE65D4C}"
RootNamespace="libqtv"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="2"
CharacterSet="2"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
PreprocessorDefinitions="LIBQTV;_CRT_SECURE_NO_WARNINGS"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="3"
WarningLevel="3"
Detect64BitPortabilityProblems="true"
DebugInformationFormat="4"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
GenerateDebugInformation="true"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="2"
CharacterSet="2"
WholeProgramOptimization="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
PreprocessorDefinitions="LIBQTV;_CRT_SECURE_NO_WARNINGS"
RuntimeLibrary="2"
WarningLevel="3"
Detect64BitPortabilityProblems="true"
DebugInformationFormat="3"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
GenerateDebugInformation="true"
OptimizeReferences="2"
EnableCOMDATFolding="2"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<Filter
Name="Source Files"
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
>
<File
RelativePath="..\bsp.c"
>
</File>
<File
RelativePath="..\control.c"
>
</File>
<File
RelativePath="..\crc.c"
>
</File>
<File
RelativePath="..\forward.c"
>
</File>
<File
RelativePath="..\libqtvc\glibc_sucks.c"
>
</File>
<File
RelativePath="..\httpsv.c"
>
</File>
<File
RelativePath="..\mdfour.c"
>
</File>
<File
RelativePath="..\menu.c"
>
</File>
<File
RelativePath="..\msg.c"
>
</File>
<File
RelativePath="..\libqtvc\msvc_sucks.c"
>
</File>
<File
RelativePath="..\netchan.c"
>
</File>
<File
RelativePath="..\nq_api.c"
>
</File>
<File
RelativePath="..\parse.c"
>
</File>
<File
RelativePath="..\pmove.c"
>
</File>
<File
RelativePath="..\qw.c"
>
</File>
<File
RelativePath="..\rcon.c"
>
</File>
<File
RelativePath="..\sc_dsound.c"
>
</File>
<File
RelativePath="..\..\engine\common\sha1.c"
>
</File>
<File
RelativePath="..\source.c"
>
</File>
<File
RelativePath="..\sp_dsound.c"
>
</File>
</Filter>
<Filter
Name="Header Files"
Filter="h;hpp;hxx;hm;inl;inc;xsd"
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
>
<File
RelativePath="..\bsd_string.h"
>
</File>
<File
RelativePath="..\cmd.h"
>
</File>
<File
RelativePath="..\protocol.h"
>
</File>
<File
RelativePath="..\qtv.h"
>
</File>
</Filter>
<Filter
Name="Resource Files"
Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
>
</Filter>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

View File

@ -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

View File

@ -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)
{

View File

@ -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)

230
fteqtv/nq_api.c Normal file
View File

@ -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;
}

View File

@ -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);

View File

@ -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);

View File

@ -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<<i;
}
@ -3405,7 +3400,7 @@ void ParseNQC(cluster_t *cluster, sv_t *qtv, viewer_t *v, netmsg_t *m)
char arg[3][ARG_LEN];
char *command = buf;
for (i = 0; i < MAX_ARGS; i++)
for (i = 0; i < 3; i++)
{
command = COM_ParseToken(command, arg[i], ARG_LEN, TOKENIZE_PUNCTUATION);
}
@ -3458,6 +3453,14 @@ void ParseNQC(cluster_t *cluster, sv_t *qtv, viewer_t *v, netmsg_t *m)
QTV_Say(cluster, v->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))

View File

@ -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");

View File

@ -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);