10 Jan 2009

This commit is contained in:
g-cont 2009-01-10 00:00:00 +03:00 committed by Alibek Omarov
parent 9a4b42440d
commit df6f03c2ba
33 changed files with 871 additions and 519 deletions

View File

@ -20,7 +20,7 @@
#define CVAR_SET_FLOAT (*g_engfuncs.pfnCvarSetValue)
#define CVAR_GET_FLOAT (*g_engfuncs.pfnGetCvarFloat)
#define CVAR_GET_STRING (*g_engfuncs.pfnGetCvarString)
#define SET_KEYDEST (*g_engfuncs.pfnSetKeyDest)
#define SERVER_COMMAND (*g_engfuncs.pfnServerCmd)
#define CLIENT_COMMAND (*g_engfuncs.pfnClientCmd)
#define GET_PLAYER_INFO (*g_engfuncs.pfnGetPlayerInfo)

View File

@ -82,8 +82,10 @@ void CHud :: VidInit( void )
if( CVAR_GET_FLOAT( "hud_scale" ))
{
m_scrinfo.iWidth = SCREEN_WIDTH;
m_scrinfo.iHeight = SCREEN_HEIGHT;
// virtual screen space 640x480
// see cl_screen.c from Quake3 code for more details
m_scrinfo.iWidth = 640;
m_scrinfo.iHeight = 480;
}
else
{

View File

@ -27,7 +27,6 @@ WEAPON *gpActiveSel; // NULL means off, 1 means just the menu bar, otherwise
WEAPON *gpLastSel; // Last weapon menu selection
client_sprite_t *GetSpriteList( client_sprite_t *pList, const char *psz, int iCount );
WeaponsResource gWR;
int g_weaponselect = 0;
void WeaponsResource :: LoadAllWeaponSprites( void )
{
@ -339,16 +338,14 @@ void CHudAmmo :: Think( void )
if( gHUD.m_iKeyBits & IN_ATTACK )
{
if( gpActiveSel != (WEAPON *)1 )
{
SERVER_COMMAND( gpActiveSel->szName );
g_weaponselect = gpActiveSel->iId;
}
gpLastSel = gpActiveSel;
gpActiveSel = NULL;
gHUD.m_iKeyBits &= ~IN_ATTACK;
CL_PlaySound( "common/wpn_select.wav", 1.0f );
SET_KEYDEST( KEY_GAME );
}
}
@ -396,6 +393,8 @@ void WeaponsResource :: SelectSlot( int iSlot, int fAdvance, int iDirection )
if( !gHUD.m_iWeaponBits ) return;
SET_KEYDEST( KEY_HUDMENU );
WEAPON *p = NULL;
bool fastSwitch = CVAR_GET_FLOAT( "hud_fastswitch" ) != 0;
@ -412,7 +411,6 @@ void WeaponsResource :: SelectSlot( int iSlot, int fAdvance, int iDirection )
if( !p2 )
{ // only one active item in bucket, so change directly to weapon
SERVER_COMMAND( p->szName );
g_weaponselect = p->iId;
return;
}
}
@ -691,6 +689,8 @@ void CHudAmmo::UserCmd_Close( void )
CL_PlaySound( "common/wpn_hudoff.wav", 1.0f );
}
else CLIENT_COMMAND( "escape" );
SET_KEYDEST( KEY_GAME );
}

View File

@ -46,7 +46,7 @@ int CHudStatusIcons::Draw( float flTime )
{
// find starting position to draw from, along right-hand side of screen
int x = 5;
int y = SCREEN_HEIGHT / 2;
int y = ScreenHeight / 2;
// loop through icon list, and draw any valid icons drawing up from the middle of screen
for( int i = 0; i < MAX_ICONSPRITES; i++ )

View File

@ -85,7 +85,7 @@ int CHudMenu :: Draw( float flTime )
}
// center it
int y = (SCREEN_HEIGHT/2) - ((nlc/2)*12) - 40; // make sure it is above the say text
int y = (ScreenHeight/2) - ((nlc/2)*12) - 40; // make sure it is above the say text
int x = 20;
i = 0;

View File

@ -105,12 +105,11 @@ int CHudScoreboard :: Draw( float fTime )
// print the heading line
int ypos = ROW_RANGE_MIN + (list_slot * ROW_GAP);
int xpos = NAME_RANGE_MIN + xpos_rel;
int xpos = NAME_RANGE_MIN + xpos_rel;
if ( !gHUD.m_Teamplay )
if( !gHUD.m_Teamplay )
gHUD.DrawHudString( xpos, ypos, NAME_RANGE_MAX + xpos_rel, "Player", 255, 140, 0 );
else
gHUD.DrawHudString( xpos, ypos, NAME_RANGE_MAX + xpos_rel, "Teams", 255, 140, 0 );
else gHUD.DrawHudString( xpos, ypos, NAME_RANGE_MAX + xpos_rel, "Teams", 255, 140, 0 );
gHUD.DrawHudStringReverse( KILLS_RANGE_MAX + xpos_rel, ypos, 0, "frags", 255, 140, 0 );
gHUD.DrawHudString( DIVIDER_POS + xpos_rel, ypos, ScreenWidth, "/", 255, 140, 0 );

View File

