fteqw/engine/server/svhl_world.c
Spoike 6a831e2f99 qwsvdef.h is no more. Many files changed because of this.
enabled ssl support in windows for plugins.
updated jabber plugin.
tweaked downloading logic in an effort to end the madness.
Updated sql code to support sleeping the qc thread until query completion, as a more readable alternative to callbacks.
Client updates should be more explicit now, removing a potential bug.
networked terrain editing should now be in a better state.
updated the dir command to show paths too (so when the same file is listed twice, the pak its in will at least be different). Many files changed because of this.

git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@4273 fc73d0e0-1445-4013-8a0c-d673dee63da5
2013-03-31 04:21:08 +00:00

781 lines
20 KiB
C

/*
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
// world.c -- world query functions
#include "quakedef.h"
#ifdef HLSERVER
#include "svhl_gcapi.h"
hull_t *World_HullForBox (vec3_t mins, vec3_t maxs);
qboolean TransformedTrace (struct model_s *model, int hulloverride, int frame, vec3_t start, vec3_t end, vec3_t mins, vec3_t maxs, struct trace_s *trace, vec3_t origin, vec3_t angles, unsigned int hitcontentsmask);
/*
entities never clip against themselves, or their owner
line of sight checks trace->crosscontent, but bullets don't
*/
extern cvar_t sv_compatiblehulls;
typedef struct
{
vec3_t boxmins, boxmaxs;// enclose the test object along entire move
float *mins, *maxs; // size of the moving object
vec3_t mins2, maxs2; // size when clipping against mosnters
float *start, *end;
trace_t trace;
int type;
hledict_t *passedict;
int hullnum;
unsigned int clipmask;
} hlmoveclip_t;
/*
===============================================================================
HULL BOXES
===============================================================================
*/
/*
===============================================================================
ENTITY AREA CHECKING
===============================================================================
*/
/*
===============
SV_UnlinkEdict
===============
*/
void SVHL_UnlinkEdict (hledict_t *ent)
{
if (!ent->area.prev)
return; // not linked in anywhere
RemoveLink (&ent->area);
ent->area.prev = ent->area.next = NULL;
}
/*
====================
SV_TouchLinks
====================
*/
#define MAX_NODELINKS 256 //all this means is that any more than this will not touch.
hledict_t *nodelinks[MAX_NODELINKS];
void SVHL_TouchLinks ( hledict_t *ent, areanode_t *node )
{ //Spike: rewritten this function to cope with killtargets used on a few maps.
link_t *l, *next;
hledict_t *touch;
int linkcount = 0, ln;
//work out who they are first.
for (l = node->edicts.next ; l != &node->edicts ; l = next)
{
if (linkcount == MAX_NODELINKS)
break;
next = l->next;
touch = HLEDICT_FROM_AREA(l);
if (touch == ent)
continue;
if (touch->v.solid != SOLID_TRIGGER)
continue;
if (ent->v.absmin[0] > touch->v.absmax[0]
|| ent->v.absmin[1] > touch->v.absmax[1]
|| ent->v.absmin[2] > touch->v.absmax[2]
|| ent->v.absmax[0] < touch->v.absmin[0]
|| ent->v.absmax[1] < touch->v.absmin[1]
|| ent->v.absmax[2] < touch->v.absmin[2] )
continue;
// if (!((int)ent->xv.dimension_solid & (int)touch->xv.dimension_hit))
// continue;
nodelinks[linkcount++] = touch;
}
for (ln = 0; ln < linkcount; ln++)
{
touch = nodelinks[ln];
//make sure nothing moved it away
if (touch->isfree)
continue;
if (touch->v.solid != SOLID_TRIGGER)
continue;
if (ent->v.absmin[0] > touch->v.absmax[0]
|| ent->v.absmin[1] > touch->v.absmax[1]
|| ent->v.absmin[2] > touch->v.absmax[2]
|| ent->v.absmax[0] < touch->v.absmin[0]
|| ent->v.absmax[1] < touch->v.absmin[1]
|| ent->v.absmax[2] < touch->v.absmin[2] )
continue;
// if (!((int)ent->xv->dimension_solid & (int)touch->xv->dimension_hit)) //didn't change did it?...
// continue;
SVHL_GameFuncs.DispatchTouch(touch, ent);
if (ent->isfree)
break;
}
// recurse down both sides
if (node->axis == -1 || ent->isfree)
return;
if ( ent->v.absmax[node->axis] > node->dist )
SVHL_TouchLinks ( ent, node->children[0] );
if ( ent->v.absmin[node->axis] < node->dist )
SVHL_TouchLinks ( ent, node->children[1] );
}
/*
===============
SV_LinkEdict
===============
*/
void SVHL_LinkEdict (hledict_t *ent, qboolean touch_triggers)
{
areanode_t *node;
if (ent->area.prev)
SVHL_UnlinkEdict (ent); // unlink from old position
if (ent == &SVHL_Edict[0])
return; // don't add the world
if (ent->isfree)
return;
// set the abs box
if (ent->v.solid == SOLID_BSP &&
(ent->v.angles[0] || ent->v.angles[1] || ent->v.angles[2]) )
{ // expand for rotation
#if 1
int i;
float v;
float max;
//q2 method
max = 0;
for (i=0 ; i<3 ; i++)
{
v =fabs( ent->v.mins[i]);
if (v > max)
max = v;
v =fabs( ent->v.maxs[i]);
if (v > max)
max = v;
}
for (i=0 ; i<3 ; i++)
{
ent->v.absmin[i] = ent->v.origin[i] - max;
ent->v.absmax[i] = ent->v.origin[i] + max;
}
#else
int i;
vec3_t f, r, u;
vec3_t mn, mx;
//we need to link to the correct leaves
AngleVectors(ent->v.angles, f,r,u);
mn[0] = DotProduct(ent->v.mins, f);
mn[1] = -DotProduct(ent->v.mins, r);
mn[2] = DotProduct(ent->v.mins, u);
mx[0] = DotProduct(ent->v.maxs, f);
mx[1] = -DotProduct(ent->v.maxs, r);
mx[2] = DotProduct(ent->v.maxs, u);
for (i = 0; i < 3; i++)
{
if (mn[i] < mx[i])
{
ent->v.absmin[i] = ent->v.origin[i]+mn[i]-0.1;
ent->v.absmax[i] = ent->v.origin[i]+mx[i]+0.1;
}
else
{ //box went inside out
ent->v.absmin[i] = ent->v.origin[i]+mx[i]-0.1;
ent->v.absmax[i] = ent->v.origin[i]+mn[i]+0.1;
}
}
#endif
}
else
{
VectorAdd (ent->v.origin, ent->v.mins, ent->v.absmin);
VectorAdd (ent->v.origin, ent->v.maxs, ent->v.absmax);
}
//
// to make items easier to pick up and allow them to be grabbed off
// of shelves, the abs sizes are expanded
//
if ((int)ent->v.flags & FL_ITEM)
{
ent->v.absmin[0] -= 15;
ent->v.absmin[1] -= 15;
ent->v.absmin[2] -= 1;
ent->v.absmax[0] += 15;
ent->v.absmax[1] += 15;
ent->v.absmax[2] += 1;
}
else
{ // because movement is clipped an epsilon away from an actual edge,
// we must fully check even when bounding boxes don't quite touch
ent->v.absmin[0] -= 1;
ent->v.absmin[1] -= 1;
ent->v.absmin[2] -= 1;
ent->v.absmax[0] += 1;
ent->v.absmax[1] += 1;
ent->v.absmax[2] += 1;
}
// link to PVS leafs
// sv.worldmodel->funcs.FindTouchedLeafs_Q1(sv.worldmodel, ent, ent->v.absmin, ent->v.absmax);
// find the first node that the ent's box crosses
node = sv.world.areanodes;
while (1)
{
if (node->axis == -1)
break;
if (ent->v.absmin[node->axis] > node->dist)
node = node->children[0];
else if (ent->v.absmax[node->axis] < node->dist)
node = node->children[1];
else
break; // crosses the node
}
// link it in
InsertLinkBefore (&ent->area, &node->edicts);
// if touch_triggers, touch all entities at this node and decend for more
if (touch_triggers)
SVHL_TouchLinks ( ent, sv.world.areanodes );
}
/*
===============================================================================
POINT TESTING IN HULLS
===============================================================================
*/
/*
==================
SV_PointContents
==================
*/
int SVHL_PointContents (vec3_t p)
{
return sv.world.worldmodel->funcs.PointContents(sv.world.worldmodel, NULL, p);
}
//===========================================================================
/*
============
SV_TestEntityPosition
A small wrapper around SV_BoxInSolidEntity that never clips against the
supplied entity.
============
*/
hledict_t *SVHL_TestEntityPosition (hledict_t *ent)
{
trace_t trace;
trace = SVHL_Move (ent->v.origin, ent->v.mins, ent->v.maxs, ent->v.origin, 0, 0, ent);
if (trace.startsolid)
return &SVHL_Edict[0];
return NULL;
}
/*
==================
SV_ClipMoveToEntity
Handles selection or creation of a clipping hull, and offseting (and
eventually rotation) of the end points
==================
*/
trace_t SVHL_ClipMoveToEntity (hledict_t *ent, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int hullnum, qboolean hitmodel, unsigned int clipmask) //hullnum overrides min/max for q1 style bsps
{
trace_t trace;
model_t *model;
/*
#ifdef Q2BSPS
if (ent->v->solid == SOLID_BSP)
if (sv.models[(int)ent->v->modelindex] && (sv.models[(int)ent->v->modelindex]->fromgame == fg_quake2 || sv.models[(int)ent->v->modelindex]->fromgame == fg_quake3))
{
trace = CM_TransformedBoxTrace (start, end, mins, maxs, sv.models[(int)ent->v->modelindex]->hulls[0].firstclipnode, MASK_PLAYERSOLID, ent->v->origin, ent->v->angles);
if (trace.fraction < 1 || trace.startsolid)
trace.ent = ent;
return trace;
}
#endif
*/
// get the clipping hull
if (ent->v.solid == SOLID_BSP)
{
model = sv.models[(int)ent->v.modelindex];
if (!model || (model->type != mod_brush && model->type != mod_heightmap))
SV_Error("SOLID_BSP with non bsp model (classname: %s)", SVHL_Globals.stringbase+ent->v.classname);
}
else
{
vec3_t boxmins, boxmaxs;
VectorSubtract (ent->v.mins, maxs, boxmins);
VectorSubtract (ent->v.maxs, mins, boxmaxs);
World_HullForBox(boxmins, boxmaxs);
model = NULL;
}
// trace a line through the apropriate clipping hull
if (ent->v.solid != SOLID_BSP)
{
ent->v.angles[0]*=-1; //carmack made bsp models rotate wrongly.
TransformedTrace(model, hullnum, ent->v.frame, start, end, mins, maxs, &trace, ent->v.origin, ent->v.angles, clipmask);
ent->v.angles[0]*=-1;
}
else
{
TransformedTrace(model, hullnum, ent->v.frame, start, end, mins, maxs, &trace, ent->v.origin, ent->v.angles, clipmask);
}
// fix trace up by the offset
if (trace.fraction != 1)
{
if (!model && hitmodel && ent->v.solid != SOLID_BSP && ent->v.modelindex > 0)
{
//okay, we hit the bbox
model_t *model;
if (ent->v.modelindex < 1 || ent->v.modelindex >= MAX_MODELS)
SV_Error("SV_ClipMoveToEntity: modelindex out of range\n");
model = sv.models[ (int)ent->v.modelindex ];
if (!model)
{ //if the model isn't loaded, load it.
//this saves on memory requirements with mods that don't ever use this.
model = sv.models[(int)ent->v.modelindex] = Mod_ForName(sv.strings.model_precache[(int)ent->v.modelindex], false);
}
if (model && model->funcs.NativeTrace)
{
//do the second trace
TransformedTrace(model, hullnum, ent->v.frame, start, end, mins, maxs, &trace, ent->v.origin, ent->v.angles, MASK_WORLDSOLID);
}
}
if (trace.startsolid)
{
if (ent != &SVHL_Edict[0])
Con_Printf("Trace started solid\n");
}
}
// did we clip the move?
if (trace.fraction < 1 || trace.startsolid )
trace.ent = ent;
return trace;
}
#ifdef Q2BSPS
float *area_mins, *area_maxs;
hledict_t **area_list;
int area_count, area_maxcount;
void SVHL_AreaEdicts_r (areanode_t *node)
{
link_t *l, *next, *start;
hledict_t *check;
int count;
count = 0;
// touch linked edicts
start = &node->edicts;
for (l=start->next ; l != start ; l = next)
{
next = l->next;
check = HLEDICT_FROM_AREA(l);
// if (check->v.solid == SOLID_NOT)
// continue; // deactivated
if (check->v.absmin[0] > area_maxs[0]
|| check->v.absmin[1] > area_maxs[1]
|| check->v.absmin[2] > area_maxs[2]
|| check->v.absmax[0] < area_mins[0]
|| check->v.absmax[1] < area_mins[1]
|| check->v.absmax[2] < area_mins[2])
continue; // not touching
if (area_count == area_maxcount)
{
Con_Printf ("SV_AreaEdicts: MAXCOUNT\n");
return;
}
area_list[area_count] = check;
area_count++;
}
if (node->axis == -1)
return; // terminal node
// recurse down both sides
if ( area_maxs[node->axis] > node->dist )
SVHL_AreaEdicts_r ( node->children[0] );
if ( area_mins[node->axis] < node->dist )
SVHL_AreaEdicts_r ( node->children[1] );
}
/*
================
SV_AreaEdicts
================
*/
int SVHL_AreaEdicts (vec3_t mins, vec3_t maxs, hledict_t **list, int maxcount)
{
area_mins = mins;
area_maxs = maxs;
area_list = list;
area_count = 0;
area_maxcount = maxcount;
SVHL_AreaEdicts_r (sv.world.areanodes);
return area_count;
}
#endif
//===========================================================================
/*
====================
SV_ClipToEverything
like SV_ClipToLinks, but doesn't use the links part. This can be used for checking triggers, solid entities, not-solid entities.
Sounds pointless, I know.
====================
*/
void SVHL_ClipToEverything (hlmoveclip_t *clip)
{
int e;
trace_t trace;
hledict_t *touch;
for (e=1 ; e<sv.world.num_edicts ; e++)
{
touch = &SVHL_Edict[e];
if (touch->isfree)
continue;
if (touch->v.solid == SOLID_NOT && !((int)touch->v.flags & FL_FINDABLE_NONSOLID))
continue;
if (touch->v.solid == SOLID_TRIGGER && !((int)touch->v.flags & FL_FINDABLE_NONSOLID))
continue;
if (touch == clip->passedict)
continue;
if (clip->type & MOVE_NOMONSTERS && touch->v.solid != SOLID_BSP)
continue;
if (clip->passedict)
{
// don't clip corpse against character
if (clip->passedict->v.solid == SOLID_CORPSE && (touch->v.solid == SOLID_SLIDEBOX || touch->v.solid == SOLID_CORPSE))
continue;
// don't clip character against corpse
if (clip->passedict->v.solid == SOLID_SLIDEBOX && touch->v.solid == SOLID_CORPSE)
continue;
// if (!((int)clip->passedict->v.dimension_hit & (int)touch->v.dimension_solid))
// continue;
}
if (clip->boxmins[0] > touch->v.absmax[0]
|| clip->boxmins[1] > touch->v.absmax[1]
|| clip->boxmins[2] > touch->v.absmax[2]
|| clip->boxmaxs[0] < touch->v.absmin[0]
|| clip->boxmaxs[1] < touch->v.absmin[1]
|| clip->boxmaxs[2] < touch->v.absmin[2] )
continue;
if (clip->passedict && clip->passedict->v.size[0] && !touch->v.size[0])
continue; // points never interact
// might intersect, so do an exact clip
if (clip->trace.allsolid)
return;
if (clip->passedict)
{
if (touch->v.owner == clip->passedict)
continue; // don't clip against own missiles
if (clip->passedict->v.owner == touch)
continue; // don't clip against owner
}
if ((int)touch->v.flags & FL_MONSTER)
trace = SVHL_ClipMoveToEntity (touch, clip->start, clip->mins2, clip->maxs2, clip->end, clip->hullnum, clip->type & MOVE_HITMODEL, clip->clipmask);
else
trace = SVHL_ClipMoveToEntity (touch, clip->start, clip->mins, clip->maxs, clip->end, clip->hullnum, clip->type & MOVE_HITMODEL, clip->clipmask);
if (trace.allsolid || trace.startsolid ||
trace.fraction < clip->trace.fraction)
{
trace.ent = touch;
clip->trace = trace;
}
}
}
/*
====================
SV_ClipToLinks
Mins and maxs enclose the entire area swept by the move
====================
*/
void SVHL_ClipToLinks ( areanode_t *node, hlmoveclip_t *clip )
{
link_t *l, *next;
hledict_t *touch;
trace_t trace;
// touch linked edicts
for (l = node->edicts.next ; l != &node->edicts ; l = next)
{
next = l->next;
touch = HLEDICT_FROM_AREA(l);
if (touch->v.solid == SOLID_NOT)
continue;
if (touch == clip->passedict)
continue;
if (touch->v.solid == SOLID_TRIGGER)
continue;
if (clip->type & MOVE_NOMONSTERS && touch->v.solid != SOLID_BSP)
continue;
if (clip->passedict)
{
// don't clip corpse against character
if (clip->passedict->v.solid == SOLID_CORPSE && (touch->v.solid == SOLID_SLIDEBOX || touch->v.solid == SOLID_CORPSE))
continue;
// don't clip character against corpse
if (clip->passedict->v.solid == SOLID_SLIDEBOX && touch->v.solid == SOLID_CORPSE)
continue;
// if (!((int)clip->passedict->xv->dimension_hit & (int)touch->xv->dimension_solid))
// continue;
}
if (clip->boxmins[0] > touch->v.absmax[0]
|| clip->boxmins[1] > touch->v.absmax[1]
|| clip->boxmins[2] > touch->v.absmax[2]
|| clip->boxmaxs[0] < touch->v.absmin[0]
|| clip->boxmaxs[1] < touch->v.absmin[1]
|| clip->boxmaxs[2] < touch->v.absmin[2] )
continue;
if (clip->passedict && clip->passedict->v.size[0] && !touch->v.size[0])
continue; // points never interact
// might intersect, so do an exact clip
if (clip->trace.allsolid)
return;
if (clip->passedict)
{
if (touch->v.owner == clip->passedict)
continue; // don't clip against own missiles
if (clip->passedict->v.owner == touch)
continue; // don't clip against owner
}
if ((int)touch->v.flags & FL_MONSTER)
trace = SVHL_ClipMoveToEntity (touch, clip->start, clip->mins2, clip->maxs2, clip->end, clip->hullnum, clip->type & MOVE_HITMODEL, clip->clipmask);
else
trace = SVHL_ClipMoveToEntity (touch, clip->start, clip->mins, clip->maxs, clip->end, clip->hullnum, clip->type & MOVE_HITMODEL, clip->clipmask);
if (trace.allsolid || trace.startsolid ||
trace.fraction < clip->trace.fraction)
{
trace.ent = touch;
clip->trace = trace;
}
}
// recurse down both sides
if (node->axis == -1)
return;
if ( clip->boxmaxs[node->axis] > node->dist )
SVHL_ClipToLinks ( node->children[0], clip );
if ( clip->boxmins[node->axis] < node->dist )
SVHL_ClipToLinks ( node->children[1], clip );
}
static void SVHL_MoveBounds (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, vec3_t boxmins, vec3_t boxmaxs)
{
#if 0
// debug to test against everything
boxmins[0] = boxmins[1] = boxmins[2] = -9999;
boxmaxs[0] = boxmaxs[1] = boxmaxs[2] = 9999;
#else
int i;
for (i=0 ; i<3 ; i++)
{
if (end[i] > start[i])
{
boxmins[i] = start[i] + mins[i] - 1;
boxmaxs[i] = end[i] + maxs[i] + 1;
}
else
{
boxmins[i] = end[i] + mins[i] - 1;
boxmaxs[i] = start[i] + maxs[i] + 1;
}
}
#endif
}
/*
==================
SV_Move
==================
*/
trace_t SVHL_Move (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int type, int forcehull, hledict_t *passedict)
{
hlmoveclip_t clip;
int i;
int hullnum;
memset ( &clip, 0, sizeof ( clip ) );
if (forcehull)
hullnum = forcehull;
else if (sv_compatiblehulls.value)
hullnum = 0;
else
{
int diff;
int best;
hullnum = 0;
best = 8192;
//x/y pos/neg are assumed to be the same magnitute.
//z pos/height are assumed to be different from all the others.
for (i = 0; i < MAX_MAP_HULLSM; i++)
{
if (!sv.world.worldmodel->hulls[i].available)
continue;
#define sq(x) ((x)*(x))
diff = sq(sv.world.worldmodel->hulls[i].clip_maxs[2] - maxs[2]) +
sq(sv.world.worldmodel->hulls[i].clip_mins[2] - mins[2]) +
sq(sv.world.worldmodel->hulls[i].clip_maxs[1] - maxs[1]) +
sq(sv.world.worldmodel->hulls[i].clip_mins[0] - mins[0]);
if (diff < best)
{
best = diff;
hullnum=i;
}
}
hullnum++;
}
if (type & MOVE_NOMONSTERS)
clip.clipmask = MASK_WORLDSOLID; /*solid only to world*/
else if (maxs[0] - mins[0])
clip.clipmask = MASK_BOXSOLID; /*impacts playerclip*/
else
clip.clipmask = MASK_POINTSOLID; /*ignores playerclip but hits everything else*/
// clip to world
clip.trace = SVHL_ClipMoveToEntity ( &SVHL_Edict[0], start, mins, maxs, end, hullnum, false, clip.clipmask);
clip.start = start;
clip.end = end;
clip.mins = mins;
clip.maxs = maxs;
clip.type = type;
clip.passedict = passedict;
clip.hullnum = hullnum;
if (type & MOVE_MISSILE)
{
for (i=0 ; i<3 ; i++)
{
clip.mins2[i] = -15;
clip.maxs2[i] = 15;
}
}
else
{
VectorCopy (mins, clip.mins2);
VectorCopy (maxs, clip.maxs2);
}
// create the bounding box of the entire move
SVHL_MoveBounds ( start, clip.mins2, clip.maxs2, end, clip.boxmins, clip.boxmaxs );
// clip to entities
if (clip.type & MOVE_EVERYTHING)
SVHL_ClipToEverything (&clip);
else
SVHL_ClipToLinks ( sv.world.areanodes, &clip );
if (clip.trace.startsolid)
clip.trace.fraction = 0;
if (!clip.trace.ent)
return clip.trace;
return clip.trace;
}
#endif