diff options
| author | Travis Bradshaw <travis.bradshaw@idsoftware.com> | 2012-01-31 14:32:09 -0600 |
|---|---|---|
| committer | Travis Bradshaw <travis.bradshaw@idsoftware.com> | 2012-01-31 14:32:09 -0600 |
| commit | 0023db327bc1db00068284b70e1db45857aeee35 (patch) | |
| tree | 01dd8366d20bd674741cfe8ceb069d82a2e33973 /QW/server | |
| download | quake-pum-0023db327bc1db00068284b70e1db45857aeee35.tar.gz | |
The Quake sources as originally release under the GPL license on December 21, 1999
Diffstat (limited to 'QW/server')
37 files changed, 18351 insertions, 0 deletions
diff --git a/QW/server/asm_i386.h b/QW/server/asm_i386.h new file mode 100644 index 0000000..03dc2aa --- /dev/null +++ b/QW/server/asm_i386.h @@ -0,0 +1,97 @@ +/* +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. + +*/ + +#ifndef __ASM_I386__ +#define __ASM_I386__ + +#ifdef ELF +#define C(label) label +#else +#define C(label) _##label +#endif + +// +// !!! note that this file must match the corresponding C structures at all +// times !!! +// + +// plane_t structure +// !!! if this is changed, it must be changed in model.h too !!! +// !!! if the size of this is changed, the array lookup in SV_HullPointContents +// must be changed too !!! +#define pl_normal 0 +#define pl_dist 12 +#define pl_type 16 +#define pl_signbits 17 +#define pl_pad 18 +#define pl_size 20 + +// hull_t structure +// !!! if this is changed, it must be changed in model.h too !!! +#define hu_clipnodes 0 +#define hu_planes 4 +#define hu_firstclipnode 8 +#define hu_lastclipnode 12 +#define hu_clip_mins 16 +#define hu_clip_maxs 28 +#define hu_size 40 + +// dnode_t structure +// !!! if this is changed, it must be changed in bspfile.h too !!! +#define nd_planenum 0 +#define nd_children 4 +#define nd_mins 8 +#define nd_maxs 20 +#define nd_firstface 32 +#define nd_numfaces 36 +#define nd_size 40 + +// sfxcache_t structure +// !!! if this is changed, it much be changed in sound.h too !!! +#define sfxc_length 0 +#define sfxc_loopstart 4 +#define sfxc_speed 8 +#define sfxc_width 12 +#define sfxc_stereo 16 +#define sfxc_data 20 + +// channel_t structure +// !!! if this is changed, it much be changed in sound.h too !!! +#define ch_sfx 0 +#define ch_leftvol 4 +#define ch_rightvol 8 +#define ch_end 12 +#define ch_pos 16 +#define ch_looping 20 +#define ch_entnum 24 +#define ch_entchannel 28 +#define ch_origin 32 +#define ch_dist_mult 44 +#define ch_master_vol 48 +#define ch_size 52 + +// portable_samplepair_t structure +// !!! if this is changed, it much be changed in sound.h too !!! +#define psp_left 0 +#define psp_right 4 +#define psp_size 8 + +#endif + diff --git a/QW/server/makefile b/QW/server/makefile new file mode 100644 index 0000000..14cf0ca --- /dev/null +++ b/QW/server/makefile @@ -0,0 +1,52 @@ + +#CFLAGS = -g -Wall -DDEBUG -I../client -I. -DSERVERONLY +#CFLAGS = -mpentium -O6 -Wall -I../client -I. -DSERVERONLY -fomit-frame-pointer -fno-strength-reduce +#CFLAGS = -mpentium -O2 -Wall -I../client -I. -DSERVERONLY -fomit-frame-pointer -fno-strength-reduce +CFLAGS=-DSERVERONLY -I../client -V2.7.2.1 -bi486-linux/ -O6 -Wall -fomit-frame-pointer -fno-strength-reduce + +EXE = qwsv + +OFILES =\ + pr_cmds.o \ + pr_edict.o \ + pr_exec.o \ + sv_init.o \ + sv_main.o \ + sv_ents.o \ + sv_send.o \ + sv_move.o \ + sv_phys.o \ + sv_user.o \ + sv_ccmds.o \ + world.o \ + sys_unix.o \ + model.o \ + cmd.o \ + common.o \ + crc.o \ + cvar.o \ + mathlib.o \ + zone.o \ + pmove.o \ + pmovetst.o \ + net_chan.o \ + net_udp.o + +LDFLAGS = -lm + +$(EXE) : $(OFILES) + cc $(CFLAGS) -o $(EXE) $(OFILES) $(LDFLAGS) + +clean: + rm -f $(OFILES) $(EXE) + +app: + make "CFLAGS = -O4 -g -Wall -I../client -DSERVERONLY" + +profile: + make "CFLAGS = -g -pg -O -Wall -I../client -DPROFILE" + cp $(EXE) /LocalApps + +.c.o: ; cc -c $(CFLAGS) -o $@ $*.c +.s.o: ; cc -c $(CFLAGS) -o $@ $*.s + diff --git a/QW/server/math.s b/QW/server/math.s new file mode 100644 index 0000000..6b658a7 --- /dev/null +++ b/QW/server/math.s @@ -0,0 +1,331 @@ +// +// math.s +// x86 assembly-language math routines. + +#include "asm_i386.h" +#include "quakeasm.h" + + +#if id386 + + .data + + .align 4 +Ljmptab: .long Lcase0, Lcase1, Lcase2, Lcase3 + .long Lcase4, Lcase5, Lcase6, Lcase7 + + .text + + +#define EMINS 4+4 +#define EMAXS 4+8 +#define P 4+12 + + .align 2 +.globl C(BoxOnPlaneSide) +C(BoxOnPlaneSide): + pushl %ebx + + movl P(%esp),%edx + movl EMINS(%esp),%ecx + xorl %eax,%eax + movl EMAXS(%esp),%ebx + movb pl_signbits(%edx),%al + cmpl $8,%al + jge Lerror + flds pl_normal(%edx) // p->normal[0] + fld %st(0) // p->normal[0] | p->normal[0] + jmp *Ljmptab(,%eax,4) + + +//dist1= p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; +//dist2= p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; +Lcase0: + fmuls (%ebx) // p->normal[0]*emaxs[0] | p->normal[0] + flds pl_normal+4(%edx) // p->normal[1] | p->normal[0]*emaxs[0] | + // p->normal[0] + fxch %st(2) // p->normal[0] | p->normal[0]*emaxs[0] | + // p->normal[1] + fmuls (%ecx) // p->normal[0]*emins[0] | + // p->normal[0]*emaxs[0] | p->normal[1] + fxch %st(2) // p->normal[1] | p->normal[0]*emaxs[0] | + // p->normal[0]*emins[0] + fld %st(0) // p->normal[1] | p->normal[1] | + // p->normal[0]*emaxs[0] | + // p->normal[0]*emins[0] + fmuls 4(%ebx) // p->normal[1]*emaxs[1] | p->normal[1] | + // p->normal[0]*emaxs[0] | + // p->normal[0]*emins[0] + flds pl_normal+8(%edx) // p->normal[2] | p->normal[1]*emaxs[1] | + // p->normal[1] | p->normal[0]*emaxs[0] | + // p->normal[0]*emins[0] + fxch %st(2) // p->normal[1] | p->normal[1]*emaxs[1] | + // p->normal[2] | p->normal[0]*emaxs[0] | + // p->normal[0]*emins[0] + fmuls 4(%ecx) // p->normal[1]*emins[1] | + // p->normal[1]*emaxs[1] | + // p->normal[2] | p->normal[0]*emaxs[0] | + // p->normal[0]*emins[0] + fxch %st(2) // p->normal[2] | p->normal[1]*emaxs[1] | + // p->normal[1]*emins[1] | + // p->normal[0]*emaxs[0] | + // p->normal[0]*emins[0] + fld %st(0) // p->normal[2] | p->normal[2] | + // p->normal[1]*emaxs[1] | + // p->normal[1]*emins[1] | + // p->normal[0]*emaxs[0] | + // p->normal[0]*emins[0] + fmuls 8(%ebx) // p->normal[2]*emaxs[2] | + // p->normal[2] | + // p->normal[1]*emaxs[1] | + // p->normal[1]*emins[1] | + // p->normal[0]*emaxs[0] | + // p->normal[0]*emins[0] + fxch %st(5) // p->normal[0]*emins[0] | + // p->normal[2] | + // p->normal[1]*emaxs[1] | + // p->normal[1]*emins[1] | + // p->normal[0]*emaxs[0] | + // p->normal[2]*emaxs[2] + faddp %st(0),%st(3) //p->normal[2] | + // p->normal[1]*emaxs[1] | + // p->normal[1]*emins[1]+p->normal[0]*emins[0]| + // p->normal[0]*emaxs[0] | + // p->normal[2]*emaxs[2] + fmuls 8(%ecx) //p->normal[2]*emins[2] | + // p->normal[1]*emaxs[1] | + // p->normal[1]*emins[1]+p->normal[0]*emins[0]| + // p->normal[0]*emaxs[0] | + // p->normal[2]*emaxs[2] + fxch %st(1) //p->normal[1]*emaxs[1] | + // p->normal[2]*emins[2] | + // p->normal[1]*emins[1]+p->normal[0]*emins[0]| + // p->normal[0]*emaxs[0] | + // p->normal[2]*emaxs[2] + faddp %st(0),%st(3) //p->normal[2]*emins[2] | + // p->normal[1]*emins[1]+p->normal[0]*emins[0]| + // p->normal[0]*emaxs[0]+p->normal[1]*emaxs[1]| + // p->normal[2]*emaxs[2] + fxch %st(3) //p->normal[2]*emaxs[2] + + // p->normal[1]*emins[1]+p->normal[0]*emins[0]| + // p->normal[0]*emaxs[0]+p->normal[1]*emaxs[1]| + // p->normal[2]*emins[2] + faddp %st(0),%st(2) //p->normal[1]*emins[1]+p->normal[0]*emins[0]| + // dist1 | p->normal[2]*emins[2] + + jmp LSetSides + +//dist1= p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; +//dist2= p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; +Lcase1: + fmuls (%ecx) // emins[0] + flds pl_normal+4(%edx) + fxch %st(2) + fmuls (%ebx) // emaxs[0] + fxch %st(2) + fld %st(0) + fmuls 4(%ebx) // emaxs[1] + flds pl_normal+8(%edx) + fxch %st(2) + fmuls 4(%ecx) // emins[1] + fxch %st(2) + fld %st(0) + fmuls 8(%ebx) // emaxs[2] + fxch %st(5) + faddp %st(0),%st(3) + fmuls 8(%ecx) // emins[2] + fxch %st(1) + faddp %st(0),%st(3) + fxch %st(3) + faddp %st(0),%st(2) + + jmp LSetSides + +//dist1= p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; +//dist2= p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; +Lcase2: + fmuls (%ebx) // emaxs[0] + flds pl_normal+4(%edx) + fxch %st(2) + fmuls (%ecx) // emins[0] + fxch %st(2) + fld %st(0) + fmuls 4(%ecx) // emins[1] + flds pl_normal+8(%edx) + fxch %st(2) + fmuls 4(%ebx) // emaxs[1] + fxch %st(2) + fld %st(0) + fmuls 8(%ebx) // emaxs[2] + fxch %st(5) + faddp %st(0),%st(3) + fmuls 8(%ecx) // emins[2] + fxch %st(1) + faddp %st(0),%st(3) + fxch %st(3) + faddp %st(0),%st(2) + + jmp LSetSides + +//dist1= p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; +//dist2= p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; +Lcase3: + fmuls (%ecx) // emins[0] + flds pl_normal+4(%edx) + fxch %st(2) + fmuls (%ebx) // emaxs[0] + fxch %st(2) + fld %st(0) + fmuls 4(%ecx) // emins[1] + flds pl_normal+8(%edx) + fxch %st(2) + fmuls 4(%ebx) // emaxs[1] + fxch %st(2) + fld %st(0) + fmuls 8(%ebx) // emaxs[2] + fxch %st(5) + faddp %st(0),%st(3) + fmuls 8(%ecx) // emins[2] + fxch %st(1) + faddp %st(0),%st(3) + fxch %st(3) + faddp %st(0),%st(2) + + jmp LSetSides + +//dist1= p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; +//dist2= p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; +Lcase4: + fmuls (%ebx) // emaxs[0] + flds pl_normal+4(%edx) + fxch %st(2) + fmuls (%ecx) // emins[0] + fxch %st(2) + fld %st(0) + fmuls 4(%ebx) // emaxs[1] + flds pl_normal+8(%edx) + fxch %st(2) + fmuls 4(%ecx) // emins[1] + fxch %st(2) + fld %st(0) + fmuls 8(%ecx) // emins[2] + fxch %st(5) + faddp %st(0),%st(3) + fmuls 8(%ebx) // emaxs[2] + fxch %st(1) + faddp %st(0),%st(3) + fxch %st(3) + faddp %st(0),%st(2) + + jmp LSetSides + +//dist1= p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; +//dist2= p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; +Lcase5: + fmuls (%ecx) // emins[0] + flds pl_normal+4(%edx) + fxch %st(2) + fmuls (%ebx) // emaxs[0] + fxch %st(2) + fld %st(0) + fmuls 4(%ebx) // emaxs[1] + flds pl_normal+8(%edx) + fxch %st(2) + fmuls 4(%ecx) // emins[1] + fxch %st(2) + fld %st(0) + fmuls 8(%ecx) // emins[2] + fxch %st(5) + faddp %st(0),%st(3) + fmuls 8(%ebx) // emaxs[2] + fxch %st(1) + faddp %st(0),%st(3) + fxch %st(3) + faddp %st(0),%st(2) + + jmp LSetSides + +//dist1= p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; +//dist2= p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; +Lcase6: + fmuls (%ebx) // emaxs[0] + flds pl_normal+4(%edx) + fxch %st(2) + fmuls (%ecx) // emins[0] + fxch %st(2) + fld %st(0) + fmuls 4(%ecx) // emins[1] + flds pl_normal+8(%edx) + fxch %st(2) + fmuls 4(%ebx) // emaxs[1] + fxch %st(2) + fld %st(0) + fmuls 8(%ecx) // emins[2] + fxch %st(5) + faddp %st(0),%st(3) + fmuls 8(%ebx) // emaxs[2] + fxch %st(1) + faddp %st(0),%st(3) + fxch %st(3) + faddp %st(0),%st(2) + + jmp LSetSides + +//dist1= p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; +//dist2= p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; +Lcase7: + fmuls (%ecx) // emins[0] + flds pl_normal+4(%edx) + fxch %st(2) + fmuls (%ebx) // emaxs[0] + fxch %st(2) + fld %st(0) + fmuls 4(%ecx) // emins[1] + flds pl_normal+8(%edx) + fxch %st(2) + fmuls 4(%ebx) // emaxs[1] + fxch %st(2) + fld %st(0) + fmuls 8(%ecx) // emins[2] + fxch %st(5) + faddp %st(0),%st(3) + fmuls 8(%ebx) // emaxs[2] + fxch %st(1) + faddp %st(0),%st(3) + fxch %st(3) + faddp %st(0),%st(2) + +LSetSides: + +// sides = 0; +// if (dist1 >= p->dist) +// sides = 1; +// if (dist2 < p->dist) +// sides |= 2; + + faddp %st(0),%st(2) // dist1 | dist2 + fcomps pl_dist(%edx) + xorl %ecx,%ecx + fnstsw %ax + fcomps pl_dist(%edx) + andb $1,%ah + xorb $1,%ah + addb %ah,%cl + + fnstsw %ax + andb $1,%ah + addb %ah,%ah + addb %ah,%cl + +// return sides; + + popl %ebx + movl %ecx,%eax // return status + + ret + + +Lerror: + call C(BOPS_Error) + +#endif // id386 diff --git a/QW/server/model.c b/QW/server/model.c new file mode 100644 index 0000000..bb24c8d --- /dev/null +++ b/QW/server/model.c @@ -0,0 +1,1137 @@ +/* +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. + +*/ +// models.c -- model loading and caching + +// models are the only shared resource between a client and server running +// on the same machine. + +#include "qwsvdef.h" + +model_t *loadmodel; +char loadname[32]; // for hunk tags + +void Mod_LoadSpriteModel (model_t *mod, void *buffer); +void Mod_LoadBrushModel (model_t *mod, void *buffer); +void Mod_LoadAliasModel (model_t *mod, void *buffer); +model_t *Mod_LoadModel (model_t *mod, qboolean crash); + +byte mod_novis[MAX_MAP_LEAFS/8]; + +#define MAX_MOD_KNOWN 256 +model_t mod_known[MAX_MOD_KNOWN]; +int mod_numknown; + +texture_t r_notexture_mip; + +unsigned *model_checksum; + +/* +=============== +Mod_Init +=============== +*/ +void Mod_Init (void) +{ + memset (mod_novis, 0xff, sizeof(mod_novis)); +} + +/* +=============== +Mod_PointInLeaf +=============== +*/ +mleaf_t *Mod_PointInLeaf (vec3_t p, model_t *model) +{ + mnode_t *node; + float d; + mplane_t *plane; + + if (!model || !model->nodes) + SV_Error ("Mod_PointInLeaf: bad model"); + + node = model->nodes; + while (1) + { + if (node->contents < 0) + return (mleaf_t *)node; + plane = node->plane; + d = DotProduct (p,plane->normal) - plane->dist; + if (d > 0) + node = node->children[0]; + else + node = node->children[1]; + } + + return NULL; // never reached +} + + +/* +=================== +Mod_DecompressVis +=================== +*/ +byte *Mod_DecompressVis (byte *in, model_t *model) +{ + static byte decompressed[MAX_MAP_LEAFS/8]; + int c; + byte *out; + int row; + + row = (model->numleafs+7)>>3; + out = decompressed; + +#if 0 + memcpy (out, in, row); +#else + if (!in) + { // no vis info, so make all visible + while (row) + { + *out++ = 0xff; + row--; + } + return decompressed; + } + + do + { + if (*in) + { + *out++ = *in++; + continue; + } + + c = in[1]; + in += 2; + while (c) + { + *out++ = 0; + c--; + } + } while (out - decompressed < row); +#endif + + return decompressed; +} + +byte *Mod_LeafPVS (mleaf_t *leaf, model_t *model) +{ + if (leaf == model->leafs) + return mod_novis; + return Mod_DecompressVis (leaf->compressed_vis, model); +} + +/* +=================== +Mod_ClearAll +=================== +*/ +void Mod_ClearAll (void) +{ + int i; + model_t *mod; + + for (i=0 , mod=mod_known ; i<mod_numknown ; i++, mod++) + if (mod->type != mod_alias) + mod->needload = true; +} + +/* +================== +Mod_FindName + +================== +*/ +model_t *Mod_FindName (char *name) +{ + int i; + model_t *mod; + + if (!name[0]) + SV_Error ("Mod_ForName: NULL name"); + +// +// search the currently loaded models +// + for (i=0 , mod=mod_known ; i<mod_numknown ; i++, mod++) + if (!strcmp (mod->name, name) ) + break; + + if (i == mod_numknown) + { + if (mod_numknown == MAX_MOD_KNOWN) + SV_Error ("mod_numknown == MAX_MOD_KNOWN"); + strcpy (mod->name, name); + mod->needload = true; + mod_numknown++; + } + + return mod; +} + + +/* +================== +Mod_LoadModel + +Loads a model into the cache +================== +*/ +model_t *Mod_LoadModel (model_t *mod, qboolean crash) +{ + void *d; + unsigned *buf; + byte stackbuf[1024]; // avoid dirtying the cache heap + + if (!mod->needload) + { + if (mod->type == mod_alias) + { + d = Cache_Check (&mod->cache); + if (d) + return mod; + } + else + return mod; // not cached at all + } + +// +// load the file +// + buf = (unsigned *)COM_LoadStackFile (mod->name, stackbuf, sizeof(stackbuf)); + if (!buf) + { + if (crash) + SV_Error ("Mod_NumForName: %s not found", mod->name); + return NULL; + } + +// +// allocate a new model +// + COM_FileBase (mod->name, loadname); + + loadmodel = mod; + +// +// fill it in +// + +// call the apropriate loader + mod->needload = false; + + Mod_LoadBrushModel (mod, buf); + + return mod; +} + +/* +================== +Mod_ForName + +Loads in a model for the given name +================== +*/ +model_t *Mod_ForName (char *name, qboolean crash) +{ + model_t *mod; + + mod = Mod_FindName (name); + + return Mod_LoadModel (mod, crash); +} + + +/* +=============================================================================== + + BRUSHMODEL LOADING + +=============================================================================== +*/ + +byte *mod_base; + + +/* +================= +Mod_LoadTextures +================= +*/ +void Mod_LoadTextures (lump_t *l) +{ + int i, j, pixels, num, max, altmax; + miptex_t *mt; + texture_t *tx, *tx2; + texture_t *anims[10]; + texture_t *altanims[10]; + dmiptexlump_t *m; + + if (!l->filelen) + { + loadmodel->textures = NULL; + return; + } + m = (dmiptexlump_t *)(mod_base + l->fileofs); + + m->nummiptex = LittleLong (m->nummiptex); + + loadmodel->numtextures = m->nummiptex; + loadmodel->textures = Hunk_AllocName (m->nummiptex * sizeof(*loadmodel->textures) , loadname); + + for (i=0 ; i<m->nummiptex ; i++) + { + m->dataofs[i] = LittleLong(m->dataofs[i]); + if (m->dataofs[i] == -1) + continue; + mt = (miptex_t *)((byte *)m + m->dataofs[i]); + mt->width = LittleLong (mt->width); + mt->height = LittleLong (mt->height); + for (j=0 ; j<MIPLEVELS ; j++) + mt->offsets[j] = LittleLong (mt->offsets[j]); + + if ( (mt->width & 15) || (mt->height & 15) ) + SV_Error ("Texture %s is not 16 aligned", mt->name); + pixels = mt->width*mt->height/64*85; + tx = Hunk_AllocName (sizeof(texture_t) +pixels, loadname ); + loadmodel->textures[i] = tx; + + memcpy (tx->name, mt->name, sizeof(tx->name)); + tx->width = mt->width; + tx->height = mt->height; + for (j=0 ; j<MIPLEVELS ; j++) + tx->offsets[j] = mt->offsets[j] + sizeof(texture_t) - sizeof(miptex_t); + // the pixels immediately follow the structures + memcpy ( tx+1, mt+1, pixels); + } + +// +// sequence the animations +// + for (i=0 ; i<m->nummiptex ; i++) + { + tx = loadmodel->textures[i]; + if (!tx || tx->name[0] != '+') + continue; + if (tx->anim_next) + continue; // allready sequenced + + // find the number of frames in the animation + memset (anims, 0, sizeof(anims)); + memset (altanims, 0, sizeof(altanims)); + + max = tx->name[1]; + altmax = 0; + if (max >= 'a' && max <= 'z') + max -= 'a' - 'A'; + if (max >= '0' && max <= '9') + { + max -= '0'; + altmax = 0; + anims[max] = tx; + max++; + } + else if (max >= 'A' && max <= 'J') + { + altmax = max - 'A'; + max = 0; + altanims[altmax] = tx; + altmax++; + } + else + SV_Error ("Bad animating texture %s", tx->name); + + for (j=i+1 ; j<m->nummiptex ; j++) + { + tx2 = loadmodel->textures[j]; + if (!tx2 || tx2->name[0] != '+') + continue; + if (strcmp (tx2->name+2, tx->name+2)) + continue; + + num = tx2->name[1]; + if (num >= 'a' && num <= 'z') + num -= 'a' - 'A'; + if (num >= '0' && num <= '9') + { + num -= '0'; + anims[num] = tx2; + if (num+1 > max) + max = num + 1; + } + else if (num >= 'A' && num <= 'J') + { + num = num - 'A'; + altanims[num] = tx2; + if (num+1 > altmax) + altmax = num+1; + } + else + SV_Error ("Bad animating texture %s", tx->name); + } + +#define ANIM_CYCLE 2 + // link them all together + for (j=0 ; j<max ; j++) + { + tx2 = anims[j]; + if (!tx2) + SV_Error ("Missing frame %i of %s",j, tx->name); + tx2->anim_total = max * ANIM_CYCLE; + tx2->anim_min = j * ANIM_CYCLE; + tx2->anim_max = (j+1) * ANIM_CYCLE; + tx2->anim_next = anims[ (j+1)%max ]; + if (altmax) + tx2->alternate_anims = altanims[0]; + } + for (j=0 ; j<altmax ; j++) + { + tx2 = altanims[j]; + if (!tx2) + SV_Error ("Missing frame %i of %s",j, tx->name); + tx2->anim_total = altmax * ANIM_CYCLE; + tx2->anim_min = j * ANIM_CYCLE; + tx2->anim_max = (j+1) * ANIM_CYCLE; + tx2->anim_next = altanims[ (j+1)%altmax ]; + if (max) + tx2->alternate_anims = anims[0]; + } + } +} + +/* +================= +Mod_LoadLighting +================= +*/ +void Mod_LoadLighting (lump_t *l) +{ + if (!l->filelen) + { + loadmodel->lightdata = NULL; + return; + } + loadmodel->lightdata = Hunk_AllocName ( l->filelen, loadname); + memcpy (loadmodel->lightdata, mod_base + l->fileofs, l->filelen); +} + + +/* +================= +Mod_LoadVisibility +================= +*/ +void Mod_LoadVisibility (lump_t *l) +{ + if (!l->filelen) + { + loadmodel->visdata = NULL; + return; + } + loadmodel->visdata = Hunk_AllocName ( l->filelen, loadname); + memcpy (loadmodel->visdata, mod_base + l->fileofs, l->filelen); +} + + +/* +================= +Mod_LoadEntities +================= +*/ +void Mod_LoadEntities (lump_t *l) +{ + if (!l->filelen) + { + loadmodel->entities = NULL; + return; + } + loadmodel->entities = Hunk_AllocName ( l->filelen, loadname); + memcpy (loadmodel->entities, mod_base + l->fileofs, l->filelen); +} + + +/* +================= +Mod_LoadVertexes +================= +*/ +void Mod_LoadVertexes (lump_t *l) +{ + dvertex_t *in; + mvertex_t *out; + int i, count; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + SV_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_AllocName ( count*sizeof(*out), loadname); + + loadmodel->vertexes = out; + loadmodel->numvertexes = count; + + for ( i=0 ; i<count ; i++, in++, out++) + { + out->position[0] = LittleFloat (in->point[0]); + out->position[1] = LittleFloat (in->point[1]); + out->position[2] = LittleFloat (in->point[2]); + } +} + +/* +================= +Mod_LoadSubmodels +================= +*/ +void Mod_LoadSubmodels (lump_t *l) +{ + dmodel_t *in; + dmodel_t *out; + int i, j, count; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + SV_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_AllocName ( count*sizeof(*out), loadname); + + loadmodel->submodels = out; + loadmodel->numsubmodels = count; + + for ( i=0 ; i<count ; i++, in++, out++) + { + for (j=0 ; j<3 ; j++) + { // spread the mins / maxs by a pixel + out->mins[j] = LittleFloat (in->mins[j]) - 1; + out->maxs[j] = LittleFloat (in->maxs[j]) + 1; + out->origin[j] = LittleFloat (in->origin[j]); + } + for (j=0 ; j<MAX_MAP_HULLS ; j++) + out->headnode[j] = LittleLong (in->headnode[j]); + out->visleafs = LittleLong (in->visleafs); + out->firstface = LittleLong (in->firstface); + out->numfaces = LittleLong (in->numfaces); + } +} + +/* +================= +Mod_LoadEdges +================= +*/ +void Mod_LoadEdges (lump_t *l) +{ + dedge_t *in; + medge_t *out; + int i, count; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + SV_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_AllocName ( (count + 1) * sizeof(*out), loadname); + + loadmodel->edges = out; + loadmodel->numedges = count; + + for ( i=0 ; i<count ; i++, in++, out++) + { + out->v[0] = (unsigned short)LittleShort(in->v[0]); + out->v[1] = (unsigned short)LittleShort(in->v[1]); + } +} + +/* +================= +Mod_LoadTexinfo +================= +*/ +void Mod_LoadTexinfo (lump_t *l) +{ + texinfo_t *in; + mtexinfo_t *out; + int i, j, count; + int miptex; + float len1, len2; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + SV_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_AllocName ( count*sizeof(*out), loadname); + + loadmodel->texinfo = out; + loadmodel->numtexinfo = count; + + for ( i=0 ; i<count ; i++, in++, out++) + { +#if 0 + for (j=0 ; j<8 ; j++) + out->vecs[0][j] = LittleFloat (in->vecs[0][j]); + len1 = Length (in->vecs[0]); + len2 = Length (in->vecs[1]); +#else + for (j=0 ; j<4 ; j++) { + out->vecs[0][j] = LittleFloat (in->vecs[0][j]); + out->vecs[1][j] = LittleFloat (in->vecs[1][j]); + } + len1 = Length (out->vecs[0]); + len2 = Length (out->vecs[1]); +#endif + if (len1 + len2 < 2 /*0.001*/) + out->mipadjust = 1; + else + out->mipadjust = 1 / floor( (len1+len2)/2 ); + + miptex = LittleLong (in->miptex); + out->flags = LittleLong (in->flags); + + if (!loadmodel->textures) + { + out->texture = &r_notexture_mip; // checkerboard texture + out->flags = 0; + } + else + { + if (miptex >= loadmodel->numtextures) + SV_Error ("miptex >= loadmodel->numtextures"); + out->texture = loadmodel->textures[miptex]; + if (!out->texture) + { + out->texture = &r_notexture_mip; // texture not found + out->flags = 0; + } + } + } +} + +/* +================ +CalcSurfaceExtents + +Fills in s->texturemins[] and s->extents[] +================ +*/ +void CalcSurfaceExtents (msurface_t *s) +{ + float mins[2], maxs[2], val; + int i,j, e; + mvertex_t *v; + mtexinfo_t *tex; + int bmins[2], bmaxs[2]; + + mins[0] = mins[1] = 999999; + maxs[0] = maxs[1] = -99999; + + tex = s->texinfo; + + for (i=0 ; i<s->numedges ; i++) + { + e = loadmodel->surfedges[s->firstedge+i]; + if (e >= 0) + v = &loadmodel->vertexes[loadmodel->edges[e].v[0]]; + else + v = &loadmodel->vertexes[loadmodel->edges[-e].v[1]]; + + for (j=0 ; j<2 ; j++) + { + val = v->position[0] * tex->vecs[j][0] + + v->position[1] * tex->vecs[j][1] + + v->position[2] * tex->vecs[j][2] + + tex->vecs[j][3]; + if (val < mins[j]) + mins[j] = val; + if (val > maxs[j]) + maxs[j] = val; + } + } + + for (i=0 ; i<2 ; i++) + { + bmins[i] = floor(mins[i]/16); + bmaxs[i] = ceil(maxs[i]/16); + + s->texturemins[i] = bmins[i] * 16; + s->extents[i] = (bmaxs[i] - bmins[i]) * 16; + if ( !(tex->flags & TEX_SPECIAL) && s->extents[i] > 256) + SV_Error ("Bad surface extents"); + } +} + + +/* +================= +Mod_LoadFaces +================= +*/ +void Mod_LoadFaces (lump_t *l) +{ + dface_t *in; + msurface_t *out; + int i, count, surfnum; + int planenum, side; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + SV_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_AllocName ( count*sizeof(*out), loadname); + + loadmodel->surfaces = out; + loadmodel->numsurfaces = count; + + for ( surfnum=0 ; surfnum<count ; surfnum++, in++, out++) + { + out->firstedge = LittleLong(in->firstedge); + out->numedges = LittleShort(in->numedges); + out->flags = 0; + + planenum = LittleShort(in->planenum); + side = LittleShort(in->side); + if (side) + out->flags |= SURF_PLANEBACK; + + out->plane = loadmodel->planes + planenum; + + out->texinfo = loadmodel->texinfo + LittleShort (in->texinfo); + + CalcSurfaceExtents (out); + + // lighting info + + for (i=0 ; i<MAXLIGHTMAPS ; i++) + out->styles[i] = in->styles[i]; + i = LittleLong(in->lightofs); + if (i == -1) + out->samples = NULL; + else + out->samples = loadmodel->lightdata + i; + + // set the drawing flags flag + + if (!Q_strncmp(out->texinfo->texture->name,"sky",3)) // sky + { + out->flags |= (SURF_DRAWSKY | SURF_DRAWTILED); + continue; + } + + if (!Q_strncmp(out->texinfo->texture->name,"*",1)) // turbulent + { + out->flags |= (SURF_DRAWTURB | SURF_DRAWTILED); + for (i=0 ; i<2 ; i++) + { + out->extents[i] = 16384; + out->texturemins[i] = -8192; + } + continue; + } + } +} + + +/* +================= +Mod_SetParent +================= +*/ +void Mod_SetParent (mnode_t *node, mnode_t *parent) +{ + node->parent = parent; + if (node->contents < 0) + return; + Mod_SetParent (node->children[0], node); + Mod_SetParent (node->children[1], node); +} + +/* +================= +Mod_LoadNodes +================= +*/ +void Mod_LoadNodes (lump_t *l) +{ + int i, j, count, p; + dnode_t *in; + mnode_t *out; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + SV_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_AllocName ( count*sizeof(*out), loadname); + + loadmodel->nodes = out; + loadmodel->numnodes = count; + + for ( i=0 ; i<count ; i++, in++, out++) + { + for (j=0 ; j<3 ; j++) + { + out->minmaxs[j] = LittleShort (in->mins[j]); + out->minmaxs[3+j] = LittleShort (in->maxs[j]); + } + + p = LittleLong(in->planenum); + out->plane = loadmodel->planes + p; + + out->firstsurface = LittleShort (in->firstface); + out->numsurfaces = LittleShort (in->numfaces); + + for (j=0 ; j<2 ; j++) + { + p = LittleShort (in->children[j]); + if (p >= 0) + out->children[j] = loadmodel->nodes + p; + else + out->children[j] = (mnode_t *)(loadmodel->leafs + (-1 - p)); + } + } + + Mod_SetParent (loadmodel->nodes, NULL); // sets nodes and leafs +} + +/* +================= +Mod_LoadLeafs +================= +*/ +void Mod_LoadLeafs (lump_t *l) +{ + dleaf_t *in; + mleaf_t *out; + int i, j, count, p; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + SV_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_AllocName ( count*sizeof(*out), loadname); + + loadmodel->leafs = out; + loadmodel->numleafs = count; + + for ( i=0 ; i<count ; i++, in++, out++) + { + for (j=0 ; j<3 ; j++) + { + out->minmaxs[j] = LittleShort (in->mins[j]); + out->minmaxs[3+j] = LittleShort (in->maxs[j]); + } + + p = LittleLong(in->contents); + out->contents = p; + + out->firstmarksurface = loadmodel->marksurfaces + + LittleShort(in->firstmarksurface); + out->nummarksurfaces = LittleShort(in->nummarksurfaces); + + p = LittleLong(in->visofs); + if (p == -1) + out->compressed_vis = NULL; + else + out->compressed_vis = loadmodel->visdata + p; + out->efrags = NULL; + + for (j=0 ; j<4 ; j++) + out->ambient_sound_level[j] = in->ambient_level[j]; + } +} + +/* +================= +Mod_LoadClipnodes +================= +*/ +void Mod_LoadClipnodes (lump_t *l) +{ + dclipnode_t *in, *out; + int i, count; + hull_t *hull; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + SV_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_AllocName ( count*sizeof(*out), loadname); + + loadmodel->clipnodes = out; + loadmodel->numclipnodes = count; + + hull = &loadmodel->hulls[1]; + hull->clipnodes = out; + hull->firstclipnode = 0; + hull->lastclipnode = count-1; + hull->planes = loadmodel->planes; + hull->clip_mins[0] = -16; + hull->clip_mins[1] = -16; + hull->clip_mins[2] = -24; + hull->clip_maxs[0] = 16; + hull->clip_maxs[1] = 16; + hull->clip_maxs[2] = 32; + + hull = &loadmodel->hulls[2]; + hull->clipnodes = out; + hull->firstclipnode = 0; + hull->lastclipnode = count-1; + hull->planes = loadmodel->planes; + hull->clip_mins[0] = -32; + hull->clip_mins[1] = -32; + hull->clip_mins[2] = -24; + hull->clip_maxs[0] = 32; + hull->clip_maxs[1] = 32; + hull->clip_maxs[2] = 64; + + for (i=0 ; i<count ; i++, out++, in++) + { + out->planenum = LittleLong(in->planenum); + out->children[0] = LittleShort(in->children[0]); + out->children[1] = LittleShort(in->children[1]); + } +} + +/* +================= +Mod_MakeHull0 + +Deplicate the drawing hull structure as a clipping hull +================= +*/ +void Mod_MakeHull0 (void) +{ + mnode_t *in, *child; + dclipnode_t *out; + int i, j, count; + hull_t *hull; + + hull = &loadmodel->hulls[0]; + + in = loadmodel->nodes; + count = loadmodel->numnodes; + out = Hunk_AllocName ( count*sizeof(*out), loadname); + + hull->clipnodes = out; + hull->firstclipnode = 0; + hull->lastclipnode = count-1; + hull->planes = loadmodel->planes; + + for (i=0 ; i<count ; i++, out++, in++) + { + out->planenum = in->plane - loadmodel->planes; + for (j=0 ; j<2 ; j++) + { + child = in->children[j]; + if (child->contents < 0) + out->children[j] = child->contents; + else + out->children[j] = child - loadmodel->nodes; + } + } +} + +/* +================= +Mod_LoadMarksurfaces +================= +*/ +void Mod_LoadMarksurfaces (lump_t *l) +{ + int i, j, count; + short *in; + msurface_t **out; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + SV_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_AllocName ( count*sizeof(*out), loadname); + + loadmodel->marksurfaces = out; + loadmodel->nummarksurfaces = count; + + for ( i=0 ; i<count ; i++) + { + j = LittleShort(in[i]); + if (j >= loadmodel->numsurfaces) + SV_Error ("Mod_ParseMarksurfaces: bad surface number"); + out[i] = loadmodel->surfaces + j; + } +} + +/* +================= +Mod_LoadSurfedges +================= +*/ +void Mod_LoadSurfedges (lump_t *l) +{ + int i, count; + int *in, *out; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + SV_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_AllocName ( count*sizeof(*out), loadname); + + loadmodel->surfedges = out; + loadmodel->numsurfedges = count; + + for ( i=0 ; i<count ; i++) + out[i] = LittleLong (in[i]); +} + +/* +================= +Mod_LoadPlanes +================= +*/ +void Mod_LoadPlanes (lump_t *l) +{ + int i, j; + mplane_t *out; + dplane_t *in; + int count; + int bits; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + SV_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_AllocName ( count*2*sizeof(*out), loadname); + + loadmodel->planes = out; + loadmodel->numplanes = count; + + for ( i=0 ; i<count ; i++, in++, out++) + { + bits = 0; + for (j=0 ; j<3 ; j++) + { + out->normal[j] = LittleFloat (in->normal[j]); + if (out->normal[j] < 0) + bits |= 1<<j; + } + + out->dist = LittleFloat (in->dist); + out->type = LittleLong (in->type); + out->signbits = bits; + } +} + + +/* +================= +Mod_LoadBrushModel +================= +*/ +void Mod_LoadBrushModel (model_t *mod, void *buffer) +{ + int i, j; + dheader_t *header; + dmodel_t *bm; + + loadmodel->type = mod_brush; + + header = (dheader_t *)buffer; + + i = LittleLong (header->version); + if (i != BSPVERSION) + SV_Error ("Mod_LoadBrushModel: %s has wrong version number (%i should be %i)", mod->name, i, BSPVERSION); + +// swap all the lumps + mod_base = (byte *)header; + + for (i=0 ; i<sizeof(dheader_t)/4 ; i++) + ((int *)header)[i] = LittleLong ( ((int *)header)[i]); + +// load into heap + + mod->checksum = 0; + mod->checksum2 = 0; + + // checksum all of the map, except for entities + for (i = 0; i < HEADER_LUMPS; i++) { + if (i == LUMP_ENTITIES) + continue; + mod->checksum ^= LittleLong(Com_BlockChecksum(mod_base + header->lumps[i].fileofs, + header->lumps[i].filelen)); + + if (i == LUMP_VISIBILITY || i == LUMP_LEAFS || i == LUMP_NODES) + continue; + mod->checksum2 ^= LittleLong(Com_BlockChecksum(mod_base + header->lumps[i].fileofs, + header->lumps[i].filelen)); + } + + Mod_LoadVertexes (&header->lumps[LUMP_VERTEXES]); + Mod_LoadEdges (&header->lumps[LUMP_EDGES]); + Mod_LoadSurfedges (&header->lumps[LUMP_SURFEDGES]); + Mod_LoadTextures (&header->lumps[LUMP_TEXTURES]); + Mod_LoadLighting (&header->lumps[LUMP_LIGHTING]); + Mod_LoadPlanes (&header->lumps[LUMP_PLANES]); + Mod_LoadTexinfo (&header->lumps[LUMP_TEXINFO]); + Mod_LoadFaces (&header->lumps[LUMP_FACES]); + Mod_LoadMarksurfaces (&header->lumps[LUMP_MARKSURFACES]); + Mod_LoadVisibility (&header->lumps[LUMP_VISIBILITY]); + Mod_LoadLeafs (&header->lumps[LUMP_LEAFS]); + Mod_LoadNodes (&header->lumps[LUMP_NODES]); + Mod_LoadClipnodes (&header->lumps[LUMP_CLIPNODES]); + Mod_LoadEntities (&header->lumps[LUMP_ENTITIES]); + Mod_LoadSubmodels (&header->lumps[LUMP_MODELS]); + + Mod_MakeHull0 (); + + mod->numframes = 2; // regular and alternate animation + +// +// set up the submodels (FIXME: this is confusing) +// + for (i=0 ; i<mod->numsubmodels ; i++) + { + bm = &mod->submodels[i]; + + mod->hulls[0].firstclipnode = bm->headnode[0]; + for (j=1 ; j<MAX_MAP_HULLS ; j++) + { + mod->hulls[j].firstclipnode = bm->headnode[j]; + mod->hulls[j].lastclipnode = mod->numclipnodes-1; + } + + mod->firstmodelsurface = bm->firstface; + mod->nummodelsurfaces = bm->numfaces; + + VectorCopy (bm->maxs, mod->maxs); + VectorCopy (bm->mins, mod->mins); + + mod->numleafs = bm->visleafs; + + if (i < mod->numsubmodels-1) + { // duplicate the basic information + char name[10]; + + sprintf (name, "*%i", i+1); + loadmodel = Mod_FindName (name); + *loadmodel = *mod; + strcpy (loadmodel->name, name); + mod = loadmodel; + } + } +} + diff --git a/QW/server/move.txt b/QW/server/move.txt new file mode 100644 index 0000000..e4687ae --- /dev/null +++ b/QW/server/move.txt @@ -0,0 +1,39 @@ + +dead state flag for no user input + + +floor = under feet test + onground? + +feet = wading test + +waist = swimming test + +head = submerged test + + +if ( floor == solid) + if (head == water) + friction = 0.8; + else if (waist == water) + friction = + else if (feet == water) + friction = + else + friction = + walk code + return; + +if (floor == water) + if (head == water) + total underwater + if (waist == water) + treading water + drifting down + +if (floor == air) + falling + + + +
\ No newline at end of file diff --git a/QW/server/newnet.txt b/QW/server/newnet.txt new file mode 100644 index 0000000..9df49d1 --- /dev/null +++ b/QW/server/newnet.txt @@ -0,0 +1,121 @@ +registered clients will auto-kick unregistered clients + +central clearingghouse for active servers + +server logon messages + +problem with reconnect to same server out of order packets + +acknowledge all command line parameters + +annoying pause on intermission screen + +seperate accept/reject list for server packets and client packets + +server compile time should be part of connect message and status + +allocate new cvars during file parse + +remote console should be able to do cvar setting/reading + +make punchangle a client side operation + +remove sys_printf +remove host_client + +packet filter list +client move by test final pos and test 1/16 down +simulate world first, then clients, clients explicitly sim weapon projectiles +allow remote locations to subscribe to console output + + + +client zombie state? +seperate command ques for client and server? +worry about partial connection during level change +worry about zombie state for if(!state) constructs + +make sound/ and models/ implicit in data types + +int sequence +int reliable_sequence; +bit reliable_payload; + +consumption +renewable resources +depletable resources +rate of extraction + + +how can you influence other characters +how can you communicate + + +login +logoff +disconnection + +client to server +---------------- +last good received server time +milliseconds since last move frame +angles +movement +buttonstates +impulse + + +server to client +---------------- +last movemessage received +origin +velocity + + +build movement +send movement packet to server +set player position to last known good +execute all unaknowledged movement packets + + +needs to know all things that can effect movement + +movement context +---------------- +origin +velocity +size + +run move command (context, move) + + + +#define MAX_MOVEMESSAGES 64 + +typedef struct +{ + double time; // client time when sent + byte milliseconds; +} movemessage_t; + +int ack_movemessage; +int current_movemessage; + +movemessage_t movemessages[MAX_MOVEMESSAGES]; + +void CL_Frame (void) +{ + get all pending server messages + + latency = cl.time - movemessage[last].time; + + movetime = cl.time - cl.oldtime; + get movemessage + + send packet to server +} + + + + +does not adress interpolating other object's movement diff --git a/QW/server/notes.txt b/QW/server/notes.txt new file mode 100644 index 0000000..a46808d --- /dev/null +++ b/QW/server/notes.txt @@ -0,0 +1,7 @@ + +new server auth design; +- server includes 32bit random token S +- master sees new server, request auth with MD5(M|S). m is 32bit master key +- server sends register MD5( MD5(M|S) | S ) +- master validates registration with server +- M value changes for each server randomly diff --git a/QW/server/pr_cmds.c b/QW/server/pr_cmds.c new file mode 100644 index 0000000..0e0cdc5 --- /dev/null +++ b/QW/server/pr_cmds.c @@ -0,0 +1,1722 @@ +/* +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. + +*/ + +#include "qwsvdef.h" + +#define RETURN_EDICT(e) (((int *)pr_globals)[OFS_RETURN] = EDICT_TO_PROG(e)) +#define RETURN_STRING(s) (((int *)pr_globals)[OFS_RETURN] = PR_SetString(s)) + +/* +=============================================================================== + + BUILT-IN FUNCTIONS + +=============================================================================== +*/ + +char *PF_VarString (int first) +{ + int i; + static char out[256]; + + out[0] = 0; + for (i=first ; i<pr_argc ; i++) + { + strcat (out, G_STRING((OFS_PARM0+i*3))); + } + return out; +} + + +/* +================= +PF_errror + +This is a TERMINAL error, which will kill off the entire server. +Dumps self. + +error(value) +================= +*/ +void PF_error (void) +{ + char *s; + edict_t *ed; + + s = PF_VarString(0); + Con_Printf ("======SERVER ERROR in %s:\n%s\n", PR_GetString(pr_xfunction->s_name) ,s); + ed = PROG_TO_EDICT(pr_global_struct->self); + ED_Print (ed); + + SV_Error ("Program error"); +} + +/* +================= +PF_objerror + +Dumps out self, then an error message. The program is aborted and self is +removed, but the level can continue. + +objerror(value) +================= +*/ +void PF_objerror (void) +{ + char *s; + edict_t *ed; + + s = PF_VarString(0); + Con_Printf ("======OBJECT ERROR in %s:\n%s\n", PR_GetString(pr_xfunction->s_name),s); + ed = PROG_TO_EDICT(pr_global_struct->self); + ED_Print (ed); + ED_Free (ed); + + SV_Error ("Program error"); +} + + + +/* +============== +PF_makevectors + +Writes new values for v_forward, v_up, and v_right based on angles +makevectors(vector) +============== +*/ +void PF_makevectors (void) +{ + AngleVectors (G_VECTOR(OFS_PARM0), pr_global_struct->v_forward, pr_global_struct->v_right, pr_global_struct->v_up); +} + +/* +================= +PF_setorigin + +This is the only valid way to move an object without using the physics of the world (setting velocity and waiting). Directly changing origin will not set internal links correctly, so clipping would be messed up. This should be called when an object is spawned, and then only if it is teleported. + +setorigin (entity, origin) +================= +*/ +void PF_setorigin (void) +{ + edict_t *e; + float *org; + + e = G_EDICT(OFS_PARM0); + org = G_VECTOR(OFS_PARM1); + VectorCopy (org, e->v.origin); + SV_LinkEdict (e, false); +} + + +/* +================= +PF_setsize + +the size box is rotated by the current angle + +setsize (entity, minvector, maxvector) +================= +*/ +void PF_setsize (void) +{ + edict_t *e; + float *min, *max; + + e = G_EDICT(OFS_PARM0); + min = G_VECTOR(OFS_PARM1); + max = G_VECTOR(OFS_PARM2); + VectorCopy (min, e->v.mins); + VectorCopy (max, e->v.maxs); + VectorSubtract (max, min, e->v.size); + SV_LinkEdict (e, false); +} + + +/* +================= +PF_setmodel + +setmodel(entity, model) +Also sets size, mins, and maxs for inline bmodels +================= +*/ +void PF_setmodel (void) +{ + edict_t *e; + char *m, **check; + int i; + model_t *mod; + + e = G_EDICT(OFS_PARM0); + m = G_STRING(OFS_PARM1); + +// check to see if model was properly precached + for (i=0, check = sv.model_precache ; *check ; i++, check++) + if (!strcmp(*check, m)) + break; + + if (!*check) + PR_RunError ("no precache: %s\n", m); + + e->v.model = PR_SetString(m); + e->v.modelindex = i; + +// if it is an inline model, get the size information for it + if (m[0] == '*') + { + mod = Mod_ForName (m, true); + VectorCopy (mod->mins, e->v.mins); + VectorCopy (mod->maxs, e->v.maxs); + VectorSubtract (mod->maxs, mod->mins, e->v.size); + SV_LinkEdict (e, false); + } + +} + +/* +================= +PF_bprint + +broadcast print to everyone on server + +bprint(value) +================= +*/ +void PF_bprint (void) +{ + char *s; + int level; + + level = G_FLOAT(OFS_PARM0); + + s = PF_VarString(1); + SV_BroadcastPrintf (level, "%s", s); +} + +/* +================= +PF_sprint + +single print to a specific client + +sprint(clientent, value) +================= +*/ +void PF_sprint (void) +{ + char *s; + client_t *client; + int entnum; + int level; + + entnum = G_EDICTNUM(OFS_PARM0); + level = G_FLOAT(OFS_PARM1); + + s = PF_VarString(2); + + if (entnum < 1 || entnum > MAX_CLIENTS) + { + Con_Printf ("tried to sprint to a non-client\n"); + return; + } + + client = &svs.clients[entnum-1]; + + SV_ClientPrintf (client, level, "%s", s); +} + + +/* +================= +PF_centerprint + +single print to a specific client + +centerprint(clientent, value) +================= +*/ +void PF_centerprint (void) +{ + char *s; + int entnum; + client_t *cl; + + entnum = G_EDICTNUM(OFS_PARM0); + s = PF_VarString(1); + + if (entnum < 1 || entnum > MAX_CLIENTS) + { + Con_Printf ("tried to sprint to a non-client\n"); + return; + } + + cl = &svs.clients[entnum-1]; + + ClientReliableWrite_Begin (cl, svc_centerprint, 2 + strlen(s)); + ClientReliableWrite_String (cl, s); +} + + +/* +================= +PF_normalize + +vector normalize(vector) +================= +*/ +void PF_normalize (void) +{ + float *value1; + vec3_t newvalue; + float new; + + value1 = G_VECTOR(OFS_PARM0); + + new = value1[0] * value1[0] + value1[1] * value1[1] + value1[2]*value1[2]; + new = sqrt(new); + + if (new == 0) + newvalue[0] = newvalue[1] = newvalue[2] = 0; + else + { + new = 1/new; + newvalue[0] = value1[0] * new; + newvalue[1] = value1[1] * new; + newvalue[2] = value1[2] * new; + } + + VectorCopy (newvalue, G_VECTOR(OFS_RETURN)); +} + +/* +================= +PF_vlen + +scalar vlen(vector) +================= +*/ +void PF_vlen (void) +{ + float *value1; + float new; + + value1 = G_VECTOR(OFS_PARM0); + + new = value1[0] * value1[0] + value1[1] * value1[1] + value1[2]*value1[2]; + new = sqrt(new); + + G_FLOAT(OFS_RETURN) = new; +} + +/* +================= +PF_vectoyaw + +float vectoyaw(vector) +================= +*/ +void PF_vectoyaw (void) +{ + float *value1; + float yaw; + + value1 = G_VECTOR(OFS_PARM0); + + if (value1[1] == 0 && value1[0] == 0) + yaw = 0; + else + { + yaw = (int) (atan2(value1[1], value1[0]) * 180 / M_PI); + if (yaw < 0) + yaw += 360; + } + + G_FLOAT(OFS_RETURN) = yaw; +} + + +/* +================= +PF_vectoangles + +vector vectoangles(vector) +================= +*/ +void PF_vectoangles (void) +{ + float *value1; + float forward; + float yaw, pitch; + + value1 = G_VECTOR(OFS_PARM0); + + if (value1[1] == 0 && value1[0] == 0) + { + yaw = 0; + if (value1[2] > 0) + pitch = 90; + else + pitch = 270; + } + else + { + yaw = (int) (atan2(value1[1], value1[0]) * 180 / M_PI); + if (yaw < 0) + yaw += 360; + + forward = sqrt (value1[0]*value1[0] + value1[1]*value1[1]); + pitch = (int) (atan2(value1[2], forward) * 180 / M_PI); + if (pitch < 0) + pitch += 360; + } + + G_FLOAT(OFS_RETURN+0) = pitch; + G_FLOAT(OFS_RETURN+1) = yaw; + G_FLOAT(OFS_RETURN+2) = 0; +} + +/* +================= +PF_Random + +Returns a number from 0<= num < 1 + +random() +================= +*/ +void PF_random (void) +{ + float num; + + num = (rand ()&0x7fff) / ((float)0x7fff); + + G_FLOAT(OFS_RETURN) = num; +} + + +/* +================= +PF_ambientsound + +================= +*/ +void PF_ambientsound (void) +{ + char **check; + char *samp; + float *pos; + float vol, attenuation; + int i, soundnum; + + pos = G_VECTOR (OFS_PARM0); + samp = G_STRING(OFS_PARM1); + vol = G_FLOAT(OFS_PARM2); + attenuation = G_FLOAT(OFS_PARM3); + +// check to see if samp was properly precached + for (soundnum=0, check = sv.sound_precache ; *check ; check++, soundnum++) + if (!strcmp(*check,samp)) + break; + + if (!*check) + { + Con_Printf ("no precache: %s\n", samp); + return; + } + +// add an svc_spawnambient command to the level signon packet + + MSG_WriteByte (&sv.signon,svc_spawnstaticsound); + for (i=0 ; i<3 ; i++) + MSG_WriteCoord(&sv.signon, pos[i]); + + MSG_WriteByte (&sv.signon, soundnum); + + MSG_WriteByte (&sv.signon, vol*255); + MSG_WriteByte (&sv.signon, attenuation*64); + +} + +/* +================= +PF_sound + +Each entity can have eight independant sound sources, like voice, +weapon, feet, etc. + +Channel 0 is an auto-allocate channel, the others override anything +allready running on that entity/channel pair. + +An attenuation of 0 will play full volume everywhere in the level. +Larger attenuations will drop off. + +================= +*/ +void PF_sound (void) +{ + char *sample; + int channel; + edict_t *entity; + int volume; + float attenuation; + + entity = G_EDICT(OFS_PARM0); + channel = G_FLOAT(OFS_PARM1); + sample = G_STRING(OFS_PARM2); + volume = G_FLOAT(OFS_PARM3) * 255; + attenuation = G_FLOAT(OFS_PARM4); + + SV_StartSound (entity, channel, sample, volume, attenuation); +} + +/* +================= +PF_break + +break() +================= +*/ +void PF_break (void) +{ +Con_Printf ("break statement\n"); +*(int *)-4 = 0; // dump to debugger +// PR_RunError ("break statement"); +} + +/* +================= +PF_traceline + +Used for use tracing and shot targeting +Traces are blocked by bbox and exact bsp entityes, and also slide box entities +if the tryents flag is set. + +traceline (vector1, vector2, tryents) +================= +*/ +void PF_traceline (void) +{ + float *v1, *v2; + trace_t trace; + int nomonsters; + edict_t *ent; + + v1 = G_VECTOR(OFS_PARM0); + v2 = G_VECTOR(OFS_PARM1); + nomonsters = G_FLOAT(OFS_PARM2); + ent = G_EDICT(OFS_PARM3); + + trace = SV_Move (v1, vec3_origin, vec3_origin, v2, nomonsters, ent); + + pr_global_struct->trace_allsolid = trace.allsolid; + pr_global_struct->trace_startsolid = trace.startsolid; + pr_global_struct->trace_fraction = trace.fraction; + pr_global_struct->trace_inwater = trace.inwater; + pr_global_struct->trace_inopen = trace.inopen; + VectorCopy (trace.endpos, pr_global_struct->trace_endpos); + VectorCopy (trace.plane.normal, pr_global_struct->trace_plane_normal); + pr_global_struct->trace_plane_dist = trace.plane.dist; + if (trace.ent) + pr_global_struct->trace_ent = EDICT_TO_PROG(trace.ent); + else + pr_global_struct->trace_ent = EDICT_TO_PROG(sv.edicts); +} + +/* +================= +PF_checkpos + +Returns true if the given entity can move to the given position from it's +current position by walking or rolling. +FIXME: make work... +scalar checkpos (entity, vector) +================= +*/ +void PF_checkpos (void) +{ +} + +//============================================================================ + +byte checkpvs[MAX_MAP_LEAFS/8]; + +int PF_newcheckclient (int check) +{ + int i; + byte *pvs; + edict_t *ent; + mleaf_t *leaf; + vec3_t org; + +// cycle to the next one + + if (check < 1) + check = 1; + if (check > MAX_CLIENTS) + check = MAX_CLIENTS; + + if (check == MAX_CLIENTS) + i = 1; + else + i = check + 1; + + for ( ; ; i++) + { + if (i == MAX_CLIENTS+1) + i = 1; + + ent = EDICT_NUM(i); + + if (i == check) + break; // didn't find anything else + + if (ent->free) + continue; + if (ent->v.health <= 0) + continue; + if ((int)ent->v.flags & FL_NOTARGET) + continue; + + // anything that is a client, or has a client as an enemy + break; + } + +// get the PVS for the entity + VectorAdd (ent->v.origin, ent->v.view_ofs, org); + leaf = Mod_PointInLeaf (org, sv.worldmodel); + pvs = Mod_LeafPVS (leaf, sv.worldmodel); + memcpy (checkpvs, pvs, (sv.worldmodel->numleafs+7)>>3 ); + + return i; +} + +/* +================= +PF_checkclient + +Returns a client (or object that has a client enemy) that would be a +valid target. + +If there are more than one valid options, they are cycled each frame + +If (self.origin + self.viewofs) is not in the PVS of the current target, +it is not returned at all. + +name checkclient () +================= +*/ +#define MAX_CHECK 16 +int c_invis, c_notvis; +void PF_checkclient (void) +{ + edict_t *ent, *self; + mleaf_t *leaf; + int l; + vec3_t view; + +// find a new check if on a new frame + if (sv.time - sv.lastchecktime >= 0.1) + { + sv.lastcheck = PF_newcheckclient (sv.lastcheck); + sv.lastchecktime = sv.time; + } + +// return check if it might be visible + ent = EDICT_NUM(sv.lastcheck); + if (ent->free || ent->v.health <= 0) + { + RETURN_EDICT(sv.edicts); + return; + } + +// if current entity can't possibly see the check entity, return 0 + self = PROG_TO_EDICT(pr_global_struct->self); + VectorAdd (self->v.origin, self->v.view_ofs, view); + leaf = Mod_PointInLeaf (view, sv.worldmodel); + l = (leaf - sv.worldmodel->leafs) - 1; + if ( (l<0) || !(checkpvs[l>>3] & (1<<(l&7)) ) ) + { +c_notvis++; + RETURN_EDICT(sv.edicts); + return; + } + +// might be able to see it +c_invis++; + RETURN_EDICT(ent); +} + +//============================================================================ + + +/* +================= +PF_stuffcmd + +Sends text over to the client's execution buffer + +stuffcmd (clientent, value) +================= +*/ +void PF_stuffcmd (void) +{ + int entnum; + char *str; + client_t *cl; + + entnum = G_EDICTNUM(OFS_PARM0); + if (entnum < 1 || entnum > MAX_CLIENTS) + PR_RunError ("Parm 0 not a client"); + str = G_STRING(OFS_PARM1); + + cl = &svs.clients[entnum-1]; + + if (strcmp(str, "disconnect\n") == 0) { + // so long and thanks for all the fish + cl->drop = true; + return; + } + + ClientReliableWrite_Begin (cl, svc_stufftext, 2+strlen(str)); + ClientReliableWrite_String (cl, str); +} + +/* +================= +PF_localcmd + +Sends text over to the client's execution buffer + +localcmd (string) +================= +*/ +void PF_localcmd (void) +{ + char *str; + + str = G_STRING(OFS_PARM0); + Cbuf_AddText (str); +} + +/* +================= +PF_cvar + +float cvar (string) +================= +*/ +void PF_cvar (void) +{ + char *str; + + str = G_STRING(OFS_PARM0); + + G_FLOAT(OFS_RETURN) = Cvar_VariableValue (str); +} + +/* +================= +PF_cvar_set + +float cvar (string) +================= +*/ +void PF_cvar_set (void) +{ + char *var, *val; + + var = G_STRING(OFS_PARM0); + val = G_STRING(OFS_PARM1); + + Cvar_Set (var, val); +} + +/* +================= +PF_findradius + +Returns a chain of entities that have origins within a spherical area + +findradius (origin, radius) +================= +*/ +void PF_findradius (void) +{ + edict_t *ent, *chain; + float rad; + float *org; + vec3_t eorg; + int i, j; + + chain = (edict_t *)sv.edicts; + + org = G_VECTOR(OFS_PARM0); + rad = G_FLOAT(OFS_PARM1); + + ent = NEXT_EDICT(sv.edicts); + for (i=1 ; i<sv.num_edicts ; i++, ent = NEXT_EDICT(ent)) + { + if (ent->free) + continue; + if (ent->v.solid == SOLID_NOT) + 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 (Length(eorg) > rad) + continue; + + ent->v.chain = EDICT_TO_PROG(chain); + chain = ent; + } + + RETURN_EDICT(chain); +} + + +/* +========= +PF_dprint +========= +*/ +void PF_dprint (void) +{ + Con_Printf ("%s",PF_VarString(0)); +} + +char pr_string_temp[128]; + +void PF_ftos (void) +{ + float v; + v = G_FLOAT(OFS_PARM0); + + if (v == (int)v) + sprintf (pr_string_temp, "%d",(int)v); + else + sprintf (pr_string_temp, "%5.1f",v); + G_INT(OFS_RETURN) = PR_SetString(pr_string_temp); +} + +void PF_fabs (void) +{ + float v; + v = G_FLOAT(OFS_PARM0); + G_FLOAT(OFS_RETURN) = fabs(v); +} + +void PF_vtos (void) +{ + sprintf (pr_string_temp, "'%5.1f %5.1f %5.1f'", G_VECTOR(OFS_PARM0)[0], G_VECTOR(OFS_PARM0)[1], G_VECTOR(OFS_PARM0)[2]); + G_INT(OFS_RETURN) = PR_SetString(pr_string_temp); +} + +void PF_Spawn (void) +{ + edict_t *ed; + ed = ED_Alloc(); + RETURN_EDICT(ed); +} + +void PF_Remove (void) +{ + edict_t *ed; + + ed = G_EDICT(OFS_PARM0); + ED_Free (ed); +} + + +// entity (entity start, .string field, string match) find = #5; +void PF_Find (void) +{ + int e; + int f; + char *s, *t; + edict_t *ed; + + e = G_EDICTNUM(OFS_PARM0); + f = G_INT(OFS_PARM1); + s = G_STRING(OFS_PARM2); + if (!s) + PR_RunError ("PF_Find: bad search string"); + + for (e++ ; e < sv.num_edicts ; e++) + { + ed = EDICT_NUM(e); + if (ed->free) + continue; + t = E_STRING(ed,f); + if (!t) + continue; + if (!strcmp(t,s)) + { + RETURN_EDICT(ed); + return; + } + } + + RETURN_EDICT(sv.edicts); +} + +void PR_CheckEmptyString (char *s) +{ + if (s[0] <= ' ') + PR_RunError ("Bad string"); +} + +void PF_precache_file (void) +{ // precache_file is only used to copy files with qcc, it does nothing + G_INT(OFS_RETURN) = G_INT(OFS_PARM0); +} + +void PF_precache_sound (void) +{ + char *s; + int i; + + if (sv.state != ss_loading) + PR_RunError ("PF_Precache_*: Precache can only be done in spawn functions"); + + s = G_STRING(OFS_PARM0); + G_INT(OFS_RETURN) = G_INT(OFS_PARM0); + PR_CheckEmptyString (s); + + for (i=0 ; i<MAX_SOUNDS ; i++) + { + if (!sv.sound_precache[i]) + { + sv.sound_precache[i] = s; + return; + } + if (!strcmp(sv.sound_precache[i], s)) + return; + } + PR_RunError ("PF_precache_sound: overflow"); +} + +void PF_precache_model (void) +{ + char *s; + int i; + + if (sv.state != ss_loading) + PR_RunError ("PF_Precache_*: Precache can only be done in spawn functions"); + + s = G_STRING(OFS_PARM0); + G_INT(OFS_RETURN) = G_INT(OFS_PARM0); + PR_CheckEmptyString (s); + + for (i=0 ; i<MAX_MODELS ; i++) + { + if (!sv.model_precache[i]) + { + sv.model_precache[i] = s; + return; + } + if (!strcmp(sv.model_precache[i], s)) + return; + } + PR_RunError ("PF_precache_model: overflow"); +} + + +void PF_coredump (void) +{ + ED_PrintEdicts (); +} + +void PF_traceon (void) +{ + pr_trace = true; +} + +void PF_traceoff (void) +{ + pr_trace = false; +} + +void PF_eprint (void) +{ + ED_PrintNum (G_EDICTNUM(OFS_PARM0)); +} + +/* +=============== +PF_walkmove + +float(float yaw, float dist) walkmove +=============== +*/ +void PF_walkmove (void) +{ + edict_t *ent; + float yaw, dist; + vec3_t move; + dfunction_t *oldf; + int oldself; + + ent = PROG_TO_EDICT(pr_global_struct->self); + yaw = G_FLOAT(OFS_PARM0); + dist = G_FLOAT(OFS_PARM1); + + if ( !( (int)ent->v.flags & (FL_ONGROUND|FL_FLY|FL_SWIM) ) ) + { + G_FLOAT(OFS_RETURN) = 0; + return; + } + + yaw = yaw*M_PI*2 / 360; + + move[0] = cos(yaw)*dist; + move[1] = sin(yaw)*dist; + move[2] = 0; + +// save program state, because SV_movestep may call other progs + oldf = pr_xfunction; + oldself = pr_global_struct->self; + + G_FLOAT(OFS_RETURN) = SV_movestep(ent, move, true); + + +// restore program state + pr_xfunction = oldf; + pr_global_struct->self = oldself; +} + +/* +=============== +PF_droptofloor + +void() droptofloor +=============== +*/ +void PF_droptofloor (void) +{ + edict_t *ent; + vec3_t end; + trace_t trace; + + ent = PROG_TO_EDICT(pr_global_struct->self); + + VectorCopy (ent->v.origin, end); + end[2] -= 256; + + trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, false, ent); + + if (trace.fraction == 1 || trace.allsolid) + G_FLOAT(OFS_RETURN) = 0; + else + { + VectorCopy (trace.endpos, ent->v.origin); + SV_LinkEdict (ent, false); + ent->v.flags = (int)ent->v.flags | FL_ONGROUND; + ent->v.groundentity = EDICT_TO_PROG(trace.ent); + G_FLOAT(OFS_RETURN) = 1; + } +} + +/* +=============== +PF_lightstyle + +void(float style, string value) lightstyle +=============== +*/ +void PF_lightstyle (void) +{ + int style; + char *val; + client_t *client; + int j; + + style = G_FLOAT(OFS_PARM0); + val = G_STRING(OFS_PARM1); + +// change the string in sv + sv.lightstyles[style] = val; + +// send message to all clients on this server + if (sv.state != ss_active) + return; + + for (j=0, client = svs.clients ; j<MAX_CLIENTS ; j++, client++) + if ( client->state == cs_spawned ) + { + ClientReliableWrite_Begin (client, svc_lightstyle, strlen(val)+3); + ClientReliableWrite_Char (client, style); + ClientReliableWrite_String (client, val); + } +} + +void PF_rint (void) +{ + float f; + f = G_FLOAT(OFS_PARM0); + if (f > 0) + G_FLOAT(OFS_RETURN) = (int)(f + 0.5); + else + G_FLOAT(OFS_RETURN) = (int)(f - 0.5); +} +void PF_floor (void) +{ + G_FLOAT(OFS_RETURN) = floor(G_FLOAT(OFS_PARM0)); +} +void PF_ceil (void) +{ + G_FLOAT(OFS_RETURN) = ceil(G_FLOAT(OFS_PARM0)); +} + + +/* +============= +PF_checkbottom +============= +*/ +void PF_checkbottom (void) +{ + edict_t *ent; + + ent = G_EDICT(OFS_PARM0); + + G_FLOAT(OFS_RETURN) = SV_CheckBottom (ent); +} + +/* +============= +PF_pointcontents +============= +*/ +void PF_pointcontents (void) +{ + float *v; + + v = G_VECTOR(OFS_PARM0); + + G_FLOAT(OFS_RETURN) = SV_PointContents (v); +} + +/* +============= +PF_nextent + +entity nextent(entity) +============= +*/ +void PF_nextent (void) +{ + int i; + edict_t *ent; + + i = G_EDICTNUM(OFS_PARM0); + while (1) + { + i++; + if (i == sv.num_edicts) + { + RETURN_EDICT(sv.edicts); + return; + } + ent = EDICT_NUM(i); + if (!ent->free) + { + RETURN_EDICT(ent); + return; + } + } +} + +/* +============= +PF_aim + +Pick a vector for the player to shoot along +vector aim(entity, missilespeed) +============= +*/ +//cvar_t sv_aim = {"sv_aim", "0.93"}; +cvar_t sv_aim = {"sv_aim", "2"}; +void PF_aim (void) +{ + edict_t *ent, *check, *bestent; + vec3_t start, dir, end, bestdir; + int i, j; + trace_t tr; + float dist, bestdist; + float speed; + char *noaim; + + ent = G_EDICT(OFS_PARM0); + speed = G_FLOAT(OFS_PARM1); + + VectorCopy (ent->v.origin, start); + start[2] += 20; + +// noaim option + i = NUM_FOR_EDICT(ent); + if (i>0 && i<MAX_CLIENTS) + { + noaim = Info_ValueForKey (svs.clients[i-1].userinfo, "noaim"); + if (atoi(noaim) > 0) + { + VectorCopy (pr_global_struct->v_forward, G_VECTOR(OFS_RETURN)); + return; + } + } + +// try sending a trace straight + VectorCopy (pr_global_struct->v_forward, dir); + VectorMA (start, 2048, dir, end); + tr = SV_Move (start, vec3_origin, vec3_origin, end, false, ent); + if (tr.ent && tr.ent->v.takedamage == DAMAGE_AIM + && (!teamplay.value || ent->v.team <=0 || ent->v.team != tr.ent->v.team) ) + { + VectorCopy (pr_global_struct->v_forward, G_VECTOR(OFS_RETURN)); + return; + } + + +// try all possible entities + VectorCopy (dir, bestdir); + bestdist = sv_aim.value; + bestent = NULL; + + check = NEXT_EDICT(sv.edicts); + for (i=1 ; i<sv.num_edicts ; i++, check = NEXT_EDICT(check) ) + { + if (check->v.takedamage != DAMAGE_AIM) + continue; + if (check == ent) + continue; + if (teamplay.value && ent->v.team > 0 && ent->v.team == check->v.team) + continue; // don't aim at teammate + for (j=0 ; j<3 ; j++) + end[j] = check->v.origin[j] + + 0.5*(check->v.mins[j] + check->v.maxs[j]); + VectorSubtract (end, start, dir); + VectorNormalize (dir); + dist = DotProduct (dir, pr_global_struct->v_forward); + if (dist < bestdist) + continue; // to far to turn + tr = SV_Move (start, vec3_origin, vec3_origin, end, false, ent); + if (tr.ent == check) + { // can shoot at this one + bestdist = dist; + bestent = check; + } + } + + if (bestent) + { + VectorSubtract (bestent->v.origin, ent->v.origin, dir); + dist = DotProduct (dir, pr_global_struct->v_forward); + VectorScale (pr_global_struct->v_forward, dist, end); + end[2] = dir[2]; + VectorNormalize (end); + VectorCopy (end, G_VECTOR(OFS_RETURN)); + } + else + { + VectorCopy (bestdir, G_VECTOR(OFS_RETURN)); + } +} + +/* +============== +PF_changeyaw + +This was a major timewaster in progs, so it was converted to C +============== +*/ +void PF_changeyaw (void) +{ + edict_t *ent; + float ideal, current, move, speed; + + ent = PROG_TO_EDICT(pr_global_struct->self); + current = anglemod( ent->v.angles[1] ); + ideal = ent->v.ideal_yaw; + speed = ent->v.yaw_speed; + + if (current == ideal) + return; + move = ideal - current; + if (ideal > current) + { + if (move >= 180) + move = move - 360; + } + else + { + if (move <= -180) + move = move + 360; + } + if (move > 0) + { + if (move > speed) + move = speed; + } + else + { + if (move < -speed) + move = -speed; + } + + ent->v.angles[1] = anglemod (current + move); +} + +/* +=============================================================================== + +MESSAGE WRITING + +=============================================================================== +*/ + +#define MSG_BROADCAST 0 // unreliable to all +#define MSG_ONE 1 // reliable to one (msg_entity) +#define MSG_ALL 2 // reliable to all +#define MSG_INIT 3 // write to the init string +#define MSG_MULTICAST 4 // for multicast() + +sizebuf_t *WriteDest (void) +{ + int entnum; + int dest; + edict_t *ent; + + dest = G_FLOAT(OFS_PARM0); + switch (dest) + { + case MSG_BROADCAST: + return &sv.datagram; + + case MSG_ONE: + SV_Error("Shouldn't be at MSG_ONE"); +#if 0 + ent = PROG_TO_EDICT(pr_global_struct->msg_entity); + entnum = NUM_FOR_EDICT(ent); + if (entnum < 1 || entnum > MAX_CLIENTS) + PR_RunError ("WriteDest: not a client"); + return &svs.clients[entnum-1].netchan.message; +#endif + + case MSG_ALL: + return &sv.reliable_datagram; + + case MSG_INIT: + if (sv.state != ss_loading) + PR_RunError ("PF_Write_*: MSG_INIT can only be written in spawn functions"); + return &sv.signon; + + case MSG_MULTICAST: + return &sv.multicast; + + default: + PR_RunError ("WriteDest: bad destination"); + break; + } + + return NULL; +} + +static client_t *Write_GetClient(void) +{ + int entnum; + edict_t *ent; + + ent = PROG_TO_EDICT(pr_global_struct->msg_entity); + entnum = NUM_FOR_EDICT(ent); + if (entnum < 1 || entnum > MAX_CLIENTS) + PR_RunError ("WriteDest: not a client"); + return &svs.clients[entnum-1]; +} + + +void PF_WriteByte (void) +{ + if (G_FLOAT(OFS_PARM0) == MSG_ONE) { + client_t *cl = Write_GetClient(); + ClientReliableCheckBlock(cl, 1); + ClientReliableWrite_Byte(cl, G_FLOAT(OFS_PARM1)); + } else + MSG_WriteByte (WriteDest(), G_FLOAT(OFS_PARM1)); +} + +void PF_WriteChar (void) +{ + if (G_FLOAT(OFS_PARM0) == MSG_ONE) { + client_t *cl = Write_GetClient(); + ClientReliableCheckBlock(cl, 1); + ClientReliableWrite_Char(cl, G_FLOAT(OFS_PARM1)); + } else + MSG_WriteChar (WriteDest(), G_FLOAT(OFS_PARM1)); +} + +void PF_WriteShort (void) +{ + if (G_FLOAT(OFS_PARM0) == MSG_ONE) { + client_t *cl = Write_GetClient(); + ClientReliableCheckBlock(cl, 2); + ClientReliableWrite_Short(cl, G_FLOAT(OFS_PARM1)); + } else + MSG_WriteShort (WriteDest(), G_FLOAT(OFS_PARM1)); +} + +void PF_WriteLong (void) +{ + if (G_FLOAT(OFS_PARM0) == MSG_ONE) { + client_t *cl = Write_GetClient(); + ClientReliableCheckBlock(cl, 4); + ClientReliableWrite_Long(cl, G_FLOAT(OFS_PARM1)); + } else + MSG_WriteLong (WriteDest(), G_FLOAT(OFS_PARM1)); +} + +void PF_WriteAngle (void) +{ + if (G_FLOAT(OFS_PARM0) == MSG_ONE) { + client_t *cl = Write_GetClient(); + ClientReliableCheckBlock(cl, 1); + ClientReliableWrite_Angle(cl, G_FLOAT(OFS_PARM1)); + } else + MSG_WriteAngle (WriteDest(), G_FLOAT(OFS_PARM1)); +} + +void PF_WriteCoord (void) +{ + if (G_FLOAT(OFS_PARM0) == MSG_ONE) { + client_t *cl = Write_GetClient(); + ClientReliableCheckBlock(cl, 2); + ClientReliableWrite_Coord(cl, G_FLOAT(OFS_PARM1)); + } else + MSG_WriteCoord (WriteDest(), G_FLOAT(OFS_PARM1)); +} + +void PF_WriteString (void) +{ + if (G_FLOAT(OFS_PARM0) == MSG_ONE) { + client_t *cl = Write_GetClient(); + ClientReliableCheckBlock(cl, 1+strlen(G_STRING(OFS_PARM1))); + ClientReliableWrite_String(cl, G_STRING(OFS_PARM1)); + } else + MSG_WriteString (WriteDest(), G_STRING(OFS_PARM1)); +} + + +void PF_WriteEntity (void) +{ + if (G_FLOAT(OFS_PARM0) == MSG_ONE) { + client_t *cl = Write_GetClient(); + ClientReliableCheckBlock(cl, 2); + ClientReliableWrite_Short(cl, G_EDICTNUM(OFS_PARM1)); + } else + MSG_WriteShort (WriteDest(), G_EDICTNUM(OFS_PARM1)); +} + +//============================================================================= + +int SV_ModelIndex (char *name); + +void PF_makestatic (void) +{ + edict_t *ent; + int i; + + ent = G_EDICT(OFS_PARM0); + + MSG_WriteByte (&sv.signon,svc_spawnstatic); + + MSG_WriteByte (&sv.signon, SV_ModelIndex(PR_GetString(ent->v.model))); + + MSG_WriteByte (&sv.signon, ent->v.frame); + MSG_WriteByte (&sv.signon, ent->v.colormap); + MSG_WriteByte (&sv.signon, ent->v.skin); + for (i=0 ; i<3 ; i++) + { + MSG_WriteCoord(&sv.signon, ent->v.origin[i]); + MSG_WriteAngle(&sv.signon, ent->v.angles[i]); + } + +// throw the entity away now + ED_Free (ent); +} + +//============================================================================= + +/* +============== +PF_setspawnparms +============== +*/ +void PF_setspawnparms (void) +{ + edict_t *ent; + int i; + client_t *client; + + ent = G_EDICT(OFS_PARM0); + i = NUM_FOR_EDICT(ent); + if (i < 1 || i > MAX_CLIENTS) + PR_RunError ("Entity is not a client"); + + // copy spawn parms out of the client_t + client = svs.clients + (i-1); + + for (i=0 ; i< NUM_SPAWN_PARMS ; i++) + (&pr_global_struct->parm1)[i] = client->spawn_parms[i]; +} + +/* +============== +PF_changelevel +============== +*/ +void PF_changelevel (void) +{ + char *s; + static int last_spawncount; + +// make sure we don't issue two changelevels + if (svs.spawncount == last_spawncount) + return; + last_spawncount = svs.spawncount; + + s = G_STRING(OFS_PARM0); + Cbuf_AddText (va("map %s\n",s)); +} + + +/* +============== +PF_logfrag + +logfrag (killer, killee) +============== +*/ +void PF_logfrag (void) +{ + edict_t *ent1, *ent2; + int e1, e2; + char *s; + + ent1 = G_EDICT(OFS_PARM0); + ent2 = G_EDICT(OFS_PARM1); + + e1 = NUM_FOR_EDICT(ent1); + e2 = NUM_FOR_EDICT(ent2); + + if (e1 < 1 || e1 > MAX_CLIENTS + || e2 < 1 || e2 > MAX_CLIENTS) + return; + + s = va("\\%s\\%s\\\n",svs.clients[e1-1].name, svs.clients[e2-1].name); + + SZ_Print (&svs.log[svs.logsequence&1], s); + if (sv_fraglogfile) { + fprintf (sv_fraglogfile, s); + fflush (sv_fraglogfile); + } +} + + +/* +============== +PF_infokey + +string(entity e, string key) infokey +============== +*/ +void PF_infokey (void) +{ + edict_t *e; + int e1; + char *value; + char *key; + static char ov[256]; + + e = G_EDICT(OFS_PARM0); + e1 = NUM_FOR_EDICT(e); + key = G_STRING(OFS_PARM1); + + if (e1 == 0) { + if ((value = Info_ValueForKey (svs.info, key)) == NULL || + !*value) + value = Info_ValueForKey(localinfo, key); + } else if (e1 <= MAX_CLIENTS) { + if (!strcmp(key, "ip")) + value = strcpy(ov, NET_BaseAdrToString (svs.clients[e1-1].netchan.remote_address)); + else if (!strcmp(key, "ping")) { + int ping = SV_CalcPing (&svs.clients[e1-1]); + sprintf(ov, "%d", ping); + value = ov; + } else + value = Info_ValueForKey (svs.clients[e1-1].userinfo, key); + } else + value = ""; + + RETURN_STRING(value); +} + +/* +============== +PF_stof + +float(string s) stof +============== +*/ +void PF_stof (void) +{ + char *s; + + s = G_STRING(OFS_PARM0); + + G_FLOAT(OFS_RETURN) = atof(s); +} + + +/* +============== +PF_multicast + +void(vector where, float set) multicast +============== +*/ +void PF_multicast (void) +{ + float *o; + int to; + + o = G_VECTOR(OFS_PARM0); + to = G_FLOAT(OFS_PARM1); + + SV_Multicast (o, to); +} + + +void PF_Fixme (void) +{ + PR_RunError ("unimplemented bulitin"); +} + + + +builtin_t pr_builtin[] = +{ + PF_Fixme, +PF_makevectors, // void(entity e) makevectors = #1; +PF_setorigin, // void(entity e, vector o) setorigin = #2; +PF_setmodel, // void(entity e, string m) setmodel = #3; +PF_setsize, // void(entity e, vector min, vector max) setsize = #4; +PF_Fixme, // void(entity e, vector min, vector max) setabssize = #5; +PF_break, // void() break = #6; +PF_random, // float() random = #7; +PF_sound, // void(entity e, float chan, string samp) sound = #8; +PF_normalize, // vector(vector v) normalize = #9; +PF_error, // void(string e) error = #10; +PF_objerror, // void(string e) objerror = #11; +PF_vlen, // float(vector v) vlen = #12; +PF_vectoyaw, // float(vector v) vectoyaw = #13; +PF_Spawn, // entity() spawn = #14; +PF_Remove, // void(entity e) remove = #15; +PF_traceline, // float(vector v1, vector v2, float tryents) traceline = #16; +PF_checkclient, // entity() clientlist = #17; +PF_Find, // entity(entity start, .string fld, string match) find = #18; +PF_precache_sound, // void(string s) precache_sound = #19; +PF_precache_model, // void(string s) precache_model = #20; +PF_stuffcmd, // void(entity client, string s)stuffcmd = #21; +PF_findradius, // entity(vector org, float rad) findradius = #22; +PF_bprint, // void(string s) bprint = #23; +PF_sprint, // void(entity client, string s) sprint = #24; +PF_dprint, // void(string s) dprint = #25; +PF_ftos, // void(string s) ftos = #26; +PF_vtos, // void(string s) vtos = #27; +PF_coredump, +PF_traceon, +PF_traceoff, +PF_eprint, // void(entity e) debug print an entire entity +PF_walkmove, // float(float yaw, float dist) walkmove +PF_Fixme, // float(float yaw, float dist) walkmove +PF_droptofloor, +PF_lightstyle, +PF_rint, +PF_floor, +PF_ceil, +PF_Fixme, +PF_checkbottom, +PF_pointcontents, +PF_Fixme, +PF_fabs, +PF_aim, +PF_cvar, +PF_localcmd, +PF_nextent, +PF_Fixme, +PF_changeyaw, +PF_Fixme, +PF_vectoangles, + +PF_WriteByte, +PF_WriteChar, +PF_WriteShort, +PF_WriteLong, +PF_WriteCoord, +PF_WriteAngle, +PF_WriteString, +PF_WriteEntity, + +PF_Fixme, +PF_Fixme, +PF_Fixme, +PF_Fixme, +PF_Fixme, +PF_Fixme, +PF_Fixme, + +SV_MoveToGoal, +PF_precache_file, +PF_makestatic, + +PF_changelevel, +PF_Fixme, + +PF_cvar_set, +PF_centerprint, + +PF_ambientsound, + +PF_precache_model, +PF_precache_sound, // precache_sound2 is different only for qcc +PF_precache_file, + +PF_setspawnparms, + +PF_logfrag, + +PF_infokey, +PF_stof, +PF_multicast +}; + +builtin_t *pr_builtins = pr_builtin; +int pr_numbuiltins = sizeof(pr_builtin)/sizeof(pr_builtin[0]); + diff --git a/QW/server/pr_comp.h b/QW/server/pr_comp.h new file mode 100644 index 0000000..b237ed5 --- /dev/null +++ b/QW/server/pr_comp.h @@ -0,0 +1,180 @@ +/* +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. + +*/ + +// this file is shared by quake and qcc + +typedef int func_t; +typedef int string_t; + +typedef enum {ev_void, ev_string, ev_float, ev_vector, ev_entity, ev_field, ev_function, ev_pointer} etype_t; + + +#define OFS_NULL 0 +#define OFS_RETURN 1 +#define OFS_PARM0 4 // leave 3 ofs for each parm to hold vectors +#define OFS_PARM1 7 +#define OFS_PARM2 10 +#define OFS_PARM3 13 +#define OFS_PARM4 16 +#define OFS_PARM5 19 +#define OFS_PARM6 22 +#define OFS_PARM7 25 +#define RESERVED_OFS 28 + + +enum { + OP_DONE, + OP_MUL_F, + OP_MUL_V, + OP_MUL_FV, + OP_MUL_VF, + OP_DIV_F, + OP_ADD_F, + OP_ADD_V, + OP_SUB_F, + OP_SUB_V, + + OP_EQ_F, + OP_EQ_V, + OP_EQ_S, + OP_EQ_E, + OP_EQ_FNC, + + OP_NE_F, + OP_NE_V, + OP_NE_S, + OP_NE_E, + OP_NE_FNC, + + OP_LE, + OP_GE, + OP_LT, + OP_GT, + + OP_LOAD_F, + OP_LOAD_V, + OP_LOAD_S, + OP_LOAD_ENT, + OP_LOAD_FLD, + OP_LOAD_FNC, + + OP_ADDRESS, + + OP_STORE_F, + OP_STORE_V, + OP_STORE_S, + OP_STORE_ENT, + OP_STORE_FLD, + OP_STORE_FNC, + + OP_STOREP_F, + OP_STOREP_V, + OP_STOREP_S, + OP_STOREP_ENT, + OP_STOREP_FLD, + OP_STOREP_FNC, + + OP_RETURN, + OP_NOT_F, + OP_NOT_V, + OP_NOT_S, + OP_NOT_ENT, + OP_NOT_FNC, + OP_IF, + OP_IFNOT, + OP_CALL0, + OP_CALL1, + OP_CALL2, + OP_CALL3, + OP_CALL4, + OP_CALL5, + OP_CALL6, + OP_CALL7, + OP_CALL8, + OP_STATE, + OP_GOTO, + OP_AND, + OP_OR, + + OP_BITAND, + OP_BITOR +}; + + +typedef struct statement_s +{ + unsigned short op; + short a,b,c; +} dstatement_t; + +typedef struct +{ + unsigned short type; // if DEF_SAVEGLOBGAL bit is set + // the variable needs to be saved in savegames + unsigned short ofs; + int s_name; +} ddef_t; +#define DEF_SAVEGLOBAL (1<<15) + +#define MAX_PARMS 8 + +typedef struct +{ + int first_statement; // negative numbers are builtins + int parm_start; + int locals; // total ints of parms + locals + + int profile; // runtime + + int s_name; + int s_file; // source file defined in + + int numparms; + byte parm_size[MAX_PARMS]; +} dfunction_t; + + +#define PROG_VERSION 6 +typedef struct +{ + int version; + int crc; // check of header file + + int ofs_statements; + int numstatements; // statement 0 is an error + + int ofs_globaldefs; + int numglobaldefs; + + int ofs_fielddefs; + int numfielddefs; + + int ofs_functions; + int numfunctions; // function 0 is an empty + + int ofs_strings; + int numstrings; // first string is a null string + + int ofs_globals; + int numglobals; + + int entityfields; +} dprograms_t; + diff --git a/QW/server/pr_edict.c b/QW/server/pr_edict.c new file mode 100644 index 0000000..f5db256 --- /dev/null +++ b/QW/server/pr_edict.c @@ -0,0 +1,1085 @@ +/* +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. + +*/ +// sv_edict.c -- entity dictionary + +#include "qwsvdef.h" + +dprograms_t *progs; +dfunction_t *pr_functions; +char *pr_strings; +ddef_t *pr_fielddefs; +ddef_t *pr_globaldefs; +dstatement_t *pr_statements; +globalvars_t *pr_global_struct; +float *pr_globals; // same as pr_global_struct +int pr_edict_size; // in bytes + +int type_size[8] = {1,sizeof(void *)/4,1,3,1,1,sizeof(void *)/4,sizeof(void *)/4}; + +ddef_t *ED_FieldAtOfs (int ofs); +qboolean ED_ParseEpair (void *base, ddef_t *key, char *s); + +#define MAX_FIELD_LEN 64 +#define GEFV_CACHESIZE 2 + +typedef struct { + ddef_t *pcache; + char field[MAX_FIELD_LEN]; +} gefv_cache; + +static gefv_cache gefvCache[GEFV_CACHESIZE] = {{NULL, ""}, {NULL, ""}}; + +func_t SpectatorConnect; +func_t SpectatorThink; +func_t SpectatorDisconnect; + + +/* +================= +ED_ClearEdict + +Sets everything to NULL +================= +*/ +void ED_ClearEdict (edict_t *e) +{ + memset (&e->v, 0, progs->entityfields * 4); + e->free = false; +} + +/* +================= +ED_Alloc + +Either finds a free edict, or allocates a new one. +Try to avoid reusing an entity that was recently freed, because it +can cause the client to think the entity morphed into something else +instead of being removed and recreated, which can cause interpolated +angles and bad trails. +================= +*/ +edict_t *ED_Alloc (void) +{ + int i; + edict_t *e; + + for ( i=MAX_CLIENTS+1 ; i<sv.num_edicts ; i++) + { + e = EDICT_NUM(i); + // the first couple seconds of server time can involve a lot of + // freeing and allocating, so relax the replacement policy + if (e->free && ( e->freetime < 2 || sv.time - e->freetime > 0.5 ) ) + { + ED_ClearEdict (e); + return e; + } + } + + if (i == MAX_EDICTS) + { + Con_Printf ("WARNING: ED_Alloc: no free edicts\n"); + i--; // step on whatever is the last edict + e = EDICT_NUM(i); + SV_UnlinkEdict(e); + } + else + sv.num_edicts++; + e = EDICT_NUM(i); + ED_ClearEdict (e); + + return e; +} + +/* +================= +ED_Free + +Marks the edict as free +FIXME: walk all entities and NULL out references to this entity +================= +*/ +void ED_Free (edict_t *ed) +{ + SV_UnlinkEdict (ed); // unlink from world bsp + + ed->free = true; + ed->v.model = 0; + ed->v.takedamage = 0; + ed->v.modelindex = 0; + ed->v.colormap = 0; + ed->v.skin = 0; + ed->v.frame = 0; + VectorCopy (vec3_origin, ed->v.origin); + VectorCopy (vec3_origin, ed->v.angles); + ed->v.nextthink = -1; + ed->v.solid = 0; + + ed->freetime = sv.time; +} + +//=========================================================================== + +/* +============ +ED_GlobalAtOfs +============ +*/ +ddef_t *ED_GlobalAtOfs (int ofs) +{ + ddef_t *def; + int i; + + for (i=0 ; i<progs->numglobaldefs ; i++) + { + def = &pr_globaldefs[i]; + if (def->ofs == ofs) + return def; + } + return NULL; +} + +/* +============ +ED_FieldAtOfs +============ +*/ +ddef_t *ED_FieldAtOfs (int ofs) +{ + ddef_t *def; + int i; + + for (i=0 ; i<progs->numfielddefs ; i++) + { + def = &pr_fielddefs[i]; + if (def->ofs == ofs) + return def; + } + return NULL; +} + +/* +============ +ED_FindField +============ +*/ +ddef_t *ED_FindField (char *name) +{ + ddef_t *def; + int i; + + for (i=0 ; i<progs->numfielddefs ; i++) + { + def = &pr_fielddefs[i]; + if (!strcmp(PR_GetString(def->s_name),name) ) + return def; + } + return NULL; +} + + +/* +============ +ED_FindGlobal +============ +*/ +ddef_t *ED_FindGlobal (char *name) +{ + ddef_t *def; + int i; + + for (i=0 ; i<progs->numglobaldefs ; i++) + { + def = &pr_globaldefs[i]; + if (!strcmp(PR_GetString(def->s_name),name) ) + return def; + } + return NULL; +} + + +/* +============ +ED_FindFunction +============ +*/ +dfunction_t *ED_FindFunction (char *name) +{ + dfunction_t *func; + int i; + + for (i=0 ; i<progs->numfunctions ; i++) + { + func = &pr_functions[i]; + if (!strcmp(PR_GetString(func->s_name),name) ) + return func; + } + return NULL; +} + +eval_t *GetEdictFieldValue(edict_t *ed, char *field) +{ + ddef_t *def = NULL; + int i; + static int rep = 0; + + for (i=0 ; i<GEFV_CACHESIZE ; i++) + { + if (!strcmp(field, gefvCache[i].field)) + { + def = gefvCache[i].pcache; + goto Done; + } + } + + def = ED_FindField (field); + + if (strlen(field) < MAX_FIELD_LEN) + { + gefvCache[rep].pcache = def; + strcpy (gefvCache[rep].field, field); + rep ^= 1; + } + +Done: + if (!def) + return NULL; + + return (eval_t *)((char *)&ed->v + def->ofs*4); +} + +/* +============ +PR_ValueString + +Returns a string describing *data in a type specific manner +============= +*/ +char *PR_ValueString (etype_t type, eval_t *val) +{ + static char line[256]; + ddef_t *def; + dfunction_t *f; + + type &= ~DEF_SAVEGLOBAL; + + switch (type) + { + case ev_string: + sprintf (line, "%s", PR_GetString(val->string)); + break; + case ev_entity: + sprintf (line, "entity %i", NUM_FOR_EDICT(PROG_TO_EDICT(val->edict)) ); + break; + case ev_function: + f = pr_functions + val->function; + sprintf (line, "%s()", PR_GetString(f->s_name)); + break; + case ev_field: + def = ED_FieldAtOfs ( val->_int ); + sprintf (line, ".%s", PR_GetString(def->s_name)); + break; + case ev_void: + sprintf (line, "void"); + break; + case ev_float: + sprintf (line, "%5.1f", val->_float); + break; + case ev_vector: + sprintf (line, "'%5.1f %5.1f %5.1f'", val->vector[0], val->vector[1], val->vector[2]); + break; + case ev_pointer: + sprintf (line, "pointer"); + break; + default: + sprintf (line, "bad type %i", type); + break; + } + + return line; +} + +/* +============ +PR_UglyValueString + +Returns a string describing *data in a type specific manner +Easier to parse than PR_ValueString +============= +*/ +char *PR_UglyValueString (etype_t type, eval_t *val) +{ + static char line[256]; + ddef_t *def; + dfunction_t *f; + + type &= ~DEF_SAVEGLOBAL; + + switch (type) + { + case ev_string: + sprintf (line, "%s", PR_GetString(val->string)); + break; + case ev_entity: + sprintf (line, "%i", NUM_FOR_EDICT(PROG_TO_EDICT(val->edict))); + break; + case ev_function: + f = pr_functions + val->function; + sprintf (line, "%s", PR_GetString(f->s_name)); + break; + case ev_field: + def = ED_FieldAtOfs ( val->_int ); + sprintf (line, "%s", PR_GetString(def->s_name)); + break; + case ev_void: + sprintf (line, "void"); + break; + case ev_float: + sprintf (line, "%f", val->_float); + break; + case ev_vector: + sprintf (line, "%f %f %f", val->vector[0], val->vector[1], val->vector[2]); + break; + default: + sprintf (line, "bad type %i", type); + break; + } + + return line; +} + +/* +============ +PR_GlobalString + +Returns a string with a description and the contents of a global, +padded to 20 field width +============ +*/ +char *PR_GlobalString (int ofs) +{ + char *s; + int i; + ddef_t *def; + void *val; + static char line[128]; + + val = (void *)&pr_globals[ofs]; + def = ED_GlobalAtOfs(ofs); + if (!def) + sprintf (line,"%i(???)", ofs); + else + { + s = PR_ValueString (def->type, val); + sprintf (line,"%i(%s)%s", ofs, PR_GetString(def->s_name), s); + } + + i = strlen(line); + for ( ; i<20 ; i++) + strcat (line," "); + strcat (line," "); + + return line; +} + +char *PR_GlobalStringNoContents (int ofs) +{ + int i; + ddef_t *def; + static char line[128]; + + def = ED_GlobalAtOfs(ofs); + if (!def) + sprintf (line,"%i(???)", ofs); + else + sprintf (line,"%i(%s)", ofs, PR_GetString(def->s_name)); + + i = strlen(line); + for ( ; i<20 ; i++) + strcat (line," "); + strcat (line," "); + + return line; +} + + +/* +============= +ED_Print + +For debugging +============= +*/ +void ED_Print (edict_t *ed) +{ + int l; + ddef_t *d; + int *v; + int i, j; + char *name; + int type; + + if (ed->free) + { + Con_Printf ("FREE\n"); + return; + } + + for (i=1 ; i<progs->numfielddefs ; i++) + { + d = &pr_fielddefs[i]; + name = PR_GetString(d->s_name); + if (name[strlen(name)-2] == '_') + continue; // skip _x, _y, _z vars + + v = (int *)((char *)&ed->v + d->ofs*4); + + // if the value is still all 0, skip the field + type = d->type & ~DEF_SAVEGLOBAL; + + for (j=0 ; j<type_size[type] ; j++) + if (v[j]) + break; + if (j == type_size[type]) + continue; + + Con_Printf ("%s",name); + l = strlen (name); + while (l++ < 15) + Con_Printf (" "); + + Con_Printf ("%s\n", PR_ValueString(d->type, (eval_t *)v)); + } +} + +/* +============= +ED_Write + +For savegames +============= +*/ +void ED_Write (FILE *f, edict_t *ed) +{ + ddef_t *d; + int *v; + int i, j; + char *name; + int type; + + fprintf (f, "{\n"); + + if (ed->free) + { + fprintf (f, "}\n"); + return; + } + + for (i=1 ; i<progs->numfielddefs ; i++) + { + d = &pr_fielddefs[i]; + name = PR_GetString(d->s_name); + if (name[strlen(name)-2] == '_') + continue; // skip _x, _y, _z vars + + v = (int *)((char *)&ed->v + d->ofs*4); + + // if the value is still all 0, skip the field + type = d->type & ~DEF_SAVEGLOBAL; + for (j=0 ; j<type_size[type] ; j++) + if (v[j]) + break; + if (j == type_size[type]) + continue; + + fprintf (f,"\"%s\" ",name); + fprintf (f,"\"%s\"\n", PR_UglyValueString(d->type, (eval_t *)v)); + } + + fprintf (f, "}\n"); +} + +void ED_PrintNum (int ent) +{ + ED_Print (EDICT_NUM(ent)); +} + +/* +============= +ED_PrintEdicts + +For debugging, prints all the entities in the current server +============= +*/ +void ED_PrintEdicts (void) +{ + int i; + + Con_Printf ("%i entities\n", sv.num_edicts); + for (i=0 ; i<sv.num_edicts ; i++) + { + Con_Printf ("\nEDICT %i:\n",i); + ED_PrintNum (i); + } +} + +/* +============= +ED_PrintEdict_f + +For debugging, prints a single edicy +============= +*/ +void ED_PrintEdict_f (void) +{ + int i; + + i = Q_atoi (Cmd_Argv(1)); + Con_Printf ("\n EDICT %i:\n",i); + ED_PrintNum (i); +} + +/* +============= +ED_Count + +For debugging +============= +*/ +void ED_Count (void) +{ + int i; + edict_t *ent; + int active, models, solid, step; + + active = models = solid = step = 0; + for (i=0 ; i<sv.num_edicts ; i++) + { + ent = EDICT_NUM(i); + if (ent->free) + continue; + active++; + if (ent->v.solid) + solid++; + if (ent->v.model) + models++; + if (ent->v.movetype == MOVETYPE_STEP) + step++; + } + + Con_Printf ("num_edicts:%3i\n", sv.num_edicts); + Con_Printf ("active :%3i\n", active); + Con_Printf ("view :%3i\n", models); + Con_Printf ("touch :%3i\n", solid); + Con_Printf ("step :%3i\n", step); + +} + +/* +============================================================================== + + ARCHIVING GLOBALS + +FIXME: need to tag constants, doesn't really work +============================================================================== +*/ + +/* +============= +ED_WriteGlobals +============= +*/ +void ED_WriteGlobals (FILE *f) +{ + ddef_t *def; + int i; + char *name; + int type; + + fprintf (f,"{\n"); + for (i=0 ; i<progs->numglobaldefs ; i++) + { + def = &pr_globaldefs[i]; + type = def->type; + if ( !(def->type & DEF_SAVEGLOBAL) ) + continue; + type &= ~DEF_SAVEGLOBAL; + + if (type != ev_string + && type != ev_float + && type != ev_entity) + continue; + + name = PR_GetString(def->s_name); + fprintf (f,"\"%s\" ", name); + fprintf (f,"\"%s\"\n", PR_UglyValueString(type, (eval_t *)&pr_globals[def->ofs])); + } + fprintf (f,"}\n"); +} + +/* +============= +ED_ParseGlobals +============= +*/ +void ED_ParseGlobals (char *data) +{ + char keyname[64]; + ddef_t *key; + + while (1) + { + // parse key + data = COM_Parse (data); + if (com_token[0] == '}') + break; + if (!data) + SV_Error ("ED_ParseEntity: EOF without closing brace"); + + strcpy (keyname, com_token); + + // parse value + data = COM_Parse (data); + if (!data) + SV_Error ("ED_ParseEntity: EOF without closing brace"); + + if (com_token[0] == '}') + SV_Error ("ED_ParseEntity: closing brace without data"); + + key = ED_FindGlobal (keyname); + if (!key) + { + Con_Printf ("%s is not a global\n", keyname); + continue; + } + + if (!ED_ParseEpair ((void *)pr_globals, key, com_token)) + SV_Error ("ED_ParseGlobals: parse error"); + } +} + +//============================================================================ + + +/* +============= +ED_NewString +============= +*/ +char *ED_NewString (char *string) +{ + char *new, *new_p; + int i,l; + + l = strlen(string) + 1; + new = Hunk_Alloc (l); + new_p = new; + + for (i=0 ; i< l ; i++) + { + if (string[i] == '\\' && i < l-1) + { + i++; + if (string[i] == 'n') + *new_p++ = '\n'; + else + *new_p++ = '\\'; + } + else + *new_p++ = string[i]; + } + + return new; +} + + +/* +============= +ED_ParseEval + +Can parse either fields or globals +returns false if error +============= +*/ +qboolean ED_ParseEpair (void *base, ddef_t *key, char *s) +{ + int i; + char string[128]; + ddef_t *def; + char *v, *w; + void *d; + dfunction_t *func; + + d = (void *)((int *)base + key->ofs); + + switch (key->type & ~DEF_SAVEGLOBAL) + { + case ev_string: + *(string_t *)d = PR_SetString(ED_NewString (s)); + break; + + case ev_float: + *(float *)d = atof (s); + break; + + case ev_vector: + strcpy (string, s); + v = string; + w = string; + for (i=0 ; i<3 ; i++) + { + while (*v && *v != ' ') + v++; + *v = 0; + ((float *)d)[i] = atof (w); + w = v = v+1; + } + break; + + case ev_entity: + *(int *)d = EDICT_TO_PROG(EDICT_NUM(atoi (s))); + break; + + case ev_field: + def = ED_FindField (s); + if (!def) + { + Con_Printf ("Can't find field %s\n", s); + return false; + } + *(int *)d = G_INT(def->ofs); + break; + + case ev_function: + func = ED_FindFunction (s); + if (!func) + { + Con_Printf ("Can't find function %s\n", s); + return false; + } + *(func_t *)d = func - pr_functions; + break; + + default: + break; + } + return true; +} + +/* +==================== +ED_ParseEdict + +Parses an edict out of the given string, returning the new position +ed should be a properly initialized empty edict. +Used for initial level load and for savegames. +==================== +*/ +char *ED_ParseEdict (char *data, edict_t *ent) +{ + ddef_t *key; + qboolean anglehack; + qboolean init; + char keyname[256]; + + init = false; + +// clear it + if (ent != sv.edicts) // hack + memset (&ent->v, 0, progs->entityfields * 4); + +// go through all the dictionary pairs + while (1) + { + // parse key + data = COM_Parse (data); + if (com_token[0] == '}') + break; + if (!data) + SV_Error ("ED_ParseEntity: EOF without closing brace"); + +// anglehack is to allow QuakeEd to write single scalar angles +// and allow them to be turned into vectors. (FIXME...) +if (!strcmp(com_token, "angle")) +{ + strcpy (com_token, "angles"); + anglehack = true; +} +else + anglehack = false; + +// FIXME: change light to _light to get rid of this hack +if (!strcmp(com_token, "light")) + strcpy (com_token, "light_lev"); // hack for single light def + + strcpy (keyname, com_token); + + // parse value + data = COM_Parse (data); + if (!data) + SV_Error ("ED_ParseEntity: EOF without closing brace"); + + if (com_token[0] == '}') + SV_Error ("ED_ParseEntity: closing brace without data"); + + init = true; + +// keynames with a leading underscore are used for utility comments, +// and are immediately discarded by quake + if (keyname[0] == '_') + continue; + + key = ED_FindField (keyname); + if (!key) + { + Con_Printf ("%s is not a field\n", keyname); + continue; + } + +if (anglehack) +{ +char temp[32]; +strcpy (temp, com_token); +sprintf (com_token, "0 %s 0", temp); +} + + if (!ED_ParseEpair ((void *)&ent->v, key, com_token)) + SV_Error ("ED_ParseEdict: parse error"); + } + + if (!init) + ent->free = true; + + return data; +} + + +/* +================ +ED_LoadFromFile + +The entities are directly placed in the array, rather than allocated with +ED_Alloc, because otherwise an error loading the map would have entity +number references out of order. + +Creates a server's entity / program execution context by +parsing textual entity definitions out of an ent file. + +Used for both fresh maps and savegame loads. A fresh map would also need +to call ED_CallSpawnFunctions () to let the objects initialize themselves. +================ +*/ +void ED_LoadFromFile (char *data) +{ + edict_t *ent; + int inhibit; + dfunction_t *func; + + ent = NULL; + inhibit = 0; + pr_global_struct->time = sv.time; + +// parse ents + while (1) + { +// parse the opening brace + data = COM_Parse (data); + if (!data) + break; + if (com_token[0] != '{') + SV_Error ("ED_LoadFromFile: found %s when expecting {",com_token); + + if (!ent) + ent = EDICT_NUM(0); + else + ent = ED_Alloc (); + data = ED_ParseEdict (data, ent); + +// remove things from different skill levels or deathmatch + if (((int)ent->v.spawnflags & SPAWNFLAG_NOT_DEATHMATCH)) + { + ED_Free (ent); + inhibit++; + continue; + } + +// +// immediately call spawn function +// + if (!ent->v.classname) + { + Con_Printf ("No classname for:\n"); + ED_Print (ent); + ED_Free (ent); + continue; + } + + // look for the spawn function + func = ED_FindFunction ( PR_GetString(ent->v.classname) ); + + if (!func) + { + Con_Printf ("No spawn function for:\n"); + ED_Print (ent); + ED_Free (ent); + continue; + } + + pr_global_struct->self = EDICT_TO_PROG(ent); + PR_ExecuteProgram (func - pr_functions); + SV_FlushSignon(); + } + + Con_DPrintf ("%i entities inhibited\n", inhibit); +} + + +/* +=============== +PR_LoadProgs +=============== +*/ +void PR_LoadProgs (void) +{ + int i; + char num[32]; + dfunction_t *f; + +// flush the non-C variable lookup cache + for (i=0 ; i<GEFV_CACHESIZE ; i++) + gefvCache[i].field[0] = 0; + + progs = (dprograms_t *)COM_LoadHunkFile ("qwprogs.dat"); + if (!progs) + progs = (dprograms_t *)COM_LoadHunkFile ("progs.dat"); + if (!progs) + SV_Error ("PR_LoadProgs: couldn't load progs.dat"); + Con_DPrintf ("Programs occupy %iK.\n", com_filesize/1024); + +// add prog crc to the serverinfo + sprintf (num, "%i", CRC_Block ((byte *)progs, com_filesize)); + Info_SetValueForStarKey (svs.info, "*progs", num, MAX_SERVERINFO_STRING); + +// byte swap the header + for (i=0 ; i<sizeof(*progs)/4 ; i++) + ((int *)progs)[i] = LittleLong ( ((int *)progs)[i] ); + + if (progs->version != PROG_VERSION) + SV_Error ("progs.dat has wrong version number (%i should be %i)", progs->version, PROG_VERSION); + if (progs->crc != PROGHEADER_CRC) + SV_Error ("You must have the progs.dat from QuakeWorld installed"); + + pr_functions = (dfunction_t *)((byte *)progs + progs->ofs_functions); + pr_strings = (char *)progs + progs->ofs_strings; + pr_globaldefs = (ddef_t *)((byte *)progs + progs->ofs_globaldefs); + pr_fielddefs = (ddef_t *)((byte *)progs + progs->ofs_fielddefs); + pr_statements = (dstatement_t *)((byte *)progs + progs->ofs_statements); + + num_prstr = 0; + + pr_global_struct = (globalvars_t *)((byte *)progs + progs->ofs_globals); + pr_globals = (float *)pr_global_struct; + + pr_edict_size = progs->entityfields * 4 + sizeof (edict_t) - sizeof(entvars_t); + +// byte swap the lumps + for (i=0 ; i<progs->numstatements ; i++) + { + pr_statements[i].op = LittleShort(pr_statements[i].op); + pr_statements[i].a = LittleShort(pr_statements[i].a); + pr_statements[i].b = LittleShort(pr_statements[i].b); + pr_statements[i].c = LittleShort(pr_statements[i].c); + } + + for (i=0 ; i<progs->numfunctions; i++) + { + pr_functions[i].first_statement = LittleLong (pr_functions[i].first_statement); + pr_functions[i].parm_start = LittleLong (pr_functions[i].parm_start); + pr_functions[i].s_name = LittleLong (pr_functions[i].s_name); + pr_functions[i].s_file = LittleLong (pr_functions[i].s_file); + pr_functions[i].numparms = LittleLong (pr_functions[i].numparms); + pr_functions[i].locals = LittleLong (pr_functions[i].locals); + } + + for (i=0 ; i<progs->numglobaldefs ; i++) + { + pr_globaldefs[i].type = LittleShort (pr_globaldefs[i].type); + pr_globaldefs[i].ofs = LittleShort (pr_globaldefs[i].ofs); + pr_globaldefs[i].s_name = LittleLong (pr_globaldefs[i].s_name); + } + + for (i=0 ; i<progs->numfielddefs ; i++) + { + pr_fielddefs[i].type = LittleShort (pr_fielddefs[i].type); + if (pr_fielddefs[i].type & DEF_SAVEGLOBAL) + SV_Error ("PR_LoadProgs: pr_fielddefs[i].type & DEF_SAVEGLOBAL"); + pr_fielddefs[i].ofs = LittleShort (pr_fielddefs[i].ofs); + pr_fielddefs[i].s_name = LittleLong (pr_fielddefs[i].s_name); + } + + for (i=0 ; i<progs->numglobals ; i++) + ((int *)pr_globals)[i] = LittleLong (((int *)pr_globals)[i]); + + // Zoid, find the spectator functions + SpectatorConnect = SpectatorThink = SpectatorDisconnect = 0; + + if ((f = ED_FindFunction ("SpectatorConnect")) != NULL) + SpectatorConnect = (func_t)(f - pr_functions); + if ((f = ED_FindFunction ("SpectatorThink")) != NULL) + SpectatorThink = (func_t)(f - pr_functions); + if ((f = ED_FindFunction ("SpectatorDisconnect")) != NULL) + SpectatorDisconnect = (func_t)(f - pr_functions); +} + + +/* +=============== +PR_Init +=============== +*/ +void PR_Init (void) +{ + Cmd_AddCommand ("edict", ED_PrintEdict_f); + Cmd_AddCommand ("edicts", ED_PrintEdicts); + Cmd_AddCommand ("edictcount", ED_Count); + Cmd_AddCommand ("profile", PR_Profile_f); +} + + + +edict_t *EDICT_NUM(int n) +{ + if (n < 0 || n >= MAX_EDICTS) + SV_Error ("EDICT_NUM: bad number %i", n); + return (edict_t *)((byte *)sv.edicts+ (n)*pr_edict_size); +} + +int NUM_FOR_EDICT(edict_t *e) +{ + int b; + + b = (byte *)e - (byte *)sv.edicts; + b = b / pr_edict_size; + + if (b < 0 || b >= sv.num_edicts) + SV_Error ("NUM_FOR_EDICT: bad pointer"); + return b; +} + + diff --git a/QW/server/pr_exec.c b/QW/server/pr_exec.c new file mode 100644 index 0000000..8f5c351 --- /dev/null +++ b/QW/server/pr_exec.c @@ -0,0 +1,699 @@ +/* +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. + +*/ + +#include "qwsvdef.h" + + +/* + +*/ + +typedef struct +{ + int s; + dfunction_t *f; +} prstack_t; + +#define MAX_STACK_DEPTH 32 +prstack_t pr_stack[MAX_STACK_DEPTH]; +int pr_depth; + +#define LOCALSTACK_SIZE 2048 +int localstack[LOCALSTACK_SIZE]; +int localstack_used; + + +qboolean pr_trace; +dfunction_t *pr_xfunction; +int pr_xstatement; + + +int pr_argc; + +char *pr_opnames[] = +{ +"DONE", + +"MUL_F", +"MUL_V", +"MUL_FV", +"MUL_VF", + +"DIV", + +"ADD_F", +"ADD_V", + +"SUB_F", +"SUB_V", + +"EQ_F", +"EQ_V", +"EQ_S", +"EQ_E", +"EQ_FNC", + +"NE_F", +"NE_V", +"NE_S", +"NE_E", +"NE_FNC", + +"LE", +"GE", +"LT", +"GT", + +"INDIRECT", +"INDIRECT", +"INDIRECT", +"INDIRECT", +"INDIRECT", +"INDIRECT", + +"ADDRESS", + +"STORE_F", +"STORE_V", +"STORE_S", +"STORE_ENT", +"STORE_FLD", +"STORE_FNC", + +"STOREP_F", +"STOREP_V", +"STOREP_S", +"STOREP_ENT", +"STOREP_FLD", +"STOREP_FNC", + +"RETURN", + +"NOT_F", +"NOT_V", +"NOT_S", +"NOT_ENT", +"NOT_FNC", + +"IF", +"IFNOT", + +"CALL0", +"CALL1", +"CALL2", +"CALL3", +"CALL4", +"CALL5", +"CALL6", +"CALL7", +"CALL8", + +"STATE", + +"GOTO", + +"AND", +"OR", + +"BITAND", +"BITOR" +}; + +char *PR_GlobalString (int ofs); +char *PR_GlobalStringNoContents (int ofs); + + +//============================================================================= + +/* +================= +PR_PrintStatement +================= +*/ +void PR_PrintStatement (dstatement_t *s) +{ + int i; + + if ( (unsigned)s->op < sizeof(pr_opnames)/sizeof(pr_opnames[0])) + { + Con_Printf ("%s ", pr_opnames[s->op]); + i = strlen(pr_opnames[s->op]); + for ( ; i<10 ; i++) + Con_Printf (" "); + } + + if (s->op == OP_IF || s->op == OP_IFNOT) + Con_Printf ("%sbranch %i",PR_GlobalString(s->a),s->b); + else if (s->op == OP_GOTO) + { + Con_Printf ("branch %i",s->a); + } + else if ( (unsigned)(s->op - OP_STORE_F) < 6) + { + Con_Printf ("%s",PR_GlobalString(s->a)); + Con_Printf ("%s", PR_GlobalStringNoContents(s->b)); + } + else + { + if (s->a) + Con_Printf ("%s",PR_GlobalString(s->a)); + if (s->b) + Con_Printf ("%s",PR_GlobalString(s->b)); + if (s->c) + Con_Printf ("%s", PR_GlobalStringNoContents(s->c)); + } + Con_Printf ("\n"); +} + +/* +============ +PR_StackTrace +============ +*/ +void PR_StackTrace (void) +{ + dfunction_t *f; + int i; + + if (pr_depth == 0) + { + Con_Printf ("<NO STACK>\n"); + return; + } + + pr_stack[pr_depth].f = pr_xfunction; + for (i=pr_depth ; i>=0 ; i--) + { + f = pr_stack[i].f; + + if (!f) + { + Con_Printf ("<NO FUNCTION>\n"); + } + else + Con_Printf ("%12s : %s\n", PR_GetString(f->s_file), PR_GetString(f->s_name)); + } +} + + +/* +============ +PR_Profile_f + +============ +*/ +void PR_Profile_f (void) +{ + dfunction_t *f, *best; + int max; + int num; + int i; + + num = 0; + do + { + max = 0; + best = NULL; + for (i=0 ; i<progs->numfunctions ; i++) + { + f = &pr_functions[i]; + if (f->profile > max) + { + max = f->profile; + best = f; + } + } + if (best) + { + if (num < 10) + Con_Printf ("%7i %s\n", best->profile, PR_GetString(best->s_name)); + num++; + best->profile = 0; + } + } while (best); +} + + +/* +============ +PR_RunError + +Aborts the currently executing function +============ +*/ +void PR_RunError (char *error, ...) +{ + va_list argptr; + char string[1024]; + + va_start (argptr,error); + vsprintf (string,error,argptr); + va_end (argptr); + + PR_PrintStatement (pr_statements + pr_xstatement); + PR_StackTrace (); + Con_Printf ("%s\n", string); + + pr_depth = 0; // dump the stack so SV_Error can shutdown functions + + SV_Error ("Program error"); +} + +/* +============================================================================ +PR_ExecuteProgram + +The interpretation main loop +============================================================================ +*/ + +/* +==================== +PR_EnterFunction + +Returns the new program statement counter +==================== +*/ +int PR_EnterFunction (dfunction_t *f) +{ + int i, j, c, o; + + pr_stack[pr_depth].s = pr_xstatement; + pr_stack[pr_depth].f = pr_xfunction; + pr_depth++; + if (pr_depth >= MAX_STACK_DEPTH) + PR_RunError ("stack overflow"); + +// save off any locals that the new function steps on + c = f->locals; + if (localstack_used + c > LOCALSTACK_SIZE) + PR_RunError ("PR_ExecuteProgram: locals stack overflow\n"); + + for (i=0 ; i < c ; i++) + localstack[localstack_used+i] = ((int *)pr_globals)[f->parm_start + i]; + localstack_used += c; + +// copy parameters + o = f->parm_start; + for (i=0 ; i<f->numparms ; i++) + { + for (j=0 ; j<f->parm_size[i] ; j++) + { + ((int *)pr_globals)[o] = ((int *)pr_globals)[OFS_PARM0+i*3+j]; + o++; + } + } + + pr_xfunction = f; + return f->first_statement - 1; // offset the s++ +} + +/* +==================== +PR_LeaveFunction +==================== +*/ +int PR_LeaveFunction (void) +{ + int i, c; + + if (pr_depth <= 0) + SV_Error ("prog stack underflow"); + +// restore locals from the stack + c = pr_xfunction->locals; + localstack_used -= c; + if (localstack_used < 0) + PR_RunError ("PR_ExecuteProgram: locals stack underflow\n"); + + for (i=0 ; i < c ; i++) + ((int *)pr_globals)[pr_xfunction->parm_start + i] = localstack[localstack_used+i]; + +// up stack + pr_depth--; + pr_xfunction = pr_stack[pr_depth].f; + return pr_stack[pr_depth].s; +} + + +/* +==================== +PR_ExecuteProgram +==================== +*/ +void PR_ExecuteProgram (func_t fnum) +{ + eval_t *a, *b, *c; + int s; + dstatement_t *st; + dfunction_t *f, *newf; + int runaway; + int i; + edict_t *ed; + int exitdepth; + eval_t *ptr; + + if (!fnum || fnum >= progs->numfunctions) + { + if (pr_global_struct->self) + ED_Print (PROG_TO_EDICT(pr_global_struct->self)); + SV_Error ("PR_ExecuteProgram: NULL function"); + } + + f = &pr_functions[fnum]; + + runaway = 100000; + pr_trace = false; + +// make a stack frame + exitdepth = pr_depth; + + s = PR_EnterFunction (f); + +while (1) +{ + s++; // next statement + + st = &pr_statements[s]; + a = (eval_t *)&pr_globals[st->a]; + b = (eval_t *)&pr_globals[st->b]; + c = (eval_t *)&pr_globals[st->c]; + + if (--runaway == 0) + PR_RunError ("runaway loop error"); + + pr_xfunction->profile++; + pr_xstatement = s; + + if (pr_trace) + PR_PrintStatement (st); + + switch (st->op) + { + case OP_ADD_F: + c->_float = a->_float + b->_float; + break; + case OP_ADD_V: + c->vector[0] = a->vector[0] + b->vector[0]; + c->vector[1] = a->vector[1] + b->vector[1]; + c->vector[2] = a->vector[2] + b->vector[2]; + break; + + case OP_SUB_F: + c->_float = a->_float - b->_float; + break; + case OP_SUB_V: + c->vector[0] = a->vector[0] - b->vector[0]; + c->vector[1] = a->vector[1] - b->vector[1]; + c->vector[2] = a->vector[2] - b->vector[2]; + break; + + case OP_MUL_F: + c->_float = a->_float * b->_float; + break; + case OP_MUL_V: + c->_float = a->vector[0]*b->vector[0] + + a->vector[1]*b->vector[1] + + a->vector[2]*b->vector[2]; + break; + case OP_MUL_FV: + c->vector[0] = a->_float * b->vector[0]; + c->vector[1] = a->_float * b->vector[1]; + c->vector[2] = a->_float * b->vector[2]; + break; + case OP_MUL_VF: + c->vector[0] = b->_float * a->vector[0]; + c->vector[1] = b->_float * a->vector[1]; + c->vector[2] = b->_float * a->vector[2]; + break; + + case OP_DIV_F: + c->_float = a->_float / b->_float; + break; + + case OP_BITAND: + c->_float = (int)a->_float & (int)b->_float; + break; + + case OP_BITOR: + c->_float = (int)a->_float | (int)b->_float; + break; + + + case OP_GE: + c->_float = a->_float >= b->_float; + break; + case OP_LE: + c->_float = a->_float <= b->_float; + break; + case OP_GT: + c->_float = a->_float > b->_float; + break; + case OP_LT: + c->_float = a->_float < b->_float; + break; + case OP_AND: + c->_float = a->_float && b->_float; + break; + case OP_OR: + c->_float = a->_float || b->_float; + break; + + case OP_NOT_F: + c->_float = !a->_float; + break; + case OP_NOT_V: + c->_float = !a->vector[0] && !a->vector[1] && !a->vector[2]; + break; + case OP_NOT_S: + c->_float = !a->string || !*PR_GetString(a->string); + break; + case OP_NOT_FNC: + c->_float = !a->function; + break; + case OP_NOT_ENT: + c->_float = (PROG_TO_EDICT(a->edict) == sv.edicts); + break; + + case OP_EQ_F: + c->_float = a->_float == b->_float; + break; + case OP_EQ_V: + c->_float = (a->vector[0] == b->vector[0]) && + (a->vector[1] == b->vector[1]) && + (a->vector[2] == b->vector[2]); + break; + case OP_EQ_S: + c->_float = !strcmp(PR_GetString(a->string), PR_GetString(b->string)); + break; + case OP_EQ_E: + c->_float = a->_int == b->_int; + break; + case OP_EQ_FNC: + c->_float = a->function == b->function; + break; + + + case OP_NE_F: + c->_float = a->_float != b->_float; + break; + case OP_NE_V: + c->_float = (a->vector[0] != b->vector[0]) || + (a->vector[1] != b->vector[1]) || + (a->vector[2] != b->vector[2]); + break; + case OP_NE_S: + c->_float = strcmp(PR_GetString(a->string), PR_GetString(b->string)); + break; + case OP_NE_E: + c->_float = a->_int != b->_int; + break; + case OP_NE_FNC: + c->_float = a->function != b->function; + break; + +//================== + case OP_STORE_F: + case OP_STORE_ENT: + case OP_STORE_FLD: // integers + case OP_STORE_S: + case OP_STORE_FNC: // pointers + b->_int = a->_int; + break; + case OP_STORE_V: + b->vector[0] = a->vector[0]; + b->vector[1] = a->vector[1]; + b->vector[2] = a->vector[2]; + break; + + case OP_STOREP_F: + case OP_STOREP_ENT: + case OP_STOREP_FLD: // integers + case OP_STOREP_S: + case OP_STOREP_FNC: // pointers + ptr = (eval_t *)((byte *)sv.edicts + b->_int); + ptr->_int = a->_int; + break; + case OP_STOREP_V: + ptr = (eval_t *)((byte *)sv.edicts + b->_int); + ptr->vector[0] = a->vector[0]; + ptr->vector[1] = a->vector[1]; + ptr->vector[2] = a->vector[2]; + break; + + case OP_ADDRESS: + ed = PROG_TO_EDICT(a->edict); +#ifdef PARANOID + NUM_FOR_EDICT(ed); // make sure it's in range +#endif + if (ed == (edict_t *)sv.edicts && sv.state == ss_active) + PR_RunError ("assignment to world entity"); + c->_int = (byte *)((int *)&ed->v + b->_int) - (byte *)sv.edicts; + break; + + case OP_LOAD_F: + case OP_LOAD_FLD: + case OP_LOAD_ENT: + case OP_LOAD_S: + case OP_LOAD_FNC: + ed = PROG_TO_EDICT(a->edict); +#ifdef PARANOID + NUM_FOR_EDICT(ed); // make sure it's in range +#endif + a = (eval_t *)((int *)&ed->v + b->_int); + c->_int = a->_int; + break; + + case OP_LOAD_V: + ed = PROG_TO_EDICT(a->edict); +#ifdef PARANOID + NUM_FOR_EDICT(ed); // make sure it's in range +#endif + a = (eval_t *)((int *)&ed->v + b->_int); + c->vector[0] = a->vector[0]; + c->vector[1] = a->vector[1]; + c->vector[2] = a->vector[2]; + break; + +//================== + + case OP_IFNOT: + if (!a->_int) + s += st->b - 1; // offset the s++ + break; + + case OP_IF: + if (a->_int) + s += st->b - 1; // offset the s++ + break; + + case OP_GOTO: + s += st->a - 1; // offset the s++ + break; + + case OP_CALL0: + case OP_CALL1: + case OP_CALL2: + case OP_CALL3: + case OP_CALL4: + case OP_CALL5: + case OP_CALL6: + case OP_CALL7: + case OP_CALL8: + pr_argc = st->op - OP_CALL0; + if (!a->function) + PR_RunError ("NULL function"); + + newf = &pr_functions[a->function]; + + if (newf->first_statement < 0) + { // negative statements are built in functions + i = -newf->first_statement; + if (i >= pr_numbuiltins) + PR_RunError ("Bad builtin call number"); + pr_builtins[i] (); + break; + } + + s = PR_EnterFunction (newf); + break; + + case OP_DONE: + case OP_RETURN: + pr_globals[OFS_RETURN] = pr_globals[st->a]; + pr_globals[OFS_RETURN+1] = pr_globals[st->a+1]; + pr_globals[OFS_RETURN+2] = pr_globals[st->a+2]; + + s = PR_LeaveFunction (); + if (pr_depth == exitdepth) + return; // all done + break; + + case OP_STATE: + ed = PROG_TO_EDICT(pr_global_struct->self); + ed->v.nextthink = pr_global_struct->time + 0.1; + if (a->_float != ed->v.frame) + { + ed->v.frame = a->_float; + } + ed->v.think = b->function; + break; + + default: + PR_RunError ("Bad opcode %i", st->op); + } +} + +} + +/*----------------------*/ + +char *pr_strtbl[MAX_PRSTR]; +int num_prstr; + +char *PR_GetString(int num) +{ + if (num < 0) { +//Con_DPrintf("GET:%d == %s\n", num, pr_strtbl[-num]); + return pr_strtbl[-num]; + } + return pr_strings + num; +} + +int PR_SetString(char *s) +{ + int i; + + if (s - pr_strings < 0) { + for (i = 0; i <= num_prstr; i++) + if (pr_strtbl[i] == s) + break; + if (i < num_prstr) + return -i; + if (num_prstr == MAX_PRSTR - 1) + Sys_Error("MAX_PRSTR"); + num_prstr++; + pr_strtbl[num_prstr] = s; +//Con_DPrintf("SET:%d == %s\n", -num_prstr, s); + return -num_prstr; + } + return (int)(s - pr_strings); +} + diff --git a/QW/server/profile.txt b/QW/server/profile.txt new file mode 100644 index 0000000..4a92872 --- /dev/null +++ b/QW/server/profile.txt @@ -0,0 +1,41 @@ + 25840.596 39.6 65204.788 100.0 1 _main (sys_win.obj) + 6449.591 9.9 6850.383 10.5 5 _COM_LoadFile (common.obj) + 3813.669 5.8 3813.669 5.8 41293 _AddEntsToPmove (sv_user.obj) + 3538.778 5.4 3671.834 5.6 2 _SV_CalcPHS (sv_init.obj) + 2832.023 4.3 3860.142 5.9 108369 _PR_ExecuteProgram (pr_exec.obj) + 2024.012 3.1 2535.985 3.9 3101158 _PM_RecursiveHullCheck (pmovetst.obj) + 1703.335 2.6 1713.442 2.6 2906788 _SV_RecursiveHullCheck (world.obj) + 1679.222 2.6 1694.639 2.6 42208 _NET_GetPacket (net_wins.obj) + 1311.947 2.0 2442.941 3.7 9423 _SV_WriteEntitiesToClient (sv_send.obj) + 995.298 1.5 1001.797 1.5 9751 _NET_SendPacket (net_wins.obj) + 957.416 1.5 957.416 1.5 122264 _AngleVectors (mathlib.obj) + 800.625 1.2 885.897 1.4 1446092 _SV_FindTouchedLeafs (world.obj) + 776.868 1.2 776.868 1.2 4583 _Sys_ConsoleInput (sys_win.obj) + 678.630 1.0 4809.817 7.4 1011039 _SV_RunEntity (sv_phys.obj) + 619.906 1.0 619.906 1.0 95170 _PM_PointContents (pmovetst.obj) + 512.033 0.8 3063.593 4.7 123219 _PM_PlayerMove (pmovetst.obj) + 511.973 0.8 511.973 0.8 183507 _PM_HullPointContents (pmovetst.obj) + 478.937 0.7 498.020 0.8 776129 _SV_ClipToLinks (world.obj) + 427.858 0.7 427.858 0.7 15333 _Mod_DecompressVis (model.obj) + 403.567 0.6 745.403 1.1 11587 _SV_AddToFatPVS (sv_send.obj) + 375.905 0.6 498.825 0.8 256663 _SV_TouchLinks (world.obj) + 324.689 0.5 361.897 0.6 4 _COM_LoadPackFile (common.obj) + 315.352 0.5 532.091 0.8 445925 _SV_Physics_Toss (sv_phys.obj) + 312.962 0.5 13855.472 21.2 45555 _SV_RunCmd (sv_user.obj) + 286.925 0.4 3259.854 5.0 1014 _SV_Push (sv_phys.obj) + 274.811 0.4 391.274 0.6 901924 _SV_RunThink (sv_phys.obj) + 269.635 0.4 269.635 0.4 19 _Sys_FileTime (sys_win.obj) + 249.745 0.4 2904.674 4.5 153853 _SV_Move (world.obj) + 233.801 0.4 233.801 0.4 13724 _Sys_DoubleTime (sys_win.obj) + 228.801 0.4 5602.910 8.6 4579 _SV_Physics (sv_phys.obj) + 203.463 0.3 203.463 0.3 8931 _SV_ModelIndex (sv_init.obj) + 193.181 0.3 193.181 0.3 315189 _PR_EnterFunction (pr_exec.obj) + 183.088 0.3 1623.544 2.5 60223 _SV_LinkEdict (world.obj) + 172.218 0.3 172.609 0.3 156480 _SV_HullForEntity (world.obj) + 171.956 0.3 171.956 0.3 242 _Sys_Printf (sys_win.obj) + 169.198 0.3 176.432 0.3 1 _Memory_Init (zone.obj) + 166.687 0.3 2052.738 3.1 156480 _SV_ClipMoveToEntity (world.obj) + 164.941 0.3 164.941 0.3 17116 _CalcSurfaceExtents (model.obj) + 161.209 0.2 161.209 0.2 118953 _VectorNormalize (mathlib.obj) + 142.722 0.2 142.722 0.2 7 _COM_filelength (common.obj) +
\ No newline at end of file diff --git a/QW/server/progdefs.h b/QW/server/progdefs.h new file mode 100644 index 0000000..7563a43 --- /dev/null +++ b/QW/server/progdefs.h @@ -0,0 +1,159 @@ +/* +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. + +*/ + +/* file generated by qcc, do not modify */ + +typedef struct +{ int pad[28]; + int self; + int other; + int world; + float time; + float frametime; + int newmis; + float force_retouch; + string_t mapname; + float serverflags; + float total_secrets; + float total_monsters; + float found_secrets; + float killed_monsters; + float parm1; + float parm2; + float parm3; + float parm4; + float parm5; + float parm6; + float parm7; + float parm8; + float parm9; + float parm10; + float parm11; + float parm12; + float parm13; + float parm14; + float parm15; + float parm16; + vec3_t v_forward; + vec3_t v_up; + vec3_t v_right; + float trace_allsolid; + float trace_startsolid; + float trace_fraction; + vec3_t trace_endpos; + vec3_t trace_plane_normal; + float trace_plane_dist; + int trace_ent; + float trace_inopen; + float trace_inwater; + int msg_entity; + func_t main; + func_t StartFrame; + func_t PlayerPreThink; + func_t PlayerPostThink; + func_t ClientKill; + func_t ClientConnect; + func_t PutClientInServer; + func_t ClientDisconnect; + func_t SetNewParms; + func_t SetChangeParms; +} globalvars_t; + +typedef struct +{ + float modelindex; + vec3_t absmin; + vec3_t absmax; + float ltime; + float lastruntime; + float movetype; + float solid; + vec3_t origin; + vec3_t oldorigin; + vec3_t velocity; + vec3_t angles; + vec3_t avelocity; + string_t classname; + string_t model; + float frame; + float skin; + float effects; + vec3_t mins; + vec3_t maxs; + vec3_t size; + func_t touch; + func_t use; + func_t think; + func_t blocked; + float nextthink; + int groundentity; + float health; + float frags; + float weapon; + string_t weaponmodel; + float weaponframe; + float currentammo; + float ammo_shells; + float ammo_nails; + float ammo_rockets; + float ammo_cells; + float items; + float takedamage; + int chain; + float deadflag; + vec3_t view_ofs; + float button0; + float button1; + float button2; + float impulse; + float fixangle; + vec3_t v_angle; + string_t netname; + int enemy; + float flags; + float colormap; + float team; + float max_health; + float teleport_time; + float armortype; + float armorvalue; + float waterlevel; + float watertype; + float ideal_yaw; + float yaw_speed; + int aiment; + int goalentity; + float spawnflags; + string_t target; + string_t targetname; + float dmg_take; + float dmg_save; + int dmg_inflictor; + int owner; + vec3_t movedir; + string_t message; + float sounds; + string_t noise; + string_t noise1; + string_t noise2; + string_t noise3; +} entvars_t; + +#define PROGHEADER_CRC 54730 diff --git a/QW/server/progs.h b/QW/server/progs.h new file mode 100644 index 0000000..dd23433 --- /dev/null +++ b/QW/server/progs.h @@ -0,0 +1,147 @@ +/* +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. + +*/ + +#include "pr_comp.h" // defs shared with qcc +#include "progdefs.h" // generated by program cdefs + +typedef union eval_s +{ + string_t string; + float _float; + float vector[3]; + func_t function; + int _int; + int edict; +} eval_t; + +#define MAX_ENT_LEAFS 16 +typedef struct edict_s +{ + qboolean free; + link_t area; // linked to a division node or leaf + + int num_leafs; + short leafnums[MAX_ENT_LEAFS]; + + entity_state_t baseline; + + float freetime; // sv.time when the object was freed + entvars_t v; // C exported fields from progs +// other fields from progs come immediately after +} edict_t; +#define EDICT_FROM_AREA(l) STRUCT_FROM_LINK(l,edict_t,area) + +//============================================================================ + +extern dprograms_t *progs; +extern dfunction_t *pr_functions; +extern char *pr_strings; +extern ddef_t *pr_globaldefs; +extern ddef_t *pr_fielddefs; +extern dstatement_t *pr_statements; +extern globalvars_t *pr_global_struct; +extern float *pr_globals; // same as pr_global_struct + +extern int pr_edict_size; // in bytes + +//============================================================================ + +void PR_Init (void); + +void PR_ExecuteProgram (func_t fnum); +void PR_LoadProgs (void); + +void PR_Profile_f (void); + +edict_t *ED_Alloc (void); +void ED_Free (edict_t *ed); + +char *ED_NewString (char *string); +// returns a copy of the string allocated from the server's string heap + +void ED_Print (edict_t *ed); +void ED_Write (FILE *f, edict_t *ed); +char *ED_ParseEdict (char *data, edict_t *ent); + +void ED_WriteGlobals (FILE *f); +void ED_ParseGlobals (char *data); + +void ED_LoadFromFile (char *data); + +//define EDICT_NUM(n) ((edict_t *)(sv.edicts+ (n)*pr_edict_size)) +//define NUM_FOR_EDICT(e) (((byte *)(e) - sv.edicts)/pr_edict_size) + +edict_t *EDICT_NUM(int n); +int NUM_FOR_EDICT(edict_t *e); + +#define NEXT_EDICT(e) ((edict_t *)( (byte *)e + pr_edict_size)) + +#define EDICT_TO_PROG(e) ((byte *)e - (byte *)sv.edicts) +#define PROG_TO_EDICT(e) ((edict_t *)((byte *)sv.edicts + e)) + +//============================================================================ + +#define G_FLOAT(o) (pr_globals[o]) +#define G_INT(o) (*(int *)&pr_globals[o]) +#define G_EDICT(o) ((edict_t *)((byte *)sv.edicts+ *(int *)&pr_globals[o])) +#define G_EDICTNUM(o) NUM_FOR_EDICT(G_EDICT(o)) +#define G_VECTOR(o) (&pr_globals[o]) +#define G_STRING(o) (PR_GetString(*(string_t *)&pr_globals[o])) +#define G_FUNCTION(o) (*(func_t *)&pr_globals[o]) + +#define E_FLOAT(e,o) (((float*)&e->v)[o]) +#define E_INT(e,o) (*(int *)&((float*)&e->v)[o]) +#define E_VECTOR(e,o) (&((float*)&e->v)[o]) +#define E_STRING(e,o) (PR_GetString(*(string_t *)&((float*)&e->v)[o])) + +extern int type_size[8]; + +typedef void (*builtin_t) (void); +extern builtin_t *pr_builtins; +extern int pr_numbuiltins; + +extern int pr_argc; + +extern qboolean pr_trace; +extern dfunction_t *pr_xfunction; +extern int pr_xstatement; + +extern func_t SpectatorConnect; +extern func_t SpectatorThink; +extern func_t SpectatorDisconnect; + +void PR_RunError (char *error, ...); + +void ED_PrintEdicts (void); +void ED_PrintNum (int ent); + +eval_t *GetEdictFieldValue(edict_t *ed, char *field); + +// +// PR STrings stuff +// +#define MAX_PRSTR 1024 + +extern char *pr_strtbl[MAX_PRSTR]; +extern int num_prstr; + +char *PR_GetString(int num); +int PR_SetString(char *s); + diff --git a/QW/server/quakeasm.h b/QW/server/quakeasm.h new file mode 100644 index 0000000..5afcce1 --- /dev/null +++ b/QW/server/quakeasm.h @@ -0,0 +1,32 @@ +/* +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. + +*/ +// +// quakeasm.h: general asm header file +// + +#if __i386__ +#define id386 1 +#else +#define id386 0 +#endif + +// !!! must be kept the same as in d_iface.h !!! +#define TRANSPARENT_COLOR 255 + diff --git a/QW/server/qwsv.dsp b/QW/server/qwsv.dsp new file mode 100644 index 0000000..a1228dd --- /dev/null +++ b/QW/server/qwsv.dsp @@ -0,0 +1,364 @@ +# Microsoft Developer Studio Project File - Name="qwsv" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=qwsv - Win32 Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "qwsv.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "qwsv.mak" CFG="qwsv - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "qwsv - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "qwsv - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "qwsv - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir ".\Release" +# PROP BASE Intermediate_Dir ".\Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir ".\Release" +# PROP Intermediate_Dir ".\Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c +# ADD CPP /nologo /GX /O2 /I "." /I "..\client" /D "NDEBUG" /D "SERVERONLY" /D "WIN32" /D "_CONSOLE" /FR /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 wsock32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib winmm.lib /nologo /subsystem:console /machine:I386 +# SUBTRACT LINK32 /profile + +!ELSEIF "$(CFG)" == "qwsv - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir ".\Debug" +# PROP BASE Intermediate_Dir ".\Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir ".\Debug" +# PROP Intermediate_Dir ".\Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /c +# ADD CPP /nologo /GX /ZI /Od /I "." /I "..\client" /D "_DEBUG" /D "SERVERONLY" /D "WIN32" /D "_CONSOLE" /FR /YX /FD /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 +# ADD LINK32 wsock32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib winmm.lib /nologo /subsystem:console /map /debug /machine:I386 +# SUBTRACT LINK32 /profile + +!ENDIF + +# Begin Target + +# Name "qwsv - Win32 Release" +# Name "qwsv - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90" +# Begin Source File + +SOURCE=..\client\cmd.c +# End Source File +# Begin Source File + +SOURCE=..\client\common.c +# End Source File +# Begin Source File + +SOURCE=..\client\crc.c +# End Source File +# Begin Source File + +SOURCE=..\client\cvar.c +# End Source File +# Begin Source File + +SOURCE=..\client\mathlib.c +# End Source File +# Begin Source File + +SOURCE=..\client\md4.c +# End Source File +# Begin Source File + +SOURCE=.\model.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\quake\v2\client\net.h +# End Source File +# Begin Source File + +SOURCE=..\client\net_chan.c +# End Source File +# Begin Source File + +SOURCE=..\client\net_wins.c +# End Source File +# Begin Source File + +SOURCE=..\client\pmove.c +# End Source File +# Begin Source File + +SOURCE=..\client\pmovetst.c +# End Source File +# Begin Source File + +SOURCE=.\pr_cmds.c +# End Source File +# Begin Source File + +SOURCE=.\pr_edict.c +# End Source File +# Begin Source File + +SOURCE=.\pr_exec.c +# End Source File +# Begin Source File + +SOURCE=.\sv_ccmds.c +# End Source File +# Begin Source File + +SOURCE=.\sv_ents.c +# End Source File +# Begin Source File + +SOURCE=.\sv_init.c +# End Source File +# Begin Source File + +SOURCE=.\sv_main.c +# End Source File +# Begin Source File + +SOURCE=.\sv_move.c +# End Source File +# Begin Source File + +SOURCE=.\sv_nchan.c +# End Source File +# Begin Source File + +SOURCE=.\sv_phys.c +# End Source File +# Begin Source File + +SOURCE=.\sv_send.c +# End Source File +# Begin Source File + +SOURCE=.\sv_user.c +# End Source File +# Begin Source File + +SOURCE=.\sys_win.c +# End Source File +# Begin Source File + +SOURCE=.\world.c +# End Source File +# Begin Source File + +SOURCE=..\client\zone.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl;fi;fd" +# Begin Source File + +SOURCE=..\client\bothdefs.h +# End Source File +# Begin Source File + +SOURCE=..\client\bspfile.h +# End Source File +# Begin Source File + +SOURCE=..\client\cdaudio.h +# End Source File +# Begin Source File + +SOURCE=..\client\client.h +# End Source File +# Begin Source File + +SOURCE=..\client\cmd.h +# End Source File +# Begin Source File + +SOURCE=..\client\common.h +# End Source File +# Begin Source File + +SOURCE=..\client\console.h +# End Source File +# Begin Source File + +SOURCE=..\client\crc.h +# End Source File +# Begin Source File + +SOURCE=..\client\cvar.h +# End Source File +# Begin Source File + +SOURCE=..\client\d_iface.h +# End Source File +# Begin Source File + +SOURCE=..\client\draw.h +# End Source File +# Begin Source File + +SOURCE=..\client\input.h +# End Source File +# Begin Source File + +SOURCE=..\client\keys.h +# End Source File +# Begin Source File + +SOURCE=..\client\mathlib.h +# End Source File +# Begin Source File + +SOURCE=..\client\menu.h +# End Source File +# Begin Source File + +SOURCE=..\client\model.h +# End Source File +# Begin Source File + +SOURCE=..\client\modelgen.h +# End Source File +# Begin Source File + +SOURCE=..\client\net.h +# End Source File +# Begin Source File + +SOURCE=..\client\pmove.h +# End Source File +# Begin Source File + +SOURCE=.\pr_comp.h +# End Source File +# Begin Source File + +SOURCE=.\progdefs.h +# End Source File +# Begin Source File + +SOURCE=.\progs.h +# End Source File +# Begin Source File + +SOURCE=..\client\protocol.h +# End Source File +# Begin Source File + +SOURCE=..\client\quakedef.h +# End Source File +# Begin Source File + +SOURCE=.\qwsvdef.h +# End Source File +# Begin Source File + +SOURCE=..\client\render.h +# End Source File +# Begin Source File + +SOURCE=..\client\sbar.h +# End Source File +# Begin Source File + +SOURCE=..\client\screen.h +# End Source File +# Begin Source File + +SOURCE=.\server.h +# End Source File +# Begin Source File + +SOURCE=..\client\sound.h +# End Source File +# Begin Source File + +SOURCE=..\client\spritegn.h +# End Source File +# Begin Source File + +SOURCE=..\client\sys.h +# End Source File +# Begin Source File + +SOURCE=.\sys.h +# End Source File +# Begin Source File + +SOURCE=..\client\vid.h +# End Source File +# Begin Source File + +SOURCE=..\client\view.h +# End Source File +# Begin Source File + +SOURCE=..\client\wad.h +# End Source File +# Begin Source File + +SOURCE=..\client\winquake.h +# End Source File +# Begin Source File + +SOURCE=.\world.h +# End Source File +# Begin Source File + +SOURCE=..\client\zone.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/QW/server/qwsv.dsw b/QW/server/qwsv.dsw new file mode 100644 index 0000000..8c2921e --- /dev/null +++ b/QW/server/qwsv.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 5.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "qwsv"=.\qwsv.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/QW/server/qwsv.mak b/QW/server/qwsv.mak new file mode 100644 index 0000000..489bf1d --- /dev/null +++ b/QW/server/qwsv.mak @@ -0,0 +1,2207 @@ +# Microsoft Developer Studio Generated NMAKE File, Format Version 4.20 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +!IF "$(CFG)" == "" +CFG=qwsv - Win32 Debug +!MESSAGE No configuration specified. Defaulting to qwsv - Win32 Debug. +!ENDIF + +!IF "$(CFG)" != "qwsv - Win32 Release" && "$(CFG)" != "qwsv - Win32 Debug" +!MESSAGE Invalid configuration "$(CFG)" specified. +!MESSAGE You can specify a configuration when running NMAKE on this makefile +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "qwsv.mak" CFG="qwsv - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "qwsv - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "qwsv - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE +!ERROR An invalid configuration is specified. +!ENDIF + +!IF "$(OS)" == "Windows_NT" +NULL= +!ELSE +NULL=nul +!ENDIF +################################################################################ +# Begin Project +# PROP Target_Last_Scanned "qwsv - Win32 Debug" +RSC=rc.exe +CPP=cl.exe + +!IF "$(CFG)" == "qwsv - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +OUTDIR=.\Release +INTDIR=.\Release + +ALL : "$(OUTDIR)\qwsv.exe" + +CLEAN : + -@erase "$(INTDIR)\cmd.obj" + -@erase "$(INTDIR)\common.obj" + -@erase "$(INTDIR)\crc.obj" + -@erase "$(INTDIR)\cvar.obj" + -@erase "$(INTDIR)\mathlib.obj" + -@erase "$(INTDIR)\model.obj" + -@erase "$(INTDIR)\net_chan.obj" + -@erase "$(INTDIR)\net_wins.obj" + -@erase "$(INTDIR)\pmove.obj" + -@erase "$(INTDIR)\pmovetst.obj" + -@erase "$(INTDIR)\pr_cmds.obj" + -@erase "$(INTDIR)\pr_edict.obj" + -@erase "$(INTDIR)\pr_exec.obj" + -@erase "$(INTDIR)\sv_ccmds.obj" + -@erase "$(INTDIR)\sv_ents.obj" + -@erase "$(INTDIR)\sv_init.obj" + -@erase "$(INTDIR)\sv_main.obj" + -@erase "$(INTDIR)\sv_move.obj" + -@erase "$(INTDIR)\sv_phys.obj" + -@erase "$(INTDIR)\sv_send.obj" + -@erase "$(INTDIR)\sv_user.obj" + -@erase "$(INTDIR)\sys_win.obj" + -@erase "$(INTDIR)\world.obj" + -@erase "$(INTDIR)\zone.obj" + -@erase "$(OUTDIR)\qwsv.exe" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c +# ADD CPP /nologo /GX /O2 /I "." /I "../client" /D "NDEBUG" /D "SERVERONLY" /D "WIN32" /D "_CONSOLE" /YX /c +# SUBTRACT CPP /Fr +CPP_PROJ=/nologo /ML /GX /O2 /I "." /I "../client" /D "NDEBUG" /D "SERVERONLY"\ + /D "WIN32" /D "_CONSOLE" /Fp"$(INTDIR)/qwsv.pch" /YX /Fo"$(INTDIR)/" /c +CPP_OBJS=.\Release/ +CPP_SBRS=.\. +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +BSC32_FLAGS=/nologo /o"$(OUTDIR)/qwsv.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 wsock32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /profile /machine:I386 +LINK32_FLAGS=wsock32.lib kernel32.lib user32.lib gdi32.lib winspool.lib\ + comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib\ + odbc32.lib odbccp32.lib /nologo /subsystem:console /profile /machine:I386\ + /out:"$(OUTDIR)/qwsv.exe" +LINK32_OBJS= \ + "$(INTDIR)\cmd.obj" \ + "$(INTDIR)\common.obj" \ + "$(INTDIR)\crc.obj" \ + "$(INTDIR)\cvar.obj" \ + "$(INTDIR)\mathlib.obj" \ + "$(INTDIR)\model.obj" \ + "$(INTDIR)\net_chan.obj" \ + "$(INTDIR)\net_wins.obj" \ + "$(INTDIR)\pmove.obj" \ + "$(INTDIR)\pmovetst.obj" \ + "$(INTDIR)\pr_cmds.obj" \ + "$(INTDIR)\pr_edict.obj" \ + "$(INTDIR)\pr_exec.obj" \ + "$(INTDIR)\sv_ccmds.obj" \ + "$(INTDIR)\sv_ents.obj" \ + "$(INTDIR)\sv_init.obj" \ + "$(INTDIR)\sv_main.obj" \ + "$(INTDIR)\sv_move.obj" \ + "$(INTDIR)\sv_phys.obj" \ + "$(INTDIR)\sv_send.obj" \ + "$(INTDIR)\sv_user.obj" \ + "$(INTDIR)\sys_win.obj" \ + "$(INTDIR)\world.obj" \ + "$(INTDIR)\zone.obj" + +"$(OUTDIR)\qwsv.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +!ELSEIF "$(CFG)" == "qwsv - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +OUTDIR=.\Debug +INTDIR=.\Debug + +ALL : "$(OUTDIR)\qwsv.exe" "$(OUTDIR)\qwsv.bsc" + +CLEAN : + -@erase "$(INTDIR)\cmd.obj" + -@erase "$(INTDIR)\cmd.sbr" + -@erase "$(INTDIR)\common.obj" + -@erase "$(INTDIR)\common.sbr" + -@erase "$(INTDIR)\crc.obj" + -@erase "$(INTDIR)\crc.sbr" + -@erase "$(INTDIR)\cvar.obj" + -@erase "$(INTDIR)\cvar.sbr" + -@erase "$(INTDIR)\mathlib.obj" + -@erase "$(INTDIR)\mathlib.sbr" + -@erase "$(INTDIR)\model.obj" + -@erase "$(INTDIR)\model.sbr" + -@erase "$(INTDIR)\net_chan.obj" + -@erase "$(INTDIR)\net_chan.sbr" + -@erase "$(INTDIR)\net_wins.obj" + -@erase "$(INTDIR)\net_wins.sbr" + -@erase "$(INTDIR)\pmove.obj" + -@erase "$(INTDIR)\pmove.sbr" + -@erase "$(INTDIR)\pmovetst.obj" + -@erase "$(INTDIR)\pmovetst.sbr" + -@erase "$(INTDIR)\pr_cmds.obj" + -@erase "$(INTDIR)\pr_cmds.sbr" + -@erase "$(INTDIR)\pr_edict.obj" + -@erase "$(INTDIR)\pr_edict.sbr" + -@erase "$(INTDIR)\pr_exec.obj" + -@erase "$(INTDIR)\pr_exec.sbr" + -@erase "$(INTDIR)\sv_ccmds.obj" + -@erase "$(INTDIR)\sv_ccmds.sbr" + -@erase "$(INTDIR)\sv_ents.obj" + -@erase "$(INTDIR)\sv_ents.sbr" + -@erase "$(INTDIR)\sv_init.obj" + -@erase "$(INTDIR)\sv_init.sbr" + -@erase "$(INTDIR)\sv_main.obj" + -@erase "$(INTDIR)\sv_main.sbr" + -@erase "$(INTDIR)\sv_move.obj" + -@erase "$(INTDIR)\sv_move.sbr" + -@erase "$(INTDIR)\sv_phys.obj" + -@erase "$(INTDIR)\sv_phys.sbr" + -@erase "$(INTDIR)\sv_send.obj" + -@erase "$(INTDIR)\sv_send.sbr" + -@erase "$(INTDIR)\sv_user.obj" + -@erase "$(INTDIR)\sv_user.sbr" + -@erase "$(INTDIR)\sys_win.obj" + -@erase "$(INTDIR)\sys_win.sbr" + -@erase "$(INTDIR)\vc40.idb" + -@erase "$(INTDIR)\vc40.pdb" + -@erase "$(INTDIR)\world.obj" + -@erase "$(INTDIR)\world.sbr" + -@erase "$(INTDIR)\zone.obj" + -@erase "$(INTDIR)\zone.sbr" + -@erase "$(OUTDIR)\qwsv.bsc" + -@erase "$(OUTDIR)\qwsv.exe" + -@erase "$(OUTDIR)\qwsv.map" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /c +# ADD CPP /nologo /Gm /GX /Zi /Od /I "." /I "../client" /D "_DEBUG" /D "SERVERONLY" /D "WIN32" /D "_CONSOLE" /FR /YX /c +CPP_PROJ=/nologo /MLd /Gm /GX /Zi /Od /I "." /I "../client" /D "_DEBUG" /D\ + "SERVERONLY" /D "WIN32" /D "_CONSOLE" /FR"$(INTDIR)/" /Fp"$(INTDIR)/qwsv.pch"\ + /YX /Fo"$(INTDIR)/" /Fd"$(INTDIR)/" /c +CPP_OBJS=.\Debug/ +CPP_SBRS=.\Debug/ +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +BSC32_FLAGS=/nologo /o"$(OUTDIR)/qwsv.bsc" +BSC32_SBRS= \ + "$(INTDIR)\cmd.sbr" \ + "$(INTDIR)\common.sbr" \ + "$(INTDIR)\crc.sbr" \ + "$(INTDIR)\cvar.sbr" \ + "$(INTDIR)\mathlib.sbr" \ + "$(INTDIR)\model.sbr" \ + "$(INTDIR)\net_chan.sbr" \ + "$(INTDIR)\net_wins.sbr" \ + "$(INTDIR)\pmove.sbr" \ + "$(INTDIR)\pmovetst.sbr" \ + "$(INTDIR)\pr_cmds.sbr" \ + "$(INTDIR)\pr_edict.sbr" \ + "$(INTDIR)\pr_exec.sbr" \ + "$(INTDIR)\sv_ccmds.sbr" \ + "$(INTDIR)\sv_ents.sbr" \ + "$(INTDIR)\sv_init.sbr" \ + "$(INTDIR)\sv_main.sbr" \ + "$(INTDIR)\sv_move.sbr" \ + "$(INTDIR)\sv_phys.sbr" \ + "$(INTDIR)\sv_send.sbr" \ + "$(INTDIR)\sv_user.sbr" \ + "$(INTDIR)\sys_win.sbr" \ + "$(INTDIR)\world.sbr" \ + "$(INTDIR)\zone.sbr" + +"$(OUTDIR)\qwsv.bsc" : "$(OUTDIR)" $(BSC32_SBRS) + $(BSC32) @<< + $(BSC32_FLAGS) $(BSC32_SBRS) +<< + +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 +# ADD LINK32 wsock32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /profile /map /debug /machine:I386 +LINK32_FLAGS=wsock32.lib kernel32.lib user32.lib gdi32.lib winspool.lib\ + comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib\ + odbc32.lib odbccp32.lib /nologo /subsystem:console /profile\ + /map:"$(INTDIR)/qwsv.map" /debug /machine:I386 /out:"$(OUTDIR)/qwsv.exe" +LINK32_OBJS= \ + "$(INTDIR)\cmd.obj" \ + "$(INTDIR)\common.obj" \ + "$(INTDIR)\crc.obj" \ + "$(INTDIR)\cvar.obj" \ + "$(INTDIR)\mathlib.obj" \ + "$(INTDIR)\model.obj" \ + "$(INTDIR)\net_chan.obj" \ + "$(INTDIR)\net_wins.obj" \ + "$(INTDIR)\pmove.obj" \ + "$(INTDIR)\pmovetst.obj" \ + "$(INTDIR)\pr_cmds.obj" \ + "$(INTDIR)\pr_edict.obj" \ + "$(INTDIR)\pr_exec.obj" \ + "$(INTDIR)\sv_ccmds.obj" \ + "$(INTDIR)\sv_ents.obj" \ + "$(INTDIR)\sv_init.obj" \ + "$(INTDIR)\sv_main.obj" \ + "$(INTDIR)\sv_move.obj" \ + "$(INTDIR)\sv_phys.obj" \ + "$(INTDIR)\sv_send.obj" \ + "$(INTDIR)\sv_user.obj" \ + "$(INTDIR)\sys_win.obj" \ + "$(INTDIR)\world.obj" \ + "$(INTDIR)\zone.obj" + +"$(OUTDIR)\qwsv.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +!ENDIF + +.c{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.cpp{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.cxx{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.c{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +.cpp{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +.cxx{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +################################################################################ +# Begin Target + +# Name "qwsv - Win32 Release" +# Name "qwsv - Win32 Debug" + +!IF "$(CFG)" == "qwsv - Win32 Release" + +!ELSEIF "$(CFG)" == "qwsv - Win32 Debug" + +!ENDIF + +################################################################################ +# Begin Source File + +SOURCE=.\model.c + +!IF "$(CFG)" == "qwsv - Win32 Release" + +DEP_CPP_MODEL=\ + "..\client\spritegn.h"\ + ".\../client\bothdefs.h"\ + ".\../client\bspfile.h"\ + ".\../client\cmd.h"\ + ".\../client\common.h"\ + ".\../client\crc.h"\ + ".\../client\cvar.h"\ + ".\../client\mathlib.h"\ + ".\../client\model.h"\ + ".\../client\net.h"\ + ".\../client\pmove.h"\ + ".\../client\protocol.h"\ + ".\../client\zone.h"\ + ".\..\client\modelgen.h"\ + ".\pr_comp.h"\ + ".\progdefs.h"\ + ".\progs.h"\ + ".\qwsvdef.h"\ + ".\server.h"\ + ".\sys.h"\ + ".\world.h"\ + +NODEP_CPP_MODEL=\ + "..\client\cmdlib.h"\ + "..\client\dictlib.h"\ + "..\client\lbmlib.h"\ + "..\client\scriplib.h"\ + "..\client\trilib.h"\ + + +"$(INTDIR)\model.obj" : $(SOURCE) $(DEP_CPP_MODEL) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "qwsv - Win32 Debug" + +DEP_CPP_MODEL=\ + "..\client\spritegn.h"\ + ".\../client\bothdefs.h"\ + ".\../client\bspfile.h"\ + ".\../client\cmd.h"\ + ".\../client\common.h"\ + ".\../client\crc.h"\ + ".\../client\cvar.h"\ + ".\../client\mathlib.h"\ + ".\../client\model.h"\ + ".\../client\net.h"\ + ".\../client\pmove.h"\ + ".\../client\protocol.h"\ + ".\../client\zone.h"\ + ".\..\client\modelgen.h"\ + ".\pr_comp.h"\ + ".\progdefs.h"\ + ".\progs.h"\ + ".\qwsvdef.h"\ + ".\server.h"\ + ".\sys.h"\ + ".\world.h"\ + +NODEP_CPP_MODEL=\ + "..\client\cmdlib.h"\ + "..\client\dictlib.h"\ + "..\client\lbmlib.h"\ + "..\client\scriplib.h"\ + "..\client\trilib.h"\ + + +"$(INTDIR)\model.obj" : $(SOURCE) $(DEP_CPP_MODEL) "$(INTDIR)" + +"$(INTDIR)\model.sbr" : $(SOURCE) $(DEP_CPP_MODEL) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\pr_cmds.c + +!IF "$(CFG)" == "qwsv - Win32 Release" + +DEP_CPP_PR_CM=\ + "..\client\spritegn.h"\ + ".\../client\bothdefs.h"\ + ".\../client\bspfile.h"\ + ".\../client\cmd.h"\ + ".\../client\common.h"\ + ".\../client\crc.h"\ + ".\../client\cvar.h"\ + ".\../client\mathlib.h"\ + ".\../client\model.h"\ + ".\../client\net.h"\ + ".\../client\pmove.h"\ + ".\../client\protocol.h"\ + ".\../client\zone.h"\ + ".\..\client\modelgen.h"\ + ".\pr_comp.h"\ + ".\progdefs.h"\ + ".\progs.h"\ + ".\qwsvdef.h"\ + ".\server.h"\ + ".\sys.h"\ + ".\world.h"\ + +NODEP_CPP_PR_CM=\ + "..\client\cmdlib.h"\ + "..\client\dictlib.h"\ + "..\client\lbmlib.h"\ + "..\client\scriplib.h"\ + "..\client\trilib.h"\ + + +"$(INTDIR)\pr_cmds.obj" : $(SOURCE) $(DEP_CPP_PR_CM) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "qwsv - Win32 Debug" + +DEP_CPP_PR_CM=\ + "..\client\spritegn.h"\ + ".\../client\bothdefs.h"\ + ".\../client\bspfile.h"\ + ".\../client\cmd.h"\ + ".\../client\common.h"\ + ".\../client\crc.h"\ + ".\../client\cvar.h"\ + ".\../client\mathlib.h"\ + ".\../client\model.h"\ + ".\../client\net.h"\ + ".\../client\pmove.h"\ + ".\../client\protocol.h"\ + ".\../client\zone.h"\ + ".\..\client\modelgen.h"\ + ".\pr_comp.h"\ + ".\progdefs.h"\ + ".\progs.h"\ + ".\qwsvdef.h"\ + ".\server.h"\ + ".\sys.h"\ + ".\world.h"\ + +NODEP_CPP_PR_CM=\ + "..\client\cmdlib.h"\ + "..\client\dictlib.h"\ + "..\client\lbmlib.h"\ + "..\client\scriplib.h"\ + "..\client\trilib.h"\ + + +"$(INTDIR)\pr_cmds.obj" : $(SOURCE) $(DEP_CPP_PR_CM) "$(INTDIR)" + +"$(INTDIR)\pr_cmds.sbr" : $(SOURCE) $(DEP_CPP_PR_CM) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\pr_edict.c + +!IF "$(CFG)" == "qwsv - Win32 Release" + +DEP_CPP_PR_ED=\ + "..\client\spritegn.h"\ + ".\../client\bothdefs.h"\ + ".\../client\bspfile.h"\ + ".\../client\cmd.h"\ + ".\../client\common.h"\ + ".\../client\crc.h"\ + ".\../client\cvar.h"\ + ".\../client\mathlib.h"\ + ".\../client\model.h"\ + ".\../client\net.h"\ + ".\../client\pmove.h"\ + ".\../client\protocol.h"\ + ".\../client\zone.h"\ + ".\..\client\modelgen.h"\ + ".\pr_comp.h"\ + ".\progdefs.h"\ + ".\progs.h"\ + ".\qwsvdef.h"\ + ".\server.h"\ + ".\sys.h"\ + ".\world.h"\ + +NODEP_CPP_PR_ED=\ + "..\client\cmdlib.h"\ + "..\client\dictlib.h"\ + "..\client\lbmlib.h"\ + "..\client\scriplib.h"\ + "..\client\trilib.h"\ + + +"$(INTDIR)\pr_edict.obj" : $(SOURCE) $(DEP_CPP_PR_ED) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "qwsv - Win32 Debug" + +DEP_CPP_PR_ED=\ + "..\client\spritegn.h"\ + ".\../client\bothdefs.h"\ + ".\../client\bspfile.h"\ + ".\../client\cmd.h"\ + ".\../client\common.h"\ + ".\../client\crc.h"\ + ".\../client\cvar.h"\ + ".\../client\mathlib.h"\ + ".\../client\model.h"\ + ".\../client\net.h"\ + ".\../client\pmove.h"\ + ".\../client\protocol.h"\ + ".\../client\zone.h"\ + ".\..\client\modelgen.h"\ + ".\pr_comp.h"\ + ".\progdefs.h"\ + ".\progs.h"\ + ".\qwsvdef.h"\ + ".\server.h"\ + ".\sys.h"\ + ".\world.h"\ + +NODEP_CPP_PR_ED=\ + "..\client\cmdlib.h"\ + "..\client\dictlib.h"\ + "..\client\lbmlib.h"\ + "..\client\scriplib.h"\ + "..\client\trilib.h"\ + + +"$(INTDIR)\pr_edict.obj" : $(SOURCE) $(DEP_CPP_PR_ED) "$(INTDIR)" + +"$(INTDIR)\pr_edict.sbr" : $(SOURCE) $(DEP_CPP_PR_ED) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\pr_exec.c + +!IF "$(CFG)" == "qwsv - Win32 Release" + +DEP_CPP_PR_EX=\ + "..\client\spritegn.h"\ + ".\../client\bothdefs.h"\ + ".\../client\bspfile.h"\ + ".\../client\cmd.h"\ + ".\../client\common.h"\ + ".\../client\crc.h"\ + ".\../client\cvar.h"\ + ".\../client\mathlib.h"\ + ".\../client\model.h"\ + ".\../client\net.h"\ + ".\../client\pmove.h"\ + ".\../client\protocol.h"\ + ".\../client\zone.h"\ + ".\..\client\modelgen.h"\ + ".\pr_comp.h"\ + ".\progdefs.h"\ + ".\progs.h"\ + ".\qwsvdef.h"\ + ".\server.h"\ + ".\sys.h"\ + ".\world.h"\ + +NODEP_CPP_PR_EX=\ + "..\client\cmdlib.h"\ + "..\client\dictlib.h"\ + "..\client\lbmlib.h"\ + "..\client\scriplib.h"\ + "..\client\trilib.h"\ + + +"$(INTDIR)\pr_exec.obj" : $(SOURCE) $(DEP_CPP_PR_EX) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "qwsv - Win32 Debug" + +DEP_CPP_PR_EX=\ + "..\client\spritegn.h"\ + ".\../client\bothdefs.h"\ + ".\../client\bspfile.h"\ + ".\../client\cmd.h"\ + ".\../client\common.h"\ + ".\../client\crc.h"\ + ".\../client\cvar.h"\ + ".\../client\mathlib.h"\ + ".\../client\model.h"\ + ".\../client\net.h"\ + ".\../client\pmove.h"\ + ".\../client\protocol.h"\ + ".\../client\zone.h"\ + ".\..\client\modelgen.h"\ + ".\pr_comp.h"\ + ".\progdefs.h"\ + ".\progs.h"\ + ".\qwsvdef.h"\ + ".\server.h"\ + ".\sys.h"\ + ".\world.h"\ + +NODEP_CPP_PR_EX=\ + "..\client\cmdlib.h"\ + "..\client\dictlib.h"\ + "..\client\lbmlib.h"\ + "..\client\scriplib.h"\ + "..\client\trilib.h"\ + + +"$(INTDIR)\pr_exec.obj" : $(SOURCE) $(DEP_CPP_PR_EX) "$(INTDIR)" + +"$(INTDIR)\pr_exec.sbr" : $(SOURCE) $(DEP_CPP_PR_EX) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\sv_ccmds.c + +!IF "$(CFG)" == "qwsv - Win32 Release" + +DEP_CPP_SV_CC=\ + "..\client\spritegn.h"\ + ".\../client\bothdefs.h"\ + ".\../client\bspfile.h"\ + ".\../client\cmd.h"\ + ".\../client\common.h"\ + ".\../client\crc.h"\ + ".\../client\cvar.h"\ + ".\../client\mathlib.h"\ + ".\../client\model.h"\ + ".\../client\net.h"\ + ".\../client\pmove.h"\ + ".\../client\protocol.h"\ + ".\../client\zone.h"\ + ".\..\client\modelgen.h"\ + ".\pr_comp.h"\ + ".\progdefs.h"\ + ".\progs.h"\ + ".\qwsvdef.h"\ + ".\server.h"\ + ".\sys.h"\ + ".\world.h"\ + +NODEP_CPP_SV_CC=\ + "..\client\cmdlib.h"\ + "..\client\dictlib.h"\ + "..\client\lbmlib.h"\ + "..\client\scriplib.h"\ + "..\client\trilib.h"\ + + +"$(INTDIR)\sv_ccmds.obj" : $(SOURCE) $(DEP_CPP_SV_CC) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "qwsv - Win32 Debug" + +DEP_CPP_SV_CC=\ + "..\client\spritegn.h"\ + ".\../client\bothdefs.h"\ + ".\../client\bspfile.h"\ + ".\../client\cmd.h"\ + ".\../client\common.h"\ + ".\../client\crc.h"\ + ".\../client\cvar.h"\ + ".\../client\mathlib.h"\ + ".\../client\model.h"\ + ".\../client\net.h"\ + ".\../client\pmove.h"\ + ".\../client\protocol.h"\ + ".\../client\zone.h"\ + ".\..\client\modelgen.h"\ + ".\pr_comp.h"\ + ".\progdefs.h"\ + ".\progs.h"\ + ".\qwsvdef.h"\ + ".\server.h"\ + ".\sys.h"\ + ".\world.h"\ + +NODEP_CPP_SV_CC=\ + "..\client\cmdlib.h"\ + "..\client\dictlib.h"\ + "..\client\lbmlib.h"\ + "..\client\scriplib.h"\ + "..\client\trilib.h"\ + + +"$(INTDIR)\sv_ccmds.obj" : $(SOURCE) $(DEP_CPP_SV_CC) "$(INTDIR)" + +"$(INTDIR)\sv_ccmds.sbr" : $(SOURCE) $(DEP_CPP_SV_CC) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\sv_init.c + +!IF "$(CFG)" == "qwsv - Win32 Release" + +DEP_CPP_SV_IN=\ + "..\client\spritegn.h"\ + ".\../client\bothdefs.h"\ + ".\../client\bspfile.h"\ + ".\../client\cmd.h"\ + ".\../client\common.h"\ + ".\../client\crc.h"\ + ".\../client\cvar.h"\ + ".\../client\mathlib.h"\ + ".\../client\model.h"\ + ".\../client\net.h"\ + ".\../client\pmove.h"\ + ".\../client\protocol.h"\ + ".\../client\zone.h"\ + ".\..\client\modelgen.h"\ + ".\pr_comp.h"\ + ".\progdefs.h"\ + ".\progs.h"\ + ".\qwsvdef.h"\ + ".\server.h"\ + ".\sys.h"\ + ".\world.h"\ + +NODEP_CPP_SV_IN=\ + "..\client\cmdlib.h"\ + "..\client\dictlib.h"\ + "..\client\lbmlib.h"\ + "..\client\scriplib.h"\ + "..\client\trilib.h"\ + + +"$(INTDIR)\sv_init.obj" : $(SOURCE) $(DEP_CPP_SV_IN) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "qwsv - Win32 Debug" + +DEP_CPP_SV_IN=\ + "..\client\spritegn.h"\ + ".\../client\bothdefs.h"\ + ".\../client\bspfile.h"\ + ".\../client\cmd.h"\ + ".\../client\common.h"\ + ".\../client\crc.h"\ + ".\../client\cvar.h"\ + ".\../client\mathlib.h"\ + ".\../client\model.h"\ + ".\../client\net.h"\ + ".\../client\pmove.h"\ + ".\../client\protocol.h"\ + ".\../client\zone.h"\ + ".\..\client\modelgen.h"\ + ".\pr_comp.h"\ + ".\progdefs.h"\ + ".\progs.h"\ + ".\qwsvdef.h"\ + ".\server.h"\ + ".\sys.h"\ + ".\world.h"\ + +NODEP_CPP_SV_IN=\ + "..\client\cmdlib.h"\ + "..\client\dictlib.h"\ + "..\client\lbmlib.h"\ + "..\client\scriplib.h"\ + "..\client\trilib.h"\ + + +"$(INTDIR)\sv_init.obj" : $(SOURCE) $(DEP_CPP_SV_IN) "$(INTDIR)" + +"$(INTDIR)\sv_init.sbr" : $(SOURCE) $(DEP_CPP_SV_IN) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\sv_main.c + +!IF "$(CFG)" == "qwsv - Win32 Release" + +DEP_CPP_SV_MA=\ + "..\client\spritegn.h"\ + ".\../client\bothdefs.h"\ + ".\../client\bspfile.h"\ + ".\../client\cmd.h"\ + ".\../client\common.h"\ + ".\../client\crc.h"\ + ".\../client\cvar.h"\ + ".\../client\mathlib.h"\ + ".\../client\model.h"\ + ".\../client\net.h"\ + ".\../client\pmove.h"\ + ".\../client\protocol.h"\ + ".\../client\zone.h"\ + ".\..\client\modelgen.h"\ + ".\pr_comp.h"\ + ".\progdefs.h"\ + ".\progs.h"\ + ".\qwsvdef.h"\ + ".\server.h"\ + ".\sys.h"\ + ".\world.h"\ + +NODEP_CPP_SV_MA=\ + "..\client\cmdlib.h"\ + "..\client\dictlib.h"\ + "..\client\lbmlib.h"\ + "..\client\scriplib.h"\ + "..\client\trilib.h"\ + + +"$(INTDIR)\sv_main.obj" : $(SOURCE) $(DEP_CPP_SV_MA) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "qwsv - Win32 Debug" + +DEP_CPP_SV_MA=\ + "..\client\spritegn.h"\ + ".\../client\bothdefs.h"\ + ".\../client\bspfile.h"\ + ".\../client\cmd.h"\ + ".\../client\common.h"\ + ".\../client\crc.h"\ + ".\../client\cvar.h"\ + ".\../client\mathlib.h"\ + ".\../client\model.h"\ + ".\../client\net.h"\ + ".\../client\pmove.h"\ + ".\../client\protocol.h"\ + ".\../client\zone.h"\ + ".\..\client\modelgen.h"\ + ".\pr_comp.h"\ + ".\progdefs.h"\ + ".\progs.h"\ + ".\qwsvdef.h"\ + ".\server.h"\ + ".\sys.h"\ + ".\world.h"\ + +NODEP_CPP_SV_MA=\ + "..\client\cmdlib.h"\ + "..\client\dictlib.h"\ + "..\client\lbmlib.h"\ + "..\client\scriplib.h"\ + "..\client\trilib.h"\ + + +"$(INTDIR)\sv_main.obj" : $(SOURCE) $(DEP_CPP_SV_MA) "$(INTDIR)" + +"$(INTDIR)\sv_main.sbr" : $(SOURCE) $(DEP_CPP_SV_MA) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\sv_move.c + +!IF "$(CFG)" == "qwsv - Win32 Release" + +DEP_CPP_SV_MO=\ + "..\client\spritegn.h"\ + ".\../client\bothdefs.h"\ + ".\../client\bspfile.h"\ + ".\../client\cmd.h"\ + ".\../client\common.h"\ + ".\../client\crc.h"\ + ".\../client\cvar.h"\ + ".\../client\mathlib.h"\ + ".\../client\model.h"\ + ".\../client\net.h"\ + ".\../client\pmove.h"\ + ".\../client\protocol.h"\ + ".\../client\zone.h"\ + ".\..\client\modelgen.h"\ + ".\pr_comp.h"\ + ".\progdefs.h"\ + ".\progs.h"\ + ".\qwsvdef.h"\ + ".\server.h"\ + ".\sys.h"\ + ".\world.h"\ + +NODEP_CPP_SV_MO=\ + "..\client\cmdlib.h"\ + "..\client\dictlib.h"\ + "..\client\lbmlib.h"\ + "..\client\scriplib.h"\ + "..\client\trilib.h"\ + + +"$(INTDIR)\sv_move.obj" : $(SOURCE) $(DEP_CPP_SV_MO) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "qwsv - Win32 Debug" + +DEP_CPP_SV_MO=\ + "..\client\spritegn.h"\ + ".\../client\bothdefs.h"\ + ".\../client\bspfile.h"\ + ".\../client\cmd.h"\ + ".\../client\common.h"\ + ".\../client\crc.h"\ + ".\../client\cvar.h"\ + ".\../client\mathlib.h"\ + ".\../client\model.h"\ + ".\../client\net.h"\ + ".\../client\pmove.h"\ + ".\../client\protocol.h"\ + ".\../client\zone.h"\ + ".\..\client\modelgen.h"\ + ".\pr_comp.h"\ + ".\progdefs.h"\ + ".\progs.h"\ + ".\qwsvdef.h"\ + ".\server.h"\ + ".\sys.h"\ + ".\world.h"\ + +NODEP_CPP_SV_MO=\ + "..\client\cmdlib.h"\ + "..\client\dictlib.h"\ + "..\client\lbmlib.h"\ + "..\client\scriplib.h"\ + "..\client\trilib.h"\ + + +"$(INTDIR)\sv_move.obj" : $(SOURCE) $(DEP_CPP_SV_MO) "$(INTDIR)" + +"$(INTDIR)\sv_move.sbr" : $(SOURCE) $(DEP_CPP_SV_MO) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\sv_phys.c + +!IF "$(CFG)" == "qwsv - Win32 Release" + +DEP_CPP_SV_PH=\ + "..\client\spritegn.h"\ + ".\../client\bothdefs.h"\ + ".\../client\bspfile.h"\ + ".\../client\cmd.h"\ + ".\../client\common.h"\ + ".\../client\crc.h"\ + ".\../client\cvar.h"\ + ".\../client\mathlib.h"\ + ".\../client\model.h"\ + ".\../client\net.h"\ + ".\../client\pmove.h"\ + ".\../client\protocol.h"\ + ".\../client\zone.h"\ + ".\..\client\modelgen.h"\ + ".\pr_comp.h"\ + ".\progdefs.h"\ + ".\progs.h"\ + ".\qwsvdef.h"\ + ".\server.h"\ + ".\sys.h"\ + ".\world.h"\ + +NODEP_CPP_SV_PH=\ + "..\client\cmdlib.h"\ + "..\client\dictlib.h"\ + "..\client\lbmlib.h"\ + "..\client\scriplib.h"\ + "..\client\trilib.h"\ + + +"$(INTDIR)\sv_phys.obj" : $(SOURCE) $(DEP_CPP_SV_PH) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "qwsv - Win32 Debug" + +DEP_CPP_SV_PH=\ + "..\client\spritegn.h"\ + ".\../client\bothdefs.h"\ + ".\../client\bspfile.h"\ + ".\../client\cmd.h"\ + ".\../client\common.h"\ + ".\../client\crc.h"\ + ".\../client\cvar.h"\ + ".\../client\mathlib.h"\ + ".\../client\model.h"\ + ".\../client\net.h"\ + ".\../client\pmove.h"\ + ".\../client\protocol.h"\ + ".\../client\zone.h"\ + ".\..\client\modelgen.h"\ + ".\pr_comp.h"\ + ".\progdefs.h"\ + ".\progs.h"\ + ".\qwsvdef.h"\ + ".\server.h"\ + ".\sys.h"\ + ".\world.h"\ + +NODEP_CPP_SV_PH=\ + "..\client\cmdlib.h"\ + "..\client\dictlib.h"\ + "..\client\lbmlib.h"\ + "..\client\scriplib.h"\ + "..\client\trilib.h"\ + + +"$(INTDIR)\sv_phys.obj" : $(SOURCE) $(DEP_CPP_SV_PH) "$(INTDIR)" + +"$(INTDIR)\sv_phys.sbr" : $(SOURCE) $(DEP_CPP_SV_PH) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\sv_send.c + +!IF "$(CFG)" == "qwsv - Win32 Release" + +DEP_CPP_SV_SE=\ + "..\client\spritegn.h"\ + ".\../client\bothdefs.h"\ + ".\../client\bspfile.h"\ + ".\../client\cmd.h"\ + ".\../client\common.h"\ + ".\../client\crc.h"\ + ".\../client\cvar.h"\ + ".\../client\mathlib.h"\ + ".\../client\model.h"\ + ".\../client\net.h"\ + ".\../client\pmove.h"\ + ".\../client\protocol.h"\ + ".\../client\zone.h"\ + ".\..\client\modelgen.h"\ + ".\pr_comp.h"\ + ".\progdefs.h"\ + ".\progs.h"\ + ".\qwsvdef.h"\ + ".\server.h"\ + ".\sys.h"\ + ".\world.h"\ + +NODEP_CPP_SV_SE=\ + "..\client\cmdlib.h"\ + "..\client\dictlib.h"\ + "..\client\lbmlib.h"\ + "..\client\scriplib.h"\ + "..\client\trilib.h"\ + + +"$(INTDIR)\sv_send.obj" : $(SOURCE) $(DEP_CPP_SV_SE) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "qwsv - Win32 Debug" + +DEP_CPP_SV_SE=\ + "..\client\spritegn.h"\ + ".\../client\bothdefs.h"\ + ".\../client\bspfile.h"\ + ".\../client\cmd.h"\ + ".\../client\common.h"\ + ".\../client\crc.h"\ + ".\../client\cvar.h"\ + ".\../client\mathlib.h"\ + ".\../client\model.h"\ + ".\../client\net.h"\ + ".\../client\pmove.h"\ + ".\../client\protocol.h"\ + ".\../client\zone.h"\ + ".\..\client\modelgen.h"\ + ".\pr_comp.h"\ + ".\progdefs.h"\ + ".\progs.h"\ + ".\qwsvdef.h"\ + ".\server.h"\ + ".\sys.h"\ + ".\world.h"\ + +NODEP_CPP_SV_SE=\ + "..\client\cmdlib.h"\ + "..\client\dictlib.h"\ + "..\client\lbmlib.h"\ + "..\client\scriplib.h"\ + "..\client\trilib.h"\ + + +"$(INTDIR)\sv_send.obj" : $(SOURCE) $(DEP_CPP_SV_SE) "$(INTDIR)" + +"$(INTDIR)\sv_send.sbr" : $(SOURCE) $(DEP_CPP_SV_SE) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\sv_user.c + +!IF "$(CFG)" == "qwsv - Win32 Release" + +DEP_CPP_SV_US=\ + "..\client\spritegn.h"\ + ".\../client\bothdefs.h"\ + ".\../client\bspfile.h"\ + ".\../client\cmd.h"\ + ".\../client\common.h"\ + ".\../client\crc.h"\ + ".\../client\cvar.h"\ + ".\../client\mathlib.h"\ + ".\../client\model.h"\ + ".\../client\net.h"\ + ".\../client\pmove.h"\ + ".\../client\protocol.h"\ + ".\../client\zone.h"\ + ".\..\client\modelgen.h"\ + ".\pr_comp.h"\ + ".\progdefs.h"\ + ".\progs.h"\ + ".\qwsvdef.h"\ + ".\server.h"\ + ".\sys.h"\ + ".\world.h"\ + +NODEP_CPP_SV_US=\ + "..\client\cmdlib.h"\ + "..\client\dictlib.h"\ + "..\client\lbmlib.h"\ + "..\client\scriplib.h"\ + "..\client\trilib.h"\ + + +"$(INTDIR)\sv_user.obj" : $(SOURCE) $(DEP_CPP_SV_US) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "qwsv - Win32 Debug" + +DEP_CPP_SV_US=\ + "..\client\spritegn.h"\ + ".\../client\bothdefs.h"\ + ".\../client\bspfile.h"\ + ".\../client\cmd.h"\ + ".\../client\common.h"\ + ".\../client\crc.h"\ + ".\../client\cvar.h"\ + ".\../client\mathlib.h"\ + ".\../client\model.h"\ + ".\../client\net.h"\ + ".\../client\pmove.h"\ + ".\../client\protocol.h"\ + ".\../client\zone.h"\ + ".\..\client\modelgen.h"\ + ".\pr_comp.h"\ + ".\progdefs.h"\ + ".\progs.h"\ + ".\qwsvdef.h"\ + ".\server.h"\ + ".\sys.h"\ + ".\world.h"\ + +NODEP_CPP_SV_US=\ + "..\client\cmdlib.h"\ + "..\client\dictlib.h"\ + "..\client\lbmlib.h"\ + "..\client\scriplib.h"\ + "..\client\trilib.h"\ + + +"$(INTDIR)\sv_user.obj" : $(SOURCE) $(DEP_CPP_SV_US) "$(INTDIR)" + +"$(INTDIR)\sv_user.sbr" : $(SOURCE) $(DEP_CPP_SV_US) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\world.c + +!IF "$(CFG)" == "qwsv - Win32 Release" + +DEP_CPP_WORLD=\ + "..\client\spritegn.h"\ + ".\../client\bothdefs.h"\ + ".\../client\bspfile.h"\ + ".\../client\cmd.h"\ + ".\../client\common.h"\ + ".\../client\crc.h"\ + ".\../client\cvar.h"\ + ".\../client\mathlib.h"\ + ".\../client\model.h"\ + ".\../client\net.h"\ + ".\../client\pmove.h"\ + ".\../client\protocol.h"\ + ".\../client\zone.h"\ + ".\..\client\modelgen.h"\ + ".\pr_comp.h"\ + ".\progdefs.h"\ + ".\progs.h"\ + ".\qwsvdef.h"\ + ".\server.h"\ + ".\sys.h"\ + ".\world.h"\ + +NODEP_CPP_WORLD=\ + "..\client\cmdlib.h"\ + "..\client\dictlib.h"\ + "..\client\lbmlib.h"\ + "..\client\scriplib.h"\ + "..\client\trilib.h"\ + + +"$(INTDIR)\world.obj" : $(SOURCE) $(DEP_CPP_WORLD) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "qwsv - Win32 Debug" + +DEP_CPP_WORLD=\ + "..\client\spritegn.h"\ + ".\../client\bothdefs.h"\ + ".\../client\bspfile.h"\ + ".\../client\cmd.h"\ + ".\../client\common.h"\ + ".\../client\crc.h"\ + ".\../client\cvar.h"\ + ".\../client\mathlib.h"\ + ".\../client\model.h"\ + ".\../client\net.h"\ + ".\../client\pmove.h"\ + ".\../client\protocol.h"\ + ".\../client\zone.h"\ + ".\..\client\modelgen.h"\ + ".\pr_comp.h"\ + ".\progdefs.h"\ + ".\progs.h"\ + ".\qwsvdef.h"\ + ".\server.h"\ + ".\sys.h"\ + ".\world.h"\ + +NODEP_CPP_WORLD=\ + "..\client\cmdlib.h"\ + "..\client\dictlib.h"\ + "..\client\lbmlib.h"\ + "..\client\scriplib.h"\ + "..\client\trilib.h"\ + + +"$(INTDIR)\world.obj" : $(SOURCE) $(DEP_CPP_WORLD) "$(INTDIR)" + +"$(INTDIR)\world.sbr" : $(SOURCE) $(DEP_CPP_WORLD) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\sys_win.c + +!IF "$(CFG)" == "qwsv - Win32 Release" + +DEP_CPP_SYS_W=\ + "..\client\spritegn.h"\ + ".\../client\bothdefs.h"\ + ".\../client\bspfile.h"\ + ".\../client\cmd.h"\ + ".\../client\common.h"\ + ".\../client\crc.h"\ + ".\../client\cvar.h"\ + ".\../client\mathlib.h"\ + ".\../client\model.h"\ + ".\../client\net.h"\ + ".\../client\pmove.h"\ + ".\../client\protocol.h"\ + ".\../client\zone.h"\ + ".\..\client\modelgen.h"\ + ".\pr_comp.h"\ + ".\progdefs.h"\ + ".\progs.h"\ + ".\qwsvdef.h"\ + ".\server.h"\ + ".\sys.h"\ + ".\world.h"\ + {$(INCLUDE)}"\sys\TIMEB.H"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_SYS_W=\ + "..\client\cmdlib.h"\ + "..\client\dictlib.h"\ + "..\client\lbmlib.h"\ + "..\client\scriplib.h"\ + "..\client\trilib.h"\ + + +"$(INTDIR)\sys_win.obj" : $(SOURCE) $(DEP_CPP_SYS_W) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "qwsv - Win32 Debug" + +DEP_CPP_SYS_W=\ + "..\client\spritegn.h"\ + ".\../client\bothdefs.h"\ + ".\../client\bspfile.h"\ + ".\../client\cmd.h"\ + ".\../client\common.h"\ + ".\../client\crc.h"\ + ".\../client\cvar.h"\ + ".\../client\mathlib.h"\ + ".\../client\model.h"\ + ".\../client\net.h"\ + ".\../client\pmove.h"\ + ".\../client\protocol.h"\ + ".\../client\zone.h"\ + ".\..\client\modelgen.h"\ + ".\pr_comp.h"\ + ".\progdefs.h"\ + ".\progs.h"\ + ".\qwsvdef.h"\ + ".\server.h"\ + ".\sys.h"\ + ".\world.h"\ + {$(INCLUDE)}"\sys\TIMEB.H"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_SYS_W=\ + "..\client\cmdlib.h"\ + "..\client\dictlib.h"\ + "..\client\lbmlib.h"\ + "..\client\scriplib.h"\ + "..\client\trilib.h"\ + + +"$(INTDIR)\sys_win.obj" : $(SOURCE) $(DEP_CPP_SYS_W) "$(INTDIR)" + +"$(INTDIR)\sys_win.sbr" : $(SOURCE) $(DEP_CPP_SYS_W) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\server.h + +!IF "$(CFG)" == "qwsv - Win32 Release" + +!ELSEIF "$(CFG)" == "qwsv - Win32 Debug" + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=\quake\v2\client\net.h + +!IF "$(CFG)" == "qwsv - Win32 Release" + +!ELSEIF "$(CFG)" == "qwsv - Win32 Debug" + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\sv_ents.c + +!IF "$(CFG)" == "qwsv - Win32 Release" + +DEP_CPP_SV_EN=\ + "..\client\spritegn.h"\ + ".\../client\bothdefs.h"\ + ".\../client\bspfile.h"\ + ".\../client\cmd.h"\ + ".\../client\common.h"\ + ".\../client\crc.h"\ + ".\../client\cvar.h"\ + ".\../client\mathlib.h"\ + ".\../client\model.h"\ + ".\../client\net.h"\ + ".\../client\pmove.h"\ + ".\../client\protocol.h"\ + ".\../client\zone.h"\ + ".\..\client\modelgen.h"\ + ".\pr_comp.h"\ + ".\progdefs.h"\ + ".\progs.h"\ + ".\qwsvdef.h"\ + ".\server.h"\ + ".\sys.h"\ + ".\world.h"\ + +NODEP_CPP_SV_EN=\ + "..\client\cmdlib.h"\ + "..\client\dictlib.h"\ + "..\client\lbmlib.h"\ + "..\client\scriplib.h"\ + "..\client\trilib.h"\ + + +"$(INTDIR)\sv_ents.obj" : $(SOURCE) $(DEP_CPP_SV_EN) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "qwsv - Win32 Debug" + +DEP_CPP_SV_EN=\ + "..\client\spritegn.h"\ + ".\../client\bothdefs.h"\ + ".\../client\bspfile.h"\ + ".\../client\cmd.h"\ + ".\../client\common.h"\ + ".\../client\crc.h"\ + ".\../client\cvar.h"\ + ".\../client\mathlib.h"\ + ".\../client\model.h"\ + ".\../client\net.h"\ + ".\../client\pmove.h"\ + ".\../client\protocol.h"\ + ".\../client\zone.h"\ + ".\..\client\modelgen.h"\ + ".\pr_comp.h"\ + ".\progdefs.h"\ + ".\progs.h"\ + ".\qwsvdef.h"\ + ".\server.h"\ + ".\sys.h"\ + ".\world.h"\ + +NODEP_CPP_SV_EN=\ + "..\client\cmdlib.h"\ + "..\client\dictlib.h"\ + "..\client\lbmlib.h"\ + "..\client\scriplib.h"\ + "..\client\trilib.h"\ + + +"$(INTDIR)\sv_ents.obj" : $(SOURCE) $(DEP_CPP_SV_EN) "$(INTDIR)" + +"$(INTDIR)\sv_ents.sbr" : $(SOURCE) $(DEP_CPP_SV_EN) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=\Projects\QW\client\mathlib.c +DEP_CPP_MATHL=\ + "..\client\cdaudio.h"\ + "..\client\client.h"\ + "..\client\console.h"\ + "..\client\d_iface.h"\ + "..\client\draw.h"\ + "..\client\gl_model.h"\ + "..\client\glquake.h"\ + "..\client\input.h"\ + "..\client\keys.h"\ + "..\client\menu.h"\ + "..\client\render.h"\ + "..\client\sbar.h"\ + "..\client\screen.h"\ + "..\client\sound.h"\ + "..\client\spritegn.h"\ + "..\client\sys.h"\ + "..\client\vid.h"\ + "..\client\view.h"\ + "..\client\wad.h"\ + ".\../client\bothdefs.h"\ + ".\../client\bspfile.h"\ + ".\../client\cmd.h"\ + ".\../client\common.h"\ + ".\../client\crc.h"\ + ".\../client\cvar.h"\ + ".\../client\mathlib.h"\ + ".\../client\model.h"\ + ".\../client\net.h"\ + ".\../client\pmove.h"\ + ".\../client\protocol.h"\ + ".\../client\zone.h"\ + ".\..\client\modelgen.h"\ + ".\..\client\quakedef.h"\ + +NODEP_CPP_MATHL=\ + "..\client\cmdlib.h"\ + "..\client\dictlib.h"\ + "..\client\lbmlib.h"\ + "..\client\scriplib.h"\ + "..\client\trilib.h"\ + + +!IF "$(CFG)" == "qwsv - Win32 Release" + + +"$(INTDIR)\mathlib.obj" : $(SOURCE) $(DEP_CPP_MATHL) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "qwsv - Win32 Debug" + + +BuildCmds= \ + $(CPP) $(CPP_PROJ) $(SOURCE) \ + + +"$(INTDIR)\mathlib.obj" : $(SOURCE) $(DEP_CPP_MATHL) "$(INTDIR)" + $(BuildCmds) + +"$(INTDIR)\mathlib.sbr" : $(SOURCE) $(DEP_CPP_MATHL) "$(INTDIR)" + $(BuildCmds) + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=\Projects\QW\client\net_chan.c +DEP_CPP_NET_C=\ + "..\client\cdaudio.h"\ + "..\client\client.h"\ + "..\client\console.h"\ + "..\client\d_iface.h"\ + "..\client\draw.h"\ + "..\client\gl_model.h"\ + "..\client\glquake.h"\ + "..\client\input.h"\ + "..\client\keys.h"\ + "..\client\menu.h"\ + "..\client\render.h"\ + "..\client\sbar.h"\ + "..\client\screen.h"\ + "..\client\sound.h"\ + "..\client\spritegn.h"\ + "..\client\sys.h"\ + "..\client\vid.h"\ + "..\client\view.h"\ + "..\client\wad.h"\ + ".\../client\bothdefs.h"\ + ".\../client\bspfile.h"\ + ".\../client\cmd.h"\ + ".\../client\common.h"\ + ".\../client\crc.h"\ + ".\../client\cvar.h"\ + ".\../client\mathlib.h"\ + ".\../client\model.h"\ + ".\../client\net.h"\ + ".\../client\pmove.h"\ + ".\../client\protocol.h"\ + ".\../client\zone.h"\ + ".\..\client\modelgen.h"\ + ".\..\client\quakedef.h"\ + +NODEP_CPP_NET_C=\ + "..\client\cmdlib.h"\ + "..\client\dictlib.h"\ + "..\client\lbmlib.h"\ + "..\client\scriplib.h"\ + "..\client\trilib.h"\ + + +!IF "$(CFG)" == "qwsv - Win32 Release" + + +"$(INTDIR)\net_chan.obj" : $(SOURCE) $(DEP_CPP_NET_C) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "qwsv - Win32 Debug" + + +BuildCmds= \ + $(CPP) $(CPP_PROJ) $(SOURCE) \ + + +"$(INTDIR)\net_chan.obj" : $(SOURCE) $(DEP_CPP_NET_C) "$(INTDIR)" + $(BuildCmds) + +"$(INTDIR)\net_chan.sbr" : $(SOURCE) $(DEP_CPP_NET_C) "$(INTDIR)" + $(BuildCmds) + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=\Projects\QW\client\common.c +DEP_CPP_COMMO=\ + "..\client\cdaudio.h"\ + "..\client\client.h"\ + "..\client\console.h"\ + "..\client\d_iface.h"\ + "..\client\draw.h"\ + "..\client\gl_model.h"\ + "..\client\glquake.h"\ + "..\client\input.h"\ + "..\client\keys.h"\ + "..\client\menu.h"\ + "..\client\render.h"\ + "..\client\sbar.h"\ + "..\client\screen.h"\ + "..\client\sound.h"\ + "..\client\spritegn.h"\ + "..\client\sys.h"\ + "..\client\vid.h"\ + "..\client\view.h"\ + "..\client\wad.h"\ + ".\../client\bothdefs.h"\ + ".\../client\bspfile.h"\ + ".\../client\cmd.h"\ + ".\../client\common.h"\ + ".\../client\crc.h"\ + ".\../client\cvar.h"\ + ".\../client\mathlib.h"\ + ".\../client\model.h"\ + ".\../client\net.h"\ + ".\../client\pmove.h"\ + ".\../client\protocol.h"\ + ".\../client\zone.h"\ + ".\..\client\modelgen.h"\ + ".\..\client\quakedef.h"\ + +NODEP_CPP_COMMO=\ + "..\client\cmdlib.h"\ + "..\client\dictlib.h"\ + "..\client\lbmlib.h"\ + "..\client\qwsvdef.h"\ + "..\client\scriplib.h"\ + "..\client\trilib.h"\ + + +!IF "$(CFG)" == "qwsv - Win32 Release" + + +"$(INTDIR)\common.obj" : $(SOURCE) $(DEP_CPP_COMMO) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "qwsv - Win32 Debug" + + +BuildCmds= \ + $(CPP) $(CPP_PROJ) $(SOURCE) \ + + +"$(INTDIR)\common.obj" : $(SOURCE) $(DEP_CPP_COMMO) "$(INTDIR)" + $(BuildCmds) + +"$(INTDIR)\common.sbr" : $(SOURCE) $(DEP_CPP_COMMO) "$(INTDIR)" + $(BuildCmds) + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=\Projects\QW\client\pmovetst.c +DEP_CPP_PMOVE=\ + "..\client\cdaudio.h"\ + "..\client\client.h"\ + "..\client\console.h"\ + "..\client\d_iface.h"\ + "..\client\draw.h"\ + "..\client\gl_model.h"\ + "..\client\glquake.h"\ + "..\client\input.h"\ + "..\client\keys.h"\ + "..\client\menu.h"\ + "..\client\render.h"\ + "..\client\sbar.h"\ + "..\client\screen.h"\ + "..\client\sound.h"\ + "..\client\spritegn.h"\ + "..\client\sys.h"\ + "..\client\vid.h"\ + "..\client\view.h"\ + "..\client\wad.h"\ + ".\../client\bothdefs.h"\ + ".\../client\bspfile.h"\ + ".\../client\cmd.h"\ + ".\../client\common.h"\ + ".\../client\crc.h"\ + ".\../client\cvar.h"\ + ".\../client\mathlib.h"\ + ".\../client\model.h"\ + ".\../client\net.h"\ + ".\../client\pmove.h"\ + ".\../client\protocol.h"\ + ".\../client\zone.h"\ + ".\..\client\modelgen.h"\ + ".\..\client\quakedef.h"\ + +NODEP_CPP_PMOVE=\ + "..\client\cmdlib.h"\ + "..\client\dictlib.h"\ + "..\client\lbmlib.h"\ + "..\client\scriplib.h"\ + "..\client\trilib.h"\ + + +!IF "$(CFG)" == "qwsv - Win32 Release" + + +"$(INTDIR)\pmovetst.obj" : $(SOURCE) $(DEP_CPP_PMOVE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "qwsv - Win32 Debug" + + +BuildCmds= \ + $(CPP) $(CPP_PROJ) $(SOURCE) \ + + +"$(INTDIR)\pmovetst.obj" : $(SOURCE) $(DEP_CPP_PMOVE) "$(INTDIR)" + $(BuildCmds) + +"$(INTDIR)\pmovetst.sbr" : $(SOURCE) $(DEP_CPP_PMOVE) "$(INTDIR)" + $(BuildCmds) + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=\Projects\QW\client\pmove.c +DEP_CPP_PMOVE_=\ + "..\client\cdaudio.h"\ + "..\client\client.h"\ + "..\client\console.h"\ + "..\client\d_iface.h"\ + "..\client\draw.h"\ + "..\client\gl_model.h"\ + "..\client\glquake.h"\ + "..\client\input.h"\ + "..\client\keys.h"\ + "..\client\menu.h"\ + "..\client\render.h"\ + "..\client\sbar.h"\ + "..\client\screen.h"\ + "..\client\sound.h"\ + "..\client\spritegn.h"\ + "..\client\sys.h"\ + "..\client\vid.h"\ + "..\client\view.h"\ + "..\client\wad.h"\ + ".\../client\bothdefs.h"\ + ".\../client\bspfile.h"\ + ".\../client\cmd.h"\ + ".\../client\common.h"\ + ".\../client\crc.h"\ + ".\../client\cvar.h"\ + ".\../client\mathlib.h"\ + ".\../client\model.h"\ + ".\../client\net.h"\ + ".\../client\pmove.h"\ + ".\../client\protocol.h"\ + ".\../client\zone.h"\ + ".\..\client\modelgen.h"\ + ".\..\client\quakedef.h"\ + +NODEP_CPP_PMOVE_=\ + "..\client\cmdlib.h"\ + "..\client\dictlib.h"\ + "..\client\lbmlib.h"\ + "..\client\scriplib.h"\ + "..\client\trilib.h"\ + + +!IF "$(CFG)" == "qwsv - Win32 Release" + + +"$(INTDIR)\pmove.obj" : $(SOURCE) $(DEP_CPP_PMOVE_) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "qwsv - Win32 Debug" + + +BuildCmds= \ + $(CPP) $(CPP_PROJ) $(SOURCE) \ + + +"$(INTDIR)\pmove.obj" : $(SOURCE) $(DEP_CPP_PMOVE_) "$(INTDIR)" + $(BuildCmds) + +"$(INTDIR)\pmove.sbr" : $(SOURCE) $(DEP_CPP_PMOVE_) "$(INTDIR)" + $(BuildCmds) + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=\Projects\QW\client\cvar.c +DEP_CPP_CVAR_=\ + "..\client\cdaudio.h"\ + "..\client\client.h"\ + "..\client\console.h"\ + "..\client\d_iface.h"\ + "..\client\draw.h"\ + "..\client\gl_model.h"\ + "..\client\glquake.h"\ + "..\client\input.h"\ + "..\client\keys.h"\ + "..\client\menu.h"\ + "..\client\render.h"\ + "..\client\sbar.h"\ + "..\client\screen.h"\ + "..\client\sound.h"\ + "..\client\spritegn.h"\ + "..\client\sys.h"\ + "..\client\vid.h"\ + "..\client\view.h"\ + "..\client\wad.h"\ + ".\../client\bothdefs.h"\ + ".\../client\bspfile.h"\ + ".\../client\cmd.h"\ + ".\../client\common.h"\ + ".\../client\crc.h"\ + ".\../client\cvar.h"\ + ".\../client\mathlib.h"\ + ".\../client\model.h"\ + ".\../client\net.h"\ + ".\../client\pmove.h"\ + ".\../client\protocol.h"\ + ".\../client\zone.h"\ + ".\..\client\modelgen.h"\ + ".\..\client\quakedef.h"\ + +NODEP_CPP_CVAR_=\ + "..\client\cmdlib.h"\ + "..\client\dictlib.h"\ + "..\client\lbmlib.h"\ + "..\client\qwsvdef.h"\ + "..\client\scriplib.h"\ + "..\client\trilib.h"\ + + +!IF "$(CFG)" == "qwsv - Win32 Release" + + +"$(INTDIR)\cvar.obj" : $(SOURCE) $(DEP_CPP_CVAR_) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "qwsv - Win32 Debug" + + +BuildCmds= \ + $(CPP) $(CPP_PROJ) $(SOURCE) \ + + +"$(INTDIR)\cvar.obj" : $(SOURCE) $(DEP_CPP_CVAR_) "$(INTDIR)" + $(BuildCmds) + +"$(INTDIR)\cvar.sbr" : $(SOURCE) $(DEP_CPP_CVAR_) "$(INTDIR)" + $(BuildCmds) + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=\Projects\QW\client\crc.c +DEP_CPP_CRC_C=\ + "..\client\cdaudio.h"\ + "..\client\client.h"\ + "..\client\console.h"\ + "..\client\d_iface.h"\ + "..\client\draw.h"\ + "..\client\gl_model.h"\ + "..\client\glquake.h"\ + "..\client\input.h"\ + "..\client\keys.h"\ + "..\client\menu.h"\ + "..\client\render.h"\ + "..\client\sbar.h"\ + "..\client\screen.h"\ + "..\client\sound.h"\ + "..\client\spritegn.h"\ + "..\client\sys.h"\ + "..\client\vid.h"\ + "..\client\view.h"\ + "..\client\wad.h"\ + ".\../client\bothdefs.h"\ + ".\../client\bspfile.h"\ + ".\../client\cmd.h"\ + ".\../client\common.h"\ + ".\../client\crc.h"\ + ".\../client\cvar.h"\ + ".\../client\mathlib.h"\ + ".\../client\model.h"\ + ".\../client\net.h"\ + ".\../client\pmove.h"\ + ".\../client\protocol.h"\ + ".\../client\zone.h"\ + ".\..\client\modelgen.h"\ + ".\..\client\quakedef.h"\ + +NODEP_CPP_CRC_C=\ + "..\client\cmdlib.h"\ + "..\client\dictlib.h"\ + "..\client\lbmlib.h"\ + "..\client\scriplib.h"\ + "..\client\trilib.h"\ + + +!IF "$(CFG)" == "qwsv - Win32 Release" + + +"$(INTDIR)\crc.obj" : $(SOURCE) $(DEP_CPP_CRC_C) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "qwsv - Win32 Debug" + + +BuildCmds= \ + $(CPP) $(CPP_PROJ) $(SOURCE) \ + + +"$(INTDIR)\crc.obj" : $(SOURCE) $(DEP_CPP_CRC_C) "$(INTDIR)" + $(BuildCmds) + +"$(INTDIR)\crc.sbr" : $(SOURCE) $(DEP_CPP_CRC_C) "$(INTDIR)" + $(BuildCmds) + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=\Projects\QW\client\net_wins.c +DEP_CPP_NET_W=\ + "..\client\cdaudio.h"\ + "..\client\client.h"\ + "..\client\console.h"\ + "..\client\d_iface.h"\ + "..\client\draw.h"\ + "..\client\gl_model.h"\ + "..\client\glquake.h"\ + "..\client\input.h"\ + "..\client\keys.h"\ + "..\client\menu.h"\ + "..\client\render.h"\ + "..\client\sbar.h"\ + "..\client\screen.h"\ + "..\client\sound.h"\ + "..\client\spritegn.h"\ + "..\client\sys.h"\ + "..\client\vid.h"\ + "..\client\view.h"\ + "..\client\wad.h"\ + ".\../client\bothdefs.h"\ + ".\../client\bspfile.h"\ + ".\../client\cmd.h"\ + ".\../client\common.h"\ + ".\../client\crc.h"\ + ".\../client\cvar.h"\ + ".\../client\mathlib.h"\ + ".\../client\model.h"\ + ".\../client\net.h"\ + ".\../client\pmove.h"\ + ".\../client\protocol.h"\ + ".\../client\zone.h"\ + ".\..\client\modelgen.h"\ + ".\..\client\quakedef.h"\ + ".\..\client\winquake.h"\ + +NODEP_CPP_NET_W=\ + "..\client\cmdlib.h"\ + "..\client\dictlib.h"\ + "..\client\lbmlib.h"\ + "..\client\scriplib.h"\ + "..\client\trilib.h"\ + + +!IF "$(CFG)" == "qwsv - Win32 Release" + + +"$(INTDIR)\net_wins.obj" : $(SOURCE) $(DEP_CPP_NET_W) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "qwsv - Win32 Debug" + + +BuildCmds= \ + $(CPP) $(CPP_PROJ) $(SOURCE) \ + + +"$(INTDIR)\net_wins.obj" : $(SOURCE) $(DEP_CPP_NET_W) "$(INTDIR)" + $(BuildCmds) + +"$(INTDIR)\net_wins.sbr" : $(SOURCE) $(DEP_CPP_NET_W) "$(INTDIR)" + $(BuildCmds) + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=\Projects\QW\client\zone.c +DEP_CPP_ZONE_=\ + "..\client\cdaudio.h"\ + "..\client\client.h"\ + "..\client\console.h"\ + "..\client\d_iface.h"\ + "..\client\draw.h"\ + "..\client\gl_model.h"\ + "..\client\glquake.h"\ + "..\client\input.h"\ + "..\client\keys.h"\ + "..\client\menu.h"\ + "..\client\render.h"\ + "..\client\sbar.h"\ + "..\client\screen.h"\ + "..\client\sound.h"\ + "..\client\spritegn.h"\ + "..\client\sys.h"\ + "..\client\vid.h"\ + "..\client\view.h"\ + "..\client\wad.h"\ + ".\../client\bothdefs.h"\ + ".\../client\bspfile.h"\ + ".\../client\cmd.h"\ + ".\../client\common.h"\ + ".\../client\crc.h"\ + ".\../client\cvar.h"\ + ".\../client\mathlib.h"\ + ".\../client\model.h"\ + ".\../client\net.h"\ + ".\../client\pmove.h"\ + ".\../client\protocol.h"\ + ".\../client\zone.h"\ + ".\..\client\modelgen.h"\ + ".\..\client\quakedef.h"\ + +NODEP_CPP_ZONE_=\ + "..\client\cmdlib.h"\ + "..\client\dictlib.h"\ + "..\client\lbmlib.h"\ + "..\client\scriplib.h"\ + "..\client\trilib.h"\ + + +!IF "$(CFG)" == "qwsv - Win32 Release" + + +"$(INTDIR)\zone.obj" : $(SOURCE) $(DEP_CPP_ZONE_) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "qwsv - Win32 Debug" + + +BuildCmds= \ + $(CPP) $(CPP_PROJ) $(SOURCE) \ + + +"$(INTDIR)\zone.obj" : $(SOURCE) $(DEP_CPP_ZONE_) "$(INTDIR)" + $(BuildCmds) + +"$(INTDIR)\zone.sbr" : $(SOURCE) $(DEP_CPP_ZONE_) "$(INTDIR)" + $(BuildCmds) + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=\Projects\QW\client\cmd.c +DEP_CPP_CMD_C=\ + "..\client\cdaudio.h"\ + "..\client\client.h"\ + "..\client\console.h"\ + "..\client\d_iface.h"\ + "..\client\draw.h"\ + "..\client\gl_model.h"\ + "..\client\glquake.h"\ + "..\client\input.h"\ + "..\client\keys.h"\ + "..\client\menu.h"\ + "..\client\render.h"\ + "..\client\sbar.h"\ + "..\client\screen.h"\ + "..\client\sound.h"\ + "..\client\spritegn.h"\ + "..\client\sys.h"\ + "..\client\vid.h"\ + "..\client\view.h"\ + "..\client\wad.h"\ + ".\../client\bothdefs.h"\ + ".\../client\bspfile.h"\ + ".\../client\cmd.h"\ + ".\../client\common.h"\ + ".\../client\crc.h"\ + ".\../client\cvar.h"\ + ".\../client\mathlib.h"\ + ".\../client\model.h"\ + ".\../client\net.h"\ + ".\../client\pmove.h"\ + ".\../client\protocol.h"\ + ".\../client\zone.h"\ + ".\..\client\modelgen.h"\ + ".\..\client\quakedef.h"\ + +NODEP_CPP_CMD_C=\ + "..\client\cmdlib.h"\ + "..\client\dictlib.h"\ + "..\client\lbmlib.h"\ + "..\client\scriplib.h"\ + "..\client\trilib.h"\ + + +!IF "$(CFG)" == "qwsv - Win32 Release" + + +"$(INTDIR)\cmd.obj" : $(SOURCE) $(DEP_CPP_CMD_C) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "qwsv - Win32 Debug" + + +BuildCmds= \ + $(CPP) $(CPP_PROJ) $(SOURCE) \ + + +"$(INTDIR)\cmd.obj" : $(SOURCE) $(DEP_CPP_CMD_C) "$(INTDIR)" + $(BuildCmds) + +"$(INTDIR)\cmd.sbr" : $(SOURCE) $(DEP_CPP_CMD_C) "$(INTDIR)" + $(BuildCmds) + +!ENDIF + +# End Source File +# End Target +# End Project +################################################################################ diff --git a/QW/server/qwsv.mdp b/QW/server/qwsv.mdp Binary files differnew file mode 100644 index 0000000..ae12639 --- /dev/null +++ b/QW/server/qwsv.mdp diff --git a/QW/server/qwsv.plg b/QW/server/qwsv.plg new file mode 100644 index 0000000..ebaa9f8 --- /dev/null +++ b/QW/server/qwsv.plg @@ -0,0 +1,107 @@ +<html> +<body> +<pre> +<h1>Build Log</h1> +<h3> +--------------------Configuration: qwsv - Win32 Release-------------------- +</h3> +<h3>Command Lines</h3> +Creating temporary file "C:\TEMP\RSP778.tmp" with contents +[ +/nologo /ML /GX /O2 /I "." /I "..\client" /D "NDEBUG" /D "SERVERONLY" /D "WIN32" /D "_CONSOLE" /FR".\Release/" /Fp".\Release/qwsv.pch" /YX /Fo".\Release/" /Fd".\Release/" /FD /c +"D:\Work\quake source\QW\client\cmd.c" +"D:\Work\quake source\QW\client\common.c" +"D:\Work\quake source\QW\client\crc.c" +"D:\Work\quake source\QW\client\cvar.c" +"D:\Work\quake source\QW\client\mathlib.c" +"D:\Work\quake source\QW\client\md4.c" +"D:\Work\quake source\QW\server\model.c" +"D:\Work\quake source\QW\client\net_chan.c" +"D:\Work\quake source\QW\client\net_wins.c" +"D:\Work\quake source\QW\client\pmove.c" +"D:\Work\quake source\QW\client\pmovetst.c" +"D:\Work\quake source\QW\server\pr_cmds.c" +"D:\Work\quake source\QW\server\pr_edict.c" +"D:\Work\quake source\QW\server\pr_exec.c" +"D:\Work\quake source\QW\server\sv_ccmds.c" +"D:\Work\quake source\QW\server\sv_ents.c" +"D:\Work\quake source\QW\server\sv_init.c" +"D:\Work\quake source\QW\server\sv_main.c" +"D:\Work\quake source\QW\server\sv_move.c" +"D:\Work\quake source\QW\server\sv_nchan.c" +"D:\Work\quake source\QW\server\sv_phys.c" +"D:\Work\quake source\QW\server\sv_send.c" +"D:\Work\quake source\QW\server\sv_user.c" +"D:\Work\quake source\QW\server\sys_win.c" +"D:\Work\quake source\QW\server\world.c" +"D:\Work\quake source\QW\client\zone.c" +] +Creating command line "cl.exe @C:\TEMP\RSP778.tmp" +Creating temporary file "C:\TEMP\RSP779.tmp" with contents +[ +wsock32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib winmm.lib /nologo /subsystem:console /incremental:no /pdb:".\Release/qwsv.pdb" /machine:I386 /out:".\Release/qwsv.exe" +".\Release\cmd.obj" +".\Release\common.obj" +".\Release\crc.obj" +".\Release\cvar.obj" +".\Release\mathlib.obj" +".\Release\md4.obj" +".\Release\model.obj" +".\Release\net_chan.obj" +".\Release\net_wins.obj" +".\Release\pmove.obj" +".\Release\pmovetst.obj" +".\Release\pr_cmds.obj" +".\Release\pr_edict.obj" +".\Release\pr_exec.obj" +".\Release\sv_ccmds.obj" +".\Release\sv_ents.obj" +".\Release\sv_init.obj" +".\Release\sv_main.obj" +".\Release\sv_move.obj" +".\Release\sv_nchan.obj" +".\Release\sv_phys.obj" +".\Release\sv_send.obj" +".\Release\sv_user.obj" +".\Release\sys_win.obj" +".\Release\world.obj" +".\Release\zone.obj" +] +Creating command line "link.exe @C:\TEMP\RSP779.tmp" +<h3>Output Window</h3> +Compiling... +cmd.c +common.c +crc.c +cvar.c +mathlib.c +md4.c +model.c +net_chan.c +net_wins.c +pmove.c +pmovetst.c +pr_cmds.c +pr_edict.c +pr_exec.c +sv_ccmds.c +sv_ents.c +sv_init.c +sv_main.c +sv_move.c +sv_nchan.c +sv_phys.c +sv_send.c +sv_user.c +sys_win.c +world.c +zone.c +Linking... + + + +<h3>Results</h3> +qwsv.exe - 0 error(s), 0 warning(s) +</pre> +</body> +</html> diff --git a/QW/server/qwsvdef.h b/QW/server/qwsvdef.h new file mode 100644 index 0000000..67058ab --- /dev/null +++ b/QW/server/qwsvdef.h @@ -0,0 +1,95 @@ +/* +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. + +*/ +// quakedef.h -- primary header for server + +#define QUAKE_GAME // as opposed to utilities + +//define PARANOID // speed sapping error checking + +#ifdef _WIN32 +#pragma warning( disable : 4244 4127 4201 4214 4514 4305 4115 4018) +#endif + +#include <math.h> +#include <string.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <setjmp.h> +#include <ctype.h> + +#include "bothdefs.h" + +#include "common.h" +#include "bspfile.h" +#include "sys.h" +#include "zone.h" +#include "mathlib.h" + +#include "cvar.h" +#include "net.h" +#include "protocol.h" +#include "cmd.h" +#include "model.h" +#include "crc.h" +#include "progs.h" + +#include "server.h" +#include "world.h" +#include "pmove.h" + +//============================================================================= + +// the host system specifies the base of the directory tree, the +// command line parms passed to the program, and the amount of memory +// available for the program to use + +typedef struct +{ + char *basedir; + char *cachedir; // for development over ISDN lines + int argc; + char **argv; + void *membase; + int memsize; +} quakeparms_t; + + +//============================================================================= + +// +// host +// +extern quakeparms_t host_parms; + +extern cvar_t sys_nostdout; +extern cvar_t developer; + +extern qboolean host_initialized; // true if into command execution +extern double host_frametime; +extern double realtime; // not bounded in any way, changed at + // start of every frame, never reset + +void SV_Error (char *error, ...); +void SV_Init (quakeparms_t *parms); + +void Con_Printf (char *fmt, ...); +void Con_DPrintf (char *fmt, ...); + diff --git a/QW/server/server.h b/QW/server/server.h new file mode 100644 index 0000000..c9622eb --- /dev/null +++ b/QW/server/server.h @@ -0,0 +1,456 @@ +/* +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. + +*/ +// server.h + +#define QW_SERVER + +#define MAX_MASTERS 8 // max recipients for heartbeat packets + +#define MAX_SIGNON_BUFFERS 8 + +typedef enum { + ss_dead, // no map loaded + ss_loading, // spawning level edicts + ss_active // actively running +} server_state_t; +// some qc commands are only valid before the server has finished +// initializing (precache commands, static sounds / objects, etc) + +typedef struct +{ + qboolean active; // false when server is going down + server_state_t state; // precache commands are only valid during load + + double time; + + int lastcheck; // used by PF_checkclient + double lastchecktime; // for monster ai + + qboolean paused; // are we paused? + + //check player/eyes models for hacks + unsigned model_player_checksum; + unsigned eyes_player_checksum; + + char name[64]; // map name + char modelname[MAX_QPATH]; // maps/<name>.bsp, for model_precache[0] + struct model_s *worldmodel; + char *model_precache[MAX_MODELS]; // NULL terminated + char *sound_precache[MAX_SOUNDS]; // NULL terminated + char *lightstyles[MAX_LIGHTSTYLES]; + struct model_s *models[MAX_MODELS]; + + int num_edicts; // increases towards MAX_EDICTS + edict_t *edicts; // can NOT be array indexed, because + // edict_t is variable sized, but can + // be used to reference the world ent + + byte *pvs, *phs; // fully expanded and decompressed + + // added to every client's unreliable buffer each frame, then cleared + sizebuf_t datagram; + byte datagram_buf[MAX_DATAGRAM]; + + // added to every client's reliable buffer each frame, then cleared + sizebuf_t reliable_datagram; + byte reliable_datagram_buf[MAX_MSGLEN]; + + // the multicast buffer is used to send a message to a set of clients + sizebuf_t multicast; + byte multicast_buf[MAX_MSGLEN]; + + // the master buffer is used for building log packets + sizebuf_t master; + byte master_buf[MAX_DATAGRAM]; + + // the signon buffer will be sent to each client as they connect + // includes the entity baselines, the static entities, etc + // large levels will have >MAX_DATAGRAM sized signons, so + // multiple signon messages are kept + sizebuf_t signon; + int num_signon_buffers; + int signon_buffer_size[MAX_SIGNON_BUFFERS]; + byte signon_buffers[MAX_SIGNON_BUFFERS][MAX_DATAGRAM]; +} server_t; + + +#define NUM_SPAWN_PARMS 16 + +typedef enum +{ + cs_free, // can be reused for a new connection + cs_zombie, // client has been disconnected, but don't reuse + // connection for a couple seconds + cs_connected, // has been assigned to a client_t, but not in game yet + cs_spawned // client is fully in game +} client_state_t; + +typedef struct +{ + // received from client + + // reply + double senttime; + float ping_time; + packet_entities_t entities; +} client_frame_t; + +#define MAX_BACK_BUFFERS 4 + +typedef struct client_s +{ + client_state_t state; + + int spectator; // non-interactive + + qboolean sendinfo; // at end of frame, send info to all + // this prevents malicious multiple broadcasts + float lastnametime; // time of last name change + int lastnamecount; // time of last name change + unsigned checksum; // checksum for calcs + qboolean drop; // lose this guy next opportunity + int lossage; // loss percentage + + int userid; // identifying number + char userinfo[MAX_INFO_STRING]; // infostring + + usercmd_t lastcmd; // for filling in big drops and partial predictions + double localtime; // of last message + int oldbuttons; + + float maxspeed; // localized maxspeed + float entgravity; // localized ent gravity + + edict_t *edict; // EDICT_NUM(clientnum+1) + char name[32]; // for printing to other people + // extracted from userinfo + int messagelevel; // for filtering printed messages + + // the datagram is written to after every frame, but only cleared + // when it is sent out to the client. overflow is tolerated. + sizebuf_t datagram; + byte datagram_buf[MAX_DATAGRAM]; + + // back buffers for client reliable data + sizebuf_t backbuf; + int num_backbuf; + int backbuf_size[MAX_BACK_BUFFERS]; + byte backbuf_data[MAX_BACK_BUFFERS][MAX_MSGLEN]; + + double connection_started; // or time of disconnect for zombies + qboolean send_message; // set on frames a datagram arived on + +// spawn parms are carried from level to level + float spawn_parms[NUM_SPAWN_PARMS]; + +// client known data for deltas + int old_frags; + + int stats[MAX_CL_STATS]; + + + client_frame_t frames[UPDATE_BACKUP]; // updates can be deltad from here + + FILE *download; // file being downloaded + int downloadsize; // total bytes + int downloadcount; // bytes sent + + int spec_track; // entnum of player tracking + + double whensaid[10]; // JACK: For floodprots + int whensaidhead; // Head value for floodprots + double lockedtill; + + qboolean upgradewarn; // did we warn him? + + FILE *upload; + char uploadfn[MAX_QPATH]; + netadr_t snap_from; + qboolean remote_snap; + +//===== NETWORK ============ + int chokecount; + int delta_sequence; // -1 = no compression + netchan_t netchan; +} client_t; + +// a client can leave the server in one of four ways: +// dropping properly by quiting or disconnecting +// timing out if no valid messages are received for timeout.value seconds +// getting kicked off by the server operator +// a program error, like an overflowed reliable buffer + +//============================================================================= + + +#define STATFRAMES 100 +typedef struct +{ + double active; + double idle; + int count; + int packets; + + double latched_active; + double latched_idle; + int latched_packets; +} svstats_t; + +// MAX_CHALLENGES is made large to prevent a denial +// of service attack that could cycle all of them +// out before legitimate users connected +#define MAX_CHALLENGES 1024 + +typedef struct +{ + netadr_t adr; + int challenge; + int time; +} challenge_t; + +typedef struct +{ + int spawncount; // number of servers spawned since start, + // used to check late spawns + client_t clients[MAX_CLIENTS]; + int serverflags; // episode completion information + + double last_heartbeat; + int heartbeat_sequence; + svstats_t stats; + + char info[MAX_SERVERINFO_STRING]; + + // log messages are used so that fraglog processes can get stats + int logsequence; // the message currently being filled + double logtime; // time of last swap + sizebuf_t log[2]; + byte log_buf[2][MAX_DATAGRAM]; + + challenge_t challenges[MAX_CHALLENGES]; // to prevent invalid IPs from connecting +} server_static_t; + +//============================================================================= + +// edict->movetype values +#define MOVETYPE_NONE 0 // never moves +#define MOVETYPE_ANGLENOCLIP 1 +#define MOVETYPE_ANGLECLIP 2 +#define MOVETYPE_WALK 3 // gravity +#define MOVETYPE_STEP 4 // gravity, special edge handling +#define MOVETYPE_FLY 5 +#define MOVETYPE_TOSS 6 // gravity +#define MOVETYPE_PUSH 7 // no clip to world, push and crush +#define MOVETYPE_NOCLIP 8 +#define MOVETYPE_FLYMISSILE 9 // extra size to monsters +#define MOVETYPE_BOUNCE 10 + +// edict->solid values +#define SOLID_NOT 0 // no interaction with other objects +#define SOLID_TRIGGER 1 // touch on edge, but not blocking +#define SOLID_BBOX 2 // touch on edge, block +#define SOLID_SLIDEBOX 3 // touch on edge, but not an onground +#define SOLID_BSP 4 // bsp clip, touch on edge, block + +// edict->deadflag values +#define DEAD_NO 0 +#define DEAD_DYING 1 +#define DEAD_DEAD 2 + +#define DAMAGE_NO 0 +#define DAMAGE_YES 1 +#define DAMAGE_AIM 2 + +// edict->flags +#define FL_FLY 1 +#define FL_SWIM 2 +#define FL_GLIMPSE 4 +#define FL_CLIENT 8 +#define FL_INWATER 16 +#define FL_MONSTER 32 +#define FL_GODMODE 64 +#define FL_NOTARGET 128 +#define FL_ITEM 256 +#define FL_ONGROUND 512 +#define FL_PARTIALGROUND 1024 // not all corners are valid +#define FL_WATERJUMP 2048 // player jumping out of water + +// entity effects + +//define EF_BRIGHTFIELD 1 +//define EF_MUZZLEFLASH 2 +#define EF_BRIGHTLIGHT 4 +#define EF_DIMLIGHT 8 + + +#define SPAWNFLAG_NOT_EASY 256 +#define SPAWNFLAG_NOT_MEDIUM 512 +#define SPAWNFLAG_NOT_HARD 1024 +#define SPAWNFLAG_NOT_DEATHMATCH 2048 + +#define MULTICAST_ALL 0 +#define MULTICAST_PHS 1 +#define MULTICAST_PVS 2 + +#define MULTICAST_ALL_R 3 +#define MULTICAST_PHS_R 4 +#define MULTICAST_PVS_R 5 + +//============================================================================ + +extern cvar_t sv_mintic, sv_maxtic; +extern cvar_t sv_maxspeed; + +extern netadr_t master_adr[MAX_MASTERS]; // address of the master server + +extern cvar_t spawn; +extern cvar_t teamplay; +extern cvar_t deathmatch; +extern cvar_t fraglimit; +extern cvar_t timelimit; + +extern server_static_t svs; // persistant server info +extern server_t sv; // local server + +extern client_t *host_client; + +extern edict_t *sv_player; + +extern char localmodels[MAX_MODELS][5]; // inline model names for precache + +extern char localinfo[MAX_LOCALINFO_STRING+1]; + +extern int host_hunklevel; +extern FILE *sv_logfile; +extern FILE *sv_fraglogfile; + +//=========================================================== + +// +// sv_main.c +// +void SV_Shutdown (void); +void SV_Frame (float time); +void SV_FinalMessage (char *message); +void SV_DropClient (client_t *drop); + +int SV_CalcPing (client_t *cl); +void SV_FullClientUpdate (client_t *client, sizebuf_t *buf); + +int SV_ModelIndex (char *name); + +qboolean SV_CheckBottom (edict_t *ent); +qboolean SV_movestep (edict_t *ent, vec3_t move, qboolean relink); + +void SV_WriteClientdataToMessage (client_t *client, sizebuf_t *msg); + +void SV_MoveToGoal (void); + +void SV_SaveSpawnparms (void); + +void SV_Physics_Client (edict_t *ent); + +void SV_ExecuteUserCommand (char *s); +void SV_InitOperatorCommands (void); + +void SV_SendServerinfo (client_t *client); +void SV_ExtractFromUserinfo (client_t *cl); + + +void Master_Heartbeat (void); +void Master_Packet (void); + +// +// sv_init.c +// +void SV_SpawnServer (char *server); +void SV_FlushSignon (void); + + +// +// sv_phys.c +// +void SV_ProgStartFrame (void); +void SV_Physics (void); +void SV_CheckVelocity (edict_t *ent); +void SV_AddGravity (edict_t *ent, float scale); +qboolean SV_RunThink (edict_t *ent); +void SV_Physics_Toss (edict_t *ent); +void SV_RunNewmis (void); +void SV_Impact (edict_t *e1, edict_t *e2); +void SV_SetMoveVars(void); + +// +// sv_send.c +// +void SV_SendClientMessages (void); + +void SV_Multicast (vec3_t origin, int to); +void SV_StartSound (edict_t *entity, int channel, char *sample, int volume, + float attenuation); +void SV_ClientPrintf (client_t *cl, int level, char *fmt, ...); +void SV_BroadcastPrintf (int level, char *fmt, ...); +void SV_BroadcastCommand (char *fmt, ...); +void SV_SendMessagesToAll (void); +void SV_FindModelNumbers (void); + +// +// sv_user.c +// +void SV_ExecuteClientMessage (client_t *cl); +void SV_UserInit (void); +void SV_TogglePause (const char *msg); + + +// +// svonly.c +// +typedef enum {RD_NONE, RD_CLIENT, RD_PACKET} redirect_t; +void SV_BeginRedirect (redirect_t rd); +void SV_EndRedirect (void); + +// +// sv_ccmds.c +// +void SV_Status_f (void); + +// +// sv_ents.c +// +void SV_WriteEntitiesToClient (client_t *client, sizebuf_t *msg); + +// +// sv_nchan.c +// + +void ClientReliableCheckBlock(client_t *cl, int maxsize); +void ClientReliable_FinishWrite(client_t *cl); +void ClientReliableWrite_Begin(client_t *cl, int c, int maxsize); +void ClientReliableWrite_Angle(client_t *cl, float f); +void ClientReliableWrite_Angle16(client_t *cl, float f); +void ClientReliableWrite_Byte(client_t *cl, int c); +void ClientReliableWrite_Char(client_t *cl, int c); +void ClientReliableWrite_Float(client_t *cl, float f); +void ClientReliableWrite_Coord(client_t *cl, float f); +void ClientReliableWrite_Long(client_t *cl, int c); +void ClientReliableWrite_Short(client_t *cl, int c); +void ClientReliableWrite_String(client_t *cl, char *s); +void ClientReliableWrite_SZ(client_t *cl, void *data, int len); + diff --git a/QW/server/sv_ccmds.c b/QW/server/sv_ccmds.c new file mode 100644 index 0000000..14cec5d --- /dev/null +++ b/QW/server/sv_ccmds.c @@ -0,0 +1,900 @@ +/* +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. + +*/ + +#include "qwsvdef.h" + +qboolean sv_allow_cheats; + +int fp_messages=4, fp_persecond=4, fp_secondsdead=10; +char fp_msg[255] = { 0 }; +extern cvar_t cl_warncmd; + extern redirect_t sv_redirected; + + +/* +=============================================================================== + +OPERATOR CONSOLE ONLY COMMANDS + +These commands can only be entered from stdin or by a remote operator datagram +=============================================================================== +*/ + +/* +==================== +SV_SetMaster_f + +Make a master server current +==================== +*/ +void SV_SetMaster_f (void) +{ + char data[2]; + int i; + + memset (&master_adr, 0, sizeof(master_adr)); + + for (i=1 ; i<Cmd_Argc() ; i++) + { + if (!strcmp(Cmd_Argv(i), "none") || !NET_StringToAdr (Cmd_Argv(i), &master_adr[i-1])) + { + Con_Printf ("Setting nomaster mode.\n"); + return; + } + if (master_adr[i-1].port == 0) + master_adr[i-1].port = BigShort (27000); + + Con_Printf ("Master server at %s\n", NET_AdrToString (master_adr[i-1])); + + Con_Printf ("Sending a ping.\n"); + + data[0] = A2A_PING; + data[1] = 0; + NET_SendPacket (2, data, master_adr[i-1]); + } + + svs.last_heartbeat = -99999; +} + + +/* +================== +SV_Quit_f +================== +*/ +void SV_Quit_f (void) +{ + SV_FinalMessage ("server shutdown\n"); + Con_Printf ("Shutting down.\n"); + SV_Shutdown (); + Sys_Quit (); +} + +/* +============ +SV_Logfile_f +============ +*/ +void SV_Logfile_f (void) +{ + char name[MAX_OSPATH]; + + if (sv_logfile) + { + Con_Printf ("File logging off.\n"); + fclose (sv_logfile); + sv_logfile = NULL; + return; + } + + sprintf (name, "%s/qconsole.log", com_gamedir); + Con_Printf ("Logging text to %s.\n", name); + sv_logfile = fopen (name, "w"); + if (!sv_logfile) + Con_Printf ("failed.\n"); +} + + +/* +============ +SV_Fraglogfile_f +============ +*/ +void SV_Fraglogfile_f (void) +{ + char name[MAX_OSPATH]; + int i; + + if (sv_fraglogfile) + { + Con_Printf ("Frag file logging off.\n"); + fclose (sv_fraglogfile); + sv_fraglogfile = NULL; + return; + } + + // find an unused name + for (i=0 ; i<1000 ; i++) + { + sprintf (name, "%s/frag_%i.log", com_gamedir, i); + sv_fraglogfile = fopen (name, "r"); + if (!sv_fraglogfile) + { // can't read it, so create this one + sv_fraglogfile = fopen (name, "w"); + if (!sv_fraglogfile) + i=1000; // give error + break; + } + fclose (sv_fraglogfile); + } + if (i==1000) + { + Con_Printf ("Can't open any logfiles.\n"); + sv_fraglogfile = NULL; + return; + } + + Con_Printf ("Logging frags to %s.\n", name); +} + + +/* +================== +SV_SetPlayer + +Sets host_client and sv_player to the player with idnum Cmd_Argv(1) +================== +*/ +qboolean SV_SetPlayer (void) +{ + client_t *cl; + int i; + int idnum; + + idnum = atoi(Cmd_Argv(1)); + + for (i=0,cl=svs.clients ; i<MAX_CLIENTS ; i++,cl++) + { + if (!cl->state) + continue; + if (cl->userid == idnum) + { + host_client = cl; + sv_player = host_client->edict; + return true; + } + } + Con_Printf ("Userid %i is not on the server\n", idnum); + return false; +} + + +/* +================== +SV_God_f + +Sets client to godmode +================== +*/ +void SV_God_f (void) +{ + if (!sv_allow_cheats) + { + Con_Printf ("You must run the server with -cheats to enable this command.\n"); + return; + } + + if (!SV_SetPlayer ()) + return; + + sv_player->v.flags = (int)sv_player->v.flags ^ FL_GODMODE; + if (!((int)sv_player->v.flags & FL_GODMODE) ) + SV_ClientPrintf (host_client, PRINT_HIGH, "godmode OFF\n"); + else + SV_ClientPrintf (host_client, PRINT_HIGH, "godmode ON\n"); +} + + +void SV_Noclip_f (void) +{ + if (!sv_allow_cheats) + { + Con_Printf ("You must run the server with -cheats to enable this command.\n"); + return; + } + + if (!SV_SetPlayer ()) + return; + + if (sv_player->v.movetype != MOVETYPE_NOCLIP) + { + sv_player->v.movetype = MOVETYPE_NOCLIP; + SV_ClientPrintf (host_client, PRINT_HIGH, "noclip ON\n"); + } + else + { + sv_player->v.movetype = MOVETYPE_WALK; + SV_ClientPrintf (host_client, PRINT_HIGH, "noclip OFF\n"); + } +} + + +/* +================== +SV_Give_f +================== +*/ +void SV_Give_f (void) +{ + char *t; + int v; + + if (!sv_allow_cheats) + { + Con_Printf ("You must run the server with -cheats to enable this command.\n"); + return; + } + + if (!SV_SetPlayer ()) + return; + + t = Cmd_Argv(2); + v = atoi (Cmd_Argv(3)); + + switch (t[0]) + { + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + sv_player->v.items = (int)sv_player->v.items | IT_SHOTGUN<< (t[0] - '2'); + break; + + case 's': + sv_player->v.ammo_shells = v; + break; + case 'n': + sv_player->v.ammo_nails = v; + break; + case 'r': + sv_player->v.ammo_rockets = v; + break; + case 'h': + sv_player->v.health = v; + break; + case 'c': + sv_player->v.ammo_cells = v; + break; + } +} + + +/* +====================== +SV_Map_f + +handle a +map <mapname> +command from the console or progs. +====================== +*/ +void SV_Map_f (void) +{ + char level[MAX_QPATH]; + char expanded[MAX_QPATH]; + FILE *f; + + if (Cmd_Argc() != 2) + { + Con_Printf ("map <levelname> : continue game on a new level\n"); + return; + } + strcpy (level, Cmd_Argv(1)); + +#if 0 + if (!strcmp (level, "e1m8")) + { // QuakeWorld can't go to e1m8 + SV_BroadcastPrintf (PRINT_HIGH, "can't go to low grav level in QuakeWorld...\n"); + strcpy (level, "e1m5"); + } +#endif + + // check to make sure the level exists + sprintf (expanded, "maps/%s.bsp", level); + COM_FOpenFile (expanded, &f); + if (!f) + { + Con_Printf ("Can't find %s\n", expanded); + return; + } + fclose (f); + + SV_BroadcastCommand ("changing\n"); + SV_SendMessagesToAll (); + + SV_SpawnServer (level); + + SV_BroadcastCommand ("reconnect\n"); +} + + +/* +================== +SV_Kick_f + +Kick a user off of the server +================== +*/ +void SV_Kick_f (void) +{ + int i; + client_t *cl; + int uid; + + uid = atoi(Cmd_Argv(1)); + + for (i = 0, cl = svs.clients; i < MAX_CLIENTS; i++, cl++) + { + if (!cl->state) + continue; + if (cl->userid == uid) + { + SV_BroadcastPrintf (PRINT_HIGH, "%s was kicked\n", cl->name); + // print directly, because the dropped client won't get the + // SV_BroadcastPrintf message + SV_ClientPrintf (cl, PRINT_HIGH, "You were kicked from the game\n"); + SV_DropClient (cl); + return; + } + } + + Con_Printf ("Couldn't find user number %i\n", uid); +} + + +/* +================ +SV_Status_f +================ +*/ +void SV_Status_f (void) +{ + int i, j, l; + client_t *cl; + float cpu, avg, pak; + char *s; + + + cpu = (svs.stats.latched_active+svs.stats.latched_idle); + if (cpu) + cpu = 100*svs.stats.latched_active/cpu; + avg = 1000*svs.stats.latched_active / STATFRAMES; + pak = (float)svs.stats.latched_packets/ STATFRAMES; + + Con_Printf ("net address : %s\n",NET_AdrToString (net_local_adr)); + Con_Printf ("cpu utilization : %3i%%\n",(int)cpu); + Con_Printf ("avg response time: %i ms\n",(int)avg); + Con_Printf ("packets/frame : %5.2f (%d)\n", pak, num_prstr); + +// min fps lat drp + if (sv_redirected != RD_NONE) { + // most remote clients are 40 columns + // 0123456789012345678901234567890123456789 + Con_Printf ("name userid frags\n"); + Con_Printf (" address rate ping drop\n"); + Con_Printf (" ---------------- ---- ---- -----\n"); + for (i=0,cl=svs.clients ; i<MAX_CLIENTS ; i++,cl++) + { + if (!cl->state) + continue; + + Con_Printf ("%-16.16s ", cl->name); + + Con_Printf ("%6i %5i", cl->userid, (int)cl->edict->v.frags); + if (cl->spectator) + Con_Printf(" (s)\n"); + else + Con_Printf("\n"); + + s = NET_BaseAdrToString ( cl->netchan.remote_address); + Con_Printf (" %-16.16s", s); + if (cl->state == cs_connected) + { + Con_Printf ("CONNECTING\n"); + continue; + } + if (cl->state == cs_zombie) + { + Con_Printf ("ZOMBIE\n"); + continue; + } + Con_Printf ("%4i %4i %5.2f\n" + , (int)(1000*cl->netchan.frame_rate) + , (int)SV_CalcPing (cl) + , 100.0*cl->netchan.drop_count / cl->netchan.incoming_sequence); + } + } else { + Con_Printf ("frags userid address name rate ping drop qport\n"); + Con_Printf ("----- ------ --------------- --------------- ---- ---- ----- -----\n"); + for (i=0,cl=svs.clients ; i<MAX_CLIENTS ; i++,cl++) + { + if (!cl->state) + continue; + Con_Printf ("%5i %6i ", (int)cl->edict->v.frags, cl->userid); + + s = NET_BaseAdrToString ( cl->netchan.remote_address); + Con_Printf ("%s", s); + l = 16 - strlen(s); + for (j=0 ; j<l ; j++) + Con_Printf (" "); + + Con_Printf ("%s", cl->name); + l = 16 - strlen(cl->name); + for (j=0 ; j<l ; j++) + Con_Printf (" "); + if (cl->state == cs_connected) + { + Con_Printf ("CONNECTING\n"); + continue; + } + if (cl->state == cs_zombie) + { + Con_Printf ("ZOMBIE\n"); + continue; + } + Con_Printf ("%4i %4i %3.1f %4i" + , (int)(1000*cl->netchan.frame_rate) + , (int)SV_CalcPing (cl) + , 100.0*cl->netchan.drop_count / cl->netchan.incoming_sequence + , cl->netchan.qport); + if (cl->spectator) + Con_Printf(" (s)\n"); + else + Con_Printf("\n"); + + + } + } + Con_Printf ("\n"); +} + +/* +================== +SV_ConSay_f +================== +*/ +void SV_ConSay_f(void) +{ + client_t *client; + int j; + char *p; + char text[1024]; + + if (Cmd_Argc () < 2) + return; + + Q_strcpy (text, "console: "); + p = Cmd_Args(); + + if (*p == '"') + { + p++; + p[Q_strlen(p)-1] = 0; + } + + Q_strcat(text, p); + + for (j = 0, client = svs.clients; j < MAX_CLIENTS; j++, client++) + { + if (client->state != cs_spawned) + continue; + SV_ClientPrintf(client, PRINT_CHAT, "%s\n", text); + } +} + + +/* +================== +SV_Heartbeat_f +================== +*/ +void SV_Heartbeat_f (void) +{ + svs.last_heartbeat = -9999; +} + +void SV_SendServerInfoChange(char *key, char *value) +{ + if (!sv.state) + return; + + MSG_WriteByte (&sv.reliable_datagram, svc_serverinfo); + MSG_WriteString (&sv.reliable_datagram, key); + MSG_WriteString (&sv.reliable_datagram, value); +} + +/* +=========== +SV_Serverinfo_f + + Examine or change the serverinfo string +=========== +*/ +char *CopyString(char *s); +void SV_Serverinfo_f (void) +{ + cvar_t *var; + + if (Cmd_Argc() == 1) + { + Con_Printf ("Server info settings:\n"); + Info_Print (svs.info); + return; + } + + if (Cmd_Argc() != 3) + { + Con_Printf ("usage: serverinfo [ <key> <value> ]\n"); + return; + } + + if (Cmd_Argv(1)[0] == '*') + { + Con_Printf ("Star variables cannot be changed.\n"); + return; + } + Info_SetValueForKey (svs.info, Cmd_Argv(1), Cmd_Argv(2), MAX_SERVERINFO_STRING); + + // if this is a cvar, change it too + var = Cvar_FindVar (Cmd_Argv(1)); + if (var) + { + Z_Free (var->string); // free the old value string + var->string = CopyString (Cmd_Argv(2)); + var->value = Q_atof (var->string); + } + + SV_SendServerInfoChange(Cmd_Argv(1), Cmd_Argv(2)); +} + + +/* +=========== +SV_Serverinfo_f + + Examine or change the serverinfo string +=========== +*/ +char *CopyString(char *s); +void SV_Localinfo_f (void) +{ + if (Cmd_Argc() == 1) + { + Con_Printf ("Local info settings:\n"); + Info_Print (localinfo); + return; + } + + if (Cmd_Argc() != 3) + { + Con_Printf ("usage: localinfo [ <key> <value> ]\n"); + return; + } + + if (Cmd_Argv(1)[0] == '*') + { + Con_Printf ("Star variables cannot be changed.\n"); + return; + } + Info_SetValueForKey (localinfo, Cmd_Argv(1), Cmd_Argv(2), MAX_LOCALINFO_STRING); +} + + +/* +=========== +SV_User_f + +Examine a users info strings +=========== +*/ +void SV_User_f (void) +{ + if (Cmd_Argc() != 2) + { + Con_Printf ("Usage: info <userid>\n"); + return; + } + + if (!SV_SetPlayer ()) + return; + + Info_Print (host_client->userinfo); +} + +/* +================ +SV_Gamedir + +Sets the fake *gamedir to a different directory. +================ +*/ +void SV_Gamedir (void) +{ + char *dir; + + if (Cmd_Argc() == 1) + { + Con_Printf ("Current *gamedir: %s\n", Info_ValueForKey (svs.info, "*gamedir")); + return; + } + + if (Cmd_Argc() != 2) + { + Con_Printf ("Usage: sv_gamedir <newgamedir>\n"); + return; + } + + dir = Cmd_Argv(1); + + if (strstr(dir, "..") || strstr(dir, "/") + || strstr(dir, "\\") || strstr(dir, ":") ) + { + Con_Printf ("*Gamedir should be a single filename, not a path\n"); + return; + } + + Info_SetValueForStarKey (svs.info, "*gamedir", dir, MAX_SERVERINFO_STRING); +} + +/* +================ +SV_Floodport_f + +Sets the gamedir and path to a different directory. +================ +*/ + +void SV_Floodprot_f (void) +{ + int arg1, arg2, arg3; + + if (Cmd_Argc() == 1) + { + if (fp_messages) { + Con_Printf ("Current floodprot settings: \nAfter %d msgs per %d seconds, silence for %d seconds\n", + fp_messages, fp_persecond, fp_secondsdead); + return; + } else + Con_Printf ("No floodprots enabled.\n"); + } + + if (Cmd_Argc() != 4) + { + Con_Printf ("Usage: floodprot <# of messages> <per # of seconds> <seconds to silence>\n"); + Con_Printf ("Use floodprotmsg to set a custom message to say to the flooder.\n"); + return; + } + + arg1 = atoi(Cmd_Argv(1)); + arg2 = atoi(Cmd_Argv(2)); + arg3 = atoi(Cmd_Argv(3)); + + if (arg1<=0 || arg2 <= 0 || arg3<=0) { + Con_Printf ("All values must be positive numbers\n"); + return; + } + + if (arg1 > 10) { + Con_Printf ("Can only track up to 10 messages.\n"); + return; + } + + fp_messages = arg1; + fp_persecond = arg2; + fp_secondsdead = arg3; +} + +void SV_Floodprotmsg_f (void) +{ + if (Cmd_Argc() == 1) { + Con_Printf("Current msg: %s\n", fp_msg); + return; + } else if (Cmd_Argc() != 2) { + Con_Printf("Usage: floodprotmsg \"<message>\"\n"); + return; + } + sprintf(fp_msg, "%s", Cmd_Argv(1)); +} + +/* +================ +SV_Gamedir_f + +Sets the gamedir and path to a different directory. +================ +*/ +char gamedirfile[MAX_OSPATH]; +void SV_Gamedir_f (void) +{ + char *dir; + + if (Cmd_Argc() == 1) + { + Con_Printf ("Current gamedir: %s\n", com_gamedir); + return; + } + + if (Cmd_Argc() != 2) + { + Con_Printf ("Usage: gamedir <newdir>\n"); + return; + } + + dir = Cmd_Argv(1); + + if (strstr(dir, "..") || strstr(dir, "/") + || strstr(dir, "\\") || strstr(dir, ":") ) + { + Con_Printf ("Gamedir should be a single filename, not a path\n"); + return; + } + + COM_Gamedir (dir); + Info_SetValueForStarKey (svs.info, "*gamedir", dir, MAX_SERVERINFO_STRING); +} + +/* +================ +SV_Snap +================ +*/ +void SV_Snap (int uid) +{ + client_t *cl; + char pcxname[80]; + char checkname[MAX_OSPATH]; + int i; + + for (i = 0, cl = svs.clients; i < MAX_CLIENTS; i++, cl++) + { + if (!cl->state) + continue; + if (cl->userid == uid) + break; + } + if (i >= MAX_CLIENTS) { + Con_Printf ("userid not found\n"); + return; + } + + sprintf(pcxname, "%d-00.pcx", uid); + + sprintf(checkname, "%s/snap", gamedirfile); + Sys_mkdir(gamedirfile); + Sys_mkdir(checkname); + + for (i=0 ; i<=99 ; i++) + { + pcxname[strlen(pcxname) - 6] = i/10 + '0'; + pcxname[strlen(pcxname) - 5] = i%10 + '0'; + sprintf (checkname, "%s/snap/%s", gamedirfile, pcxname); + if (Sys_FileTime(checkname) == -1) + break; // file doesn't exist + } + if (i==100) + { + Con_Printf ("Snap: Couldn't create a file, clean some out.\n"); + return; + } + strcpy(cl->uploadfn, checkname); + + memcpy(&cl->snap_from, &net_from, sizeof(net_from)); + if (sv_redirected != RD_NONE) + cl->remote_snap = true; + else + cl->remote_snap = false; + + ClientReliableWrite_Begin (cl, svc_stufftext, 24); + ClientReliableWrite_String (cl, "cmd snap"); + Con_Printf ("Requesting snap from user %d...\n", uid); +} + +/* +================ +SV_Snap_f +================ +*/ +void SV_Snap_f (void) +{ + int uid; + + if (Cmd_Argc() != 2) + { + Con_Printf ("Usage: snap <userid>\n"); + return; + } + + uid = atoi(Cmd_Argv(1)); + + SV_Snap(uid); +} + +/* +================ +SV_Snap +================ +*/ +void SV_SnapAll_f (void) +{ + client_t *cl; + int i; + + for (i = 0, cl = svs.clients; i < MAX_CLIENTS; i++, cl++) + { + if (cl->state < cs_connected || cl->spectator) + continue; + SV_Snap(cl->userid); + } +} + +/* +================== +SV_InitOperatorCommands +================== +*/ +void SV_InitOperatorCommands (void) +{ + if (COM_CheckParm ("-cheats")) + { + sv_allow_cheats = true; + Info_SetValueForStarKey (svs.info, "*cheats", "ON", MAX_SERVERINFO_STRING); + } + + Cmd_AddCommand ("logfile", SV_Logfile_f); + Cmd_AddCommand ("fraglogfile", SV_Fraglogfile_f); + + Cmd_AddCommand ("snap", SV_Snap_f); + Cmd_AddCommand ("snapall", SV_SnapAll_f); + Cmd_AddCommand ("kick", SV_Kick_f); + Cmd_AddCommand ("status", SV_Status_f); + + Cmd_AddCommand ("map", SV_Map_f); + Cmd_AddCommand ("setmaster", SV_SetMaster_f); + + Cmd_AddCommand ("say", SV_ConSay_f); + Cmd_AddCommand ("heartbeat", SV_Heartbeat_f); + Cmd_AddCommand ("quit", SV_Quit_f); + Cmd_AddCommand ("god", SV_God_f); + Cmd_AddCommand ("give", SV_Give_f); + Cmd_AddCommand ("noclip", SV_Noclip_f); + Cmd_AddCommand ("serverinfo", SV_Serverinfo_f); + Cmd_AddCommand ("localinfo", SV_Localinfo_f); + Cmd_AddCommand ("user", SV_User_f); + Cmd_AddCommand ("gamedir", SV_Gamedir_f); + Cmd_AddCommand ("sv_gamedir", SV_Gamedir); + Cmd_AddCommand ("floodprot", SV_Floodprot_f); + Cmd_AddCommand ("floodprotmsg", SV_Floodprotmsg_f); + + cl_warncmd.value = 1; +} diff --git a/QW/server/sv_ents.c b/QW/server/sv_ents.c new file mode 100644 index 0000000..055a9a9 --- /dev/null +++ b/QW/server/sv_ents.c @@ -0,0 +1,517 @@ +/* +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. + +*/ + +#include "qwsvdef.h" + +/* +============================================================================= + +The PVS must include a small area around the client to allow head bobbing +or other small motion on the client side. Otherwise, a bob might cause an +entity that should be visible to not show up, especially when the bob +crosses a waterline. + +============================================================================= +*/ + +int fatbytes; +byte fatpvs[MAX_MAP_LEAFS/8]; + +void SV_AddToFatPVS (vec3_t org, mnode_t *node) +{ + int i; + byte *pvs; + mplane_t *plane; + float d; + + while (1) + { + // if this is a leaf, accumulate the pvs bits + if (node->contents < 0) + { + if (node->contents != CONTENTS_SOLID) + { + pvs = Mod_LeafPVS ( (mleaf_t *)node, sv.worldmodel); + for (i=0 ; i<fatbytes ; i++) + fatpvs[i] |= pvs[i]; + } + return; + } + + plane = node->plane; + d = DotProduct (org, plane->normal) - plane->dist; + if (d > 8) + node = node->children[0]; + else if (d < -8) + node = node->children[1]; + else + { // go down both + SV_AddToFatPVS (org, node->children[0]); + node = node->children[1]; + } + } +} + +/* +============= +SV_FatPVS + +Calculates a PVS that is the inclusive or of all leafs within 8 pixels of the +given point. +============= +*/ +byte *SV_FatPVS (vec3_t org) +{ + fatbytes = (sv.worldmodel->numleafs+31)>>3; + Q_memset (fatpvs, 0, fatbytes); + SV_AddToFatPVS (org, sv.worldmodel->nodes); + return fatpvs; +} + +//============================================================================= + +// because there can be a lot of nails, there is a special +// network protocol for them +#define MAX_NAILS 32 +edict_t *nails[MAX_NAILS]; +int numnails; + +extern int sv_nailmodel, sv_supernailmodel, sv_playermodel; + +qboolean SV_AddNailUpdate (edict_t *ent) +{ + if (ent->v.modelindex != sv_nailmodel + && ent->v.modelindex != sv_supernailmodel) + return false; + if (numnails == MAX_NAILS) + return true; + nails[numnails] = ent; + numnails++; + return true; +} + +void SV_EmitNailUpdate (sizebuf_t *msg) +{ + byte bits[6]; // [48 bits] xyzpy 12 12 12 4 8 + int n, i; + edict_t *ent; + int x, y, z, p, yaw; + + if (!numnails) + return; + + MSG_WriteByte (msg, svc_nails); + MSG_WriteByte (msg, numnails); + + for (n=0 ; n<numnails ; n++) + { + ent = nails[n]; + x = (int)(ent->v.origin[0]+4096)>>1; + y = (int)(ent->v.origin[1]+4096)>>1; + z = (int)(ent->v.origin[2]+4096)>>1; + p = (int)(16*ent->v.angles[0]/360)&15; + yaw = (int)(256*ent->v.angles[1]/360)&255; + + bits[0] = x; + bits[1] = (x>>8) | (y<<4); + bits[2] = (y>>4); + bits[3] = z; + bits[4] = (z>>8) | (p<<4); + bits[5] = yaw; + + for (i=0 ; i<6 ; i++) + MSG_WriteByte (msg, bits[i]); + } +} + +//============================================================================= + + +/* +================== +SV_WriteDelta + +Writes part of a packetentities message. +Can delta from either a baseline or a previous packet_entity +================== +*/ +void SV_WriteDelta (entity_state_t *from, entity_state_t *to, sizebuf_t *msg, qboolean force) +{ + int bits; + int i; + float miss; + +// send an update + bits = 0; + + for (i=0 ; i<3 ; i++) + { + miss = to->origin[i] - from->origin[i]; + if ( miss < -0.1 || miss > 0.1 ) + bits |= U_ORIGIN1<<i; + } + + if ( to->angles[0] != from->angles[0] ) + bits |= U_ANGLE1; + + if ( to->angles[1] != from->angles[1] ) + bits |= U_ANGLE2; + + if ( to->angles[2] != from->angles[2] ) + bits |= U_ANGLE3; + + if ( to->colormap != from->colormap ) + bits |= U_COLORMAP; + + if ( to->skinnum != from->skinnum ) + bits |= U_SKIN; + + if ( to->frame != from->frame ) + bits |= U_FRAME; + + if ( to->effects != from->effects ) + bits |= U_EFFECTS; + + if ( to->modelindex != from->modelindex ) + bits |= U_MODEL; + + if (bits & 511) + bits |= U_MOREBITS; + + if (to->flags & U_SOLID) + bits |= U_SOLID; + + // + // write the message + // + if (!to->number) + SV_Error ("Unset entity number"); + if (to->number >= 512) + SV_Error ("Entity number >= 512"); + + if (!bits && !force) + return; // nothing to send! + i = to->number | (bits&~511); + if (i & U_REMOVE) + Sys_Error ("U_REMOVE"); + MSG_WriteShort (msg, i); + + if (bits & U_MOREBITS) + MSG_WriteByte (msg, bits&255); + if (bits & U_MODEL) + MSG_WriteByte (msg, to->modelindex); + if (bits & U_FRAME) + MSG_WriteByte (msg, to->frame); + if (bits & U_COLORMAP) + MSG_WriteByte (msg, to->colormap); + if (bits & U_SKIN) + MSG_WriteByte (msg, to->skinnum); + if (bits & U_EFFECTS) + MSG_WriteByte (msg, to->effects); + if (bits & U_ORIGIN1) + MSG_WriteCoord (msg, to->origin[0]); + if (bits & U_ANGLE1) + MSG_WriteAngle(msg, to->angles[0]); + if (bits & U_ORIGIN2) + MSG_WriteCoord (msg, to->origin[1]); + if (bits & U_ANGLE2) + MSG_WriteAngle(msg, to->angles[1]); + if (bits & U_ORIGIN3) + MSG_WriteCoord (msg, to->origin[2]); + if (bits & U_ANGLE3) + MSG_WriteAngle(msg, to->angles[2]); +} + +/* +============= +SV_EmitPacketEntities + +Writes a delta update of a packet_entities_t to the message. + +============= +*/ +void SV_EmitPacketEntities (client_t *client, packet_entities_t *to, sizebuf_t *msg) +{ + edict_t *ent; + client_frame_t *fromframe; + packet_entities_t *from; + int oldindex, newindex; + int oldnum, newnum; + int oldmax; + + // this is the frame that we are going to delta update from + if (client->delta_sequence != -1) + { + fromframe = &client->frames[client->delta_sequence & UPDATE_MASK]; + from = &fromframe->entities; + oldmax = from->num_entities; + + MSG_WriteByte (msg, svc_deltapacketentities); + MSG_WriteByte (msg, client->delta_sequence); + } + else + { + oldmax = 0; // no delta update + from = NULL; + + MSG_WriteByte (msg, svc_packetentities); + } + + newindex = 0; + oldindex = 0; +//Con_Printf ("---%i to %i ----\n", client->delta_sequence & UPDATE_MASK +// , client->netchan.outgoing_sequence & UPDATE_MASK); + while (newindex < to->num_entities || oldindex < oldmax) + { + newnum = newindex >= to->num_entities ? 9999 : to->entities[newindex].number; + oldnum = oldindex >= oldmax ? 9999 : from->entities[oldindex].number; + + if (newnum == oldnum) + { // delta update from old position +//Con_Printf ("delta %i\n", newnum); + SV_WriteDelta (&from->entities[oldindex], &to->entities[newindex], msg, false); + oldindex++; + newindex++; + continue; + } + + if (newnum < oldnum) + { // this is a new entity, send it from the baseline + ent = EDICT_NUM(newnum); +//Con_Printf ("baseline %i\n", newnum); + SV_WriteDelta (&ent->baseline, &to->entities[newindex], msg, true); + newindex++; + continue; + } + + if (newnum > oldnum) + { // the old entity isn't present in the new message +//Con_Printf ("remove %i\n", oldnum); + MSG_WriteShort (msg, oldnum | U_REMOVE); + oldindex++; + continue; + } + } + + MSG_WriteShort (msg, 0); // end of packetentities +} + +/* +============= +SV_WritePlayersToClient + +============= +*/ +void SV_WritePlayersToClient (client_t *client, edict_t *clent, byte *pvs, sizebuf_t *msg) +{ + int i, j; + client_t *cl; + edict_t *ent; + int msec; + usercmd_t cmd; + int pflags; + + for (j=0,cl=svs.clients ; j<MAX_CLIENTS ; j++,cl++) + { + if (cl->state != cs_spawned) + continue; + + ent = cl->edict; + + // ZOID visibility tracking + if (ent != clent && + !(client->spec_track && client->spec_track - 1 == j)) + { + if (cl->spectator) + continue; + + // ignore if not touching a PV leaf + for (i=0 ; i < ent->num_leafs ; i++) + if (pvs[ent->leafnums[i] >> 3] & (1 << (ent->leafnums[i]&7) )) + break; + if (i == ent->num_leafs) + continue; // not visible + } + + pflags = PF_MSEC | PF_COMMAND; + + if (ent->v.modelindex != sv_playermodel) + pflags |= PF_MODEL; + for (i=0 ; i<3 ; i++) + if (ent->v.velocity[i]) + pflags |= PF_VELOCITY1<<i; + if (ent->v.effects) + pflags |= PF_EFFECTS; + if (ent->v.skin) + pflags |= PF_SKINNUM; + if (ent->v.health <= 0) + pflags |= PF_DEAD; + if (ent->v.mins[2] != -24) + pflags |= PF_GIB; + + if (cl->spectator) + { // only sent origin and velocity to spectators + pflags &= PF_VELOCITY1 | PF_VELOCITY2 | PF_VELOCITY3; + } + else if (ent == clent) + { // don't send a lot of data on personal entity + pflags &= ~(PF_MSEC|PF_COMMAND); + if (ent->v.weaponframe) + pflags |= PF_WEAPONFRAME; + } + + if (client->spec_track && client->spec_track - 1 == j && + ent->v.weaponframe) + pflags |= PF_WEAPONFRAME; + + MSG_WriteByte (msg, svc_playerinfo); + MSG_WriteByte (msg, j); + MSG_WriteShort (msg, pflags); + + for (i=0 ; i<3 ; i++) + MSG_WriteCoord (msg, ent->v.origin[i]); + + MSG_WriteByte (msg, ent->v.frame); + + if (pflags & PF_MSEC) + { + msec = 1000*(sv.time - cl->localtime); + if (msec > 255) + msec = 255; + MSG_WriteByte (msg, msec); + } + + if (pflags & PF_COMMAND) + { + cmd = cl->lastcmd; + + if (ent->v.health <= 0) + { // don't show the corpse looking around... + cmd.angles[0] = 0; + cmd.angles[1] = ent->v.angles[1]; + cmd.angles[0] = 0; + } + + cmd.buttons = 0; // never send buttons + cmd.impulse = 0; // never send impulses + + MSG_WriteDeltaUsercmd (msg, &nullcmd, &cmd); + } + + for (i=0 ; i<3 ; i++) + if (pflags & (PF_VELOCITY1<<i) ) + MSG_WriteShort (msg, ent->v.velocity[i]); + + if (pflags & PF_MODEL) + MSG_WriteByte (msg, ent->v.modelindex); + + if (pflags & PF_SKINNUM) + MSG_WriteByte (msg, ent->v.skin); + + if (pflags & PF_EFFECTS) + MSG_WriteByte (msg, ent->v.effects); + + if (pflags & PF_WEAPONFRAME) + MSG_WriteByte (msg, ent->v.weaponframe); + } +} + + +/* +============= +SV_WriteEntitiesToClient + +Encodes the current state of the world as +a svc_packetentities messages and possibly +a svc_nails message and +svc_playerinfo messages +============= +*/ +void SV_WriteEntitiesToClient (client_t *client, sizebuf_t *msg) +{ + int e, i; + byte *pvs; + vec3_t org; + edict_t *ent; + packet_entities_t *pack; + edict_t *clent; + client_frame_t *frame; + entity_state_t *state; + + // this is the frame we are creating + frame = &client->frames[client->netchan.incoming_sequence & UPDATE_MASK]; + + // find the client's PVS + clent = client->edict; + VectorAdd (clent->v.origin, clent->v.view_ofs, org); + pvs = SV_FatPVS (org); + + // send over the players in the PVS + SV_WritePlayersToClient (client, clent, pvs, msg); + + // put other visible entities into either a packet_entities or a nails message + pack = &frame->entities; + pack->num_entities = 0; + + numnails = 0; + + for (e=MAX_CLIENTS+1, ent=EDICT_NUM(e) ; e<sv.num_edicts ; e++, ent = NEXT_EDICT(ent)) + { + // ignore ents without visible models + if (!ent->v.modelindex || !*PR_GetString(ent->v.model)) + continue; + + // ignore if not touching a PV leaf + for (i=0 ; i < ent->num_leafs ; i++) + if (pvs[ent->leafnums[i] >> 3] & (1 << (ent->leafnums[i]&7) )) + break; + + if (i == ent->num_leafs) + continue; // not visible + + if (SV_AddNailUpdate (ent)) + continue; // added to the special update list + + // add to the packetentities + if (pack->num_entities == MAX_PACKET_ENTITIES) + continue; // all full + + state = &pack->entities[pack->num_entities]; + pack->num_entities++; + + state->number = e; + state->flags = 0; + VectorCopy (ent->v.origin, state->origin); + VectorCopy (ent->v.angles, state->angles); + state->modelindex = ent->v.modelindex; + state->frame = ent->v.frame; + state->colormap = ent->v.colormap; + state->skinnum = ent->v.skin; + state->effects = ent->v.effects; + } + + // encode the packet entities as a delta from the + // last packetentities acknowledged by the client + + SV_EmitPacketEntities (client, pack, msg); + + // now add the specialized nail update + SV_EmitNailUpdate (msg); +} diff --git a/QW/server/sv_init.c b/QW/server/sv_init.c new file mode 100644 index 0000000..cd58d23 --- /dev/null +++ b/QW/server/sv_init.c @@ -0,0 +1,411 @@ +/* +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. + +*/ + +#include "qwsvdef.h" + +server_static_t svs; // persistant server info +server_t sv; // local server + +char localmodels[MAX_MODELS][5]; // inline model names for precache + +char localinfo[MAX_LOCALINFO_STRING+1]; // local game info + +/* +================ +SV_ModelIndex + +================ +*/ +int SV_ModelIndex (char *name) +{ + int i; + + if (!name || !name[0]) + return 0; + + for (i=0 ; i<MAX_MODELS && sv.model_precache[i] ; i++) + if (!strcmp(sv.model_precache[i], name)) + return i; + if (i==MAX_MODELS || !sv.model_precache[i]) + SV_Error ("SV_ModelIndex: model %s not precached", name); + return i; +} + +/* +================ +SV_FlushSignon + +Moves to the next signon buffer if needed +================ +*/ +void SV_FlushSignon (void) +{ + if (sv.signon.cursize < sv.signon.maxsize - 512) + return; + + if (sv.num_signon_buffers == MAX_SIGNON_BUFFERS-1) + SV_Error ("sv.num_signon_buffers == MAX_SIGNON_BUFFERS-1"); + + sv.signon_buffer_size[sv.num_signon_buffers-1] = sv.signon.cursize; + sv.signon.data = sv.signon_buffers[sv.num_signon_buffers]; + sv.num_signon_buffers++; + sv.signon.cursize = 0; +} + +/* +================ +SV_CreateBaseline + +Entity baselines are used to compress the update messages +to the clients -- only the fields that differ from the +baseline will be transmitted +================ +*/ +void SV_CreateBaseline (void) +{ + int i; + edict_t *svent; + int entnum; + + for (entnum = 0; entnum < sv.num_edicts ; entnum++) + { + svent = EDICT_NUM(entnum); + if (svent->free) + continue; + // create baselines for all player slots, + // and any other edict that has a visible model + if (entnum > MAX_CLIENTS && !svent->v.modelindex) + continue; + + // + // create entity baseline + // + VectorCopy (svent->v.origin, svent->baseline.origin); + VectorCopy (svent->v.angles, svent->baseline.angles); + svent->baseline.frame = svent->v.frame; + svent->baseline.skinnum = svent->v.skin; + if (entnum > 0 && entnum <= MAX_CLIENTS) + { + svent->baseline.colormap = entnum; + svent->baseline.modelindex = SV_ModelIndex("progs/player.mdl"); + } + else + { + svent->baseline.colormap = 0; + svent->baseline.modelindex = + SV_ModelIndex(PR_GetString(svent->v.model)); + } + + // + // flush the signon message out to a seperate buffer if + // nearly full + // + SV_FlushSignon (); + + // + // add to the message + // + MSG_WriteByte (&sv.signon,svc_spawnbaseline); + MSG_WriteShort (&sv.signon,entnum); + + MSG_WriteByte (&sv.signon, svent->baseline.modelindex); + MSG_WriteByte (&sv.signon, svent->baseline.frame); + MSG_WriteByte (&sv.signon, svent->baseline.colormap); + MSG_WriteByte (&sv.signon, svent->baseline.skinnum); + for (i=0 ; i<3 ; i++) + { + MSG_WriteCoord(&sv.signon, svent->baseline.origin[i]); + MSG_WriteAngle(&sv.signon, svent->baseline.angles[i]); + } + } +} + + +/* +================ +SV_SaveSpawnparms + +Grabs the current state of the progs serverinfo flags +and each client for saving across the +transition to another level +================ +*/ +void SV_SaveSpawnparms (void) +{ + int i, j; + + if (!sv.state) + return; // no progs loaded yet + + // serverflags is the only game related thing maintained + svs.serverflags = pr_global_struct->serverflags; + + for (i=0, host_client = svs.clients ; i<MAX_CLIENTS ; i++, host_client++) + { + if (host_client->state != cs_spawned) + continue; + + // needs to reconnect + host_client->state = cs_connected; + + // call the progs to get default spawn parms for the new client + pr_global_struct->self = EDICT_TO_PROG(host_client->edict); + PR_ExecuteProgram (pr_global_struct->SetChangeParms); + for (j=0 ; j<NUM_SPAWN_PARMS ; j++) + host_client->spawn_parms[j] = (&pr_global_struct->parm1)[j]; + } +} + +/* +================ +SV_CalcPHS + +Expands the PVS and calculates the PHS +(Potentially Hearable Set) +================ +*/ +void SV_CalcPHS (void) +{ + int rowbytes, rowwords; + int i, j, k, l, index, num; + int bitbyte; + unsigned *dest, *src; + byte *scan; + int count, vcount; + + Con_Printf ("Building PHS...\n"); + + num = sv.worldmodel->numleafs; + rowwords = (num+31)>>5; + rowbytes = rowwords*4; + + sv.pvs = Hunk_Alloc (rowbytes*num); + scan = sv.pvs; + vcount = 0; + for (i=0 ; i<num ; i++, scan+=rowbytes) + { + memcpy (scan, Mod_LeafPVS(sv.worldmodel->leafs+i, sv.worldmodel), + rowbytes); + if (i == 0) + continue; + for (j=0 ; j<num ; j++) + { + if ( scan[j>>3] & (1<<(j&7)) ) + { + vcount++; + } + } + } + + + sv.phs = Hunk_Alloc (rowbytes*num); + count = 0; + scan = sv.pvs; + dest = (unsigned *)sv.phs; + for (i=0 ; i<num ; i++, dest += rowwords, scan += rowbytes) + { + memcpy (dest, scan, rowbytes); + for (j=0 ; j<rowbytes ; j++) + { + bitbyte = scan[j]; + if (!bitbyte) + continue; + for (k=0 ; k<8 ; k++) + { + if (! (bitbyte & (1<<k)) ) + continue; + // or this pvs row into the phs + // +1 because pvs is 1 based + index = ((j<<3)+k+1); + if (index >= num) + continue; + src = (unsigned *)sv.pvs + index*rowwords; + for (l=0 ; l<rowwords ; l++) + dest[l] |= src[l]; + } + } + + if (i == 0) + continue; + for (j=0 ; j<num ; j++) + if ( ((byte *)dest)[j>>3] & (1<<(j&7)) ) + count++; + } + + Con_Printf ("Average leafs visible / hearable / total: %i / %i / %i\n" + , vcount/num, count/num, num); +} + +unsigned SV_CheckModel(char *mdl) +{ + byte stackbuf[1024]; // avoid dirtying the cache heap + byte *buf; + unsigned short crc; +// int len; + + buf = (byte *)COM_LoadStackFile (mdl, stackbuf, sizeof(stackbuf)); + crc = CRC_Block(buf, com_filesize); +// for (len = com_filesize; len; len--, buf++) +// CRC_ProcessByte(&crc, *buf); + + return crc; +} + +/* +================ +SV_SpawnServer + +Change the server to a new map, taking all connected +clients along with it. + +This is only called from the SV_Map_f() function. +================ +*/ +void SV_SpawnServer (char *server) +{ + edict_t *ent; + int i; + + Con_DPrintf ("SpawnServer: %s\n",server); + + SV_SaveSpawnparms (); + + svs.spawncount++; // any partially connected client will be + // restarted + + sv.state = ss_dead; + + Mod_ClearAll (); + Hunk_FreeToLowMark (host_hunklevel); + + // wipe the entire per-level structure + memset (&sv, 0, sizeof(sv)); + + sv.datagram.maxsize = sizeof(sv.datagram_buf); + sv.datagram.data = sv.datagram_buf; + sv.datagram.allowoverflow = true; + + sv.reliable_datagram.maxsize = sizeof(sv.reliable_datagram_buf); + sv.reliable_datagram.data = sv.reliable_datagram_buf; + + sv.multicast.maxsize = sizeof(sv.multicast_buf); + sv.multicast.data = sv.multicast_buf; + + sv.master.maxsize = sizeof(sv.master_buf); + sv.master.data = sv.master_buf; + + sv.signon.maxsize = sizeof(sv.signon_buffers[0]); + sv.signon.data = sv.signon_buffers[0]; + sv.num_signon_buffers = 1; + + strcpy (sv.name, server); + + // load progs to get entity field count + // which determines how big each edict is + PR_LoadProgs (); + + // allocate edicts + sv.edicts = Hunk_AllocName (MAX_EDICTS*pr_edict_size, "edicts"); + + // leave slots at start for clients only + sv.num_edicts = MAX_CLIENTS+1; + for (i=0 ; i<MAX_CLIENTS ; i++) + { + ent = EDICT_NUM(i+1); + svs.clients[i].edict = ent; +//ZOID - make sure we update frags right + svs.clients[i].old_frags = 0; + } + + sv.time = 1.0; + + strcpy (sv.name, server); + sprintf (sv.modelname,"maps/%s.bsp", server); + sv.worldmodel = Mod_ForName (sv.modelname, true); + SV_CalcPHS (); + + // + // clear physics interaction links + // + SV_ClearWorld (); + + sv.sound_precache[0] = pr_strings; + + sv.model_precache[0] = pr_strings; + sv.model_precache[1] = sv.modelname; + sv.models[1] = sv.worldmodel; + for (i=1 ; i<sv.worldmodel->numsubmodels ; i++) + { + sv.model_precache[1+i] = localmodels[i]; + sv.models[i+1] = Mod_ForName (localmodels[i], false); + } + + //check player/eyes models for hacks + sv.model_player_checksum = SV_CheckModel("progs/player.mdl"); + sv.eyes_player_checksum = SV_CheckModel("progs/eyes.mdl"); + + // + // spawn the rest of the entities on the map + // + + // precache and static commands can be issued during + // map initialization + sv.state = ss_loading; + + ent = EDICT_NUM(0); + ent->free = false; + ent->v.model = PR_SetString(sv.worldmodel->name); + ent->v.modelindex = 1; // world model + ent->v.solid = SOLID_BSP; + ent->v.movetype = MOVETYPE_PUSH; + + pr_global_struct->mapname = PR_SetString(sv.name); + // serverflags are for cross level information (sigils) + pr_global_struct->serverflags = svs.serverflags; + + // run the frame start qc function to let progs check cvars + SV_ProgStartFrame (); + + // load and spawn all other entities + ED_LoadFromFile (sv.worldmodel->entities); + + // look up some model indexes for specialized message compression + SV_FindModelNumbers (); + + // all spawning is completed, any further precache statements + // or prog writes to the signon message are errors + sv.state = ss_active; + + // run two frames to allow everything to settle + host_frametime = 0.1; + SV_Physics (); + SV_Physics (); + + // save movement vars + SV_SetMoveVars(); + + // create a baseline for more efficient communications + SV_CreateBaseline (); + sv.signon_buffer_size[sv.num_signon_buffers-1] = sv.signon.cursize; + + Info_SetValueForKey (svs.info, "map", sv.name, MAX_SERVERINFO_STRING); + Con_DPrintf ("Server spawned.\n"); +} + diff --git a/QW/server/sv_main.c b/QW/server/sv_main.c new file mode 100644 index 0000000..275aa54 --- /dev/null +++ b/QW/server/sv_main.c @@ -0,0 +1,1658 @@ +/* +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. + +*/ + +#include "qwsvdef.h" + +quakeparms_t host_parms; + +qboolean host_initialized; // true if into command execution (compatability) + +double host_frametime; +double realtime; // without any filtering or bounding + +int host_hunklevel; + +netadr_t master_adr[MAX_MASTERS]; // address of group servers + +client_t *host_client; // current client + +cvar_t sv_mintic = {"sv_mintic","0.03"}; // bound the size of the +cvar_t sv_maxtic = {"sv_maxtic","0.1"}; // physics time tic + +cvar_t developer = {"developer","0"}; // show extra messages + +cvar_t timeout = {"timeout","65"}; // seconds without any message +cvar_t zombietime = {"zombietime", "2"}; // seconds to sink messages + // after disconnect + +cvar_t rcon_password = {"rcon_password", ""}; // password for remote server commands +cvar_t password = {"password", ""}; // password for entering the game +cvar_t spectator_password = {"spectator_password", ""}; // password for entering as a sepctator + +cvar_t allow_download = {"allow_download", "1"}; +cvar_t allow_download_skins = {"allow_download_skins", "1"}; +cvar_t allow_download_models = {"allow_download_models", "1"}; +cvar_t allow_download_sounds = {"allow_download_sounds", "1"}; +cvar_t allow_download_maps = {"allow_download_maps", "1"}; + +cvar_t sv_highchars = {"sv_highchars", "1"}; + +cvar_t sv_phs = {"sv_phs", "1"}; + +cvar_t pausable = {"pausable", "1"}; + + +// +// game rules mirrored in svs.info +// +cvar_t fraglimit = {"fraglimit","0",false,true}; +cvar_t timelimit = {"timelimit","0",false,true}; +cvar_t teamplay = {"teamplay","0",false,true}; +cvar_t samelevel = {"samelevel","0", false, true}; +cvar_t maxclients = {"maxclients","8", false, true}; +cvar_t maxspectators = {"maxspectators","8", false, true}; +cvar_t deathmatch = {"deathmatch","1", false, true}; // 0, 1, or 2 +cvar_t spawn = {"spawn","0", false, true}; +cvar_t watervis = {"watervis", "0", false, true}; + +cvar_t hostname = {"hostname","unnamed", false, true}; + +FILE *sv_logfile; +FILE *sv_fraglogfile; + +void SV_AcceptClient (netadr_t adr, int userid, char *userinfo); +void Master_Shutdown (void); + +//============================================================================ + +qboolean ServerPaused(void) +{ + return sv.paused; +} + +/* +================ +SV_Shutdown + +Quake calls this before calling Sys_Quit or Sys_Error +================ +*/ +void SV_Shutdown (void) +{ + Master_Shutdown (); + if (sv_logfile) + { + fclose (sv_logfile); + sv_logfile = NULL; + } + if (sv_fraglogfile) + { + fclose (sv_fraglogfile); + sv_logfile = NULL; + } + NET_Shutdown (); +} + +/* +================ +SV_Error + +Sends a datagram to all the clients informing them of the server crash, +then exits +================ +*/ +void SV_Error (char *error, ...) +{ + va_list argptr; + static char string[1024]; + static qboolean inerror = false; + + if (inerror) + Sys_Error ("SV_Error: recursively entered (%s)", string); + + inerror = true; + + va_start (argptr,error); + vsprintf (string,error,argptr); + va_end (argptr); + + Con_Printf ("SV_Error: %s\n",string); + + SV_FinalMessage (va("server crashed: %s\n", string)); + + SV_Shutdown (); + + Sys_Error ("SV_Error: %s\n",string); +} + +/* +================== +SV_FinalMessage + +Used by SV_Error and SV_Quit_f to send a final message to all connected +clients before the server goes down. The messages are sent immediately, +not just stuck on the outgoing message list, because the server is going +to totally exit after returning from this function. +================== +*/ +void SV_FinalMessage (char *message) +{ + int i; + client_t *cl; + + SZ_Clear (&net_message); + MSG_WriteByte (&net_message, svc_print); + MSG_WriteByte (&net_message, PRINT_HIGH); + MSG_WriteString (&net_message, message); + MSG_WriteByte (&net_message, svc_disconnect); + + for (i=0, cl = svs.clients ; i<MAX_CLIENTS ; i++, cl++) + if (cl->state >= cs_spawned) + Netchan_Transmit (&cl->netchan, net_message.cursize + , net_message.data); +} + + + +/* +===================== +SV_DropClient + +Called when the player is totally leaving the server, either willingly +or unwillingly. This is NOT called if the entire server is quiting +or crashing. +===================== +*/ +void SV_DropClient (client_t *drop) +{ + // add the disconnect + MSG_WriteByte (&drop->netchan.message, svc_disconnect); + + if (drop->state == cs_spawned) + if (!drop->spectator) + { + // 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(drop->edict); + PR_ExecuteProgram (pr_global_struct->ClientDisconnect); + } + else if (SpectatorDisconnect) + { + // 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(drop->edict); + PR_ExecuteProgram (SpectatorDisconnect); + } + + if (drop->spectator) + Con_Printf ("Spectator %s removed\n",drop->name); + else + Con_Printf ("Client %s removed\n",drop->name); + + if (drop->download) + { + fclose (drop->download); + drop->download = NULL; + } + if (drop->upload) + { + fclose (drop->upload); + drop->upload = NULL; + } + *drop->uploadfn = 0; + + drop->state = cs_zombie; // become free in a few seconds + drop->connection_started = realtime; // for zombie timeout + + drop->old_frags = 0; + drop->edict->v.frags = 0; + drop->name[0] = 0; + memset (drop->userinfo, 0, sizeof(drop->userinfo)); + +// send notification to all remaining clients + SV_FullClientUpdate (drop, &sv.reliable_datagram); +} + + +//==================================================================== + +/* +=================== +SV_CalcPing + +=================== +*/ +int SV_CalcPing (client_t *cl) +{ + float ping; + int i; + int count; + register client_frame_t *frame; + + ping = 0; + count = 0; + for (frame = cl->frames, i=0 ; i<UPDATE_BACKUP ; i++, frame++) + { + if (frame->ping_time > 0) + { + ping += frame->ping_time; + count++; + } + } + if (!count) + return 9999; + ping /= count; + + return ping*1000; +} + +/* +=================== +SV_FullClientUpdate + +Writes all update values to a sizebuf +=================== +*/ +void SV_FullClientUpdate (client_t *client, sizebuf_t *buf) +{ + int i; + char info[MAX_INFO_STRING]; + + i = client - svs.clients; + +//Sys_Printf("SV_FullClientUpdate: Updated frags for client %d\n", i); + + MSG_WriteByte (buf, svc_updatefrags); + MSG_WriteByte (buf, i); + MSG_WriteShort (buf, client->old_frags); + + MSG_WriteByte (buf, svc_updateping); + MSG_WriteByte (buf, i); + MSG_WriteShort (buf, SV_CalcPing (client)); + + MSG_WriteByte (buf, svc_updatepl); + MSG_WriteByte (buf, i); + MSG_WriteByte (buf, client->lossage); + + MSG_WriteByte (buf, svc_updateentertime); + MSG_WriteByte (buf, i); + MSG_WriteFloat (buf, realtime - client->connection_started); + + strcpy (info, client->userinfo); + Info_RemovePrefixedKeys (info, '_'); // server passwords, etc + + MSG_WriteByte (buf, svc_updateuserinfo); + MSG_WriteByte (buf, i); + MSG_WriteLong (buf, client->userid); + MSG_WriteString (buf, info); +} + +/* +=================== +SV_FullClientUpdateToClient + +Writes all update values to a client's reliable stream +=================== +*/ +void SV_FullClientUpdateToClient (client_t *client, client_t *cl) +{ + ClientReliableCheckBlock(cl, 24 + strlen(client->userinfo)); + if (cl->num_backbuf) { + SV_FullClientUpdate (client, &cl->backbuf); + ClientReliable_FinishWrite(cl); + } else + SV_FullClientUpdate (client, &cl->netchan.message); +} + + +/* +============================================================================== + +CONNECTIONLESS COMMANDS + +============================================================================== +*/ + +/* +================ +SVC_Status + +Responds with all the info that qplug or qspy can see +This message can be up to around 5k with worst case string lengths. +================ +*/ +void SVC_Status (void) +{ + int i; + client_t *cl; + int ping; + int top, bottom; + + Cmd_TokenizeString ("status"); + SV_BeginRedirect (RD_PACKET); + Con_Printf ("%s\n", svs.info); + for (i=0 ; i<MAX_CLIENTS ; i++) + { + cl = &svs.clients[i]; + if ((cl->state == cs_connected || cl->state == cs_spawned ) && !cl->spectator) + { + top = atoi(Info_ValueForKey (cl->userinfo, "topcolor")); + bottom = atoi(Info_ValueForKey (cl->userinfo, "bottomcolor")); + top = (top < 0) ? 0 : ((top > 13) ? 13 : top); + bottom = (bottom < 0) ? 0 : ((bottom > 13) ? 13 : bottom); + ping = SV_CalcPing (cl); + Con_Printf ("%i %i %i %i \"%s\" \"%s\" %i %i\n", cl->userid, + cl->old_frags, (int)(realtime - cl->connection_started)/60, + ping, cl->name, Info_ValueForKey (cl->userinfo, "skin"), top, bottom); + } + } + SV_EndRedirect (); +} + +/* +=================== +SV_CheckLog + +=================== +*/ +#define LOG_HIGHWATER 4096 +#define LOG_FLUSH 10*60 +void SV_CheckLog (void) +{ + sizebuf_t *sz; + + sz = &svs.log[svs.logsequence&1]; + + // bump sequence if allmost full, or ten minutes have passed and + // there is something still sitting there + if (sz->cursize > LOG_HIGHWATER + || (realtime - svs.logtime > LOG_FLUSH && sz->cursize) ) + { + // swap buffers and bump sequence + svs.logtime = realtime; + svs.logsequence++; + sz = &svs.log[svs.logsequence&1]; + sz->cursize = 0; + Con_Printf ("beginning fraglog sequence %i\n", svs.logsequence); + } + +} + +/* +================ +SVC_Log + +Responds with all the logged frags for ranking programs. +If a sequence number is passed as a parameter and it is +the same as the current sequence, an A2A_NACK will be returned +instead of the data. +================ +*/ +void SVC_Log (void) +{ + int seq; + char data[MAX_DATAGRAM+64]; + + if (Cmd_Argc() == 2) + seq = atoi(Cmd_Argv(1)); + else + seq = -1; + + if (seq == svs.logsequence-1 || !sv_fraglogfile) + { // they allready have this data, or we aren't logging frags + data[0] = A2A_NACK; + NET_SendPacket (1, data, net_from); + return; + } + + Con_DPrintf ("sending log %i to %s\n", svs.logsequence-1, NET_AdrToString(net_from)); + + sprintf (data, "stdlog %i\n", svs.logsequence-1); + strcat (data, (char *)svs.log_buf[((svs.logsequence-1)&1)]); + + NET_SendPacket (strlen(data)+1, data, net_from); +} + +/* +================ +SVC_Ping + +Just responds with an acknowledgement +================ +*/ +void SVC_Ping (void) +{ + char data; + + data = A2A_ACK; + + NET_SendPacket (1, &data, net_from); +} + +/* +================= +SVC_GetChallenge + +Returns a challenge number that can be used +in a subsequent client_connect command. +We do this to prevent denial of service attacks that +flood the server with invalid connection IPs. With a +challenge, they must give a valid IP address. +================= +*/ +void SVC_GetChallenge (void) +{ + int i; + int oldest; + int oldestTime; + + oldest = 0; + oldestTime = 0x7fffffff; + + // see if we already have a challenge for this ip + for (i = 0 ; i < MAX_CHALLENGES ; i++) + { + if (NET_CompareBaseAdr (net_from, svs.challenges[i].adr)) + break; + if (svs.challenges[i].time < oldestTime) + { + oldestTime = svs.challenges[i].time; + oldest = i; + } + } + + if (i == MAX_CHALLENGES) + { + // overwrite the oldest + svs.challenges[oldest].challenge = (rand() << 16) ^ rand(); + svs.challenges[oldest].adr = net_from; + svs.challenges[oldest].time = realtime; + i = oldest; + } + + // send it back + Netchan_OutOfBandPrint (net_from, "%c%i", S2C_CHALLENGE, + svs.challenges[i].challenge); +} + +/* +================== +SVC_DirectConnect + +A connection request that did not come from the master +================== +*/ +void SVC_DirectConnect (void) +{ + char userinfo[1024]; + static int userid; + netadr_t adr; + int i; + client_t *cl, *newcl; + client_t temp; + edict_t *ent; + int edictnum; + char *s; + int clients, spectators; + qboolean spectator; + int qport; + int version; + int challenge; + + version = atoi(Cmd_Argv(1)); + if (version != PROTOCOL_VERSION) + { + Netchan_OutOfBandPrint (net_from, "%c\nServer is version %4.2f.\n", A2C_PRINT, VERSION); + Con_Printf ("* rejected connect from version %i\n", version); + return; + } + + qport = atoi(Cmd_Argv(2)); + + challenge = atoi(Cmd_Argv(3)); + + // note an extra byte is needed to replace spectator key + strncpy (userinfo, Cmd_Argv(4), sizeof(userinfo)-2); + userinfo[sizeof(userinfo) - 2] = 0; + + // see if the challenge is valid + for (i=0 ; i<MAX_CHALLENGES ; i++) + { + if (NET_CompareBaseAdr (net_from, svs.challenges[i].adr)) + { + if (challenge == svs.challenges[i].challenge) + break; // good + Netchan_OutOfBandPrint (net_from, "%c\nBad challenge.\n", A2C_PRINT); + return; + } + } + if (i == MAX_CHALLENGES) + { + Netchan_OutOfBandPrint (net_from, "%c\nNo challenge for address.\n", A2C_PRINT); + return; + } + + // check for password or spectator_password + s = Info_ValueForKey (userinfo, "spectator"); + if (s[0] && strcmp(s, "0")) + { + if (spectator_password.string[0] && + stricmp(spectator_password.string, "none") && + strcmp(spectator_password.string, s) ) + { // failed + Con_Printf ("%s:spectator password failed\n", NET_AdrToString (net_from)); + Netchan_OutOfBandPrint (net_from, "%c\nrequires a spectator password\n\n", A2C_PRINT); + return; + } + Info_RemoveKey (userinfo, "spectator"); // remove passwd + Info_SetValueForStarKey (userinfo, "*spectator", "1", MAX_INFO_STRING); + spectator = true; + } + else + { + s = Info_ValueForKey (userinfo, "password"); + if (password.string[0] && + stricmp(password.string, "none") && + strcmp(password.string, s) ) + { + Con_Printf ("%s:password failed\n", NET_AdrToString (net_from)); + Netchan_OutOfBandPrint (net_from, "%c\nserver requires a password\n\n", A2C_PRINT); + return; + } + spectator = false; + Info_RemoveKey (userinfo, "password"); // remove passwd + } + + adr = net_from; + userid++; // so every client gets a unique id + + newcl = &temp; + memset (newcl, 0, sizeof(client_t)); + + newcl->userid = userid; + + // works properly + if (!sv_highchars.value) { + byte *p, *q; + + for (p = (byte *)newcl->userinfo, q = (byte *)userinfo; + *q && p < (byte *)newcl->userinfo + sizeof(newcl->userinfo)-1; q++) + if (*q > 31 && *q <= 127) + *p++ = *q; + } else + strncpy (newcl->userinfo, userinfo, sizeof(newcl->userinfo)-1); + + // if there is allready a slot for this ip, drop it + for (i=0,cl=svs.clients ; i<MAX_CLIENTS ; i++,cl++) + { + if (cl->state == cs_free) + continue; + if (NET_CompareBaseAdr (adr, cl->netchan.remote_address) + && ( cl->netchan.qport == qport + || adr.port == cl->netchan.remote_address.port )) + { + if (cl->state == cs_connected) { + Con_Printf("%s:dup connect\n", NET_AdrToString (adr)); + userid--; + return; + } + + Con_Printf ("%s:reconnect\n", NET_AdrToString (adr)); + SV_DropClient (cl); + break; + } + } + + // count up the clients and spectators + clients = 0; + spectators = 0; + for (i=0,cl=svs.clients ; i<MAX_CLIENTS ; i++,cl++) + { + if (cl->state == cs_free) + continue; + if (cl->spectator) + spectators++; + else + clients++; + } + + // if at server limits, refuse connection + if ( maxclients.value > MAX_CLIENTS ) + Cvar_SetValue ("maxclients", MAX_CLIENTS); + if (maxspectators.value > MAX_CLIENTS) + Cvar_SetValue ("maxspectators", MAX_CLIENTS); + if (maxspectators.value + maxclients.value > MAX_CLIENTS) + Cvar_SetValue ("maxspectators", MAX_CLIENTS - maxspectators.value + maxclients.value); + if ( (spectator && spectators >= (int)maxspectators.value) + || (!spectator && clients >= (int)maxclients.value) ) + { + Con_Printf ("%s:full connect\n", NET_AdrToString (adr)); + Netchan_OutOfBandPrint (adr, "%c\nserver is full\n\n", A2C_PRINT); + return; + } + + // find a client slot + newcl = NULL; + for (i=0,cl=svs.clients ; i<MAX_CLIENTS ; i++,cl++) + { + if (cl->state == cs_free) + { + newcl = cl; + break; + } + } + if (!newcl) + { + Con_Printf ("WARNING: miscounted available clients\n"); + return; + } + + + // build a new connection + // accept the new client + // this is the only place a client_t is ever initialized + *newcl = temp; + + Netchan_OutOfBandPrint (adr, "%c", S2C_CONNECTION ); + + edictnum = (newcl-svs.clients)+1; + + Netchan_Setup (&newcl->netchan , adr, qport); + + newcl->state = cs_connected; + + newcl->datagram.allowoverflow = true; + newcl->datagram.data = newcl->datagram_buf; + newcl->datagram.maxsize = sizeof(newcl->datagram_buf); + + // spectator mode can ONLY be set at join time + newcl->spectator = spectator; + + ent = EDICT_NUM(edictnum); + newcl->edict = ent; + + // parse some info from the info strings + SV_ExtractFromUserinfo (newcl); + + // JACK: Init the floodprot stuff. + for (i=0; i<10; i++) + newcl->whensaid[i] = 0.0; + newcl->whensaidhead = 0; + newcl->lockedtill = 0; + + // call the progs to get default spawn parms for the new client + PR_ExecuteProgram (pr_global_struct->SetNewParms); + for (i=0 ; i<NUM_SPAWN_PARMS ; i++) + newcl->spawn_parms[i] = (&pr_global_struct->parm1)[i]; + + if (newcl->spectator) + Con_Printf ("Spectator %s connected\n", newcl->name); + else + Con_DPrintf ("Client %s connected\n", newcl->name); + newcl->sendinfo = true; +} + +int Rcon_Validate (void) +{ + if (!strlen (rcon_password.string)) + return 0; + + if (strcmp (Cmd_Argv(1), rcon_password.string) ) + return 0; + + return 1; +} + +/* +=============== +SVC_RemoteCommand + +A client issued an rcon command. +Shift down the remaining args +Redirect all printfs +=============== +*/ +void SVC_RemoteCommand (void) +{ + int i; + char remaining[1024]; + + + if (!Rcon_Validate ()) { + Con_Printf ("Bad rcon from %s:\n%s\n" + , NET_AdrToString (net_from), net_message.data+4); + + SV_BeginRedirect (RD_PACKET); + + Con_Printf ("Bad rcon_password.\n"); + + } else { + + Con_Printf ("Rcon from %s:\n%s\n" + , NET_AdrToString (net_from), net_message.data+4); + + SV_BeginRedirect (RD_PACKET); + + remaining[0] = 0; + + for (i=2 ; i<Cmd_Argc() ; i++) + { + strcat (remaining, Cmd_Argv(i) ); + strcat (remaining, " "); + } + + Cmd_ExecuteString (remaining); + + } + + SV_EndRedirect (); +} + + +/* +================= +SV_ConnectionlessPacket + +A connectionless packet has four leading 0xff +characters to distinguish it from a game channel. +Clients that are in the game can still send +connectionless packets. +================= +*/ +void SV_ConnectionlessPacket (void) +{ + char *s; + char *c; + + MSG_BeginReading (); + MSG_ReadLong (); // skip the -1 marker + + s = MSG_ReadStringLine (); + + Cmd_TokenizeString (s); + + c = Cmd_Argv(0); + + if (!strcmp(c, "ping") || ( c[0] == A2A_PING && (c[1] == 0 || c[1] == '\n')) ) + { + SVC_Ping (); + return; + } + if (c[0] == A2A_ACK && (c[1] == 0 || c[1] == '\n') ) + { + Con_Printf ("A2A_ACK from %s\n", NET_AdrToString (net_from)); + return; + } + else if (!strcmp(c,"status")) + { + SVC_Status (); + return; + } + else if (!strcmp(c,"log")) + { + SVC_Log (); + return; + } + else if (!strcmp(c,"connect")) + { + SVC_DirectConnect (); + return; + } + else if (!strcmp(c,"getchallenge")) + { + SVC_GetChallenge (); + return; + } + else if (!strcmp(c, "rcon")) + SVC_RemoteCommand (); + else + Con_Printf ("bad connectionless packet from %s:\n%s\n" + , NET_AdrToString (net_from), s); +} + +/* +============================================================================== + +PACKET FILTERING + + +You can add or remove addresses from the filter list with: + +addip <ip> +removeip <ip> + +The ip address is specified in dot format, and any unspecified digits will match any value, so you can specify an entire class C network with "addip 192.246.40". + +Removeip will only remove an address specified exactly the same way. You cannot addip a subnet, then removeip a single host. + +listip +Prints the current list of filters. + +writeip +Dumps "addip <ip>" commands to listip.cfg so it can be execed at a later date. The filter lists are not saved and restored by default, because I beleive it would cause too much confusion. + +filterban <0 or 1> + +If 1 (the default), then ip addresses matching the current list will be prohibited from entering the game. This is the default setting. + +If 0, then only addresses matching the list will be allowed. This lets you easily set up a private game, or a game that only allows players from your local network. + + +============================================================================== +*/ + + +typedef struct +{ + unsigned mask; + unsigned compare; +} ipfilter_t; + +#define MAX_IPFILTERS 1024 + +ipfilter_t ipfilters[MAX_IPFILTERS]; +int numipfilters; + +cvar_t filterban = {"filterban", "1"}; + +/* +================= +StringToFilter +================= +*/ +qboolean StringToFilter (char *s, ipfilter_t *f) +{ + char num[128]; + int i, j; + byte b[4]; + byte m[4]; + + for (i=0 ; i<4 ; i++) + { + b[i] = 0; + m[i] = 0; + } + + for (i=0 ; i<4 ; i++) + { + if (*s < '0' || *s > '9') + { + Con_Printf ("Bad filter address: %s\n", s); + return false; + } + + j = 0; + while (*s >= '0' && *s <= '9') + { + num[j++] = *s++; + } + num[j] = 0; + b[i] = atoi(num); + if (b[i] != 0) + m[i] = 255; + + if (!*s) + break; + s++; + } + + f->mask = *(unsigned *)m; + f->compare = *(unsigned *)b; + + return true; +} + +/* +================= +SV_AddIP_f +================= +*/ +void SV_AddIP_f (void) +{ + int i; + + for (i=0 ; i<numipfilters ; i++) + if (ipfilters[i].compare == 0xffffffff) + break; // free spot + if (i == numipfilters) + { + if (numipfilters == MAX_IPFILTERS) + { + Con_Printf ("IP filter list is full\n"); + return; + } + numipfilters++; + } + + if (!StringToFilter (Cmd_Argv(1), &ipfilters[i])) + ipfilters[i].compare = 0xffffffff; +} + +/* +================= +SV_RemoveIP_f +================= +*/ +void SV_RemoveIP_f (void) +{ + ipfilter_t f; + int i, j; + + if (!StringToFilter (Cmd_Argv(1), &f)) + return; + for (i=0 ; i<numipfilters ; i++) + if (ipfilters[i].mask == f.mask + && ipfilters[i].compare == f.compare) + { + for (j=i+1 ; j<numipfilters ; j++) + ipfilters[j-1] = ipfilters[j]; + numipfilters--; + Con_Printf ("Removed.\n"); + return; + } + Con_Printf ("Didn't find %s.\n", Cmd_Argv(1)); +} + +/* +================= +SV_ListIP_f +================= +*/ +void SV_ListIP_f (void) +{ + int i; + byte b[4]; + + Con_Printf ("Filter list:\n"); + for (i=0 ; i<numipfilters ; i++) + { + *(unsigned *)b = ipfilters[i].compare; + Con_Printf ("%3i.%3i.%3i.%3i\n", b[0], b[1], b[2], b[3]); + } +} + +/* +================= +SV_WriteIP_f +================= +*/ +void SV_WriteIP_f (void) +{ + FILE *f; + char name[MAX_OSPATH]; + byte b[4]; + int i; + + sprintf (name, "%s/listip.cfg", com_gamedir); + + Con_Printf ("Writing %s.\n", name); + + f = fopen (name, "wb"); + if (!f) + { + Con_Printf ("Couldn't open %s\n", name); + return; + } + + for (i=0 ; i<numipfilters ; i++) + { + *(unsigned *)b = ipfilters[i].compare; + fprintf (f, "addip %i.%i.%i.%i\n", b[0], b[1], b[2], b[3]); + } + + fclose (f); +} + +/* +================= +SV_SendBan +================= +*/ +void SV_SendBan (void) +{ + char data[128]; + + data[0] = data[1] = data[2] = data[3] = 0xff; + data[4] = A2C_PRINT; + data[5] = 0; + strcat (data, "\nbanned.\n"); + + NET_SendPacket (strlen(data), data, net_from); +} + +/* +================= +SV_FilterPacket +================= +*/ +qboolean SV_FilterPacket (void) +{ + int i; + unsigned in; + + in = *(unsigned *)net_from.ip; + + for (i=0 ; i<numipfilters ; i++) + if ( (in & ipfilters[i].mask) == ipfilters[i].compare) + return filterban.value; + + return !filterban.value; +} + +//============================================================================ + +/* +================= +SV_ReadPackets +================= +*/ +void SV_ReadPackets (void) +{ + int i; + client_t *cl; + qboolean good; + int qport; + + good = false; + while (NET_GetPacket ()) + { + if (SV_FilterPacket ()) + { + SV_SendBan (); // tell them we aren't listening... + continue; + } + + // check for connectionless packet (0xffffffff) first + if (*(int *)net_message.data == -1) + { + SV_ConnectionlessPacket (); + continue; + } + + // read the qport out of the message so we can fix up + // stupid address translating routers + MSG_BeginReading (); + MSG_ReadLong (); // sequence number + MSG_ReadLong (); // sequence number + qport = MSG_ReadShort () & 0xffff; + + // check for packets from connected clients + for (i=0, cl=svs.clients ; i<MAX_CLIENTS ; i++,cl++) + { + if (cl->state == cs_free) + continue; + if (!NET_CompareBaseAdr (net_from, cl->netchan.remote_address)) + continue; + if (cl->netchan.qport != qport) + continue; + if (cl->netchan.remote_address.port != net_from.port) + { + Con_DPrintf ("SV_ReadPackets: fixing up a translated port\n"); + cl->netchan.remote_address.port = net_from.port; + } + if (Netchan_Process(&cl->netchan)) + { // this is a valid, sequenced packet, so process it + svs.stats.packets++; + good = true; + cl->send_message = true; // reply at end of frame + if (cl->state != cs_zombie) + SV_ExecuteClientMessage (cl); + } + break; + } + + if (i != MAX_CLIENTS) + continue; + + // packet is not from a known client + // Con_Printf ("%s:sequenced packet without connection\n" + // ,NET_AdrToString(net_from)); + } +} + +/* +================== +SV_CheckTimeouts + +If a packet has not been received from a client in timeout.value +seconds, drop the conneciton. + +When a client is normally dropped, the client_t goes into a zombie state +for a few seconds to make sure any final reliable message gets resent +if necessary +================== +*/ +void SV_CheckTimeouts (void) +{ + int i; + client_t *cl; + float droptime; + int nclients; + + droptime = realtime - timeout.value; + nclients = 0; + + for (i=0,cl=svs.clients ; i<MAX_CLIENTS ; i++,cl++) + { + if (cl->state == cs_connected || cl->state == cs_spawned) { + if (!cl->spectator) + nclients++; + if (cl->netchan.last_received < droptime) { + SV_BroadcastPrintf (PRINT_HIGH, "%s timed out\n", cl->name); + SV_DropClient (cl); + cl->state = cs_free; // don't bother with zombie state + } + } + if (cl->state == cs_zombie && + realtime - cl->connection_started > zombietime.value) + { + cl->state = cs_free; // can now be reused + } + } + if (sv.paused && !nclients) { + // nobody left, unpause the server + SV_TogglePause("Pause released since no players are left.\n"); + } +} + +/* +=================== +SV_GetConsoleCommands + +Add them exactly as if they had been typed at the console +=================== +*/ +void SV_GetConsoleCommands (void) +{ + char *cmd; + + while (1) + { + cmd = Sys_ConsoleInput (); + if (!cmd) + break; + Cbuf_AddText (cmd); + } +} + +/* +=================== +SV_CheckVars + +=================== +*/ +void SV_CheckVars (void) +{ + static char *pw, *spw; + int v; + + if (password.string == pw && spectator_password.string == spw) + return; + pw = password.string; + spw = spectator_password.string; + + v = 0; + if (pw && pw[0] && strcmp(pw, "none")) + v |= 1; + if (spw && spw[0] && strcmp(spw, "none")) + v |= 2; + + Con_Printf ("Updated needpass.\n"); + if (!v) + Info_SetValueForKey (svs.info, "needpass", "", MAX_SERVERINFO_STRING); + else + Info_SetValueForKey (svs.info, "needpass", va("%i",v), MAX_SERVERINFO_STRING); +} + +/* +================== +SV_Frame + +================== +*/ +void SV_Frame (float time) +{ + static double start, end; + + start = Sys_DoubleTime (); + svs.stats.idle += start - end; + +// keep the random time dependent + rand (); + +// decide the simulation time + if (!sv.paused) { + realtime += time; + sv.time += time; + } + +// check timeouts + SV_CheckTimeouts (); + +// toggle the log buffer if full + SV_CheckLog (); + +// move autonomous things around if enough time has passed + if (!sv.paused) + SV_Physics (); + +// get packets + SV_ReadPackets (); + +// check for commands typed to the host + SV_GetConsoleCommands (); + +// process console commands + Cbuf_Execute (); + + SV_CheckVars (); + +// send messages back to the clients that had packets read this frame + SV_SendClientMessages (); + +// send a heartbeat to the master if needed + Master_Heartbeat (); + +// collect timing statistics + end = Sys_DoubleTime (); + svs.stats.active += end-start; + if (++svs.stats.count == STATFRAMES) + { + svs.stats.latched_active = svs.stats.active; + svs.stats.latched_idle = svs.stats.idle; + svs.stats.latched_packets = svs.stats.packets; + svs.stats.active = 0; + svs.stats.idle = 0; + svs.stats.packets = 0; + svs.stats.count = 0; + } +} + +/* +=============== +SV_InitLocal +=============== +*/ +void SV_InitLocal (void) +{ + int i; + extern cvar_t sv_maxvelocity; + extern cvar_t sv_gravity; + extern cvar_t sv_aim; + extern cvar_t sv_stopspeed; + extern cvar_t sv_spectatormaxspeed; + extern cvar_t sv_accelerate; + extern cvar_t sv_airaccelerate; + extern cvar_t sv_wateraccelerate; + extern cvar_t sv_friction; + extern cvar_t sv_waterfriction; + + SV_InitOperatorCommands (); + SV_UserInit (); + + Cvar_RegisterVariable (&rcon_password); + Cvar_RegisterVariable (&password); + Cvar_RegisterVariable (&spectator_password); + + Cvar_RegisterVariable (&sv_mintic); + Cvar_RegisterVariable (&sv_maxtic); + + Cvar_RegisterVariable (&fraglimit); + Cvar_RegisterVariable (&timelimit); + Cvar_RegisterVariable (&teamplay); + Cvar_RegisterVariable (&samelevel); + Cvar_RegisterVariable (&maxclients); + Cvar_RegisterVariable (&maxspectators); + Cvar_RegisterVariable (&hostname); + Cvar_RegisterVariable (&deathmatch); + Cvar_RegisterVariable (&spawn); + Cvar_RegisterVariable (&watervis); + + Cvar_RegisterVariable (&developer); + + Cvar_RegisterVariable (&timeout); + Cvar_RegisterVariable (&zombietime); + + Cvar_RegisterVariable (&sv_maxvelocity); + Cvar_RegisterVariable (&sv_gravity); + Cvar_RegisterVariable (&sv_stopspeed); + Cvar_RegisterVariable (&sv_maxspeed); + Cvar_RegisterVariable (&sv_spectatormaxspeed); + Cvar_RegisterVariable (&sv_accelerate); + Cvar_RegisterVariable (&sv_airaccelerate); + Cvar_RegisterVariable (&sv_wateraccelerate); + Cvar_RegisterVariable (&sv_friction); + Cvar_RegisterVariable (&sv_waterfriction); + + Cvar_RegisterVariable (&sv_aim); + + Cvar_RegisterVariable (&filterban); + + Cvar_RegisterVariable (&allow_download); + Cvar_RegisterVariable (&allow_download_skins); + Cvar_RegisterVariable (&allow_download_models); + Cvar_RegisterVariable (&allow_download_sounds); + Cvar_RegisterVariable (&allow_download_maps); + + Cvar_RegisterVariable (&sv_highchars); + + Cvar_RegisterVariable (&sv_phs); + + Cvar_RegisterVariable (&pausable); + + Cmd_AddCommand ("addip", SV_AddIP_f); + Cmd_AddCommand ("removeip", SV_RemoveIP_f); + Cmd_AddCommand ("listip", SV_ListIP_f); + Cmd_AddCommand ("writeip", SV_WriteIP_f); + + for (i=0 ; i<MAX_MODELS ; i++) + sprintf (localmodels[i], "*%i", i); + + Info_SetValueForStarKey (svs.info, "*version", va("%4.2f", VERSION), MAX_SERVERINFO_STRING); + + // init fraglog stuff + svs.logsequence = 1; + svs.logtime = realtime; + svs.log[0].data = svs.log_buf[0]; + svs.log[0].maxsize = sizeof(svs.log_buf[0]); + svs.log[0].cursize = 0; + svs.log[0].allowoverflow = true; + svs.log[1].data = svs.log_buf[1]; + svs.log[1].maxsize = sizeof(svs.log_buf[1]); + svs.log[1].cursize = 0; + svs.log[1].allowoverflow = true; +} + + +//============================================================================ + +/* +================ +Master_Heartbeat + +Send a message to the master every few minutes to +let it know we are alive, and log information +================ +*/ +#define HEARTBEAT_SECONDS 300 +void Master_Heartbeat (void) +{ + char string[2048]; + int active; + int i; + + if (realtime - svs.last_heartbeat < HEARTBEAT_SECONDS) + return; // not time to send yet + + svs.last_heartbeat = realtime; + + // + // count active users + // + active = 0; + for (i=0 ; i<MAX_CLIENTS ; i++) + if (svs.clients[i].state == cs_connected || + svs.clients[i].state == cs_spawned ) + active++; + + svs.heartbeat_sequence++; + sprintf (string, "%c\n%i\n%i\n", S2M_HEARTBEAT, + svs.heartbeat_sequence, active); + + + // send to group master + for (i=0 ; i<MAX_MASTERS ; i++) + if (master_adr[i].port) + { + Con_Printf ("Sending heartbeat to %s\n", NET_AdrToString (master_adr[i])); + NET_SendPacket (strlen(string), string, master_adr[i]); + } +} + +/* +================= +Master_Shutdown + +Informs all masters that this server is going down +================= +*/ +void Master_Shutdown (void) +{ + char string[2048]; + int i; + + sprintf (string, "%c\n", S2M_SHUTDOWN); + + // send to group master + for (i=0 ; i<MAX_MASTERS ; i++) + if (master_adr[i].port) + { + Con_Printf ("Sending heartbeat to %s\n", NET_AdrToString (master_adr[i])); + NET_SendPacket (strlen(string), string, master_adr[i]); + } +} + +/* +================= +SV_ExtractFromUserinfo + +Pull specific info from a newly changed userinfo string +into a more C freindly form. +================= +*/ +void SV_ExtractFromUserinfo (client_t *cl) +{ + char *val, *p, *q; + int i; + client_t *client; + int dupc = 1; + char newname[80]; + + + // name for C code + val = Info_ValueForKey (cl->userinfo, "name"); + + // trim user name + strncpy(newname, val, sizeof(newname) - 1); + newname[sizeof(newname) - 1] = 0; + + for (p = newname; (*p == ' ' || *p == '\r' || *p == '\n') && *p; p++) + ; + + if (p != newname && !*p) { + //white space only + strcpy(newname, "unnamed"); + p = newname; + } + + if (p != newname && *p) { + for (q = newname; *p; *q++ = *p++) + ; + *q = 0; + } + for (p = newname + strlen(newname) - 1; p != newname && (*p == ' ' || *p == '\r' || *p == '\n') ; p--) + ; + p[1] = 0; + + if (strcmp(val, newname)) { + Info_SetValueForKey (cl->userinfo, "name", newname, MAX_INFO_STRING); + val = Info_ValueForKey (cl->userinfo, "name"); + } + + if (!val[0] || !stricmp(val, "console")) { + Info_SetValueForKey (cl->userinfo, "name", "unnamed", MAX_INFO_STRING); + val = Info_ValueForKey (cl->userinfo, "name"); + } + + // check to see if another user by the same name exists + while (1) { + for (i=0, client = svs.clients ; i<MAX_CLIENTS ; i++, client++) { + if (client->state != cs_spawned || client == cl) + continue; + if (!stricmp(client->name, val)) + break; + } + if (i != MAX_CLIENTS) { // dup name + if (strlen(val) > sizeof(cl->name) - 1) + val[sizeof(cl->name) - 4] = 0; + p = val; + + if (val[0] == '(') + if (val[2] == ')') + p = val + 3; + else if (val[3] == ')') + p = val + 4; + + sprintf(newname, "(%d)%-.40s", dupc++, p); + Info_SetValueForKey (cl->userinfo, "name", newname, MAX_INFO_STRING); + val = Info_ValueForKey (cl->userinfo, "name"); + } else + break; + } + + if (strncmp(val, cl->name, strlen(cl->name))) { + if (!sv.paused) { + if (!cl->lastnametime || realtime - cl->lastnametime > 5) { + cl->lastnamecount = 0; + cl->lastnametime = realtime; + } else if (cl->lastnamecount++ > 4) { + SV_BroadcastPrintf (PRINT_HIGH, "%s was kicked for name spam\n", cl->name); + SV_ClientPrintf (cl, PRINT_HIGH, "You were kicked from the game for name spamming\n"); + SV_DropClient (cl); + return; + } + } + + if (cl->state >= cs_spawned && !cl->spectator) + SV_BroadcastPrintf (PRINT_HIGH, "%s changed name to %s\n", cl->name, val); + } + + + strncpy (cl->name, val, sizeof(cl->name)-1); + + // rate command + val = Info_ValueForKey (cl->userinfo, "rate"); + if (strlen(val)) + { + i = atoi(val); + if (i < 500) + i = 500; + if (i > 10000) + i = 10000; + cl->netchan.rate = 1.0/i; + } + + // msg command + val = Info_ValueForKey (cl->userinfo, "msg"); + if (strlen(val)) + { + cl->messagelevel = atoi(val); + } + +} + + +//============================================================================ + +/* +==================== +SV_InitNet +==================== +*/ +void SV_InitNet (void) +{ + int port; + int p; + + port = PORT_SERVER; + p = COM_CheckParm ("-port"); + if (p && p < com_argc) + { + port = atoi(com_argv[p+1]); + Con_Printf ("Port: %i\n", port); + } + NET_Init (port); + + Netchan_Init (); + + // heartbeats will allways be sent to the id master + svs.last_heartbeat = -99999; // send immediately +// NET_StringToAdr ("192.246.40.70:27000", &idmaster_adr); +} + + +/* +==================== +SV_Init +==================== +*/ +void SV_Init (quakeparms_t *parms) +{ + COM_InitArgv (parms->argc, parms->argv); + COM_AddParm ("-game"); + COM_AddParm ("qw"); + + if (COM_CheckParm ("-minmemory")) + parms->memsize = MINIMUM_MEMORY; + + host_parms = *parms; + + if (parms->memsize < MINIMUM_MEMORY) + SV_Error ("Only %4.1f megs of memory reported, can't execute game", parms->memsize / (float)0x100000); + + Memory_Init (parms->membase, parms->memsize); + Cbuf_Init (); + Cmd_Init (); + + COM_Init (); + + PR_Init (); + Mod_Init (); + + SV_InitNet (); + + SV_InitLocal (); + Sys_Init (); + Pmove_Init (); + + Hunk_AllocName (0, "-HOST_HUNKLEVEL-"); + host_hunklevel = Hunk_LowMark (); + + Cbuf_InsertText ("exec server.cfg\n"); + + host_initialized = true; + + Con_Printf ("Exe: "__TIME__" "__DATE__"\n"); + Con_Printf ("%4.1f megabyte heap\n",parms->memsize/ (1024*1024.0)); + + Con_Printf ("\nServer Version %4.2f (Build %04d)\n\n", VERSION, build_number()); + + Con_Printf ("======== QuakeWorld Initialized ========\n"); + +// process command line arguments + Cmd_StuffCmds_f (); + Cbuf_Execute (); + +// if a map wasn't specified on the command line, spawn start.map + if (sv.state == ss_dead) + Cmd_ExecuteString ("map start"); + if (sv.state == ss_dead) + SV_Error ("Couldn't spawn a server"); +} diff --git a/QW/server/sv_move.c b/QW/server/sv_move.c new file mode 100644 index 0000000..e9324bb --- /dev/null +++ b/QW/server/sv_move.c @@ -0,0 +1,419 @@ +/* +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. + +*/ +// sv_move.c -- monster movement + +#include "qwsvdef.h" + +#define STEPSIZE 18 + +/* +============= +SV_CheckBottom + +Returns false if any part of the bottom of the entity is off an edge that +is not a staircase. + +============= +*/ +int c_yes, c_no; + +qboolean SV_CheckBottom (edict_t *ent) +{ + vec3_t mins, maxs, start, stop; + trace_t trace; + int x, y; + float mid, bottom; + + VectorAdd (ent->v.origin, ent->v.mins, mins); + VectorAdd (ent->v.origin, ent->v.maxs, maxs); + +// if all of the points under the corners are solid world, don't bother +// with the tougher checks +// the corners must be within 16 of the midpoint + start[2] = mins[2] - 1; + for (x=0 ; x<=1 ; x++) + for (y=0 ; y<=1 ; y++) + { + start[0] = x ? maxs[0] : mins[0]; + start[1] = y ? maxs[1] : mins[1]; + if (SV_PointContents (start) != CONTENTS_SOLID) + goto realcheck; + } + + c_yes++; + return true; // we got out easy + +realcheck: + c_no++; +// +// check it for real... +// + start[2] = mins[2]; + +// the midpoint must be within 16 of the bottom + start[0] = stop[0] = (mins[0] + maxs[0])*0.5; + start[1] = stop[1] = (mins[1] + maxs[1])*0.5; + stop[2] = start[2] - 2*STEPSIZE; + trace = SV_Move (start, vec3_origin, vec3_origin, stop, true, ent); + + if (trace.fraction == 1.0) + return false; + mid = bottom = trace.endpos[2]; + +// the corners must be within 16 of the midpoint + for (x=0 ; x<=1 ; x++) + for (y=0 ; y<=1 ; y++) + { + start[0] = stop[0] = x ? maxs[0] : mins[0]; + start[1] = stop[1] = y ? maxs[1] : mins[1]; + + trace = SV_Move (start, vec3_origin, vec3_origin, stop, true, ent); + + if (trace.fraction != 1.0 && trace.endpos[2] > bottom) + bottom = trace.endpos[2]; + if (trace.fraction == 1.0 || mid - trace.endpos[2] > STEPSIZE) + return false; + } + + c_yes++; + return true; +} + + +/* +============= +SV_movestep + +Called by monster program code. +The move will be adjusted for slopes and stairs, but if the move isn't +possible, no move is done, false is returned, and +pr_global_struct->trace_normal is set to the normal of the blocking wall +============= +*/ +qboolean SV_movestep (edict_t *ent, vec3_t move, qboolean relink) +{ + float dz; + vec3_t oldorg, neworg, end; + trace_t trace; + int i; + edict_t *enemy; + +// try the move + VectorCopy (ent->v.origin, oldorg); + VectorAdd (ent->v.origin, move, neworg); + +// flying monsters don't step up + if ( (int)ent->v.flags & (FL_SWIM | FL_FLY) ) + { + // try one move with vertical motion, then one without + for (i=0 ; i<2 ; i++) + { + VectorAdd (ent->v.origin, move, neworg); + enemy = PROG_TO_EDICT(ent->v.enemy); + if (i == 0 && enemy != sv.edicts) + { + dz = ent->v.origin[2] - PROG_TO_EDICT(ent->v.enemy)->v.origin[2]; + if (dz > 40) + neworg[2] -= 8; + if (dz < 30) + neworg[2] += 8; + } + trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, neworg, false, ent); + + if (trace.fraction == 1) + { + if ( ((int)ent->v.flags & FL_SWIM) && SV_PointContents(trace.endpos) == CONTENTS_EMPTY ) + return false; // swim monster left water + + VectorCopy (trace.endpos, ent->v.origin); + if (relink) + SV_LinkEdict (ent, true); + return true; + } + + if (enemy == sv.edicts) + break; + } + + return false; + } + +// push down from a step height above the wished position + neworg[2] += STEPSIZE; + VectorCopy (neworg, end); + end[2] -= STEPSIZE*2; + + trace = SV_Move (neworg, ent->v.mins, ent->v.maxs, end, false, ent); + + if (trace.allsolid) + return false; + + if (trace.startsolid) + { + neworg[2] -= STEPSIZE; + trace = SV_Move (neworg, ent->v.mins, ent->v.maxs, end, false, ent); + if (trace.allsolid || trace.startsolid) + return false; + } + if (trace.fraction == 1) + { + // if monster had the ground pulled out, go ahead and fall + if ( (int)ent->v.flags & FL_PARTIALGROUND ) + { + VectorAdd (ent->v.origin, move, ent->v.origin); + if (relink) + SV_LinkEdict (ent, true); + ent->v.flags = (int)ent->v.flags & ~FL_ONGROUND; +// Con_Printf ("fall down\n"); + return true; + } + + return false; // walked off an edge + } + +// check point traces down for dangling corners + VectorCopy (trace.endpos, ent->v.origin); + + if (!SV_CheckBottom (ent)) + { + if ( (int)ent->v.flags & FL_PARTIALGROUND ) + { // entity had floor mostly pulled out from underneath it + // and is trying to correct + if (relink) + SV_LinkEdict (ent, true); + return true; + } + VectorCopy (oldorg, ent->v.origin); + return false; + } + + if ( (int)ent->v.flags & FL_PARTIALGROUND ) + { +// Con_Printf ("back on ground\n"); + ent->v.flags = (int)ent->v.flags & ~FL_PARTIALGROUND; + } + ent->v.groundentity = EDICT_TO_PROG(trace.ent); + +// the move is ok + if (relink) + SV_LinkEdict (ent, true); + return true; +} + + +//============================================================================ + +/* +====================== +SV_StepDirection + +Turns to the movement direction, and walks the current distance if +facing it. + +====================== +*/ +void PF_changeyaw (void); +qboolean SV_StepDirection (edict_t *ent, float yaw, float dist) +{ + vec3_t move, oldorigin; + float delta; + + ent->v.ideal_yaw = yaw; + PF_changeyaw(); + + yaw = yaw*M_PI*2 / 360; + move[0] = cos(yaw)*dist; + move[1] = sin(yaw)*dist; + move[2] = 0; + + VectorCopy (ent->v.origin, oldorigin); + if (SV_movestep (ent, move, false)) + { + delta = ent->v.angles[YAW] - ent->v.ideal_yaw; + if (delta > 45 && delta < 315) + { // not turned far enough, so don't take the step + VectorCopy (oldorigin, ent->v.origin); + } + SV_LinkEdict (ent, true); + return true; + } + SV_LinkEdict (ent, true); + + return false; +} + +/* +====================== +SV_FixCheckBottom + +====================== +*/ +void SV_FixCheckBottom (edict_t *ent) +{ +// Con_Printf ("SV_FixCheckBottom\n"); + + ent->v.flags = (int)ent->v.flags | FL_PARTIALGROUND; +} + + + +/* +================ +SV_NewChaseDir + +================ +*/ +#define DI_NODIR -1 +void SV_NewChaseDir (edict_t *actor, edict_t *enemy, float dist) +{ + float deltax,deltay; + float d[3]; + float tdir, olddir, turnaround; + + olddir = anglemod( (int)(actor->v.ideal_yaw/45)*45 ); + turnaround = anglemod(olddir - 180); + + deltax = enemy->v.origin[0] - actor->v.origin[0]; + deltay = enemy->v.origin[1] - actor->v.origin[1]; + if (deltax>10) + d[1]= 0; + else if (deltax<-10) + d[1]= 180; + else + d[1]= DI_NODIR; + if (deltay<-10) + d[2]= 270; + else if (deltay>10) + d[2]= 90; + else + d[2]= DI_NODIR; + +// try direct route + if (d[1] != DI_NODIR && d[2] != DI_NODIR) + { + if (d[1] == 0) + tdir = d[2] == 90 ? 45 : 315; + else + tdir = d[2] == 90 ? 135 : 215; + + if (tdir != turnaround && SV_StepDirection(actor, tdir, dist)) + return; + } + +// try other directions + if ( ((rand()&3) & 1) || abs(deltay)>abs(deltax)) + { + tdir=d[1]; + d[1]=d[2]; + d[2]=tdir; + } + + if (d[1]!=DI_NODIR && d[1]!=turnaround + && SV_StepDirection(actor, d[1], dist)) + return; + + if (d[2]!=DI_NODIR && d[2]!=turnaround + && SV_StepDirection(actor, d[2], dist)) + return; + +/* there is no direct path to the player, so pick another direction */ + + if (olddir!=DI_NODIR && SV_StepDirection(actor, olddir, dist)) + return; + + if (rand()&1) /*randomly determine direction of search*/ + { + for (tdir=0 ; tdir<=315 ; tdir += 45) + if (tdir!=turnaround && SV_StepDirection(actor, tdir, dist) ) + return; + } + else + { + for (tdir=315 ; tdir >=0 ; tdir -= 45) + if (tdir!=turnaround && SV_StepDirection(actor, tdir, dist) ) + return; + } + + if (turnaround != DI_NODIR && SV_StepDirection(actor, turnaround, dist) ) + return; + + actor->v.ideal_yaw = olddir; // can't move + +// if a bridge was pulled out from underneath a monster, it may not have +// a valid standing position at all + + if (!SV_CheckBottom (actor)) + SV_FixCheckBottom (actor); + +} + +/* +====================== +SV_CloseEnough + +====================== +*/ +qboolean SV_CloseEnough (edict_t *ent, edict_t *goal, float dist) +{ + int i; + + for (i=0 ; i<3 ; i++) + { + if (goal->v.absmin[i] > ent->v.absmax[i] + dist) + return false; + if (goal->v.absmax[i] < ent->v.absmin[i] - dist) + return false; + } + return true; +} + +/* +====================== +SV_MoveToGoal + +====================== +*/ +void SV_MoveToGoal (void) +{ + edict_t *ent, *goal; + float dist; + + ent = PROG_TO_EDICT(pr_global_struct->self); + goal = PROG_TO_EDICT(ent->v.goalentity); + dist = G_FLOAT(OFS_PARM0); + + if ( !( (int)ent->v.flags & (FL_ONGROUND|FL_FLY|FL_SWIM) ) ) + { + G_FLOAT(OFS_RETURN) = 0; + return; + } + +// if the next step hits the enemy, return immediately + if ( PROG_TO_EDICT(ent->v.enemy) != sv.edicts && SV_CloseEnough (ent, goal, dist) ) + return; + +// bump around... + if ( (rand()&3)==1 || + !SV_StepDirection (ent, ent->v.ideal_yaw, dist)) + { + SV_NewChaseDir (ent, goal, dist); + } +} + diff --git a/QW/server/sv_nchan.c b/QW/server/sv_nchan.c new file mode 100644 index 0000000..e201e93 --- /dev/null +++ b/QW/server/sv_nchan.c @@ -0,0 +1,165 @@ +/* +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. + +*/ +// sv_nchan.c, user reliable data stream writes + +#include "qwsvdef.h" + +// check to see if client block will fit, if not, rotate buffers +void ClientReliableCheckBlock(client_t *cl, int maxsize) +{ + if (cl->num_backbuf || + cl->netchan.message.cursize > + cl->netchan.message.maxsize - maxsize - 1) { + // we would probably overflow the buffer, save it for next + if (!cl->num_backbuf) { + memset(&cl->backbuf, 0, sizeof(cl->backbuf)); + cl->backbuf.allowoverflow = true; + cl->backbuf.data = cl->backbuf_data[0]; + cl->backbuf.maxsize = sizeof(cl->backbuf_data[0]); + cl->backbuf_size[0] = 0; + cl->num_backbuf++; + } + + if (cl->backbuf.cursize > cl->backbuf.maxsize - maxsize - 1) { + if (cl->num_backbuf == MAX_BACK_BUFFERS) { + Con_Printf ("WARNING: MAX_BACK_BUFFERS for %s\n", cl->name); + cl->backbuf.cursize = 0; // don't overflow without allowoverflow set + cl->netchan.message.overflowed = true; // this will drop the client + return; + } + memset(&cl->backbuf, 0, sizeof(cl->backbuf)); + cl->backbuf.allowoverflow = true; + cl->backbuf.data = cl->backbuf_data[cl->num_backbuf]; + cl->backbuf.maxsize = sizeof(cl->backbuf_data[cl->num_backbuf]); + cl->backbuf_size[cl->num_backbuf] = 0; + cl->num_backbuf++; + } + } +} + +// begin a client block, estimated maximum size +void ClientReliableWrite_Begin(client_t *cl, int c, int maxsize) +{ + ClientReliableCheckBlock(cl, maxsize); + ClientReliableWrite_Byte(cl, c); +} + +void ClientReliable_FinishWrite(client_t *cl) +{ + if (cl->num_backbuf) { + cl->backbuf_size[cl->num_backbuf - 1] = cl->backbuf.cursize; + + if (cl->backbuf.overflowed) { + Con_Printf ("WARNING: backbuf [%d] reliable overflow for %s\n",cl->num_backbuf,cl->name); + cl->netchan.message.overflowed = true; // this will drop the client + } + } +} + +void ClientReliableWrite_Angle(client_t *cl, float f) +{ + if (cl->num_backbuf) { + MSG_WriteAngle(&cl->backbuf, f); + ClientReliable_FinishWrite(cl); + } else + MSG_WriteAngle(&cl->netchan.message, f); +} + +void ClientReliableWrite_Angle16(client_t *cl, float f) +{ + if (cl->num_backbuf) { + MSG_WriteAngle16(&cl->backbuf, f); + ClientReliable_FinishWrite(cl); + } else + MSG_WriteAngle16(&cl->netchan.message, f); +} + +void ClientReliableWrite_Byte(client_t *cl, int c) +{ + if (cl->num_backbuf) { + MSG_WriteByte(&cl->backbuf, c); + ClientReliable_FinishWrite(cl); + } else + MSG_WriteByte(&cl->netchan.message, c); +} + +void ClientReliableWrite_Char(client_t *cl, int c) +{ + if (cl->num_backbuf) { + MSG_WriteChar(&cl->backbuf, c); + ClientReliable_FinishWrite(cl); + } else + MSG_WriteChar(&cl->netchan.message, c); +} + +void ClientReliableWrite_Float(client_t *cl, float f) +{ + if (cl->num_backbuf) { + MSG_WriteFloat(&cl->backbuf, f); + ClientReliable_FinishWrite(cl); + } else + MSG_WriteFloat(&cl->netchan.message, f); +} + +void ClientReliableWrite_Coord(client_t *cl, float f) +{ + if (cl->num_backbuf) { + MSG_WriteCoord(&cl->backbuf, f); + ClientReliable_FinishWrite(cl); + } else + MSG_WriteCoord(&cl->netchan.message, f); +} + +void ClientReliableWrite_Long(client_t *cl, int c) +{ + if (cl->num_backbuf) { + MSG_WriteLong(&cl->backbuf, c); + ClientReliable_FinishWrite(cl); + } else + MSG_WriteLong(&cl->netchan.message, c); +} + +void ClientReliableWrite_Short(client_t *cl, int c) +{ + if (cl->num_backbuf) { + MSG_WriteShort(&cl->backbuf, c); + ClientReliable_FinishWrite(cl); + } else + MSG_WriteShort(&cl->netchan.message, c); +} + +void ClientReliableWrite_String(client_t *cl, char *s) +{ + if (cl->num_backbuf) { + MSG_WriteString(&cl->backbuf, s); + ClientReliable_FinishWrite(cl); + } else + MSG_WriteString(&cl->netchan.message, s); +} + +void ClientReliableWrite_SZ(client_t *cl, void *data, int len) +{ + if (cl->num_backbuf) { + SZ_Write(&cl->backbuf, data, len); + ClientReliable_FinishWrite(cl); + } else + SZ_Write(&cl->netchan.message, data, len); +} + diff --git a/QW/server/sv_phys.c b/QW/server/sv_phys.c new file mode 100644 index 0000000..70db5af --- /dev/null +++ b/QW/server/sv_phys.c @@ -0,0 +1,947 @@ +/* +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. + +*/ +// sv_phys.c + +#include "qwsvdef.h" + +/* + + +pushmove objects do not obey gravity, and do not interact with each other or trigger fields, but block normal movement and push normal objects when they move. + +onground is set for toss objects when they come to a complete rest. it is set for steping or walking objects + +doors, plats, etc are SOLID_BSP, and MOVETYPE_PUSH +bonus items are SOLID_TRIGGER touch, and MOVETYPE_TOSS +corpses are SOLID_NOT and MOVETYPE_TOSS +crates are SOLID_BBOX and MOVETYPE_TOSS +walking monsters are SOLID_SLIDEBOX and MOVETYPE_STEP +flying/floating monsters are SOLID_SLIDEBOX and MOVETYPE_FLY + +solid_edge items only clip against bsp models. + +*/ + +cvar_t sv_maxvelocity = {"sv_maxvelocity","2000"}; + +cvar_t sv_gravity = { "sv_gravity", "800"}; +cvar_t sv_stopspeed = { "sv_stopspeed", "100"}; +cvar_t sv_maxspeed = { "sv_maxspeed", "320"}; +cvar_t sv_spectatormaxspeed = { "sv_spectatormaxspeed", "500"}; +cvar_t sv_accelerate = { "sv_accelerate", "10"}; +cvar_t sv_airaccelerate = { "sv_airaccelerate", "0.7"}; +cvar_t sv_wateraccelerate = { "sv_wateraccelerate", "10"}; +cvar_t sv_friction = { "sv_friction", "4"}; +cvar_t sv_waterfriction = { "sv_waterfriction", "4"}; + + +#define MOVE_EPSILON 0.01 + +void SV_Physics_Toss (edict_t *ent); + +/* +================ +SV_CheckAllEnts +================ +*/ +void SV_CheckAllEnts (void) +{ + int e; + edict_t *check; + +// see if any solid entities are inside the final position + check = NEXT_EDICT(sv.edicts); + for (e=1 ; e<sv.num_edicts ; e++, check = NEXT_EDICT(check)) + { + if (check->free) + continue; + if (check->v.movetype == MOVETYPE_PUSH + || check->v.movetype == MOVETYPE_NONE + || check->v.movetype == MOVETYPE_NOCLIP) + continue; + + if (SV_TestEntityPosition (check)) + Con_Printf ("entity in invalid position\n"); + } +} + +/* +================ +SV_CheckVelocity +================ +*/ +void SV_CheckVelocity (edict_t *ent) +{ + int i; + +// +// bound velocity +// + for (i=0 ; i<3 ; i++) + { + if (IS_NAN(ent->v.velocity[i])) + { + Con_Printf ("Got a NaN velocity on %s\n", PR_GetString(ent->v.classname)); + ent->v.velocity[i] = 0; + } + if (IS_NAN(ent->v.origin[i])) + { + Con_Printf ("Got a NaN origin on %s\n", PR_GetString(ent->v.classname)); + ent->v.origin[i] = 0; + } + if (ent->v.velocity[i] > sv_maxvelocity.value) + ent->v.velocity[i] = sv_maxvelocity.value; + else if (ent->v.velocity[i] < -sv_maxvelocity.value) + ent->v.velocity[i] = -sv_maxvelocity.value; + } +} + +/* +============= +SV_RunThink + +Runs thinking code if time. There is some play in the exact time the think +function will be called, because it is called before any movement is done +in a frame. Not used for pushmove objects, because they must be exact. +Returns false if the entity removed itself. +============= +*/ +qboolean SV_RunThink (edict_t *ent) +{ + float thinktime; + + do + { + thinktime = ent->v.nextthink; + if (thinktime <= 0) + return true; + if (thinktime > sv.time + host_frametime) + return true; + + if (thinktime < sv.time) + thinktime = sv.time; // don't let things stay in the past. + // it is possible to start that way + // by a trigger with a local time. + ent->v.nextthink = 0; + pr_global_struct->time = thinktime; + pr_global_struct->self = EDICT_TO_PROG(ent); + pr_global_struct->other = EDICT_TO_PROG(sv.edicts); + PR_ExecuteProgram (ent->v.think); + + if (ent->free) + return false; + } while (1); + + return true; +} + +/* +================== +SV_Impact + +Two entities have touched, so run their touch functions +================== +*/ +void SV_Impact (edict_t *e1, edict_t *e2) +{ + int old_self, old_other; + + old_self = pr_global_struct->self; + old_other = pr_global_struct->other; + + pr_global_struct->time = sv.time; + if (e1->v.touch && e1->v.solid != SOLID_NOT) + { + pr_global_struct->self = EDICT_TO_PROG(e1); + pr_global_struct->other = EDICT_TO_PROG(e2); + PR_ExecuteProgram (e1->v.touch); + } + + if (e2->v.touch && e2->v.solid != SOLID_NOT) + { + pr_global_struct->self = EDICT_TO_PROG(e2); + pr_global_struct->other = EDICT_TO_PROG(e1); + PR_ExecuteProgram (e2->v.touch); + } + + pr_global_struct->self = old_self; + pr_global_struct->other = old_other; +} + + +/* +================== +ClipVelocity + +Slide off of the impacting object +returns the blocked flags (1 = floor, 2 = step / wall) +================== +*/ +#define STOP_EPSILON 0.1 + +int ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce) +{ + float backoff; + float change; + int i, blocked; + + blocked = 0; + if (normal[2] > 0) + blocked |= 1; // floor + if (!normal[2]) + blocked |= 2; // step + + backoff = DotProduct (in, normal) * overbounce; + + for (i=0 ; i<3 ; i++) + { + change = normal[i]*backoff; + out[i] = in[i] - change; + if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON) + out[i] = 0; + } + + return blocked; +} + + +/* +============ +SV_FlyMove + +The basic solid body movement clip that slides along multiple planes +Returns the clipflags if the velocity was modified (hit something solid) +1 = floor +2 = wall / step +4 = dead stop +If steptrace is not NULL, the trace of any vertical wall hit will be stored +============ +*/ +#define MAX_CLIP_PLANES 5 +int SV_FlyMove (edict_t *ent, float time, trace_t *steptrace) +{ + int bumpcount, numbumps; + vec3_t dir; + float d; + int numplanes; + vec3_t planes[MAX_CLIP_PLANES]; + vec3_t primal_velocity, original_velocity, new_velocity; + int i, j; + trace_t trace; + vec3_t end; + float time_left; + int blocked; + + numbumps = 4; + + blocked = 0; + VectorCopy (ent->v.velocity, original_velocity); + VectorCopy (ent->v.velocity, primal_velocity); + numplanes = 0; + + time_left = time; + + for (bumpcount=0 ; bumpcount<numbumps ; bumpcount++) + { + for (i=0 ; i<3 ; i++) + end[i] = ent->v.origin[i] + time_left * ent->v.velocity[i]; + + trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, false, ent); + + if (trace.allsolid) + { // entity is trapped in another solid + VectorCopy (vec3_origin, ent->v.velocity); + return 3; + } + + if (trace.fraction > 0) + { // actually covered some distance + VectorCopy (trace.endpos, ent->v.origin); + VectorCopy (ent->v.velocity, original_velocity); + numplanes = 0; + } + + if (trace.fraction == 1) + break; // moved the entire distance + + if (!trace.ent) + SV_Error ("SV_FlyMove: !trace.ent"); + + if (trace.plane.normal[2] > 0.7) + { + blocked |= 1; // floor + if (trace.ent->v.solid == SOLID_BSP) + { + ent->v.flags = (int)ent->v.flags | FL_ONGROUND; + ent->v.groundentity = EDICT_TO_PROG(trace.ent); + } + } + if (!trace.plane.normal[2]) + { + blocked |= 2; // step + if (steptrace) + *steptrace = trace; // save for player extrafriction + } + +// +// run the impact function +// + SV_Impact (ent, trace.ent); + if (ent->free) + break; // removed by the impact function + + + time_left -= time_left * trace.fraction; + + // cliped to another plane + if (numplanes >= MAX_CLIP_PLANES) + { // this shouldn't really happen + VectorCopy (vec3_origin, ent->v.velocity); + return 3; + } + + VectorCopy (trace.plane.normal, planes[numplanes]); + numplanes++; + +// +// modify original_velocity so it parallels all of the clip planes +// + for (i=0 ; i<numplanes ; i++) + { + ClipVelocity (original_velocity, planes[i], new_velocity, 1); + for (j=0 ; j<numplanes ; j++) + if (j != i) + { + if (DotProduct (new_velocity, planes[j]) < 0) + break; // not ok + } + if (j == numplanes) + break; + } + + if (i != numplanes) + { // go along this plane + VectorCopy (new_velocity, ent->v.velocity); + } + else + { // go along the crease + if (numplanes != 2) + { +// Con_Printf ("clip velocity, numplanes == %i\n",numplanes); + VectorCopy (vec3_origin, ent->v.velocity); + return 7; + } + CrossProduct (planes[0], planes[1], dir); + d = DotProduct (dir, ent->v.velocity); + VectorScale (dir, d, ent->v.velocity); + } + +// +// if original velocity is against the original velocity, stop dead +// to avoid tiny occilations in sloping corners +// + if (DotProduct (ent->v.velocity, primal_velocity) <= 0) + { + VectorCopy (vec3_origin, ent->v.velocity); + return blocked; + } + } + + return blocked; +} + + +/* +============ +SV_AddGravity + +============ +*/ +void SV_AddGravity (edict_t *ent, float scale) +{ + ent->v.velocity[2] -= scale * movevars.gravity * host_frametime; +} + +/* +=============================================================================== + +PUSHMOVE + +=============================================================================== +*/ + +/* +============ +SV_PushEntity + +Does not change the entities velocity at all +============ +*/ +trace_t SV_PushEntity (edict_t *ent, vec3_t push) +{ + trace_t trace; + vec3_t end; + + VectorAdd (ent->v.origin, push, end); + + if (ent->v.movetype == MOVETYPE_FLYMISSILE) + trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, MOVE_MISSILE, ent); + else if (ent->v.solid == SOLID_TRIGGER || ent->v.solid == SOLID_NOT) + // only clip against bmodels + trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, MOVE_NOMONSTERS, ent); + else + trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, MOVE_NORMAL, ent); + + VectorCopy (trace.endpos, ent->v.origin); + SV_LinkEdict (ent, true); + + if (trace.ent) + SV_Impact (ent, trace.ent); + + return trace; +} + + +/* +============ +SV_Push + +============ +*/ +qboolean SV_Push (edict_t *pusher, vec3_t move) +{ + int i, e; + edict_t *check, *block; + vec3_t mins, maxs; + vec3_t pushorig; + int num_moved; + edict_t *moved_edict[MAX_EDICTS]; + vec3_t moved_from[MAX_EDICTS]; + + for (i=0 ; i<3 ; i++) + { + mins[i] = pusher->v.absmin[i] + move[i]; + maxs[i] = pusher->v.absmax[i] + move[i]; + } + + VectorCopy (pusher->v.origin, pushorig); + +// move the pusher to it's final position + + VectorAdd (pusher->v.origin, move, pusher->v.origin); + SV_LinkEdict (pusher, false); + +// see if any solid entities are inside the final position + num_moved = 0; + check = NEXT_EDICT(sv.edicts); + for (e=1 ; e<sv.num_edicts ; e++, check = NEXT_EDICT(check)) + { + if (check->free) + continue; + if (check->v.movetype == MOVETYPE_PUSH + || check->v.movetype == MOVETYPE_NONE + || check->v.movetype == MOVETYPE_NOCLIP) + continue; + + pusher->v.solid = SOLID_NOT; + block = SV_TestEntityPosition (check); + pusher->v.solid = SOLID_BSP; + if (block) + continue; + + // if the entity is standing on the pusher, it will definately be moved + if ( ! ( ((int)check->v.flags & FL_ONGROUND) + && PROG_TO_EDICT(check->v.groundentity) == pusher) ) + { + if ( check->v.absmin[0] >= maxs[0] + || check->v.absmin[1] >= maxs[1] + || check->v.absmin[2] >= maxs[2] + || check->v.absmax[0] <= mins[0] + || check->v.absmax[1] <= mins[1] + || check->v.absmax[2] <= mins[2] ) + continue; + + // see if the ent's bbox is inside the pusher's final position + if (!SV_TestEntityPosition (check)) + continue; + } + + VectorCopy (check->v.origin, moved_from[num_moved]); + moved_edict[num_moved] = check; + num_moved++; + + // try moving the contacted entity + VectorAdd (check->v.origin, move, check->v.origin); + block = SV_TestEntityPosition (check); + if (!block) + { // pushed ok + SV_LinkEdict (check, false); + continue; + } + + // if it is ok to leave in the old position, do it + VectorSubtract (check->v.origin, move, check->v.origin); + block = SV_TestEntityPosition (check); + if (!block) + { + num_moved--; + continue; + } + + // if it is still inside the pusher, block + if (check->v.mins[0] == check->v.maxs[0]) + { + SV_LinkEdict (check, false); + continue; + } + if (check->v.solid == SOLID_NOT || check->v.solid == SOLID_TRIGGER) + { // corpse + check->v.mins[0] = check->v.mins[1] = 0; + VectorCopy (check->v.mins, check->v.maxs); + SV_LinkEdict (check, false); + continue; + } + + VectorCopy (pushorig, pusher->v.origin); + SV_LinkEdict (pusher, false); + + // if the pusher has a "blocked" function, call it + // otherwise, just stay in place until the obstacle is gone + if (pusher->v.blocked) + { + pr_global_struct->self = EDICT_TO_PROG(pusher); + pr_global_struct->other = EDICT_TO_PROG(check); + PR_ExecuteProgram (pusher->v.blocked); + } + + // move back any entities we already moved + for (i=0 ; i<num_moved ; i++) + { + VectorCopy (moved_from[i], moved_edict[i]->v.origin); + SV_LinkEdict (moved_edict[i], false); + } + return false; + } + + return true; +} + +/* +============ +SV_PushMove + +============ +*/ +void SV_PushMove (edict_t *pusher, float movetime) +{ + int i; + vec3_t move; + + if (!pusher->v.velocity[0] && !pusher->v.velocity[1] && !pusher->v.velocity[2]) + { + pusher->v.ltime += movetime; + return; + } + + for (i=0 ; i<3 ; i++) + move[i] = pusher->v.velocity[i] * movetime; + + if (SV_Push (pusher, move)) + pusher->v.ltime += movetime; +} + + +/* +================ +SV_Physics_Pusher + +================ +*/ +void SV_Physics_Pusher (edict_t *ent) +{ + float thinktime; + float oldltime; + float movetime; +vec3_t oldorg, move; +float l; + + oldltime = ent->v.ltime; + + thinktime = ent->v.nextthink; + if (thinktime < ent->v.ltime + host_frametime) + { + movetime = thinktime - ent->v.ltime; + if (movetime < 0) + movetime = 0; + } + else + movetime = host_frametime; + + if (movetime) + { + SV_PushMove (ent, movetime); // advances ent->v.ltime if not blocked + } + + if (thinktime > oldltime && thinktime <= ent->v.ltime) + { +VectorCopy (ent->v.origin, oldorg); + ent->v.nextthink = 0; + pr_global_struct->time = sv.time; + pr_global_struct->self = EDICT_TO_PROG(ent); + pr_global_struct->other = EDICT_TO_PROG(sv.edicts); + PR_ExecuteProgram (ent->v.think); + if (ent->free) + return; +VectorSubtract (ent->v.origin, oldorg, move); + +l = Length(move); +if (l > 1.0/64) +{ +// Con_Printf ("**** snap: %f\n", Length (l)); + VectorCopy (oldorg, ent->v.origin); + SV_Push (ent, move); +} + + } + +} + + +/* +============= +SV_Physics_None + +Non moving objects can only think +============= +*/ +void SV_Physics_None (edict_t *ent) +{ +// regular thinking + SV_RunThink (ent); +} + +/* +============= +SV_Physics_Noclip + +A moving object that doesn't obey physics +============= +*/ +void SV_Physics_Noclip (edict_t *ent) +{ +// regular thinking + if (!SV_RunThink (ent)) + return; + + VectorMA (ent->v.angles, host_frametime, ent->v.avelocity, ent->v.angles); + VectorMA (ent->v.origin, host_frametime, ent->v.velocity, ent->v.origin); + + SV_LinkEdict (ent, false); +} + +/* +============================================================================== + +TOSS / BOUNCE + +============================================================================== +*/ + +/* +============= +SV_CheckWaterTransition + +============= +*/ +void SV_CheckWaterTransition (edict_t *ent) +{ + int cont; + + cont = SV_PointContents (ent->v.origin); + if (!ent->v.watertype) + { // just spawned here + ent->v.watertype = cont; + ent->v.waterlevel = 1; + return; + } + + if (cont <= CONTENTS_WATER) + { + if (ent->v.watertype == CONTENTS_EMPTY) + { // just crossed into water + SV_StartSound (ent, 0, "misc/h2ohit1.wav", 255, 1); + } + ent->v.watertype = cont; + ent->v.waterlevel = 1; + } + else + { + if (ent->v.watertype != CONTENTS_EMPTY) + { // just crossed into water + SV_StartSound (ent, 0, "misc/h2ohit1.wav", 255, 1); + } + ent->v.watertype = CONTENTS_EMPTY; + ent->v.waterlevel = cont; + } +} + +/* +============= +SV_Physics_Toss + +Toss, bounce, and fly movement. When onground, do nothing. +============= +*/ +void SV_Physics_Toss (edict_t *ent) +{ + trace_t trace; + vec3_t move; + float backoff; + +// regular thinking + if (!SV_RunThink (ent)) + return; + + if (ent->v.velocity[2] > 0) + ent->v.flags = (int)ent->v.flags & ~FL_ONGROUND; + +// if onground, return without moving + if ( ((int)ent->v.flags & FL_ONGROUND) ) + return; + + SV_CheckVelocity (ent); + +// add gravity + if (ent->v.movetype != MOVETYPE_FLY + && ent->v.movetype != MOVETYPE_FLYMISSILE) + SV_AddGravity (ent, 1.0); + +// move angles + VectorMA (ent->v.angles, host_frametime, ent->v.avelocity, ent->v.angles); + +// move origin + VectorScale (ent->v.velocity, host_frametime, move); + trace = SV_PushEntity (ent, move); + if (trace.fraction == 1) + return; + if (ent->free) + return; + + if (ent->v.movetype == MOVETYPE_BOUNCE) + backoff = 1.5; + else + backoff = 1; + + ClipVelocity (ent->v.velocity, trace.plane.normal, ent->v.velocity, backoff); + +// stop if on ground + if (trace.plane.normal[2] > 0.7) + { + if (ent->v.velocity[2] < 60 || ent->v.movetype != MOVETYPE_BOUNCE ) + { + ent->v.flags = (int)ent->v.flags | FL_ONGROUND; + ent->v.groundentity = EDICT_TO_PROG(trace.ent); + VectorCopy (vec3_origin, ent->v.velocity); + VectorCopy (vec3_origin, ent->v.avelocity); + } + } + +// check for in water + SV_CheckWaterTransition (ent); +} + +/* +=============================================================================== + +STEPPING MOVEMENT + +=============================================================================== +*/ + +/* +============= +SV_Physics_Step + +Monsters freefall when they don't have a ground entity, otherwise +all movement is done with discrete steps. + +This is also used for objects that have become still on the ground, but +will fall if the floor is pulled out from under them. +FIXME: is this true? +============= +*/ +void SV_Physics_Step (edict_t *ent) +{ + qboolean hitsound; + +// frefall if not onground + if ( ! ((int)ent->v.flags & (FL_ONGROUND | FL_FLY | FL_SWIM) ) ) + { + if (ent->v.velocity[2] < movevars.gravity*-0.1) + hitsound = true; + else + hitsound = false; + + SV_AddGravity (ent, 1.0); + SV_CheckVelocity (ent); + SV_FlyMove (ent, host_frametime, NULL); + SV_LinkEdict (ent, true); + + if ( (int)ent->v.flags & FL_ONGROUND ) // just hit ground + { + if (hitsound) + SV_StartSound (ent, 0, "demon/dland2.wav", 255, 1); + } + } + +// regular thinking + SV_RunThink (ent); + + SV_CheckWaterTransition (ent); +} + +//============================================================================ + +void SV_ProgStartFrame (void) +{ +// let the progs know that a new frame has started + pr_global_struct->self = EDICT_TO_PROG(sv.edicts); + pr_global_struct->other = EDICT_TO_PROG(sv.edicts); + pr_global_struct->time = sv.time; + PR_ExecuteProgram (pr_global_struct->StartFrame); +} + +/* +================ +SV_RunEntity + +================ +*/ +void SV_RunEntity (edict_t *ent) +{ + if (ent->v.lastruntime == (float)realtime) + return; + ent->v.lastruntime = (float)realtime; + + switch ( (int)ent->v.movetype) + { + case MOVETYPE_PUSH: + SV_Physics_Pusher (ent); + break; + case MOVETYPE_NONE: + SV_Physics_None (ent); + break; + case MOVETYPE_NOCLIP: + SV_Physics_Noclip (ent); + break; + case MOVETYPE_STEP: + SV_Physics_Step (ent); + break; + case MOVETYPE_TOSS: + case MOVETYPE_BOUNCE: + case MOVETYPE_FLY: + case MOVETYPE_FLYMISSILE: + SV_Physics_Toss (ent); + break; + default: + SV_Error ("SV_Physics: bad movetype %i", (int)ent->v.movetype); + } +} + +/* +================ +SV_RunNewmis + +================ +*/ +void SV_RunNewmis (void) +{ + edict_t *ent; + + if (!pr_global_struct->newmis) + return; + ent = PROG_TO_EDICT(pr_global_struct->newmis); + host_frametime = 0.05; + pr_global_struct->newmis = 0; + + SV_RunEntity (ent); +} + +/* +================ +SV_Physics + +================ +*/ +void SV_Physics (void) +{ + int i; + edict_t *ent; + static double old_time; + +// don't bother running a frame if sys_ticrate seconds haven't passed + host_frametime = realtime - old_time; + if (host_frametime < sv_mintic.value) + return; + if (host_frametime > sv_maxtic.value) + host_frametime = sv_maxtic.value; + old_time = realtime; + + pr_global_struct->frametime = host_frametime; + + SV_ProgStartFrame (); + +// +// treat each object in turn +// even the world gets a chance to think +// + ent = sv.edicts; + for (i=0 ; i<sv.num_edicts ; i++, ent = NEXT_EDICT(ent)) + { + if (ent->free) + continue; + + if (pr_global_struct->force_retouch) + SV_LinkEdict (ent, true); // force retouch even for stationary + + if (i > 0 && i <= MAX_CLIENTS) + continue; // clients are run directly from packets + + SV_RunEntity (ent); + SV_RunNewmis (); + } + + if (pr_global_struct->force_retouch) + pr_global_struct->force_retouch--; +} + +void SV_SetMoveVars(void) +{ + movevars.gravity = sv_gravity.value; + movevars.stopspeed = sv_stopspeed.value; + movevars.maxspeed = sv_maxspeed.value; + movevars.spectatormaxspeed = sv_spectatormaxspeed.value; + movevars.accelerate = sv_accelerate.value; + movevars.airaccelerate = sv_airaccelerate.value; + movevars.wateraccelerate = sv_wateraccelerate.value; + movevars.friction = sv_friction.value; + movevars.waterfriction = sv_waterfriction.value; + movevars.entgravity = 1.0; +} diff --git a/QW/server/sv_send.c b/QW/server/sv_send.c new file mode 100644 index 0000000..6d0172b --- /dev/null +++ b/QW/server/sv_send.c @@ -0,0 +1,806 @@ +/* +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. + +*/ +// sv_main.c -- server main program + +#include "qwsvdef.h" + +#define CHAN_AUTO 0 +#define CHAN_WEAPON 1 +#define CHAN_VOICE 2 +#define CHAN_ITEM 3 +#define CHAN_BODY 4 + +/* +============================================================================= + +Con_Printf redirection + +============================================================================= +*/ + +char outputbuf[8000]; + +redirect_t sv_redirected; + +extern cvar_t sv_phs; + +/* +================== +SV_FlushRedirect +================== +*/ +void SV_FlushRedirect (void) +{ + char send[8000+6]; + + if (sv_redirected == RD_PACKET) + { + send[0] = 0xff; + send[1] = 0xff; + send[2] = 0xff; + send[3] = 0xff; + send[4] = A2C_PRINT; + memcpy (send+5, outputbuf, strlen(outputbuf)+1); + + NET_SendPacket (strlen(send)+1, send, net_from); + } + else if (sv_redirected == RD_CLIENT) + { + ClientReliableWrite_Begin (host_client, svc_print, strlen(outputbuf)+3); + ClientReliableWrite_Byte (host_client, PRINT_HIGH); + ClientReliableWrite_String (host_client, outputbuf); + } + + // clear it + outputbuf[0] = 0; +} + + +/* +================== +SV_BeginRedirect + + Send Con_Printf data to the remote client + instead of the console +================== +*/ +void SV_BeginRedirect (redirect_t rd) +{ + sv_redirected = rd; + outputbuf[0] = 0; +} + +void SV_EndRedirect (void) +{ + SV_FlushRedirect (); + sv_redirected = RD_NONE; +} + + +/* +================ +Con_Printf + +Handles cursor positioning, line wrapping, etc +================ +*/ +#define MAXPRINTMSG 4096 +// FIXME: make a buffer size safe vsprintf? +void Con_Printf (char *fmt, ...) +{ + va_list argptr; + char msg[MAXPRINTMSG]; + + va_start (argptr,fmt); + vsprintf (msg,fmt,argptr); + va_end (argptr); + + // add to redirected message + if (sv_redirected) + { + if (strlen (msg) + strlen(outputbuf) > sizeof(outputbuf) - 1) + SV_FlushRedirect (); + strcat (outputbuf, msg); + return; + } + + Sys_Printf ("%s", msg); // also echo to debugging console + if (sv_logfile) + fprintf (sv_logfile, "%s", msg); +} + +/* +================ +Con_DPrintf + +A Con_Printf that only shows up if the "developer" cvar is set +================ +*/ +void Con_DPrintf (char *fmt, ...) +{ + va_list argptr; + char msg[MAXPRINTMSG]; + + if (!developer.value) + return; + + va_start (argptr,fmt); + vsprintf (msg,fmt,argptr); + va_end (argptr); + + Con_Printf ("%s", msg); +} + +/* +============================================================================= + +EVENT MESSAGES + +============================================================================= +*/ + +static void SV_PrintToClient(client_t *cl, int level, char *string) +{ + ClientReliableWrite_Begin (cl, svc_print, strlen(string)+3); + ClientReliableWrite_Byte (cl, level); + ClientReliableWrite_String (cl, string); +} + + +/* +================= +SV_ClientPrintf + +Sends text across to be displayed if the level passes +================= +*/ +void SV_ClientPrintf (client_t *cl, int level, char *fmt, ...) +{ + va_list argptr; + char string[1024]; + + if (level < cl->messagelevel) + return; + + va_start (argptr,fmt); + vsprintf (string, fmt,argptr); + va_end (argptr); + + SV_PrintToClient(cl, level, string); +} + +/* +================= +SV_BroadcastPrintf + +Sends text to all active clients +================= +*/ +void SV_BroadcastPrintf (int level, char *fmt, ...) +{ + va_list argptr; + char string[1024]; + client_t *cl; + int i; + + va_start (argptr,fmt); + vsprintf (string, fmt,argptr); + va_end (argptr); + + Sys_Printf ("%s", string); // print to the console + + for (i=0, cl = svs.clients ; i<MAX_CLIENTS ; i++, cl++) + { + if (level < cl->messagelevel) + continue; + if (!cl->state) + continue; + + SV_PrintToClient(cl, level, string); + } +} + +/* +================= +SV_BroadcastCommand + +Sends text to all active clients +================= +*/ +void SV_BroadcastCommand (char *fmt, ...) +{ + va_list argptr; + char string[1024]; + + if (!sv.state) + return; + va_start (argptr,fmt); + vsprintf (string, fmt,argptr); + va_end (argptr); + + MSG_WriteByte (&sv.reliable_datagram, svc_stufftext); + MSG_WriteString (&sv.reliable_datagram, string); +} + + +/* +================= +SV_Multicast + +Sends the contents of sv.multicast to a subset of the clients, +then clears sv.multicast. + +MULTICAST_ALL same as broadcast +MULTICAST_PVS send to clients potentially visible from org +MULTICAST_PHS send to clients potentially hearable from org +================= +*/ +void SV_Multicast (vec3_t origin, int to) +{ + client_t *client; + byte *mask; + mleaf_t *leaf; + int leafnum; + int j; + qboolean reliable; + + leaf = Mod_PointInLeaf (origin, sv.worldmodel); + if (!leaf) + leafnum = 0; + else + leafnum = leaf - sv.worldmodel->leafs; + + reliable = false; + + switch (to) + { + case MULTICAST_ALL_R: + reliable = true; // intentional fallthrough + case MULTICAST_ALL: + mask = sv.pvs; // leaf 0 is everything; + break; + + case MULTICAST_PHS_R: + reliable = true; // intentional fallthrough + case MULTICAST_PHS: + mask = sv.phs + leafnum * 4*((sv.worldmodel->numleafs+31)>>5); + break; + + case MULTICAST_PVS_R: + reliable = true; // intentional fallthrough + case MULTICAST_PVS: + mask = sv.pvs + leafnum * 4*((sv.worldmodel->numleafs+31)>>5); + break; + + default: + mask = NULL; + SV_Error ("SV_Multicast: bad to:%i", to); + } + + // send the data to all relevent clients + for (j = 0, client = svs.clients; j < MAX_CLIENTS; j++, client++) + { + if (client->state != cs_spawned) + continue; + + if (to == MULTICAST_PHS_R || to == MULTICAST_PHS) { + vec3_t delta; + VectorSubtract(origin, client->edict->v.origin, delta); + if (Length(delta) <= 1024) + goto inrange; + } + + leaf = Mod_PointInLeaf (client->edict->v.origin, sv.worldmodel); + if (leaf) + { + // -1 is because pvs rows are 1 based, not 0 based like leafs + leafnum = leaf - sv.worldmodel->leafs - 1; + if ( !(mask[leafnum>>3] & (1<<(leafnum&7)) ) ) + { +// Con_Printf ("supressed multicast\n"); + continue; + } + } + +inrange: + if (reliable) { + ClientReliableCheckBlock(client, sv.multicast.cursize); + ClientReliableWrite_SZ(client, sv.multicast.data, sv.multicast.cursize); + } else + SZ_Write (&client->datagram, sv.multicast.data, sv.multicast.cursize); + } + + SZ_Clear (&sv.multicast); +} + + +/* +================== +SV_StartSound + +Each entity can have eight independant sound sources, like voice, +weapon, feet, etc. + +Channel 0 is an auto-allocate channel, the others override anything +allready running on that entity/channel pair. + +An attenuation of 0 will play full volume everywhere in the level. +Larger attenuations will drop off. (max 4 attenuation) + +================== +*/ +void SV_StartSound (edict_t *entity, int channel, char *sample, int volume, + float attenuation) +{ + int sound_num; + int field_mask; + int i; + int ent; + vec3_t origin; + qboolean use_phs; + qboolean reliable = false; + + if (volume < 0 || volume > 255) + SV_Error ("SV_StartSound: volume = %i", volume); + + if (attenuation < 0 || attenuation > 4) + SV_Error ("SV_StartSound: attenuation = %f", attenuation); + + if (channel < 0 || channel > 15) + SV_Error ("SV_StartSound: channel = %i", channel); + +// find precache number for sound + for (sound_num=1 ; sound_num<MAX_SOUNDS + && sv.sound_precache[sound_num] ; sound_num++) + if (!strcmp(sample, sv.sound_precache[sound_num])) + break; + + if ( sound_num == MAX_SOUNDS || !sv.sound_precache[sound_num] ) + { + Con_Printf ("SV_StartSound: %s not precacheed\n", sample); + return; + } + + ent = NUM_FOR_EDICT(entity); + + if ((channel & 8) || !sv_phs.value) // no PHS flag + { + if (channel & 8) + reliable = true; // sounds that break the phs are reliable + use_phs = false; + channel &= 7; + } + else + use_phs = true; + +// if (channel == CHAN_BODY || channel == CHAN_VOICE) +// reliable = true; + + channel = (ent<<3) | channel; + + field_mask = 0; + if (volume != DEFAULT_SOUND_PACKET_VOLUME) + channel |= SND_VOLUME; + if (attenuation != DEFAULT_SOUND_PACKET_ATTENUATION) + channel |= SND_ATTENUATION; + + // use the entity origin unless it is a bmodel + if (entity->v.solid == SOLID_BSP) + { + for (i=0 ; i<3 ; i++) + origin[i] = entity->v.origin[i]+0.5*(entity->v.mins[i]+entity->v.maxs[i]); + } + else + { + VectorCopy (entity->v.origin, origin); + } + + MSG_WriteByte (&sv.multicast, svc_sound); + MSG_WriteShort (&sv.multicast, channel); + if (channel & SND_VOLUME) + MSG_WriteByte (&sv.multicast, volume); + if (channel & SND_ATTENUATION) + MSG_WriteByte (&sv.multicast, attenuation*64); + MSG_WriteByte (&sv.multicast, sound_num); + for (i=0 ; i<3 ; i++) + MSG_WriteCoord (&sv.multicast, origin[i]); + + if (use_phs) + SV_Multicast (origin, reliable ? MULTICAST_PHS_R : MULTICAST_PHS); + else + SV_Multicast (origin, reliable ? MULTICAST_ALL_R : MULTICAST_ALL); +} + + +/* +=============================================================================== + +FRAME UPDATES + +=============================================================================== +*/ + +int sv_nailmodel, sv_supernailmodel, sv_playermodel; + +void SV_FindModelNumbers (void) +{ + int i; + + sv_nailmodel = -1; + sv_supernailmodel = -1; + sv_playermodel = -1; + + for (i=0 ; i<MAX_MODELS ; i++) + { + if (!sv.model_precache[i]) + break; + if (!strcmp(sv.model_precache[i],"progs/spike.mdl")) + sv_nailmodel = i; + if (!strcmp(sv.model_precache[i],"progs/s_spike.mdl")) + sv_supernailmodel = i; + if (!strcmp(sv.model_precache[i],"progs/player.mdl")) + sv_playermodel = i; + } +} + + +/* +================== +SV_WriteClientdataToMessage + +================== +*/ +void SV_WriteClientdataToMessage (client_t *client, sizebuf_t *msg) +{ + int i; + edict_t *other; + edict_t *ent; + + ent = client->edict; + + // send the chokecount for r_netgraph + if (client->chokecount) + { + MSG_WriteByte (msg, svc_chokecount); + MSG_WriteByte (msg, client->chokecount); + client->chokecount = 0; + } + + // send a damage message if the player got hit this frame + if (ent->v.dmg_take || ent->v.dmg_save) + { + other = PROG_TO_EDICT(ent->v.dmg_inflictor); + MSG_WriteByte (msg, svc_damage); + MSG_WriteByte (msg, ent->v.dmg_save); + MSG_WriteByte (msg, ent->v.dmg_take); + for (i=0 ; i<3 ; i++) + MSG_WriteCoord (msg, other->v.origin[i] + 0.5*(other->v.mins[i] + other->v.maxs[i])); + + ent->v.dmg_take = 0; + ent->v.dmg_save = 0; + } + + // a fixangle might get lost in a dropped packet. Oh well. + if ( ent->v.fixangle ) + { + MSG_WriteByte (msg, svc_setangle); + for (i=0 ; i < 3 ; i++) + MSG_WriteAngle (msg, ent->v.angles[i] ); + ent->v.fixangle = 0; + } +} + +/* +======================= +SV_UpdateClientStats + +Performs a delta update of the stats array. This should only be performed +when a reliable message can be delivered this frame. +======================= +*/ +void SV_UpdateClientStats (client_t *client) +{ + edict_t *ent; + int stats[MAX_CL_STATS]; + int i; + + ent = client->edict; + memset (stats, 0, sizeof(stats)); + + // if we are a spectator and we are tracking a player, we get his stats + // so our status bar reflects his + if (client->spectator && client->spec_track > 0) + ent = svs.clients[client->spec_track - 1].edict; + + stats[STAT_HEALTH] = ent->v.health; + stats[STAT_WEAPON] = SV_ModelIndex(PR_GetString(ent->v.weaponmodel)); + stats[STAT_AMMO] = ent->v.currentammo; + stats[STAT_ARMOR] = ent->v.armorvalue; + stats[STAT_SHELLS] = ent->v.ammo_shells; + stats[STAT_NAILS] = ent->v.ammo_nails; + stats[STAT_ROCKETS] = ent->v.ammo_rockets; + stats[STAT_CELLS] = ent->v.ammo_cells; + if (!client->spectator) + stats[STAT_ACTIVEWEAPON] = ent->v.weapon; + // stuff the sigil bits into the high bits of items for sbar + stats[STAT_ITEMS] = (int)ent->v.items | ((int)pr_global_struct->serverflags << 28); + + for (i=0 ; i<MAX_CL_STATS ; i++) + if (stats[i] != client->stats[i]) + { + client->stats[i] = stats[i]; + if (stats[i] >=0 && stats[i] <= 255) + { + ClientReliableWrite_Begin(client, svc_updatestat, 3); + ClientReliableWrite_Byte(client, i); + ClientReliableWrite_Byte(client, stats[i]); + } + else + { + ClientReliableWrite_Begin(client, svc_updatestatlong, 6); + ClientReliableWrite_Byte(client, i); + ClientReliableWrite_Long(client, stats[i]); + } + } +} + +/* +======================= +SV_SendClientDatagram +======================= +*/ +qboolean SV_SendClientDatagram (client_t *client) +{ + byte buf[MAX_DATAGRAM]; + sizebuf_t msg; + + msg.data = buf; + msg.maxsize = sizeof(buf); + msg.cursize = 0; + msg.allowoverflow = true; + msg.overflowed = false; + + // add the client specific data to the datagram + SV_WriteClientdataToMessage (client, &msg); + + // send over all the objects that are in the PVS + // this will include clients, a packetentities, and + // possibly a nails update + SV_WriteEntitiesToClient (client, &msg); + + // copy the accumulated multicast datagram + // for this client out to the message + if (client->datagram.overflowed) + Con_Printf ("WARNING: datagram overflowed for %s\n", client->name); + else + SZ_Write (&msg, client->datagram.data, client->datagram.cursize); + SZ_Clear (&client->datagram); + + // send deltas over reliable stream + if (Netchan_CanReliable (&client->netchan)) + SV_UpdateClientStats (client); + + if (msg.overflowed) + { + Con_Printf ("WARNING: msg overflowed for %s\n", client->name); + SZ_Clear (&msg); + } + + // send the datagram + Netchan_Transmit (&client->netchan, msg.cursize, buf); + + return true; +} + +/* +======================= +SV_UpdateToReliableMessages +======================= +*/ +void SV_UpdateToReliableMessages (void) +{ + int i, j; + client_t *client; + eval_t *val; + edict_t *ent; + +// check for changes to be sent over the reliable streams to all clients + for (i=0, host_client = svs.clients ; i<MAX_CLIENTS ; i++, host_client++) + { + if (host_client->state != cs_spawned) + continue; + if (host_client->sendinfo) + { + host_client->sendinfo = false; + SV_FullClientUpdate (host_client, &sv.reliable_datagram); + } + if (host_client->old_frags != host_client->edict->v.frags) + { + for (j=0, client = svs.clients ; j<MAX_CLIENTS ; j++, client++) + { + if (client->state < cs_connected) + continue; + ClientReliableWrite_Begin(client, svc_updatefrags, 4); + ClientReliableWrite_Byte(client, i); + ClientReliableWrite_Short(client, host_client->edict->v.frags); + } + + host_client->old_frags = host_client->edict->v.frags; + } + + // maxspeed/entgravity changes + ent = host_client->edict; + + val = GetEdictFieldValue(ent, "gravity"); + if (val && host_client->entgravity != val->_float) { + host_client->entgravity = val->_float; + ClientReliableWrite_Begin(host_client, svc_entgravity, 5); + ClientReliableWrite_Float(host_client, host_client->entgravity); + } + val = GetEdictFieldValue(ent, "maxspeed"); + if (val && host_client->maxspeed != val->_float) { + host_client->maxspeed = val->_float; + ClientReliableWrite_Begin(host_client, svc_maxspeed, 5); + ClientReliableWrite_Float(host_client, host_client->maxspeed); + } + + } + + if (sv.datagram.overflowed) + SZ_Clear (&sv.datagram); + + // append the broadcast messages to each client messages + for (j=0, client = svs.clients ; j<MAX_CLIENTS ; j++, client++) + { + if (client->state < cs_connected) + continue; // reliables go to all connected or spawned + + ClientReliableCheckBlock(client, sv.reliable_datagram.cursize); + ClientReliableWrite_SZ(client, sv.reliable_datagram.data, sv.reliable_datagram.cursize); + + if (client->state != cs_spawned) + continue; // datagrams only go to spawned + SZ_Write (&client->datagram + , sv.datagram.data + , sv.datagram.cursize); + } + + SZ_Clear (&sv.reliable_datagram); + SZ_Clear (&sv.datagram); +} + +#ifdef _WIN32 +#pragma optimize( "", off ) +#endif + + + +/* +======================= +SV_SendClientMessages +======================= +*/ +void SV_SendClientMessages (void) +{ + int i, j; + client_t *c; + +// update frags, names, etc + SV_UpdateToReliableMessages (); + +// build individual updates + for (i=0, c = svs.clients ; i<MAX_CLIENTS ; i++, c++) + { + if (!c->state) + continue; + + if (c->drop) { + SV_DropClient(c); + c->drop = false; + continue; + } + + // check to see if we have a backbuf to stick in the reliable + if (c->num_backbuf) { + // will it fit? + if (c->netchan.message.cursize + c->backbuf_size[0] < + c->netchan.message.maxsize) { + + Con_DPrintf("%s: backbuf %d bytes\n", + c->name, c->backbuf_size[0]); + + // it'll fit + SZ_Write(&c->netchan.message, c->backbuf_data[0], + c->backbuf_size[0]); + + //move along, move along + for (j = 1; j < c->num_backbuf; j++) { + memcpy(c->backbuf_data[j - 1], c->backbuf_data[j], + c->backbuf_size[j]); + c->backbuf_size[j - 1] = c->backbuf_size[j]; + } + + c->num_backbuf--; + if (c->num_backbuf) { + memset(&c->backbuf, 0, sizeof(c->backbuf)); + c->backbuf.data = c->backbuf_data[c->num_backbuf - 1]; + c->backbuf.cursize = c->backbuf_size[c->num_backbuf - 1]; + c->backbuf.maxsize = sizeof(c->backbuf_data[c->num_backbuf - 1]); + } + } + } + + // if the reliable message overflowed, + // drop the client + if (c->netchan.message.overflowed) + { + SZ_Clear (&c->netchan.message); + SZ_Clear (&c->datagram); + SV_BroadcastPrintf (PRINT_HIGH, "%s overflowed\n", c->name); + Con_Printf ("WARNING: reliable overflow for %s\n",c->name); + SV_DropClient (c); + c->send_message = true; + c->netchan.cleartime = 0; // don't choke this message + } + + // only send messages if the client has sent one + // and the bandwidth is not choked + if (!c->send_message) + continue; + c->send_message = false; // try putting this after choke? + if (!sv.paused && !Netchan_CanPacket (&c->netchan)) + { + c->chokecount++; + continue; // bandwidth choke + } + + if (c->state == cs_spawned) + SV_SendClientDatagram (c); + else + Netchan_Transmit (&c->netchan, 0, NULL); // just update reliable + + } +} + +#ifdef _WIN32 +#pragma optimize( "", on ) +#endif + + + +/* +======================= +SV_SendMessagesToAll + +FIXME: does this sequence right? +======================= +*/ +void SV_SendMessagesToAll (void) +{ + int i; + client_t *c; + + for (i=0, c = svs.clients ; i<MAX_CLIENTS ; i++, c++) + if (c->state) // FIXME: should this only send to active? + c->send_message = true; + + SV_SendClientMessages (); +} + diff --git a/QW/server/sv_user.c b/QW/server/sv_user.c new file mode 100644 index 0000000..a8e5570 --- /dev/null +++ b/QW/server/sv_user.c @@ -0,0 +1,1702 @@ +/* +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. + +*/ +// sv_user.c -- server code for moving users + +#include "qwsvdef.h" + +edict_t *sv_player; + +usercmd_t cmd; + +cvar_t cl_rollspeed = {"cl_rollspeed", "200"}; +cvar_t cl_rollangle = {"cl_rollangle", "2.0"}; +cvar_t sv_spectalk = {"sv_spectalk", "1"}; + +cvar_t sv_mapcheck = {"sv_mapcheck", "1"}; + +extern vec3_t player_mins; + +extern int fp_messages, fp_persecond, fp_secondsdead; +extern char fp_msg[]; +extern cvar_t pausable; + +/* +============================================================ + +USER STRINGCMD EXECUTION + +host_client and sv_player will be valid. +============================================================ +*/ + +/* +================ +SV_New_f + +Sends the first message from the server to a connected client. +This will be sent on the initial connection and upon each server load. +================ +*/ +void SV_New_f (void) +{ + char *gamedir; + int playernum; + + if (host_client->state == cs_spawned) + return; + + host_client->state = cs_connected; + host_client->connection_started = realtime; + + // send the info about the new client to all connected clients +// SV_FullClientUpdate (host_client, &sv.reliable_datagram); +// host_client->sendinfo = true; + + gamedir = Info_ValueForKey (svs.info, "*gamedir"); + if (!gamedir[0]) + gamedir = "qw"; + +//NOTE: This doesn't go through ClientReliableWrite since it's before the user +//spawns. These functions are written to not overflow + if (host_client->num_backbuf) { + Con_Printf("WARNING %s: [SV_New] Back buffered (%d0, clearing", host_client->name, host_client->netchan.message.cursize); + host_client->num_backbuf = 0; + SZ_Clear(&host_client->netchan.message); + } + + // send the serverdata + MSG_WriteByte (&host_client->netchan.message, svc_serverdata); + MSG_WriteLong (&host_client->netchan.message, PROTOCOL_VERSION); + MSG_WriteLong (&host_client->netchan.message, svs.spawncount); + MSG_WriteString (&host_client->netchan.message, gamedir); + + playernum = NUM_FOR_EDICT(host_client->edict)-1; + if (host_client->spectator) + playernum |= 128; + MSG_WriteByte (&host_client->netchan.message, playernum); + + // send full levelname + MSG_WriteString (&host_client->netchan.message, PR_GetString(sv.edicts->v.message)); + + // send the movevars + MSG_WriteFloat(&host_client->netchan.message, movevars.gravity); + MSG_WriteFloat(&host_client->netchan.message, movevars.stopspeed); + MSG_WriteFloat(&host_client->netchan.message, movevars.maxspeed); + MSG_WriteFloat(&host_client->netchan.message, movevars.spectatormaxspeed); + MSG_WriteFloat(&host_client->netchan.message, movevars.accelerate); + MSG_WriteFloat(&host_client->netchan.message, movevars.airaccelerate); + MSG_WriteFloat(&host_client->netchan.message, movevars.wateraccelerate); + MSG_WriteFloat(&host_client->netchan.message, movevars.friction); + MSG_WriteFloat(&host_client->netchan.message, movevars.waterfriction); + MSG_WriteFloat(&host_client->netchan.message, movevars.entgravity); + + // send music + MSG_WriteByte (&host_client->netchan.message, svc_cdtrack); + MSG_WriteByte (&host_client->netchan.message, sv.edicts->v.sounds); + + // send server info string + MSG_WriteByte (&host_client->netchan.message, svc_stufftext); + MSG_WriteString (&host_client->netchan.message, va("fullserverinfo \"%s\"\n", svs.info) ); +} + +/* +================== +SV_Soundlist_f +================== +*/ +void SV_Soundlist_f (void) +{ + char **s; + int n; + + if (host_client->state != cs_connected) + { + Con_Printf ("soundlist not valid -- allready spawned\n"); + return; + } + + // handle the case of a level changing while a client was connecting + if ( atoi(Cmd_Argv(1)) != svs.spawncount ) + { + Con_Printf ("SV_Soundlist_f from different level\n"); + SV_New_f (); + return; + } + + n = atoi(Cmd_Argv(2)); + +//NOTE: This doesn't go through ClientReliableWrite since it's before the user +//spawns. These functions are written to not overflow + if (host_client->num_backbuf) { + Con_Printf("WARNING %s: [SV_Soundlist] Back buffered (%d0, clearing", host_client->name, host_client->netchan.message.cursize); + host_client->num_backbuf = 0; + SZ_Clear(&host_client->netchan.message); + } + + MSG_WriteByte (&host_client->netchan.message, svc_soundlist); + MSG_WriteByte (&host_client->netchan.message, n); + for (s = sv.sound_precache+1 + n ; + *s && host_client->netchan.message.cursize < (MAX_MSGLEN/2); + s++, n++) + MSG_WriteString (&host_client->netchan.message, *s); + + MSG_WriteByte (&host_client->netchan.message, 0); + + // next msg + if (*s) + MSG_WriteByte (&host_client->netchan.message, n); + else + MSG_WriteByte (&host_client->netchan.message, 0); +} + +/* +================== +SV_Modellist_f +================== +*/ +void SV_Modellist_f (void) +{ + char **s; + int n; + + if (host_client->state != cs_connected) + { + Con_Printf ("modellist not valid -- allready spawned\n"); + return; + } + + // handle the case of a level changing while a client was connecting + if ( atoi(Cmd_Argv(1)) != svs.spawncount ) + { + Con_Printf ("SV_Modellist_f from different level\n"); + SV_New_f (); + return; + } + + n = atoi(Cmd_Argv(2)); + +//NOTE: This doesn't go through ClientReliableWrite since it's before the user +//spawns. These functions are written to not overflow + if (host_client->num_backbuf) { + Con_Printf("WARNING %s: [SV_Modellist] Back buffered (%d0, clearing", host_client->name, host_client->netchan.message.cursize); + host_client->num_backbuf = 0; + SZ_Clear(&host_client->netchan.message); + } + + MSG_WriteByte (&host_client->netchan.message, svc_modellist); + MSG_WriteByte (&host_client->netchan.message, n); + for (s = sv.model_precache+1+n ; + *s && host_client->netchan.message.cursize < (MAX_MSGLEN/2); + s++, n++) + MSG_WriteString (&host_client->netchan.message, *s); + MSG_WriteByte (&host_client->netchan.message, 0); + + // next msg + if (*s) + MSG_WriteByte (&host_client->netchan.message, n); + else + MSG_WriteByte (&host_client->netchan.message, 0); +} + +/* +================== +SV_PreSpawn_f +================== +*/ +void SV_PreSpawn_f (void) +{ + unsigned buf; + unsigned check; + + if (host_client->state != cs_connected) + { + Con_Printf ("prespawn not valid -- allready spawned\n"); + return; + } + + // handle the case of a level changing while a client was connecting + if ( atoi(Cmd_Argv(1)) != svs.spawncount ) + { + Con_Printf ("SV_PreSpawn_f from different level\n"); + SV_New_f (); + return; + } + + buf = atoi(Cmd_Argv(2)); + if (buf >= sv.num_signon_buffers) + buf = 0; + + if (!buf) { + // should be three numbers following containing checksums + check = atoi(Cmd_Argv(3)); + +// Con_DPrintf("Client check = %d\n", check); + + if (sv_mapcheck.value && check != sv.worldmodel->checksum && + check != sv.worldmodel->checksum2) { + SV_ClientPrintf (host_client, PRINT_HIGH, + "Map model file does not match (%s), %i != %i/%i.\n" + "You may need a new version of the map, or the proper install files.\n", + sv.modelname, check, sv.worldmodel->checksum, sv.worldmodel->checksum2); + SV_DropClient (host_client); + return; + } + host_client->checksum = check; + } + +//NOTE: This doesn't go through ClientReliableWrite since it's before the user +//spawns. These functions are written to not overflow + if (host_client->num_backbuf) { + Con_Printf("WARNING %s: [SV_PreSpawn] Back buffered (%d0, clearing", host_client->name, host_client->netchan.message.cursize); + host_client->num_backbuf = 0; + SZ_Clear(&host_client->netchan.message); + } + + SZ_Write (&host_client->netchan.message, + sv.signon_buffers[buf], + sv.signon_buffer_size[buf]); + + buf++; + if (buf == sv.num_signon_buffers) + { // all done prespawning + MSG_WriteByte (&host_client->netchan.message, svc_stufftext); + MSG_WriteString (&host_client->netchan.message, va("cmd spawn %i 0\n",svs.spawncount) ); + } + else + { // need to prespawn more + MSG_WriteByte (&host_client->netchan.message, svc_stufftext); + MSG_WriteString (&host_client->netchan.message, + va("cmd prespawn %i %i\n", svs.spawncount, buf) ); + } +} + +/* +================== +SV_Spawn_f +================== +*/ +void SV_Spawn_f (void) +{ + int i; + client_t *client; + edict_t *ent; + eval_t *val; + int n; + + if (host_client->state != cs_connected) + { + Con_Printf ("Spawn not valid -- allready spawned\n"); + return; + } + +// handle the case of a level changing while a client was connecting + if ( atoi(Cmd_Argv(1)) != svs.spawncount ) + { + Con_Printf ("SV_Spawn_f from different level\n"); + SV_New_f (); + return; + } + + n = atoi(Cmd_Argv(2)); + + // make sure n is valid + if ( n < 0 || n > MAX_CLIENTS ) + { + Con_Printf ("SV_Spawn_f invalid client start\n"); + SV_New_f (); + return; + } + + + +// send all current names, colors, and frag counts + // FIXME: is this a good thing? + SZ_Clear (&host_client->netchan.message); + +// send current status of all other players + + // normally this could overflow, but no need to check due to backbuf + for (i=n, client = svs.clients + n ; i<MAX_CLIENTS ; i++, client++) + SV_FullClientUpdateToClient (client, host_client); + +// send all current light styles + for (i=0 ; i<MAX_LIGHTSTYLES ; i++) + { + ClientReliableWrite_Begin (host_client, svc_lightstyle, + 3 + (sv.lightstyles[i] ? strlen(sv.lightstyles[i]) : 1)); + ClientReliableWrite_Byte (host_client, (char)i); + ClientReliableWrite_String (host_client, sv.lightstyles[i]); + } + + // set up the edict + ent = host_client->edict; + + memset (&ent->v, 0, progs->entityfields * 4); + ent->v.colormap = NUM_FOR_EDICT(ent); + ent->v.team = 0; // FIXME + ent->v.netname = PR_SetString(host_client->name); + + host_client->entgravity = 1.0; + val = GetEdictFieldValue(ent, "gravity"); + if (val) + val->_float = 1.0; + host_client->maxspeed = sv_maxspeed.value; + val = GetEdictFieldValue(ent, "maxspeed"); + if (val) + val->_float = sv_maxspeed.value; + +// +// force stats to be updated +// + memset (host_client->stats, 0, sizeof(host_client->stats)); + + ClientReliableWrite_Begin (host_client, svc_updatestatlong, 6); + ClientReliableWrite_Byte (host_client, STAT_TOTALSECRETS); + ClientReliableWrite_Long (host_client, pr_global_struct->total_secrets); + + ClientReliableWrite_Begin (host_client, svc_updatestatlong, 6); + ClientReliableWrite_Byte (host_client, STAT_TOTALMONSTERS); + ClientReliableWrite_Long (host_client, pr_global_struct->total_monsters); + + ClientReliableWrite_Begin (host_client, svc_updatestatlong, 6); + ClientReliableWrite_Byte (host_client, STAT_SECRETS); + ClientReliableWrite_Long (host_client, pr_global_struct->found_secrets); + + ClientReliableWrite_Begin (host_client, svc_updatestatlong, 6); + ClientReliableWrite_Byte (host_client, STAT_MONSTERS); + ClientReliableWrite_Long (host_client, pr_global_struct->killed_monsters); + + // get the client to check and download skins + // when that is completed, a begin command will be issued + ClientReliableWrite_Begin (host_client, svc_stufftext, 8); + ClientReliableWrite_String (host_client, "skins\n" ); + +} + +/* +================== +SV_SpawnSpectator +================== +*/ +void SV_SpawnSpectator (void) +{ + int i; + edict_t *e; + + VectorCopy (vec3_origin, sv_player->v.origin); + VectorCopy (vec3_origin, sv_player->v.view_ofs); + sv_player->v.view_ofs[2] = 22; + + // search for an info_playerstart to spawn the spectator at + for (i=MAX_CLIENTS-1 ; i<sv.num_edicts ; i++) + { + e = EDICT_NUM(i); + if (!strcmp(PR_GetString(e->v.classname), "info_player_start")) + { + VectorCopy (e->v.origin, sv_player->v.origin); + return; + } + } + +} + +/* +================== +SV_Begin_f +================== +*/ +void SV_Begin_f (void) +{ + unsigned pmodel = 0, emodel = 0; + int i; + + if (host_client->state == cs_spawned) + return; // don't begin again + + host_client->state = cs_spawned; + + // handle the case of a level changing while a client was connecting + if ( atoi(Cmd_Argv(1)) != svs.spawncount ) + { + Con_Printf ("SV_Begin_f from different level\n"); + SV_New_f (); + return; + } + + if (host_client->spectator) + { + SV_SpawnSpectator (); + + if (SpectatorConnect) { + // copy spawn parms out of the client_t + for (i=0 ; i< NUM_SPAWN_PARMS ; i++) + (&pr_global_struct->parm1)[i] = host_client->spawn_parms[i]; + + // call the spawn function + pr_global_struct->time = sv.time; + pr_global_struct->self = EDICT_TO_PROG(sv_player); + PR_ExecuteProgram (SpectatorConnect); + } + } + else + { + // copy spawn parms out of the client_t + for (i=0 ; i< NUM_SPAWN_PARMS ; i++) + (&pr_global_struct->parm1)[i] = host_client->spawn_parms[i]; + + // call the spawn function + pr_global_struct->time = sv.time; + pr_global_struct->self = EDICT_TO_PROG(sv_player); + PR_ExecuteProgram (pr_global_struct->ClientConnect); + + // actually spawn the player + pr_global_struct->time = sv.time; + pr_global_struct->self = EDICT_TO_PROG(sv_player); + PR_ExecuteProgram (pr_global_struct->PutClientInServer); + } + + // clear the net statistics, because connecting gives a bogus picture + host_client->netchan.frame_latency = 0; + host_client->netchan.frame_rate = 0; + host_client->netchan.drop_count = 0; + host_client->netchan.good_count = 0; + + //check he's not cheating + + pmodel = atoi(Info_ValueForKey (host_client->userinfo, "pmodel")); + emodel = atoi(Info_ValueForKey (host_client->userinfo, "emodel")); + + if (pmodel != sv.model_player_checksum || + emodel != sv.eyes_player_checksum) + SV_BroadcastPrintf (PRINT_HIGH, "%s WARNING: non standard player/eyes model detected\n", host_client->name); + + // if we are paused, tell the client + if (sv.paused) { + ClientReliableWrite_Begin (host_client, svc_setpause, 2); + ClientReliableWrite_Byte (host_client, sv.paused); + SV_ClientPrintf(host_client, PRINT_HIGH, "Server is paused.\n"); + } + +#if 0 +// +// send a fixangle over the reliable channel to make sure it gets there +// Never send a roll angle, because savegames can catch the server +// in a state where it is expecting the client to correct the angle +// and it won't happen if the game was just loaded, so you wind up +// with a permanent head tilt + ent = EDICT_NUM( 1 + (host_client - svs.clients) ); + MSG_WriteByte (&host_client->netchan.message, svc_setangle); + for (i=0 ; i < 2 ; i++) + MSG_WriteAngle (&host_client->netchan.message, ent->v.angles[i] ); + MSG_WriteAngle (&host_client->netchan.message, 0 ); +#endif +} + +//============================================================================= + +/* +================== +SV_NextDownload_f +================== +*/ +void SV_NextDownload_f (void) +{ + byte buffer[1024]; + int r; + int percent; + int size; + + if (!host_client->download) + return; + + r = host_client->downloadsize - host_client->downloadcount; + if (r > 768) + r = 768; + r = fread (buffer, 1, r, host_client->download); + ClientReliableWrite_Begin (host_client, svc_download, 6+r); + ClientReliableWrite_Short (host_client, r); + + host_client->downloadcount += r; + size = host_client->downloadsize; + if (!size) + size = 1; + percent = host_client->downloadcount*100/size; + ClientReliableWrite_Byte (host_client, percent); + ClientReliableWrite_SZ (host_client, buffer, r); + + if (host_client->downloadcount != host_client->downloadsize) + return; + + fclose (host_client->download); + host_client->download = NULL; + +} + +void OutofBandPrintf(netadr_t where, char *fmt, ...) +{ + va_list argptr; + char send[1024]; + + send[0] = 0xff; + send[1] = 0xff; + send[2] = 0xff; + send[3] = 0xff; + send[4] = A2C_PRINT; + va_start (argptr, fmt); + vsprintf (send+5, fmt, argptr); + va_end (argptr); + + NET_SendPacket (strlen(send)+1, send, where); +} + +/* +================== +SV_NextUpload +================== +*/ +void SV_NextUpload (void) +{ + byte buffer[1024]; + int r; + int percent; + int size; + client_t *client; + + if (!*host_client->uploadfn) { + SV_ClientPrintf(host_client, PRINT_HIGH, "Upload denied\n"); + ClientReliableWrite_Begin (host_client, svc_stufftext, 8); + ClientReliableWrite_String (host_client, "stopul"); + + // suck out rest of packet + size = MSG_ReadShort (); MSG_ReadByte (); + msg_readcount += size; + return; + } + + size = MSG_ReadShort (); + percent = MSG_ReadByte (); + + if (!host_client->upload) + { + host_client->upload = fopen(host_client->uploadfn, "wb"); + if (!host_client->upload) { + Sys_Printf("Can't create %s\n", host_client->uploadfn); + ClientReliableWrite_Begin (host_client, svc_stufftext, 8); + ClientReliableWrite_String (host_client, "stopul"); + *host_client->uploadfn = 0; + return; + } + Sys_Printf("Receiving %s from %d...\n", host_client->uploadfn, host_client->userid); + if (host_client->remote_snap) + OutofBandPrintf(host_client->snap_from, "Server receiving %s from %d...\n", host_client->uploadfn, host_client->userid); + } + + fwrite (net_message.data + msg_readcount, 1, size, host_client->upload); + msg_readcount += size; + +Con_DPrintf ("UPLOAD: %d received\n", size); + + if (percent != 100) { + ClientReliableWrite_Begin (host_client, svc_stufftext, 8); + ClientReliableWrite_String (host_client, "nextul\n"); + } else { + fclose (host_client->upload); + host_client->upload = NULL; + + Sys_Printf("%s upload completed.\n", host_client->uploadfn); + + if (host_client->remote_snap) { + char *p; + + if ((p = strchr(host_client->uploadfn, '/')) != NULL) + p++; + else + p = host_client->uploadfn; + OutofBandPrintf(host_client->snap_from, "%s upload completed.\nTo download, enter:\ndownload %s\n", + host_client->uploadfn, p); + } + } + +} + +/* +================== +SV_BeginDownload_f +================== +*/ +void SV_BeginDownload_f(void) +{ + char *name; + extern cvar_t allow_download; + extern cvar_t allow_download_skins; + extern cvar_t allow_download_models; + extern cvar_t allow_download_sounds; + extern cvar_t allow_download_maps; + extern int file_from_pak; // ZOID did file come from pak? + + name = Cmd_Argv(1); +// hacked by zoid to allow more conrol over download + // first off, no .. or global allow check + if (strstr (name, "..") || !allow_download.value + // leading dot is no good + || *name == '.' + // leading slash bad as well, must be in subdir + || *name == '/' + // next up, skin check + || (strncmp(name, "skins/", 6) == 0 && !allow_download_skins.value) + // now models + || (strncmp(name, "progs/", 6) == 0 && !allow_download_models.value) + // now sounds + || (strncmp(name, "sound/", 6) == 0 && !allow_download_sounds.value) + // now maps (note special case for maps, must not be in pak) + || (strncmp(name, "maps/", 6) == 0 && !allow_download_maps.value) + // MUST be in a subdirectory + || !strstr (name, "/") ) + { // don't allow anything with .. path + ClientReliableWrite_Begin (host_client, svc_download, 4); + ClientReliableWrite_Short (host_client, -1); + ClientReliableWrite_Byte (host_client, 0); + return; + } + + if (host_client->download) { + fclose (host_client->download); + host_client->download = NULL; + } + + // lowercase name (needed for casesen file systems) + { + char *p; + + for (p = name; *p; p++) + *p = (char)tolower(*p); + } + + + host_client->downloadsize = COM_FOpenFile (name, &host_client->download); + host_client->downloadcount = 0; + + if (!host_client->download + // special check for maps, if it came from a pak file, don't allow + // download ZOID + || (strncmp(name, "maps/", 5) == 0 && file_from_pak)) + { + if (host_client->download) { + fclose(host_client->download); + host_client->download = NULL; + } + + Sys_Printf ("Couldn't download %s to %s\n", name, host_client->name); + ClientReliableWrite_Begin (host_client, svc_download, 4); + ClientReliableWrite_Short (host_client, -1); + ClientReliableWrite_Byte (host_client, 0); + return; + } + + SV_NextDownload_f (); + Sys_Printf ("Downloading %s to %s\n", name, host_client->name); +} + +//============================================================================= + +/* +================== +SV_Say +================== +*/ +void SV_Say (qboolean team) +{ + client_t *client; + int j, tmp; + char *p; + char text[2048]; + char t1[32], *t2; + + if (Cmd_Argc () < 2) + return; + + if (team) + { + strncpy (t1, Info_ValueForKey (host_client->userinfo, "team"), 31); + t1[31] = 0; + } + + if (host_client->spectator && (!sv_spectalk.value || team)) + sprintf (text, "[SPEC] %s: ", host_client->name); + else if (team) + sprintf (text, "(%s): ", host_client->name); + else { + sprintf (text, "%s: ", host_client->name); + } + + if (fp_messages) { + if (!sv.paused && realtime<host_client->lockedtill) { + SV_ClientPrintf(host_client, PRINT_CHAT, + "You can't talk for %d more seconds\n", + (int) (host_client->lockedtill - realtime)); + return; + } + tmp = host_client->whensaidhead - fp_messages + 1; + if (tmp < 0) + tmp = 10+tmp; + if (!sv.paused && + host_client->whensaid[tmp] && (realtime-host_client->whensaid[tmp] < fp_persecond)) { + host_client->lockedtill = realtime + fp_secondsdead; + if (fp_msg[0]) + SV_ClientPrintf(host_client, PRINT_CHAT, + "FloodProt: %s\n", fp_msg); + else + SV_ClientPrintf(host_client, PRINT_CHAT, + "FloodProt: You can't talk for %d seconds.\n", fp_secondsdead); + return; + } + host_client->whensaidhead++; + if (host_client->whensaidhead > 9) + host_client->whensaidhead = 0; + host_client->whensaid[host_client->whensaidhead] = realtime; + } + + p = Cmd_Args(); + + if (*p == '"') + { + p++; + p[Q_strlen(p)-1] = 0; + } + + Q_strcat(text, p); + Q_strcat(text, "\n"); + + Sys_Printf ("%s", text); + + for (j = 0, client = svs.clients; j < MAX_CLIENTS; j++, client++) + { + if (client->state != cs_spawned) + continue; + if (host_client->spectator && !sv_spectalk.value) + if (!client->spectator) + continue; + + if (team) + { + // the spectator team + if (host_client->spectator) { + if (!client->spectator) + continue; + } else { + t2 = Info_ValueForKey (client->userinfo, "team"); + if (strcmp(t1, t2) || client->spectator) + continue; // on different teams + } + } + SV_ClientPrintf(client, PRINT_CHAT, "%s", text); + } +} + + +/* +================== +SV_Say_f +================== +*/ +void SV_Say_f(void) +{ + SV_Say (false); +} +/* +================== +SV_Say_Team_f +================== +*/ +void SV_Say_Team_f(void) +{ + SV_Say (true); +} + + + +//============================================================================ + +/* +================= +SV_Pings_f + +The client is showing the scoreboard, so send new ping times for all +clients +================= +*/ +void SV_Pings_f (void) +{ + client_t *client; + int j; + + for (j = 0, client = svs.clients; j < MAX_CLIENTS; j++, client++) + { + if (client->state != cs_spawned) + continue; + + ClientReliableWrite_Begin (host_client, svc_updateping, 4); + ClientReliableWrite_Byte (host_client, j); + ClientReliableWrite_Short (host_client, SV_CalcPing(client)); + ClientReliableWrite_Begin (host_client, svc_updatepl, 4); + ClientReliableWrite_Byte (host_client, j); + ClientReliableWrite_Byte (host_client, client->lossage); + } +} + + + +/* +================== +SV_Kill_f +================== +*/ +void SV_Kill_f (void) +{ + if (sv_player->v.health <= 0) + { + SV_ClientPrintf (host_client, PRINT_HIGH, "Can't suicide -- allready dead!\n"); + return; + } + + pr_global_struct->time = sv.time; + pr_global_struct->self = EDICT_TO_PROG(sv_player); + PR_ExecuteProgram (pr_global_struct->ClientKill); +} + +/* +================== +SV_TogglePause +================== +*/ +void SV_TogglePause (const char *msg) +{ + int i; + client_t *cl; + + sv.paused ^= 1; + + if (msg) + SV_BroadcastPrintf (PRINT_HIGH, "%s", msg); + + // send notification to all clients + for (i=0, cl = svs.clients ; i<MAX_CLIENTS ; i++, cl++) + { + if (!cl->state) + continue; + ClientReliableWrite_Begin (cl, svc_setpause, 2); + ClientReliableWrite_Byte (cl, sv.paused); + } +} + + +/* +================== +SV_Pause_f +================== +*/ +void SV_Pause_f (void) +{ + int i; + client_t *cl; + char st[sizeof(host_client->name) + 32]; + + if (!pausable.value) { + SV_ClientPrintf (host_client, PRINT_HIGH, "Pause not allowed.\n"); + return; + } + + if (host_client->spectator) { + SV_ClientPrintf (host_client, PRINT_HIGH, "Spectators can not pause.\n"); + return; + } + + if (sv.paused) + sprintf (st, "%s paused the game\n", host_client->name); + else + sprintf (st, "%s unpaused the game\n", host_client->name); + + SV_TogglePause(st); +} + + +/* +================= +SV_Drop_f + +The client is going to disconnect, so remove the connection immediately +================= +*/ +void SV_Drop_f (void) +{ + SV_EndRedirect (); + if (!host_client->spectator) + SV_BroadcastPrintf (PRINT_HIGH, "%s dropped\n", host_client->name); + SV_DropClient (host_client); +} + +/* +================= +SV_PTrack_f + +Change the bandwidth estimate for a client +================= +*/ +void SV_PTrack_f (void) +{ + int i; + edict_t *ent, *tent; + + if (!host_client->spectator) + return; + + if (Cmd_Argc() != 2) + { + // turn off tracking + host_client->spec_track = 0; + ent = EDICT_NUM(host_client - svs.clients + 1); + tent = EDICT_NUM(0); + ent->v.goalentity = EDICT_TO_PROG(tent); + return; + } + + i = atoi(Cmd_Argv(1)); + if (i < 0 || i >= MAX_CLIENTS || svs.clients[i].state != cs_spawned || + svs.clients[i].spectator) { + SV_ClientPrintf (host_client, PRINT_HIGH, "Invalid client to track\n"); + host_client->spec_track = 0; + ent = EDICT_NUM(host_client - svs.clients + 1); + tent = EDICT_NUM(0); + ent->v.goalentity = EDICT_TO_PROG(tent); + return; + } + host_client->spec_track = i + 1; // now tracking + + ent = EDICT_NUM(host_client - svs.clients + 1); + tent = EDICT_NUM(i + 1); + ent->v.goalentity = EDICT_TO_PROG(tent); +} + + +/* +================= +SV_Rate_f + +Change the bandwidth estimate for a client +================= +*/ +void SV_Rate_f (void) +{ + int rate; + + if (Cmd_Argc() != 2) + { + SV_ClientPrintf (host_client, PRINT_HIGH, "Current rate is %i\n", + (int)(1.0/host_client->netchan.rate + 0.5)); + return; + } + + rate = atoi(Cmd_Argv(1)); + if (rate < 500) + rate = 500; + if (rate > 10000) + rate = 10000; + + SV_ClientPrintf (host_client, PRINT_HIGH, "Net rate set to %i\n", rate); + host_client->netchan.rate = 1.0/rate; +} + + +/* +================= +SV_Msg_f + +Change the message level for a client +================= +*/ +void SV_Msg_f (void) +{ + if (Cmd_Argc() != 2) + { + SV_ClientPrintf (host_client, PRINT_HIGH, "Current msg level is %i\n", + host_client->messagelevel); + return; + } + + host_client->messagelevel = atoi(Cmd_Argv(1)); + + SV_ClientPrintf (host_client, PRINT_HIGH, "Msg level set to %i\n", host_client->messagelevel); +} + +/* +================== +SV_SetInfo_f + +Allow clients to change userinfo +================== +*/ +void SV_SetInfo_f (void) +{ + int i; + char oldval[MAX_INFO_STRING]; + + + if (Cmd_Argc() == 1) + { + Con_Printf ("User info settings:\n"); + Info_Print (host_client->userinfo); + return; + } + + if (Cmd_Argc() != 3) + { + Con_Printf ("usage: setinfo [ <key> <value> ]\n"); + return; + } + + if (Cmd_Argv(1)[0] == '*') + return; // don't set priveledged values + + strcpy(oldval, Info_ValueForKey(host_client->userinfo, Cmd_Argv(1))); + + Info_SetValueForKey (host_client->userinfo, Cmd_Argv(1), Cmd_Argv(2), MAX_INFO_STRING); +// name is extracted below in ExtractFromUserInfo +// strncpy (host_client->name, Info_ValueForKey (host_client->userinfo, "name") +// , sizeof(host_client->name)-1); +// SV_FullClientUpdate (host_client, &sv.reliable_datagram); +// host_client->sendinfo = true; + + if (!strcmp(Info_ValueForKey(host_client->userinfo, Cmd_Argv(1)), oldval)) + return; // key hasn't changed + + // process any changed values + SV_ExtractFromUserinfo (host_client); + + i = host_client - svs.clients; + MSG_WriteByte (&sv.reliable_datagram, svc_setinfo); + MSG_WriteByte (&sv.reliable_datagram, i); + MSG_WriteString (&sv.reliable_datagram, Cmd_Argv(1)); + MSG_WriteString (&sv.reliable_datagram, Info_ValueForKey(host_client->userinfo, Cmd_Argv(1))); +} + +/* +================== +SV_ShowServerinfo_f + +Dumps the serverinfo info string +================== +*/ +void SV_ShowServerinfo_f (void) +{ + Info_Print (svs.info); +} + +void SV_NoSnap_f(void) +{ + if (*host_client->uploadfn) { + *host_client->uploadfn = 0; + SV_BroadcastPrintf (PRINT_HIGH, "%s refused remote screenshot\n", host_client->name); + } +} + +typedef struct +{ + char *name; + void (*func) (void); +} ucmd_t; + +ucmd_t ucmds[] = +{ + {"new", SV_New_f}, + {"modellist", SV_Modellist_f}, + {"soundlist", SV_Soundlist_f}, + {"prespawn", SV_PreSpawn_f}, + {"spawn", SV_Spawn_f}, + {"begin", SV_Begin_f}, + + {"drop", SV_Drop_f}, + {"pings", SV_Pings_f}, + +// issued by hand at client consoles + {"rate", SV_Rate_f}, + {"kill", SV_Kill_f}, + {"pause", SV_Pause_f}, + {"msg", SV_Msg_f}, + + {"say", SV_Say_f}, + {"say_team", SV_Say_Team_f}, + + {"setinfo", SV_SetInfo_f}, + + {"serverinfo", SV_ShowServerinfo_f}, + + {"download", SV_BeginDownload_f}, + {"nextdl", SV_NextDownload_f}, + + {"ptrack", SV_PTrack_f}, //ZOID - used with autocam + + {"snap", SV_NoSnap_f}, + + {NULL, NULL} +}; + +/* +================== +SV_ExecuteUserCommand +================== +*/ +void SV_ExecuteUserCommand (char *s) +{ + ucmd_t *u; + + Cmd_TokenizeString (s); + sv_player = host_client->edict; + + SV_BeginRedirect (RD_CLIENT); + + for (u=ucmds ; u->name ; u++) + if (!strcmp (Cmd_Argv(0), u->name) ) + { + u->func (); + break; + } + + if (!u->name) + Con_Printf ("Bad user command: %s\n", Cmd_Argv(0)); + + SV_EndRedirect (); +} + +/* +=========================================================================== + +USER CMD EXECUTION + +=========================================================================== +*/ + +/* +=============== +V_CalcRoll + +Used by view and sv_user +=============== +*/ +float V_CalcRoll (vec3_t angles, vec3_t velocity) +{ + vec3_t forward, right, up; + float sign; + float side; + float value; + + AngleVectors (angles, forward, right, up); + side = DotProduct (velocity, right); + sign = side < 0 ? -1 : 1; + side = fabs(side); + + value = cl_rollangle.value; + + if (side < cl_rollspeed.value) + side = side * value / cl_rollspeed.value; + else + side = value; + + return side*sign; + +} + + + + +//============================================================================ + +vec3_t pmove_mins, pmove_maxs; + +/* +==================== +AddLinksToPmove + +==================== +*/ +void AddLinksToPmove ( areanode_t *node ) +{ + link_t *l, *next; + edict_t *check; + int pl; + int i; + physent_t *pe; + + pl = EDICT_TO_PROG(sv_player); + + // touch linked edicts + for (l = node->solid_edicts.next ; l != &node->solid_edicts ; l = next) + { + next = l->next; + check = EDICT_FROM_AREA(l); + + if (check->v.owner == pl) + continue; // player's own missile + if (check->v.solid == SOLID_BSP + || check->v.solid == SOLID_BBOX + || check->v.solid == SOLID_SLIDEBOX) + { + if (check == sv_player) + continue; + + for (i=0 ; i<3 ; i++) + if (check->v.absmin[i] > pmove_maxs[i] + || check->v.absmax[i] < pmove_mins[i]) + break; + if (i != 3) + continue; + if (pmove.numphysent == MAX_PHYSENTS) + return; + pe = &pmove.physents[pmove.numphysent]; + pmove.numphysent++; + + VectorCopy (check->v.origin, pe->origin); + pe->info = NUM_FOR_EDICT(check); + if (check->v.solid == SOLID_BSP) + pe->model = sv.models[(int)(check->v.modelindex)]; + else + { + pe->model = NULL; + VectorCopy (check->v.mins, pe->mins); + VectorCopy (check->v.maxs, pe->maxs); + } + } + } + +// recurse down both sides + if (node->axis == -1) + return; + + if ( pmove_maxs[node->axis] > node->dist ) + AddLinksToPmove ( node->children[0] ); + if ( pmove_mins[node->axis] < node->dist ) + AddLinksToPmove ( node->children[1] ); +} + + +/* +================ +AddAllEntsToPmove + +For debugging +================ +*/ +void AddAllEntsToPmove (void) +{ + int e; + edict_t *check; + int i; + physent_t *pe; + int pl; + + pl = EDICT_TO_PROG(sv_player); + check = NEXT_EDICT(sv.edicts); + for (e=1 ; e<sv.num_edicts ; e++, check = NEXT_EDICT(check)) + { + if (check->free) + continue; + if (check->v.owner == pl) + continue; + if (check->v.solid == SOLID_BSP + || check->v.solid == SOLID_BBOX + || check->v.solid == SOLID_SLIDEBOX) + { + if (check == sv_player) + continue; + + for (i=0 ; i<3 ; i++) + if (check->v.absmin[i] > pmove_maxs[i] + || check->v.absmax[i] < pmove_mins[i]) + break; + if (i != 3) + continue; + pe = &pmove.physents[pmove.numphysent]; + + VectorCopy (check->v.origin, pe->origin); + pmove.physents[pmove.numphysent].info = e; + if (check->v.solid == SOLID_BSP) + pe->model = sv.models[(int)(check->v.modelindex)]; + else + { + pe->model = NULL; + VectorCopy (check->v.mins, pe->mins); + VectorCopy (check->v.maxs, pe->maxs); + } + + if (++pmove.numphysent == MAX_PHYSENTS) + break; + } + } +} + +/* +=========== +SV_PreRunCmd +=========== +Done before running a player command. Clears the touch array +*/ +byte playertouch[(MAX_EDICTS+7)/8]; + +void SV_PreRunCmd(void) +{ + memset(playertouch, 0, sizeof(playertouch)); +} + +/* +=========== +SV_RunCmd +=========== +*/ +void SV_RunCmd (usercmd_t *ucmd) +{ + edict_t *ent; + int i, n; + int oldmsec; + + cmd = *ucmd; + + // chop up very long commands + if (cmd.msec > 50) + { + oldmsec = ucmd->msec; + cmd.msec = oldmsec/2; + SV_RunCmd (&cmd); + cmd.msec = oldmsec/2; + cmd.impulse = 0; + SV_RunCmd (&cmd); + return; + } + + if (!sv_player->v.fixangle) + VectorCopy (ucmd->angles, sv_player->v.v_angle); + + sv_player->v.button0 = ucmd->buttons & 1; + sv_player->v.button2 = (ucmd->buttons & 2)>>1; + if (ucmd->impulse) + sv_player->v.impulse = ucmd->impulse; + +// +// angles +// show 1/3 the pitch angle and all the roll angle + if (sv_player->v.health > 0) + { + if (!sv_player->v.fixangle) + { + sv_player->v.angles[PITCH] = -sv_player->v.v_angle[PITCH]/3; + sv_player->v.angles[YAW] = sv_player->v.v_angle[YAW]; + } + sv_player->v.angles[ROLL] = + V_CalcRoll (sv_player->v.angles, sv_player->v.velocity)*4; + } + + host_frametime = ucmd->msec * 0.001; + if (host_frametime > 0.1) + host_frametime = 0.1; + + if (!host_client->spectator) + { + pr_global_struct->frametime = host_frametime; + + pr_global_struct->time = sv.time; + pr_global_struct->self = EDICT_TO_PROG(sv_player); + PR_ExecuteProgram (pr_global_struct->PlayerPreThink); + + SV_RunThink (sv_player); + } + + for (i=0 ; i<3 ; i++) + pmove.origin[i] = sv_player->v.origin[i] + (sv_player->v.mins[i] - player_mins[i]); + VectorCopy (sv_player->v.velocity, pmove.velocity); + VectorCopy (sv_player->v.v_angle, pmove.angles); + + pmove.spectator = host_client->spectator; + pmove.waterjumptime = sv_player->v.teleport_time; + pmove.numphysent = 1; + pmove.physents[0].model = sv.worldmodel; + pmove.cmd = *ucmd; + pmove.dead = sv_player->v.health <= 0; + pmove.oldbuttons = host_client->oldbuttons; + + movevars.entgravity = host_client->entgravity; + movevars.maxspeed = host_client->maxspeed; + + for (i=0 ; i<3 ; i++) + { + pmove_mins[i] = pmove.origin[i] - 256; + pmove_maxs[i] = pmove.origin[i] + 256; + } +#if 1 + AddLinksToPmove ( sv_areanodes ); +#else + AddAllEntsToPmove (); +#endif + +#if 0 +{ + int before, after; + +before = PM_TestPlayerPosition (pmove.origin); + PlayerMove (); +after = PM_TestPlayerPosition (pmove.origin); + +if (sv_player->v.health > 0 && before && !after ) + Con_Printf ("player %s got stuck in playermove!!!!\n", host_client->name); +} +#else + PlayerMove (); +#endif + + host_client->oldbuttons = pmove.oldbuttons; + sv_player->v.teleport_time = pmove.waterjumptime; + sv_player->v.waterlevel = waterlevel; + sv_player->v.watertype = watertype; + if (onground != -1) + { + sv_player->v.flags = (int)sv_player->v.flags | FL_ONGROUND; + sv_player->v.groundentity = EDICT_TO_PROG(EDICT_NUM(pmove.physents[onground].info)); + } + else + sv_player->v.flags = (int)sv_player->v.flags & ~FL_ONGROUND; + for (i=0 ; i<3 ; i++) + sv_player->v.origin[i] = pmove.origin[i] - (sv_player->v.mins[i] - player_mins[i]); + +#if 0 + // truncate velocity the same way the net protocol will + for (i=0 ; i<3 ; i++) + sv_player->v.velocity[i] = (int)pmove.velocity[i]; +#else + VectorCopy (pmove.velocity, sv_player->v.velocity); +#endif + + VectorCopy (pmove.angles, sv_player->v.v_angle); + + if (!host_client->spectator) + { + // link into place and touch triggers + SV_LinkEdict (sv_player, true); + + // touch other objects + for (i=0 ; i<pmove.numtouch ; i++) + { + n = pmove.physents[pmove.touchindex[i]].info; + ent = EDICT_NUM(n); + if (!ent->v.touch || (playertouch[n/8]&(1<<(n%8)))) + continue; + pr_global_struct->self = EDICT_TO_PROG(ent); + pr_global_struct->other = EDICT_TO_PROG(sv_player); + PR_ExecuteProgram (ent->v.touch); + playertouch[n/8] |= 1 << (n%8); + } + } +} + +/* +=========== +SV_PostRunCmd +=========== +Done after running a player command. +*/ +void SV_PostRunCmd(void) +{ + // run post-think + + if (!host_client->spectator) { + pr_global_struct->time = sv.time; + pr_global_struct->self = EDICT_TO_PROG(sv_player); + PR_ExecuteProgram (pr_global_struct->PlayerPostThink); + SV_RunNewmis (); + } else if (SpectatorThink) { + pr_global_struct->time = sv.time; + pr_global_struct->self = EDICT_TO_PROG(sv_player); + PR_ExecuteProgram (SpectatorThink); + } +} + + +/* +=================== +SV_ExecuteClientMessage + +The current net_message is parsed for the given client +=================== +*/ +void SV_ExecuteClientMessage (client_t *cl) +{ + int c; + char *s; + usercmd_t oldest, oldcmd, newcmd; + client_frame_t *frame; + vec3_t o; + qboolean move_issued = false; //only allow one move command + int checksumIndex; + byte checksum, calculatedChecksum; + int seq_hash; + + // calc ping time + frame = &cl->frames[cl->netchan.incoming_acknowledged & UPDATE_MASK]; + frame->ping_time = realtime - frame->senttime; + + // make sure the reply sequence number matches the incoming + // sequence number + if (cl->netchan.incoming_sequence >= cl->netchan.outgoing_sequence) + cl->netchan.outgoing_sequence = cl->netchan.incoming_sequence; + else + cl->send_message = false; // don't reply, sequences have slipped + + // save time for ping calculations + cl->frames[cl->netchan.outgoing_sequence & UPDATE_MASK].senttime = realtime; + cl->frames[cl->netchan.outgoing_sequence & UPDATE_MASK].ping_time = -1; + + host_client = cl; + sv_player = host_client->edict; + +// seq_hash = (cl->netchan.incoming_sequence & 0xffff) ; // ^ QW_CHECK_HASH; + seq_hash = cl->netchan.incoming_sequence; + + // mark time so clients will know how much to predict + // other players + cl->localtime = sv.time; + cl->delta_sequence = -1; // no delta unless requested + while (1) + { + if (msg_badread) + { + Con_Printf ("SV_ReadClientMessage: badread\n"); + SV_DropClient (cl); + return; + } + + c = MSG_ReadByte (); + if (c == -1) + break; + + switch (c) + { + default: + Con_Printf ("SV_ReadClientMessage: unknown command char\n"); + SV_DropClient (cl); + return; + + case clc_nop: + break; + + case clc_delta: + cl->delta_sequence = MSG_ReadByte (); + break; + + case clc_move: + if (move_issued) + return; // someone is trying to cheat... + + move_issued = true; + + checksumIndex = MSG_GetReadCount(); + checksum = (byte)MSG_ReadByte (); + + // read loss percentage + cl->lossage = MSG_ReadByte(); + + MSG_ReadDeltaUsercmd (&nullcmd, &oldest); + MSG_ReadDeltaUsercmd (&oldest, &oldcmd); + MSG_ReadDeltaUsercmd (&oldcmd, &newcmd); + + if ( cl->state != cs_spawned ) + break; + + // if the checksum fails, ignore the rest of the packet + calculatedChecksum = COM_BlockSequenceCRCByte( + net_message.data + checksumIndex + 1, + MSG_GetReadCount() - checksumIndex - 1, + seq_hash); + + if (calculatedChecksum != checksum) + { + Con_DPrintf ("Failed command checksum for %s(%d) (%d != %d)\n", + cl->name, cl->netchan.incoming_sequence, checksum, calculatedChecksum); + return; + } + + if (!sv.paused) { + SV_PreRunCmd(); + + if (net_drop < 20) + { + while (net_drop > 2) + { + SV_RunCmd (&cl->lastcmd); + net_drop--; + } + if (net_drop > 1) + SV_RunCmd (&oldest); + if (net_drop > 0) + SV_RunCmd (&oldcmd); + } + SV_RunCmd (&newcmd); + + SV_PostRunCmd(); + } + + cl->lastcmd = newcmd; + cl->lastcmd.buttons = 0; // avoid multiple fires on lag + break; + + + case clc_stringcmd: + s = MSG_ReadString (); + SV_ExecuteUserCommand (s); + break; + + case clc_tmove: + o[0] = MSG_ReadCoord(); + o[1] = MSG_ReadCoord(); + o[2] = MSG_ReadCoord(); + // only allowed by spectators + if (host_client->spectator) { + VectorCopy(o, sv_player->v.origin); + SV_LinkEdict(sv_player, false); + } + break; + + case clc_upload: + SV_NextUpload(); + break; + + } + } +} + +/* +============== +SV_UserInit +============== +*/ +void SV_UserInit (void) +{ + Cvar_RegisterVariable (&cl_rollspeed); + Cvar_RegisterVariable (&cl_rollangle); + Cvar_RegisterVariable (&sv_spectalk); + Cvar_RegisterVariable (&sv_mapcheck); +} + + diff --git a/QW/server/sys.h b/QW/server/sys.h new file mode 100644 index 0000000..c1bf217 --- /dev/null +++ b/QW/server/sys.h @@ -0,0 +1,35 @@ +/* +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. + +*/ +// sys.h -- non-portable functions + +int Sys_FileTime (char *path); + +void Sys_mkdir (char *path); + +void Sys_Error (char *error, ...); +// an error will cause the entire program to exit + +void Sys_Printf (char *fmt, ...); +// send text to the console + +void Sys_Quit (void); +double Sys_DoubleTime (void); +char *Sys_ConsoleInput (void); +void Sys_Init (void); diff --git a/QW/server/sys_unix.c b/QW/server/sys_unix.c new file mode 100644 index 0000000..dbd9f45 --- /dev/null +++ b/QW/server/sys_unix.c @@ -0,0 +1,283 @@ +/* +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. + +*/ +#include <sys/types.h> +#include "qwsvdef.h" + +#ifdef NeXT +#include <libc.h> +#endif + +#if defined(__linux__) || defined(sun) +#include <sys/stat.h> +#include <unistd.h> +#include <sys/time.h> +#include <errno.h> +#else +#include <sys/dir.h> +#endif + +cvar_t sys_nostdout = {"sys_nostdout","0"}; +cvar_t sys_extrasleep = {"sys_extrasleep","0"}; + +qboolean stdin_ready; + +/* +=============================================================================== + + REQUIRED SYS FUNCTIONS + +=============================================================================== +*/ + +/* +============ +Sys_FileTime + +returns -1 if not present +============ +*/ +int Sys_FileTime (char *path) +{ + struct stat buf; + + if (stat (path,&buf) == -1) + return -1; + + return buf.st_mtime; +} + + +/* +============ +Sys_mkdir + +============ +*/ +void Sys_mkdir (char *path) +{ + if (mkdir (path, 0777) != -1) + return; + if (errno != EEXIST) + Sys_Error ("mkdir %s: %s",path, strerror(errno)); +} + + +/* +================ +Sys_DoubleTime +================ +*/ +double Sys_DoubleTime (void) +{ + struct timeval tp; + struct timezone tzp; + static int secbase; + + gettimeofday(&tp, &tzp); + + if (!secbase) + { + secbase = tp.tv_sec; + return tp.tv_usec/1000000.0; + } + + return (tp.tv_sec - secbase) + tp.tv_usec/1000000.0; +} + +/* +================ +Sys_Error +================ +*/ +void Sys_Error (char *error, ...) +{ + va_list argptr; + char string[1024]; + + va_start (argptr,error); + vsprintf (string,error,argptr); + va_end (argptr); + printf ("Fatal error: %s\n",string); + + exit (1); +} + +/* +================ +Sys_Printf +================ +*/ +void Sys_Printf (char *fmt, ...) +{ + va_list argptr; + static char text[2048]; + unsigned char *p; + + va_start (argptr,fmt); + vsprintf (text,fmt,argptr); + va_end (argptr); + + if (strlen(text) > sizeof(text)) + Sys_Error("memory overwrite in Sys_Printf"); + + if (sys_nostdout.value) + return; + + for (p = (unsigned char *)text; *p; p++) { + *p &= 0x7f; + if ((*p > 128 || *p < 32) && *p != 10 && *p != 13 && *p != 9) + printf("[%02x]", *p); + else + putc(*p, stdout); + } + fflush(stdout); +} + + +/* +================ +Sys_Quit +================ +*/ +void Sys_Quit (void) +{ + exit (0); // appkit isn't running +} + +static int do_stdin = 1; + +/* +================ +Sys_ConsoleInput + +Checks for a complete line of text typed in at the console, then forwards +it to the host command processor +================ +*/ +char *Sys_ConsoleInput (void) +{ + static char text[256]; + int len; + + if (!stdin_ready || !do_stdin) + return NULL; // the select didn't say it was ready + stdin_ready = false; + + len = read (0, text, sizeof(text)); + if (len == 0) { + // end of file + do_stdin = 0; + return NULL; + } + if (len < 1) + return NULL; + text[len-1] = 0; // rip off the /n and terminate + + return text; +} + +/* +============= +Sys_Init + +Quake calls this so the system can register variables before host_hunklevel +is marked +============= +*/ +void Sys_Init (void) +{ + Cvar_RegisterVariable (&sys_nostdout); + Cvar_RegisterVariable (&sys_extrasleep); +} + +/* +============= +main +============= +*/ +void main(int argc, char *argv[]) +{ + double time, oldtime, newtime; + quakeparms_t parms; + fd_set fdset; + extern int net_socket; + struct timeval timeout; + int j; + + memset (&parms, 0, sizeof(parms)); + + COM_InitArgv (argc, argv); + parms.argc = com_argc; + parms.argv = com_argv; + + parms.memsize = 16*1024*1024; + + j = COM_CheckParm("-mem"); + if (j) + parms.memsize = (int) (Q_atof(com_argv[j+1]) * 1024 * 1024); + if ((parms.membase = malloc (parms.memsize)) == NULL) + Sys_Error("Can't allocate %ld\n", parms.memsize); + + parms.basedir = "."; + +/* + if (Sys_FileTime ("id1/pak0.pak") != -1) + else + parms.basedir = "/raid/quake/v2"; +*/ + + SV_Init (&parms); + +// run one frame immediately for first heartbeat + SV_Frame (0.1); + +// +// main loop +// + oldtime = Sys_DoubleTime () - 0.1; + while (1) + { + // select on the net socket and stdin + // the only reason we have a timeout at all is so that if the last + // connected client times out, the message would not otherwise + // be printed until the next event. + FD_ZERO(&fdset); + if (do_stdin) + FD_SET(0, &fdset); + FD_SET(net_socket, &fdset); + timeout.tv_sec = 1; + timeout.tv_usec = 0; + if (select (net_socket+1, &fdset, NULL, NULL, &timeout) == -1) + continue; + stdin_ready = FD_ISSET(0, &fdset); + + // find time passed since last cycle + newtime = Sys_DoubleTime (); + time = newtime - oldtime; + oldtime = newtime; + + SV_Frame (time); + + // extrasleep is just a way to generate a fucked up connection on purpose + if (sys_extrasleep.value) + usleep (sys_extrasleep.value); + } +} + diff --git a/QW/server/sys_win.c b/QW/server/sys_win.c new file mode 100644 index 0000000..9a237ee --- /dev/null +++ b/QW/server/sys_win.c @@ -0,0 +1,260 @@ +/* +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. + +*/ +#include <sys/types.h> +#include <sys/timeb.h> +#include "qwsvdef.h" +#include <winsock.h> +#include <conio.h> + + +cvar_t sys_nostdout = {"sys_nostdout","0"}; + +/* +================ +Sys_FileTime +================ +*/ +int Sys_FileTime (char *path) +{ + FILE *f; + + f = fopen(path, "rb"); + if (f) + { + fclose(f); + return 1; + } + + return -1; +} + +/* +================ +Sys_mkdir +================ +*/ +void Sys_mkdir (char *path) +{ + _mkdir(path); +} + + +/* +================ +Sys_Error +================ +*/ +void Sys_Error (char *error, ...) +{ + va_list argptr; + char text[1024]; + + va_start (argptr,error); + vsprintf (text, error,argptr); + va_end (argptr); + +// MessageBox(NULL, text, "Error", 0 /* MB_OK */ ); + printf ("ERROR: %s\n", text); + + exit (1); +} + + +/* +================ +Sys_DoubleTime +================ +*/ +double Sys_DoubleTime (void) +{ + double t; + struct _timeb tstruct; + static int starttime; + + _ftime( &tstruct ); + + if (!starttime) + starttime = tstruct.time; + t = (tstruct.time-starttime) + tstruct.millitm*0.001; + + return t; +} + + +/* +================ +Sys_ConsoleInput +================ +*/ +char *Sys_ConsoleInput (void) +{ + static char text[256]; + static int len; + int c; + + // read a line out + while (_kbhit()) + { + c = _getch(); + putch (c); + if (c == '\r') + { + text[len] = 0; + putch ('\n'); + len = 0; + return text; + } + if (c == 8) + { + if (len) + { + putch (' '); + putch (c); + len--; + text[len] = 0; + } + continue; + } + text[len] = c; + len++; + text[len] = 0; + if (len == sizeof(text)) + len = 0; + } + + return NULL; +} + + +/* +================ +Sys_Printf +================ +*/ +void Sys_Printf (char *fmt, ...) +{ + va_list argptr; + + if (sys_nostdout.value) + return; + + va_start (argptr,fmt); + vprintf (fmt,argptr); + va_end (argptr); +} + +/* +================ +Sys_Quit +================ +*/ +void Sys_Quit (void) +{ + exit (0); +} + + +/* +============= +Sys_Init + +Quake calls this so the system can register variables before host_hunklevel +is marked +============= +*/ +void Sys_Init (void) +{ + Cvar_RegisterVariable (&sys_nostdout); +} + +/* +================== +main + +================== +*/ +char *newargv[256]; + +int main (int argc, char **argv) +{ + quakeparms_t parms; + double newtime, time, oldtime; + static char cwd[1024]; + struct timeval timeout; + fd_set fdset; + int t; + + COM_InitArgv (argc, argv); + + parms.argc = com_argc; + parms.argv = com_argv; + + parms.memsize = 16*1024*1024; + + if ((t = COM_CheckParm ("-heapsize")) != 0 && + t + 1 < com_argc) + parms.memsize = Q_atoi (com_argv[t + 1]) * 1024; + + if ((t = COM_CheckParm ("-mem")) != 0 && + t + 1 < com_argc) + parms.memsize = Q_atoi (com_argv[t + 1]) * 1024 * 1024; + + parms.membase = malloc (parms.memsize); + + if (!parms.membase) + Sys_Error("Insufficient memory.\n"); + + parms.basedir = "."; + parms.cachedir = NULL; + + SV_Init (&parms); + +// run one frame immediately for first heartbeat + SV_Frame (0.1); + +// +// main loop +// + oldtime = Sys_DoubleTime () - 0.1; + while (1) + { + // select on the net socket and stdin + // the only reason we have a timeout at all is so that if the last + // connected client times out, the message would not otherwise + // be printed until the next event. + FD_ZERO(&fdset); + FD_SET(net_socket, &fdset); + timeout.tv_sec = 0; + timeout.tv_usec = 100; + if (select (net_socket+1, &fdset, NULL, NULL, &timeout) == -1) + continue; + + // find time passed since last cycle + newtime = Sys_DoubleTime (); + time = newtime - oldtime; + oldtime = newtime; + + SV_Frame (time); + } + + return true; +} + + diff --git a/QW/server/world.c b/QW/server/world.c new file mode 100644 index 0000000..5301df9 --- /dev/null +++ b/QW/server/world.c @@ -0,0 +1,924 @@ +/* +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 "qwsvdef.h" + +/* + +entities never clip against themselves, or their owner + +line of sight checks trace->crosscontent, but bullets don't + +*/ + + +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; + edict_t *passedict; +} moveclip_t; + + +int SV_HullPointContents (hull_t *hull, int num, vec3_t p); + +/* +=============================================================================== + +HULL BOXES + +=============================================================================== +*/ + + +static hull_t box_hull; +static dclipnode_t box_clipnodes[6]; +static mplane_t box_planes[6]; + +/* +=================== +SV_InitBoxHull + +Set up the planes and clipnodes so that the six floats of a bounding box +can just be stored out and get a proper hull_t structure. +=================== +*/ +void SV_InitBoxHull (void) +{ + int i; + int side; + + box_hull.clipnodes = box_clipnodes; + box_hull.planes = box_planes; + box_hull.firstclipnode = 0; + box_hull.lastclipnode = 5; + + for (i=0 ; i<6 ; i++) + { + box_clipnodes[i].planenum = i; + + side = i&1; + + box_clipnodes[i].children[side] = CONTENTS_EMPTY; + if (i != 5) + box_clipnodes[i].children[side^1] = i + 1; + else + box_clipnodes[i].children[side^1] = CONTENTS_SOLID; + + box_planes[i].type = i>>1; + box_planes[i].normal[i>>1] = 1; + } + +} + + +/* +=================== +SV_HullForBox + +To keep everything totally uniform, bounding boxes are turned into small +BSP trees instead of being compared directly. +=================== +*/ +hull_t *SV_HullForBox (vec3_t mins, vec3_t maxs) +{ + box_planes[0].dist = maxs[0]; + box_planes[1].dist = mins[0]; + box_planes[2].dist = maxs[1]; + box_planes[3].dist = mins[1]; + box_planes[4].dist = maxs[2]; + box_planes[5].dist = mins[2]; + + return &box_hull; +} + + + +/* +================ +SV_HullForEntity + +Returns a hull that can be used for testing or clipping an object of mins/maxs +size. +Offset is filled in to contain the adjustment that must be added to the +testing object's origin to get a point to use with the returned hull. +================ +*/ +hull_t *SV_HullForEntity (edict_t *ent, vec3_t mins, vec3_t maxs, vec3_t offset) +{ + model_t *model; + vec3_t size; + vec3_t hullmins, hullmaxs; + hull_t *hull; + +// decide which clipping hull to use, based on the size + if (ent->v.solid == SOLID_BSP) + { // explicit hulls in the BSP model + if (ent->v.movetype != MOVETYPE_PUSH) + SV_Error ("SOLID_BSP without MOVETYPE_PUSH"); + + model = sv.models[ (int)ent->v.modelindex ]; + + if (!model || model->type != mod_brush) + SV_Error ("MOVETYPE_PUSH with a non bsp model"); + + VectorSubtract (maxs, mins, size); + if (size[0] < 3) + hull = &model->hulls[0]; + else if (size[0] <= 32) + hull = &model->hulls[1]; + else + hull = &model->hulls[2]; + +// calculate an offset value to center the origin + VectorSubtract (hull->clip_mins, mins, offset); + VectorAdd (offset, ent->v.origin, offset); + } + else + { // create a temp hull from bounding box sizes + + VectorSubtract (ent->v.mins, maxs, hullmins); + VectorSubtract (ent->v.maxs, mins, hullmaxs); + hull = SV_HullForBox (hullmins, hullmaxs); + + VectorCopy (ent->v.origin, offset); + } + + + return hull; +} + +/* +=============================================================================== + +ENTITY AREA CHECKING + +=============================================================================== +*/ + + +areanode_t sv_areanodes[AREA_NODES]; +int sv_numareanodes; + +/* +=============== +SV_CreateAreaNode + +=============== +*/ +areanode_t *SV_CreateAreaNode (int depth, vec3_t mins, vec3_t maxs) +{ + areanode_t *anode; + vec3_t size; + vec3_t mins1, maxs1, mins2, maxs2; + + anode = &sv_areanodes[sv_numareanodes]; + sv_numareanodes++; + + ClearLink (&anode->trigger_edicts); + ClearLink (&anode->solid_edicts); + + if (depth == AREA_DEPTH) + { + anode->axis = -1; + anode->children[0] = anode->children[1] = NULL; + return anode; + } + + VectorSubtract (maxs, mins, size); + if (size[0] > size[1]) + anode->axis = 0; + else + anode->axis = 1; + + anode->dist = 0.5 * (maxs[anode->axis] + mins[anode->axis]); + VectorCopy (mins, mins1); + VectorCopy (mins, mins2); + VectorCopy (maxs, maxs1); + VectorCopy (maxs, maxs2); + + maxs1[anode->axis] = mins2[anode->axis] = anode->dist; + + anode->children[0] = SV_CreateAreaNode (depth+1, mins2, maxs2); + anode->children[1] = SV_CreateAreaNode (depth+1, mins1, maxs1); + + return anode; +} + +/* +=============== +SV_ClearWorld + +=============== +*/ +void SV_ClearWorld (void) +{ + SV_InitBoxHull (); + + memset (sv_areanodes, 0, sizeof(sv_areanodes)); + sv_numareanodes = 0; + SV_CreateAreaNode (0, sv.worldmodel->mins, sv.worldmodel->maxs); +} + + +/* +=============== +SV_UnlinkEdict + +=============== +*/ +void SV_UnlinkEdict (edict_t *ent) +{ + if (!ent->area.prev) + return; // not linked in anywhere + RemoveLink (&ent->area); + ent->area.prev = ent->area.next = NULL; +} + + +/* +==================== +SV_TouchLinks +==================== +*/ +void SV_TouchLinks ( edict_t *ent, areanode_t *node ) +{ + link_t *l, *next; + edict_t *touch; + int old_self, old_other; + +// touch linked edicts + for (l = node->trigger_edicts.next ; l != &node->trigger_edicts ; l = next) + { + next = l->next; + touch = EDICT_FROM_AREA(l); + if (touch == ent) + continue; + if (!touch->v.touch || 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; + + old_self = pr_global_struct->self; + old_other = pr_global_struct->other; + + pr_global_struct->self = EDICT_TO_PROG(touch); + pr_global_struct->other = EDICT_TO_PROG(ent); + pr_global_struct->time = sv.time; + PR_ExecuteProgram (touch->v.touch); + + pr_global_struct->self = old_self; + pr_global_struct->other = old_other; + } + +// recurse down both sides + if (node->axis == -1) + return; + + if ( ent->v.absmax[node->axis] > node->dist ) + SV_TouchLinks ( ent, node->children[0] ); + if ( ent->v.absmin[node->axis] < node->dist ) + SV_TouchLinks ( ent, node->children[1] ); +} + + +/* +=============== +SV_FindTouchedLeafs + +=============== +*/ +void SV_FindTouchedLeafs (edict_t *ent, mnode_t *node) +{ + mplane_t *splitplane; + mleaf_t *leaf; + int sides; + int leafnum; + + if (node->contents == CONTENTS_SOLID) + return; + +// add an efrag if the node is a leaf + + if ( node->contents < 0) + { + if (ent->num_leafs == MAX_ENT_LEAFS) + return; + + leaf = (mleaf_t *)node; + leafnum = leaf - sv.worldmodel->leafs - 1; + + ent->leafnums[ent->num_leafs] = leafnum; + ent->num_leafs++; + return; + } + +// NODE_MIXED + + splitplane = node->plane; + sides = BOX_ON_PLANE_SIDE(ent->v.absmin, ent->v.absmax, splitplane); + +// recurse down the contacted sides + if (sides & 1) + SV_FindTouchedLeafs (ent, node->children[0]); + + if (sides & 2) + SV_FindTouchedLeafs (ent, node->children[1]); +} + +/* +=============== +SV_LinkEdict + +=============== +*/ +void SV_LinkEdict (edict_t *ent, qboolean touch_triggers) +{ + areanode_t *node; + + if (ent->area.prev) + SV_UnlinkEdict (ent); // unlink from old position + + if (ent == sv.edicts) + return; // don't add the world + + if (ent->free) + return; + +// set the abs box + 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.absmax[0] += 15; + ent->v.absmax[1] += 15; + } + 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 + ent->num_leafs = 0; + if (ent->v.modelindex) + SV_FindTouchedLeafs (ent, sv.worldmodel->nodes); + + if (ent->v.solid == SOLID_NOT) + return; + +// find the first node that the ent's box crosses + node = sv_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 + + if (ent->v.solid == SOLID_TRIGGER) + InsertLinkBefore (&ent->area, &node->trigger_edicts); + else + InsertLinkBefore (&ent->area, &node->solid_edicts); + +// if touch_triggers, touch all entities at this node and decend for more + if (touch_triggers) + SV_TouchLinks ( ent, sv_areanodes ); +} + + + +/* +=============================================================================== + +POINT TESTING IN HULLS + +=============================================================================== +*/ + +#if !id386 + +/* +================== +SV_HullPointContents + +================== +*/ +int SV_HullPointContents (hull_t *hull, int num, vec3_t p) +{ + float d; + dclipnode_t *node; + mplane_t *plane; + + while (num >= 0) + { + if (num < hull->firstclipnode || num > hull->lastclipnode) + SV_Error ("SV_HullPointContents: bad node number"); + + node = hull->clipnodes + num; + plane = hull->planes + node->planenum; + + if (plane->type < 3) + d = p[plane->type] - plane->dist; + else + d = DotProduct (plane->normal, p) - plane->dist; + if (d < 0) + num = node->children[1]; + else + num = node->children[0]; + } + + return num; +} + +#endif // !id386 + + +/* +================== +SV_PointContents + +================== +*/ +int SV_PointContents (vec3_t p) +{ + return SV_HullPointContents (&sv.worldmodel->hulls[0], 0, p); +} + +//=========================================================================== + +/* +============ +SV_TestEntityPosition + +A small wrapper around SV_BoxInSolidEntity that never clips against the +supplied entity. +============ +*/ +edict_t *SV_TestEntityPosition (edict_t *ent) +{ + trace_t trace; + + trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, ent->v.origin, 0, ent); + + if (trace.startsolid) + return sv.edicts; + + return NULL; +} + +/* +=============================================================================== + +LINE TESTING IN HULLS + +=============================================================================== +*/ + +// 1/32 epsilon to keep floating point happy +#define DIST_EPSILON (0.03125) + +/* +================== +SV_RecursiveHullCheck + +================== +*/ +qboolean SV_RecursiveHullCheck (hull_t *hull, int num, float p1f, float p2f, vec3_t p1, vec3_t p2, trace_t *trace) +{ + dclipnode_t *node; + mplane_t *plane; + float t1, t2; + float frac; + int i; + vec3_t mid; + int side; + float midf; + +// check for empty + if (num < 0) + { + if (num != CONTENTS_SOLID) + { + trace->allsolid = false; + if (num == CONTENTS_EMPTY) + trace->inopen = true; + else + trace->inwater = true; + } + else + trace->startsolid = true; + return true; // empty + } + + if (num < hull->firstclipnode || num > hull->lastclipnode) + SV_Error ("SV_RecursiveHullCheck: bad node number"); + +// +// find the point distances +// + node = hull->clipnodes + num; + plane = hull->planes + node->planenum; + + if (plane->type < 3) + { + t1 = p1[plane->type] - plane->dist; + t2 = p2[plane->type] - plane->dist; + } + else + { + t1 = DotProduct (plane->normal, p1) - plane->dist; + t2 = DotProduct (plane->normal, p2) - plane->dist; + } + +#if 1 + if (t1 >= 0 && t2 >= 0) + return SV_RecursiveHullCheck (hull, node->children[0], p1f, p2f, p1, p2, trace); + if (t1 < 0 && t2 < 0) + return SV_RecursiveHullCheck (hull, node->children[1], p1f, p2f, p1, p2, trace); +#else + if ( (t1 >= DIST_EPSILON && t2 >= DIST_EPSILON) || (t2 > t1 && t1 >= 0) ) + return SV_RecursiveHullCheck (hull, node->children[0], p1f, p2f, p1, p2, trace); + if ( (t1 <= -DIST_EPSILON && t2 <= -DIST_EPSILON) || (t2 < t1 && t1 <= 0) ) + return SV_RecursiveHullCheck (hull, node->children[1], p1f, p2f, p1, p2, trace); +#endif + +// put the crosspoint DIST_EPSILON pixels on the near side + if (t1 < 0) + frac = (t1 + DIST_EPSILON)/(t1-t2); + else + frac = (t1 - DIST_EPSILON)/(t1-t2); + if (frac < 0) + frac = 0; + if (frac > 1) + frac = 1; + + midf = p1f + (p2f - p1f)*frac; + for (i=0 ; i<3 ; i++) + mid[i] = p1[i] + frac*(p2[i] - p1[i]); + + side = (t1 < 0); + +// move up to the node + if (!SV_RecursiveHullCheck (hull, node->children[side], p1f, midf, p1, mid, trace) ) + return false; + +#ifdef PARANOID + if (SV_HullPointContents (sv_hullmodel, mid, node->children[side]) + == CONTENTS_SOLID) + { + Con_Printf ("mid PointInHullSolid\n"); + return false; + } +#endif + + if (SV_HullPointContents (hull, node->children[side^1], mid) + != CONTENTS_SOLID) +// go past the node + return SV_RecursiveHullCheck (hull, node->children[side^1], midf, p2f, mid, p2, trace); + + if (trace->allsolid) + return false; // never got out of the solid area + +//================== +// the other side of the node is solid, this is the impact point +//================== + if (!side) + { + VectorCopy (plane->normal, trace->plane.normal); + trace->plane.dist = plane->dist; + } + else + { + VectorSubtract (vec3_origin, plane->normal, trace->plane.normal); + trace->plane.dist = -plane->dist; + } + + while (SV_HullPointContents (hull, hull->firstclipnode, mid) + == CONTENTS_SOLID) + { // shouldn't really happen, but does occasionally + frac -= 0.1; + if (frac < 0) + { + trace->fraction = midf; + VectorCopy (mid, trace->endpos); + Con_Printf ("backup past 0\n"); + return false; + } + midf = p1f + (p2f - p1f)*frac; + for (i=0 ; i<3 ; i++) + mid[i] = p1[i] + frac*(p2[i] - p1[i]); + } + + trace->fraction = midf; + VectorCopy (mid, trace->endpos); + + return false; +} + + +/* +================== +SV_ClipMoveToEntity + +Handles selection or creation of a clipping hull, and offseting (and +eventually rotation) of the end points +================== +*/ +trace_t SV_ClipMoveToEntity (edict_t *ent, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end) +{ + trace_t trace; + vec3_t offset; + vec3_t start_l, end_l; + hull_t *hull; + +// fill in a default trace + memset (&trace, 0, sizeof(trace_t)); + trace.fraction = 1; + trace.allsolid = true; + VectorCopy (end, trace.endpos); + +// get the clipping hull + hull = SV_HullForEntity (ent, mins, maxs, offset); + + VectorSubtract (start, offset, start_l); + VectorSubtract (end, offset, end_l); + +// trace a line through the apropriate clipping hull + SV_RecursiveHullCheck (hull, hull->firstclipnode, 0, 1, start_l, end_l, &trace); + +// fix trace up by the offset + if (trace.fraction != 1) + VectorAdd (trace.endpos, offset, trace.endpos); + +// did we clip the move? + if (trace.fraction < 1 || trace.startsolid ) + trace.ent = ent; + + return trace; +} + +//=========================================================================== + +/* +==================== +SV_ClipToLinks + +Mins and maxs enclose the entire area swept by the move +==================== +*/ +void SV_ClipToLinks ( areanode_t *node, moveclip_t *clip ) +{ + link_t *l, *next; + edict_t *touch; + trace_t trace; + +// touch linked edicts + for (l = node->solid_edicts.next ; l != &node->solid_edicts ; l = next) + { + next = l->next; + touch = EDICT_FROM_AREA(l); + if (touch->v.solid == SOLID_NOT) + continue; + if (touch == clip->passedict) + continue; + if (touch->v.solid == SOLID_TRIGGER) + SV_Error ("Trigger in clipping list"); + + if (clip->type == MOVE_NOMONSTERS && touch->v.solid != SOLID_BSP) + 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 (PROG_TO_EDICT(touch->v.owner) == clip->passedict) + continue; // don't clip against own missiles + if (PROG_TO_EDICT(clip->passedict->v.owner) == touch) + continue; // don't clip against owner + } + + if ((int)touch->v.flags & FL_MONSTER) + trace = SV_ClipMoveToEntity (touch, clip->start, clip->mins2, clip->maxs2, clip->end); + else + trace = SV_ClipMoveToEntity (touch, clip->start, clip->mins, clip->maxs, clip->end); + if (trace.allsolid || trace.startsolid || + trace.fraction < clip->trace.fraction) + { + trace.ent = touch; + if (clip->trace.startsolid) + { + clip->trace = trace; + clip->trace.startsolid = true; + } + else + clip->trace = trace; + } + else if (trace.startsolid) + clip->trace.startsolid = true; + } + +// recurse down both sides + if (node->axis == -1) + return; + + if ( clip->boxmaxs[node->axis] > node->dist ) + SV_ClipToLinks ( node->children[0], clip ); + if ( clip->boxmins[node->axis] < node->dist ) + SV_ClipToLinks ( node->children[1], clip ); +} + + +/* +================== +SV_MoveBounds +================== +*/ +void SV_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 SV_Move (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int type, edict_t *passedict) +{ + moveclip_t clip; + int i; + + memset ( &clip, 0, sizeof ( moveclip_t ) ); + +// clip to world + clip.trace = SV_ClipMoveToEntity ( sv.edicts, start, mins, maxs, end ); + + clip.start = start; + clip.end = end; + clip.mins = mins; + clip.maxs = maxs; + clip.type = type; + clip.passedict = passedict; + + 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 + SV_MoveBounds ( start, clip.mins2, clip.maxs2, end, clip.boxmins, clip.boxmaxs ); + +// clip to entities + SV_ClipToLinks ( sv_areanodes, &clip ); + + return clip.trace; +} + +//============================================================================= + +/* +============ +SV_TestPlayerPosition + +============ +*/ +edict_t *SV_TestPlayerPosition (edict_t *ent, vec3_t origin) +{ + hull_t *hull; + edict_t *check; + vec3_t boxmins, boxmaxs; + vec3_t offset; + int e; + +// check world first + hull = &sv.worldmodel->hulls[1]; + if ( SV_HullPointContents (hull, hull->firstclipnode, origin) != CONTENTS_EMPTY ) + return sv.edicts; + +// check all entities + VectorAdd (origin, ent->v.mins, boxmins); + VectorAdd (origin, ent->v.maxs, boxmaxs); + + check = NEXT_EDICT(sv.edicts); + for (e=1 ; e<sv.num_edicts ; e++, check = NEXT_EDICT(check)) + { + if (check->free) + continue; + if (check->v.solid != SOLID_BSP && + check->v.solid != SOLID_BBOX && + check->v.solid != SOLID_SLIDEBOX) + continue; + + if (boxmins[0] > check->v.absmax[0] + || boxmins[1] > check->v.absmax[1] + || boxmins[2] > check->v.absmax[2] + || boxmaxs[0] < check->v.absmin[0] + || boxmaxs[1] < check->v.absmin[1] + || boxmaxs[2] < check->v.absmin[2] ) + continue; + + if (check == ent) + continue; + + // get the clipping hull + hull = SV_HullForEntity (check, ent->v.mins, ent->v.maxs, offset); + + VectorSubtract (origin, offset, offset); + + // test the point + if ( SV_HullPointContents (hull, hull->firstclipnode, offset) != CONTENTS_EMPTY ) + return check; + } + + return NULL; +} + + diff --git a/QW/server/world.h b/QW/server/world.h new file mode 100644 index 0000000..3dd9e81 --- /dev/null +++ b/QW/server/world.h @@ -0,0 +1,93 @@ +/* +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.h + +typedef struct +{ + vec3_t normal; + float dist; +} plane_t; + +typedef struct +{ + qboolean allsolid; // if true, plane is not valid + qboolean startsolid; // if true, the initial point was in a solid area + qboolean inopen, inwater; + float fraction; // time completed, 1.0 = didn't hit anything + vec3_t endpos; // final position + plane_t plane; // surface normal at impact + edict_t *ent; // entity the surface is on +} trace_t; + + +#define MOVE_NORMAL 0 +#define MOVE_NOMONSTERS 1 +#define MOVE_MISSILE 2 + +typedef struct areanode_s +{ + int axis; // -1 = leaf node + float dist; + struct areanode_s *children[2]; + link_t trigger_edicts; + link_t solid_edicts; +} areanode_t; + +#define AREA_DEPTH 4 +#define AREA_NODES 32 + +extern areanode_t sv_areanodes[AREA_NODES]; + + +void SV_ClearWorld (void); +// called after the world model has been loaded, before linking any entities + +void SV_UnlinkEdict (edict_t *ent); +// call before removing an entity, and before trying to move one, +// so it doesn't clip against itself +// flags ent->v.modified + +void SV_LinkEdict (edict_t *ent, qboolean touch_triggers); +// Needs to be called any time an entity changes origin, mins, maxs, or solid +// flags ent->v.modified +// sets ent->v.absmin and ent->v.absmax +// if touchtriggers, calls prog functions for the intersected triggers + +int SV_PointContents (vec3_t p); +// returns the CONTENTS_* value from the world at the given point. +// does not check any entities at all + +edict_t *SV_TestEntityPosition (edict_t *ent); + +trace_t SV_Move (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int type, edict_t *passedict); +// mins and maxs are reletive + +// if the entire move stays in a solid volume, trace.allsolid will be set + +// if the starting point is in a solid, it will be allowed to move out +// to an open area + +// nomonsters is used for line of sight or edge testing, where mosnters +// shouldn't be considered solid objects + +// passedict is explicitly excluded from clipping checks (normally NULL) + + +edict_t *SV_TestPlayerPosition (edict_t *ent, vec3_t origin); diff --git a/QW/server/worlda.s b/QW/server/worlda.s new file mode 100644 index 0000000..c4f57fa --- /dev/null +++ b/QW/server/worlda.s @@ -0,0 +1,124 @@ +// +// worlda.s +// x86 assembly-language server testing stuff +// + +#include "asm_i386.h" +#include "quakeasm.h" +//include "d_ifacea.h" + +#if id386 + + .data + +Ltemp: .long 0 + + .text + +//---------------------------------------------------------------------- +// hull-point test +//---------------------------------------------------------------------- + +#define hull 4+8 // because only partially pushed +#define num 8+4 // because only partially pushed +#define p 12+12 // because only partially pushed + + .align 4 +.globl C(SV_HullPointContents) +C(SV_HullPointContents): + pushl %edi // preserve register variables + movl num(%esp),%eax + testl %eax,%eax + js Lhquickout + +// float d; +// dclipnode_t *node; +// mplane_t *plane; + + pushl %ebx + movl hull(%esp),%ebx + + pushl %ebp + movl p(%esp),%edx + + movl hu_clipnodes(%ebx),%edi + movl hu_planes(%ebx),%ebp + + subl %ebx,%ebx + pushl %esi + +// %ebx: 0 +// %eax: num +// %edx: p +// %edi: hull->clipnodes +// %ebp: hull->planes + +// while (num >= 0) +// { + +Lhloop: + +// node = hull->clipnodes + num; +// plane = hull->planes + node->planenum; +// !!! if the size of dclipnode_t changes, the scaling of %eax needs to be +// changed !!! + movl nd_planenum(%edi,%eax,8),%ecx + movl nd_children(%edi,%eax,8),%eax + movl %eax,%esi + rorl $16,%eax + leal (%ecx,%ecx,4),%ecx + +// if (plane->type < 3) +// d = p[plane->type] - plane->dist; + movl pl_type(%ebp,%ecx,4),%bl + cmpb $3,%bl + jb Lnodot + +// else +// d = DotProduct (plane->normal, p) - plane->dist; + flds pl_normal(%ebp,%ecx,4) + fmuls 0(%edx) + flds pl_normal+4(%ebp,%ecx,4) + fmuls 4(%edx) + flds pl_normal+8(%ebp,%ecx,4) + fmuls 8(%edx) + fxch %st(1) + faddp %st(0),%st(2) + faddp %st(0),%st(1) + fsubs pl_dist(%ebp,%ecx,4) + jmp Lsub + +Lnodot: + flds pl_dist(%ebp,%ecx,4) + fsubrs (%edx,%ebx,4) + +Lsub: + sarl $16,%eax + sarl $16,%esi + +// if (d < 0) +// num = node->children[1]; +// else +// num = node->children[0]; + fstps Ltemp + movl Ltemp,%ecx + sarl $31,%ecx + andl %ecx,%esi + xorl $0xFFFFFFFF,%ecx + andl %ecx,%eax + orl %esi,%eax + jns Lhloop + +// return num; +Lhdone: + popl %esi + popl %ebp + popl %ebx // restore register variables + +Lhquickout: + popl %edi + + ret + +#endif // id386 + |
