diff --git a/engine/Makefile b/engine/Makefile index cf48aaf33..9cadbd8a8 100644 --- a/engine/Makefile +++ b/engine/Makefile @@ -473,7 +473,7 @@ ALL_CXXFLAGS=$(subst -Wno-pointer-sign,,$(ALL_CFLAGS)) #cheap compile-everything-in-one-unit (compile becomes preprocess only) ifneq ($(WPO),) LTO_CC= -E - LTO_LD= -combine -fwhole-program -x c + LTO_LD= -flto -fwhole-program -x c LTO_END=ltoxnone LTO_START=ltoxc endif diff --git a/engine/client/cl_input.c b/engine/client/cl_input.c index 6e401807a..70969e29c 100644 --- a/engine/client/cl_input.c +++ b/engine/client/cl_input.c @@ -706,14 +706,11 @@ void CL_BaseMove (usercmd_t *cmd, int pnum, float priortime, float extratime) CL_GatherButtons(cmd, pnum); } -void CL_ClampPitch (int pnum) +static void CL_ClampPitch (int pnum, float frametime) { float mat[16]; float roll; - static float oldtime; - float timestep = realtime - oldtime; playerview_t *pv = &cl.playerview[pnum]; - oldtime = realtime; if (cl.intermissionmode != IM_NONE) { @@ -819,17 +816,17 @@ void CL_ClampPitch (int pnum) } else { - if (fabs(vang[ROLL]) < host_frametime*180) + if (fabs(vang[ROLL]) < frametime*180) vang[ROLL] = 0; else if (vang[ROLL] > 0) { // Con_Printf("Roll %f\n", vang[ROLL]); - vang[ROLL] -= host_frametime*180; + vang[ROLL] -= frametime*180; } else { // Con_Printf("Roll %f\n", vang[ROLL]); - vang[ROLL] += host_frametime*180; + vang[ROLL] += frametime*180; } } VectorClear(pv->viewanglechange); @@ -904,11 +901,11 @@ void CL_ClampPitch (int pnum) // cl.viewangles[pnum][ROLL] = 50; // if (cl.viewangles[pnum][ROLL] < -50) // cl.viewangles[pnum][ROLL] = -50; - roll = timestep*pv->viewangles[ROLL]*30; + roll = frametime*pv->viewangles[ROLL]*30; if ((pv->viewangles[ROLL]-roll < 0) != (pv->viewangles[ROLL]<0)) pv->viewangles[ROLL] = 0; else - pv->viewangles[ROLL] -= timestep*pv->viewangles[ROLL]*3; + pv->viewangles[ROLL] -= frametime*pv->viewangles[ROLL]*3; } /* @@ -920,7 +917,7 @@ static void CL_FinishMove (usercmd_t *cmd, int pnum) { int i; - CL_ClampPitch(pnum); + CL_ClampPitch(pnum, 0); // // always dump the first two message, because it may contain leftover inputs @@ -1869,6 +1866,7 @@ void CL_SendCmd (double frametime, qboolean mainloop) cl_pendingcmd[plnum].forwardmove += mousemovements[0]; cl_pendingcmd[plnum].sidemove += mousemovements[1]; cl_pendingcmd[plnum].upmove += mousemovements[2]; + CL_ClampPitch(plnum, frametime); // if we are spectator, try autocam if (pv->spectator) @@ -2021,7 +2019,7 @@ void CL_SendCmd (double frametime, qboolean mainloop) CL_AdjustAngles (plnum, frametime); VectorClear(mousemovements); IN_Move (mousemovements, plnum, frametime); - CL_ClampPitch(plnum); + CL_ClampPitch(plnum, frametime); cl_pendingcmd[plnum].forwardmove += mousemovements[0]; //FIXME: this will get nuked by CL_BaseMove. cl_pendingcmd[plnum].sidemove += mousemovements[1]; cl_pendingcmd[plnum].upmove += mousemovements[2]; diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 996da91a2..81a5f6d4d 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -1026,7 +1026,7 @@ void CL_CheckForResend (void) t1 = Sys_DoubleTime (); if (!connectinfo.istransfer) { - host = strrchr(cls.servername, '@'); + host = strrchr(cls.servername+1, '@'); if (host) host++; else @@ -1087,7 +1087,7 @@ void CL_CheckForResend (void) Con_TPrintf ("Connecting to %s...\n", cls.servername); if (connectinfo.tries == 0) - if (!NET_EnsureRoute(cls.sockets, "conn", cls.servername)) + if (!NET_EnsureRoute(cls.sockets, "conn", cls.servername, &connectinfo.adr)) { Con_Printf ("Unable to establish connection to %s\n", cls.servername); connectinfo.trying = false; @@ -1564,12 +1564,14 @@ void CL_ResetFog(int ftype) ===================== CL_ClearState +gamestart==true says that we're changing map, as opposed to servers. ===================== */ -void CL_ClearState (void) +void CL_ClearState (qboolean gamestart) { extern cvar_t cfg_save_auto; int i, j; + downloadlist_t *pendingdownloads, *faileddownloads; #ifndef CLIENTONLY #define serverrunning (sv.state != ss_dead) #define tolocalserver NET_IsLoopBackAddress(&cls.netchan.remote_address) @@ -1655,6 +1657,7 @@ void CL_ClearState (void) Z_Free(t); } + if (!gamestart) { downloadlist_t *next; while(cl.downloadlist) @@ -1670,6 +1673,8 @@ void CL_ClearState (void) cl.faileddownloads = next; } } + pendingdownloads = cl.downloadlist; + faileddownloads = cl.faileddownloads; #ifdef Q2CLIENT for (i = 0; i < countof(cl.configstring_general); i++) @@ -1741,6 +1746,8 @@ void CL_ClearState (void) cl.splitclients = 1; cl.autotrack_hint = -1; cl.autotrack_killer = -1; + cl.downloadlist = pendingdownloads; + cl.faileddownloads = faileddownloads; if (cfg_save_auto.ival && Cvar_UnsavedArchive()) Cmd_ExecuteString("cfg_save\n", RESTRICT_LOCAL); @@ -1879,7 +1886,7 @@ void CL_Disconnect (void) Cvar_ForceSet(&cl_servername, "none"); - CL_ClearState(); + CL_ClearState(false); FS_PureMode(0, NULL, NULL, NULL, NULL, 0); @@ -3779,7 +3786,7 @@ void CL_ReadPackets (void) //============================================================================= -qboolean CL_AllowArbitaryDownload(char *oldname, char *localfile) +qboolean CL_AllowArbitaryDownload(const char *oldname, const char *localfile) { int allow; //never allow certain (native code) arbitary downloads. @@ -3813,6 +3820,114 @@ qboolean CL_AllowArbitaryDownload(char *oldname, char *localfile) return false; } +#if defined(NQPROT) && !defined(NOLEGACY) +//this is for DP compat. +static void CL_Curl_f(void) +{ + //curl --args url + int i, argc = Cmd_Argc(); + const char *arg, *gamedir, *localterse/*no dlcache*/= NULL; + char localname[MAX_QPATH]; + int usage = 0; + qboolean alreadyhave = false; + extern char cl_dp_packagenames[4096]; + unsigned int dlflags = DLLF_VERBOSE; + if (argc < 2) + { + Con_Printf("%s: No args\n", Cmd_Argv(0)); + return; + } +// Con_Printf("%s %s\n", Cmd_Argv(0), Cmd_Args()); + for (i = 1; i < argc; i++) + { + arg = Cmd_Argv(i); + if (!strcmp(arg, "--info")) + { + Con_Printf("%s %s: not implemented\n", Cmd_Argv(0), arg); + return; + } + else if (!strcmp(arg, "--cancel")) + { + Con_Printf("%s %s: not implemented\n", Cmd_Argv(0), arg); + return; + } + else if (!strcmp(arg, "--pak")) + usage |= 1; + else if (!strcmp(arg, "--cachepic")) + usage |= 2; + else if (!strcmp(arg, "--skinframe")) + usage |= 4; + else if (!strcmp(arg, "--for")) + { + alreadyhave = true; //assume we have it. + for (i++; i < argc-1; i++) + { + arg = Cmd_Argv(i); + if (!CL_CheckDLFile(arg)) + { + alreadyhave = false; + break; + } + } + } + else if (!strcmp(arg, "--forthismap")) + { + //'don't reconnect on failure' + //though I'm guessing its better expressed as just flagging it as mandatory. + dlflags |= DLLF_REQUIRED; + } + else if (!strcmp(arg, "--as")) + { + //explicit local filename + localterse = Cmd_Argv(++i); + } + else if (!strcmp(arg, "--clear_autodownload")) + { + *cl_dp_packagenames = 0; + return; + } + else if (!strcmp(arg, "--finish_autodownload")) + { + //not really sure why this is needed +// Con_Printf("%s %s: not implemented\n", Cmd_Argv(0), arg); + return; + } + else if (!strcmp(arg, "--maxspeed=")) + ; + else if (*arg == '-') + Con_Printf("%s: Unknown option %s\n", Cmd_Argv(0), arg); + else + ; //usually just the last arg, but may also be some parameter for an unknown arg. + } + arg = Cmd_Argv(argc-1); + if (!localterse) + { + //for compat, we should look for the last / and truncate on a ?. + Con_Printf("%s: skipping download of %s, as the local name was not explicitly given\n", Cmd_Argv(0), arg); + return; + } + if (usage == 1) + { + dlflags |= DLLF_NONGAME; + gamedir = FS_GetGamedir(true); + FS_GenCachedPakName(va("%s/%s", gamedir, localterse), NULL, localname, sizeof(localname)); + + if (!alreadyhave) + if (!CL_CheckOrEnqueDownloadFile(arg, localname, dlflags)) + Con_Printf("Downloading %s to %s\n", arg, localname); + + if (*cl_dp_packagenames) + Q_strncatz(cl_dp_packagenames, " ", sizeof(cl_dp_packagenames)); + Q_strncatz(cl_dp_packagenames, va("%s/%s", gamedir, localterse), sizeof(cl_dp_packagenames)); + } + else + { + Con_Printf("%s: %s: non-package downloads are not supported\n", Cmd_Argv(0), arg); + return; + } +} +#endif + /* ===================== CL_Download_f @@ -4518,6 +4633,9 @@ void CL_Init (void) Cmd_AddCommand ("fullinfo", CL_FullInfo_f); Cmd_AddCommand ("color", CL_Color_f); +#if defined(NQPROT) && !defined(NOLEGACY) + Cmd_AddCommand ("curl", CL_Curl_f); +#endif Cmd_AddCommand ("download", CL_Download_f); Cmd_AddCommandD ("dlsize", CL_DownloadSize_f, "For internal use"); Cmd_AddCommandD ("nextul", CL_NextUpload, "For internal use"); @@ -4566,7 +4684,7 @@ void CL_Init (void) #ifdef QUAKEHUD Stats_Init(); #endif - CL_ClearState(); //make sure the cl.* fields are set properly if there's no ssqc or whatever. + CL_ClearState(false); //make sure the cl.* fields are set properly if there's no ssqc or whatever. } diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index 6cf1d50a7..98328be1a 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -36,6 +36,7 @@ static void DLC_Poll(qdownload_t *dl); static void CL_ProcessUserInfo (int slot, player_info_t *player); #ifdef NQPROT +char cl_dp_packagenames[4096]; static char cl_dp_csqc_progsname[128]; static int cl_dp_csqc_progssize; static int cl_dp_csqc_progscrc; @@ -712,27 +713,35 @@ static void CL_SendDownloadStartRequest(char *filename, char *localname, unsigne static int dlsequence; qdownload_t *dl; + //don't download multiple things at once... its leaky if nothing else. + if (cls.download) + return; + #ifdef WEBCLIENT if (!strncmp(filename, "http://", 7) || !strncmp(filename, "https://", 8)) { - if (!cls.download || !(cls.download->flags & DLLF_ALLOWWEB)) + struct dl_download *wdl = HTTP_CL_Get(filename, localname, CL_WebDownloadFinished); + if (wdl) { - struct dl_download *wdl = HTTP_CL_Get(filename, localname, CL_WebDownloadFinished); - if (wdl) + if (flags & DLLF_NONGAME) { - if (!(flags & DLLF_TEMPORARY)) - Con_TPrintf ("Downloading %s to %s...\n", wdl->url, wdl->localname); - wdl->qdownload.flags = flags; - cls.download = &wdl->qdownload; + wdl->fsroot = FS_ROOT; + if (!strncmp(localname, "package/", 8)) + Q_strncpyz(wdl->localname, localname+8, sizeof(wdl->localname)); } - else - CL_DownloadFailed(filename, NULL); + if (!(flags & DLLF_TEMPORARY)) + Con_TPrintf ("Downloading %s to %s...\n", wdl->url, wdl->localname); + wdl->qdownload.flags = flags; + + CL_DisenqueDownload(filename); + + cls.download = &wdl->qdownload; } + else + CL_DownloadFailed(filename, NULL); return; } #endif - if (cls.download) - return; //no! dl = Z_Malloc(sizeof(*dl)); dl->filesequence = ++dlsequence; @@ -785,7 +794,7 @@ void CL_DownloadFinished(qdownload_t *dl) //should probably ask the filesytem code if its a package format instead. - if (!strncmp(filename, "package/", 8) || !strncmp(ext, "pk4", 3) || !strncmp(ext, "pk3", 3) || !strncmp(ext, "pak", 3)) + if (!strncmp(filename, "package/", 8) || !strncmp(ext, "pk4", 3) || !strncmp(ext, "pk3", 3) || !strncmp(ext, "pak", 3) || (dl->fsroot == FS_ROOT)) { FS_ReloadPackFiles(); CL_CheckServerInfo(); @@ -815,7 +824,9 @@ void CL_DownloadFinished(qdownload_t *dl) { if (!strcmp(cl.model_name[i], filename)) { - cl.model_precache[i] = Mod_ForName(cl.model_name[i], MLV_WARN); //throw away result. + if (cl.model_precache[i] && cl.model_precache[i]->loadstate == MLS_FAILED) + cl.model_precache[i]->loadstate = MLS_NOTLOADED; + cl.model_precache[i] = Mod_ForName(cl.model_name[i], MLV_WARN); if (i == 1) cl.worldmodel = cl.model_precache[i]; break; @@ -825,7 +836,9 @@ void CL_DownloadFinished(qdownload_t *dl) { if (!strcmp(cl.model_csqcname[i], filename)) { - cl.model_csqcprecache[i] = Mod_ForName(cl.model_csqcname[i], MLV_WARN); //throw away result. + if (cl.model_csqcprecache[i] && cl.model_csqcprecache[i]->loadstate == MLS_FAILED) + cl.model_csqcprecache[i]->loadstate = MLS_NOTLOADED; + cl.model_csqcprecache[i] = Mod_ForName(cl.model_csqcname[i], MLV_WARN); break; } } @@ -834,6 +847,8 @@ void CL_DownloadFinished(qdownload_t *dl) { if (!strcmp(cl.model_name_vwep[i], filename)) { + if (cl.model_precache_vwep[i] && cl.model_precache_vwep[i]->loadstate == MLS_FAILED) + cl.model_precache_vwep[i]->loadstate = MLS_NOTLOADED; cl.model_precache_vwep[i] = Mod_ForName(cl.model_name_vwep[i], MLV_WARN); break; } @@ -1194,34 +1209,42 @@ static int CL_LoadModels(int stage, qboolean dontactuallyload) pmove.numphysent = 0; pmove.physents[0].model = NULL; -/*#ifdef CSQC_DAT - if (atstage()) - { +#if defined(CSQC_DAT) && defined(NQPROT) + if (cls.protocol == CP_NETQUAKE && atstage()) + { //we only need this for nq. for qw we checked for downloads with the other stuff. + //there are also too many possible names to load... :( extern cvar_t cl_nocsqc; - if (cls.protocol == CP_NETQUAKE && !cl_nocsqc.ival && !cls.demoplayback) + if (!cl_nocsqc.ival && !cls.demoplayback) { - char *s; + const char *cscrc = InfoBuf_ValueForKey(&cl.serverinfo, "*csprogs"); + const char *cssize = InfoBuf_ValueForKey(&cl.serverinfo, "*csprogssize"); + const char *csname = InfoBuf_ValueForKey(&cl.serverinfo, "*csprogsname"); + unsigned int chksum = strtoul(cscrc, NULL, 0); + size_t chksize = strtoul(cssize, NULL, 0); SCR_SetLoadingFile("csprogs"); - s = Info_ValueForKey(cl.serverinfo, "*csprogs"); - if (*s) //only allow csqc if the server says so, and the 'checksum' matches. + if (!*csname) + csname = "csprogs.dat"; + if (*cscrc && !CSQC_CheckDownload(csname, chksum, chksize)) //only allow csqc if the server says so, and the 'checksum' matches. { extern cvar_t cl_download_csprogs; - unsigned int chksum = strtoul(s, NULL, 0); + unsigned int chksum = strtoul(cscrc, NULL, 0); if (cl_download_csprogs.ival) { char *str = va("csprogsvers/%x.dat", chksum); - if (CL_CheckOrEnqueDownloadFile("csprogs.dat", str, DLLF_REQUIRED)) - return stage; //its kinda required + if (CL_IsDownloading(str)) + return -1; //don't progress to loading it while we're still downloading it. + if (CL_CheckOrEnqueDownloadFile(csname, str, DLLF_REQUIRED)) + return -1; //its kinda required } else { - Con_Printf("Not downloading csprogs.dat due to allow_download_csprogs\n"); + Con_Printf("Not downloading csprogs.dat due to %s\n", cl_download_csprogs.name); } } } endstage(); } -#endif*/ +#endif #ifdef HLCLIENT if (atstage()) @@ -1239,19 +1262,25 @@ static int CL_LoadModels(int stage, qboolean dontactuallyload) qboolean anycsqc; char *endptr; unsigned int chksum; + size_t progsize; + const char *progsname; anycsqc = atoi(InfoBuf_ValueForKey(&cl.serverinfo, "anycsqc")); if (cls.demoplayback) anycsqc = true; + s = InfoBuf_ValueForKey(&cl.serverinfo, "*csprogssize"); + progsize = strtoul(s, NULL, 0); s = InfoBuf_ValueForKey(&cl.serverinfo, "*csprogs"); chksum = strtoul(s, &endptr, 0); + progsname = InfoBuf_ValueForKey(&cl.serverinfo, "*csprogsname"); if (*endptr) { Con_Printf("corrupt *csprogs key in serverinfo\n"); anycsqc = true; chksum = 0; } + progsname = *s?InfoBuf_ValueForKey(&cl.serverinfo, "*csprogsname"):NULL; SCR_SetLoadingFile("csprogs"); - if (!CSQC_Init(anycsqc, *s?true:false, chksum)) + if (!CSQC_Init(anycsqc, progsname, chksum, progsize)) { Sbar_Start(); //try and start this before we're actually on the server, //this'll stop the mod from sending so much stuffed data at us, whilst we're frozen while trying to load. @@ -2700,13 +2729,25 @@ static void CLDP_ParseDownloadData(void) if (dl->file) { - VFS_SEEK(dl->file, start); - VFS_WRITE(dl->file, buffer, size); + if (start > dl->completedbytes) + ; //this protocol cannot deal with gaps. we might as well wait until its repeated later. + else if (start+size < dl->completedbytes) + ; //already completed this data + else + { + int offset = dl->completedbytes-start; //we may already have completed some chunk already - dl->percent = (start+size) / (float)VFS_GETLEN(dl->file) * 100; + VFS_WRITE(dl->file, buffer+offset, size-offset); + dl->completedbytes += size-offset; + dl->ratebytes += size-offset; //for download rate calcs + } + + dl->percent = (start+size) / (float)dl->size * 100; } - //this is only reliable because I'm lazy + //we need to ack in order. + //the server doesn't actually track packets, only position, however there's no way to tell it that we already have a chunk + //we could send the acks unreliably, but any cl->sv loss would involve a sv->cl resend (because we can't dupe). MSG_WriteByte(&cls.netchan.message, clcdp_ackdownloaddata); MSG_WriteLong(&cls.netchan.message, start); MSG_WriteShort(&cls.netchan.message, size); @@ -3133,7 +3174,7 @@ static void CLQW_ParseServerData (void) #endif } - CL_ClearState (); + CL_ClearState (true); #ifdef QUAKEHUD Stats_NewMap(); #endif @@ -3408,7 +3449,7 @@ static void CLQ2_ParseServerData (void) //FTE doesn't actually have a timescale cvar, so create one to 'fool' q2admin. //I can't really blame q2admin for rejecting engines that don't have this cvar, as it could have been renamed via a hex-edit. - CL_ClearState (); + CL_ClearState (true); CLQ2_ClearState (); cl.minpitch = -89; cl.maxpitch = 89; @@ -3504,7 +3545,9 @@ static void CLQ2_ParseServerData (void) void CL_ParseEstablished(void) { #ifdef NQPROT + *cl_dp_packagenames = 0; cl_dp_serverextension_download = false; + *cl_dp_csqc_progsname = 0; cl_dp_csqc_progscrc = 0; cl_dp_csqc_progssize = 0; #endif @@ -3671,7 +3714,7 @@ static void CLNQ_ParseServerData(void) //Doesn't change gamedir - use with caut int gametype; Con_DPrintf ("Serverdata packet %s.\n", cls.demoplayback?"read":"received"); SCR_SetLoadingStage(LS_CLIENT); - CL_ClearState (); + CL_ClearState (true); #ifdef QUAKEHUD Stats_NewMap(); #endif @@ -3776,6 +3819,24 @@ static void CLNQ_ParseServerData(void) //Doesn't change gamedir - use with caut InfoBuf_SetStarKey(&cl.serverinfo, "*csprogsname", va("%s", cl_dp_csqc_progsname)); } + if (*cl_dp_packagenames) + { + char *in = cl_dp_packagenames; + while (*in) + { + in = COM_Parse(in); + + if (*cl.serverpaknames) + Q_strncatz(cl.serverpaknames, " ", sizeof(cl.serverpaknames)); + Q_strncatz(cl.serverpaknames, com_token, sizeof(cl.serverpaknames)); + if (*cl.serverpakcrcs) + Q_strncatz(cl.serverpakcrcs, " ", sizeof(cl.serverpakcrcs)); + Q_strncatz(cl.serverpakcrcs, "-", sizeof(cl.serverpakcrcs)); //we don't have any crc info. we'll instead need this info as part of the filename. + cl.serverpakschanged = true; + } + } + + //update gamemode if (gametype != GAME_COOP) InfoBuf_SetKey(&cl.serverinfo, "deathmatch", "1"); diff --git a/engine/client/client.h b/engine/client/client.h index 7fd1a376c..3c8eb48d6 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -396,9 +396,9 @@ typedef struct qdownload_s 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; + double ratetime; //periodically updated + int rate; //ratebytes/ratetimedelta + int ratebytes; //updated by download reception code, and cleared when ratetime is bumped unsigned int flags; //chunked downloads uses this @@ -425,7 +425,7 @@ enum qdlabort }; qboolean DL_Begun(qdownload_t *dl); void DL_Abort(qdownload_t *dl, enum qdlabort aborttype); //just frees the download's resources. does not delete the temp file. -qboolean CL_AllowArbitaryDownload(char *oldname, char *localfile); +qboolean CL_AllowArbitaryDownload(const char *oldname, const char *localfile); // @@ -1185,11 +1185,10 @@ enum beamtype_e typedef struct beam_s beam_t; beam_t *CL_AddBeam (enum beamtype_e tent, int ent, vec3_t start, vec3_t end); -void CL_ClearState (void); +void CL_ClearState (qboolean gamestart); void CLQ2_ClearState(void); void CL_ReadPackets (void); -void CL_ClampPitch (int pnum); int CL_ReadFromServer (void); void CL_WriteToServer (usercmd_t *cmd); @@ -1398,7 +1397,8 @@ qboolean CSQC_Inited(void); void CSQC_RendererRestarted(void); qboolean CSQC_UnconnectedOkay(qboolean inprinciple); qboolean CSQC_UnconnectedInit(void); -qboolean CSQC_Init (qboolean anycsqc, qboolean csdatenabled, unsigned int checksum); +qboolean CSQC_CheckDownload(const char *name, unsigned int checksum, size_t checksize); //reports whether we already have a usable csprogs.dat +qboolean CSQC_Init (qboolean anycsqc, const char *csprogsname, unsigned int checksum, size_t progssize); qboolean CSQC_ConsoleLink(char *text, char *info); void CSQC_RegisterCvarsAndThings(void); qboolean CSQC_SetupToRenderPortal(int entnum); diff --git a/engine/client/clq3_parse.c b/engine/client/clq3_parse.c index 966795eb5..04e8f3efa 100644 --- a/engine/client/clq3_parse.c +++ b/engine/client/clq3_parse.c @@ -539,7 +539,7 @@ void CLQ3_ParseGameState(void) // // wipe the client_state_t struct // - CL_ClearState(); + CL_ClearState(true); ccs.firstParseEntity = 0; memset(ccs.parseEntities, 0, sizeof(ccs.parseEntities)); memset(ccs.baselines, 0, sizeof(ccs.baselines)); diff --git a/engine/client/console.c b/engine/client/console.c index d78867d28..e01114b69 100644 --- a/engine/client/console.c +++ b/engine/client/console.c @@ -1176,6 +1176,27 @@ void VARGS Con_DLPrintf (int level, const char *fmt, ...) } } +void VARGS Con_ThrottlePrintf (float *timer, int developerlevel, const char *fmt, ...) +{ + va_list argptr; + char msg[MAXPRINTMSG]; + float now = realtime; + + if (*timer > now) + ; //in the future? zomg + else if (*timer > now-1) + return; //within the last second + *timer = now; //in the future? zomg + + va_start (argptr,fmt); + vsnprintf (msg,sizeof(msg)-1, fmt,argptr); + va_end (argptr); + + if (developerlevel) + Con_DLPrintf(developerlevel, "%s", msg); + else + Con_Printf("%s", msg); +} /*description text at the bottom of the console*/ void Con_Footerf(console_t *con, qboolean append, const char *fmt, ...) @@ -2640,7 +2661,7 @@ void Con_DrawConsole (int lines, qboolean noback) key = Info_ValueForKey(info, "tiprawimg"); if (*key) { - shader = R2D_SafeCachePic("riprawimg"); + shader = R2D_SafeCachePic("tiprawimg"); shader->defaulttextures->base = Image_FindTexture(key, NULL, IF_NOREPLACE|IF_PREMULTIPLYALPHA); if (!shader->defaulttextures->base) { @@ -2707,6 +2728,16 @@ void Con_DrawConsole (int lines, qboolean noback) else shader = NULL; } + if (iw > (vid.width/4.0)) + { + ih *= (vid.width/4.0)/iw; + iw *= (vid.width/4.0)/iw; + } + if (ih > (vid.height/4.0)) + { + iw *= (vid.width/4.0)/ih; + ih *= (vid.width/4.0)/ih; + } if (x + iw/2 + 8 + 256 > vid.width) x = vid.width - (iw/2 + 8 + 256); diff --git a/engine/client/m_mp3.c b/engine/client/m_mp3.c index 53d192f4c..845b7becc 100644 --- a/engine/client/m_mp3.c +++ b/engine/client/m_mp3.c @@ -5030,7 +5030,7 @@ typedef struct #define MPEGLAYER3_ID_MPEG 1 #endif -qboolean QDECL S_LoadMP3Sound (sfx_t *s, qbyte *data, size_t datalen, int sndspeed) +static qboolean QDECL S_LoadMP3Sound (sfx_t *s, qbyte *data, size_t datalen, int sndspeed, qboolean forcedecode) { WAVEFORMATEX pcm_format; MPEGLAYER3WAVEFORMAT mp3format; diff --git a/engine/client/m_options.c b/engine/client/m_options.c index b57b80027..ef2f0beef 100644 --- a/engine/client/m_options.c +++ b/engine/client/m_options.c @@ -2967,7 +2967,7 @@ void M_Menu_Video_f (void) MC_AddRedText(menu, 200, y, current3dres, false); y+=8; y+=8; - MC_AddRedText(menu, 0, y, " ", false); y+=8; + MC_AddRedText(menu, 0, y, " ^Ue080^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue082 ", false); y+=8; y+=8; info->renderer = MC_AddCombo(menu, 16, y, " Renderer", rendererops, i); y+=8; info->bppcombo = MC_AddCombo(menu, 16, y, " Color Depth", bppnames, currentbpp); y+=8; diff --git a/engine/client/m_single.c b/engine/client/m_single.c index 881b81621..64ac420c1 100644 --- a/engine/client/m_single.c +++ b/engine/client/m_single.c @@ -385,7 +385,7 @@ void M_Menu_SinglePlayer_f (void) } else if (!strncmp(Cmd_Argv(1), "skill", 5)) { - //yes, hexen2 has per-class names for the skill levels. because being weird and obtuse is kinda its fort + //yes, hexen2 has per-class names for the skill levels. because being weird and obtuse is kinda its forte static char *skillnames[6][4] = { { diff --git a/engine/client/merged.h b/engine/client/merged.h index bf4f41bcc..dc63cc4f0 100644 --- a/engine/client/merged.h +++ b/engine/client/merged.h @@ -44,7 +44,12 @@ typedef enum #define MAX_BONE_CONTROLLERS 5 #endif -#define FRAME_BLENDS 4 +#ifdef NOLEGACY +#define FRAME_BLENDS 2 +#else +#define FRAME_BLENDS 4 //for compat with DP (for mods that want 4-way blending yet refuse to use framegroups properly). real mods should be using skeletal objects allowing for N-way blending. +#endif + #define FST_BASE 0 //base frames #define FS_REG 1 //regular frames #define FS_COUNT 2 //regular frames diff --git a/engine/client/net_master.c b/engine/client/net_master.c index d89c423de..1383e599c 100644 --- a/engine/client/net_master.c +++ b/engine/client/net_master.c @@ -368,7 +368,7 @@ void SV_Master_Worker_Resolved(void *ctx, void *data, size_t a, size_t b) { //tcp masters require a route if (NET_AddrIsReliable(na)) - NET_EnsureRoute(svs.sockets, master->cv.name, master->cv.string); + NET_EnsureRoute(svs.sockets, master->cv.name, master->cv.string, na); //q2+qw masters are given a ping to verify that they're still up switch (master->protocol) @@ -684,14 +684,14 @@ int slist_customkeys; #define POLLUDP4SOCKETS 64 //it's big so we can have lots of messages when behind a firewall. Basically if a firewall only allows replys, and only remembers 3 servers per socket, we need this big cos it can take a while for a packet to find a fast optimised route and we might be waiting for a few secs for a reply the first time around. int lastpollsockUDP4; -#ifdef IPPROTO_IPV6 +#ifdef HAVE_IPV6 #define POLLUDP6SOCKETS 4 //it's non-zero so we can have lots of messages when behind a firewall. Basically if a firewall only allows replys, and only remembers 3 servers per socket, we need this big cos it can take a while for a packet to find a fast optimised route and we might be waiting for a few secs for a reply the first time around. int lastpollsockUDP6; #else #define POLLUDP6SOCKETS 0 #endif -#ifdef USEIPX +#ifdef HAVE_IPX #define POLLIPXSOCKETS 2 //ipx isn't used as much. In fact, we only expect local servers to be using it. I'm not sure why I implemented it anyway. You might see a q2 server using it. Rarely. int lastpollsockIPX; #else @@ -1800,7 +1800,7 @@ qboolean NET_SendPollPacket(int len, void *data, netadr_t to) char buf[128]; NetadrToSockadr (&to, &addr); -#ifdef USEIPX +#ifdef HAVE_IPX if (((struct sockaddr*)&addr)->sa_family == AF_IPX) { lastpollsockIPX++; @@ -1923,7 +1923,7 @@ int Master_CheckPollSockets(void) continue; if (e == NET_EMSGSIZE) { - SockadrToNetadr (&from, &net_from); + SockadrToNetadr (&from, fromlen, &net_from); Con_Printf ("Warning: Oversize packet from %s\n", NET_AdrToString (adr, sizeof(adr), &net_from)); continue; @@ -1938,7 +1938,7 @@ int Master_CheckPollSockets(void) Con_Printf ("NET_CheckPollSockets: %i, %s\n", e, strerror(e)); continue; } - SockadrToNetadr (&from, &net_from); + SockadrToNetadr (&from, fromlen, &net_from); net_message.cursize = ret; if (ret >= sizeof(net_message_buffer) ) @@ -1968,7 +1968,7 @@ int Master_CheckPollSockets(void) CL_ReadServerInfo(MSG_ReadString(), MP_QUAKE2, false); continue; } -#ifdef IPPROTO_IPV6 +#ifdef HAVE_IPV6 if (!strncmp(s, "server6", 7)) //parse a bit more... { msg_readcount = c+7; @@ -1991,7 +1991,7 @@ int Master_CheckPollSockets(void) } #endif -#ifdef IPPROTO_IPV6 +#ifdef HAVE_IPV6 if (!strncmp(s, "getserversResponse6", 19) && (s[19] == '\\' || s[19] == '/')) //parse a bit more... { msg_readcount = c+19-1; @@ -2017,7 +2017,7 @@ int Master_CheckPollSockets(void) continue; } -#ifdef IPPROTO_IPV6 +#ifdef HAVE_IPV6 if (!strncmp(s, "qw_slist6\\", 10)) //parse a bit more... { msg_readcount = c+9-1; @@ -2450,7 +2450,6 @@ void MasterInfo_ProcessHTTPJSON(struct dl_download *dl) //don't try sending to servers we don't support void MasterInfo_Request(master_t *mast) { - //static int mastersequence; // warning: unused variable mastersequence if (!mast) return; @@ -2645,21 +2644,21 @@ void MasterInfo_Refresh(qboolean doreset) Master_AddMaster("255.255.255.255:"STRINGIFY(PORT_DEFAULTSERVER), MT_BCAST, MP_DPMASTER, "Nearby Game Servers."); #ifndef QUAKETC Master_AddMaster("255.255.255.255:"STRINGIFY(PORT_QWSERVER), MT_BCAST, MP_QUAKEWORLD, "Nearby QuakeWorld UDP servers."); - Master_AddMasterHTTP("http://www.gameaholic.com/servers/qspy-quakeworld", MT_MASTERHTTP, MP_QUAKEWORLD, "gameaholic's QW master"); +// Master_AddMasterHTTP("http://www.gameaholic.com/servers/qspy-quakeworld", MT_MASTERHTTP, MP_QUAKEWORLD, "gameaholic's QW master"); Master_AddMasterHTTP("https://www.quakeservers.net/lists/servers/global.txt",MT_MASTERHTTP, MP_QUAKEWORLD, "QuakeServers.net (http)"); #endif #ifdef NQPROT - Master_AddMasterHTTP("http://www.gameaholic.com/servers/qspy-quake", MT_MASTERHTTP, MP_NETQUAKE, "gameaholic's NQ master"); +// Master_AddMasterHTTP("http://www.gameaholic.com/servers/qspy-quake", MT_MASTERHTTP, MP_NETQUAKE, "gameaholic's NQ master"); // Master_AddMasterHTTP("http://servers.quakeone.com/index.php?format=json", MT_MASTERHTTPJSON, MP_NETQUAKE, "quakeone's server listing"); Master_AddMaster("255.255.255.255:"STRINGIFY(PORT_NQSERVER), MT_BCAST, MP_NETQUAKE, "Nearby Quake1 servers"); - Master_AddMaster("255.255.255.255:"STRINGIFY(PORT_NQSERVER), MT_BCAST, MP_DPMASTER, "Nearby DarkPlaces servers"); + Master_AddMaster("255.255.255.255:"STRINGIFY(PORT_NQSERVER), MT_BCAST, MP_DPMASTER, "Nearby DarkPlaces servers"); //only responds to one type, depending on active protocol. #endif #ifdef Q2CLIENT - Master_AddMasterHTTP("http://www.gameaholic.com/servers/qspy-quake2", MT_MASTERHTTP, MP_QUAKE2, "gameaholic's Q2 master"); +// Master_AddMasterHTTP("http://www.gameaholic.com/servers/qspy-quake2", MT_MASTERHTTP, MP_QUAKE2, "gameaholic's Q2 master"); Master_AddMaster("255.255.255.255:27910", MT_BCAST, MP_QUAKE2, "Nearby Quake2 UDP servers."); #endif #ifdef Q3CLIENT - Master_AddMasterHTTP("http://www.gameaholic.com/servers/qspy-quake3", MT_MASTERHTTP, MP_QUAKE3, "gameaholic's Q3 master"); +// Master_AddMasterHTTP("http://www.gameaholic.com/servers/qspy-quake3", MT_MASTERHTTP, MP_QUAKE3, "gameaholic's Q3 master"); Master_AddMaster("255.255.255.255:"STRINGIFY(PORT_Q3SERVER), MT_BCAST, MP_QUAKE3, "Nearby Quake3 UDP servers."); #endif diff --git a/engine/client/pr_csqc.c b/engine/client/pr_csqc.c index 4cfa34fd1..74d474c87 100644 --- a/engine/client/pr_csqc.c +++ b/engine/client/pr_csqc.c @@ -52,6 +52,8 @@ typedef struct csqctreadstate_s { static qboolean csprogs_promiscuous; static unsigned int csprogs_checksum; +static size_t csprogs_checksize; +static char csprogs_checkname[MAX_QPATH]; static csqctreadstate_t *csqcthreads; qboolean csqc_resortfrags; world_t csqc_world; @@ -584,7 +586,7 @@ static const char *csqcmapentitydata; static qboolean csqcmapentitydataloaded; static unsigned int csqc_deprecated_warned; -#define csqc_deprecated(s) do {if (!csqc_deprecated_warned++){Con_Printf("csqc warning: %s\n", s); PR_StackTrace (prinst, false);}}while(0) +#define csqc_deprecated(s) do {if (!csqc_deprecated_warned++){Con_Printf(CON_WARNING"csqc deprecation warning: %s\n", s); PR_StackTrace (prinst, false);}}while(0) static model_t *CSQC_GetModelForIndex(int index); @@ -600,7 +602,7 @@ static void CS_CheckVelocity(csqcedict_t *ent) -static void cs_getframestate(csqcedict_t *in, unsigned int rflags, framestate_t *out) +static void cs_getframestate(csqcedict_t *in, unsigned int rflags, framestate_t *fte_restrict out) { //FTE_CSQC_HALFLIFE_MODELS #ifdef HALFLIFEMODELS @@ -623,12 +625,18 @@ static void cs_getframestate(csqcedict_t *in, unsigned int rflags, framestate_t out->g[FST_BASE].frame[0] = in->xv->baseframe; out->g[FST_BASE].frame[1] = in->xv->baseframe2; - //out->g[FST_BASE].frame[2] = in->xv->baseframe3; - //out->g[FST_BASE].frame[3] = in->xv->baseframe4; out->g[FST_BASE].lerpweight[1] = in->xv->baselerpfrac; + //out->g[FST_BASE].frame[3] = in->xv->baseframe4; + +#if 0//FRAME_BLENDS >= 4 +// out->g[FST_BASE].frame[2] = in->xv->baseframe3; // out->g[FST_BASE].lerpweight[2] = in->xv->baselerpfrac3; +// out->g[FST_BASE].frame[3] = in->xv->baseframe3; // out->g[FST_BASE].lerpweight[3] = in->xv->baselerpfrac4; + out->g[FST_BASE].lerpweight[0] = 1-(out->g[FST_BASE].lerpweight[1]+out->g[FST_BASE].lerpweight[2]+out->g[FST_BASE].lerpweight[3]); +#else out->g[FST_BASE].lerpweight[0] = 1-(out->g[FST_BASE].lerpweight[1]); +#endif if (rflags & CSQCRF_FRAMETIMESARESTARTTIMES) { out->g[FST_BASE].frametime[0] = *csqcg.simtime - in->xv->baseframe1time; @@ -649,25 +657,33 @@ static void cs_getframestate(csqcedict_t *in, unsigned int rflags, framestate_t out->g[FS_REG].endbone = 0x7fffffff; out->g[FS_REG].frame[0] = in->v->frame; out->g[FS_REG].frame[1] = in->xv->frame2; - out->g[FS_REG].frame[2] = in->xv->frame3; - out->g[FS_REG].frame[3] = in->xv->frame4; out->g[FS_REG].lerpweight[1] = in->xv->lerpfrac; +#if FRAME_BLENDS >= 4 + out->g[FS_REG].frame[2] = in->xv->frame3; out->g[FS_REG].lerpweight[2] = in->xv->lerpfrac3; + out->g[FS_REG].frame[3] = in->xv->frame4; out->g[FS_REG].lerpweight[3] = in->xv->lerpfrac4; out->g[FS_REG].lerpweight[0] = 1-(out->g[FS_REG].lerpweight[1]+out->g[FS_REG].lerpweight[2]+out->g[FS_REG].lerpweight[3]); +#else + out->g[FS_REG].lerpweight[0] = 1-(out->g[FS_REG].lerpweight[1]); +#endif if ((rflags & CSQCRF_FRAMETIMESARESTARTTIMES) || csqc_isdarkplaces) { out->g[FS_REG].frametime[0] = *csqcg.simtime - in->xv->frame1time; out->g[FS_REG].frametime[1] = *csqcg.simtime - in->xv->frame2time; - out->g[FS_REG].frametime[2] = 0;//*csqcg.simtime - in->xv->frame3time; - out->g[FS_REG].frametime[3] = 0;//*csqcg.simtime - in->xv->frame4time; +#if FRAME_BLENDS >= 4 + out->g[FS_REG].frametime[2] = *csqcg.simtime - in->xv->frame3time; + out->g[FS_REG].frametime[3] = *csqcg.simtime - in->xv->frame4time; +#endif } else { out->g[FS_REG].frametime[0] = in->xv->frame1time; out->g[FS_REG].frametime[1] = in->xv->frame2time; - out->g[FS_REG].frametime[2] = 0;//in->xv->frame3time; - out->g[FS_REG].frametime[3] = 0;//in->xv->frame4time; +#if FRAME_BLENDS >= 4 + out->g[FS_REG].frametime[2] = in->xv->frame3time; + out->g[FS_REG].frametime[3] = in->xv->frame4time; +#endif } @@ -728,7 +744,7 @@ static void QCBUILTIN PF_cvar (pubprogfuncs_t *prinst, struct globalvars_s *pr_g } else { - var = Cvar_Get(str, "", 0, "csqc cvars"); + var = PF_Cvar_FindOrGet (str); if (var && !(var->flags & CVAR_NOUNSAFEEXPAND)) G_FLOAT(OFS_RETURN) = var->value; else @@ -1833,8 +1849,16 @@ static void QCBUILTIN PF_cs_project (pubprogfuncs_t *prinst, struct globalvars_s out[1] = 1-(1+tempv[1])/2; out[2] = tempv[2]; - out[0] = out[0]*r_refdef.vrect.width + r_refdef.vrect.x; - out[1] = out[1]*r_refdef.vrect.height + r_refdef.vrect.y; + if (csqc_isdarkplaces) + { /*sigh*/ + out[0] = out[0]*vid.width + r_refdef.vrect.x; + out[1] = out[1]*vid.height + r_refdef.vrect.y; + } + else + { + out[0] = out[0]*r_refdef.vrect.width + r_refdef.vrect.x; + out[1] = out[1]*r_refdef.vrect.height + r_refdef.vrect.y; + } if (tempv[3] < 0) out[2] *= -1; @@ -1852,8 +1876,16 @@ static void QCBUILTIN PF_cs_unproject (pubprogfuncs_t *prinst, struct globalvars float v[4], tempv[4]; - tx = ((tx-r_refdef.vrect.x)/r_refdef.vrect.width); - ty = ((ty-r_refdef.vrect.y)/r_refdef.vrect.height); + if (csqc_isdarkplaces) + { /*sigh*/ + tx = ((tx-r_refdef.vrect.x)/vid.width); + ty = ((ty-r_refdef.vrect.y)/vid.height); + } + else + { + tx = ((tx-r_refdef.vrect.x)/r_refdef.vrect.width); + ty = ((ty-r_refdef.vrect.y)/r_refdef.vrect.height); + } ty = 1-ty; v[0] = tx*2-1; v[1] = ty*2-1; @@ -2532,7 +2564,7 @@ static void QCBUILTIN PF_cs_SetSize (pubprogfuncs_t *prinst, struct globalvars_s World_LinkEdict (w, (wedict_t*)e, false); } -static void cs_settracevars(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals, trace_t *tr) +static void cs_settracevars(pubprogfuncs_t *prinst, trace_t *tr) { *csqcg.trace_allsolid = tr->allsolid; *csqcg.trace_startsolid = tr->startsolid; @@ -2596,7 +2628,7 @@ static void QCBUILTIN PF_cs_traceline(pubprogfuncs_t *prinst, struct globalvars_ trace = World_Move (&csqc_world, v1, mins, maxs, v2, nomonsters|MOVE_IGNOREHULL, (wedict_t*)ent); - cs_settracevars(prinst, pr_globals, &trace); + cs_settracevars(prinst, &trace); } static void QCBUILTIN PF_cs_tracebox(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { @@ -2614,7 +2646,7 @@ static void QCBUILTIN PF_cs_tracebox(pubprogfuncs_t *prinst, struct globalvars_s trace = World_Move (&csqc_world, v1, mins, maxs, v2, nomonsters|MOVE_IGNOREHULL, (wedict_t*)ent); - cs_settracevars(prinst, pr_globals, &trace); + cs_settracevars(prinst, &trace); } static trace_t CS_Trace_Toss (csqcedict_t *tossent, csqcedict_t *ignore) @@ -2668,7 +2700,7 @@ static void QCBUILTIN PF_cs_tracetoss (pubprogfuncs_t *prinst, struct globalvars trace = CS_Trace_Toss (ent, ignore); - cs_settracevars(prinst, pr_globals, &trace); + cs_settracevars(prinst, &trace); } static void QCBUILTIN PF_cs_pointcontents(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) @@ -3116,7 +3148,29 @@ static void QCBUILTIN PF_cs_boxparticles(pubprogfuncs_t *prinst, struct globalva float count = G_FLOAT(OFS_PARM6); int flags = (prinst->callargc < 7)?0:G_FLOAT(OFS_PARM7); - if (flags & 128) +/* if (flags & 1) //PARTICLES_USEALPHA + { + float *alphamin = (float*)PR_FindGlobal(csqcprogs, "particles_alphamin", 0, NULL); + float *alphamax = (float*)PR_FindGlobal(csqcprogs, "particles_alphamax", 0, NULL); + if (alphamin && alphamax) + ; + } + if (flags & 2) //PARTICLES_USECOLOR + { //rgb vectors + float *colourmin = (float*)PR_FindGlobal(csqcprogs, "particles_colormin", 0, NULL); + float *colourmax = (float*)PR_FindGlobal(csqcprogs, "particles_colormax", 0, NULL); + if (colourmin && colourmax) + ; + } + if (flags & 4) //PARTICLES_USEFADE + { + float *fade = (float*)PR_FindGlobal(csqcprogs, "particles_fade", 0, NULL); + if (fade) + ; + } +*/ + + if (flags & 128) //PARTICLES_DRAWASTRAIL { flags &= ~128; P_ParticleTrail(org_from, org_to, effectnum, 0, NULL, NULL); @@ -3126,8 +3180,11 @@ static void QCBUILTIN PF_cs_boxparticles(pubprogfuncs_t *prinst, struct globalva P_RunParticleCube(effectnum, org_from, org_to, vel_from, vel_to, count, 0, true, 0); } - if (flags) - Con_DPrintf("PF_cs_boxparticles: flags & %x is not supported\n", flags); + if (flags & ~128) + { + static float throttletimer; + Con_ThrottlePrintf(&throttletimer, 1, "PF_cs_boxparticles: flags & %x is not supported\n", flags); + } } static void QCBUILTIN PF_cs_pointparticles (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) @@ -4852,7 +4909,7 @@ static void QCBUILTIN PF_cs_walkmove (pubprogfuncs_t *prinst, struct globalvars_ // save program state, because CS_movestep may call other progs oldself = *csqcg.self; - G_FLOAT(OFS_RETURN) = World_movestep(&csqc_world, (wedict_t*)ent, move, axis, true, false, settrace?cs_settracevars:NULL, pr_globals); + G_FLOAT(OFS_RETURN) = World_movestep(&csqc_world, (wedict_t*)ent, move, axis, true, false, settrace?cs_settracevars:NULL); // restore program state *csqcg.self = oldself; @@ -5935,7 +5992,7 @@ static void QCBUILTIN PF_resourcestatus(pubprogfuncs_t *prinst, struct globalvar else { if (doload && sfx->loadstate == SLS_NOTLOADED) - S_LoadSound(sfx); + S_LoadSound(sfx, true); switch(sfx->loadstate) { case SLS_NOTLOADED: @@ -6805,6 +6862,8 @@ static void PDECL CSQC_EntSpawn (struct edict_s *e, int loading) // ent->xv->dimension_ghost = 0; ent->xv->dimension_solid = *csqcg.dimension_default; ent->xv->dimension_hit = *csqcg.dimension_default; + + ent->xv->drawflags = SCALE_ORIGIN_ORIGIN; } } @@ -6835,7 +6894,7 @@ static pbool QDECL CSQC_EntFree (struct edict_s *e) return true; } -static void QDECL CSQC_Event_Touch(world_t *w, wedict_t *s, wedict_t *o) +static void QDECL CSQC_Event_Touch(world_t *w, wedict_t *s, wedict_t *o, trace_t *trace) { int oself = *csqcg.self; int oother = *csqcg.other; @@ -6968,73 +7027,91 @@ void CSQC_Shutdown(void) } } -//when the qclib needs a file, it calls out to this function. -void *PDECL CSQC_PRLoadFile (const char *path, unsigned char *(PDECL *buf_get)(void *ctx, size_t len), void *buf_ctx, size_t *sz, pbool issource) +static qboolean CSQC_ValidateMainCSProgs(void *file, size_t filesize, unsigned int checksum, size_t checksize) { + if (!file) + return false; + if (checksize && filesize != checksize) + return false; + if (cls.protocol == CP_NETQUAKE && !(cls.fteprotocolextensions2 & PEXT2_PREDINFO)) + { //DP uses really lame checksums. + if (QCRC_Block(file, filesize) != checksum) + return false; + } + else + { //FTE uses folded-md4. yeah, its broken but at least its still more awkward + if (LittleLong(Com_BlockChecksum(file, filesize)) != checksum) + return false; + } + return true; +} +static void *CSQC_FindMainProgs(size_t *sz, const char *name, unsigned int checksum, size_t checksize) +{ //returns a TempFile + char newname[MAX_QPATH]; extern cvar_t sv_demo_write_csqc; - qbyte *file = NULL; + void *file = NULL; - if (!strcmp(path, "csprogs.dat")) + //the filename we'll cache to + snprintf(newname, MAX_QPATH, "csprogsvers/%x.dat", checksum); + + //we can use FSLF_IGNOREPURE because we have our own hashes/size checks instead. + //this should make it slightly easier for server admins + if (checksum) { - char newname[MAX_QPATH]; - snprintf(newname, MAX_QPATH, "csprogsvers/%x.dat", csprogs_checksum); + file = COM_LoadTempFile (newname, FSLF_IGNOREPURE, sz); + if (!CSQC_ValidateMainCSProgs(file, *sz, checksum, checksize)) + file = NULL; + } - //we can use FSLF_IGNOREPURE because we have our own hashes/size checks instead. - //this should make it slightly easier for server admins - if (csprogs_checksum) + if (!file) + { + const char *progsname = InfoBuf_ValueForKey(&cl.serverinfo, "*csprogsname"); + if (*progsname && cls.state) + file = COM_LoadTempFile (progsname, FSLF_IGNOREPURE, sz); + if (!file && strcmp(progsname, "csprogs.dat")) + file = COM_LoadTempFile ("csprogs.dat", FSLF_IGNOREPURE, sz); + + if (file && !cls.demoplayback) //allow them to use csprogs.dat if playing a demo, and don't care about the checksum { - file = COM_LoadTempFile (newname, FSLF_IGNOREPURE, sz); - if (file) + if (checksum && !csprogs_promiscuous) { - if (cls.protocol == CP_NETQUAKE && !(cls.fteprotocolextensions2 & PEXT2_PREDINFO)) - { - if (QCRC_Block(file, *sz) != csprogs_checksum) - file = NULL; - } - else - { - if (LittleLong(Com_BlockChecksum(file, *sz)) != csprogs_checksum) //and the user wasn't trying to be cunning. - file = NULL; - } - } - } + if (!CSQC_ValidateMainCSProgs(file, *sz, checksum, checksize)) + file = NULL; - if (!file) - { - file = COM_LoadTempFile (path, FSLF_IGNOREPURE, sz); - - if (file && !cls.demoplayback) //allow them to use csprogs.dat if playing a demo, and don't care about the checksum - { - if (csprogs_checksum && !csprogs_promiscuous) - { - if (cls.protocol == CP_NETQUAKE && !(cls.fteprotocolextensions2 & PEXT2_PREDINFO)) - { - if (QCRC_Block(file, *sz) != csprogs_checksum) - file = NULL; - } - else - { - if (LittleLong(Com_BlockChecksum(file, *sz)) != csprogs_checksum) - file = NULL; //not valid - } - - //we write the csprogs into our archive if it was loaded from outside of there. - //this is to ensure that demos will play on the same machine later on... - //this is unreliable though, and redundant if we're writing the csqc into the demos themselves. - //also kinda irrelevant with sv_pure. + //we write the csprogs into our archive if it was loaded from outside of there. + //this is to ensure that demos will play on the same machine later on... + //this is unreliable though, and redundant if we're writing the csqc into the demos themselves. + //also kinda irrelevant with sv_pure. + //FIXME: don't back up if it was in a package. #ifndef FTE_TARGET_WEB - if (file + if (file #if !defined(CLIENTONLY) && defined(MVD_RECORDING) - && !sv_demo_write_csqc.ival + && !sv_demo_write_csqc.ival #endif - ) - //back it up - COM_WriteFile(newname, FS_GAMEONLY, file, *sz); + ) + //back it up + COM_WriteFile(newname, FS_GAMEONLY, file, *sz); #endif - } } } } + return file; +} +qboolean CSQC_CheckDownload(const char *name, unsigned int checksum, size_t checksize) +{ + size_t sz; + if (CSQC_FindMainProgs(&sz, name, checksum, checksize)) + return true; + return false; +} + +//when the qclib needs a file, it calls out to this function. +void *PDECL CSQC_PRLoadFile (const char *path, unsigned char *(PDECL *buf_get)(void *ctx, size_t len), void *buf_ctx, size_t *sz, pbool issource) +{ + qbyte *file; + + if (!strcmp(path, csprogs_checkname)) + file = CSQC_FindMainProgs(sz, csprogs_checkname, csprogs_checksum, csprogs_checksize); else file = COM_LoadTempFile (path, 0, sz); @@ -7078,7 +7155,7 @@ qboolean CSQC_UnconnectedInit(void) if (csqcprogs) return true; - return CSQC_Init(true, true, 0); + return CSQC_Init(true, "csprogs.dat", 0, 0); } void ASMCALL CSQC_StateOp(pubprogfuncs_t *prinst, float var, func_t func) { @@ -7212,27 +7289,40 @@ pbool PDECL CSQC_CheckHeaderCrc(pubprogfuncs_t *progs, progsnum_t num, int crc) } double csqctime; -qboolean CSQC_Init (qboolean anycsqc, qboolean csdatenabled, unsigned int checksum) +qboolean CSQC_Init (qboolean anycsqc, const char *csprogsname, unsigned int checksum, size_t progssize) { int i; string_t *str; csqcedict_t *worldent; char *cheats; - if (csprogs_promiscuous != anycsqc || csprogs_checksum != checksum) + qboolean csdatenabled = true; + if (!csprogsname) + { + csdatenabled = false; + csprogsname = "csprogs.dat"; + } + if (csprogs_promiscuous != anycsqc || csprogs_checksum != checksum || csprogs_checksize != progssize || strcmp(csprogs_checkname,csprogsname)) CSQC_Shutdown(); csprogs_promiscuous = anycsqc; csprogs_checksum = checksum; + csprogs_checksize = progssize; + Q_strncpyz(csprogs_checkname, csprogsname, sizeof(csprogs_checkname)); csqc_mayread = false; csqc_singlecheats = cls.demoplayback; - cheats = InfoBuf_ValueForKey(&cl.serverinfo, "*cheats"); +#ifdef HAVE_SERVER + if (sv.state == ss_active) + { + cheats = InfoBuf_ValueForKey(&svs.info, "*cheats"); + if (!*cheats && sv.allocated_client_slots == 1) + cheats = "ON"; + } + else +#endif + cheats = InfoBuf_ValueForKey(&cl.serverinfo, "*cheats"); if (!Q_strcasecmp(cheats, "ON") || atoi(cheats)) csqc_singlecheats = true; -#ifndef CLIENTONLY - else if (sv.state == ss_active && sv.allocated_client_slots == 1) - csqc_singlecheats = true; -#endif //its already running... if (csqcprogs) @@ -7352,7 +7442,7 @@ qboolean CSQC_Init (qboolean anycsqc, qboolean csdatenabled, unsigned int checks if (!csqc_nogameaccess) { //only load csprogs if its expected to be able to work without failing for game access reasons - csprogsnum = PR_LoadProgs(csqcprogs, "csprogs.dat"); + csprogsnum = PR_LoadProgs(csqcprogs, csprogs_checkname); if (csprogsnum >= 0) Con_DPrintf("Loaded csprogs.dat\n"); } diff --git a/engine/client/r_2d.c b/engine/client/r_2d.c index 262076c1a..ce5a88eda 100644 --- a/engine/client/r_2d.c +++ b/engine/client/r_2d.c @@ -1279,7 +1279,7 @@ void R2D_PolyBlend (void) if (r_refdef.flags & RDF_NOWORLDMODEL) return; - R2D_ImageColours (r_refdef.playerview->screentint[0], r_refdef.playerview->screentint[1], r_refdef.playerview->screentint[2], r_refdef.playerview->screentint[3]); + R2D_ImageColours (SRGBA(r_refdef.playerview->screentint[0], r_refdef.playerview->screentint[1], r_refdef.playerview->screentint[2], r_refdef.playerview->screentint[3])); R2D_ScalePic(r_refdef.vrect.x, r_refdef.vrect.y, r_refdef.vrect.width, r_refdef.vrect.height, shader_polyblend); R2D_ImageColours (1, 1, 1, 1); } diff --git a/engine/client/r_surf.c b/engine/client/r_surf.c index 5520689c2..2c2b1d45d 100644 --- a/engine/client/r_surf.c +++ b/engine/client/r_surf.c @@ -405,7 +405,6 @@ static void Surf_AddDynamicLights (msurface_t *surf) } } -// warning: Surf_AddDynamicLightNorms defined but not used /* static void Surf_AddDynamicLightNorms (msurface_t *surf) { @@ -1571,8 +1570,6 @@ static void Surf_BuildLightMap_Worker (model_t *wmodel, msurface_t *surf, qbyte static vec3_t *blocknormals; static unsigned int *blocklights; - //int stride = LMBLOCK_WIDTH*lightmap_bytes; //warning: unused variable stride - shift += 7; // increase to base value surf->cached_dlight = false; diff --git a/engine/client/snd_al.c b/engine/client/snd_al.c index c5e3b22bb..c4a730a2c 100644 --- a/engine/client/snd_al.c +++ b/engine/client/snd_al.c @@ -694,7 +694,7 @@ static void OpenAL_ChannelUpdate(soundcardinfo_t *sc, channel_t *chan, unsigned { if (!sfx->openal_buffer) { - if (!S_LoadSound(sfx)) + if (!S_LoadSound(sfx, false)) return; //can't load it if (sfx->loadstate != SLS_LOADED) { @@ -1110,24 +1110,24 @@ static void QDECL OnChangeALSettings (cvar_t *var, char *value) switch ((enum distancemodel_e)s_al_distancemodel.ival) { case DM_INVERSE: - //gain = AL_REFERENCE_DISTANCE / (AL_REFERENCE_DISTANCE + AL_ROLLOFF_FACTOR * (distance AL_REFERENCE_DISTANCE) ) + //gain = AL_REFERENCE_DISTANCE / (AL_REFERENCE_DISTANCE + AL_ROLLOFF_FACTOR * (distance - AL_REFERENCE_DISTANCE) ) palDistanceModel(AL_INVERSE_DISTANCE); break; case DM_INVERSE_CLAMPED: //openal's default mode //istance = max(distance,AL_REFERENCE_DISTANCE); //distance = min(distance,AL_MAX_DISTANCE); - //gain = AL_REFERENCE_DISTANCE / (AL_REFERENCE_DISTANCE + AL_ROLLOFF_FACTOR * (distance AL_REFERENCE_DISTANCE) ) + //gain = AL_REFERENCE_DISTANCE / (AL_REFERENCE_DISTANCE + AL_ROLLOFF_FACTOR * (distance - AL_REFERENCE_DISTANCE) ) palDistanceModel(AL_INVERSE_DISTANCE_CLAMPED); break; case DM_LINEAR: //most quake-like. linear //distance = min(distance, AL_MAX_DISTANCE) // avoid negative gain - //gain = ( 1 AL_ROLLOFF_FACTOR * (distance AL_REFERENCE_DISTANCE) / (AL_MAX_DISTANCE AL_REFERENCE_DISTANCE) ) + //gain = ( 1 - AL_ROLLOFF_FACTOR * (distance - AL_REFERENCE_DISTANCE) / (AL_MAX_DISTANCE - AL_REFERENCE_DISTANCE) ) palDistanceModel(AL_LINEAR_DISTANCE); break; case DM_LINEAR_CLAMPED: //linear, with near stuff clamped to further away //distance = max(distance, AL_REFERENCE_DISTANCE) //distance = min(distance, AL_MAX_DISTANCE) - //gain = ( 1 AL_ROLLOFF_FACTOR * (distance AL_REFERENCE_DISTANCE) / (AL_MAX_DISTANCE AL_REFERENCE_DISTANCE) ) + //gain = ( 1 - AL_ROLLOFF_FACTOR * (distance - AL_REFERENCE_DISTANCE) / (AL_MAX_DISTANCE - AL_REFERENCE_DISTANCE) ) palDistanceModel(AL_LINEAR_DISTANCE_CLAMPED); break; case DM_EXPONENT: diff --git a/engine/client/snd_dma.c b/engine/client/snd_dma.c index e15b21b44..b1ee4d47a 100644 --- a/engine/client/snd_dma.c +++ b/engine/client/snd_dma.c @@ -2485,7 +2485,7 @@ sfx_t *S_PrecacheSound2 (const char *name, qboolean syspath) // cache it in if (precache.ival && sndcardinfo) - S_LoadSound (sfx); + S_LoadSound (sfx, true); return sfx; } @@ -2829,7 +2829,7 @@ static void S_UpdateSoundCard(soundcardinfo_t *sc, qboolean updateonly, channel_ target_chan->entchannel = entchannel; SND_Spatialize(sc, target_chan); - if (!S_LoadSound (sfx)) + if (!S_LoadSound (sfx, false)) { target_chan->sfx = NULL; return; // couldn't load the sound's data @@ -3156,7 +3156,7 @@ void S_StaticSound (sfx_t *sfx, vec3_t origin, float vol, float attenuation) } } - if (!S_LoadSound (sfx)) + if (!S_LoadSound (sfx, true)) break; ss = &scard->channel[scard->total_chans]; @@ -3476,7 +3476,7 @@ static void S_Q2_AddEntitySounds(soundcardinfo_t *sc) if (!sfx) continue; if (sfx->loadstate == SLS_NOTLOADED) - S_LoadSound(sfx); + S_LoadSound(sfx, true); if (sfx->loadstate != SLS_LOADED) continue; //not ready yet diff --git a/engine/client/snd_mem.c b/engine/client/snd_mem.c index 99f072e82..7f1e6beb7 100644 --- a/engine/client/snd_mem.c +++ b/engine/client/snd_mem.c @@ -613,7 +613,7 @@ static qboolean ResampleSfx (sfx_t *sfx, int inrate, int inchannels, int inwidth #define DSPK_EXP 0.0433 /* -qboolean QDECL S_LoadDoomSpeakerSound (sfx_t *s, qbyte *data, size_t datalen, int sndspeed) +qboolean QDECL S_LoadDoomSpeakerSound (sfx_t *s, qbyte *data, size_t datalen, int sndspeed, qboolean forcedecode) { sfxcache_t *sc; @@ -689,7 +689,7 @@ qboolean QDECL S_LoadDoomSpeakerSound (sfx_t *s, qbyte *data, size_t datalen, in return sc; } */ -static qboolean QDECL S_LoadDoomSound (sfx_t *s, qbyte *data, size_t datalen, int sndspeed) +static qboolean QDECL S_LoadDoomSound (sfx_t *s, qbyte *data, size_t datalen, int sndspeed, qboolean forcedecode) { // format data from Unofficial Doom Specs v1.6 unsigned short *dataus; @@ -731,7 +731,7 @@ void S_ShortedLittleFloats(void *p, size_t samples) } } -static qboolean QDECL S_LoadWavSound (sfx_t *s, qbyte *data, size_t datalen, int sndspeed) +static qboolean QDECL S_LoadWavSound (sfx_t *s, qbyte *data, size_t datalen, int sndspeed, qboolean forcedecode) { wavinfo_t info; @@ -772,11 +772,11 @@ static qboolean QDECL S_LoadWavSound (sfx_t *s, qbyte *data, size_t datalen, int return ResampleSfx (s, info.rate, info.numchannels, info.width, info.samples, info.loopstart, data + info.dataofs); } -qboolean QDECL S_LoadOVSound (sfx_t *s, qbyte *data, size_t datalen, int sndspeed); +qboolean QDECL S_LoadOVSound (sfx_t *s, qbyte *data, size_t datalen, int sndspeed, qboolean forcedecode); #ifdef FTE_TARGET_WEB //web browsers contain their own decoding libraries that our openal stuff can use. -static qboolean QDECL S_LoadBrowserFile (sfx_t *s, qbyte *data, size_t datalen, int sndspeed) +static qboolean QDECL S_LoadBrowserFile (sfx_t *s, qbyte *data, size_t datalen, int sndspeed, qboolean forcedecode) { sfxcache_t *sc; s->decoder.buf = sc = BZ_Malloc(sizeof(sfxcache_t) + datalen); @@ -834,7 +834,7 @@ S_LoadSound ============== */ -static void S_LoadSoundWorker (void *ctx, void *ctxdata, size_t a, size_t b) +static void S_LoadSoundWorker (void *ctx, void *ctxdata, size_t forcedecode, size_t b) { sfx_t *s = ctx; char namebuffer[256]; @@ -920,7 +920,7 @@ static void S_LoadSoundWorker (void *ctx, void *ctxdata, size_t a, size_t b) data = FS_LoadMallocFile(namebuffer, &filesize); if (data) { - Con_DPrintf("found a mangled name: %s\n", namebuffer); + Con_DPrintf("S_LoadSound: %s%s requested, but could only find %s\n", prefixes[pre], name, namebuffer); break; } } @@ -942,7 +942,7 @@ static void S_LoadSoundWorker (void *ctx, void *ctxdata, size_t a, size_t b) { if (AudioInputPlugins[i]) { - if (AudioInputPlugins[i](s, data, filesize, snd_speed)) + if (AudioInputPlugins[i](s, data, filesize, snd_speed, forcedecode)) { //wake up the main thread in case it decided to wait for us. COM_AddWork(WG_MAIN, S_LoadedOrFailed, s, NULL, SLS_LOADED, 0); @@ -960,12 +960,12 @@ static void S_LoadSoundWorker (void *ctx, void *ctxdata, size_t a, size_t b) return; } -qboolean S_LoadSound (sfx_t *s) +qboolean S_LoadSound (sfx_t *s, qboolean force) { if (s->loadstate == SLS_NOTLOADED && sndcardinfo) { s->loadstate = SLS_LOADING; - COM_AddWork(WG_LOADER, S_LoadSoundWorker, s, NULL, 0, 0); + COM_AddWork(WG_LOADER, S_LoadSoundWorker, s, NULL, force, 0); } if (s->loadstate == SLS_FAILED) return false; //it failed to load once before, don't bother trying again. diff --git a/engine/client/snd_ov.c b/engine/client/snd_ov.c index 47c4e6e6d..f376f5757 100644 --- a/engine/client/snd_ov.c +++ b/engine/client/snd_ov.c @@ -62,6 +62,7 @@ typedef struct { int srcspeed; int srcchannels; + qboolean nopurge; qboolean failed; char *tempbuffer; @@ -79,52 +80,7 @@ typedef struct { sfx_t *s; } ovdecoderbuffer_t; -float QDECL OV_Query(struct sfx_s *sfx, struct sfxcache_s *buf, char *name, size_t namesize); -static sfxcache_t *QDECL OV_DecodeSome(struct sfx_s *sfx, struct sfxcache_s *buf, ssamplepos_t start, int length); -static void QDECL OV_CancelDecoder(sfx_t *s); -static qboolean OV_StartDecode(unsigned char *start, unsigned long length, ovdecoderbuffer_t *buffer); - -qboolean QDECL S_LoadOVSound (sfx_t *s, qbyte *data, size_t datalen, int sndspeed) -{ - ovdecoderbuffer_t *buffer; - - if (datalen < 4 || strncmp(data, "OggS", 4)) - return false; - - buffer = Z_Malloc(sizeof(ovdecoderbuffer_t)); - - buffer->decodedbytestart = 0; - buffer->decodedbytecount = 0; - buffer->s = s; - s->decoder.buf = buffer; - s->loopstart = -1; - - if (!OV_StartDecode(data, datalen, buffer)) - { - if (buffer->decodedbuffer) - { - BZ_Free(buffer->decodedbuffer); - buffer->decodedbuffer = NULL; - } - buffer->decodedbufferbytes = 0; - buffer->decodedbytestart = 0; - buffer->decodedbytecount = 0; - Z_Free(s->decoder.buf); - s->decoder.buf = NULL; - s->loadstate = SLS_FAILED; //failed! - return false; - } - s->decoder.decodedata = OV_DecodeSome; - s->decoder.querydata = OV_Query; - s->decoder.purge = OV_CancelDecoder; - s->decoder.ended = OV_CancelDecoder; - - s->decoder.decodedata(s, NULL, 0, 100); - - return true; -} - -float QDECL OV_Query(struct sfx_s *sfx, struct sfxcache_s *buf, char *name, size_t namesize) +static float QDECL OV_Query(struct sfx_s *sfx, struct sfxcache_s *buf, char *name, size_t namesize) { ovdecoderbuffer_t *dec = sfx->decoder.buf; if (!dec) @@ -206,7 +162,7 @@ static sfxcache_t *QDECL OV_DecodeSome(struct sfx_s *sfx, struct sfxcache_s *buf p_ov_pcm_seek(&dec->vf, (dec->decodedbytestart * dec->srcspeed) / outspeed); } */ - if (dec->decodedbytecount > outspeed*8) + if (dec->decodedbytecount > outspeed*8 && !dec->nopurge) { /*everything is okay, but our buffer is getting needlessly large. keep anything after the 'new' position, but discard all before that @@ -329,7 +285,7 @@ static sfxcache_t *QDECL OV_DecodeSome(struct sfx_s *sfx, struct sfxcache_s *buf s->loadstate = SLS_NOTLOADED; }*/ static void QDECL OV_CancelDecoder(sfx_t *s) -{ +{ //called when the sound is unloaded. the entire thing is going away. ovdecoderbuffer_t *dec; s->loadstate = SLS_FAILED; @@ -358,6 +314,26 @@ static void QDECL OV_CancelDecoder(sfx_t *s) // COM_AddWork(WG_MAIN, OV_CanceledDecoder, s, NULL, SLS_NOTLOADED, 0); s->loadstate = SLS_NOTLOADED; } +static void QDECL OV_ClearDecoder(sfx_t *s) +{ //called when the sound is no longer playing. + ovdecoderbuffer_t *dec; + dec = s->decoder.buf; + if (dec->nopurge) + { +/* BZ_Free(dec->tempbuffer); + dec->tempbuffer = NULL; + dec->tempbufferbytes = 0; + + BZ_Free(dec->decodedbuffer); + dec->decodedbuffer = NULL; + dec->decodedbufferbytes = 0; + dec->decodedbytestart = 0; + dec->decodedbytecount = 0; +*/ + return; + } + OV_CancelDecoder(s); +} static size_t VARGS read_func (void *ptr, size_t size, size_t nmemb, void *datasource) { @@ -515,5 +491,48 @@ static qboolean OV_StartDecode(unsigned char *start, unsigned long length, ovdec return true; } + + +qboolean QDECL S_LoadOVSound (sfx_t *s, qbyte *data, size_t datalen, int sndspeed, qboolean forcedecode) +{ + ovdecoderbuffer_t *buffer; + + if (datalen < 4 || strncmp(data, "OggS", 4)) + return false; + + buffer = Z_Malloc(sizeof(ovdecoderbuffer_t)); + + buffer->decodedbytestart = 0; + buffer->decodedbytecount = 0; + buffer->nopurge = forcedecode; + buffer->s = s; + s->decoder.buf = buffer; + s->loopstart = -1; + + if (!OV_StartDecode(data, datalen, buffer)) + { + if (buffer->decodedbuffer) + { + BZ_Free(buffer->decodedbuffer); + buffer->decodedbuffer = NULL; + } + buffer->decodedbufferbytes = 0; + buffer->decodedbytestart = 0; + buffer->decodedbytecount = 0; + Z_Free(s->decoder.buf); + s->decoder.buf = NULL; + s->loadstate = SLS_FAILED; //failed! + return false; + } + s->decoder.decodedata = OV_DecodeSome; + s->decoder.querydata = OV_Query; + s->decoder.purge = OV_CancelDecoder; + s->decoder.ended = OV_ClearDecoder; + + s->decoder.decodedata(s, NULL, 0, 100); + + return true; +} + #endif diff --git a/engine/client/sound.h b/engine/client/sound.h index b06cfa265..e82dab472 100644 --- a/engine/client/sound.h +++ b/engine/client/sound.h @@ -326,9 +326,9 @@ extern int snd_blocked; void S_LocalSound (const char *s); void S_LocalSound2 (const char *sound, int channel, float volume); -qboolean S_LoadSound (sfx_t *s); +qboolean S_LoadSound (sfx_t *s, qboolean forcedecode); -typedef qboolean (QDECL *S_LoadSound_t) (sfx_t *s, qbyte *data, size_t datalen, int sndspeed); +typedef qboolean (QDECL *S_LoadSound_t) (sfx_t *s, qbyte *data, size_t datalen, int sndspeed, qboolean forcedecode); qboolean S_RegisterSoundInputPlugin(S_LoadSound_t loadfnc); //called to register additional sound input plugins void S_AmbientOff (void); diff --git a/engine/client/sys_linux.c b/engine/client/sys_linux.c index 3190fdca4..811ba09b2 100644 --- a/engine/client/sys_linux.c +++ b/engine/client/sys_linux.c @@ -548,6 +548,10 @@ int Sys_EnumerateFiles2 (const char *truepath, int apathofs, const char *match, break; if (*ent->d_name != '.') { +#ifdef _DIRENT_HAVE_D_TYPE + if (ent->d_type != DT_DIR && ent->d_type != DT_UNKNOWN) + continue; +#endif if (wildcmp(subdir, ent->d_name)) { memcpy(file, truepath, wild-truepath); diff --git a/engine/client/textedit.c b/engine/client/textedit.c index 136cf68c6..0869a04b3 100644 --- a/engine/client/textedit.c +++ b/engine/client/textedit.c @@ -204,30 +204,33 @@ static void Con_Editor_DeleteSelection(console_t *con) { conline_t *n; con->flags &= ~CONF_KEEPSELECTION; - if (con->selstartline == con->selendline) + if (con->selstartline) { - memmove((conchar_t*)(con->selstartline+1)+con->selstartoffset, (conchar_t*)(con->selendline+1)+con->selendoffset, sizeof(conchar_t)*(con->selendline->length - con->selendoffset)); - con->selendline->length = con->selstartoffset + (con->selendline->length - con->selendoffset); - } - else - { - con->selstartline->length = con->selstartoffset; - for(n = con->selstartline;;) + if (con->selstartline == con->selendline) { - n = n->newer; - if (!n) - break; //shouldn't happen - if (n == con->selendline) + memmove((conchar_t*)(con->selstartline+1)+con->selstartoffset, (conchar_t*)(con->selendline+1)+con->selendoffset, sizeof(conchar_t)*(con->selendline->length - con->selendoffset)); + con->selendline->length = con->selstartoffset + (con->selendline->length - con->selendoffset); + } + else + { + con->selstartline->length = con->selstartoffset; + for(n = con->selstartline;;) { - //this is the last line, we need to keep the end of the string but not the start. - memmove(n+1, (conchar_t*)(n+1)+con->selendoffset, sizeof(conchar_t)*(n->length - con->selendoffset)); - n->length = n->length - con->selendoffset; + n = n->newer; + if (!n) + break; //shouldn't happen + if (n == con->selendline) + { + //this is the last line, we need to keep the end of the string but not the start. + memmove(n+1, (conchar_t*)(n+1)+con->selendoffset, sizeof(conchar_t)*(n->length - con->selendoffset)); + n->length = n->length - con->selendoffset; + n = Con_EditorMerge(con, con->selstartline, n); + break; + } + //truncate and merge + n->length = 0; n = Con_EditorMerge(con, con->selstartline, n); - break; } - //truncate and merge - n->length = 0; - n = Con_EditorMerge(con, con->selstartline, n); } } con->userline = con->selstartline; diff --git a/engine/client/zqtp.c b/engine/client/zqtp.c index 8ec7ca198..d586387de 100644 --- a/engine/client/zqtp.c +++ b/engine/client/zqtp.c @@ -745,8 +745,6 @@ static char *Macro_demoplayback (void) { switch (cls.demoplayback) { - case DPB_EZTV: // warning: enumeration value DPB_EZTV not handled in switch - break; case DPB_NONE: return "0"; case DPB_QUAKEWORLD: @@ -761,7 +759,10 @@ static char *Macro_demoplayback (void) case DPB_QUAKE2: return "dm2playback"; #endif + //gcc will warn if we add annother playback and forget here, otherwise I'd use a default. + case DPB_EZTV: + break; } return "1"; //unknown. } diff --git a/engine/common/bothdefs.h b/engine/common/bothdefs.h index 4cac1a4f3..6bc46a9eb 100644 --- a/engine/common/bothdefs.h +++ b/engine/common/bothdefs.h @@ -361,6 +361,12 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #if defined(SERVERONLY) && defined(CLIENTONLY) #undef CLIENTONLY //impossible build. assume the config had CLIENTONLY and they tried building a dedicated server #endif +#ifndef CLIENTONLY + #define HAVE_SERVER +#endif +#ifndef SERVERONLY + #define HAVE_CLIENT +#endif //software rendering is just too glitchy, don't use it - unless its the only choice. #if defined(SWQUAKE) && !defined(_DEBUG) && !defined(__DJGPP__) @@ -1067,6 +1073,7 @@ STAT_PUNCHVECTOR_X = 29, STAT_PUNCHVECTOR_Y = 30, STAT_PUNCHVECTOR_Z = 31, +#ifdef HEXEN2 //these stats are used only when running a hexen2 mod/hud, and will never be used for a quake mod/hud/generic code. STAT_H2_LEVEL = 32, // changes stat bar STAT_H2_INTELLIGENCE, // changes stat bar @@ -1123,7 +1130,7 @@ STAT_H2_PLAYERCLASS, STAT_H2_OBJECTIVE1, //integer STAT_H2_OBJECTIVE2, //integer - +#endif STAT_MOVEVARS_AIRACCEL_QW_STRETCHFACTOR = 220, // DP STAT_MOVEVARS_AIRCONTROL_PENALTY = 221, // DP diff --git a/engine/common/bspfile.h b/engine/common/bspfile.h index c5e6a0a6b..53d11b38e 100644 --- a/engine/common/bspfile.h +++ b/engine/common/bspfile.h @@ -369,7 +369,7 @@ typedef struct q2miptex_s // upper design bounds // leaffaces, leafbrushes, planes, and verts are still bounded by // 16 bit short limits -#define SANITY_MAX_Q2MAP_MODELS 1024 +#define SANITY_MAX_Q2MAP_MODELS MAX_PRECACHE_MODELS //#define MAX_Q2MAP_ENTITIES 2048 #define SANITY_MAX_MAP_BRUSHES (~0u/sizeof(*out)) #define SANITY_MAX_MAP_LEAFFACES 262144 //sanity only @@ -378,11 +378,7 @@ typedef struct q2miptex_s #define MAX_Q2MAP_AREAPORTALS 1024 //#define MAX_Q2MAP_VERTS MAX_MAP_VERTS //#define MAX_Q2MAP_FACES MAX_MAP_FACES -#ifdef FTE_TARGET_WEB -#define MAX_Q2MAP_LEAFBRUSHES (32768) //used in an array -#else -#define MAX_Q2MAP_LEAFBRUSHES (65536*2) //used in an array -#endif +#define SANITY_MAX_MAP_LEAFBRUSHES (65536*64) //used in an array //#define MAX_Q2MAP_PORTALS 65536 //unused //#define MAX_Q2MAP_EDGES 128000 //unused //#define MAX_Q2MAP_SURFEDGES 256000 //unused diff --git a/engine/common/cmd.c b/engine/common/cmd.c index cc722e974..5253ad098 100644 --- a/engine/common/cmd.c +++ b/engine/common/cmd.c @@ -707,7 +707,7 @@ void Cmd_Exec_f (void) return; } - if (!FS_FLocateFile(name, FSLF_IFFOUND, &loc) && !FS_FLocateFile(va("%s.cfg", name), FSLF_IFFOUND, &loc)) + if (!FS_FLocateFile(name, FSLF_IFFOUND|FSLF_IGNOREPURE, &loc) && !FS_FLocateFile(va("%s.cfg", name), FSLF_IFFOUND, &loc)) { Con_TPrintf ("couldn't exec %s\n", name); return; @@ -721,9 +721,9 @@ void Cmd_Exec_f (void) if (cl_warncmd.ival || developer.ival || cvar_watched) { if (loc.search) - Con_TPrintf ("execing %s/%s\n",name, loc.search->logicalpath); + Con_TPrintf ("execing ^[^7%s\\tip\\from %s/%s^]\n", name, loc.search->logicalpath, name); else - Con_TPrintf ("execing %s\n",name); + Con_TPrintf ("execing %s\n", name); } l = VFS_GETLEN(file); diff --git a/engine/common/com_mesh.c b/engine/common/com_mesh.c index 8c04f2b75..605685f18 100644 --- a/engine/common/com_mesh.c +++ b/engine/common/com_mesh.c @@ -1002,8 +1002,8 @@ typedef struct skeltype_t skeltype; //the skeletal type of this bone block. all blocks should have the same result or the whole thing is unusable or whatever. int firstbone; //first bone of interest int endbone; //the first bone of the next group (ie: if first is 0, this is the count) - float frac[8]; //weight of this animation (1 if lerpcount is 1) - float *pose[8]; //pointer to the raw frame data for bone 0. + float frac[FRAME_BLENDS*2]; //weight of this animation (1 if lerpcount is 1) + float *pose[FRAME_BLENDS*2]; //pointer to the raw frame data for bone 0. int lerpcount; //number of pose+frac entries. } skellerps_t; static qboolean Alias_BuildSkelLerps(skellerps_t *lerps, struct framestateregion_s *fs, int numbones, galiasinfo_t *inf) @@ -1249,9 +1249,6 @@ const float *Alias_GetBoneInformation(galiasinfo_t *inf, framestate_t *framestat endbone = lerp->endbone; switch(lerp->lerpcount) { - case 1://no blend required, data can be used as-is, once merged with the other bone groups, anyway. - memcpy(targetbuffer+bone*12, lerp->pose[0]+bone*12, (endbone-bone)*12*sizeof(float)); - break; case 2: { int k; @@ -1291,6 +1288,32 @@ const float *Alias_GetBoneInformation(galiasinfo_t *inf, framestate_t *framestat } } break; + case 0: + case 1: //the weight will usually be 1, which won't take this path. + default: + { //the generic slow path. + int k, i, b; + float *out, *pose, frac; + for (i = 0; i < lerp->lerpcount; i++) + { + out = targetbuffer + bone*12; + pose = lerp->pose[i] + bone*12; + frac = lerp->frac[i]; + if (!i) + { //first influence shouldn't add, saving us a memcpy. + for (b = bone; b < endbone; b++, out+=12, pose+=12) + for (k = 0; k < 12; k++) + out[k] = (pose[k]*frac); + } + else + { + for (b = bone; b < endbone; b++, out+=12, pose+=12) + for (k = 0; k < 12; k++) + out[k] += (pose[k]*frac); + } + } + } + break; } } @@ -1681,7 +1704,7 @@ qboolean Alias_GAliasBuildMesh(mesh_t *mesh, vbo_t **vbop, galiasinfo_t *inf, in meshcache.ent = e; -#ifdef _DEBUG +#if defined(_DEBUG) && FRAME_BLENDS == 4 if (!e->framestate.g[FS_REG].lerpweight[0] && !e->framestate.g[FS_REG].lerpweight[1] && !e->framestate.g[FS_REG].lerpweight[2] && !e->framestate.g[FS_REG].lerpweight[3]) Con_Printf("Entity with no lerp info\n"); #endif @@ -1817,6 +1840,12 @@ qboolean Alias_GAliasBuildMesh(mesh_t *mesh, vbo_t **vbop, galiasinfo_t *inf, in float lerp; float fg1time; //float fg2time; + static float printtimer; + +#if FRAME_BLENDS != 2 + if (e->framestate.g[FS_REG].lerpweight[2] || e->framestate.g[FS_REG].lerpweight[3]) + Con_ThrottlePrintf(&printtimer, 1, "Alias_GAliasBuildMesh(%s): non-skeletal animation only supports two animations\n", e->model->name); +#endif //FIXME: replace most of this logic with Alias_BuildSkelLerps @@ -1828,22 +1857,22 @@ qboolean Alias_GAliasBuildMesh(mesh_t *mesh, vbo_t **vbop, galiasinfo_t *inf, in if (frame1 < 0) { - Con_DPrintf("Negative frame (%s)\n", e->model->name); + Con_ThrottlePrintf(&printtimer, 1, "Negative frame (%s)\n", e->model->name); frame1 = 0; } if (frame2 < 0) { - Con_DPrintf("Negative frame (%s)\n", e->model->name); + Con_ThrottlePrintf(&printtimer, 1, "Negative frame (%s)\n", e->model->name); frame2 = frame1; } if (frame1 >= inf->numanimations) { - Con_DPrintf("Too high frame %i (%s)\n", frame1, e->model->name); + Con_ThrottlePrintf(&printtimer, 1, "Too high frame %i (%s)\n", frame1, e->model->name); frame1 %= inf->numanimations; } if (frame2 >= inf->numanimations) { - Con_DPrintf("Too high frame %i (%s)\n", frame2, e->model->name); + Con_ThrottlePrintf(&printtimer, 1, "Too high frame %i (%s)\n", frame2, e->model->name); frame2 %= inf->numanimations; } @@ -1857,7 +1886,7 @@ qboolean Alias_GAliasBuildMesh(mesh_t *mesh, vbo_t **vbop, galiasinfo_t *inf, in if (!inf->numanimations || !g1->numposes || !g2->numposes) { - Con_Printf("Invalid animation data on entity with model %s\n", e->model->name); + Con_ThrottlePrintf(&printtimer, 1, "Invalid animation data on entity with model %s\n", e->model->name); //no animation data. panic! memset(mesh, 0, sizeof(*mesh)); *vbop = NULL; @@ -4508,6 +4537,14 @@ qboolean Mod_GetTag(model_t *model, int tagnum, framestate_t *fstate, float *res //float f1time, f2time; //tags/md3s don't support framegroups. float f2ness; +#if FRAME_BLENDS != 2 + if (fstate->g[FS_REG].lerpweight[2] || fstate->g[FS_REG].lerpweight[3]) + { + static float printtimer; + Con_ThrottlePrintf(&printtimer, 1, "Mod_GetTag(%s): non-skeletal animation only supports two animations\n", model->name); + } +#endif + frame1 = fstate->g[FS_REG].frame[0]; frame2 = fstate->g[FS_REG].frame[1]; //f1time = fstate->g[FS_REG].frametime[0]; diff --git a/engine/common/console.h b/engine/common/console.h index 1b9e2c5e0..6e4642a99 100644 --- a/engine/common/console.h +++ b/engine/common/console.h @@ -227,6 +227,7 @@ void VARGS Con_Printf (const char *fmt, ...) LIKEPRINTF(1); void VARGS Con_TPrintf (translation_t text, ...); void VARGS Con_DPrintf (const char *fmt, ...) LIKEPRINTF(1); //developer>=1, for stuff that's probably actually slightly useful void VARGS Con_DLPrintf (int level, const char *fmt, ...) LIKEPRINTF(2); //developer>=2, for spammy stuff +void VARGS Con_ThrottlePrintf (float *timer, int developerlevel, const char *fmt, ...); //for spammed warnings, so they don't spam prints with every single frame/call. the timer arg should be a static local. void VARGS Con_SafePrintf (const char *fmt, ...) LIKEPRINTF(1); void Con_Footerf(console_t *con, qboolean append, const char *fmt, ...) LIKEPRINTF(3); void Con_Clear_f (void); diff --git a/engine/common/fs.c b/engine/common/fs.c index 3551c7507..2868fa601 100644 --- a/engine/common/fs.c +++ b/engine/common/fs.c @@ -3201,6 +3201,8 @@ void QDECL Q_strnlowercatz(char *d, const char *s, int n) *d = 0; } +//pname must be of the form "gamedir/foo.pk3" +//as a special exception, we allow "downloads/*.pk3 too" qboolean FS_GenCachedPakName(const char *pname, const char *crc, char *local, int llen) { const char *fn; @@ -3227,7 +3229,7 @@ qboolean FS_GenCachedPakName(const char *pname, const char *crc, char *local, in } } // fn = COM_SkipPath(pname); - if (fn == pname) + if (fn == pname || !*fn) { //only allow it if it has some game path first. *local = 0; return false; @@ -3531,6 +3533,7 @@ void FS_PureMode(int puremode, char *purenamelist, char *purecrclist, char *refn #ifndef SERVERONLY int FS_PureOkay(void) { + qboolean ret = true; //returns true if all pure packages that we're meant to need could load. //if they couldn't then they won't override things, or the game will just be completely screwed due to having absolutely no game data if (fs_puremode == 1 && fs_purenames && *fs_purenames && fs_purecrcs && *fs_purecrcs) @@ -3595,14 +3598,13 @@ int FS_PureOkay(void) if (!CL_CheckDLFile(va("package/%s", pname))) if (CL_CheckOrEnqueDownloadFile(va("package/%s", pname), va("%s.%i", pname, crc), DLLF_NONGAME)) return -1; - Con_Printf(CON_ERROR"Pure package %s:%i missing.\n", pname, crc); - return false; + Con_Printf(CON_ERROR"Pure package %s:%08x missing.\n", pname, crc); + ret = false; } } - return true; } - return true; + return ret; } #endif @@ -3825,8 +3827,10 @@ void FS_ReloadPackFilesFlags(unsigned int reloadflags) names = COM_ParseOut(names, nametok, sizeof(nametok)); crc = strtoul(crctok, NULL, 0); - if (!crc) + if (!*crctok) continue; + if (!strcmp(crctok, "-")) + *crctok = 0; pname = nametok; @@ -3855,7 +3859,8 @@ void FS_ReloadPackFilesFlags(unsigned int reloadflags) for (sp = com_searchpaths; sp; sp = sp->next) { if (sp->nextpure == (void*)0x1) //don't add twice. - if (sp->crc_check == crc) + if ((*crctok && sp->crc_check == crc) || + (!*crctok && !strcmp(COM_SkipPath(sp->purepath), COM_SkipPath(pname)))) { if (fs_puremode) { @@ -3888,14 +3893,19 @@ void FS_ReloadPackFilesFlags(unsigned int reloadflags) int i; COM_FileExtension(pname, ext, sizeof(ext)); - if (FS_GenCachedPakName(pname, va("%i", crc), local, sizeof(local))) + if (FS_GenCachedPakName(pname, crctok, local, sizeof(local))) { unsigned int keptflags; handle = FS_GetOldPath(&oldpaths, local, &keptflags); if (handle) { sp = FS_AddPathHandle(&oldpaths, pname, local, handle, "", SPF_COPYPROTECTED|SPF_UNTRUSTED|SPF_TEMPORARY|keptflags, (unsigned int)-1); - if (sp->crc_check == crc) + if (sp->handle->GeneratePureCRC) + { + sp->crc_check = sp->handle->GeneratePureCRC(sp->handle, fs_pureseed, 0); + sp->crc_reply = sp->handle->GeneratePureCRC(sp->handle, fs_pureseed, 1); + } + if (sp->crc_check == crc || !*crctok) { if (fs_puremode) { @@ -3925,11 +3935,13 @@ void FS_ReloadPackFilesFlags(unsigned int reloadflags) if (!handle) break; sp = FS_AddPathHandle(&oldpaths, pname, local, handle, "", SPF_COPYPROTECTED|SPF_UNTRUSTED|SPF_TEMPORARY, (unsigned int)-1); + if (sp->handle->GeneratePureCRC) + { + sp->crc_check = sp->handle->GeneratePureCRC(sp->handle, fs_pureseed, 0); + sp->crc_reply = sp->handle->GeneratePureCRC(sp->handle, fs_pureseed, 1); + } - sp->crc_check = sp->handle->GeneratePureCRC(sp->handle, fs_pureseed, 0); - sp->crc_reply = sp->handle->GeneratePureCRC(sp->handle, fs_pureseed, 1); - - if (sp->crc_check == crc) + if (!*crctok) { if (fs_puremode) { @@ -3947,7 +3959,7 @@ void FS_ReloadPackFilesFlags(unsigned int reloadflags) } if (!sp) - Con_DPrintf("Pure package %s:%i wasn't found\n", pname, crc); + Con_DPrintf("Pure package %s:%08x wasn't found\n", pname, crc); } } } @@ -5198,11 +5210,10 @@ void FS_BeginManifestUpdates(void) qboolean FS_FoundManifest(void *usr, ftemanifest_t *man) { if (!*(ftemanifest_t**)usr) - { *(ftemanifest_t**)usr = man; - return true; - } - return false; + else + FS_Manifest_Free(man); + return true; } //reads the default manifest based upon the basedir, the commandline arguments, the name of the exe, etc. @@ -5366,7 +5377,7 @@ qboolean FS_ChangeGame(ftemanifest_t *man, qboolean allowreloadconfigs, qboolean { if (allowreloadconfigs) { - FS_FLocateFile(conffile[i], FSLF_IFFOUND, &loc); //q1 + FS_FLocateFile(conffile[i], FSLF_IFFOUND|FSLF_IGNOREPURE, &loc); //q1 confpath[i] = loc.search?loc.search->handle:NULL; } else @@ -5409,9 +5420,12 @@ qboolean FS_ChangeGame(ftemanifest_t *man, qboolean allowreloadconfigs, qboolean //if we're a client, display a menu to pick between them (or display an error) //servers can just use the first they find, they'd effectively just crash otherwise, but still give a warning. if (!isDedicated) + { + FS_Manifest_Free(man); man = NULL; + } else if (found) - Con_Printf(CON_WARNING "Warning: found multiple possible games. Using the first found.\n"); + Con_Printf(CON_WARNING "Warning: found multiple possible games. Using the first found (%s).\n", man->formalname); else Con_Printf(CON_ERROR "Error: unable to determine correct game/basedir.\n"); } @@ -5610,7 +5624,7 @@ qboolean FS_ChangeGame(ftemanifest_t *man, qboolean allowreloadconfigs, qboolean { for (i = 0; i < countof(conffile); i++) { - FS_FLocateFile(conffile[i], FSLF_IFFOUND, &loc); + FS_FLocateFile(conffile[i], FSLF_IFFOUND|FSLF_IGNOREPURE, &loc); if (confpath[i] != (loc.search?loc.search->handle:NULL)) { reloadconfigs = true; @@ -5756,6 +5770,7 @@ static int QDECL FS_EnumerateFMFs(const char *fname, qofs_t fsize, time_t mtime, return true; } +//callback must call FS_Manifest_Free. int FS_EnumerateKnownGames(qboolean (*callback)(void *usr, ftemanifest_t *man), void *usr) { int i; @@ -5787,6 +5802,15 @@ int FS_EnumerateKnownGames(qboolean (*callback)(void *usr, ftemanifest_t *man), if (!e.found) Sys_EnumerateFiles(host_parms.basedir, "*.fmf", FS_EnumerateFMFs, &e, NULL); + if (!e.found) + { + if (*com_homepath) + Sys_EnumerateFiles(NULL, va("%s/*.fmf", com_homepath), FS_EnumerateFMFs, &e, NULL); +#ifdef __linux__ + Sys_EnumerateFiles(NULL, "/etc/fte/*.fmf", FS_EnumerateFMFs, &e, NULL); +#endif + } + //right, no fmf files anywhere. //just make stuff up from whatever games they may have installed on their system. if (!e.found) @@ -6251,6 +6275,12 @@ void COM_InitFilesystem (void) com_homepathusable = usehome; com_homepathenabled = false; + i = COM_CheckParm("-homedir"); + if (i && i+1 MAX_Q2MAP_LEAFBRUSHES) + if (count > SANITY_MAX_MAP_LEAFBRUSHES) { Con_Printf (CON_ERROR "Map has too many leafbrushes\n"); return false; } - out = prv->leafbrushes; + //prv->numbrushes is because of submodels being weird. + out = prv->leafbrushes = ZG_Malloc(&mod->memgroup, sizeof(*out) * (count+prv->numbrushes)); prv->numleafbrushes = count; for ( i=0 ; ifileofs); if (l->filelen % sizeof(*in)) @@ -3258,10 +3259,10 @@ static qboolean CModQ3_LoadLeafs (model_t *mod, qbyte *mod_base, lump_t *l) out->nummarksurfaces = 0; } + brush = &prv->leafbrushes[out->firstleafbrush]; for (j=0 ; jnumleafbrushes ; j++) { - brush = prv->leafbrushes[out->firstleafbrush + j]; - out->contents |= brush->contents; + out->contents |= brush[j]->contents; } if (out->area >= prv->numareas) @@ -3333,17 +3334,18 @@ static qboolean CModQ3_LoadLeafBrushes (model_t *mod, qbyte *mod_base, lump_t *l return false; } // need to save space for box planes - if (count > MAX_Q2MAP_LEAFBRUSHES) + if (count > SANITY_MAX_MAP_LEAFBRUSHES) { Con_Printf (CON_ERROR "Map has too many leafbrushes\n"); return false; } - out = prv->leafbrushes; + //prv->numbrushes is because of submodels being weird. + out = prv->leafbrushes = ZG_Malloc(&mod->memgroup, sizeof(*out) * (count+prv->numbrushes)); prv->numleafbrushes = count; for ( i=0 ; ibrushes + LittleLong (*in); + *out = prv->brushes + (unsigned int)LittleLong (*in); return true; } diff --git a/engine/common/net.h b/engine/common/net.h index 48d092e20..b7f168015 100644 --- a/engine/common/net.h +++ b/engine/common/net.h @@ -25,6 +25,10 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define HAVE_WEBSOCKCL #endif +#ifdef __linux__ +//#define UNIXSOCKETS +#endif + //FIXME: should split this into loopback/dgram/stream/dtls/tls/irc //with the ipv4/v6/x as a separate parameter typedef enum { @@ -34,6 +38,9 @@ typedef enum { NA_IP, NA_IPV6, NA_IPX, +#ifdef UNIXSOCKETS + NA_UNIX, +#endif #ifdef IRCCONNECT NA_IRC/*remove!*/, #endif @@ -48,7 +55,9 @@ typedef enum { NP_TLS, NP_WS, NP_WSS, - NP_NATPMP + NP_NATPMP, //server-only scheme for registering public ports. + + NP_INVALID } netproto_t; typedef enum {NS_CLIENT, NS_SERVER} netsrc_t; @@ -71,6 +80,13 @@ typedef struct #endif #ifdef HAVE_WEBSOCKCL char websocketurl[64]; +#endif +#ifdef UNIXSOCKETS + struct + { + int len; //abstract addresses contain nulls, so this is needed. + char path[108]; + } un; #endif } address; @@ -126,7 +142,7 @@ neterr_t NET_SendPacket (netsrc_t socket, int length, const void *data, netadr_t int NET_LocalAddressForRemote(struct ftenet_connections_s *collection, netadr_t *remote, netadr_t *local, int idx); void NET_PrintAddresses(struct ftenet_connections_s *collection); qboolean NET_AddressSmellsFunny(netadr_t *a); -qboolean NET_EnsureRoute(struct ftenet_connections_s *collection, char *routename, char *host); +qboolean NET_EnsureRoute(struct ftenet_connections_s *collection, char *routename, char *host, netadr_t *adr); void NET_PrintConnectionsStatus(struct ftenet_connections_s *collection); enum addressscope_e @@ -143,10 +159,10 @@ qboolean NET_CompareAdr (netadr_t *a, netadr_t *b); qboolean NET_CompareBaseAdr (netadr_t *a, netadr_t *b); void NET_AdrToStringResolve (netadr_t *adr, void (*resolved)(void *ctx, void *data, size_t a, size_t b), void *ctx, size_t a, size_t b); char *NET_AdrToString (char *s, int len, netadr_t *a); -char *NET_SockadrToString (char *s, int len, struct sockaddr_qstorage *a); +char *NET_SockadrToString (char *s, int len, struct sockaddr_qstorage *a, size_t sizeofa); char *NET_BaseAdrToString (char *s, int len, netadr_t *a); -size_t NET_StringToSockaddr2 (const char *s, int defaultport, struct sockaddr_qstorage *sadr, int *addrfamily, int *addrsize, size_t addrcount); -#define NET_StringToSockaddr(s,p,a,f,z) (NET_StringToSockaddr2(s,p,a,f,z,1)>0) +size_t NET_StringToSockaddr2 (const char *s, int defaultport, netadrtype_t afhint, struct sockaddr_qstorage *sadr, int *addrfamily, int *addrsize, size_t addrcount); +#define NET_StringToSockaddr(s,p,a,f,z) (NET_StringToSockaddr2(s,p,NA_INVALID,a,f,z,1)>0) size_t NET_StringToAdr2 (const char *s, int defaultport, netadr_t *a, size_t addrcount); #define NET_StringToAdr(s,p,a) NET_StringToAdr2(s,p,a,1) qboolean NET_PortToAdr (netadrtype_t adrfamily, netproto_t adrprot, const char *s, netadr_t *a); @@ -344,5 +360,5 @@ int UDP_OpenSocket (int port); int UDP6_OpenSocket (int port); int IPX_OpenSocket (int port); int NetadrToSockadr (netadr_t *a, struct sockaddr_qstorage *s); -void SockadrToNetadr (struct sockaddr_qstorage *s, netadr_t *a); +void SockadrToNetadr (struct sockaddr_qstorage *s, int sizeofsockaddr, netadr_t *a); qboolean NET_Sleep(float seconds, qboolean stdinissocket); diff --git a/engine/common/net_wins.c b/engine/common/net_wins.c index 56828df6e..a0dbddeb6 100644 --- a/engine/common/net_wins.c +++ b/engine/common/net_wins.c @@ -22,6 +22,35 @@ struct sockaddr; #include "quakedef.h" #include "netinc.h" +#include + +#ifdef UNIXSOCKETS +#include //to delete the file/socket. +#endif + +//try to be slightly cleaner about the protocols that'll get killed. +#ifdef TCPCONNECT + #define NP_STREAM_OR_INVALID NP_STREAM + #define NP_TLS_OR_INVALID NP_TLS + #define NP_WS_OR_INVALID NP_WS + #define NP_WSS_OR_INVALID NP_WSS +#else + #define NP_STREAM_OR_INVALID NP_INVALID + #define NP_TLS_OR_INVALID NP_INVALID + #define NP_WS_OR_INVALID NP_INVALID + #define NP_WSS_OR_INVALID NP_INVALID +#endif +#define NP_DTLS_OR_INVALID NP_DTLS +#ifndef HAVE_SSL + #undef NP_WSS_OR_INVALID + #define NP_WSS_OR_INVALID NP_INVALID + #undef NP_TLS_OR_INVALID + #define NP_TLS_OR_INVALID NP_INVALID + #undef NP_DTLS_OR_INVALID + #define NP_DTLS_OR_INVALID NP_INVALID +#endif + + extern ftemanifest_t *fs_manifest; @@ -55,7 +84,7 @@ FTE_ALIGN(4) qbyte net_message_buffer[MAX_OVERALLMSGLEN]; WSADATA winsockdata; #endif -#ifdef IPPROTO_IPV6 +#ifdef HAVE_IPV6 #ifdef _WIN32 int (WINAPI *pgetaddrinfo) ( const char* nodename, @@ -85,10 +114,10 @@ void (*pfreeaddrinfo) (struct addrinfo*); void NET_GetLocalAddress (int socket, netadr_t *out); //int TCP_OpenListenSocket (const char *localip, int port); -#ifdef IPPROTO_IPV6 +#ifdef HAVE_IPV6 int UDP6_OpenSocket (int port); #endif -#ifdef USEIPX +#ifdef HAVE_IPX void IPX_CloseSocket (int socket); #endif cvar_t net_hybriddualstack = CVARD("net_hybriddualstack", "1", "Uses hybrid ipv4+ipv6 sockets where possible. Not supported on xp or below."); @@ -184,7 +213,7 @@ int NetadrToSockadr (netadr_t *a, struct sockaddr_qstorage *s) ((struct sockaddr_in*)s)->sin_port = a->port; return sizeof(struct sockaddr_in); #endif -#ifdef IPPROTO_IPV6 +#ifdef HAVE_IPV6 case NA_IPV6: memset (s, 0, sizeof(struct sockaddr_in6)); ((struct sockaddr_in6*)s)->sin6_family = AF_INET6; @@ -194,13 +223,29 @@ int NetadrToSockadr (netadr_t *a, struct sockaddr_qstorage *s) ((struct sockaddr_in6 *)s)->sin6_scope_id = a->scopeid; return sizeof(struct sockaddr_in6); #endif -#ifdef USEIPX +#ifdef HAVE_IPX case NA_IPX: +#ifdef _WIN32 ((struct sockaddr_ipx *)s)->sa_family = AF_IPX; memcpy(((struct sockaddr_ipx *)s)->sa_netnum, &a->address.ipx[0], 4); memcpy(((struct sockaddr_ipx *)s)->sa_nodenum, &a->address.ipx[4], 6); ((struct sockaddr_ipx *)s)->sa_socket = a->port; +#else + ((struct sockaddr_ipx *)s)->sipx_family = AF_IPX; + memcpy(&((struct sockaddr_ipx *)s)->sipx_network, &a->address.ipx[0], 4); + memcpy(((struct sockaddr_ipx *)s)->sipx_node, &a->address.ipx[4], 6); + ((struct sockaddr_ipx *)s)->sipx_port = a->port; +#endif return sizeof(struct sockaddr_ipx); +#endif +#ifdef UNIXSOCKETS + case NA_UNIX: + { + struct sockaddr_un *un = (struct sockaddr_un*)s; + un->sun_family = AF_UNIX; + memcpy(un->sun_path, a->address.un.path, a->address.un.len); + return offsetof(struct sockaddr_un, sun_path) + a->address.un.len; + } #endif default: Sys_Error("NetadrToSockadr: Bad type %i", a->type); @@ -208,12 +253,19 @@ int NetadrToSockadr (netadr_t *a, struct sockaddr_qstorage *s) } } -void SockadrToNetadr (struct sockaddr_qstorage *s, netadr_t *a) +void SockadrToNetadr (struct sockaddr_qstorage *s, int sizeofsockaddr, netadr_t *a) { a->scopeid = 0; a->connum = 0; a->prot = NP_DGRAM; + if (sizeofsockaddr < offsetof(struct sockaddr, sa_family)+sizeof(((struct sockaddr*)s)->sa_family)) + { //truncated far too much... + memset(a, 0, sizeof(*a)); + a->type = NA_INVALID; + return; + } + switch (((struct sockaddr*)s)->sa_family) { #ifdef HAVE_WEBSOCKCL @@ -230,7 +282,7 @@ void SockadrToNetadr (struct sockaddr_qstorage *s, netadr_t *a) a->port = ((struct sockaddr_in *)s)->sin_port; break; #endif -#ifdef IPPROTO_IPV6 +#ifdef HAVE_IPV6 case AF_INET6: a->type = NA_IPV6; memcpy(&a->address.ip6, &((struct sockaddr_in6 *)s)->sin6_addr, sizeof(a->address.ip6)); @@ -238,13 +290,32 @@ void SockadrToNetadr (struct sockaddr_qstorage *s, netadr_t *a) a->scopeid = ((struct sockaddr_in6 *)s)->sin6_scope_id; break; #endif -#ifdef USEIPX +#ifdef HAVE_IPX case AF_IPX: a->type = NA_IPX; *(int *)a->address.ip = 0xffffffff; +#ifdef _WIN32 memcpy(&a->address.ipx[0], ((struct sockaddr_ipx *)s)->sa_netnum, 4); memcpy(&a->address.ipx[4], ((struct sockaddr_ipx *)s)->sa_nodenum, 6); a->port = ((struct sockaddr_ipx *)s)->sa_socket; +#else + memcpy(&a->address.ipx[0], &((struct sockaddr_ipx *)s)->sipx_network, 4); + memcpy(&a->address.ipx[4], ((struct sockaddr_ipx *)s)->sipx_node, 6); + a->port = ((struct sockaddr_ipx *)s)->sipx_port; +#endif + break; +#endif +#ifdef UNIXSOCKETS + case AF_UNIX: + { + struct sockaddr_un *un = (struct sockaddr_un*)s; + a->type = NA_UNIX; + a->address.un.len = sizeofsockaddr - offsetof(struct sockaddr_un, sun_path); + memcpy(a->address.un.path, un->sun_path, a->address.un.len); + if (a->address.un.len && a->address.un.path) + a->address.un.len = strnlen(a->address.un.path, a->address.un.len); + a->port = 0; + } break; #endif default: @@ -255,10 +326,10 @@ void SockadrToNetadr (struct sockaddr_qstorage *s, netadr_t *a) break; } } -char *NET_SockadrToString (char *s, int len, struct sockaddr_qstorage *a) +char *NET_SockadrToString (char *s, int len, struct sockaddr_qstorage *a, size_t sizeofa) { netadr_t na; - SockadrToNetadr(a, &na); + SockadrToNetadr(a, sizeofa, &na); return NET_AdrToString(s, len, &na); } @@ -343,7 +414,7 @@ qboolean NET_CompareAdr (netadr_t *a, netadr_t *b) } #endif -#ifdef IPPROTO_IPV6 +#ifdef HAVE_IPV6 if (a->type == NA_IPV6) { if ((memcmp(a->address.ip6, b->address.ip6, sizeof(a->address.ip6)) == 0) && a->port == b->port) @@ -352,7 +423,7 @@ qboolean NET_CompareAdr (netadr_t *a, netadr_t *b) } #endif -#ifdef USEIPX +#ifdef HAVE_IPX if (a->type == NA_IPX) { if ((memcmp(a->address.ipx, b->address.ipx, sizeof(a->address.ipx)) == 0) && a->port == b->port) @@ -370,6 +441,15 @@ qboolean NET_CompareAdr (netadr_t *a, netadr_t *b) } #endif +#ifdef UNIXSOCKETS + if (a->type == NA_UNIX) + { + if (a->address.un.len == b->address.un.len && !memcmp(a->address.un.path, b->address.un.path, a->address.un.len)) + return true; + return false; + } +#endif + Con_Printf("NET_CompareAdr: Bad address type\n"); return false; } @@ -401,7 +481,7 @@ qboolean NET_CompareBaseAdr (netadr_t *a, netadr_t *b) return false; } #endif -#ifdef IPPROTO_IPV6 +#ifdef HAVE_IPV6 if (a->type == NA_IPV6) { if ((memcmp(a->address.ip6, b->address.ip6, 16) == 0)) @@ -409,7 +489,7 @@ qboolean NET_CompareBaseAdr (netadr_t *a, netadr_t *b) return false; } #endif -#ifdef USEIPX +#ifdef HAVE_IPX if (a->type == NA_IPX) { if ((memcmp(a->address.ipx, b->address.ipx, 10) == 0)) @@ -435,13 +515,22 @@ qboolean NET_CompareBaseAdr (netadr_t *a, netadr_t *b) } #endif +#ifdef UNIXSOCKETS + if (a->type == NA_UNIX) + { + if (a->address.un.len == b->address.un.len && !memcmp(a->address.un.path, b->address.un.path, a->address.un.len)) + return true; + return false; + } +#endif + Sys_Error("NET_CompareBaseAdr: Bad address type"); return false; } qboolean NET_AddressSmellsFunny(netadr_t *a) { -#ifdef IPPROTO_IPV6 +#ifdef HAVE_IPV6 int i; #endif @@ -463,7 +552,7 @@ qboolean NET_AddressSmellsFunny(netadr_t *a) return false; #endif -#ifdef IPPROTO_IPV6 +#ifdef HAVE_IPV6 case NA_IPV6: //reject [::XXXX] (this includes obsolete ipv4-compatible (not ipv4 mapped), and localhost) for (i = 0; i < 12; i++) @@ -474,7 +563,7 @@ qboolean NET_AddressSmellsFunny(netadr_t *a) return false; #endif -#ifdef USEIPX +#ifdef HAVE_IPX //no idea how this protocol's addresses work case NA_IPX: return false; @@ -523,12 +612,13 @@ char *NET_AdrToString (char *s, int len, netadr_t *a) { char *rs = s; char *prot = ""; -#ifdef IPPROTO_IPV6 +#ifdef HAVE_IPV6 int doneblank; #endif switch(a->prot) { + case NP_INVALID:prot = "invalid://";break; case NP_DGRAM: prot = ""; break; case NP_DTLS: prot = "dtls://"; break; case NP_STREAM: prot = "tcp://"; break; //not strictly true for ipx, but whatever. @@ -585,7 +675,7 @@ char *NET_AdrToString (char *s, int len, netadr_t *a) } break; #endif -#ifdef IPPROTO_IPV6 +#ifdef HAVE_IPV6 case NA_IPV6: { char *p; @@ -669,7 +759,7 @@ char *NET_AdrToString (char *s, int len, netadr_t *a) } break; #endif -#ifdef USEIPX +#ifdef HAVE_IPX case NA_IPX: snprintf (s, len, "%s%02x%02x%02x%02x:%02x%02x%02x%02x%02x%02x:%i", prot, @@ -699,6 +789,69 @@ char *NET_AdrToString (char *s, int len, netadr_t *a) break; #endif +#ifdef UNIXSOCKETS + case NA_UNIX: + switch(a->prot) + { + case NP_DGRAM: prot = "udg://"; break; + case NP_STREAM: prot = "unix://"; break; + default: + snprintf (s, len, "unix+"); + len-=strlen(s); + s+=strlen(s); + break; + } + snprintf (s, len, prot); + len-=strlen(s); + s+=strlen(s); + + if (len) //hopefully this will always be true... + { + char *end = a->address.un.path+a->address.un.len, *in; + char c; + for (in = a->address.un.path; in < end; in++) + { + if (--len == 0) + break; + if (*in == '\\') //ugly encoding + c = '\\'; + else if (*in == '\0') //null chars are always a problem. abstract sockets generally get them displayed using @ chars. + { + *s++ = '@'; + continue; + } + else if (*in == '@') //which means actual @ chars need to be escaped + c = '@'; + //don't screw up from these, either. + else if (*in == '\n') + c = 'n'; + else if (*in == '\r') + c = 'r'; + else if (*in == '\t') + c = 't'; + //special quake chars can screw up display too + else if (*in == '\1') + c = '1'; + else if (*in == '\2') + c = '2'; + else if (*in == '\3') + c = '3'; + else + { //as-is. + *s++ = *in; + continue; + } + //marked up chars need extra storage. + if (--len == 0) + break; + *s++ = '\\'; + *s++ = c; + } + *s = 0; //and always null terminate the string. + } + break; +#endif + default: snprintf (s, len, "invalid netadr_t type"); // Sys_Error("NET_AdrToString: Bad netadr_t type"); @@ -712,6 +865,7 @@ char *NET_BaseAdrToString (char *s, int len, netadr_t *a) char *prot = ""; switch(a->prot) { + case NP_INVALID:prot = "invalid://";break; case NP_DGRAM: prot = ""; break; case NP_DTLS: prot = "dtls://"; break; case NP_STREAM: prot = "tcp://"; break; //not strictly true for ipx, but whatever. @@ -751,7 +905,7 @@ char *NET_BaseAdrToString (char *s, int len, netadr_t *a) a->address.ip[2], a->address.ip[3]); break; -#ifdef IPPROTO_IPV6 +#ifdef HAVE_IPV6 case NA_IPV6: { char *p; @@ -809,7 +963,7 @@ char *NET_BaseAdrToString (char *s, int len, netadr_t *a) } break; #endif -#ifdef USEIPX +#ifdef HAVE_IPX case NA_IPX: snprintf (s, len, "%s%02x%02x%02x%02x:%02x%02x%02x%02x%02x%02x", prot, @@ -834,6 +988,13 @@ char *NET_BaseAdrToString (char *s, int len, netadr_t *a) NET_AdrToString(s, len, a); break; #endif + +#ifdef UNIXSOCKETS + case NA_UNIX: + //no ports, so no base paths. + return NET_AdrToString(s, len, a); +#endif + default: Sys_Error("NET_BaseAdrToString: Bad netadr_t type"); } @@ -852,7 +1013,7 @@ idnewt:28000 any form of ipv6, including port number. ============= */ -size_t NET_StringToSockaddr2 (const char *s, int defaultport, struct sockaddr_qstorage *sadr, int *addrfamily, int *addrsize, size_t addresses) +size_t NET_StringToSockaddr2 (const char *s, int defaultport, netadrtype_t afhint, struct sockaddr_qstorage *sadr, int *addrfamily, int *addrsize, size_t addresses) { size_t result = 0; @@ -861,7 +1022,59 @@ size_t NET_StringToSockaddr2 (const char *s, int defaultport, struct sockaddr_qs memset (sadr, 0, sizeof(*sadr)); -#ifdef USEIPX +#ifdef UNIXSOCKETS + if (afhint == NA_UNIX) + { + struct sockaddr_un *sa = (struct sockaddr_un *)sadr; + int i; + sa->sun_family = AF_UNIX; + + //this parsing is so annoying because I want to support abstract sockets too. + for (i = 0; *s && i < countof(sa->sun_path); ) + { + if (*s == '@') + { + sa->sun_path[i++] = 0; + s++; + } + else if (*s == '\\') + { + if (s[1] == 0) + break; //error. + else if (s[1] == '\\') + sa->sun_path[i++] = '\\'; + else if (s[1] == '@') + sa->sun_path[i++] = '@'; + else if (s[1] == 'n') + sa->sun_path[i++] = 'n'; + else if (s[1] == 'r') + sa->sun_path[i++] = 'r'; + else if (s[1] == 't') + sa->sun_path[i++] = 't'; + else if (s[1] == '1') + sa->sun_path[i++] = '1'; + else if (s[1] == '2') + sa->sun_path[i++] = '2'; + else if (s[1] == '3') + sa->sun_path[i++] = '3'; + else + sa->sun_path[i++] = '?'; + s+=2; + } + else + sa->sun_path[i++] = *s++; + } + if (i < countof(sa->sun_path)) + sa->sun_path[i] = 'X'; + if (addrsize) + *addrsize = offsetof(struct sockaddr_un, sun_path) + i; + if (addrfamily) + *addrfamily = AF_UNIX; + result++; + } + else +#endif +#ifdef HAVE_IPX if ((strlen(s) >= 23) && (s[8] == ':') && (s[21] == ':')) // check for an IPX address { unsigned int val; @@ -900,7 +1113,7 @@ size_t NET_StringToSockaddr2 (const char *s, int defaultport, struct sockaddr_qs } else #endif -#ifdef IPPROTO_IPV6 +#ifdef HAVE_IPV6 #ifdef pgetaddrinfo if (1) #else @@ -918,7 +1131,27 @@ size_t NET_StringToSockaddr2 (const char *s, int defaultport, struct sockaddr_qs double restime = Sys_DoubleTime(); memset(&udp6hint, 0, sizeof(udp6hint)); - udp6hint.ai_family = 0;//Any... we check for AF_INET6 or 4 + switch(afhint) + { +#ifdef HAVE_IPV4 + case NA_IP: + udp6hint.ai_family = AF_INET; + break; +#endif +#ifdef HAVE_IPV6 + case NA_IPV6: + udp6hint.ai_family = AF_INET6; + break; +#endif +#ifdef HAVE_IPX + case NA_IPX: + udp6hint.ai_family = AF_IPX; + break; +#endif + default: + udp6hint.ai_family = 0;//Any... we check for AF_INET6 or 4 + break; + } udp6hint.ai_socktype = SOCK_DGRAM; udp6hint.ai_protocol = 0; @@ -1070,6 +1303,48 @@ size_t NET_StringToAdr2 (const char *s, int defaultport, netadr_t *a, size_t num { size_t result = 0, i; struct sockaddr_qstorage sadr[8]; + int asize[countof(sadr)]; + netproto_t prot; + netadrtype_t afhint; + + struct + { + const char *name; + netproto_t prot; + netadrtype_t family; + } schemes[] = + { + {"udp://", NP_DGRAM, NA_INVALID}, //placeholder for dgram rather than an actual family. + {"udp4//", NP_DGRAM, NA_IP}, + {"udp6//", NP_DGRAM, NA_IPV6}, + {"ipx://", NP_DGRAM, NA_IPX}, + + //compat with qtv. we don't have any way to exclude specific protocols though. + {"qw://", NP_DGRAM, NA_INVALID}, + {"nq://", NP_DGRAM, NA_INVALID}, + {"dp://", NP_DGRAM, NA_INVALID}, + {"q2://", NP_DGRAM, NA_INVALID}, + {"q3://", NP_DGRAM, NA_INVALID}, + + {"tcp://", NP_STREAM_OR_INVALID, NA_INVALID}, //placeholder for dgram rather than an actual family. + {"tcp4//", NP_STREAM_OR_INVALID, NA_IP}, + {"tcp6//", NP_STREAM_OR_INVALID, NA_IPV6}, + {"spx://", NP_STREAM_OR_INVALID, NA_IPX}, + + {"ws://", NP_WS_OR_INVALID, NA_INVALID}, + {"wss://", NP_WSS_OR_INVALID, NA_INVALID}, + + {"tls://", NP_TLS_OR_INVALID, NA_INVALID}, + {"dtls://", NP_DTLS_OR_INVALID, NA_INVALID}, + + {"irc://", NP_INVALID, NA_INVALID}, //should have been handled explicitly, if supported. + +#ifdef UNIXSOCKETS + {"udg://", NP_DGRAM, NA_UNIX}, + {"unix://", NP_STREAM_OR_INVALID, NA_UNIX}, +#endif + }; + memset(a, 0, sizeof(*a)*numaddresses); @@ -1097,21 +1372,23 @@ size_t NET_StringToAdr2 (const char *s, int defaultport, netadr_t *a, size_t num Con_DPrintf("Resolving address: %s\n", s); #ifdef HAVE_WEBSOCKCL + //with websockets we can't really resolve anything. failure happens only when trying to connect. + //`connect /GAMENAME` is equivelent to `connect rtc://broker/GAMENAME` if (!strncmp (s, "/", 1)) { char *prefix = ""; if (!fs_manifest->rtcbroker || !*fs_manifest->rtcbroker) { //FIXME: use referrer? or the website's host? Con_DPrintf("No default rtc broker\n"); - return false; //can't accept it + return 0; //can't accept it } if (!strstr(fs_manifest->rtcbroker, "://")) prefix = "ws://"; Q_snprintfz(a->address.websocketurl, sizeof(a->address.websocketurl), "%s%s%s", prefix, fs_manifest->rtcbroker, s); - return true; + return 1; } else if (!strncmp (s, "rtc://", 6) || !strncmp (s, "rtcs://", 7)) - { + { //basically ICE using sdp-via-websockets to a named relay server. const char *prot, *host, *path; a->type = NA_WEBSOCKET; a->prot = NP_DTLS; @@ -1129,14 +1406,14 @@ size_t NET_StringToAdr2 (const char *s, int defaultport, netadr_t *a, size_t num { host = fs_manifest->rtcbroker; if (!host || !*host) //can't guess the host - return false; + return 0; if (strstr(host, "://")) prot = ""; } else host = ""; Q_snprintfz(a->address.websocketurl, sizeof(a->address.websocketurl), "%s%s%s", prot, host, path); - return true; + return 1; } else if (!strncmp (s, "ws://", 5) || !strncmp (s, "wss://", 6)) { @@ -1146,7 +1423,7 @@ size_t NET_StringToAdr2 (const char *s, int defaultport, netadr_t *a, size_t num else a->prot = NP_WS; Q_strncpyz(a->address.websocketurl, s, sizeof(a->address.websocketurl)); - return true; + return 1; } else { @@ -1161,91 +1438,13 @@ size_t NET_StringToAdr2 (const char *s, int defaultport, netadr_t *a, size_t num a->prot = NP_WS; memcpy(a->address.websocketurl, "ws://", 5); Q_strncpyz(a->address.websocketurl+5, s, sizeof(a->address.websocketurl)-5); - return true; + return 1; } #endif -#ifdef TCPCONNECT - if (!strncmp (s, "tcp://", 6)) - { - //make sure that the rest of the address is a valid ip address (4 or 6) - if (!NET_StringToSockaddr (s+6, defaultport, &sadr[0], NULL, NULL)) - { - a->type = NA_INVALID; - return false; - } - SockadrToNetadr (&sadr[0], a); - a->prot = NP_STREAM; - return true; - } - if (!strncmp (s, "ws://", 5)) - { - //make sure that the rest of the address is a valid ip address (4 or 6) - if (!NET_StringToSockaddr (s+5, defaultport, &sadr[0], NULL, NULL)) - { - a->type = NA_INVALID; - return false; - } - - SockadrToNetadr (&sadr[0], a); - a->prot = NP_WS; - return true; - } - if (!strncmp (s, "wss://", 6)) - { -#ifndef HAVE_SSL - return false; -#else - //make sure that the rest of the address is a valid ip address (4 or 6) - if (!NET_StringToSockaddr (s+6, defaultport, &sadr[0], NULL, NULL)) - { - a->type = NA_INVALID; - return false; - } - - SockadrToNetadr (&sadr[0], a); - a->prot = NP_WSS; - return true; -#endif - } - if (!strncmp (s, "tls://", 6)) - { -#ifndef HAVE_SSL - return false; -#else - //make sure that the rest of the address is a valid ip address (4 or 6) - if (!NET_StringToSockaddr (s+6, defaultport, &sadr[0], NULL, NULL)) - { - a->type = NA_INVALID; - return false; - } - - SockadrToNetadr (&sadr[0], a); - a->prot = NP_TLS; - return true; -#endif - } -#endif - if (!strncmp (s, "dtls://", 7)) - { -#ifndef HAVE_DTLS - return false; -#else - //make sure that the rest of the address is a valid ip address (4 or 6) - if (!NET_StringToSockaddr (s+7, defaultport, &sadr[0], NULL, NULL)) - { - a->type = NA_INVALID; - return false; - } - - SockadrToNetadr (&sadr[0], a); - a->prot = NP_DTLS; - return true; -#endif - } -#ifdef IRCCONNECT if (!strncmp (s, "irc://", 6)) { +#ifdef IRCCONNECT char *at; char *slash; memset (a, 0, sizeof(*a)); @@ -1273,9 +1472,11 @@ size_t NET_StringToAdr2 (const char *s, int defaultport, netadr_t *a, size_t num //just a user. Q_strncpyz(a->address.irc.user, s, sizeof(a->address.irc.user)); } - return true; - } + return 1; +#else + return 0; #endif + } #ifdef HAVE_NATPMP if (!strncmp (s, "natpmp://", 9)) { //our natpmp thing omits the host part. FIXME: host should be the NAT that we're sending to @@ -1286,10 +1487,22 @@ size_t NET_StringToAdr2 (const char *s, int defaultport, netadr_t *a, size_t num } #endif - result = NET_StringToSockaddr2 (s, defaultport, sadr, NULL, NULL, min(numaddresses, sizeof(sadr)/sizeof(sadr[0]))); + for (prot = NP_DGRAM, afhint = NA_INVALID/*any*/, i = 0; i < countof(schemes); i++) + { + if (!strncmp(s, schemes[i].name, strlen(schemes[i].name))) + { + s += strlen(schemes[i].name); + prot = schemes[i].prot; + afhint = schemes[i].family; + break; + } + } + + result = NET_StringToSockaddr2 (s, defaultport, afhint, sadr, NULL, asize, min(numaddresses, countof(sadr))); for (i = 0; i < result; i++) { - SockadrToNetadr (&sadr[i], &a[i]); + SockadrToNetadr (&sadr[i], asize[i], &a[i]); + a[i].prot = prot; } //invalidate any others @@ -1338,7 +1551,7 @@ void NET_IntegerToMask (netadr_t *a, netadr_t *amask, int bits) } break; case NA_IPV6: -#ifdef IPPROTO_IPV6 +#ifdef HAVE_IPV6 n = amask->address.ip6; if (i > 128) i = 128; @@ -1358,7 +1571,7 @@ void NET_IntegerToMask (netadr_t *a, netadr_t *amask, int bits) #endif break; case NA_IPX: -#ifdef USEIPX +#ifdef HAVE_IPX n = amask->address.ipx; if (i > 80) i = 80; @@ -1379,7 +1592,9 @@ void NET_IntegerToMask (netadr_t *a, netadr_t *amask, int bits) break; case NA_LOOPBACK: break; - // warning: enumeration value NA_* not handled in switch +#ifdef UNIXSOCKETS + case NA_UNIX: //address masks/filtering don't make sense. +#endif #ifdef HAVE_WEBSOCKCL case NA_WEBSOCKET: #endif @@ -1629,7 +1844,7 @@ qboolean NET_CompareAdrMasked(netadr_t *a, netadr_t *b, netadr_t *mask) return false; } break; -#ifdef IPPROTO_IPV6 +#ifdef HAVE_IPV6 case NA_IPV6: for (i = 0; i < 16; i++) { @@ -1638,7 +1853,7 @@ qboolean NET_CompareAdrMasked(netadr_t *a, netadr_t *b, netadr_t *mask) } break; #endif -#ifdef USEIPX +#ifdef HAVE_IPX case NA_IPX: for (i = 0; i < 10; i++) { @@ -1701,7 +1916,7 @@ int UniformMaskedBits(netadr_t *mask) bits -= 8; } break; -#ifdef IPPROTO_IPV6 +#ifdef HAVE_IPV6 case NA_IPV6: bits = 128; for (b = 15; b >= 0; b--) @@ -1731,7 +1946,7 @@ int UniformMaskedBits(netadr_t *mask) } break; #endif -#ifdef USEIPX +#ifdef HAVE_IPX case NA_IPX: bits = 80; for (b = 9; b >= 0; b--) @@ -2532,9 +2747,17 @@ qboolean FTENET_AddToCollection(ftenet_connections_t *col, const char *name, con #ifdef HAVE_IPV6 if ((adr[i].prot == NP_DGRAM) && adr[i].type == NA_IPV6) establish[i] = FTENET_Datagram_EstablishConnection; else #endif -#ifdef USEIPX +#ifdef HAVE_IPX if (adr[i].prot == NP_DGRAM && adr[i].type == NA_IPX) establish[i] = FTENET_Datagram_EstablishConnection; else #endif +#ifdef UNIXSOCKETS + if (adr[i].prot == NP_DGRAM && adr[i].type == NA_UNIX) establish[i] = FTENET_Datagram_EstablishConnection; else + #if defined(TCPCONNECT) + if (adr[i].prot == NP_STREAM&& adr[i].type == NA_UNIX) establish[i] = FTENET_TCPConnect_EstablishConnection; else + if (adr[i].prot == NP_WS && adr[i].type == NA_UNIX) establish[i] = FTENET_TCPConnect_EstablishConnection; else + if (adr[i].prot == NP_TLS && adr[i].type == NA_UNIX) establish[i] = FTENET_TCPConnect_EstablishConnection; else + #endif +#endif #if defined(TCPCONNECT) && defined(HAVE_IPV4) if (adr[i].prot == NP_WS && adr[i].type == NA_IP) establish[i] = FTENET_TCPConnect_EstablishConnection; else if (adr[i].prot == NP_STREAM&& adr[i].type == NA_IP) establish[i] = FTENET_TCPConnect_EstablishConnection; else @@ -2606,7 +2829,7 @@ int FTENET_GetLocalAddress(int port, qboolean ipx, qboolean ipv4, qboolean ipv6, int found = 0; gethostname(adrs, sizeof(adrs)); -#ifdef IPPROTO_IPV6 +#ifdef HAVE_IPV6 if (pgetaddrinfo) { struct addrinfo hints, *result, *itr; @@ -2622,13 +2845,13 @@ int FTENET_GetLocalAddress(int port, qboolean ipx, qboolean ipv4, qboolean ipv6, { if ((itr->ai_addr->sa_family == AF_INET && ipv4) || (itr->ai_addr->sa_family == AF_INET6 && ipv6) -#ifdef USEIPX +#ifdef HAVE_IPX || (itr->ai_addr->sa_family == AF_IPX && ipx) #endif ) if (maxaddresses) { - SockadrToNetadr((struct sockaddr_qstorage*)itr->ai_addr, addresses); + SockadrToNetadr((struct sockaddr_qstorage*)itr->ai_addr, sizeof(struct sockaddr_qstorage), addresses); addresses->port = port; *adrflags++ = 0; addresses++; @@ -2673,7 +2896,7 @@ int FTENET_GetLocalAddress(int port, qboolean ipx, qboolean ipv4, qboolean ipv6, from.sin_family = AF_INET; from.sin_port = port; memcpy(&from.sin_addr, h->h_addr_list[b], sizeof(from.sin_addr)); - SockadrToNetadr((struct sockaddr_qstorage*)&from, addresses); + SockadrToNetadr((struct sockaddr_qstorage*)&from, sizeof(from), addresses); *adrflags++ = 0; addresses++; @@ -2682,7 +2905,7 @@ int FTENET_GetLocalAddress(int port, qboolean ipx, qboolean ipv4, qboolean ipv6, } } #endif -#ifdef IPPROTO_IPV6 +#ifdef HAVE_IPV6 if(h && h->h_addrtype == AF_INET6) { for (b = 0; h->h_addr_list[b] && maxaddresses; b++) @@ -2692,7 +2915,7 @@ int FTENET_GetLocalAddress(int port, qboolean ipx, qboolean ipv4, qboolean ipv6, from.sin6_port = port; from.sin6_scope_id = 0; memcpy(&from.sin6_addr, h->h_addr_list[b], sizeof(((struct sockaddr_in6*)&from)->sin6_addr)); - SockadrToNetadr((struct sockaddr_qstorage*)&from, addresses); + SockadrToNetadr((struct sockaddr_qstorage*)&from, sizeof(from), addresses); *adrflags++ = 0; addresses++; maxaddresses--; @@ -2742,15 +2965,15 @@ int FTENET_GetLocalAddress(int port, qboolean ipx, qboolean ipv4, qboolean ipv6, #ifdef HAVE_IPV4 (fam == AF_INET && ipv4) || #endif -#ifdef IPPROTO_IPV6 +#ifdef HAVE_IPV6 (fam == AF_INET6 && ipv6) || #endif -#ifdef USEIPX +#ifdef HAVE_IPX (fam == AF_IPX && ipx) || #endif 0) { - SockadrToNetadr((struct sockaddr_qstorage*)ifa->ifa_addr, &addresses[idx]); + SockadrToNetadr((struct sockaddr_qstorage*)ifa->ifa_addr, sizeof(struct sockaddr_qstorage), &addresses[idx]); addresses[idx].port = port; adrflags[idx] = 0; idx++; @@ -2778,7 +3001,7 @@ int FTENET_Generic_GetLocalAddresses(struct ftenet_generic_connection_s *con, un if (getsockname (con->thesocket, (struct sockaddr*)&from, &fromsize) != -1) { memset(&adr, 0, sizeof(adr)); - SockadrToNetadr(&from, &adr); + SockadrToNetadr(&from, fromsize, &adr); #ifdef USE_GETHOSTNAME_LOCALLISTING //if its bound to 'any' address, ask the system what addresses it actually accepts. @@ -2889,7 +3112,7 @@ qboolean FTENET_Datagram_GetPacket(ftenet_generic_connection_t *con) unsigned int curtime = Sys_Milliseconds(); if (curtime-resettime >= 5000) //throttle prints to once per 5 secs (even if they're about different clients, yay ddos) { - SockadrToNetadr (&from, &net_from); + SockadrToNetadr (&from, fromlen, &net_from); Con_TPrintf ("Warning: Oversize packet from %s\n", NET_AdrToString (adr, sizeof(adr), &net_from)); } @@ -2903,7 +3126,7 @@ qboolean FTENET_Datagram_GetPacket(ftenet_generic_connection_t *con) { if (((struct sockaddr*)&from)->sa_family != AF_UNSPEC) { - SockadrToNetadr (&from, &net_from); + SockadrToNetadr (&from, fromlen, &net_from); Con_TPrintf ("Connection lost or aborted (%s)\n", NET_AdrToString (adr, sizeof(adr), &net_from)); //server died/connection lost. } else @@ -2930,7 +3153,14 @@ qboolean FTENET_Datagram_GetPacket(ftenet_generic_connection_t *con) Con_Printf ("NET_GetPacket: Error (%i): %s\n", err, strerror(err)); return false; } - SockadrToNetadr (&from, &net_from); + + SockadrToNetadr (&from, fromlen, &net_from); + + if (net_from.type == NA_INVALID) + { //this really shouldn't happen. Blame the OS. + Con_TPrintf ("Warning: sender's address type not known (%i)\n", (int)((struct sockaddr*)&from)->sa_family); + return false; //packet from an unsupported protocol? no way can we respond, so what's the point + } net_message.packing = SZ_RAWBYTES; net_message.currentbit = 0; @@ -2963,7 +3193,7 @@ neterr_t FTENET_Datagram_SendPacket(ftenet_generic_connection_t *con, int length if (size == FTENET_ADDRTYPES) return NETERR_NOROUTE; -#ifdef IPPROTO_IPV6 +#ifdef HAVE_IPV6 /*special code to handle sending to hybrid sockets*/ if (con->addrtype[1] == NA_IPV6 && to->type == NA_IP) { @@ -3059,7 +3289,7 @@ static qboolean FTENET_Datagram_ChangeLocalAddress(struct ftenet_generic_connect int namelen = sizeof(address); if (getsockname (con->thesocket, (struct sockaddr *)&address, &namelen) == 0) { - SockadrToNetadr(&address, ¤t); + SockadrToNetadr(&address, namelen, ¤t); //make sure the types match (special check for ipv6 hybrid sockets that accept ipv4 too) if (adr->type == current.type @@ -3107,7 +3337,7 @@ ftenet_generic_connection_t *FTENET_Datagram_EstablishConnection(qboolean isserv protocol = IPPROTO_UDP; break; #endif -#ifdef USEIPX +#ifdef HAVE_IPX case NA_IPX: protocol = NSPROTO_IPX; break; @@ -3126,7 +3356,7 @@ ftenet_generic_connection_t *FTENET_Datagram_EstablishConnection(qboolean isserv temp = NetadrToSockadr(&adr, &qs); family = ((struct sockaddr*)&qs)->sa_family; -#if defined(IPPROTO_IPV6) && defined(IPV6_V6ONLY) +#if defined(HAVE_IPV6) && defined(IPV6_V6ONLY) if (/*isserver &&*/ family == AF_INET && net_hybriddualstack.ival && !((struct sockaddr_in*)&qs)->sin_addr.s_addr) { unsigned long _false = false; @@ -3171,7 +3401,7 @@ ftenet_generic_connection_t *FTENET_Datagram_EstablishConnection(qboolean isserv return NULL; } -#if defined(IPPROTO_IPV6) && defined(IPV6_V6ONLY) +#if defined(HAVE_IPV6) && defined(IPV6_V6ONLY) if (family == AF_INET6) setsockopt(newsocket, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&_true, sizeof(_true)); #endif @@ -3184,42 +3414,81 @@ ftenet_generic_connection_t *FTENET_Datagram_EstablishConnection(qboolean isserv bufsz = 1<<18; setsockopt(newsocket, SOL_SOCKET, SO_RCVBUF, (void*)&bufsz, sizeof(bufsz)); - //try and find an unused port. - port = ntohs(((struct sockaddr_in*)&qs)->sin_port); - for (bindtries = 0; bindtries < bindmaxtries; bindtries++) + switch(family) { - ((struct sockaddr_in*)&qs)->sin_port = htons((unsigned short)(port+bindtries)); - if ((bind(newsocket, (struct sockaddr *)&qs, temp) == INVALID_SOCKET)) +#ifdef UNIXSOCKETS + case AF_UNIX: { - if (port == 0) - { //if binding to an ephemerial port failed, binding to the admin-only ports won't work any better... - bindtries = bindmaxtries; - break; + struct sockaddr_un *sa = (struct sockaddr_un *)&qs; + if (!isserver) + temp = (char*)&sa->sun_path[0] - (char*)sa; //linux-specific: bind to an automatic abstract address. + if (*sa->sun_path && isserver) + { //non-abstract sockets don't clean up the filesystem when the socket is closed + //and we can't re-bind to it while it still exists. + //so standard practise is to delete it before the bind. + //we do want to make sure the file is actually a socket before we remove it (so people can't abuse stuffcmds) + struct stat s; + if (stat(sa->sun_path, &s)!=-1) + { + if ((s.st_mode & S_IFMT) == S_IFSOCK) + unlink(sa->sun_path); + } + } + if (bind(newsocket, (struct sockaddr *)sa, temp) == INVALID_SOCKET) + { +// perror("gah"); + SockadrToNetadr(&qs, temp, &adr); + NET_AdrToString(addrstr, sizeof(addrstr), &adr); + Con_Printf(CON_ERROR "Unable to bind to %s\n", addrstr); + closesocket(newsocket); + return NULL; } - continue; } break; - } - if (bindtries == bindmaxtries) - { - SockadrToNetadr(&qs, &adr); - NET_AdrToString(addrstr, sizeof(addrstr), &adr); - Con_Printf(CON_ERROR "Unable to listen at %s\n", addrstr); - closesocket(newsocket); - return NULL; - } - else if (bindtries && isserver) - { - SockadrToNetadr(&qs, &adr); - NET_AdrToString(addrstr, sizeof(addrstr), &adr); - Con_Printf(CON_ERROR "Unable to bind to port %i, bound to %s instead\n", port, addrstr); +#endif + +// case AF_INET: +// case AF_INET6: +// case AF_IPX: + default: + //try and find an unused port. + port = ntohs(((struct sockaddr_in*)&qs)->sin_port); + for (bindtries = 0; bindtries < bindmaxtries; bindtries++) + { + ((struct sockaddr_in*)&qs)->sin_port = htons((unsigned short)(port+bindtries)); + if ((bind(newsocket, (struct sockaddr *)&qs, temp) == INVALID_SOCKET)) + { + if (port == 0) + { //if binding to an ephemerial port failed, binding to the admin-only ports won't work any better... + bindtries = bindmaxtries; + break; + } + continue; + } + break; + } + if (bindtries == bindmaxtries) + { + SockadrToNetadr(&qs, temp, &adr); + NET_AdrToString(addrstr, sizeof(addrstr), &adr); + Con_Printf(CON_ERROR "Unable to listen at %s\n", addrstr); + closesocket(newsocket); + return NULL; + } + else if (bindtries && isserver) + { + SockadrToNetadr(&qs, temp, &adr); + NET_AdrToString(addrstr, sizeof(addrstr), &adr); + Con_Printf(CON_ERROR "Unable to bind to port %i, bound to %s instead\n", port, addrstr); + } + break; } if (ioctlsocket (newsocket, FIONBIO, &_true) == -1) Sys_Error ("UDP_OpenSocket: ioctl FIONBIO: %s", strerror(neterrno())); //ipv6 sockets need to add themselves to a multicast group, so that we can receive broadcasts on a lan -#if defined(IPPROTO_IPV6) +#if defined(HAVE_IPV6) if (family == AF_INET6 || hybrid || isserver) { struct ipv6_mreq req; @@ -4016,12 +4285,12 @@ qboolean FTENET_TCP_ParseHTTPRequest(ftenet_tcpconnect_connection_t *con, ftenet attr = WCATTR_WSKEY; else if (j-i == 21 && !strnicmp(&st->inbuffer[i], "Sec-WebSocket-Version", 21)) attr = WCATTR_WSVER; -// else if (j-i == 6 && !strnicmp(&st->inbuffer[i], "Origin", j-i)) -// attr = WCATTR_ORIGIN; +// else if (j-i == 6 && !strnicmp(&st->inbuffer[i], "Origin", j-i)) +// attr = WCATTR_ORIGIN; else if (j-i == 22 && !strnicmp(&st->inbuffer[i], "Sec-WebSocket-Protocol", 22)) attr = WCATTR_WSPROTO; -// else if (j-i == 24 && !strnicmp(&st->inbuffer[i], "Sec-WebSocket-Extensions", 24)) -// attr = WCATTR_WSEXT; +// else if (j-i == 24 && !strnicmp(&st->inbuffer[i], "Sec-WebSocket-Extensions", 24)) +// attr = WCATTR_WSEXT; //http stuff else if (j-i == 14 && !strnicmp(&st->inbuffer[i], "Content-Length", 14)) attr = WCATTR_CONTENT_LENGTH; //in case they're trying to post/put stuff @@ -4842,7 +5111,7 @@ closesvstream: con->active++; st = Z_Malloc(sizeof(*con->tcpstreams)); /*grab the net address*/ - SockadrToNetadr(&from, &st->remoteaddr); + SockadrToNetadr(&from, fromlen, &st->remoteaddr); st->clienttype = TCPC_UNKNOWN; st->next = con->tcpstreams; con->tcpstreams = st; @@ -4969,10 +5238,29 @@ qboolean FTENET_TCPConnect_ChangeLocalAddress(struct ftenet_generic_connection_s netadr_t n; SOCKET newsocket; unsigned long _true = true; + int sysprot; addrsize = NetadrToSockadr(adr, &qs); family = ((struct sockaddr*)&qs)->sa_family; + switch(adr->type) + { +#if defined(HAVE_IPV4) || defined(HAVE_IPV6) + case NA_IP: + case NA_IPV6: + sysprot = IPPROTO_TCP; + break; +#endif +#ifdef HAVE_IPX + case NA_IPX: + sysprot = NSPROTO_IPX; + break; +#endif + default: + sysprot = 0; + break; + } + if (con->thesocket != INVALID_SOCKET) { addrsize2 = sizeof(cur); @@ -4980,7 +5268,7 @@ qboolean FTENET_TCPConnect_ChangeLocalAddress(struct ftenet_generic_connection_s if (addrsize == addrsize2) { - SockadrToNetadr(&cur, &n); + SockadrToNetadr(&cur, addrsize2, &n); if (NET_CompareAdr(adr, &n)) //the address+port we're trying is already current, apparently. return true; } @@ -4989,11 +5277,11 @@ qboolean FTENET_TCPConnect_ChangeLocalAddress(struct ftenet_generic_connection_s con->thesocket = INVALID_SOCKET; } -#if defined(IPPROTO_IPV6) && defined(IPV6_V6ONLY) +#if defined(HAVE_IPV6) && defined(IPV6_V6ONLY) if (family == AF_INET && net_hybriddualstack.ival && !((struct sockaddr_in*)&qs)->sin_addr.s_addr) { //hybrid sockets pathway takes over when INADDR_ANY unsigned long _false = false; - if ((newsocket = socket (AF_INET6, SOCK_STREAM, IPPROTO_TCP)) != INVALID_SOCKET) + if ((newsocket = socket (AF_INET6, SOCK_STREAM, sysprot)) != INVALID_SOCKET) { if (0 == setsockopt(newsocket, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&_false, sizeof(_false))) { @@ -5022,14 +5310,36 @@ qboolean FTENET_TCPConnect_ChangeLocalAddress(struct ftenet_generic_connection_s } #endif - if ((newsocket = socket (family, SOCK_STREAM, IPPROTO_TCP)) != INVALID_SOCKET) + if ((newsocket = socket (family, SOCK_STREAM, sysprot)) != INVALID_SOCKET) { +#ifdef UNIXSOCKETS + if (family == AF_UNIX) + { + struct sockaddr_un *un = (struct sockaddr_un *)&qs; + struct stat s; + if (*un->sun_path) + { //non-abstract sockets don't clean up the filesystem when the socket is closed + //and we can't re-bind to it while it still exists. + //so standard practise is to delete it before the bind. + //we do want to make sure the file is actually a socket before we remove it (so people can't abuse stuffcmds) + if (stat(un->sun_path, &s)!=-1) + { + if ((s.st_mode & S_IFMT) == S_IFSOCK) + unlink(un->sun_path); + } + } + } +#endif + if ((bind(newsocket, (struct sockaddr *)&qs, addrsize) != INVALID_SOCKET) && (listen(newsocket, 2) != INVALID_SOCKET)) { if (ioctlsocket (newsocket, FIONBIO, &_true) != -1) { - setsockopt(newsocket, IPPROTO_TCP, TCP_NODELAY, (char *)&_true, sizeof(_true)); +#ifdef UNIXSOCKETS + if (family != NA_UNIX) +#endif + setsockopt(newsocket, IPPROTO_TCP, TCP_NODELAY, (char *)&_true, sizeof(_true)); con->thesocket = newsocket; return true; @@ -5141,8 +5451,13 @@ ftenet_generic_connection_t *FTENET_TCPConnect_EstablishConnection(qboolean isse return NULL; } - //this isn't fatal - setsockopt(newsocket, IPPROTO_TCP, TCP_NODELAY, (char *)&_true, sizeof(_true)); +#ifdef UNIXSOCKETS + if (adr.type != NA_UNIX) +#endif + { + //this isn't fatal + setsockopt(newsocket, IPPROTO_TCP, TCP_NODELAY, (char *)&_true, sizeof(_true)); + } } @@ -6563,7 +6878,7 @@ static neterr_t NET_SendPacketCol (ftenet_connections_t *collection, int length, if (!collection) return NETERR_NOROUTE; - if (net_fakeloss.value) + if (net_fakeloss.value && data) { if (frandom () < net_fakeloss.value) { @@ -6664,32 +6979,26 @@ neterr_t NET_SendPacket (netsrc_t netsrc, int length, const void *data, netadr_t return NET_SendPacketCol (collection, length, data, to); } -qboolean NET_EnsureRoute(ftenet_connections_t *collection, char *routename, char *host) +qboolean NET_EnsureRoute(ftenet_connections_t *collection, char *routename, char *host, netadr_t *adr) { - netadr_t adr; - - if (NET_StringToAdr(host, 0, &adr)) + switch(adr->prot) { - switch(adr.prot) - { - case NP_DTLS: -#ifdef HAVE_DTLS -// if (FTENET_DTLS_Create(collection, &adr) == NETERR_NOROUTE) -/// return false; - break; -#endif - case NP_WS: - case NP_WSS: - case NP_TLS: - case NP_STREAM: - if (!FTENET_AddToCollection(collection, routename, host, adr.type, adr.prot)) - return false; - Con_Printf("Establishing connection to %s\n", host); - break; - default: - //not recognised, or not needed - break; - } + case NP_DTLS: + break; + case NP_DGRAM: + if (NET_SendPacketCol(collection, 0, NULL, adr) != NETERR_NOROUTE) + return true; + case NP_WS: + case NP_WSS: + case NP_TLS: + case NP_STREAM: + if (!FTENET_AddToCollection(collection, routename, host, adr->type, adr->prot)) + return false; + Con_Printf("Establishing connection to %s\n", host); + break; + default: + //not recognised, or not needed + break; } return true; } @@ -6850,10 +7159,28 @@ int TCP_OpenStream (netadr_t *remoteaddr) struct sockaddr_qstorage qs; // struct sockaddr_qstorage loc; int recvbufsize = (1<<19);//512kb + int sysprot; + switch(remoteaddr->type) + { +#if defined(HAVE_IPV4) || defined(HAVE_IPV6) + case NA_IP: + case NA_IPV6: + sysprot = IPPROTO_TCP; + break; +#endif +#ifdef HAVE_IPX + case NA_IPX: + protocol = NSPROTO_IPX; + break; +#endif + default: + sysprot = 0; + break; + } temp = NetadrToSockadr(remoteaddr, &qs); - if ((newsocket = socket (((struct sockaddr_in*)&qs)->sin_family, SOCK_CLOEXEC|SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) + if ((newsocket = socket (((struct sockaddr_in*)&qs)->sin_family, SOCK_CLOEXEC|SOCK_STREAM, sysprot)) == INVALID_SOCKET) return (int)INVALID_SOCKET; setsockopt(newsocket, SOL_SOCKET, SO_RCVBUF, (void*)&recvbufsize, sizeof(recvbufsize)); @@ -6861,9 +7188,20 @@ int TCP_OpenStream (netadr_t *remoteaddr) if (ioctlsocket (newsocket, FIONBIO, &_true) == -1) Sys_Error ("UDP_OpenSocket: ioctl FIONBIO: %s", strerror(neterrno())); -// memset(&loc, 0, sizeof(loc)); -// ((struct sockaddr*)&loc)->sa_family = ((struct sockaddr*)&loc)->sa_family; -// bind(newsocket, (struct sockaddr *)&loc, ((struct sockaddr_in*)&qs)->sin_family == AF_INET?sizeof(struct sockaddr_in):sizeof(struct sockaddr_in6)); +#ifdef UNIXSOCKETS + if (remoteaddr->type == AF_UNIX) + { //if its a unix socket, attempt to bind it to an unnamed address. linux should generate an ephemerial abstract address (otherwise the server will see an empty address). + struct sockaddr_un un; + memset(&un, 0, offsetof(struct sockaddr_un, sun_path)); + bind(newsocket, &un, offsetof(struct sockaddr_un, sun_path)); + } + else +#endif + { +// memset(&loc, 0, sizeof(loc)); +// ((struct sockaddr*)&loc)->sa_family = ((struct sockaddr*)&loc)->sa_family; +// bind(newsocket, (struct sockaddr *)&loc, ((struct sockaddr_in*)&qs)->sin_family == AF_INET?sizeof(struct sockaddr_in):sizeof(struct sockaddr_in6)); + } if (connect(newsocket, (struct sockaddr *)&qs, temp) == INVALID_SOCKET) { @@ -6879,6 +7217,8 @@ int TCP_OpenStream (netadr_t *remoteaddr) else Con_Printf ("TCP_OpenStream: invalid address trying to connect to %s\n", buf); } + else if (err == NET_ECONNREFUSED) + Con_Printf ("TCP_OpenStream: connection refused (%s)\n", buf); else if (err == NET_EACCES) Con_Printf ("TCP_OpenStream: access denied: check firewall (%s)\n", buf); else @@ -7017,7 +7357,7 @@ int maxport = port + 100; return newsocket; } -#ifdef IPPROTO_IPV6 +#ifdef HAVE_IPV6 int UDP6_OpenSocket (int port) { int err; @@ -7091,7 +7431,7 @@ void UDP_CloseSocket (int socket) int IPX_OpenSocket (int port) { -#ifndef USEIPX +#ifndef HAVE_IPX return 0; #else SOCKET newsocket; @@ -7134,7 +7474,7 @@ int IPX_OpenSocket (int port) void IPX_CloseSocket (int socket) { -#ifdef USEIPX +#ifdef HAVE_IPX closesocket(socket); #endif } @@ -7232,11 +7572,11 @@ void NET_GetLocalAddress (int socket, netadr_t *out) if (getsockname (socket, (struct sockaddr *)&address, &namelen) == -1) { notvalid = true; - NET_StringToSockaddr2("0.0.0.0", 0, (struct sockaddr_qstorage *)&address, NULL, NULL, 1); + NET_StringToSockaddr2("0.0.0.0", 0, NA_INVALID, (struct sockaddr_qstorage *)&address, NULL, NULL, 1); // Sys_Error ("NET_Init: getsockname:", strerror(qerrno)); } - SockadrToNetadr(&address, out); + SockadrToNetadr(&address, namelen, out); if (out->type == NA_IP) { if (!*(int*)out->address.ip) //socket was set to auto @@ -7363,7 +7703,7 @@ void NET_Init (void) { #if defined(_WIN32) && defined(HAVE_PACKET) int r; -#ifdef IPPROTO_IPV6 +#ifdef HAVE_IPV6 dllfunction_t fncs[] = { {(void**)&pgetaddrinfo, "getaddrinfo"}, @@ -7451,7 +7791,7 @@ void NET_InitClient(qboolean loopbackonly) FTENET_AddToCollection(cls.sockets, "CLUDP6", port, NA_IPV6, NP_DGRAM); #endif } -#ifdef USEIPX +#ifdef HAVE_IPX FTENET_AddToCollection(cls.sockets, "CLIPX", port, NA_IPX, NP_DGRAM); #endif @@ -7474,7 +7814,7 @@ cvar_t qtv_streamport = CVARAFCD( "qtv_streamport", "", "mvd_streamport", 0, SV_Tcpport_Callback, "Legacy cvar. Use sv_port_tcp instead."); #endif #endif -#ifdef IPPROTO_IPV6 +#ifdef HAVE_IPV6 void QDECL SV_Tcpport6_Callback(struct cvar_s *var, char *oldvalue) { FTENET_AddToCollection(svs.sockets, var->name, var->string, NA_IPV6, NP_STREAM); @@ -7488,20 +7828,27 @@ void QDECL SV_Port_Callback(struct cvar_s *var, char *oldvalue) } cvar_t sv_port_ipv4 = CVARC("sv_port", STRINGIFY(PORT_DEFAULTSERVER), SV_Port_Callback); #endif -#ifdef IPPROTO_IPV6 +#ifdef HAVE_IPV6 void QDECL SV_PortIPv6_Callback(struct cvar_s *var, char *oldvalue) { FTENET_AddToCollection(svs.sockets, var->name, var->string, NA_IPV6, NP_DGRAM); } cvar_t sv_port_ipv6 = CVARCD("sv_port_ipv6", "", SV_PortIPv6_Callback, "Port to use for incoming ipv6 udp connections. Due to hybrid sockets this might not be needed. You can specify an ipv4 address:port for a second ipv4 port if you want."); #endif -#ifdef USEIPX +#ifdef HAVE_IPX void QDECL SV_PortIPX_Callback(struct cvar_s *var, char *oldvalue) { FTENET_AddToCollection(svs.sockets, var->name, var->string, NA_IPX, NP_DGRAM); } cvar_t sv_port_ipx = CVARC("sv_port_ipx", "", SV_PortIPX_Callback); #endif +#ifdef HAVE_IPX +void QDECL SV_PortUNIX_Callback(struct cvar_s *var, char *oldvalue) +{ + FTENET_AddToCollection(svs.sockets, var->name, var->string, NA_UNIX, NP_DGRAM); +} +cvar_t sv_port_unix = CVARC("sv_port_unix", "/tmp/fte.sock", SV_PortUNIX_Callback); +#endif #ifdef HAVE_NATPMP void QDECL SV_Port_NatPMP_Callback(struct cvar_s *var, char *oldvalue) { @@ -7537,15 +7884,15 @@ void SVNET_RegisterCvars(void) qtv_streamport.restriction = RESTRICT_MAX; #endif #endif -#if defined(TCPCONNECT) && defined(IPPROTO_IPV6) +#if defined(TCPCONNECT) && defined(HAVE_IPV6) Cvar_Register (&sv_port_tcp6, "networking"); sv_port_tcp6.restriction = RESTRICT_MAX; #endif -#ifdef IPPROTO_IPV6 +#ifdef HAVE_IPV6 Cvar_Register (&sv_port_ipv6, "networking"); sv_port_ipv6.restriction = RESTRICT_MAX; #endif -#ifdef USEIPX +#ifdef HAVE_IPX Cvar_Register (&sv_port_ipx, "networking"); sv_port_ipx.restriction = RESTRICT_MAX; #endif @@ -7557,6 +7904,9 @@ void SVNET_RegisterCvars(void) Cvar_Register (&sv_port_natpmp, "networking"); sv_port_natpmp.restriction = RESTRICT_MAX; #endif +#ifdef UNIXSOCKETS +// Cvar_Register (&sv_port_unix, "networking"); +#endif #if defined(TCPCONNECT) && !defined(CLIENTONLY) @@ -7580,11 +7930,12 @@ void NET_CloseServer(void) void NET_InitServer(void) { - if (sv_listen_nq.value || sv_listen_dp.value || sv_listen_qw.value + qboolean singleplayer = (sv.allocated_client_slots == 1) && !isDedicated; + if ((sv_listen_nq.value || sv_listen_dp.value || sv_listen_qw.value #ifdef QWOVERQ3 || sv_listen_q3.ival #endif - ) + ) && !singleplayer) { if (!svs.sockets) { @@ -7600,10 +7951,10 @@ void NET_InitServer(void) #ifdef HAVE_IPV4 Cvar_ForceCallback(&sv_port_ipv4); #endif -#ifdef IPPROTO_IPV6 +#ifdef HAVE_IPV6 Cvar_ForceCallback(&sv_port_ipv6); #endif -#ifdef USEIPX +#ifdef HAVE_IPX Cvar_ForceCallback(&sv_port_ipx); #endif #if defined(TCPCONNECT) && defined(HAVE_TCP) @@ -7611,13 +7962,16 @@ void NET_InitServer(void) #ifndef NOLEGACY Cvar_ForceCallback(&qtv_streamport); #endif -#ifdef IPPROTO_IPV6 +#ifdef HAVE_IPV6 Cvar_ForceCallback(&sv_port_tcp6); #endif #endif #ifdef HAVE_NATPMP Cvar_ForceCallback(&sv_port_natpmp); #endif +#ifdef UNIXSOCKETS +// Cvar_ForceCallback(&sv_port_unix); +#endif #ifdef HAVE_DTLS Cvar_ForceCallback(&net_enable_dtls); #endif diff --git a/engine/common/netinc.h b/engine/common/netinc.h index 719eab88c..f70410d71 100644 --- a/engine/common/netinc.h +++ b/engine/common/netinc.h @@ -41,13 +41,13 @@ #include #include #else - #ifdef _MSC_VER - #define USEIPX + #if defined(_MSC_VER) && !defined(NOLEGACY) + #define HAVE_IPX #endif #define WIN32_LEAN_AND_MEAN #include #include - #ifdef USEIPX + #ifdef HAVE_IPX #include "wsipx.h" #endif #include @@ -130,6 +130,9 @@ #ifdef sun #include #endif + #ifdef UNIXSOCKETS + #include + #endif #ifdef NeXT #include @@ -143,17 +146,18 @@ #define ioctlsocket ioctl #endif - #if defined(AF_INET6) && !defined(IPPROTO_IPV6) - #define IPPROTO_IPV6 IPPROTO_IPV6 //fte often just checks to see if IPPROTO_IPV6 is defined or not, which doesn't work if its an enum value or somesuch... - #endif - #if defined(AF_INET) #define HAVE_IPV4 #endif - #ifdef IPPROTO_IPV6 + #if defined(AF_INET6) #define HAVE_IPV6 #endif +// #if defined(AF_IPX) && !defined(NOLEGACY) +// #include +// #define HAVE_IPX +// #endif + #define SOCKET int #endif @@ -204,7 +208,9 @@ #endif #if defined(FTE_TARGET_WEB) - #undef IPPROTO_IPV6 + #undef HAVE_IPV4 + #undef HAVE_IPV6 + #undef HAVE_IPX #endif #if 1//def SUPPORT_ICE diff --git a/engine/common/plugin.c b/engine/common/plugin.c index 6175ecd71..e6b82f01a 100644 --- a/engine/common/plugin.c +++ b/engine/common/plugin.c @@ -960,26 +960,13 @@ static qintptr_t VARGS Plug_Net_TCPListen(void *offset, quintptr_t mask, const q if (!currentplug) return -3; //streams depend upon current plugin context. which isn't valid in a thread. if (!localip) - localip = "0.0.0.0"; //pass "[::]" for ipv6 + localip = "tcp://0.0.0.0"; //pass "[::]" for ipv6 if (!NET_StringToAdr(localip, localport, &a)) return -1; - NetadrToSockadr(&a, &address); - - switch(((struct sockaddr*)&address)->sa_family) - { - case AF_INET: - alen = sizeof(struct sockaddr_in); - break; -#ifdef IPPROTO_IPV6 - case AF_INET6: - alen = sizeof(struct sockaddr_in6); - break; -#endif - default: - return -2; - } - + if (a.prot != NP_STREAM && a.prot != NP_DGRAM) + return -1; + alen = NetadrToSockadr(&a, &address); if ((sock = socket(((struct sockaddr*)&address)->sa_family, SOCK_STREAM, 0)) == -1) { @@ -1041,7 +1028,7 @@ static qintptr_t VARGS Plug_Net_Accept(void *offset, quintptr_t mask, const qint { netadr_t a; char *s; - SockadrToNetadr((struct sockaddr_qstorage *)&address, &a); + SockadrToNetadr((struct sockaddr_qstorage *)&address, addrlen, &a); s = NET_AdrToString(adr, sizeof(adr), &a); Q_strncpyz(VM_POINTER(arg[1]), s, addrlen); } diff --git a/engine/common/pr_bgcmd.c b/engine/common/pr_bgcmd.c index 86a547493..8e745c5a5 100644 --- a/engine/common/pr_bgcmd.c +++ b/engine/common/pr_bgcmd.c @@ -8,18 +8,20 @@ #include -#define VMUTF8 0 +#define VMUTF8 utf8_enable.ival #define VMUTF8MARKUP false static char *cvargroup_progs = "Progs variables"; +cvar_t utf8_enable = CVARD("utf8_enable", "0", "When 1, changes the qc builtins to act upon codepoints instead of bytes. Do not use unless com_parseutf8 is also set."); cvar_t sv_gameplayfix_nolinknonsolid = CVARD("sv_gameplayfix_nolinknonsolid", "1", "When 0, setorigin et al will not link the entity into the collision nodes (which is faster, especially if you have a lot of non-solid entities. When 1, allows entities to freely switch between .solid values (except for SOLID_BSP) without relinking. A lot of DP mods assume a value of 1 and will bug out otherwise, while 0 will restore a bugs present in various mods."); cvar_t sv_gameplayfix_blowupfallenzombies = CVARD("sv_gameplayfix_blowupfallenzombies", "0", "Allow findradius to find non-solid entities. This may break certain mods. It is better for mods to use FL_FINDABLE_NONSOLID instead."); cvar_t dpcompat_findradiusarealinks = CVARD("dpcompat_findradiusarealinks", "0", "Use the world collision info to accelerate findradius instead of looping through every single entity. May actually be slower for large radiuses, or fail to find entities which have not been linked properly with setorigin."); #ifndef NOLEGACY cvar_t dpcompat_strcat_limit = CVARD("dpcompat_strcat_limit", "", "When set, cripples strcat (and related function) string lengths to the value specified.\nSet to 16383 to replicate DP's limit, otherwise leave as 0 to avoid limits."); #endif +cvar_t pr_autocreatecvars = CVARD("pr_autocreatecvars", "1", "Implicitly create any cvars that don't exist when read."); cvar_t pr_droptofloorunits = CVARD("pr_droptofloorunits", "256", "Distance that droptofloor is allowed to drop to be considered successul."); cvar_t pr_brokenfloatconvert = CVAR("pr_brokenfloatconvert", "0"); cvar_t pr_fixbrokenqccarrays = CVARFD("pr_fixbrokenqccarrays", "0", CVAR_LATCH, "As part of its nq/qw/h2/csqc support, FTE remaps QC fields to match an internal order. This is a faster way to handle extended fields. However, some QCCs are buggy and don't report all field defs.\n0: do nothing. QCC must be well behaved.\n1: Duplicate engine fields, remap the ones we can to known offsets. This is sufficient for QCCX/FrikQCC mods that use hardcoded or even occasional calculated offsets (fixes ktpro).\n2: Scan the mod for field accessing instructions, and assume those are the fields (and that they don't alias non-fields). This can be used to work around gmqcc's WTFs (fixes xonotic)."); @@ -85,6 +87,8 @@ void PF_Common_RegisterCvars(void) Cvar_Register (&pr_enable_profiling, cvargroup_progs); Cvar_Register (&pr_sourcedir, cvargroup_progs); Cvar_Register (&pr_fixbrokenqccarrays, cvargroup_progs); + Cvar_Register (&utf8_enable, cvargroup_progs); + Cvar_Register (&pr_autocreatecvars, cvargroup_progs); #ifdef RAGDOLL Cmd_AddCommand("skel_info", skel_info_f); @@ -102,6 +106,16 @@ qofs_t PR_ReadBytesString(char *str) { //use doubles, so we can cope with eg "5.3mb" or much larger values double d = strtod(str, &str); + if (d < 0) + { +#if defined(_WIN64) && !defined(WINRT) + return 0x80000000; //use of virtual address space rather than physical memory means we can just go crazy and use the max of 2gb. +#elif defined(FTE_TARGET_WEB) + return 8*1024*1024; +#else + return 32*1024*1024; +#endif + } if (*str == 'g') d *= 1024*1024*1024; if (*str == 'm') @@ -1411,11 +1425,37 @@ void QCBUILTIN PF_FindString (pubprogfuncs_t *prinst, struct globalvars_s *pr_gl //////////////////////////////////////////////////// //Cvars +cvar_t *PF_Cvar_FindOrGet(const char *var_name) +{ + const char *def; + cvar_t *var; + + var = Cvar_FindVar(var_name); + if (!var && pr_autocreatecvars.ival) + { + //this little chunk is so cvars dp creates are created with meaningful values + def = ""; + if (!strcmp(var_name, "sv_maxairspeed")) + def = "30"; + else if (!strcmp(var_name, "sv_jumpvelocity")) + def = "270"; + else + def = ""; + + var = Cvar_Get(var_name, def, 0, "Implicit QC variables"); + if (var) + Con_Printf("^3Created QC Cvar %s\n", var_name); + else + Con_Printf(CON_ERROR"Unable to create QC Cvar %s\n", var_name); + } + return var; +} + //string(string cvarname) cvar_string void QCBUILTIN PF_cvar_string (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { const char *str = PR_GetStringOfs(prinst, OFS_PARM0); - cvar_t *cv = Cvar_Get(str, "", 0, "QC variables"); + cvar_t *cv = PF_Cvar_FindOrGet(str); if (cv && !(cv->flags & CVAR_NOUNSAFEEXPAND)) { if(cv->latched_string) @@ -1436,7 +1476,7 @@ void QCBUILTIN PF_cvars_haveunsaved (pubprogfuncs_t *prinst, struct globalvars_s void QCBUILTIN PF_cvar_defstring (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { const char *str = PR_GetStringOfs(prinst, OFS_PARM0); - cvar_t *cv = Cvar_Get(str, "", 0, "QC variables"); + cvar_t *cv = PF_Cvar_FindOrGet(str); if (cv && !(cv->flags & CVAR_NOUNSAFEEXPAND)) RETURN_CSTRING(cv->defaultstr); else @@ -1447,7 +1487,7 @@ void QCBUILTIN PF_cvar_defstring (pubprogfuncs_t *prinst, struct globalvars_s *p void QCBUILTIN PF_cvar_description (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { const char *str = PR_GetStringOfs(prinst, OFS_PARM0); - cvar_t *cv = Cvar_Get(str, "", 0, "QC variables"); + cvar_t *cv = PF_Cvar_FindOrGet(str); if (cv && !(cv->flags & CVAR_NOUNSAFEEXPAND)) RETURN_CSTRING(cv->description); else @@ -1461,7 +1501,7 @@ void QCBUILTIN PF_cvar_type (pubprogfuncs_t *prinst, struct globalvars_s *pr_glo int ret = 0; cvar_t *v; - v = Cvar_FindVar(str); + v = Cvar_FindVar(str); //this builtin MUST NOT create cvars implicitly, otherwise there would be no way to test if it exists. if (v && !(v->flags & CVAR_NOUNSAFEEXPAND)) { ret |= 1; // CVAR_EXISTS @@ -1486,7 +1526,7 @@ void QCBUILTIN PF_cvar_set (pubprogfuncs_t *prinst, struct globalvars_s *pr_glob var_name = PR_GetStringOfs(prinst, OFS_PARM0); val = PR_GetStringOfs(prinst, OFS_PARM1); - var = Cvar_Get(var_name, val, 0, "QC variables"); + var = PF_Cvar_FindOrGet(var_name); if (!var || (var->flags & CVAR_NOTFROMSERVER)) return; Cvar_Set (var, val); @@ -1500,7 +1540,7 @@ void QCBUILTIN PF_cvar_setlatch (pubprogfuncs_t *prinst, struct globalvars_s *pr var_name = PR_GetStringOfs(prinst, OFS_PARM0); val = PR_GetStringOfs(prinst, OFS_PARM1); - var = Cvar_Get(var_name, val, 0, "QC variables"); + var = PF_Cvar_FindOrGet(var_name); if (!var || (var->flags & CVAR_NOTFROMSERVER)) return; Cvar_LockFromServer(var, val); @@ -1515,7 +1555,7 @@ void QCBUILTIN PF_cvar_setf (pubprogfuncs_t *prinst, struct globalvars_s *pr_glo var_name = PR_GetStringOfs(prinst, OFS_PARM0); val = G_FLOAT(OFS_PARM1); - var = Cvar_FindVar(var_name); + var = PF_Cvar_FindOrGet(var_name); if (!var || (var->flags & CVAR_NOTFROMSERVER)) return; Cvar_SetValue (var, val); @@ -6553,7 +6593,6 @@ lh_extension_t QSG_Extensions[] = { {"DP_INPUTBUTTONS"}, {"DP_LIGHTSTYLE_STATICVALUE"}, {"DP_LITSUPPORT"}, - {"DP_MD3_TAGSINFO", 2, NULL, {"gettagindex", "gettaginfo"}}, {"DP_MONSTERWALK", 0, NULL, {NULL}, "MOVETYPE_WALK is valid on non-player entities. Note that only players receive acceleration etc in line with none/bounce/fly/noclip movetypes on the player, thus you will have to provide your own accelerations (incluing gravity) yourself."}, {"DP_MOVETYPEBOUNCEMISSILE"}, //I added the code for hexen2 support. {"DP_MOVETYPEFOLLOW"}, @@ -6575,6 +6614,7 @@ lh_extension_t QSG_Extensions[] = { {"DP_QC_FS_SEARCH", 4, NULL, {"search_begin", "search_end", "search_getsize", "search_getfilename"}}, {"DP_QC_GETSURFACE", 6, NULL, {"getsurfacenumpoints", "getsurfacepoint", "getsurfacenormal", "getsurfacetexture", "getsurfacenearpoint", "getsurfaceclippedpoint"}}, {"DP_QC_GETSURFACEPOINTATTRIBUTE", 1, NULL, {"getsurfacepointattribute"}}, + {"DP_QC_GETTAGINFO", 2, NULL, {"gettagindex", "gettaginfo"}}, {"DP_QC_MINMAXBOUND", 3, NULL, {"min", "max", "bound"}}, {"DP_QC_MULTIPLETEMPSTRINGS", 0, NULL, {NULL}, "Superseded by DP_QC_UNLIMITEDTEMPSTRINGS. Functions that return a temporary string will not overwrite/destroy previous temporary strings until at least 16 strings are returned (or control returns to the engine)."}, {"DP_QC_RANDOMVEC", 1, NULL, {"randomvec"}}, diff --git a/engine/common/pr_common.h b/engine/common/pr_common.h index 59ffb5fc3..462dfd38a 100644 --- a/engine/common/pr_common.h +++ b/engine/common/pr_common.h @@ -300,7 +300,7 @@ void QCBUILTIN PF_setattachment(pubprogfuncs_t *prinst, struct globalvars_s *pr_ #endif #if defined(SKELETALOBJECTS) || defined(RAGDOLL) - void skel_lookup(world_t *prinst, int skelidx, framestate_t *out); + void skel_lookup(world_t *prinst, int skelidx, framestate_t *fte_restrict out); void skel_dodelete(world_t *world); void skel_reset(world_t *world); void skel_reload(void); @@ -328,6 +328,7 @@ void QCBUILTIN PF_touchtriggers(pubprogfuncs_t *prinst, struct globalvars_s *pr_ //pr_cmds.c builtins that need to be moved to a common. void VARGS PR_BIError(pubprogfuncs_t *progfuncs, char *format, ...) LIKEPRINTF(2); +cvar_t *PF_Cvar_FindOrGet(const char *var_name); void QCBUILTIN PF_cvar_string (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); void QCBUILTIN PF_cvars_haveunsaved (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); void QCBUILTIN PF_cvar_set (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); diff --git a/engine/common/qvm.c b/engine/common/qvm.c index 0bab9a97a..34407ca2a 100644 --- a/engine/common/qvm.c +++ b/engine/common/qvm.c @@ -76,7 +76,7 @@ struct vm_s { qboolean QVM_LoadDLL(vm_t *vm, const char *name, qboolean binroot, void **vmMain, sys_calldll_t syscall) { void (EXPORT_FN *dllEntry)(sys_calldll_t syscall); - char dllname_arch[MAX_OSPATH]; //id compatible + char dllname_arch[MAX_OSPATH]; //id compatiblehttps://slashdot.org/ char dllname_anycpu[MAX_OSPATH];//simple dllhandle_t *hVM; @@ -526,7 +526,7 @@ do{ \ ** ** calls function */ -static void inline QVM_Call(qvm_t *vm, int addr) +inline static void QVM_Call(qvm_t *vm, int addr) { vm->sp--; if (vm->sp < vm->min_sp) Sys_Error("QVM Stack underflow"); @@ -558,7 +558,7 @@ static void inline QVM_Call(qvm_t *vm, int addr) ** [oPC][0][.......]| <- oldBP ** ^BP */ -static void inline QVM_Enter(qvm_t *vm, int size) +inline static void QVM_Enter(qvm_t *vm, int size) { int *fp; @@ -575,8 +575,9 @@ static void inline QVM_Enter(qvm_t *vm, int size) /* ** QVM_Return +** returns failure when returning to the engine. */ -static void inline QVM_Return(qvm_t *vm, int size) +inline static qboolean QVM_Return(qvm_t *vm, int size) { int *fp; @@ -586,16 +587,17 @@ static void inline QVM_Return(qvm_t *vm, int size) if(vm->bp>vm->max_bp) Sys_Error("VM run time error: freed too much stack\n"); - if(fp[1]>=vm->len_cs*2) - if ((size_t)(vm->cs+fp[1]) != (size_t)RETURNOFFSETMARKER) //this being false causes the program to quit. - Sys_Error("VM run time error: program returned to hyperspace (%p, %#x)\n", (char*)vm->cs, fp[1]); - if(fp[1]<0) - if ((size_t)(vm->cs+fp[1]) != (size_t)RETURNOFFSETMARKER) - Sys_Error("VM run time error: program returned to negative hyperspace\n"); - if (vm->sp-vm->max_sp != fp[0]) Sys_Error("VM run time error: stack push/pop mismatch \n"); vm->pc=vm->cs+fp[1]; // restore PC + + if((unsigned int)fp[1]>=(unsigned int)(vm->len_cs*2)) //explicit casts to make sure the C compiler can't make assumptions about overflows. + { + if (fp[1] == -1) + return false; //return to engine. + Sys_Error("VM run time error: program returned to hyperspace (%p, %#x)\n", (char*)vm->cs, fp[1]); + } + return true; } // ------------------------- * execution * ------------------------- @@ -622,7 +624,7 @@ int QVM_ExecVM(register qvm_t *qvm, int command, int arg0, int arg1, int arg2, i oldpc = qvm->pc; // setup execution environment - qvm->pc=RETURNOFFSETMARKER; + qvm->pc=qvm->cs-1; // qvm->cycles=0; // prepare local stack qvm->bp -= 15*4; //we have to do this each call for the sake of (reliable) recursion. @@ -669,11 +671,9 @@ int QVM_ExecVM(register qvm_t *qvm, int command, int arg0, int arg1, int arg2, i QVM_Enter(qvm, param); break; case OP_LEAVE: - QVM_Return(qvm, param); - - if ((size_t)qvm->pc == (size_t)RETURNOFFSETMARKER) + if (!QVM_Return(qvm, param)) { - // pick return value from stack + // pick return value from C stack qvm->pc = oldpc; qvm->bp += 15*4; diff --git a/engine/common/sys_linux_threads.c b/engine/common/sys_linux_threads.c index f05ef4164..7ed7928fb 100644 --- a/engine/common/sys_linux_threads.c +++ b/engine/common/sys_linux_threads.c @@ -58,10 +58,10 @@ typedef struct { int (*func)(void *); void *args; } qthread_t; -static int Sys_CreatedThread(void *v) +static void *Sys_CreatedThread(void *v) { qthread_t *qthread = v; - int r; + qintptr_t r; #ifdef ANDROID JNIEnv* env; @@ -74,7 +74,7 @@ static int Sys_CreatedThread(void *v) (*sys_jvm)->DetachCurrentThread(sys_jvm); #endif - return r; + return (void*)r; } void *Sys_CreateThread(char *name, int (*func)(void *), void *args, int priority, int stacksize) diff --git a/engine/common/world.h b/engine/common/world.h index 8c0ad2783..e1e806b7f 100644 --- a/engine/common/world.h +++ b/engine/common/world.h @@ -198,7 +198,7 @@ typedef struct struct world_s { - void (QDECL *Event_Touch)(struct world_s *w, wedict_t *s, wedict_t *o); + void (QDECL *Event_Touch)(struct world_s *w, wedict_t *s, wedict_t *o, trace_t *trace); void (QDECL *Event_Think)(struct world_s *w, wedict_t *s); void (QDECL *Event_Sound) (float *origin, wedict_t *entity, int channel, const char *sample, int volume, float attenuation, float pitchadj, float timeoffset, unsigned int flags); qboolean (QDECL *Event_ContentsTransition) (struct world_s *w, wedict_t *ent, int oldwatertype, int newwatertype); @@ -370,7 +370,7 @@ void Q23BSP_FindTouchedLeafs(model_t *mod, struct pvscache_s *ent, float *mins, /*sv_move.c*/ #if defined(CSQC_DAT) || !defined(CLIENTONLY) qboolean World_CheckBottom (world_t *world, wedict_t *ent, vec3_t up); -qboolean World_movestep (world_t *world, wedict_t *ent, vec3_t move, vec3_t axis[3], qboolean relink, qboolean noenemy, void (*set_move_trace)(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals, trace_t *trace), struct globalvars_s *set_trace_globs); +qboolean World_movestep (world_t *world, wedict_t *ent, vec3_t move, vec3_t axis[3], qboolean relink, qboolean noenemy, void (*set_move_trace)(pubprogfuncs_t *prinst, trace_t *trace)); qboolean World_MoveToGoal (world_t *world, wedict_t *ent, float dist); qboolean World_GetEntGravityAxis(wedict_t *ent, vec3_t axis[3]); #endif diff --git a/engine/gl/gl_alias.c b/engine/gl/gl_alias.c index 8ac16f8b3..46b02de71 100644 --- a/engine/gl/gl_alias.c +++ b/engine/gl/gl_alias.c @@ -1505,7 +1505,7 @@ qboolean R_CalcModelLighting(entity_t *e, model_t *clmodel) case PTI_RGBA16F: case PTI_RGBA32F: break; - default: + default: //non-hdr lightmap format. clamp model lighting to match the lightmap's clamps. m = max(max(ambientlight[0], ambientlight[1]), ambientlight[2]); if (m > 255) { @@ -2860,7 +2860,6 @@ void BE_GenModelBatches(batch_t **batches, const dlight_t *dl, unsigned int bemo R_HalfLife_GenerateBatches(ent, batches); #endif break; - // warning: enumeration value mod_* not handled in switch case mod_dummy: case mod_heightmap: #if defined(TERRAIN) diff --git a/engine/gl/gl_font.c b/engine/gl/gl_font.c index 4967e0187..97f3ba95c 100644 --- a/engine/gl/gl_font.c +++ b/engine/gl/gl_font.c @@ -275,13 +275,16 @@ typedef struct font_s unsigned char advance; //how wide this char is, when drawn unsigned short block; //to quickly find the char again - //glyph offset+sizes. I guess these are not strictly needed, but whatever + //texture offsets. unsigned short bmx; unsigned short bmy; + + //texture/screen pixel sizes. unsigned char bmw; unsigned char bmh; + //unsigned short pad; - //positions inside the atlas + //screen offsets. short top; short left; } *chars[FONTBLOCKS]; diff --git a/engine/gl/gl_heightmap.c b/engine/gl/gl_heightmap.c index 894a28d53..32c1c88f9 100644 --- a/engine/gl/gl_heightmap.c +++ b/engine/gl/gl_heightmap.c @@ -7911,6 +7911,61 @@ void *Mod_LoadTerrainInfo(model_t *mod, char *loadname, qboolean force) } #ifndef SERVERONLY +struct ted_import_s +{ + int x, y; + int width; + int height; + unsigned short *data; +}; +//static void ted_itterate(heightmap_t *hm, int distribution, float *pos, float radius, float strength, int steps, +void ted_import_heights(void *vctx, hmsection_t *s, int idx, float wx, float wy, float strength) +{ + struct ted_import_s *ctx = vctx; + unsigned int y = idx/SECTHEIGHTSIZE; + unsigned int x = idx%SECTHEIGHTSIZE; + x += s->sx*(SECTHEIGHTSIZE-1) - ctx->x; + y += s->sy*(SECTHEIGHTSIZE-1) - ctx->y; + if (x < 0 || x >= ctx->width || y < 0 || y >= ctx->height) + return; + s->flags |= TSF_NOTIFY|TSF_EDITED|TSF_DIRTY|TSF_RELIGHT; + s->heights[idx] = ctx->data[x + y*ctx->width] * (8192.0/(1<<16)); +} +void Mod_Terrain_Import_f(void) +{ + model_t *mod; + struct ted_import_s ctx; + const char *mapname = Cmd_Argv(1); + size_t fsize; + heightmap_t *hm; + vec3_t pos = {0}; + if (Cmd_IsInsecure()) + { + Con_Printf("Please use this command via the console\n"); + return; + } + if (*mapname) + mod = NULL;//Mod_FindName(va("maps/%s", mapname)); + else + mod = cl.worldmodel; + if (!mod || mod->type == mod_dummy) + return; + hm = mod->terrain; + if (!hm) + return; + + fsize = 0; + ctx.data = (void*)FS_LoadMallocFile("quake8km/height8km.r16", &fsize); + ctx.width = ctx.height = sqrt(fsize/2); + ctx.x = 0; + ctx.y = 0; + pos[0] += hm->sectionsize * CHUNKBIAS; + pos[1] += hm->sectionsize * CHUNKBIAS; + if (fsize == ctx.width*ctx.height*2) + ted_itterate(hm, tid_flat, pos, max(ctx.width, ctx.height), 1, SECTHEIGHTSIZE, ted_import_heights, &ctx); + FS_FreeFile(ctx.data); +} + void Mod_Terrain_Create_f(void) { int x,y; @@ -7932,7 +7987,6 @@ void Mod_Terrain_Create_f(void) Con_Printf("%s: NAME \"DESCRIPTION\" SKYNAME DEFAULTGROUNDTEX DEFAULTHEIGHT DEFAULTWATER DEFAULTWATERHEIGHT seed\nGenerates a fresh maps/foo.hmp file. You may wish to edit it with notepad later to customise it. You will need csaddon.dat in order to edit the actual terrain.\n", Cmd_Argv(0)); return; } - mname = va("maps/%s.hmp", Cmd_Argv(1)); mapdesc = Cmd_Argv(2); if (!*mapdesc) mapdesc = Cmd_Argv(1); skyname = Cmd_Argv(3); @@ -7999,6 +8053,7 @@ void Mod_Terrain_Create_f(void) } } + mname = va("maps/%s.hmp", Cmd_Argv(1)); if (COM_FCheckExists(mname)) { Con_Printf("%s: already exists, not overwriting.\n", mname); @@ -8154,6 +8209,7 @@ void Terr_Init(void) Cmd_AddCommand("mod_terrain_save", Mod_Terrain_Save_f); Cmd_AddCommand("mod_terrain_reload", Mod_Terrain_Reload_f); #ifndef SERVERONLY + Cmd_AddCommandD("mod_terrain_import", Mod_Terrain_Import_f, "Import a raw heightmap"); Cmd_AddCommand("mod_terrain_create", Mod_Terrain_Create_f); Cmd_AddCommandD("mod_terrain_convert", Mod_Terrain_Convert_f, "mod_terrain_convert [mapname] [texkill]\nConvert a terrain to the current format. If texkill is specified, only tiles with the named texture will be converted, and tiles with that texture will be stripped. This is a slow operation."); #endif diff --git a/engine/gl/gl_model.c b/engine/gl/gl_model.c index a64ad8c24..59b2d3164 100644 --- a/engine/gl/gl_model.c +++ b/engine/gl/gl_model.c @@ -825,11 +825,11 @@ void Mod_Init (qboolean initial) #ifdef Q1BSPS //q1-based formats - Mod_RegisterModelFormatMagic(NULL, "Quake1 2PSB Map(bsp)", BSPVERSION_LONG1, Mod_LoadBrushModel); - Mod_RegisterModelFormatMagic(NULL, "Quake1 BSP2 Map(bsp)", BSPVERSION_LONG2, Mod_LoadBrushModel); - Mod_RegisterModelFormatMagic(NULL, "Half-Life Map (bsp)", 30, Mod_LoadBrushModel); - Mod_RegisterModelFormatMagic(NULL, "Quake1 Map (bsp)", 29, Mod_LoadBrushModel); - Mod_RegisterModelFormatMagic(NULL, "Quake1 Prerelease Map (bsp)", 28, Mod_LoadBrushModel); + Mod_RegisterModelFormatMagic(NULL, "Quake1 2PSB Map (bsp)", BSPVERSION_LONG1, Mod_LoadBrushModel); + Mod_RegisterModelFormatMagic(NULL, "Quake1 BSP2 Map (bsp)", BSPVERSION_LONG2, Mod_LoadBrushModel); + Mod_RegisterModelFormatMagic(NULL, "Half-Life Map (bsp)", BSPVERSIONHL, Mod_LoadBrushModel); + Mod_RegisterModelFormatMagic(NULL, "Quake1 Map (bsp)", BSPVERSION, Mod_LoadBrushModel); + Mod_RegisterModelFormatMagic(NULL, "Quake1 Prerelease Map (bsp)", BSPVERSIONPREREL, Mod_LoadBrushModel); #endif } } diff --git a/engine/gl/gl_rmisc.c b/engine/gl/gl_rmisc.c index 506cad942..4f67d4513 100644 --- a/engine/gl/gl_rmisc.c +++ b/engine/gl/gl_rmisc.c @@ -428,15 +428,6 @@ void R_MakeTexWad_f(void) } #endif void GLR_TimeRefresh_f (void); - -extern cvar_t v_contrast, r_drawflat; -extern cvar_t r_stains, r_stainfadetime, r_stainfadeammount; - -// callback defines -extern cvar_t gl_font; -extern cvar_t vid_conautoscale, vid_conheight, vid_conwidth; -extern cvar_t crosshair, crosshairimage, crosshaircolor, r_skyboxname; -extern cvar_t r_fastskycolour; void GLV_Gamma_Callback(struct cvar_s *var, char *oldvalue); void GLR_DeInit (void) diff --git a/engine/gl/gl_shader.c b/engine/gl/gl_shader.c index 8b6af8f70..5a71e4904 100644 --- a/engine/gl/gl_shader.c +++ b/engine/gl/gl_shader.c @@ -5535,6 +5535,15 @@ void QDECL R_BuildDefaultTexnums(texnums_t *src, shader_t *shader) tex->fullbright = R_LoadHiResTexture(va("%s_luma:%s_glow", imagename, imagename), subpath, imageflags); } } + + //if there's a reflectcube texture specified by the shader, make sure we have a reflectmask to go with it. + if (tex->reflectcube) + { + if (!TEXVALID(tex->reflectmask) && *mapname) + tex->reflectmask = R_LoadHiResTexture(va("%s_reflect", mapname), NULL, imageflags); + if (!TEXVALID(tex->reflectmask)) + tex->reflectmask = R_LoadHiResTexture(va("%s_reflect", imagename), subpath, imageflags); + } } } @@ -5792,7 +5801,6 @@ void QDECL R_BuildLegacyTexnums(shader_t *shader, const char *fallbackname, cons if (tex->reflectcube) { - extern cvar_t r_shadow_bumpscale_basetexture; if (!TEXVALID(tex->reflectmask) && *mapname) tex->reflectmask = R_LoadHiResTexture(va("%s_reflect", mapname), NULL, imageflags); if (!TEXVALID(tex->reflectmask)) diff --git a/engine/gl/gl_shadow.c b/engine/gl/gl_shadow.c index 75dab73dc..a32b882b5 100644 --- a/engine/gl/gl_shadow.c +++ b/engine/gl/gl_shadow.c @@ -3103,10 +3103,14 @@ static void Sh_DrawStencilLightShadows(dlight_t *dl, qbyte *lvis, qbyte *vvis, q switch (emodel->type) { case mod_alias: + if (r_drawentities.ival == 3) + continue; R_DrawGAliasShadowVolume (ent, dl->origin, dl->radius); break; case mod_brush: + if (r_drawentities.ival == 2) + continue; Sh_DrawBrushModelShadow (dl, ent); break; diff --git a/engine/gl/gl_vidcommon.c b/engine/gl/gl_vidcommon.c index 5cdf2e57d..ccaae6319 100644 --- a/engine/gl/gl_vidcommon.c +++ b/engine/gl/gl_vidcommon.c @@ -513,7 +513,7 @@ void GL_CheckExtensions (void *(*getglfunction) (char *name)) { int i; qglGetIntegerv(GL_NUM_EXTENSIONS, &gl_num_extensions); - if (developer.value) + if (developer.value>1) { Con_Printf ("GL_EXTENSIONS:\n"); for (i = 0; i < gl_num_extensions; i++) @@ -2262,7 +2262,7 @@ static GLhandleARB GLSlang_CreateShader (program_t *prog, const char *name, int GLcharARB *combined; int totallen = 1; for (i = 0; i < glsl.strings; i++) - totallen += glsl.len[i] + 64; + totallen += glsl.len[i] + 64 + (glsl.file[i]?strlen(glsl.file[i]):0); combined = malloc(totallen); totallen = 0; combined[totallen] = 0; @@ -2272,12 +2272,18 @@ static GLhandleARB GLSlang_CreateShader (program_t *prog, const char *name, int ; //#version MUST be the first line, don't prefix it with a #line, it'll just break things. else if (!totallen || combined[totallen-1] == '\n') { //last line was a newline, hurrah. safe to insert without breaking anything - Q_snprintfz(combined+totallen, 64, "#line %i %i //%s\n", glsl.line[i], i, glsl.file[i]); + if (glsl.file[i]) + Q_snprintfz(combined+totallen, 64+strlen(glsl.file[i]), "#line %i %i //%s\n", glsl.line[i], i, glsl.file[i]); + else + Q_snprintfz(combined+totallen, 64, "#line %i %i\n", glsl.line[i], i); totallen += strlen(combined+totallen); } else if (glsl.len[i] && *glsl.str[i] == '\n') { //last line didn't end with a newline, but there is one after. that's okay too, but we need to play it safe. - Q_snprintfz(combined+totallen, 64, "\n#line %i %i //%s\n", glsl.line[i], i, glsl.file[i]); + if (glsl.file[i]) + Q_snprintfz(combined+totallen, 64+strlen(glsl.file[i]), "\n#line %i %i //%s\n", glsl.line[i], i, glsl.file[i]); + else + Q_snprintfz(combined+totallen, 64, "\n#line %i %i\n", glsl.line[i], i); totallen += strlen(combined+totallen); } //now shove stuff there. @@ -3367,8 +3373,6 @@ qboolean GL_Init(rendererstate_t *info, void *(*getglfunction) (char *name)) sh_config.env_add = gl_config.env_add; } - GL_SetupFormats(); - return true; } diff --git a/engine/gl/gl_vidlinuxglx.c b/engine/gl/gl_vidlinuxglx.c index 1c54fd61b..216c9819a 100644 --- a/engine/gl/gl_vidlinuxglx.c +++ b/engine/gl/gl_vidlinuxglx.c @@ -50,6 +50,7 @@ none of these issues will be fixed by a compositing window manager, because ther #include #include #include +#include #include @@ -1411,7 +1412,7 @@ GLXFBConfig GLX_GetFBConfig(rendererstate_t *info) int numconfigs; GLXFBConfig *fbconfigs; - qboolean hassrgb, hasmultisample, hasfloats; + qboolean hassrgb, hasmultisample;//, hasfloats; if (glx.QueryExtensionsString) glx.glxextensions = glx.QueryExtensionsString(vid_dpy, scrnum); @@ -1427,9 +1428,9 @@ GLXFBConfig GLX_GetFBConfig(rendererstate_t *info) return NULL; //don't worry about it } - hassrgb = GLX_CheckExtension("GLX_ARB_framebuffer_sRGB"); + hassrgb = GLX_CheckExtension("GLX_ARB_framebuffer_sRGB") || GLX_CheckExtension("GLX_EXT_framebuffer_sRGB"); hasmultisample = GLX_CheckExtension("GLX_ARB_multisample"); - hasfloats = GLX_CheckExtension("GLX_ARB_fbconfig_float"); +// hasfloats = GLX_CheckExtension("GLX_ARB_fbconfig_float"); //do it in a loop, mostly to disable extensions that are unlikely to be supported on various glx implementations. for (i = 0; i < (16<<1); i++) @@ -1452,28 +1453,31 @@ GLXFBConfig GLX_GetFBConfig(rendererstate_t *info) //attrib[n++] = GLX_AUX_BUFFERS; attrib[n++] = 0; +#if 0 if (!(i&4)) - { - if (info->srgb <= 2 || !hasfloats) + { //unlike on windows, this is explicitly blocked except for pbuffers. ffs. + if (info->srgb < 2 || !hasfloats) continue; //skip fp16 framebuffers + //unlike on windows, this is explicitly blocked except for pbuffers. ffs. attrib[n++] = GLX_RENDER_TYPE; attrib[n++] = GLX_RGBA_FLOAT_BIT; attrib[n++] = GLX_RED_SIZE; attrib[n++] = info->bpp?info->bpp/3:4; attrib[n++] = GLX_GREEN_SIZE; attrib[n++] = info->bpp?info->bpp/3:4; attrib[n++] = GLX_BLUE_SIZE; attrib[n++] = info->bpp?info->bpp/3:4; } else +#endif { - if (info->bpp > 24) - { + if (info->bpp == 32) + { //bpp32 is an alias for 24 (we ignore the alpha channel) attrib[n++] = GLX_RED_SIZE; attrib[n++] = 8; attrib[n++] = GLX_GREEN_SIZE; attrib[n++] = 8; attrib[n++] = GLX_BLUE_SIZE; attrib[n++] = 8; } else - { - attrib[n++] = GLX_RED_SIZE; attrib[n++] = info->bpp?info->bpp/3:4; - attrib[n++] = GLX_GREEN_SIZE; attrib[n++] = info->bpp?info->bpp/3:4; - attrib[n++] = GLX_BLUE_SIZE; attrib[n++] = info->bpp?info->bpp/3:4; + { //clamp requested bitdepth to 8bits on the second pass, so that bpp30 doesn't fail + attrib[n++] = GLX_RED_SIZE; attrib[n++] = bound(1,info->bpp?info->bpp/3:4, (i&4)?8:16); + attrib[n++] = GLX_GREEN_SIZE; attrib[n++] = bound(1,info->bpp?info->bpp/3:4, (i&4)?8:16); + attrib[n++] = GLX_BLUE_SIZE; attrib[n++] = bound(1,info->bpp?info->bpp/3:4, (i&4)?8:16); } } //attrib[n++] = GLX_ALPHA_SIZE; attrib[n++] = GLX_DONT_CARE; @@ -1642,9 +1646,9 @@ qboolean GLX_Init(rendererstate_t *info, GLXFBConfig fbconfig, XVisualInfo *visi if (glx.GetFBConfigAttrib) { - if (glx.GetFBConfigAttrib(vid_dpy, fbconfig, GLX_RENDER_TYPE, &val) && val == GLX_RGBA_FLOAT_BIT) + if (!glx.GetFBConfigAttrib(vid_dpy, fbconfig, GLX_RENDER_TYPE, &val) && val == GLX_RGBA_FLOAT_BIT) vid.flags |= VID_FP16; //other things need to be 16bit too, to avoid loss of precision. - if (glx.GetFBConfigAttrib(vid_dpy, fbconfig, GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB, &val) && val) + if (!glx.GetFBConfigAttrib(vid_dpy, fbconfig, GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB, &val) && val) vid.flags |= VID_SRGB_CAPABLE; //can use srgb properly, without faking it etc. } @@ -1958,7 +1962,7 @@ static void install_grabs(void) { if (!mouse_grabbed) { - Con_DPrintf("Grabbing mouse\n"); + Con_DLPrintf(2, "Grabbing mouse\n"); mouse_grabbed = true; //XGrabPointer can cause alt+tab type shortcuts to be skipped by the window manager. This means we don't want to use it unless we have no choice. //the grab is purely to constrain the pointer to the window @@ -1990,7 +1994,7 @@ static void uninstall_grabs(void) { if (mouse_grabbed && vid_dpy) { - Con_DPrintf("Releasing mouse grab\n"); + Con_DLPrintf(2, "Releasing mouse grab\n"); mouse_grabbed = false; if (x11_input_method == XIM_DGA) { @@ -2358,7 +2362,12 @@ static void GetEvent(void) char *protname = x11.pXGetAtomName(vid_dpy, event.xclient.data.l[0]); if (!strcmp(protname, "WM_DELETE_WINDOW")) { - Cmd_ExecuteString("menu_quit prompt", RESTRICT_LOCAL); + if (Cmd_Exists("menu_quit") || Cmd_AliasExist("menu_quit", RESTRICT_LOCAL)) + Cmd_ExecuteString("menu_quit prompt", RESTRICT_LOCAL); + else if (Cmd_Exists("m_quit") || Cmd_AliasExist("m_quit", RESTRICT_LOCAL)) + Cmd_ExecuteString("m_quit", RESTRICT_LOCAL); + else + Cmd_ExecuteString("quit", RESTRICT_LOCAL); x11.pXSetInputFocus(vid_dpy, vid_window, RevertToParent, CurrentTime); } else @@ -2369,14 +2378,24 @@ static void GetEvent(void) else if (!strcmp(name, "XdndEnter") && event.xclient.format == 32) { //check for text/uri-list + x11.dnd.type = None; int i; for (i = 2; i < 2+3; i++) { if (event.xclient.data.l[i]) { char *t = x11.pXGetAtomName(vid_dpy, event.xclient.data.l[i]); +#ifdef XDS + //direct-save has no way to deal with multiple files, other than lying about it. + //which is unfortunately what other programs do. we have no real way to tell which file(s) actually got dragged/written. + //we would need to report an empty directory, then scan for new files, then wipe the lot recursively. + if (!strcmp(t, "XdndDirectSave0") && !x11.dnd.type) //single file + x11.dnd.type = event.xclient.data.l[i]; +#endif if (!strcmp(t, "text/uri-list")) //file list x11.dnd.type = event.xclient.data.l[i]; +// else if (!strcmp(t, "application/octet-stream")) //raw file data without a name. +// x11.dnd.type = event.xclient.data.l[i]; x11.pXFree(t); } } @@ -2391,7 +2410,7 @@ static void GetEvent(void) xev.xclient.message_type = x11.pXInternAtom(vid_dpy, "XdndStatus", False); xev.xclient.format = 32; xev.xclient.data.l[0] = vid_window; //so source can ignore it if stale - xev.xclient.data.l[1] = 1; + xev.xclient.data.l[1] = x11.dnd.type?1:0; xev.xclient.data.l[2] = 0; //(x<<16)|y (should be in root coords) xev.xclient.data.l[3] = 0; //(w<<16)|h xev.xclient.data.l[4] = x11.pXInternAtom (vid_dpy, "XdndActionCopy", False); @@ -2408,9 +2427,35 @@ static void GetEvent(void) else if (!strcmp(name, "XdndDrop") && event.xclient.format == 32) { Atom xa_XdndSelection = x11.pXInternAtom(vid_dpy, "XdndSelection", False); + Window source = event.xclient.data.l[0]; Time t = CurrentTime;//event.xclient.data.l[2]; + +#ifdef XDS + char *droptype = x11.dnd.type?x11.pXGetAtomName(vid_dpy, x11.dnd.type):NULL; + if (droptype && !strcmp(droptype, "XdndDirectSave0")) //single file + { + unsigned char *data = NULL; + Atom type; + int fmt; + unsigned long nitems; + unsigned long bytesleft; + if (x11.pXGetWindowProperty(vid_dpy, source, x11.dnd.type, 0, 65536, False, AnyPropertyType, &type, &fmt, &nitems, &bytesleft, &data) == Success && data) + { + char hostname[1024]; + if (gethostname(hostname, sizeof(hostname)) < 0) + *hostname = 0; //failed? o.O + char *path = va("file://%s/tmp/%s", hostname, data); + Atom proptype = x11.pXInternAtom(vid_dpy, "text/plain", false); + x11.pXChangeProperty(vid_dpy, source, x11.dnd.type, proptype, 8, PropModeReplace, (void*)path, strlen(path)); + Con_Printf("Dropping file %s\n", data); + x11.pXFree(data); + } + } + x11.pXFree(droptype); +#endif + x11.dnd.myprop = x11.pXInternAtom(vid_dpy, "_FTE_dnd", False); - if (x11.pXGetSelectionOwner(vid_dpy, xa_XdndSelection) == event.xclient.data.l[0]) + if (x11.pXGetSelectionOwner(vid_dpy, xa_XdndSelection) == source) { x11.pXDeleteProperty(vid_dpy, vid_window, x11.dnd.myprop); x11.pXConvertSelection(vid_dpy, xa_XdndSelection, x11.dnd.type, x11.dnd.myprop, vid_window, t); @@ -2435,7 +2480,8 @@ static void GetEvent(void) unsigned long bytesleft; if (x11.pXGetWindowProperty(vid_dpy, vid_window, x11.dnd.myprop, 0, 65536, False, AnyPropertyType, &type, &fmt, &nitems, &bytesleft, &data) == Success && data) { - if (type == x11.dnd.type) + char *tname = x11.pXGetAtomName(vid_dpy, x11.dnd.type); + if (type == x11.dnd.type && !strcmp(tname, "text/uri-list")) { char *start, *end; for (start = data; *start; ) @@ -2449,8 +2495,20 @@ static void GetEvent(void) start++; } okay = true; - x11.pXFree(data); } +#ifdef XDS + else if (type == x11.dnd.type && !strcmp(tname, "XdndDirectSave0") && nitems == 1) + { + switch(data[0]) + { + case 'S': //sender wrote the file + case 'E': //sender failed to generate the data or something + case 'F': //sender failed to write the file. we should use application/octet-stream and write it ourself. + } + } +#endif + x11.pXFree(tname); + x11.pXFree(data); } x11.pXDeleteProperty(vid_dpy, vid_window, x11.dnd.myprop); //might be large, so don't force it to hang around. diff --git a/engine/http/ftpserver.c b/engine/http/ftpserver.c index 8fd498369..de4e361fc 100644 --- a/engine/http/ftpserver.c +++ b/engine/http/ftpserver.c @@ -1478,7 +1478,7 @@ unsigned long _true = true; closesocket(clientsock); //try to forget this ever happend return true; } - NET_SockadrToString(cl->peername, sizeof(cl->peername), &from); + NET_SockadrToString(cl->peername, sizeof(cl->peername), &from, fromlen); IWebPrintf("%s: New FTP connection\n", cl->peername); //RFC1700 if (((struct sockaddr *)&from)->sa_family == AF_INET) diff --git a/engine/http/httpclient.c b/engine/http/httpclient.c index ef76649b2..8f7d849ef 100644 --- a/engine/http/httpclient.c +++ b/engine/http/httpclient.c @@ -38,8 +38,8 @@ static void DL_OnLoad(void *c, int buf) { if (*dl->localname) { - FS_CreatePath(dl->localname, FS_GAMEONLY); - dl->file = FS_OpenVFS(dl->localname, "w+b", FS_GAMEONLY); + FS_CreatePath(dl->localname, dl->fsroot); + dl->file = FS_OpenVFS(dl->localname, "w+b", dl->fsroot); } else { @@ -199,8 +199,8 @@ static void readfinished(void* user_data, int32_t result) { if (*f->localname) { - FS_CreatePath(f->localname, FS_GAME); - f->file = FS_OpenVFS(f->localname, "w+b", FS_GAME); + FS_CreatePath(f->localname, dl->fsroot); + f->file = FS_OpenVFS(f->localname, "w+b", dl->fsroot); } else f->file = FS_OpenTemp(); @@ -797,8 +797,8 @@ static qboolean HTTP_DL_Work(struct dl_download *dl) #ifndef NPFTE if (*dl->localname) { - FS_CreatePath(dl->localname, FS_GAME); - dl->file = FS_OpenVFS(dl->localname, "w+b", FS_GAME); + FS_CreatePath(dl->localname, dl->fsroot); + dl->file = FS_OpenVFS(dl->localname, "w+b", dl->fsroot); } else dl->file = FS_OpenTemp(); @@ -1332,8 +1332,8 @@ qboolean DataScheme_Decode(struct dl_download *dl) #ifndef NPFTE if (*dl->localname) { - FS_CreatePath(dl->localname, FS_GAME); - dl->file = FS_OpenVFS(dl->localname, "w+b", FS_GAME); + FS_CreatePath(dl->localname, dl->fsroot); + dl->file = FS_OpenVFS(dl->localname, "w+b", dl->fsroot); } else dl->file = FS_OpenTemp(); @@ -1485,6 +1485,7 @@ struct dl_download *DL_Create(const char *url) newdl->url = (char*)(newdl+1); strcpy(newdl->url, url); newdl->poll = DL_Decide; + newdl->fsroot = FS_GAMEONLY; newdl->sizelimit = 0x80000000u; //some sanity limit. #if !defined(NPFTE) && !defined(SERVERONLY) newdl->qdownload.method = DL_HTTP; diff --git a/engine/http/iweb.h b/engine/http/iweb.h index 952a4c6cf..8a78415c6 100644 --- a/engine/http/iweb.h +++ b/engine/http/iweb.h @@ -99,6 +99,7 @@ struct dl_download char *url; /*original url*/ char redir[MAX_OSPATH]; /*current redirected url*/ char localname[MAX_OSPATH]; /*leave empty for a temp file*/ + enum fs_relative fsroot; struct vfsfile_s *file; /*downloaded to, if not already set when starting will open localname or a temp file*/ char postmimetype[64]; diff --git a/engine/qclib/execloop.h b/engine/qclib/execloop.h index 5bd899429..814dbb39c 100644 --- a/engine/qclib/execloop.h +++ b/engine/qclib/execloop.h @@ -777,12 +777,22 @@ reeval: break; case OP_CP_ITOF: - ptr = (eval_t *)(((qbyte *)sv_edicts) + OPA->_int); + i = OPA->_int; + errorif (QCPOINTERREADFAIL(i, sizeof(char))) + { + QCFAULT(&progfuncs->funcs, "bad pointer read in %s (%#x)", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name), OPA->_int); + } + ptr = QCPOINTERM(i); OPC->_float = (float)ptr->_int; break; case OP_CP_FTOI: - ptr = (eval_t *)(((qbyte *)sv_edicts) + OPA->_int); + i = OPA->_int; + errorif (QCPOINTERREADFAIL(i, sizeof(char))) + { + QCFAULT(&progfuncs->funcs, "bad pointer read in %s (%#x)", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name), OPA->_int); + } + ptr = QCPOINTERM(i); OPC->_int = (int)ptr->_float; break; diff --git a/engine/qclib/qcc_cmdlib.c b/engine/qclib/qcc_cmdlib.c index 3ee9f7053..2eeffce66 100644 --- a/engine/qclib/qcc_cmdlib.c +++ b/engine/qclib/qcc_cmdlib.c @@ -958,7 +958,7 @@ unsigned int utf8_check(const void *in, unsigned int *value) if ((str[1] & 0xc0) == 0x80) { *value = uc = ((str[0] & 0x1f)<<6) | (str[1] & 0x3f); - if (!uc || uc >= (1u<<7)) //allow modified utf-8 + if (!uc || uc >= (1u<<7)) //allow modified utf-8 (only for nulls) return 2; } } diff --git a/engine/qclib/qcc_pr_comp.c b/engine/qclib/qcc_pr_comp.c index c22669f0f..5fd9b9e6a 100644 --- a/engine/qclib/qcc_pr_comp.c +++ b/engine/qclib/qcc_pr_comp.c @@ -9571,6 +9571,23 @@ QCC_opcode_t *QCC_PR_ChooseOpcode(QCC_sref_t lhs, QCC_sref_t rhs, QCC_opcode_t * return op; } +//used to optimise logicops slightly. +pbool QCC_OpHasSideEffects(QCC_statement_t *st) +{ + //function calls potentially always have side effects (and are expensive) + if ((st->op >= OP_CALL0 && st->op <= OP_CALL8) || (st->op >= OP_CALL1H && st->op <= OP_CALL8H)) + return true; + //otherwise if we're assigning to some variable (that isn't a temp) then it has a side effect. + //FIXME: this doesn't catch op_address+op_storep_*, but that should generally not happen as logicops is tied to a single statement. + if (st->c.sym && !st->c.sym->temp && OpAssignsToC(st->op)) + return true; + if (st->b.sym && !st->b.sym->temp && OpAssignsToB(st->op)) + return true; + if (st->a.sym && !st->a.sym->temp && OpAssignsToA(st->op)) + return true; + return false; +} + QCC_ref_t *QCC_PR_RefExpression (QCC_ref_t *retbuf, int priority, int exprflags) { QCC_ref_t rhsbuf; @@ -9996,7 +10013,7 @@ QCC_ref_t *QCC_PR_RefExpression (QCC_ref_t *retbuf, int priority, int exprflags) logicjump->b.ofs = &statements[numstatements] - logicjump; if (logicjump->b.ofs == 1) numstatements--; //err, that was pointless. - else if (logicjump->b.ofs == 2 && !(logicjump[1].op >= OP_CALL0 && logicjump[1].op <= OP_CALL8) && !(logicjump[1].op >= OP_CALL1H && logicjump[1].op <= OP_CALL8H)) + else if (logicjump->b.ofs == 2 && !QCC_OpHasSideEffects(&logicjump[1])) { logicjump[0] = logicjump[1]; numstatements--; //don't bother if the jump is the same cost as the thing we're trying to skip (calls are expensive despite being a single opcode). diff --git a/engine/qclib/qcc_pr_lex.c b/engine/qclib/qcc_pr_lex.c index 2e48d61b3..a2c934f88 100644 --- a/engine/qclib/qcc_pr_lex.c +++ b/engine/qclib/qcc_pr_lex.c @@ -1571,12 +1571,16 @@ void QCC_PR_LexString (void) #else void QCC_PR_LexString (void) { - int c; + unsigned int c, t; + int bytecount; int len = 0; char *end, *cnst; int raw; char rawdelim[64]; unsigned int code; + int stringtype = 0; //0 - quake output, input is 8bit. warnings when its not ascii. \u will still give utf-8 text, other chars as-is. Expect \s to screw everything up with utf-8 output. + //1 - quake output, input is utf-8. due to editors not supporting it, that generally means the input (ab)uses markup. + //2 - utf-8 output, input is utf-8. welcome to the future! unfortunately not the present. int texttype; pbool first = true; @@ -1630,6 +1634,22 @@ void QCC_PR_LexString (void) pr_source_line++; } } + else if (*pr_file_p == 'Q' && pr_file_p[1] == '\"') + { //quake output with utf-8 input (expect to need markup). + stringtype = 1; + pr_file_p+=2; + } + else if ((*pr_file_p == 'U' || *pr_file_p == 'u' || *pr_file_p == 'L') && pr_file_p[1] == '\"') + { //unicode string, char32_t, char16_t, wchar_t respectively. we spit out utf-8 regardless. + QCC_PR_ParseWarning(WARN_NOTUTF8, "interpretting char32_t/char16_t/wchar_t as utf-8"); + stringtype = 2; + pr_file_p+=2; + } + else if (*pr_file_p == 'u' && pr_file_p[1] == '8' && pr_file_p[2] == '\"') + { //utf-8 string. + stringtype = 2; + pr_file_p+=3; + } else if (*pr_file_p == '\"') pr_file_p++; else if (first) @@ -1687,11 +1707,12 @@ void QCC_PR_LexString (void) break; } - //make sure line numbers are correct + //make sure line numbers are correct though. if (c == '\r' && *pr_file_p != '\n') pr_source_line++; //mac if (c == '\n') //dos/unix pr_source_line++; + goto forcebyte; } else { @@ -1726,9 +1747,9 @@ void QCC_PR_LexString (void) //else if (c == 'b') // c = '\b'; else if (c == '[') - c = 16; //quake specific + c = 0xe010; //quake specific else if (c == ']') - c = 17; //quake specific + c = 0xe011; //quake specific else if (c == '{') { int d; @@ -1741,71 +1762,44 @@ void QCC_PR_LexString (void) } } else if (c == '.') - c = 0x1c | texttype; + c = 0xe01c | texttype; else if (c == '<') - c = 29; + c = 0xe01d; //separator start else if (c == '-') - c = 30; + c = 0xe01e; //separator middle else if (c == '>') - c = 31; + c = 0xe01f; //separator end else if (c == '(') - c = 128; + c = 0xe080; //slider start else if (c == '=') - c = 129; + c = 0xe081; //slider middle else if (c == ')') - c = 130; + c = 0xe082; //slider end + else if (c == '+') + c = 0xe083; //slider box else if (c == 'u' || c == 'U') { //lower case u specifies exactly 4 nibbles. - //upper case U specifies variable length. terminate with a double-doublequote pair, or some other non-hex char. - int count = 0; - unsigned long d; - unsigned long unicode; - unicode = 0; - for(;;) + //upper case U specifies exactly 8 nibbles. + unsigned int nibbles = (c=='u')?4:8; + c = 0; + while (nibbles --> 0) { - d = (unsigned char)*pr_file_p; - if (d >= '0' && d <= '9') - unicode = (unicode*16) + (d - '0'); - else if (d >= 'A' && d <= 'F') - unicode = (unicode*16) + (d - 'A') + 10; - else if (d >= 'a' && d <= 'f') - unicode = (unicode*16) + (d - 'a') + 10; + t = (unsigned char)*pr_file_p; + if (t >= '0' && t <= '9') + c = (c*16) + (t - '0'); + else if (t >= 'A' && t <= 'F') + c = (c*16) + (t - 'A') + 10; + else if (t >= 'a' && t <= 'f') + c = (c*16) + (t - 'a') + 10; else break; - count++; pr_file_p++; } - if (!count || ((c=='u')?(count!=4):(count>8)) || unicode > 0x10FFFFu) //RFC 3629 imposes the same limit as UTF-16 surrogate pairs. - QCC_PR_ParseWarning(ERR_BADCHARACTERCODE, "Bad unicode character code"); + if (nibbles) + QCC_PR_ParseWarning(ERR_BADCHARACTERCODE, "Unicode character terminated unexpectedly"); - //figure out the count of bytes required to encode this char - count = 1; - d = 0x7f; - while (unicode > d) - { - count++; - d = (d<<5) | 0x1f; - } - - //error if needed - if (len+count >= sizeof(pr_token)) - QCC_Error(ERR_INVALIDSTRINGIMMEDIATE, "String length exceeds %i", sizeof(pr_token)-1); - - //output it. - if (count == 1) - pr_token[len++] = (unsigned char)(c&0x7f); - else - { - c = count*6; - pr_token[len++] = (unsigned char)((unicode>>c)&(0x0000007f>>count)) | (0xffffff00 >> count); - do - { - c = c-6; - pr_token[len++] = (unsigned char)((unicode>>c)&0x3f) | 0x80; - } while(c); - } - continue; + goto forceutf8; } else if (c == 'x' || c == 'X') { @@ -1833,13 +1827,14 @@ void QCC_PR_LexString (void) c += d - 'a' + 10; else QCC_PR_ParseError(ERR_BADCHARACTERCODE, "Bad character code"); + goto forcebyte; } else if (c == '\\') c = '\\'; else if (c == '\'') c = '\''; else if (c >= '0' && c <= '9') //WARNING: This is not octal, but uses 'yellow' numbers instead (as on hud). - c = 18 + c - '0'; + c = 0xe012 + c - '0'; else if (c == '\r') { //sigh c = *pr_file_p++; @@ -1913,13 +1908,82 @@ void QCC_PR_LexString (void) else if (c == 0x7C && flag_acc) //reacc support... reacc is strange. c = '\n'; else - c |= texttype; + { + unsigned int cp; + unsigned int len = stringtype?utf8_check(pr_file_p-1, &cp):0; + if (!len) + { //invalid utf-8 encoding? don't treat it as utf-8! + c |= texttype; + goto forcequake; + } + if (texttype) + { + c = cp; + if (cp < 128) + c |= 0xe080; //FIXME: technically invalid for C0 chars. + else + { + QCC_PR_ParseWarning(ERR_BADCHARACTERCODE, "Unable to mask non-ascii chars. Attempting to mask bytes"); + c |= texttype; + goto forcequake; + } + } + else + c = cp; + pr_file_p += len-1; + } } - if (len >= sizeof(pr_token)-1) - QCC_Error(ERR_INVALIDSTRINGIMMEDIATE, "String length exceeds %i", sizeof(pr_token)-1); - pr_token[len] = c; - len++; + if (stringtype == 2) + { //we're outputting a utf-8 string. +forceutf8: + if (c > 0x10FFFFu) //RFC 3629 imposes the same limit as UTF-16 surrogate pairs. + QCC_PR_ParseWarning(WARN_NOTUTF8, "Bad unicode character code - codepoint is above 0x10FFFFu"); + + //figure out the count of bytes required to encode this char + bytecount = 1; + t = 0x7f; + while (c > t) + { + bytecount++; + t = (t<<5) | 0x1f; + } + + //error if needed + if (len+bytecount >= sizeof(pr_token)) + QCC_Error(ERR_INVALIDSTRINGIMMEDIATE, "String length exceeds %i", sizeof(pr_token)-1); + + //output it. + if (bytecount == 1) + pr_token[len++] = (unsigned char)(c&0x7f); + else + { + t = bytecount*6; + pr_token[len++] = (unsigned char)((c>>t)&(0x0000007f>>bytecount)) | (0xffffff00 >> bytecount); + do + { + t = t-6; + pr_token[len++] = (unsigned char)((c>>t)&0x3f) | 0x80; + } while(t); + } + } + else + { +forcequake: + //we need to convert it to a quake char... + if (c >= 0xe000 && c <= 0xe0ff) + c = c & 0xff; //this private use range is commonly used for quake's glyphs. + else if (c >= 0 && c <= 0x7f) + ; //FIXME: SOME c0 codes are known to quake, but many got reused for random glyphs. however I'm going to treat quake as full ascii. + else if (c > 0xff) + QCC_PR_ParseWarning(WARN_NOTUTF8, "Cannot convert character to quake's charset"); + +forcebyte: + if (len >= sizeof(pr_token)-1) + QCC_Error(ERR_INVALIDSTRINGIMMEDIATE, "String length exceeds %i", sizeof(pr_token)-1); + pr_token[len] = c; + len++; + } } } @@ -1940,7 +2004,7 @@ void QCC_PR_LexString (void) len = utf8_check(&pr_token[c], &code); if (!len || c+len>pr_immediate_strlen) { - QCC_PR_ParseWarning(WARN_NOTUTF8, "String constant is not valid utf-8"); + QCC_PR_ParseWarning(WARN_NOTUTF8, "String literal is not valid utf-8"); break; } c += len; @@ -3690,7 +3754,7 @@ void QCC_PR_Lex (void) } // handle quoted strings as a unit - if (c == '\"' || (c == 'R' && pr_file_p[1] == '\"')) + if (c == '\"' || ((c == 'R' || c == 'Q' || c == 'u' || c == 'U') && pr_file_p[1] == '\"') || (c == 'u' && pr_file_p[1] == '8' && pr_file_p[2] == '\"')) { QCC_PR_LexString (); return; diff --git a/engine/server/pr_cmds.c b/engine/server/pr_cmds.c index 8d413dda0..2b27f1d53 100644 --- a/engine/server/pr_cmds.c +++ b/engine/server/pr_cmds.c @@ -62,6 +62,7 @@ cvar_t temp1 = CVARF("temp1", "0", CVAR_ARCHIVE); cvar_t noexit = CVAR("noexit", "0"); extern cvar_t sv_specprint; +extern cvar_t pr_autocreatecvars; cvar_t pr_ssqc_memsize = CVARD("pr_ssqc_memsize", "-1", "The ammount of memory available to the QC vm. This has a theoretical maximum of 1gb, but that value can only really be used in 64bit builds. -1 will attempt to use some conservative default, but you may need to increase it. Consider also clearing pr_fixbrokenqccarrays if you need to change this cvar."); /*cvars purely for compat with others*/ @@ -95,6 +96,7 @@ cvar_t sv_gameplayfix_setmodelrealbox = CVARD("sv_gameplayfix_setmodelrealbox", #endif cvar_t sv_gameplayfix_setmodelsize_qw = CVARD("sv_gameplayfix_setmodelsize_qw", "0", "The setmodel builtin will act as a setsize for QuakeWorld mods also."); cvar_t dpcompat_nopreparse = CVARD("dpcompat_nopreparse", "0", "Xonotic uses svc_tempentity with unknowable lengths mixed with other data that needs to be translated. This cvar disables any attempt to translate or pre-parse network messages, including disabling nq/qw cross compatibility. NOTE: because preparsing will be disabled, messages might not get backbuffered correctly if too much reliable data is written."); +cvar_t dpcompat_traceontouch = CVARD("dpcompat_traceontouch", "0", "Report trace plane etc when an entity touches another."); extern cvar_t sv_listen_dp; cvar_t sv_addon[MAXADDONS]; @@ -590,11 +592,15 @@ static void QDECL SVPR_Get_FrameState(world_t *w, wedict_t *ent, framestate_t *f #endif } -static void QDECL SVPR_Event_Touch(world_t *w, wedict_t *s, wedict_t *o) +static void set_trace_globals(pubprogfuncs_t *prinst, trace_t *trace); +static void QDECL SVPR_Event_Touch(world_t *w, wedict_t *s, wedict_t *o, trace_t *trace) { int oself = pr_global_struct->self; int oother = pr_global_struct->other; + if (trace) + set_trace_globals(w->progs, trace); + pr_global_struct->self = EDICT_TO_PROG(w->progs, s); pr_global_struct->other = EDICT_TO_PROG(w->progs, o); pr_global_struct->time = w->physicstime; @@ -815,6 +821,8 @@ void PR_LoadGlabalStruct(qboolean muted) static float svphysicsmode = 2; static float writeonly; static int writeonly_int; + static int endcontentsi, surfaceflagsi; + static float endcontentsf, surfaceflagsf; static float dimension_send_default; static float dimension_default = 255; static float zero_default; @@ -938,10 +946,10 @@ void PR_LoadGlabalStruct(qboolean muted) // make sure these entries are always valid pointers ensureglobal(dimension_send, dimension_send_default); ensureglobal(dimension_default, dimension_default); - ensureglobal(trace_endcontentsf, writeonly); - ensureglobal(trace_endcontentsi, writeonly_int); - ensureglobal(trace_surfaceflagsf, writeonly); - ensureglobal(trace_surfaceflagsi, writeonly_int); + ensureglobal(trace_endcontentsf, endcontentsf); + ensureglobal(trace_endcontentsi, endcontentsi); + ensureglobal(trace_surfaceflagsf, surfaceflagsf); + ensureglobal(trace_surfaceflagsi, surfaceflagsi); ensureglobal(trace_brush_id, writeonly_int); ensureglobal(trace_brush_faceid, writeonly_int); ensureglobal(trace_surface_id, writeonly_int); @@ -1223,7 +1231,7 @@ static void PR_Decompile_f(void) if (!svprogfuncs) { Q_SetProgsParms(false); - PR_Configure(svprogfuncs, pr_ssqc_memsize.ival, MAX_PROGS, 0); + PR_Configure(svprogfuncs, PR_ReadBytesString(pr_ssqc_memsize.string), MAX_PROGS, 0); } @@ -1303,7 +1311,7 @@ static void PR_ApplyCompilation_f (void) s = PR_SaveEnts(svprogfuncs, NULL, &len, 0, 1); - PR_Configure(svprogfuncs, pr_ssqc_memsize.ival, MAX_PROGS, pr_enable_profiling.ival); + PR_Configure(svprogfuncs, PR_ReadBytesString(pr_ssqc_memsize.string), MAX_PROGS, pr_enable_profiling.ival); PR_RegisterFields(); sv.world.edict_size=PR_InitEnts(svprogfuncs, sv.world.max_edicts); @@ -1521,12 +1529,12 @@ void SVQ1_CvarChanged(cvar_t *var) static void QCBUILTIN PF_precache_model (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); static void QCBUILTIN PF_setmodel (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); void QCBUILTIN PF_makestatic (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); -static void PR_FallbackSpawn_Misc_Model(pubprogfuncs_t *progfuncs, edict_t *self) +static void PR_FallbackSpawn_Misc_Model(pubprogfuncs_t *progfuncs, edict_t *self, qboolean force) { void *pr_globals; eval_t *val; - if (sv.world.worldmodel && sv.world.worldmodel->type==mod_brush && sv.world.worldmodel->fromgame == fg_quake3) + if (sv.world.worldmodel && sv.world.worldmodel->type==mod_brush && sv.world.worldmodel->fromgame == fg_quake3 && !force) { //on q3bsp, these are expected to be handled directly by q3map2, but it doesn't always strip it. ED_Free(progfuncs, self); return; @@ -1560,12 +1568,6 @@ static void PR_FallbackSpawn_Func_Detail(pubprogfuncs_t *progfuncs, edict_t *sel void *pr_globals; eval_t *val; - if (sv.world.worldmodel && sv.world.worldmodel->type==mod_brush && sv.world.worldmodel->fromgame == fg_quake3) - { //on q3bsp, these are expected to be handled directly by q3map2, but it doesn't always strip it. - ED_Free(progfuncs, self); - return; - } - if (!self->v->model && (val = progfuncs->GetEdictFieldValue(progfuncs, self, "mdl", ev_string, NULL))) self->v->model = val->string; if (!*PR_GetString(progfuncs, self->v->model)) //must have a model, because otherwise various things will assume its not valid at all. @@ -1700,10 +1702,10 @@ static void PDECL PR_DoSpawnInitialEntity(pubprogfuncs_t *progfuncs, struct edic //the mod is responsible for freeing unrecognised ents. } else if (!strcmp(eclassname, "misc_model")) - PR_FallbackSpawn_Misc_Model(progfuncs, ed); + PR_FallbackSpawn_Misc_Model(progfuncs, ed, false); //func_detail+func_group are for compat with ericw-tools, etc. else if (!strcmp(eclassname, "func_detail_illusionary")) - PR_FallbackSpawn_Misc_Model(progfuncs, ed); + PR_FallbackSpawn_Misc_Model(progfuncs, ed, true); else if (!strcmp(eclassname, "func_detail") || !strcmp(eclassname, "func_detail_wall") || !strcmp(eclassname, "func_detail_fence")) PR_FallbackSpawn_Func_Detail(progfuncs, ed); else if (!strcmp(eclassname, "func_group")) @@ -3586,7 +3588,7 @@ static void QCBUILTIN PF_ss_LocalSound(pubprogfuncs_t *prinst, struct globalvars #define PF_ss_LocalSound PF_Fixme #endif -static void set_trace_globals(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals, trace_t *trace) +static void set_trace_globals(pubprogfuncs_t *prinst, /*struct globalvars_s *pr_globals,*/ trace_t *trace) { pr_global_struct->trace_allsolid = trace->allsolid; pr_global_struct->trace_startsolid = trace->startsolid; @@ -3674,7 +3676,7 @@ void QCBUILTIN PF_svtraceline (pubprogfuncs_t *prinst, struct globalvars_s *pr_g trace = World_Move (&sv.world, v1, mins, maxs, v2, nomonsters|MOVE_IGNOREHULL, (wedict_t*)ent); - set_trace_globals(prinst, pr_globals, &trace); + set_trace_globals(prinst, &trace); } #ifdef HEXEN2 @@ -3694,7 +3696,7 @@ static void QCBUILTIN PF_traceboxh2 (pubprogfuncs_t *prinst, struct globalvars_s trace = World_Move (&sv.world, v1, mins, maxs, v2, nomonsters|MOVE_IGNOREHULL, (wedict_t*)ent); - set_trace_globals(prinst, pr_globals, &trace); + set_trace_globals(prinst, &trace); } #endif @@ -3716,7 +3718,7 @@ static void QCBUILTIN PF_traceboxdp (pubprogfuncs_t *prinst, struct globalvars_s trace = World_Move (&sv.world, v1, mins, maxs, v2, nomonsters|MOVE_IGNOREHULL, (wedict_t*)ent); - set_trace_globals(prinst, pr_globals, &trace); + set_trace_globals(prinst, &trace); } static void QCBUILTIN PF_TraceToss (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) @@ -3732,7 +3734,7 @@ static void QCBUILTIN PF_TraceToss (pubprogfuncs_t *prinst, struct globalvars_s trace = WPhys_Trace_Toss (&sv.world, (wedict_t*)ent, (wedict_t*)ignore); - set_trace_globals(prinst, pr_globals, &trace); + set_trace_globals(prinst, &trace); } //============================================================================ @@ -4100,22 +4102,8 @@ static void QCBUILTIN PF_cvar (pubprogfuncs_t *prinst, struct globalvars_s *pr_g G_FLOAT(OFS_RETURN) = sv.world.worldmodel->fromgame == fg_halflife; else { - cvar_t *cv = Cvar_FindVar(str); - if (!cv) - { - //this little chunk is so cvars dp creates are created with meaningful values - char *def = ""; - if (!strcmp(str, "sv_maxairspeed")) - def = "30"; - else if (!strcmp(str, "sv_jumpvelocity")) - def = "270"; - else - def = ""; - - cv = Cvar_Get(str, def, 0, "QC variables"); - Con_Printf("^3Creating cvar %s\n", str); - } - if (cv->flags & CVAR_NOUNSAFEEXPAND) + cvar_t *cv = PF_Cvar_FindOrGet(str); + if (!cv || (cv->flags & CVAR_NOUNSAFEEXPAND)) G_FLOAT(OFS_RETURN) = 0; else G_FLOAT(OFS_RETURN) = cv->value; @@ -4487,7 +4475,6 @@ void QCBUILTIN PF_precache_vwep_model (pubprogfuncs_t *prinst, struct globalvars } #endif -// warning: PF_svcoredump defined but not used /* static void QCBUILTIN PF_svcoredump (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { @@ -4558,7 +4545,7 @@ static void QCBUILTIN PF_walkmove (pubprogfuncs_t *prinst, struct globalvars_s * // } // else if (!SV_TestEntityPosition(ent)) // { - G_FLOAT(OFS_RETURN) = World_movestep(&sv.world, (wedict_t*)ent, move, axis, true, false, settrace?set_trace_globals:NULL, pr_globals); + G_FLOAT(OFS_RETURN) = World_movestep(&sv.world, (wedict_t*)ent, move, axis, true, false, settrace?set_trace_globals:NULL); // if (SV_TestEntityPosition(ent)) // Con_Printf("Entity became stuck\n"); // } @@ -6243,7 +6230,6 @@ static void QCBUILTIN PF_mvdsv_freestring(pubprogfuncs_t *prinst, struct globalv } #endif -// warning: PF_strcatp defined but not used /* static void QCBUILTIN PF_strcatp(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { @@ -6269,7 +6255,6 @@ static void QCBUILTIN PF_strcatp(pubprogfuncs_t *prinst, struct globalvars_s *pr } */ -// warning: PF_redstring defined but not used /* static void QCBUILTIN PF_redstring(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { @@ -7743,7 +7728,7 @@ static void QCBUILTIN PF_h2movestep (pubprogfuncs_t *prinst, struct globalvars_s // save program state, because SV_movestep may call other progs oldself = pr_global_struct->self; - G_INT(OFS_RETURN) = World_movestep (&sv.world, (wedict_t*)ent, v, NULL, false, true, set_trace?set_trace_globals:NULL, pr_globals); + G_INT(OFS_RETURN) = World_movestep (&sv.world, (wedict_t*)ent, v, NULL, false, true, set_trace?set_trace_globals:NULL); // restore program state pr_global_struct->self = oldself; @@ -9781,7 +9766,7 @@ static void QCBUILTIN PF_runclientphys(pubprogfuncs_t *prinst, struct globalvars if (!touched->v->touch || n >= playertouchmax || (playertouch[n/8]&(1<<(n%8)))) continue; - sv.world.Event_Touch(&sv.world, (wedict_t*)touched, (wedict_t*)ent); + sv.world.Event_Touch(&sv.world, (wedict_t*)touched, (wedict_t*)ent, NULL); playertouch[n/8] |= 1 << (n%8); } pmove.numtouch = 0; @@ -11839,7 +11824,7 @@ void PR_DumpPlatform_f(void) {"SV_ShouldPause", "float(float newstatus)", QW|NQ, "Called to give the qc a change to block pause/unpause requests. Return false for the pause request to be ignored. newstatus is 1 if the user is trying to pause the game. For the duration of the call, self will be set to the player who tried to pause, or to world if it was triggered by a server-side event."}, {"SV_RunClientCommand", "void()", QW|NQ, "Called each time a player movement packet was received from a client. Self is set to the player entity which should be updated, while the input_* globals specify the various properties stored within the input packet. The contents of this function should be somewaht identical to the equivelent function in CSQC, or prediction misses will occur. If you're feeling lazy, you can simply call 'runstandardplayerphysics' after modifying the inputs."}, {"SV_AddDebugPolygons", "void()", QW|NQ, "Called each video frame. This is the only place where ssqc is allowed to call the R_BeginPolygon/R_PolygonVertex/R_EndPolygon builtins. This is exclusively for debugging, and will break in anything but single player as it will not be called if the engine is not running both a client and a server."}, - {"SV_PlayerPhysics", "void()", QW|NQ, "Legacy method to tweak player input that does not reliably work with prediction (prediction WILL break). Mods that care about prediction should use SV_RunClientCommand instead. If pr_no_playerphysics is set to 1, this function will never be called, which will either fix prediction or completely break player movement depending on whether the feature was even useful."}, + {"SV_PlayerPhysics", "void()", QW|NQ, "Compatibility method to tweak player input that does not reliably work with prediction (prediction WILL break). Mods that care about prediction should use SV_RunClientCommand instead. If pr_no_playerphysics is set to 1, this function will never be called, which will either fix prediction or completely break player movement depending on whether the feature was even useful."}, {"EndFrame", "void()", QW|NQ, "Called after non-player entities have been run at the end of the physics frame. Player physics is performed out of order and can/will still occur between EndFrame and BeginFrame."}, {"SV_CheckRejectConnection","string(string addr, string uinfo, string features) ", QW|NQ, "Called to give the mod a chance to ignore connection requests based upon client protocol support or other properties. Use infoget to read the uinfo and features arguments."}, #ifdef HEXEN2 diff --git a/engine/server/pr_q1qvm.c b/engine/server/pr_q1qvm.c index 5c349570a..8b25de434 100755 --- a/engine/server/pr_q1qvm.c +++ b/engine/server/pr_q1qvm.c @@ -977,7 +977,7 @@ static qintptr_t QVM_WalkMove (void *offset, quintptr_t mask, const qintptr_t *a move[1] = sin(yaw)*dist; move[2] = 0; - return World_movestep(&sv.world, (wedict_t*)ed, move, axis, true, false, NULL, NULL); + return World_movestep(&sv.world, (wedict_t*)ed, move, axis, true, false, NULL); } static qintptr_t QVM_DropToFloor (void *offset, quintptr_t mask, const qintptr_t *arg) { @@ -2106,7 +2106,7 @@ static void QDECL Q1QVM_Get_FrameState(world_t *w, wedict_t *ent, framestate_t * #endif } -static void QDECL Q1QVM_Event_Touch(world_t *w, wedict_t *s, wedict_t *o) +static void QDECL Q1QVM_Event_Touch(world_t *w, wedict_t *s, wedict_t *o, trace_t *trace) { int oself = pr_global_struct->self; int oother = pr_global_struct->other; diff --git a/engine/server/progdefs.h b/engine/server/progdefs.h index 7dd25209a..13f20c5c9 100644 --- a/engine/server/progdefs.h +++ b/engine/server/progdefs.h @@ -326,17 +326,27 @@ and the extension fields are added on the end and can have extra vm-specific stu #define HALFLIFEMODEL_FIELDS #endif +#if FRAME_BLENDS >= 4 +#define frame34fields \ + comfieldfloat(frame3,"Some people just don't understand how to use framegroups...") /**/\ + comfieldfloat(frame3time,".frame3 equivelent of frame1time.") /*EXT_CSQC_1*/\ + comfieldfloat(lerpfrac3,"Weight of .frame3 - .frame's weight is automatically calculated as 1-(lerpfrac+lerpfrac3+lerpfrac4), as a result these fields should NEVER add to above 1.") /**/\ + comfieldfloat(frame4,NULL) /**/\ + comfieldfloat(frame4time,".frame4 equivelent of frame1time.") /*EXT_CSQC_1*/\ + comfieldfloat(lerpfrac4,NULL) /**/\ + +#else +#define frame34fields +#endif + //this is the list for all the csqc fields. //(the #define is so the list always matches the ones pulled out) #define csqcextfields \ comfieldfloat(entnum,"This is the number of the entity that the ssqc is using.") \ comfieldfloat(frame2,"This is typically the old frame of the entity. if lerpfrac is 1, .frame will be ignored and .frame2 will be used solely. lerpfrac 0.5 will give an even 50/50 blend.") /*EXT_CSQC_1*/\ - comfieldfloat(frame3,"Some people just don't understand how to use framegroups...") /**/\ - comfieldfloat(frame4,NULL) /**/\ comfieldfloat(frame2time,".frame2 equivelent of frame1time.") /*EXT_CSQC_1*/\ - comfieldfloat(lerpfrac,"The value 0 means the entity will animate using only .frame, which will be jerky. As this value is incremented, more of frame2 will be used. If you wish to use .frame2 as the 'old' frame, it is generally recommended to start this field with the value 1, to decrement it by frametime, and when it drops below 0 add 1 to it and update .frame2 and .frame to lerp into the new frame.") /*EXT_CSQC_1*/\ - comfieldfloat(lerpfrac3,NULL) /**/\ - comfieldfloat(lerpfrac4,NULL) /**/\ + comfieldfloat(lerpfrac,"The weight of .frame2. A value of 0 means the entity will animate using only .frame, while 1 would exclusively be .frame2. As this value is incremented, more of frame2 will be used. If you wish to use .frame2 as the 'old' frame, it is generally recommended to start this field with the value 1, to decrement it by frametime, and when it drops below 0 add 1 to it and update .frame2 and .frame to lerp into the new frame.") /*EXT_CSQC_1*/\ + frame34fields \ comfieldfloat(renderflags,NULL)\ comfieldfloat(forceshader,"Contains a shader handle used to replace all surfaces upon the entity.")/*FTE_CSQC_SHADERS*/\ \ diff --git a/engine/server/savegame.c b/engine/server/savegame.c index a8db66115..45de1451b 100644 --- a/engine/server/savegame.c +++ b/engine/server/savegame.c @@ -12,6 +12,7 @@ extern cvar_t pr_enable_profiling; cvar_t sv_savefmt = CVARFD("sv_savefmt", "", CVAR_SAVE, "Specifies the format used for the saved game.\n0=legacy.\n1=fte\n2=binary"); cvar_t sv_autosave = CVARFD("sv_autosave", "5", CVAR_SAVE, "Interval for autosaves, in minutes. Set to 0 to disable autosave."); +extern cvar_t pr_ssqc_memsize; void SV_Savegame_f (void); @@ -379,7 +380,7 @@ static qboolean SV_Loadgame_Legacy(char *filename, vfsfile_t *f, int version) Q_SetProgsParms(false); svs.numprogs = 0; - PR_Configure(svprogfuncs, -1, MAX_PROGS, pr_enable_profiling.ival); + PR_Configure(svprogfuncs, PR_ReadBytesString(pr_ssqc_memsize.string), MAX_PROGS, pr_enable_profiling.ival); PR_RegisterFields(); PR_InitEnts(svprogfuncs, sv.world.max_edicts); //just in case the max edicts isn't set. progstype = pt; //presumably the progs.dat will be what they were before. @@ -901,7 +902,7 @@ qboolean SV_LoadLevelCache(const char *savename, const char *level, const char * if (progstype != PROG_H2) { Q_SetProgsParms(false); - PR_Configure(svprogfuncs, -1, MAX_PROGS, pr_enable_profiling.ival); + PR_Configure(svprogfuncs, PR_ReadBytesString(pr_ssqc_memsize.string), MAX_PROGS, pr_enable_profiling.ival); PR_RegisterFields(); PR_InitEnts(svprogfuncs, sv.world.max_edicts); } diff --git a/engine/server/sv_ccmds.c b/engine/server/sv_ccmds.c index 136229bed..a5791d104 100644 --- a/engine/server/sv_ccmds.c +++ b/engine/server/sv_ccmds.c @@ -381,10 +381,30 @@ static void SV_Give_f (void) static int QDECL ShowMapList (const char *name, qofs_t flags, time_t mtime, void *parm, searchpathfuncs_t *spath) { + const char *levelshots[] = + { + "levelshots/%s.tga", + "levelshots/%s.jpg", + "levelshots/%s.png", + "maps/%s.tga", + "maps/%s.jpg", + "maps/%s.png" + }; + size_t u; char stripped[64]; if (name[5] == 'b' && name[6] == '_') //skip box models return true; COM_StripExtension(name+5, stripped, sizeof(stripped)); + for (u = 0; u < countof(levelshots); u++) + { + const char *ls = va(levelshots[u], stripped); + if (COM_FCheckExists(ls)) + { + Con_Printf("^[\\map\\%s\\img\\%s\\w\\64\\h\\48^]", stripped, ls); + Con_Printf("^[[%s]\\map\\%s\\tipimg\\%s^]\n", stripped, stripped, ls); + return true; + } + } Con_Printf("^[[%s]\\map\\%s^]\n", stripped, stripped); return true; } diff --git a/engine/server/sv_init.c b/engine/server/sv_init.c index 90766e2ba..51f287620 100644 --- a/engine/server/sv_init.c +++ b/engine/server/sv_init.c @@ -852,8 +852,6 @@ void SV_SpawnServer (const char *server, const char *startspot, qboolean noents, SCR_ImageName(server); #endif - NET_InitServer(); - sv.state = ss_dead; if (sv.gamedirchanged) @@ -1391,6 +1389,8 @@ void SV_SpawnServer (const char *server, const char *startspot, qboolean noents, SCR_ImageName(server); #endif + NET_InitServer(); + // // spawn the rest of the entities on the map // diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index 41f7f5fb3..df59ea43f 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -110,10 +110,10 @@ cvar_t sv_reconnectlimit = CVARD("sv_reconnectlimit", "0", "Blocks dupe connecti extern cvar_t net_enable_dtls; cvar_t sv_reportheartbeats = CVARD("sv_reportheartbeats", "2", "Print a notice each time a heartbeat is sent to a master server. When set to 2, the message will be displayed once."); cvar_t sv_highchars = CVAR("sv_highchars", "1"); -cvar_t sv_maxrate = CVAR("sv_maxrate", "30000"); -cvar_t sv_maxdrate = CVARAF("sv_maxdrate", "500000", - "sv_maxdownloadrate", 0); -cvar_t sv_minping = CVARF("sv_minping", "", CVAR_SERVERINFO); +cvar_t sv_maxrate = CVARD("sv_maxrate", "50000", "This controls the maximum number of bytes any indivual player may receive (when not downloading). The individual user's rate will also be controlled by the user's rate cvar."); +cvar_t sv_maxdrate = CVARAFD("sv_maxdrate", "500000", + "sv_maxdownloadrate", 0, "This cvar controls the maximum number of bytes sent to each player per second while that player is downloading.\nIf this cvar is set to 0, there will be NO CAP for download rates (if the user's drate is empty/0 too, then expect really fast+abusive downloads that could potentially be considered denial of service attacks)"); +cvar_t sv_minping = CVARFD("sv_minping", "", CVAR_SERVERINFO, "Simulate fake lag for any players with a ping under the value specified here. Value is in milliseconds."); cvar_t sv_bigcoords = CVARFD("sv_bigcoords", "1", 0, "Uses floats for coordinates instead of 16bit values.\nAlso boosts angle precision, so can be useful even on small maps.\nAffects clients thusly:\nQW: enforces a mandatory protocol extension\nDP: enables DPP7 protocol support\nNQ: uses RMQ protocol (protocol 999)."); cvar_t sv_calcphs = CVARFD("sv_calcphs", "2", CVAR_LATCH, "Enables culling of sound effects. 0=always skip phs. Sounds are globally broadcast. 1=always generate phs. Sounds are always culled. On large maps the phs will be dumped to disk. 2=On large single-player maps, generation of phs is skipped. Otherwise like option 1."); @@ -5546,6 +5546,8 @@ void SV_ExtractFromUserinfo (client_t *cl, qboolean verbose) } //make CERTAIN that the name we think they're using is actually the name everyone else sees too. + //bots are allowed empty names. this gives the gamecode a chance to actually assign one. + if (cl->protocol != SCP_BAD) { InfoBuf_SetValueForKey (&cl->userinfo, "name", newname); val = InfoBuf_ValueForKey (&cl->userinfo, "name"); diff --git a/engine/server/sv_move.c b/engine/server/sv_move.c index 0530afeab..5e23efd0b 100644 --- a/engine/server/sv_move.c +++ b/engine/server/sv_move.c @@ -142,7 +142,7 @@ possible, no move is done, false is returned, and pr_global_struct->trace_normal is set to the normal of the blocking wall ============= */ -qboolean World_movestep (world_t *world, wedict_t *ent, vec3_t move, vec3_t axis[3], qboolean relink, qboolean noenemy, void (*set_move_trace)(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals, trace_t *trace), struct globalvars_s *set_trace_globs) +qboolean World_movestep (world_t *world, wedict_t *ent, vec3_t move, vec3_t axis[3], qboolean relink, qboolean noenemy, void (*set_move_trace)(pubprogfuncs_t *prinst, trace_t *trace)) { float dz; vec3_t oldorg, neworg, end; @@ -192,7 +192,7 @@ qboolean World_movestep (world_t *world, wedict_t *ent, vec3_t move, vec3_t axis } trace = World_Move (world, ent->v->origin, ent->v->mins, ent->v->maxs, neworg, false, ent); if (set_move_trace) - set_move_trace(world->progs, set_trace_globs, &trace); + set_move_trace(world->progs, &trace); if (trace.fraction == 1) { @@ -218,7 +218,7 @@ qboolean World_movestep (world_t *world, wedict_t *ent, vec3_t move, vec3_t axis trace = World_Move (world, neworg, ent->v->mins, ent->v->maxs, end, false, ent); if (set_move_trace) - set_move_trace(world->progs, set_trace_globs, &trace); + set_move_trace(world->progs, &trace); if (trace.allsolid) return false; @@ -229,7 +229,7 @@ qboolean World_movestep (world_t *world, wedict_t *ent, vec3_t move, vec3_t axis VectorMA(neworg, -movevars.stepheight, axis[2], neworg); trace = World_Move (world, neworg, ent->v->mins, ent->v->maxs, end, false, ent); if (set_move_trace) - set_move_trace(world->progs, set_trace_globs, &trace); + set_move_trace(world->progs, &trace); if (trace.allsolid || trace.startsolid) return false; } @@ -448,7 +448,7 @@ qboolean World_StepDirection (world_t *world, wedict_t *ent, float yaw, float di //FIXME: Hexen2: ent flags & FL_SET_TRACE VectorCopy (ent->v->origin, oldorigin); - if (World_movestep (world, ent, move, axis, false, false, NULL, NULL)) + if (World_movestep (world, ent, move, axis, false, false, NULL)) { delta = anglemod(delta); if (delta > 45 && delta < 315) diff --git a/engine/server/sv_mvd.c b/engine/server/sv_mvd.c index 9e0cc8dc3..81e9caa1e 100644 --- a/engine/server/sv_mvd.c +++ b/engine/server/sv_mvd.c @@ -2659,8 +2659,8 @@ char *SV_MVDName2Txt(char *name) ext = COM_GetFileExtension(s, ext); if (!ext || !*ext) //if there's no extension on there, then make sure we're pointing to the end of the string. ext = s+strlen(s); - if (ext > s+sizeof(s)+4) //make sure we don't overflow the buffer by truncating the base/path, ensuring that we don't write some other type of file. - ext = s+sizeof(s)+4; //should probably make this an error case and abort instead. + if (ext > s+sizeof(s)-4) //make sure we don't overflow the buffer by truncating the base/path, ensuring that we don't write some other type of file. + ext = s+sizeof(s)-4; //should probably make this an error case and abort instead. strcpy((char*)ext, ".txt"); return va("%s", s); diff --git a/engine/server/sv_phys.c b/engine/server/sv_phys.c index a534792fa..2bd8f6daa 100644 --- a/engine/server/sv_phys.c +++ b/engine/server/sv_phys.c @@ -70,7 +70,7 @@ cvar_t pm_watersinkspeed = CVARFD("pm_watersinkspeed", "", CVAR_SERVERINFO, "Th cvar_t pm_flyfriction = CVARFD("pm_flyfriction", "", CVAR_SERVERINFO, "Amount of friction that applies in fly or 6dof mode. Empty means 4."); cvar_t pm_slidefix = CVARF("pm_slidefix", "", CVAR_SERVERINFO); cvar_t pm_slidyslopes = CVARF("pm_slidyslopes", "", CVAR_SERVERINFO); -cvar_t pm_airstep = CVARF("pm_airstep", "", CVAR_SERVERINFO); +cvar_t pm_airstep = CVARAF("pm_airstep", "", "sv_jumpstep", CVAR_SERVERINFO); cvar_t pm_stepdown = CVARF("pm_stepdown", "", CVAR_SERVERINFO); cvar_t pm_walljump = CVARF("pm_walljump", "", CVAR_SERVERINFO); @@ -105,7 +105,6 @@ void WPhys_Init(void) static void WPhys_Physics_Toss (world_t *w, wedict_t *ent); -// warning: SV_CheckAllEnts defined but not used /* ================ SV_CheckAllEnts @@ -239,12 +238,12 @@ static void WPhys_Impact (world_t *w, wedict_t *e1, trace_t *trace) *w->g.time = w->physicstime; if (e1->v->touch && e1->v->solid != SOLID_NOT) { - w->Event_Touch(w, e1, e2); + w->Event_Touch(w, e1, e2, trace); } if (e2->v->touch && e2->v->solid != SOLID_NOT) { - w->Event_Touch(w, e2, e1); + w->Event_Touch(w, e2, e1, trace); } } @@ -1657,7 +1656,6 @@ static void WPhys_WallFriction (wedict_t *ent, trace_t *trace) ent->v->velocity[1] = side[1] * (1 + d); } -// warning: SV_TryUnstick defined but not used /* ===================== SV_TryUnstick @@ -1951,7 +1949,7 @@ static void WPhys_WalkMove (world_t *w, wedict_t *ent, const float *gravitydir) return; // only step up while jumping if that is enabled -// if (!(sv_jumpstep.value && sv_gameplayfix_stepwhilejumping.value)) + if (!pm_airstep.value) if (!oldonground && ent->v->waterlevel == 0) return; } @@ -2079,7 +2077,7 @@ void WPhys_RunEntity (world_t *w, wedict_t *ent) { #ifdef HEXEN2 wedict_t *movechain; - vec3_t initial_origin = {0},initial_angle = {0}; // warning: initial_?[?] may be used uninitialized in this function + vec3_t initial_origin = {0},initial_angle = {0}; #endif const float *gravitydir; diff --git a/engine/server/sv_send.c b/engine/server/sv_send.c index 4d0f4c257..3daeb4de0 100644 --- a/engine/server/sv_send.c +++ b/engine/server/sv_send.c @@ -329,6 +329,22 @@ void Con_DLPrintf (int level, const char *fmt, ...) if (log_developer.value) Con_Log(msg); // log to console } + +//for spammed warnings, so they don't spam prints with every single frame/call. the timer arg should be a static local. +void VARGS Con_ThrottlePrintf (float *timer, int developerlevel, const char *fmt, ...) +{ + va_list argptr; + char msg[MAXPRINTMSG]; + + va_start (argptr,fmt); + vsnprintf (msg,sizeof(msg)-1, fmt,argptr); + va_end (argptr); + + if (developerlevel) + Con_DLPrintf (developerlevel, "%s", msg); + else + Con_Printf("%s", msg); +} #endif /* diff --git a/engine/server/sv_sys_unix.c b/engine/server/sv_sys_unix.c index 08e693385..f500fff36 100644 --- a/engine/server/sv_sys_unix.c +++ b/engine/server/sv_sys_unix.c @@ -879,10 +879,10 @@ int main(int argc, char *argv[]) #endif //decide if we should be printing colours to the stdout or not. - if (!COM_CheckParm("-nocolour")||!COM_CheckParm("-nocolor")) + if (COM_CheckParm("-nocolour")||COM_CheckParm("-nocolor")) useansicolours = false; else - useansicolours = (isatty(STDOUT_FILENO) || !COM_CheckParm("-colour")||!COM_CheckParm("-color")); + useansicolours = (isatty(STDOUT_FILENO) || COM_CheckParm("-colour") || COM_CheckParm("-color")); switch(Sys_CheckChRoot()) { diff --git a/engine/server/sv_user.c b/engine/server/sv_user.c index 8ca2112fe..f82c2b0b0 100644 --- a/engine/server/sv_user.c +++ b/engine/server/sv_user.c @@ -4224,7 +4224,14 @@ void SV_PTrack_f (void) if (!SV_CanTrack(host_client, i+1)) { - SV_ClientTPrintf (host_client, PRINT_HIGH, "invalid player to track\n"); + if (i < 0 || i >= sv.allocated_client_slots) + SV_ClientTPrintf (host_client, PRINT_HIGH, "invalid player to track\n"); + else if (svs.clients[i].spectator) + SV_ClientTPrintf (host_client, PRINT_HIGH, "cannot track other spectators\n"); + else if (svs.clients[i].state != cs_spawned) + SV_ClientTPrintf (host_client, PRINT_HIGH, "cannot track - player not spawned yet\n"); + else + SV_ClientTPrintf (host_client, PRINT_HIGH, "invalid player to track\n"); host_client->spec_track = 0; ent = EDICT_NUM_PB(svprogfuncs, host_client - svs.clients + 1); tent = EDICT_NUM_PB(svprogfuncs, 0); @@ -7416,11 +7423,11 @@ if (sv_player->v->health > 0 && before && !after ) VectorCopy(pmove.touchvel[i], old_vel); VectorCopy(pmove.touchvel[i], sv_player->v->velocity); } - sv.world.Event_Touch(&sv.world, (wedict_t*)ent, (wedict_t*)sv_player); + sv.world.Event_Touch(&sv.world, (wedict_t*)ent, (wedict_t*)sv_player, NULL); } if (sv_player->v->touch && !ED_ISFREE(ent)) - sv.world.Event_Touch(&sv.world, (wedict_t*)sv_player, (wedict_t*)ent); + sv.world.Event_Touch(&sv.world, (wedict_t*)sv_player, (wedict_t*)ent, NULL); } } diff --git a/engine/server/svq2_game.c b/engine/server/svq2_game.c index 023b4050b..700837f90 100644 --- a/engine/server/svq2_game.c +++ b/engine/server/svq2_game.c @@ -803,7 +803,7 @@ static void QDECL PFQ2_SetAreaPortalState(unsigned int p, qboolean s) qboolean SVQ2_InitGameProgs(void) { extern cvar_t maxclients; - volatile static game_import_t import; //volatile because msvc sucks + static volatile game_import_t import; //volatile because msvc sucks if (COM_CheckParm("-noq2dll")) { SVQ2_ShutdownGameProgs(); diff --git a/engine/server/world.c b/engine/server/world.c index 3deab7fd3..95c1f8ede 100644 --- a/engine/server/world.c +++ b/engine/server/world.c @@ -1921,7 +1921,7 @@ void World_TouchAllLinks (world_t *w, wedict_t *ent) if (!((int)ent->xv->dimension_solid & (int)touch->xv->dimension_hit)) //didn't change did it?... continue; - w->Event_Touch(w, touch, ent); + w->Event_Touch(w, touch, ent, NULL); if (ED_ISFREE(ent)) break; @@ -2059,7 +2059,7 @@ static void World_ClipToLinks (world_t *w, areagridlink_t *node, moveclip_t *cli //even if the trace traveled less, we still care if it was in a solid. clip->trace.startsolid |= trace.startsolid; clip->trace.allsolid |= trace.allsolid; - if (!clip->trace.ent) + if (!clip->trace.ent || trace.fraction == clip->trace.fraction) //xonotic requires that second test (DP has no check at all, which would end up reporting mismatched fraction/ent results, so yuck). { clip->trace.contents = trace.contents; clip->trace.ent = touch; diff --git a/engine/web/gl_vidweb.c b/engine/web/gl_vidweb.c index 4c030ea7e..d17306c79 100644 --- a/engine/web/gl_vidweb.c +++ b/engine/web/gl_vidweb.c @@ -138,7 +138,7 @@ static unsigned int domkeytoshift(unsigned int code) /* 0*/ 0,0,0,0,0,0,0,0, K_BACKSPACE,K_TAB,0,0,0,K_ENTER,0,0, /* 16*/ K_SHIFT,K_CTRL,K_ALT,K_PAUSE,K_CAPSLOCK,0,0,0,0,0,0,K_ESCAPE,0,0,0,0, /* 32*/ ' ',K_PGUP,K_PGDN,K_END,K_HOME,K_LEFTARROW,K_UPARROW,K_RIGHTARROW, K_DOWNARROW,0,0,0,K_PRINTSCREEN,K_INS,K_DEL,0, - /* 48*/ ')','!','\"',0/**/,'$','%','^','&', '*','(',0,':',0,'+',0,0, + /* 48*/ ')','!','\"',0xA3/*poundsign*/,'$','%','^','&', '*','(',0,':',0,'+',0,0, /* 64*/ 0,'A','B','C','D','E','F','G', 'H','I','J','K','L','M','N','O', /* 80*/ 'P','Q','R','S','T','U','V','W', 'X','Y','Z',K_LWIN,K_RWIN,K_APP,0,0,