rewrite download code detect rates and get the best speeds practical.

client can cope with files over 4gb.
added download resumption. hopefully.
fix some q3 bugs.

git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@4685 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
Spoike 2014-06-12 23:08:42 +00:00
parent 4405abcc93
commit a4db77b22f
26 changed files with 1245 additions and 688 deletions

View File

@ -320,16 +320,16 @@ typedef struct {
qbyte weapon; // weapon
signed char forwardmove, rightmove, upmove;
} q3usercmd_t;
#define CMD_MASK Q3UPDATE_MASK
#define CMD_BACKUP UPDATE_BACKUP
#define CMD_MASK UPDATE_MASK
qboolean CGQ3_GetUserCmd(int cmdNumber, q3usercmd_t *ucmd)
{
usercmd_t *cmd;
cmdNumber--;
if (cmdNumber > cl.movesequence)
Host_EndGame("CL_GetUserCmd: cmdNumber > ccs.currentUserCmdNumber");
if (cl.movesequence - cmdNumber > CMD_MASK)
if (cl.movesequence - cmdNumber >= CMD_BACKUP)
return false; // too old
cmd = &cl.outframes[(cmdNumber) & CMD_MASK].cmd[0];
@ -956,7 +956,7 @@ static qintptr_t CG_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con
VALIDATEPOINTER(arg[0], sizeof(int));
VALIDATEPOINTER(arg[1], sizeof(int));
*(int *)VM_POINTER(arg[0]) = ccs.snap.serverMessageNum;
*(int *)VM_POINTER(arg[1]) = ccs.snap.serverTime;// + Sys_DoubleTime()*1000-ccs.snap.localTime;
*(int *)VM_POINTER(arg[1]) = ccs.snap.serverTime;
break;
case CG_GETSNAPSHOT:
@ -965,7 +965,7 @@ static qintptr_t CG_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con
break;
case CG_GETCURRENTCMDNUMBER:
VM_LONG(ret) = cl.movesequence;
VM_LONG(ret) = cl.movesequence-1;
break;
case CG_GETUSERCMD:
VALIDATEPOINTER(arg[1], sizeof(q3usercmd_t));
@ -1143,7 +1143,7 @@ int CG_Refresh(void)
if (!cgvm)
return false;
time = ccs.serverTime;
time = cl.time*1000;
VM_Call(cgvm, CG_DRAW_ACTIVE_FRAME, time, 0, false);
R2D_ImageColours(1, 1, 1, 1);

View File

@ -1663,7 +1663,7 @@ void CL_SendCmd (double frametime, qboolean mainloop)
if (!runningindepphys)
{
// while we're not playing send a slow keepalive fullsend to stop mvdsv from screwing up
if (cls.state < ca_active && !cls.downloadmethod)
if (cls.state < ca_active && !cls.download)
{
#ifdef IRCCONNECT //don't spam irc.
if (cls.netchan.remote_address.type == NA_IRC)

View File

@ -554,6 +554,7 @@ void CL_SendConnectPacket (netadr_t *to, int mtu,
#ifdef Q3CLIENT
if (connectinfo.protocol == CP_QUAKE3)
{ //q3 requires some very strange things.
cls.challenge = connectinfo.challenge;
CLQ3_SendConnectPacket(to);
return;
}
@ -1483,7 +1484,7 @@ void CL_Disconnect (void)
CL_ClearState();
FS_PureMode(0, NULL, NULL, 0);
FS_PureMode(0, NULL, NULL, NULL, NULL, 0);
Alias_WipeStuffedAliases();
@ -1660,6 +1661,7 @@ void CL_PakDownloads(int mode)
mode=0 no downloads (forced to 1 for pure)
mode=1 archived names so local stuff is not poluted
mode=2 downloaded packages will always be present. Use With Caution.
mode&4 download even packages that are not referenced.
*/
char local[256];
char *pname;
@ -1678,10 +1680,10 @@ void CL_PakDownloads(int mode)
//'*' prefix means 'referenced'. so if the server isn't using any files from it, don't bother downloading it.
if (*pname == '*')
pname++;
else
else if (!(mode & 4))
continue;
if (mode != 2)
if ((mode&3) != 2)
{
/*if we already have such a file, this is a no-op*/
if (CL_CheckDLFile(va("package/%s", pname)))
@ -1709,7 +1711,7 @@ void CL_CheckServerPacks(void)
if (pure != oldpure || cl.serverpakschanged)
{
CL_PakDownloads((pure && !cl_download_packages.ival)?1:cl_download_packages.ival);
FS_PureMode(pure, cl.serverpaknames, cl.serverpakcrcs, cls.challenge);
FS_PureMode(pure, cl.serverpaknames, cl.serverpakcrcs, NULL, NULL, cls.challenge);
if (pure)
{
@ -2290,7 +2292,7 @@ drop to full console
void CL_Changing_f (void)
{
char *mapname = Cmd_Argv(1);
if (cls.downloadqw) // don't change when downloading
if (cls.download) // don't change when downloading
return;
if (*mapname)
@ -2323,7 +2325,7 @@ The server is changing levels
*/
void CL_Reconnect_f (void)
{
if (cls.downloadqw) // don't change when downloading
if (cls.download) // don't change when downloading
return;
#ifdef NQPROT
if (cls.protocol == CP_NETQUAKE && Cmd_FromGamecode())
@ -2430,6 +2432,7 @@ void CL_ConnectionlessPacket (void)
Con_Printf ("junk on the end of the packet\n");
CL_Disconnect_f();
}
cls.netchan.last_received = realtime; //in case there's some virus scanner running on the server making it stall... for instance...
}
return;
}
@ -3171,6 +3174,7 @@ void CL_Download_f (void)
if (Cmd_IsInsecure()) //mark server specified downloads.
{
//don't let gamecode order us to download random junk
if (!CL_AllowArbitaryDownload(url))
return;
@ -3198,14 +3202,14 @@ void CL_DownloadSize_f(void)
if (!strcmp(size, "e"))
{
Con_Printf("Download of \"%s\" failed. Not found.\n", rname);
CL_DownloadFailed(rname, false);
CL_DownloadFailed(rname, NULL);
}
else if (!strcmp(size, "p"))
{
if (stricmp(cls.downloadremotename, rname))
if (cls.download && stricmp(cls.download->remotename, rname))
{
Con_Printf("Download of \"%s\" failed. Not allowed.\n", rname);
CL_DownloadFailed(rname, false);
CL_DownloadFailed(rname, NULL);
}
}
else if (!strcmp(size, "r"))
@ -3215,7 +3219,7 @@ void CL_DownloadSize_f(void)
if (!CL_AllowArbitaryDownload(redirection))
return;
dl = CL_DownloadFailed(rname, false);
dl = CL_DownloadFailed(rname, NULL);
Con_DPrintf("Download of \"%s\" redirected to \"%s\".\n", rname, redirection);
CL_CheckOrEnqueDownloadFile(redirection, NULL, dl->flags);
}
@ -3234,42 +3238,26 @@ void CL_DownloadSize_f(void)
}
void CL_FinishDownload(char *filename, char *tempname);
void CL_ForceStopDownload (qboolean finish)
void CL_ForceStopDownload (qdownload_t *dl, qboolean finish)
{
if (Cmd_IsInsecure())
{
Con_Printf(CON_WARNING "Execution from server rejected for %s\n", Cmd_Argv(0));
return;
}
if (!dl)
return;
if (!cls.downloadqw)
if (!dl->file)
{
Con_Printf("No files downloading by QW protocol\n");
return;
}
VFS_CLOSE (cls.downloadqw);
cls.downloadqw = NULL;
if (finish)
CL_DownloadFinished();
DL_Abort(dl, QDL_COMPLETED);
else
{
char *tempname;
if (*cls.downloadtempname)
tempname = cls.downloadtempname;
else
tempname = cls.downloadlocalname;
if (strncmp(tempname,"skins/",6))
FS_Remove(tempname, FS_GAME);
else
FS_Remove(tempname, FS_PUBBASEGAMEONLY);
}
*cls.downloadlocalname = '\0';
*cls.downloadremotename = '\0';
cls.downloadpercent = 0;
DL_Abort(dl, QDL_FAILED);
// get another file if needed
CL_RequestNextDownload ();
@ -3277,12 +3265,12 @@ void CL_ForceStopDownload (qboolean finish)
void CL_SkipDownload_f (void)
{
CL_ForceStopDownload(false);
CL_ForceStopDownload(cls.download, false);
}
void CL_FinishDownload_f (void)
{
CL_ForceStopDownload(true);
CL_ForceStopDownload(cls.download, true);
}
#if defined(_WIN32) && !defined(WINRT)
@ -4333,6 +4321,10 @@ double Host_Frame (double time)
cl.paused;
// TODO: check if minimized or unfocused
//read packets early and always, so we don't have stuff waiting for reception quite so often.
//should smooth out a few things, and increase download speeds.
CL_ReadPackets ();
if (idle && cl_idlefps.value > 0)
{
double idlesec = 1.0 / cl_idlefps.value;

File diff suppressed because it is too large Load Diff

View File

@ -537,7 +537,7 @@ void CL_CalcClientTime(void)
extern float demtime;
if (!cls.state)
cl.servertime += host_frametime;
else if (cls.protocol != CP_QUAKE3)
else// if (cls.protocol != CP_QUAKE3)
{
float oldst = realtime;
@ -549,7 +549,7 @@ void CL_CalcClientTime(void)
f = bound(0, f, 1);
cl.servertime = cl.gametime*f + cl.oldgametime*(1-f);
}
else if (!cl_predict_smooth.ival || (cl_predict_smooth.ival == 2 && !cls.demoplayback))
else if (cls.protocol != CP_QUAKE3 && (!cl_predict_smooth.ival || (cl_predict_smooth.ival == 2 && !cls.demoplayback)))
{
float f;
f = cl.gametime - cl.oldgametime;

View File

@ -1571,20 +1571,20 @@ void SCR_DrawLoading (qboolean opaque)
}
R2D_ImageColours(1, 1, 1, 1);
if (cl.downloadlist || cls.downloadmethod)
if (cl.downloadlist || cls.download)
{
unsigned int fcount;
unsigned int tsize;
qofs_t tsize;
qboolean sizeextra;
x = vid.width/2 - 160;
CL_GetDownloadSizes(&fcount, &tsize, &sizeextra);
//downloading files?
if (cls.downloadmethod)
if (cls.download)
Draw_FunString(x+8, y+4, va("Downloading %s... %i%%",
cls.downloadlocalname,
(int)cls.downloadpercent));
cls.download->localname,
(int)cls.download->percent));
if (tsize > 1024*1024*16)
{
@ -2017,7 +2017,7 @@ void SCR_DrawCharToSnap (int num, qbyte *dest, int width)
if (!draw_chars)
{
draw_chars = W_GetLumpName("conchars");
draw_chars = W_SafeGetLumpName("conchars");
if (!draw_chars)
return;
}

View File

@ -324,6 +324,58 @@ typedef enum {
dl_singlestuffed
} dltype_t; // download type
typedef struct qdownload_s
{
enum {DL_QW, DL_QWCHUNKS, DL_Q3, DL_DARKPLACES, DL_QWPENDING, DL_HTTP, DL_FTP} method;
vfsfile_t *file; // file transfer from server
char dclname[MAX_OSPATH]; //file to read/write the chunklist from, for download resumption.
char tempname[MAX_OSPATH]; //file its currently writing to.
char localname[MAX_OSPATH]; //file its going to be renamed to.
int prefixbytes; //number of bytes that prefix the above names (ie: package/ or nothing).
char remotename[MAX_OSPATH]; //file its coming from.
float percent; //for progress indicator.
float starttime; //for speed info
qofs_t completedbytes; //number of bytes downloaded, for progress/speed info
qofs_t size; //total size (may be a guess)
qboolean sizeunknown; //says that size is a guess
unsigned int filesequence; //unique file id.
enum fs_relative fsroot; //where the local+temp file is meant to be relative to.
double ratetime;
int rate;
int ratebytes;
unsigned int flags;
//chunked downloads uses this
struct dlblock_s
{
qofs_t start;
qofs_t end;
enum
{
DLB_MISSING,
DLB_PENDING,
DLB_RECEIVED
} state:16;
unsigned int sequence; //sequence is only valid on pending blocks.
struct dlblock_s *next;
} *dlblocks;
} qdownload_t;
enum qdlabort
{
QDL_FAILED, //delete file, tell server.
QDL_DISCONNECT, //delete file, don't tell server.
QDL_COMPLETED, //rename file, tell server.
};
qboolean DL_Begun(qdownload_t *dl);
void DL_Completed(qdownload_t *dl, qofs_t start, qofs_t end); //notifies the download logic that a chunk of the file is no longer needed.
void DL_Abort(qdownload_t *dl, enum qdlabort aborttype); //just frees the download's resources. does not delete the temp file.
//chunked downloads
void DLC_Poll(qdownload_t *dl);
//
// the client_static_t structure is persistant through an arbitrary number
// of server connections
@ -384,15 +436,7 @@ typedef struct
struct ftenet_connections_s *sockets;
enum {DL_NONE, DL_QW, DL_QWCHUNKS, DL_Q3, DL_DARKPLACES, DL_QWPENDING, DL_HTTP, DL_FTP} downloadmethod;
vfsfile_t *downloadqw; // file transfer from server
char downloadtempname[MAX_OSPATH]; //file its currently writing to.
char downloadlocalname[MAX_OSPATH]; //file its going to be renamed to.
char downloadremotename[MAX_OSPATH]; //file its coming from.
float downloadpercent; //for progress indicator.
int downloadchunknum; //for QW downloads only.
float downloadstarttime; //for speed info
unsigned int downloadedbytes; //number of bytes downloaded, for progress/speed info
qdownload_t *download;
// demo loop control
int demonum; // -1 = don't play demos
@ -452,13 +496,15 @@ typedef struct downloadlist_s {
char localname[128];
unsigned int size;
unsigned int flags;
#define DLLF_VERBOSE 1 //tell the user that its downloading
#define DLLF_REQUIRED 2 //means that it won't load models etc until its downloaded (ie: requiredownloads 0 makes no difference)
#define DLLF_OVERWRITE 4 //overwrite it even if it already exists
#define DLLF_SIZEUNKNOWN 8 //download's size isn't known
#define DLLF_IGNOREFAILED 16 //
#define DLLF_NONGAME 32 //means the requested download filename+localname is gamedir explicit (so id1/foo.txt is distinct from qw/foo.txt)
#define DLLF_TEMPORARY 64 //download it, but don't actually save it (DLLF_OVERWRITE doesn't actually overwrite, but does ignore any local files)
#define DLLF_VERBOSE (1u<<0) //tell the user that its downloading
#define DLLF_REQUIRED (1u<<1) //means that it won't load models etc until its downloaded (ie: requiredownloads 0 makes no difference)
#define DLLF_OVERWRITE (1u<<2) //overwrite it even if it already exists
#define DLLF_SIZEUNKNOWN (1u<<3) //download's size isn't known
#define DLLF_IGNOREFAILED (1u<<4) //
#define DLLF_NONGAME (1u<<5) //means the requested download filename+localname is gamedir explicit (so id1/foo.txt is distinct from qw/foo.txt)
#define DLLF_TEMPORARY (1u<<6) //download it, but don't actually save it (DLLF_OVERWRITE doesn't actually overwrite, but does ignore any local files)
#define DLLF_BEGUN (1u<<7) //server has confirmed that the file exists, is readable, and we've opened a file. should not be set on new requests.
struct downloadlist_s *next;
} downloadlist_t;
@ -1033,11 +1079,11 @@ void CL_NewTranslation (int slot);
int CL_IsDownloading(const char *localname);
qboolean CL_CheckOrEnqueDownloadFile (const char *filename, const char *localname, unsigned int flags);
qboolean CL_EnqueDownload(const char *filename, const char *localname, unsigned int flags);
downloadlist_t *CL_DownloadFailed(const char *name, qboolean cancel);
downloadlist_t *CL_DownloadFailed(const char *name, qdownload_t *qdl);
int CL_DownloadRate(void);
void CL_GetDownloadSizes(unsigned int *filecount, unsigned int *totalsize, qboolean *somesizesunknown);
void CL_GetDownloadSizes(unsigned int *filecount, qofs_t *totalsize, qboolean *somesizesunknown);
qboolean CL_ParseOOBDownload(void);
void CL_DownloadFinished(void);
void CL_DownloadFinished(qdownload_t *dl);
void CL_RequestNextDownload (void);
void CL_SendDownloadReq(sizebuf_t *msg);
void Sound_CheckDownload(const char *s); /*checkorenqueue a sound file*/

View File

@ -8,7 +8,8 @@
#include "clq3defs.h"
#define CMD_MASK Q3UPDATE_MASK
#define CMD_BACKUP UPDATE_BACKUP
#define CMD_MASK UPDATE_MASK
#define SHOWSTRING(s) if(cl_shownet.value==2)Con_Printf ("%s\n", s);
#define SHOWNET(x) if(cl_shownet.value==2)Con_Printf ("%3i:%s\n", msg_readcount-1, x);
@ -263,7 +264,12 @@ void CLQ3_ParseSnapshot(void)
snap.serverMessageNum = ccs.serverMessageNum;
snap.serverCommandNum = ccs.lastServerCommandNum;
snap.serverTime = MSG_ReadLong();
snap.localTime = Sys_Milliseconds();
//so we can delta to it properly.
cl.oldgametime = cl.gametime;
cl.oldgametimemark = cl.gametimemark;
cl.gametime = snap.serverTime / 1000.0f;
cl.gametimemark = Sys_DoubleTime();
// If the frame is delta compressed from data that we
// no longer have available, we must suck up the rest of
@ -327,9 +333,9 @@ void CLQ3_ParseSnapshot(void)
// Find last usercmd server has processed and calculate snap.ping
snap.ping = 3;
for (i=cls.netchan.outgoing_sequence-1 ; i>cls.netchan.outgoing_sequence-Q3UPDATE_BACKUP ; i--)
for (i=cls.netchan.outgoing_sequence-1 ; i>cls.netchan.outgoing_sequence-CMD_BACKUP ; i--)
{
frame = &cl.outframes[i & Q3UPDATE_MASK];
frame = &cl.outframes[i & CMD_MASK];
if (frame->server_message_num == snap.deltaFrame)
{
snap.ping = Sys_Milliseconds() - frame->client_time;
@ -343,37 +349,34 @@ void CLQ3_ParseSnapshot(void)
SHOWSTRING(va("snapshot:%i delta:%i ping:%i", snap.serverMessageNum, snap.deltaFrame, snap.ping));
}
#define MAXCHUNKSIZE 2048
#define MAXCHUNKSIZE 65536
void CLQ3_ParseDownload(void)
{
qdownload_t *dl = cls.download;
unsigned int chunknum;
static unsigned int downloadsize;
unsigned int chunksize;
unsigned char chunkdata[MAXCHUNKSIZE];
int i;
char *s;
chunknum = (unsigned short) MSG_ReadShort();
if (downloadsize >= MAXCHUNKSIZE*0xffff)
{
chunknum |= ccs.downloadchunknum&0x10000; //add the chunk number, truncated by the network protocol.
}
chunknum |= ccs.downloadchunknum&~0xffff; //add the chunk number, truncated by the network protocol.
if (!chunknum)
{
downloadsize = MSG_ReadLong();
Cvar_SetValue( Cvar_Get("cl_downloadSize", "0", 0, "Download stuff"), downloadsize );
dl->size = (unsigned int)MSG_ReadLong();
Cvar_SetValue( Cvar_Get("cl_downloadSize", "0", 0, "Download stuff"), dl->size );
}
if (downloadsize == (unsigned int)-1)
if (dl->size == (unsigned int)-1)
{
s = MSG_ReadString();
Con_Printf("\nDownload refused:\n %s\n", s);
CL_DownloadFailed(dl->remotename, dl);
return;
}
chunksize = MSG_ReadShort();
chunksize = (unsigned short)MSG_ReadShort();
if (chunksize > MAXCHUNKSIZE)
Host_EndGame("Server sent a download chunk of size %i (it's too damn big!)\n", chunksize);
@ -387,53 +390,102 @@ void CLQ3_ParseDownload(void)
}
ccs.downloadchunknum++;
if (!cls.downloadqw)
if (!dl || dl->method != DL_Q3)
{
if (!*cls.downloadtempname)
{
Con_Printf("Server sending download, but no download was requested\n");
CLQ3_SendClientCommand("stopdl");
cls.downloadmethod = DL_NONE;
return;
}
Con_Printf("Server sending download, but no download was requested\n");
CLQ3_SendClientCommand("stopdl");
return;
}
FS_CreatePath(cls.downloadtempname, FS_ROOT);
cls.downloadqw = FS_OpenVFS(cls.downloadtempname, "wb", FS_ROOT);
if (!cls.downloadqw)
if (!dl->file)
{
if (!DL_Begun(dl))
{
Con_Printf("Couldn't write to temporary file %s - stopping download\n", cls.downloadtempname);
CLQ3_SendClientCommand("stopdl");
cls.downloadmethod = DL_NONE;
CL_DownloadFailed(dl->remotename, dl);
return;
}
}
Con_Printf("dl: chnk %i, size %i, csize %i\n", chunknum, downloadsize, chunksize);
Con_DPrintf("dl: chnk %u, size %u, csize %u\n", (unsigned int)chunknum, (unsigned int)dl->size, (unsigned int)chunksize);
if (!chunksize)
{
VFS_CLOSE(cls.downloadqw);
cls.downloadqw = NULL;
FS_Rename(cls.downloadtempname, cls.downloadlocalname, FS_ROOT); // ->
*cls.downloadtempname = *cls.downloadlocalname = *cls.downloadremotename = 0;
cls.downloadmethod = DL_NONE;
CL_DownloadFinished(dl);
FS_ReloadPackFiles();
cl.servercount = -1; //make sure the server resends us that vital gamestate.
ccs.downloadchunknum = -1;
return;
}
else
{
VFS_WRITE(cls.downloadqw, chunkdata, chunksize);
chunksize=VFS_TELL(cls.downloadqw);
VFS_WRITE(dl->file, chunkdata, chunksize);
dl->ratebytes += chunksize;
chunksize=VFS_TELL(dl->file);
// Con_Printf("Recieved %i\n", chunksize);
cls.downloadpercent = (100.0 * chunksize) / downloadsize;
dl->percent = (100.0 * chunksize) / dl->size;
}
CLQ3_SendClientCommand("nextdl %i", chunknum);
CLQ3_SendClientCommand("nextdl %u", chunknum);
}
static qboolean CLQ3_SendDownloads(char *rc, char *rn)
{
while(rn)
{
qdownload_t *dl;
char localname[MAX_QPATH];
char tempname[MAX_QPATH];
char crc[64];
vfsfile_t *f;
rc = COM_ParseOut(rc, crc, sizeof(crc));
rn = COM_Parse(rn);
if (!*com_token)
break;
if (!strchr(com_token, '/')) //don't let some muppet tell us to download quake3.exe
break;
//as much as I'd like to use COM_FCheckExists, this stuf is relative to root, not the gamedir.
f = FS_OpenVFS(va("%s.pk3", com_token), "rb", FS_ROOT);
if (f)
{
VFS_CLOSE(f);
continue;
}
if (!FS_GenCachedPakName(va("%s.pk3", com_token), crc, localname, sizeof(localname)))
continue;
f = FS_OpenVFS(localname, "rb", FS_ROOT);
if (f)
{
VFS_CLOSE(f);
continue;
}
if (!FS_GenCachedPakName(va("%s.tmp", com_token), crc, tempname, sizeof(tempname)))
continue;
//fixme: request to download it
Con_Printf("Sending request to download %s\n", com_token);
CLQ3_SendClientCommand("download %s.pk3", com_token);
ccs.downloadchunknum = 0;
dl = Z_Malloc(sizeof(*dl));
//q3's downloads are relative to root, but they do at least force a pk3 extension.
Q_snprintfz(dl->localname, sizeof(dl->localname), "package/%s", localname);
Q_snprintfz(dl->tempname, sizeof(dl->tempname), "package/%s", tempname);
dl->prefixbytes = 8;
dl->fsroot = FS_ROOT;
Q_snprintfz(dl->remotename, sizeof(dl->remotename), "%s.pk3", com_token);
dl->method = DL_Q3;
dl->percent = 0;
cls.download = dl;
return true;
}
return false;
}
qboolean CLQ3_SystemInfoChanged(char *str)
@ -466,63 +518,14 @@ qboolean CLQ3_SystemInfoChanged(char *str)
COM_FlushFSCache();
}
if (usingpure)
{
rc = Info_ValueForKey(str, "sv_referencedPaks"); //the ones that we should download.
rn = Info_ValueForKey(str, "sv_referencedPakNames");
rc = Info_ValueForKey(str, "sv_referencedPaks"); //the ones that we should download.
rn = Info_ValueForKey(str, "sv_referencedPakNames");
if (CLQ3_SendDownloads(rc, rn))
return false;
while(rn)
{
char crc[64];
vfsfile_t *f;
rc = COM_ParseOut(rc, crc, sizeof(crc));
rn = COM_Parse(rn);
if (!*com_token)
break;
if (!strchr(com_token, '/')) //don't let some muppet tell us to download quake3.exe
break;
//as much as I'd like to use COM_FCheckExists, this stuf is relative to root, not the gamedir.
f = FS_OpenVFS(va("%s.pk3", com_token), "rb", FS_ROOT);
if (f)
{
VFS_CLOSE(f);
continue;
}
if (!FS_GenCachedPakName(va("%s.pk3", com_token), crc, cls.downloadlocalname, sizeof(cls.downloadlocalname)))
continue;
f = FS_OpenVFS(cls.downloadlocalname, "rb", FS_ROOT);
if (f)
{
VFS_CLOSE(f);
continue;
}
if (!FS_GenCachedPakName(va("%s.tmp", com_token), crc, cls.downloadtempname, sizeof(cls.downloadtempname)))
continue;
//fixme: request to download it
Con_Printf("Sending request to download %s\n", com_token);
CLQ3_SendClientCommand("download %s.pk3", com_token);
ccs.downloadchunknum = 0;
//q3's downloads are relative to root, but they do at least force a pk3 extension.
snprintf(cls.downloadremotename, sizeof(cls.downloadremotename), "%s.pk3", com_token);
cls.downloadmethod = DL_Q3;
cls.downloadpercent = 0;
return false;
}
pc = Info_ValueForKey(str, "sv_paks"); //the ones that we are allowed to use (in order!)
pn = Info_ValueForKey(str, "sv_pakNames");
FS_PureMode(2, pn, pc, ccs.fs_key);
}
else
{
FS_PureMode(0, NULL, NULL, ccs.fs_key);
}
pc = Info_ValueForKey(str, "sv_paks"); //the ones that we are allowed to use (in order!)
pn = Info_ValueForKey(str, "sv_pakNames");
FS_PureMode(usingpure?2:0, pn, pc, rn, rc, ccs.fs_key);
return true; //yay, we're in
}
@ -597,7 +600,10 @@ void CLQ3_ParseGameState(void)
ccs.fs_key = MSG_ReadLong();
if (!CLQ3_SystemInfoChanged(CG_GetConfigString(CFGSTR_SYSINFO)))
{
UI_Restart_f();
return;
}
CG_Restart_f();
UI_Restart_f();
@ -900,13 +906,8 @@ void CLQ3_SendCmd(usercmd_t *cmd)
CLQ3_SendClientCommand("userinfo \"%s\"", cls.userinfo[0]);
}
ccs.serverTime = ccs.snap.serverTime + (Sys_Milliseconds()-ccs.snap.localTime);
cl.servertime = ccs.serverTime / 1000.0f;
cl.gametime = cl.oldgametime = cl.servertime;
//reuse the q1 array
cmd->servertime = ccs.serverTime;
cmd->servertime = cl.servertime*1000;
cmd->weapon = ccs.selected_weapon;
cmd->forwardmove *= 127/400.0f;
@ -933,18 +934,15 @@ void CLQ3_SendCmd(usercmd_t *cmd)
if (Key_Dest_Has(~kdm_game) || (keycatcher&3))
cmd->buttons |= 2; //add in the 'at console' button
cl.outframes[cl.movesequence&Q3UPDATE_MASK].cmd[0] = *cmd;
cl.movesequence++;
cl.outframes[cl.movesequence&CMD_MASK].cmd[0] = *cmd;
frame = &cl.outframes[cls.netchan.outgoing_sequence & Q3UPDATE_MASK];
frame->cmd_sequence = cl.movesequence;
frame = &cl.outframes[cls.netchan.outgoing_sequence & CMD_MASK];
frame->cmd_sequence = cl.movesequence++;
frame->server_message_num = ccs.serverMessageNum;
frame->server_time = cl.gametime;
frame->client_time = Sys_DoubleTime()*1000;
memset(&msg, 0, sizeof(msg));
msg.maxsize = sizeof(data);
msg.data = data;
@ -965,11 +963,11 @@ void CLQ3_SendCmd(usercmd_t *cmd)
MSG_WriteBits(&msg, 0, 8);
}
i = (cls.netchan.outgoing_sequence - 1);
oldframe = &cl.outframes[i & Q3UPDATE_MASK];
i = (cls.netchan.outgoing_sequence-1);
oldframe = &cl.outframes[i & CMD_MASK];
cmdcount = cl.movesequence - oldframe->cmd_sequence;
if (cmdcount > Q3UPDATE_MASK)
cmdcount = Q3UPDATE_MASK;
if (cmdcount > CMD_MASK)
cmdcount = CMD_MASK;
// begin a client move command, if any
if( cmdcount )
{
@ -990,7 +988,7 @@ void CLQ3_SendCmd(usercmd_t *cmd)
// send this and the previous cmds in the message, so
// if the last packet was dropped, it can be recovered
from = &nullcmd;
for (i = oldframe->cmd_sequence; i < cl.movesequence; i++)
for (i = cl.movesequence-cmdcount; i < cl.movesequence; i++)
{
to = &cl.outframes[i&CMD_MASK].cmd[0];
MSG_Q3_WriteDeltaUsercmd( &msg, key, from, to );

View File

@ -186,7 +186,6 @@ typedef struct clientSnap_s {
int serverMessageNum;
int serverCommandNum;
int serverTime; // server time the message is valid for (in msec)
int localTime;
int deltaFrame;
qbyte areabits[MAX_MAP_AREA_BYTES]; // portalarea visibility bits
q3playerState_t playerstate;
@ -224,7 +223,6 @@ typedef struct {
int numClientCommands;
int serverMessageNum;
int serverTime;
int downloadchunknum;

View File

@ -1385,12 +1385,13 @@ static int Con_DrawProgress(int left, int right, int y)
// draw the download bar
// figure out width
if (cls.downloadmethod)
if (cls.download)
{
unsigned int count, total;
unsigned int count;
qofs_t total;
qboolean extra;
progresstext = cls.downloadlocalname;
progresspercent = cls.downloadpercent;
progresstext = cls.download->localname;
progresspercent = cls.download->percent;
CL_GetDownloadSizes(&count, &total, &extra);
@ -1398,7 +1399,7 @@ static int Con_DrawProgress(int left, int right, int y)
sprintf(progresspercenttext, " %5.1f%% (%ukbps)", progresspercent, CL_DownloadRate()/1000);
else
{
sprintf(progresspercenttext, " %5.1f%% (%u%skb)", progresspercent, total/1024, extra?"+":"");
sprintf(progresspercenttext, " %5.1f%% (%u%skb)", progresspercent, (int)(total/1024), extra?"+":"");
}
}
#ifdef RUNTIMELIGHTING

View File

@ -500,7 +500,7 @@ void M_Download_UpdateStatus(struct menu_s *m)
dlmenu_t *info = m->data;
int i;
while (!cls.downloadmethod && (info->parsedsourcenum==-1 || info->parsedsourcenum < numdownloadablelists))
while (!cls.download && (info->parsedsourcenum==-1 || info->parsedsourcenum < numdownloadablelists))
{ //done downloading
char basename[64];

View File

@ -3471,7 +3471,7 @@ static void QCBUILTIN PF_cs_OpenPortal (pubprogfuncs_t *prinst, struct globalvar
int state = G_FLOAT(OFS_PARM1)!=0;
edict_t *portal = G_EDICT(prinst, OFS_PARM0);
int area1 = portal->pvsinfo.areanum, area2 = portal->pvsinfo.areanum2;
if (area1 == area2 || !area1 || !area2)
if (area1 == area2 || area1<0 || area2<0)
return;
CMQ3_SetAreaPortalState(portal->pvsinfo.areanum, portal->pvsinfo.areanum2, state);
}

View File

@ -451,7 +451,7 @@ vfsfile_t *FS_OpenTCP(const char *name, int defaultport);
void FS_UnloadPackFiles(void);
void FS_ReloadPackFiles(void);
char *FSQ3_GenerateClientPacksList(char *buffer, int maxlen, int basechecksum);
void FS_PureMode(int mode, char *packagelist, char *crclist, int seed); //implies an fs_restart
void FS_PureMode(int mode, char *purenamelist, char *purecrclist, char *refnamelist, char *refcrclist, int seed); //implies an fs_restart. ref package names are optional, for q3 where pure names don't contain usable paths
//recursively tries to open files until it can get a zip.
vfsfile_t *CL_OpenFileInPackage(searchpathfuncs_t *search, char *name);

View File

@ -505,6 +505,8 @@ static searchpath_t *com_purepaths;
static searchpath_t *com_base_searchpaths; // without gamedirs
static int fs_puremode; //0=deprioritise pure, 1=prioritise pure, 2=pure only.
static char *fs_refnames; //list of allowed packages
static char *fs_refcrcs; //list of crcs for those packages. one token per package.
static char *fs_purenames; //list of allowed packages
static char *fs_purecrcs; //list of crcs for those packages. one token per package.
static unsigned int fs_pureseed; //used as a key so the server knows we're obeying. completely unreliable/redundant in an open source project, but needed for q3 network compat.
@ -1673,6 +1675,7 @@ void COM_EnumerateFiles (const char *match, int (QDECL *func)(const char *, qofs
void COM_FlushTempoaryPacks(void)
{
#if 0
searchpath_t *sp, **link;
link = &com_searchpaths;
while (*link)
@ -1691,6 +1694,7 @@ void COM_FlushTempoaryPacks(void)
link = &sp->next;
}
com_purepaths = NULL;
#endif
}
qboolean COM_LoadMapPackFile (const char *filename, qofs_t ofs)
@ -2535,26 +2539,31 @@ vfsfile_t *CL_OpenFileInPackage(searchpathfuncs_t *search, char *name)
return NULL;
}
void FS_PureMode(int puremode, char *packagenames, char *packagecrcs, int pureseed)
void FS_PureMode(int puremode, char *purenamelist, char *purecrclist, char *refnamelist, char *refcrclist, int pureseed)
{
qboolean pureflush;
if (puremode == fs_puremode && fs_pureseed == pureseed)
{
if ((!packagenames && !fs_purenames) || !strcmp(fs_purenames?fs_purenames:"", packagenames?packagenames:""))
if ((!packagecrcs && !fs_purecrcs) || !strcmp(fs_purecrcs?fs_purecrcs:"", packagecrcs?packagecrcs:""))
return;
if ((!purenamelist && !fs_purenames) || !strcmp(fs_purenames?fs_purenames:"", purenamelist?purenamelist:""))
if ((!purecrclist && !fs_purecrcs) || !strcmp(fs_purecrcs?fs_purecrcs:"", purecrclist?purecrclist:""))
if ((!refnamelist && !fs_refnames) || !strcmp(fs_refnames?fs_refnames:"", refnamelist?refnamelist:""))
if ((!refcrclist && !fs_refcrcs) || !strcmp(fs_refcrcs?fs_refcrcs:"", refcrclist?refcrclist:""))
return;
}
Z_Free(fs_purenames);
Z_Free(fs_purecrcs);
Z_Free(fs_refnames);
Z_Free(fs_refcrcs);
pureflush = (fs_puremode != 2 && puremode == 2);
fs_puremode = puremode;
fs_purenames = packagenames?Z_StrDup(packagenames):NULL;
fs_purecrcs = packagecrcs?Z_StrDup(packagecrcs):NULL;
fs_purenames = purenamelist?Z_StrDup(purenamelist):NULL;
fs_purecrcs = purecrclist?Z_StrDup(purecrclist):NULL;
fs_pureseed = pureseed;
fs_refnames = refnamelist?Z_StrDup(refnamelist):NULL;
fs_refcrcs = refcrclist?Z_StrDup(refcrclist):NULL;
FS_ChangeGame(fs_manifest, false);
@ -2717,6 +2726,7 @@ void FS_ReloadPackFilesFlags(unsigned int reloadflags)
{
char crctok[64];
char nametok[MAX_QPATH];
char nametok2[MAX_QPATH];
searchpath_t *sp, *lastpure = NULL;
char *names = fs_purenames, *pname;
char *crcs = fs_purecrcs;
@ -2748,6 +2758,27 @@ void FS_ReloadPackFilesFlags(unsigned int reloadflags)
continue;
pname = nametok;
if (fs_refnames && fs_refcrcs)
{ //q3 is annoying as hell
int r;
int crc2;
char *rc = fs_refcrcs;
char *rn = fs_refnames;
pname = "";
for (; rc && rn; )
{
rc = COM_ParseOut(rc, crctok, sizeof(crctok));
rn = COM_ParseOut(rn, nametok2, sizeof(nametok2));
crc2 = strtoul(crctok, NULL, 0);
if (crc2 == crc)
{
COM_DefaultExtension(nametok2, ".pk3", sizeof(nametok2));
pname = nametok2;
break;
}
}
}
if (*pname == '*') // * means that its 'referenced' (read: actually useful) thus should be downloaded, which is not relevent here.
pname++;
@ -2778,7 +2809,7 @@ void FS_ReloadPackFilesFlags(unsigned int reloadflags)
}
}
//if its not already loaded (via wildcards), load it from the download cache, if we can
if (!sp)
if (!sp && *pname)
{
char local[MAX_OSPATH];
vfsfile_t *vfs;
@ -2787,7 +2818,28 @@ void FS_ReloadPackFilesFlags(unsigned int reloadflags)
int i;
if (FS_GenCachedPakName(pname, va("%i", crc), local, sizeof(local)))
{
unsigned int keptflags;
handle = FS_GetOldPath(&oldpaths, local, &keptflags);
if (handle)
{
sp = FS_AddPathHandle(&oldpaths, pname, local, handle, SPF_COPYPROTECTED|SPF_TEMPORARY|keptflags, (unsigned int)-1);
if (sp->crc_check == crc)
{
if (fs_puremode)
{
if (lastpure)
lastpure->nextpure = sp;
else
com_purepaths = sp;
sp->nextpure = NULL;
lastpure = sp;
}
}
continue;
}
vfs = FS_OpenVFS(local, "rb", FS_ROOT);
}
else
vfs = NULL;
if (vfs)

View File

@ -364,7 +364,7 @@ searchpathfuncs_t *QDECL FSPAK_LoadArchive (vfsfile_t *file, const char *desc)
pack->references++;
Con_TPrintf ("Added packfile %s (%i files)\n", desc, numpackfiles);
// Con_TPrintf ("Added packfile %s (%i files)\n", desc, numpackfiles);
pack->pub.fsver = FSVER;
pack->pub.GetPathDetails = FSPAK_GetPathDetails;

View File

@ -245,7 +245,8 @@ typedef struct
{
fsbucket_t bucket;
char name[MAX_QPATH];
qofs_t localpos, filelen;
qofs_t localpos; //location of local header
qofs_t filelen; //uncompressed size
unsigned int crc;
unsigned int flags;
} zpackfile_t;
@ -1094,9 +1095,8 @@ static qboolean FSZIP_EnumerateCentralDirectory(zipfile_t *zip, struct zipinfo *
return success;
}
static qboolean FSZIP_FindEndCentralDirectory(zipfile_t *zip)
static qboolean FSZIP_FindEndCentralDirectory(zipfile_t *zip, struct zipinfo *info)
{
struct zipinfo info;
qboolean result = false;
//zip comment is capped to 65k or so, so we can use a single buffer for this
qbyte traildata[0x10000 + SIZE_ENDOFCENTRALDIRECTORY+SIZE_ZIP64ENDOFCENTRALDIRECTORYLOCATOR];
@ -1108,7 +1108,7 @@ static qboolean FSZIP_FindEndCentralDirectory(zipfile_t *zip)
VFS_SEEK(zip->raw, zip->rawsize - trailsize);
VFS_READ(zip->raw, traildata, trailsize);
memset(&info, 0, sizeof(info));
memset(info, 0, sizeof(*info));
for (magic = traildata+trailsize-SIZE_ENDOFCENTRALDIRECTORY; magic >= traildata; magic--)
{
@ -1117,15 +1117,15 @@ static qboolean FSZIP_FindEndCentralDirectory(zipfile_t *zip)
magic[2] == 5 &&
magic[3] == 6)
{
info.centraldir_end = (zip->rawsize-trailsize)+(magic-traildata);
info->centraldir_end = (zip->rawsize-trailsize)+(magic-traildata);
info.thisdisk = LittleU2FromPtr(magic+4);
info.centraldir_startdisk = LittleU2FromPtr(magic+6);
info.centraldir_numfiles_disk = LittleU2FromPtr(magic+8);
info.centraldir_numfiles_all = LittleU2FromPtr(magic+10);
info.centraldir_size = LittleU4FromPtr(magic+12);
info.centraldir_offset = LittleU4FromPtr(magic+16);
info.commentlength = LittleU2FromPtr(magic+20);
info->thisdisk = LittleU2FromPtr(magic+4);
info->centraldir_startdisk = LittleU2FromPtr(magic+6);
info->centraldir_numfiles_disk = LittleU2FromPtr(magic+8);
info->centraldir_numfiles_all = LittleU2FromPtr(magic+10);
info->centraldir_size = LittleU4FromPtr(magic+12);
info->centraldir_offset = LittleU4FromPtr(magic+16);
info->commentlength = LittleU2FromPtr(magic+20);
result = true;
break;
@ -1148,17 +1148,17 @@ static qboolean FSZIP_FindEndCentralDirectory(zipfile_t *zip)
{
qbyte z64eocd[SIZE_ZIP64ENDOFCENTRALDIRECTORY];
info.zip64_centraldirend_disk = LittleU4FromPtr(magic+4);
info.zip64_centraldirend_offset = LittleU8FromPtr(magic+8);
info.zip64_diskcount = LittleU4FromPtr(magic+16);
info->zip64_centraldirend_disk = LittleU4FromPtr(magic+4);
info->zip64_centraldirend_offset = LittleU8FromPtr(magic+8);
info->zip64_diskcount = LittleU4FromPtr(magic+16);
if (info.zip64_diskcount != 1 || info.zip64_centraldirend_disk != 0)
if (info->zip64_diskcount != 1 || info->zip64_centraldirend_disk != 0)
{
Con_Printf("zip: archive is spanned\n");
return false;
}
VFS_SEEK(zip->raw, info.zip64_centraldirend_offset);
VFS_SEEK(zip->raw, info->zip64_centraldirend_offset);
VFS_READ(zip->raw, z64eocd, sizeof(z64eocd));
if (z64eocd[0] == 'P' &&
@ -1166,27 +1166,27 @@ static qboolean FSZIP_FindEndCentralDirectory(zipfile_t *zip)
z64eocd[2] == 6 &&
z64eocd[3] == 6)
{
info.zip64_eocdsize = LittleU8FromPtr(z64eocd+4) + 12;
info.zip64_version_madeby = LittleU2FromPtr(z64eocd+12);
info.zip64_version_needed = LittleU2FromPtr(z64eocd+14);
info.thisdisk = LittleU4FromPtr(z64eocd+16);
info.centraldir_startdisk = LittleU4FromPtr(z64eocd+20);
info.centraldir_numfiles_disk = LittleU8FromPtr(z64eocd+24);
info.centraldir_numfiles_all = LittleU8FromPtr(z64eocd+32);
info.centraldir_size = LittleU8FromPtr(z64eocd+40);
info.centraldir_offset = LittleU8FromPtr(z64eocd+48);
info->zip64_eocdsize = LittleU8FromPtr(z64eocd+4) + 12;
info->zip64_version_madeby = LittleU2FromPtr(z64eocd+12);
info->zip64_version_needed = LittleU2FromPtr(z64eocd+14);
info->thisdisk = LittleU4FromPtr(z64eocd+16);
info->centraldir_startdisk = LittleU4FromPtr(z64eocd+20);
info->centraldir_numfiles_disk = LittleU8FromPtr(z64eocd+24);
info->centraldir_numfiles_all = LittleU8FromPtr(z64eocd+32);
info->centraldir_size = LittleU8FromPtr(z64eocd+40);
info->centraldir_offset = LittleU8FromPtr(z64eocd+48);
if (info.zip64_eocdsize >= 84)
if (info->zip64_eocdsize >= 84)
{
info.centraldir_compressionmethod = LittleU2FromPtr(z64eocd+56);
// info.zip64_2_centraldir_csize = LittleU8FromPtr(z64eocd+58);
// info.zip64_2_centraldir_usize = LittleU8FromPtr(z64eocd+66);
info.centraldir_algid = LittleU2FromPtr(z64eocd+74);
info->centraldir_compressionmethod = LittleU2FromPtr(z64eocd+56);
// info->zip64_2_centraldir_csize = LittleU8FromPtr(z64eocd+58);
// info->zip64_2_centraldir_usize = LittleU8FromPtr(z64eocd+66);
info->centraldir_algid = LittleU2FromPtr(z64eocd+74);
// info.zip64_2_bitlen = LittleU2FromPtr(z64eocd+76);
// info.zip64_2_flags = LittleU2FromPtr(z64eocd+78);
// info.zip64_2_hashid = LittleU2FromPtr(z64eocd+80);
// info.zip64_2_hashlength = LittleU2FromPtr(z64eocd+82);
//info.zip64_2_hashdata = LittleUXFromPtr(z64eocd+84, info.zip64_2_hashlength);
// info->zip64_2_flags = LittleU2FromPtr(z64eocd+78);
// info->zip64_2_hashid = LittleU2FromPtr(z64eocd+80);
// info->zip64_2_hashlength = LittleU2FromPtr(z64eocd+82);
//info->zip64_2_hashdata = LittleUXFromPtr(z64eocd+84, info->zip64_2_hashlength);
}
}
else
@ -1199,28 +1199,17 @@ static qboolean FSZIP_FindEndCentralDirectory(zipfile_t *zip)
}
}
if (info.thisdisk || info.centraldir_startdisk || info.centraldir_numfiles_disk != info.centraldir_numfiles_all)
if (info->thisdisk || info->centraldir_startdisk || info->centraldir_numfiles_disk != info->centraldir_numfiles_all)
{
Con_Printf("zip: archive is spanned\n");
result = false;
}
if (info.centraldir_compressionmethod || info.centraldir_algid)
if (info->centraldir_compressionmethod || info->centraldir_algid)
{
Con_Printf("zip: encrypted centraldir\n");
result = false;
}
if (result)
{
result = FSZIP_EnumerateCentralDirectory(zip, &info);
if (!result && !info.zip64_diskcount)
{
//uh oh... the central directory wasn't where it was meant to be!
//assuming that the endofcentraldir is packed at the true end of the centraldir (and that we're not zip64 and thus don't have an extra block), then we can guess based upon the offset difference
info.zipoffset = info.centraldir_end - (info.centraldir_offset+info.centraldir_size);
result = FSZIP_EnumerateCentralDirectory(zip, &info);
}
}
return result;
}
@ -1237,42 +1226,46 @@ of the list so they override previous pack files.
searchpathfuncs_t *QDECL FSZIP_LoadArchive (vfsfile_t *packhandle, const char *desc)
{
zipfile_t *zip;
struct zipinfo info;
zip = Z_Malloc(sizeof(zipfile_t));
Q_strncpyz(zip->filename, desc, sizeof(zip->filename));
zip->raw = packhandle;
zip->rawsize = VFS_GETLEN(zip->raw);
if (!FSZIP_FindEndCentralDirectory(zip))
//find the footer
if (!FSZIP_FindEndCentralDirectory(zip, &info))
{
Z_Free(zip);
Con_TPrintf ("Failed opening zipfile \"%s\" corrupt?\n", desc);
return NULL;
}
/*
for (i = 0; i < zip->numfiles; i++)
//find the central directory.
if (!FSZIP_FindEndCentralDirectory(zip, &info))
{
if (unzGetCurrentFileInfo (zip->handle, &file_info, newfiles[i].name, sizeof(newfiles[i].name), NULL, 0, NULL, 0) != UNZ_OK)
Con_Printf("Zip Error\n");
Q_strlwr(newfiles[i].name);
if (!*newfiles[i].name || newfiles[i].name[strlen(newfiles[i].name)-1] == '/')
newfiles[i].filelen = -1;
else
newfiles[i].filelen = file_info.uncompressed_size;
newfiles[i].filepos = file_info.c_offset;
nextfileziphandle = unzGoToNextFile (zip->handle);
if (nextfileziphandle == UNZ_END_OF_LIST_OF_FILE)
break;
else if (nextfileziphandle != UNZ_OK)
Con_Printf("Zip Error\n");
Z_Free(zip);
Con_TPrintf ("Failed opening zipfile \"%s\" corrupt?\n", desc);
return NULL;
}
//now read it.
if (!FSZIP_EnumerateCentralDirectory(zip, &info) && !info.zip64_diskcount)
{
//uh oh... the central directory wasn't where it was meant to be!
//assuming that the endofcentraldir is packed at the true end of the centraldir (and that we're not zip64 and thus don't have an extra block), then we can guess based upon the offset difference
info.zipoffset = info.centraldir_end - (info.centraldir_offset+info.centraldir_size);
if (FSZIP_EnumerateCentralDirectory(zip, &info))
{
Z_Free(zip);
Con_TPrintf ("zipfile \"%s\" appears to be missing its central directory\n", desc);
return NULL;
}
}
*/
zip->references = 1;
Con_TPrintf ("Added zipfile %s (%i files)\n", desc, zip->numfiles);
// Con_TPrintf ("Added zipfile %s (%i files)\n", desc, zip->numfiles);
zip->pub.fsver = FSVER;
zip->pub.GetPathDetails = FSZIP_GetPathDetails;
@ -1286,5 +1279,144 @@ searchpathfuncs_t *QDECL FSZIP_LoadArchive (vfsfile_t *packhandle, const char *d
return &zip->pub;
}
#if 0
typedef struct
{
struct archivedeltafuncs_s
{
//called when the downloader needs to know a new target region. each call should return a new region, eventually returning the entire file as data is injected from elsewhere
//return false on error.
qboolean (*GetNextRange) (struct archivedeltafuncs_s *archive, qofs_t *start, qofs_t *end);
//called when the download completes/aborts. frees memory.
void (*Finish) (struct archivedeltafuncs_s);
} pub;
qdownload_t *dl;
enum
{
step_eocd,
step_cd,
step_file
} step;
struct
{
qofs_t start;
qofs_t end;
} eocd;
zipfile_t *old;
zipfile_t zip;
struct zipinfo info;
} ziparchivedelta_t;
void FSZIPDL_Finish(struct archivedeltafuncs_s *archive)
{
ziparchivedelta_t *za = (ziparchivedelta_t*)archive;
za->old->pub.ClosePath(&za->old->pub);
if (za->zip.files)
Z_Free(za->zip.files);
Z_Free(za);
}
qboolean FSZIPDL_GetNextRange(struct archivedeltafuncs_s *archive, qofs_t *start, qofs_t *end)
{
flocation_t loc;
int i;
ziparchivedelta_t *za = (ziparchivedelta_t*)archive;
switch(za->step)
{
case step_eocd:
//find the header (actually, the trailer)
*start = za->eocd.start;
*end = za->eocd.end;
za->step++;
break;
case step_cd:
if (!FSZIP_FindEndCentralDirectory(&za->zip, &za->info))
return false; //corrupt/unusable.
//central directory should be located at this position
*start = za->info.centraldir_offset+za->info.zipoffset;
*end = za->info.centraldir_end+za->info.zipoffset;
za->step++;
break;
case step_file:
if (FSZIP_EnumerateCentralDirectory(&za->zip, &za->info))
{
return false; //couldn't find the central directory properly (fixme: this can happen with zip32 self-extractors)
}
//walk through the central directory looking for matching files in the existing versions of the zip
for (i = 0; i < za->zip.numfiles; i++)
if (FSZIP_FLocate(&za->old->pub, &loc, za->zip.files[i].name, NULL) != FF_NOTFOUND)
{
if (za->old->files[loc.index].crc == za->zip.files[i].crc)
{
qofs_t datastart, datasize;
if (FSZIP_ValidateLocalHeader(za->old, &za->old->files[loc.index], &datastart, &datasize))
{
size_t chunk;
datastart = za->old->files[loc.index].localpos;
//tell the download context that we already know this data region
DL_Completed(za->dl, datastart, datastart + datasize);
//and inject the data into the downloaded file.
VFS_SEEK(za->old->raw, datastart);
VFS_SEEK(za->dl->file, za->zip.files[i].localpos);
for(;datasize;)
{
char copybuffer[0x10000];
chunk = datasize;
if (chunk > sizeof(copybuffer))
chunk = sizeof(copybuffer);
VFS_READ(za->old->raw, copybuffer, chunk);
datasize -= chunk;
}
//FIXME: graphics/progress updates.
}
}
}
//anything we didn't receive yet needs to be completed. the download code is meant to track the holes.
*start = 0;
*end = za->eocd.end;
za->step++;
break;
default:
return false;
}
return true;
}
struct archivedeltafuncs_s *FSZIP_OpenDeltaZip(qdownload_t *dl)
{
ziparchivedelta_t *za;
unsigned int trailsize = 0x10000 + SIZE_ENDOFCENTRALDIRECTORY+SIZE_ZIP64ENDOFCENTRALDIRECTORYLOCATOR;
za->eocd.end = dl->size;
if (dl->size < trailsize)
za->eocd.start = 0;
else
za->eocd.start = dl->size - trailsize;
za->dl = dl;
za->zip.rawsize = dl->size;
za->zip.raw = dl->file;
za->pub.GetNextRange = FSZIPDL_GetNextRange;
return &za->pub;
}
#endif
#endif

View File

@ -3218,7 +3218,7 @@ qboolean CModQ3_LoadLeafs (lump_t *l)
out->minmaxs[3+j] = LittleLong(in->maxs[j]);
}
out->cluster = LittleLong(in->cluster);
out->area = LittleLong(in->area) + 1;
out->area = LittleLong(in->area);
// out->firstleafface = LittleLong(in->firstleafsurface);
// out->numleaffaces = LittleLong(in->num_leafsurfaces);
out->contents = 0;
@ -6103,7 +6103,7 @@ static void FloodArea_r (q2carea_t *area, int floodnum)
area->floodvalid = floodvalid;
if (mapisq3)
{
for (i=1 ; i<numareas ; i++)
for (i=0 ; i<numareas ; i++)
{
if (map_q3areas[area - map_q2areas].numareaportals[i]>0)
FloodArea_r (&map_q2areas[i], floodnum);
@ -6138,7 +6138,7 @@ static void FloodAreaConnections (void)
floodnum = 0;
// area 0 is not used
for (i=1 ; i<numareas ; i++)
for (i=0 ; i<numareas ; i++)
{
area = &map_q2areas[i];
if (area->floodvalid == floodvalid)
@ -6170,7 +6170,7 @@ void CMQ3_SetAreaPortalState (unsigned int area1, unsigned int area2, qboolean o
return;
// Host_Error ("CMQ3_SetAreaPortalState on non-q3 map");
if (area1 > numareas || area2 > numareas)
if (area1 >= numareas || area2 >= numareas)
Host_Error ("CMQ3_SetAreaPortalState: area > numareas");
if (open)
@ -6192,6 +6192,8 @@ qboolean VARGS CM_AreasConnected (model_t *mod, unsigned int area1, unsigned int
if (map_noareas.value)
return true;
if (area1 == ~0 || area2 == ~0)
return area1 == area2;
if (area1 > numareas || area2 > numareas)
Host_Error ("area > numareas");
@ -6228,7 +6230,7 @@ int CM_WriteAreaBits (model_t *mod, qbyte *buffer, int area)
memset (buffer, 0, bytes);
floodnum = map_q2areas[area].floodnum;
for (i=1 ; i<numareas ; i++)
for (i=0 ; i<numareas ; i++)
{
if (map_q2areas[i].floodnum == floodnum || !area)
buffer[i>>3] |= 1<<(i&7);

View File

@ -2301,6 +2301,8 @@ void BE_GenPolyBatches(batch_t **batches)
return;
shader = cl_stris[i].shader;
if (!shader)
continue;
b->buildmeshes = R_DB_Poly;
b->ent = &r_worldentity;

View File

@ -519,10 +519,13 @@ qboolean GLInitialise (char *renderer)
RECT centerrect(unsigned int parentleft, unsigned int parenttop, unsigned int parentwidth, unsigned int parentheight, unsigned int cwidth, unsigned int cheight)
{
RECT r;
if (!vid_width.ival)
cwidth = parentwidth;
if (!vid_height.ival)
cheight = parentwidth;
if (modestate!=MS_WINDOWED)
{
if (!vid_width.ival)
cwidth = parentwidth;
if (!vid_height.ival)
cheight = parentwidth;
}
if (parentwidth < cwidth)
{
@ -557,6 +560,8 @@ qboolean VID_SetWindowedMode (rendererstate_t *info)
int wwidth, wheight, pleft, ptop, pwidth, pheight;
RECT rect;
modestate = MS_WINDOWED;
hdc = GetDC(NULL);
if (GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE)
{
@ -671,8 +676,6 @@ qboolean VID_SetWindowedMode (rendererstate_t *info)
// ShowWindow (dibwindow, SW_SHOWDEFAULT);
// UpdateWindow (dibwindow);
modestate = MS_WINDOWED;
// because we have set the background brush for the window to NULL
// (to avoid flickering when re-sizing the window on the desktop),
// we clear the window to black when created, otherwise it will be

View File

@ -894,21 +894,13 @@ struct dl_download *DL_Create(const char *url)
return newdl;
}
static struct dl_download *showndownload;
/*destroys an entire download context*/
void DL_Close(struct dl_download *dl)
{
#ifndef NPFTE
if (showndownload == dl)
{
if (cls.downloadmethod == DL_HTTP)
{
cls.downloadmethod = DL_NONE;
*cls.downloadlocalname = *cls.downloadremotename = 0;
}
showndownload = NULL;
}
if (cls.download == &dl->qdownload)
cls.download = NULL;
#endif
#ifdef MULTITHREAD
@ -930,7 +922,6 @@ void DL_Close(struct dl_download *dl)
#ifndef NPFTE
static struct dl_download *activedownloads;
unsigned int shownbytestart;
/*create a download context and add it to the list, for lazy people. not threaded*/
struct dl_download *HTTP_CL_Get(const char *url, const char *localfile, void (*NotifyFunction)(struct dl_download *dl))
{
@ -995,34 +986,27 @@ void HTTP_CL_Think(void)
}
link = &dl->next;
if (!cls.downloadmethod)
if (!cls.download)
{
cls.downloadmethod = DL_HTTP;
showndownload = dl;
cls.download = &dl->qdownload;
dl->qdownload.method = DL_HTTP;
if (*dl->localname)
strcpy(cls.downloadlocalname, dl->localname);
strcpy(dl->qdownload.localname, dl->localname);
else
strcpy(cls.downloadlocalname, dl->url);
strcpy(cls.downloadremotename, dl->url);
cls.downloadstarttime = Sys_DoubleTime();
cls.downloadedbytes = 0;
shownbytestart = dl->completed;
}
if (cls.downloadmethod == DL_HTTP)
{
if (showndownload == dl)
{
if (dl->status == DL_FINISHED)
cls.downloadpercent = 100;
else if (dl->status != DL_ACTIVE)
cls.downloadpercent = 0;
else if (dl->totalsize <= 0)
cls.downloadpercent = 50;
else
cls.downloadpercent = dl->completed*100.0f/dl->totalsize;
cls.downloadedbytes = dl->completed;
}
strcpy(dl->qdownload.localname, dl->url);
strcpy(dl->qdownload.remotename, dl->url);
dl->qdownload.starttime = Sys_DoubleTime();
}
if (dl->status == DL_FINISHED)
dl->qdownload.percent = 100;
else if (dl->status != DL_ACTIVE)
dl->qdownload.percent = 0;
else if (dl->totalsize <= 0)
dl->qdownload.percent = 50;
else
dl->qdownload.percent = dl->completed*100.0f/dl->totalsize;
dl->qdownload.completedbytes = dl->completed;
}
}
#endif

View File

@ -103,6 +103,8 @@ struct dl_download
unsigned int user_num;
void *user_ctx;
qdownload_t qdownload;
/*stream config*/
char url[MAX_OSPATH]; /*original url*/
char redir[MAX_OSPATH]; /*current redirected url*/

View File

@ -3105,9 +3105,9 @@ void QCC_PR_Lex (void)
// if the first character is a valid identifier, parse until a non-id
// character is reached
if ((c == '~' || c == '%') && pr_file_p[1] >= '0' && pr_file_p[1] <= '9') //let's see which one we make into an operator first... possibly both...
if ((c == '%') && pr_file_p[1] >= '0' && pr_file_p[1] <= '9') //let's see which one we make into an operator first... possibly both...
{
QCC_PR_ParseWarning(0, "~ or %% prefixes to denote integers are deprecated. Please use a postfix of 'i'");
QCC_PR_ParseWarning(0, "%% prefixes to denote integers are deprecated. Please use a postfix of 'i'");
pr_file_p++;
pr_token_type = tt_immediate;
pr_immediate_type = type_integer;

View File

@ -6269,7 +6269,7 @@ static void QCBUILTIN PF_OpenPortal (pubprogfuncs_t *prinst, struct globalvars_s
client_t *client;
edict_t *portal = G_EDICT(prinst, OFS_PARM0);
int area1 = portal->pvsinfo.areanum, area2 = portal->pvsinfo.areanum2;
if (area1 == area2 || !area1 || !area2)
if (area1 == area2 || area1<0 || area2<0)
return;
for (client = svs.clients, i = 0; i < sv.allocated_client_slots; i++, client++)
{

View File

@ -2746,10 +2746,16 @@ unsigned int Q2BSP_FatPVS(model_t *mod, vec3_t org, qbyte *buffer, unsigned int
qboolean Q2BSP_EdictInFatPVS(model_t *mod, pvscache_t *ent, qbyte *pvs)
{
int i,l;
if (!CM_AreasConnected (mod, clientarea, ent->areanum))
int nullarea = (mod->fromgame == fg_quake2)?0:-1;
if (clientarea == ent->areanum)
{
if (clientarea == nullarea)
return false;
}
else if (!CM_AreasConnected (mod, clientarea, ent->areanum))
{ // doors can legally straddle two areas, so
// we may need to check another one
if (!ent->areanum2
if (ent->areanum2 == nullarea
|| !CM_AreasConnected (mod, clientarea, ent->areanum2))
return false; // blocked by a door
}

View File

@ -604,7 +604,7 @@ void SVNQ_New_f (void)
// set view
MSG_WriteByte (&host_client->netchan.message, svc_setview);
MSG_WriteEntity (&host_client->netchan.message, host_client - svs.clients);//NUM_FOR_EDICT(svprogfuncs, host_client->edict));
MSG_WriteEntity (&host_client->netchan.message, (host_client - svs.clients)+1);//NUM_FOR_EDICT(svprogfuncs, host_client->edict));
MSG_WriteByte (&host_client->netchan.message, svc_signonnum);
MSG_WriteByte (&host_client->netchan.message, 1);
@ -3063,7 +3063,7 @@ void SV_StopDownload_f(void)
host_client->download = NULL;
}
else
SV_ClientPrintf(host_client, PRINT_HIGH, "But you're not downloading anything\n");
SV_ClientPrintf(host_client, PRINT_HIGH, "Can't stop download - not downloading anything\n");
host_client->downloadstarted = false;
}

View File

@ -283,7 +283,7 @@ static void Q3G_LinkEntity(q3sharedEntity_t *ent)
{
clusters[i] = CM_LeafCluster(sv.world.worldmodel, leafs[i]);
area = CM_LeafArea(sv.world.worldmodel, leafs[i]);
if(area > 0) //FIXME: this should be >=
if(area >= 0)
{
// doors may legally straggle two areas,
// but nothing should ever need more than that
@ -2982,11 +2982,38 @@ void SVQ3_UpdateUserinfo_f(client_t *cl)
VM_Call(q3gamevm, GAME_CLIENT_USERINFO_CHANGED, (int)(cl-svs.clients));
}
void SVQ3_Drop_f(client_t *cl)
static void SVQ3_Drop_f(client_t *cl)
{
SV_DropClient(cl);
}
//part of the sv_pure mechanism. verifies the client's pack list and kicks if they're wrong.
//safe to ignore, if you're okay with potential cheats.
static void SVQ3_ClientPacks_f(client_t *cl)
{
}
static void SVQ3_Download_f(client_t *cl)
{
//clients might end up waiting for the download which will never come.
//kick them so that doesn't happen. downloads are not supported at this time. not even reporting failure! :s
SV_DropClient(cl);
// short 0
// long -1
}
static void SVQ3_NextDL_f(client_t *cl)
{
//send next chunk
}
static void SVQ3_StopDL_f(client_t *cl)
{
//abort/close current download, if any
}
static void SVQ3_DoneDL_f(client_t *cl)
{
//send new gamestate
}
typedef struct ucmd_s
{
char *name;
@ -2998,11 +3025,11 @@ static const ucmd_t ucmds[] =
{"userinfo", SVQ3_UpdateUserinfo_f},
{"disconnect", SVQ3_Drop_f},
{"cp", NULL},
{"download", NULL},
{"nextdl", NULL},
{"stopdl", NULL},
{"donedl", NULL},
{"cp", SVQ3_ClientPacks_f},
{"download", SVQ3_Download_f},
{"nextdl", SVQ3_NextDL_f},
{"stopdl", SVQ3_StopDL_f},
{"donedl", SVQ3_DoneDL_f},
{NULL, NULL}
};