From 667e8dec10f53f494a0c28c2bfc8a55fb7b060af Mon Sep 17 00:00:00 2001 From: Spoike Date: Wed, 1 Jul 2015 23:15:25 +0000 Subject: [PATCH] add geometry shader support. because why not. bind command now makes it a bit easier to edit the binding. and any alias. autoid tweaks. slightly better q1qvm/ktx support. extend the api a little. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@4927 fc73d0e0-1445-4013-8a0c-d673dee63da5 --- engine/client/cl_pred.c | 3 +- engine/client/image.c | 7 + engine/client/keys.c | 42 +- engine/client/view.c | 26 +- engine/common/cmd.c | 4 +- engine/common/pr_bgcmd.c | 3 +- engine/common/pr_common.h | 4 +- engine/common/qvm.c | 19 +- engine/d3d/d3d11_backend.c | 2 + engine/d3d/d3d11_image.c | 8 - engine/d3d/d3d11_shader.c | 64 +- engine/d3d/d3d_image.c | 2 + engine/d3d/d3d_shader.c | 8 +- engine/gl/gl_backend.c | 6 +- engine/gl/gl_draw.c | 7 - engine/gl/gl_shader.c | 8 +- engine/gl/gl_vidcommon.c | 32 +- engine/gl/glquake.h | 4 +- engine/gl/glsupp.h | 4 + engine/gl/shader.h | 3 +- engine/http/iweb.h | 2 + engine/server/pr_cmds.c | 19 +- engine/server/pr_q1qvm.c | 1817 +++++++++++++++++++++-------------- engine/server/sv_main.c | 4 +- engine/server/sv_user.c | 69 +- plugins/ezhud/ezquakeisms.c | 36 + 26 files changed, 1408 insertions(+), 795 deletions(-) diff --git a/engine/client/cl_pred.c b/engine/client/cl_pred.c index 9f55db972..2f036e27b 100644 --- a/engine/client/cl_pred.c +++ b/engine/client/cl_pred.c @@ -896,10 +896,11 @@ void CL_PredictMovePNum (int seat) pv->nolocalplayer = !!(cls.fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS) || (cls.protocol != CP_QUAKEWORLD); - if (!cl.spectator) //just in case + if (!cl.spectator && (pv->cam_state != CAM_FREECAM || pv->cam_spec_track != -1)) //just in case { pv->cam_state = CAM_FREECAM; pv->cam_spec_track = -1; + pv->viewentity = (cls.demoplayback)?0:(pv->playernum+1); } #ifdef Q2CLIENT diff --git a/engine/client/image.c b/engine/client/image.c index 51f4be30c..289c03252 100644 --- a/engine/client/image.c +++ b/engine/client/image.c @@ -2279,6 +2279,7 @@ static void Image_LoadTexture_Failed(void *ctx, void *data, size_t a, size_t b) } static void Image_LoadTextureMips(void *ctx, void *data, size_t a, size_t b) { + int i; texid_t tex = ctx; struct pendingtextureinfo *mips = data; @@ -2288,6 +2289,12 @@ static void Image_LoadTextureMips(void *ctx, void *data, size_t a, size_t b) tex->status = TEX_LOADED; else tex->status = TEX_FAILED; + + for (i = 0; i < mips->mipcount; i++) + if (mips->mip[i].needfree) + BZ_Free(mips->mip[i].data); + if (mips->extrafree) + BZ_Free(mips->extrafree); BZ_Free(mips); if (!strncmp(tex->ident, "gfx/", 4)) diff --git a/engine/client/keys.c b/engine/client/keys.c index 31432e6f2..2585bde28 100644 --- a/engine/client/keys.c +++ b/engine/client/keys.c @@ -601,6 +601,15 @@ void Key_ConsoleInsert(char *instext) } key_linepos += len; } +void Key_ConsoleReplace(char *instext) +{ + if (!*instext) + return; + + key_linepos = 1; + key_lines[edit_line][key_linepos] = 0; + Key_ConsoleInsert(instext); +} void Key_DefaultLinkClicked(console_t *con, char *text, char *info) { @@ -792,6 +801,12 @@ void Key_DefaultLinkClicked(console_t *con, char *text, char *info) Cbuf_AddText(va("\nmap %s\n", c), RESTRICT_LOCAL); return; } + c = Info_ValueForKey(info, "type"); + if (*c) + { + Key_ConsoleReplace(c); + return; + } c = Info_ValueForKey(info, "cmd"); if (*c && !strchr(c, ';') && !strchr(c, '\n')) { @@ -1716,6 +1731,18 @@ void Key_Unbindall_f (void) Key_SetBinding (i, ~0, NULL, Cmd_ExecLevel); } +void Key_AliasEdit_f (void) +{ + char *alias = Cmd_AliasExist(Cmd_Argv(1), RESTRICT_LOCAL); + char quotedalias[2048]; + if (alias) + { + COM_QuotedString(alias, quotedalias, sizeof(quotedalias), false); + Key_ConsoleReplace(va("alias %s %s", Cmd_Argv(1), quotedalias)); + } + else + Con_Printf("Not an alias\n"); +} /* =================== @@ -1747,7 +1774,19 @@ void Key_Bind_f (void) if (modifier == ~0) //modifier unspecified. default to no modifier modifier = 0; if (keybindings[b][modifier]) - Con_Printf ("\"%s\" = \"%s\"\n", Cmd_Argv(1), keybindings[b][modifier] ); + { + char *alias = Cmd_AliasExist(keybindings[b][modifier], RESTRICT_LOCAL); + char quotedbind[2048]; + char quotedalias[2048]; + COM_QuotedString(keybindings[b][modifier], quotedbind, sizeof(quotedbind), false); + if (alias) + { + COM_QuotedString(alias, quotedalias, sizeof(quotedalias), false); + Con_Printf ("^[\"%s\"\\type\\bind %s %s^] = ^[\"%s\"\\type\\alias %s %s^]\n", Cmd_Argv(1), Cmd_Argv(1), quotedbind, keybindings[b][modifier], keybindings[b][modifier], quotedalias); + } + else + Con_Printf ("^[\"%s\"\\type\\bind %s %s^] = \"%s\"\n", Cmd_Argv(1), keybindings[b][modifier], Cmd_Argv(1), keybindings[b][modifier] ); + } else Con_Printf ("\"%s\" is not bound\n", Cmd_Argv(1) ); return; @@ -1976,6 +2015,7 @@ void Key_Init (void) Cmd_AddCommand ("bindlevel",Key_BindLevel_f); Cmd_AddCommand ("unbind",Key_Unbind_f); Cmd_AddCommand ("unbindall",Key_Unbindall_f); + Cmd_AddCommand ("aliasedit",Key_AliasEdit_f); Cvar_Register (&con_selectioncolour, "Console variables"); Cvar_Register (&con_echochat, "Console variables"); diff --git a/engine/client/view.c b/engine/client/view.c index 9e752af3e..1c2689059 100644 --- a/engine/client/view.c +++ b/engine/client/view.c @@ -1537,6 +1537,7 @@ static void SCR_DrawAutoID(vec3_t org, player_info_t *pl, qboolean isteam) float barwidth; qboolean haveinfo; unsigned int textflags; + int h; static vec4_t healthcolours[] = { @@ -1614,6 +1615,8 @@ static void SCR_DrawAutoID(vec3_t org, player_info_t *pl, qboolean isteam) if (!haveinfo) return; //we don't trust the info that we have, so no ids. + h = 0; + //display health bar if (scr_autoid_health.ival) { @@ -1627,6 +1630,7 @@ static void SCR_DrawAutoID(vec3_t org, player_info_t *pl, qboolean isteam) health = 100; } barwidth = 32; + h += 8; y -= 8; R2D_ImageColours(healthcolours[r][0], healthcolours[r][1], healthcolours[r][2], healthcolours[r][3]*alpha); R2D_FillBlock(x - barwidth*0.5 + barwidth * health/100.0, y, barwidth * (100-health)/100.0, 8); @@ -1647,6 +1651,7 @@ static void SCR_DrawAutoID(vec3_t org, player_info_t *pl, qboolean isteam) else r = -1; if (r >= 0) { + h += 8; y -= 8; armour = bound(0, armour, health); barwidth = 32; @@ -1660,10 +1665,9 @@ static void SCR_DrawAutoID(vec3_t org, player_info_t *pl, qboolean isteam) if (scr_autoid_weapon.ival) { - if (scr_autoid_armour.ival && scr_autoid_health.ival) - y += 4; - else if (!scr_autoid_armour.ival && !scr_autoid_health.ival) - y -= 8; + if (h < 8) + h = 8; + y += (h-8)/2; for (r = 7; r>=0; r--) if (items & (1<= 0) { len = COM_ParseFunString(textflags, wbitnames[r]->string, buffer, sizeof(buffer), false) - buffer; - Draw_ExpandedString(x + barwidth*0.5 + 4, y, buffer); + if (textflags & CON_HALFALPHA) + { + for (r = 0; r < len; r++) + if (!(buffer[r] & CON_RICHFORECOLOUR)) + buffer[r] |= CON_HALFALPHA; + } + if (len && (buffer[0] & CON_CHARMASK) == '{' && (buffer[len-1] & CON_CHARMASK) == '}') + { //these are often surrounded by {} to make them white in chat messages, and recoloured. + buffer[len-1] = 0; + Draw_ExpandedString(x + barwidth*0.5 + 4, y, buffer+1); + } + else + Draw_ExpandedString(x + barwidth*0.5 + 4, y, buffer); } } } diff --git a/engine/common/cmd.c b/engine/common/cmd.c index 055343df9..51dc083e8 100644 --- a/engine/common/cmd.c +++ b/engine/common/cmd.c @@ -2165,6 +2165,8 @@ void Cmd_ExecuteString (char *text, int level) Cmd_ExpandStringArguments (a->value, dest, sizeof(dest)); Cbuf_InsertText (dest, execlevel, false); + + Con_DPrintf("Execing alias %s:\n%s\n", a->name, a->value); return; } } @@ -2806,7 +2808,7 @@ void Cmd_set_f(void) if (Cmd_Argc()<3) { - Con_TPrintf("set \n"); + Con_TPrintf("%s %s \n", Cmd_Argv(0), *Cmd_Argv(1)?Cmd_Argv(1):""); return; } diff --git a/engine/common/pr_bgcmd.c b/engine/common/pr_bgcmd.c index 406983f07..21fe6fece 100644 --- a/engine/common/pr_bgcmd.c +++ b/engine/common/pr_bgcmd.c @@ -3962,7 +3962,7 @@ static void PR_uri_get_callback(struct dl_download *dl) int selfnum = dl->user_num; func_t func; - if (!prinst) + if (!prinst || dl->user_sequence != svs.spawncount) return; func = PR_FindFunction(prinst, "URI_Get_Callback", PR_ANY); @@ -4048,6 +4048,7 @@ void QCBUILTIN PF_uri_get (pubprogfuncs_t *prinst, struct globalvars_s *pr_glob dl->user_ctx = w; dl->user_float = id; dl->user_num = *w->g.self; + dl->user_sequence = svs.spawncount; dl->isquery = true; G_FLOAT(OFS_RETURN) = 1; } diff --git a/engine/common/pr_common.h b/engine/common/pr_common.h index 1a5030366..9dd743748 100644 --- a/engine/common/pr_common.h +++ b/engine/common/pr_common.h @@ -483,11 +483,11 @@ void QCBUILTIN PF_setspawnparms (pubprogfuncs_t *prinst, struct globalvars_s *pr void QCBUILTIN PF_precache_vwep_model(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); int PF_ForceInfoKey_Internal(unsigned int entnum, const char *key, const char *value); int PF_checkclient_Internal (pubprogfuncs_t *prinst); -void PF_precache_sound_Internal (pubprogfuncs_t *prinst, const char *s); +int PF_precache_sound_Internal (pubprogfuncs_t *prinst, const char *s); int PF_precache_model_Internal (pubprogfuncs_t *prinst, const char *s, qboolean queryonly); void PF_setmodel_Internal (pubprogfuncs_t *prinst, edict_t *e, const char *m); char *PF_infokey_Internal (int entnum, const char *value); -void PF_stuffcmd_Internal(int entnum, const char *str); +void PF_stuffcmd_Internal(int entnum, const char *str, unsigned int flags); void PF_centerprint_Internal (int entnum, qboolean plaque, const char *s); void PF_WriteString_Internal (int target, const char *str); pbool QDECL ED_CanFree (edict_t *ed); diff --git a/engine/common/qvm.c b/engine/common/qvm.c index c2756dcc9..167c93b68 100644 --- a/engine/common/qvm.c +++ b/engine/common/qvm.c @@ -99,12 +99,29 @@ dllhandle_t *QVM_LoadDLL(const char *name, qboolean binroot, void **vmMain, sys_ hVM = Sys_LoadLibrary(fname, funcs); if (!hVM && FS_NativePath(dllname_anycpu, FS_BINARYPATH, fname, sizeof(fname))) hVM = Sys_LoadLibrary(fname, funcs); + + // run through the search paths + iterator = NULL; + while (!hVM && COM_IteratePaths(&iterator, NULL, 0, gpath, sizeof(gpath))) + { + if (!hVM && FS_NativePath(va("%s_%s_"ARCH_CPU_POSTFIX ARCH_DL_POSTFIX, name, gpath), FS_BINARYPATH, fname, sizeof(fname))) + { + Con_DPrintf("Loading native: %s\n", fname); + hVM = Sys_LoadLibrary(fname, funcs); + } + + if (!hVM && FS_NativePath(va("%s_%s"ARCH_DL_POSTFIX, name, gpath), FS_BINARYPATH, fname, sizeof(fname))) + { + Con_DPrintf("Loading native: %s\n", fname); + hVM = Sys_LoadLibrary(fname, funcs); + } + } } else { // run through the search paths iterator = NULL; - while (!hVM && COM_IteratePaths(&iterator, gpath, sizeof(gpath), NULL, false)) + while (!hVM && COM_IteratePaths(&iterator, gpath, sizeof(gpath), NULL, 0)) { if (!hVM) { diff --git a/engine/d3d/d3d11_backend.c b/engine/d3d/d3d11_backend.c index c422211b9..fb1eee76d 100644 --- a/engine/d3d/d3d11_backend.c +++ b/engine/d3d/d3d11_backend.c @@ -1893,12 +1893,14 @@ static void BE_ApplyUniforms(program_t *prog, int permu) ID3D11DeviceContext_VSSetShader(d3ddevctx, prog->permu[permu].handle.hlsl.vert, NULL, 0); ID3D11DeviceContext_HSSetShader(d3ddevctx, prog->permu[permu].handle.hlsl.hull, NULL, 0); ID3D11DeviceContext_DSSetShader(d3ddevctx, prog->permu[permu].handle.hlsl.domain, NULL, 0); + ID3D11DeviceContext_GSSetShader(d3ddevctx, prog->permu[permu].handle.hlsl.geom, NULL, 0); ID3D11DeviceContext_PSSetShader(d3ddevctx, prog->permu[permu].handle.hlsl.frag, NULL, 0); ID3D11DeviceContext_IASetPrimitiveTopology(d3ddevctx, prog->permu[permu].handle.hlsl.topology); ID3D11DeviceContext_VSSetConstantBuffers(d3ddevctx, 0, 3, cbuf); ID3D11DeviceContext_HSSetConstantBuffers(d3ddevctx, 0, 3, cbuf); ID3D11DeviceContext_DSSetConstantBuffers(d3ddevctx, 0, 3, cbuf); + ID3D11DeviceContext_GSSetConstantBuffers(d3ddevctx, 0, 3, cbuf); ID3D11DeviceContext_PSSetConstantBuffers(d3ddevctx, 0, 3, cbuf); } diff --git a/engine/d3d/d3d11_image.c b/engine/d3d/d3d11_image.c index 04f8a3287..064b840d5 100644 --- a/engine/d3d/d3d11_image.c +++ b/engine/d3d/d3d11_image.c @@ -246,14 +246,6 @@ qboolean D3D11_LoadTextureMips(image_t *tex, struct pendingtextureinfo *mips) D3D11_DestroyTexture(tex); hr = ID3D11Device_CreateTexture2D(pD3DDev11, &tdesc, (mips->mip[0].data?subresdesc:NULL), (ID3D11Texture2D**)&tex->ptr); - for (i = 0; i < mips->mipcount; i++) - { - if (mips->mip[i].needfree) - BZ_Free(mips->mip[i].data); - } - if (mips->extrafree) - BZ_Free(mips->extrafree); - return !FAILED(hr); } void D3D11_UploadLightmap(lightmapinfo_t *lm) diff --git a/engine/d3d/d3d11_shader.c b/engine/d3d/d3d11_shader.c index 34c8a61bd..ecffd21fe 100644 --- a/engine/d3d/d3d11_shader.c +++ b/engine/d3d/d3d11_shader.c @@ -236,6 +236,7 @@ static qboolean D3D11Shader_CreateShaders(program_t *prog, const char *name, int void *vblob, size_t vsize, void *hblob, size_t hsize, void *dblob, size_t dsize, + void *gblob, size_t gsize, void *fblob, size_t fsize) { qboolean success = true; @@ -255,6 +256,9 @@ static qboolean D3D11Shader_CreateShaders(program_t *prog, const char *name, int else prog->permu[permu].handle.hlsl.topology = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST; + if (gblob && FAILED(ID3D11Device_CreateGeometryShader(pD3DDev11, gblob, gsize, NULL, (ID3D11GeometryShader**)&prog->permu[permu].handle.hlsl.geom))) + success = false; + if (FAILED(ID3D11Device_CreatePixelShader(pD3DDev11, fblob, fsize, NULL, (ID3D11PixelShader**)&prog->permu[permu].handle.hlsl.frag))) success = false; @@ -358,8 +362,8 @@ static qboolean D3D11Shader_CreateShaders(program_t *prog, const char *name, int static qboolean D3D11Shader_LoadBlob(program_t *prog, const char *name, unsigned int permu, vfsfile_t *blobfile) { qboolean success; - char *vblob, *hblob, *dblob, *fblob; - unsigned int vsz, hsz, dsz, fsz; + char *vblob, *hblob, *dblob, *gblob, *fblob; + unsigned int vsz, hsz, dsz, gsz, fsz; VFS_READ(blobfile, &vsz, sizeof(vsz)); vblob = Z_Malloc(vsz); @@ -383,20 +387,30 @@ static qboolean D3D11Shader_LoadBlob(program_t *prog, const char *name, unsigned else dblob = NULL; + VFS_READ(blobfile, &gsz, sizeof(gsz)); + if (dsz != ~0u) + { + gblob = Z_Malloc(gsz); + VFS_READ(blobfile, gblob, gsz); + } + else + gblob = NULL; + VFS_READ(blobfile, &fsz, sizeof(fsz)); fblob = Z_Malloc(fsz); VFS_READ(blobfile, fblob, fsz); - success = D3D11Shader_CreateShaders(prog, name, permu, vblob, vsz, hblob, hsz, dblob, dsz, fblob, fsz); + success = D3D11Shader_CreateShaders(prog, name, permu, vblob, vsz, hblob, hsz, dblob, dsz, gblob, gsz, fblob, fsz); Z_Free(vblob); Z_Free(hblob); Z_Free(dblob); + Z_Free(gblob); Z_Free(fblob); return success; } -qboolean D3D11Shader_CreateProgram (program_t *prog, const char *name, unsigned int permu, int ver, const char **precompilerconstants, const char *vert, const char *hull, const char *domain, const char *frag, qboolean silenterrors, vfsfile_t *blobfile) +qboolean D3D11Shader_CreateProgram (program_t *prog, const char *name, unsigned int permu, int ver, const char **precompilerconstants, const char *vert, const char *hull, const char *domain, const char *geom, const char *frag, qboolean silenterrors, vfsfile_t *blobfile) { static const char *defaultsamplers[] = { @@ -427,8 +441,9 @@ qboolean D3D11Shader_CreateProgram (program_t *prog, const char *name, unsigned char *hsformat = NULL; char *dsformat = NULL; char *fsformat; + char *gsformat = NULL; D3D_SHADER_MACRO defines[64]; - ID3DBlob *vcode = NULL, *hcode = NULL, *dcode = NULL, *fcode = NULL, *errors = NULL; + ID3DBlob *vcode = NULL, *hcode = NULL, *dcode = NULL, *gcode = NULL, *fcode = NULL, *errors = NULL; qboolean success = false; ID3D11ShaderReflection *freflect; int i; @@ -438,16 +453,19 @@ qboolean D3D11Shader_CreateProgram (program_t *prog, const char *name, unsigned vsformat = "vs_5_0"; hsformat = "hs_5_0"; dsformat = "ds_5_0"; + gsformat = "gs_5_0"; fsformat = "ps_5_0"; } else if (d3dfeaturelevel >= D3D_FEATURE_LEVEL_10_1) { vsformat = "vs_4_1"; + gsformat = "gs_4_1"; fsformat = "ps_4_1"; } else if (d3dfeaturelevel >= D3D_FEATURE_LEVEL_10_0) { vsformat = "vs_4_0"; + gsformat = "gs_4_0"; fsformat = "ps_4_0"; } else if (d3dfeaturelevel >= D3D_FEATURE_LEVEL_9_3) @@ -463,6 +481,9 @@ qboolean D3D11Shader_CreateProgram (program_t *prog, const char *name, unsigned prog->permu[permu].handle.hlsl.vert = NULL; prog->permu[permu].handle.hlsl.frag = NULL; + prog->permu[permu].handle.hlsl.hull = NULL; + prog->permu[permu].handle.hlsl.domain = NULL; + prog->permu[permu].handle.hlsl.geom = NULL; prog->permu[permu].handle.hlsl.layout = NULL; if (pD3DCompile) @@ -547,6 +568,24 @@ qboolean D3D11Shader_CreateProgram (program_t *prog, const char *name, unsigned } } + if (geom) + { + if (!dsformat) + success = false; + else + { + defines[0].Name = "GEOMETRY_SHADER"; + if (FAILED(pD3DCompile(domain, strlen(domain), name, defines, &myd3dinclude, "main", gsformat, 0, 0, &gcode, &errors))) + success = false; + if (errors && !silenterrors) + { + char *messages = ID3DBlob_GetBufferPointer(errors); + Con_Printf("geometry shader %s:\n%s", name, messages); + ID3DBlob_Release(errors); + } + } + } + defines[0].Name = "FRAGMENT_SHADER"; if (FAILED(pD3DCompile(frag, strlen(frag), name, defines, &myd3dinclude, "main", fsformat, 0, 0, &fcode, &errors))) success = false; @@ -568,6 +607,7 @@ qboolean D3D11Shader_CreateProgram (program_t *prog, const char *name, unsigned ID3DBlob_GetBufferPointer(vcode), ID3DBlob_GetBufferSize(vcode), hcode?ID3DBlob_GetBufferPointer(hcode):NULL, hcode?ID3DBlob_GetBufferSize(hcode):0, dcode?ID3DBlob_GetBufferPointer(dcode):NULL, dcode?ID3DBlob_GetBufferSize(dcode):0, + gcode?ID3DBlob_GetBufferPointer(gcode):NULL, gcode?ID3DBlob_GetBufferSize(gcode):0, ID3DBlob_GetBufferPointer(fcode), ID3DBlob_GetBufferSize(fcode)); if (success && blobfile) @@ -601,6 +641,18 @@ qboolean D3D11Shader_CreateProgram (program_t *prog, const char *name, unsigned VFS_WRITE(blobfile, ID3DBlob_GetBufferPointer(dcode), sz); } + if (!gcode) + { + sz = ~0u; + VFS_WRITE(blobfile, &sz, sizeof(sz)); + } + else + { + sz = ID3DBlob_GetBufferSize(gcode); + VFS_WRITE(blobfile, &sz, sizeof(sz)); + VFS_WRITE(blobfile, ID3DBlob_GetBufferPointer(gcode), sz); + } + sz = ID3DBlob_GetBufferSize(fcode); VFS_WRITE(blobfile, &sz, sizeof(sz)); VFS_WRITE(blobfile, ID3DBlob_GetBufferPointer(fcode), sz); @@ -641,6 +693,8 @@ qboolean D3D11Shader_CreateProgram (program_t *prog, const char *name, unsigned ID3DBlob_Release(hcode); if (dcode) ID3DBlob_Release(dcode); + if (gcode) + ID3DBlob_Release(gcode); if (fcode) ID3DBlob_Release(fcode); } diff --git a/engine/d3d/d3d_image.c b/engine/d3d/d3d_image.c index 836b5abbb..a1fb56f62 100644 --- a/engine/d3d/d3d_image.c +++ b/engine/d3d/d3d_image.c @@ -84,6 +84,8 @@ qboolean D3D9_LoadTextureMips(image_t *tex, struct pendingtextureinfo *mips) return false; } + if (!pD3DDev9) + return false; //can happen on errors if (FAILED(IDirect3DDevice9_CreateTexture(pD3DDev9, mips->mip[0].width, mips->mip[0].height, mips->mipcount, 0, fmt, D3DPOOL_MANAGED, &dt, NULL))) return false; diff --git a/engine/d3d/d3d_shader.c b/engine/d3d/d3d_shader.c index 2bd29af7b..b98a40c1e 100644 --- a/engine/d3d/d3d_shader.c +++ b/engine/d3d/d3d_shader.c @@ -141,12 +141,18 @@ static dllhandle_t *shaderlib; (This)->lpVtbl -> Release(This) #endif -static qboolean D3D9Shader_CreateProgram (program_t *prog, const char *sname, unsigned int permu, int ver, const char **precompilerconstants, const char *vert, const char *tcs, const char *tes, const char *frag, qboolean silent, vfsfile_t *blobfile) +static qboolean D3D9Shader_CreateProgram (program_t *prog, const char *sname, unsigned int permu, int ver, const char **precompilerconstants, const char *vert, const char *tcs, const char *tes, const char *geom, const char *frag, qboolean silent, vfsfile_t *blobfile) { D3DXMACRO defines[64]; LPD3DXBUFFER code = NULL, errors = NULL; qboolean success = false; + if (geom || tcs || tes) + { + Con_Printf("geometry and tessellation shaders are not availale in d3d9 (%s)\n", sname); + return false; + } + prog->permu[permu].handle.hlsl.vert = NULL; prog->permu[permu].handle.hlsl.frag = NULL; diff --git a/engine/gl/gl_backend.c b/engine/gl/gl_backend.c index 788e7afcd..9dc9307d2 100644 --- a/engine/gl/gl_backend.c +++ b/engine/gl/gl_backend.c @@ -3508,7 +3508,7 @@ void GLBE_SelectMode(backendmode_t mode) if (!shaderstate.allblackshader.glsl.handle) { const char *defs[] = {NULL}; - shaderstate.allblackshader = GLSlang_CreateProgram("allblackprogram", gl_config_gles?100:110, defs, "#include \"sys/skeletal.h\"\nvoid main(){gl_Position = skeletaltransform();}", NULL, NULL, "void main(){gl_FragColor=vec4(0.0,0.0,0.0,1.0);}", false, NULL); + shaderstate.allblackshader = GLSlang_CreateProgram("allblackprogram", gl_config_gles?100:110, defs, "#include \"sys/skeletal.h\"\nvoid main(){gl_Position = skeletaltransform();}", NULL, NULL, NULL, "void main(){gl_FragColor=vec4(0.0,0.0,0.0,1.0);}", false, NULL); shaderstate.allblack_mvp = qglGetUniformLocationARB(shaderstate.allblackshader.glsl.handle, "m_modelviewprojection"); } /*BEM_DEPTHONLY does support mesh writing, but its not the only way its used... FIXME!*/ @@ -3533,7 +3533,7 @@ void GLBE_SelectMode(backendmode_t mode) if (gl_config_nofixedfunc && !shaderstate.allblackshader.glsl.handle) { const char *defs[] = {NULL}; - shaderstate.allblackshader = GLSlang_CreateProgram("allblackprogram", gl_config_gles?100:110, defs, "#include \"sys/skeletal.h\"\nvoid main(){gl_Position = skeletaltransform();}", NULL, NULL, "void main(){gl_FragColor=vec4(0.0,0.0,0.0,1.0);}", false, NULL); + shaderstate.allblackshader = GLSlang_CreateProgram("allblackprogram", gl_config_gles?100:110, defs, "#include \"sys/skeletal.h\"\nvoid main(){gl_Position = skeletaltransform();}", NULL, NULL, NULL, "void main(){gl_FragColor=vec4(0.0,0.0,0.0,1.0);}", false, NULL); shaderstate.allblack_mvp = qglGetUniformLocationARB(shaderstate.allblackshader.glsl.handle, "m_modelviewprojection"); } @@ -4058,7 +4058,7 @@ static void DrawMeshes(void) if (!shaderstate.allblackshader.glsl.handle) { const char *defs[] = {NULL}; - shaderstate.allblackshader = GLSlang_CreateProgram("allblackprogram", gl_config_gles?100:110, defs, "#include \"sys/skeletal.h\"\nvoid main(){gl_Position = skeletaltransform();}", NULL, NULL, "void main(){gl_FragColor=vec4(0.0,0.0,0.0,1.0);}", false, NULL); + shaderstate.allblackshader = GLSlang_CreateProgram("allblackprogram", gl_config_gles?100:110, defs, "#include \"sys/skeletal.h\"\nvoid main(){gl_Position = skeletaltransform();}", NULL, NULL, NULL, "void main(){gl_FragColor=vec4(0.0,0.0,0.0,1.0);}", false, NULL); shaderstate.allblack_mvp = qglGetUniformLocationARB(shaderstate.allblackshader.glsl.handle, "m_modelviewprojection"); } diff --git a/engine/gl/gl_draw.c b/engine/gl/gl_draw.c index 3d3e5e912..51dae4455 100644 --- a/engine/gl/gl_draw.c +++ b/engine/gl/gl_draw.c @@ -376,9 +376,6 @@ qboolean GL_LoadTextureMips(texid_t tex, struct pendingtextureinfo *mips) qglTexImage3D(targface, i, GL_RGB, size, size, size, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, mips->mip[i].data); break; } - - if (mips->mip[i].needfree) - Z_Free(mips->mip[i].data); } } else @@ -466,13 +463,9 @@ qboolean GL_LoadTextureMips(texid_t tex, struct pendingtextureinfo *mips) qglCompressedTexImage2DARB(targface, j, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, mips->mip[i].width, mips->mip[i].height, 0, mips->mip[i].datasize, mips->mip[i].data); break; } - if (mips->mip[i].needfree) - Z_Free(mips->mip[i].data); } } - if (mips->extrafree) - Z_Free(mips->extrafree); return true; } diff --git a/engine/gl/gl_shader.c b/engine/gl/gl_shader.c index 9d1153ee1..7012eb3c0 100644 --- a/engine/gl/gl_shader.c +++ b/engine/gl/gl_shader.c @@ -983,6 +983,7 @@ static qboolean Shader_LoadPermutations(char *name, program_t *prog, char *scrip unsigned int permuoffsets[PERMUTATIONS], initoffset=0; unsigned int blobheaderoffset=0; qboolean blobadded; + qboolean geom = false; qboolean tess = false; char *cvarnames[64]; @@ -1014,6 +1015,11 @@ static qboolean Shader_LoadPermutations(char *name, program_t *prog, char *scrip prog->nofixedcompat = false; script += 7; } + else if (!strncmp(script, "!!geom", 6)) + { + geom = true; + script += 6; + } else if (!strncmp(script, "!!tess", 6)) { tess = true; @@ -1334,7 +1340,7 @@ static qboolean Shader_LoadPermutations(char *name, program_t *prog, char *scrip initoffset = VFS_GETLEN(blobfile); VFS_SEEK(blobfile, initoffset); } - if (!sh_config.pCreateProgram(prog, name, p, ver, permutationdefines, script, tess?script:NULL, tess?script:NULL, script, (p & PERMUTATION_SKELETAL)?true:onefailed, sh_config.pValidateProgram?NULL:blobfile)) + if (!sh_config.pCreateProgram(prog, name, p, ver, permutationdefines, script, tess?script:NULL, tess?script:NULL, geom?script:NULL, script, (p & PERMUTATION_SKELETAL)?true:onefailed, sh_config.pValidateProgram?NULL:blobfile)) { if (!(p & PERMUTATION_SKELETAL)) onefailed = true; //don't flag it if skeletal failed. diff --git a/engine/gl/gl_vidcommon.c b/engine/gl/gl_vidcommon.c index c0e1304ac..b39c6ccbc 100644 --- a/engine/gl/gl_vidcommon.c +++ b/engine/gl/gl_vidcommon.c @@ -1005,6 +1005,11 @@ void GL_CheckExtensions (void *(*getglfunction) (char *name)) if (GL_CheckExtension("GL_ARB_seamless_cube_map")) qglEnable(0x884F); //TEXTURE_CUBE_MAP_SEAMLESS 0x884F + if (!gl_config.gles && gl_config.glversion >= 3.2) + gl_config.geometryshaders = true; + else + gl_config.geometryshaders = false; + #ifdef GL_STATIC gl_config.ext_framebuffer_objects = true; //exists as core in gles2 #else @@ -1773,6 +1778,11 @@ static GLhandleARB GLSlang_CreateShader (const char *name, int ver, const char * strings++; } break; + case GL_GEOMETRY_SHADER_ARB: + prstrings[strings] = "#define GEOMETRY_SHADER\n"; + length[strings] = strlen(prstrings[strings]); + strings++; + break; case GL_TESS_CONTROL_SHADER_ARB: prstrings[strings] = "#define TESS_CONTROL_SHADER\n"; length[strings] = strlen(prstrings[strings]); @@ -1976,12 +1986,13 @@ static GLhandleARB GLSlang_FinishShader(GLhandleARB shader, const char *name, GL return shader; } -GLhandleARB GLSlang_CreateProgramObject (const char *name, GLhandleARB vert, GLhandleARB cont, GLhandleARB eval, GLhandleARB frag, qboolean silent) +GLhandleARB GLSlang_CreateProgramObject (const char *name, GLhandleARB vert, GLhandleARB cont, GLhandleARB eval, GLhandleARB geom, GLhandleARB frag, qboolean silent) { GLhandleARB program; program = qglCreateProgramObjectARB(); if (vert) qglAttachObjectARB(program, vert); + if (geom) qglAttachObjectARB(program, geom); if (cont) qglAttachObjectARB(program, cont); if (eval) qglAttachObjectARB(program, eval); if (frag) qglAttachObjectARB(program, frag); @@ -2058,10 +2069,11 @@ qboolean GLSlang_ValidateProgram(union programhandle_u *h, const char *name, qbo return true; } -union programhandle_u GLSlang_CreateProgram(const char *name, int ver, const char **precompilerconstants, const char *vert, const char *cont, const char *eval, const char *frag, qboolean silent, vfsfile_t *blobfile) +union programhandle_u GLSlang_CreateProgram(const char *name, int ver, const char **precompilerconstants, const char *vert, const char *cont, const char *eval, const char *geom, const char *frag, qboolean silent, vfsfile_t *blobfile) { union programhandle_u ret; GLhandleARB vs; + GLhandleARB gs; GLhandleARB fs; GLhandleARB cs; GLhandleARB es; @@ -2073,7 +2085,12 @@ union programhandle_u GLSlang_CreateProgram(const char *name, int ver, const cha return ret; if ((cont || eval) && !qglPatchParameteriARB) { - Con_Printf("GLSlang_CreateProgram: %s requires tesselation support, but your gl drivers do not appear to support this\n", name); + Con_Printf("GLSlang_CreateProgram: %s requires tesselation support, but your gl drivers do not appear to support this (gl4.0 feature)\n", name); + return ret; + } + if (geom && !gl_config.geometryshaders) + { + Con_Printf("GLSlang_CreateProgram: %s requires geometry shader support, but your gl drivers do not appear to support this (gl3.2 feature)\n", name); return ret; } @@ -2081,11 +2098,13 @@ union programhandle_u GLSlang_CreateProgram(const char *name, int ver, const cha precompilerconstants = &nullconstants; fs = GLSlang_CreateShader(name, ver, precompilerconstants, frag, GL_FRAGMENT_SHADER_ARB, silent); + gs = GLSlang_CreateShader(name, ver, precompilerconstants, geom, GL_GEOMETRY_SHADER_ARB, silent); vs = GLSlang_CreateShader(name, ver, precompilerconstants, vert, GL_VERTEX_SHADER_ARB, silent); cs = GLSlang_CreateShader(name, ver, precompilerconstants, cont, GL_TESS_CONTROL_SHADER_ARB, silent); es = GLSlang_CreateShader(name, ver, precompilerconstants, eval, GL_TESS_EVALUATION_SHADER_ARB, silent); fs = GLSlang_FinishShader(fs, name, GL_FRAGMENT_SHADER_ARB, silent); + gs = GLSlang_FinishShader(gs, name, GL_GEOMETRY_SHADER_ARB, silent); vs = GLSlang_FinishShader(vs, name, GL_VERTEX_SHADER_ARB, silent); cs = GLSlang_FinishShader(cs, name, GL_TESS_CONTROL_SHADER_ARB, silent); es = GLSlang_FinishShader(es, name, GL_TESS_EVALUATION_SHADER_ARB, silent); @@ -2093,9 +2112,10 @@ union programhandle_u GLSlang_CreateProgram(const char *name, int ver, const cha if (!vs || !fs) ret.glsl.handle = 0; else - ret.glsl.handle = GLSlang_CreateProgramObject(name, vs, cs, es, fs, silent); + ret.glsl.handle = GLSlang_CreateProgramObject(name, vs, cs, es, gs, fs, silent); //delete ignores 0s. if (vs) qglDeleteShaderObject_(vs); + if (gs) qglDeleteShaderObject_(gs); if (fs) qglDeleteShaderObject_(fs); if (cs) qglDeleteShaderObject_(cs); if (es) qglDeleteShaderObject_(es); @@ -2132,7 +2152,7 @@ qboolean GLSlang_ValidateProgramPermu(program_t *prog, const char *name, unsigne { return GLSlang_ValidateProgram(&prog->permu[permu].handle, name, noerrors, blobfile); } -qboolean GLSlang_CreateProgramPermu(program_t *prog, const char *name, unsigned int permu, int ver, const char **precompilerconstants, const char *vert, const char *tcs, const char *tes, const char *frag, qboolean noerrors, vfsfile_t *blobfile) +qboolean GLSlang_CreateProgramPermu(program_t *prog, const char *name, unsigned int permu, int ver, const char **precompilerconstants, const char *vert, const char *tcs, const char *tes, const char *geom, const char *frag, qboolean noerrors, vfsfile_t *blobfile) { #ifdef FTE_TARGET_WEB //emscripten's uniform code results in excessive stalls that hinder usability. @@ -2150,7 +2170,7 @@ qboolean GLSlang_CreateProgramPermu(program_t *prog, const char *name, unsigned ver = 120; #endif } - prog->permu[permu].handle = GLSlang_CreateProgram(name, ver, precompilerconstants, vert, tcs, tes, frag, noerrors, blobfile); + prog->permu[permu].handle = GLSlang_CreateProgram(name, ver, precompilerconstants, vert, tcs, tes, geom, frag, noerrors, blobfile); if (prog->permu[permu].handle.glsl.handle) return true; return false; diff --git a/engine/gl/glquake.h b/engine/gl/glquake.h index dac0a934a..e73a9d24c 100644 --- a/engine/gl/glquake.h +++ b/engine/gl/glquake.h @@ -235,6 +235,8 @@ typedef struct { qboolean arb_texture_compression; + qboolean geometryshaders; + // qboolean arb_fragment_program; qboolean arb_shader_objects; qboolean arb_shadow; @@ -1077,7 +1079,7 @@ extern void (APIENTRY *qglBindVertexArray)(GLuint vaoarray); //glslang helper api -union programhandle_u GLSlang_CreateProgram(const char *name, int ver, const char **precompilerconstants, const char *vert, const char *cont, const char *eval, const char *frag, qboolean silent, vfsfile_t *blobfile); +union programhandle_u GLSlang_CreateProgram(const char *name, int ver, const char **precompilerconstants, const char *vert, const char *cont, const char *eval, const char *geom, const char *frag, qboolean silent, vfsfile_t *blobfile); GLint GLSlang_GetUniformLocation (int prog, char *name); void GL_SelectProgram(int program); #define GLSlang_UseProgram(prog) GL_SelectProgram(prog) diff --git a/engine/gl/glsupp.h b/engine/gl/glsupp.h index d57d04b9b..e98f54bf6 100644 --- a/engine/gl/glsupp.h +++ b/engine/gl/glsupp.h @@ -586,6 +586,10 @@ typedef void (APIENTRYP PFNGLGETSHADERSOURCEARBPROC) (GLhandleARB obj, GLsizei #define GL_OBJECT_ACTIVE_ATTRIBUTE_MAX_LENGTH_ARB 0x8B8A #endif +#ifndef GL_GEOMETRY_SHADER_ARB +#define GL_GEOMETRY_SHADER_ARB 0x8DD9 +#endif + #ifndef GL_PATCH_VERTICES_ARB //GL_ARB_tessellation_shader lacks _ARB postfix. #define GL_PATCHES_ARB 0xE #define GL_PATCH_VERTICES_ARB 0x8E72 diff --git a/engine/gl/shader.h b/engine/gl/shader.h index b09630fc2..c1105f347 100644 --- a/engine/gl/shader.h +++ b/engine/gl/shader.h @@ -443,6 +443,7 @@ union programhandle_u int topology; //D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST void *hull; void *domain; + void *geom; void *layout; #endif } hlsl; @@ -674,7 +675,7 @@ typedef struct void (*pDeleteProg) (program_t *prog, unsigned int permu); qboolean (*pLoadBlob) (program_t *prog, const char *name, unsigned int permu, vfsfile_t *blobfile); - qboolean (*pCreateProgram) (program_t *prog, const char *name, unsigned int permu, int ver, const char **precompilerconstants, const char *vert, const char *tcs, const char *tes, const char *frag, qboolean noerrors, vfsfile_t *blobfile); + qboolean (*pCreateProgram) (program_t *prog, const char *name, unsigned int permu, int ver, const char **precompilerconstants, const char *vert, const char *tcs, const char *tes, const char *geom, const char *frag, qboolean noerrors, vfsfile_t *blobfile); qboolean (*pValidateProgram)(program_t *prog, const char *name, unsigned int permu, qboolean noerrors, vfsfile_t *blobfile); void (*pProgAutoFields) (program_t *prog, char **cvarnames, int *cvartypes); } sh_config_t; diff --git a/engine/http/iweb.h b/engine/http/iweb.h index 52f8a2966..5325c45b3 100644 --- a/engine/http/iweb.h +++ b/engine/http/iweb.h @@ -103,6 +103,8 @@ struct dl_download unsigned int user_num; float user_float; void *user_ctx; + int user_sequence; + qboolean isquery; //will not be displayed in the download/progress bar stuff. #ifndef SERVERONLY diff --git a/engine/server/pr_cmds.c b/engine/server/pr_cmds.c index b2c5d4958..69332a5e7 100644 --- a/engine/server/pr_cmds.c +++ b/engine/server/pr_cmds.c @@ -3375,7 +3375,7 @@ static void QCBUILTIN PF_checkclient (pubprogfuncs_t *prinst, struct globalvars_ //============================================================================ -void PF_stuffcmd_Internal(int entnum, const char *str) +void PF_stuffcmd_Internal(int entnum, const char *str, unsigned int flags) { client_t *cl; static qboolean expectingcolour; @@ -3468,7 +3468,7 @@ stuffcmd (clientent, value) */ static void QCBUILTIN PF_stuffcmd (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - PF_stuffcmd_Internal(G_EDICTNUM(prinst, OFS_PARM0), PR_GetStringOfs(prinst, OFS_PARM1)); + PF_stuffcmd_Internal(G_EDICTNUM(prinst, OFS_PARM0), PR_GetStringOfs(prinst, OFS_PARM1), 0); } //DP_QC_DROPCLIENT @@ -3700,7 +3700,7 @@ void PR_CheckEmptyString (char *s) */ //float(string effectname) particleeffectnum (EXT_CSQC) -static void QCBUILTIN PF_sv_particleeffectnum(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +void QCBUILTIN PF_sv_particleeffectnum(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { const char *s = PR_GetStringOfs(prinst, OFS_PARM0); /* @@ -3762,14 +3762,14 @@ static void QCBUILTIN PF_precache_file (pubprogfuncs_t *prinst, struct globalvar FS_FLocateFile(s, FSLFRT_IFFOUND, NULL); } -void PF_precache_sound_Internal (pubprogfuncs_t *prinst, const char *s) +int PF_precache_sound_Internal (pubprogfuncs_t *prinst, const char *s) { int i; if (s[0] <= ' ') { PR_BIError (prinst, "PF_precache_sound: Bad string"); - return; + return 0; } for (i=1 ; i= mask || VM_POINTER(o) < offset) SV_Error("Call to game trap %i passes invalid pointer\n", (int)fn); //out of bounds. -static qintptr_t syscallhandle (void *offset, quintptr_t mask, qintptr_t fn, const qintptr_t *arg) +#define VALIDATEPOINTER(o,l) if ((qintptr_t)o + l >= mask || VM_POINTER(o) < offset) SV_Error("Call to game trap passes invalid pointer\n"); //out of bounds. +static qintptr_t QVM_GetAPIVersion (void *offset, quintptr_t mask, const qintptr_t *arg) { - switch (fn) + return GAME_API_VERSION; +} + +static qintptr_t QVM_DPrint (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + Con_DPrintf("%s", (char*)VM_POINTER(arg[0])); + return 0; +} + +static qintptr_t QVM_Error (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + SV_Error("Q1QVM: %s", (char*)VM_POINTER(arg[0])); + return 0; +} + +static qintptr_t QVM_GetEntityToken (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + if (VM_OOB(arg[0], arg[1]) || !arg[1]) + return false; + if (q1qvmentstring) { - case G_GETAPIVERSION: - return GAME_API_VERSION; + char *ret = VM_POINTER(arg[0]); + q1qvmentstring = COM_Parse(q1qvmentstring); + Q_strncpyz(ret, com_token, VM_LONG(arg[1])); + return *com_token != 0; + } + else + { + char *ret = VM_POINTER(arg[0]); + *ret = '\0'; + return false; + } +} - case G_DPRINT: - Con_Printf("%s", (char*)VM_POINTER(arg[0])); - break; +static qintptr_t QVM_Spawn_Ent (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + return Q1QVMPF_EntAlloc(svprogfuncs)->entnum; +} - case G_ERROR: - SV_Error("Q1QVM: %s", (char*)VM_POINTER(arg[0])); - break; +static qintptr_t QVM_Remove_Ent (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + if (arg[0] >= sv.world.max_edicts) + return false; + Q1QVMPF_EntRemove(svprogfuncs, q1qvmedicttable[arg[0]]); + return true; +} - case G_GetEntityToken: - { - if (VM_OOB(arg[0], arg[1]) || !arg[1]) - return false; - if (q1qvmentstring) - { - char *ret = VM_POINTER(arg[0]); - q1qvmentstring = COM_Parse(q1qvmentstring); - Q_strncpyz(ret, com_token, VM_LONG(arg[1])); - return *com_token != 0; - } - else - { - char *ret = VM_POINTER(arg[0]); - *ret = '\0'; - return false; - } - } - break; +static qintptr_t QVM_Precache_Sound (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + return PF_precache_sound_Internal(svprogfuncs, VM_POINTER(arg[0])); +} +static qintptr_t QVM_Precache_Model (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + return PF_precache_model_Internal(svprogfuncs, VM_POINTER(arg[0]), false); +} +static qintptr_t QVM_LightStyle (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + vec3_t rgb = {1,1,1}; + PF_applylightstyle(VM_LONG(arg[0]), VM_POINTER(arg[1]), rgb); + return 0; +} +static qintptr_t QVM_SetOrigin (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + edict_t *e = Q1QVMPF_EdictNum(svprogfuncs, VM_LONG(arg[0])); + if (!e || e->isfree) + return false; - case G_SPAWN_ENT: - return Q1QVMPF_EntAlloc(svprogfuncs)->entnum; + e->v->origin[0] = VM_FLOAT(arg[1]); + e->v->origin[1] = VM_FLOAT(arg[2]); + e->v->origin[2] = VM_FLOAT(arg[3]); + World_LinkEdict (&sv.world, (wedict_t*)e, false); + return true; +} +static qintptr_t QVM_SetSize (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + edict_t *e = Q1QVMPF_EdictNum(svprogfuncs, arg[0]); + if (!e || e->isfree) + return false; - case G_REMOVE_ENT: - if (arg[0] >= sv.world.max_edicts) - return false; - Q1QVMPF_EntRemove(svprogfuncs, q1qvmedicttable[arg[0]]); - return true; + e->v->mins[0] = VM_FLOAT(arg[1]); + e->v->mins[1] = VM_FLOAT(arg[2]); + e->v->mins[2] = VM_FLOAT(arg[3]); - case G_PRECACHE_SOUND: - PF_precache_sound_Internal(svprogfuncs, VM_POINTER(arg[0])); - break; + e->v->maxs[0] = VM_FLOAT(arg[4]); + e->v->maxs[1] = VM_FLOAT(arg[5]); + e->v->maxs[2] = VM_FLOAT(arg[6]); - case G_PRECACHE_MODEL: - PF_precache_model_Internal(svprogfuncs, VM_POINTER(arg[0]), false); - break; - - case G_LIGHTSTYLE: - { - vec3_t rgb = {1,1,1}; - PF_applylightstyle(VM_LONG(arg[0]), VM_POINTER(arg[1]), rgb); - } - break; - - case G_SETORIGIN: - { - edict_t *e = Q1QVMPF_EdictNum(svprogfuncs, VM_LONG(arg[0])); - if (!e || e->isfree) - return false; - - e->v->origin[0] = VM_FLOAT(arg[1]); - e->v->origin[1] = VM_FLOAT(arg[2]); - e->v->origin[2] = VM_FLOAT(arg[3]); - World_LinkEdict (&sv.world, (wedict_t*)e, false); - return true; - } - break; - - case G_SETSIZE: - { - edict_t *e = Q1QVMPF_EdictNum(svprogfuncs, arg[0]); - if (!e || e->isfree) - return false; - - e->v->mins[0] = VM_FLOAT(arg[1]); - e->v->mins[1] = VM_FLOAT(arg[2]); - e->v->mins[2] = VM_FLOAT(arg[3]); - - e->v->maxs[0] = VM_FLOAT(arg[4]); - e->v->maxs[1] = VM_FLOAT(arg[5]); - e->v->maxs[2] = VM_FLOAT(arg[6]); - - VectorSubtract (e->v->maxs, e->v->mins, e->v->size); - World_LinkEdict (&sv.world, (wedict_t*)e, false); - return true; - } - case G_SETMODEL: - { - edict_t *e = Q1QVMPF_EdictNum(svprogfuncs, arg[0]); - PF_setmodel_Internal(svprogfuncs, e, VM_POINTER(arg[1])); - } - break; - - case G_BPRINT: - SV_BroadcastPrintf(arg[0], "%s", (char*)VM_POINTER(arg[1])); - break; - - case G_SPRINT: - if ((unsigned)VM_LONG(arg[0]) > sv.allocated_client_slots) - return 0; - SV_ClientPrintf(&svs.clients[VM_LONG(arg[0])-1], VM_LONG(arg[1]), "%s", (char*)VM_POINTER(arg[2])); - break; - - case G_CENTERPRINT: - PF_centerprint_Internal(VM_LONG(arg[0]), false, VM_POINTER(arg[1])); - break; - - case G_AMBIENTSOUND: - { - vec3_t pos; - pos[0] = VM_FLOAT(arg[0]); - pos[1] = VM_FLOAT(arg[1]); - pos[2] = VM_FLOAT(arg[2]); - PF_ambientsound_Internal(pos, VM_POINTER(arg[3]), VM_FLOAT(arg[4]), VM_FLOAT(arg[5])); - } - break; - - case G_SOUND: -// ( int edn, int channel, char *samp, float vol, float att ) - SVQ1_StartSound (NULL, (wedict_t*)Q1QVMPF_EdictNum(svprogfuncs, VM_LONG(arg[0])), VM_LONG(arg[1]), VM_POINTER(arg[2]), VM_FLOAT(arg[3])*255, VM_FLOAT(arg[4]), 0); - break; - - case G_TRACELINE: - WrapQCBuiltin(PF_svtraceline, offset, mask, arg, "vvin"); - break; - - case G_CHECKCLIENT: - return PF_checkclient_Internal(svprogfuncs); - - case G_STUFFCMD: - PF_stuffcmd_Internal(VM_LONG(arg[0]), VM_POINTER(arg[1])); - break; - - case G_LOCALCMD: - Cbuf_AddText (VM_POINTER(arg[0]), RESTRICT_INSECURE); - break; - - case G_CVAR: - { - int i; - cvar_t *c; - char *vname = VM_POINTER(arg[0]); - - //paused state is not a cvar. - if (!strcmp(vname, "sv_paused")) - { - float f; - f = sv.paused; - return VM_LONG(f); - } - - c = Cvar_Get(vname, "", 0, "Gamecode"); - i = VM_LONG(c->value); - return i; - } - - case G_CVAR_SET: - { - cvar_t *var; - var = Cvar_Get(VM_POINTER(arg[0]), VM_POINTER(arg[1]), 0, "Gamecode variables"); - if (!var) - return -1; - Cvar_Set (var, VM_POINTER(arg[1])); - } - break; - - case G_FINDRADIUS: - { - int start = ((char*)VM_POINTER(arg[0]) - (char*)evars) / sv.world.edict_size; - edict_t *ed; - vec3_t diff; - float *org = VM_POINTER(arg[1]); - float rad = VM_FLOAT(arg[2]); - rad *= rad; - for(start++; start < sv.world.num_edicts; start++) - { - ed = EDICT_NUM(svprogfuncs, start); - if (ed->isfree) - continue; - VectorSubtract(ed->v->origin, org, diff); - if (rad > DotProduct(diff, diff)) - return (qintptr_t)(vevars + start*sv.world.edict_size); - } - return 0; - } - - case G_WALKMOVE: - { - wedict_t *ed = WEDICT_NUM(svprogfuncs, arg[0]); - float yaw = VM_FLOAT(arg[1]); - float dist = VM_FLOAT(arg[2]); - vec3_t move; - vec3_t axis[3]; - - World_GetEntGravityAxis(ed, axis); - - yaw = yaw*M_PI*2 / 360; - move[0] = cos(yaw)*dist; - move[1] = sin(yaw)*dist; - move[2] = 0; - - return World_movestep(&sv.world, (wedict_t*)ed, move, axis, true, false, NULL, NULL); - } - - case G_DROPTOFLOOR: - { - edict_t *ent; - vec3_t end; - vec3_t start; - trace_t trace; - extern cvar_t pr_droptofloorunits; - - ent = EDICT_NUM(svprogfuncs, arg[0]); - - VectorCopy (ent->v->origin, end); - if (pr_droptofloorunits.value > 0) - end[2] -= pr_droptofloorunits.value; - else - end[2] -= 256; - - VectorCopy (ent->v->origin, start); - trace = World_Move (&sv.world, start, ent->v->mins, ent->v->maxs, end, MOVE_NORMAL, (wedict_t*)ent); - - if (trace.fraction == 1 || trace.allsolid) - return false; - else - { - VectorCopy (trace.endpos, ent->v->origin); - World_LinkEdict (&sv.world, (wedict_t*)ent, false); - ent->v->flags = (int)ent->v->flags | FL_ONGROUND; - ent->v->groundentity = EDICT_TO_PROG(svprogfuncs, trace.ent); - return true; - } - } - break; - - case G_CHECKBOTTOM: - { - vec3_t up = {0,0,1}; - return World_CheckBottom(&sv.world, (wedict_t*)EDICT_NUM(svprogfuncs, VM_LONG(arg[0])), up); - } - - case G_POINTCONTENTS: - { - vec3_t v; - v[0] = VM_FLOAT(arg[0]); - v[1] = VM_FLOAT(arg[1]); - v[2] = VM_FLOAT(arg[2]); - return sv.world.worldmodel->funcs.PointContents(sv.world.worldmodel, NULL, v); - } - break; - - case G_NEXTENT: - { //input output are entity numbers - unsigned int i; - edict_t *ent; - - i = VM_LONG(arg[0]); - while (1) - { - i++; - if (i >= sv.world.num_edicts) - { - return 0; - } - ent = EDICT_NUM(svprogfuncs, i); - if (!ent->isfree) - { - return i; - } - } - break; - } - /* - case G_AIM: //not in mvdsv anyway - break; -*/ - case G_MAKESTATIC: - WrapQCBuiltin(PF_makestatic, offset, mask, arg, "n"); - break; - - case G_SETSPAWNPARAMS: - WrapQCBuiltin(PF_setspawnparms, offset, mask, arg, "n"); - break; - - case G_CHANGELEVEL: - WrapQCBuiltin(PF_changelevel, offset, mask, arg, "s"); - break; - - case G_LOGFRAG: - WrapQCBuiltin(PF_logfrag, offset, mask, arg, "nn"); - break; - case G_PRECACHE_VWEP_MODEL: - { - int i = WrapQCBuiltin(PF_precache_vwep_model, offset, mask, arg, "s"); - float f = *(float*)&i; - return f; - } - break; - - case G_GETINFOKEY: - { - char *v; - if (VM_OOB(arg[2], arg[3])) - return -1; - v = PF_infokey_Internal(VM_LONG(arg[0]), VM_POINTER(arg[1])); - Q_strncpyz(VM_POINTER(arg[2]), v, VM_LONG(arg[3])); - } - break; - - case G_MULTICAST: - WrapQCBuiltin(PF_multicast, offset, mask, arg, "vi"); - break; - - case G_DISABLEUPDATES: - //FIXME: remember to ask mvdsv people why this is useful - Con_Printf("G_DISABLEUPDATES: not supported\n"); - break; - - case G_WRITEBYTE: - WrapQCBuiltin(PF_WriteByte, offset, mask, arg, "ii"); - break; - case G_WRITECHAR: - WrapQCBuiltin(PF_WriteChar, offset, mask, arg, "ii"); - break; - case G_WRITESHORT: - WrapQCBuiltin(PF_WriteShort, offset, mask, arg, "ii"); - break; - case G_WRITELONG: - WrapQCBuiltin(PF_WriteLong, offset, mask, arg, "ii"); - break; - case G_WRITEANGLE: - WrapQCBuiltin(PF_WriteAngle, offset, mask, arg, "if"); - break; - case G_WRITECOORD: - WrapQCBuiltin(PF_WriteCoord, offset, mask, arg, "if"); - break; - case G_WRITESTRING: - PF_WriteString_Internal(VM_LONG(arg[0]), VM_POINTER(arg[1])); - break; - case G_WRITEENTITY: - WrapQCBuiltin(PF_WriteEntity, offset, mask, arg, "in"); - break; - - case G_FLUSHSIGNON: - SV_FlushSignon (); - break; - - case g_memset: - { - void *dst = VM_POINTER(arg[0]); - VALIDATEPOINTER(arg[0], arg[2]); - memset(dst, arg[1], arg[2]); - return arg[0]; - } - case g_memcpy: - { - void *dst = VM_POINTER(arg[0]); - void *src = VM_POINTER(arg[1]); - VALIDATEPOINTER(arg[0], arg[2]); - memmove(dst, src, arg[2]); - return arg[0]; - } - break; - case g_strncpy: - VALIDATEPOINTER(arg[0], arg[2]); - Q_strncpyS(VM_POINTER(arg[0]), VM_POINTER(arg[1]), arg[2]); - return arg[0]; - case g_sin: - VM_FLOAT(fn)=(float)sin(VM_FLOAT(arg[0])); - return fn; - case g_cos: - VM_FLOAT(fn)=(float)cos(VM_FLOAT(arg[0])); - return fn; - case g_atan2: - VM_FLOAT(fn)=(float)atan2(VM_FLOAT(arg[0]), VM_FLOAT(arg[1])); - return fn; - case g_sqrt: - VM_FLOAT(fn)=(float)sqrt(VM_FLOAT(arg[0])); - return fn; - case g_floor: - VM_FLOAT(fn)=(float)floor(VM_FLOAT(arg[0])); - return fn; - case g_ceil: - VM_FLOAT(fn)=(float)ceil(VM_FLOAT(arg[0])); - return fn; - case g_acos: - VM_FLOAT(fn)=(float)acos(VM_FLOAT(arg[0])); - return fn; - - case G_CMD_ARGC: - return Cmd_Argc(); - case G_CMD_ARGV: - { - char *c; - c = Cmd_Argv(VM_LONG(arg[0])); - if (VM_OOB(arg[1], arg[2])) - return -1; - Q_strncpyz(VM_POINTER(arg[1]), c, VM_LONG(arg[2])); - } - break; - - case G_TraceBox: - WrapQCBuiltin(PF_svtraceline, offset, mask, arg, "vvinvv"); - break; - - case G_FS_OpenFile: - //0 = name - //1 = &handle - //2 = mode - //ret = filesize or -1 - - // Con_Printf("G_FSOpenFile: %s (mode %i)\n", VM_POINTER(arg[0]), arg[2]); - { - int mode; - switch((q1qvmfsMode_t)arg[2]) - { - default: - return -1; - case FS_READ_BIN: - case FS_READ_TXT: - mode = VM_FS_READ; - break; - case FS_WRITE_BIN: - case FS_WRITE_TXT: - mode = VM_FS_WRITE; - break; - case FS_APPEND_BIN: - case FS_APPEND_TXT: - mode = VM_FS_APPEND; - break; - } - return VM_fopen(VM_POINTER(arg[0]), VM_POINTER(arg[1]), mode, VMFSID_Q1QVM); - } - break; - - case G_FS_CloseFile: - VM_fclose(arg[0], VMFSID_Q1QVM); - break; - - case G_FS_ReadFile: - if (VM_OOB(arg[0], arg[1])) - return 0; - return VM_FRead(VM_POINTER(arg[0]), VM_LONG(arg[1]), VM_LONG(arg[2]), VMFSID_Q1QVM); - - //not supported, open will fail anyway - case G_FS_WriteFile: - if (VM_OOB(arg[0], arg[1])) - return 0; - return VM_FWrite(VM_POINTER(arg[0]), VM_LONG(arg[1]), VM_LONG(arg[2]), VMFSID_Q1QVM); - case G_FS_SeekFile: - VM_FSeek(VM_LONG(arg[0]), VM_LONG(arg[1]), VM_LONG(arg[2]), VMFSID_Q1QVM); + VectorSubtract (e->v->maxs, e->v->mins, e->v->size); + World_LinkEdict (&sv.world, (wedict_t*)e, false); + return true; +} +static qintptr_t QVM_SetModel (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + edict_t *e = Q1QVMPF_EdictNum(svprogfuncs, arg[0]); + PF_setmodel_Internal(svprogfuncs, e, VM_POINTER(arg[1])); + return 0; +} +static qintptr_t QVM_BPrint (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + SV_BroadcastPrintf(arg[0], "%s", (char*)VM_POINTER(arg[1])); + return 0; +} +static qintptr_t QVM_SPrint (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + if ((unsigned)VM_LONG(arg[0]) > sv.allocated_client_slots) return 0; - case G_FS_TellFile: - return VM_FTell(VM_LONG(arg[0]), VMFSID_Q1QVM); + SV_ClientPrintf(&svs.clients[VM_LONG(arg[0])-1], VM_LONG(arg[1]), "%s", (char*)VM_POINTER(arg[2])); + return 0; +} +static qintptr_t QVM_CenterPrint (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + PF_centerprint_Internal(VM_LONG(arg[0]), false, VM_POINTER(arg[1])); + return 0; +} +static qintptr_t QVM_AmbientSound (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + vec3_t pos; + pos[0] = VM_FLOAT(arg[0]); + pos[1] = VM_FLOAT(arg[1]); + pos[2] = VM_FLOAT(arg[2]); + PF_ambientsound_Internal(pos, VM_POINTER(arg[3]), VM_FLOAT(arg[4]), VM_FLOAT(arg[5])); + return 0; +} +static qintptr_t QVM_Sound (void *offset, quintptr_t mask, const qintptr_t *arg) +{ +// ( int edn, int channel, char *samp, float vol, float att ) + SVQ1_StartSound (NULL, (wedict_t*)Q1QVMPF_EdictNum(svprogfuncs, VM_LONG(arg[0])), VM_LONG(arg[1]), VM_POINTER(arg[2]), VM_FLOAT(arg[3])*255, VM_FLOAT(arg[4]), 0); + return 0; +} +static qintptr_t QVM_TraceLine (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + WrapQCBuiltin(PF_svtraceline, offset, mask, arg, "vvin"); + return 0; +} +static qintptr_t QVM_CheckClient (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + return PF_checkclient_Internal(svprogfuncs); +} +static qintptr_t QVM_StuffCmd (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + PF_stuffcmd_Internal(VM_LONG(arg[0]), VM_POINTER(arg[1]), VM_LONG(arg[2])); + return 0; +} +static qintptr_t QVM_LocalCmd (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + Cbuf_AddText (VM_POINTER(arg[0]), RESTRICT_INSECURE); + return 0; +} +static qintptr_t QVM_CVar (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + cvar_t *c; + char *vname = VM_POINTER(arg[0]); - case G_FS_GetFileList: - if (VM_OOB(arg[2], arg[3])) - return 0; - return VM_GetFileList(VM_POINTER(arg[0]), VM_POINTER(arg[1]), VM_POINTER(arg[2]), VM_LONG(arg[3])); + //paused state is not a cvar in fte. + if (!strcmp(vname, "sv_paused")) + return VM_LONG(sv.paused); - case G_CVAR_SET_FLOAT: - { - cvar_t *var; - var = Cvar_Get(VM_POINTER(arg[0]), va("%f", VM_FLOAT(arg[1])), 0, "Gamecode variables"); - if (!var) - return -1; - Cvar_SetValue (var, VM_FLOAT(arg[1])); - } - break; + c = Cvar_Get(vname, "", 0, "Gamecode"); + return VM_LONG(c->value); +} +static qintptr_t QVM_CVar_Set (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + cvar_t *var; + var = Cvar_Get(VM_POINTER(arg[0]), VM_POINTER(arg[1]), 0, "Gamecode variables"); + if (!var) + return -1; + Cvar_Set (var, VM_POINTER(arg[1])); + return 0; +} - case G_CVAR_STRING: - { - char *n = VM_POINTER(arg[0]); - cvar_t *cv; - if (VM_OOB(arg[1], arg[2])) - return -1; - if (!strcmp(n, "version")) - { - n = version_string(); - Q_strncpyz(VM_POINTER(arg[1]), n, VM_LONG(arg[2])); - } - else - { - cv = Cvar_Get(n, "", 0, "QC variables"); - if (cv) - Q_strncpyz(VM_POINTER(arg[1]), cv->string, VM_LONG(arg[2])); - else - Q_strncpyz(VM_POINTER(arg[1]), "", VM_LONG(arg[2])); - } - } - break; - - case G_Map_Extension: - //yes, this does exactly match mvdsv... - if (VM_LONG(arg[1]) < G_MAX) - return -2; //can't map that there - else - return -1; //extension not known - - case G_strcmp: - { - char *a = VM_POINTER(arg[0]); - char *b = VM_POINTER(arg[1]); - return strcmp(a, b); - } - case G_strncmp: - { - char *a = VM_POINTER(arg[0]); - char *b = VM_POINTER(arg[1]); - return strncmp(a, b, VM_LONG(arg[2])); - } - case G_stricmp: - { - char *a = VM_POINTER(arg[0]); - char *b = VM_POINTER(arg[1]); - return stricmp(a, b); - } - case G_strnicmp: - { - char *a = VM_POINTER(arg[0]); - char *b = VM_POINTER(arg[1]); - return strnicmp(a, b, VM_LONG(arg[2])); - } - - case G_Find: - { - edict_t *e = VM_POINTER(arg[0]); - int ofs = VM_LONG(arg[1]) - WASTED_EDICT_T_SIZE; - char *match = VM_POINTER(arg[2]); - char *field; - int first = e?((char*)e - (char*)evars)/sv.world.edict_size:0; - int i; - if (!match) - match = ""; - for (i = first+1; i < sv.world.num_edicts; i++) - { - e = q1qvmedicttable[i]; - field = VM_POINTER(*((string_t*)e->v + ofs/4)); - if (field == NULL) - { - if (*match == '\0') - return ((char*)e->v - (char*)offset)-WASTED_EDICT_T_SIZE; - } - else - { - if (!strcmp(field, match)) - return ((char*)e->v - (char*)offset)-WASTED_EDICT_T_SIZE; - } - } - } - return 0; - - case G_executecmd: - WrapQCBuiltin(PF_ExecuteCommand, offset, mask, arg, ""); - break; - - case G_conprint: - Con_Printf("%s", (char*)VM_POINTER(arg[0])); - break; - - case G_readcmd: - { - extern char outputbuf[]; - extern redirect_t sv_redirected; - extern int sv_redirectedlang; - redirect_t old; - int oldl; - - char *s = VM_POINTER(arg[0]); - char *output = VM_POINTER(arg[1]); - int outputlen = VM_LONG(arg[2]); - - if (VM_OOB(arg[1], arg[2])) - return -1; - - Cbuf_Execute(); //FIXME: this code is flawed - Cbuf_AddText (s, RESTRICT_LOCAL); - - old = sv_redirected; - oldl = sv_redirectedlang; - if (old != RD_NONE) - SV_EndRedirect(); - - SV_BeginRedirect(RD_OBLIVION, TL_FindLanguage("")); - Cbuf_Execute(); - Q_strncpyz(output, outputbuf, outputlen); - SV_EndRedirect(); - - if (old != RD_NONE) - SV_BeginRedirect(old, oldl); - -Con_DPrintf("PF_readcmd: %s\n%s", s, output); - - } - break; - - case G_redirectcmd: - //FIXME: KTX uses this, along with a big fat warning. - //it shouldn't be vital to the normal functionality - //just restricts admin a little (did these guys never hear of rcon?) - //I'm too lazy to implement it though. - return 0; - - case G_Add_Bot: - //FIXME: not implemented, always returns failure. - //the other bot functions only ever work on bots anyway, so don't need to be implemented until this one is - return 0; -/* - case G_Remove_Bot: - break; - case G_SetBotCMD: - break; -*/ - case G_SETUSERINFO: - { - char *key = VM_POINTER(arg[1]); - if (*key == '*' && (VM_LONG(arg[3])&1)) - return -1; //denied! - return PF_ForceInfoKey_Internal(VM_LONG(arg[0]), VM_POINTER(arg[1]), VM_POINTER(arg[2])); - } - //fallthrough - - case G_SetBotUserInfo: - return PF_ForceInfoKey_Internal(VM_LONG(arg[0]), VM_POINTER(arg[1]), VM_POINTER(arg[2])); - - case G_MOVETOGOAL: - return World_MoveToGoal(&sv.world, (wedict_t*)Q1QVMPF_ProgsToEdict(svprogfuncs, pr_global_struct->self), VM_FLOAT(arg[0])); - - - case G_strftime: - { - char *out = VM_POINTER(arg[0]); - char *fmt = VM_POINTER(arg[2]); - time_t curtime; - struct tm *local; - if (VM_OOB(arg[0], arg[1]) || !out) - return -1; //please don't corrupt me - time(&curtime); - curtime += VM_LONG(arg[3]); - local = localtime(&curtime); - strftime(out, VM_LONG(arg[1]), fmt, local); - } - break; - case G_CMD_ARGS: - { - char *c; - c = Cmd_Args(); - if (VM_OOB(arg[0], arg[1])) - return -1; - Q_strncpyz(VM_POINTER(arg[0]), c, VM_LONG(arg[1])); - } - break; - case G_CMD_TOKENIZE: - { - char *str = VM_POINTER(arg[0]); - Cmd_TokenizeString(str, false, false); - return Cmd_Argc(); - } - break; - case G_strlcpy: - { - char *dst = VM_POINTER(arg[0]); - char *src = VM_POINTER(arg[1]); - if (VM_OOB(arg[0], arg[2]) || VM_LONG(arg[2]) < 1) - return -1; - else if (!src) - { - *dst = 0; - return 0; - } - else - { - Q_strncpyz(dst, src, VM_LONG(arg[2])); - return strlen(src); - } - } - break; - case G_strlcat: - { - char *dst = VM_POINTER(arg[0]); - char *src = VM_POINTER(arg[1]); - if (VM_OOB(arg[0], arg[2])) - return -1; - Q_strncatz(dst, src, VM_LONG(arg[2])); - //WARNING: no return value - } - break; - - case G_MAKEVECTORS: - AngleVectors(VM_POINTER(arg[0]), P_VEC(v_forward), P_VEC(v_right), P_VEC(v_up)); - break; - - case G_NEXTCLIENT: - { - unsigned int start = ((char*)VM_POINTER(arg[0]) - (char*)evars) / sv.world.edict_size; - while (start < sv.allocated_client_slots) - { - if (svs.clients[start].state == cs_spawned) - return (qintptr_t)(vevars + (start+1) * sv.world.edict_size); - start++; - } - return 0; - } - break; - - case G_SETPAUSE: - { - int pause = VM_LONG(arg[0]); - if ((sv.paused&1) == (pause&1)) - break; //nothing changed, ignore it. - sv.paused = pause; - sv.pausedstart = Sys_DoubleTime(); - } - break; - - default: - SV_Error("Q1QVM: Trap %i not implemented\n", (int)fn); - break; +static qintptr_t QVM_FindRadius (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + int start = ((char*)VM_POINTER(arg[0]) - (char*)evars) / sv.world.edict_size; + edict_t *ed; + vec3_t diff; + float *org = VM_POINTER(arg[1]); + float rad = VM_FLOAT(arg[2]); + rad *= rad; + for(start++; start < sv.world.num_edicts; start++) + { + ed = EDICT_NUM(svprogfuncs, start); + if (ed->isfree) + continue; + VectorSubtract(ed->v->origin, org, diff); + if (rad > DotProduct(diff, diff)) + return (qintptr_t)(vevars + start*sv.world.edict_size); } return 0; } +static qintptr_t QVM_WalkMove (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + wedict_t *ed = WEDICT_NUM(svprogfuncs, arg[0]); + float yaw = VM_FLOAT(arg[1]); + float dist = VM_FLOAT(arg[2]); + vec3_t move; + vec3_t axis[3]; + + World_GetEntGravityAxis(ed, axis); + + yaw = yaw*M_PI*2 / 360; + move[0] = cos(yaw)*dist; + move[1] = sin(yaw)*dist; + move[2] = 0; + + return World_movestep(&sv.world, (wedict_t*)ed, move, axis, true, false, NULL, NULL); +} +static qintptr_t QVM_DropToFloor (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + edict_t *ent; + vec3_t end; + vec3_t start; + trace_t trace; + extern cvar_t pr_droptofloorunits; + + ent = EDICT_NUM(svprogfuncs, arg[0]); + + VectorCopy (ent->v->origin, end); + if (pr_droptofloorunits.value > 0) + end[2] -= pr_droptofloorunits.value; + else + end[2] -= 256; + + VectorCopy (ent->v->origin, start); + trace = World_Move (&sv.world, start, ent->v->mins, ent->v->maxs, end, MOVE_NORMAL, (wedict_t*)ent); + + if (trace.fraction == 1 || trace.allsolid) + return false; + else + { + VectorCopy (trace.endpos, ent->v->origin); + World_LinkEdict (&sv.world, (wedict_t*)ent, false); + ent->v->flags = (int)ent->v->flags | FL_ONGROUND; + ent->v->groundentity = EDICT_TO_PROG(svprogfuncs, trace.ent); + return true; + } +} +static qintptr_t QVM_CheckBottom (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + vec3_t up = {0,0,1}; + return World_CheckBottom(&sv.world, (wedict_t*)EDICT_NUM(svprogfuncs, VM_LONG(arg[0])), up); +} +static qintptr_t QVM_PointContents (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + vec3_t v; + v[0] = VM_FLOAT(arg[0]); + v[1] = VM_FLOAT(arg[1]); + v[2] = VM_FLOAT(arg[2]); + return sv.world.worldmodel->funcs.PointContents(sv.world.worldmodel, NULL, v); +} +static qintptr_t QVM_NextEnt (void *offset, quintptr_t mask, const qintptr_t *arg) +{ //input output are entity numbers + unsigned int i; + edict_t *ent; + + i = VM_LONG(arg[0]); + while (1) + { + i++; + if (i >= sv.world.num_edicts) + { + return 0; + } + ent = EDICT_NUM(svprogfuncs, i); + if (!ent->isfree) + { + return i; + } + } +} +static qintptr_t QVM_Aim (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + Con_DPrintf("QVM_Aim: not implemented\n"); + return 0; //not in mvdsv anyway +} +static qintptr_t QVM_MakeStatic (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + WrapQCBuiltin(PF_makestatic, offset, mask, arg, "n"); + return 0; +} +static qintptr_t QVM_SetSpawnParams (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + WrapQCBuiltin(PF_setspawnparms, offset, mask, arg, "n"); + return 0; +} +static qintptr_t QVM_ChangeLevel (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + WrapQCBuiltin(PF_changelevel, offset, mask, arg, "s"); + return 0; +} +static qintptr_t QVM_ChangeLevel2 (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + WrapQCBuiltin(PF_changelevel, offset, mask, arg, "ss"); + return 0; +} +static qintptr_t QVM_LogFrag (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + WrapQCBuiltin(PF_logfrag, offset, mask, arg, "nn"); + return 0; +} +static qintptr_t QVM_Precache_VWep_Model (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + int i = WrapQCBuiltin(PF_precache_vwep_model, offset, mask, arg, "s"); + float f = *(float*)&i; + return f; +} +static qintptr_t QVM_GetInfoKey (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + char *v; + if (VM_OOB(arg[2], arg[3])) + return -1; + v = PF_infokey_Internal(VM_LONG(arg[0]), VM_POINTER(arg[1])); + Q_strncpyz(VM_POINTER(arg[2]), v, VM_LONG(arg[3])); + return 0; +} +static qintptr_t QVM_Multicast (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + WrapQCBuiltin(PF_multicast, offset, mask, arg, "vi"); + return 0; +} +static qintptr_t QVM_DisableUpdates (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + //FIXME: remember to ask mvdsv people why this is useful + Con_Printf("G_DISABLEUPDATES: not supported\n"); + return 0; +} +static qintptr_t QVM_WriteByte (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + WrapQCBuiltin(PF_WriteByte, offset, mask, arg, "ii"); + return 0; +} +static qintptr_t QVM_WriteChar (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + WrapQCBuiltin(PF_WriteChar, offset, mask, arg, "ii"); + return 0; +} +static qintptr_t QVM_WriteShort (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + WrapQCBuiltin(PF_WriteShort, offset, mask, arg, "ii"); + return 0; +} +static qintptr_t QVM_WriteLong (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + WrapQCBuiltin(PF_WriteLong, offset, mask, arg, "ii"); + return 0; +} +static qintptr_t QVM_WriteAngle (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + WrapQCBuiltin(PF_WriteAngle, offset, mask, arg, "if"); + return 0; +} +static qintptr_t QVM_WriteCoord (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + WrapQCBuiltin(PF_WriteCoord, offset, mask, arg, "if"); + return 0; +} +static qintptr_t QVM_WriteString (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + PF_WriteString_Internal(VM_LONG(arg[0]), VM_POINTER(arg[1])); + return 0; +} +static qintptr_t QVM_WriteEntity (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + WrapQCBuiltin(PF_WriteEntity, offset, mask, arg, "in"); + return 0; +} +static qintptr_t QVM_FlushSignon (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + SV_FlushSignon (); + return 0; +} +static qintptr_t QVM_memset (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + void *dst = VM_POINTER(arg[0]); + VALIDATEPOINTER(arg[0], arg[2]); + memset(dst, arg[1], arg[2]); + return arg[0]; +} +static qintptr_t QVM_memcpy (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + void *dst = VM_POINTER(arg[0]); + void *src = VM_POINTER(arg[1]); + VALIDATEPOINTER(arg[0], arg[2]); + memmove(dst, src, arg[2]); + return arg[0]; +} +static qintptr_t QVM_strncpy (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + VALIDATEPOINTER(arg[0], arg[2]); + Q_strncpyS(VM_POINTER(arg[0]), VM_POINTER(arg[1]), arg[2]); + return arg[0]; +} +static qintptr_t QVM_sin (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + qintptr_t fn; + VM_FLOAT(fn)=(float)sin(VM_FLOAT(arg[0])); + return fn; +} +static qintptr_t QVM_cos (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + qintptr_t fn; + VM_FLOAT(fn)=(float)cos(VM_FLOAT(arg[0])); + return fn; +} +static qintptr_t QVM_atan2 (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + int fn; + VM_FLOAT(fn)=(float)atan2(VM_FLOAT(arg[0]), VM_FLOAT(arg[1])); + return fn; +} +static qintptr_t QVM_sqrt (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + int fn; + VM_FLOAT(fn)=(float)sqrt(VM_FLOAT(arg[0])); + return fn; +} +static qintptr_t QVM_floor (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + int fn; + VM_FLOAT(fn)=(float)floor(VM_FLOAT(arg[0])); + return fn; +} +static qintptr_t QVM_ceil (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + int fn; + VM_FLOAT(fn)=(float)ceil(VM_FLOAT(arg[0])); + return fn; +} +static qintptr_t QVM_acos (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + int fn; + VM_FLOAT(fn)=(float)acos(VM_FLOAT(arg[0])); + return fn; +} +static qintptr_t QVM_Cmd_ArgC (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + return Cmd_Argc(); +} +static qintptr_t QVM_Cmd_ArgV (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + char *c; + c = Cmd_Argv(VM_LONG(arg[0])); + if (VM_OOB(arg[1], arg[2])) + return -1; + Q_strncpyz(VM_POINTER(arg[1]), c, VM_LONG(arg[2])); + return 0; +} +static qintptr_t QVM_TraceBox (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + WrapQCBuiltin(PF_svtraceline, offset, mask, arg, "vvinvv"); + return 0; +} +static qintptr_t QVM_FS_OpenFile (void *offset, quintptr_t mask, const qintptr_t *arg) +{ +//0 = name +//1 = &handle +//2 = mode +//ret = filesize or -1 + +// Con_Printf("G_FSOpenFile: %s (mode %i)\n", VM_POINTER(arg[0]), arg[2]); + int mode; + switch((q1qvmfsMode_t)arg[2]) + { + default: + return -1; + case FS_READ_BIN: + case FS_READ_TXT: + mode = VM_FS_READ; + break; + case FS_WRITE_BIN: + case FS_WRITE_TXT: + mode = VM_FS_WRITE; + break; + case FS_APPEND_BIN: + case FS_APPEND_TXT: + mode = VM_FS_APPEND; + break; + } + return VM_fopen(VM_POINTER(arg[0]), VM_POINTER(arg[1]), mode, VMFSID_Q1QVM); +} +static qintptr_t QVM_FS_CloseFile (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + VM_fclose(arg[0], VMFSID_Q1QVM); + return 0; +} +static qintptr_t QVM_FS_ReadFile (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + if (VM_OOB(arg[0], arg[1])) + return 0; + return VM_FRead(VM_POINTER(arg[0]), VM_LONG(arg[1]), VM_LONG(arg[2]), VMFSID_Q1QVM); +} +static qintptr_t QVM_FS_WriteFile (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + if (VM_OOB(arg[0], arg[1])) + return 0; + return VM_FWrite(VM_POINTER(arg[0]), VM_LONG(arg[1]), VM_LONG(arg[2]), VMFSID_Q1QVM); +} +static qintptr_t QVM_FS_SeekFile (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + //fixme: what should the return value be? + VM_FSeek(VM_LONG(arg[0]), VM_LONG(arg[1]), VM_LONG(arg[2]), VMFSID_Q1QVM); + return 0; +} +static qintptr_t QVM_FS_TellFile (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + return VM_FTell(VM_LONG(arg[0]), VMFSID_Q1QVM); +} +static qintptr_t QVM_FS_GetFileList (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + if (VM_OOB(arg[2], arg[3])) + return 0; + return VM_GetFileList(VM_POINTER(arg[0]), VM_POINTER(arg[1]), VM_POINTER(arg[2]), VM_LONG(arg[3])); +} +static qintptr_t QVM_CVar_Set_Float (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + cvar_t *var; + var = Cvar_Get(VM_POINTER(arg[0]), va("%f", VM_FLOAT(arg[1])), 0, "Gamecode variables"); + if (!var) + return -1; + Cvar_SetValue (var, VM_FLOAT(arg[1])); + return 0; +} +static qintptr_t QVM_CVar_String (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + char *n = VM_POINTER(arg[0]); + cvar_t *cv; + if (VM_OOB(arg[1], arg[2])) + return -1; + if (!strcmp(n, "version")) + { + n = version_string(); + Q_strncpyz(VM_POINTER(arg[1]), n, VM_LONG(arg[2])); + } + else + { + cv = Cvar_Get(n, "", 0, "QC variables"); + if (cv) + Q_strncpyz(VM_POINTER(arg[1]), cv->string, VM_LONG(arg[2])); + else + Q_strncpyz(VM_POINTER(arg[1]), "", VM_LONG(arg[2])); + } + return 0; +} +static qintptr_t QVM_strcmp (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + char *a = VM_POINTER(arg[0]); + char *b = VM_POINTER(arg[1]); + return strcmp(a, b); +} +static qintptr_t QVM_strncmp (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + char *a = VM_POINTER(arg[0]); + char *b = VM_POINTER(arg[1]); + return strncmp(a, b, VM_LONG(arg[2])); +} +static qintptr_t QVM_stricmp (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + char *a = VM_POINTER(arg[0]); + char *b = VM_POINTER(arg[1]); + return stricmp(a, b); +} +static qintptr_t QVM_strnicmp (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + char *a = VM_POINTER(arg[0]); + char *b = VM_POINTER(arg[1]); + return strnicmp(a, b, VM_LONG(arg[2])); +} +static qintptr_t QVM_Find (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + edict_t *e = VM_POINTER(arg[0]); + int ofs = VM_LONG(arg[1]) - WASTED_EDICT_T_SIZE; + char *match = VM_POINTER(arg[2]); + char *field; + int first = e?((char*)e - (char*)evars)/sv.world.edict_size:0; + int i; + if (!match) + match = ""; + for (i = first+1; i < sv.world.num_edicts; i++) + { + e = q1qvmedicttable[i]; + field = VM_POINTER(*((string_t*)e->v + ofs/4)); + if (field == NULL) + { + if (*match == '\0') + return ((char*)e->v - (char*)offset)-WASTED_EDICT_T_SIZE; + } + else + { + if (!strcmp(field, match)) + return ((char*)e->v - (char*)offset)-WASTED_EDICT_T_SIZE; + } + } + return 0; +} +static qintptr_t QVM_ExecuteCmd (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + WrapQCBuiltin(PF_ExecuteCommand, offset, mask, arg, ""); + return 0; +} +static qintptr_t QVM_ConPrint (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + Con_Printf("%s", (char*)VM_POINTER(arg[0])); + return 0; +} +static qintptr_t QVM_ReadCmd (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + extern char outputbuf[]; + extern redirect_t sv_redirected; + extern int sv_redirectedlang; + redirect_t old; + int oldl; + + char *s = VM_POINTER(arg[0]); + char *output = VM_POINTER(arg[1]); + int outputlen = VM_LONG(arg[2]); + + if (VM_OOB(arg[1], arg[2])) + return -1; + + Cbuf_Execute(); //FIXME: this code is flawed + Cbuf_AddText (s, RESTRICT_LOCAL); + + old = sv_redirected; + oldl = sv_redirectedlang; + if (old != RD_NONE) + SV_EndRedirect(); + + SV_BeginRedirect(RD_OBLIVION, TL_FindLanguage("")); + Cbuf_Execute(); + Q_strncpyz(output, outputbuf, outputlen); + SV_EndRedirect(); + + if (old != RD_NONE) + SV_BeginRedirect(old, oldl); + +Con_DPrintf("PF_readcmd: %s\n%s", s, output); + + return 0; +} +static qintptr_t QVM_RedirectCmd (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + //FIXME: KTX uses this, along with a big fat warning. + //it shouldn't be vital to the normal functionality + //just restricts admin a little (did these guys never hear of rcon?) + //I'm too lazy to implement it though. + + Con_DPrintf("QVM_RedirectCmd: not implemented\n"); + return 0; +} +static qintptr_t QVM_Add_Bot (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + //FIXME: not implemented, always returns failure. + //the other bot functions only ever work on bots anyway, so don't need to be implemented until this one is + + //return WrapQCBuiltin(PF_spawnclient, offset, mask, arg, ""); + Con_DPrintf("QVM_Add_Bot: not implemented\n"); + return 0; +} +static qintptr_t QVM_Remove_Bot (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + //fixme: should become general kick + + Con_DPrintf("QVM_Remove_Bot: not implemented\n"); + + //WrapQCBuiltin(PF_dropclient, offset, mask, arg, "n"); + return 0; +} +static qintptr_t QVM_SetBotCMD (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + //in mvdsv, this is run *after* the frame. + Con_DPrintf("QVM_SetBotCMD: not implemented\n"); + return 0; +} +static qintptr_t QVM_SetUserInfo (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + char *key = VM_POINTER(arg[1]); + if (*key == '*' && (VM_LONG(arg[3])&1)) + return -1; //denied! + return PF_ForceInfoKey_Internal(VM_LONG(arg[0]), VM_POINTER(arg[1]), VM_POINTER(arg[2])); +} +static qintptr_t QVM_SetBotUserInfo (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + return PF_ForceInfoKey_Internal(VM_LONG(arg[0]), VM_POINTER(arg[1]), VM_POINTER(arg[2])); +} +static qintptr_t QVM_MoveToGoal (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + return World_MoveToGoal(&sv.world, (wedict_t*)Q1QVMPF_ProgsToEdict(svprogfuncs, pr_global_struct->self), VM_FLOAT(arg[0])); +} +static qintptr_t QVM_strftime (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + char *out = VM_POINTER(arg[0]); + char *fmt = VM_POINTER(arg[2]); + time_t curtime; + struct tm *local; + if (VM_OOB(arg[0], arg[1]) || !out) + return -1; //please don't corrupt me + time(&curtime); + curtime += VM_LONG(arg[3]); + local = localtime(&curtime); + strftime(out, VM_LONG(arg[1]), fmt, local); + return 0; +} +static qintptr_t QVM_Cmd_ArgS (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + char *c; + c = Cmd_Args(); + if (VM_OOB(arg[0], arg[1])) + return -1; + Q_strncpyz(VM_POINTER(arg[0]), c, VM_LONG(arg[1])); + return arg[0]; +} +static qintptr_t QVM_Cmd_Tokenize (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + char *str = VM_POINTER(arg[0]); + Cmd_TokenizeString(str, false, false); + return Cmd_Argc(); +} +static qintptr_t QVM_strlcpy (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + char *dst = VM_POINTER(arg[0]); + char *src = VM_POINTER(arg[1]); + if (VM_OOB(arg[0], arg[2]) || VM_LONG(arg[2]) < 1) + return -1; + else if (!src) + { + *dst = 0; + return 0; + } + else + { + Q_strncpyz(dst, src, VM_LONG(arg[2])); + return strlen(src); + } +} +static qintptr_t QVM_strlcat (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + char *dst = VM_POINTER(arg[0]); + char *src = VM_POINTER(arg[1]); + if (VM_OOB(arg[0], arg[2])) + return -1; + Q_strncatz(dst, src, VM_LONG(arg[2])); + //WARNING: no return value + return 0; +} +static qintptr_t QVM_MakeVectors (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + AngleVectors(VM_POINTER(arg[0]), P_VEC(v_forward), P_VEC(v_right), P_VEC(v_up)); + return 0; +} +static qintptr_t QVM_NextClient (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + unsigned int start = ((char*)VM_POINTER(arg[0]) - (char*)evars) / sv.world.edict_size; + while (start < sv.allocated_client_slots) + { + if (svs.clients[start].state == cs_spawned) + return (qintptr_t)(vevars + (start+1) * sv.world.edict_size); + start++; + } + return 0; +} +static qintptr_t QVM_SetPause (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + int pause = VM_LONG(arg[0]); + if ((sv.paused&1) == (pause&1)) + return sv.paused&1; //nothing changed, ignore it. + sv.paused = pause; + sv.pausedstart = Sys_DoubleTime(); + return sv.paused&1; +} +static qintptr_t QVM_NotYetImplemented (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + SV_Error("Q1QVM: Trap not implemented\n"); + return 0; +} + +static int QVM_FindExtField(char *fname) +{ + extentvars_t *xv = NULL; +#define comfieldfloat(name,desc) if (!strcmp(fname, #name)) return ((int*)&xv->name - (int*)xv); +#define comfieldvector(name,desc) if (!strcmp(fname, #name)) return ((int*)&xv->name - (int*)xv); +#define comfieldentity(name,desc) if (!strcmp(fname, #name)) return ((int*)&xv->name - (int*)xv); +#define comfieldstring(name,desc) if (!strcmp(fname, #name)) return ((int*)&xv->name - (int*)xv); +#define comfieldfunction(name, typestr,desc) if (!strcmp(fname, #name)) return ((int*)&xv->name - (int*)xv); +comextqcfields +svextqcfields +#undef comfieldfloat +#undef comfieldvector +#undef comfieldentity +#undef comfieldstring +#undef comfieldfunction + return -1; //unsupported +} +static qintptr_t QVM_SetExtField (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + edict_t *e = VM_POINTER(arg[0]); + int i = QVM_FindExtField(VM_POINTER(arg[1])); + int value = VM_LONG(arg[2]); + + if (i < 0) + return 0; + ((int*)e->xv)[i] = value; + return value; +} +static qintptr_t QVM_GetExtField (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + edict_t *e = VM_POINTER(arg[0]); + int i = QVM_FindExtField(VM_POINTER(arg[1])); + + if (i < 0) + return 0; + return ((int*)e->xv)[i]; +} + +#ifdef WEBCLIENT +static void QVM_uri_query_callback(struct dl_download *dl) +{ + void *cb_context = dl->user_ctx; + int cb_entry = dl->user_float; + int selfnum = dl->user_num; + + if (svs.gametype != GT_Q1QVM || svs.spawncount != dl->user_sequence) + return; //the world moved on. + //fixme: pointers might not still be valid if the map changed. + + *sv.world.g.self = selfnum; + if (dl->file) + { + size_t len = VFS_GETLEN(dl->file); + char *buffer = malloc(len+1); + buffer[len] = 0; + VFS_READ(dl->file, buffer, len); + Cmd_Args_Set(buffer); + free(buffer); + } + else + Cmd_Args_Set(NULL); + VM_Call(q1qvm, cb_entry, cb_context, dl->replycode, 0, 0, 0); +} + +//bool uri_get(char *uri, int cb_entry, void *cb_ctx, char *mime, void *data, unsigned datasize) +static qintptr_t QVM_uri_query (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + struct dl_download *dl; + + const unsigned char *url = VM_POINTER(arg[0]); + int cb_entry = VM_LONG(arg[1]); + void *cb_context = VM_POINTER(arg[2]); + const char *mimetype = VM_POINTER(arg[3]); + const char *data = VM_POINTER(arg[4]); + size_t datasize = VM_LONG(arg[5]); + extern cvar_t pr_enable_uriget; + + if (!pr_enable_uriget.ival) + { + Con_Printf("QVM_uri_query(\"%s\",%x): %s disabled\n", url, (int)cb_context, pr_enable_uriget.name); + return 0; + } + + if (mimetype && *mimetype) + { + VALIDATEPOINTER(arg[4],datasize); + Con_DPrintf("QVM_uri_query(%s,%x)\n", url, (int)cb_context); + dl = HTTP_CL_Put(url, mimetype, data, datasize, QVM_uri_query_callback); + } + else + { + Con_DPrintf("QVM_uri_query(%s,%x)\n", url, (int)cb_context); + dl = HTTP_CL_Get(url, NULL, QVM_uri_query_callback); + } + if (dl) + { + dl->user_ctx = cb_context; + dl->user_float = cb_entry; + dl->user_num = *sv.world.g.self; + dl->user_sequence = svs.spawncount; + dl->isquery = true; + return 1; + } + else + return 0; +} +#endif + +void QCBUILTIN PF_sv_trailparticles(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); +void QCBUILTIN PF_sv_pointparticles(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); +void QCBUILTIN PF_sv_particleeffectnum(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); +static qintptr_t QVM_particleeffectnum (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + int i = WrapQCBuiltin(PF_sv_particleeffectnum, offset, mask, arg, "s"); + return VM_FLOAT(i); +} +static qintptr_t QVM_trailparticles (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + return WrapQCBuiltin(PF_sv_trailparticles, offset, mask, arg, "invv"); +} +static qintptr_t QVM_pointparticles (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + return WrapQCBuiltin(PF_sv_pointparticles, offset, mask, arg, "ivvi"); +} + +static qintptr_t QVM_Map_Extension (void *offset, quintptr_t mask, const qintptr_t *arg); + +typedef qintptr_t (*traps_t) (void *offset, quintptr_t mask, const qintptr_t *arg); +traps_t bitraps[G_MAX] = +{ + QVM_GetAPIVersion, + QVM_DPrint, + QVM_Error, + QVM_GetEntityToken, + QVM_Spawn_Ent, + QVM_Remove_Ent, + QVM_Precache_Sound, + QVM_Precache_Model, + QVM_LightStyle, + QVM_SetOrigin, + QVM_SetSize, //10 + QVM_SetModel, + QVM_BPrint, + QVM_SPrint, + QVM_CenterPrint, + QVM_AmbientSound, //15 + QVM_Sound, + QVM_TraceLine, + QVM_CheckClient, + QVM_StuffCmd, + QVM_LocalCmd, //20 + QVM_CVar, + QVM_CVar_Set, + QVM_FindRadius, + QVM_WalkMove, + QVM_DropToFloor, //25 + QVM_CheckBottom, + QVM_PointContents, + QVM_NextEnt, + QVM_Aim, + QVM_MakeStatic, //30 + QVM_SetSpawnParams, + QVM_ChangeLevel, + QVM_LogFrag, + QVM_GetInfoKey, + QVM_Multicast, //35 + QVM_DisableUpdates, + QVM_WriteByte, + QVM_WriteChar, + QVM_WriteShort, + QVM_WriteLong, //40 + QVM_WriteAngle, + QVM_WriteCoord, + QVM_WriteString, + QVM_WriteEntity, + QVM_FlushSignon, //45 + QVM_memset, + QVM_memcpy, + QVM_strncpy, + QVM_sin, + QVM_cos, //50 + QVM_atan2, + QVM_sqrt, + QVM_floor, + QVM_ceil, + QVM_acos, //55 + QVM_Cmd_ArgC, + QVM_Cmd_ArgV, + QVM_TraceBox, //was G_TraceCapsule + QVM_FS_OpenFile, + QVM_FS_CloseFile, //60 + QVM_FS_ReadFile, + QVM_FS_WriteFile, + QVM_FS_SeekFile, + QVM_FS_TellFile, + QVM_FS_GetFileList, //65 + QVM_CVar_Set_Float, + QVM_CVar_String, + QVM_Map_Extension, + QVM_strcmp, + QVM_strncmp, //70 + QVM_stricmp, + QVM_strnicmp, + QVM_Find, + QVM_ExecuteCmd, + QVM_ConPrint, //75 + QVM_ReadCmd, + QVM_RedirectCmd, + QVM_Add_Bot, + QVM_Remove_Bot, + QVM_SetBotUserInfo, //80 + QVM_SetBotCMD, + + QVM_strftime, + QVM_Cmd_ArgS, + QVM_Cmd_Tokenize, + QVM_strlcpy, //85 + QVM_strlcat, + QVM_MakeVectors, + QVM_NextClient, + + QVM_Precache_VWep_Model, + QVM_SetPause, + QVM_SetUserInfo, + QVM_MoveToGoal +}; + +struct +{ + char *extname; + traps_t trap; +} qvmextensions[] = +{ + {"SetExtField", QVM_SetExtField}, + {"GetExtField", QVM_GetExtField}, + {"ChangeLevel2", QVM_ChangeLevel2}, //with start spot + {"URI_Query", QVM_uri_query}, //with start spot + {"particleeffectnum", QVM_particleeffectnum}, + {"trailparticles", QVM_trailparticles}, + {"pointparticles", QVM_pointparticles}, + + //sql? + //model querying? + //heightmap / brush editing? + //custom stats (mod can always writebyte, I guess, sounds horrible though) + //csqc ents + {NULL, NULL} +}; + +traps_t traps[512]; + +static qintptr_t QVM_Map_Extension (void *offset, quintptr_t mask, const qintptr_t *arg) +{ + char *extname = VM_POINTER(arg[0]); + unsigned int slot = VM_LONG(arg[1]); + int i; + + if (slot >= countof(traps)) + return -2; //invalid slot. + + if (!extname) + { //special handling for vauge compat with mvdsv, for testing how many 'known' builtins are implemented. + if (slot < G_MAX) + return -2; + return -1; + } + + //find the extension and map it to the slot if found. + for (i = 0; qvmextensions[i].extname; i++) + { + if (!Q_strcasecmp(extname, qvmextensions[i].extname)) + { + traps[slot] = qvmextensions[i].trap; + return slot; + } + } + return -1; //extension not known +} + +//============== general Quake services ================== #if __WORDSIZE == 64 static int syscallqvm (void *offset, quintptr_t mask, int fn, const int *arg) @@ -1281,10 +1621,17 @@ static int syscallqvm (void *offset, quintptr_t mask, int fn, const int *arg) int i; for (i = 0; i < 13; i++) args[i] = arg[i]; - return syscallhandle(offset, mask, fn, args); + if (fn >= countof(traps)) + return QVM_NotYetImplemented(offset, mask, args); + return traps[fn](offset, mask, args); } #else -#define syscallqvm (sys_callqvm_t)syscallhandle +static int syscallqvm (void *offset, quintptr_t mask, int fn, const int *arg) +{ + if (fn >= countof(traps)) + return QVM_NotYetImplemented(offset, mask, arg); + return traps[fn](offset, mask, arg); +} #endif static qintptr_t EXPORT_FN syscallnative (qintptr_t arg, ...) @@ -1308,7 +1655,9 @@ static qintptr_t EXPORT_FN syscallnative (qintptr_t arg, ...) args[12]=va_arg(argptr, qintptr_t); va_end(argptr); - return syscallhandle(NULL, ~0, arg, args); + if (arg >= countof(traps)) + return QVM_NotYetImplemented(NULL, ~(quintptr_t)0, args); + return traps[arg](NULL, ~(quintptr_t)0, args); } void Q1QVM_Shutdown(void) @@ -1388,6 +1737,8 @@ qboolean PR_LoadQ1QVM(void) Q1QVM_Shutdown(); q1qvm = VM_Create("qwprogs", com_nogamedirnativecode.ival?NULL:syscallnative, syscallqvm); + if (!q1qvm) + q1qvm = VM_Create("qwprogs", syscallnative, NULL); if (!q1qvm) { if (svprogfuncs == &q1qvmprogfuncs) @@ -1395,6 +1746,13 @@ qboolean PR_LoadQ1QVM(void) return false; } + for(i = 0; i < G_MAX; i++) + traps[i] = bitraps[i]; + for(; i < countof(traps); i++) + traps[i] = QVM_NotYetImplemented; + + + memset(&fofs, 0, sizeof(fofs)); progstype = PROG_QW; @@ -1538,6 +1896,14 @@ qboolean PR_LoadQ1QVM(void) for (; i < NUM_SPAWN_PARMS; i++) pr_global_ptrs->spawnparamglobals[i] = NULL; + for (i = 0; gd->fields[i].name; i++) + { + const char *fname = Q1QVMPF_StringToNative(&q1qvmprogfuncs, gd->fields[i].name); +#define emufield(n,t) if (gd->fields[i].type == t && !strcmp(#n, fname)) {fofs.n = (gd->fields[i].ofs - WASTED_EDICT_T_SIZE)/sizeof(float); continue;} + emufields +#undef emufield + } + sv.world.progs = &q1qvmprogfuncs; sv.world.edicts = (wedict_t*)EDICT_NUM(svprogfuncs, 0); @@ -1545,6 +1911,8 @@ qboolean PR_LoadQ1QVM(void) if ((unsigned)gd->global->mapname && (unsigned)gd->global->mapname+MAPNAME_LEN < VM_MemoryMask(q1qvm)) Q_strncpyz((char*)VM_MemoryBase(q1qvm) + gd->global->mapname, sv.mapname, MAPNAME_LEN); + else + gd->global->mapname = Q1QVMPF_StringToProgs(sv.world.progs, sv.mapname); PR_SV_FillWorldGlobals(&sv.world); return true; @@ -1573,6 +1941,11 @@ void Q1QVM_ClientConnect(client_t *cl) else Con_Printf("WARNING: Mod provided no netname buffer. Player names will not be set properly.\n"); + if (fofs.gravity) + ((float*)sv_player->v)[fofs.gravity] = sv_player->xv->gravity; + if (fofs.maxspeed) + ((float*)sv_player->v)[fofs.maxspeed] = sv_player->xv->maxspeed; + // call the spawn function pr_global_struct->time = sv.world.physicstime; pr_global_struct->self = EDICT_TO_PROG(svprogfuncs, cl->edict); @@ -1632,6 +2005,17 @@ qboolean Q1QVM_UserInfoChanged(edict_t *player) void Q1QVM_PlayerPreThink(void) { + if (fofs.movement) + { + sv_player->xv->movement[0] = ((float*)sv_player->v)[fofs.movement+0]; + sv_player->xv->movement[1] = ((float*)sv_player->v)[fofs.movement+1]; + sv_player->xv->movement[2] = ((float*)sv_player->v)[fofs.movement+2]; + } + if (fofs.gravity) + sv_player->xv->gravity = ((float*)sv_player->v)[fofs.gravity]; + if (fofs.maxspeed) + sv_player->xv->maxspeed = ((float*)sv_player->v)[fofs.maxspeed]; + VM_Call(q1qvm, GAME_CLIENT_PRETHINK, host_client->spectator, 0, 0, 0); } @@ -1644,6 +2028,9 @@ void Q1QVM_RunPlayerThink(void) void Q1QVM_PostThink(void) { VM_Call(q1qvm, GAME_CLIENT_POSTTHINK, host_client->spectator, 0, 0, 0); + + if (fofs.vw_index) + sv_player->xv->vw_index = ((float*)sv_player->v)[fofs.vw_index]; } void Q1QVM_StartFrame(void) diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index cdefe7a4f..12eb6bea6 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -668,7 +668,7 @@ void SV_DropClient (client_t *drop) else if (ISQ3CLIENT(drop)) { } - if (drop->netchan.remote_address.type != NA_INVALID) + if (drop->netchan.remote_address.type != NA_INVALID && drop->netchan.message.maxsize) { //send twice, to cover packetloss a little. Netchan_Transmit (&drop->netchan, termmsg.cursize, termmsg.data, 10000); @@ -1958,7 +1958,7 @@ client_t *SV_AddSplit(client_t *controller, char *info, int id) prev->controlled = cl; prev = cl; - cl->controller = prev->controller?prev->controller:host_client; + cl->controller = controller; cl->controlled = NULL; Q_strncpyS (cl->userinfo, info, sizeof(cl->userinfo)-1); diff --git a/engine/server/sv_user.c b/engine/server/sv_user.c index 12cd38429..3f131cf47 100644 --- a/engine/server/sv_user.c +++ b/engine/server/sv_user.c @@ -4653,7 +4653,7 @@ void Cmd_Join_f (void) if (host_client->state != cs_spawned) return; - if (svs.gametype != GT_PROGS) + if (svs.gametype != GT_PROGS && svs.gametype != GT_Q1QVM) { SV_TPrintToClient(host_client, PRINT_HIGH, "Sorry, not implemented in this gamecode type. Try moaning at the dev team\n"); return; @@ -4714,7 +4714,9 @@ void Cmd_Join_f (void) // call the prog function for removing a client // this will set the body to a dead frame, among other things pr_global_struct->self = EDICT_TO_PROG(svprogfuncs, sv_player); - if (SpectatorDisconnect) + if (svs.gametype == GT_Q1QVM) + Q1QVM_DropClient(host_client); + else if (SpectatorDisconnect) PR_ExecuteProgram (svprogfuncs, SpectatorDisconnect); sv.spawned_observer_slots--; @@ -4727,7 +4729,9 @@ void Cmd_Join_f (void) // FIXME, bump the client's userid? // call the progs to get default spawn parms for the new client - if (pr_global_ptrs->SetNewParms) + if (svs.gametype == GT_Q1QVM) + Q1QVM_SetNewParms(); + else if (pr_global_ptrs->SetNewParms) PR_ExecuteProgram (svprogfuncs, pr_global_struct->SetNewParms); for (i=0 ; ispawn_parms[i] = 0; } - // call the spawn function - pr_global_struct->time = sv.world.physicstime; - pr_global_struct->self = EDICT_TO_PROG(svprogfuncs, sv_player); - PR_ExecuteProgram (svprogfuncs, pr_global_struct->ClientConnect); + if (svs.gametype == GT_Q1QVM) + Q1QVM_ClientConnect(host_client); + else + { + // call the spawn function + pr_global_struct->time = sv.world.physicstime; + pr_global_struct->self = EDICT_TO_PROG(svprogfuncs, sv_player); + PR_ExecuteProgram (svprogfuncs, pr_global_struct->ClientConnect); - // actually spawn the player - pr_global_struct->time = sv.world.physicstime; - pr_global_struct->self = EDICT_TO_PROG(svprogfuncs, sv_player); - PR_ExecuteProgram (svprogfuncs, pr_global_struct->PutClientInServer); + // actually spawn the player + pr_global_struct->time = sv.world.physicstime; + pr_global_struct->self = EDICT_TO_PROG(svprogfuncs, sv_player); + PR_ExecuteProgram (svprogfuncs, pr_global_struct->PutClientInServer); + } sv.spawned_client_slots++; // send notification to all clients @@ -4781,7 +4790,7 @@ void Cmd_Observe_f (void) if (host_client->state != cs_spawned) return; - if (svs.gametype != GT_PROGS) + if (svs.gametype != GT_PROGS && svs.gametype != GT_Q1QVM) { SV_TPrintToClient(host_client, PRINT_HIGH, "Sorry, not implemented in this gamecode type. Try moaning at the dev team\n"); return; @@ -4835,8 +4844,13 @@ void Cmd_Observe_f (void) // call the prog function for removing a client // this will set the body to a dead frame, among other things - pr_global_struct->self = EDICT_TO_PROG(svprogfuncs, sv_player); - PR_ExecuteProgram (svprogfuncs, pr_global_struct->ClientDisconnect); + if (svs.gametype == GT_Q1QVM) + Q1QVM_DropClient(host_client); + else + { + pr_global_struct->self = EDICT_TO_PROG(svprogfuncs, sv_player); + PR_ExecuteProgram (svprogfuncs, pr_global_struct->ClientDisconnect); + } sv.spawned_client_slots--; SV_SetUpClientEdict (host_client, host_client->edict); @@ -4848,7 +4862,9 @@ void Cmd_Observe_f (void) // FIXME, bump the client's userid? // call the progs to get default spawn parms for the new client - if (pr_global_ptrs->SetNewParms) + if (svs.gametype == GT_Q1QVM) + Q1QVM_SetNewParms(); + else if (pr_global_ptrs->SetNewParms) PR_ExecuteProgram (svprogfuncs, pr_global_struct->SetNewParms); for (i=0 ; itime = sv.world.physicstime; - pr_global_struct->self = EDICT_TO_PROG(svprogfuncs, sv_player); - PR_ExecuteProgram (svprogfuncs, SpectatorConnect); - } + if (svs.gametype == GT_Q1QVM) + Q1QVM_ClientConnect(host_client); else { - sv_player->v->movetype = MOVETYPE_NOCLIP; - sv_player->v->model = 0; - sv_player->v->modelindex = 0; + if (SpectatorConnect) + { + pr_global_struct->time = sv.world.physicstime; + pr_global_struct->self = EDICT_TO_PROG(svprogfuncs, sv_player); + PR_ExecuteProgram (svprogfuncs, SpectatorConnect); + } + else + { + sv_player->v->movetype = MOVETYPE_NOCLIP; + sv_player->v->model = 0; + sv_player->v->modelindex = 0; + } } sv.spawned_observer_slots++; diff --git a/plugins/ezhud/ezquakeisms.c b/plugins/ezhud/ezquakeisms.c index 517bd82f9..9c48007de 100644 --- a/plugins/ezhud/ezquakeisms.c +++ b/plugins/ezhud/ezquakeisms.c @@ -252,6 +252,42 @@ char *TP_ParseFunChars(char *str, qbool chat) *out++ = (dehex(str[2]) << 4) | dehex(str[3]); str+=4; } + else if (str[0] == '$') + { + int c = 0; + switch (str[1]) + { + case '\\': c = 0x0D; break; + case ':': c = 0x0A; break; + case '[': c = 0x10; break; + case ']': c = 0x11; break; + case 'G': c = 0x86; break; + case 'R': c = 0x87; break; + case 'Y': c = 0x88; break; + case 'B': c = 0x89; break; + case '(': c = 0x80; break; + case '=': c = 0x81; break; + case ')': c = 0x82; break; + case 'a': c = 0x83; break; + case '<': c = 0x1d; break; + case '-': c = 0x1e; break; + case '>': c = 0x1f; break; + case ',': c = 0x1c; break; + case '.': c = 0x9c; break; + case 'b': c = 0x8b; break; + case 'c': + case 'd': c = 0x8d; break; + case '$': c = '$'; break; + case '^': c = '^'; break; + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9': c = str[1] -'0' + 0x12;break; + } + if (c) + { + *out++ = c; + str++; + } + str++; + } else if (*str) *out++ = *str++; else