The Quake sources as originally release under the GPL license on December 21, 1999

This commit is contained in:
Travis Bradshaw
2012-01-31 14:32:09 -06:00
commit 0023db327b
605 changed files with 282548 additions and 0 deletions

97
QW/server/asm_i386.h Normal file
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

39
QW/server/move.txt Normal file
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

180
QW/server/pr_comp.h Normal file
View 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

File diff suppressed because it is too large Load Diff

699
QW/server/pr_exec.c Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

BIN
QW/server/qwsv.mdp Normal file

Binary file not shown.

107
QW/server/qwsv.plg Normal file
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

419
QW/server/sv_move.c Normal file
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

35
QW/server/sys.h Normal file
View 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
View 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
View 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
View 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
View 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
View 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