All changes present in QSS-R7, plus

md3 support
quoth/etc bug fix
strzone bug fix
png/jpg replacement wall textures
rewrote mdl rendering to always use arrays, relaxing vertex+tri limits.
removed static ents limit
fixed ambient_level not working at high framerates.
This commit is contained in:
Spike 2017-09-17 03:12:53 +01:00 committed by Shpoike
parent ad3aadb373
commit d76ca606bf
99 changed files with 35331 additions and 2968 deletions

15
Quake/Makefile Normal file → Executable file
View File

@ -10,6 +10,9 @@ DO_USERDIRS=0
### Enable/Disable SDL2
USE_SDL2=0
### Enable the use of zlib, for compressed pk3s.
USE_ZLIB=1
### Enable/Disable codecs for streaming music support
USE_CODEC_WAVE=1
USE_CODEC_FLAC=0
@ -176,7 +179,12 @@ ifeq ($(USE_CODEC_UMX),1)
CFLAGS+= -DUSE_CODEC_UMX
endif
COMMON_LIBS:= -lm -lGL
COMMON_LIBS:= -ldl -lm -lGL
ifeq ($(USE_ZLIB),1)
CFLAGS+= -DUSE_ZLIB
COMMON_LIBS+= -lz
endif
LIBS := $(COMMON_LIBS) $(NET_LIBS) $(CODECLIBS)
@ -210,7 +218,7 @@ MUSIC_OBJS:= bgmusic.o \
snd_mikmod.o \
snd_xmp.o \
snd_umx.o
COMOBJ_SND := snd_dma.o snd_mix.o snd_mem.o $(MUSIC_OBJS)
COMOBJ_SND := snd_voip.o snd_dma.o snd_mix.o snd_mem.o $(MUSIC_OBJS)
SYSOBJ_SND := snd_sdl.o
SYSOBJ_CDA := cd_sdl.o
SYSOBJ_INPUT := in_sdl.o
@ -227,6 +235,7 @@ GLOBJS = \
gl_fog.o \
gl_rmisc.o \
r_part.o \
r_part_fte.o \
r_world.o \
gl_screen.o \
gl_sky.o \
@ -266,6 +275,7 @@ OBJS := strlcat.o \
wad.o \
cmd.o \
common.o \
fs_zip.o \
crc.o \
cvar.o \
cfgfile.o \
@ -273,6 +283,7 @@ OBJS := strlcat.o \
host_cmd.o \
mathlib.o \
pr_cmds.o \
pr_ext.o \
pr_edict.o \
pr_exec.o \
sv_main.o \

11
Quake/Makefile.darwin Normal file → Executable file
View File

@ -40,6 +40,7 @@ HOST_OS := $(shell uname|sed -e s/_.*//|tr '[:upper:]' '[:lower:]')
MACH_TYPE= $(shell sh detect.sh arch)
DEBUG ?= 0
USE_ZLIB?= 1
# ---------------------------
# build variables
@ -200,6 +201,11 @@ CFLAGS+= $(CODEC_INC)
COMMON_LIBS:= -Wl,-framework,IOKit -Wl,-framework,OpenGL
ifeq ($(USE_ZLIB),1)
CFLAGS+= -DUSE_ZLIB
COMMON_LIBS+= `$(CC) -print-file-name=libz.a`
endif
LIBS := $(COMMON_LIBS) $(NET_LIBS) $(CODEC_LINK) $(CODECLIBS)
# ---------------------------
@ -236,7 +242,7 @@ MUSIC_OBJS:= bgmusic.o \
snd_mikmod.o \
snd_xmp.o \
snd_umx.o
COMOBJ_SND := snd_dma.o snd_mix.o snd_mem.o $(MUSIC_OBJS)
COMOBJ_SND := snd_voip.o snd_dma.o snd_mix.o snd_mem.o $(MUSIC_OBJS)
SYSOBJ_SND := snd_sdl.o
SYSOBJ_CDA := cd_sdl.o
SYSOBJ_INPUT := in_sdl.o
@ -253,6 +259,7 @@ GLOBJS = \
gl_fog.o \
gl_rmisc.o \
r_part.o \
r_part_fte.o \
r_world.o \
gl_screen.o \
gl_sky.o \
@ -292,6 +299,7 @@ OBJS := strlcat.o \
wad.o \
cmd.o \
common.o \
fs_zip.o \
crc.o \
cvar.o \
cfgfile.o \
@ -299,6 +307,7 @@ OBJS := strlcat.o \
host_cmd.o \
mathlib.o \
pr_cmds.o \
pr_ext.o \
pr_edict.o \
pr_exec.o \
sv_main.o \

15
Quake/Makefile.w32 Normal file → Executable file
View File

@ -33,7 +33,8 @@ check_gcc = $(shell if echo | $(CC) $(1) -Werror -S -o /dev/null -xc - > /dev/nu
# ---------------------------
DEBUG ?= 0
WINSOCK2?= 0
WINSOCK2?= 1
USE_ZLIB?= 1
# ---------------------------
# build variables
@ -48,7 +49,7 @@ STRIP = strip
#CPUFLAGS= -mtune=i686
#CPUFLAGS= -march=pentium4
CPUFLAGS=
LDFLAGS = -m32 -mwindows
LDFLAGS = -m32 -mwindows -Wl,--large-address-aware
DFLAGS ?=
CFLAGS ?= -m32 -Wall -Wno-trigraphs
CFLAGS += $(CPUFLAGS)
@ -166,6 +167,11 @@ CFLAGS+= $(CODEC_INC)
COMMON_LIBS:= -lm -lopengl32 -lwinmm
ifeq ($(USE_ZLIB),1)
CFLAGS+= -DUSE_ZLIB
COMMON_LIBS+= `$(CC) -print-file-name=libz.a`
endif
LIBS := $(COMMON_LIBS) $(NET_LIBS) $(CODEC_LINK) $(CODECLIBS)
# ---------------------------
@ -200,7 +206,7 @@ MUSIC_OBJS:= bgmusic.o \
snd_mikmod.o \
snd_xmp.o \
snd_umx.o
COMOBJ_SND := snd_dma.o snd_mix.o snd_mem.o $(MUSIC_OBJS)
COMOBJ_SND := snd_voip.o snd_dma.o snd_mix.o snd_mem.o $(MUSIC_OBJS)
SYSOBJ_SND := snd_sdl.o
SYSOBJ_CDA := cd_sdl.o
SYSOBJ_INPUT := in_sdl.o
@ -217,6 +223,7 @@ GLOBJS = \
gl_fog.o \
gl_rmisc.o \
r_part.o \
r_part_fte.o \
r_world.o \
gl_screen.o \
gl_sky.o \
@ -256,6 +263,7 @@ OBJS := strlcat.o \
wad.o \
cmd.o \
common.o \
fs_zip.o \
crc.o \
cvar.o \
cfgfile.o \
@ -263,6 +271,7 @@ OBJS := strlcat.o \
host_cmd.o \
mathlib.o \
pr_cmds.o \
pr_ext.o \
pr_edict.o \
pr_exec.o \
sv_main.o \

18
Quake/Makefile.w64 Normal file → Executable file
View File

@ -34,6 +34,7 @@ check_gcc = $(shell if echo | $(CC) $(1) -Werror -S -o /dev/null -xc - > /dev/nu
DEBUG ?= 0
WINSOCK2?= 1
USE_ZLIB?= 1
# ---------------------------
# build variables
@ -101,11 +102,11 @@ $(error Invalid MP3LIB setting)
endif
endif
ifeq ($(MP3LIB),mad)
mp3_obj=snd_mp3
mp3_obj=snd_mp3.o
lib_mp3dec=-lmad
endif
ifeq ($(MP3LIB),mpg123)
mp3_obj=snd_mpg123
mp3_obj=snd_mpg123.o
lib_mp3dec=-lmpg123
endif
ifeq ($(VORBISLIB),vorbis)
@ -164,6 +165,11 @@ CFLAGS+= $(CODEC_INC)
COMMON_LIBS:= -lm -lopengl32 -lwinmm
ifeq ($(USE_ZLIB),1)
CFLAGS+= -DUSE_ZLIB
COMMON_LIBS+= `$(CC) -print-file-name=libz.a`
endif
LIBS := $(COMMON_LIBS) $(NET_LIBS) $(CODEC_LINK) $(CODECLIBS)
# ---------------------------
@ -193,12 +199,11 @@ MUSIC_OBJS:= bgmusic.o \
snd_wave.o \
snd_vorbis.o \
snd_opus.o \
$(mp3_obj).o \
snd_mp3tag.o \
$(mp3_obj) \
snd_mikmod.o \
snd_xmp.o \
snd_umx.o
COMOBJ_SND := snd_dma.o snd_mix.o snd_mem.o $(MUSIC_OBJS)
COMOBJ_SND := snd_voip.o snd_dma.o snd_mix.o snd_mem.o $(MUSIC_OBJS)
SYSOBJ_SND := snd_sdl.o
SYSOBJ_CDA := cd_sdl.o
SYSOBJ_INPUT := in_sdl.o
@ -215,6 +220,7 @@ GLOBJS = \
gl_fog.o \
gl_rmisc.o \
r_part.o \
r_part_fte.o \
r_world.o \
gl_screen.o \
gl_sky.o \
@ -254,6 +260,7 @@ OBJS := strlcat.o \
wad.o \
cmd.o \
common.o \
fs_zip.o \
crc.o \
cvar.o \
cfgfile.o \
@ -261,6 +268,7 @@ OBJS := strlcat.o \
host_cmd.o \
mathlib.o \
pr_cmds.o \
pr_ext.o \
pr_edict.o \
pr_exec.o \
sv_main.o \

11
Quake/OWMakefile.win32 Normal file → Executable file
View File

@ -21,6 +21,8 @@ MP3LIB=mad
VORBISLIB=vorbis
WINSOCK2= 0
#FIXME: set this to 1
USE_ZLIB=0
# ---------------------------
# build variables
@ -106,6 +108,10 @@ CFLAGS+= -DUSE_CODEC_UMX
CFLAGS+= $(CODEC_INC)
COMMON_LIBS= opengl32.lib winmm.lib
!ifeq USE_ZLIB 1
CFLAGS+= -DUSE_ZLIB
COMMON_LIBS+= -lz
!endif
LIBS = $(CODECLIBS) $(SDL_LIBS) $(COMMON_LIBS) $(NET_LIBS)
@ -145,7 +151,7 @@ MUSIC_OBJS= bgmusic.obj &
snd_mikmod.obj &
snd_xmp.obj &
snd_umx.obj
COMOBJ_SND = snd_dma.obj snd_mix.obj snd_mem.obj $(MUSIC_OBJS)
COMOBJ_SND = snd_voip.obj snd_dma.obj snd_mix.obj snd_mem.obj $(MUSIC_OBJS)
SYSOBJ_SND = snd_sdl.obj
SYSOBJ_CDA = cd_sdl.obj
SYSOBJ_INPUT = in_sdl.obj
@ -167,6 +173,7 @@ GLOBJS = &
gl_fog.obj &
gl_rmisc.obj &
r_part.obj &
r_part_fte.obj &
r_world.obj &
gl_screen.obj &
gl_sky.obj &
@ -206,6 +213,7 @@ OBJS = strlcat.obj &
wad.obj &
cmd.obj &
common.obj &
fs_zip.obj &
crc.obj &
cvar.obj &
cfgfile.obj &
@ -213,6 +221,7 @@ OBJS = strlcat.obj &
host_cmd.obj &
mathlib.obj &
pr_cmds.obj &
pr_ext.obj &
pr_edict.obj &
pr_exec.obj &
sv_main.obj &

View File

@ -98,7 +98,23 @@ typedef struct
int headnode[MAX_MAP_HULLS];
int visleafs; // not including the solid leaf 0
int firstface, numfaces;
} dmodel_t;
} mmodel_t;
typedef struct
{
float mins[3], maxs[3];
float origin[3];
int headnode[4];
int visleafs; // not including the solid leaf 0
int firstface, numfaces;
} dmodelq1_t;
typedef struct
{
float mins[3], maxs[3];
float origin[3];
int headnode[8];
int visleafs; // not including the solid leaf 0
int firstface, numfaces;
} dmodelh2_t;
typedef struct
{

View File

@ -111,7 +111,7 @@ void Chase_UpdateForDrawing (void)
// calculate camera angles to look at the same spot
VectorSubtract (crosshair, r_refdef.vieworg, temp);
VectorAngles (temp, r_refdef.viewangles);
VectorAngles (temp, NULL, r_refdef.viewangles);
if (r_refdef.viewangles[PITCH] == 90 || r_refdef.viewangles[PITCH] == -90)
r_refdef.viewangles[YAW] = cl.viewangles[YAW];
}

View File

@ -36,10 +36,6 @@ read from the demo file.
==============================================================================
*/
// from ProQuake: space to fill out the demo header for record at any time
static byte demo_head[3][MAX_MSGLEN];
static int demo_head_size[2];
/*
==============
CL_StopPlayback
@ -166,14 +162,6 @@ int CL_GetMessage (void)
if (cls.demorecording)
CL_WriteDemoMessage ();
if (cls.signon < 2)
{
// record messages before full connection, so that a
// demo record can happen after connection is done
memcpy(demo_head[cls.signon], net_message.data, net_message.cursize);
demo_head_size[cls.signon] = net_message.cursize;
}
return r;
}
@ -207,10 +195,234 @@ void CL_Stop_f (void)
cls.demorecording = false;
Con_Printf ("Completed demo\n");
Cvar_SetROM(cl_recordingdemo.name, "");
// ericw -- update demo tab-completion list
DemoList_Rebuild ();
}
static void CL_Record_Serverdata(void)
{
size_t i;
MSG_WriteByte(&net_message, svc_serverinfo);
if (cl.protocol_pext2)
{
MSG_WriteLong (&net_message, PROTOCOL_FTE_PEXT2);
MSG_WriteLong (&net_message, cl.protocol_pext2);
}
MSG_WriteLong (&net_message, cl.protocol);
if (cl.protocol == PROTOCOL_RMQ)
MSG_WriteLong (&net_message, cl.protocolflags);
if (cl.protocol_pext2 & PEXT2_PREDINFO)
MSG_WriteString(&net_message, COM_SkipPath(com_gamedir));
MSG_WriteByte (&net_message, cl.maxclients);
MSG_WriteByte (&net_message, cl.gametype);
MSG_WriteString (&net_message, cl.levelname);
for (i=1; cl.model_precache[i]; i++)
MSG_WriteString (&net_message, cl.model_precache[i]->name);
MSG_WriteByte (&net_message, 0);
for (i=1; cl.sound_precache[i]; i++) //FIXME: might not send any if nosound is set
MSG_WriteString (&net_message, cl.sound_precache[i]->name);
MSG_WriteByte (&net_message, 0);
//FIXME: cd track (current rather than initial?)
//FIXME: initial view entity (for clients that don't want to mess up scoreboards)
MSG_WriteByte (&net_message, svc_signonnum);
MSG_WriteByte (&net_message, 1);
CL_WriteDemoMessage();
SZ_Clear (&net_message);
}
//spins out a baseline(idx>=0) or static entity(idx<0) into net_message
void CL_Record_Prespawn(void)
{
int idx, i;
//baselines
for (idx = 0; idx < cl.num_entities; idx++)
{
entity_state_t *state = &cl.entities[idx].baseline;
if (!memcmp(state, &nullentitystate, sizeof(entity_state_t)))
continue; //no need
MSG_WriteStaticOrBaseLine(&net_message, idx, state, cl.protocol_pext2, cl.protocol, cl.protocolflags);
if (net_message.cursize > 4096)
{ //periodically flush so that large maps don't need larger than vanilla limits
CL_WriteDemoMessage();
SZ_Clear (&net_message);
}
}
//static ents
for (idx = 1; idx < cl.num_statics; idx++)
{
MSG_WriteStaticOrBaseLine(&net_message, -1, &cl.static_entities[idx]->baseline, cl.protocol_pext2, cl.protocol, cl.protocolflags);
if (net_message.cursize > 4096)
{ //periodically flush so that large maps don't need larger than vanilla limits
CL_WriteDemoMessage();
SZ_Clear (&net_message);
}
}
//static sounds
for (i = NUM_AMBIENTS; i < total_channels; i++)
{
channel_t *ss = &snd_channels[i];
sfxcache_t *sc;
if (!ss->sfx)
continue;
if (ss->entnum || ss->entchannel)
continue; //can't have been a static sound
sc = S_LoadSound(ss->sfx);
if (!sc || sc->loopstart == -1)
continue; //can't have been a (valid) static sound
for (idx = 1; idx < MAX_SOUNDS && cl.sound_precache[idx]; idx++)
if (cl.sound_precache[idx] == ss->sfx)
break;
if (idx == MAX_SOUNDS)
continue; //can't figure out which sound it was
MSG_WriteByte(&net_message, (idx > 255)?svc_spawnstaticsound2:svc_spawnstaticsound);
MSG_WriteCoord(&net_message, ss->origin[0], cl.protocolflags);
MSG_WriteCoord(&net_message, ss->origin[1], cl.protocolflags);
MSG_WriteCoord(&net_message, ss->origin[2], cl.protocolflags);
if (idx > 255)
MSG_WriteShort(&net_message, idx);
else
MSG_WriteByte(&net_message, idx);
MSG_WriteByte(&net_message, ss->master_vol);
MSG_WriteByte(&net_message, ss->dist_mult*1000*64);
if (net_message.cursize > 4096)
{ //periodically flush so that large maps don't need larger than vanilla limits
CL_WriteDemoMessage();
SZ_Clear (&net_message);
}
}
#ifdef PSET_SCRIPT
//particleindexes
for (idx = 0; idx < MAX_PARTICLETYPES; idx++)
{
if (!cl.particle_precache[idx].name)
continue;
MSG_WriteByte(&net_message, svcdp_precache);
MSG_WriteShort(&net_message, 0x4000 | idx);
MSG_WriteString(&net_message, cl.particle_precache[idx].name);
if (net_message.cursize > 4096)
{ //periodically flush so that large maps don't need larger than vanilla limits
CL_WriteDemoMessage();
SZ_Clear (&net_message);
}
}
#endif
MSG_WriteByte (&net_message, svc_signonnum);
MSG_WriteByte (&net_message, 2);
CL_WriteDemoMessage();
SZ_Clear (&net_message);
}
void CL_Record_Spawn(void)
{
const char *cmd;
int i;
// player names, colors, and frag counts
for (i = 0; i < cl.maxclients; i++)
{
MSG_WriteByte (&net_message, svc_updatename);
MSG_WriteByte (&net_message, i);
MSG_WriteString (&net_message, cl.scores[i].name);
MSG_WriteByte (&net_message, svc_updatefrags);
MSG_WriteByte (&net_message, i);
MSG_WriteShort (&net_message, cl.scores[i].frags);
MSG_WriteByte (&net_message, svc_updatecolors);
MSG_WriteByte (&net_message, i);
MSG_WriteByte (&net_message, cl.scores[i].colors);
}
// send all current light styles
for (i = 0; i < MAX_LIGHTSTYLES; i++)
{
if (*cl_lightstyle[i].map)
{
MSG_WriteByte (&net_message, svc_lightstyle);
MSG_WriteByte (&net_message, i);
MSG_WriteString (&net_message, cl_lightstyle[i].map);
}
if (net_message.cursize > 4096)
{ //periodically flush so that large maps don't need larger than vanilla limits
CL_WriteDemoMessage();
SZ_Clear (&net_message);
}
}
// what about the current CD track... future consideration.
//if this mod is using dynamic fog, make sure we start with the right values.
cmd = Fog_GetFogCommand();
if (cmd)
{
MSG_WriteByte (&net_message, svc_stufftext);
MSG_WriteString (&net_message, cmd);
}
//stats
for (i = 0; i < MAX_CL_STATS; i++)
{
if (!cl.stats[i] && !cl.statsf[i])
continue;
if (net_message.cursize > 4096)
{ //periodically flush so that large maps don't need larger than vanilla limits
CL_WriteDemoMessage();
SZ_Clear (&net_message);
}
if ((double)cl.stats[i] != cl.statsf[i] && (unsigned int)cl.stats[i] <= 0x00ffffff)
{ //if the float representation seems to have more precision then use that, unless its getting huge in which case we're probably getting fpu truncation, so go back to more compatible ints
MSG_WriteByte (&net_message, svcfte_updatestatfloat);
MSG_WriteByte (&net_message, i);
MSG_WriteFloat (&net_message, cl.statsf[i]);
}
else if (cl.stats[i] >= 0 && cl.stats[i] <= 255 && (cl.protocol_pext2 & PEXT2_PREDINFO))
{
MSG_WriteByte (&net_message, svcdp_updatestatbyte);
MSG_WriteByte (&net_message, i);
MSG_WriteByte (&net_message, cl.stats[i]);
}
else
{
MSG_WriteByte (&net_message, svc_updatestat);
MSG_WriteByte (&net_message, i);
MSG_WriteLong (&net_message, cl.stats[i]);
}
}
// view entity
MSG_WriteByte (&net_message, svc_setview);
MSG_WriteShort (&net_message, cl.viewentity);
// signon
MSG_WriteByte (&net_message, svc_signonnum);
MSG_WriteByte (&net_message, 3);
CL_WriteDemoMessage();
SZ_Clear (&net_message);
//ask the server to reset entity deltas. yes this means playback will wait a couple of frames before it actually starts playing but oh well.
if (cl.protocol_pext2 & PEXT2_REPLACEMENTDELTAS)
{
cl.ackframes_count = 0;
cl.ackframes[cl.ackframes_count++] = -1;
}
}
/*
====================
CL_Record_f
@ -260,6 +472,23 @@ void CL_Record_f (void)
Con_Printf("Can't record - try again when connected\n");
return;
}
switch(cl.protocol)
{
case PROTOCOL_NETQUAKE:
case PROTOCOL_FITZQUAKE:
case PROTOCOL_RMQ:
case PROTOCOL_VERSION_BJP3:
break;
//case PROTOCOL_VERSION_NEHD:
//case PROTOCOL_VERSION_DP5:
//case PROTOCOL_VERSION_DP6:
case PROTOCOL_VERSION_DP7:
//case PROTOCOL_VERSION_BJP1:
//case PROTOCOL_VERSION_BJP2:
default:
Con_Printf("Can not record - protocol not supported for recording mid-map\nClient demo recording must be started before connecting\n");
return;
}
}
// write the forced cd track number, or -1
@ -286,11 +515,14 @@ void CL_Record_f (void)
// open the demo file
COM_AddExtension (name, ".dem", sizeof(name));
Cvar_SetROM(cl_recordingdemo.name, name);
Con_Printf ("recording to %s.\n", name);
cls.demofile = fopen (name, "wb");
if (!cls.demofile)
{
Con_Printf ("ERROR: couldn't create %s\n", name);
Cvar_SetROM(cl_recordingdemo.name, "");
return;
}
@ -304,66 +536,14 @@ void CL_Record_f (void)
{
byte *data = net_message.data;
int cursize = net_message.cursize;
int i;
byte weirdaltbufferthatprobablyisntneeded[NET_MAXMESSAGE];
for (i = 0; i < 2; i++)
{
net_message.data = demo_head[i];
net_message.cursize = demo_head_size[i];
CL_WriteDemoMessage();
}
net_message.data = demo_head[2];
net_message.data = weirdaltbufferthatprobablyisntneeded;
SZ_Clear (&net_message);
// current names, colors, and frag counts
for (i = 0; i < cl.maxclients; i++)
{
MSG_WriteByte (&net_message, svc_updatename);
MSG_WriteByte (&net_message, i);
MSG_WriteString (&net_message, cl.scores[i].name);
MSG_WriteByte (&net_message, svc_updatefrags);
MSG_WriteByte (&net_message, i);
MSG_WriteShort (&net_message, cl.scores[i].frags);
MSG_WriteByte (&net_message, svc_updatecolors);
MSG_WriteByte (&net_message, i);
MSG_WriteByte (&net_message, cl.scores[i].colors);
}
// send all current light styles
for (i = 0; i < MAX_LIGHTSTYLES; i++)
{
MSG_WriteByte (&net_message, svc_lightstyle);
MSG_WriteByte (&net_message, i);
MSG_WriteString (&net_message, cl_lightstyle[i].map);
}
// what about the CD track or SVC fog... future consideration.
MSG_WriteByte (&net_message, svc_updatestat);
MSG_WriteByte (&net_message, STAT_TOTALSECRETS);
MSG_WriteLong (&net_message, cl.stats[STAT_TOTALSECRETS]);
MSG_WriteByte (&net_message, svc_updatestat);
MSG_WriteByte (&net_message, STAT_TOTALMONSTERS);
MSG_WriteLong (&net_message, cl.stats[STAT_TOTALMONSTERS]);
MSG_WriteByte (&net_message, svc_updatestat);
MSG_WriteByte (&net_message, STAT_SECRETS);
MSG_WriteLong (&net_message, cl.stats[STAT_SECRETS]);
MSG_WriteByte (&net_message, svc_updatestat);
MSG_WriteByte (&net_message, STAT_MONSTERS);
MSG_WriteLong (&net_message, cl.stats[STAT_MONSTERS]);
// view entity
MSG_WriteByte (&net_message, svc_setview);
MSG_WriteShort (&net_message, cl.viewentity);
// signon
MSG_WriteByte (&net_message, svc_signonnum);
MSG_WriteByte (&net_message, 3);
CL_WriteDemoMessage();
CL_Record_Serverdata();
CL_Record_Prespawn();
CL_Record_Spawn();
// restore net_message
net_message.data = data;

View File

@ -54,7 +54,7 @@ state bit 2 is edge triggered on the down to up transition
kbutton_t in_mlook, in_klook;
kbutton_t in_left, in_right, in_forward, in_back;
kbutton_t in_lookup, in_lookdown, in_moveleft, in_moveright;
kbutton_t in_strafe, in_speed, in_use, in_jump, in_attack;
kbutton_t in_strafe, in_speed, in_jump, in_attack, in_button3, in_button4, in_button5, in_button6, in_button7, in_button8;
kbutton_t in_up, in_down;
int in_impulse;
@ -156,11 +156,24 @@ void IN_StrafeUp(void) {KeyUp(&in_strafe);}
void IN_AttackDown(void) {KeyDown(&in_attack);}
void IN_AttackUp(void) {KeyUp(&in_attack);}
void IN_UseDown (void) {KeyDown(&in_use);}
void IN_UseUp (void) {KeyUp(&in_use);}
void IN_UseDown (void) {KeyDown(&in_button3);}
void IN_UseUp (void) {KeyUp(&in_button3);}
void IN_JumpDown (void) {KeyDown(&in_jump);}
void IN_JumpUp (void) {KeyUp(&in_jump);}
void IN_Button3Down(void) {KeyDown(&in_button3);}
void IN_Button3Up(void) {KeyUp(&in_button3);}
void IN_Button4Down(void) {KeyDown(&in_button4);}
void IN_Button4Up(void) {KeyUp(&in_button4);}
void IN_Button5Down(void) {KeyDown(&in_button5);}
void IN_Button5Up(void) {KeyUp(&in_button5);}
void IN_Button6Down(void) {KeyDown(&in_button6);}
void IN_Button6Up(void) {KeyUp(&in_button6);}
void IN_Button7Down(void) {KeyDown(&in_button7);}
void IN_Button7Up(void) {KeyUp(&in_button7);}
void IN_Button8Down(void) {KeyDown(&in_button8);}
void IN_Button8Up(void) {KeyUp(&in_button8);}
void IN_Impulse (void) {in_impulse=Q_atoi(Cmd_Argv(1));}
/*
@ -339,15 +352,25 @@ CL_SendMove
*/
void CL_SendMove (const usercmd_t *cmd)
{
int i;
unsigned int i;
int bits;
sizebuf_t buf;
byte data[128];
byte data[1024];
buf.maxsize = 128;
buf.maxsize = sizeof(data);
buf.cursize = 0;
buf.data = data;
for (i = 0; i < cl.ackframes_count; i++)
{
MSG_WriteByte(&buf, clcdp_ackframe);
MSG_WriteLong(&buf, cl.ackframes[i]);
}
cl.ackframes_count = 0;
if (cmd)
{
int dump = buf.cursize;
cl.cmd = *cmd;
//
@ -355,11 +378,23 @@ void CL_SendMove (const usercmd_t *cmd)
//
MSG_WriteByte (&buf, clc_move);
if (cl.protocol == PROTOCOL_VERSION_DP7)
{
if (1)
MSG_WriteLong(&buf, 0);
else
MSG_WriteLong(&buf, cl.movemessages);
}
else if (cl.protocol_pext2 & PEXT2_PREDINFO)
MSG_WriteShort(&buf, cl.movemessages&0xffff); //server will ack this once it has been applied to the player's entity state
MSG_WriteFloat (&buf, cl.mtime[0]); // so server can get ping times
for (i=0 ; i<3 ; i++)
//johnfitz -- 16-bit angles for PROTOCOL_FITZQUAKE
if (cl.protocol == PROTOCOL_NETQUAKE)
//spike -- nq+bjp3 use 8bit angles. all other supported protocols use 16bit ones.
//spike -- proquake servers bump client->server angles up to at least 16bit. this is safe because it only happens when both client+server advertise it, and because it never actually gets recorded into demos anyway.
//spike -- predinfo also always means 16bit angles, even if for some reason the server doesn't advertise proquake (like dp).
if ((cl.protocol == PROTOCOL_NETQUAKE || cl.protocol == PROTOCOL_VERSION_BJP3) && !NET_QSocketGetProQuakeAngleHack(cls.netcon) && !(cl.protocol_pext2 & PEXT2_PREDINFO))
MSG_WriteAngle (&buf, cl.viewangles[i], cl.protocolflags);
else
MSG_WriteAngle16 (&buf, cl.viewangles[i], cl.protocolflags);
@ -382,22 +417,69 @@ void CL_SendMove (const usercmd_t *cmd)
bits |= 2;
in_jump.state &= ~2;
MSG_WriteByte (&buf, bits);
if (in_button3.state & 3)
bits |= 4;
in_button3.state &= ~2;
if (in_button4.state & 3)
bits |= 8;
in_button4.state &= ~2;
if (in_button5.state & 3)
bits |= 16;
in_button5.state &= ~2;
if (in_button6.state & 3)
bits |= 32;
in_button6.state &= ~2;
if (in_button7.state & 3)
bits |= 64;
in_button7.state &= ~2;
if (in_button8.state & 3)
bits |= 128;
in_button8.state &= ~2;
if (cl.protocol == PROTOCOL_VERSION_DP7)
{
MSG_WriteLong (&buf, bits);
MSG_WriteByte (&buf, in_impulse);
MSG_WriteShort(&buf, 32767);//cursor x
MSG_WriteShort(&buf, 32767);//cursor y
MSG_WriteFloat(&buf, r_refdef.vieworg[0]); //start (view pos)
MSG_WriteFloat(&buf, r_refdef.vieworg[1]);
MSG_WriteFloat(&buf, r_refdef.vieworg[2]);
MSG_WriteFloat(&buf, r_refdef.vieworg[0]); //impact
MSG_WriteFloat(&buf, r_refdef.vieworg[1]);
MSG_WriteFloat(&buf, r_refdef.vieworg[2]);
MSG_WriteShort(&buf, 0); //entity
}
else
{
MSG_WriteByte (&buf, bits);
MSG_WriteByte (&buf, in_impulse);
}
in_impulse = 0;
//
// deliver the message
//
if (cls.demoplayback)
return;
//
// allways dump the first two message, because it may contain leftover inputs
// from the last level
//
if (++cl.movemessages <= 2)
buf.cursize = dump;
else
S_Voip_Transmit(clcfte_voicechat, &buf);/*Spike: Add voice data*/
}
else
S_Voip_Transmit(clcfte_voicechat, NULL);/*Spike: Add voice data (with cl_voip_test anyway)*/
//fixme: nops if we're still connecting, or something.
//
// deliver the message
//
if (cls.demoplayback || !buf.cursize)
return;
if (NET_SendUnreliableMessage (cls.netcon, &buf) == -1)
@ -442,6 +524,18 @@ void CL_InitInput (void)
Cmd_AddCommand ("-attack", IN_AttackUp);
Cmd_AddCommand ("+use", IN_UseDown);
Cmd_AddCommand ("-use", IN_UseUp);
Cmd_AddCommand ("+button3", IN_Button3Down);
Cmd_AddCommand ("-button3", IN_Button3Up);
Cmd_AddCommand ("+button4", IN_Button4Down);
Cmd_AddCommand ("-button4", IN_Button4Up);
Cmd_AddCommand ("+button5", IN_Button5Down);
Cmd_AddCommand ("-button5", IN_Button5Up);
Cmd_AddCommand ("+button6", IN_Button6Down);
Cmd_AddCommand ("-button6", IN_Button6Up);
Cmd_AddCommand ("+button7", IN_Button7Down);
Cmd_AddCommand ("-button7", IN_Button7Up);
Cmd_AddCommand ("+button8", IN_Button8Down);
Cmd_AddCommand ("-button8", IN_Button8Up);
Cmd_AddCommand ("+jump", IN_JumpDown);
Cmd_AddCommand ("-jump", IN_JumpUp);
Cmd_AddCommand ("impulse", IN_Impulse);

View File

@ -24,6 +24,12 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "quakedef.h"
#include "bgmusic.h"
#include "arch_def.h"
#ifdef PLATFORM_UNIX
//for unlink
#include <unistd.h>
#endif
// we need to declare some mouse variables here, because the menu system
// references them even when on a unix system.
@ -48,21 +54,39 @@ cvar_t m_side = {"m_side","0.8", CVAR_ARCHIVE};
cvar_t cl_maxpitch = {"cl_maxpitch", "90", CVAR_ARCHIVE}; //johnfitz -- variable pitch clamping
cvar_t cl_minpitch = {"cl_minpitch", "-90", CVAR_ARCHIVE}; //johnfitz -- variable pitch clamping
cvar_t cl_recordingdemo = {"cl_recordingdemo", "", CVAR_ROM}; //the name of the currently-recording demo.
client_static_t cls;
client_state_t cl;
// FIXME: put these on hunk?
entity_t cl_static_entities[MAX_STATIC_ENTITIES];
lightstyle_t cl_lightstyle[MAX_LIGHTSTYLES];
dlight_t cl_dlights[MAX_DLIGHTS];
entity_t *cl_entities; //johnfitz -- was a static array, now on hunk
int cl_max_edicts; //johnfitz -- only changes when new map loads
int cl_numvisedicts;
entity_t *cl_visedicts[MAX_VISEDICTS];
int cl_maxvisedicts;
entity_t **cl_visedicts;
extern cvar_t r_lerpmodels, r_lerpmove; //johnfitz
void CL_ClearTrailStates(void)
{
int i;
for (i = 0; i < cl.num_statics; i++)
{
PScript_DelinkTrailstate(&(cl.static_entities[i]->trailstate));
PScript_DelinkTrailstate(&(cl.static_entities[i]->emitstate));
}
for (i = 0; i < cl.max_edicts; i++)
{
PScript_DelinkTrailstate(&(cl.entities[i].trailstate));
PScript_DelinkTrailstate(&(cl.entities[i].emitstate));
}
for (i = 0; i < MAX_BEAMS; i++)
{
PScript_DelinkTrailstate(&(cl_beams[i].trailstate));
}
}
/*
=====================
CL_ClearState
@ -74,6 +98,8 @@ void CL_ClearState (void)
if (!sv.active)
Host_ClearMemory ();
CL_ClearTrailStates();
// wipe the entire cl structure
memset (&cl, 0, sizeof(cl));
@ -86,9 +112,14 @@ void CL_ClearState (void)
memset (cl_beams, 0, sizeof(cl_beams));
//johnfitz -- cl_entities is now dynamically allocated
cl_max_edicts = CLAMP (MIN_EDICTS,(int)max_edicts.value,MAX_EDICTS);
cl_entities = (entity_t *) Hunk_AllocName (cl_max_edicts*sizeof(entity_t), "cl_entities");
cl.max_edicts = CLAMP (MIN_EDICTS,(int)max_edicts.value,MAX_EDICTS);
cl.entities = (entity_t *) Hunk_AllocName (cl.max_edicts*sizeof(entity_t), "cl_entities");
//johnfitz
cl.viewent.netstate = nullentitystate;
#ifdef PSET_SCRIPT
PScript_Shutdown();
#endif
}
/*
@ -132,7 +163,12 @@ void CL_Disconnect (void)
cls.demoplayback = cls.timedemo = false;
cls.demopaused = false;
cls.signon = 0;
if (cls.download.file)
fclose(cls.download.file);
memset(&cls.download, 0, sizeof(cls.download));
cl.intermission = 0;
cl.worldmodel = NULL;
cl.sendprespawn = false;
}
void CL_Disconnect_f (void)
@ -152,11 +188,20 @@ Host should be either "local" or a net address to be passed on
*/
void CL_EstablishConnection (const char *host)
{
static char lasthost[NET_NAMELEN];
if (cls.state == ca_dedicated)
return;
if (cls.demoplayback)
return;
if (!host)
{
host = lasthost;
if (!*host)
return;
}
else
q_strlcpy(lasthost, host, sizeof(lasthost));
CL_Disconnect ();
@ -188,12 +233,12 @@ void CL_SignonReply (void)
{
case 1:
MSG_WriteByte (&cls.message, clc_stringcmd);
MSG_WriteString (&cls.message, "prespawn");
MSG_WriteString (&cls.message, va("name \"%s\"\n", cl_name.string));
cl.sendprespawn = true;
break;
case 2:
MSG_WriteByte (&cls.message, clc_stringcmd);
MSG_WriteString (&cls.message, va("name \"%s\"\n", cl_name.string));
MSG_WriteByte (&cls.message, clc_stringcmd);
MSG_WriteString (&cls.message, va("color %i %i\n", ((int)cl_color.value)>>4, ((int)cl_color.value)&15));
@ -261,7 +306,7 @@ void CL_PrintEntities_f (void)
if (cls.state != ca_connected)
return;
for (i=0,ent=cl_entities ; i<cl.num_entities ; i++,ent++)
for (i=0,ent=cl.entities ; i<cl.num_entities ; i++,ent++)
{
Con_Printf ("%3i:",i);
if (!ent->model)
@ -398,6 +443,120 @@ float CL_LerpPoint (void)
return frac;
}
static qboolean CL_LerpEntity(entity_t *ent, vec3_t org, vec3_t ang, float frac)
{
float f, d;
int j;
vec3_t delta;
qboolean teleported = false;
//figure out the pos+angles of the parent
if (ent->forcelink)
{ // the entity was not updated in the last message
// so move to the final spot
VectorCopy (ent->msg_origins[0], org);
VectorCopy (ent->msg_angles[0], ang);
}
else
{ // if the delta is large, assume a teleport and don't lerp
f = frac;
for (j=0 ; j<3 ; j++)
{
delta[j] = ent->msg_origins[0][j] - ent->msg_origins[1][j];
if (delta[j] > 100 || delta[j] < -100)
{
f = 1; // assume a teleportation, not a motion
teleported = true; //johnfitz -- don't lerp teleports
}
}
//johnfitz -- don't cl_lerp entities that will be r_lerped
if (r_lerpmove.value && (ent->lerpflags & LERP_MOVESTEP))
f = 1;
//johnfitz
// interpolate the origin and angles
for (j=0 ; j<3 ; j++)
{
org[j] = ent->msg_origins[1][j] + f*delta[j];
d = ent->msg_angles[0][j] - ent->msg_angles[1][j];
if (d > 180)
d -= 360;
else if (d < -180)
d += 360;
ang[j] = ent->msg_angles[1][j] + f*d;
}
}
return teleported;
}
static qboolean CL_AttachEntity(entity_t *ent, float frac)
{
entity_t *parent;
vec3_t porg, pang;
vec3_t paxis[3];
vec3_t tmp, fwd, up;
unsigned int tagent = ent->netstate.tagentity;
int runaway = 0;
while(1)
{
if (!tagent)
return true; //nothing to do.
if (runaway++==10 || tagent >= (unsigned int)cl.num_entities)
return false; //parent isn't valid
parent = &cl.entities[tagent];
if (!parent->model)
return false;
if (0)//tagent < ent-cl_entities)
{
tagent = parent->netstate.tagentity;
VectorCopy(parent->origin, porg);
VectorCopy(parent->angles, pang);
}
else
{
tagent = parent->netstate.tagentity;
CL_LerpEntity(parent, porg, pang, frac);
}
//FIXME: this code needs to know the exact lerp info of the underlaying model.
//however for some idiotic reason, someone decided to figure out what should be displayed somewhere far removed from the code that deals with timing
//so we have absolutely no way to get a reliable origin
//in the meantime, r_lerpmove 0; r_lerpmodels 0
//you might be able to work around it by setting the attached entity to movetype_step to match the attachee, and to avoid EF_MUZZLEFLASH.
//personally I'm just going to call it a quakespasm bug that I cba to fix.
//FIXME: update porg+pang according to the tag index (we don't support md3s/iqms, so we don't need to do anything here yet)
if (parent->model && parent->model->type == mod_alias)
pang[0] *= -1;
AngleVectors(pang, paxis[0], paxis[1], paxis[2]);
if (ent->model && ent->model->type == mod_alias)
ent->angles[0] *= -1;
AngleVectors(ent->angles, fwd, tmp, up);
//transform the origin
VectorMA(parent->origin, ent->origin[0], paxis[0], tmp);
VectorMA(tmp, -ent->origin[1], paxis[1], tmp);
VectorMA(tmp, ent->origin[2], paxis[2], ent->origin);
//transform the forward vector
VectorMA(vec3_origin, fwd[0], paxis[0], tmp);
VectorMA(tmp, -fwd[1], paxis[1], tmp);
VectorMA(tmp, fwd[2], paxis[2], fwd);
//transform the up vector
VectorMA(vec3_origin, up[0], paxis[0], tmp);
VectorMA(tmp, -up[1], paxis[1], tmp);
VectorMA(tmp, up[2], paxis[2], up);
//regenerate the new angles.
VectorAngles(fwd, up, ent->angles);
if (ent->model && ent->model->type == mod_alias)
ent->angles[0] *= -1;
}
}
/*
===============
CL_RelinkEntities
@ -407,15 +566,27 @@ void CL_RelinkEntities (void)
{
entity_t *ent;
int i, j;
float frac, f, d;
vec3_t delta;
float frac, d;
float bobjrotate;
vec3_t oldorg;
dlight_t *dl;
float frametime;
int modelflags;
// determine partial update time
frac = CL_LerpPoint ();
frametime = cl.time - cl.oldtime;
if (frametime < 0)
frametime = 0;
if (frametime > 0.1)
frametime = 0.1;
if (cl_numvisedicts + 64 > cl_maxvisedicts)
{
cl_maxvisedicts = cl_maxvisedicts+64;
cl_visedicts = realloc(cl_visedicts, sizeof(*cl_visedicts)*cl_maxvisedicts);
}
cl_numvisedicts = 0;
//
@ -442,10 +613,10 @@ void CL_RelinkEntities (void)
bobjrotate = anglemod(100*cl.time);
// start on the entity after the world
for (i=1,ent=cl_entities+1 ; i<cl.num_entities ; i++,ent++)
for (i=1,ent=cl.entities+1 ; i<cl.num_entities ; i++,ent++)
{
if (!ent->model)
{ // empty slot
{ // empty slot, ish.
// ericw -- efrags are only used for static entities in GLQuake
// ent can't be static, so this is a no-op.
@ -464,46 +635,22 @@ void CL_RelinkEntities (void)
VectorCopy (ent->origin, oldorg);
if (ent->forcelink)
{ // the entity was not updated in the last message
// so move to the final spot
VectorCopy (ent->msg_origins[0], ent->origin);
VectorCopy (ent->msg_angles[0], ent->angles);
}
else
{ // if the delta is large, assume a teleport and don't lerp
f = frac;
for (j=0 ; j<3 ; j++)
if (CL_LerpEntity(ent, ent->origin, ent->angles, frac))
ent->lerpflags |= LERP_RESETMOVE;
if (ent->netstate.tagentity)
if (!CL_AttachEntity(ent, frac))
{
delta[j] = ent->msg_origins[0][j] - ent->msg_origins[1][j];
if (delta[j] > 100 || delta[j] < -100)
{
f = 1; // assume a teleportation, not a motion
ent->lerpflags |= LERP_RESETMOVE; //johnfitz -- don't lerp teleports
}
//can't draw it if we don't know where its parent is.
continue;
}
//johnfitz -- don't cl_lerp entities that will be r_lerped
if (r_lerpmove.value && (ent->lerpflags & LERP_MOVESTEP))
f = 1;
//johnfitz
// interpolate the origin and angles
for (j=0 ; j<3 ; j++)
{
ent->origin[j] = ent->msg_origins[1][j] + f*delta[j];
d = ent->msg_angles[0][j] - ent->msg_angles[1][j];
if (d > 180)
d -= 360;
else if (d < -180)
d += 360;
ent->angles[j] = ent->msg_angles[1][j] + f*d;
}
}
modelflags = (ent->effects>>24)&0xff;
if (!(ent->effects & EF_NOMODELFLAGS))
modelflags |= ent->model->flags;
// rotate binary objects locally
if (ent->model->flags & EF_ROTATE)
if (modelflags & EF_ROTATE)
ent->angles[1] = bobjrotate;
if (ent->effects & EF_BRIGHTFIELD)
@ -526,7 +673,7 @@ void CL_RelinkEntities (void)
//johnfitz -- assume muzzle flash accompanied by muzzle flare, which looks bad when lerped
if (r_lerpmodels.value != 2)
{
if (ent == &cl_entities[cl.viewentity])
if (ent == &cl.entities[cl.viewentity])
cl.viewent.lerpflags |= LERP_RESETANIM|LERP_RESETANIM2; //no lerping for two frames
else
ent->lerpflags |= LERP_RESETANIM|LERP_RESETANIM2; //no lerping for two frames
@ -549,33 +696,95 @@ void CL_RelinkEntities (void)
dl->die = cl.time + 0.001;
}
if (ent->model->flags & EF_GIB)
R_RocketTrail (oldorg, ent->origin, 2);
else if (ent->model->flags & EF_ZOMGIB)
R_RocketTrail (oldorg, ent->origin, 4);
else if (ent->model->flags & EF_TRACER)
R_RocketTrail (oldorg, ent->origin, 3);
else if (ent->model->flags & EF_TRACER2)
R_RocketTrail (oldorg, ent->origin, 5);
else if (ent->model->flags & EF_ROCKET)
#ifdef PSET_SCRIPT
if (cl.paused)
;
else if (ent->netstate.traileffectnum > 0 && ent->netstate.traileffectnum < MAX_PARTICLETYPES)
{
vec3_t axis[3];
AngleVectors(ent->angles, axis[0], axis[1], axis[2]);
PScript_ParticleTrail(oldorg, ent->origin, cl.particle_precache[ent->netstate.traileffectnum].index, i, axis, &ent->trailstate);
}
else if (ent->model->traileffect >= 0)
{
vec3_t axis[3];
AngleVectors(ent->angles, axis[0], axis[1], axis[2]);
PScript_ParticleTrail(oldorg, ent->origin, ent->model->traileffect, i, axis, &ent->trailstate);
}
else
#endif
if (modelflags & EF_GIB)
{
if (PScript_EntParticleTrail(oldorg, ent, "TR_BLOOD"))
R_RocketTrail (oldorg, ent->origin, 2);
}
else if (modelflags & EF_ZOMGIB)
{
if (PScript_EntParticleTrail(oldorg, ent, "TR_SLIGHTBLOOD"))
R_RocketTrail (oldorg, ent->origin, 4);
}
else if (modelflags & EF_TRACER)
{
if (PScript_EntParticleTrail(oldorg, ent, "TR_WIZSPIKE"))
R_RocketTrail (oldorg, ent->origin, 3);
}
else if (modelflags & EF_TRACER2)
{
if (PScript_EntParticleTrail(oldorg, ent, "TR_KNIGHTSPIKE"))
R_RocketTrail (oldorg, ent->origin, 5);
}
else if (modelflags & EF_ROCKET)
{
if (PScript_EntParticleTrail(oldorg, ent, "TR_ROCKET"))
R_RocketTrail (oldorg, ent->origin, 0);
dl = CL_AllocDlight (i);
VectorCopy (ent->origin, dl->origin);
dl->radius = 200;
dl->die = cl.time + 0.01;
}
else if (ent->model->flags & EF_GRENADE)
else if (modelflags & EF_GRENADE)
{
if (PScript_EntParticleTrail(oldorg, ent, "TR_GRENADE"))
R_RocketTrail (oldorg, ent->origin, 1);
else if (ent->model->flags & EF_TRACER3)
}
else if (modelflags & EF_TRACER3)
{
if (PScript_EntParticleTrail(oldorg, ent, "TR_VORESPIKE"))
R_RocketTrail (oldorg, ent->origin, 6);
}
ent->forcelink = false;
#ifdef PSET_SCRIPT
if (ent->netstate.emiteffectnum > 0)
{
vec3_t axis[3];
AngleVectors(ent->angles, axis[0], axis[1], axis[2]);
if (ent->model->type == mod_alias)
axis[0][2] *= -1; //stupid vanilla bug
PScript_RunParticleEffectState(ent->origin, axis[0], frametime, cl.particle_precache[ent->netstate.emiteffectnum].index, &ent->emitstate);
}
else if (ent->model->emiteffect >= 0)
{
vec3_t axis[3];
AngleVectors(ent->angles, axis[0], axis[1], axis[2]);
if (ent->model->flags & MOD_EMITFORWARDS)
{
if (ent->model->type == mod_alias)
axis[0][2] *= -1; //stupid vanilla bug
}
else
VectorScale(axis[2], -1, axis[0]);
PScript_RunParticleEffectState(ent->origin, axis[0], frametime, ent->model->emiteffect, &ent->emitstate);
if (ent->model->flags & MOD_EMITREPLACE)
continue;
}
#endif
if (i == cl.viewentity && !chase_active.value)
continue;
if (cl_numvisedicts < MAX_VISEDICTS)
if (cl_numvisedicts < cl_maxvisedicts)
{
cl_visedicts[cl_numvisedicts] = ent;
cl_numvisedicts++;
@ -583,6 +792,248 @@ void CL_RelinkEntities (void)
}
}
#ifdef PSET_SCRIPT
int CL_GenerateRandomParticlePrecache(const char *pname)
{ //for dpp7 compat
size_t i;
pname = va("%s", pname);
for (i = 1; i < MAX_PARTICLETYPES; i++)
{
if (!cl.particle_precache[i].name)
{
cl.particle_precache[i].name = strcpy(Hunk_Alloc(strlen(pname)+1), pname);
cl.particle_precache[i].index = PScript_FindParticleType(cl.particle_precache[i].name);
return i;
}
if (!strcmp(cl.particle_precache[i].name, pname))
return i;
}
return 0;
}
#endif
//sent by the server to let us know that dp downloads can be used
void CL_ServerExtension_Download_f(void)
{
if (Cmd_Argc() == 2)
cl.protocol_dpdownload = atoi(Cmd_Argv(1));
}
//sent by the server to let us know when its finished sending the entire file
void CL_Download_Finished_f(void)
{
if (cls.download.file)
{
char finalpath[MAX_OSPATH];
unsigned int size = strtoul(Cmd_Argv(1), NULL, 0);
unsigned int hash = strtoul(Cmd_Argv(2), NULL, 0);
//const char *fname = Cmd_Argv(3);
qboolean hashokay = false;
if (size == cls.download.size)
{
byte *tmp = malloc(size);
if (tmp)
{
fseek(cls.download.file, 0, SEEK_SET);
fread(tmp, 1, size, cls.download.file);
hashokay = (hash == CRC_Block(tmp, size));
free(tmp);
if (!hashokay) Con_Warning("Download hash failure\n");
}
else Con_Warning("Download size too large\n");
}
else Con_Warning("Download size mismatch\n");
fclose(cls.download.file);
cls.download.file = NULL;
if (hashokay)
{
q_snprintf (finalpath, sizeof(finalpath), "%s/%s", com_gamedir, cls.download.current);
rename(cls.download.temp, finalpath);
Con_SafePrintf("Downloaded %s: %u bytes\n", cls.download.current, cls.download.size);
}
else
{
Con_Warning("Download of %s failed\n", cls.download.current);
unlink(cls.download.temp); //kill the temp
}
}
cls.download.active = false;
}
//sent by the server (or issued by the user) to stop the current download for any reason.
void CL_StopDownload_f(void)
{
if (cls.download.file)
{
fclose(cls.download.file);
cls.download.file = NULL;
unlink(cls.download.temp);
// Con_SafePrintf("Download cancelled\n", cl.download_current, cl.download_size);
}
cls.download.active = false;
}
//sent by the server to let us know that its going to start spamming us now.
void CL_Download_Begin_f(void)
{
if (!cls.download.active)
return;
if (cls.download.file)
CL_StopDownload_f();
//cl_downloadbegin size "name"
cls.download.size = strtoul(Cmd_Argv(1), NULL, 0);
COM_CreatePath(cls.download.temp);
cls.download.file = fopen(cls.download.temp, "wb+"); //+ so we can read the data back to validate it
MSG_WriteByte (&cls.message, clc_stringcmd);
MSG_WriteString (&cls.message, "sv_startdownload\n");
}
void CL_Download_Data(void)
{
byte *data;
unsigned int start, size;
start = MSG_ReadLong();
size = (unsigned short)MSG_ReadShort();
data = MSG_ReadData(size);
if (msg_badread)
return;
if (!cls.download.file)
return; //demo started mid-record? something weird anyway
fseek(cls.download.file, start, SEEK_SET);
fwrite(data, 1, size, cls.download.file);
Con_SafePrintf("Downloading %s: %g%%\r", cls.download.current, 100*(start+size) / (double)cls.download.size);
//should maybe use unreliables, but whatever, shouldn't matter too much, it'll still complete
MSG_WriteByte(&cls.message, clcdp_ackdownloaddata);
MSG_WriteLong(&cls.message, start);
MSG_WriteShort(&cls.message, size);
}
//returns true if we should block waiting for a download, false if there's no point.
qboolean CL_CheckDownload(const char *filename)
{
if (sv.active)
return false; //no point downloading if we're the server...
if (*filename == '*')
return false; //don't download these...
if (cls.download.active)
return true; //block while we're already downloading something
if (!cl.protocol_dpdownload)
return false; //can't download anyway
if (*cls.download.current && !strcmp(cls.download.current, filename))
return false; //if the previous download failed, don't endlessly retry.
if (COM_FileExists(filename, NULL))
return false; //no need to download anything.
if (!COM_DownloadNameOkay(filename))
return false; //diediedie
cls.download.active = true;
q_strlcpy(cls.download.current, filename, sizeof(cls.download.current));
q_snprintf (cls.download.temp, sizeof(cls.download.temp), "%s/%s.tmp", com_gamedir, filename);
Con_Printf("Downloading %s...\r", filename);
MSG_WriteByte (&cls.message, clc_stringcmd);
MSG_WriteString (&cls.message, va("download \"%s\"\n", filename));
return true;
}
//download+load models and sounds as needed, once complete let the server know we're ready for the next stage.
//returning false will trigger nops.
qboolean CL_CheckDownloads(void)
{
int i;
if (cl.model_download == 0 && cl.model_count && cl.model_name[1])
{ //haxors, download the lit first, but only if we don't already have the bsp
//this ensures that we don't keep requesting the lit for maps that just don't have one (although may be problematic if the first server we find deleted them all, but oh well)
char litname[MAX_QPATH];
char *ext;
q_strlcpy(litname, cl.model_name[1], sizeof(litname));
ext = (char*)COM_FileGetExtension(litname);
if (!q_strcasecmp(ext, "bsp"))
{
if (!COM_FileExists(litname, NULL))
{
strcpy(ext, "lit");
if (CL_CheckDownload(litname))
return false;
}
}
cl.model_download++;
}
for (; cl.model_download < cl.model_count; )
{
if (*cl.model_name[cl.model_download])
{
if (CL_CheckDownload(cl.model_name[cl.model_download]))
return false;
cl.model_precache[cl.model_download] = Mod_ForName (cl.model_name[cl.model_download], false);
if (cl.model_precache[cl.model_download] == NULL)
{
Host_Error ("Model %s not found", cl.model_name[cl.model_download]);
}
}
cl.model_download++;
}
for (; cl.sound_download < cl.sound_count; )
{
if (*cl.sound_name[cl.sound_download])
{
if (CL_CheckDownload(va("sound/%s", cl.sound_name[cl.sound_download])))
return false;
cl.sound_precache[cl.sound_download] = S_PrecacheSound (cl.sound_name[cl.sound_download]);
}
cl.sound_download++;
}
if (!cl.worldmodel && cl.model_count >= 2)
{
// local state
cl.entities[0].model = cl.worldmodel = cl.model_precache[1];
if (cl.worldmodel->type != mod_brush)
{
if (cl.worldmodel->type == mod_ext_invalid)
Host_Error ("Worldmodel %s was not loaded", cl.model_name[1]);
else
Host_Error ("Worldmodel %s is not a brushmodel", cl.model_name[1]);
}
//fixme: deal with skybox somehow
R_NewMap ();
#ifdef PSET_SCRIPT
//the protocol changing depending upon files found on the client's computer is of course a really shit way to design things
//especially when users have a nasty habit of changing config files.
if (cl.protocol == PROTOCOL_VERSION_DP7)
{
PScript_FindParticleType("effectinfo."); //make sure this is implicitly loaded.
COM_Effectinfo_Enumerate(CL_GenerateRandomParticlePrecache);
cl.protocol_particles = true;
}
else if (cl.protocol_pext2)
cl.protocol_particles = true; //doesn't have a pext flag of its own, but at least we know what it is.
#endif
}
//make sure ents have the correct models, now that they're actually loaded.
for (i = 0; i < cl.num_statics; i++)
{
if (cl.static_entities[i]->model)
continue;
cl.static_entities[i]->model = cl.model_precache[cl.static_entities[i]->netstate.modelindex];
R_AddEfrags (cl.static_entities[i]);
}
return true;
}
/*
===============
@ -627,7 +1078,7 @@ int CL_ReadFromServer (void)
//visedicts
if (cl_numvisedicts > 256 && dev_peakstats.visedicts <= 256)
Con_DWarning ("%i visedicts exceeds standard limit of 256 (max = %d).\n", cl_numvisedicts, MAX_VISEDICTS);
Con_DWarning ("%i visedicts exceeds standard limit of 256.\n", cl_numvisedicts);
dev_stats.visedicts = cl_numvisedicts;
dev_peakstats.visedicts = q_max(cl_numvisedicts, dev_peakstats.visedicts);
@ -686,6 +1137,8 @@ void CL_SendCmd (void)
// send the unreliable message
CL_SendMove (&cmd);
}
else
CL_SendMove (NULL);
if (cls.demoplayback)
{
@ -755,15 +1208,30 @@ void CL_Viewpos_f (void)
#else
//player position
Con_Printf ("Viewpos: (%i %i %i) %i %i %i\n",
(int)cl_entities[cl.viewentity].origin[0],
(int)cl_entities[cl.viewentity].origin[1],
(int)cl_entities[cl.viewentity].origin[2],
(int)cl.entities[cl.viewentity].origin[0],
(int)cl.entities[cl.viewentity].origin[1],
(int)cl.entities[cl.viewentity].origin[2],
(int)cl.viewangles[PITCH],
(int)cl.viewangles[YAW],
(int)cl.viewangles[ROLL]);
#endif
}
static void CL_ServerExtension_FullServerinfo_f(void)
{
// const char *newserverinfo = Cmd_Argv(1);
}
static void CL_ServerExtension_ServerinfoUpdate_f(void)
{
// const char *newserverkey = Cmd_Argv(1);
// const char *newservervalue = Cmd_Argv(2);
}
static void CL_ServerExtension_Ignore_f(void)
{
Con_DPrintf2("Ignoring stufftext: %s\n", Cmd_Argv(0));
}
/*
=================
CL_Init
@ -803,6 +1271,7 @@ void CL_Init (void)
Cvar_RegisterVariable (&cl_maxpitch); //johnfitz -- variable pitch clamping
Cvar_RegisterVariable (&cl_minpitch); //johnfitz -- variable pitch clamping
Cvar_RegisterVariable (&cl_recordingdemo); //spike -- for mod hacks. combine with cvar_string or something
Cmd_AddCommand ("entities", CL_PrintEntities_f);
Cmd_AddCommand ("disconnect", CL_Disconnect_f);
@ -813,5 +1282,27 @@ void CL_Init (void)
Cmd_AddCommand ("tracepos", CL_Tracepos_f); //johnfitz
Cmd_AddCommand ("viewpos", CL_Viewpos_f); //johnfitz
//spike -- add stubs to mute various invalid stuffcmds
Cmd_AddCommand_ServerCommand ("fullserverinfo", CL_ServerExtension_FullServerinfo_f); //spike
Cmd_AddCommand_ServerCommand ("svi", CL_ServerExtension_ServerinfoUpdate_f); //spike
Cmd_AddCommand_ServerCommand ("paknames", CL_ServerExtension_Ignore_f); //package names in use by the server (including gamedir+extension)
Cmd_AddCommand_ServerCommand ("paks", CL_ServerExtension_Ignore_f); //provides hashes to go with the paknames list
//Cmd_AddCommand_ServerCommand ("vwep", CL_ServerExtension_Ignore_f); //invalid for nq, provides an alternative list of model precaches for vweps.
//Cmd_AddCommand_ServerCommand ("at", CL_ServerExtension_Ignore_f); //invalid for nq, autotrack info for mvds
Cmd_AddCommand_ServerCommand ("wps", CL_ServerExtension_Ignore_f); //ktx/cspree weapon stats
Cmd_AddCommand_ServerCommand ("it", CL_ServerExtension_Ignore_f); //cspree item timers
Cmd_AddCommand_ServerCommand ("tinfo", CL_ServerExtension_Ignore_f); //ktx team info
Cmd_AddCommand_ServerCommand ("exectrigger", CL_ServerExtension_Ignore_f); //spike
Cmd_AddCommand_ServerCommand ("csqc_progname", CL_ServerExtension_Ignore_f); //spike
Cmd_AddCommand_ServerCommand ("csqc_progsize", CL_ServerExtension_Ignore_f); //spike
Cmd_AddCommand_ServerCommand ("csqc_progcrc", CL_ServerExtension_Ignore_f); //spike
Cmd_AddCommand_ServerCommand ("cl_fullpitch", CL_ServerExtension_Ignore_f); //spike
Cmd_AddCommand_ServerCommand ("pq_fullpitch", CL_ServerExtension_Ignore_f); //spike
Cmd_AddCommand_ServerCommand ("cl_serverextension_download", CL_ServerExtension_Download_f); //spike
Cmd_AddCommand_ServerCommand ("cl_downloadbegin", CL_Download_Begin_f); //spike
Cmd_AddCommand_ServerCommand ("cl_downloadfinished", CL_Download_Finished_f); //spike
Cmd_AddCommand ("stopdownload", CL_StopDownload_f); //spike
}

File diff suppressed because it is too large Load Diff

View File

@ -56,7 +56,7 @@ void CL_InitTEnts (void)
CL_ParseBeam
=================
*/
void CL_ParseBeam (qmodel_t *m)
void CL_ParseBeam (qmodel_t *m, const char *trailname, const char *impactname)
{
int ent;
vec3_t start, end;
@ -73,12 +73,24 @@ void CL_ParseBeam (qmodel_t *m)
end[1] = MSG_ReadCoord (cl.protocolflags);
end[2] = MSG_ReadCoord (cl.protocolflags);
#ifdef PSET_SCRIPT
{
vec3_t normal, extra, impact;
VectorSubtract(end, start, normal);
VectorNormalize(normal);
VectorMA(end, 4, normal, extra); //extend the end-point by four
if (CL_TraceLine(start, extra, impact, normal, NULL)<1)
PScript_RunParticleEffectTypeString(impact, normal, 1, impactname);
}
#endif
// override any beam with the same entity
for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++)
if (b->entity == ent)
{
b->entity = ent;
b->model = m;
b->trailname = trailname;
b->endtime = cl.time + 0.2;
VectorCopy (start, b->start);
VectorCopy (end, b->end);
@ -92,6 +104,7 @@ void CL_ParseBeam (qmodel_t *m)
{
b->entity = ent;
b->model = m;
b->trailname = trailname;
b->endtime = cl.time + 0.2;
VectorCopy (start, b->start);
VectorCopy (end, b->end);
@ -108,6 +121,15 @@ void CL_ParseBeam (qmodel_t *m)
//johnfitz
}
void CL_SpawnSpriteEffect(vec3_t org/*, vec3_t dir, vec3_t orientationup*/, qmodel_t *model, int startframe, int framecount, float framerate/*, float alpha, float scale, float randspin, float gravity, int traileffect, unsigned int renderflags, int skinnum*/)
{
if (startframe < 0)
startframe = framecount = 0;
if (!framecount)
framecount = model->numframes;
Con_DPrintf("CL_SpawnSpriteEffect: not implemented\n");
}
/*
=================
CL_ParseTEnt
@ -128,6 +150,7 @@ void CL_ParseTEnt (void)
pos[0] = MSG_ReadCoord (cl.protocolflags);
pos[1] = MSG_ReadCoord (cl.protocolflags);
pos[2] = MSG_ReadCoord (cl.protocolflags);
if (PScript_RunParticleEffectTypeString(pos, NULL, 1, "TE_WIZSPIKE"))
R_RunParticleEffect (pos, vec3_origin, 20, 30);
S_StartSound (-1, 0, cl_sfx_wizhit, pos, 1, 1);
break;
@ -136,14 +159,17 @@ void CL_ParseTEnt (void)
pos[0] = MSG_ReadCoord (cl.protocolflags);
pos[1] = MSG_ReadCoord (cl.protocolflags);
pos[2] = MSG_ReadCoord (cl.protocolflags);
if (PScript_RunParticleEffectTypeString(pos, NULL, 1, "TE_KNIGHTSPIKE"))
R_RunParticleEffect (pos, vec3_origin, 226, 20);
S_StartSound (-1, 0, cl_sfx_knighthit, pos, 1, 1);
break;
case TEDP_SPIKEQUAD:
case TE_SPIKE: // spike hitting wall
pos[0] = MSG_ReadCoord (cl.protocolflags);
pos[1] = MSG_ReadCoord (cl.protocolflags);
pos[2] = MSG_ReadCoord (cl.protocolflags);
if (PScript_RunParticleEffectTypeString(pos, NULL, 1, (type==TEDP_SPIKEQUAD)?"TE_SPIKEQUAD":"TE_SPIKE"))
R_RunParticleEffect (pos, vec3_origin, 0, 10);
if ( rand() % 5 )
S_StartSound (-1, 0, cl_sfx_tink1, pos, 1, 1);
@ -158,10 +184,12 @@ void CL_ParseTEnt (void)
S_StartSound (-1, 0, cl_sfx_ric3, pos, 1, 1);
}
break;
case TEDP_SUPERSPIKEQUAD:
case TE_SUPERSPIKE: // super spike hitting wall
pos[0] = MSG_ReadCoord (cl.protocolflags);
pos[1] = MSG_ReadCoord (cl.protocolflags);
pos[2] = MSG_ReadCoord (cl.protocolflags);
if (PScript_RunParticleEffectTypeString(pos, NULL, 1, (type==TEDP_SUPERSPIKEQUAD)?"TE_SUPERSPIKEQUAD":"TE_SUPERSPIKE"))
R_RunParticleEffect (pos, vec3_origin, 0, 20);
if ( rand() % 5 )
@ -178,50 +206,73 @@ void CL_ParseTEnt (void)
}
break;
case TEDP_GUNSHOTQUAD:
case TEFTE_GUNSHOT_COUNT: //for compat with qw mods
case TE_GUNSHOT: // bullet hitting wall
rnd = 20;
if (type == TEFTE_GUNSHOT_COUNT)
rnd *= MSG_ReadByte();
pos[0] = MSG_ReadCoord (cl.protocolflags);
pos[1] = MSG_ReadCoord (cl.protocolflags);
pos[2] = MSG_ReadCoord (cl.protocolflags);
R_RunParticleEffect (pos, vec3_origin, 0, 20);
if (PScript_RunParticleEffectTypeString(pos, NULL, rnd, (type==TEDP_GUNSHOTQUAD)?"TE_GUNSHOTQUAD":"TE_GUNSHOT"))
R_RunParticleEffect (pos, vec3_origin, 0, rnd);
break;
case TEFTE_EXPLOSION_SPRITE://for compat with qw mods
case TEDP_EXPLOSIONQUAD:
case TENEH_EXPLOSION3:
case TE_EXPLOSION: // rocket explosion
pos[0] = MSG_ReadCoord (cl.protocolflags);
pos[1] = MSG_ReadCoord (cl.protocolflags);
pos[2] = MSG_ReadCoord (cl.protocolflags);
if (PScript_RunParticleEffectTypeString(pos, NULL, 1, (type==TEDP_EXPLOSIONQUAD)?"TE_EXPLOSIONQUAD":"TE_EXPLOSION"))
R_ParticleExplosion (pos);
dl = CL_AllocDlight (0);
VectorCopy (pos, dl->origin);
dl->radius = 350;
dl->die = cl.time + 0.5;
dl->decay = 300;
if (type == TENEH_EXPLOSION3)
{ //the *2 is to match dp's expectations, for some reason.
dl->color[0] = MSG_ReadCoord(cl.protocolflags)*2.0;
dl->color[1] = MSG_ReadCoord(cl.protocolflags)*2.0;
dl->color[2] = MSG_ReadCoord(cl.protocolflags)*2.0;
}
S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1);
if (type==TEFTE_EXPLOSION_SPRITE)
{
qmodel_t *mod = Mod_ForName ("progs/s_explod.spr", false);
CL_SpawnSpriteEffect(pos/*, NULL, NULL*/, mod, 0, 0, 10/*, mod->type==mod_sprite?-1:1, 1, 0, 0, P_INVALID, 0, 0*/);
}
break;
case TE_TAREXPLOSION: // tarbaby explosion
pos[0] = MSG_ReadCoord (cl.protocolflags);
pos[1] = MSG_ReadCoord (cl.protocolflags);
pos[2] = MSG_ReadCoord (cl.protocolflags);
if (PScript_RunParticleEffectTypeString(pos, NULL, 1, "TE_TAREXPLOSION"))
R_BlobExplosion (pos);
S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1);
break;
case TE_LIGHTNING1: // lightning bolts
CL_ParseBeam (Mod_ForName("progs/bolt.mdl", true));
CL_ParseBeam (Mod_ForName("progs/bolt.mdl", true), "TE_LIGHTNING1", "TE_LIGHTNING1_END");
break;
case TE_LIGHTNING2: // lightning bolts
CL_ParseBeam (Mod_ForName("progs/bolt2.mdl", true));
CL_ParseBeam (Mod_ForName("progs/bolt2.mdl", true), "TE_LIGHTNING2", "TE_LIGHTNING2_END");
break;
case TE_LIGHTNING3: // lightning bolts
CL_ParseBeam (Mod_ForName("progs/bolt3.mdl", true));
CL_ParseBeam (Mod_ForName("progs/bolt3.mdl", true), "TE_LIGHTNING3", "TE_LIGHTNING3_END");
break;
// PGM 01/21/97
case TE_BEAM: // grappling hook beam
CL_ParseBeam (Mod_ForName("progs/beam.mdl", true));
CL_ParseBeam (Mod_ForName("progs/beam.mdl", true), "TE_BEAM", "TE_BEAM_END");
break;
// PGM 01/21/97
@ -229,6 +280,7 @@ void CL_ParseTEnt (void)
pos[0] = MSG_ReadCoord (cl.protocolflags);
pos[1] = MSG_ReadCoord (cl.protocolflags);
pos[2] = MSG_ReadCoord (cl.protocolflags);
if (PScript_RunParticleEffectTypeString(pos, NULL, 1, "TE_LAVASPLASH"))
R_LavaSplash (pos);
break;
@ -236,6 +288,7 @@ void CL_ParseTEnt (void)
pos[0] = MSG_ReadCoord (cl.protocolflags);
pos[1] = MSG_ReadCoord (cl.protocolflags);
pos[2] = MSG_ReadCoord (cl.protocolflags);
if (PScript_RunParticleEffectTypeString(pos, NULL, 1, "TE_TELEPORT"))
R_TeleportSplash (pos);
break;
@ -245,6 +298,7 @@ void CL_ParseTEnt (void)
pos[2] = MSG_ReadCoord (cl.protocolflags);
colorStart = MSG_ReadByte ();
colorLength = MSG_ReadByte ();
if (PScript_RunParticleEffectTypeString(pos, NULL, 1, va("TE_EXPLOSION2_%i_%i", colorStart, colorLength)))
R_ParticleExplosion2 (pos, colorStart, colorLength);
dl = CL_AllocDlight (0);
VectorCopy (pos, dl->origin);
@ -254,11 +308,196 @@ void CL_ParseTEnt (void)
S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1);
break;
case TENEH_LIGHTNING4:
{
const char *beamname = MSG_ReadString();
CL_ParseBeam (Mod_ForName(beamname, true), "TE_LIGHTNING4", "TE_LIGHTNING4_END");
}
break;
case TEDP_CUSTOMFLASH:
dl = CL_AllocDlight (0);
dl->origin[0] = MSG_ReadCoord(cl.protocolflags);
dl->origin[1] = MSG_ReadCoord(cl.protocolflags);
dl->origin[2] = MSG_ReadCoord(cl.protocolflags);
dl->radius = 8*MSG_ReadByte();
dl->die = (MSG_ReadByte()+1)*(1/256.0);
dl->decay = dl->radius / dl->die;
dl->die += cl.time;
dl->color[0] = MSG_ReadByte()*(1/127.0);
dl->color[1] = MSG_ReadByte()*(1/127.0);
dl->color[2] = MSG_ReadByte()*(1/127.0);
break;
case TEDP_PARTICLERAIN:
case TEDP_PARTICLESNOW:
{
vec3_t dir, pos2;
int cnt, colour;
//min
pos[0] = MSG_ReadCoord(cl.protocolflags);
pos[1] = MSG_ReadCoord(cl.protocolflags);
pos[2] = MSG_ReadCoord(cl.protocolflags);
//max
pos2[0] = MSG_ReadCoord(cl.protocolflags);
pos2[1] = MSG_ReadCoord(cl.protocolflags);
pos2[2] = MSG_ReadCoord(cl.protocolflags);
//dir
dir[0] = MSG_ReadCoord(cl.protocolflags);
dir[1] = MSG_ReadCoord(cl.protocolflags);
dir[2] = MSG_ReadCoord(cl.protocolflags);
cnt = (unsigned short)MSG_ReadShort(); //count
colour = MSG_ReadByte (); //colour
PScript_RunParticleWeather(pos, pos2, dir, cnt, colour, ((type==TEDP_PARTICLESNOW)?"snow":"rain"));
}
break;
case TEDP_BLOOD:
{
vec3_t dir;
int cnt;
pos[0] = MSG_ReadCoord(cl.protocolflags);
pos[1] = MSG_ReadCoord(cl.protocolflags);
pos[2] = MSG_ReadCoord(cl.protocolflags);
dir[0] = MSG_ReadChar();
dir[1] = MSG_ReadChar();
dir[2] = MSG_ReadChar();
cnt = MSG_ReadByte();
if (PScript_RunParticleEffectTypeString(pos, dir, cnt, "TE_BLOOD"))
Con_Printf ("CL_ParseTEnt: TEDP_BLOOD unavailable\n");
}
break;
case TEDP_SPARK:
{
vec3_t dir;
int cnt;
pos[0] = MSG_ReadCoord(cl.protocolflags);
pos[1] = MSG_ReadCoord(cl.protocolflags);
pos[2] = MSG_ReadCoord(cl.protocolflags);
dir[0] = MSG_ReadChar();
dir[1] = MSG_ReadChar();
dir[2] = MSG_ReadChar();
cnt = MSG_ReadByte();
if (PScript_RunParticleEffectTypeString(pos, dir, cnt, "TE_SPARK"))
Con_Printf ("CL_ParseTEnt: TEDP_SPARK unavailable\n");
}
break;
case TEDP_SMALLFLASH: // [vector] origin
pos[0] = MSG_ReadCoord(cl.protocolflags);
pos[1] = MSG_ReadCoord(cl.protocolflags);
pos[2] = MSG_ReadCoord(cl.protocolflags);
if (PScript_RunParticleEffectTypeString(pos, NULL, 1, "TE_SMALLFLASH"))
Con_Printf ("CL_ParseTEnt: TEDP_SMALLFLASH unavailable\n");
break;
//spike: these are all kinda useless once the ssqc has access to pointparticles...
//I'm too lazy to bother implementing them properly.
case TEDP_BLOODSHOWER:
/*mins[0] =*/ MSG_ReadCoord(cl.protocolflags);
/*mins[1] =*/ MSG_ReadCoord(cl.protocolflags);
/*mins[2] =*/ MSG_ReadCoord(cl.protocolflags);
/*maxs[0] =*/ MSG_ReadCoord(cl.protocolflags);
/*maxs[1] =*/ MSG_ReadCoord(cl.protocolflags);
/*maxs[2] =*/ MSG_ReadCoord(cl.protocolflags);
/*velspeed =*/ MSG_ReadCoord(cl.protocolflags);
/*count =*/ MSG_ReadShort();
Con_Printf ("CL_ParseTEnt: TEDP_BLOODSHOWER unsupported\n");
break;
case TEDP_EXPLOSIONRGB:
/*pos[0] =*/ MSG_ReadCoord(cl.protocolflags);
/*pos[1] =*/ MSG_ReadCoord(cl.protocolflags);
/*pos[2] =*/ MSG_ReadCoord(cl.protocolflags);
/*col[0] =*/ MSG_ReadByte();
/*col[1] =*/ MSG_ReadByte();
/*col[2] =*/ MSG_ReadByte();
Con_Printf ("CL_ParseTEnt: TEDP_EXPLOSIONRGB unsupported\n");
break;
case TEDP_PARTICLECUBE:
/*mins[0] =*/ MSG_ReadCoord(cl.protocolflags);
/*mins[1] =*/ MSG_ReadCoord(cl.protocolflags);
/*mins[2] =*/ MSG_ReadCoord(cl.protocolflags);
/*maxs[0] =*/ MSG_ReadCoord(cl.protocolflags);
/*maxs[1] =*/ MSG_ReadCoord(cl.protocolflags);
/*maxs[2] =*/ MSG_ReadCoord(cl.protocolflags);
/*dir[0] =*/ MSG_ReadCoord(cl.protocolflags);
/*dir[1] =*/ MSG_ReadCoord(cl.protocolflags);
/*dir[2] =*/ MSG_ReadCoord(cl.protocolflags);
/*count =*/ MSG_ReadShort();
/*pal_start =*/ MSG_ReadByte();
/*pal_rand =*/ MSG_ReadByte();
/*velspeed =*/ MSG_ReadCoord(cl.protocolflags);
Con_Printf ("CL_ParseTEnt: TEDP_PARTICLECUBE unsupported\n");
break;
case TEDP_FLAMEJET:
{
vec3_t dir;
int cnt;
pos[0] = MSG_ReadCoord(cl.protocolflags);
pos[1] = MSG_ReadCoord(cl.protocolflags);
pos[2] = MSG_ReadCoord(cl.protocolflags);
dir[0] = MSG_ReadCoord(cl.protocolflags);
dir[1] = MSG_ReadCoord(cl.protocolflags);
dir[2] = MSG_ReadCoord(cl.protocolflags);
cnt = MSG_ReadByte();
if (PScript_RunParticleEffectTypeString(pos, dir, cnt, "TE_FLAMEJET"))
Con_Printf ("CL_ParseTEnt: TEDP_FLAMEJET unavailable\n");
}
break;
case TEDP_PLASMABURN:
pos[0] = MSG_ReadCoord(cl.protocolflags);
pos[1] = MSG_ReadCoord(cl.protocolflags);
pos[2] = MSG_ReadCoord(cl.protocolflags);
if (PScript_RunParticleEffectTypeString(pos, NULL, 1, "TE_PLASMABURN"))
Con_Printf ("CL_ParseTEnt: TEDP_PLASMABURN unavailable\n");
break;
case TEDP_TEI_G3:
Host_Error ("CL_ParseTEnt: TEDP_TEI_G3 unsupported");
case TEDP_SMOKE:
Host_Error ("CL_ParseTEnt: TEDP_SMOKE unsupported");
case TEDP_TEI_BIGEXPLOSION:
Host_Error ("CL_ParseTEnt: TEDP_TEI_BIGEXPLOSION unsupported");
case TEDP_TEI_PLASMAHIT:
Host_Error ("CL_ParseTEnt: TEDP_TEI_PLASMAHIT unsupported");
default:
Sys_Error ("CL_ParseTEnt: bad type");
Host_Error ("CL_ParseTEnt: unsupported tempentity type %i", type);
}
}
void CL_ParseEffect (qboolean big)
{
vec3_t org;
int modelindex;
int startframe;
int framecount;
int framerate;
qmodel_t *mod;
org[0] = MSG_ReadCoord(cl.protocolflags);
org[1] = MSG_ReadCoord(cl.protocolflags);
org[2] = MSG_ReadCoord(cl.protocolflags);
if (big)
modelindex = MSG_ReadShort();
else
modelindex = MSG_ReadByte();
if (big)
startframe = MSG_ReadShort();
else
startframe = MSG_ReadByte();
framecount = MSG_ReadByte();
framerate = MSG_ReadByte();
mod = cl.model_precache[modelindex];
CL_SpawnSpriteEffect(org/*, NULL, NULL*/, mod, startframe, framecount, framerate/*, mod->type==mod_sprite?-1:1, 1, 0, 0, P_INVALID, 0, 0*/);
}
/*
=================
@ -269,7 +508,7 @@ entity_t *CL_NewTempEntity (void)
{
entity_t *ent;
if (cl_numvisedicts == MAX_VISEDICTS)
if (cl_numvisedicts == cl_maxvisedicts)
return NULL;
if (num_temp_entities == MAX_TEMP_ENTITIES)
return NULL;
@ -279,6 +518,8 @@ entity_t *CL_NewTempEntity (void)
cl_visedicts[cl_numvisedicts] = ent;
cl_numvisedicts++;
ent->netstate.scale = 16;
ent->netstate.colormod[0] = ent->netstate.colormod[1] = ent->netstate.colormod[2] = 32;
ent->colormap = vid.colormap;
return ent;
}
@ -301,6 +542,7 @@ void CL_UpdateTEnts (void)
num_temp_entities = 0;
if (cl.paused)
srand ((int) (cl.time * 1000)); //johnfitz -- freeze beams when paused
// update lightning
@ -310,11 +552,14 @@ void CL_UpdateTEnts (void)
continue;
// if coming from the player, update the start position
if (b->entity == cl.viewentity)
if (b->entity == cl.viewentity && cl.entities)
{
VectorCopy (cl_entities[cl.viewentity].origin, b->start);
VectorCopy (cl.entities[cl.viewentity].origin, b->start);
}
if (!PScript_ParticleTrail(b->start, b->end, PScript_FindParticleType(b->trailname), b->entity, NULL, &b->trailstate))
continue;
// calculate pitch and yaw
VectorSubtract (b->end, b->start, dist);

View File

@ -39,6 +39,7 @@ typedef struct
float entertime;
int frags;
int colors; // two 4 bit fields
int ping;
byte translations[VID_GRADES*256];
} scoreboard_t;
@ -83,6 +84,8 @@ typedef struct
struct qmodel_s *model;
float endtime;
vec3_t start, end;
const char *trailname;
struct trailstate_s *trailstate;
} beam_t;
#define MAX_MAPSTRING 2048
@ -131,6 +134,16 @@ typedef struct
struct qsocket_s *netcon;
sizebuf_t message; // writing buffer to send to server
//downloads don't restart/fail when the server sends random serverinfo packets
struct
{
qboolean active;
unsigned int size;
FILE *file;
char current[MAX_QPATH]; //also prevents us from repeatedly trying to download the same file
char temp[MAX_OSPATH]; //the temp filename for the download, will be renamed to current
float starttime;
} download;
} client_static_t;
extern client_static_t cls;
@ -149,6 +162,7 @@ typedef struct
// information for local display
int stats[MAX_CL_STATS]; // health, etc
float statsf[MAX_CL_STATS];
int items; // inventory bit flags
float item_gettime[32]; // cl.time of aquiring item, for blinking
float faceanimtime; // use anim frame if cl.time < this
@ -171,13 +185,11 @@ typedef struct
vec3_t punchangle; // temporary offset
// pitch drifting vars
float idealpitch;
float pitchvel;
qboolean nodrift;
float driftmove;
double laststop;
float viewheight;
float crouch; // local amount for smoothing stepups
qboolean paused; // send over by server
@ -213,10 +225,18 @@ typedef struct
struct qmodel_s *worldmodel; // cl_entitites[0].model
struct efrag_s *free_efrags;
int num_efrags;
int num_entities; // held in cl_entities array
int num_statics; // held in cl_staticentities array
// int num_entities; // held in cl_entities array
// int num_statics; // held in cl_staticentities array
entity_t viewent; // the gun model
entity_t *entities; //spike -- moved into here
int max_edicts;
int num_entities;
entity_t **static_entities; //spike -- was static
int max_static_entities;
int num_statics;
int cdtrack, looptrack; // cd audio
// frag scoreboard
@ -224,6 +244,43 @@ typedef struct
unsigned protocol; //johnfitz
unsigned protocolflags;
unsigned protocol_pext2; //spike -- flag of fte protocol extensions
qboolean protocol_dpdownload;
#ifdef PSET_SCRIPT
qboolean protocol_particles;
struct
{
const char *name;
int index;
} particle_precache[MAX_PARTICLETYPES];
#endif
int ackframes[8]; //big enough to cover burst
unsigned int ackframes_count;
qboolean requestresend;
char stuffcmdbuf[1024]; //comment-extensions are a thing with certain servers, make sure we can handle them properly without further hacks/breakages. there's also some server->client only console commands that we might as well try to handle a bit better, like reconnect
enum
{
PRINT_NONE,
PRINT_PINGS,
// PRINT_STATUSINFO,
// PRINT_STATUSPLAYER,
// PRINT_STATUSIP,
} printtype;
int printplayer;
float expectingpingtimes;
float printversionresponse;
//spike -- moved this stuff here to deal with downloading content named by the server
qboolean sendprespawn; //download+load content, send the prespawn command once done
int model_count;
int model_download;
char model_name[MAX_MODELS][MAX_QPATH];
int sound_count;
int sound_download;
char sound_name[MAX_SOUNDS][MAX_QPATH];
//spike -- end downloads
} client_state_t;
@ -249,6 +306,7 @@ extern cvar_t cl_alwaysrun; // QuakeSpasm
extern cvar_t cl_autofire;
extern cvar_t cl_recordingdemo;
extern cvar_t cl_shownet;
extern cvar_t cl_nolerp;
@ -266,22 +324,17 @@ extern cvar_t m_side;
#define MAX_TEMP_ENTITIES 256 //johnfitz -- was 64
#define MAX_STATIC_ENTITIES 4096 //ericw -- was 512 //johnfitz -- was 128
#define MAX_VISEDICTS 4096 // larger, now we support BSP2
extern client_state_t cl;
// FIXME, allocate dynamically
extern entity_t cl_static_entities[MAX_STATIC_ENTITIES];
extern lightstyle_t cl_lightstyle[MAX_LIGHTSTYLES];
extern dlight_t cl_dlights[MAX_DLIGHTS];
extern entity_t cl_temp_entities[MAX_TEMP_ENTITIES];
extern beam_t cl_beams[MAX_BEAMS];
extern entity_t *cl_visedicts[MAX_VISEDICTS];
extern entity_t **cl_visedicts;
extern int cl_numvisedicts;
extern entity_t *cl_entities; //johnfitz -- was a static array, now on hunk
extern int cl_max_edicts; //johnfitz -- only changes when new map loads
extern int cl_maxvisedicts; //extended if we exceeded it the previous frame
//=============================================================================
@ -322,10 +375,15 @@ void CL_SendMove (const usercmd_t *cmd);
int CL_ReadFromServer (void);
void CL_BaseMove (usercmd_t *cmd);
void CL_Download_Data(void);
qboolean CL_CheckDownloads(void);
void CL_ParseEffect (qboolean big);
void CL_ParseTEnt (void);
void CL_UpdateTEnts (void);
void CL_ClearState (void);
void CL_ClearTrailStates(void);
//
// cl_demo.c
@ -342,7 +400,8 @@ void CL_TimeDemo_f (void);
// cl_parse.c
//
void CL_ParseServerMessage (void);
void CL_NewTranslation (int slot);
void CL_RegisterParticles(void);
//void CL_NewTranslation (int slot);
//
// view
@ -361,6 +420,7 @@ void V_SetContentsColor (int contents);
//
void CL_InitTEnts (void);
void CL_SignonReply (void);
float CL_TraceLine (vec3_t start, vec3_t end, vec3_t impact, vec3_t normal, int *ent);
//
// chase

View File

@ -24,6 +24,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "quakedef.h"
cvar_t cl_nopext = {"cl_nopext","0",CVAR_NONE}; //Spike -- prevent autodetection of protocol extensions, so that servers fall back to only their base protocol (without needing to reconfigure the server. Requires reconnect.
cvar_t cmd_warncmd = {"cl_warncmd","1",CVAR_NONE}; //Spike -- prevent autodetection of protocol extensions, so that servers fall back to only their base protocol (without needing to reconfigure the server. Requires reconnect.
void Cmd_ForwardToServer (void);
#define MAX_ALIAS_NAME 32
@ -97,7 +99,17 @@ void Cbuf_AddText (const char *text)
return;
}
SZ_Write (&cmd_text, text, Q_strlen (text));
SZ_Write (&cmd_text, text, l);
}
void Cbuf_AddTextLen (const char *text, int l)
{
if (cmd_text.cursize + l >= cmd_text.maxsize)
{
Con_Printf ("Cbuf_AddText: overflow\n");
return;
}
SZ_Write (&cmd_text, text, l);
}
@ -147,7 +159,7 @@ void Cbuf_Execute (void)
int i;
char *text;
char line[1024];
int quotes;
int quotes, comment;
while (cmd_text.cursize)
{
@ -155,11 +167,14 @@ void Cbuf_Execute (void)
text = (char *)cmd_text.data;
quotes = 0;
comment = 0;
for (i=0 ; i< cmd_text.cursize ; i++)
{
if (text[i] == '"')
quotes++;
if ( !(quotes&1) && text[i] == ';')
if (text[i] == '/' && text[i+1] == '/')
comment=true;
if ( !(quotes&1) && !comment && text[i] == ';')
break; // don't break if inside a quoted string
if (text[i] == '\n')
break;
@ -270,9 +285,11 @@ void Cmd_Exec_f (void)
f = (char *)COM_LoadHunkFile (Cmd_Argv(1), NULL);
if (!f)
{
if (cmd_warncmd.value)
Con_Printf ("couldn't exec %s\n",Cmd_Argv(1));
return;
}
if (cmd_warncmd.value)
Con_Printf ("execing %s\n",Cmd_Argv(1));
Cbuf_InsertText (f);
@ -410,6 +427,17 @@ void Cmd_Unalias_f (void)
}
}
qboolean Cmd_AliasExists (const char *aliasname)
{
cmdalias_t *a;
for (a=cmd_alias ; a ; a=a->next)
{
if (!q_strcasecmp (aliasname, a->name))
return true;
}
return false;
}
/*
===============
Cmd_Unaliasall_f -- johnfitz
@ -436,14 +464,6 @@ void Cmd_Unaliasall_f (void)
=============================================================================
*/
typedef struct cmd_function_s
{
struct cmd_function_s *next;
const char *name;
xcommand_t function;
} cmd_function_t;
#define MAX_ARGS 80
static int cmd_argc;
@ -536,7 +556,7 @@ void Cmd_Apropos_f(void)
}
for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
{
if (q_strcasestr(cmd->name, substr))
if (q_strcasestr(cmd->name, substr) && cmd->srctype != src_server)
{
hits++;
Con_SafePrintf ("%s\n", Cmd_TintSubstring(cmd->name, substr, tmpbuf, sizeof(tmpbuf)));
@ -575,6 +595,9 @@ void Cmd_Init (void)
Cmd_AddCommand ("apropos", Cmd_Apropos_f);
Cmd_AddCommand ("find", Cmd_Apropos_f);
Cvar_RegisterVariable (&cl_nopext);
Cvar_RegisterVariable (&cmd_warncmd);
}
/*
@ -664,9 +687,11 @@ void Cmd_TokenizeString (const char *text)
/*
============
Cmd_AddCommand
spike -- added an extra arg for client (also renamed and made a macro)
============
*/
void Cmd_AddCommand (const char *cmd_name, xcommand_t function)
void Cmd_AddCommand2 (const char *cmd_name, xcommand_t function, cmd_source_t srctype)
{
cmd_function_t *cmd;
cmd_function_t *cursor,*prev; //johnfitz -- sorted list insert
@ -684,7 +709,7 @@ void Cmd_AddCommand (const char *cmd_name, xcommand_t function)
// fail if the command already exists
for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
{
if (!Q_strcmp (cmd_name, cmd->name))
if (!Q_strcmp (cmd_name, cmd->name) && cmd->srctype == srctype)
{
Con_Printf ("Cmd_AddCommand: %s already defined\n", cmd_name);
return;
@ -694,6 +719,7 @@ void Cmd_AddCommand (const char *cmd_name, xcommand_t function)
cmd = (cmd_function_t *) Hunk_Alloc (sizeof(cmd_function_t));
cmd->name = cmd_name;
cmd->function = function;
cmd->srctype = srctype;
//johnfitz -- insert each entry in alphabetical order
if (cmd_functions == NULL || strcmp(cmd->name, cmd_functions->name) < 0) //insert at front
@ -767,7 +793,7 @@ A complete command line has been parsed, so try to execute it
FIXME: lookupnoadd the token to speed search?
============
*/
void Cmd_ExecuteString (const char *text, cmd_source_t src)
qboolean Cmd_ExecuteString (const char *text, cmd_source_t src)
{
cmd_function_t *cmd;
cmdalias_t *a;
@ -777,32 +803,49 @@ void Cmd_ExecuteString (const char *text, cmd_source_t src)
// execute the command line
if (!Cmd_Argc())
return; // no tokens
return true; // no tokens
// check functions
for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
{
if (!q_strcasecmp (cmd_argv[0],cmd->name))
{
if (src == src_client && cmd->srctype != src_client)
Con_DPrintf("%s tried to %s\n", host_client->name, text); //src_client only allows client commands
else if (src == src_command && cmd->srctype == src_server)
continue; //src_command can execute anything but server commands (which it ignores, allowing for alternative behaviour)
else if (src == src_server && cmd->srctype != src_server)
continue; //src_server may only execute server commands (such commands must be safe to parse within the context of a network message, so no disconnect/connect/playdemo/etc)
else
cmd->function ();
return;
return true;
}
}
if (src == src_client)
{ //spike -- please don't execute similarly named aliases, nor custom cvars...
Con_DPrintf("%s tried to %s\n", host_client->name, text);
return false;
}
if (src != src_command)
return false;
// check alias
for (a=cmd_alias ; a ; a=a->next)
{
if (!q_strcasecmp (cmd_argv[0], a->name))
{
Cbuf_InsertText (a->value);
return;
return true;
}
}
// check cvars
if (!Cvar_Command ())
if (cmd_warncmd.value || developer.value)
Con_Printf ("Unknown command \"%s\"\n", Cmd_Argv(0));
return true;
}
@ -830,6 +873,22 @@ void Cmd_ForwardToServer (void)
SZ_Print (&cls.message, Cmd_Argv(0));
SZ_Print (&cls.message, " ");
}
else
{
//hack zone for compat.
//stuffcmd("cmd foo\n") is a good way to query the client to see if it knows foo because the server is guarenteed a response even if it doesn't understand it, saving a timeout
if (!strcmp(Cmd_Args(), "protocols"))
{ //server asked us for a list of protocol numbers that we claim to support. this allows cool servers like fte to autodetect higher limits etc.
//servers may assume that the client's preferred protocol will be listed first.
SZ_Print (&cls.message, va("protocols %i %i %i %i %i", PROTOCOL_RMQ, PROTOCOL_FITZQUAKE, PROTOCOL_VERSION_BJP3, PROTOCOL_VERSION_DP7, PROTOCOL_NETQUAKE));
return;
}
if (!strcmp(Cmd_Args(), "pext") && !cl_nopext.value)
{ //server asked us for a key+value list of the extensions+attributes we support
SZ_Print (&cls.message, va("pext %#x %#x", PROTOCOL_FTE_PEXT2, PEXT2_SUPPORTED_CLIENT));
return;
}
}
if (Cmd_Argc() > 1)
SZ_Print (&cls.message, Cmd_Args());
else

View File

@ -42,6 +42,7 @@ The game starts with a Cbuf_AddText ("exec quake.rc\n"); Cbuf_Execute ();
void Cbuf_Init (void);
// allocates an initial text buffer that will grow as needed
void Cbuf_AddTextLen (const char *text, int l);
void Cbuf_AddText (const char *text);
// as new commands are generated from the console or keybindings,
// the text is added to the end of the command buffer.
@ -70,24 +71,36 @@ not apropriate.
*/
typedef void (*xcommand_t) (void);
typedef enum
{
src_client, // came in over a net connection as a clc_stringcmd
// host_client will be valid during this state.
src_command // from the command buffer
src_command, // from the command buffer
src_server // from a svc_stufftext
} cmd_source_t;
extern cmd_source_t cmd_source;
typedef void (*xcommand_t) (void);
typedef struct cmd_function_s
{
struct cmd_function_s *next;
const char *name;
xcommand_t function;
cmd_source_t srctype;
} cmd_function_t;
void Cmd_Init (void);
void Cmd_AddCommand (const char *cmd_name, xcommand_t function);
void Cmd_AddCommand2 (const char *cmd_name, xcommand_t function, cmd_source_t srctype);
// called by the init functions of other parts of the program to
// register commands and functions to call for them.
// The cmd_name is referenced later, so it should not be in temp memory
#define Cmd_AddCommand(cmdname,func) Cmd_AddCommand2(cmdname,func,src_command) //regular console commands
#define Cmd_AddCommand_ClientCommand(cmdname,func) Cmd_AddCommand2(cmdname,func,src_client) //command is meant to be safe for anyone to execute.
#define Cmd_AddCommand_ServerCommand(cmdname,func) Cmd_AddCommand2(cmdname,func,src_server) //command came from a server
#define Cmd_AddCommand_Console Cmd_AddCommand //to make the disabiguation more obvious
qboolean Cmd_AliasExists (const char *aliasname);
qboolean Cmd_Exists (const char *cmd_name);
// used by the cvar code to check for cvar / command name overlap
@ -110,7 +123,7 @@ void Cmd_TokenizeString (const char *text);
// Takes a null terminated string. Does not need to be /n terminated.
// breaks the string up into arg tokens.
void Cmd_ExecuteString (const char *text, cmd_source_t src);
qboolean Cmd_ExecuteString (const char *text, cmd_source_t src);
// Parses a single line of text into arguments and tries to execute it.
// The text can come from the command buffer, a remote client, or stdin.

View File

@ -26,6 +26,10 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "q_ctype.h"
#include <errno.h>
#ifndef _WIN32
#include <dirent.h>
#endif
static char *largv[MAX_NUM_ARGVS + 1];
static char argvdummy[] = " ";
@ -33,6 +37,7 @@ int safemode;
cvar_t registered = {"registered","1",CVAR_ROM}; /* set to correct value in COM_CheckRegistered() */
cvar_t cmdline = {"cmdline","",CVAR_ROM/*|CVAR_SERVERINFO*/}; /* sending cmdline upon CCREQ_RULE_INFO is evil */
cvar_t allow_download = {"allow_download", "1"}; /*set to 0 to block file downloads, both client+server*/
static qboolean com_modified; // set true if using non-id files
@ -923,6 +928,23 @@ float MSG_ReadAngle16 (unsigned int flags)
}
//johnfitz
//spike -- for downloads
byte *MSG_ReadData (unsigned int length)
{
byte *data;
if (msg_readcount+length > (unsigned int)net_message.cursize)
{
msg_badread = true;
return NULL;
}
data = net_message.data+msg_readcount;
msg_readcount += length;
return data;
}
//===========================================================================
void SZ_Alloc (sizebuf_t *buf, int startsize)
@ -946,6 +968,7 @@ void SZ_Free (sizebuf_t *buf)
void SZ_Clear (sizebuf_t *buf)
{
buf->cursize = 0;
buf->overflowed = false;
}
void *SZ_GetSpace (sizebuf_t *buf, int length)
@ -960,9 +983,9 @@ void *SZ_GetSpace (sizebuf_t *buf, int length)
if (length > buf->maxsize)
Sys_Error ("SZ_GetSpace: %i is > full buffer size", length);
buf->overflowed = true;
Con_Printf ("SZ_GetSpace: overflow");
Con_Printf ("SZ_GetSpace: overflow\n");
SZ_Clear (buf);
buf->overflowed = true;
}
data = buf->data + buf->cursize;
@ -1152,6 +1175,56 @@ void COM_AddExtension (char *path, const char *extension, size_t len)
q_strlcat(path, extension, len);
}
/*
spike -- this function simply says whether a filename is acceptable for downloading (used by both client+server)
*/
qboolean COM_DownloadNameOkay(const char *filename)
{
if (!allow_download.value)
return false;
//quickly test the prefix to ensure that its in one of the allowed subdirs
if (strncmp(filename, "sound/", 6) &&
strncmp(filename, "progs/", 6) &&
strncmp(filename, "maps/", 5) &&
strncmp(filename, "models/", 7))
return false;
//windows paths are NOT permitted, nor are alternative data streams, nor wildcards, and double quotes are always bad(which allows for spaces)
if (strchr(filename, '\\') || strchr(filename, ':') || strchr(filename, '*') || strchr(filename, '?') || strchr(filename, '\"'))
return false;
//some operating systems interpret this as 'parent directory'
if (strstr(filename, "//"))
return false;
//block unix hidden files, also blocks relative paths.
if (*filename == '.' || strstr(filename, "/."))
return false;
//test the extension to ensure that its in one of the allowed file types
//(no .dll, .so, .com, .exe, .bat, .vbs, .xls, .doc, etc please)
//also don't allow config files.
filename = COM_FileGetExtension(filename);
if (
//model formats
q_strcasecmp(filename, "bsp") &&
q_strcasecmp(filename, "mdl") &&
q_strcasecmp(filename, "iqm") && //in case we ever support these later
q_strcasecmp(filename, "md3") && //in case we ever support these later
q_strcasecmp(filename, "spr") &&
q_strcasecmp(filename, "spr32") &&
//audio formats
q_strcasecmp(filename, "wav") &&
q_strcasecmp(filename, "ogg") &&
//image formats (if we ever need that)
q_strcasecmp(filename, "tga") &&
q_strcasecmp(filename, "png") &&
//misc stuff
q_strcasecmp(filename, "lux") &&
q_strcasecmp(filename, "lit2") &&
q_strcasecmp(filename, "lit"))
return false;
//okay, well, we didn't throw a hissy fit, so whatever dude, go ahead and download
return true;
}
/*
==============
@ -1251,11 +1324,11 @@ Returns the position (1 to argc-1) in the program's argument list
where the given parameter apears, or 0 if not present
================
*/
int COM_CheckParm (const char *parm)
int COM_CheckParmNext (int last, const char *parm)
{
int i;
for (i = 1; i < com_argc; i++)
for (i = last+1; i < com_argc; i++)
{
if (!com_argv[i])
continue; // NEXTSTEP sometimes clears appkit vars.
@ -1265,6 +1338,10 @@ int COM_CheckParm (const char *parm)
return 0;
}
int COM_CheckParm (const char *parm)
{
return COM_CheckParmNext(0, parm);
}
/*
================
@ -1384,6 +1461,23 @@ static void FitzTest_f (void)
}
#endif
entity_state_t nullentitystate;
static void COM_SetupNullState(void)
{
//the null state has some specific default values
// nullentitystate.drawflags = /*SCALE_ORIGIN_ORIGIN*/96;
nullentitystate.colormod[0] = 32;
nullentitystate.colormod[1] = 32;
nullentitystate.colormod[2] = 32;
// nullentitystate.glowmod[0] = 32;
// nullentitystate.glowmod[1] = 32;
// nullentitystate.glowmod[2] = 32;
nullentitystate.alpha = 0; //fte has 255 by default, with 0 for invisible. fitz uses 1 for invisible, 0 default, and 255=full alpha
nullentitystate.scale = 16;
// nullentitystate.solidsize = 0;//ES_SOLID_BSP;
}
/*
================
COM_Init
@ -1435,6 +1529,8 @@ void COM_Init (void)
#ifdef _DEBUG
Cmd_AddCommand ("fitztest", FitzTest_f); //johnfitz
#endif
COM_SetupNullState();
}
@ -1501,6 +1597,7 @@ typedef struct
#define MAX_FILES_IN_PACK 2048
char com_gamenames[1024]; //eg: "hipnotic;quoth;warp", no id1, no private stuff
char com_gamedir[MAX_OSPATH];
char com_basedir[MAX_OSPATH];
int file_from_pak; // ZOID: global indicating that file came from a pak
@ -1635,16 +1732,39 @@ static int COM_FindFile (const char *filename, int *handle, FILE **file,
if (path_id)
*path_id = search->path_id;
if (handle)
{
if (pak->files[i].deflatedsize)
{
FILE *f;
f = fopen (pak->filename, "rb");
if (f)
{
fseek (f, pak->files[i].filepos, SEEK_SET);
f = FSZIP_Deflate(f, pak->files[i].deflatedsize, pak->files[i].filelen);
*handle = Sys_FileOpenStdio(f);
}
else
{ //error!
com_filesize = -1;
*handle = -1;
}
}
else
{
*handle = pak->handle;
Sys_FileSeek (pak->handle, pak->files[i].filepos);
}
return com_filesize;
}
else if (file)
{ /* open a new file on the pakfile */
*file = fopen (pak->filename, "rb");
if (*file)
{
fseek (*file, pak->files[i].filepos, SEEK_SET);
if (pak->files[i].deflatedsize)
*file = FSZIP_Deflate(*file, pak->files[i].deflatedsize, pak->files[i].filelen);
}
return com_filesize;
}
else /* for COM_FileExists() */
@ -1659,6 +1779,8 @@ static int COM_FindFile (const char *filename, int *handle, FILE **file,
{ /* if not a registered version, don't ever go beyond base */
if ( strchr (filename, '/') || strchr (filename,'\\'))
continue;
if (q_strcasecmp(COM_FileGetExtension(filename), "dat")) //don't load custom progs.dats either
continue;
}
q_snprintf (netpath, sizeof(netpath), "%s/%s",search->filename, filename);
@ -1981,20 +2103,22 @@ static pack_t *COM_LoadPackFile (const char *packfile)
if (numpackfiles > MAX_FILES_IN_PACK)
Sys_Error ("%s has %i files", packfile, numpackfiles);
if (numpackfiles != PAK0_COUNT)
com_modified = true; // not the original file
newfiles = (packfile_t *) Z_Malloc(numpackfiles * sizeof(packfile_t));
Sys_FileSeek (packhandle, header.dirofs);
Sys_FileRead (packhandle, (void *)info, header.dirlen);
if (numpackfiles != PAK0_COUNT)
com_modified = true; // not the original file
else
{
// crc the directory to check for modifications
CRC_Init (&crc);
for (i = 0; i < header.dirlen; i++)
CRC_ProcessByte (&crc, ((byte *)info)[i]);
if (crc != PAK0_CRC_V106 && crc != PAK0_CRC_V101 && crc != PAK0_CRC_V100)
com_modified = true;
}
// parse the directory
for (i = 0; i < numpackfiles; i++)
@ -2014,19 +2138,172 @@ static pack_t *COM_LoadPackFile (const char *packfile)
return pack;
}
static void COM_ListSystemFiles(void *ctx, const char *gamedir, const char *ext, qboolean (*cb)(void *ctx, const char *fname))
{
#ifdef _WIN32
WIN32_FIND_DATA fdat;
HANDLE fhnd;
char filestring[MAX_OSPATH];
q_snprintf (filestring, sizeof(filestring), "%s/*.%s", gamedir, ext);
fhnd = FindFirstFile(filestring, &fdat);
if (fhnd == INVALID_HANDLE_VALUE)
return;
do
{
cb (ctx, fdat.cFileName);
} while (FindNextFile(fhnd, &fdat));
FindClose(fhnd);
#else
DIR *dir_p;
struct dirent *dir_t;
dir_p = opendir(gamedir);
if (dir_p == NULL)
return;
while ((dir_t = readdir(dir_p)) != NULL)
{
if (q_strcasecmp(COM_FileGetExtension(dir_t->d_name), ext) != 0)
continue;
cb (ctx, dir_t->d_name);
}
closedir(dir_p);
#endif
}
static qboolean COM_AddPackage(searchpath_t *basepath, const char *pakfile)
{
searchpath_t *search;
pack_t *pak;
const char *ext = COM_FileGetExtension(pakfile);
//don't add the same pak twice.
for (search = com_searchpaths; search; search = search->next)
{
if (search->pack)
if (!q_strcasecmp(pakfile, search->pack->filename))
return true;
}
if (!q_strcasecmp(ext, "pak"))
pak = COM_LoadPackFile (pakfile);
else if (!q_strcasecmp(ext, "pk3") || !q_strcasecmp(ext, "pk4") || !q_strcasecmp(ext, "zip") || !q_strcasecmp(ext, "apk"))
{
pak = FSZIP_LoadArchive(pakfile);
if (pak)
com_modified = true; //would always be true, so we don't bother with crcs.
}
else
pak = NULL;
if (!pak)
return false;
search = (searchpath_t *) Z_Malloc(sizeof(searchpath_t));
search->path_id = basepath->path_id;
search->pack = pak;
search->next = com_searchpaths;
com_searchpaths = search;
return true;
}
static qboolean COM_AddEnumeratedPackage(void *ctx, const char *pakfile)
{
searchpath_t *basepath = ctx;
char fullpakfile[MAX_OSPATH];
q_snprintf (fullpakfile, sizeof(fullpakfile), "%s/%s", basepath->filename, pakfile);
return COM_AddPackage(basepath, fullpakfile);
}
const char *COM_GetGameNames(qboolean full)
{
if (full)
{
if (*com_gamenames)
return va("%s;%s", GAMENAME, com_gamenames);
else
return GAMENAME;
}
return com_gamenames;
// return COM_SkipPath(com_gamedir);
}
//if either contain id1 then that gets ignored
qboolean COM_GameDirMatches(const char *tdirs)
{
int gnl = strlen(GAMENAME);
const char *odirs = COM_GetGameNames(false);
//ignore any core paths.
if (!strncmp(tdirs, GAMENAME, gnl) && (tdirs[gnl] == ';' || !tdirs[gnl]))
{
tdirs+=gnl;
if (*tdirs == ';')
tdirs++;
}
if (!strncmp(odirs, GAMENAME, gnl) && (odirs[gnl] == ';' || !odirs[gnl]))
{
odirs+=gnl;
if (*odirs == ';')
odirs++;
}
//skip any qw in there from quakeworld (remote servers should really be skipping this, unless its maybe the only one in the path).
if (!strncmp(tdirs, "qw;", 3) || !strcmp(tdirs, "qw"))
{
tdirs+=2;
if (*tdirs==';')
tdirs++;
}
if (!strncmp(odirs, "qw;", 3) || !strcmp(odirs, "qw")) //need to cope with ourselves setting it that way too, just in case.
{
odirs+=2;
if (*odirs==';')
odirs++;
}
//okay, now check it properly
if (!strcmp(odirs, tdirs))
return true;
return false;
}
/*
=================
COM_AddGameDirectory -- johnfitz -- modified based on topaz's tutorial
=================
*/
static void COM_AddGameDirectory (const char *base, const char *dir)
static void COM_AddGameDirectory (const char *dir)
{
const char *base = com_basedir;
int i;
unsigned int path_id;
searchpath_t *search;
pack_t *pak, *qspak;
searchpath_t *searchdir;
char pakfile[MAX_OSPATH];
qboolean been_here = false;
FILE *listing;
qboolean found;
const char *enginepackname = "quakespasm";
if (*dir == '*')
dir++;
else if (!strchr(dir, '/') && !strchr(dir, '\\'))
{
//fixme: block dupes
if (*com_gamenames)
q_strlcat(com_gamenames, ";", sizeof(com_gamenames));
q_strlcat(com_gamenames, dir, sizeof(com_gamenames));
}
//quakespasm enables mission pack flags automatically, so -game rogue works without breaking the hud
//we might as well do that here to simplify the code.
if (!q_strcasecmp(dir,"rogue")) {
rogue = true;
standard_quake = false;
}
if (!q_strcasecmp(dir,"hipnotic") || !q_strcasecmp(dir,"quoth")) {
hipnotic = true;
standard_quake = false;
}
q_strlcpy (com_gamedir, va("%s/%s", base, dir), sizeof(com_gamedir));
@ -2036,44 +2313,85 @@ static void COM_AddGameDirectory (const char *base, const char *dir)
else path_id = 1U;
_add_path:
// add the directory to the search path
search = (searchpath_t *) Z_Malloc(sizeof(searchpath_t));
search->path_id = path_id;
q_strlcpy (search->filename, com_gamedir, sizeof(search->filename));
search->next = com_searchpaths;
com_searchpaths = search;
searchdir = (searchpath_t *) Z_Malloc(sizeof(searchpath_t));
searchdir->path_id = path_id;
q_strlcpy (searchdir->filename, com_gamedir, sizeof(searchdir->filename));
q_snprintf (pakfile, sizeof(pakfile), "%s/pak.lst", com_gamedir);
listing = fopen(pakfile, "rb");
if (listing)
{
int len;
char *buffer;
const char *name;
fseek(listing, 0, SEEK_END);
len = ftell(listing);
fseek(listing, 0, SEEK_SET);
buffer = Z_Malloc(len+1);
fread(buffer, 1, len, listing);
buffer[len] = 0;
fclose(listing);
name = buffer;
com_modified = true; //any reordering of paks should be frowned upon
while ((name = COM_Parse(name)))
{
if (!*com_token)
continue;
if (strchr(com_token, '/') || strchr(com_token, '\\') || strchr(com_token, ':'))
continue;
q_snprintf (pakfile, sizeof(pakfile), "%s/%s", com_gamedir, com_token);
COM_AddPackage(searchdir, pakfile);
if (path_id == 1 && !fitzmode && !q_strncasecmp(com_token, "pak0.", 5))
{ //add this now, to try to retain correct ordering.
qboolean old = com_modified;
if (been_here) base = host_parms->userdir;
q_snprintf (pakfile, sizeof(pakfile), "%s/%s.%s", base, enginepackname, COM_FileGetExtension(com_token));
COM_AddPackage(searchdir, pakfile);
com_modified = old;
}
}
}
// add any pak files in the format pak0.pak pak1.pak, ...
for (i = 0; ; i++)
{
found = false;
q_snprintf (pakfile, sizeof(pakfile), "%s/pak%i.pak", com_gamedir, i);
pak = COM_LoadPackFile (pakfile);
if (i != 0 || path_id != 1 || fitzmode)
qspak = NULL;
else {
found |= COM_AddPackage(searchdir, pakfile);
q_snprintf (pakfile, sizeof(pakfile), "%s/pak%i.pk3", com_gamedir, i);
found |= COM_AddPackage(searchdir, pakfile);
if (i == 0 && path_id == 1 && !fitzmode)
{
qboolean old = com_modified;
if (been_here) base = host_parms->userdir;
q_snprintf (pakfile, sizeof(pakfile), "%s/quakespasm.pak", base);
qspak = COM_LoadPackFile (pakfile);
q_snprintf (pakfile, sizeof(pakfile), "%s/%s.pak", base, enginepackname);
COM_AddPackage(searchdir, pakfile);
q_snprintf (pakfile, sizeof(pakfile), "%s/%s.pk3", base, enginepackname);
COM_AddPackage(searchdir, pakfile);
com_modified = old;
}
if (pak) {
search = (searchpath_t *) Z_Malloc(sizeof(searchpath_t));
search->path_id = path_id;
search->pack = pak;
search->next = com_searchpaths;
com_searchpaths = search;
if (!found)
break;
}
if (qspak) {
search = (searchpath_t *) Z_Malloc(sizeof(searchpath_t));
search->path_id = path_id;
search->pack = qspak;
search->next = com_searchpaths;
com_searchpaths = search;
}
if (!pak) break;
i = COM_CheckParm ("-nowildpaks");
if (!i)
{
COM_ListSystemFiles(searchdir, com_gamedir, "pak", COM_AddEnumeratedPackage);
COM_ListSystemFiles(searchdir, com_gamedir, "pk3", COM_AddEnumeratedPackage);
}
// then finally link the directory to the search path
//spike -- moved this last (also explicitly blocked loading progs.dat from system paths when running the demo)
searchdir->next = com_searchpaths;
com_searchpaths = searchdir;
if (!been_here && host_parms->userdir != host_parms->basedir)
{
been_here = true;
@ -2083,71 +2401,10 @@ _add_path:
}
}
//==============================================================================
//johnfitz -- dynamic gamedir stuff -- modified by QuakeSpasm team.
//==============================================================================
void ExtraMaps_NewGame (void);
static void COM_Game_f (void)
void COM_ResetGameDirectories(char *newgamedirs)
{
if (Cmd_Argc() > 1)
{
const char *p = Cmd_Argv(1);
const char *p2 = Cmd_Argv(2);
char *newpath, *path;
searchpath_t *search;
if (!registered.value) //disable shareware quake
{
Con_Printf("You must have the registered version to use modified games\n");
return;
}
if (!*p || !strcmp(p, ".") || strstr(p, "..") || strstr(p, "/") || strstr(p, "\\") || strstr(p, ":"))
{
Con_Printf ("gamedir should be a single directory name, not a path\n");
return;
}
if (*p2)
{
if (strcmp(p2,"-hipnotic") && strcmp(p2,"-rogue") && strcmp(p2,"-quoth")) {
Con_Printf ("invalid mission pack argument to \"game\"\n");
return;
}
if (!q_strcasecmp(p, GAMENAME)) {
Con_Printf ("no mission pack arguments to %s game\n", GAMENAME);
return;
}
}
if (!q_strcasecmp(p, COM_SkipPath(com_gamedir))) //no change
{
if (com_searchpaths->path_id > 1) { //current game not id1
if (*p2 && com_searchpaths->path_id == 2) {
// rely on QuakeSpasm extension treating '-game missionpack'
// as '-missionpack', otherwise would be a mess
if (!q_strcasecmp(p, &p2[1]))
goto _same;
Con_Printf("reloading game \"%s\" with \"%s\" support\n", p, &p2[1]);
}
else if (!*p2 && com_searchpaths->path_id > 2)
Con_Printf("reloading game \"%s\" without mission pack support\n", p);
else goto _same;
}
else { _same:
Con_Printf("\"game\" is already \"%s\"\n", COM_SkipPath(com_gamedir));
return;
}
}
com_modified = true;
//Kill the server
CL_Disconnect ();
Host_ShutdownServer(true);
//Write config file
Host_WriteConfiguration ();
//Kill the extra game if it is loaded
while (com_searchpaths != com_base_searchpaths)
{
@ -2164,40 +2421,99 @@ static void COM_Game_f (void)
hipnotic = false;
rogue = false;
standard_quake = true;
//wipe the list of mod gamedirs
*com_gamenames = 0;
//reset this too
q_strlcpy (com_gamedir, va("%s/%s", (host_parms->userdir != host_parms->basedir)?host_parms->userdir:com_basedir, GAMENAME), sizeof(com_gamedir));
if (q_strcasecmp(p, GAMENAME)) //game is not id1
for(newpath = newgamedirs; newpath && *newpath; )
{
if (*p2) {
COM_AddGameDirectory (com_basedir, &p2[1]);
standard_quake = false;
if (!strcmp(p2,"-hipnotic") || !strcmp(p2,"-quoth"))
hipnotic = true;
else if (!strcmp(p2,"-rogue"))
rogue = true;
if (q_strcasecmp(p, &p2[1])) //don't load twice
COM_AddGameDirectory (com_basedir, p);
}
else {
COM_AddGameDirectory (com_basedir, p);
// QuakeSpasm extension: treat '-game missionpack' as '-missionpack'
if (!q_strcasecmp(p,"hipnotic") || !q_strcasecmp(p,"quoth")) {
hipnotic = true;
standard_quake = false;
}
else if (!q_strcasecmp(p,"rogue")) {
rogue = true;
standard_quake = false;
}
}
}
else // just update com_gamedir
char *e = strchr(newpath, ';');
if (e)
*e++ = 0;
if (!q_strcasecmp(GAMENAME, newpath))
path = NULL;
else for (path = newgamedirs; path < newpath; path += strlen(path)+1)
{
q_snprintf (com_gamedir, sizeof(com_gamedir), "%s/%s",
(host_parms->userdir != host_parms->basedir)?
host_parms->userdir : com_basedir,
GAMENAME);
if (!q_strcasecmp(path, newpath))
break;
}
if (path == newpath) //not already loaded
COM_AddGameDirectory(newpath);
newpath = e;
}
}
//==============================================================================
//johnfitz -- dynamic gamedir stuff -- modified by QuakeSpasm team.
//==============================================================================
void ExtraMaps_NewGame (void);
static void COM_Game_f (void)
{
if (Cmd_Argc() > 1)
{
int i, pri;
char paths[1024];
if (!registered.value) //disable shareware quake
{
Con_Printf("You must have the registered version to use modified games\n");
return;
}
*paths = 0;
q_strlcat(paths, GAMENAME, sizeof(paths));
for (pri = 0; pri <= 1; pri++)
{
for (i = 1; i < Cmd_Argc(); i++)
{
const char *p = Cmd_Argv(i);
if (!*p)
p = GAMENAME;
if (pri == 0)
{
if (*p != '-')
continue;
p++;
}
else if (*p == '-')
continue;
if (!*p || !strcmp(p, ".") || strstr(p, "..") || strstr(p, "/") || strstr(p, "\\") || strstr(p, ":"))
{
Con_Printf ("gamedir should be a single directory name, not a path\n");
return;
}
if (!q_strcasecmp(p, GAMENAME))
continue; //don't add id1, its not interesting enough.
if (*paths)
q_strlcat(paths, ";", sizeof(paths));
q_strlcat(paths, p, sizeof(paths));
}
}
if (!q_strcasecmp(paths, COM_GetGameNames(true)))
{
Con_Printf("\"game\" is already \"%s\"\n", COM_GetGameNames(true));
return;
}
com_modified = true;
//Kill the server
CL_Disconnect ();
Host_ShutdownServer(true);
//Write config file
//fixme -- writing configs without reloading when switching between many mods is SERIOUSLY dangerous. ignore if no 'exec default.cfg' commands were used?
Host_WriteConfiguration ();
COM_ResetGameDirectories(paths);
//clear out and reload appropriate data
Cache_Flush ();
Mod_ResetAll();
@ -2210,14 +2526,14 @@ static void COM_Game_f (void)
ExtraMaps_NewGame ();
DemoList_Rebuild ();
Con_Printf("\"game\" changed to \"%s\"\n", COM_SkipPath(com_gamedir));
Con_Printf("\"game\" changed to \"%s\"\n", COM_GetGameNames(true));
VID_Lock ();
Cbuf_AddText ("exec quake.rc\n");
Cbuf_AddText ("vid_unlock\n");
}
else //Diplay the current gamedir
Con_Printf("\"game\" is \"%s\"\n", COM_SkipPath(com_gamedir));
Con_Printf("\"game\" is \"%s\"\n", COM_GetGameNames(true));
}
/*
@ -2228,11 +2544,14 @@ COM_InitFilesystem
void COM_InitFilesystem (void) //johnfitz -- modified based on topaz's tutorial
{
int i, j;
const char *p;
Cvar_RegisterVariable (&allow_download);
Cvar_RegisterVariable (&registered);
Cvar_RegisterVariable (&cmdline);
Cmd_AddCommand ("path", COM_Path_f);
Cmd_AddCommand ("game", COM_Game_f); //johnfitz
Cmd_AddCommand ("gamedir", COM_Game_f); //Spike -- alternative name for it, consistent with quakeworld and a few other engines
i = COM_CheckParm ("-basedir");
if (i && i < com_argc-1)
@ -2246,46 +2565,36 @@ void COM_InitFilesystem (void) //johnfitz -- modified based on topaz's tutorial
com_basedir[j-1] = 0;
// start up with GAMENAME by default (id1)
COM_AddGameDirectory (com_basedir, GAMENAME);
COM_AddGameDirectory (GAMENAME);
/* this is the end of our base searchpath:
* any set gamedirs, such as those from -game command line
* arguments or by the 'game' console command will be freed
* up to here upon a new game command. */
com_base_searchpaths = com_searchpaths;
COM_ResetGameDirectories("");
// add mission pack requests (only one should be specified)
if (COM_CheckParm ("-rogue"))
COM_AddGameDirectory (com_basedir, "rogue");
COM_AddGameDirectory ("rogue");
if (COM_CheckParm ("-hipnotic"))
COM_AddGameDirectory (com_basedir, "hipnotic");
COM_AddGameDirectory ("hipnotic");
if (COM_CheckParm ("-quoth"))
COM_AddGameDirectory (com_basedir, "quoth");
COM_AddGameDirectory ("quoth");
i = COM_CheckParm ("-game");
if (i && i < com_argc-1)
for(i = 0;;)
{
const char *p = com_argv[i + 1];
if (!*p || !strcmp(p, ".") || strstr(p, "..") || strstr(p, "/") || strstr(p, "\\") || strstr(p, ":"))
i = COM_CheckParmNext (i, "-game");
if (!i || i >= com_argc-1)
break;
p = com_argv[i + 1];
if (!*p || !strcmp(p, ".") || strstr(p, "..") || *p=='/' || *p=='\\' || strstr(p, ":"))
Sys_Error ("gamedir should be a single directory name, not a path\n");
com_modified = true;
// don't load mission packs twice
if (COM_CheckParm ("-rogue") && !q_strcasecmp(p, "rogue")) p = NULL;
if (COM_CheckParm ("-hipnotic") && !q_strcasecmp(p, "hipnotic")) p = NULL;
if (COM_CheckParm ("-quoth") && !q_strcasecmp(p, "quoth")) p = NULL;
if (p != NULL) {
COM_AddGameDirectory (com_basedir, p);
// QuakeSpasm extension: treat '-game missionpack' as '-missionpack'
if (!q_strcasecmp(p,"rogue")) {
rogue = true;
standard_quake = false;
}
if (!q_strcasecmp(p,"hipnotic") || !q_strcasecmp(p,"quoth")) {
hipnotic = true;
standard_quake = false;
}
}
if (p != NULL)
COM_AddGameDirectory (p);
}
COM_CheckRegistered ();
@ -2461,3 +2770,69 @@ long FS_filelength (fshandle_t *fh)
return fh->length;
}
//for compat with dpp7 protocols, and mods that cba to precache things.
void COM_Effectinfo_Enumerate(int (*cb)(const char *pname))
{
int i;
const char *f, *e;
char *buf;
static const char *dpnames[] =
{
"TE_GUNSHOT",
"TE_GUNSHOTQUAD",
"TE_SPIKE",
"TE_SPIKEQUAD",
"TE_SUPERSPIKE",
"TE_SUPERSPIKEQUAD",
"TE_WIZSPIKE",
"TE_KNIGHTSPIKE",
"TE_EXPLOSION",
"TE_EXPLOSIONQUAD",
"TE_TAREXPLOSION",
"TE_TELEPORT",
"TE_LAVASPLASH",
"TE_SMALLFLASH",
"TE_FLAMEJET",
"EF_FLAME",
"TE_BLOOD",
"TE_SPARK",
"TE_PLASMABURN",
"TE_TEI_G3",
"TE_TEI_SMOKE",
"TE_TEI_BIGEXPLOSION",
"TE_TEI_PLASMAHIT",
"EF_STARDUST",
"TR_ROCKET",
"TR_GRENADE",
"TR_BLOOD",
"TR_WIZSPIKE",
"TR_SLIGHTBLOOD",
"TR_KNIGHTSPIKE",
"TR_VORESPIKE",
"TR_NEHAHRASMOKE",
"TR_NEXUIZPLASMA",
"TR_GLOWTRAIL",
"SVC_PARTICLE",
NULL
};
buf = (char*)COM_LoadMallocFile("effectinfo.txt", NULL);
if (!buf)
return;
for (i = 0; dpnames[i]; i++)
cb(dpnames[i]);
for (f = buf; f; f = e)
{
e = COM_Parse (f);
if (!strcmp(com_token, "effect"))
{
e = COM_Parse (e);
cb(com_token);
}
while (e && *e && *e != '\n')
e++;
}
free(buf);
}

View File

@ -103,6 +103,8 @@ void MSG_WriteString (sizebuf_t *sb, const char *s);
void MSG_WriteCoord (sizebuf_t *sb, float f, unsigned int flags);
void MSG_WriteAngle (sizebuf_t *sb, float f, unsigned int flags);
void MSG_WriteAngle16 (sizebuf_t *sb, float f, unsigned int flags); //johnfitz
struct entity_state_s;
void MSG_WriteStaticOrBaseLine(sizebuf_t *buf, int idx, struct entity_state_s *state, unsigned int protocol_pext2, unsigned int protocol, unsigned int protocolflags); //spike
extern int msg_readcount;
extern qboolean msg_badread; // set if a read goes beyond end of message
@ -118,6 +120,9 @@ const char *MSG_ReadString (void);
float MSG_ReadCoord (unsigned int flags);
float MSG_ReadAngle (unsigned int flags);
float MSG_ReadAngle16 (unsigned int flags); //johnfitz
byte *MSG_ReadData (unsigned int length); // spike
void COM_Effectinfo_Enumerate(int (*cb)(const char *pname)); //spike -- for dp compat
//============================================================================
@ -171,6 +176,7 @@ extern int safemode;
*/
int COM_CheckParm (const char *parm);
int COM_CheckParmNext (int last, const char *parm);
void COM_Init (void);
void COM_InitArgv (int argc, char **argv);
@ -180,6 +186,7 @@ const char *COM_SkipPath (const char *pathname);
void COM_StripExtension (const char *in, char *out, size_t outsize);
void COM_FileBase (const char *in, char *out, size_t outsize);
void COM_AddExtension (char *path, const char *extension, size_t len);
qboolean COM_DownloadNameOkay(const char *filename);
#if 0 /* COM_DefaultExtension can be dangerous */
void COM_DefaultExtension (char *path, const char *extension, size_t len);
#endif
@ -198,6 +205,7 @@ typedef struct
{
char name[MAX_QPATH];
int filepos, filelen;
int deflatedsize;
} packfile_t;
typedef struct pack_s
@ -228,6 +236,12 @@ extern char com_basedir[MAX_OSPATH];
extern char com_gamedir[MAX_OSPATH];
extern int file_from_pak; // global indicating that file came from a pak
const char *COM_GetGameNames(qboolean full);
qboolean COM_GameDirMatches(const char *tdirs);
pack_t *FSZIP_LoadArchive (const char *packfile);
FILE *FSZIP_Deflate(FILE *src, int srcsize, int outsize);
void COM_WriteFile (const char *filename, const void *data, int len);
int COM_OpenFile (const char *filename, int *handle, unsigned int *path_id);
int COM_FOpenFile (const char *filename, FILE **file, unsigned int *path_id);

View File

@ -31,6 +31,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include <unistd.h>
#endif
#include "quakedef.h"
#include "q_ctype.h"
int con_linewidth;
@ -54,6 +55,9 @@ cvar_t con_logcenterprint = {"con_logcenterprint", "1", CVAR_NONE}; //johnfitz
char con_lastcenterstring[1024]; //johnfitz
void (*con_redirect_flush)(const char *buffer); //call this to flush the redirection buffer (for rcon)
char con_redirect_buffer[8192];
#define NUM_CON_TIMES 4
float con_times[NUM_CON_TIMES]; // realtime time the line was generated
// for transparent notify lines
@ -358,6 +362,17 @@ static void Con_Linefeed (void)
Q_memset (&con_text[(con_current%con_totallines)*con_linewidth], ' ', con_linewidth);
}
#define ishex(c) ((c>='0' && c<= '9') || (c>='a' && c<='f') || (c>='A' && c<='F'))
static int dehex(char c)
{
if (c >= '0' && c <= '9')
return c-'0';
if (c >= 'A' && c <= 'F')
return c-('A'-10);
if (c >= 'a' && c <= 'f')
return c-('a'-10);
return 0;
}
/*
================
Con_Print
@ -395,6 +410,95 @@ static void Con_Print (const char *txt)
while ( (c = *txt) )
{
if (c == '^' && pr_checkextension.value)
{ //parse markup like FTE/DP might.
switch(txt[1])
{
case '^': //doubled up char for escaping.
txt++;
break;
case '0': //black
case '1': //red
case '2': //green
case '3': //yellow
case '4': //blue
case '5': //cyan
case '6': //magenta
case '7': //white
case '8': //white+half-alpha
case '9': //grey
case 'h': //toggle half-alpha
case 'b': //blink
case 'd': //reset to defaults (fixme: should reset ^m without resetting \1)
case 's': //modstack push
case 'r': //modstack restore
txt+=2;
continue;
case 'x': //RGB 12-bit colour
if (ishex(txt[2]) && ishex(txt[3]) && ishex(txt[4]))
{
txt+=4;
continue;
}
break; //malformed
case '[': //start fte's ^[text\key\value\key\value^] links
case ']': //end link
break; //fixme... skip the keys, recolour properly, etc
// txt+=2;
// continue;
case '&':
if ((ishex(txt[2])||txt[2]=='-') && (ishex(txt[3])||txt[3]=='-'))
{ //ignore fte's fore/back ansi colours
txt += 4;
continue;
}
break; //malformed
case 'm': //toggle masking.
txt+=2;
mask ^= 128;
continue;
case 'U': //ucs-2 unicode codepoint
if (ishex(txt[2]) && ishex(txt[3]) && ishex(txt[4]) && ishex(txt[5]))
{
c = (dehex(txt[2])<<12) | (dehex(txt[3])<<8) | (dehex(txt[4])<<4) | dehex(txt[5]);
txt += 6-1;
if (c >= 0xe000 && c <= 0xe0ff)
c &= 0xff; //private-use 0xE0XX maps to quake's chars
else if (c >= 0x20 && c <= 0x7f)
c &= 0x7f; //ascii is okay too.
else
c = '?'; //otherwise its some unicode char that we don't know how to handle.
break;
}
break; //malformed
case '{': //full unicode codepoint, for chars up to 0x10ffff
txt += 2;
c = 0; //no idea
while(*txt)
{
if (*txt == '}')
{
txt++;
break;
}
if (!ishex(*txt))
break;
c<<=4;
c |= dehex(*txt++);
}
txt--; // for the ++ below
if (c >= 0xe000 && c <= 0xe0ff)
c &= 0xff; //private-use 0xE0XX maps to quake's chars
else if (c >= 0x20 && c <= 0x7f)
c &= 0x7f; //ascii is okay too.
else
c = '?'; //otherwise its some unicode char that we don't know how to handle.
break;
}
}
if (c <= ' ')
{
boundary = true;
@ -489,6 +593,9 @@ void Con_Printf (const char *fmt, ...)
q_vsnprintf (msg, sizeof(msg), fmt, argptr);
va_end (argptr);
if (con_redirect_flush)
q_strlcat(con_redirect_buffer, msg, sizeof(con_redirect_buffer));
// also echo to debugging console
Sys_Printf ("%s", msg);
@ -533,8 +640,9 @@ void Con_DWarning (const char *fmt, ...)
va_list argptr;
char msg[MAXPRINTMSG];
if (!developer.value)
return; // don't confuse non-developers with techie stuff...
if (developer.value >= 2)
{ // don't confuse non-developers with techie stuff...
// (this is limit exceeded warnings)
va_start (argptr, fmt);
q_vsnprintf (msg, sizeof(msg), fmt, argptr);
@ -543,6 +651,7 @@ void Con_DWarning (const char *fmt, ...)
Con_SafePrintf ("\x02Warning: ");
Con_Printf ("%s", msg);
}
}
/*
================
@ -695,6 +804,18 @@ void Con_LogCenterPrint (const char *str)
}
}
qboolean Con_IsRedirected(void)
{
return !!con_redirect_flush;
}
void Con_Redirect(void(*flush)(const char *))
{
if (con_redirect_flush)
con_redirect_flush(con_redirect_buffer);
*con_redirect_buffer = 0;
con_redirect_flush = flush;
}
/*
==============================================================================
@ -717,12 +838,6 @@ tab_t *tablist;
//defs from elsewhere
extern qboolean keydown[256];
typedef struct cmd_function_s
{
struct cmd_function_s *next;
const char *name;
xcommand_t function;
} cmd_function_t;
extern cmd_function_t *cmd_functions;
#define MAX_ALIAS_NAME 32
typedef struct cmdalias_s
@ -763,7 +878,7 @@ void AddToTabList (const char *name, const char *type)
// find max common between bash_partial and name
i_bash = bash_partial;
i_name = name;
while (*i_bash && (*i_bash == *i_name))
while (*i_bash && (q_tolower(*i_bash) == q_tolower(*i_name)))
{
i_bash++;
i_name++;
@ -901,15 +1016,15 @@ void BuildTabList (const char *partial)
cvar = Cvar_FindVarAfter ("", CVAR_NONE);
for ( ; cvar ; cvar=cvar->next)
if (!Q_strncmp (partial, cvar->name, len))
if (!q_strncasecmp (partial, cvar->name, len))
AddToTabList (cvar->name, "cvar");
for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
if (!Q_strncmp (partial,cmd->name, len))
if (!q_strncasecmp (partial,cmd->name, len) && cmd->srctype != src_server)
AddToTabList (cmd->name, "command");
for (alias=cmd_alias ; alias ; alias=alias->next)
if (!Q_strncmp (partial, alias->name, len))
if (!q_strncasecmp (partial, alias->name, len))
AddToTabList (alias->name, "alias");
}
@ -1184,7 +1299,7 @@ void Con_DrawConsole (int lines, qboolean drawinput)
{
int i, x, y, j, sb, rows;
const char *text;
char ver[32];
const char *ver = ENGINE_NAME_AND_VER;
if (lines <= 0)
return;
@ -1227,7 +1342,6 @@ void Con_DrawConsole (int lines, qboolean drawinput)
//draw version number in bottom right
y += 8;
q_snprintf (ver, sizeof(ver), "QuakeSpasm " QUAKESPASM_VER_STRING);
for (x = 0; x < (int)strlen(ver); x++)
Draw_Character ((con_linewidth - strlen(ver) + x + 2)<<3, y, ver[x] /*+ 128*/);
}

View File

@ -48,6 +48,8 @@ void Con_SafePrintf (const char *fmt, ...) FUNC_PRINTF(1,2);
void Con_DrawNotify (void);
void Con_ClearNotify (void);
void Con_ToggleConsole_f (void);
qboolean Con_IsRedirected(void); //returns true if its redirected. this generally means that things are a little more verbose.
void Con_Redirect(void(*flush)(const char *text));
void Con_NotifyBox (const char *text); // during startup for sound / cd warnings

View File

@ -101,6 +101,40 @@ void Cvar_Inc_f (void)
}
}
/*
============
Cvar_Set_f -- spike
both set+seta commands
============
*/
void Cvar_Set_f (void)
{
//q2: set name value flags
//dp: set name value description
//fte: set name some freeform value with spaces or whatever //description
//to avoid politics, its easier to just stick with name+value only.
//that leaves someone else free to pick a standard for what to do with extra args.
const char *varname = Cmd_Argv(1);
const char *varvalue = Cmd_Argv(2);
cvar_t *var;
if (Cmd_Argc() < 3)
{
Con_Printf("%s <cvar> <value>\n", Cmd_Argv(0));
return;
}
if (Cmd_Argc() > 3)
{
Con_Warning("%s \"%s\" command with extra args\n", Cmd_Argv(0), varname);
return;
}
var = Cvar_Create(varname, varvalue);
Cvar_SetQuick(var, varvalue);
if (!strcmp(Cmd_Argv(0), "seta"))
var->flags |= CVAR_ARCHIVE|CVAR_SETA;
}
/*
============
Cvar_Toggle_f -- johnfitz
@ -232,6 +266,8 @@ void Cvar_Init (void)
Cmd_AddCommand ("reset", Cvar_Reset_f);
Cmd_AddCommand ("resetall", Cvar_ResetAll_f);
Cmd_AddCommand ("resetcfg", Cvar_ResetCfg_f);
Cmd_AddCommand ("set", Cvar_Set_f);
Cmd_AddCommand ("seta", Cvar_Set_f);
}
//==============================================================================
@ -426,6 +462,8 @@ void Cvar_SetQuick (cvar_t *var, const char *value)
if (var->callback)
var->callback (var);
if (var->flags & CVAR_AUTOCVAR)
PR_AutoCvarChanged(var);
}
void Cvar_SetValueQuick (cvar_t *var, const float value)
@ -588,6 +626,34 @@ void Cvar_RegisterVariable (cvar_t *variable)
variable->flags |= CVAR_ROM;
}
/*
============
Cvar_Create -- spike
Creates a cvar if it does not already exist, otherwise does nothing.
Must not be used until after all other cvars are registered.
Cvar will be persistent.
============
*/
cvar_t *Cvar_Create (const char *name, const char *value)
{
cvar_t *newvar;
newvar = Cvar_FindVar(name);
if (newvar)
return newvar; //already exists.
if (Cmd_Exists (name))
return NULL; //error! panic! oh noes!
newvar = Z_Malloc(sizeof(cvar_t) + strlen(name)+1);
newvar->name = (char*)(newvar+1);
strcpy((char*)(newvar+1), name);
newvar->flags = CVAR_USERDEFINED;
newvar->string = value;
Cvar_RegisterVariable(newvar);
return newvar;
}
/*
============
Cvar_SetCallback
@ -626,6 +692,9 @@ qboolean Cvar_Command (void)
return true;
}
if (Con_IsRedirected())
Con_Printf ("changing \"%s\" from \"%s\" to \"%s\"\n", v->name, v->string, Cmd_Argv(1));
Cvar_Set (v->name, Cmd_Argv(1));
return true;
}
@ -646,7 +715,11 @@ void Cvar_WriteVariables (FILE *f)
for (var = cvar_vars ; var ; var = var->next)
{
if (var->flags & CVAR_ARCHIVE)
{
if (var->flags & (CVAR_USERDEFINED|CVAR_SETA))
fprintf (f, "seta ");
fprintf (f, "%s \"%s\"\n", var->name, var->string);
}
}
}

View File

@ -73,6 +73,9 @@ interface from being ambiguous.
#define CVAR_LOCKED (1U << 8) // locked temporarily
#define CVAR_REGISTERED (1U << 10) // the var is added to the list of variables
#define CVAR_CALLBACK (1U << 16) // var has a callback
#define CVAR_USERDEFINED (1U << 17) // cvar was created by the user/mod, and needs to be saved a bit differently.
#define CVAR_AUTOCVAR (1U << 18) // cvar changes need to feed back to qc global changes.
#define CVAR_SETA (1U << 19) // cvar will be saved with seta.
typedef void (*cvarcallback_t) (struct cvar_s *);
@ -92,6 +95,9 @@ void Cvar_RegisterVariable (cvar_t *variable);
// registers a cvar that already has the name, string, and optionally
// the archive elements set.
cvar_t *Cvar_Create (const char *name, const char *value);
//creates+registers a cvar, otherwise just returns it.
void Cvar_SetCallback (cvar_t *var, cvarcallback_t func);
// set a callback function to the var

822
Quake/fs_zip.c Executable file
View File

@ -0,0 +1,822 @@
#include "quakedef.h"
#ifdef USE_ZLIB
#include <zlib.h>
#else
#pragma message("zips supported but with no zlib")
#endif
//ZIP features:
//zip64 for huge zips, except that quakespasm's filesystem calls are all otherwise 31bit.
//utf-8 encoding support (non-utf-8 is always ibm437)
//compression mode: store
//compression mode: deflate (via zlib)
//bigendian cpus. everything misaligned.
//NOT supported:
//symlink flag (files are ignored completely)
//compression mode: deflate64
//other compression modes
//split archives
//if central dir is crypted/compressed, the archive will fail to open
//if a file is crypted/compressed, the file will (internally) be marked as corrupt
//crc verification
//infozip utf-8 name override.
//other 'extra' fields.
#define qofs_t long long
#define qofs_Make(low,high) ((unsigned int)(low) | ((qofs_t)(high)<<32))
struct zipinfo
{
unsigned int thisdisk; //this disk number
unsigned int centraldir_startdisk; //central directory starts on this disk
qofs_t centraldir_numfiles_disk; //number of files in the centraldir on this disk
qofs_t centraldir_numfiles_all; //number of files in the centraldir on all disks
qofs_t centraldir_size; //total size of the centraldir
qofs_t centraldir_offset; //start offset of the centraldir with respect to the first disk that contains it
unsigned short commentlength; //length of the comment at the end of the archive.
unsigned int zip64_centraldirend_disk; //zip64 weirdness
qofs_t zip64_centraldirend_offset;
unsigned int zip64_diskcount;
qofs_t zip64_eocdsize;
unsigned short zip64_version_madeby;
unsigned short zip64_version_needed;
unsigned short centraldir_compressionmethod;
unsigned short centraldir_algid;
qofs_t centraldir_end;
qofs_t zipoffset;
};
struct zipcentralentry
{
unsigned char *fname;
qofs_t cesize;
unsigned int flags;
time_t mtime;
//PK12
unsigned short version_madeby;
unsigned short version_needed;
unsigned short gflags;
unsigned short cmethod;
unsigned short lastmodfiletime;
unsigned short lastmodfiledate;
unsigned int crc32;
qofs_t csize;
qofs_t usize;
unsigned short fnane_len;
unsigned short extra_len;
unsigned short comment_len;
unsigned int disknum;
unsigned short iattributes;
unsigned int eattributes;
qofs_t localheaderoffset; //from start of disk
};
struct ziplocalentry
{
//PK34
unsigned short version_needed;
unsigned short gpflags;
unsigned short cmethod;
unsigned short lastmodfiletime;
unsigned short lastmodfiledate;
unsigned int crc32;
qofs_t csize;
qofs_t usize;
unsigned short fname_len;
unsigned short extra_len;
};
#define LittleU2FromPtr(p) (unsigned int)((p)[0] | ((p)[1]<<8u))
#define LittleU4FromPtr(p) (unsigned int)((p)[0] | ((p)[1]<<8u) | ((p)[2]<<16u) | ((p)[3]<<24u))
#define LittleU8FromPtr(p) qofs_Make(LittleU4FromPtr(p), LittleU4FromPtr((p)+4))
#define SIZE_LOCALENTRY 30
#define SIZE_ENDOFCENTRALDIRECTORY 22
#define SIZE_ZIP64ENDOFCENTRALDIRECTORY_V1 56
#define SIZE_ZIP64ENDOFCENTRALDIRECTORY_V2 84
#define SIZE_ZIP64ENDOFCENTRALDIRECTORY SIZE_ZIP64ENDOFCENTRALDIRECTORY_V2
#define SIZE_ZIP64ENDOFCENTRALDIRECTORYLOCATOR 20
typedef struct
{
char name[MAX_QPATH];
qofs_t localpos;
qofs_t filelen;
unsigned int crc;
unsigned int flags;
// time_t time;
} zpackfile_t;
typedef struct
{
int raw;
qofs_t rawsize;
size_t numfiles;
zpackfile_t *files;
} zipfile_t;
#define ZFL_DEFLATED 1 //need to use zlib
#define ZFL_STORED 2 //direct access is okay
#define ZFL_SYMLINK 4 //file is a symlink
#define ZFL_CORRUPT 8 //file is corrupt or otherwise unreadable.
#define ZFL_WEAKENCRYPT 16 //traditional zip encryption
//for zip->quake charsets
unsigned short ibmtounicode[256] =
{
0x0000,0x263A,0x263B,0x2665,0x2666,0x2663,0x2660,0x2022,0x25D8,0x25CB,0x25D9,0x2642,0x2640,0x266A,0x266B,0x263C, //0x00(non-ascii, display-only)
0x25BA,0x25C4,0x2195,0x203C,0x00B6,0x00A7,0x25AC,0x21A8,0x2191,0x2193,0x2192,0x2190,0x221F,0x2194,0x25B2,0x25BC, //0x10(non-ascii, display-only)
0x0020,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0027,0x0028,0x0029,0x002A,0x002B,0x002C,0x002D,0x002E,0x002F, //0x20(ascii)
0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,0x0038,0x0039,0x003A,0x003B,0x003C,0x003D,0x003E,0x003F, //0x30(ascii)
0x0040,0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,0x0049,0x004A,0x004B,0x004C,0x004D,0x004E,0x004F, //0x40(ascii)
0x0050,0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,0x0059,0x005A,0x005B,0x005C,0x005D,0x005E,0x005F, //0x50(ascii)
0x0060,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,0x0067,0x0068,0x0069,0x006A,0x006B,0x006C,0x006D,0x006E,0x006F, //0x60(ascii)
0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007A,0x007B,0x007C,0x007D,0x007E,0x2302, //0x70(mostly ascii, one display-only)
0x00C7,0x00FC,0x00E9,0x00E2,0x00E4,0x00E0,0x00E5,0x00E7,0x00EA,0x00EB,0x00E8,0x00EF,0x00EE,0x00EC,0x00C4,0x00C5, //0x80(non-ascii, printable)
0x00C9,0x00E6,0x00C6,0x00F4,0x00F6,0x00F2,0x00FB,0x00F9,0x00FF,0x00D6,0x00DC,0x00A2,0x00A3,0x00A5,0x20A7,0x0192, //0x90(non-ascii, printable)
0x00E1,0x00ED,0x00F3,0x00FA,0x00F1,0x00D1,0x00AA,0x00BA,0x00BF,0x2310,0x00AC,0x00BD,0x00BC,0x00A1,0x00AB,0x00BB, //0xa0(non-ascii, printable)
0x2591,0x2592,0x2593,0x2502,0x2524,0x2561,0x2562,0x2556,0x2555,0x2563,0x2551,0x2557,0x255D,0x255C,0x255B,0x2510, //0xb0(box-drawing, printable)
0x2514,0x2534,0x252C,0x251C,0x2500,0x253C,0x255E,0x255F,0x255A,0x2554,0x2569,0x2566,0x2560,0x2550,0x256C,0x2567, //0xc0(box-drawing, printable)
0x2568,0x2564,0x2565,0x2559,0x2558,0x2552,0x2553,0x256B,0x256A,0x2518,0x250C,0x2588,0x2584,0x258C,0x2590,0x2580, //0xd0(box-drawing, printable)
0x03B1,0x00DF,0x0393,0x03C0,0x03A3,0x03C3,0x00B5,0x03C4,0x03A6,0x0398,0x03A9,0x03B4,0x221E,0x03C6,0x03B5,0x2229, //0xe0(maths(greek), printable)
0x2261,0x00B1,0x2265,0x2264,0x2320,0x2321,0x00F7,0x2248,0x00B0,0x2219,0x00B7,0x221A,0x207F,0x00B2,0x25A0,0x00A0, //0xf0(maths, printable)
};
static qboolean FSZIP_FindEndCentralDirectory(zipfile_t *zip, struct zipinfo *info)
{
qboolean result = false;
//zip comment is capped to 65k or so, so we can use a single buffer for this
byte traildata[0x10000 + SIZE_ENDOFCENTRALDIRECTORY+SIZE_ZIP64ENDOFCENTRALDIRECTORYLOCATOR];
byte *magic;
unsigned int trailsize = 0x10000 + SIZE_ENDOFCENTRALDIRECTORY+SIZE_ZIP64ENDOFCENTRALDIRECTORYLOCATOR;
if ((qofs_t)trailsize > zip->rawsize)
trailsize = zip->rawsize;
//FIXME: do in a loop to avoid a huge block of stack use
Sys_FileSeek(zip->raw, zip->rawsize - trailsize);
Sys_FileRead(zip->raw, traildata, trailsize);
memset(info, 0, sizeof(*info));
for (magic = traildata+trailsize-SIZE_ENDOFCENTRALDIRECTORY; magic >= traildata; magic--)
{
if (magic[0] == 'P' &&
magic[1] == 'K' &&
magic[2] == 5 &&
magic[3] == 6)
{
info->centraldir_end = (zip->rawsize-trailsize)+(magic-traildata);
info->thisdisk = LittleU2FromPtr(magic+4);
info->centraldir_startdisk = LittleU2FromPtr(magic+6);
info->centraldir_numfiles_disk = LittleU2FromPtr(magic+8);
info->centraldir_numfiles_all = LittleU2FromPtr(magic+10);
info->centraldir_size = LittleU4FromPtr(magic+12);
info->centraldir_offset = LittleU4FromPtr(magic+16);
info->commentlength = LittleU2FromPtr(magic+20);
result = true;
break;
}
}
if (!result)
Con_Printf("zip: unable to find end-of-central-directory\n");
else
//now look for a zip64 header.
//this gives more larger archives, more files, larger files, more spanned disks.
//note that the central directory itself is the same, it just has a couple of extra attributes on files that need them.
for (magic -= SIZE_ZIP64ENDOFCENTRALDIRECTORYLOCATOR; magic >= traildata; magic--)
{
if (magic[0] == 'P' &&
magic[1] == 'K' &&
magic[2] == 6 &&
magic[3] == 7)
{
byte z64eocd[SIZE_ZIP64ENDOFCENTRALDIRECTORY];
info->zip64_centraldirend_disk = LittleU4FromPtr(magic+4);
info->zip64_centraldirend_offset = LittleU8FromPtr(magic+8);
info->zip64_diskcount = LittleU4FromPtr(magic+16);
if (info->zip64_diskcount != 1 || info->zip64_centraldirend_disk != 0)
{
Con_Printf("zip: archive is spanned\n");
return false;
}
Sys_FileSeek(zip->raw, info->zip64_centraldirend_offset);
Sys_FileRead(zip->raw, z64eocd, sizeof(z64eocd));
if (z64eocd[0] == 'P' &&
z64eocd[1] == 'K' &&
z64eocd[2] == 6 &&
z64eocd[3] == 6)
{
info->zip64_eocdsize = LittleU8FromPtr(z64eocd+4) + 12;
info->zip64_version_madeby = LittleU2FromPtr(z64eocd+12);
info->zip64_version_needed = LittleU2FromPtr(z64eocd+14);
info->thisdisk = LittleU4FromPtr(z64eocd+16);
info->centraldir_startdisk = LittleU4FromPtr(z64eocd+20);
info->centraldir_numfiles_disk = LittleU8FromPtr(z64eocd+24);
info->centraldir_numfiles_all = LittleU8FromPtr(z64eocd+32);
info->centraldir_size = LittleU8FromPtr(z64eocd+40);
info->centraldir_offset = LittleU8FromPtr(z64eocd+48);
if (info->zip64_eocdsize >= 84)
{
info->centraldir_compressionmethod = LittleU2FromPtr(z64eocd+56);
// info->zip64_2_centraldir_csize = LittleU8FromPtr(z64eocd+58);
// info->zip64_2_centraldir_usize = LittleU8FromPtr(z64eocd+66);
info->centraldir_algid = LittleU2FromPtr(z64eocd+74);
// info.zip64_2_bitlen = LittleU2FromPtr(z64eocd+76);
// info->zip64_2_flags = LittleU2FromPtr(z64eocd+78);
// info->zip64_2_hashid = LittleU2FromPtr(z64eocd+80);
// info->zip64_2_hashlength = LittleU2FromPtr(z64eocd+82);
//info->zip64_2_hashdata = LittleUXFromPtr(z64eocd+84, info->zip64_2_hashlength);
}
}
else
{
Con_Printf("zip: zip64 end-of-central directory at unknown offset.\n");
result = false;
}
break;
}
}
if (info->thisdisk || info->centraldir_startdisk || info->centraldir_numfiles_disk != info->centraldir_numfiles_all)
{
Con_Printf("zip: archive is spanned\n");
result = false;
}
if (info->centraldir_compressionmethod || info->centraldir_algid)
{
Con_Printf("zip: encrypted centraldir\n");
result = false;
}
return result;
}
static qboolean FSZIP_ReadCentralEntry(zipfile_t *zip, byte *data, struct zipcentralentry *entry)
{
entry->flags = 0;
entry->fname = (unsigned char*)"";
entry->fnane_len = 0;
if (data[0] != 'P' ||
data[1] != 'K' ||
data[2] != 1 ||
data[3] != 2)
return false; //verify the signature
//if we read too much, we'll catch it after.
entry->version_madeby = LittleU2FromPtr(data+4);
entry->version_needed = LittleU2FromPtr(data+6);
entry->gflags = LittleU2FromPtr(data+8);
entry->cmethod = LittleU2FromPtr(data+10);
entry->lastmodfiletime = LittleU2FromPtr(data+12);
entry->lastmodfiledate = LittleU2FromPtr(data+12);
entry->crc32 = LittleU4FromPtr(data+16);
entry->csize = LittleU4FromPtr(data+20);
entry->usize = LittleU4FromPtr(data+24);
entry->fnane_len = LittleU2FromPtr(data+28);
entry->extra_len = LittleU2FromPtr(data+30);
entry->comment_len = LittleU2FromPtr(data+32);
entry->disknum = LittleU2FromPtr(data+34);
entry->iattributes = LittleU2FromPtr(data+36);
entry->eattributes = LittleU4FromPtr(data+38);
entry->localheaderoffset = LittleU4FromPtr(data+42);
entry->cesize = 46;
//mark the filename position
entry->fname = data+entry->cesize;
entry->cesize += entry->fnane_len;
entry->mtime = 0;
//parse extra
if (entry->extra_len)
{
byte *extra = data + entry->cesize;
byte *extraend = extra + entry->extra_len;
unsigned short extrachunk_tag;
unsigned short extrachunk_len;
while(extra+4 < extraend)
{
extrachunk_tag = LittleU2FromPtr(extra+0);
extrachunk_len = LittleU2FromPtr(extra+2);
if (extra + extrachunk_len > extraend)
break; //error
extra += 4;
switch(extrachunk_tag)
{
case 1: //zip64 extended information extra field. the attributes are only present if the reegular file info is nulled out with a -1
if (entry->usize == 0xffffffffu)
{
entry->usize = LittleU8FromPtr(extra);
extra += 8;
}
if (entry->csize == 0xffffffffu)
{
entry->csize = LittleU8FromPtr(extra);
extra += 8;
}
if (entry->localheaderoffset == 0xffffffffu)
{
entry->localheaderoffset = LittleU8FromPtr(extra);
extra += 8;
}
if (entry->disknum == 0xffffu)
{
entry->disknum = LittleU4FromPtr(extra);
extra += 4;
}
break;
#if !defined(_MSC_VER) || _MSC_VER > 1200
case 0x000a: //NTFS extra field
//0+4: reserved
//4+2: subtag(must be 1, for times)
//6+2: subtagsize(times: must be == 8*3+4)
//8+8: mtime
//16+8: atime
//24+8: ctime
if (extrachunk_len >= 32 && LittleU2FromPtr(extra+4) == 1 && LittleU2FromPtr(extra+6) == 8*3)
entry->mtime = LittleU8FromPtr(extra+8) / 10000000ULL - 11644473600ULL;
else
Con_Printf("zip: unsupported ntfs subchunk %x\n", extrachunk_tag);
extra += extrachunk_len;
break;
#endif
case 0x5455:
if (extra[0] & 1)
entry->mtime = LittleU4FromPtr(extra+1);
//access and creation do NOT exist in the central header.
extra += extrachunk_len;
break;
default:
/* Con_Printf("Unknown chunk %x\n", extrachunk_tag);
case 0x5455: //extended timestamp
case 0x7875: //unix uid/gid
case 0x9901: //aes crypto
*/ extra += extrachunk_len;
break;
}
}
entry->cesize += entry->extra_len;
}
//parse comment
entry->cesize += entry->comment_len;
//check symlink flags
{
byte madeby = entry->version_madeby>>8; //system
//vms, unix, or beos file attributes includes a symlink attribute.
//symlinks mean the file contents is just the name of another file.
if (madeby == 2 || madeby == 3 || madeby == 16)
{
unsigned short unixattr = entry->eattributes>>16;
if ((unixattr & 0xF000) == 0xA000)//fa&S_IFMT==S_IFLNK
entry->flags |= ZFL_SYMLINK;
else if ((unixattr & 0xA000) == 0xA000)//fa&S_IFMT==S_IFLNK
entry->flags |= ZFL_SYMLINK;
}
}
if (entry->gflags & (1u<<0)) //encrypted
{
#ifdef ZIPCRYPT
entry->flags |= ZFL_WEAKENCRYPT;
#else
entry->flags |= ZFL_CORRUPT;
#endif
}
if (entry->gflags & (1u<<5)) //is patch data
entry->flags |= ZFL_CORRUPT;
else if (entry->gflags & (1u<<6)) //strong encryption
entry->flags |= ZFL_CORRUPT;
else if (entry->gflags & (1u<<13)) //strong encryption
entry->flags |= ZFL_CORRUPT;
else if (entry->cmethod == 0)
entry->flags |= ZFL_STORED;
//1: shrink
//2-5: reduce
//6: implode
//7: tokenize
else if (entry->cmethod == 8)
entry->flags |= ZFL_DEFLATED;
//8: deflate64 - patented. sometimes written by microsoft's crap, so this might be problematic. only minor improvements.
//10: implode
//12: bzip2
// else if (entry->cmethod == 12)
// entry->flags |= ZFL_BZIP2;
// else if (entry->cmethod == 14)
// entry->flags |= ZFL_LZMA;
//19: lz77
//97: wavpack
//98: ppmd
else
entry->flags |= ZFL_CORRUPT; //unsupported compression method.
if ((entry->flags & ZFL_WEAKENCRYPT) && !(entry->flags & ZFL_DEFLATED))
entry->flags |= ZFL_CORRUPT; //only support decryption with deflate.
return true;
}
static qboolean FSZIP_EnumerateCentralDirectory(zipfile_t *zip, struct zipinfo *info, const char *prefix)
{
qboolean success = false;
zpackfile_t *f;
struct zipcentralentry entry;
qofs_t ofs = 0;
unsigned int i;
//lazily read the entire thing.
byte *centraldir = malloc(info->centraldir_size);
if (centraldir)
{
Sys_FileSeek(zip->raw, info->centraldir_offset+info->zipoffset);
if ((qofs_t)Sys_FileRead(zip->raw, centraldir, info->centraldir_size) == info->centraldir_size)
{
zip->numfiles = info->centraldir_numfiles_disk;
zip->files = f = Z_Malloc (zip->numfiles * sizeof(*f));
for (i = 0; i < zip->numfiles; i++)
{
if (!FSZIP_ReadCentralEntry(zip, centraldir+ofs, &entry) || ofs + entry.cesize > info->centraldir_size)
break;
f->crc = entry.crc32;
//copy out the filename and lowercase it
if (entry.gflags & (1u<<11))
{ //already utf-8 encoding
if (entry.fnane_len > sizeof(f->name)-1)
entry.fnane_len = sizeof(f->name)-1;
memcpy(f->name, entry.fname, entry.fnane_len);
f->name[entry.fnane_len] = 0;
}
else
{ //legacy charset
int i;
int nlen = 0;
int cl;
for (i = 0; i < entry.fnane_len; i++)
{
#if 1
cl = ibmtounicode[entry.fname[i]];
if (cl > 127)
f->name[nlen] = '?'; //we can't encode non-ascii chars
else
f->name[nlen] = cl;
cl = 1;
#else
cl = utf8_encode(f->name+nlen, ibmtounicode[entry.fname[i]], sizeof(f->name)-1 - nlen);
#endif
if (!cl) //overflowed, truncate cleanly.
break;
nlen += cl;
}
f->name[nlen] = 0;
}
if (prefix && *prefix)
{
if (!strcmp(prefix, ".."))
{
char *c;
for (c = f->name; *c; )
{
if (*c++ == '/')
break;
}
memmove(f->name, c, strlen(c)+1);
}
else
{
size_t prelen = strlen(prefix);
size_t oldlen = strlen(f->name);
if (prelen+1+oldlen+1 > sizeof(f->name))
*f->name = 0;
else
{
memmove(f->name+prelen+1, f->name, oldlen);
f->name[prelen] = '/';
memmove(f->name, prefix, prelen);
}
}
}
q_strlwr(f->name);
f->filelen = *f->name?entry.usize:0;
f->localpos = entry.localheaderoffset+info->zipoffset;
f->flags = entry.flags;
// f->mtime = entry.mtime;
ofs += entry.cesize;
f++;
}
success = i == zip->numfiles;
if (!success)
{
free(zip->files);
zip->files = NULL;
zip->numfiles = 0;
}
}
}
free(centraldir);
return success;
}
static qboolean FSZIP_ValidateLocalHeader(zipfile_t *zip, zpackfile_t *zfile, qofs_t *datastart, qofs_t *datasize)
{
struct ziplocalentry local;
byte localdata[SIZE_LOCALENTRY];
qofs_t localstart = zfile->localpos;
Sys_FileSeek(zip->raw, localstart);
Sys_FileRead(zip->raw, localdata, sizeof(localdata));
//make sure we found the right sort of table.
if (localdata[0] != 'P' ||
localdata[1] != 'K' ||
localdata[2] != 3 ||
localdata[3] != 4)
return false;
local.version_needed = LittleU2FromPtr(localdata+4);
local.gpflags = LittleU2FromPtr(localdata+6);
local.cmethod = LittleU2FromPtr(localdata+8);
local.lastmodfiletime = LittleU2FromPtr(localdata+10);
local.lastmodfiledate = LittleU2FromPtr(localdata+12);
local.crc32 = LittleU4FromPtr(localdata+14);
local.csize = LittleU4FromPtr(localdata+18);
local.usize = LittleU4FromPtr(localdata+22);
local.fname_len = LittleU2FromPtr(localdata+26);
local.extra_len = LittleU2FromPtr(localdata+28);
localstart += SIZE_LOCALENTRY;
localstart += local.fname_len;
//parse extra
if (local.usize == 0xffffffffu || local.csize == 0xffffffffu) //don't bother otherwise.
if (local.extra_len)
{
byte extradata[65536];
byte *extra = extradata;
byte *extraend = extradata + local.extra_len;
unsigned short extrachunk_tag;
unsigned short extrachunk_len;
Sys_FileSeek(zip->raw, localstart);
Sys_FileRead(zip->raw, extradata, sizeof(extradata));
while(extra+4 < extraend)
{
extrachunk_tag = LittleU2FromPtr(extra+0);
extrachunk_len = LittleU2FromPtr(extra+2);
if (extra + extrachunk_len > extraend)
break; //error
extra += 4;
switch(extrachunk_tag)
{
case 1: //zip64 extended information extra field. the attributes are only present if the reegular file info is nulled out with a -1
if (local.usize == 0xffffffffu)
{
local.usize = LittleU8FromPtr(extra);
extra += 8;
}
if (local.csize == 0xffffffffu)
{
local.csize = LittleU8FromPtr(extra);
extra += 8;
}
break;
default:
/* Con_Printf("Unknown chunk %x\n", extrachunk_tag);
case 0x000a: //NTFS (timestamps)
case 0x5455: //extended timestamp
case 0x7875: //unix uid/gid
*/ extra += extrachunk_len;
break;
}
}
}
localstart += local.extra_len;
*datastart = localstart; //this is the end of the local block, and the start of the data block (well, actually, should be encryption, but we don't support that).
*datasize = local.csize;
if (local.gpflags & (1u<<3))
{
//crc, usize, and csize were not known at the time the file was compressed.
//there is a 'data descriptor' after the file data, but to parse that we would need to decompress the file.
//instead, just depend upon upon the central directory and don't bother checking.
}
else
{
if (local.crc32 != zfile->crc)
return false;
if (local.usize != zfile->filelen)
return false;
}
//FIXME: with pure paths, we still don't bother checking the crc (again, would require decompressing the entire file in advance).
if (local.cmethod == 0)
return (zfile->flags & (ZFL_STORED|ZFL_CORRUPT|ZFL_DEFLATED)) == ZFL_STORED;
if (local.cmethod == 8)
return (zfile->flags & (ZFL_STORED|ZFL_CORRUPT|ZFL_DEFLATED)) == ZFL_DEFLATED;
return false; //some other method that we don't know.
}
pack_t *FSZIP_LoadArchive (const char *packfile)
{
size_t i;
packfile_t *newfiles;
int numpackfiles;
pack_t *pack;
zipfile_t zip;
struct zipinfo info;
#ifndef USE_ZLIB
qboolean zlibneeded = false;
#endif
zip.rawsize = Sys_FileOpenRead(packfile, &zip.raw);
if (zip.raw < 0)
return NULL;
//try to find the header
if (!FSZIP_FindEndCentralDirectory(&zip, &info))
{
Sys_FileClose(zip.raw);
return NULL;
}
//now try to read it.
if (!FSZIP_EnumerateCentralDirectory(&zip, &info, "") && !info.zip64_diskcount)
{
//uh oh... the central directory wasn't where it was meant to be!
//assuming that the endofcentraldir is packed at the true end of the centraldir (and that we're not zip64 and thus don't have an extra block), then we can guess based upon the offset difference
info.zipoffset = info.centraldir_end - (info.centraldir_offset+info.centraldir_size);
if (!FSZIP_EnumerateCentralDirectory(&zip, &info, ""))
{
Sys_FileClose(zip.raw);
Con_Printf ("zipfile \"%s\" appears to be missing its central directory\n", packfile);
return NULL;
}
}
//lame zone.
//copy the files into something compatible with quake's pak support.
//ignore compressed / corrupt / unusable files
pack = (pack_t *) Z_Malloc (sizeof (pack_t));
q_strlcpy (pack->filename, packfile, sizeof(pack->filename));
pack->handle = zip.raw;
numpackfiles = 0;
newfiles = NULL;
newfiles = Z_Malloc(sizeof(*newfiles) * zip.numfiles);
for (numpackfiles = 0, i = 0; i < zip.numfiles; i++)
{
qofs_t startpos, datasize;
zpackfile_t *zp = &zip.files[i];
if (zp->flags & ZFL_CORRUPT)
continue; //we can't cope with this.
if (zp->flags & ZFL_SYMLINK)
continue; //file data is just a filename
if (!FSZIP_ValidateLocalHeader(&zip, zp, &startpos, &datasize))
continue; //local header is corrupt
if (zp->flags & ZFL_DEFLATED)
{
#ifndef USE_ZLIB
zlibneeded = true;
continue;
#endif
}
else if (zp->flags & ZFL_STORED)
{
if (datasize != zp->filelen)
continue;
datasize = 0;
}
else
continue;
//usable file.
memcpy(newfiles[numpackfiles].name, zp->name, MAX_QPATH-1);
newfiles[numpackfiles].name[MAX_QPATH-1] = 0;
newfiles[numpackfiles].filelen = zp->filelen;
newfiles[numpackfiles].filepos = startpos;
newfiles[numpackfiles].deflatedsize = datasize;
numpackfiles++;
}
pack->numfiles = numpackfiles;
pack->files = newfiles;
//we don't need this stuff now.
Z_Free(zip.files);
#ifndef USE_ZLIB
if (zlibneeded)
Con_Printf ("zipfile \"%s\" contains compressed files, but zlib was disabled at compile time.\n", packfile);
#endif
//Sys_Printf ("Added packfile %s (%i files)\n", packfile, numpackfiles);
return pack;
}
FILE *FSZIP_Deflate(FILE *src, int srcsize, int outsize)
{
#ifdef USE_ZLIB
byte inbuffer[65536];
byte outbuffer[65536];
z_stream strm;
int ret;
FILE *of;
#ifdef _WIN32
/*warning: annother app might manage to open the file before we can. if the file is not opened exclusively then we can end up with issues
on windows, fopen is typically exclusive anyway, but not on unix. but on unix, tmpfile is actually usable, so special-case the windows code and hope that its never an issue
tmpfile isn't usable in windows. it creates the file in the root dir and requires admin rights, which is stupid.
*/
char *fname = _tempnam(NULL, "ftemp");
of = fopen(fname, "w+bD");
#else
of = tmpfile();
#endif
if (!of)
{
fclose(src);
return NULL;
}
memset(&strm, 0, sizeof(strm));
strm.data_type = Z_UNKNOWN;
inflateInit2(&strm, -MAX_WBITS);
while ((ret=inflate(&strm, Z_SYNC_FLUSH)) != Z_STREAM_END)
{
if (strm.avail_in == 0 || strm.avail_out == 0)
{
if (strm.avail_in == 0)
{
strm.avail_in = fread(inbuffer, 1, sizeof(inbuffer), src);
strm.next_in = inbuffer;
if (!strm.avail_in)
break;
}
if (strm.avail_out == 0)
{
strm.next_out = outbuffer;
fwrite(outbuffer, 1, strm.total_out, of);
strm.total_out = 0;
strm.avail_out = sizeof(outbuffer);
}
continue;
}
//doh, it terminated for no reason
if (ret != Z_STREAM_END)
{
inflateEnd(&strm);
fclose(src);
fclose(of);
Con_Printf("Couldn't decompress file\n");
return NULL;
}
}
fwrite(outbuffer, 1, strm.total_out, of);
inflateEnd(&strm);
fclose(src);
fseek(of, SEEK_SET, 0);
return of;
#else
fclose(src);
return NULL;
#endif
}

View File

@ -27,6 +27,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//extern unsigned char d_15to8table[65536]; //johnfitz -- never used
qboolean premul_hud = false;//true;
cvar_t scr_conalpha = {"scr_conalpha", "0.5", CVAR_ARCHIVE}; //johnfitz
qpic_t *draw_disc;
@ -202,7 +203,7 @@ void Scrap_Upload (void)
{
sprintf (name, "scrap%i", i);
scrap_textures[i] = TexMgr_LoadImage (NULL, name, BLOCK_WIDTH, BLOCK_HEIGHT, SRC_INDEXED, scrap_texels[i],
"", (src_offset_t)scrap_texels[i], TEXPREF_ALPHA | TEXPREF_OVERWRITE | TEXPREF_NOPICMIP);
"", (src_offset_t)scrap_texels[i], (premul_hud?TEXPREF_PREMULTIPLY:0)|TEXPREF_ALPHA | TEXPREF_OVERWRITE | TEXPREF_NOPICMIP);
}
scrap_dirty = false;
@ -252,7 +253,7 @@ qpic_t *Draw_PicFromWad (const char *name)
offset = (src_offset_t)p - (src_offset_t)wad_base + sizeof(int)*2; //johnfitz
gl.gltexture = TexMgr_LoadImage (NULL, texturename, p->width, p->height, SRC_INDEXED, p->data, WADFILENAME,
offset, TEXPREF_ALPHA | TEXPREF_PAD | TEXPREF_NOPICMIP); //johnfitz -- TexMgr
offset, (premul_hud?TEXPREF_PREMULTIPLY:0)|TEXPREF_ALPHA | TEXPREF_PAD | TEXPREF_NOPICMIP); //johnfitz -- TexMgr
gl.sl = 0;
gl.sh = (float)p->width/(float)TexMgr_PadConditional(p->width); //johnfitz
gl.tl = 0;
@ -304,7 +305,7 @@ qpic_t *Draw_CachePic (const char *path)
pic->pic.height = dat->height;
gl.gltexture = TexMgr_LoadImage (NULL, path, dat->width, dat->height, SRC_INDEXED, dat->data, path,
sizeof(int)*2, TEXPREF_ALPHA | TEXPREF_PAD | TEXPREF_NOPICMIP); //johnfitz -- TexMgr
sizeof(int)*2, (premul_hud?TEXPREF_PREMULTIPLY:0)|TEXPREF_ALPHA | TEXPREF_PAD | TEXPREF_NOPICMIP); //johnfitz -- TexMgr
gl.sl = 0;
gl.sh = (float)dat->width/(float)TexMgr_PadConditional(dat->width); //johnfitz
gl.tl = 0;
@ -359,7 +360,7 @@ void Draw_LoadPics (void)
if (!data) Sys_Error ("Draw_LoadPics: couldn't load conchars");
offset = (src_offset_t)data - (src_offset_t)wad_base;
char_texture = TexMgr_LoadImage (NULL, WADFILENAME":conchars", 128, 128, SRC_INDEXED, data,
WADFILENAME, offset, TEXPREF_ALPHA | TEXPREF_NEAREST | TEXPREF_NOPICMIP | TEXPREF_CONCHARS);
WADFILENAME, offset, (premul_hud?TEXPREF_PREMULTIPLY:0)|TEXPREF_ALPHA | TEXPREF_NEAREST | TEXPREF_NOPICMIP | TEXPREF_CONCHARS);
draw_disc = Draw_PicFromWad ("disc");
draw_backtile = Draw_PicFromWad ("backtile");
@ -566,20 +567,29 @@ void Draw_ConsoleBackground (void)
if (alpha > 0.0)
{
if (alpha < 1.0)
{
if (premul_hud)
glColor4f (alpha,alpha,alpha,alpha);
else
{
glEnable (GL_BLEND);
glColor4f (1,1,1,alpha);
glDisable (GL_ALPHA_TEST);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glColor4f (1,1,1,alpha);
}
}
Draw_Pic (0, 0, pic);
if (alpha < 1.0)
{
if (!premul_hud)
{
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glEnable (GL_ALPHA_TEST);
glDisable (GL_BLEND);
}
glColor4f (1,1,1,1);
}
}
@ -766,7 +776,17 @@ void GL_Set2D (void)
glDisable (GL_DEPTH_TEST);
glDisable (GL_CULL_FACE);
if (premul_hud)
{
glEnable (GL_BLEND);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
}
else
{
glDisable (GL_BLEND);
glEnable (GL_ALPHA_TEST);
}
glColor4f (1,1,1,1);
}

View File

@ -375,6 +375,23 @@ void Fog_NewMap (void)
Fog_MarkModels (); //for volumetric fog
}
/*
=============
Fog_NewMap
so fog is preserved when starting a demo recording
=============
*/
const char *Fog_GetFogCommand(void)
{
if (fade_done)
{ //if this mod is using dynamic fog, make sure we start with the right values.
//don't bother with this if we got fog from a clientside worldspawn key.
return va("\nfog %g %g %g %g\n", fog_density, fog_red, fog_green, fog_blue);
}
return NULL;
}
/*
=============
Fog_Init

View File

@ -32,333 +32,11 @@ ALIAS MODEL DISPLAY LIST GENERATION
=================================================================
*/
qmodel_t *aliasmodel;
aliashdr_t *paliashdr;
int used[8192]; // qboolean
// the command list holds counts and s/t values that are valid for
// every frame
int commands[8192];
int numcommands;
// all frames will have their vertexes rearranged and expanded
// so they are in the order expected by the command list
int vertexorder[8192];
int numorder;
int allverts, alltris;
int stripverts[128];
int striptris[128];
int stripcount;
/*
================
StripLength
================
*/
int StripLength (int starttri, int startv)
{
int m1, m2;
int j;
mtriangle_t *last, *check;
int k;
used[starttri] = 2;
last = &triangles[starttri];
stripverts[0] = last->vertindex[(startv)%3];
stripverts[1] = last->vertindex[(startv+1)%3];
stripverts[2] = last->vertindex[(startv+2)%3];
striptris[0] = starttri;
stripcount = 1;
m1 = last->vertindex[(startv+2)%3];
m2 = last->vertindex[(startv+1)%3];
// look for a matching triangle
nexttri:
for (j=starttri+1, check=&triangles[starttri+1] ; j<pheader->numtris ; j++, check++)
{
if (check->facesfront != last->facesfront)
continue;
for (k=0 ; k<3 ; k++)
{
if (check->vertindex[k] != m1)
continue;
if (check->vertindex[ (k+1)%3 ] != m2)
continue;
// this is the next part of the fan
// if we can't use this triangle, this tristrip is done
if (used[j])
goto done;
// the new edge
if (stripcount & 1)
m2 = check->vertindex[ (k+2)%3 ];
else
m1 = check->vertindex[ (k+2)%3 ];
stripverts[stripcount+2] = check->vertindex[ (k+2)%3 ];
striptris[stripcount] = j;
stripcount++;
used[j] = 2;
goto nexttri;
}
}
done:
// clear the temp used flags
for (j=starttri+1 ; j<pheader->numtris ; j++)
if (used[j] == 2)
used[j] = 0;
return stripcount;
}
/*
===========
FanLength
===========
*/
int FanLength (int starttri, int startv)
{
int m1, m2;
int j;
mtriangle_t *last, *check;
int k;
used[starttri] = 2;
last = &triangles[starttri];
stripverts[0] = last->vertindex[(startv)%3];
stripverts[1] = last->vertindex[(startv+1)%3];
stripverts[2] = last->vertindex[(startv+2)%3];
striptris[0] = starttri;
stripcount = 1;
m1 = last->vertindex[(startv+0)%3];
m2 = last->vertindex[(startv+2)%3];
// look for a matching triangle
nexttri:
for (j=starttri+1, check=&triangles[starttri+1] ; j<pheader->numtris ; j++, check++)
{
if (check->facesfront != last->facesfront)
continue;
for (k=0 ; k<3 ; k++)
{
if (check->vertindex[k] != m1)
continue;
if (check->vertindex[ (k+1)%3 ] != m2)
continue;
// this is the next part of the fan
// if we can't use this triangle, this tristrip is done
if (used[j])
goto done;
// the new edge
m2 = check->vertindex[ (k+2)%3 ];
stripverts[stripcount+2] = m2;
striptris[stripcount] = j;
stripcount++;
used[j] = 2;
goto nexttri;
}
}
done:
// clear the temp used flags
for (j=starttri+1 ; j<pheader->numtris ; j++)
if (used[j] == 2)
used[j] = 0;
return stripcount;
}
/*
================
BuildTris
Generate a list of trifans or strips
for the model, which holds for all frames
================
*/
void BuildTris (void)
{
int i, j, k;
int startv;
float s, t;
int len, bestlen, besttype;
int bestverts[1024];
int besttris[1024];
int type;
//
// build tristrips
//
numorder = 0;
numcommands = 0;
memset (used, 0, sizeof(used));
for (i = 0; i < pheader->numtris; i++)
{
// pick an unused triangle and start the trifan
if (used[i])
continue;
bestlen = 0;
besttype = 0;
for (type = 0 ; type < 2 ; type++)
// type = 1;
{
for (startv = 0; startv < 3; startv++)
{
if (type == 1)
len = StripLength (i, startv);
else
len = FanLength (i, startv);
if (len > bestlen)
{
besttype = type;
bestlen = len;
for (j = 0; j < bestlen+2; j++)
bestverts[j] = stripverts[j];
for (j = 0; j < bestlen; j++)
besttris[j] = striptris[j];
}
}
}
// mark the tris on the best strip as used
for (j = 0; j < bestlen; j++)
used[besttris[j]] = 1;
if (besttype == 1)
commands[numcommands++] = (bestlen+2);
else
commands[numcommands++] = -(bestlen+2);
for (j = 0; j < bestlen+2; j++)
{
int tmp;
// emit a vertex into the reorder buffer
k = bestverts[j];
vertexorder[numorder++] = k;
// emit s/t coords into the commands stream
s = stverts[k].s;
t = stverts[k].t;
if (!triangles[besttris[0]].facesfront && stverts[k].onseam)
s += pheader->skinwidth / 2; // on back side
s = (s + 0.5) / pheader->skinwidth;
t = (t + 0.5) / pheader->skinheight;
// *(float *)&commands[numcommands++] = s;
// *(float *)&commands[numcommands++] = t;
// NOTE: 4 == sizeof(int)
// == sizeof(float)
memcpy (&tmp, &s, 4);
commands[numcommands++] = tmp;
memcpy (&tmp, &t, 4);
commands[numcommands++] = tmp;
}
}
commands[numcommands++] = 0; // end of list marker
Con_DPrintf2 ("%3i tri %3i vert %3i cmd\n", pheader->numtris, numorder, numcommands);
allverts += numorder;
alltris += pheader->numtris;
}
static void GL_MakeAliasModelDisplayLists_VBO (void);
static void GLMesh_LoadVertexBuffer (qmodel_t *m, const aliashdr_t *hdr);
#define countof(x) (sizeof(x)/sizeof((x)[0]))
/*
================
GL_MakeAliasModelDisplayLists
================
*/
void GL_MakeAliasModelDisplayLists (qmodel_t *m, aliashdr_t *hdr)
{
int i, j;
int *cmds;
trivertx_t *verts;
float hscale, vscale; //johnfitz -- padded skins
int count; //johnfitz -- precompute texcoords for padded skins
int *loadcmds; //johnfitz
//johnfitz -- padded skins
hscale = (float)hdr->skinwidth/(float)TexMgr_PadConditional(hdr->skinwidth);
vscale = (float)hdr->skinheight/(float)TexMgr_PadConditional(hdr->skinheight);
//johnfitz
aliasmodel = m;
paliashdr = hdr; // (aliashdr_t *)Mod_Extradata (m);
//johnfitz -- generate meshes
Con_DPrintf2 ("meshing %s...\n",m->name);
BuildTris ();
// save the data out
paliashdr->poseverts = numorder;
cmds = (int *) Hunk_Alloc (numcommands * 4);
paliashdr->commands = (byte *)cmds - (byte *)paliashdr;
//johnfitz -- precompute texcoords for padded skins
loadcmds = commands;
while(1)
{
*cmds++ = count = *loadcmds++;
if (!count)
break;
if (count < 0)
count = -count;
do
{
*(float *)cmds++ = hscale * (*(float *)loadcmds++);
*(float *)cmds++ = vscale * (*(float *)loadcmds++);
} while (--count);
}
//johnfitz
verts = (trivertx_t *) Hunk_Alloc (paliashdr->numposes * paliashdr->poseverts * sizeof(trivertx_t));
paliashdr->posedata = (byte *)verts - (byte *)paliashdr;
for (i=0 ; i<paliashdr->numposes ; i++)
for (j=0 ; j<numorder ; j++)
*verts++ = poseverts[i][vertexorder[j]];
// ericw
GL_MakeAliasModelDisplayLists_VBO ();
}
unsigned int r_meshindexbuffer = 0;
unsigned int r_meshvertexbuffer = 0;
/*
================
GL_MakeAliasModelDisplayLists_VBO
Saves data needed to build the VBO for this model on the hunk. Afterwards this
is copied to Mod_Extradata.
@ -366,37 +44,28 @@ is copied to Mod_Extradata.
Original code by MH from RMQEngine
================
*/
void GL_MakeAliasModelDisplayLists_VBO (void)
void GL_MakeAliasModelDisplayLists (qmodel_t *m, aliashdr_t *paliashdr)
{
int i, j;
int maxverts_vbo;
trivertx_t *verts;
unsigned short *indexes;
trivertx_t *verts;
aliasmesh_t *desc;
if (!gl_glsl_alias_able)
return;
// first, copy the verts onto the hunk
verts = (trivertx_t *) Hunk_Alloc (paliashdr->numposes * paliashdr->numverts * sizeof(trivertx_t));
paliashdr->vertexes = (byte *)verts - (byte *)paliashdr;
for (i=0 ; i<paliashdr->numposes ; i++)
for (j=0 ; j<paliashdr->numverts ; j++)
verts[i*paliashdr->numverts + j] = poseverts[i][j];
// there can never be more than this number of verts and we just put them all on the hunk
maxverts_vbo = pheader->numtris * 3;
// front/back logic says we can never have more than numverts*2
maxverts_vbo = paliashdr->numverts * 2;
desc = (aliasmesh_t *) Hunk_Alloc (sizeof (aliasmesh_t) * maxverts_vbo);
// there will always be this number of indexes
indexes = (unsigned short *) Hunk_Alloc (sizeof (unsigned short) * maxverts_vbo);
indexes = (unsigned short *) Hunk_Alloc (sizeof (unsigned short) * paliashdr->numtris * 3);
pheader->indexes = (intptr_t) indexes - (intptr_t) pheader;
pheader->meshdesc = (intptr_t) desc - (intptr_t) pheader;
pheader->numindexes = 0;
pheader->numverts_vbo = 0;
paliashdr->indexes = (intptr_t) indexes - (intptr_t) paliashdr;
paliashdr->meshdesc = (intptr_t) desc - (intptr_t) paliashdr;
paliashdr->numindexes = 0;
paliashdr->numverts_vbo = 0;
for (i = 0; i < pheader->numtris; i++)
for (i = 0; i < paliashdr->numtris; i++)
{
for (j = 0; j < 3; j++)
{
@ -410,36 +79,39 @@ void GL_MakeAliasModelDisplayLists_VBO (void)
int t = stverts[vertindex].t;
// check for back side and adjust texcoord s
if (!triangles[i].facesfront && stverts[vertindex].onseam) s += pheader->skinwidth / 2;
if (!triangles[i].facesfront && stverts[vertindex].onseam) s += paliashdr->skinwidth / 2;
// see does this vert already exist
for (v = 0; v < pheader->numverts_vbo; v++)
for (v = 0; v < paliashdr->numverts_vbo; v++)
{
// it could use the same xyz but have different s and t
if (desc[v].vertindex == vertindex && (int) desc[v].st[0] == s && (int) desc[v].st[1] == t)
{
// exists; emit an index for it
indexes[pheader->numindexes++] = v;
indexes[paliashdr->numindexes++] = v;
// no need to check any more
break;
}
}
if (v == pheader->numverts_vbo)
if (v == paliashdr->numverts_vbo)
{
// doesn't exist; emit a new vert and index
indexes[pheader->numindexes++] = pheader->numverts_vbo;
indexes[paliashdr->numindexes++] = paliashdr->numverts_vbo;
desc[pheader->numverts_vbo].vertindex = vertindex;
desc[pheader->numverts_vbo].st[0] = s;
desc[pheader->numverts_vbo++].st[1] = t;
desc[paliashdr->numverts_vbo].vertindex = vertindex;
desc[paliashdr->numverts_vbo].st[0] = s;
desc[paliashdr->numverts_vbo++].st[1] = t;
}
}
}
// upload immediately
GLMesh_LoadVertexBuffer (aliasmodel, pheader);
verts = (trivertx_t *) Hunk_Alloc (paliashdr->numposes * paliashdr->numverts_vbo * sizeof(*verts));
paliashdr->vertexes = (byte *)verts - (byte *)paliashdr;
for (i=0 ; i<paliashdr->numposes ; i++)
for (j=0 ; j<paliashdr->numverts_vbo ; j++)
verts[i*paliashdr->numverts_vbo + j] = poseverts_mdl[i][desc[j].vertindex];
}
#define NUMVERTEXNORMALS 162
@ -452,79 +124,137 @@ GLMesh_LoadVertexBuffer
Upload the given alias model's mesh to a VBO
Original code by MH from RMQEngine
may update the mesh vbo/ebo offsets.
================
*/
static void GLMesh_LoadVertexBuffer (qmodel_t *m, const aliashdr_t *hdr)
void GLMesh_LoadVertexBuffer (qmodel_t *m, aliashdr_t *mainhdr)
{
//we always need vertex array data.
//if we don't support vbos(gles?) then we just use system memory.
//if we're not using glsl(gles1?), then we don't actually need all the data, but we do still need some so its easier to just alloc the lot.
int totalvbosize = 0;
const aliasmesh_t *desc;
const short *indexes;
const trivertx_t *trivertexes;
const void *trivertexes;
byte *ebodata;
byte *vbodata;
int f;
aliashdr_t *hdr;
unsigned int numindexes, numverts;
intptr_t stofs;
intptr_t vertofs;
if (!gl_glsl_alias_able)
return;
//count how much space we're going to need.
for(hdr = mainhdr, numverts = 0, numindexes = 0; ; )
{
if (hdr->posevertssize == 1)
totalvbosize += (hdr->numposes * hdr->numverts_vbo * sizeof (meshxyz_mdl_t)); // ericw -- what RMQEngine called nummeshframes is called numposes in QuakeSpasm
else if (hdr->posevertssize == 2)
totalvbosize += (hdr->numposes * hdr->numverts_vbo * sizeof (meshxyz_md3_t)); // ericw -- what RMQEngine called nummeshframes is called numposes in QuakeSpasm
// count the sizes we need
numverts += hdr->numverts_vbo;
numindexes += hdr->numindexes;
// ericw -- RMQEngine stored these vbo*ofs values in aliashdr_t, but we must not
// mutate Mod_Extradata since it might be reloaded from disk, so I moved them to qmodel_t
// (test case: roman1.bsp from arwop, 64mb heap)
m->vboindexofs = 0;
if (hdr->nextsurface)
hdr = (aliashdr_t*)((byte*)hdr + hdr->nextsurface);
else
break;
}
hdr = NULL;
m->vboxyzofs = 0;
totalvbosize += (hdr->numposes * hdr->numverts_vbo * sizeof (meshxyz_t)); // ericw -- what RMQEngine called nummeshframes is called numposes in QuakeSpasm
vertofs = 0;
totalvbosize = (totalvbosize+7)&~7; //align it.
stofs = totalvbosize;
totalvbosize += (numverts * sizeof (meshst_t));
m->vbostofs = totalvbosize;
totalvbosize += (hdr->numverts_vbo * sizeof (meshst_t));
if (!hdr->numindexes) return;
if (!totalvbosize) return;
if (!numindexes) return;
// grab the pointers to data in the extradata
desc = (aliasmesh_t *) ((byte *) hdr + hdr->meshdesc);
indexes = (short *) ((byte *) hdr + hdr->indexes);
trivertexes = (trivertx_t *) ((byte *)hdr + hdr->vertexes);
// upload indices buffer
GL_DeleteBuffersFunc (1, &m->meshindexesvbo);
GL_GenBuffersFunc (1, &m->meshindexesvbo);
GL_BindBufferFunc (GL_ELEMENT_ARRAY_BUFFER, m->meshindexesvbo);
GL_BufferDataFunc (GL_ELEMENT_ARRAY_BUFFER, hdr->numindexes * sizeof (unsigned short), indexes, GL_STATIC_DRAW);
//create an elements buffer
ebodata = (byte *) malloc(numindexes * sizeof(unsigned short));
if (!ebodata)
return; //fatal
// create the vertex buffer (empty)
vbodata = (byte *) malloc(totalvbosize);
if (!vbodata)
{ //fatal
free(ebodata);
return;
}
memset(vbodata, 0, totalvbosize);
numindexes = 0;
for(hdr = mainhdr, numverts = 0, numindexes = 0; ; )
{
// grab the pointers to data in the extradata
desc = (aliasmesh_t *) ((byte *) hdr + hdr->meshdesc);
trivertexes = (void *) ((byte *)hdr + hdr->vertexes);
//submit the index data.
hdr->eboofs = numindexes * sizeof (unsigned short);
numindexes += hdr->numindexes;
memcpy(ebodata + hdr->eboofs, (short *) ((byte *) hdr + hdr->indexes), hdr->numindexes * sizeof (unsigned short));
hdr->vbovertofs = vertofs;
// fill in the vertices at the start of the buffer
if (hdr->posevertssize == 1)
{
for (f = 0; f < hdr->numposes; f++) // ericw -- what RMQEngine called nummeshframes is called numposes in QuakeSpasm
{
int v;
meshxyz_t *xyz = (meshxyz_t *) (vbodata + (f * hdr->numverts_vbo * sizeof (meshxyz_t)));
const trivertx_t *tv = trivertexes + (hdr->numverts * f);
meshxyz_mdl_t *xyz = (meshxyz_mdl_t *) (vbodata + vertofs);
const trivertx_t *tv = (trivertx_t*)trivertexes + (hdr->numverts_vbo * f);
vertofs += hdr->numverts_vbo * sizeof (*xyz);
for (v = 0; v < hdr->numverts_vbo; v++)
for (v = 0; v < hdr->numverts_vbo; v++, tv++)
{
trivertx_t trivert = tv[desc[v].vertindex];
xyz[v].xyz[0] = trivert.v[0];
xyz[v].xyz[1] = trivert.v[1];
xyz[v].xyz[2] = trivert.v[2];
xyz[v].xyz[0] = tv->v[0];
xyz[v].xyz[1] = tv->v[1];
xyz[v].xyz[2] = tv->v[2];
xyz[v].xyz[3] = 1; // need w 1 for 4 byte vertex compression
// map the normal coordinates in [-1..1] to [-127..127] and store in an unsigned char.
// this introduces some error (less than 0.004), but the normals were very coarse
// to begin with
xyz[v].normal[0] = 127 * r_avertexnormals[trivert.lightnormalindex][0];
xyz[v].normal[1] = 127 * r_avertexnormals[trivert.lightnormalindex][1];
xyz[v].normal[2] = 127 * r_avertexnormals[trivert.lightnormalindex][2];
xyz[v].normal[0] = 127 * r_avertexnormals[tv->lightnormalindex][0];
xyz[v].normal[1] = 127 * r_avertexnormals[tv->lightnormalindex][1];
xyz[v].normal[2] = 127 * r_avertexnormals[tv->lightnormalindex][2];
xyz[v].normal[3] = 0; // unused; for 4-byte alignment
}
}
}
else if (hdr->posevertssize == 2)
{
for (f = 0; f < hdr->numposes; f++) // ericw -- what RMQEngine called nummeshframes is called numposes in QuakeSpasm
{
int v;
meshxyz_md3_t *xyz = (meshxyz_md3_t *) (vbodata + vertofs);
const md3XyzNormal_t *tv = (md3XyzNormal_t*)trivertexes + (hdr->numverts_vbo * f);
float lat,lng;
vertofs += hdr->numverts_vbo * sizeof (*xyz);
for (v = 0; v < hdr->numverts_vbo; v++, tv++)
{
xyz[v].xyz[0] = tv->xyz[0];
xyz[v].xyz[1] = tv->xyz[1];
xyz[v].xyz[2] = tv->xyz[2];
xyz[v].xyz[3] = 1; // need w 1 for 4 byte vertex compression
// map the normal coordinates in [-1..1] to [-127..127] and store in an unsigned char.
// this introduces some error (less than 0.004), but the normals were very coarse
// to begin with
lat = (float)tv->latlong[0] * (2 * M_PI)*(1.0 / 255.0);
lng = (float)tv->latlong[1] * (2 * M_PI)*(1.0 / 255.0);
xyz[v].normal[0] = 127 * cos ( lng ) * sin ( lat );
xyz[v].normal[1] = 127 * sin ( lng ) * sin ( lat );
xyz[v].normal[2] = 127 * cos ( lat );
xyz[v].normal[3] = 0; // unused; for 4-byte alignment
}
}
}
// fill in the ST coords at the end of the buffer
{
@ -536,13 +266,41 @@ static void GLMesh_LoadVertexBuffer (qmodel_t *m, const aliashdr_t *hdr)
vscale = (float)hdr->skinheight/(float)TexMgr_PadConditional(hdr->skinheight);
//johnfitz
st = (meshst_t *) (vbodata + m->vbostofs);
hdr->vbostofs = stofs;
st = (meshst_t *) (vbodata + stofs);
stofs += hdr->numverts_vbo*sizeof(*st);
if (hdr->posevertssize == 2)
{
for (f = 0; f < hdr->numverts_vbo; f++)
{ //md3 has floating-point skin coords. use the values directly.
st[f].st[0] = hscale * desc[f].st[0];
st[f].st[1] = vscale * desc[f].st[1];
}
}
else
{
for (f = 0; f < hdr->numverts_vbo; f++)
{
st[f].st[0] = hscale * ((float) desc[f].st[0] + 0.5f) / (float) hdr->skinwidth;
st[f].st[1] = vscale * ((float) desc[f].st[1] + 0.5f) / (float) hdr->skinheight;
}
}
}
if (hdr->nextsurface)
hdr = (aliashdr_t*)((byte*)hdr + hdr->nextsurface);
else
break;
}
hdr = NULL;
if (gl_vbo_able)
{
// upload indexes buffer
GL_DeleteBuffersFunc (1, &m->meshindexesvbo);
GL_GenBuffersFunc (1, &m->meshindexesvbo);
GL_BindBufferFunc (GL_ELEMENT_ARRAY_BUFFER, m->meshindexesvbo);
GL_BufferDataFunc (GL_ELEMENT_ARRAY_BUFFER, numindexes * sizeof (unsigned short), ebodata, GL_STATIC_DRAW);
// upload vertexes buffer
GL_DeleteBuffersFunc (1, &m->meshvbo);
@ -551,6 +309,16 @@ static void GLMesh_LoadVertexBuffer (qmodel_t *m, const aliashdr_t *hdr)
GL_BufferDataFunc (GL_ARRAY_BUFFER, totalvbosize, vbodata, GL_STATIC_DRAW);
free (vbodata);
free (ebodata);
m->meshvboptr = NULL;
m->meshindexesvboptr = NULL;
}
else
{
m->meshvboptr = vbodata;
m->meshindexesvboptr = ebodata;
}
// invalidate the cached bindings
GL_ClearBufferBindings ();
@ -567,17 +335,14 @@ void GLMesh_LoadVertexBuffers (void)
{
int j;
qmodel_t *m;
const aliashdr_t *hdr;
if (!gl_glsl_alias_able)
return;
aliashdr_t *hdr;
for (j = 1; j < MAX_MODELS; j++)
{
if (!(m = cl.model_precache[j])) break;
if (m->type != mod_alias) continue;
hdr = (const aliashdr_t *) Mod_Extradata (m);
hdr = (aliashdr_t *) Mod_Extradata (m);
GLMesh_LoadVertexBuffer (m, hdr);
}
@ -595,7 +360,7 @@ void GLMesh_DeleteVertexBuffers (void)
int j;
qmodel_t *m;
if (!gl_glsl_alias_able)
if (!gl_vbo_able)
return;
for (j = 1; j < MAX_MODELS; j++)
@ -603,12 +368,282 @@ void GLMesh_DeleteVertexBuffers (void)
if (!(m = cl.model_precache[j])) break;
if (m->type != mod_alias) continue;
if (m->meshvbo)
GL_DeleteBuffersFunc (1, &m->meshvbo);
m->meshvbo = 0;
free(m->meshvboptr);
m->meshvboptr = NULL;
if (m->meshindexesvbo)
GL_DeleteBuffersFunc (1, &m->meshindexesvbo);
m->meshindexesvbo = 0;
free(m->meshindexesvboptr);
m->meshindexesvboptr = NULL;
}
GL_ClearBufferBindings ();
}
//from gl_model.c
extern char loadname[]; // for hunk tags
void Mod_CalcAliasBounds (aliashdr_t *a);
#define MD3_VERSION 15
//structures from Tenebrae
typedef struct {
int ident;
int version;
char name[64];
int flags; //assumed to match quake1 models, for lack of somewhere better.
int numFrames;
int numTags;
int numSurfaces;
int numSkins;
int ofsFrames;
int ofsTags;
int ofsSurfaces;
int ofsEnd;
} md3Header_t;
//then has header->numFrames of these at header->ofs_Frames
typedef struct md3Frame_s {
vec3_t bounds[2];
vec3_t localOrigin;
float radius;
char name[16];
} md3Frame_t;
//there are header->numSurfaces of these at header->ofsSurfaces, following from ofsEnd
typedef struct {
int ident; //
char name[64]; // polyset name
int flags;
int numFrames; // all surfaces in a model should have the same
int numShaders; // all surfaces in a model should have the same
int numVerts;
int numTriangles;
int ofsTriangles;
int ofsShaders; // offset from start of md3Surface_t
int ofsSt; // texture coords are common for all frames
int ofsXyzNormals; // numVerts * numFrames
int ofsEnd; // next surface follows
} md3Surface_t;
//at surf+surf->ofsXyzNormals
/*typedef struct {
short xyz[3];
byte latlong[2];
} md3XyzNormal_t;*/
//surf->numTriangles at surf+surf->ofsTriangles
typedef struct {
int indexes[3];
} md3Triangle_t;
//surf->numVerts at surf+surf->ofsSt
typedef struct {
float s;
float t;
} md3St_t;
typedef struct {
char name[64];
int shaderIndex;
} md3Shader_t;
void Mod_LoadMD3Model (qmodel_t *mod, void *buffer)
{
md3Header_t *pinheader;
md3Surface_t *pinsurface;
md3Frame_t *pinframes;
md3Triangle_t *pintriangle;
unsigned short *poutindexes;
md3XyzNormal_t *pinvert;
md3XyzNormal_t *poutvert;
md3St_t *pinst;
aliasmesh_t *poutst;
md3Shader_t *pinshader;
int size;
int start, end, total;
int ival, j;
int numsurfs, surf;
int numframes;
aliashdr_t *outhdr;
start = Hunk_LowMark ();
pinheader = (md3Header_t *)buffer;
ival = LittleLong (pinheader->version);
if (ival != MD3_VERSION)
Sys_Error ("%s has wrong version number (%i should be %i)",
mod->name, ival, MD3_VERSION);
numsurfs = LittleLong (pinheader->numSurfaces);
numframes = LittleLong(pinheader->numFrames);
if (numframes > MAXALIASFRAMES)
Sys_Error ("%s has too many frames (%i vs %i)",
mod->name, numframes, MAXALIASFRAMES);
if (!numsurfs)
Sys_Error ("%s has nosurfaces", mod->name);
pinframes = (md3Frame_t*)((byte*)buffer + LittleLong(pinheader->ofsFrames));
//
// allocate space for a working header, plus all the data except the frames,
// skin and group info
//
size = sizeof(aliashdr_t) + (numframes-1) * sizeof (outhdr->frames[0]);
outhdr = (aliashdr_t *) Hunk_AllocName (size * numsurfs, loadname);
for (surf = 0, pinsurface = (md3Surface_t*)((byte*)buffer + LittleLong(pinheader->ofsSurfaces)); surf < numsurfs; surf++, pinsurface = (md3Surface_t*)((byte*)pinsurface + LittleLong(pinsurface->ofsEnd)))
{
aliashdr_t *osurf = (aliashdr_t*)((byte*)outhdr + size*surf);
if (LittleLong(pinsurface->ident) != (('I'<<0)|('D'<<8)|('P'<<16)|('3'<<24)))
Sys_Error ("%s corrupt surface ident", mod->name);
if (LittleLong(pinsurface->numFrames) != numframes)
Sys_Error ("%s mismatched framecounts", mod->name);
if (surf+1 < numsurfs)
osurf->nextsurface = size;
else
osurf->nextsurface = 0;
osurf->posevertssize = 2;
osurf->numverts_vbo = osurf->numverts = LittleLong(pinsurface->numVerts);
pinvert = (md3XyzNormal_t*)((byte*)pinsurface + LittleLong(pinsurface->ofsXyzNormals));
poutvert = (md3XyzNormal_t *) Hunk_Alloc (numframes * osurf->numverts * sizeof(*poutvert));
osurf->vertexes = (byte *)poutvert - (byte *)osurf;
for (ival = 0; ival < numframes; ival++)
{
osurf->frames[ival].firstpose = ival;
osurf->frames[ival].numposes = 1;
osurf->frames[ival].interval = 0.1;
osurf->frames[ival].frame = ival;
q_strlcpy(osurf->frames[ival].name, pinframes->name, sizeof(osurf->frames[ival].name));
for (j = 0; j < 3; j++)
{ //fixme...
osurf->frames[ival].bboxmin.v[j] = 0;
osurf->frames[ival].bboxmax.v[j] = 255;
}
for (j=0 ; j<osurf->numverts ; j++)
poutvert[j] = pinvert[j];
poutvert += osurf->numverts;
pinvert += osurf->numverts;
}
osurf->numposes = osurf->numframes = numframes;
osurf->numtris = LittleLong(pinsurface->numTriangles);
osurf->numindexes = osurf->numtris*3;
pintriangle = (md3Triangle_t*)((byte*)pinsurface + LittleLong(pinsurface->ofsTriangles));
poutindexes = (unsigned short *) Hunk_Alloc (sizeof (*poutindexes) * osurf->numindexes);
osurf->indexes = (intptr_t) poutindexes - (intptr_t) osurf;
for (ival = 0; ival < osurf->numtris; ival++, pintriangle++, poutindexes+=3)
{
for (j = 0; j < 3; j++)
poutindexes[j] = LittleLong(pintriangle->indexes[j]);
}
for (j = 0; j < 3; j++)
{
osurf->scale_origin[j] = 0;
osurf->scale[j] = 1/64.0;
}
//guess at skin sizes
osurf->skinwidth = 320;
osurf->skinheight = 200;
//load the textures
pinshader = (md3Shader_t*)((byte*)pinsurface + LittleLong(pinsurface->ofsShaders));
osurf->numskins = LittleLong(pinsurface->numShaders);
for (j = 0; j < osurf->numskins; j++, pinshader++)
{
char texturename[MAX_QPATH];
char fullbrightname[MAX_QPATH];
char *ext;
//texture names in md3s are kinda fucked. they could be just names relative to the mdl, or full paths, or just simple shader names.
//our texture manager is too lame to scan all 1000 possibilities
if (strchr(pinshader->name, '/') || strchr(pinshader->name, '\\'))
{ //so if there's a path then we want to use that.
q_strlcpy(texturename, pinshader->name, sizeof(texturename));
}
else
{ //and if there's no path then we want to prefix it with our own.
q_strlcpy(texturename, mod->name, sizeof(texturename));
*(char*)COM_SkipPath(texturename) = 0;
//and concat the specified name
q_strlcat(texturename, pinshader->name, sizeof(texturename));
}
//and make sure there's no extensions. these get ignored in q3, which is kinda annoying, but this is an md3 and standards are standards (and it makes luma easier).
ext = (char*)COM_FileGetExtension(texturename);
if (*ext)
*--ext = 0;
//luma has an extra postfix.
q_snprintf(fullbrightname, sizeof(fullbrightname), "%s_luma", texturename);
osurf->gltextures[j][0] = TexMgr_LoadImage(mod, texturename, osurf->skinwidth, osurf->skinheight, SRC_EXTERNAL, NULL, texturename, 0, TEXPREF_PAD|TEXPREF_ALPHA|TEXPREF_NOBRIGHT|TEXPREF_MIPMAP);
osurf->fbtextures[j][0] = TexMgr_LoadImage(mod, fullbrightname, osurf->skinwidth, osurf->skinheight, SRC_EXTERNAL, NULL, texturename, 0, TEXPREF_PAD|TEXPREF_ALPHA|TEXPREF_FULLBRIGHT|TEXPREF_MIPMAP);
osurf->gltextures[j][3] = osurf->gltextures[j][2] = osurf->gltextures[j][1] = osurf->gltextures[j][0];
osurf->fbtextures[j][3] = osurf->fbtextures[j][2] = osurf->fbtextures[j][1] = osurf->fbtextures[j][0];
}
if (osurf->numskins)
{
osurf->skinwidth = osurf->gltextures[0][0]->source_width;
osurf->skinheight = osurf->gltextures[0][0]->source_height;
}
//and figure out the texture coords properly, now we know the actual sizes.
pinst = (md3St_t*)((byte*)pinsurface + LittleLong(pinsurface->ofsSt));
poutst = (aliasmesh_t *) Hunk_Alloc (sizeof (*poutst) * osurf->numverts);
osurf->meshdesc = (intptr_t) poutst - (intptr_t) osurf;
for (j = 0; j < osurf->numverts; j++)
{
poutst[j].vertindex = j; //how is this useful?
poutst[j].st[0] = pinst->s;
poutst[j].st[1] = pinst->t;
}
}
GLMesh_LoadVertexBuffer (mod, outhdr);
//small violation of the spec, but it seems like noone else uses it.
mod->flags = LittleLong (pinheader->flags);
mod->type = mod_alias;
Mod_CalcAliasBounds (outhdr); //johnfitz
//
// move the complete, relocatable alias model to the cache
//
end = Hunk_LowMark ();
total = end - start;
Cache_Alloc (&mod->cache, total, loadname);
if (!mod->cache.data)
return;
memcpy (mod->cache.data, outhdr, total);
Hunk_FreeToLowMark (start);
}

View File

@ -32,9 +32,11 @@ char loadname[32]; // for hunk tags
void Mod_LoadSpriteModel (qmodel_t *mod, void *buffer);
void Mod_LoadBrushModel (qmodel_t *mod, void *buffer);
void Mod_LoadAliasModel (qmodel_t *mod, void *buffer);
void Mod_LoadMD3Model (qmodel_t *mod, void *buffer);
qmodel_t *Mod_LoadModel (qmodel_t *mod, qboolean crash);
cvar_t external_ents = {"external_ents", "1", CVAR_ARCHIVE};
cvar_t gl_load24bit = {"gl_load24bit", "1", CVAR_ARCHIVE};
static byte *mod_novis;
static int mod_novis_capacity;
@ -42,7 +44,7 @@ static int mod_novis_capacity;
static byte *mod_decompressed;
static int mod_decompressed_capacity;
#define MAX_MOD_KNOWN 2048 /*johnfitz -- was 512 */
#define MAX_MOD_KNOWN 8192 /*spike -- new value, was 2048 in qs, 512 in vanilla. Needs to be big for big maps with many many inline models. */
qmodel_t mod_known[MAX_MOD_KNOWN];
int mod_numknown;
@ -58,6 +60,7 @@ void Mod_Init (void)
{
Cvar_RegisterVariable (&gl_subdivide_size);
Cvar_RegisterVariable (&external_ents);
Cvar_RegisterVariable (&gl_load24bit);
//johnfitz -- create notexture miptex
r_notexture_mip = (texture_t *) Hunk_AllocName (sizeof(texture_t), "r_notexture_mip");
@ -166,6 +169,8 @@ byte *Mod_DecompressVis (byte *in, qmodel_t *model)
c = in[1];
in += 2;
if (c > row - (out - mod_decompressed))
c = row - (out - mod_decompressed); //now that we're dynamically allocating pvs buffers, we have to be more careful to avoid heap overflows with buggy maps.
while (c)
{
if (out == outend)
@ -223,6 +228,7 @@ void Mod_ClearAll (void)
{
mod->needload = true;
TexMgr_FreeTexturesForOwner (mod); //johnfitz
PScript_ClearSurfaceParticles(mod);
}
}
@ -237,7 +243,10 @@ void Mod_ResetAll (void)
for (i=0 , mod=mod_known ; i<mod_numknown ; i++, mod++)
{
if (!mod->needload) //otherwise Mod_ClearAll() did it already
{
TexMgr_FreeTexturesForOwner (mod);
PScript_ClearSurfaceParticles(mod);
}
memset(mod, 0, sizeof(qmodel_t));
}
mod_numknown = 0;
@ -319,23 +328,29 @@ qmodel_t *Mod_LoadModel (qmodel_t *mod, qboolean crash)
return mod; // not cached at all
}
//
// because the world is so huge, load it one piece at a time
//
if (!crash)
{
}
//
// load the file
//
if (*mod->name == '*')
buf = NULL;
else
buf = COM_LoadStackFile (mod->name, stackbuf, sizeof(stackbuf), & mod->path_id);
if (!buf)
{
if (crash)
Host_Error ("Mod_LoadModel: %s not found", mod->name); //johnfitz -- was "Mod_NumForName"
return NULL;
else if (mod->name[0] == '*' && (mod->name[1] < '0' || mod->name[1] > '9'))
; //*foo doesn't warn, unless its *NUM. inline models. gah.
else
Con_Warning("Mod_LoadModel: %s not found\n", mod->name);
//avoid crashes
mod->needload = false;
mod->type = mod_ext_invalid;
mod->flags = 0;
Mod_SetExtraFlags (mod); //johnfitz. spike -- moved this to be generic, because most of the flags are anyway.
return mod;
}
//
@ -363,11 +378,52 @@ qmodel_t *Mod_LoadModel (qmodel_t *mod, qboolean crash)
Mod_LoadSpriteModel (mod, buf);
break;
//Spike -- md3 support
case (('I'<<0)+('D'<<8)+('P'<<16)+('3'<<24)): //md3
Mod_LoadMD3Model(mod, buf);
break;
//Spike -- added checks for a few other model types.
//this is useful because of the number of models with renamed extensions.
//that and its hard to test the extension stuff when this was crashing.
case (('R'<<0)+('A'<<8)+('P'<<16)+('O'<<24)): //h2mp
Con_Warning("%s is a hexen2-missionpack model (unsupported)\n", mod->name);
mod->type = mod_ext_invalid;
break;
case (('I'<<0)+('D'<<8)+('P'<<16)+('2'<<24)): //md2
Con_Warning("%s is an md2 (unsupported)\n", mod->name);
mod->type = mod_ext_invalid;
break;
case (('I'<<0)+('N'<<8)+('T'<<16)+('E'<<24)): //iqm
Con_Warning("%s is an iqm (unsupported)\n", mod->name);
mod->type = mod_ext_invalid;
break;
case (('D'<<0)+('A'<<8)+('R'<<16)+('K'<<24)): //dpm
Con_Warning("%s is an dpm (unsupported)\n", mod->name);
mod->type = mod_ext_invalid;
break;
case (('A'<<0)+('C'<<8)+('T'<<16)+('R'<<24)): //psk
Con_Warning("%s is a psk (unsupported)\n", mod->name);
mod->type = mod_ext_invalid;
break;
case (('I'<<0)+('B'<<8)+('S'<<16)+('P'<<24)): //q2/q3bsp
Con_Warning("%s is a q2/q3bsp (unsupported)\n", mod->name);
mod->type = mod_ext_invalid;
break;
default:
Mod_LoadBrushModel (mod, buf);
break;
}
if (crash && mod->type == mod_ext_invalid)
{ //any of those formats for a world map will be screwed up.
Sys_Error ("Mod_LoadModel: couldn't load %s", mod->name); //johnfitz -- was "Mod_NumForName"
return NULL;
}
Mod_SetExtraFlags (mod); //johnfitz. spike -- moved this to be generic, because most of the flags are anyway.
return mod;
}
@ -396,7 +452,82 @@ qmodel_t *Mod_ForName (const char *name, qboolean crash)
===============================================================================
*/
byte *mod_base;
static byte *mod_base;
typedef struct {
char lumpname[24]; // up to 23 chars, zero-padded
int fileofs; // from file start
int filelen;
} bspx_lump_t;
typedef struct {
char id[4]; // 'BSPX'
int numlumps;
bspx_lump_t lumps[1];
} bspx_header_t;
static char *bspxbase;
static bspx_header_t *bspxheader;
//supported lumps:
//RGBLIGHTING (.lit)
//unsupported lumps ('documented' elsewhere):
//BRUSHLIST (because hulls suck)
//LIGHTINGDIR (.lux)
//LMSHIFT (.lit2)
//LMOFFSET (.lit2)
//LMSTYLE (.lit2)
static void *Q1BSPX_FindLump(char *lumpname, int *lumpsize)
{
int i;
*lumpsize = 0;
if (!bspxheader)
return NULL;
for (i = 0; i < bspxheader->numlumps; i++)
{
if (!strncmp(bspxheader->lumps[i].lumpname, lumpname, 24))
{
*lumpsize = bspxheader->lumps[i].filelen;
return bspxbase + bspxheader->lumps[i].fileofs;
}
}
return NULL;
}
static void Q1BSPX_Setup(qmodel_t *mod, char *filebase, unsigned int filelen, lump_t *lumps, int numlumps)
{
int i;
int offs = 0;
bspx_header_t *h;
bspxbase = filebase;
bspxheader = NULL;
for (i = 0; i < numlumps; i++, lumps++)
{
if (offs < lumps->fileofs + lumps->filelen)
offs = lumps->fileofs + lumps->filelen;
}
offs = (offs + 3) & ~3;
if (offs + sizeof(*bspxheader) > filelen)
return; /*no space for it*/
h = (bspx_header_t*)(filebase + offs);
i = LittleLong(h->numlumps);
/*verify the header*/
if (!strncmp(h->id, "BSPX", 4) ||
i < 0 ||
offs + sizeof(*h) + sizeof(h->lumps[0])*(i-1) > filelen)
return;
h->numlumps = i;
while(i-->0)
{
h->lumps[i].fileofs = LittleLong(h->lumps[i].fileofs);
h->lumps[i].filelen = LittleLong(h->lumps[i].filelen);
if ((unsigned int)h->lumps[i].fileofs + (unsigned int)h->lumps[i].filelen > filelen)
return;
}
bspxheader = h;
}
/*
=================
@ -434,6 +565,7 @@ void Mod_LoadTextures (lump_t *l)
byte *data;
extern byte *hunk_base;
//johnfitz
qboolean malloced; //spike
//johnfitz -- don't return early if no textures; still need to create dummy texture
if (!l->filelen)
@ -461,22 +593,34 @@ void Mod_LoadTextures (lump_t *l)
mt = (miptex_t *)((byte *)m + m->dataofs[i]);
mt->width = LittleLong (mt->width);
mt->height = LittleLong (mt->height);
for (j=0 ; j<MIPLEVELS ; j++)
mt->offsets[j] = LittleLong (mt->offsets[j]);
// for (j=0 ; j<MIPLEVELS ; j++)
// mt->offsets[j] = LittleLong (mt->offsets[j]);
if ( (mt->width & 15) || (mt->height & 15) )
Sys_Error ("Texture %s is not 16 aligned", mt->name);
pixels = mt->width*mt->height/64*85;
// spike -- quakespasm doesn't use the submips anyway
pixels = mt->width*mt->height;
//pixels = mt->width*mt->height/64*85;
tx = (texture_t *) Hunk_AllocName (sizeof(texture_t) +pixels, loadname );
loadmodel->textures[i] = tx;
memcpy (tx->name, mt->name, sizeof(tx->name));
tx->width = mt->width;
tx->height = mt->height;
for (j=0 ; j<MIPLEVELS ; j++)
tx->offsets[j] = mt->offsets[j] + sizeof(texture_t) - sizeof(miptex_t);
// for (j=0 ; j<MIPLEVELS ; j++)
// tx->offsets[j] = mt->offsets[j] + sizeof(texture_t) - sizeof(miptex_t);
// the pixels immediately follow the structures
if (!LittleLong (mt->offsets[0]))
{ // spike -- support tyrlight-ericw's -notex argument to avoid gpl violations from embedded textures
size_t x,y;
for(y=0;y<tx->width;y++)
for(x=0;x<tx->width;x++)
((byte*)(tx+1))[y*tx->width+x] = (((x>>2)^(y>>2))&1)?6:2;
}
else
{
// ericw -- check for pixels extending past the end of the lump.
// appears in the wild; e.g. jam2_tronyn.bsp (func_mapjam2),
// kellbase1.bsp (quoth), and can lead to a segfault if we read past
@ -486,7 +630,13 @@ void Mod_LoadTextures (lump_t *l)
Con_DPrintf("Texture %s extends past end of lump\n", mt->name);
pixels = q_max(0, (mod_base + l->fileofs + l->filelen) - (byte*)(mt+1));
}
//spike -- this is actually a pointless waste of memory.
//its not like this data will actually be used beyond this function in any gl renderer.
//this makes copying it pointless
//which in turn makes the pointer-to-array-of-pointers-to-texture a bit silly.
memcpy ( tx+1, mt+1, pixels);
}
tx->update_warp = false; //johnfitz
tx->warpimage = NULL; //johnfitz
@ -503,11 +653,11 @@ void Mod_LoadTextures (lump_t *l)
mark = Hunk_LowMark();
COM_StripExtension (loadmodel->name + 5, mapname, sizeof(mapname));
q_snprintf (filename, sizeof(filename), "textures/%s/#%s", mapname, tx->name+1); //this also replaces the '*' with a '#'
data = Image_LoadImage (filename, &fwidth, &fheight);
data = !gl_load24bit.value?NULL:Image_LoadImage (filename, &fwidth, &fheight, &malloced);
if (!data)
{
q_snprintf (filename, sizeof(filename), "textures/#%s", tx->name+1);
data = Image_LoadImage (filename, &fwidth, &fheight);
data = !gl_load24bit.value?NULL:Image_LoadImage (filename, &fwidth, &fheight, &malloced);
}
//now load whatever we found
@ -532,6 +682,8 @@ void Mod_LoadTextures (lump_t *l)
tx->warpimage = TexMgr_LoadImage (loadmodel, texturename, gl_warpimagesize,
gl_warpimagesize, SRC_RGBA, hunk_base, "", (src_offset_t)hunk_base, TEXPREF_NOPICMIP | TEXPREF_WARPIMAGE);
tx->update_warp = true;
if (malloced)
free(data);
}
else //regular texture
{
@ -547,11 +699,11 @@ void Mod_LoadTextures (lump_t *l)
mark = Hunk_LowMark ();
COM_StripExtension (loadmodel->name + 5, mapname, sizeof(mapname));
q_snprintf (filename, sizeof(filename), "textures/%s/%s", mapname, tx->name);
data = Image_LoadImage (filename, &fwidth, &fheight);
data = !gl_load24bit.value?NULL:Image_LoadImage (filename, &fwidth, &fheight, &malloced);
if (!data)
{
q_snprintf (filename, sizeof(filename), "textures/%s", tx->name);
data = Image_LoadImage (filename, &fwidth, &fheight);
data = !gl_load24bit.value?NULL:Image_LoadImage (filename, &fwidth, &fheight, &malloced);
}
//now load whatever we found
@ -561,13 +713,15 @@ void Mod_LoadTextures (lump_t *l)
SRC_RGBA, data, filename, 0, TEXPREF_MIPMAP | extraflags );
//now try to load glow/luma image from the same place
if (malloced)
free(data);
Hunk_FreeToLowMark (mark);
q_snprintf (filename2, sizeof(filename2), "%s_glow", filename);
data = Image_LoadImage (filename2, &fwidth, &fheight);
data = !gl_load24bit.value?NULL:Image_LoadImage (filename2, &fwidth, &fheight, &malloced);
if (!data)
{
q_snprintf (filename2, sizeof(filename2), "%s_luma", filename);
data = Image_LoadImage (filename2, &fwidth, &fheight);
data = !gl_load24bit.value?NULL:Image_LoadImage (filename2, &fwidth, &fheight, &malloced);
}
if (data)
@ -592,6 +746,8 @@ void Mod_LoadTextures (lump_t *l)
SRC_INDEXED, (byte *)(tx+1), loadmodel->name, offset, TEXPREF_MIPMAP | extraflags);
}
}
if (malloced)
free(data);
Hunk_FreeToLowMark (mark);
}
}
@ -708,6 +864,7 @@ void Mod_LoadLighting (lump_t *l)
byte d;
char litfilename[MAX_OSPATH];
unsigned int path_id;
int bspxsize;
loadmodel->lightdata = NULL;
// LordHavoc: check for a .lit file
@ -755,6 +912,11 @@ void Mod_LoadLighting (lump_t *l)
// LordHavoc: no .lit found, expand the white lighting data to color
if (!l->filelen)
return;
loadmodel->lightdata = Q1BSPX_FindLump("RGBLIGHTING", &bspxsize);
if (loadmodel->lightdata && bspxsize == l->filelen*3)
Con_DPrintf("bspx lighting loaded\n");
else
{
loadmodel->lightdata = (byte *) Hunk_AllocName ( l->filelen*3, litfilename);
in = loadmodel->lightdata + l->filelen*2; // place the file at the end, so it will not be overwritten until the very last write
out = loadmodel->lightdata;
@ -767,6 +929,7 @@ void Mod_LoadLighting (lump_t *l)
*out++ = d;
}
}
}
/*
@ -1457,7 +1620,7 @@ void Mod_LoadNodes (lump_t *l, int bsp2)
else
Mod_LoadNodes_S(l);
Mod_SetParent (loadmodel->nodes, NULL); // sets nodes and leafs
// Mod_SetParent (loadmodel->nodes, NULL); // sets nodes and leafs
}
void Mod_ProcessLeafs_S (dsleaf_t *in, int filelen)
@ -1609,6 +1772,96 @@ void Mod_LoadLeafs (lump_t *l, int bsp2)
Mod_ProcessLeafs_S ((dsleaf_t *) in, l->filelen);
}
void Mod_CheckWaterVis(void)
{
mleaf_t *leaf, *other;
int i, j, k;
int numclusters = loadmodel->submodels[0].visleafs;
int contentfound = 0;
int contenttransparent = 0;
int contenttype;
if (r_novis.value)
{ //all can be
loadmodel->contentstransparent = (SURF_DRAWWATER|SURF_DRAWTELE|SURF_DRAWSLIME|SURF_DRAWLAVA);
return;
}
//pvs is 1-based. leaf 0 sees all (the solid leaf).
//leaf 0 has no pvs, and does not appear in other leafs either, so watch out for the biases.
for (i=0,leaf=loadmodel->leafs+1 ; i<numclusters ; i++, leaf++)
{
byte *vis;
if (leaf->contents == CONTENTS_WATER)
{
if ((contenttransparent & (SURF_DRAWWATER|SURF_DRAWTELE))==(SURF_DRAWWATER|SURF_DRAWTELE))
continue;
//this check is somewhat risky, but we should be able to get away with it.
for (contenttype = 0, i = 0; i < leaf->nummarksurfaces; i++)
if (leaf->firstmarksurface[i]->flags & (SURF_DRAWWATER|SURF_DRAWTELE))
{
contenttype = leaf->firstmarksurface[i]->flags & (SURF_DRAWWATER|SURF_DRAWTELE);
break;
}
//its possible that this leaf has absolutely no surfaces in it, turb or otherwise.
if (contenttype == 0)
continue;
}
else if (leaf->contents == CONTENTS_SLIME)
contenttype = SURF_DRAWSLIME;
else if (leaf->contents == CONTENTS_LAVA)
contenttype = SURF_DRAWLAVA;
//fixme: tele
else
continue;
if (contenttransparent & contenttype)
{
nextleaf:
continue; //found one of this type already
}
contentfound |= contenttype;
vis = Mod_DecompressVis(leaf->compressed_vis, loadmodel);
for (j = 0; j < (numclusters+7)/8; j++)
{
if (vis[j])
{
for (k = 0; k < 8; k++)
{
if (vis[j] & (1u<<k))
{
other = &loadmodel->leafs[(j<<3)+k+1];
if (leaf->contents != other->contents)
{
// Con_Printf("%p:%i sees %p:%i\n", leaf, leaf->contents, other, other->contents);
contenttransparent |= contenttype;
goto nextleaf;
}
}
}
}
}
}
if (!contenttransparent)
Con_DPrintf("%s is not watervised\n", loadmodel->name);
else
{
Con_DPrintf("%s is vised for transparent", loadmodel->name);
if (contenttransparent & SURF_DRAWWATER)
Con_DPrintf(" water");
if (contenttransparent & SURF_DRAWTELE)
Con_DPrintf(" tele");
if (contenttransparent & SURF_DRAWLAVA)
Con_DPrintf(" lava");
if (contenttransparent & SURF_DRAWSLIME)
Con_DPrintf(" slime");
Con_DPrintf("\n");
}
//any types that we didn't find are assumed to be transparent.
//this allows submodels to work okay (eg: ad uses func_illusionary teleporters for some reason).
loadmodel->contentstransparent = contenttransparent | (~contentfound & (SURF_DRAWWATER|SURF_DRAWTELE|SURF_DRAWSLIME|SURF_DRAWLAVA));
}
/*
=================
Mod_LoadClipnodes
@ -1641,7 +1894,10 @@ void Mod_LoadClipnodes (lump_t *l, qboolean bsp2)
count = l->filelen / sizeof(*ins);
}
if (count)
out = (mclipnode_t *) Hunk_AllocName ( count*sizeof(*out), loadname);
else
out = NULL; //will use rnodes.
//johnfitz -- warn about exceeding old limits
if (count > 32767 && !bsp2)
@ -1752,6 +2008,26 @@ void Mod_MakeHull0 (void)
out->children[j] = child - loadmodel->nodes;
}
}
//if qbsp was run with -noclip, make sure the extra hulls use the rnodes instead of the missing clipnodes
//this won't 'fix' it, but it will stop it from crashing if it was just quickly built for debugging or whatever.
if (!loadmodel->hulls[1].clipnodes)
{ //hulls will be point-sized.
//bias that point so that its mid,mid,bottom instead of at the absmin or origin. this will retain view offsets.
loadmodel->hulls[1].clip_maxs[2] -= loadmodel->hulls[1].clip_mins[2];
loadmodel->hulls[1].clip_mins[2] = 0;
loadmodel->hulls[1].clipnodes = hull->clipnodes;
loadmodel->hulls[1].firstclipnode = hull->firstclipnode;
loadmodel->hulls[1].lastclipnode = hull->lastclipnode;
}
if (!loadmodel->hulls[2].clipnodes)
{
loadmodel->hulls[2].clip_maxs[2] -= loadmodel->hulls[2].clip_mins[2];
loadmodel->hulls[2].clip_mins[2] = 0;
loadmodel->hulls[2].clipnodes = loadmodel->hulls[1].clipnodes;
loadmodel->hulls[2].firstclipnode = loadmodel->hulls[1].firstclipnode;
loadmodel->hulls[2].lastclipnode = loadmodel->hulls[1].lastclipnode;
}
}
/*
@ -1899,15 +2175,25 @@ Mod_LoadSubmodels
*/
void Mod_LoadSubmodels (lump_t *l)
{
dmodel_t *in;
dmodel_t *out;
int i, j, count;
mmodel_t *out;
size_t i, j, count;
in = (dmodel_t *)(mod_base + l->fileofs);
//detect whether this is a hexen2 8-hull map or a quake 4-hull map
dmodelq1_t *inq1 = (dmodelq1_t *)(mod_base + l->fileofs);
dmodelh2_t *inh2 = (dmodelh2_t *)(mod_base + l->fileofs);
//the numfaces is a bit of a hack. hexen2 only actually uses 6 of its 8 hulls and we depend upon this.
//this means that the 7th and 8th are null. q1.numfaces of the world equates to h2.hull[6], so should have a value for q1, and be 0 for hexen2.
//this should work even for maps that have enough submodels to realign the size.
//note that even if the map loads, you're on your own regarding the palette (hurrah for retexturing projects?).
//fixme: we don't fix up the clipnodes yet, the player is fine, shamblers/ogres/fiends/vores will have issues.
//unfortunately c doesn't do templates, which would make all this code a bit less copypastay
if ((size_t)l->filelen >= sizeof(*inh2) && !(l->filelen % sizeof(*inh2)) && !inq1->numfaces && inq1[1].firstface)
{
dmodelh2_t *in = inh2;
if (l->filelen % sizeof(*in))
Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
count = l->filelen / sizeof(*in);
out = (dmodel_t *) Hunk_AllocName ( count*sizeof(*out), loadname);
out = (mmodel_t *) Hunk_AllocName ( count*sizeof(*out), loadname);
loadmodel->submodels = out;
loadmodel->numsubmodels = count;
@ -1920,12 +2206,43 @@ void Mod_LoadSubmodels (lump_t *l)
out->maxs[j] = LittleFloat (in->maxs[j]) + 1;
out->origin[j] = LittleFloat (in->origin[j]);
}
for (j=0 ; j<MAX_MAP_HULLS ; j++)
for (j=0 ; j<MAX_MAP_HULLS && j<sizeof(in->headnode)/sizeof(in->headnode[0]) ; j++)
out->headnode[j] = LittleLong (in->headnode[j]);
for (; j<MAX_MAP_HULLS ; j++)
out->headnode[j] = 0;
out->visleafs = LittleLong (in->visleafs);
out->firstface = LittleLong (in->firstface);
out->numfaces = LittleLong (in->numfaces);
}
}
else
{
dmodelq1_t *in = inq1;
if (l->filelen % sizeof(*in))
Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
count = l->filelen / sizeof(*in);
out = (mmodel_t *) Hunk_AllocName ( count*sizeof(*out), loadname);
loadmodel->submodels = out;
loadmodel->numsubmodels = count;
for (i=0 ; i<count ; i++, in++, out++)
{
for (j=0 ; j<3 ; j++)
{ // spread the mins / maxs by a pixel
out->mins[j] = LittleFloat (in->mins[j]) - 1;
out->maxs[j] = LittleFloat (in->maxs[j]) + 1;
out->origin[j] = LittleFloat (in->origin[j]);
}
for (j=0 ; j<MAX_MAP_HULLS && j<sizeof(in->headnode)/sizeof(in->headnode[0]) ; j++)
out->headnode[j] = LittleLong (in->headnode[j]);
for (; j<MAX_MAP_HULLS ; j++)
out->headnode[j] = 0;
out->visleafs = LittleLong (in->visleafs);
out->firstface = LittleLong (in->firstface);
out->numfaces = LittleLong (in->numfaces);
}
}
// johnfitz -- check world visleafs -- adapted from bjp
out = loadmodel->submodels;
@ -1997,7 +2314,7 @@ void Mod_LoadBrushModel (qmodel_t *mod, void *buffer)
int i, j;
int bsp2;
dheader_t *header;
dmodel_t *bm;
mmodel_t *bm;
float radius; //johnfitz
loadmodel->type = mod_brush;
@ -2018,8 +2335,9 @@ void Mod_LoadBrushModel (qmodel_t *mod, void *buffer)
bsp2 = 2; //sanitised revision
break;
default:
Sys_Error ("Mod_LoadBrushModel: %s has wrong version number (%i should be %i)", mod->name, mod->bspversion, BSPVERSION);
break;
loadmodel->type = mod_ext_invalid;
Con_Warning ("Mod_LoadBrushModel: %s has wrong version number (%i should be %i)\n", mod->name, mod->bspversion, BSPVERSION);
return;
}
// swap all the lumps
@ -2028,6 +2346,8 @@ void Mod_LoadBrushModel (qmodel_t *mod, void *buffer)
for (i = 0; i < (int) sizeof(dheader_t) / 4; i++)
((int *)header)[i] = LittleLong ( ((int *)header)[i]);
Q1BSPX_Setup(mod, buffer, com_filesize, header->lumps, HEADER_LUMPS);
// load into heap
Mod_LoadVertexes (&header->lumps[LUMP_VERTEXES]);
@ -2050,6 +2370,8 @@ void Mod_LoadBrushModel (qmodel_t *mod, void *buffer)
mod->numframes = 2; // regular and alternate animation
Mod_CheckWaterVis();
//
// set up the submodels (FIXME: this is confusing)
//
@ -2066,6 +2388,9 @@ void Mod_LoadBrushModel (qmodel_t *mod, void *buffer)
for (j=1 ; j<MAX_MAP_HULLS ; j++)
{
mod->hulls[j].firstclipnode = bm->headnode[j];
if (mod->hulls[j].clipnodes == mod->hulls[0].clipnodes)
mod->hulls[j].lastclipnode = mod->hulls[0].lastclipnode;
else
mod->hulls[j].lastclipnode = mod->numclipnodes-1;
}
@ -2105,6 +2430,8 @@ void Mod_LoadBrushModel (qmodel_t *mod, void *buffer)
*loadmodel = *mod;
strcpy (loadmodel->name, name);
mod = loadmodel;
Mod_SetExtraFlags(mod);
}
}
}
@ -2120,11 +2447,12 @@ ALIAS MODELS
aliashdr_t *pheader;
stvert_t stverts[MAXALIASVERTS];
mtriangle_t triangles[MAXALIASTRIS];
mtriangle_t *triangles;
int max_triangles;
// a pose is a single set of vertexes. a frame may be
// an animating sequence of poses
trivertx_t *poseverts[MAXALIASFRAMES];
trivertx_t *poseverts_mdl[MAXALIASFRAMES];
int posenum;
byte **player_8bit_texels_tbl;
@ -2161,7 +2489,7 @@ void * Mod_LoadAliasFrame (void * pin, maliasframedesc_t *frame)
pinframe = (trivertx_t *)(pdaliasframe + 1);
poseverts[posenum] = pinframe;
poseverts_mdl[posenum] = pinframe;
posenum++;
pinframe += pheader->numverts;
@ -2209,7 +2537,7 @@ void *Mod_LoadAliasGroup (void * pin, maliasframedesc_t *frame)
{
if (posenum >= MAXALIASFRAMES) Sys_Error ("posenum >= MAXALIASFRAMES");
poseverts[posenum] = (trivertx_t *)((daliasframe_t *)ptemp + 1);
poseverts_mdl[posenum] = (trivertx_t *)((daliasframe_t *)ptemp + 1);
posenum++;
ptemp = (trivertx_t *)((daliasframe_t *)ptemp + 1) + pheader->numverts;
@ -2262,7 +2590,7 @@ void Mod_FloodFillSkin( byte *skin, int skinwidth, int skinheight )
filledcolor = 0;
// attempt to find opaque black
for (i = 0; i < 256; ++i)
if (d_8to24table[i] == (255 << 0)) // alpha 1.0
if (d_8to24table[i] == (unsigned int)LittleLong(255 << 24)) // alpha 1.0
{
filledcolor = i;
break;
@ -2423,15 +2751,21 @@ void Mod_CalcAliasBounds (aliashdr_t *a)
{
loadmodel->mins[i] = loadmodel->ymins[i] = loadmodel->rmins[i] = FLT_MAX;
loadmodel->maxs[i] = loadmodel->ymaxs[i] = loadmodel->rmaxs[i] = -FLT_MAX;
radius = yawradius = 0;
}
radius = yawradius = 0;
for (;;)
{
if (a->numposes && a->numverts)
{
if (a->posevertssize == 1)
{
//process verts
for (i=0 ; i<a->numposes; i++)
for (j=0; j<a->numverts; j++)
{
for (k=0; k<3;k++)
v[k] = poseverts[i][j].v[k] * pheader->scale[k] + pheader->scale_origin[k];
v[k] = poseverts_mdl[i][j].v[k] * pheader->scale[k] + pheader->scale_origin[k];
for (k=0; k<3;k++)
{
@ -2445,6 +2779,48 @@ void Mod_CalcAliasBounds (aliashdr_t *a)
if (radius < dist)
radius = dist;
}
}
else if (a->posevertssize == 2)
{
//process verts
for (i=0 ; i<a->numposes; i++)
{
md3XyzNormal_t *pv = (md3XyzNormal_t *)((byte*)a+a->vertexes) + i*a->numverts;
for (j=0; j<a->numverts; j++)
{
for (k=0; k<3;k++)
v[k] = pv[j].xyz[k] * 1/64.0;
for (k=0; k<3;k++)
{
loadmodel->mins[k] = q_min(loadmodel->mins[k], v[k]);
loadmodel->maxs[k] = q_max(loadmodel->maxs[k], v[k]);
}
dist = v[0] * v[0] + v[1] * v[1];
if (yawradius < dist)
yawradius = dist;
dist += v[2] * v[2];
if (radius < dist)
radius = dist;
}
}
}
}
if (!a->nextsurface)
break;
a = (aliashdr_t*)((byte*)a + a->nextsurface);
}
//dodgy model that lacks any frames or verts
for (i=0; i<3;i++)
{
if (loadmodel->mins[i] > loadmodel->maxs[i])
{ //set sizes to 0 if its invalid.
loadmodel->mins[i] = 0;
loadmodel->maxs[i] = 0;
}
}
//rbounds will be used when entity has nonzero pitch or roll
radius = sqrt(radius);
@ -2500,11 +2876,13 @@ void Mod_SetExtraFlags (qmodel_t *mod)
{
extern cvar_t r_nolerp_list, r_noshadow_list;
if (!mod || mod->type != mod_alias)
if (!mod)
return;
mod->flags &= (0xFF | MF_HOLEY); //only preserve first byte, plus MF_HOLEY
if (mod->type == mod_alias)
{
// nolerp flag
if (nameInList(r_nolerp_list.string, mod->name))
mod->flags |= MOD_NOLERP;
@ -2520,6 +2898,11 @@ void Mod_SetExtraFlags (qmodel_t *mod)
mod->flags |= MOD_FBRIGHTHACK;
}
#ifdef PSET_SCRIPT
PScript_UpdateModelEffects(mod);
#endif
}
/*
=================
Mod_LoadAliasModel
@ -2566,29 +2949,54 @@ void Mod_LoadAliasModel (qmodel_t *mod, void *buffer)
pheader->skinheight = LittleLong (pinmodel->skinheight);
if (pheader->skinheight > MAX_LBM_HEIGHT)
Sys_Error ("model %s has a skin taller than %d", mod->name,
MAX_LBM_HEIGHT);
Con_DWarning ("model %s has a skin taller than %d\n", mod->name,
MAX_LBM_HEIGHT); //Spike -- this was always a bogus error in gl renderers. its width*height that really matters.
pheader->numverts = LittleLong (pinmodel->numverts);
if (pheader->numverts <= 0)
Sys_Error ("model %s has no vertices", mod->name);
if (pheader->numverts > MAXALIASVERTS)
Sys_Error ("model %s has too many vertices (%d; max = %d)", mod->name, pheader->numverts, MAXALIASVERTS);
{ //Spike -- made this more tollerant. its still an error of course.
Con_Warning("model %s has too many vertices (%i > %i)\n", mod->name, pheader->numverts, MAXALIASVERTS);
mod->type = mod_ext_invalid;
return;
}
if (pheader->numverts > VANILLA_MAXALIASVERTS)
Con_DWarning("model %s exceeds standard vertex limit (%i > %i)\n", mod->name, pheader->numverts, VANILLA_MAXALIASVERTS);
pheader->numtris = LittleLong (pinmodel->numtris);
if (pheader->numtris <= 0)
Sys_Error ("model %s has no triangles", mod->name);
if (pheader->numtris > MAXALIASTRIS)
Sys_Error ("model %s has too many triangles (%d; max = %d)", mod->name, pheader->numtris, MAXALIASTRIS);
if (pheader->numtris > max_triangles)
{
mtriangle_t *n = malloc(sizeof(*triangles) * pheader->numtris);
if (n)
{
free(triangles);
triangles = n;
max_triangles = pheader->numtris;
}
else
{
max_triangles = 0;
//Spike -- added this check, because I'm segfaulting out.
Con_Warning("model %s has too many triangles (%i)\n", mod->name, pheader->numtris);
mod->type = mod_ext_invalid;
return;
}
}
pheader->numframes = LittleLong (pinmodel->numframes);
numframes = pheader->numframes;
if (numframes < 1)
Sys_Error ("Mod_LoadAliasModel: Invalid # of frames: %d\n", numframes);
if (numframes > MAXALIASFRAMES)
{
numframes = MAXALIASFRAMES;
Con_Warning("model %s has too many frames (%i > %i)\n", mod->name, numframes, MAXALIASFRAMES);
}
pheader->size = LittleFloat (pinmodel->size) * ALIAS_BASE_SIZE_RATIO;
mod->synctype = (synctype_t) LittleLong (pinmodel->synctype);
@ -2653,17 +3061,17 @@ void Mod_LoadAliasModel (qmodel_t *mod, void *buffer)
}
pheader->numposes = posenum;
pheader->posevertssize = 1;
mod->type = mod_alias;
Mod_SetExtraFlags (mod); //johnfitz
Mod_CalcAliasBounds (pheader); //johnfitz
//
// build the draw lists
//
GL_MakeAliasModelDisplayLists (mod, pheader);
GLMesh_LoadVertexBuffer (mod, pheader);
//
// move the complete, relocatable alias model to the cache
@ -2686,7 +3094,7 @@ void Mod_LoadAliasModel (qmodel_t *mod, void *buffer)
Mod_LoadSpriteFrame
=================
*/
void * Mod_LoadSpriteFrame (void * pin, mspriteframe_t **ppframe, int framenum)
void * Mod_LoadSpriteFrame (void * pin, mspriteframe_t **ppframe, int framenum, enum srcformat fmt)
{
dspriteframe_t *pinframe;
mspriteframe_t *pspriteframe;
@ -2699,6 +3107,8 @@ void * Mod_LoadSpriteFrame (void * pin, mspriteframe_t **ppframe, int framenum)
width = LittleLong (pinframe->width);
height = LittleLong (pinframe->height);
size = width * height;
if (fmt == SRC_RGBA)
size *= 4;
pspriteframe = (mspriteframe_t *) Hunk_AllocName (sizeof (mspriteframe_t),loadname);
*ppframe = pspriteframe;
@ -2721,7 +3131,7 @@ void * Mod_LoadSpriteFrame (void * pin, mspriteframe_t **ppframe, int framenum)
q_snprintf (name, sizeof(name), "%s:frame%i", loadmodel->name, framenum);
offset = (src_offset_t)(pinframe+1) - (src_offset_t)mod_base; //johnfitz
pspriteframe->gltexture =
TexMgr_LoadImage (loadmodel, name, width, height, SRC_INDEXED,
TexMgr_LoadImage (loadmodel, name, width, height, fmt,
(byte *)(pinframe + 1), loadmodel->name, offset,
TEXPREF_PAD | TEXPREF_ALPHA | TEXPREF_NOPICMIP); //johnfitz -- TexMgr
@ -2734,7 +3144,7 @@ void * Mod_LoadSpriteFrame (void * pin, mspriteframe_t **ppframe, int framenum)
Mod_LoadSpriteGroup
=================
*/
void * Mod_LoadSpriteGroup (void * pin, mspriteframe_t **ppframe, int framenum)
void * Mod_LoadSpriteGroup (void * pin, mspriteframe_t **ppframe, int framenum, enum srcformat fmt)
{
dspritegroup_t *pingroup;
mspritegroup_t *pspritegroup;
@ -2742,6 +3152,7 @@ void * Mod_LoadSpriteGroup (void * pin, mspriteframe_t **ppframe, int framenum)
dspriteinterval_t *pin_intervals;
float *poutintervals;
void *ptemp;
float prevtime;
pingroup = (dspritegroup_t *)pin;
@ -2760,11 +3171,13 @@ void * Mod_LoadSpriteGroup (void * pin, mspriteframe_t **ppframe, int framenum)
pspritegroup->intervals = poutintervals;
for (i=0 ; i<numframes ; i++)
for (i=0,prevtime=0 ; i<numframes ; i++)
{
*poutintervals = LittleFloat (pin_intervals->interval);
if (*poutintervals <= 0.0)
Sys_Error ("Mod_LoadSpriteGroup: interval<=0");
//Spike -- we need to accumulate the previous time too, so we get actual timestamps, otherwise spritegroups won't animate (vanilla bug).
prevtime = *poutintervals = prevtime+*poutintervals;
poutintervals++;
pin_intervals++;
@ -2774,7 +3187,7 @@ void * Mod_LoadSpriteGroup (void * pin, mspriteframe_t **ppframe, int framenum)
for (i=0 ; i<numframes ; i++)
{
ptemp = Mod_LoadSpriteFrame (ptemp, &pspritegroup->frames[i], framenum * 100 + i);
ptemp = Mod_LoadSpriteFrame (ptemp, &pspritegroup->frames[i], framenum * 100 + i, fmt);
}
return ptemp;
@ -2795,14 +3208,22 @@ void Mod_LoadSpriteModel (qmodel_t *mod, void *buffer)
int numframes;
int size;
dspriteframetype_t *pframetype;
enum srcformat fmt = SRC_INDEXED;
pin = (dsprite_t *)buffer;
mod_base = (byte *)buffer; //johnfitz
version = LittleLong (pin->version);
if (version != SPRITE_VERSION)
Sys_Error ("%s has wrong version number "
"(%i should be %i)", mod->name, version, SPRITE_VERSION);
if (version == 32)
fmt = SRC_RGBA; //Spike -- spr32 is identical to regular sprites, but uses rgba instead of indexed values. should probably also blend these sprites instead of alphatest, but meh.
else if (version != SPRITE_VERSION)
{
//Spike -- made this more tolerant. its still an error, it just won't crash us out
Con_Printf( "%s has wrong version number "
"(%i should be %i)\n", mod->name, version, SPRITE_VERSION);
mod->type = mod_ext_invalid;
return;
}
numframes = LittleLong (pin->numframes);
@ -2844,12 +3265,12 @@ void Mod_LoadSpriteModel (qmodel_t *mod, void *buffer)
if (frametype == SPR_SINGLE)
{
pframetype = (dspriteframetype_t *)
Mod_LoadSpriteFrame (pframetype + 1, &psprite->frames[i].frameptr, i);
Mod_LoadSpriteFrame (pframetype + 1, &psprite->frames[i].frameptr, i, fmt);
}
else
{
pframetype = (dspriteframetype_t *)
Mod_LoadSpriteGroup (pframetype + 1, &psprite->frames[i].frameptr, i);
Mod_LoadSpriteGroup (pframetype + 1, &psprite->frames[i].frameptr, i, fmt);
}
}

View File

@ -33,14 +33,6 @@ m*_t structures are in-memory
*/
// entity effects
#define EF_BRIGHTFIELD 1
#define EF_MUZZLEFLASH 2
#define EF_BRIGHTLIGHT 4
#define EF_DIMLIGHT 8
/*
==============================================================================
@ -95,7 +87,7 @@ typedef struct texture_s
int anim_min, anim_max; // time for this frame min <=time< max
struct texture_s *anim_next; // in the animation sequence
struct texture_s *alternate_anims; // bmodels in frmae 1 use these
unsigned offsets[MIPLEVELS]; // four mip maps stored
// unsigned offsets[MIPLEVELS]; // four mip maps stored
} texture_t;
@ -294,11 +286,17 @@ typedef struct aliasmesh_s
unsigned short vertindex;
} aliasmesh_t;
typedef struct meshxyz_s
typedef struct meshxyz_mdl_s
{
byte xyz[4];
signed char normal[4];
} meshxyz_t;
} meshxyz_mdl_t;
typedef struct meshxyz_md3_s
{
signed short xyz[4];
signed char normal[4];
} meshxyz_md3_t;
typedef struct meshst_s
{
@ -362,25 +360,33 @@ typedef struct {
int numindexes;
intptr_t indexes; // offset into extradata: numindexes unsigned shorts
intptr_t vertexes; // offset into extradata: numposes*vertsperframe trivertx_t
intptr_t vbovertofs;
intptr_t vbostofs;
intptr_t eboofs;
//ericw --
intptr_t nextsurface; //spike
int numposes;
int poseverts;
int posedata; // numposes*poseverts trivert_t
int commands; // gl command list with embedded s/t
int posevertssize; //spike 1=mdl, 2=md3
struct gltexture_s *gltextures[MAX_SKINS][4]; //johnfitz
struct gltexture_s *fbtextures[MAX_SKINS][4]; //johnfitz
int texels[MAX_SKINS]; // only for player skins
intptr_t texels[MAX_SKINS]; // only for player skins
maliasframedesc_t frames[1]; // variable sized
} aliashdr_t;
#define MAXALIASVERTS 2000 //johnfitz -- was 1024
#define MAXALIASFRAMES 256
#define MAXALIASTRIS 4096 //ericw -- was 2048
extern aliashdr_t *pheader;
typedef struct {
short xyz[3];
byte latlong[2];
} md3XyzNormal_t;
#define VANILLA_MAXALIASVERTS 1024
#define MAXALIASVERTS 65536 // spike -- was 2000 //johnfitz -- was 1024
#define MAXALIASFRAMES 1024 //spike -- was 256
extern stvert_t stverts[MAXALIASVERTS];
extern mtriangle_t triangles[MAXALIASTRIS];
extern trivertx_t *poseverts[MAXALIASFRAMES];
extern mtriangle_t *triangles;
extern trivertx_t *poseverts_mdl[MAXALIASFRAMES];
extern md3XyzNormal_t *poseverts_md3[MAXALIASFRAMES];
//===================================================================
@ -388,8 +394,9 @@ extern trivertx_t *poseverts[MAXALIASFRAMES];
// Whole model
//
typedef enum {mod_brush, mod_sprite, mod_alias} modtype_t;
typedef enum {mod_brush, mod_sprite, mod_alias, mod_ext_invalid} modtype_t;
//Spike -- these are misnamed/ambiguous.
#define EF_ROCKET 1 // leave a trail
#define EF_GRENADE 2 // leave a trail
#define EF_GIB 4 // leave a trail
@ -405,6 +412,10 @@ typedef enum {mod_brush, mod_sprite, mod_alias} modtype_t;
#define MOD_NOSHADOW 512 //don't cast a shadow
#define MOD_FBRIGHTHACK 1024 //when fullbrights are disabled, use a hack to render this model brighter
//johnfitz
//spike -- added this for particle stuff
#define MOD_EMITREPLACE 2048 //particle effect completely replaces the model (for flames or whatever).
#define MOD_EMITFORWARDS 4096 //particle effect is emitted forwards, rather than downwards. why down? good question.
//spike
typedef struct qmodel_s
{
@ -419,6 +430,13 @@ typedef struct qmodel_s
int flags;
#ifdef PSET_SCRIPT
int emiteffect; //spike -- this effect is emitted per-frame by entities with this model
int traileffect; //spike -- this effect is used when entities move
struct skytris_s *skytris; //spike -- surface-based particle emission for this model
struct skytriblock_s *skytrimem; //spike -- surface-based particle emission for this model (for better cache performance+less allocs)
double skytime; //doesn't really cope with multiples. oh well...
#endif
//
// volume occupied by the model graphics
//
@ -439,7 +457,7 @@ typedef struct qmodel_s
int firstmodelsurface, nummodelsurfaces;
int numsubmodels;
dmodel_t *submodels;
mmodel_t *submodels;
int numplanes;
mplane_t *planes;
@ -483,16 +501,16 @@ typedef struct qmodel_s
qboolean viswarn; // for Mod_DecompressVis()
int bspversion;
int contentstransparent; //spike -- added this so we can disable glitchy wateralpha where its not supported.
//
// alias model
//
GLuint meshvbo;
byte *meshvboptr; //for non-vbo fallback.
GLuint meshindexesvbo;
int vboindexofs; // offset in vbo of the hdr->numindexes unsigned shorts
int vboxyzofs; // offset in vbo of hdr->numposes*hdr->numverts_vbo meshxyz_t
int vbostofs; // offset in vbo of hdr->numverts_vbo meshst_t
byte *meshindexesvboptr; //for non-ebo fallback.
//
// additional model data

View File

@ -208,8 +208,37 @@ void R_StoreEfrags (efrag_t **ppefrag)
{
pent = pefrag->entity;
if ((pent->visframe != r_framecount) && (cl_numvisedicts < MAX_VISEDICTS))
if ((pent->visframe != r_framecount) && (cl_numvisedicts < cl_maxvisedicts))
{
#ifdef PSET_SCRIPT
if (pent->netstate.emiteffectnum > 0)
{
float t = cl.time-cl.oldtime;
vec3_t axis[3];
if (t < 0) t = 0; else if (t > 0.1) t= 0.1;
AngleVectors(pent->angles, axis[0], axis[1], axis[2]);
if (pent->model->type == mod_alias)
axis[0][2] *= -1; //stupid vanilla bug
PScript_RunParticleEffectState(pent->origin, axis[0], t, cl.particle_precache[pent->netstate.emiteffectnum].index, &pent->emitstate);
}
else if (pent->model->emiteffect >= 0)
{
float t = cl.time-cl.oldtime;
vec3_t axis[3];
if (t < 0) t = 0; else if (t > 0.1) t= 0.1;
AngleVectors(pent->angles, axis[0], axis[1], axis[2]);
if (pent->model->flags & MOD_EMITFORWARDS)
{
if (pent->model->type == mod_alias)
axis[0][2] *= -1; //stupid vanilla bug
}
else
VectorScale(axis[2], -1, axis[0]);
PScript_RunParticleEffectState(pent->origin, axis[0], t, pent->model->emiteffect, &pent->emitstate);
if (pent->model->flags & MOD_EMITREPLACE)
continue;
}
#endif
cl_visedicts[cl_numvisedicts++] = pent;
pent->visframe = r_framecount;
}

View File

@ -172,7 +172,8 @@ void R_MarkLights (dlight_t *light, int num, mnode_t *node)
msurface_t *surf;
vec3_t impact;
float dist, l, maxdist;
int i, j, s, t;
unsigned int i;
int j, s, t;
start:
@ -311,7 +312,8 @@ loc0:
return true; // hit something
else
{
int i, ds, dt;
unsigned int i;
int ds, dt;
msurface_t *surf;
// check for impact on this node
VectorCopy (mid, lightspot);

View File

@ -109,6 +109,7 @@ cvar_t r_telealpha = {"r_telealpha","0",CVAR_NONE};
cvar_t r_slimealpha = {"r_slimealpha","0",CVAR_NONE};
float map_wateralpha, map_lavaalpha, map_telealpha, map_slimealpha;
float map_fallbackalpha;
qboolean r_drawflat_cheatsafe, r_fullbright_cheatsafe, r_lightmap_cheatsafe, r_drawworld_cheatsafe; //johnfitz
@ -347,12 +348,15 @@ qboolean R_CullModelForEntity (entity_t *e)
R_RotateForEntity -- johnfitz -- modified to take origin and angles instead of pointer to entity
===============
*/
void R_RotateForEntity (vec3_t origin, vec3_t angles)
void R_RotateForEntity (vec3_t origin, vec3_t angles, unsigned char scale)
{
glTranslatef (origin[0], origin[1], origin[2]);
glRotatef (angles[1], 0, 0, 1);
glRotatef (-angles[0], 0, 1, 0);
glRotatef (angles[2], 1, 0, 0);
if (scale != 16)
glScalef (scale/16.0, scale/16.0, scale/16.0);
}
/*
@ -635,7 +639,7 @@ void R_DrawEntitiesOnList (qboolean alphapass) //johnfitz -- added parameter
continue;
//johnfitz -- chasecam
if (currententity == &cl_entities[cl.viewentity])
if (currententity == &cl.entities[cl.viewentity])
currententity->angles[0] *= 0.3;
//johnfitz
@ -650,6 +654,9 @@ void R_DrawEntitiesOnList (qboolean alphapass) //johnfitz -- added parameter
case mod_sprite:
R_DrawSpriteModel (currententity);
break;
case mod_ext_invalid:
//nothing. could draw a blob instead.
break;
}
}
}
@ -812,7 +819,7 @@ void R_ShowTris (void)
{
currententity = cl_visedicts[i];
if (currententity == &cl_entities[cl.viewentity]) // chasecam
if (currententity == &cl.entities[cl.viewentity]) // chasecam
currententity->angles[0] *= 0.3;
switch (currententity->model->type)
@ -931,6 +938,9 @@ void R_RenderScene (void)
R_RenderDlights (); //triangle fan dlights -- johnfitz -- moved after water
R_DrawParticles ();
#ifdef PSET_SCRIPT
PScript_DrawParticles();
#endif
Fog_DisableGFog (); //johnfitz
@ -1120,9 +1130,9 @@ void R_RenderView (void)
time2 = Sys_DoubleTime ();
if (r_pos.value)
Con_Printf ("x %i y %i z %i (pitch %i yaw %i roll %i)\n",
(int)cl_entities[cl.viewentity].origin[0],
(int)cl_entities[cl.viewentity].origin[1],
(int)cl_entities[cl.viewentity].origin[2],
(int)cl.entities[cl.viewentity].origin[0],
(int)cl.entities[cl.viewentity].origin[1],
(int)cl.entities[cl.viewentity].origin[2],
(int)cl.viewangles[PITCH],
(int)cl.viewangles[YAW],
(int)cl.viewangles[ROLL]);

View File

@ -116,7 +116,10 @@ R_SetWateralpha_f -- ericw
*/
static void R_SetWateralpha_f (cvar_t *var)
{
if (cls.signon == SIGNONS && cl.worldmodel && !(cl.worldmodel->contentstransparent&SURF_DRAWWATER) && var->value < 1)
Con_Warning("Map does not appear to be water-vised\n");
map_wateralpha = var->value;
map_fallbackalpha = var->value;
}
/*
@ -126,6 +129,8 @@ R_SetLavaalpha_f -- ericw
*/
static void R_SetLavaalpha_f (cvar_t *var)
{
if (cls.signon == SIGNONS && cl.worldmodel && !(cl.worldmodel->contentstransparent&SURF_DRAWLAVA) && var->value && var->value < 1)
Con_Warning("Map does not appear to be lava-vised\n");
map_lavaalpha = var->value;
}
@ -136,6 +141,8 @@ R_SetTelealpha_f -- ericw
*/
static void R_SetTelealpha_f (cvar_t *var)
{
if (cls.signon == SIGNONS && cl.worldmodel && !(cl.worldmodel->contentstransparent&SURF_DRAWTELE) && var->value && var->value < 1)
Con_Warning("Map does not appear to be tele-vised\n");
map_telealpha = var->value;
}
@ -146,6 +153,8 @@ R_SetSlimealpha_f -- ericw
*/
static void R_SetSlimealpha_f (cvar_t *var)
{
if (cls.signon == SIGNONS && cl.worldmodel && !(cl.worldmodel->contentstransparent&SURF_DRAWSLIME) && var->value && var->value < 1)
Con_Warning("Map does not appear to be slime-vised\n");
map_slimealpha = var->value;
}
@ -157,13 +166,13 @@ GL_WaterAlphaForSurfface -- ericw
float GL_WaterAlphaForSurface (msurface_t *fa)
{
if (fa->flags & SURF_DRAWLAVA)
return map_lavaalpha > 0 ? map_lavaalpha : map_wateralpha;
return map_lavaalpha > 0 ? map_lavaalpha : map_fallbackalpha;
else if (fa->flags & SURF_DRAWTELE)
return map_telealpha > 0 ? map_telealpha : map_wateralpha;
return map_telealpha > 0 ? map_telealpha : map_fallbackalpha;
else if (fa->flags & SURF_DRAWSLIME)
return map_slimealpha > 0 ? map_slimealpha : map_wateralpha;
return map_slimealpha > 0 ? map_slimealpha : map_fallbackalpha;
else
return map_wateralpha;
return map_wateralpha;// > 0 ? map_wateralpha : map_fallbackalpha;
}
@ -242,6 +251,9 @@ void R_Init (void)
Cvar_SetCallback (&r_slimealpha, R_SetSlimealpha_f);
R_InitParticles ();
#ifdef PSET_SCRIPT
PScript_InitParticles();
#endif
R_SetClearColor_f (&r_clearcolor); //johnfitz
Sky_Init (); //johnfitz
@ -281,7 +293,7 @@ void R_TranslateNewPlayerSkin (int playernum)
int skinnum;
//get correct texture pixels
currententity = &cl_entities[1+playernum];
currententity = &cl.entities[1+playernum];
if (!currententity->model || currententity->model->type != mod_alias)
return;
@ -290,6 +302,8 @@ void R_TranslateNewPlayerSkin (int playernum)
skinnum = currententity->skinnum;
if (paliashdr->numskins)
{
//TODO: move these tests to the place where skinnum gets received from the server
if (skinnum < 0 || skinnum >= paliashdr->numskins)
{
@ -303,6 +317,13 @@ void R_TranslateNewPlayerSkin (int playernum)
q_snprintf(name, sizeof(name), "player_%i", playernum);
playertextures[playernum] = TexMgr_LoadImage (currententity->model, name, paliashdr->skinwidth, paliashdr->skinheight,
SRC_INDEXED, pixels, paliashdr->gltextures[skinnum][0]->source_file, paliashdr->gltextures[skinnum][0]->source_offset, TEXPREF_PAD | TEXPREF_OVERWRITE);
}
else
{
q_snprintf(name, sizeof(name), "player_%i", playernum);
playertextures[playernum] = TexMgr_LoadImage (currententity->model, name, paliashdr->skinwidth, paliashdr->skinheight,
SRC_EXTERNAL, NULL, "skins/base.pcx", 0, TEXPREF_PAD | TEXPREF_OVERWRITE);
}
//now recolor it
R_TranslatePlayerSkin (playernum);
@ -334,10 +355,11 @@ static void R_ParseWorldspawn (void)
char key[128], value[4096];
const char *data;
map_wateralpha = r_wateralpha.value;
map_lavaalpha = r_lavaalpha.value;
map_telealpha = r_telealpha.value;
map_slimealpha = r_slimealpha.value;
map_fallbackalpha = r_wateralpha.value;
map_wateralpha = (cl.worldmodel->contentstransparent&SURF_DRAWWATER)?r_wateralpha.value:1;
map_lavaalpha = (cl.worldmodel->contentstransparent&SURF_DRAWLAVA)?r_lavaalpha.value:1;
map_telealpha = (cl.worldmodel->contentstransparent&SURF_DRAWTELE)?r_telealpha.value:1;
map_slimealpha = (cl.worldmodel->contentstransparent&SURF_DRAWSLIME)?r_slimealpha.value:1;
data = COM_Parse(cl.worldmodel->entities);
if (!data)
@ -363,7 +385,7 @@ static void R_ParseWorldspawn (void)
q_strlcpy(value, com_token, sizeof(value));
if (!strcmp("wateralpha", key))
map_wateralpha = atof(value);
map_fallbackalpha = map_wateralpha = atof(value);
if (!strcmp("lavaalpha", key))
map_lavaalpha = atof(value);
@ -396,6 +418,9 @@ void R_NewMap (void)
r_viewleaf = NULL;
R_ClearParticles ();
#ifdef PSET_SCRIPT
PScript_ClearParticles();
#endif
GL_BuildLightmaps ();
GL_BuildBModelVertexBuffer ();
@ -498,7 +523,7 @@ GLint GL_GetUniformLocation (GLuint *programPtr, const char *name)
{
GLint location;
if (!programPtr)
if (!*programPtr)
return -1;
location = GL_GetUniformLocationFunc(*programPtr, name);

View File

@ -134,6 +134,10 @@ float scr_centertime_off;
int scr_center_lines;
int scr_erase_lines;
int scr_erase_center;
#define CPRINT_TYPEWRITER (1u<<0)
#define CPRINT_PERSIST (1u<<1)
#define CPRINT_TALIGN (1u<<2)
unsigned int scr_centerprint_flags;
/*
==============
@ -145,10 +149,87 @@ for a few moments
*/
void SCR_CenterPrint (const char *str) //update centerprint data
{
unsigned int flags = 0;
if (*str != '/' && cl.intermission)
flags |= CPRINT_TYPEWRITER | CPRINT_PERSIST | CPRINT_TALIGN;
//check for centerprint prefixes/flags
while (*str == '/')
{
if (str[1] == '.')
{ //no more
str+=2;
break;
}
else if (str[1] == 'P')
flags |= CPRINT_PERSIST;
else if (str[1] == 'W') //typewriter
flags ^= CPRINT_TYPEWRITER;
else if (str[1] == 'S') //typewriter
flags ^= CPRINT_PERSIST;
else if (str[1] == 'M') //masked background
;
else if (str[1] == 'O') //obituary print (lower half)
;
else if (str[1] == 'B') //bottom-align
;
else if (str[1] == 'B') //top-align
;
else if (str[1] == 'L') //left-align
;
else if (str[1] == 'R') //right-align
;
else if (str[1] == 'F') //alternative 'finale' control
{
str+=2;
if (!cl.intermission)
cl.completed_time = cl.time;
switch(*str++)
{
case 0:
str--;
break;
case 'R': //remove intermission (no other method to do this)
cl.intermission = 0;
break;
case 'I': //regular intermission
case 'S': //show scoreboard
cl.intermission = 1;
break;
case 'F': //like svc_finale
cl.intermission = 2;
break;
default:
break; //any other flag you want
}
vid.recalc_refdef = true;
continue;
}
else if (str[1] == 'I') //title image
{
const char *e;
str+=2;
e = strchr(str, ':');
if (!e)
e = strchr(str, ' '); //probably an error
if (!e)
e = str+strlen(str)-1; //error
str = e+1;
continue;
}
else
break;
str+=2;
}
strncpy (scr_centerstring, str, sizeof(scr_centerstring)-1);
scr_centertime_off = scr_centertime.value;
scr_centertime_off = (flags&CPRINT_PERSIST)?999999:scr_centertime.value;
scr_centertime_start = cl.time;
if (*scr_centerstring && !(flags&CPRINT_PERSIST))
Con_LogCenterPrint (scr_centerstring);
// count the number of lines for centering
scr_center_lines = 1;
str = scr_centerstring;
@ -241,8 +322,12 @@ float AdaptFovx (float fov_x, float width, float height)
{
float a, x;
if (fov_x < 1 || fov_x > 179)
Sys_Error ("Bad fov: %f", fov_x);
if (cl.statsf[STAT_VIEWZOOM])
fov_x *= cl.statsf[STAT_VIEWZOOM]/255.0;
if (fov_x < 1)
fov_x = 1;
if (fov_x > 179)
fov_x = 179;
if (!scr_fov_adapt.value)
return fov_x;

View File

@ -101,7 +101,7 @@ void Sky_LoadTexture (texture_t *mt)
static byte back_data[128*128]; //FIXME: Hunk_Alloc
unsigned *rgba;
src = (byte *)mt + mt->offsets[0];
src = (byte *)(mt+1);
// extract back layer and upload
for (i=0 ; i<128 ; i++)
@ -155,6 +155,7 @@ void Sky_LoadSkyBox (const char *name)
char filename[MAX_OSPATH];
byte *data;
qboolean nonefound = true;
qboolean malloced;
if (strcmp(skybox_name, name) == 0)
return; //no change
@ -179,7 +180,7 @@ void Sky_LoadSkyBox (const char *name)
{
mark = Hunk_LowMark ();
q_snprintf (filename, sizeof(filename), "gfx/env/%s%s", name, suf[i]);
data = Image_LoadImage (filename, &width, &height);
data = Image_LoadImage (filename, &width, &height, &malloced);
if (data)
{
skybox_textures[i] = TexMgr_LoadImage (cl.worldmodel, filename, width, height, SRC_RGBA, data, filename, 0, TEXPREF_NONE);
@ -190,6 +191,8 @@ void Sky_LoadSkyBox (const char *name)
Con_Printf ("Couldn't load %s\n", filename);
skybox_textures[i] = notexture;
}
if (malloced)
free(data);
Hunk_FreeToLowMark (mark);
}

View File

@ -810,6 +810,8 @@ TexMgr_AlphaEdgeFix
eliminate pink edges on sprites, etc.
operates in place on 32bit data
spike -- small note that would be better to use premultiplied alpha to completely eliminate these skirts without the possibility of misbehaving.
===============
*/
static void TexMgr_AlphaEdgeFix (byte *data, int width, int height)
@ -1006,6 +1008,23 @@ static byte *TexMgr_PadImageH (byte *in, int width, int height, byte padbyte)
return data;
}
static byte *TexMgr_PreMultiply32(byte *in, size_t width, size_t height)
{
size_t pixels = width * height;
byte *out = (byte *) Hunk_Alloc(pixels*4);
byte *result = out;
while (pixels --> 0)
{
out[0] = (in[0]*in[3])>>8;
out[1] = (in[1]*in[3])>>8;
out[2] = (in[2]*in[3])>>8;
out[3] = in[3];
in += 4;
out += 4;
}
return result;
}
/*
================
TexMgr_LoadImage32 -- handles 32bit source data
@ -1015,6 +1034,10 @@ static void TexMgr_LoadImage32 (gltexture_t *glt, unsigned *data)
{
int internalformat, miplevel, mipwidth, mipheight, picmip;
//do this before any rescaling
if (glt->flags & TEXPREF_PREMULTIPLY)
data = (unsigned*)TexMgr_PreMultiply32((byte*)data, glt->width, glt->height);
if (!gl_texture_NPOT)
{
// resample up
@ -1156,7 +1179,7 @@ static void TexMgr_LoadImage8 (gltexture_t *glt, byte *data)
data = (byte *)TexMgr_8to32(data, glt->width * glt->height, usepal);
// fix edges
if (glt->flags & TEXPREF_ALPHA)
if ((glt->flags & TEXPREF_ALPHA) && !(glt->flags & TEXPREF_PREMULTIPLY))
TexMgr_AlphaEdgeFix (data, glt->width, glt->height);
else
{
@ -1196,6 +1219,7 @@ gltexture_t *TexMgr_LoadImage (qmodel_t *owner, const char *name, int width, int
unsigned short crc;
gltexture_t *glt;
int mark;
qboolean malloced;
if (isDedicated)
return NULL;
@ -1212,6 +1236,7 @@ gltexture_t *TexMgr_LoadImage (qmodel_t *owner, const char *name, int width, int
case SRC_RGBA:
crc = CRC_Block(data, width * height * 4);
break;
case SRC_EXTERNAL:
default: /* not reachable but avoids compiler warnings */
crc = 0;
}
@ -1249,6 +1274,24 @@ gltexture_t *TexMgr_LoadImage (qmodel_t *owner, const char *name, int width, int
case SRC_LIGHTMAP:
TexMgr_LoadLightmap (glt, data);
break;
case SRC_EXTERNAL:
data = Image_LoadImage (glt->source_file, (int *)&glt->source_width, (int *)&glt->source_height, &malloced); //simple file
if (!data)
{
glt->source_width = glt->source_height = 1;
glt->width = glt->source_width;
glt->height = glt->source_height;
TexMgr_LoadImage8 (glt, (byte*)"\x07");
}
else
{
glt->width = glt->source_width;
glt->height = glt->source_height;
TexMgr_LoadImage32 (glt, (unsigned *)data);
if (malloced)
free(data);
}
break;
case SRC_RGBA:
TexMgr_LoadImage32 (glt, (unsigned *)data);
break;
@ -1277,6 +1320,7 @@ void TexMgr_ReloadImage (gltexture_t *glt, int shirt, int pants)
byte translation[256];
byte *src, *dst, *data = NULL, *translated;
int mark, size, i;
qboolean malloced = false;
//
// get source data
//
@ -1302,7 +1346,7 @@ void TexMgr_ReloadImage (gltexture_t *glt, int shirt, int pants)
fclose (f);
}
else if (glt->source_file[0] && !glt->source_offset)
data = Image_LoadImage (glt->source_file, (int *)&glt->source_width, (int *)&glt->source_height); //simple file
data = Image_LoadImage (glt->source_file, (int *)&glt->source_width, (int *)&glt->source_height, &malloced); //simple file
else if (!glt->source_file[0] && glt->source_offset)
data = (byte *) glt->source_offset; //image in memory
@ -1382,11 +1426,14 @@ invalid:
case SRC_LIGHTMAP:
TexMgr_LoadLightmap (glt, data);
break;
case SRC_EXTERNAL:
case SRC_RGBA:
TexMgr_LoadImage32 (glt, (unsigned *)data);
break;
}
if (malloced)
free(data);
Hunk_FreeToLowMark(mark);
}

View File

@ -38,8 +38,9 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#define TEXPREF_NOBRIGHT 0x0200 // use nobright mask palette
#define TEXPREF_CONCHARS 0x0400 // use conchars palette
#define TEXPREF_WARPIMAGE 0x0800 // resize this texture when warpimagesize changes
#define TEXPREF_PREMULTIPLY 0x1000 // rgb = rgb*a; a=a;
enum srcformat {SRC_INDEXED, SRC_LIGHTMAP, SRC_RGBA};
enum srcformat {SRC_INDEXED, SRC_LIGHTMAP, SRC_RGBA, SRC_EXTERNAL};
typedef uintptr_t src_offset_t;

View File

@ -607,7 +607,7 @@ static qboolean VID_SetMode (int width, int height, int refreshrate, int bpp, qb
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, fsaa > 0 ? 1 : 0);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, fsaa);
q_snprintf(caption, sizeof(caption), "QuakeSpasm " QUAKESPASM_VER_STRING);
q_snprintf(caption, sizeof(caption), ENGINE_NAME_AND_VER);
#if defined(USE_SDL2)
/* Create the window if needed, hidden */
@ -617,6 +617,8 @@ static qboolean VID_SetMode (int width, int height, int refreshrate, int bpp, qb
if (vid_borderless.value)
flags |= SDL_WINDOW_BORDERLESS;
else if (!fullscreen)
flags |= SDL_WINDOW_RESIZABLE;
draw_context = SDL_CreateWindow (caption, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, flags);
if (!draw_context) { // scale back fsaa
@ -1290,10 +1292,11 @@ static void GL_SetupState (void)
glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); //johnfitz
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
//spike -- these are invalid as there is no texture bound to receive this state.
//glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
//glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
//glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
//glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glDepthRange (0, 1); //johnfitz -- moved here becuase gl_ztrick is gone.
glDepthFunc (GL_LEQUAL); //johnfitz -- moved here becuase gl_ztrick is gone.
}

View File

@ -99,6 +99,35 @@ typedef struct particle_s
ptype_t type;
} particle_t;
#define P_INVALID -1
#ifdef PSET_SCRIPT
void PScript_InitParticles (void);
void PScript_Shutdown (void);
void PScript_DrawParticles (void);
struct trailstate_s;
int PScript_ParticleTrail (vec3_t startpos, vec3_t end, int type, int dlkey, vec3_t axis[3], struct trailstate_s **tsk);
int PScript_RunParticleEffectState (vec3_t org, vec3_t dir, float count, int typenum, struct trailstate_s **tsk);
void PScript_RunParticleWeather(vec3_t minb, vec3_t maxb, vec3_t dir, float count, int colour, const char *efname);
void PScript_EmitSkyEffectTris(qmodel_t *mod, msurface_t *fa, int ptype);
int PScript_FindParticleType(const char *fullname);
int PScript_RunParticleEffectTypeString (vec3_t org, vec3_t dir, float count, const char *name);
int PScript_EntParticleTrail(vec3_t oldorg, entity_t *ent, const char *name);
int PScript_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count);
void PScript_DelinkTrailstate(struct trailstate_s **tsk);
void PScript_ClearParticles (void);
void PScript_UpdateModelEffects(qmodel_t *mod);
void PScript_ClearSurfaceParticles(qmodel_t *mod); //model is being unloaded.
#else
#define PScript_RunParticleEffectState(o,d,c,t,s) true
#define PScript_RunParticleEffectTypeString(o,d,c,n) true //just unconditionally returns an error
#define PScript_EntParticleTrail(o,e,n) true
#define PScript_ParticleTrail(o,e,t,d,a,s) true
#define PScript_EntParticleTrail(o,e,n) true
#define PScript_RunParticleEffect(o,d,p,c) true
#define PScript_RunParticleWeather(min,max,d,c,p,n) true
#define PScript_ClearSurfaceParticles(m)
#define PScript_DelinkTrailstate(tsp)
#endif
//====================================================
@ -283,7 +312,6 @@ extern devstats_t dev_stats, dev_peakstats;
//ohnfitz -- reduce overflow warning spam
typedef struct {
double packetsize;
double efrags;
double beams;
double varstring;
} overflowtimes_t;
@ -323,6 +351,7 @@ typedef struct glsl_attrib_binding_s {
} glsl_attrib_binding_t;
extern float map_wateralpha, map_lavaalpha, map_telealpha, map_slimealpha; //ericw
extern float map_fallbackalpha; //spike -- because we might want r_wateralpha to apply to teleporters while water itself wasn't watervised
//johnfitz -- fog functions called from outside gl_fog.c
void Fog_ParseServerMessage (void);
@ -336,6 +365,7 @@ void Fog_SetupFrame (void);
void Fog_NewMap (void);
void Fog_Init (void);
void Fog_SetupState (void);
const char *Fog_GetFogCommand(void); //for demo recording
void R_NewGame (void);
@ -345,7 +375,7 @@ void R_CullSurfaces (void);
qboolean R_CullBox (vec3_t emins, vec3_t emaxs);
void R_StoreEfrags (efrag_t **ppefrag);
qboolean R_CullModelForEntity (entity_t *e);
void R_RotateForEntity (vec3_t origin, vec3_t angles);
void R_RotateForEntity (vec3_t origin, vec3_t angles, unsigned char scale);
void R_MarkLights (dlight_t *light, int num, mnode_t *node);
void R_InitParticles (void);
@ -370,13 +400,14 @@ void GL_DeleteBModelVertexBuffer (void);
void GL_BuildBModelVertexBuffer (void);
void GLMesh_LoadVertexBuffers (void);
void GLMesh_DeleteVertexBuffers (void);
void GLMesh_LoadVertexBuffer (qmodel_t *m, aliashdr_t *hdr);
void R_RebuildAllLightmaps (void);
int R_LightPoint (vec3_t p);
void GL_SubdivideSurface (msurface_t *fa);
void R_BuildLightMap (msurface_t *surf, byte *dest, int stride);
void R_RenderDynamicLightmaps (msurface_t *fa);
void R_BuildLightMap (qmodel_t *model, msurface_t *surf, byte *dest, int stride);
void R_RenderDynamicLightmaps (qmodel_t *model, msurface_t *fa);
void R_UploadLightmaps (void);
void R_DrawWorld_ShowTris (void);

View File

@ -61,7 +61,7 @@ cvar_t host_framerate = {"host_framerate","0",CVAR_NONE}; // set for slow motion
cvar_t host_speeds = {"host_speeds","0",CVAR_NONE}; // set for running times
cvar_t host_maxfps = {"host_maxfps", "72", CVAR_ARCHIVE}; //johnfitz
cvar_t host_timescale = {"host_timescale", "0", CVAR_NONE}; //johnfitz
cvar_t max_edicts = {"max_edicts", "8192", CVAR_NONE}; //johnfitz //ericw -- changed from 2048 to 8192, removed CVAR_ARCHIVE
cvar_t max_edicts = {"max_edicts", "15000", CVAR_NONE}; //johnfitz //ericw -- changed from 2048 to 8192, removed CVAR_ARCHIVE
cvar_t sys_ticrate = {"sys_ticrate","0.05",CVAR_NONE}; // dedicated server
cvar_t serverprofile = {"serverprofile","0",CVAR_NONE};
@ -162,6 +162,8 @@ void Host_Error (const char *error, ...)
va_end (argptr);
Con_Printf ("Host_Error: %s\n",string);
Con_Redirect(NULL);
if (sv.active)
Host_ShutdownServer (false);
@ -434,16 +436,22 @@ void SV_DropClient (qboolean crash)
NET_Close (host_client->netconnection);
host_client->netconnection = NULL;
SVFTE_DestroyFrames(host_client); //release any delta state
// free the client (the body stays around)
host_client->active = false;
host_client->name[0] = 0;
host_client->old_frags = -999999;
net_activeconnections--;
if (host_client->download.file)
fclose(host_client->download.file);
memset(&host_client->download, 0, sizeof(host_client->download));
// send notification to all clients
for (i = 0, client = svs.clients; i < svs.maxclients; i++, client++)
{
if (!client->active)
if (!client->knowntoqc)
continue;
MSG_WriteByte (&client->message, svc_updatename);
MSG_WriteByte (&client->message, host_client - svs.clients);
@ -488,7 +496,7 @@ void Host_ShutdownServer(qboolean crash)
count = 0;
for (i=0, host_client = svs.clients ; i<svs.maxclients ; i++, host_client++)
{
if (host_client->active && host_client->message.cursize)
if (host_client->active && host_client->message.cursize && host_client->netconnection)
{
if (NET_CanSendMessage (host_client->netconnection))
{
@ -520,6 +528,8 @@ void Host_ShutdownServer(qboolean crash)
if (host_client->active)
SV_DropClient(crash);
sv.worldmodel = NULL;
//
// clear structures
//
@ -545,6 +555,9 @@ void Host_ClearMemory (void)
Hunk_FreeToLowMark (host_hunklevel);
cls.signon = 0;
free(sv.edicts); // ericw -- sv.edicts switched to use malloc()
free(cl.static_entities);
free(sv.static_entities); //spike -- this is dynamic too, now
free(sv.ambientsounds);
memset (&sv, 0, sizeof(sv));
memset (&cl, 0, sizeof(cl));
}
@ -571,7 +584,7 @@ qboolean Host_FilterTime (float time)
//johnfitz -- max fps cvar
maxfps = CLAMP (10.0, host_maxfps.value, 1000.0);
if (!cls.timedemo && realtime - oldrealtime < 1.0/maxfps)
if (host_maxfps.value && !cls.timedemo && realtime - oldrealtime < 1.0/maxfps)
return false; // framerate is too high
//johnfitz
@ -584,7 +597,7 @@ qboolean Host_FilterTime (float time)
//johnfitz
else if (host_framerate.value > 0)
host_frametime = host_framerate.value;
else // don't allow really long or short frames
else if (host_maxfps.value)// don't allow really long or short frames
host_frametime = CLAMP (0.001, host_frametime, 0.1); //johnfitz -- use CLAMP
return true;
@ -697,6 +710,18 @@ void _Host_Frame (float time)
NET_Poll();
if (cl.sendprespawn)
{
if (CL_CheckDownloads())
{
cl.sendprespawn = false;
MSG_WriteByte (&cls.message, clc_stringcmd);
MSG_WriteString (&cls.message, "prespawn");
}
else if (!cls.message.cursize)
MSG_WriteByte (&cls.message, clc_nop);
}
// if running the server locally, make intentions now
if (sv.active)
CL_SendCmd ();
@ -874,8 +899,13 @@ void Host_Init (void)
host_initialized = true;
Con_Printf ("\n========= Quake Initialized =========\n\n");
//spike -- create these aliases, because they're useful.
Cbuf_AddText ("alias startmap_sp \"map start\"\n");
Cbuf_AddText ("alias startmap_dm \"map start\"\n");
if (cls.state != ca_dedicated)
{
Cbuf_AddText ("cl_warncmd 0\n");
Cbuf_InsertText ("exec quake.rc\n");
// johnfitz -- in case the vid mode was locked during vid_init, we can unlock it now.
// note: two leading newlines because the command buffer swallows one of them.
@ -884,11 +914,15 @@ void Host_Init (void)
if (cls.state == ca_dedicated)
{
Cbuf_AddText ("cl_warncmd 0\n");
Cbuf_AddText ("exec default.cfg\n"); //spike -- someone decided that quake.rc shouldn't be execed on dedicated servers, but that means you'll get bad defaults
Cbuf_AddText ("cl_warncmd 1\n");
Cbuf_AddText ("exec server.cfg\n"); //spike -- for people who want things explicit.
Cbuf_AddText ("exec autoexec.cfg\n");
Cbuf_AddText ("stuffcmds");
Cbuf_Execute ();
if (!sv.active)
Cbuf_AddText ("map start\n");
Cbuf_AddText ("startmap_dm\n");
}
}

View File

@ -427,7 +427,7 @@ void Host_Status_f (void)
int seconds;
int minutes;
int hours = 0;
int j;
int j, i;
if (cmd_source == src_command)
{
@ -442,18 +442,42 @@ void Host_Status_f (void)
print_fn = SV_ClientPrintf;
print_fn ( "host: %s\n", Cvar_VariableString ("hostname"));
print_fn ("version: %4.2f\n", VERSION);
if (tcpipAvailable)
print_fn ("tcp/ip: %s\n", my_tcpip_address);
print_fn ( "version: "ENGINE_NAME_AND_VER"\n");
if (ipv4Available)
print_fn ("tcp/ip: %s\n", my_ipv4_address); //Spike -- FIXME: we should really have ports displayed here or something
if (ipv6Available)
print_fn ("ipv6: %s\n", my_ipv6_address);
if (ipxAvailable)
print_fn ("ipx: %s\n", my_ipx_address);
print_fn ( "map: %s\n", sv.name);
for (i = 1,j=0; i < MAX_MODELS; i++)
if (sv.model_precache[i])
j++;
print_fn ( "models: %i/%i\n", j, MAX_MODELS-1);
for (i = 1,j=0; i < MAX_SOUNDS; i++)
if (sv.sound_precache[i])
j++;
print_fn ( "sounds: %i/%i\n", j, MAX_SOUNDS-1);
for (i = 0,j=0; i < MAX_PARTICLETYPES; i++)
if (sv.particle_precache[i])
j++;
if (j)
print_fn ( "effects: %i/%i\n", j, MAX_PARTICLETYPES-1);
for (i = 1,j=1; i < sv.num_edicts; i++)
if (!sv.edicts[i].free)
j++;
print_fn ( "entities:%i/%i\n", j, sv.max_edicts);
print_fn ( "players: %i active (%i max)\n\n", net_activeconnections, svs.maxclients);
for (j = 0, client = svs.clients; j < svs.maxclients; j++, client++)
{
if (!client->active)
continue;
if (client->netconnection)
seconds = (int)(net_time - NET_QSocketGetTime(client->netconnection));
else
seconds = 0;
minutes = seconds / 60;
if (minutes)
{
@ -465,7 +489,10 @@ void Host_Status_f (void)
else
hours = 0;
print_fn ("#%-2u %-16.16s %3i %2i:%02i:%02i\n", j+1, client->name, (int)client->edict->v.frags, hours, minutes, seconds);
print_fn (" %s\n", NET_QSocketGetAddressString(client->netconnection));
if (cmd_source == src_command)
print_fn (" %s\n", client->netconnection?NET_QSocketGetTrueAddressString(client->netconnection):"botclient");
else
print_fn (" %s\n", client->netconnection?NET_QSocketGetMaskedAddressString(client->netconnection):"botclient");
}
}
@ -752,7 +779,7 @@ void Host_Ping_f (void)
SV_ClientPrintf ("Client ping times:\n");
for (i = 0, client = svs.clients; i < svs.maxclients; i++, client++)
{
if (!client->active)
if (!client->spawned || !client->netconnection)
continue;
total = 0;
for (j = 0; j < NUM_PING_TIMES; j++)
@ -928,11 +955,16 @@ void Host_Restart_f (void)
{
char mapname[MAX_QPATH];
if (cls.demoplayback || !sv.active)
if (cls.demoplayback)
return;
if (cmd_source != src_command)
return;
if (!sv.active)
{
if (*sv.name)
Cmd_ExecuteString(va("map \"%s\"\n", sv.name), src_command);
return;
}
q_strlcpy (mapname, sv.name, sizeof(mapname)); // mapname gets cleared in spawnserver
SV_SpawnServer (mapname);
if (!sv.active)
@ -945,14 +977,28 @@ Host_Reconnect_f
This command causes the client to wait for the signon messages again.
This is sent just before a server changes levels
for compatibility with quakeworld et al, we also allow this as a user-command to reconnect to the last server we tried, but we can only reliably do that when we're not already connected
==================
*/
void Host_Reconnect_f (void)
void Host_Reconnect_Con_f (void)
{
CL_Disconnect_f();
cls.demonum = -1; // stop demo loop in case this fails
if (cls.demoplayback)
{
CL_StopPlayback ();
CL_Disconnect ();
}
CL_EstablishConnection (NULL);
}
void Host_Reconnect_Sv_f (void)
{
if (cls.demoplayback) // cross-map demo playback fix from Baker
return;
SCR_BeginLoadingPlaque ();
cl.protocol_dpdownload = false;
cls.signon = 0; // need new connection messages
}
@ -975,7 +1021,7 @@ void Host_Connect_f (void)
}
q_strlcpy (name, Cmd_Argv(1), sizeof(name));
CL_EstablishConnection (name);
Host_Reconnect_f ();
Host_Reconnect_Sv_f ();
}
@ -1092,7 +1138,7 @@ void Host_Savegame_f (void)
// write the light styles
for (i = 0; i < MAX_LIGHTSTYLES; i++)
for (i = 0; i < MAX_LIGHTSTYLES_VANILLA; i++)
{
if (sv.lightstyles[i])
fprintf (f, "%s\n", sv.lightstyles[i]);
@ -1107,6 +1153,37 @@ void Host_Savegame_f (void)
ED_Write (f, EDICT_NUM(i));
fflush (f);
}
//add extra info (lightstyles, precaches, etc) in a way that's supposed to be compatible with DP.
//sidenote - this provides extended lightstyles and support for late precaches
//it does NOT protect against spawnfunc precache changes - we would need to include makestatics here too (and optionally baselines, or just recalculate those).
fprintf(f, "/*\n");
fprintf(f, "// QuakeSpasm extended savegame\n");
for (i = MAX_LIGHTSTYLES_VANILLA; i < MAX_LIGHTSTYLES; i++)
{
if (sv.lightstyles[i])
fprintf (f, "sv.lightstyles %i \"%s\"\n", i, sv.lightstyles[i]);
else
fprintf (f, "sv.lightstyles %i \"\"\n", i);
}
for (i = 1; i < MAX_MODELS; i++)
{
if (sv.model_precache[i])
fprintf (f, "sv.model_precache %i \"%s\"\n", i, sv.model_precache[i]);
}
for (i = 1; i < MAX_SOUNDS; i++)
{
if (sv.sound_precache[i])
fprintf (f, "sv.sound_precache %i \"%s\"\n", i, sv.sound_precache[i]);
}
for (i = 1; i < MAX_PARTICLETYPES; i++)
{
if (sv.particle_precache[i])
fprintf (f, "sv.particle_precache %i \"%s\"\n", i, sv.particle_precache[i]);
}
fprintf(f, "*/\n");
fclose (f);
Con_Printf ("done.\n");
}
@ -1205,7 +1282,7 @@ void Host_Loadgame_f (void)
// load the light styles
for (i = 0; i < MAX_LIGHTSTYLES; i++)
for (i = 0; i < MAX_LIGHTSTYLES_VANILLA; i++)
{
data = COM_ParseStringNewline (data);
sv.lightstyles[i] = (const char *)Hunk_Strdup (com_token, "lightstyles");
@ -1215,6 +1292,68 @@ void Host_Loadgame_f (void)
entnum = -1; // -1 is the globals
while (*data)
{
while (*data == ' ' || *data == '\r' || *data == '\n')
data++;
if (data[0] == '/' && data[1] == '*' && (data[2] == '\r' || data[2] == '\n'))
{ //looks like an extended saved game
char *end;
const char *ext;
ext = data+2;
while ((end = strchr(ext, '\n')))
{
*end = 0;
ext = COM_Parse(ext);
if (!strcmp(com_token, "sv.lightstyles"))
{
int idx;
ext = COM_Parse(ext);
idx = atoi(com_token);
ext = COM_Parse(ext);
if (idx >= 0 && idx < MAX_LIGHTSTYLES)
{
if (*com_token)
sv.lightstyles[idx] = (const char *)Hunk_Strdup (com_token, "lightstyles");
else
sv.lightstyles[idx] = NULL;
}
}
else if (!strcmp(com_token, "sv.model_precache"))
{
int idx;
ext = COM_Parse(ext);
idx = atoi(com_token);
ext = COM_Parse(ext);
if (idx >= 1 && idx < MAX_MODELS)
{
sv.model_precache[idx] = (const char *)Hunk_Strdup (com_token, "model_precache");
sv.models[idx] = Mod_ForName (sv.model_precache[idx], idx==1);
//if (idx == 1)
// sv.worldmodel = sv.models[idx];
}
}
else if (!strcmp(com_token, "sv.sound_precache"))
{
int idx;
ext = COM_Parse(ext);
idx = atoi(com_token);
ext = COM_Parse(ext);
if (idx >= 1 && idx < MAX_MODELS)
sv.sound_precache[idx] = (const char *)Hunk_Strdup (com_token, "sound_precache");
}
else if (!strcmp(com_token, "sv.particle_precache"))
{
int idx;
ext = COM_Parse(ext);
idx = atoi(com_token);
ext = COM_Parse(ext);
if (idx >= 1 && idx < MAX_PARTICLETYPES)
sv.particle_precache[idx] = (const char *)Hunk_Strdup (com_token, "particle_precache");
}
*end = '\n';
ext = end+1;
}
}
data = COM_Parse(data);
if (!com_token[0])
break; // end of file
@ -1259,7 +1398,7 @@ void Host_Loadgame_f (void)
if (cls.state != ca_dedicated)
{
CL_EstablishConnection ("local");
Host_Reconnect_f ();
Host_Reconnect_Sv_f ();
}
}
@ -1604,10 +1743,9 @@ void Host_PreSpawn_f (void)
return;
}
SZ_Write (&host_client->message, sv.signon.data, sv.signon.cursize);
MSG_WriteByte (&host_client->message, svc_signonnum);
MSG_WriteByte (&host_client->message, 2);
host_client->sendsignon = true;
//will start splurging out prespawn data
host_client->sendsignon = 2;
host_client->signonidx = 0;
}
/*
@ -1633,6 +1771,7 @@ void Host_Spawn_f (void)
return;
}
host_client->knowntoqc = true;
// run the entrance script
if (sv.loadgame)
{ // loaded games are fully inited allready
@ -1670,9 +1809,13 @@ void Host_Spawn_f (void)
// send time of update
MSG_WriteByte (&host_client->message, svc_time);
MSG_WriteFloat (&host_client->message, sv.time);
if (host_client->protocol_pext2 & PEXT2_PREDINFO)
MSG_WriteShort(&host_client->message, (host_client->lastmovemessage&0xffff));
for (i = 0, client = svs.clients; i < svs.maxclients; i++, client++)
{
if (!client->knowntoqc)
continue;
MSG_WriteByte (&host_client->message, svc_updatename);
MSG_WriteByte (&host_client->message, i);
MSG_WriteString (&host_client->message, client->name);
@ -1686,11 +1829,15 @@ void Host_Spawn_f (void)
// send all current light styles
for (i = 0; i < MAX_LIGHTSTYLES; i++)
{
//CL_ClearState should have cleared all lightstyles, so don't send irrelevant ones
if (sv.lightstyles[i])
{
MSG_WriteByte (&host_client->message, svc_lightstyle);
MSG_WriteByte (&host_client->message, (char)i);
MSG_WriteString (&host_client->message, sv.lightstyles[i]);
}
}
//
// send some stats
@ -1723,7 +1870,8 @@ void Host_Spawn_f (void)
MSG_WriteAngle (&host_client->message, ent->v.angles[i], sv.protocolflags );
MSG_WriteAngle (&host_client->message, 0, sv.protocolflags );
SV_WriteClientdataToMessage (sv_player, &host_client->message);
if (!(host_client->protocol_pext2 & PEXT2_REPLACEMENTDELTAS))
SV_WriteClientdataToMessage (host_client, &host_client->message);
MSG_WriteByte (&host_client->message, svc_signonnum);
MSG_WriteByte (&host_client->message, 3);
@ -1905,7 +2053,7 @@ void Host_Give_f (void)
case 's':
if (rogue)
{
val = GetEdictFieldValue(sv_player, "ammo_shells1");
val = GetEdictFieldValue(sv_player, ED_FindFieldOffset("ammo_shells1"));
if (val)
val->_float = v;
}
@ -1915,7 +2063,7 @@ void Host_Give_f (void)
case 'n':
if (rogue)
{
val = GetEdictFieldValue(sv_player, "ammo_nails1");
val = GetEdictFieldValue(sv_player, ED_FindFieldOffset("ammo_nails1"));
if (val)
{
val->_float = v;
@ -1932,7 +2080,7 @@ void Host_Give_f (void)
case 'l':
if (rogue)
{
val = GetEdictFieldValue(sv_player, "ammo_lava_nails");
val = GetEdictFieldValue(sv_player, ED_FindFieldOffset("ammo_lava_nails"));
if (val)
{
val->_float = v;
@ -1945,7 +2093,7 @@ void Host_Give_f (void)
case 'r':
if (rogue)
{
val = GetEdictFieldValue(sv_player, "ammo_rockets1");
val = GetEdictFieldValue(sv_player, ED_FindFieldOffset("ammo_rockets1"));
if (val)
{
val->_float = v;
@ -1962,7 +2110,7 @@ void Host_Give_f (void)
case 'm':
if (rogue)
{
val = GetEdictFieldValue(sv_player, "ammo_multi_rockets");
val = GetEdictFieldValue(sv_player, ED_FindFieldOffset("ammo_multi_rockets"));
if (val)
{
val->_float = v;
@ -1979,7 +2127,7 @@ void Host_Give_f (void)
case 'c':
if (rogue)
{
val = GetEdictFieldValue(sv_player, "ammo_cells1");
val = GetEdictFieldValue(sv_player, ED_FindFieldOffset("ammo_cells1"));
if (val)
{
val->_float = v;
@ -1996,7 +2144,7 @@ void Host_Give_f (void)
case 'p':
if (rogue)
{
val = GetEdictFieldValue(sv_player, "ammo_plasma");
val = GetEdictFieldValue(sv_player, ED_FindFieldOffset("ammo_plasma"));
if (val)
{
val->_float = v;
@ -2280,6 +2428,155 @@ void Host_Stopdemo_f (void)
CL_Disconnect ();
}
//=============================================================================
//download stuff
static void Host_Download_f(void)
{
const char *fname = Cmd_Argv(1);
int fsize;
if (cmd_source == src_command)
{
//FIXME: add some sort of queuing thing
// if (cls.state == ca_connected)
// Cmd_ForwardToServer ();
return;
}
else if (cmd_source == src_client)
{
if (host_client->download.file)
{ //abort the current download if the previous didn't terminate properly.
SV_ClientPrintf("cancelling previous download\n");
MSG_WriteByte (&host_client->message, svc_stufftext);
MSG_WriteString (&host_client->message, "\nstopdownload\n");
fclose(host_client->download.file);
host_client->download.file = NULL;
}
host_client->download.size = 0;
host_client->download.started = false;
host_client->download.sendpos = 0;
host_client->download.ackpos = 0;
fsize = -1;
if (!COM_DownloadNameOkay(fname))
SV_ClientPrintf("refusing download of %s - restricted filename\n", fname);
else
{
fsize = COM_FOpenFile(fname, &host_client->download.file, NULL);
if (!host_client->download.file)
SV_ClientPrintf("server does not have file %s\n", fname);
else if (file_from_pak)
{
SV_ClientPrintf("refusing download of %s from inside pak\n", fname);
fclose(host_client->download.file);
host_client->download.file = NULL;
}
else if (fsize < 0 || fsize > 50*1024*1024)
{
SV_ClientPrintf("refusing download of large file %s\n", fname);
fclose(host_client->download.file);
host_client->download.file = NULL;
}
}
host_client->download.size = (unsigned int)fsize;
if (host_client->download.file)
{
host_client->download.startpos = ftell(host_client->download.file);
Con_Printf("downloading %s to %s\n", fname, host_client->name);
MSG_WriteByte (&host_client->message, svc_stufftext);
MSG_WriteString (&host_client->message, va("\ncl_downloadbegin %u \"%s\"\n", host_client->download.size, fname));
q_strlcpy(host_client->download.name, fname, sizeof(host_client->download.name));
}
else
{
Con_Printf("refusing download of %s to %s\n", fname, host_client->name);
MSG_WriteByte (&host_client->message, svc_stufftext);
MSG_WriteString (&host_client->message, "\nstopdownload\n");
}
host_client->sendsignon = true; //override any keepalive issues.
}
}
static void Host_StartDownload_f(void)
{
if (cmd_source != src_client)
return;
if (host_client->download.file)
host_client->download.started = true;
else
SV_ClientPrintf("no download started\n");
}
//just writes download data onto the end of the outgoing unreliable buffer
void Host_AppendDownloadData(client_t *client, sizebuf_t *buf)
{
if (buf->cursize+7 > buf->maxsize)
return; //no space for anything
if (client->download.file && client->download.started)
{
byte tbuf[1400]; //don't be too aggressive, ethernet mtu is about 1450
unsigned int size = client->download.size - client->download.sendpos;
//size might be 0 at eof, and that's needed to avoid failure if we drop the last few packets
if (size > sizeof(tbuf))
size = sizeof(tbuf);
if ((int)size > buf->maxsize-(buf->cursize+7))
size = (int)(buf->maxsize-(buf->cursize+7)); //don't overflow
if (size && fread(tbuf, 1, size, host_client->download.file) < size)
client->download.ackpos = client->download.sendpos = client->download.size; //some kind of error...
else
{
MSG_WriteByte(buf, svcdp_downloaddata);
MSG_WriteLong(buf, client->download.sendpos);
MSG_WriteShort(buf, size);
SZ_Write(buf, tbuf, size);
client->download.sendpos += size;
}
}
}
//parses incoming acks from the client, so we know which parts of the file the client actually received.
void Host_DownloadAck(client_t *client)
{
unsigned int start = MSG_ReadLong();
unsigned int size = (unsigned short)MSG_ReadShort();
if (!client->download.started || !client->download.file)
return;
if (client->download.ackpos < start)
{
client->download.sendpos = client->download.ackpos;//there was a gap, rewind to the known gap
fseek(client->download.file, host_client->download.startpos+client->download.sendpos, SEEK_SET);
}
else if (client->download.ackpos < start+size)
client->download.ackpos = start+size; //no loss yet.
//else FIXME: build a log of parts known to be acked to avoid resending them later, skip past them in acks
if (client->download.ackpos == client->download.size)
{
unsigned int hash = 0;
byte *data;
client->download.started = false;
data = malloc(client->download.size);
if (data)
{
fseek(client->download.file, host_client->download.startpos, SEEK_SET);
fread(data, 1, host_client->download.size, client->download.file);
hash = CRC_Block(data, host_client->download.size);
free(data);
}
fclose(client->download.file);
client->download.file = NULL;
MSG_WriteByte (&host_client->message, svc_stufftext);
MSG_WriteString (&host_client->message, va("cl_downloadfinished %u %u \"%s\"\n", client->download.size, hash, client->download.name));
*client->download.name = 0;
host_client->sendsignon = true; //override any keepalive issues.
}
}
//=============================================================================
/*
@ -2295,34 +2592,37 @@ void Host_InitCommands (void)
Cmd_AddCommand ("mapname", Host_Mapname_f); //johnfitz
Cmd_AddCommand ("randmap", Host_Randmap_f); //ericw
Cmd_AddCommand ("status", Host_Status_f);
Cmd_AddCommand_ClientCommand ("status", Host_Status_f);
Cmd_AddCommand ("quit", Host_Quit_f);
Cmd_AddCommand ("god", Host_God_f);
Cmd_AddCommand ("notarget", Host_Notarget_f);
Cmd_AddCommand ("fly", Host_Fly_f);
Cmd_AddCommand_ClientCommand ("god", Host_God_f);
Cmd_AddCommand_ClientCommand ("notarget", Host_Notarget_f);
Cmd_AddCommand_ClientCommand ("fly", Host_Fly_f);
Cmd_AddCommand ("map", Host_Map_f);
Cmd_AddCommand ("restart", Host_Restart_f);
Cmd_AddCommand ("changelevel", Host_Changelevel_f);
Cmd_AddCommand ("connect", Host_Connect_f);
Cmd_AddCommand ("reconnect", Host_Reconnect_f);
Cmd_AddCommand ("name", Host_Name_f);
Cmd_AddCommand ("noclip", Host_Noclip_f);
Cmd_AddCommand ("setpos", Host_SetPos_f); //QuakeSpasm
Cmd_AddCommand_Console ("reconnect", Host_Reconnect_Con_f);
Cmd_AddCommand_ServerCommand ("reconnect", Host_Reconnect_Sv_f);
Cmd_AddCommand_ClientCommand ("name", Host_Name_f);
Cmd_AddCommand_ClientCommand ("noclip", Host_Noclip_f);
Cmd_AddCommand_ClientCommand ("setpos", Host_SetPos_f); //QuakeSpasm
Cmd_AddCommand ("say", Host_Say_f);
Cmd_AddCommand ("say_team", Host_Say_Team_f);
Cmd_AddCommand ("tell", Host_Tell_f);
Cmd_AddCommand ("color", Host_Color_f);
Cmd_AddCommand ("kill", Host_Kill_f);
Cmd_AddCommand ("pause", Host_Pause_f);
Cmd_AddCommand ("spawn", Host_Spawn_f);
Cmd_AddCommand ("begin", Host_Begin_f);
Cmd_AddCommand ("prespawn", Host_PreSpawn_f);
Cmd_AddCommand ("kick", Host_Kick_f);
Cmd_AddCommand ("ping", Host_Ping_f);
Cmd_AddCommand_ClientCommand ("say", Host_Say_f);
Cmd_AddCommand_ClientCommand ("say_team", Host_Say_Team_f);
Cmd_AddCommand_ClientCommand ("tell", Host_Tell_f);
Cmd_AddCommand_ClientCommand ("color", Host_Color_f);
Cmd_AddCommand_ClientCommand ("kill", Host_Kill_f);
Cmd_AddCommand_ClientCommand ("pause", Host_Pause_f);
Cmd_AddCommand_ClientCommand ("spawn", Host_Spawn_f);
Cmd_AddCommand_ClientCommand ("begin", Host_Begin_f);
Cmd_AddCommand_ClientCommand ("prespawn", Host_PreSpawn_f);
Cmd_AddCommand_ClientCommand ("kick", Host_Kick_f);
Cmd_AddCommand_ClientCommand ("ping", Host_Ping_f);
Cmd_AddCommand ("load", Host_Loadgame_f);
Cmd_AddCommand ("save", Host_Savegame_f);
Cmd_AddCommand ("give", Host_Give_f);
Cmd_AddCommand_ClientCommand ("give", Host_Give_f);
Cmd_AddCommand_ClientCommand ("download", Host_Download_f);
Cmd_AddCommand_ClientCommand ("sv_startdownload", Host_StartDownload_f);
Cmd_AddCommand ("startdemos", Host_Startdemos_f);
Cmd_AddCommand ("demos", Host_Demos_f);

View File

@ -69,6 +69,54 @@ static inline int Buf_GetC(stdio_buffer_t *buf)
return buf->buffer[buf->pos++];
}
/*small function to read files with stb_image - single-file image loader library.
** downloaded from: https://raw.githubusercontent.com/nothings/stb/master/stb_image.h
** only use jpeg+png formats, because tbh there's not much need for the others.
** */
#define STB_IMAGE_IMPLEMENTATION
#define STBI_ONLY_JPEG
#ifdef LODEPNG_NO_COMPILE_DECODER
#define STBI_ONLY_PNG
#endif
#include "stb_image.h"
static byte *Image_LoadSTBI(FILE *f, int *width, int *height)
{
int bytesPerPixel;
byte *heap = stbi_load_from_file(f, width, height, &bytesPerPixel, 4);
fclose(f);
if (heap)
{ //this is silly, but we do it for consistency.
//frankly, most people should be using tga-inside-pk3.
byte *hunk = Hunk_Alloc(*width**height*4);
memcpy(hunk, heap, *width**height*4);
free(heap);
return hunk;
}
return NULL;
}
byte *Image_LoadPNG(FILE *f, int *width, int *height, qboolean *malloced)
{
#ifdef LODEPNG_NO_COMPILE_DECODER
return Image_LoadSTBI (f, width, height);
#else
unsigned w, h;
unsigned char *out = NULL, *in;
size_t insize = com_filesize;
in = malloc(com_filesize);
if (!in)
return NULL;
if (com_filesize == fread(in, 1, com_filesize, f))
{
*malloced = true;
lodepng_decode32(&out, &w, &h, in, insize);
}
free(in);
return out;
#endif
}
/*
============
Image_LoadImage
@ -78,19 +126,44 @@ returns a pointer to hunk allocated RGBA data
TODO: search order: tga png jpg pcx lmp
============
*/
byte *Image_LoadImage (const char *name, int *width, int *height)
byte *Image_LoadImage (const char *name, int *width, int *height, qboolean *malloced)
{
FILE *f;
char *prefixes[3] = {"", "textures/", "textures/"};
int i;
q_snprintf (loadfilename, sizeof(loadfilename), "%s.tga", name);
*malloced = false;
for (i = 0; i < sizeof(prefixes)/sizeof(prefixes[0]); i++)
{
if (i == 2) //last resort...
name = COM_SkipPath(name);
q_snprintf (loadfilename, sizeof(loadfilename), "%s%s.tga", prefixes[i], name);
COM_FOpenFile (loadfilename, &f, NULL);
if (f)
return Image_LoadTGA (f, width, height);
q_snprintf (loadfilename, sizeof(loadfilename), "%s.pcx", name);
q_snprintf (loadfilename, sizeof(loadfilename), "%s%s.png", prefixes[i], name);
COM_FOpenFile (loadfilename, &f, NULL);
if (f)
return Image_LoadPNG (f, width, height, malloced);
q_snprintf (loadfilename, sizeof(loadfilename), "%s%s.jpeg", prefixes[i], name);
COM_FOpenFile (loadfilename, &f, NULL);
if (f)
return Image_LoadSTBI (f, width, height);
q_snprintf (loadfilename, sizeof(loadfilename), "%s%s.jpg", prefixes[i], name);
COM_FOpenFile (loadfilename, &f, NULL);
if (f)
return Image_LoadSTBI (f, width, height);
q_snprintf (loadfilename, sizeof(loadfilename), "%s%s.pcx", prefixes[i], name);
COM_FOpenFile (loadfilename, &f, NULL);
if (f)
return Image_LoadPCX (f, width, height);
}
return NULL;
}

View File

@ -28,7 +28,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//be sure to free the hunk after using these loading functions
byte *Image_LoadTGA (FILE *f, int *width, int *height);
byte *Image_LoadPCX (FILE *f, int *width, int *height);
byte *Image_LoadImage (const char *name, int *width, int *height);
byte *Image_LoadImage (const char *name, int *width, int *height, qboolean *malloced);
qboolean Image_WriteTGA (const char *name, byte *data, int width, int height, int bpp, qboolean upsidedown);
qboolean Image_WritePNG (const char *name, byte *data, int width, int height, int bpp, qboolean upsidedown);

View File

@ -1014,6 +1014,12 @@ void IN_SendKeyEvents (void)
S_UnblockSound();
else if (event.window.event == SDL_WINDOWEVENT_FOCUS_LOST)
S_BlockSound();
else if (event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED)
{
vid.width = event.window.data1;
vid.height = event.window.data2;
Cvar_FindVar("scr_conscale")->callback(NULL);
}
break;
#else
case SDL_ACTIVEEVENT:

View File

@ -31,7 +31,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
char key_lines[CMDLINES][MAXCMDLINE];
int key_linepos;
int key_insert; //johnfitz -- insert key toggle (for editing)
int key_insert = true; //johnfitz -- insert key toggle (for editing)
double key_blinktime; //johnfitz -- fudge cursor blinking to make it easier to spot in certain cases
int edit_line = 0;

View File

@ -136,6 +136,7 @@ int main(int argc, char *argv[])
Sys_Printf("FitzQuake %1.2f (c) John Fitzgibbons\n", FITZQUAKE_VERSION);
Sys_Printf("FitzQuake SDL port (c) SleepwalkR, Baker\n");
Sys_Printf("QuakeSpasm " QUAKESPASM_VER_STRING " (c) Ozkan Sezer, Eric Wasylishen & others\n");
Sys_Printf("QuakeSpasm-Spiked (c) Spike\n");
Sys_Printf("Host_Init\n");
Host_Init();

View File

@ -211,18 +211,47 @@ int BoxOnPlaneSide (vec3_t emins, vec3_t emaxs, mplane_t *p)
}
//johnfitz -- the opposite of AngleVectors. this takes forward and generates pitch yaw roll
//TODO: take right and up vectors to properly set yaw and roll
void VectorAngles (const vec3_t forward, vec3_t angles)
//Spike: take right and up vectors to properly set yaw and roll
void VectorAngles (const vec3_t forward, float *up, vec3_t angles)
{
vec3_t temp;
temp[0] = forward[0];
temp[1] = forward[1];
temp[2] = 0;
angles[PITCH] = -atan2(forward[2], VectorLength(temp)) / M_PI_DIV_180;
angles[YAW] = atan2(forward[1], forward[0]) / M_PI_DIV_180;
if (forward[0] == 0 && forward[1] == 0)
{ //either vertically up or down
if (forward[2] > 0)
{
angles[PITCH] = -90;
angles[YAW] = up ? atan2(-up[1], -up[0]) / M_PI_DIV_180: 0;
}
else
{
angles[PITCH] = 90;
angles[YAW] = up ? atan2(up[1], up[0]) / M_PI_DIV_180: 0;
}
angles[ROLL] = 0;
}
else
{
angles[PITCH] = -atan2(forward[2], sqrt(DotProduct2(forward,forward)));
angles[YAW] = atan2(forward[1], forward[0]);
if (up)
{
vec_t cp = cos(angles[PITCH]), sp = sin(angles[PITCH]);
vec_t cy = cos(angles[YAW]), sy = sin(angles[YAW]);
vec3_t tleft, tup;
tleft[0] = -sy;
tleft[1] = cy;
tleft[2] = 0;
tup[0] = sp*cy;
tup[1] = sp*sy;
tup[2] = cp;
angles[ROLL] = -atan2(DotProduct(up, tleft), DotProduct(up, tup)) / M_PI_DIV_180;
}
else angles[ROLL] = 0;
angles[PITCH] /= M_PI_DIV_180;
angles[YAW] /= M_PI_DIV_180;
}
}
void AngleVectors (vec3_t angles, vec3_t forward, vec3_t right, vec3_t up)
{
@ -250,7 +279,7 @@ void AngleVectors (vec3_t angles, vec3_t forward, vec3_t right, vec3_t up)
up[2] = cr*cp;
}
int VectorCompare (vec3_t v1, vec3_t v2)
int VectorCompare (const vec3_t v1, const vec3_t v2)
{
int i;
@ -261,7 +290,7 @@ int VectorCompare (vec3_t v1, vec3_t v2)
return 1;
}
void VectorMA (vec3_t veca, float scale, vec3_t vecb, vec3_t vecc)
void VectorMA (const vec3_t veca, float scale, const vec3_t vecb, vec3_t vecc)
{
vecc[0] = veca[0] + scale*vecb[0];
vecc[1] = veca[1] + scale*vecb[1];
@ -269,40 +298,40 @@ void VectorMA (vec3_t veca, float scale, vec3_t vecb, vec3_t vecc)
}
vec_t _DotProduct (vec3_t v1, vec3_t v2)
vec_t _DotProduct (const vec3_t v1, const vec3_t v2)
{
return v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2];
}
void _VectorSubtract (vec3_t veca, vec3_t vecb, vec3_t out)
void _VectorSubtract (const vec3_t veca, const vec3_t vecb, vec3_t out)
{
out[0] = veca[0]-vecb[0];
out[1] = veca[1]-vecb[1];
out[2] = veca[2]-vecb[2];
}
void _VectorAdd (vec3_t veca, vec3_t vecb, vec3_t out)
void _VectorAdd (const vec3_t veca, const vec3_t vecb, vec3_t out)
{
out[0] = veca[0]+vecb[0];
out[1] = veca[1]+vecb[1];
out[2] = veca[2]+vecb[2];
}
void _VectorCopy (vec3_t in, vec3_t out)
void _VectorCopy (const vec3_t in, vec3_t out)
{
out[0] = in[0];
out[1] = in[1];
out[2] = in[2];
}
void CrossProduct (vec3_t v1, vec3_t v2, vec3_t cross)
void CrossProduct (const vec3_t v1, const vec3_t v2, vec3_t cross)
{
cross[0] = v1[1]*v2[2] - v1[2]*v2[1];
cross[1] = v1[2]*v2[0] - v1[0]*v2[2];
cross[2] = v1[0]*v2[1] - v1[1]*v2[0];
}
vec_t VectorLength(vec3_t v)
vec_t VectorLength(const vec3_t v)
{
return sqrt(DotProduct(v,v));
}
@ -332,7 +361,7 @@ void VectorInverse (vec3_t v)
v[2] = -v[2];
}
void VectorScale (vec3_t in, vec_t scale, vec3_t out)
void VectorScale (const vec3_t in, vec_t scale, vec3_t out)
{
out[0] = in[0]*scale;
out[1] = in[1]*scale;

View File

@ -52,6 +52,7 @@ static inline int IS_NAN (float x) {
#define Q_rint(x) ((x) > 0 ? (int)((x) + 0.5) : (int)((x) - 0.5)) //johnfitz -- from joequake
#define DotProduct(x,y) (x[0]*y[0]+x[1]*y[1]+x[2]*y[2])
#define DotProduct2(x,y) (x[0]*y[0]+x[1]*y[1])
#define DoublePrecisionDotProduct(x,y) ((double)x[0]*y[0]+(double)x[1]*y[1]+(double)x[2]*y[2])
#define VectorSubtract(a,b,c) {c[0]=a[0]-b[0];c[1]=a[1]-b[1];c[2]=a[2]-b[2];}
#define VectorAdd(a,b,c) {c[0]=a[0]+b[0];c[1]=a[1]+b[1];c[2]=a[2]+b[2];}
@ -72,21 +73,21 @@ static inline int IS_NAN (float x) {
}
void TurnVector (vec3_t out, const vec3_t forward, const vec3_t side, float angle); //johnfitz
void VectorAngles (const vec3_t forward, vec3_t angles); //johnfitz
void VectorAngles (const vec3_t forward, float *up, vec3_t angles); //johnfitz, spike(up is optional)
void VectorMA (vec3_t veca, float scale, vec3_t vecb, vec3_t vecc);
void VectorMA (const vec3_t veca, float scale, const vec3_t vecb, vec3_t vecc);
vec_t _DotProduct (vec3_t v1, vec3_t v2);
void _VectorSubtract (vec3_t veca, vec3_t vecb, vec3_t out);
void _VectorAdd (vec3_t veca, vec3_t vecb, vec3_t out);
void _VectorCopy (vec3_t in, vec3_t out);
vec_t _DotProduct (const vec3_t v1, const vec3_t v2);
void _VectorSubtract (const vec3_t veca, const vec3_t vecb, vec3_t out);
void _VectorAdd (const vec3_t veca, const vec3_t vecb, vec3_t out);
void _VectorCopy (const vec3_t in, vec3_t out);
int VectorCompare (vec3_t v1, vec3_t v2);
vec_t VectorLength (vec3_t v);
void CrossProduct (vec3_t v1, vec3_t v2, vec3_t cross);
int VectorCompare (const vec3_t v1, const vec3_t v2);
vec_t VectorLength (const vec3_t v);
void CrossProduct (const vec3_t v1, const vec3_t v2, vec3_t cross);
float VectorNormalize (vec3_t v); // returns vector length
void VectorInverse (vec3_t v);
void VectorScale (vec3_t in, vec_t scale, vec3_t out);
void VectorScale (const vec3_t in, vec_t scale, vec3_t out);
int Q_log2(int val);
void R_ConcatRotations (float in1[3][3], float in2[3][3], float out[3][3]);

View File

@ -38,7 +38,7 @@ void M_Menu_Main_f (void);
void M_Menu_Net_f (void);
void M_Menu_LanConfig_f (void);
void M_Menu_GameOptions_f (void);
void M_Menu_Search_f (void);
void M_Menu_Search_f (enum slistScope_e scope);
void M_Menu_ServerList_f (void);
void M_Menu_Options_f (void);
void M_Menu_Keys_f (void);
@ -398,9 +398,10 @@ void M_SinglePlayer_Key (int key)
if (sv.active)
Cbuf_AddText ("disconnect\n");
Cbuf_AddText ("maxplayers 1\n");
Cbuf_AddText ("samelevel 0\n"); //spike -- you'd be amazed how many qw players have this setting breaking their singleplayer experience...
Cbuf_AddText ("deathmatch 0\n"); //johnfitz
Cbuf_AddText ("coop 0\n"); //johnfitz
Cbuf_AddText ("map start\n");
Cbuf_AddText ("startmap_sp\n");
break;
case 1:
@ -625,7 +626,7 @@ void M_MultiPlayer_Draw (void)
M_DrawTransPic (54, 32 + m_multiplayer_cursor * 20,Draw_CachePic( va("gfx/menudot%i.lmp", f+1 ) ) );
if (ipxAvailable || tcpipAvailable)
if (ipxAvailable || ipv4Available || ipv6Available)
return;
M_PrintWhite ((320/2) - ((27*8)/2), 148, "No Communications Available");
}
@ -659,12 +660,12 @@ void M_MultiPlayer_Key (int key)
switch (m_multiplayer_cursor)
{
case 0:
if (ipxAvailable || tcpipAvailable)
if (ipxAvailable || ipv4Available || ipv6Available)
M_Menu_Net_f ();
break;
case 1:
if (ipxAvailable || tcpipAvailable)
if (ipxAvailable || ipv4Available || ipv6Available)
M_Menu_Net_f ();
break;
@ -913,7 +914,7 @@ void M_Net_Draw (void)
M_DrawTransPic (72, f, p);
f += 19;
if (tcpipAvailable)
if (ipv4Available || ipv6Available)
p = Draw_CachePic ("gfx/netmen4.lmp");
else
p = Draw_CachePic ("gfx/dim_tcp.lmp");
@ -964,7 +965,7 @@ again:
if (m_net_cursor == 0 && !ipxAvailable)
goto again;
if (m_net_cursor == 1 && !tcpipAvailable)
if (m_net_cursor == 1 && !(ipv4Available || ipv6Available))
goto again;
}
@ -1333,7 +1334,7 @@ void M_Options_Key (int k)
//=============================================================================
/* KEYS MENU */
const char *bindnames[][2] =
const char *quakebindnames[][2] =
{
{"+attack", "attack"},
{"impulse 10", "next weapon"},
@ -1353,20 +1354,87 @@ const char *bindnames[][2] =
{"+mlook", "mouse look"},
{"+klook", "keyboard look"},
{"+moveup", "swim up"},
{"+movedown", "swim down"}
{"+movedown", "swim down"},
{"+voip", "Voice Chat"}
};
#define NUMQUAKECOMMANDS (sizeof(quakebindnames)/sizeof(quakebindnames[0]))
#define NUMCOMMANDS (sizeof(bindnames)/sizeof(bindnames[0]))
static struct
{
char *cmd;
char *desc;
} *bindnames;
static size_t numbindnames;
static int keys_cursor;
static size_t keys_first;
static size_t keys_cursor;
static qboolean bind_grab;
void M_Keys_Close (void)
{
while (numbindnames>0)
{
numbindnames--;
Z_Free(bindnames[numbindnames].cmd);
Z_Free(bindnames[numbindnames].desc);
}
Z_Free(bindnames);
bindnames = NULL;
numbindnames = 0;
}
void M_Keys_Populate(void)
{
FILE *file;
char line[1024];
if (numbindnames)
return;
if (COM_FOpenFile("bindlist.lst", &file, NULL) >= 0)
{
while (fgets(line, sizeof(line), file))
{
const char *cmd, *desc/*, tip*/;
Cmd_TokenizeString(line);
cmd = Cmd_Argv(0);
desc = Cmd_Argv(1);
/*tip = Cmd_Argv(2); unused in quakespasm*/
if (*cmd)
{
bindnames = Z_Realloc(bindnames, sizeof(*bindnames)*(numbindnames+1));
bindnames[numbindnames].cmd = strcpy(Z_Malloc(strlen(cmd)+1), cmd);
bindnames[numbindnames].desc = strcpy(Z_Malloc(strlen(desc)+1), desc);
numbindnames++;
}
}
fclose(file);
}
if (!numbindnames)
{
bindnames = Z_Realloc(bindnames, sizeof(*bindnames)*NUMQUAKECOMMANDS);
while(numbindnames < NUMQUAKECOMMANDS)
{
bindnames[numbindnames].cmd = strcpy(Z_Malloc(strlen(quakebindnames[numbindnames][0])+1), quakebindnames[numbindnames][0]);
bindnames[numbindnames].desc = strcpy(Z_Malloc(strlen(quakebindnames[numbindnames][1])+1), quakebindnames[numbindnames][1]);
numbindnames++;
}
}
//don't start with it on text
keys_first = keys_cursor = 0;
while (keys_cursor < numbindnames-1 && !strcmp(bindnames[keys_cursor].cmd, "-"))
keys_cursor++;
}
void M_Menu_Keys_f (void)
{
IN_Deactivate(modestate == MS_WINDOWED);
key_dest = key_menu;
m_state = m_keys;
m_entersound = true;
M_Keys_Populate();
}
@ -1418,10 +1486,12 @@ extern qpic_t *pic_up, *pic_down;
void M_Keys_Draw (void)
{
int i, x, y;
size_t i;
int x, y;
int keys[3];
const char *name;
qpic_t *p;
size_t keys_shown;
p = Draw_CachePic ("gfx/ttl_cstm.lmp");
M_DrawPic ( (320-p->width)/2, 4, p);
@ -1431,14 +1501,28 @@ void M_Keys_Draw (void)
else
M_Print (18, 32, "Enter to change, backspace to clear");
keys_shown = numbindnames;
if (keys_shown > (200-48)/8)
keys_shown = (200-48)/8;
if (keys_first+keys_shown-1 < keys_cursor)
keys_first = keys_cursor-(keys_shown-1);
if (keys_first > keys_cursor)
keys_first = keys_cursor;
// search for known bindings
for (i = 0; i < (int)NUMCOMMANDS; i++)
for (i = keys_first; i < keys_first+keys_shown; i++)
{
y = 48 + 8*i;
y = 48 + 8*(i-keys_first);
M_Print (16, y, bindnames[i][1]);
if (!strcmp(bindnames[i].cmd, "-"))
{
M_PrintWhite ((320-strlen(bindnames[i].desc)*8)/2, y, bindnames[i].desc);
continue;
}
M_FindKeysForCommand (bindnames[i][0], keys);
M_Print (16, y, bindnames[i].desc);
M_FindKeysForCommand (bindnames[i].cmd, keys);
if (keys[0] == -1)
{
@ -1465,9 +1549,9 @@ void M_Keys_Draw (void)
}
if (bind_grab)
M_DrawCharacter (130, 48 + keys_cursor*8, '=');
M_DrawCharacter (130, 48 + (keys_cursor-keys_first)*8, '=');
else
M_DrawCharacter (130, 48 + keys_cursor*8, 12+((int)(realtime*4)&1));
M_DrawCharacter (130, 48 + (keys_cursor-keys_first)*8, 12+((int)(realtime*4)&1));
}
@ -1481,7 +1565,7 @@ void M_Keys_Key (int k)
S_LocalSound ("misc/menu1.wav");
if ((k != K_ESCAPE) && (k != '`'))
{
sprintf (cmd, "bind \"%s\" \"%s\"\n", Key_KeynumToString (k), bindnames[keys_cursor][0]);
sprintf (cmd, "bind \"%s\" \"%s\"\n", Key_KeynumToString (k), bindnames[keys_cursor].cmd);
Cbuf_InsertText (cmd);
}
@ -1500,26 +1584,43 @@ void M_Keys_Key (int k)
case K_LEFTARROW:
case K_UPARROW:
S_LocalSound ("misc/menu1.wav");
do
{
keys_cursor--;
if (keys_cursor < 0)
keys_cursor = NUMCOMMANDS-1;
if (keys_cursor >= numbindnames)
{
if (keys_first && strcmp(bindnames[keys_first].cmd, "-"))
{ //some weirdness, so the user can re-view any leading titles
keys_cursor = keys_first;
keys_first = 0;
}
else
keys_cursor = numbindnames-1;
break;
}
} while (!strcmp(bindnames[keys_cursor].cmd, "-"));
break;
case K_DOWNARROW:
case K_RIGHTARROW:
S_LocalSound ("misc/menu1.wav");
do
{
keys_cursor++;
if (keys_cursor >= (int)NUMCOMMANDS)
keys_cursor = 0;
if (keys_cursor >= numbindnames)
keys_cursor = keys_first = 0;
else if (keys_cursor == numbindnames-1)
break;
} while (!strcmp(bindnames[keys_cursor].cmd, "-"));
break;
case K_ENTER: // go into bind mode
case K_KP_ENTER:
case K_ABUTTON:
M_FindKeysForCommand (bindnames[keys_cursor][0], keys);
M_FindKeysForCommand (bindnames[keys_cursor].cmd, keys);
S_LocalSound ("misc/menu2.wav");
if (keys[2] != -1)
M_UnbindCommand (bindnames[keys_cursor][0]);
M_UnbindCommand (bindnames[keys_cursor].cmd);
bind_grab = true;
IN_Activate(); // activate to allow mouse key binding
break;
@ -1527,7 +1628,7 @@ void M_Keys_Key (int k)
case K_BACKSPACE: // delete bindings
case K_DEL:
S_LocalSound ("misc/menu2.wav");
M_UnbindCommand (bindnames[keys_cursor][0]);
M_UnbindCommand (bindnames[keys_cursor].cmd);
break;
}
}
@ -1696,7 +1797,7 @@ void M_Quit_Draw (void) //johnfitz -- modified for new quit message
m_state = m_quit;
}
sprintf(msg1, "QuakeSpasm " QUAKESPASM_VER_STRING);
sprintf(msg1, ENGINE_NAME_AND_VER);
//okay, this is kind of fucked up. M_DrawTextBox will always act as if
//width is even. Also, the width and lines values are for the interior of the box,
@ -1715,8 +1816,8 @@ void M_Quit_Draw (void) //johnfitz -- modified for new quit message
/* LAN CONFIG MENU */
int lanConfig_cursor = -1;
int lanConfig_cursor_table [] = {72, 92, 124};
#define NUM_LANCONFIG_CMDS 3
int lanConfig_cursor_table [] = {72, 92, 100, 132};
#define NUM_LANCONFIG_CMDS 4
int lanConfig_port;
char lanConfig_portname[6];
@ -1735,7 +1836,7 @@ void M_Menu_LanConfig_f (void)
else
lanConfig_cursor = 1;
}
if (StartingGame && lanConfig_cursor == 2)
if (StartingGame && lanConfig_cursor >= 2)
lanConfig_cursor = 1;
lanConfig_port = DEFAULTnet_hostport;
sprintf(lanConfig_portname, "%u", lanConfig_port);
@ -1772,7 +1873,20 @@ void M_LanConfig_Draw (void)
if (IPXConfig)
M_Print (basex+9*8, 52, my_ipx_address);
else
M_Print (basex+9*8, 52, my_tcpip_address);
{
if (ipv4Available && ipv6Available)
{
M_Print (basex+9*8, 52-4, my_ipv4_address);
M_Print (basex+9*8, 52+4, my_ipv6_address);
}
else
{
if (ipv4Available)
M_Print (basex+9*8, 52, my_ipv4_address);
if (ipv6Available)
M_Print (basex+9*8, 52, my_ipv6_address);
}
}
M_Print (basex, lanConfig_cursor_table[0], "Port");
M_DrawTextBox (basex+8*8, lanConfig_cursor_table[0]-8, 6, 1);
@ -1781,9 +1895,10 @@ void M_LanConfig_Draw (void)
if (JoiningGame)
{
M_Print (basex, lanConfig_cursor_table[1], "Search for local games...");
M_Print (basex, lanConfig_cursor_table[2], "Search for public games...");
M_Print (basex, 108, "Join game at:");
M_DrawTextBox (basex+8, lanConfig_cursor_table[2]-8, 22, 1);
M_Print (basex+16, lanConfig_cursor_table[2], lanConfig_joinname);
M_DrawTextBox (basex+8, lanConfig_cursor_table[3]-8, 22, 1);
M_Print (basex+16, lanConfig_cursor_table[3], lanConfig_joinname);
}
else
{
@ -1796,8 +1911,8 @@ void M_LanConfig_Draw (void)
if (lanConfig_cursor == 0)
M_DrawCharacter (basex+9*8 + 8*strlen(lanConfig_portname), lanConfig_cursor_table [0], 10+((int)(realtime*4)&1));
if (lanConfig_cursor == 2)
M_DrawCharacter (basex+16 + 8*strlen(lanConfig_joinname), lanConfig_cursor_table [2], 10+((int)(realtime*4)&1));
if (lanConfig_cursor == 3)
M_DrawCharacter (basex+16 + 8*strlen(lanConfig_joinname), lanConfig_cursor_table [3], 10+((int)(realtime*4)&1));
if (*m_return_reason)
M_PrintWhite (basex, 148, m_return_reason);
@ -1839,18 +1954,18 @@ void M_LanConfig_Key (int key)
M_ConfigureNetSubsystem ();
if (lanConfig_cursor == 1)
{
if (StartingGame)
{
if (lanConfig_cursor == 1)
M_Menu_GameOptions_f ();
break;
}
M_Menu_Search_f();
break;
}
if (lanConfig_cursor == 2)
else
{
if (lanConfig_cursor == 1)
M_Menu_Search_f(SLIST_LAN);
else if (lanConfig_cursor == 2)
M_Menu_Search_f(SLIST_INTERNET);
else if (lanConfig_cursor == 3)
{
m_return_state = m_state;
m_return_onerror = true;
@ -1858,7 +1973,7 @@ void M_LanConfig_Key (int key)
key_dest = key_game;
m_state = m_none;
Cbuf_AddText ( va ("connect \"%s\"\n", lanConfig_joinname) );
break;
}
}
break;
@ -1870,7 +1985,7 @@ void M_LanConfig_Key (int key)
lanConfig_portname[strlen(lanConfig_portname)-1] = 0;
}
if (lanConfig_cursor == 2)
if (lanConfig_cursor == 3)
{
if (strlen(lanConfig_joinname))
lanConfig_joinname[strlen(lanConfig_joinname)-1] = 0;
@ -1878,7 +1993,7 @@ void M_LanConfig_Key (int key)
break;
}
if (StartingGame && lanConfig_cursor == 2)
if (StartingGame && lanConfig_cursor >= 2)
{
if (key == K_UPARROW)
lanConfig_cursor = 1;
@ -1911,7 +2026,7 @@ void M_LanConfig_Char (int key)
lanConfig_portname[l] = key;
}
break;
case 2:
case 3:
l = strlen(lanConfig_joinname);
if (l < 21)
{
@ -1925,7 +2040,7 @@ void M_LanConfig_Char (int key)
qboolean M_LanConfig_TextEntry (void)
{
return (lanConfig_cursor == 0 || lanConfig_cursor == 2);
return (lanConfig_cursor == 0 || lanConfig_cursor == 3);
}
//=============================================================================
@ -2075,6 +2190,8 @@ episode_t rogueepisodes[] =
{"Deathmatch Arena", 16, 1}
};
extern cvar_t sv_public;
int startepisode;
int startlevel;
int maxplayers;
@ -2094,32 +2211,43 @@ void M_Menu_GameOptions_f (void)
}
int gameoptions_cursor_table[] = {40, 56, 64, 72, 80, 88, 96, 112, 120};
#define NUM_GAMEOPTIONS 9
int gameoptions_cursor_table[] = {40, 56, 64, 72, 80, 88, 96, 104, 120, 128};
#define NUM_GAMEOPTIONS 10
int gameoptions_cursor;
void M_GameOptions_Draw (void)
{
qpic_t *p;
int x;
int y = 40;
M_DrawTransPic (16, 4, Draw_CachePic ("gfx/qplaque.lmp") );
p = Draw_CachePic ("gfx/p_multi.lmp");
M_DrawPic ( (320-p->width)/2, 4, p);
M_DrawTextBox (152, 32, 10, 1);
M_Print (160, 40, "begin game");
M_DrawTextBox (152, y-8, 10, 1);
M_Print (160, y, "begin game");
y+=16;
M_Print (0, 56, " Max players");
M_Print (160, 56, va("%i", maxplayers) );
M_Print (0, y, " Max players");
M_Print (160, y, va("%i", maxplayers) );
y+=8;
M_Print (0, 64, " Game Type");
if (coop.value)
M_Print (160, 64, "Cooperative");
M_Print (0, y, " Public");
if (sv_public.value)
M_Print (160, y, "Yes");
else
M_Print (160, 64, "Deathmatch");
M_Print (160, y, "No");
y+=8;
M_Print (0, 72, " Teamplay");
M_Print (0, y, " Game Type");
if (coop.value)
M_Print (160, y, "Cooperative");
else
M_Print (160, y, "Deathmatch");
y+=8;
M_Print (0, y, " Teamplay");
if (rogue)
{
const char *msg;
@ -2134,7 +2262,7 @@ void M_GameOptions_Draw (void)
case 6: msg = "Three Team CTF"; break;
default: msg = "Off"; break;
}
M_Print (160, 72, msg);
M_Print (160, y, msg);
}
else
{
@ -2146,59 +2274,67 @@ void M_GameOptions_Draw (void)
case 2: msg = "Friendly Fire"; break;
default: msg = "Off"; break;
}
M_Print (160, 72, msg);
M_Print (160, y, msg);
}
y+=8;
M_Print (0, 80, " Skill");
M_Print (0, y, " Skill");
if (skill.value == 0)
M_Print (160, 80, "Easy difficulty");
M_Print (160, y, "Easy difficulty");
else if (skill.value == 1)
M_Print (160, 80, "Normal difficulty");
M_Print (160, y, "Normal difficulty");
else if (skill.value == 2)
M_Print (160, 80, "Hard difficulty");
M_Print (160, y, "Hard difficulty");
else
M_Print (160, 80, "Nightmare difficulty");
M_Print (160, y, "Nightmare difficulty");
y+=8;
M_Print (0, 88, " Frag Limit");
M_Print (0, y, " Frag Limit");
if (fraglimit.value == 0)
M_Print (160, 88, "none");
M_Print (160, y, "none");
else
M_Print (160, 88, va("%i frags", (int)fraglimit.value));
M_Print (160, y, va("%i frags", (int)fraglimit.value));
y+=8;
M_Print (0, 96, " Time Limit");
M_Print (0, y, " Time Limit");
if (timelimit.value == 0)
M_Print (160, 96, "none");
M_Print (160, y, "none");
else
M_Print (160, 96, va("%i minutes", (int)timelimit.value));
M_Print (160, y, va("%i minutes", (int)timelimit.value));
y+=8;
M_Print (0, 112, " Episode");
y+=8;
M_Print (0, y, " Episode");
// MED 01/06/97 added hipnotic episodes
if (hipnotic)
M_Print (160, 112, hipnoticepisodes[startepisode].description);
M_Print (160, y, hipnoticepisodes[startepisode].description);
// PGM 01/07/97 added rogue episodes
else if (rogue)
M_Print (160, 112, rogueepisodes[startepisode].description);
M_Print (160, y, rogueepisodes[startepisode].description);
else
M_Print (160, 112, episodes[startepisode].description);
M_Print (160, y, episodes[startepisode].description);
y+=8;
M_Print (0, 120, " Level");
M_Print (0, y, " Level");
// MED 01/06/97 added hipnotic episodes
if (hipnotic)
{
M_Print (160, 120, hipnoticlevels[hipnoticepisodes[startepisode].firstLevel + startlevel].description);
M_Print (160, 128, hipnoticlevels[hipnoticepisodes[startepisode].firstLevel + startlevel].name);
M_Print (160, y, hipnoticlevels[hipnoticepisodes[startepisode].firstLevel + startlevel].description);
M_Print (160, y+8, hipnoticlevels[hipnoticepisodes[startepisode].firstLevel + startlevel].name);
}
// PGM 01/07/97 added rogue episodes
else if (rogue)
{
M_Print (160, 120, roguelevels[rogueepisodes[startepisode].firstLevel + startlevel].description);
M_Print (160, 128, roguelevels[rogueepisodes[startepisode].firstLevel + startlevel].name);
M_Print (160, y, roguelevels[rogueepisodes[startepisode].firstLevel + startlevel].description);
M_Print (160, y+8, roguelevels[rogueepisodes[startepisode].firstLevel + startlevel].name);
}
else
{
M_Print (160, 120, levels[episodes[startepisode].firstLevel + startlevel].description);
M_Print (160, 128, levels[episodes[startepisode].firstLevel + startlevel].name);
M_Print (160, y, levels[episodes[startepisode].firstLevel + startlevel].description);
M_Print (160, y+8, levels[episodes[startepisode].firstLevel + startlevel].name);
}
y+=8;
// line cursor
M_DrawCharacter (144, gameoptions_cursor_table[gameoptions_cursor], 12+((int)(realtime*4)&1));
@ -2243,10 +2379,14 @@ void M_NetStart_Change (int dir)
break;
case 2:
Cvar_Set ("coop", coop.value ? "0" : "1");
Cvar_SetQuick (&sv_public, sv_public.value ? "0" : "1");
break;
case 3:
Cvar_Set ("coop", coop.value ? "0" : "1");
break;
case 4:
count = (rogue) ? 6 : 2;
f = teamplay.value + dir;
if (f > count) f = 0;
@ -2254,28 +2394,28 @@ void M_NetStart_Change (int dir)
Cvar_SetValue ("teamplay", f);
break;
case 4:
case 5:
f = skill.value + dir;
if (f > 3) f = 0;
else if (f < 0) f = 3;
Cvar_SetValue ("skill", f);
break;
case 5:
case 6:
f = fraglimit.value + dir * 10;
if (f > 100) f = 0;
else if (f < 0) f = 100;
Cvar_SetValue ("fraglimit", f);
break;
case 6:
case 7:
f = timelimit.value + dir * 5;
if (f > 60) f = 0;
else if (f < 0) f = 60;
Cvar_SetValue ("timelimit", f);
break;
case 7:
case 8:
startepisode += dir;
//MED 01/06/97 added hipnotic count
if (hipnotic)
@ -2298,7 +2438,7 @@ void M_NetStart_Change (int dir)
startlevel = 0;
break;
case 8:
case 9:
startlevel += dir;
//MED 01/06/97 added hipnotic episodes
if (hipnotic)
@ -2387,15 +2527,16 @@ void M_GameOptions_Key (int key)
qboolean searchComplete = false;
double searchCompleteTime;
enum slistScope_e searchLastScope = SLIST_LAN;
void M_Menu_Search_f (void)
void M_Menu_Search_f (enum slistScope_e scope)
{
IN_Deactivate(modestate == MS_WINDOWED);
key_dest = key_menu;
m_state = m_search;
m_entersound = false;
slistSilent = true;
slistLocal = false;
slistScope = searchLastScope = scope;
searchComplete = false;
NET_Slist_f();
@ -2446,7 +2587,8 @@ void M_Search_Key (int key)
//=============================================================================
/* SLIST MENU */
int slist_cursor;
size_t slist_cursor;
size_t slist_first;
qboolean slist_sorted;
void M_Menu_ServerList_f (void)
@ -2456,6 +2598,7 @@ void M_Menu_ServerList_f (void)
m_state = m_slist;
m_entersound = true;
slist_cursor = 0;
slist_first = 0;
m_return_onerror = false;
m_return_reason[0] = 0;
slist_sorted = false;
@ -2464,7 +2607,7 @@ void M_Menu_ServerList_f (void)
void M_ServerList_Draw (void)
{
int n;
size_t n, slist_shown;
qpic_t *p;
if (!slist_sorted)
@ -2473,11 +2616,19 @@ void M_ServerList_Draw (void)
NET_SlistSort ();
}
slist_shown = hostCacheCount;
if (slist_shown > (200-32)/8)
slist_shown = (200-32)/8;
if (slist_first+slist_shown-1 < slist_cursor)
slist_first = slist_cursor-(slist_shown-1);
if (slist_first > slist_cursor)
slist_first = slist_cursor;
p = Draw_CachePic ("gfx/p_multi.lmp");
M_DrawPic ( (320-p->width)/2, 4, p);
for (n = 0; n < hostCacheCount; n++)
M_Print (16, 32 + 8*n, NET_SlistPrintServer (n));
M_DrawCharacter (0, 32 + slist_cursor*8, 12+((int)(realtime*4)&1));
for (n = 0; n < slist_shown; n++)
M_Print (16, 32 + 8*n, NET_SlistPrintServer (slist_first+n));
M_DrawCharacter (0, 32 + (slist_cursor-slist_first)*8, 12+((int)(realtime*4)&1));
if (*m_return_reason)
M_PrintWhite (16, 148, m_return_reason);
@ -2494,14 +2645,14 @@ void M_ServerList_Key (int k)
break;
case K_SPACE:
M_Menu_Search_f ();
M_Menu_Search_f (searchLastScope);
break;
case K_UPARROW:
case K_LEFTARROW:
S_LocalSound ("misc/menu1.wav");
slist_cursor--;
if (slist_cursor < 0)
if (slist_cursor >= hostCacheCount)
slist_cursor = hostCacheCount - 1;
break;

View File

@ -56,12 +56,20 @@ struct qsocket_s *NET_Connect (const char *host);
// called by client to connect to a host. Returns -1 if not able to
double NET_QSocketGetTime (const struct qsocket_s *sock);
const char *NET_QSocketGetAddressString (const struct qsocket_s *sock);
const char *NET_QSocketGetTrueAddressString (const struct qsocket_s *sock);
const char *NET_QSocketGetMaskedAddressString (const struct qsocket_s *sock);
qboolean NET_QSocketGetProQuakeAngleHack (const struct qsocket_s *sock);
int NET_QSocketGetSequenceIn (const struct qsocket_s *sock);
int NET_QSocketGetSequenceOut (const struct qsocket_s *sock);
void NET_QSocketSetMSS(struct qsocket_s *s, int mss);
qboolean NET_CanSendMessage (struct qsocket_s *sock);
// Returns true or false if the given qsocket can currently accept a
// message to be transmitted.
struct qsocket_s *NET_GetServerMessage(void);
//returns data in net_message, qsocket says which client its from
int NET_GetMessage (struct qsocket_s *sock);
// returns data in net_message sizebuf
// returns 0 if no data is waiting
@ -94,22 +102,29 @@ void NET_Poll (void);
// Server list related globals:
extern qboolean slistInProgress;
extern qboolean slistSilent;
extern qboolean slistLocal;
extern enum slistScope_e
{
SLIST_LOOP,
SLIST_LAN,
SLIST_INTERNET
} slistScope;
extern int hostCacheCount;
extern size_t hostCacheCount;
void NET_Slist_f (void);
void NET_SlistSort (void);
const char *NET_SlistPrintServer (int n);
const char *NET_SlistPrintServerName (int n);
const char *NET_SlistPrintServer (size_t n);
const char *NET_SlistPrintServerName (size_t n);
/* FIXME: driver related, but public:
*/
extern qboolean ipxAvailable;
extern qboolean tcpipAvailable;
extern qboolean ipv4Available;
extern qboolean ipv6Available;
extern char my_ipx_address[NET_NAMELEN];
extern char my_tcpip_address[NET_NAMELEN];
extern char my_ipv4_address[NET_NAMELEN];
extern char my_ipv6_address[NET_NAMELEN];
#endif /* _QUAKE_NET_H */

View File

@ -37,6 +37,7 @@ net_driver_t net_drivers[] =
Loop_SearchForHosts,
Loop_Connect,
Loop_CheckNewConnections,
Loop_GetAnyMessage,
Loop_GetMessage,
Loop_SendMessage,
Loop_SendUnreliableMessage,
@ -53,6 +54,7 @@ net_driver_t net_drivers[] =
Datagram_SearchForHosts,
Datagram_Connect,
Datagram_CheckNewConnections,
Datagram_GetAnyMessage,
Datagram_GetMessage,
Datagram_SendMessage,
Datagram_SendUnreliableMessage,
@ -72,21 +74,43 @@ net_landriver_t net_landrivers[] =
{ "UDP",
false,
0,
UDP_Init,
UDP_Shutdown,
UDP_Listen,
UDP_OpenSocket,
UDP4_Init,
UDP4_Shutdown,
UDP4_Listen,
UDP4_OpenSocket,
UDP_CloseSocket,
UDP_Connect,
UDP_CheckNewConnections,
UDP4_CheckNewConnections,
UDP_Read,
UDP_Write,
UDP_Broadcast,
UDP4_Broadcast,
UDP_AddrToString,
UDP_StringToAddr,
UDP4_StringToAddr,
UDP_GetSocketAddr,
UDP_GetNameFromAddr,
UDP_GetAddrFromName,
UDP4_GetAddrFromName,
UDP_AddrCompare,
UDP_GetSocketPort,
UDP_SetSocketPort
},
{ "UDP6",
false,
0,
UDP6_Init,
UDP6_Shutdown,
UDP6_Listen,
UDP6_OpenSocket,
UDP_CloseSocket,
UDP_Connect,
UDP6_CheckNewConnections,
UDP_Read,
UDP_Write,
UDP6_Broadcast,
UDP_AddrToString,
UDP6_StringToAddr,
UDP_GetSocketAddr,
UDP_GetNameFromAddr,
UDP6_GetAddrFromName,
UDP_AddrCompare,
UDP_GetSocketPort,
UDP_SetSocketPort

View File

@ -32,7 +32,7 @@ struct qsockaddr
#else
short qsa_family;
#endif /* BSD, sockaddr */
unsigned char qsa_data[14];
unsigned char qsa_data[62];
};
#define NET_HEADERSIZE (2 * sizeof(unsigned int))
@ -120,12 +120,14 @@ CCREP_RULE_INFO
#define CCREQ_SERVER_INFO 0x02
#define CCREQ_PLAYER_INFO 0x03
#define CCREQ_RULE_INFO 0x04
#define CCREQ_RCON 0x05
#define CCREP_ACCEPT 0x81
#define CCREP_REJECT 0x82
#define CCREP_SERVER_INFO 0x83
#define CCREP_PLAYER_INFO 0x84
#define CCREP_RULE_INFO 0x85
#define CCREP_RCON 0x86
typedef struct qsocket_s
{
@ -134,6 +136,7 @@ typedef struct qsocket_s
double lastMessageTime;
double lastSendTime;
qboolean isvirtual; //qsocket is emulated by the network layer (closing will not close any system sockets).
qboolean disconnected;
qboolean canSend;
qboolean sendNext;
@ -155,8 +158,12 @@ typedef struct qsocket_s
byte receiveMessage [NET_MAXMESSAGE];
struct qsockaddr addr;
char address[NET_NAMELEN];
char trueaddress[NET_NAMELEN]; //lazy address string
char maskedaddress[NET_NAMELEN]; //addresses for this player that may be displayed publically
qboolean proquake_angle_hack; //1 if we're trying, 2 if the server acked.
int max_datagram; //32000 for local, 1442 for 666, 1024 for 15. this is for reliable fragments.
int pending_max_datagram; //don't change the mtu if we're resending, as that would confuse the peer.
} qsocket_t;
extern qsocket_t *net_activeSockets;
@ -170,7 +177,7 @@ typedef struct
sys_socket_t controlSock;
sys_socket_t (*Init) (void);
void (*Shutdown) (void);
void (*Listen) (qboolean state);
sys_socket_t (*Listen) (qboolean state);
sys_socket_t (*Open_Socket) (int port);
int (*Close_Socket) (sys_socket_t socketid);
int (*Connect) (sys_socket_t socketid, struct qsockaddr *addr);
@ -178,7 +185,7 @@ typedef struct
int (*Read) (sys_socket_t socketid, byte *buf, int len, struct qsockaddr *addr);
int (*Write) (sys_socket_t socketid, byte *buf, int len, struct qsockaddr *addr);
int (*Broadcast) (sys_socket_t socketid, byte *buf, int len);
const char * (*AddrToString) (struct qsockaddr *addr);
const char * (*AddrToString) (struct qsockaddr *addr, qboolean masked);
int (*StringToAddr) (const char *string, struct qsockaddr *addr);
int (*GetSocketAddr) (sys_socket_t socketid, struct qsockaddr *addr);
int (*GetNameFromAddr) (struct qsockaddr *addr, char *name);
@ -186,6 +193,8 @@ typedef struct
int (*AddrCompare) (struct qsockaddr *addr1, struct qsockaddr *addr2);
int (*GetSocketPort) (struct qsockaddr *addr);
int (*SetSocketPort) (struct qsockaddr *addr, int port);
sys_socket_t listeningSock;
} net_landriver_t;
#define MAX_NET_DRIVERS 8
@ -198,9 +207,10 @@ typedef struct
qboolean initialized;
int (*Init) (void);
void (*Listen) (qboolean state);
void (*SearchForHosts) (qboolean xmit);
qboolean (*SearchForHosts) (qboolean xmit);
qsocket_t *(*Connect) (const char *host);
qsocket_t *(*CheckNewConnections) (void);
qsocket_t *(*QGetAnyMessage) (void);
int (*QGetMessage) (qsocket_t *sock);
int (*QSendMessage) (qsocket_t *sock, sizebuf_t *data);
int (*SendUnreliableMessage) (qsocket_t *sock, sizebuf_t *data);
@ -228,13 +238,13 @@ void NET_FreeQSocket(qsocket_t *);
double SetNetTime(void);
#define HOSTCACHESIZE 8
#define HOSTCACHESIZE 128 //fixme: make dynamic.
typedef struct
{
char name[16];
char map[16];
char cname[32];
char cname[NET_NAMELEN];
int users;
int maxusers;
int driver;
@ -242,7 +252,7 @@ typedef struct
struct qsockaddr addr;
} hostcache_t;
extern int hostCacheCount;
extern size_t hostCacheCount;
extern hostcache_t hostcache[HOSTCACHESIZE];

File diff suppressed because it is too large Load Diff

View File

@ -24,9 +24,10 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
int Datagram_Init (void);
void Datagram_Listen (qboolean state);
void Datagram_SearchForHosts (qboolean xmit);
qboolean Datagram_SearchForHosts (qboolean xmit);
qsocket_t *Datagram_Connect (const char *host);
qsocket_t *Datagram_CheckNewConnections (void);
qsocket_t *Datagram_GetAnyMessage (void);
int Datagram_GetMessage (qsocket_t *sock);
int Datagram_SendMessage (qsocket_t *sock, sizebuf_t *data);
int Datagram_SendUnreliableMessage (qsocket_t *sock, sizebuf_t *data);

View File

@ -48,10 +48,10 @@ void Loop_Listen (qboolean state)
}
void Loop_SearchForHosts (qboolean xmit)
qboolean Loop_SearchForHosts (qboolean xmit)
{
if (!sv.active)
return;
return false;
hostCacheCount = 1;
if (Q_strcmp(hostname.string, "UNNAMED") == 0)
@ -63,6 +63,7 @@ void Loop_SearchForHosts (qboolean xmit)
hostcache[0].maxusers = svs.maxclients;
hostcache[0].driver = net_driverlevel;
Q_strcpy(hostcache[0].cname, "local");
return false;
}
@ -80,7 +81,8 @@ qsocket_t *Loop_Connect (const char *host)
Con_Printf("Loop_Connect: no qsocket available\n");
return NULL;
}
Q_strcpy (loop_client->address, "localhost");
Q_strcpy (loop_client->trueaddress, "localhost");
Q_strcpy (loop_client->maskedaddress, "localhost");
}
loop_client->receiveMessageLength = 0;
loop_client->sendMessageLength = 0;
@ -93,7 +95,8 @@ qsocket_t *Loop_Connect (const char *host)
Con_Printf("Loop_Connect: no qsocket available\n");
return NULL;
}
Q_strcpy (loop_server->address, "LOCAL");
Q_strcpy (loop_server->trueaddress, "LOCAL");
Q_strcpy (loop_server->maskedaddress, "LOCAL");
}
loop_server->receiveMessageLength = 0;
loop_server->sendMessageLength = 0;
@ -102,6 +105,8 @@ qsocket_t *Loop_Connect (const char *host)
loop_client->driverdata = (void *)loop_server;
loop_server->driverdata = (void *)loop_client;
loop_client->proquake_angle_hack = loop_server->proquake_angle_hack = true;
return loop_client;
}
@ -127,7 +132,6 @@ static int IntAlign(int value)
return (value + (sizeof(int) - 1)) & (~(sizeof(int) - 1));
}
int Loop_GetMessage (qsocket_t *sock)
{
int ret;
@ -140,9 +144,19 @@ int Loop_GetMessage (qsocket_t *sock)
length = sock->receiveMessage[1] + (sock->receiveMessage[2] << 8);
// alignment byte skipped here
SZ_Clear (&net_message);
if (ret == 2)
{ //unreliables have sequences that we (now) care about so that clients can ack them.
sock->unreliableReceiveSequence = sock->receiveMessage[4] | (sock->receiveMessage[5]<<8) | (sock->receiveMessage[6]<<16) | (sock->receiveMessage[7]<<16);
sock->unreliableReceiveSequence++;
SZ_Write (&net_message, &sock->receiveMessage[8], length);
length = IntAlign(length + 8);
}
else
{ //reliable
SZ_Write (&net_message, &sock->receiveMessage[4], length);
length = IntAlign(length + 4);
}
sock->receiveMessageLength -= length;
if (sock->receiveMessageLength)
@ -154,6 +168,16 @@ int Loop_GetMessage (qsocket_t *sock)
return ret;
}
qsocket_t *Loop_GetAnyMessage(void)
{
if (loop_server)
{
if (Loop_GetMessage(loop_server) > 0)
return loop_server;
}
return NULL;
}
int Loop_SendMessage (qsocket_t *sock, sizebuf_t *data)
{
@ -193,6 +217,7 @@ int Loop_SendUnreliableMessage (qsocket_t *sock, sizebuf_t *data)
{
byte *buffer;
int *bufferLength;
int sequence = sock->unreliableSendSequence++;
if (!sock->driverdata)
return -1;
@ -214,9 +239,14 @@ int Loop_SendUnreliableMessage (qsocket_t *sock, sizebuf_t *data)
// align
buffer++;
*buffer++ = (sequence >> 0) & 0xff;
*buffer++ = (sequence >> 8) & 0xff;
*buffer++ = (sequence >> 16) & 0xff;
*buffer++ = (sequence >> 24) & 0xff;
// message
Q_memcpy(buffer, data->data, data->cursize);
*bufferLength = IntAlign(*bufferLength + data->cursize + 4);
*bufferLength = IntAlign(*bufferLength + data->cursize + 8);
return 1;
}

View File

@ -25,9 +25,10 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
// net_loop.h
int Loop_Init (void);
void Loop_Listen (qboolean state);
void Loop_SearchForHosts (qboolean xmit);
qboolean Loop_SearchForHosts (qboolean xmit);
qsocket_t *Loop_Connect (const char *host);
qsocket_t *Loop_CheckNewConnections (void);
qsocket_t *Loop_GetAnyMessage(void);
int Loop_GetMessage (qsocket_t *sock);
int Loop_SendMessage (qsocket_t *sock, sizebuf_t *data);
int Loop_SendUnreliableMessage (qsocket_t *sock, sizebuf_t *data);

View File

@ -30,20 +30,23 @@ qsocket_t *net_freeSockets = NULL;
int net_numsockets = 0;
qboolean ipxAvailable = false;
qboolean tcpipAvailable = false;
qboolean ipv4Available = false;
qboolean ipv6Available = false;
int net_hostport;
int DEFAULTnet_hostport = 26000;
char my_ipx_address[NET_NAMELEN];
char my_tcpip_address[NET_NAMELEN];
char my_ipv4_address[NET_NAMELEN];
char my_ipv6_address[NET_NAMELEN];
static qboolean listening = false;
qboolean slistInProgress = false;
qboolean slistSilent = false;
qboolean slistLocal = true;
enum slistScope_e slistScope = SLIST_LOOP;
static double slistStartTime;
static double slistActiveTime;
static int slistLastShown;
static void Slist_Send (void *);
@ -59,7 +62,8 @@ int messagesReceived = 0;
int unreliableMessagesSent = 0;
int unreliableMessagesReceived = 0;
static cvar_t net_messagetimeout = {"net_messagetimeout","300",CVAR_NONE};
cvar_t net_messagetimeout = {"net_messagetimeout","300",CVAR_NONE};
cvar_t net_connecttimeout = {"net_connecttimeout","10",CVAR_NONE}; //this might be a little brief, but we don't have a way to protect against smurf attacks.
cvar_t hostname = {"hostname", "UNNAMED", CVAR_NONE};
// these two macros are to make the code more readable
@ -104,9 +108,11 @@ qsocket_t *NET_NewQSocket (void)
sock->next = net_activeSockets;
net_activeSockets = sock;
sock->isvirtual = false;
sock->disconnected = false;
sock->connecttime = net_time;
Q_strcpy (sock->address,"UNSET ADDRESS");
Q_strcpy (sock->trueaddress,"UNSET ADDRESS");
Q_strcpy (sock->maskedaddress,"UNSET ADDRESS");
sock->driver = net_driverlevel;
sock->socket = 0;
sock->driverdata = NULL;
@ -120,6 +126,8 @@ qsocket_t *NET_NewQSocket (void)
sock->receiveSequence = 0;
sock->unreliableReceiveSequence = 0;
sock->receiveMessageLength = 0;
sock->pending_max_datagram = 1024;
sock->proquake_angle_hack = false;
return sock;
}
@ -154,15 +162,38 @@ void NET_FreeQSocket(qsocket_t *sock)
}
int NET_QSocketGetSequenceIn (const qsocket_t *s)
{ //returns the last unreliable sequence that was received
return s->unreliableReceiveSequence-1;
}
int NET_QSocketGetSequenceOut (const qsocket_t *s)
{ //returns the next unreliable sequence that will be sent
return s->unreliableSendSequence;
}
double NET_QSocketGetTime (const qsocket_t *s)
{
return s->connecttime;
}
const char *NET_QSocketGetAddressString (const qsocket_t *s)
const char *NET_QSocketGetTrueAddressString (const qsocket_t *s)
{
return s->address;
return s->trueaddress;
}
const char *NET_QSocketGetMaskedAddressString (const qsocket_t *s)
{
return s->maskedaddress;
}
qboolean NET_QSocketGetProQuakeAngleHack(const qsocket_t *s)
{
if (s && !s->disconnected)
return s->proquake_angle_hack;
else
return false; //happens with demos
}
void NET_QSocketSetMSS(qsocket_t *s, int mss)
{
s->pending_max_datagram = mss;
}
@ -263,7 +294,7 @@ static void PrintSlistHeader(void)
static void PrintSlist(void)
{
int n;
size_t n;
for (n = slistLastShown; n < hostCacheCount; n++)
{
@ -297,7 +328,7 @@ void NET_Slist_f (void)
}
slistInProgress = true;
slistStartTime = Sys_DoubleTime();
slistActiveTime = slistStartTime = Sys_DoubleTime();
SchedulePollProcedure(&slistSendProcedure, 0.0);
SchedulePollProcedure(&slistPollProcedure, 0.1);
@ -310,7 +341,7 @@ void NET_SlistSort (void)
{
if (hostCacheCount > 1)
{
int i, j;
size_t i, j;
hostcache_t temp;
for (i = 0; i < hostCacheCount; i++)
{
@ -328,11 +359,11 @@ void NET_SlistSort (void)
}
const char *NET_SlistPrintServer (int idx)
const char *NET_SlistPrintServer (size_t idx)
{
static char string[64];
if (idx < 0 || idx >= hostCacheCount)
if (idx >= hostCacheCount)
return "";
if (hostcache[idx].maxusers)
@ -351,9 +382,9 @@ const char *NET_SlistPrintServer (int idx)
}
const char *NET_SlistPrintServerName (int idx)
const char *NET_SlistPrintServerName (size_t idx)
{
if (idx < 0 || idx >= hostCacheCount)
if (idx >= hostCacheCount)
return "";
return hostcache[idx].cname;
}
@ -363,7 +394,7 @@ static void Slist_Send (void *unused)
{
for (net_driverlevel = 0; net_driverlevel < net_numdrivers; net_driverlevel++)
{
if (!slistLocal && IS_LOOP_DRIVER(net_driverlevel))
if (slistScope!=SLIST_LOOP && IS_LOOP_DRIVER(net_driverlevel))
continue;
if (net_drivers[net_driverlevel].initialized == false)
continue;
@ -379,17 +410,18 @@ static void Slist_Poll (void *unused)
{
for (net_driverlevel = 0; net_driverlevel < net_numdrivers; net_driverlevel++)
{
if (!slistLocal && IS_LOOP_DRIVER(net_driverlevel))
if (slistScope!=SLIST_LOOP && IS_LOOP_DRIVER(net_driverlevel))
continue;
if (net_drivers[net_driverlevel].initialized == false)
continue;
dfunc.SearchForHosts (false);
if (dfunc.SearchForHosts (false))
slistActiveTime = Sys_DoubleTime(); //something was sent, reset the timer.
}
if (! slistSilent)
PrintSlist();
if ((Sys_DoubleTime() - slistStartTime) < 1.5)
if ((Sys_DoubleTime() - slistActiveTime) < 1.5)
{
SchedulePollProcedure(&slistPollProcedure, 0.1);
return;
@ -399,7 +431,7 @@ static void Slist_Poll (void *unused)
PrintSlistTrailer();
slistInProgress = false;
slistSilent = false;
slistLocal = true;
slistScope = SLIST_LOOP;
}
@ -409,13 +441,13 @@ NET_Connect
===================
*/
int hostCacheCount = 0;
size_t hostCacheCount = 0;
hostcache_t hostcache[HOSTCACHESIZE];
qsocket_t *NET_Connect (const char *host)
{
qsocket_t *ret;
int n;
size_t n;
int numdrivers = net_numdrivers;
SetNetTime();
@ -594,6 +626,29 @@ int NET_GetMessage (qsocket_t *sock)
return ret;
}
/*
=================
NET_GetServerMessage
If there is a complete message, return it in net_message
returns the qsocket that the message was meant to be for.
=================
*/
qsocket_t *NET_GetServerMessage(void)
{
qsocket_t *s;
for (net_driverlevel = 0; net_driverlevel < net_numdrivers; net_driverlevel++)
{
if (!net_drivers[net_driverlevel].initialized)
continue;
s = net_drivers[net_driverlevel].QGetAnyMessage();
if (s)
return s;
}
return NULL;
}
/*
==================
@ -797,6 +852,7 @@ void NET_Init (void)
SZ_Alloc (&net_message, NET_MAXMESSAGE);
Cvar_RegisterVariable (&net_messagetimeout);
Cvar_RegisterVariable (&net_connecttimeout);
Cvar_RegisterVariable (&hostname);
Cmd_AddCommand ("slist", NET_Slist_f);
@ -828,9 +884,13 @@ void NET_Init (void)
{
Con_DPrintf("IPX address %s\n", my_ipx_address);
}
if (*my_tcpip_address)
if (*my_ipv4_address)
{
Con_DPrintf("TCP/IP address %s\n", my_tcpip_address);
Con_DPrintf("IPv4 address %s\n", my_ipv4_address);
}
if (*my_ipv6_address)
{
Con_DPrintf("IPv6 address %s\n", my_ipv6_address);
}
}

View File

@ -143,9 +143,10 @@ COMPILE_TIME_ASSERT(sockaddr, offsetof(struct sockaddr, sa_family) == SA_FAM_OFF
#if defined(PLATFORM_WINDOWS)
/* NOTE: winsock[2].h already includes windows.h */
#if !defined(_USE_WINSOCK2)
#if 0//!defined(_USE_WINSOCK2)
#include <winsock.h>
#else
//winsock2 has been available since win98, and is available as a separate download for win95, which would be needed for any web browsers anyway.
#include <winsock2.h>
#include <ws2tcpip.h>
#endif

View File

@ -26,18 +26,23 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "quakedef.h"
#include "net_defs.h"
static sys_socket_t net_acceptsocket = INVALID_SOCKET; // socket for fielding new connections
static sys_socket_t net_controlsocket;
static sys_socket_t net_broadcastsocket = 0;
static struct sockaddr_in broadcastaddr;
static sys_socket_t net_acceptsocket4 = INVALID_SOCKET; // socket for fielding new connections
static sys_socket_t net_controlsocket4;
static sys_socket_t net_broadcastsocket4 = INVALID_SOCKET;
static struct sockaddr_in broadcastaddr4;
static in_addr_t myAddr;
static in_addr_t myAddr4;
static sys_socket_t net_acceptsocket6 = INVALID_SOCKET; // socket for fielding new connections
static sys_socket_t net_controlsocket6;
static struct in6_addr myAddrv6;
#include "net_udp.h"
//=============================================================================
sys_socket_t UDP_Init (void)
sys_socket_t UDP4_Init (void)
{
int err;
char *tst;
@ -45,15 +50,15 @@ sys_socket_t UDP_Init (void)
struct hostent *local;
struct qsockaddr addr;
if (COM_CheckParm ("-noudp"))
if (COM_CheckParm ("-noudp") || COM_CheckParm ("-noudp4"))
return INVALID_SOCKET;
// determine my name & address
myAddr = htonl(INADDR_LOOPBACK);
myAddr4 = htonl(INADDR_LOOPBACK);
if (gethostname(buff, MAXHOSTNAMELEN) != 0)
{
err = SOCKETERRNO;
Con_SafePrintf("UDP_Init: gethostname failed (%s)\n",
Con_SafePrintf("UDP4_Init: gethostname failed (%s)\n",
socketerror(err));
}
else
@ -72,72 +77,76 @@ sys_socket_t UDP_Init (void)
#endif
if (!(local = gethostbyname(buff)))
{
Con_SafePrintf("UDP_Init: gethostbyname failed (%s)\n",
Con_SafePrintf("UDP4_Init: gethostbyname failed (%s)\n",
hstrerror(h_errno));
}
else if (local->h_addrtype != AF_INET)
{
Con_SafePrintf("UDP_Init: address from gethostbyname not IPv4\n");
Con_SafePrintf("UDP4_Init: address from gethostbyname not IPv4\n");
}
else
{
myAddr = *(in_addr_t *)local->h_addr_list[0];
myAddr4 = *(in_addr_t *)local->h_addr_list[0];
}
}
if ((net_controlsocket = UDP_OpenSocket(0)) == INVALID_SOCKET)
if ((net_controlsocket4 = UDP4_OpenSocket(0)) == INVALID_SOCKET)
{
Con_SafePrintf("UDP_Init: Unable to open control socket, UDP disabled\n");
Con_SafePrintf("UDP4_Init: Unable to open control socket, UDP disabled\n");
return INVALID_SOCKET;
}
broadcastaddr.sin_family = AF_INET;
broadcastaddr.sin_addr.s_addr = INADDR_BROADCAST;
broadcastaddr.sin_port = htons((unsigned short)net_hostport);
broadcastaddr4.sin_family = AF_INET;
broadcastaddr4.sin_addr.s_addr = INADDR_BROADCAST;
broadcastaddr4.sin_port = htons((unsigned short)net_hostport);
UDP_GetSocketAddr (net_controlsocket, &addr);
strcpy(my_tcpip_address, UDP_AddrToString (&addr));
tst = strrchr(my_tcpip_address, ':');
UDP_GetSocketAddr (net_controlsocket4, &addr);
strcpy(my_ipv4_address, UDP_AddrToString (&addr, false));
tst = strrchr (my_ipv4_address, ':');
if (tst) *tst = 0;
Con_SafePrintf("UDP Initialized\n");
tcpipAvailable = true;
Con_SafePrintf("UDP4 Initialized\n");
ipv4Available = true;
return net_controlsocket;
return net_controlsocket4;
}
//=============================================================================
void UDP_Shutdown (void)
void UDP4_Shutdown (void)
{
UDP_Listen (false);
UDP_CloseSocket (net_controlsocket);
UDP4_Listen (false);
UDP_CloseSocket (net_controlsocket4);
}
//=============================================================================
void UDP_Listen (qboolean state)
sys_socket_t UDP4_Listen (qboolean state)
{
// enable listening
if (state)
{
if (net_acceptsocket != INVALID_SOCKET)
return;
if ((net_acceptsocket = UDP_OpenSocket (net_hostport)) == INVALID_SOCKET)
Sys_Error ("UDP_Listen: Unable to open accept socket");
return;
// enable listening
if (net_acceptsocket4 == INVALID_SOCKET)
{
if ((net_acceptsocket4 = UDP4_OpenSocket (net_hostport)) == INVALID_SOCKET)
Sys_Error ("UDP4_Listen: Unable to open accept socket");
}
}
else
{
// disable listening
if (net_acceptsocket == INVALID_SOCKET)
return;
UDP_CloseSocket (net_acceptsocket);
net_acceptsocket = INVALID_SOCKET;
if (net_acceptsocket4 != INVALID_SOCKET)
{
UDP_CloseSocket (net_acceptsocket4);
net_acceptsocket4 = INVALID_SOCKET;
}
}
return net_acceptsocket4;
}
//=============================================================================
sys_socket_t UDP_OpenSocket (int port)
sys_socket_t UDP4_OpenSocket (int port)
{
sys_socket_t newsocket;
struct sockaddr_in address;
@ -147,7 +156,7 @@ sys_socket_t UDP_OpenSocket (int port)
if ((newsocket = socket (PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == INVALID_SOCKET)
{
err = SOCKETERRNO;
Con_SafePrintf("UDP_OpenSocket: %s\n", socketerror(err));
Con_SafePrintf("UDP4_OpenSocket: %s\n", socketerror(err));
return INVALID_SOCKET;
}
@ -163,7 +172,7 @@ sys_socket_t UDP_OpenSocket (int port)
ErrorReturn:
err = SOCKETERRNO;
Con_SafePrintf("UDP_OpenSocket: %s\n", socketerror(err));
Con_SafePrintf("UDP4_OpenSocket: %s\n", socketerror(err));
UDP_CloseSocket (newsocket);
return INVALID_SOCKET;
}
@ -172,8 +181,8 @@ ErrorReturn:
int UDP_CloseSocket (sys_socket_t socketid)
{
if (socketid == net_broadcastsocket)
net_broadcastsocket = 0;
if (socketid == net_broadcastsocket4)
net_broadcastsocket4 = INVALID_SOCKET;
return closesocket (socketid);
}
@ -228,7 +237,7 @@ static int PartialIPAddress (const char *in, struct qsockaddr *hostaddr)
hostaddr->qsa_family = AF_INET;
((struct sockaddr_in *)hostaddr)->sin_port = htons((unsigned short)port);
((struct sockaddr_in *)hostaddr)->sin_addr.s_addr =
(myAddr & htonl(mask)) | htonl(addr);
(myAddr4 & htonl(mask)) | htonl(addr);
return 0;
}
@ -242,25 +251,25 @@ int UDP_Connect (sys_socket_t socketid, struct qsockaddr *addr)
//=============================================================================
sys_socket_t UDP_CheckNewConnections (void)
sys_socket_t UDP4_CheckNewConnections (void)
{
int available;
struct sockaddr_in from;
socklen_t fromlen;
char buff[1];
if (net_acceptsocket == INVALID_SOCKET)
if (net_acceptsocket4 == INVALID_SOCKET)
return INVALID_SOCKET;
if (ioctl (net_acceptsocket, FIONREAD, &available) == -1)
if (ioctl (net_acceptsocket4, FIONREAD, &available) == -1)
{
int err = SOCKETERRNO;
Sys_Error ("UDP: ioctlsocket (FIONREAD) failed (%s)", socketerror(err));
}
if (available)
return net_acceptsocket;
return net_acceptsocket4;
// quietly absorb empty packets
recvfrom (net_acceptsocket, buff, 0, 0, (struct sockaddr *) &from, &fromlen);
recvfrom (net_acceptsocket4, buff, 0, 0, (struct sockaddr *) &from, &fromlen);
return INVALID_SOCKET;
}
@ -284,7 +293,7 @@ int UDP_Read (sys_socket_t socketid, byte *buf, int len, struct qsockaddr *addr)
//=============================================================================
static int UDP_MakeSocketBroadcastCapable (sys_socket_t socketid)
static int UDP4_MakeSocketBroadcastCapable (sys_socket_t socketid)
{
int i = 1;
@ -296,30 +305,30 @@ static int UDP_MakeSocketBroadcastCapable (sys_socket_t socketid)
Con_SafePrintf ("UDP, setsockopt: %s\n", socketerror(err));
return -1;
}
net_broadcastsocket = socketid;
net_broadcastsocket4 = socketid;
return 0;
}
//=============================================================================
int UDP_Broadcast (sys_socket_t socketid, byte *buf, int len)
int UDP4_Broadcast (sys_socket_t socketid, byte *buf, int len)
{
int ret;
if (socketid != net_broadcastsocket)
if (socketid != net_broadcastsocket4)
{
if (net_broadcastsocket != 0)
if (net_broadcastsocket4 != INVALID_SOCKET)
Sys_Error("Attempted to use multiple broadcasts sockets");
ret = UDP_MakeSocketBroadcastCapable (socketid);
ret = UDP4_MakeSocketBroadcastCapable (socketid);
if (ret == -1)
{
Con_Printf("Unable to make socket broadcast capable\n");
Con_SafePrintf("Unable to make socket broadcast capable\n");
return ret;
}
}
return UDP_Write (socketid, buf, len, (struct qsockaddr *)&broadcastaddr);
return UDP_Write (socketid, buf, len, (struct qsockaddr *)&broadcastaddr4);
}
//=============================================================================
@ -327,14 +336,28 @@ int UDP_Broadcast (sys_socket_t socketid, byte *buf, int len)
int UDP_Write (sys_socket_t socketid, byte *buf, int len, struct qsockaddr *addr)
{
int ret;
socklen_t addrsize;
if (addr->qsa_family == AF_INET)
addrsize = sizeof(struct sockaddr_in);
else if (addr->qsa_family == AF_INET6)
addrsize = sizeof(struct sockaddr_in6);
else
{
Con_SafePrintf ("UDP_Write: unknown family\n");
return -1; //some kind of error. a few systems get pissy if the size doesn't exactly match the address family
}
ret = sendto (socketid, buf, len, 0, (struct sockaddr *)addr,
sizeof(struct qsockaddr));
ret = sendto (socketid, buf, len, 0, (struct sockaddr *)addr, addrsize);
if (!addr->qsa_family)
Con_SafePrintf ("UDP_Write: family was cleared\n");
if (ret == SOCKET_ERROR)
{
int err = SOCKETERRNO;
if (err == NET_EWOULDBLOCK)
return 0;
if (err == ENETUNREACH)
Con_SafePrintf ("UDP_Write: %s (%s)\n", socketerror(err), UDP_AddrToString(addr, false));
else
Con_SafePrintf ("UDP_Write, sendto: %s\n", socketerror(err));
}
return ret;
@ -342,21 +365,57 @@ int UDP_Write (sys_socket_t socketid, byte *buf, int len, struct qsockaddr *addr
//=============================================================================
const char *UDP_AddrToString (struct qsockaddr *addr)
const char *UDP_AddrToString (struct qsockaddr *addr, qboolean masked)
{
static char buffer[22];
int haddr;
static char buffer[64];
haddr = ntohl(((struct sockaddr_in *)addr)->sin_addr.s_addr);
if (addr->qsa_family == AF_INET)
{
int haddr = ntohl(((struct sockaddr_in *)addr)->sin_addr.s_addr);
q_snprintf (buffer, sizeof(buffer), "%d.%d.%d.%d:%d", (haddr >> 24) & 0xff,
(haddr >> 16) & 0xff, (haddr >> 8) & 0xff, haddr & 0xff,
ntohs(((struct sockaddr_in *)addr)->sin_port));
}
else if (addr->qsa_family == AF_INET6)
{
//evil type punning.
unsigned short *s = (unsigned short*)&((struct sockaddr_in6 *)addr)->sin6_addr;
if (((struct sockaddr_in6 *)addr)->sin6_scope_id)
{
q_snprintf(buffer, sizeof(buffer), "[%x:%x:%x:%x:%x:%x:%x:%x%%%i]:%d",
ntohs(s[0]),
ntohs(s[1]),
ntohs(s[2]),
ntohs(s[3]),
ntohs(s[4]),
ntohs(s[5]),
ntohs(s[6]),
ntohs(s[7]),
(int)((struct sockaddr_in6 *)addr)->sin6_scope_id,
ntohs(((struct sockaddr_in6 *)addr)->sin6_port));
}
else
{
q_snprintf(buffer, sizeof(buffer), "[%x:%x:%x:%x:%x:%x:%x:%x]:%d",
ntohs(s[0]),
ntohs(s[1]),
ntohs(s[2]),
ntohs(s[3]),
ntohs(s[4]),
ntohs(s[5]),
ntohs(s[6]),
ntohs(s[7]),
ntohs(((struct sockaddr_in6 *)addr)->sin6_port));
}
}
else
strcpy(buffer, "?");
return buffer;
}
//=============================================================================
int UDP_StringToAddr (const char *string, struct qsockaddr *addr)
int UDP4_StringToAddr (const char *string, struct qsockaddr *addr)
{
int ha1, ha2, ha3, ha4, hp, ipaddr;
@ -380,9 +439,18 @@ int UDP_GetSocketAddr (sys_socket_t socketid, struct qsockaddr *addr)
if (getsockname(socketid, (struct sockaddr *)addr, &addrlen) != 0)
return -1;
if (addr->qsa_family == AF_INET)
{
a = ((struct sockaddr_in *)addr)->sin_addr.s_addr;
if (a == 0 || a == htonl(INADDR_LOOPBACK))
((struct sockaddr_in *)addr)->sin_addr.s_addr = myAddr;
((struct sockaddr_in *)addr)->sin_addr.s_addr = myAddr4;
}
else if (addr->qsa_family == AF_INET6)
{
static const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT;
if (!memcmp(&((struct sockaddr_in6 *)addr)->sin6_addr, &in6addr_any, sizeof(in6addr_any)))
memcpy(&((struct sockaddr_in6 *)addr)->sin6_addr, &myAddrv6, sizeof(((struct sockaddr_in6 *)addr)->sin6_addr));
}
return 0;
}
@ -390,6 +458,8 @@ int UDP_GetSocketAddr (sys_socket_t socketid, struct qsockaddr *addr)
//=============================================================================
int UDP_GetNameFromAddr (struct qsockaddr *addr, char *name)
{
if (addr->qsa_family == AF_INET)
{
struct hostent *hostentry;
@ -400,26 +470,45 @@ int UDP_GetNameFromAddr (struct qsockaddr *addr, char *name)
strncpy (name, (char *)hostentry->h_name, NET_NAMELEN - 1);
return 0;
}
}
else if (addr->qsa_family == AF_INET6)
{
//meh, don't bother, its unreliable anyway.
}
strcpy (name, UDP_AddrToString (addr));
strcpy (name, UDP_AddrToString (addr, false));
return 0;
}
//=============================================================================
int UDP_GetAddrFromName (const char *name, struct qsockaddr *addr)
int UDP4_GetAddrFromName (const char *name, struct qsockaddr *addr)
{
struct hostent *hostentry;
char *colon;
unsigned short port = net_hostport;
if (name[0] >= '0' && name[0] <= '9')
return PartialIPAddress (name, addr);
colon = strrchr(name, ':');
if (colon)
{
char dupe[MAXHOSTNAMELEN];
if (colon-name+1 > MAXHOSTNAMELEN)
return -1;
memcpy(dupe, name, colon-name);
dupe[colon-name] = 0;
hostentry = gethostbyname (dupe);
port = strtoul(colon+1, NULL, 10);
}
else
hostentry = gethostbyname (name);
if (!hostentry)
if (!hostentry || hostentry->h_addrtype != AF_INET)
return -1;
addr->qsa_family = AF_INET;
((struct sockaddr_in *)addr)->sin_port = htons((unsigned short)net_hostport);
((struct sockaddr_in *)addr)->sin_port = htons(port);
((struct sockaddr_in *)addr)->sin_addr.s_addr =
*(in_addr_t *)hostentry->h_addr_list[0];
@ -433,6 +522,8 @@ int UDP_AddrCompare (struct qsockaddr *addr1, struct qsockaddr *addr2)
if (addr1->qsa_family != addr2->qsa_family)
return -1;
if (addr1->qsa_family == AF_INET)
{
if (((struct sockaddr_in *)addr1)->sin_addr.s_addr !=
((struct sockaddr_in *)addr2)->sin_addr.s_addr)
return -1;
@ -443,20 +534,310 @@ int UDP_AddrCompare (struct qsockaddr *addr1, struct qsockaddr *addr2)
return 0;
}
else if (addr1->qsa_family == AF_INET6)
{
if (memcmp( &((struct sockaddr_in6 *)addr1)->sin6_addr,
&((struct sockaddr_in6 *)addr2)->sin6_addr,
sizeof(((struct sockaddr_in6 *)addr1)->sin6_addr)))
return -1;
if (((struct sockaddr_in6 *)addr1)->sin6_port !=
((struct sockaddr_in6 *)addr2)->sin6_port)
return 1;
if (((struct sockaddr_in6 *)addr1)->sin6_scope_id &&
((struct sockaddr_in6 *)addr2)->sin6_scope_id &&
((struct sockaddr_in6 *)addr1)->sin6_scope_id !=
((struct sockaddr_in6 *)addr2)->sin6_scope_id) //the ipv6 scope id is for use with link-local addresses, to identify the specific interface.
return 1;
return 0;
}
else
return -1;
}
//=============================================================================
int UDP_GetSocketPort (struct qsockaddr *addr)
{
if (addr->qsa_family == AF_INET)
return ntohs(((struct sockaddr_in *)addr)->sin_port);
else if (addr->qsa_family == AF_INET6)
return ntohs(((struct sockaddr_in6 *)addr)->sin6_port);
else
return -1;
}
int UDP_SetSocketPort (struct qsockaddr *addr, int port)
{
if (addr->qsa_family == AF_INET)
((struct sockaddr_in *)addr)->sin_port = htons((unsigned short)port);
else if (addr->qsa_family == AF_INET6)
((struct sockaddr_in6 *)addr)->sin6_port = htons((unsigned short)port);
else
return -1;
return 0;
}
//=============================================================================
sys_socket_t UDP6_Init (void)
{
char *colon;
struct qsockaddr addr;
if (COM_CheckParm ("-noudp") || COM_CheckParm ("-noudp6"))
return INVALID_SOCKET;
// TODO: determine my name & address
if ((net_controlsocket6 = UDP6_OpenSocket(0)) == INVALID_SOCKET)
{
Con_SafePrintf("UDP6_Init: Unable to open control socket, UDPv6 disabled\n");
return INVALID_SOCKET;
}
UDP_GetSocketAddr (net_controlsocket6, &addr);
strcpy(my_ipv6_address, UDP_AddrToString (&addr, false));
colon = strrchr (my_ipv6_address, ':');
if (colon)
*colon = 0;
Con_SafePrintf("UDPv6 Initialized\n");
ipv6Available = true;
return net_controlsocket6;
}
//=============================================================================
void UDP6_Shutdown (void)
{
UDP6_Listen (false);
UDP_CloseSocket (net_controlsocket6);
}
//=============================================================================
sys_socket_t UDP6_Listen (qboolean state)
{
if (state)
{
// enable listening
if (net_acceptsocket6 == INVALID_SOCKET)
{
if ((net_acceptsocket6 = UDP6_OpenSocket (net_hostport)) == INVALID_SOCKET)
Sys_Error ("UDP6_Listen: Unable to open accept socket");
}
}
else
{
// disable listening
if (net_acceptsocket6 != INVALID_SOCKET)
{
UDP_CloseSocket (net_acceptsocket6);
net_acceptsocket6 = INVALID_SOCKET;
}
}
return net_acceptsocket6;
}
//=============================================================================
sys_socket_t UDP6_OpenSocket (int port)
{
sys_socket_t newsocket;
struct sockaddr_in6 address;
int _true = 1;
int err;
if ((newsocket = socket (PF_INET6, SOCK_DGRAM, IPPROTO_UDP)) == INVALID_SOCKET)
{
err = SOCKETERRNO;
Con_SafePrintf("UDP6_OpenSocket: %s\n", socketerror(err));
return INVALID_SOCKET;
}
setsockopt(newsocket, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&_true, sizeof(_true));
if (ioctlsocket (newsocket, FIONBIO, &_true) == SOCKET_ERROR)
goto ErrorReturn;
memset(&address, 0, sizeof(struct sockaddr_in6));
address.sin6_family = AF_INET6;
memset(&address.sin6_addr, 0, sizeof(address.sin6_addr));
address.sin6_port = htons((unsigned short)port);
if (bind (newsocket, (struct sockaddr *)&address, sizeof(address)) == 0)
{
//we don't know if we're the server or not. oh well.
struct ipv6_mreq req;
memset(&req, 0, sizeof(req));
req.ipv6mr_multiaddr.s6_addr[0] = 0xff;
req.ipv6mr_multiaddr.s6_addr[1] = 0x03;
req.ipv6mr_multiaddr.s6_addr[15] = 0x01;
req.ipv6mr_interface = 0;
setsockopt(newsocket, IPPROTO_IPV6, IPV6_JOIN_GROUP, (char *)&req, sizeof(req));
return newsocket;
}
ErrorReturn:
err = SOCKETERRNO;
Con_SafePrintf("UDP6_OpenSocket: %s\n", socketerror(err));
UDP_CloseSocket (newsocket);
return INVALID_SOCKET;
}
//=============================================================================
sys_socket_t UDP6_CheckNewConnections (void)
{
int available;
struct sockaddr_in from;
socklen_t fromlen;
char buff[1];
if (net_acceptsocket6 == INVALID_SOCKET)
return INVALID_SOCKET;
if (ioctl (net_acceptsocket6, FIONREAD, &available) == -1)
{
int err = SOCKETERRNO;
Sys_Error ("UDP6: ioctlsocket (FIONREAD) failed (%s)", socketerror(err));
}
if (available)
return net_acceptsocket6;
// quietly absorb empty packets
fromlen = sizeof(from);
recvfrom (net_acceptsocket6, buff, 0, 0, (struct sockaddr *) &from, &fromlen);
return INVALID_SOCKET;
}
//=============================================================================
int UDP6_Broadcast (sys_socket_t socketid, byte *buf, int len)
{
struct sockaddr_in6 address;
memset(&address, 0, sizeof(struct sockaddr_in6));
address.sin6_family = AF_INET6;
memset(&address.sin6_addr, 0, sizeof(address.sin6_addr));
address.sin6_addr.s6_addr[0] = 0xff;
address.sin6_addr.s6_addr[1] = 0x03;
address.sin6_addr.s6_addr[15] = 0x1;
address.sin6_port = htons((unsigned short)net_hostport);
return UDP_Write (socketid, buf, len, (struct qsockaddr *)&address);
}
//=============================================================================
int UDP6_StringToAddr (const char *string, struct qsockaddr *addr)
{ //This is never actually called...
Con_SafePrintf("UDP6_StringToAddr: %s\n", string);
return UDP6_GetAddrFromName(string, addr);
}
//=============================================================================
int UDP6_GetAddrFromName (const char *name, struct qsockaddr *addr)
{
struct addrinfo *addrinfo = NULL;
struct addrinfo *pos;
struct addrinfo udp6hint;
int error;
char *port;
char dupbase[256];
size_t len;
qboolean success = false;
memset(&udp6hint, 0, sizeof(udp6hint));
udp6hint.ai_family = 0;//Any... we check for AF_INET6 or 4
udp6hint.ai_socktype = SOCK_DGRAM;
udp6hint.ai_protocol = IPPROTO_UDP;
if (*name == '[')
{
port = strstr(name, "]");
if (!port)
error = EAI_NONAME;
else
{
len = port - (name+1);
if (len >= sizeof(dupbase))
len = sizeof(dupbase)-1;
strncpy(dupbase, name+1, len);
dupbase[len] = '\0';
error = getaddrinfo(dupbase, (port[1] == ':')?port+2:NULL, &udp6hint, &addrinfo);
}
}
else
{
port = strrchr(name, ':');
if (port)
{
len = port - name;
if (len >= sizeof(dupbase))
len = sizeof(dupbase)-1;
strncpy(dupbase, name, len);
dupbase[len] = '\0';
error = getaddrinfo(dupbase, port+1, &udp6hint, &addrinfo);
}
else
error = EAI_NONAME;
if (error) //failed, try string with no port.
error = getaddrinfo(name, NULL, &udp6hint, &addrinfo); //remember, this func will return any address family that could be using the udp protocol... (ip4 or ip6)
}
if (!error)
{
((struct sockaddr*)addr)->sa_family = 0;
for (pos = addrinfo; pos; pos = pos->ai_next)
{
if (0)//pos->ai_family == AF_INET)
{
memcpy(addr, pos->ai_addr, pos->ai_addrlen);
success = true;
break;
}
if (pos->ai_family == AF_INET6 && !success)
{
memcpy(addr, pos->ai_addr, pos->ai_addrlen);
success = true;
}
}
freeaddrinfo (addrinfo);
}
if (success)
{
if (((struct sockaddr*)addr)->sa_family == AF_INET)
{
if (!((struct sockaddr_in *)addr)->sin_port)
((struct sockaddr_in *)addr)->sin_port = htons(net_hostport);
}
else if (((struct sockaddr*)addr)->sa_family == AF_INET6)
{
if (!((struct sockaddr_in6 *)addr)->sin6_port)
((struct sockaddr_in6 *)addr)->sin6_port = htons(net_hostport);
}
return 0;
}
return -1;
}
//=============================================================================

View File

@ -22,21 +22,40 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#ifndef __net_udp_h
#define __net_udp_h
sys_socket_t UDP_Init (void);
void UDP_Shutdown (void);
void UDP_Listen (qboolean state);
sys_socket_t UDP_OpenSocket (int port);
sys_socket_t UDP4_Init (void);
void UDP4_Shutdown (void);
sys_socket_t UDP4_Listen (qboolean state);
sys_socket_t UDP4_OpenSocket (int port);
int UDP_CloseSocket (sys_socket_t socketid);
int UDP_Connect (sys_socket_t socketid, struct qsockaddr *addr);
sys_socket_t UDP_CheckNewConnections (void);
sys_socket_t UDP4_CheckNewConnections (void);
int UDP_Read (sys_socket_t socketid, byte *buf, int len, struct qsockaddr *addr);
int UDP_Write (sys_socket_t socketid, byte *buf, int len, struct qsockaddr *addr);
int UDP_Broadcast (sys_socket_t socketid, byte *buf, int len);
const char *UDP_AddrToString (struct qsockaddr *addr);
int UDP_StringToAddr (const char *string, struct qsockaddr *addr);
int UDP4_Broadcast (sys_socket_t socketid, byte *buf, int len);
const char *UDP_AddrToString (struct qsockaddr *addr, qboolean masked);
int UDP4_StringToAddr (const char *string, struct qsockaddr *addr);
int UDP_GetSocketAddr (sys_socket_t socketid, struct qsockaddr *addr);
int UDP_GetNameFromAddr (struct qsockaddr *addr, char *name);
int UDP_GetAddrFromName (const char *name, struct qsockaddr *addr);
int UDP4_GetAddrFromName (const char *name, struct qsockaddr *addr);
int UDP_AddrCompare (struct qsockaddr *addr1, struct qsockaddr *addr2);
int UDP_GetSocketPort (struct qsockaddr *addr);
int UDP_SetSocketPort (struct qsockaddr *addr, int port);
sys_socket_t UDP6_Init (void);
void UDP6_Shutdown (void);
sys_socket_t UDP6_Listen (qboolean state);
sys_socket_t UDP6_OpenSocket (int port);
int UDP_CloseSocket (sys_socket_t socketid);
int UDP_Connect (sys_socket_t socketid, struct qsockaddr *addr);
sys_socket_t UDP6_CheckNewConnections (void);
int UDP_Read (sys_socket_t socketid, byte *buf, int len, struct qsockaddr *addr);
int UDP_Write (sys_socket_t socketid, byte *buf, int len, struct qsockaddr *addr);
int UDP6_Broadcast (sys_socket_t socketid, byte *buf, int len);
const char *UDP_AddrToString (struct qsockaddr *addr, qboolean masked);
int UDP6_StringToAddr (const char *string, struct qsockaddr *addr);
int UDP_GetSocketAddr (sys_socket_t socketid, struct qsockaddr *addr);
int UDP_GetNameFromAddr (struct qsockaddr *addr, char *name);
int UDP6_GetAddrFromName (const char *name, struct qsockaddr *addr);
int UDP_AddrCompare (struct qsockaddr *addr1, struct qsockaddr *addr2);
int UDP_GetSocketPort (struct qsockaddr *addr);
int UDP_SetSocketPort (struct qsockaddr *addr, int port);

View File

@ -37,6 +37,7 @@ net_driver_t net_drivers[] =
Loop_SearchForHosts,
Loop_Connect,
Loop_CheckNewConnections,
Loop_GetAnyMessage,
Loop_GetMessage,
Loop_SendMessage,
Loop_SendUnreliableMessage,
@ -53,6 +54,7 @@ net_driver_t net_drivers[] =
Datagram_SearchForHosts,
Datagram_Connect,
Datagram_CheckNewConnections,
Datagram_GetAnyMessage,
Datagram_GetMessage,
Datagram_SendMessage,
Datagram_SendUnreliableMessage,
@ -74,26 +76,49 @@ net_landriver_t net_landrivers[] =
{ "Winsock TCPIP",
false,
0,
WINS_Init,
WINS_Shutdown,
WINS_Listen,
WINS_OpenSocket,
WINIPv4_Init,
WINIPv4_Shutdown,
WINIPv4_Listen,
WINIPv4_OpenSocket,
WINS_CloseSocket,
WINS_Connect,
WINS_CheckNewConnections,
WINIPv4_CheckNewConnections,
WINS_Read,
WINS_Write,
WINS_Broadcast,
WINIPv4_Broadcast,
WINS_AddrToString,
WINS_StringToAddr,
WINIPv4_StringToAddr,
WINS_GetSocketAddr,
WINS_GetNameFromAddr,
WINS_GetAddrFromName,
WINIPv4_GetNameFromAddr,
WINIPv4_GetAddrFromName,
WINS_AddrCompare,
WINS_GetSocketPort,
WINS_SetSocketPort
},
#ifdef IPPROTO_IPV6
{ "Winsock IPv6",
false,
0,
WINIPv6_Init,
WINIPv6_Shutdown,
WINIPv6_Listen,
WINIPv6_OpenSocket,
WINS_CloseSocket,
WINS_Connect,
WINIPv6_CheckNewConnections,
WINS_Read,
WINS_Write,
WINIPv6_Broadcast,
WINS_AddrToString,
WINIPv6_StringToAddr,
WINS_GetSocketAddr,
WINIPv6_GetNameFromAddr,
WINIPv6_GetAddrFromName,
WINS_AddrCompare,
WINS_GetSocketPort,
WINS_SetSocketPort
},
#endif
{ "Winsock IPX",
false,
0,

View File

@ -24,12 +24,30 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "quakedef.h"
#include "net_defs.h"
static sys_socket_t net_acceptsocket = INVALID_SOCKET; // socket for fielding new connections
static sys_socket_t net_controlsocket;
static sys_socket_t net_broadcastsocket = 0;
static struct sockaddr_in broadcastaddr;
//ipv4 defs
static sys_socket_t netv4_acceptsocket = INVALID_SOCKET; // socket for fielding new connections
static sys_socket_t netv4_controlsocket;
static sys_socket_t netv4_broadcastsocket = INVALID_SOCKET;
static struct sockaddr_in broadcastaddrv4;
static in_addr_t myAddrv4, bindAddrv4; //spike --keeping separate bind and detected values.
//ipv6 defs
#ifdef IPPROTO_IPV6
typedef struct in_addr6 in_addr6_t;
static sys_socket_t netv6_acceptsocket = INVALID_SOCKET; // socket for fielding new connections
static sys_socket_t netv6_controlsocket;
static struct sockaddr_in6 broadcastaddrv6;
static in_addr6_t myAddrv6, bindAddrv6;
#ifndef IPV6_V6ONLY
#define IPV6_V6ONLY 27
#endif
#endif
//getaddrinfo was added with winxp, but earlier versions don't do ipv6.
//we don't use hybrid sockets, so things are a little easier when it comes to xp vs vista.
//we don't detect the win2k ipv6 tech preview thing. it has different sized addresses, so lets hope microsoft's code handles that if it ever comes up.
int (WSAAPI *qgetaddrinfo)(const char *nodename, const char *servname, const struct addrinfo *hints, struct addrinfo **res);
static in_addr_t myAddr;
#include "net_wins.h"
@ -70,20 +88,20 @@ static INT_PTR PASCAL FAR BlockingHook (void)
#endif /* ! _USE_WINSOCK2 */
static void WINS_GetLocalAddress (void)
static void WINIPv4_GetLocalAddress (void)
{
struct hostent *local = NULL;
char buff[MAXHOSTNAMELEN];
in_addr_t addr;
int err;
if (myAddr != INADDR_ANY)
if (myAddrv4 != INADDR_ANY)
return;
if (gethostname(buff, MAXHOSTNAMELEN) == SOCKET_ERROR)
{
err = SOCKETERRNO;
Con_SafePrintf("WINS_GetLocalAddress: gethostname failed (%s)\n",
Con_SafePrintf("WINIPV4_GetLocalAddress: gethostname failed (%s)\n",
socketerror(err));
return;
}
@ -100,24 +118,24 @@ static void WINS_GetLocalAddress (void)
#endif
if (local == NULL)
{
Con_SafePrintf("WINS_GetLocalAddress: gethostbyname failed (%s)\n",
Con_SafePrintf("WINIPV4_GetLocalAddress: gethostbyname failed (%s)\n",
__WSAE_StrError(err));
return;
}
myAddr = *(in_addr_t *)local->h_addr_list[0];
myAddrv4 = *(in_addr_t *)local->h_addr_list[0];
addr = ntohl(myAddr);
sprintf(my_tcpip_address, "%ld.%ld.%ld.%ld", (addr >> 24) & 0xff, (addr >> 16) & 0xff, (addr >> 8) & 0xff, addr & 0xff);
addr = ntohl(myAddrv4);
sprintf(my_ipv4_address, "%ld.%ld.%ld.%ld", (addr >> 24) & 0xff, (addr >> 16) & 0xff, (addr >> 8) & 0xff, addr & 0xff);
}
sys_socket_t WINS_Init (void)
sys_socket_t WINIPv4_Init (void)
{
int i, err;
char buff[MAXHOSTNAMELEN];
if (COM_CheckParm ("-noudp"))
if (COM_CheckParm ("-noudp") || COM_CheckParm ("-noudp4"))
return -1;
if (winsock_initialized == 0)
@ -148,10 +166,10 @@ sys_socket_t WINS_Init (void)
{
if (i < com_argc-1)
{
myAddr = inet_addr(com_argv[i+1]);
if (myAddr == INADDR_NONE)
bindAddrv4 = inet_addr(com_argv[i+1]);
if (bindAddrv4 == INADDR_NONE)
Sys_Error ("%s is not a valid IP address", com_argv[i+1]);
strcpy(my_tcpip_address, com_argv[i+1]);
strcpy(my_ipv4_address, com_argv[i+1]);
}
else
{
@ -160,11 +178,13 @@ sys_socket_t WINS_Init (void)
}
else
{
myAddr = INADDR_ANY;
strcpy(my_tcpip_address, "INADDR_ANY");
bindAddrv4 = INADDR_ANY;
strcpy(my_ipv4_address, "INADDR_ANY");
}
if ((net_controlsocket = WINS_OpenSocket(0)) == INVALID_SOCKET)
myAddrv4 = bindAddrv4;
if ((netv4_controlsocket = WINIPv4_OpenSocket(0)) == INVALID_SOCKET)
{
Con_SafePrintf("WINS_Init: Unable to open control socket, UDP disabled\n");
if (--winsock_initialized == 0)
@ -172,51 +192,51 @@ sys_socket_t WINS_Init (void)
return INVALID_SOCKET;
}
broadcastaddr.sin_family = AF_INET;
broadcastaddr.sin_addr.s_addr = INADDR_BROADCAST;
broadcastaddr.sin_port = htons((unsigned short)net_hostport);
broadcastaddrv4.sin_family = AF_INET;
broadcastaddrv4.sin_addr.s_addr = INADDR_BROADCAST;
broadcastaddrv4.sin_port = htons((unsigned short)net_hostport);
Con_SafePrintf("UDP Initialized\n");
tcpipAvailable = true;
Con_SafePrintf("IPv4 UDP Initialized\n");
ipv4Available = true;
return net_controlsocket;
return netv4_controlsocket;
}
//=============================================================================
void WINS_Shutdown (void)
void WINIPv4_Shutdown (void)
{
WINS_Listen (false);
WINS_CloseSocket (net_controlsocket);
WINIPv4_Listen (false);
WINS_CloseSocket (netv4_controlsocket);
if (--winsock_initialized == 0)
WSACleanup ();
}
//=============================================================================
void WINS_Listen (qboolean state)
sys_socket_t WINIPv4_Listen (qboolean state)
{
// enable listening
if (state)
{
if (net_acceptsocket != INVALID_SOCKET)
return;
WINS_GetLocalAddress();
if ((net_acceptsocket = WINS_OpenSocket (net_hostport)) == INVALID_SOCKET)
Sys_Error ("WINS_Listen: Unable to open accept socket");
return;
if (netv4_acceptsocket != INVALID_SOCKET)
return netv4_acceptsocket;
WINIPv4_GetLocalAddress();
netv4_acceptsocket = WINIPv4_OpenSocket (net_hostport);
return netv4_acceptsocket;
}
// disable listening
if (net_acceptsocket == INVALID_SOCKET)
return;
WINS_CloseSocket (net_acceptsocket);
net_acceptsocket = INVALID_SOCKET;
if (netv4_acceptsocket == INVALID_SOCKET)
return INVALID_SOCKET;
WINS_CloseSocket (netv4_acceptsocket);
netv4_acceptsocket = INVALID_SOCKET;
return INVALID_SOCKET;
}
//=============================================================================
sys_socket_t WINS_OpenSocket (int port)
sys_socket_t WINIPv4_OpenSocket (int port)
{
sys_socket_t newsocket;
struct sockaddr_in address;
@ -235,16 +255,16 @@ sys_socket_t WINS_OpenSocket (int port)
memset(&address, 0, sizeof(struct sockaddr_in));
address.sin_family = AF_INET;
address.sin_addr.s_addr = myAddr;
address.sin_addr.s_addr = bindAddrv4;
address.sin_port = htons((unsigned short)port);
if (bind (newsocket, (struct sockaddr *)&address, sizeof(address)) == 0)
return newsocket;
if (tcpipAvailable)
if (ipv4Available)
{
err = SOCKETERRNO;
Sys_Error ("Unable to bind to %s (%s)",
WINS_AddrToString ((struct qsockaddr *) &address),
Con_Warning ("Unable to bind to %s (%s)\n",
WINS_AddrToString ((struct qsockaddr *) &address, false),
socketerror(err));
return INVALID_SOCKET; /* not reached */
}
@ -261,8 +281,8 @@ ErrorReturn:
int WINS_CloseSocket (sys_socket_t socketid)
{
if (socketid == net_broadcastsocket)
net_broadcastsocket = 0;
if (socketid == netv4_broadcastsocket)
netv4_broadcastsocket = INVALID_SOCKET;
return closesocket (socketid);
}
@ -317,7 +337,7 @@ static int PartialIPAddress (const char *in, struct qsockaddr *hostaddr)
hostaddr->qsa_family = AF_INET;
((struct sockaddr_in *)hostaddr)->sin_port = htons((unsigned short)port);
((struct sockaddr_in *)hostaddr)->sin_addr.s_addr =
(myAddr & htonl(mask)) | htonl(addr);
(myAddrv4 & htonl(mask)) | htonl(addr);
return 0;
}
@ -331,17 +351,17 @@ int WINS_Connect (sys_socket_t socketid, struct qsockaddr *addr)
//=============================================================================
sys_socket_t WINS_CheckNewConnections (void)
sys_socket_t WINIPv4_CheckNewConnections (void)
{
char buf[4096];
if (net_acceptsocket == INVALID_SOCKET)
if (netv4_acceptsocket == INVALID_SOCKET)
return INVALID_SOCKET;
if (recvfrom (net_acceptsocket, buf, sizeof(buf), MSG_PEEK, NULL, NULL)
if (recvfrom (netv4_acceptsocket, buf, sizeof(buf), MSG_PEEK, NULL, NULL)
!= SOCKET_ERROR)
{
return net_acceptsocket;
return netv4_acceptsocket;
}
return INVALID_SOCKET;
}
@ -359,6 +379,9 @@ int WINS_Read (sys_socket_t socketid, byte *buf, int len, struct qsockaddr *addr
int err = SOCKETERRNO;
if (err == NET_EWOULDBLOCK || err == NET_ECONNREFUSED)
return 0;
if (err == WSAECONNRESET)
Con_DPrintf ("WINS_Read, recvfrom: %s (%s)\n", socketerror(err), WINS_AddrToString(addr, false));
else
Con_SafePrintf ("WINS_Read, recvfrom: %s\n", socketerror(err));
}
return ret;
@ -378,31 +401,31 @@ static int WINS_MakeSocketBroadcastCapable (sys_socket_t socketid)
Con_SafePrintf ("UDP, setsockopt: %s\n", socketerror(err));
return -1;
}
net_broadcastsocket = socketid;
netv4_broadcastsocket = socketid;
return 0;
}
//=============================================================================
int WINS_Broadcast (sys_socket_t socketid, byte *buf, int len)
int WINIPv4_Broadcast (sys_socket_t socketid, byte *buf, int len)
{
int ret;
if (socketid != net_broadcastsocket)
if (socketid != netv4_broadcastsocket)
{
if (net_broadcastsocket != 0)
if (netv4_broadcastsocket != INVALID_SOCKET)
Sys_Error("Attempted to use multiple broadcasts sockets");
WINS_GetLocalAddress();
WINIPv4_GetLocalAddress();
ret = WINS_MakeSocketBroadcastCapable (socketid);
if (ret == -1)
{
Con_Printf("Unable to make socket broadcast capable\n");
Con_SafePrintf("Unable to make socket broadcast capable\n");
return ret;
}
}
return WINS_Write (socketid, buf, len, (struct qsockaddr *)&broadcastaddr);
return WINS_Write (socketid, buf, len, (struct qsockaddr *)&broadcastaddrv4);
}
//=============================================================================
@ -425,21 +448,81 @@ int WINS_Write (sys_socket_t socketid, byte *buf, int len, struct qsockaddr *add
//=============================================================================
const char *WINS_AddrToString (struct qsockaddr *addr)
unsigned short ntohs_v6word(struct qsockaddr *addr, int wordnum)
{
static char buffer[22];
unsigned char *ptr = ((struct sockaddr_in6 *)addr)->sin6_addr.s6_addr + wordnum*2;
return (unsigned short)(ptr[0]<<8) | ptr[1];
}
const char *WINS_AddrToString (struct qsockaddr *addr, qboolean masked)
{
static char buffer[64];
int haddr;
#ifdef IPPROTO_IPV6
if (addr->qsa_family == AF_INET6)
{
if (masked)
{
q_snprintf(buffer, sizeof(buffer), "[%x:%x:%x:%x::]/64",
ntohs_v6word(addr, 0),
ntohs_v6word(addr, 1),
ntohs_v6word(addr, 2),
ntohs_v6word(addr, 3));
}
else
{
if (((struct sockaddr_in6 *)addr)->sin6_scope_id)
{
q_snprintf(buffer, sizeof(buffer), "[%x:%x:%x:%x:%x:%x:%x:%x%%%i]:%d",
ntohs_v6word(addr, 0),
ntohs_v6word(addr, 1),
ntohs_v6word(addr, 2),
ntohs_v6word(addr, 3),
ntohs_v6word(addr, 4),
ntohs_v6word(addr, 5),
ntohs_v6word(addr, 6),
ntohs_v6word(addr, 7),
(int)((struct sockaddr_in6 *)addr)->sin6_scope_id,
ntohs(((struct sockaddr_in6 *)addr)->sin6_port));
}
else
{
q_snprintf(buffer, sizeof(buffer), "[%x:%x:%x:%x:%x:%x:%x:%x]:%d",
ntohs_v6word(addr, 0),
ntohs_v6word(addr, 1),
ntohs_v6word(addr, 2),
ntohs_v6word(addr, 3),
ntohs_v6word(addr, 4),
ntohs_v6word(addr, 5),
ntohs_v6word(addr, 6),
ntohs_v6word(addr, 7),
ntohs(((struct sockaddr_in6 *)addr)->sin6_port));
}
}
}
else
#endif
{
haddr = ntohl(((struct sockaddr_in *)addr)->sin_addr.s_addr);
if (masked)
{
sprintf(buffer, "%d.%d.%d.0/24", (haddr >> 24) & 0xff,
(haddr >> 16) & 0xff, (haddr >> 8) & 0xff);
}
else
{
sprintf(buffer, "%d.%d.%d.%d:%d", (haddr >> 24) & 0xff,
(haddr >> 16) & 0xff, (haddr >> 8) & 0xff, haddr & 0xff,
ntohs(((struct sockaddr_in *)addr)->sin_port));
}
}
return buffer;
}
//=============================================================================
int WINS_StringToAddr (const char *string, struct qsockaddr *addr)
int WINIPv4_StringToAddr (const char *string, struct qsockaddr *addr)
{
int ha1, ha2, ha3, ha4, hp, ipaddr;
@ -457,21 +540,31 @@ int WINS_StringToAddr (const char *string, struct qsockaddr *addr)
int WINS_GetSocketAddr (sys_socket_t socketid, struct qsockaddr *addr)
{
socklen_t addrlen = sizeof(struct qsockaddr);
in_addr_t a;
memset(addr, 0, sizeof(struct qsockaddr));
getsockname(socketid, (struct sockaddr *)addr, &addrlen);
a = ((struct sockaddr_in *)addr)->sin_addr.s_addr;
if (addr->qsa_family == AF_INET)
{
in_addr_t a = ((struct sockaddr_in *)addr)->sin_addr.s_addr;
if (a == 0 || a == htonl(INADDR_LOOPBACK))
((struct sockaddr_in *)addr)->sin_addr.s_addr = myAddr;
((struct sockaddr_in *)addr)->sin_addr.s_addr = myAddrv4;
}
#ifdef IPPROTO_IPV6
if (addr->qsa_family == AF_INET6)
{
static const in_addr6_t in6addr_any;// = IN6ADDR_ANY_INIT;
if (!memcmp(&((struct sockaddr_in6 *)addr)->sin6_addr, &in6addr_any, sizeof(in_addr6_t)))
memcpy(&((struct sockaddr_in6 *)addr)->sin6_addr, &myAddrv6, sizeof(struct sockaddr_in6));
}
#endif
return 0;
}
//=============================================================================
int WINS_GetNameFromAddr (struct qsockaddr *addr, char *name)
int WINIPv4_GetNameFromAddr (struct qsockaddr *addr, char *name)
{
struct hostent *hostentry;
@ -483,25 +576,41 @@ int WINS_GetNameFromAddr (struct qsockaddr *addr, char *name)
return 0;
}
Q_strcpy (name, WINS_AddrToString (addr));
Q_strcpy (name, WINS_AddrToString (addr, false));
return 0;
}
//=============================================================================
int WINS_GetAddrFromName (const char *name, struct qsockaddr *addr)
int WINIPv4_GetAddrFromName (const char *name, struct qsockaddr *addr)
{
struct hostent *hostentry;
char *colon;
unsigned short port = net_hostport;
if (name[0] >= '0' && name[0] <= '9')
return PartialIPAddress (name, addr);
colon = strrchr(name, ':');
if (colon)
{
char dupe[MAXHOSTNAMELEN];
if (colon-name+1 > MAXHOSTNAMELEN)
return -1;
memcpy(dupe, name, colon-name);
dupe[colon-name] = 0;
if (strchr(dupe, ':'))
return -1; //don't resolve a name to an ipv4 address if it has multiple colons in it. its probably an ipx or ipv6 address, and I'd rather not block on any screwed dns resolves
hostentry = gethostbyname (dupe);
port = strtoul(colon+1, NULL, 10);
}
else
hostentry = gethostbyname (name);
if (!hostentry)
return -1;
addr->qsa_family = AF_INET;
((struct sockaddr_in *)addr)->sin_port = htons((unsigned short)net_hostport);
((struct sockaddr_in *)addr)->sin_port = htons(port);
((struct sockaddr_in *)addr)->sin_addr.s_addr =
*(in_addr_t *)hostentry->h_addr_list[0];
@ -515,6 +624,24 @@ int WINS_AddrCompare (struct qsockaddr *addr1, struct qsockaddr *addr2)
if (addr1->qsa_family != addr2->qsa_family)
return -1;
#ifdef IPPROTO_IPV6
if (addr1->qsa_family == AF_INET6)
{
if (memcmp(&((struct sockaddr_in6 *)addr1)->sin6_addr, &((struct sockaddr_in6 *)addr2)->sin6_addr, sizeof(((struct sockaddr_in6 *)addr2)->sin6_addr)))
return -1;
if (((struct sockaddr_in6 *)addr1)->sin6_port !=
((struct sockaddr_in6 *)addr2)->sin6_port)
return 1;
if (((struct sockaddr_in6 *)addr1)->sin6_scope_id &&
((struct sockaddr_in6 *)addr2)->sin6_scope_id &&
((struct sockaddr_in6 *)addr1)->sin6_scope_id !=
((struct sockaddr_in6 *)addr2)->sin6_scope_id) //the ipv6 scope id is for use with link-local addresses, to identify the specific interface.
return 1;
}
else
#endif
{
if (((struct sockaddr_in *)addr1)->sin_addr.s_addr !=
((struct sockaddr_in *)addr2)->sin_addr.s_addr)
return -1;
@ -522,6 +649,7 @@ int WINS_AddrCompare (struct qsockaddr *addr1, struct qsockaddr *addr2)
if (((struct sockaddr_in *)addr1)->sin_port !=
((struct sockaddr_in *)addr2)->sin_port)
return 1;
}
return 0;
}
@ -530,15 +658,358 @@ int WINS_AddrCompare (struct qsockaddr *addr1, struct qsockaddr *addr2)
int WINS_GetSocketPort (struct qsockaddr *addr)
{
#ifdef IPPROTO_IPV6
if (addr->qsa_family == AF_INET6)
return ntohs(((struct sockaddr_in6 *)addr)->sin6_port);
else
#endif
return ntohs(((struct sockaddr_in *)addr)->sin_port);
}
int WINS_SetSocketPort (struct qsockaddr *addr, int port)
{
#ifdef IPPROTO_IPV6
if (addr->qsa_family == AF_INET6)
((struct sockaddr_in6 *)addr)->sin6_port = htons((unsigned short)port);
else
#endif
((struct sockaddr_in *)addr)->sin_port = htons((unsigned short)port);
return 0;
}
//=============================================================================
#ifdef IPPROTO_IPV6
//winxp (and possibly win2k) is dual stack.
//vista+ has a hybrid stack
static void WINIPv6_GetLocalAddress (void)
{
char buff[MAXHOSTNAMELEN];
int err;
struct addrinfo hints, *local = NULL;
// if (myAddrv6 != IN6ADDR_ANY)
// return;
if (gethostname(buff, MAXHOSTNAMELEN) == SOCKET_ERROR)
{
err = SOCKETERRNO;
Con_SafePrintf("WINIPv6_GetLocalAddress: gethostname failed (%s)\n",
socketerror(err));
return;
}
buff[MAXHOSTNAMELEN - 1] = 0;
#ifndef _USE_WINSOCK2
blocktime = Sys_DoubleTime();
WSASetBlockingHook(BlockingHook);
#endif
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET6;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_protocol = IPPROTO_UDP;
if (qgetaddrinfo && qgetaddrinfo(buff, NULL, &hints, &local) == 0)
{
size_t l;
q_strlcpy(my_ipv6_address, WINS_AddrToString((struct qsockaddr*)local->ai_addr, false), sizeof(my_ipv6_address));
l = strlen(my_ipv6_address);
if (l > 2 && !strcmp(my_ipv6_address+l-2, ":0"))
my_ipv6_address[l-2] = 0;
freeaddrinfo(local);
}
err = WSAGetLastError();
#ifndef _USE_WINSOCK2
WSAUnhookBlockingHook();
#endif
if (local == NULL)
{
Con_SafePrintf("WINIPv6_GetLocalAddress: gethostbyname failed (%s)\n",
__WSAE_StrError(err));
return;
}
}
sys_socket_t WINIPv6_Init (void)
{
int i;
char buff[MAXHOSTNAMELEN];
if (COM_CheckParm ("-noudp") || COM_CheckParm ("-noudp6"))
return -1;
qgetaddrinfo = (void*)GetProcAddress(GetModuleHandle("ws2_32.dll"), "getaddrinfo");
if (!qgetaddrinfo)
{
Con_SafePrintf("Winsock lacks getaddrinfo, ipv6 support is unavailable.\n");
return INVALID_SOCKET;
}
if (winsock_initialized == 0)
{
int err = WSAStartup(MAKEWORD(2,2), &winsockdata);
if (err != 0)
{
Con_SafePrintf("Winsock initialization failed (%s)\n",
socketerror(err));
return INVALID_SOCKET;
}
}
winsock_initialized++;
// determine my name & address
if (gethostname(buff, MAXHOSTNAMELEN) != 0)
{
int err = SOCKETERRNO;
Con_SafePrintf("WINIPv6_Init: gethostname failed (%s)\n",
socketerror(err));
}
else
{
buff[MAXHOSTNAMELEN - 1] = 0;
}
i = COM_CheckParm ("-ip6");
if (i)
{
if (i < com_argc-1)
{
if (WINIPv6_GetAddrFromName(com_argv[i+1], (struct qsockaddr*)&bindAddrv6))
Sys_Error ("%s is not a valid IPv6 address", com_argv[i+1]);
if (!*my_ipv6_address)
strcpy(my_ipv6_address, com_argv[i+1]);
}
else
{
Sys_Error ("WINIPv6_Init: you must specify an IP address after -ip");
}
}
else
{
memset(&bindAddrv6, 0, sizeof(bindAddrv6));
if (!*my_ipv6_address)
{
strcpy(my_ipv6_address, "[::]");
WINIPv6_GetLocalAddress();
}
}
myAddrv6 = bindAddrv6;
if ((netv6_controlsocket = WINIPv6_OpenSocket(0)) == INVALID_SOCKET)
{
Con_SafePrintf("WINIPv6_Init: Unable to open control socket, UDP disabled\n");
if (--winsock_initialized == 0)
WSACleanup ();
return INVALID_SOCKET;
}
broadcastaddrv6.sin6_family = AF_INET6;
memset(&broadcastaddrv6.sin6_addr, 0, sizeof(broadcastaddrv6.sin6_addr));
broadcastaddrv6.sin6_addr.s6_addr[0] = 0xff;
broadcastaddrv6.sin6_addr.s6_addr[1] = 0x03;
broadcastaddrv6.sin6_addr.s6_addr[15] = 0x01;
broadcastaddrv6.sin6_port = htons((unsigned short)net_hostport);
Con_SafePrintf("IPv6 UDP Initialized\n");
ipv6Available = true;
return netv6_controlsocket;
}
sys_socket_t WINIPv6_Listen (qboolean state)
{
if (state)
{
// enable listening
if (netv6_acceptsocket == INVALID_SOCKET)
netv6_acceptsocket = WINIPv6_OpenSocket (net_hostport);
}
else
{
// disable listening
if (netv6_acceptsocket != INVALID_SOCKET)
{
WINS_CloseSocket (netv6_acceptsocket);
netv6_acceptsocket = INVALID_SOCKET;
}
}
return netv6_acceptsocket;
}
void WINIPv6_Shutdown (void)
{
WINIPv6_Listen(false);
WINS_CloseSocket (netv6_controlsocket);
if (--winsock_initialized == 0)
WSACleanup ();
}
sys_socket_t WINIPv6_OpenSocket (int port)
{
sys_socket_t newsocket;
struct sockaddr_in6 address;
u_long _true = 1;
int err;
if ((newsocket = socket (PF_INET6, SOCK_DGRAM, IPPROTO_UDP)) == INVALID_SOCKET)
{
err = SOCKETERRNO;
Con_SafePrintf("WINS_OpenSocket: %s\n", socketerror(err));
return INVALID_SOCKET;
}
setsockopt(newsocket, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&_true, sizeof(_true));
if (ioctlsocket (newsocket, FIONBIO, &_true) == SOCKET_ERROR)
goto ErrorReturn;
memset(&address, 0, sizeof(address));
address.sin6_family = AF_INET6;
address.sin6_addr = bindAddrv6;
address.sin6_port = htons((unsigned short)port);
if (bind (newsocket, (struct sockaddr *)&address, sizeof(address)) == 0)
{
//we don't know if we're the server or not. oh well.
struct ipv6_mreq req;
req.ipv6mr_multiaddr = broadcastaddrv6.sin6_addr;
req.ipv6mr_interface = 0;
setsockopt(newsocket, IPPROTO_IPV6, IPV6_JOIN_GROUP, (char *)&req, sizeof(req));
return newsocket;
}
if (ipv6Available)
{
err = SOCKETERRNO;
Con_Warning ("Unable to bind to %s (%s)\n",
WINS_AddrToString ((struct qsockaddr *) &address, false),
socketerror(err));
return INVALID_SOCKET; /* not reached */
}
/* else: we are still in init phase, no need to error */
ErrorReturn:
err = SOCKETERRNO;
Con_SafePrintf("WINS_OpenSocket: %s\n", socketerror(err));
closesocket (newsocket);
return INVALID_SOCKET;
}
sys_socket_t WINIPv6_CheckNewConnections (void)
{
char buf[4096];
if (netv6_acceptsocket == INVALID_SOCKET)
return INVALID_SOCKET;
if (recvfrom (netv6_acceptsocket, buf, sizeof(buf), MSG_PEEK, NULL, NULL)
!= SOCKET_ERROR)
{
return netv6_acceptsocket;
}
return INVALID_SOCKET;
}
int WINIPv6_Broadcast (sys_socket_t socketid, byte *buf, int len)
{
broadcastaddrv6.sin6_port = htons((unsigned short)net_hostport);
return WINS_Write(socketid, buf, len, (struct qsockaddr*)&broadcastaddrv6);
}
int WINIPv6_StringToAddr (const char *string, struct qsockaddr *addr)
{ //This is never actually called...
return -1;
}
int WINIPv6_GetNameFromAddr (struct qsockaddr *addr, char *name)
{
//FIXME: should really do a reverse dns lookup.
q_strlcpy(name, WINS_AddrToString(addr, false), NET_NAMELEN);
return 0;
}
int WINIPv6_GetAddrFromName (const char *name, struct qsockaddr *addr)
{
//ipv6 addresses take form of [::1]:26000 or eg localhost:26000. just ::1 is NOT supported, but localhost as-is is okay. [localhost]:26000 is acceptable, but will fail to resolve as ipv4.
struct addrinfo *addrinfo = NULL;
struct addrinfo *pos;
struct addrinfo udp6hint;
int error;
char *port;
char dupbase[256];
size_t len;
qboolean success = false;
memset(&udp6hint, 0, sizeof(udp6hint));
udp6hint.ai_family = 0;//Any... we check for AF_INET6 or 4
udp6hint.ai_socktype = SOCK_DGRAM;
udp6hint.ai_protocol = IPPROTO_UDP;
if (*name == '[')
{
port = strstr(name, "]");
if (!port)
error = EAI_NONAME;
else
{
len = port - (name+1);
if (len >= sizeof(dupbase))
len = sizeof(dupbase)-1;
strncpy(dupbase, name+1, len);
dupbase[len] = '\0';
error = qgetaddrinfo?qgetaddrinfo(dupbase, (port[1] == ':')?port+2:NULL, &udp6hint, &addrinfo):EAI_NONAME;
}
}
else
{
port = strrchr(name, ':');
if (port)
{
len = port - name;
if (len >= sizeof(dupbase))
len = sizeof(dupbase)-1;
strncpy(dupbase, name, len);
dupbase[len] = '\0';
error = qgetaddrinfo?qgetaddrinfo(dupbase, port+1, &udp6hint, &addrinfo):EAI_NONAME;
}
else
error = EAI_NONAME;
if (error) //failed, try string with no port.
error = qgetaddrinfo?qgetaddrinfo(name, NULL, &udp6hint, &addrinfo):EAI_NONAME; //remember, this func will return any address family that could be using the udp protocol... (ip4 or ip6)
}
if (!error)
{
((struct sockaddr*)addr)->sa_family = 0;
for (pos = addrinfo; pos; pos = pos->ai_next)
{
if (0)//pos->ai_family == AF_INET)
{
memcpy(addr, pos->ai_addr, pos->ai_addrlen);
success = true;
break;
}
if (pos->ai_family == AF_INET6 && !success)
{
memcpy(addr, pos->ai_addr, pos->ai_addrlen);
success = true;
}
}
freeaddrinfo (addrinfo);
}
if (success)
{
if (((struct sockaddr*)addr)->sa_family == AF_INET)
{
if (!((struct sockaddr_in *)addr)->sin_port)
((struct sockaddr_in *)addr)->sin_port = htons(net_hostport);
}
else if (((struct sockaddr*)addr)->sa_family == AF_INET6)
{
if (!((struct sockaddr_in6 *)addr)->sin6_port)
((struct sockaddr_in6 *)addr)->sin6_port = htons(net_hostport);
}
return 0;
}
return -1;
}
#endif

View File

@ -22,24 +22,38 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#ifndef __NET_WINSOCK_H
#define __NET_WINSOCK_H
sys_socket_t WINS_Init (void);
void WINS_Shutdown (void);
void WINS_Listen (qboolean state);
sys_socket_t WINS_OpenSocket (int port);
int WINS_CloseSocket (sys_socket_t socketid);
int WINS_Connect (sys_socket_t socketid, struct qsockaddr *addr);
sys_socket_t WINS_CheckNewConnections (void);
int WINS_Read (sys_socket_t socketid, byte *buf, int len, struct qsockaddr *addr);
int WINS_Write (sys_socket_t socketid, byte *buf, int len, struct qsockaddr *addr);
int WINS_Broadcast (sys_socket_t socketid, byte *buf, int len);
const char *WINS_AddrToString (struct qsockaddr *addr);
int WINS_StringToAddr (const char *string, struct qsockaddr *addr);
const char *WINS_AddrToString (struct qsockaddr *addr, qboolean masked);
int WINS_GetSocketAddr (sys_socket_t socketid, struct qsockaddr *addr);
int WINS_GetNameFromAddr (struct qsockaddr *addr, char *name);
int WINS_GetAddrFromName (const char *name, struct qsockaddr *addr);
int WINS_AddrCompare (struct qsockaddr *addr1, struct qsockaddr *addr2);
int WINS_GetSocketPort (struct qsockaddr *addr);
int WINS_SetSocketPort (struct qsockaddr *addr, int port);
sys_socket_t WINIPv4_Init (void);
void WINIPv4_Shutdown (void);
sys_socket_t WINIPv4_Listen (qboolean state);
sys_socket_t WINIPv4_OpenSocket (int port);
sys_socket_t WINIPv4_CheckNewConnections (void);
int WINIPv4_Broadcast (sys_socket_t socketid, byte *buf, int len);
int WINIPv4_StringToAddr (const char *string, struct qsockaddr *addr);
int WINIPv4_GetNameFromAddr (struct qsockaddr *addr, char *name);
int WINIPv4_GetAddrFromName (const char *name, struct qsockaddr *addr);
#ifdef IPPROTO_IPV6
sys_socket_t WINIPv6_Init (void);
void WINIPv6_Shutdown (void);
sys_socket_t WINIPv6_Listen (qboolean state);
sys_socket_t WINIPv6_OpenSocket (int port);
sys_socket_t WINIPv6_CheckNewConnections (void);
int WINIPv6_Broadcast (sys_socket_t socketid, byte *buf, int len);
int WINIPv6_StringToAddr (const char *string, struct qsockaddr *addr);
int WINIPv6_GetNameFromAddr (struct qsockaddr *addr, char *name);
int WINIPv6_GetAddrFromName (const char *name, struct qsockaddr *addr);
#endif
#endif /* __NET_WINSOCK_H */

View File

@ -97,7 +97,7 @@ sys_socket_t WIPX_Init (void)
broadcastaddr.sa_socket = htons((unsigned short)net_hostport);
WIPX_GetSocketAddr (net_controlsocket, &addr);
Q_strcpy(my_ipx_address, WIPX_AddrToString (&addr));
Q_strcpy(my_ipx_address, WIPX_AddrToString (&addr, false));
colon = Q_strrchr (my_ipx_address, ':');
if (colon)
*colon = 0;
@ -120,24 +120,28 @@ void WIPX_Shutdown (void)
//=============================================================================
void WIPX_Listen (qboolean state)
sys_socket_t WIPX_Listen (qboolean state)
{
// enable listening
if (state)
{
if (net_acceptsocket != INVALID_SOCKET)
return;
// enable listening
if (net_acceptsocket == INVALID_SOCKET)
{
if ((net_acceptsocket = WIPX_OpenSocket (net_hostport)) == INVALID_SOCKET)
Sys_Error ("WIPX_Listen: Unable to open accept socket");
return;
}
}
else
{
// disable listening
if (net_acceptsocket == INVALID_SOCKET)
return;
if (net_acceptsocket != INVALID_SOCKET)
{
WIPX_CloseSocket (net_acceptsocket);
net_acceptsocket = INVALID_SOCKET;
}
}
return net_acceptsocket;
}
//=============================================================================
@ -300,10 +304,22 @@ int WIPX_Write (sys_socket_t handle, byte *buf, int len, struct qsockaddr *addr)
//=============================================================================
const char *WIPX_AddrToString (struct qsockaddr *addr)
const char *WIPX_AddrToString (struct qsockaddr *addr, qboolean masked)
{
static char buf[28];
if (masked)
{
sprintf(buf, "%02x%02x%02x%02x:??""??""??""??""??""??:%u",
((struct sockaddr_ipx *)addr)->sa_netnum[0] & 0xff,
((struct sockaddr_ipx *)addr)->sa_netnum[1] & 0xff,
((struct sockaddr_ipx *)addr)->sa_netnum[2] & 0xff,
((struct sockaddr_ipx *)addr)->sa_netnum[3] & 0xff,
ntohs(((struct sockaddr_ipx *)addr)->sa_socket)
);
}
else
{
sprintf(buf, "%02x%02x%02x%02x:%02x%02x%02x%02x%02x%02x:%u",
((struct sockaddr_ipx *)addr)->sa_netnum[0] & 0xff,
((struct sockaddr_ipx *)addr)->sa_netnum[1] & 0xff,
@ -317,6 +333,7 @@ const char *WIPX_AddrToString (struct qsockaddr *addr)
((struct sockaddr_ipx *)addr)->sa_nodenum[5] & 0xff,
ntohs(((struct sockaddr_ipx *)addr)->sa_socket)
);
}
return buf;
}
@ -379,7 +396,7 @@ int WIPX_GetSocketAddr (sys_socket_t handle, struct qsockaddr *addr)
int WIPX_GetNameFromAddr (struct qsockaddr *addr, char *name)
{
Q_strcpy(name, WIPX_AddrToString(addr));
Q_strcpy(name, WIPX_AddrToString(addr, false));
return 0;
}

View File

@ -24,7 +24,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
sys_socket_t WIPX_Init (void);
void WIPX_Shutdown (void);
void WIPX_Listen (qboolean state);
sys_socket_t WIPX_Listen (qboolean state);
sys_socket_t WIPX_OpenSocket (int port);
int WIPX_CloseSocket (sys_socket_t socketid);
int WIPX_Connect (sys_socket_t socketid, struct qsockaddr *addr);
@ -32,7 +32,7 @@ sys_socket_t WIPX_CheckNewConnections (void);
int WIPX_Read (sys_socket_t socketid, byte *buf, int len, struct qsockaddr *addr);
int WIPX_Write (sys_socket_t socketid, byte *buf, int len, struct qsockaddr *addr);
int WIPX_Broadcast (sys_socket_t socketid, byte *buf, int len);
const char *WIPX_AddrToString (struct qsockaddr *addr);
const char *WIPX_AddrToString (struct qsockaddr *addr, qboolean masked);
int WIPX_StringToAddr (const char *string, struct qsockaddr *addr);
int WIPX_GetSocketAddr (sys_socket_t socketid, struct qsockaddr *addr);
int WIPX_GetNameFromAddr (struct qsockaddr *addr, char *name);

View File

@ -27,7 +27,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
static char pr_string_temp[STRINGTEMP_BUFFERS][STRINGTEMP_LENGTH];
static byte pr_string_tempindex = 0;
static char *PR_GetTempString (void)
char *PR_GetTempString (void)
{
return pr_string_temp[(STRINGTEMP_BUFFERS-1) & ++pr_string_tempindex];
}
@ -38,6 +38,8 @@ static char *PR_GetTempString (void)
#define MSG_ONE 1 // reliable to one (msg_entity)
#define MSG_ALL 2 // reliable to all
#define MSG_INIT 3 // write to the init string
#define MSG_EXT_MULTICAST 4 // temporary buffer that can be splurged more reliably / with more control.
#define MSG_EXT_ENTITY 5 // for csqc networking. we don't actually support this. I'm just defining it for completeness.
/*
===============================================================================
@ -47,7 +49,7 @@ static char *PR_GetTempString (void)
===============================================================================
*/
static char *PF_VarString (int first)
char *PF_VarString (int first)
{
int i;
static char out[1024];
@ -287,6 +289,19 @@ static void PF_setmodel (void)
if (!*check)
{
if (pr_checkextension.value)
{ //Spike: so that func_illusionaries work with custom models even in vanilla.
if (sv.state == ss_loading)
Con_DWarning("PF_setmodel(\"%s\"): Model was not precached\n", m);
else
{
// PR_PrintStatement(pr_statements + pr_xstatement);
// PR_StackTrace();
Con_Warning("PF_setmodel(\"%s\"): Model was not precached\n", m);
}
i = SV_Precache_Model(m);
}
else
PR_RunError ("no precache: %s", m);
}
e->v.model = PR_SetEngineString(*check);
@ -511,13 +526,15 @@ PF_Random
Returns a number from 0 <= num < 1
random()
bug: vanilla could return 1, contrary to the (unchanged) comment just above.
=================
*/
static void PF_random (void)
{
float num;
num = (rand() & 0x7fff) / ((float)0x7fff);
num = (rand() & 0x7fff) / ((float)0x8000);
G_FLOAT(OFS_RETURN) = num;
}
@ -554,8 +571,8 @@ static void PF_ambientsound (void)
const char *samp, **check;
float *pos;
float vol, attenuation;
int i, soundnum;
int large = false; //johnfitz -- PROTOCOL_FITZQUAKE
int soundnum;
struct ambientsound_s *st;
pos = G_VECTOR (OFS_PARM0);
samp = G_STRING(OFS_PARM1);
@ -575,38 +592,22 @@ static void PF_ambientsound (void)
return;
}
//johnfitz -- PROTOCOL_FITZQUAKE
if (soundnum > 255)
//generate data to splurge on a per-client basis in SV_SendAmbientSounds
if (sv.num_ambients == sv.max_ambients)
{
if (sv.protocol == PROTOCOL_NETQUAKE)
return; //don't send any info protocol can't support
else
large = true;
int nm = sv.max_ambients + 128;
struct ambientsound_s *n = (nm*sizeof(*n)<sv.max_ambients*sizeof(*n))?NULL:realloc(sv.ambientsounds, nm*sizeof(*n));
if (!n)
PR_RunError ("PF_ambientsound: out of memory"); //shouldn't really happen.
sv.ambientsounds = n;
memset(sv.ambientsounds+sv.max_ambients, 0, (nm-sv.max_ambients)*sizeof(*n));
sv.max_ambients = nm;
}
//johnfitz
// add an svc_spawnambient command to the level signon packet
//johnfitz -- PROTOCOL_FITZQUAKE
if (large)
MSG_WriteByte (&sv.signon,svc_spawnstaticsound2);
else
MSG_WriteByte (&sv.signon,svc_spawnstaticsound);
//johnfitz
for (i = 0; i < 3; i++)
MSG_WriteCoord(&sv.signon, pos[i], sv.protocolflags);
//johnfitz -- PROTOCOL_FITZQUAKE
if (large)
MSG_WriteShort(&sv.signon, soundnum);
else
MSG_WriteByte (&sv.signon, soundnum);
//johnfitz
MSG_WriteByte (&sv.signon, vol*255);
MSG_WriteByte (&sv.signon, attenuation*64);
st = &sv.ambientsounds[sv.num_ambients++];
VectorCopy(pos, st->origin);
st->soundindex = soundnum;
st->volume = vol;
st->attenuation = attenuation;
}
/*
@ -638,6 +639,7 @@ static void PF_sound (void)
volume = G_FLOAT(OFS_PARM3) * 255;
attenuation = G_FLOAT(OFS_PARM4);
/* Spike -- these checks are redundant
if (volume < 0 || volume > 255)
Host_Error ("SV_StartSound: volume = %i", volume);
@ -646,8 +648,8 @@ static void PF_sound (void)
if (channel < 0 || channel > 7)
Host_Error ("SV_StartSound: channel = %i", channel);
SV_StartSound (entity, channel, sample, volume, attenuation);
*/
SV_StartSound (entity, NULL, channel, sample, volume, attenuation);
}
/*
@ -1075,39 +1077,77 @@ static void PF_precache_file (void)
G_INT(OFS_RETURN) = G_INT(OFS_PARM0);
}
static void PF_precache_sound (void)
{
const char *s;
int SV_Precache_Sound(const char *s)
{ //must be a persistent string.
int i;
if (sv.state != ss_loading)
PR_RunError ("PF_Precache_*: Precache can only be done in spawn functions");
s = G_STRING(OFS_PARM0);
G_INT(OFS_RETURN) = G_INT(OFS_PARM0);
PR_CheckEmptyString (s);
for (i = 0; i < MAX_SOUNDS; i++)
{
if (!sv.sound_precache[i])
{
if (sv.state != ss_loading) //spike -- moved this so that there's no actual error any more.
{
Con_Warning("PF_precache_sound(\"%s\"): Precache should only be done in spawn functions\n", s);
//let existing clients know about it
MSG_WriteByte(&sv.reliable_datagram, svcdp_precache);
MSG_WriteShort(&sv.reliable_datagram, i|0x8000);
MSG_WriteString(&sv.reliable_datagram, s);
}
sv.sound_precache[i] = s;
return;
return i;
}
if (!strcmp(sv.sound_precache[i], s))
return;
{
if (sv.state != ss_loading && !pr_checkextension.value)
Con_Warning("PF_precache_sound(\"%s\"): Precache should only be done in spawn functions\n", s);
return i;
}
}
return 0;
}
static void PF_precache_sound (void)
{
const char *s;
s = G_STRING(OFS_PARM0);
G_INT(OFS_RETURN) = G_INT(OFS_PARM0);
PR_CheckEmptyString (s);
if (!SV_Precache_Sound(s))
PR_RunError ("PF_precache_sound: overflow");
}
int SV_Precache_Model(const char *s)
{
size_t i;
for (i = 0; i < MAX_MODELS; i++)
{
if (!sv.model_precache[i])
{
if (sv.state != ss_loading)
{
//let existing clients know about it
MSG_WriteByte(&sv.reliable_datagram, svcdp_precache);
MSG_WriteShort(&sv.reliable_datagram, i|0x8000);
MSG_WriteString(&sv.reliable_datagram, s);
}
sv.model_precache[i] = s;
sv.models[i] = Mod_ForName (s, i==1);
return i;
}
if (!strcmp(sv.model_precache[i], s))
return i;
}
return 0;
}
static void PF_precache_model (void)
{
const char *s;
int i;
if (sv.state != ss_loading)
PR_RunError ("PF_Precache_*: Precache can only be done in spawn functions");
s = G_STRING(OFS_PARM0);
G_INT(OFS_RETURN) = G_INT(OFS_PARM0);
PR_CheckEmptyString (s);
@ -1116,13 +1156,26 @@ static void PF_precache_model (void)
{
if (!sv.model_precache[i])
{
if (sv.state != ss_loading)
{
Con_Warning ("PF_precache_model(\"%s\"): Precache should only be done in spawn functions\n", s);
//let existing clients know about it
MSG_WriteByte(&sv.reliable_datagram, svcdp_precache);
MSG_WriteShort(&sv.reliable_datagram, i|0x8000);
MSG_WriteString(&sv.reliable_datagram, s);
}
sv.model_precache[i] = s;
sv.models[i] = Mod_ForName (s, true);
sv.models[i] = Mod_ForName (s, i==1);
return;
}
if (!strcmp(sv.model_precache[i], s))
{
if (sv.state != ss_loading && !pr_checkextension.value)
Con_Warning ("PF_precache_model(\"%s\"): Precache should only be done in spawn functions\n", s);
return;
}
}
PR_RunError ("PF_precache_model: overflow");
}
@ -1475,7 +1528,7 @@ MESSAGE WRITING
===============================================================================
*/
static sizebuf_t *WriteDest (void)
sizebuf_t *WriteDest (void)
{
int entnum;
int dest;
@ -1500,6 +1553,9 @@ static sizebuf_t *WriteDest (void)
case MSG_INIT:
return &sv.signon;
case MSG_EXT_MULTICAST:
return &sv.multicast;
default:
PR_RunError ("WriteDest: bad destination");
break;
@ -1543,78 +1599,37 @@ static void PF_WriteString (void)
MSG_WriteString (WriteDest(), G_STRING(OFS_PARM1));
}
#define MSG_WriteEntity MSG_WriteShort //fixme - replacement deltas encodes 0x8000+ in 24 bits
static void PF_WriteEntity (void)
{
MSG_WriteShort (WriteDest(), G_EDICTNUM(OFS_PARM1));
MSG_WriteEntity (WriteDest(), G_EDICTNUM(OFS_PARM1));
}
//=============================================================================
static void PF_makestatic (void)
{
entity_state_t *st;
edict_t *ent;
int i;
int bits = 0; //johnfitz -- PROTOCOL_FITZQUAKE
ent = G_EDICT(OFS_PARM0);
//johnfitz -- don't send invisible static entities
if (ent->alpha == ENTALPHA_ZERO) {
ED_Free (ent);
return;
}
//johnfitz
//johnfitz -- PROTOCOL_FITZQUAKE
if (sv.protocol == PROTOCOL_NETQUAKE)
if (sv.num_statics == sv.max_statics)
{
if (SV_ModelIndex(PR_GetString(ent->v.model)) & 0xFF00 || (int)(ent->v.frame) & 0xFF00)
{
ED_Free (ent);
return; //can't display the correct model & frame, so don't show it at all
}
int nm = sv.max_statics + 128;
entity_state_t *n = (nm*sizeof(*n)<sv.max_statics*sizeof(*n))?NULL:realloc(sv.static_entities, nm*sizeof(*n));
if (!n)
PR_RunError ("PF_makestatic: out of memory"); //shouldn't really happen.
sv.static_entities = n;
memset(sv.static_entities+sv.max_statics, 0, (nm-sv.max_statics)*sizeof(*n));
sv.max_statics = nm;
}
st = &sv.static_entities[sv.num_statics];
SV_BuildEntityState(ent, st);
if (st->alpha == ENTALPHA_ZERO)
; //no point
else
{
if (SV_ModelIndex(PR_GetString(ent->v.model)) & 0xFF00)
bits |= B_LARGEMODEL;
if ((int)(ent->v.frame) & 0xFF00)
bits |= B_LARGEFRAME;
if (ent->alpha != ENTALPHA_DEFAULT)
bits |= B_ALPHA;
}
if (bits)
{
MSG_WriteByte (&sv.signon, svc_spawnstatic2);
MSG_WriteByte (&sv.signon, bits);
}
else
MSG_WriteByte (&sv.signon, svc_spawnstatic);
if (bits & B_LARGEMODEL)
MSG_WriteShort (&sv.signon, SV_ModelIndex(PR_GetString(ent->v.model)));
else
MSG_WriteByte (&sv.signon, SV_ModelIndex(PR_GetString(ent->v.model)));
if (bits & B_LARGEFRAME)
MSG_WriteShort (&sv.signon, ent->v.frame);
else
MSG_WriteByte (&sv.signon, ent->v.frame);
//johnfitz
MSG_WriteByte (&sv.signon, ent->v.colormap);
MSG_WriteByte (&sv.signon, ent->v.skin);
for (i = 0; i < 3; i++)
{
MSG_WriteCoord(&sv.signon, ent->v.origin[i], sv.protocolflags);
MSG_WriteAngle(&sv.signon, ent->v.angles[i], sv.protocolflags);
}
//johnfitz -- PROTOCOL_FITZQUAKE
if (bits & B_ALPHA)
MSG_WriteByte (&sv.signon, ent->alpha);
//johnfitz
sv.num_statics++;
// throw the entity away now
ED_Free (ent);
@ -1663,11 +1678,30 @@ static void PF_changelevel (void)
Cbuf_AddText (va("changelevel %s\n",s));
}
static void PF_Fixme (void)
{
PR_RunError ("unimplemented builtin");
}
void PF_Fixme (void);
//{
// PR_RunError ("unimplemented builtin");
//}
void PR_spawnfunc_misc_model(edict_t *self)
{
eval_t *val;
if (!self->v.model && (val = GetEdictFieldValue(self, ED_FindFieldOffset("mdl"))))
self->v.model = val->string;
if (!*PR_GetString(self->v.model)) //must have a model, because otherwise various things will assume its not valid at all.
self->v.model = PR_SetEngineString("*null");
if (self->v.angles[1] < 0) //mimic AD. shame there's no avelocity clientside.
self->v.angles[1] = (rand()*(360.0f/RAND_MAX));
//make sure the model is precached, to avoid errors.
G_INT(OFS_PARM0) = self->v.model;
PF_precache_model();
//and lets just call makestatic instead of worrying if it'll interfere with the rest of the qc.
G_INT(OFS_PARM0) = EDICT_TO_PROG(self);
PF_makestatic();
}
static builtin_t pr_builtin[] =
{

View File

@ -37,7 +37,9 @@ typedef enum
ev_entity,
ev_field,
ev_function,
ev_pointer
ev_pointer,
ev_ext_integer
} etype_t;
#define OFS_NULL 0

View File

@ -23,6 +23,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "quakedef.h"
struct pr_extfields_s pr_extfields;
dprograms_t *progs;
dfunction_t *pr_functions;
@ -31,11 +33,10 @@ static int pr_stringssize;
static const char **pr_knownstrings;
static int pr_maxknownstrings;
static int pr_numknownstrings;
static ddef_t *pr_fielddefs;
static int pr_freeknownstrings;
ddef_t *pr_fielddefs;
static ddef_t *pr_globaldefs;
qboolean pr_alpha_supported; //johnfitz
dstatement_t *pr_statements;
globalvars_t *pr_global_struct;
float *pr_globals; // same as pr_global_struct
@ -55,21 +56,7 @@ int type_size[8] = {
};
static ddef_t *ED_FieldAtOfs (int ofs);
static qboolean ED_ParseEpair (void *base, ddef_t *key, const char *s);
#define MAX_FIELD_LEN 64
#define GEFV_CACHESIZE 2
typedef struct {
ddef_t *pcache;
char field[MAX_FIELD_LEN];
} gefv_cache;
static gefv_cache gefvCache[GEFV_CACHESIZE] =
{
{ NULL, "" },
{ NULL, "" }
};
qboolean ED_ParseEpair (void *base, ddef_t *key, const char *s);
cvar_t nomonsters = {"nomonsters", "0", CVAR_NONE};
cvar_t gamecfg = {"gamecfg", "0", CVAR_NONE};
@ -207,7 +194,7 @@ static ddef_t *ED_FieldAtOfs (int ofs)
ED_FindField
============
*/
static ddef_t *ED_FindField (const char *name)
ddef_t *ED_FindField (const char *name)
{
ddef_t *def;
int i;
@ -221,13 +208,22 @@ static ddef_t *ED_FindField (const char *name)
return NULL;
}
/*
*/
int ED_FindFieldOffset (const char *name)
{
ddef_t *def = ED_FindField(name);
if (!def)
return -1;
return def->ofs;
}
/*
============
ED_FindGlobal
============
*/
static ddef_t *ED_FindGlobal (const char *name)
ddef_t *ED_FindGlobal (const char *name)
{
ddef_t *def;
int i;
@ -247,7 +243,7 @@ static ddef_t *ED_FindGlobal (const char *name)
ED_FindFunction
============
*/
static dfunction_t *ED_FindFunction (const char *fn_name)
dfunction_t *ED_FindFunction (const char *fn_name)
{
dfunction_t *func;
int i;
@ -266,35 +262,12 @@ static dfunction_t *ED_FindFunction (const char *fn_name)
GetEdictFieldValue
============
*/
eval_t *GetEdictFieldValue(edict_t *ed, const char *field)
eval_t *GetEdictFieldValue(edict_t *ed, int fldofs)
{
ddef_t *def = NULL;
int i;
static int rep = 0;
for (i = 0; i < GEFV_CACHESIZE; i++)
{
if (!strcmp(field, gefvCache[i].field))
{
def = gefvCache[i].pcache;
goto Done;
}
}
def = ED_FindField (field);
if (strlen(field) < MAX_FIELD_LEN)
{
gefvCache[rep].pcache = def;
strcpy (gefvCache[rep].field, field);
rep ^= 1;
}
Done:
if (!def)
if (fldofs < 0)
return NULL;
return (eval_t *)((char *)&ed->v + def->ofs*4);
return (eval_t *)((char *)&ed->v + fldofs*4);
}
@ -336,6 +309,9 @@ static const char *PR_ValueString (int type, eval_t *val)
case ev_float:
sprintf (line, "%5.1f", val->_float);
break;
case ev_ext_integer:
sprintf (line, "%i", val->_int);
break;
case ev_vector:
sprintf (line, "'%5.1f %5.1f %5.1f'", val->vector[0], val->vector[1], val->vector[2]);
break;
@ -359,7 +335,7 @@ Returns a string describing *data in a type specific manner
Easier to parse than PR_ValueString
=============
*/
static const char *PR_UglyValueString (int type, eval_t *val)
const char *PR_UglyValueString (int type, eval_t *val)
{
static char line[1024];
ddef_t *def;
@ -389,6 +365,9 @@ static const char *PR_UglyValueString (int type, eval_t *val)
case ev_float:
q_snprintf (line, sizeof(line), "%f", val->_float);
break;
case ev_ext_integer:
sprintf (line, "%i", val->_int);
break;
case ev_vector:
q_snprintf (line, sizeof(line), "%f %f %f", val->vector[0], val->vector[1], val->vector[2]);
break;
@ -554,7 +533,7 @@ void ED_Write (FILE *f, edict_t *ed)
}
//johnfitz -- save entity alpha manually when progs.dat doesn't know about alpha
if (!pr_alpha_supported && ed->alpha != ENTALPHA_DEFAULT)
if (pr_extfields.alpha<0 && ed->alpha != ENTALPHA_DEFAULT)
fprintf (f, "\"alpha\" \"%f\"\n", ENTALPHA_TOSAVE(ed->alpha));
//johnfitz
@ -771,7 +750,7 @@ Can parse either fields or globals
returns false if error
=============
*/
static qboolean ED_ParseEpair (void *base, ddef_t *key, const char *s)
qboolean ED_ParseEpair (void *base, ddef_t *key, const char *s)
{
int i;
char string[128];
@ -793,6 +772,10 @@ static qboolean ED_ParseEpair (void *base, ddef_t *key, const char *s)
*(float *)d = atof (s);
break;
case ev_ext_integer:
*(int *)d = atoi (s);
break;
case ev_vector:
q_strlcpy (string, s, sizeof(string));
end = (char *)string + strlen(string);
@ -919,17 +902,53 @@ const char *ED_ParseEdict (const char *data, edict_t *ent)
// keynames with a leading underscore are used for utility comments,
// and are immediately discarded by quake
if (keyname[0] == '_')
{
//spike -- hacks to support func_illusionary with all sorts of mdls, and various particle effects
if (!strcmp(keyname, "_precache_model") && sv.state == ss_loading)
SV_Precache_Model(PR_GetString(ED_NewString(com_token)));
else if (!strcmp(keyname, "_precache_sound") && sv.state == ss_loading)
SV_Precache_Sound(PR_GetString(ED_NewString(com_token)));
//spike
continue;
}
//johnfitz -- hack to support .alpha even when progs.dat doesn't know about it
if (!strcmp(keyname, "alpha"))
ent->alpha = ENTALPHA_ENCODE(atof(com_token));
//johnfitz
//spike -- hacks to support func_illusionary/info_notnull with all sorts of mdls, and various particle effects
if (!strcmp(keyname, "modelindex") && sv.state == ss_loading)
{
//"model" "progs/foobar.mdl"
//"modelindex" "progs/foobar.mdl"
//"mins" "-16 -16 -16"
//"maxs" "16 16 16"
char *e;
strtol(com_token, &e, 0);
if (e != com_token && *e)
ent->v.modelindex = SV_Precache_Model(PR_GetString(ED_NewString(com_token)));
}
//spike
key = ED_FindField (keyname);
if (!key)
{
#ifdef PSET_SCRIPT
eval_t *val;
if (!strcmp(keyname, "traileffect") && sv.state == ss_loading)
{
if ((val = GetEdictFieldValue(ent, pr_extfields.traileffectnum)))
val->_float = PF_SV_ForceParticlePrecache(com_token);
}
else if (!strcmp(keyname, "emiteffect") && sv.state == ss_loading)
{
if ((val = GetEdictFieldValue(ent, pr_extfields.emiteffectnum)))
val->_float = PF_SV_ForceParticlePrecache(com_token);
}
//johnfitz -- HACK -- suppress error becuase fog/sky/alpha fields might not be mentioned in defs.qc
else
#endif
if (strncmp(keyname, "sky", 3) && strcmp(keyname, "fog") && strcmp(keyname, "alpha"))
Con_DPrintf ("\"%s\" is not a field\n", keyname); //johnfitz -- was Con_Printf
continue;
@ -973,6 +992,7 @@ void ED_LoadFromFile (const char *data)
dfunction_t *func;
edict_t *ent = NULL;
int inhibit = 0;
int usingspawnfunc = 0;
pr_global_struct->time = sv.time;
@ -1023,13 +1043,27 @@ void ED_LoadFromFile (const char *data)
}
// look for the spawn function
//
func = ED_FindFunction (va("spawnfunc_%s", PR_GetString(ent->v.classname)));
if (func)
{
if (!usingspawnfunc++)
Con_DPrintf2 ("Using DP_SV_SPAWNFUNC_PREFIX\n");
}
else
func = ED_FindFunction ( PR_GetString(ent->v.classname) );
if (!func)
{
const char *classname = PR_GetString(ent->v.classname);
if (!strcmp(classname, "misc_model"))
PR_spawnfunc_misc_model(ent);
else
{
Con_SafePrintf ("No spawn function for:\n"); //johnfitz -- was Con_Printf
ED_Print (ent);
ED_Free (ent);
}
continue;
}
@ -1049,10 +1083,9 @@ PR_LoadProgs
void PR_LoadProgs (void)
{
int i;
unsigned int u;
// flush the non-C variable lookup cache
for (i = 0; i < GEFV_CACHESIZE; i++)
gefvCache[i].field[0] = 0;
PR_ShutdownExtensions();
CRC_Init (&pr_crc);
@ -1080,6 +1113,7 @@ void PR_LoadProgs (void)
// initialize the strings
pr_numknownstrings = 0;
pr_freeknownstrings = 0;
pr_maxknownstrings = 0;
pr_stringssize = progs->numstrings;
if (pr_knownstrings)
@ -1120,7 +1154,8 @@ void PR_LoadProgs (void)
pr_globaldefs[i].s_name = LittleLong (pr_globaldefs[i].s_name);
}
pr_alpha_supported = false; //johnfitz
for (u = 0; u < sizeof(pr_extfields)/sizeof(int); u++)
((int*)&pr_extfields)[u] = -1;
for (i = 0; i < progs->numfielddefs; i++)
{
@ -1129,22 +1164,46 @@ void PR_LoadProgs (void)
Host_Error ("PR_LoadProgs: pr_fielddefs[i].type & DEF_SAVEGLOBAL");
pr_fielddefs[i].ofs = LittleShort (pr_fielddefs[i].ofs);
pr_fielddefs[i].s_name = LittleLong (pr_fielddefs[i].s_name);
//johnfitz -- detect alpha support in progs.dat
if (!strcmp(pr_strings + pr_fielddefs[i].s_name,"alpha"))
pr_alpha_supported = true;
//johnfitz
}
for (i = 0; i < progs->numglobals; i++)
((int *)pr_globals)[i] = LittleLong (((int *)pr_globals)[i]);
pr_edict_size = progs->entityfields * 4 + sizeof(edict_t) - sizeof(entvars_t);
//spike: detect extended fields from progs
pr_extfields.items2 = ED_FindFieldOffset("items2");
pr_extfields.gravity = ED_FindFieldOffset("gravity");
pr_extfields.alpha = ED_FindFieldOffset("alpha");
pr_extfields.movement = ED_FindFieldOffset("movement");
pr_extfields.traileffectnum = ED_FindFieldOffset("traileffectnum");
pr_extfields.emiteffectnum = ED_FindFieldOffset("emiteffectnum");
pr_extfields.viewmodelforclient = ED_FindFieldOffset("viewmodelforclient");
pr_extfields.scale = ED_FindFieldOffset("scale");
pr_extfields.colormod = ED_FindFieldOffset("colormod");
pr_extfields.tag_entity = ED_FindFieldOffset("tag_entity");
pr_extfields.tag_index = ED_FindFieldOffset("tag_index");
pr_extfields.button3 = ED_FindFieldOffset("button3");
pr_extfields.button4 = ED_FindFieldOffset("button4");
pr_extfields.button5 = ED_FindFieldOffset("button5");
pr_extfields.button6 = ED_FindFieldOffset("button6");
pr_extfields.button7 = ED_FindFieldOffset("button7");
pr_extfields.button8 = ED_FindFieldOffset("button8");
pr_extfields.viewzoom = ED_FindFieldOffset("viewzoom");
pr_extfields.modelflags = ED_FindFieldOffset("modelflags");
i = progs->entityfields;
if (pr_extfields.emiteffectnum < 0)
pr_extfields.emiteffectnum = i++;
if (pr_extfields.traileffectnum < 0)
pr_extfields.traileffectnum = i++;
pr_edict_size = i * 4 + sizeof(edict_t) - sizeof(entvars_t);
// round off to next highest whole word address (esp for Alpha)
// this ensures that pointers in the engine data area are always
// properly aligned
pr_edict_size += sizeof(void *) - 1;
pr_edict_size &= ~(sizeof(void *) - 1);
PR_EnableExtensions(pr_globaldefs);
}
@ -1159,6 +1218,7 @@ void PR_Init (void)
Cmd_AddCommand ("edicts", ED_PrintEdicts);
Cmd_AddCommand ("edictcount", ED_Count);
Cmd_AddCommand ("profile", PR_Profile_f);
Cmd_AddCommand ("pr_dumpplatform", PR_DumpPlatform_f);
Cvar_RegisterVariable (&nomonsters);
Cvar_RegisterVariable (&gamecfg);
Cvar_RegisterVariable (&scratch1);
@ -1219,11 +1279,23 @@ const char *PR_GetString (int num)
}
else
{
return pr_strings;
Host_Error("PR_GetString: invalid string offset %d\n", num);
return "";
}
}
void PR_ClearEngineString(int num)
{
if (num < 0 && num >= -pr_numknownstrings)
{
num = -1 - num;
pr_knownstrings[num] = NULL;
if (pr_freeknownstrings > num)
pr_freeknownstrings = num;
}
}
int PR_SetEngineString (const char *s)
{
int i;
@ -1244,19 +1316,22 @@ int PR_SetEngineString (const char *s)
}
// new unknown engine string
//Con_DPrintf ("PR_SetEngineString: new engine string %p\n", s);
#if 0
for (i = 0; i < pr_numknownstrings; i++)
for (i = pr_freeknownstrings; ; i++)
{
if (!pr_knownstrings[i])
break;
if (i < pr_numknownstrings)
{
if (pr_knownstrings[i])
continue;
}
#endif
// if (i >= pr_numknownstrings)
// {
else
{
if (i >= pr_maxknownstrings)
PR_AllocStringSlots();
pr_numknownstrings++;
// }
}
break;
}
pr_freeknownstrings = i+1;
pr_knownstrings[i] = s;
return -1 - i;
}

View File

@ -371,6 +371,8 @@ void PR_ExecuteProgram (func_t fnum)
f = &pr_functions[fnum];
//FIXME: if this is a builtin, then we're going to crash.
pr_trace = false;
// make a stack frame
@ -383,7 +385,7 @@ void PR_ExecuteProgram (func_t fnum)
{
st++; /* next statement */
if (++profile > 100000)
if (++profile > 10000000) //spike -- was 100000
{
pr_xstatement = st - pr_statements;
PR_RunError("runaway loop error");
@ -612,7 +614,7 @@ void PR_ExecuteProgram (func_t fnum)
{ // Built-in function
int i = -newf->first_statement;
if (i >= pr_numbuiltins)
PR_RunError("Bad builtin call number %d", i);
i = 0; //just invoke the fixme builtin.
pr_builtins[i]();
break;
}

4651
Quake/pr_ext.c Executable file

File diff suppressed because it is too large Load Diff

View File

@ -42,7 +42,7 @@ typedef struct edict_s
qboolean free;
link_t area; /* linked to a division node or leaf */
int num_leafs;
unsigned int num_leafs;
int leafnums[MAX_ENT_LEAFS];
entity_state_t baseline;
@ -64,6 +64,7 @@ extern dfunction_t *pr_functions;
extern dstatement_t *pr_statements;
extern globalvars_t *pr_global_struct;
extern float *pr_globals; /* same as pr_global_struct */
extern ddef_t *pr_fielddefs; //yay reflection.
extern int pr_edict_size; /* in bytes */
@ -73,9 +74,28 @@ void PR_Init (void);
void PR_ExecuteProgram (func_t fnum);
void PR_LoadProgs (void);
//from pr_ext.c
void PR_EnableExtensions(ddef_t *pr_globaldefs); //adds in the extra builtins etc
void PR_AutoCvarChanged(cvar_t *var); //updates the autocvar_ globals when their cvar is changed
void PR_ShutdownExtensions(void); //nooooes!
void PR_DumpPlatform_f(void); //console command: writes out a qsextensions.qc file
//special hacks...
int PF_SV_ForceParticlePrecache(const char *s);
int SV_Precache_Model(const char *s);
int SV_Precache_Sound(const char *s);
void PR_spawnfunc_misc_model(edict_t *self);
//from pr_edict, for pr_ext. reflection is messy.
qboolean ED_ParseEpair (void *base, ddef_t *key, const char *s);
const char *PR_UglyValueString (int type, eval_t *val);
ddef_t *ED_FindField (const char *name);
ddef_t *ED_FindGlobal (const char *name);
dfunction_t *ED_FindFunction (const char *fn_name);
const char *PR_GetString (int num);
int PR_SetEngineString (const char *s);
int PR_AllocString (int bufferlength, char **ptr);
void PR_ClearEngineString(int num);
void PR_Profile_f (void);
@ -138,7 +158,49 @@ FUNC_NORETURN void PR_RunError (const char *error, ...) FUNC_PRINTF(1,2);
void ED_PrintEdicts (void);
void ED_PrintNum (int ent);
eval_t *GetEdictFieldValue(edict_t *ed, const char *field);
eval_t *GetEdictFieldValue(edict_t *ed, int fldofs); //handles invalid offsets with a null
int ED_FindFieldOffset (const char *name);
//from pr_cmds, no longer static so that pr_ext can use them.
sizebuf_t *WriteDest (void);
char *PR_GetTempString (void);
char *PF_VarString (int first);
#define STRINGTEMP_BUFFERS 16
#define STRINGTEMP_LENGTH 1024
void PF_Fixme(void); //the 'unimplemented' builtin. woot.
extern struct pr_extfuncs_s
{ //various global qc entry points that might be called by the engine, if set.
func_t endframe;
func_t parseclientcommand;
} pr_extfuncs;
extern cvar_t pr_checkextension; //if 0, extensions are disabled (unless they'd be fatal, but they're still spammy)
extern struct pr_extfields_s
{ //various fields that might be wanted by the engine. -1 == invalid
//I should probably use preprocessor magic for this list or something
int items2; //float
int gravity; //float
int alpha; //float
int movement; //vector
int viewmodelforclient; //entity
int traileffectnum; //float
int emiteffectnum; //float
int scale; //float
int colormod; //vector
int tag_entity; //entity
int tag_index; //float
int button3; //float
int button4; //float
int button5; //float
int button6; //float
int button7; //float
int button8; //float
int viewzoom; //float
int modelflags; //float, the upper 8 bits of .effects
//REMEMBER TO ADD THESE TO qsextensions.qc AND pr_edict.c
} pr_extfields;
#endif /* _QUAKE_PROGS_H */

View File

@ -26,8 +26,18 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
// protocol.h -- communications protocols
#define PROTOCOL_NETQUAKE 15 //johnfitz -- standard quake protocol
//#define PROTOCOL_VERSION_H2 19
//#define PROTOCOL_VERSION_NEHD 250
#define PROTOCOL_FITZQUAKE 666 //johnfitz -- added new protocol for fitzquake 0.85
#define PROTOCOL_RMQ 999
//#define PROTOCOL_VERSION_DP5 3502
//#define PROTOCOL_VERSION_DP6 3503
#define PROTOCOL_VERSION_DP7 3504
//#define PROTOCOL_VERSION_BJP1 10000
//#define PROTOCOL_VERSION_BJP2 10001
#define PROTOCOL_VERSION_BJP3 10002 //spike, note that this protocol is intentionally flawed to work around mods+writebytes - svc_staticsound is limited to 8bit indexes.
//#define PROTOCOL_FTE_PEXT1 (('F'<<0) + ('T'<<8) + ('E'<<16) + ('X' << 24)) //fte extensions, provides extensions to the underlying base protocol (like 666 or even 15).
#define PROTOCOL_FTE_PEXT2 (('F'<<0) + ('T'<<8) + ('E'<<16) + ('2' << 24)) //fte extensions, provides extensions to the underlying base protocol (like 666 or even 15).
// PROTOCOL_RMQ protocol flags
#define PRFL_SHORTANGLE (1 << 1)
@ -39,6 +49,21 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#define PRFL_INT32COORD (1 << 7)
#define PRFL_MOREFLAGS (1 << 31) // not supported
// PROTOCOL_FTE_PEXT(1) flags
//mostly uninteresting, mostly superseeded by PEXT2_REPLACEMENTDELTAS...
// PROTOCOL_FTE_PEXT2 flags
#define PEXT2_PRYDONCURSOR 0x00000001 //a mouse cursor exposed to ssqc
#define PEXT2_VOICECHAT 0x00000002 //+voip or cl_voip_send 1; requires opus dll, and others to also have that same dll.
#define PEXT2_SETANGLEDELTA 0x00000004 //less annoying when teleporting.
#define PEXT2_REPLACEMENTDELTAS 0x00000008 //more compact entity deltas (can also be split across multiple packets)
#define PEXT2_MAXPLAYERS 0x00000010 //up to 255 player slots
#define PEXT2_PREDINFO 0x00000020 //provides input acks and reworks stats such that clc_clientdata becomes redundant.
#define PEXT2_NEWSIZEENCODING 0x00000040 //richer size encoding, for more precise bboxes.
#define PEXT2_ACCEPTED_CLIENT (PEXT2_SUPPORTED_CLIENT|PEXT2_NEWSIZEENCODING|PEXT2_PRYDONCURSOR) //pext2 flags that we can parse, but don't want to advertise
#define PEXT2_SUPPORTED_CLIENT (PEXT2_SETANGLEDELTA|PEXT2_VOICECHAT|PEXT2_REPLACEMENTDELTAS|PEXT2_MAXPLAYERS|PEXT2_PREDINFO) //pext2 flags that we understand+support
#define PEXT2_SUPPORTED_SERVER ( PEXT2_VOICECHAT|PEXT2_REPLACEMENTDELTAS |PEXT2_PREDINFO)
// if the high bit of the servercmd is set, the low bits are fast update flags:
#define U_MOREBITS (1<<0)
#define U_ORIGIN1 (1<<1)
@ -73,6 +98,59 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#define U_TRANS (1<<15)
//johnfitz
//spike -- FTE Replacement Deltas
//first byte contains the stuff that's most likely to change constantly
#define UF_FRAME (1u<<0)
#define UF_ORIGINXY (1u<<1)
#define UF_ORIGINZ (1u<<2)
#define UF_ANGLESXZ (1u<<3)
#define UF_ANGLESY (1u<<4)
#define UF_EFFECTS (1u<<5)
#define UF_PREDINFO (1u<<6) /*ent is predicted, probably a player*/
#define UF_EXTEND1 (1u<<7)
/*stuff which is common on ent spawning*/
#define UF_RESET (1u<<8)
#define UF_16BIT (1u<<9) /*within this update, frame/skin/model is 16bit, not part of the deltaing itself*/
#define UF_MODEL (1u<<10)
#define UF_SKIN (1u<<11)
#define UF_COLORMAP (1u<<12)
#define UF_SOLID (1u<<13) /*encodes the size of the entity, so prediction can bump against it*/
#define UF_FLAGS (1u<<14) /*some extra flags like viewmodelforclient*/
#define UF_EXTEND2 (1u<<15)
/*the rest is optional extensions*/
#define UF_ALPHA (1u<<16) /*transparency*/
#define UF_SCALE (1u<<17) /*rescaling stuff, 1/16th*/
#define UF_BONEDATA (1u<<18) /*for ssqc control over skeletal models*/
#define UF_DRAWFLAGS (1u<<19) /*scale offsets and things*/
#define UF_TAGINFO (1u<<20) /*simple entity attachments, generally needs either md3s or skeletal models*/
#define UF_LIGHT (1u<<21) /*attaching rtlights to dynamic entities from ssqc*/
#define UF_TRAILEFFECT (1u<<22) /*attaches custom particle trails to entities, woo.*/
#define UF_EXTEND3 (1u<<23)
#define UF_COLORMOD (1u<<24) /*rgb tints. 1/16th*/
#define UF_GLOW (1u<<25) /*tbh only useful as an extra 'renderable' field for csqc...*/
#define UF_FATNESS (1u<<26) /*push the entity's normals out by this distance*/
#define UF_MODELINDEX2 (1u<<27) /*for lame visible weapon models, like q2. just adds a second ent at the same point*/
#define UF_GRAVITYDIR (1u<<28) /*yay prediction*/
#define UF_EFFECTS2 (1u<<29) /*effects is 16bit, or if both effects flags are set then 32bit*/
#define UF_UNUSED2 (1u<<30)
#define UF_UNUSED1 (1u<<31)
/*these flags are generally not deltaed as they're changing constantly*/
#define UFP_FORWARD (1u<<0)
#define UFP_SIDE (1u<<1)
#define UFP_UP (1u<<2)
#define UFP_MOVETYPE (1u<<3) /*deltaed*/
#define UFP_VELOCITYXY (1u<<4)
#define UFP_VELOCITYZ (1u<<5)
#define UFP_MSEC (1u<<6)
#define UFP_WEAPONFRAME_OLD (1u<<7) //no longer used. just a stat now that I rewrote stat deltas.
#define UFP_VIEWANGLE (1u<<7)
//spike
#define SU_VIEWHEIGHT (1<<0)
#define SU_IDEALPITCH (1<<1)
#define SU_PUNCH1 (1<<2)
@ -107,11 +185,17 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#define SU_UNUSED30 (1<<30)
#define SU_EXTEND3 (1<<31) // another byte to follow, future expansion
//johnfitz
//spike dp crap
#define DPSU_PUNCHVEC1 (1<<16)
#define DPSU_PUNCHVEC2 (1<<17)
#define DPSU_PUNCHVEC3 (1<<18)
#define DPSU_VIEWZOOM (1<<19) // byte factor (0 = 0.0 (not valid), 255 = 1.0)
//spike
// a sound with no channel is a local only sound
#define SND_VOLUME (1<<0) // a byte
#define SND_ATTENUATION (1<<1) // a byte
#define SND_LOOPING (1<<2) // a long
//#define SND_LOOPING (1<<2) // a long (unused in vanilla)
#define DEFAULT_SOUND_PACKET_VOLUME 255
#define DEFAULT_SOUND_PACKET_ATTENUATION 1.0
@ -120,6 +204,13 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#define SND_LARGEENTITY (1<<3) // a short + byte (instead of just a short)
#define SND_LARGESOUND (1<<4) // a short soundindex (instead of a byte)
//johnfitz
//spike -- parsing, but not using at this time
#define SND_FTE_MOREFLAGS (1<<2) // a byte, for channel flags
#define SND_DP_PITCH (1<<5) //dp uses this for pitch...
#define SND_FTE_TIMEOFS (1<<6) //signed short, in milliseconds.
#define SND_FTE_PITCHADJ (1<<7) //a byte (speed percent (0=100%))
#define SND_FTE_VELOCITY (1<<8) //3 shorts (1/8th), for doppler or whatever.
//spike
//johnfitz -- PROTOCOL_FITZQUAKE -- flags for entity baseline messages
#define B_LARGEMODEL (1<<0) // modelindex is short instead of byte
@ -178,6 +269,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#define svc_damage 19
#define svc_spawnstatic 20
//#define svc_spawnbinary 21
#define svcfte_spawnstatic2 21
#define svc_spawnbaseline 22
#define svc_temp_entity 23
#define svc_setpause 24 // [byte] on / off
@ -201,6 +293,31 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#define svc_spawnstaticsound2 44 // [coord3] [short] samp [byte] vol [byte] aten
//johnfitz
//spike -- some extensions for particles.
//some extra stuff for fte's pext2_replacementdeltas, including stats
//fte reuses the dp svcs for nq (instead of qw-specific ones), at least where the protocol is identical. this should make dpp7 support a little easier if you ever want to implement that.
//dp has a tendancy to use the svcs even when told to use protocol 15, so supporting them helps there too.
#define svcdp_downloaddata 50
#define svcdp_updatestatbyte 51
#define svcdp_effect 52 // [vector] org [byte] modelindex [byte] startframe [byte] framecount [byte] framerate
#define svcdp_effect2 53 // [vector] org [short] modelindex [short] startframe [byte] framecount [byte] framerate
#define svcdp_precache 54 // [short] precacheindex [string] filename. index&0x8000 = sound, 0x4000 = particle, 0xc000 = reserved (probably to reclaim these bits eventually), otherwise model.
#define svcdp_spawnbaseline2 55
#define svcdp_spawnstatic2 56
#define svcdp_entities 57
#define svcdp_csqcentities 58
#define svcdp_spawnstaticsound2 59 // [coord3] [short] samp [byte] vol [byte] aten
#define svcdp_trailparticles 60 // [short] entnum [short] effectnum [vector] start [vector] end
#define svcdp_pointparticles 61 // [short] effectnum [vector] start [vector] velocity [short] count
#define svcdp_pointparticles1 62 // [short] effectnum [vector] start, same as svc_pointparticles except velocity is zero and count is 1
#define svcfte_spawnbaseline2 66
#define svcfte_updatestatstring 78
#define svcfte_updatestatfloat 79
#define svcfte_voicechat 84
#define svcfte_setangledelta 85
#define svcfte_updateentities 86
//spike -- end
//
// client to server
//
@ -209,6 +326,9 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#define clc_disconnect 2
#define clc_move 3 // [usercmd_t]
#define clc_stringcmd 4 // [string] message
#define clcdp_ackframe 50 // [long] frame sequence. reused by fte replacement deltas
#define clcdp_ackdownloaddata 51
#define clcfte_voicechat 83 /*spike*/
//
// temp entity events
@ -231,17 +351,93 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#define TE_BEAM 13
// PGM 01/21/97
typedef struct
//spike, these are from nehahra. misc servers might expect us to support them
#define TENEH_EXPLOSION3 16 // [vector] origin [coord] red [coord] green [coord] blue
#define TENEH_LIGHTNING4 17 // [string] model [entity] entity [vector] start [vector] end
//spike, quakeworld mods have different explosion+gunshot behaviour, which we might be expected to support
#define TEFTE_EXPLOSION_SPRITE 20 //for compat with qw mods
#define TEFTE_GUNSHOT_COUNT 21 //for compat with qw mods
//spike, these are the various te effects from DP. ideally people would just use pointparticles so these are somewhat considered legacy.
#define TEDP_BLOOD 50
#define TEDP_SPARK 51
#define TEDP_BLOODSHOWER 52
#define TEDP_EXPLOSIONRGB 53
#define TEDP_PARTICLECUBE 54
#define TEDP_PARTICLERAIN 55 // [vector] min [vector] max [vector] dir [short] count [byte] color
#define TEDP_PARTICLESNOW 56 // [vector] min [vector] max [vector] dir [short] count [byte] color
#define TEDP_GUNSHOTQUAD 57 // [vector] origin
#define TEDP_SPIKEQUAD 58 // [vector] origin
#define TEDP_SUPERSPIKEQUAD 59 // [vector] origin
#define TEDP_EXPLOSIONQUAD 70 // [vector] origin
#define TEDP_SMALLFLASH 72 // [vector] origin
#define TEDP_CUSTOMFLASH 73
#define TEDP_FLAMEJET 74
#define TEDP_PLASMABURN 75
#define TEDP_TEI_G3 76
#define TEDP_SMOKE 77
#define TEDP_TEI_BIGEXPLOSION 78
#define TEDP_TEI_PLASMAHIT 79
//end spike
// entity effects
#define EF_BRIGHTFIELD 1
#define EF_MUZZLEFLASH 2
#define EF_BRIGHTLIGHT 4
#define EF_DIMLIGHT 8
//#define EF_NODRAW 16
//#define EF_ADDITIVE 32
//#define EF_BLUE 64
//#define EF_RED 128
//#define EFDP_NOGUNBOB (1u<<8)
#define EF_FULLBRIGHT (1u<<9)
//#define EFDP_PART_FLAME (1u<<10)
//#define EFDP_PART_STARDUST (1u<<11)
#define EF_NOSHADOW (1u<<12)
//#define EF_NODEPTHTEST (1u<<13)
//#define EFDP_SELECTABLE (1u<<14)
//#define EFDP_DOUBLESIDED (1u<<15)
//#define EFDP_NOSELFSHADOW (1u<<16)
//#define EFDP_DYNAMICMODELLIGHT (1u<<17)
//#define EF_GREEN (1u<<18)
//#define EF_UNUSED (1u<<19)
//#define EF_RESTARTANIM_BIT (1u<<20) //reset model lerps over toggles
//#define EF_TELEPORT_BIT (1u<<21) //reset origin lerps over toggles
//#define EFDP_LOWPRECISION (1u<<22)
#define EF_NOMODELFLAGS (1u<<23)
//24-31 are used for modelflag overrides (rotate, legacy trails, etc)
typedef struct entity_state_s
{
vec3_t origin;
vec3_t angles;
unsigned short modelindex; //johnfitz -- was int
unsigned short frame; //johnfitz -- was int
unsigned int effects;
unsigned char colormap; //johnfitz -- was int
unsigned char skin; //johnfitz -- was int
unsigned char scale; //spike -- *16
unsigned char pmovetype; //spike
unsigned short traileffectnum; //spike -- for qc-defined particle trails. typically evilly used for things that are not trails.
unsigned short emiteffectnum; //spike -- for qc-defined particle trails. typically evilly used for things that are not trails.
short velocity[3]; //spike -- the player's velocity.
unsigned char eflags;
unsigned char tagindex;
unsigned short tagentity;
// unsigned short pad;
unsigned char colormod[3]; //spike -- entity tints, *32
unsigned char alpha; //johnfitz -- added
int effects;
} entity_state_t;
#define EFLAGS_STEP 1
//#define EFLAGS_GLOWTRAIL 2
#define EFLAGS_VIEWMODEL 4 //does not appear in reflections/third person. attached to the view.
#define EFLAGS_EXTERIORMODEL 8 //only appears in reflections/third person
//#define EFLAGS_ 16
//#define EFLAGS_COLOURMAPPED 32 //.colormap=1024|(top<<4)|bottom), instead of a player number
//#define EFLAGS_ 64
#define EFLAGS_ONGROUND 128 //for bobbing more than anything else. *sigh*.
extern entity_state_t nullentitystate; //note: not all null.
typedef struct
{

View File

@ -105,9 +105,6 @@ void S_UnblockSound (void);
sfx_t *S_PrecacheSound (const char *sample);
void S_TouchSound (const char *sample);
void S_ClearPrecache (void);
void S_BeginPrecaching (void);
void S_EndPrecaching (void);
void S_PaintChannels (int endtime);
void S_InitPaintChannels (void);
@ -147,10 +144,10 @@ void SNDDMA_UnblockSound(void);
* ====================================================================
*/
#define MAX_CHANNELS 1024 // ericw -- was 512 /* johnfitz -- was 128 */
//#define MAX_CHANNELS 1024 // spike -- made this obsolete. ericw -- was 512 /* johnfitz -- was 128 */
#define MAX_DYNAMIC_CHANNELS 128 /* johnfitz -- was 8 */
extern channel_t snd_channels[MAX_CHANNELS];
extern channel_t *snd_channels;
/* 0 to MAX_DYNAMIC_CHANNELS-1 = normal entity sounds
* MAX_DYNAMIC_CHANNELS to MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS -1 = water, etc
* MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS to total_channels = static sounds
@ -158,11 +155,14 @@ extern channel_t snd_channels[MAX_CHANNELS];
extern volatile dma_t *shm;
extern int max_channels;
extern int total_channels;
extern int soundtime;
extern int paintedtime;
extern int s_rawend;
extern float voicevolumescale;
extern vec3_t listener_origin;
extern vec3_t listener_forward;
extern vec3_t listener_right;

View File

@ -48,10 +48,16 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
// combined version string like "0.92.1-beta1"
#define QUAKESPASM_VER_STRING QS_STRINGIFY(QUAKESPASM_VERSION) "." QS_STRINGIFY(QUAKESPASM_VER_PATCH) QUAKESPASM_VER_SUFFIX
#define ENGINE_NAME_AND_VER "QSS" " " QUAKESPASM_VER_STRING
//define PARANOID // speed sapping error checking
#define GAMENAME "id1" // directory to look in by default
#define PSET_SCRIPT //enable the scriptable particle system (poorly ported from FTE)
#define PSET_SCRIPT_EFFECTINFO //scripted particle system can load dp's effects
#include "q_stdinc.h"
// !!! if this is changed, it must be changed in d_ifacea.h too !!!
@ -91,12 +97,14 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#define MIN_EDICTS 256 // johnfitz -- lowest allowed value for max_edicts cvar
#define MAX_EDICTS 32000 // johnfitz -- highest allowed value for max_edicts cvar
// ents past 8192 can't play sounds in the standard protocol
#define MAX_LIGHTSTYLES 64
#define MAX_MODELS 2048 // johnfitz -- was 256
#define MAX_LIGHTSTYLES 255 //spike -- file format max of 255, increasing will break saved games.
#define MAX_MODELS 4096 // johnfitz -- was 256
#define MAX_SOUNDS 2048 // johnfitz -- was 256
#define MAX_PARTICLETYPES 2048
#define SAVEGAME_COMMENT_LENGTH 39
#define MAX_LIGHTSTYLES_VANILLA 64
#define MAX_STYLESTRING 64
//
@ -119,6 +127,17 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#define STAT_SECRETS 13 // bumped on client side by svc_foundsecret
#define STAT_MONSTERS 14 // bumped by svc_killedmonster
#define STAT_ITEMS 15 //replaces clc_clientdata info
#define STAT_VIEWHEIGHT 16 //replaces clc_clientdata info
//#define STAT_TIME 17 //zquake, redundant for nq.
//#define STAT_MATCHSTARTTIME 18
//#define STAT_VIEW2 20
#define STAT_VIEWZOOM 21 // DP
#define STAT_IDEALPITCH 25 //nq-emu
#define STAT_PUNCHANGLE_X 26 //nq-emu
#define STAT_PUNCHANGLE_Y 27 //nq-emu
#define STAT_PUNCHANGLE_Z 28 //nq-emu
// stock defines
//
#define IT_SHOTGUN 1
@ -186,7 +205,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//===========================================
#define MAX_SCOREBOARD 16
#define MAX_SCOREBOARD 255
#define MAX_SCOREBOARDNAME 32
#define SOUND_CHANNELS 8
@ -219,6 +238,7 @@ typedef struct
#include "cmd.h"
#include "crc.h"
#include "snd_voip.h"
#include "progs.h"
#include "server.h"
@ -316,6 +336,9 @@ void Host_ClientCommands (const char *fmt, ...) FUNC_PRINTF(1,2);
void Host_ShutdownServer (qboolean crash);
void Host_WriteConfiguration (void);
void Host_AppendDownloadData(client_t *client, sizebuf_t *buf);
void Host_DownloadAck(client_t *client);
void ExtraMaps_Init (void);
void Modlist_Init (void);
void DemoList_Init (void);

View File

@ -94,10 +94,15 @@ Returns the offset of the first vertex's meshxyz_t.xyz in the vbo for the given
model and pose.
=============
*/
static void *GLARB_GetXYZOffset (aliashdr_t *hdr, int pose)
static void *GLARB_GetXYZOffset_MDL (aliashdr_t *hdr, int pose)
{
const int xyzoffs = offsetof (meshxyz_t, xyz);
return (void *) (currententity->model->vboxyzofs + (hdr->numverts_vbo * pose * sizeof (meshxyz_t)) + xyzoffs);
const size_t xyzoffs = offsetof (meshxyz_mdl_t, xyz);
return currententity->model->meshvboptr+(hdr->vbovertofs + (hdr->numverts_vbo * pose * sizeof (meshxyz_mdl_t)) + xyzoffs);
}
static void *GLARB_GetXYZOffset_MD3 (aliashdr_t *hdr, int pose)
{
const size_t xyzoffs = offsetof (meshxyz_md3_t, xyz);
return currententity->model->meshvboptr+(hdr->vbovertofs + (hdr->numverts_vbo * pose * sizeof (meshxyz_md3_t)) + xyzoffs);
}
/*
@ -108,10 +113,15 @@ Returns the offset of the first vertex's meshxyz_t.normal in the vbo for the
given model and pose.
=============
*/
static void *GLARB_GetNormalOffset (aliashdr_t *hdr, int pose)
static void *GLARB_GetNormalOffset_MDL (aliashdr_t *hdr, int pose)
{
const int normaloffs = offsetof (meshxyz_t, normal);
return (void *)(currententity->model->vboxyzofs + (hdr->numverts_vbo * pose * sizeof (meshxyz_t)) + normaloffs);
const size_t normaloffs = offsetof (meshxyz_mdl_t, normal);
return currententity->model->meshvboptr+(hdr->vbovertofs + (hdr->numverts_vbo * pose * sizeof (meshxyz_mdl_t)) + normaloffs);
}
static void *GLARB_GetNormalOffset_MD3 (aliashdr_t *hdr, int pose)
{
const size_t normaloffs = offsetof (meshxyz_md3_t, normal);
return currententity->model->meshvboptr+(hdr->vbovertofs + (hdr->numverts_vbo * pose * sizeof (meshxyz_md3_t)) + normaloffs);
}
/*
@ -249,12 +259,23 @@ void GL_DrawAliasFrame_GLSL (aliashdr_t *paliashdr, lerpdata_t lerpdata, gltextu
GL_EnableVertexAttribArrayFunc (pose1NormalAttrIndex);
GL_EnableVertexAttribArrayFunc (pose2NormalAttrIndex);
GL_VertexAttribPointerFunc (texCoordsAttrIndex, 2, GL_FLOAT, GL_FALSE, 0, (void *)(intptr_t)currententity->model->vbostofs);
GL_VertexAttribPointerFunc (pose1VertexAttrIndex, 4, GL_UNSIGNED_BYTE, GL_FALSE, sizeof (meshxyz_t), GLARB_GetXYZOffset (paliashdr, lerpdata.pose1));
GL_VertexAttribPointerFunc (pose2VertexAttrIndex, 4, GL_UNSIGNED_BYTE, GL_FALSE, sizeof (meshxyz_t), GLARB_GetXYZOffset (paliashdr, lerpdata.pose2));
GL_VertexAttribPointerFunc (texCoordsAttrIndex, 2, GL_FLOAT, GL_FALSE, 0, currententity->model->meshvboptr+paliashdr->vbostofs);
if (paliashdr->posevertssize == 1)
{
GL_VertexAttribPointerFunc (pose1VertexAttrIndex, 4, GL_UNSIGNED_BYTE, GL_FALSE, sizeof (meshxyz_mdl_t), GLARB_GetXYZOffset_MDL (paliashdr, lerpdata.pose1));
GL_VertexAttribPointerFunc (pose2VertexAttrIndex, 4, GL_UNSIGNED_BYTE, GL_FALSE, sizeof (meshxyz_mdl_t), GLARB_GetXYZOffset_MDL (paliashdr, lerpdata.pose2));
// GL_TRUE to normalize the signed bytes to [-1 .. 1]
GL_VertexAttribPointerFunc (pose1NormalAttrIndex, 4, GL_BYTE, GL_TRUE, sizeof (meshxyz_t), GLARB_GetNormalOffset (paliashdr, lerpdata.pose1));
GL_VertexAttribPointerFunc (pose2NormalAttrIndex, 4, GL_BYTE, GL_TRUE, sizeof (meshxyz_t), GLARB_GetNormalOffset (paliashdr, lerpdata.pose2));
GL_VertexAttribPointerFunc (pose1NormalAttrIndex, 4, GL_BYTE, GL_TRUE, sizeof (meshxyz_mdl_t), GLARB_GetNormalOffset_MDL (paliashdr, lerpdata.pose1));
GL_VertexAttribPointerFunc (pose2NormalAttrIndex, 4, GL_BYTE, GL_TRUE, sizeof (meshxyz_mdl_t), GLARB_GetNormalOffset_MDL (paliashdr, lerpdata.pose2));
}
else if (paliashdr->posevertssize == 2)
{
GL_VertexAttribPointerFunc (pose1VertexAttrIndex, 4, GL_SHORT, GL_FALSE, sizeof (meshxyz_md3_t), GLARB_GetXYZOffset_MD3 (paliashdr, lerpdata.pose1));
GL_VertexAttribPointerFunc (pose2VertexAttrIndex, 4, GL_SHORT, GL_FALSE, sizeof (meshxyz_md3_t), GLARB_GetXYZOffset_MD3 (paliashdr, lerpdata.pose2));
// GL_TRUE to normalize the signed bytes to [-1 .. 1]
GL_VertexAttribPointerFunc (pose1NormalAttrIndex, 4, GL_BYTE, GL_TRUE, sizeof (meshxyz_md3_t), GLARB_GetNormalOffset_MD3 (paliashdr, lerpdata.pose1));
GL_VertexAttribPointerFunc (pose2NormalAttrIndex, 4, GL_BYTE, GL_TRUE, sizeof (meshxyz_md3_t), GLARB_GetNormalOffset_MD3 (paliashdr, lerpdata.pose2));
}
// set uniforms
GL_Uniform1fFunc (blendLoc, blend);
@ -277,7 +298,7 @@ void GL_DrawAliasFrame_GLSL (aliashdr_t *paliashdr, lerpdata_t lerpdata, gltextu
}
// draw
glDrawElements (GL_TRIANGLES, paliashdr->numindexes, GL_UNSIGNED_SHORT, (void *)(intptr_t)currententity->model->vboindexofs);
glDrawElements (GL_TRIANGLES, paliashdr->numindexes, GL_UNSIGNED_SHORT, currententity->model->meshindexesvboptr+paliashdr->eboofs);
// clean up
GL_DisableVertexAttribArrayFunc (texCoordsAttrIndex);
@ -294,111 +315,215 @@ void GL_DrawAliasFrame_GLSL (aliashdr_t *paliashdr, lerpdata_t lerpdata, gltextu
/*
=============
GL_DrawAliasFrame -- johnfitz -- rewritten to support colored light, lerping, entalpha, multitexture, and r_drawflat
GL_DrawAliasFrame
-- johnfitz -- rewritten to support colored light, lerping, entalpha, multitexture, and r_drawflat
-- spike -- rewritten to use vertex arrays, which should be slightly faster thanks to less branches+gl calls (note that this requires gl1.1, which we depend on anyway for texture objects, and is pretty much universal.
=============
*/
void GL_DrawAliasFrame (aliashdr_t *paliashdr, lerpdata_t lerpdata)
{
float vertcolor[4];
trivertx_t *verts1, *verts2;
int *commands;
int count;
float u,v;
static vec3_t vpos[65536];
static vec4_t vc[65536];
int i;
float blend, iblend;
qboolean lerping;
if (lerpdata.pose1 != lerpdata.pose2)
{
lerping = true;
verts1 = (trivertx_t *)((byte *)paliashdr + paliashdr->posedata);
verts2 = verts1;
verts1 += lerpdata.pose1 * paliashdr->poseverts;
verts2 += lerpdata.pose2 * paliashdr->poseverts;
blend = lerpdata.blend;
iblend = 1.0f - blend;
iblend = 1.0-blend;
}
else // poses the same means either 1. the entity has paused its animation, or 2. r_lerpmodels is disabled
{
lerping = false;
verts1 = (trivertx_t *)((byte *)paliashdr + paliashdr->posedata);
verts2 = verts1; // avoid bogus compiler warning
verts1 += lerpdata.pose1 * paliashdr->poseverts;
blend = iblend = 0; // avoid bogus compiler warning
blend = 1;
iblend = 0;
}
commands = (int *)((byte *)paliashdr + paliashdr->commands);
//pose1*iblend + pose2*blend
vertcolor[3] = entalpha; //never changes, so there's no need to put this inside the loop
while (1)
if (shading && r_drawflat_cheatsafe)
{
// get the vertex count and primitive type
count = *commands++;
if (!count)
break; // done
if (count < 0)
{
count = -count;
glBegin (GL_TRIANGLE_FAN);
shading = false;
glColor4f (rand()%256/255.0, rand()%256/255.0, rand()%256/255.0, entalpha);
}
else
glBegin (GL_TRIANGLE_STRIP);
do
glEnableClientState(GL_VERTEX_ARRAY);
if (paliashdr->posevertssize == 1)
{
u = ((float *)commands)[0];
v = ((float *)commands)[1];
if (mtexenabled)
trivertx_t *verts1 = (trivertx_t*)((byte *)paliashdr + paliashdr->vertexes) + lerpdata.pose1 * paliashdr->numverts_vbo;
trivertx_t *verts2 = (trivertx_t*)((byte *)paliashdr + paliashdr->vertexes) + lerpdata.pose2 * paliashdr->numverts_vbo;
if (iblend)
{
GL_MTexCoord2fFunc (GL_TEXTURE0_ARB, u, v);
GL_MTexCoord2fFunc (GL_TEXTURE1_ARB, u, v);
for (i = 0; i < paliashdr->numverts_vbo; i++)
{
vpos[i][0] = verts1[i].v[0] * iblend + blend * verts2[i].v[0];
vpos[i][1] = verts1[i].v[1] * iblend + blend * verts2[i].v[1];
vpos[i][2] = verts1[i].v[2] * iblend + blend * verts2[i].v[2];
}
else
glTexCoord2f (u, v);
commands += 2;
GL_BindBuffer (GL_ARRAY_BUFFER, 0);
glVertexPointer(3, GL_FLOAT, sizeof (vpos[0]), vpos);
if (shading)
{
if (r_drawflat_cheatsafe)
for (i = 0; i < paliashdr->numverts_vbo; i++)
{
srand(count * (unsigned int)(src_offset_t)commands);
glColor3f (rand()%256/255.0, rand()%256/255.0, rand()%256/255.0);
vc[i][0] = (shadedots[verts1->lightnormalindex]*iblend + shadedots[verts2->lightnormalindex]*blend) * lightcolor[0];
vc[i][1] = (shadedots[verts1->lightnormalindex]*iblend + shadedots[verts2->lightnormalindex]*blend) * lightcolor[1];
vc[i][2] = (shadedots[verts1->lightnormalindex]*iblend + shadedots[verts2->lightnormalindex]*blend) * lightcolor[2];
vc[i][3] = entalpha;
}
glEnableClientState(GL_COLOR_ARRAY);
glColorPointer(4, GL_FLOAT, sizeof(*vc), vc);
}
else if (lerping)
{
vertcolor[0] = (shadedots[verts1->lightnormalindex]*iblend + shadedots[verts2->lightnormalindex]*blend) * lightcolor[0];
vertcolor[1] = (shadedots[verts1->lightnormalindex]*iblend + shadedots[verts2->lightnormalindex]*blend) * lightcolor[1];
vertcolor[2] = (shadedots[verts1->lightnormalindex]*iblend + shadedots[verts2->lightnormalindex]*blend) * lightcolor[2];
glColor4fv (vertcolor);
}
else
{
vertcolor[0] = shadedots[verts1->lightnormalindex] * lightcolor[0];
vertcolor[1] = shadedots[verts1->lightnormalindex] * lightcolor[1];
vertcolor[2] = shadedots[verts1->lightnormalindex] * lightcolor[2];
glColor4fv (vertcolor);
if (shading)
{
for (i = 0; i < paliashdr->numverts_vbo; i++)
{
vc[i][0] = shadedots[verts2->lightnormalindex] * lightcolor[0];
vc[i][1] = shadedots[verts2->lightnormalindex] * lightcolor[1];
vc[i][2] = shadedots[verts2->lightnormalindex] * lightcolor[2];
vc[i][3] = entalpha;
}
glEnableClientState(GL_COLOR_ARRAY);
GL_BindBuffer (GL_ARRAY_BUFFER, 0);
glColorPointer(4, GL_FLOAT, 0, vc);
}
if (lerping)
//glVertexPointer may not take GL_UNSIGNED_BYTE, which means we can't use our vbos. attribute 0 MAY be vertex coords, but I don't want to depend on that.
for (i = 0; i < paliashdr->numverts_vbo; i++)
{
glVertex3f (verts1->v[0]*iblend + verts2->v[0]*blend,
verts1->v[1]*iblend + verts2->v[1]*blend,
verts1->v[2]*iblend + verts2->v[2]*blend);
verts1++;
verts2++;
vpos[i][0] = verts2[i].v[0];
vpos[i][1] = verts2[i].v[1];
vpos[i][2] = verts2[i].v[2];
}
GL_BindBuffer (GL_ARRAY_BUFFER, 0);
glVertexPointer(3, GL_FLOAT, sizeof (vpos[0]), vpos);
}
}
else if (paliashdr->posevertssize == 2)
{
md3XyzNormal_t *verts1 = (md3XyzNormal_t*)((byte *)paliashdr + paliashdr->vertexes) + lerpdata.pose1 * paliashdr->numverts_vbo;
md3XyzNormal_t *verts2 = (md3XyzNormal_t*)((byte *)paliashdr + paliashdr->vertexes) + lerpdata.pose2 * paliashdr->numverts_vbo;
if (iblend)
{
for (i = 0; i < paliashdr->numverts_vbo; i++)
{
vpos[i][0] = verts1[i].xyz[0] * iblend + blend * verts2[i].xyz[0];
vpos[i][1] = verts1[i].xyz[1] * iblend + blend * verts2[i].xyz[1];
vpos[i][2] = verts1[i].xyz[2] * iblend + blend * verts2[i].xyz[2];
}
GL_BindBuffer (GL_ARRAY_BUFFER, 0);
glVertexPointer(3, GL_FLOAT, sizeof (vpos[0]), vpos);
if (shading)
{
for (i = 0; i < paliashdr->numverts_vbo; i++)
{
vec3_t n;
float dot;
// map the normal coordinates in [-1..1] to [-127..127] and store in an unsigned char.
// this introduces some error (less than 0.004), but the normals were very coarse
// to begin with
//this should be a table.
float lat = (float)verts2[i].latlong[0] * (2 * M_PI)*(1.0 / 255.0);
float lng = (float)verts2[i].latlong[1] * (2 * M_PI)*(1.0 / 255.0);
n[0] = blend * cos ( lng ) * sin ( lat );
n[1] = blend * sin ( lng ) * sin ( lat );
n[2] = blend * cos ( lat );
lat = (float)verts1[i].latlong[0] * (2 * M_PI)*(1.0 / 255.0);
lng = (float)verts1[i].latlong[1] * (2 * M_PI)*(1.0 / 255.0);
n[0] += iblend * cos ( lng ) * sin ( lat );
n[1] += iblend * sin ( lng ) * sin ( lat );
n[2] += iblend * cos ( lat );
dot = DotProduct(n, shadevector);
if (dot < 0.0) //bizzare maths guessed by mh
dot = 1.0 + dot * (13.0 / 44.0);
else
dot = 1.0 + dot;
vc[i][0] = dot * lightcolor[0];
vc[i][1] = dot * lightcolor[1];
vc[i][2] = dot * lightcolor[2];
vc[i][3] = entalpha;
}
glEnableClientState(GL_COLOR_ARRAY);
glColorPointer(4, GL_FLOAT, 0, vc);
}
}
else
{
glVertex3f (verts1->v[0], verts1->v[1], verts1->v[2]);
verts1++;
if (shading)
{
for (i = 0; i < paliashdr->numverts_vbo; i++)
{
vec3_t n;
float dot;
// map the normal coordinates in [-1..1] to [-127..127] and store in an unsigned char.
// this introduces some error (less than 0.004), but the normals were very coarse
// to begin with
//this should be a table.
float lat = (float)verts2[i].latlong[0] * (2 * M_PI)*(1.0 / 255.0);
float lng = (float)verts2[i].latlong[1] * (2 * M_PI)*(1.0 / 255.0);
n[0] = cos ( lng ) * sin ( lat );
n[1] = sin ( lng ) * sin ( lat );
n[2] = cos ( lat );
dot = DotProduct(n, shadevector);
if (dot < 0.0) //bizzare maths guessed by mh
dot = 1.0 + dot * (13.0 / 44.0);
else
dot = 1.0 + dot;
vc[i][0] = dot * lightcolor[0];
vc[i][1] = dot * lightcolor[1];
vc[i][2] = dot * lightcolor[2];
vc[i][3] = entalpha;
}
glEnableClientState(GL_COLOR_ARRAY);
GL_BindBuffer (GL_ARRAY_BUFFER, 0);
glColorPointer(4, GL_FLOAT, 0, vc);
}
GL_BindBuffer (GL_ARRAY_BUFFER, currententity->model->meshvbo);
glVertexPointer(3, GL_SHORT, sizeof (meshxyz_md3_t), GLARB_GetXYZOffset_MD3 (paliashdr, lerpdata.pose2));
}
}
} while (--count);
glEnd ();
// set textures
GL_BindBuffer (GL_ARRAY_BUFFER, currententity->model->meshvbo);
if (mtexenabled)
{
GL_ClientActiveTextureFunc (GL_TEXTURE0);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glTexCoordPointer(2, GL_FLOAT, 0, currententity->model->meshvboptr+paliashdr->vbostofs);
GL_ClientActiveTextureFunc (GL_TEXTURE1);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glTexCoordPointer(2, GL_FLOAT, 0, currententity->model->meshvboptr+paliashdr->vbostofs);
}
else
{
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glTexCoordPointer(2, GL_FLOAT, 0, currententity->model->meshvboptr+paliashdr->vbostofs);
}
// draw
GL_BindBuffer (GL_ELEMENT_ARRAY_BUFFER, currententity->model->meshindexesvbo);
glDrawElements (GL_TRIANGLES, paliashdr->numindexes, GL_UNSIGNED_SHORT, currententity->model->meshindexesvboptr + paliashdr->eboofs);
GL_BindBuffer (GL_ELEMENT_ARRAY_BUFFER, 0);
GL_BindBuffer (GL_ARRAY_BUFFER, 0);
// clean up
if (mtexenabled)
{
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
GL_ClientActiveTextureFunc (GL_TEXTURE0);
}
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
rs_aliaspasses += paliashdr->numtris;
}
@ -546,15 +671,18 @@ void R_SetupAliasLighting (entity_t *e)
int i;
int quantizedangle;
float radiansangle;
float *origin = e->origin;
R_LightPoint (e->origin);
if (e->netstate.eflags & EFLAGS_VIEWMODEL)
origin = r_refdef.vieworg;
R_LightPoint (origin);
//add dlights
for (i=0 ; i<MAX_DLIGHTS ; i++)
{
if (cl_dlights[i].die >= cl.time)
{
VectorSubtract (currententity->origin, cl_dlights[i].origin, dist);
VectorSubtract (origin, cl_dlights[i].origin, dist);
add = cl_dlights[i].radius - VectorLength(dist);
if (add > 0)
VectorMA (lightcolor, add, cl_dlights[i].color, lightcolor);
@ -574,7 +702,7 @@ void R_SetupAliasLighting (entity_t *e)
}
// minimum light value on players (8)
if (currententity > cl_entities && currententity <= cl_entities + cl.maxclients)
if (e > cl.entities && e <= cl.entities + cl.maxclients)
{
add = 24.0f - (lightcolor[0] + lightcolor[1] + lightcolor[2]);
if (add > 0.0f)
@ -615,6 +743,10 @@ void R_SetupAliasLighting (entity_t *e)
shadedots = r_avertexnormal_dots[quantizedangle];
VectorScale (lightcolor, 1.0f / 200.0f, lightcolor);
lightcolor[0] *= e->netstate.colormod[0] / 32.0;
lightcolor[1] *= e->netstate.colormod[1] / 32.0;
lightcolor[2] *= e->netstate.colormod[2] / 32.0;
}
/*
@ -629,6 +761,7 @@ void R_DrawAliasModel (entity_t *e)
gltexture_t *tx, *fb;
lerpdata_t lerpdata;
qboolean alphatest = !!(e->model->flags & MF_HOLEY);
int surf;
//
// setup pose/lerp data -- do it first so we don't miss updates due to culling
@ -637,6 +770,18 @@ void R_DrawAliasModel (entity_t *e)
R_SetupAliasFrame (paliashdr, e->frame, &lerpdata);
R_SetupEntityTransform (e, &lerpdata);
if (e->netstate.eflags & EFLAGS_VIEWMODEL)
{
//transform it relative to the view, by rebuilding the modelview matrix without the view position.
glPushMatrix ();
glLoadIdentity();
glRotatef (-90, 1, 0, 0); // put Z going up
glRotatef (90, 0, 0, 1); // put Z going up
glDepthRange (0, 0.3);
}
else
{
//
// cull it
//
@ -647,7 +792,8 @@ void R_DrawAliasModel (entity_t *e)
// transform it
//
glPushMatrix ();
R_RotateForEntity (lerpdata.origin, lerpdata.angles);
}
R_RotateForEntity (lerpdata.origin, lerpdata.angles, e->netstate.scale);
glTranslatef (paliashdr->scale_origin[0], paliashdr->scale_origin[1], paliashdr->scale_origin[2]);
glScalef (paliashdr->scale[0], paliashdr->scale[1], paliashdr->scale[2]);
@ -682,9 +828,12 @@ void R_DrawAliasModel (entity_t *e)
//
// set up lighting
//
rs_aliaspolys += paliashdr->numtris;
R_SetupAliasLighting (e);
for(surf=0;;surf++)
{
rs_aliaspolys += paliashdr->numtris;
//
// set up textures
//
@ -697,11 +846,19 @@ void R_DrawAliasModel (entity_t *e)
// ericw -- display skin 0 for winquake compatibility
skinnum = 0;
}
if (paliashdr->numskins <= 0)
{
tx = NULL; // NULL will give the checkerboard texture
fb = NULL;
}
else
{
tx = paliashdr->gltextures[skinnum][anim];
fb = paliashdr->fbtextures[skinnum][anim];
}
if (e->colormap != vid.colormap && !gl_nocolors.value)
{
i = e - cl_entities;
i = e - cl.entities;
if (i >= 1 && i<=cl.maxclients /* && !strcmp (currententity->model->name, "progs/player.mdl") */)
tx = playertextures[i - 1];
}
@ -767,9 +924,9 @@ void R_DrawAliasModel (entity_t *e)
GL_EnableMultitexture(); // selects TEXTURE1
GL_Bind (fb);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_ADD);
glEnable(GL_BLEND);
// glEnable(GL_BLEND);
GL_DrawAliasFrame (paliashdr, lerpdata);
glDisable(GL_BLEND);
// glDisable(GL_BLEND);
GL_DisableMultitexture();
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
}
@ -881,6 +1038,10 @@ void R_DrawAliasModel (entity_t *e)
}
}
}
if (!paliashdr->nextsurface)
break;
paliashdr = (aliashdr_t*)((byte*)paliashdr + paliashdr->nextsurface);
}
cleanup:
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
@ -891,6 +1052,8 @@ cleanup:
if (alphatest)
glDisable (GL_ALPHA_TEST);
glColor3f(1,1,1);
if (e->netstate.eflags & EFLAGS_VIEWMODEL)
glDepthRange (0, 1);
glPopMatrix ();
}
@ -921,7 +1084,7 @@ void GL_DrawAliasShadow (entity_t *e)
if (R_CullModelForEntity(e))
return;
if (e == &cl.viewent || e->model->flags & MOD_NOSHADOW)
if (e == &cl.viewent || e->effects & EF_NOSHADOW || e->model->flags & MOD_NOSHADOW)
return;
entalpha = ENTALPHA_DECODE(e->alpha);
@ -979,7 +1142,7 @@ void R_DrawAliasModel_ShowTris (entity_t *e)
R_SetupEntityTransform (e, &lerpdata);
glPushMatrix ();
R_RotateForEntity (lerpdata.origin,lerpdata.angles);
R_RotateForEntity (lerpdata.origin,lerpdata.angles, e->netstate.scale);
glTranslatef (paliashdr->scale_origin[0], paliashdr->scale_origin[1], paliashdr->scale_origin[2]);
glScalef (paliashdr->scale[0], paliashdr->scale[1], paliashdr->scale[2]);

View File

@ -549,7 +549,7 @@ void R_DrawBrushModel (entity_t *e)
e->origin[1] -= DIST_EPSILON;
e->origin[2] -= DIST_EPSILON;
}
R_RotateForEntity (e->origin, e->angles);
R_RotateForEntity (e->origin, e->angles, e->netstate.scale);
if (gl_zfix.value)
{
e->origin[0] += DIST_EPSILON;
@ -614,7 +614,7 @@ void R_DrawBrushModel_ShowTris (entity_t *e)
glPushMatrix ();
e->angles[0] = -e->angles[0]; // stupid quake bug
R_RotateForEntity (e->origin, e->angles);
R_RotateForEntity (e->origin, e->angles, e->netstate.scale);
e->angles[0] = -e->angles[0]; // stupid quake bug
//
@ -652,7 +652,7 @@ R_RenderDynamicLightmaps
called during rendering
================
*/
void R_RenderDynamicLightmaps (msurface_t *fa)
void R_RenderDynamicLightmaps (qmodel_t *model, msurface_t *fa)
{
byte *base;
int maps;
@ -698,7 +698,7 @@ dynamic:
theRect->h = (fa->light_t-theRect->t)+tmax;
base = lm->data;
base += fa->light_t * LMBLOCK_WIDTH * lightmap_bytes + fa->light_s * lightmap_bytes;
R_BuildLightMap (fa, base, LMBLOCK_WIDTH*lightmap_bytes);
R_BuildLightMap (model, fa, base, LMBLOCK_WIDTH*lightmap_bytes);
}
}
}
@ -777,7 +777,7 @@ int nColinElim;
GL_CreateSurfaceLightmap
========================
*/
void GL_CreateSurfaceLightmap (msurface_t *surf)
void GL_CreateSurfaceLightmap (qmodel_t *model, msurface_t *surf)
{
int smax, tmax;
byte *base;
@ -788,7 +788,7 @@ void GL_CreateSurfaceLightmap (msurface_t *surf)
surf->lightmaptexturenum = AllocBlock (smax, tmax, &surf->light_s, &surf->light_t);
base = lightmap[surf->lightmaptexturenum].data;
base += (surf->light_t * LMBLOCK_WIDTH + surf->light_s) * lightmap_bytes;
R_BuildLightMap (surf, base, LMBLOCK_WIDTH*lightmap_bytes);
R_BuildLightMap (model, surf, base, LMBLOCK_WIDTH*lightmap_bytes);
}
/*
@ -917,7 +917,7 @@ void GL_BuildLightmaps (void)
//johnfitz -- rewritten to use SURF_DRAWTILED instead of the sky/water flags
if (m->surfaces[i].flags & SURF_DRAWTILED)
continue;
GL_CreateSurfaceLightmap (m->surfaces + i);
GL_CreateSurfaceLightmap (m, m->surfaces + i);
BuildSurfaceDisplayList (m->surfaces + i);
//johnfitz
}
@ -1130,7 +1130,7 @@ R_BuildLightMap -- johnfitz -- revised for lit support via lordhavoc
Combine and scale multiple lightmaps into the 8.8 format in blocklights
===============
*/
void R_BuildLightMap (msurface_t *surf, byte *dest, int stride)
void R_BuildLightMap (qmodel_t *model, msurface_t *surf, byte *dest, int stride)
{
int smax, tmax;
int r,g,b;
@ -1147,7 +1147,7 @@ void R_BuildLightMap (msurface_t *surf, byte *dest, int stride)
size = smax*tmax;
lightmap = surf->samples;
if (cl.worldmodel->lightdata)
if (model->lightdata)
{
// clear to no light
memset (&blocklights[0], 0, size * 3 * sizeof (unsigned int)); //johnfitz -- lit support via lordhavoc
@ -1310,7 +1310,7 @@ void R_RebuildAllLightmaps (void)
continue;
base = lightmap[fa->lightmaptexturenum].data;
base += fa->light_t * LMBLOCK_WIDTH * lightmap_bytes + fa->light_s * lightmap_bytes;
R_BuildLightMap (fa, base, LMBLOCK_WIDTH*lightmap_bytes);
R_BuildLightMap (mod, fa, base, LMBLOCK_WIDTH*lightmap_bytes);
}
}

View File

@ -337,9 +337,19 @@ void R_ParseParticleEffect (void)
color = MSG_ReadByte ();
if (msgcount == 255)
{
if (!PScript_RunParticleEffectTypeString(org, dir, 1, "te_explosion"))
count = 0;
else
count = 1024;
}
else
{
if (!PScript_RunParticleEffect(org, dir, color, msgcount))
count = 0;
else
count = msgcount;
}
R_RunParticleEffect (org, dir, color, count);
}

7787
Quake/r_part_fte.c Executable file

File diff suppressed because it is too large Load Diff

View File

@ -86,6 +86,7 @@ void R_DrawSpriteModel (entity_t *e)
mspriteframe_t *frame;
float *s_up, *s_right;
float angle, sr, cr;
float scale;
//TODO: frustum cull it?
@ -144,7 +145,12 @@ void R_DrawSpriteModel (entity_t *e)
if (psprite->type == SPR_ORIENTED)
GL_PolygonOffset (OFFSET_DECAL);
glColor3f (1,1,1);
if (e->netstate.scale != 16)
scale = e->netstate.scale/16.0;
else
scale = 1;
glColor3f (e->netstate.colormod[0]/32.0,e->netstate.colormod[0]/32.0,e->netstate.colormod[0]/32.0);
GL_DisableMultitexture();
@ -154,28 +160,30 @@ void R_DrawSpriteModel (entity_t *e)
glBegin (GL_TRIANGLE_FAN); //was GL_QUADS, but changed to support r_showtris
glTexCoord2f (0, frame->tmax);
VectorMA (e->origin, frame->down, s_up, point);
VectorMA (point, frame->left, s_right, point);
VectorMA (e->origin, frame->down*scale, s_up, point);
VectorMA (point, frame->left*scale, s_right, point);
glVertex3fv (point);
glTexCoord2f (0, 0);
VectorMA (e->origin, frame->up, s_up, point);
VectorMA (point, frame->left, s_right, point);
VectorMA (e->origin, frame->up*scale, s_up, point);
VectorMA (point, frame->left*scale, s_right, point);
glVertex3fv (point);
glTexCoord2f (frame->smax, 0);
VectorMA (e->origin, frame->up, s_up, point);
VectorMA (point, frame->right, s_right, point);
VectorMA (e->origin, frame->up*scale, s_up, point);
VectorMA (point, frame->right*scale, s_right, point);
glVertex3fv (point);
glTexCoord2f (frame->smax, frame->tmax);
VectorMA (e->origin, frame->down, s_up, point);
VectorMA (point, frame->right, s_right, point);
VectorMA (e->origin, frame->down*scale, s_up, point);
VectorMA (point, frame->right*scale, s_right, point);
glVertex3fv (point);
glEnd ();
glDisable (GL_ALPHA_TEST);
glColor3f (1, 1, 1);
//johnfitz: offset decals
if (psprite->type == SPR_ORIENTED)
GL_PolygonOffset (OFFSET_NONE);

View File

@ -81,6 +81,7 @@ void R_MarkSurfaces (void)
mnode_t *node;
msurface_t *surf, **mark;
int i, j;
unsigned int k;
qboolean nearwaterportal;
// clear lightmap chains
@ -146,7 +147,7 @@ void R_MarkSurfaces (void)
//becuase his tool doesn't actually remove the surfaces from the bsp surfaces lump
//nor does it remove references to them in each leaf's marksurfaces list
for (i=0, node = cl.worldmodel->nodes ; i<cl.worldmodel->numnodes ; i++, node++)
for (j=0, surf=&cl.worldmodel->surfaces[node->firstsurface] ; j<node->numsurfaces ; j++, surf++)
for (k=0, surf=&cl.worldmodel->surfaces[node->firstsurface] ; k<node->numsurfaces ; k++, surf++)
if (surf->visframe == r_visframecount)
{
R_ChainSurface(surf, chain_world);
@ -261,7 +262,7 @@ void R_BuildLightmapChains (qmodel_t *model, texchain_t chain)
for (s = t->texturechains[chain]; s; s = s->texturechain)
if (!s->culled)
R_RenderDynamicLightmaps (s);
R_RenderDynamicLightmaps (model, s);
}
}

View File

@ -53,6 +53,7 @@ typedef struct entity_s
int update_type;
entity_state_t baseline; // to fill in defaults in updates
entity_state_t netstate; // the latest network state
double msgtime; // time of last update
vec3_t msg_origins[2]; // last two updates (0 is newest)
@ -91,6 +92,9 @@ typedef struct entity_s
vec3_t currentorigin; //johnfitz -- transform lerping
vec3_t previousangles; //johnfitz -- transform lerping
vec3_t currentangles; //johnfitz -- transform lerping
struct trailstate_s *trailstate; //spike -- managed by the particle system, so we don't loose our position and spawn the wrong number of particles, and we can track beams etc
struct trailstate_s *emitstate; //spike -- for effects which are not so static.
} entity_t;
// !!! if this is changed, it must be changed in asm_draw.h too !!!

View File

@ -23,6 +23,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "quakedef.h"
extern qboolean premul_hud;
int sb_updates; // if >= vid.numpages, no update needed
#define STAT_MINUS 10 // num frame for '-' stats digit
@ -63,6 +64,11 @@ int hipweapons[4] = {HIT_LASER_CANNON_BIT,HIT_MJOLNIR_BIT,4,HIT_PROXIMITY_GUN_B
//MED 01/04/97 added hipnotic items array
qpic_t *hsb_items[2];
//spike -- fix -game hipnotic by autodetecting hud types. the fte protocols will deal with the networking issue, other than demos, anyway
static int hudtype;
#define hipnotic (hudtype==1)
#define rogue (hudtype==2)
void Sbar_MiniDeathmatchOverlay (void);
void Sbar_DeathmatchOverlay (void);
void M_DrawPic (int x, int y, qpic_t *pic);
@ -105,6 +111,21 @@ void Sbar_Changed (void)
sb_updates = 0; // update next frame
}
qpic_t *Sbar_CheckPicFromWad (const char *name)
{
extern qpic_t *pic_nul;
qpic_t *r;
if (!hudtype)
return pic_nul; //one already failed, don't waste cpu
if (!W_GetLumpinfo(name))
r = pic_nul;
else
r = Draw_PicFromWad(name);
if (r == pic_nul)
hudtype = 0;
return r;
}
/*
===============
Sbar_LoadPics -- johnfitz -- load all the sbar pics
@ -194,55 +215,59 @@ void Sbar_LoadPics (void)
sb_ibar = Draw_PicFromWad ("ibar");
sb_scorebar = Draw_PicFromWad ("scorebar");
//MED 01/04/97 added new hipnotic weapons
if (hipnotic)
{
hsb_weapons[0][0] = Draw_PicFromWad ("inv_laser");
hsb_weapons[0][1] = Draw_PicFromWad ("inv_mjolnir");
hsb_weapons[0][2] = Draw_PicFromWad ("inv_gren_prox");
hsb_weapons[0][3] = Draw_PicFromWad ("inv_prox_gren");
hsb_weapons[0][4] = Draw_PicFromWad ("inv_prox");
hudtype = 0;
hsb_weapons[1][0] = Draw_PicFromWad ("inv2_laser");
hsb_weapons[1][1] = Draw_PicFromWad ("inv2_mjolnir");
hsb_weapons[1][2] = Draw_PicFromWad ("inv2_gren_prox");
hsb_weapons[1][3] = Draw_PicFromWad ("inv2_prox_gren");
hsb_weapons[1][4] = Draw_PicFromWad ("inv2_prox");
//MED 01/04/97 added new hipnotic weapons
if (!hudtype)
{
hudtype = 1;
hsb_weapons[0][0] = Sbar_CheckPicFromWad ("inv_laser");
hsb_weapons[0][1] = Sbar_CheckPicFromWad ("inv_mjolnir");
hsb_weapons[0][2] = Sbar_CheckPicFromWad ("inv_gren_prox");
hsb_weapons[0][3] = Sbar_CheckPicFromWad ("inv_prox_gren");
hsb_weapons[0][4] = Sbar_CheckPicFromWad ("inv_prox");
hsb_weapons[1][0] = Sbar_CheckPicFromWad ("inv2_laser");
hsb_weapons[1][1] = Sbar_CheckPicFromWad ("inv2_mjolnir");
hsb_weapons[1][2] = Sbar_CheckPicFromWad ("inv2_gren_prox");
hsb_weapons[1][3] = Sbar_CheckPicFromWad ("inv2_prox_gren");
hsb_weapons[1][4] = Sbar_CheckPicFromWad ("inv2_prox");
for (i = 0; i < 5; i++)
{
hsb_weapons[2+i][0] = Draw_PicFromWad (va("inva%i_laser",i+1));
hsb_weapons[2+i][1] = Draw_PicFromWad (va("inva%i_mjolnir",i+1));
hsb_weapons[2+i][2] = Draw_PicFromWad (va("inva%i_gren_prox",i+1));
hsb_weapons[2+i][3] = Draw_PicFromWad (va("inva%i_prox_gren",i+1));
hsb_weapons[2+i][4] = Draw_PicFromWad (va("inva%i_prox",i+1));
hsb_weapons[2+i][0] = Sbar_CheckPicFromWad (va("inva%i_laser",i+1));
hsb_weapons[2+i][1] = Sbar_CheckPicFromWad (va("inva%i_mjolnir",i+1));
hsb_weapons[2+i][2] = Sbar_CheckPicFromWad (va("inva%i_gren_prox",i+1));
hsb_weapons[2+i][3] = Sbar_CheckPicFromWad (va("inva%i_prox_gren",i+1));
hsb_weapons[2+i][4] = Sbar_CheckPicFromWad (va("inva%i_prox",i+1));
}
hsb_items[0] = Draw_PicFromWad ("sb_wsuit");
hsb_items[1] = Draw_PicFromWad ("sb_eshld");
hsb_items[0] = Sbar_CheckPicFromWad ("sb_wsuit");
hsb_items[1] = Sbar_CheckPicFromWad ("sb_eshld");
}
if (rogue)
if (!hudtype)
{
rsb_invbar[0] = Draw_PicFromWad ("r_invbar1");
rsb_invbar[1] = Draw_PicFromWad ("r_invbar2");
hudtype = 2;
rsb_invbar[0] = Sbar_CheckPicFromWad ("r_invbar1");
rsb_invbar[1] = Sbar_CheckPicFromWad ("r_invbar2");
rsb_weapons[0] = Draw_PicFromWad ("r_lava");
rsb_weapons[1] = Draw_PicFromWad ("r_superlava");
rsb_weapons[2] = Draw_PicFromWad ("r_gren");
rsb_weapons[3] = Draw_PicFromWad ("r_multirock");
rsb_weapons[4] = Draw_PicFromWad ("r_plasma");
rsb_weapons[0] = Sbar_CheckPicFromWad ("r_lava");
rsb_weapons[1] = Sbar_CheckPicFromWad ("r_superlava");
rsb_weapons[2] = Sbar_CheckPicFromWad ("r_gren");
rsb_weapons[3] = Sbar_CheckPicFromWad ("r_multirock");
rsb_weapons[4] = Sbar_CheckPicFromWad ("r_plasma");
rsb_items[0] = Draw_PicFromWad ("r_shield1");
rsb_items[1] = Draw_PicFromWad ("r_agrav1");
rsb_items[0] = Sbar_CheckPicFromWad ("r_shield1");
rsb_items[1] = Sbar_CheckPicFromWad ("r_agrav1");
// PGM 01/19/97 - team color border
rsb_teambord = Draw_PicFromWad ("r_teambord");
rsb_teambord = Sbar_CheckPicFromWad ("r_teambord");
// PGM 01/19/97 - team color border
rsb_ammo[0] = Draw_PicFromWad ("r_ammolava");
rsb_ammo[1] = Draw_PicFromWad ("r_ammomulti");
rsb_ammo[2] = Draw_PicFromWad ("r_ammoplasma");
rsb_ammo[0] = Sbar_CheckPicFromWad ("r_ammolava");
rsb_ammo[1] = Sbar_CheckPicFromWad ("r_ammomulti");
rsb_ammo[2] = Sbar_CheckPicFromWad ("r_ammoplasma");
}
}
@ -280,6 +305,14 @@ Sbar_DrawPicAlpha -- johnfitz
=============
*/
void Sbar_DrawPicAlpha (int x, int y, qpic_t *pic, float alpha)
{
if (premul_hud)
{
glColor4f(alpha,alpha,alpha,alpha);
Draw_Pic (x, y + 24, pic);
glColor4f(1,1,1,1);
}
else
{
glDisable (GL_ALPHA_TEST);
glEnable (GL_BLEND);
@ -289,6 +322,7 @@ void Sbar_DrawPicAlpha (int x, int y, qpic_t *pic, float alpha)
glDisable (GL_BLEND);
glEnable (GL_ALPHA_TEST);
}
}
/*
================
@ -918,6 +952,35 @@ void Sbar_DrawFace (void)
Sbar_DrawPic (112, 0, sb_faces[f][anim]);
}
static void Sbar_Voice(int y)
{
cvar_t snd_voip_showmeter;
int loudness;
snd_voip_showmeter.value = 1;
if (!snd_voip_showmeter.value)
return;
loudness = S_Voip_Loudness(snd_voip_showmeter.value>=2);
if (loudness >= 0)
{
int cw = 8;
int w;
int x=160;
int s, i;
float range = loudness/100.0f;
w = (5+16+1)*cw;
x -= w/2;
Draw_Character (x, y, 'M'); x+=cw;
Draw_Character (x, y, 'i'); x+=cw;
Draw_Character (x, y, 'c'); x+=cw;
x+=cw;
Draw_Character (x, y, 0xe080); x+=cw;
for (s=x,i=0 ; i<16 ; i++, x+=cw)
Draw_Character(x, y, 0xe081);
Draw_Character (x, y, 0xe082);
Draw_Character (s + (x-s) * range - cw/2, y, 0xe083);
}
}
/*
===============
Sbar_Draw
@ -941,6 +1004,13 @@ void Sbar_Draw (void)
GL_SetCanvas (CANVAS_DEFAULT); //johnfitz
if (sb_lines > 24)
Sbar_Voice(-32);
else if (sb_lines > 0)
Sbar_Voice(-8);
else
Sbar_Voice(16);
//johnfitz -- don't waste fillrate by clearing the area behind the sbar
w = CLAMP (320.0f, scr_sbarscale.value * 320.0f, (float)glwidth);
if (sb_lines && glwidth > w)
@ -1138,6 +1208,9 @@ void Sbar_DeathmatchOverlay (void)
top = Sbar_ColorForMap (top);
bottom = Sbar_ColorForMap (bottom);
if (S_Voip_Speaking(k)) //spike -- display an underlay for people who are speaking
Draw_Fill ( x, y, 320-x*2, 8, ((k+1)==cl.viewentity)?75:73, 1);
Draw_Fill ( x, y, 40, 4, top, 1); //johnfitz -- stretched overlays
Draw_Fill ( x, y+4, 40, 4, bottom, 1); //johnfitz -- stretched overlays
@ -1170,6 +1243,9 @@ void Sbar_DeathmatchOverlay (void)
}
#endif
sprintf (num, "%4i", s->ping);
M_PrintWhite (x-8*5, y, num); //johnfitz -- was Draw_String, changed for stretched overlays
// draw name
M_Print (x+64, y, s->name); //johnfitz -- was Draw_String, changed for stretched overlays
@ -1177,6 +1253,13 @@ void Sbar_DeathmatchOverlay (void)
}
GL_SetCanvas (CANVAS_SBAR); //johnfitz
if (!cls.message.cursize && cl.expectingpingtimes < realtime)
{
cl.expectingpingtimes = realtime + 5;
MSG_WriteByte (&cls.message, clc_stringcmd);
MSG_WriteString(&cls.message, "ping");
}
}
/*

View File

@ -75,6 +75,25 @@ typedef struct
unsigned protocol; //johnfitz
unsigned protocolflags;
sizebuf_t multicast; // selectively copied to clients by the multicast builtin
byte multicast_buf[MAX_DATAGRAM];
const char *particle_precache[MAX_PARTICLETYPES]; // NULL terminated
entity_state_t *static_entities;
int num_statics;
int max_statics;
struct ambientsound_s
{
vec3_t origin;
unsigned int soundindex;
float volume;
float attenuation;
} *ambientsounds;
int num_ambients;
int max_ambients;
} server_t;
@ -84,9 +103,10 @@ typedef struct
typedef struct client_s
{
qboolean active; // false = client is free
qboolean spawned; // false = don't send datagrams
qboolean spawned; // false = don't send datagrams (set when client acked the first entities)
qboolean dropasap; // has been told to go to another level
qboolean sendsignon; // only valid before spawned
int sendsignon; // only valid before spawned
int signonidx;
double last_message; // reliable messages must be sent
// periodically
@ -111,6 +131,64 @@ typedef struct client_s
// client known data for deltas
int old_frags;
sizebuf_t datagram;
byte datagram_buf[MAX_DATAGRAM];
unsigned int limit_entities; //vanilla is 600
unsigned int limit_unreliable; //max allowed size for unreliables
unsigned int limit_reliable; //max (total) size of a reliable message.
unsigned int limit_models; //
unsigned int limit_sounds; //
qboolean pextknown;
unsigned int protocol_pext2;
unsigned int resendstats[MAX_CL_STATS/32]; //the stats which need to be resent.
int oldstats_i[MAX_CL_STATS]; //previous values of stats. if these differ from the current values, reflag resendstats.
float oldstats_f[MAX_CL_STATS]; //previous values of stats. if these differ from the current values, reflag resendstats.
struct entity_num_state_s{
unsigned int num; //ascending order, there can be gaps.
entity_state_t state;
} *previousentities;
size_t numpreviousentities;
size_t maxpreviousentities;
unsigned int snapshotresume;
unsigned int *pendingentities_bits; //UF_ flags for each entity
size_t numpendingentities; //realloc if too small
struct deltaframe_s
{ //quick overview of how this stuff actually works:
//when the server notices a gap in the ack sequence, we walk through the dropped frames and reflag everything that was dropped.
//if the server isn't tracking enough frames, then we just treat those as dropped;
//small note: when an entity is new, it re-flags itself as new for the next packet too, this reduces the immediate impact of packetloss on new entities.
//reflagged state includes stats updates, entity updates, and entity removes.
int sequence; //to see if its stale
float timestamp;
unsigned int resendstats[MAX_CL_STATS/32];
struct
{
unsigned int num;
unsigned int bits;
} *ents;
int numents; //doesn't contain an entry for every entity, just ones that were sent this frame. no 0 bits
int maxents;
} *frames;
size_t numframes; //preallocated power-of-two
int lastacksequence;
int lastmovemessage;
client_voip_t voip; //spike -- for voip
struct
{
char name[MAX_QPATH];
FILE *file;
qboolean started; //actually sending
unsigned int startpos; //within the pak, so we don't break stuff when seeking
unsigned int size;
unsigned int sendpos; //file offset we last tried sending
unsigned int ackpos; //if they don't ack this properly, we restart sending from here instead.
//for more speed, the server should build a collection of blocks to track which parts were actually acked, thereby avoiding redundant resends, but in the intererest of simplicity...
} download;
qboolean knowntoqc; // putclientinserver was called
} client_t;
@ -128,6 +206,8 @@ typedef struct client_s
#define MOVETYPE_NOCLIP 8
#define MOVETYPE_FLYMISSILE 9 // extra size to monsters
#define MOVETYPE_BOUNCE 10
//#define MOVETYPE_EXT_BOUNCEMISSILE 11
#define MOVETYPE_EXT_FOLLOW 12
// edict->solid values
#define SOLID_NOT 0 // no interaction with other objects
@ -135,6 +215,7 @@ typedef struct client_s
#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
#define SOLID_EXT_CORPSE 5 // passes through slidebox+other corpses, but not bsp/bbox/triggers
// edict->deadflag values
#define DEAD_NO 0
@ -161,13 +242,6 @@ typedef struct client_s
#define FL_WATERJUMP 2048 // player jumping out of water
#define FL_JUMPRELEASED 4096 // for jump debouncing
// 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
@ -194,11 +268,14 @@ extern edict_t *sv_player;
void SV_Init (void);
void SV_StartParticle (vec3_t org, vec3_t dir, int color, int count);
void SV_StartSound (edict_t *entity, int channel, const char *sample, int volume,
float attenuation);
void SV_StartSound (edict_t *entity, float *origin, int channel,
const char *sample, int volume, float attenuation);
void SV_DropClient (qboolean crash);
void SVFTE_Ack(client_t *client, int sequence);
void SVFTE_DestroyFrames(client_t *client);
void SV_BuildEntityState(edict_t *ent, entity_state_t *state);
void SV_SendClientMessages (void);
void SV_ClearDatagram (void);
@ -219,10 +296,11 @@ void SV_Physics (void);
qboolean SV_CheckBottom (edict_t *ent);
qboolean SV_movestep (edict_t *ent, vec3_t move, qboolean relink);
void SV_WriteClientdataToMessage (edict_t *ent, sizebuf_t *msg);
void SV_WriteClientdataToMessage (client_t *client, sizebuf_t *msg);
void SV_MoveToGoal (void);
void SV_ConnectClient (int clientnum); //called from the netcode to add new clients. also called from pr_ext to spawn new botclients.
void SV_CheckForNewClients (void);
void SV_RunClients (void);
void SV_SaveSpawnparms ();

View File

@ -45,7 +45,7 @@ static snd_codec_t *codecs;
S_CodecRegister
=================
*/
static void S_CodecRegister(snd_codec_t *codec)
void S_CodecRegister(snd_codec_t *codec)
{
codec->next = codecs;
codecs = codec;

View File

@ -24,6 +24,18 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
// snd_dma.c -- main control for any streaming sound output device
/* FIXME -- spike
** with SDL, the SDL api provides a callback that is called whenever SDL thinks more audio is needed
** if we were to move our mixing into the callback instead, we would obsolete _snd_mixahead and reduce audio latency as a result (instead of having to pre-mix audio just in case).
** this callback can typically also be assumed to be on another thread, so mixing audio there would result in a drop in cpu usage on the main thread, increasing framerates.
** typically quake's audio mixer isn't that expensive, but when you have maps with 1000 static sounds with 8-channel surround sound, things can start to get pricy.
**
** S_Update_ would become a stub, and we'd need to call SDL_LockAudio to block the callback from happening any time we change an audio channel.
** snd_mix.c would also need to be threadsafe with regard to the rest of the code
**
** alternatively, sdl2 provides a different audio api that more closely matches what we currently do, where we would directly submit audio (snd_mixahead would again be obsolete).
*/
#include "quakedef.h"
#include "snd_codec.h"
#include "bgmusic.h"
@ -39,8 +51,9 @@ static void S_StopAllSoundsC (void);
// Internal sound data & structures
// =======================================================================
channel_t snd_channels[MAX_CHANNELS];
channel_t *snd_channels;
int total_channels;
int max_channels;
static int snd_blocked = 0;
static qboolean snd_initialized = false;
@ -52,6 +65,7 @@ vec3_t listener_origin;
vec3_t listener_forward;
vec3_t listener_right;
vec3_t listener_up;
float voicevolumescale = 1; //for audio ducking while speaking
#define sound_nominal_clip_dist 1000.0
@ -183,6 +197,8 @@ void S_Init (void)
Cvar_RegisterVariable(&snd_mixspeed);
Cvar_RegisterVariable(&snd_filterquality);
S_Voip_Init();
if (safemode || COM_CheckParm("-nosound"))
return;
@ -401,11 +417,17 @@ void SND_Spatialize (channel_t *ch)
vec_t lscale, rscale, scale;
vec3_t source_vec;
if (ch->entchannel == -2)
{
ch->leftvol = ch->master_vol; //voip comes out full volume
ch->rightvol = ch->master_vol;
return;
}
// anything coming from the view entity will always be full volume
if (ch->entnum == cl.viewentity)
{
ch->leftvol = ch->master_vol;
ch->rightvol = ch->master_vol;
ch->leftvol = ch->master_vol * voicevolumescale;
ch->rightvol = ch->master_vol * voicevolumescale;
return;
}
@ -427,12 +449,12 @@ void SND_Spatialize (channel_t *ch)
// add in distance effect
scale = (1.0 - dist) * rscale;
ch->rightvol = (int) (ch->master_vol * scale);
ch->rightvol = (int) (ch->master_vol * scale * voicevolumescale);
if (ch->rightvol < 0)
ch->rightvol = 0;
scale = (1.0 - dist) * lscale;
ch->leftvol = (int) (ch->master_vol * scale);
ch->leftvol = (int) (ch->master_vol * scale * voicevolumescale);
if (ch->leftvol < 0)
ch->leftvol = 0;
}
@ -532,20 +554,17 @@ void S_StopSound (int entnum, int entchannel)
void S_StopAllSounds (qboolean clear)
{
int i;
if (!sound_started)
return;
total_channels = MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS; // no statics
for (i = 0; i < MAX_CHANNELS; i++)
{
if (snd_channels[i].sfx)
snd_channels[i].sfx = NULL;
if (max_channels != total_channels + 64)
{ //shrink it if needed
max_channels = total_channels + 64;
free(snd_channels);
snd_channels = malloc(sizeof(channel_t) * max_channels);
}
memset(snd_channels, 0, MAX_CHANNELS * sizeof(channel_t));
memset(snd_channels, 0, max_channels * sizeof(channel_t));
if (clear)
S_ClearBuffer ();
@ -593,11 +612,19 @@ void S_StaticSound (sfx_t *sfx, vec3_t origin, float vol, float attenuation)
if (!sfx)
return;
if (total_channels == MAX_CHANNELS)
if (total_channels == max_channels)
{
Con_Printf ("total_channels == MAX_CHANNELS\n");
int nm = max_channels+64;
ss = realloc(snd_channels, sizeof(*ss)*nm);
if (!ss)
{
Con_Printf ("unable to increase max_channels\n");
return;
}
snd_channels = ss;
memset(snd_channels+max_channels, 0, sizeof(*ss)*(nm-max_channels));
max_channels = nm;
}
ss = &snd_channels[total_channels];
total_channels++;
@ -632,14 +659,15 @@ S_UpdateAmbientSounds
static void S_UpdateAmbientSounds (void)
{
mleaf_t *l;
int vol, ambient_channel;
int ambient_channel;
channel_t *chan;
static float vol, levels[NUM_AMBIENTS]; //Spike: fixing ambient levels not changing at high enough framerates due to integer precison.
// no ambients when disconnected
if (cls.state != ca_connected)
if (cls.state != ca_connected || cls.signon != SIGNONS)
return;
// calc ambient sound levels
if (!cl.worldmodel)
if (!cl.worldmodel || cl.worldmodel->needload)
return;
l = Mod_PointInLeaf (listener_origin, cl.worldmodel);
@ -660,20 +688,20 @@ static void S_UpdateAmbientSounds (void)
vol = 0;
// don't adjust volume too fast
if (chan->master_vol < vol)
if (levels[ambient_channel] < vol)
{
chan->master_vol += (int) (host_frametime * ambient_fade.value);
if (chan->master_vol > vol)
chan->master_vol = vol;
levels[ambient_channel] += (host_frametime * ambient_fade.value);
if (levels[ambient_channel] > vol)
levels[ambient_channel] = vol;
}
else if (chan->master_vol > vol)
{
chan->master_vol -= (int) (host_frametime * ambient_fade.value);
if (chan->master_vol < vol)
chan->master_vol = vol;
levels[ambient_channel] -= (host_frametime * ambient_fade.value);
if (levels[ambient_channel] < vol)
levels[ambient_channel] = vol;
}
chan->leftvol = chan->rightvol = chan->master_vol;
chan->leftvol = chan->rightvol = chan->master_vol = levels[ambient_channel];
}
}

View File

@ -52,8 +52,30 @@ static void ResampleSfx (sfx_t *sfx, int inrate, int inwidth, byte *data)
sc->width = 1;
else
sc->width = inwidth;
if (sc->stereo == 1)
{ //crappy approach to stereo - strip it out by merging left+right channels
sc->stereo = 0;
samplefrac = 0;
fracstep = stepscale*256;
for (i = 0; i < outcount; i++)
{
srcsample = samplefrac >> 8;
srcsample<<=1;
samplefrac += fracstep;
if (inwidth == 2)
sample = LittleShort ( ((short *)data)[srcsample] ) + LittleShort ( ((short *)data)[srcsample+1] );
else
sample = ((int)( (unsigned char)(data[srcsample]) - 128) << 8) + ((int)( (unsigned char)(data[srcsample+1]) - 128) << 8);
sample /= 2;
if (sc->width == 2)
((short *)sc->data)[i] = sample;
else
((signed char *)sc->data)[i] = sample >> 8;
}
}
else
{
// resample / decimate to the current source rate
if (stepscale == 1 && inwidth == 1 && sc->width == 1)
@ -82,6 +104,7 @@ static void ResampleSfx (sfx_t *sfx, int inrate, int inwidth, byte *data)
}
}
}
}
//=============================================================================
@ -122,7 +145,7 @@ sfxcache_t *S_LoadSound (sfx_t *s)
}
info = GetWavinfo (s->name, data, com_filesize);
if (info.channels != 1)
if (info.channels != 1 && info.channels != 2)
{
Con_Printf ("%s is a stereo sample\n",s->name);
return NULL;
@ -137,7 +160,7 @@ sfxcache_t *S_LoadSound (sfx_t *s)
stepscale = (float)info.rate / shm->speed;
len = info.samples / stepscale;
len = len * info.width * info.channels;
len = len * info.width;// * info.channels;
if (info.samples == 0 || len == 0)
{
@ -149,11 +172,11 @@ sfxcache_t *S_LoadSound (sfx_t *s)
if (!sc)
return NULL;
sc->length = info.samples;
sc->length = info.samples / info.channels;
sc->loopstart = info.loopstart;
sc->speed = info.rate;
sc->width = info.width;
sc->stereo = info.channels;
sc->stereo = info.channels-1;
ResampleSfx (s, sc->speed, sc->width, data + info.dataofs);

View File

@ -244,6 +244,8 @@ static void S_ApplyFilter(filter_t *filter, int *data, int stride, int count)
int parity;
input = (float *) malloc(sizeof(float) * (filter->kernelsize + count));
if (!input)
return;
// set up the input buffer
// memory holds the previous filter->kernelsize samples of input.

2866
Quake/snd_voip.c Executable file

File diff suppressed because it is too large Load Diff

41
Quake/snd_voip.h Executable file
View File

@ -0,0 +1,41 @@
//spike -- this file contains prototypes+etc for voice chat.
//it should be fairly straight forward to integrate this into other engines, however, to implement it properly you'll need to deal with the whole pext2_voicechat handshake thing.
//for quakespasm-spiked this is already handled for entity deltas etc.
//you'll also need to figure out something with the 4 clientcommands that servers might receive.
//to test, cl_voip_test 1;sv_voip_echo 0;+voip should start playing even without any protocol extensions. then move on to cl_voip_test 0;sv_voip_echo 1;+voip once you have protocol stuff working.
//you'll also want to add the various voip settings to the menu, especially cl_voip_play (slider 0-1), cl_voip_send (boolean), +voip binding.
//defined elsewhere
//#define svcfte_voicechat 84
//#define clcfte_voicechat 83
struct client_s;
//client functions
void S_Voip_Transmit(unsigned char clc, sizebuf_t *buf); //call from CL_SendMove (null buf if not connecting, grabs new data, encodes, and writes into the buffer)
void S_Voip_MapChange(void); //call from end of CL_ParseServerinfo (tells server to reenable voice chat)
void S_Voip_Parse(void); //call from CL_ParseServerMessage+svcfte_voicechat. processes voip data from the server
int S_Voip_Loudness(qboolean ignorevad); //for sbar stuff, if you want to draw some mic-level bar (returns 0-100, or -1 for not transmitting)
qboolean S_Voip_Speaking(unsigned int plno); //for sbar stuff, if you want to query which other players are speaking (add a scoreboard back-colour or something).
void S_Voip_Init(void); //call from S_Init, registers client cvars+commands
//server functions
void SV_VoiceInit(void); //call from SV_Init, registers server cvars+commands
void SV_VoiceInitClient(struct client_s *client); //call from start of SV_SendServerinfo, disables voice chat until the client is ready to re-enable it
void SV_VoiceSendPacket(struct client_s *client, sizebuf_t *buf); //call from near end of SV_SendClientDatagram, to forward voice data to other clients
void SV_VoiceReadPacket(struct client_s *client); //call from SV_ReadClientMessage+clcfte_voicechat. processes voip data from clients and figures out which clients to forward to
typedef struct
{
unsigned int read; /*place in ring*/
unsigned char mute[MAX_SCOREBOARD/8]; /*which other clients should be muted for this player, reducing bandwidth from annoying cunts*/
qboolean active; /*client wants to hear other people*/
enum
{
/*should we add one to respond to the last speaker? or should that be an automagic +voip_reply instead?*/
VT_TEAM,
VT_ALL,
VT_NONMUTED, /*cheap, but allows custom private channels with no external pesters*/
VT_PLAYERSLOT0
/*player0+...*/
} target;
} client_voip_t; //embedded within struct client_s as a member named voip

7102
Quake/stb_image.h Executable file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -47,6 +47,7 @@ cvar_t sv_gravity = {"sv_gravity","800",CVAR_NOTIFY|CVAR_SERVERINFO};
cvar_t sv_maxvelocity = {"sv_maxvelocity","2000",CVAR_NONE};
cvar_t sv_nostep = {"sv_nostep","0",CVAR_NONE};
cvar_t sv_freezenonclients = {"sv_freezenonclients","0",CVAR_NONE};
cvar_t sv_gameplayfix_spawnbeforethinks = {"sv_gameplayfix_spawnbeforethinks","0",CVAR_NONE};
#define MOVE_EPSILON 0.01
@ -388,7 +389,7 @@ void SV_AddGravity (edict_t *ent)
float ent_gravity;
eval_t *val;
val = GetEdictFieldValue(ent, "gravity");
val = GetEdictFieldValue(ent, pr_extfields.gravity);
if (val && val->_float)
ent_gravity = val->_float;
else
@ -453,6 +454,7 @@ void SV_PushMove (edict_t *pusher, float movetime)
edict_t **moved_edict; //johnfitz -- dynamically allocate
vec3_t *moved_from; //johnfitz -- dynamically allocate
int mark; //johnfitz
float solid_backup;
if (!pusher->v.velocity[0] && !pusher->v.velocity[1] && !pusher->v.velocity[2])
{
@ -519,13 +521,23 @@ void SV_PushMove (edict_t *pusher, float movetime)
moved_edict[num_moved] = check;
num_moved++;
//QIP fix for end.bsp
solid_backup = pusher->v.solid;
if ( solid_backup == SOLID_BSP // everything that blocks: bsp models = map brushes = doors, plats, etc.
|| solid_backup == SOLID_BBOX // normally boxes
|| solid_backup == SOLID_SLIDEBOX ) // normally monsters
{
// try moving the contacted entity
pusher->v.solid = SOLID_NOT;
SV_PushEntity (check, move);
pusher->v.solid = SOLID_BSP;
pusher->v.solid = solid_backup;
// if it is still inside the pusher, block
block = SV_TestEntityPosition (check);
}
else
block = NULL;
if (block)
{ // fail the move
if (check->v.mins[0] == check->v.maxs[0])
@ -907,6 +919,9 @@ void SV_Physics_Client (edict_t *ent, int num)
if ( ! svs.clients[num-1].active )
return; // unconnected slot
if (!svs.clients[num-1].knowntoqc && sv_gameplayfix_spawnbeforethinks.value)
return; //don't spam prethinks before we called putclientinserver.
//
// call standard client pre-think
//
@ -956,7 +971,7 @@ void SV_Physics_Client (edict_t *ent, int num)
break;
default:
Sys_Error ("SV_Physics_client: bad movetype %i", (int)ent->v.movetype);
Host_EndGame ("SV_Physics_client: bad movetype %i", (int)ent->v.movetype);
}
//
@ -1034,7 +1049,7 @@ void SV_CheckWaterTransition (edict_t *ent)
{
if (ent->v.watertype == CONTENTS_EMPTY)
{ // just crossed into water
SV_StartSound (ent, 0, "misc/h2ohit1.wav", 255, 1);
SV_StartSound (ent, NULL, 0, "misc/h2ohit1.wav", 255, 1);
}
ent->v.watertype = cont;
ent->v.waterlevel = 1;
@ -1043,7 +1058,7 @@ void SV_CheckWaterTransition (edict_t *ent)
{
if (ent->v.watertype != CONTENTS_EMPTY)
{ // just crossed into water
SV_StartSound (ent, 0, "misc/h2ohit1.wav", 255, 1);
SV_StartSound (ent, NULL, 0, "misc/h2ohit1.wav", 255, 1);
}
ent->v.watertype = CONTENTS_EMPTY;
ent->v.waterlevel = cont;
@ -1112,6 +1127,55 @@ void SV_Physics_Toss (edict_t *ent)
SV_CheckWaterTransition (ent);
}
/*
=============
SV_Physics_Follow
Entities that are "stuck" to another entity
=============
*/
static void SV_Physics_Follow (edict_t *ent)
{
vec3_t vf, vr, vu, angles, v;
edict_t *e;
// regular thinking
if (!SV_RunThink (ent))
return;
// LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
e = PROG_TO_EDICT(ent->v.aiment);
if (e->v.angles[0] == ent->v.punchangle[0] && e->v.angles[1] == ent->v.punchangle[1] && e->v.angles[2] == ent->v.punchangle[2])
{
// quick case for no rotation
VectorAdd(e->v.origin, ent->v.view_ofs, ent->v.origin);
}
else
{
angles[0] = -ent->v.punchangle[0];
angles[1] = ent->v.punchangle[1];
angles[2] = ent->v.punchangle[2];
AngleVectors (angles, vf, vr, vu);
v[0] = ent->v.view_ofs[0] * vf[0] + ent->v.view_ofs[1] * vr[0] + ent->v.view_ofs[2] * vu[0];
v[1] = ent->v.view_ofs[0] * vf[1] + ent->v.view_ofs[1] * vr[1] + ent->v.view_ofs[2] * vu[1];
v[2] = ent->v.view_ofs[0] * vf[2] + ent->v.view_ofs[1] * vr[2] + ent->v.view_ofs[2] * vu[2];
angles[0] = -e->v.angles[0];
angles[1] = e->v.angles[1];
angles[2] = e->v.angles[2];
AngleVectors (angles, vf, vr, vu);
ent->v.origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->v.origin[0];
ent->v.origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->v.origin[1];
ent->v.origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->v.origin[2];
}
VectorAdd (e->v.angles, ent->v.v_angle, ent->v.angles);
SV_LinkEdict (ent, true);
}
/*
===============================================================================
@ -1151,7 +1215,7 @@ void SV_Physics_Step (edict_t *ent)
if ( (int)ent->v.flags & FL_ONGROUND ) // just hit ground
{
if (hitsound)
SV_StartSound (ent, 0, "demon/dland2.wav", 255, 1);
SV_StartSound (ent, NULL, 0, "demon/dland2.wav", 255, 1);
}
}
@ -1220,13 +1284,34 @@ void SV_Physics (void)
|| ent->v.movetype == MOVETYPE_FLY
|| ent->v.movetype == MOVETYPE_FLYMISSILE)
SV_Physics_Toss (ent);
else if (ent->v.movetype == MOVETYPE_EXT_FOLLOW)
SV_Physics_Follow (ent);
else if (ent->v.movetype == MOVETYPE_WALK)
{
if (SV_RunThink (ent))
{
if (!SV_CheckWater (ent) && ! ((int)ent->v.flags & FL_WATERJUMP) )
SV_AddGravity (ent);
SV_CheckStuck (ent);
SV_WalkMove (ent);
}
}
else
Sys_Error ("SV_Physics: bad movetype %i", (int)ent->v.movetype);
Host_EndGame ("SV_Physics: bad movetype %i", (int)ent->v.movetype);
}
if (pr_global_struct->force_retouch)
pr_global_struct->force_retouch--;
if (pr_extfuncs.endframe)
{
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_extfuncs.endframe);
}
if (!sv_freezenonclients.value)
sv.time += host_frametime;
}

View File

@ -439,37 +439,92 @@ void SV_ReadClientMove (usercmd_t *move)
{
int i;
vec3_t angle;
int bits;
int buttonbits;
int newimpulse;
eval_t *eval;
qboolean drop = false;
float timestamp;
vec3_t movevalues;
int sequence;
eval_t *val;
// read ping time
host_client->ping_times[host_client->num_pings%NUM_PING_TIMES]
= sv.time - MSG_ReadFloat ();
host_client->num_pings++;
if (host_client->protocol_pext2 & PEXT2_PREDINFO)
{
i = (unsigned short)MSG_ReadShort();
sequence = (host_client->lastmovemessage & 0xffff0000) | (i&0xffff);
// read current angles
//tollerance of a few old frames, so we can have redundancy for packetloss
if (sequence+0x100 < host_client->lastmovemessage)
sequence += 0x10000;
if (sequence <= host_client->lastmovemessage)
drop = true;
}
else
sequence = 0;
//read the data
timestamp = MSG_ReadFloat();
for (i=0 ; i<3 ; i++)
//johnfitz -- 16-bit angles for PROTOCOL_FITZQUAKE
if (sv.protocol == PROTOCOL_NETQUAKE)
{
if (sv.protocol == PROTOCOL_NETQUAKE && !(host_client->protocol_pext2 & PEXT2_PREDINFO) && !NET_QSocketGetProQuakeAngleHack(host_client->netconnection))
angle[i] = MSG_ReadAngle (sv.protocolflags);
else
angle[i] = MSG_ReadAngle16 (sv.protocolflags);
//johnfitz
angle[i] = MSG_ReadAngle16 (sv.protocolflags); //johnfitz -- 16-bit angles for PROTOCOL_FITZQUAKE
}
movevalues[0] = MSG_ReadShort ();
movevalues[1] = MSG_ReadShort ();
movevalues[2] = MSG_ReadShort ();
buttonbits = MSG_ReadByte();
newimpulse = MSG_ReadByte();
VectorCopy (angle, host_client->edict->v.v_angle);
if (drop)
return; //okay, we don't care about that then
// calc ping times
host_client->lastmovemessage = sequence;
if (!(host_client->protocol_pext2 & PEXT2_PREDINFO))
{
host_client->ping_times[host_client->num_pings%NUM_PING_TIMES]
= sv.time - timestamp;
host_client->num_pings++;
} //otherwise time is still useful for determining the input frame's time value
// read movement
move->forwardmove = MSG_ReadShort ();
move->sidemove = MSG_ReadShort ();
move->upmove = MSG_ReadShort ();
VectorCopy (angle, host_client->edict->v.v_angle);
move->forwardmove = movevalues[0];
move->sidemove = movevalues[1];
move->upmove = movevalues[2];
// read buttons
bits = MSG_ReadByte ();
host_client->edict->v.button0 = bits & 1;
host_client->edict->v.button2 = (bits & 2)>>1;
host_client->edict->v.button0 = (buttonbits & 1)>>0;
//button1 was meant to be 'use', but got reused by too many mods to get implemented now
host_client->edict->v.button2 = (buttonbits & 2)>>1;
if ((val = GetEdictFieldValue(host_client->edict, pr_extfields.button3)))
val->_float = (buttonbits & 4)>>2;
if ((val = GetEdictFieldValue(host_client->edict, pr_extfields.button4)))
val->_float = (buttonbits & 8)>>3;
if ((val = GetEdictFieldValue(host_client->edict, pr_extfields.button5)))
val->_float = (buttonbits & 0x10)>>4;
if ((val = GetEdictFieldValue(host_client->edict, pr_extfields.button6)))
val->_float = (buttonbits & 0x20)>>5;
if ((val = GetEdictFieldValue(host_client->edict, pr_extfields.button7)))
val->_float = (buttonbits & 0x40)>>6;
if ((val = GetEdictFieldValue(host_client->edict, pr_extfields.button8)))
val->_float = (buttonbits & 0x80)>>7;
i = MSG_ReadByte ();
if (i)
host_client->edict->v.impulse = i;
if (newimpulse)
host_client->edict->v.impulse = newimpulse;
eval = GetEdictFieldValue(host_client->edict, pr_extfields.movement);
if (eval)
{
eval->vector[0] = move->forwardmove;
eval->vector[1] = move->sidemove;
eval->vector[2] = move->upmove;
}
//FIXME: attempt to apply physics command now, if the mod has custom physics+csqc-prediction
}
/*
@ -481,22 +536,9 @@ Returns false if the client should be killed
*/
qboolean SV_ReadClientMessage (void)
{
int ret;
int ccmd;
const char *s;
do
{
nextmsg:
ret = NET_GetMessage (host_client->netconnection);
if (ret == -1)
{
Sys_Printf ("SV_ReadClientMessage: NET_GetMessage failed\n");
return false;
}
if (!ret)
return true;
MSG_BeginReading ();
while (1)
@ -515,7 +557,7 @@ nextmsg:
switch (ccmd)
{
case -1:
goto nextmsg; // end of message
return true; //msg_badread, meaning we just hit eof.
default:
Sys_Printf ("SV_ReadClientMessage: unknown command char\n");
@ -527,52 +569,18 @@ nextmsg:
case clc_stringcmd:
s = MSG_ReadString ();
ret = 0;
if (q_strncasecmp(s, "status", 6) == 0)
ret = 1;
else if (q_strncasecmp(s, "god", 3) == 0)
ret = 1;
else if (q_strncasecmp(s, "notarget", 8) == 0)
ret = 1;
else if (q_strncasecmp(s, "fly", 3) == 0)
ret = 1;
else if (q_strncasecmp(s, "name", 4) == 0)
ret = 1;
else if (q_strncasecmp(s, "noclip", 6) == 0)
ret = 1;
else if (q_strncasecmp(s, "setpos", 6) == 0)
ret = 1;
else if (q_strncasecmp(s, "say", 3) == 0)
ret = 1;
else if (q_strncasecmp(s, "say_team", 8) == 0)
ret = 1;
else if (q_strncasecmp(s, "tell", 4) == 0)
ret = 1;
else if (q_strncasecmp(s, "color", 5) == 0)
ret = 1;
else if (q_strncasecmp(s, "kill", 4) == 0)
ret = 1;
else if (q_strncasecmp(s, "pause", 5) == 0)
ret = 1;
else if (q_strncasecmp(s, "spawn", 5) == 0)
ret = 1;
else if (q_strncasecmp(s, "begin", 5) == 0)
ret = 1;
else if (q_strncasecmp(s, "prespawn", 8) == 0)
ret = 1;
else if (q_strncasecmp(s, "kick", 4) == 0)
ret = 1;
else if (q_strncasecmp(s, "ping", 4) == 0)
ret = 1;
else if (q_strncasecmp(s, "give", 4) == 0)
ret = 1;
else if (q_strncasecmp(s, "ban", 3) == 0)
ret = 1;
if (ret == 1)
Cmd_ExecuteString (s, src_client);
if (q_strncasecmp(s, "spawn", 5) && q_strncasecmp(s, "begin", 5) && q_strncasecmp(s, "prespawn", 8) && pr_extfuncs.parseclientcommand)
{ //the spawn/begin/prespawn are because of numerous mods that disobey the rules.
//at a minimum, we must be able to join the server, so that we can see any sprints/bprints (because dprint sucks, yes there's proper ways to deal with this, but moders don't always know them).
client_t *ohc = host_client;
G_INT(OFS_PARM0) = PR_SetEngineString(s);
pr_global_struct->time = sv.time;
pr_global_struct->self = EDICT_TO_PROG(host_client->edict);
PR_ExecuteProgram(pr_extfuncs.parseclientcommand);
host_client = ohc;
}
else
Con_DPrintf("%s tried to %s\n", host_client->name, s);
Cmd_ExecuteString (s, src_client);
break;
case clc_disconnect:
@ -580,11 +588,24 @@ nextmsg:
return false;
case clc_move:
if (!host_client->spawned)
return true; //this is to suck up any stale moves on map changes, so we don't get confused (quite so easily) when protocols are changed between maps
SV_ReadClientMove (&host_client->cmd);
break;
case clcdp_ackframe:
SVFTE_Ack(host_client, MSG_ReadLong());
break;
case clcdp_ackdownloaddata:
Host_DownloadAck(host_client);
break;
case clcfte_voicechat:
SV_VoiceReadPacket(host_client);
break;
}
}
} while (ret == 1);
return true;
}
@ -599,6 +620,31 @@ void SV_RunClients (void)
{
int i;
//receive from clients first
//Spike -- reworked this to query the network code for an active connection.
//this allows the network code to serve multiple clients with the same listening port.
//this solves server-side nats, which is important for coop etc.
while(1)
{
struct qsocket_s *sock = NET_GetServerMessage();
if (!sock)
break; //no more this frame
for (i=0, host_client = svs.clients ; i<svs.maxclients ; i++, host_client++)
{
if (host_client->netconnection == sock)
{
sv_player = host_client->edict;
if (!SV_ReadClientMessage ())
{
SV_DropClient (false); // client misbehaved...
break;
}
}
}
}
//then do the per-frame stuff
for (i=0, host_client = svs.clients ; i<svs.maxclients ; i++, host_client++)
{
if (!host_client->active)
@ -606,12 +652,6 @@ void SV_RunClients (void)
sv_player = host_client->edict;
if (!SV_ReadClientMessage ())
{
SV_DropClient (false); // client misbehaved...
continue;
}
if (!host_client->spawned)
{
// clear client movement until a new packet is received
@ -619,6 +659,23 @@ void SV_RunClients (void)
continue;
}
if (!host_client->netconnection)
{
//botclients can't receive packets. don't even try.
//not sure where to put this code, but here seems sane enough.
//fill in the user's desired stuff according to a few things.
eval_t *ev = GetEdictFieldValue(host_client->edict, pr_extfields.movement);
if (ev) //.movement normally works the other way around. oh well.
{
host_client->cmd.forwardmove = ev->vector[0];
host_client->cmd.sidemove = ev->vector[1];
host_client->cmd.upmove = ev->vector[2];
}
host_client->cmd.viewangles[0] = host_client->edict->v.v_angle[0];
host_client->cmd.viewangles[1] = host_client->edict->v.v_angle[1];
host_client->cmd.viewangles[2] = host_client->edict->v.v_angle[2];
}
// always pause in single player if in console or menus
if (!sv.paused && (svs.maxclients > 1 || key_dest == key_game) )
SV_ClientThink ();

View File

@ -33,8 +33,9 @@ void Sys_Init (void);
// returns the file size or -1 if file is not present.
// the file should be in BINARY mode for stupid OSs that care
int Sys_FileOpenRead (const char *path, int *hndl);
int Sys_FileOpenWrite (const char *path);
int Sys_FileOpenStdio (FILE *file);
void Sys_FileClose (int handle);
void Sys_FileSeek (int handle, int position);
int Sys_FileRead (int handle, void *dest, int count);

View File

@ -119,6 +119,14 @@ int Sys_FileOpenWrite (const char *path)
return i;
}
int Sys_FileOpenStdio (FILE *file)
{
int i;
i = findhandle ();
sys_handles[i] = file;
return i;
}
void Sys_FileClose (int handle)
{
fclose (sys_handles[handle]);
@ -382,6 +390,7 @@ void Sys_Error (const char *error, ...)
va_end (argptr);
fputs (errortxt1, stderr);
Con_Redirect(NULL);
Host_Shutdown ();
fputs (errortxt2, stderr);
fputs (text, stderr);

View File

@ -118,6 +118,14 @@ int Sys_FileOpenWrite (const char *path)
return i;
}
int Sys_FileOpenStdio (FILE *file)
{
int i;
i = findhandle ();
sys_handles[i] = file;
return i;
}
void Sys_FileClose (int handle)
{
fclose (sys_handles[handle]);
@ -304,6 +312,8 @@ void Sys_Error (const char *error, ...)
q_vsnprintf (text, sizeof(text), error, argptr);
va_end (argptr);
Con_Redirect(NULL);
if (isDedicated)
WriteFile (houtput, errortxt1, strlen(errortxt1), &dummy, NULL);
/* SDL will put these into its own stderr log,
@ -338,6 +348,13 @@ void Sys_Printf (const char *fmt, ...)
if (isDedicated)
{
if (*text == 1 || *text == 2)
{ //mostly for Con_[D]Warning
SetConsoleTextAttribute(houtput, FOREGROUND_RED);
WriteFile(houtput, text+1, strlen(text+1), &dummy, NULL);
SetConsoleTextAttribute(houtput, FOREGROUND_BLUE|FOREGROUND_GREEN|FOREGROUND_RED);
}
else
WriteFile(houtput, text, strlen(text), &dummy, NULL);
}
else

View File

@ -23,6 +23,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "quakedef.h"
extern qboolean premul_hud;
/*
The view is allowed to move slightly from it's true position for bobbing,
@ -208,7 +209,7 @@ void V_DriftPitch (void)
return;
}
delta = cl.idealpitch - cl.viewangles[PITCH];
delta = cl.statsf[STAT_IDEALPITCH] - cl.viewangles[PITCH];
if (!delta)
{
@ -312,7 +313,7 @@ void V_ParseDamage (void)
//
// calculate view angle kicks
//
ent = &cl_entities[cl.viewentity];
ent = &cl.entities[cl.viewentity];
VectorSubtract (from, ent->origin, from);
VectorNormalize (from);
@ -351,12 +352,22 @@ When you run over an item, the server sends this command
==================
*/
void V_BonusFlash_f (void)
{
if (Cmd_Argc() >= 5)
{
cl.cshifts[CSHIFT_BONUS].destcolor[0] = atof(Cmd_Argv(1))*255;
cl.cshifts[CSHIFT_BONUS].destcolor[1] = atof(Cmd_Argv(2))*255;
cl.cshifts[CSHIFT_BONUS].destcolor[2] = atof(Cmd_Argv(3))*255;
cl.cshifts[CSHIFT_BONUS].percent = atof(Cmd_Argv(4))*255;
}
else
{
cl.cshifts[CSHIFT_BONUS].destcolor[0] = 215;
cl.cshifts[CSHIFT_BONUS].destcolor[1] = 186;
cl.cshifts[CSHIFT_BONUS].destcolor[2] = 69;
cl.cshifts[CSHIFT_BONUS].percent = 50;
}
}
/*
=============
@ -533,10 +544,13 @@ void V_PolyBlend (void)
GL_DisableMultitexture();
glDisable (GL_ALPHA_TEST);
glDisable (GL_TEXTURE_2D);
glDisable (GL_DEPTH_TEST);
glEnable (GL_BLEND);
if (premul_hud)
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
else
glDisable (GL_ALPHA_TEST);
glMatrixMode(GL_PROJECTION);
glLoadIdentity ();
@ -553,11 +567,16 @@ void V_PolyBlend (void)
glVertex2f (0, 1);
glEnd ();
glDisable (GL_BLEND);
glEnable (GL_DEPTH_TEST);
glEnable (GL_TEXTURE_2D);
if (premul_hud)
glBlendFunc (GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
else
{
glDisable (GL_BLEND);
glEnable (GL_ALPHA_TEST);
}
}
/*
==============================================================================
@ -642,7 +661,7 @@ void V_BoundOffsets (void)
{
entity_t *ent;
ent = &cl_entities[cl.viewentity];
ent = &cl.entities[cl.viewentity];
// absolutely bound refresh reletive to entity clipping hull
// so the view can never be inside a solid wall
@ -687,7 +706,7 @@ void V_CalcViewRoll (void)
{
float side;
side = V_CalcRoll (cl_entities[cl.viewentity].angles, cl.velocity);
side = V_CalcRoll (cl.entities[cl.viewentity].angles, cl.velocity);
r_refdef.viewangles[ROLL] += side;
if (v_dmg_time > 0)
@ -716,7 +735,7 @@ void V_CalcIntermissionRefdef (void)
float old;
// ent is the player model (visible when out of body)
ent = &cl_entities[cl.viewentity];
ent = &cl.entities[cl.viewentity];
// view is the weapon model (only visible from inside body)
view = &cl.viewent;
@ -750,7 +769,7 @@ void V_CalcRefdef (void)
V_DriftPitch ();
// ent is the player model (visible when out of body)
ent = &cl_entities[cl.viewentity];
ent = &cl.entities[cl.viewentity];
// view is the weapon model (only visible from inside body)
view = &cl.viewent;
@ -764,7 +783,7 @@ void V_CalcRefdef (void)
// refresh position
VectorCopy (ent->origin, r_refdef.vieworg);
r_refdef.vieworg[2] += cl.viewheight + bob;
r_refdef.vieworg[2] += cl.stats[STAT_VIEWHEIGHT] + bob;
// never let it sit exactly on a node line, because a water plane can
// dissapear when viewed with the eye exactly on it.
@ -796,7 +815,7 @@ void V_CalcRefdef (void)
CalcGunAngle ();
VectorCopy (ent->origin, view->origin);
view->origin[2] += cl.viewheight;
view->origin[2] += cl.stats[STAT_VIEWHEIGHT];
for (i=0 ; i<3 ; i++)
view->origin[i] += forward[i]*bob*0.4;

View File

@ -125,7 +125,6 @@ lumpinfo_t *W_GetLumpinfo (const char *name)
return lump_p;
}
Con_SafePrintf ("W_GetLumpinfo: %s not found\n", name); //johnfitz -- was Sys_Error
return NULL;
}
@ -135,7 +134,11 @@ void *W_GetLumpName (const char *name)
lump = W_GetLumpinfo (name);
if (!lump) return NULL; //johnfitz
if (!lump)
{
Con_SafePrintf ("W_GetLumpName: %s not found\n", name); //johnfitz -- was Sys_Error
return NULL; //johnfitz
}
return (void *)(wad_base + lump->filepos);
}

View File

@ -601,6 +601,9 @@ LINE TESTING IN HULLS
==================
SV_RecursiveHullCheck
Spike -- note that the pointcontents in this function are completely redundant.
This function should instead return the state of the contents of the mid position.
This would avoid all redundant recursion.
==================
*/
qboolean SV_RecursiveHullCheck (hull_t *hull, int num, float p1f, float p2f, vec3_t p1, vec3_t p2, trace_t *trace)
@ -818,6 +821,16 @@ void SV_ClipToLinks ( areanode_t *node, moveclip_t *clip )
if (clip->passedict && clip->passedict->v.size[0] && !touch->v.size[0])
continue; // points never interact
if (pr_checkextension.value)
{
//corpses are nonsolid to slidebox
if (clip->passedict->v.solid == SOLID_SLIDEBOX && touch->v.solid == SOLID_EXT_CORPSE)
continue;
//corpses ignore slidebox or corpses
if (clip->passedict->v.solid == SOLID_EXT_CORPSE && (touch->v.solid == SOLID_SLIDEBOX || touch->v.solid == SOLID_EXT_CORPSE))
continue;
}
// might intersect, so do an exact clip
if (clip->trace.allsolid)
return;

16
Windows/VisualStudio/quakespasm-sdl2.vcproj Normal file → Executable file
View File

@ -373,6 +373,10 @@
RelativePath="..\..\Quake\common.c"
>
</File>
<File
RelativePath="..\..\Quake\fs_zip.c"
>
</File>
<File
RelativePath="..\..\Quake\console.c"
>
@ -501,6 +505,10 @@
RelativePath="..\..\Quake\pr_cmds.c"
>
</File>
<File
RelativePath="..\..\Quake\pr_ext.c"
>
</File>
<File
RelativePath="..\..\Quake\pr_edict.c"
>
@ -521,6 +529,10 @@
RelativePath="..\..\Quake\r_part.c"
>
</File>
<File
RelativePath="..\..\Quake\r_part_fte.c"
>
</File>
<File
RelativePath="..\..\Quake\r_sprite.c"
>
@ -537,6 +549,10 @@
RelativePath="..\..\Quake\snd_codec.c"
>
</File>
<File
RelativePath="..\..\Quake\snd_voip.c"
>
</File>
<File
RelativePath="..\..\Quake\snd_dma.c"
>

16
Windows/VisualStudio/quakespasm.vcproj Normal file → Executable file
View File

@ -633,6 +633,22 @@
RelativePath="..\..\Quake\zone.c"
>
</File>
<File
RelativePath="..\..\Quake\fs_zip.c"
>
</File>
<File
RelativePath="..\..\Quake\snd_voip.c"
>
</File>
<File
RelativePath="..\..\Quake\pr_ext.c"
>
</File>
<File
RelativePath="..\..\Quake\r_part_fte.c"
>
</File>
<File
RelativePath="..\SDL\main\SDL_win32_main.c"
>