10 Jan 2009
This commit is contained in:
parent
9a4b42440d
commit
df6f03c2ba
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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++ )
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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" );
|
||||
}
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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" );
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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 );
|
||||
//============================================================
|
||||
|
|
|
@ -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" );
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 );
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 );
|
||||
|
||||
|
|
|
@ -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 )
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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 )
|
||||
|
|
26
todo.log
26
todo.log
|
@ -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)
|
||||
|
|
Reference in New Issue