@ -182,7 +182,7 @@ int CHudTextMessage::MsgFunc_TextMsg( const char *pszName, int iSize, void *pbuf
{
case HUD_PRINTCENTER:
sprintf( psz, msg_text, sstr1, sstr2, sstr3, sstr4 );
CenterPrint( ConvertCRtoNL( psz ), SCREEN_HEIGHT / 2, BIGCHAR_WIDTH );
CenterPrint( ConvertCRtoNL( psz ), 240.0f, BIGCHAR_WIDTH );
break;
case HUD_PRINTNOTIFY:
psz[0] = 1; // mark this message to go into the notify buffer

View File

@ -493,6 +493,34 @@ void pfnClientCmd( const char *szCmdString )
Cmd_ExecuteString( szCmdString );
}
/*
=============
pfnSetKeyDest
=============
*/
void pfnSetKeyDest( int key_dest )
{
switch( key_dest )
{
case key_game:
cls.key_dest = key_game;
break;
case key_hudmenu:
cls.key_dest = key_hudmenu;
break;
case key_menu:
cls.key_dest = key_menu;
break;
case key_message:
cls.key_dest = key_message;
break;
default:
MsgDev( D_ERROR, "CL_SetKeyDest: wrong destination %i!\n", key_dest );
break;
}
}
/*
=============
pfnGetPlayerInfo
@ -850,6 +878,7 @@ static cl_enginefuncs_t gEngfuncs =
pfnHookUserMsg,
pfnServerCmd,
pfnClientCmd,
pfnSetKeyDest,
pfnGetPlayerInfo,
pfnTextMessageGet,
pfnCmdArgc,

View File

@ -30,12 +30,11 @@ cvar_t *ui_sensitivity;
cvar_t *m_filter; // mouse filtering
uint frame_msec;
int in_impulse;
int in_cancel;
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_attack, in_attack2;
kbutton_t in_up, in_down, in_reload, in_score;
kbutton_t in_up, in_down, in_reload;
/*
============================================================
@ -262,16 +261,13 @@ void IN_SpeedUp(void) {IN_KeyUp(&in_speed);}
void IN_StrafeDown(void) {IN_KeyDown(&in_strafe);}
void IN_StrafeUp(void) {IN_KeyUp(&in_strafe);}
void IN_AttackDown(void) {IN_KeyDown(&in_attack);}
void IN_AttackUp(void) {IN_KeyUp(&in_attack); in_cancel = 0; }
void IN_AttackUp(void) {IN_KeyUp(&in_attack); }
void IN_Attack2Down(void) {IN_KeyDown(&in_attack2);}
void IN_Attack2Up(void) {IN_KeyUp(&in_attack2);}
void IN_UseDown (void) {IN_KeyDown(&in_use);}
void IN_UseUp (void) {IN_KeyUp(&in_use);}
void IN_ReloadDown(void) {IN_KeyDown(&in_reload);}
void IN_ReloadUp(void) {IN_KeyUp(&in_reload);}
void IN_ScoreDown(void) {IN_KeyDown(&in_score);}
void IN_ScoreUp(void) {IN_KeyUp(&in_score);}
void IN_Cancel(void) {in_cancel = true;}
void IN_Impulse (void) {in_impulse = com.atoi(Cmd_Argv(1));}
void IN_MLookDown( void ){Cvar_SetValue( "cl_mouselook", 1 );}
void IN_MLookUp( void ){ IN_CenterView(); Cvar_SetValue( "cl_mouselook", 0 );}
@ -359,41 +355,63 @@ void CL_ClampPitch (void)
/*
==============
CL_CmdButtons
CL_ButtonBits
==============
*/
void CL_CmdButtons( usercmd_t *cmd )
int CL_ButtonBits( int bResetState )
{
int bits = 0;
if( in_attack.state & 3 )
cmd->buttons |= IN_ATTACK;
in_attack.state &= ~2;
bits |= IN_ATTACK;
if( in_attack2.state & 3 )
cmd->buttons |= IN_ATTACK2;
in_attack2.state &= ~2;
bits |= IN_ATTACK2;
if( in_use.state & 3 )
cmd->buttons |= IN_USE;
in_use.state &= ~2;
bits |= IN_USE;
if (in_speed.state & 3)
cmd->buttons |= IN_RUN;
in_speed.state &= ~2;
bits |= IN_RUN;
if( in_reload.state & 3)
cmd->buttons |= IN_RELOAD;
in_reload.state &= ~2;
bits |= IN_RELOAD;
if( in_cancel )
cmd->buttons |= IN_CANCEL;
if( bResetState )
{
in_attack.state &= ~2;
in_attack2.state &= ~2;
in_use.state &= ~2;
in_speed.state &= ~2;
in_reload.state &= ~2;
}
return bits;
}
if( in_score.state & 3 )
cmd->buttons |= IN_SCORE;
in_score.state &= ~2;
/*
============
CL_ResetButtonBits
// dead or in intermission? Shore scoreboard, too
if( cl.frame.ps.health <= 0 || cl.refdef.intermission )
cmd->buttons |= IN_SCORE;
============
*/
void CL_ResetButtonBits( int bits )
{
int bitsNew = CL_ButtonBits( 0 ) ^ bits;
// has the attack button been changed
if( bitsNew & IN_ATTACK )
{
// was it pressed? or let go?
if( bits & IN_ATTACK )
{
IN_KeyDown( &in_attack );
}
else
{
// totally clear state
in_attack.state &= ~7;
}
}
}
/*
@ -418,19 +436,22 @@ void CL_FinishMove( usercmd_t *cmd )
cmd->impulse = in_impulse;
in_impulse = 0;
// FIXME: send the ambient light level for all! entities properly
// HACKHACK: client lightlevel
cmd->lightlevel = (byte)cl_lightlevel->value;
if( cls.key_dest == key_game )
cmd->buttons = CL_ButtonBits( 1 );
// process commands with user dll's
cl.data.fov = cl.frame.ps.fov;
cl.data.iKeyBits = cmd->buttons;
cl.data.iKeyBits = CL_ButtonBits( 0 );
cl.data.iWeaponBits = cl.frame.ps.weapons;
cl.data.mouse_sensitivity = cl.mouse_sens;
VectorCopy( cl.viewangles, cl.data.angles );
VectorCopy( cl.refdef.origin, cl.data.origin );
cls.dllFuncs.pfnUpdateClientData( &cl.data, ( cl.time * 0.001f ));
cmd->buttons = cl.data.iKeyBits;
CL_ResetButtonBits( cl.data.iKeyBits );
cl.refdef.fov_x = cl.data.fov;
cl.mouse_sens = cl.data.mouse_sensitivity;
}
@ -452,7 +473,6 @@ usercmd_t CL_CreateCmd( void )
// get basic movement from mouse
CL_BaseMove( &cmd );
CL_MouseMove( &cmd );
CL_CmdButtons( &cmd );
CL_FinishMove( &cmd );
host.frametime[1] = host.frametime[0];
@ -603,10 +623,7 @@ void CL_InitInput( void )
Cmd_AddCommand ("-use", IN_UseUp, "stop using item" );
Cmd_AddCommand ("+reload", IN_ReloadDown, "reload current weapon" );
Cmd_AddCommand ("-reload", IN_ReloadUp, "continue reload weapon" );
Cmd_AddCommand ("+showscores", IN_ScoreDown, "show scores" );
Cmd_AddCommand ("-showscores", IN_ScoreUp, "hide scores" );
Cmd_AddCommand ("impulse", IN_Impulse, "send an impulse number to server (select weapon, use item, etc)");
Cmd_AddCommand ("cancelselect", IN_Cancel, "cancel selected item in menu");
Cmd_AddCommand ("+mlook", IN_MLookDown, "activate mouse looking mode, do not recenter view" );
Cmd_AddCommand ("-mlook", IN_MLookUp, "deactivate mouse looking mode" );
}
@ -654,7 +671,6 @@ void CL_ShutdownInput( void )
Cmd_RemoveCommand ("+use" );
Cmd_RemoveCommand ("-use" );
Cmd_RemoveCommand ("impulse" );
Cmd_RemoveCommand ("cancelselect" );
Cmd_RemoveCommand ("+mlook" );
Cmd_RemoveCommand ("-mlook" );
}

View File

@ -192,7 +192,7 @@ typedef struct serverinfo_s
} serverinfo_t;
typedef enum { key_game, key_console, key_message, key_menu, key_gamemenu } keydest_t;
typedef enum { key_console = 0, key_game, key_hudmenu, key_message, key_menu } keydest_t;
typedef struct
{

View File

@ -29,6 +29,11 @@ extern vsound_exp_t *se;
#define MAX_HEARTBEAT -99999 // connection time
#define MAX_EVENTS 1024 // system events
// all drawing is done to a 640*480 virtual screen size
// and will be automatically scaled to the real resolution
#define SCREEN_WIDTH 640
#define SCREEN_HEIGHT 480
// cvars
extern cvar_t *scr_loading;
extern cvar_t *scr_download;
@ -327,7 +332,7 @@ extern byte *zonepool;
#define Z_Malloc(size) Mem_Alloc( zonepool, size )
void CL_GetEntitySoundSpatialization( int ent, vec3_t origin, vec3_t velocity );
bool SV_ReadComment( char *comment, int savenum );
bool SV_GetComment( char *comment, int savenum );
int CL_PMpointcontents( vec3_t point );
void CL_MouseEvent( int mx, int my, int time );
void CL_AddLoopingSounds( void );

View File

@ -1108,7 +1108,7 @@ void Key_Event(int key, bool down, uint time)
case key_menu:
UI_KeyEvent( key );
return;
case key_gamemenu:
case key_hudmenu:
// passed to client dll's
break;
default:
@ -1144,10 +1144,9 @@ void Key_Event(int key, bool down, uint time)
{
Key_Console( key );
}
else if( cls.key_dest == key_game || cls.key_dest == key_gamemenu )
else if( cls.key_dest == key_game || cls.key_dest == key_hudmenu )
{
// send the bound action
cls.key_dest = key_game;
kb = keys[key].binding;
if( !kb )
{
@ -1186,10 +1185,6 @@ void Key_Event(int key, bool down, uint time)
}
else
{
// HACKHACK: handle user menus here
if( !com.strncmp( "slot", kb, 4 ))
cls.key_dest = key_gamemenu;
// down-only command
Cbuf_AddText( kb );
Cbuf_AddText( "\n" );

View File

@ -199,6 +199,7 @@ void PF_newgame( void )
// disable updates and start the cinematic going
cl.servercount = -1;
Cvar_SetValue( "deathmatch", 0 );
Cvar_SetValue( "teamplay", 0 );
Cvar_SetValue( "gamerules", 0 );
Cvar_SetValue( "paused", 0 );
Cvar_SetValue( "coop", 0 );
@ -223,7 +224,7 @@ void PF_readcomment( void )
return;
savenum = (int)PRVM_G_FLOAT(OFS_PARM0);
SV_ReadComment( comment, savenum );
SV_GetComment( comment, savenum );
PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString( comment );
}

View File

@ -254,7 +254,7 @@ SOURCE=.\server\sv_phys.c
# End Source File
# Begin Source File
SOURCE=.\server\sv_progs.c
SOURCE=.\server\sv_save.c
# End Source File
# Begin Source File

View File

@ -228,6 +228,7 @@ typedef struct
dword funcBase; // base offset
int hStringTable; // stringtable handle
SAVERESTOREDATA SaveData; // shared struct, used for save data
} svgame_static_t;
typedef struct
@ -236,9 +237,8 @@ typedef struct
dword realtime; // always increasing, no clamping, etc
float timeleft; // frametime * game_frames
string mapcmd; // ie: *intro.cin+base
string comment; // map name, e.t.c.
char mapname[CS_SIZE]; // current mapname
char comment[CS_SIZE]; // map name, e.t.c.
int spawncount; // incremented each server start
// used to check late spawns
sv_client_t *clients; // [host_maxclients->integer]
@ -248,7 +248,6 @@ typedef struct
entity_state_t *baselines; // [host.max_edicts]
int last_heartbeat;
challenge_t challenges[MAX_CHALLENGES]; // to prevent invalid IPs from connecting
} server_static_t;
@ -415,7 +414,7 @@ void SV_TouchTriggers (edict_t *ent);
//
// sv_save.c
//
void SV_WriteSaveFile( const char *name );
void SV_WriteSaveFile( const char *name, bool autosave );
void SV_ReadSaveFile( const char *name );
void SV_ReadLevelFile( const char *name );
//============================================================

View File

@ -206,7 +206,7 @@ void SV_Map_f( void )
SV_BroadcastCommand( "reconnect\n" );
// archive server state
com.strncpy( svs.mapcmd, filename, sizeof(svs.mapcmd) - 1);
com.strncpy( svs.mapname, filename, sizeof( svs.mapname ) - 1 );
}
void SV_Newgame_f( void )
@ -241,7 +241,7 @@ void SV_Load_f( void )
SV_ReadSaveFile( filename );
SV_BroadcastCommand( "changing\n" );
SV_SpawnServer( svs.mapcmd, filename );
SV_SpawnServer( svs.mapname, filename );
SV_BroadcastCommand( "reconnect\n" );
}
@ -255,14 +255,25 @@ void SV_Save_f( void )
{
string filename;
if(Cmd_Argc() != 2)
if( Cmd_Argc() != 2 )
{
Msg ("Usage: savegame <directory>\n");
Msg ("Usage: save <name>\n");
return;
}
com.snprintf( filename, MAX_STRING, "%s.bin", Cmd_Argv(1));
SV_WriteSaveFile( filename );
com.snprintf( filename, MAX_STRING, "%s.bin", Cmd_Argv( 1 ));
SV_WriteSaveFile( filename, false );
}
/*
==============
SV_AutoSave_f
==============
*/
void SV_AutoSave_f( void )
{
SV_WriteSaveFile( "autosave.bin", true );
}
/*
@ -290,7 +301,7 @@ void SV_ChangeLevel_f( void )
return;
}
if(sv.state == ss_active)
if( sv.state == ss_active )
{
bool *savedFree;
sv_client_t *cl;
@ -305,7 +316,7 @@ void SV_ChangeLevel_f( void )
savedFree[i] = cl->edict->free;
cl->edict->free = true;
}
SV_WriteSaveFile( "save0.bin" ); // autosave
SV_WriteSaveFile( "autosave.bin", true );
// we must restore these for clients to transfer over correctly
for (i = 0, cl = svs.clients; i < Host_MaxClients(); i++, cl++)
cl->edict->free = savedFree[i];
@ -319,7 +330,7 @@ void SV_ChangeLevel_f( void )
SV_BroadcastCommand ("reconnect\n");
// archive server state
com.strncpy (svs.mapcmd, filename, sizeof(svs.mapcmd) - 1);
com.strncpy( svs.mapname, filename, sizeof( svs.mapname ) - 1 );
}
/*
@ -333,13 +344,12 @@ void SV_Restart_f( void )
{
string filename;
if(sv.state != ss_active) return;
com.strncpy( filename, svs.mapcmd, MAX_STRING );
if( sv.state != ss_active ) return;
com.strncpy( filename, svs.mapname, MAX_STRING );
FS_StripExtension( filename );
// just sending console command
Cbuf_AddText(va("map %s\n", filename ));
Cbuf_AddText( va( "map %s\n", filename ));
}
/*
@ -537,31 +547,33 @@ void SV_InitOperatorCommands( void )
Cmd_AddCommand( "save", SV_Save_f, "save the game to a file" );
Cmd_AddCommand( "load", SV_Load_f, "load a saved game file" );
Cmd_AddCommand( "autosave", SV_AutoSave_f, "save the game to 'autosave' file" );
Cmd_AddCommand( "killserver", SV_KillServer_f, "shutdown current server" );
}
void SV_KillOperatorCommands( void )
{
Cmd_RemoveCommand("heartbeat");
Cmd_RemoveCommand("kick");
Cmd_RemoveCommand("status");
Cmd_RemoveCommand("serverinfo");
Cmd_RemoveCommand("clientinfo");
Cmd_RemoveCommand( "heartbeat" );
Cmd_RemoveCommand( "kick" );
Cmd_RemoveCommand( "status" );
Cmd_RemoveCommand( "serverinfo" );
Cmd_RemoveCommand( "clientinfo" );
Cmd_RemoveCommand("map");
Cmd_RemoveCommand("movie");
Cmd_RemoveCommand("newgame");
Cmd_RemoveCommand("changelevel");
Cmd_RemoveCommand("restart");
Cmd_RemoveCommand("sectorlist");
Cmd_RemoveCommand( "map" );
Cmd_RemoveCommand( "movie" );
Cmd_RemoveCommand( "newgame" );
Cmd_RemoveCommand( "changelevel" );
Cmd_RemoveCommand( "restart" );
Cmd_RemoveCommand( "sectorlist" );
if( host.type == HOST_DEDICATED )
{
Cmd_RemoveCommand("say");
Cmd_RemoveCommand("setmaster");
Cmd_RemoveCommand( "say" );
Cmd_RemoveCommand( "setmaster" );
}
Cmd_RemoveCommand("save");
Cmd_RemoveCommand("load");
Cmd_RemoveCommand("killserver");
Cmd_RemoveCommand( "save" );
Cmd_RemoveCommand( "load" );
Cmd_RemoveCommand( "autosave" );
Cmd_RemoveCommand( "killserver" );
}

View File

@ -708,319 +708,18 @@ void SV_FreeEdicts( void )
SV_FreeEdict( ent );
}
// clear globals
StringTable_Clear( svgame.hStringTable );
if( !sv.loadgame )
{
// leave unchanged, because we wan't load it twice
StringTable_Clear( svgame.hStringTable );
svgame.dllFuncs.pfnServerDeactivate();
}
svgame.globals->numEntities = 0;
svgame.globals->numClients = 0;
svgame.globals->mapname = 0;
}
/*
============
Save\Load gamestate
savegame operations
============
*/
static int s_table;
void SV_AddSaveLump( wfile_t *f, const char *lumpname, void *data, size_t len, bool compress )
{
if( f ) WAD_Write( f, lumpname, data, len, TYPE_BINDATA, ( compress ? CMP_ZLIB : CMP_NONE ));
}
static void SV_SetPair( const char *name, const char *value, dkeyvalue_t *cvars, int *numpairs )
{
if( !name || !value ) return; // SetString can't register null strings
cvars[*numpairs].epair[DENT_KEY] = StringTable_SetString( s_table, name );
cvars[*numpairs].epair[DENT_VAL] = StringTable_SetString( s_table, value);
(*numpairs)++; // increase epairs
}
void SV_AddCvarLump( wfile_t *f )
{
dkeyvalue_t cvbuffer[MAX_FIELDS];
int numpairs = 0;
Cvar_LookupVars( CVAR_LATCH, cvbuffer, &numpairs, SV_SetPair );
SV_AddSaveLump( f, LUMP_GAMECVARS, cvbuffer, numpairs * sizeof(dkeyvalue_t), true );
}
void SV_AddCStrLump( wfile_t *f )
{
string_t csbuffer[MAX_CONFIGSTRINGS];
int i;
// pack the cfg string data
for( i = 0; i < MAX_CONFIGSTRINGS; i++ )
csbuffer[i] = StringTable_SetString( s_table, sv.configstrings[i] );
SV_AddSaveLump( f, LUMP_CFGSTRING, &csbuffer, sizeof(csbuffer), true );
}
void SV_WriteGlobal( wfile_t *f )
{
dkeyvalue_t globals[MAX_FIELDS];
int numpairs = 0;
PRVM_ED_WriteGlobals( &globals, &numpairs, SV_SetPair );
SV_AddSaveLump( f, LUMP_GAMESTATE, &globals, numpairs * sizeof(dkeyvalue_t), true );
}
void SV_WriteLocals( wfile_t *f )
{
dkeyvalue_t fields[MAX_FIELDS];
vfile_t *h = VFS_Open( NULL, "wb" );
int i, numpairs = 0;
for( i = 0; i < svgame.globals->numEntities; i++ )
{
numpairs = 0; // reset fields info
PRVM_ED_Write( PRVM_EDICT_NUM( i ), &fields, &numpairs, SV_SetPair );
VFS_Write( h, &numpairs, sizeof( int )); // numfields
VFS_Write( h, &fields, numpairs * sizeof( dkeyvalue_t )); // fields
}
// all allocated memory will be freed at end of SV_WriteSaveFile
SV_AddSaveLump( f, LUMP_GAMEENTS, VFS_GetBuffer( h ), VFS_Tell( h ), true );
VFS_Close( h ); // release virtual file
}
/*
=============
SV_WriteSaveFile
=============
*/
void SV_WriteSaveFile( const char *name )
{
string comment;
wfile_t *savfile = NULL;
bool autosave = false;
char path[MAX_SYSPATH];
byte *portalstate = Z_Malloc( MAX_MAP_AREAPORTALS );
int portalsize;
if( sv.state != ss_active )
return;
if(Cvar_VariableValue("deathmatch") || Cvar_VariableValue("coop"))
{
MsgDev(D_ERROR, "SV_WriteSaveFile: can't savegame in a multiplayer\n");
return;
}
if(Host_MaxClients() == 1 && svs.clients[0].edict->v.health <= 0 )
{
MsgDev(D_ERROR, "SV_WriteSaveFile: can't savegame while dead!\n");
return;
}
if(!com.strcmp(name, "save0.bin")) autosave = true;
com.sprintf( path, "save/%s", name );
savfile = WAD_Open( path, "wb" );
if( !savfile )
{
MsgDev(D_ERROR, "SV_WriteSaveFile: failed to open %s\n", path );
return;
}
MsgDev( D_INFO, "Saving svgame..." );
com.sprintf (comment, "%s - %s", sv.configstrings[CS_NAME], timestamp( TIME_FULL ));
s_table = StringTable_Create( name, 0x10000 ); // 65535 unique strings
// write lumps
pe->GetAreaPortals( &portalstate, &portalsize );
SV_AddSaveLump( savfile, LUMP_AREASTATE, portalstate, portalsize, true );
SV_AddSaveLump( savfile, LUMP_COMMENTS, comment, sizeof(comment), false );
SV_AddSaveLump( savfile, LUMP_MAPCMDS, svs.mapcmd, sizeof(svs.mapcmd), false );
SV_AddCStrLump( savfile );
SV_AddCvarLump( savfile );
SV_WriteGlobal( savfile );
SV_WriteLocals( savfile );
StringTable_Save( s_table, savfile ); // now system released
Mem_Free( portalstate ); // release portalinfo
WAD_Close( savfile );
MsgDev( D_INFO, "done.\n" );
}
void Sav_LoadComment( wfile_t *l )
{
byte *in;
int size;
in = WAD_Read( l, LUMP_COMMENTS, &size, TYPE_BINDATA );
com.strncpy( svs.comment, in, size );
}
void Sav_LoadCvars( wfile_t *l )
{
dkeyvalue_t *in;
int i, numpairs;
const char *name, *value;
in = (dkeyvalue_t *)WAD_Read( l, LUMP_GAMECVARS, &numpairs, TYPE_BINDATA );
if( numpairs % sizeof(*in)) Host_Error( "Sav_LoadCvars: funny lump size\n" );
numpairs /= sizeof( dkeyvalue_t );
for( i = 0; i < numpairs; i++ )
{
name = StringTable_GetString( s_table, in[i].epair[DENT_KEY] );
value = StringTable_GetString( s_table, in[i].epair[DENT_VAL] );
Cvar_SetLatched( name, value );
}
}
void Sav_LoadMapCmds( wfile_t *l )
{
byte *in;
int size;
in = WAD_Read( l, LUMP_MAPCMDS, &size, TYPE_BINDATA );
com.strncpy( svs.mapcmd, in, size );
}
void Sav_LoadCfgString( wfile_t *l )
{
string_t *in;
int i, numstrings;
in = (string_t *)WAD_Read( l, LUMP_CFGSTRING, &numstrings, TYPE_BINDATA );
if( numstrings % sizeof(*in)) Host_Error( "Sav_LoadCfgString: funny lump size\n" );
numstrings /= sizeof( string_t ); // because old saves can contain last values of MAX_CONFIGSTRINGS
// unpack the cfg string data
for( i = 0; i < numstrings; i++ )
com.strncpy( sv.configstrings[i], StringTable_GetString( s_table, in[i] ), CS_SIZE );
}
void Sav_LoadAreaPortals( wfile_t *l )
{
byte *in;
int size;
in = WAD_Read( l, LUMP_AREASTATE, &size, TYPE_BINDATA );
pe->SetAreaPortals( in, size ); // CM_ReadPortalState
}
void Sav_LoadGlobal( wfile_t *l )
{
dkeyvalue_t *globals;
int numpairs;
globals = (dkeyvalue_t *)WAD_Read( l, LUMP_GAMESTATE, &numpairs, TYPE_BINDATA );
if( numpairs % sizeof(*globals)) Host_Error( "Sav_LoadGlobal: funny lump size\n" );
numpairs /= sizeof( dkeyvalue_t );
PRVM_ED_ReadGlobals( s_table, globals, numpairs );
}
void Sav_LoadLocals( wfile_t *l )
{
dkeyvalue_t fields[MAX_FIELDS];
int numpairs, entnum = 0;
byte *buff;
size_t size;
vfile_t *h;
buff = WAD_Read( l, LUMP_GAMEENTS, &size, TYPE_BINDATA );
h = VFS_Create( buff, size );
while(!VFS_Eof( h ))
{
VFS_Read( h, &numpairs, sizeof( int ));
VFS_Read( h, &fields, numpairs * sizeof( dkeyvalue_t ));
PRVM_ED_Read( s_table, entnum, fields, numpairs );
entnum++;
}
svgame.globals->numEntities = entnum;
}
/*
=============
SV_ReadSaveFile
=============
*/
void SV_ReadSaveFile( const char *name )
{
char path[MAX_SYSPATH];
wfile_t *savfile;
int s_table;
com.sprintf(path, "save/%s", name );
savfile = WAD_Open( path, "rb" );
if( !savfile )
{
MsgDev(D_ERROR, "SV_ReadSaveFile: can't open %s\n", path );
return;
}
s_table = StringTable_Load( savfile, name );
Sav_LoadComment( savfile );
Sav_LoadCvars( savfile );
StringTable_Delete( s_table );
SV_InitGame(); // start a new game fresh with new cvars
Sav_LoadMapCmds( savfile );
WAD_Close( savfile );
CL_Drop();
}
/*
=============
SV_ReadLevelFile
=============
*/
void SV_ReadLevelFile( const char *name )
{
char path[MAX_SYSPATH];
wfile_t *savfile;
int s_table;
com.sprintf (path, "save/%s", name );
savfile = WAD_Open( path, "rb" );
if( !savfile )
{
MsgDev( D_ERROR, "SV_ReadLevelFile: can't open %s\n", path );
return;
}
s_table = StringTable_Load( savfile, name );
Sav_LoadCfgString( savfile );
Sav_LoadAreaPortals( savfile );
Sav_LoadGlobal( savfile );
Sav_LoadLocals( savfile );
StringTable_Delete( s_table );
WAD_Close( savfile );
}
bool SV_ReadComment( char *comment, int savenum )
{
wfile_t *savfile;
int result;
if( !comment ) return false;
result = WAD_Check( va( "save/save%i.bin", savenum ));
switch( result )
{
case 0:
com.strncpy( comment, "<empty>", MAX_STRING );
return false;
case 1: break;
default:
com.strncpy( comment, "<corrupted>", MAX_STRING );
return false;
}
savfile = WAD_Open( va("save/save%i.bin", savenum), "rb" );
Sav_LoadComment( savfile );
com.strncpy( comment, svs.comment, MAX_STRING );
WAD_Close( savfile );
return true;
}
/*
===============================================================================
Game Builtin Functions

View File

@ -136,7 +136,8 @@ void SV_CheckForSavegame( const char *savename )
{
sv.loadgame = true; // predicting state
if( sv_noreload->value ) sv.loadgame = false;
if( Cvar_VariableValue( "deathmatch" )) sv.loadgame = false;
if( svgame.globals->deathmatch || svgame.globals->coop || svgame.globals->teamplay )
sv.loadgame = false;
if( !savename ) sv.loadgame = false;
if( !FS_FileExists( va( "save/%s", savename )))
sv.loadgame = false;
@ -267,22 +268,23 @@ void SV_InitGame( void )
Cmd_ExecuteString( "latch\n" );
svs.initialized = true;
if (Cvar_VariableValue ("coop") && Cvar_VariableValue ("deathmatch"))
if( Cvar_VariableValue ("coop") && Cvar_VariableValue ( "deathmatch" ) && Cvar_VariableValue( "teamplay" ))
{
Msg("Deathmatch and Coop both set, disabling Coop\n");
Cvar_FullSet ("coop", "0", CVAR_SERVERINFO | CVAR_LATCH);
Msg("Deathmatch, Teamplay and Coop set, defaulting to Deathmatch\n");
Cvar_FullSet( "coop", "0", CVAR_SERVERINFO|CVAR_LATCH );
Cvar_FullSet( "teamplay", "0", CVAR_SERVERINFO|CVAR_LATCH );
}
// dedicated servers are can't be single player and are usually DM
// so unless they explicity set coop, force it to deathmatch
if( host.type == HOST_DEDICATED )
{
if(!Cvar_VariableValue( "coop" ))
if(!Cvar_VariableValue( "coop" ) && !Cvar_VariableValue( "teamplay" ))
Cvar_FullSet( "deathmatch", "1", CVAR_SERVERINFO|CVAR_LATCH );
}
// init clients
if( Cvar_VariableValue( "deathmatch" ))
if( Cvar_VariableValue( "deathmatch" ) || Cvar_VariableValue( "teamplay" ))
{
if( Host_MaxClients() <= 1 )
Cvar_FullSet( "host_maxclients", "8", CVAR_SERVERINFO|CVAR_LATCH );
@ -306,6 +308,13 @@ void SV_InitGame( void )
svs.client_entities = Z_Malloc( sizeof(entity_state_t) * svs.num_client_entities );
svs.baselines = Z_Malloc( sizeof(entity_state_t) * host.max_edicts );
// copy gamemode into svgame.globals
svgame.globals->deathmatch = (int)Cvar_VariableValue( "deathmatch" );
svgame.globals->teamplay = (int)Cvar_VariableValue( "teamplay" );
svgame.globals->coop = (int)Cvar_VariableValue( "coop" );
svgame.dllFuncs.pfnResetGlobalState();
// heartbeats will always be sent to the id master
svs.last_heartbeat = MAX_HEARTBEAT; // send immediately
com.sprintf( idmaster, "192.246.40.37:%i", PORT_MASTER );

View File

@ -343,8 +343,9 @@ void SV_Init( void )
rcon_password = Cvar_Get( "rcon_password", "", 0, "remote connect password" );
Cvar_Get ("skill", "1", 0, "game skill level" );
Cvar_Get ("deathmatch", "0", CVAR_LATCH, "displays deathmatch state" );
Cvar_Get ("coop", "0", CVAR_LATCH, "displays cooperative state" );
Cvar_Get ("deathmatch", "0", CVAR_SERVERINFO|CVAR_LATCH, "displays deathmatch state" );
Cvar_Get ("teamplay", "0", CVAR_SERVERINFO|CVAR_LATCH, "displays teamplay state" );
Cvar_Get ("coop", "0", CVAR_SERVERINFO|CVAR_LATCH, "displays cooperative state" );
Cvar_Get ("dmflags", "0", CVAR_SERVERINFO, "setup deathmatch flags" );
Cvar_Get ("fraglimit", "0", CVAR_SERVERINFO, "multiplayer fraglimit" );
Cvar_Get ("timelimit", "0", CVAR_SERVERINFO, "multiplayer timelimit" );
@ -440,12 +441,8 @@ void SV_Shutdown( bool reconnect )
if( svs.clients ) SV_FinalMessage( host.finalmsg, reconnect );
Master_Shutdown();
if( reconnect )
{
if( svgame.hInstance )
svgame.dllFuncs.pfnServerDeactivate();
if( reconnect )
SV_FreeEdicts();
}
else SV_UnloadProgs();
// free current level

View File

@ -1,19 +0,0 @@
//=======================================================================
// Copyright XashXT Group 2008 ©
// sv_progs.c - server.dat interface
//=======================================================================
#include "common.h"
#include "server.h"
#include "byteorder.h"
#include "matrix_lib.h"
#include "const.h"
void SV_RestoreEdict( edict_t *ent )
{
// link it into the bsp tree
SV_LinkEdict( ent );
SV_CreatePhysBody( ent );
SV_SetPhysForce( ent ); // restore forces ...
SV_SetMassCentre( ent ); // and mass force
}

556
engine/server/sv_save.c Normal file
View File

@ -0,0 +1,556 @@
//=======================================================================
// Copyright XashXT Group 2008 ©
// sv_save.c - game serialization
//=======================================================================
#include "common.h"
#include "server.h"
#include "byteorder.h"
#include "matrix_lib.h"
#include "const.h"
// contains some info from globalvars_t
typedef struct save_header_s
{
int numEntities; // actual edicts count
int numConnections; // level transitions count
float time; // sv.time at saved moment
char mapName[CS_SIZE]; // svs.mapname
} save_header_t;
typedef struct game_header_s
{
int mapCount; // svs.mapcount
char mapName[CS_SIZE]; // svs.mapname
char comment[CS_SIZE]; // svs.comment
} game_header_t;
static TYPEDESCRIPTION gSaveHeader[] =
{
DEFINE_FIELD( save_header_t, numEntities, FIELD_INTEGER ),
DEFINE_FIELD( save_header_t, numConnections, FIELD_INTEGER ),
DEFINE_FIELD( save_header_t, time, FIELD_TIME ),
DEFINE_ARRAY( save_header_t, mapName, FIELD_CHARACTER, CS_SIZE ),
};
static TYPEDESCRIPTION gGameHeader[] =
{
DEFINE_FIELD( game_header_t, mapCount, FIELD_INTEGER ),
DEFINE_ARRAY( game_header_t, mapName, FIELD_CHARACTER, CS_SIZE ),
DEFINE_ARRAY( game_header_t, comment, FIELD_CHARACTER, CS_SIZE ),
};
static TYPEDESCRIPTION gAdjacency[] =
{
DEFINE_ARRAY( LEVELLIST, mapName, FIELD_CHARACTER, CS_SIZE ),
DEFINE_ARRAY( LEVELLIST, landmarkName, FIELD_CHARACTER, CS_SIZE ),
DEFINE_FIELD( LEVELLIST, pentLandmark, FIELD_EDICT ),
DEFINE_FIELD( LEVELLIST, vecLandmarkOrigin, FIELD_VECTOR ),
};
static TYPEDESCRIPTION gETable[] =
{
DEFINE_FIELD( ENTITYTABLE, id, FIELD_INTEGER ),
DEFINE_FIELD( ENTITYTABLE, location, FIELD_INTEGER ),
DEFINE_FIELD( ENTITYTABLE, size, FIELD_INTEGER ),
DEFINE_FIELD( ENTITYTABLE, flags, FIELD_INTEGER ),
DEFINE_FIELD( ENTITYTABLE, classname, FIELD_STRING ),
};
// TEST
uint HashString( const char *pszToken )
{
uint hash = 0;
while( *pszToken )
hash = _rotr( hash, 4 ) ^ *pszToken++;
return hash;
}
word TokenHash( const char *pszToken, const char **pTokens, uint tokenCount )
{
word hash = (word)(HashString( pszToken ) % (uint)tokenCount );
int i, index;
for( i = 0; i < tokenCount; i++ )
{
index = hash + i;
if( index >= tokenCount )
index -= tokenCount;
if( !pTokens[index] || !com.strcmp( pszToken, pTokens[index] ))
{
pTokens[index] = (char *)pszToken;
return index;
}
}
// Token hash table full!!!
// [Consider doing overflow table(s) after the main table & limiting linear hash table search]
MsgDev( D_ERROR, "CSaveRestoreBuffer :: TokenHash() is COMPLETELY FULL!" );
return 0;
}
static void SV_AddSaveLump( wfile_t *f, const char *lumpname, void *data, size_t len, bool compress )
{
if( f ) WAD_Write( f, lumpname, data, len, TYPE_BINDATA, ( compress ? CMP_ZLIB : CMP_NONE ));
}
static void SV_SetPair( const char *name, const char *value, dkeyvalue_t *cvars, int *numpairs )
{
if( !name || !value ) return; // ignore emptycvars
cvars[*numpairs].epair[DENT_KEY] = StringTable_SetString( svgame.hStringTable, name );
cvars[*numpairs].epair[DENT_VAL] = StringTable_SetString( svgame.hStringTable, value);
(*numpairs)++; // increase epairs
}
static void SV_SaveEngineData( wfile_t *f )
{
byte *portalstate = NULL;
int i, portalsize, numpairs = 0;
dkeyvalue_t cvbuffer[MAX_FIELDS];
string_t csbuffer[MAX_CONFIGSTRINGS];
// save areaportals state
portalstate = Z_Malloc( MAX_MAP_AREAPORTALS );
pe->GetAreaPortals( &portalstate, &portalsize );
SV_AddSaveLump( f, LUMP_AREASTATE, portalstate, portalsize, true );
if( portalstate ) Mem_Free( portalstate ); // release portalinfo
// make sure what all configstrings are passes through StringTable system
for( i = 0; i < MAX_CONFIGSTRINGS; i++ )
csbuffer[i] = StringTable_SetString( svgame.hStringTable, sv.configstrings[i] );
SV_AddSaveLump( f, LUMP_CFGSTRING, &csbuffer, sizeof( csbuffer ), true );
// save latched cvars
Cvar_LookupVars( CVAR_LATCH, cvbuffer, &numpairs, SV_SetPair );
SV_AddSaveLump( f, LUMP_GAMECVARS, cvbuffer, numpairs * sizeof( dkeyvalue_t ), true );
}
static void SV_SaveServerData( wfile_t *f )
{
SAVERESTOREDATA *pSaveData;
byte *savepool;
ENTITYTABLE *pTable;
save_header_t shdr;
game_header_t ghdr;
int i;
// initialize local mempool
savepool = Mem_AllocPool( "Save Pool" );
// initialize SAVERESTOREDATA
Mem_Set( &svgame.SaveData, 0, sizeof( SAVERESTOREDATA ));
svgame.SaveData.bufferSize = 0x80000; // reserve 512K for now
svgame.SaveData.pBaseData = Mem_Alloc( savepool, svgame.SaveData.bufferSize );
svgame.SaveData.pCurrentData = svgame.SaveData.pBaseData;
svgame.SaveData.tokenCount = 0xFFF; // assume a maximum of 4K-1 symbol table entries
svgame.SaveData.pTokens = Mem_Alloc( savepool, svgame.SaveData.tokenCount * sizeof( char* ));
svgame.SaveData.time = svgame.globals->time;
pSaveData = svgame.globals->pSaveData = &svgame.SaveData;
// initialize level connections
svgame.dllFuncs.pfnBuildLevelList();
// initialize save header
shdr.numEntities = svgame.globals->numEntities;
shdr.numConnections = svgame.SaveData.connectionCount;
shdr.time = svgame.SaveData.time;
com.strncpy( shdr.mapName, svs.mapname, CS_SIZE );
// initialize game header
ghdr.mapCount = svs.spawncount;
com.strncpy( ghdr.mapName, svs.mapname, CS_SIZE );
com.strncpy( ghdr.comment, svs.comment, CS_SIZE );
// initialize ENTITYTABLE
pSaveData->tableCount = svgame.globals->numEntities;
pSaveData->pTable = Mem_Alloc( savepool, pSaveData->tableCount * sizeof( ENTITYTABLE ));
for( i = 0; i < svgame.globals->numEntities; i++ )
{
edict_t *pent = EDICT_NUM( i );
pTable = &pSaveData->pTable[i];
pTable->id = pent->serialnumber;
if( pent->free ) pTable->flags |= FENTTABLE_REMOVED;
pTable->pent = pent;
// setup some flags
if( pent->v.flags & FL_CLIENT ) pTable->flags |= FENTTABLE_PLAYER;
if( pent->free ) pTable->flags |= FENTTABLE_REMOVED;
}
// write save header
svgame.dllFuncs.pfnSaveWriteFields( pSaveData, "Save Header", &shdr, gSaveHeader, ARRAYSIZE( gSaveHeader ));
// write level connections
for( i = 0; i < pSaveData->connectionCount; i++ )
{
LEVELLIST *pList = &pSaveData->levelList[i];
svgame.dllFuncs.pfnSaveWriteFields( pSaveData, "ADJACENCY", pList, gAdjacency, ARRAYSIZE( gAdjacency ));
}
// write entity descriptions
for( i = 0; i < svgame.globals->numEntities; i++ )
{
edict_t *pent = EDICT_NUM( i );
if( pent->free ) continue;
svgame.dllFuncs.pfnSave( pent, pSaveData );
pSaveData->currentIndex++; // move pointer
}
// write entity table
for( i = 0; i < pSaveData->tableCount; i++ )
{
pTable = &pSaveData->pTable[i];
svgame.dllFuncs.pfnSaveWriteFields( pSaveData, "ETABLE", pTable, gETable, ARRAYSIZE( gETable ));
}
// write result into lump
SV_AddSaveLump( f, LUMP_ENTITIES, pSaveData->pBaseData, pSaveData->size, false );
// clear buffer for global lump
Mem_Set( pSaveData->pBaseData, 0, pSaveData->size );
pSaveData->pCurrentData = pSaveData->pBaseData;
pSaveData->size = 0;
// write game header
svgame.dllFuncs.pfnSaveWriteFields( pSaveData, "Game Header", &ghdr, gGameHeader, ARRAYSIZE( gGameHeader ));
// at end of description save globals
svgame.dllFuncs.pfnSaveGlobalState( pSaveData );
// write result into lump
SV_AddSaveLump( f, LUMP_GLOBALS, pSaveData->pBaseData, pSaveData->size, false );
// do cleanup operations
Mem_Set( &svgame.SaveData, 0, sizeof( SAVERESTOREDATA ));
svgame.globals->pSaveData = NULL;
Mem_FreePool( &savepool );
}
/*
=============
SV_WriteSaveFile
=============
*/
void SV_WriteSaveFile( const char *name, bool autosave )
{
wfile_t *savfile = NULL;
char path[MAX_SYSPATH];
if( sv.state != ss_active ) return;
if( svgame.globals->deathmatch || svgame.globals->coop || svgame.globals->teamplay )
{
MsgDev( D_ERROR, "SV_WriteSaveFile: can't savegame in a multiplayer\n" );
return;
}
if( Host_MaxClients() == 1 && svs.clients[0].edict->v.health <= 0 )
{
MsgDev( D_ERROR, "SV_WriteSaveFile: can't savegame while dead!\n" );
return;
}
com.sprintf( path, "save/%s", name );
savfile = WAD_Open( path, "wb" );
if( !savfile )
{
MsgDev(D_ERROR, "SV_WriteSaveFile: failed to open %s\n", path );
return;
}
com.snprintf( svs.comment, CS_SIZE, "%s - %s", sv.configstrings[CS_NAME], timestamp( TIME_FULL ));
MsgDev( D_INFO, "Saving game..." );
// write lumps
SV_SaveEngineData( savfile );
SV_SaveServerData( savfile );
StringTable_Save( svgame.hStringTable, savfile );
WAD_Close( savfile );
MsgDev( D_INFO, "done.\n" );
}
void SV_ReadComment( wfile_t *l )
{
SAVERESTOREDATA *pSaveData;
game_header_t ghdr;
int i;
// initialize SAVERESTOREDATA
Mem_Set( &svgame.SaveData, 0, sizeof( SAVERESTOREDATA ));
svgame.SaveData.pBaseData = WAD_Read( l, LUMP_GLOBALS, &svgame.SaveData.bufferSize, TYPE_BINDATA );
svgame.SaveData.pCurrentData = svgame.SaveData.pBaseData;
svgame.SaveData.tokenCount = 0xFFF; // assume a maximum of 4K-1 symbol table entries
svgame.SaveData.pTokens = (char **)Z_Malloc( svgame.SaveData.tokenCount * sizeof( char* ));
pSaveData = svgame.globals->pSaveData = &svgame.SaveData;
// TEST: tokenize strings
for( i = 0; i < ARRAYSIZE( gGameHeader ); i++ )
TokenHash( gGameHeader[i].fieldName, svgame.SaveData.pTokens, svgame.SaveData.tokenCount );
// read game header
svgame.dllFuncs.pfnSaveReadFields( pSaveData, "Game Header", &ghdr, gGameHeader, ARRAYSIZE( gGameHeader ));
com.strncpy( svs.comment, ghdr.comment, CS_SIZE );
if( svgame.SaveData.pTokens ) Mem_Free( svgame.SaveData.pTokens );
if( svgame.SaveData.pBaseData ) Mem_Free( svgame.SaveData.pBaseData );
Mem_Set( &svgame.SaveData, 0, sizeof( SAVERESTOREDATA ));
}
void SV_ReadCvars( wfile_t *l )
{
dkeyvalue_t *in;
int i, numpairs;
const char *name, *value;
in = (dkeyvalue_t *)WAD_Read( l, LUMP_GAMECVARS, &numpairs, TYPE_BINDATA );
if( numpairs % sizeof( *in )) Host_Error( "Sav_LoadCvars: funny lump size\n" );
numpairs /= sizeof( dkeyvalue_t );
for( i = 0; i < numpairs; i++ )
{
name = StringTable_GetString( svgame.hStringTable, in[i].epair[DENT_KEY] );
value = StringTable_GetString( svgame.hStringTable, in[i].epair[DENT_VAL] );
Cvar_SetLatched( name, value );
}
}
void SV_ReadCfgString( wfile_t *l )
{
string_t *in;
int i, numstrings;
in = (string_t *)WAD_Read( l, LUMP_CFGSTRING, &numstrings, TYPE_BINDATA );
if( numstrings % sizeof(*in)) Host_Error( "Sav_LoadCfgString: funny lump size\n" );
numstrings /= sizeof( string_t ); // because old saves can contain last values of MAX_CONFIGSTRINGS
// unpack the cfg string data
for( i = 0; i < numstrings; i++ )
com.strncpy( sv.configstrings[i], StringTable_GetString( svgame.hStringTable, in[i] ), CS_SIZE );
}
void SV_ReadAreaPortals( wfile_t *l )
{
byte *in;
int size;
in = WAD_Read( l, LUMP_AREASTATE, &size, TYPE_BINDATA );
pe->SetAreaPortals( in, size ); // CM_ReadPortalState
}
void SV_ReadGlobals( wfile_t *l )
{
SAVERESTOREDATA *pSaveData;
byte *restorepool;
game_header_t ghdr;
int i;
// initialize local mempool
// restorepool = Mem_AllocPool( "Restore Pool" );
// initialize SAVERESTOREDATA
Mem_Set( &svgame.SaveData, 0, sizeof( SAVERESTOREDATA ));
svgame.SaveData.pBaseData = WAD_Read( l, LUMP_GLOBALS, &svgame.SaveData.bufferSize, TYPE_BINDATA );
svgame.SaveData.pCurrentData = svgame.SaveData.pBaseData;
svgame.SaveData.tokenCount = 0xFFF; // assume a maximum of 4K-1 symbol table entries
svgame.SaveData.pTokens = Mem_Alloc( svgame.mempool, svgame.SaveData.tokenCount * sizeof( char* ));
pSaveData = svgame.globals->pSaveData = &svgame.SaveData;
// TEST: tokenize strings
for( i = 0; i < ARRAYSIZE( gGameHeader ); i++ )
TokenHash( gGameHeader[i].fieldName, svgame.SaveData.pTokens, svgame.SaveData.tokenCount );
// read the game header
svgame.dllFuncs.pfnSaveReadFields( pSaveData, "Game Header", &ghdr, gGameHeader, ARRAYSIZE( gGameHeader ));
svs.spawncount = ghdr.mapCount; // restore spawncount
com.strncpy( svs.comment, ghdr.comment, MAX_STRING );
com.strncpy( svs.mapname, ghdr.mapName, MAX_STRING );
// restore global state
svgame.dllFuncs.pfnRestoreGlobalState( pSaveData );
svgame.dllFuncs.pfnServerDeactivate();
//Mem_FreePool( &restorepool );
}
void SV_ReadEntities( wfile_t *l )
{
SAVERESTOREDATA *pSaveData;
byte *restorepool;
ENTITYTABLE *pTable;
LEVELLIST *pList;
save_header_t shdr;
int i;
// initialize local mempool
//restorepool = Mem_AllocPool( "Restore Pool" );
// SAVERESTOREDATA partially initialized, continue filling
pSaveData = svgame.globals->pSaveData = &svgame.SaveData;
svgame.SaveData.pBaseData = WAD_Read( l, LUMP_ENTITIES, &svgame.SaveData.bufferSize, TYPE_BINDATA );
svgame.SaveData.pCurrentData = svgame.SaveData.pBaseData;
svgame.SaveData.size = 0;
// TEST: tokenize strings
for( i = 0; i < ARRAYSIZE( gSaveHeader ); i++ )
TokenHash( gSaveHeader[i].fieldName, svgame.SaveData.pTokens, svgame.SaveData.tokenCount );
// read save header
svgame.dllFuncs.pfnSaveReadFields( pSaveData, "Save Header", &shdr, gSaveHeader, ARRAYSIZE( gSaveHeader ));
SV_ConfigString( CS_MAXCLIENTS, va("%i", Host_MaxClients()));
com.strncpy( sv.name, shdr.mapName, MAX_STRING );
svgame.globals->mapname = MAKE_STRING( sv.name );
svgame.globals->time = sv.time = shdr.time;
pSaveData->connectionCount = shdr.numConnections;
// initialize ENTITYTABLE
pSaveData->tableCount = shdr.numEntities;
pSaveData->pTable = Mem_Alloc( svgame.mempool, pSaveData->tableCount * sizeof( ENTITYTABLE ));
while( svgame.globals->numEntities < shdr.numEntities ) SV_AllocEdict(); // allocate edicts
// TEST: tokenize strings
for( i = 0; i < ARRAYSIZE( gETable ); i++ )
TokenHash( gETable[i].fieldName, svgame.SaveData.pTokens, svgame.SaveData.tokenCount );
// read entity table
for( i = 0; i < pSaveData->tableCount; i++ )
{
edict_t *pent = EDICT_NUM( i );
pTable = &pSaveData->pTable[i];
svgame.dllFuncs.pfnSaveReadFields( pSaveData, "ETABLE", pTable, gETable, ARRAYSIZE( gETable ));
if( pTable->id != pent->serialnumber )
MsgDev( D_ERROR, "ETABLE id( %i ) != edict->id( %i )\n", pTable->id, pent->serialnumber );
if( pTable->flags & FENTTABLE_REMOVED ) SV_FreeEdict( pent );
pTable->pent = pent;
}
// TEST: tokenize strings
for( i = 0; i < ARRAYSIZE( gAdjacency ); i++ )
TokenHash( gAdjacency[i].fieldName, svgame.SaveData.pTokens, svgame.SaveData.tokenCount );
// read ADJACENCY sections
for( i = 0; i < pSaveData->connectionCount; i++ )
{
pList = &pSaveData->levelList[i];
svgame.dllFuncs.pfnSaveReadFields( pSaveData, "ADJACENCY", pList, gAdjacency, ARRAYSIZE( gAdjacency ));
}
Com_Assert( 1 );
// and read entities ...
for( i = 0; i < pSaveData->tableCount; i++ )
{
edict_t *pent = EDICT_NUM( i );
pTable = &pSaveData->pTable[i];
// ignore removed edicts
if( pTable->flags & FENTTABLE_REMOVED ) continue;
svgame.dllFuncs.pfnRestore( pent, pSaveData, (pTable->flags & FENTTABLE_GLOBAL));
}
// do cleanup operations
Mem_Set( &svgame.SaveData, 0, sizeof( SAVERESTOREDATA ));
svgame.globals->pSaveData = NULL;
//Mem_FreePool( &restorepool );
}
/*
=============
SV_ReadSaveFile
=============
*/
void SV_ReadSaveFile( const char *name )
{
char path[MAX_SYSPATH];
wfile_t *savfile;
com.sprintf( path, "save/%s", name );
savfile = WAD_Open( path, "rb" );
if( !savfile )
{
MsgDev(D_ERROR, "SV_ReadSaveFile: can't open %s\n", path );
return;
}
sv.loadgame = true; // to avoid clearing StringTables in SV_Shutdown
StringTable_Delete( svgame.hStringTable ); // remove old string table
svgame.hStringTable = StringTable_Load( savfile, name );
SV_ReadCvars( savfile );
SV_InitGame(); // start a new game fresh with new cvars
// SV_Shutdown will be clear svs ans sv struct, so load it here
sv.loadgame = true; // restore state
SV_ReadGlobals( savfile );
WAD_Close( savfile );
CL_Drop();
}
/*
=============
SV_ReadLevelFile
=============
*/
void SV_ReadLevelFile( const char *name )
{
char path[MAX_SYSPATH];
wfile_t *savfile;
com.sprintf( path, "save/%s", name );
savfile = WAD_Open( path, "rb" );
if( !savfile )
{
MsgDev( D_ERROR, "SV_ReadLevelFile: can't open %s\n", path );
return;
}
SV_ReadCfgString( savfile );
SV_ReadAreaPortals( savfile );
SV_ReadEntities( savfile );
WAD_Close( savfile );
}
bool SV_GetComment( char *comment, int savenum )
{
wfile_t *savfile;
int result;
if( !comment ) return false;
result = WAD_Check( va( "save/save%i.bin", savenum ));
switch( result )
{
case 0:
com.strncpy( comment, "<empty>", MAX_STRING );
return false;
case 1: break;
default:
com.strncpy( comment, "<corrupted>", MAX_STRING );
return false;
}
savfile = WAD_Open( va( "save/save%i.bin", savenum ), "rb" );
SV_ReadComment( savfile );
com.strncpy( comment, svs.comment, MAX_STRING );
WAD_Close( savfile );
return true;
}
void SV_RestoreEdict( edict_t *ent )
{
// link it into the bsp tree
SV_LinkEdict( ent );
SV_CreatePhysBody( ent );
SV_SetPhysForce( ent ); // restore forces ...
SV_SetMassCentre( ent ); // and mass force
}

View File

@ -386,10 +386,8 @@ bool StringTable_SaveSystem( int h, wfile_t *wad )
if(!W_SaveLump( wad, "stringdata", dstring[h]->data, dstring[h]->datasize, TYPE_STRDATA, CMP_ZLIB ))
return false;
table_size = dstring[h]->numstrings * sizeof( string_t );
if(!W_SaveLump( wad, "stringtable", dstring[h]->table, table_size, TYPE_STRDATA, CMP_ZLIB ))
if( !W_SaveLump( wad, "stringtable", dstring[h]->table, table_size, TYPE_STRDATA, CMP_ZLIB ))
return false;
StringTable_DeleteSystem( h ); // strings dumped, now we can free it
return true;
}

