1
0
Fork 0
forked from fte/fteqw
fteqw/engine/server/svhl_game.c

1913 lines
46 KiB
C
Raw Normal View History

#include "quakedef.h"
/*
I think globals.maxentities is the hard cap, rather than current max like in q1.
*/
#ifdef HLSERVER
#include "winquake.h"
#include "svhl_gcapi.h"
#include "pr_common.h"
#include "crc.h"
#include "model_hl.h"
#if defined(_MSC_VER)
#if _MSC_VER >= 1300
#define __func__ __FUNCTION__
#else
#define __func__ "unknown"
#endif
#else
//I hope you're c99 and have a __func__
#endif
extern cvar_t temp1;
#define ignore(s) Con_DPrintf("Fixme: " s "\n")
#define notimpl(l) Con_Printf("halflife sv builtin not implemented on line %i\n", l)
#define notimpf(f) Con_Printf("halflife sv builtin %s not implemented\n", f)
#define bi_begin() if (temp1.ival)Con_Printf("enter %s\n", __func__)
#define bi_end() if (temp1.ival)Con_Printf("leave %s\n", __func__)
#define bi_trace() bi_begin(); bi_end()
dllhandle_t *hlgamecode;
SVHL_Globals_t SVHL_Globals;
SVHL_GameFuncs_t SVHL_GameFuncs;
static zonegroup_t hlmapmemgroup; //flushed at end-of-map.
#define MAX_HL_EDICTS 2048
hledict_t *SVHL_Edict;
int SVHL_NumActiveEnts;
int lastusermessage;
string_t QDECL GHL_AllocString(char *string)
{
char *news;
bi_begin();
if (!string)
return 0;
news = ZG_Malloc(&hlmapmemgroup, strlen(string)+1);
memcpy(news, string, strlen(string)+1);
bi_end();
return news - SVHL_Globals.stringbase;
}
int QDECL GHL_PrecacheModel(char *name)
{
int i;
bi_trace();
if (name[0] <= ' ')
{
Con_Printf ("precache_model: empty string\n");
return 0;
}
for (i=1 ; i<MAX_MODELS ; i++)
{
if (!sv.strings.model_precache[i])
{
if (strlen(name)>=MAX_QPATH-1) //probably safest to keep this.
{
SV_Error ("Precache name too long");
return 0;
}
name = sv.strings.model_precache[i] = SVHL_Globals.stringbase+GHL_AllocString(name);
if (!strcmp(name + strlen(name) - 4, ".bsp"))
sv.models[i] = Mod_FindName(name);
if (sv.state != ss_loading)
{
Con_DPrintf("Delayed model precache: %s\n", name);
MSG_WriteByte(&sv.reliable_datagram, svcfte_precache);
MSG_WriteShort(&sv.reliable_datagram, i);
MSG_WriteString(&sv.reliable_datagram, name);
#ifdef NQPROT
MSG_WriteByte(&sv.nqreliable_datagram, svcdp_precache);
MSG_WriteShort(&sv.nqreliable_datagram, i);
MSG_WriteString(&sv.nqreliable_datagram, name);
#endif
}
return i;
}
if (!strcmp(sv.strings.model_precache[i], name))
{
return i;
}
}
SV_Error ("GHL_precache_model: overflow");
return 0;
}
int QDECL GHL_PrecacheSound(char *name)
{
int i;
bi_trace();
if (name[0] <= ' ')
{
Con_Printf ("precache_sound: empty string\n");
return 0;
}
for (i=1 ; i<MAX_SOUNDS ; i++)
{
if (!*sv.strings.sound_precache[i])
{
if (strlen(name)>=MAX_QPATH-1) //probably safest to keep this.
{
SV_Error ("Precache name too long");
return 0;
}
strcpy(sv.strings.sound_precache[i], name);
name = sv.strings.sound_precache[i];
if (sv.state != ss_loading)
{
Con_DPrintf("Delayed sound precache: %s\n", name);
MSG_WriteByte(&sv.reliable_datagram, svcfte_precache);
MSG_WriteShort(&sv.reliable_datagram, -i);
MSG_WriteString(&sv.reliable_datagram, name);
#ifdef NQPROT
MSG_WriteByte(&sv.nqreliable_datagram, svcdp_precache);
MSG_WriteShort(&sv.nqreliable_datagram, -i);
MSG_WriteString(&sv.nqreliable_datagram, name);
#endif
}
return i;
}
if (!strcmp(sv.strings.sound_precache[i], name))
{
return i;
}
}
SV_Error ("GHL_precache_sound: overflow");
return 0;
}
void QDECL GHL_SetModel(hledict_t *ed, char *modelname)
{
model_t *mod;
int mdlidx = GHL_PrecacheModel(modelname);
bi_trace();
ed->v.modelindex = mdlidx;
ed->v.model = sv.strings.model_precache[mdlidx] - SVHL_Globals.stringbase;
mod = sv.models[mdlidx];
if (mod)
{
VectorCopy(mod->mins, ed->v.mins);
VectorCopy(mod->maxs, ed->v.maxs);
VectorSubtract(mod->maxs, mod->mins, ed->v.size);
}
SVHL_LinkEdict(ed, false);
}
unk QDECL GHL_ModelIndex(unk){notimpf(__func__);}
int QDECL GHL_ModelFrames(int midx)
{
bi_trace();
//returns the number of frames(sequences I assume) this model has
ignore("ModelFrames");
return 1;
}
void QDECL GHL_SetSize(hledict_t *ed, float *mins, float *maxs)
{
bi_trace();
VectorCopy(mins, ed->v.mins);
VectorCopy(maxs, ed->v.maxs);
SVHL_LinkEdict(ed, false);
}
void QDECL GHL_ChangeLevel(char *nextmap, char *startspot)
{
bi_trace();
Cbuf_AddText(va("changelevel %s %s@%f@%f@%f\n", nextmap, startspot, SVHL_Globals.landmark[0], SVHL_Globals.landmark[1], SVHL_Globals.landmark[2]), RESTRICT_PROGS);
}
unk QDECL GHL_GetSpawnParms(unk){notimpf(__func__);}
unk QDECL GHL_SaveSpawnParms(unk){notimpf(__func__);}
float QDECL GHL_VecToYaw(float *inv)
{
vec3_t outa;
bi_trace();
VectorAngles(inv, NULL, outa);
return outa[1];
}
void QDECL GHL_VecToAngles(float *inv, float *outa)
{
bi_trace();
VectorAngles(inv, NULL, outa);
}
void QDECL GHL_MoveToOrigin(hledict_t *ent, vec3_t dest, float dist, int moveflags)
{
bi_trace();
ignore("GHL_MoveToOrigin");
}
unk QDECL GHL_ChangeYaw(unk){notimpf(__func__);}
unk QDECL GHL_ChangePitch(unk){notimpf(__func__);}
hledict_t *QDECL GHL_FindEntityByString(hledict_t *last, char *field, char *value)
{
hledict_t *ent;
int i;
int ofs;
string_t str;
bi_trace();
if (!strcmp(field, "targetname"))
ofs = (char*)&((hledict_t *)NULL)->v.targetname - (char*)NULL;
else if (!strcmp(field, "classname"))
ofs = (char*)&((hledict_t *)NULL)->v.classname - (char*)NULL;
else
{
Con_Printf("Fixme: Look for %s=%s\n", field, value);
return NULL;
}
if (last)
i=last-SVHL_Edict+1;
else
i = 0;
for (; i<SVHL_Globals.maxentities; i++)
{
ent = &SVHL_Edict[i];
if (ent->isfree)
continue;
str = *(string_t*)((char*)ent+ofs);
if (str && !strcmp(SVHL_Globals.stringbase+str, value))
return ent;
}
return SVHL_Edict;
}
unk QDECL GHL_GetEntityIllum(unk){notimpf(__func__);}
hledict_t *QDECL GHL_FindEntityInSphere(hledict_t *last, float *org, float radius)
{
int i, j;
vec3_t eorg;
hledict_t *ent;
bi_trace();
radius = radius*radius;
if (last)
i=last-SVHL_Edict+1;
else
i = 0;
for (; i<SVHL_Globals.maxentities; i++)
{
ent = &SVHL_Edict[i];
if (ent->isfree)
continue;
if (!ent->v.solid)
continue;
for (j=0; j<3; j++)
eorg[j] = org[j] - (ent->v.origin[j] + (ent->v.mins[j] + ent->v.maxs[j])*0.5);
if (DotProduct(eorg,eorg) > radius)
continue;
//its close enough
return ent;
}
return NULL;
}
hledict_t *QDECL GHL_FindClientInPVS(hledict_t *ed)
{
qbyte *viewerpvs;
int best = 0, i;
float bestdist = 99999999; //HL maps are limited in size anyway
float d;
int leafnum;
vec3_t ofs;
hledict_t *other;
bi_trace();
//fixme: we need to track some state
//a different client should be returned each call _per ent_ (so it can be used once per frame)
viewerpvs = sv.world.worldmodel->funcs.LeafPVS(sv.world.worldmodel, sv.world.worldmodel->funcs.LeafnumForPoint(sv.world.worldmodel, ed->v.origin), NULL, 0);
for (i = 0; i < svs.allocated_client_slots; i++)
{
if (svs.clients[i].state == cs_spawned)
{
other = &SVHL_Edict[i+1];
if (ed == other)
continue; //ignore yourself.
if (svs.clients[i].spectator)
continue; //ignore spectators
leafnum = sv.world.worldmodel->funcs.LeafnumForPoint(sv.world.worldmodel, other->v.origin)-1;/*pvs is 1 based, leafs are 0 based*/
if (viewerpvs[leafnum>>3] & (1<<(leafnum&7)))
{
VectorSubtract(ed->v.origin, other->v.origin, ofs);
d = DotProduct(ofs, ofs);
if (d < bestdist)
{
bestdist = d;
best = i+1;
}
}
}
}
if (best)
return &SVHL_Edict[best];
return NULL;
}
unk QDECL GHL_EntitiesInPVS(unk){notimpf(__func__);}
void QDECL GHL_MakeVectors(float *angles)
{
bi_trace();
AngleVectors(angles, SVHL_Globals.v_forward, SVHL_Globals.v_right, SVHL_Globals.v_up);
}
void QDECL GHL_AngleVectors(float *angles, float *forward, float *right, float *up)
{
bi_trace();
AngleVectors(angles, forward, right, up);
}
///////////////////////////////////////////////////////////
hledict_t *QDECL GHL_CreateEntity(void)
{
int i;
static int spawnnumber;
bi_trace();
spawnnumber++;
for (i = sv.allocated_client_slots; i < SVHL_NumActiveEnts; i++)
{
if (SVHL_Edict[i].isfree)
{
if (SVHL_Edict[i].freetime > sv.time)
continue;
memset(&SVHL_Edict[i], 0, sizeof(SVHL_Edict[i]));
SVHL_Edict[i].spawnnumber = spawnnumber;
SVHL_Edict[i].v.edict = &SVHL_Edict[i];
return &SVHL_Edict[i];
}
}
if (i < MAX_HL_EDICTS)
{
SVHL_NumActiveEnts++;
memset(&SVHL_Edict[i], 0, sizeof(SVHL_Edict[i]));
SVHL_Edict[i].spawnnumber = spawnnumber;
SVHL_Edict[i].v.edict = &SVHL_Edict[i];
return &SVHL_Edict[i];
}
SV_Error("Ran out of free edicts");
return NULL;
}
void QDECL GHL_RemoveEntity(hledict_t *ed)
{
bi_trace();
SVHL_UnlinkEdict(ed);
ed->isfree = true;
ed->freetime = sv.time+2;
}
hledict_t *QDECL GHL_CreateNamedEntity(string_t name)
{
void (QDECL *spawnfunc)(hlentvars_t *evars);
hledict_t *ed;
bi_trace();
ed = GHL_CreateEntity();
if (!ed)
return NULL;
ed->v.classname = name;
spawnfunc = (void(QDECL *)(hlentvars_t*))GetProcAddress((HINSTANCE)hlgamecode, name+SVHL_Globals.stringbase);
if (spawnfunc)
spawnfunc(&ed->v);
return ed;
}
void *QDECL GHL_PvAllocEntPrivateData(hledict_t *ed, long quant)
{
bi_trace();
if (!ed)
return NULL;
ed->moddata = ZG_Malloc(&hlmapmemgroup, quant);
return ed->moddata;
}
unk QDECL GHL_PvEntPrivateData(unk)
{
bi_trace();
notimpf(__func__);
}
unk QDECL GHL_FreeEntPrivateData(unk)
{
bi_trace();
notimpf(__func__);
}
unk QDECL GHL_GetVarsOfEnt(unk)
{
bi_trace();
notimpf(__func__);
}
hledict_t *QDECL GHL_PEntityOfEntOffset(int ednum)
{
bi_trace();
return (hledict_t *)(ednum + (char*)SVHL_Edict);
}
int QDECL GHL_EntOffsetOfPEntity(hledict_t *ed)
{
bi_trace();
return (char*)ed - (char*)SVHL_Edict;
}
int QDECL GHL_IndexOfEdict(hledict_t *ed)
{
bi_trace();
return ed - SVHL_Edict;
}
hledict_t *QDECL GHL_PEntityOfEntIndex(int idx)
{
bi_trace();
return &SVHL_Edict[idx];
}
unk QDECL GHL_FindEntityByVars(unk)
{
bi_trace();
notimpf(__func__);
}
///////////////////////////////////////////////////////
unk QDECL GHL_MakeStatic(unk){notimpf(__func__);}
unk QDECL GHL_EntIsOnFloor(unk){notimpf(__func__);}
int QDECL GHL_DropToFloor(hledict_t *ed)
{
vec3_t top;
vec3_t bottom;
trace_t tr;
bi_trace();
VectorCopy(ed->v.origin, top);
VectorCopy(ed->v.origin, bottom);
top[2] += 1;
bottom[2] -= 256;
tr = SVHL_Move(top, ed->v.mins, ed->v.maxs, bottom, 0, 0, ed);
VectorCopy(tr.endpos, ed->v.origin);
return tr.fraction != 0 && tr.fraction != 1;
}
int QDECL GHL_WalkMove(hledict_t *ed, float yaw, float dist, int mode)
{
bi_trace();
ignore("walkmove");
return 1;
}
void QDECL GHL_SetOrigin(hledict_t *ed, float *neworg)
{
bi_trace();
VectorCopy(neworg, ed->v.origin);
SVHL_LinkEdict(ed, false);
}
void QDECL GHL_EmitSound(hledict_t *ed, int chan, char *soundname, float vol, float atten, int flags, int pitch)
{
bi_trace();
SV_StartSound(ed-SVHL_Edict, ed->v.origin, ~0, chan, soundname, vol*255, atten, pitch);
}
void QDECL GHL_EmitAmbientSound(hledict_t *ed, float *org, char *soundname, float vol, float atten, unsigned int flags, int pitch)
{
bi_trace();
SV_StartSound(0, org, ~0, 0, soundname, vol*255, atten, 0);
}
void QDECL GHL_TraceLine(float *start, float *end, int flags, hledict_t *ignore, hltraceresult_t *result)
{
trace_t t;
bi_trace();
t = SVHL_Move(start, vec3_origin, vec3_origin, end, flags, 0, ignore);
result->allsolid = t.allsolid;
result->startsolid = t.startsolid;
result->inopen = t.inopen;
result->inwater = t.inwater;
result->fraction = t.fraction;
VectorCopy(t.endpos, result->endpos);
result->planedist = t.plane.dist;
VectorCopy(t.plane.normal, result->planenormal);
result->touched = t.ent;
if (!result->touched)
result->touched = &SVHL_Edict[0];
result->hitgroup = 0;
}
unk QDECL GHL_TraceToss(unk){notimpf(__func__);}
unk QDECL GHL_TraceMonsterHull(unk){notimpf(__func__);}
void QDECL GHL_TraceHull(float *start, float *end, int flags, int hullnum, hledict_t *ignore, hltraceresult_t *result)
{
trace_t t;
bi_trace();
t = SVHL_Move(start, sv.world.worldmodel->hulls[hullnum].clip_mins, sv.world.worldmodel->hulls[hullnum].clip_maxs, end, flags, 0, ignore);
result->allsolid = t.allsolid;
result->startsolid = t.startsolid;
result->inopen = t.inopen;
result->inwater = t.inwater;
result->fraction = t.fraction;
VectorCopy(t.endpos, result->endpos);
result->planedist = t.plane.dist;
VectorCopy(t.plane.normal, result->planenormal);
result->touched = t.ent;
result->hitgroup = 0;
}
unk QDECL GHL_TraceModel(unk){notimpf(__func__);}
char *QDECL GHL_TraceTexture(hledict_t *againstent, vec3_t start, vec3_t end)
{
trace_t tr;
bi_trace();
sv.world.worldmodel->funcs.NativeTrace(sv.world.worldmodel, 0, 0, NULL, start, end, vec3_origin, vec3_origin, MASK_WORLDSOLID, &tr);
return tr.surface->name;
}
unk QDECL GHL_TraceSphere(unk){notimpf(__func__);}
unk QDECL GHL_GetAimVector(unk){notimpf(__func__);}
void QDECL GHL_ServerCommand(char *cmd)
{
bi_trace();
Cbuf_AddText(cmd, RESTRICT_PROGS);
}
void QDECL GHL_ServerExecute(void)
{
bi_trace();
Cbuf_ExecuteLevel(RESTRICT_PROGS);
}
unk QDECL GHL_ClientCommand(unk){notimpf(__func__);}
unk QDECL GHL_ParticleEffect(unk){notimpf(__func__);}
void QDECL GHL_LightStyle(int stylenum, char *stylestr)
{
bi_trace();
PF_applylightstyle(stylenum, stylestr, 7);
}
int QDECL GHL_DecalIndex(char *decalname)
{
bi_trace();
Con_Printf("Fixme: precache decal %s\n", decalname);
return 0;
}
int QDECL GHL_PointContents(float *org)
{
bi_trace();
return Q1CONTENTS_EMPTY;
}
int svhl_messagedest;
vec3_t svhl_messageorigin;
hledict_t *svhl_messageent;
void QDECL GHL_MessageBegin(int dest, int type, float *org, hledict_t *ent)
{
bi_trace();
svhl_messagedest = dest;
if (org)
VectorCopy(org, svhl_messageorigin);
else
VectorClear(svhl_messageorigin);
svhl_messageent = ent;
if (sv.multicast.cursize)
{
Con_Printf("MessageBegin called without MessageEnd\n");
SZ_Clear (&sv.multicast);
}
MSG_WriteByte(&sv.multicast, svcfte_cgamepacket);
MSG_WriteShort(&sv.multicast, 0);
MSG_WriteByte(&sv.multicast, type);
}
void QDECL GHL_MessageEnd(unk)
{
unsigned short len;
client_t *cl;
bi_trace();
if (!sv.multicast.cursize)
{
Con_Printf("MessageEnd called without MessageBegin\n");
return;
}
//update the length
len = sv.multicast.cursize - 3;
sv.multicast.data[1] = len&0xff;
sv.multicast.data[2] = len>>8;
switch(svhl_messagedest)
{
case MSG_BROADCAST:
SZ_Write(&sv.datagram, sv.multicast.data, sv.multicast.cursize);
break;
case MSG_ONE:
cl = &svs.clients[svhl_messageent - SVHL_Edict - 1];
if (cl->state >= cs_spawned)
{
ClientReliableCheckBlock(cl, sv.multicast.cursize);
ClientReliableWrite_SZ(cl, sv.multicast.data, sv.multicast.cursize);
}
break;
case MSG_ALL:
SZ_Write(&sv.reliable_datagram, sv.multicast.data, sv.multicast.cursize);
break;
case MSG_MULTICAST:
SV_Multicast(svhl_messageorigin, MULTICAST_PVS);
break;
case MSG_MULTICAST+1:
SV_Multicast(svhl_messageorigin, MULTICAST_PHS);
break;
default:
Con_Printf("GHL_MessageEnd: dest type %i not supported\n", svhl_messagedest);
break;
}
SZ_Clear (&sv.multicast);
}
void QDECL GHL_WriteByte(int value)
{
bi_trace();
MSG_WriteByte(&sv.multicast, value);
}
void QDECL GHL_WriteChar(int value)
{
bi_trace();
MSG_WriteChar(&sv.multicast, value);
}
void QDECL GHL_WriteShort(int value)
{
bi_trace();
MSG_WriteShort(&sv.multicast, value);
}
void QDECL GHL_WriteLong(int value)
{
bi_trace();
MSG_WriteLong(&sv.multicast, value);
}
void QDECL GHL_WriteAngle(float value)
{
bi_trace();
MSG_WriteAngle8(&sv.multicast, value);
}
void QDECL GHL_WriteCoord(float value)
{
coorddata i = MSG_ToCoord(value, 2);
bi_trace();
SZ_Write (&sv.multicast, (void*)&i, 2);
}
void QDECL GHL_WriteString(char *string)
{
bi_trace();
MSG_WriteString(&sv.multicast, string);
}
void QDECL GHL_WriteEntity(int entnum)
{
bi_trace();
------------------------------------------------------------------------ r4169 | acceptthis | 2013-01-17 08:55:12 +0000 (Thu, 17 Jan 2013) | 31 lines removed MAX_VISEDICTS limit. PEXT2_REPLACEMENTDELTAS tweaked, now has 4 million entity limit. still not enabled by default. TE_BEAM now maps to a separate TEQW_BEAM to avoid conflicts with QW. added android multitouch emulation for windows/rawinput (in_simulatemultitouch). split topcolor/bottomcolor from scoreboard, for dp's colormap|1024 feature. now using utf-8 for windows consoles. qcc warnings/errors now give clickable console links for quick+easy editing. disabled menutint when the currently active item changes contrast or gamma (for OneManClan). Added support for drawfont/drawfontscale. tweaked the qcvm a little to reduce the number of pointers. .doll file loading. still experimental and will likely crash. requires csqc active, even if its a dummy progs. this will be fixed in time. Still other things that need cleaning up. windows: gl_font "?" shows the standard windows font-selection dialog, and can be used to select windows fonts. not all work. and you probably don't want to use windings. fixed splitscreen support when playing mvds. added mini-scoreboards to splitscreen. editor/debugger now shows asm if there's no linenumber info. also, pressing f1 for help shows the shortcuts. Added support for .framegroups files for psk(psa) and iqm formats. True support for ezquake's colour codes. Mutually exclusive with background colours. path command output slightly more readable. added support for digest_hex (MD4, SHA1, CRC16). skingroups now colourmap correctly. Fix terrain colour hints, and litdata from the wrong bsp. fix ftp dual-homed issue. support epsv command, and enable ipv6 (eprt still not supported). remove d3d11 compilation from the makefile. the required headers are not provided by mingw, and are not available to the build bot, so don't bother. fix v *= v.x and similar opcodes. fteqcc: fixed support for áéíóú type chars in names. utf-8 files now properly supported (even with the utf-8 bom/identifier). utf-16 also supported. fteqcc: fixed '#if 1 == 3 && 4' parsing. fteqcc: -Werror acts on the warning, rather than as a separate error. Line numbers are thus more readable. fteqcc: copyright message now includes compile date instead. fteqccgui: the treeview control is now coloured depending on whether there were warnings/errors in the last compile. fteqccgui: the output window is now focused and scrolls down as compilation progresses. pr_dumpplatform command dumps out some pragmas to convert more serious warnings to errors. This is to avoid the infamous 'fteqcc sucks cos my code sucks' issue. rewrote prespawn/modelist/soundlist code. server tracks progress now. ------------------------------------------------------------------------ git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@4167 fc73d0e0-1445-4013-8a0c-d673dee63da5
2013-03-12 22:29:40 +00:00
MSG_WriteEntity(&sv.multicast, entnum);
}
void QDECL GHL_AlertMessage(int level, char *fmt, ...)
{
va_list argptr;
char string[1024];
bi_trace();
if (level == 2 && !developer.ival)
return;
va_start (argptr, fmt);
vsnprintf (string,sizeof(string)-1, fmt,argptr);
va_end (argptr);
Con_Printf("%s\n", string);
}
void QDECL GHL_EngineFprintf(FILE *f, char *fmt, ...)
{
bi_trace();
SV_Error("Halflife gamecode tried to use EngineFprintf\n");
}
unk QDECL GHL_SzFromIndex(unk){notimpf(__func__);}
void *QDECL GHL_GetModelPtr(hledict_t *ed)
{
bi_trace();
#ifdef SERVERONLY
return NULL;
#else
if (!ed->v.modelindex)
return NULL;
if (!sv.models[ed->v.modelindex])
sv.models[ed->v.modelindex] = Mod_ForName(sv.strings.model_precache[ed->v.modelindex], false);
return Mod_GetHalfLifeModelData(sv.models[ed->v.modelindex]);
#endif
}
int QDECL GHL_RegUserMsg(char *msgname, int msgsize)
{
bi_trace();
//we use 1 as the code to choose others.
if (lastusermessage <= 1)
return -1;
SV_FlushSignon ();
//for new clients
MSG_WriteByte(&sv.signon, svcfte_cgamepacket);
MSG_WriteShort(&sv.signon, strlen(msgname)+3);
MSG_WriteByte(&sv.signon, 1);
MSG_WriteByte(&sv.signon, lastusermessage);
MSG_WriteString(&sv.signon, msgname);
//and if the client is already spawned...
MSG_WriteByte(&sv.reliable_datagram, svcfte_cgamepacket);
MSG_WriteShort(&sv.reliable_datagram, strlen(msgname)+3);
MSG_WriteByte(&sv.reliable_datagram, 1);
MSG_WriteByte(&sv.reliable_datagram, lastusermessage);
MSG_WriteString(&sv.reliable_datagram, msgname);
return lastusermessage--;
}
unk QDECL GHL_AnimationAutomove(unk){notimpf(__func__);}
unk QDECL GHL_GetBonePosition(unk){notimpf(__func__);}
hlintptr_t QDECL GHL_FunctionFromName(char *name)
{
bi_trace();
return (hlintptr_t)Sys_GetAddressForName(hlgamecode, name);
}
char *QDECL GHL_NameForFunction(hlintptr_t function)
{
bi_trace();
return Sys_GetNameForAddress(hlgamecode, (void*)function);
}
unk QDECL GHL_ClientPrintf(unk)
{
bi_trace();
// SV_ClientPrintf(
notimpf(__func__);
}
void QDECL GHL_ServerPrint(char *msg)
{
bi_trace();
Con_Printf("%s", msg);
}
char *QDECL GHL_Cmd_Args(void)
{
bi_trace();
return Cmd_Args();
}
char *QDECL GHL_Cmd_Argv(int arg)
{
bi_trace();
return Cmd_Argv(arg);
}
int QDECL GHL_Cmd_Argc(unk)
{
bi_trace();
return Cmd_Argc();
}
unk QDECL GHL_GetAttachment(unk){notimpf(__func__);}
void QDECL GHL_CRC32_Init(hlcrc_t *crc)
{
unsigned short crc16 = *crc;
bi_trace();
QCRC_Init(&crc16);
*crc = crc16;
}
void QDECL GHL_CRC32_ProcessBuffer(hlcrc_t *crc, qbyte *p, int len)
{
unsigned short crc16 = *crc;
bi_trace();
while(len-->0)
{
QCRC_ProcessByte(&crc16, *p++);
}
*crc = crc16;
}
void QDECL GHL_CRC32_ProcessByte(hlcrc_t *crc, qbyte b)
{
unsigned short crc16 = *crc;
bi_trace();
QCRC_ProcessByte(&crc16, b);
*crc = crc16;
}
hlcrc_t QDECL GHL_CRC32_Final(hlcrc_t crc)
{
unsigned short crc16 = crc;
bi_trace();
return QCRC_Value(crc16);
}
long QDECL GHL_RandomLong(long minv, long maxv)
{
bi_trace();
return minv + frandom()*(maxv-minv);
}
float QDECL GHL_RandomFloat(float minv, float maxv)
{
bi_trace();
return minv + frandom()*(maxv-minv);
}
unk QDECL GHL_SetView(unk){notimpf(__func__);}
unk QDECL GHL_Time(unk){notimpf(__func__);}
unk QDECL GHL_CrosshairAngle(unk){notimpf(__func__);}
void *QDECL GHL_LoadFileForMe(char *name, int *size_out)
{
int fsize;
void *fptr;
bi_trace();
fsize = FS_LoadFile(name, &fptr);
if (size_out)
*size_out = fsize;
if (fsize == -1)
return NULL;
return fptr;
}
void QDECL GHL_FreeFile(void *fptr)
{
bi_trace();
FS_FreeFile(fptr);
}
unk QDECL GHL_EndSection(unk){notimpf(__func__);}
#include <sys/stat.h>
int QDECL GHL_CompareFileTime(char *fname1, char *fname2, int *result)
{
flocation_t loc1, loc2;
struct stat stat1, stat2;
bi_trace();
//results:
//1 = f1 is newer
//0 = equal age
//-1 = f2 is newer
//at least I think that's what it means.
*result = 0;
if (!FS_FLocateFile(fname1, FSLFRT_IFFOUND, &loc1) || !FS_FLocateFile(fname2, FSLFRT_IFFOUND, &loc2))
return 0;
if (stat(loc1.rawname, &stat1) || stat(loc2.rawname, &stat2))
return 0;
if (stat1.st_mtime > stat2.st_mtime)
*result = 1;
else if (stat1.st_mtime < stat2.st_mtime)
*result = -1;
else
*result = 0;
return 1;
}
void QDECL GHL_GetGameDir(char *gamedir)
{
extern char gamedirfile[];
bi_trace();
//warning: the output buffer size is not specified!
Q_strncpyz(gamedir, gamedirfile, MAX_QPATH);
}
unk QDECL GHL_Cvar_RegisterVariable(unk){notimpf(__func__);}
unk QDECL GHL_FadeClientVolume(unk){notimpf(__func__);}
unk QDECL GHL_SetClientMaxspeed(unk)
{
bi_trace();
notimpf(__func__);
}
unk QDECL GHL_CreateFakeClient(unk){notimpf(__func__);}
unk QDECL GHL_RunPlayerMove(unk){notimpf(__func__);}
int QDECL GHL_NumberOfEntities(void)
{
bi_trace();
return 0;
}
char *QDECL GHL_GetInfoKeyBuffer(hledict_t *ed)
{
bi_trace();
if (!ed)
return svs.info;
return svs.clients[ed - SVHL_Edict - 1].userinfo;
}
char *QDECL GHL_InfoKeyValue(char *infostr, char *key)
{
bi_trace();
return Info_ValueForKey(infostr, key);
}
unk QDECL GHL_SetKeyValue(unk){notimpf(__func__);}
unk QDECL GHL_SetClientKeyValue(unk){notimpf(__func__);}
unk QDECL GHL_IsMapValid(unk){notimpf(__func__);}
unk QDECL GHL_StaticDecal(unk){notimpf(__func__);}
unk QDECL GHL_PrecacheGeneric(unk){notimpf(__func__);}
int QDECL GHL_GetPlayerUserId(hledict_t *ed)
{
unsigned int clnum = (ed - SVHL_Edict) - 1;
bi_trace();
if (clnum >= sv.allocated_client_slots)
return -1;
return svs.clients[clnum].userid;
}
unk QDECL GHL_BuildSoundMsg(unk){notimpf(__func__);}
int QDECL GHL_IsDedicatedServer(void)
{
bi_trace();
#ifdef SERVERONLY
return 1;
#else
return qrenderer == QR_NONE;
#endif
}
hlcvar_t *hlcvar_malloced;
hlcvar_t *hlcvar_stored;
void SVHL_UpdateCvar(cvar_t *var)
{
if (!var->hlcvar)
return; //nothing to do
var->hlcvar->string = var->string;
var->hlcvar->value = var->value;
}
void SVHL_FreeCvars(void)
{
cvar_t *nc;
hlcvar_t *n;
//forget all
while (hlcvar_malloced)
{
n = hlcvar_malloced;
hlcvar_malloced = n->next;
nc = Cvar_FindVar(n->name);
if (nc)
nc->hlcvar = NULL;
Z_Free(n);
}
while (hlcvar_stored)
{
n = hlcvar_stored;
hlcvar_stored = n->next;
nc = Cvar_FindVar(n->name);
if (nc)
nc->hlcvar = NULL;
}
}
void SVHL_FreeCvar(hlcvar_t *var)
{
cvar_t *nc;
hlcvar_t **ref;
//unlink (free if it was malloced)
ref = &hlcvar_malloced;
while (*ref)
{
if (*ref == var)
{
(*ref) = (*ref)->next;
nc = Cvar_FindVar(var->name);
if (nc)
nc->hlcvar = NULL;
Z_Free(var);
return;
}
ref = &(*ref)->next;
}
ref = &hlcvar_stored;
while (*ref)
{
if (*ref == var)
{
(*ref) = (*ref)->next;
nc = Cvar_FindVar(var->name);
if (nc)
nc->hlcvar = NULL;
return;
}
ref = &(*ref)->next;
}
}
hlcvar_t *QDECL GHL_CVarGetPointer(char *varname)
{
cvar_t *var;
hlcvar_t *hlvar;
bi_trace();
var = Cvar_Get(varname, "", 0, "HalfLife");
if (!var)
{
Con_Printf("Not giving cvar \"%s\" to game\n", varname);
return NULL;
}
hlvar = var->hlcvar;
if (!hlvar)
{
hlvar = var->hlcvar = Z_Malloc(sizeof(hlcvar_t));
hlvar->name = var->name;
hlvar->string = var->string;
hlvar->value = var->value;
hlvar->next = hlcvar_malloced;
hlcvar_malloced = hlvar;
}
return hlvar;
}
void QDECL GHL_CVarRegister(hlcvar_t *hlvar)
{
cvar_t *var;
bi_trace();
var = Cvar_Get(hlvar->name, hlvar->string, 0, "HalfLife");
if (!var)
{
Con_Printf("Not giving cvar \"%s\" to game\n", hlvar->name);
return;
}
if (var->hlcvar)
{
SVHL_FreeCvar(var->hlcvar);
hlvar->next = hlcvar_stored;
hlcvar_stored = hlvar;
}
var->hlcvar = hlvar;
}
float QDECL GHL_CVarGetFloat(char *vname)
{
cvar_t *var = Cvar_FindVar(vname);
bi_trace();
if (var)
return var->value;
Con_Printf("cvar %s does not exist\n", vname);
return 0;
}
char *QDECL GHL_CVarGetString(char *vname)
{
cvar_t *var = Cvar_FindVar(vname);
bi_trace();
if (var)
return var->string;
Con_Printf("cvar %s does not exist\n", vname);
return "";
}
void QDECL GHL_CVarSetFloat(char *vname, float value)
{
cvar_t *var = Cvar_FindVar(vname);
bi_trace();
if (var)
Cvar_SetValue(var, value);
else
Con_Printf("cvar %s does not exist\n", vname);
}
void QDECL GHL_CVarSetString(char *vname, char *value)
{
cvar_t *var = Cvar_FindVar(vname);
bi_trace();
if (var)
Cvar_Set(var, value);
else
Con_Printf("cvar %s does not exist\n", vname);
}
unk QDECL GHL_GetPlayerWONId(unk){notimpf(__func__);}
unk QDECL GHL_Info_RemoveKey(unk){notimpf(__func__);}
unk QDECL GHL_GetPhysicsKeyValue(unk){notimpf(__func__);}
void QDECL GHL_SetPhysicsKeyValue(hledict_t *ent, char *key, char *value)
{
bi_begin();
notimpf(__func__);
bi_end();
}
unk QDECL GHL_GetPhysicsInfoString(unk){notimpf(__func__);}
unsigned short QDECL GHL_PrecacheEvent(int eventtype, char *eventname)
{
bi_trace();
Con_Printf("Fixme: GHL_PrecacheEvent: %s\n", eventname);
return 0;
}
void QDECL GHL_PlaybackEvent(int flags, hledict_t *ent, unsigned short eventidx, float delay, float *origin, float *angles, float f1, float f2, int i1, int i2, int b1, int b2)
{
bi_trace();
ignore("GHL_PlaybackEvent not implemented");
}
unk QDECL GHL_SetFatPVS(unk){notimpf(__func__);}
unk QDECL GHL_SetFatPAS(unk){notimpf(__func__);}
unk QDECL GHL_CheckVisibility(unk){notimpf(__func__);}
unk QDECL GHL_DeltaSetField(unk){notimpf(__func__);}
unk QDECL GHL_DeltaUnsetField(unk){notimpf(__func__);}
unk QDECL GHL_DeltaAddEncoder(unk){notimpf(__func__);}
unk QDECL GHL_GetCurrentPlayer(unk){notimpf(__func__);}
int QDECL GHL_CanSkipPlayer(hledict_t *playerent)
{
bi_trace();
return false;
// notimpf(__func__);
}
unk QDECL GHL_DeltaFindField(unk){notimpf(__func__);}
unk QDECL GHL_DeltaSetFieldByIndex(unk){notimpf(__func__);}
unk QDECL GHL_DeltaUnsetFieldByIndex(unk){notimpf(__func__);}
unk QDECL GHL_SetGroupMask(unk){notimpf(__func__);}
unk QDECL GHL_CreateInstancedBaseline(unk){notimpf(__func__);}
unk QDECL GHL_Cvar_DirectSet(unk){notimpf(__func__);}
unk QDECL GHL_ForceUnmodified(unk){notimpf(__func__);}
unk QDECL GHL_GetPlayerStats(unk){notimpf(__func__);}
unk QDECL GHL_AddServerCommand(unk){notimpf(__func__);}
unk QDECL GHL_Voice_GetClientListening(unk){notimpf(__func__);}
qboolean QDECL GHL_Voice_SetClientListening(int listener, int sender, int shouldlisten)
{
bi_trace();
return false;
}
char *QDECL GHL_GetPlayerAuthId(hledict_t *playered)
{
unsigned int clnum = (playered - SVHL_Edict) - 1;
bi_trace();
if (clnum >= sv.allocated_client_slots)
return NULL;
return svs.clients[clnum].guid;
}
unk QDECL GHL_SequenceGet(unk){notimpf(__func__);}
unk QDECL GHL_SequencePickSentence(unk){notimpf(__func__);}
unk QDECL GHL_GetFileSize(unk){notimpf(__func__);}
unk QDECL GHL_GetApproxWavePlayLen(unk){notimpf(__func__);}
unk QDECL GHL_IsCareerMatch(unk){notimpf(__func__);}
unk QDECL GHL_GetLocalizedStringLength(unk){notimpf(__func__);}
unk QDECL GHL_RegisterTutorMessageShown(unk){notimpf(__func__);}
unk QDECL GHL_GetTimesTutorMessageShown(unk){notimpf(__func__);}
unk QDECL GHL_ProcessTutorMessageDecayBuffer(unk){notimpf(__func__);}
unk QDECL GHL_ConstructTutorMessageDecayBuffer(unk){notimpf(__func__);}
unk QDECL GHL_ResetTutorMessageDecayData(unk){notimpf(__func__);}
unk QDECL GHL_QueryClientCvarValue(unk){notimpf(__func__);}
unk QDECL GHL_QueryClientCvarValue2(unk){notimpf(__func__);}
//====================================================================================================
SVHL_Builtins_t SVHL_Builtins =
{
GHL_PrecacheModel,
GHL_PrecacheSound,
GHL_SetModel,
GHL_ModelIndex,
GHL_ModelFrames,
GHL_SetSize,
GHL_ChangeLevel,
GHL_GetSpawnParms,
GHL_SaveSpawnParms,
GHL_VecToYaw,
GHL_VecToAngles,
GHL_MoveToOrigin,
GHL_ChangeYaw,
GHL_ChangePitch,
GHL_FindEntityByString,
GHL_GetEntityIllum,
GHL_FindEntityInSphere,
GHL_FindClientInPVS,
GHL_EntitiesInPVS,
GHL_MakeVectors,
GHL_AngleVectors,
GHL_CreateEntity,
GHL_RemoveEntity,
GHL_CreateNamedEntity,
GHL_MakeStatic,
GHL_EntIsOnFloor,
GHL_DropToFloor,
GHL_WalkMove,
GHL_SetOrigin,
GHL_EmitSound,
GHL_EmitAmbientSound,
GHL_TraceLine,
GHL_TraceToss,
GHL_TraceMonsterHull,
GHL_TraceHull,
GHL_TraceModel,
GHL_TraceTexture,
GHL_TraceSphere,
GHL_GetAimVector,
GHL_ServerCommand,
GHL_ServerExecute,
GHL_ClientCommand,
GHL_ParticleEffect,
GHL_LightStyle,
GHL_DecalIndex,
GHL_PointContents,
GHL_MessageBegin,
GHL_MessageEnd,
GHL_WriteByte,
GHL_WriteChar,
GHL_WriteShort,
GHL_WriteLong,
GHL_WriteAngle,
GHL_WriteCoord,
GHL_WriteString,
GHL_WriteEntity,
GHL_CVarRegister,
GHL_CVarGetFloat,
GHL_CVarGetString,
GHL_CVarSetFloat,
GHL_CVarSetString,
GHL_AlertMessage,
GHL_EngineFprintf,
GHL_PvAllocEntPrivateData,
GHL_PvEntPrivateData,
GHL_FreeEntPrivateData,
GHL_SzFromIndex,
GHL_AllocString,
GHL_GetVarsOfEnt,
GHL_PEntityOfEntOffset,
GHL_EntOffsetOfPEntity,
GHL_IndexOfEdict,
GHL_PEntityOfEntIndex,
GHL_FindEntityByVars,
GHL_GetModelPtr,
GHL_RegUserMsg,
GHL_AnimationAutomove,
GHL_GetBonePosition,
GHL_FunctionFromName,
GHL_NameForFunction,
GHL_ClientPrintf,
GHL_ServerPrint,
GHL_Cmd_Args,
GHL_Cmd_Argv,
GHL_Cmd_Argc,
GHL_GetAttachment,
GHL_CRC32_Init,
GHL_CRC32_ProcessBuffer,
GHL_CRC32_ProcessByte,
GHL_CRC32_Final,
GHL_RandomLong,
GHL_RandomFloat,
GHL_SetView,
GHL_Time,
GHL_CrosshairAngle,
GHL_LoadFileForMe,
GHL_FreeFile,
GHL_EndSection,
GHL_CompareFileTime,
GHL_GetGameDir,
GHL_Cvar_RegisterVariable,
GHL_FadeClientVolume,
GHL_SetClientMaxspeed,
GHL_CreateFakeClient,
GHL_RunPlayerMove,
GHL_NumberOfEntities,
GHL_GetInfoKeyBuffer,
GHL_InfoKeyValue,
GHL_SetKeyValue,
GHL_SetClientKeyValue,
GHL_IsMapValid,
GHL_StaticDecal,
GHL_PrecacheGeneric,
GHL_GetPlayerUserId,
GHL_BuildSoundMsg,
GHL_IsDedicatedServer,
#if HALFLIFE_API_VERSION > 138
GHL_CVarGetPointer,
GHL_GetPlayerWONId,
GHL_Info_RemoveKey,
GHL_GetPhysicsKeyValue,
GHL_SetPhysicsKeyValue,
GHL_GetPhysicsInfoString,
GHL_PrecacheEvent,
GHL_PlaybackEvent,
GHL_SetFatPVS,
GHL_SetFatPAS,
GHL_CheckVisibility,
GHL_DeltaSetField,
GHL_DeltaUnsetField,
GHL_DeltaAddEncoder,
GHL_GetCurrentPlayer,
GHL_CanSkipPlayer,
GHL_DeltaFindField,
GHL_DeltaSetFieldByIndex,
GHL_DeltaUnsetFieldByIndex,
GHL_SetGroupMask,
GHL_CreateInstancedBaseline,
GHL_Cvar_DirectSet,
GHL_ForceUnmodified,
GHL_GetPlayerStats,
GHL_AddServerCommand,
GHL_Voice_GetClientListening,
GHL_Voice_SetClientListening,
GHL_GetPlayerAuthId,
GHL_SequenceGet,
GHL_SequencePickSentence,
GHL_GetFileSize,
GHL_GetApproxWavePlayLen,
GHL_IsCareerMatch,
GHL_GetLocalizedStringLength,
GHL_RegisterTutorMessageShown,
GHL_GetTimesTutorMessageShown,
GHL_ProcessTutorMessageDecayBuffer,
GHL_ConstructTutorMessageDecayBuffer,
GHL_ResetTutorMessageDecayData,
GHL_QueryClientCvarValue,
GHL_QueryClientCvarValue2,
#endif
0xdeadbeef
};
void SV_ReadLibListDotGam(void)
{
char key[1024];
char value[1024];
char *file;
char *s;
Info_SetValueForStarKey(svs.info, "*gamedll", "", sizeof(svs.info));
Info_SetValueForStarKey(svs.info, "*cldll", "", sizeof(svs.info));
file = COM_LoadTempFile("liblist.gam");
if (!file)
return;
Info_SetValueForStarKey(svs.info, "*cldll", "1", sizeof(svs.info));
while ((file = COM_ParseOut(file, key, sizeof(key))))
{
file = COM_ParseOut(file, value, sizeof(value));
while((s = strchr(value, '\\')))
*s = '/';
if (!strcmp(key, "gamedll"
#ifdef __linux__
"_linux"
#endif
))
Info_SetValueForStarKey(svs.info, "*gamedll", value, sizeof(svs.info));
if (!strcmp(key, "cldll"))
Info_SetValueForStarKey(svs.info, "*cldll", atoi(value)?"1":"", sizeof(svs.info));
}
}
int SVHL_InitGame(void)
{
char *gamedll;
void *iterator;
char path[MAX_OSPATH];
char fullname[MAX_OSPATH];
void (WINAPI *GiveFnptrsToDll) (funcs, globals);
int (QDECL *GetEntityAPI)(SVHL_GameFuncs_t *pFunctionTable, int apivers);
dllfunction_t hlgamefuncs[] =
{
{(void**)&GiveFnptrsToDll, "GiveFnptrsToDll"},
{(void**)&GetEntityAPI, "GetEntityAPI"},
{NULL, NULL}
};
if (sizeof(long) != sizeof(void*))
{
Con_Printf("sizeof(long)!=sizeof(ptr): Cannot run halflife gamecode on this platform\n");
return 0;
}
SV_ReadLibListDotGam();
if (hlgamecode)
{
ZG_FreeGroup(&hlmapmemgroup);
SVHL_Edict = ZG_Malloc(&hlmapmemgroup, sizeof(*SVHL_Edict) * MAX_HL_EDICTS);
SVHL_Globals.maxentities = MAX_HL_EDICTS; //I think this is correct
return 1;
}
gamedll = Info_ValueForKey(svs.info, "*gamedll");
iterator = NULL;
while(COM_IteratePaths(&iterator, path, sizeof(path)))
{
snprintf (fullname, sizeof(fullname), "%s%s", path, gamedll);
hlgamecode = Sys_LoadLibrary(fullname, hlgamefuncs);
if (hlgamecode)
break;
}
if (!hlgamecode)
return 0;
SVHL_Edict = ZG_Malloc(&hlmapmemgroup, sizeof(*SVHL_Edict) * MAX_HL_EDICTS);
SVHL_Globals.maxentities = MAX_HL_EDICTS; //I think this is correct
GiveFnptrsToDll(&SVHL_Builtins, &SVHL_Globals);
if (!GetEntityAPI(&SVHL_GameFuncs, HALFLIFE_API_VERSION))
{
Con_Printf(CON_ERROR "Error: %s is incompatible (FTE is compiled for %i)\n", fullname, HALFLIFE_API_VERSION);
if (GetEntityAPI(&SVHL_GameFuncs, 138))
Con_Printf(CON_ERROR "mod is 138\n");
Sys_CloseLibrary(hlgamecode);
hlgamecode = NULL;
return 0;
}
bi_begin();
SVHL_GameFuncs.GameDLLInit();
bi_end();
return 1;
}
void SVHL_SaveLevelCache(char *filename)
{
}
void SVHL_SetupGame(void)
{
lastusermessage = 255;
//called on new map
}
void SVHL_SpawnEntities(char *entstring)
{
char key[256];
char value[1024];
char classname[1024];
hlfielddef_t fdef;
hledict_t *ed, *world;
extern cvar_t coop; //who'd have thought it, eh?
char *ts;
int i;
//initialise globals
SVHL_Globals.stringbase = ""; //uninitialised strings are considered empty and not crashy. this ensures that is true.
SVHL_Globals.maxclients = svs.allocated_client_slots;
SVHL_Globals.deathmatch = deathmatch.value;
SVHL_Globals.coop = coop.value;
SVHL_Globals.serverflags = 0;
SVHL_Globals.mapname = GHL_AllocString(sv.name);
SVHL_NumActiveEnts = 0;
sv.allocated_client_slots = 0;
//touch world.
world = GHL_CreateNamedEntity(GHL_AllocString("worldspawn"));
world->v.solid = SOLID_BSP;
GHL_SetModel(world, sv.modelname);
//spawn player ents
for (i = 0; i < SVHL_Globals.maxclients; i++)
{
sv.allocated_client_slots++;
ed = GHL_CreateNamedEntity(GHL_AllocString("player"));
}
for (i = 0; i < sv.allocated_client_slots; i++)
{
SVHL_Edict[i].isfree = true;
}
sv.allocated_client_slots = i;
//precache the inline models (and touch them).
sv.strings.model_precache[0] = "";
sv.strings.model_precache[1] = sv.modelname; //the qvm doesn't have access to this array
for (i=1 ; i<sv.world.worldmodel->numsubmodels ; i++)
{
sv.strings.model_precache[1+i] = localmodels[i];
sv.models[i+1] = Mod_ForName (localmodels[i], false);
}
while (entstring)
{
entstring = COM_ParseOut(entstring, key, sizeof(key));
if (strcmp(key, "{"))
break;
*classname = 0;
ts = entstring;
while (ts)
{
ts = COM_ParseOut(ts, key, sizeof(key));
if (!strcmp(key, "}"))
break;
ts = COM_ParseOut(ts, value, sizeof(value));
if (!strcmp(key, "classname"))
{
memcpy(classname, value, strlen(value)+1);
break;
}
}
if (world)
{
if (strcmp(classname, "worldspawn"))
SV_Error("first entity is not worldspawn");
ed = world;
world = NULL;
}
else
ed = GHL_CreateNamedEntity(GHL_AllocString(classname));
while (entstring)
{
entstring = COM_ParseOut(entstring, key, sizeof(key));
if (!strcmp(key, "}"))
break;
entstring = COM_ParseOut(entstring, value, sizeof(value));
if (*key == '_')
continue;
if (!strcmp(key, "classname"))
memcpy(classname, value, strlen(value)+1);
fdef.handled = false;
if (!*classname)
fdef.classname = NULL;
else
fdef.classname = classname;
fdef.key = key;
fdef.value = value;
SVHL_GameFuncs.DispatchKeyValue(ed, &fdef);
if (!fdef.handled)
{
if (!strcmp(key, "angle"))
{
float a = atof(value);
sprintf(value, "%f %f %f", 0.0f, a, 0.0f);
strcpy(key, "angles");
SVHL_GameFuncs.DispatchKeyValue(ed, &fdef);
}
if (!fdef.handled)
Con_Printf("Bad field on %s, %s\n", classname, key);
}
}
SVHL_GameFuncs.DispatchSpawn(ed);
}
bi_begin();
SVHL_GameFuncs.ServerActivate(SVHL_Edict, SVHL_NumActiveEnts, sv.allocated_client_slots);
bi_end();
}
void SVHL_ShutdownGame(void)
{
SVHL_FreeCvars();
//gametype changed, or server shutdown
Sys_CloseLibrary(hlgamecode);
hlgamecode = NULL;
SVHL_Edict = NULL;
memset(&SVHL_Globals, 0, sizeof(SVHL_Globals));
memset(&SVHL_GameFuncs, 0, sizeof(SVHL_GameFuncs));
memset(&SVHL_GameFuncsEx, 0, sizeof(SVHL_GameFuncsEx));
ZG_FreeGroup(&hlmapmemgroup);
}
qboolean HLSV_ClientCommand(client_t *client)
{
hledict_t *ed = &SVHL_Edict[client - svs.clients + 1];
if (!hlgamecode)
return false;
bi_begin();
SVHL_GameFuncs.ClientCommand(ed);
bi_end();
return true;
}
qboolean SVHL_ClientConnect(client_t *client, netadr_t adr, char rejectmessage[128])
{
qboolean result;
char ipadr[256];
NET_AdrToString(ipadr, sizeof(ipadr), &adr);
strcpy(rejectmessage, "Rejected by gamecode");
bi_begin();
result = SVHL_GameFuncs.ClientConnect(&SVHL_Edict[client-svs.clients+1], client->name, ipadr, rejectmessage);
bi_end();
return result;
}
void SVHL_BuildStats(client_t *client, int *si, float *sf, char **ss)
{
hledict_t *ed = &SVHL_Edict[client - svs.clients + 1];
si[STAT_HEALTH] = ed->v.health;
si[STAT_VIEWHEIGHT] = ed->v.view_ofs[2];
si[STAT_WEAPON] = SV_ModelIndex(SVHL_Globals.stringbase+ed->v.vmodelindex);
si[STAT_ITEMS] = ed->v.weapons;
}
void SVHL_PutClientInServer(client_t *client)
{
hledict_t *ed = &SVHL_Edict[client - svs.clients + 1];
ed->isfree = false;
bi_begin();
SVHL_GameFuncs.ClientPutInServer(&SVHL_Edict[client-svs.clients+1]);
bi_end();
}
void SVHL_DropClient(client_t *drop)
{
hledict_t *ed = &SVHL_Edict[drop - svs.clients + 1];
bi_begin();
SVHL_GameFuncs.ClientDisconnect(&SVHL_Edict[drop-svs.clients+1]);
bi_end();
ed->isfree = true;
}
static void SVHL_RunCmdR(hledict_t *ed, usercmd_t *ucmd)
{
int i;
hledict_t *other;
extern cvar_t temp1;
// chop up very long commands
if (ucmd->msec > 50)
{
usercmd_t cmd = *ucmd;
cmd.msec = ucmd->msec/2;
SVHL_RunCmdR (ed, &cmd);
cmd.msec = ucmd->msec/2 + (ucmd->msec&1); //give them back their msec.
cmd.impulse = 0;
SVHL_RunCmdR (ed, &cmd);
return;
}
host_frametime = ucmd->msec * 0.001;
host_frametime *= sv.gamespeed;
if (host_frametime > 0.1)
host_frametime = 0.1;
pmove.cmd = *ucmd;
pmove.pm_type = temp1.value;//PM_NORMAL;//FLY;
pmove.numphysent = 1;
pmove.physents[0].model = sv.world.worldmodel;
pmove.physents[0].info = 0;
pmove.skipent = -1;
pmove.onladder = false;
if (ed->v.flags & (1<<24))
{
pmove.cmd.forwardmove = 0;
pmove.cmd.sidemove = 0;
pmove.cmd.upmove = 0;
}
{
hledict_t *list[256];
int count;
physent_t *pe;
vec3_t pmove_mins, pmove_maxs;
for (i = 0; i < 3; i++)
{
pmove_mins[i] = pmove.origin[i] - 256;
pmove_maxs[i] = pmove.origin[i] + 256;
}
count = SVHL_AreaEdicts(pmove_mins, pmove_maxs, list, sizeof(list)/sizeof(list[0]));
for (i = 0; i < count; i++)
{
other = list[i];
if (other == ed)
continue;
if (other->v.owner == ed)
continue;
if (other->v.flags & (1<<23)) //has monsterclip flag set, so ignore it
continue;
pe = &pmove.physents[pmove.numphysent];
switch(other->v.skin)
{
case Q1CONTENTS_EMPTY:
pe->forcecontentsmask = FTECONTENTS_EMPTY;
break;
case Q1CONTENTS_SOLID:
pe->forcecontentsmask = FTECONTENTS_SOLID;
break;
case Q1CONTENTS_WATER:
pe->forcecontentsmask = FTECONTENTS_WATER;
break;
case Q1CONTENTS_SLIME:
pe->forcecontentsmask = FTECONTENTS_SLIME;
break;
case Q1CONTENTS_LAVA:
pe->forcecontentsmask = FTECONTENTS_LAVA;
break;
case Q1CONTENTS_SKY:
pe->forcecontentsmask = FTECONTENTS_SKY;
break;
case Q1CONTENTS_LADDER:
pe->forcecontentsmask = FTECONTENTS_LADDER;
break;
default:
pe->forcecontentsmask = 0;
break;
}
if (other->v.solid == SOLID_NOT || other->v.solid == SOLID_TRIGGER)
{
if (!pe->forcecontentsmask)
continue;
pe->nonsolid = true;
}
else
pe->nonsolid = false;
if (other->v.modelindex)
{
pe->model = sv.models[other->v.modelindex];
if (pe->model && pe->model->type != mod_brush)
pe->model = NULL;
}
else
pe->model = NULL;
pmove.numphysent++;
pe->info = other - SVHL_Edict;
VectorCopy(other->v.origin, pe->origin);
VectorCopy(other->v.mins, pe->mins);
VectorCopy(other->v.maxs, pe->maxs);
VectorCopy(other->v.angles, pe->angles);
}
}
VectorCopy(ed->v.mins, player_mins);
VectorCopy(ed->v.maxs, player_maxs);
VectorCopy(ed->v.origin, pmove.origin);
VectorCopy(ed->v.velocity, pmove.velocity);
if (ed->v.flags & (1<<22))
{
VectorCopy(ed->v.basevelocity, pmove.basevelocity);
}
else
VectorClear(pmove.basevelocity);
PM_PlayerMove(sv.gamespeed);
VectorCopy(pmove.origin, ed->v.origin);
VectorCopy(pmove.velocity, ed->v.velocity);
if (pmove.onground)
{
ed->v.flags |= FL_ONGROUND;
ed->v.groundentity = &SVHL_Edict[pmove.physents[pmove.groundent].info];
}
else
ed->v.flags &= ~FL_ONGROUND;
for (i = 0; i < pmove.numtouch; i++)
{
other = &SVHL_Edict[pmove.physents[pmove.touchindex[i]].info];
SVHL_GameFuncs.DispatchTouch(other, ed);
}
SVHL_LinkEdict(ed, true);
}
void SVHL_RunCmd(client_t *cl, usercmd_t *ucmd)
{
hledict_t *ed = &SVHL_Edict[cl - svs.clients + 1];
#if HALFLIFE_API_VERSION >= 140
ed->v.buttons = ucmd->buttons;
#else
//assume they're not running halflife cgame
ed->v.buttons = 0;
if (ucmd->buttons & 1)
ed->v.buttons |= (1<<0); //shoot
if (ucmd->buttons & 2)
ed->v.buttons |= (1<<1); //jump
if (ucmd->buttons & 8)
ed->v.buttons |= (1<<2); //duck
if (ucmd->forwardmove > 0)
ed->v.buttons |= (1<<3); //forward
if (ucmd->forwardmove < 0)
ed->v.buttons |= (1<<4); //back
if (ucmd->buttons & 4)
ed->v.buttons |= (1<<5); //use
//ed->v.buttons |= (1<<6); //cancel
//ed->v.buttons |= (1<<7); //turn left
//ed->v.buttons |= (1<<8); //turn right
if (ucmd->sidemove > 0)
ed->v.buttons |= (1<<9); //move left
if (ucmd->sidemove < 0)
ed->v.buttons |= (1<<10); //move right
//ed->v.buttons |= (1<<11); //shoot2
//ed->v.buttons |= (1<<12); //run
if (ucmd->buttons & 16)
ed->v.buttons |= (1<<13); //reload
//ed->v.buttons |= (1<<14); //alt1
//ed->v.buttons |= (1<<15); //alt2
#endif
if (ucmd->impulse)
ed->v.impulse = ucmd->impulse;
ed->v.v_angle[0] = SHORT2ANGLE(ucmd->angles[0]);
ed->v.v_angle[1] = SHORT2ANGLE(ucmd->angles[1]);
ed->v.v_angle[2] = SHORT2ANGLE(ucmd->angles[2]);
ed->v.angles[0] = 0;
ed->v.angles[1] = SHORT2ANGLE(ucmd->angles[1]);
ed->v.angles[2] = SHORT2ANGLE(ucmd->angles[2]);
bi_begin();
SVHL_GameFuncs.PlayerPreThink(ed);
SVHL_RunCmdR(ed, ucmd);
if (ed->v.nextthink && ed->v.nextthink < sv.time)
{
ed->v.nextthink = 0;
SVHL_GameFuncs.DispatchThink(ed);
}
SVHL_GameFuncs.PlayerPostThink(ed);
bi_end();
}
void SVHL_RunPlayerCommand(client_t *cl, usercmd_t *oldest, usercmd_t *oldcmd, usercmd_t *newcmd)
{
hledict_t *e = &SVHL_Edict[cl - svs.clients + 1];
SVHL_Globals.time = sv.time;
if (net_drop < 20)
{
while (net_drop > 2)
{
SVHL_RunCmd (cl, &cl->lastcmd);
net_drop--;
}
if (net_drop > 1)
SVHL_RunCmd (cl, oldest);
if (net_drop > 0)
SVHL_RunCmd (cl, oldcmd);
}
SVHL_RunCmd (cl, newcmd);
}
void SVHL_Snapshot_Build(client_t *client, packet_entities_t *pack, qbyte *pvs, edict_t *clent, qboolean ignorepvs)
{
hledict_t *e;
entity_state_t *s;
int i;
pack->servertime = sv.time;
pack->num_entities = 0;
for (i = 1; i < MAX_HL_EDICTS; i++)
{
e = &SVHL_Edict[i];
if (!e)
break;
if (e->isfree)
continue;
if (!e->v.modelindex || !e->v.model)
continue;
if (e->v.effects & 128)
continue;
if (pack->num_entities == pack->max_entities)
break;
s = &pack->entities[pack->num_entities++];
s->number = i;
s->modelindex = e->v.modelindex;
s->frame = e->v.sequence1;
s->effects = e->v.effects;
s->skinnum = e->v.skin;
s->scale = 16;
s->trans = 255;
VectorCopy(e->v.angles, s->angles);
VectorCopy(e->v.origin, s->origin);
}
}
qbyte *SVHL_Snapshot_SetupPVS(client_t *client, qbyte *pvs, unsigned int pvsbufsize)
{
vec3_t org;
if (client->hledict)
{
VectorAdd (client->hledict->v.origin, client->hledict->v.view_ofs, org);
sv.world.worldmodel->funcs.FatPVS(sv.world.worldmodel, org, pvs, pvsbufsize, false);
}
return pvs;
}
#endif