fteqw/engine/qclib/pr_multi.c
Spoike 6d36834f8e Reworked client support for DPP5+. less code now, its much more graceful.
added waterfog command. waterfog overrides regular fog only when the view is in water.
fixed 64bit printf format specifiers. should work better on winxp64.
fixed some spec angle weirdness.
fixed viewsize 99.99 weirdness with ezhud.
fixed extra offset on the console (exhibited in 64bit builds, but not limited to).
fixed .avi playback, can now actually display frames again.
reimplemented line sparks.
fixed r_editlights_save flipping the light's pitch.
fixed issue with oggs failing to load.
fixed condump to cope with unicode properly.
made sv_bigcoords default except in quake. hexen2 kinda needs it for bsp angle precision.
fixed nq server to not stall weirdly on map changes.
fixed qwprogs svc_cdtrack not bugging out with nq clients on the server.
fixed restart command to load the last map run by the server, instead of start.bsp (when idle)
optimised d3d9 renderer a little. now uses less draw calls, especially with complex scenes. seems to get higher framerates than opengl now.
fixed d3d9 renderer to not bug out quite so much when run fullscreen (shader subsystem is now correctly initialised).
fixed a couple of bugs from font change. also now supports utf-8 in a few more places.
r_editlights_reload no longer generates rtlights inside the void. this resolves a few glitches (but should also help framerates a little).
fixed so corona-only lights won't generate shadowmaps and waste lots of time.
removed lots of #defines from qclib. I should never have made them in the first place, but I was lazy. obviously there's more left that I cba to remove yet.
fixed nested calls with variant-vectors. this fixes csaddon's light editor.
fixed qcc hc calling conventions using redundant stores.
disabled keywords can still be used by using __keyword instead.
fixed ftegccgui grep feature.
fixed motionless-dog qcc bug.
tweaked qcc warnings a little. -Wall is now a viable setting. you should be able to fix all those warnings.
fixed qw svc_intermission + dpp5+ clients bug.
fixed annoying spam about disconnecting in hexen2.
rewrote status command a little to cope with ipv6 addresses more gracefully
fixed significant stall when hibernating/debugging a server with a player sitting on it.
fixed truelightning.
fixed rocketlight overriding pflags.
fixed torches vanishing on vid_restart.
fixed issue with decal scaling.
fixed findentityfield builtin.
fixed fteqcc issue with ptr+1
fixed use of arrays inside class functions.
fixed/implemented fteqcc emulation of pointer opcodes.
added __inout keyword to fteqcc, so that it doesn't feel so horrendous.
fixed sizeof(*foo)
fixed *struct = struct;
fixed recursive structs.
fixed fteqcc warning report.
fixed sdl2 controller support, hopefully.
attempted to implement xinput, including per-player audio playback.
slightly fixed relaxed attitude to mouse focus when running fullscreen.
fixed weird warnings/errors with 'ent.arrayhead' terms. now generates sane errors.
implemented bindmaps (for csqc).
fixed crashing bug with eprint builtin.
implemented subset of music_playlist_* functionality. significant changes to music playback.
fixed some more dpcsqc compat.
fixed binds menu. now displays and accepts modifiers.
fixed issues with huge lightmaps.
fixed protocol determinism with dp clients connecting to fte servers. the initial getchallenge request now inhibits vanilla nq connection requests.
implemented support for 'dupe' userinfo key, allowing clients to request client->server packet duplication. should probably queue them tbh.
implemented sv_saveentfile command.
fixed resume after breaking inside a stepped-over function.
fixed erroneous footer after debugging.
(I wonder just how many things I broke with these fixes)

git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@4946 fc73d0e0-1445-4013-8a0c-d673dee63da5
2015-07-26 10:56:18 +00:00

491 lines
14 KiB
C