View File

@ -182,6 +182,8 @@ typedef struct cl_enginefuncs_s
void (*pfnHookUserMsg)( const char *szMsgName, pfnUserMsgHook pfn );
void (*pfnServerCmd)( const char *szCmdString );
void (*pfnClientCmd)( const char *szCmdString );
void (*pfnSetKeyDest)( int key_dest );
void (*pfnGetPlayerInfo)( int player_num, hud_player_info_t *pinfo );
client_textmessage_t *(*pfnTextMessageGet)( const char *pName );

View File

@ -235,21 +235,20 @@ typedef enum
#define EVENT_SHARED 2000
#define EVENT_CLIENT 5000 // less than this value it's a server-side studio events
// player_state_t->renderfx
// FIXME: get rid of this
#define RDF_UNDERWATER (1<<0) // warp the screen as apropriate
#define RDF_NOWORLDMODEL (1<<1) // used for player configuration screen
#define RDF_BLOOM (1<<2) // light blooms
// all drawing is done to a 640*480 virtual screen size
// and will be automatically scaled to the real resolution
#define SCREEN_WIDTH 640
#define SCREEN_HEIGHT 480
// client screen state
#define CL_DISCONNECTED 1 //
#define CL_LOADING 2 // draw loading progress-bar
#define CL_ACTIVE 3 // draw normal hud
// client key destination
#define KEY_GAME 1
#define KEY_HUDMENU 2
#define TINYCHAR_WIDTH (SMALLCHAR_WIDTH)
#define TINYCHAR_HEIGHT (SMALLCHAR_HEIGHT/2)
#define SMALLCHAR_WIDTH 8

