mirror of
https://github.com/id-Software/Quake.git
synced 2026-03-20 00:49:48 +01:00
The Quake sources as originally release under the GPL license on December 21, 1999
This commit is contained in:
97
QW/server/asm_i386.h
Normal file
97
QW/server/asm_i386.h
Normal file
@@ -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
|
||||
|
||||
52
QW/server/makefile
Normal file
52
QW/server/makefile
Normal file
@@ -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
|
||||
|
||||
331
QW/server/math.s
Normal file
331
QW/server/math.s
Normal file
@@ -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
|
||||
1137
QW/server/model.c
Normal file
1137
QW/server/model.c
Normal file
File diff suppressed because it is too large
Load Diff
39
QW/server/move.txt
Normal file
39
QW/server/move.txt
Normal file
@@ -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
|
||||
|
||||
|
||||
|
||||
|
||||
121
QW/server/newnet.txt
Normal file
121
QW/server/newnet.txt
Normal file
@@ -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
|
||||
7
QW/server/notes.txt
Normal file
7
QW/server/notes.txt
Normal file
@@ -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
|
||||
1722
QW/server/pr_cmds.c
Normal file
1722
QW/server/pr_cmds.c
Normal file
File diff suppressed because it is too large
Load Diff
180
QW/server/pr_comp.h
Normal file
180
QW/server/pr_comp.h
Normal file
@@ -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;
|
||||
|
||||
1085
QW/server/pr_edict.c
Normal file
1085
QW/server/pr_edict.c
Normal file
File diff suppressed because it is too large
Load Diff
699
QW/server/pr_exec.c
Normal file
699
QW/server/pr_exec.c
Normal file
@@ -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);
|
||||
}
|
||||
|
||||
41
QW/server/profile.txt
Normal file
41
QW/server/profile.txt
Normal file
@@ -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)
|
||||
|
||||
159
QW/server/progdefs.h
Normal file
159
QW/server/progdefs.h
Normal file
@@ -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
|
||||
147
QW/server/progs.h
Normal file
147
QW/server/progs.h
Normal file
@@ -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);
|
||||
|
||||
32
QW/server/quakeasm.h
Normal file
32
QW/server/quakeasm.h
Normal file
@@ -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
|
||||
|
||||
364
QW/server/qwsv.dsp
Normal file
364
QW/server/qwsv.dsp
Normal file
@@ -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
|
||||
29
QW/server/qwsv.dsw
Normal file
29
QW/server/qwsv.dsw
Normal file
@@ -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>
|
||||
{{{
|
||||
}}}
|
||||
|
||||
###############################################################################
|
||||
|
||||
2207
QW/server/qwsv.mak
Normal file
2207
QW/server/qwsv.mak
Normal file
File diff suppressed because it is too large
Load Diff
BIN
QW/server/qwsv.mdp
Normal file
BIN
QW/server/qwsv.mdp
Normal file
Binary file not shown.
107
QW/server/qwsv.plg
Normal file
107
QW/server/qwsv.plg
Normal file
@@ -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>
|
||||
95
QW/server/qwsvdef.h
Normal file
95
QW/server/qwsvdef.h
Normal file
@@ -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, ...);
|
||||
|
||||
456
QW/server/server.h
Normal file
456
QW/server/server.h
Normal file
@@ -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);
|
||||
|
||||
900
QW/server/sv_ccmds.c
Normal file
900
QW/server/sv_ccmds.c
Normal file
@@ -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;
|
||||
}
|
||||
517
QW/server/sv_ents.c
Normal file
517
QW/server/sv_ents.c
Normal file
@@ -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);
|
||||
}
|
||||
411
QW/server/sv_init.c
Normal file
411
QW/server/sv_init.c
Normal file
@@ -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");
|
||||
}
|
||||
|
||||
1658
QW/server/sv_main.c
Normal file
1658
QW/server/sv_main.c
Normal file
File diff suppressed because it is too large
Load Diff
419
QW/server/sv_move.c
Normal file
419
QW/server/sv_move.c
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
165
QW/server/sv_nchan.c
Normal file
165
QW/server/sv_nchan.c
Normal file
@@ -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);
|
||||
}
|
||||
|
||||
947
QW/server/sv_phys.c
Normal file
947
QW/server/sv_phys.c
Normal file
@@ -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;
|
||||
}
|
||||
806
QW/server/sv_send.c
Normal file
806
QW/server/sv_send.c
Normal file
@@ -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 ();
|
||||
}
|
||||
|
||||
1702
QW/server/sv_user.c
Normal file
1702
QW/server/sv_user.c
Normal file
File diff suppressed because it is too large
Load Diff
35
QW/server/sys.h
Normal file
35
QW/server/sys.h
Normal file
@@ -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);
|
||||
283
QW/server/sys_unix.c
Normal file
283
QW/server/sys_unix.c
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
260
QW/server/sys_win.c
Normal file
260
QW/server/sys_win.c
Normal file
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
924
QW/server/world.c
Normal file
924
QW/server/world.c
Normal file
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
93
QW/server/world.h
Normal file
93
QW/server/world.h
Normal file
@@ -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);
|
||||
124
QW/server/worlda.s
Normal file
124
QW/server/worlda.s
Normal file
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user