diff --git a/engine/client/cl_demo.c b/engine/client/cl_demo.c index ee66ff19f..a55c5b66e 100644 --- a/engine/client/cl_demo.c +++ b/engine/client/cl_demo.c @@ -1802,7 +1802,7 @@ void CL_QTVPoll (void) int len; qboolean streamavailable = false; qboolean saidheader = false; -#ifndef NOBUITINMENUS +#ifndef NOBUILTINMENUS menu_t *sourcesmenu = NULL; #endif int sourcenum = 0; @@ -1971,7 +1971,7 @@ void CL_QTVPoll (void) { streamid = atoi(colon); -#ifndef NOBUITINMENUS +#ifndef NOBUILTINMENUS //now put it on a menu if (!sourcesmenu) { diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 17378108e..4ebce8914 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -3908,6 +3908,8 @@ void Host_BeginFileDownload(struct dl_download *dl, char *mimetype) f->flags |= HRF_QTVINFO; else if (!strcmp(mimetype, "text/x-quaketvident")) f->flags |= HRF_QTVINFO; + else if (!strcmp(mimetype, "application/x-fteplugin")) + f->flags |= HRF_MANIFEST; else if (!strcmp(mimetype, "application/x-ftemanifest")) f->flags |= HRF_MANIFEST; else if (!strcmp(mimetype, "application/x-multiviewdemo")) @@ -3916,6 +3918,9 @@ void Host_BeginFileDownload(struct dl_download *dl, char *mimetype) // f->flags |= HRF_BSP; // else if (!strcmp(mimetype, "application/x-ftepackage")) // f->flags |= HRF_PACKAGE; + + if (f->flags & HRF_MANIFEST) + waitingformanifest++; } if (!(f->flags & HRF_FILETYPES)) @@ -4001,7 +4006,7 @@ static qboolean isurl(char *url) #ifdef FTE_TARGET_WEB return true; //assume EVERYTHING is a url, because the local filesystem is pointless. #endif - return !strncmp(url, "http://", 7) || !strncmp(url, "https://", 8); + return /*!strncmp(url, "data:", 5) || */!strncmp(url, "http://", 7) || !strncmp(url, "https://", 8); } void Host_DoRunFile(hrf_t *f) @@ -4041,13 +4046,17 @@ void Host_DoRunFile(hrf_t *f) if (!(f->flags & HRF_OPENED)) { struct dl_download *dl; - f->flags |= HRF_OPENED|HRF_DOWNLOADED|HRF_WAITING; + f->flags |= HRF_OPENED; dl = HTTP_CL_Get(f->fname, NULL, Host_RunFileDownloaded); - dl->notifystarted = Host_BeginFileDownload; - dl->user_ctx = f; + if (dl) + { + f->flags |= HRF_WAITING|HRF_DOWNLOADED; + dl->notifystarted = Host_BeginFileDownload; + dl->user_ctx = f; - waitingformanifest++; - return; + waitingformanifest++; + return; + } } } #endif @@ -4169,9 +4178,12 @@ void Host_DoRunFile(hrf_t *f) if (isurl(f->fname)) { struct dl_download *dl = HTTP_CL_Get(f->fname, NULL, Host_RunFileDownloaded); - dl->notifystarted = Host_BeginFileDownload; - dl->user_ctx = f; - return; + if (dl) + { + dl->notifystarted = Host_BeginFileDownload; + dl->user_ctx = f; + return; + } } #endif f->srcfile = VFSOS_Open(f->fname, "rb"); //input file is a system path, or something. @@ -4733,7 +4745,7 @@ void CL_StartCinematicOrMenu(void) { if (qrenderer > QR_NONE && !m_state) { -#ifndef NOBUITINMENUS +#ifndef NOBUILTINMENUS if (!cls.state && !m_state && !*FS_GetGamedir(false)) M_Menu_Mods_f(); #endif diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index d706cdbfd..584d89205 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -4341,7 +4341,7 @@ CL_NewTranslation void CL_NewTranslation (int slot) { int top, bottom; - int local; + int local; char *s; player_info_t *player; @@ -4351,10 +4351,33 @@ void CL_NewTranslation (int slot) player = &cl.players[slot]; + if (cls.protocol == CP_QUAKE2) + { + char *mod, *skin; + player->qwskin = NULL; + player->skinid = 0; + player->model = NULL; + player->ttopcolor = TOP_DEFAULT; + player->tbottomcolor = BOTTOM_DEFAULT; + + mod = Info_ValueForKey(player->userinfo, "skin"); + skin = strchr(mod, '/'); + if (skin) + *skin++ = 0; + + player->model = Mod_ForName(va("players/%s/tris.md2", mod), 0); + player->skinid = Mod_RegisterSkinFile(va("players/%s/%s.skin", mod,skin)); + if (!player->skinid) + player->skinid = Mod_ReadSkinFile(va("players/%s/%s.skin", mod,skin), va("replace \"\" \"players/%s/%s.pcx\"", mod,skin)); + return; + } + s = Skin_FindName (player); COM_StripExtension(s, s, MAX_QPATH); - if (player->skin && !stricmp(s, player->skin->name)) - player->skin = NULL; + if (player->qwskin && !stricmp(s, player->qwskin->name)) + player->qwskin = NULL; + player->skinid = 0; + player->model = NULL; diff --git a/engine/client/cl_screen.c b/engine/client/cl_screen.c index 40ece19f8..c0a3d3ffd 100644 --- a/engine/client/cl_screen.c +++ b/engine/client/cl_screen.c @@ -226,7 +226,7 @@ void SCR_ScreenShot_f (void); void SCR_RSShot_f (void); void SCR_CPrint_f(void); -cvar_t con_stayhidden = CVARFD("con_stayhidden", "1", CVAR_NOTFROMSERVER, "0: allow console to pounce on the user\n1: console stays hidden unless explicitly invoked\n2:toggleconsole command no longer works\n3: shift+escape key no longer works"); +cvar_t con_stayhidden = CVARFD("con_stayhidden", "0", CVAR_NOTFROMSERVER, "0: allow console to pounce on the user\n1: console stays hidden unless explicitly invoked\n2:toggleconsole command no longer works\n3: shift+escape key no longer works"); cvar_t show_fps = SCVARF("show_fps", "0", CVAR_ARCHIVE); cvar_t show_fps_x = SCVAR("show_fps_x", "-1"); cvar_t show_fps_y = SCVAR("show_fps_y", "-1"); diff --git a/engine/client/client.h b/engine/client/client.h index 3f242ac53..b4b4c2f3c 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -21,7 +21,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "particles.h" -typedef struct +typedef struct qwskin_s { char name[64]; int width; @@ -32,7 +32,7 @@ typedef struct qboolean failedload; // the name isn't a valid skin void *skindata; -} skin_t; +} qwskin_t; // player_state_t is the information needed by a player entity // to do move prediction and to generate a drawable entity @@ -168,8 +168,9 @@ typedef struct player_info_s unsigned int ttopcolor; //team, according to colour forcing unsigned int tbottomcolor; - int spectator; - skin_t *skin; + int spectator; + qwskin_t *qwskin; + skinid_t skinid; struct model_s *model; @@ -1314,10 +1315,10 @@ typedef struct qbyte *ReadPCXData(qbyte *buf, int length, int width, int height, qbyte *result); +qwskin_t *Skin_Lookup (char *fullname); char *Skin_FindName (player_info_t *sc); void Skin_Find (player_info_t *sc); -qbyte *Skin_Cache8 (skin_t *skin); -qbyte *Skin_Cache32 (skin_t *skin); +qbyte *Skin_Cache8 (qwskin_t *skin); void Skin_Skins_f (void); void Skin_FlushSkin(char *name); void Skin_AllSkins_f (void); diff --git a/engine/client/clq2_ents.c b/engine/client/clq2_ents.c index c1125f5ff..22d455666 100644 --- a/engine/client/clq2_ents.c +++ b/engine/client/clq2_ents.c @@ -1228,6 +1228,7 @@ void CLQ2_AddPacketEntities (q2frame_t *frame) ent.bottomcolour = 1; ent.h2playerclass = 0; ent.playerindex = -1; + ent.customskin = 0; // set frame if (effects & Q2EF_ANIM01) @@ -1315,11 +1316,14 @@ void CLQ2_AddPacketEntities (q2frame_t *frame) ent.model = player->model; if (!ent.model || ent.model->needload) //we need to do better than this { - if (!ent.model || ent.model->needload) - ent.model = Mod_ForName("players/male/tris.md2", MLV_SILENT); + ent.model = Mod_ForName("players/male/tris.md2", MLV_SILENT); + ent.customskin = Mod_RegisterSkinFile("players/male/grunt.skin"); + if (!ent.customskin) + ent.customskin = Mod_ReadSkinFile("players/male/grunt.skin", "replace \"\" \"players/male/grunt.pcx\""); } + else + ent.customskin = player->skinid; ent.playerindex = (s1->skinnum&0xff)%cl.allocated_client_slots; - player->model = ent.model; /* ci = &cl.clientinfo[s1->skinnum & 0xff]; // ent.skin = ci->skin; ent.model = ci->model; @@ -1813,6 +1817,8 @@ void CLQ2_AddViewWeapon (q2player_state_t *ps, q2player_state_t *ops) else gun.framestate.g[FS_REG].frame[1] = ops->gunframe; + gun.playerindex = -1; + gun.flags = Q2RF_MINLIGHT | RF_DEPTHHACK | RF_WEAPONMODEL; gun.framestate.g[FS_REG].lerpfrac = 1-cl.lerpfrac; VectorCopy (gun.origin, gun.oldorigin); // don't lerp at all @@ -1926,6 +1932,7 @@ Emits all entities, particles, and lights to the refresh */ void CLQ2_AddEntities (void) { + extern cvar_t chase_active, chase_back, chase_up; if (cls.state != ca_active) return; @@ -1952,6 +1959,22 @@ void CLQ2_AddEntities (void) CLQ2_AddProjectiles (); #endif CL_UpdateTEnts (); + + + if (chase_active.ival) + { + playerview_t *pv = &cl.playerview[0]; + vec3_t axis[3]; + vec3_t camorg; + trace_t tr; + AngleVectors(r_refdef.viewangles, axis[0], axis[1], axis[2]); + VectorMA(r_refdef.vieworg, -chase_back.value, axis[0], camorg); + VectorMA(camorg, -chase_up.value, pv->gravitydir, camorg); +// if (cl.worldmodel && cl.worldmodel->funcs.NativeTrace(cl.worldmodel, 0, 0, NULL, r_refdef.vieworg, camorg, vec3_origin, vec3_origin, MASK_WORLDSOLID, &tr)) + VectorCopy(camorg, r_refdef.vieworg); + + CL_EditExternalModels(0, NULL, 0); + } } void CL_GetNumberedEntityInfo (int num, float *org, float *ang) diff --git a/engine/client/m_download.c b/engine/client/m_download.c index 57d44bc39..dcf33363b 100644 --- a/engine/client/m_download.c +++ b/engine/client/m_download.c @@ -1,6 +1,6 @@ #include "quakedef.h" -#if defined(WEBCLIENT) && !defined(NOBUITINMENUS) +#if defined(WEBCLIENT) && !defined(NOBUILTINMENUS) #define DOWNLOADMENU #endif diff --git a/engine/client/m_items.c b/engine/client/m_items.c index 5fb014d6e..a2ef2e643 100644 --- a/engine/client/m_items.c +++ b/engine/client/m_items.c @@ -76,7 +76,7 @@ void Draw_TextBox (int x, int y, int width, int lines) R2D_ScalePic (cx, cy+8, 8, 8, p); } -#ifndef NOBUITINMENUS +#ifndef NOBUILTINMENUS int omousex; int omousey; diff --git a/engine/client/m_master.c b/engine/client/m_master.c index a9e2512db..b1919f149 100644 --- a/engine/client/m_master.c +++ b/engine/client/m_master.c @@ -1,6 +1,6 @@ #include "quakedef.h" -#if defined(CL_MASTER) && !defined(NOBUITINMENUS) +#if defined(CL_MASTER) && !defined(NOBUILTINMENUS) #include "cl_master.h" //filtering diff --git a/engine/client/m_mp3.c b/engine/client/m_mp3.c index d868ffe5c..e12554ce3 100644 --- a/engine/client/m_mp3.c +++ b/engine/client/m_mp3.c @@ -743,7 +743,7 @@ void M_Media_Key (int key) int dir; if (key == K_ESCAPE) { -#ifndef NOBUITINMENUS +#ifndef NOBUILTINMENUS M_Menu_Main_f(); #else m_state = m_none; diff --git a/engine/client/m_multi.c b/engine/client/m_multi.c index fa37ab8a4..f4d1a5be3 100644 --- a/engine/client/m_multi.c +++ b/engine/client/m_multi.c @@ -4,7 +4,7 @@ #include "winquake.h" #include "shader.h" -#ifndef NOBUITINMENUS +#ifndef NOBUILTINMENUS extern cvar_t maxclients; diff --git a/engine/client/m_options.c b/engine/client/m_options.c index 578b64281..a1f5b6e32 100644 --- a/engine/client/m_options.c +++ b/engine/client/m_options.c @@ -93,7 +93,7 @@ qboolean M_Vid_GetMode(int num, int *w, int *h) -#ifndef NOBUITINMENUS +#ifndef NOBUILTINMENUS extern qboolean forcesaveprompt; extern cvar_t pr_debugger; @@ -712,6 +712,7 @@ const char *presetexec[] = "gl_texturemode2d n;" //yeah, 2d too. "r_part_classic_square 1;" //blocky baby! "r_part_classic_expgrav 1;" //vanillaery + "r_particlesystem script;" //q2 or hexen2 particle effects need to be loadable "cl_sbar 1;" //its a style thing "sv_nqplayerphysics 1;" //gb wanted this "cl_demoreel 1;" //yup, arcadey diff --git a/engine/client/m_script.c b/engine/client/m_script.c index 2423d2ae2..c66a3ba80 100644 --- a/engine/client/m_script.c +++ b/engine/client/m_script.c @@ -3,7 +3,7 @@ #include "quakedef.h" #include "shader.h" -#ifndef NOBUITINMENUS +#ifndef NOBUILTINMENUS int selectitem; menu_t *menu_script; diff --git a/engine/client/menu.c b/engine/client/menu.c index bb1102638..d42ad5148 100644 --- a/engine/client/menu.c +++ b/engine/client/menu.c @@ -154,7 +154,7 @@ void M_FindKeysForCommand (int pnum, const char *command, int *twokeys) M_FindKeysForBind(va("%s%s", prefix, command), twokeys, 2); } -#ifndef NOBUITINMENUS +#ifndef NOBUILTINMENUS void M_Menu_Audio_f (void); void M_Menu_Demos_f (void); @@ -1270,7 +1270,7 @@ void M_Draw (int uimenu) #endif } -#ifndef NOBUITINMENUS +#ifndef NOBUILTINMENUS if (m_state != m_complex) { M_RemoveAllMenus(); @@ -1286,7 +1286,7 @@ void M_Draw (int uimenu) if (m_state == m_none || m_state == m_menu_dat) return; -#ifndef NOBUITINMENUS +#ifndef NOBUILTINMENUS if ((!menu_script || scr_con_current) && !m_recursiveDraw) { extern menu_t *firstmenu; @@ -1308,7 +1308,7 @@ void M_Draw (int uimenu) case m_none: break; -#ifndef NOBUITINMENUS +#ifndef NOBUILTINMENUS case m_help: M_Help_Draw (); break; @@ -1345,7 +1345,7 @@ void M_Keydown (int key, int unicode) case m_none: Key_Dest_Remove(kdm_menu); return; -#ifndef NOBUITINMENUS +#ifndef NOBUILTINMENUS case m_help: M_Help_Key (key); return; @@ -1380,7 +1380,7 @@ void M_Keyup (int key, int unicode) { switch (m_state) { -#ifndef NOBUITINMENUS +#ifndef NOBUILTINMENUS case m_complex: if (key == K_MOUSE1) M_Complex_Key (key, unicode); diff --git a/engine/client/menu.h b/engine/client/menu.h index 43b1d7346..ca0ac1250 100644 --- a/engine/client/menu.h +++ b/engine/client/menu.h @@ -102,7 +102,7 @@ void M_DrawTextBox (int x, int y, int width, int lines); #define NOMEDIAMENU -#ifndef NOBUITINMENUS +#ifndef NOBUILTINMENUS // // menus diff --git a/engine/client/render.h b/engine/client/render.h index 9078b8a5c..99714fb9f 100644 --- a/engine/client/render.h +++ b/engine/client/render.h @@ -148,6 +148,8 @@ typedef struct int nummappings; int maxmappings; qbyte geomset[MAX_GEOMSETS]; //allows selecting a single set of geometry from alternatives. this might be a can of worms. + char qwskinname[MAX_QPATH]; + struct qwskin_s *qwskin; struct { char surface[MAX_QPATH]; @@ -266,7 +268,7 @@ extern entity_t r_worldentity; void BE_GenModelBatches(struct batch_s **batches, const struct dlight_s *dl, unsigned int bemode); //if dl, filters based upon the dlight. //gl_alias.c -void GL_GAliasFlushSkinCache(void); +void R_GAliasFlushSkinCache(qboolean final); void R_GAlias_DrawBatch(struct batch_s *batch); void R_GAlias_GenerateBatches(entity_t *e, struct batch_s **batches); void R_LightArraysByte_BGR(const entity_t *entity, vecV_t *coords, byte_vec4_t *colours, int vertcount, vec3_t *normals); diff --git a/engine/client/sbar.c b/engine/client/sbar.c index 42803416f..0214c14aa 100644 --- a/engine/client/sbar.c +++ b/engine/client/sbar.c @@ -411,7 +411,7 @@ void Sbar_ExecuteLayoutString (char *s) Draw_FunString (x+32, y+16, va("Ping: %i", ping)); Draw_FunString (x+32, y+24, va("Time: %i", time)); - p = R2D_SafeCachePic(va("players/%s_i.pcx", cl.players[value].skin->name)); + p = R2D_SafeCachePic(va("players/%s_i.pcx", cl.players[value].qwskin->name)); if (!p) //display a default if the icon couldn't be found. p = R2D_SafeCachePic("players/male/grunt_i.pcx"); R2D_ScalePic (x, y, 32, 32, p); diff --git a/engine/client/skin.c b/engine/client/skin.c index 610f74c1e..0e2ea742b 100644 --- a/engine/client/skin.c +++ b/engine/client/skin.c @@ -30,8 +30,8 @@ extern cvar_t cl_enemyskin; extern cvar_t r_fb_models; char allskins[128]; -#define MAX_CACHED_SKINS 128 -skin_t skins[MAX_CACHED_SKINS]; +#define MAX_CACHED_SKINS 256 //max_clients is 255. hopefully this will not be reached, but hey. +qwskin_t skins[MAX_CACHED_SKINS]; int numskins; //returns the name @@ -108,6 +108,37 @@ char *Skin_FindName (player_info_t *sc) return name; } +qwskin_t *Skin_Lookup (char *fullname) +{ + int i; + qwskin_t *skin; + char cleanname[sizeof(skin->name)]; + COM_StripExtension (fullname, cleanname, sizeof(cleanname)); + + for (i=0 ; iname, cleanname, sizeof(skin->name)); + Skin_Cache8 (skin); + return skin; +} /* ================ Skin_Find @@ -119,52 +150,35 @@ Skin_Find */ void Skin_Find (player_info_t *sc) { - skin_t *skin; + qwskin_t *skin; int i; char name[128], *s; - model_t *model; if (allskins[0]) s = allskins; else s = Info_ValueForKey (sc->userinfo, "skin"); + sc->model = NULL; + sc->skinid = 0; + sc->qwskin = NULL; + if (!*s) s = baseskin.string; if (!*s) - s = "default"; + return; +// s = "default"; s = Skin_FindName(sc); COM_StripExtension (s, name, sizeof(name)); - s = strchr(name, '/'); - if (s) - { - *s = '\0'; -#ifdef Q2CLIENT - if (cls.protocol == CP_QUAKE2) - model = Mod_ForName(va("players/%s/tris.md2", name), MLV_SILENT); - else -#endif - model = NULL;//Mod_ForName(va("models/players/%s.mdl", name), false); - if (model && model->type == mod_dummy) - model = NULL; - *s = '/'; - } - else - model = NULL; - - sc->model = model; for (i=0 ; iskin = &skins[i]; - if (cls.protocol == CP_QUAKE2) - Skin_Cache32 (sc->skin); - else - Skin_Cache8 (sc->skin); + sc->qwskin = &skins[i]; + Skin_Cache8 (sc->qwskin); return; } } @@ -176,7 +190,7 @@ void Skin_Find (player_info_t *sc) } skin = &skins[numskins]; - sc->skin = skin; + sc->qwskin = skin; numskins++; memset (skin, 0, sizeof(*skin)); @@ -191,7 +205,7 @@ Skin_Cache Returns a pointer to the skin bitmap, or NULL to use the default ========== */ -qbyte *Skin_Cache8 (skin_t *skin) +qbyte *Skin_Cache8 (qwskin_t *skin) { char name[1024]; qbyte *raw; @@ -251,12 +265,7 @@ qbyte *Skin_Cache8 (skin_t *skin) return out; } -#ifdef Q2CLIENT - if (cls.protocol == CP_QUAKE2) - skinpath = "players"; - else -#endif - skinpath = "skins"; + skinpath = "skins"; //favour 24bit+recoloured skins if gl_load24bit is enabled. Q_snprintfz (name, sizeof(name), "%s_shirt", skin->name); @@ -431,106 +440,6 @@ qbyte *Skin_Cache8 (skin_t *skin) return out; } -qbyte *Skin_Cache32 (skin_t *skin) -{ - char name[1024]; - qbyte *raw; - qbyte *out, *pix; - char *path; - qboolean hasalpha; - - if (noskins.value==1) // JACK: So NOSKINS > 1 will show skins, but - return NULL; // not download new ones. - - if (skin->failedload) - return NULL; - - out = skin->skindata; - if (out) - return out; - - if (cls.protocol == CP_QUAKE2) - path = "players/"; - else - path = "skins/"; - -// -// load the pic from disk -// - Q_snprintfz (name, sizeof(name), "%s%s.tga", path, skin->name); - raw = COM_LoadTempFile (name); - if (raw) - { - pix = ReadTargaFile(raw, com_filesize, &skin->width, &skin->height, &hasalpha, false); - if (pix) - { - skin->skindata = out = BZ_Malloc(skin->width*skin->height*4); - memcpy(out, pix, skin->width*skin->height*4); - BZ_Free(pix); - return out; - } - } - Q_snprintfz (name, sizeof(name), "%s%s.pcx", path, skin->name); - raw = COM_LoadTempFile (name); - if (raw) - { - pix = ReadPCXFile(raw, com_filesize, &skin->width, &skin->height); - if (pix) - { - skin->skindata = out = BZ_Malloc(skin->width*skin->height*4); - memcpy(out, pix, skin->width*skin->height*4); - BZ_Free(pix); - return out; - } - } -#ifdef AVAIL_PNGLIB - Q_snprintfz (name, sizeof(name), "%s%s.png", path, skin->name); - raw = COM_LoadTempFile (name); - if (raw) - { - pix = ReadPNGFile(raw, com_filesize, &skin->width, &skin->height, name); - if (pix) - { - skin->skindata = out = BZ_Malloc(skin->width*skin->height*4); - memcpy(out, pix, skin->width*skin->height*4); - BZ_Free(pix); - return out; - } - } -#endif -#ifdef AVAIL_JPEGLIB - Q_snprintfz (name, sizeof(name), "%s%s.jpeg", path, skin->name); - raw = COM_LoadTempFile (name); - if (raw) - { - pix = ReadJPEGFile(raw, com_filesize, &skin->width, &skin->height); - if (pix) - { - skin->skindata = out = BZ_Malloc(skin->width*skin->height*4); - memcpy(out, pix, skin->width*skin->height*4); - BZ_Free(pix); - return out; - } - } - Q_snprintfz (name, sizeof(name), "%s%s.jpg", path, skin->name); //jpegs are gready with 2 extensions... - raw = COM_LoadTempFile (name); - if (raw) - { - pix = ReadJPEGFile(raw, com_filesize, &skin->width, &skin->height); - if (pix) - { - skin->skindata = out = BZ_Malloc(skin->width*skin->height*4); - memcpy(out, pix, skin->width*skin->height*4); - BZ_Free(pix); - return out; - } - } -#endif - - skin->failedload = true; - return NULL; -} - /* ================= Skin_NextDownload @@ -542,62 +451,61 @@ void Skin_NextDownload (void) int i; //Con_Printf ("Checking skins...\n"); - + if (cls.protocol == CP_QUAKE2) + { + int j; + char *slash; + char *skinname; + for (i = 0; i != MAX_CLIENTS; i++) + { + sc = &cl.players[i]; + if (!sc->name[0]) + continue; + skinname = Info_ValueForKey(sc->userinfo, "skin"); + slash = strchr(skinname, '/'); + if (slash) + { + *slash = 0; + CL_CheckOrEnqueDownloadFile(va("players/%s/tris.md2", skinname), NULL, 0); + for (j = 0; j < MAX_MODELS; j++) + { + if (cl.model_name[j][0] == '#') + CL_CheckOrEnqueDownloadFile(va("players/%s/%s", skinname, cl.model_name[j]+1), NULL, 0); + } + for (j = 0; j < MAX_SOUNDS; j++) + { + if (cl.sound_name[j][0] == '*') + CL_CheckOrEnqueDownloadFile(va("players/%s/%s", skinname, cl.sound_name[j]+1), NULL, 0); + } + *slash = '/'; + CL_CheckOrEnqueDownloadFile(va("players/%s.pcx", skinname), NULL, 0); + } + } + return; + } for (i = 0; i != MAX_CLIENTS; i++) { sc = &cl.players[i]; if (!sc->name[0]) continue; Skin_Find (sc); - if (noskins.ival) + if (noskins.ival || !sc->qwskin) continue; - - if (strchr(sc->skin->name, ' ')) //skip over skins using a space + if (strchr(sc->qwskin->name, ' ')) //skip over skins using a space continue; - - if (!*sc->skin->name) + if (!*sc->qwskin->name) continue; - - if (cls.protocol == CP_QUAKE2) - { - int j; - char *slash; - slash = strchr(sc->skin->name, '/'); - if (slash) - { - *slash = 0; - CL_CheckOrEnqueDownloadFile(va("players/%s/tris.md2", sc->skin->name), NULL, 0); - for (j = 0; j < MAX_MODELS; j++) - { - if (cl.model_name[j][0] == '#') - CL_CheckOrEnqueDownloadFile(va("players/%s/%s", sc->skin->name, cl.model_name[j]+1), NULL, 0); - } - for (j = 0; j < MAX_SOUNDS; j++) - { - if (cl.sound_name[j][0] == '*') - CL_CheckOrEnqueDownloadFile(va("players/%s/%s", sc->skin->name, cl.sound_name[j]+1), NULL, 0); - } - *slash = '/'; - CL_CheckOrEnqueDownloadFile(va("players/%s.pcx", sc->skin->name), NULL, 0); - } - } - else - CL_CheckOrEnqueDownloadFile(va("skins/%s.pcx", sc->skin->name), NULL, 0); + CL_CheckOrEnqueDownloadFile(va("skins/%s.pcx", sc->qwskin->name), NULL, 0); } // now load them in for real for (i=0 ; iname[0]) + if (!sc->name[0] || !sc->qwskin) continue; - if (cls.protocol == CP_QUAKE2) - Skin_Cache32(sc->skin); - else - Skin_Cache8 (sc->skin); -#ifdef GLQUAKE - sc->skin = NULL; -#endif + Skin_Cache8 (sc->qwskin); + //sc->qwskin = NULL; } } @@ -607,7 +515,7 @@ void Skin_FlushPlayers(void) { //wipe the skin info int i; for (i = 0; i < cl.allocated_client_slots; i++) - cl.players[i].skin = NULL; + cl.players[i].qwskin = NULL; for (i = 0; i < cl.allocated_client_slots; i++) CL_NewTranslation(i); @@ -643,7 +551,7 @@ void Skin_Skins_f (void) return; } - GL_GAliasFlushSkinCache(); + R_GAliasFlushSkinCache(false); for (i=0 ; igravitydir, camorg); +// if (cl.worldmodel && cl.worldmodel->funcs.NativeTrace(cl.worldmodel, 0, 0, NULL, r_refdef.vieworg, camorg, vec3_origin, vec3_origin, MASK_WORLDSOLID, &tr)) + VectorCopy(camorg, r_refdef.vieworg); + + CL_EditExternalModels(0, NULL, 0); + } } /* @@ -1471,6 +1486,11 @@ void V_RenderPlayerViews(playerview_t *pv) DropPunchAngle (pv); Cam_SelfTrack(pv); + + oldnuments = cl_numvisedicts; + oldstris = cl_numstris; + CL_LinkViewModel (); + if (cl.intermission) { // intermission / finale rendering V_CalcIntermissionRefdef (pv); @@ -1482,10 +1502,6 @@ void V_RenderPlayerViews(playerview_t *pv) } V_ApplyRefdef(); - oldnuments = cl_numvisedicts; - oldstris = cl_numstris; - CL_LinkViewModel (); - R_RenderView (); R2D_PolyBlend (); R_DrawNameTags(); @@ -1758,4 +1774,8 @@ void V_Init (void) Cvar_Register (&v_gamma, VIEWVARS); Cvar_Register (&v_contrast, VIEWVARS); Cvar_Register (&v_brightness, VIEWVARS); + +// Cvar_Register (&chase_active, VIEWVARS); +// Cvar_Register (&chase_back, VIEWVARS); +// Cvar_Register (&chase_up, VIEWVARS); } diff --git a/engine/common/bothdefs.h b/engine/common/bothdefs.h index 2d76afa6b..a3c86e119 100644 --- a/engine/common/bothdefs.h +++ b/engine/common/bothdefs.h @@ -270,7 +270,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #endif #ifdef QUAKETC - #define NOBUITINMENUS //kill engine menus (should be replaced with ewither csqc or menuqc) + #define NOBUILTINMENUS //kill engine menus (should be replaced with ewither csqc or menuqc) #undef Q2CLIENT //not useful #undef Q2SERVER //not useful #undef Q3CLIENT //not useful diff --git a/engine/common/pr_bgcmd.c b/engine/common/pr_bgcmd.c index 36fc0c041..6a3e2845d 100644 --- a/engine/common/pr_bgcmd.c +++ b/engine/common/pr_bgcmd.c @@ -1281,7 +1281,17 @@ pf_hashtab_t pf_reverthashtab; //pf_peristanthashtab as it was at map start, fo static pf_hashtab_t *PF_hash_findtab(pubprogfuncs_t *prinst, int idx) { if (!idx) + { + if (!pf_peristanthashtab.tab.numbuckets) + { + int numbuckets = 256; + pf_peristanthashtab.defaulttype = ev_string; + pf_peristanthashtab.prinst = NULL; + pf_peristanthashtab.bucketmem = Z_Malloc(Hash_BytesForBuckets(numbuckets)); + Hash_InitTable(&pf_peristanthashtab.tab, numbuckets, pf_peristanthashtab.bucketmem); + } return &pf_peristanthashtab; + } idx -= 1; if (idx >= 0 && idx < MAX_QC_HASHTABLES && pf_hashtab[idx].prinst) return &pf_hashtab[idx]; @@ -1331,7 +1341,7 @@ void QCBUILTIN PF_hash_get (pubprogfuncs_t *prinst, struct globalvars_s *pr_glob { pf_hashtab_t *tab = PF_hash_findtab(prinst, G_FLOAT(OFS_PARM0)); const char *name = PR_GetStringOfs(prinst, OFS_PARM1); - void *dflt = G_VECTOR(OFS_PARM2); + void *dflt = (prinst->callargc>2)?G_VECTOR(OFS_PARM2):vec3_origin; int type = (prinst->callargc>3)?G_FLOAT(OFS_PARM3):0; int index = (prinst->callargc>4)?G_FLOAT(OFS_PARM4):0; pf_hashentry_t *ent = NULL; @@ -1386,19 +1396,26 @@ void QCBUILTIN PF_hash_add (pubprogfuncs_t *prinst, struct globalvars_s *pr_glob const char *name = PR_GetStringOfs(prinst, OFS_PARM1); void *data = G_VECTOR(OFS_PARM2); int flags = (prinst->callargc>3)?G_FLOAT(OFS_PARM3):0; - int type = (prinst->callargc>4)?G_FLOAT(OFS_PARM4):0; + int type = flags & 0xff; pf_hashentry_t *ent = NULL; if (tab) { if (!type) type = tab->defaulttype; - if (flags & 1) - Hash_Remove(&tab->tab, name); + if (flags & 256) + { + ent = Hash_Get(&tab->tab, name); + if (ent) + { + Hash_RemoveData(&tab->tab, name, ent); + BZ_Free(ent); + } + } if (type == ev_string) { //strings copy their value out. const char *value = PR_GetStringOfs(prinst, OFS_PARM2); int nlen = strlen(name); - int vlen = strlen(data); + int vlen = strlen(value); ent = BZ_Malloc(sizeof(*ent) + nlen+1 + vlen+1); ent->name = (char*)(ent+1); ent->type = ev_string; @@ -4452,7 +4469,7 @@ void QCBUILTIN PF_externcall (pubprogfuncs_t *prinst, struct globalvars_s *pr_gl prinst->pr_trace++; //continue debugging PR_ExecuteProgram(prinst, f); } - else if (!f) + else { f = PR_FindFunction(prinst, "MissingFunc", progsnum); if (!f) diff --git a/engine/dotnet2005/ftequake.vcproj b/engine/dotnet2005/ftequake.vcproj index 37c486a4f..5ac545f3d 100644 --- a/engine/dotnet2005/ftequake.vcproj +++ b/engine/dotnet2005/ftequake.vcproj @@ -1380,7 +1380,9 @@ AdditionalIncludeDirectories="../libs/speex,..\client,../libs/freetype2/include,../common,../server,../gl,../sw,../qclib,../libs,../libs/dxsdk7/include,../d3d,../d3d9,../libs/dxsdk9/include" PreprocessorDefinitions="_DEBUG;WIN32;_WINDOWS;SERVERONLY;MULTITHREAD" MinimalRebuild="true" + BasicRuntimeChecks="3" RuntimeLibrary="0" + EnableFunctionLevelLinking="true" FloatingPointModel="2" UsePrecompiledHeader="2" PrecompiledHeaderThrough="quakedef.h" @@ -27238,13 +27240,6 @@ - - - = numregisteredskins) + return; //invalid! + sk = registeredskins[id]; + if (!sk) + return; + sk->qwskin = NULL; +} void Mod_WipeSkin(skinid_t id) { skinfile_t *sk; @@ -89,14 +100,22 @@ void Mod_WipeSkin(skinid_t id) Z_Free(registeredskins[id]); registeredskins[id] = NULL; } -static void Mod_WipeAllSkins(void) +static void Mod_WipeAllSkins(qboolean final) { skinid_t id; - for (id = 0; id < numregisteredskins; ) - Mod_WipeSkin(++id); - Z_Free(registeredskins); - registeredskins = NULL; - numregisteredskins = 0; + if (final) + { + for (id = 0; id < numregisteredskins; ) + Mod_WipeSkin(++id); + Z_Free(registeredskins); + registeredskins = NULL; + numregisteredskins = 0; + } + else + { + for (id = 0; id < numregisteredskins; ) + Mod_FlushSkin(++id); + } } skinfile_t *Mod_LookupSkin(skinid_t id) { @@ -295,6 +314,11 @@ skinid_t Mod_ReadSkinFile(const char *skinname, const char *skintext) { //ignore it. matches q3. } + else if (!strcmp(com_token, "qwskin")) + { + skintext = COM_ParseToken(skintext, NULL); + Q_strncpyz(skin->qwskinname, com_token, sizeof(skin->qwskinname)); + } else { while(*skintext == ' ' || *skintext == '\t') @@ -467,7 +491,10 @@ void GL_GAliasFlushOneSkin(char *skinname) } } }*/ -void GL_GAliasFlushSkinCache(void) + +//final is set when the renderer is going down. +//if not set, this is mid-map, and everything should be regeneratable. +void R_GAliasFlushSkinCache(qboolean final) { int i; bucket_t *b; @@ -494,14 +521,14 @@ void GL_GAliasFlushSkinCache(void) numFacing = 0; #endif - Mod_WipeAllSkins(); + Mod_WipeAllSkins(final); } static shader_t *GL_ChooseSkin(galiasinfo_t *inf, model_t *model, int surfnum, entity_t *e, texnums_t **forcedtex) { galiasskin_t *skins; shader_t *shader; - skin_t *plskin; + qwskin_t *plskin = NULL; int frame; unsigned int subframe; extern int cl_playerindex; //so I don't have to strcmp @@ -528,6 +555,9 @@ static shader_t *GL_ChooseSkin(galiasinfo_t *inf, model_t *model, int surfnum, e return sk->mappings[i].shader; } } + if (!sk->qwskin && *sk->qwskinname) + sk->qwskin = Skin_Lookup(sk->qwskinname); + plskin = sk->qwskin; } } @@ -552,14 +582,23 @@ static shader_t *GL_ChooseSkin(galiasinfo_t *inf, model_t *model, int surfnum, e if (!gl_nocolors.ival || forced) { - if (e->playerindex >= 0 && e->playerindex <= MAX_CLIENTS) + if (!plskin || plskin->failedload) { - if (!cl.players[e->playerindex].skin) - Skin_Find(&cl.players[e->playerindex]); - plskin = cl.players[e->playerindex].skin; + if (e->playerindex >= 0 && e->playerindex <= MAX_CLIENTS) + { + //heads don't get skinned, only players (and weaponless players), they do still get recoloured. + if (model==cl.model_precache[cl_playerindex] || model==cl.model_precache_vwep[0]) + { + if (!cl.players[e->playerindex].qwskin) + Skin_Find(&cl.players[e->playerindex]); + plskin = cl.players[e->playerindex].qwskin; + } + else + plskin = NULL; + } + else + plskin = NULL; } - else - plskin = NULL; tc = e->topcolour; bc = e->bottomcolour; pc = e->h2playerclass; @@ -667,27 +706,13 @@ static shader_t *GL_ChooseSkin(galiasinfo_t *inf, model_t *model, int surfnum, e if (plskin) { - if (cls.protocol == CP_QUAKE2) + original = Skin_Cache8(plskin); + if (original) { - original = Skin_Cache32(plskin); - if (original) - { - inwidth = plskin->width; - inheight = plskin->height; - cm->texnum.base = R_LoadTexture32(plskin->name, inwidth, inheight, (unsigned int*)original, IF_NOALPHA|IF_NOGAMMA); - return shader; - } - } - else - { - original = Skin_Cache8(plskin); - if (original) - { - inwidth = plskin->width; - inheight = plskin->height; - cm->texnum.base = R_LoadTexture8(plskin->name, inwidth, inheight, original, IF_NOALPHA|IF_NOGAMMA, 1); - return shader; - } + inwidth = plskin->width; + inheight = plskin->height; + cm->texnum.base = R_LoadTexture8(plskin->name, inwidth, inheight, original, IF_NOALPHA|IF_NOGAMMA, 1); + return shader; } if (TEXVALID(plskin->textures.base)) @@ -707,7 +732,7 @@ static shader_t *GL_ChooseSkin(galiasinfo_t *inf, model_t *model, int surfnum, e } cm->texnum.bump = shader->defaulttextures.bump; //can't colour bumpmapping - if (cls.protocol != CP_QUAKE2 && ((model==cl.model_precache[cl_playerindex] || model==cl.model_precache_vwep[0]) && plskin)) + if (plskin) { /*q1 only reskins the player model, not gibbed heads (which have the same colourmap)*/ original = Skin_Cache8(plskin); diff --git a/engine/gl/gl_draw.c b/engine/gl/gl_draw.c index 25d2ea76d..80580b11e 100644 --- a/engine/gl/gl_draw.c +++ b/engine/gl/gl_draw.c @@ -543,7 +543,7 @@ void GLDraw_DeInit (void) R2D_Shutdown(); - GL_GAliasFlushSkinCache(); + R_GAliasFlushSkinCache(true); draw_disc = NULL; GL_ShutdownPostProcessing(); diff --git a/engine/gl/gl_model.c b/engine/gl/gl_model.c index e1119120f..fa5bb31a2 100644 --- a/engine/gl/gl_model.c +++ b/engine/gl/gl_model.c @@ -32,7 +32,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. extern cvar_t r_shadow_bumpscale_basetexture; extern cvar_t r_replacemodels; extern cvar_t gl_lightmap_average; - +cvar_t mod_external_vis = CVARD("mod_external_vis", "1", "Attempt to load .vis patches for quake maps, allowing transparent water to work properly."); #ifdef SERVERONLY cvar_t gl_overbright, gl_specular, gl_load24bit, r_replacemodels, gl_miptexLevel, r_fb_bmodels; //all of these can/should default to 0 cvar_t r_noframegrouplerp = CVARF ("r_noframegrouplerp", "0", CVAR_ARCHIVE); @@ -529,6 +529,8 @@ void Mod_Init (qboolean initial) Cmd_AddCommand("mod_usetexture", Mod_BlockTextureColour_f); #endif } + else + Cvar_Register(&mod_external_vis, "Graphical Nicaties"); if (initial) { @@ -2065,15 +2067,20 @@ void Mod_LoadLighting (lump_t *l) Mod_LoadVisibility ================= */ -void Mod_LoadVisibility (lump_t *l) +void Mod_LoadVisibility (lump_t *l, qbyte *ptr, size_t len) { - if (!l->filelen) + if (!ptr) + { + ptr = mod_base + l->fileofs; + len = l->filelen; + } + if (!len) { loadmodel->visdata = NULL; return; } - loadmodel->visdata = ZG_Malloc(&loadmodel->memgroup, l->filelen); - memcpy (loadmodel->visdata, mod_base + l->fileofs, l->filelen); + loadmodel->visdata = ZG_Malloc(&loadmodel->memgroup, len); + memcpy (loadmodel->visdata, ptr, len); } @@ -3321,21 +3328,27 @@ qboolean Mod_LoadNodes (lump_t *l, int lm) Mod_LoadLeafs ================= */ -qboolean Mod_LoadLeafs (lump_t *l, int lm) +qboolean Mod_LoadLeafs (lump_t *l, int lm, qbyte *ptr, size_t len) { mleaf_t *out; int i, j, count, p; + if (!ptr) + { + ptr = mod_base + l->fileofs; + len = l->filelen; + } + if (lm==2) { dl2leaf_t *in; - in = (void *)(mod_base + l->fileofs); - if (l->filelen % sizeof(*in)) + in = (void *)ptr; + if (len % sizeof(*in)) { Con_Printf (CON_ERROR "MOD_LoadBmodel: funny lump size in %s\n",loadmodel->name); return false; } - count = l->filelen / sizeof(*in); + count = len / sizeof(*in); if (count > MAX_MAP_LEAFS) { Con_Printf (CON_ERROR "Mod_LoadLeafs: %s has more than %i leafs\n",loadmodel->name, MAX_MAP_LEAFS); @@ -3392,13 +3405,13 @@ qboolean Mod_LoadLeafs (lump_t *l, int lm) else if (lm) { dl1leaf_t *in; - in = (void *)(mod_base + l->fileofs); - if (l->filelen % sizeof(*in)) + in = (void *)(ptr); + if (len % sizeof(*in)) { Con_Printf (CON_ERROR "MOD_LoadBmodel: funny lump size in %s\n",loadmodel->name); return false; } - count = l->filelen / sizeof(*in); + count = len / sizeof(*in); if (count > MAX_MAP_LEAFS) { Con_Printf (CON_ERROR "Mod_LoadLeafs: %s has more than %i leafs\n",loadmodel->name, MAX_MAP_LEAFS); @@ -3455,13 +3468,13 @@ qboolean Mod_LoadLeafs (lump_t *l, int lm) else { dsleaf_t *in; - in = (void *)(mod_base + l->fileofs); - if (l->filelen % sizeof(*in)) + in = (void *)(ptr); + if (len % sizeof(*in)) { Con_Printf (CON_ERROR "MOD_LoadBmodel: funny lump size in %s\n",loadmodel->name); return false; } - count = l->filelen / sizeof(*in); + count = len / sizeof(*in); if (count > MAX_MAP_LEAFS) { Con_Printf (CON_ERROR "Mod_LoadLeafs: %s has more than %i leafs\n",loadmodel->name, MAX_MAP_LEAFS); @@ -4195,6 +4208,79 @@ static void Mod_FixupMinsMaxs(void) Mod_FixupNodeMinsMaxs (loadmodel->nodes, NULL); // sets nodes and leafs } +struct vispatch_s +{ + void *fileptr; + size_t filelen; + + void *visptr; + int vislen; + + void *leafptr; + int leaflen; +}; + +void Mod_FindVisPatch(struct vispatch_s *patch, model_t *mod, size_t leaflumpsize) +{ + char patchname[MAX_QPATH]; + int *lenptr, len; + int ofs; + qbyte *file; + memset(patch, 0, sizeof(*patch)); + + if (!mod_external_vis.ival) + return; + + COM_StripExtension(mod->name, patchname, sizeof(patchname)); + Q_strncatz(patchname, ".vis", sizeof(patchname)); + + //ignore the patch file if its in a different gamedir. + //this file format sucks too much for other verification. + if (FS_FLocateFile(mod->name,FSLFRT_DEPTH_OSONLY, NULL) != FS_FLocateFile(patchname,FSLFRT_DEPTH_OSONLY, NULL)) + return; + + patch->filelen = FS_LoadFile(patchname, &patch->fileptr); + if (!patch->fileptr) + return; + ofs = 0; + while (ofs+36 <= patch->filelen) + { + file = patch->fileptr; + file += ofs; + memcpy(patchname, file, 32); + patchname[32] = 0; + file += 32; + lenptr = (int*)file; + file += sizeof(int); + len = LittleLong(*lenptr); + if (ofs+36+len > patch->filelen) + break; + +// if (!Q_strcasecmp(patchname, "foo")) + { + lenptr = (int*)file; + patch->vislen = LittleLong(*lenptr); + file += sizeof(int); + patch->visptr = file; + file += patch->vislen; + + lenptr = (int*)file; + patch->leaflen = LittleLong(*lenptr); + file += sizeof(int); + patch->leafptr = file; + file += patch->leaflen; + + if (sizeof(int)*2 + patch->vislen + patch->leaflen != len || patch->leaflen != leaflumpsize) + { + Con_Printf("Vis patch is unsuitable\n"); + patch->visptr = NULL; + patch->leafptr = NULL; + } + } + ofs += 36+len; + } +} + /* ================= Mod_LoadBrushModel @@ -4202,6 +4288,7 @@ Mod_LoadBrushModel */ qboolean QDECL Mod_LoadBrushModel (model_t *mod, void *buffer, size_t fsize) { + struct vispatch_s vispatch; int i, j; dheader_t *header; mmodel_t *bm; @@ -4306,6 +4393,8 @@ qboolean QDECL Mod_LoadBrushModel (model_t *mod, void *buffer, size_t fsize) crouchhullfile = NULL; + Mod_FindVisPatch(&vispatch, loadmodel, header->lumps[LUMP_LEAFS].filelen); + TRACE(("Loading info\n")); #ifndef SERVERONLY if (!isnotmap) @@ -4354,9 +4443,9 @@ qboolean QDECL Mod_LoadBrushModel (model_t *mod, void *buffer, size_t fsize) if (noerrors) { TRACE(("Loading Vis\n")); - Mod_LoadVisibility (&header->lumps[LUMP_VISIBILITY]); + Mod_LoadVisibility (&header->lumps[LUMP_VISIBILITY], vispatch.visptr, vispatch.vislen); } - noerrors = noerrors && Mod_LoadLeafs (&header->lumps[LUMP_LEAFS], longm); + noerrors = noerrors && Mod_LoadLeafs (&header->lumps[LUMP_LEAFS], longm, vispatch.leafptr, vispatch.leaflen); TRACE(("Loading Nodes\n")); noerrors = noerrors && Mod_LoadNodes (&header->lumps[LUMP_NODES], longm); TRACE(("Loading Clipnodes\n")); @@ -4379,6 +4468,8 @@ qboolean QDECL Mod_LoadBrushModel (model_t *mod, void *buffer, size_t fsize) crouchhullfile=NULL; } + BZ_Free(vispatch.fileptr); + if (!noerrors) { return false; diff --git a/engine/gl/gl_shadow.c b/engine/gl/gl_shadow.c index 6f640767d..00ec08327 100644 --- a/engine/gl/gl_shadow.c +++ b/engine/gl/gl_shadow.c @@ -9,7 +9,7 @@ model meshes are interpolated multiple times per frame //#define DBG_COLOURNOTDEPTH -#ifdef RTLIGHTS +#if defined(RTLIGHTS) && !defined(SERVERONLY) #include "glquake.h" #include "shader.h" @@ -3506,7 +3506,7 @@ void Sh_DrawLights(qbyte *vis) //so this little function is used to check if its needed or not. qboolean Sh_StencilShadowsActive(void) { -#ifdef RTLIGHTS +#if defined(RTLIGHTS) && !defined(SERVERONLY) //if shadowmapping is forced on all lights then we don't need special depth stuff // if (r_shadow_shadowmapping.ival) // return false; @@ -3521,7 +3521,7 @@ qboolean Sh_StencilShadowsActive(void) void Sh_RegisterCvars(void) { -#ifdef RTLIGHTS +#if defined(RTLIGHTS) && !defined(SERVERONLY) #define REALTIMELIGHTING "Realtime Lighting" Cvar_Register (&r_shadow_scissor, REALTIMELIGHTING); Cvar_Register (&r_shadow_realtime_world, REALTIMELIGHTING); diff --git a/engine/gl/glquake.h b/engine/gl/glquake.h index d33eaacb1..7989e9a31 100644 --- a/engine/gl/glquake.h +++ b/engine/gl/glquake.h @@ -134,9 +134,6 @@ void GL_InitFogTexture(void); #define GL_BeginRendering() #define GL_EndRendering() -void GL_FlushSkinCache(void); -void GL_GAliasFlushSkinCache(void); - // Function prototypes for the Texture Object Extension routines typedef GLboolean (APIENTRY *ARETEXRESFUNCPTR)(GLsizei, const GLuint *, const GLboolean *); diff --git a/engine/http/httpclient.c b/engine/http/httpclient.c index 8450e9114..51d4302d3 100644 --- a/engine/http/httpclient.c +++ b/engine/http/httpclient.c @@ -814,12 +814,190 @@ qboolean HTTPDL_Poll(struct dl_download *dl) return true; } +/* +//decode a base64 byte to a 0-63 value. Cannot cope with =. +static unsigned int Base64_DecodeByte(char byt) +{ + if (byt >= 'A' && byt <= 'Z') + return (byt-'A') + 0; + if (byt >= 'a' && byt <= 'z') + return (byt-'a') + 26; + if (byt >= '0' && byt <= '9') + return (byt-'0') + 52; + if (byt == '+') + return 62; + if (byt == '/') + return 63; + return -1; +} +//FIXME: we should be able to skip whitespace. +static int Base64_Decode(char *out, int outlen, char **srcout, int *srclenout) +{ + int len = 0; + unsigned int result; + + char *src = *srcout; + int srclen = *srclenout; + + //4 input chars give 3 output chars + while(srclen >= 4) + { + if (len+3 > outlen) + { + //ran out of space in the output buffer + *srcout = src; + *srclenout = srclen; + return len; + } + result = Base64_DecodeByte(src[0])<<18; + result |= Base64_DecodeByte(src[1])<<12; + out[len++] = (result>>16)&0xff; + if (src[2] != '=') + { + result |= Base64_DecodeByte(src[2])<<6; + out[len++] = (result>>8)&0xff; + if (src[3] != '=') + { + result |= Base64_DecodeByte(src[3])<<0; + out[len++] = (result>>0)&0xff; + } + } + if (result & 0xff000000) + return 0; //some kind of invalid char + + src += 4; + srclen -= 4; + } + + //end of string + *srcout = src; + *srclenout = srclen; + + //some kind of error + if (srclen) + { + if (srclen != 1 || *src) + return 0; + } + + return len; +} + +qboolean DataScheme_Decode(struct dl_download *dl) +{ + char block[8192]; + int remaining, blocksize; + char mimetype[256]; + char baseval[256]; + char charsetval[256]; + char *url; + char *data; + char *charset; + char *base; + //failed previously + if (dl->status == DL_FAILED) + return false; + + //data:[][;charset=][;base64], + + *mimetype = 0; + *baseval = 0; + *charsetval = 0; + + url = dl->url; + if (!strncmp(url, "data:", 5)) + url+=5; //should always match + data = strchr(url, ','); + if (!data) + return false; + charset = memchr(url, ';', data-url); + if (charset) + { + base = memchr(charset+1, ';', data-charset); + if (base) + { + if (data-(base+1) >= sizeof(baseval)) + return false; + memcpy(baseval, base+1, data-(base+1)); + baseval[data-(base+1)] = 0; + } + else + base = data; + if (base-(charset+1) >= sizeof(charsetval)) + return false; + memcpy(charsetval, charset+1, base-(charset+1)); + charsetval[base-(charset+1)] = 0; + + if (!strchr(charsetval, '=')) + { + strcpy(baseval, charsetval); + *charsetval = 0; + } + } + else + charset = data; + if (charset-(url) >= sizeof(charsetval)) + return false; + memcpy(mimetype, url, charset-(url)); + mimetype[charset-(url)] = 0; + + if (!*mimetype) + Q_strncpyz(mimetype, "text/plain", sizeof(mimetype)); + if (!*charsetval) + Q_strncpyz(charsetval, "charset=US-ASCII", sizeof(charsetval)); + + if (dl->notifystarted) + dl->notifystarted(dl, *mimetype?mimetype:NULL); + + if (!dl->file) + { +#ifndef NPFTE + if (*dl->localname) + { + FS_CreatePath(dl->localname, FS_GAME); + dl->file = FS_OpenVFS(dl->localname, "w+b", FS_GAME); + } + else + dl->file = FS_OpenTemp(); +#endif + if (!dl->file) + { + if (*dl->localname) + Con_Printf("HTTP: Couldn't open file \"%s\"\n", dl->localname); + else + Con_Printf("HTTP: Couldn't open temporary file\n"); + dl->status = DL_FAILED; + return false; + } + } + + data++; + remaining = strlen(data); + while(remaining > 0) + { + blocksize = Base64_Decode(block, sizeof(block), &data, &remaining); + if (!blocksize) + { + dl->status = DL_FAILED; + return false; + } + VFS_WRITE(dl->file, block, blocksize); + } + + dl->status = DL_FINISHED; + return false; +} +*/ + qboolean DL_Decide(struct dl_download *dl) { const char *url = dl->redir; if (!*url) url = dl->url; + /*if (!strnicmp(url, "data:", 5)) + dl->poll = DataScheme_Decode; + else*/ if (!strnicmp(url, "http://", 7)) dl->poll = HTTPDL_Poll; else @@ -883,11 +1061,12 @@ qboolean DL_CreateThread(struct dl_download *dl, vfsfile_t *file, void (*NotifyF struct dl_download *DL_Create(const char *url) { struct dl_download *newdl; - newdl = malloc(sizeof(*newdl)); + newdl = malloc(sizeof(*newdl) + strlen(url)+1); if (!newdl) return NULL; memset(newdl, 0, sizeof(*newdl)); - Q_strncpyz(newdl->url, url, sizeof(newdl->url)); + newdl->url = (char*)(newdl+1); + strcpy(newdl->url, url); newdl->poll = DL_Decide; if (!newdl->poll(newdl)) @@ -995,10 +1174,10 @@ void HTTP_CL_Think(void) cls.download = &dl->qdownload; dl->qdownload.method = DL_HTTP; if (*dl->localname) - strcpy(dl->qdownload.localname, dl->localname); + Q_strncpyz(dl->qdownload.localname, dl->localname, sizeof(dl->qdownload.localname)); else - strcpy(dl->qdownload.localname, dl->url); - strcpy(dl->qdownload.remotename, dl->url); + Q_strncpyz(dl->qdownload.localname, dl->url, sizeof(dl->qdownload.localname)); + Q_strncpyz(dl->qdownload.remotename, dl->url, sizeof(dl->qdownload.remotename)); dl->qdownload.starttime = Sys_DoubleTime(); } diff --git a/engine/http/httpserver.c b/engine/http/httpserver.c index 883921e31..387ac6d6e 100644 --- a/engine/http/httpserver.c +++ b/engine/http/httpserver.c @@ -189,6 +189,7 @@ void HTTP_ServerShutdown(void) typedef struct HTTP_active_connections_s { SOCKET datasock; + char peername[256]; vfsfile_t *file; struct HTTP_active_connections_s *next; @@ -242,6 +243,7 @@ void HTTP_RunExisting (void) char *msg, *nl; char buf2[2560]; //short lived temp buffer. char resource[2560]; + char host[256]; char mode[80]; qboolean hostspecified; unsigned int contentlen; @@ -260,7 +262,7 @@ void HTTP_RunExisting (void) if (cl->closereason) { - IWebPrintf("Closing connection: %s\n", cl->closereason); + IWebPrintf("%s: Closing connection: %s\n", cl->peername, cl->closereason); *link = cl->next; closesocket(cl->datasock); @@ -342,6 +344,9 @@ cont: continue; } + host[0] = '?'; + host[1] = 0; + hostspecified = false; if (!strnicmp(resource, "http://", 7)) { //groan... 1.1 compliance requires parsing this correctly, without the client ever specifiying it. @@ -351,7 +356,14 @@ cont: if (!slash) strcpy(resource, "/"); else + { + int hlen = slash-(resource+7); + if (hlen > sizeof(host)-1) + hlen = sizeof(host)-1; + memcpy(host, resource+7, hlen); + host[hlen] = 0; memmove(resource, slash, strlen(slash+1)); //just get rid of the http:// stuff. + } } if (!strcmp(resource, "/")) @@ -397,7 +409,16 @@ cont: msg++; if (!strnicmp(msg, "Host: ", 6)) //parse needed header fields + { + int l = 0; + msg += 6; + while (*msg == ' ' || *msg == '\t') + msg++; + while (*msg != '\r' && *msg != '\n' && l < sizeof(host)-1) + host[l++] = *msg++; + host[l] = 0; hostspecified = true; + } else if (!strnicmp(msg, "Content-Length: ", 16)) //parse needed header fields contentlen = strtoul(msg+16, NULL, 0); else if (!strnicmp(msg, "Accept-Encoding:", 16)) //parse needed header fields @@ -491,7 +512,7 @@ cont: resource[0] = '/'; resource[1] = 0; //I'm lazy, they need to comply } - IWebPrintf("Download request for \"%s\"\n", resource+1); + IWebPrintf("%s: Download request for \"http://%s/%s\"\n", cl->peername, host, resource+1); if (!strnicmp(mode, "P", 1)) //when stuff is posted, data is provided. Give an error message if we couldn't do anything with that data. cl->file = IWebGenerateFile(resource+1, content, contentlen); @@ -519,7 +540,7 @@ cont: } if (!cl->file) { - IWebPrintf("Download rejected\n"); + IWebPrintf("%s: Download rejected\n", cl->peername); if (HTTPmarkup >= 3) msg = "HTTP/1.1 404 Not Found\r\n" "Content-Type: text/plain\r\n" "Content-Length: 15\r\n" "Server: "FULLENGINENAME"/0\r\n" "\r\n" "404 Bad address"; @@ -611,7 +632,7 @@ notimplemented: VFS_CLOSE(cl->file); cl->file = NULL; - IWebPrintf("Download complete\n"); + IWebPrintf("%s: Download complete\n", cl->peername); } else cl->outbufferused+=ammount; @@ -663,14 +684,24 @@ notimplemented: } } +#ifdef WEBSVONLY +void VARGS Q_snprintfz (char *dest, size_t size, const char *fmt, ...) +{ + va_list args; + va_start (args, fmt); + vsnprintf (dest, size-1, fmt, args); + va_end (args); + //make sure its terminated. + dest[size-1] = 0; +} +#endif + qboolean HTTP_ServerPoll(qboolean httpserverwanted, int portnum) //loop while true { struct sockaddr_qstorage from; int fromlen; int clientsock; int _true = true; - char buf[128]; - netadr_t na; HTTP_active_connections_t *cl; @@ -712,7 +743,7 @@ qboolean HTTP_ServerPoll(qboolean httpserverwanted, int portnum) //loop while tr } - Con_Printf ("NET_GetPacket: %s\n", strerror(e)); + IWebPrintf ("NET_GetPacket: %s\n", strerror(e)); return false; } @@ -723,15 +754,19 @@ qboolean HTTP_ServerPoll(qboolean httpserverwanted, int portnum) //loop while tr return false; } -#ifndef WEBSVONLY - SockadrToNetadr(&from, &na); - IWebPrintf("New http connection from %s\n", NET_AdrToString(buf, sizeof(buf), &na)); -#else - IWebPrintf("New http connection from %s\n", inet_ntoa(((struct sockaddr_in*)&from)->sin_addr)); -#endif - cl = IWebMalloc(sizeof(HTTP_active_connections_t)); +#ifndef WEBSVONLY + { + netadr_t na; + SockadrToNetadr(&from, &na); + NET_AdrToString(cl->peername, sizeof(cl->peername), &na); + } +#else + Q_snprintfz(cl->peername, sizeof(cl->peername), "%s:%i", inet_ntoa(((struct sockaddr_in*)&from)->sin_addr), ntohs(((struct sockaddr_in*)&from)->sin_port)); +#endif + IWebPrintf("%s: New http connection\n", cl->peername); + cl->datasock = clientsock; cl->next = HTTP_ServerConnections; diff --git a/engine/http/iweb.h b/engine/http/iweb.h index 2e7c3b8f6..3a46d6917 100644 --- a/engine/http/iweb.h +++ b/engine/http/iweb.h @@ -106,7 +106,7 @@ struct dl_download qdownload_t qdownload; /*stream config*/ - char url[MAX_OSPATH]; /*original url*/ + char *url; /*original url*/ char redir[MAX_OSPATH]; /*current redirected url*/ char localname[MAX_OSPATH]; /*leave empty for a temp file*/ struct vfsfile_s *file; /*downloaded to, if not already set when starting will open localname or a temp file*/ diff --git a/engine/http/iwebiface.c b/engine/http/iwebiface.c index abfedf2b9..4892d8ac5 100644 --- a/engine/http/iwebiface.c +++ b/engine/http/iwebiface.c @@ -10,7 +10,7 @@ qboolean SV_AllowDownload (const char *name) { return true; } -char com_token[1024]; +char com_token[sizeof(com_token)]; com_tokentype_t com_tokentype; int com_argc; const char **com_argv; @@ -103,7 +103,7 @@ int main(int argc, char **argv) } -void COM_EnumerateFiles (const char *match, int (*func)(const char *, int, void *, searchpathfuncs_t *f), void *parm) +void COM_EnumerateFiles (const char *match, int (*func)(const char *, qofs_t, void *, searchpathfuncs_t *f), void *parm) { HANDLE r; WIN32_FIND_DATA fd; diff --git a/engine/server/pr_cmds.c b/engine/server/pr_cmds.c index f299eafaf..bbfff2d8f 100644 --- a/engine/server/pr_cmds.c +++ b/engine/server/pr_cmds.c @@ -1017,6 +1017,7 @@ void PR_Decompile_f(void) } void PR_Compile_f(void) { + qboolean killondone = false; int argc=3; double time = Sys_DoubleTime(); char *argv[64] = {"", "-src", pr_sourcedir.string, "-srcfile", "progs.src"}; @@ -1054,10 +1055,16 @@ void PR_Compile_f(void) } if (!svprogfuncs) + { Q_SetProgsParms(true); + killondone = true; + } if (PR_StartCompile(svprogfuncs, argc, argv)) while(PR_ContinueCompile(svprogfuncs)); + if (killondone) + PR_Deinit(); + time = Sys_DoubleTime() - time; Con_TPrintf("Compile took %f secs\n", time); @@ -8904,7 +8911,7 @@ static void QCBUILTIN PF_clusterevent(pubprogfuncs_t *prinst, struct globalvars_ const char *dest = PR_GetStringOfs(prinst, OFS_PARM0); const char *src = PR_GetStringOfs(prinst, OFS_PARM1); const char *cmd = PR_GetStringOfs(prinst, OFS_PARM2); - const char *info = PF_VarString(prinst, 13, pr_globals); + const char *info = PF_VarString(prinst, 3, pr_globals); SSV_Send(dest, src, cmd, info); #endif } @@ -8973,10 +8980,10 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs {"bound", PF_Fixme, 0, 0, 0, 45, "float(float min,float value,float max)"}, {"pow", PF_Fixme, 0, 0, 0, 46, "float(float,float)"}, {"copyentity", PF_Fixme, 0, 0, 0, 47, "void(entity src, entity dst)"}, - {"fopen", PF_Fixme, 0, 0, 0, 48, "float(string filename, float mode)"}, - {"fclose", PF_Fixme, 0, 0, 0, 49, "void(float fhandle)"}, - {"fgets", PF_Fixme, 0, 0, 0, 50, "string(float fhandle)"}, - {"fputs", PF_Fixme, 0, 0, 0, 51, "void(float fhandle, string s)"}, + {"fopen", PF_Fixme, 0, 0, 0, 48, "filestream(string filename, float mode)"}, + {"fclose", PF_Fixme, 0, 0, 0, 49, "void(filestream fhandle)"}, + {"fgets", PF_Fixme, 0, 0, 0, 50, "string(filestream fhandle)"}, + {"fputs", PF_Fixme, 0, 0, 0, 51, "void(filestream fhandle, string s)"}, {"strlen", PF_Fixme, 0, 0, 0, 52, "float(string)"}, {"strcat", PF_Fixme, 0, 0, 0, 53, "string(string, optional string, optional string, optional string, optional string, optional string, optional string, optional string)"}, {"substring", PF_Fixme, 0, 0, 0, 54, "string(string s, float start, float length)"}, @@ -8999,10 +9006,10 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs {"cvar_string", PF_Fixme, 0, 0, 0, 71, "string(string name)"}, {"crash", PF_Fixme, 0, 0, 0, 72, "void()"}, {"stackdump", PF_Fixme, 0, 0, 0, 73, "void()"}, - {"search_begin", PF_Fixme, 0, 0, 0, 74, "float(string pattern, float caseinsensitive, float quiet)"}, - {"search_end", PF_Fixme, 0, 0, 0, 75, "void(float handle)"}, - {"search_getsize", PF_Fixme, 0, 0, 0, 76, "float(float handle)"}, - {"search_getfilename",PF_Fixme, 0, 0, 0, 77, "string(float handle, float num)"}, + {"search_begin", PF_Fixme, 0, 0, 0, 74, "searchhandle(string pattern, float caseinsensitive, float quiet)"}, + {"search_end", PF_Fixme, 0, 0, 0, 75, "void(searchhandle handle)"}, + {"search_getsize", PF_Fixme, 0, 0, 0, 76, "float(searchhandle handle)"}, + {"search_getfilename",PF_Fixme, 0, 0, 0, 77, "string(searchhandle handle, float num)"}, {"etof", PF_Fixme, 0, 0, 0, 79, "float(entity)"}, {"ftoe", PF_Fixme, 0, 0, 0, 80, "entity(float)"}, {"validstring", PF_Fixme, 0, 0, 0, 81, "float(string)"}, @@ -9161,10 +9168,10 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs // Tomaz - QuakeC String Manipulation End // Tomaz - QuakeC File System Begin (new mods use frik_file instead) - {"tq_fopen", PF_fopen, 0, 0, 0, 86, D("float(string filename, float mode)",NULL), true},// (QSG_FILE) - {"tq_fclose", PF_fclose, 0, 0, 0, 87, D("void(float fhandle)",NULL), true},// (QSG_FILE) - {"tq_fgets", PF_fgets, 0, 0, 0, 88, D("string(float fhandle)",NULL), true},// (QSG_FILE) - {"tq_fputs", PF_fputs, 0, 0, 0, 89, D("void(float fhandle, string s)",NULL), true},// (QSG_FILE) + {"tq_fopen", PF_fopen, 0, 0, 0, 86, D("filestream(string filename, float mode)",NULL), true},// (QSG_FILE) + {"tq_fclose", PF_fclose, 0, 0, 0, 87, D("void(filestream fhandle)",NULL), true},// (QSG_FILE) + {"tq_fgets", PF_fgets, 0, 0, 0, 88, D("string(filestream fhandle)",NULL), true},// (QSG_FILE) + {"tq_fputs", PF_fputs, 0, 0, 0, 89, D("void(filestream fhandle, string s)",NULL), true},// (QSG_FILE) // Tomaz - QuakeC File System End {"rain_go", PF_h2rain_go, 0, 0, 80, 0}, //80 @@ -9260,10 +9267,10 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs //End TEU_SHOWLMP2 //frik file - {"fopen", PF_fopen, 0, 0, 0, 110, "float(string filename, float mode, optional float mmapminsize)"}, // (FRIK_FILE) - {"fclose", PF_fclose, 0, 0, 0, 111, "void(float fhandle)"}, // (FRIK_FILE) - {"fgets", PF_fgets, 0, 0, 0, 112, "string(float fhandle)"}, // (FRIK_FILE) - {"fputs", PF_fputs, 0, 0, 0, 113, "void(float fhandle, string s, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6, optional string s7)"}, // (FRIK_FILE) + {"fopen", PF_fopen, 0, 0, 0, 110, "filestream(string filename, float mode, optional float mmapminsize)"}, // (FRIK_FILE) + {"fclose", PF_fclose, 0, 0, 0, 111, "void(filestream fhandle)"}, // (FRIK_FILE) + {"fgets", PF_fgets, 0, 0, 0, 112, "string(filestream fhandle)"}, // (FRIK_FILE) + {"fputs", PF_fputs, 0, 0, 0, 113, "void(filestream fhandle, string s, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6, optional string s7)"}, // (FRIK_FILE) {"strlen", PF_strlen, 0, 0, 0, 114, "float(string s)"}, // (FRIK_FILE) {"strcat", PF_strcat, 0, 0, 0, 115, "string(string s1, optional string s2, ...)"}, // (FRIK_FILE) {"substring", PF_substring, 0, 0, 0, 116, "string(string s, float start, float length)"}, // (FRIK_FILE) @@ -9284,7 +9291,7 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs {"externvalue", PF_externvalue, 0, 0, 0, 203, D("__variant(float prnum, string varname)", "Reads a global in the named progs by the name of that global.\nprnum=0 is the 'default' or 'main' progs.\nprnum=-1 means current progs.\nprnum=-2 will scan through the active progs and will use the first it finds.")}, {"externset", PF_externset, 0, 0, 0, 204, D("void(float prnum, __variant newval, string varname)", "Sets a global in the named progs by name.\nprnum=0 is the 'default' or 'main' progs.\nprnum=-1 means current progs.\nprnum=-2 will scan through the active progs and will use the first it finds.")}, {"externrefcall", PF_externrefcall, 0, 0, 0, 205, D("__variant(float prnum, void() func, ...)","Calls a function between progs by its reference. No longer needed as direct function calls now switch progs context automatically, and have done for a long time. There is no remaining merit for this function."), true}, - {"instr", PF_instr, 0, 0, 0, 206, D("float(string input, string token)", "Returns substring(input, strstrpot(input, token), -1), or the null string if token was not found in input. You're probably better off using strstrpos.")}, + {"instr", PF_instr, 0, 0, 0, 206, D("float(string input, string token)", "Returns substring(input, strstrpos(input, token), -1), or the null string if token was not found in input. You're probably better off using strstrpos.")}, #if defined(Q2BSPS) || defined(Q3BSPS) {"openportal", PF_OpenPortal, 0, 0, 0, 207, D("void(entity portal, float state)", "Opens or closes the portals associated with a door or some such on q2 or q3 maps. On Q2BSPs, the entity should be the 'func_areaportal' entity - its style field will say which portal to open. On Q3BSPs, the entity is the door itself, the portal will be determined by the two areas found from a preceding setorigin call.")}, #endif @@ -9317,8 +9324,8 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs {"chr2str", PF_chr2str, 0, 0, 0, 223, D("string(float chr, ...)", "The input floats are considered character values, and are concatenated.")}, {"strconv", PF_strconv, 0, 0, 0, 224, D("string(float ccase, float redalpha, float redchars, string str, ...)", "Converts quake chars in the input string amongst different representations.\nccase specifies the new case for letters.\n 0: not changed.\n 1: forced to lower case.\n 2: forced to upper case.\nredalpha and redchars switch between colour ranges.\n 0: no change.\n 1: Forced white.\n 2: Forced red.\n 3: Forced gold(low) (numbers only).\n 4: Forced gold (high) (numbers only).\n 5+6: Forced to white and red alternately.\nYou should not use this builtin in combination with UTF-8.")}, {"strpad", PF_strpad, 0, 0, 0, 225, D("string(float pad, string str1, ...)", "Pads the string with spaces, to ensure its a specific length (so long as a fixed-width font is used, anyway). If pad is negative, the spaces are added on the left. If positive the padding is on the right.")}, //will be moved - {"infoadd", PF_infoadd, 0, 0, 0, 226, D("string(string old, string key, string value)", "Returns a new tempstring infostring with the named value changed (or added if it was previously unspecified). Key and value may not contain the \\ character.")}, - {"infoget", PF_infoget, 0, 0, 0, 227, D("string(string info, string key)", "Reads a named value from an infostring. The returned value is a tempstring")}, + {"infoadd", PF_infoadd, 0, 0, 0, 226, D("string(infostring old, string key, string value)", "Returns a new tempstring infostring with the named value changed (or added if it was previously unspecified). Key and value may not contain the \\ character.")}, + {"infoget", PF_infoget, 0, 0, 0, 227, D("string(infostring info, string key)", "Reads a named value from an infostring. The returned value is a tempstring")}, // {"strcmp", PF_strncmp, 0, 0, 0, 228, D("float(string s1, string s2)", "Compares the two strings exactly. s1ofs allows you to treat s2 as a substring to compare against, or should be 0.\nReturns 0 if the two strings are equal, a negative value if s1 appears numerically lower, and positive if s1 appears numerically higher.")}, {"strncmp", PF_strncmp, 0, 0, 0, 228, D("#define strcmp strncmp\nfloat(string s1, string s2, optional float len, optional float s1ofs, optional float s2ofs)", "Compares up to 'len' chars in the two strings. s1ofs allows you to treat s2 as a substring to compare against, or should be 0.\nReturns 0 if the two strings are equal, a negative value if s1 appears numerically lower, and positive if s1 appears numerically higher.")}, {"strcasecmp", PF_strncasecmp, 0, 0, 0, 229, D("float(string s1, string s2)", "Compares the two strings without case sensitivity.\nReturns 0 if they are equal. The sign of the return value may be significant, but should not be depended upon.")}, @@ -9393,13 +9400,13 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs {"frametoname", PF_frametoname, 0, 0, 0, 284, "string(float modidx, float framenum)"}, {"skintoname", PF_skintoname, 0, 0, 0, 285, "string(float modidx, float skin)"}, // {"cvar_setlatch", PF_cvar_setlatch, 0, 0, 0, 286, "void(string cvarname, optional string value)"}, - {"hash_createtab", PF_hash_createtab, 0, 0, 0, 287, D("float(float tabsize, optional float defaulttype)", "Creates a hash table object with at least 'tabsize' slots. hash table with index 0 is a game-persistant table and will NEVER be returned by this builtin (except as an error return).")}, - {"hash_destroytab", PF_hash_destroytab, 0, 0, 0, 288, D("void(float table)", "Destroys a hash table object.")}, - {"hash_add", PF_hash_add, 0, 0, 0, 289, D("void(float table, string name, __variant value, optional float flags, optional float type)", "Adds the given key with the given value to the table.\nIf flags&HASH_REPLACE, the old value will be removed, if not set then multiple values may be added for a single key, they won't overwrite.\nThe type argument describes how the value should be stored and saved to files. While you can claim that all variables are just vectors, being more precise can result in less issues with tempstrings or saved games.")}, - {"hash_get", PF_hash_get, 0, 0, 0, 290, D("__variant(float table, string name, __variant deflt, optional float requiretype, optional float index)", "looks up the specified key name in the hash table. returns deflt if key was not found. If stringsonly=1, the return value will be in the form of a tempstring, otherwise it'll be the original value argument exactly as it was. If requiretype is specified, then values not of the specified type will be ignored. Hurrah for multiple types with the same name.")}, - {"hash_delete", PF_hash_delete, 0, 0, 0, 291, D("__variant(float table, string name)", "removes the named key. returns the value of the object that was destroyed, or 0 on error.")}, - {"hash_getkey", PF_hash_getkey, 0, 0, 0, 292, D("string(float table, float idx)", "gets some random key name. add+delete can change return values of this, so don't blindly increment the key index if you're removing all.")}, - {"hash_getcb", PF_hash_getcb, 0, 0, 0, 293, D("void(float table, void(string keyname, __variant val) callback, optional string name)", "For each item in the table that matches the name, call the callback. if name is omitted, will enumerate ALL keys."), true}, + {"hash_createtab", PF_hash_createtab, 0, 0, 0, 287, D("hashtable(float tabsize, optional float defaulttype)", "Creates a hash table object with at least 'tabsize' slots. hash table with index 0 is a game-persistant table and will NEVER be returned by this builtin (except as an error return).")}, + {"hash_destroytab", PF_hash_destroytab, 0, 0, 0, 288, D("void(hashtable table)", "Destroys a hash table object.")}, + {"hash_add", PF_hash_add, 0, 0, 0, 289, D("void(hashtable table, string name, __variant value, optional float typeandflags)", "Adds the given key with the given value to the table.\nIf flags&HASH_REPLACE, the old value will be removed, if not set then multiple values may be added for a single key, they won't overwrite.\nThe type argument describes how the value should be stored and saved to files. While you can claim that all variables are just vectors, being more precise can result in less issues with tempstrings or saved games.")}, + {"hash_get", PF_hash_get, 0, 0, 0, 290, D("__variant(hashtable table, string name, optional __variant deflt, optional float requiretype, optional float index)", "looks up the specified key name in the hash table. returns deflt if key was not found. If stringsonly=1, the return value will be in the form of a tempstring, otherwise it'll be the original value argument exactly as it was. If requiretype is specified, then values not of the specified type will be ignored. Hurrah for multiple types with the same name.")}, + {"hash_delete", PF_hash_delete, 0, 0, 0, 291, D("__variant(hashtable table, string name)", "removes the named key. returns the value of the object that was destroyed, or 0 on error.")}, + {"hash_getkey", PF_hash_getkey, 0, 0, 0, 292, D("string(hashtable table, float idx)", "gets some random key name. add+delete can change return values of this, so don't blindly increment the key index if you're removing all.")}, + {"hash_getcb", PF_hash_getcb, 0, 0, 0, 293, D("void(hashtable table, void(string keyname, __variant val) callback, optional string name)", "For each item in the table that matches the name, call the callback. if name is omitted, will enumerate ALL keys."), true}, {"checkcommand", PF_checkcommand, 0, 0, 0, 294, D("float(string name)", "Checks to see if the supplied name is a valid command, cvar, or alias. Returns 0 if it does not exist.")}, {"argescape", PF_argescape, 0, 0, 0, 295, D("string(string s)", "Marks up a string so that it can be reliably tokenized as a single argument later.")}, {"clusterevent", PF_clusterevent, 0, 0, 0, 296, D("void(string dest, string from, string cmd, string info)", "Only functions in mapcluster mode. Sends an event to whichever server the named player is on. The destination server can then dispatch the event to the client or handle it itself via the SV_ParseClusterEvent entrypoint. If dest is empty, the event is broadcast to ALL servers. If the named player can't be found, the event will be returned to this server with the cmd prefixed with 'error:'.")}, @@ -9586,16 +9593,16 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs #ifndef SERVERONLY //begin menu-only - {"buf_create", PF_Fixme, 0, 0, 0, 440, "float()"},//DP_QC_STRINGBUFFERS - {"buf_del", PF_Fixme, 0, 0, 0, 441, "void(float bufhandle)"},//DP_QC_STRINGBUFFERS - {"buf_getsize", PF_Fixme, 0, 0, 0, 442, "float(float bufhandle)"},//DP_QC_STRINGBUFFERS - {"buf_copy", PF_Fixme, 0, 0, 0, 443, "void(float bufhandle_from, float bufhandle_to)"},//DP_QC_STRINGBUFFERS - {"buf_sort", PF_Fixme, 0, 0, 0, 444, "void(float bufhandle, float sortprefixlen, float backward)"},//DP_QC_STRINGBUFFERS - {"buf_implode", PF_Fixme, 0, 0, 0, 445, "string(float bufhandle, string glue)"},//DP_QC_STRINGBUFFERS - {"bufstr_get", PF_Fixme, 0, 0, 0, 446, "string(float bufhandle, float string_index)"},//DP_QC_STRINGBUFFERS - {"bufstr_set", PF_Fixme, 0, 0, 0, 447, "void(float bufhandle, float string_index, string str)"},//DP_QC_STRINGBUFFERS - {"bufstr_add", PF_Fixme, 0, 0, 0, 448, "float(float bufhandle, string str, float order)"},//DP_QC_STRINGBUFFERS - {"bufstr_free", PF_Fixme, 0, 0, 0, 449, "void(float bufhandle, float string_index)"},//DP_QC_STRINGBUFFERS + {"buf_create", PF_Fixme, 0, 0, 0, 440, "strbuf()"},//DP_QC_STRINGBUFFERS + {"buf_del", PF_Fixme, 0, 0, 0, 441, "void(strbuf bufhandle)"},//DP_QC_STRINGBUFFERS + {"buf_getsize", PF_Fixme, 0, 0, 0, 442, "float(strbuf bufhandle)"},//DP_QC_STRINGBUFFERS + {"buf_copy", PF_Fixme, 0, 0, 0, 443, "void(strbuf bufhandle_from, float bufhandle_to)"},//DP_QC_STRINGBUFFERS + {"buf_sort", PF_Fixme, 0, 0, 0, 444, "void(strbuf bufhandle, float sortprefixlen, float backward)"},//DP_QC_STRINGBUFFERS + {"buf_implode", PF_Fixme, 0, 0, 0, 445, "string(strbuf bufhandle, string glue)"},//DP_QC_STRINGBUFFERS + {"bufstr_get", PF_Fixme, 0, 0, 0, 446, "string(strbuf bufhandle, float string_index)"},//DP_QC_STRINGBUFFERS + {"bufstr_set", PF_Fixme, 0, 0, 0, 447, "void(strbuf bufhandle, float string_index, string str)"},//DP_QC_STRINGBUFFERS + {"bufstr_add", PF_Fixme, 0, 0, 0, 448, "float(strbuf bufhandle, string str, float order)"},//DP_QC_STRINGBUFFERS + {"bufstr_free", PF_Fixme, 0, 0, 0, 449, "void(strbuf bufhandle, float string_index)"},//DP_QC_STRINGBUFFERS {"iscachedpic", PF_Fixme, 0, 0, 0, 451, "float(string name)"},// (EXT_CSQC) {"precache_pic", PF_Fixme, 0, 0, 0, 452, "string(string name, optional float trywad)"},// (EXT_CSQC) {"freepic", PF_Fixme, 0, 0, 0, 453, "void(string name)"},// (EXT_CSQC) @@ -9622,10 +9629,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, 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, 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.")}, + {"search_begin", PF_search_begin, 0, 0, 0, 444, D("searchhandle(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(searchhandle handle)"}, + {"search_getsize", PF_search_getsize, 0, 0, 0, 446, D("float(searchhandle handle)", "Retrieves the number of files that were found.")}, + {"search_getfilename", PF_search_getfilename,0, 0, 0, 447, D("string(searchhandle 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 @@ -9638,16 +9645,16 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs // {"te_flamejet", PF_te_flamejet, 0, 0, 0, 457, "void(vector org, vector vel, float howmany)"},//DP_TE_FLAMEJET // {"undefined", PF_Fixme, 0, 0, 0, 458, ""}, {"edict_num", PF_edict_for_num, 0, 0, 0, 459, "entity(float entnum)"},//DP_QC_EDICT_NUM - {"buf_create", PF_buf_create, 0, 0, 0, 460, "float()"},//DP_QC_STRINGBUFFERS - {"buf_del", PF_buf_del, 0, 0, 0, 461, "void(float bufhandle)"},//DP_QC_STRINGBUFFERS - {"buf_getsize", PF_buf_getsize, 0, 0, 0, 462, "float(float bufhandle)"},//DP_QC_STRINGBUFFERS - {"buf_copy", PF_buf_copy, 0, 0, 0, 463, "void(float bufhandle_from, float bufhandle_to)"},//DP_QC_STRINGBUFFERS - {"buf_sort", PF_buf_sort, 0, 0, 0, 464, "void(float bufhandle, float sortprefixlen, float backward)"},//DP_QC_STRINGBUFFERS - {"buf_implode", PF_buf_implode, 0, 0, 0, 465, "string(float bufhandle, string glue)"},//DP_QC_STRINGBUFFERS - {"bufstr_get", PF_bufstr_get, 0, 0, 0, 466, "string(float bufhandle, float string_index)"},//DP_QC_STRINGBUFFERS - {"bufstr_set", PF_bufstr_set, 0, 0, 0, 467, "void(float bufhandle, float string_index, string str)"},//DP_QC_STRINGBUFFERS - {"bufstr_add", PF_bufstr_add, 0, 0, 0, 468, "float(float bufhandle, string str, float order)"},//DP_QC_STRINGBUFFERS - {"bufstr_free", PF_bufstr_free, 0, 0, 0, 469, "void(float bufhandle, float string_index)"},//DP_QC_STRINGBUFFERS + {"buf_create", PF_buf_create, 0, 0, 0, 460, "strbuf()"},//DP_QC_STRINGBUFFERS + {"buf_del", PF_buf_del, 0, 0, 0, 461, "void(strbuf bufhandle)"},//DP_QC_STRINGBUFFERS + {"buf_getsize", PF_buf_getsize, 0, 0, 0, 462, "float(strbuf bufhandle)"},//DP_QC_STRINGBUFFERS + {"buf_copy", PF_buf_copy, 0, 0, 0, 463, "void(strbuf bufhandle_from, strbuf bufhandle_to)"},//DP_QC_STRINGBUFFERS + {"buf_sort", PF_buf_sort, 0, 0, 0, 464, "void(strbuf bufhandle, float sortprefixlen, float backward)"},//DP_QC_STRINGBUFFERS + {"buf_implode", PF_buf_implode, 0, 0, 0, 465, "string(strbuf bufhandle, string glue)"},//DP_QC_STRINGBUFFERS + {"bufstr_get", PF_bufstr_get, 0, 0, 0, 466, "string(strbuf bufhandle, float string_index)"},//DP_QC_STRINGBUFFERS + {"bufstr_set", PF_bufstr_set, 0, 0, 0, 467, "void(strbuf bufhandle, float string_index, string str)"},//DP_QC_STRINGBUFFERS + {"bufstr_add", PF_bufstr_add, 0, 0, 0, 468, "float(strbuf bufhandle, string str, float order)"},//DP_QC_STRINGBUFFERS + {"bufstr_free", PF_bufstr_free, 0, 0, 0, 469, "void(strbuf bufhandle, float string_index)"},//DP_QC_STRINGBUFFERS //end non-menu // {"undefined", PF_Fixme, 0, 0, 0, 470, ""}, @@ -9699,7 +9706,7 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs {"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)"}, - {"buf_cvarlist", PF_buf_cvarlist, 0, 0, 0, 517, "void(float strbuf)"}, + {"buf_cvarlist", PF_buf_cvarlist, 0, 0, 0, 517, "void(strbuf strbuf)"}, {"cvar_description",PF_cvar_description,0, 0, 0, 518, "string(string cvarname)"}, {"gettime", PF_gettime, 0, 0, 0, 519, "float(optional float timetype)"}, {"keynumtostring_omgwtf",PF_Fixme, 0, 0, 0, 520, "string(float keynum)"}, //excessive third version in dp's csqc. @@ -9722,8 +9729,8 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs // {"log", PF_Fixme, 0, 0, 0, 532, "float(string mname)", true}, {"getsoundtime", PF_Ignore, 0, 0, 0, 533, "float(entity e, float channel)"}, {"soundlength", PF_Ignore, 0, 0, 0, 534, "float(string sample)"}, - {"buf_loadfile", PF_buf_loadfile, 0, 0, 0, 535, "float(string filename, float bufhandle)"}, - {"buf_writefile", PF_buf_writefile, 0, 0, 0, 536, "float(float filehandle, float bufhandle, optional float startpos, optional float numstrings)"}, + {"buf_loadfile", PF_buf_loadfile, 0, 0, 0, 535, "float(string filename, strbuf bufhandle)"}, + {"buf_writefile", PF_buf_writefile, 0, 0, 0, 536, "float(filestream filehandle, strbuf bufhandle, optional float startpos, optional float numstrings)"}, // {"bufstr_find", PF_Fixme, 0, 0, 0, 537, "float(float bufhandle, string match, float matchrule, float startpos)"}, // {"matchpattern", PF_Fixme, 0, 0, 0, 538, "float(string s, string pattern, float matchrule)"}, // {"undefined", PF_Fixme, 0, 0, 0, 539, ""}, @@ -9739,7 +9746,7 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs {"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, 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.")}, + {"writetofile", PF_writetofile, 0, 0, 0, 606, D("void(filestream 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. @@ -10133,6 +10140,7 @@ void PR_DumpPlatform_f(void) char dbgfname[MAX_OSPATH]; unsigned int targ = 0; qboolean defines = false; + qboolean accessors = false; char *comment; /*this list is here to ensure that the file can be used as a valid initial qc file (ignoring precompiler options)*/ @@ -10369,7 +10377,7 @@ void PR_DumpPlatform_f(void) {"TRUE", "const float", ALL, NULL, 1}, {"FALSE", "const float", ALL, "File not found...", 0}, - {"M_PI", "const float", ALL, "File not found...", M_PI}, + {"M_PI", "const float", ALL, NULL, M_PI}, {"MOVETYPE_NONE", "const float", QW|NQ|CS, NULL, MOVETYPE_NONE}, {"MOVETYPE_WALK", "const float", QW|NQ|CS, NULL, MOVETYPE_WALK}, @@ -10585,16 +10593,15 @@ void PR_DumpPlatform_f(void) {"EV_VECTOR", "const float", QW|NQ, NULL, ev_vector}, {"EV_ENTITY", "const float", QW|NQ, NULL, ev_entity}, // {"EV_FIELD", "const float", QW|NQ, NULL, ev_field}, -// {"EV_FUNCTION", "const float", QW|NQ, NULL, ev_function}, -// {"EV_POINTER", "const float", QW|NQ, NULL, ev_pointer}, + {"EV_FUNCTION", "const float", QW|NQ, NULL, ev_function}, + {"EV_POINTER", "const float", QW|NQ, NULL, ev_pointer}, {"EV_INTEGER", "const float", QW|NQ, NULL, ev_integer}, -// {"EV_VARIANT", "const float", QW|NQ, NULL, ev_variant}, + {"EV_VARIANT", "const float", QW|NQ, NULL, ev_variant}, // {"EV_STRUCT", "const float", QW|NQ, NULL, ev_struct}, // {"EV_UNION", "const float", QW|NQ, NULL, ev_union}, - {"HASHT_PERSISTANT", "const float", ALL, "Special hash table index for hash_add and hash_get. Entries in this table will persist over map changes (and doesn't need to be created/deleted).", 0}, - {"HASH_REPLACE", "const float", ALL, "Used with hash_add. Attempts to remove the old value instead of adding two values for a single key.", 1}, - {"HASH_STRING", "const float", ALL, "Used with hash_add. Specifies that the contents of the string argument should be internally zoned.", 2}, + {"gamestate", "hashtable", ALL, "Special hash table index for hash_add and hash_get. Entries in this table will persist over map changes (and doesn't need to be created/deleted).", 0}, + {"HASH_REPLACE", "const float", ALL, "Used with hash_add. Attempts to remove the old value instead of adding two values for a single key.", 256}, {"STAT_HEALTH", "const float", CS, NULL, STAT_HEALTH}, {"STAT_WEAPON", "const float", CS, NULL, STAT_WEAPON}, @@ -10779,6 +10786,10 @@ void PR_DumpPlatform_f(void) defines = true; if (!stricmp(Cmd_Argv(i), "-Fnodefines")) defines = false; + if (!stricmp(Cmd_Argv(i), "-Faccessors")) + accessors = true; + if (!stricmp(Cmd_Argv(i), "-Fnoaccessors")) + accessors = false; if (!stricmp(Cmd_Argv(i), "-O")) fname = Cmd_Argv(++i); } @@ -10808,6 +10819,7 @@ void PR_DumpPlatform_f(void) "-Tcs - dump specifically CSQC fields\n" "-Tmenu - dump specifically menuqc fields\n" "-Fdefines - generate #defines instead of constants\n" + "-Faccessors - use accessors instead of basic types via defines\n" "-O - write to a different qc file\n" "*/\n" , FULLENGINENAME, FTE_VER_MAJOR, FTE_VER_MINOR, Cmd_Argv(0), Cmd_Args()); @@ -10877,6 +10889,25 @@ void PR_DumpPlatform_f(void) "#endif\n" ); + + if (accessors) + { + VFS_PRINTF(f, "accessor strbuf : float;\n"); + VFS_PRINTF(f, "accessor searchhandle : float;\n"); + VFS_PRINTF(f, "accessor hashtable : float;\n"); + VFS_PRINTF(f, "accessor infostring : string;\n"); + VFS_PRINTF(f, "accessor filestream : float;\n"); + } + else + { + VFS_PRINTF(f, "#define strbuf float\n"); + VFS_PRINTF(f, "#define searchhandle float\n"); + VFS_PRINTF(f, "#define hashtable float\n"); + VFS_PRINTF(f, "#define infostring string\n"); + VFS_PRINTF(f, "#define filestream float\n"); + } + + for (i = 0; knowndefs[i].name; i++) { for (j = 0; j < i; j++) @@ -10963,10 +10994,20 @@ void PR_DumpPlatform_f(void) comment = ""; if (!strcmp(knowndefs[i].type, "const float")) { - if (defines) - VFS_PRINTF(f, "#define %s %g%s\n", knowndefs[i].name, knowndefs[i].value, comment); + if (knowndefs[i].value >= (1<<23)) + { + if (defines) + VFS_PRINTF(f, "#define %s %i%s\n", knowndefs[i].name, (int)knowndefs[i].value, comment); + else + VFS_PRINTF(f, "%s %s = %i;%s\n", knowndefs[i].type, knowndefs[i].name, (int)knowndefs[i].value, comment); + } else - VFS_PRINTF(f, "%s %s = %g;%s\n", knowndefs[i].type, knowndefs[i].name, knowndefs[i].value, comment); + { + if (defines) + VFS_PRINTF(f, "#define %s %g%s\n", knowndefs[i].name, knowndefs[i].value, comment); + else + VFS_PRINTF(f, "%s %s = %g;%s\n", knowndefs[i].type, knowndefs[i].name, knowndefs[i].value, comment); + } } else if (!strcmp(knowndefs[i].type, "const string")) { @@ -11198,6 +11239,42 @@ void PR_DumpPlatform_f(void) VFS_PRINTF(f, "#endif\n"); } + if (accessors) + { + VFS_PRINTF(f, + "accessor strbuf : float\n{\n" + "\tget float asfloat[float idx] = {return stof(bufstr_get(this, idx));};\n" + "\tset float asfloat[float idx] = {bufstr_set(this, idx, ftos(value));};\n" + "\tget string[float] = bufstr_get;\n" + "\tset string[float] = bufstr_set;\n" + "\tget float length = buf_getsize;\n" + "};\n"); + VFS_PRINTF(f, + "accessor searchhandle : float\n{\n" + "\tget string[float] = search_getfilename;\n" + "\tget float length = search_getsize;\n" + "};\n"); + VFS_PRINTF(f, + "accessor hashtable : float\n{\n" + "\tget vector v[string key] = {return hash_get(this, key, '0 0 0', EV_VECTOR);};\n" + "\tset vector v[string key] = {hash_add(this, key, value, 1, EV_VECTOR);};\n" + "\tget string s[string key] = {return hash_get(this, key, \"\", EV_STRING);};\n" + "\tset string s[string key] = {hash_add(this, key, value, 1, EV_STRING);};\n" + "\tget string f[string key] = {return hash_get(this, key, 0.0, EV_FLOAT);};\n" + "\tset string f[string key] = {hash_add(this, key, value, 1, EV_FLOAT);};\n" + "\tget __variant[string key] = {return hash_get(this, key, __NULL__);};\n" + "\tset __variant[string key] = {hash_add(this, key, value, 1);};\n" + "};\n"); + VFS_PRINTF(f, + "accessor infostring : string\n{\n" + "\tget string[string] = infoget;\n" + "};\n"); + VFS_PRINTF(f, + "accessor filestream : float\n{\n" + "\tget string = fgets;\n" + "\tset string = {fputs(this,value);};\n" + "};\n"); + } VFS_PRINTF(f, "#pragma noref 0\n"); diff --git a/engine/server/server.h b/engine/server/server.h index 076ad3b4d..715ac07e3 100644 --- a/engine/server/server.h +++ b/engine/server/server.h @@ -1022,6 +1022,7 @@ void SSV_InstructMaster(sizebuf_t *cmd); void SSV_PrintToMaster(char *s); void SSV_ReadFromControlServer(void); void SSV_SavePlayerStats(client_t *cl, int reason); //initial, periodic (in case of node crashes), part +void SSV_RequestShutdown(void); //asks the cluster to not send us new players void Sys_InstructSlave(pubsubserver_t *s, sizebuf_t *cmd); int Sys_SubServerRead(pubsubserver_t *s); //1: yes. 0: no. -1: error diff --git a/engine/server/sv_ccmds.c b/engine/server/sv_ccmds.c index feab03116..a2868280e 100644 --- a/engine/server/sv_ccmds.c +++ b/engine/server/sv_ccmds.c @@ -569,6 +569,9 @@ void SV_Map_f (void) #ifndef SERVERONLY SCR_SetLoadingStage(LS_NONE); #endif + + if (SSV_IsSubServer()) + Cbuf_AddText("\nquit\n", RESTRICT_LOCAL); return; } } diff --git a/engine/server/sv_cluster.c b/engine/server/sv_cluster.c index b49bd0556..9a12ff359 100644 --- a/engine/server/sv_cluster.c +++ b/engine/server/sv_cluster.c @@ -14,12 +14,6 @@ void VARGS SV_RejectMessage(int protocol, char *format, ...); void MSV_UpdatePlayerStats(unsigned int playerid, unsigned int serverid, int numstats, float *stats); -static char *knownmaps[] = -{ - "", - "start" -}; - typedef struct { //fixme: hash tables unsigned int playerid; @@ -93,11 +87,7 @@ pubsubserver_t *MSV_StartSubServer(unsigned int id, const char *mapname) if (s) { if (!id) - { - if (nextserverid < sizeof(knownmaps)/sizeof(knownmaps[0])) - nextserverid = sizeof(knownmaps)/sizeof(knownmaps[0]); - id = nextserverid++; - } + id = ++nextserverid; s->id = id; s->next = subservers; subservers = s; @@ -125,33 +115,54 @@ pubsubserver_t *MSV_FindSubServer(unsigned int id) return s; } - if (!s && id >= 1 && id < sizeof(knownmaps)/sizeof(knownmaps[0])) - s = MSV_StartSubServer(id, knownmaps[id]); - - return s; + return NULL; } -pubsubserver_t *MSV_FindSubServerName(const char *mapname) + +//"5" finds server 5 only +//"5:dm4" finds server 5, and will start it if not known (even if a different node is running the same map) +//"0:dm4" starts a new server running dm4, even if its already running +//":dm4" finds any server running dm4. starts a new one if needed. +pubsubserver_t *MSV_FindSubServerName(const char *servername) { 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); - } + unsigned int id; + qboolean forcenew = false; + char *mapname; - for (s = subservers; s; s = s->next) + id = strtoul(servername, &mapname, 0); + if (*mapname == ':') { - if (!strcmp(s->name, mapname)) + if (!id && servername != mapname) + forcenew = true; + mapname++; + } + else + mapname = ""; + + if (id) + { + s = MSV_FindSubServer(id); + if (s) return s; } - return MSV_StartSubServer(0, mapname); -} -qboolean MSV_AddressForMap(netadr_t *ret, int natype, int serverid) -{ - pubsubserver_t *s = MSV_FindSubServer(serverid); + if (*mapname) + { + if (!forcenew) + { + for (s = subservers; s; s = s->next) + { + if (!strcmp(s->name, mapname)) + return s; + } + } + return MSV_StartSubServer(id, mapname); + } + return NULL; +} +qboolean MSV_AddressForServer(netadr_t *ret, int natype, pubsubserver_t *s) +{ if (s) { if (natype == s->addrv6.type) @@ -458,7 +469,7 @@ void MSV_ReadFromSubServer(pubsubserver_t *s) send.cursize = 2; NET_StringToAdr(claddr, 0, &cladr); - MSV_AddressForMap(&svadr, cladr.type, s->id); + MSV_AddressForServer(&svadr, cladr.type, s); if (!to) { if (svadr.type != NA_INVALID) @@ -525,6 +536,13 @@ void MSV_ReadFromSubServer(pubsubserver_t *s) for (s = subservers; s; s = s->next) Sys_InstructSlave(s, &send); } + else if (*dest == '\\') + { + //send to a specific server (backslashes should not be valid in infostrings, and thus not in names. + //FIXME: broadcasting for now. + for (s = subservers; s; s = s->next) + Sys_InstructSlave(s, &send); + } else { //send it to the server that the player is currently on. @@ -940,15 +958,15 @@ qboolean MSV_ClusterLoginReply(netadr_t *legacyclientredirect, unsigned int serv { char tmpbuf[256]; netadr_t serveraddr; + pubsubserver_t *s = NULL; - if (!serverid) - serverid = 1; + if (!s) + s = MSV_FindSubServerName(":start"); - if (!MSV_AddressForMap(&serveraddr, clientaddr->type, serverid) && !MSV_AddressForMap(&serveraddr, clientaddr->type, serverid=1)) + if (!s || !MSV_AddressForServer(&serveraddr, clientaddr->type, s)) SV_RejectMessage(SCP_QUAKEWORLD, "Unable to find lobby.\n"); else { - pubsubserver_t *s; sizebuf_t send; qbyte send_buf[MAX_QWMSGLEN]; clusterplayer_t *pl; @@ -963,7 +981,7 @@ qboolean MSV_ClusterLoginReply(netadr_t *legacyclientredirect, unsigned int serv NET_AdrToString(pl->address, sizeof(pl->address), clientaddr); pl->playerid = playerid; InsertLinkBefore(&pl->allplayers, &clusterplayers); - pl->server = s = MSV_FindSubServer(serverid); + pl->server = s; MSG_WriteByte(&send, ccmd_takeplayer); MSG_WriteLong(&send, playerid); diff --git a/engine/server/sv_user.c b/engine/server/sv_user.c index db9315289..233aebdb0 100644 --- a/engine/server/sv_user.c +++ b/engine/server/sv_user.c @@ -3764,41 +3764,42 @@ void SV_SetInfo_f (void) return; } - if (Cmd_Argv(1)[0] == '*') + key = Cmd_Argv(1); + + if (key[0] == '*') return; // don't set priveledged values - if (strstr(Cmd_Argv(1), "\\") || strstr(Cmd_Argv(2), "\\")) + if (strstr(key, "\\") || strstr(Cmd_Argv(2), "\\")) return; // illegal char - Q_strncpyz(oldval, Info_ValueForKey(host_client->userinfo, Cmd_Argv(1)), sizeof(oldval)); + Q_strncpyz(oldval, Info_ValueForKey(host_client->userinfo, key), sizeof(oldval)); #ifdef VM_Q1 if (Q1QVM_UserInfoChanged(sv_player)) return; #endif - Info_SetValueForKey (host_client->userinfo, Cmd_Argv(1), Cmd_Argv(2), sizeof(host_client->userinfo)); + Info_SetValueForKey (host_client->userinfo, key, Cmd_Argv(2), sizeof(host_client->userinfo)); // name is extracted below in ExtractFromUserInfo // strncpy (host_client->name, Info_ValueForKey (host_client->userinfo, "name") // , sizeof(host_client->name)-1); // SV_FullClientUpdate (host_client, &sv.reliable_datagram); // host_client->sendinfo = true; - if (!strcmp(Info_ValueForKey(host_client->userinfo, Cmd_Argv(1)), oldval)) + if (!strcmp(Info_ValueForKey(host_client->userinfo, key), oldval)) return; // key hasn't changed // process any changed values SV_ExtractFromUserinfo (host_client, true); - if (progstype != PROG_QW && !strcmp(Cmd_Argv(1), "bottomcolor")) + if (progstype != PROG_QW && !strcmp(key, "bottomcolor")) { //team fortress has a nasty habit of booting people without this sv_player->v->team = atoi(Cmd_Argv(2))+1; } - if (*Cmd_Argv(1) != '_') + if (*key != '_') { i = host_client - svs.clients; - key = Cmd_Argv(1); val = Info_ValueForKey(host_client->userinfo, key); basic = SV_UserInfoIsBasic(key); @@ -3845,9 +3846,11 @@ void SV_SetInfo_f (void) } } - SV_LogPlayer(host_client, "userinfo changed"); + //doh't spam chat changes. they're not interesting, and just spammy. + if (strcmp(key, "chat")) + SV_LogPlayer(host_client, "userinfo changed"); - PR_ClientUserInfoChanged(Cmd_Argv(1), oldval, Info_ValueForKey(host_client->userinfo, Cmd_Argv(1))); + PR_ClientUserInfoChanged(key, oldval, Info_ValueForKey(host_client->userinfo, key)); } /* diff --git a/engine/web/fs_web.c b/engine/web/fs_web.c index 267d5e0b3..5866feb4e 100644 --- a/engine/web/fs_web.c +++ b/engine/web/fs_web.c @@ -74,12 +74,10 @@ static qboolean QDECL VFSWEB_ClosePersist(vfsfile_t *file) return VFSWEB_Close(file); } -vfsfile_t *FSWEB_OpenTemp(void) +vfsfile_t *FSWEB_OpenTempHandle(int f) { - int f; vfswebfile_t *file; - f = emscriptenfte_buf_create(); if (f == -1) { Con_Printf("FSWEB_OpenTemp failed\n"); @@ -102,6 +100,11 @@ vfsfile_t *FSWEB_OpenTemp(void) return &file->funcs; } +vfsfile_t *FSWEB_OpenTemp(void) +{ + return FSWEB_OpenTempHandle(emscriptenfte_buf_create()); +} + vfsfile_t *VFSWEB_Open(const char *osname, const char *mode, qboolean *needsflush) { int f; diff --git a/engine/web/ftejslib.h b/engine/web/ftejslib.h index 0c78285d2..0d6d5db16 100644 --- a/engine/web/ftejslib.h +++ b/engine/web/ftejslib.h @@ -36,6 +36,6 @@ int emscriptenfte_setupcanvas( void(*Mouse)(int devid,int abs,float x,float y,float z,float size), void(*Button)(int devid, int down, int mbutton), int(*Keyboard)(int devid, int down, int keycode, int unicode), - void(*Hash)(char *newhash) + void(*LoadFile)(char *newhash, int filehandle) ); diff --git a/engine/web/ftejslib.js b/engine/web/ftejslib.js index 52affe983..8491db864 100644 --- a/engine/web/ftejslib.js +++ b/engine/web/ftejslib.js @@ -29,6 +29,33 @@ mergeInto(LibraryManager.library, alert(msg); }, + emscriptenfte_handle_alloc__deps : ['$FTEH'], + emscriptenfte_handle_alloc : function(h) + { + for (var i = 0; FTEH.h.length; i+=1) + { + if (FTEH.h[i] == null) + { + FTEH.h[i] = h; + return i; + } + } + i = FTEH.h.length; + FTEH.h[i] = h; + return i; + }, + + //temp files + emscriptenfte_buf_createfromarraybuf__deps : ['emscriptenfte_handle_alloc'], + emscriptenfte_buf_createfromarraybuf : function(buf) + { + var len = buf.length; + var b = {h:-1, r:1, l:len,m:len,d:new Uint8Array(buf), n:null}; + b.h = _emscriptenfte_handle_alloc(b); + return b.h; + }, + + $FTEC__deps : ['emscriptenfte_buf_createfromarraybuf'], $FTEC: { ctxwarned:0, @@ -42,6 +69,20 @@ mergeInto(LibraryManager.library, key:0 }, + loadurl : function(url, arraybuf) + { + if (FTEC.evcb.loadfile != 0) + { + var handle = -1; + if (arraybuf !== undefined) + handle = _emscriptenfte_buf_createfromarraybuf(arraybuf); + var ptr = _malloc(url.length); + writeStringToMemory(url, ptr); + Runtime.dynCall('vii', FTEC.evcb.loadfile, [ptr, handle]); + _free(ptr); + } + }, + handleevent : function(event) { switch(event.type) @@ -138,28 +179,49 @@ mergeInto(LibraryManager.library, } event.preventDefault(); break; + case 'dragenter': + case 'dragover': + event.stopPropagation(); + event.preventDefault(); + break; + case 'drop': + event.stopPropagation(); + event.preventDefault(); + var files = event.dataTransfer.files; + for (var i = 0; i < files.length; i++) + { + var file = files[i]; + var reader = new FileReader(); + reader.onload = function(evt) + { + FTEC.loadurl(file.name, evt.target.result); + }; + reader.readAsArrayBuffer(file); + } + break; default: console.log(event); break; } } }, - emscriptenfte_setupcanvas__deps: ['$FTEC', '$Browser'], - emscriptenfte_setupcanvas : function(nw,nh,evresz,evm,evb,evk,evh) + emscriptenfte_setupcanvas__deps: ['$FTEC', '$Browser', 'emscriptenfte_buf_createfromarraybuf'], + emscriptenfte_setupcanvas : function(nw,nh,evresz,evm,evb,evk,evf) { FTEC.evcb.resize = evresz; FTEC.evcb.mouse = evm; FTEC.evcb.button = evb; FTEC.evcb.key = evk; - FTEC.evcb.hashchange = evh; + FTEC.evcb.loadfile = evf; if (!FTEC.donecb) { FTEC.donecb = 1; - ['mousedown', 'mouseup', 'mousemove', 'wheel', 'mousewheel', 'mouseout', 'keypress', 'keydown', 'keyup', 'touchstart', 'touchend', 'touchcancel', 'touchleave', 'touchmove'].forEach(function(event) + var events = ['mousedown', 'mouseup', 'mousemove', 'wheel', 'mousewheel', 'mouseout', 'keypress', 'keydown', 'keyup', 'touchstart', 'touchend', 'touchcancel', 'touchleave', 'touchmove', 'dragenter', 'dragover', 'drop']; + events.forEach(function(event) { Module['canvas'].addEventListener(event, FTEC.handleevent, true); }); - ['keydown', 'keyup', 'keypress'].forEach(function(event) + events.forEach(function(event) { document.addEventListener(event, FTEC.handleevent, true); }); @@ -172,7 +234,7 @@ mergeInto(LibraryManager.library, } if (Module.print === undefined) Module.print = function(msg){console.log(msg);}; - var ctx = Browser.createContext(Module['canvas'], true, true); + var ctx = Browser.createContext(Module['canvas'], true, true); if (ctx == null) { var msg = "Unable to set up webgl context.\n\nPlease use a browser that supports it and has it enabled\nYour graphics drivers may also be blacklisted, so try updating those too. woo, might as well update your entire operating system while you're at it.\nIt'll be expensive, but hey, its YOUR money, not mine.\nYou can probably just disable the blacklist, but please don't moan at me when your computer blows up, seriously, make sure those drivers are not too buggy.\nI knew a guy once. True story. Boring, but true.\nYou're probably missing out on something right now. Don't you just hate it when that happens?\nMeh, its probably just tinkertoys, right?\n\nYou know, you could always try Internet Explorer, you never know, hell might have frozen over.\nDon't worry, I wasn't serious.\n\nTum te tum. Did you get it working yet?\nDude, fix it already.\n\nThis message was brought to you by Sleep Deprivation, sponsoring quake since I don't know when"; @@ -202,17 +264,12 @@ mergeInto(LibraryManager.library, window.onresize(); if (FTEC.evcb.hashchange) - window.onhashchange = function() { - if (FTEC.evcb.hashchange != 0) + window.onhashchange = function() { - var val = location.hash; - var ptr = _malloc(val.length); - writeStringToMemory(val, ptr); - Runtime.dynCall('vi', FTEC.evcb.hashchange, [ptr]); - _free(ptr); - } - }; + FTEC.loadurl(location.hash.substring(1)); + }; + } return 1; }, @@ -228,23 +285,6 @@ mergeInto(LibraryManager.library, return Date.now(); }, - emscriptenfte_handle_alloc__deps : ['$FTEH'], - emscriptenfte_handle_alloc : function(h) - { - for (var i = 0; FTEH.h.length; i+=1) - { - if (FTEH.h[i] == null) - { - FTEH.h[i] = h; - return i; - } - } - i = FTEH.h.length; - FTEH.h[i] = h; - return i; - }, - - //temp files emscriptenfte_buf_create__deps : ['emscriptenfte_handle_alloc'], emscriptenfte_buf_create : function() { @@ -481,7 +521,7 @@ mergeInto(LibraryManager.library, http.onload = function(e) { -// console.log("onload: " + _url + " status " + http.status); +console.log("onload: " + _url + " status " + http.status); if (http.status == 200) { var bar = new Uint8Array(http.response); @@ -499,7 +539,7 @@ mergeInto(LibraryManager.library, http.onerror = function(e) { -// console.log("onerror: " + _url + " status " + http.status); +console.log("onerror: " + _url + " status " + http.status); if (onerror) Runtime.dynCall('vii', onerror, [ctx, http.status]); }; diff --git a/engine/web/gl_vidweb.c b/engine/web/gl_vidweb.c index a3bff58e6..62f478b81 100644 --- a/engine/web/gl_vidweb.c +++ b/engine/web/gl_vidweb.c @@ -151,15 +151,22 @@ static void DOM_ButtonEvent(int devid, int down, int button) IN_KeyEvent(devid, down, K_MOUSE1+button, 0); } } -void DOM_HashChanged(char *loc) +vfsfile_t *FSWEB_OpenTempHandle(int f); +void DOM_LoadFile(char *loc, int handle) { + vfsfile_t *file = NULL; + Con_Printf("DOM_LoadFile: %s %i\n", loc, handle); + if (handle != -1) + file = FSWEB_OpenTempHandle(handle); //try and open it. generally downloading it from the server. - Host_RunFile(loc+1, strlen(loc+1), NULL); + if (!Host_RunFile(loc, strlen(loc), file)) + { + if (file) + VFS_CLOSE(file); + } } qboolean GLVID_Init (rendererstate_t *info, unsigned char *palette) { - int flags; - vid_isfullscreen = true; if (!emscriptenfte_setupcanvas( @@ -169,7 +176,7 @@ qboolean GLVID_Init (rendererstate_t *info, unsigned char *palette) IN_MouseMove, DOM_ButtonEvent, DOM_KeyEvent, - DOM_HashChanged + DOM_LoadFile )) { Con_Printf("Couldn't set up canvas\n");