View File

@ -901,13 +901,11 @@ SAVE FILE
included global, and both (client & server) pent list
==============================================================================
*/
#define LUMP_COMMENTS "map_comment"
#define LUMP_CFGSTRING "configstrings"
#define LUMP_AREASTATE "areaportals"
#define LUMP_GAMESTATE "globals"
#define LUMP_MAPCMDS "map_cmds"
#define LUMP_ENTITIES "entities"
#define LUMP_GLOBALS "global_data"
#define LUMP_GAMECVARS "latched_cvars"
#define LUMP_GAMEENTS "entities"
#define LUMP_SNAPSHOT "saveshot" // currently not implemented
#define DENT_KEY 0

View File

@ -74,7 +74,7 @@ typedef struct globalvars_s
int total_monsters;
int killed_monsters; // number of monsters killed
void *pSaveData; // savedata base offset
void *pSaveData; // (SAVERESTOREDATA *) pointer
} globalvars_t;
// engine hands this to DLLs for functionality callbacks
@ -218,8 +218,8 @@ typedef struct
typedef struct
{
int id; // ordinal ID of this entity (used for entity <--> pointer conversions)
edict_t *pent; // pointer to the in-game entity
int id; // ENG ordinal ID of this entity (used for entity <--> pointer conversions)
edict_t *pent; // ENG pointer to the in-game entity
int location; // offset from the base data of this entity
int size; // byte size of this entity's data
@ -237,25 +237,25 @@ typedef struct
typedef struct saverestore_s
{
char *pBaseData; // start of all entity save data
char *pBaseData; // ENG start of all entity save data
char *pCurrentData; // current buffer pointer for sequential access
int size; // current data size
int bufferSize; // total space for data
int tokenSize; // size of the linear list of tokens
int tokenCount; // number of elements in the pTokens table
char **pTokens; // hash table of entity strings (sparse)
int currentIndex; // holds a global entity table ID
int tableCount; // number of elements in the entity table
int bufferSize; // ENG total space for data (Valve used 512 kb's)
int tokenSize; // always equal 0 (probably not used)
int tokenCount; // ENG number of elements in the pTokens table (Valve used 4096 tokens)
char **pTokens; // ENG hash table of entity strings (sparse)
int currentIndex; // ENG holds a global entity table ID
int tableCount; // ENG number of elements in the entity table (numEntities)
int connectionCount; // number of elements in the levelList[]
ENTITYTABLE *pTable; // array of ENTITYTABLE elements (1 for each entity)
ENTITYTABLE *pTable; // ENG array of ENTITYTABLE elements (1 for each entity)
LEVELLIST levelList[MAX_LEVEL_CONNECTIONS]; // list of connections from this level
// smooth transition
int fUseLandmark;
char szLandmarkName[64]; // landmark we'll spawn near in next level
int fUseLandmark; // ENG
char szLandmarkName[64]; // probably not used
vec3_t vecLandmarkOffset; // for landmark transitions
float time;
char szCurrentMap[64]; // To check global entities
char szCurrentMap[64]; // ENG To check global entities
float time; // ENG
} SAVERESTOREDATA;
typedef enum _fieldtypes
@ -342,7 +342,7 @@ typedef struct
void (*pfnStartFrame)( void );
void (*pfnEndFrame)( void );
void (*pfnParmsChangeLevel)( void );
void (*pfnBuildLevelList)( void );
// returns string describing current .dll. E.g., TeamFotrress 2, Half-Life
const char *(*pfnGetGameDescription)( void );

View File

@ -19,7 +19,7 @@
extern int gEvilImpulse101;
ItemInfo CBasePlayerWeapon::ItemInfoArray[MAX_WEAPONS];
AmmoInfo CBasePlayerWeapon::AmmoInfoArray[MAX_AMMO_SLOTS];
char NameItems[MAX_WEAPONS][64];
char NameItems[32][MAX_WEAPONS];
int ID[MAX_WEAPONS];
int GlobalID = 0;
int g_iSwing;

View File

@ -731,7 +731,7 @@ void PlayerPostThink( edict_t *pEntity )
gpGlobals->frametime = cached_frametime;
}
void ParmsChangeLevel( void )
void BuildLevelList( void )
{
// retrieve the pointer to the save data
SAVERESTOREDATA *pSaveData = (SAVERESTOREDATA *)gpGlobals->pSaveData;

View File

@ -32,7 +32,7 @@ extern void StartFrame( void );
extern void EndFrame( void );
extern void PlayerPostThink( edict_t *pEntity );
extern void PlayerPreThink( edict_t *pEntity );
extern void ParmsChangeLevel( void );
extern void BuildLevelList( void );
extern void InitBodyQue(void);
extern void InitWorld( void );

View File

@ -47,29 +47,29 @@ static DLL_FUNCTIONS gFunctionTable =
DispatchRestore, // pfnRestore
DispatchObjectCollsionBox, // pfnAbsBox
SaveWriteFields, //pfnSaveWriteFields
SaveReadFields, //pfnSaveReadFields
SaveWriteFields, // pfnSaveWriteFields
SaveReadFields, // pfnSaveReadFields
SaveGlobalState, //pfnSaveGlobalState
RestoreGlobalState, //pfnRestoreGlobalState
ResetGlobalState, //pfnResetGlobalState
SaveGlobalState, // pfnSaveGlobalState
RestoreGlobalState, // pfnRestoreGlobalState
ResetGlobalState, // pfnResetGlobalState
ClientConnect, //pfnClientConnect
ClientDisconnect, //pfnClientDisconnect
ClientKill, //pfnClientKill
ClientPutInServer, //pfnClientPutInServer
ClientCommand, //pfnClientCommand
ClientUserInfoChanged, //pfnClientUserInfoChanged
ClientConnect, // pfnClientConnect
ClientDisconnect, // pfnClientDisconnect
ClientKill, // pfnClientKill
ClientPutInServer, // pfnClientPutInServer
ClientCommand, // pfnClientCommand
ClientUserInfoChanged, // pfnClientUserInfoChanged
ServerActivate, //pfnServerActivate
ServerDeactivate, //pfnServerDeactivate
ServerActivate, // pfnServerActivate
ServerDeactivate, // pfnServerDeactivate
PlayerPreThink, //pfnPlayerPreThink
PlayerPostThink, //pfnPlayerPostThink
PlayerPreThink, // pfnPlayerPreThink
PlayerPostThink, // pfnPlayerPostThink
StartFrame, //pfnStartFrame
EndFrame, //pfnEndFrame
ParmsChangeLevel, //pfnParmsChangeLevel
StartFrame, // pfnStartFrame
EndFrame, // pfnEndFrame
BuildLevelList, // pfnBuildLevelList
GetGameDescription, //pfnGetGameDescription Returns string describing current .dll game.
};
@ -207,6 +207,7 @@ void DispatchSave( edict_t *pent, SAVERESTOREDATA *pSaveData )
if ( pEntity && pSaveData )
{
ALERT( at_console, "DispatchSave( %s )\n", STRING( pent->v.classname ));
ENTITYTABLE *pTable = &pSaveData->pTable[ pSaveData->currentIndex ];
if ( pTable->pent != pent )

View File

@ -152,8 +152,34 @@ static int gSizes[FIELD_TYPECOUNT] =
sizeof(int), // FIELD_MODELNAME
sizeof(int), // FIELD_SOUNDNAME
sizeof(float)*2, // FIELD_RANGE
sizeof(int64), // FIELD_INTEGER64
sizeof(double), // FIELD_DOUBLE
};
static const char *gNames[FIELD_TYPECOUNT] =
{
"float", // FIELD_FLOAT
"string", // FIELD_STRING
"entity", // FIELD_ENTITY
"classptr", // FIELD_CLASSPTR
"ehandle", // FIELD_EHANDLE
"entvars", // FIELD_entvars_t
"edict", // FIELD_EDICT
"vector", // FIELD_VECTOR
"position vector", // FIELD_POSITION_VECTOR
"pointer", // FIELD_POINTER
"integer", // FIELD_INTEGER
"function", // FIELD_FUNCTION
"boolean", // FIELD_BOOLEAN
"short", // FIELD_SHORT
"char", // FIELD_CHARACTER
"time", // FIELD_TIME
"modelname", // FIELD_MODELNAME
"soundname", // FIELD_SOUNDNAME
"random range", // FIELD_RANGE
"integer 64", // FIELD_INTEGER64
"double", // FIELD_DOUBLE
};
// Base class includes common SAVERESTOREDATA pointer, and manages the entity table
CSaveRestoreBuffer :: CSaveRestoreBuffer( void )
@ -549,11 +575,19 @@ int CSave :: WriteEntVars( const char *pname, entvars_t *pev )
int CSave :: WriteFields( const char *cname, const char *pname, void *pBaseData, TYPEDESCRIPTION *pFields, int fieldCount )
{
int i, j, actualCount, emptyCount;
int i, j, actualCount, emptyCount;
TYPEDESCRIPTION *pTest;
int entityArray[MAX_ENTITYARRAY];
int entityArray[MAX_ENTITYARRAY];
// Precalculate the number of empty fields
ALERT( at_console, "CSave::WriteFields( %s [%i fields])\n", pname, fieldCount );
if( !strcmp( pname, "Save Header" ) || !strcmp( pname, "ADJACENCY" ) || !strcmp( pname, "Game Header" ))
{
for( i = 0; i < fieldCount; i++ )
ALERT( at_console, "FIELD: %s [%s][0x%x]\n", pFields[i].fieldName, gNames[pFields[i].fieldType], pFields[i].flags );
}
// precalculate the number of empty fields
emptyCount = 0;
for ( i = 0; i < fieldCount; i++ )
{
@ -736,18 +770,18 @@ int CRestore::ReadField( void *pBaseData, TYPEDESCRIPTION *pFields, int fieldCou
time = 0;
position = Vector(0,0,0);
if ( m_pdata )
if( m_pdata )
{
time = m_pdata->time;
if ( m_pdata->fUseLandmark )
position = m_pdata->vecLandmarkOffset;
}
for ( i = 0; i < fieldCount; i++ )
for( i = 0; i < fieldCount; i++ )
{
fieldNumber = (i+startField)%fieldCount;
pTest = &pFields[ fieldNumber ];
if ( !stricmp( pTest->fieldName, pName ) )
fieldNumber = (i + startField) % fieldCount;
pTest = &pFields[fieldNumber];
if( !stricmp( pTest->fieldName, pName ))
{
if ( !m_global || !(pTest->flags & FTYPEDESC_GLOBAL) )
{
@ -910,38 +944,39 @@ int CRestore::ReadFields( const char *pname, void *pBaseData, TYPEDESCRIPTION *p
HEADER header;
i = ReadShort();
ASSERT( i == sizeof(int) ); // First entry should be an int
ASSERT( i == sizeof( int )); // First entry should be an int
token = ReadShort();
// Check the struct name
if ( token != TokenHash(pname) ) // Field Set marker
if( token != TokenHash( pname )) // Field Set marker
{
// ALERT( at_error, "Expected %s found %s!\n", pname, BufferPointer() );
BufferRewind( 2*sizeof(short) );
ALERT( at_error, "Expected %s found %s!\n", pname, BufferPointer() );
BufferRewind( 2 * sizeof( short ));
return 0;
}
ALERT( at_console, "CRestore:ReadFields: %s\n", pname );
// Skip over the struct name
fileCount = ReadInt(); // Read field count
fileCount = ReadInt(); // Read field count
lastField = 0; // Make searches faster, most data is read/written in the same order
lastField = 0; // Make searches faster, most data is read/written in the same order
// Clear out base data
for ( i = 0; i < fieldCount; i++ )
// clear out base data
for( i = 0; i < fieldCount; i++ )
{
// Don't clear global fields
if ( !m_global || !(pFields[i].flags & FTYPEDESC_GLOBAL) )
memset( ((char *)pBaseData + pFields[i].fieldOffset), 0, pFields[i].fieldSize * gSizes[pFields[i].fieldType] );
}
for ( i = 0; i < fileCount; i++ )
{
BufferReadHeader( &header );
lastField = ReadField( pBaseData, pFields, fieldCount, lastField, header.size, m_pdata->pTokens[header.token], header.pData );
lastField++;
}
return 1;
}
@ -953,6 +988,8 @@ void CRestore::BufferReadHeader( HEADER *pheader )
pheader->token = ReadShort(); // Read field name token
pheader->pData = BufferPointer(); // Field Data is next
BufferSkipBytes( pheader->size ); // Advance to next field
ALERT( at_console, "header.token is %i\n", pheader->token );
}

View File

@ -1576,23 +1576,16 @@ edict_t *DBG_EntOfVars( const entvars_t *pev )
#ifdef DEBUG
void
DBG_AssertFunction(
BOOL fExpr,
const char* szExpr,
const char* szFile,
int szLine,
const char* szMessage)
{
if (fExpr)
return;
void DBG_AssertFunction( BOOL fExpr, const char* szExpr, const char* szFile, int szLine, const char* szMessage )
{
if( fExpr ) return;
char szOut[512];
if (szMessage != NULL)
sprintf(szOut, "ASSERT FAILED:\n %s \n(%s@%d)\n%s", szExpr, szFile, szLine, szMessage);
else
sprintf(szOut, "ASSERT FAILED:\n %s \n(%s@%d)", szExpr, szFile, szLine);
ALERT(at_debug, szOut);
}
if( szMessage != NULL )
sprintf( szOut, "ASSERT FAILED:\n %s \n(%s@%d)\n%s", szExpr, szFile, szLine, szMessage );
else sprintf( szOut, "ASSERT FAILED:\n %s \n(%s@%d)", szExpr, szFile, szLine );
HOST_ERROR( szOut );
}
#endif // DEBUG
BOOL UTIL_GetNextBestWeapon( CBasePlayer *pPlayer, CBasePlayerWeapon *pCurrentWeapon )

View File

@ -45,9 +45,33 @@ Beta 13.12.08
24.UpdateClientData - move call to cl_input.c OK
25.wrote HUD_StudioEvents OK
16.register cmd->buttons OK
17.pfnSetKeyDest in client.dll
17.pfnSetKeyDest in client.dll OK
18.IMPLEMENT SAVERESTORE
// savegame algorhytem
1. Init gpGlobals->pSaveData (for save )
{
pBaseData = malloc( 512 kb )
pCurrentData = pBaseData
size = 0
bufferSize = 512 kb
tokenSize = 0
tokenCount = 4096
pTokens = (pTokens **)malloc( (pTokens*) * 4096 ) // pointers array
currentIndex = 0
tableCount = numEnts
pTable = malloc( (ENTITYTABLE) * numEnts )
// also set ENTITYTABLE.id and ENTITTYTABLE.pent
fUseLandmark = 0
szLandmarkName = 0
vecLandmarkOffset = 0
szCurrentMap = 0;
time = sv.time;
}
2. Save Engine Variables
Ñïèñîê äîñòóïíûõ ðåíäåðåðîâ: ×òî â íèõ èíòåðåñíîãî
0. Q3Fusion (Mirrors, Portals)