#define PROGSUSED
#include "progsint.h"
//#define MAPPING_DEBUG
//#define MAPPING_PARANOID //may actually break unions, so beware.
/*
progstate_t *pr_progstate;
progsnum_t pr_typecurrent;
int maxprogs;
progstate_t *current_progstate;
int numshares;
sharedvar_t *shares; //shared globals, not including parms
int maxshares;
*/
//switches progs without preserving parms/ret/shared
pbool PR_SwitchProgs(progfuncs_t *progfuncs, progsnum_t type)
{
if ((unsigned)type >= prinst.maxprogs)
{
if (type == -1)
{
prinst.pr_typecurrent = -1;
current_progstate = NULL;
return true;
}
PR_RunError(&progfuncs->funcs, "QCLIB: Bad prog type - %i", type);
// Sys_Error("Bad prog type - %i", type);
}
if (pr_progstate[(unsigned)type].progs == NULL) //we havn't loaded it yet, for some reason
return false;
current_progstate = &pr_progstate[(unsigned)type];
prinst.pr_typecurrent = type;
return true;
}
//switch to new progs, preserving all arguments. oldpr should be 'pr_typecurrent'
pbool PR_SwitchProgsParms(progfuncs_t *progfuncs, progsnum_t newpr) //from 2 to 1
{
unsigned int a;
progstate_t *np;
progstate_t *op;
int oldpr = prinst.pr_typecurrent;
if (newpr == oldpr)
{
//don't bother coping variables to themselves...
return true;
}
np = &pr_progstate[(int)newpr];
op = &pr_progstate[(int)oldpr];
if ((unsigned)newpr >= prinst.maxprogs || !np->globals)
{
printf("QCLIB: Bad prog type - %i", newpr);
return false;
}
if ((unsigned)oldpr >= prinst.maxprogs || !op->globals) //startup?
return PR_SwitchProgs(progfuncs, newpr);
//copy parms.
for (a = 0; a < MAX_PARMS;a++)
{
*(int *)&np->globals[OFS_PARM0+3*a ] = *(int *)&op->globals[OFS_PARM0+3*a ];
*(int *)&np->globals[OFS_PARM0+3*a+1] = *(int *)&op->globals[OFS_PARM0+3*a+1];
*(int *)&np->globals[OFS_PARM0+3*a+2] = *(int *)&op->globals[OFS_PARM0+3*a+2];
}
np->globals[OFS_RETURN] = op->globals[OFS_RETURN];
np->globals[OFS_RETURN+1] = op->globals[OFS_RETURN+1];
np->globals[OFS_RETURN+2] = op->globals[OFS_RETURN+2];
//move the vars defined as shared.
for (a = 0; a < prinst.numshares; a++)//fixme: make offset per progs
{
memmove(&((int *)np->globals)[prinst.shares[a].varofs], &((int *)op->globals)[prinst.shares[a].varofs], prinst.shares[a].size*4);
/* ((int *)p1->globals)[shares[a].varofs] = ((int *)p2->globals)[shares[a].varofs];
if (shares[a].size > 1)
{
((int *)p1->globals)[shares[a].varofs+1] = ((int *)p2->globals)[shares[a].varofs+1];
if (shares[a].size > 2)
((int *)p1->globals)[shares[a].varofs+2] = ((int *)p2->globals)[shares[a].varofs+2];
}
*/
}
return PR_SwitchProgs(progfuncs, newpr);
}
progsnum_t PDECL PR_LoadProgs(pubprogfuncs_t *ppf, const char *s)
{
progfuncs_t *progfuncs = (progfuncs_t*)ppf;
unsigned int a;
progsnum_t oldtype;
oldtype = prinst.pr_typecurrent;
for (a = 0; a < prinst.maxprogs; a++)
{
if (pr_progstate[a].progs == NULL)
{
prinst.pr_typecurrent = a;
current_progstate = &pr_progstate[a];
if (PR_ReallyLoadProgs(progfuncs, s, &pr_progstate[a], false)) //try and load it
{
if (a <= progfuncs->funcs.numprogs)
progfuncs->funcs.numprogs = a+1;
#ifdef QCJIT
current_progstate->jit = PR_GenerateJit(progfuncs);
#endif
if (oldtype != -1)
PR_SwitchProgs(progfuncs, oldtype);
return a; //we could load it. Yay!
}
PR_SwitchProgs(progfuncs, oldtype);
return -1; // loading failed.
}
}
PR_SwitchProgs(progfuncs, oldtype);
return -1;
}
void PR_ShiftParms(progfuncs_t *progfuncs, int amount)
{
int a;
for (a = 0; a < MAX_PARMS - amount;a++)
*(int *)&pr_globals[OFS_PARM0+3*a] = *(int *)&pr_globals[OFS_PARM0+3*(amount+a)];
}
//forget a progs
void PR_Clear(progfuncs_t *progfuncs)
{
unsigned int a;
for (a = 0; a < prinst.maxprogs; a++)
{
#ifdef QCJIT
if (pr_progstate[a].jit)
PR_CloseJit(pr_progstate[a].jit);
#endif
pr_progstate[a].progs = NULL;
}
}
void QC_StartShares(progfuncs_t *progfuncs)
{
prinst.numshares = 0;
prinst.maxshares = 32;
if (prinst.shares)
externs->memfree(prinst.shares);
prinst.shares = externs->memalloc(sizeof(sharedvar_t)*prinst.maxshares);
}
void PDECL QC_AddSharedVar(pubprogfuncs_t *ppf, int start, int size) //fixme: make offset per progs and optional
{
progfuncs_t *progfuncs = (progfuncs_t*)ppf;
int ofs;
unsigned int a;
if (prinst.numshares >= prinst.maxshares)
{
void *buf;
buf = prinst.shares;
prinst.maxshares += 16;
prinst.shares = externs->memalloc(sizeof(sharedvar_t)*prinst.maxshares);
memcpy(prinst.shares, buf, sizeof(sharedvar_t)*prinst.numshares);
externs->memfree(buf);
}
ofs = start;
for (a = 0; a < prinst.numshares; a++)
{
if (prinst.shares[a].varofs+prinst.shares[a].size == ofs)
{
prinst.shares[a].size += size; //expand size.
return;
}
if (prinst.shares[a].varofs == start)
return;
}
prinst.shares[prinst.numshares].varofs = start;
prinst.shares[prinst.numshares].size = size;
prinst.numshares++;
}
//void ShowWatch(void);
void QC_InitShares(progfuncs_t *progfuncs)
{
// ShowWatch();
if (!prinst.field) //don't make it so we will just need to remalloc everything
{
prinst.maxfields = 64;
prinst.field = externs->memalloc(sizeof(fdef_t) * prinst.maxfields);
}
prinst.numfields = 0;
progfuncs->funcs.fieldadjust = 0;
}
void QC_FlushProgsOffsets(progfuncs_t *progfuncs)
{ //sets the fields up for loading a new progs.
//fields are matched by name to other progs
//not by offset
unsigned int i;
for (i = 0; i < prinst.numfields; i++)
prinst.field[i].progsofs = -1;
}
//called if a global is defined as a field
//returns offset.
//vectors must be added before any of their corresponding _x/y/z vars
//in this way, even screwed up progs work.
//requestedpos is the offset the engine WILL put it at.
//origionaloffs is used to track matching field offsets. fields with the same progs offset overlap
//note: we probably suffer from progs with renamed system globals.
int PDECL QC_RegisterFieldVar(pubprogfuncs_t *ppf, unsigned int type, char *name, signed long engineofs, signed long progsofs)
{
progfuncs_t *progfuncs = (progfuncs_t*)ppf;
// progstate_t *p;
// int pnum;
unsigned int i;
int namelen;
int ofs;
int fnum;
if (!name) //engine can use this to offset all progs fields
{ //which fixes constant field offsets (some ktpro arrays)
progfuncs->funcs.fieldadjust = prinst.fields_size/4;
#ifdef MAPPING_DEBUG
printf("FIELD ADJUST: %i %i %i\n", progfuncs->funcs.fieldadjust, prinst.fields_size, (int)prinst.fields_size/4);
#endif
return 0;
}
prinst.reorganisefields = true;
//look for an existing match
for (i = 0; i < prinst.numfields; i++)
{
if (!strcmp(name, prinst.field[i].name))
{
if (prinst.field[i].type != type)
{
/*Hexen2/DP compat hack: if the new type is a float and the original type is a vector, make the new def alias to the engine's _x field
this 'works around' the unused .vector color field used for rtlight colours vs the .float color used for particle colours (the float initialisers in map files will expand into the x slot safely).
qc/hc can work around this by just using .vector color/color_x instead, which is the same as this hack, but would resolve defs to allow rtlight colours.
*/
if (prinst.field[i].type != ev_vector || type != ev_float)
{
printf("Field type mismatch on \"%s\". %i != %i\n", name, prinst.field[i].type, type);
continue;
}
}
if (!progfuncs->funcs.fieldadjust && engineofs>=0)
if ((unsigned)engineofs/4 != prinst.field[i].ofs)
Sys_Error("Field %s at wrong offset", name);
if (prinst.field[i].progsofs == -1)
prinst.field[i].progsofs = progsofs;
#ifdef MAPPING_DEBUG
printf("Dupfield %s %i -> %i\n", name, prinst.field[i].progsofs,prinst.field[i].ofs);
#endif
return prinst.field[i].ofs-progfuncs->funcs.fieldadjust; //got a match
}
}
if (prinst.numfields+1>prinst.maxfields)
{
fdef_t *nf;
i = prinst.maxfields;
prinst.maxfields += 32;
nf = externs->memalloc(sizeof(fdef_t) * prinst.maxfields);
memcpy(nf, prinst.field, sizeof(fdef_t) * i);
externs->memfree(prinst.field);
prinst.field = nf;
}
//try to add a new one
fnum = prinst.numfields;
prinst.numfields++;
prinst.field[fnum].name = name;
prinst.field[fnum].type = type;
prinst.field[fnum].progsofs = progsofs;
if (engineofs >= 0)
{ //the engine is setting up a list of required field indexes.
//paranoid checking of the offset.
#if 0//def MAPPING_PARANOID
for (i = 0; i < numfields-1; i++)
{
if (field[i].ofs == ((unsigned)engineofs)/4)
{
if (type == ev_float && field[i].type == ev_vector) //check names
{
if (strncmp(field[i].name, name, strlen(field[i].name)))
Sys_Error("Duplicated offset");
}
else
Sys_Error("Duplicated offset");
}
}
#endif
if (engineofs&3)
Sys_Error("field %s is %i&3", name, (int)engineofs);
prinst.field[fnum].ofs = ofs = engineofs/4;
}
else
{ //we just found a new fieldname inside a progs
prinst.field[fnum].ofs = ofs = prinst.fields_size/4; //add on the end
//if the progs field offset matches annother offset in the same progs, make it match up with the earlier one.
if (progsofs>=0)
{
unsigned otherofs;
for (i = 0; i < prinst.numfields-1; i++)
{
otherofs = prinst.field[i].progsofs;
if (otherofs == -1) //qc unions work purely by progs offsets, not by engine offsets.
continue; //this is because there really is no reliable mapping between progs and engine, so don't get confused.
if (otherofs == (unsigned)progsofs)
{
#ifdef MAPPING_DEBUG
printf("union(%s) ", prinst.field[i].name);
#endif
prinst.field[fnum].ofs = ofs = prinst.field[i].ofs;
break;
}
if (prinst.field[i].type == ev_vector && otherofs+1 == (unsigned)progsofs)
{
#ifdef MAPPING_DEBUG
printf("union(%s) ", prinst.field[i].name);
#endif
prinst.field[fnum].ofs = ofs = prinst.field[i].ofs+1;
break;
}
if (prinst.field[i].type == ev_vector && otherofs+2 == (unsigned)progsofs)
{
#ifdef MAPPING_DEBUG
printf("union(%s) ", prinst.field[i].name);
#endif
prinst.field[fnum].ofs = ofs = prinst.field[i].ofs+2;
break;
}
}
}
}
// if (type != ev_vector)
if (prinst.fields_size < (ofs+type_size[type])*4)
prinst.fields_size = (ofs+type_size[type])*4;
if (prinst.max_fields_size && prinst.fields_size > prinst.max_fields_size)
Sys_Error("Allocated too many additional fields after ents were inited.");
#ifdef MAPPING_DEBUG
printf("Field %s %i -> %i\n", name, prinst.field[fnum].progsofs,prinst.field[fnum].ofs);
#endif
if (type == ev_vector)
{
//vectors define float fields too. this avoids issues if the mod has (pointless) anti-decompile field reordering/stripping that works thanks to decompilers expecting things to be ordered exactly..
char *n;
namelen = strlen(name)+5;
n=PRHunkAlloc(progfuncs, namelen, "str");
sprintf(n, "%s_x", name);
ofs = QC_RegisterFieldVar(&progfuncs->funcs, ev_float, n, engineofs, progsofs);
n=PRHunkAlloc(progfuncs, namelen, "str");
sprintf(n, "%s_y", name);
QC_RegisterFieldVar(&progfuncs->funcs, ev_float, n, (engineofs==-1)?-1:(engineofs+4), (progsofs==-1)?-1:progsofs+1);
n=PRHunkAlloc(progfuncs, namelen, "str");
sprintf(n, "%s_z", name);
QC_RegisterFieldVar(&progfuncs->funcs, ev_float, n, (engineofs==-1)?-1:(engineofs+8), (progsofs==-1)?-1:progsofs+2);
}
//we've finished setting the structure
return ofs - progfuncs->funcs.fieldadjust;
}
//called if a global is defined as a field
void PDECL QC_AddSharedFieldVar(pubprogfuncs_t *ppf, int num, char *stringtable)
{
progfuncs_t *progfuncs = (progfuncs_t*)ppf;
// progstate_t *p;
// int pnum;
unsigned int i, o;
//look for an existing match not needed, cos we look a little later too.
/*
for (i = 0; i < numfields; i++)
{
if (!strcmp(pr_globaldefs[num].s_name, field[i].s_name))
{
//really we should look for a field def
*(int *)&pr_globals[pr_globaldefs[num].ofs] = field[i].ofs; //got a match
return;
}
}
*/
switch(current_progstate->structtype)
{
case PST_KKQWSV:
case PST_DEFAULT:
for (i=1 ; i<pr_progs->numfielddefs; i++)
{
if (!strcmp(pr_fielddefs16[i].s_name+stringtable, pr_globaldefs16[num].s_name+stringtable))
{
#ifdef MAPPING_DEBUG
int old = *(int *)&pr_globals[pr_globaldefs16[num].ofs];
#endif
*(int *)&pr_globals[pr_globaldefs16[num].ofs] = QC_RegisterFieldVar(&progfuncs->funcs, pr_fielddefs16[i].type, pr_globaldefs16[num].s_name+stringtable, -1, *(int *)&pr_globals[pr_globaldefs16[num].ofs]);
#ifdef MAPPING_DEBUG
printf("Field=%s global %i -> %i\n", pr_globaldefs16[num].s_name+stringtable, old, *(volatile int *)&pr_globals[pr_globaldefs16[num].ofs]);
#endif
return;
}
}
for (i = 0; i < prinst.numfields; i++)
{
o = prinst.field[i].progsofs;
if (o == *(unsigned int *)&pr_globals[pr_globaldefs16[num].ofs])
{
#ifdef MAPPING_DEBUG
int old = *(int *)&pr_globals[pr_globaldefs16[num].ofs];
#endif
*(int *)&pr_globals[pr_globaldefs16[num].ofs] = prinst.field[i].ofs-progfuncs->funcs.fieldadjust;
#ifdef MAPPING_DEBUG
printf("Field global=%s %i -> %i\n", pr_globaldefs16[num].s_name+stringtable, old, *(volatile int *)&pr_globals[pr_globaldefs16[num].ofs]);
#endif
return;
}
}
//oh well, must be a parameter.
// if (*(int *)&pr_globals[pr_globaldefs16[num].ofs])
// Sys_Error("QCLIB: Global field var with no matching field \"%s\", from offset %i", pr_globaldefs16[num].s_name+stringtable, *(int *)&pr_globals[pr_globaldefs16[num].ofs]);
return;
case PST_FTE32:
case PST_QTEST:
for (i=1 ; i<pr_progs->numfielddefs; i++)
{
if (!strcmp(pr_fielddefs32[i].s_name+stringtable, pr_globaldefs32[num].s_name+stringtable))
{
*(int *)&pr_globals[pr_globaldefs32[num].ofs] = QC_RegisterFieldVar(&progfuncs->funcs, pr_fielddefs32[i].type, pr_globaldefs32[num].s_name+stringtable, -1, *(int *)&pr_globals[pr_globaldefs32[num].ofs]);
return;
}
}
for (i = 0; i < prinst.numfields; i++)
{
o = prinst.field[i].progsofs;
if (o == *(unsigned int *)&pr_globals[pr_globaldefs32[num].ofs])
{
*(int *)&pr_globals[pr_globaldefs32[num].ofs] = prinst.field[i].ofs-progfuncs->funcs.fieldadjust;
return;
}
}
//oh well, must be a parameter.
if (*(int *)&pr_globals[pr_globaldefs32[num].ofs])
Sys_Error("QCLIB: Global field var with no matching field \"%s\", from offset %i", pr_globaldefs32[num].s_name+stringtable, *(int *)&pr_globals[pr_globaldefs32[num].ofs]);
return;
default:
Sys_Error("Bad bits");
break;
}
Sys_Error("Should be unreachable");
}