diff --git a/engine/Makefile b/engine/Makefile index afdea79d..29cdbbbd 100644 --- a/engine/Makefile +++ b/engine/Makefile @@ -317,7 +317,7 @@ ifeq ($(FTE_TARGET),macosx) endif BASELDFLAGS ?= -lm -ldl -lpthread -ifneq (,$(findstring win,$(FTE_TARGET))) +ifeq (win,$(findstring cyg,$(FTE_TARGET))$(findstring win,$(FTE_TARGET))) BASELDFLAGS=-lm MINGW_LIBS_DIR=$(LIBS_DIR)/mingw-libs @@ -544,6 +544,7 @@ PROGS_OBJS = \ SERVER_OBJS = \ pr_cmds.o \ pr_q1qvm.o \ + pr_lua.o \ sv_master.o \ sv_init.o \ sv_main.o \ @@ -852,27 +853,51 @@ endif ifeq ($(FTE_TARGET),vc) DEBUG_CFLAGS= MSVCDIR=Microsoft Visual Studio 10.0 - WINDOWSSDKDIR=C:/Program Files/Microsoft SDKs/Windows/v7.1 - ifeq ($(BITS),64) - WINDRES=x86_64-w64-mingw32-windres - MSVCPATH=C:/Program Files (x86)/$(MSVCDIR)/VC/BIN/amd64/ + ifeq ($(WINRT),1) + WINDOWSSDKDIR=C:/Program Files (x86)/Windows Kits/8.1 - MSVCINC=-I"C:\Program Files (x86)\$(MSVCDIR)\VC\ATLMFC\INCLUDE" -I"C:\Program Files (x86)\$(MSVCDIR)\VC\INCLUDE" -I"C:\Program Files (x86)\Microsoft Visual Studio 8\VC\PlatformSDK\include" -I"C:\Program Files (x86)\Microsoft Visual Studio 8\SDK\v2.0\include" - MSVCLIB=/LIBPATH:"C:\Program Files (x86)\$(MSVCPATH)\VC\ATLMFC\LIB\amd64" /LIBPATH:"C:\Program Files (x86)\$(MSVCPATH)\VC\LIB\amd64" /LIBPATH:"$(WINDOWSSDKDIR)\lib\amd64" /LIBPATH:"C:\Program Files (x86)\$(MSVCPATH)\SDK\v2.0\LIB\AMD64" - JPEGLIB=libs/libjpeg$(BITS).lib + ifeq ($(BITS),64) + WINDRES=x86_64-w64-mingw32-windres + MSVCPATH=C:/Program Files (x86)/$(MSVCDIR)/VC/BIN/amd64/ + + + else + WINDRES=i686-w64-mingw32-windres + MSVCPATH=C:/Program Files (x86)/$(MSVCDIR)/VC/BIN/ + + SDKINC=-I"$(WINDOWSSDKDIR)\Include\shared" -I"$(WINDOWSSDKDIR)\Include\um" + MSVCINC=-I"C:\Program Files (x86)\$(MSVCDIR)\VC\INCLUDE" +#-I"C:\Program Files (x86)\$(MSVCDIR)\VC\ATLMFC\INCLUDE" +# -I"C:\Program Files (x86)\$(MSVCDIR)\VC\PlatformSDK\include" -I"C:\Program Files (x86)\$(MSVCDIR)\SDK\v2.0\include" + + MSVCLIB=/LIBPATH:"C:\Program Files (x86)\$(MSVCDIR)\VC\ATLMFC\LIB" /LIBPATH:"C:\Program Files (x86)\$(MSVCDIR)\VC\LIB" /LIBPATH:"$(WINDOWSSDKDIR)/lib/winv6.3/um/x86" /LIBPATH:"C:\Program Files (x86)\$(MSVCDIR)\SDK\v2.0\LIB" + JPEGLIB=libs/jpeg.lib + endif else - WINDRES=i686-w64-mingw32-windres - MSVCPATH=C:/Program Files (x86)/$(MSVCDIR)/VC/BIN/ + WINDOWSSDKDIR=C:/Program Files/Microsoft SDKs/Windows/v7.1 - MSVCINC=-I"C:\Program Files (x86)\$(MSVCDIR)\VC\ATLMFC\INCLUDE" -I"C:\Program Files (x86)\$(MSVCDIR)\VC\INCLUDE" -I"C:\Program Files (x86)\$(MSVCDIR)\VC\PlatformSDK\include" -I"C:\Program Files (x86)\$(MSVCDIR)\SDK\v2.0\include" - MSVCLIB=/LIBPATH:"C:\Program Files (x86)\$(MSVCDIR)\VC\ATLMFC\LIB" /LIBPATH:"C:\Program Files (x86)\$(MSVCDIR)\VC\LIB" /LIBPATH:"$(WINDOWSSDKDIR)\lib" /LIBPATH:"C:\Program Files (x86)\$(MSVCDIR)\SDK\v2.0\LIB" - JPEGLIB=libs/jpeg.lib + ifeq ($(BITS),64) + WINDRES=x86_64-w64-mingw32-windres + + MSVCPATH=C:/Program Files (x86)/$(MSVCDIR)/VC/BIN/amd64/ + + MSVCINC=-I"C:\Program Files (x86)\$(MSVCDIR)\VC\ATLMFC\INCLUDE" -I"C:\Program Files (x86)\$(MSVCDIR)\VC\INCLUDE" -I"C:\Program Files (x86)\Microsoft Visual Studio 8\VC\PlatformSDK\include" -I"C:\Program Files (x86)\Microsoft Visual Studio 8\SDK\v2.0\include" + MSVCLIB=/LIBPATH:"C:\Program Files (x86)\$(MSVCPATH)\VC\ATLMFC\LIB\amd64" /LIBPATH:"C:\Program Files (x86)\$(MSVCPATH)\VC\LIB\amd64" /LIBPATH:"$(WINDOWSSDKDIR)\lib\amd64" /LIBPATH:"C:\Program Files (x86)\$(MSVCPATH)\SDK\v2.0\LIB\AMD64" + JPEGLIB=libs/libjpeg$(BITS).lib + else + WINDRES=i686-w64-mingw32-windres + MSVCPATH=C:/Program Files (x86)/$(MSVCDIR)/VC/BIN/ + + MSVCINC=-I"C:\Program Files (x86)\$(MSVCDIR)\VC\ATLMFC\INCLUDE" -I"C:\Program Files (x86)\$(MSVCDIR)\VC\INCLUDE" -I"C:\Program Files (x86)\$(MSVCDIR)\VC\PlatformSDK\include" -I"C:\Program Files (x86)\$(MSVCDIR)\SDK\v2.0\include" + MSVCLIB=/LIBPATH:"C:\Program Files (x86)\$(MSVCDIR)\VC\ATLMFC\LIB" /LIBPATH:"C:\Program Files (x86)\$(MSVCDIR)\VC\LIB" /LIBPATH:"$(WINDOWSSDKDIR)\lib" /LIBPATH:"C:\Program Files (x86)\$(MSVCDIR)\SDK\v2.0\LIB" + JPEGLIB=libs/jpeg.lib + endif endif STRIP=@echo strip EXEPOSTFIX=.exe - CC="$(MSVCPATH)cl" $(MSVCINC) -D_CRT_SECURE_NO_WARNINGS + CC="$(MSVCPATH)cl" $(SDKINC) $(MSVCINC) -D_CRT_SECURE_NO_WARNINGS DEBUG_CFLAGS ?= -Od $(CPUOPTIMIZATIONS) /fp:fast PROFILE_CFLAGS = -O2 -Ot -Ox -GL $(CPUOPTIMISATIONS) /fp:fast PROFILE_LDFLAGS = /LTCG:PGINSTRUMENT @@ -880,8 +905,8 @@ ifeq ($(FTE_TARGET),vc) RELEASE_LDFLAGS = /LTCG # /LTCG:PGOPTIMIZE - DO_CC=@$(CC) /nologo $(ALL_CFLAGS) -Fo$(shell cygpath -m $@) -c $(shell cygpath -m $<) - DO_LD=$(DO_ECHO) "$(MSVCPATH)link" /nologo /out:"$(shell cygpath -m $@)" /nodefaultlib:libc.lib /LARGEADDRESSAWARE /nodefaultlib:MSVCRT $(MSVCLIB) /manifest:no /OPT:REF wsock32.lib user32.lib kernel32.lib advapi32.lib winmm.lib libs/zlib$(BITS).lib shell32.lib + DO_CC=$(CC) /nologo $(ALL_CFLAGS) -Fo$(shell cygpath -m $@) -c $(shell cygpath -m $<) + DO_LD=$(DO_ECHO) "$(MSVCPATH)link" /nologo /out:"$(shell cygpath -m $@)" /nodefaultlib:libc.lib /LARGEADDRESSAWARE /nodefaultlib:MSVCRT $(MSVCLIB) $(SDKLIB) /manifest:no /OPT:REF wsock32.lib user32.lib kernel32.lib advapi32.lib winmm.lib libs/zlib$(BITS).lib shell32.lib PRECOMPHEADERS = DEPCC= diff --git a/engine/client/cd_win.c b/engine/client/cd_win.c index c8261489..f3e49d9c 100644 --- a/engine/client/cd_win.c +++ b/engine/client/cd_win.c @@ -23,6 +23,10 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "quakedef.h" #include "winquake.h" +#ifdef WINRT +#include "cd_null.c" +#else + #if defined(_MSC_VER) && (_MSC_VER < 1300) #define DWORD_PTR DWORD #endif @@ -366,3 +370,4 @@ void CDAudio_Update(void) void CDAudio_Init(void) { } +#endif diff --git a/engine/client/cl_cg.c b/engine/client/cl_cg.c index 7d7f8eb4..826dde6d 100644 --- a/engine/client/cl_cg.c +++ b/engine/client/cl_cg.c @@ -24,7 +24,7 @@ extern int mod_numknown; #define VM_FROMMHANDLE(a) ((a&&((unsigned int)a)<=mod_numknown)?mod_known+a-1:NULL) #define VM_TOMHANDLE(a) (a?a-mod_known+1:0) -#define VM_FROMSHANDLE(a) (a?r_shaders[a-1]:NULL) +#define VM_FROMSHANDLE(a) ((a&&(unsigned int)a<=r_numshaders)?r_shaders[a-1]:NULL) #define VM_TOSHANDLE(a) (a?a->id+1:0) extern model_t box_model; @@ -730,14 +730,14 @@ static qintptr_t CG_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con int i; char *mapname = VM_POINTER(arg[0]); strcpy(cl.model_name[1], mapname); - cl.worldmodel = cl.model_precache[1] = Mod_ForName(mapname, false); + cl.worldmodel = cl.model_precache[1] = Mod_ForName(mapname, MLV_SILENT); if (cl.worldmodel->needload) - Host_EndGame("Couldn't load map"); + Host_EndGame("Couldn't load map %s", mapname); for (i=1 ; inumsubmodels ; i++) { strcpy(cl.model_name[1+i], va("*%i", i)); - cl.model_precache[i+1] = Mod_ForName (cl.model_name[i+1], false); + cl.model_precache[i+1] = Mod_ForName (cl.model_name[i+1], MLV_SILENT); } } @@ -772,16 +772,18 @@ static qintptr_t CG_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con case CG_R_REGISTERMODEL: //precache model { + char *name = VM_POINTER(arg[0]); model_t *mod; - mod = Mod_ForName(VM_POINTER(arg[0]), false); + mod = Mod_ForName(name, MLV_SILENT); if (mod->needload || mod->type == mod_dummy) - return 0; - VM_LONG(ret) = VM_TOMHANDLE(mod); + VM_LONG(ret) = 0; + else + VM_LONG(ret) = VM_TOMHANDLE(mod); } break; case CG_R_REGISTERSKIN: - VM_LONG(ret) = VM_TOSTRCACHE(arg[0]); + VM_LONG(ret) = Mod_RegisterSkinFile(VM_POINTER(arg[0])); break; case CG_R_REGISTERSHADER: @@ -969,7 +971,7 @@ static qintptr_t CG_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con VALIDATEPOINTER(arg[1], sizeof(q3usercmd_t)); VM_LONG(ret) = CGQ3_GetUserCmd(VM_LONG(arg[0]), VM_POINTER(arg[1])); break; - case CG_SETUSERCMDVALUE: //weaponselect, zoomsensativity. + case CG_SETUSERCMDVALUE: //weaponselect, zoomsensitivity. ccs.selected_weapon = VM_LONG(arg[0]); in_sensitivityscale = VM_FLOAT(arg[1]); break; @@ -1163,6 +1165,16 @@ void CG_Stop (void) } } +qboolean CG_VideoRestarted(void) +{ + if (cgvm) + { + VM_Call(cgvm, CG_INIT, ccs.serverMessageNum, ccs.lastServerCommandNum, cl.playerview[0].playernum); + return true; + } + return false; +} + void CG_Start (void) { SCR_SetLoadingStage(0); diff --git a/engine/client/cl_demo.c b/engine/client/cl_demo.c index 0fececff..fd5f1f99 100644 --- a/engine/client/cl_demo.c +++ b/engine/client/cl_demo.c @@ -1643,6 +1643,13 @@ void CL_PlayDemoStream(vfsfile_t *file, struct dl_download *dl, char *filename, TP_ExecTrigger ("f_demostart"); } +vfsfile_t *CL_OpenFileInZipOrSys(char *name) +{ + if (*name == '#') + return VFSOS_Open(name+1, "rb"); + else + return CL_OpenFileInPackage(NULL, name); +} void CL_PlayDemo(char *demoname) { char name[256]; @@ -1654,10 +1661,7 @@ void CL_PlayDemo(char *demoname) // Q_strncpyz (name, demoname, sizeof(name)); COM_DefaultExtension (name, ".qwd", sizeof(name)); - if (*name == '#') - f = VFSOS_Open(name+1, "rb"); - else - f = FS_OpenVFS(name, "rb", FS_GAME); + f = CL_OpenFileInZipOrSys(name); if (!f) { Q_strncpyz (name, demoname, sizeof(name)); @@ -1683,7 +1687,6 @@ void CL_PlayDemo(char *demoname) return; } Q_strncpyz (lastdemoname, demoname, sizeof(lastdemoname)); - Con_Printf ("Playing demo from %s.\n", name); if (!VFS_GETLEN (f)) { @@ -1958,7 +1961,7 @@ void CL_QTVPoll (void) MC_AddCenterPicture(sourcesmenu, 4, 24, "gfx/p_option.lmp"); } if (init_numplayers == true && init_numviewers == true) - MC_AddConsoleCommand(sourcesmenu, 42, (sourcenum++)*8 + 32, va("%s (p%i, v%i)", srchost, numplayers, numviewers), va("qtvplay %i@%s\n", streamid, qtvhostname)); + MC_AddConsoleCommand(sourcesmenu, 42, 170, (sourcenum++)*8 + 32, va("%s (p%i, v%i)", srchost, numplayers, numviewers), va("qtvplay %i@%s\n", streamid, qtvhostname)); //else // FIXME: add error message here } diff --git a/engine/client/cl_ents.c b/engine/client/cl_ents.c index 50479cd1..60a08f96 100644 --- a/engine/client/cl_ents.c +++ b/engine/client/cl_ents.c @@ -2932,7 +2932,7 @@ static void CL_TransitionPacketEntities(int newsequence, packet_entities_t *newp if (age > 1) age = 1; - if (cl_predict_players.ival) + if (cl_predict_players.ival && pmove.numphysent) { CL_PredictEntityMovement(from, age); VectorCopy(from->u.q1.predorg, le->origin); @@ -3076,7 +3076,7 @@ static qboolean CL_ChooseInterpolationFrames(int *newf, int *oldf, float servert This can happen if the client's predicted time is greater than the most recently received packet. This should of course not happen... */ - Con_DPrintf("Warning: No lerp-to frame packet\n"); +// Con_DPrintf("Warning: No lerp-to frame packet\n"); /*just grab the most recent frame that is valid*/ for (i = cls.netchan.incoming_sequence; i >= cls.netchan.incoming_sequence-UPDATE_MASK; i--) @@ -3152,8 +3152,8 @@ void CL_TransitionEntities (void) else frac = (servertime-packold->servertime)/(packnew->servertime-packold->servertime); -// Con_Printf("%f %f %f (%f) (%i)\n", packold->servertime, servertime, packnew->servertime, frac, newff); -// Con_Printf("%f %f %f\n", cl.oldgametime, servertime, cl.gametime); +// if (!cl.paused) +// Con_Printf("%f %f %f (%f) (%i) %f %f %f\n", packold->servertime, servertime, packnew->servertime, frac, newff, cl.oldgametime, servertime, cl.gametime); CL_TransitionPacketEntities(newff, packnew, packold, frac, servertime); cl.packfrac = frac; @@ -3268,6 +3268,7 @@ void CL_LinkPacketEntities (void) ent = &cl_visedicts[cl_numvisedicts]; ent->rtype = RT_MODEL; ent->playerindex = -1; + ent->customskin = 0; ent->topcolour = TOP_DEFAULT; ent->bottomcolour = BOTTOM_DEFAULT; ent->h2playerclass = 0; diff --git a/engine/client/cl_input.c b/engine/client/cl_input.c index 1a112c81..3d15b36f 100644 --- a/engine/client/cl_input.c +++ b/engine/client/cl_input.c @@ -37,7 +37,7 @@ cvar_t cl_netfps = CVAR("cl_netfps", "150"); cvar_t cl_sparemsec = CVARC("cl_sparemsec", "10", CL_SpareMsec_Callback); cvar_t cl_queueimpulses = CVAR("cl_queueimpulses", "0"); cvar_t cl_smartjump = CVAR("cl_smartjump", "1"); -cvar_t cl_run = CVARD("cl_run", "1", "Enables autorun, doubling the effective value of cl_forwardspeed and friends."); +cvar_t cl_run = CVARD("cl_run", "0", "Enables autorun, inverting the state of the +speed key."); cvar_t cl_prydoncursor = CVAR("cl_prydoncursor", ""); //for dp protocol cvar_t cl_instantrotate = CVARF("cl_instantrotate", "1", CVAR_SEMICHEAT); @@ -1120,9 +1120,6 @@ void VARGS CL_SendClientCommand(qboolean reliable, char *format, ...) Q_vsnprintfz (string,sizeof(string), format,argptr); va_end (argptr); - - Con_DPrintf("Queing stringcmd %s\n", string); - #ifdef Q3CLIENT if (cls.protocol == CP_QUAKE3) { @@ -1477,7 +1474,7 @@ qboolean CLQW_SendCmd (sizebuf_t *buf) MSG_WriteByte (buf, 0); // write our lossage percentage - lost = CL_CalcNet(); + lost = CL_CalcNet(r_netgraph.value); MSG_WriteByte (buf, (qbyte)lost); firstsize=0; @@ -1585,7 +1582,7 @@ void CL_SendCmd (double frametime, qboolean mainloop) CL_BaseMove (cmd, plnum, 1, 1); // allow mice or other external controllers to add to the move - IN_Move (mousemovements[plnum], plnum); + IN_Move (mousemovements[plnum], plnum, frametime); independantphysics[plnum].forwardmove += mousemovements[plnum][0]; independantphysics[plnum].sidemove += mousemovements[plnum][1]; independantphysics[plnum].upmove += mousemovements[plnum][2]; @@ -1620,7 +1617,7 @@ void CL_SendCmd (double frametime, qboolean mainloop) cls.netchan.outgoing_sequence = cl.movesequence; } - IN_Move (NULL, 0); + IN_Move (NULL, 0, frametime); return; // sendcmds come from the demo } @@ -1696,7 +1693,7 @@ void CL_SendCmd (double frametime, qboolean mainloop) { // CL_BaseMove (&independantphysics[plnum], plnum, (msecstouse - independantphysics[plnum].msec), wantfps); CL_AdjustAngles (plnum, frametime); - IN_Move (mousemovements[plnum], plnum); + IN_Move (mousemovements[plnum], plnum, frametime); CL_ClampPitch(plnum); independantphysics[plnum].forwardmove += mousemovements[plnum][0]; independantphysics[plnum].sidemove += mousemovements[plnum][1]; diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 81af0d7e..54df0659 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -113,7 +113,7 @@ cvar_t skin = CVARF("skin", "", CVAR_ARCHIVE | CVAR_USERINFO); cvar_t model = CVARF("model", "", CVAR_ARCHIVE | CVAR_USERINFO); cvar_t topcolor = CVARF("topcolor", "", CVAR_ARCHIVE | CVAR_USERINFO); cvar_t bottomcolor = CVARF("bottomcolor", "", CVAR_ARCHIVE | CVAR_USERINFO); -cvar_t rate = CVARFD("rate", "10000"/*"6480"*/, CVAR_ARCHIVE | CVAR_USERINFO, "A rough measure of the bandwidth to try to use while playing. Too high a value may result in 'buffer bloat'."); +cvar_t rate = CVARFD("rate", "30000"/*"6480"*/, CVAR_ARCHIVE | CVAR_USERINFO, "A rough measure of the bandwidth to try to use while playing. Too high a value may result in 'buffer bloat'."); cvar_t drate = CVARFD("drate", "100000", CVAR_ARCHIVE | CVAR_USERINFO, "A rough measure of the bandwidth to try to use while downloading."); // :) cvar_t noaim = CVARF("noaim", "", CVAR_ARCHIVE | CVAR_USERINFO); cvar_t msg = CVARFD("msg", "1", CVAR_ARCHIVE | CVAR_USERINFO, "Filter console prints/messages. Only functions on QuakeWorld servers. 0=pickup messages. 1=death messages. 2=critical messages. 3=chat."); @@ -170,7 +170,7 @@ cvar_t ruleset_allow_particle_lightning = CVAR("ruleset_allow_particle_lightning cvar_t ruleset_allow_overlongsounds = CVAR("ruleset_allow_overlong_sounds", "1"); cvar_t ruleset_allow_larger_models = CVAR("ruleset_allow_larger_models", "1"); cvar_t ruleset_allow_modified_eyes = CVAR("ruleset_allow_modified_eyes", "0"); -cvar_t ruleset_allow_sensative_texture_replacements = CVAR("ruleset_allow_sensative_texture_replacements", "1"); +cvar_t ruleset_allow_sensitive_texture_replacements = CVAR("ruleset_allow_sensitive_texture_replacements", "1"); cvar_t ruleset_allow_localvolume = CVAR("ruleset_allow_localvolume", "1"); cvar_t ruleset_allow_shaders = CVARF("ruleset_allow_shaders", "1", CVAR_SHADERSYSTEM); cvar_t ruleset_allow_fbmodels = CVARF("ruleset_allow_fbmodels", "1", CVAR_SHADERSYSTEM); @@ -218,9 +218,20 @@ unsigned int cl_maxstrisvert; unsigned int cl_numstris; unsigned int cl_maxstris; -double connect_time = -1; // for connection retransmits -int connect_defaultport = 0; -int connect_tries = 0; //increased each try, every fourth trys nq connect packets. +static struct +{ + qboolean trying; + qboolean istransfer; //ignore the user's desired server (don't change connect.adr). + netadr_t adr; //address that we're trying to transfer to. + int mtu; + qboolean compress; + int protocol; //tracked as part of guesswork based upon what replies we get. + int challenge; //tracked as part of guesswork based upon what replies we get. + double time; //for connection retransmits + int defaultport; + int tries; //increased each try, every fourth trys nq connect packets. + unsigned char guid[64]; +} connectinfo; quakeparms_t host_parms; @@ -343,7 +354,7 @@ void CL_ConnectToDarkPlaces(char *challenge, netadr_t *adr) cls.resendinfo = false; - connect_time = realtime; // for retransmit requests + connectinfo.time = realtime; // for retransmit requests Q_snprintfz(data, sizeof(data), "%c%c%c%cconnect\\protocol\\darkplaces 3\\challenge\\%s", 255, 255, 255, 255, challenge); @@ -383,6 +394,10 @@ char *CL_GUIDString(netadr_t *adr) char serveraddr[256]; void *blocks[2]; int lens[2]; + + if (*connectinfo.guid && connectinfo.istransfer) + return connectinfo.guid; + if (!buflen) { vfsfile_t *f; @@ -420,7 +435,8 @@ char *CL_GUIDString(netadr_t *adr) blocks[1] = serveraddr;lens[1] = strlen(serveraddr); Com_BlocksChecksum(2, blocks, lens, (void*)digest); - return va("%08x%08x%08x%08x", digest[0], digest[1], digest[2], digest[3]); + Q_snprintfz(connectinfo.guid, sizeof(connectinfo.guid), "%08x%08x%08x%08x", digest[0], digest[1], digest[2], digest[3]); + return connectinfo.guid; } /* @@ -430,7 +446,7 @@ CL_SendConnectPacket called by CL_Connect_f and CL_CheckResend ====================== */ -void CL_SendConnectPacket (int mtu, +void CL_SendConnectPacket (netadr_t *to, int mtu, #ifdef PROTOCOL_VERSION_FTE int ftepext, int ftepext2, #endif @@ -438,7 +454,7 @@ void CL_SendConnectPacket (int mtu, /*, ...*/) //dmw new parms { extern cvar_t qport; - netadr_t adr; + netadr_t addr; char data[2048]; char *info; double t1, t2; @@ -453,7 +469,7 @@ void CL_SendConnectPacket (int mtu, // Now, adds lookup time to the connect time. // Should I add it to realtime instead?!?! - if (cls.state != ca_disconnected) + if (!connectinfo.trying) return; if (cl_nopext.ival) //imagine it's an unenhanced server @@ -468,7 +484,7 @@ void CL_SendConnectPacket (int mtu, fteprotextsupported2 &= ftepext2; #ifdef Q2CLIENT - if (cls.protocol != CP_QUAKEWORLD) + if (connectinfo.protocol != CP_QUAKEWORLD) fteprotextsupported = 0; #endif @@ -478,20 +494,24 @@ void CL_SendConnectPacket (int mtu, t1 = Sys_DoubleTime (); - if (!NET_StringToAdr (cls.servername, PORT_QWSERVER, &adr)) + if (!to) { - Con_TPrintf ("Bad server address\n"); - connect_time = -1; - return; + to = &addr; + if (!NET_StringToAdr (cls.servername, PORT_QWSERVER, to)) + { + Con_TPrintf ("Bad server address\n"); + connectinfo.trying = false; + return; + } } - NET_AdrToString(data, sizeof(data), &adr); + NET_AdrToString(data, sizeof(data), to); Cvar_ForceSet(&cl_serveraddress, data); - if (!NET_IsClientLegal(&adr)) + if (!NET_IsClientLegal(to)) { Con_TPrintf ("Illegal server address\n"); - connect_time = -1; + connectinfo.trying = false; return; } @@ -499,7 +519,7 @@ void CL_SendConnectPacket (int mtu, cls.resendinfo = false; - connect_time = realtime+t2-t1; // for retransmit requests + connectinfo.time = realtime+t2-t1; // for retransmit requests cls.qport = qport.value; Cvar_SetValue(&qport, (cls.qport+1)&0xffff); @@ -521,14 +541,14 @@ void CL_SendConnectPacket (int mtu, clients = MAX_SPLITS; #ifdef Q2CLIENT - if (cls.protocol == CP_QUAKE2) //sorry - too lazy. + if (connectinfo.protocol == CP_QUAKE2) //sorry - too lazy. clients = 1; #endif #ifdef Q3CLIENT - if (cls.protocol == CP_QUAKE3) + if (connectinfo.protocol == CP_QUAKE3) { //q3 requires some very strange things. - CLQ3_SendConnectPacket(&adr); + CLQ3_SendConnectPacket(to); return; } #endif @@ -539,17 +559,17 @@ void CL_SendConnectPacket (int mtu, Q_strncatz(data, va("%i", clients), sizeof(data)); #ifdef Q2CLIENT - if (cls.protocol == CP_QUAKE2) + if (connectinfo.protocol == CP_QUAKE2) Q_strncatz(data, va(" %i", PROTOCOL_VERSION_Q2), sizeof(data)); else #endif Q_strncatz(data, va(" %i", PROTOCOL_VERSION_QW), sizeof(data)); - Q_strncatz(data, va(" %i %i", cls.qport, cls.challenge), sizeof(data)); + Q_strncatz(data, va(" %i %i", cls.qport, connectinfo.challenge), sizeof(data)); //userinfo 0 + zquake extension info. - if (cls.protocol == CP_QUAKEWORLD) + if (connectinfo.protocol == CP_QUAKEWORLD) Q_strncatz(data, va(" \"%s\\*z_ext\\%i\"", cls.userinfo[0], SUPPORTED_Z_EXTENSIONS), sizeof(data)); else Q_strncatz(data, va(" \"%s\"", cls.userinfo[0]), sizeof(data)); @@ -571,41 +591,39 @@ void CL_SendConnectPacket (int mtu, if (mtu > 0) { - if (adr.type == NA_LOOPBACK) + if (to->type == NA_LOOPBACK) mtu = MAX_UDP_PACKET; else if (net_mtu.ival > 64 && mtu > net_mtu.ival) mtu = net_mtu.ival; mtu &= ~7; Q_strncatz(data, va("0x%x %i\n", PROTOCOL_VERSION_FRAGMENT, mtu), sizeof(data)); - cls.netchan.fragmentsize = mtu; + connectinfo.mtu = mtu; } else - cls.netchan.fragmentsize = 0; + connectinfo.mtu = 0; #ifdef HUFFNETWORK if (compressioncrc && Huff_CompressionCRC(compressioncrc)) { Q_strncatz(data, va("0x%x 0x%x\n", PROTOCOL_VERSION_HUFFMAN, LittleLong(compressioncrc)), sizeof(data)); - cls.netchan.compress = true; + connectinfo.compress = true; } else #endif - cls.netchan.compress = false; + connectinfo.compress = false; - info = CL_GUIDString(&adr); + info = CL_GUIDString(to); if (info) Q_strncatz(data, va("0x%x \"%s\"\n", PROTOCOL_INFO_GUID, info), sizeof(data)); - NET_SendPacket (NS_CLIENT, strlen(data), data, &adr); + NET_SendPacket (NS_CLIENT, strlen(data), data, to); cl.splitclients = 0; } char *CL_TryingToConnect(void) { - if (connect_time == -1) - return NULL; - if (cls.state != ca_disconnected) + if (!connectinfo.trying) return NULL; return cls.servername; @@ -626,20 +644,22 @@ Resend a connect message if the last one has timed out */ void CL_CheckForResend (void) { - netadr_t adr; char data[2048]; double t1, t2; int contype = 0; qboolean keeptrying = true; #ifndef CLIENTONLY - if (!cls.state && sv.state) + if (!cls.state && (!connectinfo.trying || sv.state != ss_clustermode) && sv.state) { unsigned int pext1, pext2; pext1 = 0; pext2 = 0; + connectinfo.trying = true; + connectinfo.istransfer = false; Q_strncpyz (cls.servername, "internalserver", sizeof(cls.servername)); Cvar_ForceSet(&cl_servername, cls.servername); + NET_StringToAdr(cls.servername, 0, &connectinfo.adr); cls.state = ca_disconnected; switch (svs.gametype) @@ -725,16 +745,17 @@ void CL_CheckForResend (void) CL_FlushClientCommands(); //clear away all client->server clientcommands. - if (cls.protocol == CP_NETQUAKE) + connectinfo.protocol = cls.protocol; + if (connectinfo.protocol == CP_NETQUAKE) { - if (!NET_StringToAdr (cls.servername, connect_defaultport, &adr)) + if (!NET_StringToAdr (cls.servername, connectinfo.defaultport, &connectinfo.adr)) { Con_TPrintf ("Bad server address\n"); - connect_time = -1; + connectinfo.trying = false; SCR_EndLoadingPlaque(); return; } - NET_AdrToString(data, sizeof(data), &adr); + NET_AdrToString(data, sizeof(data), &connectinfo.adr); /*eat up the server's packets, to clear any lingering loopback packets (like disconnect commands... yes this might cause packetloss for other clients)*/ while(NET_GetPacket (NS_SERVER, 0) >= 0) @@ -745,38 +766,37 @@ void CL_CheckForResend (void) if (cls.protocol_nq == CPNQ_ID) { - net_from = adr; + net_from = connectinfo.adr; Cmd_TokenizeString (va("connect %i %i %i \"\\name\\unconnected\"", NET_PROTOCOL_VERSION, 0, SV_NewChallenge()), false, false); SVC_DirectConnect(); } else if (cls.protocol_nq == CPNQ_FITZ666) { - net_from = adr; + net_from = connectinfo.adr; Cmd_TokenizeString (va("connect %i %i %i \"\\name\\unconnected\\mod\\666\"", NET_PROTOCOL_VERSION, 0, SV_NewChallenge()), false, false); SVC_DirectConnect(); } else if (cls.protocol_nq == CPNQ_PROQUAKE3_4) { - net_from = adr; + net_from = connectinfo.adr; Cmd_TokenizeString (va("connect %i %i %i \"\\name\\unconnected\\mod\\1\"", NET_PROTOCOL_VERSION, 0, SV_NewChallenge()), false, false); SVC_DirectConnect(); } else - CL_ConnectToDarkPlaces("", &adr); + CL_ConnectToDarkPlaces("", &connectinfo.adr); } else - CL_SendConnectPacket (8192-16, pext1, pext2, false); + CL_SendConnectPacket (NULL, 8192-16, pext1, pext2, false); return; } #endif - if (connect_time == -1) - return; - if (cls.state != ca_disconnected) + if (!connectinfo.trying) return; + /* #ifdef NQPROT if (connect_type) @@ -793,22 +813,25 @@ void CL_CheckForResend (void) } #endif */ - if (connect_time && realtime - connect_time < 5.0) + if (connectinfo.time && realtime - connectinfo.time < 5.0) return; t1 = Sys_DoubleTime (); - if (!NET_StringToAdr (cls.servername, connect_defaultport, &adr)) + if (!connectinfo.istransfer) { - Con_TPrintf ("Bad server address\n"); - connect_time = -1; - SCR_EndLoadingPlaque(); - return; + if (!NET_StringToAdr (cls.servername, connectinfo.defaultport, &connectinfo.adr)) + { + Con_TPrintf ("Bad server address\n"); + connectinfo.trying = false; + SCR_EndLoadingPlaque(); + return; + } } - if (!NET_IsClientLegal(&adr)) + if (!NET_IsClientLegal(&connectinfo.adr)) { Con_TPrintf ("Illegal server address\n"); SCR_EndLoadingPlaque(); - connect_time = -1; + connectinfo.trying = false; return; } @@ -816,7 +839,7 @@ void CL_CheckForResend (void) t2 = Sys_DoubleTime (); - connect_time = realtime+t2-t1; // for retransmit requests + connectinfo.time = realtime+t2-t1; // for retransmit requests Cvar_ForceSet(&cl_servername, cls.servername); @@ -824,16 +847,19 @@ void CL_CheckForResend (void) //Q3 clients send their cdkey to the q3 authorize server. //they send this packet with the challenge. //and the server will refuse the client if it hasn't sent it. - CLQ3_SendAuthPacket(&adr); + CLQ3_SendAuthPacket(&connectinfo.adr); #endif - Con_TPrintf ("Connecting to %s...\n", cls.servername); + if (connectinfo.istransfer) + Con_TPrintf ("Connecting to %s(%s)...\n", cls.servername, NET_AdrToString(data, sizeof(data), &connectinfo.adr)); + else + Con_TPrintf ("Connecting to %s...\n", cls.servername); - if (connect_tries == 0) + if (connectinfo.tries == 0) if (!NET_EnsureRoute(cls.sockets, "conn", cls.servername, false)) { Con_Printf ("Unable to establish connection to %s\n", cls.servername); - connect_time = -1; + connectinfo.trying = false; SCR_EndLoadingPlaque(); return; } @@ -846,7 +872,7 @@ void CL_CheckForResend (void) if (contype & 1) { Q_snprintfz (data, sizeof(data), "%c%c%c%cgetchallenge\n", 255, 255, 255, 255); - keeptrying &= NET_SendPacket (NS_CLIENT, strlen(data), data, &adr); + keeptrying &= NET_SendPacket (NS_CLIENT, strlen(data), data, &connectinfo.adr); } /*NQ*/ #ifdef NQPROT @@ -878,16 +904,16 @@ void CL_CheckForResend (void) MSG_WriteString(&sb, "getchallenge"); *(int*)sb.data = LongSwap(NETFLAG_CTL | sb.cursize); - keeptrying &= NET_SendPacket (NS_CLIENT, sb.cursize, sb.data, &adr); + keeptrying &= NET_SendPacket (NS_CLIENT, sb.cursize, sb.data, &connectinfo.adr); } #endif - connect_tries++; + connectinfo.tries++; if (!keeptrying) { Con_TPrintf ("No route to host, giving up\n"); - connect_time = -1; + connectinfo.trying = false; SCR_EndLoadingPlaque(); } } @@ -896,11 +922,11 @@ void CL_BeginServerConnect(int port) { if (!port) port = cl_defaultport.value; - cls.protocol = CP_UNKNOWN; + memset(&connectinfo, 0, sizeof(connectinfo)); + connectinfo.trying = true; + connectinfo.defaultport = port; + connectinfo.protocol = CP_UNKNOWN; SCR_SetLoadingStage(LS_CONNECTION); - connect_time = 0; - connect_defaultport = port; - connect_tries = 0; CL_CheckForResend(); } @@ -913,8 +939,48 @@ void CL_BeginServerReconnect(void) return; } #endif - connect_time = 0; - CL_CheckForResend(); + connectinfo.trying = true; + connectinfo.istransfer = false; + connectinfo.time = 0; +} + +void CL_Transfer_f(void) +{ + char oldguid[64]; + char *server; + if (Cmd_Argc() != 2) + { + Con_TPrintf ("usage: cl_transfer \n"); + return; + } + + server = Cmd_Argv (1); + if (!*server) + { + //if they didn't specify a server, abort any active transfer/connection. + connectinfo.trying = false; + return; + } + + Q_strncpyz(oldguid, connectinfo.guid, sizeof(oldguid)); + memset(&connectinfo, 0, sizeof(connectinfo)); + if (NET_StringToAdr(server, 0, &connectinfo.adr)) + { + if (cls.state) + { + connectinfo.istransfer = true; + Q_strncpyz(connectinfo.guid, oldguid, sizeof(oldguid)); //retain the same guid on transfers + } + connectinfo.trying = true; + connectinfo.defaultport = cl_defaultport.value; + connectinfo.protocol = CP_UNKNOWN; + SCR_SetLoadingStage(LS_CONNECTION); + CL_CheckForResend(); + } + else + { + Con_Printf("cl_transfer: bad address\n"); + } } /* ================ @@ -934,7 +1000,12 @@ void CL_Connect_f (void) server = Cmd_Argv (1); - CL_Disconnect_f (); +#ifndef CLIENTONLY + if (sv.state == ss_clustermode) + CL_Disconnect (); + else +#endif + CL_Disconnect_f (); Q_strncpyz (cls.servername, server, sizeof(cls.servername)); CL_BeginServerConnect(0); @@ -1036,7 +1107,10 @@ void CL_IRCConnect_f (void) #ifdef TCPCONNECT void CL_TCPConnect_f (void) { - Cbuf_InsertText(va("connect tcp://%s", Cmd_Argv(1)), Cmd_ExecLevel, true); + if (!Q_strcasecmp(Cmd_Argv(0), "tlsconnect")) + Cbuf_InsertText(va("connect tls://%s", Cmd_Argv(1)), Cmd_ExecLevel, true); + else + Cbuf_InsertText(va("connect tcp://%s", Cmd_Argv(1)), Cmd_ExecLevel, true); } #endif @@ -1109,6 +1183,44 @@ void CL_Rcon_f (void) NET_SendPacket (NS_CLIENT, strlen(message)+1, message, &to); } +void CL_BlendFog(fogstate_t *result, fogstate_t *oldf, float time, fogstate_t *newf) +{ + float nfrac, ofrac; + if (time >= newf->time) + nfrac = 1; + else if (time < oldf->time) + nfrac = 0; + else + nfrac = (time - oldf->time) / (newf->time - oldf->time); + ofrac = 1 - nfrac; + + FloatInterpolate(oldf->alpha, nfrac, newf->alpha, result->alpha); + FloatInterpolate(oldf->depthbias, nfrac, newf->depthbias, result->depthbias); + FloatInterpolate(oldf->density, nfrac, newf->density, result->density); //this should be non-linear, but that sort of maths is annoying. + VectorInterpolate(oldf->colour, nfrac, newf->colour, result->colour); + + result->time = time; +} +void CL_ResetFog(void) +{ + //blend from the current state, not the old state. this means things work properly if we've not reached the new state yet. + CL_BlendFog(&cl.oldfog, &cl.oldfog, realtime, &cl.fog); + + //reset the new state to defaults, to be filled in by the caller. + memset(&cl.fog, 0, sizeof(cl.fog)); + cl.fog.time = realtime; + cl.fog.density = 0; + cl.fog.colour[0] = 0.3; + cl.fog.colour[1] = 0.3; + cl.fog.colour[2] = 0.3; + cl.fog.alpha = 1; + cl.fog.depthbias = 0; + /* + cl.fog.end = 16384; + cl.fog.height = 1<<30; + cl.fog.fadedepth = 128; + */ +} /* ===================== @@ -1142,8 +1254,10 @@ void CL_ClearState (void) Con_DPrintf ("Clearing memory\n"); if (!serverrunning || !tolocalserver) { - if (serverrunning) +#ifndef CLIENTONLY + if (serverrunning && sv.state != ss_clustermode) SV_UnspawnServer(); +#endif Mod_ClearAll (); Cvar_ApplyLatches(CVAR_LATCH); @@ -1154,7 +1268,7 @@ void CL_ClearState (void) CL_ClearCustomTEnts(); Surf_ClearLightmaps(); T_FreeInfoStrings(); - SCR_ShowPic_Clear(); + SCR_ShowPic_Clear(false); if (cl.playerview[0].playernum == -1) { //left over from q2 connect. @@ -1192,10 +1306,7 @@ void CL_ClearState (void) // wipe the entire cl structure memset (&cl, 0, sizeof(cl)); - cl.fog_density = 0; - cl.fog_colour[0] = 0.3; - cl.fog_colour[1] = 0.3; - cl.fog_colour[2] = 0.3; + CL_ResetFog(); cl.allocated_client_slots = QWMAX_CLIENTS; #ifndef CLIENTONLY @@ -1241,8 +1352,7 @@ void CL_Disconnect (void) { qbyte final[12]; - connect_time = -1; - connect_tries = 0; + connectinfo.trying = false; SCR_SetLoadingStage(0); @@ -1308,7 +1418,7 @@ void CL_Disconnect (void) #ifndef CLIENTONLY //running a server, and it's our own - if (serverrunning && !tolocalserver) + if (serverrunning && !tolocalserver && sv.state != ss_clustermode) SV_UnspawnServer(); #endif } @@ -1528,7 +1638,7 @@ void CL_Color_f (void) #endif } -qboolean CL_CheckDLFile(char *filename); +qboolean CL_CheckDLFile(const char *filename); void CL_PakDownloads(int mode) { /* @@ -2315,9 +2425,48 @@ void CL_ConnectionlessPacket (void) Con_Printf ("%s: ", NET_AdrToString (adr, sizeof(adr), &net_from)); // Con_DPrintf ("%s", net_message.data + 4); + if (c == 'f') //using 'f' as a prefix so that I don't need lots of hacks + { + s = MSG_ReadStringLine (); + if (!strcmp(s, "redir")) + { + netadr_t adr; + char *data = MSG_ReadStringLine(); + Con_TPrintf ("redirect to %s\n", data); + NET_StringToAdr(data, 27500, &adr); + data = "\xff\xff\xff\xffgetchallenge\n"; + connectinfo.istransfer = true; + connectinfo.adr = adr; + NET_SendPacket (NS_CLIENT, strlen(data), data, &adr); + return; + } + else if (!strcmp(s, "reject")) + { //generic rejection. stop trying. + char *data = MSG_ReadStringLine(); + Con_Printf ("reject\n%s\n", s); + connectinfo.trying = false; + return; + } + else if (!strcmp(s, "badname")) + { //rejected purely because of player name + Con_Printf ("f%s\n", s); + connectinfo.trying = false; + return; + } + else if (!strcmp(s, "badaccount")) + { //rejected because username or password is wrong + Con_Printf ("f%s\n", s); + connectinfo.trying = false; + return; + } + else + Con_Printf ("f%s", s); + } + if (c == S2C_CHALLENGE) { static unsigned int lasttime = 0xdeadbeef; + netadr_t lastadr; unsigned int curtime = Sys_Milliseconds(); unsigned long pext = 0, pext2 = 0, huffcrc=0, mtu=0; Con_TPrintf ("challenge\n"); @@ -2328,21 +2477,21 @@ void CL_ConnectionlessPacket (void) { /*Quake3*/ #ifdef Q3CLIENT - if (cls.protocol == CP_QUAKE3 || cls.protocol == CP_UNKNOWN) + if (connectinfo.protocol == CP_QUAKE3 || connectinfo.protocol == CP_UNKNOWN) { /*throttle*/ if (curtime - lasttime < 500) return; lasttime = curtime; - cls.protocol = CP_QUAKE3; - cls.challenge = atoi(s+17); - CL_SendConnectPacket (0, 0, 0, 0/*, ...*/); + connectinfo.protocol = CP_QUAKE3; + connectinfo.challenge = atoi(s+17); + CL_SendConnectPacket (&net_from, 0, 0, 0, 0/*, ...*/); } else { - Con_Printf("\nChallenge from another protocol, ignoring Q3 challenge\n"); - return; + Con_Printf("\nChallenge from another protocol, ignoring Q3 challenge\n"); + return; } return; #else @@ -2363,14 +2512,14 @@ void CL_ConnectionlessPacket (void) if (*s2 && *s2 != ' ') {//and if it's not, we're unlikly to be compatible with whatever it is that's talking at us. #ifdef NQPROT - if (cls.protocol == CP_NETQUAKE || cls.protocol == CP_UNKNOWN) + if (connectinfo.protocol == CP_NETQUAKE || connectinfo.protocol == CP_UNKNOWN) { /*throttle*/ if (curtime - lasttime < 500) return; lasttime = curtime; - cls.protocol = CP_NETQUAKE; + connectinfo.protocol = CP_NETQUAKE; CL_ConnectToDarkPlaces(s+9, &net_from); } else @@ -2382,8 +2531,8 @@ void CL_ConnectionlessPacket (void) } #ifdef Q2CLIENT - if (cls.protocol == CP_QUAKE2 || cls.protocol == CP_UNKNOWN) - cls.protocol = CP_QUAKE2; + if (connectinfo.protocol == CP_QUAKE2 || connectinfo.protocol == CP_UNKNOWN) + connectinfo.protocol = CP_QUAKE2; else { Con_Printf("\nChallenge from another protocol, ignoring Q2 challenge\n"); @@ -2397,34 +2546,35 @@ void CL_ConnectionlessPacket (void) #ifdef Q3CLIENT else if (!strcmp(com_token, "onnectResponse")) { - cls.protocol = CP_QUAKE3; + connectinfo.protocol = CP_QUAKE3; goto client_connect; } #endif #ifdef Q2CLIENT else if (!strcmp(com_token, "lient_connect")) { - cls.protocol = CP_QUAKE2; + connectinfo.protocol = CP_QUAKE2; goto client_connect; } #endif /*no idea, assume a QuakeWorld challenge response ('c' packet)*/ - else if (cls.protocol == CP_QUAKEWORLD || cls.protocol == CP_UNKNOWN) - cls.protocol = CP_QUAKEWORLD; + else if (connectinfo.protocol == CP_QUAKEWORLD || connectinfo.protocol == CP_UNKNOWN) + connectinfo.protocol = CP_QUAKEWORLD; else { Con_Printf("\nChallenge from another protocol, ignoring QW challenge\n"); return; } - /*throttle*/ - if (curtime - lasttime < 500) + /*throttle connect requests*/ + if (curtime - lasttime < 500 && NET_CompareAdr(&net_from, &lastadr)) return; lasttime = curtime; + lastadr = net_from; - cls.challenge = atoi(s); + connectinfo.challenge = atoi(s); for(;;) { @@ -2453,11 +2603,11 @@ void CL_ConnectionlessPacket (void) else MSG_ReadLong (); } - CL_SendConnectPacket (mtu, pext, pext2, huffcrc/*, ...*/); + CL_SendConnectPacket (&net_from, mtu, pext, pext2, huffcrc/*, ...*/); return; } #ifdef Q2CLIENT - if (cls.protocol == CP_QUAKE2) + if (connectinfo.protocol == CP_QUAKE2) { char *nl; msg_readcount--; @@ -2481,7 +2631,7 @@ void CL_ConnectionlessPacket (void) } else if (!strcmp(s, "client_connect")) { - cls.protocol = CP_QUAKE2; + connectinfo.protocol = CP_QUAKE2; goto client_connect; } else if (!strcmp(s, "disconnect")) @@ -2524,6 +2674,8 @@ void CL_ConnectionlessPacket (void) cls.netchan.isnqprotocol = true; cls.protocol = CP_NETQUAKE; cls.protocol_nq = CPNQ_ID; + cls.challenge = connectinfo.challenge; + connectinfo.trying = false; cls.demonum = -1; // not in the demo loop now cls.state = ca_connected; @@ -2548,25 +2700,42 @@ void CL_ConnectionlessPacket (void) if (c == S2C_CONNECTION) { - int compress; - int mtu; - cls.protocol = CP_QUAKEWORLD; + connectinfo.protocol = CP_QUAKEWORLD; #ifdef Q2CLIENT client_connect: //fixme: make function #endif - if (net_from.type != NA_LOOPBACK) - Con_TPrintf ("connection\n"); - if (cls.state >= ca_connected) + + if (!NET_CompareAdr(&connectinfo.adr, &net_from)) { - if (cls.demoplayback == DPB_NONE) - Con_TPrintf ("Dup connect received. Ignored.\n"); + if (net_from.type != NA_LOOPBACK) + Con_TPrintf ("ignoring connection\n"); return; } - compress = cls.netchan.compress; - mtu = cls.netchan.fragmentsize; + if (net_from.type != NA_LOOPBACK) + Con_TPrintf ("connection\n"); + + if (cls.state >= ca_connected) + { + if (!NET_CompareAdr(&cls.netchan.remote_address, &net_from)) + { +#ifndef CLIENTONLY + if (sv.state != ss_clustermode) +#endif + CL_Disconnect_f(); + } + else + { + if (cls.demoplayback == DPB_NONE) + Con_TPrintf ("Dup connect received. Ignored.\n"); + return; + } + } + connectinfo.trying = false; + cls.protocol = connectinfo.protocol; + cls.challenge = connectinfo.challenge; Netchan_Setup (NS_CLIENT, &cls.netchan, &net_from, cls.qport); - cls.netchan.fragmentsize = mtu; - cls.netchan.compress = compress; + cls.netchan.fragmentsize = connectinfo.mtu; + cls.netchan.compress = connectinfo.compress; CL_ParseEstablished(); #ifdef Q3CLIENT if (cls.protocol != CP_QUAKE3) @@ -2596,7 +2765,7 @@ client_connect: //fixme: make function Con_TPrintf ("Command packet from remote host. Ignored.\n"); return; } -#ifdef _WIN32 +#if defined(_WIN32) && !defined(WINRT) ShowWindow (mainwindow, SW_RESTORE); SetForegroundWindow (mainwindow); #endif @@ -3072,7 +3241,7 @@ void CL_ForceStopDownload (qboolean finish) if (strncmp(tempname,"skins/",6)) FS_Remove(tempname, FS_GAME); else - FS_Remove(tempname+6, FS_SKINS); + FS_Remove(tempname, FS_PUBBASEGAMEONLY); } *cls.downloadlocalname = '\0'; *cls.downloadremotename = '\0'; @@ -3092,13 +3261,8 @@ void CL_FinishDownload_f (void) CL_ForceStopDownload(true); } -#ifdef _WIN32 - -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#endif - -#include +#if defined(_WIN32) && !defined(WINRT) +#include "winquake.h" /* ================= CL_Minimize_f @@ -3148,24 +3312,54 @@ void CL_FTP_f(void) void CL_Fog_f(void) { - if (cl.fog_locked && !Cmd_FromGamecode()) - Con_Printf("Current fog %f (r:%f g:%f b:%f)\n", cl.fog_density, cl.fog_colour[0], cl.fog_colour[1], cl.fog_colour[2]); - else if (Cmd_Argc() <= 1) - { - Con_Printf("Current fog %f (r:%f g:%f b:%f)\n", cl.fog_density, cl.fog_colour[0], cl.fog_colour[1], cl.fog_colour[2]); - } + if ((cl.fog_locked && !Cmd_FromGamecode()) || Cmd_Argc() <= 1) + Con_Printf("Current fog %f (r:%f g:%f b:%f)\n", cl.fog.density, cl.fog.colour[0], cl.fog.colour[1], cl.fog.colour[2]); else { - cl.fog_density = atof(Cmd_Argv(1)); - if (Cmd_Argc() >= 5) + CL_ResetFog(); + + switch(Cmd_Argc()) { - cl.fog_colour[0] = atof(Cmd_Argv(2)); - cl.fog_colour[1] = atof(Cmd_Argv(3)); - cl.fog_colour[2] = atof(Cmd_Argv(4)); + case 1: + break; + case 2: + cl.fog.density = atof(Cmd_Argv(1)); + break; + case 3: + cl.fog.density = atof(Cmd_Argv(1)); + cl.fog.colour[0] = cl.fog.colour[1] = cl.fog.colour[2] = atof(Cmd_Argv(2)); + break; + case 4: + cl.fog.density = 0.05; //make something up for vauge compat with fitzquake, so it doesn't get the default of 0 + cl.fog.colour[0] = atof(Cmd_Argv(1)); + cl.fog.colour[1] = atof(Cmd_Argv(2)); + cl.fog.colour[2] = atof(Cmd_Argv(3)); + break; + case 5: + default: + cl.fog.density = atof(Cmd_Argv(1)); + cl.fog.colour[0] = atof(Cmd_Argv(2)); + cl.fog.colour[1] = atof(Cmd_Argv(3)); + cl.fog.colour[2] = atof(Cmd_Argv(4)); + break; } + if (cls.state == ca_active) + cl.fog.time += 1; + + //fitz: + //if (Cmd_Argc() >= 6) cl.fog_time = atof(Cmd_Argv(5)); + //dp: + if (Cmd_Argc() >= 6) cl.fog.alpha = atof(Cmd_Argv(5)); + if (Cmd_Argc() >= 7) cl.fog.depthbias = atof(Cmd_Argv(6)); + /* + if (Cmd_Argc() >= 8) cl.fog.end = atof(Cmd_Argv(7)); + if (Cmd_Argc() >= 9) cl.fog.height = atof(Cmd_Argv(8)); + if (Cmd_Argc() >= 10) cl.fog.fadedepth = atof(Cmd_Argv(9)); + */ + if (Cmd_FromGamecode()) - cl.fog_locked = !!cl.fog_density; + cl.fog_locked = !!cl.fog.density; } } @@ -3175,7 +3369,6 @@ void CL_CrashMeEndgame_f(void) } void CL_Skygroup_f(void); -void SCR_ShowPic_Script_f(void); /* ================= CL_Init @@ -3358,7 +3551,7 @@ void CL_Init (void) Cvar_Register (&ruleset_allow_overlongsounds, cl_controlgroup); Cvar_Register (&ruleset_allow_larger_models, cl_controlgroup); Cvar_Register (&ruleset_allow_modified_eyes, cl_controlgroup); - Cvar_Register (&ruleset_allow_sensative_texture_replacements, cl_controlgroup); + Cvar_Register (&ruleset_allow_sensitive_texture_replacements, cl_controlgroup); Cvar_Register (&ruleset_allow_localvolume, cl_controlgroup); Cvar_Register (&ruleset_allow_shaders, cl_controlgroup); Cvar_Register (&ruleset_allow_fbmodels, cl_controlgroup); @@ -3369,7 +3562,7 @@ void CL_Init (void) // Cmd_AddCommand ("ftp", CL_FTP_f); //#endif - Cmd_AddCommand ("changing", CL_Changing_f); + Cmd_AddCommandD ("changing", CL_Changing_f, "Part of network protocols. This command should not be used manually."); Cmd_AddCommand ("disconnect", CL_Disconnect_f); Cmd_AddCommand ("record", CL_Record_f); Cmd_AddCommand ("rerecord", CL_ReRecord_f); @@ -3382,7 +3575,7 @@ void CL_Init (void) Cmd_AddCommand ("timedemo", CL_TimeDemo_f); Cmd_AddCommand ("crashme_endgame", CL_CrashMeEndgame_f); - Cmd_AddCommand ("showpic", SCR_ShowPic_Script_f); + Cmd_AddCommandD ("showpic", SCR_ShowPic_Script_f, "showpic [width] [height] [touchcommand]\nDisplays an image onscreen."); Cmd_AddCommand ("startdemos", CL_Startdemos_f); Cmd_AddCommand ("demos", CL_Demos_f); @@ -3393,15 +3586,30 @@ void CL_Init (void) Cmd_AddCommand ("quit", CL_Quit_f); - Cmd_AddCommand ("connect", CL_Connect_f); + Cmd_AddCommandD ("connect", CL_Connect_f, "connect scheme://address:port\nConnect to a server. Use a scheme of tcp:// or tls:// to connect via non-udp protocols." +#if defined(NQPROT) || defined(Q2CLIENT) || defined(Q3CLIENT) + "\nDefault port is port 27500." +#ifdef NQPROT + " NQ:"STRINGIFY(PORT_NQSERVER)"." +#endif + " QW:"STRINGIFY(PORT_QWSERVER)"." +#ifdef Q2CLIENT + " Q2:"STRINGIFY(PORT_Q2SERVER)"." +#endif +#ifdef Q3CLIENT + " Q3:"STRINGIFY(PORT_Q3SERVER)"." +#endif +#endif + ); + Cmd_AddCommandD ("cl_transfer", CL_Transfer_f, "Connect to a different server, disconnecting from the current server only when the new server replies."); #ifdef TCPCONNECT - Cmd_AddCommand ("tcpconnect", CL_TCPConnect_f); + Cmd_AddCommandD ("tcpconnect", CL_TCPConnect_f, "Connect to a server using the tcp:// prefix"); #endif #ifdef IRCCONNECT Cmd_AddCommand ("ircconnect", CL_IRCConnect_f); #endif #ifdef NQPROT - Cmd_AddCommand ("nqconnect", CLNQ_Connect_f); + Cmd_AddCommandD ("nqconnect", CLNQ_Connect_f, "Connects to the specified server, defaulting to port "STRINGIFY(PORT_NQSERVER)"."); #endif Cmd_AddCommand ("reconnect", CL_Reconnect_f); Cmd_AddCommand ("join", CL_Join_f); @@ -3455,7 +3663,7 @@ void CL_Init (void) // // Windows commands // -#ifdef _WINDOWS +#if defined(_WIN32) && !defined(WINRT) Cmd_AddCommand ("windows", CL_Windows_f); #endif @@ -3849,7 +4057,7 @@ void Host_DoRunFile(hrf_t *f) char *fdata = BZ_Malloc(len+1); VFS_READ(f->srcfile, fdata, len); fdata[len] = 0; - man = FS_Manifest_Parse(fdata); + man = FS_Manifest_Parse(NULL, fdata); if (!man->updateurl) man->updateurl = Z_StrDup(f->fname); BZ_Free(fdata); @@ -3988,7 +4196,7 @@ void Host_DoRunFile(hrf_t *f) qboolean Host_RunFile(const char *fname, int nlen, vfsfile_t *file) { hrf_t *f; -#if defined(_WIN32) && !defined(FTE_SDL) +#if defined(_WIN32) && !defined(FTE_SDL) && !defined(WINRT) //win32 file urls are basically fucked, so defer to the windows api. char utf8[MAX_OSPATH*3]; if (nlen >= 7 && !strncmp(fname, "file://", 7)) @@ -4214,7 +4422,7 @@ double Host_Frame (double time) // resend a connection request if necessary if (cls.state == ca_disconnected) { - IN_Move(NULL, 0); + IN_Move(NULL, 0, time); CL_CheckForResend (); #ifdef VOICECHAT @@ -4223,6 +4431,8 @@ double Host_Frame (double time) } else { + if (connectinfo.trying) + CL_CheckForResend (); CL_SendCmd (cl.gamespeed?host_frametime/cl.gamespeed:host_frametime, true); if (cls.state == ca_onserver && cl.validsequence && cl.worldmodel) @@ -4381,6 +4591,9 @@ void CL_StartCinematicOrMenu(void) //and any startup cinematics #ifndef NOMEDIA +#ifndef CLIENTONLY + if (!sv.state) +#endif if (!cls.demoinfile && !cls.state && !Media_PlayingFullScreen()) { int ol_depth; @@ -4388,7 +4601,7 @@ void CL_StartCinematicOrMenu(void) int idroq_depth; idcin_depth = COM_FDepthFile("video/idlog.cin", true); //q2 - idroq_depth = COM_FDepthFile("video/idlogo.roq", true); //q2 + idroq_depth = COM_FDepthFile("video/idlogo.roq", true); //q3 ol_depth = COM_FDepthFile("video/openinglogos.roq", true); //jk2 if (ol_depth != 0x7fffffff && (ol_depth <= idroq_depth || ol_depth <= idcin_depth)) @@ -4416,7 +4629,7 @@ void CL_StartCinematicOrMenu(void) if (!sv.state) #endif { - if (qrenderer > QR_NONE) + if (qrenderer > QR_NONE && !m_state) M_ToggleMenu_f(); //Con_ForceActiveNow(); } @@ -4462,10 +4675,13 @@ void CL_ExecInitialConfigs(char *resetcommand) { int qrc, hrc, def; + Cbuf_Execute (); //make sure any pending console commands are done with. mostly, anyway... + SCR_ShowPic_Clear(true); + Cbuf_AddText("unbindall\n", RESTRICT_LOCAL); + Cbuf_AddText("cl_warncmd 0\n", RESTRICT_LOCAL); Cbuf_AddText("cvar_purgedefaults\n", RESTRICT_LOCAL); //reset cvar defaults to their engine-specified values. the tail end of 'exec default.cfg' will update non-cheat defaults to mod-specified values. Cbuf_AddText("cvarreset *\n", RESTRICT_LOCAL); //reset all cvars to their current (engine) defaults - Cbuf_AddText("cl_warncmd 0\n", RESTRICT_LOCAL); Cbuf_AddText(resetcommand, RESTRICT_LOCAL); Cbuf_AddText("\n", RESTRICT_LOCAL); @@ -4504,11 +4720,13 @@ void CL_ExecInitialConfigs(char *resetcommand) Cbuf_Execute (); //if the server initialisation causes a problem, give it a place to abort to - //assuming they didn't use any waits in their config (fools) //the configs should be fully loaded. //so convert the backwards compable commandline parameters in cvar sets. CL_ArgumentOverrides(); + + //and disable the 'you have unsaved stuff' prompt. + Cvar_Saved(); } @@ -4679,14 +4897,14 @@ void Host_Shutdown(void) CDAudio_Shutdown (); IN_Shutdown (); - R_ShutdownRenderer(); + R_ShutdownRenderer(true); S_Shutdown(true); #ifdef CL_MASTER MasterInfo_Shutdown(); #endif CL_FreeDlights(); CL_FreeVisEdicts(); - M_Shutdown(); + M_Shutdown(true); Mod_Shutdown(true); #ifndef CLIENTONLY SV_Shutdown(); diff --git a/engine/client/cl_master.h b/engine/client/cl_master.h index b60ee0d3..79dbc7fa 100644 --- a/engine/client/cl_master.h +++ b/engine/client/cl_master.h @@ -182,7 +182,7 @@ void Master_SetupSockets(void); void Master_QueryServer(serverinfo_t *server); void MasterInfo_WriteServers(void); -int Master_KeyForName(char *keyname); +int Master_KeyForName(const char *keyname); float Master_ReadKeyFloat(serverinfo_t *server, int keynum); char *Master_ReadKeyString(serverinfo_t *server, int keynum); @@ -194,5 +194,5 @@ qboolean Master_GetSortDescending(void); int Master_NumSorted(void); void Master_ClearMasks(void); serverinfo_t *Master_SortedServer(int idx); -void Master_SetMaskString(qboolean or, hostcachekey_t field, char *param, slist_test_t testop); +void Master_SetMaskString(qboolean or, hostcachekey_t field, const char *param, slist_test_t testop); void Master_SetMaskInteger(qboolean or, hostcachekey_t field, int param, slist_test_t testop); diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index 37122ac4..767d777c 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -258,7 +258,7 @@ char *svc_nqstrings[] = "NEW PROTOCOL(80)", //80 "NEW PROTOCOL(81)", //81 "NEW PROTOCOL(82)", //82 - "NEW PROTOCOL(83)", //83 + "nqsvcfte_cgamepacket(83)", //83 "NEW PROTOCOL(84)", //84 "NEW PROTOCOL(85)", //85 "nqsvcfte_updateentities", //86 @@ -312,7 +312,7 @@ void CL_Parse_Disconnected(void) int packet_latency[NET_TIMINGS]; -int CL_CalcNet (void) +int CL_CalcNet (float scale) { int i; outframe_t *frame; @@ -354,7 +354,7 @@ int CL_CalcNet (void) // else if (frame->invalid) // packet_latency[i&NET_TIMINGSMASK] = 9998; // invalid delta else - packet_latency[i&NET_TIMINGSMASK] = frame->latency * 60; + packet_latency[i&NET_TIMINGSMASK] = frame->latency * 60 * scale; } if (sent < 1) @@ -411,7 +411,7 @@ void CL_AckedInputFrame(int inseq, int outseq, qboolean worldstateokay) //============================================================================= -int CL_IsDownloading(char *localname) +int CL_IsDownloading(const char *localname) { downloadlist_t *dl; /*check for dupes*/ @@ -428,7 +428,7 @@ int CL_IsDownloading(char *localname) //note: this will overwrite existing files. //returns true if the download is going to be downloaded after the call. -qboolean CL_EnqueDownload(char *filename, char *localname, unsigned int flags) +qboolean CL_EnqueDownload(const char *filename, const char *localname, unsigned int flags) { char *ext; downloadlist_t *dl; @@ -653,7 +653,7 @@ void CL_DownloadFinished(void) { if (!FS_Rename(tempname+8, filename+8, FS_ROOT)) { - char nativetmp[MAX_OSPATH], nativefinal[MAX_OSPATH];; + char nativetmp[MAX_OSPATH], nativefinal[MAX_OSPATH]; FS_NativePath(tempname+8, FS_ROOT, nativetmp, sizeof(nativetmp)); FS_NativePath(filename+8, FS_ROOT, nativefinal, sizeof(nativefinal)); Con_Printf("Couldn't rename %s to %s\n", nativetmp, nativefinal); @@ -661,11 +661,11 @@ void CL_DownloadFinished(void) } else if (!strncmp(tempname,"skins/",6)) { - if (!FS_Rename(tempname+6, filename+6, FS_SKINS)) + if (!FS_Rename(tempname, filename, FS_PUBBASEGAMEONLY)) { - char nativetmp[MAX_OSPATH], nativefinal[MAX_OSPATH];; - FS_NativePath(tempname+6, FS_SKINS, nativetmp, sizeof(nativetmp)); - FS_NativePath(filename+6, FS_SKINS, nativefinal, sizeof(nativefinal)); + char nativetmp[MAX_OSPATH], nativefinal[MAX_OSPATH]; + FS_NativePath(tempname, FS_PUBBASEGAMEONLY, nativetmp, sizeof(nativetmp)); + FS_NativePath(filename, FS_PUBBASEGAMEONLY, nativefinal, sizeof(nativefinal)); Con_Printf("Couldn't rename %s to %s\n", nativetmp, nativefinal); } } @@ -673,7 +673,7 @@ void CL_DownloadFinished(void) { if (!FS_Rename(tempname, filename, FS_GAME)) { - char nativetmp[MAX_OSPATH], nativefinal[MAX_OSPATH];; + char nativetmp[MAX_OSPATH], nativefinal[MAX_OSPATH]; FS_NativePath(tempname, FS_GAME, nativetmp, sizeof(nativetmp)); FS_NativePath(filename, FS_GAME, nativefinal, sizeof(nativefinal)); Con_Printf("Couldn't rename %s to %s\n", nativetmp, nativefinal); @@ -716,7 +716,7 @@ void CL_DownloadFinished(void) { if (!strcmp(cl.model_name[i], filename)) { - cl.model_precache[i] = Mod_ForName(cl.model_name[i], false); //throw away result. + cl.model_precache[i] = Mod_ForName(cl.model_name[i], MLV_WARN); //throw away result. if (i == 1) cl.worldmodel = cl.model_precache[i]; break; @@ -726,7 +726,7 @@ void CL_DownloadFinished(void) { if (!strcmp(cl.model_csqcname[i], filename)) { - cl.model_csqcprecache[i] = Mod_ForName(cl.model_csqcname[i], false); //throw away result. + cl.model_csqcprecache[i] = Mod_ForName(cl.model_csqcname[i], MLV_WARN); //throw away result. break; } } @@ -734,7 +734,7 @@ void CL_DownloadFinished(void) { if (!strcmp(cl.model_name_vwep[i], filename)) { - cl.model_precache_vwep[i] = Mod_ForName(cl.model_name_vwep[i], false); + cl.model_precache_vwep[i] = Mod_ForName(cl.model_name_vwep[i], MLV_WARN); break; } } @@ -746,7 +746,7 @@ void CL_DownloadFinished(void) } } -qboolean CL_CheckFile(char *filename) +qboolean CL_CheckFile(const char *filename) { if (strstr (filename, "..")) { @@ -761,7 +761,7 @@ qboolean CL_CheckFile(char *filename) return false; } -qboolean CL_CheckDLFile(char *filename) +qboolean CL_CheckDLFile(const char *filename) { if (!strncmp(filename, "package/", 8)) { @@ -785,7 +785,7 @@ Returns true if the file exists, returns false if it triggered a download. =============== */ -qboolean CL_CheckOrEnqueDownloadFile (char *filename, char *localname, unsigned int flags) +qboolean CL_CheckOrEnqueDownloadFile (const char *filename, const char *localname, unsigned int flags) { //returns false if we don't have the file yet. if (flags & DLLF_NONGAME) { @@ -1160,7 +1160,7 @@ int CL_LoadModels(int stage, qboolean dontactuallyload) if (cl.playerview[0].playernum == -1) { //q2 cinematic - don't load the models. - cl.worldmodel = cl.model_precache[1] = Mod_ForName ("", false); + cl.worldmodel = cl.model_precache[1] = Mod_ForName ("", MLV_WARN); } else { @@ -1183,7 +1183,7 @@ int CL_LoadModels(int stage, qboolean dontactuallyload) cl.model_precache[i] = NULL; else #endif - cl.model_precache[i] = Mod_ForName (cl.model_name[i], false); + cl.model_precache[i] = Mod_ForName (cl.model_name[i], MLV_WARN); S_ExtraUpdate(); @@ -1201,7 +1201,7 @@ int CL_LoadModels(int stage, qboolean dontactuallyload) #ifdef CSQC_DAT CSQC_LoadResource(cl.model_name_vwep[i], "vwep"); #endif - cl.model_precache_vwep[i] = Mod_ForName (cl.model_name_vwep[i], false); + cl.model_precache_vwep[i] = Mod_ForName (cl.model_name_vwep[i], MLV_WARN); endstage(); } } @@ -1243,7 +1243,7 @@ int CL_LoadModels(int stage, qboolean dontactuallyload) else CSQC_LoadResource(cl.model_csqcname[i], "model"); #endif - cl.model_csqcprecache[i] = Mod_ForName (cl.model_csqcname[i], false); + cl.model_csqcprecache[i] = Mod_ForName (cl.model_csqcname[i], MLV_WARN); S_ExtraUpdate(); @@ -1335,7 +1335,7 @@ int CL_LoadSounds(int stage, qboolean dontactuallyload) return stage; } -void Sound_CheckDownload(char *s) +void Sound_CheckDownload(const char *s) { char mangled[512]; @@ -1640,7 +1640,7 @@ char *ZLibDownloadDecode(int *messagesize, char *input, int finalsize) } #endif -downloadlist_t *CL_DownloadFailed(char *name, qboolean cancel) +downloadlist_t *CL_DownloadFailed(const char *name, qboolean cancel) { //add this to our failed list. (so we don't try downloading it again...) downloadlist_t *failed, **link, *dl; @@ -1992,18 +1992,18 @@ void CL_ParseDownload (void) // open the file if not opened yet if (!cls.downloadqw) { - if (strncmp(cls.downloadtempname,"skins/",6)) + if (!strncmp(cls.downloadtempname,"skins/",6)) + { + Q_snprintfz(name, sizeof(name), "%s", cls.downloadtempname); + FS_CreatePath (name, FS_PUBBASEGAMEONLY); + cls.downloadqw = FS_OpenVFS (name, "wb", FS_PUBBASEGAMEONLY); + } + else { Q_snprintfz(name, sizeof(name), "%s", cls.downloadtempname); FS_CreatePath (name, FS_GAME); cls.downloadqw = FS_OpenVFS (name, "wb", FS_GAME); } - else - { - Q_snprintfz(name, sizeof(name)-6, "%s", cls.downloadtempname+6); - FS_CreatePath (name, FS_SKINS); - cls.downloadqw = FS_OpenVFS (name, "wb", FS_SKINS); - } if (!cls.downloadqw) { msg_readcount += size; @@ -3491,7 +3491,7 @@ void CLQ2_ParseConfigString (void) cl.model_precache[i-Q2CS_MODELS] = NULL; } else - cl.model_precache[i-Q2CS_MODELS] = Mod_ForName (cl.model_name[i-Q2CS_MODELS], false); + cl.model_precache[i-Q2CS_MODELS] = Mod_ForName (cl.model_name[i-Q2CS_MODELS], MLV_WARN); } } else if (i >= Q2CS_SOUNDS && i < Q2CS_SOUNDS+Q2MAX_MODELS) @@ -4978,7 +4978,7 @@ void CL_PrintChat(player_info_t *plr, char *rawmsg, char *msg, int plrflags) else Q_strncatz(fullchatmessage, "\1", sizeof(fullchatmessage)); -#if defined(_WIN32) && !defined(NOMEDIA) +#if defined(_WIN32) && !defined(NOMEDIA) && !defined(WINRT) TTS_SayChatString(&msg); #endif @@ -5213,8 +5213,8 @@ void CL_ParseStuffCmd(char *msg, int destsplit) //this protects stuffcmds from n Q_strncpyz(cl.model_name_vwep[i], mname, sizeof(cl.model_name_vwep[i])); if (cls.state == ca_active) { - CL_CheckOrEnqueDownloadFile(mname, NULL, 0); - cl.model_precache_vwep[i] = Mod_ForName(mname, false); + CL_CheckOrEnqueDownloadFile(cl.model_name_vwep[i], NULL, 0); + cl.model_precache_vwep[i] = Mod_ForName(cl.model_name_vwep[i], MLV_WARN); } } } @@ -5281,7 +5281,7 @@ void CL_ParsePrecache(void) { model_t *model; CL_CheckOrEnqueDownloadFile(s, s, 0); - model = Mod_ForName(s, i == 1); + model = Mod_ForName(s, (i == 1)?MLV_ERROR:MLV_WARN); if (!model) Con_Printf("svc_precache: Mod_ForName(\"%s\") failed\n", s); cl.model_precache[i] = model; @@ -5315,7 +5315,7 @@ void CL_ParsePrecache(void) if (cl.particle_ssname[i]) free(cl.particle_ssname[i]); cl.particle_ssname[i] = strdup(s); - cl.particle_ssprecache[i] = 1+P_FindParticleType(s); + cl.particle_ssprecache[i] = P_FindParticleType(s); cl.particle_ssprecaches = true; } else @@ -5419,7 +5419,7 @@ void CLQW_ParseServerMessage (void) if (cmd == svcfte_choosesplitclient) { - SHOWNET(svc_qwstrings[cmd]); + SHOWNET2(svc_qwstrings[cmd], cmd); destsplit = MSG_ReadByte() % MAX_SPLITS; cmd = MSG_ReadByte(); @@ -5434,7 +5434,7 @@ void CLQW_ParseServerMessage (void) break; } - SHOWNET(svc_qwstrings[cmd]); + SHOWNET2(svc_qwstrings[cmd], cmd); // other commands switch (cmd) @@ -6650,12 +6650,13 @@ void CLNQ_ParseServerMessage (void) Cmd_ExecuteString("bf", RESTRICT_SERVER); break; case svcfitz_fog: - cl.fog_density = MSG_ReadByte()/255.0f; - cl.fog_colour[0] = MSG_ReadByte()/255.0f; - cl.fog_colour[1] = MSG_ReadByte()/255.0f; - cl.fog_colour[2] = MSG_ReadByte()/255.0f; - /*time =*/ MSG_ReadShort(); - cl.fog_locked = !!cl.fog_density; + CL_ResetFog(); + cl.fog.density = MSG_ReadByte()/255.0f; + cl.fog.colour[0] = MSG_ReadByte()/255.0f; + cl.fog.colour[1] = MSG_ReadByte()/255.0f; + cl.fog.colour[2] = MSG_ReadByte()/255.0f; + cl.fog.time += ((unsigned short)MSG_ReadShort()) / 100.0; + cl.fog_locked = !!cl.fog.density; break; case svcfitz_spawnbaseline2: i = MSGCL_ReadEntity (); diff --git a/engine/client/cl_plugin.inc b/engine/client/cl_plugin.inc index f242afe8..65fc9e04 100644 --- a/engine/client/cl_plugin.inc +++ b/engine/client/cl_plugin.inc @@ -594,6 +594,35 @@ qintptr_t VARGS Plug_S_RawAudio(void *offset, quintptr_t mask, const qintptr_t * return 0; } +#include "com_mesh.h" +qintptr_t VARGS Plug_Mod_GetPluginModelFuncs(void *offset, quintptr_t mask, const qintptr_t *arg) +{ +#ifdef SKELETALMODELS + static modplugfuncs_t funcs = + { + Mod_RegisterModelFormatText, + Mod_RegisterModelFormatMagic, + Mod_UnRegisterModelFormat, + Mod_UnRegisterAllModelFormats, + ZG_Malloc, + + R_ConcatTransforms, + Matrix3x4_Invert_Simple, + COM_StripExtension, + GenMatrixPosQuat4Scale, + Alias_ForceConvertBoneData, + + R_RegisterShader, + R_RegisterSkin, + R_BuildDefaultTexnums + }; + if (VM_LONG(arg[0]) == sizeof(funcs)) + return (qintptr_t)&funcs; + else +#endif + return 0; +} + void Plug_Client_Init(void) { Plug_RegisterBuiltin("CL_GetStats", Plug_CL_GetStats, PLUG_BIF_NEEDSRENDERER); @@ -629,6 +658,8 @@ void Plug_Client_Init(void) Plug_RegisterBuiltin("SetUserInfo", Plug_SetUserInfo, PLUG_BIF_NEEDSRENDERER); Plug_RegisterBuiltin("S_RawAudio", Plug_S_RawAudio, PLUG_BIF_NEEDSRENDERER); + + Plug_RegisterBuiltin("Mod_GetPluginModelFuncs", Plug_Mod_GetPluginModelFuncs, PLUG_BIF_NEEDSRENDERER|PLUG_BIF_DLLONLY); } void Plug_Client_Close(plugin_t *plug) diff --git a/engine/client/cl_pred.c b/engine/client/cl_pred.c index 6a1efd59..cad3f7ad 100644 --- a/engine/client/cl_pred.c +++ b/engine/client/cl_pred.c @@ -1177,9 +1177,6 @@ void CL_PredictMove (void) { int i; - //work out which packet entities are solid - CL_SetSolidEntities (); - // Set up prediction for other players CL_SetUpPlayerPrediction(false); diff --git a/engine/client/cl_screen.c b/engine/client/cl_screen.c index dc34bbda..c28474f8 100644 --- a/engine/client/cl_screen.c +++ b/engine/client/cl_screen.c @@ -584,7 +584,7 @@ void SCR_CheckDrawCenterString (void) } } -void R_DrawTextField(int x, int y, int w, int h, char *text, unsigned int defaultmask, unsigned int fieldflags) +void R_DrawTextField(int x, int y, int w, int h, const char *text, unsigned int defaultmask, unsigned int fieldflags) { cprint_t p; vrect_t r; @@ -663,9 +663,11 @@ static void SCR_DrawSimMTouchCursor(void) typedef struct showpic_s { struct showpic_s *next; qbyte zone; - short x, y; + qboolean persist; + short x, y, w, h; char *name; char *picname; + char *tcommand; } showpic_t; showpic_t *showpics; @@ -745,13 +747,54 @@ void SCR_ShowPics_Draw(void) p = R2D_SafeCachePic(sp->picname); if (!p) continue; - R2D_ScalePic(x, y, p->width, p->height, p); + R2D_ScalePic(x, y, sp->w?sp->w:p->width, sp->h?sp->h:p->height, p); } } - -void SCR_ShowPic_Clear(void) +char *SCR_ShowPics_ClickCommand(int cx, int cy) { + downloadlist_t *failed; + float x, y, w, h; showpic_t *sp; + mpic_t *p; + for (sp = showpics; sp; sp = sp->next) + { + if (!sp->tcommand || !*sp->tcommand) + continue; + + x = sp->x; + y = sp->y; + w = sp->w; + h = sp->h; + SP_RecalcXY(&x, &y, sp->zone); + + if (!w || !h) + { + if (!*sp->picname) + continue; + for (failed = cl.faileddownloads; failed; failed = failed->next) + { //don't try displaying ones that we know to have failed. + if (!strcmp(failed->rname, sp->picname)) + break; + } + if (failed) + continue; + p = R2D_SafeCachePic(sp->picname); + if (!p) + continue; + w = w?w:sp->w; + h = h?h:sp->h; + } + if (cx >= x && cx < x+w) + if (cy >= y && cy < y+h) + return sp->tcommand; //if they overlap, that's your own damn fault. + } + return NULL; +} + +//all=false clears only server pics, not ones from configs. +void SCR_ShowPic_Clear(qboolean all) +{ + showpic_t **link, *sp; int pnum; for (pnum = 0; pnum < MAX_SPLITS; pnum++) @@ -760,12 +803,19 @@ void SCR_ShowPic_Clear(void) scr_centerprint[pnum].charcount = 0; } - while((sp = showpics)) + for (link = &showpics; (sp=*link); ) { - showpics = sp->next; + if (sp->persist) + { + link = &sp->next; + continue; + } + + *link = sp->next; Z_Free(sp->name); Z_Free(sp->picname); + Z_Free(sp->tcommand); Z_Free(sp); } } @@ -879,7 +929,8 @@ void SCR_ShowPic_Script_f(void) { char *imgname; char *name; - int x, y; + char *tcommand; + int x, y, w, h; int zone; showpic_t *sp; @@ -889,17 +940,27 @@ void SCR_ShowPic_Script_f(void) y = atoi(Cmd_Argv(4)); zone = atoi(Cmd_Argv(5)); + w = atoi(Cmd_Argv(6)); + h = atoi(Cmd_Argv(7)); + tcommand = Cmd_Argv(8); sp = SCR_ShowPic_Find(name); Z_Free(sp->picname); - sp->picname = Z_Malloc(strlen(imgname)+1); - strcpy(sp->picname, imgname); + Z_Free(sp->tcommand); + sp->picname = Z_StrDup(imgname); + sp->tcommand = Z_StrDup(tcommand); sp->zone = zone; sp->x = x; sp->y = y; + sp->w = w; + sp->h = h; + + if (!sp->persist) + sp->persist = !Cmd_FromGamecode(); + } //============================================================================= diff --git a/engine/client/cl_tent.c b/engine/client/cl_tent.c index 9c19ea77..33d89541 100644 --- a/engine/client/cl_tent.c +++ b/engine/client/cl_tent.c @@ -711,7 +711,7 @@ void CL_AddBeam (int tent, int ent, vec3_t start, vec3_t end) //fixme: use TE_ n if (ruleset_allow_particle_lightning.ival && btype >= 0) m = NULL; else - m = Mod_ForName(mname, false); + m = Mod_ForName(mname, MLV_WARN); if (m && m->needload) CL_CheckOrEnqueDownloadFile(m->name, NULL, 0); @@ -835,23 +835,23 @@ void CL_ParseStream (int type) switch(type) { case TEH2_STREAM_LIGHTNING_SMALL: - b->model = Mod_ForName("models/stltng2.mdl", true); + b->model = Mod_ForName("models/stltng2.mdl", MLV_WARN); b->flags |= 2; b->particleeffect = P_FindParticleType("te_stream_lightning_small"); break; case TEH2_STREAM_LIGHTNING: - b->model = Mod_ForName("models/stlghtng.mdl", true); + b->model = Mod_ForName("models/stlghtng.mdl", MLV_WARN); b->flags |= 2; b->particleeffect = P_FindParticleType("te_stream_lightning"); break; case TEH2_STREAM_ICECHUNKS: - b->model = Mod_ForName("models/stice.mdl", true); + b->model = Mod_ForName("models/stice.mdl", MLV_WARN); b->flags |= 2; b->particleeffect = P_FindParticleType("te_stream_icechunks"); if (cl_legacystains.ival) Surf_AddStain(end, -10, -10, 0, 20); break; case TEH2_STREAM_SUNSTAFF1: - b->model = Mod_ForName("models/stsunsf1.mdl", true); + b->model = Mod_ForName("models/stsunsf1.mdl", MLV_WARN); b->particleeffect = P_FindParticleType("te_stream_sunstaff1"); if (b->particleeffect < 0) { @@ -859,26 +859,26 @@ void CL_ParseStream (int type) if (b2) { memcpy(b2, b, sizeof(*b2)); - b2->model = Mod_ForName("models/stsunsf2.mdl", true); + b2->model = Mod_ForName("models/stsunsf2.mdl", MLV_WARN); b2->alpha = 0.5; } } break; case TEH2_STREAM_SUNSTAFF2: - b->model = Mod_ForName("models/stsunsf1.mdl", true); + b->model = Mod_ForName("models/stsunsf1.mdl", MLV_WARN); b->particleeffect = P_FindParticleType("te_stream_sunstaff2"); if (cl_legacystains.ival) Surf_AddStain(end, -10, -10, -10, 20); break; case TEH2_STREAM_COLORBEAM: - b->model = Mod_ForName("models/stclrbm.mdl", true); + b->model = Mod_ForName("models/stclrbm.mdl", MLV_WARN); b->particleeffect = P_FindParticleType("te_stream_colorbeam"); break; case TEH2_STREAM_GAZE: - b->model = Mod_ForName("models/stmedgaz.mdl", true); + b->model = Mod_ForName("models/stmedgaz.mdl", MLV_WARN); b->particleeffect = P_FindParticleType("te_stream_gaze"); break; case TEH2_STREAM_FAMINE: - b->model = Mod_ForName("models/fambeam.mdl", true); + b->model = Mod_ForName("models/fambeam.mdl", MLV_WARN); b->particleeffect = P_FindParticleType("te_stream_famine"); break; default: @@ -909,17 +909,13 @@ void CL_ParseTEnt (void) // explosion_t *ex; int cnt, colour; - type = MSG_ReadByte (); - #ifdef CSQC_DAT - if (type != TE_GUNSHOT) - { - //I know I'm going to regret this. - if (CSQC_ParseTempEntity((unsigned char)type)) - return; - } + //I know I'm going to regret this. + if (CSQC_ParseTempEntity()) + return; #endif + type = MSG_ReadByte (); if (nqprot) { @@ -1157,7 +1153,7 @@ void CL_ParseTEnt (void) explosion_t *ex = CL_AllocExplosion (); VectorCopy (pos, ex->origin); ex->start = cl.time; - ex->model = Mod_ForName ("progs/s_explod.spr", true); + ex->model = Mod_ForName ("progs/s_explod.spr", MLV_WARN); } break; case TE_EXPLOSION: // rocket explosion @@ -1197,7 +1193,7 @@ void CL_ParseTEnt (void) explosion_t *ex = CL_AllocExplosion (); VectorCopy (pos, ex->origin); ex->start = cl.time; - ex->model = Mod_ForName ("progs/s_explod.spr", true); + ex->model = Mod_ForName ("progs/s_explod.spr", MLV_WARN); } break; @@ -1919,7 +1915,9 @@ void CL_RefreshCustomTEnts(void) for (i = 0; i <= MAX_SSPARTICLESPRE; i++) { if (cl.particle_ssname[i]) - cl.particle_ssprecache[i] = 1+P_FindParticleType(cl.particle_ssname[i]); + cl.particle_ssprecache[i] = P_FindParticleType(cl.particle_ssname[i]); + else + cl.particle_ssprecache[i] = P_INVALID; } } if (cl.particle_csprecaches) @@ -1927,7 +1925,9 @@ void CL_RefreshCustomTEnts(void) for (i = 0; i <= MAX_CSPARTICLESPRE; i++) { if (cl.particle_csname[i]) - cl.particle_csprecache[i] = 1+P_FindParticleType(cl.particle_csname[i]); + cl.particle_csprecache[i] = P_FindParticleType(cl.particle_csname[i]); + else + cl.particle_csprecache[i] = P_INVALID; } } } @@ -1953,12 +1953,12 @@ int CL_TranslateParticleFromServer(int qceffect) if (cl.particle_ssprecaches && qceffect >= 0 && qceffect < MAX_SSPARTICLESPRE) { /*proper precaches*/ - return cl.particle_ssprecache[qceffect]-1; + return cl.particle_ssprecache[qceffect]; } else if (-qceffect >= 0 && -qceffect < MAX_CSPARTICLESPRE) { qceffect = -qceffect; - return cl.particle_csprecache[qceffect]-1; + return cl.particle_csprecache[qceffect]; } else return -1; @@ -2177,7 +2177,7 @@ void CL_SmokeAndFlash(vec3_t origin) ex->flags = Q2RF_TRANSLUCENT; ex->alpha = 1; ex->start = cl.time; - ex->model = Mod_ForName (q2tentmodels[q2cl_mod_smoke].modelname, false); + ex->model = Mod_ForName (q2tentmodels[q2cl_mod_smoke].modelname, MLV_WARN); ex = CL_AllocExplosion (); VectorCopy (origin, ex->origin); @@ -2186,7 +2186,7 @@ void CL_SmokeAndFlash(vec3_t origin) ex->flags = Q2RF_FULLBRIGHT; ex->numframes = 2; ex->start = cl.time; - ex->model = Mod_ForName (q2tentmodels[q2cl_mod_flash].modelname, false); + ex->model = Mod_ForName (q2tentmodels[q2cl_mod_flash].modelname, MLV_WARN); } void CL_Laser (vec3_t start, vec3_t end, int colors) @@ -2347,7 +2347,7 @@ void CLQ2_ParseTEnt (void) ex = CL_AllocExplosion (); VectorCopy (pos, ex->origin); ex->start = cl.time; - ex->model = Mod_ForName (q2tentmodels[q2cl_mod_explode].modelname, false); + ex->model = Mod_ForName (q2tentmodels[q2cl_mod_explode].modelname, MLV_WARN); ex->firstframe = 0; ex->numframes = 4; ex->flags = Q2RF_FULLBRIGHT|Q2RF_ADDITIVE|RF_NOSHADOW|Q2RF_TRANSLUCENT; @@ -2435,7 +2435,7 @@ void CLQ2_ParseTEnt (void) VectorCopy (pos, ex->origin); VectorClear(ex->angles); ex->start = cl.time; - ex->model = Mod_ForName (q2tentmodels[q2cl_mod_explo4].modelname, true); + ex->model = Mod_ForName (q2tentmodels[q2cl_mod_explo4].modelname, MLV_WARN); ex->firstframe = 30; ex->alpha = 1; ex->flags |= Q2RF_TRANSLUCENT; @@ -2530,7 +2530,7 @@ void CLQ2_ParseTEnt (void) VectorCopy (pos, ex->origin); VectorClear(ex->angles); ex->start = cl.time; - ex->model = Mod_ForName (q2tentmodels[q2cl_mod_explo4].modelname, false); + ex->model = Mod_ForName (q2tentmodels[q2cl_mod_explo4].modelname, MLV_WARN); ex->alpha = 1; ex->flags |= Q2RF_TRANSLUCENT; if (rand()&1) @@ -2678,7 +2678,7 @@ void CLQ2_ParseTEnt (void) ex = CL_AllocExplosion (); VectorCopy (pos, ex->origin); ex->start = cl.time; - ex->model = Mod_ForName (q2tentmodels[q2cl_mod_explode].modelname, false); + ex->model = Mod_ForName (q2tentmodels[q2cl_mod_explode].modelname, MLV_WARN); ex->firstframe = 0; ex->numframes = 4; ex->flags = Q2RF_FULLBRIGHT|RF_NOSHADOW; @@ -2728,7 +2728,7 @@ void CLQ2_ParseTEnt (void) ex = CL_AllocExplosion (); VectorCopy (pos, ex->origin); ex->start = cl.time; - ex->model = Mod_ForName (q2tentmodels[q2cl_mod_explode].modelname, false); + ex->model = Mod_ForName (q2tentmodels[q2cl_mod_explode].modelname, MLV_WARN); ex->firstframe = 0; ex->numframes = 4; ex->flags = Q2RF_FULLBRIGHT|RF_NOSHADOW; @@ -2786,7 +2786,7 @@ void CLQ2_ParseTEnt (void) // ex->type = ex_poly; ex->flags = Q2RF_FULLBRIGHT|RF_NOSHADOW; ex->angles[1] = rand() % 360; - ex->model = Mod_ForName (q2tentmodels[q2cl_mod_explo4].modelname, false); + ex->model = Mod_ForName (q2tentmodels[q2cl_mod_explo4].modelname, MLV_WARN); if (rand() < RAND_MAX/2) ex->firstframe = 15; ex->numframes = 15; @@ -3106,21 +3106,21 @@ void CL_UpdateBeams (void) playerview_t *pv = &cl.playerview[j]; if (b->entity == ((cl.spectator&&pv->cam_auto)?pv->cam_spec_track+1:(pv->playernum+1))) { - player_state_t *pl; +// player_state_t *pl; // VectorSubtract(cl.simorg, b->start, org); // VectorAdd(b->end, org, b->end); //move the end point by simorg-start - pl = &cl.inframes[cl.parsecount&UPDATE_MASK].playerstate[b->entity-1]; - if (pl->messagenum == cl.parsecount || cls.protocol == CP_NETQUAKE) +// pl = &cl.inframes[cl.parsecount&UPDATE_MASK].playerstate[b->entity-1]; +// if (pl->messagenum == cl.parsecount || cls.protocol == CP_NETQUAKE) { vec3_t fwd, org, ang; float delta, f, len; - if (cl.spectator && pv->cam_auto) - { //if we're tracking someone, use their origin explicitly. - vieworg = pl->origin; - } - else +// if (cl.spectator && pv->cam_auto) +// { //if we're tracking someone, use their origin explicitly. +// vieworg = pl->origin; +// } +// else vieworg = pv->simorg; viewang = pv->simangles; diff --git a/engine/client/cl_ui.c b/engine/client/cl_ui.c index 3a68f351..6ceaa0d7 100644 --- a/engine/client/cl_ui.c +++ b/engine/client/cl_ui.c @@ -405,10 +405,8 @@ void VQ3_AddEntity(const q3refEntity_t *q3) ent.scale = 1; ent.rtype = q3->reType; - if (q3->customSkin) - ent.skinnum = Mod_SkinNumForName(ent.model, VM_FROMSTRCACHE(q3->customSkin)); - else - ent.skinnum = q3->skinNum; + ent.customskin = q3->customSkin; + ent.skinnum = q3->skinNum; ent.shaderRGBAf[0] = q3->shaderRGBA[0]/255.0f; ent.shaderRGBAf[1] = q3->shaderRGBA[1]/255.0f; @@ -430,6 +428,10 @@ void VQ3_AddEntity(const q3refEntity_t *q3) if (q3->renderfx & Q3RF_NOSHADOW) ent.flags |= RF_NOSHADOW; + ent.topcolour = TOP_DEFAULT; + ent.bottomcolour = BOTTOM_DEFAULT; + ent.playerindex = -1; + VectorCopy(q3->origin, ent.origin); VectorCopy(q3->oldorigin, ent.oldorigin); V_AddAxisEntity(&ent); @@ -839,7 +841,12 @@ static qintptr_t UI_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con case UI_R_REGISTERMODEL: //precache model { char *name = VM_POINTER(arg[0]); - VM_LONG(ret) = VM_TOMHANDLE(Mod_ForName(name, false)); + model_t *mod; + mod = Mod_ForName(name, MLV_SILENT); + if (mod->needload || mod->type == mod_dummy) + VM_LONG(ret) = 0; + else + VM_LONG(ret) = VM_TOMHANDLE(mod); } break; case UI_R_MODELBOUNDS: @@ -863,7 +870,7 @@ static qintptr_t UI_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con break; case UI_R_REGISTERSKIN: - VM_LONG(ret) = VM_TOSTRCACHE(arg[0]); + VM_LONG(ret) = Mod_RegisterSkinFile(VM_POINTER(arg[0])); break; case UI_R_REGISTERFONT: //register font diff --git a/engine/client/client.h b/engine/client/client.h index 6dd68372..feb1c53e 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -696,8 +696,8 @@ typedef struct vec3_t skyaxis; qboolean fog_locked; - float fog_density; - vec3_t fog_colour; + fogstate_t fog; + fogstate_t oldfog; char levelname[40]; // for display on solo scoreboard @@ -807,7 +807,7 @@ extern cvar_t ruleset_allow_particle_lightning; extern cvar_t ruleset_allow_overlongsounds; extern cvar_t ruleset_allow_larger_models; extern cvar_t ruleset_allow_modified_eyes; -extern cvar_t ruleset_allow_sensative_texture_replacements; +extern cvar_t ruleset_allow_sensitive_texture_replacements; extern cvar_t ruleset_allow_localvolume; extern cvar_t ruleset_allow_shaders; @@ -953,7 +953,7 @@ void CL_BaseMove (usercmd_t *cmd, int pnum, float extra, float wantfps); float CL_KeyState (kbutton_t *key, int pnum); char *Key_KeynumToString (int keynum); -int Key_StringToKeynum (char *str, int *modifier); +int Key_StringToKeynum (const char *str, int *modifier); char *Key_GetBinding(int keynum); void CL_UseIndepPhysics(qboolean allow); @@ -1015,7 +1015,7 @@ void CL_ParseQTVFile(vfsfile_t *f, const char *fname, qtvfile_t *result); #define NET_TIMINGS 256 #define NET_TIMINGSMASK 255 extern int packet_latency[NET_TIMINGS]; -int CL_CalcNet (void); +int CL_CalcNet (float scale); void CL_ClearParseState(void); void CL_Parse_Disconnected(void); void CL_DumpPacket(void); @@ -1027,17 +1027,17 @@ void CLQ2_ParseServerMessage (void); #endif void CL_NewTranslation (int slot); -int CL_IsDownloading(char *localname); -qboolean CL_CheckOrEnqueDownloadFile (char *filename, char *localname, unsigned int flags); -qboolean CL_EnqueDownload(char *filename, char *localname, unsigned int flags); -downloadlist_t *CL_DownloadFailed(char *name, qboolean cancel); +int CL_IsDownloading(const char *localname); +qboolean CL_CheckOrEnqueDownloadFile (const char *filename, const char *localname, unsigned int flags); +qboolean CL_EnqueDownload(const char *filename, const char *localname, unsigned int flags); +downloadlist_t *CL_DownloadFailed(const char *name, qboolean cancel); int CL_DownloadRate(void); void CL_GetDownloadSizes(unsigned int *filecount, unsigned int *totalsize, qboolean *somesizesunknown); qboolean CL_ParseOOBDownload(void); void CL_DownloadFinished(void); void CL_RequestNextDownload (void); void CL_SendDownloadReq(sizebuf_t *msg); -void Sound_CheckDownload(char *s); /*checkorenqueue a sound file*/ +void Sound_CheckDownload(const char *s); /*checkorenqueue a sound file*/ qboolean CL_IsUploading(void); void CL_NextUpload(void); @@ -1155,7 +1155,7 @@ qboolean CSQC_ParseGamePacket(void); qboolean CSQC_CenterPrint(int lplayernum, char *cmd); void CSQC_Input_Frame(int lplayernum, usercmd_t *cmd); void CSQC_WorldLoaded(void); -qboolean CSQC_ParseTempEntity(unsigned char firstbyte); +qboolean CSQC_ParseTempEntity(void); qboolean CSQC_ConsoleCommand(char *cmd); qboolean CSQC_KeyPress(int key, int unicode, qboolean down, int devid); qboolean CSQC_MouseMove(float xdelta, float ydelta, int devid); @@ -1333,6 +1333,7 @@ void CL_BeginServerReconnect(void); void SV_User_f (void); //called by client version of the function void SV_Serverinfo_f (void); +void SV_ConSay_f(void); @@ -1374,12 +1375,12 @@ struct cin_s *Media_StartCin(char *name); texid_tf Media_UpdateForShader(cin_t *cin); void Media_ShutdownCin(cin_t *cin); #endif -qboolean Media_BackgroundTrack(char *initialtrack, char *looptrack); //new background music interface +qboolean Media_BackgroundTrack(const char *initialtrack, const char *looptrack); //new background music interface void Media_NumberedTrack(unsigned int initialtrack, unsigned int looptrack); //legacy cd interface for protocols that only support numbered tracks. void Media_EndedTrack(void); //cd is no longer running, media code needs to pick a new track (cd track or faketrack) //these accept NULL for cin to mean the current fullscreen video -void Media_Send_Command(cin_t *cin, char *command); +void Media_Send_Command(cin_t *cin, const char *command); void Media_Send_MouseMove(cin_t *cin, float x, float y); void Media_Send_Resize(cin_t *cin, int x, int y); void Media_Send_GetSize(cin_t *cin, int *x, int *y); @@ -1415,7 +1416,7 @@ typedef struct void (VARGS *key) (void *ctx, int code, int unicode, int event); qboolean (VARGS *setsize) (void *ctx, int width, int height); void (VARGS *getsize) (void *ctx, int *width, int *height); - void (VARGS *changestream) (void *ctx, char *streamname); + void (VARGS *changestream) (void *ctx, const char *streamname); } media_decoder_funcs_t; typedef struct { char *drivername; diff --git a/engine/client/clq2_ents.c b/engine/client/clq2_ents.c index 27bed6d4..1e283c30 100644 --- a/engine/client/clq2_ents.c +++ b/engine/client/clq2_ents.c @@ -1312,9 +1312,9 @@ void CLQ2_AddPacketEntities (q2frame_t *frame) { char *pmodel = Info_ValueForKey(player->userinfo, "model"); if (*pmodel) - ent.model = Mod_ForName(va("players/%s/tris.md2", pmodel), false); + ent.model = Mod_ForName(va("players/%s/tris.md2", pmodel), MLV_WARN); if (!ent.model || ent.model->needload) - ent.model = Mod_ForName("players/male/tris.md2", false); + ent.model = Mod_ForName("players/male/tris.md2", MLV_SILENT); } ent.playerindex = (s1->skinnum&0xff)%cl.allocated_client_slots; player->model = ent.model; @@ -1535,7 +1535,7 @@ void CLQ2_AddPacketEntities (q2frame_t *frame) i = (s1->skinnum >> 8); // 0 is default weapon model if (i >= 0 && i < cl.numq2visibleweapons) - ent.model = Mod_ForName(va("players/%s/%s", modelname, cl.q2visibleweapons[i]), false); + ent.model = Mod_ForName(va("players/%s/%s", modelname, cl.q2visibleweapons[i]), MLV_WARN); /* ci = &cl.clientinfo[s1->skinnum & 0xff]; i = (s1->skinnum >> 8); // 0 is default weapon model diff --git a/engine/client/clq3_parse.c b/engine/client/clq3_parse.c index 85c0bfb1..be9f9224 100644 --- a/engine/client/clq3_parse.c +++ b/engine/client/clq3_parse.c @@ -538,6 +538,10 @@ void CLQ3_ParseGameState(void) // wipe the client_state_t struct // CL_ClearState(); + ccs.firstParseEntity = 0; + memset(ccs.parseEntities, 0, sizeof(ccs.parseEntities)); + memset(ccs.baselines, 0, sizeof(ccs.baselines)); + cl.minpitch = -90; cl.maxpitch = 90; diff --git a/engine/client/console.c b/engine/client/console.c index 1250283d..93f85619 100644 --- a/engine/client/console.c +++ b/engine/client/console.c @@ -142,7 +142,7 @@ void Con_Destroy (console_t *con) con_current = &con_main; } /*obtains a console_t without creating*/ -console_t *Con_FindConsole(char *name) +console_t *Con_FindConsole(const char *name) { console_t *con; if (!strcmp(name, "current") && con_current) @@ -155,7 +155,7 @@ console_t *Con_FindConsole(char *name) return NULL; } /*creates a potentially duplicate console_t - please use Con_FindConsole first, as its confusing otherwise*/ -console_t *Con_Create(char *name, unsigned int flags) +console_t *Con_Create(const char *name, unsigned int flags) { console_t *con; if (!strcmp(name, "current")) @@ -717,7 +717,7 @@ void Con_PrintCon (console_t *con, char *txt) else con->current->time = realtime; -#if defined(_WIN32) && !defined(NOMEDIA) +#if defined(_WIN32) && !defined(NOMEDIA) && !defined(WINRT) if (con->current) TTS_SayConString((conchar_t*)(con->current+1)); #endif @@ -1128,18 +1128,26 @@ int Con_DrawInput (console_t *con, qboolean focused, int left, int right, int y, /*just above that, we have the tab completion list*/ if (con_commandmatch && con_displaypossibilities.value) { - int lines; - conchar_t *starts[32]; - conchar_t *ends[32]; - conchar_t *end; - char *cmd; + conchar_t *end, *s; + char *cmd;//, *desc; int cmdstart; + size_t newlen; cmdstart = text[1] == '/'?2:1; end = maskedtext; + if (!con->completionline || con->completionline->length + 512 > con->completionline->maxlength) + { + newlen = (con->completionline?con->completionline->length:0) + 2048; + + Z_Free(con->completionline); + con->completionline = Z_Malloc(sizeof(*con->completionline) + newlen*sizeof(conchar_t)); + con->completionline->maxlength = newlen; + } + con->completionline->length = 0; + for (i = 1; ; i++) { - cmd = Cmd_CompleteCommand (text+cmdstart, true, true, i, NULL); + cmd = Cmd_CompleteCommand (text+cmdstart, true, true, i, NULL);//&desc); if (!cmd) { if (i <= 2) @@ -1147,19 +1155,16 @@ int Con_DrawInput (console_t *con, qboolean focused, int left, int right, int y, break; } - end = COM_ParseFunString((COLOR_GREEN<completionline+1); +// if (desc) +// end = COM_ParseFunString((COLOR_GREEN<completionline->length, (con->completionline->maxlength-con->completionline->length)*sizeof(maskedtext[0]), true); +// else + end = COM_ParseFunString((COLOR_GREEN<completionline->length, (con->completionline->maxlength-con->completionline->length)*sizeof(maskedtext[0]), true); + con->completionline->length = end - s; } - lines = Font_LineBreaks(maskedtext, end, right - left, 32, starts, ends); - while(lines-->0) - { - rhs = left; - y -= Font_CharHeight(); - for (cchar = starts[lines]; cchar < ends[lines]; cchar++) - { - rhs = Font_DrawChar(rhs, y, *cchar); - } - } + if (con->completionline->length) + y = Con_DrawConsoleLines(con, con->completionline, left, right, y, 0, selactive, selsx, selex, selsy, seley); } return y; @@ -1302,9 +1307,10 @@ void Con_DrawNotify (void) foo[pos] = i; //k, build the string properly. - end = COM_ParseFunString(CON_WHITEMASK, foo, markup, sizeof(markup) - sizeof(markup[0]), PFS_KEEPMARKUP | PFS_FORCEUTF8); + end = COM_ParseFunString(CON_WHITEMASK, foo, markup, sizeof(markup) - sizeof(markup[0])-1, PFS_KEEPMARKUP | PFS_FORCEUTF8); //and overwrite the cursor so that it blinks. + *end = ' '|CON_WHITEMASK; if (((int)(realtime*con_cursorspeed)&1)) *c = 0xe00b|CON_WHITEMASK; if (c == end) @@ -1563,6 +1569,7 @@ static int Con_DrawConsoleLines(console_t *con, conline_t *l, int sx, int ex, in int i; int x; + if (l != con->completionline) if (l != con->footerline) if (l != con->current) { @@ -1730,18 +1737,18 @@ static int Con_DrawConsoleLines(console_t *con, conline_t *l, int sx, int ex, in sstart = sx+picw; send = sstart; for (i = 0; i < linelength; i++) - send += Font_CharWidth(s[i]); + send = Font_CharEndCoord(font_console, send, s[i]); //show something on blank lines if (send == sstart) - send = sstart + Font_CharWidth(' '); + send = Font_CharEndCoord(font_console, send, ' '); if (y >= seley) { send = sstart; for (i = 0; i < linelength; ) { - send += Font_CharWidth(s[i++]); + send = Font_CharEndCoord(font_console, send, s[i++]); if (send > selex) break; @@ -1757,10 +1764,10 @@ static int Con_DrawConsoleLines(console_t *con, conline_t *l, int sx, int ex, in { for (i = 0; i < linelength; i++) { - x = Font_CharWidth(s[i]); - if (sstart + x > selsx) + x = Font_CharEndCoord(font_console, sstart, s[i]); + if (x > selsx) break; - sstart += x; + sstart = x; } con->selstartline = l; @@ -1969,6 +1976,7 @@ char *Con_CopyConsole(qboolean nomarkup, qboolean onlyiflink) char *result; int outlen, maxlen; int finalendoffset; + unsigned int uc; if (!con->selstartline || !con->selendline) return NULL; @@ -1989,7 +1997,8 @@ char *Con_CopyConsole(qboolean nomarkup, qboolean onlyiflink) while (cur > (conchar_t*)(l+1)) { cur--; - if ((*cur & CON_CHARMASK) == ' ') + uc = (*cur & CON_CHARMASK); + if (uc == ' ' || uc == '\t') { cur++; break; @@ -1999,7 +2008,8 @@ char *Con_CopyConsole(qboolean nomarkup, qboolean onlyiflink) } while (finalendoffset < con->selendline->length) { - if ((((conchar_t*)(l+1))[finalendoffset] & CON_CHARMASK) != ' ') + uc = (((conchar_t*)(l+1))[finalendoffset] & CON_CHARMASK); + if (uc != ' ' && uc != '\t' && ((conchar_t*)(l+1))[finalendoffset] != CON_LINKEND) finalendoffset++; else break; @@ -2023,20 +2033,19 @@ char *Con_CopyConsole(qboolean nomarkup, qboolean onlyiflink) } } //scan forwards to find the end of the selected link - for(lend = (conchar_t*)(con->selendline+1) + finalendoffset; lend < (conchar_t*)(con->selendline+1) + con->selendline->length; lend++) + if (*cur == CON_LINKSTART) { - if (*lend == CON_LINKEND) + for(lend = (conchar_t*)(con->selendline+1) + finalendoffset; lend < (conchar_t*)(con->selendline+1) + con->selendline->length; lend++) { - finalendoffset = lend+1 - (conchar_t*)(con->selendline+1); - break; + if (*lend == CON_LINKEND) + { + finalendoffset = lend+1 - (conchar_t*)(con->selendline+1); + break; + } } } - - if (onlyiflink) - { - if (*cur != CON_LINKSTART) - return NULL; - } + else if (onlyiflink) + return NULL; maxlen = 1024*1024; result = Z_Malloc(maxlen+1); diff --git a/engine/client/image.c b/engine/client/image.c index d87be4e3..f5c6fdf5 100644 --- a/engine/client/image.c +++ b/engine/client/image.c @@ -2274,7 +2274,7 @@ typedef struct { } ddsheader; -texid_tf GL_ReadTextureDDS(char *iname, unsigned char *buffer, int filesize) +texid_tf GL_ReadTextureDDS(const char *iname, unsigned char *buffer, int filesize) { extern int gl_filter_min; extern int gl_filter_max; @@ -2368,7 +2368,7 @@ texid_tf GL_ReadTextureDDS(char *iname, unsigned char *buffer, int filesize) #endif #ifdef IMAGEFMT_BLP -texid_tf GL_ReadBLPFile(char *iname, unsigned char *buffer, int filesize) +texid_tf GL_ReadBLPFile(const char *iname, unsigned char *buffer, int filesize, int *width, int *height) { extern int gl_filter_min; extern int gl_filter_max; @@ -2400,8 +2400,8 @@ texid_tf GL_ReadBLPFile(char *iname, unsigned char *buffer, int filesize) if (memcmp(blp->blp2, "BLP2", 4) || blp->type != 1 || qrenderer != QR_OPENGL) return r_nulltex; - w = blp->xres; - h = blp->yres; + *width = w = blp->xres; + *height = h = blp->yres; texnum = GL_AllocNewTexture(iname, w, h, 0); GL_MTBind(0, GL_TEXTURE_2D, texnum); @@ -2465,10 +2465,11 @@ texid_tf GL_ReadBLPFile(char *iname, unsigned char *buffer, int filesize) if (!tmpmem) tmpmem = malloc(4*w*h); - //8bit palette index + //load the rgb data first (8-bit paletted) for (i = 0; i < w*h; i++) tmpmem[i] = blp->palette[*in++] | 0xff000000; + //and then change the alpha bits accordingly. switch(blp->alphadepth) { case 0: @@ -2561,7 +2562,7 @@ qbyte *Read32BitImageFile(qbyte *buf, int len, int *width, int *height, qboolean int w = LittleLong(((int*)buf)[0]); int h = LittleLong(((int*)buf)[1]); int i; - if (w >= 4 && h >= 4 && w*h+sizeof(int)*2 == com_filesize) + if (w >= 3 && h >= 4 && w*h+sizeof(int)*2 == com_filesize) { qboolean foundalpha = false; qbyte *in = (qbyte*)((int*)buf+2); @@ -2686,7 +2687,7 @@ static struct }; int image_width, image_height; -qboolean R_LoadTextureFromMemory(texid_t *tex, int flags, char *iname, char *fname, qbyte *filedata, int filesize) +qboolean R_LoadTextureFromMemory(texid_t *tex, int flags, const char *iname, char *fname, qbyte *filedata, int filesize) { qboolean hasalpha; qbyte *rgbadata; @@ -2700,7 +2701,7 @@ qboolean R_LoadTextureFromMemory(texid_t *tex, int flags, char *iname, char *fna #ifdef IMAGEFMT_BLP if (filedata[0] == 'B' && filedata[1] == 'L' && filedata[2] == 'P' && filedata[3] == '2') { - *tex = GL_ReadBLPFile(iname, filedata, filesize); + *tex = GL_ReadBLPFile(iname, filedata, filesize, &image_width, &image_height); if (TEXVALID(*tex)) return true; } @@ -2752,7 +2753,7 @@ qboolean R_LoadTextureFromMemory(texid_t *tex, int flags, char *iname, char *fna Con_Printf("Unable to read file %s (format unsupported)\n", fname); return false; } -texid_t R_LoadHiResTexture(char *name, char *subpath, unsigned int flags) +texid_t R_LoadHiResTexture(const char *name, const char *subpath, unsigned int flags) { qboolean alphaed; char *buf; @@ -2975,7 +2976,7 @@ texid_t R_LoadHiResTexture(char *name, char *subpath, unsigned int flags) } return r_nulltex; } -texid_t R_LoadReplacementTexture(char *name, char *subpath, unsigned int flags) +texid_t R_LoadReplacementTexture(const char *name, const char *subpath, unsigned int flags) { if (!gl_load24bit.value) return r_nulltex; @@ -2983,7 +2984,7 @@ texid_t R_LoadReplacementTexture(char *name, char *subpath, unsigned int flags) } extern cvar_t r_shadow_bumpscale_bumpmap; -texid_t R_LoadBumpmapTexture(char *name, char *subpath) +texid_t R_LoadBumpmapTexture(const char *name, const char *subpath) { char *buf, *data; texid_t tex; diff --git a/engine/client/in_generic.c b/engine/client/in_generic.c index 2be2f97c..d6107ec4 100644 --- a/engine/client/in_generic.c +++ b/engine/client/in_generic.c @@ -26,7 +26,8 @@ struct eventlist_s IEV_KEYDOWN, IEV_KEYRELEASE, IEV_MOUSEABS, - IEV_MOUSEDELTA + IEV_MOUSEDELTA, + IEV_JOYAXIS } type; int devid; @@ -41,6 +42,11 @@ struct eventlist_s { int scancode, unicode; } keyboard; + struct + { + int axis; + float value; + } joy; }; } eventlist[EVENTQUEUELENGTH]; volatile int events_avail; /*volatile to make sure the cc doesn't try leaving these cached in a register*/ @@ -67,7 +73,7 @@ struct mouse_s M_MOUSE, //using deltas M_TOUCH //using absolutes } type; - int qdeviceid; + int qdeviceid; //so we can just use pointers. vec2_t oldpos; vec2_t downpos; float moveddist; //how far it has moved while held. this provides us with our emulated mouse1 when they release the press @@ -77,7 +83,13 @@ struct mouse_s int down; } ptr[MAXPOINTERS]; - +#define MAXJOYAXIS 6 +#define MAXJOYSTICKS 4 +struct joy_s +{ + int qdeviceid; + int axis[MAXJOYAXIS]; +} joy[MAXJOYSTICKS]; void IN_Shutdown(void) { @@ -88,20 +100,25 @@ void IN_ReInit(void) { int i; - events_avail = 0; - events_used = 0; - for (i = 0; i < MAXPOINTERS; i++) { ptr[i].type = M_INVALID; ptr[i].qdeviceid = i; } + for (i = 0; i < MAXJOYSTICKS; i++) + { + joy[i].qdeviceid = i; + } + INS_ReInit(); } void IN_Init(void) { + events_avail = 0; + events_used = 0; + Cvar_Register (&m_filter, "input controls"); Cvar_Register (&m_accel, "input controls"); Cvar_Register (&m_forcewheel, "Input Controls"); @@ -114,6 +131,40 @@ void IN_Init(void) INS_Init(); } +//tells the keys.c code whether the cursor is currently active, causing mouse clicks instead of binds. +qboolean IN_MouseDevIsTouch(int devid) +{ + if (devid < MAXPOINTERS) + return ptr[devid].type == M_TOUCH; + return false; +} +//there was no ui to click on at least... +//translates MOUSE1 press events into begin-look-or-strafe events. +//translates to MOUSE2 accordingly +//returns 0 if it ate it completely. +int IN_TranslateMButtonPress(int devid) +{ + int ret; + if (!ptr[devid].down) + { + //set the cursor-pressed state, so we begin to look/strafe around + ptr[devid].down = 1; + ptr[devid].moveddist = 0; + ptr[devid].downpos[0] = ptr[devid].oldpos[0]; + ptr[devid].downpos[1] = ptr[devid].oldpos[1]; + ptr[devid].delta[0] = 0; + ptr[devid].delta[1] = 0; + ret = 0; //eat it + } + else + { + //this is the key binding that the press should use + ret = (m_strafeonright.ival && ptr[devid].downpos[0] > vid.pixelwidth/2)?K_MOUSE2:K_MOUSE1; + } + + return ret; +} + /*a 'pointer' is either a multitouch pointer, or a separate device note that mice use the keyboard button api, but separate devices*/ void IN_Commands(void) @@ -137,39 +188,15 @@ void IN_Commands(void) else { if (ev->type == IEV_KEYDOWN) - { - ptr[ev->devid].down = 1; - ptr[ev->devid].moveddist = 0; - ptr[ev->devid].downpos[0] = ptr[ev->devid].oldpos[0]; - ptr[ev->devid].downpos[1] = ptr[ev->devid].oldpos[1]; - ptr[ev->devid].delta[0] = 0; - ptr[ev->devid].delta[1] = 0; - - if (ev->mouse.tsize > m_fatpressthreshold.value) - { - int key = (m_strafeonright.ival && ptr[ev->devid].downpos[0] > vid.pixelwidth/2)?K_MOUSE2:K_MOUSE1; - Key_Event(ev->devid, key, 0, true); - ptr[ev->devid].down = 2; - } - } + Key_Event(ev->devid, ev->keyboard.scancode, ev->keyboard.unicode, ev->type == IEV_KEYDOWN); else { - if (ptr[ev->devid].down > 1) + if (ptr[ev->devid].down == 1 || ptr[ev->devid].moveddist < m_slidethreshold.value) { - int key = (m_strafeonright.ival && ptr[ev->devid].downpos[0] > vid.pixelwidth/2)?K_MOUSE2:K_MOUSE1; - Key_Event(ev->devid, key, 0, false); - ptr[ev->devid].down = 1; - } - if (ptr[ev->devid].down) - { - if (ptr[ev->devid].moveddist < m_slidethreshold.value) - { - /*if its on the right, make it a mouse2*/ - int key = (m_strafeonright.ival && ptr[ev->devid].downpos[0] > vid.pixelwidth/2)?K_MOUSE2:K_MOUSE1; - Key_Event(ev->devid, key, 0, true); - Key_Event(ev->devid, key, 0, false); - } + ptr[ev->devid].down = 2; + Key_Event(ev->devid, K_MOUSE1, 0, true); } + Key_Event(ev->devid, K_MOUSE1, 0, false); ptr[ev->devid].down = 0; } break; @@ -177,6 +204,9 @@ void IN_Commands(void) } Key_Event(ev->devid, ev->keyboard.scancode, ev->keyboard.unicode, ev->type == IEV_KEYDOWN); break; + case IEV_JOYAXIS: + joy[ev->devid].axis[ev->joy.axis] = ev->joy.value; + break; case IEV_MOUSEDELTA: if (ev->devid < MAXPOINTERS) { @@ -237,15 +267,13 @@ void IN_Commands(void) if (ptr[ev->devid].down > 1 && ev->mouse.tsize < m_fatpressthreshold.value) { - int key = (m_strafeonright.ival && ptr[ev->devid].downpos[0] > vid.pixelwidth/2)?K_MOUSE2:K_MOUSE1; - Key_Event(ev->devid, key, 0, false); ptr[ev->devid].down = 1; + Key_Event(ev->devid, K_MOUSE1, 0, false); } if (ptr[ev->devid].down == 1 && ev->mouse.tsize > m_fatpressthreshold.value) { - int key = (m_strafeonright.ival && ptr[ev->devid].downpos[0] > vid.pixelwidth/2)?K_MOUSE2:K_MOUSE1; - Key_Event(ev->devid, key, 0, true); ptr[ev->devid].down = 2; + Key_Event(ev->devid, K_MOUSE1, 0, true); } } break; @@ -407,15 +435,15 @@ void IN_MoveMouse(struct mouse_s *mouse, float *movements, int pnum) } else { - //if game is not focused, kill any mouse look - if (Key_Dest_Has(~kdm_game)) + if (mx || my) + if (CSQC_MouseMove(mx, my, mouse->qdeviceid)) { mx = 0; my = 0; } - if (mx || my) - if (CSQC_MouseMove(mx, my, mouse->qdeviceid)) + //if game is not focused, kill any mouse look + if (Key_Dest_Has(~kdm_game)) { mx = 0; my = 0; @@ -487,12 +515,109 @@ void IN_MoveMouse(struct mouse_s *mouse, float *movements, int pnum) } } -void IN_Move (float *movements, int pnum) +static float joydeadzone(float mag, float deadzone) +{ + if (mag > 1) //erg? + mag = 1; + if (mag > deadzone) + { + mag -= deadzone; + mag = mag / (1.f-deadzone); + } + else + mag = 0; + return mag; +} + +void IN_MoveJoystick(struct joy_s *joy, float *movements, int pnum, float frametime) +{ + float mag; + vec3_t jlook, jstrafe; + + int wpnum; + + /*each device will be processed when its player comes to be processed*/ + wpnum = cl.splitclients; + if (wpnum < 1) + wpnum = 1; + if (cl_forcesplitclient.ival) + wpnum = (cl_forcesplitclient.ival-1) % wpnum; + else + wpnum = joy->qdeviceid % wpnum; + if (wpnum != pnum) + return; + + jlook[0] = joy->axis[0]; + jlook[1] = joy->axis[1]; + jlook[2] = joy->axis[2]; + + jstrafe[0] = joy->axis[3]; + jstrafe[1] = joy->axis[4]; + jstrafe[2] = joy->axis[5]; + + //uses a radial deadzone for x+y axis, and separate out the z axis, just because most controllers are 2d affairs with any 3rd axis being a separate knob. + //deadzone values are stolen from microsoft's xinput documentation. they seem quite large to me - I guess that means that xbox controllers are just dodgy imprecise crap with excessive amounts of friction and finger grease. + mag = joydeadzone(sqrt(jlook[0]*jlook[0] + jlook[1]*jlook[1]), 0.239); + mag = pow(mag, 2); + jlook[0] *= mag; + jlook[1] *= mag; + mag = joydeadzone(fabs(jlook[2]), 0.00092); + jlook[2] *= mag; + + mag = joydeadzone(sqrt(jstrafe[0]*jstrafe[0] + jstrafe[1]*jstrafe[1]), 0.265); + mag = pow(mag, 2); + jstrafe[0] *= mag; + jstrafe[1] *= mag; + mag = joydeadzone(fabs(jstrafe[2]), 0.00092); + jstrafe[2] *= mag; + + if (Key_Dest_Has(~kdm_game)) + { + VectorClear(jlook); + VectorClear(jstrafe); + } + + VectorScale(jlook, frametime, jlook); + VectorScale(jstrafe, frametime, jstrafe); + + if (!movements) //if this is null, gamecode should still get inputs, just no camera looking or anything. + return; + + //angle changes + cl.playerview[pnum].viewanglechange[YAW] -= m_yaw.value * jlook[0]; + cl.playerview[pnum].viewanglechange[PITCH] += m_pitch.value * jlook[1]; +// cl.playerview[pnum].viewanglechange[ROLL] += m_roll.value * jlook[2]; //this would be too weird. + + if (in_mlook.state[pnum] & 1) + V_StopPitchDrift (&cl.playerview[pnum]); + + //movement + movements[1] += m_side.value * jstrafe[0]; //x=right=1 + movements[0] -= m_forward.value * jstrafe[1]; //y=forward=0 + movements[2] += m_side.value * jstrafe[2]; //z=up=2 +} + +void IN_Move (float *movements, int pnum, float frametime) { int i; INS_Move(movements, pnum); for (i = 0; i < MAXPOINTERS; i++) IN_MoveMouse(&ptr[i], movements, pnum); + + for (i = 0; i < MAXJOYSTICKS; i++) + IN_MoveJoystick(&joy[i], movements, pnum, frametime); +} + +void IN_JoystickAxisEvent(int devid, int axis, float value) +{ + struct eventlist_s *ev = in_newevent(); + if (!ev) + return; + ev->type = IEV_JOYAXIS; + ev->devid = devid; + ev->joy.axis = axis; + ev->joy.value = value; + in_finishevent(); } void IN_KeyEvent(int devid, int down, int keycode, int unicode) diff --git a/engine/client/in_sdl.c b/engine/client/in_sdl.c index c37e8044..131c3390 100644 --- a/engine/client/in_sdl.c +++ b/engine/client/in_sdl.c @@ -48,6 +48,214 @@ void IN_DeactivateMouse(void) #endif } +#if SDL_MAJOR_VERSION >= 2 +#define MAX_JOYSTICKS 4 +static struct sdljoy_s +{ + //fte doesn't distinguish between joysticks and controllers. + //in sdl, controllers are some glorified version of joysticks apparently. + char *devname; + SDL_Joystick *joystick; + SDL_GameController *controller; + SDL_JoystickID id; +} sdljoy[MAX_JOYSTICKS]; +//the enumid is the value for the open function rather than the working id. +static void J_ControllerAdded(int enumid) +{ + char *cname; + int i; + for (i = 0; i < MAX_JOYSTICKS; i++) + if (j_controller[i] == NULL) + break; + if (i == MAX_JOYSTICKS) + return; + + sdljoy[i].controller = SDL_GameControllerOpen(enumid); + if (!sdljoy[i].controller) + return; + sdljoy[i].joystick = SDL_GameControllerGetJoystick(sdljoy[i].controller); + sdljoy[i].id = SDL_JoystickInstanceID(sdljoy[i].joystick); + + cname = SDL_GameControllerName(sdljoy[i].controller); + if (!cname) + cname = "Unknown Controller"; + Con_Printf("Found new controller (%i): %s\n", i, cname); + sdljoy[i].devname = Z_StrDup(cname); +} +static void J_JoystickAdded(int enumid) +{ + char *cname; + int i; + for (i = 0; i < MAX_JOYSTICKS; i++) + if (sdljoy[i].joystick == NULL) + break; + if (i == MAX_JOYSTICKS) + return; + + sdljoy[i].joystick = SDL_JoystickOpen(enumid); + if (!sdljoy[i].joystick) + return; + sdljoy[i].id = SDL_JoystickInstanceID(sdljoy[i].joystick); + + cname = SDL_GameControllerName(sdljoy[i].controller); + if (!cname) + cname = "Unknown Joystick"; + Con_Printf("Found new joystick (%i): %s\n", i, cname); +} +static struct sdljoy_s *J_DevId(int jid) +{ + for (i = 0; i < MAX_JOYSTICKS; i++) + if (sdljoy[i].joystick && sdljoy[i].id == jid) + return &sdljoy[i]; + return NULL; +} +static void J_ControllerAxis(int jid, int axis, int value) +{ + int axismap[] = {0,1,3,4,2,5}; + + struct sdljoy_s *joy = J_DevId(jid); + if (joy && axis < sizeof(axismap)/sizeof(axismap[0])) + IN_JoystickAxisEvent(joy - sdljoy, axismap[axis], value / 32767.0); +} +static void J_JoystickAxis(int jid, int axis, int value) +{ + int axismap[] = {0,1,3,4,2,5}; + + struct sdljoy_s *joy = J_DevId(jid); + if (joy && axis < sizeof(axismap)/sizeof(axismap[0])) + IN_JoystickAxisEvent(joy - sdljoy, axismap[axis], value / 32767.0); +} +//we don't do hats and balls and stuff. +static void J_ControllerButton(int jid, int button, qboolean pressed) +{ + //controllers have reliable button maps. + //but that doesn't meant that fte has specific k_ names for those buttons, but the mapping should be reliable, at least until they get mapped to proper k_ values. + int buttonmap[] = { +#if 0 + //NOTE: DP has specific 'X360' buttons for many of these. of course, its not an exact mapping... + K_X360_A, /*SDL_CONTROLLER_BUTTON_A*/ + K_X360_B, /*SDL_CONTROLLER_BUTTON_B*/ + K_X360_X, /*SDL_CONTROLLER_BUTTON_X*/ + K_X360_Y, /*SDL_CONTROLLER_BUTTON_Y*/ + K_X360_BACK, /*SDL_CONTROLLER_BUTTON_BACK*/ + K_AUX2, /*SDL_CONTROLLER_BUTTON_GUIDE*/ + K_X360_START, /*SDL_CONTROLLER_BUTTON_START*/ + K_X360_LEFT_THUMB, /*SDL_CONTROLLER_BUTTON_LEFTSTICK*/ + K_X360_RIGHT_THUMB, /*SDL_CONTROLLER_BUTTON_RIGHTSTICK*/ + K_X360_LEFT_SHOULDER, /*SDL_CONTROLLER_BUTTON_LEFTSHOULDER*/ + K_X360_RIGHT_SHOULDER, /*SDL_CONTROLLER_BUTTON_RIGHTSHOULDER*/ + K_X360_DPAD_UP, /*SDL_CONTROLLER_BUTTON_DPAD_UP*/ + K_X360_DPAD_DOWN, /*SDL_CONTROLLER_BUTTON_DPAD_DOWN*/ + K_X360_DPAD_LEFT, /*SDL_CONTROLLER_BUTTON_DPAD_LEFT*/ + K_X360_DPAD_RIGHT /*SDL_CONTROLLER_BUTTON_DPAD_RIGHT*/ +#else + K_JOY1, /*SDL_CONTROLLER_BUTTON_A*/ + K_JOY2, /*SDL_CONTROLLER_BUTTON_B*/ + K_JOY3, /*SDL_CONTROLLER_BUTTON_X*/ + K_JOY4, /*SDL_CONTROLLER_BUTTON_Y*/ + K_AUX1, /*SDL_CONTROLLER_BUTTON_BACK*/ + K_AUX2, /*SDL_CONTROLLER_BUTTON_GUIDE*/ + K_AUX3, /*SDL_CONTROLLER_BUTTON_START*/ + K_AUX4, /*SDL_CONTROLLER_BUTTON_LEFTSTICK*/ + K_AUX5, /*SDL_CONTROLLER_BUTTON_RIGHTSTICK*/ + K_AUX6, /*SDL_CONTROLLER_BUTTON_LEFTSHOULDER*/ + K_AUX7, /*SDL_CONTROLLER_BUTTON_RIGHTSHOULDER*/ + K_AUX8, /*SDL_CONTROLLER_BUTTON_DPAD_UP*/ + K_AUX9, /*SDL_CONTROLLER_BUTTON_DPAD_DOWN*/ + K_AUX10, /*SDL_CONTROLLER_BUTTON_DPAD_LEFT*/ + K_AUX11 /*SDL_CONTROLLER_BUTTON_DPAD_RIGHT*/ +#endif + }; + + struct sdljoy_s *joy = J_DevId(jid); + if (joy && button < sizeof(buttonmap)/sizeof(buttonmap[0])) + IN_KeyEvent(joy - sdljoy, pressed, buttonmap[button], 0); +} +static void J_JoystickButton(int jid, int button, qboolean pressed) +{ + //generic joysticks have no specific mappings. they're really random like that. + int buttonmap[] = { + K_JOY1, + K_JOY2, + K_JOY3, + K_JOY4, + K_AUX1, + K_AUX2, + K_AUX3, + K_AUX4, + K_AUX5, + K_AUX6, + K_AUX7, + K_AUX8, + K_AUX9, + K_AUX10, + K_AUX11, + K_AUX12, + K_AUX13, + K_AUX14, + K_AUX15, + K_AUX16, + K_AUX17, + K_AUX18, + K_AUX19, + K_AUX20, + K_AUX21, + K_AUX22, + K_AUX23, + K_AUX24, + K_AUX25, + K_AUX26, + K_AUX27, + K_AUX28, + K_AUX29, + K_AUX30, + K_AUX31, + K_AUX32 + }; + + struct sdljoy_s *joy = J_DevId(jid); + if (joy && button < sizeof(buttonmap)/sizeof(buttonmap[0])) + IN_KeyEvent(joy - sdljoy, pressed, buttonmap[button], 0); +} +static void J_Kill(int jid, qboolean verbose) +{ + int i; + struct sdljoy_s *joy = J_DevId(jid); + + if (!joy) + return; + + //make sure all the axis are nulled out, to avoid surprises. + for (i = 0; i < 6; i++) + IN_JoystickAxisEvent(joy - sdljoy, i, 0); + + if (joy->controller) + { + for (i = 0; i < 32; i++) + J_ControllerButton(jid, i, false); + Con_Printf("Controller unplugged(%i): %s\n", joy - sdljoy, joy->devname); + SDL_GameControllerClose(joy->controller); + } + else + { + for (i = 0; i < 32; i++) + J_JoystickButton(jid, i, false); + Con_Printf("Joystick unplugged(%i): %s\n", joy - sdljoy, joy->devname); + SDL_JoystickClose(joy->joystick); + } + joy->controller = NULL; + joy->joystick = NULL; + Z_Free(joy->devname); + joy->devname = NULL; +} +static void J_KillAll(void) +{ + int i; + for (i = 0; i < MAX_JOYSTICKS; i++) + J_Kill(sdljoy[i].id, false); +} +#endif + #if SDL_MAJOR_VERSION >= 2 unsigned int MySDL_MapKey(unsigned int sdlkey) { @@ -620,6 +828,41 @@ void Sys_SendKeyEvents(void) case SDL_QUIT: Cbuf_AddText("quit\n", RESTRICT_LOCAL); break; + +#if SDL_MAJOR_VERSION >= 2 + //actually, joysticks *should* work with sdl1 as well, but there are some differences (like no hot plugging, I think). + case SDL_JOYAXISMOTION: + break; +// case SDL_JOYBALLMOTION: +// case SDL_JOYHATMOTION: + break; + case SDL_JOYBUTTONDOWN: + case SDL_JOYBUTTONUP: + J_JoystickButton(event.jbutton.which, event.jbutton.button, event.type==SDL_CONTROLLERBUTTONDOWN); + break; + case SDL_JOYDEVICEADDED: + J_JoystickAdded(event.jdevice.which); + break; + case SDL_JOYDEVICEREMOVED: + J_Kill(event.jdevice.which, true); + break; + + case SDL_CONTROLLERAXISMOTION: + J_ControllerAxis(event.caxis.which, event.caxis.axis, event.caxis.value); + break; + case SDL_CONTROLLERBUTTONDOWN: + case SDL_CONTROLLERBUTTONUP: + J_ControllerButton(event.cbutton.which, event.cbutton.button, event.type==SDL_CONTROLLERBUTTONDOWN); + break; + case SDL_CONTROLLERDEVICEADDED: + J_ControllerAdded(event.cdevice.which); + break; + case SDL_CONTROLLERDEVICEREMOVED: + J_Kill(event.cdevice.which, true); + break; +// case SDL_CONTROLLERDEVICEREMAPPED: +// break; +#endif } } } @@ -632,6 +875,11 @@ void Sys_SendKeyEvents(void) void INS_Shutdown (void) { IN_DeactivateMouse(); + +#if SDL_MAJOR_VERSION >= 2 + J_KillAll(); + SDL_QuitSubSystem(SDL_INIT_JOYSTICK|SDL_INIT_GAMECONTROLLER); +#endif } void INS_ReInit (void) @@ -654,6 +902,9 @@ void INS_Move(float *movements, int pnum) } void INS_Init (void) { +#if SDL_MAJOR_VERSION >= 2 + SDL_InitSubSystem(SDL_INIT_JOYSTICK|SDL_INIT_GAMECONTROLLER); +#endif } void INS_Accumulate(void) //input polling { diff --git a/engine/client/in_win.c b/engine/client/in_win.c index a8d872ef..477a5c25 100644 --- a/engine/client/in_win.c +++ b/engine/client/in_win.c @@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "quakedef.h" #include "winquake.h" //#include "dosisms.h" - +#ifndef WINRT #define USINGRAWINPUT #ifdef USINGRAWINPUT @@ -2113,3 +2113,4 @@ void INS_TranslateKeyEvent(WPARAM wParam, LPARAM lParam, qboolean down, int qdev Key_Event (qdeviceid, qcode, unicode, down); } +#endif diff --git a/engine/client/input.h b/engine/client/input.h index 0119fce8..bb21ae92 100644 --- a/engine/client/input.h +++ b/engine/client/input.h @@ -28,7 +28,10 @@ void IN_Shutdown (void); void IN_Commands (void); // oportunity for devices to stick commands on the script buffer -void IN_Move (float *movements, int pnum); +qboolean IN_MouseDevIsTouch(int devid); //check if a mouse devid is a touch screen, and thus if we should check the cursor and simulate a ui event or not +int IN_TranslateMButtonPress(int devid); //allow the touchscreen code to swallow mouse1 as a begin-looking event + +void IN_Move (float *movements, int pnum, float frametime); // add additional movement on top of the keyboard move cmd extern cvar_t in_xflip; diff --git a/engine/client/keys.c b/engine/client/keys.c index 6ddef89e..461e29e3 100644 --- a/engine/client/keys.c +++ b/engine/client/keys.c @@ -53,7 +53,10 @@ int keyshift[K_MAX]; // key to map to if shift held down in console int key_repeats[K_MAX]; // if > 1, it is autorepeating qboolean keydown[K_MAX]; -qboolean deltaused[K_MAX][KEY_MODIFIERSTATES]; +#define MAX_INDEVS 8 + +char *releasecommand[K_MAX][MAX_INDEVS]; //this is the console command to be invoked when the key is released. should free it. +qbyte releasecommandlevel[K_MAX][MAX_INDEVS]; //and this is the cbuf level it is to be run at. void Con_Selectioncolour_Callback(struct cvar_s *var, char *oldvalue); @@ -721,6 +724,12 @@ void Key_DefaultLinkClicked(char *text, char *info) Cbuf_AddText(va("\nplaydemo %s\n", c), RESTRICT_LOCAL); return; } + c = Info_ValueForKey(info, "map"); + if (*c && !strchr(c, ';') && !strchr(c, '\n')) + { + Cbuf_AddText(va("\nmap %s\n", c), RESTRICT_LOCAL); + return; + } c = Info_ValueForKey(info, "cmd"); if (*c && !strchr(c, ';') && !strchr(c, '\n')) { @@ -751,12 +760,19 @@ void Key_DefaultLinkClicked(char *text, char *info) Con_Footerf(false, "%s", c); return; } - if (!*info && *text == '/') + + //if there's no info and the text starts with a leading / then insert it as a suggested/completed console command + //skip any leading colour code. + if (text[0] == '^' && text[1] >= '0' && text[1] <= '9') + text+=2; + if (*text == '/') { + int tlen = info - text; Z_Free(key_lines[edit_line]); - key_lines[edit_line] = BZ_Malloc(strlen(text) + 2); + key_lines[edit_line] = BZ_Malloc(tlen + 2); key_lines[edit_line][0] = ']'; - strcpy(key_lines[edit_line]+1, text); + memcpy(key_lines[edit_line]+1, text, tlen); + key_lines[edit_line][1+tlen] = 0; key_linepos = strlen(key_lines[edit_line]); return; } @@ -1424,7 +1440,7 @@ the given string. Single ascii characters return themselves, while the K_* names are matched up. =================== */ -int Key_StringToKeynum (char *str, int *modifier) +int Key_StringToKeynum (const char *str, int *modifier) { keyname_t *kn; char *underscore; @@ -1670,9 +1686,12 @@ void Key_BindLevel_f (void) return; } + if (modifier == ~0) //modifier unspecified. default to no modifier + modifier = 0; + if (c == 2) { - if (keybindings[b]) + if (keybindings[b][modifier]) Con_Printf ("\"%s\" (%i)= \"%s\"\n", Cmd_Argv(1), bindcmdlevel[b][modifier], keybindings[b][modifier] ); else Con_Printf ("\"%s\" is not bound\n", Cmd_Argv(1) ); @@ -1899,89 +1918,27 @@ Should NOT be called during an interrupt! */ void Key_Event (int devid, int key, unsigned int unicode, qboolean down) { - char *kb; + int bl, bkey; + char *dc, *uc; char p[16]; - char cmd[1024]; - int keystate, oldstate; + int modifierstate; int conkey = consolekeys[key] || (unicode && (key != '`' || key_linepos>1)); //if the input line is empty, allow ` to toggle the console, otherwise enter it as actual text. // Con_Printf ("%i : %i : %i\n", key, unicode, down); //@@@ - oldstate = KeyModifier(keydown[K_LSHIFT]|keydown[K_RSHIFT], keydown[K_LALT]|keydown[K_RALT], keydown[K_LCTRL]|keydown[K_RCTRL]); + //bug: my keyboard doesn't fire release events if the other shift is already pressed. + //hack around that by just force-releasing eg left if right is pressed, but only on inital press to avoid potential infinite loops if the state got bad. + //ctrl+alt don't seem to have the problem. + //you can still see a difference in that + if (key == K_LSHIFT && !keydown[K_LSHIFT] && keydown[K_RSHIFT]) + Key_Event(devid, K_RSHIFT, 0, false); + if (key == K_RSHIFT && !keydown[K_RSHIFT] && keydown[K_LSHIFT]) + Key_Event(devid, K_LSHIFT, 0, false); + + modifierstate = KeyModifier(keydown[K_LSHIFT]|keydown[K_RSHIFT], keydown[K_LALT]|keydown[K_RALT], keydown[K_LCTRL]|keydown[K_RCTRL]); keydown[key] = down; - if (key == K_LSHIFT || key == K_RSHIFT || key == K_LALT || key == K_RALT || key == K_LCTRL || key == K_RCTRL) - { - int k; - - keystate = KeyModifier(keydown[K_LSHIFT]|keydown[K_RSHIFT], keydown[K_LALT]|keydown[K_RALT], keydown[K_LCTRL]|keydown[K_RCTRL]); - - for (k = 0; k < K_MAX; k++) - { //go through the old state removing all depressed keys. they are all up now. - - if (k == K_LSHIFT || k == K_RSHIFT || k == K_LALT || k == K_RALT || k == K_LCTRL || k == K_RCTRL) - continue; - - if (deltaused[k][oldstate]) - { - if (keybindings[k][oldstate] == keybindings[k][keystate] || !strcmp(keybindings[k][oldstate], keybindings[k][keystate])) - { //bindings match. skip this key -// Con_Printf ("keeping bind %i\n", k); //@@@ - deltaused[k][oldstate] = false; - deltaused[k][keystate] = true; - continue; - } - -// Con_Printf ("removing bind %i\n", k); //@@@ - - deltaused[k][oldstate] = false; - - kb = keybindings[k][oldstate]; - if (kb && kb[0] == '+') - { - Q_snprintfz (cmd, sizeof(cmd), "-%s %i\n", kb+1, k+oldstate*256); - Cbuf_AddText (cmd, bindcmdlevel[k][oldstate]); - } - if (keyshift[k] != k) - { - kb = keybindings[keyshift[k]][oldstate]; - if (kb && kb[0] == '+') - { - Q_snprintfz (cmd, sizeof(cmd), "-%s %i\n", kb+1, k+oldstate*256); - Cbuf_AddText (cmd, bindcmdlevel[k][oldstate]); - } - } - - if (keydown[k] && !Key_Dest_Has(~kdm_game)) - { - deltaused[k][keystate] = true; - - // Con_Printf ("adding bind %i\n", k); //@@@ - - kb = keybindings[k][keystate]; - if (kb) - { - if (kb[0] == '+') - { // button commands add keynum as a parm - Q_snprintfz (cmd, sizeof(cmd), "%s %i\n", kb, k+keystate*256); - Cbuf_AddText (cmd, bindcmdlevel[k][keystate]); - } - else - { - Cbuf_AddText (kb, bindcmdlevel[k][keystate]); - Cbuf_AddText ("\n", bindcmdlevel[k][keystate]); - } - } - } - } - } - - keystate = oldstate = 0; - } - else - keystate = oldstate; - if (!down) key_repeats[key] = 0; @@ -2011,7 +1968,8 @@ void Key_Event (int devid, int key, unsigned int unicode, qboolean down) { if (down) { - Con_ToggleConsole_Force(); + if (!Key_Dest_Has(kdm_console)) //don't toggle it when the console is already down. this allows typing blind to not care if its already active. + Con_ToggleConsole_Force(); return; } } @@ -2104,35 +2062,12 @@ void Key_Event (int devid, int key, unsigned int unicode, qboolean down) Media_Send_KeyEvent(NULL, key, unicode, down?0:1); #endif - if (!deltaused[key][keystate]) //this wasn't down, so don't make it leave down state. - return; - deltaused[key][keystate] = false; - - if (key == K_RALT) //simulate a singular alt for binds. really though, this code should translate to csqc/menu keycodes and back to resolve the weirdness instead. - key = K_ALT; - if (key == K_RCTRL) //simulate a singular alt for binds. really though, this code should translate to csqc/menu keycodes and back to resolve the weirdness instead. - key = K_CTRL; - if (key == K_RSHIFT)//simulate a singular alt for binds. really though, this code should translate to csqc/menu keycodes and back to resolve the weirdness instead. - key = K_SHIFT; - - if (devid) - Q_snprintfz (p, sizeof(p), "p %i ", devid+1); - else - *p = 0; - kb = keybindings[key][keystate]; - if (kb && kb[0] == '+') + uc = releasecommand[key][devid%MAX_INDEVS]; + if (uc) //this wasn't down, so don't crash on bad commands. { - Q_snprintfz (cmd, sizeof(cmd), "-%s%s %i\n", p, kb+1, key+oldstate*256); - Cbuf_AddText (cmd, bindcmdlevel[key][keystate]); - } - if (keyshift[key] != key) - { - kb = keybindings[keyshift[key]][keystate]; - if (kb && kb[0] == '+') - { - Q_snprintfz (cmd, sizeof(cmd), "-%s%s %i\n", p, kb+1, key+oldstate*256); - Cbuf_AddText (cmd, bindcmdlevel[key][keystate]); - } + releasecommand[key][devid%MAX_INDEVS] = NULL; + Cbuf_AddText (uc, releasecommandlevel[key][devid%MAX_INDEVS]); + Z_Free(uc); } return; } @@ -2197,47 +2132,88 @@ void Key_Event (int devid, int key, unsigned int unicode, qboolean down) if (key_repeats[key] > 1) return; - deltaused[key][keystate] = true; - if (devid) Q_snprintfz (p, sizeof(p), "p %i ", devid+1); else *p = 0; - if (key == K_RALT) //simulate a singular alt for binds. really though, this code should translate to csqc/menu keycodes and back to resolve the weirdness instead. - key = K_ALT; - if (key == K_RCTRL) //simulate a singular alt for binds. really though, this code should translate to csqc/menu keycodes and back to resolve the weirdness instead. - key = K_CTRL; - if (key == K_RSHIFT)//simulate a singular alt for binds. really though, this code should translate to csqc/menu keycodes and back to resolve the weirdness instead. - key = K_SHIFT; + dc = keybindings[key][modifierstate]; + bl = bindcmdlevel[key][modifierstate]; - if ((key == K_MOUSE1 || key == K_MOUSE2) && 1) + //simulate singular shift+alt+ctrl for binds (no left/right). really though, this code should translate to csqc/menu keycodes and back to resolve the weirdness instead. + if (key == K_RALT && (!dc || !*dc)) { - int nkey = Sbar_TranslateHudClick(); - if (nkey) - { - //handle +/- events 'properly' the only safe way we can - by releasing them instantly and discarding the mouse-up event. - Key_Event(devid, nkey, 0, true); - Key_Event(devid, nkey, 0, false); - return; - } + bkey = K_LALT; + dc = keybindings[bkey][modifierstate]; + bl = bindcmdlevel[bkey][modifierstate]; } - - kb = keybindings[key][keystate]; - if (kb) + else if (key == K_RCTRL && (!dc || !*dc)) { - if (kb[0] == '+') - { // button commands add keynum as a parm - Q_snprintfz (cmd, sizeof(cmd), "+%s%s %i\n", p, kb+1, key+oldstate*256); - Cbuf_AddText (cmd, bindcmdlevel[key][keystate]); - } + bkey = K_LCTRL; + dc = keybindings[bkey][modifierstate]; + bl = bindcmdlevel[bkey][modifierstate]; + } + else if (key == K_RSHIFT && (!dc || !*dc)) + { + bkey = K_LSHIFT; + dc = keybindings[bkey][modifierstate]; + bl = bindcmdlevel[bkey][modifierstate]; + } + else + bkey = key; + + if (key == K_MOUSE1 && IN_MouseDevIsTouch(devid)) + { + dc = SCR_ShowPics_ClickCommand(mousecursor_x, mousecursor_y); + if (dc) + bl = RESTRICT_INSECURE; else { - if (*p)Cbuf_AddText (p, bindcmdlevel[key][keystate]); - Cbuf_AddText (kb, bindcmdlevel[key][keystate]); - Cbuf_AddText ("\n", bindcmdlevel[key][keystate]); + int bkey = Sbar_TranslateHudClick(); + if (bkey) + { + dc = keybindings[bkey][modifierstate]; + bl = bindcmdlevel[bkey][modifierstate]; + } + } + + if (!dc) //no buttons to click, + { + bkey = IN_TranslateMButtonPress(devid); + if (bkey) + { + dc = keybindings[bkey][modifierstate]; + bl = bindcmdlevel[bkey][modifierstate]; + } + else + return; } } + + if (!dc) + dc = ""; + if (dc[0] == '+') + { + uc = va("-%s%s %i\n", p, dc+1, bkey); + dc = va("+%s%s %i\n", p, dc+1, bkey); + } + else + { + uc = NULL; + dc = va("%s%s\n", p, dc); + } + + //don't mess up if we ran out of devices, just silently release the one that it conflicted with (and only if its in conflict). + if (releasecommand[key][devid%MAX_INDEVS] && (!uc || strcmp(uc, releasecommand[key][devid%MAX_INDEVS]))) + { + Cbuf_AddText (releasecommand[key][devid%MAX_INDEVS], releasecommandlevel[key][devid%MAX_INDEVS]); + Z_Free(releasecommand[key][devid%MAX_INDEVS]); + releasecommand[key][devid%MAX_INDEVS] = NULL; + } + Cbuf_AddText (dc, bl); + if (uc) + releasecommand[key][devid%MAX_INDEVS] = Z_StrDup(uc); + releasecommandlevel[key][devid%MAX_INDEVS] = bl; } /* diff --git a/engine/client/m_download.c b/engine/client/m_download.c index 50897cb3..bbdfbec0 100644 --- a/engine/client/m_download.c +++ b/engine/client/m_download.c @@ -429,7 +429,7 @@ void M_AddItemsToDownloadMenu(menu_t *m) break; if (!mo) { - MC_AddConsoleCommand(m, 6*8, y, path+prefixlen, va("menu_download \"%s/\"", path)); + MC_AddConsoleCommand(m, 6*8, 170, y, path+prefixlen, va("menu_download \"%s/\"", path)); y += 8; } } @@ -445,9 +445,9 @@ void M_AddItemsToDownloadMenu(menu_t *m) } y+=4; - MC_AddCommand(m, 0, y, " Back", MD_PopMenu); + MC_AddCommand(m, 0, 170, y, "Back", MD_PopMenu); y+=8; - MC_AddCommand(m, 0, y, " Apply", MD_ApplyDownloads); + MC_AddCommand(m, 0, 170, y, "Apply", MD_ApplyDownloads); /* for (pn = 1, p = availablepackages; p && pn < info->firstpackagenum ; p=p->next, pn++) @@ -785,8 +785,8 @@ void Menu_DownloadStuff_f (void) downloadablelistreceived[i] = 0; } - MC_AddWhiteText(menu, 24, 8, "Downloads", false); - MC_AddWhiteText(menu, 16, 24, "\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37", false); + MC_AddWhiteText(menu, 24, 170, 8, "Downloads", false); + MC_AddWhiteText(menu, 16, 170, 24, "\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37", false); { static qboolean loadedinstalled; diff --git a/engine/client/m_items.c b/engine/client/m_items.c index f1bf16a6..b7396068 100644 --- a/engine/client/m_items.c +++ b/engine/client/m_items.c @@ -408,20 +408,19 @@ static void MenuDrawItems(int xpos, int ypos, menuoption_t *option, menu_t *menu { case mt_text: if (!option->text.text) - { + { //blinking cursor image hack if ((int)(realtime*4)&1) Draw_FunString(xpos+option->common.posx, ypos+option->common.posy, "^Ue00d"); } + else if (option->common.width) + Draw_FunStringWidth(xpos + option->common.posx, ypos+option->common.posy, option->text.text, option->common.width, true, option->text.isred); else if (option->text.isred) Draw_AltFunString(xpos+option->common.posx, ypos+option->common.posy, option->text.text); else Draw_FunString(xpos+option->common.posx, ypos+option->common.posy, option->text.text); break; case mt_button: - if (!menu->cursoritem && menu->selecteditem == option) - Draw_AltFunString(xpos+option->common.posx, ypos+option->common.posy, option->button.text); - else - Draw_FunString(xpos+option->common.posx, ypos+option->common.posy, option->button.text); + Draw_FunStringWidth(xpos + option->common.posx, ypos+option->common.posy, option->button.text, option->common.width, true, !menu->cursoritem && menu->selecteditem == option); break; case mt_hexen2buttonbigfont: Draw_Hexen2BigFontString(xpos+option->common.posx, ypos+option->common.posy, option->button.text); @@ -661,13 +660,14 @@ static void MenuDraw(menu_t *menu) } -menutext_t *MC_AddWhiteText(menu_t *menu, int x, int y, const char *text, qboolean rightalign) +menutext_t *MC_AddWhiteText(menu_t *menu, int lhs, int rhs, int y, const char *text, qboolean rightalign) { menutext_t *n = Z_Malloc(sizeof(menutext_t)); n->common.type = mt_text; n->common.iszone = true; - n->common.posx = x; + n->common.posx = lhs; n->common.posy = y; + n->common.width = rhs?rhs-lhs:0; n->text = text; if (rightalign && text) @@ -678,13 +678,14 @@ menutext_t *MC_AddWhiteText(menu_t *menu, int x, int y, const char *text, qboole return n; } -menutext_t *MC_AddBufferedText(menu_t *menu, int x, int y, const char *text, qboolean rightalign, qboolean red) +menutext_t *MC_AddBufferedText(menu_t *menu, int lhs, int rhs, int y, const char *text, qboolean rightalign, qboolean red) { menutext_t *n = Z_Malloc(sizeof(menutext_t) + strlen(text)+1); n->common.type = mt_text; n->common.iszone = true; - n->common.posx = x; + n->common.posx = lhs; n->common.posy = y; + n->common.width = rhs?rhs-lhs:0; n->text = (char *)(n+1); strcpy((char *)(n+1), text); n->isred = red; @@ -697,10 +698,10 @@ menutext_t *MC_AddBufferedText(menu_t *menu, int x, int y, const char *text, qbo return n; } -menutext_t *MC_AddRedText(menu_t *menu, int x, int y, const char *text, qboolean rightalign) +menutext_t *MC_AddRedText(menu_t *menu, int lhs, int rhs, int y, const char *text, qboolean rightalign) { menutext_t *n; - n = MC_AddWhiteText(menu, x, y, text, false); + n = MC_AddWhiteText(menu, lhs, rhs, y, text, false); n->isred = true; return n; } @@ -1138,15 +1139,15 @@ menucombo_t *MC_AddCvarCombo(menu_t *menu, int tx, int cx, int y, const char *ca return n; } -menubutton_t *MC_AddConsoleCommand(menu_t *menu, int x, int y, const char *text, const char *command) +menubutton_t *MC_AddConsoleCommand(menu_t *menu, int lhs, int rhs, int y, const char *text, const char *command) { menubutton_t *n = Z_Malloc(sizeof(menubutton_t)+strlen(text)+1+strlen(command)+1); n->common.type = mt_button; n->common.iszone = true; - n->common.posx = x; + n->common.posx = lhs; n->common.posy = y; n->common.height = 8; - n->common.width = strlen(text)*8; + n->common.width = rhs?rhs - lhs:strlen(text)*8; n->text = (char *)(n+1); strcpy((char *)(n+1), text); n->command = n->text + strlen(n->text)+1; @@ -1194,25 +1195,25 @@ menubutton_t *MC_AddConsoleCommandHexen2BigFont(menu_t *menu, int x, int y, cons return n; } -menubutton_t *MC_AddCommand(menu_t *menu, int x, int y, char *text, qboolean (*command) (union menuoption_s *,struct menu_s *,int)) +menubutton_t *MC_AddCommand(menu_t *menu, int lhs, int rhs, int y, char *text, qboolean (*command) (union menuoption_s *,struct menu_s *,int)) { menubutton_t *n = Z_Malloc(sizeof(menubutton_t)); n->common.type = mt_button; n->common.iszone = true; - n->common.posx = x; + n->common.posx = lhs; n->common.posy = y; n->text = text; n->command = NULL; n->key = command; n->common.height = 8; - n->common.width = strlen(text)*8; + n->common.width = rhs?rhs-lhs:strlen(text)*8; n->common.next = menu->options; menu->options = (menuoption_t *)n; return n; } -menubutton_t *VARGS MC_AddConsoleCommandf(menu_t *menu, int x, int y, const char *text, char *command, ...) +menubutton_t *VARGS MC_AddConsoleCommandf(menu_t *menu, int lhs, int rhs, int y, const char *text, char *command, ...) { va_list argptr; static char string[1024]; @@ -1225,8 +1226,9 @@ menubutton_t *VARGS MC_AddConsoleCommandf(menu_t *menu, int x, int y, const char n = Z_Malloc(sizeof(menubutton_t) + strlen(string)+1); n->common.type = mt_button; n->common.iszone = true; - n->common.posx = x; + n->common.posx = lhs; n->common.posy = y; + n->common.width = rhs-lhs; n->text = text; n->command = (char *)(n+1); strcpy((char *)(n+1), string); @@ -1751,7 +1753,7 @@ typedef struct { menu_t *parent; } guiinfo_t; -qboolean MC_GuiKey(int key, menu_t *menu) +static qboolean MC_GuiKey(int key, menu_t *menu) { guiinfo_t *info = (guiinfo_t *)menu->data; switch(key) @@ -1798,7 +1800,7 @@ qboolean MC_GuiKey(int key, menu_t *menu) gui->text[2] = "Hello yet again"; for (y = 0, i = 0; gui->text[i]; i++, y+=1*8) { - info->op[i] = MC_AddRedText(info->dropout, 0, y, gui->text[i], false); + info->op[i] = MC_AddRedText(info->dropout, 0, 0, y, gui->text[i], false); } } break; @@ -1919,28 +1921,28 @@ void M_Menu_Main_f (void) MC_AddSelectablePicture(mainm, 68, 173, "pics/m_main_quit"); #ifndef CLIENTONLY - b = MC_AddConsoleCommand (mainm, 68, 13, "", "menu_single\n"); + b = MC_AddConsoleCommand (mainm, 68, 320, 13, "", "menu_single\n"); b->common.tooltip = "Singleplayer."; mainm->selecteditem = (menuoption_t *)b; b->common.width = 12*20; b->common.height = 32; #endif - b = MC_AddConsoleCommand (mainm, 68, 53, "", "menu_multi\n"); + b = MC_AddConsoleCommand (mainm, 68, 320, 53, "", "menu_multi\n"); b->common.tooltip = "Multiplayer."; #ifdef CLIENTONLY mainm->selecteditem = (menuoption_t *)b; #endif b->common.width = 12*20; b->common.height = 32; - b = MC_AddConsoleCommand (mainm, 68, 93, "", "menu_options\n"); + b = MC_AddConsoleCommand (mainm, 68, 320, 93, "", "menu_options\n"); b->common.tooltip = "Options."; b->common.width = 12*20; b->common.height = 32; - b = MC_AddConsoleCommand (mainm, 68, 133, "", "menu_video\n"); + b = MC_AddConsoleCommand (mainm, 68, 320, 133, "", "menu_video\n"); b->common.tooltip = "Video Options."; b->common.width = 12*20; b->common.height = 32; - b = MC_AddConsoleCommand (mainm, 68, 173, "", "menu_quit\n"); + b = MC_AddConsoleCommand (mainm, 68, 320, 173, "", "menu_quit\n"); b->common.tooltip = "Quit to DOS."; b->common.width = 12*20; b->common.height = 32; @@ -1997,12 +1999,12 @@ void M_Menu_Main_f (void) p = R2D_SafeCachePic("gfx/ttl_main.lmp"); if (!p) { - MC_AddRedText(mainm, 16, 0, "MAIN MENU", false); + MC_AddRedText(mainm, 16, 170, 0, "MAIN MENU", false); mainm->selecteditem = (menuoption_t *) - MC_AddConsoleCommand (mainm, 64, 32, "Join server", "menu_servers\n"); - MC_AddConsoleCommand (mainm, 64, 40, "Options", "menu_options\n"); - MC_AddConsoleCommand (mainm, 64, 48, "Quit", "menu_quit\n"); + MC_AddConsoleCommand (mainm, 64, 170, 32, "Join server", "menu_servers\n"); + MC_AddConsoleCommand (mainm, 64, 170, 40, "Options", "menu_options\n"); + MC_AddConsoleCommand (mainm, 64, 170, 48, "Quit", "menu_quit\n"); return; } mainm->key = MC_Main_Key; @@ -2031,12 +2033,12 @@ void M_Menu_Main_f (void) p = R2D_SafeCachePic("gfx/ttl_main.lmp"); if (!p) { - MC_AddRedText(mainm, 16, 0, "MAIN MENU", false); + MC_AddRedText(mainm, 16, 170, 0, "MAIN MENU", false); mainm->selecteditem = (menuoption_t *) - MC_AddConsoleCommand (mainm, 64, 32, "Join server", "menu_servers\n"); - MC_AddConsoleCommand (mainm, 64, 40, "Options", "menu_options\n"); - MC_AddConsoleCommand (mainm, 64, 48, "Quit", "menu_quit\n"); + MC_AddConsoleCommand (mainm, 64, 170, 32, "Join server", "menu_servers\n"); + MC_AddConsoleCommand (mainm, 64, 170, 40, "Options", "menu_options\n"); + MC_AddConsoleCommand (mainm, 64, 170, 48, "Quit", "menu_quit\n"); return; } mainm->key = MC_Main_Key; @@ -2048,32 +2050,32 @@ void M_Menu_Main_f (void) p = R2D_SafeCachePic("gfx/mainmenu.lmp"); - b=MC_AddConsoleCommand (mainm, 72, 32, "", "menu_single\n"); + b=MC_AddConsoleCommand (mainm, 72, 312, 32, "", "menu_single\n"); b->common.tooltip = "Start singleplayer Quake game."; mainm->selecteditem = (menuoption_t *)b; b->common.width = p->width; b->common.height = 20; - b=MC_AddConsoleCommand (mainm, 72, 52, "", "menu_multi\n"); + b=MC_AddConsoleCommand (mainm, 72, 312, 52, "", "menu_multi\n"); b->common.tooltip = "Multiplayer menu."; b->common.width = p->width; b->common.height = 20; - b=MC_AddConsoleCommand (mainm, 72, 72, "", "menu_options\n"); + b=MC_AddConsoleCommand (mainm, 72, 312, 72, "", "menu_options\n"); b->common.tooltip = "Options menu."; b->common.width = p->width; b->common.height = 20; if (m_helpismedia.value) { - b=MC_AddConsoleCommand(mainm, 72, 92, "", "menu_media\n"); + b=MC_AddConsoleCommand(mainm, 72, 312, 92, "", "menu_media\n"); b->common.tooltip = "Media menu."; } else { - b=MC_AddConsoleCommand(mainm, 72, 92, "", "help\n"); + b=MC_AddConsoleCommand(mainm, 72, 312, 92, "", "help\n"); b->common.tooltip = "Help menu."; } b->common.width = p->width; b->common.height = 20; - b=MC_AddConsoleCommand (mainm, 72, 112, "", "menu_quit\n"); + b=MC_AddConsoleCommand (mainm, 72, 312, 112, "", "menu_quit\n"); b->common.tooltip = "Exit to DOS."; b->common.width = p->width; b->common.height = 20; @@ -2112,10 +2114,10 @@ int MC_AddBulk(struct menu_s *menu, menubulk_t *bulk, int xstart, int xtextend, control = NULL; continue; case 0: // white text - control = (union menuoption_s *)MC_AddWhiteText(menu, x, y, bulk->text, bulk->rightalign); + control = (union menuoption_s *)MC_AddWhiteText(menu, xleft, xtextend, y, bulk->text, bulk->rightalign); break; case 1: // red text - control = (union menuoption_s *)MC_AddRedText(menu, x, y, bulk->text, bulk->rightalign); + control = (union menuoption_s *)MC_AddRedText(menu, xleft, xtextend, y, bulk->text, bulk->rightalign); break; case 2: // spacing spacing = bulk->spacing; @@ -2128,10 +2130,10 @@ int MC_AddBulk(struct menu_s *menu, menubulk_t *bulk, int xstart, int xtextend, { default: case 0: // console command - control = (union menuoption_s *)MC_AddConsoleCommand(menu, x, y, bulk->text, bulk->consolecmd); + control = (union menuoption_s *)MC_AddConsoleCommand(menu, xleft, xtextend, y, bulk->text, bulk->consolecmd); break; case 1: // function command - control = (union menuoption_s *)MC_AddCommand(menu, x, y, bulk->text, bulk->command); + control = (union menuoption_s *)MC_AddCommand(menu, xleft, xtextend, y, bulk->text, bulk->command); break; } break; @@ -2190,6 +2192,6 @@ int MC_AddBulk(struct menu_s *menu, menubulk_t *bulk, int xstart, int xtextend, menu->selecteditem = selected; if (selected) selectedy = selected->common.posy; - menu->cursoritem = (menuoption_t*)MC_AddWhiteText(menu, xtextend + 8, selectedy, NULL, false); + menu->cursoritem = (menuoption_t*)MC_AddWhiteText(menu, xtextend + 8, 0, selectedy, NULL, false); return y; } diff --git a/engine/client/m_master.c b/engine/client/m_master.c index 8b141a44..0b8de325 100644 --- a/engine/client/m_master.c +++ b/engine/client/m_master.c @@ -666,7 +666,7 @@ void M_Menu_ServerList2_f(void) MC_AddCheckBoxFunc(menu, 128, 208, vid.height - 64+8*6, "Hide Empty", SL_ReFilter, 6); MC_AddCheckBoxFunc(menu, 128, 208, vid.height - 64+8*7, "Hide Full ", SL_ReFilter, 7); - MC_AddCommand(menu, 64, 0, info->refreshtext, SL_DoRefresh); + MC_AddCommand(menu, 64, 208, 0, info->refreshtext, SL_DoRefresh); info->filter[1] = !sb_hidenetquake.value; info->filter[2] = !sb_hidequakeworld.value; @@ -778,8 +778,8 @@ void M_QuickConnect_f(void) cust->common.height = 8; cust->common.width = vid.width-8; - MC_AddCommand(menu, 64, 128, "Refresh", SL_DoRefresh); - MC_AddCommand(menu, 64, 136, "Cancel", M_QuickConnect_Cancel); + MC_AddCommand(menu, 64, 0, 128, "Refresh", SL_DoRefresh); + MC_AddCommand(menu, 64, 0, 136, "Cancel", M_QuickConnect_Cancel); } diff --git a/engine/client/m_mp3.c b/engine/client/m_mp3.c index 90a30410..0e5f0c63 100644 --- a/engine/client/m_mp3.c +++ b/engine/client/m_mp3.c @@ -54,7 +54,7 @@ void Media_Clear (void) } //fake cd tracks. -qboolean Media_BackgroundTrack(char *track, char *looptrack) +qboolean Media_BackgroundTrack(const char *track, const char *looptrack) { unsigned int tracknum; #if !defined(NOMEDIA) @@ -206,10 +206,10 @@ void Media_EndedTrack(void) #include "winquake.h" -#ifdef _WIN32 +#if defined(_WIN32) && !defined(WINRT) #define WINAMP #endif -#if defined(_WIN32) +#if defined(_WIN32) && !defined(WINRT) #define WINAVI #endif @@ -1262,7 +1262,7 @@ struct cin_s void (*key) (struct cin_s *cin, int code, int unicode, int event); qboolean (*setsize) (struct cin_s *cin, int width, int height); void (*getsize) (struct cin_s *cin, int *width, int *height); - void (*changestream) (struct cin_s *cin, char *streamname); + void (*changestream) (struct cin_s *cin, const char *streamname); @@ -1761,7 +1761,7 @@ void Media_Plugin_GetSize(cin_t *cin, int *width, int *height) cin->plugin.funcs->getsize(cin->plugin.ctx, width, height); currentplug = oldplug; } -void Media_Plugin_ChangeStream(cin_t *cin, char *streamname) +void Media_Plugin_ChangeStream(cin_t *cin, const char *streamname) { struct plugin_s *oldplug = currentplug; currentplug = cin->plugin.plug; @@ -2579,18 +2579,26 @@ qboolean Media_ShowFilm(void) } Media_StopFilm(false); } - else + else if (cin) { + int cw = cin->outwidth, ch = cin->outheight; float ratiox, ratioy; if (cin->cursormove) cin->cursormove(cin, mousecursor_x/(float)vid.width, mousecursor_y/(float)vid.height); if (cin->setsize) cin->setsize(cin, vid.pixelwidth, vid.pixelheight); - ratiox = (float)cin->outwidth / vid.pixelwidth; - ratioy = (float)cin->outheight / vid.pixelheight; + //FIXME: should have a proper aspect ratio setting. RoQ files are always power of two, which makes things ugly. + if (1) + { + cw = 4; + ch = 3; + } - if (!cin->outheight || !cin->outwidth) + ratiox = (float)cw / vid.pixelwidth; + ratioy = (float)ch / vid.pixelheight; + + if (!ch || !cw) { R2D_ImageColours(0, 0, 0, 1); R2D_FillBlock(0, 0, vid.width, vid.height); @@ -2598,7 +2606,7 @@ qboolean Media_ShowFilm(void) } else if (ratiox > ratioy) { - int h = (vid.width * cin->outheight) / cin->outwidth; + int h = (vid.width * ch) / cw; int p = vid.height - h; //letterbox @@ -2611,7 +2619,7 @@ qboolean Media_ShowFilm(void) } else { - int w = (vid.height * cin->outwidth) / cin->outheight; + int w = (vid.height * cw) / ch; int p = vid.width - w; //sidethingies R2D_ImageColours(0, 0, 0, 1); @@ -2656,7 +2664,7 @@ texid_tf Media_UpdateForShader(cin_t *cin) } #endif -void Media_Send_Command(cin_t *cin, char *command) +void Media_Send_Command(cin_t *cin, const char *command) { if (!cin) cin = R_ShaderGetCinematic(videoshader); @@ -3443,7 +3451,7 @@ void Media_RecordDemo_f(void) CL_Stopdemo_f(); //capturing failed for some reason } -#ifdef _WIN32 +#if defined(_WIN32) && !defined(WINRT) typedef struct ISpNotifySink ISpNotifySink; typedef void *ISpNotifyCallback; typedef void __stdcall SPNOTIFYCALLBACK(WPARAM wParam, LPARAM lParam); @@ -4136,7 +4144,7 @@ qboolean S_LoadMP3Sound (sfx_t *s, qbyte *data, int datalen, int sndspeed); void Media_Init(void) { -#ifdef _WIN32 +#if defined(_WIN32) && !defined(WINRT) Cmd_AddCommand("tts", TTS_Say_f); Cmd_AddCommand("stt", STT_Init_f); Cvar_Register(&tts_mode, "Gimmicks"); diff --git a/engine/client/m_multi.c b/engine/client/m_multi.c index 28e1312a..750d6574 100644 --- a/engine/client/m_multi.c +++ b/engine/client/m_multi.c @@ -6,8 +6,6 @@ extern cvar_t maxclients; -menutext_t *MC_AddWhiteText(menu_t *menu, int x, int y, const char *text, qboolean rightalign); - /* MULTIPLAYER MENU */ void M_Menu_MultiPlayer_f (void) { @@ -29,13 +27,13 @@ void M_Menu_MultiPlayer_f (void) MC_AddCenterPicture(menu, 4, 24, "pics/m_banner_multiplayer"); menu->selecteditem = (menuoption_t*) - MC_AddConsoleCommand (menu, 64, 40, "Join network server", "menu_slist\n"); - MC_AddConsoleCommand (menu, 64, 40, "Quick Connect", "quickconnect qw\n"); - MC_AddConsoleCommand (menu, 64, 48, "Start network server", "menu_newmulti\n"); - MC_AddConsoleCommand (menu, 64, 56, "Player setup", "menu_setup\n"); - MC_AddConsoleCommand (menu, 64, 64, "Demos", "menu_demo\n"); + MC_AddConsoleCommand (menu, 64, 170, 40, "Join network server", "menu_slist\n"); + MC_AddConsoleCommand (menu, 64, 170, 48, "Quick Connect", "quickconnect qw\n"); + MC_AddConsoleCommand (menu, 64, 170, 56, "Start network server", "menu_newmulti\n"); + MC_AddConsoleCommand (menu, 64, 170, 64, "Player setup", "menu_setup\n"); + MC_AddConsoleCommand (menu, 64, 170, 72, "Demos", "menu_demo\n"); - menu->cursoritem = (menuoption_t*)MC_AddWhiteText(menu, 48, 40, NULL, false); + menu->cursoritem = (menuoption_t*)MC_AddWhiteText(menu, 48, 0, 40, NULL, false); return; } else if (mgt == MGT_HEXEN2) @@ -77,29 +75,29 @@ void M_Menu_MultiPlayer_f (void) MC_AddCenterPicture(menu, 4, 24, "gfx/p_multi.lmp"); MC_AddPicture(menu, 72, 32, 232, 64, "gfx/mp_menu.lmp"); } + + b = MC_AddConsoleCommand(menu, 72, 320, 32, "", "menu_slist\n"); + menu->selecteditem = (menuoption_t*)b; + b->common.height = 20; + b->common.width = p?p->width:320; + b = MC_AddConsoleCommand(menu, 72, 320, 52, "", "menu_newmulti\n"); + b->common.height = 20; + b->common.width = p?p->width:320; + b = MC_AddConsoleCommand(menu, 72, 320, 72, "", "menu_setup\n"); + b->common.height = 20; + b->common.width = p?p->width:320; + + b = MC_AddConsoleCommand(menu, 72, 320, 92, "", "menu_demo\n"); + MC_AddWhiteText(menu, 72, 0, 92+20/2-6, "Demos", false); + b->common.height = 20/2+2; + b->common.width = p?p->width:320; + + b = MC_AddConsoleCommand(menu, 72, 320, 112, "", "quickconnect qw\n"); + MC_AddWhiteText(menu, 72, 0, 112+20/2-6, "Quick Connect", false); + b->common.height = 20/2+2; + b->common.width = p?p->width:320; } - b = MC_AddConsoleCommand(menu, 72, 32, "", "menu_slist\n"); - menu->selecteditem = (menuoption_t*)b; - b->common.height = 20; - b->common.width = p?p->width:320; - b = MC_AddConsoleCommand(menu, 72, 52, "", "menu_newmulti\n"); - b->common.height = 20; - b->common.width = p?p->width:320; - b = MC_AddConsoleCommand(menu, 72, 72, "", "menu_setup\n"); - b->common.height = 20; - b->common.width = p?p->width:320; - - b = MC_AddConsoleCommand(menu, 72, 92, "", "menu_demo\n"); - MC_AddWhiteText(menu, 72, 92+20/2-6, "Demos", false); - b->common.height = 20/2+2; - b->common.width = p?p->width:320; - - b = MC_AddConsoleCommand(menu, 72, 112, "", "quickconnect qw\n"); - MC_AddWhiteText(menu, 72, 112+20/2-6, "Quick Connect", false); - b->common.height = 20/2+2; - b->common.width = p?p->width:320; - menu->cursoritem = (menuoption_t*)MC_AddCursor(menu, 54, 32); } @@ -269,7 +267,7 @@ void MSetupQ2_TransDraw (int x, int y, menucustom_t *option, menu_t *menu) void MSetup_TransDraw (int x, int y, menucustom_t *option, menu_t *menu) { - qbyte translationTable[256]; + unsigned int translationTable[256]; setupmenu_t *info = menu->data; mpic_t *p; void *f; @@ -310,6 +308,7 @@ void MSetup_TransDraw (int x, int y, menucustom_t *option, menu_t *menu) } } + R2D_ImageColours(1,1,1,1); p = R2D_SafeCachePic ("gfx/bigbox.lmp"); if (p) R2D_ScalePic (x-12, y-8, 72, 72, p); @@ -383,7 +382,7 @@ void M_Menu_Setup_f (void) b->common.width = 12*20; b->common.height = 20; */ - menu->cursoritem = (menuoption_t*)MC_AddWhiteText(menu, 54, 32, NULL, false); + menu->cursoritem = (menuoption_t*)MC_AddWhiteText(menu, 54, 0, 32, NULL, false); } return; } @@ -424,15 +423,15 @@ void M_Menu_Setup_f (void) ci->draw = MSetup_TransDraw; ci->key = NULL; - MC_AddCommand(menu, 64, 96, "Top colour", SetupMenuColour); - MC_AddCommand(menu, 64, 120, "Lower colour", SetupMenuColour); + MC_AddCommand(menu, 64, 160, 96, "Top colour", SetupMenuColour); + MC_AddCommand(menu, 64, 160, 120, "Lower colour", SetupMenuColour); - MC_AddCommand(menu, 64, 152, "Accept changes", ApplySetupMenu); - b = MC_AddConsoleCommand(menu, 64, 168, "Network Settings", "menu_network\n"); + MC_AddCommand(menu, 64, 160, 152, "Accept changes", ApplySetupMenu); + b = MC_AddConsoleCommand(menu, 64, 160, 168, "Network Settings", "menu_network\n"); b->common.tooltip = "Change network and client prediction settings."; - b = MC_AddConsoleCommand(menu, 64, 176, "Teamplay Settings", "menu_teamplay\n"); + b = MC_AddConsoleCommand(menu, 64, 160, 176, "Teamplay Settings", "menu_teamplay\n"); b->common.tooltip = "Change teamplay macro settings."; - menu->cursoritem = (menuoption_t*)MC_AddWhiteText(menu, 54, 32, NULL, false); + menu->cursoritem = (menuoption_t*)MC_AddWhiteText(menu, 54, 0, 32, NULL, false); info->lowercolour = bottomcolor.value; @@ -598,9 +597,9 @@ void M_Menu_GameOptions_f (void) // MC_AddPicture(menu, 72, 32, ("gfx/mp_menu.lmp") ); menu->selecteditem = (menuoption_t*) - MC_AddCommand (menu, 64, y, " Start game", MultiBeginGame);y+=16; + MC_AddCommand (menu, 64, 160, y, "Start game", MultiBeginGame);y+=16; - info->hostnameedit = MC_AddEdit (menu, 64, 160, y, " Hostname", name.string);y+=16; + info->hostnameedit = MC_AddEdit (menu, 64, 160, y, "Hostname", name.string);y+=16; for (players = 0; players < sizeof(numplayeroptions)/ sizeof(numplayeroptions[0]); players++) { @@ -610,23 +609,21 @@ void M_Menu_GameOptions_f (void) info->numplayers = MC_AddCombo (menu, 64, 160, y, "Max players", (const char **)numplayeroptions, players);y+=8; - info->deathmatch = MC_AddCombo (menu, 64, 160, y, " Deathmatch", (const char **)deathmatchoptions, deathmatch.value);y+=8; - info->teamplay = MC_AddCombo (menu, 64, 160, y, " Teamplay", (const char **)teamplayoptions, teamplay.value);y+=8; - info->skill = MC_AddCombo (menu, 64, 160, y, " Skill", (const char **)skilloptions, skill.value);y+=8; - info->rundedicated = MC_AddCheckBox(menu, 64, 160, y, " dedicated", NULL, 0);y+=8; + info->deathmatch = MC_AddCombo (menu, 64, 160, y, "Deathmatch", (const char **)deathmatchoptions, deathmatch.value);y+=8; + info->teamplay = MC_AddCombo (menu, 64, 160, y, "Teamplay", (const char **)teamplayoptions, teamplay.value);y+=8; + info->skill = MC_AddCombo (menu, 64, 160, y, "Skill", (const char **)skilloptions, skill.value);y+=8; + info->rundedicated = MC_AddCheckBox(menu, 64, 160, y, "dedicated", NULL, 0);y+=8; y+=8; - info->timelimit = MC_AddCombo (menu, 64, 160, y, " Time Limit", (const char **)timelimitoptions, timelimit.value/5);y+=8; - info->fraglimit = MC_AddCombo (menu, 64, 160, y, " Frag Limit", (const char **)fraglimitoptions, fraglimit.value/10);y+=8; - y+=8; - MC_AddSlider (menu, 64-7*8, 160, y, "Extra edict support", &pr_maxedicts, 512, 2047, 256);y+=8; + info->timelimit = MC_AddCombo (menu, 64, 160, y, "Time Limit", (const char **)timelimitoptions, timelimit.value/5);y+=8; + info->fraglimit = MC_AddCombo (menu, 64, 160, y, "Frag Limit", (const char **)fraglimitoptions, fraglimit.value/10);y+=8; y+=8; if (mgt == MGT_QUAKE2) - info->mapnameedit = MC_AddEdit (menu, 64, 160, y, " map", "base1"); + info->mapnameedit = MC_AddEdit (menu, 64, 160, y, "map", "base1"); else - info->mapnameedit = MC_AddEdit (menu, 64, 160, y, " map", "start"); + info->mapnameedit = MC_AddEdit (menu, 64, 160, y, "map", "start"); y += 16; - menu->cursoritem = (menuoption_t*)MC_AddWhiteText(menu, 54, 32, NULL, false); + menu->cursoritem = (menuoption_t*)MC_AddWhiteText(menu, 54, 0, 32, NULL, false); info->lowercolour = bottomcolor.value; @@ -656,7 +653,7 @@ void M_Menu_Teamplay_f (void) menubulk_t bulk[] = { MB_REDTEXT("Teamplay Options", false), - MB_TEXT("\x80\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x82", false), + MB_TEXT("^Ue080^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue082", false), MB_COMBOCVAR("Skins", noskins, noskinsoptions, noskinsvalues, "Enable or disable player skin usage. No download will use skins but will not download them from the server."), MB_EDITCVARTIP("Enemy Skin", "cl_enemyskin", "Override enemy skin with this."), MB_EDITCVARTIP("Team Skin", "cl_teamskin", "Override teammate skin with this."), @@ -686,7 +683,7 @@ void M_Menu_Teamplay_Locations_f (void) menubulk_t bulk[] = { MB_REDTEXT("Teamplay Location Names", false), - MB_TEXT("\x80\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x82", false), + MB_TEXT("^Ue080^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue082", false), MB_EDITCVARSLIM("Separator", "loc_name_separator", "Location name seperator character(s)"), MB_SPACING(4), MB_EDITCVARSLIM("Super Shotgun", "loc_name_ssg", "Short name for Super Shotgun in teamplay location 'reports'"), @@ -720,7 +717,7 @@ void M_Menu_Teamplay_Needs_f (void) menubulk_t bulk[] = { MB_REDTEXT("Teamplay Needed Items", false), - MB_TEXT("\x80\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x82", false), + MB_TEXT("^Ue080^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue082", false), MB_EDITCVARSLIM("Shells", "tp_need_shells", "Short name for Shotgun Shells in teamplay 'need' reports"), MB_EDITCVARSLIM("Nails", "tp_need_nails", "Short name for Nails in teamplay 'need' reports"), MB_EDITCVARSLIM("Rockets", "tp_need_rockets", "Short name for Rockets/Grenades in teamplay 'need' reports"), @@ -746,7 +743,7 @@ void M_Menu_Teamplay_Items_f (void) menubulk_t bulk[] = { MB_REDTEXT("Teamplay Item Names", false), - MB_TEXT("\x80\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x82", false), + MB_TEXT("^Ue080^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue082", false), MB_CONSOLECMD("Armor", "menu_teamplay_armor\n", "Modify team play macro armor names."), MB_CONSOLECMD("Weapon", "menu_teamplay_weapons\n", "Modify team play macro weapon names."), MB_CONSOLECMD("Powerups", "menu_teamplay_powerups\n", "Modify team play macro powerup names."), @@ -766,7 +763,7 @@ void M_Menu_Teamplay_Items_Armor_f (void) menubulk_t bulk[] = { MB_REDTEXT("Teamplay Armor Names", false), - MB_TEXT("\x80\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x82", false), + MB_TEXT("^Ue080^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue082", false), MB_EDITCVARSLIM("Armor", "tp_name_armor", "Short name for Armor type"), MB_EDITCVARSLIM("Green Type -", "tp_name_armortype_ga", "Short name for Green Armor type"), MB_EDITCVARSLIM("Yellow Type -", "tp_name_armortype_ya", "Short name for Yellow Armor type"), @@ -788,7 +785,7 @@ void M_Menu_Teamplay_Items_Weapons_f (void) menubulk_t bulk[] = { MB_REDTEXT("Teamplay Weapon Names", false), - MB_TEXT("\x80\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x82", false), + MB_TEXT("^Ue080^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue082", false), MB_EDITCVARSLIM("Weapon", "tp_name_weapon", "Short name for Weapon"), MB_SPACING(4), MB_EDITCVARSLIM("Axe", "tp_name_axe", "Short name for Weapon"), @@ -812,7 +809,7 @@ void M_Menu_Teamplay_Items_Powerups_f (void) menubulk_t bulk[] = { MB_REDTEXT("Teamplay Powerup Names", false), - MB_TEXT("\x80\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x82", false), + MB_TEXT("^Ue080^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue082", false), MB_EDITCVARSLIM("Quad Damage", "tp_name_quad", "Short name for Quad Damage"), MB_EDITCVARSLIM("Pentagram", "tp_name_pent", "Short name for Pentgram of Protection"), MB_EDITCVARSLIM("Ring of Invis", "tp_name_ring", "Short name for Ring Of Invisibilty"), @@ -839,7 +836,7 @@ void M_Menu_Teamplay_Items_Ammo_Health_f (void) menubulk_t bulk[] = { MB_REDTEXT("Teamplay Ammo/Health", false), - MB_TEXT("\x80\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x82", false), + MB_TEXT("^Ue080^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue082", false), MB_EDITCVARSLIM("Shells", "tp_name_shells", "Short name for Shells"), MB_EDITCVARSLIM("Nails", "tp_name_nails", "Short name for Nails"), MB_EDITCVARSLIM("Rockets", "tp_name_rockets", "Short name for Rockets"), @@ -861,7 +858,7 @@ void M_Menu_Teamplay_Items_Team_Fortress_f (void) menubulk_t bulk[] = { MB_REDTEXT("Teamplay Team Fortress", false), - MB_TEXT("\x80\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x82", false), + MB_TEXT("^Ue080^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue082", false), MB_EDITCVARSLIM("Sentry Gun", "tp_name_sentry", "Short name for the Engineer's Sentry Gun"), MB_EDITCVARSLIM("Dispenser", "tp_name_disp", "Short name for the Engineer's Ammo Dispenser"), MB_EDITCVARSLIM("Flag", "tp_name_flag", "Short name for Flag"), @@ -878,7 +875,7 @@ void M_Menu_Teamplay_Items_Status_Location_Misc_f (void) menubulk_t bulk[] = { MB_REDTEXT("Teamplay Misc", false), - MB_TEXT("\x80\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x82", false), + MB_TEXT("^Ue080^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue082", false), MB_EDITCVARSLIM("Enemy", "tp_name_enemy", "Short for Enemy in teamplay 'status' & 'location' reports"), MB_EDITCVARSLIM("Teammate", "tp_name_teammate", "Short for Enemy in teamplay 'status' & 'location' reports"), MB_SPACING(4), @@ -912,6 +909,7 @@ void M_Menu_Network_f (void) "Lower Latency", "Smoother", "Smooth Demos Only", + NULL }; static const char *smoothingvalues[] = {"0", "1", "2", NULL}; extern cvar_t cl_download_csprogs, cl_download_redirection, requiredownloads, cl_solid_players; @@ -921,7 +919,7 @@ void M_Menu_Network_f (void) menubulk_t bulk[] = { MB_REDTEXT("Network Settings", false), - MB_TEXT("\x80\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x82", false), + MB_TEXT("^Ue080^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue082", false), MB_EDITCVARSLIM("Network FPS", "cl_netfps", "Sets ammount of FPS used to communicate with server (sent and received)"), MB_EDITCVARSLIM("Rate", "rate", "Maximum bytes per second that the server should send to the client"), MB_EDITCVARSLIM("Download Rate", "drate", "Maximum bytes per second that the server should send maps and demos to the client"), diff --git a/engine/client/m_options.c b/engine/client/m_options.c index 23a03ef9..b8b53228 100644 --- a/engine/client/m_options.c +++ b/engine/client/m_options.c @@ -36,21 +36,33 @@ menu_t *M_Options_Title(int *y, int infosize) //these are awkward/strange qboolean M_Options_AlwaysRun (menucheck_t *option, struct menu_s *menu, chk_set_t set) { - if (set == CHK_CHECKED) - return cl_forwardspeed.value > 200; - else if (cl_forwardspeed.value > 200) + if (M_GameType() == MGT_QUAKE2) { - Cvar_SetValue (&cl_forwardspeed, 200); - if (*cl_backspeed.string) - Cvar_SetValue (&cl_backspeed, 200); - return false; + extern cvar_t cl_run; + //quake2 mods have a nasty tendancy to hack at the various cvars, which breaks everything + if (set != CHK_CHECKED) + Cvar_SetValue(&cl_run, !cl_run.ival); + return cl_run.ival; } else { - Cvar_SetValue (&cl_forwardspeed, 400); - if (*cl_backspeed.string) - Cvar_SetValue (&cl_backspeed, 400); - return true; + //for better compat with other quake engines, we just ignore the cl_run cvar, at least for the menu. + if (set == CHK_CHECKED) + return cl_forwardspeed.value > 200; + else if (cl_forwardspeed.value > 200) + { + Cvar_SetValue (&cl_forwardspeed, 200); + if (*cl_backspeed.string) + Cvar_SetValue (&cl_backspeed, 200); + return false; + } + else + { + Cvar_SetValue (&cl_forwardspeed, 400); + if (*cl_backspeed.string) + Cvar_SetValue (&cl_backspeed, 400); + return true; + } } } qboolean M_Options_InvertMouse (menucheck_t *option, struct menu_s *menu, chk_set_t set) @@ -223,9 +235,9 @@ void M_Menu_Audio_Speakers_f (void) menu->event = M_Audio_StartSound; for (i = 0; i < 6; i++) - info->speaker[i] = MC_AddBufferedText(menu, 0, 0, va("%i", i), false, true); + info->speaker[i] = MC_AddBufferedText(menu, 0, 0, 0, va("%i", i), false, true); - info->testsoundsource = MC_AddBufferedText(menu, 0, 0, "X", false, true); + info->testsoundsource = MC_AddBufferedText(menu, 0, 0, 0, "X", false, true); info->card = sndcardinfo; @@ -364,7 +376,7 @@ void M_Menu_Audio_f (void) menubulk_t bulk[] = { MB_REDTEXT("Sound Options", false), - MB_TEXT("\x80\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x82", false), + MB_TEXT("^Ue080^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue082", false), MB_SPACING(8), MB_CONSOLECMD("Restart Sound", "snd_restart\n", "Restart audio systems and apply set options."), MB_SPACING(4), @@ -392,7 +404,7 @@ void M_Menu_Audio_f (void) #ifdef VOICECHAT MB_REDTEXT("Voice Options", false), - MB_TEXT("\x80\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x82", false), + MB_TEXT("^Ue080^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue082", false), MB_COMBOCVAR("Microphone Device", snd_voip_capturedevice, (const char**)info->capdevdescs, (const char**)info->capdevnames, NULL), MB_SLIDER("Voice Volume", snd_voip_play, 0, 2, 0.1, NULL), MB_CHECKBOXCVAR("Microphone Test", snd_voip_test, 0), @@ -483,7 +495,7 @@ void M_Menu_Particles_f (void) int y; menubulk_t bulk[] = { MB_REDTEXT("Particle Options", false), - MB_TEXT("\x80\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x82", false), + MB_TEXT("^Ue080^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue082", false), // MB_COMBOCVAR("Particle System", r_particlesystem, psystemopts, psystemvals, "Selects particle system to use. Classic is standard Quake particles, script is FTE style scripted particles, and none disables particles entirely."), MB_COMBOCVAR("Particle Set", r_particledesc, pdescopts, pdescvals, "Selects particle set to use with the scripted particle system."), MB_SPACING(4), @@ -597,7 +609,7 @@ const char *presetexec[] = "r_particledesc \"high tsshaft\";" #endif "gl_specular 1;" - "r_loadlit 2;" +// "r_loadlit 2;" "r_waterstyle 2;" "gl_blendsprites 1;" // "r_fastsky -1;" @@ -640,7 +652,7 @@ void M_Menu_Preset_f (void) menubulk_t bulk[] = { MB_REDTEXT("Please Choose Preset", false), - MB_TEXT("\x80\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x82", false), + MB_TEXT("^Ue080^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue082", false), MB_CONSOLECMD("286 (untextured)", "fps_preset 286;menupop\n", "Lacks textures, particles, pretty much everything."), MB_CONSOLECMD("fast (deathmatch)", "fps_preset fast;menupop\n", "Fullscreen effects off to give consistant framerates"), MB_CONSOLECMD("normal (faithful)", "fps_preset normal;menupop\n", "This is for Quake purists!"), @@ -749,7 +761,7 @@ void M_Menu_FPS_f (void) menubulk_t bulk[] = { MB_REDTEXT("FPS Options", false), - MB_TEXT("\x80\x81\x81\x81\x81\x81\x81\x81\x81\x81\x82", false), + MB_TEXT("^Ue080^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue082", false), MB_COMBORETURN("Preset", presetname, 2, info->preset, "Select a builtin configuration of graphical settings."), MB_CMD("Apply", M_PresetApply, "Applies selected preset."), MB_SPACING(4), @@ -795,7 +807,7 @@ void M_Menu_Render_f (void) menubulk_t bulk[] = { MB_REDTEXT("Rendering Options", false), - MB_TEXT("\x80\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x82", false), + MB_TEXT("^Ue080^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue082", false), MB_CHECKBOXCVAR("Calculate VIS", r_novis, 0), MB_CHECKBOXCVAR("Fast Sky", r_fastsky, 0), MB_CHECKBOXCVAR("Disable Model Lerp", r_nolerp, 0), @@ -888,7 +900,7 @@ void M_Menu_Textures_f (void) menubulk_t bulk[] = { MB_REDTEXT("Texture Options", false), - MB_TEXT("\x80\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x82", false), + MB_TEXT("^Ue080^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue082", false), MB_CHECKBOXCVAR("Load Replacements", gl_load24bit, 0), MB_CHECKBOXCVAR("Simple Texturing", r_drawflat, 0), MB_COMBOCVAR("3D Filter Mode", gl_texturemode, texturefilternames, texturefiltervalues, "Chooses the texture filtering method used for 3D objects."), @@ -1120,7 +1132,7 @@ void M_Menu_Lighting_f (void) menubulk_t bulk[] = { MB_REDTEXT("Lighting Options", false), - MB_TEXT("\x80\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x82", false), + MB_TEXT("^Ue080^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue082", false), #ifdef RTLIGHTS MB_COMBORETURN("Lighting Mode", lightingopts, lightselect, info->lightcombo, "Selects method used for world lighting. Realtime lighting requires appropriate realtime lighting files for maps."), MB_COMBORETURN("Dynamic Lighting Mode", dlightopts, dlightselect, info->dlightcombo, "Selects method used for dynamic lighting such as explosion lights and muzzle flashes."), @@ -1384,8 +1396,8 @@ void M_Menu_Singleplayer_Cheats_Quake (void) /*anything that doesn't match will end up with 0*/ #endif - MC_AddRedText(menu, 16, y, " Quake Singleplayer Cheats", false); y+=8; - MC_AddWhiteText(menu, 16, y, " €‚ ", false); y+=8; + MC_AddRedText(menu, 16, 170, y, " Quake Singleplayer Cheats", false); y+=8; + MC_AddWhiteText(menu, 16, 170, y, " €‚ ", false); y+=8; y+=8; #ifndef CLIENTONLY info->skillcombo = MC_AddCombo(menu,16,170, y, "Difficulty", skilloptions, currentskill); y+=8; @@ -1395,10 +1407,10 @@ void M_Menu_Singleplayer_Cheats_Quake (void) #ifdef TEXTEDITOR MC_AddCheckBox(menu, 16, 170, y, "Debugger", &debugger, 0); y+=8; #endif - MC_AddConsoleCommand(menu, 16, y, " Toggle Godmode", "god\n"); y+=8; - MC_AddConsoleCommand(menu, 16, y, " Toggle Flymode", "fly\n"); y+=8; - MC_AddConsoleCommand(menu, 16, y, " Toggle Noclip", "noclip\n"); y+=8; - MC_AddConsoleCommand(menu, 16, y, " Quad Damage", "impulse 255\n"); y+=8; + MC_AddConsoleCommand(menu, 16, 170, y, " Toggle Godmode", "god\n"); y+=8; + MC_AddConsoleCommand(menu, 16, 170, y, " Toggle Flymode", "fly\n"); y+=8; + MC_AddConsoleCommand(menu, 16, 170, y, " Toggle Noclip", "noclip\n"); y+=8; + MC_AddConsoleCommand(menu, 16, 170, y, " Quad Damage", "impulse 255\n"); y+=8; #ifndef CLIENTONLY MC_AddSlider(menu, 16, 170, y, "Gravity", &sv_gravity,0,800,25); y+=8; #endif @@ -1408,20 +1420,20 @@ void M_Menu_Singleplayer_Cheats_Quake (void) #ifndef CLIENTONLY MC_AddSlider(menu, 16, 170, y, "Max Movement Speed", &sv_maxspeed,0,1000,50); y+=8; #endif - MC_AddConsoleCommand(menu, 16, y, " Silver & Gold Keys", "impulse 13\nimpulse 14\n"); y+=8; - MC_AddConsoleCommand(menu, 16, y, "All Weapons & Items", "impulse 9\n"); y+=8; - MC_AddConsoleCommand(menu, 16, y, "No Enemy Targetting", "notarget\n"); y+=8; + MC_AddConsoleCommand(menu, 16, 170, y, " Silver & Gold Keys", "impulse 13\nimpulse 14\n"); y+=8; + MC_AddConsoleCommand(menu, 16, 170, y, "All Weapons & Items", "impulse 9\n"); y+=8; + MC_AddConsoleCommand(menu, 16, 170, y, "No Enemy Targetting", "notarget\n"); y+=8; #ifndef CLIENTONLY - MC_AddConsoleCommand(menu, 16, y, " Restart Map", "restart\n"); y+=8; + MC_AddConsoleCommand(menu, 16, 170, y, "Restart Map", "restart\n"); y+=8; #else - MC_AddConsoleCommand(menu, 16, y, " Suicide", "kill\n"); y+=8; + MC_AddConsoleCommand(menu, 16, 170, y, "Suicide", "kill\n"); y+=8; #endif y+=8; - MC_AddCommand(menu, 16, y, " Apply Changes", M_Apply_SP_Cheats); y+=8; + MC_AddCommand(menu, 16, 170, y, "Apply Changes", M_Apply_SP_Cheats); y+=8; menu->selecteditem = (union menuoption_s *)info->skillcombo; - menu->cursoritem = (menuoption_t*)MC_AddWhiteText(menu, 170, cursorpositionY, NULL, false); + menu->cursoritem = (menuoption_t*)MC_AddWhiteText(menu, 170, 0, cursorpositionY, NULL, false); } // Quake 2 @@ -1498,16 +1510,16 @@ void M_Menu_Singleplayer_Cheats_Quake2 (void) /*anything that doesn't match will end up with 0*/ #endif - MC_AddRedText(menu, 16, y, " Quake2 Singleplayer Cheats", false); y+=8; - MC_AddWhiteText(menu, 16, y, " €‚ ", false); y+=8; + MC_AddRedText(menu, 16, 170, y, "Quake2 Singleplayer Cheats", false); y+=8; + MC_AddWhiteText(menu, 16, 170, y, "€‚ ", false); y+=8; y+=8; #ifndef CLIENTONLY info->skillcombo = MC_AddCombo(menu,16,170, y, "Difficulty", skilloptions, currentskill); y+=8; info->mapcombo = MC_AddCombo(menu,16,170, y, "Map", mapoptions_q2, currentmap); y+=8; MC_AddCheckBox(menu, 16, 170, y, "Cheats", &sv_cheats,0); y+=8; #endif - MC_AddConsoleCommand(menu, 16, y, " Toggle Godmode", "god\n"); y+=8; - MC_AddConsoleCommand(menu, 16, y, " Toggle Noclip", "noclip\n"); y+=8; + MC_AddConsoleCommand(menu, 16, 170, y, "Toggle Godmode", "god\n"); y+=8; + MC_AddConsoleCommand(menu, 16, 170, y, "Toggle Noclip", "noclip\n"); y+=8; #ifndef CLIENTONLY MC_AddSlider(menu, 16, 170, y, "Gravity", &sv_gravity,0,850,25); y+=8; #endif @@ -1517,35 +1529,35 @@ void M_Menu_Singleplayer_Cheats_Quake2 (void) #ifndef CLIENTONLY MC_AddSlider(menu, 16, 170, y, "Max Movement Speed", &sv_maxspeed,0,1000,50); y+=8; #endif - MC_AddConsoleCommand(menu, 16, y, " Unlimited Ammo", "dmflags 8192\n"); y+=8; - MC_AddConsoleCommand(menu, 16, y, " Quad Damage", "give quad damage\n"); y+=8; - MC_AddConsoleCommand(menu, 16, y, " Blue & Red Key", "give blue key\ngive red key\n"); y+=8; - MC_AddConsoleCommand(menu, 16, y, " Pyramid Key", "give pyramid key\n"); y+=8; - MC_AddConsoleCommand(menu, 16, y, "All Weapons & Items", "give all\n"); y+=8; - MC_AddConsoleCommand(menu, 16, y, " Data Spinner", "give data spinner\n"); y+=8; - MC_AddConsoleCommand(menu, 16, y, " Power Cube", "give power cube\n"); y+=8; - MC_AddConsoleCommand(menu, 16, y, " Data CD", "give data cd\n"); y+=8; - MC_AddConsoleCommand(menu, 16, y, " Ammo Pack", "give ammo pack\n"); y+=8; - MC_AddConsoleCommand(menu, 16, y, " Bandolier", "give bandolier\n"); y+=8; - MC_AddConsoleCommand(menu, 16, y, " Adrenaline", "give adrenaline\n"); y+=8; - MC_AddConsoleCommand(menu, 16, y, " Ancient Head", "give ancient head\n"); y+=8; - MC_AddConsoleCommand(menu, 16, y, " Environment Suit", "give environment suit\n"); y+=8; - MC_AddConsoleCommand(menu, 16, y, " Rebreather", "give rebreather\n"); y+=8; - MC_AddConsoleCommand(menu, 16, y, " Invulnerability", "give invulnerability\n"); y+=8; - MC_AddConsoleCommand(menu, 16, y, " Silencer", "give silencer\n"); y+=8; - MC_AddConsoleCommand(menu, 16, y, " Power Shield", "give power shield\n"); y+=8; - MC_AddConsoleCommand(menu, 16, y, " Commander's Head", "give commander's head\n"); y+=8; - MC_AddConsoleCommand(menu, 16, y, " Security Pass", "give security pass\n"); y+=8; - MC_AddConsoleCommand(menu, 16, y, " Airstrike Marker", "give airstrike marker\n"); y+=8; + MC_AddConsoleCommand(menu, 16, 170, y, "Unlimited Ammo", "dmflags 8192\n"); y+=8; + MC_AddConsoleCommand(menu, 16, 170, y, "Quad Damage", "give quad damage\n"); y+=8; + MC_AddConsoleCommand(menu, 16, 170, y, "Blue & Red Key", "give blue key\ngive red key\n"); y+=8; + MC_AddConsoleCommand(menu, 16, 170, y, "Pyramid Key", "give pyramid key\n"); y+=8; + MC_AddConsoleCommand(menu, 16, 170, y, "All Weapons & Items", "give all\n"); y+=8; + MC_AddConsoleCommand(menu, 16, 170, y, "Data Spinner", "give data spinner\n"); y+=8; + MC_AddConsoleCommand(menu, 16, 170, y, "Power Cube", "give power cube\n"); y+=8; + MC_AddConsoleCommand(menu, 16, 170, y, "Data CD", "give data cd\n"); y+=8; + MC_AddConsoleCommand(menu, 16, 170, y, "Ammo Pack", "give ammo pack\n"); y+=8; + MC_AddConsoleCommand(menu, 16, 170, y, "Bandolier", "give bandolier\n"); y+=8; + MC_AddConsoleCommand(menu, 16, 170, y, "Adrenaline", "give adrenaline\n"); y+=8; + MC_AddConsoleCommand(menu, 16, 170, y, "Ancient Head", "give ancient head\n"); y+=8; + MC_AddConsoleCommand(menu, 16, 170, y, "Environment Suit", "give environment suit\n"); y+=8; + MC_AddConsoleCommand(menu, 16, 170, y, "Rebreather", "give rebreather\n"); y+=8; + MC_AddConsoleCommand(menu, 16, 170, y, "Invulnerability", "give invulnerability\n"); y+=8; + MC_AddConsoleCommand(menu, 16, 170, y, "Silencer", "give silencer\n"); y+=8; + MC_AddConsoleCommand(menu, 16, 170, y, "Power Shield", "give power shield\n"); y+=8; + MC_AddConsoleCommand(menu, 16, 170, y, "Commander's Head", "give commander's head\n"); y+=8; + MC_AddConsoleCommand(menu, 16, 170, y, "Security Pass", "give security pass\n"); y+=8; + MC_AddConsoleCommand(menu, 16, 170, y, "Airstrike Marker", "give airstrike marker\n"); y+=8; #ifndef CLIENTONLY - MC_AddConsoleCommand(menu, 16, y, " Restart Map", va("restart\n")); y+=8; + MC_AddConsoleCommand(menu, 16, 170, y, "Restart Map", va("restart\n")); y+=8; #endif y+=8; - MC_AddCommand(menu, 16, y, " Apply Changes", M_Apply_SP_Cheats_Q2); y+=8; + MC_AddCommand(menu, 16, 170, y, "Apply Changes", M_Apply_SP_Cheats_Q2); y+=8; menu->selecteditem = (union menuoption_s *)info->skillcombo; - menu->cursoritem = (menuoption_t*)MC_AddWhiteText(menu, 170, cursorpositionY, NULL, false); + menu->cursoritem = (menuoption_t*)MC_AddWhiteText(menu, 170, 0, cursorpositionY, NULL, false); } // Hexen 2 @@ -1856,8 +1868,8 @@ void M_Menu_Singleplayer_Cheats_Hexen2 (void) else currentmap = 0; - MC_AddRedText(menu, 16, y, " Hexen2 Singleplayer Cheats", false); y+=8; - MC_AddWhiteText(menu, 16, y, " €‚ ", false); y+=8; + MC_AddRedText(menu, 16, 170, y, "Hexen2 Singleplayer Cheats", false); y+=8; + MC_AddWhiteText(menu, 16, 170, y, "€‚ ", false); y+=8; y+=8; #ifndef CLIENTONLY info->skillcombo = MC_AddCombo(menu,16,170, y, "Difficulty", skilloptions, currentskill); y+=8; @@ -1866,9 +1878,9 @@ void M_Menu_Singleplayer_Cheats_Hexen2 (void) #ifndef CLIENTONLY MC_AddCheckBox(menu, 16, 170, y, "Cheats", &sv_cheats,0); y+=8; #endif - MC_AddConsoleCommand(menu, 16, y, " Toggle Godmode", "god\n"); y+=8; - MC_AddConsoleCommand(menu, 16, y, " Toggle Flymode", "fly\n"); y+=8; - MC_AddConsoleCommand(menu, 16, y, " Toggle Noclip", "noclip\n"); y+=8; + MC_AddConsoleCommand(menu, 16, 170, y, "Toggle Godmode", "god\n"); y+=8; + MC_AddConsoleCommand(menu, 16, 170, y, "Toggle Flymode", "fly\n"); y+=8; + MC_AddConsoleCommand(menu, 16, 170, y, "Toggle Noclip", "noclip\n"); y+=8; #ifndef CLIENTONLY MC_AddSlider(menu, 16, 170, y, "Gravity", &sv_gravity,0,800,25); y+=8; #endif @@ -1878,29 +1890,29 @@ void M_Menu_Singleplayer_Cheats_Hexen2 (void) #ifndef CLIENTONLY MC_AddSlider(menu, 16, 170, y, "Max Movement Speed", &sv_maxspeed,0,1000,50); y+=8; #endif - MC_AddConsoleCommand(menu, 16, y, " Sheep Transformation", "impulse 14\n"); y+=8; - MC_AddConsoleCommand(menu, 16, y, " Change To Paladin (lvl3+)", "impulse 171\n"); y+=8; - MC_AddConsoleCommand(menu, 16, y, " Change To Crusader (lvl3+)", "impulse 172\n"); y+=8; - MC_AddConsoleCommand(menu, 16, y, "Change to Necromancer (lvl3+)", "impulse 173\n"); y+=8; - MC_AddConsoleCommand(menu, 16, y, " Change to Assassin (lvl3+)", "impulse 174\n"); y+=8; - MC_AddConsoleCommand(menu, 16, y, " Remove Monsters", "impulse 35\n"); y+=8; - MC_AddConsoleCommand(menu, 16, y, " Freeze Monsters", "impulse 36\n"); y+=8; - MC_AddConsoleCommand(menu, 16, y, " Unfreeze Monsters", "impulse 37\n"); y+=8; - MC_AddConsoleCommand(menu, 16, y, " Increase Level By 1", "impulse 40\n"); y+=8; - MC_AddConsoleCommand(menu, 16, y, " Increase Experience", "impulse 41\n"); y+=8; - MC_AddConsoleCommand(menu, 16, y, " Display Co-ordinates", "impulse 42\n"); y+=8; - MC_AddConsoleCommand(menu, 16, y, " All Weapons & Mana", "impulse 9\n"); y+=8; - MC_AddConsoleCommand(menu, 16, y, " All Weapons & Mana & Items", "impulse 43\n"); y+=8; - MC_AddConsoleCommand(menu, 16, y, " No Enemy Targetting", "notarget\n"); y+=8; - MC_AddConsoleCommand(menu, 16, y, " Enable Crosshair", "crosshair 1\n"); y+=8; - MC_AddConsoleCommand(menu, 16, y, " 20 Of Each Artifact", "impulse 299\n"); y+=8; - MC_AddConsoleCommand(menu, 16, y, " Restart Map", "impulse 99\n"); y+=8; + MC_AddConsoleCommand(menu, 16, 170, y, "Sheep Transformation", "impulse 14\n"); y+=8; + MC_AddConsoleCommand(menu, 16, 170, y, "Change To Paladin (lvl3+)", "impulse 171\n"); y+=8; + MC_AddConsoleCommand(menu, 16, 170, y, "Change To Crusader (lvl3+)", "impulse 172\n"); y+=8; + MC_AddConsoleCommand(menu, 16, 170, y, "Change to Necromancer (lvl3+)", "impulse 173\n"); y+=8; + MC_AddConsoleCommand(menu, 16, 170, y, "Change to Assassin (lvl3+)", "impulse 174\n"); y+=8; + MC_AddConsoleCommand(menu, 16, 170, y, "Remove Monsters", "impulse 35\n"); y+=8; + MC_AddConsoleCommand(menu, 16, 170, y, "Freeze Monsters", "impulse 36\n"); y+=8; + MC_AddConsoleCommand(menu, 16, 170, y, "Unfreeze Monsters", "impulse 37\n"); y+=8; + MC_AddConsoleCommand(menu, 16, 170, y, "Increase Level By 1", "impulse 40\n"); y+=8; + MC_AddConsoleCommand(menu, 16, 170, y, "Increase Experience", "impulse 41\n"); y+=8; + MC_AddConsoleCommand(menu, 16, 170, y, "Display Co-ordinates", "impulse 42\n"); y+=8; + MC_AddConsoleCommand(menu, 16, 170, y, "All Weapons & Mana", "impulse 9\n"); y+=8; + MC_AddConsoleCommand(menu, 16, 170, y, "All Weapons & Mana & Items", "impulse 43\n"); y+=8; + MC_AddConsoleCommand(menu, 16, 170, y, "No Enemy Targetting", "notarget\n"); y+=8; + MC_AddConsoleCommand(menu, 16, 170, y, "Enable Crosshair", "crosshair 1\n"); y+=8; + MC_AddConsoleCommand(menu, 16, 170, y, "20 Of Each Artifact", "impulse 299\n"); y+=8; + MC_AddConsoleCommand(menu, 16, 170, y, "Restart Map", "impulse 99\n"); y+=8; y+=8; - MC_AddCommand(menu, 16, y, " Apply Changes", M_Apply_SP_Cheats_H2); y+=8; + MC_AddCommand(menu, 16, 170, y, "Apply Changes", M_Apply_SP_Cheats_H2); y+=8; menu->selecteditem = (union menuoption_s *)info->skillcombo; - menu->cursoritem = (menuoption_t*)MC_AddWhiteText(menu, 250, cursorpositionY, NULL, false); + menu->cursoritem = (menuoption_t*)MC_AddWhiteText(menu, 250, 0, cursorpositionY, NULL, false); } void M_Menu_Singleplayer_Cheats_f (void) @@ -2358,7 +2370,7 @@ void M_Menu_Video_f (void) menubulk_t bulk[] = { MB_REDTEXT("Video Options", false), - MB_TEXT("\x80\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x82", false), + MB_TEXT("^Ue080^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue082", false), #ifdef MULTIRENDERER MB_COMBOCVAR("Renderer", vid_renderer, rendererops, renderervalues, NULL), #endif @@ -2448,3 +2460,290 @@ void M_Menu_Video_f (void) */ menu->event = CheckCustomMode; } + +typedef struct +{ + int skingroup; + int framegroup; + double framechangetime; + double skinchangetime; + float pitch; + float yaw; + float dist; + char modelname[MAX_QPATH]; + char forceshader[MAX_QPATH]; +} modelview_t; + +static unsigned int genhsv(float h_, float s, float v) +{ + float r=0, g=1, b=0; + + float h = h_ - floor(h_); + + int i = floor(h * 6); + float f = h * 6 - i; + float p = v * (1 - s); + float q = v * (1 - f * s); + float t = v * (1 - (1 - f) * s); + switch(i) + { + case 0: r = v, g = t, b = p; break; + case 1: r = q, g = v, b = p; break; + case 2: r = p, g = v, b = t; break; + case 3: r = p, g = q, b = v; break; + case 4: r = t, g = p, b = v; break; + case 5: r = v, g = p, b = q; break; + } + + return 0xff000000 | + ((int)(r*255)<<16) | + ((int)(g*255)<<8) | + ((int)(b*255)<<0); +}; +const char *Mod_FrameNameForNum(model_t *model, int num); +const char *Mod_SkinNameForNum(model_t *model, int num); + +static void M_ModelViewerDraw(int x, int y, struct menucustom_s *c, struct menu_s *m) +{ + static playerview_t pv; + entity_t ent; + vec3_t fwd, rgt, up; + const char *fname; + + modelview_t *mods = c->dptr; + + memset(&pv, 0, sizeof(pv)); + + CL_DecayLights (); + CL_ClearEntityLists(); + V_ClearRefdef(&pv); + r_refdef.drawsbar = false; + V_CalcRefdef(&pv); + r_refdef.grect.width = vid.width; + r_refdef.grect.height = vid.height; + r_refdef.grect.x = 0; + r_refdef.grect.y = 0; + r_refdef.time = realtime; + + r_refdef.flags = Q2RDF_NOWORLDMODEL; + + r_refdef.afov = 60; + r_refdef.fov_x = 0; + r_refdef.fov_y = 0; + r_refdef.dirty |= RDFD_FOV; + + VectorClear(r_refdef.viewangles); + r_refdef.viewangles[0] = mods->pitch; + r_refdef.viewangles[1] = mods->yaw; + AngleVectors(r_refdef.viewangles, fwd, rgt, up); + VectorScale(fwd, -mods->dist, r_refdef.vieworg); + + memset(&ent, 0, sizeof(ent)); + ent.scale = 1; + ent.model = Mod_ForName(mods->modelname, MLV_WARN); + if (!ent.model) + return; //panic! + ent.origin[2] -= (ent.model->maxs[2]-ent.model->mins[2]) * 0.5 + ent.model->mins[2]; + Vector4Set(ent.shaderRGBAf, 1, 1, 1, 1); + if (strstr(mods->modelname, "player")) + { + ent.bottomcolour = genhsv(realtime*0.1 + 0, 1, 1); + ent.topcolour = genhsv(realtime*0.1 + 0.5, 1, 1); + } + else + { + ent.topcolour = TOP_DEFAULT; + ent.bottomcolour = BOTTOM_DEFAULT; + } +// ent.fatness = sin(realtime)*5; + ent.playerindex = -1; + ent.scale = 33.3333; + ent.skinnum = mods->skingroup; + ent.shaderTime = realtime; + ent.framestate.g[FS_REG].frame[0] = mods->framegroup; + ent.framestate.g[FS_REG].frametime[0] = realtime - mods->framechangetime; + ent.framestate.g[FS_REG].frametime[1] = realtime - mods->framechangetime; + ent.customskin = Mod_RegisterSkinFile(va("%s_0.skin", mods->modelname)); + V_AddEntity(&ent); + + V_ApplyRefdef(); + R_RenderView(); + + fname = Mod_FrameNameForNum(ent.model, mods->framegroup); + if (!fname) + fname = "Unknown Frame"; + Draw_FunString(0, 0, va("%i: %s", mods->framegroup, fname)); + fname = Mod_SkinNameForNum(ent.model, mods->skingroup); + if (!fname) + fname = "Unknown Skin"; + Draw_FunString(0, 8, va("%i: %s", mods->skingroup, fname)); +} +static qboolean M_ModelViewerKey(struct menucustom_s *c, struct menu_s *m, int key) +{ + modelview_t *mods = c->dptr; + + if (key == 'w') + { + mods->dist *= 0.9; + if (mods->dist < 5) + mods->dist = 5; + } + else if (key == 's') + mods->dist /= 0.9; + else if (key == K_UPARROW) + mods->pitch += 5; + else if (key == K_DOWNARROW) + mods->pitch -= 5; + else if (key == K_LEFTARROW) + mods->yaw -= 5; + else if (key == K_RIGHTARROW) + mods->yaw += 5; + else if (key == K_HOME) + { + mods->skingroup = max(0, mods->skingroup-1); + mods->skinchangetime = realtime; + } + else if (key == K_END) + { + mods->skingroup += 1; + mods->skinchangetime = realtime; + } + else if (key == K_PGDN) + { + mods->framegroup = max(0, mods->framegroup-1); + mods->framechangetime = realtime; + } + else if (key == K_PGUP) + { + mods->framegroup += 1; + mods->framechangetime = realtime; + } + else + return false; + return true; +} + +void M_Menu_ModelViewer_f(void) +{ + modelview_t *mv; + menucustom_t *c; + menu_t *menu; + + Key_Dest_Add(kdm_menu); + + menu = M_CreateMenu(sizeof(*mv)); + mv = menu->data; + c = MC_AddCustom(menu, 64, 32, mv, 0); + menu->cursoritem = (menuoption_t*)c; + c->draw = M_ModelViewerDraw; + c->key = M_ModelViewerKey; + + mv->yaw = 180 + crandom()*45; + mv->dist = 150; + Q_strncpyz(mv->modelname, Cmd_Argv(1), sizeof(mv->modelname)); + Q_strncpyz(mv->forceshader, Cmd_Argv(2), sizeof(mv->forceshader)); +} + +typedef struct +{ + ftemanifest_t **manifests; + size_t nummanifests; + int y; +} modmenu_t; + +static void Mods_Draw(int x, int y, struct menucustom_s *c, struct menu_s *m) +{ + modmenu_t *mods = c->dptr; + int i, ym; + c->common.height = vid.height - y; + ym = y+c->common.height; + + mods->y = y; + + for (i = 0; y+8 <= ym && i < mods->nummanifests; y+=8, i++) + { + if (mousecursor_y >= y && mousecursor_y < y+8) + Draw_AltFunString(x, y, mods->manifests[i]->formalname); + else + Draw_FunString(x, y, mods->manifests[i]->formalname); + } +} +static qboolean Mods_Key(struct menucustom_s *c, struct menu_s *m, int key) +{ + modmenu_t *mods = c->dptr; + int i; + ftemanifest_t *man; + if (key == K_MOUSE1) + { + i = (mousecursor_y - mods->y)/8; + if (i < 0 || i > mods->nummanifests) + return false; + man = mods->manifests[i]; + mods->manifests[i] = NULL; + M_RemoveMenu(m); + FS_ChangeGame(man, true); + + //starting to a blank state generally means that the current config settings are utterly useless and windowed by default. + //so generally when switching to a *real* game, we want to restart video just so things like fullscreen etc are saved+used properly. + //if we're already running a game, this should probably just be a vid_reload instead to ensure that the conback etc is reloaded. + Cbuf_AddText("\nvid_restart\n", RESTRICT_LOCAL); + return true; + } + + return false; +} +static void Mods_Remove (struct menu_s *m) +{ + modmenu_t *mods = m->data; + int i; + + for (i = 0; i < mods->nummanifests; i++) + { + if (mods->manifests[i]) + FS_Manifest_Free(mods->manifests[i]); + } + Z_Free(mods->manifests); + mods->manifests = NULL; +} + +static qboolean Mods_AddMod(void *usr, ftemanifest_t *man) +{ + modmenu_t *mods = usr; + int i = mods->nummanifests; + mods->manifests = BZ_Realloc(mods->manifests, (i+1) * sizeof(*mods->manifests)); + mods->manifests[i] = man; + mods->nummanifests = i+1; + return true; +} + +#include "fs.h" + +void M_Menu_Mods_f (void) +{ + modmenu_t *mods; + menucustom_t *c; + menu_t *menu; + + Key_Dest_Add(kdm_menu); + + menu = M_CreateMenu(sizeof(modmenu_t)); + mods = menu->data; + MC_AddPicture(menu, 16, 4, 32, 144, "gfx/qplaque.lmp"); + MC_AddCenterPicture(menu, 0, 24, "gfx/p_option.lmp"); + + c = MC_AddCustom(menu, 64, 32, mods, 0); + menu->cursoritem = (menuoption_t*)c; + c->draw = Mods_Draw; + c->key = Mods_Key; + menu->remove = Mods_Remove; + + FS_EnumerateKnownGames(Mods_AddMod, menu->data); + + if (mods->nummanifests == 1) + { + ftemanifest_t *man = mods->manifests[0]; + mods->manifests[0] = NULL; + M_RemoveMenu(menu); + FS_ChangeGame(man, true); + } +} \ No newline at end of file diff --git a/engine/client/m_script.c b/engine/client/m_script.c index 301a1078..cbc4031c 100644 --- a/engine/client/m_script.c +++ b/engine/client/m_script.c @@ -243,10 +243,10 @@ void M_MenuS_Text_f (void) return; } if (Cmd_Argc() == 4) - MC_AddBufferedText(menu_script, x, y, text, false, false); + MC_AddBufferedText(menu_script, x, 0, y, text, false, false); else { - option = (menuoption_t *)MC_AddConsoleCommand(menu_script, x, y, text, va("menucallback %s\n", command)); + option = (menuoption_t *)MC_AddConsoleCommand(menu_script, x, 0, y, text, va("menucallback %s\n", command)); if (selectitem-- == 0) menu_script->selecteditem = option; } diff --git a/engine/client/m_single.c b/engine/client/m_single.c index 7f7f3fc9..db8fded4 100644 --- a/engine/client/m_single.c +++ b/engine/client/m_single.c @@ -79,13 +79,13 @@ void M_Menu_Save_f (void) menu->data = menu+1; MC_AddCenterPicture (menu, 4, 24, "gfx/p_save.lmp"); - menu->cursoritem = (menuoption_t *)MC_AddRedText(menu, 8, 32, NULL, false); + menu->cursoritem = (menuoption_t *)MC_AddRedText(menu, 8, 0, 32, NULL, false); M_ScanSaves (); for (i=0 ; i< MAX_SAVEGAMES; i++) { - op = (menuoption_t *)MC_AddConsoleCommandf(menu, 16, 32+8*i, m_filenames[i], "savegame s%i\nclosemenu\n", i); + op = (menuoption_t *)MC_AddConsoleCommandf(menu, 16, 170, 32+8*i, m_filenames[i], "savegame s%i\nclosemenu\n", i); if (!menu->selecteditem) menu->selecteditem = op; } @@ -103,16 +103,16 @@ void M_Menu_Load_f (void) menu->data = menu+1; MC_AddCenterPicture(menu, 4, 24, "gfx/p_load.lmp"); - menu->cursoritem = (menuoption_t *)MC_AddRedText(menu, 8, 32, NULL, false); + menu->cursoritem = (menuoption_t *)MC_AddRedText(menu, 8, 0, 32, NULL, false); M_ScanSaves (); for (i=0 ; i< MAX_SAVEGAMES; i++) { if (loadable[i]) - op = (menuoption_t *)MC_AddConsoleCommandf(menu, 16, 32+8*i, m_filenames[i], "loadgame s%i\nclosemenu\n", i); + op = (menuoption_t *)MC_AddConsoleCommandf(menu, 16, 170, 32+8*i, m_filenames[i], "loadgame s%i\nclosemenu\n", i); else - MC_AddWhiteText(menu, 16, 32+8*i, m_filenames[i], false); + MC_AddWhiteText(menu, 16, 170, 32+8*i, m_filenames[i], false); if (!menu->selecteditem && op) menu->selecteditem = op; } @@ -136,8 +136,8 @@ void M_Menu_SinglePlayer_f (void) #ifdef CLIENTONLY menu = M_CreateMenu(0); - MC_AddWhiteText(menu, 84, 12*8, "This build is unable", false); - MC_AddWhiteText(menu, 84, 13*8, "to start a local game", false); + MC_AddWhiteText(menu, 84, 0, 12*8, "This build is unable", false); + MC_AddWhiteText(menu, 84, 0, 13*8, "to start a local game", false); MC_AddBox (menu, 60, 10*8, 25, 4); #else @@ -151,14 +151,14 @@ void M_Menu_SinglePlayer_f (void) //quake2 uses the 'newgame' alias. menu->selecteditem = (menuoption_t*) - MC_AddConsoleCommand (menu, 64, 40, "Easy", "closemenu; skill 0;deathmatch 0; coop 0;newgame\n"); - MC_AddConsoleCommand (menu, 64, 48, "Medium", "closemenu; skill 1;deathmatch 0; coop 0;newgame\n"); - MC_AddConsoleCommand (menu, 64, 56, "Hard", "closemenu; skill 2;deathmatch 0; coop 0;newgame\n"); + MC_AddConsoleCommand (menu, 64, 170, 40, "Easy", "closemenu; skill 0;deathmatch 0; coop 0;newgame\n"); + MC_AddConsoleCommand (menu, 64, 170, 48, "Medium", "closemenu; skill 1;deathmatch 0; coop 0;newgame\n"); + MC_AddConsoleCommand (menu, 64, 170, 56, "Hard", "closemenu; skill 2;deathmatch 0; coop 0;newgame\n"); - MC_AddConsoleCommand (menu, 64, 72, "Load Game", "menu_load\n"); - MC_AddConsoleCommand (menu, 64, 80, "Save Game", "menu_save\n"); + MC_AddConsoleCommand (menu, 64, 170, 72, "Load Game", "menu_load\n"); + MC_AddConsoleCommand (menu, 64, 170, 80, "Save Game", "menu_save\n"); - menu->cursoritem = (menuoption_t*)MC_AddWhiteText(menu, 48, 40, NULL, false); + menu->cursoritem = (menuoption_t*)MC_AddWhiteText(menu, 48, 0, 40, NULL, false); return; } else if (mgt == MGT_HEXEN2) @@ -194,7 +194,7 @@ void M_Menu_SinglePlayer_f (void) MC_AddCenterPicture(menu, 0, 60, "gfx/menu/title2.lmp"); if (cl_splitscreen.ival) - MC_AddBufferedText(menu, 80, (y+=8)+12, va("Player %i\n", pnum), false, true); + MC_AddBufferedText(menu, 80, 0, (y+=8)+12, va("Player %i\n", pnum), false, true); for (i = 0; i < 4+havemp; i++) { @@ -335,21 +335,21 @@ void M_Menu_SinglePlayer_f (void) { MC_AddBox (menu, 60, 10*8, 23, 4); - MC_AddWhiteText(menu, 92, 12*8, "Could find file", false); - MC_AddWhiteText(menu, 92, 13*8, "gfx/sp_menu.lmp", false); + MC_AddWhiteText(menu, 92, 0, 12*8, "Couldn't find file", false); + MC_AddWhiteText(menu, 92, 0, 13*8, "gfx/sp_menu.lmp", false); } else { MC_AddPicture(menu, 72, 32, 232, 64, "gfx/sp_menu.lmp"); - b = MC_AddConsoleCommand (menu, 16, 32, "", "closemenu;disconnect;maxclients 1;deathmatch 0;coop 0;startmap_sp\n"); + b = MC_AddConsoleCommand (menu, 16, 304, 32, "", "closemenu;disconnect;maxclients 1;deathmatch 0;coop 0;startmap_sp\n"); menu->selecteditem = (menuoption_t *)b; b->common.width = p->width; b->common.height = 20; - b = MC_AddConsoleCommand (menu, 16, 52, "", "menu_load\n"); + b = MC_AddConsoleCommand (menu, 16, 304, 52, "", "menu_load\n"); b->common.width = p->width; b->common.height = 20; - b = MC_AddConsoleCommand (menu, 16, 72, "", "menu_save\n"); + b = MC_AddConsoleCommand (menu, 16, 304, 72, "", "menu_save\n"); b->common.width = p->width; b->common.height = 20; @@ -374,7 +374,7 @@ typedef struct { int pathlen; char path[MAX_OSPATH]; - int fsroot; //FS_ROOT, FS_GAME, FS_GAMEONLY. if FS_ROOT, executed command will have a leading # + int fsroot; //FS_SYSTEM, FS_GAME, FS_GAMEONLY. if FS_SYSTEM, executed command will have a leading # char *command[64]; //these let the menu be used for nearly any sort of file browser. char *ext[64]; @@ -491,8 +491,7 @@ static qboolean M_DemoKey(menucustom_t *control, menu_t *menu, int key) extnum = 0; Cbuf_AddText(va("%s \"%s%s\"\n", info->command[extnum], (info->fsroot==FS_ROOT)?"#":"", info->selected->name), RESTRICT_LOCAL); - M_ToggleMenu_f(); - + M_RemoveMenu(menu); } } return true; @@ -660,8 +659,8 @@ static void ShowDemoMenu (menu_t *menu, const char *path) { if (!strcmp(path, "../")) { - info->fsroot = FS_ROOT; FS_NativePath("", FS_ROOT, info->path, sizeof(info->path)); + info->fsroot = FS_SYSTEM; while((s = strchr(info->path, '\\'))) *s = '/'; } @@ -686,7 +685,7 @@ static void ShowDemoMenu (menu_t *menu, const char *path) info->pathlen = strlen(info->path); M_Demo_Flush(menu->data); - if (info->fsroot == FS_ROOT) + if (info->fsroot == FS_SYSTEM) { s = strchr(info->path, '/'); if (s && strchr(s+1, '/')) @@ -705,7 +704,7 @@ static void ShowDemoMenu (menu_t *menu, const char *path) Q_snprintfz(match, sizeof(match), "../"); DemoAddItem(match, 0, info, NULL); } - if (info->fsroot == FS_ROOT) + if (info->fsroot == FS_SYSTEM) { Q_snprintfz(match, sizeof(match), *info->path?"%s*":"/*", info->path); Sys_EnumerateFiles("", match, DemoAddItem, info, NULL); @@ -732,20 +731,32 @@ void M_Menu_Demos_f (void) info->fsroot = FS_GAME; - info->command[0] = "playdemo"; - info->ext[0] = ".qwd"; - info->command[1] = "playdemo"; - info->ext[1] = ".dem"; - info->command[2] = "playdemo"; - info->ext[2] = ".dm2"; - info->command[3] = "playdemo"; - info->ext[3] = ".mvd"; + info->numext = 0; + info->command[info->numext] = "playdemo"; + info->ext[info->numext++] = ".qwd"; + info->command[info->numext] = "playdemo"; + info->ext[info->numext++] = ".dem"; + info->command[info->numext] = "playdemo"; + info->ext[info->numext++] = ".dm2"; + info->command[info->numext] = "playdemo"; + info->ext[info->numext++] = ".mvd"; + info->command[info->numext] = "playdemo"; + info->ext[info->numext++] = ".mvd.gz"; //there are also qizmo demos (.qwz) out there... //we don't support them, but if we were to ask quizmo to decode them for us, we could do. - info->numext = 4; - MC_AddWhiteText(menu, 24, 8, "Choose a Demo", false); - MC_AddWhiteText(menu, 16, 24, "\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37", false); + //and some archive formats... for the luls + info->command[info->numext] = NULL; + info->ext[info->numext++] = ".zip"; + info->command[info->numext] = NULL; + info->ext[info->numext++] = ".pk3"; + info->command[info->numext] = NULL; + info->ext[info->numext++] = ".pk4"; + info->command[info->numext] = NULL; + info->ext[info->numext++] = ".pak"; + + MC_AddWhiteText(menu, 24, 170, 8, "Choose a Demo", false); + MC_AddWhiteText(menu, 16, 170, 24, "\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37", false); info->list = MC_AddCustom(menu, 0, 32, NULL, 0); info->list->draw = M_DemoDraw; @@ -788,8 +799,8 @@ void M_Menu_MediaFiles_f (void) info->numext++; #endif - MC_AddWhiteText(menu, 24, 8, "Media List", false); - MC_AddWhiteText(menu, 16, 24, "\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37", false); + MC_AddWhiteText(menu, 24, 170, 8, "Media List", false); + MC_AddWhiteText(menu, 16, 170, 24, "\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37", false); info->list = MC_AddCustom(menu, 0, 32, NULL, 0); info->list->draw = M_DemoDraw; diff --git a/engine/client/menu.c b/engine/client/menu.c index 2edf8a3f..7b74068c 100644 --- a/engine/client/menu.c +++ b/engine/client/menu.c @@ -23,6 +23,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. void M_Menu_Audio_f (void); void M_Menu_Demos_f (void); +void M_Menu_Mods_f (void); +void M_Menu_ModelViewer_f(void); m_state_t m_state; @@ -51,11 +53,9 @@ void M_DrawScalePic (int x, int y, int w, int h, mpic_t *pic) R2D_ScalePic (x + ((vid.width - 320)>>1), y, w, h, pic); } -void M_BuildTranslationTable(int top, int bottom, qbyte *translationTable) +void M_BuildTranslationTable(int top, int bottom, unsigned int *translationTable) { int j; - qbyte *dest, *source; - qbyte identityTable[256]; int pc = Cvar_Get("cl_playerclass", "1", 0, "Hexen2")->value; if (h2playertranslations && pc) @@ -67,38 +67,49 @@ void M_BuildTranslationTable(int top, int bottom, qbyte *translationTable) colorB = colorA + 256; sourceA = colorB + (top * 256); sourceB = colorB + (bottom * 256); - for(i=0;i<256;i++) + for(i=0;i<255;i++) { if (bottom > 0 && (colorB[i] != 255)) - translationTable[i] = sourceB[i]; + translationTable[i] = d_8to24rgbtable[sourceB[i]] | 0xff000000; else if (top > 0 && (colorA[i] != 255)) - translationTable[i] = sourceA[i]; + translationTable[i] = d_8to24rgbtable[sourceA[i]] | 0xff000000; else - translationTable[i] = i; + translationTable[i] = d_8to24rgbtable[i] | 0xff000000; } } else { - top *= 16; - bottom *= 16; - for (j = 0; j < 256; j++) - identityTable[j] = j; - dest = translationTable; - source = identityTable; - memcpy (dest, source, 256); - - if (top < 128) // the artists made some backwards ranges. sigh. - memcpy (dest + TOP_RANGE, source + top, 16); - else - for (j=0 ; j<16 ; j++) - dest[TOP_RANGE+j] = source[top+15-j]; - - if (bottom < 128) - memcpy (dest + BOTTOM_RANGE, source + bottom, 16); - else - for (j=0 ; j<16 ; j++) - dest[BOTTOM_RANGE+j] = source[bottom+15-j]; + for(j=0;j<255;j++) + { + if (j >= TOP_RANGE && j < TOP_RANGE + (1<<4)) + { + if (top >= 16) + { + *((unsigned char*)&translationTable[j]+0) = (((top&0xff0000)>>16)**((unsigned char*)&d_8to24rgbtable[j&15]+0))>>8; + *((unsigned char*)&translationTable[j]+1) = (((top&0x00ff00)>> 8)**((unsigned char*)&d_8to24rgbtable[j&15]+1))>>8; + *((unsigned char*)&translationTable[j]+2) = (((top&0x0000ff)>> 0)**((unsigned char*)&d_8to24rgbtable[j&15]+2))>>8; + *((unsigned char*)&translationTable[j]+3) = 0xff; + } + else + translationTable[j] = d_8to24rgbtable[top<8?j-TOP_RANGE+(top<<4):(top<<4)+15-(j-TOP_RANGE)] | 0xff000000; + } + else if (j >= BOTTOM_RANGE && j < BOTTOM_RANGE + (1<<4)) + { + if (bottom >= 16) + { + *((unsigned char*)&translationTable[j]+0) = (((bottom&0xff0000)>>16)**((unsigned char*)&d_8to24rgbtable[j&15]+0))>>8; + *((unsigned char*)&translationTable[j]+1) = (((bottom&0x00ff00)>> 8)**((unsigned char*)&d_8to24rgbtable[j&15]+1))>>8; + *((unsigned char*)&translationTable[j]+2) = (((bottom&0x0000ff)>> 0)**((unsigned char*)&d_8to24rgbtable[j&15]+2))>>8; + *((unsigned char*)&translationTable[j]+3) = 0xff; + } + else + translationTable[j] = d_8to24rgbtable[bottom<8?j-BOTTOM_RANGE+(bottom<<4):(bottom<<4)+15-(j-BOTTOM_RANGE)] | 0xff000000; + } + else + translationTable[j] = d_8to24rgbtable[j] | 0xff000000; + } } + translationTable[255] = 0; //alpha } @@ -454,7 +465,7 @@ int M_FindKeysForBind (char *command, int *keylist, int total) return count; } -void M_FindKeysForCommand (int pnum, char *command, int *twokeys) +void M_FindKeysForCommand (int pnum, const char *command, int *twokeys) { char prefix[5]; @@ -485,7 +496,7 @@ void M_FindKeysForCommand (int pnum, char *command, int *twokeys) M_FindKeysForBind(va("%s%s", prefix, command), twokeys, 2); } -void M_UnbindCommand (char *command) +void M_UnbindCommand (const char *command) { int j; int l; @@ -507,32 +518,38 @@ void M_UnbindCommand (char *command) /* HELP MENU */ int help_page; -char *helpstyle; int num_help_pages; -int helppagemin; +struct +{ + char *pattern; + int base; +} helpstyles[] = +{ + {"gfx/help%i.dxt",0}, //quake extended + {"gfx/help%i.tga",0}, //quake extended + {"gfx/help%i.png",0}, //quake extended + {"gfx/help%i.jpeg",0}, //quake extended + {"gfx/help%i.lmp",0}, //quake + {"gfx/menu/help%02i.lmp",1} //hexen2 +}; void M_Menu_Help_f (void) { + int i; Key_Dest_Add(kdm_menu); m_state = m_help; help_page = 0; - if (COM_FDepthFile("gfx/help0.lmp", true) <= COM_FDepthFile("gfx/menu/help1.lmp", true)) - { - helpstyle = "gfx/help%i.lmp"; - helppagemin=0; - } - else - { - helpstyle = "gfx/menu/help%02i.lmp"; - helppagemin = 1; - } - num_help_pages = 1; while(num_help_pages < 100) { - if (!COM_FDepthFile(va(helpstyle, num_help_pages+helppagemin), true)) + for (i = 0; i < sizeof(helpstyles)/sizeof(helpstyles[0]); i++) + { + if (COM_FDepthFile(va(helpstyles[i].pattern, num_help_pages+helpstyles[i].base), true)) + break; + } + if (i == sizeof(helpstyles)/sizeof(helpstyles[0])) break; num_help_pages++; } @@ -542,8 +559,10 @@ void M_Menu_Help_f (void) void M_Help_Draw (void) { - mpic_t *pic; - pic = R2D_SafeCachePic(va(helpstyle, help_page+helppagemin)); + int i; + mpic_t *pic = NULL; + for (i = 0; i < sizeof(helpstyles)/sizeof(helpstyles[0]) && !pic; i++) + pic = R2D_SafeCachePic(va(helpstyles[i].pattern, help_page+helpstyles[i].base)); if (!pic) M_Menu_Main_f (); else @@ -649,13 +668,13 @@ void M_Menu_Prompt (void (*callback)(void *, int), void *ctx, char *m1, char *m2 strcpy(t, optioncancel); optioncancel = t; - MC_AddWhiteText(&m->m, 64, 84, m1, false); - MC_AddWhiteText(&m->m, 64, 92, m2, false); - MC_AddWhiteText(&m->m, 64, 100, m3, false); + MC_AddWhiteText(&m->m, 64, 0, 84, m1, false); + MC_AddWhiteText(&m->m, 64, 0, 92, m2, false); + MC_AddWhiteText(&m->m, 64, 0, 100, m3, false); - m->b_yes = MC_AddCommand(&m->m, 64, 116, optionyes, M_Menu_Prompt_Button); - m->b_no = MC_AddCommand(&m->m, 144,116, optionno, M_Menu_Prompt_Button); - m->b_cancel = MC_AddCommand(&m->m, 224,116, optioncancel, M_Menu_Prompt_Button); + m->b_yes = MC_AddCommand(&m->m, 64, 0, 116, optionyes, M_Menu_Prompt_Button); + m->b_no = MC_AddCommand(&m->m, 144,0, 116, optionno, M_Menu_Prompt_Button); + m->b_cancel = MC_AddCommand(&m->m, 224,0, 116, optioncancel, M_Menu_Prompt_Button); m->m.selecteditem = (menuoption_t *)m->b_cancel; @@ -920,14 +939,14 @@ void M_Menu_Quit_f (void) quitmenu = M_CreateMenuInfront(0); quitmenu->key = MC_SaveQuit_Key; - MC_AddWhiteText(quitmenu, 64, 84, "You have unsaved settings ", false); - MC_AddWhiteText(quitmenu, 64, 92, " Would you like to ", false); - MC_AddWhiteText(quitmenu, 64, 100, " save them now? ", false); - - quitmenu->selecteditem = (menuoption_t *) - MC_AddConsoleCommand (quitmenu, 64, 116, "Yes", "menu_quit forcesave\n"); - MC_AddConsoleCommand (quitmenu, 144,116, "No", "menu_quit force\n"); - MC_AddConsoleCommand (quitmenu, 224,116, "Cancel", "menupop\n"); + MC_AddWhiteText(quitmenu, 64, 0, 84, "You have unsaved settings ", false); + MC_AddWhiteText(quitmenu, 64, 0, 92, " Would you like to ", false); + MC_AddWhiteText(quitmenu, 64, 0, 100, " save them now? ", false); + + quitmenu->selecteditem = (menuoption_t *) + MC_AddConsoleCommand (quitmenu, 64, 0, 116, "Yes", "menu_quit forcesave\n"); + MC_AddConsoleCommand (quitmenu, 144,0, 116, "No", "menu_quit force\n"); + MC_AddConsoleCommand (quitmenu, 224,0, 116, "Cancel", "menupop\n"); MC_AddBox (quitmenu, 56, 76, 25, 5); break; @@ -942,14 +961,14 @@ void M_Menu_Quit_f (void) i = rand()&7; - MC_AddWhiteText(quitmenu, 64, 84, quitMessage[i*4+0], false); - MC_AddWhiteText(quitmenu, 64, 92, quitMessage[i*4+1], false); - MC_AddWhiteText(quitmenu, 64, 100, quitMessage[i*4+2], false); - MC_AddWhiteText(quitmenu, 64, 108, quitMessage[i*4+3], false); - + MC_AddWhiteText(quitmenu, 64, 0, 84, quitMessage[i*4+0], false); + MC_AddWhiteText(quitmenu, 64, 0, 92, quitMessage[i*4+1], false); + MC_AddWhiteText(quitmenu, 64, 0, 100, quitMessage[i*4+2], false); + MC_AddWhiteText(quitmenu, 64, 0, 108, quitMessage[i*4+3], false); + quitmenu->selecteditem = (menuoption_t *) - MC_AddConsoleCommand (quitmenu, 120, 116, "Yes", "menu_quit force\n"); - MC_AddConsoleCommand (quitmenu, 208,116, "No", "menupop\n"); + MC_AddConsoleCommand (quitmenu, 120, 0, 116, "Yes", "menu_quit force\n"); + MC_AddConsoleCommand (quitmenu, 208, 0, 116, "No", "menupop\n"); MC_AddBox (quitmenu, 56, 76, 24, 5); break; @@ -1007,6 +1026,8 @@ void M_Init_Internal (void) Cmd_AddCommand ("menu_quit", M_Menu_Quit_f); Cmd_AddCommand ("menu_media", M_Menu_Media_f); Cmd_AddCommand ("menu_mediafiles", M_Menu_MediaFiles_f); + Cmd_AddCommand ("menu_mods", M_Menu_Mods_f); + Cmd_AddCommand ("modelviewer", M_Menu_ModelViewer_f); #ifdef CL_MASTER Cmd_AddCommand ("menu_servers", M_Menu_ServerList2_f); @@ -1111,12 +1132,13 @@ void M_DeInit_Internal (void) Cmd_RemoveCommand ("quickconnect"); } -void M_Shutdown(void) +void M_Shutdown(qboolean total) { #ifdef MENU_DAT MP_Shutdown(); #endif - M_DeInit_Internal(); + if (total) + M_DeInit_Internal(); } void M_Reinit(void) diff --git a/engine/client/menu.h b/engine/client/menu.h index 06827af0..2aacc79b 100644 --- a/engine/client/menu.h +++ b/engine/client/menu.h @@ -103,7 +103,7 @@ void M_SomeInitialisationFunctionCalledAtStartup(void) // void M_Init (void); void M_Reinit(void); -void M_Shutdown(void); +void M_Shutdown(qboolean total); void M_Keydown (int key, int unicode); void M_Keyup (int key, int unicode); void M_Draw (int uimenu); @@ -290,9 +290,9 @@ typedef struct menu_s { menuoption_t *cursoritem; } menu_t; -menutext_t *MC_AddBufferedText(menu_t *menu, int x, int y, const char *text, qboolean rightalign, qboolean red); -menutext_t *MC_AddRedText(menu_t *menu, int x, int y, const char *text, qboolean rightalign); -menutext_t *MC_AddWhiteText(menu_t *menu, int x, int y, const char *text, qboolean rightalign); +menutext_t *MC_AddBufferedText(menu_t *menu, int lhs, int rhs, int y, const char *text, qboolean rightalign, qboolean red); +menutext_t *MC_AddRedText(menu_t *menu, int lhs, int rhs, int y, const char *text, qboolean rightalign); +menutext_t *MC_AddWhiteText(menu_t *menu, int lhs, int rhs, int y, const char *text, qboolean rightalign); menubind_t *MC_AddBind(menu_t *menu, int cx, int bx, int y, const char *caption, char *command); menubox_t *MC_AddBox(menu_t *menu, int x, int y, int width, int height); menupicture_t *MC_AddPicture(menu_t *menu, int x, int y, int width, int height, char *picname); @@ -302,15 +302,14 @@ menupicture_t *MC_AddCursor(menu_t *menu, int x, int y); menuslider_t *MC_AddSlider(menu_t *menu, int tx, int sx, int y, const char *text, cvar_t *var, float min, float max, float delta); menucheck_t *MC_AddCheckBox(menu_t *menu, int tx, int cx, int y, const char *text, cvar_t *var, int cvarbitmask); menucheck_t *MC_AddCheckBoxFunc(menu_t *menu, int tx, int cx, int y, const char *text, qboolean (*func) (menucheck_t *option, menu_t *menu, chk_set_t set), int bits); -menubutton_t *MC_AddConsoleCommand(menu_t *menu, int x, int y, const char *text, const char *command); +menubutton_t *MC_AddConsoleCommand(menu_t *menu, int lhs, int rhs, int y, const char *text, const char *command); menubutton_t *MC_AddConsoleCommandQBigFont(menu_t *menu, int x, int y, const char *text, const char *command); mpic_t *QBigFontWorks(void); menubutton_t *MC_AddConsoleCommandHexen2BigFont(menu_t *menu, int x, int y, const char *text, const char *command); -menubutton_t *VARGS MC_AddConsoleCommandf(menu_t *menu, int x, int y, const char *text, char *command, ...); -menubutton_t *MC_AddCommand(menu_t *menu, int x, int y, char *text, qboolean (*command) (union menuoption_s *,struct menu_s *,int)); +menubutton_t *VARGS MC_AddConsoleCommandf(menu_t *menu, int lhs, int rhs, int y, const char *text, char *command, ...); +menubutton_t *MC_AddCommand(menu_t *menu, int lhs, int rhs, int y, char *text, qboolean (*command) (union menuoption_s *,struct menu_s *,int)); menucombo_t *MC_AddCombo(menu_t *menu, int tx, int cx, int y, const char *caption, const char **ops, int initialvalue); menucombo_t *MC_AddCvarCombo(menu_t *menu, int tx, int cx, int y, const char *caption, cvar_t *cvar, const char **ops, const char **values); -menubutton_t *MC_AddCommand(menu_t *menu, int x, int y, char *text, qboolean (*command) (union menuoption_s *,struct menu_s *,int)); menuedit_t *MC_AddEdit(menu_t *menu, int cx, int ex, int y, char *text, char *def); menuedit_t *MC_AddEditCvar(menu_t *menu, int cx, int ex, int y, char *text, char *name, qboolean slim); menucustom_t *MC_AddCustom(menu_t *menu, int x, int y, void *dptr, int dint); @@ -432,15 +431,15 @@ void M_DrawServers(void); void M_SListKey(int key); //drawing funcs -void M_BuildTranslationTable(int top, int bottom, qbyte *translationTable); +void M_BuildTranslationTable(int top, int bottom, unsigned int *translationTable); void M_DrawCharacter (int cx, int line, unsigned int num); void M_Print (int cx, int cy, qbyte *str); void M_PrintWhite (int cx, int cy, qbyte *str); void M_DrawScalePic (int x, int y, int w, int h, mpic_t *pic); -void M_FindKeysForCommand (int pnum, char *command, int *twokeys); -void M_UnbindCommand (char *command); +void M_FindKeysForCommand (int pnum, const char *command, int *twokeys); +void M_UnbindCommand (const char *command); void MP_CvarChanged(cvar_t *var); qboolean MP_Init (void); diff --git a/engine/client/merged.h b/engine/client/merged.h index 149da302..774d4f80 100644 --- a/engine/client/merged.h +++ b/engine/client/merged.h @@ -10,7 +10,13 @@ struct entity_s; struct dlight_s; struct galiasbone_s; - +typedef enum +{ + SKEL_RELATIVE, //relative to parent. + SKEL_ABSOLUTE, //relative to model. doesn't blend very well. + SKEL_INVERSE_RELATIVE, //pre-inverted. faster than regular relative but has weirdness with skeletal objects. blends okay. + SKEL_INVERSE_ABSOLUTE //final renderable type. +} skeltype_t; #ifdef HALFLIFEMODELS #define MAX_BONE_CONTROLLERS 5 @@ -32,9 +38,11 @@ typedef struct { int endbone; } g[FS_COUNT]; +#ifdef SKELETALOBJECTS float *bonestate; int bonecount; - qboolean boneabs; + skeltype_t skeltype; +#endif #ifdef HALFLIFEMODELS float bonecontrols[MAX_BONE_CONTROLLERS]; //hl special bone controllers @@ -55,12 +63,12 @@ typedef struct { extern r_qrenderer_t qrenderer; extern char *q_renderername; -mpic_t *R2D_SafeCachePic (char *path); -mpic_t *R2D_SafePicFromWad (char *name); +mpic_t *R2D_SafeCachePic (const char *path); +mpic_t *R2D_SafePicFromWad (const char *name); void R2D_DrawCrosshair (void); void R2D_ScalePic (float x, float y, float width, float height, mpic_t *pic); void R2D_SubPic(float x, float y, float width, float height, mpic_t *pic, float srcx, float srcy, float srcwidth, float srcheight); -void R2D_TransPicTranslate (float x, float y, int width, int height, qbyte *pic, qbyte *translation); +void R2D_TransPicTranslate (float x, float y, int width, int height, qbyte *pic, unsigned int *palette); void R2D_TileClear (float x, float y, float w, float h); void R2D_FadeScreen (void); @@ -98,7 +106,7 @@ extern void SCR_SetUpToDrawConsole (void); extern void SCR_EraseCenterString (void); extern void SCR_CenterPrint (int pnum, char *str, qboolean skipgamecode); -void R_DrawTextField(int x, int y, int w, int h, char *text, unsigned int defaultmask, unsigned int fieldflags); +void R_DrawTextField(int x, int y, int w, int h, const char *text, unsigned int defaultmask, unsigned int fieldflags); #define CPRINT_BALIGN (1<<0) //B #define CPRINT_TALIGN (1<<1) //T #define CPRINT_LALIGN (1<<2) //L @@ -118,25 +126,32 @@ enum mod_purge_e MP_FLUSH, //user flush command. anything flushable goes. MP_RESET //*everything* is destroyed. renderer is going down. }; +enum mlverbosity_e +{ + MLV_SILENT, + MLV_WARN, + MLV_ERROR +}; extern void Mod_ClearAll (void); extern void Mod_Purge (enum mod_purge_e type); -extern struct model_s *Mod_ForName (char *name, qboolean crash); -extern struct model_s *Mod_FindName (char *name); +extern struct model_s *Mod_FindName (const char *name); //find without loading. needload should be set. +extern struct model_s *Mod_ForName (const char *name, enum mlverbosity_e verbosity); //finds+loads +extern struct model_s *Mod_LoadModel (struct model_s *mod, enum mlverbosity_e verbose); //makes sure a model is loaded extern void *Mod_Extradata (struct model_s *mod); // handles caching -extern void Mod_TouchModel (char *name); +extern void Mod_TouchModel (const char *name); extern void Mod_NowLoadExternal (void); extern void Mod_Think (void); -extern int Mod_SkinNumForName (struct model_s *model, char *name); -extern int Mod_FrameNumForName (struct model_s *model, char *name); +extern int Mod_SkinNumForName (struct model_s *model, const char *name); +extern int Mod_FrameNumForName (struct model_s *model, const char *name); extern float Mod_GetFrameDuration (struct model_s *model, int framenum); #undef FNC extern qboolean Mod_GetTag (struct model_s *model, int tagnum, framestate_t *framestate, float *transforms); -extern int Mod_TagNumForName (struct model_s *model, char *name); +extern int Mod_TagNumForName (struct model_s *model, const char *name); int Mod_GetNumBones(struct model_s *model, qboolean allowtags); int Mod_GetBoneRelations(struct model_s *model, int firstbone, int lastbone, framestate_t *fstate, float *result); @@ -291,12 +306,12 @@ typedef struct rendererinfo_s { void (*Draw_Init) (void); void (*Draw_Shutdown) (void); - texid_tf (*IMG_LoadTexture) (char *identifier, int width, int height, uploadfmt_t fmt, void *data, unsigned int flags); - texid_tf (*IMG_LoadTexture8Pal24) (char *identifier, int width, int height, qbyte *data, qbyte *palette24, unsigned int flags); - texid_tf (*IMG_LoadTexture8Pal32) (char *identifier, int width, int height, qbyte *data, qbyte *palette32, unsigned int flags); - texid_tf (*IMG_LoadCompressed) (char *name); - texid_tf (*IMG_FindTexture) (char *identifier, unsigned int flags); - texid_tf (*IMG_AllocNewTexture) (char *identifier, int w, int h, unsigned int flags); + texid_tf (*IMG_LoadTexture) (const char *identifier, int width, int height, uploadfmt_t fmt, void *data, unsigned int flags); + texid_tf (*IMG_LoadTexture8Pal24) (const char *identifier, int width, int height, qbyte *data, qbyte *palette24, unsigned int flags); + texid_tf (*IMG_LoadTexture8Pal32) (const char *identifier, int width, int height, qbyte *data, qbyte *palette32, unsigned int flags); + texid_tf (*IMG_LoadCompressed) (const char *name); + texid_tf (*IMG_FindTexture) (const char *identifier, unsigned int flags); + texid_tf (*IMG_AllocNewTexture) (const char *identifier, int w, int h, unsigned int flags); void (*IMG_Upload) (texid_t tex, char *name, uploadfmt_t fmt, void *data, void *palette, int width, int height, unsigned int flags); void (*IMG_DestroyTexture) (texid_t tex); @@ -385,4 +400,5 @@ typedef struct rendererinfo_s { texid_t R2D_RT_Configure(unsigned int id, int width, int height, uploadfmt_t rtfmt); texid_t R2D_RT_GetTexture(unsigned int id, unsigned int *width, unsigned int *height); +texid_t R2D_RT_DetachTexture(unsigned int id); diff --git a/engine/client/net_master.c b/engine/client/net_master.c index 65d33cb6..92c62aca 100644 --- a/engine/client/net_master.c +++ b/engine/client/net_master.c @@ -63,7 +63,7 @@ typedef struct { hostcachekey_t fieldindex; float operandi; - char *operands; + const char *operands; qboolean or; int compareop; @@ -175,7 +175,7 @@ qboolean Master_CompareInteger(int a, int b, slist_test_t rule) } return false; } -qboolean Master_CompareString(char *a, char *b, slist_test_t rule) +qboolean Master_CompareString(const char *a, const char *b, slist_test_t rule) { switch(rule) { @@ -325,7 +325,7 @@ void Master_ClearMasks(void) numvisrules = 0; } -void Master_SetMaskString(qboolean or, hostcachekey_t field, char *param, slist_test_t testop) +void Master_SetMaskString(qboolean or, hostcachekey_t field, const char *param, slist_test_t testop) { if (numvisrules == MAX_VISRULES) return; //just don't add it. @@ -540,7 +540,7 @@ char *Master_ReadKeyString(serverinfo_t *server, int keynum) return ""; } -int Master_KeyForName(char *keyname) +int Master_KeyForName(const char *keyname) { int i; if (!strcmp(keyname, "map")) diff --git a/engine/client/p_classic.c b/engine/client/p_classic.c index 59fcdf0d..b6fa3333 100644 --- a/engine/client/p_classic.c +++ b/engine/client/p_classic.c @@ -27,9 +27,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define POLYS -void D_DrawParticleTrans (vec3_t porg, float palpha, float pscale, unsigned int pcolour, blendmode_t blendmode); - - typedef enum { DODGY, @@ -95,7 +92,7 @@ static union c } classiccolours[BUFFERVERTS]; static vec2_t classictexcoords[BUFFERVERTS]; static index_t classicindexes[BUFFERVERTS]; -mesh_t classicmesh; +static mesh_t classicmesh; #endif static shader_t *classicshader; @@ -103,7 +100,7 @@ static shader_t *classicshader; //obtains an index for the name, even if it is unknown (one can be loaded after. will only fail if the effect limit is reached) //technically this function is not meant to fail often, but thats fine so long as the other functions are meant to safely reject invalid effect numbers. -static int PClassic_FindParticleType(char *name) +static int PClassic_FindParticleType(const char *name) { if (!stricmp("tr_rocket", name)) return ROCKET_TRAIL; @@ -134,7 +131,7 @@ static int PClassic_FindParticleType(char *name) return P_INVALID; } -qboolean PClassic_Query(int type, int body, char *outstr, int outstrlen) +static qboolean PClassic_Query(int type, int body, char *outstr, int outstrlen) { char *n = NULL; switch(type) @@ -765,6 +762,10 @@ static float Classic_ParticleTrail (vec3_t start, vec3_t end, float leftover, ef scale = 6; break; default: scale = 3; break; + case TRACER1_TRAIL: + case TRACER2_TRAIL: + scale = (r_part_density.value < 0.5)?6*r_part_density.value:3; + break; } scale /= r_part_density.value; diff --git a/engine/client/p_null.c b/engine/client/p_null.c index d9f918c2..62885f9e 100644 --- a/engine/client/p_null.c +++ b/engine/client/p_null.c @@ -5,7 +5,7 @@ #include "renderque.h" //returns a valid effect if its existance is known. -static int PNULL_FindParticleType(char *name) +static int PNULL_FindParticleType(const char *name) { // Con_DPrintf("P_FindParticleType %s\n", name); return P_INVALID; diff --git a/engine/client/p_script.c b/engine/client/p_script.c index 46693d27..55d367c9 100644 --- a/engine/client/p_script.c +++ b/engine/client/p_script.c @@ -462,7 +462,7 @@ static int P_AllocateParticleType(char *config, char *name) //guarentees that th } //public interface. get without creating. -static int PScript_FindParticleType(char *name) +static int PScript_FindParticleType(const char *name) { int i; part_type_t *ptype = NULL; @@ -3171,7 +3171,7 @@ static void PScript_EffectSpawned(part_type_t *ptype, vec3_t org, vec3_t dir, in { mod = &ptype->models[rand() % ptype->nummodels]; if (!mod->model) - mod->model = Mod_ForName(mod->name, false); + mod->model = Mod_ForName(mod->name, MLV_WARN); if (mod->model && !mod->model->needload) { vec3_t morg, mdir; diff --git a/engine/client/pr_clcmd.c b/engine/client/pr_clcmd.c index 63223c9e..14b499a2 100644 --- a/engine/client/pr_clcmd.c +++ b/engine/client/pr_clcmd.c @@ -284,7 +284,7 @@ int MP_TranslateQCtoFTECodes(int code) //string findkeysforcommand(string command) = #610; void QCBUILTIN PF_cl_findkeysforcommand (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *cmdname = PR_GetStringOfs(prinst, OFS_PARM0); + const char *cmdname = PR_GetStringOfs(prinst, OFS_PARM0); int keynums[2]; char keyname[512]; @@ -308,7 +308,7 @@ void QCBUILTIN PF_cl_stringtokeynum(pubprogfuncs_t *prinst, struct globalvars_s { int i; int modifier; - char *s; + const char *s; s = PR_GetStringOfs(prinst, OFS_PARM0); i = Key_StringToKeynum(s, &modifier); @@ -353,7 +353,7 @@ void QCBUILTIN PF_cl_runningserver (pubprogfuncs_t *prinst, struct globalvars_s // #487 float(string name) gecko_create( string name ) void QCBUILTIN PF_cs_gecko_create (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *shadername = PR_GetStringOfs(prinst, OFS_PARM0); + const char *shadername = PR_GetStringOfs(prinst, OFS_PARM0); cin_t *cin; cin = R_ShaderGetCinematic(R_RegisterShader(shadername, SUF_2D, "{\n" @@ -375,8 +375,8 @@ void QCBUILTIN PF_cs_gecko_destroy (pubprogfuncs_t *prinst, struct globalvars_s // #489 void(string name) gecko_navigate( string name, string URI ) void QCBUILTIN PF_cs_gecko_navigate (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *shader = PR_GetStringOfs(prinst, OFS_PARM0); - char *command = PR_GetStringOfs(prinst, OFS_PARM1); + const char *shader = PR_GetStringOfs(prinst, OFS_PARM0); + const char *command = PR_GetStringOfs(prinst, OFS_PARM1); cin_t *cin; cin = R_ShaderFindCinematic(shader); @@ -388,7 +388,7 @@ void QCBUILTIN PF_cs_gecko_navigate (pubprogfuncs_t *prinst, struct globalvars_s // #490 float(string name) gecko_keyevent( string name, float key, float eventtype ) void QCBUILTIN PF_cs_gecko_keyevent (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *shader = PR_GetStringOfs(prinst, OFS_PARM0); + const char *shader = PR_GetStringOfs(prinst, OFS_PARM0); int key = G_FLOAT(OFS_PARM1); int eventtype = G_FLOAT(OFS_PARM2); cin_t *cin; @@ -401,7 +401,7 @@ void QCBUILTIN PF_cs_gecko_keyevent (pubprogfuncs_t *prinst, struct globalvars_s // #491 void gecko_mousemove( string name, float x, float y ) void QCBUILTIN PF_cs_gecko_mousemove (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *shader = PR_GetStringOfs(prinst, OFS_PARM0); + const char *shader = PR_GetStringOfs(prinst, OFS_PARM0); float posx = G_FLOAT(OFS_PARM1); float posy = G_FLOAT(OFS_PARM2); cin_t *cin; @@ -414,7 +414,7 @@ void QCBUILTIN PF_cs_gecko_mousemove (pubprogfuncs_t *prinst, struct globalvars_ // #492 void gecko_resize( string name, float w, float h ) void QCBUILTIN PF_cs_gecko_resize (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *shader = PR_GetStringOfs(prinst, OFS_PARM0); + const char *shader = PR_GetStringOfs(prinst, OFS_PARM0); float sizex = G_FLOAT(OFS_PARM1); float sizey = G_FLOAT(OFS_PARM2); cin_t *cin; @@ -426,7 +426,7 @@ void QCBUILTIN PF_cs_gecko_resize (pubprogfuncs_t *prinst, struct globalvars_s * // #493 vector gecko_get_texture_extent( string name ) void QCBUILTIN PF_cs_gecko_get_texture_extent (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *shader = PR_GetStringOfs(prinst, OFS_PARM0); + const char *shader = PR_GetStringOfs(prinst, OFS_PARM0); float *ret = G_VECTOR(OFS_RETURN); int sx, sy; cin_t *cin; @@ -448,7 +448,7 @@ void QCBUILTIN PF_cs_gecko_get_texture_extent (pubprogfuncs_t *prinst, struct gl void QCBUILTIN PF_soundlength (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *sample = PR_GetStringOfs(prinst, OFS_PARM0); + const char *sample = PR_GetStringOfs(prinst, OFS_PARM0); sfx_t *sfx = S_PrecacheSound(sample); if (!sfx || sfx->failedload) @@ -512,7 +512,7 @@ void QCBUILTIN PF_cl_sethostcachemaskstring(pubprogfuncs_t *prinst, struct globa { int mask = G_FLOAT(OFS_PARM0); int field = G_FLOAT(OFS_PARM1); - char *str = PR_GetStringOfs(prinst, OFS_PARM2); + const char *str = PR_GetStringOfs(prinst, OFS_PARM2); int op = G_FLOAT(OFS_PARM3); Master_SetMaskString(mask, field, str, op); @@ -571,7 +571,7 @@ void QCBUILTIN PF_cl_gethostcachestring (pubprogfuncs_t *prinst, struct globalva //float gethostcacheindexforkey(string key) = #622; void QCBUILTIN PF_cl_gethostcacheindexforkey(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *keyname = PR_GetStringOfs(prinst, OFS_PARM0); + const char *keyname = PR_GetStringOfs(prinst, OFS_PARM0); G_FLOAT(OFS_RETURN) = Master_KeyForName(keyname); } @@ -610,12 +610,10 @@ void PF_cl_addwantedhostcachekey(pubprogfuncs_t *prinst, struct globalvars_s *pr #endif - - void QCBUILTIN PF_shaderforname (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *str = PR_GetStringOfs(prinst, OFS_PARM0); - char *defaultbody = PF_VarString(prinst, 1, pr_globals); + const char *str = PR_GetStringOfs(prinst, OFS_PARM0); + const char *defaultbody = PF_VarString(prinst, 1, pr_globals); shader_t *shad; diff --git a/engine/client/pr_csqc.c b/engine/client/pr_csqc.c index f7f3b961..a6d74ebd 100644 --- a/engine/client/pr_csqc.c +++ b/engine/client/pr_csqc.c @@ -64,7 +64,6 @@ static unsigned int csprogs_checksum; static csqctreadstate_t *csqcthreads; qboolean csqc_resortfrags; qboolean csqc_addcrosshair; -static int csqc_fakereadbyte; world_t csqc_world; int csqc_playerseat; //can be negative. @@ -81,7 +80,7 @@ cvar_t pr_csqc_memsize = CVAR("pr_csqc_memsize", "-1"); cvar_t cl_csqcdebug = CVAR("cl_csqcdebug", "0"); //prints entity numbers which arrive (so I can tell people not to apply it to players...) cvar_t cl_nocsqc = CVAR("cl_nocsqc", "0"); cvar_t pr_csqc_coreonerror = CVAR("pr_csqc_coreonerror", "1"); -cvar_t pr_csqc_formenus = CVAR("pr_csqc_formenus", "1"); +cvar_t pr_csqc_formenus = CVAR("pr_csqc_formenus", "0"); extern cvar_t dpcompat_stats; cvar_t dpcompat_corruptglobals = CVAR("dpcompat_corruptglobals", "0"); @@ -382,6 +381,7 @@ typedef struct csqcedict_s //add whatever you wish here trailstate_t *trailstate; + int skinobject; } csqcedict_t; @@ -426,7 +426,7 @@ static int maxcsqcentities; static int csqcentsize; -static char *csqcmapentitydata; +static const char *csqcmapentitydata; static qboolean csqcmapentitydataloaded; qboolean csqc_deprecated_warned; @@ -525,7 +525,7 @@ static void QCBUILTIN PF_cs_remove (pubprogfuncs_t *prinst, struct globalvars_s static void QCBUILTIN PF_cvar (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { cvar_t *var; - char *str; + const char *str; str = PR_GetStringOfs(prinst, OFS_PARM0); @@ -588,7 +588,7 @@ static model_t *CSQC_GetModelForIndex(int index) else if (index < 0 && index > -MAX_CSMODELS) { if (!cl.model_csqcprecache[-index]) - cl.model_csqcprecache[-index] = Mod_ForName(cl.model_csqcname[-index], false); + cl.model_csqcprecache[-index] = Mod_ForName(cl.model_csqcname[-index], MLV_WARN); return cl.model_csqcprecache[-index]; } else @@ -724,6 +724,7 @@ static qboolean CopyCSQCEdictToEntity(csqcedict_t *in, entity_t *out) out->forcedshader = r_shaders[(ival-1)]; else out->forcedshader = NULL; + out->customskin = (in->skinobject<0)?-in->skinobject:in->skinobject; out->keynum = -in->entnum; @@ -782,7 +783,7 @@ static void QCBUILTIN PF_R_AddDecal(pubprogfuncs_t *prinst, struct globalvars_s static void QCBUILTIN PF_R_DynamicLight_Set(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *s; + const char *s; dlight_t *l; unsigned int lno = G_FLOAT(OFS_PARM0); int field = G_FLOAT(OFS_PARM1); @@ -924,7 +925,7 @@ static void QCBUILTIN PF_R_DynamicLight_Add(pubprogfuncs_t *prinst, struct globa float radius = G_FLOAT(OFS_PARM1); float *rgb = G_VECTOR(OFS_PARM2); float style = (prinst->callargc > 3)?G_FLOAT(OFS_PARM3):0; - char *cubemapname = (prinst->callargc > 4)?PR_GetStringOfs(prinst, OFS_PARM4):""; + const char *cubemapname = (prinst->callargc > 4)?PR_GetStringOfs(prinst, OFS_PARM4):""; int pflags = (prinst->callargc > 5)?G_FLOAT(OFS_PARM5):PFLAGS_CORONA; wedict_t *self = PROG_TO_WEDICT(prinst, *csqcg.self); @@ -1629,6 +1630,7 @@ static void QCBUILTIN PF_R_RenderScene(pubprogfuncs_t *prinst, struct globalvars Sbar_DrawScoreboard (); } #endif + SCR_ShowPics_Draw(); } if (csqc_addcrosshair) @@ -1862,7 +1864,7 @@ static void QCBUILTIN PF_cs_pointcontents(pubprogfuncs_t *prinst, struct globalv G_FLOAT(OFS_RETURN) = Q1CONTENTS_EMPTY; } -static int FindModel(char *name, int *free) +static int FindModel(const char *name, int *free) { int i; @@ -1900,7 +1902,7 @@ static void csqc_setmodel(pubprogfuncs_t *prinst, csqcedict_t *ent, int modelind return; ent->v->model = PR_SetString(prinst, cl.model_csqcname[-modelindex]); if (!cl.model_csqcprecache[-modelindex]) - cl.model_csqcprecache[-modelindex] = Mod_ForName(cl.model_csqcname[-modelindex], false); + cl.model_csqcprecache[-modelindex] = Mod_ForName(cl.model_csqcname[-modelindex], MLV_WARN); model = cl.model_csqcprecache[-modelindex]; } else @@ -1927,7 +1929,7 @@ static void csqc_setmodel(pubprogfuncs_t *prinst, csqcedict_t *ent, int modelind static void QCBUILTIN PF_cs_SetModel(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { csqcedict_t *ent = (void*)G_EDICT(prinst, OFS_PARM0); - char *modelname = PR_GetStringOfs(prinst, OFS_PARM1); + const char *modelname = PR_GetStringOfs(prinst, OFS_PARM1); int freei; int modelindex = FindModel(modelname, &freei); @@ -1954,7 +1956,7 @@ static void QCBUILTIN PF_cs_SetModelIndex(pubprogfuncs_t *prinst, struct globalv static void QCBUILTIN PF_cs_PrecacheModel(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { int modelindex, freei; - char *modelname = PR_GetStringOfs(prinst, OFS_PARM0); + const char *modelname = PR_GetStringOfs(prinst, OFS_PARM0); int i; if (!*modelname) @@ -1969,7 +1971,7 @@ static void QCBUILTIN PF_cs_PrecacheModel(pubprogfuncs_t *prinst, struct globalv break; if (!strcmp(cl.model_name[i], modelname)) { - cl.model_precache[i] = Mod_ForName(cl.model_name[i], false); + cl.model_precache[i] = Mod_ForName(cl.model_name[i], MLV_WARN); break; } } @@ -1989,9 +1991,14 @@ static void QCBUILTIN PF_cs_PrecacheModel(pubprogfuncs_t *prinst, struct globalv G_FLOAT(OFS_RETURN) = modelindex; } +static void QCBUILTIN PF_cs_precachefile(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +{ + const char *filename = PR_GetStringOfs(prinst, OFS_PARM0); + G_FLOAT(OFS_RETURN) = CL_CheckOrEnqueDownloadFile(filename, NULL, 0); +} static void QCBUILTIN PF_cs_PrecacheSound(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *soundname = PR_GetStringOfs(prinst, OFS_PARM0); + const char *soundname = PR_GetStringOfs(prinst, OFS_PARM0); Sound_CheckDownload(soundname); S_PrecacheSound(soundname); } @@ -2005,6 +2012,26 @@ static void QCBUILTIN PF_cs_ModelnameForIndex(pubprogfuncs_t *prinst, struct glo else G_INT(OFS_RETURN) = (int)PR_SetString(prinst, cl.model_name[modelindex]); } +void QCBUILTIN PF_cs_setcustomskin (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +{ + csqcedict_t *ent = (void*)G_EDICT(prinst, OFS_PARM0); + const char *fname = PR_GetStringOfs(prinst, OFS_PARM1); + const char *skindata = PF_VarString(prinst, 2, pr_globals); + + if (ent->skinobject > 0) + { + Mod_WipeSkin(ent->skinobject); + ent->skinobject = 0; + } + + if (*fname || *skindata) + { + if (*skindata) + ent->skinobject = Mod_ReadSkinFile(fname, skindata); + else + ent->skinobject = -(int)Mod_RegisterSkinFile(fname); + } +} static void QCBUILTIN PF_ReadByte(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { @@ -2014,15 +2041,7 @@ static void QCBUILTIN PF_ReadByte(pubprogfuncs_t *prinst, struct globalvars_s *p G_FLOAT(OFS_RETURN) = -1; return; } - if (csqc_fakereadbyte != -1) - { - G_FLOAT(OFS_RETURN) = csqc_fakereadbyte; - csqc_fakereadbyte = -1; - } - else - { - G_FLOAT(OFS_RETURN) = MSG_ReadByte(); - } + G_FLOAT(OFS_RETURN) = MSG_ReadByte(); } static void QCBUILTIN PF_ReadChar(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) @@ -2143,7 +2162,7 @@ static void QCBUILTIN PF_objerror (pubprogfuncs_t *prinst, struct globalvars_s * } } -static void QCBUILTIN PF_cs_setsensativityscaler (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +static void QCBUILTIN PF_cs_setsensitivityscaler (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { in_sensitivityscale = G_FLOAT(OFS_PARM0); } @@ -2220,7 +2239,9 @@ static void QCBUILTIN PF_cs_trailparticles (pubprogfuncs_t *prinst, struct globa static void QCBUILTIN PF_cs_particleeffectnum (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { int i; - char *effectname = PR_GetStringOfs(prinst, OFS_PARM0); + const char *effectname = PR_GetStringOfs(prinst, OFS_PARM0); + + G_FLOAT(OFS_RETURN) = 0; //default to failure. //use the server's index first. for (i = 1; i < MAX_SSPARTICLESPRE && cl.particle_ssname[i]; i++) @@ -2236,7 +2257,10 @@ static void QCBUILTIN PF_cs_particleeffectnum (pubprogfuncs_t *prinst, struct gl { if (!strcmp(cl.particle_csname[i], effectname)) { - G_FLOAT(OFS_RETURN) = -i; + //effects can be in the list despite now being stale. they still take up a slot to avoid reuse as the qc can potentially still potentially reference it. + //csqc needs to be able to detect a now-stale effect + if (cl.particle_csprecache[i] != P_INVALID) + G_FLOAT(OFS_RETURN) = -i; return; } } @@ -2244,12 +2268,14 @@ static void QCBUILTIN PF_cs_particleeffectnum (pubprogfuncs_t *prinst, struct gl { free(cl.particle_csname[i]); cl.particle_csname[i] = NULL; - cl.particle_csprecache[i] = 1+P_FindParticleType(effectname); - if (cl.particle_csprecache[i]) + cl.particle_csprecache[i] = P_FindParticleType(effectname); + if (cl.particle_csprecache[i] != P_INVALID) + { + //it exists, allow it. cl.particle_csname[i] = strdup(effectname); - G_FLOAT(OFS_RETURN) = -i; + G_FLOAT(OFS_RETURN) = -i; + } } - G_FLOAT(OFS_RETURN) = 0; //if we're using dp network protocols, we should use the effectinfo.txt file as a lookup table instead. //G_FLOAT(OFS_RETURN) = COM_Effectinfo_ForName(effectname); @@ -2275,8 +2301,8 @@ static void QCBUILTIN PF_cs_sendevent (pubprogfuncs_t *prinst, struct globalvars { csqcedict_t *ent; int i; - char *eventname = PR_GetStringOfs(prinst, OFS_PARM0); - char *argtypes = PR_GetStringOfs(prinst, OFS_PARM1); + const char *eventname = PR_GetStringOfs(prinst, OFS_PARM0); + const char *argtypes = PR_GetStringOfs(prinst, OFS_PARM1); if (!cls.state) return; @@ -2532,7 +2558,7 @@ static void QCBUILTIN PF_cs_getentitytoken (pubprogfuncs_t *prinst, struct globa { if (prinst->callargc) { - char *s = PR_GetStringOfs(prinst, OFS_PARM0); + const char *s = PR_GetStringOfs(prinst, OFS_PARM0); if (*s == 0) s = cl.worldmodel?cl.worldmodel->entities:NULL; csqcmapentitydata = s; @@ -2674,7 +2700,7 @@ static void QCBUILTIN PF_cs_getplayerkey (pubprogfuncs_t *prinst, struct globalv char buffer[64]; char *ret; int pnum = G_FLOAT(OFS_PARM0); - char *keyname = PR_GetStringOfs(prinst, OFS_PARM1); + const char *keyname = PR_GetStringOfs(prinst, OFS_PARM1); if (pnum < 0) { if (csqc_resortfrags) @@ -2787,7 +2813,7 @@ static void QCBUILTIN PF_cs_getplayerkey (pubprogfuncs_t *prinst, struct globalv static void QCBUILTIN PF_checkextension (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *extname = PR_GetStringOfs(prinst, OFS_PARM0); + const char *extname = PR_GetStringOfs(prinst, OFS_PARM0); int i; for (i = 0; i < QSG_Extensions_count; i++) { @@ -2809,7 +2835,7 @@ static void QCBUILTIN PF_checkextension (pubprogfuncs_t *prinst, struct globalva static void QCBUILTIN PF_cs_sound(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *sample; + const char *sample; int channel; csqcedict_t *entity; float volume; @@ -2835,7 +2861,7 @@ static void QCBUILTIN PF_cs_sound(pubprogfuncs_t *prinst, struct globalvars_s *p static void QCBUILTIN PF_cs_pointsound(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *sample; + const char *sample; float *origin; float volume; float attenuation; @@ -2920,13 +2946,13 @@ static void QCBUILTIN PF_cs_particle4(pubprogfuncs_t *prinst, struct globalvars_ void QCBUILTIN PF_cl_effect(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { float *org = G_VECTOR(OFS_PARM0); - char *name = PR_GetStringOfs(prinst, OFS_PARM1); + const char *name = PR_GetStringOfs(prinst, OFS_PARM1); float startframe = G_FLOAT(OFS_PARM2); float endframe = G_FLOAT(OFS_PARM3); float framerate = G_FLOAT(OFS_PARM4); model_t *mdl; - mdl = Mod_ForName(name, false); + mdl = Mod_ForName(name, MLV_WARN); if (mdl) CL_SpawnSpriteEffect(org, NULL, mdl, startframe, endframe, framerate, 1, 0, 0); else @@ -2935,7 +2961,7 @@ void QCBUILTIN PF_cl_effect(pubprogfuncs_t *prinst, struct globalvars_s *pr_glob void QCBUILTIN PF_cl_ambientsound(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *samp; + const char *samp; float *pos; float vol, attenuation; @@ -2957,7 +2983,7 @@ static void QCBUILTIN PF_cs_vectorvectors (pubprogfuncs_t *prinst, struct global static void QCBUILTIN PF_cs_lightstyle (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { int stnum = G_FLOAT(OFS_PARM0); - char *str = PR_GetStringOfs(prinst, OFS_PARM1); + const char *str = PR_GetStringOfs(prinst, OFS_PARM1); int colourflags = 7; if ((unsigned)stnum >= MAX_LIGHTSTYLES) @@ -2970,38 +2996,6 @@ static void QCBUILTIN PF_cs_lightstyle (pubprogfuncs_t *prinst, struct globalvar cl_lightstyle[stnum].length = Q_strlen(cl_lightstyle[stnum].map); } -static void QCBUILTIN PF_cs_findradius (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) -{ - csqcedict_t *ent, *chain; - float rad; - float *org; - vec3_t eorg; - int i, j; - - chain = (csqcedict_t *)*prinst->parms->sv_edicts; - - org = G_VECTOR(OFS_PARM0); - rad = G_FLOAT(OFS_PARM1); - - for (i=1 ; i<*prinst->parms->sv_num_edicts ; i++) - { - ent = (void*)EDICT_NUM(prinst, i); - if (ent->isfree) - continue; -// if (ent->v->solid == SOLID_NOT && !sv_gameplayfix_blowupfallenzombies.value) -// continue; - for (j=0 ; j<3 ; j++) - eorg[j] = org[j] - (ent->v->origin[j] + (ent->v->mins[j] + ent->v->maxs[j])*0.5); - if (Length(eorg) > rad) - continue; - - ent->v->chain = EDICT_TO_PROG(prinst, (void*)chain); - chain = ent; - } - - RETURN_EDICT(prinst, (void*)chain); -} - //entity(string field, float match) findchainflags = #450 //chained search for float, int, and entity reference fields static void QCBUILTIN PF_cs_findchainflags (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) @@ -3063,7 +3057,7 @@ static void QCBUILTIN PF_cs_findchainfloat (pubprogfuncs_t *prinst, struct globa static void QCBUILTIN PF_cs_findchain (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { int i, f; - char *s; + const char *s; string_t t; csqcedict_t *ent, *chain; @@ -3418,7 +3412,7 @@ void CSQC_RunThreads(void) static void QCBUILTIN PF_cs_addprogs (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *s = PR_GetStringOfs(prinst, OFS_PARM0); + const char *s = PR_GetStringOfs(prinst, OFS_PARM0); int newp; if (!s || !*s) newp = -1; @@ -3975,7 +3969,7 @@ void CSQC_DeltaEnd(void) static void QCBUILTIN PF_DeltaListen(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { int i; - char *mname = PR_GetStringOfs(prinst, OFS_PARM0); + const char *mname = PR_GetStringOfs(prinst, OFS_PARM0); func_t func = G_INT(OFS_PARM1); unsigned int flags = G_FLOAT(OFS_PARM2); @@ -4324,7 +4318,7 @@ static struct { //20 {"precache_model", PF_cs_PrecacheModel, 20}, // #20 void(string str) precache_model (QUAKE) {"stuffcmd", PF_NoCSQC, 21}, // #21 void(entity client, string s) stuffcmd (QUAKE) (don't support) - {"findradius", PF_cs_findradius, 22}, // #22 entity(vector org, float rad) findradius (QUAKE) + {"findradius", PF_findradius, 22}, // #22 entity(vector org, float rad) findradius (QUAKE) {"bprint", PF_NoCSQC, 23}, // #23 void(string s, ...) bprint (QUAKE) (don't support) {"sprint", PF_NoCSQC, 24}, // #24 void(entity e, string s, ...) sprint (QUAKE) (don't support) {"dprint", PF_dprint, 25}, // #25 void(string s, ...) dprint (QUAKE) @@ -4376,8 +4370,8 @@ static struct { {"etos", PF_etos, 65}, // #65 string(entity ent) etos (DP_QC_ETOS) {"?", PF_Fixme, 66}, // #66 - {"movetogoal", PF_cs_movetogoal, 67}, // #67 void(float step) movetogoal (QUAKE) - {"precache_file", PF_NoCSQC, 68}, // #68 void(string s) precache_file (QUAKE) (don't support) + {"movetogoal", PF_cs_movetogoal, 67}, // #67 void(float step) movetogoal (QUAKE) + {"precache_file", PF_cs_precachefile, 68}, // #68 void(string s) precache_file (QUAKE) (don't support) {"makestatic", PF_cs_makestatic, 69}, // #69 void(entity e) makestatic (QUAKE) //70 {"changelevel", PF_NoCSQC, 70}, // #70 void(string mapname) changelevel (QUAKE) (don't support) @@ -4388,7 +4382,7 @@ static struct { {"precache_model2", PF_cs_PrecacheModel, 75}, // #75 void(string str) precache_model2 (QUAKE) {"precache_sound2", PF_cs_PrecacheSound, 76}, // #76 void(string str) precache_sound2 (QUAKE) - {"precache_file2", PF_NoCSQC, 77}, // #77 void(string str) precache_file2 (QUAKE) + {"precache_file2", PF_cs_precachefile, 77}, // #77 void(string str) precache_file2 (QUAKE) {"setspawnparms", PF_NoCSQC, 78}, // #78 void() setspawnparms (QUAKE) (don't support) {"logfrag", PF_NoCSQC, 79}, // #79 void(entity killer, entity killee) logfrag (QW_ENGINE) (don't support) @@ -4602,7 +4596,7 @@ static struct { {"getmousepos", PF_cl_getmousepos, 344}, // #344 This is a DP extension {"getinputstate", PF_cs_getinputstate, 345}, // #345 float(float framenum) getinputstate (EXT_CSQC) - {"setsensitivityscaler", PF_cs_setsensativityscaler, 346}, // #346 void(float sens) setsensitivityscaler (EXT_CSQC) + {"setsensitivityscaler", PF_cs_setsensitivityscaler, 346}, // #346 void(float sens) setsensitivityscaler (EXT_CSQC) {"runstandardplayerphysics",PF_cs_runplayerphysics, 347}, // #347 void() runstandardplayerphysics (EXT_CSQC) @@ -4644,6 +4638,7 @@ static struct { {"dynamiclight_set", PF_R_DynamicLight_Set, 373}, {"particleeffectquery", PF_cs_particleeffectquery, 374}, {"adddecal", PF_R_AddDecal, 375}, + {"setcustomskin", PF_cs_setcustomskin, 376}, {"memalloc", PF_memalloc, 384}, {"memfree", PF_memfree, 385}, @@ -4997,6 +4992,10 @@ pbool QDECL CSQC_EntFree (struct edict_s *e) ent->xv->drawmask = 0; ent->xv->renderflags = 0; + if (ent->skinobject>0) + Mod_WipeSkin(ent->skinobject); + ent->skinobject = 0; + #ifdef USEODE World_ODE_RemoveFromEntity(&csqc_world, (wedict_t*)ent); World_ODE_RemoveJointFromEntity(&csqc_world, (wedict_t*)ent); @@ -5228,9 +5227,6 @@ qboolean CSQC_Inited(void) qboolean CSQC_UnconnectedOkay(qboolean inprinciple) { -#ifndef _DEBUG - return false; -#endif if (!pr_csqc_formenus.ival) return false; @@ -5377,6 +5373,8 @@ qboolean CSQC_Init (qboolean anycsqc, qboolean csdatenabled, unsigned int checks if (csprogsnum != -1) Con_Printf(CON_WARNING "Running outdated or unknown csprogs.dat version\n"); } + if (csprogsnum == -1) + Con_DPrintf("Loaded csprogs.dat\n"); } if (csqc_singlecheats || anycsqc) @@ -5403,7 +5401,6 @@ qboolean CSQC_Init (qboolean anycsqc, qboolean csdatenabled, unsigned int checks csqc_world.physicstime = 0; - csqc_fakereadbyte = -1; memset(csqcent, 0, sizeof(*csqcent)*maxcsqcentities); //clear the server->csqc entity translations. for (i = 0; i < csqcprogs->numprogs; i++) @@ -5477,7 +5474,7 @@ void CSQC_WorldLoaded(void) { char *map; csqcedict_t *worldent; - char *entfile; + const char *entfile; if (!csqcprogs) return; @@ -5503,7 +5500,7 @@ void CSQC_WorldLoaded(void) if (csqcg.worldloaded) PR_ExecuteProgram(csqcprogs, csqcg.worldloaded); csqcmapentitydata = NULL; - BZ_Free(entfile); + BZ_Free((char*)entfile); worldent->readonly = true; } @@ -5842,6 +5839,8 @@ qboolean CSQC_DrawView(void) if (csqcg.intermission) *csqcg.intermission = cl.intermission; + //work out which packet entities are solid + CL_SetSolidEntities (); CL_TransitionEntities(); if (cl.worldmodel) CL_PredictMove (); @@ -5998,22 +5997,24 @@ static void CSQC_GameCommand_f(void) PR_ExecuteProgram (csqcprogs, csqcg.gamecommand); } -#ifdef warningmsg -#pragma warningmsg("do we really need the firstbyte parameter here?") -#endif -qboolean CSQC_ParseTempEntity(unsigned char firstbyte) +qboolean CSQC_ParseTempEntity(void) { + int orc; void *pr_globals; if (!csqcprogs || !csqcg.parse_tempentity) return false; - csqc_fakereadbyte = firstbyte; pr_globals = PR_globals(csqcprogs, PR_CURRENT); csqc_mayread = true; + orc = msg_readcount; PR_ExecuteProgram (csqcprogs, csqcg.parse_tempentity); csqc_mayread = false; - csqc_fakereadbyte = -1; - return !!G_FLOAT(OFS_RETURN); + if (G_FLOAT(OFS_RETURN)) + return true; + //failed. reset the read position. + msg_readcount = orc; + msg_badread = false; + return false; } qboolean CSQC_ParseGamePacket(void) diff --git a/engine/client/pr_menu.c b/engine/client/pr_menu.c index 4a118b15..d814fa0e 100644 --- a/engine/client/pr_menu.c +++ b/engine/client/pr_menu.c @@ -115,7 +115,7 @@ void PR_CL_BeginString(pubprogfuncs_t *prinst, float vx, float vy, float szx, fl } Font_BeginScaledString(font, vx, vy, szx, szy, px, py); } -int PR_findnamedfont(char *name, qboolean isslotname) +int PR_findnamedfont(const char *name, qboolean isslotname) { int i; if (isslotname) @@ -176,14 +176,14 @@ void PR_ResetFonts(unsigned int purgeowner) } void QCBUILTIN PF_CL_findfont (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *slotname = PR_GetStringOfs(prinst, OFS_PARM0); + const char *slotname = PR_GetStringOfs(prinst, OFS_PARM0); G_FLOAT(OFS_RETURN) = PR_findnamedfont(slotname, true) + 1; //return default on failure. } void QCBUILTIN PF_CL_loadfont (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *slotname = PR_GetStringOfs(prinst, OFS_PARM0); - char *facename = PR_GetStringOfs(prinst, OFS_PARM1); - char *sizestr = PR_GetStringOfs(prinst, OFS_PARM2); + const char *slotname = PR_GetStringOfs(prinst, OFS_PARM0); + const char *facename = PR_GetStringOfs(prinst, OFS_PARM1); + const char *sizestr = PR_GetStringOfs(prinst, OFS_PARM2); int slotnum = G_FLOAT(OFS_PARM3); //float fix_scale = G_FLOAT(OFS_PARM4); //float fix_voffset = G_FLOAT(OFS_PARM5); @@ -345,7 +345,7 @@ void QCBUILTIN PF_CL_DrawTextField (pubprogfuncs_t *prinst, struct globalvars_s float *pos = G_VECTOR(OFS_PARM0); float *size = G_VECTOR(OFS_PARM1); unsigned int flags = G_FLOAT(OFS_PARM2); - char *text = PR_GetStringOfs(prinst, OFS_PARM3); + const char *text = PR_GetStringOfs(prinst, OFS_PARM3); R_DrawTextField(pos[0], pos[1], size[0], size[1], text, CON_WHITEMASK, flags); } @@ -353,7 +353,7 @@ void QCBUILTIN PF_CL_DrawTextField (pubprogfuncs_t *prinst, struct globalvars_s void QCBUILTIN PF_CL_drawcolouredstring (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { float *pos = G_VECTOR(OFS_PARM0); - char *text = PR_GetStringOfs(prinst, OFS_PARM1); + const char *text = PR_GetStringOfs(prinst, OFS_PARM1); float *size = G_VECTOR(OFS_PARM2); float alpha = 0; float flag = 0; @@ -411,7 +411,7 @@ void QCBUILTIN PF_CL_stringwidth(pubprogfuncs_t *prinst, struct globalvars_s *pr { conchar_t buffer[2048], *end; float px, py; - char *text = PR_GetStringOfs(prinst, OFS_PARM0); + const char *text = PR_GetStringOfs(prinst, OFS_PARM0); int usecolours = G_FLOAT(OFS_PARM1); float *size = (prinst->callargc > 2)?G_VECTOR(OFS_PARM2):NULL; @@ -428,7 +428,7 @@ void QCBUILTIN PF_CL_stringwidth(pubprogfuncs_t *prinst, struct globalvars_s *pr void QCBUILTIN PF_CL_drawpic (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { float *pos = G_VECTOR(OFS_PARM0); - char *picname = PR_GetStringOfs(prinst, OFS_PARM1); + const char *picname = PR_GetStringOfs(prinst, OFS_PARM1); float *size = G_VECTOR(OFS_PARM2); float *rgb = G_VECTOR(OFS_PARM3); float alpha = G_FLOAT(OFS_PARM4); @@ -458,7 +458,7 @@ void QCBUILTIN PF_CL_drawsubpic (pubprogfuncs_t *prinst, struct globalvars_s *pr { float *pos = G_VECTOR(OFS_PARM0); float *size = G_VECTOR(OFS_PARM1); - char *picname = PR_GetStringOfs(prinst, OFS_PARM2); + const char *picname = PR_GetStringOfs(prinst, OFS_PARM2); float *srcPos = G_VECTOR(OFS_PARM3); float *srcSize = G_VECTOR(OFS_PARM4); float *rgb = G_VECTOR(OFS_PARM5); @@ -488,14 +488,14 @@ void QCBUILTIN PF_CL_drawsubpic (pubprogfuncs_t *prinst, struct globalvars_s *pr void QCBUILTIN PF_CL_is_cached_pic (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *str; + const char *str; str = PR_GetStringOfs(prinst, OFS_PARM0); G_FLOAT(OFS_RETURN) = !!R_RegisterCustom(str, SUF_2D, NULL, NULL); } void QCBUILTIN PF_CL_precache_pic (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *str; + const char *str; mpic_t *pic; float fromwad; @@ -573,7 +573,7 @@ void QCBUILTIN PF_CL_drawcharacter (pubprogfuncs_t *prinst, struct globalvars_s void QCBUILTIN PF_CL_drawrawstring (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { float *pos = G_VECTOR(OFS_PARM0); - char *text = PR_GetStringOfs(prinst, OFS_PARM1); + const char *text = PR_GetStringOfs(prinst, OFS_PARM1); float *size = G_VECTOR(OFS_PARM2); float *rgb = G_VECTOR(OFS_PARM3); float alpha = G_FLOAT(OFS_PARM4); @@ -595,7 +595,7 @@ void QCBUILTIN PF_CL_drawrawstring (pubprogfuncs_t *prinst, struct globalvars_s while(*text) { if (1)//VMUTF8) - c = unicode_decode(&error, text, &text, false); + c = unicode_decode(&error, text, (char**)&text, false); else { //FIXME: which charset is this meant to be using? @@ -666,7 +666,7 @@ void QCBUILTIN PF_CL_drawline (pubprogfuncs_t *prinst, struct globalvars_s *pr_g //vector drawgetimagesize(string pic) = #460; void QCBUILTIN PF_CL_drawgetimagesize (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *picname = PR_GetStringOfs(prinst, OFS_PARM0); + const char *picname = PR_GetStringOfs(prinst, OFS_PARM0); mpic_t *p = R2D_SafeCachePic(picname); float *ret = G_VECTOR(OFS_RETURN); @@ -715,9 +715,9 @@ void QCBUILTIN PF_cl_getmousepos (pubprogfuncs_t *prinst, struct globalvars_s *p void QCBUILTIN PF_SubConGetSet (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *conname = PR_GetStringOfs(prinst, OFS_PARM0); - char *field = PR_GetStringOfs(prinst, OFS_PARM1); - char *value = (prinst->callargc>2)?PR_GetStringOfs(prinst, OFS_PARM2):NULL; + const char *conname = PR_GetStringOfs(prinst, OFS_PARM0); + const char *field = PR_GetStringOfs(prinst, OFS_PARM1); + const char *value = (prinst->callargc>2)?PR_GetStringOfs(prinst, OFS_PARM2):NULL; console_t *con = Con_FindConsole(conname); G_INT(OFS_RETURN) = 0; if (!con) @@ -795,8 +795,8 @@ void QCBUILTIN PF_SubConGetSet (pubprogfuncs_t *prinst, struct globalvars_s *pr_ void QCBUILTIN PF_SubConPrintf (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { char outbuf[4096]; - char *conname = PR_GetStringOfs(prinst, OFS_PARM0); - char *fmt = PR_GetStringOfs(prinst, OFS_PARM1); + const char *conname = PR_GetStringOfs(prinst, OFS_PARM0); + const char *fmt = PR_GetStringOfs(prinst, OFS_PARM1); console_t *con = Con_FindConsole(conname); if (!con) return; @@ -805,7 +805,7 @@ void QCBUILTIN PF_SubConPrintf (pubprogfuncs_t *prinst, struct globalvars_s *pr_ } void QCBUILTIN PF_SubConDraw (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *conname = PR_GetStringOfs(prinst, OFS_PARM0); + const char *conname = PR_GetStringOfs(prinst, OFS_PARM0); float *pos = G_VECTOR(OFS_PARM1); float *size = G_VECTOR(OFS_PARM2); float fontsize = G_FLOAT(OFS_PARM3); @@ -826,7 +826,7 @@ qboolean Key_Console (console_t *con, unsigned int unicode, int key); void Key_ConsoleRelease (console_t *con, unsigned int unicode, int key); void QCBUILTIN PF_SubConInput (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *conname = PR_GetStringOfs(prinst, OFS_PARM0); + const char *conname = PR_GetStringOfs(prinst, OFS_PARM0); int ie = G_FLOAT(OFS_PARM1); float pa = G_FLOAT(OFS_PARM2); float pb = G_FLOAT(OFS_PARM3); @@ -914,7 +914,7 @@ void QCBUILTIN PF_mod (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) G_FLOAT(OFS_RETURN) = a % b; } -char *RemapCvarNameFromDPToFTE(char *name) +const char *RemapCvarNameFromDPToFTE(const char *name) { if (!stricmp(name, "vid_bitsperpixel")) return "vid_bpp"; @@ -942,7 +942,7 @@ char *RemapCvarNameFromDPToFTE(char *name) static void QCBUILTIN PF_menu_cvar (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { cvar_t *var; - char *str; + const char *str; str = PR_GetStringOfs(prinst, OFS_PARM0); @@ -971,7 +971,7 @@ static void QCBUILTIN PF_menu_cvar (pubprogfuncs_t *prinst, struct globalvars_s } static void QCBUILTIN PF_menu_cvar_set (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *var_name, *val; + const char *var_name, *val; cvar_t *var; var_name = PR_GetStringOfs(prinst, OFS_PARM0); @@ -983,7 +983,7 @@ static void QCBUILTIN PF_menu_cvar_set (pubprogfuncs_t *prinst, struct globalvar } static void QCBUILTIN PF_menu_cvar_string (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *str = PR_GetStringOfs(prinst, OFS_PARM0); + const char *str = PR_GetStringOfs(prinst, OFS_PARM0); cvar_t *cv = Cvar_Get(RemapCvarNameFromDPToFTE(str), "", 0, "QC variables"); G_INT( OFS_RETURN ) = (int)PR_SetString( prinst, cv->string ); } @@ -1076,7 +1076,7 @@ static void QCBUILTIN PF_Fixme (pubprogfuncs_t *prinst, struct globalvars_s *pr_ void QCBUILTIN PF_CL_precache_sound (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *str; + const char *str; str = PR_GetStringOfs(prinst, OFS_PARM0); @@ -1185,13 +1185,13 @@ static void QCBUILTIN PF_CopyEntity (pubprogfuncs_t *prinst, struct globalvars_s void QCBUILTIN PF_localsound (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *soundname = PR_GetStringOfs(prinst, OFS_PARM0); + const char *soundname = PR_GetStringOfs(prinst, OFS_PARM0); S_LocalSound (soundname); } void QCBUILTIN PF_menu_checkextension (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *extname = PR_GetStringOfs(prinst, OFS_PARM0); + const char *extname = PR_GetStringOfs(prinst, OFS_PARM0); int i; G_FLOAT(OFS_RETURN) = 0; @@ -1221,7 +1221,7 @@ void QCBUILTIN PF_CL_precache_file (pubprogfuncs_t *prinst, struct globalvars_s void QCBUILTIN PF_menu_findchain (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { int i, f; - char *s; + const char *s; string_t t; menuedict_t *ent, *chain; //note, all edicts share the common header, but don't use it's fields! eval_t *val; @@ -1330,7 +1330,7 @@ void QCBUILTIN PF_IsNotNull(pubprogfuncs_t *prinst, struct globalvars_s *pr_glob //returns number of single quoted strings in the string. void QCBUILTIN PF_altstr_count(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *s; + const char *s; int count = 0; s = PR_GetStringOfs(prinst, OFS_PARM0); for (;*s;s++) @@ -1349,7 +1349,7 @@ void QCBUILTIN PF_altstr_count(pubprogfuncs_t *prinst, struct globalvars_s *pr_g void QCBUILTIN PF_altstr_prepare(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { char outstr[8192], *out; - char *instr, *in; + const char *instr, *in; int size; // VM_SAFEPARMCOUNT( 1, VM_altstr_prepare ); @@ -1375,7 +1375,8 @@ void QCBUILTIN PF_altstr_prepare(pubprogfuncs_t *prinst, struct globalvars_s *pr //string altstr_get(string str, float num) = #84; void QCBUILTIN PF_altstr_get(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *altstr, *pos, outstr[8192], *out; + const char *altstr, *pos; + char outstr[8192], *out; int count, size; // VM_SAFEPARMCOUNT( 2, VM_altstr_get ); @@ -1422,8 +1423,8 @@ void QCBUILTIN PF_altstr_get(pubprogfuncs_t *prinst, struct globalvars_s *pr_glo void QCBUILTIN PF_altstr_set(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { int num; - char *altstr, *str; - char *in; + const char *altstr, *str; + const char *in; char outstr[8192], *out; // VM_SAFEPARMCOUNT( 3, VM_altstr_set ); @@ -1845,8 +1846,11 @@ void MP_Shutdown (void) Master_ClearMasks(); #endif - Key_Dest_Remove(kdm_menu); - m_state = 0; + if (m_state == m_menu_dat) + { + Key_Dest_Remove(kdm_menu); + m_state = 0; + } key_dest_absolutemouse &= ~kdm_menu; } @@ -1905,8 +1909,6 @@ qboolean MP_Init (void) if (forceqmenu.value) return false; - M_DeInit_Internal(); - MP_SetupBuiltins(); memset(&menuc_eval_chain, 0, sizeof(menuc_eval_chain)); @@ -1974,6 +1976,8 @@ qboolean MP_Init (void) } inmenuprogs++; + M_DeInit_Internal(); + PF_InitTempStrings(menu_world.progs); mp_time = (float*)PR_FindGlobal(menu_world.progs, "time", 0, NULL); @@ -2049,7 +2053,7 @@ void MP_CoreDump_f(void) void MP_Reload_f(void) { - M_Shutdown(); + M_Shutdown(true); M_Reinit(); } diff --git a/engine/client/pr_skelobj.c b/engine/client/pr_skelobj.c index 974feb7d..ec13fcd2 100644 --- a/engine/client/pr_skelobj.c +++ b/engine/client/pr_skelobj.c @@ -89,11 +89,7 @@ typedef struct skelobject_s model_t *model; world_t *world; /*be it ssqc or csqc*/ - enum - { - SKOT_RELATIVE, //relative to parent - SKOT_ABSOLUTE //relative to model - } type; + skeltype_t type; unsigned int numbones; float *bonematrix; @@ -124,7 +120,7 @@ void skel_copy_toabs(skelobject_t *skelobjdst, skelobject_t *skelobjsrc, int sta if (!boneinfo) return; endbone = min(endbone, maxbones-1); - if (skelobjsrc->type == SKOT_ABSOLUTE) + if (skelobjsrc->type == SKEL_ABSOLUTE) { if (skelobjsrc != skelobjdst) { @@ -171,7 +167,7 @@ void skel_copy_toabs(skelobject_t *skelobjdst, skelobject_t *skelobjsrc, int sta } } - skelobjdst->type = SKOT_ABSOLUTE; + skelobjdst->type = SKEL_ABSOLUTE; } static void bonemat_fromidentity(float *out) { @@ -303,7 +299,7 @@ typedef struct { odebodyinfo_t defbody; odejointinfo_t defjoint; } dollcreatectx_t; -static dollcreatectx_t *rag_createdoll(model_t *mod, char *fname, int numbones) +static dollcreatectx_t *rag_createdoll(model_t *mod, const char *fname, int numbones) { int i; dollcreatectx_t *ctx; @@ -603,7 +599,7 @@ static doll_t *rag_finishdoll(dollcreatectx_t *ctx) return d; }; -doll_t *rag_createdollfromstring(model_t *mod, char *fname, int numbones, char *file) +doll_t *rag_createdollfromstring(model_t *mod, const char *fname, int numbones, const char *file) { int linenum = 0; dollcreatectx_t *ctx; @@ -949,7 +945,7 @@ void skel_info_f(void) if (skelobjects[i].world == &csqc_world) Con_Printf(" CSQC\n"); #endif - Con_Printf(" type: %s\n", (skelobjects[i].type == SKOT_RELATIVE)?"parentspace":"modelspace"); + Con_Printf(" type: %s\n", (skelobjects[i].type == SKEL_RELATIVE)?"parentspace":"modelspace"); Con_Printf(" model: %s\n", skelobjects[i].model->name); Con_Printf(" bone count: %i\n", skelobjects[i].numbones); #ifdef RAGDOLL @@ -1066,7 +1062,7 @@ void skel_lookup(pubprogfuncs_t *prinst, int skelidx, framestate_t *out) skelobject_t *sko = skel_get(prinst, skelidx); if (sko && sko->inuse) { - out->boneabs = sko->type; + out->skeltype = sko->type; out->bonecount = sko->numbones; out->bonestate = sko->bonematrix; } @@ -1354,7 +1350,7 @@ void rag_derive(skelobject_t *sko, skelobject_t *asko, float *emat) } //if it wasn't before, it definitely is now. - sko->type = SKOT_ABSOLUTE; + sko->type = SKEL_ABSOLUTE; } //called each physics frame to update the body velocities for animation @@ -1411,7 +1407,7 @@ void rag_updatedeltaent(entity_t *ent, lerpents_t *le) skelobject_t skorel = {0}; float relmat[MAX_BONES*12]; skorel.bonematrix = relmat; - skorel.type = SKOT_RELATIVE; + skorel.type = SKEL_RELATIVE; if (mod->dollinfo) { @@ -1425,7 +1421,7 @@ void rag_updatedeltaent(entity_t *ent, lerpents_t *le) if (!sko) return; //couldn't get one, ran out of memory or something? sko->model = mod; - sko->type = SKOT_RELATIVE; + sko->type = SKEL_RELATIVE; le->skeletalobject = (sko - skelobjects) + 1; } else @@ -1466,7 +1462,7 @@ void rag_updatedeltaent(entity_t *ent, lerpents_t *le) ent->framestate.bonestate = sko->bonematrix; ent->framestate.bonecount = sko->numbones; - ent->framestate.boneabs = sko->type == SKOT_ABSOLUTE; + ent->framestate.skeltype = sko->type; } } #endif @@ -1480,7 +1476,7 @@ void QCBUILTIN PF_skel_ragedit(pubprogfuncs_t *prinst, struct globalvars_s *pr_g //do we want to be able to generate a ragdoll object with this function too? #ifdef RAGDOLL wedict_t *wed = (wedict_t*)G_EDICT(prinst, OFS_PARM0); - char *ragname = PR_GetStringOfs(prinst, OFS_PARM1); + const char *ragname = PR_GetStringOfs(prinst, OFS_PARM1); int parentskel = G_FLOAT(OFS_PARM2); int skelidx; skelobject_t *sko, *psko; @@ -1500,7 +1496,7 @@ void QCBUILTIN PF_skel_ragedit(pubprogfuncs_t *prinst, struct globalvars_s *pr_g //the parent skeletal object must be relative, if specified. psko = skel_get(prinst, parentskel); - if (psko && psko->type != SKOT_RELATIVE) + if (psko && psko->type != SKEL_RELATIVE) return; sko = skel_get(prinst, skelidx); @@ -1634,7 +1630,7 @@ void QCBUILTIN PF_skel_create (pubprogfuncs_t *prinst, struct globalvars_s *pr_g int type; midx = G_FLOAT(OFS_PARM0); - type = (prinst->callargc > 1)?G_FLOAT(OFS_PARM1):SKOT_RELATIVE; + type = (prinst->callargc > 1)?G_FLOAT(OFS_PARM1):SKEL_RELATIVE; //default to failure G_FLOAT(OFS_RETURN) = 0; @@ -1643,7 +1639,7 @@ void QCBUILTIN PF_skel_create (pubprogfuncs_t *prinst, struct globalvars_s *pr_g if (!model) return; //no model set, can't get a skeleton - numbones = Mod_GetNumBones(model, type != SKOT_RELATIVE); + numbones = Mod_GetNumBones(model, type != SKEL_RELATIVE); if (!numbones) { // isabs = true; @@ -1724,14 +1720,14 @@ void QCBUILTIN PF_skel_build(pubprogfuncs_t *prinst, struct globalvars_s *pr_glo if (lastbone < firstbone) lastbone = firstbone; - if (skelobj->type != SKOT_RELATIVE) + if (skelobj->type != SKEL_RELATIVE) { if (firstbone > 0 || lastbone < skelobj->numbones || retainfrac) { Con_Printf("skel_build on non-relative skeleton\n"); return; } - skelobj->type = SKOT_RELATIVE; //entire model will get replaced, convert it. + skelobj->type = SKEL_RELATIVE; //entire model will get replaced, convert it. } if (retainfrac == 0) @@ -1840,7 +1836,7 @@ void QCBUILTIN PF_skel_get_boneparent (pubprogfuncs_t *prinst, struct globalvars void QCBUILTIN PF_skel_find_bone (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { int skelidx = G_FLOAT(OFS_PARM0); - char *bname = PR_GetStringOfs(prinst, OFS_PARM1); + const char *bname = PR_GetStringOfs(prinst, OFS_PARM1); skelobject_t *skelobj; skelobj = skel_get(prinst, skelidx); @@ -1859,7 +1855,7 @@ void QCBUILTIN PF_skel_get_bonerel (pubprogfuncs_t *prinst, struct globalvars_s skelobject_t *skelobj = skel_get(prinst, skelidx); if (!skelobj || (unsigned int)boneidx >= skelobj->numbones) bonematident_toqcvectors(w->g.v_forward, w->g.v_right, w->g.v_up, G_VECTOR(OFS_RETURN)); - else if (skelobj->type!=SKOT_RELATIVE) + else if (skelobj->type!=SKEL_RELATIVE) { float tmp[12]; float invparent[12]; @@ -1886,7 +1882,7 @@ void QCBUILTIN PF_skel_get_boneabs (pubprogfuncs_t *prinst, struct globalvars_s if (!skelobj || (unsigned int)boneidx >= skelobj->numbones) bonematident_toqcvectors(w->g.v_forward, w->g.v_right, w->g.v_up, G_VECTOR(OFS_RETURN)); - else if (skelobj->type != SKOT_RELATIVE) + else if (skelobj->type != SKEL_RELATIVE) { //can just copy it out bonemat_toqcvectors(skelobj->bonematrix + boneidx*12, w->g.v_forward, w->g.v_right, w->g.v_up, G_VECTOR(OFS_RETURN)); @@ -1966,7 +1962,7 @@ void QCBUILTIN PF_skel_set_bone_world (pubprogfuncs_t *prinst, struct globalvars float parentent[12]; framestate_t fstate; w->Get_FrameState(w, ent, &fstate); - if (skelobj->type == SKOT_ABSOLUTE || !Mod_GetTag(skelobj->model, Mod_GetBoneParent(skelobj->model, boneidx+1), &fstate, parentabs)) + if (skelobj->type == SKEL_ABSOLUTE || !Mod_GetTag(skelobj->model, Mod_GetBoneParent(skelobj->model, boneidx+1), &fstate, parentabs)) { bonemat_fromentity(w, ent, parentw); } @@ -2110,7 +2106,7 @@ void QCBUILTIN PF_skel_copybones (pubprogfuncs_t *prinst, struct globalvars_s *p startbone++; } } - else if (skelobjsrc->type == SKOT_RELATIVE && skelobjdst->type == SKOT_ABSOLUTE) + else if (skelobjsrc->type == SKEL_RELATIVE && skelobjdst->type == SKEL_ABSOLUTE) { /*copy from relative to absolute*/ skel_copy_toabs(skelobjdst, skelobjsrc, startbone, endbone); @@ -2180,7 +2176,7 @@ void QCBUILTIN PF_gettagindex (pubprogfuncs_t *prinst, struct globalvars_s *pr_g { world_t *w = prinst->parms->user; wedict_t *ent = G_WEDICT(prinst, OFS_PARM0); - char *tagname = PR_GetStringOfs(prinst, OFS_PARM1); + const char *tagname = PR_GetStringOfs(prinst, OFS_PARM1); model_t *mod = *tagname?w->Get_CModel(w, ent->v->modelindex):NULL; if (mod) G_FLOAT(OFS_RETURN) = Mod_TagNumForName(mod, tagname); @@ -2206,21 +2202,6 @@ void QCBUILTIN PF_frametoname (pubprogfuncs_t *prinst, struct globalvars_s *pr_g G_INT(OFS_RETURN) = 0; //null string (which is also empty in qc) } -//string(float modidx, float skinnum) skintoname -void QCBUILTIN PF_skintoname (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) -{ - world_t *w = prinst->parms->user; - unsigned int modelindex = G_FLOAT(OFS_PARM0); - unsigned int skinnum = G_FLOAT(OFS_PARM1); - model_t *mod = w->Get_CModel(w, modelindex); - const char *n = Mod_SkinNameForNum(mod, skinnum); - - if (n) - RETURN_TSTRING(n); - else - G_INT(OFS_RETURN) = 0; //null string (which is also empty in qc) -} - void QCBUILTIN PF_frameforname (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { world_t *w = prinst->parms->user; @@ -2245,6 +2226,21 @@ void QCBUILTIN PF_frameduration (pubprogfuncs_t *prinst, struct globalvars_s *pr else G_FLOAT(OFS_RETURN) = 0; } + +//string(float modidx, float skinnum) skintoname +void QCBUILTIN PF_skintoname (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +{ + world_t *w = prinst->parms->user; + unsigned int modelindex = G_FLOAT(OFS_PARM0); + unsigned int skinnum = G_FLOAT(OFS_PARM1); + model_t *mod = w->Get_CModel(w, modelindex); + const char *n = Mod_SkinNameForNum(mod, skinnum); + + if (n) + RETURN_TSTRING(n); + else + G_INT(OFS_RETURN) = 0; //null string (which is also empty in qc) +} void QCBUILTIN PF_skinforname (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { #ifndef SERVERONLY diff --git a/engine/client/quakedef.h b/engine/client/quakedef.h index bd5b17c7..50832933 100644 --- a/engine/client/quakedef.h +++ b/engine/client/quakedef.h @@ -184,7 +184,9 @@ extern "C" { #include "world.h" #include "q2game.h" #include "../http/iweb.h" -#ifndef CLIENTONLY +#ifdef CLIENTONLY +#define SSV_IsSubServer() false +#else #include "server.h" #endif diff --git a/engine/client/r_2d.c b/engine/client/r_2d.c index caf543ef..a4c59219 100644 --- a/engine/client/r_2d.c +++ b/engine/client/r_2d.c @@ -200,6 +200,7 @@ void R2D_Init(void) "if $nofixed\n" "program default2d\n" "endif\n" + "affine\n" "nomipmaps\n" "{\n" "map $diffuse\n" @@ -268,6 +269,7 @@ void R2D_Init(void) shader_gammacb = R_RegisterShader("gammacbshader", SUF_NONE, "{\n" "program defaultgammacb\n" + "affine\n" "cull back\n" "{\n" "map $currentrender\n" @@ -288,6 +290,7 @@ void R2D_Init(void) ); shader_menutint = R_RegisterShader("menutint", SUF_NONE, "{\n" + "affine\n" "if $glsl && gl_menutint_shader != 0\n" "program menutint\n" "{\n" @@ -307,6 +310,7 @@ void R2D_Init(void) "if $nofixed\n" "program default2d\n" "endif\n" + "affine\n" "nomipmaps\n" "{\n" "map $diffuse\n" @@ -342,7 +346,7 @@ void R2D_Init(void) R2D_Font_Changed(); } -mpic_t *R2D_SafeCachePic (char *path) +mpic_t *R2D_SafeCachePic (const char *path) { shader_t *s; if (!qrenderer) @@ -354,7 +358,7 @@ mpic_t *R2D_SafeCachePic (char *path) } -mpic_t *R2D_SafePicFromWad (char *name) +mpic_t *R2D_SafePicFromWad (const char *name) { char newnamewad[32]; char newnamegfx[32]; @@ -465,25 +469,18 @@ void R2D_SubPic(float x, float y, float width, float height, mpic_t *pic, float } /* this is an ugly special case drawing func that's only used for the player color selection menu */ -void R2D_TransPicTranslate (float x, float y, int width, int height, qbyte *pic, qbyte *translation) +void R2D_TransPicTranslate (float x, float y, int width, int height, qbyte *pic, unsigned int *palette) { int v, u; unsigned trans[64*64], *dest; qbyte *src; - int p; dest = trans; for (v=0 ; v<64 ; v++, dest += 64) { src = &pic[ ((v*height)>>6) *width]; for (u=0 ; u<64 ; u++) - { - p = src[(u*width)>>6]; - if (p == 255) - dest[u] = 0x0; - else - dest[u] = d_8to24rgbtable[translation[p]]; - } + dest[u] = palette[src[(u*width)>>6]]; } if (!TEXVALID(translate_texture)) @@ -498,6 +495,8 @@ void R2D_TransPicTranslate (float x, float y, int width, int height, qbyte *pic, "{\n" "map $diffuse\n" "blendfunc blend\n" + "rgbgen vertex\n" + "alphagen vertex\n" "}\n" "}\n"); translate_shader->defaulttextures.base = translate_texture; @@ -631,7 +630,7 @@ void R2D_Conback_Callback(struct cvar_s *var, char *oldvalue) } } -#if defined(_WIN32) && !defined(FTE_SDL) +#if defined(_WIN32) && !defined(FTE_SDL) && !defined(WINRT) #include qboolean R2D_Font_WasAdded(char *buffer, char *fontfilename) { @@ -699,6 +698,10 @@ void R2D_Font_Changed(void) Font_Free(font_default); font_default = NULL; + if (font_tiny) + Font_Free(font_tiny); + font_tiny = NULL; + #if defined(MENU_DAT) || defined(CSQC_DAT) PR_ResetFonts(0); #endif @@ -706,7 +709,7 @@ void R2D_Font_Changed(void) if (qrenderer == QR_NONE) return; -#if defined(_WIN32) && !defined(FTE_SDL) +#if defined(_WIN32) && !defined(FTE_SDL) && !defined(WINRT) if (!strcmp(gl_font.string, "?")) { BOOL (APIENTRY *pChooseFontA)(LPCHOOSEFONTA) = NULL; @@ -1368,5 +1371,20 @@ texid_t R2D_RT_GetTexture(unsigned int id, unsigned int *width, unsigned int *he return rendertargets[id].id; } +texid_t R2D_RT_DetachTexture(unsigned int id) +{ + texid_t r; + id--; + if (id >= numrendertargets) + return r_nulltex; + + r = rendertargets[id].id; + rendertargets[id].id = r_nulltex; + rendertargets[id].fmt = TF_INVALID; + rendertargets[id].width = 0; + rendertargets[id].height = 0; + return r; +} + #endif diff --git a/engine/client/r_surf.c b/engine/client/r_surf.c index aa61ce34..c2e8588c 100644 --- a/engine/client/r_surf.c +++ b/engine/client/r_surf.c @@ -20,7 +20,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // r_surf.c: surface-related refresh code #include "quakedef.h" -#if defined(GLQUAKE) || defined(D3DQUAKE) +#ifndef SERVERONLY #include "glquake.h" #include "shader.h" #include "renderque.h" @@ -1967,12 +1967,26 @@ void Surf_SetupFrame(void) R_AnimateLight(); r_framecount++; + if (r_refdef.recurse) + { + VectorCopy(r_refdef.pvsorigin, pvsorg); + } + else + { + VectorCopy(r_refdef.vieworg, pvsorg); + } + r_viewcontents = 0; if (r_refdef.flags & Q2RDF_NOWORLDMODEL) { } + else if (!cl.worldmodel || cl.worldmodel->needload || cl.worldmodel->fromgame == fg_doom3 ) + { + r_viewleaf = NULL; + r_viewleaf2 = NULL; + } #ifdef Q2BSPS - else if (cl.worldmodel && (cl.worldmodel->fromgame == fg_quake2 || cl.worldmodel->fromgame == fg_quake3)) + else if (cl.worldmodel->fromgame == fg_quake2 || cl.worldmodel->fromgame == fg_quake3) { static mleaf_t fakeleaf; mleaf_t *leaf; @@ -1984,16 +1998,9 @@ void Surf_SetupFrame(void) r_oldviewcluster = r_viewcluster; r_oldviewcluster2 = r_viewcluster2; - if (r_refdef.recurse) - { - leaf = Mod_PointInLeaf (cl.worldmodel, r_refdef.pvsorigin); - r_viewcontents = cl.worldmodel->funcs.PointContents(cl.worldmodel, NULL, r_refdef.pvsorigin); - } - else - { - leaf = Mod_PointInLeaf (cl.worldmodel, r_origin); - r_viewcontents = cl.worldmodel->funcs.PointContents(cl.worldmodel, NULL, r_origin); - } + + leaf = Mod_PointInLeaf (cl.worldmodel, pvsorg); + r_viewcontents = cl.worldmodel->funcs.PointContents(cl.worldmodel, NULL, pvsorg); r_viewcluster = r_viewcluster2 = leaf->cluster; // check above and below so crossing solid water doesn't draw wrong @@ -2001,7 +2008,7 @@ void Surf_SetupFrame(void) { // look down a bit vec3_t temp; - VectorCopy (r_origin, temp); + VectorCopy (pvsorg, temp); temp[2] -= 16; leaf = Mod_PointInLeaf (cl.worldmodel, temp); if ( !(leaf->contents & Q2CONTENTS_SOLID) && @@ -2012,7 +2019,7 @@ void Surf_SetupFrame(void) { // look up a bit vec3_t temp; - VectorCopy (r_origin, temp); + VectorCopy (pvsorg, temp); temp[2] += 16; leaf = Mod_PointInLeaf (cl.worldmodel, temp); if ( !(leaf->contents & Q2CONTENTS_SOLID) && @@ -2021,22 +2028,8 @@ void Surf_SetupFrame(void) } } #endif - else if (cl.worldmodel && cl.worldmodel->fromgame == fg_doom3) - { - r_viewleaf = NULL; - r_viewleaf2 = NULL; - } else { - if (r_refdef.recurse) - { - VectorCopy(r_refdef.pvsorigin, pvsorg); - } - else - { - VectorCopy(r_origin, pvsorg); - } - r_viewleaf = Mod_PointInLeaf (cl.worldmodel, pvsorg); if (!r_viewleaf) @@ -2095,7 +2088,7 @@ void Surf_SetupFrame(void) #ifdef TERRAIN if (!(r_refdef.flags & Q2RDF_NOWORLDMODEL) && cl.worldmodel && cl.worldmodel->terrain) { - r_viewcontents |= Heightmap_PointContents(cl.worldmodel, NULL, r_origin); + r_viewcontents |= Heightmap_PointContents(cl.worldmodel, NULL, pvsorg); } #endif @@ -2106,18 +2099,19 @@ void Surf_SetupFrame(void) VectorCopy(pmove.player_maxs, t2); VectorClear(pmove.player_maxs); VectorClear(pmove.player_mins); - r_viewcontents |= PM_ExtraBoxContents(r_origin); + r_viewcontents |= PM_ExtraBoxContents(pvsorg); VectorCopy(t1, pmove.player_mins); VectorCopy(t2, pmove.player_maxs); } - V_SetContentsColor (r_viewcontents); + if (!r_secondaryview) + V_SetContentsColor (r_viewcontents); if (r_refdef.audio.defaulted) { //first scene is the 'main' scene and audio defaults to that (unless overridden later in the frame) r_refdef.audio.defaulted = false; - VectorCopy(r_origin, r_refdef.audio.origin); + VectorCopy(r_refdef.vieworg, r_refdef.audio.origin); VectorCopy(vpn, r_refdef.audio.forward); VectorCopy(vright, r_refdef.audio.right); VectorCopy(vup, r_refdef.audio.up); @@ -2283,8 +2277,6 @@ void Surf_DrawWorld (void) return; } - Surf_SetupFrame(); - currentmodel = cl.worldmodel; currententity = &r_worldentity; @@ -2460,6 +2452,7 @@ void Surf_LightmapMode(void) switch(qrenderer) { + default: case QR_SOFTWARE: lightmap_bytes = 4; lightmap_bgra = true; @@ -2472,8 +2465,8 @@ void Surf_LightmapMode(void) lightmap_bgra = true; break; #endif - case QR_OPENGL: #ifdef GLQUAKE + case QR_OPENGL: /*favour bgra if the gpu supports it, otherwise use rgb only if it'll be used*/ lightmap_bgra = false; if (gl_config.gles) @@ -2496,8 +2489,6 @@ void Surf_LightmapMode(void) lightmap_bytes = 1; break; #endif - default: - break; } } @@ -2703,6 +2694,7 @@ void Surf_BuildModelLightmaps (model_t *m) int j; unsigned char *src; unsigned char *dst; + if (*m->name != '*') for (i = 0; i < m->lightmaps.count; i++) { if (lightmap[newfirst+i]->external) diff --git a/engine/client/render.h b/engine/client/render.h index 7cc16319..fd066f66 100644 --- a/engine/client/render.h +++ b/engine/client/render.h @@ -75,6 +75,8 @@ typedef enum { RT_MAX_REF_ENTITY_TYPE } refEntityType_t; +typedef unsigned int skinid_t; //skin 0 is 'unused' + struct dlight_s; typedef struct entity_s { @@ -97,6 +99,7 @@ typedef struct entity_s struct model_s *model; // NULL = no model int skinnum; // for Alias models + skinid_t customskin; // quake3 style skins int playerindex; //for qw skins int topcolour; //colourmapping @@ -138,6 +141,22 @@ typedef struct entity_s #endif } entity_t; +#define MAX_GEOMSETS 32 +typedef struct +{ + char skinname[MAX_QPATH]; + int nummappings; + int maxmappings; + qbyte geomset[MAX_GEOMSETS]; //allows selecting a single set of geometry from alternatives. this might be a can of worms. + struct + { + char surface[MAX_QPATH]; + shader_t *shader; + texnums_t texnums; + int needsfree; //which textures need to be freed. + } mappings[1]; +} skinfile_t; + // plane_t structure typedef struct mplane_s { @@ -149,6 +168,27 @@ typedef struct mplane_s } mplane_t; #define MAXFRUSTUMPLANES 7 //4 side, 1 near, 1 far (fog), 1 water plane. +typedef struct +{ + //note: uniforms expect specific padding/ordering. be really careful with reordering this + vec3_t colour; //w_fog[0].xyz + float alpha; //w_fog[0].w scales clamped fog value + float density; //w_fog[1].x egads, everyone has a different opinion. + float depthbias; //w_fog[1].y distance until the fog actually starts + float glslpad1; //w_fog[1].z + float glslpad2; //w_fog[1].w + +// float alpha; +// float start; +// float end; +// float height; +// float fadedepth; + + float time; //timestamp for when its current. +} fogstate_t; +void CL_BlendFog(fogstate_t *result, fogstate_t *oldf, float time, fogstate_t *newf); +void CL_ResetFog(void); + #define R_MAX_RECURSE 6 #define R_POSTPROC_PASSES 6 #define RDFD_FOV 1 @@ -180,7 +220,7 @@ typedef struct mplane_t frustum[MAXFRUSTUMPLANES]; int frustum_numplanes; - vec4_t gfog_rgbd; + fogstate_t globalfog; pxrect_t pxrect; /*vrect, but in pixels rather than virtual coords*/ qboolean externalview; /*draw external models and not viewmodels*/ @@ -227,6 +267,7 @@ void R_DrawSkyChain (struct batch_s *batch); /*called from the backend, and call void R_InitSky (struct texnums_s *ret, struct texture_s *mt, qbyte *src); /*generate q1 sky texnums*/ //r_surf.c +void Surf_SetupFrame(void); //determine pvs+viewcontents void Surf_DrawWorld(void); void Surf_GenBrushBatches(struct batch_s **batches, entity_t *ent); void Surf_StainSurf(struct msurface_s *surf, float *parms); @@ -325,37 +366,37 @@ enum imageflags /*it seems a little excessive to have to include glquake (and windows headers), just to load some textures/shaders for the backend*/ #ifdef GLQUAKE -texid_tf GL_AllocNewTexture(char *name, int w, int h, unsigned int flags); -void GL_UploadFmt(texid_t tex, char *name, enum uploadfmt fmt, void *data, void *palette, int width, int height, unsigned int flags); -texid_tf GL_LoadTextureFmt (char *identifier, int width, int height, enum uploadfmt fmt, void *data, unsigned int flags); +texid_tf GL_AllocNewTexture(const char *name, int w, int h, unsigned int flags); +void GL_UploadFmt(texid_t tex, const char *name, enum uploadfmt fmt, void *data, void *palette, int width, int height, unsigned int flags); +texid_tf GL_LoadTextureFmt (const char *identifier, int width, int height, enum uploadfmt fmt, void *data, unsigned int flags); void GL_DestroyTexture(texid_t tex); #endif #ifdef D3DQUAKE -texid_t D3D9_LoadTexture (char *identifier, int width, int height, enum uploadfmt fmt, void *data, unsigned int flags); -texid_t D3D9_LoadTexture8Pal24 (char *identifier, int width, int height, qbyte *data, qbyte *palette24, unsigned int flags); -texid_t D3D9_LoadTexture8Pal32 (char *identifier, int width, int height, qbyte *data, qbyte *palette32, unsigned int flags); -texid_t D3D9_LoadCompressed (char *name); -texid_t D3D9_FindTexture (char *identifier, unsigned int flags); -texid_t D3D9_AllocNewTexture(char *ident, int width, int height, unsigned int flags); -void D3D9_Upload (texid_t tex, char *name, enum uploadfmt fmt, void *data, void *palette, int width, int height, unsigned int flags); +texid_t D3D9_LoadTexture (const char *identifier, int width, int height, enum uploadfmt fmt, void *data, unsigned int flags); +texid_t D3D9_LoadTexture8Pal24 (const char *identifier, int width, int height, qbyte *data, qbyte *palette24, unsigned int flags); +texid_t D3D9_LoadTexture8Pal32 (const char *identifier, int width, int height, qbyte *data, qbyte *palette32, unsigned int flags); +texid_t D3D9_LoadCompressed (const char *name); +texid_t D3D9_FindTexture (const char *identifier, unsigned int flags); +texid_t D3D9_AllocNewTexture(const char *ident, int width, int height, unsigned int flags); +void D3D9_Upload (texid_t tex, const char *name, enum uploadfmt fmt, void *data, void *palette, int width, int height, unsigned int flags); void D3D9_DestroyTexture (texid_t tex); void D3D9_Image_Shutdown(void); -texid_t D3D11_LoadTexture (char *identifier, int width, int height, enum uploadfmt fmt, void *data, unsigned int flags); -texid_t D3D11_LoadTexture8Pal24 (char *identifier, int width, int height, qbyte *data, qbyte *palette24, unsigned int flags); -texid_t D3D11_LoadTexture8Pal32 (char *identifier, int width, int height, qbyte *data, qbyte *palette32, unsigned int flags); -texid_t D3D11_LoadCompressed (char *name); -texid_t D3D11_FindTexture (char *identifier, unsigned int flags); -texid_t D3D11_AllocNewTexture(char *ident, int width, int height, unsigned int flags); -void D3D11_Upload (texid_t tex, char *name, enum uploadfmt fmt, void *data, void *palette, int width, int height, unsigned int flags); +texid_t D3D11_LoadTexture (const char *identifier, int width, int height, enum uploadfmt fmt, void *data, unsigned int flags); +texid_t D3D11_LoadTexture8Pal24 (const char *identifier, int width, int height, qbyte *data, qbyte *palette24, unsigned int flags); +texid_t D3D11_LoadTexture8Pal32 (const char *identifier, int width, int height, qbyte *data, qbyte *palette32, unsigned int flags); +texid_t D3D11_LoadCompressed (const char *name); +texid_t D3D11_FindTexture (const char *identifier, unsigned int flags); +texid_t D3D11_AllocNewTexture(const char *ident, int width, int height, unsigned int flags); +void D3D11_Upload (texid_t tex, const char *name, enum uploadfmt fmt, void *data, void *palette, int width, int height, unsigned int flags); void D3D11_DestroyTexture (texid_t tex); void D3D11_Image_Shutdown(void); #endif extern int image_width, image_height; -texid_tf R_LoadReplacementTexture(char *name, char *subpath, unsigned int flags); -texid_tf R_LoadHiResTexture(char *name, char *subpath, unsigned int flags); -texid_tf R_LoadBumpmapTexture(char *name, char *subpath); +texid_tf R_LoadReplacementTexture(const char *name, const char *subpath, unsigned int flags); +texid_tf R_LoadHiResTexture(const char *name, const char *subpath, unsigned int flags); +texid_tf R_LoadBumpmapTexture(const char *name, const char *subpath); qbyte *Read32BitImageFile(qbyte *buf, int len, int *width, int *height, qboolean *hasalpha, char *fname); @@ -366,19 +407,23 @@ extern texid_t balltexture; extern texid_t beamtexture; extern texid_t ptritexture; +skinid_t Mod_RegisterSkinFile(const char *skinname); +skinid_t Mod_ReadSkinFile(const char *skinname, const char *skintext); +void Mod_WipeSkin(skinid_t id); +skinfile_t *Mod_LookupSkin(skinid_t id); + void Mod_Init (qboolean initial); void Mod_Shutdown (qboolean final); -int Mod_TagNumForName(struct model_s *model, char *name); -int Mod_SkinNumForName(struct model_s *model, char *name); -int Mod_FrameNumForName(struct model_s *model, char *name); +int Mod_TagNumForName(struct model_s *model, const char *name); +int Mod_SkinNumForName(struct model_s *model, const char *name); +int Mod_FrameNumForName(struct model_s *model, const char *name); float Mod_GetFrameDuration(struct model_s *model, int frameno); void Mod_ResortShaders(void); void Mod_ClearAll (void); -struct model_s *Mod_ForName (char *name, qboolean crash); -struct model_s *Mod_FindName (char *name); +struct model_s *Mod_FindName (const char *name); void *Mod_Extradata (struct model_s *mod); // handles caching -void Mod_TouchModel (char *name); +void Mod_TouchModel (const char *name); void Mod_RebuildLightmaps (void); struct mleaf_s *Mod_PointInLeaf (struct model_s *model, float *p); @@ -388,8 +433,8 @@ void Mod_NowLoadExternal(void); void GLR_LoadSkys (void); void R_BloomRegister(void); -int Mod_RegisterModelFormatText(void *module, const char *formatname, char *magictext, qboolean (QDECL *load) (struct model_s *mod, void *buffer)); -int Mod_RegisterModelFormatMagic(void *module, const char *formatname, unsigned int magic, qboolean (QDECL *load) (struct model_s *mod, void *buffer)); +int Mod_RegisterModelFormatText(void *module, const char *formatname, char *magictext, qboolean (QDECL *load) (struct model_s *mod, void *buffer, size_t fsize)); +int Mod_RegisterModelFormatMagic(void *module, const char *formatname, unsigned int magic, qboolean (QDECL *load) (struct model_s *mod, void *buffer, size_t fsize)); void Mod_UnRegisterModelFormat(int idx); void Mod_UnRegisterAllModelFormats(void *module); @@ -451,7 +496,7 @@ void AddOcranaLEDsIndexed (qbyte *image, int h, int w); void Renderer_Init(void); void Renderer_Start(void); qboolean Renderer_Started(void); -void R_ShutdownRenderer(void); +void R_ShutdownRenderer(qboolean videotoo); void R_RestartRenderer_f (void);//this goes here so we can save some stack when first initing the sw renderer. //used to live in glquake.h diff --git a/engine/client/renderer.c b/engine/client/renderer.c index 09c95ff6..5b0089e4 100644 --- a/engine/client/renderer.c +++ b/engine/client/renderer.c @@ -121,7 +121,7 @@ cvar_t r_loadlits = CVARF ("r_loadlit", "1", CVAR_ARCHIVE); cvar_t r_menutint = SCVARF ("r_menutint", "0.68 0.4 0.13", CVAR_RENDERERCALLBACK); cvar_t r_netgraph = SCVAR ("r_netgraph", "0"); -cvar_t r_lerpmuzzlehack = CVARF ("r_lerpmuzzlehack", "1", CVAR_ARCHIVE); +extern cvar_t r_lerpmuzzlehack; cvar_t r_nolerp = CVARF ("r_nolerp", "0", CVAR_ARCHIVE); cvar_t r_noframegrouplerp = CVARF ("r_noframegrouplerp", "0", CVAR_ARCHIVE); cvar_t r_nolightdir = CVARF ("r_nolightdir", "0", CVAR_ARCHIVE); @@ -159,7 +159,7 @@ cvar_t scr_chatmodecvar = CVAR ("scr_chatmode", "0"); cvar_t scr_conalpha = CVARC ("scr_conalpha", "0.7", Cvar_Limiter_ZeroToOne_Callback); cvar_t scr_consize = CVAR ("scr_consize", "0.5"); -cvar_t scr_conspeed = CVAR ("scr_conspeed", "300"); +cvar_t scr_conspeed = CVAR ("scr_conspeed", "2000"); // 10 - 170 cvar_t scr_fov = CVARFC("fov", "90", CVAR_ARCHIVE, @@ -209,6 +209,8 @@ cvar_t vid_multisample = CVARF ("vid_multisample", "0", CVAR_ARCHIVE | CVAR_RENDERERLATCH); cvar_t vid_refreshrate = CVARF ("vid_displayfrequency", "0", CVAR_ARCHIVE | CVAR_RENDERERLATCH); +cvar_t vid_srgb = CVARF ("vid_srgb", "0", + CVAR_ARCHIVE); cvar_t vid_wndalpha = CVAR ("vid_wndalpha", "1"); //more readable defaults to match conwidth/conheight. cvar_t vid_width = CVARFD ("vid_width", "0", @@ -255,7 +257,7 @@ cvar_t vid_gl_context_debug = CVARD ("vid_gl_context_debug", "0", "Requests cvar_t vid_gl_context_es = CVARD ("vid_gl_context_es", "0", "Requests an OpenGLES context. Be sure to set vid_gl_context_version to 2 or so."); //requires version set correctly, no debug, no compat #endif -#if defined(GLQUAKE) || defined(D3DQUAKE) +#if 1 cvar_t gl_ati_truform = CVAR ("gl_ati_truform", "0"); cvar_t gl_ati_truform_type = CVAR ("gl_ati_truform_type", "1"); cvar_t gl_ati_truform_tesselation = CVAR ("gl_ati_truform_tesselation", "3"); @@ -263,6 +265,7 @@ cvar_t gl_blend2d = CVAR ("gl_blend2d", "1"); cvar_t gl_blendsprites = CVARD ("gl_blendsprites", "0", "Blend sprites instead of alpha testing them"); cvar_t r_deluxemapping = CVARAFD ("r_deluxemapping", "0", "r_glsl_deluxemapping", CVAR_ARCHIVE | CVAR_RENDERERLATCH, "Enables bumpmapping based upon precomputed light directions"); +cvar_t r_shaderblobs = CVARD ("r_shaderblobs", "0", "If enabled, can massively accelerate vid restarts / loading (especially with the d3d renderer). Can cause issues when upgrading engine versions, so this is disabled by default."); cvar_t gl_compress = CVARFD ("gl_compress", "0", CVAR_ARCHIVE, "Enable automatic texture compression even for textures which are not pre-compressed."); cvar_t gl_conback = CVARFDC ("gl_conback", "", CVAR_RENDERERCALLBACK, "Specifies which conback shader/image to use. The Quake fallback is gfx/conback.lmp", R2D_Conback_Callback); @@ -361,7 +364,7 @@ cvar_t vid_hardwaregamma = CVARFD ("vid_hardwaregamma", "1", cvar_t vid_desktopgamma = CVARFD ("vid_desktopgamma", "0", CVAR_ARCHIVE | CVAR_RENDERERLATCH, "Apply gamma ramps upon the desktop rather than the window."); -cvar_t r_fog_exp2 = CVARD ("r_fog_exp2", "1", "Expresses how fog fades with distance. 0 (matching DarkPlaces) is typically more realistic, while 1 (matching FitzQuake and others) is more common."); +cvar_t r_fog_exp2 = CVARD ("r_fog_exp2", "1", "Expresses how fog fades with distance. 0 (matching DarkPlaces's default) is typically more realistic, while 1 (matching FitzQuake and others) is more common."); extern cvar_t gl_dither; cvar_t gl_screenangle = SCVAR("gl_screenangle", "0"); @@ -438,6 +441,7 @@ void GLRenderer_Init(void) Cvar_Register (&gl_picmip, GLRENDEREROPTIONS); Cvar_Register (&gl_picmip2d, GLRENDEREROPTIONS); + Cvar_Register (&r_shaderblobs, GLRENDEREROPTIONS); Cvar_Register (&gl_mipcap, GLRENDEREROPTIONS); Cvar_Register (&gl_texturemode, GLRENDEREROPTIONS); Cvar_Register (&gl_texturemode2d, GLRENDEREROPTIONS); @@ -595,6 +599,7 @@ void Renderer_Init(void) Cvar_Register (&vid_height, VIDCOMMANDGROUP); Cvar_Register (&vid_refreshrate, VIDCOMMANDGROUP); Cvar_Register (&vid_multisample, GLRENDEREROPTIONS); + Cvar_Register (&vid_srgb, GLRENDEREROPTIONS); Cvar_Register (&vid_desktopsettings, VIDCOMMANDGROUP); @@ -845,9 +850,6 @@ rendererinfo_t swrendererinfo; rendererinfo_t *rendererinfo[] = { -#ifndef NPQTV - &dedicatedrendererinfo, -#endif #ifdef GLQUAKE #ifdef FTE_RPI &rpirendererinfo, @@ -864,6 +866,9 @@ rendererinfo_t *rendererinfo[] = #ifdef SWQUAKE &swrendererinfo, #endif +#ifndef NPQTV + &dedicatedrendererinfo, +#endif }; @@ -916,9 +921,8 @@ void D3DSucks(void) Sys_Error("Failed to reload content after mode switch\n"); } -void R_ShutdownRenderer(void) +void R_ShutdownRenderer(qboolean videotoo) { - CL_AllowIndependantSendCmd(false); //FIXME: figure out exactly which parts are going to affect the model loading. P_Shutdown(); @@ -935,7 +939,7 @@ void R_ShutdownRenderer(void) if (Draw_Shutdown) Draw_Shutdown(); - if (VID_DeInit) + if (VID_DeInit && videotoo) { TRACE(("dbg: R_ApplyRenderer: VID_DeInit\n")); VID_DeInit(); @@ -983,7 +987,7 @@ qboolean R_ApplyRenderer (rendererstate_t *newr) if (!newr->renderer) return false; - R_ShutdownRenderer(); + R_ShutdownRenderer(true); if (qrenderer == QR_NONE) { @@ -1094,6 +1098,8 @@ TRACE(("dbg: R_ApplyRenderer: screen inited\n")); Sbar_Flush(); IN_ReInit(); + + Cvar_ForceCallback(&v_gamma); } else { @@ -1137,7 +1143,7 @@ TRACE(("dbg: R_ApplyRenderer: initing mods\n")); #endif TRACE(("dbg: R_ApplyRenderer: reloading server map\n")); - sv.world.worldmodel = Mod_ForName (sv.modelname, false); + sv.world.worldmodel = Mod_ForName (sv.modelname, MLV_WARN); TRACE(("dbg: R_ApplyRenderer: loaded\n")); if (sv.world.worldmodel->needload) { @@ -1202,6 +1208,14 @@ TRACE(("dbg: R_ApplyRenderer: clearing world\n")); } } } +#endif +#ifdef Q3SERVER + else if (svs.gametype == GT_QUAKE3) + { + //traditionally a q3 server can just keep hold of its world cmodel and nothing is harmed. + //this means we just need to reload the worldmodel and all is fine... + //there are some edge cases however, like lingering pointers refering to entities. + } #endif else SV_UnspawnServer(); @@ -1215,6 +1229,19 @@ TRACE(("dbg: R_ApplyRenderer: clearing world\n")); CL_InitDlights(); TRACE(("dbg: R_ApplyRenderer: starting on client state\n")); + + if (newr) + memcpy(¤trendererstate, newr, sizeof(currentrendererstate)); + +#ifdef Q3SERVER + if (svs.gametype == GT_QUAKE3) + { + CG_Stop(); + CG_Start(); + R_NewMap(); + } + else +#endif if (cl.worldmodel) { cl.worldmodel = NULL; @@ -1227,7 +1254,6 @@ TRACE(("dbg: R_ApplyRenderer: reloading ALL models\n")); if (!cl.model_name[i][0]) break; - cl.model_precache[i] = NULL; TRACE(("dbg: R_ApplyRenderer: reloading model %s\n", cl.model_name[i])); #ifdef Q2CLIENT //skip vweps @@ -1235,9 +1261,9 @@ TRACE(("dbg: R_ApplyRenderer: reloading ALL models\n")); cl.model_precache[i] = NULL; else #endif - cl.model_precache[i] = Mod_ForName (cl.model_name[i], false); + cl.model_precache[i] = Mod_ForName (cl.model_name[i], MLV_SILENT); - if (!cl.model_precache[i] && i == 1) + if ((!cl.model_precache[i] || cl.model_precache[i]->type == mod_dummy) && i == 1) { Con_Printf ("\nThe required model file '%s' could not be found.\n\n" , cl.model_name[i]); @@ -1254,7 +1280,7 @@ TRACE(("dbg: R_ApplyRenderer: reloading ALL models\n")); for (i=0; i < MAX_VWEP_MODELS; i++) { if (*cl.model_name_vwep[i]) - cl.model_precache_vwep[i] = Mod_ForName (cl.model_name_vwep[i], false); + cl.model_precache_vwep[i] = Mod_ForName (cl.model_name_vwep[i], MLV_SILENT); else cl.model_precache_vwep[i] = NULL; } @@ -1267,7 +1293,7 @@ TRACE(("dbg: R_ApplyRenderer: reloading ALL models\n")); cl.model_csqcprecache[i] = NULL; TRACE(("dbg: R_ApplyRenderer: reloading csqc model %s\n", cl.model_csqcname[i])); - cl.model_csqcprecache[i] = Mod_ForName (cl.model_csqcname[i], false); + cl.model_csqcprecache[i] = Mod_ForName (cl.model_csqcname[i], MLV_SILENT); if (!cl.model_csqcprecache[i]) { @@ -1340,13 +1366,12 @@ TRACE(("dbg: R_ApplyRenderer: efrags\n")); TRACE(("dbg: R_ApplyRenderer: done\n")); - if (newr) - memcpy(¤trendererstate, newr, sizeof(currentrendererstate)); return true; } void R_ReloadRenderer_f (void) { + R_ShutdownRenderer(false); //reloads textures without destroying video context. R_ApplyRenderer_Load(NULL); } @@ -1371,6 +1396,7 @@ qboolean R_BuildRenderstate(rendererstate_t *newr, char *rendererstring) newr->fullscreen = vid_fullscreen.value; newr->rate = vid_refreshrate.value; newr->stereo = (r_stereo_method.ival == 1); + newr->srgb = vid_srgb.ival; if (!*vid_vsync.string || vid_vsync.value < 0) newr->wait = -1; @@ -1483,7 +1509,7 @@ TRACE(("dbg: R_RestartRenderer_f\n")); return; } - M_Shutdown(); + M_Shutdown(false); Media_CaptureDemoEnd(); TRACE(("dbg: R_RestartRenderer_f renderer %i\n", newr.renderer)); @@ -1499,7 +1525,9 @@ TRACE(("dbg: R_RestartRenderer_f\n")); } else { + int i; qboolean failed = true; + rendererinfo_t *skip = newr.renderer; if (newr.rate != 0) { @@ -1516,23 +1544,31 @@ TRACE(("dbg: R_RestartRenderer_f\n")); failed = !R_ApplyRenderer(&newr); } - if (failed) + for (i = 0; failed && i < sizeof(rendererinfo)/sizeof(rendererinfo[0]); i++) { - newr.renderer = &dedicatedrendererinfo; - if (R_ApplyRenderer(&newr)) + newr.renderer = rendererinfo[i]; + if (newr.renderer && newr.renderer != skip) { - TRACE(("dbg: R_RestartRenderer_f going to dedicated\n")); - Con_Printf(CON_ERROR "Video mode switch failed. Old mode wasn't supported either. Console forced.\n\nChange the following vars to something useable, and then use the setrenderer command.\n"); - Con_Printf("%s: %s\n", vid_width.name, vid_width.string); - Con_Printf("%s: %s\n", vid_height.name, vid_height.string); - Con_Printf("%s: %s\n", vid_bpp.name, vid_bpp.string); - Con_Printf("%s: %s\n", vid_refreshrate.name, vid_refreshrate.string); - Con_Printf("%s: %s\n", vid_renderer.name, vid_renderer.string); - Con_Printf("%s: %s\n", gl_driver.name, gl_driver.string); + Con_Printf(CON_NOTICE "Trying %s\n", newr.renderer->description); + failed = !R_ApplyRenderer(&newr); } - else - Sys_Error("Couldn't fall back to previous renderer\n"); } + + //if we ended up resorting to our last choice (dedicated) then print some informative message about it + //fixme: on unixy systems, we should make sure we're actually printing to something (ie: that we're not running via some x11 shortcut with our stdout redirected to /dev/nul + if (!failed && newr.renderer == &dedicatedrendererinfo) + { + Con_Printf(CON_ERROR "Video mode switch failed. Console forced.\n\nPlease change the following vars to something useable, and then use the setrenderer command.\n"); + Con_Printf("%s: %s\n", vid_width.name, vid_width.string); + Con_Printf("%s: %s\n", vid_height.name, vid_height.string); + Con_Printf("%s: %s\n", vid_bpp.name, vid_bpp.string); + Con_Printf("%s: %s\n", vid_refreshrate.name, vid_refreshrate.string); + Con_Printf("%s: %s\n", vid_renderer.name, vid_renderer.string); + Con_Printf("%s: %s\n", gl_driver.name, gl_driver.string); + } + + if (failed) + Sys_Error("Unable to initialise any video mode\n"); } } @@ -2245,7 +2281,7 @@ void R_SetFrustum (float projmat[16], float viewmat[16]) r_refdef.frustum_numplanes++; //do far plane - //fog will not logically not actually reach 0, though precision issues will force it. we cut off at an exponant of -500 + //fog will logically not actually reach 0, though precision issues will force it. we cut off at an exponant of -500 if (r_refdef.gfog_rgbd[3] #ifdef TERRAIN && cl.worldmodel && cl.worldmodel->type == mod_heightmap @@ -2261,9 +2297,9 @@ void R_SetFrustum (float projmat[16], float viewmat[16]) /*Documentation: the GLSL/GL will do this maths: float dist = 1024; if (r_fog_exp2.ival) - fog = pow(2, -r_refdef.gfog_rgbd[3] * r_refdef.gfog_rgbd[3] * dist * dist * 1.442695); + fog = pow(2, -r_refdef.globalfog.density * r_refdef.globalfog.density * dist * dist * 1.442695); else - fog = pow(2, -r_refdef.gfog_rgbd[3] * dist * 1.442695); + fog = pow(2, -r_refdef.globalfog.density * dist * 1.442695); */ //the fog factor cut-off where its pointless to allow it to get closer to 0 (0 is technically infinite) @@ -2272,9 +2308,9 @@ void R_SetFrustum (float projmat[16], float viewmat[16]) //figure out the eyespace distance required to reach that fog value culldist = log(fog); if (r_fog_exp2.ival) - culldist = sqrt(culldist / (-r_refdef.gfog_rgbd[3] * r_refdef.gfog_rgbd[3])); + culldist = sqrt(culldist / (-r_refdef.globalfog.density * r_refdef.globalfog.density)); else - culldist = culldist / (-r_refdef.gfog_rgbd[3]); + culldist = culldist / (-r_refdef.globalfog.density); //anything drawn beyond this point is fully obscured by fog r_refdef.frustum[r_refdef.frustum_numplanes].normal[0] = mvp[3] - mvp[2]; diff --git a/engine/client/screen.h b/engine/client/screen.h index ea13ab34..aa43f2b8 100644 --- a/engine/client/screen.h +++ b/engine/client/screen.h @@ -73,7 +73,9 @@ void SCR_ShowPic_Create(void); void SCR_ShowPic_Hide(void); void SCR_ShowPic_Move(void); void SCR_ShowPic_Update(void); -void SCR_ShowPic_Clear(void); +void SCR_ShowPic_Clear(qboolean all); +char *SCR_ShowPics_ClickCommand(int cx, int cy); +void SCR_ShowPic_Script_f(void); //a header is better than none... void Draw_TextBox (int x, int y, int width, int lines); @@ -95,7 +97,7 @@ void SCR_SetLoadingFile(char *str); /*fonts*/ void Font_Init(void); void Font_Shutdown(void); -struct font_s *Font_LoadFont(int height, char *fontfilename); +struct font_s *Font_LoadFont(int height, const char *fontfilename); void Font_Free(struct font_s *f); void Font_BeginString(struct font_s *font, float vx, float vy, int *px, int *py); void Font_BeginScaledString(struct font_s *font, float vx, float vy, float szx, float szy, float *px, float *py); /*avoid using*/ diff --git a/engine/client/skin.c b/engine/client/skin.c index 4a202315..4fa1c41e 100644 --- a/engine/client/skin.c +++ b/engine/client/skin.c @@ -158,7 +158,7 @@ void Skin_Find (player_info_t *sc) *s = '\0'; #ifdef Q2CLIENT if (cls.protocol == CP_QUAKE2) - model = Mod_ForName(va("players/%s/tris.md2", name), false); + model = Mod_ForName(va("players/%s/tris.md2", name), MLV_SILENT); else #endif model = NULL;//Mod_ForName(va("models/players/%s.mdl", name), false); diff --git a/engine/client/snd_al.c b/engine/client/snd_al.c index 702fec73..2df79bff 100644 --- a/engine/client/snd_al.c +++ b/engine/client/snd_al.c @@ -298,7 +298,7 @@ static void PrintALError(char *string) Con_Printf("OpenAL - %s: %x: %s\n",string,err,text); } -void OpenAL_LoadCache(unsigned int *bufptr, sfxcache_t *sc) +void OpenAL_LoadCache(unsigned int *bufptr, sfxcache_t *sc, float volume) { unsigned int fmt; unsigned int size; @@ -334,20 +334,50 @@ void OpenAL_LoadCache(unsigned int *bufptr, sfxcache_t *sc) PrintALError("pre Buffer Data"); palGenBuffers(1, bufptr); /*openal is inconsistant and supports only 8bit unsigned or 16bit signed*/ - if (sc->width == 1) + if (volume != 1) { - unsigned char *tmp = malloc(size); - char *src = sc->data; - int i; - for (i = 0; i < size; i++) + if (sc->width == 1) { - tmp[i] = src[i]+128; + unsigned char *tmp = malloc(size); + char *src = sc->data; + int i; + for (i = 0; i < size; i++) + { + tmp[i] = src[i]*volume+128; //signed->unsigned + } + palBufferData(*bufptr, fmt, tmp, size, sc->speed); + free(tmp); + } + else + { + short *tmp = malloc(size); + short *src = (short*)sc->data; + int i; + for (i = 0; i < (size>>1); i++) + { + tmp[i] = bound(-32767, src[i]*volume, 32767); //signed. + } + palBufferData(*bufptr, fmt, tmp, size, sc->speed); + free(tmp); } - palBufferData(*bufptr, fmt, tmp, size, sc->speed); - free(tmp); } else - palBufferData(*bufptr, fmt, sc->data, size, sc->speed); + { + if (sc->width == 1) + { + unsigned char *tmp = malloc(size); + char *src = sc->data; + int i; + for (i = 0; i < size; i++) + { + tmp[i] = src[i]+128; + } + palBufferData(*bufptr, fmt, tmp, size, sc->speed); + free(tmp); + } + else + palBufferData(*bufptr, fmt, sc->data, size, sc->speed); + } //FIXME: we need to handle oal-oom error codes @@ -385,7 +415,7 @@ static void OpenAL_ListenerUpdate(soundcardinfo_t *sc, vec3_t origin, vec3_t for if (!s_al_static_listener.value) { - palListenerf(AL_GAIN, volume.value*voicevolumemod); + palListenerf(AL_GAIN, 1); palListenerfv(AL_POSITION, oali->ListenPos); palListenerfv(AL_VELOCITY, oali->ListenVel); palListenerfv(AL_ORIENTATION, oali->ListenOri); @@ -398,7 +428,7 @@ static void OpenAL_ChannelUpdate(soundcardinfo_t *sc, channel_t *chan, unsigned oalinfo_t *oali = sc->handle; ALuint src; sfx_t *sfx = chan->sfx; - float pitch; + float pitch, cvolume; int chnum = chan - sc->channel; ALuint buf; @@ -453,6 +483,10 @@ static void OpenAL_ChannelUpdate(soundcardinfo_t *sc, channel_t *chan, unsigned return; } + cvolume = chan->master_vol/255.0f; + if (!(chan->flags & CF_ABSVOLUME)) + cvolume *= volume.value*voicevolumemod; + if (schanged || sfx->decoder.decodedata) { if (!sfx->openal_buffer) @@ -473,7 +507,7 @@ static void OpenAL_ChannelUpdate(soundcardinfo_t *sc, channel_t *chan, unsigned //build a buffer with it and queue it up. //buffer will be purged later on when its unqueued - OpenAL_LoadCache(&buf, &sbuf); + OpenAL_LoadCache(&buf, &sbuf, max(1,cvolume)); palSourceQueueBuffers(src, 1, &buf); //yay @@ -485,14 +519,15 @@ static void OpenAL_ChannelUpdate(soundcardinfo_t *sc, channel_t *chan, unsigned } else { - OpenAL_LoadCache(&sfx->openal_buffer, sfx->decoder.buf); + OpenAL_LoadCache(&sfx->openal_buffer, sfx->decoder.buf, 1); palSourcei(src, AL_BUFFER, sfx->openal_buffer); } } else palSourcei(src, AL_BUFFER, sfx->openal_buffer); } - palSourcef(src, AL_GAIN, chan->master_vol/255.0f); + + palSourcef(src, AL_GAIN, min(cvolume, 1)); //openal only supports a max volume of 1. anything above is an error and will be clamped. if (chan->entnum == -1 || chan->entnum == cl.playerview[0].viewentity) palSourcefv(src, AL_POSITION, vec3_origin); else diff --git a/engine/client/snd_directx.c b/engine/client/snd_directx.c index d1359417..bc8955e1 100644 --- a/engine/client/snd_directx.c +++ b/engine/client/snd_directx.c @@ -951,7 +951,8 @@ static qboolean QDECL DSOUND_InitCard (soundcardinfo_t *sc, const char *device) } //wait for the thread to finish (along with all its error con printfs etc - Sys_ConditionWait(cond); + if (!Sys_ConditionWait(cond)) + Con_SafePrintf ("Looks like the sound thread isn't starting up\n"); Sys_UnlockConditional(cond); Sys_DestroyConditional(cond); diff --git a/engine/client/snd_dma.c b/engine/client/snd_dma.c index b5d671a2..6f0792a2 100644 --- a/engine/client/snd_dma.c +++ b/engine/client/snd_dma.c @@ -30,7 +30,7 @@ static void S_StopAllSounds_f (void); static void S_UpdateCard(soundcardinfo_t *sc); static void S_ClearBuffer (soundcardinfo_t *sc); -static sfx_t *S_FindName (char *name); +static sfx_t *S_FindName (const char *name); // ======================================================================= // Internal sound data & structures @@ -131,7 +131,7 @@ cvar_t snd_voip_showmeter = CVARAFD("cl_voip_showmeter", "1", NULL, CVAR_ARCHIV cvar_t snd_voip_play = CVARAFDC("cl_voip_play", "1", NULL, CVAR_ARCHIVE, "Enables voip playback. Value is a volume scaler.", S_Voip_Play_Callback); cvar_t snd_voip_ducking = CVARAFD("cl_voip_ducking", "0.5", NULL, CVAR_ARCHIVE, "Scales game audio by this much when someone is talking to you. Does not affect your speaker volume when you speak (minimum of cl_voip_capturingvol and cl_voip_ducking is used)."); cvar_t snd_voip_micamp = CVARAFDC("cl_voip_micamp", "2", NULL, CVAR_ARCHIVE, "Amplifies your microphone when using voip.", 0); -cvar_t snd_voip_codec = CVARAFDC("cl_voip_codec", "0", NULL, CVAR_ARCHIVE, "0: speex. 1: raw. 2: opus.", 0); +cvar_t snd_voip_codec = CVARAFDC("cl_voip_codec", "0", NULL, CVAR_ARCHIVE, "0: speex(@11khz). 1: raw. 2: opus. 3: speex(@8khz). 4: speex(@16). 5:speex(@32).", 0); cvar_t snd_voip_noisefilter = CVARAFDC("cl_voip_noisefilter", "1", NULL, CVAR_ARCHIVE, "Enable the use of the noise cancelation filter.", 0); cvar_t snd_voip_autogain = CVARAFDC("cl_voip_autogain", "0", NULL, CVAR_ARCHIVE, "Attempts to normalize your voice levels to a standard level. Useful for lazy people, but interferes with voice activation levels.", 0); #endif @@ -2007,7 +2007,7 @@ S_FindName also touches it ================== */ -static sfx_t *S_FindName (char *name) +static sfx_t *S_FindName (const char *name) { int i; sfx_t *sfx; @@ -2112,7 +2112,7 @@ S_PrecacheSound ================== */ -sfx_t *S_PrecacheSound (char *name) +sfx_t *S_PrecacheSound (const char *name) { sfx_t *sfx; @@ -2834,7 +2834,7 @@ void S_ExtraUpdate (void) if (!sound_started) return; -#ifdef _WIN32 +#if defined(_WIN32) && !defined(WINRT) INS_Accumulate (); #endif @@ -3015,7 +3015,7 @@ void S_SoundList_f(void) } -void S_LocalSound (char *sound) +void S_LocalSound (const char *sound) { sfx_t *sfx; diff --git a/engine/client/snd_win.c b/engine/client/snd_win.c index 10a8d46f..11cf6b58 100644 --- a/engine/client/snd_win.c +++ b/engine/client/snd_win.c @@ -19,6 +19,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "quakedef.h" #include "winquake.h" +#ifndef WINRT // 64K is > 1 second at 16-bit, 22050 Hz #define WAV_BUFFERS 64 @@ -367,3 +368,4 @@ int WAV_InitCard (soundcardinfo_t *sc, int cardnum) return true; } int (*pWAV_InitCard) (soundcardinfo_t *sc, int cardnum) = &WAV_InitCard; +#endif diff --git a/engine/client/sound.h b/engine/client/sound.h index 1a564793..5a81b8d5 100644 --- a/engine/client/sound.h +++ b/engine/client/sound.h @@ -132,7 +132,7 @@ qboolean S_HaveOutput(void); void S_Music_Clear(sfx_t *onlyifsample); void S_Music_Seek(float time); -sfx_t *S_PrecacheSound (char *sample); +sfx_t *S_PrecacheSound (const char *sample); void S_TouchSound (char *sample); void S_UntouchAll(void); void S_ClearPrecache (void); @@ -237,7 +237,7 @@ extern cvar_t snd_mixerthread; extern int snd_blocked; -void S_LocalSound (char *s); +void S_LocalSound (const char *s); qboolean S_LoadSound (sfx_t *s); typedef qboolean (*S_LoadSound_t) (sfx_t *s, qbyte *data, int datalen, int sndspeed); diff --git a/engine/client/sys_win.c b/engine/client/sys_win.c index 4e908628..e1382691 100644 --- a/engine/client/sys_win.c +++ b/engine/client/sys_win.c @@ -36,6 +36,254 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include #endif +wchar_t *widen(wchar_t *out, size_t outlen, const char *utf8); +char *narrowen(char *out, size_t outlen, wchar_t *wide); + + +#ifdef WINRT //you're going to need a different sys_ port. +qboolean isDedicated = false; +qboolean ActiveApp; +void VARGS Sys_Error (const char *error, ...){} //eep +void VARGS Sys_Printf (char *fmt, ...){} //safe, but not ideal (esp for debugging) +void Sys_SendKeyEvents (void){} //safe, but not ideal +void Sys_ServerActivity(void){} //empty is safe +void Sys_RecentServer(char *command, char *target, char *title, char *desc){} //empty is safe +qboolean Sys_InitTerminal(void){return false;} //failure will break 'setrenderer sv' +char *Sys_ConsoleInput (void){return NULL;} //safe to stub +void Sys_CloseTerminal (void){} //called when switching from dedicated->non-dedicated +dllhandle_t *Sys_LoadLibrary(const char *name, dllfunction_t *funcs){return NULL;} //can just about get away with it +void *Sys_GetAddressForName(dllhandle_t *module, const char *exportname){return NULL;} +void Sys_CloseLibrary(dllhandle_t *lib){} //safe, ish +void Sys_Init (void){} //safe, stub is fine. used to register system-specific cvars/commands. +void Sys_Shutdown(void){} //safe +qboolean Sys_RandomBytes(qbyte *string, int len){return false;} +qboolean Sys_GetDesktopParameters(int *width, int *height, int *bpp, int *refreshrate){return false;} +void INS_Move(float *movements, int pnum){} //safe +void INS_Commands(void){} //safe +void INS_Init(void){} //safe. should be xinput2 I guess. nothing else is actually supported. touchscreens don't really count. +void INS_ReInit(void){} //safe +void INS_Shutdown(void){} //safe +void INS_UpdateGrabs(int fullscreen, int activeapp){} //safe +void *RT_GetCoreWindow(int *width, int *height){return NULL;} //I already wrote the d3d code, but it needs a window to attach to. you can override width+height by writing to them +void D3D11_DoResize(int newwidth, int newheight); //already written, call if resized since getcorewindow + +static char *clippy; +char *Sys_GetClipboard(void) +{ + return Z_StrDup(clippy); +} +void Sys_CloseClipboard(char *bf) +{ + Z_Free(bf); +} +void Sys_SaveClipboard(char *text) +{ + Z_Free(clippy); + clippy = Z_StrDup(text); +} + +static LARGE_INTEGER timestart, timefreq; +static qboolean timeinited = false; +unsigned int Sys_Milliseconds(void) +{ + LARGE_INTEGER cur, diff; + if (!timeinited) + { + timeinited = true; + QueryPerformanceFrequency(&timefreq); + QueryPerformanceCounter(×tart); + } + QueryPerformanceCounter(&cur); + diff.QuadPart = cur.QuadPart - timestart.QuadPart; + diff.QuadPart *= 1000; + diff.QuadPart /= timefreq.QuadPart; + return diff.QuadPart; +} +double Sys_DoubleTime (void) +{ + LARGE_INTEGER cur, diff; + if (!timeinited) + { + timeinited = true; + QueryPerformanceFrequency(&timefreq); + QueryPerformanceCounter(×tart); + } + QueryPerformanceCounter(&cur); + diff.QuadPart = cur.QuadPart - timestart.QuadPart; + diff.QuadPart *= 1000; + return (double)diff.QuadPart / (double)timefreq.QuadPart; //I hope the timefreq doesn't get rounded and cause milliseconds and doubletime to start to drift apart. +} + +void Sys_Quit (void) +{ + Host_Shutdown (); + exit(1); +} + +void Sys_mkdir (char *path) +{ + wchar_t wide[MAX_OSPATH]; + widen(wide, sizeof(wide), path); + CreateDirectoryW(wide, NULL); +} + +qboolean Sys_remove (char *path) +{ + wchar_t wide[MAX_OSPATH]; + widen(wide, sizeof(wide), path); + if (DeleteFileW(wide)) + return true; //success + if (GetLastError() == ERROR_FILE_NOT_FOUND) + return true; //succeed when the file already didn't exist + return false; //other errors? panic +} + +qboolean Sys_Rename (char *oldfname, char *newfname) +{ + wchar_t oldwide[MAX_OSPATH]; + wchar_t newwide[MAX_OSPATH]; + widen(oldwide, sizeof(oldwide), oldfname); + widen(newwide, sizeof(newwide), newfname); + return MoveFileExW(oldwide, newwide, MOVEFILE_REPLACE_EXISTING|MOVEFILE_COPY_ALLOWED); +} + +//enumeratefiles is recursive for */* to work +static int Sys_EnumerateFiles2 (const char *match, int matchstart, int neststart, int (QDECL *func)(const char *fname, qofs_t fsize, void *parm, searchpathfuncs_t *spath), void *parm, searchpathfuncs_t *spath) +{ + qboolean go; + + HANDLE r; + WIN32_FIND_DATAW fd; + int nest = neststart; //neststart refers to just after a / + qboolean wild = false; + + while(match[nest] && match[nest] != '/') + { + if (match[nest] == '?' || match[nest] == '*') + wild = true; + nest++; + } + if (match[nest] == '/') + { + char submatch[MAX_OSPATH]; + char tmproot[MAX_OSPATH]; + + if (!wild) + return Sys_EnumerateFiles2(match, matchstart, nest+1, func, parm, spath); + + if (nest-neststart+1> MAX_OSPATH) + return 1; + memcpy(submatch, match+neststart, nest - neststart); + submatch[nest - neststart] = 0; + nest++; + + if (neststart+4 > MAX_OSPATH) + return 1; + memcpy(tmproot, match, neststart); + strcpy(tmproot+neststart, "*.*"); + + { + wchar_t wroot[MAX_OSPATH]; + r = FindFirstFileExW(widen(wroot, sizeof(wroot), tmproot), FindExInfoStandard, &fd, FindExSearchNameMatch, NULL, 0); + } + strcpy(tmproot+neststart, ""); + if (r==(HANDLE)-1) + return 1; + go = true; + do + { + char utf8[MAX_OSPATH]; + char file[MAX_OSPATH]; + narrowen(utf8, sizeof(utf8), fd.cFileName); + if (*utf8 == '.'); //don't ever find files with a name starting with '.' + else if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) //is a directory + { + if (wildcmp(submatch, utf8)) + { + int newnest; + if (strlen(tmproot) + strlen(utf8) + strlen(match+nest) + 2 < MAX_OSPATH) + { + Q_snprintfz(file, sizeof(file), "%s%s/", tmproot, utf8); + newnest = strlen(file); + strcpy(file+newnest, match+nest); + go = Sys_EnumerateFiles2(file, matchstart, newnest, func, parm, spath); + } + } + } + } while(FindNextFileW(r, &fd) && go); + FindClose(r); + } + else + { + const char *submatch = match + neststart; + char tmproot[MAX_OSPATH]; + + if (neststart+4 > MAX_OSPATH) + return 1; + memcpy(tmproot, match, neststart); + strcpy(tmproot+neststart, "*.*"); + + { + wchar_t wroot[MAX_OSPATH]; + r = FindFirstFileExW(widen(wroot, sizeof(wroot), tmproot), FindExInfoStandard, &fd, FindExSearchNameMatch, NULL, 0); + } + strcpy(tmproot+neststart, ""); + if (r==(HANDLE)-1) + return 1; + go = true; + do + { + char utf8[MAX_OSPATH]; + char file[MAX_OSPATH]; + + narrowen(utf8, sizeof(utf8), fd.cFileName); + if (*utf8 == '.') + ; //don't ever find files with a name starting with '.' (includes .. and . directories, and unix hidden files) + else if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) //is a directory + { + if (wildcmp(submatch, utf8)) + { + if (strlen(tmproot+matchstart) + strlen(utf8) + 2 < MAX_OSPATH) + { + Q_snprintfz(file, sizeof(file), "%s%s/", tmproot+matchstart, utf8); + go = func(file, qofs_Make(fd.nFileSizeLow, fd.nFileSizeHigh), parm, spath); + } + } + } + else + { + if (wildcmp(submatch, utf8)) + { + if (strlen(tmproot+matchstart) + strlen(utf8) + 1 < MAX_OSPATH) + { + Q_snprintfz(file, sizeof(file), "%s%s", tmproot+matchstart, utf8); + go = func(file, qofs_Make(fd.nFileSizeLow, fd.nFileSizeHigh), parm, spath); + } + } + } + } while(FindNextFileW(r, &fd) && go); + FindClose(r); + } + return go; +} +int Sys_EnumerateFiles (const char *gpath, const char *match, int (QDECL *func)(const char *fname, qofs_t fsize, void *parm, searchpathfuncs_t *spath), void *parm, searchpathfuncs_t *spath) +{ + char fullmatch[MAX_OSPATH]; + int start; + if (strlen(gpath) + strlen(match) + 2 > MAX_OSPATH) + return 1; + + strcpy(fullmatch, gpath); + start = strlen(fullmatch); + if (start && fullmatch[start-1] != '/') + fullmatch[start++] = '/'; + fullmatch[start] = 0; + strcat(fullmatch, match); + return Sys_EnumerateFiles2(fullmatch, start, start, func, parm, spath); +} + +#else + #if defined(GLQUAKE) && defined(DEBUG) #define PRINTGLARRAYS #endif @@ -448,7 +696,14 @@ DWORD CrashExceptionHandler (qboolean iswatchdog, DWORD exceptionCode, LPEXCEPTI Sys_SaveClipboard(stacklog+logstart); #ifdef _MSC_VER if (MessageBoxA(0, stacklog, "KABOOM!", MB_ICONSTOP|MB_YESNO) != IDYES) + { + if (pIsDebuggerPresent ()) + { + //its possible someone attached a debugger while we were showing that message + return EXCEPTION_CONTINUE_SEARCH; + } return EXCEPTION_EXECUTE_HANDLER; + } #else MessageBox(0, stacklog, "KABOOM!", MB_ICONSTOP); return EXCEPTION_EXECUTE_HANDLER; @@ -485,10 +740,10 @@ DWORD CrashExceptionHandler (qboolean iswatchdog, DWORD exceptionCode, LPEXCEPTI } /*take a dump*/ - if (*com_homedir) - Q_strncpyz(dumpPath, com_homedir, sizeof(dumpPath)); - else if (*com_quakedir) - Q_strncpyz(dumpPath, com_quakedir, sizeof(dumpPath)); + if (*com_homepath) + Q_strncpyz(dumpPath, com_homepath, sizeof(dumpPath)); + else if (*com_gamepath) + Q_strncpyz(dumpPath, com_gamepath, sizeof(dumpPath)); else GetTempPath (sizeof(dumpPath)-16, dumpPath); Q_strncatz(dumpPath, DISTRIBUTION"CrashDump.dmp", sizeof(dumpPath)); @@ -677,10 +932,6 @@ FILE IO =============================================================================== */ - -wchar_t *widen(wchar_t *out, size_t outlen, const char *utf8); -char *narrowen(char *out, size_t outlen, wchar_t *wide); - void Sys_mkdir (char *path) { if (WinNT) @@ -1193,13 +1444,21 @@ void VARGS Sys_Printf (char *fmt, ...) wchar_t wide[1024], *out; int wlen; - if (!houtput && !debugout) + if (!houtput && !debugout && !SSV_IsSubServer()) return; va_start (argptr,fmt); vsnprintf (text, sizeof(text), fmt, argptr); va_end (argptr); +#ifdef SUBSERVERS + if (SSV_IsSubServer()) + { + SSV_PrintToMaster(text); + return; + } +#endif + end = COM_ParseFunString(CON_WHITEMASK, text, msg, sizeof(msg), false); out = wide; in = msg; @@ -1451,7 +1710,7 @@ void Sys_SaveClipboard(char *text) while (*text) { //NOTE: should be \r\n and not just \n - *temp++ = utf8_decode(&error, text, &text); + *temp++ = utf8_decode(&error, text, &text) & 0xff; } *temp = 0; strcpy(temp, text); @@ -1487,6 +1746,55 @@ char *Sys_ConsoleInput (void) HANDLE th; char *clipText, *textCopied; +#ifdef SUBSERVERS + if (SSV_IsSubServer()) + { + DWORD avail; + static char text[1024], *nl; + static int textpos = 0; + + HANDLE input = GetStdHandle(STD_INPUT_HANDLE); + for (;;) + { + if (!PeekNamedPipe(input, NULL, 0, NULL, &avail, NULL)) + { + SV_FinalMessage("Cluster shut down\n"); + Cmd_ExecuteString("quit force", RESTRICT_LOCAL); + } + else if (avail) + { + if (avail > sizeof(text)-1-textpos) + avail = sizeof(text)-1-textpos; + if (ReadFile(input, text+textpos, avail, &avail, NULL)) + { + textpos += avail; + while(textpos >= 2) + { + unsigned short len = text[0] | (text[1]<<8); + if (textpos >= len && len >= 2) + { + memcpy(net_message.data, text+2, len-2); + net_message.cursize = len-2; + MSG_BeginReading (msg_nullnetprim); + + SSV_ReadFromControlServer(); + + memmove(text, text+len, textpos - len); + textpos -= len; + } + else + break; + } + } + } + else + break; + } + return NULL; + } +#endif + + if (!hinput) return NULL; @@ -1608,6 +1916,10 @@ BOOL WINAPI HandlerRoutine (DWORD dwCtrlType) qboolean Sys_InitTerminal (void) { DWORD m; + + if (SSV_IsSubServer()) + return true; //just pretend we did + if (!AllocConsole()) return false; @@ -1963,7 +2275,7 @@ static IShellLinkW *CreateShellLink(char *command, char *target, char *title, ch IShellLinkW_SetIconLocation(link, buf, 0); /*grab the first icon from our exe*/ IShellLinkW_SetPath(link, buf); /*program to run*/ - Q_strncpyz(tmp, com_quakedir, sizeof(tmp)); + Q_strncpyz(tmp, com_gamepath, sizeof(tmp)); /*normalize the gamedir, so we don't end up with the same thing multiple times*/ for(s = tmp; *s; s++) { @@ -2884,24 +3196,28 @@ int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLin parms.argc = com_argc; parms.argv = com_argv; - #if !defined(CLIENTONLY) && !defined(SERVERONLY) +#if !defined(CLIENTONLY) && !defined(SERVERONLY) if (COM_CheckParm ("-dedicated")) isDedicated = true; + #ifdef SUBSERVERS + if (COM_CheckParm("-clusterslave")) + isDedicated = isClusterSlave = true; #endif +#endif if (isDedicated) { - #if !defined(CLIENTONLY) +#if !defined(CLIENTONLY) if (!Sys_InitTerminal()) Sys_Error ("Couldn't allocate dedicated server console"); - #endif +#endif } if (!Sys_Startup_CheckMem(&parms)) Sys_Error ("Not enough memory free; check disk space\n"); - #ifndef CLIENTONLY +#ifndef CLIENTONLY if (isDedicated) //compleate denial to switch to anything else - many of the client structures are not initialized. { int delay; @@ -2919,19 +3235,19 @@ int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLin } return TRUE; } - #endif +#endif tevent = CreateEvent(NULL, FALSE, FALSE, NULL); if (!tevent) Sys_Error ("Couldn't create event"); - #ifdef SERVERONLY +#ifdef SERVERONLY Sys_Printf ("SV_Init\n"); SV_Init(&parms); - #else +#else Sys_Printf ("Host_Init\n"); Host_Init (&parms); - #endif +#endif oldtime = Sys_DoubleTime (); @@ -3064,3 +3380,4 @@ void Sys_Sleep (double seconds) { Sleep(seconds * 1000); } +#endif diff --git a/engine/client/valid.c b/engine/client/valid.c index a4382b9c..d14c84ef 100644 --- a/engine/client/valid.c +++ b/engine/client/valid.c @@ -2,11 +2,7 @@ #include #ifdef _WIN32 -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#endif - -#include +#include "winquake.h" #endif typedef struct f_modified_s { @@ -59,7 +55,7 @@ static Security_Supported_Binaries_t Security_Supported_Binaries; static Security_Shutdown_t Security_Shutdown; -#ifdef _WIN32 +#if 0//def _WIN32 static void *secmodule; #endif @@ -209,7 +205,7 @@ void InitValidation(void) Cvar_Register(&allow_f_cmdline, "Authentication"); Cvar_Register(&ruleset, "Authentication"); -#ifdef _WIN32 +#if 0//def _WIN32 secmodule = LoadLibrary("fteqw-security.dll"); if (secmodule) { @@ -242,7 +238,7 @@ void InitValidation(void) Cvar_Register(&auth_validateclients, "Authentication"); return; } -#ifdef _WIN32 +#if 0//def _WIN32 FreeLibrary(secmodule); #endif } @@ -380,7 +376,7 @@ rulesetrule_t rulesetrules_strict[] = { {"ruleset_allow_overlong_sounds", "0"}, {"ruleset_allow_larger_models", "0"}, {"ruleset_allow_modified_eyes", "0"}, - {"ruleset_allow_sensative_texture_replacements", "0"}, + {"ruleset_allow_sensitive_texture_replacements", "0"}, {"ruleset_allow_localvolume", "0"}, {"ruleset_allow_fbmodels", "0"}, {"tp_disputablemacros", "0"}, @@ -397,7 +393,7 @@ rulesetrule_t rulesetrules_nqr[] = { {"ruleset_allow_packet", "0"}, {"ruleset_allow_frj", "0"}, {"ruleset_allow_modified_eyes", "0"}, - {"ruleset_allow_sensative_texture_replacements", "0"}, + {"ruleset_allow_sensitive_texture_replacements", "0"}, {"ruleset_allow_localvolume", "0"}, {"ruleset_allow_shaders", "0"}, {"ruleset_allow_fbmodels", "0"}, diff --git a/engine/client/vid.h b/engine/client/vid.h index bd9ec0f8..978a834a 100644 --- a/engine/client/vid.h +++ b/engine/client/vid.h @@ -33,6 +33,7 @@ typedef struct { int height; qboolean fullscreen; qboolean stereo; + qboolean srgb; int bpp; int rate; int wait; //-1 = default, 0 = off, 1 = on, 2 = every other diff --git a/engine/client/view.c b/engine/client/view.c index 0ce95688..544c6a07 100644 --- a/engine/client/view.c +++ b/engine/client/view.c @@ -290,6 +290,7 @@ void V_DriftPitch (playerview_t *pv) ============================================================================== */ +void V_Gamma_Callback(struct cvar_s *var, char *oldvalue); cshift_t cshift_empty = { {130,80,50}, 0 }; cshift_t cshift_water = { {130,80,50}, 128 }; @@ -298,9 +299,9 @@ cshift_t cshift_lava = { {255,80,0}, 150 }; cshift_t cshift_server = { {130,80,50}, 0 }; -cvar_t v_gamma = CVARFD("gamma", "1.0", CVAR_ARCHIVE|CVAR_RENDERERCALLBACK, "Controls how bright the screen is. Setting this to anything but 1 without hardware gamma requires glsl support and can noticably harm your framerate."); -cvar_t v_contrast = CVARFD("contrast", "1.0", CVAR_ARCHIVE, "Scales colour values linearly to make your screen easier to see. Setting this to anything but 1 without hardware gamma will reduce your framerates a little."); -cvar_t v_brightness = CVARFD("brightness", "0.0", CVAR_ARCHIVE, "Brightness is how much 'white' to add to each and every pixel on the screen."); +cvar_t v_gamma = CVARFDC("gamma", "1.0", CVAR_ARCHIVE|CVAR_RENDERERCALLBACK, "Controls how bright the screen is. Setting this to anything but 1 without hardware gamma requires glsl support and can noticably harm your framerate.", V_Gamma_Callback); +cvar_t v_contrast = CVARFDC("contrast", "1.0", CVAR_ARCHIVE, "Scales colour values linearly to make your screen easier to see. Setting this to anything but 1 without hardware gamma will reduce your framerates a little.", V_Gamma_Callback); +cvar_t v_brightness = CVARFDC("brightness", "0.0", CVAR_ARCHIVE, "Brightness is how much 'white' to add to each and every pixel on the screen.", V_Gamma_Callback); qbyte gammatable[256]; // palette is sent through this @@ -362,13 +363,11 @@ void BuildGammaTable (float g, float c, float b) V_CheckGamma ================= */ -#if defined(GLQUAKE) || defined(D3DQUAKE) -void GLV_Gamma_Callback(struct cvar_s *var, char *oldvalue) +void V_Gamma_Callback(struct cvar_s *var, char *oldvalue) { BuildGammaTable (v_gamma.value, v_contrast.value, v_brightness.value); V_UpdatePalette (true); } -#endif /* =============== @@ -758,10 +757,13 @@ void V_UpdatePalette (qboolean force) ramps[2][i] = gammatable[ib]<<8; } - applied = rf->VID_ApplyGammaRamps ((unsigned short*)ramps); - if (!applied && r2d_canhwgamma) - rf->VID_ApplyGammaRamps (NULL); - r2d_canhwgamma = applied; + if (qrenderer) + { + applied = rf->VID_ApplyGammaRamps ((unsigned short*)ramps); + if (!applied && r2d_canhwgamma) + rf->VID_ApplyGammaRamps (NULL); + r2d_canhwgamma = applied; + } } RSpeedEnd(RSPEED_PALETTEFLASHES); @@ -1225,8 +1227,8 @@ void V_CalcRefdef (playerview_t *pv) return; #endif - VectorCopy(cl.fog_colour, r_refdef.gfog_rgbd); - r_refdef.gfog_rgbd[3] = cl.fog_density / 64; + CL_BlendFog(&r_refdef.globalfog, &cl.oldfog, realtime, &cl.fog); + r_refdef.globalfog.density /= 64; //FIXME if (v_viewheight.value < -7) bob=-7; @@ -1628,14 +1630,14 @@ void V_RenderView (void) } else { - if (r_worldentity.model) + if (r_worldentity.model && !r_worldentity.model->needload) { RSpeedMark(); CL_AllowIndependantSendCmd(false); + CL_SetSolidEntities (); CL_TransitionEntities(); - CL_PredictMove (); // build a refresh entity list diff --git a/engine/client/wad.c b/engine/client/wad.c index a26910d5..892f0517 100644 --- a/engine/client/wad.c +++ b/engine/client/wad.c @@ -424,7 +424,7 @@ qbyte *W_ConvertWAD3Texture(miptex_t *tex, int *width, int *height, qboolean *us return data; } -qbyte *W_GetTexture(char *name, int *width, int *height, qboolean *usesalpha)//returns rgba +qbyte *W_GetTexture(const char *name, int *width, int *height, qboolean *usesalpha)//returns rgba { char texname[17]; int i, j; diff --git a/engine/client/wad.h b/engine/client/wad.h index 34992174..afd3c328 100644 --- a/engine/client/wad.h +++ b/engine/client/wad.h @@ -115,4 +115,4 @@ void Mod_ParseWadsFromEntityLump(char *data); qbyte *W_ConvertWAD3Texture(miptex_t *tex, int *width, int *height, qboolean *usesalpha); void Mod_ParseInfoFromEntityLump(struct model_s *wmodel, char *data, char *mapname); qboolean Wad_NextDownload (void); -qbyte *W_GetTexture(char *name, int *width, int *height, qboolean *usesalpha); +qbyte *W_GetTexture(const char *name, int *width, int *height, qboolean *usesalpha); diff --git a/engine/client/winquake.h b/engine/client/winquake.h index 79730abb..f9dfb512 100644 --- a/engine/client/winquake.h +++ b/engine/client/winquake.h @@ -41,6 +41,14 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define _LPCWAVEFORMATEX_DEFINED +#if defined(WINAPI_FAMILY) && !defined(WINRT) + #if WINAPI_FAMILY != WINAPI_FAMILY_DESKTOP_APP + //don't just define it. things that don't #include winquake.h / glquake.h need it too. + #error "WINRT needs to be defined for non-desktop" + #endif +#endif + + #ifndef WM_MOUSEWHEEL #define WM_MOUSEWHEEL 0x020A #endif @@ -96,7 +104,7 @@ void S_BlockSound (void); void S_UnblockSound (void); void VID_SetDefaultMode (void); - +/* int (PASCAL FAR *pWSAStartup)(WORD wVersionRequired, LPWSADATA lpWSAData); int (PASCAL FAR *pWSACleanup)(void); int (PASCAL FAR *pWSAGetLastError)(void); @@ -115,6 +123,7 @@ struct hostent FAR * (PASCAL FAR *pgethostbyaddr)(const char FAR * addr, int len, int type); int (PASCAL FAR *pgetsockname)(SOCKET s, struct sockaddr FAR *name, int FAR * namelen); +*/ #endif #endif diff --git a/engine/client/zqtp.c b/engine/client/zqtp.c index c1dd7852..6aa06dc4 100644 --- a/engine/client/zqtp.c +++ b/engine/client/zqtp.c @@ -1051,7 +1051,9 @@ char *Macro_CombinedHealth(void) //work out the max useful armour //this will under-exagurate, due to usage of ceil based on damage m = h/(1-t); - if (a > m && m > 0) + if (m < 0) + a = 0; + else if (m < a) a = m; h = h + a; diff --git a/engine/common/bothdefs.h b/engine/common/bothdefs.h index 6af5bac4..50368c30 100644 --- a/engine/common/bothdefs.h +++ b/engine/common/bothdefs.h @@ -106,7 +106,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define AVAIL_D3D #endif -#if defined(_WIN32) && !defined(FTE_SDL) +#if defined(_WIN32) && !defined(FTE_SDL) && !defined(WINRT) #define HAVE_WINSSPI //built in component, checks against windows' root ca database and revocations etc. #elif defined(__linux__) || defined(__CYGWIN__) #define HAVE_GNUTLS //currently disabled as it does not validate the server's certificate, beware the mitm attack. @@ -188,6 +188,9 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #ifdef SERVERONLY #define USE_MYSQL //allow mysql in dedicated servers. #endif + #if defined(_WIN32) && !defined(FTE_SDL) && !defined(WINRT) + //#define SUBSERVERS //use subserver code. + #endif #define SIDEVIEWS 4 //enable secondary/reverse views. @@ -234,6 +237,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #endif #define VM_Q1 //q1 qvm gamecode interface + //#define VM_LUA //q1 lua gamecode interface #define TCPCONNECT //a tcpconnect command, that allows the player to connect to tcp-encapsulated qw protocols. #define IRCCONNECT //an ircconnect command, that allows the player to connect to irc-encapsulated qw protocols... yeah, really. @@ -289,6 +293,17 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #undef Q3CLIENT #undef Q3SERVER //trying to trim memory use #endif +#ifdef WINRT + #undef TCPCONNECT //err... + #undef IRCCONNECT //not happening + #undef AVAIL_DSOUND //yeah, good luck there + #undef AVAIL_DINPUT //nope, not supported. + #undef SV_MASTER //no socket interface + #undef CL_MASTER //no socket interface + #undef WEBSERVER //http/ftp servers + #undef WEBCLIENT //http/ftp clients. + #undef MULTITHREAD +#endif #ifdef ANDROID #undef RTLIGHTS #ifndef SPEEX_STATIC @@ -350,7 +365,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #endif #ifndef AVAIL_ZLIB - #undef SUPPORT_ICE + #undef SUPPORT_ICE //depends upon zlib's crc32 for fingerprinting. I cba writing my own. #endif #ifdef SERVERONLY //remove options that don't make sense on only a server @@ -484,12 +499,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define ARCH_CPU_POSTFIX "unk" #endif -#if (defined(_M_IX86) || defined(__i386__)) && !defined(__amd64__) && !defined(_AMD64_) -#define UNALIGNED_OK 1 // set to 0 if unaligned accesses are not supported -#else -#define UNALIGNED_OK 0 -#endif - #ifdef _MSC_VER #define VARGS __cdecl #define MSVCDISABLEWARNINGS @@ -512,7 +521,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define NORETURN __attribute__((noreturn)) #endif -//I'm making my own restrict, because msvc can't cope if I #define restrict to __restrict, and quite possibly other platforms too +//I'm making my own restrict, because msvc's headers can't cope if I #define restrict to __restrict, and quite possibly other platforms too #if __STDC_VERSION__ >= 199901L #define fte_restrict restrict #elif defined(_MSC_VER) diff --git a/engine/common/bspfile.h b/engine/common/bspfile.h index c6cd6e67..bf3f75e7 100644 --- a/engine/common/bspfile.h +++ b/engine/common/bspfile.h @@ -895,7 +895,7 @@ typedef struct pvscache_s { int num_leafs; short leafnums[MAX_ENT_LEAFS]; -#ifdef Q2BSPS +#if defined(Q2BSPS) || defined(TERRAIN) int areanum; //q2bsp int areanum2; //q2bsp int headnode; //q2bsp diff --git a/engine/common/cmd.c b/engine/common/cmd.c index b3e5b9cc..7843c65b 100644 --- a/engine/common/cmd.c +++ b/engine/common/cmd.c @@ -45,10 +45,10 @@ typedef struct cmdalias_s cmdalias_t *cmd_alias; -cvar_t cl_warncmd = SCVAR("cl_warncmd", "1"); -cvar_t cl_aliasoverlap = SCVARF("cl_aliasoverlap", "1", CVAR_NOTFROMSERVER); +cvar_t cl_warncmd = CVARF("cl_warncmd", "1", CVAR_NOSAVE|CVAR_NORESET); +cvar_t cl_aliasoverlap = CVARF("cl_aliasoverlap", "1", CVAR_NOTFROMSERVER); -cvar_t tp_disputablemacros = SCVARF("tp_disputablemacros", "1", CVAR_SEMICHEAT); +cvar_t tp_disputablemacros = CVARF("tp_disputablemacros", "1", CVAR_SEMICHEAT); //============================================================================= @@ -843,7 +843,7 @@ void Cmd_DeleteAlias(char *name) } } -char *Cmd_AliasExist(char *name, int restrictionlevel) +char *Cmd_AliasExist(const char *name, int restrictionlevel) { cmdalias_t *a; // if the alias already exists, reuse it @@ -1061,7 +1061,7 @@ char *VARGS Cmd_Args (void) return cmd_args; } -void Cmd_Args_Set(char *newargs) +void Cmd_Args_Set(const char *newargs) { if (cmd_args_buf) Z_Free(cmd_args_buf); @@ -1365,7 +1365,7 @@ Cmd_TokenizeString Parses the given string into command line tokens, stopping at the \n ============ */ -char *Cmd_TokenizeString (char *text, qboolean expandmacros, qboolean qctokenize) +const char *Cmd_TokenizeString (const char *text, qboolean expandmacros, qboolean qctokenize) { int i; @@ -1668,7 +1668,7 @@ int Cmd_Level(char *name) Cmd_Exists ============ */ -qboolean Cmd_Exists (char *cmd_name) +qboolean Cmd_Exists (const char *cmd_name) { cmd_function_t *cmd; @@ -2862,8 +2862,8 @@ void Cmd_WriteConfig_f(void) snprintf(fname, sizeof(fname), "configs/%s", filename); COM_DefaultExtension(fname, ".cfg", sizeof(fname)); - FS_CreatePath(fname, FS_CONFIGONLY); - f = FS_OpenVFS(fname, "wb", FS_CONFIGONLY); + FS_CreatePath(fname, FS_BASEGAMEONLY); + f = FS_OpenVFS(fname, "wb", FS_BASEGAMEONLY); } if (!f) { diff --git a/engine/common/cmd.h b/engine/common/cmd.h index 6e0cf6e3..7a3e88b8 100644 --- a/engine/common/cmd.h +++ b/engine/common/cmd.h @@ -86,7 +86,7 @@ qboolean Cmd_AddCommandD (char *cmd_name, xcommand_t function, char *description // if function is NULL, the command will be forwarded to the server // as a clc_stringcmd instead of executed locally -qboolean Cmd_Exists (char *cmd_name); +qboolean Cmd_Exists (const char *cmd_name); // used by the cvar code to check for cvar / command name overlap char *Cmd_Describe (char *cmd_name); @@ -115,19 +115,19 @@ int Cmd_CheckParm (char *parm); // Returns the position (1 to argc-1) in the command's argument list // where the given parameter apears, or 0 if not present -char *Cmd_AliasExist(char *name, int restrictionlevel); +char *Cmd_AliasExist(const char *name, int restrictionlevel); void Alias_WipeStuffedAliases(void); void Cmd_AddMacro(char *s, char *(*f)(void), int disputableintentions); void Cmd_TokenizePunctation (char *text, char *punctuation); -char *Cmd_TokenizeString (char *text, qboolean expandmacros, qboolean qctokenize); +const char *Cmd_TokenizeString (const char *text, qboolean expandmacros, qboolean qctokenize); // Takes a null terminated string. Does not need to be /n terminated. // breaks the string up into arg tokens. void Cmd_ExecuteString (char *text, int restrictionlevel); -void Cmd_Args_Set(char *newargs); +void Cmd_Args_Set(const char *newargs); #define RESTRICT_MAX_TOTAL 31 #define RESTRICT_MAX_USER 29 //1-64 it's all about bit size. This is max settable. servers are +1 or +2 diff --git a/engine/common/com_mesh.c b/engine/common/com_mesh.c index 950c9a06..2189ee3a 100644 --- a/engine/common/com_mesh.c +++ b/engine/common/com_mesh.c @@ -7,7 +7,7 @@ extern char loadname[]; qboolean r_loadbumpmapping; extern cvar_t dpcompat_psa_ungroup; extern cvar_t r_noframegrouplerp; -extern cvar_t r_lerpmuzzlehack; +cvar_t r_lerpmuzzlehack = CVARF ("r_lerpmuzzlehack", "1", CVAR_ARCHIVE); //Common loader function. void Mod_DoCRC(model_t *mod, char *buffer, int buffersize) @@ -47,7 +47,6 @@ void Mod_DoCRC(model_t *mod, char *buffer, int buffersize) -#if 1 #ifdef _WIN32 #include @@ -212,38 +211,6 @@ void Mod_NormaliseTextureVectors(vec3_t *n, vec3_t *s, vec3_t *t, int v) #ifdef SKELETALMODELS -static void GenMatrixPosQuat4Scale(vec3_t pos, vec4_t quat, vec3_t scale, float result[12]) -{ - float xx, xy, xz, xw, yy, yz, yw, zz, zw; - float x2, y2, z2; - float s; - x2 = quat[0] + quat[0]; - y2 = quat[1] + quat[1]; - z2 = quat[2] + quat[2]; - - xx = quat[0] * x2; xy = quat[0] * y2; xz = quat[0] * z2; - yy = quat[1] * y2; yz = quat[1] * z2; zz = quat[2] * z2; - xw = quat[3] * x2; yw = quat[3] * y2; zw = quat[3] * z2; - - s = scale[0]; - result[0*4+0] = s*(1.0f - (yy + zz)); - result[1*4+0] = s*(xy + zw); - result[2*4+0] = s*(xz - yw); - - s = scale[1]; - result[0*4+1] = s*(xy - zw); - result[1*4+1] = s*(1.0f - (xx + zz)); - result[2*4+1] = s*(yz + xw); - - s = scale[2]; - result[0*4+2] = s*(xz + yw); - result[1*4+2] = s*(yz - xw); - result[2*4+2] = s*(1.0f - (xx + yy)); - - result[0*4+3] = pos[0]; - result[1*4+3] = pos[1]; - result[2*4+3] = pos[2]; -} /*like above, but guess the quat.w*/ static void GenMatrixPosQuat3Scale(vec3_t pos, vec3_t quat3, vec3_t scale, float result[12]) { @@ -350,12 +317,11 @@ static void PSKGenMatrix(float x, float y, float z, float qx, float qy, float qz result[2*4+3] = z; } -#if 0 /*transforms some skeletal vecV_t values*/ -static void Alias_TransformVerticies_V(float *bonepose, int vertcount, qbyte *bidx, float *weights, float *xyzin, float *fte_restrict xyzout) +static void Alias_TransformVerticies_V(const float *bonepose, int vertcount, qbyte *bidx, float *weights, float *xyzin, float *fte_restrict xyzout) { int i; - float *matrix; + const float *matrix; for (i = 0; i < vertcount; i++, xyzout+=sizeof(vecV_t)/sizeof(vec_t), xyzin+=sizeof(vecV_t)/sizeof(vec_t), bidx+=4, weights+=4) { matrix = &bonepose[12*bidx[0]]; @@ -363,21 +329,21 @@ static void Alias_TransformVerticies_V(float *bonepose, int vertcount, qbyte *bi xyzout[1] = weights[0] * (xyzin[0] * matrix[4] + xyzin[1] * matrix[5] + xyzin[2] * matrix[ 6] + xyzin[3] * matrix[ 7]); xyzout[2] = weights[0] * (xyzin[0] * matrix[8] + xyzin[1] * matrix[9] + xyzin[2] * matrix[10] + xyzin[3] * matrix[11]); - if (bidx[1] != ~(qbyte)0) + if (weights[1]) { matrix = &bonepose[12*bidx[1]]; xyzout[0] += weights[1] * (xyzin[0] * matrix[0] + xyzin[1] * matrix[1] + xyzin[2] * matrix[ 2] + xyzin[3] * matrix[ 3]); xyzout[1] += weights[1] * (xyzin[0] * matrix[4] + xyzin[1] * matrix[5] + xyzin[2] * matrix[ 6] + xyzin[3] * matrix[ 7]); xyzout[2] += weights[1] * (xyzin[0] * matrix[8] + xyzin[1] * matrix[9] + xyzin[2] * matrix[10] + xyzin[3] * matrix[11]); - if (bidx[2] != ~(qbyte)0) + if (weights[2]) { matrix = &bonepose[12*bidx[2]]; xyzout[0] += weights[2] * (xyzin[0] * matrix[0] + xyzin[1] * matrix[1] + xyzin[2] * matrix[ 2] + xyzin[3] * matrix[ 3]); xyzout[1] += weights[2] * (xyzin[0] * matrix[4] + xyzin[1] * matrix[5] + xyzin[2] * matrix[ 6] + xyzin[3] * matrix[ 7]); xyzout[2] += weights[2] * (xyzin[0] * matrix[8] + xyzin[1] * matrix[9] + xyzin[2] * matrix[10] + xyzin[3] * matrix[11]); - if (bidx[3] != ~(qbyte)0) + if (weights[3]) { matrix = &bonepose[12*bidx[3]]; xyzout[0] += weights[3] * (xyzin[0] * matrix[0] + xyzin[1] * matrix[1] + xyzin[2] * matrix[ 2] + xyzin[3] * matrix[ 3]); @@ -388,15 +354,14 @@ static void Alias_TransformVerticies_V(float *bonepose, int vertcount, qbyte *bi } } } -#endif /*transforms some skeletal vecV_t values*/ -static void Alias_TransformVerticies_VN(float *bonepose, int vertcount, qbyte *bidx, float *weights, - float *xyzin, float *fte_restrict xyzout, - float *normin, float *fte_restrict normout) +static void Alias_TransformVerticies_VN(const float *bonepose, int vertcount, const qbyte *bidx, float *weights, + const float *xyzin, float *fte_restrict xyzout, + const float *normin, float *fte_restrict normout) { int i, j; - float *matrix; + const float *matrix, *matrix1; float mat[12]; for (i = 0; i < vertcount; i++, xyzout+=sizeof(vecV_t)/sizeof(vec_t), xyzin+=sizeof(vecV_t)/sizeof(vec_t), @@ -404,13 +369,11 @@ static void Alias_TransformVerticies_VN(float *bonepose, int vertcount, qbyte *b bidx+=4, weights+=4) { matrix = &bonepose[12*bidx[0]]; - for (j = 0; j < 12; j++) - mat[j] = weights[0] * matrix[j]; if (weights[1]) { - matrix = &bonepose[12*bidx[1]]; + matrix1 = &bonepose[12*bidx[1]]; for (j = 0; j < 12; j++) - mat[j] += weights[1] * matrix[j]; + mat[j] = (weights[0] * matrix[j]) + (weights[1] * matrix1[j]); if (weights[2]) { matrix = &bonepose[12*bidx[2]]; @@ -423,9 +386,9 @@ static void Alias_TransformVerticies_VN(float *bonepose, int vertcount, qbyte *b mat[j] += weights[3] * matrix[j]; } } + matrix = mat; } - matrix = mat; xyzout[0] = (xyzin[0] * matrix[0] + xyzin[1] * matrix[1] + xyzin[2] * matrix[ 2] + matrix[ 3]); xyzout[1] = (xyzin[0] * matrix[4] + xyzin[1] * matrix[5] + xyzin[2] * matrix[ 6] + matrix[ 7]); xyzout[2] = (xyzin[0] * matrix[8] + xyzin[1] * matrix[9] + xyzin[2] * matrix[10] + matrix[11]); @@ -436,50 +399,70 @@ static void Alias_TransformVerticies_VN(float *bonepose, int vertcount, qbyte *b } } -#if 0 -/*transforms some skeletal vec3_t values*/ -static void Alias_TransformVerticies_3(float *fte_restrict bonepose, int vertcount, qbyte *bidx, float *weights, float *xyzin, float *fte_restrict xyzout) +/*transforms some skeletal vecV_t values*/ +static void Alias_TransformVerticies_VNST(const float *bonepose, int vertcount, const qbyte *bidx, const float *weights, + const float *xyzin, float *fte_restrict xyzout, + const float *normin, float *fte_restrict normout, + const float *sdirin, float *fte_restrict sdirout, + const float *tdirin, float *fte_restrict tdirout) { - int i; - float *matrix; - for (i = 0; i < vertcount; i++, xyzout+=sizeof(vec3_t)/sizeof(vec_t), xyzin+=sizeof(vec3_t)/sizeof(vec_t), bidx+=4, weights+=4) + int i, j; + const float *matrix, *matrix1; + float mat[12]; + for (i = 0; i < vertcount; i++, bidx+=4, weights+=4) { matrix = &bonepose[12*bidx[0]]; - xyzout[0] = weights[0] * (xyzin[0] * matrix[0] + xyzin[1] * matrix[1] + xyzin[2] * matrix[ 2] + xyzin[3] * matrix[ 3]); - xyzout[1] = weights[0] * (xyzin[0] * matrix[4] + xyzin[1] * matrix[5] + xyzin[2] * matrix[ 6] + xyzin[3] * matrix[ 7]); - xyzout[2] = weights[0] * (xyzin[0] * matrix[8] + xyzin[1] * matrix[9] + xyzin[2] * matrix[10] + xyzin[3] * matrix[11]); - - if (bidx[1] != ~(qbyte)0) + if (weights[1]) { - matrix = &bonepose[12*bidx[1]]; - xyzout[0] += weights[1] * (xyzin[0] * matrix[0] + xyzin[1] * matrix[1] + xyzin[2] * matrix[ 2] + xyzin[3] * matrix[ 3]); - xyzout[1] += weights[1] * (xyzin[0] * matrix[4] + xyzin[1] * matrix[5] + xyzin[2] * matrix[ 6] + xyzin[3] * matrix[ 7]); - xyzout[2] += weights[1] * (xyzin[0] * matrix[8] + xyzin[1] * matrix[9] + xyzin[2] * matrix[10] + xyzin[3] * matrix[11]); - - if (bidx[2] != ~(qbyte)0) + matrix1 = &bonepose[12*bidx[1]]; + for (j = 0; j < 12; j++) + mat[j] = (weights[0] * matrix[j]) + (weights[1] * matrix1[j]); + if (weights[2]) { matrix = &bonepose[12*bidx[2]]; - xyzout[0] += weights[2] * (xyzin[0] * matrix[0] + xyzin[1] * matrix[1] + xyzin[2] * matrix[ 2] + xyzin[3] * matrix[ 3]); - xyzout[1] += weights[2] * (xyzin[0] * matrix[4] + xyzin[1] * matrix[5] + xyzin[2] * matrix[ 6] + xyzin[3] * matrix[ 7]); - xyzout[2] += weights[2] * (xyzin[0] * matrix[8] + xyzin[1] * matrix[9] + xyzin[2] * matrix[10] + xyzin[3] * matrix[11]); - - if (bidx[3] != ~(qbyte)0) + for (j = 0; j < 12; j++) + mat[j] += weights[2] * matrix[j]; + if (weights[3]) { matrix = &bonepose[12*bidx[3]]; - xyzout[0] += weights[3] * (xyzin[0] * matrix[0] + xyzin[1] * matrix[1] + xyzin[2] * matrix[ 2] + xyzin[3] * matrix[ 3]); - xyzout[1] += weights[3] * (xyzin[0] * matrix[4] + xyzin[1] * matrix[5] + xyzin[2] * matrix[ 6] + xyzin[3] * matrix[ 7]); - xyzout[2] += weights[3] * (xyzin[0] * matrix[8] + xyzin[1] * matrix[9] + xyzin[2] * matrix[10] + xyzin[3] * matrix[11]); + for (j = 0; j < 12; j++) + mat[j] += weights[3] * matrix[j]; } } + matrix = mat; } + + xyzout[0] = (xyzin[0] * matrix[0] + xyzin[1] * matrix[1] + xyzin[2] * matrix[ 2] + matrix[ 3]); + xyzout[1] = (xyzin[0] * matrix[4] + xyzin[1] * matrix[5] + xyzin[2] * matrix[ 6] + matrix[ 7]); + xyzout[2] = (xyzin[0] * matrix[8] + xyzin[1] * matrix[9] + xyzin[2] * matrix[10] + matrix[11]); + xyzout+=sizeof(vecV_t)/sizeof(vec_t); + xyzin+=sizeof(vecV_t)/sizeof(vec_t); + + normout[0] = (normin[0] * matrix[0] + normin[1] * matrix[1] + normin[2] * matrix[ 2]); + normout[1] = (normin[0] * matrix[4] + normin[1] * matrix[5] + normin[2] * matrix[ 6]); + normout[2] = (normin[0] * matrix[8] + normin[1] * matrix[9] + normin[2] * matrix[10]); + normout+=sizeof(vec3_t)/sizeof(vec_t); + normin+=sizeof(vec3_t)/sizeof(vec_t); + + sdirout[0] = (sdirin[0] * matrix[0] + sdirin[1] * matrix[1] + sdirin[2] * matrix[ 2]); + sdirout[1] = (sdirin[0] * matrix[4] + sdirin[1] * matrix[5] + sdirin[2] * matrix[ 6]); + sdirout[2] = (sdirin[0] * matrix[8] + sdirin[1] * matrix[9] + sdirin[2] * matrix[10]); + sdirout+=sizeof(vec3_t)/sizeof(vec_t); + sdirin+=sizeof(vec3_t)/sizeof(vec_t); + + tdirout[0] = (tdirin[0] * matrix[0] + tdirin[1] * matrix[1] + tdirin[2] * matrix[ 2]); + tdirout[1] = (tdirin[0] * matrix[4] + tdirin[1] * matrix[5] + tdirin[2] * matrix[ 6]); + tdirout[2] = (tdirin[0] * matrix[8] + tdirin[1] * matrix[9] + tdirin[2] * matrix[10]); + tdirout+=sizeof(vec3_t)/sizeof(vec_t); + tdirin+=sizeof(vec3_t)/sizeof(vec_t); } } -#endif -static void Alias_TransformVerticies_SW(float *bonepose, galisskeletaltransforms_t *weights, int numweights, vecV_t *xyzout, vec3_t *normout) +static void Alias_TransformVerticies_SW(const float *bonepose, galisskeletaltransforms_t *weights, int numweights, vecV_t *xyzout, vec3_t *normout) { int i; - float *out, *matrix; + float *out; + const float *matrix; galisskeletaltransforms_t *v = weights; #ifndef SERVERONLY float *normo; @@ -518,6 +501,149 @@ static void Alias_TransformVerticies_SW(float *bonepose, galisskeletaltransforms } } +//converts one entire frame to another skeleton type +//only writes to destbuffer if absolutely needed +const float *Alias_ConvertBoneData(skeltype_t sourcetype, const float *sourcedata, size_t bonecount, galiasbone_t *bones, skeltype_t desttype, float *destbuffer, float *destbufferalt, size_t destbonecount) +{ + int i; + if (sourcetype == desttype) + return sourcedata; + + //everything can be converted up to SKEL_INVERSE_ABSOLUTE and back. + //this means that everything can be converted to everything else, but it might take lots of individual transforms. + //a->ia + //r->a->ia + //a->r + //ia->ir + //ir->ia + //r->a->ia->ir + //a->ia->ir + + if (bonecount > destbonecount || bonecount > MAX_BONES) + Sys_Error("Alias_ConvertBoneData: too many bones %i>%i\n", bonecount, destbonecount); + + //r(->a)->ia(->ir) + if (desttype == SKEL_INVERSE_RELATIVE && sourcetype == SKEL_RELATIVE) + { + //for this conversion, we need absolute data. + //this is not an efficient operation. + sourcedata = Alias_ConvertBoneData(sourcetype, sourcedata, bonecount, bones, SKEL_ABSOLUTE, destbuffer, destbufferalt, destbonecount); + sourcetype = SKEL_INVERSE_ABSOLUTE; + } + //ir->ia(->a->r) + //ir->ia(->a) + //a->ia(->ir) + if ((desttype == SKEL_ABSOLUTE && sourcetype == SKEL_INVERSE_RELATIVE) || + (desttype == SKEL_RELATIVE && sourcetype == SKEL_INVERSE_RELATIVE) || + (desttype == SKEL_INVERSE_RELATIVE && sourcetype == SKEL_ABSOLUTE)) + { + //for this conversion, we need absolute data. + //this is not an efficient operation. + sourcedata = Alias_ConvertBoneData(sourcetype, sourcedata, bonecount, bones, SKEL_INVERSE_ABSOLUTE, destbuffer, destbufferalt, destbonecount); + sourcetype = SKEL_INVERSE_ABSOLUTE; + } + + //r->a + //r->a(->ia) + //ir->ia + if ((sourcetype == SKEL_RELATIVE && (desttype == SKEL_ABSOLUTE || desttype == SKEL_INVERSE_ABSOLUTE)) || + (sourcetype == SKEL_INVERSE_RELATIVE && desttype == SKEL_INVERSE_ABSOLUTE)) + { + float *dest = (sourcedata == destbuffer)?destbufferalt:destbuffer; + /*needs to be an absolute skeleton*/ + for (i = 0; i < bonecount; i++) + { + if (bones[i].parent >= 0) + R_ConcatTransforms((void*)(dest + bones[i].parent*12), (void*)(sourcedata+i*12), (void*)(dest+i*12)); + else + { + Vector4Copy(sourcedata+i*12+0, dest+i*12+0); + Vector4Copy(sourcedata+i*12+4, dest+i*12+4); + Vector4Copy(sourcedata+i*12+8, dest+i*12+8); + } + } + sourcedata = dest; + if (sourcetype == SKEL_INVERSE_RELATIVE) + sourcetype = SKEL_INVERSE_ABSOLUTE; + else + sourcetype = SKEL_ABSOLUTE; + } + + //ia->a(->r) + //ia->a + if ((desttype == SKEL_RELATIVE || desttype == SKEL_ABSOLUTE) && sourcetype == SKEL_INVERSE_ABSOLUTE) + { + float iim[12]; + float *dest = (sourcedata == destbuffer)?destbufferalt:destbuffer; + for (i = 0; i < bonecount; i++) + { + Matrix3x4_Invert_Simple(bones[i].inverse, iim); + R_ConcatTransforms((void*)(sourcedata + i*12), (void*)iim, (void*)(dest + i*12)); + } + sourcedata = dest; + sourcetype = SKEL_ABSOLUTE; + } + + //ia->ir + //a->r + if ((desttype == SKEL_RELATIVE && sourcetype == SKEL_ABSOLUTE) || + (desttype == SKEL_INVERSE_RELATIVE && sourcetype == SKEL_INVERSE_ABSOLUTE)) + { + float ip[12]; + float *dest = (sourcedata == destbuffer)?destbufferalt:destbuffer; + for (i = 0; i < bonecount; i++) + { + if (bones[i].parent >= 0) + { + Matrix3x4_Invert_Simple(sourcedata+bones[i].parent*12, ip); + R_ConcatTransforms((void*)ip, (void*)(sourcedata+i*12), (void*)(dest+i*12)); + } + else + { + Vector4Copy(sourcedata+i*12+0, dest+i*12+0); + Vector4Copy(sourcedata+i*12+4, dest+i*12+4); + Vector4Copy(sourcedata+i*12+8, dest+i*12+8); + } + } + sourcedata = dest; + if (sourcetype == SKEL_INVERSE_ABSOLUTE) + sourcetype = SKEL_INVERSE_RELATIVE; + else + sourcetype = SKEL_RELATIVE; + } + + //a->ia + if (desttype == SKEL_INVERSE_ABSOLUTE && sourcetype == SKEL_ABSOLUTE) + { + float *dest = (sourcedata == destbuffer)?destbufferalt:destbuffer; + for (i = 0; i < bonecount; i++) + R_ConcatTransforms((void*)(sourcedata + i*12), (void*)(bones[i].inverse), (void*)(dest + i*12)); + sourcedata = dest; + sourcetype = SKEL_INVERSE_ABSOLUTE; + } + + if (sourcetype != desttype) + Sys_Error("Alias_ConvertBoneData: %i->%i not supported\n", sourcetype, desttype); + + return sourcedata; +} +/* +converts the bone data from source to dest. +uses parent bone info, so don't try to offset for a first bone. +ALWAYS writes dest. Don't force it if you don't want to waste cycles when no conversion is actually needed. +destbonecount is to catch errors, its otherwise ignored for now. no identity padding. +*/ +void Alias_ForceConvertBoneData(skeltype_t sourcetype, const float *sourcedata, size_t bonecount, galiasbone_t *bones, skeltype_t desttype, float *destbuffer, size_t destbonecount) +{ + float altbuffer[MAX_BONES*12]; + const float *buf = Alias_ConvertBoneData(sourcetype, sourcedata, bonecount, bones, desttype, destbuffer, altbuffer, destbonecount); + if (buf != destbuffer) + { + //Alias_ConvertBoneData successfully managed to avoid doing any work. bah. + memcpy(destbuffer, buf, bonecount*12*sizeof(float)); + } +} + static float Alias_CalculateSkeletalNormals(galiasinfo_t *model) { #ifndef SERVERONLY @@ -537,10 +663,11 @@ static float Alias_CalculateSkeletalNormals(galiasinfo_t *model) vec3_t tn; vec3_t d1, d2; index_t *idx; - float *bonepose = NULL; + const float *bonepose = NULL; float angle; float maxvdist = 0, d, maxbdist = 0; float absmatrix[MAX_BONES*12]; + float absmatrixalt[MAX_BONES*12]; float bonedist[MAX_BONES]; int modnum = 0; int bcmodnum = -1; @@ -575,26 +702,13 @@ static float Alias_CalculateSkeletalNormals(galiasinfo_t *model) g = model->groupofs; if (g->numposes < 1) return 0; - bonepose = g->boneofs; - if (g->isheirachical) - { - /*needs to be an absolute skeleton*/ - for (i = 0; i < model->numbones; i++) - { - if (bones[i].parent >= 0) - R_ConcatTransforms((void*)(absmatrix + bones[i].parent*12), (void*)(bonepose+i*12), (void*)(absmatrix+i*12)); - else - for (j = 0;j < 12;j++) //parentless - absmatrix[i*12+j] = (bonepose)[i*12+j]; - } - bonepose = absmatrix; - } + bonepose = Alias_ConvertBoneData(g->skeltype, g->boneofs, numbones, bones, SKEL_ABSOLUTE, absmatrix, absmatrixalt, MAX_BONES); } /*calculate the bone sizes (assuming the bones are strung up and hanging or such)*/ for (i = 0; i < model->numbones; i++) { vec3_t d; - float *b; + const float *b; b = bonepose + i*12; d[0] = b[3]; d[1] = b[7]; @@ -716,397 +830,31 @@ static float Alias_CalculateSkeletalNormals(galiasinfo_t *model) return 0; #endif } - -static int Alias_BuildLerps(float plerp[4], float *pose[4], int numbones, galiasgroup_t *g1, galiasgroup_t *g2, float lerpfrac, float fg1time, float fg2time) -{ - int frame1; - int frame2; - float mlerp; //minor lerp, poses within a group. - int l = 0; - if (g1 == g2) - lerpfrac = 0; - if (fg1time < 0) - fg1time = 0; - mlerp = (fg1time)*g1->rate; - frame1=mlerp; - frame2=frame1+1; - mlerp-=frame1; - if (g1->loop) - { - frame1=frame1%g1->numposes; - frame2=frame2%g1->numposes; - } - else - { - frame1=(frame1>g1->numposes-1)?g1->numposes-1:frame1; - frame2=(frame2>g1->numposes-1)?g1->numposes-1:frame2; - } - - if (frame1 == frame2 || r_noframegrouplerp.ival) - mlerp = 0; - plerp[l] = (1-mlerp)*(1-lerpfrac); - if (plerp[l]>0) - pose[l++] = g1->boneofs + numbones*12*frame1; - plerp[l] = (mlerp)*(1-lerpfrac); - if (plerp[l]>0) - pose[l++] = g1->boneofs + numbones*12*frame2; - - if (lerpfrac) - { - if (fg2time < 0) - fg2time = 0; - mlerp = (fg2time)*g2->rate; - frame1=mlerp; - frame2=frame1+1; - mlerp-=frame1; - if (g2->loop) - { - frame1=frame1%g2->numposes; - frame2=frame2%g2->numposes; - } - else - { - frame1=(frame1>g2->numposes-1)?g2->numposes-1:frame1; - frame2=(frame2>g2->numposes-1)?g2->numposes-1:frame2; - } - if (frame1 == frame2 || r_noframegrouplerp.ival) - mlerp = 0; - plerp[l] = (1-mlerp)*(lerpfrac); - if (plerp[l]>0) - pose[l++] = g2->boneofs + numbones*12*frame1; - plerp[l] = (mlerp)*(lerpfrac); - if (plerp[l]>0) - pose[l++] = g2->boneofs + numbones*12*frame2; - } - - return l; -} - -//ignores any skeletal objects -int Alias_GetBoneRelations(galiasinfo_t *inf, framestate_t *fstate, float *result, int firstbone, int lastbones) -{ -#ifdef SKELETALMODELS - if (inf->numbones) - { - galiasgroup_t *g1, *g2; - - float *matrix; //the matrix for a single bone in a single pose. - int b, k; //counters - - float *pose[4]; //the per-bone matricies (one for each pose) - float plerp[4]; //the ammount of that pose to use (must combine to 1) - int numposes = 0; - - int frame1, frame2; - float f1time, f2time; - float f2ness; - - int bonegroup; - int cbone = 0; - int endbone; - - if (lastbones > inf->numbones) - lastbones = inf->numbones; - if (!lastbones) - return 0; - - for (bonegroup = 0; bonegroup < FS_COUNT; bonegroup++) - { - endbone = fstate->g[bonegroup].endbone; - if (bonegroup == FS_COUNT-1 || endbone > lastbones) - endbone = lastbones; - - if (endbone == cbone) - continue; - - frame1 = fstate->g[bonegroup].frame[0]; - frame2 = fstate->g[bonegroup].frame[1]; - f1time = fstate->g[bonegroup].frametime[0]; - f2time = fstate->g[bonegroup].frametime[1]; - f2ness = fstate->g[bonegroup].lerpfrac; - - //FIXME: fixup these framestates earlier, because this just isn't nice - if (frame1 < 0 || frame1 >= inf->groups) - { - if (frame2 < 0 || frame2 >= inf->groups || f2ness == 0) - { - if (bonegroup != FS_COUNT-1) - continue; //just ignore this group - - //there's no escaping it, both are bad. use the base pose - f2ness = 0; - frame1 = frame2 = 0; - } - else - { - //kill it, just use frame2 - f2ness = 1; - frame1 = frame2; - } - } - else - { - if (frame2 < 0 || frame2 >= inf->groups) - { - //kill this anim - f2ness = 0; - frame2 = frame1; - } - } - - //the higher level merges old/new anims, but we still need to blend between automated frame-groups. - g1 = &inf->groupofs[frame1]; - g2 = &inf->groupofs[frame2]; - - if (!g1->isheirachical) - return 0; - if (!g2->isheirachical) - g2 = g1; - - numposes = Alias_BuildLerps(plerp, pose, inf->numbones, g1, g2, f2ness, f1time, f2time); - - if (numposes == 1) - { - memcpy(result, pose[0]+cbone*12, (lastbones-cbone)*12*sizeof(float)); - result += (lastbones-cbone)*12; - cbone = lastbones; - } - else - { - //set up the identity matrix - for (; cbone < lastbones; cbone++) - { - //set up the per-bone transform matrix - for (k = 0;k < 12;k++) - result[k] = 0; - for (b = 0;b < numposes;b++) - { - matrix = pose[b] + cbone*12; - - for (k = 0;k < 12;k++) - result[k] += matrix[k] * plerp[b]; - } - result += 12; - } - } - } - return cbone; - } -#endif - return 0; -} - -//_may_ write into bonepose, return value is the real result. obtains absolute values -float *Alias_GetBonePositions(galiasinfo_t *inf, framestate_t *fstate, float *buffer, int buffersize, qboolean renderable) -{ -#ifdef SKELETALMODELS - float relationsbuf[MAX_BONES][12]; - float *relations = NULL; - galiasbone_t *bones = inf->ofsbones; - int numbones; - - if (buffersize < inf->numbones) - numbones = 0; - else if (fstate->bonestate && fstate->bonecount >= inf->numbones) - { - relations = fstate->bonestate; - numbones = inf->numbones; - if (fstate->boneabs) - { - /*we may need to invert by the inverse of the base pose to get the bones into the proper positions*/ - if (!inf->numswtransforms && renderable) - { - int i; - for (i = 0; i < inf->numbones; i++) - { - R_ConcatTransforms((void*)(relations + i*12), (void*)(bones[i].inverse), (void*)(buffer + i*12)); - } - return buffer; - } - return relations; - } - } - else - { - numbones = Alias_GetBoneRelations(inf, fstate, (float*)relationsbuf, 0, inf->numbones); - if (numbones == inf->numbones) - relations = (float*)relationsbuf; - } - if (relations) - { - int i, k; - - if (!inf->numswtransforms && renderable) - { - float absbuf[MAX_BONES][12]; - for (i = 0; i < numbones; i++) - { - if (bones[i].parent >= 0) - R_ConcatTransforms((void*)(absbuf[bones[i].parent]), (void*)((float*)relations+i*12), (void*)absbuf[i]); - else - for (k = 0;k < 12;k++) //parentless - absbuf[i][k] = ((float*)relations)[i*12+k]; - - R_ConcatTransforms((void*)absbuf[i], (void*)bones[i].inverse, (void*)(buffer+i*12)); - } - } - else - { - for (i = 0; i < numbones; i++) - { - if (bones[i].parent >= 0) - R_ConcatTransforms((void*)(buffer + bones[i].parent*12), (void*)((float*)relations+i*12), (void*)(buffer+i*12)); - else - for (k = 0;k < 12;k++) //parentless - buffer[i*12+k] = ((float*)relations)[i*12+k]; - } - } - return buffer; - } - else - { - int i, k; - - int l=0; - float plerp[4]; - float *pose[4]; - - int numposes; - int f; - float lerpfrac = fstate->g[FS_REG].lerpfrac; - - galiasgroup_t *g1, *g2; - - //galiasbone_t *bones = (galiasbone_t *)((char*)inf+inf->ofsbones); //unsed variable - - if (buffersize < inf->numbones) - return NULL; - - f = fstate->g[FS_REG].frame[0]; - if (f < 0 || f >= inf->groups) - f = 0; - g1 = &inf->groupofs[bound(0, f, inf->groups-1)]; - f = fstate->g[FS_REG].frame[1]; - if (f < 0 || f >= inf->groups) - g2 = g1; - else - g2 = &inf->groupofs[bound(0, f, inf->groups-1)]; - - if (g2->isheirachical) - g2 = g1; - - - numposes = Alias_BuildLerps(plerp, pose, inf->numbones, g1, g2, lerpfrac, fstate->g[FS_REG].frametime[0], fstate->g[FS_REG].frametime[1]); - - { - //this is not hierachal, using base frames is not a good idea. - //just blend the poses here - if (numposes == 1) - return pose[0]; - else if (numposes == 2) - { - for (i = 0; i < inf->numbones*12; i++) - { - ((float*)buffer)[i] = pose[0][i]*plerp[0] + pose[1][i]*plerp[1]; - } - } - else - { - for (i = 0; i < inf->numbones; i++) - { - for (l = 0; l < 12; l++) - buffer[i*12+l] = 0; - for (k = 0; k < numposes; k++) - { - for (l = 0; l < 12; l++) - buffer[i*12+l] += pose[k][i*12+l] * plerp[k]; - } - } - } - } - return buffer; - } -#endif - return NULL; -} - - - - - - - - - - -static void R_LerpBones(float *plerp, float **pose, int poses, galiasbone_t *bones, int bonecount, float bonepose[MAX_BONES][12]) -{ - int i, k, b; - float *matrix, m[12]; - - if (poses == 1) - { - // vertex weighted skeletal - // interpolate matrices and concatenate them to their parents - for (i = 0;i < bonecount;i++) - { - matrix = pose[0] + i*12; - - if (bones[i].parent >= 0) - R_ConcatTransforms((void*)bonepose[bones[i].parent], (void*)matrix, (void*)bonepose[i]); - else - for (k = 0;k < 12;k++) //parentless - bonepose[i][k] = matrix[k]; - } - } - else - { - // vertex weighted skeletal - // interpolate matrices and concatenate them to their parents - for (i = 0;i < bonecount;i++) - { - for (k = 0;k < 12;k++) - m[k] = 0; - for (b = 0;b < poses;b++) - { - matrix = pose[b] + i*12; - - for (k = 0;k < 12;k++) - m[k] += matrix[k] * plerp[b]; - } - if (bones[i].parent >= 0) - R_ConcatTransforms((void*)bonepose[bones[i].parent], (void*)m, (void*)bonepose[i]); - else - for (k = 0;k < 12;k++) //parentless - bonepose[i][k] = m[k]; - } - } -} #endif -#if defined(D3DQUAKE) || defined(GLQUAKE) +#if 1 struct { - int numcolours; - avec4_t *colours; - int numcoords; vecV_t *coords; int numnorm; vec3_t *norm; - int surfnum; + int bonegroup; + int vertgroup; entity_t *ent; #ifdef SKELETALMODELS - float bonepose[MAX_BONES*12]; - float *usebonepose; + float boneposebuffer1[MAX_BONES*12]; + float boneposebuffer2[MAX_BONES*12]; + skeltype_t bonecachetype; + const float *usebonepose; int bonecount; #endif @@ -1125,6 +873,7 @@ struct #include #endif +#ifndef SERVERONLY void R_LightArraysByte_BGR(const entity_t *entity, vecV_t *coords, byte_vec4_t *colours, int vertcount, vec3_t *normals) { int i; @@ -1260,28 +1009,21 @@ void R_LightArrays(const entity_t *entity, vecV_t *coords, avec4_t *colours, int } } } +#endif static void R_LerpFrames(mesh_t *mesh, galiaspose_t *p1, galiaspose_t *p2, float lerp, float expand, float lerpcutoff) { +#ifdef SERVERONLY + //no lerping in dedicated servers. too lazy. + mesh->xyz_array = p1->ofsverts; +#else extern cvar_t r_nolerp; // r_nolightdir is unused float blerp = 1-lerp; int i; - vecV_t *p1v, *p2v; - vec3_t *p1n, *p2n; - vec3_t *p1s, *p2s; - vec3_t *p1t, *p2t; - - p1v = p1->ofsverts; - p2v = p2->ofsverts; - - p1n = p1->ofsnormals; - p2n = p2->ofsnormals; - - p1s = p1->ofssvector; - p2s = p2->ofssvector; - - p1t = p1->ofstvector; - p2t = p2->ofstvector; + vecV_t *p1v = p1->ofsverts, *p2v = p2->ofsverts; + vec3_t *p1n = p1->ofsnormals, *p2n = p2->ofsnormals; + vec3_t *p1s = p1->ofssvector, *p2s = p2->ofssvector; + vec3_t *p1t = p1->ofstvector, *p2t = p2->ofstvector; mesh->snormals_array = blerp>0.5?p2s:p1s; //never lerp mesh->tnormals_array = blerp>0.5?p2t:p1t; //never lerp @@ -1366,39 +1108,357 @@ static void R_LerpFrames(mesh_t *mesh, galiaspose_t *p1, galiaspose_t *p2, float } } } +#endif } +#endif #ifdef SKELETALMODELS -#ifndef SERVERONLY -static void Alias_BuildSkeletalMesh(mesh_t *mesh, float *bonepose, galiasinfo_t *inf) +/* + returns the up-to-4 skeletal bone poses to blend together. + return value is the number of blends that are actually live. +*/ +typedef struct { - galisskeletaltransforms_t *weights = inf->ofsswtransforms; - int numweights = inf->numswtransforms; - - if (inf->ofs_skel_idx) + 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[4]; //weight of this animation (1 if lerpcount is 1) + float *pose[4]; //pointer to the raw frame data for bone 0. + int lerpcount; //number of pose+frac entries. +} skellerps_t; +static void Alias_BuildSkelLerps(skellerps_t *lerps, int numbones, galiasgroup_t *g1, galiasgroup_t *g2, float lerpfrac, float fg1time, float fg2time) +{ + int frame1; + int frame2; + float mlerp; //minor lerp, poses within a group. + int l = 0; + if (g1 == g2) + lerpfrac = 0; + if (fg1time < 0) + fg1time = 0; + mlerp = (fg1time)*g1->rate; + frame1=mlerp; + frame2=frame1+1; + mlerp-=frame1; + if (g1->loop) { - float *fte_restrict xyzout = mesh->xyz_array[0]; - float *fte_restrict normout = mesh->normals_array[0]; - qbyte *fte_restrict bidx = inf->ofs_skel_idx[0]; - float *fte_restrict xyzin = inf->ofs_skel_xyz[0]; - float *fte_restrict normin = inf->ofs_skel_norm[0]; -// float *fte_restrict svect = inf->ofs_skel_svect[0]; -// float *fte_restrict tvect = inf->ofs_skel_tvect[0]; - float *fte_restrict weight = inf->ofs_skel_weight[0]; - - Alias_TransformVerticies_VN(bonepose, inf->numverts, bidx, weight, xyzin, xyzout, normin, normout); -// Alias_TransformVerticies_3(bonepose, inf->numverts, bidx, weight, svect, mesh->snormals_array[0]); -// Alias_TransformVerticies_3(bonepose, inf->numverts, bidx, weight, tvect, mesh->tnormals_array[0]); - + frame1=frame1%g1->numposes; + frame2=frame2%g1->numposes; } else { + frame1=(frame1>g1->numposes-1)?g1->numposes-1:frame1; + frame2=(frame2>g1->numposes-1)?g1->numposes-1:frame2; + } + + if (frame1 == frame2 || r_noframegrouplerp.ival) + mlerp = 0; + lerps->frac[l] = (1-mlerp)*(1-lerpfrac); + if (lerps->frac[l]>0) + lerps->pose[l++] = g1->boneofs + numbones*12*frame1; + lerps->frac[l] = (mlerp)*(1-lerpfrac); + if (lerps->frac[l]>0) + lerps->pose[l++] = g1->boneofs + numbones*12*frame2; + + if (lerpfrac) + { + if (fg2time < 0) + fg2time = 0; + mlerp = (fg2time)*g2->rate; + frame1=mlerp; + frame2=frame1+1; + mlerp-=frame1; + if (g2->loop) + { + frame1=frame1%g2->numposes; + frame2=frame2%g2->numposes; + } + else + { + frame1=(frame1>g2->numposes-1)?g2->numposes-1:frame1; + frame2=(frame2>g2->numposes-1)?g2->numposes-1:frame2; + } + if (frame1 == frame2 || r_noframegrouplerp.ival) + mlerp = 0; + lerps->frac[l] = (1-mlerp)*(lerpfrac); + if (lerps->frac[l]>0) + lerps->pose[l++] = g2->boneofs + numbones*12*frame1; + lerps->frac[l] = (mlerp)*(lerpfrac); + if (lerps->frac[l]>0) + lerps->pose[l++] = g2->boneofs + numbones*12*frame2; + } + + lerps->lerpcount = l; +} +/* +finds the various blend info. returns number of bone blocks used. +*/ +static int Alias_FindRawSkelData(galiasinfo_t *inf, framestate_t *fstate, skellerps_t *lerps, size_t firstbone, size_t lastbone) +{ + galiasgroup_t *g1, *g2; + + int frame1, frame2; + float f1time, f2time; + float f2ness; + + int bonegroup; + int cbone = 0; + int endbone; + int numbonegroups=0; + + if (lastbone > inf->numbones) + lastbone = inf->numbones; + + for (bonegroup = 0; bonegroup < FS_COUNT; bonegroup++) + { + endbone = fstate->g[bonegroup].endbone; + if (bonegroup == FS_COUNT-1 || endbone > lastbone) + endbone = lastbone; + + if (endbone == cbone) + continue; + + frame1 = fstate->g[bonegroup].frame[0]; + frame2 = fstate->g[bonegroup].frame[1]; + f1time = fstate->g[bonegroup].frametime[0]; + f2time = fstate->g[bonegroup].frametime[1]; + f2ness = fstate->g[bonegroup].lerpfrac; + + if (!inf->groups) //if there's no animations in this model, use the base pose instead. + { + if (!inf->baseframeofs) + continue; //nope, not happening. + lerps->skeltype = SKEL_ABSOLUTE; + lerps->frac[0] = 1; + lerps->pose[0] = inf->baseframeofs; + lerps->lerpcount = 1; + } + else + { + if (frame1 < 0) + { + if (frame2 < 0) + { + if (bonegroup != FS_COUNT-1) + continue; //just ignore this group + frame2 = 0; + } + frame1 = frame2; + } + else if (frame2 < 0) + frame2 = frame1; + if (frame1 >= inf->groups) + frame1 %= inf->groups; + if (frame2 >= inf->groups) + frame2 %= inf->groups; + + //the higher level merges old/new anims, but we still need to blend between automated frame-groups. + g1 = &inf->groupofs[frame1]; + g2 = &inf->groupofs[frame2]; + + if (g2->skeltype != g1->skeltype) + g2 = g1; + lerps->skeltype = g1->skeltype; + Alias_BuildSkelLerps(lerps, inf->numbones, g1, g2, f2ness, f1time, f2time); + } + lerps->firstbone = cbone; + lerps->endbone = endbone; + cbone = endbone; + numbonegroups++; + } + return numbonegroups; +} +/* + retrieves the raw bone data for a current frame state. + ignores poses that don't match the desired skeltype + ignores skeletal objects. + return value is the lastbone argument, or less if the model simply doesn't have that many bones. + _always_ writes into result +*/ +int Alias_BlendBoneData(galiasinfo_t *inf, framestate_t *fstate, float *result, skeltype_t skeltype, int firstbone, int lastbone) +{ + skellerps_t lerps[FS_COUNT], *lerp; + size_t bone, endbone = 0; + size_t numgroups = Alias_FindRawSkelData(inf, fstate, lerps, 0, inf->numbones); + + float *pose, *matrix; + int k, b; + + for (lerp = lerps; numgroups--; lerp++) + { + if (lerp[0].skeltype != skeltype) + continue; //egads, its buggy. should probably convert. + + bone = lerp->firstbone; + endbone = lerp->endbone; + if (lerp->lerpcount == 1 && lerp->frac[0] == 1) + memcpy(result+bone*12, lerp->pose[0]+bone*12, (endbone-bone)*12*sizeof(float)); + else + { + //set up the identity matrix + for (; bone < endbone; bone++) + { + pose = result + 12*bone; + //set up the per-bone transform matrix + matrix = lerps->pose[0] + bone*12; + for (k = 0;k < 12;k++) + pose[k] = matrix[k] * lerp->frac[0]; + for (b = 1;b < lerp->lerpcount;b++) + { + matrix = lerps->pose[b] + bone*12; + + for (k = 0;k < 12;k++) + pose[k] += matrix[k] * lerp->frac[b]; + } + } + } + } + return endbone; +} + +/*retrieves the bone data. +only writes targetbuffer if needed. the return value is the only real buffer result. +assumes that all blended types are the same. probably buggy, but meh. +*/ +const float *Alias_GetBoneInformation(galiasinfo_t *inf, framestate_t *framestate, skeltype_t targettype, float *targetbuffer, float *targetbufferalt, size_t maxbufferbones) +{ + skellerps_t lerps[FS_COUNT], *lerp; + size_t numgroups; + size_t bone, endbone; + +#ifdef SKELETALOBJECTS + if (framestate->bonestate && framestate->bonecount >= inf->numbones) + { + lerps[0].skeltype = framestate->skeltype; + lerps[0].firstbone = 0; + lerps[0].endbone = framestate->bonecount; + lerps[0].pose[0] = framestate->bonestate; + lerps[0].frac[0] = 1; + lerps[0].lerpcount = 1; + numgroups = 1; + } + else +#endif + { + numgroups = Alias_FindRawSkelData(inf, framestate, lerps, 0, inf->numbones); + } + + //try to return data in-place. + if (numgroups==1 && lerps[0].lerpcount == 1) + return Alias_ConvertBoneData(lerps[0].skeltype, lerps[0].pose[0], lerps[0].endbone, inf->ofsbones, targettype, targetbuffer, targetbufferalt, maxbufferbones); + + for (lerp = lerps; numgroups--; lerp++) + { + bone = lerp->firstbone; + 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; + float *out = targetbuffer + bone*12; + float *pose1 = lerp->pose[0] + bone*12, *pose2 = lerp->pose[1] + bone*12; + float frac1 = lerp->frac[0], frac2 = lerp->frac[1]; + for (; bone < endbone; bone++, out+=12, pose1+=12, pose2+=12) + { + for (k = 0; k < 12; k++) //please please unroll! + out[k] = pose1[k]*frac1 + frac2*pose2[k]; + } + } + break; + case 3: + { + int k; + float *out = targetbuffer + bone*12; + float *pose1 = lerp->pose[0] + bone*12, *pose2 = lerp->pose[1] + bone*12, *pose3 = lerp->pose[2] + bone*12; + float frac1 = lerp->frac[0], frac2 = lerp->frac[1], frac3 = lerp->frac[2]; + for (; bone < endbone; bone++) + { + for (k = 0; k < 12; k++) //please please unroll! + out[k] = pose1[k]*frac1 + frac2*pose2[k] + pose3[k]*frac3; + } + } + break; + case 4: + { + int k; + float *out = targetbuffer + bone*12; + float *pose1 = lerp->pose[0] + bone*12, *pose2 = lerp->pose[1] + bone*12, *pose3 = lerp->pose[2] + bone*12, *pose4 = lerp->pose[3] + bone*12; + float frac1 = lerp->frac[0], frac2 = lerp->frac[1], frac3 = lerp->frac[2], frac4 = lerp->frac[3]; + for (; bone < endbone; bone++) + { + for (k = 0; k < 12; k++) //please please unroll! + out[k] = pose1[k]*frac1 + frac2*pose2[k] + pose3[k]*frac3 + frac4*pose4[k]; + } + } + break; + } + } + + return Alias_ConvertBoneData(lerps[0].skeltype, targetbuffer, inf->numbones, inf->ofsbones, targettype, targetbuffer, targetbufferalt, maxbufferbones); +} + +static void Alias_BuildSkeletalMesh(mesh_t *mesh, framestate_t *framestate, galiasinfo_t *inf) +{ + if (inf->ofs_skel_idx) + { + qbyte *fte_restrict bidx = inf->ofs_skel_idx[0]; + float *fte_restrict weight = inf->ofs_skel_weight[0]; + + if (meshcache.bonecachetype != SKEL_INVERSE_ABSOLUTE) + meshcache.usebonepose = Alias_GetBoneInformation(inf, framestate, meshcache.bonecachetype=SKEL_INVERSE_ABSOLUTE, meshcache.boneposebuffer1, meshcache.boneposebuffer2, MAX_BONES); + + if (1) + Alias_TransformVerticies_VNST(meshcache.usebonepose, inf->numverts, bidx, weight, + inf->ofs_skel_xyz[0], mesh->xyz_array[0], + inf->ofs_skel_norm[0], mesh->normals_array[0], + inf->ofs_skel_svect[0], mesh->snormals_array[0], + inf->ofs_skel_tvect[0], mesh->tnormals_array[0] + ); + else + Alias_TransformVerticies_VN(meshcache.usebonepose, inf->numverts, bidx, weight, + inf->ofs_skel_xyz[0], mesh->xyz_array[0], + inf->ofs_skel_norm[0], mesh->normals_array[0] + ); + } + else + { + galisskeletaltransforms_t *weights = inf->ofsswtransforms; + int numweights = inf->numswtransforms; + if (meshcache.bonecachetype != SKEL_ABSOLUTE) + meshcache.usebonepose = Alias_GetBoneInformation(inf, framestate, meshcache.bonecachetype=SKEL_ABSOLUTE, meshcache.boneposebuffer1, meshcache.boneposebuffer2, MAX_BONES); memset(mesh->xyz_array, 0, mesh->numvertexes*sizeof(vecV_t)); memset(mesh->normals_array, 0, mesh->numvertexes*sizeof(vec3_t)); - Alias_TransformVerticies_SW(bonepose, weights, numweights, mesh->xyz_array, mesh->normals_array); + Alias_TransformVerticies_SW(meshcache.usebonepose, weights, numweights, mesh->xyz_array, mesh->normals_array); } } +static void Alias_BuildSkeletalVPositionsPose(float *xyzout, skeltype_t bonetype, const float *bonepose, galiasinfo_t *inf) +{ + float buffer[MAX_BONES*12]; + float bufferalt[MAX_BONES*12]; + if (inf->ofs_skel_idx) + { + qbyte *fte_restrict bidx = inf->ofs_skel_idx[0]; + float *fte_restrict xyzin = inf->ofs_skel_xyz[0]; + float *fte_restrict weight = inf->ofs_skel_weight[0]; + bonepose = Alias_ConvertBoneData(bonetype, bonepose, inf->numbones, inf->ofsbones, SKEL_INVERSE_ABSOLUTE, buffer, bufferalt, MAX_BONES); + + Alias_TransformVerticies_V(bonepose, inf->numverts, bidx, weight, xyzin, xyzout); + } + else + { + galisskeletaltransforms_t *weights = inf->ofsswtransforms; + int numweights = inf->numswtransforms; + bonepose = Alias_ConvertBoneData(bonetype, bonepose, inf->numbones, inf->ofsbones, SKEL_ABSOLUTE, buffer, bufferalt, MAX_BONES); + Alias_TransformVerticies_SW(bonepose, weights, numweights, (vecV_t*)xyzout, NULL); + } +} + +#ifndef SERVERONLY #ifdef GLQUAKE #include "glquake.h" static void Alias_GLDrawSkeletalBones(galiasbone_t *bones, float *bonepose, int bonecount) @@ -1469,11 +1529,6 @@ void Alias_FlushCache(void) void Alias_Shutdown(void) { - if (meshcache.colours) - BZ_Free(meshcache.colours); - meshcache.colours = NULL; - meshcache.numcolours = 0; - if (meshcache.norm) BZ_Free(meshcache.norm); meshcache.norm = NULL; @@ -1510,13 +1565,6 @@ qboolean Alias_GAliasBuildMesh(mesh_t *mesh, vbo_t **vbop, galiasinfo_t *inf, in } } - if (meshcache.numcolours < inf->numverts) - { - if (meshcache.colours) - BZ_Free(meshcache.colours); - meshcache.colours = BZ_Malloc(sizeof(*meshcache.colours)*inf->numverts); - meshcache.numcolours = inf->numverts; - } if (meshcache.numnorm < inf->numverts) { if (meshcache.norm) @@ -1536,37 +1584,57 @@ qboolean Alias_GAliasBuildMesh(mesh_t *mesh, vbo_t **vbop, galiasinfo_t *inf, in mesh->indexes = inf->ofs_indexes; mesh->numindexes = inf->numindexes; +#ifndef SERVERONLY + mesh->colors4f_array[0] = inf->ofs_rgbaf; + mesh->colors4b_array = inf->ofs_rgbaub; mesh->st_array = inf->ofs_st_array; - mesh->trneighbors = inf->ofs_trineighbours; - mesh->colors4f_array[0] = meshcache.colours; - - if (meshcache.surfnum == inf->shares_verts && meshcache.ent == e) - { - mesh->xyz_array = meshcache.acoords1; - mesh->xyz2_array = meshcache.acoords2; - mesh->normals_array = meshcache.anorm; - mesh->snormals_array = meshcache.anorms; - mesh->tnormals_array = meshcache.anormt; - if (vbop) - *vbop = meshcache.vbop; - -#ifdef SKELETALMODELS - if (meshcache.usebonepose) - { - mesh->bonenums = inf->ofs_skel_idx; - mesh->boneweights = inf->ofs_skel_weight; - mesh->bones = meshcache.usebonepose; - mesh->numbones = inf->numbones; - } #endif - return false; //don't generate the new vertex positions. We still have them all. + mesh->trneighbors = inf->ofs_trineighbours; + + if (meshcache.ent == e) + { + if (meshcache.vertgroup == inf->shares_verts && meshcache.ent == e) + { + mesh->xyz_array = meshcache.acoords1; + mesh->xyz2_array = meshcache.acoords2; + mesh->normals_array = meshcache.anorm; + mesh->snormals_array = meshcache.anorms; + mesh->tnormals_array = meshcache.anormt; + if (vbop) + *vbop = meshcache.vbop; + +#ifndef SKELETALMODELS + return false; + } } - meshcache.surfnum = inf->shares_verts; +#else + if (usebones && meshcache.bonecachetype != -1) + { + mesh->bonenums = inf->ofs_skel_idx; + mesh->boneweights = inf->ofs_skel_weight; + mesh->bones = meshcache.usebonepose; + mesh->numbones = inf->numbones; + } + return false; //don't generate the new vertex positions. We still have them all. + } + if (meshcache.bonegroup != inf->shares_bones) + { + meshcache.usebonepose = NULL; + meshcache.bonecachetype = -1; + } + } + else + { + meshcache.usebonepose = NULL; + meshcache.bonecachetype = -1; + } + meshcache.bonegroup = inf->shares_bones; +#endif + meshcache.vertgroup = inf->shares_verts; meshcache.ent = e; #ifndef SERVERONLY - mesh->st_array = inf->ofs_st_array; mesh->trneighbors = inf->ofs_trineighbours; mesh->normals_array = meshcache.norm; mesh->snormals_array = meshcache.norm+meshcache.numnorm; @@ -1578,7 +1646,6 @@ qboolean Alias_GAliasBuildMesh(mesh_t *mesh, vbo_t **vbop, galiasinfo_t *inf, in //we don't support meshes with one group skeletal and annother not. #ifdef SKELETALMODELS - meshcache.usebonepose = NULL; meshcache.vbop = NULL; if (vbop) *vbop = NULL; @@ -1603,6 +1670,7 @@ qboolean Alias_GAliasBuildMesh(mesh_t *mesh, vbo_t **vbop, galiasinfo_t *inf, in meshcache.vbo.normals = inf->vbo_skel_normals; meshcache.vbo.svector = inf->vbo_skel_svector; meshcache.vbo.tvector = inf->vbo_skel_tvector; + meshcache.vbo.colours[0] = inf->vborgba; meshcache.vbo.bonenums = inf->vbo_skel_bonenum; meshcache.vbo.boneweights = inf->vbo_skel_bweight; if (meshcache.vbo.indicies.dummy) @@ -1611,38 +1679,46 @@ qboolean Alias_GAliasBuildMesh(mesh_t *mesh, vbo_t **vbop, galiasinfo_t *inf, in } else if (inf->numbones) { - mesh->xyz2_array = NULL; - meshcache.usebonepose = Alias_GetBonePositions(inf, &e->framestate, meshcache.bonepose, MAX_BONES, true); + mesh->xyz2_array = NULL; //skeltal animations blend bones, not verticies. - if (e->fatness || !inf->ofs_skel_idx || !usebones || inf->numswtransforms) + if (e->fatness || !inf->ofs_skel_idx || !usebones || inf->numswtransforms || inf->numbones > MAX_GPU_BONES) { - //software bone animation - //there are two ways to animate a skeleton, one is to transform - Alias_BuildSkeletalMesh(mesh, meshcache.usebonepose, inf); + usebones = false; + if (inf->numindexes) + { + //software bone animation + //there are two ways to animate a skeleton + Alias_BuildSkeletalMesh(mesh, &e->framestate, inf); #ifdef PEXT_FATNESS - if (e->fatness) - { - int i; - for (i = 0; i < mesh->numvertexes; i++) + if (e->fatness) { - VectorMA(mesh->xyz_array[i], e->fatness, mesh->normals_array[i], meshcache.coords[i]); + int i; + for (i = 0; i < mesh->numvertexes; i++) + { + VectorMA(mesh->xyz_array[i], e->fatness, mesh->normals_array[i], meshcache.coords[i]); + } + mesh->xyz_array = meshcache.coords; } - - mesh->xyz_array = meshcache.coords; - } #endif - -#ifdef GLQUAKE - if (!inf->numswtransforms && qrenderer == QR_OPENGL) + } + else { - Alias_GLDrawSkeletalBones(inf->ofsbones, (float *)meshcache.usebonepose, inf->numbones); - } +#ifdef GLQUAKE + if (meshcache.bonecachetype != SKEL_ABSOLUTE) + meshcache.usebonepose = Alias_GetBoneInformation(inf, &e->framestate, meshcache.bonecachetype=SKEL_ABSOLUTE, meshcache.boneposebuffer1, meshcache.boneposebuffer2, MAX_BONES); + if (qrenderer == QR_OPENGL) + { + Alias_GLDrawSkeletalBones(inf->ofsbones, (float *)meshcache.usebonepose, inf->numbones); + } #endif - meshcache.usebonepose = NULL; + } } else { + if (meshcache.bonecachetype != SKEL_INVERSE_ABSOLUTE) + meshcache.usebonepose = Alias_GetBoneInformation(inf, &e->framestate, meshcache.bonecachetype=SKEL_INVERSE_ABSOLUTE, meshcache.boneposebuffer1, meshcache.boneposebuffer2, MAX_BONES); + //hardware bone animation mesh->xyz_array = inf->ofs_skel_xyz; mesh->normals_array = inf->ofs_skel_norm; @@ -1677,7 +1753,7 @@ qboolean Alias_GAliasBuildMesh(mesh_t *mesh, vbo_t **vbop, galiasinfo_t *inf, in if (frame2 >= inf->groups) { Con_DPrintf("Too high frame %i (%s)\n", frame2, e->model->name); - frame2 = frame1; + frame2 %= inf->groups; } if (lerp <= 0) @@ -1728,17 +1804,23 @@ qboolean Alias_GAliasBuildMesh(mesh_t *mesh, vbo_t **vbop, galiasinfo_t *inf, in galiaspose_t *p1 = &g1->poseofs[frame1]; galiaspose_t *p2 = &g2->poseofs[frame2]; - mesh->normals_array = p1->ofsnormals; - mesh->snormals_array = p1->ofssvector; - mesh->tnormals_array = p1->ofstvector; - meshcache.vbo.indicies = inf->vboindicies; meshcache.vbo.indexcount = inf->numindexes; meshcache.vbo.vertcount = inf->numverts; meshcache.vbo.texcoord = inf->vbotexcoords; + +#ifdef SERVERONLY + mesh->xyz_array = p1->ofsverts; + mesh->xyz2_array = NULL; +#else + mesh->normals_array = p1->ofsnormals; + mesh->snormals_array = p1->ofssvector; + mesh->tnormals_array = p1->ofstvector; + meshcache.vbo.normals = p1->vbonormals; meshcache.vbo.svector = p1->vbosvector; meshcache.vbo.tvector = p1->vbotvector; + memset(&meshcache.vbo.colours[0], 0, sizeof(meshcache.vbo.colours[0])); if (p1 == p2 || r_nolerp.ival) { @@ -1756,7 +1838,7 @@ qboolean Alias_GAliasBuildMesh(mesh_t *mesh, vbo_t **vbop, galiasinfo_t *inf, in mesh->xyz_array = p1->ofsverts; mesh->xyz2_array = p2->ofsverts; } - +#endif if (vbop && meshcache.vbo.indicies.dummy) *vbop = meshcache.vbop = &meshcache.vbo; } @@ -1771,7 +1853,7 @@ qboolean Alias_GAliasBuildMesh(mesh_t *mesh, vbo_t **vbop, galiasinfo_t *inf, in meshcache.vbop = *vbop; #ifdef SKELETALMODELS - if (meshcache.usebonepose) + if (usebones) { mesh->bonenums = inf->ofs_skel_idx; mesh->boneweights = inf->ofs_skel_weight; @@ -1783,8 +1865,6 @@ qboolean Alias_GAliasBuildMesh(mesh_t *mesh, vbo_t **vbop, galiasinfo_t *inf, in return true; //to allow the mesh to be dlighted. } -#endif - @@ -1888,7 +1968,6 @@ qboolean Mod_Trace(model_t *model, int forcehullnum, int frame, vec3_t axis[3], galiasgroup_t *group; galiaspose_t *pose; - float frac; // float temp; vecV_t *posedata; @@ -1931,17 +2010,8 @@ qboolean Mod_Trace(model_t *model, int forcehullnum, int frame, vec3_t axis[3], #ifdef SKELETALMODELS if (mod->numbones && mod->shares_verts != cursurfnum) { - float bonepose[MAX_BONES][12]; posedata = alloca(mod->numverts*sizeof(vecV_t)); - frac = 1; - if (group->isheirachical) - { - if (mod->shares_bones != cursurfnum) - R_LerpBones(&frac, (float**)posedata, 1, mod->ofsbones, mod->numbones, bonepose); - Alias_TransformVerticies_SW((float*)bonepose, mod->ofsswtransforms, mod->numswtransforms, posedata, NULL); - } - else - Alias_TransformVerticies_SW((float*)posedata, mod->ofsswtransforms, mod->numswtransforms, posedata, NULL); + Alias_BuildSkeletalVPositionsPose((float*)posedata, group->skeltype, group->boneofs, mod); cursurfnum = mod->shares_verts; } @@ -2098,7 +2168,8 @@ static frameinfo_t *ParseFrameInfo(char *modelname, int *numgroups) { int count = 0; int maxcount = 0; - char *line, *file; + const char *line; + char *file; frameinfo_t *frames = NULL; line = file = FS_LoadMallocFile(va("%s.framegroups", modelname)); if (!file) @@ -2191,7 +2262,7 @@ void Mod_BuildTextureVectors(galiasinfo_t *galias) #endif } -#if defined(D3DQUAKE) || defined(GLQUAKE) +#ifndef SERVERONLY /* ================= Mod_FloodFillSkin @@ -2267,14 +2338,14 @@ static void Mod_FloodFillSkin( qbyte *skin, int skinwidth, int skinheight ) } #endif -//additional skin loading -char ** skinfilelist; +skinid_t *skinfilelist; int skinfilecount; static qboolean VARGS Mod_TryAddSkin(qboolean force, const char *skinname, ...) { va_list argptr; char string[MAX_QPATH]; + char *filedata; //make sure we don't add it twice int i; @@ -2287,17 +2358,17 @@ static qboolean VARGS Mod_TryAddSkin(qboolean force, const char *skinname, ...) for (i = 0; i < skinfilecount; i++) { - if (!strcmp(skinfilelist[i], string)) + if (!strcmp(Mod_LookupSkin(skinfilelist[i])->skinname, string)) return true; //already added } - if (!force && !COM_FCheckExists(string)) + filedata = FS_LoadMallocFile(string); + if (!filedata && !force) return false; skinfilelist = BZ_Realloc(skinfilelist, sizeof(*skinfilelist)*(skinfilecount+1)); - skinfilelist[skinfilecount] = Z_Malloc(strlen(string)+1); - strcpy(skinfilelist[skinfilecount], string); - skinfilecount++; + skinfilelist[skinfilecount++] = Mod_ReadSkinFile(string, filedata); + Z_Free(filedata); return true; } @@ -2307,6 +2378,7 @@ int QDECL Mod_EnumerateSkins(const char *name, qofs_t size, void *param, searchp return true; } +//looks for foo.md3_0.skin files, for dp compat int Mod_BuildSkinFileList(qboolean forcedefault, char *modelname) { int i; @@ -2315,8 +2387,8 @@ int Mod_BuildSkinFileList(qboolean forcedefault, char *modelname) //flush the old list for (i = 0; i < skinfilecount; i++) { - Z_Free(skinfilelist[i]); - skinfilelist[i] = NULL; + Mod_WipeSkin(skinfilelist[i]); + skinfilelist[i] = ~0u; } skinfilecount=0; @@ -2327,6 +2399,7 @@ int Mod_BuildSkinFileList(qboolean forcedefault, char *modelname) { if (!Mod_TryAddSkin(false, "%s_%i.skin", modelname, i)) { + /*FIXME: only use this logic in q1, and not q3. if (i == 0) { if (!Mod_TryAddSkin(forcedefault, "%s_default.skin", skinfilename, i)) @@ -2352,110 +2425,64 @@ int Mod_BuildSkinFileList(qboolean forcedefault, char *modelname) if (!Mod_TryAddSkin(false, "%s_yellow.skin", skinfilename, i)) break; } - else + else*/ break; } } -// if (strstr(modelname, "lower") || strstr(modelname, "upper") || strstr(modelname, "head")) -// { - COM_EnumerateFiles(va("%s_*.skin", modelname), Mod_EnumerateSkins, NULL); - COM_EnumerateFiles(va("%s_*.skin", skinfilename), Mod_EnumerateSkins, NULL); -// } -// else -// COM_EnumerateFiles("*.skin", Mod_EnumerateSkins, NULL); + COM_EnumerateFiles(va("%s_*.skin", modelname), Mod_EnumerateSkins, NULL); +// COM_EnumerateFiles(va("%s_*.skin", skinfilename), Mod_EnumerateSkins, NULL); return skinfilecount; } -//This is a hack. It uses an assuption about q3 player models. -void Mod_ParseQ3SkinFile(char *out, char *surfname, char *modelname, int skinnum, char *skinfilename) +//support for foo.md3_0.skin +shader_t *Mod_ShaderFromQ3SkinFile(char *out, galiasinfo_t *surf, char *modelname, int skinnum, char *skinfilename) { - const char *f = NULL, *p; - int len; + skinfile_t *skinfile; + int i; - if (skinnum >= skinfilecount) - return; + if (qrenderer == QR_NONE) + return NULL; - if (skinfilename) - strcpy(skinfilename, skinfilelist[skinnum]); - - f = COM_LoadTempMoreFile(skinfilelist[skinnum]); - - while(f) + if (skinnum < skinfilecount) { - f = COM_ParseToken(f,NULL); - if (!f) - return; - if (!strcmp(com_token, "replace")) + skinfile = Mod_LookupSkin(skinfilelist[skinnum]); + + if (skinfilename) + strcpy(skinfilename, skinfile->skinname); + + //check if this skinfile has a mapping. + for (i = 0; i < skinfile->nummappings; i++) { - f = COM_ParseToken(f, NULL); - - len = strlen(com_token); - - //copy surfname -> out, until we meet the part we need to replace - while(*surfname) + if (!strcmp(surf->surfacename, skinfile->mappings[i].surface)) { - if (!strncmp(com_token, surfname, len)) - //found it - { - surfname+=len; - f = COM_ParseToken(f, NULL); - p = com_token; - while(*p) //copy the replacement - *out++ = *p++; - - while(*surfname) //copy the remaining - *out++ = *surfname++; - *out++ = '\0'; //we didn't find it. - return; - } - *out++ = *surfname++; - } - *out++ = '\0'; //we didn't find it. - return; - } - else - { - while(*f == ' ' || *f == '\t') - f++; - if (*f == ',') - { - if (!strcmp(com_token, surfname)) - { - f++; - COM_ParseToken(f, NULL); - strcpy(out, com_token); - return; - } + skinfile->mappings[i].shader->uses++; //so it doesn't blow up when the skin gets freed. + return skinfile->mappings[i].shader; } } - - p = strchr(f, '\n'); - if (!p) - f = f+strlen(f); - else - f = p+1; - if (!*f) - break; } + + return NULL; } -#if defined(D3DQUAKE) || defined(GLQUAKE) -shader_t *Mod_LoadSkinFile(char *defaultshadername, char *surfacename, int skinnumber, unsigned char *rawdata, int width, int height, unsigned char *palette) +#ifndef SERVERONLY +shader_t *Mod_LoadSkinFile(char *defaultshadername, galiasinfo_t *surf, int skinnumber, unsigned char *rawdata, int width, int height, unsigned char *palette, char *outskinname) { shader_t *shader; char shadername[MAX_QPATH]; - Q_strncpyz(shadername, defaultshadername?defaultshadername:surfacename, sizeof(shadername)); + Q_strncpyz(shadername, defaultshadername?defaultshadername:surf->surfacename, sizeof(shadername)); - Mod_ParseQ3SkinFile(shadername, surfacename, loadmodel->name, skinnumber, NULL); - - shader = R_RegisterSkin(shadername, loadmodel->name); - - R_BuildDefaultTexnums(&shader->defaulttextures, shader); - if (shader->flags & SHADER_NOIMAGE) - Con_Printf("Unable to load texture for shader \"%s\" for model \"%s\"\n", shader->name, loadmodel->name); + shader = Mod_ShaderFromQ3SkinFile(shadername, surf, loadmodel->name, skinnumber, outskinname); + if (!shader) + shader = R_RegisterSkin(surf->surfacename, loadmodel->name); + if (shader) + { + R_BuildDefaultTexnums(&shader->defaulttextures, shader); + if (shader->flags & SHADER_NOIMAGE) + Con_Printf("Unable to load texture for shader \"%s\" for model \"%s\"\n", shader->name, loadmodel->name); + } return shader; } @@ -2682,7 +2709,7 @@ static void *Q1_LoadSkins_SV (daliasskintype_t *pskintype, unsigned int skintran return pskintype; } -#if defined(GLQUAKE) || defined(D3DQUAKE) +#ifndef SERVERONLY static void *Q1_LoadSkins_GL (daliasskintype_t *pskintype, unsigned int skintranstype) { shader_t **shaders; @@ -2712,33 +2739,33 @@ static void *Q1_LoadSkins_GL (daliasskintype_t *pskintype, unsigned int skintran //LH's naming scheme ("models" is likly to be ignored) fbtexture = r_nulltex; bumptexture = r_nulltex; - snprintf(skinname, sizeof(skinname), "%s_%i", loadmodel->name, i); + snprintf(skinname, sizeof(skinname), "%s_%i.", loadmodel->name, i); texture = R_LoadReplacementTexture(skinname, "models", IF_NOALPHA); if (TEXVALID(texture)) { if (TEXVALID(texture) && r_fb_models.ival) { - snprintf(skinname, sizeof(skinname), "%s_%i_luma", loadmodel->name, i); + snprintf(skinname, sizeof(skinname), "%s_%i_luma.", loadmodel->name, i); fbtexture = R_LoadReplacementTexture(skinname, "models", 0); } if (r_loadbumpmapping) { - snprintf(skinname, sizeof(skinname), "%s_%i_bump", loadmodel->name, i); + snprintf(skinname, sizeof(skinname), "%s_%i_bump.", loadmodel->name, i); bumptexture = R_LoadBumpmapTexture(skinname, "models"); } } else { - snprintf(skinname, sizeof(skinname), "%s_%i", loadname, i); + snprintf(skinname, sizeof(skinname), "%s_%i.", loadname, i); texture = R_LoadReplacementTexture(skinname, "models", IF_NOALPHA); if (TEXVALID(texture) && r_fb_models.ival) { - snprintf(skinname, sizeof(skinname), "%s_%i_luma", loadname, i); + snprintf(skinname, sizeof(skinname), "%s_%i_luma.", loadname, i); fbtexture = R_LoadReplacementTexture(skinname, "models", 0); } if (TEXVALID(texture) && r_loadbumpmapping) { - snprintf(skinname, sizeof(skinname), "%s_%i_bump", loadname, i); + snprintf(skinname, sizeof(skinname), "%s_%i_bump.", loadname, i); bumptexture = R_LoadBumpmapTexture(skinname, "models"); } } @@ -2758,19 +2785,19 @@ static void *Q1_LoadSkins_GL (daliasskintype_t *pskintype, unsigned int skintran //the extra underscore is to stop replacement matches if (!TEXVALID(texture)) { - snprintf(skinname, sizeof(skinname), "%s__%i", loadname, i); + snprintf(skinname, sizeof(skinname), "%s__%i.", loadname, i); switch (skintranstype) { default: texture = R_LoadTexture(skinname,outskin->skinwidth,outskin->skinheight, TF_SOLID8, saved, IF_NOALPHA|IF_NOGAMMA); if (r_fb_models.ival) { - snprintf(skinname, sizeof(skinname), "%s__%i_luma", loadname, i); + snprintf(skinname, sizeof(skinname), "%s__%i_luma.", loadname, i); fbtexture = R_LoadTextureFB(skinname, outskin->skinwidth, outskin->skinheight, saved, IF_NOGAMMA); } if (r_loadbumpmapping) { - snprintf(skinname, sizeof(skinname), "%s__%i_bump", loadname, i); + snprintf(skinname, sizeof(skinname), "%s__%i_bump.", loadname, i); bumptexture = R_LoadTexture8BumpPal(skinname, outskin->skinwidth, outskin->skinheight, saved, IF_NOGAMMA); } break; @@ -2794,7 +2821,7 @@ static void *Q1_LoadSkins_GL (daliasskintype_t *pskintype, unsigned int skintran - Q_snprintfz(skinname, sizeof(skinname), "%s_%i", loadname, i); + Q_snprintfz(skinname, sizeof(skinname), "%s_%i.", loadname, i); if (skintranstype == 4) shaders[0] = R_RegisterShader(skinname, SUF_NONE, "{\n" @@ -2836,10 +2863,10 @@ static void *Q1_LoadSkins_GL (daliasskintype_t *pskintype, unsigned int skintran //13/4/08 IMPLEMENTME if (r_skin_overlays.ival) { - snprintf(skinname, sizeof(skinname), "%s_%i_pants", loadname, i); + snprintf(skinname, sizeof(skinname), "%s_%i_pants.", loadname, i); shaders[0]->defaulttextures.loweroverlay = R_LoadReplacementTexture(skinname, "models", 0); - snprintf(skinname, sizeof(skinname), "%s_%i_shirt", loadname, i); + snprintf(skinname, sizeof(skinname), "%s_%i_shirt.", loadname, i); shaders[0]->defaulttextures.upperoverlay = R_LoadReplacementTexture(skinname, "models", 0); } @@ -2872,24 +2899,24 @@ static void *Q1_LoadSkins_GL (daliasskintype_t *pskintype, unsigned int skintran //LH naming scheme if (!TEXVALID(texture)) { - Q_snprintfz(skinname, sizeof(skinname), "%s_%i_%i", loadmodel->name, i, t); + Q_snprintfz(skinname, sizeof(skinname), "%s_%i_%i.", loadmodel->name, i, t); texture = R_LoadReplacementTexture(skinname, "models", IF_NOALPHA); } if (!TEXVALID(fbtexture) && r_fb_models.ival) { - Q_snprintfz(skinname, sizeof(skinname), "%s_%i_%i_luma", loadmodel->name, i, t); + Q_snprintfz(skinname, sizeof(skinname), "%s_%i_%i_luma.", loadmodel->name, i, t); fbtexture = R_LoadReplacementTexture(skinname, "models", 0); } //Fuhquake naming scheme if (!TEXVALID(texture)) { - Q_snprintfz(skinname, sizeof(skinname), "%s_%i_%i", loadname, i, t); + Q_snprintfz(skinname, sizeof(skinname), "%s_%i_%i.", loadname, i, t); texture = R_LoadReplacementTexture(skinname, "models", IF_NOALPHA); } if (!TEXVALID(fbtexture) && r_fb_models.ival) { - Q_snprintfz(skinname, sizeof(skinname), "%s_%i_%i_luma", loadname, i, t); + Q_snprintfz(skinname, sizeof(skinname), "%s_%i_%i_luma.", loadname, i, t); fbtexture = R_LoadReplacementTexture(skinname, "models", 0); } @@ -2901,19 +2928,19 @@ static void *Q1_LoadSkins_GL (daliasskintype_t *pskintype, unsigned int skintran Mod_FloodFillSkin(saved, outskin->skinwidth, outskin->skinheight); if (!TEXVALID(texture)) { - Q_snprintfz(skinname, sizeof(skinname), "%s_%i_%i", loadname, i, t); + Q_snprintfz(skinname, sizeof(skinname), "%s_%i_%i.", loadname, i, t); texture = R_LoadTexture8(skinname, outskin->skinwidth, outskin->skinheight, saved, (skintranstype?0:IF_NOALPHA)|IF_NOGAMMA, skintranstype); } if (!TEXVALID(fbtexture) && r_fb_models.value) { - Q_snprintfz(skinname, sizeof(skinname), "%s_%i_%i_luma", loadname, i, t); + Q_snprintfz(skinname, sizeof(skinname), "%s_%i_%i_luma.", loadname, i, t); fbtexture = R_LoadTextureFB(skinname, outskin->skinwidth, outskin->skinheight, saved, IF_NOGAMMA); } } - Q_snprintfz(skinname, sizeof(skinname), "%s_%i_%i", loadname, i, t); + Q_snprintfz(skinname, sizeof(skinname), "%s_%i_%i.", loadname, i, t); shaders[t] = R_RegisterSkin(skinname, loadmodel->name); TEXASSIGN(shaders[t]->defaulttextures.base, texture); @@ -2933,7 +2960,7 @@ static void *Q1_LoadSkins_GL (daliasskintype_t *pskintype, unsigned int skintran } #endif -qboolean QDECL Mod_LoadQ1Model (model_t *mod, void *buffer) +qboolean QDECL Mod_LoadQ1Model (model_t *mod, void *buffer, size_t fsize) { #ifndef SERVERONLY vec2_t *st_array; @@ -3325,7 +3352,7 @@ static void Q2_LoadSkins(md2_t *pq2inmodel, char *skins) } #define MD2_MAX_TRIANGLES 4096 -qboolean QDECL Mod_LoadQ2Model (model_t *mod, void *buffer) +qboolean QDECL Mod_LoadQ2Model (model_t *mod, void *buffer, size_t fsize) { #ifndef SERVERONLY dmd2stvert_t *pinstverts; @@ -3599,7 +3626,7 @@ int Mod_GetBoneRelations(model_t *model, int firstbone, int lastbone, framestate return false; inf = Mod_Extradata(model); - return Alias_GetBoneRelations(inf, fstate, result, firstbone, lastbone); + return Alias_BlendBoneData(inf, fstate, result, SKEL_RELATIVE, firstbone, lastbone); #endif return 0; } @@ -3712,8 +3739,8 @@ qboolean Mod_GetTag(model_t *model, int tagnum, framestate_t *fstate, float *res if (tagnum >= fstate->bonecount) return false; - if (fstate->boneabs) - { + if (fstate->skeltype == SKEL_ABSOLUTE) + { //can just directly read it, woo. memcpy(result, fstate->bonestate + 12 * tagnum, 12*sizeof(*result)); return true; } @@ -3877,7 +3904,7 @@ qboolean Mod_GetTag(model_t *model, int tagnum, framestate_t *fstate, float *res return false; } -int Mod_TagNumForName(model_t *model, char *name) +int Mod_TagNumForName(model_t *model, const char *name) { int i; galiasinfo_t *inf; @@ -3915,7 +3942,7 @@ int Mod_TagNumForName(model_t *model, char *name) return 0; } -int Mod_FrameNumForName(model_t *model, char *name) +int Mod_FrameNumForName(model_t *model, const char *name) { galiasgroup_t *group; galiasinfo_t *inf; @@ -3942,7 +3969,7 @@ int Mod_FrameNumForName(model_t *model, char *name) } #ifndef SERVERONLY -int Mod_SkinNumForName(model_t *model, char *name) +int Mod_SkinNumForName(model_t *model, const char *name) { int i; galiasinfo_t *inf; @@ -4116,7 +4143,7 @@ typedef struct { } md3Shader_t; //End of Tenebrae 'assistance' -qboolean QDECL Mod_LoadQ3Model(model_t *mod, void *buffer) +qboolean QDECL Mod_LoadQ3Model(model_t *mod, void *buffer, size_t fsize) { #ifndef SERVERONLY galiasskin_t *skin; @@ -4191,6 +4218,8 @@ qboolean QDECL Mod_LoadQ3Model(model_t *mod, void *buffer) root = galias; parent = galias; + Q_strncpyz(galias->surfacename, surf->name, sizeof(galias->surfacename)); + #ifndef SERVERONLY st_array = ZG_Malloc(&loadmodel->memgroup, sizeof(vec2_t)*galias->numindexes); galias->ofs_st_array = st_array; @@ -4277,9 +4306,9 @@ qboolean QDECL Mod_LoadQ3Model(model_t *mod, void *buffer) { char shadname[1024]; - skin = ZG_Malloc(&loadmodel->memgroup, (LittleLong(surf->numShaders)+externalskins)*((sizeof(galiasskin_t)+sizeof(shader_t*)))); + skin = ZG_Malloc(&loadmodel->memgroup, (externalskins)*((sizeof(galiasskin_t)+sizeof(shader_t*)))); galias->ofsskins = skin; - shaders = (shader_t **)(skin + LittleLong(surf->numShaders)+externalskins); + shaders = (shader_t **)(skin + externalskins); inshader = (md3Shader_t *)((qbyte *)surf + LittleLong(surf->ofsShaders)); for (i = 0; i < externalskins; i++) { @@ -4292,24 +4321,21 @@ qboolean QDECL Mod_LoadQ3Model(model_t *mod, void *buffer) shadname[0] = '\0'; - Mod_ParseQ3SkinFile(shadname, surf->name, loadmodel->name, i, skin->name); + shaders[i] = Mod_ShaderFromQ3SkinFile(shadname, galias, loadmodel->name, i, skin->name); - if (!*shadname) + if (!shaders[i]) { - if (i >= LittleLong(surf->numShaders) || !*inshader->name) - strcpy(shadname, "missingskin"); //this shouldn't be possible + if (i >= LittleLong(surf->numShaders)) + Q_strncpyz(shadname, "", sizeof(shadname)); //this shouldn't be possible else - strcpy(shadname, inshader->name); + Q_strncpyz(shadname, inshader->name, sizeof(shadname)); Q_strncpyz(skin->name, shadname, sizeof(skin->name)); - } - - if (qrenderer != QR_NONE) - { - shaders[i] = R_RegisterSkin(shadname, mod->name); + shaders[i] = R_RegisterSkin(shadname, loadmodel->name); + R_BuildDefaultTexnums(NULL, shaders[i]); - if (shaders[i]->flags & SHADER_NOIMAGE) + if ((shaders[i]->flags & SHADER_NOIMAGE) && *shadname) Con_Printf("Unable to load texture for shader \"%s\" on mesh \"%s\" for model \"%s\"\n", shaders[i]->name, surf->name, loadmodel->name); } @@ -4445,7 +4471,7 @@ typedef struct zymvertex_s //this can generate multiple meshes (one for each shader). //but only one set of transforms are ever generated. -qboolean QDECL Mod_LoadZymoticModel(model_t *mod, void *buffer) +qboolean QDECL Mod_LoadZymoticModel(model_t *mod, void *buffer, size_t fsize) { #ifndef SERVERONLY galiasskin_t *skin; @@ -4619,6 +4645,8 @@ qboolean QDECL Mod_LoadZymoticModel(model_t *mod, void *buffer) root[i].groups = header->numscenes; root[i].groupofs = grp; + Q_strncpyz(root[i].surfacename, surfname, sizeof(root[i].surfacename)); + #ifdef SERVERONLY root[i].numskins = 1; #else @@ -4632,7 +4660,7 @@ qboolean QDECL Mod_LoadZymoticModel(model_t *mod, void *buffer) skin[j].numshaders = 1; //non-sequenced skins. skin[j].ofsshaders = shaders; - shaders[0] = Mod_LoadSkinFile(NULL, surfname, j, NULL, 0, 0, NULL); + shaders[0] = Mod_LoadSkinFile(NULL, &root[i], j, NULL, 0, 0, NULL, skin->name); } root[i].ofsskins = skin; @@ -4644,7 +4672,7 @@ qboolean QDECL Mod_LoadZymoticModel(model_t *mod, void *buffer) { Q_strncpyz(grp->name, inscene->name, sizeof(grp->name)); - grp->isheirachical = 1; + grp->skeltype = SKEL_RELATIVE; grp->rate = BigFloat(inscene->framerate); grp->loop = !(BigLong(inscene->flags) & ZYMSCENEFLAG_NOLOOP); grp->numposes = BigLong(inscene->length); @@ -4792,7 +4820,7 @@ typedef struct pskanimkeys_s } pskanimkeys_t; -qboolean QDECL Mod_LoadPSKModel(model_t *mod, void *buffer) +qboolean QDECL Mod_LoadPSKModel(model_t *mod, void *buffer, size_t fsize) { pskchunk_t *chunk; unsigned int pos = 0; @@ -5222,7 +5250,7 @@ qboolean QDECL Mod_LoadPSKModel(model_t *mod, void *buffer) snprintf(group[j].name, sizeof(group[j].name), "frame_%i", j); group[j].loop = frameinfo[j].loop; group[j].rate = frameinfo[j].fps; - group[j].isheirachical = true; + group[j].skeltype = SKEL_RELATIVE; } num_animinfo = numgroups; } @@ -5245,7 +5273,7 @@ qboolean QDECL Mod_LoadPSKModel(model_t *mod, void *buffer) snprintf(group[iframe].name, sizeof(group[iframe].name), "%s_%i", animinfo[j].name, i); group[iframe].loop = true; group[iframe].rate = animinfo[j].fps; - group[iframe].isheirachical = true; + group[iframe].skeltype = SKEL_RELATIVE; iframe++; } } @@ -5263,7 +5291,7 @@ qboolean QDECL Mod_LoadPSKModel(model_t *mod, void *buffer) Q_strncpyz(group[i].name, animinfo[i].name, sizeof(group[i].name)); group[i].loop = true; group[i].rate = animinfo[i].fps; - group[i].isheirachical = true; + group[i].skeltype = SKEL_RELATIVE; } } for (j = 0; j < num_animkeys; j += num_boneinfo) @@ -5290,7 +5318,7 @@ qboolean QDECL Mod_LoadPSKModel(model_t *mod, void *buffer) strcpy(group->name, "base"); group->loop = true; group->rate = 10; - group->isheirachical = false; + group->skeltype = SKEL_ABSOLUTE; } #ifndef SERVERONLY @@ -5465,7 +5493,7 @@ typedef struct dpmvertex_s // immediately followed by 1 or more dpmbonevert_t structures } dpmvertex_t; -qboolean QDECL Mod_LoadDarkPlacesModel(model_t *mod, void *buffer) +qboolean QDECL Mod_LoadDarkPlacesModel(model_t *mod, void *buffer, size_t fsize) { #ifndef SERVERONLY galiasskin_t *skin; @@ -5647,7 +5675,7 @@ qboolean QDECL Mod_LoadDarkPlacesModel(model_t *mod, void *buffer) outgroups[i].rate = 10; outgroups[i].numposes = 1; - outgroups[i].isheirachical = true; + outgroups[i].skeltype = SKEL_RELATIVE; outgroups[i].boneofs = outposedata; inposedata = (float*)((char*)buffer + inframes[i].ofs_bonepositions); @@ -5674,6 +5702,7 @@ qboolean QDECL Mod_LoadDarkPlacesModel(model_t *mod, void *buffer) m->groupofs = outgroups; + Q_strncpyz(m->surfacename, mesh->shadername, sizeof(m->surfacename)); #ifdef SERVERONLY m->numskins = 1; @@ -5687,7 +5716,7 @@ qboolean QDECL Mod_LoadDarkPlacesModel(model_t *mod, void *buffer) skin[j].numshaders = 1; //non-sequenced skins. skin[j].ofsshaders = shaders; - shaders[0] = Mod_LoadSkinFile(NULL, mesh->shadername, j, NULL, 0, 0, NULL); + shaders[0] = Mod_LoadSkinFile(NULL, m, j, NULL, 0, 0, NULL, skin->name); } m->ofsskins = skin; @@ -5868,28 +5897,30 @@ galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, char *buffer) char *strings; - float *vpos = NULL, *tcoord = NULL, *vnorm = NULL, *vtang = NULL; - unsigned char *vbone = NULL, *vweight = NULL; + float *vpos = NULL, *vtcoord = NULL, *vnorm = NULL, *vtang = NULL, *vrgbaf = NULL; + unsigned char *vbone = NULL, *vweight = NULL, *vrgbaub = NULL; unsigned int type, fmt, size, offset; unsigned short *framedata; - vecV_t *opos; - vec3_t *onorm1, *onorm2, *onorm3; - vec4_t *oweight; - byte_vec4_t *oindex; - float *opose,*oposebase; - vec2_t *otcoords; int memsize; + qbyte *obase=NULL; + vecV_t *opos=NULL; + vec3_t *onorm1=NULL, *onorm2=NULL, *onorm3=NULL; + vec4_t *oweight=NULL; + byte_vec4_t *oindex=NULL; + float *opose=NULL,*oposebase=NULL; + vec2_t *otcoords = NULL; + vec4_t *orgbaf = NULL; - galiasinfo_t *gai; + galiasinfo_t *gai=NULL; #ifndef SERVERONLY - galiasskin_t *skin; - shader_t **shaders; + galiasskin_t *skin=NULL; + shader_t **shaders=NULL; int skinfiles; #endif - galiasgroup_t *fgroup; - galiasbone_t *bones; + galiasgroup_t *fgroup=NULL; + galiasbone_t *bones = NULL; index_t *idx; float basepose[12 * MAX_BONES]; qboolean noweights; @@ -5922,7 +5953,7 @@ galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, char *buffer) if (type == IQM_POSITION && fmt == IQM_FLOAT && size == 3) vpos = (float*)(buffer + offset); else if (type == IQM_TEXCOORD && fmt == IQM_FLOAT && size == 2) - tcoord = (float*)(buffer + offset); + vtcoord = (float*)(buffer + offset); else if (type == IQM_NORMAL && fmt == IQM_FLOAT && size == 3) vnorm = (float*)(buffer + offset); else if (type == IQM_TANGENT && fmt == IQM_FLOAT && size == 4) /*yup, 4*/ @@ -5931,6 +5962,10 @@ galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, char *buffer) vbone = (unsigned char *)(buffer + offset); else if (type == IQM_BLENDWEIGHTS && fmt == IQM_UBYTE && size == 4) vweight = (unsigned char *)(buffer + offset); + else if (type == IQM_COLOR && fmt == IQM_UBYTE && size == 4) + vrgbaub = (qbyte *)(buffer + offset); + else if (type == IQM_COLOR && fmt == IQM_FLOAT && size == 4) + vrgbaf = (float *)(buffer + offset); else Con_Printf("Unrecognised iqm info\n"); } @@ -5942,7 +5977,7 @@ galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, char *buffer) //we also require texcoords because we can. //we don't require normals //we don't require weights, but such models won't animate. - if (h->num_vertexes > 0 && (!vpos || !tcoord)) + if (h->num_vertexes > 0 && (!vpos || !vtcoord)) { Con_Printf("%s is missing vertex array data\n", loadmodel->name); return NULL; @@ -5990,41 +6025,53 @@ galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, char *buffer) mesh = (struct iqmmesh*)(buffer + h->ofs_meshes); - - memsize = sizeof(*gai)*h->num_meshes; - memsize += sizeof(*fgroup)*numgroups + sizeof(float)*12*(h->num_joints + (h->num_poses*h->num_frames)) + sizeof(*bones)*h->num_joints; - memsize += (sizeof(*opos) + sizeof(*onorm1) + sizeof(*onorm2) + sizeof(*onorm3) + sizeof(*otcoords) + (noweights?0:(sizeof(*oindex)+sizeof(*oweight)))) * h->num_vertexes; #ifndef SERVERONLY skinfiles = Mod_BuildSkinFileList(true, loadmodel->name); - memsize += (sizeof(*skin)*h->num_meshes + sizeof(*shaders)*h->num_meshes)*skinfiles; + if (skinfiles < 1) + skinfiles = 1; //iqms have 1 skin and one skin only and always. make sure its loaded. #endif /*allocate a nice big block of memory and figure out where stuff is*/ - gai = ZG_Malloc(&loadmodel->memgroup, memsize); - bones = (galiasbone_t*)(gai + h->num_meshes); - opos = (vecV_t*)(bones + h->num_joints); - onorm3 = (vec3_t*)(opos + h->num_vertexes); - onorm2 = (vec3_t*)(onorm3 + h->num_vertexes); - onorm1 = (vec3_t*)(onorm2 + h->num_vertexes); - if (noweights) + /*run through twice, so things are consistant*/ +#define dalloc(o,count) do{o = (void*)(obase+memsize); memsize += sizeof(*o)*(count);}while(0) + for (i = 0, memsize = 0, obase = NULL; i < 2; i++) { - oindex = NULL; - oweight = NULL; - otcoords = (vec2_t*)(onorm1 + h->num_vertexes); - } - else - { - oindex = (byte_vec4_t*)(onorm1 + h->num_vertexes); - oweight = (vec4_t*)(oindex + h->num_vertexes); - otcoords = (vec2_t*)(oweight + h->num_vertexes); - } - fgroup = (galiasgroup_t*)(otcoords + h->num_vertexes); - oposebase = (float*)(fgroup + numgroups); - opose = oposebase + 12*h->num_joints; + if (i) + obase = ZG_Malloc(&loadmodel->memgroup, memsize); + memsize = 0; + dalloc(gai, h->num_meshes); + dalloc(bones, h->num_joints); + dalloc(opos, h->num_vertexes); + dalloc(onorm1, h->num_vertexes); + dalloc(onorm2, h->num_vertexes); + dalloc(onorm3, h->num_vertexes); + if (!noweights) + { + dalloc(oindex, h->num_vertexes); + dalloc(oweight, h->num_vertexes); + } + else + { + oindex = NULL; + oweight = NULL; + } #ifndef SERVERONLY - skin = (galiasskin_t*)(opose + 12*(h->num_poses*h->num_frames)); - shaders = (shader_t**)(skin + h->num_meshes*skinfiles); + if (vtcoord) + dalloc(otcoords, h->num_vertexes); + else + otcoords = NULL; + if (vrgbaf || vrgbaub) + dalloc(orgbaf, h->num_vertexes); + else + orgbaf = NULL; + dalloc(skin, h->num_meshes*skinfiles); + dalloc(shaders, h->num_meshes*skinfiles); #endif + dalloc(fgroup, numgroups); + dalloc(oposebase, 12*h->num_joints); + dalloc(opose, 12*(h->num_poses*h->num_frames)); + } +#undef dalloc //no code to load animations or bones framedata = (unsigned short*)(buffer + h->ofs_frames); @@ -6128,13 +6175,13 @@ galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, char *buffer) if ((unsigned)framegroups[i].firstpose >= h->num_frames) { //invalid/basepose - fgroup[i].isheirachical = false; + fgroup[i].skeltype = SKEL_ABSOLUTE; fgroup[i].boneofs = oposebase; fgroup[i].numposes = 1; } else { - fgroup[i].isheirachical = true; + fgroup[i].skeltype = SKEL_RELATIVE; fgroup[i].boneofs = opose + framegroups[i].firstpose*12*h->num_poses; fgroup[i].numposes = framegroups[i].posecount; } @@ -6160,7 +6207,12 @@ galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, char *buffer) offset = LittleLong(mesh[i].first_vertex); + Q_strncpyz(gai[i].surfacename, strings+mesh[i].name, sizeof(gai[i].surfacename)); + #ifndef SERVERONLY + /*colours*/ + gai[i].ofs_rgbaf = orgbaf?(orgbaf+offset):NULL; + gai[i].ofs_rgbaub = NULL; /*texture coords*/ gai[i].ofs_st_array = (otcoords+offset); /*skins*/ @@ -6169,7 +6221,6 @@ galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, char *buffer) for (j = 0; j < skinfiles; j++) { - Q_strncpyz(skin->name, skinfilelist[j], sizeof(skin[i].name)); skin->skinwidth = 1; skin->skinheight = 1; skin->ofstexels = 0; /*doesn't support 8bit colourmapping*/ @@ -6178,9 +6229,8 @@ galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, char *buffer) skin->ofsshaders = shaders; skin++; - *shaders++ = Mod_LoadSkinFile(strings+mesh[i].material, strings+mesh[i].name, j, NULL, 0, 0, NULL); + *shaders++ = Mod_LoadSkinFile(strings+mesh[i].material, &gai[i], j, NULL, 0, 0, NULL, skin->name); } - skin += skinfiles; #endif nt = LittleLong(mesh[i].num_triangles); @@ -6218,9 +6268,21 @@ galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, char *buffer) oweight[i][0] = 1; } } + + if (otcoords) + memcpy(otcoords, vtcoord, h->num_vertexes*sizeof(*otcoords)); + if (orgbaf) + { + if (vrgbaf) + memcpy(orgbaf, vrgbaf, h->num_vertexes*sizeof(*orgbaf)); + else + { + for (i = 0; i < h->num_vertexes; i++) + Vector4Scale(vrgbaub+i*4, 1/255.0f, orgbaf[i]); + } + } for (i = 0; i < h->num_vertexes; i++) { - Vector2Copy(tcoord+i*2, otcoords[i]); VectorCopy(vpos+i*3, opos[i]); if (vnorm) { @@ -6245,7 +6307,7 @@ qboolean Mod_ParseIQMAnim(char *buffer, galiasinfo_t *prototype, void**poseofs, -qboolean QDECL Mod_LoadInterQuakeModel(model_t *mod, void *buffer) +qboolean QDECL Mod_LoadInterQuakeModel(model_t *mod, void *buffer, size_t fsize) { int i; galiasinfo_t *root; @@ -6476,7 +6538,7 @@ qboolean Mod_ParseMD5Anim(char *buffer, galiasinfo_t *prototype, void**poseofs, BZ_Free(baseframe); Q_strncpyz(grp.name, "", sizeof(grp.name)); - grp.isheirachical = true; + grp.skeltype = SKEL_RELATIVE; grp.numposes = numframes; grp.rate = framespersecond; grp.loop = true; @@ -6573,7 +6635,7 @@ galiasinfo_t *Mod_ParseMD5MeshModel(char *buffer, char *modname) bones = ZG_Malloc(&loadmodel->memgroup, sizeof(*bones) * numjoints); pose = ZG_Malloc(&loadmodel->memgroup, sizeof(galiasgroup_t)); posedata = ZG_Malloc(&loadmodel->memgroup, sizeof(float)*12 * numjoints); - pose->isheirachical = false; + pose->skeltype = SKEL_ABSOLUTE; pose->rate = 1; pose->numposes = 1; pose->boneofs = posedata; @@ -6848,7 +6910,7 @@ galiasinfo_t *Mod_ParseMD5MeshModel(char *buffer, char *modname) #undef EXPECT } -qboolean QDECL Mod_LoadMD5MeshModel(model_t *mod, void *buffer) +qboolean QDECL Mod_LoadMD5MeshModel(model_t *mod, void *buffer, size_t fsize) { galiasinfo_t *root; @@ -6886,7 +6948,7 @@ clampgroup test/idle1.md5anim frames test/idle1.md5anim */ -qboolean QDECL Mod_LoadCompositeAnim(model_t *mod, void *buffer) +qboolean QDECL Mod_LoadCompositeAnim(model_t *mod, void *buffer, size_t fsize) { int i; @@ -7011,7 +7073,7 @@ qboolean QDECL Mod_LoadCompositeAnim(model_t *mod, void *buffer) //pull out each frame individually for (i = 0; i < ng.numposes; i++) { - grouplist[numgroups].isheirachical = ng.isheirachical; + grouplist[numgroups].skeltype = ng.skeltype; grouplist[numgroups].loop = false; grouplist[numgroups].numposes = 1; grouplist[numgroups].rate = 24; @@ -7059,34 +7121,6 @@ qboolean QDECL Mod_LoadCompositeAnim(model_t *mod, void *buffer) #endif //MD5MODELS -#else -int Mod_TagNumForName(model_t *model, char *name) -{ - return 0; -} -qboolean Mod_GetTag(model_t *model, int tagnum, framestate_t *framestate, float *result) -{ - return false; -} - -int Mod_GetNumBones(struct model_s *model, qboolean allowtags) -{ - return 0; -} -int Mod_GetBoneRelations(model_t *model, int firstbone, int lastbone, framestate_t *fstate, float *result) -{ - return 0; -} -int Mod_GetBoneParent(struct model_s *model, int bonenum) -{ - return 0; -} -char *Mod_GetBoneName(struct model_s *model, int bonenum) -{ - return ""; -} -#endif //#if defined(D3DQUAKE) || defined(GLQUAKE) - void Alias_Register(void) { diff --git a/engine/common/com_mesh.h b/engine/common/com_mesh.h index d116edd6..c836234d 100644 --- a/engine/common/com_mesh.h +++ b/engine/common/com_mesh.h @@ -6,8 +6,8 @@ #include #endif -int HLMod_BoneForName(model_t *mod, char *name); -int HLMod_FrameForName(model_t *mod, char *name); +int HLMod_BoneForName(model_t *mod, const char *name); +int HLMod_FrameForName(model_t *mod, const char *name); //a single pose within an animation (note: always refered to via a framegroup, even if there's only one frame in that group). typedef struct @@ -32,14 +32,14 @@ typedef struct typedef struct { #ifdef SKELETALMODELS - qboolean isheirachical; //for models with transforms, states that bones need to be transformed from their parent. + skeltype_t skeltype; //for models with transforms, states that bones need to be transformed from their parent. //this is actually bad, and can result in bones shortening as they interpolate. + float *boneofs; //numposes*12*numbones #endif qboolean loop; int numposes; float rate; galiaspose_t *poseofs; - float *boneofs; //numposes*12*numbones char name[64]; } galiasgroup_t; @@ -52,8 +52,9 @@ struct galiasbone_s float inverse[12]; }; -typedef struct +typedef struct FTE_DEPRECATED { + //DEPRECATED //skeletal poses refer to this. int vertexindex; int boneindex; @@ -100,6 +101,9 @@ typedef struct typedef struct galiasinfo_s { + char surfacename[MAX_QPATH]; + unsigned short geomset; + unsigned short geomid; index_t *ofs_indexes; int numindexes; @@ -118,6 +122,8 @@ typedef struct galiasinfo_s #ifndef SERVERONLY vec2_t *ofs_st_array; + vec4_t *ofs_rgbaf; + byte_vec4_t *ofs_rgbaub; #endif int groups; @@ -148,6 +154,7 @@ typedef struct galiasinfo_s #endif vboarray_t vboindicies; vboarray_t vbotexcoords; + vboarray_t vborgba; //yeah, just you try reading THAT as an actual word. //these exist only in the root mesh. int numtagframes; @@ -155,9 +162,29 @@ typedef struct galiasinfo_s md3tag_t *ofstags; } galiasinfo_t; -float *Alias_GetBonePositions(galiasinfo_t *inf, framestate_t *fstate, float *buffer, int buffersize, qboolean renderable); +typedef struct +{ + int (*RegisterModelFormatText)(void *module, const char *formatname, char *magictext, qboolean (QDECL *load) (struct model_s *mod, void *buffer, size_t fsize)); + int (*RegisterModelFormatMagic)(void *module, const char *formatname, unsigned int magic, qboolean (QDECL *load) (struct model_s *mod, void *buffer, size_t fsize)); + void (*UnRegisterModelFormat)(int idx); + void (*UnRegisterAllModelFormats)(void *module); + + void *(*ZG_Malloc)(zonegroup_t *ctx, int size); + + void (*ConcatTransforms) (float in1[3][4], float in2[3][4], float out[3][4]); + void (*M3x4_Invert) (const float *in1, float *out); + void (*StripExtension) (const char *in, char *out, int outlen); + void (*GenMatrixPosQuat4Scale)(vec3_t pos, vec4_t quat, vec3_t scale, float result[12]); + void (*ForceConvertBoneData)(skeltype_t sourcetype, const float *sourcedata, size_t bonecount, galiasbone_t *bones, skeltype_t desttype, float *destbuffer, size_t destbonecount); + + shader_t *(*RegisterShader) (const char *name, unsigned int usageflags, const char *shaderscript); + shader_t *(*RegisterSkin) (const char *shadername, const char *modname); + void (*BuildDefaultTexnums)(texnums_t *tn, shader_t *shader); +} modplugfuncs_t; + #ifdef SKELETALMODELS void Alias_TransformVerticies(float *bonepose, galisskeletaltransforms_t *weights, int numweights, vecV_t *xyzout, vec3_t *normout); +void Alias_ForceConvertBoneData(skeltype_t sourcetype, const float *sourcedata, size_t bonecount, galiasbone_t *bones, skeltype_t desttype, float *destbuffer, size_t destbonecount); #endif qboolean Alias_GAliasBuildMesh(mesh_t *mesh, vbo_t **vbop, galiasinfo_t *inf, int surfnum, entity_t *e, qboolean allowskel); void Alias_FlushCache(void); @@ -166,7 +193,7 @@ void Alias_Register(void); void Mod_DoCRC(model_t *mod, char *buffer, int buffersize); -qboolean QDECL Mod_LoadHLModel (model_t *mod, void *buffer); +qboolean QDECL Mod_LoadHLModel (model_t *mod, void *buffer, size_t fsize); #ifdef MAP_PROC qboolean Mod_LoadMap_Proc(model_t *mode, void *buffer); #endif diff --git a/engine/common/com_phys_ode.c b/engine/common/com_phys_ode.c index d8d88176..a9f6c569 100644 --- a/engine/common/com_phys_ode.c +++ b/engine/common/com_phys_ode.c @@ -1673,7 +1673,7 @@ static void World_ODE_Frame_JointFromEntity(world_t *world, wedict_t *ed) } } -static qboolean GenerateCollisionMesh(world_t *world, model_t *mod, wedict_t *ed, vec3_t geomcenter) +static qboolean GenerateCollisionMesh_BSP(world_t *world, model_t *mod, wedict_t *ed, vec3_t geomcenter) { unsigned int sno; msurface_t *surf; @@ -1770,6 +1770,81 @@ static qboolean GenerateCollisionMesh(world_t *world, model_t *mod, wedict_t *ed return true; } +#include "com_mesh.h" +static qboolean GenerateCollisionMesh_Alias(world_t *world, model_t *mod, wedict_t *ed, vec3_t geomcenter) +{ + mesh_t mesh; + unsigned int numverts; + unsigned int numindexes,i; + galiasinfo_t *inf; + unsigned int surfnum = 0; + entity_t re; + + numverts = 0; + numindexes = 0; + + //fill in the parts of the entity_t that Alias_GAliasBuildMesh needs. + world->Get_FrameState(world, ed, &re.framestate); + re.fatness = ed->xv->fatness; + re.model = mod; + + inf = Mod_Extradata (mod); + while(inf) + { + numverts += inf->numverts; + numindexes += inf->numindexes; + inf = inf->nextsurf; + } + + if (!numindexes) + { + Con_DPrintf("entity %i (classname %s) has no geometry\n", NUM_FOR_EDICT(world->progs, (edict_t*)ed), PR_GetString(world->progs, ed->v->classname)); + return false; + } + ed->ode.ode_element3i = BZ_Malloc(numindexes*sizeof(*ed->ode.ode_element3i)); + ed->ode.ode_vertex3f = BZ_Malloc(numverts*sizeof(vec3_t)); + + numverts = 0; + numindexes = 0; + + inf = Mod_Extradata (mod); + while(inf) + { + Alias_GAliasBuildMesh(&mesh, NULL, inf, surfnum++, &re, false); + for (i = 0; i < mesh.numvertexes; i++) + VectorSubtract(mesh.xyz_array[i], geomcenter, (ed->ode.ode_vertex3f + 3*(numverts+i))); + for (i = 0; i < mesh.numindexes; i+=3) + { + //flip the triangles as we go + ed->ode.ode_element3i[numindexes+i+0] = numverts+mesh.indexes[i+2]; + ed->ode.ode_element3i[numindexes+i+1] = numverts+mesh.indexes[i+1]; + ed->ode.ode_element3i[numindexes+i+2] = numverts+mesh.indexes[i+0]; + } + numverts += inf->numverts; + numindexes += inf->numindexes; + inf = inf->nextsurf; + } + + Alias_FlushCache(); //it got built using an entity on the stack, make sure other stuff doesn't get hurt. + + ed->ode.ode_numvertices = numverts; + ed->ode.ode_numtriangles = numindexes/3; + return true; +} + +static qboolean GenerateCollisionMesh(world_t *world, model_t *mod, wedict_t *ed, vec3_t geomcenter) +{ + switch(mod->type) + { + case mod_brush: + return GenerateCollisionMesh_BSP(world, mod, ed, geomcenter); + case mod_alias: + return GenerateCollisionMesh_Alias(world, mod, ed, geomcenter); + default: + return false; //panic! + } +} + qboolean World_ODE_RagMatrixToBody(odebody_t *bodyptr, float *mat) { dVector3 r[3]; @@ -2070,7 +2145,6 @@ static void World_ODE_Frame_BodyFromEntity(world_t *world, wedict_t *ed) dMass mass; float test; void *dataID; - dVector3 capsulerot[3]; model_t *model; int axisindex; int modelindex = 0; @@ -2146,6 +2220,13 @@ static void World_ODE_Frame_BodyFromEntity(world_t *world, wedict_t *ed) case GEOMTYPE_BOX: case GEOMTYPE_SPHERE: case GEOMTYPE_CAPSULE: + case GEOMTYPE_CAPSULE_X: + case GEOMTYPE_CAPSULE_Y: + case GEOMTYPE_CAPSULE_Z: + case GEOMTYPE_CYLINDER: + case GEOMTYPE_CYLINDER_X: + case GEOMTYPE_CYLINDER_Y: + case GEOMTYPE_CYLINDER_Z: VectorCopy(ed->v->mins, entmins); VectorCopy(ed->v->maxs, entmaxs); if (ed->xv->mass) @@ -2232,30 +2313,95 @@ static void World_ODE_Frame_BodyFromEntity(world_t *world, wedict_t *ed) dMassSetSphereTotal(&mass, massval, geomsize[0] * 0.5f); break; case GEOMTYPE_CAPSULE: - axisindex = 0; - if (geomsize[axisindex] < geomsize[1]) - axisindex = 1; - if (geomsize[axisindex] < geomsize[2]) - axisindex = 2; + case GEOMTYPE_CAPSULE_X: + case GEOMTYPE_CAPSULE_Y: + case GEOMTYPE_CAPSULE_Z: + if (geomtype == GEOMTYPE_CAPSULE) + { + axisindex = 0; + if (geomsize[axisindex] < geomsize[1]) + axisindex = 1; + if (geomsize[axisindex] < geomsize[2]) + axisindex = 2; + } + else + axisindex = geomtype-GEOMTYPE_CAPSULE_X; // the qc gives us 3 axis radius, the longest axis is the capsule // axis, since ODE doesn't like this idea we have to create a // capsule which uses the standard orientation, and apply a // transform to it - memset(capsulerot, 0, sizeof(capsulerot)); if (axisindex == 0) + { Matrix4x4_CM_ModelMatrix(ed->ode.ode_offsetmatrix, geomcenter[0], geomcenter[1], geomcenter[2], 0, 0, 90, 1); + radius = min(geomsize[1], geomsize[2]) * 0.5f; + } else if (axisindex == 1) + { Matrix4x4_CM_ModelMatrix(ed->ode.ode_offsetmatrix, geomcenter[0], geomcenter[1], geomcenter[2], 90, 0, 0, 1); + radius = min(geomsize[0], geomsize[2]) * 0.5f; + } else + { Matrix4x4_CM_ModelMatrix(ed->ode.ode_offsetmatrix, geomcenter[0], geomcenter[1], geomcenter[2], 0, 0, 0, 1); - radius = geomsize[!axisindex] * 0.5f; // any other axis is the radius + radius = min(geomsize[0], geomsize[1]) * 0.5f; + } length = geomsize[axisindex] - radius*2; + if (length <= 0) + { + radius -= (1 - length)*0.5; + length = 1; + } // because we want to support more than one axisindex, we have to // create a transform, and turn on its cleanup setting (which will // cause the child to be destroyed when it is destroyed) ed->ode.ode_geom = (void *)dCreateCapsule(world->ode.ode_space, radius, length); dMassSetCapsuleTotal(&mass, massval, axisindex+1, radius, length); break; + case GEOMTYPE_CYLINDER: + case GEOMTYPE_CYLINDER_X: + case GEOMTYPE_CYLINDER_Y: + case GEOMTYPE_CYLINDER_Z: + if (geomtype == GEOMTYPE_CYLINDER) + { + axisindex = 0; + if (geomsize[axisindex] < geomsize[1]) + axisindex = 1; + if (geomsize[axisindex] < geomsize[2]) + axisindex = 2; + } + else + axisindex = geomtype-GEOMTYPE_CYLINDER_X; + // the qc gives us 3 axis radius, the longest axis is the capsule + // axis, since ODE doesn't like this idea we have to create a + // capsule which uses the standard orientation, and apply a + // transform to it + if (axisindex == 0) + { + Matrix4x4_CM_ModelMatrix(ed->ode.ode_offsetmatrix, geomcenter[0], geomcenter[1], geomcenter[2], 0, 0, 90, 1); + radius = min(geomsize[1], geomsize[2]) * 0.5f; + } + else if (axisindex == 1) + { + Matrix4x4_CM_ModelMatrix(ed->ode.ode_offsetmatrix, geomcenter[0], geomcenter[1], geomcenter[2], 90, 0, 0, 1); + radius = min(geomsize[0], geomsize[2]) * 0.5f; + } + else + { + Matrix4x4_CM_ModelMatrix(ed->ode.ode_offsetmatrix, geomcenter[0], geomcenter[1], geomcenter[2], 0, 0, 0, 1); + radius = min(geomsize[0], geomsize[1]) * 0.5f; + } + length = geomsize[axisindex] - radius*2; + if (length <= 0) + { + radius -= (1 - length)*0.5; + length = 1; + } + // because we want to support more than one axisindex, we have to + // create a transform, and turn on its cleanup setting (which will + // cause the child to be destroyed when it is destroyed) + ed->ode.ode_geom = (void *)dCreateCylinder(world->ode.ode_space, radius, length); + dMassSetCylinderTotal(&mass, massval, axisindex+1, radius, length); + break; default: Sys_Error("World_ODE_BodyFromEntity: unrecognized solid value %i was accepted by filter\n", solid); } diff --git a/engine/common/common.c b/engine/common/common.c index cc4037ca..59e056fa 100644 --- a/engine/common/common.c +++ b/engine/common/common.c @@ -648,10 +648,10 @@ float Q_atof (const char *str) attempts to remove leet strange chars from a name the resulting string is not intended to be visible to humans, but this functions results can be matched against each other. */ -void deleetstring(char *result, char *leet) +void deleetstring(char *result, const char *leet) { char *s = result; - char *s2 = leet; + const char *s2 = leet; while(*s2) { if (*s2 == (char)0xff) @@ -1434,6 +1434,24 @@ float MSG_ReadFloat (void) return dat.f; } +char *MSG_ReadStringBuffer (char *out, size_t outsize) +{ + int l,c; + + l = 0; + do + { + c = MSG_ReadChar (); + if (msg_badread || c == 0) + break; + out[l] = c; + l++; + } while (l < outsize-1); + + out[l] = 0; + + return out; +} char *MSG_ReadString (void) { static char string[8192]; @@ -2299,14 +2317,14 @@ unsigned int unicode_encode(char *out, unsigned int unicode, int maxlen, qboolea } //char-based strlen. -unsigned int unicode_charcount(char *in, size_t buffersize, qboolean markup) +unsigned int unicode_charcount(const char *in, size_t buffersize, qboolean markup) { int error; - char *end = in + buffersize; + const char *end = in + buffersize; int chars = 0; for(chars = 0; in < end && *in; chars+=1) { - unicode_decode(&error, in, &in, markup); + unicode_decode(&error, in, (char**)&in, markup); if (in > end) break; //exceeded buffer size uncleanly @@ -2315,9 +2333,9 @@ unsigned int unicode_charcount(char *in, size_t buffersize, qboolean markup) } //handy hacky function. -unsigned int unicode_byteofsfromcharofs(char *str, unsigned int charofs, qboolean markup) +unsigned int unicode_byteofsfromcharofs(const char *str, unsigned int charofs, qboolean markup) { - char *in = str; + const char *in = str; int error; int chars; for(chars = 0; *in; chars+=1) @@ -2325,19 +2343,19 @@ unsigned int unicode_byteofsfromcharofs(char *str, unsigned int charofs, qboolea if (chars >= charofs) return in - str; - unicode_decode(&error, in, &in, markup); + unicode_decode(&error, in, (char**)&in, markup); } return in - str; } //handy hacky function. -unsigned int unicode_charofsfrombyteofs(char *str, unsigned int byteofs, qboolean markup) +unsigned int unicode_charofsfrombyteofs(const char *str, unsigned int byteofs, qboolean markup) { int error; - char *end = str + byteofs; + const char *end = str + byteofs; int chars = 0; for(chars = 0; str < end && *str; chars+=1) { - unicode_decode(&error, str, &str, markup); + unicode_decode(&error, str, (char**)&str, markup); if (str > end) break; //exceeded buffer size uncleanly @@ -2363,7 +2381,7 @@ int towlower(int c) } #endif -size_t unicode_strtoupper(char *in, char *out, size_t outsize, qboolean markup) +size_t unicode_strtoupper(const char *in, char *out, size_t outsize, qboolean markup) { //warning: towupper is locale-specific (eg: turkish has both I and dotted-I and thus i should transform to dotted-I rather than to I). //also it can't easily cope with accent prefixes. @@ -2374,7 +2392,7 @@ size_t unicode_strtoupper(char *in, char *out, size_t outsize, qboolean markup) while(*in) { - c = unicode_decode(&error, in, &in, markup); + c = unicode_decode(&error, in, (char**)&in, markup); if (c >= 0xe020 && c <= 0xe07f) //quake-char-aware. c = towupper(c & 0x7f) + (c & 0xff80); else @@ -2387,7 +2405,7 @@ size_t unicode_strtoupper(char *in, char *out, size_t outsize, qboolean markup) return l; } -size_t unicode_strtolower(char *in, char *out, size_t outsize, qboolean markup) +size_t unicode_strtolower(const char *in, char *out, size_t outsize, qboolean markup) { //warning: towlower is locale-specific (eg: turkish has both i and dotless-i and thus I should transform to dotless-i rather than to i). //also it can't easily cope with accent prefixes. @@ -2398,7 +2416,7 @@ size_t unicode_strtolower(char *in, char *out, size_t outsize, qboolean markup) while(*in) { - c = unicode_decode(&error, in, &in, markup); + c = unicode_decode(&error, in, (char**)&in, markup); if (c >= 0xe020 && c <= 0xe07f) //quake-char-aware. c = towlower(c & 0x7f) + (c & 0xff80); else @@ -2976,6 +2994,30 @@ conchar_t *COM_ParseFunString(conchar_t defaultflags, const char *str, conchar_t } continue; } + else if (str[0] == '=' && str[1] == '`' && str[2] == 'u' && str[3] == '8' && str[4] == ':' && !keepmarkup) + { + int l; + char temp[1024]; + str += 5; + while(*str) + { + l = 0; + while (*str && l < sizeof(temp)-32 && !(str[0] == '`' && str[1] == '=')) + temp[l++] = *str++; + //recurse + temp[l] = 0; + l = COM_ParseFunString(ext, temp, out, outsize, PFS_FORCEUTF8) - out; + outsize -= l; + out += l; + if (str[0] == '`' && str[1] == '=') + { + str+=2; + break; + } + } + continue; + } + /* else if ((str[0] == 'h' && str[1] == 't' && str[2] == 't' && str[3] == 'p' && str[4] == ':' && !linkstart && !(flags & (PFS_NOMARKUP|PFS_KEEPMARKUP))) || (str[0] == 'h' && str[1] == 't' && str[2] == 't' && str[3] == 'p' && str[4] == 's' && str[5] == ':' && !linkstart && !(flags & (PFS_NOMARKUP|PFS_KEEPMARKUP)))) @@ -4341,6 +4383,7 @@ int memsearch (qbyte *start, int count, int search) return -1; } +/* struct effectinfo_s { struct effectinfo_s *next; @@ -4486,7 +4529,7 @@ char *COM_Effectinfo_ForNumber(unsigned int efnum) } return ""; } - +*/ /*************************************************************************/ /*remaps map checksums from known non-cheat GPL maps to authentic id1 maps*/ @@ -4569,7 +4612,7 @@ Searches the string for the given key and returns the associated value, or an empty string. =============== */ -char *Info_ValueForKey (char *s, const char *key) +char *Info_ValueForKey (const char *s, const char *key) { char pkey[1024]; static char value[4][1024]; // use two buffers so compares diff --git a/engine/common/common.h b/engine/common/common.h index b496942f..96abbb57 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -195,6 +195,7 @@ struct client_s; unsigned int MSGSV_ReadEntity (struct client_s *fromclient); unsigned int MSGCL_ReadEntity (void); float MSG_ReadFloat (void); +char *MSG_ReadStringBuffer (char *out, size_t outsize); char *MSG_ReadString (void); char *MSG_ReadStringLine (void); @@ -256,7 +257,7 @@ int Q_strncasecmp (const char *s1, const char *s2, int n); int Q_strcasecmp (const char *s1, const char *s2); int Q_atoi (const char *str); float Q_atof (const char *str); -void deleetstring(char *result, char *leet); +void deleetstring(char *result, const char *leet); //============================================================================ @@ -303,14 +304,17 @@ unsigned int iso88591_encode(char *out, unsigned int unicode, int maxlen, qboole unsigned int qchar_encode(char *out, unsigned int unicode, int maxlen, qboolean markup); unsigned int COM_DeQuake(conchar_t chr); +//small macro to tell COM_ParseFunString (and related functions like con_printf) that the input is a utf-8 string. +#define U8(s) "=`u8:"s"`=" + //handles whatever charset is active, including ^U stuff. -unsigned int unicode_byteofsfromcharofs(char *str, unsigned int charofs, qboolean markup); -unsigned int unicode_charofsfrombyteofs(char *str, unsigned int byteofs, qboolean markup); +unsigned int unicode_byteofsfromcharofs(const char *str, unsigned int charofs, qboolean markup); +unsigned int unicode_charofsfrombyteofs(const char *str, unsigned int byteofs, qboolean markup); unsigned int unicode_encode(char *out, unsigned int unicode, int maxlen, qboolean markup); unsigned int unicode_decode(int *error, const void *in, char **out, qboolean markup); -size_t unicode_strtolower(char *in, char *out, size_t outsize, qboolean markup); -size_t unicode_strtoupper(char *in, char *out, size_t outsize, qboolean markup); -unsigned int unicode_charcount(char *in, size_t buffersize, qboolean markup); +size_t unicode_strtolower(const char *in, char *out, size_t outsize, qboolean markup); +size_t unicode_strtoupper(const char *in, char *out, size_t outsize, qboolean markup); +unsigned int unicode_charcount(const char *in, size_t buffersize, qboolean markup); char *COM_SkipPath (const char *pathname); void COM_StripExtension (const char *in, char *out, int outlen); @@ -331,8 +335,8 @@ extern int com_filesize; extern qboolean com_file_untrusted; struct cache_user_s; -extern char com_quakedir[MAX_OSPATH]; -extern char com_homedir[MAX_OSPATH]; +extern char com_gamepath[MAX_OSPATH]; +extern char com_homepath[MAX_OSPATH]; extern char com_configdir[MAX_OSPATH]; //dir to put cfg_save configs in //extern char *com_basedir; @@ -342,7 +346,7 @@ void COM_WriteFile (const char *filename, const void *data, int len); #if defined(__amd64__) || defined(_AMD64_) || __WORDSIZE == 64 #define FS_64BIT #endif -#ifdef FS_64BIT +#if 1//def FS_64BIT typedef unsigned long long qofs_t; //type to use for a file offset #define qofs_Make(low,high) (low | (((qofs_t)(high))<<32)) #define qofs_Low(o) ((o)&0xffffffffu) @@ -397,7 +401,7 @@ typedef struct vfsfile_s qboolean (QDECL *Seek) (struct vfsfile_s *file, qofs_t pos); //returns false for error qofs_t (QDECL *Tell) (struct vfsfile_s *file); qofs_t (QDECL *GetLen) (struct vfsfile_s *file); //could give some lag - void (QDECL *Close) (struct vfsfile_s *file); + qboolean (QDECL *Close) (struct vfsfile_s *file); //returns false if there was some error. void (QDECL *Flush) (struct vfsfile_s *file); qboolean seekingisabadplan; @@ -407,25 +411,26 @@ typedef struct vfsfile_s } vfsfile_t; typedef struct searchpathfuncs_s searchpathfuncs_t; -#define VFS_CLOSE(vf) (vf->Close(vf)) -#define VFS_TELL(vf) (vf->Tell(vf)) -#define VFS_GETLEN(vf) (vf->GetLen(vf)) -#define VFS_SEEK(vf,pos) (vf->Seek(vf,pos)) -#define VFS_READ(vf,buffer,buflen) (vf->ReadBytes(vf,buffer,buflen)) -#define VFS_WRITE(vf,buffer,buflen) (vf->WriteBytes(vf,buffer,buflen)) -#define VFS_FLUSH(vf) do{if(vf->Flush)vf->Flush(vf);}while(0) -#define VFS_PUTS(vf,s) do{const char *t=s;vf->WriteBytes(vf,t,strlen(t));}while(0) +#define VFS_CLOSE(vf) ((vf)->Close(vf)) +#define VFS_TELL(vf) ((vf)->Tell(vf)) +#define VFS_GETLEN(vf) ((vf)->GetLen(vf)) +#define VFS_SEEK(vf,pos) ((vf)->Seek(vf,pos)) +#define VFS_READ(vf,buffer,buflen) ((vf)->ReadBytes(vf,buffer,buflen)) +#define VFS_WRITE(vf,buffer,buflen) ((vf)->WriteBytes(vf,buffer,buflen)) +#define VFS_FLUSH(vf) do{if((vf)->Flush)(vf)->Flush(vf);}while(0) +#define VFS_PUTS(vf,s) do{const char *t=s;(vf)->WriteBytes(vf,t,strlen(t));}while(0) char *VFS_GETS(vfsfile_t *vf, char *buffer, int buflen); void VARGS VFS_PRINTF(vfsfile_t *vf, char *fmt, ...) LIKEPRINTF(2); enum fs_relative{ FS_GAME, //standard search (not generally valid for save/rename/delete/etc) FS_BINARYPATH, //for dlls and stuff - FS_ROOT, //./ + FS_ROOT, //./ (the root basepath or root homepath.) FS_GAMEONLY, //$gamedir/ FS_GAMEDOWNLOADCACHE, //typically the same as FS_GAMEONLY - FS_CONFIGONLY, //fte/ (should still be part of the game path) - FS_SKINS //qw/skins/ + FS_BASEGAMEONLY, //fte/ (fixme: should be the last basegame.) + FS_PUBBASEGAMEONLY, //qw/ (fixme: should be the last non-private basedir) + FS_SYSTEM //a system path. absolute paths are explicitly allowed and expected. }; void FS_FlushFSHashReally(void); @@ -447,6 +452,8 @@ void FS_ReloadPackFiles(void); char *FSQ3_GenerateClientPacksList(char *buffer, int maxlen, int basechecksum); void FS_PureMode(int mode, char *packagelist, char *crclist, int seed); //implies an fs_restart +//recursively tries to open files until it can get a zip. +vfsfile_t *CL_OpenFileInPackage(searchpathfuncs_t *search, char *name); qbyte *QDECL COM_LoadStackFile (const char *path, void *buffer, int bufsize); qbyte *COM_LoadTempFile (const char *path); @@ -461,7 +468,13 @@ qboolean FS_Restarted(unsigned int *since); typedef struct { + qboolean blockupdate; //set to block the updateurl from being used this session. this avoids recursive updates when manifests contain the same update url. + + int minver; //if the engine svn revision is lower than this, the manifest will not be used as an 'upgrade'. + int maxver; //if not 0, the manifest will not be used + qboolean disablehomedir; char *updateurl; //url to download an updated manifest file from. + char *updatefile; //this is the file that needs to be written to update the manifest. char *installation; //optional hardcoded commercial name, used for scanning the registry to find existing installs. char *formalname; //the commercial name of the game. you'll get FULLENGINENAME otherwise. char *protocolname; //the name used for purposes of dpmaster @@ -481,19 +494,20 @@ typedef struct } package[64]; } ftemanifest_t; void FS_Manifest_Free(ftemanifest_t *man); -ftemanifest_t *FS_Manifest_Parse(const char *data); +ftemanifest_t *FS_Manifest_Parse(const char *fname, const char *data); void COM_InitFilesystem (void); //does not set up any gamedirs. qboolean FS_ChangeGame(ftemanifest_t *newgame, qboolean allowreloadconfigs); void FS_Shutdown(void); void COM_Gamedir (const char *dir); -char *FS_GetGamedir(void); +char *FS_GetGamedir(qboolean publicpathonly); char *FS_GetBasedir(void); +char *FS_GetManifestArgs(void); struct zonegroup_s; void *FS_LoadMallocGroupFile(struct zonegroup_s *ctx, char *path); qbyte *FS_LoadMallocFile (const char *path); -qofs_t FS_LoadFile(char *name, void **file); +qofs_t FS_LoadFile(const char *name, void **file); void FS_FreeFile(void *file); qbyte *COM_LoadFile (const char *path, int usehunk); @@ -513,7 +527,7 @@ char *COM_Effectinfo_ForNumber(unsigned int efnum); unsigned int COM_RemapMapChecksum(unsigned int checksum); #define MAX_INFO_KEY 256 -char *Info_ValueForKey (char *s, const char *key); +char *Info_ValueForKey (const char *s, const char *key); void Info_RemoveKey (char *s, const char *key); char *Info_KeyForNumber (char *s, int num); void Info_RemovePrefixedKeys (char *start, char prefix); @@ -524,14 +538,14 @@ void Info_Print (char *s, char *lineprefix); void Info_WriteToFile(vfsfile_t *f, char *info, char *commandname, int cvarflags); void Com_BlocksChecksum (int blocks, void **buffer, int *len, unsigned char *outbuf); -unsigned int Com_BlockChecksum (void *buffer, int length); -void Com_BlockFullChecksum (void *buffer, int len, unsigned char *outbuf); +unsigned int Com_BlockChecksum (const void *buffer, int length); +void Com_BlockFullChecksum (const void *buffer, int len, unsigned char *outbuf); qbyte COM_BlockSequenceCheckByte (qbyte *base, int length, int sequence, unsigned mapchecksum); qbyte COM_BlockSequenceCRCByte (qbyte *base, int length, int sequence); qbyte Q2COM_BlockSequenceCRCByte (qbyte *base, int length, int sequence); -int SHA1(char *digest, int maxdigestsize, char *string, int stringlen); -int SHA1_HMAC(unsigned char *digest, int maxdigestsize, unsigned char *data, int datalen, unsigned char *key, int keylen); +int SHA1(char *digest, int maxdigestsize, const char *string, int stringlen); +int SHA1_HMAC(unsigned char *digest, int maxdigestsize, const unsigned char *data, int datalen, const unsigned char *key, int keylen); int version_number(void); char *version_string(void); diff --git a/engine/common/console.h b/engine/common/console.h index 0f146a07..4a7c0a28 100644 --- a/engine/common/console.h +++ b/engine/common/console.h @@ -139,6 +139,7 @@ typedef struct console_s void (*redirect) (struct console_s *con, int key); //if present, called every character. void *userdata; + conline_t *completionline; //temp text at the bottom of the console conline_t *footerline; //temp text at the bottom of the console conline_t *selstartline, *selendline; unsigned int selstartoffset, selendoffset; @@ -199,8 +200,8 @@ int Con_IsActive (console_t *con); void Con_Destroy (console_t *con); void Con_SetActive (console_t *con); qboolean Con_NameForNum(int num, char *buffer, int buffersize); -console_t *Con_FindConsole(char *name); -console_t *Con_Create(char *name, unsigned int flags); +console_t *Con_FindConsole(const char *name); +console_t *Con_Create(const char *name, unsigned int flags); void Con_SetVisible (console_t *con); void Con_PrintCon (console_t *con, char *txt); diff --git a/engine/common/crc.c b/engine/common/crc.c index bebc3081..d014ad25 100644 --- a/engine/common/crc.c +++ b/engine/common/crc.c @@ -81,7 +81,7 @@ unsigned short QCRC_Value(unsigned short crcvalue) return crcvalue ^ QCRC_XOR_VALUE; } -unsigned short QCRC_Block (qbyte *start, int count) +unsigned short QCRC_Block (const qbyte *start, int count) { unsigned short crc; @@ -92,7 +92,7 @@ unsigned short QCRC_Block (qbyte *start, int count) return crc; } -unsigned short QCRC_Block_AsLower (qbyte *start, int count) +unsigned short QCRC_Block_AsLower (const qbyte *start, int count) { unsigned short crc; @@ -103,7 +103,7 @@ unsigned short QCRC_Block_AsLower (qbyte *start, int count) return crc; } -void QCRC_AddBlock (unsigned short *crcvalue, qbyte *start, int count) +void QCRC_AddBlock (unsigned short *crcvalue, const qbyte *start, int count) { while (count--) QCRC_ProcessByte(crcvalue, *start++); diff --git a/engine/common/crc.h b/engine/common/crc.h index b73203db..c61a6d20 100644 --- a/engine/common/crc.h +++ b/engine/common/crc.h @@ -20,8 +20,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. /* crc.h */ void QCRC_Init(unsigned short *crcvalue); -void QCRC_AddBlock (unsigned short *crcvalue, qbyte *start, int count); +void QCRC_AddBlock (unsigned short *crcvalue, const qbyte *start, int count); void QCRC_ProcessByte(unsigned short *crcvalue, qbyte data); unsigned short QCRC_Value(unsigned short crcvalue); -unsigned short QCRC_Block (qbyte *start, int count); -unsigned short QCRC_Block_AsLower (qbyte *start, int count); +unsigned short QCRC_Block (const qbyte *start, int count); +unsigned short QCRC_Block_AsLower (const qbyte *start, int count); diff --git a/engine/common/cvar.c b/engine/common/cvar.c index aa884a18..1e8273d6 100644 --- a/engine/common/cvar.c +++ b/engine/common/cvar.c @@ -68,7 +68,7 @@ Cvar_FindVar */ cvar_t *Cvar_FindVar (const char *var_name) { - return Hash_GetInsensative(&cvar_hash, var_name); + return Hash_GetInsensitive(&cvar_hash, var_name); /* cvar_group_t *grp; cvar_t *var; @@ -791,10 +791,21 @@ cvar_t *Cvar_SetCore (cvar_t *var, const char *value, qboolean force) var->modified++; //only modified if it changed. if (var->callback) var->callback(var, latch); - - if ((var->flags & CVAR_ARCHIVE) && !(var->flags & CVAR_SERVEROVERRIDE) && cl_warncmd.ival) - Cvar_ConfigChanged(); } + if ((var->flags & CVAR_ARCHIVE) && !(var->flags & CVAR_SERVEROVERRIDE) && cl_warncmd.ival) + { + if (var->latched_string) + { + if (strcmp(var->latched_string, value)) + Cvar_ConfigChanged(); + } + else + { + if (strcmp(latch, value)) + Cvar_ConfigChanged(); + } + } + Z_Free (latch); // free the old value string } @@ -968,7 +979,7 @@ void Cvar_SetValue (cvar_t *var, float value) if (value == (int)value) sprintf (val, "%i",(int)value); //make it look nicer. else - sprintf (val, "%f",value); + sprintf (val, "%g",value); Cvar_Set (var, val); } @@ -1063,9 +1074,9 @@ qboolean Cvar_Register (cvar_t *variable, const char *groupname) Cvar_Free(old); - Hash_AddInsensative(&cvar_hash, variable->name, variable, &variable->hbn1); + Hash_AddInsensitive(&cvar_hash, variable->name, variable, &variable->hbn1); if (variable->name2) - Hash_AddInsensative(&cvar_hash, variable->name2, variable, &variable->hbn2); + Hash_AddInsensitive(&cvar_hash, variable->name2, variable, &variable->hbn2); return false; } @@ -1088,9 +1099,9 @@ qboolean Cvar_Register (cvar_t *variable, const char *groupname) variable->restriction = 0; //exe registered vars group->cvars = variable; - Hash_AddInsensative(&cvar_hash, variable->name, variable, &variable->hbn1); + Hash_AddInsensitive(&cvar_hash, variable->name, variable, &variable->hbn1); if (variable->name2) - Hash_AddInsensative(&cvar_hash, variable->name2, variable, &variable->hbn2); + Hash_AddInsensitive(&cvar_hash, variable->name2, variable, &variable->hbn2); variable->string = NULL; diff --git a/engine/common/fs.c b/engine/common/fs.c index a8a439ab..9e93fbf3 100644 --- a/engine/common/fs.c +++ b/engine/common/fs.c @@ -15,6 +15,7 @@ void fs_game_callback(cvar_t *var, char *oldvalue); hashtable_t filesystemhash; qboolean com_fschanged = true; +qboolean fs_readonly; static unsigned int fs_restarts; cvar_t com_fs_cache = CVARF("fs_cache", IFMINIMAL("2","1"), CVAR_ARCHIVE); @@ -101,11 +102,6 @@ void FS_UnRegisterFileSystemModule(void *module) } } - -vfsfile_t *FS_OpenVFSLoc(flocation_t *loc, char *mode); - - - char *VFS_GETS(vfsfile_t *vf, char *buffer, int buflen) { char in; @@ -156,6 +152,7 @@ void VARGS VFS_PRINTF(vfsfile_t *vf, char *format, ...) char gamedirfile[MAX_OSPATH]; +char pubgamedirfile[MAX_OSPATH]; //like gamedirfile, but not set to the fte-only paths @@ -165,11 +162,9 @@ int com_filesize; qboolean com_file_copyprotected;//file should not be available for download. qboolean com_file_untrusted; //file was downloaded inside a package - -//char *com_basedir; //obsolete - -char com_quakedir[MAX_OSPATH]; -char com_homedir[MAX_OSPATH]; +char com_gamepath[MAX_OSPATH]; //c:\games\quake +char com_homepath[MAX_OSPATH]; //c:\users\foo\my docs\fte\quake +qboolean com_homepathenabled; char com_configdir[MAX_OSPATH]; //homedir/fte/configs @@ -200,6 +195,7 @@ void FS_Manifest_Free(ftemanifest_t *man) if (!man) return; Z_Free(man->updateurl); + Z_Free(man->updatefile); Z_Free(man->installation); Z_Free(man->formalname); Z_Free(man->protocolname); @@ -233,6 +229,7 @@ static ftemanifest_t *FS_Manifest_Clone(ftemanifest_t *oldm) newm->protocolname = Z_StrDup(oldm->protocolname); if (oldm->defaultexec) newm->defaultexec = Z_StrDup(oldm->defaultexec); + newm->disablehomedir = oldm->disablehomedir; for (i = 0; i < sizeof(newm->gamepath) / sizeof(newm->gamepath[0]); i++) { @@ -309,26 +306,40 @@ static void FS_Manifest_PurgeGamedirs(ftemanifest_t *man) } //create a new empty manifest with default values. -static ftemanifest_t *FS_Manifest_Create(void) +static ftemanifest_t *FS_Manifest_Create(const char *syspath) { ftemanifest_t *man = Z_Malloc(sizeof(*man)); -// man->installation = Z_StrDup("quake"); man->formalname = Z_StrDup(FULLENGINENAME); + + if (syspath) + man->updatefile = Z_StrDup(syspath); //this should be a system path. return man; } //parse Cmd_Argv tokens into the manifest. -static void FS_Manifest_ParseTokens(ftemanifest_t *man) +static qboolean FS_Manifest_ParseTokens(ftemanifest_t *man) { + qboolean result = true; char *fname; if (!Cmd_Argc()) - return; + return result; fname = Cmd_Argv(0); if (*fname == '*') fname++; - - if (!Q_strcasecmp(fname, "game")) + if (!Q_strcasecmp(fname, "minver")) + { + //ignore minimum versions for other engines. + if (!strcmp(Cmd_Argv(2), DISTRIBUTION)) + man->minver = atoi(Cmd_Argv(3)); + } + else if (!Q_strcasecmp(fname, "maxver")) + { + //ignore minimum versions for other engines. + if (!strcmp(Cmd_Argv(2), DISTRIBUTION)) + man->maxver = atoi(Cmd_Argv(3)); + } + else if (!Q_strcasecmp(fname, "game")) { Z_Free(man->installation); man->installation = Z_StrDup(Cmd_Argv(1)); @@ -348,6 +359,15 @@ static void FS_Manifest_ParseTokens(ftemanifest_t *man) Z_Free(man->defaultexec); man->defaultexec = Z_StrDup(Cmd_Argv(1)); } + else if (!Q_strcasecmp(fname, "updateurl")) + { + Z_Free(man->updateurl); + man->updateurl = Z_StrDup(Cmd_Argv(1)); + } + else if (!Q_strcasecmp(fname, "disablehomedir")) + { + man->disablehomedir = !!atoi(Cmd_Argv(1)); + } else if (!Q_strcasecmp(fname, "basegame") || !Q_strcasecmp(fname, "gamedir")) { int i; @@ -375,27 +395,27 @@ static void FS_Manifest_ParseTokens(ftemanifest_t *man) } } } - else + else if (!Q_strcasecmp(fname, "package")) { qboolean crcknown; int crc; int i, j; - if (!Q_strcasecmp(fname, "package")) - Cmd_ShiftArgs(1, false); - crcknown = (strcmp(Cmd_Argv(1), "-") && *Cmd_Argv(1)); - crc = strtoul(Cmd_Argv(1), NULL, 0); + fname = Cmd_Argv(1); + + crcknown = (strcmp(Cmd_Argv(2), "-") && *Cmd_Argv(2)); + crc = strtoul(Cmd_Argv(2), NULL, 0); for (i = 0; i < sizeof(man->package) / sizeof(man->package[0]); i++) { if (!man->package[i].path) { - man->package[i].path = Z_StrDup(Cmd_Argv(0)); + man->package[i].path = Z_StrDup(fname); man->package[i].crcknown = crcknown; man->package[i].crc = crc; - for (j = 0; j < Cmd_Argc()-2 && j < sizeof(man->package[i].mirrors) / sizeof(man->package[i].mirrors[0]); j++) + for (j = 0; j < Cmd_Argc()-3 && j < sizeof(man->package[i].mirrors) / sizeof(man->package[i].mirrors[0]); j++) { - man->package[i].mirrors[j] = Z_StrDup(Cmd_Argv(2+j)); + man->package[i].mirrors[j] = Z_StrDup(Cmd_Argv(3+j)); } break; } @@ -405,10 +425,14 @@ static void FS_Manifest_ParseTokens(ftemanifest_t *man) Con_Printf("Too many packages specified in manifest\n"); } } + else + result = false; + return result; } //read a manifest file -ftemanifest_t *FS_Manifest_Parse(const char *data) +ftemanifest_t *FS_Manifest_Parse(const char *fname, const char *data) { + int hasheaderver = 0; ftemanifest_t *man; if (!data) return NULL; @@ -417,21 +441,42 @@ ftemanifest_t *FS_Manifest_Parse(const char *data) if (!*data) return NULL; + if (!Q_strncasecmp(data, "FTEMANIFEST", 11)) + { + data = Cmd_TokenizeString((char*)data, false, false); + hasheaderver = atoi(Cmd_Argv(1)); + } - man = FS_Manifest_Create(); + man = FS_Manifest_Create(fname); while (data && *data) { data = Cmd_TokenizeString((char*)data, false, false); - FS_Manifest_ParseTokens(man); + if (!FS_Manifest_ParseTokens(man) && !hasheaderver) + { //only support unknown things if there's an actual version specified. + FS_Manifest_Free(man); + return NULL; + } } if (!man->installation) - { //every manifest should have an internal name specified, so we can use the correct basedir + { //every manifest should have an internal name specified, so we can guess the correct basedir //if we don't recognise it, then we'll typically prompt (or just use the working directory), but always assuming a default at least ensures things are sane. //fixme: we should probably fill in the basegame here (and share that logic with the legacy manifest generation code) data = Cmd_TokenizeString((char*)"game quake", false, false); FS_Manifest_ParseTokens(man); } + +#ifdef SVNREVISION + //svnrevision is often '-', which means we can't just use it as a constant. + { + int ver = atoi(STRINGIFY(SVNREVISION)); + if (man->minver > ver || (man->maxver && man->maxver < ver)) + { + FS_Manifest_Free(man); + return NULL; + } + } +#endif return man; } @@ -549,13 +594,13 @@ static int QDECL COM_Dir_List(const char *name, qofs_t size, void *parm, searchp break; } if (size > 1.0*1024*1024*1024) - Con_Printf("%s \t(%#.3ggb) (%s)\n", name, size/(1024.0*1024*1024), s?s->logicalpath:"??"); + Con_Printf(U8("%s \t(%#.3ggb) (%s)\n"), name, size/(1024.0*1024*1024), s?s->logicalpath:"??"); else if (size > 1.0*1024*1024) - Con_Printf("%s \t(%#.3gmb) (%s)\n", name, size/(1024.0*1024), s?s->logicalpath:"??"); + Con_Printf(U8("%s \t(%#.3gmb) (%s)\n"), name, size/(1024.0*1024), s?s->logicalpath:"??"); else if (size > 1.0*1024) - Con_Printf("%s \t(%#.3gkb) (%s)\n", name, size/1024.0, s?s->logicalpath:"??"); + Con_Printf(U8("%s \t(%#.3gkb) (%s)\n"), name, size/1024.0, s?s->logicalpath:"??"); else - Con_Printf("%s \t(%ub) (%s)\n", name, (unsigned int)size, s?s->logicalpath:"??"); + Con_Printf(U8("%s \t(%ub) (%s)\n"), name, (unsigned int)size, s?s->logicalpath:"??"); return 1; } @@ -636,6 +681,9 @@ static void COM_CreatePath (char *path) { char *ofs; + if (fs_readonly) + return; + for (ofs = path+1 ; *ofs ; ofs++) { if (*ofs == '/') @@ -731,7 +779,7 @@ static void QDECL FS_AddFileHash(int depth, const char *fname, fsbucket_t *fileh { fsbucket_t *old; - old = Hash_GetInsensativeBucket(&filesystemhash, fname); + old = Hash_GetInsensitiveBucket(&filesystemhash, fname); if (old) { @@ -765,7 +813,7 @@ static void QDECL FS_AddFileHash(int depth, const char *fname, fsbucket_t *fileh } filehandle->depth = depth; - Hash_AddInsensative(&filesystemhash, fname, pathhandle, &filehandle->buck); + Hash_AddInsensitive(&filesystemhash, fname, pathhandle, &filehandle->buck); fs_hash_files++; } @@ -848,7 +896,7 @@ int FS_FLocateFile(const char *filename, FSLF_ReturnType_e returntype, flocation if (com_fs_cache.ival && !com_fschanged) { - pf = Hash_GetInsensative(&filesystemhash, filename); + pf = Hash_GetInsensitive(&filesystemhash, filename); if (!pf) goto fail; } @@ -1198,7 +1246,23 @@ vfsfile_t *VFS_Filter(const char *filename, vfsfile_t *handle) qboolean FS_NativePath(const char *fname, enum fs_relative relativeto, char *out, int outlen) { + char *last; + int i; char cleanname[MAX_QPATH]; + + if (relativeto == FS_SYSTEM) + { + //system is already the native path. we can just pass it through. perhaps we should clean it up first however, although that's just making sure all \ are / + snprintf(out, outlen, "%s", fname); + + for (; *out; out++) + { + if (*out == '\\') + *out = '/'; + } + return true; + } + fname = FS_GetCleanPath(fname, cleanname, sizeof(cleanname)); if (!fname) return false; @@ -1207,17 +1271,10 @@ qboolean FS_NativePath(const char *fname, enum fs_relative relativeto, char *out { case FS_GAMEONLY: case FS_GAME: - if (*com_homedir) - snprintf(out, outlen, "%s%s/%s", com_homedir, gamedirfile, fname); + if (com_homepathenabled) + snprintf(out, outlen, "%s%s/%s", com_homepath, gamedirfile, fname); else - snprintf(out, outlen, "%s%s/%s", com_quakedir, gamedirfile, fname); - break; - case FS_SKINS: - //FIXME: validate that qw/ is actually loaded and valid - if (*com_homedir) - snprintf(out, outlen, "%sqw/skins/%s", com_homedir, fname); - else - snprintf(out, outlen, "%sqw/skins/%s", com_quakedir, fname); + snprintf(out, outlen, "%s%s/%s", com_gamepath, gamedirfile, fname); break; case FS_BINARYPATH: if (host_parms.binarydir && *host_parms.binarydir) @@ -1226,17 +1283,48 @@ qboolean FS_NativePath(const char *fname, enum fs_relative relativeto, char *out snprintf(out, outlen, "%s%s", host_parms.basedir, fname); break; case FS_ROOT: - if (*com_homedir) - snprintf(out, outlen, "%s%s", com_homedir, fname); + if (com_homepathenabled) + snprintf(out, outlen, "%s%s", com_homepath, fname); else - snprintf(out, outlen, "%s%s", com_quakedir, fname); + snprintf(out, outlen, "%s%s", com_gamepath, fname); break; - case FS_CONFIGONLY: - //FIXME: use the highest-precidence active system path instead - if (*com_homedir) - snprintf(out, outlen, "%sfte/%s", com_homedir, fname); + case FS_BASEGAMEONLY: + last = NULL; + for (i = 0; i < sizeof(fs_manifest->gamepath)/sizeof(fs_manifest->gamepath[0]); i++) + { + if (fs_manifest->gamepath[i].base && fs_manifest->gamepath[i].path) + { + if (!strcmp(fs_manifest->gamepath[i].path, "*")) + continue; + last = fs_manifest->gamepath[i].path; + if (*last == '*') + last++; + } + } + if (!last) + return false; //eep? + if (com_homepathenabled) + snprintf(out, outlen, "%s%s/%s", com_homepath, last, fname); else - snprintf(out, outlen, "%sfte/%s", com_quakedir, fname); + snprintf(out, outlen, "%s%s/%s", com_gamepath, last, fname); + break; + case FS_PUBBASEGAMEONLY: + last = NULL; + for (i = 0; i < sizeof(fs_manifest->gamepath)/sizeof(fs_manifest->gamepath[0]); i++) + { + if (fs_manifest->gamepath[i].base && fs_manifest->gamepath[i].path) + { + if (*fs_manifest->gamepath[i].path == '*') + continue; + last = fs_manifest->gamepath[i].path; + } + } + if (!last) + return false; //eep? + if (com_homepathenabled) + snprintf(out, outlen, "%s%s/%s", com_homepath, last, fname); + else + snprintf(out, outlen, "%s%s/%s", com_gamepath, last, fname); break; default: Sys_Error("FS_NativePath case not handled\n"); @@ -1254,8 +1342,10 @@ vfsfile_t *FS_OpenVFS(const char *filename, const char *mode, enum fs_relative r //eventually, this function will be the *ONLY* way to get at files - //blanket-bans + if (relativeto == FS_SYSTEM) + return VFSOS_Open(filename, mode); + //blanket-bans filename = FS_GetCleanPath(filename, cleanname, sizeof(cleanname)); if (!filename) @@ -1273,21 +1363,21 @@ vfsfile_t *FS_OpenVFS(const char *filename, const char *mode, enum fs_relative r switch (relativeto) { case FS_GAMEONLY: //OS access only, no paks - if (*com_homedir) + if (com_homepathenabled) { - snprintf(fullname, sizeof(fullname), "%s%s/%s", com_homedir, gamedirfile, filename); + snprintf(fullname, sizeof(fullname), "%s%s/%s", com_homepath, gamedirfile, filename); if (*mode == 'w') COM_CreatePath(fullname); vfs = VFSOS_Open(fullname, mode); if (vfs) return vfs; } - snprintf(fullname, sizeof(fullname), "%s%s/%s", com_quakedir, gamedirfile, filename); + snprintf(fullname, sizeof(fullname), "%s%s/%s", com_gamepath, gamedirfile, filename); if (*mode == 'w') COM_CreatePath(fullname); return VFSOS_Open(fullname, mode); case FS_GAME: //load from paks in preference to system paths. overwriting be damned. - case FS_SKINS: //load from paks in preference to system paths. overwriting be damned. + case FS_PUBBASEGAMEONLY: //load from paks in preference to system paths. overwriting be damned. FS_NativePath(filename, relativeto, fullname, sizeof(fullname)); break; case FS_BINARYPATH: @@ -1296,24 +1386,24 @@ vfsfile_t *FS_OpenVFS(const char *filename, const char *mode, enum fs_relative r FS_NativePath(filename, relativeto, fullname, sizeof(fullname)); return VFSOS_Open(fullname, mode); case FS_ROOT: //always bypass packs and gamedirs - if (*com_homedir) + if (com_homepathenabled) { - snprintf(fullname, sizeof(fullname), "%s%s", com_homedir, filename); + snprintf(fullname, sizeof(fullname), "%s%s", com_homepath, filename); vfs = VFSOS_Open(fullname, mode); if (vfs) return vfs; } - snprintf(fullname, sizeof(fullname), "%s%s", com_quakedir, filename); + snprintf(fullname, sizeof(fullname), "%s%s", com_gamepath, filename); return VFSOS_Open(fullname, mode); - case FS_CONFIGONLY: //always bypass packs+pure. - if (*com_homedir) + case FS_BASEGAMEONLY: //always bypass packs+pure. + if (com_homepathenabled) { - snprintf(fullname, sizeof(fullname), "%sfte/%s", com_homedir, filename); + snprintf(fullname, sizeof(fullname), "%sfte/%s", com_homepath, filename); vfs = VFSOS_Open(fullname, mode); if (vfs) return vfs; } - snprintf(fullname, sizeof(fullname), "%sfte/%s", com_quakedir, filename); + snprintf(fullname, sizeof(fullname), "%sfte/%s", com_gamepath, filename); return VFSOS_Open(fullname, mode); default: Sys_Error("FS_OpenVFS: Bad relative path (%i)", relativeto); @@ -1552,7 +1642,7 @@ qbyte *QDECL COM_LoadStackFile (const char *path, void *buffer, int bufsize) /*warning: at some point I'll change this function to return only read-only buffers*/ -qofs_t FS_LoadFile(char *name, void **file) +qofs_t FS_LoadFile(const char *name, void **file) { *file = FS_LoadMallocFile(name); if (!*file) @@ -1688,6 +1778,28 @@ static int QDECL FS_AddWildDataFiles (const char *descriptor, qofs_t size, void return true; } +searchpathfuncs_t *FS_OpenPackByExtension(vfsfile_t *f, const char *pakname) +{ + searchpathfuncs_t *pak; + int j; + char *ext = COM_FileExtension(pakname); + for (j = 0; j < sizeof(searchpathformats)/sizeof(searchpathformats[0]); j++) + { + if (!searchpathformats[j].extension || !searchpathformats[j].OpenNew) + continue; + if (!strcmp(ext, searchpathformats[j].extension)) + { + pak = searchpathformats[j].OpenNew(f, pakname); + if (pak) + return pak; + Con_Printf("Unable to open %s - corrupt?\n", pakname); + break; + } + } + + VFS_CLOSE(f); + return NULL; +} static void FS_AddDataFiles(searchpath_t **oldpaths, const char *purepath, const char *logicalpath, searchpath_t *search, const char *extension, searchpathfuncs_t *(QDECL *OpenNew)(vfsfile_t *file, const char *desc)) { @@ -1897,7 +2009,7 @@ Sets com_gamedir, adds the directory to the head of the path, then loads and adds pak1.pak pak2.pak ... ================ */ -void FS_AddGameDirectory (searchpath_t **oldpaths, const char *puredir, const char *dir, unsigned int loadstuff) +void FS_AddGameDirectory (searchpath_t **oldpaths, const char *puredir, const char *dir, unsigned int loadstuff, unsigned int flags) { unsigned int keptflags; searchpath_t *search; @@ -1907,17 +2019,24 @@ void FS_AddGameDirectory (searchpath_t **oldpaths, const char *puredir, const ch fs_restarts++; - if ((p = strrchr(dir, '/')) != NULL) - strcpy(gamedirfile, ++p); - else - strcpy(gamedirfile, dir); - for (search = com_searchpaths; search; search = search->next) { if (!Q_strcasecmp(search->logicalpath, dir)) return; //already loaded (base paths?) } + if (!(flags & SPF_PRIVATE)) + { + if ((p = strrchr(dir, '/')) != NULL) + strcpy(pubgamedirfile, ++p); + else + strcpy(pubgamedirfile, dir); + } + if ((p = strrchr(dir, '/')) != NULL) + strcpy(gamedirfile, ++p); + else + strcpy(gamedirfile, dir); + // // add the directory to the search path // @@ -1925,7 +2044,7 @@ void FS_AddGameDirectory (searchpath_t **oldpaths, const char *puredir, const ch if (!handle) handle = VFSOS_OpenPath(NULL, dir); - FS_AddPathHandle(oldpaths, puredir, dir, handle, SPF_EXPLICIT|keptflags, loadstuff); + FS_AddPathHandle(oldpaths, puredir, dir, handle, flags|keptflags, loadstuff); } searchpathfuncs_t *COM_IteratePaths (void **iterator, char *buffer, int buffersize) @@ -1954,9 +2073,21 @@ searchpathfuncs_t *COM_IteratePaths (void **iterator, char *buffer, int buffersi return NULL; } -char *FS_GetGamedir(void) +char *FS_GetGamedir(qboolean publicpathonly) { - return gamedirfile; + if (publicpathonly) + return pubgamedirfile; + else + return gamedirfile; +} + +//returns the commandline arguments required to duplicate the fs details +char *FS_GetManifestArgs(void) +{ + if (fs_manifest->updatefile) + return va("-manifest %s -basedir %s", fs_manifest->updatefile, com_gamepath); + + return va("-game %s -basedir %s", pubgamedirfile, com_gamepath); } //given a 'c:/foo/bar/' path, will extract 'bar'. @@ -2118,16 +2249,16 @@ void COM_Gamedir (const char *dir) if (!*dir) continue; - FS_AddGameDirectory(dir, va("%s%s", com_quakedir, com_token), ~0); - if (*com_homedir) - FS_AddGameDirectory(dir, va("%s%s", com_homedir, com_token), ~0); + FS_AddGameDirectory(dir, va("%s%s", com_gamepath, com_token), ~0, SPF_EXPLICIT); + if (com_homepathenabled) + FS_AddGameDirectory(dir, va("%s%s", com_homepath, com_token), ~0, SPF_EXPLICIT); } } else { - FS_AddGameDirectory(dir, va("%s%s", com_quakedir, dir), ~0); - if (*com_homedir) - FS_AddGameDirectory(dir, va("%s%s", com_homedir, dir), ~0); + FS_AddGameDirectory(dir, va("%s%s", com_gamepath, dir), ~0, SPF_EXPLICIT); + if (com_homepathenabled) + FS_AddGameDirectory(dir, va("%s%s", com_homepath, dir), ~0, SPF_EXPLICIT); } @@ -2138,7 +2269,7 @@ void COM_Gamedir (const char *dir) // Cbuf_InsertText("vid_restart\n", RESTRICT_LOCAL); - if (COM_FDepthFile("config.cfg", true) <= (*com_homedir?1:0)) + if (COM_FDepthFile("config.cfg", true) <= (com_homepathenabled?1:0)) { Cbuf_InsertText("cl_warncmd 0\n" "exec config.cfg\n" @@ -2167,7 +2298,7 @@ void COM_Gamedir (const char *dir) /*some modern non-compat settings*/ #define DMFCFG "set com_parseutf8 1\npm_airstep 1\nsv_demoExtensions 1\n" /*set some stuff so our regular qw client appears more like hexen2*/ -#define HEX2CFG "set com_parseutf8 -1\nset gl_font gfx/hexen2\nset in_builtinkeymap 0\nset_calc cl_playerclass int (random * 5) + 1\nset sv_maxspeed 640\nset watervis 1\nset r_wateralpha 0.5\nset sv_pupglow 1\nset cl_model_bobbing 1\nsv_sound_land \"fx/thngland.wav\"\n" +#define HEX2CFG "set com_parseutf8 -1\nset gl_font gfx/hexen2\nset in_builtinkeymap 0\nset_calc cl_playerclass int (random * 5) + 1\nset sv_maxspeed 640\ncl_run 0\nset watervis 1\nset r_wateralpha 0.5\nset sv_pupglow 1\nset cl_model_bobbing 1\nsv_sound_land \"fx/thngland.wav\"\n" /*yay q2!*/ #define Q2CFG "com_nogamedirnativecode 0\n" /*Q3's ui doesn't like empty model/headmodel/handicap cvars, even if the gamecode copes*/ @@ -2197,42 +2328,47 @@ const gamemode_info_t gamemode_info[] = { //cmdline switch exename protocol name(dpmaster) identifying file exec dir1 dir2 dir3 dir(fte) full name {"-quake", "q1", MASTER_PREFIX"Quake", {"id1/pak0.pak", - "id1/quake.rc"}, QCFG, {"id1", "qw", "fte"}, "Quake"/*, "id1/pak0.pak|http://quakeservers.nquake.com/qsw106.zip|http://nquake.localghost.net/qsw106.zip|http://qw.quakephil.com/nquake/qsw106.zip|http://fnu.nquake.com/qsw106.zip"*/}, - {"-hipnotic", "hipnotic", MASTER_PREFIX"Hipnotic",{"hipnotic/pak0.pak"}, QCFG, {"id1", "qw", "hipnotic", "fte"}, "Quake: Scourge of Armagon"}, - {"-rogue", "rogue", MASTER_PREFIX"Rogue", {"rogue/pak0.pak"}, QCFG, {"id1", "qw", "rogue", "fte"}, "Quake: Dissolution of Eternity"}, - {"-nexuiz", "nexuiz", "Nexuiz", {"nexuiz.exe"}, NEXCFG, {"data", "ftedata"}, "Nexuiz"}, - {"-xonotic", "xonotic", "Xonotic", {"xonotic.exe"}, NEXCFG, {"data", "ftedata"}, "Xonotic"}, + "id1/quake.rc"}, QCFG, {"id1", "qw", "*fte"}, "Quake"/*, "id1/pak0.pak|http://quakeservers.nquake.com/qsw106.zip|http://nquake.localghost.net/qsw106.zip|http://qw.quakephil.com/nquake/qsw106.zip|http://fnu.nquake.com/qsw106.zip"*/}, + {"-hipnotic", "hipnotic", MASTER_PREFIX"Hipnotic",{"hipnotic/pak0.pak"}, QCFG, {"id1", "qw", "hipnotic", "*fte"}, "Quake: Scourge of Armagon"}, + {"-rogue", "rogue", MASTER_PREFIX"Rogue", {"rogue/pak0.pak"}, QCFG, {"id1", "qw", "rogue", "*fte"}, "Quake: Dissolution of Eternity"}, + + //various quake-based mods. + {"-nexuiz", "nexuiz", "Nexuiz", {"nexuiz.exe"}, NEXCFG, {"data", "*ftedata"}, "Nexuiz"}, + {"-xonotic", "xonotic", "Xonotic", {"xonotic.exe"}, NEXCFG, {"data", "*ftedata"}, "Xonotic"}, {"-spark", "spark", "Spark", {"base/src/progs.src", "base/qwprogs.dat", "base/pak0.pak"}, DMFCFG, {"base", }, "Spark"}, {"-scouts", "scouts", "FTE-SJ", {"basesj/src/progs.src", "basesj/progs.dat", "basesj/pak0.pak"}, NULL, {"basesj", }, "Scouts Journey"}, - {"-rmq", "rmq", "RMQ", {NULL}, RMQCFG, {"id1", "qw", "rmq", "fte"}, "Remake Quake"}, + {"-rmq", "rmq", "RMQ", {NULL}, RMQCFG, {"id1", "qw", "rmq", "*fte"}, "Remake Quake"}, //supported commercial mods (some are currently only partially supported) {"-portals", "h2mp", "FTE-H2MP", {"portals/hexen.rc", - "portals/pak3.pak"}, HEX2CFG,{"data1", "portals", "fteh2"}, "Hexen II MP"}, - {"-hexen2", "hexen2", "FTE-Hexen2", {"data1/pak0.pak"}, HEX2CFG,{"data1", "fteh2"}, "Hexen II"}, - {"-quake2", "q2", "FTE-Quake2", {"baseq2/pak0.pak"}, Q2CFG, {"baseq2", "fteq2"}, "Quake II"}, - {"-quake3", "q3", "FTE-Quake3", {"baseq3/pak0.pk3"}, Q3CFG, {"baseq3", "fteq3"}, "Quake III Arena"}, + "portals/pak3.pak"}, HEX2CFG,{"data1", "portals", "*fteh2"}, "Hexen II MP"}, + {"-hexen2", "hexen2", "FTE-Hexen2", {"data1/pak0.pak"}, HEX2CFG,{"data1", "*fteh2"}, "Hexen II"}, + {"-quake2", "q2", "FTE-Quake2", {"baseq2/pak0.pak"}, Q2CFG, {"baseq2", "*fteq2"}, "Quake II"}, + {"-quake3", "q3", "FTE-Quake3", {"baseq3/pak0.pk3"}, Q3CFG, {"baseq3", "*fteq3"}, "Quake III Arena"}, + + //mods of the above that should generally work. + {"-dday", "dday", "FTE-Quake2", {"dday/pak0.pak"}, Q2CFG, {"baseq2", "dday", "*fteq2"}, "D-Day: Normandy"}, //can run in windows, needs - {"-halflife", "hl", "FTE-HalfLife", {"valve/liblist.gam"}, NULL, {"valve", "ftehl"}, "Half-Life"}, + {"-halflife", "hl", "FTE-HalfLife", {"valve/liblist.gam"}, NULL, {"valve", "*ftehl"}, "Half-Life"}, //the rest are not supported in any real way. maps-only mostly, if that - {"-quake4", "q4", "FTE-Quake4", {"q4base/pak00.pk4"}, NULL, {"q4base", "fteq4"}, "Quake 4"}, - {"-et", "et", "FTE-EnemyTerritory", {"etmain/pak0.pk3"}, NULL, {"etmain", "fteet"}, "Wolfenstein - Enemy Territory"}, + {"-quake4", "q4", "FTE-Quake4", {"q4base/pak00.pk4"}, NULL, {"q4base", "*fteq4"}, "Quake 4"}, + {"-et", "et", "FTE-EnemyTerritory", {"etmain/pak0.pk3"}, NULL, {"etmain", "*fteet"}, "Wolfenstein - Enemy Territory"}, - {"-jk2", "jk2", "FTE-JK2", {"base/assets0.pk3"}, NULL, {"base", "ftejk2"}, "Jedi Knight II: Jedi Outcast"}, - {"-warsow", "warsow", "FTE-Warsow", {"basewsw/pak0.pk3"}, NULL, {"basewsw", "ftewsw"}, "Warsow"}, + {"-jk2", "jk2", "FTE-JK2", {"base/assets0.pk3"}, NULL, {"base", "*ftejk2"}, "Jedi Knight II: Jedi Outcast"}, + {"-warsow", "warsow", "FTE-Warsow", {"basewsw/pak0.pk3"}, NULL, {"basewsw", "*ftewsw"}, "Warsow"}, - {"-doom", "doom", "FTE-Doom", {"doom.wad"}, NULL, {"*doom.wad", "ftedoom"}, "Doom"}, - {"-doom2", "doom2", "FTE-Doom2", {"doom2.wad"}, NULL, {"*doom2.wad", "ftedoom"}, "Doom2"}, - {"-doom3", "doom3", "FTE-Doom3", {"doom3.wad"}, NULL, {"based3", "ftedoom3"},"Doom3"}, + {"-doom", "doom", "FTE-Doom", {"doom.wad"}, NULL, {"*", "*ftedoom"}, "Doom"}, + {"-doom2", "doom2", "FTE-Doom2", {"doom2.wad"}, NULL, {"*", "*ftedoom"}, "Doom2"}, + {"-doom3", "doom3", "FTE-Doom3", {"doom3.wad"}, NULL, {"based3", "*ftedoom3"},"Doom3"}, //for the luls - {"-diablo2", NULL, "FTE-Diablo2", {"d2music.mpq"}, NULL, {"**.mpq", "fted2"}, "Diablo 2"}, + {"-diablo2", NULL, "FTE-Diablo2", {"d2music.mpq"}, NULL, {"*", "*fted2"}, "Diablo 2"}, {NULL} }; @@ -2293,7 +2429,7 @@ qboolean FS_LoadPackageFromFile(vfsfile_t *vfs, char *pname, char *localname, in if (truecrc != *crc) { *crc = truecrc; - VFS_CLOSE(vfs); + handle->ClosePath(handle); return false; } } @@ -2312,6 +2448,87 @@ qboolean FS_LoadPackageFromFile(vfsfile_t *vfs, char *pname, char *localname, in } #endif +//'small' wrapper to open foo.zip/bar to read files within zips that are not part of the gamedir. +//name needs to be null terminated. recursive. pass null for search. +vfsfile_t *CL_OpenFileInPackage(searchpathfuncs_t *search, char *name) +{ + int found; + vfsfile_t *f; + flocation_t loc; + char e, *n; + char *ext; + char *end; + int i; + + end = name + strlen(name); + + while (end > name) + { + e = *end; + *end = 0; + + if (!e) + { + //always open the last file properly. + loc.search = NULL; + if (search) + found = search->FindFile(search, &loc, name, NULL); + else + found = FS_FLocateFile(name, FSLFRT_IFFOUND, &loc); + if (found) + { + f = (search?search:loc.search->handle)->OpenVFS(search?search:loc.search->handle, &loc, "rb"); + if (f) + return f; + } + } + else + { + ext = COM_FileExtension(name); + for (i = 0; i < sizeof(searchpathformats)/sizeof(searchpathformats[0]); i++) + { + if (!searchpathformats[i].extension || !searchpathformats[i].OpenNew) + continue; + if (!strcmp(ext, searchpathformats[i].extension)) + { + loc.search = NULL; + if (search) + found = search->FindFile(search, &loc, name, NULL); + else + found = FS_FLocateFile(name, FSLFRT_IFFOUND, &loc); + if (found) + { + f = (search?search:loc.search->handle)->OpenVFS(search?search:loc.search->handle, &loc, "rb"); + if (f) + { + searchpathfuncs_t *newsearch = searchpathformats[i].OpenNew(f, name); + if (newsearch) + { + f = CL_OpenFileInPackage(newsearch, end+1); + newsearch->ClosePath(newsearch); + if (f) + { + *end = e; + return f; + } + } + else + VFS_CLOSE(f); + } + break; + } + } + } + } + + n = COM_SkipPath(name); + *end = e; + end = n-1; + } + return NULL; +} + + void FS_PureMode(int puremode, char *packagenames, char *packagecrcs, int pureseed) { qboolean pureflush; @@ -2390,6 +2607,18 @@ void FS_ReloadPackFilesFlags(unsigned int reloadflags) com_purepaths = NULL; com_base_searchpaths = NULL; + i = COM_CheckParm ("-basepack"); + while (i && i < com_argc-1) + { + const char *pakname = com_argv[i+1]; + searchpathfuncs_t *pak; + vfsfile_t *vfs = VFSOS_Open(pakname, "rb"); + pak = FS_OpenPackByExtension(vfs, pakname); + if (pak) + FS_AddPathHandle(&oldpaths, "", pakname, pak, SPF_COPYPROTECTED|SPF_EXPLICIT, reloadflags); + i = COM_CheckNextParm ("-basepack", i); + } + for (i = 0; i < sizeof(fs_manifest->gamepath) / sizeof(fs_manifest->gamepath[0]); i++) { char *dir = fs_manifest->gamepath[i].path; @@ -2404,16 +2633,16 @@ void FS_ReloadPackFilesFlags(unsigned int reloadflags) continue; } - //paths with '*' actually result in loading packages without an actual gamedir. note that this does not imply that we can write anything. - if (*dir == '*') + //paths equal to '*' actually result in loading packages without an actual gamedir. note that this does not imply that we can write anything. + if (!strcmp(dir, "*")) { int j; - searchpathfuncs_t *handle = VFSOS_OpenPath(NULL, com_quakedir); + searchpathfuncs_t *handle = VFSOS_OpenPath(NULL, com_gamepath); searchpath_t *search = (searchpath_t*)Z_Malloc (sizeof(searchpath_t)); search->flags = 0; search->handle = handle; Q_strncpyz(search->purepath, "", sizeof(search->purepath)); - Q_strncpyz(search->logicalpath, com_quakedir, sizeof(search->logicalpath)); + Q_strncpyz(search->logicalpath, com_gamepath, sizeof(search->logicalpath)); for (j = 0; j < sizeof(searchpathformats)/sizeof(searchpathformats[0]); j++) { @@ -2427,11 +2656,18 @@ void FS_ReloadPackFilesFlags(unsigned int reloadflags) handle->ClosePath(handle); Z_Free(search); } + else if (*dir == '*') + { + //paths with a leading * are private, and not announced to clients that ask what the current gamedir is. + FS_AddGameDirectory(&oldpaths, dir+1, va("%s%s", com_gamepath, dir+1), reloadflags, SPF_EXPLICIT|SPF_PRIVATE); + if (com_homepathenabled) + FS_AddGameDirectory(&oldpaths, dir+1, va("%s%s", com_homepath, dir+1), reloadflags, SPF_EXPLICIT|SPF_PRIVATE); + } else { - FS_AddGameDirectory(&oldpaths, dir, va("%s%s", com_quakedir, dir), reloadflags); - if (*com_homedir) - FS_AddGameDirectory(&oldpaths, dir, va("%s%s", com_homedir, dir), reloadflags); + FS_AddGameDirectory(&oldpaths, dir, va("%s%s", com_gamepath, dir), reloadflags, SPF_EXPLICIT); + if (com_homepathenabled) + FS_AddGameDirectory(&oldpaths, dir, va("%s%s", com_homepath, dir), reloadflags, SPF_EXPLICIT); } } } @@ -2455,9 +2691,9 @@ void FS_ReloadPackFilesFlags(unsigned int reloadflags) } else { - FS_AddGameDirectory(&oldpaths, dir, va("%s%s", com_quakedir, dir), reloadflags); - if (*com_homedir) - FS_AddGameDirectory(&oldpaths, dir, va("%s%s", com_homedir, dir), reloadflags); + FS_AddGameDirectory(&oldpaths, dir, va("%s%s", com_gamepath, dir), reloadflags, SPF_EXPLICIT); + if (com_homepathenabled) + FS_AddGameDirectory(&oldpaths, dir, va("%s%s", com_homepath, dir), reloadflags, SPF_EXPLICIT); } } } @@ -2608,8 +2844,8 @@ void FS_ReloadPackFiles_f(void) FS_ReloadPackFilesFlags(~0); } -#ifdef _WIN32 -#include +#if defined(_WIN32) && !defined(WINRT) +#include "winquake.h" #ifdef MINGW #define byte BYTE //some versions of mingw headers are broken slightly. this lets it compile. #endif @@ -2810,7 +3046,7 @@ qboolean Sys_FindGameData(const char *poshname, const char *gamename, char *base } #if !defined(NPFTE) && !defined(SERVERONLY) //this is *really* unfortunate, but doing this crashes the browser - if (poshname && !COM_CheckParm("-manifest")) + if (poshname && *gamename && !COM_CheckParm("-manifest")) { char resultpath[MAX_PATH]; BROWSEINFO bi; @@ -2908,160 +3144,6 @@ void FS_Shutdown(void) fs_manifest = NULL; } -#if 0 -static void FS_AddGamePack(const char *pakname) -{ - int j; - char *ext = COM_FileExtension(pakname); - vfsfile_t *vfs = VFSOS_Open(pakname, "rb"); - void *pak; - searchpath_t *oldlist = NULL; - if (!vfs) - Con_Printf("Unable to open %s - missing?\n", pakname); - else - { - for (j = 0; j < sizeof(searchpathformats)/sizeof(searchpathformats[0]); j++) - { - if (!searchpathformats[j].extension || !searchpathformats[j].OpenNew) - continue; - if (!strcmp(ext, searchpathformats[j].extension)) - { - pak = searchpathformats[j].OpenNew(vfs, pakname); - if (pak) - { - FS_AddPathHandle(&oldlist, "", pakname, pak, SPF_COPYPROTECTED|SPF_EXPLICIT, (unsigned int)-1); - } - else - { - Con_Printf("Unable to open %s - corrupt?\n", pakname); - VFS_CLOSE(vfs); - } - vfs = NULL; - break; - } - } - if (vfs) - { - VFS_CLOSE(vfs); - Con_Printf("Unable to open %s - unsupported?\n", pakname); - } - } -} - -static void FS_StartupWithGame(int gamenum) -{ - int i; - searchpath_t *oldlist = NULL; - -#ifdef AVAIL_ZLIB - LibZ_Init(); -#endif - - Cvar_Set(&com_protocolname, gamemode_info[gamenum].protocolname); - Cvar_ForceSet(&fs_gamename, gamemode_info[gamenum].poshname); -// Cvar_ForceSet(&fs_gamemanifest, gamemode_info[gamenum].manifestaddr?gamemode_info[gamenum].manifestaddr:""); - - i = COM_CheckParm ("-basepack"); - while (i && i < com_argc-1) - { -// Con_Printf("found -basepack: %s\n", com_argv[i+1]); - FS_AddGamePack(com_argv[i+1]); - i = COM_CheckNextParm ("-basepack", i); - } - -// -// start up with id1 by default -// - i = COM_CheckParm ("-basegame"); - if (i && i < com_argc-1) - { - do //use multiple -basegames - { - FS_AddGameDirectory (&oldlist, com_argv[i+1], va("%s%s", com_quakedir, com_argv[i+1]), ~0); - if (*com_homedir) - FS_AddGameDirectory (&oldlist, com_argv[i+1], va("%s%s", com_homedir, com_argv[i+1]), ~0); - - i = COM_CheckNextParm ("-basegame", i); - } - while (i && i < com_argc-1); - } - else - { - for (i = 0; i < sizeof(gamemode_info[gamenum].dir)/sizeof(gamemode_info[gamenum].dir[0]); i++) - { - if (gamemode_info[gamenum].dir[i] && *gamemode_info[gamenum].dir[i] == '*') - { - char buf[MAX_OSPATH]; - snprintf(buf, sizeof(buf), "%s%s", com_quakedir, gamemode_info[gamenum].dir[i]+1); - FS_AddGamePack(buf); - } - else if (gamemode_info[gamenum].dir[i]) - { - FS_AddGameDirectory (&oldlist, gamemode_info[gamenum].dir[i], va("%s%s", com_quakedir, gamemode_info[gamenum].dir[i]), ~0); - if (*com_homedir) - FS_AddGameDirectory (&oldlist, gamemode_info[gamenum].dir[i], va("%s%s", com_homedir, gamemode_info[gamenum].dir[i]), ~0); - } - } - } - - i = COM_CheckParm ("-addbasegame"); - while (i && i < com_argc-1) //use multiple -addbasegames (this is so the basic dirs don't die) - { - //reject various evil path arguments. - if (*com_argv[i+1] && !(strchr(com_argv[i+1], '.') || strchr(com_argv[i+1], ':') || strchr(com_argv[i+1], '?') || strchr(com_argv[i+1], '*') || strchr(com_argv[i+1], '/') || strchr(com_argv[i+1], '\\') || strchr(com_argv[i+1], '$'))) - { - FS_AddGameDirectory (&oldlist, com_argv[i+1], va("%s%s", com_quakedir, com_argv[i+1]), ~0); - if (*com_homedir) - FS_AddGameDirectory (&oldlist, com_argv[i+1], va("%s%s", com_homedir, com_argv[i+1]), ~0); - } - - i = COM_CheckNextParm ("-addbasegame", i); - } - - // any set gamedirs will be freed up to here - com_base_searchpaths = com_searchpaths; - - //-game specifies the mod gamedir to use in NQ - i = COM_CheckParm ("-game"); //effectivly replace with +gamedir x (But overridable) - if (i && i < com_argc-1) - { - COM_Gamedir(com_argv[i+1]); -#ifndef CLIENTONLY - Info_SetValueForStarKey (svs.info, "*gamedir", com_argv[i+1], MAX_SERVERINFO_STRING); -#endif - } - - //+gamedir specifies the mod gamedir to use in QW - //hack - we parse the commandline after the config so commandline always overrides - //but this means ktpro/server.cfg (for example) is not found - //so if they specify a gamedir on the commandline, let the default configs be loaded from that gamedir - //note that -game +gamedir will result in both being loaded. but hey, who cares - i = COM_CheckParm ("+gamedir"); //effectivly replace with +gamedir x (But overridable) - if (i && i < com_argc-1) - { - COM_Gamedir(com_argv[i+1]); -#ifndef CLIENTONLY - Info_SetValueForStarKey (svs.info, "*gamedir", com_argv[i+1], MAX_SERVERINFO_STRING); -#endif - } - -#ifdef ANDROID - { - vfsfile_t *f; - //write a .nomedia file to avoid people from getting random explosion sounds etc intersperced with their music - f = FS_OpenVFS(".nomedia", "rb", FS_ROOT); - if (f) - VFS_CLOSE(f); - else - FS_WriteFile(".nomedia", NULL, 0, FS_ROOT); - } -#endif - - if (gamemode_info[gamenum].customexec) - Cbuf_AddText(gamemode_info[gamenum].customexec, RESTRICT_LOCAL); -} -#endif - static qboolean FS_DirHasAPackage(char *basedir, ftemanifest_t *man) { int j; @@ -3118,7 +3200,7 @@ static int FS_IdentifyDefaultGameFromDir(char *basedir) //3: if we are ftequake3.exe then we always try to run quake3. //4: identify characteristic files within the working directory (like id1/pak0.pak implies we're running quake) //5: check where the exe actually is instead of simply where we're being run from. -//6: fallback to quake +//6: fallback to prompting. just returns -1 here. //if autobasedir is not set, block gamedir changes/prompts. static int FS_IdentifyDefaultGame(char *newbase, int sizeof_newbase, qboolean fixedbase) { @@ -3160,35 +3242,22 @@ static int FS_IdentifyDefaultGame(char *newbase, int sizeof_newbase, qboolean fi if (gamenum != -1) Q_strncpyz(newbase, host_parms.binarydir, sizeof_newbase); } - - //still failed? find quake and use that one by default - if (gamenum<0) - { - for (i = 0; gamemode_info[i].argname; i++) - { - if (!strcmp(gamemode_info[i].argname, "-quake")) - { - gamenum = i; - break; - } - } - } return gamenum; } +static + //allowed to modify newbasedir if fixedbasedir isn't set ftemanifest_t *FS_GenerateLegacyManifest(char *newbasedir, int sizeof_newbasedir, qboolean fixedbasedir, int game) { int i; ftemanifest_t *man; - if (game == -1) - game = FS_IdentifyDefaultGame(newbasedir, sizeof_newbasedir, fixedbasedir); if (gamemode_info[game].manifestfile) - man = FS_Manifest_Parse(gamemode_info[game].manifestfile); + man = FS_Manifest_Parse(NULL, gamemode_info[game].manifestfile); else { - man = FS_Manifest_Create(); + man = FS_Manifest_Create(NULL); Cmd_TokenizeString(va("game \"%s\"", gamemode_info[game].argname+1), false, false); FS_Manifest_ParseTokens(man); @@ -3317,19 +3386,30 @@ static void FS_BeginNextPackageDownload(void) if (!man->package[j].path) continue; + //check if we already have a version of the pak. if the user installed one, don't corrupt it with some unwanted pak. this may cause problems but whatever, user versitility wins. + //this matches the rules for loading packs too. double is utterly pointless. + check = FS_OpenVFS(man->package[j].path, "rb", FS_ROOT); + if (check) + { + VFS_CLOSE(check); + continue; + } + + //figure out what the cached name should be and see if we already have that or not if (man->package[j].crcknown) crcstr = va("%#x", man->package[j].crc); else crcstr = ""; if (!FS_GenCachedPakName(man->package[j].path, crcstr, buffer, sizeof(buffer))) continue; - check = FS_OpenVFS(buffer, "rb", FS_ROOT); if (check) { VFS_CLOSE(check); continue; } + + //figure out a temp name and figure out where we're going to get it from. FS_NativePath(buffer, FS_ROOT, fspdl_finalpath, sizeof(fspdl_finalpath)); if (!FS_GenCachedPakName(va("%s.tmp", man->package[j].path), crcstr, buffer, sizeof(buffer))) continue; @@ -3350,6 +3430,7 @@ static void FS_BeginNextPackageDownload(void) if (!url) continue; + Con_Printf("Downloading %s from %s\n", fspdl_finalpath, url); curpackagedownload = HTTP_CL_Get(url, NULL, FS_PackageDownloaded); if (curpackagedownload) { @@ -3359,8 +3440,95 @@ static void FS_BeginNextPackageDownload(void) } } } +static void FS_ManifestUpdated(struct dl_download *dl) +{ + ftemanifest_t *man = fs_manifest; + + curpackagedownload = NULL; + + if (dl->file) + { + if (dl->user_ctx == man) + { + size_t len = VFS_GETLEN(dl->file), len2; + char *fdata = BZ_Malloc(len+1), *fdata2 = NULL; + if (fdata) + { + VFS_READ(dl->file, fdata, len); + fdata[len] = 0; + man = FS_Manifest_Parse(fs_manifest->updatefile, fdata); + if (man) + { + //the updateurl MUST match the current one in order for the local version of the manifest to be saved (to avoid extra updates, and so it appears in the menu_mods) + //this is a paranoia measure to avoid too much damage from buggy/malicious proxies that return empty pages or whatever. + if (man->updateurl && fs_manifest->updateurl && !strcmp(man->updateurl, fs_manifest->updateurl)) + { + man->blockupdate = true; //don't download it multiple times. that's just crazy. + if (man->updatefile) + { + vfsfile_t *f2 = FS_OpenVFS(fs_manifest->updatefile, "rb", FS_SYSTEM); + if (f2) + { + len2 = VFS_GETLEN(f2); + if (len != len2) + { + fdata2 = NULL; + len2 = 0; + } + else + { + fdata2 = BZ_Malloc(len2); + VFS_READ(f2, fdata2, len2); + } + VFS_CLOSE(f2); + if (len == len2 && !memcmp(fdata, fdata2, len)) + { + //files match, no need to use this new manifest at all. + FS_Manifest_Free(man); + man = NULL; + } + BZ_Free(fdata2); + } + if (man) + FS_WriteFile(man->updatefile, fdata, len, FS_SYSTEM); + } + if (man) + FS_ChangeGame(man, true); + } + else + FS_Manifest_Free(man); + } + BZ_Free(fdata); + } + } + + VFS_CLOSE(dl->file); + dl->file = NULL; + } + + FS_BeginNextPackageDownload(); +} +void FS_BeginManifestUpdates(void) +{ + ftemanifest_t *man = fs_manifest; + if (curpackagedownload || !man) + return; + + if (man->updateurl && !man->blockupdate) + { + man->blockupdate = true; + Con_Printf("Updating manifest from %s\n", man->updateurl); + curpackagedownload = HTTP_CL_Get(man->updateurl, NULL, FS_ManifestUpdated); + if (curpackagedownload) + curpackagedownload->user_ctx = man; + } + + //urr, failed for some reason (like the url not specified?) + if (!curpackagedownload) + FS_BeginNextPackageDownload(); +} #else -void FS_BeginNextPackageDownload(void) +void FS_BeginManifestUpdates(void) { } #endif @@ -3399,7 +3567,41 @@ qboolean FS_ChangeGame(ftemanifest_t *man, qboolean allowreloadconfigs) if (fs_manifest) return false; - man = FS_GenerateLegacyManifest(newbasedir, sizeof(newbasedir), fixedbasedir, -1); + if (!man) + { + vfsfile_t *f; + f = VFSOS_Open(va("%sdefault.fmf", newbasedir), "rb"); + if (f) + { + size_t len = VFS_GETLEN(f); + char *fdata = BZ_Malloc(len+1); + if (fdata) + { + VFS_READ(f, fdata, len); + fdata[len] = 0; + man = FS_Manifest_Parse(NULL, fdata); + BZ_Free(fdata); + } + VFS_CLOSE(f); + } + } + + if (!man) + { + int game = FS_IdentifyDefaultGame(newbasedir, sizeof(newbasedir), fixedbasedir); + if (game != -1) + man = FS_GenerateLegacyManifest(newbasedir, sizeof(newbasedir), fixedbasedir, game); + } + + if (!man) + { + man = FS_Manifest_Parse(NULL, + "FTEMANIFEST 1\n" + "game \"\"\n" + "name \"" FULLENGINENAME "\"\n" + "defaultexec \\\"vid_fullscreen 0; gl_font cour;vid_width 640; vid_height 480; menu_mods\"\n" + ); + } } if (man == fs_manifest) @@ -3465,9 +3667,12 @@ qboolean FS_ChangeGame(ftemanifest_t *man, qboolean allowreloadconfigs) if (Sys_FindGameData(man->formalname, man->installation, realpath, sizeof(realpath))) Q_strncpyz (newbasedir, realpath, sizeof(newbasedir)); - Q_strncpyz (com_quakedir, newbasedir, sizeof(com_quakedir)); + Q_strncpyz (com_gamepath, newbasedir, sizeof(com_gamepath)); //make sure it has a trailing slash, or is empty. woo. - FS_CleanDir(com_quakedir, sizeof(com_quakedir)); + FS_CleanDir(com_gamepath, sizeof(com_gamepath)); + + if (man->disablehomedir && !COM_CheckParm("-usehome")) + com_homepathenabled = false; #ifdef ANDROID { @@ -3483,7 +3688,7 @@ qboolean FS_ChangeGame(ftemanifest_t *man, qboolean allowreloadconfigs) FS_ReloadPackFilesFlags(~0); - FS_BeginNextPackageDownload(); + FS_BeginManifestUpdates(); COM_CheckRegistered(); @@ -3523,20 +3728,124 @@ qboolean FS_ChangeGame(ftemanifest_t *man, qboolean allowreloadconfigs) //rebuild the cache now, should be safe to waste some cycles on it FS_RebuildFSHash(); - COM_Effectinfo_Clear(); +// COM_Effectinfo_Clear(); #ifndef SERVERONLY Validation_FlushFileList(); //prevent previous hacks from making a difference. #endif - Cvar_ForceSet(&fs_game, FS_GetGamedir()); + { + void (*callback)(struct cvar_s *var, char *oldvalue) = fs_game.callback; + fs_game.callback = NULL; + Cvar_ForceSet(&fs_game, FS_GetGamedir(false)); + fs_game.callback = callback; + } #ifdef Q2SERVER - Cvar_ForceSet(&fs_gamedir, va("%s%s", com_quakedir, FS_GetGamedir())); - Cvar_ForceSet(&fs_basedir, com_quakedir); + Cvar_ForceSet(&fs_gamedir, va("%s%s", com_gamepath, FS_GetGamedir(false))); + Cvar_ForceSet(&fs_basedir, com_gamepath); #endif return true; } +typedef struct +{ + int found; + qboolean (*callback)(void *usr, ftemanifest_t *man); + void *usr; +} fmfenums_t; +static int QDECL FS_EnumerateFMFs(const char *fname, qofs_t fsize, void *inf, searchpathfuncs_t *spath) +{ + fmfenums_t *e = inf; + vfsfile_t *f = NULL; + char *homem = va("%s%s", com_homepathenabled?com_homepath:com_gamepath, COM_SkipPath(fname)); + if (!f) //always try the homedir first, because that can be updated automagically. + f = VFSOS_Open(fname, "rb"); + if (!f) + { //*then* try in packages or basedir etc. + if (spath) + { + flocation_t loc; + if (spath->FindFile(spath, &loc, fname, NULL)) + f = spath->OpenVFS(spath, &loc, "rb"); + } + else + f = VFSOS_Open(fname, "rb"); + } + if (f) + { + size_t l = VFS_GETLEN(f); + char *data = Z_Malloc(l+1); + if (data) + { + ftemanifest_t *man; + VFS_READ(f, data, l); + data[l] = 0; //just in case. + + man = FS_Manifest_Parse(homem, data); + if (man) + { + if (e->callback(e->usr, man)) + e->found++; + else + FS_Manifest_Free(man); + } + Z_Free(data); + } + VFS_CLOSE(f); + } + + return true; +} + +void FS_EnumerateKnownGames(qboolean (*callback)(void *usr, ftemanifest_t *man), void *usr) +{ + int i; + char basedir[MAX_OSPATH]; + fmfenums_t e; + e.found = 0; + e.callback = callback; + e.usr = usr; + + //-basepack is primarily an android feature, where the apk file is specified. + //this allows custom mods purely by customising the apk + i = COM_CheckParm ("-basepack"); + while (i && i < com_argc-1) + { + const char *pakname = com_argv[i+1]; + searchpathfuncs_t *pak; + vfsfile_t *vfs = VFSOS_Open(pakname, "rb"); + pak = FS_OpenPackByExtension(vfs, pakname); + if (pak) + { + pak->EnumerateFiles(pak, "*.fmf", FS_EnumerateFMFs, &e); + pak->ClosePath(pak); + } + i = COM_CheckNextParm ("-basepack", i); + } + + //okay, no manifests in the basepack, try looking in the basedir. + //this defaults to the working directory. perhaps try the exe's location instead? + if (!e.found) + Sys_EnumerateFiles(host_parms.basedir, "*.fmf", FS_EnumerateFMFs, &e, NULL); + + //right, no fmf files anywhere. + //just make stuff up from whatever games they may have installed on their system. + if (!e.found) + { + for (i = 0; gamemode_info[i].argname; i++) + { + if (gamemode_info[i].manifestfile || Sys_FindGameData(NULL, gamemode_info[i].argname+1, basedir, sizeof(basedir))) + { + ftemanifest_t *man = FS_GenerateLegacyManifest(NULL, 0, true, i); + if (e.callback(e.usr, man)) + e.found++; + else + FS_Manifest_Free(man); + } + } + } +} + void FS_ChangeGame_f(void) { int i; @@ -3601,11 +3910,11 @@ void COM_InitFilesystem (void) // i = COM_CheckParm ("-basedir"); if (i && i < com_argc-1) - strcpy (com_quakedir, com_argv[i+1]); + strcpy (com_gamepath, com_argv[i+1]); else - strcpy (com_quakedir, host_parms.basedir); + strcpy (com_gamepath, host_parms.basedir); - FS_CleanDir(com_quakedir, sizeof(com_quakedir)); + FS_CleanDir(com_gamepath, sizeof(com_gamepath)); Cvar_Register(&cfg_reload_on_gamedir, "Filesystem"); @@ -3622,7 +3931,7 @@ void COM_InitFilesystem (void) usehome = false; -#ifdef _WIN32 +#if defined(_WIN32) && !defined(WINRT) { //win32 sucks. HMODULE shfolder = LoadLibrary("shfolder.dll"); DWORD winver = (DWORD)LOBYTE(LOWORD(GetVersion())); @@ -3636,21 +3945,21 @@ void COM_InitFilesystem (void) char folder[MAX_PATH]; // 0x5 == CSIDL_PERSONAL if (dSHGetFolderPath(NULL, 0x5, NULL, 0, folder) == S_OK) - Q_snprintfz(com_homedir, sizeof(com_homedir), "%s/My Games/%s/", folder, FULLENGINENAME); + Q_snprintfz(com_homepath, sizeof(com_homepath), "%s/My Games/%s/", folder, FULLENGINENAME); } // FreeLibrary(shfolder); } - if (!*com_homedir) + if (!*com_homepath) { ev = getenv("USERPROFILE"); if (ev) - Q_snprintfz(com_homedir, sizeof(com_homedir), "%s/My Documents/My Games/%s/", ev, FULLENGINENAME); + Q_snprintfz(com_homepath, sizeof(com_homepath), "%s/My Documents/My Games/%s/", ev, FULLENGINENAME); } #ifdef NPFTE - if (!*com_homedir) - Q_snprintfz(com_homedir, sizeof(com_homedir), "/%s/", FULLENGINENAME); + if (!*com_homepath) + Q_snprintfz(com_homepath, sizeof(com_homepath), "/%s/", FULLENGINENAME); //as a browser plugin, always use their home directory usehome = true; #else @@ -3711,23 +4020,28 @@ void COM_InitFilesystem (void) if (ev && *ev) { if (ev[strlen(ev)-1] == '/') - Q_snprintfz(com_homedir, sizeof(com_homedir), "%s.fte/", ev); + Q_snprintfz(com_homepath, sizeof(com_homepath), "%s.fte/", ev); else - Q_snprintfz(com_homedir, sizeof(com_homedir), "%s/.fte/", ev); + Q_snprintfz(com_homepath, sizeof(com_homepath), "%s/.fte/", ev); usehome = true; // always use home on unix unless told not to } else - *com_homedir = '\0'; + *com_homepath = '\0'; #endif - if (!usehome && !COM_CheckParm("-usehome")) - *com_homedir = '\0'; + com_homepathenabled = usehome; + if (COM_CheckParm("-usehome")) + com_homepathenabled = true; if (COM_CheckParm("-nohome")) - *com_homedir = '\0'; + com_homepathenabled = false; + if (!*com_homepath) + com_homepathenabled = false; - if (*com_homedir) - Con_TPrintf("Using home directory \"%s\"\n", com_homedir); + fs_readonly = COM_CheckParm("-readonly"); + + if (com_homepathenabled) + Con_TPrintf("Using home directory \"%s\"\n", com_homepath); #ifdef PLUGINS Plug_Initialise(false); diff --git a/engine/common/fs.h b/engine/common/fs.h index f9ce1e49..db93e48c 100644 --- a/engine/common/fs.h +++ b/engine/common/fs.h @@ -2,9 +2,10 @@ #define FSVER 2 -#define FF_NOTFOUND 0 //file wasn't found -#define FF_FOUND 1 //file was found -#define FF_SYMLINK 2 //file contents are the name of a different file (symlink). do a recursive lookup on the name +#define FF_NOTFOUND (0u) //file wasn't found +#define FF_FOUND (1u<<0u) //file was found +#define FF_SYMLINK (1u<<1u) //file contents are the name of a different file (symlink). do a recursive lookup on the name +#define FF_DIRECTORY (1u<<2u) typedef struct { @@ -14,6 +15,7 @@ typedef struct extern hashtable_t filesystemhash; //this table is the one to build your hash references into extern int fs_hash_dups; //for tracking efficiency. no functional use. extern int fs_hash_files; //for tracking efficiency. no functional use. +extern qboolean fs_readonly; //if true, fopen(, "w") should always fail. struct searchpath_s; struct searchpathfuncs_s @@ -54,9 +56,12 @@ int FS_RegisterFileSystemType(void *module, const char *extension, searchpathfun void FS_UnRegisterFileSystemType(int idx); void FS_UnRegisterFileSystemModule(void *module); +void FS_EnumerateKnownGames(qboolean (*callback)(void *usr, ftemanifest_t *man), void *usr); + #define SPF_REFERENCED 1 //something has been loaded from this path. should filter out client references... #define SPF_COPYPROTECTED 2 //downloads are not allowed fom here. #define SPF_TEMPORARY 4 //a map-specific path, purged at map change. #define SPF_EXPLICIT 8 //a root gamedir (bumps depth on gamedir depth checks). #define SPF_UNTRUSTED 16 //has been downloaded from somewhere. configs inside it should never be execed with local access rights. +#define SPF_PRIVATE 32 //private to the client. ie: the fte dir. qboolean FS_LoadPackageFromFile(vfsfile_t *vfs, char *pname, char *localname, int *crc, unsigned int flags); diff --git a/engine/common/fs_pak.c b/engine/common/fs_pak.c index 53bf454d..e33cbcf2 100644 --- a/engine/common/fs_pak.c +++ b/engine/common/fs_pak.c @@ -225,11 +225,12 @@ static qofs_t QDECL VFSPAK_GetLen (struct vfsfile_s *vfs) vfspack_t *vfsp = (vfspack_t*)vfs; return vfsp->length; } -static void QDECL VFSPAK_Close(vfsfile_t *vfs) +static qboolean QDECL VFSPAK_Close(vfsfile_t *vfs) { vfspack_t *vfsp = (vfspack_t*)vfs; FSPAK_ClosePath(&vfsp->parentpak->pub); //tell the parent that we don't need it open any more (reference counts) Z_Free(vfsp); //free ourselves. + return true; } static vfsfile_t *QDECL FSPAK_OpenVFS(searchpathfuncs_t *handle, flocation_t *loc, const char *mode) { diff --git a/engine/common/fs_stdio.c b/engine/common/fs_stdio.c index 51aa19ea..7c00b5f9 100644 --- a/engine/common/fs_stdio.c +++ b/engine/common/fs_stdio.c @@ -8,7 +8,7 @@ #define Z_Free free #define Z_Malloc malloc #else -#if !defined(_WIN32) || defined(FTE_SDL) +#if !defined(_WIN32) || defined(FTE_SDL) || defined(WINRT) #define FSSTDIO_OpenPath VFSOS_OpenPath #endif #define FSSTDIO_OpenTemp FS_OpenTemp @@ -62,21 +62,27 @@ static qofs_t QDECL VFSSTDIO_GetSize (struct vfsfile_s *file) return maxlen; } -static void QDECL VFSSTDIO_Close(vfsfile_t *file) +static qboolean QDECL VFSSTDIO_Close(vfsfile_t *file) { + qboolean success; vfsstdiofile_t *intfile = (vfsstdiofile_t*)file; + success = !ferror(intfile->handle); fclose(intfile->handle); Z_Free(file); + return success; } #ifdef _WIN32 -static void QDECL VFSSTDIO_CloseTemp(vfsfile_t *file) +static qboolean QDECL VFSSTDIO_CloseTemp(vfsfile_t *file) { + qboolean success; vfsstdiofile_t *intfile = (vfsstdiofile_t*)file; char *fname = (char*)(intfile+1); + success = !ferror(intfile->handle); fclose(intfile->handle); _unlink(fname); Z_Free(file); + return success; } #endif @@ -184,7 +190,7 @@ vfsfile_t *VFSSTDIO_Open(const char *osname, const char *mode, qboolean *needsfl return (vfsfile_t*)file; } -#if !defined(_WIN32) || defined(FTE_SDL) +#if !defined(_WIN32) || defined(FTE_SDL) || defined(WINRT) vfsfile_t *VFSOS_Open(const char *osname, const char *mode) { vfsfile_t *f; diff --git a/engine/common/fs_win32.c b/engine/common/fs_win32.c index 399f3dc5..6782f6ae 100644 --- a/engine/common/fs_win32.c +++ b/engine/common/fs_win32.c @@ -2,33 +2,6 @@ #include "fs.h" #include "winquake.h" -#ifndef INVALID_SET_FILE_POINTER -#define INVALID_SET_FILE_POINTER ~0 -#endif - -//read-only memory mapped files. -//for write access, we use the stdio module as a fallback. -//do you think anyone will ever notice that utf8 filenames work even in windows? probably not. oh well, worth a try. - -#define VFSW32_Open VFSOS_Open -#define VFSW32_OpenPath VFSOS_OpenPath - -typedef struct { - searchpathfuncs_t pub; - HANDLE changenotification; - void (QDECL *AddFileHash)(int depth, const char *fname, fsbucket_t *filehandle, void *pathhandle); - int hashdepth; - char rootpath[1]; -} vfsw32path_t; -typedef struct { - vfsfile_t funcs; - HANDLE hand; - HANDLE mmh; - void *mmap; - unsigned int length; - unsigned int offset; -} vfsw32file_t; - //outlen is the size of out in _BYTES_. wchar_t *widen(wchar_t *out, size_t outlen, const char *utf8) { @@ -99,6 +72,35 @@ char *narrowen(char *out, size_t outlen, wchar_t *wide) } +#ifndef WINRT //winrt is too annoying. lets just use stdio. + +#ifndef INVALID_SET_FILE_POINTER +#define INVALID_SET_FILE_POINTER ~0 +#endif + +//read-only memory mapped files. +//for write access, we use the stdio module as a fallback. +//do you think anyone will ever notice that utf8 filenames work even in windows? probably not. oh well, worth a try. + +#define VFSW32_Open VFSOS_Open +#define VFSW32_OpenPath VFSOS_OpenPath + +typedef struct { + searchpathfuncs_t pub; + HANDLE changenotification; + void (QDECL *AddFileHash)(int depth, const char *fname, fsbucket_t *filehandle, void *pathhandle); + int hashdepth; + char rootpath[1]; +} vfsw32path_t; +typedef struct { + vfsfile_t funcs; + HANDLE hand; + HANDLE mmh; + void *mmap; + unsigned int length; + unsigned int offset; +} vfsw32file_t; + static int QDECL VFSW32_ReadBytes (struct vfsfile_s *file, void *buffer, int bytestoread) { DWORD read; @@ -174,7 +176,7 @@ static qofs_t QDECL VFSW32_GetSize (struct vfsfile_s *file) lo = GetFileSize(intfile->hand, &hi); return qofs_Make(lo,hi); } -static void QDECL VFSW32_Close(vfsfile_t *file) +static qboolean QDECL VFSW32_Close(vfsfile_t *file) { vfsw32file_t *intfile = (vfsw32file_t*)file; if (intfile->mmap) @@ -184,6 +186,7 @@ static void QDECL VFSW32_Close(vfsfile_t *file) } CloseHandle(intfile->hand); Z_Free(file); + return true; } //WARNING: handle can be null @@ -203,6 +206,9 @@ static vfsfile_t *QDECL VFSW32_OpenInternal(vfsw32path_t *handle, const char *qu if (strchr(mode, '+')) read = write = true; + if (fs_readonly && (write || append)) + return NULL; + if (!WinNT) { if ((write && read) || append) @@ -222,6 +228,7 @@ static vfsfile_t *QDECL VFSW32_OpenInternal(vfsw32path_t *handle, const char *qu h = INVALID_HANDLE_VALUE; if (write || append) { + //this extra block is to avoid flushing fs caches needlessly h = CreateFileW(wide, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (h == INVALID_HANDLE_VALUE) { @@ -246,7 +253,10 @@ static vfsfile_t *QDECL VFSW32_OpenInternal(vfsw32path_t *handle, const char *qu h = INVALID_HANDLE_VALUE; } if (h == INVALID_HANDLE_VALUE) + { + DWORD e = GetLastError(); return NULL; + } if (!didexist) { @@ -370,13 +380,15 @@ static void QDECL VFSW32_BuildHash(searchpathfuncs_t *handle, int hashdepth, voi wp->hashdepth = hashdepth; Sys_EnumerateFiles(wp->rootpath, "*", VFSW32_RebuildFSHash, AddFileHash, handle); } +#include static unsigned int QDECL VFSW32_FLocate(searchpathfuncs_t *handle, flocation_t *loc, const char *filename, void *hashedresult) { vfsw32path_t *wp = (void*)handle; - FILE *f; - int len; char netpath[MAX_OSPATH]; wchar_t wide[MAX_OSPATH]; + qofs_t len; + HANDLE h; + DWORD attr; if (hashedresult && (void *)hashedresult != wp) @@ -394,23 +406,35 @@ static unsigned int QDECL VFSW32_FLocate(searchpathfuncs_t *handle, flocation_t snprintf (netpath, sizeof(netpath)-1, "%s/%s", wp->rootpath, filename); if (!WinNT) - f = fopen(netpath, "rb"); + { + WIN32_FIND_DATAA fda; + h = FindFirstFileA(netpath, &fda); + attr = fda.dwFileAttributes; + len = (h == INVALID_HANDLE_VALUE)?0:qofs_Make(fda.nFileSizeLow, fda.nFileSizeHigh); + } else - f = _wfopen(widen(wide, sizeof(wide), netpath), L"rb"); - if (!f) + { + WIN32_FIND_DATAW fdw; + h = FindFirstFileW(widen(wide, sizeof(wide), netpath), &fdw); + attr = fdw.dwFileAttributes; + len = (h == INVALID_HANDLE_VALUE)?0:qofs_Make(fdw.nFileSizeLow, fdw.nFileSizeHigh); + } + if (h == INVALID_HANDLE_VALUE) + { + // int e = GetLastError(); + // if (e == ERROR_PATH_NOT_FOUND) //then look inside a zip return FF_NOTFOUND; - - fseek(f, 0, SEEK_END); - len = ftell(f); - fclose(f); + } + FindClose(h); if (loc) { loc->len = len; loc->offset = 0; loc->index = 0; - snprintf(loc->rawname, sizeof(loc->rawname), "%s/%s", wp->rootpath, filename); + Q_strncpyz(loc->rawname, netpath, sizeof(loc->rawname)); } - + if (attr & FILE_ATTRIBUTE_DIRECTORY) + return FF_DIRECTORY; //not actually openable. return FF_FOUND; } static void QDECL VFSW32_ReadFile(searchpathfuncs_t *handle, flocation_t *loc, char *buffer) @@ -440,6 +464,8 @@ static qboolean QDECL VFSW32_RenameFile(searchpathfuncs_t *handle, const char *o vfsw32path_t *wp = (vfsw32path_t*)handle; char oldsyspath[MAX_OSPATH]; char newsyspath[MAX_OSPATH]; + if (fs_readonly) + return false; snprintf (oldsyspath, sizeof(oldsyspath)-1, "%s/%s", wp->rootpath, oldfname); snprintf (newsyspath, sizeof(newsyspath)-1, "%s/%s", wp->rootpath, newfname); return Sys_Rename(oldsyspath, newsyspath); @@ -448,6 +474,8 @@ static qboolean QDECL VFSW32_RemoveFile(searchpathfuncs_t *handle, const char *f { vfsw32path_t *wp = (vfsw32path_t*)handle; char syspath[MAX_OSPATH]; + if (fs_readonly) + return false; snprintf (syspath, sizeof(syspath)-1, "%s/%s", wp->rootpath, filename); return Sys_remove(syspath); } @@ -455,6 +483,8 @@ static qboolean QDECL VFSW32_MkDir(searchpathfuncs_t *handle, const char *filena { vfsw32path_t *wp = (vfsw32path_t*)handle; char syspath[MAX_OSPATH]; + if (fs_readonly) + return false; snprintf (syspath, sizeof(syspath)-1, "%s/%s", wp->rootpath, filename); Sys_mkdir(syspath); return true; @@ -492,3 +522,4 @@ searchpathfuncs_t *QDECL VFSW32_OpenPath(vfsfile_t *mustbenull, const char *desc return &np->pub; } +#endif \ No newline at end of file diff --git a/engine/common/fs_zip.c b/engine/common/fs_zip.c index 99c40f94..79656973 100644 --- a/engine/common/fs_zip.c +++ b/engine/common/fs_zip.c @@ -604,15 +604,20 @@ static qofs_t QDECL VFSZIP_GetLen (struct vfsfile_s *file) vfszip_t *vfsz = (vfszip_t*)file; return vfsz->length; } -static void QDECL VFSZIP_Close (struct vfsfile_s *file) +static qboolean QDECL VFSZIP_Close (struct vfsfile_s *file) { vfszip_t *vfsz = (vfszip_t*)file; if (vfsz->defer) VFS_CLOSE(vfsz->defer); + if (vfsz->decompress) + FSZIP_Decompress_Destroy(vfsz->decompress); + FSZIP_ClosePath(&vfsz->parent->pub); Z_Free(vfsz); + + return true; //FIXME: return an error if the crc was wrong. } static qboolean FSZIP_ValidateLocalHeader(zipfile_t *zip, zpackfile_t *zfile, qofs_t *datastart, qofs_t *datasize); @@ -627,7 +632,7 @@ static vfsfile_t *QDECL FSZIP_OpenVFS(searchpathfuncs_t *handle, flocation_t *lo if (strcmp(mode, "rb")) return NULL; //urm, unable to write/append - if (loc->len < 0) + if (qofs_Error(loc->len)) return NULL; flags = zip->files[loc->index].flags; diff --git a/engine/common/gl_q2bsp.c b/engine/common/gl_q2bsp.c index 9d0d6341..8a6b5907 100644 --- a/engine/common/gl_q2bsp.c +++ b/engine/common/gl_q2bsp.c @@ -6049,4 +6049,9 @@ void CM_Init(void) //register cvars. Cvar_Register(&map_autoopenportals, MAPOPTIONS); Cvar_Register(&r_subdivisions, MAPOPTIONS); } +void CM_Shutdown(void) +{ + ZG_FreeGroup(&box_model.memgroup); + box_planes = NULL; +} #endif diff --git a/engine/common/mathlib.c b/engine/common/mathlib.c index fb436563..bbd7f102 100644 --- a/engine/common/mathlib.c +++ b/engine/common/mathlib.c @@ -757,6 +757,38 @@ void VectorTransform (const vec3_t in1, const matrix3x4 in2, vec3_t out) out[2] = DotProduct(in1, in2[2]) + in2[2][3]; } +void GenMatrixPosQuat4Scale(vec3_t pos, vec4_t quat, vec3_t scale, float result[12]) +{ + float xx, xy, xz, xw, yy, yz, yw, zz, zw; + float x2, y2, z2; + float s; + x2 = quat[0] + quat[0]; + y2 = quat[1] + quat[1]; + z2 = quat[2] + quat[2]; + + xx = quat[0] * x2; xy = quat[0] * y2; xz = quat[0] * z2; + yy = quat[1] * y2; yz = quat[1] * z2; zz = quat[2] * z2; + xw = quat[3] * x2; yw = quat[3] * y2; zw = quat[3] * z2; + + s = scale[0]; + result[0*4+0] = s*(1.0f - (yy + zz)); + result[1*4+0] = s*(xy + zw); + result[2*4+0] = s*(xz - yw); + + s = scale[1]; + result[0*4+1] = s*(xy - zw); + result[1*4+1] = s*(1.0f - (xx + zz)); + result[2*4+1] = s*(yz + xw); + + s = scale[2]; + result[0*4+2] = s*(xz + yw); + result[1*4+2] = s*(yz - xw); + result[2*4+2] = s*(1.0f - (xx + yy)); + + result[0*4+3] = pos[0]; + result[1*4+3] = pos[1]; + result[2*4+3] = pos[2]; +} #ifdef HALFLIFEMODELS void AngleQuaternion( const vec3_t angles, vec4_t quaternion ) @@ -1646,7 +1678,7 @@ void Matrix3x4_InvertTo4x4_Simple (const float *in1, float *out) out[15] = 1; } -void Matrix3x4_InvertTo3x3(float *in, float *result) +void Matrix3x4_InvertTo3x3(const float *in, float *result) { float t1[16], tr[16]; memcpy(t1, in, sizeof(float)*12); diff --git a/engine/common/mathlib.h b/engine/common/mathlib.h index 315c89f6..87e9e28b 100644 --- a/engine/common/mathlib.h +++ b/engine/common/mathlib.h @@ -182,6 +182,8 @@ void Matrix3x4_RM_Transform3(const float *matrix, const float *vector, float *p float *Matrix4x4_CM_NewRotation(float a, float x, float y, float z); float *Matrix4x4_CM_NewTranslation(float x, float y, float z); +void GenMatrixPosQuat4Scale(vec3_t pos, vec4_t quat, vec3_t scale, float result[12]); + #define AngleVectorsFLU(a,f,l,u) do{AngleVectors(a,f,l,u);VectorNegate(l,l);}while(0) //projection matricies of different types... gesh @@ -194,7 +196,7 @@ void Matrix4x4_CM_Projection_Inf(float *proj, float fovx, float fovy, float nea fixed16_t Mul16_30 (fixed16_t multiplier, fixed16_t multiplicand); int Q_log2 (int val); -void Matrix3x4_InvertTo3x3(float *in, float *result); +void Matrix3x4_InvertTo3x3(const float *in, float *result); fixed16_t Mul16_30 (fixed16_t multiplier, fixed16_t multiplicand); int Q_log2 (int val); diff --git a/engine/common/net.h b/engine/common/net.h index d5d55976..4824ba8f 100644 --- a/engine/common/net.h +++ b/engine/common/net.h @@ -27,7 +27,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. //FIXME: should split this into loopback/dgram/stream/irc //with the ipv4/v6/x as a separate parameter -typedef enum {NA_INVALID, NA_LOOPBACK, NA_IP, NA_IPV6, NA_IPX, NA_BROADCAST_IP, NA_BROADCAST_IP6, NA_BROADCAST_IPX, NA_TCP, NA_TCPV6, NA_IRC, NA_WEBSOCKET, NA_NATPMP} netadrtype_t; +typedef enum {NA_INVALID, NA_LOOPBACK, NA_IP, NA_IPV6, NA_IPX, NA_BROADCAST_IP, NA_BROADCAST_IP6, NA_BROADCAST_IPX, NA_TCP, NA_TCPV6, NA_TLSV4, NA_TLSV6, NA_IRC, NA_WEBSOCKET, NA_NATPMP} netadrtype_t; typedef enum {NS_CLIENT, NS_SERVER} netsrc_t; @@ -92,7 +92,7 @@ void NET_CloseServer (void); void UDP_CloseSocket (int socket); void NET_Shutdown (void); int NET_GetPacket (netsrc_t netsrc, int firstsock); -qboolean NET_SendPacket (netsrc_t socket, int length, void *data, netadr_t *to); +qboolean NET_SendPacket (netsrc_t socket, int length, const void *data, netadr_t *to); 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); diff --git a/engine/common/net_chan.c b/engine/common/net_chan.c index e84e9172..8bd7dd9f 100644 --- a/engine/common/net_chan.c +++ b/engine/common/net_chan.c @@ -425,9 +425,15 @@ nqprot_t NQNetChan_Process(netchan_t *chan) chan->last_received = realtime; } else if (sequence < chan->reliable_sequence) - Con_DPrintf("Stale ack recieved\n"); + { + if (showdrop.ival) + Con_Printf("Stale ack recieved\n"); + } else if (sequence > chan->reliable_sequence) - Con_Printf("Future ack recieved\n"); + { + if (showdrop.ival) + Con_Printf("Future ack recieved\n"); + } if (showpackets.value) Con_Printf ("in %s a=%i %i\n" @@ -442,7 +448,8 @@ nqprot_t NQNetChan_Process(netchan_t *chan) { if (sequence <= chan->incoming_unreliable) { - Con_DPrintf("Stale datagram recieved (%i<=%i)\n", sequence, chan->incoming_unreliable); + if (showdrop.ival) + Con_Printf("Stale datagram recieved (%i<=%i)\n", sequence, chan->incoming_unreliable); return NQP_ERROR; } drop = sequence - chan->incoming_unreliable - 1; @@ -516,7 +523,10 @@ nqprot_t NQNetChan_Process(netchan_t *chan) } } else - Con_DPrintf("Stale reliable (%i)\n", sequence); + { + if (showdrop.ival) + Con_Printf("Stale reliable (%i)\n", sequence); + } return NQP_ERROR; } @@ -553,7 +563,7 @@ int Netchan_Transmit (netchan_t *chan, int length, qbyte *data, int rate) send.maxsize = MAX_NQMSGLEN + PACKET_HEADER; send.cursize = 0; - if (chan->remote_address.type == NA_TCP && chan->reliable_length) + if ((chan->remote_address.type == NA_TCP || chan->remote_address.type == NA_TCPV6 || chan->remote_address.type == NA_TLSV4 || chan->remote_address.type == NA_TLSV6) && chan->reliable_length) { //if over tcp, everything is assumed to be reliable. pretend it got acked. chan->reliable_length = 0; //they got the entire message @@ -775,7 +785,8 @@ int Netchan_Transmit (netchan_t *chan, int length, qbyte *data, int rate) #endif if (showpackets.value) - Con_Printf ("--> s=%i(%i) a=%i(%i) %i\n" + Con_Printf ("%s --> s=%i(%i) a=%i(%i) %i\n" + , chan->sock == NS_SERVER?"s2c":"c2s" , chan->outgoing_sequence , send_reliable , chan->incoming_sequence @@ -832,7 +843,8 @@ qboolean Netchan_Process (netchan_t *chan) sequence_ack &= ~(1<<31); if (showpackets.value) - Con_Printf ("<-- s=%i(%i) a=%i(%i) %i%s\n" + Con_Printf ("%s <-- s=%i(%i) a=%i(%i) %i%s\n" + , chan->sock == NS_SERVER?"c2s":"s2c" , sequence , reliable_message , sequence_ack diff --git a/engine/common/net_ssl_winsspi.c b/engine/common/net_ssl_winsspi.c index 5f66f6fb..e0c28c50 100644 --- a/engine/common/net_ssl_winsspi.c +++ b/engine/common/net_ssl_winsspi.c @@ -2,7 +2,7 @@ #if defined(HAVE_WINSSPI) cvar_t *tls_ignorecertificateerrors; -#include +#include "winquake.h" #define SECURITY_WIN32 #include #include @@ -16,6 +16,7 @@ static struct SECURITY_STATUS (WINAPI *pEncryptMessage) (PCtxtHandle,ULONG,PSecBufferDesc,ULONG); SECURITY_STATUS (WINAPI *pAcquireCredentialsHandleA) (SEC_CHAR*,SEC_CHAR*,ULONG,PLUID,PVOID,SEC_GET_KEY_FN,PVOID,PCredHandle,PTimeStamp); SECURITY_STATUS (WINAPI *pInitializeSecurityContextA) (PCredHandle,PCtxtHandle,SEC_CHAR*,ULONG,ULONG,ULONG,PSecBufferDesc,ULONG,PCtxtHandle,PSecBufferDesc,PULONG,PTimeStamp); + SECURITY_STATUS (WINAPI *pAcceptSecurityContext) (PCredHandle,PCtxtHandle,PSecBufferDesc,unsigned long,unsigned long,PCtxtHandle,PSecBufferDesc,unsigned long SEC_FAR *,PTimeStamp); SECURITY_STATUS (WINAPI *pCompleteAuthToken) (PCtxtHandle,PSecBufferDesc); SECURITY_STATUS (WINAPI *pQueryContextAttributesA) (PCtxtHandle,ULONG,PVOID); SECURITY_STATUS (WINAPI *pFreeCredentialsHandle) (PCredHandle); @@ -28,6 +29,9 @@ static struct BOOL (WINAPI *pCertVerifyCertificateChainPolicy) (LPCSTR,PCCERT_CHAIN_CONTEXT,PCERT_CHAIN_POLICY_PARA,PCERT_CHAIN_POLICY_STATUS); void (WINAPI *pCertFreeCertificateChain) (PCCERT_CHAIN_CONTEXT); DWORD (WINAPI *pCertNameToStrA) (DWORD dwCertEncodingType, PCERT_NAME_BLOB pName, DWORD dwStrType, LPTSTR psz, DWORD csz); + + PCCERT_CONTEXT (WINAPI *pCertCreateSelfSignCertificate) (HCRYPTPROV,PCERT_NAME_BLOB,DWORD,PCRYPT_KEY_PROV_INFO,PCRYPT_ALGORITHM_IDENTIFIER,PSYSTEMTIME,PSYSTEMTIME,PCERT_EXTENSIONS); + BOOL (WINAPI *pCertStrToNameA) (DWORD,LPCSTR,DWORD,void *,BYTE *,DWORD *,LPCSTR *); } crypt; static qboolean SSL_Init(void) { @@ -37,6 +41,7 @@ static qboolean SSL_Init(void) {(void**)&secur.pEncryptMessage, "EncryptMessage"}, {(void**)&secur.pAcquireCredentialsHandleA, "AcquireCredentialsHandleA"}, {(void**)&secur.pInitializeSecurityContextA, "InitializeSecurityContextA"}, + {(void**)&secur.pAcceptSecurityContext, "AcceptSecurityContext"}, {(void**)&secur.pCompleteAuthToken, "CompleteAuthToken"}, {(void**)&secur.pQueryContextAttributesA, "QueryContextAttributesA"}, {(void**)&secur.pFreeCredentialsHandle, "FreeCredentialsHandle"}, @@ -50,6 +55,8 @@ static qboolean SSL_Init(void) {(void**)&crypt.pCertVerifyCertificateChainPolicy, "CertVerifyCertificateChainPolicy"}, {(void**)&crypt.pCertFreeCertificateChain, "CertFreeCertificateChain"}, {(void**)&crypt.pCertNameToStrA, "CertNameToStrA"}, + {(void**)&crypt.pCertCreateSelfSignCertificate, "CertCreateSelfSignCertificate"}, + {(void**)&crypt.pCertStrToNameA, "CertStrToNameA"}, {NULL, NULL} }; @@ -114,6 +121,7 @@ static int SSPI_CopyIntoBuffer(struct sslbuf *buf, const void *data, unsigned in static void SSPI_Error(sslfile_t *f, char *error) { + f->handshaking = HS_ERROR; Sys_Printf("%s", error); if (f->stream) VFS_CLOSE(f->stream); @@ -184,7 +192,11 @@ static void SSPI_Decode(sslfile_t *f) { if (ss == SEC_E_INCOMPLETE_MESSAGE) return; //no error if its incomplete, we can just get more data later on. - SSPI_Error(f, "DecryptMessage failed\n"); + switch(ss) + { + case SEC_E_INVALID_HANDLE: SSPI_Error(f, "DecryptMessage failed: SEC_E_INVALID_HANDLE\n"); break; + default: SSPI_Error(f, va("DecryptMessage failed: %0#x\n", ss)); break; + } return; } @@ -383,15 +395,84 @@ static DWORD VerifyServerCertificate(PCCERT_CONTEXT pServerCert, PWSTR pwszServe return Status; } -static void SSPI_Handshake (sslfile_t *f) +static PCCERT_CONTEXT SSPI_GetServerCertificate(void) +{ + static PCCERT_CONTEXT ret; + char *issuertext = "CN=127.0.0.1, O=\"FTE QuakeWorld\", OU=Testing, C=TR"; + CERT_NAME_BLOB issuerblob; + + CRYPT_ALGORITHM_IDENTIFIER sigalg; + SYSTEMTIME expiredate; + + if (ret) + return ret; + + memset(&sigalg, 0, sizeof(sigalg)); + sigalg.pszObjId = szOID_RSA_SHA1RSA; + + GetSystemTime(&expiredate); + expiredate.wYear += 2; //2 years hence. woo + + + memset(&issuerblob, 0, sizeof(issuerblob)); + crypt.pCertStrToNameA(X509_ASN_ENCODING, issuertext, CERT_X500_NAME_STR, NULL, issuerblob.pbData, &issuerblob.cbData, NULL); + issuerblob.pbData = Z_Malloc(issuerblob.cbData); + crypt.pCertStrToNameA(X509_ASN_ENCODING, issuertext, CERT_X500_NAME_STR, NULL, issuerblob.pbData, &issuerblob.cbData, NULL); + + ret = crypt.pCertCreateSelfSignCertificate( + 0, + &issuerblob, + 0, + NULL, + &sigalg, + NULL, + &expiredate, + NULL + ); + + Z_Free(issuerblob.pbData); + return ret; +} + +static void SSPI_GenServerCredentials(sslfile_t *f) { SECURITY_STATUS ss; TimeStamp Lifetime; - SecBufferDesc OutBuffDesc; - SecBuffer OutSecBuff; - SecBufferDesc InBuffDesc; - SecBuffer InSecBuff[2]; - ULONG ContextAttributes; + SCHANNEL_CRED SchannelCred; + PCCERT_CONTEXT cred; + + memset(&SchannelCred, 0, sizeof(SchannelCred)); + SchannelCred.dwVersion = SCHANNEL_CRED_VERSION; + SchannelCred.grbitEnabledProtocols = (SP_PROT_TLS1|SP_PROT_SSL3) & SP_PROT_SERVERS; + SchannelCred.dwFlags |= SCH_CRED_NO_SYSTEM_MAPPER|SCH_CRED_DISABLE_RECONNECTS; /*don't use windows login info or anything*/ + + cred = SSPI_GetServerCertificate(); + SchannelCred.cCreds = 1; + SchannelCred.paCred = &cred; + + if (!cred) + { + SSPI_Error(f, "Unable to load/generate certificate\n"); + return; + } + + ss = secur.pAcquireCredentialsHandleA (NULL, UNISP_NAME_A, SECPKG_CRED_INBOUND, NULL, &SchannelCred, NULL, NULL, &f->cred, &Lifetime); + if (ss < 0) + { + SSPI_Error(f, "AcquireCredentialsHandle failed\n"); + return; + } +} + +static void SSPI_Handshake (sslfile_t *f) +{ + SECURITY_STATUS ss; + TimeStamp Lifetime; + SecBufferDesc OutBuffDesc; + SecBuffer OutSecBuff; + SecBufferDesc InBuffDesc; + SecBuffer InSecBuff[2]; + ULONG ContextAttributes; SCHANNEL_CRED SchannelCred; if (f->outcrypt.avail) @@ -402,6 +483,8 @@ static void SSPI_Handshake (sslfile_t *f) return; } + //FIXME: skip this if we've had no new data since last time + OutBuffDesc.ulVersion = SECBUFFER_VERSION; OutBuffDesc.cBuffers = 1; OutBuffDesc.pBuffers = &OutSecBuff; @@ -410,7 +493,9 @@ static void SSPI_Handshake (sslfile_t *f) OutSecBuff.BufferType = SECBUFFER_TOKEN; OutSecBuff.pvBuffer = f->outcrypt.data + f->outcrypt.avail; - if (f->handshaking == HS_STARTCLIENT) + if (f->handshaking == HS_ERROR) + return; //gave up. + else if (f->handshaking == HS_STARTCLIENT) { //no input data yet. f->handshaking = HS_CLIENT; @@ -429,7 +514,7 @@ static void SSPI_Handshake (sslfile_t *f) ss = secur.pInitializeSecurityContextA (&f->cred, NULL, NULL, MessageAttribute, 0, SECURITY_NATIVE_DREP, NULL, 0, &f->sechnd, &OutBuffDesc, &ContextAttributes, &Lifetime); } - else + else if (f->handshaking == HS_CLIENT) { //only if we actually have data. if (!f->incrypt.avail) @@ -460,6 +545,41 @@ static void SSPI_Handshake (sslfile_t *f) } else f->incrypt.avail = 0; } + else if (f->handshaking == HS_STARTSERVER || f->handshaking == HS_SERVER) + { + //only if we actually have data. + if (!f->incrypt.avail) + return; + + InBuffDesc.ulVersion = SECBUFFER_VERSION; + InBuffDesc.cBuffers = 2; + InBuffDesc.pBuffers = InSecBuff; + + InSecBuff[0].BufferType = SECBUFFER_TOKEN; + InSecBuff[0].cbBuffer = f->incrypt.avail; + InSecBuff[0].pvBuffer = f->incrypt.data; + + InSecBuff[1].BufferType = SECBUFFER_EMPTY; + InSecBuff[1].pvBuffer = NULL; + InSecBuff[1].cbBuffer = 0; + + ss = secur.pAcceptSecurityContext(&f->cred, (f->handshaking==HS_SERVER)?&f->sechnd:NULL, &InBuffDesc, ASC_REQ_ALLOCATE_MEMORY|ASC_REQ_STREAM|ASC_REQ_CONFIDENTIALITY, SECURITY_NATIVE_DREP, &f->sechnd, &OutBuffDesc, &ContextAttributes, &Lifetime); + + if (ss == SEC_E_INCOMPLETE_MESSAGE) + return; + f->handshaking = HS_SERVER; + + //any extra data should still remain for the next time around. this might be more handshake data or payload data. + if (InSecBuff[1].BufferType == SECBUFFER_EXTRA) + { + memmove(f->incrypt.data, f->incrypt.data + (f->incrypt.avail - InSecBuff[1].cbBuffer), InSecBuff[1].cbBuffer); + f->incrypt.avail = InSecBuff[1].cbBuffer; + } + else f->incrypt.avail = 0; + } + else + return; + if (ss == SEC_I_INCOMPLETE_CREDENTIALS) { @@ -469,7 +589,14 @@ static void SSPI_Handshake (sslfile_t *f) if (ss < 0) { - SSPI_Error(f, "InitializeSecurityContext failed\n"); + switch(ss) + { + case SEC_E_ALGORITHM_MISMATCH: SSPI_Error(f, "InitializeSecurityContext failed: SEC_E_ALGORITHM_MISMATCH\n"); break; + case SEC_E_INVALID_HANDLE: SSPI_Error(f, "InitializeSecurityContext failed: SEC_E_INVALID_HANDLE\n"); break; + case SEC_E_ILLEGAL_MESSAGE: SSPI_Error(f, "InitializeSecurityContext failed: SEC_E_ILLEGAL_MESSAGE\n"); break; + case SEC_E_INVALID_TOKEN: SSPI_Error(f, "InitializeSecurityContext failed: SEC_E_INVALID_TOKEN\n"); break; + default: SSPI_Error(f, va("InitializeSecurityContext failed: %#x\n", ss)); break; + } return; } @@ -501,26 +628,29 @@ static void SSPI_Handshake (sslfile_t *f) secur.pQueryContextAttributesA(&f->sechnd, SECPKG_ATTR_STREAM_SIZES, &strsizes); f->headersize = strsizes.cbHeader; f->footersize = strsizes.cbTrailer; - f->handshaking = HS_ESTABLISHED; - - if (*f->wpeername) - { - ss = secur.pQueryContextAttributesA(&f->sechnd, SECPKG_ATTR_REMOTE_CERT_CONTEXT, &remotecert); - if (ss != SEC_E_OK) + if (f->handshaking != HS_SERVER) + { //server takes an annonymous client. client expects a proper certificate. + if (*f->wpeername) { - f->handshaking = HS_ERROR; - SSPI_Error(f, "unable to read server's certificate\n"); - return; - } - if (VerifyServerCertificate(remotecert, f->wpeername, 0)) - { - f->handshaking = HS_ERROR; - SSPI_Error(f, "Error validating certificante\n"); + ss = secur.pQueryContextAttributesA(&f->sechnd, SECPKG_ATTR_REMOTE_CERT_CONTEXT, &remotecert); + if (ss != SEC_E_OK) + { + f->handshaking = HS_ERROR; + SSPI_Error(f, "unable to read server's certificate\n"); + return; + } + if (VerifyServerCertificate(remotecert, f->wpeername, 0)) + { + f->handshaking = HS_ERROR; + SSPI_Error(f, "Error validating certificante\n"); + return; + } } + else + Sys_Printf("SSL/TLS Server name not specified, skipping verification\n"); } - else - Sys_Printf("SSL/TLS Server name not specified, skipping verification\n"); + f->handshaking = HS_ESTABLISHED; SSPI_Encode(f); } @@ -587,10 +717,13 @@ static qofs_t QDECL SSPI_GetLen (struct vfsfile_s *file) { return 0; } -static void QDECL SSPI_Close (struct vfsfile_s *file) +static qboolean QDECL SSPI_Close (struct vfsfile_s *file) { - SSPI_Error((sslfile_t*)file, ""); - Z_Free(file); + sslfile_t *f = (sslfile_t *)file; + qboolean success = f->stream != NULL; + SSPI_Error(f, ""); + Z_Free(f); + return success; } #include @@ -609,11 +742,13 @@ vfsfile_t *FS_OpenSSL(const char *hostname, vfsfile_t *source, qboolean server) if (!hostname) hostname = ""; +/* if (server) //unsupported { VFS_CLOSE(source); return NULL; } +*/ newf = Z_Malloc(sizeof(*newf)); while(*hostname) @@ -644,6 +779,9 @@ vfsfile_t *FS_OpenSSL(const char *hostname, vfsfile_t *source, qboolean server) newf->funcs.WriteBytes = SSPI_WriteBytes; newf->funcs.seekingisabadplan = true; + if (server) + SSPI_GenServerCredentials(newf); + return &newf->funcs; } #endif diff --git a/engine/common/net_wins.c b/engine/common/net_wins.c index feaacb19..b4553e25 100644 --- a/engine/common/net_wins.c +++ b/engine/common/net_wins.c @@ -35,7 +35,7 @@ sizebuf_t net_message; //#define MAX_UDP_PACKET (MAX_MSGLEN*2) // one more than msg + header #define MAX_UDP_PACKET 8192 // one more than msg + header qbyte net_message_buffer[MAX_OVERALLMSGLEN]; -#ifdef _WIN32 +#if defined(_WIN32) && defined(HAVE_PACKET) WSADATA winsockdata; #endif @@ -86,11 +86,12 @@ static qboolean allowconnects = false; -#define MAX_LOOPBACK 8 +#define MAX_LOOPBACK 64 typedef struct { - qbyte data[MAX_OVERALLMSGLEN]; + qbyte *data; int datalen; + int datamax; } loopmsg_t; typedef struct @@ -123,6 +124,7 @@ int NetadrToSockadr (netadr_t *a, struct sockaddr_qstorage *s) ((struct sockaddr_in*)s)->sin_port = a->port; return sizeof(struct sockaddr_in); + case NA_TLSV4: case NA_TCP: case NA_IP: memset (s, 0, sizeof(struct sockaddr_in)); @@ -144,6 +146,7 @@ int NetadrToSockadr (netadr_t *a, struct sockaddr_qstorage *s) ((struct sockaddr_in6*)s)->sin6_port = a->port; return sizeof(struct sockaddr_in6); + case NA_TLSV6: case NA_TCPV6: case NA_IPV6: memset (s, 0, sizeof(struct sockaddr_in6)); @@ -272,7 +275,7 @@ qboolean NET_CompareAdr (netadr_t *a, netadr_t *b) #endif #ifdef HAVE_IPV4 - if (a->type == NA_IP || a->type == NA_BROADCAST_IP || a->type == NA_TCP) + if (a->type == NA_IP || a->type == NA_BROADCAST_IP || a->type == NA_TCP || a->type == NA_TLSV4) { if ((memcmp(a->address.ip, b->address.ip, sizeof(a->address.ip)) == 0) && a->port == b->port) return true; @@ -281,7 +284,7 @@ qboolean NET_CompareAdr (netadr_t *a, netadr_t *b) #endif #ifdef IPPROTO_IPV6 - if (a->type == NA_IPV6 || a->type == NA_BROADCAST_IP6 || a->type == NA_TCPV6) + if (a->type == NA_IPV6 || a->type == NA_BROADCAST_IP6 || a->type == NA_TCPV6 || a->type == NA_TLSV6) { if ((memcmp(a->address.ip6, b->address.ip6, sizeof(a->address.ip6)) == 0) && a->port == b->port) return true; @@ -327,7 +330,7 @@ qboolean NET_CompareBaseAdr (netadr_t *a, netadr_t *b) return true; #ifdef HAVE_IPV4 - if (a->type == NA_IP || a->type == NA_TCP) + if (a->type == NA_IP || a->type == NA_TCP || a->type == NA_TLSV4) { if ((memcmp(a->address.ip, b->address.ip, sizeof(a->address.ip)) == 0)) return true; @@ -335,7 +338,7 @@ qboolean NET_CompareBaseAdr (netadr_t *a, netadr_t *b) } #endif #ifdef IPPROTO_IPV6 - if (a->type == NA_IPV6 || a->type == NA_BROADCAST_IP6) + if (a->type == NA_IPV6 || a->type == NA_TCPV6 || a->type == NA_TLSV6) { if ((memcmp(a->address.ip6, b->address.ip6, 16) == 0)) return true; @@ -432,10 +435,11 @@ char *NET_AdrToString (char *s, int len, netadr_t *a) break; #endif #ifdef TCPCONNECT + case NA_TLSV4: case NA_TCP: if (len < 7) return "?"; - snprintf (s, len, "tcp://"); + snprintf (s, len, (a->type == NA_TLSV4)?"tls://":"tcp://"); s += 6; len -= 6; //fallthrough @@ -463,10 +467,11 @@ char *NET_AdrToString (char *s, int len, netadr_t *a) break; #endif #ifdef TCPCONNECT + case NA_TLSV6: case NA_TCPV6: if (len < 7) return "?"; - snprintf (s, len, "tcp://"); + snprintf (s, len, (a->type == NA_TLSV4)?"tls://":"tcp://"); s += 6; len -= 6; //fallthrough @@ -562,7 +567,7 @@ char *NET_AdrToString (char *s, int len, netadr_t *a) break; #endif case NA_LOOPBACK: - snprintf (s, len, "QLoopBack"); + snprintf (s, len, "QLoopBack:%i", a->port); break; #ifdef IRCCONNECT @@ -597,12 +602,25 @@ char *NET_BaseAdrToString (char *s, int len, netadr_t *a) a->address.ip[2], a->address.ip[3]); break; + case NA_TLSV4: + case NA_TLSV6: + snprintf (s, len, "tls://"); + if (len > 6) + { + a->type = (a->type-NA_TLSV4)+NA_IP; + NET_BaseAdrToString(s+6, len-6, a); + a->type = (a->type-NA_IP)+NA_TLSV4; + } + break; case NA_TCP: - snprintf (s, len, "tcp://%i.%i.%i.%i", - a->address.ip[0], - a->address.ip[1], - a->address.ip[2], - a->address.ip[3]); + case NA_TCPV6: + snprintf (s, len, "tcp://"); + if (len > 6) + { + a->type = (a->type-NA_TCP)+NA_IP; + NET_BaseAdrToString(s+6, len-6, a); + a->type = (a->type-NA_IP)+NA_TCP; + } break; #ifdef IPPROTO_IPV6 case NA_BROADCAST_IP6: @@ -911,6 +929,16 @@ qboolean NET_StringToAdr (const char *s, int defaultport, netadr_t *a) a->type = NA_LOOPBACK; return true; } + if (!strncmp(s, "QLoopBack", 9)) + { + memset (a, 0, sizeof(*a)); + a->type = NA_LOOPBACK; + if (s[9] == ':') + a->port = atoi(s+10); + else + a->port = defaultport; + return true; + } #ifdef HAVE_WEBSOCKCL if (!strncmp (s, "ws://", 5) || !strncmp (s, "wss://", 6)) @@ -961,6 +989,30 @@ qboolean NET_StringToAdr (const char *s, int defaultport, netadr_t *a) } return false; } + if (!strncmp (s, "tls://", 6)) + { + //make sure that the rest of the address is a valid ip address (4 or 6) + + if (!NET_StringToSockaddr (s+6, defaultport, &sadr, NULL, NULL)) + { + a->type = NA_INVALID; + return false; + } + + SockadrToNetadr (&sadr, a); + + if (a->type == NA_IP) + { + a->type = NA_TLSV4; + return true; + } + if (a->type == NA_IPV6) + { + a->type = NA_TLSV6; + return true; + } + return false; + } #endif #ifdef IRCCONNECT if (!strncmp (s, "irc://", 6)) @@ -1046,6 +1098,8 @@ void NET_IntegerToMask (netadr_t *a, netadr_t *amask, int bits) { case NA_INVALID: break; + case NA_TCP: + case NA_TLSV4: case NA_IP: case NA_BROADCAST_IP: n = amask->address.ip; @@ -1065,6 +1119,8 @@ void NET_IntegerToMask (netadr_t *a, netadr_t *amask, int bits) *n = i; } break; + case NA_TCPV6: + case NA_TLSV6: case NA_IPV6: case NA_BROADCAST_IP6: #ifdef IPPROTO_IPV6 @@ -1112,8 +1168,6 @@ void NET_IntegerToMask (netadr_t *a, netadr_t *amask, int bits) // warning: enumeration value âNA_*â not handled in switch case NA_NATPMP: case NA_WEBSOCKET: - case NA_TCP: - case NA_TCPV6: case NA_IRC: break; @@ -1530,12 +1584,13 @@ qboolean NET_GetLoopPacket (int sock, netadr_t *from, sizebuf_t *message) from->type = NA_LOOPBACK; message->packing = SZ_RAWBYTES; message->currentbit = 0; + loop->msgs[i].datalen = 0; return true; } -void NET_SendLoopPacket (int sock, int length, void *data, netadr_t *to) +void NET_SendLoopPacket (int sock, int length, const void *data, netadr_t *to) { int i; loopback_t *loop; @@ -1543,14 +1598,16 @@ void NET_SendLoopPacket (int sock, int length, void *data, netadr_t *to) sock &= 1; loop = &loopbacks[sock^1]; - - if (length > sizeof(loop->msgs[i].data)) - { - Con_Printf("NET_SendLoopPacket: Loopback buffer is too small"); - return; - } - i = loop->send & (MAX_LOOPBACK-1); + if (length > loop->msgs[i].datamax) + { + loop->msgs[i].datamax = length + 1024; + BZ_Free(loop->msgs[i].data); + loop->msgs[i].data = BZ_Malloc(loop->msgs[i].datamax); + } + if (loop->msgs[i].datalen) + Con_Printf("Warning: loopback queue overflow\n"); + loop->send++; memcpy (loop->msgs[i].data, data, length); @@ -1582,7 +1639,7 @@ int FTENET_Loop_SetReceiveFDSet(ftenet_generic_connection_t *gcon, fd_set *fdset } #endif -qboolean FTENET_Loop_SendPacket(ftenet_generic_connection_t *con, int length, void *data, netadr_t *to) +qboolean FTENET_Loop_SendPacket(ftenet_generic_connection_t *con, int length, const void *data, netadr_t *to) { if (to->type == NA_LOOPBACK) { @@ -1646,6 +1703,8 @@ static ftenet_generic_connection_t *FTENET_UDP4_EstablishConnection(qboolean iss static ftenet_generic_connection_t *FTENET_UDP6_EstablishConnection(qboolean isserver, const char *address); static ftenet_generic_connection_t *FTENET_TCP4Connect_EstablishConnection(qboolean isserver, const char *address); static ftenet_generic_connection_t *FTENET_TCP6Connect_EstablishConnection(qboolean isserver, const char *address); +static ftenet_generic_connection_t *FTENET_TLS4Connect_EstablishConnection(qboolean isserver, const char *address); +static ftenet_generic_connection_t *FTENET_TLS6Connect_EstablishConnection(qboolean isserver, const char *address); #ifdef USEIPX static ftenet_generic_connection_t *FTENET_IPX_EstablishConnection(qboolean isserver, const char *address); #endif @@ -1880,7 +1939,7 @@ qboolean FTENET_NATPMP_GetPacket(struct ftenet_generic_connection_s *con) } return false; } -qboolean FTENET_NATPMP_SendPacket(struct ftenet_generic_connection_s *con, int length, void *data, netadr_t *to) +qboolean FTENET_NATPMP_SendPacket(struct ftenet_generic_connection_s *con, int length, const void *data, netadr_t *to) { return false; } @@ -2015,9 +2074,11 @@ qboolean FTENET_AddToCollection(ftenet_connections_t *col, const char *name, con #endif #ifdef TCPCONNECT case NA_TCP: establish = FTENET_TCP4Connect_EstablishConnection; break; + case NA_TLSV4: establish = FTENET_TLS4Connect_EstablishConnection; break; #endif #if defined(TCPCONNECT) && defined(IPPROTO_IPV6) case NA_TCPV6: establish = FTENET_TCP6Connect_EstablishConnection; break; + case NA_TLSV6: establish = FTENET_TLS6Connect_EstablishConnection; break; #endif } @@ -2048,7 +2109,7 @@ void FTENET_Generic_Close(ftenet_generic_connection_t *con) Z_Free(con); } -#ifdef _WIN32 +#if defined(_WIN32) && defined(HAVE_PACKET) int FTENET_GetLocalAddress(int port, qboolean ipx, qboolean ipv4, qboolean ipv6, unsigned int *adrflags, netadr_t *addresses, int maxaddresses) { //in win32, we can look up our own hostname to retrieve a list of local interface addresses. @@ -2375,7 +2436,7 @@ qboolean FTENET_Generic_GetPacket(ftenet_generic_connection_t *con) #endif } -qboolean FTENET_Generic_SendPacket(ftenet_generic_connection_t *con, int length, void *data, netadr_t *to) +qboolean FTENET_Generic_SendPacket(ftenet_generic_connection_t *con, int length, const void *data, netadr_t *to) { #ifndef HAVE_PACKET return false; @@ -2646,7 +2707,7 @@ ftenet_generic_connection_t *FTENET_IPX_EstablishConnection(qboolean isserver, c #ifdef TCPCONNECT typedef struct ftenet_tcpconnect_stream_s { - SOCKET socketnum; + vfsfile_t *clientstream; int inlen; int outlen; @@ -2662,16 +2723,19 @@ typedef struct ftenet_tcpconnect_stream_s { } clienttype; char inbuffer[3000]; char outbuffer[3000]; - vfsfile_t *file; + vfsfile_t *dlfile; //if the client looked like an http client, this is the file that they're downloading. float timeouttime; netadr_t remoteaddr; struct ftenet_tcpconnect_stream_s *next; + SOCKET socketnum; //for select. + int fakesequence; //TCPC_WEBSOCKETNQ } ftenet_tcpconnect_stream_t; typedef struct { ftenet_generic_connection_t generic; + qboolean tls; int active; ftenet_tcpconnect_stream_t *tcpstreams; @@ -2717,7 +2781,6 @@ qboolean FTENET_TCPConnect_GetPacket(ftenet_generic_connection_t *gcon) { ftenet_tcpconnect_connection_t *con = (ftenet_tcpconnect_connection_t*)gcon; int ret; - int err; char adr[MAX_ADR_SIZE]; struct sockaddr_qstorage from; int fromlen; @@ -2727,7 +2790,7 @@ qboolean FTENET_TCPConnect_GetPacket(ftenet_generic_connection_t *gcon) st = con->tcpstreams; //remove any stale ones - while (con->tcpstreams && con->tcpstreams->socketnum == INVALID_SOCKET) + while (con->tcpstreams && con->tcpstreams->clientstream == NULL) { st = con->tcpstreams; con->tcpstreams = con->tcpstreams->next; @@ -2737,7 +2800,7 @@ qboolean FTENET_TCPConnect_GetPacket(ftenet_generic_connection_t *gcon) for (st = con->tcpstreams; st; st = st->next) {//client receiving only via tcp - while (st->next && st->next->socketnum == INVALID_SOCKET) + while (st->next && st->next->clientstream == NULL) { ftenet_tcpconnect_stream_t *temp; temp = st->next; @@ -2754,34 +2817,14 @@ qboolean FTENET_TCPConnect_GetPacket(ftenet_generic_connection_t *gcon) goto closesvstream; } - ret = recv(st->socketnum, st->inbuffer+st->inlen, sizeof(st->inbuffer)-st->inlen, 0); - if (ret == 0) + ret = VFS_READ(st->clientstream, st->inbuffer+st->inlen, sizeof(st->inbuffer)-st->inlen); + if (ret < 0) { Con_Printf ("tcp peer %s closed connection\n", NET_AdrToString (adr, sizeof(adr), &st->remoteaddr)); - goto closesvstream; - } - else if (ret == -1) - { - err = neterrno(); - - if (err == NET_EWOULDBLOCK) - ret = 0; - else - { - if (err == NET_ECONNABORTED || err == NET_ECONNRESET) - { - Con_TPrintf ("Connection lost or aborted\n"); //server died/connection lost. - } - else if (err == NET_ENOTCONN) - Con_Printf ("TCPConnect_GetPacket: connection failed\n"); - else - Con_Printf ("TCPConnect_GetPacket: Error (%i): %s\n", err, strerror(err)); - closesvstream: - closesocket(st->socketnum); - st->socketnum = INVALID_SOCKET; - continue; - } + VFS_CLOSE(st->clientstream); + st->clientstream = NULL; + continue; } st->inlen += ret; @@ -2799,7 +2842,7 @@ closesvstream: if (con->generic.islisten) { //send the qizmo handshake response. - send(st->socketnum, "qizmo\n", 6, 0); + VFS_WRITE(st->clientstream, "qizmo\n", 6); } } else if (con->generic.islisten && !strncmp(st->inbuffer, "GET ", 4)) @@ -2943,7 +2986,7 @@ closesvstream: "Sec-WebSocket-Version: 13\r\n" "\r\n"); //send the websocket handshake rejection. - send(st->socketnum, resp, strlen(resp), 0); + VFS_WRITE(st->clientstream, resp, strlen(resp)); goto closesvstream; } @@ -2985,7 +3028,7 @@ closesvstream: "%s" "\r\n", acceptkey, protoname); //send the websocket handshake response. - send(st->socketnum, resp, strlen(resp), 0); + VFS_WRITE(st->clientstream, resp, strlen(resp)); //and the connection is okay @@ -3060,7 +3103,7 @@ closesvstream: } //send the websocket handshake rejection. - send(st->socketnum, resp, strlen(resp), 0); + VFS_WRITE(st->clientstream, resp, strlen(resp)); goto closesvstream; } @@ -3069,7 +3112,7 @@ closesvstream: else { handshakeerror: - Con_Printf ("Unknown TCP handshake from %s\n", NET_AdrToString (adr, sizeof(adr), &net_from)); + Con_Printf ("Unknown TCP handshake from %s\n", NET_AdrToString (adr, sizeof(adr), &st->remoteaddr)); goto closesvstream; } @@ -3078,7 +3121,7 @@ handshakeerror: if (st->outlen) { /*try and flush the old data*/ int done; - done = send(st->socketnum, st->outbuffer, st->outlen, 0); + done = VFS_WRITE(st->clientstream, st->outbuffer, st->outlen); if (done > 0) { memmove(st->outbuffer, st->outbuffer + done, st->outlen - done); @@ -3089,11 +3132,11 @@ handshakeerror: } if (!st->outlen) { - st->outlen = VFS_READ(st->file, st->outbuffer, sizeof(st->outbuffer)); + st->outlen = VFS_READ(st->dlfile, st->outbuffer, sizeof(st->outbuffer)); if (st->outlen <= 0) { - VFS_CLOSE(st->file); - st->file = NULL; + VFS_CLOSE(st->dlfile); + st->dlfile = NULL; st->clienttype = TCPC_UNKNOWN; Con_Printf ("Outgoing file transfer complete\n"); } @@ -3313,25 +3356,41 @@ handshakeerror: newsock = accept(con->generic.thesocket, (struct sockaddr*)&from, &fromlen); if (newsock != INVALID_SOCKET) { + char tmpbuf[256]; int _true = true; ioctlsocket(newsock, FIONBIO, (u_long *)&_true); setsockopt(newsock, IPPROTO_TCP, TCP_NODELAY, (char *)&_true, sizeof(_true)); con->active++; st = Z_Malloc(sizeof(*con->tcpstreams)); + /*grab the net address*/ + SockadrToNetadr(&from, &st->remoteaddr); st->clienttype = TCPC_UNKNOWN; st->next = con->tcpstreams; con->tcpstreams = st; st->socketnum = newsock; + st->clientstream = FS_OpenTCPSocket(newsock, false, NET_AdrToString(tmpbuf, sizeof(tmpbuf), &st->remoteaddr)); st->inlen = 0; - /*grab the net address*/ - SockadrToNetadr(&from, &st->remoteaddr); - /*sockadr doesn't contain transport info, so fix that up here*/ - if (st->remoteaddr.type == NA_IP) - st->remoteaddr.type = NA_TCP; - else if (st->remoteaddr.type == NA_IPV6) - st->remoteaddr.type = NA_TCPV6; +#ifdef HAVE_SSL + if (con->tls) //if we're meant to be using tls, wrap the stream in a tls connection + { + st->clientstream = FS_OpenSSL(NULL, st->clientstream, true); + /*sockadr doesn't contain transport info, so fix that up here*/ + if (st->remoteaddr.type == NA_IP) + st->remoteaddr.type = NA_TLSV4; + else if (st->remoteaddr.type == NA_IPV6) + st->remoteaddr.type = NA_TLSV6; + } + else +#endif + { + /*sockadr doesn't contain transport info, so fix that up here*/ + if (st->remoteaddr.type == NA_IP) + st->remoteaddr.type = NA_TCP; + else if (st->remoteaddr.type == NA_IPV6) + st->remoteaddr.type = NA_TCPV6; + } st->timeouttime = timeval + 30; } @@ -3339,14 +3398,14 @@ handshakeerror: return false; } -qboolean FTENET_TCPConnect_SendPacket(ftenet_generic_connection_t *gcon, int length, void *data, netadr_t *to) +qboolean FTENET_TCPConnect_SendPacket(ftenet_generic_connection_t *gcon, int length, const void *data, netadr_t *to) { ftenet_tcpconnect_connection_t *con = (ftenet_tcpconnect_connection_t*)gcon; ftenet_tcpconnect_stream_t *st; for (st = con->tcpstreams; st; st = st->next) { - if (st->socketnum == INVALID_SOCKET) + if (st->clientstream == NULL) continue; if (NET_CompareAdr(to, &st->remoteaddr)) @@ -3459,7 +3518,7 @@ qboolean FTENET_TCPConnect_SendPacket(ftenet_generic_connection_t *gcon, int len if (st->outlen) { /*try and flush the old data*/ int done; - done = send(st->socketnum, st->outbuffer, st->outlen, 0); + done = VFS_WRITE(st->clientstream, st->outbuffer, st->outlen); if (done > 0) { memmove(st->outbuffer, st->outbuffer + done, st->outlen - done); @@ -3486,8 +3545,8 @@ void FTENET_TCPConnect_Close(ftenet_generic_connection_t *gcon) st = con->tcpstreams; con->tcpstreams = st->next; - if (st->socketnum != INVALID_SOCKET) - closesocket(st->socketnum); + if (st->clientstream != NULL) + VFS_CLOSE(st->clientstream); BZ_Free(st); } @@ -3504,7 +3563,7 @@ int FTENET_TCPConnect_SetReceiveFDSet(ftenet_generic_connection_t *gcon, fd_set for (st = con->tcpstreams; st; st = st->next) { - if (st->socketnum == INVALID_SOCKET) + if (st->clientstream == NULL || st->socketnum == INVALID_SOCKET) continue; FD_SET(st->socketnum, fdset); // network socket if (maxfd < st->socketnum) @@ -3531,12 +3590,14 @@ ftenet_generic_connection_t *FTENET_TCPConnect_EstablishConnection(int affamily, netadr_t adr; struct sockaddr_qstorage qs; int family; - if (!strncmp(address, "tcp://", 6)) - address += 6; - if (!strncmp(address, "ws://", 5)) - address += 5; - if (!strncmp(address, "wss://", 6)) - address += 6; + +#ifndef HAVE_SSL + if ((affamily == NA_TLSV4 || affamily == NA_TLSV6)) + { + Con_Printf("tls not supported in this build\n"); + return NULL; + } +#endif if (isserver) { @@ -3544,10 +3605,22 @@ ftenet_generic_connection_t *FTENET_TCPConnect_EstablishConnection(int affamily, //unable to listen on tcp if we have no packet interface return NULL; #else + + if (!strncmp(address, "tls://", 6)) + address += 6; + if (!strncmp(address, "tcp://", 6)) + address += 6; + if (!strncmp(address, "ws://", 5)) + address += 5; + if (!strncmp(address, "wss://", 6)) + address += 6; + if (!NET_PortToAdr(affamily, address, &adr)) return NULL; //couldn't resolve the name if (adr.type == NA_IP) adr.type = NA_TCP; + else if (adr.type == NA_IPV6) + adr.type = NA_TCPV6; temp = NetadrToSockadr(&adr, &qs); family = ((struct sockaddr_in*)&qs)->sin_family; @@ -3579,6 +3652,8 @@ ftenet_generic_connection_t *FTENET_TCPConnect_EstablishConnection(int affamily, if (adr.type == NA_IP) adr.type = NA_TCP; + else if (adr.type == NA_IPV6) + adr.type = NA_TCPV6; newsocket = TCP_OpenStream(&adr); if (newsocket == INVALID_SOCKET) return NULL; @@ -3590,6 +3665,7 @@ ftenet_generic_connection_t *FTENET_TCPConnect_EstablishConnection(int affamily, newcon = Z_Malloc(sizeof(*newcon)); if (newcon) { + newcon->tls = (affamily == NA_TLSV4 || affamily == NA_TLSV6); if (isserver) newcon->generic.GetLocalAddresses = FTENET_Generic_GetLocalAddresses; newcon->generic.GetPacket = FTENET_TCPConnect_GetPacket; @@ -3613,16 +3689,22 @@ ftenet_generic_connection_t *FTENET_TCPConnect_EstablishConnection(int affamily, newcon->tcpstreams = Z_Malloc(sizeof(*newcon->tcpstreams)); newcon->tcpstreams->next = NULL; newcon->tcpstreams->socketnum = newsocket; + newcon->tcpstreams->clientstream = FS_OpenTCPSocket(newsocket, true, address); newcon->tcpstreams->inlen = 0; newcon->tcpstreams->remoteaddr = adr; +#ifdef HAVE_SSL + if (newcon->tls) //if we're meant to be using tls, wrap the stream in a tls connection + newcon->tcpstreams->clientstream = FS_OpenSSL(address, newcon->tcpstreams->clientstream, false); +#endif + #ifdef FTE_TARGET_WEB newcon->tcpstreams->clienttype = TCPC_UNFRAMED; #else //send the qizmo greeting. newcon->tcpstreams->clienttype = TCPC_UNKNOWN; - send(newsocket, "qizmo\n", 6, 0); + VFS_WRITE(newcon->tcpstreams->clientstream, "qizmo\n", 6); #endif newcon->tcpstreams->timeouttime = Sys_DoubleTime() + 30; @@ -3647,12 +3729,20 @@ ftenet_generic_connection_t *FTENET_TCP6Connect_EstablishConnection(qboolean iss { return FTENET_TCPConnect_EstablishConnection(NA_TCPV6, isserver, address); } +ftenet_generic_connection_t *FTENET_TLS6Connect_EstablishConnection(qboolean isserver, const char *address) +{ + return FTENET_TCPConnect_EstablishConnection(NA_TLSV6, isserver, address); +} #endif ftenet_generic_connection_t *FTENET_TCP4Connect_EstablishConnection(qboolean isserver, const char *address) { return FTENET_TCPConnect_EstablishConnection(NA_TCP, isserver, address); } +ftenet_generic_connection_t *FTENET_TLS4Connect_EstablishConnection(qboolean isserver, const char *address) +{ + return FTENET_TCPConnect_EstablishConnection(NA_TLSV4, isserver, address); +} #endif @@ -4064,7 +4154,7 @@ qboolean FTENET_IRCConnect_GetPacket(ftenet_generic_connection_t *gcon) return false; } -qboolean FTENET_IRCConnect_SendPacket(ftenet_generic_connection_t *gcon, int length, void *data, netadr_t *to) +qboolean FTENET_IRCConnect_SendPacket(ftenet_generic_connection_t *gcon, int length, const void *data, netadr_t *to) { ftenet_ircconnect_connection_t *con = (ftenet_ircconnect_connection_t*)gcon; @@ -4677,7 +4767,7 @@ int NET_LocalAddressForRemote(ftenet_connections_t *collection, netadr_t *remote return collection->conn[remote->connum-1]->GetLocalAddresses(collection->conn[remote->connum-1], &adrflags, local, 1); } -qboolean NET_SendPacket (netsrc_t netsrc, int length, void *data, netadr_t *to) +qboolean NET_SendPacket (netsrc_t netsrc, int length, const void *data, netadr_t *to) { // char buffer[64]; ftenet_connections_t *collection; @@ -4739,6 +4829,8 @@ qboolean NET_EnsureRoute(ftenet_connections_t *collection, char *routename, char switch(adr.type) { case NA_WEBSOCKET: + case NA_TLSV4: + case NA_TLSV6: case NA_TCP: case NA_TCPV6: case NA_IRC: @@ -5205,7 +5297,7 @@ qboolean NET_Sleep(int msec, qboolean stdinissocket) //thus its only needed on windows and with ipv4. void NET_GetLocalAddress (int socket, netadr_t *out) { -#ifdef _WIN32 +#if defined(_WIN32) && defined(HAVE_PACKET) char buff[512]; char adrbuf[MAX_ADR_SIZE]; struct sockaddr_qstorage address; @@ -5345,7 +5437,7 @@ NET_Init */ void NET_Init (void) { -#ifdef _WIN32 +#if defined(_WIN32) && defined(HAVE_PACKET) int r; #ifdef IPPROTO_IPV6 HMODULE ws2_32dll; @@ -5365,7 +5457,7 @@ void NET_Init (void) pgetaddrinfo = NULL; #endif - r = WSAStartup (MAKEWORD(1, 1), &winsockdata); + r = WSAStartup (MAKEWORD(2, 2), &winsockdata); if (r) Sys_Error ("Winsock initialization failed."); @@ -5605,7 +5697,7 @@ void NET_Shutdown (void) #endif -#ifdef _WIN32 +#if defined(_WIN32) && defined(HAVE_PACKET) #ifdef SERVERTONLY if (!serverthreadID) //running as subsystem of client. Don't close all of it's sockets too. #endif @@ -5781,37 +5873,42 @@ qofs_t QDECL VFSTCP_GetLen (struct vfsfile_s *file) { return 0; } -void QDECL VFSTCP_Close (struct vfsfile_s *file) +qboolean QDECL VFSTCP_Close (struct vfsfile_s *file) { - VFSTCP_Error((tcpfile_t*)file); - Z_Free(file); + tcpfile_t *f = (tcpfile_t *)file; + qboolean success = f->sock != INVALID_SOCKET; + VFSTCP_Error(f); + Z_Free(f); + return success; } -vfsfile_t *FS_OpenTCP(const char *name, int defaultport) +vfsfile_t *FS_OpenTCPSocket(SOCKET sock, qboolean conpending, const char *peername) { tcpfile_t *newf; - int sock; + if (sock == INVALID_SOCKET) + return NULL; + + newf = Z_Malloc(sizeof(*newf) + strlen(peername)); + strcpy(newf->peer, peername); + newf->conpending = conpending; + newf->sock = sock; + newf->funcs.Close = VFSTCP_Close; + newf->funcs.Flush = NULL; + newf->funcs.GetLen = VFSTCP_GetLen; + newf->funcs.ReadBytes = VFSTCP_ReadBytes; + newf->funcs.Seek = VFSTCP_Seek; + newf->funcs.Tell = VFSTCP_Tell; + newf->funcs.WriteBytes = VFSTCP_WriteBytes; + newf->funcs.seekingisabadplan = true; + + return &newf->funcs; +} +vfsfile_t *FS_OpenTCP(const char *name, int defaultport) +{ netadr_t adr = {0}; if (NET_StringToAdr(name, defaultport, &adr)) { - sock = TCP_OpenStream(&adr); - if (sock == INVALID_SOCKET) - return NULL; - - newf = Z_Malloc(sizeof(*newf) + strlen(name)); - strcpy(newf->peer, name); - newf->conpending = true; - newf->sock = sock; - newf->funcs.Close = VFSTCP_Close; - newf->funcs.Flush = NULL; - newf->funcs.GetLen = VFSTCP_GetLen; - newf->funcs.ReadBytes = VFSTCP_ReadBytes; - newf->funcs.Seek = VFSTCP_Seek; - newf->funcs.Tell = VFSTCP_Tell; - newf->funcs.WriteBytes = VFSTCP_WriteBytes; - newf->funcs.seekingisabadplan = true; - - return &newf->funcs; + return FS_OpenTCPSocket(TCP_OpenStream(&adr), true, name); } else return NULL; diff --git a/engine/common/netinc.h b/engine/common/netinc.h index 9a39c89f..afa70fa5 100644 --- a/engine/common/netinc.h +++ b/engine/common/netinc.h @@ -1,11 +1,11 @@ -#if !defined(NACL) && !defined(FTE_TARGET_WEB) +#if !defined(NACL) && !defined(FTE_TARGET_WEB) && !defined(WINRT) #define HAVE_IPV4 //says we can send and receive AF_INET ipv4 udp packets. #define HAVE_TCP //says we can use tcp too (either ipv4 or ipv6) #define HAVE_PACKET //if we have the socket api at all... #endif -#if defined(NACL) || defined(FTE_TARGET_WEB) +#ifndef HAVE_PACKET struct sockaddr { @@ -30,12 +30,16 @@ char url[64]; }; + #define ntohs BigShort + #define htons BigShort + #define htonl BigLong + #define ntohl BigLong + #elif defined(_WIN32) #ifdef _MSC_VER #define USEIPX #endif #define WIN32_LEAN_AND_MEAN - #define byte winbyte #include #include // #include "winquake.h" @@ -99,34 +103,6 @@ #ifndef IPV6_V6ONLY #define IPV6_V6ONLY 27 #endif - - #ifdef EADDRNOTAVAIL - #undef EADDRNOTAVAIL - #endif - #ifdef EAFNOSUPPORT - #undef EAFNOSUPPORT - #endif - #ifdef ECONNABORTED - #undef ECONNABORTED - #endif - #ifdef ECONNREFUSED - #undef ECONNREFUSED - #endif - #ifdef ECONNREFUSED - #undef ECONNREFUSED - #endif - #ifdef EMSGSIZE - #undef EMSGSIZE - #endif - #ifdef EWOULDBLOCK - #undef EWOULDBLOCK - #endif - #ifdef EACCES - #undef EACCES - #endif - #ifdef ENOTCONN - #undef ENOTCONN - #endif #else #include #include @@ -278,7 +254,7 @@ typedef struct ftenet_generic_connection_s { int (*GetLocalAddresses)(struct ftenet_generic_connection_s *con, unsigned int *adrflags, netadr_t *addresses, int maxaddresses); qboolean (*ChangeLocalAddress)(struct ftenet_generic_connection_s *con, const char *newaddress); qboolean (*GetPacket)(struct ftenet_generic_connection_s *con); - qboolean (*SendPacket)(struct ftenet_generic_connection_s *con, int length, void *data, netadr_t *to); + qboolean (*SendPacket)(struct ftenet_generic_connection_s *con, int length, const void *data, netadr_t *to); void (*Close)(struct ftenet_generic_connection_s *con); #ifdef HAVE_PACKET int (*SetReceiveFDSet) (struct ftenet_generic_connection_s *con, fd_set *fdset); /*set for connections which have multiple sockets (ie: listening tcp connections)*/ @@ -310,3 +286,8 @@ void FTENET_CloseCollection(ftenet_connections_t *col); qboolean FTENET_AddToCollection(struct ftenet_connections_s *col, const char *name, const char *address, netadrtype_t addrtype, qboolean islisten); int NET_EnumerateAddresses(ftenet_connections_t *collection, struct ftenet_generic_connection_s **con, int *adrflags, netadr_t *addresses, int maxaddresses); +vfsfile_t *FS_OpenSSL(const char *hostname, vfsfile_t *source, qboolean server); +#ifdef HAVE_PACKET +vfsfile_t *FS_OpenTCPSocket(SOCKET socket, qboolean conpending, const char *peername); //conpending allows us to reject any writes until the connection has succeeded +#endif +vfsfile_t *FS_OpenTCP(const char *name, int defaultport); diff --git a/engine/common/particles.h b/engine/common/particles.h index 6d24849d..4bcc765a 100644 --- a/engine/common/particles.h +++ b/engine/common/particles.h @@ -123,7 +123,7 @@ typedef struct { char *name1; char *name2; - int (*FindParticleType) (char *name); + int (*FindParticleType) (const char *name); qboolean (*ParticleQuery) (int type, int body, char *outstr, int outstrlen); int (*RunParticleEffectTypeString) (vec3_t org, vec3_t dir, float count, char *name); diff --git a/engine/common/plugin.c b/engine/common/plugin.c index 29639ca7..b1636d18 100644 --- a/engine/common/plugin.c +++ b/engine/common/plugin.c @@ -764,7 +764,7 @@ static int Plug_NewStreamHandle(plugstream_e type) return i; } -#ifndef NACL +#ifdef HAVE_PACKET //EBUILTIN(int, NET_TCPListen, (char *ip, int port, int maxcount)); //returns a new socket with listen enabled. static qintptr_t VARGS Plug_Net_TCPListen(void *offset, quintptr_t mask, const qintptr_t *arg) @@ -939,6 +939,7 @@ qintptr_t VARGS Plug_FS_Open(void *offset, quintptr_t mask, const qintptr_t *arg if (VM_OOB(arg[1], sizeof(int))) return -2; ret = VM_POINTER(arg[1]); + *ret = -1; switch(arg[2]) { @@ -1059,7 +1060,7 @@ void Plug_Net_Close_Internal(int handle) pluginstreamarray[handle].vfs = NULL; break; case STREAM_SOCKET: -#ifndef NACL +#ifdef HAVE_PACKET closesocket(pluginstreamarray[handle].socket); #endif break; @@ -1081,7 +1082,7 @@ qintptr_t VARGS Plug_Net_Recv(void *offset, quintptr_t mask, const qintptr_t *ar return -2; switch(pluginstreamarray[handle].type) { -#ifndef NACL +#ifdef HAVE_PACKET case STREAM_SOCKET: read = recv(pluginstreamarray[handle].socket, dest, destlen, 0); if (read < 0) @@ -1112,7 +1113,7 @@ qintptr_t VARGS Plug_Net_Send(void *offset, quintptr_t mask, const qintptr_t *ar return -2; switch(pluginstreamarray[handle].type) { -#ifndef NACL +#ifdef HAVE_PACKET case STREAM_SOCKET: written = send(pluginstreamarray[handle].socket, src, srclen, 0); if (written < 0) @@ -1157,7 +1158,7 @@ qintptr_t VARGS Plug_Net_SendTo(void *offset, quintptr_t mask, const qintptr_t * return -2; switch(pluginstreamarray[handle].type) { -#ifndef NACL +#ifdef HAVE_PACKET case STREAM_SOCKET: written = sendto(pluginstreamarray[handle].socket, src, srclen, 0, (struct sockaddr*)&sockaddr, sizeof(sockaddr)); if (written < 0) @@ -1289,7 +1290,7 @@ void Plug_Initialise(qboolean fromgamedir) Plug_RegisterBuiltin("Cvar_GetString", Plug_Cvar_GetString, 0); Plug_RegisterBuiltin("Cvar_GetFloat", Plug_Cvar_GetFloat, 0); -#ifndef NACL +#ifdef HAVE_PACKET Plug_RegisterBuiltin("Net_TCPListen", Plug_Net_TCPListen, 0); Plug_RegisterBuiltin("Net_Accept", Plug_Net_Accept, 0); Plug_RegisterBuiltin("Net_TCPConnect", Plug_Net_TCPConnect, 0); diff --git a/engine/common/pr_bgcmd.c b/engine/common/pr_bgcmd.c index c668f834..ee2512fd 100644 --- a/engine/common/pr_bgcmd.c +++ b/engine/common/pr_bgcmd.c @@ -14,16 +14,20 @@ static char *cvargroup_progs = "Progs variables"; +cvar_t sv_gameplayfix_blowupfallenzombies = CVARD("sv_gameplayfix_blowupfallenzombies", "0", "Allow findradius to find non-solid entities. This may break certain mods."); +cvar_t pr_droptofloorunits = CVAR("pr_droptofloorunits", ""); cvar_t pr_brokenfloatconvert = SCVAR("pr_brokenfloatconvert", "0"); cvar_t pr_tempstringcount = SCVAR("pr_tempstringcount", "");//"16"); cvar_t pr_tempstringsize = SCVAR("pr_tempstringsize", "4096"); cvar_t pr_enable_uriget = SCVAR("pr_enable_uriget", "1"); -int tokenizeqc(char *str, qboolean dpfuckage); +int tokenizeqc(const char *str, qboolean dpfuckage); void skel_info_f(void); void skel_generateragdoll_f(void); void PF_Common_RegisterCvars(void) { + Cvar_Register (&sv_gameplayfix_blowupfallenzombies, cvargroup_progs); + Cvar_Register (&pr_droptofloorunits, cvargroup_progs); Cvar_Register (&pr_brokenfloatconvert, cvargroup_progs); Cvar_Register (&pr_tempstringcount, cvargroup_progs); Cvar_Register (&pr_tempstringsize, cvargroup_progs); @@ -43,7 +47,8 @@ char *PF_VarString (pubprogfuncs_t *prinst, int first, struct globalvars_s *pr_g int i; static char buffer[2][VARSTRINGLEN]; static int bufnum; - char *s, *out; + const char *s; + char *out; out = buffer[(bufnum++)&1]; @@ -169,7 +174,7 @@ void VARGS PR_BIError(pubprogfuncs_t *progfuncs, char *format, ...) vsnprintf (string,sizeof(string)-1, format,argptr); va_end (argptr); - if (developer.value) + if (developer.value || !progfuncs) { struct globalvars_s *pr_globals = PR_globals(progfuncs, PR_CURRENT); Con_Printf("%s\n", string); @@ -625,7 +630,7 @@ void QCBUILTIN PF_setattachment(pubprogfuncs_t *prinst, struct globalvars_s *pr_ { wedict_t *e = G_WEDICT(prinst, OFS_PARM0); wedict_t *tagentity = G_WEDICT(prinst, OFS_PARM1); - char *tagname = PR_GetStringOfs(prinst, OFS_PARM2); + const char *tagname = PR_GetStringOfs(prinst, OFS_PARM2); world_t *world = prinst->parms->user; model_t *model; @@ -829,7 +834,7 @@ void QCBUILTIN PF_FindString (pubprogfuncs_t *prinst, struct globalvars_s *pr_gl { int e; int f; - char *s; + const char *s; string_t t; wedict_t *ed; @@ -867,7 +872,7 @@ void QCBUILTIN PF_FindString (pubprogfuncs_t *prinst, struct globalvars_s *pr_gl //string(string cvarname) cvar_string void QCBUILTIN PF_cvar_string (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *str = PR_GetStringOfs(prinst, OFS_PARM0); + const char *str = PR_GetStringOfs(prinst, OFS_PARM0); cvar_t *cv = Cvar_Get(str, "", 0, "QC variables"); RETURN_CSTRING(cv->string); } @@ -875,7 +880,7 @@ void QCBUILTIN PF_cvar_string (pubprogfuncs_t *prinst, struct globalvars_s *pr_g //string(string cvarname) cvar_defstring void QCBUILTIN PF_cvar_defstring (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *str = PR_GetStringOfs(prinst, OFS_PARM0); + const char *str = PR_GetStringOfs(prinst, OFS_PARM0); cvar_t *cv = Cvar_Get(str, "", 0, "QC variables"); RETURN_CSTRING(cv->defaultstr); } @@ -883,7 +888,7 @@ void QCBUILTIN PF_cvar_defstring (pubprogfuncs_t *prinst, struct globalvars_s *p //string(string cvarname) cvar_description void QCBUILTIN PF_cvar_description (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *str = PR_GetStringOfs(prinst, OFS_PARM0); + const char *str = PR_GetStringOfs(prinst, OFS_PARM0); cvar_t *cv = Cvar_Get(str, "", 0, "QC variables"); RETURN_CSTRING(cv->description); } @@ -891,7 +896,7 @@ void QCBUILTIN PF_cvar_description (pubprogfuncs_t *prinst, struct globalvars_s //float(string name) cvar_type void QCBUILTIN PF_cvar_type (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *str = PR_GetStringOfs(prinst, OFS_PARM0); + const char *str = PR_GetStringOfs(prinst, OFS_PARM0); int ret = 0; cvar_t *v; @@ -914,7 +919,7 @@ void QCBUILTIN PF_cvar_type (pubprogfuncs_t *prinst, struct globalvars_s *pr_glo //void(string cvarname, string newvalue) cvar void QCBUILTIN PF_cvar_set (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *var_name, *val; + const char *var_name, *val; cvar_t *var; var_name = PR_GetStringOfs(prinst, OFS_PARM0); @@ -928,7 +933,7 @@ void QCBUILTIN PF_cvar_set (pubprogfuncs_t *prinst, struct globalvars_s *pr_glob void QCBUILTIN PF_cvar_setlatch (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *var_name, *val; + const char *var_name, *val; cvar_t *var; var_name = PR_GetStringOfs(prinst, OFS_PARM0); @@ -942,7 +947,7 @@ void QCBUILTIN PF_cvar_setlatch (pubprogfuncs_t *prinst, struct globalvars_s *pr void QCBUILTIN PF_cvar_setf (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *var_name; + const char *var_name; float val; cvar_t *var; @@ -959,7 +964,7 @@ void QCBUILTIN PF_cvar_setf (pubprogfuncs_t *prinst, struct globalvars_s *pr_glo //float(string name, string value) registercvar void QCBUILTIN PF_registercvar (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *name, *value; + const char *name, *value; value = PR_GetStringOfs(prinst, OFS_PARM0); if (Cvar_FindVar(value)) @@ -1124,7 +1129,7 @@ void QCBUILTIN PF_hash_getkey (pubprogfuncs_t *prinst, struct globalvars_s *pr_g void QCBUILTIN PF_hash_delete (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { pf_hashtab_t *tab = PF_hash_findtab(prinst, G_FLOAT(OFS_PARM0)); - char *name = PR_GetStringOfs(prinst, OFS_PARM1); + const char *name = PR_GetStringOfs(prinst, OFS_PARM1); pf_hashentry_t *ent = NULL; memset(G_VECTOR(OFS_RETURN), 0, sizeof(vec3_t)); if (tab) @@ -1141,7 +1146,7 @@ void QCBUILTIN PF_hash_delete (pubprogfuncs_t *prinst, struct globalvars_s *pr_g void QCBUILTIN PF_hash_get (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { pf_hashtab_t *tab = PF_hash_findtab(prinst, G_FLOAT(OFS_PARM0)); - char *name = PR_GetStringOfs(prinst, OFS_PARM1); + const char *name = PR_GetStringOfs(prinst, OFS_PARM1); pf_hashentry_t *ent = NULL; if (tab) { @@ -1181,7 +1186,7 @@ void QCBUILTIN PF_hash_getcb (pubprogfuncs_t *prinst, struct globalvars_s *pr_gl void QCBUILTIN PF_hash_add (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { pf_hashtab_t *tab = PF_hash_findtab(prinst, G_FLOAT(OFS_PARM0)); - char *name = PR_GetStringOfs(prinst, OFS_PARM1); + const char *name = PR_GetStringOfs(prinst, OFS_PARM1); void *data = G_VECTOR(OFS_PARM2); qboolean replace = (prinst->callargc>3)?G_FLOAT(OFS_PARM3):false; pf_hashentry_t *ent = NULL; @@ -1191,7 +1196,7 @@ void QCBUILTIN PF_hash_add (pubprogfuncs_t *prinst, struct globalvars_s *pr_glob Hash_Remove(&tab->tab, name); if (tab->dupestrings) { - char *value = PR_GetStringOfs(prinst, OFS_PARM2); + const char *value = PR_GetStringOfs(prinst, OFS_PARM2); int nlen = strlen(name); int vlen = strlen(data); ent = BZ_Malloc(sizeof(*ent) + nlen+1 + vlen+1); @@ -1270,7 +1275,7 @@ pf_fopen_files_t pf_fopen_files[MAX_QC_FILES]; //returns false if the file is denied. //fallbackread can be NULL, if the qc is not allowed to read that (original) file at all. -qboolean QC_FixFileName(char *name, char **result, char **fallbackread) +qboolean QC_FixFileName(const char *name, const char **result, const char **fallbackread) { if (strchr(name, ':') || //dos/win absolute path, ntfs ADS, amiga drives. reject them all. strchr(name, '\\') || //windows-only paths. @@ -1290,7 +1295,7 @@ qboolean QC_FixFileName(char *name, char **result, char **fallbackread) void QCBUILTIN PF_fopen (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *name = PR_GetStringOfs(prinst, OFS_PARM0); + const char *name = PR_GetStringOfs(prinst, OFS_PARM0); int fmode = G_FLOAT(OFS_PARM1); int fsize = G_FLOAT(OFS_PARM2); char *fallbackread; @@ -1323,6 +1328,11 @@ void QCBUILTIN PF_fopen (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals case FRIK_FILE_MMAP_RW: { vfsfile_t *f = FS_OpenVFS(pf_fopen_files[i].name, "rb", FS_GAME); + if (!f && fallbackread) + { + Q_strncpyz(pf_fopen_files[i].name, fallbackread, sizeof(pf_fopen_files[i].name)); + f = FS_OpenVFS(pf_fopen_files[i].name, "rb", FS_GAME); + } if (f) { pf_fopen_files[i].bufferlen = pf_fopen_files[i].len = VFS_GETLEN(f); @@ -1610,7 +1620,7 @@ void PF_fcloseall (pubprogfuncs_t *prinst) //DP_QC_WHICHPACK void QCBUILTIN PF_whichpack (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *srcname = PR_GetStringOfs(prinst, OFS_PARM0); + const char *srcname = PR_GetStringOfs(prinst, OFS_PARM0); qboolean makereferenced = prinst->callargc>1?G_FLOAT(OFS_PARM1):true; flocation_t loc; @@ -1735,8 +1745,8 @@ int QDECL search_enumerate(const char *name, qofs_t fsize, void *parm, searchpat //float search_begin(string pattern, float caseinsensitive, float quiet) = #74; void QCBUILTIN PF_search_begin (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { //< 0 for error, > 0 for handle. - char *pattern = PR_GetStringOfs(prinst, OFS_PARM0); -// qboolean caseinsensative = G_FLOAT(OFS_PARM1); + const char *pattern = PR_GetStringOfs(prinst, OFS_PARM0); +// qboolean caseinsensitive = G_FLOAT(OFS_PARM1); // qboolean quiet = G_FLOAT(OFS_PARM2); prvmsearch_t *s; @@ -1825,14 +1835,14 @@ void PR_fclose_progs (pubprogfuncs_t *prinst) //float isfunction(string function_name) void QCBUILTIN PF_isfunction (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *name = PR_GetStringOfs(prinst, OFS_PARM0); + const char *name = PR_GetStringOfs(prinst, OFS_PARM0); G_FLOAT(OFS_RETURN) = !!PR_FindFunction(prinst, name, PR_ANY); } //void callfunction(...) void QCBUILTIN PF_callfunction (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *name; + const char *name; func_t f; if (prinst->callargc < 1) PR_BIError(prinst, "callfunction needs at least one argument\n"); @@ -1846,8 +1856,8 @@ void QCBUILTIN PF_callfunction (pubprogfuncs_t *prinst, struct globalvars_s *pr_ //void loadfromfile(string file) void QCBUILTIN PF_loadfromfile (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *filename = PR_GetStringOfs(prinst, OFS_PARM0); - char *file = COM_LoadTempFile(filename); + const char *filename = PR_GetStringOfs(prinst, OFS_PARM0); + const char *file = COM_LoadTempFile(filename); int size; @@ -1884,7 +1894,7 @@ void QCBUILTIN PF_writetofile(pubprogfuncs_t *prinst, struct globalvars_s *pr_gl void QCBUILTIN PF_loadfromdata (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *file = PR_GetStringOfs(prinst, OFS_PARM0); + const char *file = PR_GetStringOfs(prinst, OFS_PARM0); int size; @@ -1905,7 +1915,7 @@ void QCBUILTIN PF_loadfromdata (pubprogfuncs_t *prinst, struct globalvars_s *pr_ void QCBUILTIN PF_parseentitydata(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { void *ed = G_EDICT(prinst, OFS_PARM0); - char *file = PR_GetStringOfs(prinst, OFS_PARM1); + const char *file = PR_GetStringOfs(prinst, OFS_PARM1); int size; @@ -1954,6 +1964,49 @@ void QCBUILTIN PF_edict_for_num(pubprogfuncs_t *prinst, struct globalvars_s *pr_ RETURN_EDICT(prinst, ent); } +/* +================= +PF_findradius + +Returns a chain of entities that have origins within a spherical area + +findradius (origin, radius) +================= +*/ +void QCBUILTIN PF_findradius (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +{ + extern cvar_t sv_gameplayfix_blowupfallenzombies; + edict_t *ent, *chain; + float rad; + float *org; + vec3_t eorg; + int i, j; + + chain = (edict_t *)sv.world.edicts; + + org = G_VECTOR(OFS_PARM0); + rad = G_FLOAT(OFS_PARM1); + rad = rad*rad; + + for (i=1 ; iisfree) + continue; + if (ent->v->solid == SOLID_NOT && (progstype != PROG_QW || !((int)ent->v->flags & FL_FINDABLE_NONSOLID)) && !sv_gameplayfix_blowupfallenzombies.value) + continue; + for (j=0 ; j<3 ; j++) + eorg[j] = org[j] - (ent->v->origin[j] + (ent->v->mins[j] + ent->v->maxs[j])*0.5); + if (DotProduct(eorg,eorg) > rad) + continue; + + ent->v->chain = EDICT_TO_PROG(prinst, chain); + chain = ent; + } + + RETURN_EDICT(prinst, chain); +} + //entity nextent(entity) void QCBUILTIN PF_nextent (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { @@ -2004,12 +2057,12 @@ void QCBUILTIN PF_print (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals } //FTE_STRINGS -//C style strncasecmp (compare first n characters - case insensative) -//C style strcasecmp (case insensative string compare) +//C style strncasecmp (compare first n characters - case insensitive) +//C style strcasecmp (case insensitive string compare) void QCBUILTIN PF_strncasecmp (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *a = PR_GetStringOfs(prinst, OFS_PARM0); - char *b = PR_GetStringOfs(prinst, OFS_PARM1); + const char *a = PR_GetStringOfs(prinst, OFS_PARM0); + const char *b = PR_GetStringOfs(prinst, OFS_PARM1); if (prinst->callargc > 2) { @@ -2038,11 +2091,11 @@ void QCBUILTIN PF_strncasecmp (pubprogfuncs_t *prinst, struct globalvars_s *pr_g } //FTE_STRINGS -//C style strncmp (compare first n characters - case sensative. Note that there is no strcmp provided) +//C style strncmp (compare first n characters - case sensitive. Note that there is no strcmp provided) void QCBUILTIN PF_strncmp (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *a = PR_GetStringOfs(prinst, OFS_PARM0); - char *b = PR_GetStringOfs(prinst, OFS_PARM1); + const char *a = PR_GetStringOfs(prinst, OFS_PARM0); + const char *b = PR_GetStringOfs(prinst, OFS_PARM1); if (prinst->callargc > 2) { @@ -2073,8 +2126,8 @@ void QCBUILTIN PF_strncmp (pubprogfuncs_t *prinst, struct globalvars_s *pr_globa //uses qw style \key\value strings void QCBUILTIN PF_infoget (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *info = PR_GetStringOfs(prinst, OFS_PARM0); - char *key = PR_GetStringOfs(prinst, OFS_PARM1); + const char *info = PR_GetStringOfs(prinst, OFS_PARM0); + const char *key = PR_GetStringOfs(prinst, OFS_PARM1); key = Info_ValueForKey(info, key); @@ -2084,9 +2137,9 @@ void QCBUILTIN PF_infoget (pubprogfuncs_t *prinst, struct globalvars_s *pr_globa //uses qw style \key\value strings void QCBUILTIN PF_infoadd (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *info = PR_GetStringOfs(prinst, OFS_PARM0); - char *key = PR_GetStringOfs(prinst, OFS_PARM1); - char *value = PF_VarString(prinst, 2, pr_globals); + const char *info = PR_GetStringOfs(prinst, OFS_PARM0); + const char *key = PR_GetStringOfs(prinst, OFS_PARM1); + const char *value = PF_VarString(prinst, 2, pr_globals); char temp[8192]; Q_strncpyz(temp, info, MAXTEMPBUFFERLEN); @@ -2296,7 +2349,7 @@ void QCBUILTIN PF_str2chr (pubprogfuncs_t *prinst, struct globalvars_s *pr_globa { int err; char *next; - char *instr = PR_GetStringOfs(prinst, OFS_PARM0); + const char *instr = PR_GetStringOfs(prinst, OFS_PARM0); int ofs = (prinst->callargc>1)?G_FLOAT(OFS_PARM1):0; if (VMUTF8) @@ -2326,8 +2379,8 @@ void QCBUILTIN PF_str2chr (pubprogfuncs_t *prinst, struct globalvars_s *pr_globa //strstr, without generating a new string. Use in conjunction with FRIK_FILE's substring for more similar strstr. void QCBUILTIN PF_strstrofs (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *instr = PR_GetStringOfs(prinst, OFS_PARM0); - char *match = PR_GetStringOfs(prinst, OFS_PARM1); + const char *instr = PR_GetStringOfs(prinst, OFS_PARM0); + const char *match = PR_GetStringOfs(prinst, OFS_PARM1); int firstofs = (prinst->callargc>2)?G_FLOAT(OFS_PARM2):0; @@ -2350,7 +2403,7 @@ void QCBUILTIN PF_strstrofs (pubprogfuncs_t *prinst, struct globalvars_s *pr_glo //float(string input) stof void QCBUILTIN PF_stof (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *s; + const char *s; s = PR_GetStringOfs(prinst, OFS_PARM0); @@ -2387,7 +2440,7 @@ void QCBUILTIN PF_itos (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) //int(string input) stoi void QCBUILTIN PF_stoi (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *input = PR_GetStringOfs(prinst, OFS_PARM0); + const char *input = PR_GetStringOfs(prinst, OFS_PARM0); G_INT(OFS_RETURN) = atoi(input); } @@ -2406,7 +2459,7 @@ void QCBUILTIN PF_htos (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) //int(string input) stoh void QCBUILTIN PF_stoh (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *input = PR_GetStringOfs(prinst, OFS_PARM0); + const char *input = PR_GetStringOfs(prinst, OFS_PARM0); G_INT(OFS_RETURN) = strtoul(input, NULL, 16); } @@ -2459,7 +2512,7 @@ void QCBUILTIN PF_dupstring(pubprogfuncs_t *prinst, struct globalvars_s *pr_glob { char *buf; int len = 0; - char *s[8]; + const char *s[8]; int l[8]; int i; for (i = 0; i < prinst->callargc; i++) @@ -2492,7 +2545,7 @@ void QCBUILTIN PF_strcat (pubprogfuncs_t *prinst, struct globalvars_s *pr_global { char *buf; int len = 0; - char *s[8]; + const char *s[8]; int l[8]; int i; for (i = 0; i < prinst->callargc; i++) @@ -2516,7 +2569,7 @@ void QCBUILTIN PF_strcat (pubprogfuncs_t *prinst, struct globalvars_s *pr_global void QCBUILTIN PF_substring (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { int start, length, slen; - char *s; + const char *s; char *string; s = PR_GetStringOfs(prinst, OFS_PARM0); @@ -2574,9 +2627,9 @@ void QCBUILTIN PF_strlen(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals //float(string input, string token) instr void QCBUILTIN PF_instr (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *sub; - char *s1; - char *s2; + const char *sub; + const char *s1; + const char *s2; s1 = PR_GetStringOfs(prinst, OFS_PARM0); s2 = PF_VarString(prinst, 1, pr_globals); @@ -2599,9 +2652,9 @@ void QCBUILTIN PF_strreplace (pubprogfuncs_t *prinst, struct globalvars_s *pr_gl { char resultbuf[4096]; char *result = resultbuf; - char *search = PR_GetStringOfs(prinst, OFS_PARM0); - char *replace = PR_GetStringOfs(prinst, OFS_PARM1); - char *subject = PR_GetStringOfs(prinst, OFS_PARM2); + const char *search = PR_GetStringOfs(prinst, OFS_PARM0); + const char *replace = PR_GetStringOfs(prinst, OFS_PARM1); + const char *subject = PR_GetStringOfs(prinst, OFS_PARM2); int searchlen = strlen(search); int replacelen = strlen(replace); @@ -2628,9 +2681,9 @@ void QCBUILTIN PF_strireplace (pubprogfuncs_t *prinst, struct globalvars_s *pr_g { char resultbuf[4096]; char *result = resultbuf; - char *search = PR_GetStringOfs(prinst, OFS_PARM0); - char *replace = PR_GetStringOfs(prinst, OFS_PARM1); - char *subject = PR_GetStringOfs(prinst, OFS_PARM2); + const char *search = PR_GetStringOfs(prinst, OFS_PARM0); + const char *replace = PR_GetStringOfs(prinst, OFS_PARM1); + const char *subject = PR_GetStringOfs(prinst, OFS_PARM2); int searchlen = strlen(search); int replacelen = strlen(replace); @@ -2667,7 +2720,7 @@ void QCBUILTIN PF_etos (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) // #476 float(string s) strlennocol - returns how many characters are in a string, minus color codes void QCBUILTIN PF_strlennocol (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *in = PR_GetStringOfs(prinst, OFS_PARM0); + const char *in = PR_GetStringOfs(prinst, OFS_PARM0); char result[8192]; unsigned int flagged[8192]; unsigned int len = 0; @@ -2683,7 +2736,7 @@ void QCBUILTIN PF_strlennocol (pubprogfuncs_t *prinst, struct globalvars_s *pr_g // string (string s) strdecolorize - returns the passed in string with color codes stripped void QCBUILTIN PF_strdecolorize (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *in = PR_GetStringOfs(prinst, OFS_PARM0); + const char *in = PR_GetStringOfs(prinst, OFS_PARM0); char result[8192]; unsigned int flagged[8192]; COM_ParseFunString(CON_WHITEMASK, in, flagged, sizeof(flagged), false); @@ -2695,7 +2748,7 @@ void QCBUILTIN PF_strdecolorize (pubprogfuncs_t *prinst, struct globalvars_s *pr //DP_QC_STRING_CASE_FUNCTIONS void QCBUILTIN PF_strtolower (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *in = PR_GetStringOfs(prinst, OFS_PARM0); + const char *in = PR_GetStringOfs(prinst, OFS_PARM0); char result[8192]; unicode_strtolower(in, result, sizeof(result), VMUTF8MARKUP); @@ -2706,7 +2759,7 @@ void QCBUILTIN PF_strtolower (pubprogfuncs_t *prinst, struct globalvars_s *pr_gl //DP_QC_STRING_CASE_FUNCTIONS void QCBUILTIN PF_strtoupper (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *in = PR_GetStringOfs(prinst, OFS_PARM0); + const char *in = PR_GetStringOfs(prinst, OFS_PARM0); char result[8192]; unicode_strtoupper(in, result, sizeof(result), VMUTF8MARKUP); @@ -2902,7 +2955,7 @@ void QCBUILTIN PF_buf_sort (pubprogfuncs_t *prinst, struct globalvars_s *pr_glo void QCBUILTIN PF_buf_implode (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { int bufno = G_FLOAT(OFS_PARM0)-BUFSTRBASE; - char *glue = PR_GetStringOfs(prinst, OFS_PARM1); + const char *glue = PR_GetStringOfs(prinst, OFS_PARM1); unsigned int gluelen = strlen(glue); unsigned int retlen, l, i; char **strings; @@ -2977,7 +3030,7 @@ void QCBUILTIN PF_bufstr_set (pubprogfuncs_t *prinst, struct globalvars_s *pr_g { int bufno = G_FLOAT(OFS_PARM0)-BUFSTRBASE; int index = G_FLOAT(OFS_PARM1); - char *string = PR_GetStringOfs(prinst, OFS_PARM2); + const char *string = PR_GetStringOfs(prinst, OFS_PARM2); int oldcount; if ((unsigned int)bufno >= NUMSTRINGBUFS) @@ -3001,7 +3054,7 @@ void QCBUILTIN PF_bufstr_set (pubprogfuncs_t *prinst, struct globalvars_s *pr_g strbuflist[bufno].used = index+1; } -int PF_bufstr_add_internal(int bufno, char *string, int appendonend) +int PF_bufstr_add_internal(int bufno, const char *string, int appendonend) { int index; if (appendonend) @@ -3043,7 +3096,7 @@ int PF_bufstr_add_internal(int bufno, char *string, int appendonend) void QCBUILTIN PF_bufstr_add (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { int bufno = G_FLOAT(OFS_PARM0)-BUFSTRBASE; - char *string = PR_GetStringOfs(prinst, OFS_PARM1); + const char *string = PR_GetStringOfs(prinst, OFS_PARM1); int order = G_FLOAT(OFS_PARM2); if ((unsigned int)bufno >= NUMSTRINGBUFS) @@ -3075,8 +3128,8 @@ void QCBUILTIN PF_bufstr_free (pubprogfuncs_t *prinst, struct globalvars_s *pr_ void QCBUILTIN PF_buf_cvarlist (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { int bufno = G_FLOAT(OFS_PARM0)-BUFSTRBASE; - char *pattern = PR_GetStringOfs(prinst, OFS_PARM1); - char *antipattern = PR_GetStringOfs(prinst, OFS_PARM2); + const char *pattern = PR_GetStringOfs(prinst, OFS_PARM1); + const char *antipattern = PR_GetStringOfs(prinst, OFS_PARM2); int i; cvar_group_t *grp; cvar_t *var; @@ -3109,7 +3162,7 @@ void QCBUILTIN PF_buf_cvarlist (pubprogfuncs_t *prinst, struct globalvars_s *pr //directly reads a file into a stringbuffer void QCBUILTIN PF_buf_loadfile (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *fname = PR_GetStringOfs(prinst, OFS_PARM0); + const char *fname = PR_GetStringOfs(prinst, OFS_PARM0); int bufno = G_FLOAT(OFS_PARM1)-BUFSTRBASE; vfsfile_t *file; char line[8192]; @@ -3195,11 +3248,10 @@ void QCBUILTIN PF_crc16 (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals G_FLOAT(OFS_RETURN) = QCRC_Block(str, len); } -int SHA1(char *digest, int maxdigestsize, char *string, int stringlen); void QCBUILTIN PF_digest_hex (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *hashtype = PR_GetStringOfs(prinst, OFS_PARM0); - char *str = PF_VarString(prinst, 1, pr_globals); + const char *hashtype = PR_GetStringOfs(prinst, OFS_PARM0); + const char *str = PF_VarString(prinst, 1, pr_globals); int digestsize, i; unsigned char digest[64]; unsigned char hexdig[sizeof(digest)*2+1]; @@ -3243,7 +3295,7 @@ void QCBUILTIN PF_uri_escape (pubprogfuncs_t *prinst, struct globalvars_s *pr_g unsigned char result[8192]; unsigned char *o = result; - unsigned char *s = PR_GetStringOfs(prinst, OFS_PARM0); + const unsigned char *s = PR_GetStringOfs(prinst, OFS_PARM0); *result = 0; while (*s && o < result+sizeof(result)-4) { @@ -3355,10 +3407,10 @@ void QCBUILTIN PF_uri_get (pubprogfuncs_t *prinst, struct globalvars_s *pr_glob world_t *w = prinst->parms->user; struct dl_download *dl; - unsigned char *url = PR_GetStringOfs(prinst, OFS_PARM0); + const unsigned char *url = PR_GetStringOfs(prinst, OFS_PARM0); float id = G_FLOAT(OFS_PARM1); - char *mimetype = (prinst->callargc >= 3)?PR_GetStringOfs(prinst, OFS_PARM2):""; - char *dataorsep = PR_GetStringOfs(prinst, OFS_PARM3); + const char *mimetype = (prinst->callargc >= 3)?PR_GetStringOfs(prinst, OFS_PARM2):""; + const char *dataorsep = PR_GetStringOfs(prinst, OFS_PARM3); int strbufid = G_FLOAT(OFS_PARM4); //float cryptokey = G_FLOAT(OFS_PARM5); //DP feature, not supported in FTE. @@ -3371,7 +3423,7 @@ void QCBUILTIN PF_uri_get (pubprogfuncs_t *prinst, struct globalvars_s *pr_glob if (*mimetype) { - char *data; + const char *data; Con_DPrintf("PF_uri_post(%s,%g)\n", url, id); if (strbufid) { @@ -3403,7 +3455,7 @@ void QCBUILTIN PF_uri_get (pubprogfuncs_t *prinst, struct globalvars_s *pr_glob } void QCBUILTIN PF_netaddress_resolve(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *address = PR_GetStringOfs(prinst, OFS_PARM0); + const char *address = PR_GetStringOfs(prinst, OFS_PARM0); int defaultport = (prinst->callargc > 1)?G_FLOAT(OFS_PARM1):0; netadr_t adr; char result[256]; @@ -3439,9 +3491,9 @@ void QCBUILTIN PF_ArgC (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals G_FLOAT(OFS_RETURN) = qctoken_count; } -int tokenizeqc(char *str, qboolean dpfuckage) +int tokenizeqc(const char *str, qboolean dpfuckage) { - char *start = str; + const char *start = str; while(qctoken_count > 0) { qctoken_count--; @@ -3483,11 +3535,11 @@ void QCBUILTIN PF_tokenize_console (pubprogfuncs_t *prinst, struct globalvars_s void QCBUILTIN PF_tokenizebyseparator (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *str = PR_GetStringOfs(prinst, OFS_PARM0); - char *sep[7]; + const char *str = PR_GetStringOfs(prinst, OFS_PARM0); + const char *sep[7]; int seplen[7]; int seps = 0, s; - char *start = str; + const char *start = str; int tlen; qboolean found = true; @@ -3589,7 +3641,7 @@ void QCBUILTIN PF_ArgV (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals void QCBUILTIN PF_argescape(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { char temp[8192]; - char *str = PR_GetStringOfs(prinst, OFS_PARM0); + const char *str = PR_GetStringOfs(prinst, OFS_PARM0); RETURN_TSTRING(COM_QuotedString(str, temp, sizeof(temp))); } @@ -3760,6 +3812,53 @@ void QCBUILTIN PF_anglemod (pubprogfuncs_t *prinst, struct globalvars_s *pr_glob } //Maths functions +//////////////////////////////////////////////////// +/* +=============== +PF_droptofloor + +void() droptofloor +=============== +*/ +void QCBUILTIN PF_droptofloor (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +{ + extern cvar_t pr_droptofloorunits; + world_t *world = prinst->parms->user; + wedict_t *ent; + vec3_t end; + vec3_t start; + trace_t trace; + const float *gravitydir; + extern const vec3_t standardgravity; + + ent = PROG_TO_WEDICT(prinst, pr_global_struct->self); + + if (ent->xv->gravitydir[2] || ent->xv->gravitydir[1] || ent->xv->gravitydir[0]) + gravitydir = ent->xv->gravitydir; + else + gravitydir = standardgravity; + + VectorCopy (ent->v->origin, end); + if (pr_droptofloorunits.value > 0) + VectorMA(end, pr_droptofloorunits.value, gravitydir, end); + else + VectorMA(end, 256, gravitydir, end); + + VectorCopy (ent->v->origin, start); + trace = World_Move (world, start, ent->v->mins, ent->v->maxs, end, MOVE_NORMAL, ent); + + if (trace.fraction == 1 || trace.allsolid) + G_FLOAT(OFS_RETURN) = 0; + else + { + VectorCopy (trace.endpos, ent->v->origin); + World_LinkEdict (world, ent, false); + ent->v->flags = (int)ent->v->flags | FL_ONGROUND; + ent->v->groundentity = EDICT_TO_PROG(prinst, trace.ent); + G_FLOAT(OFS_RETURN) = 1; + } +} + //////////////////////////////////////////////////// //Vector functions @@ -4054,7 +4153,7 @@ void QCBUILTIN PF_externvalue (pubprogfuncs_t *prinst, struct globalvars_s *pr_g void QCBUILTIN PF_externcall (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) //this func calls a function in annother progs (by name) { int progsnum; - char *funcname; + const char *funcname; int i; string_t failedst = G_INT(OFS_PARM1); func_t f; @@ -4215,7 +4314,7 @@ void QCBUILTIN PF_calltimeofday (pubprogfuncs_t *prinst, struct globalvars_s *pr } } -void QCBUILTIN PF_sprintf_internal (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals, char *s, int firstarg, char *outbuf, int outbuflen) +void QCBUILTIN PF_sprintf_internal (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals, const char *s, int firstarg, char *outbuf, int outbuflen) { const char *s0; char *o = outbuf, *end = outbuf + outbuflen, *err; @@ -4608,7 +4707,7 @@ void QCBUILTIN PF_putentityfieldstring (pubprogfuncs_t *prinst, struct globalvar { unsigned int fidx = G_FLOAT(OFS_PARM0); wedict_t *ent = (wedict_t *)G_EDICT(prinst, OFS_PARM1); - char *str = PR_GetStringOfs(prinst, OFS_PARM2); + const char *str = PR_GetStringOfs(prinst, OFS_PARM2); eval_t *eval; unsigned int count = 0; fdef_t *fdef = prinst->FieldInfo(prinst, &count); @@ -4624,7 +4723,7 @@ void QCBUILTIN PF_putentityfieldstring (pubprogfuncs_t *prinst, struct globalvar //must match ordering in Cmd_ExecuteString. void QCBUILTIN PF_checkcommand (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *str = PR_GetStringOfs(prinst, OFS_PARM0); + const char *str = PR_GetStringOfs(prinst, OFS_PARM0); //functions, aliases, cvars. in that order. if (Cmd_Exists(str)) { @@ -4722,7 +4821,7 @@ void PR_AutoCvar(pubprogfuncs_t *prinst, cvar_t *var) void PDECL PR_FoundAutoCvarGlobal(pubprogfuncs_t *progfuncs, char *name, eval_t *val, etype_t type, void *ctx) { cvar_t *var; - char *vals; + const char *vals; int nlen; name += 9; //autocvar_ @@ -4946,6 +5045,7 @@ lh_extension_t QSG_Extensions[] = { {"DP_SV_EXTERIORMODELFORCLIENT"}, {"DP_SV_NODRAWTOCLIENT"}, //I prefer my older system. Guess I might as well remove that older system at some point. {"DP_SV_PLAYERPHYSICS"}, + //FTE cannot implement this one, because dp's arguments are the wrong way around. its otherwise implemented. {"DP_SV_POINTPARTICLES"}, {"DP_SV_POINTSOUND", 1, NULL, {"pointsound"}}, {"DP_SV_PRECACHEANYTIME"}, {"DP_SV_SETCOLOR"}, @@ -4954,7 +5054,7 @@ lh_extension_t QSG_Extensions[] = { {"DP_SV_WRITEUNTERMINATEDSTRING", 1, NULL, {"WriteUnterminatedString"}}, {"DP_TE_BLOOD", 1, NULL, {"te_blood"}}, {"DP_TE_BLOODSHOWER", 1, NULL, {"te_bloodshower"}}, - {"_DP_TE_CUSTOMFLASH", 1, NULL, {"te_customflash"}}, + {"DP_TE_CUSTOMFLASH", 1, NULL, {"te_customflash"}}, {"DP_TE_EXPLOSIONRGB", 1, NULL, {"te_explosionrgb"}}, {"_DP_TE_FLAMEJET", 1, NULL, {"te_flamejet"}}, {"DP_TE_PARTICLECUBE", 1, NULL, {"te_particlecube"}}, diff --git a/engine/common/pr_common.h b/engine/common/pr_common.h index cfe105f5..aef8d0df 100644 --- a/engine/common/pr_common.h +++ b/engine/common/pr_common.h @@ -123,6 +123,7 @@ void QCBUILTIN PF_stov (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) void QCBUILTIN PF_dupstring(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); void QCBUILTIN PF_forgetstring(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); void QCBUILTIN PF_Spawn (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); +void QCBUILTIN PF_droptofloor (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); void QCBUILTIN PF_min (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); void QCBUILTIN PF_max (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); void QCBUILTIN PF_registercvar (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); @@ -133,7 +134,7 @@ void QCBUILTIN PF_atan (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) void QCBUILTIN PF_atan2 (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); void QCBUILTIN PF_tan (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); void QCBUILTIN PF_localcmd (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); -void QCBUILTIN PF_sprintf_internal (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals, char *s, int firstarg, char *outbuf, int outbuflen); +void QCBUILTIN PF_sprintf_internal (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals, const char *s, int firstarg, char *outbuf, int outbuflen); void QCBUILTIN PF_sprintf (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); void QCBUILTIN PF_random (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); void QCBUILTIN PF_fclose (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); @@ -304,6 +305,7 @@ void QCBUILTIN PF_strpad (pubprogfuncs_t *prinst, struct globalvars_s *pr_global void QCBUILTIN PF_digest_hex (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); +void QCBUILTIN PF_findradius (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); void QCBUILTIN PF_edict_for_num (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); void QCBUILTIN PF_num_for_edict (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); void QCBUILTIN PF_cvar_defstring (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); @@ -442,21 +444,22 @@ void QCBUILTIN PF_WriteEntity (pubprogfuncs_t *prinst, struct globalvars_s *pr_g void QCBUILTIN PF_multicast (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); void QCBUILTIN PF_svtraceline (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); void QCBUILTIN PF_changelevel (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); -void QCBUILTIN PF_applylightstyle(int style, char *val, int col); -void PF_ambientsound_Internal (float *pos, char *samp, float vol, float attenuation); +void QCBUILTIN PF_applylightstyle(int style, const char *val, int col); +void PF_ambientsound_Internal (float *pos, const char *samp, float vol, float attenuation); void QCBUILTIN PF_makestatic (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); void QCBUILTIN PF_logfrag (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); void QCBUILTIN PF_ExecuteCommand (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); void QCBUILTIN PF_setspawnparms (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); -void QCBUILTIN PF_ForceInfoKey(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); void QCBUILTIN PF_precache_vwep_model(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); +int PF_ForceInfoKey_Internal(unsigned int entnum, const char *key, const char *value); int PF_checkclient_Internal (pubprogfuncs_t *prinst); -void PF_precache_sound_Internal (pubprogfuncs_t *prinst, char *s); -int PF_precache_model_Internal (pubprogfuncs_t *prinst, char *s, qboolean queryonly); -void PF_setmodel_Internal (pubprogfuncs_t *prinst, edict_t *e, char *m); -char *PF_infokey_Internal (int entnum, char *value); -void PF_centerprint_Internal (int entnum, qboolean plaque, char *s); -void PF_WriteString_Internal (int target, char *str); +void PF_precache_sound_Internal (pubprogfuncs_t *prinst, const char *s); +int PF_precache_model_Internal (pubprogfuncs_t *prinst, const char *s, qboolean queryonly); +void PF_setmodel_Internal (pubprogfuncs_t *prinst, edict_t *e, const char *m); +char *PF_infokey_Internal (int entnum, const char *value); +void PF_stuffcmd_Internal(int entnum, const char *str); +void PF_centerprint_Internal (int entnum, qboolean plaque, const char *s); +void PF_WriteString_Internal (int target, const char *str); pbool QDECL ED_CanFree (edict_t *ed); #endif diff --git a/engine/common/protocol.h b/engine/common/protocol.h index f1ecfb21..81d7bb11 100644 --- a/engine/common/protocol.h +++ b/engine/common/protocol.h @@ -111,8 +111,9 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define PORT_QWCLIENT 27001 #define PORT_QWMASTER 27000 #define PORT_QWSERVER 27500 -#define PORT_Q2CLIENT 27901 -#define PORT_Q2SERVER 27910 +#define PORT_Q2CLIENT 27901 +#define PORT_Q2SERVER 27910 +#define PORT_Q3SERVER 27960 //hexen2: 26900 @@ -310,6 +311,47 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define svc_invalid 256 +enum clustercmdops_e +{ + ccmd_bad = 0, //abort! + ccmd_stuffcmd = 1, //regular ol stuffcmd + //string concommand + ccmd_print = 2, + //string message + ccmd_acceptserver, + //serverid + ccmd_takeplayer, //master->server, saying to allocate a slot for a player. + //long plid + //long fromsvid (0=no reply needed) + //byte statcount + //float stats[statcount] + ccmd_transferplayer, //server->master, asking to move them to a new server. + //long plid + //string map + //byte ipv4=0, ipv6=1 + //byte statcount + //float stats[statcount] + ccmd_transferedplayer, //master->server, saying the transfer was completed. original server no longer owns the player. + //long toserver, + //long playerid + ccmd_tookplayer, //server->master->server, saying that a player was taken. + //long svid (this is always the *other* server) + //long plid + //string addr (this is the client's address when sent to the master, and the server's address that took the message in the message to the source server) + ccmd_transferabort, //server->master->server, saying that a player was rejected. + //long plid + //long fromsvid + //string server + ccmd_saveplayer, //server->master, saves a player's stats. + //long plid + //byte statcount + //float stats[statcount] + ccmd_serveraddress, //server->master, contains a few net addresses + //string address[] + //byte 0 +}; + + enum svcq2_ops_e { svcq2_bad, //0 diff --git a/engine/common/q3common.c b/engine/common/q3common.c index bf90a273..7b1965dd 100644 --- a/engine/common/q3common.c +++ b/engine/common/q3common.c @@ -286,9 +286,9 @@ int VM_GetFileList(char *path, char *ext, char *output, int buffersize) if (!strcmp(path, "$modlist")) { vms.skip=0; - Sys_EnumerateFiles((vms.dir=com_quakedir), "*", VMEnumMods, &vms, NULL); - if (*com_homedir) - Sys_EnumerateFiles((vms.dir=com_homedir), "*", VMEnumMods, &vms, NULL); + Sys_EnumerateFiles((vms.dir=com_gamepath), "*", VMEnumMods, &vms, NULL); + if (*com_homepath) + Sys_EnumerateFiles((vms.dir=com_homepath), "*", VMEnumMods, &vms, NULL); } else if (*(char *)ext == '.' || *(char *)ext == '/') COM_EnumerateFiles(va("%s/*%s", path, ext), VMEnum, &vms); diff --git a/engine/common/sha1.c b/engine/common/sha1.c index e5abef55..f9ee56b1 100644 --- a/engine/common/sha1.c +++ b/engine/common/sha1.c @@ -44,15 +44,15 @@ typedef struct } SHA1_CTX; #define DIGEST_SIZE 20 -void SHA1Transform(unsigned int state[5], unsigned char buffer[64]); +void SHA1Transform(unsigned int state[5], const unsigned char buffer[64]); void SHA1Init(SHA1_CTX* context); -void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int len); +void SHA1Update(SHA1_CTX* context, const unsigned char* data, unsigned int len); void SHA1Final(unsigned char digest[DIGEST_SIZE], SHA1_CTX* context); /* Hash a single 512-bit block. This is the core of the algorithm. */ -void SHA1Transform(unsigned int state[5], unsigned char buffer[64]) +void SHA1Transform(unsigned int state[5], const unsigned char buffer[64]) { unsigned int a, b, c, d, e; typedef union @@ -122,7 +122,7 @@ void SHA1Init(SHA1_CTX* context) /* Run your data through this. */ -void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int len) +void SHA1Update(SHA1_CTX* context, const unsigned char* data, unsigned int len) { unsigned int i, j; @@ -179,7 +179,7 @@ memset(&finalcount, 0, 8); } -int SHA1(char *digest, int maxdigestsize, char *string, int stringlen) +int SHA1(char *digest, int maxdigestsize, const char *string, int stringlen) { SHA1_CTX context; if (maxdigestsize < DIGEST_SIZE) @@ -216,7 +216,7 @@ hacked up a bit by someone else... #define IPAD 0x36 #define OPAD 0x5c -static void memxor(char *dest, char *src, size_t length) +static void memxor(char *dest, const char *src, size_t length) { size_t i; for (i = 0; i < length; i++) @@ -226,8 +226,8 @@ static void memxor(char *dest, char *src, size_t length) } int SHA1_HMAC(unsigned char *digest, int maxdigestsize, - unsigned char *data, int datalen, - unsigned char *key, int keylen) + const unsigned char *data, int datalen, + const unsigned char *key, int keylen) { SHA1_CTX inner; SHA1_CTX outer; diff --git a/engine/common/sys_win_threads.c b/engine/common/sys_win_threads.c index 8c22589d..de691ab9 100644 --- a/engine/common/sys_win_threads.c +++ b/engine/common/sys_win_threads.c @@ -21,14 +21,11 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include #include -#include +#include "winquake.h" #include -#ifdef MULTITHREAD +#if !defined(WINRT) && defined(MULTITHREAD) #include -#endif - -#ifdef MULTITHREAD /* Thread creation calls */ typedef struct threadwrap_s { @@ -309,4 +306,113 @@ void Sys_DestroyConditional(void *condv) DeleteCriticalSection(&cv->mainlock); free(cv); } + #endif + +#ifdef SUBSERVERS +typedef struct slaveserver_s +{ + pubsubserver_t pub; + + HANDLE inpipe; + HANDLE outpipe; + + qbyte inbuffer[2048]; + int inbufsize; +} winsubserver_t; + + +pubsubserver_t *Sys_ForkServer(void) +{ + char exename[256]; + char curdir[256]; + char cmdline[8192]; + PROCESS_INFORMATION childinfo; + STARTUPINFO startinfo; + SECURITY_ATTRIBUTES pipesec = {sizeof(pipesec), NULL, TRUE}; + winsubserver_t *ctx = Z_Malloc(sizeof(*ctx)); + + GetModuleFileName(NULL, exename, sizeof(exename)); + GetCurrentDirectory(sizeof(curdir), curdir); + Q_snprintfz(cmdline, sizeof(cmdline), "foo -clusterslave %s", FS_GetManifestArgs()); //fixme: include which manifest is in use, so configs get set up the same. + + memset(&startinfo, 0, sizeof(startinfo)); + startinfo.cb = sizeof(startinfo); + startinfo.hStdInput = NULL; + startinfo.hStdError = NULL; + startinfo.hStdOutput = NULL; + startinfo.dwFlags |= STARTF_USESTDHANDLES; + + //create pipes for the stdin/stdout. + CreatePipe(&ctx->inpipe, &startinfo.hStdOutput, &pipesec, 0); + CreatePipe(&startinfo.hStdInput, &ctx->outpipe, &pipesec, 0); + + SetHandleInformation(ctx->inpipe, HANDLE_FLAG_INHERIT, 0); + SetHandleInformation(ctx->outpipe, HANDLE_FLAG_INHERIT, 0); + SetHandleInformation(startinfo.hStdOutput, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT); + SetHandleInformation(startinfo.hStdInput, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT); + + CreateProcess(exename, cmdline, NULL, NULL, TRUE, 0, NULL, curdir, &startinfo, &childinfo); + + //these ends of the pipes were inherited by now, so we can discard them in the caller. + CloseHandle(startinfo.hStdOutput); + CloseHandle(startinfo.hStdInput); + return &ctx->pub; +} + +void Sys_InstructSlave(pubsubserver_t *ps, sizebuf_t *cmd) +{ + winsubserver_t *s = (winsubserver_t*)ps; + DWORD written = 0; + cmd->data[0] = cmd->cursize & 0xff; + cmd->data[1] = (cmd->cursize>>8) & 0xff; + WriteFile(s->outpipe, cmd->data, cmd->cursize, &written, NULL); +} + +void SSV_InstructMaster(sizebuf_t *cmd) +{ + HANDLE output = GetStdHandle(STD_OUTPUT_HANDLE); + DWORD written = 0; + cmd->data[0] = cmd->cursize & 0xff; + cmd->data[1] = (cmd->cursize>>8) & 0xff; + WriteFile(output, cmd->data, cmd->cursize, &written, NULL); +} + +int Sys_SubServerRead(pubsubserver_t *ps) +{ + DWORD avail; + winsubserver_t *s = (winsubserver_t*)ps; + + if (!PeekNamedPipe(s->inpipe, NULL, 0, NULL, &avail, NULL)) + { + CloseHandle(s->inpipe); + CloseHandle(s->outpipe); + Con_Printf("%i:%s has died\n", s->pub.id, s->pub.name); + return -1; + } + else if (avail) + { + if (avail > sizeof(s->inbuffer)-1-s->inbufsize) + avail = sizeof(s->inbuffer)-1-s->inbufsize; + if (ReadFile(s->inpipe, s->inbuffer+s->inbufsize, avail, &avail, NULL)) + s->inbufsize += avail; + } + + if(s->inbufsize >= 2) + { + unsigned short len = s->inbuffer[0] | (s->inbuffer[1]<<8); + if (s->inbufsize >= len && len>=2) + { + memcpy(net_message.data, s->inbuffer+2, len-2); + net_message.cursize = len-2; + memmove(s->inbuffer, s->inbuffer+len, s->inbufsize - len); + s->inbufsize -= len; + MSG_BeginReading (msg_nullnetprim); + + return 1; + } + } + return 0; +} +#endif + diff --git a/engine/common/vm.h b/engine/common/vm.h index 25ecce6e..cdd6c250 100644 --- a/engine/common/vm.h +++ b/engine/common/vm.h @@ -133,6 +133,7 @@ int VM_GetFileList(char *path, char *ext, char *output, int buffersize); #ifdef VM_CG void CG_Stop (void); void CG_Start (void); +qboolean CG_VideoRestarted(void); int CG_Refresh(void); qboolean CG_Command(void); qboolean CG_KeyPress(int key, int unicode, int down); diff --git a/engine/common/zone.c b/engine/common/zone.c index 15dd7f83..9a7a18ea 100644 --- a/engine/common/zone.c +++ b/engine/common/zone.c @@ -398,7 +398,7 @@ void *BZ_MallocNamed(int size, char *file, int line) //BZ_MallocNamed but allowe return mem; } #else -void *BZ_Malloc(int size) //Doesn't clear. The expectation is a large file, rather than sensative data structures. +void *BZ_Malloc(int size) //Doesn't clear. The expectation is a large file, rather than sensitive data structures. { void *mem = BZF_Malloc(size); if (!mem) diff --git a/engine/common/zone.h b/engine/common/zone.h index 682d5fc9..a64cf703 100644 --- a/engine/common/zone.h +++ b/engine/common/zone.h @@ -97,7 +97,7 @@ void VARGS Z_TagFree(void *ptr); void VARGS Z_FreeTags(int tag); //void *Z_Realloc (void *ptr, int size); -//Big Zone: allowed to fail, doesn't clear. The expectation is a large file, rather than sensative data structures. +//Big Zone: allowed to fail, doesn't clear. The expectation is a large file, rather than sensitive data structures. //(this is a nicer name for malloc) void *BZ_Malloc(int size); void *BZF_Malloc(int size); diff --git a/engine/d3d/d3d11_backend.c b/engine/d3d/d3d11_backend.c index 57088d1a..d183a094 100644 --- a/engine/d3d/d3d11_backend.c +++ b/engine/d3d/d3d11_backend.c @@ -12,6 +12,7 @@ extern ID3D11DeviceContext *d3ddevctx; extern cvar_t r_shadow_realtime_world_lightmaps; extern cvar_t gl_overbright; +extern cvar_t r_portalrecursion; void D3D11_TerminateShadowMap(void); void D3D11BE_BeginShadowmapFace(void); @@ -229,10 +230,14 @@ typedef struct float depthrange; qboolean purgevertexstream; - ID3D11Buffer *vertexstream; +#define NUMVBUFFERS 3 + ID3D11Buffer *vertexstream[NUMVBUFFERS]; + int vertexstreamcycle; int vertexstreamoffset; qboolean purgeindexstream; - ID3D11Buffer *indexstream; +#define NUMIBUFFERS 3 + ID3D11Buffer *indexstream[NUMIBUFFERS]; + int indexstreamcycle; int indexstreamoffset; @@ -337,13 +342,19 @@ static void BE_DestroyVariousStates(void) BZ_Free(bs); } - if (shaderstate.indexstream) - ID3D11Buffer_Release(shaderstate.indexstream); - shaderstate.indexstream = NULL; + for (i = 0; i < NUMIBUFFERS; i++) + { + if (shaderstate.indexstream[i]) + ID3D11Buffer_Release(shaderstate.indexstream[i]); + shaderstate.indexstream[i] = NULL; + } - if (shaderstate.vertexstream) - ID3D11Buffer_Release(shaderstate.vertexstream); - shaderstate.vertexstream = NULL; + for (i = 0; i < NUMVBUFFERS; i++) + { + if (shaderstate.vertexstream[i]) + ID3D11Buffer_Release(shaderstate.vertexstream[i]); + shaderstate.vertexstream[i] = NULL; + } if (shaderstate.lcbuffer) ID3D11Buffer_Release(shaderstate.lcbuffer); @@ -748,23 +759,30 @@ void D3D11BE_Init(void) return; //generate the streaming buffers for stuff that doesn't provide info in nice static vbos - bd.BindFlags = D3D11_BIND_INDEX_BUFFER; - bd.ByteWidth = VERTEXSTREAMSIZE; - bd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; - bd.MiscFlags = 0; - bd.StructureByteStride = 0; - bd.Usage = D3D11_USAGE_DYNAMIC; - if (FAILED(ID3D11Device_CreateBuffer(pD3DDev11, &bd, NULL, &shaderstate.indexstream))) - return; - bd.BindFlags = D3D11_BIND_VERTEX_BUFFER; - bd.ByteWidth = VERTEXSTREAMSIZE; - bd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; - bd.MiscFlags = 0; - bd.StructureByteStride = 0; - bd.Usage = D3D11_USAGE_DYNAMIC; - if (FAILED(ID3D11Device_CreateBuffer(pD3DDev11, &bd, NULL, &shaderstate.vertexstream))) - return; + for (i = 0; i < NUMIBUFFERS; i++) + { + bd.BindFlags = D3D11_BIND_INDEX_BUFFER; + bd.ByteWidth = VERTEXSTREAMSIZE; + bd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; + bd.MiscFlags = 0; + bd.StructureByteStride = 0; + bd.Usage = D3D11_USAGE_DYNAMIC; + if (FAILED(ID3D11Device_CreateBuffer(pD3DDev11, &bd, NULL, &shaderstate.indexstream[i]))) + return; + } + for (i = 0; i < NUMVBUFFERS; i++) + { + bd.BindFlags = D3D11_BIND_VERTEX_BUFFER; + bd.ByteWidth = VERTEXSTREAMSIZE; + bd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; + bd.MiscFlags = 0; + bd.StructureByteStride = 0; + bd.Usage = D3D11_USAGE_DYNAMIC; + if (FAILED(ID3D11Device_CreateBuffer(pD3DDev11, &bd, NULL, &shaderstate.vertexstream[i]))) + return; + } + /* for (i = 0; i < LSHADER_MODES; i++) { if ((i & LSHADER_CUBE) && (i & LSHADER_SPOT)) @@ -775,6 +793,7 @@ void D3D11BE_Init(void) (i & LSHADER_CUBE)?"#CUBE":"") , SUF_NONE, LIGHTPASS_SHADER); } + */ // shaderstate.shader_rtlight = R_RegisterShader("rtlight", SUF_NONE, LIGHTPASS_SHADER); shaderstate.depthonly = R_RegisterShader("depthonly", SUF_NONE, "{\n" @@ -791,7 +810,9 @@ void D3D11BE_Init(void) void D3D11BE_Shutdown(void) { shaderstate.inited = false; +#ifdef RTLIGHTS D3D11_TerminateShadowMap(); +#endif BE_DestroyVariousStates(); Z_Free(shaderstate.wbatches); shaderstate.wbatches = NULL; @@ -846,7 +867,19 @@ static void BindTexture(unsigned int tu, const texid_t *id) } } -static void SelectPassTexture(unsigned int tu, shaderpass_t *pass) +void D3D11BE_UnbindAllTextures(void) +{ + int i; + for (i = 0; i < shaderstate.lastpasscount; i++) + shaderstate.pendingtextures[i] = NULL; + if (i) + { + ID3D11DeviceContext_PSSetShaderResources(d3ddevctx, 0, i, shaderstate.pendingtextures); + shaderstate.lastpasscount = 0; + } +} + +static void SelectPassTexture(unsigned int tu, const shaderpass_t *pass) { extern texid_t r_whiteimage, missing_texture_gloss, missing_texture_normal; texid_t foo; @@ -912,10 +945,15 @@ static void SelectPassTexture(unsigned int tu, shaderpass_t *pass) FIXME: no code to grab the current screen and convert to a texture break;*/ case T_GEN_VIDEOMAP: +#ifndef NOMEDIA + if (pass->cin) { foo = Media_UpdateForShader(pass->cin); BindTexture(tu, &foo); + break; } +#endif + BindTexture(tu, &r_nulltex); break; case T_GEN_LIGHTCUBEMAP: //light's projected cubemap @@ -923,10 +961,15 @@ static void SelectPassTexture(unsigned int tu, shaderpass_t *pass) break; case T_GEN_SHADOWMAP: //light's depth values. - if (!shaderstate.curdlight) +#ifdef RTLIGHTS + if (shaderstate.curdlight) + { + foo = D3D11_GetShadowMap(shaderstate.curdlight->fov>0); + BindTexture(tu, &foo); break; - foo = D3D11_GetShadowMap(shaderstate.curdlight->fov>0); - BindTexture(tu, &foo); + } +#endif + BindTexture(tu, &r_nulltex); break; case T_GEN_CURRENTRENDER://copy the current screen to a texture, and draw that @@ -940,6 +983,7 @@ static void SelectPassTexture(unsigned int tu, shaderpass_t *pass) case T_GEN_RIPPLEMAP: //ripplemap image (water surface distortions-as-fbo) case T_GEN_SOURCECUBE: //used for render-to-texture targets + BindTexture(tu, &r_nulltex); break; } @@ -1824,14 +1868,18 @@ static void BE_ApplyUniforms(program_t *prog, int permu) //FIXME: how many of these calls can we avoid? ID3D11DeviceContext_IASetInputLayout(d3ddevctx, prog->permu[permu].handle.hlsl.layout); ID3D11DeviceContext_VSSetShader(d3ddevctx, prog->permu[permu].handle.hlsl.vert, NULL, 0); + ID3D11DeviceContext_HSSetShader(d3ddevctx, prog->permu[permu].handle.hlsl.hull, NULL, 0); + ID3D11DeviceContext_DSSetShader(d3ddevctx, prog->permu[permu].handle.hlsl.domain, NULL, 0); ID3D11DeviceContext_PSSetShader(d3ddevctx, prog->permu[permu].handle.hlsl.frag, NULL, 0); - ID3D11DeviceContext_IASetPrimitiveTopology(d3ddevctx, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + ID3D11DeviceContext_IASetPrimitiveTopology(d3ddevctx, prog->permu[permu].handle.hlsl.topology); ID3D11DeviceContext_VSSetConstantBuffers(d3ddevctx, 0, 3, cbuf); + ID3D11DeviceContext_HSSetConstantBuffers(d3ddevctx, 0, 3, cbuf); + ID3D11DeviceContext_DSSetConstantBuffers(d3ddevctx, 0, 3, cbuf); ID3D11DeviceContext_PSSetConstantBuffers(d3ddevctx, 0, 3, cbuf); } -static void BE_RenderMeshProgram(shader_t *s, unsigned int vertcount, unsigned int idxfirst, unsigned int idxcount) +static void BE_RenderMeshProgram(const shader_t *s, unsigned int vertcount, unsigned int idxfirst, unsigned int idxcount) { int passno; int perm = 0; @@ -1844,7 +1892,7 @@ static void BE_RenderMeshProgram(shader_t *s, unsigned int vertcount, unsigned i perm |= PERMUTATION_FULLBRIGHT; if (p->permu[perm|PERMUTATION_UPPERLOWER].handle.hlsl.vert && (TEXVALID(shaderstate.curtexnums->upperoverlay) || TEXVALID(shaderstate.curtexnums->loweroverlay))) perm |= PERMUTATION_UPPERLOWER; - if (r_refdef.gfog_rgbd[3] && p->permu[perm|PERMUTATION_FOG].handle.hlsl.vert) + if (r_refdef.globalfog.density && p->permu[perm|PERMUTATION_FOG].handle.hlsl.vert) perm |= PERMUTATION_FOG; // if (r_glsl_offsetmapping.ival && TEXVALID(shaderstate.curtexnums->bump) && p->handle[perm|PERMUTATION_OFFSET.hlsl.vert) // perm |= PERMUTATION_OFFSET; @@ -1910,7 +1958,7 @@ static void D3D11BE_Cull(unsigned int cullflags) rasterdesc.DepthBias = 0; rasterdesc.DepthBiasClamp = 0.0f; rasterdesc.DepthClipEnable = true; - rasterdesc.FillMode = D3D11_FILL_SOLID; + rasterdesc.FillMode = 0?D3D11_FILL_WIREFRAME:D3D11_FILL_SOLID; rasterdesc.FrontCounterClockwise = false; rasterdesc.MultisampleEnable = false; rasterdesc.ScissorEnable = false;//true; @@ -1924,16 +1972,16 @@ static void D3D11BE_Cull(unsigned int cullflags) switch(hr) { case DXGI_ERROR_DEVICE_HUNG: - Sys_Error("Your graphics driver found something too interesting\n"); + Sys_Error("DXGI_ERROR_DEVICE_HUNG\nThe application's device failed due to badly formed commands sent by the application.\n"); break; case DXGI_ERROR_DEVICE_REMOVED: - Sys_Error("You unplugged your graphics card. A bit rude, don't you think?\n"); + Sys_Error("DXGI_ERROR_DEVICE_REMOVED\nThe video card has been physically removed from the system, or a driver upgrade for the video card has occurred.\n"); break; case DXGI_ERROR_DEVICE_RESET: - Sys_Error("Your drivers are fucked and got reset\n"); + Sys_Error("DXGI_ERROR_DEVICE_RESET\nThe device failed due to a badly formed command.\n"); break; case DXGI_ERROR_DRIVER_INTERNAL_ERROR: - Sys_Error("Your graphics driver is beyond redemption\n"); + Sys_Error("DXGI_ERROR_DRIVER_INTERNAL_ERROR\nThe driver encountered a problem and was put into the device removed state.\n"); break; case DXGI_ERROR_INVALID_CALL: Sys_Error("invalid call! oh noes!\n"); @@ -1953,6 +2001,7 @@ static void D3D11BE_Cull(unsigned int cullflags) static void BE_DrawMeshChain_Internal(void) { + const shader_t *altshader; unsigned int vertcount, idxcount, idxfirst; mesh_t *m; // void *map; @@ -1964,6 +2013,20 @@ static void BE_DrawMeshChain_Internal(void) // float pushdepth; // float pushfactor; + if (0)//shaderstate.force2d) + { + RQuantAdd(RQUANT_2DBATCHES, 1); + } + else if (shaderstate.curentity == &r_worldentity) + { + RQuantAdd(RQUANT_WORLDBATCHES, 1); + } + else + { + RQuantAdd(RQUANT_ENTBATCHES, 1); + } + + D3D11BE_Cull(shaderstate.curshader->flags & (SHADER_CULL_FRONT | SHADER_CULL_BACK)); /* pushdepth = (shaderstate.curshader->polyoffset.factor + ((shaderstate.flags & BEF_PUSHDEPTH)?r_polygonoffset_submodel_factor.value:0))/0xffff; @@ -2053,25 +2116,11 @@ static void BE_DrawMeshChain_Internal(void) BE_RenderMeshProgram(shaderstate.shader_rtlight[shaderstate.curlmode], vertcount, idxfirst, idxcount); break; case BEM_DEPTHONLY: - if (shaderstate.depthonly->prog) - BE_RenderMeshProgram(shaderstate.depthonly, vertcount, idxfirst, idxcount); -#if 0 - shaderstate.lastpasscount = 0; - i = 0; - if (i != shaderstate.curvertdecl) - { - shaderstate.curvertdecl = i; -// d3dcheck(IDirect3DDevice9_SetVertexDeclaration(pD3DDev9, vertexdecls[shaderstate.curvertdecl])); - } - /*deactivate any extras*/ - for (passno = 0; passno < shaderstate.lastpasscount; ) - { - BindTexture(passno, NULL); - passno++; - } - shaderstate.lastpasscount = 0; - BE_SubmitMeshChain(idxfirst); -#endif + altshader = shaderstate.curshader->bemoverrides[bemoverride_depthonly]; + if (!altshader) + altshader = shaderstate.depthonly; + if (altshader->prog) + BE_RenderMeshProgram(altshader, vertcount, idxfirst, idxcount); break; default: case BEM_STANDARD: @@ -2093,18 +2142,37 @@ void D3D11BE_SelectMode(backendmode_t mode) if (mode == BEM_STENCIL) D3D11BE_ApplyShaderBits(SBITS_MASK_BITS, NULL); } - +qboolean D3D11BE_GenerateRTLightShader(unsigned int lmode) +{ + if (!shaderstate.shader_rtlight[lmode]) + { + shaderstate.shader_rtlight[lmode] = R_RegisterShader(va("rtlight%s%s%s", + (lmode & LSHADER_SMAP)?"#PCF":"", + (lmode & LSHADER_SPOT)?"#SPOT":"", + (lmode & LSHADER_CUBE)?"#CUBE":"") + , SUF_NONE, LIGHTPASS_SHADER); + } + if (!shaderstate.shader_rtlight[lmode]->prog) + return false; + return true; +} qboolean D3D11BE_SelectDLight(dlight_t *dl, vec3_t colour, unsigned int lmode) { + if (!D3D11BE_GenerateRTLightShader(lmode)) + { + lmode &= ~(LSHADER_SMAP|LSHADER_CUBE); + if (!D3D11BE_GenerateRTLightShader(lmode)) + return false; + } shaderstate.curdlight = dl; shaderstate.curlmode = lmode; VectorCopy(colour, shaderstate.curdlight_colours); D3D11BE_SetupLightCBuffer(dl, colour); - return true; } +#ifdef RTLIGHTS void D3D11BE_SetupForShadowMap(dlight_t *dl, qboolean isspot, int texwidth, int texheight, float shadowscale) { #define SHADOWMAP_SIZE 512 @@ -2124,6 +2192,7 @@ void D3D11BE_SetupForShadowMap(dlight_t *dl, qboolean isspot, int texwidth, int shaderstate.lightshadowmapscale[0] = 1.0/(SHADOWMAP_SIZE*3); shaderstate.lightshadowmapscale[1] = -1.0/(SHADOWMAP_SIZE*2); } +#endif void D3D11BE_SelectEntity(entity_t *ent) { @@ -2138,6 +2207,7 @@ static qboolean BE_GenTempMeshVBO(vbo_t **vbo, mesh_t *mesh) D3D11_MAP type; int sz; + ID3D11Buffer *buf; //vbo first { @@ -2149,12 +2219,16 @@ static qboolean BE_GenTempMeshVBO(vbo_t **vbo, mesh_t *mesh) shaderstate.purgevertexstream = false; shaderstate.vertexstreamoffset = 0; type = D3D11_MAP_WRITE_DISCARD; + shaderstate.vertexstreamcycle++; + if (shaderstate.vertexstreamcycle == NUMVBUFFERS) + shaderstate.vertexstreamcycle = 0; } else { type = D3D11_MAP_WRITE_NO_OVERWRITE; //yes sir, sorry sir, we promise to not break anything } - if (FAILED(ID3D11DeviceContext_Map(d3ddevctx, (ID3D11Resource*)shaderstate.vertexstream, 0, type, 0, &msr))) + buf = shaderstate.vertexstream[shaderstate.vertexstreamcycle]; + if (FAILED(ID3D11DeviceContext_Map(d3ddevctx, (ID3D11Resource*)buf, 0, type, 0, &msr))) { Con_Printf("BE_RotateForEntity: failed to map vertex stream buffer start\n"); return false; @@ -2163,19 +2237,19 @@ static qboolean BE_GenTempMeshVBO(vbo_t **vbo, mesh_t *mesh) //figure out where our pointer is and mark it as consumed out = (vbovdata_t*)((qbyte*)msr.pData + shaderstate.vertexstreamoffset); //FIXME: do we actually need to bother setting all this junk? - tmpvbo.coord.d3d.buff = shaderstate.vertexstream; + tmpvbo.coord.d3d.buff = buf; tmpvbo.coord.d3d.offs = (quintptr_t)&out[0].coord - (quintptr_t)&out[0] + shaderstate.vertexstreamoffset; - tmpvbo.texcoord.d3d.buff = shaderstate.vertexstream; + tmpvbo.texcoord.d3d.buff = buf; tmpvbo.texcoord.d3d.offs = (quintptr_t)&out[0].tex - (quintptr_t)&out[0] + shaderstate.vertexstreamoffset; - tmpvbo.lmcoord[0].d3d.buff = shaderstate.vertexstream; + tmpvbo.lmcoord[0].d3d.buff = buf; tmpvbo.lmcoord[0].d3d.offs = (quintptr_t)&out[0].lm - (quintptr_t)&out[0] + shaderstate.vertexstreamoffset; - tmpvbo.normals.d3d.buff = shaderstate.vertexstream; + tmpvbo.normals.d3d.buff = buf; tmpvbo.normals.d3d.offs = (quintptr_t)&out[0].ndir - (quintptr_t)&out[0] + shaderstate.vertexstreamoffset; - tmpvbo.svector.d3d.buff = shaderstate.vertexstream; + tmpvbo.svector.d3d.buff = buf; tmpvbo.svector.d3d.offs = (quintptr_t)&out[0].sdir - (quintptr_t)&out[0] + shaderstate.vertexstreamoffset; - tmpvbo.tvector.d3d.buff = shaderstate.vertexstream; + tmpvbo.tvector.d3d.buff = buf; tmpvbo.tvector.d3d.offs = (quintptr_t)&out[0].tdir - (quintptr_t)&out[0] + shaderstate.vertexstreamoffset; - tmpvbo.colours[0].d3d.buff = shaderstate.vertexstream; + tmpvbo.colours[0].d3d.buff = buf; tmpvbo.colours[0].d3d.offs = (quintptr_t)&out[0].colorsb - (quintptr_t)&out[0] + shaderstate.vertexstreamoffset; //consumed shaderstate.vertexstreamoffset += sz; @@ -2263,7 +2337,7 @@ static qboolean BE_GenTempMeshVBO(vbo_t **vbo, mesh_t *mesh) } //and we're done - ID3D11DeviceContext_Unmap(d3ddevctx, (ID3D11Resource*)shaderstate.vertexstream, 0); + ID3D11DeviceContext_Unmap(d3ddevctx, (ID3D11Resource*)buf, 0); } //now ebo @@ -2275,19 +2349,23 @@ static qboolean BE_GenTempMeshVBO(vbo_t **vbo, mesh_t *mesh) shaderstate.purgeindexstream = false; shaderstate.indexstreamoffset = 0; type = D3D11_MAP_WRITE_DISCARD; + shaderstate.indexstreamcycle++; + if (shaderstate.indexstreamcycle == NUMVBUFFERS) + shaderstate.indexstreamcycle = 0; } else { type = D3D11_MAP_WRITE_NO_OVERWRITE; } - if (FAILED(ID3D11DeviceContext_Map(d3ddevctx, (ID3D11Resource*)shaderstate.indexstream, 0, type, 0, &msr))) + buf = shaderstate.indexstream[shaderstate.indexstreamcycle]; + if (FAILED(ID3D11DeviceContext_Map(d3ddevctx, (ID3D11Resource*)buf, 0, type, 0, &msr))) { Con_Printf("BE_RotateForEntity: failed to map vertex stream buffer start\n"); return false; } out = (index_t*)((qbyte*)msr.pData + shaderstate.indexstreamoffset); - tmpvbo.indicies.d3d.buff = shaderstate.indexstream; + tmpvbo.indicies.d3d.buff = buf; tmpvbo.indicies.d3d.offs = shaderstate.indexstreamoffset; //consumed shaderstate.indexstreamoffset += sz; @@ -2295,7 +2373,7 @@ static qboolean BE_GenTempMeshVBO(vbo_t **vbo, mesh_t *mesh) memcpy(out, mesh->indexes, sz); //and we're done - ID3D11DeviceContext_Unmap(d3ddevctx, (ID3D11Resource*)shaderstate.indexstream, 0); + ID3D11DeviceContext_Unmap(d3ddevctx, (ID3D11Resource*)buf, 0); } tmpvbo.indexcount = mesh->numindexes; @@ -2609,10 +2687,12 @@ void D3D11BE_SetupLightCBuffer(dlight_t *l, vec3_t colour) VectorCopy(l->origin, cbl->l_lightposition); cbl->padl1 = 0; VectorCopy(colour, cbl->l_colour); +#ifdef RTLIGHTS VectorCopy(l->lightcolourscales, cbl->l_lightcolourscale); cbl->l_lightcolourscale[0] = l->lightcolourscales[0]; cbl->l_lightcolourscale[1] = l->lightcolourscales[1]; cbl->l_lightcolourscale[2] = l->lightcolourscales[2] * gl_specular.value; +#endif cbl->l_lightradius = l->radius; Vector4Copy(shaderstate.lightshadowmapproj, cbl->l_shadowmapproj); Vector2Copy(shaderstate.lightshadowmapscale, cbl->l_shadowmapscale); @@ -2750,6 +2830,18 @@ static void BE_RotateForEntity (const entity_t *e, const model_t *mod) float sc; if (s == 255) { + if (i == 0) + { + if (shaderstate.curentity->model && shaderstate.curentity->model->engineflags & MDLF_NEEDOVERBRIGHT) + sc = (1<e_lmscale[i][0] = sc; + cbe->e_lmscale[i][1] = sc; + cbe->e_lmscale[i][2] = sc; + cbe->e_lmscale[i][3] = 1; + i++; + } for (; i < MAXRLIGHTMAPS ; i++) { cbe->e_lmscale[i][0] = 0; @@ -3028,7 +3120,7 @@ static void R_DrawPortal(batch_t *batch, batch_t **blist) mesh_t *mesh = batch->mesh[batch->firstmesh]; int sort; - if (r_refdef.recurse) + if (r_refdef.recurse || !r_portalrecursion.ival) return; VectorCopy(mesh->normals_array[0], plane.normal); @@ -3258,6 +3350,9 @@ void D3D11BE_RenderShadowBuffer(unsigned int numverts, void *vbuf, unsigned int int voffsets[] = {0}; int i; + if (!shaderstate.depthonly->prog) + return; + D3D11BE_SetupViewCBuffer(); D3D11BE_Cull(SHADER_CULL_FRONT); diff --git a/engine/d3d/d3d11_image.c b/engine/d3d/d3d11_image.c index ccb21c85..558471ca 100644 --- a/engine/d3d/d3d11_image.c +++ b/engine/d3d/d3d11_image.c @@ -6,6 +6,11 @@ extern ID3D11Device *pD3DDev11; extern ID3D11DeviceContext *d3ddevctx; +extern D3D_FEATURE_LEVEL d3dfeaturelevel; +#define D3D_HAVE_FULL_NPOT() (d3dfeaturelevel>=D3D_FEATURE_LEVEL_10_0) + +void D3D11BE_UnbindAllTextures(void); + //FIXME: need support for cubemaps. typedef struct d3dtexture_s @@ -73,7 +78,7 @@ void D3D11_DestroyTexture (texid_t tex) free(t); } -static d3d11texture_t *d3d_lookup_texture(char *ident) +static d3d11texture_t *d3d_lookup_texture(const char *ident) { d3d11texture_t *tex; @@ -110,6 +115,25 @@ static texid_t ToTexID(d3d11texture_t *tex) return tid; } +//outpitch = width*4 +static void D3D_Resize32(void *out, int outpitch, int outwidth, int outheight, const void *in, int inwidth, int inheight) +{ + int x, y; + int iny; + unsigned int *row; + const unsigned int *inrow; + + for (y = 0; y < outheight; y++) + { + row = (unsigned int*)((char*)out + outpitch*y); + iny = (y * inheight) / outheight; + inrow = (const unsigned int*)in + inwidth*iny; + for (x = 0; x < outwidth; x++) + { + row[x] = inrow[(x * inwidth)/outwidth]; + } + } +} static void D3D_MipMap (qbyte *out, int outwidth, int outheight, const qbyte *in, int inwidth, int inheight) { int i, j; @@ -119,19 +143,19 @@ static void D3D_MipMap (qbyte *out, int outwidth, int outheight, const qbyte *in int rowwidth = inwidth*4; //rowwidth is the byte width of the input inrow = in; - for (i=0 ; i>2; - out[1] = (in[1] + in[5] + in[rowwidth+1] + in[rowwidth+5])>>2; - out[2] = (in[2] + in[6] + in[rowwidth+2] + in[rowwidth+6])>>2; - out[3] = (in[3] + in[7] + in[rowwidth+3] + in[rowwidth+7])>>2; - } - } + for (i=0 ; i>2; + out[1] = (in[1] + in[5] + in[rowwidth+1] + in[rowwidth+5])>>2; + out[2] = (in[2] + in[6] + in[rowwidth+2] + in[rowwidth+6])>>2; + out[3] = (in[3] + in[7] + in[rowwidth+3] + in[rowwidth+7])>>2; + } + } } -static void *D3D11_AllocNewTextureData(void *datargba, int width, int height, unsigned int flags) +static void *D3D11_AllocNewTextureData(void *datargba, int datawidth, int dataheight, int width, int height, unsigned int flags) { HRESULT hr; ID3D11Texture2D *tx = NULL; @@ -139,6 +163,7 @@ static void *D3D11_AllocNewTextureData(void *datargba, int width, int height, un D3D11_SUBRESOURCE_DATA subresdesc[64] = {0}; int i; int owidth, oheight; + qboolean fakenpot = false; tdesc.Width = width; tdesc.Height = height; @@ -164,8 +189,15 @@ static void *D3D11_AllocNewTextureData(void *datargba, int width, int height, un } else { - subresdesc[0].pSysMem = datargba; - for (i = 1; i < 64 && width > 1 && height > 1; i++) + if (datawidth != width || dataheight != height) + { + fakenpot = true; + subresdesc[0].pSysMem = malloc(width*height*4); + D3D_Resize32((void*)subresdesc[0].pSysMem, width*4, width, height, datargba, datawidth, dataheight); + } + else + subresdesc[0].pSysMem = datargba; + for (i = 1; i < 64 && width > 1 && height > 1 && !(flags & IF_NOMIPMAP); i++) { owidth = width; oheight = height; @@ -189,19 +221,19 @@ static void *D3D11_AllocNewTextureData(void *datargba, int width, int height, un tx = NULL; } - for (i = 1; i < tdesc.MipLevels; i++) + for (i = fakenpot?0:1; i < tdesc.MipLevels; i++) { free((void*)subresdesc[i].pSysMem); } return tx; } -texid_t D3D11_AllocNewTexture(char *ident, int width, int height, unsigned int flags) +texid_t D3D11_AllocNewTexture(const char *ident, int width, int height, unsigned int flags) { d3d11texture_t *t = d3d_lookup_texture(""); texid_t id; if (t->tex2d) return ToTexID(t); - t->tex2d = D3D11_AllocNewTextureData(NULL, width, height, flags); + t->tex2d = D3D11_AllocNewTextureData(NULL, 0, 0, width, height, flags); t->com.width = width; t->com.height = height; @@ -215,13 +247,15 @@ texid_t D3D11_AllocNewTexture(char *ident, int width, int height, unsigned int f return id; } -static void D3D11_RoundDimensions(int *scaled_width, int *scaled_height, qboolean mipmap) +static void D3D11_RoundDimensions(int *scaled_width, int *scaled_height, qboolean mipmap, qboolean clamp) { -// if (gl_config.arb_texture_non_power_of_two) //NPOT is a simple extension that relaxes errors. -// { -// TRACE(("dbg: GL_RoundDimensions: GL_ARB_texture_non_power_of_two\n")); -// } -// else + int maxsize; + if (D3D_HAVE_FULL_NPOT() || (!mipmap && clamp)) + { //NPOT is a simple extension that relaxes errors. + //featurelevel 9 supports npot but only if there's no mipmapping or texture coord wrapping. + TRACE(("dbg: D3D11_RoundDimensions: npot\n")); + } + else { int width = *scaled_width; int height = *scaled_height; @@ -243,13 +277,24 @@ static void D3D11_RoundDimensions(int *scaled_width, int *scaled_height, qboolea *scaled_height >>= gl_picmip2d.ival; } - TRACE(("dbg: GL_RoundDimensions: %i\n", gl_max_size.ival)); - if (gl_max_size.ival) + if (d3dfeaturelevel>=D3D_FEATURE_LEVEL_11_0) + maxsize = 16384; + else if (d3dfeaturelevel>=D3D_FEATURE_LEVEL_10_0) + maxsize = 8192; + else if (d3dfeaturelevel>=D3D_FEATURE_LEVEL_9_3) + maxsize = 4096; + else + maxsize = 2048; + if (gl_max_size.ival && maxsize > gl_max_size.ival) + maxsize = gl_max_size.ival; + + TRACE(("dbg: GL_RoundDimensions: %i\n", maxsize)); + if (maxsize) { - if (*scaled_width > gl_max_size.ival) - *scaled_width = gl_max_size.ival; - if (*scaled_height > gl_max_size.ival) - *scaled_height = gl_max_size.ival; + if (*scaled_width > maxsize) + *scaled_width = maxsize; + if (*scaled_height > maxsize) + *scaled_height = maxsize; } if (*scaled_width < 1) @@ -258,7 +303,7 @@ static void D3D11_RoundDimensions(int *scaled_width, int *scaled_height, qboolea *scaled_height = 1; } -static void Upload_Texture_32(ID3D11Texture2D *tex, unsigned int *data, int width, int height, unsigned int flags) +static void Upload_Texture_32(ID3D11Texture2D *tex, unsigned int *data, int datawidth, int dataheight, unsigned int flags) { int x, y; unsigned int *dest; @@ -289,19 +334,19 @@ static void Upload_Texture_32(ID3D11Texture2D *tex, unsigned int *data, int widt return; } - if (width == desc.Width && height == desc.Height) + if (datawidth == desc.Width && dataheight == desc.Height) { - for (y = 0; y < height; y++) + for (y = 0; y < dataheight; y++) { dest = (unsigned int *)((char *)lock.pData + lock.RowPitch*y); - for (x = 0; x < width; x++) + for (x = 0; x < datawidth; x++) { // *(unsigned int*)swapbuf2 = *(unsigned int*)swapbuf = data[x]; // swapbuf[0] = swapbuf2[2]; // swapbuf[2] = swapbuf2[0]; dest[x] = data[x];//*(unsigned int*)swapbuf; } - data += width; + data += datawidth; } } else @@ -313,14 +358,14 @@ static void Upload_Texture_32(ID3D11Texture2D *tex, unsigned int *data, int widt for (y = 0; y < desc.Height; y++) { row = (unsigned int*)((char *)lock.pData + lock.RowPitch*y); - iny = (y * height) / desc.Height; - inrow = data + width*iny; + iny = (y * dataheight) / desc.Height; + inrow = data + datawidth*iny; for (x = 0; x < desc.Width; x++) { //*(unsigned int*)swapbuf2 = *(unsigned int*)swapbuf = inrow[(x * width)/desc.Width]; //swapbuf[0] = swapbuf2[2]; //swapbuf[2] = swapbuf2[0]; - row[x] = inrow[(x * width)/desc.Width];//*(unsigned int*)swapbuf; + row[x] = inrow[(x * datawidth)/desc.Width];//*(unsigned int*)swapbuf; } } } @@ -332,7 +377,7 @@ static void Upload_Texture_32(ID3D11Texture2D *tex, unsigned int *data, int widt //create a basic shader from a 32bit image static void D3D11_LoadTexture_32(d3d11texture_t *tex, unsigned int *data, int width, int height, int flags) { -// int nwidth, nheight; + int nwidth, nheight; /* if (!(flags & TF_MANDATORY)) @@ -342,15 +387,18 @@ static void D3D11_LoadTexture_32(d3d11texture_t *tex, unsigned int *data, int wi } */ -// nwidth = width; -// nheight = height; -// D3D11_RoundDimensions(&nwidth, &nheight, !(flags & IF_NOMIPMAP)); + nwidth = width; + nheight = height; + D3D11_RoundDimensions(&nwidth, &nheight, !(flags & IF_NOMIPMAP), flags & IF_CLAMP); - tex->com.width = width; - tex->com.height = height; - if (!tex->tex2d) + if (!tex->tex2d || tex->com.width != nwidth || tex->com.height != nheight) { - tex->tex2d = D3D11_AllocNewTextureData(data, width, height, flags); + tex->com.width = nwidth; + tex->com.height = nheight; + if (tex->tex2d) + ID3D11Texture2D_Release(tex->tex2d); + + tex->tex2d = D3D11_AllocNewTextureData(data, width, height, nwidth, nheight, flags); return; } else @@ -473,7 +521,7 @@ static void D3D11_LoadTexture_8(d3d11texture_t *tex, unsigned char *data, unsign D3D11_LoadTexture_32(tex, trans, width, height, flags); } -void D3D11_Upload (texid_t id, char *name, enum uploadfmt fmt, void *data, void *palette, int width, int height, unsigned int flags) +void D3D11_Upload (texid_t id, const char *name, enum uploadfmt fmt, void *data, void *palette, int width, int height, unsigned int flags) { d3d11texture_t *tex = (d3d11texture_t *)id.ref; switch (fmt) @@ -521,7 +569,7 @@ void D3D11_UploadLightmap(lightmapinfo_t *lm) tex = (d3d11texture_t*)lm->lightmap_texture.ref; if (!tex->tex2d) - tex->tex2d = D3D11_AllocNewTextureData(lm->lightmaps, lm->width, lm->height, 0); + tex->tex2d = D3D11_AllocNewTextureData(lm->lightmaps, lm->width, lm->height, lm->width, lm->height, 0); else { if (tex->view) @@ -584,7 +632,7 @@ static void genNormalMap(unsigned int *nmap, qbyte *pixels, int w, int h, float } -texid_t D3D11_LoadTexture (char *identifier, int width, int height, enum uploadfmt fmt, void *data, unsigned int flags) +texid_t D3D11_LoadTexture (const char *identifier, int width, int height, enum uploadfmt fmt, void *data, unsigned int flags) { d3d11texture_t *tex; switch (fmt) @@ -665,12 +713,12 @@ texid_t D3D11_LoadTexture (char *identifier, int width, int height, enum uploadf } } -texid_t D3D11_LoadCompressed (char *name) +texid_t D3D11_LoadCompressed (const char *name) { return r_nulltex; } -texid_t D3D11_FindTexture (char *identifier, unsigned int flags) +texid_t D3D11_FindTexture (const char *identifier, unsigned int flags) { d3d11texture_t *tex = d3d_lookup_texture(identifier); if (tex->tex2d) @@ -678,13 +726,13 @@ texid_t D3D11_FindTexture (char *identifier, unsigned int flags) return r_nulltex; } -texid_t D3D11_LoadTexture8Pal32 (char *identifier, int width, int height, qbyte *data, qbyte *palette32, unsigned int flags) +texid_t D3D11_LoadTexture8Pal32 (const char *identifier, int width, int height, qbyte *data, qbyte *palette32, unsigned int flags) { d3d11texture_t *tex = d3d_lookup_texture(identifier); D3D11_LoadTexture_8(tex, data, (unsigned int *)palette32, width, height, flags, TF_SOLID8); return ToTexID(tex); } -texid_t D3D11_LoadTexture8Pal24 (char *identifier, int width, int height, qbyte *data, qbyte *palette24, unsigned int flags) +texid_t D3D11_LoadTexture8Pal24 (const char *identifier, int width, int height, qbyte *data, qbyte *palette24, unsigned int flags) { unsigned int pal32[256]; int i; @@ -714,6 +762,10 @@ texid_t D3D11_GetShadowMap(int id) { d3d11texture_t *tex = &shadowmap_texture[id]; texid_t tid; + if (!tex->tex2d) + { + return r_nulltex; + } tid.ref = &tex->com; if (!tex->view) { @@ -740,7 +792,7 @@ void D3D11_TerminateShadowMap(void) shadowmap_texture[i].tex2d = NULL; } } -void D3D11_BeginShadowMap(int id, int w, int h) +qboolean D3D11_BeginShadowMap(int id, int w, int h) { D3D11_TEXTURE2D_DESC texdesc; HRESULT hr; @@ -765,9 +817,12 @@ void D3D11_BeginShadowMap(int id, int w, int h) texdesc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE; // Create the texture - hr = ID3D11Device_CreateTexture2D(pD3DDev11, &texdesc, NULL, &shadowmap_texture[id].tex2d); - if (FAILED(hr)) - Sys_Error("Failed to create depth texture\n"); + if (!shadowmap_texture[id].tex2d) + { + hr = ID3D11Device_CreateTexture2D(pD3DDev11, &texdesc, NULL, &shadowmap_texture[id].tex2d); + if (FAILED(hr)) + return false; + } if (shadowfmt == 2) @@ -784,8 +839,9 @@ void D3D11_BeginShadowMap(int id, int w, int h) hr = ID3D11Device_CreateDepthStencilView(pD3DDev11, (ID3D11Resource *)shadowmap_texture[id].tex2d, &rtdesc, &shadowmap_dsview[id]); } if (FAILED(hr)) - Sys_Error("ID3D11Device_CreateDepthStencilView failed\n"); + return false; } + D3D11BE_UnbindAllTextures(); if (shadowfmt == 2) { float colours[4] = {0, 1, 0, 0}; @@ -800,6 +856,7 @@ void D3D11_BeginShadowMap(int id, int w, int h) ID3D11DeviceContext_OMSetRenderTargets(d3ddevctx, 0, NULL, shadowmap_dsview[id]); ID3D11DeviceContext_ClearDepthStencilView(d3ddevctx, shadowmap_dsview[id], D3D11_CLEAR_DEPTH, 1.0f, 0); } + return true; } void D3D11_EndShadowMap(void) { diff --git a/engine/d3d/d3d11_shader.c b/engine/d3d/d3d11_shader.c index 2db6cfae..9dba7188 100644 --- a/engine/d3d/d3d11_shader.c +++ b/engine/d3d/d3d11_shader.c @@ -8,41 +8,44 @@ extern ID3D11Device *pD3DDev11; -typedef struct _D3D_SHADER_MACRO -{ - LPCSTR Name; - LPCSTR Definition; +#ifndef IID_ID3DBlob + //microsoft can be such a pain sometimes. + typedef struct _D3D_SHADER_MACRO + { + LPCSTR Name; + LPCSTR Definition; -} D3D_SHADER_MACRO, *LPD3D_SHADER_MACRO; + } D3D_SHADER_MACRO, *LPD3D_SHADER_MACRO; -typedef enum _D3D_INCLUDE_TYPE { - D3D_INCLUDE_LOCAL = 0, - D3D_INCLUDE_SYSTEM = ( D3D_INCLUDE_LOCAL + 1 ), - D3D_INCLUDE_FORCE_DWORD = 0x7fffffff -} D3D_INCLUDE_TYPE; + typedef enum _D3D_INCLUDE_TYPE { + D3D_INCLUDE_LOCAL = 0, + D3D_INCLUDE_SYSTEM = ( D3D_INCLUDE_LOCAL + 1 ), + D3D_INCLUDE_FORCE_DWORD = 0x7fffffff + } D3D_INCLUDE_TYPE; -#undef INTERFACE -#define INTERFACE ID3DInclude -DECLARE_INTERFACE_(INTERFACE, IUnknown) -{ - STDMETHOD(Open)(THIS_ D3D_INCLUDE_TYPE IncludeType, LPCSTR pFileName, LPCVOID pParentData, LPCVOID *ppData, UINT *pBytes) PURE; - STDMETHOD(Close)(THIS_ LPCVOID pData) PURE; -}; + #undef INTERFACE + #define INTERFACE ID3DInclude + DECLARE_INTERFACE_(INTERFACE, IUnknown) + { + STDMETHOD(Open)(THIS_ D3D_INCLUDE_TYPE IncludeType, LPCSTR pFileName, LPCVOID pParentData, LPCVOID *ppData, UINT *pBytes) PURE; + STDMETHOD(Close)(THIS_ LPCVOID pData) PURE; + }; -#undef INTERFACE -#define INTERFACE ID3DBlob -DECLARE_INTERFACE_(INTERFACE, IUnknown) -{ - STDMETHOD(QueryInterface)(THIS_ REFIID iid, LPVOID *ppv) PURE; - STDMETHOD_(ULONG, AddRef)(THIS) PURE; - STDMETHOD_(ULONG, Release)(THIS) PURE; - STDMETHOD_(LPVOID, GetBufferPointer)(THIS) PURE; - STDMETHOD_(SIZE_T, GetBufferSize)(THIS) PURE; -}; + #undef INTERFACE + #define INTERFACE ID3DBlob + DECLARE_INTERFACE_(INTERFACE, IUnknown) + { + STDMETHOD(QueryInterface)(THIS_ REFIID iid, LPVOID *ppv) PURE; + STDMETHOD_(ULONG, AddRef)(THIS) PURE; + STDMETHOD_(ULONG, Release)(THIS) PURE; + STDMETHOD_(LPVOID, GetBufferPointer)(THIS) PURE; + STDMETHOD_(SIZE_T, GetBufferSize)(THIS) PURE; + }; + #undef INTERFACE +#endif #define ID3DBlob_GetBufferPointer(b) b->lpVtbl->GetBufferPointer(b) #define ID3DBlob_Release(b) b->lpVtbl->Release(b) #define ID3DBlob_GetBufferSize(b) b->lpVtbl->GetBufferSize(b) -#undef INTERFACE HRESULT (WINAPI *pD3DCompile) ( LPCVOID pSrcData, @@ -61,34 +64,7 @@ static dllhandle_t *shaderlib; D3D_FEATURE_LEVEL d3dfeaturelevel; -qboolean D3D11Shader_Init(unsigned int flevel) -{ - //FIXME: if the feature level is below 10, make sure the compiler supports all the right targets etc - int ver; - dllfunction_t funcsold[] = - { - {(void**)&pD3DCompile, "D3DCompileFromMemory"}, - {NULL,NULL} - }; - dllfunction_t funcsnew[] = - { - {(void**)&pD3DCompile, "D3DCompile"}, - {NULL,NULL} - }; - for (ver = 43; ver >= 33; ver--) - { - shaderlib = Sys_LoadLibrary(va("D3dcompiler_%i.dll", ver), (ver>=40)?funcsnew:funcsold); - if (shaderlib) - break; - } - - if (!shaderlib) - return false; - - d3dfeaturelevel = flevel; - return true; -} HRESULT STDMETHODCALLTYPE d3dinclude_Close(ID3DInclude *this, LPCVOID pData) { @@ -168,37 +144,202 @@ typedef struct byte_vec4_t colorsb; } vbovdata_t; -void D3D11Shader_DeleteProgram(program_t *prog) +void D3D11Shader_DeleteProg(program_t *prog, unsigned int permu) { ID3D11InputLayout *layout; ID3D11PixelShader *frag; ID3D11VertexShader *vert; - int permu; - for (permu = 0; permu < PERMUTATIONS; permu++) - { - vert = prog->permu[permu].handle.hlsl.vert; - frag = prog->permu[permu].handle.hlsl.frag; - layout = prog->permu[permu].handle.hlsl.layout; - if (vert) - ID3D11VertexShader_Release(vert); - if (frag) - ID3D11PixelShader_Release(frag); - if (layout) - ID3D11InputLayout_Release(layout); - } + vert = prog->permu[permu].handle.hlsl.vert; + frag = prog->permu[permu].handle.hlsl.frag; + layout = prog->permu[permu].handle.hlsl.layout; + if (vert) + ID3D11VertexShader_Release(vert); + if (frag) + ID3D11PixelShader_Release(frag); + if (layout) + ID3D11InputLayout_Release(layout); } -qboolean D3D11Shader_CreateProgram (program_t *prog, const char *name, int permu, char **precompilerconstants, char *vert, char *frag) +//create a program from two blobs. +static qboolean D3D11Shader_CreateShaders(program_t *prog, const char *name, int permu, + void *vblob, size_t vsize, + void *hblob, size_t hsize, + void *dblob, size_t dsize, + void *fblob, size_t fsize) +{ + qboolean success = true; + + if (FAILED(ID3D11Device_CreateVertexShader(pD3DDev11, vblob, vsize, NULL, (ID3D11VertexShader**)&prog->permu[permu].handle.hlsl.vert))) + success = false; + + if (hblob || dblob) + { + prog->permu[permu].handle.hlsl.topology = D3D11_PRIMITIVE_TOPOLOGY_3_CONTROL_POINT_PATCHLIST; + if (FAILED(ID3D11Device_CreateHullShader(pD3DDev11, hblob, hsize, NULL, (ID3D11HullShader**)&prog->permu[permu].handle.hlsl.hull))) + success = false; + + if (FAILED(ID3D11Device_CreateDomainShader(pD3DDev11, dblob, dsize, NULL, (ID3D11DomainShader**)&prog->permu[permu].handle.hlsl.domain))) + success = false; + } + else + prog->permu[permu].handle.hlsl.topology = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST; + + if (FAILED(ID3D11Device_CreatePixelShader(pD3DDev11, fblob, fsize, NULL, (ID3D11PixelShader**)&prog->permu[permu].handle.hlsl.frag))) + success = false; + + if (success) + { + D3D11_INPUT_ELEMENT_DESC decl[13]; + int elements = 0; + vbovdata_t *foo = NULL; + + decl[elements].SemanticName = "POSITION"; + decl[elements].SemanticIndex = 0; + decl[elements].Format = DXGI_FORMAT_R32G32B32_FLOAT; + decl[elements].InputSlot = 0; + decl[elements].AlignedByteOffset = (char*)&foo->coord[0] - (char*)NULL; + decl[elements].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA; + decl[elements].InstanceDataStepRate = 0; + elements++; + + decl[elements].SemanticName = "TEXCOORD"; + decl[elements].SemanticIndex = 0; + decl[elements].Format = DXGI_FORMAT_R32G32B32A32_FLOAT; + decl[elements].InputSlot = 0; + decl[elements].AlignedByteOffset = (char*)&foo->tex[0] - (char*)NULL; + decl[elements].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA; + decl[elements].InstanceDataStepRate = 0; + elements++; + /* + decl[elements].SemanticName = "TEXCOORD"; + decl[elements].SemanticIndex = 1; + decl[elements].Format = DXGI_FORMAT_R32G32_FLOAT; + decl[elements].InputSlot = 1; + decl[elements].AlignedByteOffset = (char*)&foo->lm[0] - (char*)NULL; + decl[elements].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA; + decl[elements].InstanceDataStepRate = 0; + elements++; + */ + decl[elements].SemanticName = "COLOR"; + decl[elements].SemanticIndex = 0; + decl[elements].Format = DXGI_FORMAT_R8G8B8A8_UNORM; + decl[elements].InputSlot = 0; + decl[elements].AlignedByteOffset = (char*)&foo->colorsb[0] - (char*)NULL; + decl[elements].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA; + decl[elements].InstanceDataStepRate = 0; + elements++; + + decl[elements].SemanticName = "NORMAL"; + decl[elements].SemanticIndex = 0; + decl[elements].Format = DXGI_FORMAT_R32G32B32_FLOAT; + decl[elements].InputSlot = 0; + decl[elements].AlignedByteOffset = (char*)&foo->ndir[0] - (char*)NULL; + decl[elements].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA; + decl[elements].InstanceDataStepRate = 0; + elements++; + + decl[elements].SemanticName = "TANGENT"; + decl[elements].SemanticIndex = 0; + decl[elements].Format = DXGI_FORMAT_R32G32B32_FLOAT; + decl[elements].InputSlot = 0; + decl[elements].AlignedByteOffset = (char*)&foo->sdir[0] - (char*)NULL; + decl[elements].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA; + decl[elements].InstanceDataStepRate = 0; + elements++; + + decl[elements].SemanticName = "BINORMAL"; + decl[elements].SemanticIndex = 0; + decl[elements].Format = DXGI_FORMAT_R32G32B32_FLOAT; + decl[elements].InputSlot = 0; + decl[elements].AlignedByteOffset = (char*)&foo->tdir[0] - (char*)NULL; + decl[elements].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA; + decl[elements].InstanceDataStepRate = 0; + elements++; + +/* + decl[elements].SemanticName = "BLENDWEIGHT"; + decl[elements].SemanticIndex = 0; + decl[elements].Format = DXGI_FORMAT_R32G32B32A32_FLOAT; + decl[elements].InputSlot = 0; + decl[elements].AlignedByteOffset = 0; + decl[elements].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA; + decl[elements].InstanceDataStepRate = 0; + elements++; + + decl[elements].SemanticName = "BLENDINDICIES"; + decl[elements].SemanticIndex = 0; + decl[elements].Format = DXGI_FORMAT_R8G8B8A8_UINT; + decl[elements].InputSlot = 0; + decl[elements].AlignedByteOffset = 0; + decl[elements].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA; + decl[elements].InstanceDataStepRate = 0; + elements++; +*/ + if (FAILED(ID3D11Device_CreateInputLayout(pD3DDev11, decl, elements, vblob, vsize, (ID3D11InputLayout**)&prog->permu[permu].handle.hlsl.layout))) + { + Con_Printf("HLSL Shader %s requires unsupported inputs\n", name); + success = false; + } + } + return success; +} + +static qboolean D3D11Shader_LoadBlob(program_t *prog, const char *name, unsigned int permu, vfsfile_t *blobfile) +{ + qboolean success; + char *vblob, *hblob, *dblob, *fblob; + unsigned int vsz, hsz, dsz, fsz; + + VFS_READ(blobfile, &vsz, sizeof(vsz)); + vblob = Z_Malloc(vsz); + VFS_READ(blobfile, vblob, vsz); + + VFS_READ(blobfile, &hsz, sizeof(hsz)); + if (hsz != ~0u) + { + hblob = Z_Malloc(hsz); + VFS_READ(blobfile, hblob, hsz); + } + else + hblob = NULL; + + VFS_READ(blobfile, &dsz, sizeof(dsz)); + if (dsz != ~0u) + { + dblob = Z_Malloc(dsz); + VFS_READ(blobfile, dblob, dsz); + } + else + dblob = NULL; + + VFS_READ(blobfile, &fsz, sizeof(fsz)); + fblob = Z_Malloc(fsz); + VFS_READ(blobfile, fblob, fsz); + + + success = D3D11Shader_CreateShaders(prog, name, permu, vblob, vsz, hblob, hsz, dblob, dsz, fblob, fsz); + Z_Free(vblob); + Z_Free(hblob); + Z_Free(dblob); + Z_Free(fblob); + return success; +} + +qboolean D3D11Shader_CreateProgram (program_t *prog, const char *name, unsigned int permu, const char **precompilerconstants, const char *vert, const char *hull, const char *domain, const char *frag, qboolean silenterrors, vfsfile_t *blobfile) { char *vsformat; + char *hsformat = NULL; + char *dsformat = NULL; char *fsformat; D3D_SHADER_MACRO defines[64]; - ID3DBlob *vcode = NULL, *fcode = NULL, *errors = NULL; + ID3DBlob *vcode = NULL, *hcode = NULL, *dcode = NULL, *fcode = NULL, *errors = NULL; qboolean success = false; if (d3dfeaturelevel >= D3D_FEATURE_LEVEL_11_0) //and 11.1 { vsformat = "vs_5_0"; + hsformat = "hs_5_0"; + dsformat = "ds_5_0"; fsformat = "ps_5_0"; } else if (d3dfeaturelevel >= D3D_FEATURE_LEVEL_10_1) @@ -229,9 +370,9 @@ qboolean D3D11Shader_CreateProgram (program_t *prog, const char *name, int permu if (pD3DCompile) { int consts; - for (consts = 2; precompilerconstants[consts]; consts++) + for (consts = 0; precompilerconstants[consts]; consts++) ; - if (consts >= sizeof(defines) / sizeof(defines[0])) + if (consts+3 >= sizeof(defines) / sizeof(defines[0])) return success; consts = 0; @@ -249,7 +390,7 @@ qboolean D3D11Shader_CreateProgram (program_t *prog, const char *name, int permu for (; *precompilerconstants; precompilerconstants++) { - char *t = *precompilerconstants; + const char *t = *precompilerconstants; t = COM_Parse(t); t = COM_Parse(t); defines[consts].Name = Z_StrDup(com_token); @@ -265,30 +406,56 @@ qboolean D3D11Shader_CreateProgram (program_t *prog, const char *name, int permu defines[0].Name = "VERTEX_SHADER"; if (FAILED(pD3DCompile(vert, strlen(vert), name, defines, &myd3dinclude, "main", vsformat, 0, 0, &vcode, &errors))) success = false; - else - { - if (FAILED(ID3D11Device_CreateVertexShader(pD3DDev11, ID3DBlob_GetBufferPointer(vcode), ID3DBlob_GetBufferSize(vcode), NULL, (ID3D11VertexShader**)&prog->permu[permu].handle.hlsl.vert))) - success = false; - } - if (errors) + if (errors && !silenterrors) { char *messages = ID3DBlob_GetBufferPointer(errors); - Con_Printf("vertex shader:\n%s", messages); + Con_Printf("vertex shader %s:\n%s", name, messages); ID3DBlob_Release(errors); } + if (hull) + { + if (!hsformat) + success = false; + else + { + defines[0].Name = "HULL_SHADER"; + if (FAILED(pD3DCompile(hull, strlen(hull), name, defines, &myd3dinclude, "main", hsformat, 0, 0, &hcode, &errors))) + success = false; + if (errors && !silenterrors) + { + char *messages = ID3DBlob_GetBufferPointer(errors); + Con_Printf("hull shader %s:\n%s", name, messages); + ID3DBlob_Release(errors); + } + } + } + + if (domain) + { + if (!dsformat) + success = false; + else + { + defines[0].Name = "DOMAIN_SHADER"; + if (FAILED(pD3DCompile(domain, strlen(domain), name, defines, &myd3dinclude, "main", dsformat, 0, 0, &dcode, &errors))) + success = false; + if (errors && !silenterrors) + { + char *messages = ID3DBlob_GetBufferPointer(errors); + Con_Printf("domain shader %s:\n%s", name, messages); + ID3DBlob_Release(errors); + } + } + } + defines[0].Name = "FRAGMENT_SHADER"; if (FAILED(pD3DCompile(frag, strlen(frag), name, defines, &myd3dinclude, "main", fsformat, 0, 0, &fcode, &errors))) success = false; - else - { - if (FAILED(ID3D11Device_CreatePixelShader(pD3DDev11, ID3DBlob_GetBufferPointer(fcode), ID3DBlob_GetBufferSize(fcode), NULL, (ID3D11PixelShader**)&prog->permu[permu].handle.hlsl.frag))) - success = false; - } - if (errors) + if (errors && !silenterrors) { char *messages = ID3DBlob_GetBufferPointer(errors); - Con_Printf("fragment shader:\n%s", messages); + Con_Printf("fragment shader %s:\n%s", name, messages); ID3DBlob_Release(errors); } @@ -299,110 +466,112 @@ qboolean D3D11Shader_CreateProgram (program_t *prog, const char *name, int permu } if (success) + success = D3D11Shader_CreateShaders(prog, name, permu, + ID3DBlob_GetBufferPointer(vcode), ID3DBlob_GetBufferSize(vcode), + hcode?ID3DBlob_GetBufferPointer(hcode):NULL, hcode?ID3DBlob_GetBufferSize(hcode):0, + dcode?ID3DBlob_GetBufferPointer(dcode):NULL, dcode?ID3DBlob_GetBufferSize(dcode):0, + ID3DBlob_GetBufferPointer(fcode), ID3DBlob_GetBufferSize(fcode)); + + if (success && blobfile) { - D3D11_INPUT_ELEMENT_DESC decl[13]; - int elements = 0; - vbovdata_t *foo = NULL; + unsigned int sz; + sz = ID3DBlob_GetBufferSize(vcode); + VFS_WRITE(blobfile, &sz, sizeof(sz)); + VFS_WRITE(blobfile, ID3DBlob_GetBufferPointer(vcode), sz); - decl[elements].SemanticName = "POSITION"; - decl[elements].SemanticIndex = 0; - decl[elements].Format = DXGI_FORMAT_R32G32B32_FLOAT; - decl[elements].InputSlot = 0; - decl[elements].AlignedByteOffset = (char*)&foo->coord[0] - (char*)NULL; - decl[elements].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA; - decl[elements].InstanceDataStepRate = 0; - elements++; - - decl[elements].SemanticName = "TEXCOORD"; - decl[elements].SemanticIndex = 0; - decl[elements].Format = DXGI_FORMAT_R32G32B32A32_FLOAT; - decl[elements].InputSlot = 0; - decl[elements].AlignedByteOffset = (char*)&foo->tex[0] - (char*)NULL; - decl[elements].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA; - decl[elements].InstanceDataStepRate = 0; - elements++; - /* - decl[elements].SemanticName = "TEXCOORD"; - decl[elements].SemanticIndex = 1; - decl[elements].Format = DXGI_FORMAT_R32G32_FLOAT; - decl[elements].InputSlot = 1; - decl[elements].AlignedByteOffset = (char*)&foo->lm[0] - (char*)NULL; - decl[elements].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA; - decl[elements].InstanceDataStepRate = 0; - elements++; - */ - decl[elements].SemanticName = "COLOR"; - decl[elements].SemanticIndex = 0; - decl[elements].Format = DXGI_FORMAT_R8G8B8A8_UNORM; - decl[elements].InputSlot = 0; - decl[elements].AlignedByteOffset = (char*)&foo->colorsb[0] - (char*)NULL; - decl[elements].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA; - decl[elements].InstanceDataStepRate = 0; - elements++; - - decl[elements].SemanticName = "NORMAL"; - decl[elements].SemanticIndex = 0; - decl[elements].Format = DXGI_FORMAT_R32G32B32_FLOAT; - decl[elements].InputSlot = 0; - decl[elements].AlignedByteOffset = (char*)&foo->ndir[0] - (char*)NULL; - decl[elements].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA; - decl[elements].InstanceDataStepRate = 0; - elements++; - - decl[elements].SemanticName = "TANGENT"; - decl[elements].SemanticIndex = 0; - decl[elements].Format = DXGI_FORMAT_R32G32B32_FLOAT; - decl[elements].InputSlot = 0; - decl[elements].AlignedByteOffset = (char*)&foo->sdir[0] - (char*)NULL; - decl[elements].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA; - decl[elements].InstanceDataStepRate = 0; - elements++; - - decl[elements].SemanticName = "BINORMAL"; - decl[elements].SemanticIndex = 0; - decl[elements].Format = DXGI_FORMAT_R32G32B32_FLOAT; - decl[elements].InputSlot = 0; - decl[elements].AlignedByteOffset = (char*)&foo->tdir[0] - (char*)NULL; - decl[elements].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA; - decl[elements].InstanceDataStepRate = 0; - elements++; - -/* - decl[elements].SemanticName = "BLENDWEIGHT"; - decl[elements].SemanticIndex = 0; - decl[elements].Format = DXGI_FORMAT_R32G32B32A32_FLOAT; - decl[elements].InputSlot = 0; - decl[elements].AlignedByteOffset = 0; - decl[elements].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA; - decl[elements].InstanceDataStepRate = 0; - elements++; - - decl[elements].SemanticName = "BLENDINDICIES"; - decl[elements].SemanticIndex = 0; - decl[elements].Format = DXGI_FORMAT_R8G8B8A8_UINT; - decl[elements].InputSlot = 0; - decl[elements].AlignedByteOffset = 0; - decl[elements].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA; - decl[elements].InstanceDataStepRate = 0; - elements++; -*/ - if (FAILED(ID3D11Device_CreateInputLayout(pD3DDev11, decl, elements, ID3DBlob_GetBufferPointer(vcode), ID3DBlob_GetBufferSize(vcode), (ID3D11InputLayout**)&prog->permu[permu].handle.hlsl.layout))) + if (!hcode) { - Con_Printf("HLSL Shader %s requires unsupported inputs\n", name); - success = false; + sz = ~0u; + VFS_WRITE(blobfile, &sz, sizeof(sz)); } + else + { + sz = ID3DBlob_GetBufferSize(hcode); + VFS_WRITE(blobfile, &sz, sizeof(sz)); + VFS_WRITE(blobfile, ID3DBlob_GetBufferPointer(hcode), sz); + } + + if (!dcode) + { + sz = ~0u; + VFS_WRITE(blobfile, &sz, sizeof(sz)); + } + else + { + sz = ID3DBlob_GetBufferSize(dcode); + VFS_WRITE(blobfile, &sz, sizeof(sz)); + VFS_WRITE(blobfile, ID3DBlob_GetBufferPointer(dcode), sz); + } + + sz = ID3DBlob_GetBufferSize(fcode); + VFS_WRITE(blobfile, &sz, sizeof(sz)); + VFS_WRITE(blobfile, ID3DBlob_GetBufferPointer(fcode), sz); } if (vcode) ID3DBlob_Release(vcode); + if (hcode) + ID3DBlob_Release(hcode); + if (dcode) + ID3DBlob_Release(dcode); if (fcode) ID3DBlob_Release(fcode); } return success; } -int D3D11Shader_FindUniform(union programhandle_u *h, int type, char *name) + +qboolean D3D11Shader_Init(unsigned int flevel) { - return -1; + //FIXME: if the feature level is below 10, make sure the compiler supports all the right targets etc + int ver; + dllfunction_t funcsold[] = + { + {(void**)&pD3DCompile, "D3DCompileFromMemory"}, + {NULL,NULL} + }; + dllfunction_t funcsnew[] = + { + {(void**)&pD3DCompile, "D3DCompile"}, + {NULL,NULL} + }; + + memset(&sh_config, 0, sizeof(sh_config)); + + for (ver = 47; ver >= 33; ver--) + { + shaderlib = Sys_LoadLibrary(va("D3dcompiler_%i.dll", ver), (ver>=40)?funcsnew:funcsold); + if (shaderlib) + break; + } + + if (!shaderlib) + { + //no shader library available. at least make sure that there's a 2d blob that we can use. + if (!COM_FCheckExists("hlsl11/default2d.blob")) + return false; + } + + sh_config.minver = 11; + sh_config.maxver = 11; + sh_config.blobpath = "hlsl11/%s.blob"; + sh_config.progpath = shaderlib?"hlsl11/%s.hlsl":NULL; + sh_config.shadernamefmt = "%s_hlsl11"; + + sh_config.progs_supported = true; + sh_config.progs_required = true; + + sh_config.pDeleteProg = D3D11Shader_DeleteProg; + sh_config.pLoadBlob = D3D11Shader_LoadBlob; + sh_config.pCreateProgram = D3D11Shader_CreateProgram; + sh_config.pProgAutoFields = NULL; + + sh_config.tex_env_combine = 1; + sh_config.nv_tex_env_combine4 = 1; + sh_config.env_add = 1; + + d3dfeaturelevel = flevel; + return true; } + #endif diff --git a/engine/d3d/d3d_backend.c b/engine/d3d/d3d_backend.c index 2f2b8255..3a076d60 100644 --- a/engine/d3d/d3d_backend.c +++ b/engine/d3d/d3d_backend.c @@ -777,9 +777,9 @@ static void SelectPassTexture(unsigned int tu, shaderpass_t *pass) IDirect3DDevice9_SetTextureStageState(pD3DDev9, tu, D3DTSS_COLORARG2, D3DTA_TEXTURE); IDirect3DDevice9_SetTextureStageState(pD3DDev9, tu, D3DTSS_COLOROP, D3DTOP_BLENDTEXTUREALPHA); -// IDirect3DDevice9_SetTextureStageState(pD3DDev9, tu, D3DTSS_ALPHAARG1, last); - IDirect3DDevice9_SetTextureStageState(pD3DDev9, tu, D3DTSS_ALPHAARG2, D3DTA_TEXTURE); - IDirect3DDevice9_SetTextureStageState(pD3DDev9, tu, D3DTSS_ALPHAOP, D3DTOP_SELECTARG2); + IDirect3DDevice9_SetTextureStageState(pD3DDev9, tu, D3DTSS_ALPHAARG1, last); +// IDirect3DDevice9_SetTextureStageState(pD3DDev9, tu, D3DTSS_ALPHAARG2, D3DTA_TEXTURE); + IDirect3DDevice9_SetTextureStageState(pD3DDev9, tu, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1); break; case PBM_OVERBRIGHT: IDirect3DDevice9_SetTextureStageState(pD3DDev9, tu, D3DTSS_COLORARG1, last); @@ -1094,7 +1094,7 @@ static unsigned int BE_GenerateColourMods(unsigned int vertcount, const shaderpa m = shaderstate.meshlist[0]; - if (1)//pass->flags & SHADER_PASS_NOCOLORARRAY) + if (pass->flags & SHADER_PASS_NOCOLORARRAY) { shaderstate.passsinglecolour = true; shaderstate.passcolour = D3DCOLOR_RGBA(255,255,255,255); @@ -1807,7 +1807,7 @@ static void BE_RenderMeshProgram(shader_t *s, unsigned int vertcount, unsigned i perm |= PERMUTATION_FULLBRIGHT; if (p->permu[perm|PERMUTATION_UPPERLOWER].handle.hlsl.vert && (TEXVALID(shaderstate.curtexnums->upperoverlay) || TEXVALID(shaderstate.curtexnums->loweroverlay))) perm |= PERMUTATION_UPPERLOWER; - if (r_refdef.gfog_rgbd[3] && p->permu[perm|PERMUTATION_FOG].handle.hlsl.vert) + if (r_refdef.globalfog.density && p->permu[perm|PERMUTATION_FOG].handle.hlsl.vert) perm |= PERMUTATION_FOG; if (p->permu[perm|PERMUTATION_FRAMEBLEND].handle.hlsl.vert && shaderstate.batchvbo && shaderstate.batchvbo->coord2.d3d.buff) perm |= PERMUTATION_FRAMEBLEND; @@ -2704,7 +2704,7 @@ void D3D9BE_SubmitBatch(batch_t *batch) shaderstate.batchvbo = batch->vbo; shaderstate.meshlist = batch->mesh + batch->firstmesh; shaderstate.curshader = batch->shader; - shaderstate.curtexnums = batch->skin; + shaderstate.curtexnums = batch->skin?batch->skin:&batch->shader->defaulttextures; shaderstate.curbatch = batch; shaderstate.flags = batch->flags; if (batch->lightmap[0] < 0) diff --git a/engine/d3d/d3d_image.c b/engine/d3d/d3d_image.c index 2c3fe340..12ce62fd 100644 --- a/engine/d3d/d3d_image.c +++ b/engine/d3d/d3d_image.c @@ -33,7 +33,7 @@ void D3D9_Image_Shutdown(void) } } -static d3dtexture_t *d3d_lookup_texture(char *ident) +static d3dtexture_t *d3d_lookup_texture(const char *ident) { d3dtexture_t *tex; @@ -57,7 +57,7 @@ static d3dtexture_t *d3d_lookup_texture(char *ident) extern cvar_t gl_picmip; extern cvar_t gl_picmip2d; -texid_t D3D9_AllocNewTexture(char *ident, int width, int height, unsigned int flags) +texid_t D3D9_AllocNewTexture(const char *ident, int width, int height, unsigned int flags) { IDirect3DTexture9 *tx; @@ -411,7 +411,7 @@ static void genNormalMap(unsigned int *nmap, qbyte *pixels, int w, int h, float } } -void D3D9_Upload (texid_t tex, char *name, enum uploadfmt fmt, void *data, void *palette, int width, int height, unsigned int flags) +void D3D9_Upload (texid_t tex, const char *name, enum uploadfmt fmt, void *data, void *palette, int width, int height, unsigned int flags) { switch (fmt) { @@ -423,13 +423,28 @@ void D3D9_Upload (texid_t tex, char *name, enum uploadfmt fmt, void *data, vo tex.ref->height = height; Upload_Texture_32(tex.ptr, data, width, height, flags); break; + case TF_8PAL24: + { + qbyte *pal24 = palette; + unsigned int pal32[256]; + int i; + for (i = 0; i < 256; i++) + { + pal32[i] = 0x00000000 | + (pal24[i*3+2]<<24) | + (pal24[i*3+1]<<8) | + (pal24[i*3+0]<<0); + } + D3D9_LoadTexture_8((d3dtexture_t*)tex.ref, data, (unsigned int *)pal32, width, height, flags, fmt); + } + break; default: - OutputDebugString(va("D3D9_LoadTextureFmt doesn't support fmt %i (%s)", fmt, name)); + OutputDebugString(va("D3D9_Upload doesn't support fmt %i (%s)", fmt, name)); break; } } -texid_t D3D9_LoadTexture (char *identifier, int width, int height, enum uploadfmt fmt, void *data, unsigned int flags) +texid_t D3D9_LoadTexture (const char *identifier, int width, int height, enum uploadfmt fmt, void *data, unsigned int flags) { d3dtexture_t *tex; switch (fmt) @@ -508,12 +523,12 @@ texid_t D3D9_LoadTexture (char *identifier, int width, int height, enum uploadfm } } -texid_t D3D9_LoadCompressed (char *name) +texid_t D3D9_LoadCompressed (const char *name) { return r_nulltex; } -texid_t D3D9_FindTexture (char *identifier, unsigned int flags) +texid_t D3D9_FindTexture (const char *identifier, unsigned int flags) { d3dtexture_t *tex = d3d_lookup_texture(identifier); if (tex->tex.ptr) @@ -521,13 +536,13 @@ texid_t D3D9_FindTexture (char *identifier, unsigned int flags) return r_nulltex; } -texid_t D3D9_LoadTexture8Pal32 (char *identifier, int width, int height, qbyte *data, qbyte *palette32, unsigned int flags) +texid_t D3D9_LoadTexture8Pal32 (const char *identifier, int width, int height, qbyte *data, qbyte *palette32, unsigned int flags) { d3dtexture_t *tex = d3d_lookup_texture(identifier); D3D9_LoadTexture_8(tex, data, (unsigned int *)palette32, width, height, flags, TF_SOLID8); return tex->tex; } -texid_t D3D9_LoadTexture8Pal24 (char *identifier, int width, int height, qbyte *data, qbyte *palette24, unsigned int flags) +texid_t D3D9_LoadTexture8Pal24 (const char *identifier, int width, int height, qbyte *data, qbyte *palette24, unsigned int flags) { unsigned int pal32[256]; int i; diff --git a/engine/d3d/d3d_shader.c b/engine/d3d/d3d_shader.c index 81d910a3..44351097 100644 --- a/engine/d3d/d3d_shader.c +++ b/engine/d3d/d3d_shader.c @@ -136,26 +136,12 @@ HRESULT (WINAPI *pD3DXCompileShader) ( ); static dllhandle_t *shaderlib; +#ifndef IUnknown_Release +#define IUnknown_Release(This) \ + (This)->lpVtbl -> Release(This) +#endif - -void D3D9Shader_Init(void) -{ - dllfunction_t funcs[] = - { - {(void**)&pD3DXCompileShader, "D3DXCompileShader"}, - {NULL,NULL} - }; - - if (!shaderlib) - shaderlib = Sys_LoadLibrary("d3dx9_32", funcs); - if (!shaderlib) - shaderlib = Sys_LoadLibrary("d3dx9_34", funcs); - - if (!shaderlib) - return; -} - -qboolean D3D9Shader_CreateProgram (program_t *prog, char *sname, int permu, char **precompilerconstants, char *vert, char *frag) +static qboolean D3D9Shader_CreateProgram (program_t *prog, const char *sname, unsigned int permu, const char **precompilerconstants, const char *vert, const char *tcs, const char *tes, const char *frag, qboolean silent, vfsfile_t *blobfile) { D3DXMACRO defines[64]; LPD3DXBUFFER code = NULL, errors = NULL; @@ -239,7 +225,7 @@ static int D3D9Shader_FindUniform_(LPD3DXCONSTANTTABLE ct, char *name) return -1; } -int D3D9Shader_FindUniform(union programhandle_u *h, int type, char *name) +static int D3D9Shader_FindUniform(union programhandle_u *h, int type, char *name) { int offs; @@ -258,5 +244,142 @@ int D3D9Shader_FindUniform(union programhandle_u *h, int type, char *name) return -1; } + +static void D3D9Shader_ProgAutoFields(program_t *prog, char **cvarnames, int *cvartypes) +{ + unsigned int i, p; + qboolean found; + int uniformloc; + char tmpname[128]; + cvar_t *cvar; + + prog->numparams = 0; + + prog->nofixedcompat = true; + + /*set cvar uniforms*/ + for (i = 0; cvarnames[i]; i++) + { + for (p = 0; cvarnames[i][p] && (unsigned char)cvarnames[i][p] > 32 && p < sizeof(tmpname)-1; p++) + tmpname[p] = cvarnames[i][p]; + tmpname[p] = 0; + cvar = Cvar_FindVar(tmpname); + if (!cvar) + continue; + cvar->flags |= CVAR_SHADERSYSTEM; + for (p = 0; p < PERMUTATIONS; p++) + { + if (!prog->permu[p].handle.hlsl.vert || !prog->permu[p].handle.hlsl.frag) + continue; + uniformloc = D3D9Shader_FindUniform(&prog->permu[p].handle, 1, va("cvar_%s", tmpname)); + if (uniformloc != -1) + { + vec4_t v = {cvar->value, 0, 0, 0}; + IDirect3DDevice9_SetVertexShader(pD3DDev9, prog->permu[p].handle.hlsl.vert); + IDirect3DDevice9_SetVertexShaderConstantF(pD3DDev9, 0, v, 1); + } + uniformloc = D3D9Shader_FindUniform(&prog->permu[p].handle, 2, va("cvar_%s", tmpname)); + if (uniformloc != -1) + { + vec4_t v = {cvar->value, 0, 0, 0}; + IDirect3DDevice9_SetPixelShader(pD3DDev9, prog->permu[p].handle.hlsl.frag); + IDirect3DDevice9_SetPixelShaderConstantF(pD3DDev9, 0, v, 1); + } + } + } + for (i = 0; shader_unif_names[i].name; i++) + { + found = false; + for (p = 0; p < PERMUTATIONS; p++) + { + uniformloc = D3D9Shader_FindUniform(&prog->permu[p].handle, 0, shader_unif_names[i].name); + if (uniformloc != -1) + found = true; + prog->permu[p].parm[prog->numparams] = uniformloc; + } + if (found) + { + prog->parm[prog->numparams].type = shader_unif_names[i].ptype; + prog->numparams++; + } + } + /*set texture uniforms*/ + for (p = 0; p < PERMUTATIONS; p++) + { + for (i = 0; i < 8; i++) + { + uniformloc = D3D9Shader_FindUniform(&prog->permu[p].handle, 2, va("s_t%i", i)); + if (uniformloc != -1) + { + int v[4] = {i}; + IDirect3DDevice9_SetPixelShader(pD3DDev9, prog->permu[p].handle.hlsl.frag); + IDirect3DDevice9_SetPixelShaderConstantI(pD3DDev9, 0, v, 1); + } + } + } + IDirect3DDevice9_SetVertexShader(pD3DDev9, NULL); + IDirect3DDevice9_SetPixelShader(pD3DDev9, NULL); +} + +void D3D9Shader_DeleteProg(program_t *prog, unsigned int permu) +{ + if (prog->permu[permu].handle.hlsl.vert) + { + IDirect3DVertexShader9 *vs = prog->permu[permu].handle.hlsl.vert; + prog->permu[permu].handle.hlsl.vert = NULL; + IDirect3DVertexShader9_Release(vs); + } + if (prog->permu[permu].handle.hlsl.frag) + { + IDirect3DPixelShader9 *fs = prog->permu[permu].handle.hlsl.frag; + prog->permu[permu].handle.hlsl.frag = NULL; + IDirect3DPixelShader9_Release(fs); + } + if (prog->permu[permu].handle.hlsl.ctabv) + { + LPD3DXCONSTANTTABLE vct = prog->permu[permu].handle.hlsl.ctabv; + prog->permu[permu].handle.hlsl.ctabv = NULL; + IUnknown_Release(vct); + } + if (prog->permu[permu].handle.hlsl.ctabf) + { + LPD3DXCONSTANTTABLE fct = prog->permu[permu].handle.hlsl.ctabf; + prog->permu[permu].handle.hlsl.ctabf = NULL; + IUnknown_Release(fct); + } +} + +void D3D9Shader_Init(void) +{ + dllfunction_t funcs[] = + { + {(void**)&pD3DXCompileShader, "D3DXCompileShader"}, + {NULL,NULL} + }; + + if (!shaderlib) + shaderlib = Sys_LoadLibrary("d3dx9_32", funcs); + if (!shaderlib) + shaderlib = Sys_LoadLibrary("d3dx9_34", funcs); + + memset(&sh_config, 0, sizeof(sh_config)); + sh_config.minver = 9; + sh_config.maxver = 9; + sh_config.blobpath = "hlsl/%s.blob"; + sh_config.progpath = shaderlib?"hlsl/%s.hlsl":NULL; + sh_config.shadernamefmt = "%s_hlsl9"; + + sh_config.progs_supported = !!shaderlib; + sh_config.progs_required = false; + + sh_config.pDeleteProg = D3D9Shader_DeleteProg; + sh_config.pLoadBlob = NULL;//D3D9Shader_LoadBlob; + sh_config.pCreateProgram = D3D9Shader_CreateProgram; + sh_config.pProgAutoFields = D3D9Shader_ProgAutoFields; + + sh_config.tex_env_combine = 1; + sh_config.nv_tex_env_combine4 = 1; + sh_config.env_add = 1; +} #endif diff --git a/engine/d3d/vid_d3d.c b/engine/d3d/vid_d3d.c index f407c6e6..d4c842a1 100644 --- a/engine/d3d/vid_d3d.c +++ b/engine/d3d/vid_d3d.c @@ -407,6 +407,11 @@ static LRESULT WINAPI D3D9_WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA return lRet; } +static void D3D9_VID_SwapBuffers(void) +{ + IDirect3DDevice9_Present(pD3DDev9, NULL, NULL, NULL, NULL); +} + static void resetD3D9(void) { HRESULT res; @@ -959,7 +964,7 @@ static void (D3D9_SCR_UpdateScreen) (void) { IDirect3DDevice9_BeginScene(pD3DDev9); scr_drawloading = true; - SCR_DrawLoading (); + SCR_DrawLoading (true); scr_drawloading = false; IDirect3DDevice9_EndScene(pD3DDev9); IDirect3DDevice9_Present(pD3DDev9, NULL, NULL, NULL, NULL); @@ -1110,7 +1115,7 @@ static void (D3D9_R_DeInit) (void) -static void D3D9_SetupViewPort(void) +static void D3D9_SetupViewPortProjection(void) { extern cvar_t gl_mindist; float screenaspect; @@ -1176,11 +1181,15 @@ static void D3D9_SetupViewPort(void) static void (D3D9_R_RenderView) (void) { - D3D9_SetupViewPort(); + Surf_SetupFrame(); + + D3D9_SetupViewPortProjection(); + if (r_clear.ival && !(r_refdef.flags & Q2RDF_NOWORLDMODEL)) d3d9error(IDirect3DDevice9_Clear(pD3DDev9, 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(255,0,0), 1, 0)); else d3d9error(IDirect3DDevice9_Clear(pD3DDev9, 0, NULL, D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0,0,0), 1, 0)); + R_SetFrustum (r_refdef.m_projection, r_refdef.m_view); RQ_BeginFrame(); if (!(r_refdef.flags & Q2RDF_NOWORLDMODEL)) @@ -1210,8 +1219,6 @@ void (D3D9_VID_SetWindowCaption) (char *msg); void (D3D9_SCR_UpdateScreen) (void); - - void D3D9BE_RenderToTextureUpdate2d(qboolean destchanged) { } @@ -1248,9 +1255,10 @@ rendererinfo_t d3d9rendererinfo = D3D9_VID_Init, D3D9_VID_DeInit, + D3D9_VID_SwapBuffers, D3D9_VID_ApplyGammaRamps, - D3D9_VID_GetRGBInfo, D3D9_VID_SetWindowCaption, + D3D9_VID_GetRGBInfo, D3D9_SCR_UpdateScreen, diff --git a/engine/d3d/vid_d3d11.c b/engine/d3d/vid_d3d11.c index 7fdc33ee..68ce7bb0 100644 --- a/engine/d3d/vid_d3d11.c +++ b/engine/d3d/vid_d3d11.c @@ -9,6 +9,15 @@ #define COBJMACROS #include +ID3D11Device *pD3DDev11; +ID3D11DeviceContext *d3ddevctx; + +#ifdef WINRT //winrt crap has its own non-hwnd window crap, after years of microsoft forcing everyone to use hwnds for everything. I wonder why they don't have that many winrt apps. +#pragma comment(lib, "dxgi.lib") +#pragma comment(lib, "D3D11.lib") +#include "dxgi1_2.h" +#else + /*Fixup outdated windows headers*/ #ifndef WM_XBUTTONDOWN #define WM_XBUTTONDOWN 0x020B @@ -40,6 +49,7 @@ #ifndef WM_INPUT #define WM_INPUT 255 #endif +#endif #define DEFINE_QGUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ const GUID DECLSPEC_SELECTANY name \ @@ -47,9 +57,12 @@ DEFINE_QGUID(qIID_ID3D11Texture2D,0x6f15aaf2,0xd208,0x4e89,0x9a,0xb4,0x48,0x95,0x35,0xd3,0x4f,0x9c); -ID3D11Device *pD3DDev11; -ID3D11DeviceContext *d3ddevctx; +#ifdef WINRT +IDXGISwapChain1 *d3dswapchain; +#else IDXGISwapChain *d3dswapchain; +#endif +IDXGIOutput *d3dscreen; ID3D11RenderTargetView *fb_backbuffer; ID3D11DepthStencilView *fb_backdepthstencil; @@ -136,6 +149,7 @@ typedef enum {MS_WINDOWED, MS_FULLSCREEN, MS_FULLDIB, MS_UNINIT} modestate_t; static modestate_t modestate; +#ifndef WINRT //winrt crap has its own non-hwnd window crap, after years of microsoft forcing everyone to use hwnds for everything. I wonder why they don't have that many winrt apps. static void D3DVID_UpdateWindowStatus (HWND hWnd) { POINT p; @@ -143,7 +157,7 @@ static void D3DVID_UpdateWindowStatus (HWND hWnd) int window_width, window_height; GetClientRect(hWnd, &nr); - Sys_Printf("Update: %i %i %i %i\n", nr.left, nr.top, nr.right, nr.bottom); +// Sys_Printf("Update: %i %i %i %i\n", nr.left, nr.top, nr.right, nr.bottom); //if its bad then we're probably minimised if (nr.right <= nr.left) @@ -168,7 +182,7 @@ static void D3DVID_UpdateWindowStatus (HWND hWnd) window_center_x = (window_rect.left + window_rect.right) / 2; window_center_y = (window_rect.top + window_rect.bottom) / 2; - Sys_Printf("Window: %i %i %i %i\n", window_x, window_y, window_width, window_height); +// Sys_Printf("Window: %i %i %i %i\n", window_x, window_y, window_width, window_height); INS_UpdateClipCursor (); @@ -208,20 +222,36 @@ static qboolean D3D11AppActivate(BOOL fActive, BOOL minimize) INS_UpdateGrabs(modestate != MS_WINDOWED, ActiveApp); - if (fActive) - { - Cvar_ForceCallback(&v_gamma); - } - if (!fActive) - { - Cvar_ForceCallback(&v_gamma); //wham bam thanks. - } - return true; } +static void D3D11_DoResize(void) +{ + d3d_resized = true; + D3DVID_UpdateWindowStatus(mainwindow); + + if (d3dscreen) + { //seriously? this is disgusting. + DXGI_OUTPUT_DESC desc; + IDXGIOutput_GetDesc(d3dscreen, &desc); + vid.pixelwidth = desc.DesktopCoordinates.right - desc.DesktopCoordinates.left; + vid.pixelheight = desc.DesktopCoordinates.bottom - desc.DesktopCoordinates.top; + } + else + { + vid.pixelwidth = window_rect.right - window_rect.left; + vid.pixelheight = window_rect.bottom - window_rect.top; + } +// Con_Printf("Resizing buffer to %i*%i\n", vid.pixelwidth, vid.pixelheight); + released3dbackbuffer(); + IDXGISwapChain_ResizeBuffers(d3dswapchain, 0, vid.pixelwidth, vid.pixelheight, DXGI_FORMAT_UNKNOWN, DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH); + + D3D11BE_Reset(true); + resetd3dbackbuffer(vid.pixelwidth, vid.pixelheight); + D3D11BE_Reset(false); +} static LRESULT WINAPI D3D11_WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) @@ -254,6 +284,12 @@ static LRESULT WINAPI D3D11_WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPAR case WM_SYSKEYDOWN: if (keydown[K_LALT] && wParam == '\r') { + if (d3dscreen) + { + IDXGIOutput_Release(d3dscreen); + d3dscreen = NULL; + } + if (modestate == MS_FULLSCREEN) modestate = MS_WINDOWED; else @@ -262,15 +298,28 @@ static LRESULT WINAPI D3D11_WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPAR extern cvar_t vid_width, vid_height; int width = vid_width.ival; int height = vid_height.ival; - rect.left = (GetSystemMetrics(SM_CXSCREEN) - width) / 2; - rect.top = (GetSystemMetrics(SM_CYSCREEN) - height) / 2; - rect.right = rect.left+width; - rect.bottom = rect.top+height; + if (!width || !height) + { + DXGI_OUTPUT_DESC desc; + IDXGISwapChain_GetContainingOutput(d3dswapchain, &d3dscreen); + IDXGIOutput_GetDesc(d3dscreen, &desc); + rect = desc.DesktopCoordinates; + } + else + { + rect.left = (GetSystemMetrics(SM_CXSCREEN) - width) / 2; + rect.top = (GetSystemMetrics(SM_CYSCREEN) - height) / 2; + rect.right = rect.left+width; + rect.bottom = rect.top+height; + } AdjustWindowRectEx(&rect, WS_OVERLAPPED, FALSE, 0); SetWindowPos(hWnd, NULL, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, SWP_SHOWWINDOW|SWP_FRAMECHANGED); modestate = MS_FULLSCREEN; } - IDXGISwapChain_SetFullscreenState(d3dswapchain, modestate == MS_FULLSCREEN, NULL); + + if (!d3dscreen && modestate == MS_FULLSCREEN) + IDXGISwapChain_GetContainingOutput(d3dswapchain, &d3dscreen); + IDXGISwapChain_SetFullscreenState(d3dswapchain, modestate == MS_FULLSCREEN, d3dscreen); if (modestate == MS_WINDOWED) { @@ -286,7 +335,12 @@ static LRESULT WINAPI D3D11_WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPAR SetWindowPos(hWnd, HWND_TOP, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, SWP_SHOWWINDOW|SWP_FRAMECHANGED); SetForegroundWindow(hWnd); SetFocus(hWnd); + + //work around a windows bug by forcing all windows to be repainted. + InvalidateRect(NULL, NULL, false); } + D3D11_DoResize(); + Cvar_ForceCallback(&v_gamma); } else if (!vid_initializing) INS_TranslateKeyEvent (wParam, lParam, true, 0); @@ -424,7 +478,11 @@ static LRESULT WINAPI D3D11_WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPAR ShowWindow(mainwindow, SW_SHOWNORMAL); if (ActiveApp && modestate == MS_FULLSCREEN) - IDXGISwapChain_SetFullscreenState(d3dswapchain, modestate == MS_FULLSCREEN, NULL); + { + IDXGISwapChain_SetFullscreenState(d3dswapchain, modestate == MS_FULLSCREEN, d3dscreen); + D3D11_DoResize(); + } + Cvar_ForceCallback(&v_gamma); // fix the leftover Alt from any Alt-Tab or the like that switched us away // ClearAllStates (); @@ -454,6 +512,7 @@ static LRESULT WINAPI D3D11_WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPAR /* return 1 if handled message, 0 if not */ return lRet; } +#endif #if (WINVER < 0x500) && !defined(__GNUC__) typedef struct tagMONITORINFO @@ -520,6 +579,82 @@ static qboolean resetd3dbackbuffer(int width, int height) return true; } +#ifdef WINRT //winrt crap has its own non-hwnd window crap, after years of microsoft forcing everyone to use hwnds for everything. I wonder why they don't have that many winrt apps. +void D3D11_DoResize(int newwidth, int newheight) +{ + d3d_resized = true; + +// Con_Printf("Resizing buffer to %i*%i\n", vid.pixelwidth, vid.pixelheight); + released3dbackbuffer(); + if (d3dswapchain) + { + IDXGISwapChain_ResizeBuffers(d3dswapchain, 0, vid.pixelwidth, vid.pixelheight, DXGI_FORMAT_UNKNOWN, DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH); + + D3D11BE_Reset(true); + resetd3dbackbuffer(vid.pixelwidth, vid.pixelheight); + D3D11BE_Reset(false); + } +} +void *RT_GetCoreWindow(int *width, int *height); +static qboolean D3D11_VID_Init(rendererstate_t *info, unsigned char *palette) +{ + static IID factiid1 = {0x770aae78, 0xf26f, 0x4dba, 0xa8, 0x29, 0x25, 0x3c, 0x83, 0xd1, 0xb3, 0x87}; + static IID factiid2 = {0x50c83a1c, 0xe072, 0x4c48, 0x87, 0xb0, 0x36, 0x30, 0xfa, 0x36, 0xa6, 0xd0}; + IDXGIFactory2 *fact = NULL; + HRESULT hr; + D3D_FEATURE_LEVEL flevel, flevels[] = + { + //D3D_FEATURE_LEVEL_11_1, + D3D_FEATURE_LEVEL_11_0, + D3D_FEATURE_LEVEL_10_1, + D3D_FEATURE_LEVEL_10_0, + + D3D_FEATURE_LEVEL_9_3, + D3D_FEATURE_LEVEL_9_2, + D3D_FEATURE_LEVEL_9_1 + }; + DXGI_SWAP_CHAIN_DESC1 scd = {0}; + + IUnknown *window = RT_GetCoreWindow(&info->width, &info->height); + + modestate = MS_FULLSCREEN; + + //fill scd + scd.Width = info->width; + scd.Height = info->height; + scd.Format = info->srgb?DXGI_FORMAT_R8G8B8A8_UNORM_SRGB:DXGI_FORMAT_B8G8R8A8_UNORM; + scd.Stereo = info->stereo; + scd.SampleDesc.Count = 1+info->multisample; + scd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + scd.BufferCount = 2+info->triplebuffer; //rt only supports fullscreen, so the frontbuffer needs to be created by us. + scd.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; + scd.Flags = 0; + + //create d3d stuff + hr = CreateDXGIFactory1(&factiid2, &fact); + if (FAILED(D3D11CreateDevice(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, 0, flevels, sizeof(flevels)/sizeof(flevels[0]), D3D11_SDK_VERSION, &pD3DDev11, &flevel, &d3ddevctx))) + Sys_Error("D3D11CreateDevice failed\n"); + else + { + if (FAILED(IDXGIFactory2_CreateSwapChainForCoreWindow(fact, (IUnknown*)pD3DDev11, window, &scd, NULL, &d3dswapchain))) + Sys_Error("IDXGIFactory2_CreateSwapChainForCoreWindow failed\n"); + else + { + vid.numpages = scd.BufferCount; + if (!resetd3dbackbuffer(info->width, info->height)) + Sys_Error("unable to reset back buffer\n"); + else + { + if (!D3D11Shader_Init(flevel)) + Con_Printf("Unable to intialise a suitable HLSL compiler, please install the DirectX runtime.\n"); + else + return true; + } + } + } + return false; +} +#else static qboolean initD3D11Device(HWND hWnd, rendererstate_t *info, PFN_D3D11_CREATE_DEVICE_AND_SWAP_CHAIN func, IDXGIAdapter *adapt) { int flags = 0;//= D3D11_CREATE_DEVICE_SINGLETHREADED; @@ -533,9 +668,9 @@ static qboolean initD3D11Device(HWND hWnd, rendererstate_t *info, PFN_D3D11_CREA D3D_FEATURE_LEVEL_10_0, //FIXME: need npot. -// D3D_FEATURE_LEVEL_9_3, -// D3D_FEATURE_LEVEL_9_2, -// D3D_FEATURE_LEVEL_9_1 + D3D_FEATURE_LEVEL_9_3, + D3D_FEATURE_LEVEL_9_2, + D3D_FEATURE_LEVEL_9_1 }; memset(&scd, 0, sizeof(scd)); @@ -563,10 +698,10 @@ static qboolean initD3D11Device(HWND hWnd, rendererstate_t *info, PFN_D3D11_CREA scd.BufferDesc.RefreshRate.Numerator = 0; scd.BufferDesc.RefreshRate.Denominator = 0; scd.BufferCount = 1+info->triplebuffer; //back buffer count - scd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; //32bit colour + scd.BufferDesc.Format = info->srgb?DXGI_FORMAT_R8G8B8A8_UNORM_SRGB:DXGI_FORMAT_R8G8B8A8_UNORM; //32bit colour scd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; scd.OutputWindow = hWnd; - scd.SampleDesc.Count = 1+info->multisample; + scd.SampleDesc.Count = 1+info->multisample; //as we're starting up windowed (and switching to fullscreen after), the frontbuffer is handled by windows. scd.Windowed = TRUE; scd.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;// | DXGI_SWAP_CHAIN_FLAG_NONPREROTATED; @@ -731,7 +866,11 @@ static qboolean D3D11_VID_Init(rendererstate_t *info, unsigned char *palette) } if (info->fullscreen) - IDXGISwapChain_SetFullscreenState(d3dswapchain, true, NULL); + { + if (!d3dscreen) + IDXGISwapChain_GetContainingOutput(d3dswapchain, &d3dscreen); + IDXGISwapChain_SetFullscreenState(d3dswapchain, true, d3dscreen); + } vid.pixelwidth = width; vid.pixelheight = height; @@ -758,10 +897,9 @@ static qboolean D3D11_VID_Init(rendererstate_t *info, unsigned char *palette) mouseactive = false; } - Cvar_ForceCallback(&v_gamma); - return true; } +#endif /*a new model has been loaded*/ static void (D3D11_R_NewMap) (void) @@ -824,17 +962,71 @@ static void (D3D11_VID_DeInit) (void) } d3ddevctx = NULL; +#ifndef WINRT if (mainwindow) { DestroyWindow(mainwindow); mainwindow = NULL; } +#endif + + if (d3dscreen) + IUnknown_Release(d3dscreen); + d3dscreen = NULL; DoDXGIDebug(); } +extern float hw_blend[4]; // rgba 0.0 - 1.0 + +static void D3D11_BuildRamps(int points, DXGI_RGB *out) +{ + int i; + vec3_t cshift; + vec3_t c; + float sc; + VectorScale(hw_blend, hw_blend[3], cshift); + for (i = 0; i < points; i++) + { + sc = i / (float)(points-1); + VectorSet(c, sc, sc, sc); + VectorAdd(c, cshift, c); + VectorScale(c, v_contrast.value, c); + + out[i].Red = pow (c[0], v_gamma.value) + v_brightness.value; + out[i].Green = pow (c[1], v_gamma.value) + v_brightness.value; + out[i].Blue = pow (c[2], v_gamma.value) + v_brightness.value; + } +} + static qboolean D3D11_VID_ApplyGammaRamps(unsigned short *ramps) { + HRESULT hr; + DXGI_GAMMA_CONTROL_CAPABILITIES caps; + DXGI_GAMMA_CONTROL gam; + //cache the screen, so we don't get too confused. + if (!d3dscreen) + return false; //don't do it when we're running windowed. + + if (d3dscreen) + { + gam.Scale.Red = 1; + gam.Scale.Green = 1; + gam.Scale.Blue = 1; + gam.Offset.Red = 0; + gam.Offset.Green = 0; + gam.Offset.Blue = 0; + + if (FAILED(IDXGIOutput_GetGammaControlCapabilities(d3dscreen, &caps))) + return false; + if (caps.NumGammaControlPoints > 1025) + caps.NumGammaControlPoints = 1025; + D3D11_BuildRamps(caps.NumGammaControlPoints, gam.GammaCurve); + + hr = IDXGIOutput_SetGammaControl(d3dscreen, &gam); + if (SUCCEEDED(hr)) + return true; + } return false; } static char *D3D11_VID_GetRGBInfo(int prepad, int *truevidwidth, int *truevidheight) @@ -889,7 +1081,9 @@ static char *D3D11_VID_GetRGBInfo(int prepad, int *truevidwidth, int *truevidhei } static void (D3D11_VID_SetWindowCaption) (char *msg) { +#ifndef WINRT SetWindowText(mainwindow, msg); +#endif } void D3D11_Set2D (void) @@ -924,7 +1118,7 @@ static void (D3D11_SCR_UpdateScreen) (void) if (r_clear.ival) { - float colours[4] = {1, 0, 0, 0}; + float colours[4] = {(r_clear.ival==2)?0:1, 0, 0, 0}; ID3D11DeviceContext_ClearRenderTargetView(d3ddevctx, fb_backbuffer, colours); } @@ -939,6 +1133,7 @@ static void (D3D11_SCR_UpdateScreen) (void) Cvar_ForceCallback(&vid_conautoscale); Cvar_ForceCallback(&vid_conwidth); } + R2D_Font_Changed(); if (scr_disabled_for_loading) { @@ -951,7 +1146,7 @@ static void (D3D11_SCR_UpdateScreen) (void) { // IDirect3DDevice9_BeginScene(pD3DDev9); scr_drawloading = true; - SCR_DrawLoading (); + SCR_DrawLoading (true); scr_drawloading = false; // IDirect3DDevice9_EndScene(pD3DDev9); D3D11_PresentOrCrash(); @@ -1101,6 +1296,7 @@ static void (D3D11_R_Init) (void) } static void (D3D11_R_DeInit) (void) { + GL_GAliasFlushSkinCache(); Surf_DeInit(); Shader_Shutdown(); D3D11_Image_Shutdown(); @@ -1161,6 +1357,10 @@ static void D3D11_SetupViewPort(void) static void (D3D11_R_RenderView) (void) { float x, x2, y, y2; + double time1 = 0, time2 = 0; + + if (r_speeds.ival) + time1 = Sys_DoubleTime(); D3D11_SetupViewPort(); //unlike gl, we clear colour beforehand, because that seems more sane. @@ -1176,6 +1376,10 @@ static void (D3D11_R_RenderView) (void) r_refdef.pxrect.width = (int)ceil(x2) - r_refdef.pxrect.x; r_refdef.pxrect.height = (int)ceil(y2) - r_refdef.pxrect.y; + Surf_SetupFrame(); + + //fixme: waterwarp fov + R_SetFrustum (r_refdef.m_projection, r_refdef.m_view); RQ_BeginFrame(); // if (!(r_refdef.flags & Q2RDF_NOWORLDMODEL)) @@ -1197,6 +1401,12 @@ static void (D3D11_R_RenderView) (void) RQ_RenderBatchClear(); D3D11_Set2D (); + + if (r_speeds.ival) + { + time2 = Sys_DoubleTime(); + RQuantAdd(RQUANT_MSECS, (int)((time2-time1)*1000000)); + } } void D3D11BE_RenderToTextureUpdate2d(qboolean destchanged) @@ -1235,9 +1445,10 @@ rendererinfo_t d3d11rendererinfo = D3D11_VID_Init, D3D11_VID_DeInit, + D3D11_PresentOrCrash, D3D11_VID_ApplyGammaRamps, - D3D11_VID_GetRGBInfo, D3D11_VID_SetWindowCaption, + D3D11_VID_GetRGBInfo, D3D11_SCR_UpdateScreen, diff --git a/engine/dotnet2005/ftequake.sln b/engine/dotnet2005/ftequake.sln index 5f9c042f..27aedeaf 100644 --- a/engine/dotnet2005/ftequake.sln +++ b/engine/dotnet2005/ftequake.sln @@ -3,6 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 9.00 Project("{54435603-DBB4-11D2-8724-00A0C9A8B90C}") = "FTEQuake", "..\setup\setup.vdproj", "{E0EE8B50-3A75-42A9-B80A-787675979B0C}" ProjectSection(ProjectDependencies) = postProject {9767E236-8454-44E9-8999-CD5BDAFBE9BA} = {9767E236-8454-44E9-8999-CD5BDAFBE9BA} + {72269FEE-293D-40BC-A7AE-E429F4496869} = {72269FEE-293D-40BC-A7AE-E429F4496869} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "botlib", "botlib.vcproj", "{0018E098-B12A-4E4D-9B22-6772DA287080}" @@ -143,7 +144,6 @@ Global {2866F783-6B44-4655-A38D-D53874037454}.Debug Dedicated Server|Win32.Build.0 = Debug|Win32 {2866F783-6B44-4655-A38D-D53874037454}.Debug Dedicated Server|x64.ActiveCfg = Debug|Win32 {2866F783-6B44-4655-A38D-D53874037454}.Debug|Win32.ActiveCfg = Debug|Win32 - {2866F783-6B44-4655-A38D-D53874037454}.Debug|Win32.Build.0 = Debug|Win32 {2866F783-6B44-4655-A38D-D53874037454}.Debug|x64.ActiveCfg = Release|Win32 {2866F783-6B44-4655-A38D-D53874037454}.GLDebug|Win32.ActiveCfg = Debug|Win32 {2866F783-6B44-4655-A38D-D53874037454}.GLDebug|Win32.Build.0 = Debug|Win32 @@ -177,7 +177,6 @@ Global {62669E6C-7E18-4E4D-BA54-DFBE29E7D24E}.Debug Dedicated Server|Win32.Build.0 = Debug|Win32 {62669E6C-7E18-4E4D-BA54-DFBE29E7D24E}.Debug Dedicated Server|x64.ActiveCfg = Debug|Win32 {62669E6C-7E18-4E4D-BA54-DFBE29E7D24E}.Debug|Win32.ActiveCfg = Debug|Win32 - {62669E6C-7E18-4E4D-BA54-DFBE29E7D24E}.Debug|Win32.Build.0 = Debug|Win32 {62669E6C-7E18-4E4D-BA54-DFBE29E7D24E}.Debug|x64.ActiveCfg = Release|Win32 {62669E6C-7E18-4E4D-BA54-DFBE29E7D24E}.GLDebug|Win32.ActiveCfg = Debug|Win32 {62669E6C-7E18-4E4D-BA54-DFBE29E7D24E}.GLDebug|x64.ActiveCfg = Debug|Win32 @@ -230,7 +229,6 @@ Global {873CCE24-3549-49D4-A4B4-653F91B1532A}.Debug Dedicated Server|Win32.ActiveCfg = Debug|Win32 {873CCE24-3549-49D4-A4B4-653F91B1532A}.Debug Dedicated Server|x64.ActiveCfg = Debug|Win32 {873CCE24-3549-49D4-A4B4-653F91B1532A}.Debug|Win32.ActiveCfg = Debug|Win32 - {873CCE24-3549-49D4-A4B4-653F91B1532A}.Debug|Win32.Build.0 = Debug|Win32 {873CCE24-3549-49D4-A4B4-653F91B1532A}.Debug|x64.ActiveCfg = Release|Win32 {873CCE24-3549-49D4-A4B4-653F91B1532A}.GLDebug|Win32.ActiveCfg = Debug|Win32 {873CCE24-3549-49D4-A4B4-653F91B1532A}.GLDebug|x64.ActiveCfg = Debug|Win32 @@ -261,7 +259,6 @@ Global {4877586B-E85B-4DF8-BCCE-59D31514D240}.Debug Dedicated Server|Win32.ActiveCfg = Debug|Win32 {4877586B-E85B-4DF8-BCCE-59D31514D240}.Debug Dedicated Server|x64.ActiveCfg = Debug|Win32 {4877586B-E85B-4DF8-BCCE-59D31514D240}.Debug|Win32.ActiveCfg = Debug|Win32 - {4877586B-E85B-4DF8-BCCE-59D31514D240}.Debug|Win32.Build.0 = Debug|Win32 {4877586B-E85B-4DF8-BCCE-59D31514D240}.Debug|x64.ActiveCfg = Release|Win32 {4877586B-E85B-4DF8-BCCE-59D31514D240}.GLDebug|Win32.ActiveCfg = Debug|Win32 {4877586B-E85B-4DF8-BCCE-59D31514D240}.GLDebug|x64.ActiveCfg = Debug|Win32 @@ -293,7 +290,6 @@ Global {32B12987-DF8C-4E40-B07C-B18586A4CA65}.Debug Dedicated Server|Win32.ActiveCfg = Debug|Win32 {32B12987-DF8C-4E40-B07C-B18586A4CA65}.Debug Dedicated Server|x64.ActiveCfg = Debug|Win32 {32B12987-DF8C-4E40-B07C-B18586A4CA65}.Debug|Win32.ActiveCfg = Debug|Win32 - {32B12987-DF8C-4E40-B07C-B18586A4CA65}.Debug|Win32.Build.0 = Debug|Win32 {32B12987-DF8C-4E40-B07C-B18586A4CA65}.Debug|x64.ActiveCfg = Release|Win32 {32B12987-DF8C-4E40-B07C-B18586A4CA65}.GLDebug|Win32.ActiveCfg = Debug|Win32 {32B12987-DF8C-4E40-B07C-B18586A4CA65}.GLDebug|x64.ActiveCfg = Debug|Win32 @@ -323,7 +319,6 @@ Global {4735677B-6D5A-4BE6-A945-CB32DEADBEEF}.Debug Dedicated Server|Win32.ActiveCfg = Debug|Win32 {4735677B-6D5A-4BE6-A945-CB32DEADBEEF}.Debug Dedicated Server|x64.ActiveCfg = Debug|Win32 {4735677B-6D5A-4BE6-A945-CB32DEADBEEF}.Debug|Win32.ActiveCfg = Debug|Win32 - {4735677B-6D5A-4BE6-A945-CB32DEADBEEF}.Debug|Win32.Build.0 = Debug|Win32 {4735677B-6D5A-4BE6-A945-CB32DEADBEEF}.Debug|x64.ActiveCfg = Release|Win32 {4735677B-6D5A-4BE6-A945-CB32DEADBEEF}.GLDebug|Win32.ActiveCfg = Debug|Win32 {4735677B-6D5A-4BE6-A945-CB32DEADBEEF}.GLDebug|x64.ActiveCfg = Debug|Win32 @@ -355,7 +350,6 @@ Global {9767E236-8454-44E9-8999-CD5BDAFBE9BA}.Debug Dedicated Server|Win32.Build.0 = Debug|Win32 {9767E236-8454-44E9-8999-CD5BDAFBE9BA}.Debug Dedicated Server|x64.ActiveCfg = Debug|Win32 {9767E236-8454-44E9-8999-CD5BDAFBE9BA}.Debug|Win32.ActiveCfg = Debug|Win32 - {9767E236-8454-44E9-8999-CD5BDAFBE9BA}.Debug|Win32.Build.0 = Debug|Win32 {9767E236-8454-44E9-8999-CD5BDAFBE9BA}.Debug|x64.ActiveCfg = Debug|Win32 {9767E236-8454-44E9-8999-CD5BDAFBE9BA}.GLDebug|Win32.ActiveCfg = Debug|Win32 {9767E236-8454-44E9-8999-CD5BDAFBE9BA}.GLDebug|Win32.Build.0 = Debug|Win32 @@ -546,7 +540,6 @@ Global {88BFEE0E-7BC0-43AD-9CCC-6B1A6E4C1365}.Debug Dedicated Server|x64.ActiveCfg = GLRelease|x64 {88BFEE0E-7BC0-43AD-9CCC-6B1A6E4C1365}.Debug Dedicated Server|x64.Build.0 = GLRelease|x64 {88BFEE0E-7BC0-43AD-9CCC-6B1A6E4C1365}.Debug|Win32.ActiveCfg = GLDebug|Win32 - {88BFEE0E-7BC0-43AD-9CCC-6B1A6E4C1365}.Debug|Win32.Build.0 = GLDebug|Win32 {88BFEE0E-7BC0-43AD-9CCC-6B1A6E4C1365}.Debug|x64.ActiveCfg = GLDebug|x64 {88BFEE0E-7BC0-43AD-9CCC-6B1A6E4C1365}.Debug|x64.Build.0 = GLDebug|x64 {88BFEE0E-7BC0-43AD-9CCC-6B1A6E4C1365}.GLDebug|Win32.ActiveCfg = GLDebug|Win32 @@ -588,7 +581,6 @@ Global {6ABD62A3-C5A0-43E8-BA4F-84606057774F}.Debug Dedicated Server|Win32.Build.0 = Debug|Win32 {6ABD62A3-C5A0-43E8-BA4F-84606057774F}.Debug Dedicated Server|x64.ActiveCfg = Debug|Win32 {6ABD62A3-C5A0-43E8-BA4F-84606057774F}.Debug|Win32.ActiveCfg = Debug|Win32 - {6ABD62A3-C5A0-43E8-BA4F-84606057774F}.Debug|Win32.Build.0 = Debug|Win32 {6ABD62A3-C5A0-43E8-BA4F-84606057774F}.Debug|x64.ActiveCfg = Debug|Win32 {6ABD62A3-C5A0-43E8-BA4F-84606057774F}.GLDebug|Win32.ActiveCfg = Debug|Win32 {6ABD62A3-C5A0-43E8-BA4F-84606057774F}.GLDebug|Win32.Build.0 = Debug|Win32 diff --git a/engine/dotnet2005/ftequake.vcproj b/engine/dotnet2005/ftequake.vcproj index bf19dc66..b4312236 100644 --- a/engine/dotnet2005/ftequake.vcproj +++ b/engine/dotnet2005/ftequake.vcproj @@ -1479,7 +1479,7 @@ Name="VCCLCompilerTool" Optimization="0" AdditionalIncludeDirectories="../libs/speex;..\client;../libs/freetype2/include;../common;../server;../gl;../sw;../qclib;../libs;../libs/dxsdk9/include;../libs/dxsdk7/include" - PreprocessorDefinitions="_DEBUG;WIN32;_WINDOWS;GLQUAKE;D3DQUAKE;SWQUAKE;MULTITHREAD" + PreprocessorDefinitions="_DEBUG;WIN32;_WINDOWS;GLQUAKE;D3D9QUAKE;D3D11QUAKE;SWQUAKE;MULTITHREAD" BasicRuntimeChecks="3" RuntimeLibrary="1" FloatingPointModel="2" @@ -2462,6 +2462,10 @@ /> + + diff --git a/engine/gl/gl_alias.c b/engine/gl/gl_alias.c index f3f9fdcc..706a8362 100644 --- a/engine/gl/gl_alias.c +++ b/engine/gl/gl_alias.c @@ -60,6 +60,270 @@ extern cvar_t r_globalskin_first, r_globalskin_count; #ifndef SERVERONLY static hashtable_t skincolourmapped; +//q3 .skin support +static skinfile_t **registeredskins; +static skinid_t numregisteredskins; +void Mod_WipeSkin(skinid_t id) +{ + skinfile_t *sk = registeredskins[id-1]; + int i; + if (!sk) + return; + + for (i = 0; i < sk->nummappings; i++) + { + if (sk->mappings[i].needsfree) + R_DestroyTexture(sk->mappings[i].texnums.base); + R_UnloadShader(sk->mappings[i].shader); + } + Z_Free(registeredskins[id]); + registeredskins[id] = NULL; +} +static void Mod_WipeAllSkins(void) +{ + skinid_t id; + for (id = 0; id < numregisteredskins; ) + Mod_WipeSkin(++id); + Z_Free(registeredskins); + registeredskins = NULL; + numregisteredskins = 0; +} +skinfile_t *Mod_LookupSkin(skinid_t id) +{ + id--; + if (id < numregisteredskins) + return registeredskins[id]; + return NULL; +} +static void Mod_ComposeSkin(char *texture) +{ + float x=0, y=0; + float w, h; + float r=1,g=1,b=1,a=1; + int l; + char *s, tname[MAX_QPATH]; + shader_t *sourceimg; + for (s = texture; *s; s++) + { + if (*s == '@' || *s == ':' || *s == '?' || *s == '*') + break; + } + + l = s - texture; + if (l > MAX_QPATH-1) + l = MAX_QPATH-1; + memcpy(tname, texture, l); + tname[l] = 0; + + //load the image and set some default sizes, etc. + sourceimg = R2D_SafeCachePic(tname); + if (!sourceimg) //no shader? no point in doing anything. + return; + w = sourceimg->width; + h = sourceimg->height; + + //create a render target if one is not already selected + if (!r_refdef.rt_destcolour) + { + r_refdef.rt_destcolour = 1; //fixme: this 1 here is a technicality. it will destroy this render target slot. we should find/make a free one instead. + R2D_RT_Configure(r_refdef.rt_destcolour, x+w, y+h, TF_RGBA32); + BE_RenderToTextureUpdate2d(true); + } + + + while(*s) + { + switch(*s) + { + case '@': + x = strtod(s+1, &s); + if (*s == ',') + s++; + y = strtod(s, &s); + break; + case ':': + w = strtod(s+1, &s); + if (*s == ',') + s++; + h = strtod(s, &s); + break; + case '?': + r = strtod(s+1, &s); + if (*s == ',') + s++; + g = strtod(s, &s); + if (*s == ',') + s++; + b = strtod(s, &s); + if (*s == ',') + s++; + a = strtod(s, &s); + break; +// case '*': +// break; + default: + s+=strlen(s); //some sort of error or other thing we don't support + break; + } + } + + + R2D_ImageColours(r,g,b,a); + R2D_Image(x, 512-(y+h), w, h, 0, 1, 1, 0, sourceimg); + R_UnloadShader(sourceimg); //we're done with it now +} +//create a new skin with explicit name and text. even if its already loaded. this means you can free it safely. +skinid_t Mod_ReadSkinFile(const char *skinname, const char *skintext) +{ + skinid_t id; + char *nl; + skinfile_t *skin; + char shadername[MAX_QPATH]; + + for (id = 0; id < numregisteredskins; id++) + { + if (!registeredskins[id]) + break; + } + if (id == numregisteredskins) + { + int newn = numregisteredskins + 64; + registeredskins = BZ_Realloc(registeredskins, sizeof(*registeredskins) * newn); + memset(registeredskins + numregisteredskins, 0, (newn-numregisteredskins)*sizeof(*registeredskins)); + numregisteredskins = newn; + } + + skin = Z_Malloc(sizeof(*skin) - sizeof(skin->mappings) + sizeof(skin->mappings[0])*4); + skin->maxmappings = 4; + Q_strncpyz(skin->skinname, skinname, sizeof(skin->skinname)); + + while(skintext) + { + if (skin->nummappings == skin->maxmappings) + { + skin->maxmappings += 4; + skin = BZ_Realloc(skin, sizeof(*skin) - sizeof(skin->mappings) + sizeof(skin->mappings[0])*skin->maxmappings); + } + + skintext = COM_ParseToken(skintext, ","); + if (!skintext) + break; + if (!strcmp(com_token, "replace")) + { + skintext = COM_ParseToken(skintext, NULL); + + if (com_tokentype != TTP_LINEENDING) + { + Q_strncpyz(skin->mappings[skin->nummappings].surface, com_token, sizeof(skin->mappings[skin->nummappings].surface)); + skintext = COM_ParseToken(skintext, NULL); + Q_strncpyz(shadername, com_token, sizeof(shadername)); + skin->mappings[skin->nummappings].shader = R_RegisterSkin(shadername, skin->skinname); + R_BuildDefaultTexnums(NULL, skin->mappings[skin->nummappings].shader); + skin->mappings[skin->nummappings].texnums = skin->mappings[skin->nummappings].shader->defaulttextures; + skin->nummappings++; + } + } + else if (!strcmp(com_token, "compose")) + { + skintext = COM_ParseToken(skintext, NULL); + //body + if (com_tokentype != TTP_LINEENDING) + { + Q_strncpyz(skin->mappings[skin->nummappings].surface, com_token, sizeof(skin->mappings[skin->nummappings].surface)); + skintext = COM_ParseToken(skintext, NULL); + Q_strncpyz(shadername, com_token, sizeof(shadername)); + skin->mappings[skin->nummappings].shader = R_RegisterSkin(shadername, skin->skinname); + R_BuildDefaultTexnums(NULL, skin->mappings[skin->nummappings].shader); + skin->mappings[skin->nummappings].texnums = skin->mappings[skin->nummappings].shader->defaulttextures; + + for(;;) + { + while(*skintext == ' ' || *skintext == '\t') + skintext++; + if (*skintext == '+') + skintext++; + else + break; + skintext = COM_Parse(skintext); + Mod_ComposeSkin(com_token); + } + + skin->mappings[skin->nummappings].needsfree = 1; + skin->mappings[skin->nummappings].texnums.base = R2D_RT_DetachTexture(r_refdef.rt_destcolour); + skin->nummappings++; + + r_refdef.rt_destcolour = 0; + BE_RenderToTextureUpdate2d(true); + } + } + else if (!strcmp(com_token, "geomset")) + { + int set; + skintext = COM_ParseToken(skintext, NULL); + set = atoi(com_token); + + if (com_tokentype != TTP_LINEENDING) + { + skintext = COM_ParseToken(skintext, NULL); + if (set < MAX_GEOMSETS) + skin->geomset[set] = atoi(com_token); + } + } + else if (!strncmp(com_token, "tag_", 4)) + { + //ignore it. matches q3. + } + else + { + while(*skintext == ' ' || *skintext == '\t') + skintext++; + if (*skintext == ',') + { + Q_strncpyz(skin->mappings[skin->nummappings].surface, com_token, sizeof(skin->mappings[skin->nummappings].surface)); + skintext = COM_ParseToken(skintext+1, NULL); + skin->mappings[skin->nummappings].shader = R_RegisterSkin(com_token, skin->skinname); + R_BuildDefaultTexnums(NULL, skin->mappings[skin->nummappings].shader); + skin->mappings[skin->nummappings].texnums = skin->mappings[skin->nummappings].shader->defaulttextures; + skin->nummappings++; + } + } + + if (com_tokentype == TTP_LINEENDING || !skintext) + continue; + nl = strchr(skintext, '\n'); + if (!nl) + skintext = skintext+strlen(skintext); + else + skintext = nl+1; + if (!*skintext) + break; + } + registeredskins[id] = skin; + return id+1; +} +//registers a skin. loads it if there's not one with that name already registered. +//returns 0 if it failed. +skinid_t Mod_RegisterSkinFile(const char *skinname) +{ + char *f; + skinid_t id; + //block duplicates + for (id = 0; id < numregisteredskins; id++) + { + if (!registeredskins[id]) + continue; + if (!strcmp(skinname, registeredskins[id]->skinname)) + return id+1; + } + f = FS_LoadMallocFile(skinname); + if (!f) + return 0; + id = Mod_ReadSkinFile(skinname, f); + Z_Free(f); + return id; +} + + //changes vertex lighting values #if 0 static void R_GAliasApplyLighting(mesh_t *mesh, vec3_t org, vec3_t angles, float *colormod) @@ -206,6 +470,8 @@ void GL_GAliasFlushSkinCache(void) triangleFacing = NULL; numFacing = 0; #endif + + Mod_WipeAllSkins(); } static shader_t *GL_ChooseSkin(galiasinfo_t *inf, model_t *model, int surfnum, entity_t *e, texnums_t **forcedtex) @@ -219,8 +485,28 @@ static shader_t *GL_ChooseSkin(galiasinfo_t *inf, model_t *model, int surfnum, e unsigned int tc, bc, pc; qboolean forced; + qboolean generateupperlower = false; *forcedtex = NULL; + /*q3 .skin files*/ + if (e->customskin) + { + skinfile_t *sk = Mod_LookupSkin(e->customskin); + if (sk) + { + int i; + if (inf->geomset < MAX_GEOMSETS && sk->geomset[inf->geomset] != inf->geomid) + return NULL; //don't allow this surface to be drawn. + for (i = 0; i < sk->nummappings; i++) + { + if (!strcmp(sk->mappings[i].surface, inf->surfacename)) + { + *forcedtex = &sk->mappings[i].texnums; + return sk->mappings[i].shader; + } + } + } + } /*hexen2 feature: global skins */ if (inf->numskins < e->skinnum && e->skinnum >= r_globalskin_first.ival && e->skinnum < r_globalskin_first.ival+r_globalskin_count.ival) @@ -236,7 +522,7 @@ static shader_t *GL_ChooseSkin(galiasinfo_t *inf, model_t *model, int surfnum, e } - if ((e->model->engineflags & MDLF_NOTREPLACEMENTS) && !ruleset_allow_sensative_texture_replacements.ival) + if ((e->model->engineflags & MDLF_NOTREPLACEMENTS) && !ruleset_allow_sensitive_texture_replacements.ival) forced = true; else forced = false; @@ -312,6 +598,17 @@ static shader_t *GL_ChooseSkin(galiasinfo_t *inf, model_t *model, int surfnum, e shader = skins->ofsshaders[subframe]; } } + if (shader) + { + if (!plskin && (TEXVALID(shader->defaulttextures.loweroverlay) || TEXVALID(shader->defaulttextures.upperoverlay))) + return shader; //the shader can do its own colourmapping. + if (shader->prog && shader->prog->permu[PERMUTATION_UPPERLOWER].handle.glsl && !h2playertranslations) + { //this shader can do permutations. this means we can generate only a black image, with separate top+bottom textures. + tc = 0xff000000; + bc = 0xff000000; + generateupperlower = true; + } + } for (cm = Hash_Get(&skincolourmapped, skinname); cm; cm = Hash_GetNext(&skincolourmapped, skinname, cm)) { @@ -587,6 +884,60 @@ static shader_t *GL_ChooseSkin(galiasinfo_t *inf, model_t *model, int surfnum, e cm->texnum.fullbright = R_LoadTexture(va("fb$%x$%x$%i$%i$%i$%s", tc, bc, cm->skinnum, subframe, pc, cm->name), scaled_width, scaled_height, TF_RGBA32, pixels, IF_NOMIPMAP); }*/ + + if (generateupperlower) + { + for (i=0 ; i<256 ; i++) + translate32[i] = 0xff000000; + for (i = 0; i < 16; i++) + translate32[TOP_RANGE+i] = d_8to24rgbtable[i]; + out = pixels; + fracstep = tinwidth*0x10000/scaled_width; + for (i=0 ; i> 1; + for (j=0 ; j>16]]; + frac += fracstep; + out[j+1] = translate32[inrow[frac>>16]]; + frac += fracstep; + out[j+2] = translate32[inrow[frac>>16]]; + frac += fracstep; + out[j+3] = translate32[inrow[frac>>16]]; + frac += fracstep; + } + } + cm->texnum.upperoverlay = R_LoadTexture(va("up$%i$%i$%i$%s", cm->skinnum, subframe, pc, cm->name), + scaled_width, scaled_height, TF_RGBA32, pixels, IF_NOMIPMAP); + + for (i=0 ; i<256 ; i++) + translate32[i] = 0xff000000; + for (i = 0; i < 16; i++) + translate32[BOTTOM_RANGE+i] = d_8to24rgbtable[i]; + out = pixels; + fracstep = tinwidth*0x10000/scaled_width; + for (i=0 ; i> 1; + for (j=0 ; j>16]]; + frac += fracstep; + out[j+1] = translate32[inrow[frac>>16]]; + frac += fracstep; + out[j+2] = translate32[inrow[frac>>16]]; + frac += fracstep; + out[j+3] = translate32[inrow[frac>>16]]; + frac += fracstep; + } + } + cm->texnum.loweroverlay = R_LoadTexture(va("lo$%i$%i$%i$%s", cm->skinnum, subframe, pc, cm->name), + scaled_width, scaled_height, TF_RGBA32, pixels, IF_NOMIPMAP); + + } } else { @@ -1004,7 +1355,11 @@ void R_GAlias_GenerateBatches(entity_t *e, batch_t **batches) } } - if (!(e->flags & Q2RF_WEAPONMODEL) && !e->framestate.bonestate) + if (!(e->flags & Q2RF_WEAPONMODEL) +#ifdef SKELETALMODELS + && !e->framestate.bonestate +#endif + ) { if (R_CullEntityBox (e, clmodel->mins, clmodel->maxs)) return; @@ -2028,7 +2383,7 @@ void BE_GenModelBatches(batch_t **batches, const dlight_t *dl, unsigned int bemo if (ent->model->engineflags & MDLF_NOTREPLACEMENTS) { if (ent->model->fromgame != fg_quake || ent->model->type != mod_alias) - if (!ruleset_allow_sensative_texture_replacements.value) + if (!ruleset_allow_sensitive_texture_replacements.value) continue; } diff --git a/engine/gl/gl_backend.c b/engine/gl/gl_backend.c index 7c17fafd..1fa76cdd 100644 --- a/engine/gl/gl_backend.c +++ b/engine/gl/gl_backend.c @@ -933,7 +933,7 @@ static void RevertToKnownState(void) GL_DeSelectProgram(); } - shaderstate.shaderbits &= ~(SBITS_MISC_DEPTHEQUALONLY|SBITS_MISC_DEPTHCLOSERONLY|SBITS_MASK_BITS); + shaderstate.shaderbits &= ~(SBITS_MISC_DEPTHEQUALONLY|SBITS_MISC_DEPTHCLOSERONLY|SBITS_MASK_BITS|SBITS_AFFINE); shaderstate.shaderbits |= SBITS_MISC_DEPTHWRITE; shaderstate.shaderbits &= ~(SBITS_BLEND_BITS); @@ -1050,15 +1050,15 @@ static void Shader_BindTextureForPass(int tmu, const shaderpass_t *pass) t = pass->anim_frames[(int)(pass->anim_fps * shaderstate.curtime) % pass->anim_numframes]; break; case T_GEN_LIGHTMAP: - if (shaderstate.curbatch->lightmap[0] < 0) + if ((unsigned short)shaderstate.curbatch->lightmap[0] >= numlightmaps) t = r_whiteimage; else t = lightmap[shaderstate.curbatch->lightmap[0]]->lightmap_texture; break; case T_GEN_DELUXMAP: { - int lmi = shaderstate.curbatch->lightmap[0]; - if (lmi < 0 || !lightmap[lmi]->hasdeluxe) + unsigned int lmi = shaderstate.curbatch->lightmap[0]; + if (lmi >= numlightmaps || !lightmap[lmi]->hasdeluxe) t = missing_texture_normal; else t = lightmap[lmi+1]->lightmap_texture; @@ -1080,10 +1080,16 @@ static void Shader_BindTextureForPass(int tmu, const shaderpass_t *pass) t = missing_texture_gloss; break; case T_GEN_UPPEROVERLAY: - t = shaderstate.curtexnums->upperoverlay; + if (shaderstate.curtexnums && TEXVALID(shaderstate.curtexnums->upperoverlay)) + t = shaderstate.curtexnums->upperoverlay; + else + t = r_nulltex; break; case T_GEN_LOWEROVERLAY: - t = shaderstate.curtexnums->loweroverlay; + if (shaderstate.curtexnums && TEXVALID(shaderstate.curtexnums->loweroverlay)) + t = shaderstate.curtexnums->loweroverlay; + else + t = r_nulltex; break; case T_GEN_FULLBRIGHT: t = shaderstate.curtexnums->fullbright; @@ -2558,6 +2564,14 @@ static void BE_SendPassBlendDepthMask(unsigned int sbits) else qglDisable(GL_PN_TRIANGLES_ATI); } + + if (delta & SBITS_AFFINE) + { + if (sbits & SBITS_AFFINE) + qglHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST); + else + qglHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); + } } static void BE_SubmitMeshChain(void) @@ -3026,7 +3040,7 @@ static void BE_Program_Set_Attributes(const program_t *prog, unsigned int perm, qglUniform3fvARB(ph, 1, shaderstate.lightcolours); break; case SP_W_FOG: - qglUniform4fvARB(ph, 1, r_refdef.gfog_rgbd); + qglUniform4fvARB(ph, 2, r_refdef.globalfog.colour); //and density break; case SP_V_EYEPOS: qglUniform3fvARB(ph, 1, r_origin); @@ -3136,7 +3150,7 @@ static void BE_RenderMeshProgram(const shader_t *shader, const shaderpass_t *pas perm |= PERMUTATION_FULLBRIGHT; if ((TEXVALID(shaderstate.curtexnums->loweroverlay) || TEXVALID(shaderstate.curtexnums->upperoverlay)) && p->permu[perm|PERMUTATION_UPPERLOWER].handle.glsl) perm |= PERMUTATION_UPPERLOWER; - if (r_refdef.gfog_rgbd[3] && p->permu[perm|PERMUTATION_FOG].handle.glsl) + if (r_refdef.globalfog.density && p->permu[perm|PERMUTATION_FOG].handle.glsl) perm |= PERMUTATION_FOG; if (p->permu[perm|PERMUTATION_DELUXE].handle.glsl && TEXVALID(shaderstate.curtexnums->bump) && shaderstate.curbatch->lightmap[0] >= 0 && lightmap[shaderstate.curbatch->lightmap[0]]->hasdeluxe) perm |= PERMUTATION_DELUXE; @@ -3275,7 +3289,7 @@ void GLBE_SelectMode(backendmode_t mode) if (gl_config.nofixedfunc && !shaderstate.allblackshader) { char *defs[] = {NULL}; - shaderstate.allblackshader = GLSlang_CreateProgram("allblackprogram", gl_config.gles?100:110, defs, "#include \"sys/skeletal.h\"\nvoid main(){gl_Position = skeletaltransform();}", "void main(){gl_FragColor=vec4(0.0,0.0,0.0,1.0);}", false); + shaderstate.allblackshader = GLSlang_CreateProgram("allblackprogram", gl_config.gles?100:110, defs, "#include \"sys/skeletal.h\"\nvoid main(){gl_Position = skeletaltransform();}", "void main(){gl_FragColor=vec4(0.0,0.0,0.0,1.0);}", false, NULL); shaderstate.allblack_mvp = qglGetUniformLocationARB(shaderstate.allblackshader, "m_modelviewprojection"); } @@ -3799,7 +3813,7 @@ static void DrawMeshes(void) if (!shaderstate.allblackshader) { char *defs[] = {NULL}; - shaderstate.allblackshader = GLSlang_CreateProgram("allblackprogram", gl_config.gles?100:110, defs, "#include \"sys/skeletal.h\"\nvoid main(){gl_Position = skeletaltransform();}", "void main(){gl_FragColor=vec4(0.0,0.0,0.0,1.0);}", false); + shaderstate.allblackshader = GLSlang_CreateProgram("allblackprogram", gl_config.gles?100:110, defs, "#include \"sys/skeletal.h\"\nvoid main(){gl_Position = skeletaltransform();}", "void main(){gl_FragColor=vec4(0.0,0.0,0.0,1.0);}", false, NULL); shaderstate.allblack_mvp = qglGetUniformLocationARB(shaderstate.allblackshader, "m_modelviewprojection"); } @@ -4245,7 +4259,7 @@ static void GLBE_SubmitMeshesSortList(batch_t *sortlist) r_refdef.pxrect.maxheight = shaderstate.fbo_reflectrefrac.rb_size[1]; GL_ViewportUpdate(); GL_ForceDepthWritable(); - qglClearColor(0, 0, 0, 0); + qglClearColor(0, 0, 0, 1); qglClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); GLR_DrawPortal(batch, cl.worldmodel->batches, NULL, 1); GLBE_FBO_Pop(oldfbo); @@ -4301,7 +4315,7 @@ static void GLBE_SubmitMeshesSortList(batch_t *sortlist) GL_ViewportUpdate(); GL_ForceDepthWritable(); - qglClearColor(0, 0, 0, 0); + qglClearColor(0, 0, 0, 1); qglClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); GLR_DrawPortal(batch, cl.worldmodel->batches, NULL, ((batch->shader->flags & SHADER_HASREFRACTDEPTH)?3:2)); //fixme GLBE_FBO_Pop(oldfbo); @@ -4340,7 +4354,7 @@ static void GLBE_SubmitMeshesSortList(batch_t *sortlist) r_refdef.pxrect.maxheight = shaderstate.fbo_reflectrefrac.rb_size[1]; GL_ViewportUpdate(); - qglClearColor(0, 0, 0, 0); + qglClearColor(0, 0, 0, 1); qglClear(GL_COLOR_BUFFER_BIT); // r_refdef.waterheight = DotProduct(batch->mesh[0]->xyz_array[0], batch->mesh[0]->normals_array[0]); @@ -4823,7 +4837,7 @@ void GLBE_DrawLightPrePass(qbyte *vis) GLBE_FBO_Update(&shaderstate.fbo_lprepass, true, FBO_TEX_COLOUR|FBO_RB_DEPTH, shaderstate.tex_diffuse, r_nulltex, vid.pixelwidth, vid.pixelheight); BE_SelectMode(BEM_STANDARD); - qglClearColor (0,0,0,0); + qglClearColor (0,0,0,1); qglClear(GL_COLOR_BUFFER_BIT); GLBE_SelectEntity(&r_worldentity); diff --git a/engine/gl/gl_draw.c b/engine/gl/gl_draw.c index 847aba80..6007b13f 100644 --- a/engine/gl/gl_draw.c +++ b/engine/gl/gl_draw.c @@ -32,14 +32,14 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. //#define GL_USE8BITTEX -static void GL_Upload32 (char *name, unsigned *data, int width, int height, unsigned int flags); -static void GL_Upload32_BGRA (char *name, unsigned *data, int width, int height, unsigned int flags); -static void GL_Upload24BGR_Flip (char *name, qbyte *framedata, int inwidth, int inheight, unsigned int flags); -static void GL_Upload8 (char *name, qbyte *data, int width, int height, unsigned int flags, unsigned int alpha); +static void GL_Upload32 (const char *name, unsigned *data, int width, int height, unsigned int flags); +static void GL_Upload32_BGRA (const char *name, unsigned *data, int width, int height, unsigned int flags); +static void GL_Upload24BGR_Flip (const char *name, qbyte *framedata, int inwidth, int inheight, unsigned int flags); +static void GL_Upload8 (const char *name, qbyte *data, int width, int height, unsigned int flags, unsigned int alpha); static void GL_Upload8Pal32 (qbyte *data, qbyte *pal, int width, int height, unsigned int flags); -static void GL_Upload32_Int (char *name, unsigned *data, int width, int height, unsigned int flags, GLenum glcolormode); +static void GL_Upload32_Int (const char *name, unsigned *data, int width, int height, unsigned int flags, GLenum glcolormode); -void GL_UploadFmt(texid_t tex, char *name, enum uploadfmt fmt, void *data, void *palette, int width, int height, unsigned int flags) +void GL_UploadFmt(texid_t tex, const char *name, enum uploadfmt fmt, void *data, void *palette, int width, int height, unsigned int flags) { GL_MTBind(0, GL_TEXTURE_2D, tex); switch(fmt) @@ -105,7 +105,7 @@ void GL_UploadFmt(texid_t tex, char *name, enum uploadfmt fmt, void *data, void } } -texid_t GL_LoadTextureFmt (char *name, int width, int height, enum uploadfmt fmt, void *data, unsigned int flags) +texid_t GL_LoadTextureFmt (const char *name, int width, int height, enum uploadfmt fmt, void *data, unsigned int flags) { extern cvar_t r_shadow_bumpscale_basetexture, r_shadow_bumpscale_bumpmap; switch(fmt) @@ -194,7 +194,7 @@ typedef struct gltexture_s static gltexture_t *gltextures; -static gltexture_t *GL_AllocNewGLTexture(char *ident, int w, int h, unsigned int flags) +static gltexture_t *GL_AllocNewGLTexture(const char *ident, int w, int h, unsigned int flags) { gltexture_t *glt; glt = BZ_Malloc(sizeof(*glt) + sizeof(bucket_t)); @@ -216,7 +216,7 @@ static gltexture_t *GL_AllocNewGLTexture(char *ident, int w, int h, unsigned int return glt; } -texid_t GL_AllocNewTexture(char *name, int w, int h, unsigned int flags) +texid_t GL_AllocNewTexture(const char *name, int w, int h, unsigned int flags) { gltexture_t *glt = GL_AllocNewGLTexture(name, w, h, flags); return glt->texnum; @@ -501,8 +501,10 @@ TRACE(("dbg: GLDraw_ReInit: Allocating upload buffers\n")); GL_BeginRendering (); TRACE(("dbg: GLDraw_ReInit: SCR_DrawLoading\n")); + qglDisable(GL_SCISSOR_TEST); GL_Set2D(false); + qglClearColor(0, 0, 0, 1); qglClear(GL_COLOR_BUFFER_BIT); { mpic_t *pic = R2D_SafeCachePic ("gfx/loading.lmp"); @@ -527,7 +529,7 @@ TRACE(("dbg: GLDraw_ReInit: Allocating upload buffers\n")); inited15to8 = false; #endif - qglClearColor (1,0,0,0); + qglClearColor (1,0,0,1); TRACE(("dbg: GLDraw_ReInit: PPL_LoadSpecularFragmentProgram\n")); GL_InitSceneProcessingShaders(); @@ -657,7 +659,7 @@ void GL_Set2D (qboolean flipped) GL_FindTexture ================ */ -texid_t GL_FindTexture (char *identifier, unsigned int flags) +texid_t GL_FindTexture (const char *identifier, unsigned int flags) { gltexture_t *glt; @@ -675,7 +677,7 @@ texid_t GL_FindTexture (char *identifier, unsigned int flags) return r_nulltex; } -gltexture_t *GL_MatchTexture (char *identifier, unsigned int flags, int bits, int width, int height) +gltexture_t *GL_MatchTexture (const char *identifier, unsigned int flags, int bits, int width, int height) { gltexture_t *glt; @@ -1172,7 +1174,7 @@ void GL_8888to4444(int targ, unsigned char *in, unsigned short *out, unsigned in GL_Upload32 =============== */ -static void GL_Upload32_Int (char *name, unsigned *data, int width, int height, unsigned int flags, GLenum glcolormode) +static void GL_Upload32_Int (const char *name, unsigned *data, int width, int height, unsigned int flags, GLenum glcolormode) { int miplevel=0; int samples; @@ -1443,16 +1445,16 @@ done: } } -void GL_Upload32 (char *name, unsigned *data, int width, int height, unsigned int flags) +void GL_Upload32 (const char *name, unsigned *data, int width, int height, unsigned int flags) { GL_Upload32_Int(name, data, width, height, flags, GL_RGBA); } -void GL_Upload32_BGRA (char *name, unsigned *data, int width, int height, unsigned int flags) +void GL_Upload32_BGRA (const char *name, unsigned *data, int width, int height, unsigned int flags) { GL_Upload32_Int(name, data, width, height, flags, GL_BGRA_EXT); } -void GL_Upload24BGR (char *name, qbyte *framedata, int inwidth, int inheight, unsigned int flags) +void GL_Upload24BGR (const char *name, qbyte *framedata, int inwidth, int inheight, unsigned int flags) { int outwidth, outheight; int y, x; @@ -1522,7 +1524,7 @@ void GL_Upload24BGR (char *name, qbyte *framedata, int inwidth, int inheight, un GL_Upload32 (name, (unsigned int*)uploadmemorybufferintermediate, outwidth, outheight, flags); } -void GL_Upload24BGR_Flip (char *name, qbyte *framedata, int inwidth, int inheight, unsigned int flags) +void GL_Upload24BGR_Flip (const char *name, qbyte *framedata, int inwidth, int inheight, unsigned int flags) { int outwidth, outheight; int y, x; @@ -1978,7 +1980,7 @@ unsigned ColorPercent[16] = 25, 51, 76, 102, 114, 127, 140, 153, 165, 178, 191, 204, 216, 229, 237, 247 }; -void GL_Upload8 (char *name, qbyte *data, int width, int height, unsigned int flags, unsigned int alpha) +void GL_Upload8 (const char *name, qbyte *data, int width, int height, unsigned int flags, unsigned int alpha) { unsigned *trans; int i, s; @@ -2229,7 +2231,7 @@ static void GL_Upload8Pal32 (qbyte *data, qbyte *pal, int width, int height, uns GL_LoadTexture ================ */ -texid_t GL_LoadTexture (char *identifier, int width, int height, qbyte *data, unsigned int flags, unsigned int transtype) +texid_t GL_LoadTexture (const char *identifier, int width, int height, qbyte *data, unsigned int flags, unsigned int transtype) { gltexture_t *glt = NULL; @@ -2254,7 +2256,7 @@ TRACE(("dbg: GL_LoadTexture: new %s\n", identifier)); return glt->texnum; } -texid_t GL_LoadTextureFB (char *identifier, int width, int height, qbyte *data, unsigned int flags) +texid_t GL_LoadTextureFB (const char *identifier, int width, int height, qbyte *data, unsigned int flags) { int i; gltexture_t *glt; @@ -2285,7 +2287,7 @@ texid_t GL_LoadTextureFB (char *identifier, int width, int height, qbyte *data, return glt->texnum; } -texid_t GL_LoadTexture8Pal24 (char *identifier, int width, int height, qbyte *data, qbyte *palette24, unsigned int flags) +texid_t GL_LoadTexture8Pal24 (const char *identifier, int width, int height, qbyte *data, qbyte *palette24, unsigned int flags) { gltexture_t *glt; @@ -2307,7 +2309,7 @@ texid_t GL_LoadTexture8Pal24 (char *identifier, int width, int height, qbyte *da return glt->texnum; } -texid_t GL_LoadTexture8Pal32 (char *identifier, int width, int height, qbyte *data, qbyte *palette32, unsigned int flags) +texid_t GL_LoadTexture8Pal32 (const char *identifier, int width, int height, qbyte *data, qbyte *palette32, unsigned int flags) { gltexture_t *glt; @@ -2330,7 +2332,7 @@ texid_t GL_LoadTexture8Pal32 (char *identifier, int width, int height, qbyte *da return glt->texnum; } -texid_t GL_LoadTexture32 (char *identifier, int width, int height, void *data, unsigned int flags) +texid_t GL_LoadTexture32 (const char *identifier, int width, int height, void *data, unsigned int flags) { // qboolean noalpha; // int p, s; @@ -2391,7 +2393,7 @@ texid_t GL_LoadTexture32_BGRA (char *identifier, int width, int height, unsigned return glt->texnum; } -texid_t GL_LoadCompressed(char *name) +texid_t GL_LoadCompressed(const char *name) { unsigned char *file; gltexture_t *glt; @@ -2456,7 +2458,7 @@ texid_t GL_LoadTexture8Grey (char *identifier, int width, int height, unsigned c return glt->texnum; } -texid_t GL_LoadTexture8Bump (char *identifier, int width, int height, unsigned char *data, unsigned int flags, float bumpscale) +texid_t GL_LoadTexture8Bump (const char *identifier, int width, int height, unsigned char *data, unsigned int flags, float bumpscale) { // qboolean noalpha; // int p, s; diff --git a/engine/gl/gl_draw.h b/engine/gl/gl_draw.h index daaae69b..1b7a9bf6 100644 --- a/engine/gl/gl_draw.h +++ b/engine/gl/gl_draw.h @@ -26,8 +26,8 @@ void GLDraw_DeInit (void); void Surf_DeInit (void); void R2D_Init(void); -mpic_t *R2D_SafeCachePic (char *path); -mpic_t *R2D_SafePicFromWad (char *name); +mpic_t *R2D_SafeCachePic (const char *path); +mpic_t *R2D_SafePicFromWad (const char *name); void R2D_ImageColours(float r, float g, float b, float a); void R2D_Image(float x, float y, float w, float h, float s1, float t1, float s2, float t2, mpic_t *pic); void R2D_ScalePic (float x, float y, float width, float height, mpic_t *pic); diff --git a/engine/gl/gl_font.c b/engine/gl/gl_font.c index 856f94ee..80eb327d 100644 --- a/engine/gl/gl_font.c +++ b/engine/gl/gl_font.c @@ -10,7 +10,7 @@ void Font_Init(void); void Font_Shutdown(void); -struct font_s *Font_LoadFont(int height, char *fontfilename); +struct font_s *Font_LoadFont(int height, const char *fontfilename); void Font_Free(struct font_s *f); void Font_BeginString(struct font_s *font, float vx, float vy, int *px, int *py); void Font_BeginScaledString(struct font_s *font, float vx, float vy, float szx, float szy, float *px, float *py); /*avoid using*/ @@ -700,7 +700,7 @@ static struct charcache_s *Font_GetChar(font_t *f, CHARIDXTYPE charidx) return c; } -qboolean Font_LoadFreeTypeFont(struct font_s *f, int height, char *fontfilename) +qboolean Font_LoadFreeTypeFont(struct font_s *f, int height, const char *fontfilename) { #ifdef AVAIL_FREETYPE ftfontface_t *qface; @@ -788,7 +788,7 @@ qboolean Font_LoadFreeTypeFont(struct font_s *f, int height, char *fontfilename) } } -#ifdef _WIN32 +#if defined(_WIN32) if (error) { static qboolean firsttime = true; @@ -796,19 +796,21 @@ qboolean Font_LoadFreeTypeFont(struct font_s *f, int height, char *fontfilename) if (firsttime) { - HMODULE shfolder = LoadLibrary("shfolder.dll"); + HRESULT (WINAPI *dSHGetFolderPath) (HWND hwndOwner, int nFolder, HANDLE hToken, DWORD dwFlags, LPTSTR pszPath); + dllfunction_t shfolderfuncs[] = + { + {(void**)&dSHGetFolderPath, "SHGetFolderPathA"}, + {NULL, NULL} + }; + dllhandle_t *shfolder = Sys_LoadLibrary("shfolder.dll", shfolderfuncs); + firsttime = false; if (shfolder) { - HRESULT (WINAPI *dSHGetFolderPath) (HWND hwndOwner, int nFolder, HANDLE hToken, DWORD dwFlags, LPTSTR pszPath); - dSHGetFolderPath = (void *)GetProcAddress(shfolder, "SHGetFolderPathA"); - if (dSHGetFolderPath) - { - // 0x14 == CSIDL_FONTS - if (dSHGetFolderPath(NULL, 0x14, NULL, 0, fontdir) != S_OK) - *fontdir = 0; - } - FreeLibrary(shfolder); + // 0x14 == CSIDL_FONTS + if (dSHGetFolderPath(NULL, 0x14, NULL, 0, fontdir) != S_OK) + *fontdir = 0; + Sys_CloseLibrary(shfolder); } } if (*fontdir) @@ -1063,7 +1065,7 @@ void Doom_ExpandPatch(doompatch_t *p, unsigned char *b, int stride) //creates a new font object from the given file, with each text row with the given height. //width is implicit and scales with height and choice of font. -struct font_s *Font_LoadFont(int vheight, char *fontfilename) +struct font_s *Font_LoadFont(int vheight, const char *fontfilename) { struct font_s *f; int i = 0; @@ -1271,7 +1273,7 @@ struct font_s *Font_LoadFont(int vheight, char *fontfilename) } { - char *start; + const char *start; start = fontfilename; for(;;) { @@ -1647,7 +1649,7 @@ int Font_DrawChar(int px, int py, unsigned int charcode) int v; struct font_s *font = curfont; #ifdef D3D11QUAKE - float dxbias = (qrenderer == QR_DIRECT3D11)?0.5:0; + float dxbias = 0;//(qrenderer == QR_DIRECT3D11)?0.5:0; #else #define dxbias 0 #endif @@ -1827,7 +1829,7 @@ float Font_DrawScaleChar(float px, float py, unsigned int charcode) struct font_s *font = curfont; float cw, ch; #ifdef D3D11QUAKE - float dxbias = (qrenderer == QR_DIRECT3D11)?0.5:0; + float dxbias = 0;//(qrenderer == QR_DIRECT3D11)?0.5:0; #else #define dxbias 0 #endif diff --git a/engine/gl/gl_heightmap.c b/engine/gl/gl_heightmap.c index c1855632..c31650f8 100644 --- a/engine/gl/gl_heightmap.c +++ b/engine/gl/gl_heightmap.c @@ -282,6 +282,8 @@ static void Terr_LoadSectionTextures(hmsection_t *s) { #ifndef SERVERONLY extern texid_t missing_texture; + if (isDedicated) + return; //CL_CheckOrEnqueDownloadFile(s->texname[0], NULL, 0); //CL_CheckOrEnqueDownloadFile(s->texname[1], NULL, 0); //CL_CheckOrEnqueDownloadFile(s->texname[2], NULL, 0); @@ -429,6 +431,7 @@ static char *Terr_DiskSectionName(heightmap_t *hm, int sx, int sy) sy &= CHUNKLIMIT-1; return va("maps/%s/sect_%03x_%03x.hms", hm->path, sx, sy); } +#ifndef SERVERONLY static char *Terr_TempDiskSectionName(heightmap_t *hm, int sx, int sy) { sx -= CHUNKBIAS; @@ -438,6 +441,7 @@ static char *Terr_TempDiskSectionName(heightmap_t *hm, int sx, int sy) sy &= CHUNKLIMIT-1; return va("temp/%s/sect_%03x_%03x.hms", hm->path, sx, sy); } +#endif static int dehex_e(int i, qboolean *error) { @@ -552,7 +556,8 @@ static void *Terr_GenerateWater(hmsection_t *s, float maxheight) w->next = s->water; s->water = w; #ifndef SERVERONLY - w->shader = R_RegisterCustom (s->hmmod->watershadername, SUF_NONE, Shader_DefaultWaterShader, NULL); + if (!isDedicated) + w->shader = R_RegisterCustom (s->hmmod->watershadername, SUF_NONE, Shader_DefaultWaterShader, NULL); #endif w->simple = true; w->contentmask = FTECONTENTS_WATER; @@ -567,8 +572,10 @@ static void *Terr_GenerateWater(hmsection_t *s, float maxheight) static void *Terr_ReadV1(heightmap_t *hm, hmsection_t *s, void *ptr, int len) { +#ifndef SERVERONLY dsmesh_t *dm; float *colours; +#endif dsection_v1_t *ds = ptr; int i; @@ -664,7 +671,7 @@ static void *Terr_ReadV1(heightmap_t *hm, hmsection_t *s, void *ptr, int len) s->numents = s->maxents = 0; for (i = 0, dm = (dsmesh_t*)ptr; i < s->numents; i++, dm = (dsmesh_t*)((qbyte*)dm + dm->size)) { - s->ents[i].model = Mod_ForName((char*)(dm + 1), false); + s->ents[i].model = Mod_ForName((char*)(dm + 1), MLV_WARN); if (!s->ents[i].model || s->ents[i].model->type == mod_dummy) { s->numents--; @@ -967,13 +974,17 @@ static void Terr_SaveV2(heightmap_t *hm, hmsection_t *s, vfsfile_t *f, int sx, i #endif static void *Terr_ReadV2(heightmap_t *hm, hmsection_t *s, void *ptr, int len) { +#ifndef SERVERONLY char modelname[MAX_QPATH]; + qbyte last; + int y; + qboolean present; + qbyte *lm, delta; +#endif struct terrstream_s strm = {ptr, len, 0}; float f; - int i, j, x, y; - qbyte *lm, delta, last; + int i, j, x; unsigned int flags = Terr_Read_SInt(&strm); - qboolean present; s->flags |= flags & ~TSF_INTERNAL; if (flags & TSF_HASHEIGHTS) @@ -1161,11 +1172,12 @@ static void *Terr_ReadV2(heightmap_t *hm, hmsection_t *s, void *ptr, int len) } //#include "gl_adt.inc" -//#include "gl_m2.inc" static void Terr_GenerateDefault(heightmap_t *hm, hmsection_t *s) { +#ifndef SERVERONLY int i; +#endif s->flags |= TSF_FAILEDLOAD; memset(s->holes, 0, sizeof(s->holes)); @@ -1840,6 +1852,8 @@ void Terr_DestroySection(heightmap_t *hm, hmsection_t *s, qboolean lightmapreusa hm->activesections--; } +#ifndef SERVERONLY +//dedicated servers do not support editing. no lightmap info causes problems. static void Terr_DoEditNotify(heightmap_t *hm) { int i; @@ -1852,7 +1866,7 @@ static void Terr_DoEditNotify(heightmap_t *hm) for (i = 0; i < sv.allocated_client_slots; i++) { - if (svs.clients[i].state > cs_zombie && svs.clients[i].netchan.remote_address.type != NA_LOOPBACK) + if (svs.clients[i].state >= cs_connected && svs.clients[i].netchan.remote_address.type != NA_LOOPBACK) { if (svs.clients[i].backbuf.cursize) return; @@ -1868,7 +1882,7 @@ static void Terr_DoEditNotify(heightmap_t *hm) cmd = va("mod_terrain_reload %s %i %i\n", hm->path, s->sx - CHUNKBIAS, s->sy - CHUNKBIAS); for (i = 0; i < sv.allocated_client_slots; i++) { - if (svs.clients[i].state > cs_zombie && svs.clients[i].netchan.remote_address.type != NA_LOOPBACK) + if (svs.clients[i].state >= cs_connected && svs.clients[i].netchan.remote_address.type != NA_LOOPBACK) { SV_StuffcmdToClient(&svs.clients[i], cmd); } @@ -1917,6 +1931,7 @@ static qboolean Terr_Collect(heightmap_t *hm) } return false; } +#endif /*purge all sections, but not root lightmaps only are purged whenever the client rudely kills lightmaps @@ -2753,19 +2768,19 @@ void Terr_DrawTerrainModel (batch_t **batches, entity_t *e) } - if (r_refdef.gfog_rgbd[3] || gl_maxdist.value>0) + if (r_refdef.globalfog.density || gl_maxdist.value>0) { float culldist; extern cvar_t r_fog_exp2; - if (r_refdef.gfog_rgbd[3]) + if (r_refdef.globalfog.density) { //figure out the eyespace distance required to reach that fog value culldist = log(0.5/255.0f); if (r_fog_exp2.ival) - culldist = sqrt(culldist / (-r_refdef.gfog_rgbd[3] * r_refdef.gfog_rgbd[3])); + culldist = sqrt(culldist / (-r_refdef.globalfog.density * r_refdef.globalfog.density)); else - culldist = culldist / (-r_refdef.gfog_rgbd[3]); + culldist = culldist / (-r_refdef.globalfog.density); //anything drawn beyond this point is fully obscured by fog culldist += 4096; } @@ -3760,7 +3775,7 @@ static void ted_mixnoise(void *ctx, hmsection_t *s, int idx, float wx, float wy, static void ted_mixpaint(void *ctx, hmsection_t *s, int idx, float wx, float wy, float w) { unsigned char *lm = ted_getlightmap(s, idx); - char *texname = ctx; + const char *texname = ctx; int t; vec3_t newval; if (w > 1) @@ -3954,7 +3969,7 @@ static void ted_itterate(heightmap_t *hm, int distribution, float *pos, float ra } } -void ted_texkill(hmsection_t *s, char *killtex) +void ted_texkill(hmsection_t *s, const char *killtex) { int x, y, t, to; if (!s) @@ -4108,7 +4123,7 @@ void QCBUILTIN PF_terrain_edit(pubprogfuncs_t *prinst, struct globalvars_s *pr_g // ted_itterate(hm, tid_exponential, pos, radius, 1, SECTTEXSIZE, ted_mixset, G_VECTOR(OFS_PARM4)); // break; case ter_mix_paint: - ted_itterate(hm, tid_exponential, pos, radius, quant/10, SECTTEXSIZE, ted_mixpaint, PR_GetStringOfs(prinst, OFS_PARM4)); + ted_itterate(hm, tid_exponential, pos, radius, quant/10, SECTTEXSIZE, ted_mixpaint, (void*)PR_GetStringOfs(prinst, OFS_PARM4)); break; case ter_mix_concentrate: ted_itterate(hm, tid_exponential, pos, radius, 1, SECTTEXSIZE, ted_mixconcentrate, NULL); @@ -4378,7 +4393,7 @@ void Terr_FinishTerrain(heightmap_t *hm, char *shadername, char *skyname) #endif } -qboolean QDECL Terr_LoadTerrainModel (model_t *mod, void *buffer) +qboolean QDECL Terr_LoadTerrainModel (model_t *mod, void *buffer, size_t bufsize) { heightmap_t *hm; @@ -4632,10 +4647,6 @@ void Mod_Terrain_Reload_f(void) void Terr_Init(void) { -#ifdef M2 - GL_M2_Init(); -#endif - Cvar_Register(&mod_terrain_networked, "Terrain"); Cvar_Register(&mod_terrain_defaulttexture, "Terrain"); Cvar_Register(&mod_terrain_savever, "Terrain"); diff --git a/engine/gl/gl_hlmdl.c b/engine/gl/gl_hlmdl.c index cf0661f0..9959380f 100644 --- a/engine/gl/gl_hlmdl.c +++ b/engine/gl/gl_hlmdl.c @@ -72,7 +72,7 @@ void GL_Draw_HL_AliasFrame(short *order, vec3_t *transformed, float tex_w, float ======================================================================================================================= */ extern char loadname[]; -qboolean QDECL Mod_LoadHLModel (model_t *mod, void *buffer) +qboolean QDECL Mod_LoadHLModel (model_t *mod, void *buffer, size_t fsize) { /*~~*/ int i; @@ -224,7 +224,7 @@ void *Mod_GetHalfLifeModelData(model_t *mod) } #endif -int HLMod_FrameForName(model_t *mod, char *name) +int HLMod_FrameForName(model_t *mod, const char *name) { int i; hlmdl_header_t *h; diff --git a/engine/gl/gl_model.c b/engine/gl/gl_model.c index bdafa8e8..549f21f8 100644 --- a/engine/gl/gl_model.c +++ b/engine/gl/gl_model.c @@ -38,14 +38,15 @@ model_t *loadmodel; char loadname[32]; // for hunk tags void CM_Init(void); +void CM_Shutdown(void); -qboolean QDECL Mod_LoadSpriteModel (model_t *mod, void *buffer); -qboolean QDECL Mod_LoadSprite2Model (model_t *mod, void *buffer); -qboolean QDECL Mod_LoadBrushModel (model_t *mod, void *buffer); +qboolean QDECL Mod_LoadSpriteModel (model_t *mod, void *buffer, size_t fsize); +qboolean QDECL Mod_LoadSprite2Model (model_t *mod, void *buffer, size_t fsize); +qboolean QDECL Mod_LoadBrushModel (model_t *mod, void *buffer, size_t fsize); #ifdef Q2BSPS -qboolean QDECL Mod_LoadQ2BrushModel (model_t *mod, void *buffer); +qboolean QDECL Mod_LoadQ2BrushModel (model_t *mod, void *buffer, size_t fsize); #endif -model_t *Mod_LoadModel (model_t *mod, qboolean crash); +model_t *Mod_LoadModel (model_t *mod, enum mlverbosity_e verbose); #ifdef MAP_DOOM qboolean Mod_LoadDoomLevel(model_t *mod); @@ -249,8 +250,10 @@ int RelightThread(void *arg) { #ifdef _WIN32 surf = InterlockedIncrement(&relitsurface); +#elif defined(__GNUC__) + surf = __sync_add_and_fetch(&relitsurface, 1); #else - surf = relightthreads++; + surf = relitsurface++; #endif if (surf >= lightmodel->numsurfaces) break; @@ -270,7 +273,7 @@ void Mod_Think (void) if (!relightthreads) { int i; -#ifdef _WIN32 +#if defined(_WIN32) && !defined(WINRT) HANDLE me = GetCurrentProcess(); DWORD_PTR proc, sys; /*count cpus*/ @@ -284,6 +287,8 @@ void Mod_Think (void) relightthreads = 1; else relightthreads--; +#elif defined(__GNUC__) + relightthreads = 2; //erm, lets hope... #else /*can't do atomics*/ relightthreads = 1; @@ -553,6 +558,9 @@ void Mod_Shutdown (qboolean final) if (final) { Mod_UnRegisterAllModelFormats(NULL); +#ifdef Q2BSPS + CM_Shutdown(); +#endif } else { @@ -582,7 +590,7 @@ void *Mod_Extradata (model_t *mod) if (r) return r; - Mod_LoadModel (mod, true); + Mod_LoadModel (mod, MLV_ERROR); if (!mod->meshinfo) Sys_Error ("Mod_Extradata: caching failed"); @@ -639,7 +647,7 @@ Mod_FindName ================== */ -model_t *Mod_FindName (char *name) +model_t *Mod_FindName (const char *name) { int i; model_t *mod; @@ -682,7 +690,7 @@ Mod_TouchModel ================== */ -void Mod_TouchModel (char *name) +void Mod_TouchModel (const char *name) { model_t *mod; @@ -703,10 +711,10 @@ static struct char *formatname; char *ident; unsigned int magic; - qboolean (QDECL *load) (model_t *mod, void *buffer); + qboolean (QDECL *load) (model_t *mod, void *buffer, size_t buffersize); } modelloaders[64]; -int Mod_RegisterModelFormatText(void *module, const char *formatname, char *magictext, qboolean (QDECL *load) (model_t *mod, void *buffer)) +int Mod_RegisterModelFormatText(void *module, const char *formatname, char *magictext, qboolean (QDECL *load) (model_t *mod, void *buffer, size_t fsize)) { int i, free = -1; for (i = 0; i < sizeof(modelloaders)/sizeof(modelloaders[0]); i++) @@ -730,7 +738,7 @@ int Mod_RegisterModelFormatText(void *module, const char *formatname, char *magi return free+1; } -int Mod_RegisterModelFormatMagic(void *module, const char *formatname, unsigned int magic, qboolean (QDECL *load) (model_t *mod, void *buffer)) +int Mod_RegisterModelFormatMagic(void *module, const char *formatname, unsigned int magic, qboolean (QDECL *load) (model_t *mod, void *buffer, size_t fsize)) { int i, free = -1; for (i = 0; i < sizeof(modelloaders)/sizeof(modelloaders[0]); i++) @@ -791,13 +799,13 @@ Mod_LoadModel Loads a model into the cache ================== */ -model_t *Mod_LoadModel (model_t *mod, qboolean crash) +model_t *Mod_LoadModel (model_t *mod, enum mlverbosity_e verbose) { unsigned *buf = NULL; qbyte stackbuf[1024]; // avoid dirtying the cache heap char mdlbase[MAX_QPATH]; char *replstr; - qboolean doomsprite = false; + qboolean doomsprite = false, tryreplace; unsigned int magic, i; char *ext; @@ -899,6 +907,8 @@ model_t *Mod_LoadModel (model_t *mod, qboolean crash) if (!gl_load24bit.value) replstr = ""; + tryreplace = !!*replstr; + COM_StripExtension(mod->name, mdlbase, sizeof(mdlbase)); while (replstr) @@ -949,7 +959,7 @@ model_t *Mod_LoadModel (model_t *mod, qboolean crash) } if (i < sizeof(modelloaders) / sizeof(modelloaders[0])) { - if (!modelloaders[i].load(mod, buf)) + if (!modelloaders[i].load(mod, buf, com_filesize)) continue; if (mod->type == mod_brush) Surf_BuildModelLightmaps(mod); @@ -964,7 +974,7 @@ model_t *Mod_LoadModel (model_t *mod, qboolean crash) } if (i < sizeof(modelloaders) / sizeof(modelloaders[0])) { - if (!modelloaders[i].load(mod, buf)) + if (!modelloaders[i].load(mod, buf, com_filesize)) continue; } else @@ -1007,11 +1017,24 @@ model_t *Mod_LoadModel (model_t *mod, qboolean crash) return mod; } - if (crash) + switch(verbose) + { + default: + case MLV_ERROR: Host_EndGame ("Mod_NumForName: %s not found or couldn't load", mod->name); - - if (*mod->name != '*' && strcmp(mod->name, "null")) - Con_Printf(CON_ERROR "Unable to load or replace %s\n", mod->name); + break; + case MLV_WARN: + if (*mod->name != '*' && strcmp(mod->name, "null")) + { + if (tryreplace) + Con_Printf(CON_ERROR "Unable to load or replace %s\n", mod->name); + else + Con_Printf(CON_ERROR "Unable to load %s\n", mod->name); + } + break; + case MLV_SILENT: + break; + } mod->type = mod_dummy; mod->mins[0] = -16; mod->mins[1] = -16; @@ -1032,13 +1055,13 @@ Mod_ForName Loads in a model for the given name ================== */ -model_t *Mod_ForName (char *name, qboolean crash) +model_t *Mod_ForName (const char *name, enum mlverbosity_e verbosity) { model_t *mod; mod = Mod_FindName (name); - return Mod_LoadModel (mod, crash); + return Mod_LoadModel (mod, verbosity); } @@ -4129,7 +4152,7 @@ void Mod_FixupMinsMaxs(void) Mod_LoadBrushModel ================= */ -qboolean QDECL Mod_LoadBrushModel (model_t *mod, void *buffer) +qboolean QDECL Mod_LoadBrushModel (model_t *mod, void *buffer, size_t fsize) { int i, j; dheader_t *header; @@ -4567,7 +4590,7 @@ static void * Mod_LoadSpriteGroup (void * pin, mspriteframe_t **ppframe, int fra Mod_LoadSpriteModel ================= */ -qboolean QDECL Mod_LoadSpriteModel (model_t *mod, void *buffer) +qboolean QDECL Mod_LoadSpriteModel (model_t *mod, void *buffer, size_t fsize) { int i; int version; @@ -4722,7 +4745,7 @@ qboolean QDECL Mod_LoadSpriteModel (model_t *mod, void *buffer) } #ifdef SP2MODELS -qboolean QDECL Mod_LoadSprite2Model (model_t *mod, void *buffer) +qboolean QDECL Mod_LoadSprite2Model (model_t *mod, void *buffer, size_t fsize) { int i; int version; diff --git a/engine/gl/gl_model.h b/engine/gl/gl_model.h index 692f6934..435778ce 100644 --- a/engine/gl/gl_model.h +++ b/engine/gl/gl_model.h @@ -50,11 +50,12 @@ typedef enum { SHADER_SORT_COUNT } shadersort_t; -#define MAX_BONES 128 +#define MAX_BONES 256 +#define MAX_GPU_BONES 256 struct doll_s; void rag_flushdolls(qboolean force); void rag_freedoll(struct doll_s *doll); -struct doll_s *rag_createdollfromstring(struct model_s *mod, char *fname, int numbones, char *file); +struct doll_s *rag_createdollfromstring(struct model_s *mod, const char *fname, int numbones, const char *file); struct world_s; void rag_doallanimations(struct world_s *world); void rag_removedeltaent(lerpents_t *le); @@ -93,7 +94,7 @@ typedef struct mesh_s vec3_t *trnormals; qboolean istrifan; /*if its a fan/poly/single quad (permits optimisations)*/ - float *bones; + const float *bones; int numbones; byte_vec4_t *bonenums; vec4_t *boneweights; @@ -267,7 +268,7 @@ typedef struct vbo_s vboarray_t boneweights; unsigned int vbobones; - float *bones; + const float *bones; unsigned int numbones; struct vbo_s *next; @@ -958,17 +959,6 @@ typedef struct model_s #define MDLF_HASBRUSHES 0x400 // q1bsp has brush info for more precise traceboxes //============================================================================ -/* -void Mod_Init (void); -void Mod_ClearAll (void); -model_t *Mod_ForName (char *name, qboolean crash); -model_t *Mod_FindName (char *name); -void *Mod_Extradata (model_t *mod); // handles caching -void Mod_TouchModel (char *name); - -mleaf_t *Mod_PointInLeaf (float *p, model_t *model); -qbyte *Mod_LeafPVS (mleaf_t *leaf, model_t *model); -*/ #endif // __MODEL__ diff --git a/engine/gl/gl_ngraph.c b/engine/gl/gl_ngraph.c index a69d9537..6b21ed2a 100644 --- a/engine/gl/gl_ngraph.c +++ b/engine/gl/gl_ngraph.c @@ -99,7 +99,7 @@ void R_NetGraph (void) unsigned ngraph_pixels[NET_GRAPHHEIGHT][NET_TIMINGS]; x = 0; - lost = CL_CalcNet(); + lost = CL_CalcNet(r_netgraph.value); for (a=0 ; a 1) lm = 1; + VectorScale(res_diffuse, lm, res_diffuse); + VectorScale(res_ambient, lm, res_ambient); } #endif } diff --git a/engine/gl/gl_rmain.c b/engine/gl/gl_rmain.c index 7890a338..d144a7c9 100644 --- a/engine/gl/gl_rmain.c +++ b/engine/gl/gl_rmain.c @@ -66,6 +66,7 @@ extern cvar_t r_postprocshader; extern cvar_t gl_screenangle; extern cvar_t gl_mindist; +extern cvar_t vid_srgb; extern cvar_t ffov; @@ -133,6 +134,7 @@ void GL_InitSceneProcessingShaders (void) } gl_dither.modified = true; //fixme: bad place for this, but hey + vid_srgb.modified = true; } #define PP_WARP_TEX_SIZE 64 @@ -357,6 +359,7 @@ void R_RotateForEntity (float *m, float *modelview, const entity_t *e, const mod //================================================================================== +qboolean R_GameRectIsFullscreen(void); /* ============= R_SetupGL @@ -435,7 +438,7 @@ void R_SetupGL (float stereooffset) GL_ViewportUpdate(); - if (r_waterwarp.value<0 && (r_viewcontents & FTECONTENTS_FLUID)) + if ((r_waterwarp.value<0 || (r_waterwarp.value && !R_GameRectIsFullscreen())) && (r_viewcontents & FTECONTENTS_FLUID)) { fov_x *= 1 + (((sin(cl.time * 4.7) + 1) * 0.015) * r_waterwarp.value); fov_y *= 1 + (((sin(cl.time * 3.0) + 1) * 0.015) * r_waterwarp.value); @@ -494,8 +497,16 @@ void R_SetupGL (float stereooffset) qglDisable(GL_DITHER); } } + if (vid_srgb.modified) + { + if (vid_srgb.ival) + qglEnable(GL_FRAMEBUFFER_SRGB); + else + qglDisable(GL_FRAMEBUFFER_SRGB); + } } +void Surf_SetupFrame(void); /* ================ R_RenderScene @@ -579,6 +590,8 @@ void R_RenderScene (void) if (i) qglClear (GL_DEPTH_BUFFER_BIT); + Surf_SetupFrame(); + TRACE(("dbg: calling R_SetupGL\n")); R_SetupGL (stereooffset[i]); @@ -1150,13 +1163,18 @@ void GLR_DrawPortal(batch_t *batch, batch_t **blist, batch_t *depthmasklist[2], R_Clear ============= */ +qboolean R_GameRectIsFullscreen(void) +{ + return r_refdef.grect.x == 0 && r_refdef.grect.y == 0 && (unsigned)r_refdef.grect.width == vid.fbvwidth && (unsigned)r_refdef.grect.height == vid.fbvheight; +} + int gldepthfunc = GL_LEQUAL; void R_Clear (void) { /*tbh, this entire function should be in the backend*/ GL_ForceDepthWritable(); { - if (r_clear.ival && r_refdef.grect.x == 0 && r_refdef.grect.y == 0 && (unsigned)r_refdef.grect.width == vid.fbvwidth && (unsigned)r_refdef.grect.height == vid.fbvheight && !(r_refdef.flags & Q2RDF_NOWORLDMODEL)) + if (r_clear.ival && R_GameRectIsFullscreen() && !(r_refdef.flags & Q2RDF_NOWORLDMODEL)) { qglClearColor(1, 0, 0, 0); qglClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); @@ -1593,10 +1611,10 @@ void GLR_RenderView (void) //FIXME: support bloom+waterwarp even when drawing to an fbo? //FIXME: force waterwarp to a temp fbo always - if ((r_refdef.flags & Q2RDF_NOWORLDMODEL) || r_secondaryview || dofbo) + if ((r_refdef.flags & Q2RDF_NOWORLDMODEL) || dofbo) return; - if (r_secondaryview) + if (!R_GameRectIsFullscreen()) return; if (r_bloom.value) diff --git a/engine/gl/gl_rmisc.c b/engine/gl/gl_rmisc.c index aec2ce1d..26bf0b32 100644 --- a/engine/gl/gl_rmisc.c +++ b/engine/gl/gl_rmisc.c @@ -483,9 +483,6 @@ void GLR_Init (void) // Cvar_Hook(&r_floortexture, GLR_Floortexture_Callback); // Cvar_Hook(&r_walltexture, GLR_Walltexture_Callback); // Cvar_Hook(&r_drawflat, GLR_Drawflat_Callback); - Cvar_Hook(&v_gamma, GLV_Gamma_Callback); - Cvar_Hook(&v_contrast, GLV_Gamma_Callback); - Cvar_Hook(&v_brightness, GLV_Gamma_Callback); GLR_ReInit(); } diff --git a/engine/gl/gl_screen.c b/engine/gl/gl_screen.c index 46992e00..b94a226c 100644 --- a/engine/gl/gl_screen.c +++ b/engine/gl/gl_screen.c @@ -104,6 +104,7 @@ void GLSCR_UpdateScreen (void) Shader_DoReload(); GL_BeginRendering (); + qglDisable(GL_SCISSOR_TEST); #ifdef VM_UI uimenu = UI_MenuState(); #else diff --git a/engine/gl/gl_shader.c b/engine/gl/gl_shader.c index 3db53b7f..e9325816 100644 --- a/engine/gl/gl_shader.c +++ b/engine/gl/gl_shader.c @@ -41,12 +41,15 @@ texid_t r_whiteimage; static qboolean shader_reload_needed; static qboolean shader_rescan_needed; +sh_config_t sh_config; + //cvars that affect shader generation cvar_t r_vertexlight = CVARFD("r_vertexlight", "0", CVAR_SHADERSYSTEM, "Hack loaded shaders to remove detail pass and lightmap sampling for faster rendering."); extern cvar_t r_glsl_offsetmapping_reliefmapping; extern cvar_t r_deluxemapping; extern cvar_t r_fastturb, r_fastsky, r_skyboxname; extern cvar_t r_drawflat; +extern cvar_t r_shaderblobs; //backend fills this in to say the max pass count int be_maxpasses; @@ -54,7 +57,6 @@ int be_maxpasses; #define Q_stricmp stricmp #define Q_strnicmp strnicmp -#define Com_sprintf snprintf #define clamp(v,min, max) (v) = (((v)<(min))?(min):(((v)>(max))?(max):(v))); typedef union { @@ -196,32 +198,43 @@ static float Shader_FloatArgument(shader_t *shader, char *arg) #define HASH_SIZE 128 +enum shaderparsemode_e +{ + SPM_DEFAULT, /*quake3/fte internal*/ + SPM_DOOM3, +}; + +static struct +{ + enum shaderparsemode_e mode; +} parsestate; + typedef struct shaderkey_s { char *keyword; void (*func)( shader_t *shader, shaderpass_t *pass, char **ptr ); } shaderkey_t; - +typedef struct shadercachefile_s { + char *data; + size_t length; + enum shaderparsemode_e parsemode; + struct shadercachefile_s *next; + char name[1]; +} shadercachefile_t; typedef struct shadercache_s { - char name[MAX_QPATH]; - char *path; - unsigned int offset; + shadercachefile_t *source; + size_t offset; struct shadercache_s *hash_next; + char name[1]; } shadercache_t; -static shadercache_t **shader_hash; -static char shaderbuf[MAX_QPATH * 256]; -int shaderbuflen; +static shadercachefile_t *shaderfiles; //contents of a .shader file +static shadercache_t **shader_hash; //locations of known inactive shaders. -static enum { - SPM_DEFAULT, /*quake3/fte internal*/ - SPM_DOOM3, -} shaderparsemode; - -unsigned int r_numshaders; -unsigned int r_maxshaders; -shader_t **r_shaders; -static hashtable_t shader_active_hash; +unsigned int r_numshaders; //number of active slots in r_shaders array. +unsigned int r_maxshaders; //max length of r_shaders array. resized if exceeded. +shader_t **r_shaders; //list of active shaders for a id->shader lookup +static hashtable_t shader_active_hash; //list of active shaders for a name->shader lookup void *shader_active_hash_mem; //static char r_skyboxname[MAX_QPATH]; @@ -230,9 +243,10 @@ void *shader_active_hash_mem; char *Shader_Skip( char *ptr ); static qboolean Shader_Parsetok(shader_t *shader, shaderpass_t *pass, shaderkey_t *keys, char *token, char **ptr); static void Shader_ParseFunc(shader_t *shader, char **args, shaderfunc_t *func); -static void Shader_MakeCache(char *path); -static void Shader_GetPathAndOffset(char *name, char **path, unsigned int *offset); +static void Shader_MakeCache(const char *path); +static qboolean Shader_LocateSource(char *name, char **buf, size_t *bufsize, size_t *offset, enum shaderparsemode_e *parsemode); static void Shader_ReadShader(shader_t *s, char *shadersource, int parsemode); +static qboolean Shader_ParseShader(char *parsename, shader_t *s); //=========================================================================== @@ -270,87 +284,17 @@ static qboolean Shader_EvaluateCondition(shader_t *shader, char **ptr) else if (!Q_stricmp(token, "d3d11")) conditiontrue = conditiontrue == (qrenderer == QR_DIRECT3D11); else if (!Q_stricmp(token, "gles")) - { -#ifdef GLQUAKE - conditiontrue = conditiontrue == ((qrenderer == QR_OPENGL) && !!gl_config.gles); -#else - conditiontrue = conditiontrue == false; -#endif - } + conditiontrue = conditiontrue == ((qrenderer == QR_OPENGL) && sh_config.minver == 100); else if (!Q_stricmp(token, "nofixed")) - { - switch(qrenderer) - { -#ifdef GLQUAKE - case QR_OPENGL: - conditiontrue = conditiontrue == ((qrenderer == QR_OPENGL) && !!gl_config.nofixedfunc); - break; -#endif -#ifdef D3D11QUAKE - case QR_DIRECT3D11: - conditiontrue = conditiontrue == true; - break; -#endif - default: - conditiontrue = conditiontrue == false; - break; - } - } + conditiontrue = conditiontrue == sh_config.progs_required; else if (!Q_stricmp(token, "glsl")) - { -#ifdef GLQUAKE - conditiontrue = conditiontrue == ((qrenderer == QR_OPENGL) && gl_config.arb_shader_objects); -#else - conditiontrue = conditiontrue == false; -#endif - } + conditiontrue = conditiontrue == ((qrenderer == QR_OPENGL) && sh_config.progs_supported); else if (!Q_stricmp(token, "hlsl")) - { - switch(qrenderer) - { -#ifdef D3D9QUAKE - case QR_DIRECT3D9: - conditiontrue = conditiontrue == true; //FIXME - break; -#endif -#ifdef D3D11QUAKE - case QR_DIRECT3D11: - conditiontrue = conditiontrue == true; - break; -#endif - default: - conditiontrue = conditiontrue == false; - break; - } - } + conditiontrue = conditiontrue == ((qrenderer == QR_DIRECT3D9 || qrenderer == QR_DIRECT3D11) && sh_config.progs_supported); else if (!Q_stricmp(token, "haveprogram")) - { conditiontrue = conditiontrue == !!shader->prog; - } else if (!Q_stricmp(token, "programs")) - { - switch(qrenderer) - { -#ifdef GLQUAKE - case QR_OPENGL: - conditiontrue = conditiontrue == gl_config.arb_shader_objects; //FIXME - break; -#endif -#ifdef D3D9QUAKE - case QR_DIRECT3D9: - conditiontrue = conditiontrue == true; //FIXME - break; -#endif -#ifdef D3D11QUAKE - case QR_DIRECT3D11: - conditiontrue = conditiontrue == true; - break; -#endif - default: - conditiontrue = conditiontrue == false; - break; - } - } + conditiontrue = conditiontrue == sh_config.progs_supported; else if (!Q_stricmp(token, "diffuse")) conditiontrue = conditiontrue == true; else if (!Q_stricmp(token, "specular")) @@ -599,7 +543,7 @@ qboolean Shader_ParseSkySides (char *shadername, char *texturename, texid_t *ima { for (ss = 0; ss < sizeof(skyname_suffix)/sizeof(skyname_suffix[0]); ss++) { - Com_sprintf ( path, sizeof(path), skyname_pattern[sp], texturename, skyname_suffix[ss][i] ); + Q_snprintfz ( path, sizeof(path), skyname_pattern[sp], texturename, skyname_suffix[ss][i] ); images[i] = R_LoadHiResTexture ( path, NULL, IF_NOALPHA|IF_CLAMP); if (TEXVALID(images[i])) break; @@ -682,7 +626,7 @@ static int Shader_SetImageFlags(shader_t *shader, shaderpass_t *pass, char **nam static texid_t Shader_FindImage ( char *name, int flags ) { - if (shaderparsemode == SPM_DOOM3) + if (parsestate.mode == SPM_DOOM3) { if (!Q_stricmp (name, "_default")) return r_whiteimage; /*fixme*/ @@ -917,7 +861,7 @@ static void Shader_EntityMergable ( shader_t *shader, shaderpass_t *pass, char * static void Shader_ProgAutoFields(program_t *prog, char **cvarnames, int *cvartypes); /*program text is already loaded, this function parses the 'header' of it to see which permutations it provides, and how many times we need to recompile it*/ -static qboolean Shader_LoadPermutations(char *name, program_t *prog, char *script, int qrtype, int ver) +static qboolean Shader_LoadPermutations(char *name, program_t *prog, char *script, int qrtype, int ver, char *blobfilename) { static char *permutationname[] = { @@ -937,6 +881,11 @@ static qboolean Shader_LoadPermutations(char *name, program_t *prog, char *scrip int nummodifiers = 0; int p, n, pn; char *end; + vfsfile_t *blobfile; + unsigned int permuoffsets[PERMUTATIONS], initoffset=0; + unsigned int blobheaderoffset=0; + qboolean blobadded; + qboolean tess = false; char *cvarnames[64]; int cvartypes[64]; @@ -957,7 +906,19 @@ static qboolean Shader_LoadPermutations(char *name, program_t *prog, char *scrip while (*script == ' ' || *script == '\r' || *script == '\n' || *script == '\t') script++; if (!strncmp(script, "!!fixed", 7)) + { prog->nofixedcompat = false; + script += 7; + while (*script && *script != '\n') + script++; + } + else if (!strncmp(script, "!!tess", 6)) + { + tess = true; + script += 6; + while (*script && *script != '\n') + script++; + } else if (!strncmp(script, "!!cvardf", 8)) { script += 8; @@ -1061,6 +1022,44 @@ static qboolean Shader_LoadPermutations(char *name, program_t *prog, char *scrip break; }; + if (sh_config.pLoadBlob && blobfilename && *blobfilename) + blobfile = FS_OpenVFS(blobfilename, "w+b", FS_GAMEONLY); + else + blobfile = NULL; + + if (blobfile) + { + unsigned int magic; + unsigned int corrupt = false; + char ver[MAX_QPATH]; + char *thisver = version_string(); + corrupt |= VFS_READ(blobfile, &magic, sizeof(magic)) != sizeof(magic); + corrupt |= magic != *(unsigned int*)"FBLB"; + corrupt |= VFS_READ(blobfile, &blobheaderoffset, sizeof(blobheaderoffset)) != sizeof(blobheaderoffset); + corrupt |= VFS_READ(blobfile, ver, sizeof(ver)) != sizeof(ver); + + corrupt |= strcmp(ver, thisver); + //if the magic or header didn't read properly then the file is corrupt + if (corrupt) + { + //close and reopen it without the + flag, to replace it with a new file. + VFS_CLOSE(blobfile); + blobfile = FS_OpenVFS(blobfilename, "wb", FS_GAMEONLY); + + if (blobfile) + { + VFS_SEEK(blobfile, 0); + magic = *(unsigned int*)"FBLB"; //magic + VFS_WRITE(blobfile, &magic, sizeof(magic)); + memset(ver, 0, sizeof(ver)); //make sure we don't leak stuff. + Q_strncpyz(ver, thisver, sizeof(ver)); + VFS_WRITE(blobfile, ver, sizeof(ver)); + blobheaderoffset = 0; + } + } + } + blobadded = false; + if (gl_specular.value) { if (nummodifiers < MAXMODIFIERS) @@ -1090,6 +1089,78 @@ static qboolean Shader_LoadPermutations(char *name, program_t *prog, char *scrip } } + if (blobfile) + { + unsigned int next; + unsigned int argsz; + char *args, *mp, *mv; + int ml, mi; + unsigned int bloblink = 4; + + //walk through looking for an argset match + while (blobheaderoffset) + { + VFS_SEEK(blobfile, blobheaderoffset); + VFS_READ(blobfile, &next, sizeof(next)); + VFS_READ(blobfile, &argsz, sizeof(argsz)); + args = Z_Malloc(argsz+1); + VFS_READ(blobfile, args, argsz); + args[argsz] = 0; + for (mi = 0, mp = args; mi < nummodifiers; mi++) + { + mv = permutationdefines[mi]+8; + ml = strlen(mv); + if (mp+ml > args+argsz) + break; + if (strncmp(mp, mv, ml)) + break; + mp += ml; + } + //this one is a match. + if (mi == nummodifiers && mp == args+argsz) + { + blobheaderoffset = VFS_TELL(blobfile); + VFS_READ(blobfile, permuoffsets, sizeof(permuoffsets)); + break; + } + + bloblink = blobheaderoffset; + blobheaderoffset = next; + } + + //these arguments have never been seen before. add a new argset. + if (!blobheaderoffset) + { + unsigned int link = 0; + initoffset = VFS_GETLEN(blobfile); + VFS_SEEK(blobfile, initoffset); + VFS_WRITE(blobfile, &link, sizeof(link)); + + for (mi = 0, argsz = 0; mi < nummodifiers; mi++) + { + mv = permutationdefines[mi]+8; + ml = strlen(mv); + argsz += ml; + } + VFS_WRITE(blobfile, &argsz, sizeof(argsz)); + for (mi = 0; mi < nummodifiers; mi++) + { + mv = permutationdefines[mi]+8; + ml = strlen(mv); + VFS_WRITE(blobfile, mv, ml); + } + + //and the offsets come here + blobheaderoffset = VFS_TELL(blobfile); + memset(permuoffsets, 0, sizeof(permuoffsets)); + VFS_WRITE(blobfile, permuoffsets, sizeof(permuoffsets)); + + //now rewrite the link to add us. the value in the file should always be set to 0. + VFS_SEEK(blobfile, bloblink); + VFS_WRITE(blobfile, &initoffset, sizeof(initoffset)); + } + } + for (p = 0; p < PERMUTATIONS; p++) { memset(&prog->permu[p].handle, 0, sizeof(prog->permu[p].handle)); @@ -1116,40 +1187,45 @@ static qboolean Shader_LoadPermutations(char *name, program_t *prog, char *scrip } permutationdefines[pn++] = NULL; - if (0) + + if (blobfile && permuoffsets[p]) { + VFS_SEEK(blobfile, permuoffsets[p]); + if (sh_config.pLoadBlob(prog, name, p, blobfile)) + continue; //blob was loaded from disk, yay. + //otherwise fall through. } -#ifdef GLQUAKE - else if (qrenderer == QR_OPENGL) + if (blobfile) { - if (prog->permu[p].handle.glsl) - qglDeleteProgramObject_(prog->permu[p].handle.glsl); - prog->permu[p].handle.glsl = GLSlang_CreateProgram(name, (((p & PERMUTATION_SKELETAL) && ver < 120)?120:ver), permutationdefines, script, script, (p & PERMUTATION_SKELETAL)?true:onefailed); - if (!prog->permu[p].handle.glsl && !(p & PERMUTATION_SKELETAL)) - onefailed = true; - if (!p && !prog->permu[p].handle.glsl) + initoffset = VFS_GETLEN(blobfile); + VFS_SEEK(blobfile, initoffset); + } + if (!sh_config.pCreateProgram(prog, name, p, permutationdefines, script, tess?script:NULL, tess?script:NULL, script, (p & PERMUTATION_SKELETAL)?true:onefailed, blobfile)) + { + if (!(p & PERMUTATION_SKELETAL)) + onefailed = true; //don't flag it if skeletal failed. + if (!p) //give up if permutation 0 failed. that one failing is fatal. break; } -#endif -#ifdef D3D9QUAKE - else if (qrenderer == QR_DIRECT3D9) + if (blobfile && initoffset != VFS_GETLEN(blobfile)) { - if (!D3D9Shader_CreateProgram(prog, name, p, permutationdefines, script, script)) - break; + permuoffsets[p] = initoffset; + blobadded = true; } -#endif -#ifdef D3D11QUAKE - else if (qrenderer == QR_DIRECT3D11) - { - if (!D3D11Shader_CreateProgram(prog, name, p, permutationdefines, script, script)) - break; - } -#endif } while(nummodifiers) Z_Free(permutationdefines[--nummodifiers]); - Shader_ProgAutoFields(prog, cvarnames, cvartypes); + if (sh_config.pProgAutoFields) + sh_config.pProgAutoFields(prog, cvarnames, cvartypes); + + if (blobfile && blobadded) + { + VFS_SEEK(blobfile, blobheaderoffset); + VFS_WRITE(blobfile, permuoffsets, sizeof(permuoffsets)); + } + if (blobfile) + VFS_CLOSE(blobfile); if (p == PERMUTATIONS) return true; @@ -1171,289 +1247,18 @@ struct sbuiltin_s char *body; } sbuiltins[] = { -#if 0//def GLQUAKE - /*a quick note on glsl versions: - gl versioning started with 110 - gles versioning started at 100 and only had a single one defined - with gl3's combined support, gl3 supports 130+ and 100, but 110 requries compat extension - with gl4, versions are meant to match the gl version more closely, so gl4.0 uses 400.*/ - /*glsl es shaders require precisions to be defined for fragment shader variables - more precision for shaders would be a good candidate for a cvar */ - -/*defaultfill is a simple shader for block-filling with vertex colours. note that the blendfunc stuff is done after the shader anyway.*/ - {QR_OPENGL/*ES*/, 100, "defaultfill", - "#ifdef VERTEX_SHADER\n" - "attribute vec4 v_colour;\n" - "varying vec4 vc;\n" - - "void main ()\n" - "{\n" - " vc = v_colour;\n" - " gl_Position = ftetransform();\n" - "}\n" - "#endif\n" - - "#ifdef FRAGMENT_SHADER\n" - "varying lowp vec4 vc;\n" - "void main ()\n" - "{\n" - " gl_FragColor = vc;\n" - "}\n" - "#endif\n" - }, -/*default2d is a simple shader to draw the 2d elements. simple copying of a texture image to its dest (with vertex colour blending)*/ - {QR_OPENGL/*ES*/, 100, "default2d", - "#ifdef VERTEX_SHADER\n" - "attribute vec2 v_texcoord;\n" - "attribute vec4 v_colour;\n" - "varying vec2 tc;\n" - "varying vec4 vc;\n" - - "void main ()\n" - "{\n" - " tc = v_texcoord;\n" - " vc = v_colour;\n" - " gl_Position = ftetransform();\n" - "}\n" - "#endif\n" - - "#ifdef FRAGMENT_SHADER\n" - "uniform sampler2D s_t0;\n" - "varying mediump vec2 tc;\n" - "varying lowp vec4 vc;\n" - - "void main ()\n" - "{\n" - " gl_FragColor = texture2D(s_t0, tc) * vc;\n" - "}\n" - "#endif\n" - }, -/*draws a wall, with lightmap.*/ - {QR_OPENGL/*ES*/, 100, "defaultwall", - "#ifdef VERTEX_SHADER\n" - "attribute vec2 v_texcoord;\n" - "attribute vec2 v_lmcoord;\n" - "varying vec2 tc, lm;\n" - - "void main ()\n" - "{\n" - " tc = v_texcoord;\n" - " lm = v_lmcoord;\n" - " gl_Position = ftetransform();\n" - "}\n" - "#endif\n" - - "#ifdef FRAGMENT_SHADER\n" - "uniform sampler2D s_t0;\n" /*tex_diffuse*/ - "uniform sampler2D s_t1;\n" /*tex_lightmap*/ - //"uniform sampler2D s_t2;\n" /*tex_normalmap*/ - //"uniform sampler2D s_t3;\n" /*tex_deluxmap*/ - //"uniform sampler2D s_t4;\n" /*tex_fullbright*/ - "varying mediump vec2 tc, lm;\n" - "uniform mediump vec4 e_lmscale;\n" - - "void main ()\n" - "{\n" - " gl_FragColor = texture2D(s_t0, tc) * texture2D(s_t1, lm) * e_lmscale;\n" - "}\n" - "#endif\n" - }, - {QR_OPENGL, 100, "drawflat_wall", - "!!cvarv r_floorcolor\n" - "!!cvarv r_wallcolor\n" - "!!permu FOG\n" - "#include \"sys/fog.h\"\n" - "varying vec4 col;\n" - "#ifdef VERTEX_SHADER\n" - "attribute vec3 v_normal;\n" - "attribute vec2 v_lmcoord;\n" - "varying vec2 lm;\n" - "uniform vec3 cvar_r_wallcolor;\n" - "uniform vec3 cvar_r_floorcolor;\n" - "uniform vec4 e_lmscale;\n" - - "void main ()\n" - "{\n" - " col = vec4(e_lmscale.rgb/255.0 * ((v_normal.z < 0.73)?cvar_r_wallcolor:cvar_r_floorcolor), e_lmscale.a);\n" - " lm = v_lmcoord;\n" - " gl_Position = ftetransform();\n" - "}\n" - "#endif\n" - - "#ifdef FRAGMENT_SHADER\n" - "uniform sampler2D s_t0;\n" /*tex_lightmap*/ - "varying vec2 lm;\n" - - "void main ()\n" - "{\n" - " gl_FragColor = fog4(col * texture2D(s_t0, lm));\n" - "}\n" - "#endif\n" - }, -/*defaultwarp: draws a water surface*/ - {QR_OPENGL/*ES*/, 100, "defaultwarp", - "!!cvarf r_wateralpha\n" - "varying mediump vec2 tc;\n" - "#ifdef VERTEX_SHADER\n" - "attribute vec2 v_texcoord;\n" - "void main ()\n" - "{\n" - " tc = v_texcoord;\n" - " gl_Position = ftetransform();\n" - "}\n" - "#endif\n" - - "#ifdef FRAGMENT_SHADER\n" - "uniform sampler2D s_t0;\n" - "uniform mediump float e_time;\n" - "uniform lowp float cvar_r_wateralpha;\n" - - "void main ()\n" - "{\n" - " mediump vec2 ntc;\n" - " ntc.s = tc.s + sin(tc.t+e_time)*0.125;\n" - " ntc.t = tc.t + sin(tc.s+e_time)*0.125;\n" - " lowp vec3 ts = vec3(texture2D(s_t0, ntc));\n" - - " gl_FragColor = vec4(ts, cvar_r_wateralpha);\n" - "}\n" - "#endif\n" - }, -/*defautsky projects the texture in order to match q1 skies, along with two separate layers scrolling at separate speeds*/ - {QR_OPENGL/*ES*/, 100, "defaultsky", - "#ifdef VERTEX_SHADER\n" - "varying vec3 pos;\n" - - "void main ()\n" - "{\n" - " pos = v_position.xyz;\n" - " gl_Position = ftetransform();\n" - "}\n" - "#endif\n" - - "#ifdef FRAGMENT_SHADER\n" - "uniform sampler2D s_t0;\n" - "uniform sampler2D s_t1;\n" - - "uniform mediump float e_time;\n" - "uniform mediump vec3 e_eyepos;\n" - "varying mediump vec3 pos;\n" - - "void main ()\n" - "{\n" - " mediump vec2 tccoord;\n" - - " mediump vec3 dir = pos - e_eyepos;\n" - - " dir.z *= 3.0;\n" - " dir.xy /= 0.5*length(dir);\n" - - " tccoord = (dir.xy + e_time*0.03125);\n" - " lowp vec3 solid = vec3(texture2D(s_t0, tccoord));\n" - - " tccoord = (dir.xy + e_time*0.0625);\n" - " lowp vec4 clouds = texture2D(s_t1, tccoord);\n" - - " gl_FragColor.rgb = (solid.rgb*(1.0-clouds.a)) + (clouds.a*clouds.rgb);\n" -// " gl_FragColor.rgb = solid.rgb;/*gl_FragColor.g = clouds.r;*/gl_FragColor.b = clouds.a;\n" - "}\n" - "#endif\n" - }, -/*draws a model. there's lots of extra stuff for light shading calcs and upper/lower textures*/ - {QR_OPENGL/*ES*/, 100, "defaultskin", - "!!permu FULLBRIGHT\n" - "!!permu UPPERLOWER\n" - "!!permu FRAMEBLEND\n" - "!!permu SKELETAL\n" - "#ifdef VERTEX_SHADER\n" - "#include \"sys/skeletal.h\"\n" - "attribute vec2 v_texcoord;\n" - "varying vec2 tc;\n" - - "uniform vec3 e_light_dir;\n" - "uniform vec3 e_light_mul;\n" - "uniform vec3 e_light_ambient;\n" - "varying vec3 light;\n" - - "void main ()\n" - "{\n" - " vec3 n;\n" - " gl_Position = skeletaltransform_n(n);\n" - " light = e_light_ambient + (dot(n,e_light_dir)*e_light_mul);\n" - " tc = v_texcoord;\n" - "}\n" - "#endif\n" - - "#ifdef FRAGMENT_SHADER\n" - "uniform sampler2D s_t0;\n" /*tex_diffuse*/ - "#ifdef LOWER\n" - "uniform sampler2D s_t1;\n" /*tex_lower*/ - "uniform lowp vec3 e_lowercolour;\n" - "#endif\n" - "#ifdef UPPER\n" - "uniform sampler2D s_t2;\n" /*tex_upper*/ - "uniform lowp vec3 e_uppercolour;\n" - "#endif\n" - "#ifdef FULLBRIGHT\n" - "uniform sampler2D s_t3;\n" /*tex_fullbright*/ - "#endif\n" - "varying mediump vec2 tc;\n" - "varying lowp vec3 light;\n" - "uniform lowp vec4 e_colourident;\n" - - "void main ()\n" - "{\n" - " lowp vec4 col;\n" - " col = texture2D(s_t0, tc);\n" - "#ifdef UPPER\n" - " lowp vec4 uc = texture2D(s_t2, tc);\n" - " col.rgb = mix(col.rgb, uc.rgb*e_uppercolour, uc.a);\n" - "#endif\n" - "#ifdef LOWER\n" - " lowp vec4 lc = texture2D(s_t1, tc);\n" - " col.rgb = mix(col.rgb, lc.rgb*e_lowercolour, lc.a);\n" - "#endif\n" - " col.rgb *= light;\n" - "#ifdef FULLBRIGHT\n" - " lowp vec4 fb = texture2D(s_t3, tc);\n" - " col.rgb = mix(col.rgb, fb.rgb, fb.a);\n" - "#endif\n" - " gl_FragColor = col * e_colourident;\n" - "}\n" - "#endif\n" - }, -#endif #include "r_bishaders.h" {QR_NONE} }; void Shader_UnloadProg(program_t *prog) { -#ifdef GLQUAKE - if (qrenderer == QR_OPENGL) + if (sh_config.pDeleteProg) { int p; for (p = 0; p < PERMUTATIONS; p++) - { - if (prog->permu[p].handle.glsl) - qglDeleteProgramObject_(prog->permu[p].handle.glsl); - } + sh_config.pDeleteProg(prog, p); } -#endif -#ifdef D3D9QUAKE - if (qrenderer == QR_DIRECT3D9) - { - int p; - for (p = 0; p < PERMUTATIONS; p++) - { -// if (prog->handle[p].hlsl.vert || prog->handle[p].hlsl.frag) -// D3DShader_DeleteProgram(&prog->handle[p].hlsl); - } - } -#endif -#ifdef D3D11QUAKE - if (qrenderer == QR_DIRECT3D11) - D3D11Shader_DeleteProgram(prog); -#endif + free(prog); } static void Shader_FlushGenerics(void) @@ -1478,6 +1283,7 @@ static void Shader_LoadGeneric(sgeneric_t *g, int qrtype) unsigned int i; void *file; char basicname[MAX_QPATH]; + char blobname[MAX_QPATH]; char *h; g->failed = true; @@ -1489,26 +1295,26 @@ static void Shader_LoadGeneric(sgeneric_t *g, int qrtype) *h = '\0'; if (strchr(basicname, '/') || strchr(basicname, '.')) - FS_LoadFile(basicname, &file); - else if (qrenderer == QR_DIRECT3D9) - FS_LoadFile(va("hlsl/%s.hlsl", basicname), &file); - else if (qrenderer == QR_DIRECT3D11) - FS_LoadFile(va("hlsl11/%s.hlsl", basicname), &file); - else if (qrenderer == QR_OPENGL) { -#ifdef GLQUAKE - if (gl_config.gles) - FS_LoadFile(va("gles/%s.glsl", basicname), &file); - else -#endif - FS_LoadFile(va("glsl/%s.glsl", basicname), &file); + FS_LoadFile(basicname, &file); + *blobname = 0; } else - file = NULL; + { + if (sh_config.progpath) + FS_LoadFile(va(sh_config.progpath, basicname), &file); + else + file = NULL; + if (sh_config.blobpath && r_shaderblobs.ival) + Q_snprintfz(blobname, sizeof(blobname), sh_config.blobpath, basicname); + else + *blobname = 0; + } + if (file) { Con_DPrintf("Loaded %s from disk\n", basicname); - g->failed = !Shader_LoadPermutations(g->name, &g->prog, file, qrtype, 0); + g->failed = !Shader_LoadPermutations(g->name, &g->prog, file, qrtype, 0, blobname); FS_FreeFile(file); return; } @@ -1520,24 +1326,11 @@ static void Shader_LoadGeneric(sgeneric_t *g, int qrtype) if (sbuiltins[i].qrtype == qrenderer && !strcmp(sbuiltins[i].name, basicname)) { ver = sbuiltins[i].apiver; -#ifdef GLQUAKE - if (qrenderer == QR_OPENGL) - { - if (gl_config.gles) - { - if (ver == 110) - ver = 100; /*allow gles to use desktop gl if there's no shader for it - lets hope numeric sorting applies!*/ - if (ver != 100) - continue; - } - else - { - if (ver == 100) /*don't use gles shaders on desktop gl*/ - continue; - } - } -#endif - g->failed = !Shader_LoadPermutations(g->name, &g->prog, sbuiltins[i].body, sbuiltins[i].qrtype, ver); + + if (ver < sh_config.minver || ver > sh_config.maxver) + continue; + + g->failed = !Shader_LoadPermutations(g->name, &g->prog, sbuiltins[i].body, sbuiltins[i].qrtype, ver, blobname); if (g->failed) continue; @@ -1570,6 +1363,10 @@ static program_t *Shader_FindGeneric(char *name, int qrtype) } } + //don't even try if we know it won't work. + if (!sh_config.progs_supported) + return NULL; + g = malloc(sizeof(*g) + strlen(name)+1); memset(g, 0, sizeof(*g)); g->name = (char*)(g+1); @@ -1699,193 +1496,6 @@ struct shader_field_names_s shader_unif_names[] = {NULL} }; -static void Shader_ProgAutoFields(program_t *prog, char **cvarnames, int *cvartypes) -{ - unsigned int i, p; - qboolean found; - int uniformloc; - char tmpname[128]; - cvar_t *cvar; - - prog->numparams = 0; -#ifdef GLQUAKE - if (qrenderer == QR_OPENGL) - { - //figure out visible attributes - for (p = 0; p < PERMUTATIONS; p++) - { - if (!prog->permu[p].handle.glsl) - continue; - GLSlang_UseProgram(prog->permu[p].handle.glsl); - for (i = 0; shader_attr_names[i].name; i++) - { - uniformloc = qglGetAttribLocationARB(prog->permu[p].handle.glsl, shader_attr_names[i].name); - if (uniformloc != -1) - { - if (shader_attr_names[i].ptype != uniformloc) - Con_Printf("Bad attribute\n"); - else - prog->permu[p].attrmask |= 1u<permu[p].handle.glsl) - continue; - GLSlang_UseProgram(prog->permu[p].handle.glsl); - - uniformloc = qglGetUniformLocationARB(prog->permu[p].handle.glsl, shader_unif_names[i].name); - if (uniformloc != -1) - found = true; - - if (prog->numparams == SHADER_PROGPARMS_MAX) - { - if (found) - break; - } - else - prog->permu[p].parm[prog->numparams] = uniformloc; - } - if (found) - { - if (prog->numparams == SHADER_PROGPARMS_MAX) - Con_Printf("Too many paramters for program (ignoring %s)\n", shader_unif_names[i].name); - else - { - prog->parm[prog->numparams].type = shader_unif_names[i].ptype; - prog->numparams++; - } - } - } - /*set cvar unirforms*/ - for (i = 0; cvarnames[i]; i++) - { - if (prog->numparams == SHADER_PROGPARMS_MAX) - { - Con_Printf("Too many cvar paramters for program\n"); - break; - } - for (p = 0; cvarnames[i][p] && (unsigned char)cvarnames[i][p] > 32 && p < sizeof(tmpname)-1; p++) - tmpname[p] = cvarnames[i][p]; - tmpname[p] = 0; - cvar = Cvar_Get(tmpname, "0", CVAR_SHADERSYSTEM, "glsl cvars"); - if (!cvar) - continue; - cvar->flags |= CVAR_SHADERSYSTEM; - prog->parm[prog->numparams].type = cvartypes[i]; - prog->parm[prog->numparams].pval = cvar; - found = false; - for (p = 0; p < PERMUTATIONS; p++) - { - if (!prog->permu[p].handle.glsl) - continue; - GL_SelectProgram(prog->permu[p].handle.glsl); - uniformloc = qglGetUniformLocationARB(prog->permu[p].handle.glsl, va("cvar_%s", tmpname)); - if (uniformloc != -1) - { - //qglUniform1fARB(uniformloc, cvar->value); - found = true; - } - prog->permu[p].parm[prog->numparams] = uniformloc; - } - if (found) - prog->numparams++; - } - /*set texture uniforms*/ - for (p = 0; p < PERMUTATIONS; p++) - { - if (!prog->permu[p].handle.glsl) - continue; - if (!(prog->permu[p].attrmask & (1u<permu[p].attrmask |= (1u<permu[p].handle.glsl); - for (i = 0; i < 8; i++) - { - uniformloc = qglGetUniformLocationARB(prog->permu[p].handle.glsl, va("s_t%i", i)); - if (uniformloc != -1) - qglUniform1iARB(uniformloc, i); - } - } - return; - } -#endif -#ifdef D3D9QUAKE - if (qrenderer == QR_DIRECT3D9) - { - prog->nofixedcompat = true; - - /*set cvar uniforms*/ - for (i = 0; cvarnames[i]; i++) - { - for (p = 0; cvarnames[i][p] && (unsigned char)cvarnames[i][p] > 32 && p < sizeof(tmpname)-1; p++) - tmpname[p] = cvarnames[i][p]; - tmpname[p] = 0; - cvar = Cvar_FindVar(tmpname); - if (!cvar) - continue; - cvar->flags |= CVAR_SHADERSYSTEM; - for (p = 0; p < PERMUTATIONS; p++) - { - if (!prog->permu[p].handle.hlsl.vert || !prog->permu[p].handle.hlsl.frag) - continue; - uniformloc = D3D9Shader_FindUniform(&prog->permu[p].handle, 1, va("cvar_%s", tmpname)); - if (uniformloc != -1) - { - vec4_t v = {cvar->value, 0, 0, 0}; - IDirect3DDevice9_SetVertexShader(pD3DDev9, prog->permu[p].handle.hlsl.vert); - IDirect3DDevice9_SetVertexShaderConstantF(pD3DDev9, 0, v, 1); - } - uniformloc = D3D9Shader_FindUniform(&prog->permu[p].handle, 2, va("cvar_%s", tmpname)); - if (uniformloc != -1) - { - vec4_t v = {cvar->value, 0, 0, 0}; - IDirect3DDevice9_SetPixelShader(pD3DDev9, prog->permu[p].handle.hlsl.frag); - IDirect3DDevice9_SetPixelShaderConstantF(pD3DDev9, 0, v, 1); - } - } - } - for (i = 0; shader_unif_names[i].name; i++) - { - found = false; - for (p = 0; p < PERMUTATIONS; p++) - { - uniformloc = D3D9Shader_FindUniform(&prog->permu[p].handle, 0, shader_unif_names[i].name); - if (uniformloc != -1) - found = true; - prog->permu[p].parm[prog->numparams] = uniformloc; - } - if (found) - { - prog->parm[prog->numparams].type = shader_unif_names[i].ptype; - prog->numparams++; - } - } - /*set texture uniforms*/ - for (p = 0; p < PERMUTATIONS; p++) - { - for (i = 0; i < 8; i++) - { - uniformloc = D3D9Shader_FindUniform(&prog->permu[p].handle, 2, va("s_t%i", i)); - if (uniformloc != -1) - { - int v[4] = {i}; - IDirect3DDevice9_SetPixelShader(pD3DDev9, prog->permu[p].handle.hlsl.frag); - IDirect3DDevice9_SetPixelShaderConstantI(pD3DDev9, 0, v, 1); - } - } - } - IDirect3DDevice9_SetVertexShader(pD3DDev9, NULL); - IDirect3DDevice9_SetPixelShader(pD3DDev9, NULL); - } -#endif -} - static char *Shader_ParseBody(char *debugname, char **ptr) { char *body; @@ -1951,7 +1561,7 @@ static void Shader_SLProgramName (shader_t *shader, shaderpass_t *pass, char **p shader->prog = malloc(sizeof(*shader->prog)); memset(shader->prog, 0, sizeof(*shader->prog)); shader->prog->refs = 1; - if (!Shader_LoadPermutations(shader->name, shader->prog, programbody, qrtype, 0)) + if (!Shader_LoadPermutations(shader->name, shader->prog, programbody, qrtype, 0, NULL)) { free(shader->prog); shader->prog = NULL; @@ -3164,17 +2774,13 @@ void Shader_Free (shader_t *shader) int QDECL Shader_InitCallback (const char *name, qofs_t size, void *param, searchpathfuncs_t *spath) { - strcpy(shaderbuf+shaderbuflen, name); - Shader_MakeCache(shaderbuf+shaderbuflen); - shaderbuflen += strlen(name)+1; - + Shader_MakeCache(name); return true; } qboolean Shader_Init (void) { int wibuf[16]; - shaderbuflen = 0; if (!r_shaders) { @@ -3201,24 +2807,78 @@ qboolean Shader_Init (void) return true; } -static void Shader_MakeCache ( char *path ) +void Shader_FlushCache(void) { - unsigned int key, i; - char filename[MAX_QPATH]; - char *buf, *ptr, *token, *t; - shadercache_t *cache; - int size; + shadercachefile_t *sf; + shadercache_t *cache, *cache_next; + int i; - Com_sprintf( filename, sizeof(filename), "%s", path ); - Con_DPrintf ( "...loading '%s'\n", filename ); - - size = FS_LoadFile ( filename, (void **)&buf ); - if ( !buf || size <= 0 ) + for (i = 0; i < HASH_SIZE; i++) { + cache = shader_hash[i]; + + for (; cache; cache = cache_next) + { + cache_next = cache->hash_next; + cache->hash_next = NULL; + Z_Free(cache); + } + } + + while(shaderfiles) + { + sf = shaderfiles; + shaderfiles = sf->next; + if (sf->data) + FS_FreeFile(sf->data); + Z_Free(sf); + } +} + +static void Shader_MakeCache(const char *path) +{ + unsigned int key; + char *buf, *ptr, *token; + shadercache_t *cache; + shadercachefile_t *cachefile, *filelink = NULL; + qofs_t size; + + for (cachefile = shaderfiles; cachefile; cachefile = cachefile->next) + { + if (!Q_stricmp(cachefile->name, path)) + return; //already loaded. there's no source package or anything. + filelink = cachefile; + } + + + Con_DPrintf ("...loading '%s'\n", path); + + cachefile = Z_Malloc(sizeof(*cachefile) + strlen(path)); + strcpy(cachefile->name, path); + size = FS_LoadFile(path, (void **)&cachefile->data); + cachefile->length = size; + if (filelink) + filelink->next = cachefile; + else + shaderfiles = cachefile; + + if (qofs_Error(size)) + { + Con_Printf("Unable to read %s\n", path); + cachefile->length = 0; + return; + } + if (size > 1024*1024*64) //sanity limit + { + Con_Printf("Refusing to parse %s due to size\n", path); + cachefile->length = 0; + FS_FreeFile(cachefile->data); + cachefile->data = NULL; return; } - ptr = buf; + ptr = buf = cachefile->data; + size = cachefile->length; do { if ( ptr - buf >= size ) @@ -3230,9 +2890,7 @@ static void Shader_MakeCache ( char *path ) COM_CleanUpPath(token); - t = NULL; - Shader_GetPathAndOffset ( token, &t, &i ); - if (t) + if (Shader_LocateSource(token, NULL, NULL, NULL, NULL)) { ptr = Shader_Skip ( ptr ); continue; @@ -3240,17 +2898,41 @@ static void Shader_MakeCache ( char *path ) key = Hash_Key ( token, HASH_SIZE ); - cache = ( shadercache_t * )Z_Malloc ( sizeof(shadercache_t) ); + cache = ( shadercache_t * )Z_Malloc(sizeof(shadercache_t) + strlen(token)); + strcpy(cache->name, token); cache->hash_next = shader_hash[key]; - cache->path = path; - cache->offset = ptr - buf; - Com_sprintf ( cache->name, MAX_QPATH, "%s", token ); // warning: format not a string literal and no format arguments + cache->source = cachefile; + cache->offset = ptr - cachefile->data; + shader_hash[key] = cache; ptr = Shader_Skip ( ptr ); } while ( ptr ); +} - FS_FreeFile ( buf ); +static qboolean Shader_LocateSource(char *name, char **buf, size_t *bufsize, size_t *offset, enum shaderparsemode_e *parsemode) +{ + unsigned int key; + shadercache_t *cache; + + key = Hash_Key ( name, HASH_SIZE ); + cache = shader_hash[key]; + + for ( ; cache; cache = cache->hash_next ) + { + if ( !Q_stricmp (cache->name, name) ) + { + if (buf) + { + *buf = cache->source->data; + *bufsize = cache->source->length; + *offset = cache->offset; + *parsemode = cache->source->parsemode; + } + return true; + } + } + return false; } char *Shader_Skip ( char *ptr ) @@ -3288,27 +2970,6 @@ char *Shader_Skip ( char *ptr ) return ptr; } -static void Shader_GetPathAndOffset(char *name, char **path, unsigned int *offset) -{ - unsigned int key; - shadercache_t *cache; - - key = Hash_Key ( name, HASH_SIZE ); - cache = shader_hash[key]; - - for ( ; cache; cache = cache->hash_next ) - { - if ( !Q_stricmp (cache->name, name) ) - { - *path = cache->path; - *offset = cache->offset; - return; - } - } - - path = NULL; -} - void Shader_Reset(shader_t *s) { char name[MAX_QPATH]; @@ -3342,7 +3003,6 @@ void Shader_Shutdown (void) { int i; shader_t *shader; - shadercache_t *cache, *cache_next; if (!r_shaders) return; /*nothing needs freeing yet*/ @@ -3357,18 +3017,7 @@ void Shader_Shutdown (void) r_shaders[i] = NULL; } - for (i = 0; i < HASH_SIZE; i++) - { - cache = shader_hash[i]; - - for (; cache; cache = cache_next) - { - cache_next = cache->hash_next; - cache->hash_next = NULL; - Z_Free(cache); - } - } - + Shader_FlushCache(); Shader_FlushGenerics(); r_maxshaders = 0; @@ -3542,7 +3191,7 @@ void Shader_Readpass (shader_t *shader, char **ptr) { case ST_DIFFUSEMAP: if (pass->texgen == T_GEN_SINGLEMAP) - shader->defaulttextures.bump = pass->anim_frames[0]; + shader->defaulttextures.base = pass->anim_frames[0]; break; case ST_AMBIENT: break; @@ -3642,25 +3291,6 @@ static qboolean Shader_Parsetok (shader_t *shader, shaderpass_t *pass, shaderkey void Shader_SetPassFlush (shaderpass_t *pass, shaderpass_t *pass2) { - qboolean config_tex_env_combine; - qboolean config_nv_tex_env_combine4; - qboolean config_env_add; - -#ifdef GLQUAKE - if (qrenderer == QR_OPENGL) - { - config_tex_env_combine = gl_config.tex_env_combine; - config_nv_tex_env_combine4 = gl_config.nv_tex_env_combine4; - config_env_add = gl_config.env_add; - } - else -#endif - { - config_tex_env_combine = 1; - config_nv_tex_env_combine4 = 1; - config_env_add = 1; - } - if (((pass->flags & SHADER_PASS_DETAIL) && !r_detailtextures.value) || ((pass2->flags & SHADER_PASS_DETAIL) && !r_detailtextures.value) || (pass->flags & SHADER_PASS_VIDEOMAP) || (pass2->flags & SHADER_PASS_VIDEOMAP)) @@ -3703,15 +3333,15 @@ void Shader_SetPassFlush (shaderpass_t *pass, shaderpass_t *pass2) { if (pass->blendmode == PBM_REPLACE || pass->blendmode == PBM_REPLACELIGHT) { - if ((pass2->blendmode == PBM_DECAL && config_tex_env_combine) || - (pass2->blendmode == PBM_ADD && config_env_add) || - (pass2->blendmode && pass2->blendmode != PBM_ADD) || config_nv_tex_env_combine4) + if ((pass2->blendmode == PBM_DECAL && sh_config.tex_env_combine) || + (pass2->blendmode == PBM_ADD && sh_config.env_add) || + (pass2->blendmode && pass2->blendmode != PBM_ADD) || sh_config.nv_tex_env_combine4) { pass->numMergedPasses++; } } else if (pass->blendmode == PBM_ADD && - pass2->blendmode == PBM_ADD && config_env_add) + pass2->blendmode == PBM_ADD && sh_config.env_add) { pass->numMergedPasses++; } @@ -3724,13 +3354,13 @@ void Shader_SetPassFlush (shaderpass_t *pass, shaderpass_t *pass2) } else return; - if (pass->texgen == T_GEN_LIGHTMAP && pass->blendmode == PBM_REPLACELIGHT && pass2->blendmode == PBM_MODULATE && config_tex_env_combine) + if (pass->texgen == T_GEN_LIGHTMAP && pass->blendmode == PBM_REPLACELIGHT && pass2->blendmode == PBM_MODULATE && sh_config.tex_env_combine) { if (pass->rgbgen == RGB_GEN_IDENTITY) pass->rgbgen = RGB_GEN_IDENTITY_OVERBRIGHT; //get the light levels right pass2->blendmode = PBM_OVERBRIGHT; } - if (pass2->texgen == T_GEN_LIGHTMAP && pass2->blendmode == PBM_MODULATE && config_tex_env_combine) + if (pass2->texgen == T_GEN_LIGHTMAP && pass2->blendmode == PBM_MODULATE && sh_config.tex_env_combine) { if (pass->rgbgen == RGB_GEN_IDENTITY) pass->rgbgen = RGB_GEN_IDENTITY_OVERBRIGHT; //get the light levels right @@ -3739,82 +3369,88 @@ void Shader_SetPassFlush (shaderpass_t *pass, shaderpass_t *pass2) } } -void Shader_SetFeatures ( shader_t *s ) +const char *Shader_AlphaMaskProgArgs(shader_t *s) { + if (s->numpasses) + { + //alpha mask values ALWAYS come from the first pass. + shaderpass_t *pass = &s->passes[0]; + switch(pass->shaderbits & SBITS_ATEST_BITS) + { + default: + break; + //cases inverted. the test is to enable + case SBITS_ATEST_GT0: + return "#MASK=0.0#MASKOP=>"; + case SBITS_ATEST_LT128: + return "#MASK=0.5#MASKOP=<"; + case SBITS_ATEST_GE128: + return "#MASK=0.5"; + } + } + return ""; +} + +void Shader_Programify (shader_t *s) +{ + char *prog = NULL; + const char *mask; + enum + { + T_UNKNOWN, + T_WALL, + T_MODEL + } type = 0; int i; - qboolean trnormals; - shaderpass_t *pass; - - s->features = MF_NONE; - - for (i = 0, trnormals = true; i < s->numdeforms; i++) + shaderpass_t *pass, *lightmap = NULL, *modellighting = NULL; + for (i = 0; i < s->numpasses; i++) { - switch (s->deforms[i].type) - { - case DEFORMV_BULGE: - case DEFORMV_WAVE: - trnormals = false; - case DEFORMV_NORMAL: - s->features |= MF_NORMALS; - break; - case DEFORMV_MOVE: - break; - default: - trnormals = false; - break; - } + pass = &s->passes[i]; + if (pass->rgbgen == RGB_GEN_LIGHTING_DIFFUSE) + modellighting = pass; + else if (pass->texgen == T_GEN_LIGHTMAP && pass->tcgen == TC_GEN_LIGHTMAP) + lightmap = pass; } - if (trnormals) + if (modellighting) { - s->features |= MF_TRNORMALS; + pass = modellighting; + prog = "defaultskin"; + } + else if (lightmap) + { + pass = modellighting; + prog = "defaultwall"; + } + else + { + pass = NULL; + prog = "default2d"; + return; } - for (i = 0, pass = s->passes; i < s->numpasses; i++, pass++) + mask = Shader_AlphaMaskProgArgs(s); + + s->prog = Shader_FindGeneric(va("%s%s", prog, mask), qrenderer); + s->numpasses = 0; + s->passes[s->numpasses++].texgen = T_GEN_DIFFUSE; + + if (lightmap) { - switch (pass->rgbgen) - { - case RGB_GEN_LIGHTING_DIFFUSE: - s->features |= MF_NORMALS; - break; - case RGB_GEN_VERTEX_LIGHTING: - case RGB_GEN_ONE_MINUS_VERTEX: - case RGB_GEN_VERTEX_EXACT: - s->features |= MF_COLORS; - break; - default: - break; - } + s->passes[s->numpasses++].texgen = T_GEN_LIGHTMAP; + s->passes[s->numpasses++].texgen = T_GEN_NORMALMAP; + s->passes[s->numpasses++].texgen = T_GEN_DELUXMAP; + s->passes[s->numpasses++].texgen = T_GEN_FULLBRIGHT; + s->passes[s->numpasses++].texgen = T_GEN_SPECULAR; + } - switch ( pass->alphagen ) - { - case ALPHA_GEN_SPECULAR: - s->features |= MF_NORMALS; - break; - case ALPHA_GEN_VERTEX: - s->features |= MF_COLORS; - break; - default: - break; - } - - switch (pass->tcgen) - { - default: - s->features |= MF_STCOORDS; - break; - case TC_GEN_LIGHTMAP: - s->features |= MF_LMCOORDS; - break; - case TC_GEN_ENVIRONMENT: - case TC_GEN_NORMAL: - s->features |= MF_NORMALS; - break; - case TC_GEN_SVECTOR: - case TC_GEN_TVECTOR: - s->features |= MF_NORMALS; - break; - } + if (modellighting) + { + s->passes[s->numpasses++].texgen = T_GEN_LOWEROVERLAY; + s->passes[s->numpasses++].texgen = T_GEN_UPPEROVERLAY; + s->passes[s->numpasses++].texgen = T_GEN_FULLBRIGHT; + s->passes[s->numpasses++].texgen = T_GEN_NORMALMAP; + s->passes[s->numpasses++].texgen = T_GEN_SPECULAR; } } @@ -3989,14 +3625,52 @@ void Shader_Finish (shader_t *s) } done:; - for (pass = s->passes, i = 0; i < s->numpasses; i++, pass++) + //if we've no specular map, try and find whatever the q3 syntax said. hopefully it'll be compatible... + if (!TEXVALID(s->defaulttextures.specular)) { - if ((pass->texgen == T_GEN_ANIMMAP || pass->texgen == T_GEN_SINGLEMAP) && !TEXVALID(s->defaulttextures.base)) - s->defaulttextures.base = pass->anim_frames[0]; + for (pass = s->passes, i = 0; i < s->numpasses; i++, pass++) + { + if (pass->alphagen == ALPHA_GEN_SPECULAR) + if (pass->texgen == T_GEN_ANIMMAP || pass->texgen == T_GEN_SINGLEMAP) + s->defaulttextures.specular = pass->anim_frames[0]; + } + } + + if (!TEXVALID(s->defaulttextures.base)) + { + shaderpass_t *best = NULL; + int bestweight = 9999999; + int weight; + + for (pass = s->passes, i = 0; i < s->numpasses; i++, pass++) + { + weight = 0; + if (pass->flags & SHADER_PASS_DETAIL) + weight += 500; //prefer not to use a detail pass. these are generally useless. + if (pass->numtcmods || pass->tcgen != TC_GEN_BASE) + weight += 200; + if (pass->rgbgen != RGB_GEN_IDENTITY && pass->rgbgen != RGB_GEN_IDENTITY_OVERBRIGHT && pass->rgbgen != RGB_GEN_IDENTITY_LIGHTING) + weight += 100; + + if (pass->texgen != T_GEN_ANIMMAP && pass->texgen != T_GEN_SINGLEMAP && pass->texgen != T_GEN_VIDEOMAP) + weight += 1000; + + if (weight < bestweight) + { + bestweight = weight; + best = pass; + } + } + + if (best) + { + if (best->texgen == T_GEN_ANIMMAP || best->texgen == T_GEN_SINGLEMAP) + s->defaulttextures.base = best->anim_frames[0]; #ifndef NOMEDIA - if (pass->texgen == T_GEN_VIDEOMAP && pass->cin && !TEXVALID(s->defaulttextures.base)) - s->defaulttextures.base = Media_UpdateForShader(pass->cin); + else if (pass->texgen == T_GEN_VIDEOMAP && pass->cin) + s->defaulttextures.base = Media_UpdateForShader(best->cin); #endif + } } pass = s->passes; @@ -4107,18 +3781,33 @@ done:; s->flags &= ~SHADER_DEPTHWRITE; } + if (!s->bemoverrides[bemoverride_depthonly]) + { + const char *mask = Shader_AlphaMaskProgArgs(s); + if (*mask) + s->bemoverrides[bemoverride_depthonly] = R_RegisterShader(va("depthonly%s", mask), SUF_NONE, + "{\n" + "program depthonly\n" + "{\n" + "map $diffuse\n" + "depthwrite\n" + "maskcolor\n" + "}\n" + "}\n"); + } + + if (!s->prog && sh_config.progs_required) + Shader_Programify(s); + if (s->prog) { if (!s->numpasses) + { + s->passes[0].texgen = T_GEN_DIFFUSE; s->numpasses = 1; + } s->passes->numMergedPasses = s->numpasses; } - - Shader_SetFeatures(s); - -#ifdef FORCEGLSL - BE_GenerateProgram(s); -#endif } /* void Shader_UpdateRegistration (void) @@ -4239,7 +3928,7 @@ void R_BuildDefaultTexnums(texnums_t *tn, shader_t *shader) TEXASSIGN(shader->defaulttextures.fullbright, tn->fullbright); } -void Shader_DefaultScript(char *shortname, shader_t *s, const void *args) +void Shader_DefaultScript(const char *shortname, shader_t *s, const void *args) { const char *f = args; if (!args) @@ -4253,7 +3942,7 @@ void Shader_DefaultScript(char *shortname, shader_t *s, const void *args) } }; -void Shader_DefaultBSPLM(char *shortname, shader_t *s, const void *args) +void Shader_DefaultBSPLM(const char *shortname, shader_t *s, const void *args) { char *builtin = NULL; if (!builtin && r_drawflat.ival) @@ -4274,11 +3963,6 @@ void Shader_DefaultBSPLM(char *shortname, shader_t *s, const void *args) builtin = ( "{\n" "program defaultwall\n" - /*"param texture 0 tex_diffuse\n" - "param texture 1 tex_lightmap\n" - "param texture 2 tex_normalmap\n" - "param texture 3 tex_deluxmap\n" - "param texture 4 tex_fullbright\n"*/ "{\n" "map $diffuse\n" "}\n" @@ -4309,11 +3993,6 @@ void Shader_DefaultBSPLM(char *shortname, shader_t *s, const void *args) builtin = ( "{\n" "program defaultwall\n" - /*"param texture 0 tex_diffuse\n" - "param texture 1 tex_lightmap\n" - "param texture 2 tex_normalmap\n" - "param texture 3 tex_deluxmap\n" - "param texture 4 tex_fullbright\n"*/ "{\n" "map $diffuse\n" "}\n" @@ -4371,11 +4050,6 @@ void Shader_DefaultBSPLM(char *shortname, shader_t *s, const void *args) builtin = ( "{\n" "program defaultwall\n" - /*"param texture 0 tex_diffuse\n" - "param texture 1 tex_lightmap\n" - "param texture 2 tex_normalmap\n" - "param texture 3 tex_deluxmap\n" - "param texture 4 tex_fullbright\n"*/ "{\n" "map $diffuse\n" "}\n" @@ -4442,7 +4116,7 @@ void Shader_DefaultBSPLM(char *shortname, shader_t *s, const void *args) Shader_DefaultScript(shortname, s, builtin); } -void Shader_DefaultCinematic(char *shortname, shader_t *s, const void *args) +void Shader_DefaultCinematic(const char *shortname, shader_t *s, const void *args) { Shader_DefaultScript(shortname, s, va( @@ -4457,7 +4131,7 @@ void Shader_DefaultCinematic(char *shortname, shader_t *s, const void *args) } /*shortname should begin with 'skybox_'*/ -void Shader_DefaultSkybox(char *shortname, shader_t *s, const void *args) +void Shader_DefaultSkybox(const char *shortname, shader_t *s, const void *args) { Shader_DefaultScript(shortname, s, va( @@ -4468,7 +4142,7 @@ void Shader_DefaultSkybox(char *shortname, shader_t *s, const void *args) ); } -char *Shader_DefaultBSPWater(char *shortname) +char *Shader_DefaultBSPWater(const char *shortname) { int wstyle; @@ -4618,11 +4292,11 @@ char *Shader_DefaultBSPWater(char *shortname) } } -void Shader_DefaultWaterShader(char *shortname, shader_t *s, const void *args) +void Shader_DefaultWaterShader(const char *shortname, shader_t *s, const void *args) { Shader_DefaultScript(shortname, s, Shader_DefaultBSPWater(shortname)); } -void Shader_DefaultBSPQ2(char *shortname, shader_t *s, const void *args) +void Shader_DefaultBSPQ2(const char *shortname, shader_t *s, const void *args) { if (!strncmp(shortname, "sky/", 4)) { @@ -4651,7 +4325,7 @@ void Shader_DefaultBSPQ2(char *shortname, shader_t *s, const void *args) Shader_DefaultBSPLM(shortname, s, args); } -void Shader_DefaultBSPQ1(char *shortname, shader_t *s, const void *args) +void Shader_DefaultBSPQ1(const char *shortname, shader_t *s, const void *args) { char *builtin = NULL; if (r_mirroralpha.value < 1 && !strcmp(shortname, "window02_1")) @@ -4805,6 +4479,9 @@ void Shader_DefaultBSPVertex(char *shortname, shader_t *s, const void *args) s->defaulttextures.base = R_LoadHiResTexture(va("%s_d.tga", shortname), NULL, 0); + if (Shader_ParseShader("defaultflare", s)) + return; + pass = &s->passes[0]; pass->tcgen = TC_GEN_BASE; pass->shaderbits |= SBITS_MISC_DEPTHWRITE; @@ -4830,13 +4507,15 @@ void Shader_DefaultBSPVertex(char *shortname, shader_t *s, const void *args) s->numpasses = 1; s->numdeforms = 0; s->flags = SHADER_DEPTHWRITE|SHADER_CULL_FRONT; - s->features = MF_STCOORDS|MF_COLORS; s->sort = SHADER_SORT_OPAQUE; s->uses = 1; } void Shader_DefaultBSPFlare(char *shortname, shader_t *s, const void *args) { shaderpass_t *pass; + if (Shader_ParseShader("defaultflare", s)) + return; + pass = &s->passes[0]; pass->flags = SHADER_PASS_NOCOLORARRAY; pass->shaderbits |= SBITS_SRCBLEND_ONE|SBITS_DSTBLEND_ONE; @@ -4857,7 +4536,6 @@ void Shader_DefaultBSPFlare(char *shortname, shader_t *s, const void *args) s->numpasses = 1; s->numdeforms = 0; s->flags = SHADER_FLARE; - s->features = MF_STCOORDS|MF_COLORS; s->sort = SHADER_SORT_ADDITIVE; s->uses = 1; @@ -4865,6 +4543,9 @@ void Shader_DefaultBSPFlare(char *shortname, shader_t *s, const void *args) } void Shader_DefaultSkin(char *shortname, shader_t *s, const void *args) { + if (Shader_ParseShader("defaultskin", s)) + return; + Shader_DefaultScript(shortname, s, "{\n" "if $lpp\n" @@ -4872,6 +4553,9 @@ void Shader_DefaultSkin(char *shortname, shader_t *s, const void *args) "else\n" "program defaultskin\n" "endif\n" + "if gl_affinemodels\n" + "affine\n" + "endif\n" "{\n" "map $diffuse\n" "rgbgen lightingDiffuse\n" @@ -4903,8 +4587,11 @@ void Shader_DefaultSkin(char *shortname, shader_t *s, const void *args) s->flags |= SHADER_NOIMAGE; } -void Shader_DefaultSkinShell(char *shortname, shader_t *s, const void *args) +void Shader_DefaultSkinShell(const char *shortname, shader_t *s, const void *args) { + if (Shader_ParseShader("defaultskinshell", s)) + return; + Shader_DefaultScript(shortname, s, "{\n" "sort blend\n" @@ -4920,11 +4607,14 @@ void Shader_DefaultSkinShell(char *shortname, shader_t *s, const void *args) } void Shader_Default2D(char *shortname, shader_t *s, const void *genargs) { + if (Shader_ParseShader("default2d", s)) + return; Shader_DefaultScript(shortname, s, "{\n" "if $nofixed\n" "program default2d\n" "endif\n" + "affine\n" "nomipmaps\n" "{\n" "clampmap $diffuse\n" @@ -5055,7 +4745,9 @@ static void Shader_ReadShader(shader_t *s, char *shadersource, int parsemode) int conddepth = 0; int cond[8]; cond[0] = 0; - shaderparsemode = parsemode; + + memset(&parsestate, 0, sizeof(parsestate)); + parsestate.mode = parsemode; // set defaults s->flags = SHADER_CULL_FRONT; @@ -5073,51 +4765,36 @@ static void Shader_ReadShader(shader_t *s, char *shadersource, int parsemode) Shader_Finish ( s ); } -static qboolean Shader_ParseShader(char *shortname, char *usename, shader_t *s) +static qboolean Shader_ParseShader(char *parsename, shader_t *s) { - unsigned int offset = 0, length; - char path[MAX_QPATH]; - char *buf = NULL, *ts = NULL; - int parsemode = SPM_DEFAULT; + size_t offset = 0, length; + char *buf = NULL; + enum shaderparsemode_e parsemode = SPM_DEFAULT; - Shader_GetPathAndOffset( shortname, &ts, &offset ); - - if ( ts ) + if (Shader_LocateSource(parsename, &buf, &length, &offset, &parsemode)) { - if (!strcmp(COM_FileExtension(ts), "mtr")) - parsemode = SPM_DOOM3; - - Com_sprintf ( path, sizeof(path), "%s", ts ); - length = FS_LoadFile ( path, (void **)&buf ); - } - else - length = 0; - - // the shader is in the shader scripts - if ( ts && buf && (offset < length) ) - { - char *file, *token; - - - file = buf + offset; - token = COM_ParseExt (&file, true, true); - if ( !file || token[0] != '{' ) + // the shader is in the shader scripts + if (buf && offset < length ) { - FS_FreeFile(buf); - return false; + char *file, *token; + + + file = buf + offset; + token = COM_ParseExt (&file, true, true); + if ( !file || token[0] != '{' ) + { + FS_FreeFile(buf); + return false; + } + + Shader_Reset(s); + + Shader_ReadShader(s, file, parsemode); + + return true; } - - Shader_Reset(s); - - Shader_ReadShader(s, file, parsemode); - - FS_FreeFile(buf); - return true; } - if (buf) - FS_FreeFile(buf); - return false; } void R_UnloadShader(shader_t *shader) @@ -5134,7 +4811,7 @@ static shader_t *R_LoadShader (const char *name, unsigned int usageflags, shader shader_t *s; if (!*name) - name = "gfx/white"; + name = "gfx/unspecified"; Q_strncpyz(cleanname, name, sizeof(cleanname)); COM_CleanUpPath(cleanname); @@ -5215,55 +4892,14 @@ static shader_t *R_LoadShader (const char *name, unsigned int usageflags, shader if (ruleset_allow_shaders.ival) { -#ifdef GLQUAKE - if (qrenderer == QR_OPENGL) + if (sh_config.shadernamefmt) { - if (gl_config.gles && gl_config.glversion >= 2) + if (Shader_ParseShader(va(sh_config.shadernamefmt, shortname), s)) { - if (Shader_ParseShader(va("%s_gles2", shortname), shortname, s)) - { - return s; - } - } - if (gl_config.glversion >= 3) - { - if (Shader_ParseShader(va("%s_glsl3", shortname), shortname, s)) - { - return s; - } - } - if (gl_config.arb_shader_objects) - { - if (Shader_ParseShader(va("%s_glsl", shortname), shortname, s)) - { - return s; - } + return s; } } -#endif -#ifdef D3D9QUAKE - if (qrenderer == QR_DIRECT3D9) - { - { - if (Shader_ParseShader(va("%s_hlsl", shortname), shortname, s)) - { - return s; - } - } - } -#endif -#ifdef D3D11QUAKE - if (qrenderer == QR_DIRECT3D11) - { - { - if (Shader_ParseShader(va("%s_hlsl11", shortname), shortname, s)) - { - return s; - } - } - } -#endif - if (Shader_ParseShader(shortname, shortname, s)) + if (Shader_ParseShader(shortname, s)) { return s; } @@ -5326,19 +4962,10 @@ void Shader_DoReload(void) strcpy(shortname, s->name); if (ruleset_allow_shaders.ival) { -#ifdef GLQUAKE - if (qrenderer == QR_OPENGL && gl_config.arb_shader_objects) - { - if (Shader_ParseShader(va("%s_glsl", shortname), shortname, s)) - { - continue; - } - } -#endif - if (Shader_ParseShader(shortname, shortname, s)) - { + if (sh_config.shadernamefmt && Shader_ParseShader(va(sh_config.shadernamefmt, shortname), s)) + continue; + if (Shader_ParseShader(shortname, s)) continue; - } } if (s->generator) { @@ -5379,7 +5006,7 @@ cin_t *R_ShaderGetCinematic(shader_t *s) return NULL; } -cin_t *R_ShaderFindCinematic(char *name) +cin_t *R_ShaderFindCinematic(const char *name) { #ifdef NOMEDIA return NULL; @@ -5481,7 +5108,7 @@ void Shader_RemapShader_f(void) R_RemapShader(sourcename, destname, timeoffset); } -shader_t *R_RegisterPic (char *name) +shader_t *R_RegisterPic (const char *name) { shader_t *shader; @@ -5505,30 +5132,35 @@ shader_t *R_RegisterPic (char *name) return shader; } -shader_t *R_RegisterShader (char *name, unsigned int usageflags, const char *shaderscript) +shader_t *R_RegisterShader (const char *name, unsigned int usageflags, const char *shaderscript) { return R_LoadShader (name, usageflags, Shader_DefaultScript, shaderscript); } -shader_t *R_RegisterShader_Lightmap (char *name) +shader_t *R_RegisterShader_Lightmap (const char *name) { return R_LoadShader (name, SUF_LIGHTMAP, Shader_DefaultBSPLM, NULL); } -shader_t *R_RegisterShader_Vertex (char *name) +shader_t *R_RegisterShader_Vertex (const char *name) { return R_LoadShader (name, 0, Shader_DefaultBSPVertex, NULL); } -shader_t *R_RegisterShader_Flare (char *name) +shader_t *R_RegisterShader_Flare (const char *name) { return R_LoadShader (name, 0, Shader_DefaultBSPFlare, NULL); } -shader_t *R_RegisterSkin (char *shadername, char *modname) +shader_t *R_RegisterSkin (const char *shadername, const char *modname) { shader_t *shader; - if (modname && !strchr(shadername, '/')) +#ifdef _DEBUG + if (shadername == com_token) + Con_Printf("R_RegisterSkin was passed com_token. that will bug out.\n"); +#endif + + if (modname && !strchr(shadername, '/') && *shadername) { char newsname[MAX_QPATH]; char *b = COM_SkipPath(modname); @@ -5549,7 +5181,7 @@ shader_t *R_RegisterSkin (char *shadername, char *modname) shader = R_LoadShader (shadername, 0, Shader_DefaultSkin, NULL); return shader; } -shader_t *R_RegisterCustom (char *name, unsigned int usageflags, shader_gen_t *defaultgen, const void *args) +shader_t *R_RegisterCustom (const char *name, unsigned int usageflags, shader_gen_t *defaultgen, const void *args) { return R_LoadShader (name, usageflags, defaultgen, args); } diff --git a/engine/gl/gl_shadow.c b/engine/gl/gl_shadow.c index c34755dc..d5ab7101 100644 --- a/engine/gl/gl_shadow.c +++ b/engine/gl/gl_shadow.c @@ -512,7 +512,7 @@ static void SHM_BeginShadowMesh(dlight_t *dl, int type) } static struct shadowmesh_s *SHM_FinishShadowMesh(dlight_t *dl) { - if (sh_shmesh != &sh_tempshmesh) + if (sh_shmesh != &sh_tempshmesh || 1) { switch (qrenderer) { @@ -521,8 +521,8 @@ static struct shadowmesh_s *SHM_FinishShadowMesh(dlight_t *dl) default: break; - case QR_OPENGL: #ifdef GLQUAKE + case QR_OPENGL: if (!qglGenBuffersARB) return sh_shmesh; qglGenBuffersARB(2, sh_shmesh->vebo); @@ -533,10 +533,10 @@ static struct shadowmesh_s *SHM_FinishShadowMesh(dlight_t *dl) GL_SelectEBO(sh_shmesh->vebo[1]); qglBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, sizeof(*sh_shmesh->indicies) * sh_shmesh->numindicies, sh_shmesh->indicies, GL_STATIC_DRAW_ARB); -#endif break; - case QR_DIRECT3D9: +#endif #ifdef D3D9QUAKE + case QR_DIRECT3D9: if (sh_shmesh->numindicies && sh_shmesh->numverts) { void *map; @@ -551,8 +551,8 @@ static struct shadowmesh_s *SHM_FinishShadowMesh(dlight_t *dl) IDirect3DVertexBuffer9_Unlock(sh_shmesh->d3d9_vbuffer); } -#endif break; +#endif #ifdef D3D11QUAKE case QR_DIRECT3D11: D3D11BE_GenerateShadowBuffer(&sh_shmesh->d3d11_vbuffer, sh_shmesh->verts, sh_shmesh->numverts, &sh_shmesh->d3d11_ibuffer, sh_shmesh->indicies, sh_shmesh->numindicies); @@ -1420,22 +1420,8 @@ static struct shadowmesh_s *SHM_BuildShadowMesh(dlight_t *dl, unsigned char *lvi { SHM_BeginShadowMesh(dl, type); -#if 0 - { - int i; - msurface_t *surf; - for (i = 0; i < cl.worldmodel->numsurfaces; i+=2) - { - surf = &cl.worldmodel->surfaces[i]; - SHM_Shadow_Cache_Surface(surf); - SHM_MeshFrontOnly(surf->mesh->numvertexes, surf->mesh->xyz_array, surf->mesh->numindexes, surf->mesh->indexes); - } - memset(sh_shmesh->litleaves, 0xff, sh_shmesh->leafbytes); - } -#else SHM_MarkLeavesQ1(dl, lvis); SHM_RecursiveWorldNodeQ1_r(dl, cl.worldmodel->nodes); -#endif } break; #ifdef Q2BSPS @@ -1450,24 +1436,10 @@ static struct shadowmesh_s *SHM_BuildShadowMesh(dlight_t *dl, unsigned char *lvi /*q3 doesn't have edge info*/ SHM_BeginShadowMesh(dl, type); -#if 0 - { - int i; - msurface_t *surf; - for (i = 0; i < cl.worldmodel->numsurfaces; i++) - { - surf = &cl.worldmodel->surfaces[i]; - SHM_Shadow_Cache_Surface(surf); - SHM_MeshFrontOnly(surf->mesh->numvertexes, surf->mesh->xyz_array, surf->mesh->numindexes, surf->mesh->indexes); - } - memset(sh_shmesh->litleaves, 0xff, sh_shmesh->leafbytes); - } -#else sh_shadowframe++; SHM_RecursiveWorldNodeQ3_r(dl, cl.worldmodel->nodes); if (type == SMT_STENCILVOLUME) SHM_ComposeVolume_BruteForce(dl); -#endif break; #endif default: @@ -2298,7 +2270,7 @@ static void Sh_GenShadowFace(dlight_t *l, shadowmesh_t *smesh, int face, int sms */ } -void D3D11_BeginShadowMap(int id, int w, int h); +qboolean D3D11_BeginShadowMap(int id, int w, int h); void D3D11_EndShadowMap(void); void D3D11BE_SetupForShadowMap(dlight_t *dl, qboolean isspot, int texwidth, int texheight, float shadowscale); @@ -2430,7 +2402,8 @@ qboolean Sh_GenShadowMap (dlight_t *l, qbyte *lvis, int smsize) #endif #ifdef D3D11QUAKE case QR_DIRECT3D11: - D3D11_BeginShadowMap(isspot, (isspot?SHADOWMAP_SIZE:(SHADOWMAP_SIZE*3)), (isspot?SHADOWMAP_SIZE:(SHADOWMAP_SIZE*2))); + if (!D3D11_BeginShadowMap(isspot, (isspot?SHADOWMAP_SIZE:(SHADOWMAP_SIZE*3)), (isspot?SHADOWMAP_SIZE:(SHADOWMAP_SIZE*2)))) + return false; // BE_Scissor(&rect); break; @@ -3311,15 +3284,18 @@ void Com_ParseVector(char *str, vec3_t out) void Sh_CheckSettings(void) { - qboolean canstencil = false, cansmap = false; + qboolean canstencil = false, cansmap = false, canshadowless = false; r_shadow_shadowmapping.ival = r_shadow_shadowmapping.value; r_shadow_realtime_world.ival = r_shadow_realtime_world.value; r_shadow_realtime_dlight.ival = r_shadow_realtime_dlight.value; + r_shadow_realtime_world_shadows.ival = r_shadow_realtime_world_shadows.value; + r_shadow_realtime_dlight_shadows.ival = r_shadow_realtime_dlight_shadows.value; switch(qrenderer) { #ifdef GLQUAKE case QR_OPENGL: + canshadowless = true; //falls back to crappy texture env if (gl_config.arb_shader_objects && gl_config.ext_framebuffer_objects && gl_config.arb_shadow) cansmap = true; if (gl_stencilbits) @@ -3329,6 +3305,7 @@ void Sh_CheckSettings(void) #ifdef D3D9QUAKE case QR_DIRECT3D9: #ifndef GLQUAKE + canshadowless = true; //the code still has a lot of ifdefs, so will crash if you try it in a merged build. //its not really usable in d3d-only builds either, so no great loss. canstencil = true; @@ -3337,7 +3314,9 @@ void Sh_CheckSettings(void) #endif #ifdef D3D11QUAKE case QR_DIRECT3D11: - cansmap = true; + canshadowless = true; //all feature levels + if (D3D11_BeginShadowMap(0, SHADOWMAP_SIZE*3, SHADOWMAP_SIZE*2)) + cansmap = true; //tends to not work properly until feature level 10 for one error or another. break; #endif default: @@ -3346,19 +3325,28 @@ void Sh_CheckSettings(void) return; } - if (!canstencil && !cansmap) + if (!canstencil && !cansmap && !canshadowless) { //no shadow methods available at all. - Con_Printf("Missing GL extensions: realtime lighting is not possible.\n"); + if (r_shadow_realtime_world.ival || r_shadow_realtime_dlight.ival) + Con_Printf("Missing driver extensions: realtime lighting is not possible.\n"); r_shadow_realtime_world.ival = 0; r_shadow_realtime_dlight.ival = 0; } + else if (!canstencil && !cansmap) + { + //no shadow methods available at all. + if ((r_shadow_realtime_world.ival&&r_shadow_realtime_world_shadows.ival)||(r_shadow_realtime_dlight.ival&&r_shadow_realtime_dlight_shadows.ival)) + Con_Printf("Missing driver extensions: realtime shadows are not possible.\n"); + r_shadow_realtime_world_shadows.ival = 0; + r_shadow_realtime_dlight_shadows.ival = 0; + } else if (!canstencil || !cansmap) { //only one shadow method if (!!r_shadow_shadowmapping.ival != cansmap) { - Con_Printf("Missing GL extensions: forcing shadowmapping %s.\n", cansmap?"on":"off"); + Con_Printf("Missing driver extensions: forcing shadowmapping %s.\n", cansmap?"on":"off"); r_shadow_shadowmapping.ival = cansmap; } } diff --git a/engine/gl/gl_vidcommon.c b/engine/gl/gl_vidcommon.c index a38cbfc6..960d6aad 100644 --- a/engine/gl/gl_vidcommon.c +++ b/engine/gl/gl_vidcommon.c @@ -58,6 +58,11 @@ void (APIENTRY *qglStencilOpSeparateATI) (GLenum face, GLenum fail, GLenum zfail void (APIENTRY *qglGetFramebufferAttachmentParameteriv)(GLenum target, GLenum attachment, GLenum pname, GLint * params); void (APIENTRY *qglGetVertexAttribPointerv) (GLuint index, GLenum pname, GLvoid* *pointer); +//GL_OES_get_program_binary +void (APIENTRY *qglGetProgramBinary)(GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, GLvoid *binary); +void (APIENTRY *qglProgramBinary)(GLuint program, GLenum binaryFormat, const GLvoid *binary, GLint length); +#define GL_PROGRAM_BINARY_LENGTH 0x8741 + //quick hack that made quake work on both 1+ext and 1.1 gl implementations. BINDTEXFUNCPTR qglBindTexture; @@ -185,7 +190,6 @@ GLenum (APIENTRY *qglCheckFramebufferStatusEXT)(GLenum target); void (APIENTRY *qglDepthBoundsEXT) (GLclampd zmin, GLclampd zmax); /* PFNGLPROGRAMSTRINGARBPROC qglProgramStringARB; -PFNGLGETPROGRAMIVARBPROC qglGetProgramivARB; PFNGLBINDPROGRAMARBPROC qglBindProgramARB; PFNGLGENPROGRAMSARBPROC qglGenProgramsARB; */ @@ -890,6 +894,25 @@ void GL_CheckExtensions (void *(*getglfunction) (char *name)) Con_DPrintf("GLSL available\n"); } #endif + + qglGetProgramBinary = NULL; + qglProgramBinary = NULL; + if (gl_config.arb_shader_objects) + { + if (gl_config.glversion >= 4.1 || GL_CheckExtension("GL_ARB_get_program_binary")) + { + qglGetProgramBinary = (void *)getglext("glGetProgramBinary"); + qglProgramBinary = (void *)getglext("glProgramBinary"); + } + else if (GL_CheckExtension("GL_OES_get_program_binary")) + { + //no PROGRAM_BINARY_RETRIEVABLE_HINT + qglGetProgramBinary = (void *)getglext("glGetProgramBinaryOES"); + qglProgramBinary = (void *)getglext("glProgramBinaryOES"); + } + } + + //we only use vao if we don't have a choice. //certain drivers (*cough* mesa *cough*) update vao0 state even when a different vao is bound. //they also don't support client arrays, so are unusable without glsl or vertex streaming (which is *really* hard to optimise for - especially with webgl etc) @@ -997,7 +1020,7 @@ static const char *glsl_hdrs[] = "#ifdef SKELETAL\n" "attribute vec4 v_bone;" "attribute vec4 v_weight;" - "uniform mat3x4 m_bones["STRINGIFY(MAX_BONES)"];\n" + "uniform mat3x4 m_bones["STRINGIFY(MAX_GPU_BONES)"];\n" "vec4 skeletaltransform()" "{" @@ -1070,25 +1093,27 @@ static const char *glsl_hdrs[] = "sys/fog.h", "#ifdef FRAGMENT_SHADER\n" "#ifdef FOG\n" - "uniform vec4 w_fog;\n" + "uniform vec4 w_fog[2];\n" "vec3 fog3(in vec3 regularcolour)" "{" - "float z = w_fog.w * gl_FragCoord.z / gl_FragCoord.w;\n" + "float z = w_fog[1].x * gl_FragCoord.z / gl_FragCoord.w;\n" + "z = max(0.0,z-w_fog[1].y);\n" "#if #include \"cvar/r_fog_exp2\"\n" "z *= z;\n" "#endif\n" "float fac = exp2(-(z * 1.442695));\n" - "fac = clamp(fac, 0.0, 1.0);\n" - "return mix(w_fog.rgb, regularcolour, fac);\n" + "fac = (1.0-w_fog[0].a) + (clamp(fac, 0.0, 1.0)*w_fog[0].a);\n" + "return mix(w_fog[0].rgb, regularcolour, fac);\n" "}\n" "vec3 fog3additive(in vec3 regularcolour)" "{" - "float z = w_fog.w * gl_FragCoord.z / gl_FragCoord.w;\n" + "float z = w_fog[1].x * gl_FragCoord.z / gl_FragCoord.w;\n" + "z = max(0.0,z-w_fog[1].y);\n" "#if #include \"cvar/r_fog_exp2\"\n" "z *= z;\n" "#endif\n" "float fac = exp2(-(z * 1.442695));\n" - "fac = clamp(fac, 0.0, 1.0);\n" + "fac = (1.0-w_fog[0].a) + (clamp(fac, 0.0, 1.0)*w_fog[0].a);\n" "return regularcolour * fac;\n" "}\n" "vec4 fog4(in vec4 regularcolour)" @@ -1097,22 +1122,24 @@ static const char *glsl_hdrs[] = "}\n" "vec4 fog4additive(in vec4 regularcolour)" "{" - "float z = w_fog.w * gl_FragCoord.z / gl_FragCoord.w;\n" + "float z = w_fog[1].x * gl_FragCoord.z / gl_FragCoord.w;\n" + "z = max(0.0,z-w_fog[1].y);\n" "#if #include \"cvar/r_fog_exp2\"\n" "z *= z;\n" "#endif\n" "float fac = exp2(-(z * 1.442695));\n" - "fac = clamp(fac, 0.0, 1.0);\n" + "fac = (1.0-w_fog[0].a) + (clamp(fac, 0.0, 1.0)*w_fog[0].a);\n" "return regularcolour * vec4(fac, fac, fac, 1.0);\n" "}\n" "vec4 fog4blend(in vec4 regularcolour)" "{" - "float z = w_fog.w * gl_FragCoord.z / gl_FragCoord.w;\n" + "float z = w_fog[1].x * gl_FragCoord.z / gl_FragCoord.w;\n" + "z = max(0.0,z-w_fog[1].y);\n" "#if #include \"cvar/r_fog_exp2\"\n" "z *= z;\n" "#endif\n" "float fac = exp2(-(z * 1.442695));\n" - "fac = clamp(fac, 0.0, 1.0);\n" + "fac = (1.0-w_fog[0].a) + (clamp(fac, 0.0, 1.0)*w_fog[0].a);\n" "return regularcolour * vec4(1.0, 1.0, 1.0, fac);\n" "}\n" "#else\n" @@ -1338,7 +1365,7 @@ qboolean GLSlang_GenerateIncludes(int maxstrings, int *strings, const GLchar *pr // glslang helper api function definitions // type should be GL_FRAGMENT_SHADER_ARB or GL_VERTEX_SHADER_ARB -GLhandleARB GLSlang_CreateShader (char *name, int ver, char **precompilerconstants, const char *shadersource, GLenum shadertype, qboolean silent) +GLhandleARB GLSlang_CreateShader (const char *name, int ver, const char **precompilerconstants, const char *shadersource, GLenum shadertype, qboolean silent) { GLhandleARB shader; GLint compiled; @@ -1538,7 +1565,7 @@ GLhandleARB GLSlang_CreateShader (char *name, int ver, char **precompilerconstan return shader; } -GLhandleARB GLSlang_CreateProgramObject (char *name, GLhandleARB vert, GLhandleARB frag, qboolean silent) +GLhandleARB GLSlang_CreateProgramObject (const char *name, GLhandleARB vert, GLhandleARB frag, qboolean silent) { GLhandleARB program; GLint linked; @@ -1588,7 +1615,7 @@ GLhandleARB GLSlang_CreateProgramObject (char *name, GLhandleARB vert, GLhandleA return program; } -GLhandleARB GLSlang_CreateProgram(char *name, int ver, char **precompilerconstants, char *vert, char *frag, qboolean silent) +GLhandleARB GLSlang_CreateProgram(const char *name, int ver, const char **precompilerconstants, const char *vert, const char *frag, qboolean silent, vfsfile_t *blobfile) { GLhandleARB handle; GLhandleARB vs; @@ -1614,9 +1641,40 @@ GLhandleARB GLSlang_CreateProgram(char *name, int ver, char **precompilerconstan checkglerror(); + if (handle && blobfile && qglGetProgramBinary) + { + GLuint ui; + GLenum e; + unsigned int len, fmt; + void *blobdata; + + qglGetProgramParameteriv_(handle, GL_PROGRAM_BINARY_LENGTH, &ui); + len = ui; + + blobdata = BZ_Malloc(len); + qglGetProgramBinary(handle, len, NULL, &e, blobdata); + fmt = e; + + VFS_WRITE(blobfile, &fmt, sizeof(fmt)); + VFS_WRITE(blobfile, &len, sizeof(len)); + VFS_WRITE(blobfile, blobdata, len); + BZ_Free(blobdata); + } + return handle; } +qboolean GLSlang_CreateProgramPermu(program_t *prog, const char *name, unsigned int permu, const char **precompilerconstants, const char *vert, const char *tcs, const char *tes, const char *frag, qboolean noerrors, vfsfile_t *blobfile) +{ + int ver = gl_config.gles?100:110; + if (permu & PERMUTATION_SKELETAL) + ver = 120; + prog->permu[permu].handle.glsl = GLSlang_CreateProgram(name, ver, precompilerconstants, vert, frag, noerrors, blobfile); + if (prog->permu[permu].handle.glsl) + return true; + return false; +} + GLint GLSlang_GetUniformLocation (int prog, char *name) { int i = qglGetUniformLocationARB(prog, name); @@ -1627,6 +1685,154 @@ GLint GLSlang_GetUniformLocation (int prog, char *name) return i; } +static qboolean GLSlang_LoadBlob(program_t *prog, const char *name, unsigned int permu, vfsfile_t *blobfile) +{ + unsigned int fmt; + unsigned int length; + void *binary; + GLint success; + if (!qglProgramBinary) + return false; + VFS_READ(blobfile, &fmt, sizeof(fmt)); + VFS_READ(blobfile, &length, sizeof(length)); + binary = BZ_Malloc(length); + VFS_READ(blobfile, binary, length); + + prog->permu[permu].handle.glsl = qglCreateProgramObjectARB(); + qglProgramBinary(prog->permu[permu].handle.glsl, fmt, binary, length); + BZ_Free(binary); + qglGetProgramParameteriv_(prog->permu[permu].handle.glsl, GL_OBJECT_LINK_STATUS_ARB, &success); + + if (!success) + { + qglDeleteProgramObject_(prog->permu[permu].handle.glsl); + prog->permu[permu].handle.glsl = 0; + } + return !!success; +} + +static void GLSlang_DeleteProg(program_t *prog, unsigned int permu) +{ + if (prog->permu[permu].handle.glsl) + { + qglDeleteProgramObject_(prog->permu[permu].handle.glsl); + prog->permu[permu].handle.glsl = 0; + } +} + +static void GLSlang_ProgAutoFields(program_t *prog, char **cvarnames, int *cvartypes) +{ + unsigned int i, p; + qboolean found; + int uniformloc; + char tmpname[128]; + cvar_t *cvar; + + prog->numparams = 0; + + //figure out visible attributes + for (p = 0; p < PERMUTATIONS; p++) + { + if (!prog->permu[p].handle.glsl) + continue; + GLSlang_UseProgram(prog->permu[p].handle.glsl); + for (i = 0; shader_attr_names[i].name; i++) + { + uniformloc = qglGetAttribLocationARB(prog->permu[p].handle.glsl, shader_attr_names[i].name); + if (uniformloc != -1) + { + if (shader_attr_names[i].ptype != uniformloc) + Con_Printf("Bad attribute: %s\n", shader_attr_names[i].name); + else + prog->permu[p].attrmask |= 1u<permu[p].handle.glsl) + continue; + GLSlang_UseProgram(prog->permu[p].handle.glsl); + + uniformloc = qglGetUniformLocationARB(prog->permu[p].handle.glsl, shader_unif_names[i].name); + if (uniformloc != -1) + found = true; + + if (prog->numparams == SHADER_PROGPARMS_MAX) + { + if (found) + break; + } + else + prog->permu[p].parm[prog->numparams] = uniformloc; + } + if (found) + { + if (prog->numparams == SHADER_PROGPARMS_MAX) + Con_Printf("Too many paramters for program (ignoring %s)\n", shader_unif_names[i].name); + else + { + prog->parm[prog->numparams].type = shader_unif_names[i].ptype; + prog->numparams++; + } + } + } + /*set cvar unirforms*/ + for (i = 0; cvarnames[i]; i++) + { + if (prog->numparams == SHADER_PROGPARMS_MAX) + { + Con_Printf("Too many cvar paramters for program\n"); + break; + } + for (p = 0; cvarnames[i][p] && (unsigned char)cvarnames[i][p] > 32 && p < sizeof(tmpname)-1; p++) + tmpname[p] = cvarnames[i][p]; + tmpname[p] = 0; + cvar = Cvar_Get(tmpname, "0", CVAR_SHADERSYSTEM, "glsl cvars"); + if (!cvar) + continue; + cvar->flags |= CVAR_SHADERSYSTEM; + prog->parm[prog->numparams].type = cvartypes[i]; + prog->parm[prog->numparams].pval = cvar; + found = false; + for (p = 0; p < PERMUTATIONS; p++) + { + if (!prog->permu[p].handle.glsl) + continue; + GL_SelectProgram(prog->permu[p].handle.glsl); + uniformloc = qglGetUniformLocationARB(prog->permu[p].handle.glsl, va("cvar_%s", tmpname)); + if (uniformloc != -1) + { + //qglUniform1fARB(uniformloc, cvar->value); + found = true; + } + prog->permu[p].parm[prog->numparams] = uniformloc; + } + if (found) + prog->numparams++; + } + /*set texture uniforms*/ + for (p = 0; p < PERMUTATIONS; p++) + { + if (!prog->permu[p].handle.glsl) + continue; + if (!(prog->permu[p].attrmask & (1u<permu[p].attrmask |= (1u<permu[p].handle.glsl); + for (i = 0; i < 8; i++) + { + uniformloc = qglGetUniformLocationARB(prog->permu[p].handle.glsl, va("s_t%i", i)); + if (uniformloc != -1) + qglUniform1iARB(uniformloc, i); + } + } +} + //the vid routines have initialised a window, and now they are giving us a reference to some of of GetProcAddress to get pointers to the funcs. void GL_Init(void *(*getglfunction) (char *name)) { @@ -1816,6 +2022,47 @@ void GL_Init(void *(*getglfunction) (char *name)) qglDebugMessageCallbackARB(myGLDEBUGPROCAMD, NULL); qglGetError(); /*suck up the invalid operation error for non-debug contexts*/ #endif + + + + memset(&sh_config, 0, sizeof(sh_config)); + if (gl_config.gles) + { + sh_config.minver = 100; + sh_config.maxver = 110; + sh_config.blobpath = "gles/%s.blob"; + sh_config.progpath = "glsl/%s.glsl"; + sh_config.shadernamefmt = "%s_gles"; + } + else + { + sh_config.minver = gl_config.arb_shader_objects?110:0; + sh_config.maxver = gl_config.arb_shader_objects?gl_config.maxglslversion:0; + sh_config.blobpath = "glsl/%s.blob"; + sh_config.progpath = "glsl/%s.glsl"; + sh_config.shadernamefmt = "%s_glsl"; + } + + sh_config.progs_supported = gl_config.arb_shader_objects; + sh_config.progs_required = gl_config.nofixedfunc; + + sh_config.pDeleteProg = GLSlang_DeleteProg; + sh_config.pLoadBlob = qglProgramBinary?GLSlang_LoadBlob:NULL; + sh_config.pCreateProgram = GLSlang_CreateProgramPermu; + sh_config.pProgAutoFields = GLSlang_ProgAutoFields; + + if (gl_config.nofixedfunc) + { + sh_config.tex_env_combine = 1; + sh_config.nv_tex_env_combine4 = 1; + sh_config.env_add = 1; + } + else + { + sh_config.tex_env_combine = gl_config.tex_env_combine; + sh_config.nv_tex_env_combine4 = gl_config.nv_tex_env_combine4; + sh_config.env_add = gl_config.env_add; + } } diff --git a/engine/gl/gl_vidlinuxglx.c b/engine/gl/gl_vidlinuxglx.c index 23680987..b2700b5f 100644 --- a/engine/gl/gl_vidlinuxglx.c +++ b/engine/gl/gl_vidlinuxglx.c @@ -348,7 +348,7 @@ static qboolean VMODE_Init(void) if (vm.lib) { if (vm.pXF86VidModeQueryVersion(vid_dpy, &vm.vmajor, &vm.vminor)) - Con_Printf("Using XF86-VidModeExtension Ver. %d.%d\n", vm.vmajor, vm.vminor); + Con_Printf("Using XF86-VidModeExtension Ver. %d.%d\n", vm.vmajor, vm.vminor); else { Con_Printf("No XF86-VidModeExtension support\n"); @@ -409,15 +409,15 @@ static qboolean DGAM_Init(void) #define XIMaskIsSet(ptr, event) (((unsigned char*)(ptr))[(event)>>3] & (1 << ((event) & 7))) #define XIMaskLen(event) (((event + 7) >> 3)) typedef struct { - int mask_len; - unsigned char *mask; - double *values; + int mask_len; + unsigned char *mask; + double *values; } XIValuatorState; typedef struct { - int deviceid; - int mask_len; - unsigned char* mask; + int deviceid; + int mask_len; + unsigned char* mask; } XIEventMask; #define XIAllMasterDevices 1 #define XI_RawButtonPress 15 @@ -425,19 +425,19 @@ typedef struct #define XI_RawMotion 17 #define XI_LASTEVENT XI_RawMotion typedef struct { - int type; /* GenericEvent */ - unsigned long serial; /* # of last request processed by server */ - Bool send_event; /* true if this came from a SendEvent request */ - Display *display; /* Display the event was read from */ - int extension; /* XI extension offset */ - int evtype; /* XI_RawKeyPress, XI_RawKeyRelease, etc. */ - Time time; - int deviceid; - int sourceid; /* Bug: Always 0. https://bugs.freedesktop.org//show_bug.cgi?id=34240 */ - int detail; - int flags; - XIValuatorState valuators; - double *raw_values; + int type; /* GenericEvent */ + unsigned long serial; /* # of last request processed by server */ + Bool send_event; /* true if this came from a SendEvent request */ + Display *display; /* Display the event was read from */ + int extension; /* XI extension offset */ + int evtype; /* XI_RawKeyPress, XI_RawKeyRelease, etc. */ + Time time; + int deviceid; + int sourceid; /* Bug: Always 0. https://bugs.freedesktop.org//show_bug.cgi?id=34240 */ + int detail; + int flags; + XIValuatorState valuators; + double *raw_values; } XIRawEvent; #endif static struct @@ -497,9 +497,9 @@ static qboolean XI2_Init(void) XISetMask(maskbuf, XI_RawButtonRelease); /* if (xi2.vmajor >= 2 && xi2.vminor >= 2) { - XISetMask(maskbuf, XI_RawTouchBegin); - XISetMask(maskbuf, XI_RawTouchUpdate); - XISetMask(maskbuf, XI_RawTouchEnd); + XISetMask(maskbuf, XI_RawTouchBegin); + XISetMask(maskbuf, XI_RawTouchUpdate); + XISetMask(maskbuf, XI_RawTouchEnd); } */ xi2.pXISelectEvents(vid_dpy, DefaultRootWindow(vid_dpy), &evm, 1); return true; @@ -595,41 +595,43 @@ static void X_ShutdownUnicode(void) static long X_InitUnicode(void) { long requiredevents = 0; -//return 0; X_ShutdownUnicode(); - if (x11.pXSetLocaleModifiers && x11.pXSupportsLocale && x11.pXOpenIM && x11.pXCreateIC && x11.pXSetICFocus && x11.pXGetICValues && x11.pXFilterEvent && (x11.pXutf8LookupString || x11.pXwcLookupString) && x11.pXDestroyIC && x11.pXCloseIM) + if (!COM_CheckParm("-noxim")) { - setlocale(LC_CTYPE, ""); - x11.pXSetLocaleModifiers(""); - if (x11.pXSupportsLocale()) + if (x11.pXSetLocaleModifiers && x11.pXSupportsLocale && x11.pXOpenIM && x11.pXCreateIC && x11.pXSetICFocus && x11.pXGetICValues && x11.pXFilterEvent && (x11.pXutf8LookupString || x11.pXwcLookupString) && x11.pXDestroyIC && x11.pXCloseIM) { - x11.inputmethod = x11.pXOpenIM(vid_dpy, NULL, NULL, NULL); - if (x11.inputmethod) + setlocale(LC_CTYPE, ""); //just in case. + x11.pXSetLocaleModifiers(""); + if (x11.pXSupportsLocale()) { - x11.unicodecontext = x11.pXCreateIC(x11.inputmethod, - XNInputStyle, XIMPreeditNothing | XIMStatusNothing, - XNClientWindow, vid_window, - XNFocusWindow, vid_window, - NULL); - if (x11.unicodecontext) + x11.inputmethod = x11.pXOpenIM(vid_dpy, NULL, NULL, NULL); + if (x11.inputmethod) { - x11.pXSetICFocus(x11.unicodecontext); - x11.dounicode = true; + x11.unicodecontext = x11.pXCreateIC(x11.inputmethod, + XNInputStyle, XIMPreeditNothing | XIMStatusNothing, + XNClientWindow, vid_window, + XNFocusWindow, vid_window, + NULL); + if (x11.unicodecontext) + { + x11.pXSetICFocus(x11.unicodecontext); + x11.dounicode = true; - x11.pXGetICValues(x11.unicodecontext, XNFilterEvents, &requiredevents, NULL); + x11.pXGetICValues(x11.unicodecontext, XNFilterEvents, &requiredevents, NULL); + } } } +// setlocale(LC_CTYPE, "C"); } -// setlocale(LC_ALL, "C"); } - Con_Printf("Unicode support: %s\n", x11.dounicode?"available":"unavailable"); + Con_DPrintf("Unicode support: %s\n", x11.dounicode?"available":"unavailable"); return requiredevents; } -static void X_KeyEvent(XKeyEvent *ev, qboolean pressed) +static void X_KeyEvent(XKeyEvent *ev, qboolean pressed, qboolean filtered) { int i; int key; @@ -639,14 +641,14 @@ static void X_KeyEvent(XKeyEvent *ev, qboolean pressed) key = 0; keysym = x11.pXLookupKeysym(ev, 0); - if (pressed) + if (pressed && !filtered) { if (x11.dounicode) { Status status = XLookupNone; if (x11.pXutf8LookupString) { - char buf1[1] = {0}; + char buf1[4] = {0}; char *buf = buf1, *c; int count = x11.pXutf8LookupString(x11.unicodecontext, (XKeyPressedEvent*)ev, buf1, sizeof(buf1), NULL, &status); if (status == XBufferOverflow) @@ -671,10 +673,8 @@ static void X_KeyEvent(XKeyEvent *ev, qboolean pressed) if (status == XBufferOverflow) { buf = alloca(sizeof(wchar_t)*(count+1)); - printf("XBufferOverflow\n"); count = x11.pXwcLookupString(x11.unicodecontext, (XKeyPressedEvent*)ev, buf, count, NULL, &status); } - printf("Translated to \"%ls\" (%i %i)\n", buf, count, status); //if wchar_t is 16bit, then expect problems when we completely ignore surrogates. this is why we favour the utf8 route as it doesn't care whether wchar_t is defined as 16bit or 32bit. for (i = 0; i < count; i++) if (buf[i]) @@ -893,15 +893,13 @@ static void GetEvent(void) int b; qboolean x11violations = true; Window mw; + qboolean filtered = false; x11.pXNextEvent(vid_dpy, &event); if (x11.dounicode) if (x11.pXFilterEvent(&event, vid_window)) - { - Con_Printf("Event filtered\n"); - return; - } + filtered = true; switch (event.type) { @@ -957,7 +955,7 @@ static void GetEvent(void) raw_val++; } } - IN_MouseMove(raw->deviceid, false, rawx, rawy, 0, 0); + IN_MouseMove(raw->deviceid, false, rawx, rawy, 0, 0); } break; default: @@ -973,7 +971,7 @@ static void GetEvent(void) case ResizeRequest: vid.pixelwidth = event.xresizerequest.width; vid.pixelheight = event.xresizerequest.height; - Cvar_ForceCallback(&vid_conautoscale); + Cvar_ForceCallback(&vid_conautoscale); // if (fullscreenflags & FULLSCREEN_ACTIVE) // x11.pXMoveWindow(vid_dpy, vid_window, 0, 0); break; @@ -982,7 +980,7 @@ static void GetEvent(void) { vid.pixelwidth = event.xconfigurerequest.width; vid.pixelheight = event.xconfigurerequest.height; - Cvar_ForceCallback(&vid_conautoscale); + Cvar_ForceCallback(&vid_conautoscale); } else if (event.xconfigurerequest.window == vid_decoywindow) { @@ -993,10 +991,10 @@ static void GetEvent(void) // x11.pXMoveWindow(vid_dpy, vid_window, 0, 0); break; case KeyPress: - X_KeyEvent(&event.xkey, true); + X_KeyEvent(&event.xkey, true, filtered); break; case KeyRelease: - X_KeyEvent(&event.xkey, false); + X_KeyEvent(&event.xkey, false, filtered); break; case MotionNotify: @@ -1139,7 +1137,7 @@ static void GetEvent(void) if ((fullscreenflags & FULLSCREEN_LEGACY) && (fullscreenflags & FULLSCREEN_ACTIVE)) mw = vid_decoywindow; - if (event.xfocus.window == mw) + if (event.xfocus.window == mw) { ActiveApp = false; if (old_windowed_mouse) @@ -1284,24 +1282,24 @@ void InitSig(void) static Cursor CreateNullCursor(Display *display, Window root) { - Pixmap cursormask; - XGCValues xgc; - GC gc; - XColor dummycolour; - Cursor cursor; + Pixmap cursormask; + XGCValues xgc; + GC gc; + XColor dummycolour; + Cursor cursor; - cursormask = x11.pXCreatePixmap(display, root, 1, 1, 1/*depth*/); - xgc.function = GXclear; - gc = x11.pXCreateGC(display, cursormask, GCFunction, &xgc); - x11.pXFillRectangle(display, cursormask, gc, 0, 0, 1, 1); - dummycolour.pixel = 0; - dummycolour.red = 0; - dummycolour.flags = 04; - cursor = x11.pXCreatePixmapCursor(display, cursormask, cursormask, - &dummycolour,&dummycolour, 0,0); - x11.pXFreePixmap(display,cursormask); - x11.pXFreeGC(display,gc); - return cursor; + cursormask = x11.pXCreatePixmap(display, root, 1, 1, 1/*depth*/); + xgc.function = GXclear; + gc = x11.pXCreateGC(display, cursormask, GCFunction, &xgc); + x11.pXFillRectangle(display, cursormask, gc, 0, 0, 1, 1); + dummycolour.pixel = 0; + dummycolour.red = 0; + dummycolour.flags = 04; + cursor = x11.pXCreatePixmapCursor(display, cursormask, cursormask, + &dummycolour,&dummycolour, 0,0); + x11.pXFreePixmap(display,cursormask); + x11.pXFreeGC(display,gc); + return cursor; } qboolean GLVID_ApplyGammaRamps(unsigned short *ramps) @@ -1343,9 +1341,8 @@ void GLVID_SwapBuffers (void) switch(currentpsl) { #ifdef USE_EGL - default: case PSL_EGL: - EGL_SwapBuffers(); + EGL_BeginRendering(); break; #endif case PSL_GLX: @@ -1353,6 +1350,9 @@ void GLVID_SwapBuffers (void) //chances are, it's version is more suitable anyway. At least there's the chance that it might be. qglXSwapBuffers(vid_dpy, vid_window); break; + default: + case PSL_NONE: + break; } } @@ -1930,7 +1930,7 @@ void Sys_SendKeyEvents(void) } fullscreenflags &= ~FULLSCREEN_ACTIVE; } - modeswitchpending = 0; + modeswitchpending = 0; } if (modeswitchpending) @@ -2050,11 +2050,11 @@ rendererinfo_t eglrendererinfo = GLBE_LightCullModel, GLBE_VBO_Begin, - GLBE_VBO_Data, - GLBE_VBO_Finish, - GLBE_VBO_Destroy, + GLBE_VBO_Data, + GLBE_VBO_Finish, + GLBE_VBO_Destroy, - GLBE_RenderToTextureUpdate2d, + GLBE_RenderToTextureUpdate2d, "" }; @@ -2078,7 +2078,7 @@ char *Sys_GetClipboard(void) return data; } - return clipboard_buffer; + return clipboard_buffer; } void Sys_CloseClipboard(char *bf) @@ -2092,32 +2092,32 @@ void Sys_CloseClipboard(char *bf) void Sys_SaveClipboard(char *text) { Atom xa_clipboard = x11.pXInternAtom(vid_dpy, "PRIMARY", false); - Q_strncpyz(clipboard_buffer, text, SYS_CLIPBOARD_SIZE); + Q_strncpyz(clipboard_buffer, text, SYS_CLIPBOARD_SIZE); x11.pXSetSelectionOwner(vid_dpy, xa_clipboard, vid_window, CurrentTime); } #endif qboolean X11_GetDesktopParameters(int *width, int *height, int *bpp, int *refreshrate) { - Display *xtemp; - int scr; + Display *xtemp; + int scr; if (!x11_initlib()) return false; - xtemp = x11.pXOpenDisplay(NULL); + xtemp = x11.pXOpenDisplay(NULL); - if (!xtemp) - return false; + if (!xtemp) + return false; - scr = DefaultScreen(xtemp); + scr = DefaultScreen(xtemp); - *width = DisplayWidth(xtemp, scr); - *height = DisplayHeight(xtemp, scr); - *bpp = DefaultDepth(xtemp, scr); - *refreshrate = 0; + *width = DisplayWidth(xtemp, scr); + *height = DisplayHeight(xtemp, scr); + *bpp = DefaultDepth(xtemp, scr); + *refreshrate = 0; - x11.pXCloseDisplay(xtemp); + x11.pXCloseDisplay(xtemp); - return true; + return true; } diff --git a/engine/gl/gl_vidnt.c b/engine/gl/gl_vidnt.c index a6c27dc5..929f3b8d 100644 --- a/engine/gl/gl_vidnt.c +++ b/engine/gl/gl_vidnt.c @@ -842,6 +842,7 @@ qboolean VID_SetFullDIBMode (rendererstate_t *info) extern qboolean gammaworks; static void ReleaseGL(void); +static void Win_Touch_Init(HWND wnd); static qboolean CreateMainWindow(rendererstate_t *info) { qboolean stat; @@ -855,6 +856,9 @@ static qboolean CreateMainWindow(rendererstate_t *info) TRACE(("dbg: GLVID_SetMode: VID_SetFullDIBMode\n")); stat = VID_SetFullDIBMode(info); } + VID_UpdateWindowStatus(mainwindow); + + Win_Touch_Init(mainwindow); INS_UpdateGrabs(info->fullscreen, ActiveApp); @@ -1264,7 +1268,7 @@ qboolean VID_AttachGL (rendererstate_t *info) qwglSwapIntervalEXT(vid_vsync.value); } TRACE(("dbg: VID_AttachGL: qSwapBuffers\n")); - qglClearColor(0, 0, 0, 0); + qglClearColor(0, 0, 0, 1); qglClear(GL_COLOR_BUFFER_BIT); qSwapBuffers(maindc); @@ -1320,7 +1324,32 @@ void GLVID_Recenter_f(void) void VID_WndAlpha_Override_Callback(struct cvar_s *var, char *oldvalue) { + //this code tells windows to use the alpha channel of the screen, but does really nasty things with the mouse such that its unplayable. + //its not useful. +/* if (modestate==MS_WINDOWED) + { + struct qDWM_BLURBEHIND + { + DWORD dwFlags; + BOOL fEnable; + HRGN hRgnBlur; + BOOL fTransitionOnMaximized; + } bb = {1, true, NULL, true}; + HRESULT (WINAPI *pDwmEnableBlurBehindWindow)(HWND hWnd,const struct qDWM_BLURBEHIND *pBlurBehind); + dllfunction_t dwm[] = + { + {(void*)&pDwmEnableBlurBehindWindow, "DwmEnableBlurBehindWindow"}, + {NULL,NULL} + }; + if (Sys_LoadLibrary("dwmapi.dll", dwm)) + pDwmEnableBlurBehindWindow(mainwindow, &bb); + } +*/ + #ifdef WS_EX_LAYERED + //enable whole-window fixed transparency. should work in win2k+ + //note that this can destroy framerates, and they won't reset when the setting is reverted to 1. + //be prepared to do a vid_restart. if (modestate==MS_WINDOWED) { int av; @@ -1346,7 +1375,10 @@ void VID_WndAlpha_Override_Callback(struct cvar_s *var, char *oldvalue) pSetLayeredWindowAttributes(mainwindow, 0, (BYTE)av, LWA_ALPHA); } else + { SetWindowLong(mainwindow, GWL_EXSTYLE, GetWindowLong(mainwindow, GWL_EXSTYLE) & ~WS_EX_LAYERED); + pSetLayeredWindowAttributes(mainwindow, 0, (BYTE)255, LWA_ALPHA); + } } } #endif @@ -1479,39 +1511,47 @@ int forcepixelformat; BOOL CheckForcePixelFormat(rendererstate_t *info) { - if (qwglChoosePixelFormatARB && info->multisample) + if (qwglChoosePixelFormatARB && (info->multisample || info->srgb)) { HDC hDC; int valid; - float fAttributes[] = {0,0}; + float fAttribute[] = {0,0}; UINT numFormats; int pixelformat; - int iAttributes[] = { - WGL_DRAW_TO_WINDOW_ARB, GL_TRUE, - WGL_SUPPORT_OPENGL_ARB, GL_TRUE, - WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB, - WGL_COLOR_BITS_ARB, info->bpp, - WGL_ALPHA_BITS_ARB, 8, - WGL_DEPTH_BITS_ARB, 16, - WGL_STENCIL_BITS_ARB, 8, - WGL_DOUBLE_BUFFER_ARB, GL_TRUE, - WGL_SAMPLE_BUFFERS_ARB, GL_TRUE, - WGL_SAMPLES_ARB, info->multisample, // Check For 4x Multisampling - WGL_STEREO_ARB, info->stereo, - 0, 0 - }; + int iAttributes = 0; + int iAttribute[16*2]; + iAttribute[iAttributes++] = WGL_DRAW_TO_WINDOW_ARB; iAttribute[iAttributes++] = GL_TRUE; + iAttribute[iAttributes++] = WGL_SUPPORT_OPENGL_ARB; iAttribute[iAttributes++] = GL_TRUE; + iAttribute[iAttributes++] = WGL_ACCELERATION_ARB; iAttribute[iAttributes++] = WGL_FULL_ACCELERATION_ARB; + iAttribute[iAttributes++] = WGL_COLOR_BITS_ARB; iAttribute[iAttributes++] = info->bpp; + iAttribute[iAttributes++] = WGL_ALPHA_BITS_ARB; iAttribute[iAttributes++] = 4; + iAttribute[iAttributes++] = WGL_DEPTH_BITS_ARB; iAttribute[iAttributes++] = 16; + iAttribute[iAttributes++] = WGL_STENCIL_BITS_ARB; iAttribute[iAttributes++] = 8; + iAttribute[iAttributes++] = WGL_DOUBLE_BUFFER_ARB; iAttribute[iAttributes++] = GL_TRUE; + iAttribute[iAttributes++] = WGL_STEREO_ARB; iAttribute[iAttributes++] = info->stereo; + if (info->multisample) + { + iAttribute[iAttributes++] = WGL_SAMPLE_BUFFERS_ARB; iAttribute[iAttributes++] = GL_TRUE; + iAttribute[iAttributes++] = WGL_SAMPLES_ARB, iAttribute[iAttributes++] = info->multisample; // Check For 4x Multisampling + } + if (info->srgb) + { + iAttribute[iAttributes++] = WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB; iAttribute[iAttributes++] = GL_TRUE; + } + iAttribute[iAttributes++] = 0; iAttribute[iAttributes++] = 0; + TRACE(("dbg: bSetupPixelFormat: attempting wglChoosePixelFormatARB (multisample 4)\n")); hDC = GetDC(mainwindow); - valid = qwglChoosePixelFormatARB(hDC,iAttributes,fAttributes,1,&pixelformat,&numFormats); - while ((!valid || numFormats < 1) && iAttributes[19] > 1) + valid = qwglChoosePixelFormatARB(hDC,iAttribute,fAttribute,1,&pixelformat,&numFormats); +/* while ((!valid || numFormats < 1) && iAttribute[19] > 1) { //failed, switch wgl_samples to 2 - iAttributes[19] /= 2; + iAttribute[19] /= 2; TRACE(("dbg: bSetupPixelFormat: attempting wglChoosePixelFormatARB (smaller multisample)\n")); - valid = qwglChoosePixelFormatARB(hDC,iAttributes,fAttributes,1,&pixelformat,&numFormats); + valid = qwglChoosePixelFormatARB(hDC,iAttribute,fAttribute,1,&pixelformat,&numFormats); } - +*/ ReleaseDC(mainwindow, hDC); if (valid && numFormats > 0) { @@ -1777,6 +1817,77 @@ qboolean GLAppActivate(BOOL fActive, BOOL minimize) return true; } +#ifndef TWF_WANTPALM +typedef struct _TOUCHINPUT { + LONG x; + LONG y; + HANDLE hSource; + DWORD dwID; + DWORD dwFlags; + DWORD dwMask; + DWORD dwTime; + ULONG_PTR dwExtraInfo; + DWORD cxContact; + DWORD cyContact; +} TOUCHINPUT, *PTOUCHINPUT; +DECLARE_HANDLE(HTOUCHINPUT); + +#define WM_TOUCH 0x0240 +#define TOUCHINPUTMASKF_CONTACTAREA 0x0004 +#define TOUCHEVENTF_DOWN 0x0002 +#define TOUCHEVENTF_UP 0x0004 +#define TWF_WANTPALM 0x00000002 +#endif + +static BOOL (WINAPI *pRegisterTouchWindow)(HWND hWnd, ULONG ulFlags); +static BOOL (WINAPI *pGetTouchInputInfo)(HTOUCHINPUT hTouchInput, UINT cInputs, PTOUCHINPUT pInputs, int cbSize); +static BOOL (WINAPI *pCloseTouchInputHandle)(HTOUCHINPUT hTouchInput); +static void Win_Touch_Init(HWND wnd) +{ + HMODULE lib; + lib = LoadLibrary("user32.dll"); + pRegisterTouchWindow = (void*)GetProcAddress(lib, "RegisterTouchWindow"); + pGetTouchInputInfo = (void*)GetProcAddress(lib, "GetTouchInputInfo"); + pCloseTouchInputHandle = (void*)GetProcAddress(lib, "CloseTouchInputHandle"); + + if (pRegisterTouchWindow && pGetTouchInputInfo && pCloseTouchInputHandle) + pRegisterTouchWindow(wnd, TWF_WANTPALM); +} +static void Win_Touch_Event(int points, HTOUCHINPUT ti) +{ + float sz; + int i; + TOUCHINPUT *inputs = malloc(points * sizeof(*inputs)), *input; + if (inputs) + { + if (pGetTouchInputInfo(ti, points, inputs, sizeof(*inputs))) + { + for (i = 0, input = inputs; i < points; i++, input++) + { + int id = input->dwID+1; //googling implies the id is generally a low 0-based index. I can't test this. the +1 ensures that mouselook is not broken by someone trying to use a touchscreen at the same time. + if (input->dwMask & TOUCHINPUTMASKF_CONTACTAREA) + sz = sqrt((input->cxContact*input->cxContact + input->cyContact*input->cyContact) / 10000.0); + else + sz = 0; + + //the web seems to imply that the ids should be low values, <16 or so. hurrah. + + //movement *then* buttons. this should ensure that the cursor is positioned correctly. + IN_MouseMove(id, true, input->x/100.0f, input->y/100.0f, 0, sz); + + if (input->dwFlags & TOUCHEVENTF_DOWN) + IN_KeyEvent(id, true, K_MOUSE1, 0); + if (input->dwFlags & TOUCHEVENTF_UP) + IN_KeyEvent(id, false, K_MOUSE1, 0); + } + } + free(inputs); + } + + pCloseTouchInputHandle(ti); +} + + /* main window procedure */ LONG WINAPI GLMainWndProc ( HWND hWnd, @@ -1812,6 +1923,10 @@ LONG WINAPI GLMainWndProc ( ClearAllStates (); break; + case WM_TOUCH: + Win_Touch_Event(LOWORD(wParam), (HTOUCHINPUT)lParam); + return 0; //return 0 if we handled it. + case WM_CREATE: break; diff --git a/engine/gl/gl_warp.c b/engine/gl/gl_warp.c index d30479ef..88148805 100644 --- a/engine/gl/gl_warp.c +++ b/engine/gl/gl_warp.c @@ -56,6 +56,7 @@ void R_SetSky(char *skyname) "program default2d\n" "{\n" "map $diffuse\n" + "nodepth\n" //don't write depth. this stuff is meant to be an infiniteish distance away. "}\n" "}\n" ); diff --git a/engine/gl/glquake.h b/engine/gl/glquake.h index 6653ba97..b5873f18 100644 --- a/engine/gl/glquake.h +++ b/engine/gl/glquake.h @@ -32,6 +32,13 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define WIN32_LEAN_AND_MEAN #include #include + +#if defined(WINAPI_FAMILY) && !defined(WINRT) + #if WINAPI_FAMILY != WINAPI_FAMILY_DESKTOP_APP + //don't just define it. things that don't #include winquake.h / glquake.h need it too. + #error "WINRT needs to be defined for non-desktop" + #endif +#endif #endif #ifndef APIENTRY @@ -168,11 +175,11 @@ typedef void (APIENTRYP FTEPFNGLDISABLEVERTEXATTRIBARRAY) (GLuint index); typedef GLint (APIENTRYP FTEPFNGLGETUNIFORMLOCATIONARBPROC) (GLhandleARB programObj, const GLcharARB *name); typedef void (APIENTRYP FTEPFNGLGETVERTEXATTRIBIV) (GLuint index, GLenum pname, GLint *params); typedef void (APIENTRYP FTEPFNGLUNIFORM4FARBPROC) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); -typedef void (APIENTRYP FTEPFNGLUNIFORMMATRIXPROC) (GLint location, GLsizei count, GLboolean transpose, GLfloat *value); -typedef void (APIENTRYP FTEPFNGLUNIFORM4FVARBPROC) (GLint location, GLsizei count, GLfloat *value); +typedef void (APIENTRYP FTEPFNGLUNIFORMMATRIXPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP FTEPFNGLUNIFORM4FVARBPROC) (GLint location, GLsizei count, const GLfloat *value); typedef void (APIENTRYP FTEPFNGLUNIFORM3FARBPROC) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2); -typedef void (APIENTRYP FTEPFNGLUNIFORM3FVARBPROC) (GLint location, GLsizei count, GLfloat *value); -typedef void (APIENTRYP FTEPFNGLUNIFORM2FVARBPROC) (GLint location, GLsizei count, GLfloat *value); +typedef void (APIENTRYP FTEPFNGLUNIFORM3FVARBPROC) (GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP FTEPFNGLUNIFORM2FVARBPROC) (GLint location, GLsizei count, const GLfloat *value); typedef void (APIENTRYP FTEPFNGLUNIFORM1IARBPROC) (GLint location, GLint v0); typedef void (APIENTRYP FTEPFNGLUNIFORM1FARBPROC) (GLint location, GLfloat v0); @@ -232,15 +239,15 @@ void GL_Upload24BGR (char *name, qbyte *data, int width, int height, unsigned in void GL_Upload8_EXT (qbyte *data, int width, int height, qboolean mipmap, qboolean alpha); #endif */ -texid_tf GL_LoadTexture (char *identifier, int width, int height, qbyte *data, unsigned int flags, unsigned int transtype); -texid_tf GL_LoadTexture8Bump (char *identifier, int width, int height, unsigned char *data, unsigned int flags, float bumpscale); -texid_tf GL_LoadTexture8Pal24 (char *identifier, int width, int height, qbyte *data, qbyte *palette24, unsigned int flags); -texid_tf GL_LoadTexture8Pal32 (char *identifier, int width, int height, qbyte *data, qbyte *palette32, unsigned int flags); -texid_tf GL_LoadTexture32 (char *identifier, int width, int height, void *data, unsigned int flags); -texid_tf GL_LoadCompressed(char *name); -texid_tf GL_FindTexture (char *identifier, unsigned int flags); +texid_tf GL_LoadTexture (const char *identifier, int width, int height, qbyte *data, unsigned int flags, unsigned int transtype); +texid_tf GL_LoadTexture8Bump (const char *identifier, int width, int height, unsigned char *data, unsigned int flags, float bumpscale); +texid_tf GL_LoadTexture8Pal24 (const char *identifier, int width, int height, qbyte *data, qbyte *palette24, unsigned int flags); +texid_tf GL_LoadTexture8Pal32 (const char *identifier, int width, int height, qbyte *data, qbyte *palette32, unsigned int flags); +texid_tf GL_LoadTexture32 (const char *identifier, int width, int height, void *data, unsigned int flags); +texid_tf GL_LoadCompressed(const char *name); +texid_tf GL_FindTexture (const char *identifier, unsigned int flags); -texid_tf GL_LoadTextureFB (char *identifier, int width, int height, qbyte *data, unsigned int flags); +texid_tf GL_LoadTextureFB (const char *identifier, int width, int height, qbyte *data, unsigned int flags); void GL_Upload8Pal24 (qbyte *data, qbyte *pal, int width, int height, unsigned int flags); /* typedef struct @@ -1060,7 +1067,7 @@ extern void (APIENTRY *qglBindVertexArray)(GLuint vaoarray); //glslang helper api -GLhandleARB GLSlang_CreateProgram(char *name, int ver, char **precompilerconstants, char *vert, char *frag, qboolean silent); +GLhandleARB GLSlang_CreateProgram(const char *name, int ver, const char **precompilerconstants, const char *vert, const char *frag, qboolean silent, vfsfile_t *blobfile); GLint GLSlang_GetUniformLocation (int prog, char *name); void GL_SelectProgram(int program); #define GLSlang_UseProgram(prog) GL_SelectProgram(prog) diff --git a/engine/gl/glsupp.h b/engine/gl/glsupp.h index 05ed898c..cdcc6d60 100644 --- a/engine/gl/glsupp.h +++ b/engine/gl/glsupp.h @@ -582,6 +582,12 @@ typedef void (APIENTRYP PFNGLGETSHADERSOURCEARBPROC) (GLhandleARB obj, GLsizei #define WGL_SAMPLES_ARB 0x2042 #define GL_MULTISAMPLE_ARB 0x809D +#ifndef GL_FRAMEBUFFER_SRGB +#define GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20B2 +#define WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20A9 +#define GL_FRAMEBUFFER_SRGB 0x8DB9 +#endif + #ifndef GL_ARB_vertex_buffer_object #define GL_BUFFER_SIZE_ARB 0x8764 diff --git a/engine/gl/ltface.c b/engine/gl/ltface.c index ee9a64ea..bec77d9d 100644 --- a/engine/gl/ltface.c +++ b/engine/gl/ltface.c @@ -1,7 +1,7 @@ #include "quakedef.h" #ifdef RUNTIMELIGHTING -#if defined(GLQUAKE) || defined(D3DQUAKE) +#ifndef UTILITY extern model_t *lightmodel; diff --git a/engine/gl/model_hl.h b/engine/gl/model_hl.h index 64dbea20..1932e83b 100644 --- a/engine/gl/model_hl.h +++ b/engine/gl/model_hl.h @@ -234,7 +234,7 @@ void QuaternionGLMatrix(float x, float y, float z, float w, vec4_t *GLM); //void UploadTexture(hlmdl_tex_t *ptexture, qbyte *data, qbyte *pal); /* HL drawing */ -qboolean QDECL Mod_LoadHLModel (model_t *mod, void *buffer); +qboolean QDECL Mod_LoadHLModel (model_t *mod, void *buffer, size_t fsize); void R_DrawHLModel(entity_t *curent); /* physics stuff */ diff --git a/engine/gl/r_bishaders.h b/engine/gl/r_bishaders.h index e1dfc263..cbbe02bb 100644 --- a/engine/gl/r_bishaders.h +++ b/engine/gl/r_bishaders.h @@ -392,10 +392,16 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "{\n" "float4 pos: POSITION;\n" "float3 normal: NORMAL;\n" +"#ifdef MASK\n" +"float4 tc: TEXCOORD0;\n" +"#endif\n" "};\n" "struct v2f\n" "{\n" "float4 pos: SV_POSITION;\n" +"#ifdef MASK\n" +"float2 tc: TEXCOORD0;\n" +"#endif\n" "};\n" "#include \n" @@ -407,12 +413,42 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "outp.pos = mul(m_model, inp.pos);\n" "outp.pos = mul(m_view, outp.pos);\n" "outp.pos = mul(m_projection, outp.pos);\n" + +"#ifdef MASK\n" +"outp.tc = inp.tc.xy;\n" +"#endif\n" + "return outp;\n" "}\n" "#endif\n" "#ifdef FRAGMENT_SHADER\n" -"void main (v2f inp) : SV_TARGET\n" +"#ifdef MASK\n" +"Texture2D shaderTexture[1];\n" +"SamplerState SampleType[1];\n" +"#endif\n" + +"#if LEVEL < 1000\n" +//pre dx10 requires that we ALWAYS write to a target. +"float4 main (v2f inp) : SV_TARGET\n" +"#else\n" +//but on 10, it'll write depth automatically and we don't care about colour. +"void main (v2f inp) //dx10-level\n" +"#endif\n" "{\n" + +"#ifdef MASK\n" +"float alpha = shaderTexture[0].Sample(SampleType[0], inp.tc).a;\n" +"#ifndef MASKOP\n" +"#define MASKOP >= //drawn if (alpha OP ref) is true.\n" +"#endif\n" +//support for alpha masking +"if (!(alpha MASKOP MASK))\n" +"discard;\n" +"#endif\n" + +"#if LEVEL < 1000\n" +"return float4(0, 0, 0, 1);\n" +"#endif\n" "}\n" "#endif\n" }, @@ -467,7 +503,9 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "v2f main (a2v inp)\n" "{\n" "v2f outp;\n" -"outp.pos = mul(m_projection, inp.pos);\n" +"outp.pos = mul(m_model, inp.pos);\n" +"outp.pos = mul(m_view, outp.pos);\n" +"outp.pos = mul(m_projection, outp.pos);\n" "outp.tc = inp.tc;\n" "outp.vcol = inp.vcol;\n" "return outp;\n" @@ -680,6 +718,16 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "{\n" "float4 col;\n" "col = shaderTexture[0].Sample(SampleType, inp.tc);\n" + +"#ifdef MASK\n" +"#ifndef MASKOP\n" +"#define MASKOP >= //drawn if (alpha OP ref) is true.\n" +"#endif\n" +//support for alpha masking +"if (!(col.a MASKOP MASK))\n" +"discard;\n" +"#endif\n" + "#ifdef UPPER\n" "float4 uc = shaderTexture[2].Sample(SampleType, inp.tc);\n" "col.rgb = mix(col.rgb, uc.rgb*e_uppercolour, uc.a);\n" @@ -1180,7 +1228,19 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "float4 main (v2f inp) : SV_TARGET\n" "{\n" -"return shaderTexture[0].Sample(SampleType[0], inp.tc) * shaderTexture[1].Sample(SampleType[1], inp.lmtc).bgra;\n" +"float4 tex = shaderTexture[0].Sample(SampleType[0], inp.tc);\n" +"float4 lm = shaderTexture[1].Sample(SampleType[1], inp.lmtc);\n" + +"#ifdef MASK\n" +"#ifndef MASKOP\n" +"#define MASKOP >= //drawn if (alpha OP ref) is true.\n" +"#endif\n" +//support for alpha masking +"if (!(tex.a MASKOP MASK))\n" +"discard;\n" +"#endif\n" + +"return tex * lm.bgra;\n" "}\n" "#endif\n" }, diff --git a/engine/gl/shader.h b/engine/gl/shader.h index 8c3470ce..98213fb6 100644 --- a/engine/gl/shader.h +++ b/engine/gl/shader.h @@ -47,6 +47,7 @@ typedef enum { SHADER_2D } shadertype_t; +/* typedef enum { MF_NONE = 1<<0, MF_NORMALS = 1<<1, @@ -57,6 +58,7 @@ typedef enum { MF_NOCULL = 1<<6, MF_NONBATCHED = 1<<7 } meshfeatures_t; +*/ //colour manipulation typedef struct @@ -158,9 +160,10 @@ enum SBITS_MISC_NODEPTHTEST = 0x00020000, SBITS_MISC_DEPTHEQUALONLY = 0x00040000, SBITS_MISC_DEPTHCLOSERONLY = 0x00080000, -#define SBITS_MISC_BITS 0x000f0000 +//#define SBITS_MISC_BITS 0x000f0000 SBITS_TRUFORM = 0x00100000, + SBITS_AFFINE = 0x00200000, }; @@ -424,6 +427,9 @@ union programhandle_u void *ctabv; #endif #ifdef D3D11QUAKE + int topology; //D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST + void *hull; + void *domain; void *layout; #endif } hlsl; @@ -537,7 +543,7 @@ struct shader_s shader_gen_t *generator; char *genargs; - meshfeatures_t features; +// meshfeatures_t features; bucket_t bucket; }; @@ -548,27 +554,27 @@ extern int be_maxpasses; void R_UnloadShader(shader_t *shader); -shader_t *R_RegisterPic (char *name); -shader_t *R_RegisterShader (char *name, unsigned int usageflags, const char *shaderscript); -shader_t *R_RegisterShader_Lightmap (char *name); -shader_t *R_RegisterShader_Vertex (char *name); -shader_t *R_RegisterShader_Flare (char *name); -shader_t *R_RegisterSkin (char *shadername, char *modname); -shader_t *R_RegisterCustom (char *name, unsigned int usageflags, shader_gen_t *defaultgen, const void *args); +shader_t *R_RegisterPic (const char *name); +shader_t *R_RegisterShader (const char *name, unsigned int usageflags, const char *shaderscript); +shader_t *R_RegisterShader_Lightmap (const char *name); +shader_t *R_RegisterShader_Vertex (const char *name); +shader_t *R_RegisterShader_Flare (const char *name); +shader_t *R_RegisterSkin (const char *shadername, const char *modname); +shader_t *R_RegisterCustom (const char *name, unsigned int usageflags, shader_gen_t *defaultgen, const void *args); void R_BuildDefaultTexnums(texnums_t *tn, shader_t *shader); void R_RemapShader(const char *sourcename, const char *destname, float timeoffset); cin_t *R_ShaderGetCinematic(shader_t *s); -cin_t *R_ShaderFindCinematic(char *name); +cin_t *R_ShaderFindCinematic(const char *name); -void Shader_DefaultSkinShell(char *shortname, shader_t *s, const void *args); -void Shader_DefaultBSPLM(char *shortname, shader_t *s, const void *args); -void Shader_DefaultBSPQ1(char *shortname, shader_t *s, const void *args); -void Shader_DefaultBSPQ2(char *shortname, shader_t *s, const void *args); -void Shader_DefaultWaterShader(char *shortname, shader_t *s, const void *args); -void Shader_DefaultSkybox(char *shortname, shader_t *s, const void *args); -void Shader_DefaultCinematic(char *shortname, shader_t *s, const void *args); -void Shader_DefaultScript(char *shortname, shader_t *s, const void *args); +void Shader_DefaultSkinShell(const char *shortname, shader_t *s, const void *args); +void Shader_DefaultBSPLM(const char *shortname, shader_t *s, const void *args); +void Shader_DefaultBSPQ1(const char *shortname, shader_t *s, const void *args); +void Shader_DefaultBSPQ2(const char *shortname, shader_t *s, const void *args); +void Shader_DefaultWaterShader(const char *shortname, shader_t *s, const void *args); +void Shader_DefaultSkybox(const char *shortname, shader_t *s, const void *args); +void Shader_DefaultCinematic(const char *shortname, shader_t *s, const void *args); +void Shader_DefaultScript(const char *shortname, shader_t *s, const void *args); void Shader_ResetRemaps(void); //called on map changes to reset remapped shaders. void Shader_DoReload(void); //called when the shader system dies. @@ -609,6 +615,29 @@ typedef struct #define FBO_TEX_DEPTH 32 //internal #define FBO_TEX_STENCIL 64 //internal + +typedef struct +{ + char *progpath; //path to use for glsl/hlsl + char *blobpath; //path to use for binary glsl/hlsl blobs. + char *shadernamefmt; //optional postfix for shader names for this renderer FIXME: should probably have multiple, for gles to fallback to desktop gl etc. + + qboolean progs_supported; //can use programs (all but gles1) + qboolean progs_required; //no fixed function if this is true (d3d11, gles, gl3core) + unsigned int minver; //lowest glsl version usable + unsigned int maxver; //highest glsl version usable + + qboolean tex_env_combine; + qboolean nv_tex_env_combine4; + qboolean env_add; + + void (*pDeleteProg) (program_t *prog, unsigned int permu); + qboolean (*pLoadBlob) (program_t *prog, const char *name, unsigned int permu, vfsfile_t *blobfile); + qboolean (*pCreateProgram) (program_t *prog, const char *name, unsigned int permu, const char **precompilerconstants, const char *vert, const char *tcs, const char *tes, const char *frag, qboolean noerrors, vfsfile_t *blobfile); + void (*pProgAutoFields) (program_t *prog, char **cvarnames, int *cvartypes); +} sh_config_t; +extern sh_config_t sh_config; + #ifdef GLQUAKE void GLBE_Init(void); void GLBE_Shutdown(void); @@ -660,8 +689,6 @@ void D3D9BE_VBO_Finish(vbobctx_t *ctx, void *edata, unsigned int esize, vboarray void D3D9BE_VBO_Destroy(vboarray_t *vearray); void D3D9BE_Scissor(srect_t *rect); -qboolean D3D9Shader_CreateProgram (program_t *prog, char *sname, int permu, char **precompilerconstants, char *vert, char *frag); -int D3D9Shader_FindUniform(union programhandle_u *h, int type, char *name); void D3D9Shader_Init(void); void D3D9BE_Reset(qboolean before); #endif @@ -681,9 +708,6 @@ qboolean D3D11BE_LightCullModel(vec3_t org, model_t *model); void D3D11BE_SelectEntity(entity_t *ent); qboolean D3D11BE_SelectDLight(dlight_t *dl, vec3_t colour, unsigned int lmode); -qboolean D3D11Shader_CreateProgram (program_t *prog, const char *name, int permu, char **precompilerconstants, char *vert, char *frag); -void D3D11Shader_DeleteProgram(program_t *prog); -int D3D11Shader_FindUniform(union programhandle_u *h, int type, char *name); qboolean D3D11Shader_Init(unsigned int featurelevel); void D3D11BE_Reset(qboolean before); void D3D11BE_SetupViewCBuffer(void); @@ -729,4 +753,6 @@ struct shader_field_names_s enum shaderprogparmtype_e ptype; }; extern struct shader_field_names_s shader_field_names[]; +extern struct shader_field_names_s shader_unif_names[]; +extern struct shader_field_names_s shader_attr_names[]; #endif diff --git a/engine/http/httpclient.c b/engine/http/httpclient.c index d81b5a6b..40a8b72e 100644 --- a/engine/http/httpclient.c +++ b/engine/http/httpclient.c @@ -714,6 +714,16 @@ void HTTPDL_Establish(struct dl_download *dl) //not yet blocking. if (connect(con->sock, (struct sockaddr *)&serveraddr, addresssize) == -1) { + int err = neterrno(); + switch(err) + { + case NET_EACCES: + Con_Printf("HTTP: connect(%s): access denied. Check firewall.\n", server); + break; + default: + Con_Printf("HTTP: connect(%s): %s", server, strerror(neterrno())); + break; + } dl->status = DL_FAILED; return; } @@ -1023,27 +1033,28 @@ typedef struct int readpos; } vfspipe_t; -void QDECL VFSPIPE_Close(vfsfile_t *f) +static qboolean QDECL VFSPIPE_Close(vfsfile_t *f) { vfspipe_t *p = (vfspipe_t*)f; free(p->data); free(p); + return true; } -qofs_t QDECL VFSPIPE_GetLen(vfsfile_t *f) +static qofs_t QDECL VFSPIPE_GetLen(vfsfile_t *f) { vfspipe_t *p = (vfspipe_t*)f; return p->writepos - p->readpos; } -//unsigned long QDECL VFSPIPE_Tell(vfsfile_t *f) +//static unsigned long QDECL VFSPIPE_Tell(vfsfile_t *f) //{ // return 0; //} -//qboolean QDECL VFSPIPE_Seek(vfsfile_t *f, unsigned long offset) +//static qboolean QDECL VFSPIPE_Seek(vfsfile_t *f, unsigned long offset) //{ // Con_Printf("Seeking is a bad plan, mmkay?\n"); // return false; //} -int QDECL VFSPIPE_ReadBytes(vfsfile_t *f, void *buffer, int len) +static int QDECL VFSPIPE_ReadBytes(vfsfile_t *f, void *buffer, int len) { vfspipe_t *p = (vfspipe_t*)f; if (len > p->writepos - p->readpos) @@ -1062,7 +1073,7 @@ int QDECL VFSPIPE_ReadBytes(vfsfile_t *f, void *buffer, int len) } return len; } -int QDECL VFSPIPE_WriteBytes(vfsfile_t *f, const void *buffer, int len) +static int QDECL VFSPIPE_WriteBytes(vfsfile_t *f, const void *buffer, int len) { vfspipe_t *p = (vfspipe_t*)f; if (p->writepos + len > p->maxlen) diff --git a/engine/http/webgen.c b/engine/http/webgen.c index bece43e4..21b5df56 100644 --- a/engine/http/webgen.c +++ b/engine/http/webgen.c @@ -401,7 +401,7 @@ qofs_t QDECL VFSGen_GetLen(vfsfile_t *f) return g->buffer->len; } -void QDECL VFSGen_Close(vfsfile_t *f) +qboolean QDECL VFSGen_Close(vfsfile_t *f) { int fnum; vfsgen_t *g = (vfsgen_t*)f; @@ -416,6 +416,7 @@ void QDECL VFSGen_Close(vfsfile_t *f) IWebFiles[fnum].buffer = NULL; } Z_Free(g); + return true; } diff --git a/engine/qclib/cmdlib.h b/engine/qclib/cmdlib.h index 652a8e6d..b15eaa2c 100644 --- a/engine/qclib/cmdlib.h +++ b/engine/qclib/cmdlib.h @@ -92,7 +92,7 @@ void ExtractFileExtension (char *path, char *dest); long ParseNum (char *str); -char *QCC_COM_Parse (char *data); +char *QCC_COM_Parse (const char *data); char *QCC_COM_Parse2 (char *data); extern char qcc_token[1024]; diff --git a/engine/qclib/comprout.c b/engine/qclib/comprout.c index c6153122..5f9b169a 100644 --- a/engine/qclib/comprout.c +++ b/engine/qclib/comprout.c @@ -156,7 +156,7 @@ int PDECL Comp_Continue(pubprogfuncs_t *progfuncs) return true; } #endif -pbool CompileFile(progfuncs_t *progfuncs, char *filename) +pbool CompileFile(progfuncs_t *progfuncs, const char *filename) { #if defined(MINIMAL) || defined(OMIT_QCC) return false; diff --git a/engine/qclib/hash.c b/engine/qclib/hash.c index b92a6ce8..9200af15 100644 --- a/engine/qclib/hash.c +++ b/engine/qclib/hash.c @@ -45,7 +45,7 @@ unsigned int Hash_Key(const char *name, unsigned int modulus) return (key%modulus); } -unsigned int Hash_KeyInsensative(const char *name, unsigned int modulus) +unsigned int Hash_KeyInsensitive(const char *name, unsigned int modulus) { //fixme: optimize. unsigned int key; for (key=0;*name; name++) @@ -95,9 +95,9 @@ void *Hash_Get(hashtable_t *table, const char *name) } return NULL; } -void *Hash_GetInsensative(hashtable_t *table, const char *name) +void *Hash_GetInsensitive(hashtable_t *table, const char *name) { - unsigned int bucknum = Hash_KeyInsensative(name, table->numbuckets); + unsigned int bucknum = Hash_KeyInsensitive(name, table->numbuckets); bucket_t *buck; buck = table->bucket[bucknum]; @@ -111,9 +111,9 @@ void *Hash_GetInsensative(hashtable_t *table, const char *name) } return NULL; } -void *Hash_GetInsensativeBucket(hashtable_t *table, const char *name) +void *Hash_GetInsensitiveBucket(hashtable_t *table, const char *name) { - unsigned int bucknum = Hash_KeyInsensative(name, table->numbuckets); + unsigned int bucknum = Hash_KeyInsensitive(name, table->numbuckets); bucket_t *buck; buck = table->bucket[bucknum]; @@ -199,9 +199,9 @@ void *Hash_GetNext(hashtable_t *table, const char *name, void *old) return NULL; } /*Does _NOT_ support items that are added with two names*/ -void *Hash_GetNextInsensative(hashtable_t *table, const char *name, void *old) +void *Hash_GetNextInsensitive(hashtable_t *table, const char *name, void *old) { - unsigned int bucknum = Hash_KeyInsensative(name, table->numbuckets); + unsigned int bucknum = Hash_KeyInsensitive(name, table->numbuckets); bucket_t *buck; buck = table->bucket[bucknum]; @@ -242,9 +242,9 @@ void *Hash_Add(hashtable_t *table, const char *name, void *data, bucket_t *buck) return buck; } -void *Hash_AddInsensative(hashtable_t *table, const char *name, void *data, bucket_t *buck) +void *Hash_AddInsensitive(hashtable_t *table, const char *name, void *data, bucket_t *buck) { - unsigned int bucknum = Hash_KeyInsensative(name, table->numbuckets); + unsigned int bucknum = Hash_KeyInsensitive(name, table->numbuckets); buck->data = data; buck->key.string = name; @@ -292,9 +292,9 @@ void Hash_Remove(hashtable_t *table, const char *name) return; } -void Hash_RemoveDataInsensative(hashtable_t *table, const char *name, void *data) +void Hash_RemoveDataInsensitive(hashtable_t *table, const char *name, void *data) { - unsigned int bucknum = Hash_KeyInsensative(name, table->numbuckets); + unsigned int bucknum = Hash_KeyInsensitive(name, table->numbuckets); bucket_t **link, *buck; for (link = &table->bucket[bucknum]; *link; link = &(*link)->next) diff --git a/engine/qclib/hash.h b/engine/qclib/hash.h index b0a92d6d..a7c9c855 100644 --- a/engine/qclib/hash.h +++ b/engine/qclib/hash.h @@ -26,17 +26,17 @@ void *Hash_Enumerate(hashtable_t *table, void (*callback) (void *ctx, void *data unsigned int Hash_Key(const char *name, unsigned int modulus); void *Hash_GetIdx(hashtable_t *table, unsigned int idx); void *Hash_Get(hashtable_t *table, const char *name); -void *Hash_GetInsensative(hashtable_t *table, const char *name); -void *Hash_GetInsensativeBucket(hashtable_t *table, const char *name); +void *Hash_GetInsensitive(hashtable_t *table, const char *name); +void *Hash_GetInsensitiveBucket(hashtable_t *table, const char *name); void *Hash_GetKey(hashtable_t *table, unsigned int key); void *Hash_GetNext(hashtable_t *table, const char *name, void *old); -void *Hash_GetNextInsensative(hashtable_t *table, const char *name, void *old); +void *Hash_GetNextInsensitive(hashtable_t *table, const char *name, void *old); void *Hash_GetNextKey(hashtable_t *table, unsigned int key, void *old); void *Hash_Add(hashtable_t *table, const char *name, void *data, bucket_t *buck); -void *Hash_AddInsensative(hashtable_t *table, const char *name, void *data, bucket_t *buck); +void *Hash_AddInsensitive(hashtable_t *table, const char *name, void *data, bucket_t *buck); void Hash_Remove(hashtable_t *table, const char *name); void Hash_RemoveData(hashtable_t *table, const char *name, void *data); -void Hash_RemoveDataInsensative(hashtable_t *table, const char *name, void *data); +void Hash_RemoveDataInsensitive(hashtable_t *table, const char *name, void *data); void Hash_RemoveBucket(hashtable_t *table, const char *name, bucket_t *data); void Hash_RemoveKey(hashtable_t *table, unsigned int key); void *Hash_AddKey(hashtable_t *table, unsigned int key, void *data, bucket_t *buck); diff --git a/engine/qclib/initlib.c b/engine/qclib/initlib.c index 878930ce..4ddc07a0 100644 --- a/engine/qclib/initlib.c +++ b/engine/qclib/initlib.c @@ -93,7 +93,7 @@ void *PRAddressableExtend(progfuncs_t *progfuncs, int ammount) /*only do this if the caller states that it can cope with addressable-block relocations/resizes*/ if (externs->addressablerelocated) { -#ifdef _WIN32 +#if defined(_WIN32) && !defined(WINRT) char *newblock; #if 0//def _DEBUG int oldtot = addressablesize; @@ -133,7 +133,7 @@ void *PRAddressableExtend(progfuncs_t *progfuncs, int ammount) prinst.addressableused += ammount; progfuncs->funcs.stringtablesize = prinst.addressableused; -#ifdef _WIN32 +#if defined(_WIN32) && !defined(WINRT) if (!VirtualAlloc (prinst.addressablehunk, prinst.addressableused, MEM_COMMIT, PAGE_READWRITE)) Sys_Error("VirtualAlloc failed. Blame windows."); #endif @@ -400,7 +400,7 @@ void PRAddressableFlush(progfuncs_t *progfuncs, size_t totalammount) // return; } -#ifdef _WIN32 +#if defined(_WIN32) && !defined(WINRT) if (prinst.addressablehunk && prinst.addressablesize != totalammount) { VirtualFree(prinst.addressablehunk, 0, MEM_RELEASE); //doesn't this look complicated? :p @@ -482,7 +482,7 @@ void PDECL PR_Configure (pubprogfuncs_t *ppf, size_t addressable_size, int max_p PRHunkFree(progfuncs, 0); //clear mem - our hunk may not be a real hunk. if (addressable_size<0 || addressable_size == (size_t)-1) { -#ifdef _WIN64 +#if defined(_WIN64) && !defined(WINRT) addressable_size = 0x80000000; //use of virtual address space rather than physical memory means we can just go crazy and use the max of 2gb. #else addressable_size = 32*1024*1024; @@ -809,7 +809,7 @@ int PDECL EdictToProgs (pubprogfuncs_t *ppf, struct edict_s *ed) return EDICT_TO_PROG(progfuncs, ed); } -string_t PDECL PR_StringToProgs (pubprogfuncs_t *ppf, char *str) +string_t PDECL PR_StringToProgs (pubprogfuncs_t *ppf, const char *str) { progfuncs_t *progfuncs = (progfuncs_t*)ppf; char **ntable; @@ -832,7 +832,7 @@ string_t PDECL PR_StringToProgs (pubprogfuncs_t *ppf, char *str) if (free != -1) { i = free; - prinst.allocedstrings[i] = str; + prinst.allocedstrings[i] = (char*)str; return (string_t)((unsigned int)i | STRING_STATIC); } @@ -849,13 +849,18 @@ string_t PDECL PR_StringToProgs (pubprogfuncs_t *ppf, char *str) { if (!prinst.allocedstrings[i]) { - prinst.allocedstrings[i] = str; + prinst.allocedstrings[i] = (char*)str; return (string_t)((unsigned int)i | STRING_STATIC); } } return 0; } +//if ed is null, fld points to a global. if str_is_static, then s doesn't need its own memory allocated. +void PDECL PR_SetStringField(pubprogfuncs_t *progfuncs, struct edict_s *ed, string_t *fld, const char *str, pbool str_is_static) +{ + *fld = PR_StringToProgs(progfuncs, str); +} char *PDECL PR_RemoveProgsString (pubprogfuncs_t *ppf, string_t str) { @@ -1124,7 +1129,8 @@ pubprogfuncs_t deffuncs = { PR_GenerateStatementString, ED_FieldInfo, PR_UglyValueString, - ED_ParseEval + ED_ParseEval, + PR_SetStringField }; static int PDECL qclib_null_printf(const char *s, ...) { @@ -1220,7 +1226,7 @@ static void PDECL PR_CloseProgs(pubprogfuncs_t *ppf) PRHunkFree(inst, 0); -#ifdef _WIN32 +#if defined(_WIN32) && !defined(WINRT) VirtualFree(inst->inst.addressablehunk, 0, MEM_RELEASE); //doesn't this look complicated? :p #else free(inst->inst.addressablehunk); diff --git a/engine/qclib/pr_edict.c b/engine/qclib/pr_edict.c index ae9aa04a..26873aff 100644 --- a/engine/qclib/pr_edict.c +++ b/engine/qclib/pr_edict.c @@ -1081,7 +1081,7 @@ Can parse either fields or globals returns false if error ============= */ -pbool PDECL ED_ParseEval (pubprogfuncs_t *ppf, eval_t *eval, int type, char *s) +pbool PDECL ED_ParseEval (pubprogfuncs_t *ppf, eval_t *eval, int type, const char *s) { progfuncs_t *progfuncs = (progfuncs_t*)ppf; int i; @@ -1257,7 +1257,7 @@ Used for initial level load and for savegames. ==================== */ #if 1 -char *ED_ParseEdict (progfuncs_t *progfuncs, char *data, edictrun_t *ent) +const char *ED_ParseEdict (progfuncs_t *progfuncs, const char *data, edictrun_t *ent) { fdef_t *key; pbool init; @@ -1787,11 +1787,11 @@ char *PDECL PR_SaveEnts(pubprogfuncs_t *ppf, char *buf, int *bufofs, int bufmax, int header_crc; //if 'general' block is found, this is a compleate state, otherwise, we should spawn entities like -int PDECL PR_LoadEnts(pubprogfuncs_t *ppf, char *file, float killonspawnflags) +int PDECL PR_LoadEnts(pubprogfuncs_t *ppf, const char *file, float killonspawnflags) { progfuncs_t *progfuncs = (progfuncs_t*)ppf; eval_t *fulldata; //this is part of FTE_FULLSPAWNDATA - char *datastart; + const char *datastart; eval_t *selfvar = NULL; eval_t *var; @@ -2159,11 +2159,10 @@ int PDECL PR_LoadEnts(pubprogfuncs_t *ppf, char *file, float killonspawnflags) if (externs->entspawn) externs->entspawn((struct edict_s *) ed, true); - sv_num_edicts = numents; ed->isfree = false; file = ED_ParseEdict (progfuncs, file, ed); } - numents++; + sv_num_edicts = ++numents; continue; } @@ -2382,11 +2381,11 @@ char *PDECL PR_SaveEnt (pubprogfuncs_t *ppf, char *buf, int *size, int maxsize, return buf; } -struct edict_s *PDECL PR_RestoreEnt (pubprogfuncs_t *ppf, char *buf, int *size, struct edict_s *ed) +struct edict_s *PDECL PR_RestoreEnt (pubprogfuncs_t *ppf, const char *buf, int *size, struct edict_s *ed) { progfuncs_t *progfuncs = (progfuncs_t*)ppf; edictrun_t *ent; - char *start = buf; + const char *start = buf; buf = QCC_COM_Parse(buf); //read the key if (!buf || !*qcc_token) @@ -2483,7 +2482,7 @@ char *decode(int complen, int len, int method, char *info, char *buffer); PR_LoadProgs =============== */ -int PR_ReallyLoadProgs (progfuncs_t *progfuncs, char *filename, int headercrc, progstate_t *progstate, pbool complain) +int PR_ReallyLoadProgs (progfuncs_t *progfuncs, const char *filename, int headercrc, progstate_t *progstate, pbool complain) { unsigned int i, type; // extensionbuiltin_t *eb; diff --git a/engine/qclib/pr_multi.c b/engine/qclib/pr_multi.c index 2f13c1aa..172f049a 100644 --- a/engine/qclib/pr_multi.c +++ b/engine/qclib/pr_multi.c @@ -94,7 +94,7 @@ pbool PR_SwitchProgsParms(progfuncs_t *progfuncs, progsnum_t newpr) //from 2 to return PR_SwitchProgs(progfuncs, newpr); } -progsnum_t PDECL PR_LoadProgs(pubprogfuncs_t *ppf, char *s, int headercrc, builtin_t *builtins, int numbuiltins) +progsnum_t PDECL PR_LoadProgs(pubprogfuncs_t *ppf, const char *s, int headercrc, builtin_t *builtins, int numbuiltins) { progfuncs_t *progfuncs = (progfuncs_t*)ppf; unsigned int a; diff --git a/engine/qclib/progsint.h b/engine/qclib/progsint.h index 51c6a81d..93c1d587 100644 --- a/engine/qclib/progsint.h +++ b/engine/qclib/progsint.h @@ -271,9 +271,9 @@ int PDECL Comp_Continue(pubprogfuncs_t *progfuncs); pbool PDECL PR_SetWatchPoint(pubprogfuncs_t *progfuncs, char *key); char *PDECL PR_EvaluateDebugString(pubprogfuncs_t *progfuncs, char *key); char *PDECL PR_SaveEnts(pubprogfuncs_t *progfuncs, char *mem, int *size, int maxsize, int mode); -int PDECL PR_LoadEnts(pubprogfuncs_t *progfuncs, char *file, float killonspawnflags); +int PDECL PR_LoadEnts(pubprogfuncs_t *progfuncs, const char *file, float killonspawnflags); char *PDECL PR_SaveEnt (pubprogfuncs_t *progfuncs, char *buf, int *size, int maxsize, struct edict_s *ed); -struct edict_s *PDECL PR_RestoreEnt (pubprogfuncs_t *progfuncs, char *buf, int *size, struct edict_s *ed); +struct edict_s *PDECL PR_RestoreEnt (pubprogfuncs_t *progfuncs, const char *buf, int *size, struct edict_s *ed); void PDECL PR_StackTrace (pubprogfuncs_t *progfuncs); extern int noextensions; @@ -355,8 +355,8 @@ typedef struct extensionbuiltin_s { void PR_Init (void); void PDECL PR_ExecuteProgram (pubprogfuncs_t *progfuncs, func_t fnum); -int PDECL PR_LoadProgs(pubprogfuncs_t *progfncs, char *s, int headercrc, builtin_t *builtins, int numbuiltins); -int PR_ReallyLoadProgs (progfuncs_t *progfuncs, char *filename, int headercrc, progstate_t *progstate, pbool complain); +int PDECL PR_LoadProgs(pubprogfuncs_t *progfncs, const char *s, int headercrc, builtin_t *builtins, int numbuiltins); +int PR_ReallyLoadProgs (progfuncs_t *progfuncs, const char *filename, int headercrc, progstate_t *progstate, pbool complain); void *PRHunkAlloc(progfuncs_t *progfuncs, int ammount, char *name); @@ -370,7 +370,7 @@ char *PDECL ED_NewString (pubprogfuncs_t *ppf, const char *string, int minlength void PDECL ED_Print (pubprogfuncs_t *progfuncs, struct edict_s *ed); //void ED_Write (FILE *f, edictrun_t *ed); -char *ED_ParseEdict (progfuncs_t *progfuncs, char *data, edictrun_t *ent); +const char *ED_ParseEdict (progfuncs_t *progfuncs, const char *data, edictrun_t *ent); //void ED_WriteGlobals (FILE *f); void ED_ParseGlobals (char *data); @@ -434,7 +434,7 @@ eval_t *PDECL QC_GetEdictFieldValue(pubprogfuncs_t *progfuncs, struct edict_s *e void PDECL PR_GenerateStatementString (pubprogfuncs_t *progfuncs, int statementnum, char *out, int outlen); fdef_t *PDECL ED_FieldInfo (pubprogfuncs_t *progfuncs, unsigned int *count); char *PDECL PR_UglyValueString (pubprogfuncs_t *progfuncs, etype_t type, eval_t *val); -pbool PDECL ED_ParseEval (pubprogfuncs_t *progfuncs, eval_t *eval, int type, char *s); +pbool PDECL ED_ParseEval (pubprogfuncs_t *progfuncs, eval_t *eval, int type, const char *s); #endif @@ -483,7 +483,7 @@ ddef16_t *ED_FindGlobal16 (progfuncs_t *progfuncs, char *name); ddef32_t *ED_FindGlobal32 (progfuncs_t *progfuncs, char *name); ddef32_t *ED_GlobalAtOfs32 (progfuncs_t *progfuncs, unsigned int ofs); -string_t PDECL PR_StringToProgs (pubprogfuncs_t *inst, char *str); +string_t PDECL PR_StringToProgs (pubprogfuncs_t *inst, const char *str); char *ASMCALL PR_StringToNative (pubprogfuncs_t *inst, string_t str); void PR_FreeTemps (progfuncs_t *progfuncs, int depth); @@ -491,13 +491,13 @@ void PR_FreeTemps (progfuncs_t *progfuncs, int depth); char *PR_GlobalString (progfuncs_t *progfuncs, int ofs); char *PR_GlobalStringNoContents (progfuncs_t *progfuncs, int ofs); -pbool CompileFile(progfuncs_t *progfuncs, char *filename); +pbool CompileFile(progfuncs_t *progfuncs, const char *filename); struct jitstate; struct jitstate *PR_GenerateJit(progfuncs_t *progfuncs); void PR_EnterJIT(progfuncs_t *progfuncs, struct jitstate *jitstate, int statement); void PR_CloseJit(struct jitstate *jit); -char *QCC_COM_Parse (char *data); +char *QCC_COM_Parse (const char *data); extern char qcc_token[1024]; #endif diff --git a/engine/qclib/progslib.h b/engine/qclib/progslib.h index 99e5800c..d79e3860 100644 --- a/engine/qclib/progslib.h +++ b/engine/qclib/progslib.h @@ -69,7 +69,7 @@ struct pubprogfuncs_s void (PDECL *CloseProgs) (pubprogfuncs_t *inst); void (PDECL *Configure) (pubprogfuncs_t *prinst, size_t addressablesize, int max_progs); //configure buffers and memory. Used to reset and must be called first. Flushes a running VM. - progsnum_t (PDECL *LoadProgs) (pubprogfuncs_t *prinst, char *s, int headercrc, builtin_t *builtins, int numbuiltins); //load a progs + progsnum_t (PDECL *LoadProgs) (pubprogfuncs_t *prinst, const char *s, int headercrc, builtin_t *builtins, int numbuiltins); //load a progs int (PDECL *InitEnts) (pubprogfuncs_t *prinst, int max_ents); //returns size of edicts for use with nextedict macro void (PDECL *ExecuteProgram) (pubprogfuncs_t *prinst, func_t fnum); //start execution struct globalvars_s *(PDECL *globals) (pubprogfuncs_t *prinst, progsnum_t num); //get the globals of a progs @@ -103,7 +103,7 @@ struct pubprogfuncs_s int (PDECL *load_ents) (pubprogfuncs_t *prinst, char *s, float killonspawnflags); //restore the entire progs state (or just add some more ents) (returns edicts ize) char *(PDECL *saveent) (pubprogfuncs_t *prinst, char *buf, int *size, int maxsize, struct edict_s *ed); //will save just one entities vars - struct edict_s *(PDECL *restoreent) (pubprogfuncs_t *prinst, char *buf, int *size, struct edict_s *ed); //will restore the entity that had it's values saved (can use NULL for ed) + struct edict_s *(PDECL *restoreent) (pubprogfuncs_t *prinst, const char *buf, int *size, struct edict_s *ed); //will restore the entity that had it's values saved (can use NULL for ed) union eval_s *(PDECL *FindGlobal) (pubprogfuncs_t *prinst, const char *name, progsnum_t num, etype_t *type); //find a pointer to the globals value char *(PDECL *AddString) (pubprogfuncs_t *prinst, const char *val, int minlength, pbool demarkup); //dump a string into the progs memory (for setting globals and whatnot) @@ -148,8 +148,8 @@ struct pubprogfuncs_s string_t (PDECL *TempString) (pubprogfuncs_t *prinst, const char *str); - string_t (PDECL *StringToProgs) (pubprogfuncs_t *prinst, char *str); - char *(ASMCALL *StringToNative) (pubprogfuncs_t *prinst, string_t str); + string_t (PDECL *StringToProgs) (pubprogfuncs_t *prinst, const char *str); + const char *(ASMCALL *StringToNative) (pubprogfuncs_t *prinst, string_t str); int (PDECL *QueryField) (pubprogfuncs_t *prinst, unsigned int fieldoffset, etype_t *type, char **name, evalc_t *fieldcache); //find info on a field definition at an offset @@ -169,7 +169,8 @@ struct pubprogfuncs_s void (PDECL *GenerateStatementString) (pubprogfuncs_t *progfuncs, int statementnum, char *out, int outlen); fdef_t *(PDECL *FieldInfo) (pubprogfuncs_t *progfuncs, unsigned int *count); char *(PDECL *UglyValueString) (pubprogfuncs_t *progfuncs, etype_t type, union eval_s *val); - pbool (PDECL *ParseEval) (pubprogfuncs_t *progfuncs, union eval_s *eval, int type, char *s); + pbool (PDECL *ParseEval) (pubprogfuncs_t *progfuncs, union eval_s *eval, int type, const char *s); + void (PDECL *SetStringField) (pubprogfuncs_t *progfuncs, struct edict_s *ed, string_t *fld, const char *str, pbool str_is_static); //if ed is null, fld points to a global. if str_is_static, then s doesn't need its own memory allocated. }; typedef struct progexterns_s { diff --git a/engine/qclib/qcc.h b/engine/qclib/qcc.h index 5ad13ebe..fdbee293 100644 --- a/engine/qclib/qcc.h +++ b/engine/qclib/qcc.h @@ -519,7 +519,7 @@ extern pbool pr_subscopedlocals; extern pbool flag_ifstring; extern pbool flag_iffloat; extern pbool flag_acc; -extern pbool flag_caseinsensative; +extern pbool flag_caseinsensitive; extern pbool flag_laxcasts; extern pbool flag_hashonly; extern pbool flag_fasttrackarrays; @@ -647,7 +647,7 @@ enum { WARN_KEYWORDDISABLED, WARN_ENUMFLAGS_NOTINTEGER, WARN_ENUMFLAGS_NOTBINARY, - WARN_CASEINSENSATIVEFRAMEMACRO, + WARN_CASEINSENSITIVEFRAMEMACRO, WARN_DUPLICATELABEL, WARN_DUPLICATEMACRO, WARN_ASSIGNMENTINCONDITIONAL, diff --git a/engine/qclib/qcc_cmdlib.c b/engine/qclib/qcc_cmdlib.c index f2ad6d6b..645dc5f3 100644 --- a/engine/qclib/qcc_cmdlib.c +++ b/engine/qclib/qcc_cmdlib.c @@ -206,7 +206,7 @@ COM_Parse Parse a token out of a string ============== */ -char *QCC_COM_Parse (char *data) +char *QCC_COM_Parse (const char *data) { int c; int len; @@ -264,12 +264,12 @@ skipwhite: else if (c=='\"') { qcc_token[len] = 0; - return data; + return (char*)data; } else if (c=='\0'||c=='\n') { qcc_token[len] = 0; - return data; + return (char*)data; } if (len >= sizeof(qcc_token)-1) ; @@ -285,7 +285,7 @@ skipwhite: qcc_token[len] = c; len++; qcc_token[len] = 0; - return data+1; + return (char*)data+1; } // parse a regular word @@ -302,7 +302,7 @@ skipwhite: } while (c && !qcc_iswhite(c)); qcc_token[len] = 0; - return data; + return (char*)data; } //more C tokens... diff --git a/engine/qclib/qcc_pr_comp.c b/engine/qclib/qcc_pr_comp.c index 66ddcd1f..07def2af 100644 --- a/engine/qclib/qcc_pr_comp.c +++ b/engine/qclib/qcc_pr_comp.c @@ -74,7 +74,7 @@ pbool pr_subscopedlocals; //causes locals to be valid ONLY within their statemen pbool flag_ifstring; //makes if (blah) equivelent to if (blah != "") which resolves some issues in multiprogs situations. pbool flag_iffloat; //use an op_if_f instruction instead of op_if so if(-0) evaluates to false. pbool flag_acc; //reacc like behaviour of src files (finds *.qc in start dir and compiles all in alphabetical order) -pbool flag_caseinsensative; //symbols will be matched to an insensative case if the specified case doesn't exist. This should b usable for any mod +pbool flag_caseinsensitive; //symbols will be matched to an insensitive case if the specified case doesn't exist. This should b usable for any mod pbool flag_laxcasts; //Allow lax casting. This'll produce loadsa warnings of course. But allows compilation of certain dodgy code. pbool flag_hashonly; //Allows use of only #constant for precompiler constants, allows certain preqcc using mods to compile pbool flag_fasttrackarrays; //Faster arrays, dynamically detected, activated only in supporting engines. @@ -6936,7 +6936,7 @@ QCC_ref_t *QCC_PR_RefExpression (QCC_ref_t *retbuf, int priority, int exprflags) } else { - if (rhsd->constant && rhsd->type->type == ev_integer && !G_INT(rhsd->ofs)) + if (rhsd->constant && (rhsd->type->type == ev_integer || rhsd->type->type == ev_float) && !G_INT(rhsd->ofs) && !STRCMP(rhsd->name, "IMMEDIATE")) { if (lhsr->cast->type == ev_vector) rhsd = QCC_MakeVectorConst(0,0,0); @@ -6972,7 +6972,7 @@ QCC_ref_t *QCC_PR_RefExpression (QCC_ref_t *retbuf, int priority, int exprflags) if (op->associative!=ASSOC_LEFT) { - QCC_PR_ParseError(ERR_INTERNAL, "internal error: shuold be unreachable\n"); + QCC_PR_ParseError(ERR_INTERNAL, "internal error: should be unreachable\n"); } else { @@ -6985,7 +6985,7 @@ QCC_ref_t *QCC_PR_RefExpression (QCC_ref_t *retbuf, int priority, int exprflags) } if (priority > 1 && exprflags & EXPR_WARN_ABOVE_1) - QCC_PR_ParseWarning(WARN_UNARYNOTSCOPE, "You may wish to add brackets after that ! operator"); + QCC_PR_ParseWarning(WARN_UNARYNOTSCOPE, "unary-not applies to non-unary expression"); break; } diff --git a/engine/qclib/qcc_pr_lex.c b/engine/qclib/qcc_pr_lex.c index 9f541b4f..c4290ce5 100644 --- a/engine/qclib/qcc_pr_lex.c +++ b/engine/qclib/qcc_pr_lex.c @@ -2097,7 +2097,7 @@ int QCC_PR_FindMacro (char *name) { if (!stricmp (name, pr_framemacros[i])) { - QCC_PR_ParseWarning(WARN_CASEINSENSATIVEFRAMEMACRO, "Case insensative frame macro"); + QCC_PR_ParseWarning(WARN_CASEINSENSITIVEFRAMEMACRO, "Case insensitive frame macro"); return pr_framemacrovalue[i]; } } @@ -3462,7 +3462,7 @@ pbool QCC_PR_CheckName(char *string) { if (pr_token_type != tt_name) return false; - if (flag_caseinsensative) + if (flag_caseinsensitive) { if (stricmp (string, pr_token)) return false; @@ -3480,7 +3480,7 @@ pbool QCC_PR_CheckKeyword(int keywordenabled, char *string) { if (!keywordenabled) return false; - if (flag_caseinsensative) + if (flag_caseinsensitive) { if (stricmp (string, pr_token)) return false; diff --git a/engine/qclib/qccmain.c b/engine/qclib/qccmain.c index d4c61458..8983bf8a 100644 --- a/engine/qclib/qccmain.c +++ b/engine/qclib/qccmain.c @@ -175,7 +175,7 @@ struct { {" F302", WARN_UNINITIALIZED}, {" F303", WARN_EVILPREPROCESSOR}, {" F304", WARN_UNARYNOTSCOPE}, - {" F305", WARN_CASEINSENSATIVEFRAMEMACRO}, + {" F305", WARN_CASEINSENSITIVEFRAMEMACRO}, {" F306", WARN_SAMENAMEASGLOBAL}, {" F307", WARN_STRICTTYPEMISMATCH}, @@ -291,8 +291,8 @@ compiler_flag_t compiler_flag[] = { {&writeasm, 0, "wasm", "Dump Assembler", "Writes out a qc.asm which contains all your functions but in assembler. This is a great way to look for bugs in fteqcc, but can also be used to see exactly what your functions turn into, and thus how to optimise statements better."}, //spit out a qc.asm file, containing an assembler dump of the ENTIRE progs. (Doesn't include initialisation of constants) {&flag_ifstring, FLAG_MIDCOMPILE,"ifstring", "if(string) fix", "Causes if(string) to behave identically to if(string!="") This is most useful with addons of course, but also has adverse effects with FRIK_FILE's fgets, where it becomes impossible to determin the end of the file. In such a case, you can still use asm {IF string 2;RETURN} to detect eof and leave the function."}, //correction for if(string) no-ifstring to get the standard behaviour. {&flag_iffloat, FLAG_MIDCOMPILE,"iffloat","if(-0.0) fix","Fixes certain floating point logic."}, - {&flag_acc, 0, "acc", "Reacc support", "Reacc is a pascall like compiler. It was released before the Quake source was released. This flag has a few effects. It sorts all qc files in the current directory into alphabetical order to compile them. It also allows Reacc global/field distinctions, as well as allows ¦ as EOF. Whilst case insensativity and lax type checking are supported by reacc, they are seperate compiler flags in fteqcc."}, //reacc like behaviour of src files. - {&flag_caseinsensative, 0, "caseinsens", "Case insensativity", "Causes fteqcc to become case insensative whilst compiling names. It's generally not advised to use this as it compiles a little more slowly and provides little benefit. However, it is required for full reacc support."}, //symbols will be matched to an insensative case if the specified case doesn't exist. This should b usable for any mod + {&flag_acc, 0, "acc", "Reacc support", "Reacc is a pascall like compiler. It was released before the Quake source was released. This flag has a few effects. It sorts all qc files in the current directory into alphabetical order to compile them. It also allows Reacc global/field distinctions, as well as allows ¦ as EOF. Whilst case insensitivity and lax type checking are supported by reacc, they are seperate compiler flags in fteqcc."}, //reacc like behaviour of src files. + {&flag_caseinsensitive, 0, "caseinsens", "Case insensitivity", "Causes fteqcc to become case insensitive whilst compiling names. It's generally not advised to use this as it compiles a little more slowly and provides little benefit. However, it is required for full reacc support."}, //symbols will be matched to an insensitive case if the specified case doesn't exist. This should b usable for any mod {&flag_laxcasts, FLAG_MIDCOMPILE,"lax", "Lax type checks", "Disables many errors (generating warnings instead) when function calls or operations refer to two normally incompatible types. This is required for reacc support, and can also allow certain (evil) mods to compile that were originally written for frikqcc."}, //Allow lax casting. This'll produce loadsa warnings of course. But allows compilation of certain dodgy code. {&flag_hashonly, FLAG_MIDCOMPILE,"hashonly", "Hash-only constants", "Allows use of only #constant for precompiler constants, allows certain preqcc using mods to compile"}, {&opt_logicops, FLAG_MIDCOMPILE,"lo", "Logic ops", "This changes the behaviour of your code. It generates additional if operations to early-out in if statements. With this flag, the line if (0 && somefunction()) will never call the function. It can thus be considered an optimisation. However, due to the change of behaviour, it is not considered so by fteqcc. Note that due to inprecisions with floats, this flag can cause runaway loop errors within the player walk and run functions (without iffloat also enabled). This code is advised:\nplayer_stand1:\n if (self.velocity_x || self.velocity_y)\nplayer_run\n if (!(self.velocity_x || self.velocity_y))"}, @@ -2972,7 +2972,7 @@ void QCC_SetDefaultProperties (void) qccwarningaction[WARN_EVILPREPROCESSOR] = WA_WARN;//FIXME: make into WA_ERROR; if (QCC_CheckParm("-h2")) - qccwarningaction[WARN_CASEINSENSATIVEFRAMEMACRO] = WA_IGNORE; + qccwarningaction[WARN_CASEINSENSITIVEFRAMEMACRO] = WA_IGNORE; //Check the command line QCC_PR_CommandLinePrecompilerOptions(); @@ -2996,7 +2996,8 @@ void QCC_SetDefaultProperties (void) } //builds a list of files, pretends that they came from a progs.src -int QCC_FindQCFiles() +//FIXME: use sourcedir! +int QCC_FindQCFiles(const char *sourcedir) { #ifdef _WIN32 WIN32_FIND_DATA fd; @@ -3010,7 +3011,7 @@ int QCC_FindQCFiles() qccmsrc = qccHunkAlloc(8192); strcat(qccmsrc, "progs.dat\n");//"#pragma PROGS_DAT progs.dat\n"); -#ifdef _WIN32 +#if defined(_WIN32) && !defined(WINRT) h = FindFirstFile("*.qc", &fd); if (h == INVALID_HANDLE_VALUE) return 0; @@ -3281,13 +3282,13 @@ memset(pr_immediate_string, 0, sizeof(pr_immediate_string)); return; } - if (flag_caseinsensative) + if (flag_caseinsensitive) { - printf("Compiling without case sensativity\n"); - pHash_Get = &Hash_GetInsensative; - pHash_GetNext = &Hash_GetNextInsensative; - pHash_Add = &Hash_AddInsensative; - pHash_RemoveData = &Hash_RemoveDataInsensative; + printf("Compiling without case sensitivity\n"); + pHash_Get = &Hash_GetInsensitive; + pHash_GetNext = &Hash_GetNextInsensitive; + pHash_Add = &Hash_AddInsensitive; + pHash_RemoveData = &Hash_RemoveDataInsensitive; } p = QCC_CheckParm ("-src"); diff --git a/engine/server/net_preparse.c b/engine/server/net_preparse.c index 7a98a5ad..4579d751 100644 --- a/engine/server/net_preparse.c +++ b/engine/server/net_preparse.c @@ -878,7 +878,7 @@ void NPP_NQCheckDest(int dest) destprim = &writedest->prim; } } -void NPP_AddData(void *data, int len) +void NPP_AddData(const void *data, int len) { if (bufferlen+len > sizeof(buffer)) Sys_Error("Preparse buffer was filled\n"); @@ -1260,8 +1260,6 @@ void NPP_NQWriteAngle(int dest, float in) //replacement write func (nq to qw) { char data = (int)(in*256/360) & 255; NPP_NQCheckDest(dest); - if (!bufferlen) - Con_Printf("NQWriteAngle: Messages should start with WriteByte\n"); #ifdef NQPROT if (cldest) @@ -1279,6 +1277,9 @@ void NPP_NQWriteAngle(int dest, float in) //replacement write func (nq to qw) MSG_WriteAngle (NQWriteDest(dest), in); #endif + if (!bufferlen) + Con_Printf("NQWriteAngle: Messages should start with WriteByte\n"); + if (destprim->anglesize==2) { coorddata cd = MSG_ToAngle(in, destprim->anglesize); @@ -1335,7 +1336,7 @@ void NPP_NQWriteCoord(int dest, float in) //replacement write func (nq to qw) } NPP_NQCheckFlush(); } -void NPP_NQWriteString(int dest, char *data) //replacement write func (nq to qw) +void NPP_NQWriteString(int dest, const char *data) //replacement write func (nq to qw) { NPP_NQCheckDest(dest); if (!bufferlen) @@ -2002,7 +2003,7 @@ void NPP_QWWriteCoord(int dest, float in) //replacement write func (nq to qw) NPP_QWWriteShort(dest, datas); } } -void NPP_QWWriteString(int dest, char *data) //replacement write func (nq to qw) +void NPP_QWWriteString(int dest, const char *data) //replacement write func (nq to qw) { #if 0 //the slow but guarenteed routine diff --git a/engine/server/pr_cmds.c b/engine/server/pr_cmds.c index 72e94bdc..35a946c5 100644 --- a/engine/server/pr_cmds.c +++ b/engine/server/pr_cmds.c @@ -41,8 +41,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "pr_common.h" //okay, so these are a quick but easy hack -int PR_EnableEBFSBuiltin(char *name, int binum); -int PR_CSQC_BuiltinValid(char *name, int num); +int PR_EnableEBFSBuiltin(const char *name, int binum); +int PR_CSQC_BuiltinValid(const char *name, int num); /*cvars for the gamecode only*/ cvar_t nomonsters = CVAR("nomonsters", "0"); @@ -84,10 +84,7 @@ cvar_t pr_compatabilitytest = CVARFD("pr_compatabilitytest", "0", CVAR_LATCH, "O cvar_t pr_ssqc_coreonerror = CVAR("pr_coreonerror", "1"); -cvar_t pr_droptofloorunits = CVAR("pr_droptofloorunits", ""); - cvar_t sv_gameplayfix_honest_tracelines = CVAR("sv_gameplayfix_honest_tracelines", "1"); -cvar_t sv_gameplayfix_blowupfallenzombies = CVARD("sv_gameplayfix_blowupfallenzombies", "0", "Allow findradius to find non-solid entities. This may break certain mods."); cvar_t sv_gameplayfix_setmodelrealbox = CVARD("sv_gameplayfix_setmodelrealbox", "0", "Vanilla setmodel will setsize the entity to a hardcoded size for non-bsp models. This cvar will always use the real size of the model instead, but will require that the server actually loads the model."); cvar_t sv_gameplayfix_setmodelsize_qw = CVARD("sv_gameplayfix_setmodelsize_qw", "0", "The setmodel builtin will act as a setsize for QuakeWorld mods also."); @@ -167,8 +164,6 @@ func_t SpectatorDisconnect; //QW func_t SV_PlayerPhysicsQC; //DP's DP_SV_PLAYERPHYSICS extension func_t EndFrameQC; //a common extension -qboolean pr_items2; //hipnotic (or was it rogue?) - globalptrs_t realpr_global_ptrs; globalptrs_t *pr_global_ptrs = &realpr_global_ptrs; @@ -509,7 +504,7 @@ model_t *SVPR_GetCModel(world_t *w, int modelindex) if ((unsigned int)modelindex < MAX_MODELS) { if (!sv.models[modelindex] && sv.strings.model_precache[modelindex]) - sv.models[modelindex] = Mod_ForName(sv.strings.model_precache[modelindex], false); + sv.models[modelindex] = Mod_ForName(sv.strings.model_precache[modelindex], MLV_WARN); return sv.models[modelindex]; } else @@ -627,15 +622,21 @@ void PR_Deinit(void) if (svprogfuncs) { PR_Common_Shutdown(svprogfuncs, false); - if (svprogfuncs->parms) + if (svprogfuncs->CloseProgs) svprogfuncs->CloseProgs(svprogfuncs); svprogfuncs=NULL; for (i = 0; i < MAX_LIGHTSTYLES; i++) { - BZ_Free(sv.strings.lightstyles[i]); + BZ_Free((void*)sv.strings.lightstyles[i]); sv.strings.lightstyles[i] = NULL; } + + for (i = 0; i < svs.allocated_client_slots; i++) + { + svs.clients[i].name = svs.clients[i].namebuf; + svs.clients[i].team = svs.clients[i].teambuf; + } } #ifdef USEODE @@ -744,6 +745,9 @@ void PR_LoadGlabalStruct(void) memset(&evalc_idealpitch, 0, sizeof(evalc_idealpitch)); memset(&evalc_pitch_speed, 0, sizeof(evalc_pitch_speed)); + pr_global_ptrs->serverid = (float *)PR_FindGlobal(svprogfuncs, "serverid", 0, NULL); + if (pr_global_ptrs->serverid) + *pr_global_ptrs->serverid = svs.clusterserverid; for (i = 0; i < NUM_SPAWN_PARMS; i++) pr_global_ptrs->spawnparamglobals[i] = (float *)PR_FindGlobal(svprogfuncs, va("parm%i", i+1), 0, NULL); @@ -846,7 +850,8 @@ void PR_LoadGlabalStruct(void) svprogfuncs->AddSharedVar(svprogfuncs, (int *)(pr_global_ptrs)->other-v, 1); svprogfuncs->AddSharedVar(svprogfuncs, (int *)(pr_global_ptrs)->time-v, 1); - pr_items2 = !!PR_FindGlobal(svprogfuncs, "items2", 0, NULL); + //test the global rather than the field - fte internally always has the field. + sv.haveitems2 = !!PR_FindGlobal(svprogfuncs, "items2", 0, NULL); SV_ClearQCStats(); @@ -912,7 +917,7 @@ void PR_LoadGlabalStruct(void) } } -progsnum_t AddProgs(char *name) +progsnum_t AddProgs(const char *name) { float fl; func_t f; @@ -1312,10 +1317,7 @@ void PR_Init(void) Cvar_Register (&pr_ssqc_coreonerror, cvargroup_progs); Cvar_Register (&pr_ssqc_memsize, cvargroup_progs); - Cvar_Register (&pr_droptofloorunits, cvargroup_progs); - Cvar_Register (&sv_gameplayfix_honest_tracelines, cvargroup_progs); - Cvar_Register (&sv_gameplayfix_blowupfallenzombies, cvargroup_progs); Cvar_Register (&sv_gameplayfix_setmodelrealbox, cvargroup_progs); Cvar_Register (&sv_gameplayfix_setmodelsize_qw, cvargroup_progs); @@ -1865,7 +1867,7 @@ qboolean PR_UserCmd(char *s) if (gfuncs.UserCmd && pr_imitatemvdsv.value >= 0) { //we didn't recognise it. see if the mod does. - char *arg0; + const char *arg0; //ktpro bug warning: //admin + judge. I don't know the exact rules behind this bug, so I just ban the entire command //I can't be arsed detecting ktpro specifically, so assume we're always running ktpro @@ -2155,7 +2157,7 @@ int externcallsdepth; float PR_LoadAditionalProgs(char *s); static void QCBUILTIN PF_addprogs(pubprogfuncs_t *prinst, globalvars_t *pr_globals) { - char *s = PR_GetStringOfs(prinst, OFS_PARM0); + const char *s = PR_GetStringOfs(prinst, OFS_PARM0); if (!s || !*s) { G_PROG(OFS_RETURN)=-1; @@ -2319,14 +2321,17 @@ setmodel(entity, model) Also sets size, mins, and maxs for inline bmodels ================= */ -void PF_setmodel_Internal (pubprogfuncs_t *prinst, edict_t *e, char *m) +void PF_setmodel_Internal (pubprogfuncs_t *prinst, edict_t *e, const char *m) { int i; model_t *mod; - if (e->readonly) + if (!e || e->readonly) { - Con_Printf("setmodel on entity %i\n", e->entnum); + if (!e) + Con_Printf("setmodel on invalid entity\n"); + else + Con_Printf("setmodel on entity %i\n", e->entnum); return; } @@ -2381,13 +2386,13 @@ void PF_setmodel_Internal (pubprogfuncs_t *prinst, edict_t *e, char *m) } } - e->v->model = PR_SetString(prinst, m); + prinst->SetStringField(prinst, e, &e->v->model, m, true); e->v->modelindex = i; // if it is an inline model, get the size information for it if (m && (m[0] == '*' || (*m&&progstype == PROG_H2))) { - mod = Mod_ForName (m, false); + mod = Mod_ForName (m, MLV_WARN); if (mod) { VectorCopy (mod->mins, e->v->mins); @@ -2425,7 +2430,7 @@ void PF_setmodel_Internal (pubprogfuncs_t *prinst, edict_t *e, char *m) //qw dedicated servers only load bsps (better) if (mod) { - mod = Mod_ForName (m, false); + mod = Mod_ForName (m, MLV_WARN); if (mod) { VectorCopy (mod->mins, e->v->mins); @@ -2467,7 +2472,7 @@ void PF_setmodel_Internal (pubprogfuncs_t *prinst, edict_t *e, char *m) static void QCBUILTIN PF_setmodel (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { edict_t *e; - char *m; + const char *m; e = G_EDICT(prinst, OFS_PARM0); m = PR_GetStringOfs(prinst, OFS_PARM1); @@ -2478,7 +2483,7 @@ static void QCBUILTIN PF_setmodel (pubprogfuncs_t *prinst, struct globalvars_s * static void QCBUILTIN PF_h2set_puzzle_model (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { //qc/hc lacks string manipulation. edict_t *e; - char *shortname; + const char *shortname; char fullname[MAX_QPATH]; e = G_EDICT(prinst, OFS_PARM0); shortname = PR_GetStringOfs(prinst, OFS_PARM1); @@ -2596,7 +2601,7 @@ single print to a specific client centerprint(clientent, value) ================= */ -void PF_centerprint_Internal (int entnum, qboolean plaque, char *s) +void PF_centerprint_Internal (int entnum, qboolean plaque, const char *s) { client_t *cl; int slen; @@ -2924,7 +2929,7 @@ PF_ambientsound ================= */ -void PF_ambientsound_Internal (float *pos, char *samp, float vol, float attenuation) +void PF_ambientsound_Internal (float *pos, const char *samp, float vol, float attenuation) { int i, soundnum, j; sizebuf_t *buf[3] = {&sv.signon, &sv.nqmulticast, &sv.multicast}; @@ -2964,7 +2969,7 @@ void PF_ambientsound_Internal (float *pos, char *samp, float vol, float attenuat static void QCBUILTIN PF_ambientsound (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *samp; + const char *samp; float *pos; float vol, attenuation; @@ -2994,7 +2999,7 @@ pitchadj is a percent. values greater than 100 will result in a lower pitch, les */ static void QCBUILTIN PF_sound (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *sample; + const char *sample; int channel; edict_t *entity; int volume; @@ -3040,7 +3045,7 @@ static void QCBUILTIN PF_sound (pubprogfuncs_t *prinst, struct globalvars_s *pr_ static void QCBUILTIN PF_pointsound(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *sample; + const char *sample; float *origin; float volume; float attenuation; @@ -3064,7 +3069,7 @@ static void QCBUILTIN PF_LocalSound(pubprogfuncs_t *prinst, struct globalvars_s #ifndef SERVERONLY sfx_t *sfx; - char * s = PR_GetStringOfs(prinst, OFS_PARM0); + const char * s = PR_GetStringOfs(prinst, OFS_PARM0); float chan = G_FLOAT(OFS_PARM1); float vol = G_FLOAT(OFS_PARM2); @@ -3340,29 +3345,14 @@ static void QCBUILTIN PF_checkclient (pubprogfuncs_t *prinst, struct globalvars_ //============================================================================ - -/* -================= -PF_stuffcmd - -Sends text over to the client's execution buffer - -stuffcmd (clientent, value) -================= -*/ -static void QCBUILTIN PF_stuffcmd (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +void PF_stuffcmd_Internal(int entnum, const char *str) { - int entnum; - char *str; client_t *cl; static qboolean expectingcolour; int slen; - entnum = G_EDICTNUM(prinst, OFS_PARM0); if (entnum < 1 || entnum > sv.allocated_client_slots) return; -// PR_BIError ("Parm 0 not a client"); - str = PR_GetStringOfs(prinst, OFS_PARM1); cl = &svs.clients[entnum-1]; @@ -3442,6 +3432,20 @@ static void QCBUILTIN PF_stuffcmd (pubprogfuncs_t *prinst, struct globalvars_s * } } +/* +================= +PF_stuffcmd + +Sends text over to the client's execution buffer + +stuffcmd (clientent, value) +================= +*/ +static void QCBUILTIN PF_stuffcmd (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +{ + PF_stuffcmd_Internal(G_EDICTNUM(prinst, OFS_PARM0), PR_GetStringOfs(prinst, OFS_PARM1)); +} + //DP_QC_DROPCLIENT static void QCBUILTIN PF_dropclient (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { @@ -3523,7 +3527,7 @@ float cvar (string) */ static void QCBUILTIN PF_cvar (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *str; + const char *str; str = PR_GetStringOfs(prinst, OFS_PARM0); @@ -3575,48 +3579,6 @@ static void QCBUILTIN PF_sv_getlight (pubprogfuncs_t *prinst, struct globalvars_ } } -/* -================= -PF_findradius - -Returns a chain of entities that have origins within a spherical area - -findradius (origin, radius) -================= -*/ -static void QCBUILTIN PF_findradius (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) -{ - edict_t *ent, *chain; - float rad; - float *org; - vec3_t eorg; - int i, j; - - chain = (edict_t *)sv.world.edicts; - - org = G_VECTOR(OFS_PARM0); - rad = G_FLOAT(OFS_PARM1); - rad = rad*rad; - - for (i=1 ; iisfree) - continue; - if (ent->v->solid == SOLID_NOT && (progstype != PROG_QW || !((int)ent->v->flags & FL_FINDABLE_NONSOLID)) && !sv_gameplayfix_blowupfallenzombies.value) - continue; - for (j=0 ; j<3 ; j++) - eorg[j] = org[j] - (ent->v->origin[j] + (ent->v->mins[j] + ent->v->maxs[j])*0.5); - if (DotProduct(eorg,eorg) > rad) - continue; - - ent->v->chain = EDICT_TO_PROG(prinst, chain); - chain = ent; - } - - RETURN_EDICT(prinst, chain); -} - /* ========= PF_conprint @@ -3711,7 +3673,7 @@ void PR_CheckEmptyString (char *s) //float(string effectname) particleeffectnum (EXT_CSQC) static void QCBUILTIN PF_sv_particleeffectnum(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *s = PR_GetStringOfs(prinst, OFS_PARM0); + const char *s = PR_GetStringOfs(prinst, OFS_PARM0); /* #ifdef PEXT_CSQC #ifdef warningmsg @@ -3763,7 +3725,7 @@ static void QCBUILTIN PF_sv_particleeffectnum(pubprogfuncs_t *prinst, struct glo static void QCBUILTIN PF_precache_file (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { // precache_file is only used to copy files with qcc, it does nothing - char *s = PR_GetStringOfs(prinst, OFS_PARM0); + const char *s = PR_GetStringOfs(prinst, OFS_PARM0); G_INT(OFS_RETURN) = G_INT(OFS_PARM0); @@ -3771,7 +3733,7 @@ static void QCBUILTIN PF_precache_file (pubprogfuncs_t *prinst, struct globalvar FS_FLocateFile(s, FSLFRT_IFFOUND, NULL); } -void PF_precache_sound_Internal (pubprogfuncs_t *prinst, char *s) +void PF_precache_sound_Internal (pubprogfuncs_t *prinst, const char *s) { int i; @@ -3811,7 +3773,7 @@ void PF_precache_sound_Internal (pubprogfuncs_t *prinst, char *s) } static void QCBUILTIN PF_precache_sound (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *s; + const char *s; s = PR_GetStringOfs(prinst, OFS_PARM0); @@ -3820,7 +3782,7 @@ static void QCBUILTIN PF_precache_sound (pubprogfuncs_t *prinst, struct globalva PF_precache_sound_Internal(prinst, s); } -int PF_precache_model_Internal (pubprogfuncs_t *prinst, char *s, qboolean queryonly) +int PF_precache_model_Internal (pubprogfuncs_t *prinst, const char *s, qboolean queryonly) { int i; @@ -3849,7 +3811,7 @@ int PF_precache_model_Internal (pubprogfuncs_t *prinst, char *s, qboolean queryo sv.strings.model_precache[i] = PR_AddString(prinst, s, 0, false); s = sv.strings.model_precache[i]; if (!strcmp(s + strlen(s) - 4, ".bsp") || sv_gameplayfix_setmodelrealbox.ival) - sv.models[i] = Mod_ForName(s, false); + sv.models[i] = Mod_ForName(s, MLV_WARN); else { /*touch the file, so any packs will be referenced*/ @@ -3881,7 +3843,7 @@ int PF_precache_model_Internal (pubprogfuncs_t *prinst, char *s, qboolean queryo } static void QCBUILTIN PF_precache_model (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *s; + const char *s; s = PR_GetStringOfs(prinst, OFS_PARM0); @@ -3892,7 +3854,7 @@ static void QCBUILTIN PF_precache_model (pubprogfuncs_t *prinst, struct globalva static void QCBUILTIN PF_h2precache_puzzle_model (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { //qc/hc lacks string manipulation. - char *shortname; + const char *shortname; char fullname[MAX_QPATH]; shortname = PR_GetStringOfs(prinst, OFS_PARM0); snprintf(fullname, sizeof(fullname)-1, "models/puzzle/%s.mdl", shortname); @@ -3902,7 +3864,7 @@ static void QCBUILTIN PF_h2precache_puzzle_model (pubprogfuncs_t *prinst, struct static void QCBUILTIN PF_getmodelindex (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *s = PR_GetStringOfs(prinst, OFS_PARM0); + const char *s = PR_GetStringOfs(prinst, OFS_PARM0); qboolean queryonly = (svprogfuncs->callargc >= 2)?G_FLOAT(OFS_PARM1):false; G_FLOAT(OFS_RETURN) = PF_precache_model_Internal(prinst, s, queryonly); @@ -3910,7 +3872,7 @@ static void QCBUILTIN PF_getmodelindex (pubprogfuncs_t *prinst, struct globalvar void QCBUILTIN PF_precache_vwep_model (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { int i; - char *s; + const char *s; s = PR_GetStringOfs(prinst, OFS_PARM0); if (!*s || strchr(s, '\"') || strchr(s, ';') || strchr(s, '\t') || strchr(s, '\n')) @@ -4027,56 +3989,11 @@ static void QCBUILTIN PF_walkmove (pubprogfuncs_t *prinst, struct globalvars_s * pr_global_struct->self = oldself; } -/* -=============== -PF_droptofloor - -void() droptofloor -=============== -*/ -static void QCBUILTIN PF_droptofloor (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) -{ - edict_t *ent; - vec3_t end; - vec3_t start; - trace_t trace; - const float *gravitydir; - extern const vec3_t standardgravity; - - ent = PROG_TO_EDICT(prinst, pr_global_struct->self); - - if (ent->xv->gravitydir[2] || ent->xv->gravitydir[1] || ent->xv->gravitydir[0]) - gravitydir = ent->xv->gravitydir; - else - gravitydir = standardgravity; - - VectorCopy (ent->v->origin, end); - if (pr_droptofloorunits.value > 0) - VectorMA(end, pr_droptofloorunits.value, gravitydir, end); - else - VectorMA(end, 256, gravitydir, end); - - VectorCopy (ent->v->origin, start); - trace = World_Move (&sv.world, start, ent->v->mins, ent->v->maxs, end, MOVE_NORMAL, (wedict_t*)ent); - - if (trace.fraction == 1 || trace.allsolid) - G_FLOAT(OFS_RETURN) = 0; - else - { - VectorCopy (trace.endpos, ent->v->origin); - World_LinkEdict (&sv.world, (wedict_t*)ent, false); - ent->v->flags = (int)ent->v->flags | FL_ONGROUND; - ent->v->groundentity = EDICT_TO_PROG(prinst, trace.ent); - G_FLOAT(OFS_RETURN) = 1; - } -} - -void QCBUILTIN PF_applylightstyle(int style, char *val, int col) +void QCBUILTIN PF_applylightstyle(int style, const char *val, int col) { client_t *client; int j; - if (style < 0 || style >= MAX_LIGHTSTYLES) { Con_Printf("WARNING: Bad lightstyle %i.\n", style); @@ -4088,11 +4005,9 @@ void QCBUILTIN PF_applylightstyle(int style, char *val, int col) // change the string in sv if (sv.strings.lightstyles[style]) - BZ_Free(sv.strings.lightstyles[style]); - sv.strings.lightstyles[style] = BZ_Malloc(strlen(val)+1); + BZ_Free((void*)sv.strings.lightstyles[style]); + sv.strings.lightstyles[style] = Z_StrDup(val); - strcpy(sv.strings.lightstyles[style], val); -// sv.lightstyles[style] = val; #ifdef PEXT_LIGHTSTYLECOL sv.strings.lightstylecolours[style] = col; #endif @@ -4141,7 +4056,7 @@ void(float style, string value [, float colour]) lightstyle static void QCBUILTIN PF_lightstyle (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { int style; - char *val; + const char *val; #ifdef PEXT_LIGHTSTYLECOL int col; @@ -4760,7 +4675,7 @@ void QCBUILTIN PF_WriteFloat (pubprogfuncs_t *prinst, struct globalvars_s *pr_gl #endif } -void PF_WriteString_Internal (int target, char *str) +void PF_WriteString_Internal (int target, const char *str) { if (target == MSG_CSQC) { //csqc buffers are always written. @@ -5074,7 +4989,7 @@ void PF_tempentity (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) //============================================================================= -int SV_ModelIndex (char *name); +int SV_ModelIndex (const char *name); void QCBUILTIN PF_makestatic (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { @@ -5155,7 +5070,7 @@ PF_changelevel */ void QCBUILTIN PF_changelevel (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *s, *spot; + const char *s, *spot; // make sure we don't issue two changelevels (unless the last one failed) if (sv.mapchangelocked) @@ -5223,7 +5138,7 @@ PF_infokey string(entity e, string key) infokey ============== */ -char *PF_infokey_Internal (int entnum, char *key) +char *PF_infokey_Internal (int entnum, const char *key) { char *value; static char ov[256]; @@ -5318,6 +5233,18 @@ char *PF_infokey_Internal (int entnum, char *key) #endif value = ""; } + else if (!strcmp(key, "*VIP")) + value = svs.clients[entnum-1].isvip?"1":""; + else if (!strcmp(key, "*ismuted")) + value = svs.clients[entnum-1].ismuted?"1":""; + else if (!strcmp(key, "*isdeaf")) + value = svs.clients[entnum-1].isdeaf?"1":""; + else if (!strcmp(key, "*iscrippled")) + value = svs.clients[entnum-1].iscrippled?"1":""; + else if (!strcmp(key, "*iscuffed")) + value = svs.clients[entnum-1].iscuffed?"1":""; + else if (!strcmp(key, "*islagged")) + value = svs.clients[entnum-1].islagged?"1":""; else value = Info_ValueForKey (svs.clients[entnum-1].userinfo, key); } else @@ -5331,7 +5258,7 @@ static void QCBUILTIN PF_infokey (pubprogfuncs_t *prinst, struct globalvars_s *p edict_t *e; int e1; char *value; - char *key; + const char *key; e = G_EDICT(prinst, OFS_PARM0); e1 = NUM_FOR_EDICT(prinst, e); @@ -5407,7 +5334,7 @@ static void QCBUILTIN PF_newstring(pubprogfuncs_t *prinst, struct globalvars_s * char *s; int len; - char *in = PR_GetStringOfs(prinst, OFS_PARM0); + const char *in = PR_GetStringOfs(prinst, OFS_PARM0); len = strlen(in)+1; if (prinst->callargc == 2 && G_FLOAT(OFS_PARM1) > len) @@ -5460,7 +5387,7 @@ static void QCBUILTIN PF_redstring(pubprogfuncs_t *prinst, struct globalvars_s * */ #ifdef SVCHAT -void SV_Chat(char *filename, float starttag, edict_t *edict); +void SV_Chat(const char *filename, float starttag, edict_t *edict); static void QCBUILTIN PF_chat (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { SV_Chat(PR_GetStringOfs(prinst, OFS_PARM0), G_FLOAT(OFS_PARM1), G_EDICT(prinst, OFS_PARM2)); @@ -5478,8 +5405,8 @@ static void QCBUILTIN PF_chat (pubprogfuncs_t *prinst, struct globalvars_s *pr_g #ifdef SQL void QCBUILTIN PF_sqlconnect (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *paramstr[SQL_CONNECT_PARAMS]; - char *driver; + const char *paramstr[SQL_CONNECT_PARAMS]; + const char *driver; int i; if (!SQL_Available()) @@ -5654,7 +5581,7 @@ void QCBUILTIN PF_sqlreadfield (pubprogfuncs_t *prinst, struct globalvars_s *pr_ qres = SQL_GetQueryResult(server, G_FLOAT(OFS_PARM1), G_FLOAT(OFS_PARM2)); if (qres) { - data = SQL_ReadField(server, qres, G_FLOAT(OFS_PARM2), G_FLOAT(OFS_PARM3), true); + data = SQL_ReadField(server, qres, G_FLOAT(OFS_PARM2), G_FLOAT(OFS_PARM3), true, NULL); if (data) { RETURN_TSTRING(data); @@ -5716,7 +5643,7 @@ void QCBUILTIN PF_sqlreadfloat (pubprogfuncs_t *prinst, struct globalvars_s *pr_ return; } - data = SQL_ReadField(server, qres, G_FLOAT(OFS_PARM2), G_FLOAT(OFS_PARM3), true); + data = SQL_ReadField(server, qres, G_FLOAT(OFS_PARM2), G_FLOAT(OFS_PARM3), true, NULL); if (data) { G_FLOAT(OFS_RETURN) = Q_atof(data); @@ -5769,7 +5696,7 @@ void QCBUILTIN PF_sqlerror (pubprogfuncs_t *prinst, struct globalvars_s *pr_glob void QCBUILTIN PF_sqlescape (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { sqlserver_t *server; - char *toescape; + const char *toescape; char escaped[4096]; if (SQL_Available()) @@ -5819,7 +5746,7 @@ void PR_SQLCycle(void) -int PR_EnableEBFSBuiltin(char *name, int binum) +int PR_EnableEBFSBuiltin(const char *name, int binum) { int i; for (i = 0;BuiltinList[i].name;i++) @@ -5850,7 +5777,7 @@ int PR_EnableEBFSBuiltin(char *name, int binum) -lh_extension_t *checkfteextensioncl(int mask, char *name) //true if the cient extension mask matches an extension name +lh_extension_t *checkfteextensioncl(int mask, const char *name) //true if the cient extension mask matches an extension name { int i; for (i = 0; i < 32; i++) @@ -5865,12 +5792,12 @@ lh_extension_t *checkfteextensioncl(int mask, char *name) //true if the cient ex return NULL; } -lh_extension_t *checkfteextensionsv(char *name) //true if the server supports an protocol extension. +lh_extension_t *checkfteextensionsv(const char *name) //true if the server supports an protocol extension. { return checkfteextensioncl(Net_PextMask(1, false), name); } -lh_extension_t *checkextension(char *name) +lh_extension_t *checkextension(const char *name) { int i; for (i = 32; i < QSG_Extensions_count; i++) @@ -5895,7 +5822,7 @@ checkextension(string extensionname, [entity client]) static void QCBUILTIN PF_checkextension (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { lh_extension_t *ext = NULL; - char *s = PR_GetStringOfs(prinst, OFS_PARM0); + const char *s = PR_GetStringOfs(prinst, OFS_PARM0); ext = checkextension(s); if (!ext) @@ -5951,9 +5878,10 @@ static void QCBUILTIN PF_checkextension (pubprogfuncs_t *prinst, struct globalva static void QCBUILTIN PF_builtinsupported (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *s = PR_GetStringOfs(prinst, OFS_PARM0); + const char *s = PR_GetStringOfs(prinst, OFS_PARM0); + int binum = (prinst->callargc < 2)?0:G_FLOAT(OFS_PARM1); - G_FLOAT(OFS_RETURN) = PR_EnableEBFSBuiltin(s, 0); + G_FLOAT(OFS_RETURN) = PR_EnableEBFSBuiltin(s, binum); } @@ -5997,7 +5925,7 @@ string substr(string str, float start, float len) static void QCBUILTIN PF_substr (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { char dest[4096]; - char *s; + const char *s; int start, len, l; s = PR_GetStringOfs(prinst, OFS_PARM0); @@ -6063,7 +5991,7 @@ string readmcmd (string str) static void QCBUILTIN PF_readcmd (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *s; + const char *s; static char output[8000]; extern char outputbuf[]; extern redirect_t sv_redirected; @@ -6153,7 +6081,7 @@ FIXME: check for null pointers first? static void QCBUILTIN PF_MVDSV_strcpy (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { int dst = G_INT(OFS_PARM0); - char *src = PR_GetStringOfs(prinst, OFS_PARM1); + const char *src = PR_GetStringOfs(prinst, OFS_PARM1); int size = strlen(src)+1; if (dst < 0 || dst+size >= prinst->stringtablesize) @@ -6177,7 +6105,7 @@ FIXME: check for null pointers first? static void QCBUILTIN PF_MVDSV_strncpy (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { int dst = G_INT(OFS_PARM0); - char *src = PR_GetStringOfs(prinst, OFS_PARM1); + const char *src = PR_GetStringOfs(prinst, OFS_PARM1); int size = G_FLOAT(OFS_PARM2); if (dst < 0 || dst+size >= prinst->stringtablesize) @@ -6200,7 +6128,7 @@ string strstr(string str, string sub) static void QCBUILTIN PF_strstr (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *str, *sub, *p; + const char *str, *sub, *p; str = PR_GetStringOfs(prinst, OFS_PARM0); sub = PR_GetStringOfs(prinst, OFS_PARM1); @@ -6337,7 +6265,7 @@ static void QCBUILTIN PF_copyentity (pubprogfuncs_t *prinst, struct globalvars_s static void QCBUILTIN PF_sv_findchain (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { int i, f; - char *s; + const char *s; string_t t; edict_t *ent, *chain; @@ -6582,11 +6510,11 @@ static void QCBUILTIN PF_h2advanceweaponframe (pubprogfuncs_t *prinst, struct gl G_FLOAT(OFS_RETURN) = state; } -char *SV_CheckRejectConnection(netadr_t *adr, char *uinfo, unsigned int protocol, unsigned int pext1, unsigned int pext2, char *guid) +const char *SV_CheckRejectConnection(netadr_t *adr, const char *uinfo, unsigned int protocol, unsigned int pext1, unsigned int pext2, char *guid) { char addrstr[256]; char clfeatures[4096], *bp; - char *ret = NULL; + const char *ret = NULL; if (gfuncs.CheckRejectConnection) { globalvars_t *pr_globals = PR_globals(svprogfuncs, PR_CURRENT); @@ -7497,7 +7425,7 @@ static void QCBUILTIN PF_RegisterTEnt(pubprogfuncs_t *prinst, struct globalvars_ int arg; int i; int nettype = G_FLOAT(OFS_PARM0); - char *effectname = PR_GetStringOfs(prinst, OFS_PARM1); + const char *effectname = PR_GetStringOfs(prinst, OFS_PARM1); if (sv.state != ss_loading) { PR_BIError (prinst, "PF_RegisterTEnt: Registration can only be done in spawn functions"); @@ -8279,7 +8207,7 @@ static void QCBUILTIN PF_te_bloodshower(pubprogfuncs_t *prinst, struct globalvar static void QCBUILTIN PF_effect(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { float *org = G_VECTOR(OFS_PARM0); - char *name = PR_GetStringOfs(prinst, OFS_PARM1); + const char *name = PR_GetStringOfs(prinst, OFS_PARM1); float startframe = G_FLOAT(OFS_PARM2); float endframe = G_FLOAT(OFS_PARM3); float framerate = G_FLOAT(OFS_PARM4); @@ -8297,43 +8225,52 @@ static void QCBUILTIN PF_te_plasmaburn(pubprogfuncs_t *prinst, struct globalvars } -void QCBUILTIN PF_ForceInfoKey(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +int PF_ForceInfoKey_Internal(unsigned int entnum, const char *key, const char *value) { - edict_t *e; - int e1; - char *value; - char *key; - - e = G_EDICT(prinst, OFS_PARM0); - e1 = NUM_FOR_EDICT(prinst, e); - key = PR_GetStringOfs(prinst, OFS_PARM1); - value = PR_GetStringOfs(prinst, OFS_PARM2); - - G_FLOAT(OFS_RETURN) = 0; - - if (e1 == 0) + if (entnum == 0) { //localinfo Info_SetValueForKey (localinfo, key, value, MAX_LOCALINFO_STRING); - G_FLOAT(OFS_RETURN) = 2; + return 2; } - else if (e1 <= sv.allocated_client_slots) + else if (entnum <= sv.allocated_client_slots) { //woo. we found a client. - Info_SetValueForStarKey(svs.clients[e1-1].userinfo, key, value, sizeof(svs.clients[e1-1].userinfo)); + Info_SetValueForStarKey(svs.clients[entnum-1].userinfo, key, value, sizeof(svs.clients[entnum-1].userinfo)); - SV_ExtractFromUserinfo (&svs.clients[e1-1], false); + SV_ExtractFromUserinfo (&svs.clients[entnum-1], false); MSG_WriteByte (&sv.reliable_datagram, svc_setinfo); - MSG_WriteByte (&sv.reliable_datagram, e1-1); + MSG_WriteByte (&sv.reliable_datagram, entnum-1); MSG_WriteString (&sv.reliable_datagram, key); - MSG_WriteString (&sv.reliable_datagram, Info_ValueForKey(svs.clients[e1-1].userinfo, key)); + MSG_WriteString (&sv.reliable_datagram, Info_ValueForKey(svs.clients[entnum-1].userinfo, key)); if (!strcmp(key, "*spectator")) - svs.clients[e1-1].spectator = !!atoi(value); + svs.clients[entnum-1].spectator = !!atoi(value); +#ifdef SUBSERVERS + if (!strcmp(key, "*transfer")) + SSV_InitiatePlayerTransfer(&svs.clients[entnum-1], value); +#else + if (*value) + PF_ForceInfoKey_Internal(entnum, key, ""); +#endif - G_FLOAT(OFS_RETURN) = 1; + return 1; } else Con_DPrintf("PF_ForceInfoKey: not world or client\n"); + return 0; +} + +void QCBUILTIN PF_ForceInfoKey(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +{ + edict_t *e; + const char *value; + const char *key; + + e = G_EDICT(prinst, OFS_PARM0); + key = PR_GetStringOfs(prinst, OFS_PARM1); + value = PR_GetStringOfs(prinst, OFS_PARM2); + + G_FLOAT(OFS_RETURN) = PF_ForceInfoKey_Internal(e->entnum, key, value); } /* @@ -8435,8 +8372,8 @@ static void ParamNegateFix ( float * xx, float * yy, int Zone ) } static void QCBUILTIN PF_ShowPic(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *slot = PR_GetStringOfs(prinst, OFS_PARM0); - char *picname = PR_GetStringOfs(prinst, OFS_PARM1); + const char *slot = PR_GetStringOfs(prinst, OFS_PARM0); + const char *picname = PR_GetStringOfs(prinst, OFS_PARM1); float x = G_FLOAT(OFS_PARM2); float y = G_FLOAT(OFS_PARM3); float zone = G_FLOAT(OFS_PARM4); @@ -8476,7 +8413,7 @@ static void QCBUILTIN PF_ShowPic(pubprogfuncs_t *prinst, struct globalvars_s *pr static void QCBUILTIN PF_HidePic(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { client_t *cl; - char *slot = PR_GetStringOfs(prinst, OFS_PARM0); + const char *slot = PR_GetStringOfs(prinst, OFS_PARM0); int entnum; if (prinst->callargc==2) @@ -8505,7 +8442,7 @@ static void QCBUILTIN PF_HidePic(pubprogfuncs_t *prinst, struct globalvars_s *pr static void QCBUILTIN PF_MovePic(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *slot = PR_GetStringOfs(prinst, OFS_PARM0); + const char *slot = PR_GetStringOfs(prinst, OFS_PARM0); float x = G_FLOAT(OFS_PARM1); float y = G_FLOAT(OFS_PARM2); float zone = G_FLOAT(OFS_PARM3); @@ -8542,8 +8479,8 @@ static void QCBUILTIN PF_MovePic(pubprogfuncs_t *prinst, struct globalvars_s *pr static void QCBUILTIN PF_ChangePic(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - char *slot = PR_GetStringOfs(prinst, OFS_PARM0); - char *newpic= PR_GetStringOfs(prinst, OFS_PARM1); + const char *slot = PR_GetStringOfs(prinst, OFS_PARM0); + const char *newpic= PR_GetStringOfs(prinst, OFS_PARM1); int entnum; client_t *cl; @@ -8823,7 +8760,7 @@ qboolean SV_RunFullQCMovement(client_t *client, usercmd_t *ucmd) static void QCBUILTIN PF_matchclient(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { int clnum=-1; - char *name = PR_GetStringOfs(prinst, OFS_PARM0); + const char *name = PR_GetStringOfs(prinst, OFS_PARM0); int matchnum = G_FLOAT(OFS_PARM1); client_t *cl; @@ -8856,8 +8793,8 @@ static void QCBUILTIN PF_matchclient(pubprogfuncs_t *prinst, struct globalvars_s static void QCBUILTIN PF_SendPacket(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { netadr_t to; - char *address = PR_GetStringOfs(prinst, OFS_PARM0); - char *contents = PF_VarString(prinst, 1, pr_globals); + const char *address = PR_GetStringOfs(prinst, OFS_PARM0); + const char *contents = PF_VarString(prinst, 1, pr_globals); NET_StringToAdr(address, 0, &to); NET_SendPacket(NS_SERVER, strlen(contents), contents, &to); @@ -9195,7 +9132,7 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs {"findfloat", PF_FindFloat, 0, 0, 0, 98, D("entity(entity start, .float fld, float match)", "Equivelent to the find builtin, but instead of comparing strings, this builtin compares floats. This builtin requires multiple calls in order to scan all entities - set start to the previous call's return value.\nworld is returned when there are no more entities.")}, // #98 (DP_QC_FINDFLOAT) {"checkextension", PF_checkextension, 99, 99, 0, 99, D("float(string extname)", "Checks for an extension by its name (eg: checkextension(\"FRIK_FILE\") says that its okay to go ahead and use strcat).\nUse cvar_value(\"pr_checkextension\") to see if this builtin exists.")}, // #99 //darkplaces system - query a string to see if the mod supports X Y and Z. - {"builtin_find", PF_builtinsupported,100, 100, 0, 100, "float(string builtinname)"}, // #100 //per builtin system. + {"builtin_find", PF_builtinsupported,100, 100, 0, 100, D("float(string builtinname)", "Looks to see if the named builtin is valid, and returns the builtin number it exists at.")}, // #100 //per builtin system. {"anglemod", PF_anglemod, 0, 0, 0, 102, "float(float value)"}, {"qsg_cvar_string", PF_cvar_string, 0, 0, 0, 103, D("string(string cvarname)","An old/legacy equivelent of more recent/common builtins in order to read a cvar's string value."), true}, @@ -9206,8 +9143,8 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs {"changepic", PF_ChangePic, 0, 0, 0, 107, "void(string slot, string picname, optional entity player)"}, {"showpicent", PF_ShowPic, 0, 0, 0, 108, D("void(string slot, entity player)",NULL), true}, {"hidepicent", PF_HidePic, 0, 0, 0, 109, D("void(string slot, entity player)",NULL), true}, -// {"movepicent", PF_ShowPic, 0, 0, 0, 110, "void(string slot, float x, float y, float zone, entity player)", true}, -// {"changepicent", PF_HidePic, 0, 0, 0, 111, "void(string slot, string picname, entity player)", true}, +// {"movepicent", PF_MovePic, 0, 0, 0, 110, "void(string slot, float x, float y, float zone, entity player)", true}, +// {"changepicent", PF_ChangePic, 0, 0, 0, 111, "void(string slot, string picname, entity player)", true}, //End TEU_SHOWLMP2 //frik file @@ -9260,7 +9197,7 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs //I guess this should go under DP_TE_STANDARDEFFECTBUILTINS... {"te_lightningblood",PF_te_lightningblood, 0, 0, 0, 219, "void(vector pos)"},// #219 te_lightningblood - {"map_builtin", PF_builtinsupported,0, 0, 0, 220, D("",NULL), true}, //like #100 - takes 2 args. arg0 is builtinname, 1 is number to map to. + {"map_builtin", PF_builtinsupported,0, 0, 0, 220, D("float(string builtinname, float builtinnum)","Attempts to map the named builtin at a non-standard builtin number. Returns 0 on failure."), true}, //like #100 - takes 2 args. arg0 is builtinname, 1 is number to map to. //FTE_STRINGS {"strstrofs", PF_strstrofs, 0, 0, 0, 221, D("float(string s1, string sub, optional float startidx)", "Returns the 0-based offset of sub within the s1 string, or -1 if sub is not in s1.\nIf startidx is set, this builtin will ignore matches before that 0-based offset.")}, @@ -9314,10 +9251,10 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs {"sqlversion", PF_sqlversion, 0, 0, 0, 257, "string(float serveridx)"}, // sqlversion (FTE_SQL) {"sqlreadfloat", PF_sqlreadfloat, 0, 0, 0, 258, "float(float serveridx, float queryidx, float row, float column)"}, // sqlreadfloat (FTE_SQL) - {"stoi", PF_stoi, 0, 0, 0, 259, "int(string)"}, - {"itos", PF_itos, 0, 0, 0, 260, "string(int)"}, - {"stoh", PF_stoh, 0, 0, 0, 261, "int(string)"}, - {"htos", PF_htos, 0, 0, 0, 262, "string(int)"}, + {"stoi", PF_stoi, 0, 0, 0, 259, D("int(string)", "Converts the given string into an integer. Base 8, 10, or 16 is determined based upon the format of the string.")}, + {"itos", PF_itos, 0, 0, 0, 260, D("string(int)", "Converts the passed integer into a base10 string.")}, + {"stoh", PF_stoh, 0, 0, 0, 261, D("int(string)", "Reads a base-16 string (with or without 0x prefix) as an integer. Bugs out if given a base 8 or base 10 string. :P")}, + {"htos", PF_htos, 0, 0, 0, 262, D("string(int)", "Formats an integer as a base16 string, with leading 0s and no prefix. Always returns 8 characters.")}, {"skel_create", PF_skel_create, 0, 0, 0, 263, D("float(float modlindex, optional float useabstransforms)", "Allocates a new uninitiaised skeletal object, with enough bone info to animate the given model.\neg: self.skeletonobject = skel_create(self.modelindex);")}, // (FTE_CSQC_SKELETONOBJECTS) {"skel_build", PF_skel_build, 0, 0, 0, 264, D("float(float skel, entity ent, float modelindex, float retainfrac, float firstbone, float lastbone, optional float addfrac)", "Animation data (according to the entity's frame info) is pulled from the specified model and blended into the specified skeletal object.\nIf retainfrac is set to 0 on the first call and 1 on the others, you can blend multiple animations together according to the addfrac value. The final weight should be 1. Other values will result in scaling and/or other weirdness. You can use firstbone and lastbone to update only part of the skeletal object, to allow legs to animate separately from torso, use 0 for both arguments to specify all, as bones are 1-based.")}, // (FTE_CSQC_SKELETONOBJECTS) @@ -9335,7 +9272,7 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs {"frameforname", PF_frameforname, 0, 0, 0, 276, D("float(float modidx, string framename)", "Looks up a framegroup from a model by name, avoiding the need for hardcoding. Returns -1 on error.")},// (FTE_CSQC_SKELETONOBJECTS) {"frameduration", PF_frameduration, 0, 0, 0, 277, D("float(float modidx, float framenum)", "Retrieves the duration (in seconds) of the specified framegroup.")},// (FTE_CSQC_SKELETONOBJECTS) - {"terrain_edit", PF_terrain_edit, 0, 0, 0, 278, "void(float action, optional vector pos, optional float radius, optional float quant, ...)"},// (??FTE_TERRAIN_EDIT?? + {"terrain_edit", PF_terrain_edit, 0, 0, 0, 278, D("void(float action, optional vector pos, optional float radius, optional float quant, ...)", "Realtime terrain editing. Actions are the TEREDIT_ constants.")},// (??FTE_TERRAIN_EDIT?? {"touchtriggers", PF_touchtriggers, 0, 0, 0, 279, D("void()", "Triggers a touch events between self and every entity that it is in contact with. This should typically just be the triggers touch functions.")},// {"writefloat", PF_WriteFloat, 0, 0, 0, 280, "void(float buf, float fl)"},// {"skel_ragupdate", PF_skel_ragedit, 0, 0, 0, 281, D("float(entity skelent, string dollcmd, float animskel)", "Updates the skeletal object attached to the entity according to its origin and other properties.\nif animskel is non-zero, the ragdoll will animate towards the bone state in the animskel skeletal object, otherwise they will pick up the model's base pose which may not give nice results.\nIf dollcmd is not set, the ragdoll will update (this should be done each frame).\nIf the doll is updated without having a valid doll, the model's default .doll will be instanciated.\ncommands:\n doll foo.doll : sets up the entity to use the named doll file\n dollstring TEXT : uses the doll file directly embedded within qc, with that extra prefix.\n cleardoll : uninstanciates the doll without destroying the skeletal object.\n animate 0.5 : specifies the strength of the ragdoll as a whole \n animatebody somebody 0.5 : specifies the strength of the ragdoll on a specific body (0 will disable ragdoll animations on that body).\n enablejoint somejoint 1 : enables (or disables) a joint. Disabling joints will allow the doll to shatter.")}, // (FTE_CSQC_RAGDOLL) @@ -9435,7 +9372,7 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs {"getentitytoken", PF_Fixme, 0, 0, 0, 355, D("string(optional string resetstring)", "Grab the next token in the map's entity lump.\nIf resetstring is not specified, the next token will be returned with no other sideeffects.\nIf empty, will reset from the map before returning the first token, probably {.\nIf not empty, will tokenize from that string instead.\nAlways returns tempstrings.")},//; {"findfont", PF_Fixme, 0, 0, 0, 356, D("float(string s)", "Looks up a named font slot. Matches the actual font name as a last resort.")},//; {"loadfont", PF_Fixme, 0, 0, 0, 357, D("float(string fontname, string fontmaps, string sizes, float slot, optional float fix_scale, optional float fix_voffset)", "too convoluted for me to even try to explain correct usage. Try drawfont = loadfont(\"foo\", \"cour\", \"16\", 0, 0, 0); to switch to the courier font, if you have the freetype2 library in windows..")}, - {"sendevent", PF_Fixme, 0, 0, 0, 359, "void(string evname, string evargs, ...)"},// (EXT_CSQC_1) + {"sendevent", PF_Fixme, 0, 0, 0, 359, D("void(string evname, string evargs, ...)", "Invoke Cmd_evname_evargs in ssqc. evargs must be a string of initials refering to the types of the arguments to pass. v=vector, e=entity(.entnum field is sent), f=float, i=int. 6 arguments max - you can get more if you pack your floats into vectors.")},// (EXT_CSQC_1) {"readbyte", PF_Fixme, 0, 0, 0, 360, "float()"},// (EXT_CSQC) {"readchar", PF_Fixme, 0, 0, 0, 361, "float()"},// (EXT_CSQC) @@ -9457,6 +9394,7 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs {"particleeffectquery",PF_Fixme,0, 0, 0, 374, D("string(float efnum, float body)", "Retrieves either the name or the body of the effect with the given number. The effect body is regenerated from internal state, and can be changed before being reapplied via the localcmd builtin.")}, {"adddecal", PF_Fixme, 0, 0, 0, 375, D("void(string shadername, vector origin, vector up, vector side, vector rgb, float alpha)", "Adds a temporary clipped decal shader to the scene, centered at the given point with given orientation. Will be drawn by the next renderscene call, and freed by the next clearscene call.")}, + {"setcustomskin", PF_Fixme, 0, 0, 0, 376, D("float(entity e, string skinfilename, optional string skindata)", "Sets an entity's skin overrides. These are custom per-entity surface->shader lookups. The skinfilename/data should be in .skin format:\nsurfacename,shadername - makes the named surface use the named shader\nreplace \"surfacename\" \"shadername\" - same.\ncompose \"surfacename\" \"shader\" \"imagename@x,y:w,h?r,g,b,a\" - compose a skin texture from multiple images. The texture is determined to be sufficient to hold the first named image, additional images can be named as extra tokens on the same line. Use a + at the end of the line to continue reading image tokens from the next line also, the named shader must use 'map $diffuse' to read the composed texture (compatible with the defaultskin shader).")}, //END EXT_CSQC {"memalloc", PF_memalloc, 0, 0, 0, 384, D("__variant*(int size)", "Allocate an arbitary block of memory")}, @@ -9478,13 +9416,13 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs //DP_QC_COPYENTITY {"copyentity", PF_copyentity, 0, 0, 0, 400, "void(entity from, entity to)"},// (DP_QC_COPYENTITY) //DP_SV_SETCOLOR - {"setcolors", PF_setcolors, 0, 0, 0, 401, "void(entity ent, float colours)"},//DP_SV_SETCOLOR + {"setcolors", PF_setcolors, 0, 0, 0, 401, D("void(entity ent, float colours)", "Changes a player's colours. The bits 0-3 are the lower/trouser colour, bits 4-7 are the upper/shirt colours.")},//DP_SV_SETCOLOR //DP_QC_FINDCHAIN {"findchain", PF_sv_findchain, 0, 0, 0, 402, "entity(.string field, string match)"},// (DP_QC_FINDCHAIN) //DP_QC_FINDCHAINFLOAT {"findchainfloat", PF_sv_findchainfloat,0, 0, 0, 403, "entity(.float fld, float match)"},// (DP_QC_FINDCHAINFLOAT) //DP_SV_EFFECT - {"effect", PF_effect, 0, 0, 0, 404, "void(vector org, string modelname, float startframe, float endframe, float framerate)"},// (DP_SV_EFFECT) + {"effect", PF_effect, 0, 0, 0, 404, D("void(vector org, string modelname, float startframe, float endframe, float framerate)", "Spawns a self-animating sprite")},// (DP_SV_EFFECT) //DP_TE_BLOOD {"te_blood", PF_te_blooddp, 0, 0, 0, 405, "void(vector org, vector dir, float count)"},// #405 te_blood //DP_TE_BLOODSHOWER @@ -9571,10 +9509,10 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs {"tokenize", PF_Tokenize, 0, 0, 0, 441, "float(string s)"},// (KRIMZON_SV_PARSECLIENTCOMMAND) {"argv", PF_ArgV, 0, 0, 0, 442, "string(float n)"},// (KRIMZON_SV_PARSECLIENTCOMMAND {"setattachment", PF_setattachment, 0, 0, 0, 443, "void(entity e, entity tagentity, string tagname)"},// (DP_GFX_QUAKE3MODELTAGS) - {"search_begin", PF_search_begin, 0, 0, 0, 444, "float(string pattern, float caseinsensitive, float quiet)"}, + {"search_begin", PF_search_begin, 0, 0, 0, 444, D("float(string pattern, float caseinsensitive, float quiet)", "initiate a filesystem scan based upon filenames. Be sure to call search_end on the returned handle.")}, {"search_end", PF_search_end, 0, 0, 0, 445, "void(float handle)"}, - {"search_getsize", PF_search_getsize, 0, 0, 0, 446, "float(float handle)"}, - {"search_getfilename", PF_search_getfilename,0, 0, 0, 447, "string(float handle, float num)"}, + {"search_getsize", PF_search_getsize, 0, 0, 0, 446, D("float(float handle)", "Retrieves the number of files that were found.")}, + {"search_getfilename", PF_search_getfilename,0, 0, 0, 447, D("string(float handle, float num)", "Retrieves name of one of the files that was found by the initial search.")}, {"cvar_string", PF_cvar_string, 0, 0, 0, 448, "string(string cvarname)"},//DP_QC_CVAR_STRING {"findflags", PF_FindFlags, 0, 0, 0, 449, "entity(entity start, .float fld, float match)"},//DP_QC_FINDFLAGS {"findchainflags", PF_sv_findchainflags,0, 0, 0, 450, "entity(.float fld, float match)"},//DP_QC_FINDCHAINFLAGS @@ -9631,11 +9569,11 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs {"entityfieldtype", PF_entityfieldtype, 0, 0, 0, 498, "float(float fieldnum)"},//DP_QC_ENTITYDATA {"getentityfieldstring",PF_getentityfieldstring,0,0, 0, 499, "string(float fieldnum, entity ent)"},//DP_QC_ENTITYDATA {"putentityfieldstring",PF_putentityfieldstring,0,0, 0, 500, "float(float fieldnum, entity ent, string s)"},//DP_QC_ENTITYDATA - {"WritePicture", PF_WritePicture, 0, 0, 0, 501, "void(float to, string s, float sz)"},//DP_SV_WRITEPICTURE - {"ReadPicture", PF_Fixme, 0, 0, 0, 501, "string()"},//DP_SV_WRITEPICTURE + {"WritePicture", PF_WritePicture, 0, 0, 0, 501, D("void(float to, string s, float sz)", "Encodes the named image across the network as-is adhering to some size limit. In FTE, this simply writes the string and is equivelent to writestring and sz is ignored. WritePicture should be paired with ReadPicture in csqc.")},//DP_SV_WRITEPICTURE + {"ReadPicture", PF_Fixme, 0, 0, 0, 501, D("string()", "Reads a picture that was written by ReadPicture, and returns a name that can be used in drawpic and other 2d drawing functions. In FTE, this acts as a readstring-with-downloadcheck - the image will appear normally once it has been downloaded, but its size may be incorrect until then.")},//DP_SV_WRITEPICTURE {"boxparticles", PF_Fixme, 0, 0, 0, 502, "void(float effectindex, entity own, vector org_from, vector org_to, vector dir_from, vector dir_to, float countmultiplier, optional float flags)"}, {"whichpack", PF_whichpack, 0, 0, 0, 503, D("string(string filename, optional float makereferenced)", "Returns the pak file name that contains the file specified. progs/player.mdl will generally return something like 'pak0.pak'. If makereferenced is true, clients will automatically be told that the returned package should be pre-downloaded and used, even if allow_download_refpackages is not set.")},//DP_QC_WHICHPACK - {"getentity", PF_Fixme, 0, 0, 0, 504, "__variant(float entnum, float fieldnum)"},//DP_CSQC_QUERYRENDERENTITY + {"getentity", PF_Fixme, 0, 0, 0, 504, D("__variant(float entnum, float fieldnum)", "Looks up fields from non-csqc-visible entities. The entity will need to be within the player's pvs. fieldnum should be one of the GE_ constants.")},//DP_CSQC_QUERYRENDERENTITY // {"undefined", PF_Fixme, 0, 0, 0, 505, ""}, // {"undefined", PF_Fixme, 0, 0, 0, 506, ""}, // {"undefined", PF_Fixme, 0, 0, 0, 507, ""}, @@ -9644,7 +9582,7 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs {"uri_escape", PF_uri_escape, 0, 0, 0, 510, "string(string in)"},//DP_QC_URI_ESCAPE {"uri_unescape", PF_uri_unescape, 0, 0, 0, 511, "string(string in)"},//DP_QC_URI_ESCAPE {"num_for_edict", PF_num_for_edict, 0, 0, 0, 512, "float(entity ent)"},//DP_QC_NUM_FOR_EDICT - {"uri_get", PF_uri_get, 0, 0, 0, 513, "float(string uril, float id, optional string postmimetype, optional string postdata)"},//DP_QC_URI_GET + {"uri_get", PF_uri_get, 0, 0, 0, 513, D("float(string uril, float id, optional string postmimetype, optional string postdata)", "uri_get() gets content from an URL and calls a callback \"uri_get_callback\" with it set as string; an unique ID of the transfer is returned\nreturns 1 on success, and then calls the callback with the ID, 0 or the HTTP status code, and the received data in a string")},//DP_QC_URI_GET {"tokenize_console",PF_tokenize_console,0, 0, 0, 514, "float(string str)"}, {"argv_start_index",PF_argv_start_index,0, 0, 0, 515, "float(float idx)"}, {"argv_end_index", PF_argv_end_index, 0, 0, 0, 516, "float(float idx)"}, @@ -9660,8 +9598,8 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs // {"particlethemefree",PF_Fixme, 0, 0, 0, 526, "void()"}, // {"particle", PF_Fixme, 0, 0, 0, 527, "float(vector org, vector vel, optional float theme)"}, // {"delayedparticle", PF_Fixme, 0, 0, 0, 528, "float(vector org, vector vel, float delay, float collisiondelay, optional float theme)"}, - {"loadfromdata", PF_loadfromdata, 0, 0, 0, 529, "void(string s)"}, - {"loadfromfile", PF_loadfromfile, 0, 0, 0, 530, "void(string s)"}, + {"loadfromdata", PF_loadfromdata, 0, 0, 0, 529, D("void(string s)", "Reads a set of entities from the given string. This string should have the same format as a .ent file or a saved game. Entities will be spawned as required. If you need to see the entities that were created, you should use parseentitydata instead.")}, + {"loadfromfile", PF_loadfromfile, 0, 0, 0, 530, D("void(string s)", "Reads a set of entities from the named file. This file should have the same format as a .ent file or a saved game. Entities will be spawned as required. If you need to see the entities that were created, you should use parseentitydata instead.")}, // {"setpause", VM_SV_setpause, 0, 0, 0, 531, "void(float pause)" STUB}, //end dp extras //begin mvdsv extras @@ -9685,18 +9623,18 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs {"getkeydest", PF_Fixme, 0, 0, 0, 602, "float()"}, {"setmousetarget", PF_Fixme, 0, 0, 0, 603, "void(float trg)"}, {"getmousetarget", PF_Fixme, 0, 0, 0, 604, "float()"}, - {"callfunction", PF_callfunction, 0, 0, 0, 605, "void(.../*, string funcname*/)"}, - {"writetofile", PF_writetofile, 0, 0, 0, 606, "void(float fh, entity e)"}, + {"callfunction", PF_callfunction, 0, 0, 0, 605, D("void(.../*, string funcname*/)", "Invokes the named function. The function name is always passed as the last parameter and must always be present. The others are passed to the named function as-is")}, + {"writetofile", PF_writetofile, 0, 0, 0, 606, D("void(float fh, entity e)", "Writes an entity's fields to the named frik_file file handle.")}, {"isfunction", PF_isfunction, 0, 0, 0, 607, "float(string s)"}, {"getresolution", PF_Fixme, 0, 0, 0, 608, "vector(float vidmode, optional float forfullscreen)"}, {"keynumtostring_menu",PF_Fixme, 0, 0, 0, 609, "string(float keynum)"}, //third copy of this builtin in dp's csqc. {"findkeysforcommand_dp",PF_Fixme, 0, 0, 0, 610, "string(string command, optional float bindmap)"}, - {"keynumtostring", PF_Fixme, 0, 0, 0, 609, "string(float keynum)"}, //normal name is for menuqc standard. + {"keynumtostring", PF_Fixme, 0, 0, 0, 609, D("string(float keynum)", "Converts a qscancode key number into a mostly-human-readable name, matching the bind command.")}, //normal name is for menuqc standard. {"findkeysforcommand",PF_Fixme, 0, 0, 0, 610, "string(string command, optional float bindmap)"}, {"gethostcachevalue",PF_Fixme, 0, 0, 0, 611, "float(float type)"}, {"gethostcachestring",PF_Fixme, 0, 0, 0, 612, "string(float type, float hostnr)"}, - {"parseentitydata", PF_parseentitydata, 0, 0, 0, 613, "void(entity e, string s)"}, - {"stringtokeynum", PF_Fixme, 0, 0, 0, 614, "float(string key)"}, + {"parseentitydata", PF_parseentitydata, 0, 0, 0, 613, D("void(entity e, string s)", "Reads a single entity's fields into an already-spawned entity. s should contain field pairs like in a saved game: {\"foo1\" \"bar\" \"foo2\" \"5\"} ")}, + {"stringtokeynum", PF_Fixme, 0, 0, 0, 614, D("float(string key)", "Returns the qscancode of a key from its name. Names are identical to the bind command. ctrl/shift/alt modifiers are ignored.")}, {"stringtokeynum_menu", PF_Fixme, 0, 0, 0, 614, "float(string key)"}, {"resethostcachemasks",PF_Fixme, 0, 0, 0, 615, "void()"}, {"sethostcachemaskstring",PF_Fixme, 0, 0, 0, 616, "void(float mask, float fld, string str, float op)"}, @@ -10334,6 +10272,22 @@ void PR_DumpPlatform_f(void) {"SOLID_PHYSICS_BOX", "const float", QW|NQ|CS, NULL, SOLID_PHYSICS_BOX}, {"SOLID_PHYSICS_SPHERE", "const float", QW|NQ|CS, NULL, SOLID_PHYSICS_SPHERE}, {"SOLID_PHYSICS_CAPSULE", "const float", QW|NQ|CS, NULL, SOLID_PHYSICS_CAPSULE}, + {"SOLID_PHYSICS_TRIMESH", "const float", QW|NQ|CS, NULL, SOLID_PHYSICS_TRIMESH}, + {"SOLID_PHYSICS_CYLINDER", "const float", QW|NQ|CS, NULL, SOLID_PHYSICS_CYLINDER}, + + {"GEOMTYPE_NONE", "const float", QW|NQ|CS, NULL, GEOMTYPE_NONE}, + {"GEOMTYPE_SOLID", "const float", QW|NQ|CS, NULL, GEOMTYPE_SOLID}, + {"GEOMTYPE_BOX", "const float", QW|NQ|CS, NULL, GEOMTYPE_BOX}, + {"GEOMTYPE_SPHERE", "const float", QW|NQ|CS, NULL, GEOMTYPE_SPHERE}, + {"GEOMTYPE_CAPSULE", "const float", QW|NQ|CS, NULL, GEOMTYPE_CAPSULE}, + {"GEOMTYPE_TRIMESH", "const float", QW|NQ|CS, NULL, GEOMTYPE_TRIMESH}, + {"GEOMTYPE_CYLINDER", "const float", QW|NQ|CS, NULL, GEOMTYPE_CYLINDER}, + {"GEOMTYPE_CAPSULE_X", "const float", QW|NQ|CS, NULL, GEOMTYPE_CAPSULE_X}, + {"GEOMTYPE_CAPSULE_Y", "const float", QW|NQ|CS, NULL, GEOMTYPE_CAPSULE_Y}, + {"GEOMTYPE_CAPSULE_Z", "const float", QW|NQ|CS, NULL, GEOMTYPE_CAPSULE_Z}, + {"GEOMTYPE_CYLINDER_X", "const float", QW|NQ|CS, NULL, GEOMTYPE_CYLINDER_X}, + {"GEOMTYPE_CYLINDER_Y", "const float", QW|NQ|CS, NULL, GEOMTYPE_CYLINDER_Y}, + {"GEOMTYPE_CYLINDER_Z", "const float", QW|NQ|CS, NULL, GEOMTYPE_CYLINDER_Z}, {"JOINTTYPE_FIXED", "const float", QW|NQ|CS, NULL, JOINTTYPE_FIXED}, {"JOINTTYPE_POINT", "const float", QW|NQ|CS, NULL, JOINTTYPE_POINT}, @@ -10342,6 +10296,26 @@ void PR_DumpPlatform_f(void) {"JOINTTYPE_UNIVERSAL", "const float", QW|NQ|CS, NULL, JOINTTYPE_UNIVERSAL}, {"JOINTTYPE_HINGE2", "const float", QW|NQ|CS, NULL, JOINTTYPE_HINGE2}, + {"GE_MAXENTS", "const float", CS, "Valid for getentity, ignores the entity argument. Returns the maximum number of entities which may be valid, to avoid having to poll 65k when only 100 are used.", GE_MAXENTS}, + {"GE_ACTIVE", "const float", CS, "Valid for getentity. Returns whether this entity is known to the client or not.", GE_ACTIVE}, + {"GE_ORIGIN", "const float", CS, "Valid for getentity. Returns the interpolated .origin.", GE_ORIGIN}, + {"GE_FORWARD", "const float", CS, "Valid for getentity. Returns the interpolated forward vector.", GE_FORWARD}, + {"GE_RIGHT", "const float", CS, "Valid for getentity. Returns the entity's right vector.", GE_RIGHT}, + {"GE_UP", "const float", CS, "Valid for getentity. Returns the entity's up vector.", GE_UP}, + {"GE_SCALE", "const float", CS, "Valid for getentity. Returns the entity .scale.", GE_SCALE}, + {"GE_ORIGINANDVECTORS","const float", CS, "Valid for getentity. Returns interpolated .origin, but also sets v_forward, v_right, and v_up accordingly. Use vectoangles(v_forward,v_up) to determine the angles.", GE_ORIGINANDVECTORS}, + {"GE_ALPHA", "const float", CS, "Valid for getentity. Returns the entity alpha.", GE_ALPHA}, + {"GE_COLORMOD", "const float", CS, "Valid for getentity. Returns the colormod vector.", GE_COLORMOD}, + {"GE_PANTSCOLOR", "const float", CS, "Valid for getentity. Returns the entity's lower color (from .colormap), as a palette range value.", GE_PANTSCOLOR}, + {"GE_SHIRTCOLOR", "const float", CS, "Valid for getentity. Returns the entity's lower color (from .colormap), as a palette range value.", GE_SHIRTCOLOR}, + {"GE_SKIN", "const float", CS, "Valid for getentity. Returns the entity's .skin index.", GE_SKIN}, + {"GE_MINS", "const float", CS, "Valid for getentity. Guesses the entity's .min vector.", GE_MINS}, + {"GE_MAXS", "const float", CS, "Valid for getentity. Guesses the entity's .max vector.", GE_MAXS}, + {"GE_ABSMIN", "const float", CS, "Valid for getentity. Guesses the entity's .absmin vector.", GE_ABSMIN}, + {"GE_ABSMAX", "const float", CS, "Valid for getentity. Guesses the entity's .absmax vector.", GE_ABSMAX}, +// {"GE_LIGHT", "const float", CS, NULL, GE_LIGHT}, + + {"DAMAGE_NO", "const float", QW|NQ, NULL, DAMAGE_NO}, {"DAMAGE_YES", "const float", QW|NQ, NULL, DAMAGE_YES}, {"DAMAGE_AIM", "const float", QW|NQ, NULL, DAMAGE_AIM}, diff --git a/engine/server/pr_lua.c b/engine/server/pr_lua.c new file mode 100644 index 00000000..e6cd9784 --- /dev/null +++ b/engine/server/pr_lua.c @@ -0,0 +1,2153 @@ +#include "quakedef.h" + +#ifdef VM_LUA + +#include "pr_common.h" +#include "hash.h" + +#define luagloballist \ + globalentity (true, self) \ + globalentity (true, other) \ + globalentity (true, world) \ + globalfloat (true, time) \ + globalfloat (true, frametime) \ + globalentity (false, newmis) \ + globalfloat (false, force_retouch) \ + globalstring (true, mapname) \ + globalfloat (false, deathmatch) \ + globalfloat (false, coop) \ + globalfloat (false, teamplay) \ + globalfloat (true, serverflags) \ + globalfloat (false, dimension_send) \ + globalfloat (false, physics_mode) \ + globalfloat (true, total_secrets) \ + globalfloat (true, total_monsters) \ + globalfloat (true, found_secrets) \ + globalfloat (true, killed_monsters) \ + globalvec (true, v_forward) \ + globalvec (true, v_up) \ + globalvec (true, v_right) \ + globalfloat (true, trace_allsolid) \ + globalfloat (true, trace_startsolid) \ + globalfloat (true, trace_fraction) \ + globalvec (true, trace_endpos) \ + globalvec (true, trace_plane_normal) \ + globalfloat (true, trace_plane_dist) \ + globalentity (true, trace_ent) \ + globalfloat (true, trace_inopen) \ + globalfloat (true, trace_inwater) \ + globalfloat (false, trace_endcontents) \ + globalfloat (false, trace_surfaceflags) \ + globalfloat (false, cycle_wrapped) \ + globalentity (false, msg_entity) \ + globalfunc (false, main) \ + globalfunc (true, StartFrame) \ + globalfunc (true, PlayerPreThink) \ + globalfunc (true, PlayerPostThink) \ + globalfunc (true, ClientKill) \ + globalfunc (true, ClientConnect) \ + globalfunc (true, PutClientInServer) \ + globalfunc (true, ClientDisconnect) \ + globalfunc (false, SetNewParms) \ + globalfunc (false, SetChangeParms) + +//any globals or functions that the server might want access to need to be known also. +#define luaextragloballist \ + globalstring (true, startspot) \ + globalstring (true, ClientReEnter) + +typedef struct +{ +#define globalentity(required, name) int name; +#define globalint(required, name) int name; +#define globalfloat(required, name) float name; +#define globalstring(required, name) string_t name; +#define globalvec(required, name) vec3_t name; +#define globalfunc(required, name) int name; +luagloballist +luaextragloballist +#undef globalentity +#undef globalint +#undef globalfloat +#undef globalstring +#undef globalvec +#undef globalfunc +} luaglobalvars_t; + +typedef struct +{ + int type; + ptrdiff_t offset; + char *name; + bucket_t buck; +} luafld_t; + +typedef struct lua_State lua_State; +typedef void *(QDECL *lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize); +typedef const char *(QDECL *lua_Reader)(lua_State *L, void *data, size_t *size); +typedef int (QDECL *lua_CFunction) (lua_State *L); +typedef double lua_Number; +typedef int lua_Integer; + +//I'm using this struct for all the global stuff. +static struct +{ + lua_State *ctx; + char readbuf[1024]; + pubprogfuncs_t progfuncs; + progexterns_t progfuncsparms; + edict_t **edicttable; + unsigned int maxedicts; + luaglobalvars_t globals; //internal global structure + hashtable_t globalfields; //name->luafld_t + luafld_t globflds[1024]; //fld->offset+type + hashtable_t entityfields; //name->luafld_t + luafld_t entflds[1024]; //fld->offset+type + + qboolean triedlib; + dllhandle_t lib; + + lua_State * (QDECL *newstate) (lua_Alloc f, void *ud); + lua_CFunction (QDECL *atpanic) (lua_State *L, lua_CFunction panicf); + void (QDECL *close) (lua_State *L); + int (QDECL *load) (lua_State *L, lua_Reader reader, void *dt, const char *chunkname, const char *mode); + int (QDECL *pcallk) (lua_State *L, int nargs, int nresults, int errfunc, int ctx, lua_CFunction k); + void (QDECL *callk) (lua_State *L, int nargs, int nresults, int ctx, lua_CFunction k); + void (QDECL *getfield) (lua_State *L, int idx, const char *k); + void (QDECL *setfield) (lua_State *L, int idx, const char *k); + void (QDECL *gettable) (lua_State *L, int idx); + void (QDECL *settable) (lua_State *L, int idx); + void (QDECL *getglobal) (lua_State *L, const char *var); + void (QDECL *setglobal) (lua_State *L, const char *var); + int (QDECL *error) (lua_State *L); + int (QDECL *type) (lua_State *L, int idx); + const char *(QDECL *typename) (lua_State *L, int tp); + void (QDECL *rawget) (lua_State *L, int idx); + void (QDECL *rawset) (lua_State *L, int idx); + void (QDECL *createtable) (lua_State *L, int narr, int nrec); + int (QDECL *setmetatable) (lua_State *L, int objindex); + void *(QDECL *newuserdata) (lua_State *L, size_t usize); + + void (QDECL *replace) (lua_State *L, int idx); + int (QDECL *gettop) (lua_State *L); + int (QDECL *settop) (lua_State *L, int idx); + void (QDECL *pushboolean) (lua_State *L, int b); + void (QDECL *pushnil) (lua_State *L); + void (QDECL *pushnumber) (lua_State *L, lua_Number n); + void (QDECL *pushinteger) (lua_State *L, lua_Integer n); + void (QDECL *pushvalue) (lua_State *L, int idx); + void (QDECL *pushcclosure) (lua_State *L, lua_CFunction fn, int n); + const char * (QDECL *pushfstring) (lua_State *L, const char *fmt, ...); + void (QDECL *pushlightuserdata) (lua_State *L, void *p); + const char * (QDECL *tolstring) (lua_State *L, int idx, size_t *len); + int (QDECL *toboolean) (lua_State *L, int idx); + lua_Number (QDECL *tonumberx) (lua_State *L, int idx, int *isnum); + lua_Integer (QDECL *tointegerx) (lua_State *L, int idx, int *isnum); + const void *(QDECL *topointer) (lua_State *L, int idx); + void *(QDECL *touserdata) (lua_State *L, int idx); + + int (QDECL *Lcallmeta) (lua_State *L, int obj, const char *e); + int (QDECL *Lnewmetatable) (lua_State *L, const char *tname); +} lua; +#define pcall(L,n,r,f) pcallk(L, (n), (r), (f), 0, NULL) +#define call(L,n,r) callk(L, (n), (r), 0, NULL) +#define pop(L,n) settop(L, -(n)-1) +#define pushstring(L,s) pushfstring(L,"%s",s) + +#define LUA_TNONE (-1) +#define LUA_TNIL 0 +#define LUA_TBOOLEAN 1 +#define LUA_TLIGHTUSERDATA 2 +#define LUA_TNUMBER 3 +#define LUA_TSTRING 4 +#define LUA_TTABLE 5 +#define LUA_TFUNCTION 6 +#define LUA_TUSERDATA 7 +#define LUA_TTHREAD 8 +#define LUA_NUMTAGS 9 + +#define LUA_REGISTRYINDEX (-1000000 - 1000) + +static qboolean init_lua(void) +{ + if (!lua.triedlib) + { + dllfunction_t luafuncs[] = + { + {(void*)&lua.newstate, "lua_newstate"}, + {(void*)&lua.atpanic, "lua_atpanic"}, + {(void*)&lua.close, "lua_close"}, + {(void*)&lua.load, "lua_load"}, + {(void*)&lua.pcallk, "lua_pcallk"}, + {(void*)&lua.callk, "lua_callk"}, + {(void*)&lua.getfield, "lua_getfield"}, + {(void*)&lua.setfield, "lua_setfield"}, + {(void*)&lua.gettable, "lua_gettable"}, + {(void*)&lua.settable, "lua_settable"}, + {(void*)&lua.getglobal, "lua_getglobal"}, + {(void*)&lua.setglobal, "lua_setglobal"}, + {(void*)&lua.error, "lua_error"}, + {(void*)&lua.type, "lua_type"}, + {(void*)&lua.typename, "lua_typename"}, + {(void*)&lua.rawget, "lua_rawget"}, + {(void*)&lua.rawset, "lua_rawset"}, + {(void*)&lua.createtable, "lua_createtable"}, + {(void*)&lua.setmetatable, "lua_setmetatable"}, + {(void*)&lua.newuserdata, "lua_newuserdata"}, + + {(void*)&lua.replace, "lua_replace"}, + {(void*)&lua.gettop, "lua_gettop"}, + {(void*)&lua.settop, "lua_settop"}, + {(void*)&lua.pushboolean, "lua_pushboolean"}, + {(void*)&lua.pushnil, "lua_pushnil"}, + {(void*)&lua.pushnumber, "lua_pushnumber"}, + {(void*)&lua.pushinteger, "lua_pushinteger"}, + {(void*)&lua.pushvalue, "lua_pushvalue"}, + {(void*)&lua.pushcclosure, "lua_pushcclosure"}, + {(void*)&lua.pushfstring, "lua_pushfstring"}, + {(void*)&lua.pushlightuserdata, "lua_pushlightuserdata"}, + {(void*)&lua.tolstring, "lua_tolstring"}, + {(void*)&lua.toboolean, "lua_toboolean"}, + {(void*)&lua.tonumberx, "lua_tonumberx"}, + {(void*)&lua.tointegerx, "lua_tointegerx"}, + {(void*)&lua.topointer, "lua_topointer"}, + {(void*)&lua.touserdata, "lua_touserdata"}, + + {(void*)&lua.Lcallmeta, "luaL_callmeta"}, + {(void*)&lua.Lnewmetatable, "luaL_newmetatable"}, + + {NULL, NULL} + }; + lua.triedlib = true; + lua.lib = Sys_LoadLibrary("lua52", luafuncs); + } + if (!lua.lib) + return false; + return true; +} + +static void *my_lua_alloc (void *ud, void *ptr, size_t osize, size_t nsize) +{ + if (nsize == 0) + { + free(ptr); + return NULL; + } + else + return realloc(ptr, nsize); +} +const char * my_lua_Reader(lua_State *L, void *data, size_t *size) +{ + vfsfile_t *f = data; + *size = VFS_READ(f, lua.readbuf, sizeof(lua.readbuf)); + return lua.readbuf; +} + +//replace lua's standard 'print' function to use the console instead of stdout. intended to use the same linebreak rules. +static int my_lua_print(lua_State *L) +{ + //the problem is that we can only really accept strings here. + //so lets just use the tostring function to make sure things are actually readable as strings. + int args = lua.gettop(L); + int i; + const char *s; + lua.getglobal(L, "tostring"); + //args now start at 1 + for(i = 1; i <= args; i++) + { + lua.pushvalue(L, -1); + lua.pushvalue(L, i); + lua.pcall(L, 1, 1, 0); //pops args+func + s = lua.tolstring(L, -1, NULL); + if(s == NULL) + s = "?"; + + if(i > 1) Con_Printf("\t"); + Con_Printf("%s", s); + lua.pop(L, 1); //pop our lstring + }; + lua.pop(L, 1); //pop the cached tostring. + Con_Printf("\n"); + return 0; +}; +//more like quakec's print +static int my_lua_conprint(lua_State *L) +{ + //the problem is that we can only really accept strings here. + //so lets just use the tostring function to make sure things are actually readable as strings. + int args = lua.gettop(L); + int i; + const char *s; + + lua.getglobal(L, "tostring"); + //args start at stack index 1 + for(i = 1; i <= args; i++) + { + lua.pushvalue(L, -1); //dupe the tostring + lua.pushvalue(L, i); //dupe the argument + lua.pcall(L, 1, 1, 0); //pops args+func, pushes the string result + s = lua.tolstring(L, -1, NULL); + if(s == NULL) + s = "?"; + + Con_Printf("%s", s); + lua.pop(L, 1); //pop our lstring + }; + lua.pop(L, 1); //pop the cached tostring. + return 0; +}; +static int bi_lua_dprint(lua_State *L) +{ + if (!developer.ival) + return 0; + return my_lua_conprint(L); +} + +//taken from lua's baselib.c, with dependancies reduced a little. +static int my_lua_tostring(lua_State *L) +{ +// if (lua.type(L, 1) == LUA_TNONE) +// luaL_argerror(L, narg, "value expected"); + if (lua.Lcallmeta(L, 1, "__tostring")) + return 1; + switch (lua.type(L, 1)) + { + case LUA_TNUMBER: + lua.pushfstring(L, lua.tolstring(L, 1, NULL)); + break; + case LUA_TSTRING: + lua.pushvalue(L, 1); + break; + case LUA_TBOOLEAN: + lua.pushstring(L, (lua.toboolean(L, 1) ? "true" : "false")); + break; + case LUA_TNIL: + lua.pushstring(L, "nil"); + break; + case LUA_TTABLE: + //special check for things that look like vectors. + lua.getfield(L, 1, "x"); + lua.getfield(L, 1, "y"); + lua.getfield(L, 1, "z"); + if (lua.type(L, -3) == LUA_TNUMBER && lua.type(L, -2) == LUA_TNUMBER && lua.type(L, -1) == LUA_TNUMBER) + { + lua.pushfstring(L, "'%g %g %g'", lua.tonumberx(L, -3, NULL), lua.tonumberx(L, -2, NULL), lua.tonumberx(L, -1, NULL)); + return 1; + } + //fallthrough + default: + lua.pushfstring(L, "%s: %p", lua.typename(L, lua.type(L, 1)), lua.topointer(L, 1)); + break; + } + return 1; +} + +static int my_lua_panic(lua_State *L) +{ + const char *s = lua.tolstring(L, -1, NULL); + Sys_Error("lua error: %s", s); +} + +static int my_lua_entity_eq(lua_State *L) +{ + //table1=1 + //table2=2 + unsigned int entnum1, entnum2; + lua.getfield(L, 1, "entnum"); + entnum1 = lua.tointegerx(L, -1, NULL); + lua.getfield(L, 2, "entnum"); + entnum2 = lua.tointegerx(L, -1, NULL); + lua.pop(L, 2); + + lua.pushboolean(L, entnum1 == entnum2); + return 1; +} + +static int my_lua_entity_tostring(lua_State *L) +{ + //table=1 + unsigned int entnum; + lua.getfield(L, 1, "entnum"); + entnum = lua.tointegerx(L, -1, NULL); + lua.pop(L, 1); + + lua.pushstring(L, va("entity: %u", entnum)); + return 1; +} + +static void lua_readvector(lua_State *L, int idx, float *result) +{ + switch(lua.type(L, idx)) + { + case LUA_TSTRING: + { + //we parse strings primnarily for easy .ent(or bsp) loading support. + const char *str = lua.tolstring(L, idx, NULL); + str = COM_Parse(str); + result[0] = atof(com_token); + str = COM_Parse(str); + result[1] = atof(com_token); + str = COM_Parse(str); + result[2] = atof(com_token); + } + break; + case LUA_TTABLE: + lua.getfield(L, idx, "x"); + result[0] = lua.tonumberx(L, -1, NULL); + lua.getfield(L, idx, "y"); + result[1] = lua.tonumberx(L, -1, NULL); + lua.getfield(L, idx, "z"); + result[2] = lua.tonumberx(L, -1, NULL); + lua.pop(L, 3); + break; + case LUA_TNIL: + result[0] = 0; + result[1] = 0; + result[2] = 0; + break; + default: + Con_Printf("Expected vector, got something that wasn't\n"); + } +} + +static int my_lua_entity_set(lua_State *L) //__newindex +{ +// Con_Printf("lua_entity_set: "); +// my_lua_print(L); + //table=1 + //key=2 + //value=3 + + if (lua.type(L, 2) == LUA_TSTRING) + { + const char *s = lua.tolstring(L, 2, NULL); + luafld_t *fld = Hash_GetInsensitive(&lua.entityfields, s); + eval_t *eval; + unsigned int entnum; + if (fld) + { + lua.getfield(L, 1, "entnum"); + entnum = lua.tointegerx(L, -1, NULL); + lua.pop(L, 1); + if (entnum < lua.maxedicts && lua.edicttable[entnum] && !lua.edicttable[entnum]->isfree) + { + eval = (eval_t*)((char*)lua.edicttable[entnum]->v + fld->offset); + switch(fld->type) + { + case ev_float: + eval->_float = lua.tonumberx(L, 3, NULL); + return 0; + case ev_vector: + lua_readvector(L, 3, eval->_vector); + return 0; + case ev_integer: + eval->_int = lua.tointegerx(L, 3, NULL); + return 0; + case ev_function: + if (lua.type(L, 3) == LUA_TNIL) + eval->function = 0; //so the engine can distinguish between nil and not. + else + eval->function = fld->offset | ((entnum+1)<<10); + lua.pushlightuserdata(L, (void *)fld->offset); //execute only knows a function id, so we need to translate the store to match. + lua.replace(L, 2); + lua.rawset(L, 1); + return 0; + case ev_string: + if (lua.type(L, 3) == LUA_TNIL) + eval->string = 0; //so the engine can distinguish between nil and not. + else + eval->string = fld->offset | ((entnum+1)<<10); + lua.pushlightuserdata(L, (void *)fld->offset); //execute only knows a string id, so we need to translate the store to match. + lua.replace(L, 2); + lua.rawset(L, 1); + return 0; + case ev_entity: + //read the table's entnum field so we know which one its meant to be. + lua.getfield(L, 3, "entnum"); + eval->edict = lua.tointegerx(L, -1, NULL); + return 0; + } + } + } + } + + lua.rawset(L, 1); + return 0; +} +static int my_lua_entity_get(lua_State *L) //__index +{ +// Con_Printf("lua_entity_get: "); +// my_lua_print(L); + //table=1 + //key=2 + + if (lua.type(L, 2) == LUA_TSTRING) + { + const char *s = lua.tolstring(L, 2, NULL); + luafld_t *fld = Hash_GetInsensitive(&lua.entityfields, s); + eval_t *eval; + int entnum; + if (fld) + { + lua.getfield(L, 1, "entnum"); + entnum = lua.tointegerx(L, -1, NULL); + lua.pop(L, 1); + if (entnum < lua.maxedicts && lua.edicttable[entnum])// && !lua.edicttable[entnum]->isfree) + { + eval = (eval_t*)((char*)lua.edicttable[entnum]->v + fld->offset); + switch(fld->type) + { + case ev_float: + lua.pushnumber(L, eval->_float); + return 1; + case ev_integer: + lua.pushinteger(L, eval->_int); + return 1; + case ev_vector: + lua.createtable(L, 0, 0); + //FIXME: should provide a metatable with a __tostring + lua.pushnumber(L, eval->_vector[0]); + lua.setfield (L, -2, "x"); + lua.pushnumber(L, eval->_vector[1]); + lua.setfield (L, -2, "y"); + lua.pushnumber(L, eval->_vector[2]); + lua.setfield (L, -2, "z"); + return 1; + case ev_function: + case ev_string: + lua.pushlightuserdata(L, (void *)(eval->function & 1023)); //execute only knows a function id, so we need to translate the store to match. + lua.replace(L, 2); + lua.rawget(L, 1); + return 1; + case ev_entity: + //return the table for the entity via the lua registry. + lua.pushlightuserdata(lua.ctx, lua.edicttable[eval->edict]); + lua.gettable(lua.ctx, LUA_REGISTRYINDEX); + return 1; + } + } + } + } + + //make sure it exists so we don't get called constantly if code loops through stuff that wasn't set. +// lua.pushstring(L, "nil"); + lua.rawget(L, 1); + return 1; +} +static int my_lua_global_set(lua_State *L) //__newindex +{ +// Con_Printf("my_lua_global_set: "); +// my_lua_print(L); + //table=1 + //key=2 + //value=3 + + if (lua.type(L, 2) == LUA_TSTRING) + { + const char *s = lua.tolstring(L, 2, NULL); + luafld_t *fld = Hash_GetInsensitive(&lua.globalfields, s); + eval_t *eval; + if (fld) + { + eval = (eval_t*)((char*)&lua.globals + fld->offset); + switch(fld->type) + { + case ev_float: + eval->_float = lua.tonumberx(L, 3, NULL); + return 0; + case ev_vector: + lua.getfield(L, 3, "x"); + eval->_vector[0] = lua.tonumberx(L, -1, NULL); + lua.getfield(L, 3, "y"); + eval->_vector[1] = lua.tonumberx(L, -1, NULL); + lua.getfield(L, 3, "z"); + eval->_vector[2] = lua.tonumberx(L, -1, NULL); + return 0; + case ev_integer: + eval->_int = lua.tointegerx(L, 3, NULL); + return 0; + case ev_function: + if (lua.type(L, 3) == LUA_TNIL) + eval->function = 0; //so the engine can distinguish between nil and not. + else + eval->function = fld->offset; + lua.pushlightuserdata(L, (void *)fld->offset); //execute only knows a function id, so we need to translate the store to match. + lua.replace(L, 2); + lua.rawset(L, 1); + return 0; + case ev_string: + if (lua.type(L, 3) == LUA_TNIL) + eval->string = 0; //so the engine can distinguish between nil and not. + else + eval->string = fld->offset; + lua.pushlightuserdata(L, (void *)fld->offset); //execute only knows a string id, so we need to translate the store to match. + lua.replace(L, 2); + lua.rawset(L, 1); + return 0; + case ev_entity: + //read the table's entnum field so we know which one its meant to be. + lua.getfield(L, 3, "entnum"); + eval->edict = lua.tointegerx(L, -1, NULL); + return 0; + } + } + } + + lua.rawset(L, 1); + return 0; +} +static int my_lua_global_get(lua_State *L) //__index +{ +// Con_Printf("my_lua_global_get: "); +// my_lua_print(L); + //table=1 + //key=2 + + if (lua.type(L, 2) == LUA_TSTRING) + { + const char *s = lua.tolstring(L, 2, NULL); + luafld_t *fld = Hash_GetInsensitive(&lua.globalfields, s); + eval_t *eval; + if (fld) + { + eval = (eval_t*)((char*)&lua.globals + fld->offset); + switch(fld->type) + { + case ev_float: + lua.pushnumber(L, eval->_float); + return 1; + case ev_function: + lua.pushlightuserdata(L, (void *)eval->function); //execute only knows a function id, so we need to translate the store to match. + lua.replace(L, 2); + lua.rawget(L, 1); + return 1; + case ev_entity: + //return the table for the entity via the lua registry. + lua.pushlightuserdata(lua.ctx, lua.edicttable[eval->edict]); + lua.gettable(lua.ctx, LUA_REGISTRYINDEX); + return 1; + } + } + } + + //make sure it exists so we don't get called constantly if code loops through stuff that wasn't set. +// lua.pushstring(L, "nil"); + lua.rawget(L, 1); + return 1; +} + +static int bi_lua_setmodel(lua_State *L) +{ + int entnum; + edict_t *e; + lua.getfield(L, 1, "entnum"); + entnum = lua.tointegerx(L, -1, NULL); + e = (entnum>=lua.maxedicts)?NULL:lua.edicttable[entnum]; + PF_setmodel_Internal(&lua.progfuncs, e, lua.tolstring(L, 2, NULL)); + return 0; +} +static int bi_lua_precache_model(lua_State *L) +{ + PF_precache_model_Internal(&lua.progfuncs, lua.tolstring(L, 1, NULL), false); + return 0; +} +static int bi_lua_precache_sound(lua_State *L) +{ + PF_precache_sound_Internal(&lua.progfuncs, lua.tolstring(L, 1, NULL)); + return 0; +} +static int bi_lua_lightstyle(lua_State *L) +{ + PF_applylightstyle(lua.tointegerx(L, 1, NULL), lua.tolstring(L, 2, NULL), 7); + return 0; +} +static int bi_lua_spawn(lua_State *L) +{ + edict_t *e = lua.progfuncs.EntAlloc(&lua.progfuncs); + if (e) + { + lua.pushlightuserdata(L, e); + lua.gettable(L, LUA_REGISTRYINDEX); + } + else + lua.pushnil(L); + return 1; +} +static int bi_lua_remove(lua_State *L) +{ + int entnum; + edict_t *e; + lua.getfield(L, 1, "entnum"); + entnum = lua.tointegerx(L, -1, NULL); + e = (entnum>=lua.maxedicts)?NULL:lua.edicttable[entnum]; + if (e) + lua.progfuncs.EntFree(&lua.progfuncs, e); + return 0; +} +static int bi_lua_setorigin(lua_State *L) +{ + edict_t *e; + lua.getfield(L, 1, "entnum"); + e = EDICT_NUM((&lua.progfuncs), lua.tointegerx(L, -1, NULL)); + lua_readvector(L, 2, e->v->origin); + World_LinkEdict (&sv.world, (wedict_t*)e, false); + return 0; +} +static int bi_lua_setsize(lua_State *L) +{ + edict_t *e; + lua.getfield(L, 1, "entnum"); + e = EDICT_NUM((&lua.progfuncs), lua.tointegerx(L, -1, NULL)); + lua_readvector(L, 2, e->v->mins); + lua_readvector(L, 3, e->v->maxs); + VectorSubtract (e->v->maxs, e->v->mins, e->v->size); + World_LinkEdict (&sv.world, (wedict_t*)e, false); + return 0; +} + +static int bi_lua_localcmd(lua_State *L) +{ + const char *str = lua.tolstring(lua.ctx, 1, NULL); + Cbuf_AddText (str, RESTRICT_INSECURE); + return 0; +} +static int bi_lua_changelevel(lua_State *L) +{ + const char *s, *spot; + +// make sure we don't issue two changelevels (unless the last one failed) + if (sv.mapchangelocked) + return 0; + sv.mapchangelocked = true; + + if (lua.type(L, 2) == LUA_TSTRING) //and not nil or none + { + s = lua.tolstring(lua.ctx, 1, NULL); + spot = lua.tolstring(lua.ctx, 2, NULL); + Cbuf_AddText (va("\nchangelevel %s %s\n",s, spot), RESTRICT_LOCAL); + } + else + { + s = lua.tolstring(lua.ctx, 1, NULL); + Cbuf_AddText (va("\nmap %s\n",s), RESTRICT_LOCAL); + } + return 0; +} + +static int bi_lua_stuffcmd(lua_State *L) +{ + int entnum; + const char *str; + lua.getfield(L, 1, "entnum"); + entnum = lua.tointegerx(L, -1, NULL); + str = lua.tolstring(L, 2, NULL); + + PF_stuffcmd_Internal(entnum, str); + return 0; +} +static int bi_lua_centerprint(lua_State *L) +{ + int entnum; + const char *str; + lua.getfield(L, 1, "entnum"); + entnum = lua.tointegerx(L, -1, NULL); + str = lua.tolstring(L, 2, NULL); + + PF_centerprint_Internal(entnum, false, str); + return 0; +} +static int bi_lua_getinfokey(lua_State *L) +{ + int entnum; + const char *key; + lua.getfield(L, 1, "entnum"); + entnum = lua.tointegerx(L, -1, NULL); + key = lua.tolstring(L, 2, NULL); + + key = PF_infokey_Internal(entnum, key); + lua.pushstring(L, key); + return 1; +} +static int bi_lua_setinfokey(lua_State *L) +{ + int entnum; + const char *key; + const char *value; + int result; + lua.getfield(L, 1, "entnum"); + entnum = lua.tointegerx(L, -1, NULL); + key = lua.tolstring(L, 2, NULL); + value = lua.tolstring(L, 3, NULL); + + result = PF_ForceInfoKey_Internal(entnum, key, value); + lua.pushinteger(L, result); + return 1; +} +static int bi_lua_ambientsound(lua_State *L) +{ + vec3_t pos; + const char *samp = lua.tolstring(L, 2, NULL); + float vol = lua.tonumberx(L, 3, NULL); + float attenuation = lua.tonumberx(L, 4, NULL); + lua_readvector(L, 1, pos); + + PF_ambientsound_Internal(pos, samp, vol, attenuation); + return 0; +} +static int bi_lua_sound(lua_State *L) +{ + int entnum; + float channel = lua.tonumberx(L, 2, NULL); + const char *samp = lua.tolstring(L, 3, NULL); + float volume = lua.tonumberx(L, 4, NULL); + float attenuation = lua.tonumberx(L, 5, NULL); + + lua.getfield(L, 1, "entnum"); + entnum = lua.tointegerx(L, -1, NULL); + + //note: channel & 256 == reliable + + SVQ1_StartSound (NULL, (wedict_t*)EDICT_NUM((&lua.progfuncs), entnum), channel, samp, volume*255, attenuation, 0); + return 0; +} + +static int bi_lua_pointcontents(lua_State *L) +{ + vec3_t pos; + lua_readvector(L, 1, pos); + lua.pushinteger(L, sv.world.worldmodel->funcs.PointContents(sv.world.worldmodel, NULL, pos)); + return 1; +} + +static int bi_lua_setspawnparms(lua_State *L) +{ + globalvars_t pr_globals; + + lua.getfield(L, 1, "entnum"); + pr_globals.param[0].i = lua.tointegerx(L, -1, NULL); + PF_setspawnparms(&lua.progfuncs, &pr_globals); + return 0; +} +static int bi_lua_makestatic(lua_State *L) +{ + globalvars_t pr_globals; + + lua.getfield(L, 1, "entnum"); + pr_globals.param[0].i = lua.tointegerx(L, -1, NULL); + PF_makestatic(&lua.progfuncs, &pr_globals); + return 0; +} + +static int bi_lua_droptofloor(lua_State *L) +{ + extern cvar_t pr_droptofloorunits; + wedict_t *ent; + vec3_t end; + vec3_t start; + trace_t trace; + const float *gravitydir; + extern const vec3_t standardgravity; + pubprogfuncs_t *prinst = &lua.progfuncs; + world_t *world = prinst->parms->user; + + ent = PROG_TO_WEDICT((&lua.progfuncs), pr_global_struct->self); + + if (ent->xv->gravitydir[2] || ent->xv->gravitydir[1] || ent->xv->gravitydir[0]) + gravitydir = ent->xv->gravitydir; + else + gravitydir = standardgravity; + + VectorCopy (ent->v->origin, end); + if (pr_droptofloorunits.value > 0) + VectorMA(end, pr_droptofloorunits.value, gravitydir, end); + else + VectorMA(end, 256, gravitydir, end); + + VectorCopy (ent->v->origin, start); + trace = World_Move (world, start, ent->v->mins, ent->v->maxs, end, MOVE_NORMAL, ent); + + if (trace.fraction == 1 || trace.allsolid) + lua.pushboolean(L, false); + else + { + VectorCopy (trace.endpos, ent->v->origin); + World_LinkEdict (world, ent, false); + ent->v->flags = (int)ent->v->flags | FL_ONGROUND; + ent->v->groundentity = EDICT_TO_PROG(prinst, trace.ent); + lua.pushboolean(L, true); + } + return 1; +} + +static int bi_lua_checkbottom(lua_State *L) +{ + qboolean okay; + int entnum; + lua.getfield(L, 1, "entnum"); + entnum = lua.tointegerx(L, -1, NULL); + okay = World_CheckBottom(&sv.world, (wedict_t*)EDICT_NUM((&lua.progfuncs), entnum)); + lua.pushboolean(L, okay); + return 1; +} + +static int bi_lua_bprint(lua_State *L) +{ + int level = lua.tointegerx(L, 1, NULL); + const char *str = lua.tolstring(L, 2, NULL); + SV_BroadcastPrintf (level, "%s", str); + return 0; +} +static int bi_lua_sprint(lua_State *L) +{ + int entnum; + int level = lua.tointegerx(L, 2, NULL); + const char *str = lua.tolstring(L, 3, NULL); + + lua.getfield(L, 1, "entnum"); + entnum = lua.tointegerx(L, -1, NULL); + + if (entnum < 1 || entnum > sv.allocated_client_slots) + { + Con_TPrintf ("tried to sprint to a non-client\n"); + return 0; + } + SV_ClientPrintf (&svs.clients[entnum-1], level, "%s", str); + return 0; +} + +static int bi_lua_cvar_set(lua_State *L) +{ + const char *name = lua.tolstring(L, 1, NULL); + const char *str = lua.tolstring(L, 2, NULL); + cvar_t *var = Cvar_FindVar(name); + if (var) + Cvar_Set(var, str); + return 0; +} +static int bi_lua_cvar_get(lua_State *L) +{ + const char *name = lua.tolstring(L, 1, NULL); + cvar_t *var = Cvar_FindVar(name); + if (var) + lua.pushstring(L, var->string); + else + lua.pushnil(L); + return 0; +} + +static void set_trace_globals(trace_t *trace) +{ + pr_global_struct->trace_allsolid = trace->allsolid; + pr_global_struct->trace_startsolid = trace->startsolid; + pr_global_struct->trace_fraction = trace->fraction; + pr_global_struct->trace_inwater = trace->inwater; + pr_global_struct->trace_inopen = trace->inopen; + pr_global_struct->trace_surfaceflags = trace->surface?trace->surface->flags:0; + pr_global_struct->trace_endcontents = trace->contents; +// if (trace.fraction != 1) +// VectorMA (trace->endpos, 4, trace->plane.normal, P_VEC(trace_endpos)); +// else + VectorCopy (trace->endpos, P_VEC(trace_endpos)); + VectorCopy (trace->plane.normal, P_VEC(trace_plane_normal)); + pr_global_struct->trace_plane_dist = trace->plane.dist; + pr_global_struct->trace_ent = trace->ent?((wedict_t*)trace->ent)->entnum:0; +} + +static int bi_lua_traceline(lua_State *L) +{ + vec3_t v1, v2; + trace_t trace; + int nomonsters; + wedict_t *ent; + int savedhull; + + lua_readvector(L, 1, v1); + lua_readvector(L, 2, v2); + nomonsters = lua.tointegerx(L, 3, NULL); + lua.getfield(L, 4, "entnum"); + ent = (wedict_t*)EDICT_NUM((&lua.progfuncs), lua.tointegerx(L, -1, NULL)); + + savedhull = ent->xv->hull; + ent->xv->hull = 0; + trace = World_Move (&sv.world, v1, vec3_origin, vec3_origin, v2, nomonsters, (wedict_t*)ent); + ent->xv->hull = savedhull; + + //FIXME: should we just return a table instead, and ignore the globals? + set_trace_globals(&trace); + return 0; +} + +static int bi_lua_tracebox(lua_State *L) +{ + vec3_t v1, v2, mins, maxs; + trace_t trace; + int nomonsters; + wedict_t *ent; + int savedhull; + + lua_readvector(L, 1, v1); + lua_readvector(L, 2, v2); + lua_readvector(L, 3, mins); + lua_readvector(L, 4, maxs); + nomonsters = lua.tointegerx(L, 5, NULL); + lua.getfield(L, 6, "entnum"); + ent = (wedict_t*)EDICT_NUM((&lua.progfuncs), lua.tointegerx(L, -1, NULL)); + + savedhull = ent->xv->hull; + ent->xv->hull = 0; + trace = World_Move (&sv.world, v1, mins, maxs, v2, nomonsters, (wedict_t*)ent); + ent->xv->hull = savedhull; + + //FIXME: should we just return a table instead, and ignore the globals? + set_trace_globals(&trace); + return 0; +} + +static int bi_lua_walkmove(lua_State *L) +{ + pubprogfuncs_t *prinst = &lua.progfuncs; + world_t *world = prinst->parms->user; + wedict_t *ent; + float yaw, dist; + vec3_t move; + int oldself; + + ent = PROG_TO_WEDICT(prinst, *world->g.self); + yaw = lua.tonumberx(L, 1, NULL); + dist = lua.tonumberx(L, 2, NULL); + + if ( !( (int)ent->v->flags & (FL_ONGROUND|FL_FLY|FL_SWIM) ) ) + { + lua.pushboolean(L, false); + return 1; + } + + yaw = yaw*M_PI*2 / 360; + + move[0] = cos(yaw)*dist; + move[1] = sin(yaw)*dist; + move[2] = 0; + +// save program state, because World_movestep may call other progs + oldself = *world->g.self; + + lua.pushboolean(L, World_movestep(world, ent, move, true, false, NULL, NULL)); + +// restore program state + *world->g.self = oldself; + + return 1; +} +static int bi_lua_movetogoal(lua_State *L) +{ + pubprogfuncs_t *prinst = &lua.progfuncs; + world_t *world = prinst->parms->user; + wedict_t *ent; + float dist; + ent = PROG_TO_WEDICT(prinst, *world->g.self); + dist = lua.tonumberx(L, 1, NULL); + World_MoveToGoal (world, ent, dist); + return 0; +} + +static int bi_lua_nextent(lua_State *L) +{ + world_t *world = &sv.world; + int i; + wedict_t *ent; + + lua.getfield(L, 1, "entnum"); + i = lua.tointegerx(L, -1, NULL); + + while (1) + { + i++; + if (i == world->num_edicts) + { + ent = world->edicts; + break; + } + ent = WEDICT_NUM(world->progs, i); + if (!ent->isfree) + { + break; + } + } + lua.pushlightuserdata(L, ent); + lua.gettable(L, LUA_REGISTRYINDEX); + return 1; +} + +static int bi_lua_nextclient(lua_State *L) +{ + world_t *world = &sv.world; + int i; + wedict_t *ent; + + lua.getfield(L, 1, "entnum"); + i = lua.tointegerx(L, -1, NULL); + + while (1) + { + i++; + if (i == sv.allocated_client_slots) + { + ent = world->edicts; + break; + } + ent = WEDICT_NUM(world->progs, i); + if (!ent->isfree) + { + break; + } + } + lua.pushlightuserdata(L, ent); + lua.gettable(L, LUA_REGISTRYINDEX); + return 1; +} + +static int bi_lua_checkclient(lua_State *L) +{ + pubprogfuncs_t *prinst = &lua.progfuncs; + wedict_t *ent; + ent = WEDICT_NUM(prinst, PF_checkclient_Internal(prinst)); + lua.pushlightuserdata(L, ent); + lua.gettable(L, LUA_REGISTRYINDEX); + return 1; +} + +static int bi_lua_random(lua_State *L) +{ + lua.pushnumber(L, (rand ()&0x7fff) / ((float)0x8000)); + return 1; +} + +static int bi_lua_makevectors(lua_State *L) +{ + vec3_t angles; + //this is annoying as fuck in lua, what with it writing globals and stuff. + //perhaps we should support f,u,l=makevectors(ang)... meh, cba. + lua_readvector(L, 1, angles); + AngleVectors (angles, pr_global_struct->v_forward, pr_global_struct->v_right, pr_global_struct->v_up); + return 0; +} +static int bi_lua_vectoangles(lua_State *L) +{ + vec3_t forward; + vec3_t up; + float *uv = NULL; + vec3_t ret; + lua_readvector(L, 1, forward); + if (lua.type(L, 2) != LUA_TNONE) + { + lua_readvector(L, 1, up); + uv = up; + } + VectorAngles(forward, up, ret); + + lua.createtable(L, 0, 0); + //FIXME: should provide a metatable with a __tostring + lua.pushnumber(L, ret[0]); + lua.setfield (L, -2, "x"); + lua.pushnumber(L, ret[1]); + lua.setfield (L, -2, "y"); + lua.pushnumber(L, ret[2]); + lua.setfield (L, -2, "z"); + return 1; +} + +static int bi_lua_tokenize(lua_State *L) +{ + const char *instring = lua.tolstring(L, 1, NULL); + int argc = 0; + lua.createtable(L, 0, 0); + while(NULL != (instring = COM_Parse(instring))) + { + //lua is traditionally 1-based + //for i=1,t.argc do + lua.pushinteger(L, ++argc); + lua.pushstring(L, com_token); + lua.settable(L, -3); + + if (argc == 1) + { + while (*instring == ' ' || *instring == '\t') + instring++; + lua.pushstring(L, instring); + lua.setfield(L, -2, "args"); //args is all-but-the-first + } + } + lua.pushinteger(L, argc); + lua.setfield(L, -2, "argc"); //argc is the count. + return 1; +} + +static int bi_lua_findradiuschain(lua_State *L) +{ + extern cvar_t sv_gameplayfix_blowupfallenzombies; + world_t *world = &sv.world; + edict_t *ent, *chain; + float rad; + vec3_t org; + vec3_t eorg; + int i, j; + + chain = (edict_t *)world->edicts; + + lua_readvector(L, 1, org); + rad = lua.tonumberx(L, 2, NULL); + rad = rad*rad; + + for (i=1 ; inum_edicts ; i++) + { + ent = EDICT_NUM(world->progs, i); + if (ent->isfree) + continue; + if (ent->v->solid == SOLID_NOT && (progstype != PROG_QW || !((int)ent->v->flags & FL_FINDABLE_NONSOLID)) && !sv_gameplayfix_blowupfallenzombies.value) + continue; + for (j=0 ; j<3 ; j++) + eorg[j] = org[j] - (ent->v->origin[j] + (ent->v->mins[j] + ent->v->maxs[j])*0.5); + if (DotProduct(eorg,eorg) > rad) + continue; + + ent->v->chain = EDICT_TO_PROG(world->progs, chain); + chain = ent; + } + + lua.pushlightuserdata(L, chain); + lua.gettable(L, LUA_REGISTRYINDEX); + return 1; +} +static int bi_lua_findradiustable(lua_State *L) +{ + extern cvar_t sv_gameplayfix_blowupfallenzombies; + world_t *world = &sv.world; + edict_t *ent, *chain; + float rad; + vec3_t org; + vec3_t eorg; + int i, j; + int results = 1; //lua arrays are 1-based + + chain = (edict_t *)world->edicts; + + lua_readvector(L, 1, org); + rad = lua.tonumberx(L, 2, NULL); + rad = rad*rad; + + lua.createtable(L, 0, 0); //our return value. + + for (i=1 ; inum_edicts ; i++) + { + ent = EDICT_NUM(world->progs, i); + if (ent->isfree) + continue; + if (ent->v->solid == SOLID_NOT && (progstype != PROG_QW || !((int)ent->v->flags & FL_FINDABLE_NONSOLID)) && !sv_gameplayfix_blowupfallenzombies.value) + continue; + for (j=0 ; j<3 ; j++) + eorg[j] = org[j] - (ent->v->origin[j] + (ent->v->mins[j] + ent->v->maxs[j])*0.5); + if (DotProduct(eorg,eorg) > rad) + continue; + + lua.pushinteger(L, ++results); + lua.pushlightuserdata(L, ent); + lua.gettable(L, LUA_REGISTRYINDEX); + lua.settable(L, -3); + } + + lua.pushlightuserdata(L, chain); + lua.gettable(L, LUA_REGISTRYINDEX); + return 1; +} +#define bi_lua_findradius bi_lua_findradiuschain + +static int bi_lua_multicast(lua_State *L) +{ + int dest; + vec3_t org; + + dest = lua.tointegerx(L, 1, NULL); + lua_readvector(L, 2, org); + + NPP_Flush(); + SV_Multicast (org, dest); + + return 0; +} +extern sizebuf_t csqcmsgbuffer; +static int bi_lua_writechar(lua_State *L) +{ + globalvars_t pr_globals; + pr_globals.param[0].f = (csqcmsgbuffer.maxsize?MSG_CSQC:MSG_MULTICAST); + pr_globals.param[1].f = lua.tonumberx(L, 1, NULL); + PF_WriteChar(&lua.progfuncs, &pr_globals); + return 0; +} +static int bi_lua_writebyte(lua_State *L) +{ + globalvars_t pr_globals; + pr_globals.param[0].f = (csqcmsgbuffer.maxsize?MSG_CSQC:MSG_MULTICAST); + pr_globals.param[1].f = lua.tonumberx(L, 1, NULL); + PF_WriteByte(&lua.progfuncs, &pr_globals); + return 0; +} +static int bi_lua_writeshort(lua_State *L) +{ + globalvars_t pr_globals; + pr_globals.param[0].f = (csqcmsgbuffer.maxsize?MSG_CSQC:MSG_MULTICAST); + pr_globals.param[1].f = lua.tonumberx(L, 1, NULL); + PF_WriteShort(&lua.progfuncs, &pr_globals); + return 0; +} +static int bi_lua_writelong(lua_State *L) +{ + globalvars_t pr_globals; + pr_globals.param[0].f = (csqcmsgbuffer.maxsize?MSG_CSQC:MSG_MULTICAST); + pr_globals.param[1].f = lua.tonumberx(L, 1, NULL); + PF_WriteLong(&lua.progfuncs, &pr_globals); + return 0; +} +static int bi_lua_writeangle(lua_State *L) +{ + globalvars_t pr_globals; + pr_globals.param[0].f = (csqcmsgbuffer.maxsize?MSG_CSQC:MSG_MULTICAST); + pr_globals.param[1].f = lua.tonumberx(L, 1, NULL); + PF_WriteAngle(&lua.progfuncs, &pr_globals); + return 0; +} +static int bi_lua_writecoord(lua_State *L) +{ + globalvars_t pr_globals; + pr_globals.param[0].f = (csqcmsgbuffer.maxsize?MSG_CSQC:MSG_MULTICAST); + pr_globals.param[1].f = lua.tonumberx(L, 1, NULL); + PF_WriteCoord(&lua.progfuncs, &pr_globals); + return 0; +} +static int bi_lua_writestring(lua_State *L) +{ + PF_WriteString_Internal((csqcmsgbuffer.maxsize?MSG_CSQC:MSG_MULTICAST),lua.tolstring(L, 1, NULL)); + return 0; +} +static int bi_lua_writeentity(lua_State *L) +{ + globalvars_t pr_globals; + lua.getfield(L, 1, "entnum"); + pr_globals.param[0].f = (csqcmsgbuffer.maxsize?MSG_CSQC:MSG_MULTICAST); + pr_globals.param[1].i = lua.tointegerx(L, -1, NULL); + PF_WriteEntity(&lua.progfuncs, &pr_globals); + return 0; +} + +static int bi_lua_bitnot(lua_State *L) +{ + lua.pushinteger(L, ~lua.tointegerx(L, 1, NULL)); + return 1; +} +static int bi_lua_bitclear(lua_State *L) +{ + lua.pushinteger(L, lua.tointegerx(L, 1, NULL)&~lua.tointegerx(L, 2, NULL)); + return 1; +} +static int bi_lua_bitset(lua_State *L) +{ + lua.pushnumber(L, lua.tointegerx(L, 1, NULL)|lua.tointegerx(L, 2, NULL)); + return 1; +} +#define bi_lua_bitor bi_lua_bitset +static int bi_lua_bitand(lua_State *L) +{ + lua.pushnumber(L, lua.tointegerx(L, 1, NULL)&lua.tointegerx(L, 2, NULL)); + return 1; +} +static int bi_lua_bitxor(lua_State *L) +{ + lua.pushnumber(L, lua.tointegerx(L, 1, NULL)^lua.tointegerx(L, 2, NULL)); + return 1; +} + +static int bi_lua_sin(lua_State *L) +{ + lua.pushnumber(L, sin(lua.tonumberx(L, 1, NULL))); + return 1; +} +static int bi_lua_cos(lua_State *L) +{ + lua.pushnumber(L, cos(lua.tonumberx(L, 1, NULL))); + return 1; +} +static int bi_lua_atan2(lua_State *L) +{ + lua.pushnumber(L, atan2(lua.tonumberx(L, 1, NULL), lua.tonumberx(L, 2, NULL))); + return 1; +} +static int bi_lua_sqrt(lua_State *L) +{ + lua.pushnumber(L, sin(lua.tonumberx(L, 1, NULL))); + return 1; +} +static int bi_lua_floor(lua_State *L) +{ + lua.pushnumber(L, floor(lua.tonumberx(L, 1, NULL))); + return 1; +} +static int bi_lua_ceil(lua_State *L) +{ + lua.pushnumber(L, ceil(lua.tonumberx(L, 1, NULL))); + return 1; +} +static int bi_lua_acos(lua_State *L) +{ + lua.pushnumber(L, acos(lua.tonumberx(L, 1, NULL))); + return 1; +} + +typedef struct +{ + lua_State *L; + int idx; +} luafsenum_t; +static int QDECL lua_file_enumerate(const char *fname, qofs_t fsize, void *param, searchpathfuncs_t *spath) +{ + luafsenum_t *e = param; + lua.pushinteger(e->L, e->idx++); + lua.pushfstring(e->L, "%s", fname); + lua.settable(e->L, -3); + return true; +} +static int bi_lua_getfilelist(lua_State *L) +{ + luafsenum_t e; + const char *path = lua.tolstring(L, 1, NULL); + e.L = L; + e.idx = 1; //lua arrays are 1-based. + + lua.createtable(L, 0, 0); //our return value. + COM_EnumerateFiles(path, lua_file_enumerate, &e); + return 1; +} + +static int bi_lua_fclose(lua_State *L) +{ + //both fclose and __gc. + //should we use a different function so that we can warn on dupe fcloses without bugging out on fclose+gc? + //meh, cba + vfsfile_t **f = lua.touserdata(L, 1); + if (f && *f != NULL) + { + VFS_CLOSE(*f); + *f = NULL; + } + return 0; +} +static int bi_lua_fopen(lua_State *L) +{ + vfsfile_t *f; + const char *fname = lua.tolstring(L, 1, NULL); + qboolean read = true; + vfsfile_t **ud; + if (read) + f = FS_OpenVFS(fname, "rb", FS_GAME); + else + f = FS_OpenVFS(fname, "wb", FS_GAMEONLY); + if (!f) + { + lua.pushnil(L); + return 1; + } + ud = lua.newuserdata(L, sizeof(vfsfile_t*)); + *ud = f; + + lua.createtable(L, 0, 0); + lua.pushcclosure(L, bi_lua_fclose, 0); + lua.setfield(L, -2, "__gc"); + lua.setmetatable(L, -2); + return 1; +} +static int bi_lua_fgets(lua_State *L) +{ + vfsfile_t **f = lua.touserdata(L, 1); + char line[8192]; + char *r = NULL; + if (f && *f) + r = VFS_GETS(*f, line, sizeof(line)); + + if (r) + lua.pushfstring(L, "%s", r); + else + lua.pushnil(L); + return 1; +} +static int bi_lua_fputs(lua_State *L) +{ + vfsfile_t **f = lua.touserdata(L, 1); + size_t l; + const char *str = lua.tolstring(L, 2, &l); + if (f && *f != NULL) + VFS_WRITE(*f, str, l); + return 0; +} + +static int bi_lua_loadlua(lua_State *L) +{ + const char *fname = lua.tolstring(L, 1, NULL); + vfsfile_t *sourcefile = FS_OpenVFS(fname, "rb", FS_GAME); + if (!sourcefile) + { + Con_Printf("Error trying to load %s\n", fname); + lua.pushnil(L); + } + else if (0 != lua.load(L, my_lua_Reader, sourcefile, fname, "bt")) //load the file, embed it within a function and push it + { + Con_Printf("Error trying to parse %s: %s\n", fname, lua.tolstring(L, -1, NULL)); + lua.pushnil(L); + } + VFS_CLOSE(sourcefile); + return 1; +} + +#define registerfunc(n) lua.pushcclosure(L, bi_lua_##n, 0); lua.setglobal(L, #n); +static void my_lua_registerbuiltins(lua_State *L) +{ + lua.atpanic (L, my_lua_panic); + + //standard lua library replacement + //this avoids the risk of including any way to access os.execute etc, or other file access. + lua.pushcclosure(L, my_lua_tostring, 0); + lua.setglobal(L, "tostring"); + lua.pushcclosure(L, my_lua_print, 0); + lua.setglobal(L, "print"); + + lua.pushcclosure(L, my_lua_conprint, 0); //for the luls. + lua.setglobal(L, "conprint"); + + registerfunc(loadlua); + + registerfunc(setmodel); + registerfunc(precache_model); + registerfunc(precache_sound); + registerfunc(lightstyle); + registerfunc(spawn); + registerfunc(remove); + registerfunc(nextent); + registerfunc(nextclient); + //registerfunc(AIM); + registerfunc(makestatic); + registerfunc(setorigin); + registerfunc(setsize); + + registerfunc(dprint); + registerfunc(bprint); + registerfunc(sprint); + registerfunc(centerprint); + registerfunc(ambientsound); + registerfunc(sound); + registerfunc(random); + registerfunc(checkclient); + registerfunc(stuffcmd); + registerfunc(localcmd); + registerfunc(cvar_get); + registerfunc(cvar_set); + registerfunc(findradius); //qc legacy compat. should probably warn when its called or sommit. + registerfunc(findradiuschain); //like qc. + registerfunc(findradiustable); //findradius, but returns an array/table instead. + + registerfunc(traceline); + registerfunc(tracebox); + registerfunc(walkmove); + registerfunc(movetogoal); + registerfunc(droptofloor); + registerfunc(checkbottom); + registerfunc(pointcontents); + + registerfunc(setspawnparms); + registerfunc(changelevel); + //registerfunc(LOGFRAG); + registerfunc(getinfokey); + registerfunc(setinfokey); + registerfunc(multicast); + registerfunc(writebyte); + registerfunc(writechar); + registerfunc(writeshort); + registerfunc(writelong); + registerfunc(writeangle); + registerfunc(writecoord); + registerfunc(writestring); + registerfunc(writeentity); + registerfunc(bitnot); + registerfunc(bitclear); + registerfunc(bitset); + registerfunc(bitor); + registerfunc(bitand); + registerfunc(bitxor); + registerfunc(sin); + registerfunc(cos); + registerfunc(atan2); + registerfunc(sqrt); + registerfunc(floor); + registerfunc(ceil); + registerfunc(acos); + registerfunc(fopen); + registerfunc(fclose); + registerfunc(fgets); + registerfunc(fputs); + registerfunc(getfilelist); + //registerfunc(Find); + + //registerfunc(strftime); + registerfunc(tokenize); + registerfunc(makevectors); + registerfunc(vectoangles); + + //registerfunc(PRECACHE_VWEP_MODEL); + //registerfunc(SETPAUSE); + + + + lua.createtable(L, 0, 0); + if (lua.Lnewmetatable(L, "globals")) + { + lua.pushcclosure(L, my_lua_global_set, 0); //for the luls. + lua.setfield (L, -2, "__newindex"); + + lua.pushcclosure(L, my_lua_global_get, 0); //for the luls. + lua.setfield (L, -2, "__index"); + } + lua.setmetatable(L, -2); + lua.setglobal(L, "glob"); +} + + + + + + +static edict_t *QDECL Lua_EdictNum(pubprogfuncs_t *pf, unsigned int num) +{ + int newcount; + if (num >= lua.maxedicts) + { + newcount = num + 64; + lua.edicttable = realloc(lua.edicttable, newcount*sizeof(*lua.edicttable)); + while(lua.maxedicts < newcount) + lua.edicttable[lua.maxedicts++] = NULL; + } + return lua.edicttable[num]; +} +static unsigned int QDECL Lua_NumForEdict(pubprogfuncs_t *pf, edict_t *e) +{ + return e->entnum; +} +static int QDECL Lua_EdictToProgs(pubprogfuncs_t *pf, edict_t *e) +{ + return e->entnum; +} +static edict_t *QDECL Lua_ProgsToEdict(pubprogfuncs_t *pf, int num) +{ + return Lua_EdictNum(pf, num); +} +void Lua_EntClear (pubprogfuncs_t *pf, edict_t *e) +{ + int num = e->entnum; + memset (e->v, 0, sv.world.edict_size); + e->isfree = false; + e->entnum = num; +} +edict_t *Lua_CreateEdict(unsigned int num) +{ + edict_t *e; + e = lua.edicttable[num] = Z_Malloc(sizeof(edict_t) + sv.world.edict_size); + e->v = (stdentvars_t*)(e+1); +#ifdef VM_Q1 + e->xv = (extentvars_t*)(e->v + 1); +#endif + e->entnum = num; + return e; +} +static void QDECL Lua_EntRemove(pubprogfuncs_t *pf, edict_t *e) +{ + lua_State *L = lua.ctx; + + if (!ED_CanFree(e)) + return; + e->isfree = true; + e->freetime = sv.time; + + //clear out the lua version of the entity, so that it can be garbage collected. + //should probably clear out its entnum field too, just in case. + lua.pushlightuserdata(L, e); + lua.pushnil(L); + lua.settable(L, LUA_REGISTRYINDEX); +} +static edict_t *Lua_DoRespawn(pubprogfuncs_t *pf, edict_t *e, int num) +{ + lua_State *L = lua.ctx; + if (!e) + e = Lua_CreateEdict(num); + else + Lua_EntClear (pf, e); + + ED_Spawned((struct edict_s *) e, false); + + //create a new table for the entity, give it a suitable metatable, and store it into the registry (avoiding GC and allowing us to actually hold on to it). + lua.pushlightuserdata(L, lua.edicttable[num]); + lua.createtable(L, 0, 0); + if (lua.Lnewmetatable(L, "entity")) + { + lua.pushcclosure(L, my_lua_entity_set, 0); //known writes should change the internal info so the engine can use the information. + lua.setfield (L, -2, "__newindex"); + + lua.pushcclosure(L, my_lua_entity_get, 0); //we need to de-translate the engine's fields too. + lua.setfield (L, -2, "__index"); + + lua.pushcclosure(L, my_lua_entity_tostring, 0); //cos its prettier than seeing 'table 0x5425729' all over the place + lua.setfield (L, -2, "__tostring"); + + lua.pushcclosure(L, my_lua_entity_eq, 0); //for comparisons, you know? + lua.setfield (L, -2, "__eq"); + } + lua.setmetatable(L, -2); + lua.pushinteger(L, num); + lua.setfield (L, -2, "entnum"); //so we know which entity it is. + lua.settable(L, LUA_REGISTRYINDEX); + return e; +} +static edict_t *QDECL Lua_EntAlloc(pubprogfuncs_t *pf) +{ + int i; + edict_t *e; + for ( i=0 ; iisfree && ( e->freetime < 2 || sv.time - e->freetime > 0.5 ) )) + { + e = Lua_DoRespawn(pf, e, i); + return (struct edict_s *)e; + } + } + + if (i >= sv.world.max_edicts-1) //try again, but use timed out ents. + { + for ( i=0 ; iisfree)) + { + e = Lua_DoRespawn(pf, e, i); + return (struct edict_s *)e; + } + } + + if (i >= sv.world.max_edicts-1) + { + Sys_Error ("ED_Alloc: no free edicts"); + } + } + + sv.world.num_edicts++; + e = (edict_t*)EDICT_NUM(pf, i); + + e = Lua_DoRespawn(pf, e, i); + + return (struct edict_s *)e; +} + +static int QDECL Lua_LoadEnts(pubprogfuncs_t *pf, char *mapstring, float spawnflags) +{ + lua_State *L = lua.ctx; + int i = 0; + lua.getglobal(L, "LoadEnts"); + lua.createtable(L, 0, 0); + while(NULL != (mapstring = COM_Parse(mapstring))) + { + lua.pushinteger(L, i++); + lua.pushstring(L, com_token); + lua.settable(L, -3); + } + lua.pushinteger(L, spawnflags); + + if (lua.pcall(L, 2, 0, 0) != 0) + { + const char *s = lua.tolstring(L, -1, NULL); + Con_Printf(CON_WARNING "%s\n", s); + lua.pop(L, 1); + } + + return sv.world.edict_size; +} + +static eval_t *QDECL Lua_GetEdictFieldValue(pubprogfuncs_t *pf, edict_t *e, char *fieldname, evalc_t *cache) +{ + eval_t *val; + luafld_t *fld; + fld = Hash_GetInsensitive(&lua.entityfields, fieldname); + if (fld) + { + val = (eval_t*)((char*)e->v + fld->offset); + return val; + } + return NULL; +} + +static eval_t *QDECL Lua_FindGlobal (pubprogfuncs_t *prinst, const char *name, progsnum_t num, etype_t *type) +{ + eval_t *val; + luafld_t *fld; + fld = Hash_GetInsensitive(&lua.globalfields, name); + if (fld) + { + val = (eval_t*)((char*)&lua.globals + fld->offset); + return val; + } + + Con_Printf("Lua_FindGlobal: %s\n", name); + return NULL; +} +static func_t QDECL Lua_FindFunction (pubprogfuncs_t *prinst, const char *name, progsnum_t num) +{ + eval_t *val; + luafld_t *fld; + fld = Hash_GetInsensitive(&lua.globalfields, name); + if (fld) + { + val = (eval_t*)((char*)&lua.globals + fld->offset); + return val->function; + } + + Con_Printf("Lua_FindFunction: %s\n", name); + return 0; +} + +static globalvars_t *QDECL Lua_Globals(pubprogfuncs_t *prinst, int prnum) +{ +// Con_Printf("Lua_Globals: called\n"); + return NULL; +} + +char *QDECL Lua_AddString(pubprogfuncs_t *prinst, const char *val, int minlength, pbool demarkup) +{ + char *ptr; + int len = strlen(val)+1; + if (len < minlength) + len = minlength; + ptr = Z_TagMalloc(len, 0x55780128); + strcpy(ptr, val); + return ptr; +} +static string_t QDECL Lua_StringToProgs(pubprogfuncs_t *prinst, const char *str) +{ + Con_Printf("Lua_StringToProgs called instead of Lua_SetStringField\n"); + return 0; +} + +//passing NULL for ed means its setting a global. +static void QDECL Lua_SetStringField(pubprogfuncs_t *prinst, edict_t *ed, string_t *fld, const char *str, pbool str_is_static) +{ + lua_State *L = lua.ctx; + string_t val; + string_t base; + if (ed) + { + base = (ed->entnum+1)<<10; + val = (char*)fld-(char*)ed->v; + + //push the entity table + lua.pushlightuserdata(lua.ctx, lua.edicttable[ed->entnum]); + lua.gettable(lua.ctx, LUA_REGISTRYINDEX); + } + else + { + base = 0; + val = (char*)fld-(char*)&lua.globals; + + //push the globals list + lua.getglobal(lua.ctx, "glob"); + } + *fld = base | val; //set the engine's value + + //set the stuff so that lua can read it properly. + lua.pushlightuserdata(L, (void *)val); + lua.pushfstring(L, "%s", str); + lua.rawset(L, -3); + + //and pop the table + lua.pop(L, 1); +} + +static const char *ASMCALL QDECL Lua_StringToNative(pubprogfuncs_t *prinst, string_t str) +{ + const char *ret = ""; + unsigned int entnum = str >> 10; + if (str) + { + str &= 1023; + if (!entnum) + { + //okay, its the global table. + lua.getglobal(lua.ctx, "glob"); + } + else + { + entnum-=1; + if (entnum >= lua.maxedicts) + return ret; //erk... + //get the entity's table + lua.pushlightuserdata(lua.ctx, lua.edicttable[entnum]); + lua.gettable(lua.ctx, LUA_REGISTRYINDEX); + } + + //read the function from the table + lua.pushlightuserdata(lua.ctx, (void *)str); + lua.rawget(lua.ctx, -2); + ret = lua.tolstring(lua.ctx, -1, NULL); + lua.pop(lua.ctx, 2); //pop the table+string. + //popping the string is 'safe' on the understanding that the string reference is still held by its containing table, so don't store the string anywhere. + } + + return ret; +} + +static void Lua_Event_Touch(world_t *w, wedict_t *s, wedict_t *o) +{ + int oself = pr_global_struct->self; + int oother = pr_global_struct->other; + + 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; + PR_ExecuteProgram (w->progs, s->v->touch); + + pr_global_struct->self = oself; + pr_global_struct->other = oother; +} + +static void Lua_Event_Think(world_t *w, wedict_t *s) +{ + pr_global_struct->self = EDICT_TO_PROG(w->progs, s); + pr_global_struct->other = EDICT_TO_PROG(w->progs, w->edicts); + PR_ExecuteProgram (w->progs, s->v->think); +} + +static qboolean Lua_Event_ContentsTransition(world_t *w, wedict_t *ent, int oldwatertype, int newwatertype) +{ + return false; //always do legacy behaviour +} + +void PR_SV_FillWorldGlobals(world_t *w); +static void Lua_SetupGlobals(world_t *world) +{ + int flds; + int bucks; + comentvars_t *v = NULL; + extentvars_t *xv = (extentvars_t*)(v+1); + + memset(&lua.globals, 0, sizeof(lua.globals)); + lua.globals.physics_mode = 2; + lua.globals.dimension_send = 255; + + flds = 0; + bucks = 64; + Hash_InitTable(&lua.globalfields, bucks, Z_Malloc(Hash_BytesForBuckets(bucks))); + +//WARNING: global is not remapped yet... +//This code is written evilly, but works well enough +#define doglobal(n, t) \ + pr_global_ptrs->n = &lua.globals.n; \ + lua.globflds[flds].offset = (char*)&lua.globals.n - (char*)&lua.globals; \ + lua.globflds[flds].name = #n; \ + lua.globflds[flds].type = t; \ + Hash_AddInsensitive(&lua.globalfields, lua.globflds[flds].name, &lua.globflds[flds], &lua.globflds[flds].buck); \ + flds++; +#define doglobal_v(o, f, t) \ + lua.globflds[flds].offset = (char*)&lua.globals.o - (char*)&lua.globals; \ + lua.globflds[flds].name = #f; \ + lua.globflds[flds].type = t; \ + Hash_AddInsensitive(&lua.globalfields, lua.globflds[flds].name, &lua.globflds[flds], &lua.globflds[flds].buck); \ + flds++; +#define globalentity(required, name) doglobal(name, ev_entity) +#define globalint(required, name) doglobal(name, ev_integer) +#define globalfloat(required, name) doglobal(name, ev_float) +#define globalstring(required, name) doglobal(name, ev_string) +#define globalvec(required, name) doglobal(name, ev_vector) doglobal_v(name[0], name##_x, ev_float) doglobal_v(name[1], name##_y, ev_float) doglobal_v(name[2], name##_z, ev_float) +#define globalfunc(required, name) doglobal(name, ev_function) + luagloballist +#undef doglobal +#define doglobal(n, t) doglobal_v(n,n,t) + luaextragloballist + + flds = 0; + bucks = 256; + Hash_InitTable(&lua.entityfields, bucks, Z_Malloc(Hash_BytesForBuckets(bucks))); + + +#define doefield(n, t) \ + lua.entflds[flds].offset = (char*)&v->n - (char*)v; \ + lua.entflds[flds].name = #n; \ + lua.entflds[flds].type = t; \ + Hash_AddInsensitive(&lua.entityfields, lua.entflds[flds].name, &lua.entflds[flds], &lua.entflds[flds].buck); \ + flds++; +#define doefield_v(o, f, t) \ + lua.entflds[flds].offset = (char*)&v->o - (char*)v; \ + lua.entflds[flds].name = #f; \ + lua.entflds[flds].type = t; \ + Hash_AddInsensitive(&lua.entityfields, lua.entflds[flds].name, &lua.entflds[flds], &lua.entflds[flds].buck); \ + flds++; +#define comfieldentity(name,desc) doefield(name, ev_entity) +#define comfieldint(name,desc) doefield(name, ev_integer) +#define comfieldfloat(name,desc) doefield(name, ev_float) +#define comfieldstring(name,desc) doefield(name, ev_string) +#define comfieldvector(name,desc) doefield(name, ev_vector) doefield_v(name[0], name##_x, ev_float) doefield_v(name[1], name##_y, ev_float) doefield_v(name[2], name##_z, ev_float) +#define comfieldfunction(name,typestr,desc) doefield(name, ev_function) + comqcfields +#undef doefield +#undef doefield_v +#define doefield(n, t) \ + lua.entflds[flds].offset = (char*)&xv->n - (char*)v; \ + lua.entflds[flds].name = #n; \ + lua.entflds[flds].type = t; \ + Hash_AddInsensitive(&lua.entityfields, lua.entflds[flds].name, &lua.entflds[flds], &lua.entflds[flds].buck); \ + flds++; +#define doefield_v(o, f, t) \ + lua.entflds[flds].offset = (char*)&xv->o - (char*)v; \ + lua.entflds[flds].name = #f; \ + lua.entflds[flds].type = t; \ + Hash_AddInsensitive(&lua.entityfields, lua.entflds[flds].name, &lua.entflds[flds], &lua.entflds[flds].buck); \ + flds++; + comextqcfields + svextqcfields + + PR_SV_FillWorldGlobals(world); +} + +void QDECL Lua_ExecuteProgram(pubprogfuncs_t *funcs, func_t func) +{ + unsigned int entnum = func >> 10; + func &= 1023; + if (!entnum) + { + //okay, its the global table. + lua.getglobal(lua.ctx, "glob"); + } + else + { + entnum-=1; + if (entnum >= lua.maxedicts) + return; //erk... + //get the entity's table + lua.pushlightuserdata(lua.ctx, lua.edicttable[entnum]); + lua.gettable(lua.ctx, LUA_REGISTRYINDEX); + } + + //read the function from the table + lua.pushlightuserdata(lua.ctx, (void *)func); + lua.rawget(lua.ctx, -2); + + //and now invoke it. + if (lua.pcall(lua.ctx, 0, 0, 0) != 0) + { + const char *s = lua.tolstring(lua.ctx, -1, NULL); + Con_Printf(CON_WARNING "%s\n", s); + lua.pop(lua.ctx, 1); + } +} + +void PDECL Lua_CloseProgs(pubprogfuncs_t *inst) +{ + lua.close(lua.ctx); + free(lua.edicttable); + lua.edicttable = NULL; + lua.maxedicts = 0; +} + +qboolean PR_LoadLua(void) +{ + world_t *world = &sv.world; + pubprogfuncs_t *pf; + vfsfile_t *sourcefile = FS_OpenVFS("progs.lua", "rb", FS_GAME); + if (!sourcefile) + return false; + + if (!init_lua()) + { + VFS_CLOSE(sourcefile); + Con_Printf("WARNING: Found progs.lua, but could load load lua library\n"); + return false; + } + + progstype = PROG_QW; + + + pf = svprogfuncs = &lua.progfuncs; + + pf->CloseProgs = Lua_CloseProgs; + pf->AddString = Lua_AddString; + pf->EDICT_NUM = Lua_EdictNum; + pf->NUM_FOR_EDICT = Lua_NumForEdict; + pf->EdictToProgs = Lua_EdictToProgs; + pf->ProgsToEdict = Lua_ProgsToEdict; + pf->EntAlloc = Lua_EntAlloc; + pf->EntFree = Lua_EntRemove; + pf->EntClear = Lua_EntClear; + pf->FindGlobal = Lua_FindGlobal; + pf->load_ents = Lua_LoadEnts; + pf->globals = Lua_Globals; + pf->GetEdictFieldValue = Lua_GetEdictFieldValue; + pf->SetStringField = Lua_SetStringField; + pf->StringToProgs = Lua_StringToProgs; + pf->StringToNative = Lua_StringToNative; + pf->ExecuteProgram = Lua_ExecuteProgram; + pf->FindFunction = Lua_FindFunction; + + world->Event_Touch = Lua_Event_Touch; + world->Event_Think = Lua_Event_Think; + world->Event_Sound = SVQ1_StartSound; + world->Event_ContentsTransition = Lua_Event_ContentsTransition; + world->Get_CModel = SVPR_GetCModel; + + world->progs = pf; + world->progs->parms = &lua.progfuncsparms; + world->progs->parms->user = world; + world->usesolidcorpse = true; + + Lua_SetupGlobals(world); + + svs.numprogs = 0; //Why is this svs? +#ifdef VM_Q1 + world->edict_size = sizeof(stdentvars_t) + sizeof(extentvars_t); +#else + world->edict_size = sizeof(stdentvars_t); +#endif + + //force items2 instead of serverflags + sv.haveitems2 = true; + + //initalise basic lua context + lua.ctx = lua.newstate(my_lua_alloc, NULL); //create our lua state + my_lua_registerbuiltins(lua.ctx); + + //spawn the world, woo. + world->edicts = (wedict_t*)pf->EntAlloc(pf); + + //load the gamecode now. it should be safe for it to call various builtins. + if (0 != lua.load(lua.ctx, my_lua_Reader, sourcefile, "progs.lua", "bt")) //load the file, embed it within a function and push it + { + Con_Printf("Error trying to parse %s: %s\n", "progs.lua", lua.tolstring(lua.ctx, -1, NULL)); + lua.pop(lua.ctx, 1); + } + else + { + if (lua.pcall(lua.ctx, 0, 0, 0) != 0) + { + const char *s = lua.tolstring(lua.ctx, -1, NULL); + Con_Printf(CON_WARNING "%s\n", s); + lua.pop(lua.ctx, 1); + } + } + VFS_CLOSE(sourcefile); + + return true; +} +#endif //VM_LUA diff --git a/engine/server/pr_q1qvm.c b/engine/server/pr_q1qvm.c index 181ed5a7..cfcb55df 100755 --- a/engine/server/pr_q1qvm.c +++ b/engine/server/pr_q1qvm.c @@ -18,6 +18,25 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/*64bit cpu notes: +string_t is a 32bit quantity. +this datatype needs to have enough bits to express any address that contains a string. +in a 32bit build, this is fine. with a qvm, the offset between the vm base and the string is always less than 32bits so this is fine too. +HOWEVER... +native code uses a base address of 0. this needs a 48bit datatype for any userland address. 32 bits just ain't enough. +even worse: ktx defines string_t as a 'char*'. okay, its 64bit at last... but it means that the entire entity field structure is now the wrong size with the wrong offsets. +this means CRASH! +how to fix? good luck with that. seriously. + the only sane way to fix it is to either define a better base address (say the dll base, + and require that all string_t values are bss or data and not from malloc, which is problematic when loading dynamic stuff from a map) + alternatively, you could create some string_t->pointer lookup. messy. + either way, string_t cannot be a pointer. + probably the best solution is to stop using string_t stuff completely. move all those string values somewhere else. + netnames will still mess things up. +so just use qvms. +oh, wait, ktx no longer supports those properly. +*/ + #include "quakedef.h" #ifdef VM_Q1 @@ -473,14 +492,20 @@ static globalvars_t *QDECL Q1QVMPF_Globals(pubprogfuncs_t *prinst, int prnum) return NULL; } -static string_t QDECL Q1QVMPF_StringToProgs(pubprogfuncs_t *prinst, char *str) +static string_t QDECL Q1QVMPF_StringToProgs(pubprogfuncs_t *prinst, const char *str) { - return (string_t)(str - (char*)VM_MemoryBase(q1qvm)); + string_t ret = (string_t)(str - (char*)VM_MemoryBase(q1qvm)); + if (ret >= VM_MemoryMask(q1qvm)) + return 0; + return ret; } static char *ASMCALL QDECL Q1QVMPF_StringToNative(pubprogfuncs_t *prinst, string_t str) { - return (char*)VM_MemoryBase(q1qvm) + str; + char *ret = (char*)VM_MemoryBase(q1qvm) + str; + if (!ret) //qvms can never return a null. make sure native code can't crash things either. + return ""; + return ret; } static int WrapQCBuiltin(builtin_t func, void *offset, quintptr_t mask, const qintptr_t *arg, char *argtypes) @@ -651,19 +676,7 @@ static qintptr_t syscallhandle (void *offset, quintptr_t mask, qintptr_t fn, con return PF_checkclient_Internal(svprogfuncs); case G_STUFFCMD: - { - char *s; - client_t *cl; - if ((unsigned)VM_LONG(arg[0]) > sv.allocated_client_slots) - return -1; - cl = &svs.clients[VM_LONG(arg[0])-1]; - if (cl->state != cs_spawned) - return -1; - s = VM_POINTER(arg[1]); - - ClientReliableWrite_Begin (cl, svc_stufftext, 3+strlen(s)); - ClientReliableWrite_String(cl, s); - } + PF_stuffcmd_Internal(VM_LONG(arg[0]), VM_POINTER(arg[1])); break; case G_LOCALCMD: @@ -1151,15 +1164,16 @@ Con_DPrintf("PF_readcmd: %s\n%s", s, output); char *key = VM_POINTER(arg[1]); if (*key == '*' && (VM_LONG(arg[3])&1)) return -1; //denied! + return PF_ForceInfoKey_Internal(VM_LONG(arg[0]), VM_POINTER(arg[1]), VM_POINTER(arg[2])); } //fallthrough + case G_SetBotUserInfo: + return PF_ForceInfoKey_Internal(VM_LONG(arg[0]), VM_POINTER(arg[1]), VM_POINTER(arg[2])); + case G_MOVETOGOAL: return World_MoveToGoal(&sv.world, (wedict_t*)Q1QVMPF_ProgsToEdict(svprogfuncs, pr_global_struct->self), VM_FLOAT(arg[0])); - case G_SetBotUserInfo: - WrapQCBuiltin(PF_ForceInfoKey, offset, mask, arg, "ess"); - return 0; case G_strftime: { @@ -1295,12 +1309,12 @@ void Q1QVM_Shutdown(void) svs.clients[i].name = svs.clients[i].namebuf; } VM_Destroy(q1qvm); + q1qvm = NULL; + VM_fcloseall(VMFSID_Q1QVM); + if (svprogfuncs == &q1qvmprogfuncs) + sv.world.progs = svprogfuncs = NULL; + Z_FreeTags(VMFSID_Q1QVM); } - q1qvm = NULL; - VM_fcloseall(VMFSID_Q1QVM); - if (svprogfuncs == &q1qvmprogfuncs) - sv.world.progs = svprogfuncs = NULL; - Z_FreeTags(VMFSID_Q1QVM); } void Q1QVM_Event_Touch(world_t *w, wedict_t *s, wedict_t *o) @@ -1329,6 +1343,15 @@ qboolean Q1QVM_Event_ContentsTransition(world_t *w, wedict_t *ent, int oldwatert return false; //always do legacy behaviour } +void QDECL Q1QVMPF_SetStringField(pubprogfuncs_t *progfuncs, struct edict_s *ed, string_t *fld, const char *str, pbool str_is_static) +{ + string_t newval = progfuncs->StringToProgs(progfuncs, str); + if (newval || !str) + *fld = newval; + else + Con_DPrintf("Ignoring string set outside of progs VM\n"); +} + qboolean PR_LoadQ1QVM(void) { static float writable; @@ -1370,6 +1393,7 @@ qboolean PR_LoadQ1QVM(void) q1qvmprogfuncs.GetEdictFieldValue = Q1QVMPF_GetEdictFieldValue; q1qvmprogfuncs.StringToProgs = Q1QVMPF_StringToProgs; q1qvmprogfuncs.StringToNative = Q1QVMPF_StringToNative; + q1qvmprogfuncs.SetStringField = Q1QVMPF_SetStringField; sv.world.Event_Touch = Q1QVM_Event_Touch; sv.world.Event_Think = Q1QVM_Event_Think; @@ -1505,6 +1529,17 @@ void Q1QVM_ClientConnect(client_t *cl) //FIXME: check this pointer strcpy(cl->name, cl->namebuf); } + else if (!VM_NonNative(q1qvm)) + { + Q_strncpyz(cl->namebuf, cl->name, sizeof(cl->namebuf)); + cl->name = cl->namebuf; + cl->edict->v->netname = Q1QVMPF_StringToProgs(svprogfuncs, cl->namebuf); + + Con_DPrintf("WARNING: Mod provided no netname buffer and will not function correctly when compiled as a qvm.\n"); + } + else + Con_Printf("WARNING: Mod provided no netname buffer. Player names will not be set properly.\n"); + // call the spawn function pr_global_struct->time = sv.world.physicstime; pr_global_struct->self = EDICT_TO_PROG(svprogfuncs, cl->edict); diff --git a/engine/server/progdefs.h b/engine/server/progdefs.h index 9745d1d3..ea86de1b 100644 --- a/engine/server/progdefs.h +++ b/engine/server/progdefs.h @@ -90,6 +90,7 @@ typedef struct nqglobalvars_s vec3_t *input_movevalues; float *input_buttons; float *spawnparamglobals[NUM_SPAWN_PARMS]; + float *serverid; } globalptrs_t; #define P_VEC(v) (pr_global_struct->v) @@ -189,22 +190,22 @@ and the extension fields are added on the end and can have extra vm-specific stu #define comextqcfields \ comfieldvector(punchangle,NULL) /*std in nq*/\ comfieldfloat(gravity,NULL) /*added in quake 1.09 (for hipnotic)*/\ - comfieldfloat(hull,NULL)/*PEXT_HEXEN2*/\ + comfieldfloat(hull,"Overrides the hull used by the entity for walkmove/movetogoal and not traceline/tracebox.")/*PEXT_HEXEN2*/\ comfieldentity(movechain,NULL)/*hexen2*/\ comfieldfunction(chainmoved, ".void()",NULL)/*hexen2*/\ - comfieldfunction(contentstransition, ".void(float old, float new)",NULL)/*ENTITYCONTENTSTRANSITION*/\ - comfieldfloat(dimension_solid,NULL)/*EXT_DIMENSION_PHYSICS*/\ - comfieldfloat(dimension_hit,NULL)/*EXT_DIMENSION_PHYSICS*/\ + comfieldfunction(contentstransition, ".void(float old, float new)","This function is called when the entity moves between water and air. If specified, default splash sounds will be disabled allowing you to provide your own.")/*ENTITYCONTENTSTRANSITION*/\ + comfieldfloat(dimension_solid,"This is the bitmask of dimensions which the entity is solid within.")/*EXT_DIMENSION_PHYSICS*/\ + comfieldfloat(dimension_hit,"This is the bitmask of dimensions which the entity will be blocked by. If other.dimension_solid & self.dimension_hit, our traces will impact and not proceed. If its false, the traces will NOT impact, allowing self to pass straight through.")/*EXT_DIMENSION_PHYSICS*/\ comfieldfloat(hitcontentsmask,NULL)\ comfieldfloat(scale,NULL)/*DP_ENT_SCALE*/\ comfieldfloat(fatness,NULL)/*FTE_PEXT_FATNESS*/\ comfieldfloat(alpha,NULL)/*DP_ENT_ALPHA*/\ comfieldentity(tag_entity,NULL)\ comfieldfloat(tag_index,NULL)\ - comfieldfloat(skeletonindex,NULL) /*FTE_CSQC_SKELETONOBJECTS*/\ + comfieldfloat(skeletonindex,"This object serves as a container for the skeletal bone states used to override the animation data.") /*FTE_CSQC_SKELETONOBJECTS*/\ comfieldvector(colormod,NULL)\ comfieldvector(glowmod,NULL)\ - comfieldvector(gravitydir,NULL)\ + comfieldvector(gravitydir,"Specifies the direction in which gravity acts. Must be normalised. '0 0 0' also means down. Use '0 0 1' if you want the player to be able to run on ceilings.")\ comfieldfunction(camera_transform,".vector(vector org, vector ang)", NULL)\ comfieldfloat(pmove_flags,NULL)/*EXT_CSQC_1*/\ comfieldfloat(geomtype,NULL)/*DP_...PHYSICS*/\ @@ -248,13 +249,13 @@ and the extension fields are added on the end and can have extra vm-specific stu comfieldfloat(dimension_ghost,"If this entity is visible only within these dimensions, it will become transparent, as if a ghost.")/*EXT_DIMENSION_GHOST*/\ comfieldfloat(dimension_ghost_alpha,"If this entity is subject to dimension_ghost, this is the scaler for its alpha value. If 0, 0.5 will be used instead.")/*EXT_DIMENSION_GHOST*/\ comfieldfloat(playerclass,NULL)/*hexen2 requirements*/\ - comfieldfloat(drawflags,NULL)/*hexen2*/\ + comfieldfloat(drawflags,"Various flags that affect lighting values and scaling. Typically set to 96 in quake for proper compatibility with DP_QC_SCALE.")/*hexen2*/\ comfieldfloat(hasted,NULL)/*hexen2 uses this AS WELL as maxspeed*/\ comfieldfloat(light_level,NULL)/*hexen2's grabbing light level from client*/\ comfieldfloat(abslight,"Allows overriding light levels. Use drawflags to state that this field should actually be used.")/*hexen2's force a lightlevel*/\ comfieldfunction(SendEntity, ".float(entity playerent, float changedflags)",NULL)/*EXT_CSQC*/\ comfieldfloat(SendFlags,NULL)/*EXT_CSQC_1 (one of the DP guys came up with it)*/\ - comfieldfloat(Version,"Obsolete")/*EXT_CSQC (obsolete)*/\ + comfieldfloat(Version,"Obsolete, set a SendFlags bit instead.")/*EXT_CSQC (obsolete)*/\ comfieldfloat(pvsflags,"Reconfigures when the entity is visible to clients")/*EXT_CSQC_1*/\ comfieldfloat(modelflags,NULL)\ comfieldfloat(uniquespawnid,NULL)/*FTE_ENT_UNIQUESPAWNID*/\ @@ -269,7 +270,7 @@ and the extension fields are added on the end and can have extra vm-specific stu comfieldfloat(frame2time,NULL) /*EXT_CSQC_1*/\ comfieldfloat(lerpfrac,NULL) /*EXT_CSQC_1*/\ comfieldfloat(renderflags,NULL)\ - comfieldfloat(forceshader,NULL)/*FTE_CSQC_SHADERS*/\ + comfieldfloat(forceshader,"Contains a shader handle used to replace all surfaces upon the entity.")/*FTE_CSQC_SHADERS*/\ \ comfieldfloat(baseframe,NULL) /*FTE_CSQC_BASEFRAME*/\ comfieldfloat(baseframe2,NULL) /*FTE_CSQC_BASEFRAME*/\ @@ -287,7 +288,7 @@ and the extension fields are added on the end and can have extra vm-specific stu comfieldfloat(basesubblendfrac,NULL) /*FTE_CSQC_HALFLIFE_MODELS+FTE_CSQC_BASEFRAME*/\ \ comfieldfloat(drawmask,NULL) /*So that the qc can specify all rockets at once or all bannanas at once*/ \ - comfieldfunction(predraw, ".void()",NULL) /*If present, is called just before it's drawn.*/ + comfieldfunction(predraw, ".float()",NULL) /*If present, is called just before it's drawn.*/ typedef struct stdentvars_s //standard = standard for qw { diff --git a/engine/server/progs.h b/engine/server/progs.h index 98a23480..78f67567 100644 --- a/engine/server/progs.h +++ b/engine/server/progs.h @@ -121,6 +121,9 @@ void PR_ClientUserInfoChanged(char *name, char *oldivalue, char *newvalue); void PR_LocalInfoChanged(char *name, char *oldivalue, char *newvalue); void PF_InitTempStrings(pubprogfuncs_t *prinst); +#ifdef VM_LUA +qboolean PR_LoadLua(void); +#endif #ifdef VM_Q1 struct client_s; void Q1QVM_Shutdown(void); diff --git a/engine/server/savegame.c b/engine/server/savegame.c index 82748372..c135e5d1 100644 --- a/engine/server/savegame.c +++ b/engine/server/savegame.c @@ -57,6 +57,282 @@ void SV_SavegameComment (char *text) text[SAVEGAME_COMMENT_LENGTH] = '\0'; } +//expects the version to have already been parsed +void SV_Loadgame_Legacy(char *filename, vfsfile_t *f, int version) +{ + //FIXME: Multiplayer save probably won't work with spectators. + char mapname[MAX_QPATH]; + float time; + char str[32768]; + int i; + edict_t *ent; + int pt; + int lstyles; + + int slots; + + client_t *cl; + int clnum; + char plname[32]; + + int filelen, filepos; + char *file; + + char *modelnames[MAX_MODELS]; + + if (version != 667 && version != 5 && version != 6) //5 for NQ, 6 for ZQ/FQ + { + VFS_CLOSE (f); + Con_TPrintf ("Unable to load savegame of version %i\n", version); + return; + } + VFS_GETS(f, str, sizeof(str)); //discard comment. + Con_Printf("loading legacy game from %s...\n", filename); + + + + for (clnum = 0; clnum < svs.allocated_client_slots; clnum++) //clear the server for the level change. + { + cl = &svs.clients[clnum]; + if (cl->state <= cs_loadzombie) + continue; + +#ifndef SERVERONLY + if (cl->netchan.remote_address.type == NA_LOOPBACK) + CL_Disconnect(); + else +#endif + { + MSG_WriteByte (&cl->netchan.message, svc_stufftext); + MSG_WriteString (&cl->netchan.message, "disconnect;wait;reconnect\n"); //kindly ask the client to come again. + } + cl->drop = true; + } + SV_SendMessagesToAll(); + + if (version == 5 || version == 6) + { + slots = 1; + SV_UpdateMaxPlayers(1); + cl = &svs.clients[0]; +#ifdef SERVERONLY + Q_strncpyz(cl->namebuf, "", sizeof(cl->namebuf)); +#else + Q_strncpyz(cl->namebuf, name.string, sizeof(cl->namebuf)); +#endif + Q_strncpyz(cl->namebuf, com_token, sizeof(cl->namebuf)); + cl->name = cl->namebuf; + cl->state = cs_loadzombie; + cl->connection_started = realtime+20; + cl->istobeloaded = true; + + for (i=0 ; i<16 ; i++) + { + VFS_GETS(f, str, sizeof(str)); + cl->spawn_parms[i] = atof(str); + } + for (; i < NUM_SPAWN_PARMS; i++) + cl->spawn_parms[i] = 0; + } + else //fte saves ALL the clients on the server. + { + VFS_GETS(f, str, strlen(str)); + slots = atoi(str); + if (!slots) //err + { + VFS_CLOSE(f); + Con_Printf ("Corrupted save game"); + return; + } + SV_UpdateMaxPlayers(slots); + for (clnum = 0; clnum < sv.allocated_client_slots; clnum++) //work out which players we had when we saved, and hope they accepted the reconnect. + { + cl = &svs.clients[clnum]; + VFS_GETS(f, plname, sizeof(plname)); + + cl->istobeloaded = false; + + cl->state = cs_free; + + COM_Parse(plname); + + if (!*com_token) + continue; + + Q_strncpyz(cl->namebuf, com_token, sizeof(cl->namebuf)); + cl->name = cl->namebuf; + cl->state = cs_loadzombie; + cl->connection_started = realtime+20; + cl->istobeloaded = true; + + //probably should be 32, rather than NUM_SPAWN_PARMS(64) + for (i=0 ; ispawn_parms[i] = atof(str); + } + } + } + if (version == 5 || version == 6) + { + VFS_GETS(f, str, sizeof(str)); + Cvar_SetValue (Cvar_FindVar("skill"), atof(str)); + Cvar_SetValue (Cvar_FindVar("deathmatch"), 0); + Cvar_SetValue (Cvar_FindVar("coop"), 0); + Cvar_SetValue (Cvar_FindVar("teamplay"), 0); + + if (version == 5) + { + progstype = PROG_NQ; + Cvar_Set (&pr_ssqc_progs, "progs.dat"); //NQ's progs. + } + else + { + progstype = PROG_QW; + Cvar_Set (&pr_ssqc_progs, "spprogs.dat"); //zquake's single player qw progs. + } + pt = 0; + } + else + { + VFS_GETS(f, str, sizeof(str)); + pt = atoi(str); + + // this silliness is so we can load 1.06 save files, which have float skill values + VFS_GETS(f, str, sizeof(str)); + Cvar_SetValue (Cvar_FindVar("skill"), atof(str)); + + VFS_GETS(f, str, sizeof(str)); + Cvar_SetValue (Cvar_FindVar("deathmatch"), atof(str)); + VFS_GETS(f, str, sizeof(str)); + Cvar_SetValue (Cvar_FindVar("coop"), atof(str)); + VFS_GETS(f, str, sizeof(str)); + Cvar_SetValue (Cvar_FindVar("teamplay"), atof(str)); + } + VFS_GETS(f, mapname, sizeof(mapname)); + VFS_GETS(f, str, sizeof(str)); + time = atof(str); + + SV_SpawnServer (mapname, NULL, false, false); //always inits MAX_CLIENTS slots. That's okay, because we can cut the max easily. + if (sv.state != ss_active) + { + VFS_CLOSE (f); + Con_TPrintf ("Couldn't load map\n"); + return; + } + + sv.allocated_client_slots = slots; + +// load the light styles + + lstyles = 64; + for (i=0 ; iload_ents(svprogfuncs, file, 0); + BZ_Free(file); + + PR_LoadGlabalStruct(); + + pr_global_struct->time = sv.world.physicstime = sv.time = time; + sv.starttime = Sys_DoubleTime() - sv.time; + + VFS_CLOSE(f); + + //FIXME: DP saved games have some / *\nkey values\nkey values\n* / thing in them to save precaches and stuff + + World_ClearWorld(&sv.world); + + for (i=0 ; iisfree) + continue; + + World_LinkEdict (&sv.world, (wedict_t*)ent, false); + } + + sv.spawned_client_slots = 0; + for (i=0 ; istate) + sv.spawned_client_slots += 1; + ent = EDICT_NUM(svprogfuncs, i+1); + } + else + ent = NULL; + cl->edict = ent; + + cl->name = PR_AddString(svprogfuncs, cl->namebuf, sizeof(cl->namebuf), false); + cl->team = PR_AddString(svprogfuncs, cl->teambuf, sizeof(cl->teambuf), false); + + if (ent) + cl->playerclass = ent->xv->playerclass; + else + cl->playerclass = 0; + } +} + #ifndef NEWSAVEFORMAT void SV_Savegame_f (void) { @@ -169,263 +445,6 @@ void SV_Savegame_f (void) SV_BroadcastTPrintf(2, STL_GAMESAVED); } - - -//FIXME: Multiplayer save probably won't work with spectators. - -void SV_Loadgame_f(void) -{ - char filename[MAX_OSPATH]; - FILE *f; - char mapname[MAX_QPATH]; - float time, tfloat; - char str[32768]; - int i; - edict_t *ent; - int version; - int pt; - - int slots; - int current_skill; - - client_t *cl; - int clnum; - char plname[32]; - - int filelen, filepos; - char *file; - - if (Cmd_Argc() != 2) - { - Con_TPrintf (STL_LOADSYNTAX); - return; - } - -// if (sv.state != ss_active) -// { -// Con_Printf("Can't apply: Server isn't running or is still loading\n"); -// return; -// } - - sprintf (filename, "%s/saves/%s", com_gamedir, Cmd_Argv(1)); - COM_DefaultExtension (filename, ".sav"); - -// we can't call SCR_BeginLoadingPlaque, because too much stack space has -// been used. The menu calls it before stuffing loadgame command -// SCR_BeginLoadingPlaque (); - - Con_TPrintf ("Loading game from %s...\n", filename); - f = fopen (filename, "rb"); - if (!f) - { - Con_TPrintf ("ERROR: couldn't open %s.\n", filename); - return; - } - - fscanf (f, "%i\n", &version); - if (version != SAVEGAME_VERSION && version != 5 && version != 6) //5 for NQ, 6 for ZQ/FQ - { - fclose (f); - Con_TPrintf ("Savegame is version %i, not %i\n", version, SAVEGAME_VERSION); - return; - } - fscanf (f, "%s\n", str); - if (version == 5) - { - Con_Printf("loading single player game\n"); - } - else if (version == 6) //this is fuhquake's single player games - { - Con_Printf("loading single player qw game\n"); - } - else - Con_Printf("loading FTE saved game\n"); - - - - for (clnum = 0; clnum < sv.allocated_client_slots; clnum++) //clear the server for the level change. - { - cl = &svs.clients[clnum]; - if (cl->state <= cs_zombie) - continue; - - MSG_WriteByte (&cl->netchan.message, svc_stufftext); - MSG_WriteString (&cl->netchan.message, "disconnect;wait;reconnect\n"); //kindly ask the client to come again. - cl->drop = true; - } - SV_SendMessagesToAll(); - - if (version == 5 || version == 6) - { - SV_UpdateMaxPlayers(1); - cl = &svs.clients[0]; -#ifdef SERVERONLY - strcpy(cl->name, ""); -#else - strcpy(cl->name, name.string); -#endif - cl->state = cs_zombie; - cl->connection_started = realtime+20; - cl->istobeloaded = true; - - for (i=0 ; i<16 ; i++) - fscanf (f, "%f\n", &cl->spawn_parms[i]); - for (; i < NUM_SPAWN_PARMS; i++) - cl->spawn_parms[i] = 0; - } - else //fte saves ALL the clients on the server. - { - fscanf (f, "%f\n", &tfloat); - slots = tfloat; - if (!slots) //err - { - fclose (f); - Con_Printf ("Corrupted save game"); - return; - } - SV_UpdateMaxPlayers(slots); - for (clnum = 0; clnum < sv.allocated_client_slots; clnum++) //work out which players we had when we saved, and hope they accepted the reconnect. - { - cl = &svs.clients[clnum]; - fscanf(f, "%s\n", plname); - - cl->istobeloaded = false; - - cl->state = cs_free; - - COM_Parse(plname); - - if (!*com_token) - continue; - - strcpy(cl->name, com_token); - cl->state = cs_zombie; - cl->connection_started = realtime+20; - cl->istobeloaded = true; - - for (i=0 ; ispawn_parms[i]); - } - } - if (version == 5 || version == 6) - { - fscanf (f, "%f\n", &tfloat); - current_skill = (int)(tfloat + 0.1); - Cvar_Set ("skill", va("%i", current_skill)); - Cvar_SetValue ("deathmatch", 0); - Cvar_SetValue ("coop", 0); - Cvar_SetValue ("teamplay", 0); - - if (version == 5) - { - progstype = PROG_NQ; - Cvar_SetVar (pr_ssqc_progs, "progs.dat"); //NQ's progs. - } - else - { - progstype = PROG_QW; - Cvar_SetVar (&pr_ssqc_progs, "spprogs.dat"); //zquake's single player qw progs. - } - pt = 0; - } - else - { - fscanf (f, "%f\n", &tfloat); - pt = tfloat; - - // this silliness is so we can load 1.06 save files, which have float skill values - fscanf (f, "%f\n", &tfloat); - current_skill = (int)(tfloat + 0.1); - Cvar_Set ("skill", va("%i", current_skill)); - - fscanf (f, "%f\n", &tfloat); - Cvar_SetValue ("deathmatch", tfloat); - fscanf (f, "%f\n", &tfloat); - Cvar_SetValue ("coop", tfloat); - fscanf (f, "%f\n", &tfloat); - Cvar_SetValue ("teamplay", tfloat); - } - fscanf (f, "%s\n",mapname); - fscanf (f, "%f\n",&time); - - SV_SpawnServer (mapname, NULL, false, false); //always inits MAX_CLIENTS slots. That's okay, because we can cut the max easily. - if (sv.state != ss_active) - { - fclose (f); - Con_TPrintf ("Couldn't load map\n"); - return; - } - - sv.allocated_client_slots = slots; - -// load the light styles - - for (i=0 ; iload_ents(svprogfuncs, file, 0); - BZ_Free(file); - - PR_LoadGlabalStruct(); - - sv.time = time; - - pr_global_struct->time = sv.physicstime; - - fclose (f); - - SV_ClearWorld (); - - for (i=0 ; iisfree) - continue; - - SV_LinkEdict (ent, false); - } - - for (i=0 ; i= FTESAVEGAME_VERSION+GT_MAX) { - VFS_CLOSE (f); - Con_TPrintf ("Savegame is version %i, not %i\n", version, FTESAVEGAME_VERSION); + SV_Loadgame_Legacy(filename, f, version); return; } gametype = version - FTESAVEGAME_VERSION; @@ -1142,14 +1164,21 @@ void SV_Loadgame_f (void) for (clnum = 0; clnum < svs.allocated_client_slots; clnum++) //clear the server for the level change. { cl = &svs.clients[clnum]; - if (cl->state <= cs_zombie) + if (cl->state <= cs_loadzombie) continue; - if (cl->protocol == SCP_QUAKE2) - MSG_WriteByte (&cl->netchan.message, svcq2_stufftext); +#ifndef SERVERONLY + if (cl->netchan.remote_address.type == NA_LOOPBACK) + CL_Disconnect(); else - MSG_WriteByte (&cl->netchan.message, svc_stufftext); - MSG_WriteString (&cl->netchan.message, "echo Loading Game;disconnect;wait;wait;reconnect\n"); //kindly ask the client to come again. +#endif + { + if (cl->protocol == SCP_QUAKE2) + MSG_WriteByte (&cl->netchan.message, svcq2_stufftext); + else + MSG_WriteByte (&cl->netchan.message, svc_stufftext); + MSG_WriteString (&cl->netchan.message, "echo Loading Game;disconnect;wait;wait;reconnect\n"); //kindly ask the client to come again. + } cl->istobeloaded = false; } @@ -1174,7 +1203,7 @@ void SV_Loadgame_f (void) cl->name = cl->namebuf; if (*str) { - cl->state = cs_zombie; + cl->state = cs_loadzombie; cl->connection_started = realtime+20; cl->istobeloaded = true; loadzombies++; diff --git a/engine/server/server.h b/engine/server/server.h index 926da70a..bbb87b19 100644 --- a/engine/server/server.h +++ b/engine/server/server.h @@ -27,6 +27,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. typedef enum { ss_dead, // no map loaded + ss_clustermode, ss_loading, // spawning level edicts ss_active, // actively running ss_cinematic @@ -110,6 +111,7 @@ typedef struct double time; double starttime; int framenum; + int logindatabase; qboolean paused; // are we paused? float pausedstart; @@ -131,12 +133,12 @@ typedef struct }; #endif struct { - char *vw_model_precache[32]; - char *model_precache[MAX_MODELS]; // NULL terminated + const char *vw_model_precache[32]; + const char *model_precache[MAX_MODELS]; // NULL terminated char particle_precache[MAX_SSPARTICLESPRE][MAX_QPATH]; // NULL terminated char sound_precache[MAX_SOUNDS][MAX_QPATH]; // NULL terminated - char *lightstyles[MAX_LIGHTSTYLES]; - char lightstylecolours[MAX_LIGHTSTYLES]; + const char *lightstyles[MAX_LIGHTSTYLES]; + qbyte lightstylecolours[MAX_LIGHTSTYLES]; }; } strings; qbyte h2cdtrack; @@ -148,6 +150,8 @@ typedef struct model_t *models[MAX_MODELS]; qbyte *pvs, *phs; // fully expanded and decompressed + struct client_s *skipbprintclient; //SV_BroadcastPrint skips this client + // added to every client's unreliable buffer each frame, then cleared sizebuf_t datagram; qbyte datagram_buf[MAX_DATAGRAM]; @@ -199,6 +203,8 @@ typedef struct qboolean gamedirchanged; + qboolean haveitems2; //use items2 field instead of serverflags for the high bits of STAT_ITEMS + @@ -275,8 +281,8 @@ typedef struct typedef enum { cs_free, // can be reused for a new connection - cs_zombie, // client has been disconnected, but don't reuse - // connection for a couple seconds + cs_zombie, // client has been disconnected, but don't reuse connection for a couple seconds. entity was already cleared. + cs_loadzombie, // slot reserved for a client. the player's entity may or may not be known (istobeloaded says that state). parms _ARE_ known. cs_connected, // has been assigned to a client_t, but not in game yet cs_spawned // client is fully in game } client_conn_state_t; @@ -371,6 +377,9 @@ typedef struct client_s int challenge; int userid; // identifying number char userinfo[EXTENDED_INFO_STRING]; // infostring +#ifdef SUBSERVERS + unsigned int previousserver; +#endif usercmd_t lastcmd; // for filling in big drops and partial predictions double localtime; // of last message @@ -398,7 +407,7 @@ typedef struct client_s char *name; char namebuf[32]; // for printing to other people // extracted from userinfo - char guid[32]; /*+2 for split+pad*/ + char guid[64]; /*+2 for split+pad*/ int messagelevel; // for filtering printed messages // the datagram is written to after every frame, but only cleared @@ -474,6 +483,9 @@ typedef struct client_s qbyte ismuted; qbyte iscuffed; qbyte iscrippled; + qbyte isdeaf; + qbyte islagged; + qbyte isvip; qbyte istobeloaded; //loadgame creates place holders for clients to connect to. Effectivly loading a game reconnects all clients, but has precreated ents. @@ -724,18 +736,30 @@ typedef struct int time; } challenge_t; +#define BAN_BAN (1u<<0) //user is banned from the server +#define BAN_PERMIT (1u<<1) //user can evade block bans or filterban +#define BAN_CUFF (1u<<2) //can't shoot/use impulses +#define BAN_MUTE (1u<<3) //can't use say/say_team +#define BAN_CRIPPLED (1u<<4) //can't move +#define BAN_DEAF (1u<<5) //can't see say/say_team +#define BAN_LAGGED (1u<<6) //given an extra 200ms +#define BAN_VIP (1u<<7) //mods might give the user special rights + typedef struct bannedips_s { - enum {BAN_BAN, BAN_FILTER, BAN_PERMIT} type; + unsigned int banflags; struct bannedips_s *next; netadr_t adr; netadr_t adrmask; - unsigned int expiretime; + time_t expiretime; char reason[1]; } bannedips_t; typedef enum { GT_PROGS, //q1, qw, h2 are similar enough that we consider it only one game mode. (We don't support the h2 protocol) GT_Q1QVM, +#ifdef VM_LUA + GT_LUA, //for the luls +#endif GT_HALFLIFE, GT_QUAKE2, //q2 servers run from a q2 game dll GT_QUAKE3, //q3 servers run off the q3 qvm api @@ -766,6 +790,7 @@ typedef struct int spawncount; // number of servers spawned since start, // used to check late spawns int framenum; //physics frame number for out-of-sequence thinks (fix for slow rockets) + int clusterserverid; // which server we are in the cluster. for gamecode to track with stats. struct ftenet_connections_s *sockets; @@ -928,7 +953,7 @@ extern vfsfile_t *sv_fraglogfile; //=========================================================== void SV_AddDebugPolygons(void); -char *SV_CheckRejectConnection(netadr_t *adr, char *uinfo, unsigned int protocol, unsigned int pext1, unsigned int pext2, char *guid); +const char *SV_CheckRejectConnection(netadr_t *adr, const char *uinfo, unsigned int protocol, unsigned int pext1, unsigned int pext2, char *guid); // // sv_main.c @@ -946,12 +971,13 @@ int SV_CalcPing (client_t *cl, qboolean forcecalc); void SV_FullClientUpdate (client_t *client, client_t *to); void SV_GeneratePublicUserInfo(int pext, client_t *cl, char *info, int infolength); -int SV_ModelIndex (char *name); +int SV_ModelIndex (const char *name); void SV_WriteClientdataToMessage (client_t *client, sizebuf_t *msg); void SVQW_WriteDelta (entity_state_t *from, entity_state_t *to, sizebuf_t *msg, qboolean force, unsigned int protext); -void SV_SaveSpawnparms (qboolean); +void SV_SaveSpawnparms (void); +void SV_SaveSpawnparmsClient(client_t *client, float *transferparms); //if transferparms, calls SetTransferParms instead, and does not modify the player. void SV_SaveLevelCache(char *savename, qboolean dontharmgame); void SV_Savegame (char *savename); qboolean SV_LoadLevelCache(char *savename, char *level, char *startspot, qboolean ignoreplayers); @@ -972,12 +998,44 @@ void Master_Packet (void); void SV_FixupName(char *in, char *out, unsigned int outlen); +#ifdef SUBSERVERS +//cluster stuff +typedef struct pubsubserver_s +{ + struct pubsubserver_s *next; + unsigned int id; + char name[64]; + netadr_t addrv4; + netadr_t addrv6; +} pubsubserver_t; +extern qboolean isClusterSlave; +void SSV_UpdateAddresses(void); +void SSV_InitiatePlayerTransfer(client_t *cl, const char *newserver); +void SSV_PollSlaves(void); +void SSV_InstructMaster(sizebuf_t *cmd); +void SSV_PrintToMaster(char *s); +void SSV_ReadFromControlServer(void); +void SSV_SavePlayerStats(client_t *cl, unsigned int previousserver); + +void Sys_InstructSlave(pubsubserver_t *s, sizebuf_t *cmd); +int Sys_SubServerRead(pubsubserver_t *s); //1: yes. 0: no. -1: error +pubsubserver_t *Sys_ForkServer(void); + +#define SSV_IsSubServer() isClusterSlave +#else +#define SSV_UpdateAddresses() false +#define MSV_ClusterLogin(guid,info,infosize) false +#define SSV_IsSubServer() false +#endif + // // sv_init.c // +void SV_SpawnClusterMode(void); void SV_SpawnServer (char *server, char *startspot, qboolean noents, qboolean usecinematic); void SV_UnspawnServer (void); void SV_FlushSignon (void); +void SV_UpdateMaxPlayers(int newmax); void SV_FilterImpulseInit(void); qboolean SV_FilterImpulse(int imp, int level); @@ -1040,8 +1098,8 @@ void VARGS SV_Multicast (vec3_t origin, multicast_t to); #define FULLDIMENSIONMASK 0xffffffff void SV_MulticastProtExt(vec3_t origin, multicast_t to, int dimension_mask, int with, int without); -void SV_StartSound (int ent, vec3_t origin, int seenmask, int channel, char *sample, int volume, float attenuation, int pitchadj); -void SVQ1_StartSound (float *origin, wedict_t *entity, int channel, char *sample, int volume, float attenuation, int pitchadj); +void SV_StartSound (int ent, vec3_t origin, int seenmask, int channel, const char *sample, int volume, float attenuation, int pitchadj); +void SVQ1_StartSound (float *origin, wedict_t *entity, int channel, const char *sample, int volume, float attenuation, int pitchadj); void SV_PrintToClient(client_t *cl, int level, const char *string); void SV_TPrintToClient(client_t *cl, int level, const char *string); void SV_StuffcmdToClient(client_t *cl, char *string); @@ -1099,19 +1157,10 @@ void SVM_Think(int port); // // svonly.c // -typedef enum {RD_NONE, RD_CLIENT, RD_PACKET, RD_OBLIVION} redirect_t; //oblivion is provided so people can read the output before the buffer is wiped. +typedef enum {RD_NONE, RD_CLIENT, RD_PACKET, RD_OBLIVION, RD_MASTER} redirect_t; //oblivion is provided so people can read the output before the buffer is wiped. void SV_BeginRedirect (redirect_t rd, int lang); void SV_EndRedirect (void); -// -// sv_ccmds.c -// -void SV_Status_f (void); - - - - - qboolean PR_GameCodePacket(char *s); qboolean PR_GameCodePausedTic(float pausedtime); @@ -1183,7 +1232,7 @@ typedef struct { rankstats_t s; } rankinfo_t; -int Rank_GetPlayerID(char *guid, char *name, int pwd, qboolean allowcreate, qboolean requirepasswordtobeset); +int Rank_GetPlayerID(char *guid, const char *name, int pwd, qboolean allowcreate, qboolean requirepasswordtobeset); void Rank_SetPlayerStats(int id, rankstats_t *stats); rankstats_t *Rank_GetPlayerStats(int id, rankstats_t *buffer); rankinfo_t *Rank_GetPlayerInfo(int id, rankinfo_t *buffer); @@ -1197,11 +1246,11 @@ int Rank_GetPass (char *name); extern cvar_t rank_needlogin; -client_t *SV_GetClientForString(char *name, int *id); +client_t *SV_GetClientForString(const char *name, int *id); qboolean SV_MayCheat(void); -qboolean ReloadRanking(client_t *cl, char *newname); +qboolean ReloadRanking(client_t *cl, const char *newname); #endif @@ -1217,7 +1266,7 @@ void NPP_NQWriteLong(int dest, long data); void NPP_NQWriteAngle(int dest, float data); void NPP_NQWriteCoord(int dest, float data); void NPP_NQWriteFloat(int dest, float data); -void NPP_NQWriteString(int dest, char *data); +void NPP_NQWriteString(int dest, const char *data); void NPP_NQWriteEntity(int dest, short data); void NPP_QWWriteByte(int dest, qbyte data); @@ -1227,7 +1276,7 @@ void NPP_QWWriteLong(int dest, long data); void NPP_QWWriteAngle(int dest, float data); void NPP_QWWriteCoord(int dest, float data); void NPP_QWWriteFloat(int dest, float data); -void NPP_QWWriteString(int dest, char *data); +void NPP_QWWriteString(int dest, const char *data); void NPP_QWWriteEntity(int dest, short data); @@ -1253,10 +1302,6 @@ void SV_ChatThink(client_t *client); #endif -void SV_ConSay_f(void); - - - // diff --git a/engine/server/sv_ccmds.c b/engine/server/sv_ccmds.c index 232600d9..b9ed5271 100644 --- a/engine/server/sv_ccmds.c +++ b/engine/server/sv_ccmds.c @@ -42,10 +42,10 @@ cvar_t sv_cheats = SCVARF("sv_cheats", "0", CVAR_LATCH); extern cvar_t sv_public; //generic helper function for naming players. -client_t *SV_GetClientForString(char *name, int *id) +client_t *SV_GetClientForString(const char *name, int *id) { int i; - char *s; + const char *s; char nicename[80]; char niceclname[80]; client_t *cl; @@ -58,7 +58,7 @@ client_t *SV_GetClientForString(char *name, int *id) { for (i = first, cl = svs.clients+first; i < sv.allocated_client_slots; i++, cl++) { - if (cl->state<=cs_zombie) + if (cl->state<=cs_loadzombie) continue; *id=i+1; @@ -82,7 +82,7 @@ client_t *SV_GetClientForString(char *name, int *id) int uid = Q_atoi(name); for (i = first, cl = svs.clients; i < sv.allocated_client_slots; i++, cl++) { - if (cl->state<=cs_zombie) + if (cl->state<=cs_loadzombie) continue; if (cl->userid == uid) { @@ -97,7 +97,7 @@ client_t *SV_GetClientForString(char *name, int *id) for (i = first, cl = svs.clients+first; i < sv.allocated_client_slots; i++, cl++) { - if (cl->state<=cs_zombie) + if (cl->state<=cs_loadzombie) continue; @@ -135,7 +135,7 @@ void Master_ClearAll(void); void Master_ReResolve(void); void Master_Add(char *stringadr); -void SV_SetMaster_f (void) +static void SV_SetMaster_f (void) { int i; @@ -162,9 +162,10 @@ void SV_SetMaster_f (void) SV_Quit_f ================== */ -void SV_Quit_f (void) +static void SV_Quit_f (void) { - SV_FinalMessage ("server shutdown\n"); + if (sv.state >= ss_loading) + SV_FinalMessage ("server shutdown\n"); Con_TPrintf ("Shutting down.\n"); SV_Shutdown (); Sys_Quit (); @@ -175,7 +176,7 @@ void SV_Quit_f (void) SV_Fraglogfile_f ============ */ -void SV_Fraglogfile_f (void) +static void SV_Fraglogfile_f (void) { char name[MAX_OSPATH]; int i; @@ -220,7 +221,7 @@ SV_SetPlayer Sets host_client and sv_player to the player with idnum Cmd_Argv(1) ================== */ -qboolean SV_SetPlayer (void) +static qboolean SV_SetPlayer (void) { client_t *cl; int i; @@ -251,7 +252,7 @@ SV_God_f Sets client to godmode ================== */ -void SV_God_f (void) +static void SV_God_f (void) { if (!SV_MayCheat()) { @@ -271,7 +272,7 @@ void SV_God_f (void) } -void SV_Noclip_f (void) +static void SV_Noclip_f (void) { if (!SV_MayCheat()) { @@ -301,7 +302,7 @@ void SV_Noclip_f (void) SV_Give_f ================== */ -void SV_Give_f (void) +static void SV_Give_f (void) { char *t; int v; @@ -374,21 +375,23 @@ void SV_Give_f (void) } } -int QDECL ShowMapList (const char *name, qofs_t flags, void *parm, searchpathfuncs_t *spath) +static int QDECL ShowMapList (const char *name, qofs_t flags, void *parm, searchpathfuncs_t *spath) { + char stripped[64]; if (name[5] == 'b' && name[6] == '_') //skip box models return true; - Con_Printf("%s\n", name+5); + COM_StripExtension(name+5, stripped, sizeof(stripped)); + Con_Printf("^[%s\\map\\%s^]\n", stripped, stripped); return true; } -void SV_MapList_f(void) +static void SV_MapList_f(void) { COM_EnumerateFiles("maps/*.bsp", ShowMapList, NULL); COM_EnumerateFiles("maps/*.cm", ShowMapList, NULL); COM_EnumerateFiles("maps/*.hmp", ShowMapList, NULL); } -void gtcallback(struct cvar_s *var, char *oldvalue) +static void gtcallback(struct cvar_s *var, char *oldvalue) { Con_Printf("g_gametype changed\n"); } @@ -418,7 +421,7 @@ void SV_Map_f (void) char spot[MAX_QPATH]; char expanded[MAX_QPATH]; char *nextserver; - qboolean issamelevel = false; + qboolean isrestart = false; qboolean newunit = false; qboolean cinematic = false; qboolean waschangelevel = false; @@ -484,7 +487,7 @@ void SV_Map_f (void) { //grab the current map name COM_StripExtension(COM_SkipPath(sv.modelname), level, sizeof(level)); - issamelevel = true; + isrestart = true; if (!*level) { @@ -574,9 +577,10 @@ void SV_Map_f (void) CL_Disconnect(); #endif - SV_SaveSpawnparms (issamelevel); + if (!isrestart) + SV_SaveSpawnparms (); - if (startspot && !issamelevel && !newunit) + if (startspot && !isrestart && !newunit) { #ifdef Q2SERVER if (ge) @@ -676,7 +680,7 @@ void SV_Map_f (void) } } - if (!issamelevel) + if (!isrestart) { cvar_t *nsv; nsv = Cvar_Get("nextserver", "", 0, ""); @@ -757,253 +761,530 @@ void SV_KickSlot_f (void) Con_Printf("Client %i is not active\n", clnum); } -void SV_BanName_f (void) +//will kick clients if they got banned (without being safe) +void SV_EvaluatePenalties(client_t *cl) { - client_t *cl; - int clnum=-1; - char *reason = NULL; - int reasonsize = 0; + bannedips_t *banip; + bannedips_t *cuff = NULL; + bannedips_t *mute = NULL; + bannedips_t *cripple = NULL; + bannedips_t *deaf = NULL; + bannedips_t *lagged = NULL; + bannedips_t *vip = NULL; + bannedips_t *safe = NULL; + bannedips_t *banned = NULL; + char *penalties[8]; + char *reasons[8]; + int numpenalties = 0; + int numreasons = 0; - if (Cmd_Argc() < 2) + if (cl->realip.type != NA_INVALID) { - Con_Printf("%s userid|nick [reason]\n", Cmd_Argv(0)); - return; - } - - if (Cmd_Argc() > 2) - { - reason = Cmd_Argv(2); - reasonsize = strlen(reason); - } - - while((cl = SV_GetClientForString(Cmd_Argv(1), &clnum))) - if (cl) - { - bannedips_t *nb; - - if (NET_IsLoopBackAddress(&cl->netchan.remote_address)) + for (banip = svs.bannedips; banip; banip=banip->next) { - Con_Printf("You're not allowed to ban loopback!\n"); - continue; + if (NET_CompareAdrMasked(&cl->realip, &banip->adr, &banip->adrmask)) + { + if ((banip->banflags & BAN_CUFF) && !cuff) + cuff = banip; + if ((banip->banflags & BAN_MUTE) && !mute) + mute = banip; + if ((banip->banflags & BAN_CRIPPLED) && !cripple) + cripple = banip; + if ((banip->banflags & BAN_DEAF) && !deaf) + deaf = banip; + if ((banip->banflags & BAN_LAGGED) && !lagged) + lagged = banip; + if ((banip->banflags & BAN_BAN) && !banned) + banned = banip; + if ((banip->banflags & BAN_PERMIT) && !safe) + safe = banip; + if ((banip->banflags & BAN_VIP) && !vip) + vip = banip; + } } - - nb = Z_Malloc(sizeof(bannedips_t)+reasonsize); - nb->next = svs.bannedips; - nb->adr = cl->netchan.remote_address; - NET_IntegerToMask(&nb->adr, &nb->adrmask, -1); // fill mask - if (*Cmd_Argv(2)) //explicit blocking of all ports of a client ip - nb->adr.port = 0; - svs.bannedips = nb; - if (reasonsize) - Q_strcpy(nb->reason, reason); - - SV_BroadcastTPrintf (PRINT_HIGH, "%s was banned\n", cl->name); - // print directly, because the dropped client won't get the - // SV_BroadcastPrintf message - SV_ClientTPrintf (cl, PRINT_HIGH, "You were banned\n"); - SV_LogPlayer(cl, "banned name"); - SV_DropClient (cl); } - - if (clnum == -1) - Con_TPrintf ("Couldn't find user number %s\n", Cmd_Argv(1)); -} - -void SV_KickBanIP(netadr_t *banadr, netadr_t *banmask, char *reason) -{ - qboolean shouldkick; - client_t *cl; - int i; - unsigned int reasonsize; - bannedips_t *nb; - - if (reason) - reasonsize = strlen(reason); - else - reasonsize = 0; - - // loop through clients and kick the ones that match - for (i = 0, cl = svs.clients; i < sv.allocated_client_slots; i++, cl++) + for (banip = svs.bannedips; banip; banip=banip->next) { - if (cl->state<=cs_zombie) - continue; - - shouldkick = false; - - if (NET_CompareAdrMasked(&cl->netchan.remote_address, banadr, banmask)) - shouldkick = true; - else if (cl->realip_status >= 1) - if (NET_CompareAdrMasked(&cl->realip, banadr, banmask)) - shouldkick = true; - - if (shouldkick) + if (NET_CompareAdrMasked(&cl->netchan.remote_address, &banip->adr, &banip->adrmask)) { - // match, so kick - SV_BroadcastTPrintf (PRINT_HIGH, "%s was banned\n", cl->name); - // print directly, because the dropped client won't get the - // SV_BroadcastPrintf message - SV_ClientTPrintf (cl, PRINT_HIGH, "You were banned\n"); - SV_LogPlayer(cl, "banned ip"); - SV_DropClient (cl); + if ((banip->banflags & BAN_CUFF) && !cuff) + cuff = banip; + if ((banip->banflags & BAN_MUTE) && !mute) + mute = banip; + if ((banip->banflags & BAN_CRIPPLED) && !cripple) + cripple = banip; + if ((banip->banflags & BAN_DEAF) && !deaf) + deaf = banip; + if ((banip->banflags & BAN_LAGGED) && !lagged) + lagged = banip; + if ((banip->banflags & BAN_BAN) && !banned) + banned = banip; + if ((banip->banflags & BAN_PERMIT) && !safe) + safe = banip; + if ((banip->banflags & BAN_VIP) && !vip) + vip = banip; } } - // add IP and mask to ban list - nb = Z_Malloc(sizeof(bannedips_t)+reasonsize); - nb->next = svs.bannedips; - nb->adr = *banadr; - nb->adrmask = *banmask; - svs.bannedips = nb; - if (reasonsize) - Q_strcpy(nb->reason, reason); -} - -void SV_BanIP_f (void) -{ - netadr_t banadr; - netadr_t banmask; - char *reason = NULL; - - if (Cmd_Argc() < 2) + if (banned && !safe) { - Con_Printf("%s address/mask|adress/maskbits [reason]\n", Cmd_Argv(0)); - return; - } - - if (!NET_StringToAdrMasked(Cmd_Argv(1), &banadr, &banmask)) - { - Con_Printf("invalid address or mask\n"); - return; - } - - if (NET_IsLoopBackAddress(&banadr)) - { - Con_Printf("You're not allowed to ban loopback!\n"); - return; - } - - if (Cmd_Argc() > 2) - reason = Cmd_Argv(2); - - SV_KickBanIP(&banadr, &banmask, reason); -} - -void SV_BanClientIP_f (void) -{ - netadr_t banmask; - client_t *cl; - char *reason = NULL; - int clnum=-1; - - while((cl = SV_GetClientForString(Cmd_Argv(1), &clnum))) - { - if (NET_IsLoopBackAddress(&cl->netchan.remote_address)) - { - Con_Printf("You're not allowed to ban loopback!\n"); - continue; - } - - if (cl->realip_status>0) - { - memset(&banmask.address, 0xff, sizeof(banmask.address)); - banmask.type = cl->netchan.remote_address.type; - SV_KickBanIP(&cl->realip, &banmask, reason); - } + if (*banned->reason) + SV_BroadcastPrintf(PRINT_HIGH, va("%s was banned: %s\n", cl->name, banned->reason)); else + SV_BroadcastPrintf(PRINT_HIGH, va("%s was banned\n", cl->name)); + cl->drop = true; + } + + if (cuff) + { + if (!cl->iscuffed) { - memset(&banmask.address, 0xff, sizeof(banmask.address)); - banmask.type = cl->netchan.remote_address.type; - SV_KickBanIP(&cl->netchan.remote_address, &banmask, reason); + cl->iscuffed = true; + penalties[numpenalties++] = "cuffed"; + reasons[numreasons++] = cuff->reason; + } + } + else + { + if (cl->iscuffed == true) + { + cl->iscuffed = false; + SV_PrintToClient(cl, PRINT_HIGH, "Cuff expired\n"); + } + } + + if (cripple) + { + if (!cl->iscrippled) + { + cl->iscrippled = true; + penalties[numpenalties++] = "crippled"; + reasons[numreasons++] = cripple->reason; + } + } + else + { + if (cl->iscrippled == true) + { + cl->iscrippled = false; + SV_PrintToClient(cl, PRINT_HIGH, "Cripple expired\n"); + } + } + + if (mute) + { + if (!cl->ismuted) + { + cl->ismuted = true; + if (!deaf) + { + penalties[numpenalties++] = "muted"; + reasons[numreasons++] = mute->reason; + } + } + } + else + { + if (cl->ismuted == true) + { + cl->ismuted = false; + + if (!cl->isdeaf) + SV_PrintToClient(cl, PRINT_HIGH, "Mute expired\n"); + if (!deaf && cl->isdeaf == true) + cl->isdeaf = false; //don't let them know that they were ever mute+deaf. + } + } + if (deaf) + { + if (!cl->isdeaf) + { + cl->isdeaf = true; + if (!mute) + { + penalties[numpenalties++] = "deaf"; + reasons[numreasons++] = deaf->reason; + } + } + } + else + { + if (cl->isdeaf == true) + { + cl->isdeaf = false; + SV_PrintToClient(cl, PRINT_HIGH, "Deafness expired\n"); + } + } + if (lagged) + { + if (!cl->islagged) + { + cl->islagged = true; + penalties[numpenalties++] = "lagged"; + reasons[numreasons++] = lagged->reason; + } + } + else + { + if (cl->islagged == true) + { + cl->islagged = false; + SV_PrintToClient(cl, PRINT_HIGH, "Lag penalty expired\n"); + } + } + if (vip) + { + if (!cl->isvip) + { + cl->isvip = true; + penalties[numpenalties++] = "vip"; + reasons[numreasons++] = deaf->reason; + } + } + else + { + if (cl->isvip == true) + { + cl->isvip = false; + SV_PrintToClient(cl, PRINT_HIGH, "VIP expired\n"); + } + } + + if (cl->controller) + return; //don't spam it for every player in a splitscreen client. + + if (numpenalties) + { + char penaltystring[1024]; + int i, j; + Q_strncpyz(penaltystring, "You are ", sizeof(penaltystring)); + for (i = 0; i < numpenalties; i++) + { + if (i && i == numpenalties-1) + Q_strncatz(penaltystring, " and ", sizeof(penaltystring)); + else if (i) + Q_strncatz(penaltystring, ", ", sizeof(penaltystring)); + Q_strncatz(penaltystring, penalties[i], sizeof(penaltystring)); + } + Q_strncatz(penaltystring, "\n", sizeof(penaltystring)); + SV_PrintToClient(cl, PRINT_HIGH, penaltystring); + for (i = 0; i < numreasons; i++) + { + if (*reasons[i]) + { + for(j = 0; j < i; j++) + if (!strcmp(reasons[i], reasons[j])) + break; + if (i == j) + SV_PrintToClient(cl, PRINT_HIGH, va(" %s\n", reasons[i])); + } } } } -void SV_FilterIP_f (void) +static time_t reevaluatebantime; +static qboolean reevaluatebans; +//could use time(NULL) instead, but this avoids a system call. +static time_t SV_BanTime(void) { - netadr_t banadr; - netadr_t banmask; + static double bantimemark; + static time_t banstarttime; + if (!banstarttime) + { + banstarttime = time(NULL); + bantimemark = realtime; + } + return banstarttime + (realtime - bantimemark); +} +//removes anything with an expiry time in the past. +//avoids walking the list if there's nothing changed. +//can be used to force penalty reevaluation. +void SV_KillExpiredBans(void) +{ + bannedips_t **link, *banip; + time_t curtime = SV_BanTime(); int i; - client_t *cl; + if (reevaluatebantime && curtime > reevaluatebantime) + { + reevaluatebantime = 0; + reevaluatebantime = ~reevaluatebantime; //should be 64bit safe? + if (reevaluatebantime < 0) + reevaluatebantime = (unsigned long long)reevaluatebantime>>1; + for(link = &svs.bannedips; (banip = *link) != NULL; ) + { + if (banip->expiretime) + { + if (banip->expiretime < curtime) + { + reevaluatebans = true; + *link = banip->next; + Z_Free(banip); + continue; + } + if (reevaluatebantime > banip->expiretime) + reevaluatebantime = banip->expiretime; + } + link = &banip->next; + } + } + + if (reevaluatebans) + { + reevaluatebans = false; + for (i = 0; i < svs.allocated_client_slots; i++) + { + if (svs.clients[i].state<=cs_loadzombie) + continue; + + SV_EvaluatePenalties(&svs.clients[i]); + } + } +} + +//adds a new ban/penalty. +//will remove old penalties if the new one has a longer duration, otherwise will ignore the add. +static qboolean SV_AddBanEntry(bannedips_t *proto, char *reason) +{ + bannedips_t *nb, **link; + nb = svs.bannedips; + while (nb) + { + if (NET_CompareAdr(&nb->adr, &proto->adr) && NET_CompareAdr(&nb->adrmask, &proto->adrmask)) + { + //found a match, figure out which lasts longer + //the shorter ban duration gets its effective banflags stripped. + if ((proto->expiretime && proto->expiretime < nb->expiretime) || !nb->expiretime) + proto->banflags &= ~nb->banflags; + else + nb->banflags &= ~proto->banflags; + + if (!proto->banflags) + { + //we should not have been able to strip a previous nb->banflags if this ban was duped later. + return false; + } + if (!nb->banflags) + { + reevaluatebantime = nb->expiretime = 1; //make sure it expires 'soon'. + } + } + nb = nb->next; + } + + link = &svs.bannedips; + + // add IP and mask to filter list + nb = Z_Malloc(sizeof(bannedips_t) + strlen(reason)); + nb->adr = proto->adr; + nb->adrmask = proto->adrmask; + nb->banflags = proto->banflags; + nb->expiretime = proto->expiretime; + Q_strcpy(nb->reason, reason); + + nb->next = *link; + *link = nb; + + reevaluatebans = true; //make sure the new ban/penalty applies to the right IPs. + if (nb->expiretime && reevaluatebantime > nb->expiretime) + reevaluatebantime = nb->expiretime; + return true; +} + +//slightly different logic. +//if duration is specified, just does an add instead. +//otherwise ignores durations. +//only really works with a single toggle. if any are found, will not add. +//returns 1 if added, 0 if removed, and -1 if tried to add and it already existed. +static int SV_ToggleBan(bannedips_t *proto, char *reason) +{ + qboolean found = false; bannedips_t *nb; + if (proto->expiretime) + return SV_AddBanEntry(proto, reason)?true:-1; + + nb = svs.bannedips; + while (nb) + { + if (NET_CompareAdr(&nb->adr, &proto->adr) && NET_CompareAdr(&nb->adrmask, &proto->adrmask)) + { + if (nb->banflags & proto->banflags) + { + found = true; + nb->banflags &= ~proto->banflags; + reevaluatebans = true; + if (!nb->banflags) + reevaluatebantime = nb->expiretime = 1; //make sure it expires 'soon'. + } + } + nb = nb->next; + } + + if (found) + return 0; + return SV_AddBanEntry(proto, reason)?true:-1; +} + +extern cvar_t filterban; +//returns a reason if the client is banned. ignores other penalties. +char *SV_BannedReason (netadr_t *a) +{ + char *reason = filterban.value?NULL:""; //"" = banned with no explicit reason + bannedips_t *banip; + + if (NET_IsLoopBackAddress(a)) + return NULL; // never filter loopback + + for (banip = svs.bannedips; banip; banip=banip->next) + { + if (NET_CompareAdrMasked(a, &banip->adr, &banip->adrmask)) + { + if (banip->banflags & BAN_BAN) + return banip->reason; //banned, with reason. + if (banip->banflags & BAN_PERMIT) + return NULL; //allowed + } + } + return reason; +} + +#ifdef _MSC_VER +#define strtoull _strtoui64 +#endif + +static void SV_FilterIP_f (void) +{ + bannedips_t proto; extern cvar_t filterban; + int arg=2; + char *s; if (Cmd_Argc() < 2) { - Con_Printf("%s address/mask|adress/maskbits\n", Cmd_Argv(0)); + Con_Printf("%s
[flags] [+time] [reason]\n", Cmd_Argv(0)); + Con_Printf("allowed flags: ban,safe,cuff,mute,cripple,deaf,lag. time is in seconds (omitting the plus will be taken to mean unix time).\n", Cmd_Argv(0)); return; } - if (!NET_StringToAdrMasked(Cmd_Argv(1), &banadr, &banmask)) + if (!NET_StringToAdrMasked(Cmd_Argv(1), &proto.adr, &proto.adrmask)) { Con_Printf("invalid address or mask\n"); return; } - if (NET_IsLoopBackAddress(&banadr)) + if (NET_IsLoopBackAddress(&proto.adr)) { Con_Printf("You're not allowed to filter loopback!\n"); return; } - nb = svs.bannedips; - while (nb) + s = Cmd_Argv(2); + proto.banflags = 0; + while(*s) { - if (NET_CompareAdr(&nb->adr, &banadr) && NET_CompareAdr(&nb->adrmask, &banmask)) - { - Con_Printf("%s is already banned\n", Cmd_Argv(1)); - return; - } - nb = nb->next; + s=COM_ParseToken(s,","); + if (!Q_strcasecmp(com_token, ",")) + ; + else if (!Q_strcasecmp(com_token, "ban")) + proto.banflags |= BAN_BAN; + else if (!Q_strcasecmp(com_token, "safe") || !Q_strcasecmp(com_token, "permit")) + proto.banflags |= BAN_PERMIT; + else if (!Q_strcasecmp(com_token, "cuff")) + proto.banflags |= BAN_CUFF; + else if (!Q_strcasecmp(com_token, "mute")) + proto.banflags |= BAN_MUTE; + else if (!Q_strcasecmp(com_token, "cripple")) + proto.banflags |= BAN_CRIPPLED; + else if (!Q_strcasecmp(com_token, "deaf")) + proto.banflags |= BAN_DEAF; + else if (!Q_strcasecmp(com_token, "lag") || !Q_strcasecmp(com_token, "lagged")) + proto.banflags |= BAN_LAGGED; + else if (!Q_strcasecmp(com_token, "vip")) + proto.banflags |= BAN_VIP; + else + Con_Printf("Unknown ban/penalty flag: %s. ignoring.\n", com_token); + } + //if no flags were specified, + if (!proto.banflags) + { + if (!strcmp(Cmd_Argv(0), "ban")) + proto.banflags = BAN_BAN; + else + proto.banflags = filterban.ival?BAN_BAN:BAN_PERMIT; } - // loop through clients and kick the ones that match - for (i = 0, cl = svs.clients; i < sv.allocated_client_slots; i++, cl++) - { - if (cl->state<=cs_zombie) - continue; + s = Cmd_Argv(3); + if (*s == '+') + proto.expiretime = SV_BanTime() + strtoull(s+1, NULL, 0); + else + proto.expiretime = strtoull(s, NULL, 0); - if (filterban.value && NET_CompareAdrMasked(&cl->netchan.remote_address, &banadr, &banmask)) - SV_DropClient (cl); - } - - // add IP and mask to filter list - nb = Z_Malloc(sizeof(bannedips_t)); - nb->next = svs.bannedips; - nb->adr = banadr; - nb->adrmask = banmask; - nb->type = BAN_FILTER; - *nb->reason = 0; - svs.bannedips = nb; + //and then add it + if (!SV_AddBanEntry(&proto, Cmd_Argv(4))) + Con_Printf("addip: entry already exists\n"); } -void SV_BanList_f (void) +static void SV_BanList_f (void) { int bancount = 0; - bannedips_t *nb = svs.bannedips; + bannedips_t *nb; char adr[MAX_ADR_SIZE]; + char middlebit[256]; + time_t bantime = SV_BanTime(); - while (nb) + SV_KillExpiredBans(); + + for (nb = svs.bannedips; nb; nb = nb->next) { - if (nb->reason[0]) - Con_Printf("%s, %s\n", NET_AdrToStringMasked(adr, sizeof(adr), &nb->adr, &nb->adrmask), nb->reason); - else - Con_Printf("%s\n", NET_AdrToStringMasked(adr, sizeof(adr), &nb->adr, &nb->adrmask)); - bancount++; - nb = nb->next; + if (nb->banflags & BAN_BAN) + { + *middlebit = 0; + if (nb->expiretime) + Q_strncatz(middlebit, va(",\t%+llu", (unsigned long long)nb->expiretime - bantime), sizeof(middlebit)); + if (nb->reason[0]) + Q_strncatz(middlebit, ",\t", sizeof(middlebit)); + Con_Printf("%s%s%s\n", NET_AdrToStringMasked(adr, sizeof(adr), &nb->adr, &nb->adrmask), middlebit, nb->reason); + bancount++; + } } Con_Printf("%i total entries in ban list\n", bancount); } -void SV_FilterList_f (void) +static void SV_FilterList_f (void) { int filtercount = 0; - bannedips_t *nb = svs.bannedips; + bannedips_t *nb; char adr[MAX_ADR_SIZE]; + char banflags[1024]; + int i; + static const char *banflagnames[] = { + "ban", + "safe", + "cuff", + "mute", + "cripple", + "deaf", + "lag", + "vip", + NULL + }; - while (nb) + SV_KillExpiredBans(); + + for (nb = svs.bannedips; nb; ) { - Con_Printf("%s\n", NET_AdrToStringMasked(adr, sizeof(adr), &nb->adr, &nb->adrmask)); + *banflags = 0; + for (i = 0; banflagnames[i]; i++) + { + if (nb->banflags & (1u<adr, &nb->adrmask), banflags); filtercount++; nb = nb->next; } @@ -1011,62 +1292,19 @@ void SV_FilterList_f (void) Con_Printf("%i total entries in filter list\n", filtercount); } -void SV_Unban_f (void) +static void SV_Unfilter_f (void) { + qboolean found = false; qboolean all = false; bannedips_t **link; bannedips_t *nb; netadr_t unbanadr = {0}; netadr_t unbanmask = {0}; char adr[MAX_ADR_SIZE]; + unsigned int banflags, nf; + char *s; - if (Cmd_Argc() < 2) - { - Con_Printf("%s address/mask|address/maskbits|all\n", Cmd_Argv(0)); - return; - } - - if (!Q_strcasecmp(Cmd_Argv(1), "all")) - { - Con_Printf("removing all banned addresses\n"); - all = true; - } - else if (!NET_StringToAdrMasked(Cmd_Argv(1), &unbanadr, &unbanmask)) - { - Con_Printf("invalid address or mask\n"); - return; - } - - for (link = &svs.bannedips ; (nb = *link) ; ) - { - if (all || (NET_CompareAdr(&nb->adr, &unbanadr) && NET_CompareAdr(&nb->adrmask, &unbanmask))) - { - if (!all) - Con_Printf("unbanned %s\n", NET_AdrToStringMasked(adr, sizeof(adr), &nb->adr, &nb->adrmask)); - *link = nb->next; - Z_Free(nb); - - if (!all) - return; - } - else - { - link = &(*link)->next; - } - } - - if (!all) - Con_Printf("address was not banned\n"); -} - -void SV_Unfilter_f (void) -{ - qboolean all = false; - bannedips_t **link; - bannedips_t *nb; - netadr_t unbanadr = {0}; - netadr_t unbanmask = {0}; - char adr[MAX_ADR_SIZE]; + SV_KillExpiredBans(); if (Cmd_Argc() < 2) { @@ -1085,17 +1323,54 @@ void SV_Unfilter_f (void) return; } + s = Cmd_Argv(2); + banflags = 0; + while(*s) + { + s=COM_ParseToken(s,","); + if (!Q_strcasecmp(com_token, ",")) + ; + else if (!Q_strcasecmp(com_token, "ban")) + banflags |= BAN_BAN; + else if (!Q_strcasecmp(com_token, "safe") || !Q_strcasecmp(com_token, "permit")) + banflags |= BAN_PERMIT; + else if (!Q_strcasecmp(com_token, "cuff")) + banflags |= BAN_CUFF; + else if (!Q_strcasecmp(com_token, "mute")) + banflags |= BAN_MUTE; + else if (!Q_strcasecmp(com_token, "cripple")) + banflags |= BAN_CRIPPLED; + else if (!Q_strcasecmp(com_token, "deaf")) + banflags |= BAN_DEAF; + else if (!Q_strcasecmp(com_token, "lag") || !Q_strcasecmp(com_token, "lagged")) + banflags |= BAN_LAGGED; + else if (!Q_strcasecmp(com_token, "vip")) + banflags |= BAN_VIP; + else + Con_Printf("Unknown ban/penalty flag: %s. ignoring.\n", com_token); + } + //if no flags were specified, assume all + if (!banflags) + banflags = BAN_BAN|BAN_PERMIT|BAN_CUFF|BAN_MUTE|BAN_CRIPPLED|BAN_DEAF|BAN_LAGGED|BAN_VIP; + for (link = &svs.bannedips ; (nb = *link) ; ) { - if (all || (NET_CompareAdr(&nb->adr, &unbanadr) && NET_CompareAdr(&nb->adrmask, &unbanmask))) + if ((nb->banflags & banflags) && (all || (NET_CompareAdr(&nb->adr, &unbanadr) && NET_CompareAdr(&nb->adrmask, &unbanmask)))) { + found = true; if (!all) Con_Printf("unfiltered %s\n", NET_AdrToStringMasked(adr, sizeof(adr), &nb->adr, &nb->adrmask)); - *link = nb->next; - Z_Free(nb); - if (!all) - return; + nf = nb->banflags & banflags; + nb->banflags -= nf; + if (!nb->banflags) + { + //this entry no longer has any flags + *link = nb->next; + Z_Free(nb); + } + else + link = &(*link)->next; } else { @@ -1103,17 +1378,80 @@ void SV_Unfilter_f (void) } } - if (!all) + if (!all && !found) Con_Printf("address was not filtered\n"); } +static void SV_PenaltyToggle (unsigned int banflag, char *penaltyname) +{ + char *name = Cmd_Argv(1); + char *duration = Cmd_Argv(2); + char *reason = Cmd_Argv(3); + bannedips_t proto; + client_t *cl; + qboolean found = false; + int clnum=-1; -void SV_WriteIP_f (void) + proto.banflags = banflag; + + if (*duration == '+') + proto.expiretime = SV_BanTime() + strtoull(duration+1, &duration, 0); + else + proto.expiretime = strtoull(duration, &duration, 0); + + //both of these should work + //cuff foo "cos they're morons" + //cuff foo +10 "cos they're morons" + if (!*reason && *duration) + reason = duration; + + memset(&proto.adrmask.address, 0xff, sizeof(proto.adrmask.address)); + while((cl = SV_GetClientForString(Cmd_Argv(1), &clnum))) + { + found = true; + proto.adr = cl->netchan.remote_address; + proto.adr.port = 0; + proto.adrmask.type = cl->netchan.remote_address.type; + + switch(SV_ToggleBan(&proto, reason)) + { + case 1: + Con_Printf("%s: %s is now %s\n", Cmd_Argv(0), cl->name, penaltyname); + break; + case 0: + Con_Printf("%s: %s is no longer %s\n", Cmd_Argv(0), cl->name, penaltyname); + break; + default: + case -1: + Con_Printf("%s: %s already %s\n", Cmd_Argv(0), cl->name, penaltyname); + break; + } + } + if (!found) + Con_Printf("%s: no clients\n", Cmd_Argv(0)); +} + +static void SV_WriteIP_f (void) { vfsfile_t *f; char name[MAX_OSPATH]; bannedips_t *bi; char *s; char adr[MAX_ADR_SIZE]; + char banflags[1024]; + int i; + static const char *banflagnames[] = { + "ban", + "safe", + "cuff", + "mute", + "cripple", + "deaf", + "lag", + "vip", + NULL + }; + + SV_KillExpiredBans(); strcpy (name, "listip.cfg"); @@ -1129,16 +1467,22 @@ void SV_WriteIP_f (void) bi = svs.bannedips; while (bi) { - if (bi->type == BAN_BAN) - s = "banip"; - else if (bi->type == BAN_PERMIT) - s = "allowip"; - else - s = "addip"; + *banflags = 0; + for (i = 0; banflagnames[i]; i++) + { + if (bi->banflags & (1u<reason[0]) - s = va("%s %s \"%s\"\n", s, NET_AdrToStringMasked(adr, sizeof(adr), &bi->adr, &bi->adrmask), bi->reason); + s = va("%s %s %llu \"%s\"\n", banflags, NET_AdrToStringMasked(adr, sizeof(adr), &bi->adr, &bi->adrmask), (unsigned long long) bi->expiretime, bi->reason); + else if (bi->expiretime) + s = va("%s %s %llu\n", banflags, NET_AdrToStringMasked(adr, sizeof(adr), &bi->adr, &bi->adrmask), (unsigned long long) bi->expiretime); else - s = va("%s %s\n", s, NET_AdrToStringMasked(adr, sizeof(adr), &bi->adr, &bi->adrmask)); + s = va("%s %s\n", banflags, NET_AdrToStringMasked(adr, sizeof(adr), &bi->adr, &bi->adrmask)); VFS_WRITE(f, s, strlen(s)); bi = bi->next; } @@ -1147,7 +1491,7 @@ void SV_WriteIP_f (void) } -void SV_ForceName_f (void) +static void SV_ForceName_f (void) { client_t *cl; int clnum=-1; @@ -1172,112 +1516,27 @@ void SV_ForceName_f (void) Con_TPrintf ("Couldn't find user number %s\n", Cmd_Argv(1)); } -void SV_CripplePlayer_f (void) +static void SV_CripplePlayer_f (void) { - client_t *cl; - int clnum=-1; - - int persist = *Cmd_Argv(2); - - while((cl = SV_GetClientForString(Cmd_Argv(1), &clnum))) - { - if (!cl->iscrippled) - { - SV_LogPlayer(cl, "crippled"); - if (persist && cl->rankid) - { - cl->iscrippled = 2; - SV_BroadcastTPrintf (PRINT_HIGH, "%s is now crippled permanently\n", cl->name); - } - else - { - cl->iscrippled = true; - SV_BroadcastTPrintf (PRINT_HIGH, "%s is crippled\n", cl->name); - } - } - else - { - SV_LogPlayer(cl, "uncrippled"); - cl->iscrippled = false; - SV_ClientTPrintf (cl, PRINT_HIGH, "You are no longer crippled\n"); - } - } - - if (clnum == -1) - Con_TPrintf ("Couldn't find user number %s\n", Cmd_Argv(1)); + SV_PenaltyToggle(BAN_CRIPPLED, "crippled"); } -void SV_Mute_f (void) +static void SV_Mute_f (void) { - client_t *cl; - int clnum=-1; - - int persist = *Cmd_Argv(2); - - while((cl = SV_GetClientForString(Cmd_Argv(1), &clnum))) - { - if (!cl->ismuted) - { - SV_LogPlayer(cl, "muted"); - if (persist && cl->rankid) - { - cl->ismuted = 2; - SV_BroadcastTPrintf (PRINT_HIGH, "%s was muted permanently\n", cl->name); - } - else - { - cl->ismuted = true; - SV_BroadcastTPrintf (PRINT_HIGH, "%s was muted\n", cl->name); - } - } - else - { - SV_LogPlayer(cl, "unmuted"); - cl->ismuted = false; - SV_ClientTPrintf (cl, PRINT_HIGH, "You are no longer muted\n"); - } - } - - if (clnum == -1) - Con_TPrintf ("Couldn't find user number %s\n", Cmd_Argv(1)); + SV_PenaltyToggle(BAN_MUTE, "muted"); } -void SV_Cuff_f (void) +static void SV_Cuff_f (void) { - client_t *cl; - int clnum=-1; - - int persist = *Cmd_Argv(2); - - while((cl = SV_GetClientForString(Cmd_Argv(1), &clnum))) - { - if (!cl->iscuffed) - { - SV_LogPlayer(cl, "cuffed"); - if (persist && cl->rankid) - { - cl->iscuffed = 2; - SV_BroadcastTPrintf (PRINT_HIGH, "%s is still cuffed\n", cl->name); - } - else - { - cl->iscuffed = true; - SV_BroadcastTPrintf (PRINT_HIGH, "%s is cuffed\n", cl->name); - } - } - else - { - SV_LogPlayer(cl, "uncuffed"); - cl->iscuffed = false; - SV_ClientTPrintf (cl, PRINT_HIGH, "You are no longer cuffed\n"); - } - } - - if (clnum == -1) - Con_TPrintf ("Couldn't find user number %s\n", Cmd_Argv(1)); + SV_PenaltyToggle(BAN_CUFF, "cuffed"); } -void SV_Floodprot_f(void) +static void SV_BanClientIP_f (void) +{ + SV_PenaltyToggle(BAN_BAN, "banned"); +} + +static void SV_Floodprot_f(void) { extern cvar_t sv_floodprotect; extern cvar_t sv_floodprotect_messages; @@ -1307,7 +1566,7 @@ void SV_Floodprot_f(void) Cvar_SetValue(&sv_floodprotect_silencetime, atof(Cmd_Argv(3))); } -void SV_StuffToClient_f(void) +static void SV_StuffToClient_f(void) { //with this we emulate the progs 'stuffcmds' builtin client_t *cl; @@ -1399,7 +1658,7 @@ void SV_StuffToClient_f(void) Z_Free(key); } -char *ShowTime(unsigned int seconds) +static char *ShowTime(unsigned int seconds) { char buf[1024]; char *b = buf; @@ -1435,7 +1694,7 @@ char *ShowTime(unsigned int seconds) SV_Status_f ================ */ -void SV_Status_f (void) +static void SV_Status_f (void) { int i, j, l; client_t *cl; @@ -1453,6 +1712,8 @@ void SV_Status_f (void) )) columns = 40; + NET_PrintAddresses(svs.sockets); + if (!sv.state) { Con_Printf("Server is not running\n"); @@ -1468,12 +1729,12 @@ void SV_Status_f (void) avg = 1000*svs.stats.latched_active / STATFRAMES; pak = (float)svs.stats.latched_packets/ STATFRAMES; - NET_PrintAddresses(svs.sockets); - Con_Printf("cpu utilization : %3i%%\n",(int)cpu); Con_Printf("avg response time: %i ms\n",(int)avg); Con_Printf("packets/frame : %5.2f\n", pak); //not relevent as a limit. Con_Printf("server uptime : %s\n", ShowTime(realtime)); + if (sv.state == ss_clustermode) + return; Con_Printf("map uptime : %s\n", ShowTime(sv.world.physicstime)); //show the current map+name (but hide name if its too long or would be ugly) if (columns >= 80 && *sv.mapname && strlen(sv.mapname) < 45 && !strchr(sv.mapname, '\n')) @@ -1494,7 +1755,7 @@ void SV_Status_f (void) break; Con_Printf("sounds : %i/%i\n", count, MAX_SOUNDS); } - Con_Printf("gamedir : %s\n", FS_GetGamedir()); + Con_Printf("gamedir : %s\n", FS_GetGamedir(true)); if (sv.csqcdebug) Con_Printf("csqc debug : true\n"); if (sv.mvdrecording) @@ -1523,8 +1784,15 @@ void SV_Status_f (void) else Con_Printf("\n"); - if (cl->istobeloaded && cl->state == cs_zombie) - s = "LoadZombie"; + if (cl->state == cs_loadzombie) + { + if (cl->istobeloaded) + s = "LoadZombie"; + else + s = "ParmZombie"; + } + else if (cl->state == cs_zombie && cl->netchan.remote_address.type == NA_INVALID) + s = "none"; else if (cl->protocol == SCP_BAD) s = "bot"; else @@ -1535,7 +1803,7 @@ void SV_Status_f (void) Con_Printf ("CONNECTING\n"); continue; } - if (cl->state == cs_zombie) + if (cl->state == cs_zombie || cl->state == cs_loadzombie) { Con_Printf ("ZOMBIE\n"); continue; @@ -1556,8 +1824,15 @@ void SV_Status_f (void) continue; Con_Printf ("%5i %6i ", (int)cl->old_frags, cl->userid); - if (cl->istobeloaded && cl->state == cs_zombie) - s = "LoadZombie"; + if (cl->state == cs_loadzombie) + { + if (cl->istobeloaded) + s = "LoadZombie"; + else + s = "ParmZombie"; + } + else if (cl->state == cs_zombie && cl->netchan.remote_address.type == NA_INVALID) + s = "none"; else if (cl->protocol == SCP_BAD) s = "bot"; else @@ -1575,7 +1850,7 @@ void SV_Status_f (void) { Con_Printf ("CONNECTING "); } - else if (cl->state == cs_zombie) + else if (cl->state == cs_zombie || cl->state == cs_loadzombie) { Con_Printf ("ZOMBIE "); } @@ -1630,6 +1905,8 @@ void SV_ConSay_f(void) { if (client->state == cs_free) continue; + if (client->isdeaf) + continue; SV_ClientPrintf(client, PRINT_CHAT, "%s\n", text); } @@ -1646,7 +1923,7 @@ void SV_ConSay_f(void) } } -void SV_ConSayOne_f (void) +static void SV_ConSayOne_f (void) { char text[2048]; client_t *to; @@ -1684,7 +1961,7 @@ void SV_ConSayOne_f (void) SV_Heartbeat_f ================== */ -void SV_Heartbeat_f (void) +static void SV_Heartbeat_f (void) { Master_ReResolve(); svs.last_heartbeat = -9999; @@ -1693,7 +1970,7 @@ void SV_Heartbeat_f (void) #define FOREACHCLIENT(i,cl) \ for (i = sv.mvdrecording?-1:0; i < sv.allocated_client_slots; i++) \ if ((cl = (i==-1?&demo.recorder:&svs.clients[i]))) \ -if ((i == -1) || cl->state > cs_zombie) +if ((i == -1) || cl->state >= cs_connected) void SV_SendServerInfoChange(char *key, const char *value) { @@ -1817,7 +2094,7 @@ SV_Serverinfo_f Examine or change the serverinfo string =========== */ -void SV_Localinfo_f (void) +static void SV_Localinfo_f (void) { char *old; @@ -1968,6 +2245,19 @@ void SV_User_f (void) if (cl->download) Con_Printf ("download: \"%s\" %ik/%ik (%i%%)", cl->downloadfn, cl->downloadcount/1024, cl->downloadsize/1024, (cl->downloadcount*100)/cl->downloadsize); + if (cl->iscrippled) + Con_Printf("crippled\n"); + if (cl->iscuffed) + Con_Printf("cuffed\n"); + if (cl->isdeaf) + Con_Printf("deaf\n"); + if (cl->islagged) + Con_Printf("lagged\n"); + if (cl->ismuted) + Con_Printf("muted\n"); + if (cl->isvip) + Con_Printf("vip\n"); + SV_CalcNetRates(cl, &ftime, &frames, &minf, &maxf); if (frames) Con_Printf("net: %gfps (min%g max %g), c2s: %ibps, s2c: %ibps\n", ftime/frames, minf, maxf, (int)cl->inrate, (int)cl->outrate); @@ -1994,7 +2284,7 @@ SV_Gamedir Sets the fake *gamedir to a different directory. ================ */ -void SV_Gamedir (void) +static void SV_Gamedir (void) { char *dir; @@ -2029,13 +2319,13 @@ SV_Gamedir_f Sets the gamedir and path to a different directory. ================ */ -void SV_Gamedir_f (void) +static void SV_Gamedir_f (void) { char *dir; if (Cmd_Argc() == 1) { - Con_TPrintf ("Current gamedir: %s\n", FS_GetGamedir()); + Con_TPrintf ("Current gamedir: %s\n", FS_GetGamedir(true)); return; } @@ -2060,14 +2350,12 @@ void SV_Gamedir_f (void) Z_Free(dir); } - -extern char gamedirfile[MAX_OSPATH]; /* ================ SV_Snap ================ */ -void SV_Snap (int uid) +static void SV_Snap (int uid) { client_t *cl; char pcxname[80]; @@ -2095,8 +2383,6 @@ void SV_Snap (int uid) sprintf(pcxname, "%d-00.pcx", uid); strcpy(checkname, "snap"); - Sys_mkdir(gamedirfile); - Sys_mkdir(checkname); for (i=0 ; i<=99 ; i++) { @@ -2129,7 +2415,7 @@ void SV_Snap (int uid) SV_Snap_f ================ */ -void SV_Snap_f (void) +static void SV_Snap_f (void) { int uid; @@ -2149,7 +2435,7 @@ void SV_Snap_f (void) SV_Snap ================ */ -void SV_SnapAll_f (void) +static void SV_SnapAll_f (void) { client_t *cl; int i; @@ -2162,12 +2448,12 @@ void SV_SnapAll_f (void) } } -float mytimer; -float lasttimer; -int ticsleft; -float timerinterval; -int timerlevel; -cvar_t *timercommand; +static float mytimer; +static float lasttimer; +static int ticsleft; +static float timerinterval; +static int timerlevel; +static cvar_t *timercommand; void SV_CheckTimer(void) { float ctime = Sys_DoubleTime(); @@ -2192,7 +2478,7 @@ void SV_CheckTimer(void) } } -void SV_SetTimer_f(void) +static void SV_SetTimer_f(void) { int count; float interval; @@ -2232,7 +2518,7 @@ void SV_SetTimer_f(void) timerlevel = Cmd_ExecLevel; } -void SV_SendGameCommand_f(void) +static void SV_SendGameCommand_f(void) { #ifdef Q3SERVER if (SVQ3_ConsoleCommand()) @@ -2265,19 +2551,19 @@ void PIN_SaveMessages(void); void PIN_DeleteOldestMessage(void); void PIN_MakeMessage(char *from, char *msg); -void SV_Pin_Save_f(void) +static void SV_Pin_Save_f(void) { PIN_SaveMessages(); } -void SV_Pin_Reload_f(void) +static void SV_Pin_Reload_f(void) { PIN_LoadMessages(); } -void SV_Pin_Delete_f(void) +static void SV_Pin_Delete_f(void) { PIN_DeleteOldestMessage(); } -void SV_Pin_Add_f(void) +static void SV_Pin_Add_f(void) { PIN_MakeMessage(Cmd_Argv(1), Cmd_Argv(2)); } @@ -2355,25 +2641,26 @@ void SV_InitOperatorCommands (void) //various punishments Cmd_AddCommand ("kick", SV_Kick_f); Cmd_AddCommand ("clientkick", SV_KickSlot_f); + Cmd_AddCommand ("renameclient", SV_ForceName_f); Cmd_AddCommand ("mute", SV_Mute_f); Cmd_AddCommand ("cuff", SV_Cuff_f); - Cmd_AddCommand ("renameclient", SV_ForceName_f); Cmd_AddCommand ("cripple", SV_CripplePlayer_f); - Cmd_AddCommand ("banname", SV_BanName_f); - Cmd_AddCommand ("banlist", SV_BanList_f); - Cmd_AddCommand ("banip", SV_BanIP_f); Cmd_AddCommand ("ban", SV_BanClientIP_f); - Cmd_AddCommand ("unban", SV_Unban_f); -// Cmd_AddCommand ("ban", SV_BanName_f); - Cmd_AddCommand ("status", SV_Status_f); + Cmd_AddCommand ("banname", SV_BanClientIP_f); //legacy dupe-name crap + + Cmd_AddCommand ("banlist", SV_BanList_f); //shows only bans, not other penalties + Cmd_AddCommand ("unban", SV_Unfilter_f); //merely renamed. + Cmd_AddCommand ("banlist", SV_BanList_f); //shows only bans, not other penalties Cmd_AddCommand ("addip", SV_FilterIP_f); Cmd_AddCommand ("removeip", SV_Unfilter_f); - Cmd_AddCommand ("listip", SV_FilterList_f); + Cmd_AddCommand ("listip", SV_FilterList_f); //shows all penalties Cmd_AddCommand ("writeip", SV_WriteIP_f); Cmd_AddCommand ("floodprot", SV_Floodprot_f); + Cmd_AddCommand ("status", SV_Status_f); + Cmd_AddCommand ("sv", SV_SendGameCommand_f); Cmd_AddCommand ("mod", SV_SendGameCommand_f); @@ -2385,6 +2672,7 @@ void SV_InitOperatorCommands (void) Cmd_AddCommand ("gamemap", SV_Map_f); Cmd_AddCommand ("changelevel", SV_Map_f); Cmd_AddCommand ("listmaps", SV_MapList_f); + Cmd_AddCommand ("maplist", SV_MapList_f); Cmd_AddCommand ("setmaster", SV_SetMaster_f); Cmd_AddCommand ("heartbeat", SV_Heartbeat_f); diff --git a/engine/server/sv_chat.c b/engine/server/sv_chat.c index 175aba13..29105fc3 100644 --- a/engine/server/sv_chat.c +++ b/engine/server/sv_chat.c @@ -70,7 +70,7 @@ the qc code: #define SENDDELAY 1 float SV_ChatFunc(const char *func); -void Chat_GetTag(char *filename, float tag, char **text, char **condition, char **options) +void Chat_GetTag(const char *filename, float tag, char **text, char **condition, char **options) { char *file; char *s; file = COM_LoadTempFile(va("dialog/%s.dlg", filename)); @@ -372,7 +372,7 @@ void SV_EndChat(void) } -void SV_Chat(char *filename, float starttag, edict_t *edict) +void SV_Chat(const char *filename, float starttag, edict_t *edict) { int i, tag; char optiontext[1024]; diff --git a/engine/server/sv_demo.c b/engine/server/sv_demo.c index e904df1b..30cecb7c 100644 --- a/engine/server/sv_demo.c +++ b/engine/server/sv_demo.c @@ -96,7 +96,7 @@ void SV_RecordDemo_f (void) for (clnum = 0; clnum < svs.allocated_client_slots; clnum++) //clear the server so the clients reconnect and send nice fresh messages. { c = &svs.clients[clnum]; - if (c->state <= cs_zombie) + if (c->state < cs_connected) continue; ClientReliableWrite_Begin (c, svc_stufftext, 2+strlen("reconnect\n")); ClientReliableWrite_String (c, "disconnect;wait;reconnect\n"); @@ -152,7 +152,7 @@ void SV_PlayDemo_f(void) for (clnum = 0; clnum < svs.allocated_client_slots; clnum++) //clear the server so new clients don't conflict. { c = &svs.clients[clnum]; - if (c->state <= cs_zombie) + if (c->state < cs_connected) continue; ClientReliableWrite_Begin (c, svc_stufftext, 2+strlen("reconnect\n")); ClientReliableWrite_String (c, "reconnect\n"); diff --git a/engine/server/sv_ents.c b/engine/server/sv_ents.c index 8c4e3a9d..9625579c 100644 --- a/engine/server/sv_ents.c +++ b/engine/server/sv_ents.c @@ -3034,14 +3034,17 @@ void SV_Snapshot_BuildStateQ1(entity_state_t *state, edict_t *ent, client_t *cli #ifdef PEXT_SCALE - state->scale = ent->xv->scale*16; if (!ent->xv->scale) state->scale = 1*16; + else + state->scale = bound(0, ent->xv->scale*16, 255); + #endif #ifdef PEXT_TRANS - state->trans = ent->xv->alpha*255; if (!ent->xv->alpha) state->trans = 255; + else + state->trans = bound(0, ent->xv->alpha*255, 255); //QSG_DIMENSION_PLANES - if the only shared dimensions are ghost dimensions, Set half alpha. if (client && client->edict) @@ -3298,6 +3301,9 @@ void SV_Snapshot_BuildQ1(client_t *client, packet_entities_t *pack, pvscamera_t VectorMA(clent->v->origin, -0.5, org, org); dist = DotProduct(org, org); //Length +// if (dist > 1024*1024) +// continue; + // add to the packetentities if (pack->num_entities == pack->max_entities) { @@ -3475,6 +3481,12 @@ void SV_WriteEntitiesToClient (client_t *client, sizebuf_t *msg, qboolean ignore pack = &frame->entities; SV_Snapshot_Clear(pack); + if (!pack->entities) + { + Con_Printf("DON'T PANIC!\n"); + return; + } + // put other visible entities into either a packet_entities or a nails message #ifdef SERVER_DEMO_PLAYBACK diff --git a/engine/server/sv_init.c b/engine/server/sv_init.c index ceb82281..870d1709 100644 --- a/engine/server/sv_init.c +++ b/engine/server/sv_init.c @@ -42,7 +42,7 @@ extern cvar_t sv_gamespeed; extern cvar_t sv_csqcdebug; extern cvar_t sv_csqc_progname; extern cvar_t sv_calcphs; -extern cvar_t sv_playerslots; +extern cvar_t sv_playerslots, maxclients, maxspectators; /* ================ @@ -50,7 +50,7 @@ SV_ModelIndex ================ */ -int SV_ModelIndex (char *name) +int SV_ModelIndex (const char *name) { int i; @@ -264,6 +264,94 @@ void SVNQ_CreateBaseline (void) } } +void SV_SaveSpawnparmsClient(client_t *client, float *transferparms) +{ + int j; + for (j=0 ; jspawnparamglobals[j]) + *pr_global_ptrs->spawnparamglobals[j] = client->spawn_parms[j]; + } + +#ifdef VM_Q1 + if (svs.gametype == GT_Q1QVM) + { + pr_global_struct->self = EDICT_TO_PROG(svprogfuncs, client->edict); + Q1QVM_SetChangeParms(); + } +#endif + else if (pr_global_ptrs->SetChangeParms) + { + func_t setparms = 0; + if (transferparms) + { + setparms = PR_FindFunction(svprogfuncs, "SetTransferParms", PR_ANY); + if (!setparms) + setparms = pr_global_struct->SetChangeParms; + } + else + setparms = pr_global_struct->SetChangeParms; + pr_global_struct->self = EDICT_TO_PROG(svprogfuncs, client->edict); + PR_ExecuteProgram (svprogfuncs, setparms); + } + + if (transferparms) + { + for (j=0 ; jspawnparamglobals[j]) + transferparms[j] = *pr_global_ptrs->spawnparamglobals[j]; + } + return; + } + else + { + for (j=0 ; jspawnparamglobals[j]) + client->spawn_parms[j] = *pr_global_ptrs->spawnparamglobals[j]; + } + } + + // call the progs to get default spawn parms for the new client + if (PR_FindGlobal(svprogfuncs, "ClientReEnter", 0, NULL)) + {//oooh, evil. + char buffer[65536*4]; + int bufsize = 0; + char *buf; + for (j=0 ; jspawn_parms[j] = 0; + + buf = svprogfuncs->saveent(svprogfuncs, buffer, &bufsize, sizeof(buffer), client->edict); + + if (client->spawninfo) + Z_Free(client->spawninfo); + client->spawninfo = Z_Malloc(bufsize+1); + memcpy(client->spawninfo, buf, bufsize+1); + client->spawninfotime = sv.time; + } + +#ifdef SVRANKING + if (client->rankid) + { + rankstats_t rs; + if (Rank_GetPlayerStats(client->rankid, &rs)) + { + rs.timeonserver += realtime - client->stats_started; + client->stats_started = realtime; + rs.kills += client->kills; + rs.deaths += client->deaths; + client->kills=0; + client->deaths=0; + for (j=0 ; jspawn_parms[j]; + } + Rank_SetPlayerStats(client->rankid, &rs); + } + } +#endif +} /* ================ @@ -274,9 +362,9 @@ and each client for saving across the transition to another level ================ */ -void SV_SaveSpawnparms (qboolean dontsave) +void SV_SaveSpawnparms (void) { - int i, j; + int i; if (!sv.state) return; // no progs loaded yet @@ -292,77 +380,7 @@ void SV_SaveSpawnparms (qboolean dontsave) if (host_client->state != cs_spawned) continue; - if (dontsave) //level restart requires that stats can be reset - continue; - -#ifdef VM_Q1 - if (svs.gametype == GT_Q1QVM) - { - pr_global_struct->self = EDICT_TO_PROG(svprogfuncs, host_client->edict); - Q1QVM_SetChangeParms(); - for (j=0 ; jspawnparamglobals[j]) - host_client->spawn_parms[j] = *pr_global_ptrs->spawnparamglobals[j]; - else - host_client->spawn_parms[j] = 0; - } - } -#endif - else if (pr_global_ptrs->SetChangeParms) - { - pr_global_struct->self = EDICT_TO_PROG(svprogfuncs, host_client->edict); - PR_ExecuteProgram (svprogfuncs, pr_global_struct->SetChangeParms); - for (j=0 ; jspawnparamglobals[j]) - host_client->spawn_parms[j] = *pr_global_ptrs->spawnparamglobals[j]; - else - host_client->spawn_parms[j] = 0; - } - } - - // call the progs to get default spawn parms for the new client - if (PR_FindGlobal(svprogfuncs, "ClientReEnter", 0, NULL)) - {//oooh, evil. - char buffer[65536*4]; - int bufsize = 0; - char *buf; - for (j=0 ; jspawn_parms[j] = 0; - - buf = svprogfuncs->saveent(svprogfuncs, buffer, &bufsize, sizeof(buffer), host_client->edict); - - if (host_client->spawninfo) - Z_Free(host_client->spawninfo); - host_client->spawninfo = Z_Malloc(bufsize+1); - memcpy(host_client->spawninfo, buf, bufsize+1); - host_client->spawninfotime = sv.time; - } - -#ifdef SVRANKING - if (host_client->rankid) - { - rankstats_t rs; - if (Rank_GetPlayerStats(host_client->rankid, &rs)) - { - rs.timeonserver += realtime - host_client->stats_started; - host_client->stats_started = realtime; - rs.kills += host_client->kills; - rs.deaths += host_client->deaths; - host_client->kills=0; - host_client->deaths=0; - for (j=0 ; jspawnparamglobals[j]) - rs.parm[j] = *pr_global_ptrs->spawnparamglobals[j]; - else - rs.parm[j] = 0; - } - Rank_SetPlayerStats(host_client->rankid, &rs); - } - } -#endif + SV_SaveSpawnparmsClient(host_client, NULL); } } @@ -537,11 +555,17 @@ void SV_UnspawnServer (void) //terminate the running server. SV_DropClient(&svs.clients[i]); } PR_Deinit(); +#ifdef Q3SERVER + SVQ3_ShutdownGame(); +#endif #ifdef Q2SERVER SVQ2_ShutdownGameProgs(); #endif #ifdef HLSERVER SVHL_ShutdownGame(); +#endif +#ifdef VM_Q1 + Q1QVM_Shutdown(); #endif sv.world.worldmodel = NULL; sv.state = ss_dead; @@ -675,6 +699,31 @@ static void SV_SetupNetworkBuffers(qboolean bigcoords) sv.num_signon_buffers = 1; } +#ifdef SUBSERVERS +void SV_SpawnClusterMode(void) +{ + char *sqlparams[] = + { + "", + "", + "", + "login", + }; + if (sv.state) + SV_UnspawnServer(); + NET_InitServer(); + + //child processes return 0 and fall through + memset(&sv, 0, sizeof(sv)); + sv.state = ss_clustermode; + sv.logindatabase = -1;//SQL_NewServer("sqlite", sqlparams); + + //and for legacy clients, we need some server stuff inited. + SV_SetupNetworkBuffers(false); + SV_UpdateMaxPlayers(32); +} +#endif + /* ================ SV_SpawnServer @@ -795,6 +844,7 @@ void SV_SpawnServer (char *server, char *startspot, qboolean noents, qboolean us // wipe the entire per-level structure memset (&sv, 0, sizeof(sv)); + sv.logindatabase = -1; SV_SetupNetworkBuffers(sv_bigcoords.ival); @@ -834,7 +884,7 @@ void SV_SpawnServer (char *server, char *startspot, qboolean noents, qboolean us } #ifndef SERVERONLY //This fixes a bug where the server advertises cheats, the internal client connects, and doesn't think cheats are allowed. - //this applies to a few other things too, but cheats is the only special one (because of the *) + //this applies to anything that can affect the content that is loaded by the server, but cheats is the only special one (because of the *) Q_strncpyz(cl.serverinfo, svs.info, sizeof(cl.serverinfo)); if (!isDedicated) CL_CheckServerInfo(); @@ -868,7 +918,7 @@ void SV_SpawnServer (char *server, char *startspot, qboolean noents, qboolean us else if (COM_FCheckExists(va(exts[2], server))) Q_snprintfz (sv.modelname, sizeof(sv.modelname), exts[2], server); } - sv.world.worldmodel = Mod_ForName (sv.modelname, true); + sv.world.worldmodel = Mod_ForName (sv.modelname, MLV_ERROR); } if (!sv.world.worldmodel || sv.world.worldmodel->needload) Sys_Error("\"%s\" is missing or corrupt\n", sv.modelname); @@ -981,6 +1031,11 @@ void SV_SpawnServer (char *server, char *startspot, qboolean noents, qboolean us newgametype = GT_QUAKE2; //we loaded the dll else #endif +#ifdef VM_LUA + if (PR_LoadLua()) + newgametype = GT_LUA; + else +#endif #ifdef VM_Q1 if (PR_LoadQ1QVM()) newgametype = GT_Q1QVM; @@ -1027,7 +1082,7 @@ void SV_SpawnServer (char *server, char *startspot, qboolean noents, qboolean us for (i=1 ; inumsubmodels ; i++) { sv.strings.model_precache[1+i] = localmodels[i]; - sv.models[i+1] = Mod_ForName (localmodels[i], false); + sv.models[i+1] = Mod_ForName (localmodels[i], MLV_WARN); } //check player/eyes models for hacks @@ -1036,7 +1091,11 @@ void SV_SpawnServer (char *server, char *startspot, qboolean noents, qboolean us } else #endif - if (svs.gametype == GT_PROGS) + if (svs.gametype == GT_PROGS +#ifdef VM_LUA + || svs.gametype == GT_LUA +#endif + ) { strcpy(sv.strings.sound_precache[0], ""); sv.strings.model_precache[0] = ""; @@ -1045,7 +1104,7 @@ void SV_SpawnServer (char *server, char *startspot, qboolean noents, qboolean us for (i=1 ; inumsubmodels ; i++) { sv.strings.model_precache[1+i] = PR_AddString(svprogfuncs, localmodels[i], 0, false); - sv.models[i+1] = Mod_ForName (localmodels[i], false); + sv.models[i+1] = Mod_ForName (localmodels[i], MLV_WARN); } //check player/eyes models for hacks @@ -1075,7 +1134,7 @@ void SV_SpawnServer (char *server, char *startspot, qboolean noents, qboolean us for (i=1; inumsubmodels; i++) { strcpy(sv.strings.configstring[Q2CS_MODELS+1+i], localmodels[i]); - sv.models[i+1] = Mod_ForName (localmodels[i], false); + sv.models[i+1] = Mod_ForName (localmodels[i], MLV_WARN); } } #endif @@ -1101,6 +1160,9 @@ void SV_SpawnServer (char *server, char *startspot, qboolean noents, qboolean us default: SV_Error("bad gametype"); break; +#ifdef VM_LUA + case GT_LUA: +#endif case GT_Q1QVM: case GT_PROGS: ent = EDICT_NUM(svprogfuncs, 0); @@ -1118,11 +1180,15 @@ void SV_SpawnServer (char *server, char *startspot, qboolean noents, qboolean us i = sv_playerslots.ival; else { - /*only make one slot for single-player*/ - if (!isDedicated && !deathmatch.value && !coop.value) + /*only make one slot for single-player (ktx sucks)*/ + if (!isDedicated && !deathmatch.value && !coop.value && svs.gametype != GT_Q1QVM) i = 1; else - i = QWMAX_CLIENTS; + { + i = maxclients.ival + maxspectators.ival; + if (i < QWMAX_CLIENTS) + i = QWMAX_CLIENTS; + } } SV_UpdateMaxPlayers(i); @@ -1216,7 +1282,7 @@ void SV_SpawnServer (char *server, char *startspot, qboolean noents, qboolean us #ifdef VM_Q1 if (svs.gametype != GT_Q1QVM) //we cannot do this with qvm #endif - ent->v->model = PR_NewString(svprogfuncs, sv.world.worldmodel->name, 0); + svprogfuncs->SetStringField(svprogfuncs, ent, &ent->v->model, sv.world.worldmodel->name, true); ent->v->modelindex = 1; // world model ent->v->solid = SOLID_BSP; ent->v->movetype = MOVETYPE_PUSH; @@ -1227,8 +1293,8 @@ void SV_SpawnServer (char *server, char *startspot, qboolean noents, qboolean us if (svs.gametype != GT_Q1QVM) //we cannot do this with qvm #endif { - ent->v->targetname = PR_NewString(svprogfuncs, "mvdsv", 0); - ent->v->netname = PR_NewString(svprogfuncs, version_string(), 0); + svprogfuncs->SetStringField(svprogfuncs, ent, &ent->v->targetname, "mvdsv", true); + svprogfuncs->SetStringField(svprogfuncs, ent, &ent->v->netname, version_string(), false); } ent->v->impulse = 0;//QWE_VERNUM; ent->v->items = 103; @@ -1238,7 +1304,7 @@ void SV_SpawnServer (char *server, char *startspot, qboolean noents, qboolean us #ifdef VM_Q1 if (svs.gametype != GT_Q1QVM) //we cannot do this with qvm #endif - pr_global_struct->mapname = PR_NewString(svprogfuncs, sv.name, 0); + svprogfuncs->SetStringField(svprogfuncs, NULL, &pr_global_struct->mapname, sv.name, true); // serverflags are for cross level information (sigils) pr_global_struct->serverflags = svs.serverflags; @@ -1357,10 +1423,10 @@ void SV_SpawnServer (char *server, char *startspot, qboolean noents, qboolean us switch(svs.gametype) { default: - break; - case GT_Q1QVM: - case GT_PROGS: - sv.world.edict_size = PR_LoadEnts(svprogfuncs, file, spawnflagmask); + if (svprogfuncs) + sv.world.edict_size = PR_LoadEnts(svprogfuncs, file, spawnflagmask); + else + sv.world.edict_size = 0; break; #ifdef Q2SERVER case GT_QUAKE2: @@ -1534,9 +1600,11 @@ void SV_SpawnServer (char *server, char *startspot, qboolean noents, qboolean us sv_player->xv->clientcolors = atoi(Info_ValueForKey(host_client->userinfo, "topcolor"))*16 + atoi(Info_ValueForKey(host_client->userinfo, "bottomcolor")); // call the spawn function + sv.skipbprintclient = host_client; pr_global_struct->time = sv.world.physicstime; pr_global_struct->self = EDICT_TO_PROG(svprogfuncs, sv_player); PR_ExecuteProgram (svprogfuncs, pr_global_struct->ClientConnect); + sv.skipbprintclient = NULL; // actually spawn the player pr_global_struct->time = sv.world.physicstime; @@ -1564,6 +1632,8 @@ void SV_SpawnServer (char *server, char *startspot, qboolean noents, qboolean us SV_MVD_SendInitialGamestate(NULL); + + SSV_UpdateAddresses(); } #endif diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index 66d52a31..4ac970d4 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -82,14 +82,14 @@ client_t *host_client; // current client // bound the size of the physics time tic #ifdef SERVERONLY -cvar_t sv_mintic = SCVAR("sv_mintic","0.03"); +cvar_t sv_mintic = CVARD("sv_mintic","0.013", "The minimum interval between running physics frames."); #else -cvar_t sv_mintic = SCVAR("sv_mintic","0"); //client builds can think as often as they want. +cvar_t sv_mintic = CVARD("sv_mintic","0", "The minimum interval between running physics frames."); //client builds can think as often as they want. #endif -cvar_t sv_maxtic = SCVAR("sv_maxtic","0.1");//never run a tick slower than this -cvar_t sv_limittics = SCVAR("sv_limittics","3");// +cvar_t sv_maxtic = CVARD("sv_maxtic","0.1", "The maximum interval between running physics frames. If the value is too low, multiple physics interations might be run at a time (based upon sv_limittics). Set identical to sv_mintic for fixed-interval ticks, which may be required if ODE is used.");//never run a tick slower than this +cvar_t sv_limittics = CVARD("sv_limittics","3", "The maximum number of ticks that may be run within a frame, to allow the server to catch up if it stalled or if sv_maxtic is too low.");// -cvar_t sv_nailhack = SCVAR("sv_nailhack","0"); +cvar_t sv_nailhack = CVARD("sv_nailhack","0", "If set to 1, disables the nail entity networking optimisation. This hack was popularised by qizmo which recommends it for better compression. Also allows clients to interplate nail positions and add trails."); cvar_t sv_nopvs = CVARD("sv_nopvs", "0", "Set to 1 to ignore pvs on the server. This can make wallhacks more dangerous, so should only be used for debugging."); @@ -124,6 +124,7 @@ cvar_t allow_download_wads = CVAR("allow_download_wads", "1"); cvar_t allow_download_configs = CVAR("allow_download_configs", "0"); cvar_t allow_download_copyrighted = CVAR("allow_download_copyrighted", "0"); +cvar_t sv_serverip = CVARD("sv_serverip", "", "Set this cvar to the server's public ip address if the server is behind a firewall and cannot detect its own public address. Providing a port is required if the firewall/nat remaps it, but is otherwise optional."); cvar_t sv_public = CVAR("sv_public", "0"); cvar_t sv_listen_qw = CVARAF("sv_listen_qw", "1", "sv_listen", 0); cvar_t sv_listen_nq = CVARD("sv_listen_nq", "2", "Allow new (net)quake clients to connect to the server.\n0 = don't let them in.\n1 = allow them in (WARNING: this allows 'qsmurf' DOS attacks).\n2 = accept (net)quake clients by emulating a challenge (as secure as QW/Q2 but does not fully conform to the NQ protocol)."); @@ -151,7 +152,7 @@ cvar_t sv_masterport = CVAR("sv_masterport", "0"); cvar_t pext_ezquake_nochunks = CVARD("pext_ezquake_nochunks", "1", "Prevents ezquake clients from being able to use the chunked download extension. This sidesteps numerous ezquake issues, and will make downloads slower but more robust."); -cvar_t sv_gamespeed = CVAR("sv_gamespeed", "1"); +cvar_t sv_gamespeed = CVARAF("sv_gamespeed", "1", "slowmo", 0); cvar_t sv_csqcdebug = CVAR("sv_csqcdebug", "0"); cvar_t sv_csqc_progname = CVAR("sv_csqc_progname", "csprogs.dat"); cvar_t pausable = CVAR("pausable", "1"); @@ -166,7 +167,7 @@ cvar_t timelimit = CVARF("timelimit", "" , CVAR_SERVERINFO); cvar_t teamplay = CVARF("teamplay", "" , CVAR_SERVERINFO); cvar_t samelevel = CVARF("samelevel", "" , CVAR_SERVERINFO); cvar_t sv_playerslots = CVARAD("sv_playerslots", "", - "maxplayers", "Specify maximum number of player/spectator/bot slots, new value takes effect on the next map (this may result in players getting kicked). This should generally be maxclients+maxspectators. Leave blank for a default value.\nMaximum value of "STRINGIFY(MAX_CLIENTS)". Values above 16 will result in issues with vanilla NQ clients. Effective values other than 32 will result in issues with vanilla QW clients."); + "maxplayers", "Specify maximum number of player/spectator/bot slots, new value takes effect on the next map (this may result in players getting kicked). This should generally be set to maxclients+maxspectators. Leave blank for a default value.\nMaximum value of "STRINGIFY(MAX_CLIENTS)". Values above 16 will result in issues with vanilla NQ clients. Effective values other than 32 will result in issues with vanilla QW clients."); cvar_t maxclients = CVARAFD("maxclients", "8", "sv_maxclients", CVAR_SERVERINFO, "Specify the maximum number of players allowed on the server at once. Can be changed mid-map."); cvar_t maxspectators = CVARFD("maxspectators", "8", CVAR_SERVERINFO, "Specify the maximum number of spectators allowed on the server at once. Can be changed mid-map."); @@ -212,11 +213,14 @@ void SV_AcceptClient (netadr_t *adr, int userid, char *userinfo); void Master_Shutdown (void); void PRH2_SetPlayerClass(client_t *cl, int classnum, qboolean fromqc); char *SV_BannedReason (netadr_t *a); +void SV_EvaluatePenalties(client_t *cl); #ifdef SQL void PR_SQLCycle(); #endif +int nextuserid; + //============================================================================ qboolean ServerPaused(void) @@ -422,10 +426,10 @@ void SV_DropClient (client_t *drop) return; } - if (!drop->controller) + if (!drop->controller && drop->netchan.remote_address.type != NA_LOOPBACK) { // add the disconnect - if (drop->state != cs_zombie) + if (drop->state < cs_connected) { switch (drop->protocol) { @@ -448,13 +452,13 @@ void SV_DropClient (client_t *drop) } } -#ifdef SVRANKING if (drop->state == cs_spawned) { - int j; - rankstats_t rs; +#ifdef SVRANKING if (drop->rankid) { + int j; + rankstats_t rs; if (Rank_GetPlayerStats(drop->rankid, &rs)) { rs.timeonserver += realtime - drop->stats_started; @@ -480,8 +484,11 @@ void SV_DropClient (client_t *drop) Rank_SetPlayerStats(drop->rankid, &rs); } } - } #endif +#ifdef SUBSERVERS + SSV_SavePlayerStats(drop, false); +#endif + } #ifdef SVCHAT SV_WipeChat(drop); #endif @@ -493,7 +500,7 @@ void SV_DropClient (client_t *drop) case GT_PROGS: if (svprogfuncs) { - if (drop->state == cs_spawned && host_initialized) + if ((drop->state == cs_spawned || drop->istobeloaded) && host_initialized) { #ifdef VM_Q1 if (svs.gametype == GT_Q1QVM) @@ -527,7 +534,7 @@ void SV_DropClient (client_t *drop) ED_Clear(svprogfuncs, drop->edict); } - if (svprogfuncs && drop->edict) + if (svprogfuncs && drop->edict && drop->edict->v) drop->edict->v->frags = 0; drop->edict = NULL; @@ -586,14 +593,18 @@ void SV_DropClient (client_t *drop) #pragma warningmsg("This means that we may not see the reason we kicked ourselves.") #endif drop->state = cs_free; //don't do zombie stuff - CL_Disconnect(); + CL_BeginServerReconnect(); } else #endif + if (drop->state == cs_spawned || drop->istobeloaded) { + drop->istobeloaded = false; drop->state = cs_zombie; // become free in a few seconds drop->connection_started = realtime; // for zombie timeout } + else + drop->state = cs_free; //skip zombie state if qc couldn't access it anyway. drop->istobeloaded = false; drop->old_frags = 0; @@ -637,7 +648,10 @@ void SV_DropClient (client_t *drop) termmsg.data = termbuf; termmsg.maxsize = sizeof(termbuf); termmsg.cursize = 0; - if (ISQWCLIENT(drop) || ISNQCLIENT(drop)) + if (drop->netchan.remote_address.type == NA_LOOPBACK) + { + } + else if (ISQWCLIENT(drop) || ISNQCLIENT(drop)) { MSG_WriteByte(&termmsg, svc_disconnect); } @@ -1106,7 +1120,7 @@ void SVC_GetInfo (char *challenge, int fullstatus) int numclients = 0; int i; char *resp; - char *gamestatus; + const char *gamestatus; eval_t *v; if (!sv_listen_nq.ival && !sv_listen_dp.ival) @@ -1499,6 +1513,7 @@ void SVC_GetChallenge (void) void SV_GetNewSpawnParms(client_t *cl) { int i; + if (svprogfuncs) //q2 dlls don't use parms in this mannor. It's all internal to the dll. { // call the progs to get default spawn parms for the new client @@ -1757,7 +1772,7 @@ void SV_ClientProtocolExtensionsChanged(client_t *client) else if (client->protocol == SCP_FITZ666) { client->max_net_clients = NQMAX_CLIENTS; - client->max_net_ents = bound(512, pr_maxedicts.ival, 32768); //fitzquake supports 65535, but our writeentity builtin works differently. + client->max_net_ents = bound(512, pr_maxedicts.ival, 32768); //fitzquake supports 65535, but our writeentity builtin works differently, which causes problems. client->maxmodels = 1024; maxpacketentities = 512; @@ -1843,6 +1858,799 @@ void SV_ClientProtocolExtensionsChanged(client_t *client) } } +#ifdef SUBSERVERS +void MSV_UpdatePlayerStats(qboolean initial, unsigned int playerid, unsigned int serverid, int numstats, float *stats); + +static char *knownmaps[] = +{ + "", + "start" +}; + +static pubsubserver_t *subservers; +qboolean isClusterSlave; +unsigned int nextserverid; + +pubsubserver_t *MSV_StartSubServer(unsigned int id, const char *mapname) +{ + sizebuf_t send; + char send_buf[64]; + pubsubserver_t *s = Sys_ForkServer(); + if (s) + { + if (!id) + { + if (nextserverid < sizeof(knownmaps)/sizeof(knownmaps[0])) + nextserverid = sizeof(knownmaps)/sizeof(knownmaps[0]); + id = nextserverid++; + } + s->id = id; + s->next = subservers; + subservers = s; + + Q_strncpyz(s->name, mapname, sizeof(s->name)); + + memset(&send, 0, sizeof(send)); + send.data = send_buf; + send.maxsize = sizeof(send_buf); + send.cursize = 2; + MSG_WriteByte(&send, ccmd_acceptserver); + MSG_WriteLong(&send, s->id); + MSG_WriteString(&send, s->name); + Sys_InstructSlave(s, &send); + } + return s; +} + +pubsubserver_t *MSV_FindSubServer(unsigned int id) +{ + pubsubserver_t *s; + for (s = subservers; s; s = s->next) + { + if (id == s->id) + return s; + } + + if (!s && id >= 1 && id < sizeof(knownmaps)/sizeof(knownmaps[0])) + s = MSV_StartSubServer(id, knownmaps[id]); + + return s; +} +pubsubserver_t *MSV_FindSubServerName(const char *mapname) +{ + pubsubserver_t *s; + unsigned int to; + for (to = 1; to < sizeof(knownmaps)/sizeof(knownmaps[0]); to++) + { + if (!strcmp(knownmaps[to], mapname)) + return MSV_FindSubServer(to); + } + + for (s = subservers; s; s = s->next) + { + if (!strcmp(s->name, mapname)) + return s; + } + + return MSV_StartSubServer(0, mapname); +} +qboolean MSV_AddressForMap(netadr_t *ret, int natype, int serverid) +{ + pubsubserver_t *s = MSV_FindSubServer(serverid); + + if (s) + { + if (natype == s->addrv6.type) + *ret = s->addrv6; + else + *ret = s->addrv4; + return true; + } + return false; +} + +void MSV_InstructSlave(unsigned int id, sizebuf_t *cmd) +{ + pubsubserver_t *s; + if (!id) + { + for (s = subservers; s; s = s->next) + Sys_InstructSlave(s, cmd); + } + else + { + s = MSV_FindSubServer(id); + if (s) + Sys_InstructSlave(s, cmd); + } +} + +void MSV_MapCluster_f(void) +{ + SV_SpawnClusterMode(); +} + +void SSV_PrintToMaster(char *s) +{ + sizebuf_t send; + char send_buf[8192]; + memset(&send, 0, sizeof(send)); + send.data = send_buf; + send.maxsize = sizeof(send_buf); + send.cursize = 2; + + MSG_WriteByte(&send, ccmd_print); + MSG_WriteString(&send, s); + SSV_InstructMaster(&send); +} + +void MSV_SubServerCommand_f(void) +{ + sizebuf_t buf; + char bufmem[1024]; + pubsubserver_t *s; + int id; + char *c; + if (Cmd_Argc() == 1) + { + Con_Printf("Active servers on this cluster:\n"); + for (s = subservers; s; s = s->next) + { + Con_Printf("%i: %s", s->id, s->name); + if (s->addrv4.type != NA_INVALID) + Con_Printf(" %s", NET_AdrToString(bufmem, sizeof(bufmem), &s->addrv4)); + if (s->addrv6.type != NA_INVALID) + Con_Printf(" %s", NET_AdrToString(bufmem, sizeof(bufmem), &s->addrv6)); + Con_Printf("\n"); + } + return; + } + if (!strcmp(Cmd_Argv(0), "ssv_all")) + id = 0; + else + { + id = atoi(Cmd_Argv(1)); + Cmd_ShiftArgs(1, false); + } + + buf.data = bufmem; + buf.maxsize = sizeof(bufmem); + buf.cursize = 2; + buf.packing = SZ_RAWBYTES; + c = Cmd_Args(); + MSG_WriteByte(&buf, ccmd_stuffcmd); + MSG_WriteString(&buf, c); + buf.data[0] = buf.cursize & 0xff; + buf.data[1] = (buf.cursize>>8) & 0xff; + MSV_InstructSlave(id, &buf); +} + +void MSV_ReadFromSubServer(pubsubserver_t *s) +{ + sizebuf_t send; + qbyte send_buf[MAX_QWMSGLEN]; + netadr_t adr; + char *str; + int c; + + c = MSG_ReadByte(); + switch(c) + { + default: + case ccmd_bad: + Sys_Error("Corrupt message (%i) from SubServer %i:%s", c, s->id, s->name); + break; + case ccmd_print: + Con_Printf("%s", MSG_ReadString()); + break; + case ccmd_saveplayer: + { + float stats[NUM_SPAWN_PARMS]; + int i, numstats; + unsigned int lastserver = MSG_ReadLong(); + int plid = MSG_ReadLong(); + numstats = MSG_ReadByte(); + for (i = 0; i < numstats; i++) + stats[i] = MSG_ReadFloat(); + MSV_UpdatePlayerStats(lastserver, plid, s->id, numstats, stats); + } + break; + case ccmd_transferplayer: + { + char guid[64]; + char mapname[64]; + int plid = MSG_ReadLong(); + char *newmap = MSG_ReadStringBuffer(mapname, sizeof(mapname)); + char *claddr = MSG_ReadString(); + char *clguid = MSG_ReadStringBuffer(guid, sizeof(guid)); + pubsubserver_t *toptr; + + memset(&send, 0, sizeof(send)); + send.data = send_buf; + send.maxsize = sizeof(send_buf); + send.cursize = 2; + + if (NULL!=(toptr=MSV_FindSubServerName(newmap))) + { + Con_Printf("Transfer to %i:%s\n", toptr->id, toptr->name); + + MSG_WriteByte(&send, ccmd_takeplayer); + MSG_WriteLong(&send, plid); + MSG_WriteLong(&send, s->id); + MSG_WriteString(&send, claddr); + MSG_WriteString(&send, clguid); + + c = MSG_ReadByte(); + MSG_WriteByte(&send, c); + Con_Printf("Transfer %i stats\n", c); + while(c--) + MSG_WriteFloat(&send, MSG_ReadFloat()); + + Sys_InstructSlave(toptr, &send); + } + else + { + //suck up the stats + c = MSG_ReadByte(); + while(c--) + MSG_ReadFloat(); + + Con_Printf("Transfer abort\n"); + + MSG_WriteByte(&send, ccmd_tookplayer); + MSG_WriteLong(&send, s->id); + MSG_WriteLong(&send, plid); + MSG_WriteString(&send, ""); + + Sys_InstructSlave(s, &send); + } + } + break; + case ccmd_tookplayer: + { + int to = MSG_ReadLong(); + int plid = MSG_ReadLong(); + char *claddr = MSG_ReadString(); + char *rmsg; + netadr_t cladr; + netadr_t svadr; + char adrbuf[256]; + Con_Printf("Took player\n"); + + memset(&send, 0, sizeof(send)); + send.data = send_buf; + send.maxsize = sizeof(send_buf); + send.cursize = 2; + + NET_StringToAdr(claddr, 0, &cladr); + MSV_AddressForMap(&svadr, cladr.type, s->id); + if (!to) + { + if (svadr.type != NA_INVALID) + { + rmsg = va("fredir\n%s", NET_AdrToString(adrbuf, sizeof(adrbuf), &svadr)); + Netchan_OutOfBand (NS_SERVER, &cladr, strlen(rmsg), (qbyte *)rmsg); + } + } + else + { + MSG_WriteByte(&send, ccmd_tookplayer); + MSG_WriteLong(&send, s->id); + MSG_WriteLong(&send, plid); + MSG_WriteString(&send, NET_AdrToString(adrbuf, sizeof(adrbuf), &svadr)); + + MSV_InstructSlave(to, &send); + } + } + break; + case ccmd_serveraddress: + s->addrv4.type = NA_INVALID; + s->addrv6.type = NA_INVALID; + str = MSG_ReadString(); + Q_strncpyz(s->name, str, sizeof(s->name)); + for (;;) + { + str = MSG_ReadString(); + if (!*str) + break; + if (NET_StringToAdr(str, 0, &adr)) + { + if (adr.type == NA_IP && s->addrv4.type == NA_INVALID) + s->addrv4 = adr; + if (adr.type == NA_IPV6 && s->addrv6.type == NA_INVALID) + s->addrv6 = adr; + } + } + Con_Printf("%i:%s: restarted\n", s->id, s->name); + break; + } + if (msg_readcount != net_message.cursize || msg_badread) + Sys_Error("Master: Readcount isn't right (%i)\n", net_message.data[0]); +} + +void MSV_PollSlaves(void) +{ + pubsubserver_t **link, *s; + for (link = &subservers; (s=*link); ) + { + switch(Sys_SubServerRead(s)) + { + case -1: + //error - server is dead and needs to be freed. + *link = s->next; + Z_Free(s); + break; + case 0: + //no messages + link = &s->next; + break; + case 1: + //got a message. read it and see if there's more. + MSV_ReadFromSubServer(s); + break; + } + } +} + +void SSV_ReadFromControlServer(void) +{ + int c; + char *s; + + c = MSG_ReadByte(); + switch(c) + { + case ccmd_bad: + default: + SV_Error("Invalid message from cluster (%i)\n", c); + break; + case ccmd_stuffcmd: + s = MSG_ReadString(); + SV_BeginRedirect(RD_MASTER, 0); + Cmd_ExecuteString(s, RESTRICT_LOCAL); + SV_EndRedirect(); + break; + + case ccmd_acceptserver: + svs.clusterserverid = MSG_ReadLong(); + s = MSG_ReadString(); + if (*s && !strchr(s, ';') && !strchr(s, '\n') && !strchr(s, '\"')) //sanity check the argument + Cmd_ExecuteString(va("map \"%s\"", s), RESTRICT_LOCAL); + if (svprogfuncs && pr_global_ptrs->serverid) + *pr_global_ptrs->serverid = svs.clusterserverid; + break; + + case ccmd_tookplayer: + { + client_t *cl = NULL; + int to = MSG_ReadLong(); + int plid = MSG_ReadLong(); + char *addr = MSG_ReadString(); + int i; + + Con_Printf("%s: got tookplayer\n", sv.name); + + for (i = 0; i < svs.allocated_client_slots; i++) + { + if (svs.clients[i].state && svs.clients[i].userid == plid) + { + cl = &svs.clients[i]; + break; + } + } + if (cl) + { + if (!*addr) + { + Con_Printf("%s: tookplayer: failed\n", sv.name); + Info_SetValueForStarKey(cl->userinfo, "*transfer", "", sizeof(cl->userinfo)); + } + else + { + Con_Printf("%s: tookplayer: do transfer\n", sv.name); +// SV_StuffcmdToClient(cl, va("connect \"%s\"\n", addr)); + SV_StuffcmdToClient(cl, va("cl_transfer \"%s\"\n", addr)); + cl->redirect = 2; + } + } + else + Con_Printf("%s: tookplayer: invalid player.\n", sv.name); + } + break; + + case ccmd_transferedplayer: + { + client_t *cl; + char *to; + int toserver = MSG_ReadLong(); + int playerid = MSG_ReadLong(); + int i; + + for (i = 0; i < svs.allocated_client_slots; i++) + { + if (svs.clients[i].userid == playerid && svs.clients[i].state >= cs_loadzombie) + { + cl = &svs.clients[i]; + cl->drop = true; + to = Info_ValueForKey(cl->userinfo, "*transfer"); + Con_Printf("%s transfered to %s\n", cl->name, to); + break; + } + } + } + break; + + case ccmd_takeplayer: + { + client_t *cl = NULL; + int i, j; + float stat; + char guid[64]; + int plid = MSG_ReadLong(); + int fromsv = MSG_ReadLong(); + char *claddr = MSG_ReadString(); + char *clguid = MSG_ReadStringBuffer(guid, sizeof(guid)); + + if (sv.state >= ss_active) + { + for (i = 0; i < svs.allocated_client_slots; i++) + { + if (!svs.clients[i].state || (svs.clients[i].userid == plid && svs.clients[i].state >= cs_loadzombie)) + { + cl = &svs.clients[i]; + break; + } + } + } + + Con_Printf("%s: takeplayer\n", sv.name); + if (cl) + { + cl->userid = plid; + if (cl->state == cs_loadzombie && cl->istobeloaded) + cl->connection_started = realtime+20; //renew the slot + else if (!cl->state) + { //allocate a new pending player. + cl->previousserver = fromsv; + cl->state = cs_loadzombie; + cl->connection_started = realtime+20; + Q_strncpyz(cl->guid, clguid, sizeof(cl->guid)); + sv.spawned_client_slots++; + memset(&cl->netchan, 0, sizeof(cl->netchan)); + SV_GetNewSpawnParms(cl); + } + } + + j = MSG_ReadByte(); + Con_Printf("%s: %i stats\n", sv.name, j); + for (i = 0; i < j; i++) + { + stat = MSG_ReadFloat(); + if (cl && cl->state == cs_loadzombie && i < NUM_SPAWN_PARMS) + cl->spawn_parms[i] = stat; + } + + { + sizebuf_t send; + qbyte send_buf[MAX_QWMSGLEN]; + + if (fromsv) + Con_Printf("%s: send tookplayer\n", sv.name); + else + Con_Printf("%s: from master\n", sv.name); + memset(&send, 0, sizeof(send)); + send.data = send_buf; + send.maxsize = sizeof(send_buf); + send.cursize = 2; + + if (cl) + { + MSG_WriteByte(&send, ccmd_tookplayer); + MSG_WriteLong(&send, fromsv); + MSG_WriteLong(&send, plid); + MSG_WriteString(&send, claddr); + SSV_InstructMaster(&send); + } + } + } + break; + } + + if (msg_readcount != net_message.cursize || msg_badread) + Sys_Error("Subserver: Readcount isn't right (%i)\n", net_message.data[0]); +} + +void SSV_UpdateAddresses(void) +{ + char buf[256]; + netadr_t addr[64]; + struct ftenet_generic_connection_s *con[sizeof(addr)/sizeof(addr[0])]; + int flags[sizeof(addr)/sizeof(addr[0])]; + int count; + sizebuf_t send; + qbyte send_buf[MAX_QWMSGLEN]; + int i; + + if (!SSV_IsSubServer()) + return; + + count = NET_EnumerateAddresses(svs.sockets, con, flags, addr, sizeof(addr)/sizeof(addr[0])); + + if (*sv_serverip.string) + { + for (i = 0; i < count; i++) + { + if (addr[i].type == NA_IP) + { + NET_StringToAdr(sv_serverip.string, BigShort(addr[i].port), &addr[0]); + count = 1; + break; + } + } + } + + memset(&send, 0, sizeof(send)); + send.data = send_buf; + send.maxsize = sizeof(send_buf); + send.cursize = 2; + MSG_WriteByte(&send, ccmd_serveraddress); + + MSG_WriteString(&send, sv.name); + for (i = 0; i < count; i++) + MSG_WriteString(&send, NET_AdrToString(buf, sizeof(buf), &addr[i])); + MSG_WriteByte(&send, 0); + SSV_InstructMaster(&send); +} + +void SSV_SavePlayerStats(client_t *cl, unsigned int previousserver) +{ + //called when the *transfer userinfo gets set to the new map + sizebuf_t send; + qbyte send_buf[MAX_QWMSGLEN]; + int i; + if (!SSV_IsSubServer()) + return; + + if (!previousserver) + SV_SaveSpawnparmsClient(cl, NULL); + + memset(&send, 0, sizeof(send)); + send.data = send_buf; + send.maxsize = sizeof(send_buf); + send.cursize = 2; + + MSG_WriteByte(&send, ccmd_saveplayer); + MSG_WriteLong(&send, previousserver); + MSG_WriteLong(&send, cl->userid); + MSG_WriteByte(&send, NUM_SPAWN_PARMS); + for (i = 0; i < NUM_SPAWN_PARMS; i++) + { + MSG_WriteFloat(&send, cl->spawn_parms[i]); + } + + SSV_InstructMaster(&send); +} +void SSV_InitiatePlayerTransfer(client_t *cl, const char *newserver) +{ + //called when the *transfer userinfo gets set to the new map + sizebuf_t send; + qbyte send_buf[MAX_QWMSGLEN]; + int i; + char tmpbuf[256]; + float parms[NUM_SPAWN_PARMS]; + + SV_SaveSpawnparmsClient(cl, parms); + + memset(&send, 0, sizeof(send)); + send.data = send_buf; + send.maxsize = sizeof(send_buf); + send.cursize = 2; + MSG_WriteByte(&send, ccmd_transferplayer); + MSG_WriteLong(&send, cl->userid); + MSG_WriteString(&send, newserver); + MSG_WriteString(&send, NET_AdrToString(tmpbuf, sizeof(tmpbuf), &cl->netchan.remote_address)); + MSG_WriteString(&send, cl->guid); + + //stats + MSG_WriteByte(&send, NUM_SPAWN_PARMS); + for (i = 0; i < NUM_SPAWN_PARMS; i++) + { + MSG_WriteFloat(&send, parms[i]); + } + + SSV_InstructMaster(&send); +} + +#ifdef SQL +#include "sv_sql.h" +int pendinglookups = 0; +struct logininfo_s +{ + netadr_t clientaddr; + char guid[64]; +}; +#endif +qboolean SV_IgnoreSQLResult(queryrequest_t *req, int firstrow, int numrows, int numcols, qboolean eof) +{ + return false; +} +void MSV_UpdatePlayerStats(unsigned int lastserver, unsigned int playerid, unsigned int serverid, int numstats, float *stats) +{ + queryrequest_t *req; + sqlserver_t *srv; + static char hex[16] = "0123456789abcdef"; + char sql[2048], *sqle; + union{float *f;qbyte *b;} blob; + Q_snprintfz(sql, sizeof(sql), "UPDATE accounts SET stats=x'"); + sqle = sql+strlen(sql); + for (blob.f = stats, numstats*=4; numstats--; blob.b++) + { + *sqle++ = hex[*blob.b>>4]; + *sqle++ = hex[*blob.b&15]; + } + if (lastserver) + Q_snprintfz(sqle, sizeof(sql)-(sqle-sql), "', serverid=%u WHERE playerid = %u;", serverid, playerid); + else + Q_snprintfz(sqle, sizeof(sql)-(sqle-sql), "' WHERE playerid = %u AND serverid = %u;", playerid, serverid); + + srv = SQL_GetServer(sv.logindatabase, false); + if (srv) + SQL_NewQuery(srv, SV_IgnoreSQLResult, sql, &req); + + if (lastserver) + { + sizebuf_t send; + qbyte send_buf[64]; + memset(&send, 0, sizeof(send)); + send.data = send_buf; + send.maxsize = sizeof(send_buf); + send.cursize = 2; + MSG_WriteByte(&send, ccmd_transferedplayer); + MSG_WriteLong(&send, serverid); + MSG_WriteLong(&send, playerid); + MSV_InstructSlave(lastserver, &send); + } +} + +qboolean MSV_ClusterLoginReply(netadr_t *legacyclientredirect, unsigned int serverid, unsigned int playerid, char *clientguid, netadr_t *clientaddr, void *statsblob, size_t statsblobsize) +{ + char tmpbuf[256]; + netadr_t serveraddr; + + if (!serverid) + serverid = 1; + + if (!MSV_AddressForMap(&serveraddr, clientaddr->type, serverid) && !MSV_AddressForMap(&serveraddr, clientaddr->type, serverid=1)) + SV_RejectMessage(SCP_QUAKEWORLD, "Unable to find lobby.\n"); + else + { + sizebuf_t send; + qbyte send_buf[MAX_QWMSGLEN]; + memset(&send, 0, sizeof(send)); + send.data = send_buf; + send.maxsize = sizeof(send_buf); + send.cursize = 2; + MSG_WriteByte(&send, ccmd_takeplayer); + MSG_WriteLong(&send, playerid); + MSG_WriteLong(&send, 0); //from server + MSG_WriteString(&send, NET_AdrToString(tmpbuf, sizeof(tmpbuf), &net_from)); + MSG_WriteString(&send, clientguid); + + MSG_WriteByte(&send, statsblobsize/4); + SZ_Write(&send, statsblob, statsblobsize&~3); + MSV_InstructSlave(serverid, &send); + + if (serveraddr.type == NA_INVALID) + { + if (net_from.type != NA_LOOPBACK) + SV_RejectMessage(SCP_QUAKEWORLD, "Starting instance.\n"); + } + else if (legacyclientredirect) + { + *legacyclientredirect = serveraddr; + return true; + } + else + { + char *s = va("fredir\n%s", NET_AdrToString(tmpbuf, sizeof(tmpbuf), &serveraddr)); + Netchan_OutOfBand (NS_SERVER, clientaddr, strlen(s), (qbyte *)s); + return true; + } + } + return false; +} + +qboolean MSV_ClusterLoginSQLResult(queryrequest_t *req, int firstrow, int numrows, int numcols, qboolean eof) +{ + sqlserver_t *sql = SQL_GetServer(req->srvid, true); + queryresult_t *res = SQL_GetQueryResult(sql, req->num, 0); + struct logininfo_s *info = req->user.thread; + char *s; + int playerid, serverid; + char *statsblob; + size_t blobsize; + + res = SQL_GetQueryResult(sql, req->num, 0); + if (!res) + { + playerid = 0; + statsblob = NULL; + blobsize = 0; + serverid = 0; + } + else + { + s = SQL_ReadField(sql, res, 0, 0, true, NULL); + playerid = atoi(s); + + statsblob = SQL_ReadField(sql, res, 0, 2, true, &blobsize); + + s = SQL_ReadField(sql, res, 0, 1, true, NULL); + serverid = s?atoi(s):0; + } + + net_from = info->clientaddr; //okay, that's a bit stupid, rewrite rejectmessage to accept an arg? + if (!playerid) + SV_RejectMessage(SCP_QUAKEWORLD, "Bad username or password.\n"); + else + MSV_ClusterLoginReply(NULL, serverid, playerid, info->guid, &info->clientaddr, statsblob, blobsize); + Z_Free(info); + pendinglookups--; + return false; +} + +//returns true to block entry to this server. +qboolean MSV_ClusterLogin(char *guid, char *userinfo, size_t userinfosize) +{ + char escname[64], escpasswd[64]; + sqlserver_t *sql; + queryrequest_t *req; + struct logininfo_s *info; + char *sqlparams[] = + { + "", + "", + "", + "login", + }; + + if (sv.state != ss_clustermode) + return false; + + if (sv.logindatabase != -1) + { + if (pendinglookups > 10) + return true; + sql = SQL_GetServer(sv.logindatabase, false); + if (!sql) + return true; + SQL_Escape(sql, Info_ValueForKey(userinfo, "name"), escname, sizeof(escname)); + SQL_Escape(sql, Info_ValueForKey(userinfo, "password"), escpasswd, sizeof(escpasswd)); + if (SQL_NewQuery(sql, MSV_ClusterLoginSQLResult, va("SELECT playerid,serverid,stats FROM accounts WHERE name='%s' AND password='%s';", escname, escpasswd), &req) != -1) + { + pendinglookups++; + req->user.thread = info = Z_Malloc(sizeof(*info)); + Q_strncpyz(info->guid, guid, sizeof(info->guid)); + info->clientaddr = net_from; + } + } + else if (0) + { + char tmpbuf[256]; + netadr_t redir; + if (MSV_ClusterLoginReply(&redir, 0, nextuserid++, guid, &net_from, NULL, 0)) + { + Info_SetValueForStarKey(userinfo, "*redirect", NET_AdrToString(tmpbuf, sizeof(tmpbuf), &redir), userinfosize); + return false; + } + return true; + } + else + MSV_ClusterLoginReply(NULL, 0, nextuserid++, guid, &net_from, NULL, 0); + return true; +} +#endif + /* ================== SVC_DirectConnect @@ -1857,7 +2665,6 @@ SS: connect2 $VER $QPORT $CHALLENGE "\key\val" "\key\val" NQ: hacked to take the form of QW extension flags follow it. */ -int nextuserid; client_t *SVC_DirectConnect(void) { char userinfo[MAX_SPLITS][2048]; @@ -1881,6 +2688,7 @@ client_t *SVC_DirectConnect(void) char guid[128] = ""; char basic[80]; qboolean redirect = false; + qboolean preserveparms = false; int numssclients = 1; @@ -2016,10 +2824,16 @@ client_t *SVC_DirectConnect(void) Q_strncpyz (userinfo[i], Cmd_Argv(4+i), sizeof(userinfo[i])-1); } - for (i = 0; i < numssclients; i++) { - //don't let users exploit mods made for mvdsv instead of FTE - Info_RemoveKey (userinfo[i], "*VIP"); + char *banreason = SV_BannedReason(&adr); + if (banreason) + { + if (*banreason) + SV_RejectMessage (protocol, "You were banned.\nReason: %s\n", banreason); + else + SV_RejectMessage (protocol, "You were banned.\n"); + return NULL; + } } if (protocol == SCP_QUAKEWORLD) //readd? @@ -2111,6 +2925,9 @@ client_t *SVC_DirectConnect(void) } } + if (MSV_ClusterLogin(guid, userinfo[0], sizeof(userinfo[0]))) + return NULL; + // check for password or spectator_password if (svprogfuncs) { @@ -2205,6 +3022,8 @@ client_t *SVC_DirectConnect(void) Q_strncpyS (newcl->userinfo, userinfo[0], sizeof(newcl->userinfo)-1); newcl->userinfo[sizeof(newcl->userinfo)-1] = '\0'; +// Con_TPrintf("%s:%s:connect\n", sv.name, NET_AdrToString (adrbuf, sizeof(adrbuf), &adr)); + // if there is already a slot for this ip, drop it for (i=0,cl=svs.clients ; ifromgame == fg_halflife && !(newcl->fteprotocolextensions & PEXT_HLBSP)) { @@ -2302,14 +3121,23 @@ client_t *SVC_DirectConnect(void) else clients++; - if (cl->istobeloaded && cl->state == cs_zombie) + if (cl->state == cs_loadzombie) { if (!newcl) { - if (!strcmp(cl->name, name) || !*cl->name || sv.allocated_client_slots <= 1) //named, or first come first serve. + if (((!strcmp(cl->name, name) || !*cl->name) && (!*cl->guid || !strcmp(guid, cl->guid))) || sv.allocated_client_slots <= 1) //named, or first come first serve. { + if (cl->istobeloaded) + Con_Printf("%s:Using loadzombie\n", sv.name); + else + Con_Printf("%s:Using parmzombie\n", sv.name); newcl = cl; + preserveparms = true; temp.istobeloaded = cl->istobeloaded; + memcpy(temp.spawn_parms, cl->spawn_parms, sizeof(temp.spawn_parms)); +#ifdef SUBSERVERS + temp.previousserver = cl->previousserver; +#endif break; } } @@ -2318,6 +3146,12 @@ client_t *SVC_DirectConnect(void) if (!newcl) //client has no slot. It's possible to bipass this if server is loading a game. (or a duplicated qsocket) { + if (SSV_IsSubServer()) + { + SV_RejectMessage (protocol, "Direct connections are not permitted.\n"); + return NULL; + } + /*single player logic*/ if (sv.allocated_client_slots == 1 && net_from.type == NA_LOOPBACK) if (svs.clients[0].state >= cs_connected) @@ -2340,7 +3174,7 @@ client_t *SVC_DirectConnect(void) } } - /*only q1/h2 has maxclients/maxspectators limits. q2 or q3 the gamecode does this*/ + /*only q1/h2 has a maxclients/maxspectators separation. q2 or q3 the gamecode enforces any such clienttype limits*/ if (svprogfuncs) { if (spectator && spectators >= maxspectators.ival) @@ -2384,18 +3218,6 @@ client_t *SVC_DirectConnect(void) } } - { - char *banreason = SV_BannedReason(&adr); - if (banreason) - { - if (*banreason) - SV_RejectMessage (protocol, "You were banned.\nReason: %s\n", banreason); - else - SV_RejectMessage (protocol, "You were banned.\n"); - return NULL; - } - } - //set up the gamecode for this player, optionally drop them here edictnum = (newcl-svs.clients)+1; switch (svs.gametype) @@ -2418,16 +3240,22 @@ client_t *SVC_DirectConnect(void) #ifdef VM_Q1 case GT_Q1QVM: +#endif +#ifdef VM_LUA + case GT_LUA: #endif case GT_PROGS: - ent = EDICT_NUM(svprogfuncs, edictnum); + if (svprogfuncs) + ent = EDICT_NUM(svprogfuncs, edictnum); + else + ent = NULL; #ifdef Q2SERVER temp.q2edict = NULL; #endif temp.edict = ent; { - char *reject = SV_CheckRejectConnection(&adr, userinfo[0], protocol, protextsupported, protextsupported2, guid); + const char *reject = SV_CheckRejectConnection(&adr, userinfo[0], protocol, protextsupported, protextsupported2, guid); if (reject) { SV_RejectMessage(protocol, "%s", reject); @@ -2557,7 +3385,8 @@ client_t *SVC_DirectConnect(void) // SV_OutOfBandPrintf (isquake2client, adr, "\nWARNING: You have not got a place on the ranking system, probably because a user with the same name has already connected and your pwds differ.\n\n"); - SV_GetNewSpawnParms(newcl); + if (!preserveparms) + SV_GetNewSpawnParms(newcl); } else { @@ -2587,7 +3416,7 @@ client_t *SVC_DirectConnect(void) if (rs.timeonserver) { - if (cl->istobeloaded) + if (preserveparms) { //do nothing. } else if (sv_resetparms.value) @@ -2613,7 +3442,7 @@ client_t *SVC_DirectConnect(void) SV_OutOfBandPrintf (protocol == SCP_QUAKE2, &adr, s); } - else if (!cl->istobeloaded) + else if (!preserveparms) { SV_GetNewSpawnParms(newcl); @@ -2624,7 +3453,7 @@ client_t *SVC_DirectConnect(void) } #else // call the progs to get default spawn parms for the new client - if (!cl->istobeloaded) + if (!preserveparms) { SV_GetNewSpawnParms(newcl); } @@ -2688,6 +3517,8 @@ client_t *SVC_DirectConnect(void) SV_VoiceInitClient(newcl); #endif + SV_EvaluatePenalties(newcl); + newcl->fteprotocolextensions &= ~PEXT_SPLITSCREEN; for (clients = 1; clients < numssclients; clients++) { @@ -2746,7 +3577,10 @@ client_t *SVC_DirectConnect(void) SV_ExtractFromUserinfo (cl, true); - SV_GetNewSpawnParms(cl); +// if (!preserveparms) + SV_GetNewSpawnParms(cl); + + SV_EvaluatePenalties(cl); } newcl->controller = NULL; @@ -2766,6 +3600,10 @@ client_t *SVC_DirectConnect(void) newcl->redirect = redirect; +#ifdef SUBSERVERS + SSV_SavePlayerStats(newcl, newcl->previousserver); + newcl->previousserver = 0; +#endif return newcl; } @@ -3140,6 +3978,7 @@ qboolean SVNQ_ConnectionlessPacket(void) char *str; char buffer[256], buffer2[256]; netadr_t localaddr; + char *banreason; if (net_from.type == NA_LOOPBACK) return false; @@ -3155,6 +3994,8 @@ qboolean SVNQ_ConnectionlessPacket(void) if ((header & (NETFLAG_DATA|NETFLAG_EOM)) == (NETFLAG_DATA|NETFLAG_EOM)) { int sequence; + if (SV_BannedReason (&net_from)) + return false; //just in case. sequence = LongSwap(MSG_ReadLong()); if (sequence <= 1) { @@ -3259,6 +4100,20 @@ qboolean SVNQ_ConnectionlessPacket(void) NET_SendPacket(NS_SERVER, sb.cursize, sb.data, &net_from); return false; //not our version... } + + banreason = SV_BannedReason (&net_from); + if (banreason) + { + SZ_Clear(&sb); + MSG_WriteLong(&sb, 0); + MSG_WriteByte(&sb, CCREP_REJECT); + MSG_WriteString(&sb, *banreason?va("You are banned: %s\n", banreason):"You are banned\n"); + *(int*)sb.data = BigLong(NETFLAG_CTL+sb.cursize); + NET_SendPacket(NS_SERVER, sb.cursize, sb.data, &net_from); + return false; //not our version... + } + + mod = MSG_ReadByte(); modver = MSG_ReadByte(); flags = MSG_ReadByte(); @@ -3305,6 +4160,8 @@ qboolean SVNQ_ConnectionlessPacket(void) } return true; case CCREQ_SERVER_INFO: + if (SV_BannedReason (&net_from)) + return false; if (Q_strcmp (MSG_ReadString(), NET_GAMENAME_NQ) != 0) return false; @@ -3331,6 +4188,8 @@ qboolean SVNQ_ConnectionlessPacket(void) NET_SendPacket(NS_SERVER, sb.cursize, sb.data, &net_from); return true; case CCREQ_PLAYER_INFO: + if (SV_BannedReason (&net_from)) + return false; /*one request per player, ouch ouch ouch, what will it make of 32 players, I wonder*/ sb.maxsize = sizeof(buffer); sb.data = buffer; @@ -3359,6 +4218,9 @@ qboolean SVNQ_ConnectionlessPacket(void) NET_SendPacket(NS_SERVER, sb.cursize, sb.data, &net_from); return true; case CCREQ_RULE_INFO: + if (SV_BannedReason (&net_from)) + return false; + /*lol, nq is evil*/ sb.maxsize = sizeof(buffer); sb.data = buffer; @@ -3434,53 +4296,7 @@ If 0, then only addresses matching the list will be allowed. This lets you easi ============================================================================== */ -cvar_t filterban = SCVAR("filterban", "1"); - - -// SV_BannedAdress, run through ban address list and return corresponding bannedips_t -// pointer, otherwise return NULL if not in the list -bannedips_t *SV_GetBannedAddressEntry (netadr_t *a) -{ - bannedips_t *banip; - for (banip = svs.bannedips; banip; banip=banip->next) - { - if (NET_CompareAdrMasked(a, &banip->adr, &banip->adrmask)) - return banip; - } - return NULL; -} - -/* -================= -SV_FilterPacket -================= -*/ -char *SV_BannedReason (netadr_t *a) -{ - char *reason = filterban.value?NULL:""; //"" = banned with no explicit reason - bannedips_t *banip; - - if (NET_IsLoopBackAddress(a)) - return NULL; // never filter loopback - - for (banip = svs.bannedips; banip; banip=banip->next) - { - if (NET_CompareAdrMasked(a, &banip->adr, &banip->adrmask)) - { - switch(banip->type) - { - case BAN_BAN: - return banip->reason; - case BAN_PERMIT: - return 0; - default: - reason = filterban.value?banip->reason:NULL; - break; - } - } - } - return reason; -} +cvar_t filterban = CVARD("filterban", "1", "If 0, players will be kicked by default unless there is a rule that allows them. Also affects the default action of addip."); //send a network packet to a new non-connected client. //this is to combat tunneling @@ -3504,6 +4320,7 @@ void SV_OpenRoute_f(void) SV_ReadPackets ================= */ +void SV_KillExpiredBans(void); #ifdef SERVER_DEMO_PLAYBACK //FIMXE: move to header qboolean SV_GetPacket (void); @@ -3519,6 +4336,8 @@ qboolean SV_ReadPackets (float *delay) int giveup = 5000; /*we're fucked if we need this to be this high, but at least we can retain some clients if we're really running that slow*/ int cookie = 0; + SV_KillExpiredBans(); + for (i = 0; i < svs.allocated_client_slots; i++) //fixme: shouldn't we be using svs.allocated_client_slots ? { cl = &svs.clients[i]; @@ -3549,7 +4368,7 @@ qboolean SV_ReadPackets (float *delay) if (ISNQCLIENT(cl)) { - if (cl->state > cs_zombie) + if (cl->state >= cs_connected) { if (NQNetChan_Process(&cl->netchan)) { @@ -3566,7 +4385,7 @@ qboolean SV_ReadPackets (float *delay) { // this is a valid, sequenced packet, so process it received++; svs.stats.packets++; - if (cl->state > cs_zombie) + if (cl->state >= cs_connected) { //make sure they didn't already disconnect if (cl->send_message) cl->chokecount++; @@ -3592,19 +4411,24 @@ qboolean SV_ReadPackets (float *delay) while (giveup-- > 0 && (cookie=NET_GetPacket (NS_SERVER, cookie)) >= 0) #endif { - banreason = SV_BannedReason (&net_from); - if (banreason) - { - if (*banreason) - Netchan_OutOfBandTPrintf(NS_SERVER, &net_from, svs.language, "%cYou are banned: %s\n", A2C_PRINT, banreason); - else - Netchan_OutOfBandTPrintf(NS_SERVER, &net_from, svs.language, "%cYou are banned\n", A2C_PRINT); - continue; - } - // check for connectionless packet (0xffffffff) first if (*(int *)net_message.data == -1) { + banreason = SV_BannedReason (&net_from); + if (banreason) + { + static unsigned int lt; + unsigned int ct = Sys_Milliseconds(); + if (ct - lt > 5*1000) + { + if (*banreason) + Netchan_OutOfBandTPrintf(NS_SERVER, &net_from, svs.language, "You are banned: %s\n", banreason); + else + Netchan_OutOfBandTPrintf(NS_SERVER, &net_from, svs.language, "You are banned\n"); + } + continue; + } + SV_ConnectionlessPacket(); continue; } @@ -3635,7 +4459,7 @@ qboolean SV_ReadPackets (float *delay) #ifdef NQPROT if (ISNQCLIENT(cl) && cl->netchan.remote_address.port == net_from.port) { - if (cl->state != cs_zombie) + if (cl->state >= cs_connected) { if (cl->delay > 0) goto dominping; @@ -3667,7 +4491,7 @@ qboolean SV_ReadPackets (float *delay) if (cl->delay > 0) { dominping: - if (cl->state == cs_zombie) + if (cl->state < cs_connected) break; if (net_message.cursize > sizeof(svs.free_lagged_packet->data)) { @@ -3696,7 +4520,7 @@ dominping: { // this is a valid, sequenced packet, so process it received++; svs.stats.packets++; - if (cl->state != cs_zombie) + if (cl->state >= cs_connected) { if (cl->send_message) cl->chokecount++; @@ -3729,6 +4553,8 @@ dominping: if (SVNQ_ConnectionlessPacket()) continue; #endif + if (SV_BannedReason (&net_from)) + continue; if (NET_WasSpecialPacket(NS_SERVER)) continue; @@ -3765,7 +4591,8 @@ void SV_CheckTimeouts (void) for (i=0,cl=svs.clients ; istate == cs_connected || cl->state == cs_spawned) { + if (cl->state == cs_connected || cl->state == cs_spawned) + { if (!cl->spectator) nclients++; if (cl->netchan.last_received < droptime && cl->netchan.remote_address.type != NA_LOOPBACK && cl->protocol != SCP_BAD) @@ -3775,26 +4602,37 @@ void SV_CheckTimeouts (void) cl->state = cs_free; // don't bother with zombie state for local player. } } - if (cl->state == cs_zombie && - realtime - cl->connection_started > zombietime.value) - { - if (cl->connection_started == -1) - { - continue; - } + + if (cl->state == cs_zombie && realtime - cl->connection_started > zombietime.value) cl->state = cs_free; // can now be reused + + if (cl->state == cs_loadzombie && realtime - cl->connection_started > zombietime.value) + { if (cl->istobeloaded) { - pr_global_struct->self = EDICT_TO_PROG(svprogfuncs, cl->edict); - PR_ExecuteProgram (svprogfuncs, pr_global_struct->ClientDisconnect); + if (cl->istobeloaded == 1) + { + pr_global_struct->self = EDICT_TO_PROG(svprogfuncs, cl->edict); + PR_ExecuteProgram (svprogfuncs, pr_global_struct->ClientDisconnect); + if (*cl->name) + SV_BroadcastTPrintf (PRINT_HIGH, "LoadZombie %s timed out\n", cl->name); + else + SV_BroadcastTPrintf (PRINT_HIGH, "LoadZombie timed out\n", cl->name); + } sv.spawned_client_slots--; cl->istobeloaded=false; - SV_BroadcastTPrintf (PRINT_HIGH, "LoadZombie %s timed out\n", cl->name); -// cl->state = cs_zombie; // the real zombieness starts now -// cl->connection_started = realtime; + //must go through a zombie phase for 2 secs when the zombie gets removed. + cl->state = cs_zombie; // the real zombieness starts now + cl->connection_started = realtime; } + else + { + //no entity, just free them. + cl->state = cs_free; + } + cl->netchan.remote_address.type = NA_INVALID; //don't mess up from not knowing their address. } } if ((sv.paused&1) && !nclients) @@ -3945,8 +4783,10 @@ void SV_Impulse_f (void) svs.clients[i].edict->v->netname = PR_SetString(svprogfuncs, "Console"); + sv.skipbprintclient = &svs.clients[i]; pr_global_struct->self = EDICT_TO_PROG(svprogfuncs, svs.clients[i].edict); PR_ExecuteProgram (svprogfuncs, pr_global_struct->ClientConnect); + sv.skipbprintclient = NULL; pr_global_struct->time = sv.world.physicstime; pr_global_struct->self = EDICT_TO_PROG(svprogfuncs, svs.clients[i].edict); @@ -4108,6 +4948,17 @@ void SV_MVDStream_Poll(void); if (sv.state < ss_active || !sv.world.worldmodel) { +#ifdef SUBSERVERS + if (sv.state == ss_clustermode) + { + MSV_PollSlaves(); + isidle = !SV_ReadPackets (&delay); +#ifdef SQL + PR_SQLCycle(); +#endif + SV_SendClientMessages (); + } +#endif #ifndef SERVERONLY // check for commands typed to the host if (isDedicated) @@ -4195,7 +5046,8 @@ void SV_MVDStream_Poll(void); { NET_Tick(); - SV_GetConsoleCommands (); + if (sv.framenum != 1) + SV_GetConsoleCommands (); // process console commands if (!pr_imitatemvdsv.value) @@ -4330,6 +5182,7 @@ void SV_InitLocal (void) Cvar_Register (&sv_resetparms, cvargroup_servercontrol); + Cvar_Register (&sv_serverip, cvargroup_servercontrol); Cvar_Register (&sv_public, cvargroup_servercontrol); Cvar_Register (&sv_listen_qw, cvargroup_servercontrol); Cvar_Register (&sv_listen_nq, cvargroup_servercontrol); @@ -4394,6 +5247,12 @@ void SV_InitLocal (void) Cmd_AddCommand ("sv_impulse", SV_Impulse_f); +#ifdef SUBSERVERS + Cmd_AddCommand ("ssv", MSV_SubServerCommand_f); + Cmd_AddCommand ("ssv_all", MSV_SubServerCommand_f); + Cmd_AddCommand ("mapcluster", MSV_MapCluster_f); +#endif + Cmd_AddCommand ("openroute", SV_OpenRoute_f); Cmd_AddCommand ("savegame", SV_Savegame_f); @@ -4493,7 +5352,7 @@ void Master_Heartbeat (void) } else { - if (sv_masterlist[i].adr.type == NA_TCP || sv_masterlist[i].adr.type == NA_TCPV6) + if (sv_masterlist[i].adr.type == NA_TCP || sv_masterlist[i].adr.type == NA_TCPV6 || sv_masterlist[i].adr.type == NA_TLSV4 || sv_masterlist[i].adr.type == NA_TLSV6) NET_EnsureRoute(svs.sockets, sv_masterlist[i].cv.name, sv_masterlist[i].cv.string, false); //choose default port @@ -4704,7 +5563,7 @@ void SV_FixupName(char *in, char *out, unsigned int outlen) } -qboolean ReloadRanking(client_t *cl, char *newname) +qboolean ReloadRanking(client_t *cl, const char *newname) { #ifdef SVRANKING int newid; @@ -4862,6 +5721,8 @@ void SV_ExtractFromUserinfo (client_t *cl, qboolean verbose) } Q_strncpyz (cl->name, newname, sizeof(cl->namebuf)); + if (svprogfuncs) + svprogfuncs->SetStringField(svprogfuncs, cl->edict, &cl->edict->v->netname, cl->name, true); #ifdef SVRANKING if (ReloadRanking(cl, newname)) @@ -4908,6 +5769,7 @@ void SV_ExtractFromUserinfo (client_t *cl, qboolean verbose) { cl->messagelevel = atoi(val); } + #ifdef NQPROT { int top = atoi(Info_ValueForKey(cl->userinfo, "topcolor")); @@ -4921,7 +5783,8 @@ void SV_ExtractFromUserinfo (client_t *cl, qboolean verbose) cl->playercolor = top*16 + bottom; if (svs.gametype == GT_PROGS || svs.gametype == GT_Q1QVM) { - cl->edict->xv->clientcolors = cl->playercolor; + if (cl->edict) + cl->edict->xv->clientcolors = cl->playercolor; MSG_WriteByte (&sv.nqreliable_datagram, svc_updatecolors); MSG_WriteByte (&sv.nqreliable_datagram, cl-svs.clients); MSG_WriteByte (&sv.nqreliable_datagram, cl->playercolor); @@ -5073,6 +5936,14 @@ void SV_Init (quakeparms_t *parms) Con_TPrintf ("======== %s Initialized ========\n", *fs_gamename.string?fs_gamename.string:"Nothing"); +#ifdef SUBSERVERS + if (SSV_IsSubServer()) + { + NET_InitServer(); + return; + } +#endif + // if a map wasn't specified on the command line, spawn start.map //aliases require that we flush the cbuf in order to actually see the results. if (sv.state == ss_dead && Cmd_AliasExist("startmap_dm", RESTRICT_LOCAL)) diff --git a/engine/server/sv_mvd.c b/engine/server/sv_mvd.c index 6d39f103..902cd00a 100644 --- a/engine/server/sv_mvd.c +++ b/engine/server/sv_mvd.c @@ -1687,7 +1687,7 @@ void SV_MVD_SendInitialGamestate(mvddest_t *dest) sizebuf_t buf; char buf_data[MAX_QWMSGLEN]; int n, i; - char *s; + const char *s; client_t *player; char *gamedir; @@ -1720,11 +1720,7 @@ void SV_MVD_SendInitialGamestate(mvddest_t *dest) gamedir = Info_ValueForKey (svs.info, "*gamedir"); if (!gamedir[0]) - gamedir = FS_GetGamedir(); - - /*the gamedir shouldn't be fte - that should be hidden from clients*/ - if (!strncmp(gamedir, "fte", 3)) - gamedir = "qw"; + gamedir = FS_GetGamedir(true); MSG_WriteByte (&buf, svc_serverdata); diff --git a/engine/server/sv_phys.c b/engine/server/sv_phys.c index 807fe91f..54a8f95d 100644 --- a/engine/server/sv_phys.c +++ b/engine/server/sv_phys.c @@ -2270,7 +2270,11 @@ qboolean SV_Physics (void) //keep gravity tracking the cvar properly movevars.gravity = sv_gravity.value; - if (svs.gametype != GT_PROGS && svs.gametype != GT_Q1QVM && svs.gametype != GT_HALFLIFE) //make tics multiples of sv_maxtic (defaults to 0.1) + if (svs.gametype != GT_PROGS && svs.gametype != GT_Q1QVM && svs.gametype != GT_HALFLIFE +#ifdef VM_LUA + && svs.gametype != GT_LUA +#endif + ) //make tics multiples of sv_maxtic (defaults to 0.1) { host_frametime = sv.time - sv.world.physicstime; if (host_frametime<0) diff --git a/engine/server/sv_rankin.c b/engine/server/sv_rankin.c index 34c609bc..cb03c956 100644 --- a/engine/server/sv_rankin.c +++ b/engine/server/sv_rankin.c @@ -383,7 +383,7 @@ void Rank_SetPlayerStats(int id, rankstats_t *stats) } } -int Rank_GetPlayerID(char *guid, char *name, int pwd, qboolean allowadd, qboolean requirepasswordtobeset) +int Rank_GetPlayerID(char *guid, const char *name, int pwd, qboolean allowadd, qboolean requirepasswordtobeset) { rankstats_t rs; rankheader_t rh; diff --git a/engine/server/sv_send.c b/engine/server/sv_send.c index b7c4cec9..456e0a15 100644 --- a/engine/server/sv_send.c +++ b/engine/server/sv_send.c @@ -73,6 +73,21 @@ void SV_FlushRedirect (void) NET_SendPacket (NS_SERVER, strlen(send)+1, send, &net_from); } +#ifdef SUBSERVERS + else if (sv_redirected == RD_MASTER) + { + sizebuf_t s; + + memset(&s, 0, sizeof(s)); + s.data = send; + s.maxsize = sizeof(send); + s.cursize = 2; + + MSG_WriteByte(&s, ccmd_print); + MSG_WriteString(&s, outputbuf); + SSV_InstructMaster(&s); + } +#endif else if (sv_redirected == RD_CLIENT) { int chop; @@ -405,6 +420,9 @@ void VARGS SV_BroadcastPrintf (int level, char *fmt, ...) if (cl->protocol == SCP_BAD) continue; + if (cl == sv.skipbprintclient) //silence bprints about the player in ClientConnect. NQ completely wipes the buffer after clientconnect, which is what traditionally hides it. + continue; + if (cl->controller) continue; @@ -543,6 +561,22 @@ void SV_MulticastProtExt(vec3_t origin, multicast_t to, int dimension_mask, int } // to = MULTICAST_ALL; + //don't let things crash if the world model went away. can happen in broadcasts when reloading video with the map no longer available causing the server to die with the resulting broadcast messages about players dropping or gib effects appearing + if (sv.world.worldmodel->needload) + { + switch(to) + { + case MULTICAST_PHS_R: + case MULTICAST_PVS_R: + to = MULTICAST_ALL_R; + break; + case MULTICAST_PHS: + case MULTICAST_PVS: + to = MULTICAST_ALL; + break; + } + } + #ifdef Q2BSPS if (sv.world.worldmodel->fromgame == fg_quake2 || sv.world.worldmodel->fromgame == fg_quake3) { @@ -904,7 +938,7 @@ Larger attenuations will drop off. (max 4 attenuation) ================== */ -void SV_StartSound (int ent, vec3_t origin, int seenmask, int channel, char *sample, int volume, float attenuation, int pitchadj) +void SV_StartSound (int ent, vec3_t origin, int seenmask, int channel, const char *sample, int volume, float attenuation, int pitchadj) { int sound_num; int extfield_mask; @@ -1064,7 +1098,7 @@ void SV_StartSound (int ent, vec3_t origin, int seenmask, int channel, char *sam SV_MulticastProtExt(origin, reliable ? MULTICAST_ALL_R : MULTICAST_ALL, seenmask, requiredextensions, 0); } -void SVQ1_StartSound (float *origin, wedict_t *wentity, int channel, char *sample, int volume, float attenuation, int pitchadj) +void SVQ1_StartSound (float *origin, wedict_t *wentity, int channel, const char *sample, int volume, float attenuation, int pitchadj) { edict_t *entity = (edict_t*)wentity; int i; @@ -1146,8 +1180,8 @@ void SV_WriteEntityDataToMessage (client_t *client, sizebuf_t *msg, int pnum) MSG_WriteByte(msg, pnum); } MSG_WriteByte (msg, svc_damage); - MSG_WriteByte (msg, ent->v->dmg_save); - MSG_WriteByte (msg, ent->v->dmg_take); + MSG_WriteByte (msg, min(255, ent->v->dmg_save)); + MSG_WriteByte (msg, min(255, ent->v->dmg_take)); for (i=0 ; i<3 ; i++) MSG_WriteCoord (msg, other->v->origin[i] + 0.5*(other->v->mins[i] + other->v->maxs[i])); @@ -1598,9 +1632,9 @@ void SV_ClearQCStats(void) } extern cvar_t dpcompat_stats; -void SV_UpdateQCStats(edict_t *ent, int *statsi, char **statss, float *statsf) +void SV_UpdateQCStats(edict_t *ent, int *statsi, const char **statss, float *statsf) { - char *s; + const char *s; int i; int t; @@ -1651,7 +1685,6 @@ void SV_UpdateQCStats(edict_t *ent, int *statsi, char **statss, float *statsf) /*this function calculates the current stat values for the given client*/ void SV_CalcClientStats(client_t *client, int statsi[MAX_CL_STATS], float statsf[MAX_CL_STATS], char *statss[MAX_CL_STATS]) { - extern qboolean pr_items2; edict_t *ent; ent = client->edict; memset (statsi, 0, sizeof(int)*MAX_CL_STATS); @@ -1697,7 +1730,7 @@ void SV_CalcClientStats(client_t *client, int statsi[MAX_CL_STATS], float statsf } // stuff the sigil bits into the high bits of items for sbar - if (pr_items2) + if (sv.haveitems2) statsi[STAT_ITEMS] = (int)ent->v->items | ((int)ent->xv->items2 << 23); else statsi[STAT_ITEMS] = (int)ent->v->items | ((int)pr_global_struct->serverflags << 28); @@ -1992,7 +2025,7 @@ qboolean SV_SendClientDatagram (client_t *client) // copy the accumulated multicast datagram // for this client out to the message - if (client->datagram.overflowed) + if (client->datagram.overflowed || msg.cursize + client->datagram.cursize > msg.maxsize) Con_Printf ("WARNING: datagram overflowed for %s\n", client->name); else SZ_Write (&msg, client->datagram.data, client->datagram.cursize); @@ -2133,7 +2166,7 @@ void SV_UpdateToReliableMessages (void) int i, j; client_t *client, *sp; edict_t *ent; - char *name; + const char *name; float curgrav; float curspeed; @@ -2364,7 +2397,7 @@ void SV_SendClientMessages (void) { for (i=0, c = svs.clients ; istate <= cs_zombie) + if (c->state < cs_connected) continue; if (c->drop) @@ -2393,7 +2426,7 @@ void SV_SendClientMessages (void) // build individual updates for (i=0, c = svs.clients ; istate <= cs_zombie) + if (c->state < cs_loadzombie) continue; if (c->drop) @@ -2403,6 +2436,13 @@ void SV_SendClientMessages (void) continue; } + if (c->state == cs_loadzombie) + { //not yet present. + c->netchan.message.cursize = 0; + c->datagram.cursize = 0; + continue; + } + #ifdef SVCHAT SV_ChatThink(c); #endif @@ -2414,18 +2454,9 @@ void SV_SendClientMessages (void) continue; } - if (c->istobeloaded && c->state == cs_zombie) - { //not yet present. - c->netchan.message.cursize = 0; - c->datagram.cursize = 0; - continue; - } - #ifdef Q3SERVER if (ISQ3CLIENT(c)) { //q3 protocols bypass backbuffering and pretty much everything else - if (c->state <= cs_zombie) - continue; SVQ3_SendMessage(c); continue; } @@ -2521,7 +2552,7 @@ void SV_SendClientMessages (void) c->netchan.nqunreliableonly = false; c->send_message = false; //nq sends one packet only for each server physics frame - if (c->nextservertimeupdate < pt && c->state != cs_zombie) + if (c->nextservertimeupdate < pt && c->state >= ca_connected) { c->send_message = true; c->nextservertimeupdate = pt + 1.0/77; diff --git a/engine/server/sv_sql.c b/engine/server/sv_sql.c index 7f92565f..c0ed3744 100644 --- a/engine/server/sv_sql.c +++ b/engine/server/sv_sql.c @@ -17,6 +17,7 @@ static unsigned int (VARGS *qmysql_errno)(MYSQL *mysql); static const char *(VARGS *qmysql_error)(MYSQL *mysql); static MYSQL_FIELD *(VARGS *qmysql_fetch_field_direct)(MYSQL_RES *res, unsigned int fieldnr); static MYSQL_ROW (VARGS *qmysql_fetch_row)(MYSQL_RES *result); +static unsigned long *(VARGS *qmysql_fetch_lengths)(MYSQL_RES *result); static unsigned int (VARGS *qmysql_field_count)(MYSQL *mysql); static void (VARGS *qmysql_free_result)(MYSQL_RES *result); static const char *(VARGS *qmysql_get_client_info)(void); @@ -43,6 +44,7 @@ static dllfunction_t mysqlfuncs[] = {(void*)&qmysql_error, "mysql_error"}, {(void*)&qmysql_fetch_field_direct, "mysql_fetch_field_direct"}, {(void*)&qmysql_fetch_row, "mysql_fetch_row"}, + {(void*)&qmysql_fetch_lengths, "mysql_fetch_lengths"}, {(void*)&qmysql_field_count, "mysql_field_count"}, {(void*)&qmysql_free_result, "mysql_free_result"}, {(void*)&qmysql_get_client_info, "mysql_get_client_info"}, @@ -76,6 +78,7 @@ SQLITE_API int (QDECL *qsqlite3_column_count)(sqlite3_stmt *pStmt); SQLITE_API const char *(QDECL *qsqlite3_column_name)(sqlite3_stmt *pStmt, int N); SQLITE_API int (QDECL *qsqlite3_step)(sqlite3_stmt *pStmt); SQLITE_API const unsigned char *(QDECL *qsqlite3_column_text)(sqlite3_stmt *pStmt, int i); +SQLITE_API int (QDECL *qsqlite3_column_bytes)(sqlite3_stmt*, int iCol); SQLITE_API int (QDECL *qsqlite3_finalize)(sqlite3_stmt *pStmt); static dllfunction_t sqlitefuncs[] = @@ -92,6 +95,7 @@ static dllfunction_t sqlitefuncs[] = {(void*)&qsqlite3_column_name, "sqlite3_column_name"}, {(void*)&qsqlite3_step, "sqlite3_step"}, {(void*)&qsqlite3_column_text, "sqlite3_column_text"}, + {(void*)&qsqlite3_column_bytes, "sqlite3_column_bytes"}, {(void*)&qsqlite3_finalize, "sqlite3_finalize"}, {NULL} }; @@ -343,7 +347,7 @@ int sql_serverworker(void *sref) sqlite3_stmt *pStmt; const char *trailingstring; char *statementstring = qreq->query; - char **mat; + sqliteresult_t *mat; int rowspace; int totalrows = 0; qboolean keeplooping = true; @@ -361,8 +365,8 @@ int sql_serverworker(void *sref) { rowspace = 65; - qres = (queryresult_t *)ZF_Malloc(sizeof(queryresult_t) + columns * sizeof(char*) * rowspace); - mat = (char**)(qres + 1); + qres = (queryresult_t *)ZF_Malloc(sizeof(queryresult_t) + columns * sizeof(sqliteresult_t) * rowspace); + mat = (sqliteresult_t*)(qres + 1); if (qres) { qres->result = mat; @@ -376,7 +380,8 @@ int sql_serverworker(void *sref) //headers technically take a row. for (i = 0; i < columns; i++) { - mat[i] = strdup(qsqlite3_column_name(pStmt, i)); + mat[i].ptr = strdup(qsqlite3_column_name(pStmt, i)); + mat[i].len = 0; } rowspace--; mat += columns; @@ -390,7 +395,13 @@ int sql_serverworker(void *sref) //generate the row info for (i = 0; i < columns; i++) - mat[i] = strdup(qsqlite3_column_text(pStmt, i)); + { + const char *data = qsqlite3_column_text(pStmt, i); + mat[i].len = qsqlite3_column_bytes(pStmt, i); + mat[i].ptr = malloc(mat[i].len+1); + memcpy(mat[i].ptr, data, mat[i].len); + mat[i].ptr[mat[i].len] = 0; //make sure blobs are null terminated, in case someone reads them as a string. + } qres->rows++; totalrows++; rowspace--; @@ -660,8 +671,10 @@ void SQL_CloseAllRequests(sqlserver_t *server) } } -char *SQL_ReadField (sqlserver_t *server, queryresult_t *qres, int row, int col, qboolean fields) +char *SQL_ReadField (sqlserver_t *server, queryresult_t *qres, int row, int col, qboolean fields, size_t *resultsize) { + if (resultsize) + *resultsize = 0; if (!qres->result) return NULL; else @@ -681,6 +694,8 @@ char *SQL_ReadField (sqlserver_t *server, queryresult_t *qres, int row, int col, { MYSQL_FIELD *field = qmysql_fetch_field_direct(qres->result, col); + if (resultsize) + *resultsize = 0; if (!field) return NULL; else @@ -690,9 +705,13 @@ char *SQL_ReadField (sqlserver_t *server, queryresult_t *qres, int row, int col, #ifdef USE_SQLITE case SQLDRV_SQLITE: { - char **mat = qres->result; + sqliteresult_t *mat = qres->result; if (mat) - return mat[col]; + { + if (resultsize) + *resultsize = mat[col].len; + return mat[col].ptr; + } } return NULL; #endif @@ -711,9 +730,13 @@ char *SQL_ReadField (sqlserver_t *server, queryresult_t *qres, int row, int col, case SQLDRV_MYSQL: { MYSQL_ROW sqlrow; + unsigned long *lengths; qmysql_data_seek(qres->result, row); sqlrow = qmysql_fetch_row(qres->result); + lengths = qmysql_fetch_lengths(qres->result); + if (resultsize) + *resultsize = lengths?lengths[col]:0; if (!sqlrow || !sqlrow[col]) return NULL; else @@ -723,10 +746,14 @@ char *SQL_ReadField (sqlserver_t *server, queryresult_t *qres, int row, int col, #ifdef USE_SQLITE case SQLDRV_SQLITE: { - char **mat = qres->result; + sqliteresult_t *mat = qres->result; col += qres->columns * (row+1); if (mat) - return mat[col]; + { + if (resultsize) + *resultsize = mat[col].len; + return mat[col].ptr; + } } return NULL; #endif @@ -773,7 +800,7 @@ void SQL_CleanupServer(sqlserver_t *server) Z_Free(server); } -int SQL_NewServer(char *driver, char **paramstr) +int SQL_NewServer(const char *driver, const char **paramstr) { sqlserver_t *server; int serverref; @@ -929,7 +956,7 @@ void SQL_Disconnect(sqlserver_t *server) Sys_ConditionBroadcast(server->requestcondv); } -void SQL_Escape(sqlserver_t *server, char *src, char *dst, int dstlen) +void SQL_Escape(sqlserver_t *server, const char *src, char *dst, int dstlen) { switch (server->driver) { @@ -1141,6 +1168,9 @@ void SQL_ServerCycle (void) qres->next = qreq->results; qreq->results = qres; + if (developer.ival) + if (qres->error) + Con_Printf("%s\n", qres->error); if (qreq->state == SR_ABORTED) { persist = false; diff --git a/engine/server/sv_sql.h b/engine/server/sv_sql.h index fb8dc35f..f6879cb6 100644 --- a/engine/server/sv_sql.h +++ b/engine/server/sv_sql.h @@ -7,6 +7,13 @@ #endif #include #endif +#ifdef USE_SQLITE +typedef struct +{ + char *ptr; + int len; +} sqliteresult_t; +#endif #define SQL_CONNECT_STRUCTPARAMS 2 #define SQL_CONNECT_PARAMS 4 @@ -99,11 +106,11 @@ void SQL_ClosePersistantResult(sqlserver_t *server, queryresult_t *qres); void SQL_CloseResult(sqlserver_t *server, queryresult_t *qres); void SQL_CloseRequest(sqlserver_t *server, queryrequest_t *qres, qboolean force); void SQL_CloseAllResults(sqlserver_t *server); -char *SQL_ReadField (sqlserver_t *server, queryresult_t *qres, int row, int col, qboolean fields); -int SQL_NewServer(char *driver, char **paramstr); +char *SQL_ReadField (sqlserver_t *server, queryresult_t *qres, int row, int col, qboolean fields, size_t *resultsize); +int SQL_NewServer(const char *driver, const char **paramstr); int SQL_NewQuery(sqlserver_t *server, qboolean (*callback)(queryrequest_t *req, int firstrow, int numrows, int numcols, qboolean eof), char *str, queryrequest_t **reqout); //callback will be called on the main thread once the result is back void SQL_Disconnect(sqlserver_t *server); -void SQL_Escape(sqlserver_t *server, char *src, char *dst, int dstlen); +void SQL_Escape(sqlserver_t *server, const char *src, char *dst, int dstlen); const char *SQL_Info(sqlserver_t *server); qboolean SQL_Available(void); void SQL_ServerCycle (void); diff --git a/engine/server/sv_sys_win.c b/engine/server/sv_sys_win.c index db0ce464..62c33501 100644 --- a/engine/server/sv_sys_win.c +++ b/engine/server/sv_sys_win.c @@ -41,7 +41,6 @@ static HANDLE hconsoleout; qboolean WinNT; //if true, use utf-16 file paths. if false, hope that paths are in ascii. - #ifdef _DEBUG #if _MSC_VER >= 1300 #define CATCHCRASH @@ -287,6 +286,10 @@ int Sys_FileTime (char *path) return -1; } + +wchar_t *widen(wchar_t *out, size_t outlen, const char *utf8); +char *narrowen(char *out, size_t outlen, wchar_t *wide); + /* ================ Sys_mkdir @@ -309,52 +312,245 @@ qboolean Sys_Rename (char *oldfname, char *newfname) return !rename(oldfname, newfname); } -int Sys_EnumerateFiles (const char *gpath, const char *match, int (*func)(const char *fname, qofs_t fsize, void *parm, searchpathfuncs_t *spath), void *parm, searchpathfuncs_t *spath) +static int Sys_EnumerateFiles2 (const char *match, int matchstart, int neststart, int (QDECL *func)(const char *fname, qofs_t fsize, void *parm, searchpathfuncs_t *spath), void *parm, searchpathfuncs_t *spath) { - HANDLE r; - WIN32_FIND_DATA fd; - char apath[MAX_OSPATH]; - char file[MAX_OSPATH]; - char *s; - int go; - Q_strncpyz(apath, match, sizeof(apath)); -// sprintf(apath, "%s%s", gpath, match); - for (s = apath+strlen(apath)-1; s>= apath; s--) + qboolean go; + if (!WinNT) { - if (*s == '/') - break; - } - s++; - *s = '\0'; + HANDLE r; + WIN32_FIND_DATAA fd; + int nest = neststart; //neststart refers to just after a / + qboolean wild = false; - - - Q_snprintfz(file, sizeof(file), "%s/%s", gpath, match); - r = FindFirstFile(file, &fd); - if (r==(HANDLE)-1) - return 1; - go = true; - do - { - if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) //is a directory + while(match[nest] && match[nest] != '/') { - if (*fd.cFileName != '.') + if (match[nest] == '?' || match[nest] == '*') + wild = true; + nest++; + } + if (match[nest] == '/') + { + char submatch[MAX_OSPATH]; + char tmproot[MAX_OSPATH]; + char file[MAX_OSPATH]; + + if (!wild) + return Sys_EnumerateFiles2(match, matchstart, nest+1, func, parm, spath); + + if (nest-neststart+1> MAX_OSPATH) + return 1; + memcpy(submatch, match+neststart, nest - neststart); + submatch[nest - neststart] = 0; + nest++; + + if (neststart+4 > MAX_OSPATH) + return 1; + memcpy(tmproot, match, neststart); + strcpy(tmproot+neststart, "*.*"); + + r = FindFirstFile(tmproot, &fd); + strcpy(tmproot+neststart, ""); + if (r==(HANDLE)-1) + return 1; + go = true; + do { - Q_snprintfz(file, sizeof(file), "%s%s/", apath, fd.cFileName); - go = func(file, fd.nFileSizeLow, parm, spath); - } + if (*fd.cFileName == '.'); //don't ever find files with a name starting with '.' + else if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) //is a directory + { + if (wildcmp(submatch, fd.cFileName)) + { + int newnest; + if (strlen(tmproot) + strlen(fd.cFileName) + strlen(match+nest) + 2 < MAX_OSPATH) + { + Q_snprintfz(file, sizeof(file), "%s%s/", tmproot, fd.cFileName); + newnest = strlen(file); + strcpy(file+newnest, match+nest); + go = Sys_EnumerateFiles2(file, matchstart, newnest, func, parm, spath); + } + } + } + } while(FindNextFile(r, &fd) && go); + FindClose(r); } else { - Q_snprintfz(file, sizeof(file), "%s%s", apath, fd.cFileName); - go = func(file, fd.nFileSizeLow, parm, spath); + const char *submatch = match + neststart; + char tmproot[MAX_OSPATH]; + char file[MAX_OSPATH]; + + if (neststart+4 > MAX_OSPATH) + return 1; + memcpy(tmproot, match, neststart); + strcpy(tmproot+neststart, "*.*"); + + r = FindFirstFile(tmproot, &fd); + strcpy(tmproot+neststart, ""); + if (r==(HANDLE)-1) + return 1; + go = true; + do + { + if (*fd.cFileName == '.') + ; //don't ever find files with a name starting with '.' (includes .. and . directories, and unix hidden files) + else if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) //is a directory + { + if (wildcmp(submatch, fd.cFileName)) + { + if (strlen(tmproot+matchstart) + strlen(fd.cFileName) + 2 < MAX_OSPATH) + { + Q_snprintfz(file, sizeof(file), "%s%s/", tmproot+matchstart, fd.cFileName); + go = func(file, qofs_Make(fd.nFileSizeLow, fd.nFileSizeHigh), parm, spath); + } + } + } + else + { + if (wildcmp(submatch, fd.cFileName)) + { + if (strlen(tmproot+matchstart) + strlen(fd.cFileName) + 1 < MAX_OSPATH) + { + Q_snprintfz(file, sizeof(file), "%s%s", tmproot+matchstart, fd.cFileName); + go = func(file, qofs_Make(fd.nFileSizeLow, fd.nFileSizeHigh), parm, spath); + } + } + } + } while(FindNextFile(r, &fd) && go); + FindClose(r); } } - while(FindNextFile(r, &fd) && go); - FindClose(r); + else + { + HANDLE r; + WIN32_FIND_DATAW fd; + int nest = neststart; //neststart refers to just after a / + qboolean wild = false; + while(match[nest] && match[nest] != '/') + { + if (match[nest] == '?' || match[nest] == '*') + wild = true; + nest++; + } + if (match[nest] == '/') + { + char submatch[MAX_OSPATH]; + char tmproot[MAX_OSPATH]; + + if (!wild) + return Sys_EnumerateFiles2(match, matchstart, nest+1, func, parm, spath); + + if (nest-neststart+1> MAX_OSPATH) + return 1; + memcpy(submatch, match+neststart, nest - neststart); + submatch[nest - neststart] = 0; + nest++; + + if (neststart+4 > MAX_OSPATH) + return 1; + memcpy(tmproot, match, neststart); + strcpy(tmproot+neststart, "*.*"); + + { + wchar_t wroot[MAX_OSPATH]; + r = FindFirstFileW(widen(wroot, sizeof(wroot), tmproot), &fd); + } + strcpy(tmproot+neststart, ""); + if (r==(HANDLE)-1) + return 1; + go = true; + do + { + char utf8[MAX_OSPATH]; + char file[MAX_OSPATH]; + narrowen(utf8, sizeof(utf8), fd.cFileName); + if (*utf8 == '.'); //don't ever find files with a name starting with '.' + else if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) //is a directory + { + if (wildcmp(submatch, utf8)) + { + int newnest; + if (strlen(tmproot) + strlen(utf8) + strlen(match+nest) + 2 < MAX_OSPATH) + { + Q_snprintfz(file, sizeof(file), "%s%s/", tmproot, utf8); + newnest = strlen(file); + strcpy(file+newnest, match+nest); + go = Sys_EnumerateFiles2(file, matchstart, newnest, func, parm, spath); + } + } + } + } while(FindNextFileW(r, &fd) && go); + FindClose(r); + } + else + { + const char *submatch = match + neststart; + char tmproot[MAX_OSPATH]; + + if (neststart+4 > MAX_OSPATH) + return 1; + memcpy(tmproot, match, neststart); + strcpy(tmproot+neststart, "*.*"); + + { + wchar_t wroot[MAX_OSPATH]; + r = FindFirstFileW(widen(wroot, sizeof(wroot), tmproot), &fd); + } + strcpy(tmproot+neststart, ""); + if (r==(HANDLE)-1) + return 1; + go = true; + do + { + char utf8[MAX_OSPATH]; + char file[MAX_OSPATH]; + + narrowen(utf8, sizeof(utf8), fd.cFileName); + if (*utf8 == '.') + ; //don't ever find files with a name starting with '.' (includes .. and . directories, and unix hidden files) + else if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) //is a directory + { + if (wildcmp(submatch, utf8)) + { + if (strlen(tmproot+matchstart) + strlen(utf8) + 2 < MAX_OSPATH) + { + Q_snprintfz(file, sizeof(file), "%s%s/", tmproot+matchstart, utf8); + go = func(file, qofs_Make(fd.nFileSizeLow, fd.nFileSizeHigh), parm, spath); + } + } + } + else + { + if (wildcmp(submatch, utf8)) + { + if (strlen(tmproot+matchstart) + strlen(utf8) + 1 < MAX_OSPATH) + { + Q_snprintfz(file, sizeof(file), "%s%s", tmproot+matchstart, utf8); + go = func(file, qofs_Make(fd.nFileSizeLow, fd.nFileSizeHigh), parm, spath); + } + } + } + } while(FindNextFileW(r, &fd) && go); + FindClose(r); + } + } return go; } +int Sys_EnumerateFiles (const char *gpath, const char *match, int (QDECL *func)(const char *fname, qofs_t fsize, void *parm, searchpathfuncs_t *spath), void *parm, searchpathfuncs_t *spath) +{ + char fullmatch[MAX_OSPATH]; + int start; + if (strlen(gpath) + strlen(match) + 2 > MAX_OSPATH) + return 1; + + strcpy(fullmatch, gpath); + start = strlen(fullmatch); + if (start && fullmatch[start-1] != '/') + fullmatch[start++] = '/'; + fullmatch[start] = 0; + strcat(fullmatch, match); + return Sys_EnumerateFiles2(fullmatch, start, start, func, parm, spath); +} /* ================ @@ -493,7 +689,7 @@ double Sys_DoubleTime (void) Sys_ConsoleInput ================ */ - +void SV_GetNewSpawnParms(client_t *cl); char coninput_text[256]; int coninput_len; char *Sys_ConsoleInput (void) @@ -513,6 +709,50 @@ char *Sys_ConsoleInput (void) return NULL; } +#ifdef SUBSERVERS + if (SSV_IsSubServer()) + { + DWORD avail; + static char text[1024], *nl; + static int textpos = 0; + + HANDLE input = GetStdHandle(STD_INPUT_HANDLE); + if (!PeekNamedPipe(input, NULL, 0, NULL, &avail, NULL)) + { + SV_FinalMessage("Cluster shut down\n"); + Cmd_ExecuteString("quit force", RESTRICT_LOCAL); + } + else if (avail) + { + if (avail > sizeof(text)-1-textpos) + avail = sizeof(text)-1-textpos; + if (ReadFile(input, text+textpos, avail, &avail, NULL)) + { + textpos += avail; + while(textpos >= 2) + { + unsigned short len = text[0] | (text[1]<<8); + if (textpos >= len && len >= 2) + { + memcpy(net_message.data, text+2, len-2); + net_message.cursize = len-2; + MSG_BeginReading (msg_nullnetprim); + + SSV_ReadFromControlServer(); + + memmove(text, text+len, textpos - len); + textpos -= len; + } + else + break; + } + } + + } + return NULL; + } +#endif + // read a line out while (_kbhit()) { @@ -647,6 +887,14 @@ void Sys_Printf (char *fmt, ...) vsnprintf (msg,sizeof(msg)-1, fmt,argptr); va_end (argptr); +#ifdef SUBSERVERS + if (SSV_IsSubServer()) + { + SSV_PrintToMaster(msg); + return; + } +#endif + { int i; @@ -1118,6 +1366,10 @@ int main (int argc, char **argv) signal (SIGSEGV, Signal_Error_Handler); } #endif + +#ifdef SUBSERVERS + isClusterSlave = COM_CheckParm("-clusterslave"); +#endif StartQuakeServer(); ServerMainLoop(); diff --git a/engine/server/sv_user.c b/engine/server/sv_user.c index 7be7fc82..d3b00d23 100644 --- a/engine/server/sv_user.c +++ b/engine/server/sv_user.c @@ -225,11 +225,25 @@ void SV_New_f (void) if (host_client->redirect) { - char *msg = va("connect \"%s\"\n", sv_fullredirect.string); - ClientReliableWrite_Begin (host_client, svc_stufftext, 2+strlen(msg)); - ClientReliableWrite_String (host_client, msg); + if (host_client->redirect == 1) + { + char *msg = va("connect \"%s\"\n", sv_fullredirect.string); + ClientReliableWrite_Begin (host_client, svc_stufftext, 2+strlen(msg)); + ClientReliableWrite_String (host_client, msg); + } return; } + else + { + char *srv = Info_ValueForKey(host_client->userinfo, "*redirect"); + if (*srv) + { + char *msg = va("connect \"%s\"\n", srv); + ClientReliableWrite_Begin (host_client, svc_stufftext, 2+strlen(msg)); + ClientReliableWrite_String (host_client, msg); + return; + } + } /* splitt delay host_client->state = cs_connected; @@ -243,10 +257,10 @@ void SV_New_f (void) // host_client->sendinfo = true; gamedir = Info_ValueForKey (svs.info, "*gamedir"); - if (!gamedir[0] || !strcmp(gamedir, "fte")) + if (!gamedir[0]) { if (ISQWCLIENT(host_client)) - gamedir = "qw"; + gamedir = FS_GetGamedir(true); else gamedir = ""; } @@ -431,9 +445,12 @@ void SVNQ_New_f (void) host_client->send_message = true; if (host_client->redirect) { - char *msg = va("connect \"%s\"\n", sv_fullredirect.string); - ClientReliableWrite_Begin (host_client, svc_stufftext, 2+strlen(msg)); - ClientReliableWrite_String (host_client, msg); + if (host_client->redirect == 1) + { + char *msg = va("connect \"%s\"\n", sv_fullredirect.string); + ClientReliableWrite_Begin (host_client, svc_stufftext, 2+strlen(msg)); + ClientReliableWrite_String (host_client, msg); + } return; } @@ -1020,7 +1037,7 @@ void SV_SendClientPrespawnInfo(client_t *client) if (client->zquake_extensions & Z_EXT_VWEP) { char mname[MAX_QPATH]; - char vweaplist[1024] = "//vwep"; + char vweaplist[2048] = "//vwep"; for (i = 0; sv.strings.vw_model_precache[i]; i++) { @@ -1743,8 +1760,10 @@ void SV_Begin_Core(client_t *split) pr_global_struct->self = EDICT_TO_PROG(svprogfuncs, split->edict); if (pr_globals) G_FLOAT(OFS_PARM0) = split->csqcactive; //this arg is part of EXT_CSQC_1, but doesn't have to be supported by the mod + sv.skipbprintclient = host_client; if (pr_global_ptrs->ClientConnect) PR_ExecuteProgram (svprogfuncs, *pr_global_ptrs->ClientConnect); + sv.skipbprintclient = NULL; // actually spawn the player pr_global_struct->time = sv.world.physicstime; @@ -2283,6 +2302,7 @@ void SV_VoiceReadPacket(void) /*read the data from the client*/ bytes = MSG_ReadShort(); ring = &voice.ring[voice.write & (VOICE_RING_SIZE-1)]; + //voice data does not get echoed to the sender unless sv_voip_echo is on too, which is rarely the case, so no worries about leaking the mute+deaf talking-to-yourself thing if (bytes > sizeof(ring->data) || host_client->ismuted || !sv_voip.ival) { MSG_ReadSkip(bytes); @@ -2315,6 +2335,9 @@ void SV_VoiceReadPacket(void) if (!cl->spectator) continue; + if (cl->isdeaf) + continue; + if (vt == VT_TEAM) { // the spectator team @@ -2476,23 +2499,53 @@ void SV_Voice_Target_f(void) { unsigned int other; char *t = Cmd_Argv(1); + char *v = Cmd_Argv(2); + qboolean verbose = *v?atoi(v):host_client->voice_active; if (!strcmp(t, "team")) + { host_client->voice_target = VT_TEAM; + if (verbose) + { + if (teamplay.ival) + SV_ClientTPrintf(host_client, PRINT_HIGH, "Now sending voice to team\n"); + else + SV_ClientTPrintf(host_client, PRINT_HIGH, "Now sending voice to all (no teamplay)\n"); + } + } else if (!strcmp(t, "all")) + { host_client->voice_target = VT_ALL; + if (verbose) + SV_ClientTPrintf(host_client, PRINT_HIGH, "Now sending voice to all\n"); + } else if (!strcmp(t, "nonmuted")) + { host_client->voice_target = VT_NONMUTED; + if (verbose) + SV_ClientTPrintf(host_client, PRINT_HIGH, "Now sending voice to all people you've not ignored\n"); + } else if (*t >= '0' && *t <= '9') { other = atoi(t); if (other >= svs.allocated_client_slots) + if (verbose) + SV_ClientTPrintf(host_client, PRINT_HIGH, "Invalid client\n"); return; host_client->voice_target = VT_PLAYERSLOT0 + other; + if (verbose) + { + if (host_client->state >= cs_connected) + SV_ClientTPrintf(host_client, PRINT_HIGH, "Now sending voice only to %s\n", host_client->name); + else + SV_ClientTPrintf(host_client, PRINT_HIGH, "Now sending voice only to player slot %i, if someone occupies it\n", other); + } } else { /*don't know who you mean, futureproofing*/ host_client->voice_target = VT_TEAM; + if (verbose) + SV_ClientTPrintf(host_client, PRINT_HIGH, "Now sending voice to team\n"); } } void SV_Voice_MuteAll_f(void) @@ -3017,8 +3070,16 @@ void SV_SayOne_f (void) if (Cmd_Argc () < 3) return; + if (host_client->ismuted && !host_client->isdeaf) + { + SV_ClientTPrintf(host_client, PRINT_CHAT, "You are muted\n"); + return; + } + while((to = SV_GetClientForString(Cmd_Argv(1), &clnum))) { + if (host_client->ismuted) + continue; if (host_client->spectator) { if (!sv_spectalk.value || to->spectator) @@ -3029,12 +3090,8 @@ void SV_SayOne_f (void) else Q_snprintfz (text, sizeof(text), "{%s}:", host_client->name); - if (host_client->ismuted) - { - SV_ClientTPrintf(host_client, PRINT_CHAT, "%s is muted\n"); - return; - } - + if (to->isdeaf) + continue; for (i = 2; ; i++) { @@ -3147,7 +3204,8 @@ void SV_Say (qboolean team) if (Cmd_Argc () < 2) return; - Sys_ServerActivity(); + if (!host_client->ismuted) + Sys_ServerActivity(); memset(sent, 0, sizeof(sent)); @@ -3163,7 +3221,7 @@ void SV_Say (qboolean team) else Q_snprintfz (text, sizeof(text), "%s: ", host_client->name); - if (host_client->ismuted) + if (host_client->ismuted && !host_client->isdeaf) { SV_ClientTPrintf(host_client, PRINT_CHAT, "You cannot chat while muted\n"); return; @@ -3221,7 +3279,8 @@ void SV_Say (qboolean team) Q_strcat(text, "\n"); - Sys_Printf ("%s", text); + if (!host_client->ismuted) + Sys_Printf ("%s", text); mvdrecording = sv.mvdrecording; sv.mvdrecording = false; //so that the SV_ClientPrintf doesn't send to all players. @@ -3246,6 +3305,14 @@ void SV_Say (qboolean team) } } + if (host_client->ismuted) + { + if (client != host_client) + continue; + } + else if (client->isdeaf) + continue; + cls |= 1 << j; //make sure we don't send the say to the same client 20 times due to splitscreen @@ -3380,17 +3447,23 @@ void SV_Kill_f (void) } #endif -#ifdef VM_Q1 - if (svs.gametype == GT_Q1QVM) + switch(svs.gametype) { +#ifdef VM_Q1 + case GT_Q1QVM: pr_global_struct->time = sv.world.physicstime; pr_global_struct->self = EDICT_TO_PROG(svprogfuncs, sv_player); Q1QVM_ClientCommand(); return; - } #endif - if (svs.gametype != GT_PROGS) - return; +#ifdef VM_LUA + case GT_LUA: +#endif + case GT_PROGS: + break; + default: + return; //should have its own parsing. + } if (sv_player->v->health <= 0) { @@ -3480,7 +3553,9 @@ void SV_Drop_f (void) SV_EndRedirect (); if (!host_client->drop) { - if (host_client->redirect) + if (host_client->redirect == 2) + SV_BroadcastPrintf (PRINT_HIGH, "%s transfered to %s\n", host_client->name, Info_ValueForKey(host_client->userinfo, "*transfer")); + else if (host_client->redirect) SV_BroadcastPrintf (PRINT_HIGH, "%s redirected to %s\n", host_client->name, sv_fullredirect.string); else { @@ -3853,7 +3928,7 @@ void SV_Vote_f (void) int totalusers = 0; qboolean passes; - if (!votelevel.value) + if (!votelevel.value || (host_client->ismuted && host_client->isdeaf)) { SV_ClientTPrintf(host_client, PRINT_HIGH, "Voting was dissallowed\n"); return; @@ -3990,7 +4065,7 @@ void Cmd_Give_f (void) return; } - if (svs.gametype != GT_PROGS) + if (!svprogfuncs) return; t = Cmd_Argv(1); @@ -4031,7 +4106,7 @@ void Cmd_Give_f (void) SV_TPrintToClient(host_client, PRINT_HIGH, "give: unknown item\n"); } } - else + else if (svprogfuncs->EvaluateDebugString) { if (developer.value < 2 && host_client->netchan.remote_address.type != NA_LOOPBACK) //we don't want clients doing nasty things... like setting movetype 3123 { @@ -4056,7 +4131,7 @@ void Cmd_Noclip_f (void) return; } - if (svs.gametype != GT_PROGS) + if (!svprogfuncs) return; SV_LogPlayer(host_client, "noclip cheat"); @@ -4085,7 +4160,7 @@ void Cmd_Fly_f (void) return; } - if (svs.gametype != GT_PROGS) + if (!svprogfuncs) return; SV_LogPlayer(host_client, "fly cheat"); @@ -4119,7 +4194,7 @@ void Cmd_SetPos_f(void) return; } - if (svs.gametype != GT_PROGS) + if (!svprogfuncs) return; if (Cmd_Argc() != 4) @@ -4156,7 +4231,7 @@ void SV_SetUpClientEdict (client_t *cl, edict_t *ent) { if (progstype != PROG_NQ) //allow frikbots to work in NQ mods (but not qw!) ED_Clear(svprogfuncs, ent); - ent->v->netname = PR_SetString(svprogfuncs, cl->name); + svprogfuncs->SetStringField(svprogfuncs, ent, &ent->v->netname, cl->name, true); } ED_Spawned(ent, false); ent->isfree = false; @@ -4650,9 +4725,11 @@ void SVNQ_Begin_f (void) } // call the spawn function + sv.skipbprintclient = host_client; pr_global_struct->time = sv.world.physicstime; pr_global_struct->self = EDICT_TO_PROG(svprogfuncs, sv_player); PR_ExecuteProgram (svprogfuncs, pr_global_struct->ClientConnect); + sv.skipbprintclient = NULL; // actually spawn the player pr_global_struct->time = sv.world.physicstime; @@ -6276,6 +6353,9 @@ void SV_ExecuteClientMessage (client_t *cl) cl->delay = bound(0, cl->delay, 1); //but make sure things don't go crazy } } + if (cl->islagged) + if (cl->delay < 0.2) + cl->delay = 0.2; } if (sv_antilag.ival) @@ -6724,7 +6804,7 @@ void SVQ2_ExecuteClientMessage (client_t *cl) host_client = cl; sv_player = cl->edict; - if (cl->state == cs_zombie) + if (cl->state >= cs_connected) return; // disconnect command break; diff --git a/engine/server/svhl_game.c b/engine/server/svhl_game.c index b341611f..7fe4a161 100644 --- a/engine/server/svhl_game.c +++ b/engine/server/svhl_game.c @@ -1587,9 +1587,11 @@ qboolean SVHL_ClientConnect(client_t *client, netadr_t adr, char rejectmessage[1 NET_AdrToString(ipadr, sizeof(ipadr), &adr); strcpy(rejectmessage, "Rejected by gamecode"); + sv.skipbprintclient = client; bi_begin(); result = SVHL_GameFuncs.ClientConnect(&SVHL_Edict[client-svs.clients+1], client->name, ipadr, rejectmessage); bi_end(); + sv.skipbprintclient = NULL; return result; } diff --git a/engine/server/svmodel.c b/engine/server/svmodel.c index 9d6c9e0d..12d3c8be 100644 --- a/engine/server/svmodel.c +++ b/engine/server/svmodel.c @@ -26,16 +26,16 @@ model_t *loadmodel; char loadname[32]; // for hunk tags static int mod_datasequence; -qboolean Terr_LoadTerrainModel (model_t *mod, void *buffer); -qboolean Mod_LoadBrushModel (model_t *mod, void *buffer); +qboolean Terr_LoadTerrainModel (model_t *mod, void *buffer, size_t fsize); +qboolean Mod_LoadBrushModel (model_t *mod, void *buffer, size_t fsize); qboolean Mod_LoadQ2BrushModel (model_t *mod, void *buffer); -qboolean D3_LoadMap_CollisionMap(model_t *mod, char *buf); +qboolean D3_LoadMap_CollisionMap(model_t *mod, char *buf, size_t fsize); -qboolean Mod_LoadQ1Model (model_t *mod, void *buffer); -qboolean Mod_LoadQ2Model (model_t *mod, void *buffer); -qboolean Mod_LoadQ3Model (model_t *mod, void *buffer); -qboolean Mod_LoadZymoticModel (model_t *mod, void *buffer); -qboolean Mod_LoadDarkPlacesModel(model_t *mod, void *buffer); +qboolean Mod_LoadQ1Model (model_t *mod, void *buffer, size_t fsize); +qboolean Mod_LoadQ2Model (model_t *mod, void *buffer, size_t fsize); +qboolean Mod_LoadQ3Model (model_t *mod, void *buffer, size_t fsize); +qboolean Mod_LoadZymoticModel (model_t *mod, void *buffer, size_t fsize); +qboolean Mod_LoadDarkPlacesModel(model_t *mod, void *buffer, size_t fsize); qbyte mod_novis[(MAX_MAP_LEAFS+7)/8]; @@ -376,7 +376,7 @@ Mod_FindName ================== */ -model_t *Mod_FindName (char *name) +model_t *Mod_FindName (const char *name) { int i; model_t *mod; @@ -546,7 +546,7 @@ Mod_ForName Loads in a model for the given name ================== */ -model_t *Mod_ForName (char *name, qboolean crash) +model_t *Mod_ForName (const char *name, qboolean crash) { model_t *mod; diff --git a/engine/server/svq2_game.c b/engine/server/svq2_game.c index 32da15eb..58d0245b 100644 --- a/engine/server/svq2_game.c +++ b/engine/server/svq2_game.c @@ -377,9 +377,9 @@ static qboolean CMQ2_Q1BSP_SetAreaPortalState (int portalnum, qboolean open) return true; }*/ -static void VARGS PFQ2_WriteChar (int c) {MSG_WriteChar (&sv.q2multicast, c);} -static void VARGS PFQ2_WriteByte (int c) {MSG_WriteByte (&sv.q2multicast, c);} -static void VARGS PFQ2_WriteShort (int c) {MSG_WriteShort (&sv.q2multicast, c);} +static void VARGS PFQ2_WriteChar (int c) {MSG_WriteChar (&sv.q2multicast, c & 0xff);} +static void VARGS PFQ2_WriteByte (int c) {MSG_WriteByte (&sv.q2multicast, c & 0xff);} +static void VARGS PFQ2_WriteShort (int c) {MSG_WriteShort (&sv.q2multicast, c & 0xffff);} static void VARGS PFQ2_WriteLong (int c) {MSG_WriteLong (&sv.q2multicast, c);} static void VARGS PFQ2_WriteFloat (float f) {MSG_WriteFloat (&sv.q2multicast, f);} static void VARGS PFQ2_WriteString (char *s) {MSG_WriteString (&sv.q2multicast, s);} diff --git a/engine/server/svq3_game.c b/engine/server/svq3_game.c index 660ba737..d3c708de 100644 --- a/engine/server/svq3_game.c +++ b/engine/server/svq3_game.c @@ -154,6 +154,23 @@ static void Q3G_UnlinkEntity(q3sharedEntity_t *ent) #define MAX_TOTAL_ENT_LEAFS 256 +static model_t *Q3G_GetCModel(unsigned int modelindex) +{ + if ((unsigned int)modelindex < MAX_MODELS) + { + if (!sv.models[modelindex]) + { + if (modelindex == 0) + sv.models[modelindex] = sv.world.worldmodel; + else + sv.models[modelindex] = Mod_ForName(va("*%i", modelindex), MLV_WARN); + } + + if (!sv.models[modelindex]->needload) + return sv.models[modelindex]; + } + return NULL; +} static void Q3G_LinkEntity(q3sharedEntity_t *ent) { @@ -459,8 +476,9 @@ static void SVQ3_Trace(q3trace_t *result, vec3_t start, vec3_t mins, vec3_t maxs if (es->r.bmodel) { - mod = Mod_ForName(va("*%i", es->s.modelindex), false); - if (mod->needload) + //FIXME, this is inefficient. + mod = Q3G_GetCModel(es->s.modelindex); + if (!mod) continue; tr = CM_TransformedBoxTrace(mod, start, end, mins, maxs, contentmask, es->r.currentOrigin, vec3_origin); } @@ -533,8 +551,8 @@ static int SVQ3_PointContents(vec3_t pos, int entnum) if (es->r.bmodel) { - mod = Mod_ForName(va("*%i", es->s.modelindex), false); - if (mod->needload) + mod = Q3G_GetCModel(es->s.modelindex); + if (!mod) continue; tr = CM_TransformedBoxTrace(mod, pos, pos, vec3_origin, vec3_origin, 0xffffffff, es->r.currentOrigin, vec3_origin); } @@ -556,11 +574,11 @@ static int SVQ3_Contact(vec3_t mins, vec3_t maxs, q3sharedEntity_t *ent, qboolea trace_t tr; if (ent->r.bmodel) - mod = Mod_ForName(va("*%i", ent->s.modelindex), false); + mod = Q3G_GetCModel(ent->s.modelindex); else mod = CM_TempBoxModel(ent->r.mins, ent->r.maxs); - if (mod->needload || !mod->funcs.NativeTrace) + if (!mod || !mod->funcs.NativeTrace) return false; tr = CM_TransformedBoxTrace(mod, vec3_origin, vec3_origin, mins, maxs, 0xffffffff, ent->r.currentOrigin, ent->r.currentAngles); @@ -572,13 +590,25 @@ static int SVQ3_Contact(vec3_t mins, vec3_t maxs, q3sharedEntity_t *ent, qboolea static void SVQ3_SetBrushModel(q3sharedEntity_t *ent, char *modelname) { + int modelindex; model_t *mod; - mod = Mod_ForName(modelname, false); - VectorCopy(mod->mins, ent->r.mins); - VectorCopy(mod->maxs, ent->r.maxs); + if (!modelname || *modelname != '*') + SV_Error("SVQ3_SetBrushModel: not an inline model"); + modelindex = atoi(modelname+1); + mod = Q3G_GetCModel(modelindex); + if (mod) + { + VectorCopy(mod->mins, ent->r.mins); + VectorCopy(mod->maxs, ent->r.maxs); + } + else + { + VectorCopy(mod->mins, ent->r.mins); + VectorCopy(mod->maxs, ent->r.maxs); + } ent->r.bmodel = true; ent->r.contents = -1; - ent->s.modelindex = atoi(modelname+1); + ent->s.modelindex = modelindex; Q3G_LinkEntity( ent ); } @@ -625,7 +655,7 @@ void SVQ3_SendServerCommand(client_t *cl, char *str) int i; for (i = 0; i < sv.allocated_client_slots; i++) { - if (svs.clients[i].state>cs_zombie) + if (svs.clients[i].state>=cs_connected) { SVQ3_SendServerCommand(&svs.clients[i], str); //go for consistancy. } @@ -1625,9 +1655,17 @@ static void QDECL BL_BSPModelMinsMaxsOrigin(int modelnum, vec3_t angles, vec3_t float max; int i; - mod = Mod_ForName(va("*%i", modelnum), false); - VectorCopy(mod->mins, mins); - VectorCopy(mod->maxs, maxs); + mod = Q3G_GetCModel(modelnum); + if (mod) + { + VectorCopy(mod->mins, mins); + VectorCopy(mod->maxs, maxs); + } + else + { + VectorCopy(vec3_origin, mins); + VectorCopy(vec3_origin, maxs); + } //if the model is rotated if ((angles[0] || angles[1] || angles[2])) @@ -2480,16 +2518,16 @@ void SVQ3Q1_SendGamestateConfigstrings(sizebuf_t *msg) int i, j; - char *str; + const char *str; char sysinfo[MAX_SERVERINFO_STRING]; - char *cfgstr[MAX_CONFIGSTRINGS]; + const char *cfgstr[MAX_CONFIGSTRINGS]; //an empty crc string means we let the client use any //but then it doesn't download our qwoverq3 thing. char *refpackcrcs = "";//"-1309355180 0 0 0 0 0 0 0 0 0"; char *refpacknames = "baseq3/pak0 baseq3/pak1 baseq3/pak2 baseq3/pak3 baseq3/pak4 baseq3/pak5 baseq3/pak6 baseq3/pak7 baseq3/pak8 fte/qwoverq3"; - memset(cfgstr, 0, sizeof(cfgstr)); + memset((void*)cfgstr, 0, sizeof(cfgstr)); sysinfo[0] = 0; Info_SetValueForKey(sysinfo, "sv_serverid", va("%i", svs.spawncount), sizeof(sysinfo)); @@ -2850,7 +2888,7 @@ void SVQ3_ParseUsercmd(client_t *client, qboolean delta) else if(cmdCount > MAX_PACKET_USERCMDS) SV_DropClient(client); - if(client->state <= cs_zombie) + if(client->state < cs_connected) return; // was dropped // calculate key for usercmd decryption @@ -3073,7 +3111,7 @@ void SVQ3_ParseClientMessage(client_t *client) // while(1) { - if(client->state <= cs_zombie) + if(client->state < ca_connected) return; // parsed command caused client to disconnect if(msg_readcount > net_message.cursize) @@ -3128,7 +3166,7 @@ qboolean SVQ3_HandleClient(void) for (i = 0; i < sv.allocated_client_slots; i++) { - if (svs.clients[i].state <= cs_zombie) + if (svs.clients[i].state < cs_connected) continue; if (svs.clients[i].netchan.qport != qport) continue; diff --git a/engine/shaders/hlsl11/default2d.hlsl b/engine/shaders/hlsl11/default2d.hlsl index 4754a923..43707b5d 100644 --- a/engine/shaders/hlsl11/default2d.hlsl +++ b/engine/shaders/hlsl11/default2d.hlsl @@ -17,7 +17,9 @@ struct v2f v2f main (a2v inp) { v2f outp; - outp.pos = mul(m_projection, inp.pos); + outp.pos = mul(m_model, inp.pos); + outp.pos = mul(m_view, outp.pos); + outp.pos = mul(m_projection, outp.pos); outp.tc = inp.tc; outp.vcol = inp.vcol; return outp; diff --git a/engine/shaders/hlsl11/defaultskin.hlsl b/engine/shaders/hlsl11/defaultskin.hlsl index 894a9bb2..10c33374 100644 --- a/engine/shaders/hlsl11/defaultskin.hlsl +++ b/engine/shaders/hlsl11/defaultskin.hlsl @@ -38,6 +38,16 @@ struct v2f { float4 col; col = shaderTexture[0].Sample(SampleType, inp.tc); + + #ifdef MASK + #ifndef MASKOP + #define MASKOP >= //drawn if (alpha OP ref) is true. + #endif + //support for alpha masking + if (!(col.a MASKOP MASK)) + discard; + #endif + #ifdef UPPER float4 uc = shaderTexture[2].Sample(SampleType, inp.tc); col.rgb = mix(col.rgb, uc.rgb*e_uppercolour, uc.a); diff --git a/engine/shaders/hlsl11/defaultwall.hlsl b/engine/shaders/hlsl11/defaultwall.hlsl index 55207b29..f8606d33 100644 --- a/engine/shaders/hlsl11/defaultwall.hlsl +++ b/engine/shaders/hlsl11/defaultwall.hlsl @@ -31,6 +31,18 @@ struct v2f float4 main (v2f inp) : SV_TARGET { - return shaderTexture[0].Sample(SampleType[0], inp.tc) * shaderTexture[1].Sample(SampleType[1], inp.lmtc).bgra; + float4 tex = shaderTexture[0].Sample(SampleType[0], inp.tc); + float4 lm = shaderTexture[1].Sample(SampleType[1], inp.lmtc); + +#ifdef MASK +#ifndef MASKOP +#define MASKOP >= //drawn if (alpha OP ref) is true. +#endif + //support for alpha masking + if (!(tex.a MASKOP MASK)) + discard; +#endif + + return tex * lm.bgra; } #endif \ No newline at end of file diff --git a/engine/shaders/hlsl11/depthonly.hlsl b/engine/shaders/hlsl11/depthonly.hlsl new file mode 100644 index 00000000..830a265c --- /dev/null +++ b/engine/shaders/hlsl11/depthonly.hlsl @@ -0,0 +1,65 @@ +//used for generating shadowmaps and stuff that draws nothing. + +struct a2v +{ + float4 pos: POSITION; + float3 normal: NORMAL; + #ifdef MASK + float4 tc: TEXCOORD0; + #endif +}; +struct v2f +{ + float4 pos: SV_POSITION; + #ifdef MASK + float2 tc: TEXCOORD0; + #endif +}; + +#include + +#ifdef VERTEX_SHADER + v2f main (a2v inp) + { + v2f outp; + outp.pos = mul(m_model, inp.pos); + outp.pos = mul(m_view, outp.pos); + outp.pos = mul(m_projection, outp.pos); + + #ifdef MASK + outp.tc = inp.tc.xy; + #endif + + return outp; + } +#endif +#ifdef FRAGMENT_SHADER + #ifdef MASK + Texture2D shaderTexture[1]; + SamplerState SampleType[1]; + #endif + + #if LEVEL < 1000 + //pre dx10 requires that we ALWAYS write to a target. + float4 main (v2f inp) : SV_TARGET + #else + //but on 10, it'll write depth automatically and we don't care about colour. + void main (v2f inp) //dx10-level + #endif + { + + #ifdef MASK + float alpha = shaderTexture[0].Sample(SampleType[0], inp.tc).a; + #ifndef MASKOP + #define MASKOP >= //drawn if (alpha OP ref) is true. + #endif + //support for alpha masking + if (!(alpha MASKOP MASK)) + discard; + #endif + + #if LEVEL < 1000 + return float4(0, 0, 0, 1); + #endif + } +#endif diff --git a/engine/shaders/hlsl11/rtlight.hlsl b/engine/shaders/hlsl11/rtlight.hlsl new file mode 100644 index 00000000..0326e1de --- /dev/null +++ b/engine/shaders/hlsl11/rtlight.hlsl @@ -0,0 +1,230 @@ +!!permu BUMP +!!permu FRAMEBLEND +!!permu SKELETAL +!!permu UPPERLOWER +!!permu FOG +!!cvarf r_glsl_offsetmapping_scale +!!cvardf r_glsl_pcf + + +//this is the main shader responsible for realtime dlights. + +//texture units: +//s0=diffuse, s1=normal, s2=specular, s3=shadowmap +//custom modifiers: +//PCF(shadowmap) +//CUBEPROJ(projected cubemap) +//SPOT(projected circle +//CUBESHADOW + +#undef CUBE //engine cannot load cubemaps properly with d3d yet. + +#ifndef r_glsl_pcf +#error r_glsl_pcf wasn't defined +#endif +#if r_glsl_pcf < 1 + #undef r_glsl_pcf + #define r_glsl_pcf 9 +#endif + +#ifdef UPPERLOWER +#define UPPER +#define LOWER +#endif + + +struct a2v +{ + float4 pos: POSITION; + float4 tc: TEXCOORD0; + float3 n: NORMAL; + float3 s: TANGENT; + float3 t: BINORMAL; +}; +struct v2f +{ + float4 pos: SV_POSITION; + float2 tc: TEXCOORD0; + float3 lightvector: TEXCOORD1; + float3 eyevector: TEXCOORD2; + float3 vtexprojcoord: TEXCOORD3; +}; + +#include + +#ifdef VERTEX_SHADER + v2f main (a2v inp) + { + v2f outp; + float4 wpos; + wpos = mul(m_model, inp.pos); + outp.pos = mul(m_view, wpos); + outp.pos = mul(m_projection, outp.pos); + outp.tc = inp.tc.xy; + float3 lightminusvertex = l_lightposition - wpos.xyz; + outp.lightvector.x = -dot(lightminusvertex, inp.s.xyz); + outp.lightvector.y = dot(lightminusvertex, inp.t.xyz); + outp.lightvector.z = dot(lightminusvertex, inp.n.xyz); + float3 eyeminusvertex = e_eyepos - wpos.xyz; + outp.eyevector.x = -dot(eyeminusvertex, inp.s.xyz); + outp.eyevector.y = dot(eyeminusvertex, inp.t.xyz); + outp.eyevector.z = dot(eyeminusvertex, inp.n.xyz); + outp.vtexprojcoord = mul(l_cubematrix, wpos).xyz; + return outp; + } +#endif + + +#ifdef FRAGMENT_SHADER + + Texture2D tx_base : register(t0); + Texture2D tx_bump : register(t1); + Texture2D tx_spec : register(t2); + TextureCube tx_cube : register(t3); + Texture2D tx_smap : register(t4); + Texture2D tx_lower : register(t5); + Texture2D tx_upper : register(t6); + + SamplerState ss_base : register(s0); + SamplerState ss_bump : register(s1); + SamplerState ss_spec : register(s2); + SamplerState ss_cube : register(s3); + SamplerComparisonState ss_smap : register(s4); + SamplerState ss_lower : register(s5); + SamplerState ss_upper : register(s6); + +#ifdef PCF +float3 ShadowmapCoord(float3 vtexprojcoord) +{ +#ifdef SPOT + //bias it. don't bother figuring out which side or anything, its not needed + //l_projmatrix contains the light's projection matrix so no other magic needed + vtexprojcoord.z -= 0.015; + return (vtexprojcoord.xyz + float3(1.0, 1.0, 1.0)) * float3(0.5, 0.5, 0.5); +//#elif defined(CUBESHADOW) +// vec3 shadowcoord = vshadowcoord.xyz / vshadowcoord.w; +// #define dosamp(x,y) shadowCube(s_t4, shadowcoord + vec2(x,y)*texscale.xy).r +#else + //figure out which axis to use + //texture is arranged thusly: + //forward left up + //back right down + float3 dir = abs(vtexprojcoord.xyz); + //assume z is the major axis (ie: forward from the light) + float3 t = vtexprojcoord.xyz; + float ma = dir.z; + float3 axis = float3(0.5/3.0, 0.5/2.0, 0.5); + if (dir.x > ma) + { + ma = dir.x; + t = vtexprojcoord.zyx; + axis.x = 0.5; + } + if (dir.y > ma) + { + ma = dir.y; + t = vtexprojcoord.xzy; + axis.x = 2.5/3.0; + } + //if the axis is negative, flip it. + if (t.z > 0.0) + { + axis.y = 1.5/2.0; + t.z = -t.z; + } + + //we also need to pass the result through the light's projection matrix too + //the 'matrix' we need only contains 5 actual values. and one of them is a -1. So we might as well just use a vec4. + //note: the projection matrix also includes scalers to pinch the image inwards to avoid sampling over borders, as well as to cope with non-square source image + //the resulting z is prescaled to result in a value between -0.5 and 0.5. + //also make sure we're in the right quadrant type thing + return axis + ((l_shadowmapproj.xyz*t.xyz + float3(0.0, 0.0, l_shadowmapproj.w)) / -t.z); +#endif +} + +float ShadowmapFilter(float3 vtexprojcoord) +{ + float3 shadowcoord = ShadowmapCoord(vtexprojcoord); + +// #define dosamp(x,y) shadow2D(s_t4, shadowcoord.xyz + (vec3(x,y,0.0)*l_shadowmapscale.xyx)).r + +// #define dosamp(x,y) (tx_smap.Sample(ss_smap, shadowcoord.xy + (float2(x,y)*l_shadowmapscale.xy)).r < shadowcoord.z) + #define dosamp(x,y) (tx_smap.SampleCmpLevelZero(ss_smap, shadowcoord.xy+(float2(x,y)*l_shadowmapscale.xy), shadowcoord.z)) + + + float s = 0.0; + #if r_glsl_pcf >= 1 && r_glsl_pcf < 5 + s += dosamp(0.0, 0.0); + return s; + #elif r_glsl_pcf >= 5 && r_glsl_pcf < 9 + s += dosamp(-1.0, 0.0); + s += dosamp(0.0, -1.0); + s += dosamp(0.0, 0.0); + s += dosamp(0.0, 1.0); + s += dosamp(1.0, 0.0); + return s/5.0; + #else + s += dosamp(-1.0, -1.0); + s += dosamp(-1.0, 0.0); + s += dosamp(-1.0, 1.0); + s += dosamp(0.0, -1.0); + s += dosamp(0.0, 0.0); + s += dosamp(0.0, 1.0); + s += dosamp(1.0, -1.0); + s += dosamp(1.0, 0.0); + s += dosamp(1.0, 1.0); + return s/9.0; + #endif +} +#endif + + + float4 main (v2f inp) : SV_TARGET + { + float2 tc = inp.tc; //TODO: offsetmapping. + + float4 base = tx_base.Sample(ss_base, tc); +#ifdef BUMP + float4 bump = tx_bump.Sample(ss_bump, tc); + bump.rgb = normalize(bump.rgb - 0.5); +#else + float4 bump = float4(0, 0, 1, 0); +#endif + float4 spec = tx_spec.Sample(ss_spec, tc); +#ifdef CUBE + float4 cubemap = tx_cube.Sample(ss_cube, inp.vtexprojcoord); +#endif +#ifdef LOWER + float4 lower = tx_lower.Sample(ss_lower, tc); + base += lower; +#endif +#ifdef UPPER + float4 upper = tx_upper.Sample(ss_upper, tc); + base += upper; +#endif + + float lightscale = max(1.0 - (dot(inp.lightvector,inp.lightvector)/(l_lightradius*l_lightradius)), 0.0); + float3 nl = normalize(inp.lightvector); + float bumpscale = max(dot(bump.xyz, nl), 0.0); + float3 halfdir = normalize(normalize(inp.eyevector) + nl); + float specscale = pow(max(dot(halfdir, bump.rgb), 0.0), 32.0 * spec.a); + + float4 result; + result.a = base.a; + result.rgb = base.rgb * (l_lightcolourscale.x + l_lightcolourscale.y * bumpscale); //amient light + diffuse + result.rgb += spec.rgb * l_lightcolourscale.z * specscale; //specular + + result.rgb *= lightscale * l_colour; //fade light by distance and light colour. + +#ifdef CUBE + result.rgb *= cubemap.rgb; //fade by cubemap +#endif + +#ifdef PCF + result.rgb *= ShadowmapFilter(inp.vtexprojcoord); +#endif + + //TODO: fog + return result; + } +#endif diff --git a/engine/sw/sw.h b/engine/sw/sw.h index 449258f8..cf13f647 100644 --- a/engine/sw/sw.h +++ b/engine/sw/sw.h @@ -158,13 +158,13 @@ void SW_VID_UpdateViewport(wqcom_t *com); -texid_tf SW_LoadTexture(char *identifier, int width, int height, uploadfmt_t fmt, void *data, unsigned int flags); -texid_tf SW_LoadTexture8Pal24(char *identifier, int width, int height, qbyte *data, qbyte *palette24, unsigned int flags); -texid_tf SW_LoadTexture8Pal32(char *identifier, int width, int height, qbyte *data, qbyte *palette32, unsigned int flags); -texid_tf SW_LoadCompressed(char *name); -texid_tf SW_FindTexture(char *identifier, unsigned int flags); -texid_tf SW_AllocNewTexture(char *identifier, int w, int h, unsigned int flags); -void SW_Upload(texid_t tex, char *name, uploadfmt_t fmt, void *data, void *palette, int width, int height, unsigned int flags); +texid_tf SW_LoadTexture(const char *identifier, int width, int height, uploadfmt_t fmt, void *data, unsigned int flags); +texid_tf SW_LoadTexture8Pal24(const char *identifier, int width, int height, qbyte *data, qbyte *palette24, unsigned int flags); +texid_tf SW_LoadTexture8Pal32(const char *identifier, int width, int height, qbyte *data, qbyte *palette32, unsigned int flags); +texid_tf SW_LoadCompressed(const char *name); +texid_tf SW_FindTexture(const char *identifier, unsigned int flags); +texid_tf SW_AllocNewTexture(const char *identifier, int w, int h, unsigned int flags); +void SW_Upload(texid_t tex, const char *name, uploadfmt_t fmt, void *data, void *palette, int width, int height, unsigned int flags); void SW_DestroyTexture(texid_t tex); @@ -179,6 +179,7 @@ void SWBE_GenBrushModelVBO(struct model_s *mod); void SWBE_ClearVBO(struct vbo_s *vbo); void SWBE_UploadAllLightmaps(void); void SWBE_SelectEntity(struct entity_s *ent); -void SWBE_SelectDLight(struct dlight_s *dl, vec3_t colour); +qboolean SWBE_SelectDLight(struct dlight_s *dl, vec3_t colour, unsigned int lmode); qboolean SWBE_LightCullModel(vec3_t org, struct model_s *model); +void SWBE_RenderToTextureUpdate2d(qboolean destchanged); void SWBE_Set2D(void); diff --git a/engine/sw/sw_backend.c b/engine/sw/sw_backend.c index 03a25419..b80658db 100644 --- a/engine/sw/sw_backend.c +++ b/engine/sw/sw_backend.c @@ -120,10 +120,10 @@ static void Matrix3_Multiply_Vec3 (mat3_t a, vec3_t b, vec3_t product) product[2] = a[2][0]*b[0] + a[2][1]*b[1] + a[2][2]*b[2]; } -static int Matrix3_Compare(mat3_t in, mat3_t out) -{ - return memcmp(in, out, sizeof(mat3_t)); -} +//static int Matrix3_Compare(mat3_t in, mat3_t out) +//{ +// return memcmp(in, out, sizeof(mat3_t)); +//} //end matrix functions //////////////////////////////////////////////////////////////// @@ -652,11 +652,16 @@ void SWBE_SelectEntity(struct entity_s *ent) SWBE_UpdateUniforms(); } -void SWBE_SelectDLight(struct dlight_s *dl, vec3_t colour) +qboolean SWBE_SelectDLight(struct dlight_s *dl, vec3_t colour, unsigned int lmode) { + return false; } qboolean SWBE_LightCullModel(vec3_t org, struct model_s *model) { return false; } + +void SWBE_RenderToTextureUpdate2d(qboolean destchanged) +{ +} #endif diff --git a/engine/sw/sw_image.c b/engine/sw/sw_image.c index 167086ae..cda4d4e3 100644 --- a/engine/sw/sw_image.c +++ b/engine/sw/sw_image.c @@ -20,7 +20,7 @@ void SW_RoundDimensions(int width, int height, int *scaled_width, int *scaled_he *scaled_height = 256; } -texid_tf SW_AllocNewTexture(char *identifier, int w, int h, unsigned int flags) +texid_tf SW_AllocNewTexture(const char *identifier, int w, int h, unsigned int flags) { int nw, nh; texid_t n; @@ -45,7 +45,7 @@ texid_tf SW_AllocNewTexture(char *identifier, int w, int h, unsigned int flags) n.ref = &img->com; return n; } -texid_tf SW_FindTexture(char *identifier, unsigned int flags) +texid_tf SW_FindTexture(const char *identifier, unsigned int flags) { return r_nulltex; } @@ -111,7 +111,7 @@ void SW_Upload8(swimage_t *img, int iw, int ih, unsigned char *data) SW_RGBToBGR(img); } -texid_tf SW_LoadTexture(char *identifier, int width, int height, uploadfmt_t fmt, void *data, unsigned int flags) +texid_tf SW_LoadTexture(const char *identifier, int width, int height, uploadfmt_t fmt, void *data, unsigned int flags) { texid_t img = SW_FindTexture(identifier, flags); if (!img.ptr) @@ -139,19 +139,19 @@ texid_tf SW_LoadTexture(char *identifier, int width, int height, uploadfmt_t fmt } return img; } -texid_tf SW_LoadTexture8Pal24(char *identifier, int width, int height, qbyte *data, qbyte *palette24, unsigned int flags) +texid_tf SW_LoadTexture8Pal24(const char *identifier, int width, int height, qbyte *data, qbyte *palette24, unsigned int flags) { return r_nulltex; } -texid_tf SW_LoadTexture8Pal32(char *identifier, int width, int height, qbyte *data, qbyte *palette32, unsigned int flags) +texid_tf SW_LoadTexture8Pal32(const char *identifier, int width, int height, qbyte *data, qbyte *palette32, unsigned int flags) { return r_nulltex; } -texid_tf SW_LoadCompressed(char *name) +texid_tf SW_LoadCompressed(const char *name) { return r_nulltex; } -void SW_Upload(texid_t tex, char *name, uploadfmt_t fmt, void *data, void *palette, int width, int height, unsigned int flags) +void SW_Upload(texid_t tex, const char *name, uploadfmt_t fmt, void *data, void *palette, int width, int height, unsigned int flags) { } void SW_DestroyTexture(texid_t tex) diff --git a/engine/sw/sw_rast.c b/engine/sw/sw_rast.c index c31cef23..2b04dce2 100644 --- a/engine/sw/sw_rast.c +++ b/engine/sw/sw_rast.c @@ -1041,9 +1041,10 @@ rendererinfo_t swrendererinfo = SW_VID_Init, SW_VID_DeInit, + SW_VID_SwapBuffers, SW_VID_ApplyGammaRamps, - SW_VID_GetRGBInfo, SW_VID_SetWindowCaption, + SW_VID_GetRGBInfo, SW_SCR_UpdateScreen, @@ -1067,6 +1068,8 @@ rendererinfo_t swrendererinfo = SW_VBO_Finish, SW_VBO_Destroy, + SWBE_RenderToTextureUpdate2d, + "no more" }; #endif diff --git a/plugins/jabber/jabberclient.c b/plugins/jabber/jabberclient.c index 334eb850..58b4f58e 100644 --- a/plugins/jabber/jabberclient.c +++ b/plugins/jabber/jabberclient.c @@ -2494,7 +2494,6 @@ static int qsortcaps(const void *va, const void *vb) char *b = *(char**)vb; return strcmp(a, b); } -int SHA1(char *digest, int maxdigestsize, char *string, int stringlen); char *buildcapshash(jclient_t *jcl) { int i, l; diff --git a/plugins/mpq/fs_mpq.c b/plugins/mpq/fs_mpq.c index 3744dc0d..bc0e02f3 100644 --- a/plugins/mpq/fs_mpq.c +++ b/plugins/mpq/fs_mpq.c @@ -305,14 +305,25 @@ static void MPQ_ReadFile(searchpathfuncs_t *handle, flocation_t *loc, char *buff static int mpqwildcmp(const char *wild, const char *string, char **end) { + int s, w; while (*string) { - if (*string == '\r' || *string == '\n' || *string == ';') + s = *string; + if (s == '\r' || s == '\n' || s == ';') break; + w = *wild; - if (*wild == '*') + if (s >= 'A' && s <= 'Z') + s = s-'A'+'a'; + if (w >= 'A' && w <= 'Z') + w = w-'A'+'a'; + + if (w == '*') { - if (wild[1] == *string || *string == '/' || *string == '\\') + w = wild[1]; + if (w >= 'A' && w <= 'Z') + w = w-'A'+'a'; + if (w == s || s == '/' || s == '\\') { //* terminates if we get a match on the char following it, or if its a \ or / char wild++; @@ -320,7 +331,7 @@ static int mpqwildcmp(const char *wild, const char *string, char **end) } string++; } - else if ((*wild == *string) || (*wild == '?')) + else if ((w == s) || (w == '?')) { //this char matches wild++; @@ -770,7 +781,7 @@ static qofs_t MPQF_getlen (struct vfsfile_s *file) mpqfile_t *f = (mpqfile_t *)file; return f->flength; } -static void MPQF_close (struct vfsfile_s *file) +static qboolean MPQF_close (struct vfsfile_s *file) { mpqfile_t *f = (mpqfile_t *)file; if (f->buffer) @@ -778,6 +789,8 @@ static void MPQF_close (struct vfsfile_s *file) if (f->sectortab) free(f->sectortab); free(f); + + return true; } static void MPQF_flush (struct vfsfile_s *file) { diff --git a/plugins/mpq/fs_mpq.vcproj b/plugins/mpq/fs_mpq.vcproj index de9228fc..e504531b 100644 --- a/plugins/mpq/fs_mpq.vcproj +++ b/plugins/mpq/fs_mpq.vcproj @@ -95,83 +95,6 @@ Name="VCPostBuildEventTool" /> - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + +