From 338f9da2a26d0088bc9ba8313a8a732d0fe195ab Mon Sep 17 00:00:00 2001 From: g-cont Date: Sat, 24 Mar 2018 00:00:00 +0300 Subject: [PATCH] 24 Mar 2018 --- dlls/client.cpp | 1 - dlls/hl.plg | 325 +++- engine/client/cl_cmds.c | 2 +- engine/client/cl_demo.c | 4 +- engine/client/cl_frame.c | 16 +- engine/client/cl_gameui.c | 4 +- engine/client/cl_parse.c | 4 +- engine/client/gl_decals.c | 20 +- engine/client/gl_warp.c | 46 +- engine/client/s_main.c | 7 +- engine/common/cmd.c | 2 +- engine/common/com_strings.h | 6 + engine/common/common.h | 5 +- engine/common/con_utils.c | 2 +- engine/common/cvar.c | 2 +- engine/common/imagelib/img_wad.c | 4 +- engine/common/library.h | 2 +- engine/common/net_encode.c | 8 - engine/common/protocol.h | 1 + engine/physint.h | 6 +- engine/server/server.h | 26 +- engine/server/sv_client.c | 2 +- engine/server/sv_cmds.c | 29 +- engine/server/sv_game.c | 46 +- engine/server/sv_init.c | 3 +- engine/server/sv_main.c | 26 +- engine/server/sv_phys.c | 7 - engine/server/sv_pmove.c | 2 +- engine/server/sv_save.c | 2654 ++++++++++++++---------------- engine/server/sv_world.c | 2 +- pm_shared/pm_movevars.h | 4 - 31 files changed, 1710 insertions(+), 1558 deletions(-) diff --git a/dlls/client.cpp b/dlls/client.cpp index a78efd99..38e5a826 100644 --- a/dlls/client.cpp +++ b/dlls/client.cpp @@ -1025,7 +1025,6 @@ int AddToFullPack( struct entity_state_s *state, int e, edict_t *ent, edict_t *h // Don't send entity to local client if the client says it's predicting the entity itself. if ( ent->v.flags & FL_SKIPLOCALHOST ) { - if ( hostflags & 4 ) return 0; // it's a portal pass if ( ( hostflags & 1 ) && ( ent->v.owner == host ) ) return 0; } diff --git a/dlls/hl.plg b/dlls/hl.plg index 5b05c94d..95f34b9a 100644 --- a/dlls/hl.plg +++ b/dlls/hl.plg @@ -3,9 +3,332 @@
 

Build Log

---------------------Configuration: hl - Win32 Release-------------------- +--------------------Configuration: hl - Win32 Debug--------------------

Command Lines

+Creating temporary file "C:\DOCUME~1\ÌÈØÀ\LOCALS~1\Temp\RSPF.tmp" with contents +[ +/nologo /G5 /MTd /W3 /Gm /ZI /Od /I "..\dlls" /I "..\engine" /I "..\common" /I "..\game_shared" /I "..\pm_shared" /I "..\\" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "QUIVER" /D "VOXEL" /D "QUAKE2" /D "VALVE_DLL" /D "CLIENT_WEAPONS" /FR"..\temp\dlls\!debug/" /Fp"..\temp\dlls\!debug/hl.pch" /YX /Fo"..\temp\dlls\!debug/" /Fd"..\temp\dlls\!debug/" /FD /c +"D:\Xash3D\src_main\dlls\aflock.cpp" +"D:\Xash3D\src_main\dlls\agrunt.cpp" +"D:\Xash3D\src_main\dlls\airtank.cpp" +"D:\Xash3D\src_main\dlls\animating.cpp" +"D:\Xash3D\src_main\dlls\animation.cpp" +"D:\Xash3D\src_main\dlls\apache.cpp" +"D:\Xash3D\src_main\dlls\barnacle.cpp" +"D:\Xash3D\src_main\dlls\barney.cpp" +"D:\Xash3D\src_main\dlls\bigmomma.cpp" +"D:\Xash3D\src_main\dlls\bloater.cpp" +"D:\Xash3D\src_main\dlls\bmodels.cpp" +"D:\Xash3D\src_main\dlls\bullsquid.cpp" +"D:\Xash3D\src_main\dlls\buttons.cpp" +"D:\Xash3D\src_main\dlls\cbase.cpp" +"D:\Xash3D\src_main\dlls\client.cpp" +"D:\Xash3D\src_main\dlls\combat.cpp" +"D:\Xash3D\src_main\dlls\controller.cpp" +"D:\Xash3D\src_main\dlls\crossbow.cpp" +"D:\Xash3D\src_main\dlls\crowbar.cpp" +"D:\Xash3D\src_main\dlls\defaultai.cpp" +"D:\Xash3D\src_main\dlls\doors.cpp" +"D:\Xash3D\src_main\dlls\effects.cpp" +"D:\Xash3D\src_main\dlls\egon.cpp" +"D:\Xash3D\src_main\dlls\explode.cpp" +"D:\Xash3D\src_main\dlls\flyingmonster.cpp" +"D:\Xash3D\src_main\dlls\func_break.cpp" +"D:\Xash3D\src_main\dlls\func_tank.cpp" +"D:\Xash3D\src_main\dlls\game.cpp" +"D:\Xash3D\src_main\dlls\gamerules.cpp" +"D:\Xash3D\src_main\dlls\gargantua.cpp" +"D:\Xash3D\src_main\dlls\gauss.cpp" +"D:\Xash3D\src_main\dlls\genericmonster.cpp" +"D:\Xash3D\src_main\dlls\ggrenade.cpp" +"D:\Xash3D\src_main\dlls\globals.cpp" +"D:\Xash3D\src_main\dlls\glock.cpp" +"D:\Xash3D\src_main\dlls\gman.cpp" +"D:\Xash3D\src_main\dlls\h_ai.cpp" +"D:\Xash3D\src_main\dlls\h_battery.cpp" +"D:\Xash3D\src_main\dlls\h_cine.cpp" +"D:\Xash3D\src_main\dlls\h_cycler.cpp" +"D:\Xash3D\src_main\dlls\h_export.cpp" +"D:\Xash3D\src_main\dlls\handgrenade.cpp" +"D:\Xash3D\src_main\dlls\hassassin.cpp" +"D:\Xash3D\src_main\dlls\headcrab.cpp" +"D:\Xash3D\src_main\dlls\healthkit.cpp" +"D:\Xash3D\src_main\dlls\hgrunt.cpp" +"D:\Xash3D\src_main\dlls\hornet.cpp" +"D:\Xash3D\src_main\dlls\hornetgun.cpp" +"D:\Xash3D\src_main\dlls\houndeye.cpp" +"D:\Xash3D\src_main\dlls\ichthyosaur.cpp" +"D:\Xash3D\src_main\dlls\islave.cpp" +"D:\Xash3D\src_main\dlls\items.cpp" +"D:\Xash3D\src_main\dlls\leech.cpp" +"D:\Xash3D\src_main\dlls\lights.cpp" +"D:\Xash3D\src_main\dlls\maprules.cpp" +"D:\Xash3D\src_main\dlls\monstermaker.cpp" +"D:\Xash3D\src_main\dlls\monsters.cpp" +"D:\Xash3D\src_main\dlls\monsterstate.cpp" +"D:\Xash3D\src_main\dlls\mortar.cpp" +"D:\Xash3D\src_main\dlls\mp5.cpp" +"D:\Xash3D\src_main\dlls\multiplay_gamerules.cpp" +"D:\Xash3D\src_main\dlls\nihilanth.cpp" +"D:\Xash3D\src_main\dlls\nodes.cpp" +"D:\Xash3D\src_main\dlls\osprey.cpp" +"D:\Xash3D\src_main\dlls\pathcorner.cpp" +"D:\Xash3D\src_main\dlls\plane.cpp" +"D:\Xash3D\src_main\dlls\plats.cpp" +"D:\Xash3D\src_main\dlls\player.cpp" +"D:\Xash3D\src_main\pm_shared\pm_debug.c" +"D:\Xash3D\src_main\pm_shared\pm_math.c" +"D:\Xash3D\src_main\pm_shared\pm_shared.c" +"D:\Xash3D\src_main\dlls\python.cpp" +"D:\Xash3D\src_main\dlls\rat.cpp" +"D:\Xash3D\src_main\dlls\roach.cpp" +"D:\Xash3D\src_main\dlls\rpg.cpp" +"D:\Xash3D\src_main\dlls\satchel.cpp" +"D:\Xash3D\src_main\dlls\schedule.cpp" +"D:\Xash3D\src_main\dlls\scientist.cpp" +"D:\Xash3D\src_main\dlls\scripted.cpp" +"D:\Xash3D\src_main\dlls\shotgun.cpp" +"D:\Xash3D\src_main\dlls\singleplay_gamerules.cpp" +"D:\Xash3D\src_main\dlls\skill.cpp" +"D:\Xash3D\src_main\dlls\sound.cpp" +"D:\Xash3D\src_main\dlls\soundent.cpp" +"D:\Xash3D\src_main\dlls\spectator.cpp" +"D:\Xash3D\src_main\dlls\squadmonster.cpp" +"D:\Xash3D\src_main\dlls\squeakgrenade.cpp" +"D:\Xash3D\src_main\dlls\subs.cpp" +"D:\Xash3D\src_main\dlls\talkmonster.cpp" +"D:\Xash3D\src_main\dlls\teamplay_gamerules.cpp" +"D:\Xash3D\src_main\dlls\tentacle.cpp" +"D:\Xash3D\src_main\dlls\triggers.cpp" +"D:\Xash3D\src_main\dlls\tripmine.cpp" +"D:\Xash3D\src_main\dlls\turret.cpp" +"D:\Xash3D\src_main\dlls\util.cpp" +"D:\Xash3D\src_main\game_shared\voice_gamemgr.cpp" +"D:\Xash3D\src_main\dlls\weapons.cpp" +"D:\Xash3D\src_main\dlls\world.cpp" +"D:\Xash3D\src_main\dlls\xen.cpp" +"D:\Xash3D\src_main\dlls\zombie.cpp" +] +Creating command line "cl.exe @"C:\DOCUME~1\ÌÈØÀ\LOCALS~1\Temp\RSPF.tmp"" +Creating temporary file "C:\DOCUME~1\ÌÈØÀ\LOCALS~1\Temp\RSP10.tmp" with contents +[ +user32.lib advapi32.lib /nologo /subsystem:windows /dll /incremental:yes /pdb:"..\temp\dlls\!debug/hl.pdb" /debug /machine:I386 /def:".\hl.def" /out:"..\temp\dlls\!debug/hl.dll" /implib:"..\temp\dlls\!debug/hl.lib" +"\Xash3D\src_main\temp\dlls\!debug\aflock.obj" +"\Xash3D\src_main\temp\dlls\!debug\agrunt.obj" +"\Xash3D\src_main\temp\dlls\!debug\airtank.obj" +"\Xash3D\src_main\temp\dlls\!debug\animating.obj" +"\Xash3D\src_main\temp\dlls\!debug\animation.obj" +"\Xash3D\src_main\temp\dlls\!debug\apache.obj" +"\Xash3D\src_main\temp\dlls\!debug\barnacle.obj" +"\Xash3D\src_main\temp\dlls\!debug\barney.obj" +"\Xash3D\src_main\temp\dlls\!debug\bigmomma.obj" +"\Xash3D\src_main\temp\dlls\!debug\bloater.obj" +"\Xash3D\src_main\temp\dlls\!debug\bmodels.obj" +"\Xash3D\src_main\temp\dlls\!debug\bullsquid.obj" +"\Xash3D\src_main\temp\dlls\!debug\buttons.obj" +"\Xash3D\src_main\temp\dlls\!debug\cbase.obj" +"\Xash3D\src_main\temp\dlls\!debug\client.obj" +"\Xash3D\src_main\temp\dlls\!debug\combat.obj" +"\Xash3D\src_main\temp\dlls\!debug\controller.obj" +"\Xash3D\src_main\temp\dlls\!debug\crossbow.obj" +"\Xash3D\src_main\temp\dlls\!debug\crowbar.obj" +"\Xash3D\src_main\temp\dlls\!debug\defaultai.obj" +"\Xash3D\src_main\temp\dlls\!debug\doors.obj" +"\Xash3D\src_main\temp\dlls\!debug\effects.obj" +"\Xash3D\src_main\temp\dlls\!debug\egon.obj" +"\Xash3D\src_main\temp\dlls\!debug\explode.obj" +"\Xash3D\src_main\temp\dlls\!debug\flyingmonster.obj" +"\Xash3D\src_main\temp\dlls\!debug\func_break.obj" +"\Xash3D\src_main\temp\dlls\!debug\func_tank.obj" +"\Xash3D\src_main\temp\dlls\!debug\game.obj" +"\Xash3D\src_main\temp\dlls\!debug\gamerules.obj" +"\Xash3D\src_main\temp\dlls\!debug\gargantua.obj" +"\Xash3D\src_main\temp\dlls\!debug\gauss.obj" +"\Xash3D\src_main\temp\dlls\!debug\genericmonster.obj" +"\Xash3D\src_main\temp\dlls\!debug\ggrenade.obj" +"\Xash3D\src_main\temp\dlls\!debug\globals.obj" +"\Xash3D\src_main\temp\dlls\!debug\glock.obj" +"\Xash3D\src_main\temp\dlls\!debug\gman.obj" +"\Xash3D\src_main\temp\dlls\!debug\h_ai.obj" +"\Xash3D\src_main\temp\dlls\!debug\h_battery.obj" +"\Xash3D\src_main\temp\dlls\!debug\h_cine.obj" +"\Xash3D\src_main\temp\dlls\!debug\h_cycler.obj" +"\Xash3D\src_main\temp\dlls\!debug\h_export.obj" +"\Xash3D\src_main\temp\dlls\!debug\handgrenade.obj" +"\Xash3D\src_main\temp\dlls\!debug\hassassin.obj" +"\Xash3D\src_main\temp\dlls\!debug\headcrab.obj" +"\Xash3D\src_main\temp\dlls\!debug\healthkit.obj" +"\Xash3D\src_main\temp\dlls\!debug\hgrunt.obj" +"\Xash3D\src_main\temp\dlls\!debug\hornet.obj" +"\Xash3D\src_main\temp\dlls\!debug\hornetgun.obj" +"\Xash3D\src_main\temp\dlls\!debug\houndeye.obj" +"\Xash3D\src_main\temp\dlls\!debug\ichthyosaur.obj" +"\Xash3D\src_main\temp\dlls\!debug\islave.obj" +"\Xash3D\src_main\temp\dlls\!debug\items.obj" +"\Xash3D\src_main\temp\dlls\!debug\leech.obj" +"\Xash3D\src_main\temp\dlls\!debug\lights.obj" +"\Xash3D\src_main\temp\dlls\!debug\maprules.obj" +"\Xash3D\src_main\temp\dlls\!debug\monstermaker.obj" +"\Xash3D\src_main\temp\dlls\!debug\monsters.obj" +"\Xash3D\src_main\temp\dlls\!debug\monsterstate.obj" +"\Xash3D\src_main\temp\dlls\!debug\mortar.obj" +"\Xash3D\src_main\temp\dlls\!debug\mp5.obj" +"\Xash3D\src_main\temp\dlls\!debug\multiplay_gamerules.obj" +"\Xash3D\src_main\temp\dlls\!debug\nihilanth.obj" +"\Xash3D\src_main\temp\dlls\!debug\nodes.obj" +"\Xash3D\src_main\temp\dlls\!debug\osprey.obj" +"\Xash3D\src_main\temp\dlls\!debug\pathcorner.obj" +"\Xash3D\src_main\temp\dlls\!debug\plane.obj" +"\Xash3D\src_main\temp\dlls\!debug\plats.obj" +"\Xash3D\src_main\temp\dlls\!debug\player.obj" +"\Xash3D\src_main\temp\dlls\!debug\pm_debug.obj" +"\Xash3D\src_main\temp\dlls\!debug\pm_math.obj" +"\Xash3D\src_main\temp\dlls\!debug\pm_shared.obj" +"\Xash3D\src_main\temp\dlls\!debug\python.obj" +"\Xash3D\src_main\temp\dlls\!debug\rat.obj" +"\Xash3D\src_main\temp\dlls\!debug\roach.obj" +"\Xash3D\src_main\temp\dlls\!debug\rpg.obj" +"\Xash3D\src_main\temp\dlls\!debug\satchel.obj" +"\Xash3D\src_main\temp\dlls\!debug\schedule.obj" +"\Xash3D\src_main\temp\dlls\!debug\scientist.obj" +"\Xash3D\src_main\temp\dlls\!debug\scripted.obj" +"\Xash3D\src_main\temp\dlls\!debug\shotgun.obj" +"\Xash3D\src_main\temp\dlls\!debug\singleplay_gamerules.obj" +"\Xash3D\src_main\temp\dlls\!debug\skill.obj" +"\Xash3D\src_main\temp\dlls\!debug\sound.obj" +"\Xash3D\src_main\temp\dlls\!debug\soundent.obj" +"\Xash3D\src_main\temp\dlls\!debug\spectator.obj" +"\Xash3D\src_main\temp\dlls\!debug\squadmonster.obj" +"\Xash3D\src_main\temp\dlls\!debug\squeakgrenade.obj" +"\Xash3D\src_main\temp\dlls\!debug\subs.obj" +"\Xash3D\src_main\temp\dlls\!debug\talkmonster.obj" +"\Xash3D\src_main\temp\dlls\!debug\teamplay_gamerules.obj" +"\Xash3D\src_main\temp\dlls\!debug\tempmonster.obj" +"\Xash3D\src_main\temp\dlls\!debug\tentacle.obj" +"\Xash3D\src_main\temp\dlls\!debug\triggers.obj" +"\Xash3D\src_main\temp\dlls\!debug\tripmine.obj" +"\Xash3D\src_main\temp\dlls\!debug\turret.obj" +"\Xash3D\src_main\temp\dlls\!debug\util.obj" +"\Xash3D\src_main\temp\dlls\!debug\voice_gamemgr.obj" +"\Xash3D\src_main\temp\dlls\!debug\weapons.obj" +"\Xash3D\src_main\temp\dlls\!debug\world.obj" +"\Xash3D\src_main\temp\dlls\!debug\xen.obj" +"\Xash3D\src_main\temp\dlls\!debug\zombie.obj" +] +Creating command line "link.exe @"C:\DOCUME~1\ÌÈØÀ\LOCALS~1\Temp\RSP10.tmp"" +Creating temporary file "C:\DOCUME~1\ÌÈØÀ\LOCALS~1\Temp\RSP11.bat" with contents +[ +@echo off +copy \Xash3D\src_main\temp\dlls\!debug\hl.dll "D:\Xash3D\valve\dlls\hl.dll" +] +Creating command line ""C:\DOCUME~1\ÌÈØÀ\LOCALS~1\Temp\RSP11.bat"" +Compiling... +aflock.cpp +agrunt.cpp +airtank.cpp +animating.cpp +client.cpp +crossbow.cpp +game.cpp +player.cpp +pm_debug.c +pm_math.c +pm_shared.c +rpg.cpp +animation.cpp +apache.cpp +barnacle.cpp +barney.cpp +bigmomma.cpp +bloater.cpp +bmodels.cpp +bullsquid.cpp +buttons.cpp +cbase.cpp +combat.cpp +controller.cpp +crowbar.cpp +defaultai.cpp +doors.cpp +effects.cpp +egon.cpp +explode.cpp +flyingmonster.cpp +func_break.cpp +func_tank.cpp +gamerules.cpp +gargantua.cpp +gauss.cpp +genericmonster.cpp +ggrenade.cpp +globals.cpp +glock.cpp +gman.cpp +h_ai.cpp +h_battery.cpp +h_cine.cpp +h_cycler.cpp +h_export.cpp +handgrenade.cpp +hassassin.cpp +headcrab.cpp +healthkit.cpp +hgrunt.cpp +hornet.cpp +hornetgun.cpp +houndeye.cpp +ichthyosaur.cpp +islave.cpp +items.cpp +leech.cpp +lights.cpp +maprules.cpp +monstermaker.cpp +monsters.cpp +monsterstate.cpp +mortar.cpp +mp5.cpp +multiplay_gamerules.cpp +nihilanth.cpp +nodes.cpp +osprey.cpp +pathcorner.cpp +plane.cpp +plats.cpp +python.cpp +rat.cpp +roach.cpp +satchel.cpp +schedule.cpp +scientist.cpp +scripted.cpp +shotgun.cpp +singleplay_gamerules.cpp +skill.cpp +sound.cpp +soundent.cpp +spectator.cpp +squadmonster.cpp +squeakgrenade.cpp +subs.cpp +talkmonster.cpp +teamplay_gamerules.cpp +tentacle.cpp +triggers.cpp +tripmine.cpp +turret.cpp +util.cpp +voice_gamemgr.cpp +weapons.cpp +world.cpp +xen.cpp +zombie.cpp +Linking... + Creating library ..\temp\dlls\!debug/hl.lib and object ..\temp\dlls\!debug/hl.exp +

Output Window

+Performing Custom Build Step on \Xash3D\src_main\temp\dlls\!debug\hl.dll +‘ª®¯¨à®¢ ­® ä ©«®¢: 1. diff --git a/engine/client/cl_cmds.c b/engine/client/cl_cmds.c index 12c7ab3d..7c753250 100644 --- a/engine/client/cl_cmds.c +++ b/engine/client/cl_cmds.c @@ -381,7 +381,7 @@ void CL_SaveShot_f( void ) return; } - Q_sprintf( cls.shotname, "save/%s.bmp", Cmd_Argv( 1 )); + Q_sprintf( cls.shotname, "%s%s.bmp", DEFAULT_SAVE_DIRECTORY, Cmd_Argv( 1 )); cls.scrshot_action = scrshot_savegame; // build new frame for saveshot } diff --git a/engine/client/cl_demo.c b/engine/client/cl_demo.c index 8a649a13..67f7046b 100644 --- a/engine/client/cl_demo.c +++ b/engine/client/cl_demo.c @@ -1011,10 +1011,10 @@ void CL_StopPlayback( void ) /* ================== -CL_GetComment +CL_GetDemoComment ================== */ -qboolean CL_GetComment( const char *demoname, char *comment ) +qboolean CL_GetDemoComment( const char *demoname, char *comment ) { file_t *demfile; demoheader_t demohdr; diff --git a/engine/client/cl_frame.c b/engine/client/cl_frame.c index 9068da23..4492e7b0 100644 --- a/engine/client/cl_frame.c +++ b/engine/client/cl_frame.c @@ -1272,7 +1272,11 @@ qboolean CL_GetEntitySpatialization( channel_t *ch ) cl_entity_t *ent; qboolean valid_origin; - if( ch->entnum == 0 ) return true; // static sound + if( ch->entnum == 0 ) + { + ch->staticsound = true; + return true; // static sound + } if(( ch->entnum - 1 ) == cl.playernum ) { @@ -1284,15 +1288,9 @@ qboolean CL_GetEntitySpatialization( channel_t *ch ) ent = CL_GetEntityByIndex( ch->entnum ); // entity is not present on the client but has valid origin - if( !ent || !ent->index ) return valid_origin; - - if( ent->curstate.messagenum == 0 ) - { - // entity is never has updates on the client - // so we should use static origin instead - ch->staticsound = true; + if( !ent || !ent->index || ent->curstate.messagenum == 0 ) return valid_origin; - } + #if 0 // uncomment this if you want enable additional check by PVS if( ent->curstate.messagenum != cl.parsecount ) diff --git a/engine/client/cl_gameui.c b/engine/client/cl_gameui.c index 03313c30..69b35664 100644 --- a/engine/client/cl_gameui.c +++ b/engine/client/cl_gameui.c @@ -969,8 +969,8 @@ static ui_enginefuncs_t gEngfuncs = pfnGetGameInfo, pfnGetGamesList, pfnGetFilesList, - SV_GetComment, - CL_GetComment, + SV_GetSaveComment, + CL_GetDemoComment, pfnCheckGameDll, pfnGetClipboardData, Sys_ShellExecute, diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index db425cb7..59e449c1 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -509,8 +509,8 @@ void CL_ParseStaticEntity( sizebuf_t *msg ) memset( &state, 0, sizeof( state )); state.modelindex = MSG_ReadShort( msg ); - state.sequence = MSG_ReadByte( msg ); - state.frame = MSG_ReadByte( msg ); + state.sequence = MSG_ReadWord( msg ); + state.frame = MSG_ReadWord( msg ) * (1.0f / 128.0f); state.colormap = MSG_ReadWord( msg ); state.skin = MSG_ReadByte( msg ); state.body = MSG_ReadByte( msg ); diff --git a/engine/client/gl_decals.c b/engine/client/gl_decals.c index cfba6f4f..7ee77952 100644 --- a/engine/client/gl_decals.c +++ b/engine/client/gl_decals.c @@ -624,7 +624,7 @@ void R_DecalSurface( msurface_t *surf, decalinfo_t *decalinfo ) float s, t, w, h; // we in restore mode - if( cls.state == ca_connected ) + if( cls.state == ca_connected || cls.state == ca_validate ) { // NOTE: we may have the decal on this surface that come from another level. // check duplicate with same position and texture @@ -790,7 +790,9 @@ void R_DecalShoot( int textureIndex, int entityIndex, int modelIndex, vec3_t pos decalInfo.m_pModel = model; hull = &model->hulls[0]; // always use #0 hull - if( ent && !( flags & FDECAL_LOCAL_SPACE )) + // NOTE: all the decals at 'first shoot' placed into local space of parent entity + // and won't transform again on a next restore, levelchange etc + if( ent && !FBitSet( flags, FDECAL_LOCAL_SPACE )) { vec3_t pos_l; @@ -808,19 +810,19 @@ void R_DecalShoot( int textureIndex, int entityIndex, int modelIndex, vec3_t pos } VectorCopy( pos_l, decalInfo.m_Position ); - flags |= FDECAL_LOCAL_SPACE; // decal position moved into local space + // decal position moved into local space + SetBits( flags, FDECAL_LOCAL_SPACE ); } else { - // pass position in global + // already in local space VectorCopy( pos, decalInfo.m_Position ); } // this decal must use landmark for correct transition - if(!( model->flags & MODEL_HAS_ORIGIN )) - { - flags |= FDECAL_USE_LANDMARK; - } + // because their model exist only in world-space + if( !FBitSet( model->flags, MODEL_HAS_ORIGIN )) + SetBits( flags, FDECAL_USE_LANDMARK ); // more state used by R_DecalNode() decalInfo.m_iTexture = textureIndex; @@ -1173,7 +1175,7 @@ int R_CreateDecalList( decallist_t *pList ) decal_t *pdecals; // decal is in use and is not a custom decal - if( decal->psurface == NULL || ( decal->flags & FDECAL_DONTSAVE )) + if( decal->psurface == NULL || FBitSet( decal->flags, FDECAL_DONTSAVE )) continue; // compute depth diff --git a/engine/client/gl_warp.c b/engine/client/gl_warp.c index b8179300..f2aaeff2 100644 --- a/engine/client/gl_warp.c +++ b/engine/client/gl_warp.c @@ -311,16 +311,6 @@ void R_AddSkyBoxSurface( msurface_t *fa ) float *v; int i; - if( clgame.movevars.skyangle ) - { - // force full sky to draw when it has angle - for( i = 0; i < 6; i++ ) - { - RI.skyMins[0][i] = RI.skyMins[1][i] = -1.0f; - RI.skyMaxs[0][i] = RI.skyMaxs[1][i] = 1.0f; - } - } - if( FBitSet( world.flags, FWORLD_SKYSPHERE ) && fa->polys && !FBitSet( world.flags, FWORLD_CUSTOM_SKYBOX )) { glpoly_t *p = fa->polys; @@ -377,18 +367,6 @@ void R_DrawSkyBox( void ) { int i; - if( clgame.movevars.skyangle ) - { - // check for no sky at all - for( i = 0; i < 6; i++ ) - { - if( RI.skyMins[0][i] < RI.skyMaxs[0][i] && RI.skyMins[1][i] < RI.skyMaxs[1][i] ) - break; - } - - if( i == 6 ) return; // nothing visible - } - RI.isSkyVisible = true; // don't fogging skybox (this fix old Half-Life bug) @@ -398,16 +376,6 @@ void R_DrawSkyBox( void ) pglDisable( GL_ALPHA_TEST ); pglTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE ); - if( clgame.movevars.skyangle && !VectorIsNull( (float *)&clgame.movevars.skydir_x )) - { - matrix4x4 m; - Matrix4x4_CreateRotate( m, clgame.movevars.skyangle, clgame.movevars.skydir_x, clgame.movevars.skydir_y, clgame.movevars.skydir_z ); - Matrix4x4_ConcatTranslate( m, -RI.vieworg[0], -RI.vieworg[1], -RI.vieworg[2] ); - Matrix4x4_ConcatTransforms( RI.modelviewMatrix, RI.worldviewMatrix, m ); - GL_LoadMatrix( RI.modelviewMatrix ); - tr.modelviewIdentity = false; - } - for( i = 0; i < 6; i++ ) { if( RI.skyMins[0][i] >= RI.skyMaxs[0][i] || RI.skyMins[1][i] >= RI.skyMaxs[1][i] ) @@ -437,11 +405,11 @@ R_SetupSky */ void R_SetupSky( const char *skyboxname ) { - string loadname; - string sidename; - int i = 0, result; + char loadname[MAX_QPATH]; + char sidename[MAX_QPATH]; + int i, result; - if( !skyboxname || !*skyboxname ) + if( !COM_CheckString( skyboxname )) { R_UnloadSkybox(); return; // clear old skybox @@ -456,9 +424,9 @@ void R_SetupSky( const char *skyboxname ) result = CheckSkybox( loadname ); // to prevent infinite recursion if default skybox was missed - if( result == SKYBOX_MISSED && Q_stricmp( loadname, "gfx/env/desert" )) + if( result == SKYBOX_MISSED && Q_stricmp( loadname, DEFAULT_SKYBOX_PATH )) { - MsgDev( D_ERROR, "missed or incomplete skybox '%s'\n", skyboxname ); + Con_Reportf( S_WARN "missed or incomplete skybox '%s'\n", skyboxname ); R_SetupSky( "desert" ); // force to default return; } @@ -485,7 +453,7 @@ void R_SetupSky( const char *skyboxname ) return; // loaded } - MsgDev( D_ERROR, "couldn't load skybox '%s'\n", skyboxname ); + Con_DPrintf( "^2failed\n" ); R_UnloadSkybox(); } diff --git a/engine/client/s_main.c b/engine/client/s_main.c index 8edab24e..abba5707 100644 --- a/engine/client/s_main.c +++ b/engine/client/s_main.c @@ -1026,14 +1026,13 @@ void S_RestoreSound( const vec3_t pos, int ent, int chan, sound_t handle, float } // pick a channel to play on - if( chan == CHAN_STATIC ) - target_chan = SND_PickStaticChannel( pos, sfx ); + if( chan == CHAN_STATIC ) target_chan = SND_PickStaticChannel( pos, sfx ); else target_chan = SND_PickDynamicChannel( ent, chan, sfx, &bIgnore ); if( !target_chan ) { if( !bIgnore ) - Con_DPrintf( S_ERROR "S_RestoreSound: dropped sound \"%s%s\"\n", DEFAULT_SOUNDPATH, sfx->name ); + Con_DPrintf( S_ERROR "dropped sound \"%s%s\"\n", DEFAULT_SOUNDPATH, sfx->name ); return; } @@ -1070,8 +1069,6 @@ void S_RestoreSound( const vec3_t pos, int ent, int chan, sound_t handle, float // prepended with a '!'. Sentence names stored in the // sentence file do not have a leading '!'. VOX_LoadSound( target_chan, S_SkipSoundChar( sfx->name )); - - // save the sentencename for future save\restores Q_strncpy( target_chan->name, sfx->name, sizeof( target_chan->name )); // not a first word in sentence! diff --git a/engine/common/cmd.c b/engine/common/cmd.c index 7670347c..241c1bac 100644 --- a/engine/common/cmd.c +++ b/engine/common/cmd.c @@ -1107,7 +1107,7 @@ void Cmd_Unlink( int group ) count++; } - Con_DPrintf( "unlink %i commands\n", count ); + Con_Reportf( "unlink %i commands\n", count ); } /* diff --git a/engine/common/com_strings.h b/engine/common/com_strings.h index f58740ad..6f2e9989 100644 --- a/engine/common/com_strings.h +++ b/engine/common/com_strings.h @@ -44,6 +44,12 @@ GNU General Public License for more details. // path to folders where placed all sounds #define DEFAULT_SOUNDPATH "sound/" +// path store saved games +#define DEFAULT_SAVE_DIRECTORY "save/" + +// fallback to this skybox +#define DEFAULT_SKYBOX_PATH "gfx/env/desert" + // playlist for startup videos #define DEFAULT_VIDEOLIST_PATH "media/StartupVids.txt" diff --git a/engine/common/common.h b/engine/common/common.h index 7f230b7e..acde8d36 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -865,7 +865,7 @@ qboolean CL_DisableVisibility( void ); int CL_PointContents( const vec3_t point ); char *COM_ParseFile( char *data, char *token ); byte *COM_LoadFile( const char *filename, int usehunk, int *pLength ); -qboolean CL_GetComment( const char *demoname, char *comment ); +qboolean CL_GetDemoComment( const char *demoname, char *comment ); void COM_AddAppDirectoryToSearchPath( const char *pszBaseDir, const char *appName ); int COM_ExpandFilename( const char *fileName, char *nameOutBuffer, int nameOutBufferSize ); struct pmtrace_s *PM_TraceLine( float *start, float *end, int flags, int usehull, int ignore_pe ); @@ -901,7 +901,7 @@ qboolean CL_IsBackgroundDemo( void ); qboolean CL_IsBackgroundMap( void ); qboolean SV_Initialized( void ); qboolean CL_LoadProgs( const char *name ); -qboolean SV_GetComment( const char *savename, char *comment ); +qboolean SV_GetSaveComment( const char *savename, char *comment ); qboolean SV_NewGame( const char *mapName, qboolean loadGame ); void SV_ClipPMoveToEntity( struct physent_s *pe, const vec3_t start, vec3_t mins, vec3_t maxs, const vec3_t end, struct pmtrace_s *tr ); void CL_ClipPMoveToEntity( struct physent_s *pe, const vec3_t start, vec3_t mins, vec3_t maxs, const vec3_t end, struct pmtrace_s *tr ); @@ -911,7 +911,6 @@ void SV_ShutdownGame( void ); void SV_ExecLoadLevel( void ); void SV_ExecLoadGame( void ); void SV_ExecChangeLevel( void ); -void SV_ClearSaveDir( void ); void SV_InitGameProgs( void ); void SV_FreeGameProgs( void ); void CL_WriteMessageHistory( void ); diff --git a/engine/common/con_utils.c b/engine/common/con_utils.c index f0bf36a3..e595cac5 100644 --- a/engine/common/con_utils.c +++ b/engine/common/con_utils.c @@ -332,7 +332,7 @@ qboolean Cmd_GetSavesList( const char *s, char *completedname, int length ) string matchbuf; int i, numsaves; - t = FS_Search( va( "save/%s*.sav", s ), true, true ); // lookup only in gamedir + t = FS_Search( va( "%s%s*.sav", DEFAULT_SAVE_DIRECTORY, s ), true, true ); // lookup only in gamedir if( !t ) return false; COM_FileBase( t->filenames[0], matchbuf ); diff --git a/engine/common/cvar.c b/engine/common/cvar.c index 325ce737..5b82bfa9 100644 --- a/engine/common/cvar.c +++ b/engine/common/cvar.c @@ -866,7 +866,7 @@ void Cvar_Unlink( int group ) return; count = Cvar_UnlinkVar( NULL, group ); - Con_DPrintf( "unlink %i cvars\n", count ); + Con_Reportf( "unlink %i cvars\n", count ); } /* diff --git a/engine/common/imagelib/img_wad.c b/engine/common/imagelib/img_wad.c index 38f9bc3f..c2c10f38 100644 --- a/engine/common/imagelib/img_wad.c +++ b/engine/common/imagelib/img_wad.c @@ -268,11 +268,11 @@ qboolean Image_LoadLMP( const char *name, const byte *buffer, size_t filesize ) return false; } - // greatest hack from valve software (particle palette) + // valve software trick (particle palette) if( Q_stristr( name, "palette.lmp" )) return Image_LoadPAL( name, buffer, filesize ); - // greatest hack from id software (image without header) + // id software trick (image without header) if( image.hint != IL_HINT_HL && Q_stristr( name, "conchars" )) { image.width = image.height = 128; diff --git a/engine/common/library.h b/engine/common/library.h index 18ada70f..ca380280 100644 --- a/engine/common/library.h +++ b/engine/common/library.h @@ -136,7 +136,7 @@ typedef struct typedef struct dll_user_s { - void *hInstance; // to avoid possible hacks + void *hInstance; // instance handle qboolean custom_loader; // a bit who indicated loader type qboolean encrypted; // dll is crypted (some client.dll in HL, CS etc) char dllName[32]; // for debug messages diff --git a/engine/common/net_encode.c b/engine/common/net_encode.c index 3f6dfa14..c5e02c0c 100644 --- a/engine/common/net_encode.c +++ b/engine/common/net_encode.c @@ -81,10 +81,6 @@ static const delta_field_t pm_fields[] = { PHYS_DEF( skyvec_z ) }, { PHYS_DEF( fog_settings ) }, { PHYS_DEF( wateralpha ) }, -{ PHYS_DEF( skydir_x ) }, -{ PHYS_DEF( skydir_y ) }, -{ PHYS_DEF( skydir_z ) }, -{ PHYS_DEF( skyangle ) }, { NULL }, }; @@ -823,10 +819,6 @@ void Delta_Init( void ) Delta_AddField( "movevars_t", "skyvec_x", DT_FLOAT|DT_SIGNED, 16, 32.0f, 1.0f ); // 0 - 1 Delta_AddField( "movevars_t", "skyvec_y", DT_FLOAT|DT_SIGNED, 16, 32.0f, 1.0f ); Delta_AddField( "movevars_t", "skyvec_z", DT_FLOAT|DT_SIGNED, 16, 32.0f, 1.0f ); - Delta_AddField( "movevars_t", "skydir_x", DT_FLOAT|DT_SIGNED, 16, 32.0f, 1.0f ); // 0 - 1 - Delta_AddField( "movevars_t", "skydir_y", DT_FLOAT|DT_SIGNED, 16, 32.0f, 1.0f ); - Delta_AddField( "movevars_t", "skydir_z", DT_FLOAT|DT_SIGNED, 16, 32.0f, 1.0f ); - Delta_AddField( "movevars_t", "skyangle", DT_ANGLE, 16, 1.0f, 1.0f ); // 0 - 360 Delta_AddField( "movevars_t", "wateralpha", DT_FLOAT|DT_SIGNED, 16, 32.0f, 1.0f ); Delta_AddField( "movevars_t", "fog_settings", DT_INTEGER, 32, 1.0f, 1.0f ); diff --git a/engine/common/protocol.h b/engine/common/protocol.h index 3f9f2d87..67deaea9 100644 --- a/engine/common/protocol.h +++ b/engine/common/protocol.h @@ -150,6 +150,7 @@ GNU General Public License for more details. #define SND_LOCALSOUND (1<<9) // not paused, not looped, for internal use #define SND_STOP_LOOPING (1<<10) // stop all looping sounds on the entity. #define SND_FILTER_CLIENT (1<<11) // don't send sound from local player if prediction was enabled +#define SND_RESTORE_POSITION (1<<12) // passed playing position and the forced end // decal flags #define FDECAL_PERMANENT 0x01 // This decal should not be removed in favor of any new decals diff --git a/engine/physint.h b/engine/physint.h index 8a79e9cc..9f557d19 100644 --- a/engine/physint.h +++ b/engine/physint.h @@ -139,10 +139,10 @@ typedef struct physics_interface_s void ( *ClipPMoveToEntity)( struct physent_s *pe, const float *start, float *mins, float *maxs, const float *end, struct pmtrace_s *tr ); // called at end the frame of SV_Physics call void ( *SV_EndFrame )( void ); + // obsolete + void (*pfnReserved)( void ); // called through save\restore process - void (*pfnCreateEntitiesInTransitionList)( SAVERESTOREDATA*, int levelMask ); - // called through save\restore process - void (*pfnCreateEntitiesInRestoreList)( SAVERESTOREDATA* ); + void (*pfnCreateEntitiesInRestoreList)( SAVERESTOREDATA *pSaveData, int levelMask, qboolean create_world ); // allocate custom string (e.g. using user implementation of stringtable, not engine strings) string_t (*pfnAllocString)( const char *szValue ); // make custom string (e.g. using user implementation of stringtable, not engine strings) diff --git a/engine/server/server.h b/engine/server/server.h index a6e6be4b..034dd925 100644 --- a/engine/server/server.h +++ b/engine/server/server.h @@ -36,8 +36,7 @@ extern int SV_UPDATE_BACKUP; // hostflags #define SVF_SKIPLOCALHOST BIT( 0 ) -#define SVF_PLAYERSONLY BIT( 1 ) -#define SVF_MERGE_VISIBILITY BIT( 2 ) // we are do portal pass +#define SVF_MERGE_VISIBILITY BIT( 1 ) // we are do portal pass // mapvalid flags #define MAP_IS_EXIST BIT( 0 ) @@ -120,8 +119,8 @@ typedef struct char model[MAX_QPATH]; // name of static-entity model for right precache vec3_t origin; vec3_t angles; - byte sequence; - byte frame; + short sequence; + short frame; short colormap; byte skin; // can't set contents! only real skin! byte body; @@ -197,6 +196,7 @@ typedef struct server_s model_t *worldmodel; // pointer to world qboolean simulating; + qboolean playersonly; qboolean paused; // statistics @@ -424,13 +424,19 @@ extern convar_t sv_edgefriction; extern convar_t sv_gravity; extern convar_t sv_stopspeed; extern convar_t sv_maxspeed; +extern convar_t sv_wateralpha; +extern convar_t sv_wateramp; extern convar_t sv_stepsize; extern convar_t sv_maxvelocity; extern convar_t sv_rollangle; extern convar_t sv_rollspeed; extern convar_t sv_skyname; -extern convar_t sv_skyspeed; -extern convar_t sv_skyangle; +extern convar_t sv_skycolor_r; +extern convar_t sv_skycolor_g; +extern convar_t sv_skycolor_b; +extern convar_t sv_skyvec_x; +extern convar_t sv_skyvec_y; +extern convar_t sv_skyvec_z; extern convar_t sv_consistency; extern convar_t sv_password; extern convar_t sv_uploadmax; @@ -494,7 +500,7 @@ void SV_CheckVelocity( edict_t *ent ); qboolean SV_CheckWater( edict_t *ent ); qboolean SV_RunThink( edict_t *ent ); qboolean SV_PlayerRunThink( edict_t *ent, float frametime, double time ); -qboolean SV_TestEntityPosition( edict_t *ent, edict_t *blocker ); // for EntityInSolid checks +qboolean SV_TestEntityPosition( edict_t *ent, edict_t *blocker ); void SV_Impact( edict_t *e1, edict_t *e2, trace_t *trace ); qboolean SV_CanPushed( edict_t *ent ); void SV_FreeOldEntities( void ); @@ -594,6 +600,7 @@ edict_t* SV_FindEntityByString( edict_t *pStartEdict, const char *pszField, cons void SV_PlaybackEventFull( int flags, const edict_t *pInvoker, word eventindex, float delay, float *origin, float *angles, float fparam1, float fparam2, int iparam1, int iparam2, int bparam1, int bparam2 ); void SV_PlaybackReliableEvent( sizebuf_t *msg, word eventindex, float delay, event_args_t *args ); +int SV_BuildSoundMsg( sizebuf_t *msg, edict_t *ent, int chan, const char *sample, int vol, float attn, int flags, int pitch, const vec3_t pos ); qboolean SV_BoxInPVS( const vec3_t org, const vec3_t absmin, const vec3_t absmax ); void SV_WriteEntityPatch( const char *filename ); float SV_AngleMod( float ideal, float current, float speed ); @@ -607,6 +614,7 @@ sv_client_t *SV_ClientFromEdict( const edict_t *pEdict, qboolean spawned_only ); int SV_MapIsValid( const char *filename, const char *spawn_entity, const char *landmark_name ); void SV_StartSound( edict_t *ent, int chan, const char *sample, float vol, float attn, int flags, int pitch ); void SV_CreateStaticEntity( struct sizebuf_s *msg, sv_static_entity_t *ent ); +edict_t *SV_FindGlobalEntity( string_t classname, string_t globalname ); void SV_SendUserReg( sizebuf_t *msg, sv_user_message_t *user ); edict_t* pfnPEntityOfEntIndex( int iEntIndex ); int pfnIndexOfEdict( const edict_t *pEdict ); @@ -634,12 +642,10 @@ qboolean SV_ServerLog_f( sv_client_t *cl ); // // sv_save.c // -void SV_ClearSaveDir( void ); void SV_SaveGame( const char *pName ); qboolean SV_LoadGame( const char *pName ); -int SV_LoadGameState( char const *level, qboolean changelevel ); +int SV_LoadGameState( char const *level ); void SV_ChangeLevel( qboolean loadfromsavedgame, const char *mapname, const char *start, qboolean background ); -void SV_LoadAdjacentEnts( const char *pOldLevel, const char *pLandmarkName ); const char *SV_GetLatestSave( void ); void SV_InitSaveRestore( void ); void SV_ClearGameState( void ); diff --git a/engine/server/sv_client.c b/engine/server/sv_client.c index 1a56e128..ab986124 100644 --- a/engine/server/sv_client.c +++ b/engine/server/sv_client.c @@ -1191,7 +1191,7 @@ void SV_PutClientInServer( sv_client_t *cl ) svgame.dllFuncs.pfnParmsChangeLevel(); MSG_BeginServerCmd( &msg, svc_restore ); - Q_snprintf( name, sizeof( name ), "save/%s.HL2", sv.name ); + Q_snprintf( name, sizeof( name ), "%s%s.HL2", DEFAULT_SAVE_DIRECTORY, sv.name ); COM_FixSlashes( name ); MSG_WriteString( &msg, name ); MSG_WriteByte( &msg, levelData.connectionCount ); diff --git a/engine/server/sv_cmds.c b/engine/server/sv_cmds.c index e8b56933..2c001723 100644 --- a/engine/server/sv_cmds.c +++ b/engine/server/sv_cmds.c @@ -320,7 +320,7 @@ void SV_Load_f( void ) return; } - Q_snprintf( path, sizeof( path ), "save/%s.sav", Cmd_Argv( 1 )); + Q_snprintf( path, sizeof( path ), "%s%s.sav", DEFAULT_SAVE_DIRECTORY, Cmd_Argv( 1 )); SV_LoadGame( path ); } @@ -343,18 +343,18 @@ SV_Save_f */ void SV_Save_f( void ) { - const char *name; - - switch( Cmd_Argc() ) + switch( Cmd_Argc( )) { - case 1: name = "new"; break; - case 2: name = Cmd_Argv( 1 ); break; + case 1: + SV_SaveGame( "new" ); + break; + case 2: + SV_SaveGame( Cmd_Argv( 1 )); + break; default: Con_Printf( S_USAGE "save \n" ); - return; + break; } - - SV_SaveGame( name ); } /* @@ -383,8 +383,8 @@ void SV_DeleteSave_f( void ) } // delete save and saveshot - FS_Delete( va( "save/%s.sav", Cmd_Argv( 1 ))); - FS_Delete( va( "save/%s.bmp", Cmd_Argv( 1 ))); + FS_Delete( va( "%s%s.sav", DEFAULT_SAVE_DIRECTORY, Cmd_Argv( 1 ))); + FS_Delete( va( "%s%s.bmp", DEFAULT_SAVE_DIRECTORY, Cmd_Argv( 1 ))); } /* @@ -732,11 +732,10 @@ disable plhysics but players void SV_PlayersOnly_f( void ) { if( !Cvar_VariableInteger( "sv_cheats" )) return; - sv.hostflags = sv.hostflags ^ SVF_PLAYERSONLY; - if( !FBitSet( sv.hostflags, SVF_PLAYERSONLY )) - SV_BroadcastPrintf( NULL, "Resume game physic\n" ); - else SV_BroadcastPrintf( NULL, "Freeze game physic\n" ); + sv.playersonly ^= 1; + + SV_BroadcastPrintf( NULL, "%s game physic\n", sv.playersonly ? "Freeze" : "Resume" ); } /* diff --git a/engine/server/sv_game.c b/engine/server/sv_game.c index 2904f971..0c75bd8c 100644 --- a/engine/server/sv_game.c +++ b/engine/server/sv_game.c @@ -502,7 +502,7 @@ void SV_CreateStaticEntity( sizebuf_t *msg, sv_static_entity_t *ent ) int index; // this can happens if serialized map contain too many static entities... - if( MSG_GetNumBytesLeft( msg ) < 64 ) + if( MSG_GetNumBytesLeft( msg ) < 35 ) { sv.ignored_static_ents++; return; @@ -512,8 +512,8 @@ void SV_CreateStaticEntity( sizebuf_t *msg, sv_static_entity_t *ent ) MSG_BeginServerCmd( msg, svc_spawnstatic ); MSG_WriteShort( msg, index ); - MSG_WriteByte( msg, ent->sequence ); - MSG_WriteByte( msg, ent->frame ); + MSG_WriteWord( msg, ent->sequence ); + MSG_WriteWord( msg, ent->frame ); MSG_WriteWord( msg, ent->colormap ); MSG_WriteByte( msg, ent->skin ); MSG_WriteByte( msg, ent->body ); @@ -1520,6 +1520,27 @@ edict_t *SV_FindEntityByString( edict_t *pStartEdict, const char *pszField, cons return svgame.edicts; } +/* +========= +SV_FindGlobalEntity + +ripped out from the hl.dll +========= +*/ +edict_t *SV_FindGlobalEntity( string_t classname, string_t globalname ) +{ + edict_t *pent = SV_FindEntityByString( NULL, "globalname", STRING( globalname )); + + if( SV_IsValidEdict( pent )) + { + // don't spam about error - game code already tell us + if( Q_strcmp( SV_ClassName( pent ), STRING( classname ))) + pent = NULL; + } + + return pent; +} + /* ============== pfnGetEntityIllum @@ -1826,7 +1847,7 @@ static void pfnMakeStatic( edict_t *ent ) VectorCopy( ent->v.angles, clent->angles ); clent->sequence = ent->v.sequence; - clent->frame = ent->v.frame; + clent->frame = ent->v.frame * 128; clent->colormap = ent->v.colormap; clent->skin = ent->v.skin; clent->body = ent->v.body; @@ -1946,10 +1967,11 @@ SV_BuildSoundMsg ================= */ -static int SV_BuildSoundMsg( sizebuf_t *msg, edict_t *ent, int chan, const char *sample, int vol, float attn, int flags, int pitch, const vec3_t pos ) +int SV_BuildSoundMsg( sizebuf_t *msg, edict_t *ent, int chan, const char *sample, int vol, float attn, int flags, int pitch, const vec3_t pos ) { int entityIndex; int sound_idx; + qboolean spawn; if( vol < 0 || vol > 255 ) { @@ -2004,21 +2026,25 @@ static int SV_BuildSoundMsg( sizebuf_t *msg, edict_t *ent, int chan, const char return 0; } + spawn = FBitSet( flags, SND_RESTORE_POSITION ) ? false : true; + if( SV_IsValidEdict( ent ) && SV_IsValidEdict( ent->v.aiment )) entityIndex = NUM_FOR_EDICT( ent->v.aiment ); else if( SV_IsValidEdict( ent )) entityIndex = NUM_FOR_EDICT( ent ); - else entityIndex = 0; // assime world + else entityIndex = 0; // assume world if( vol != 255 ) SetBits( flags, SND_VOLUME ); if( attn != ATTN_NONE ) SetBits( flags, SND_ATTENUATION ); if( pitch != PITCH_NORM ) SetBits( flags, SND_PITCH ); // not sending (because this is out of range) + ClearBits( flags, SND_RESTORE_POSITION ); ClearBits( flags, SND_FILTER_CLIENT ); ClearBits( flags, SND_SPAWNING ); - MSG_BeginServerCmd( msg, svc_sound ); + if( spawn ) MSG_BeginServerCmd( msg, svc_sound ); + else MSG_BeginServerCmd( msg, svc_restoresound ); MSG_WriteUBitLong( msg, flags, MAX_SND_FLAGS_BITS ); MSG_WriteUBitLong( msg, sound_idx, MAX_SOUND_BITS ); MSG_WriteUBitLong( msg, chan, MAX_SND_CHAN_BITS ); @@ -2442,6 +2468,7 @@ void pfnLightStyle( int style, const char* val ) if( style < 0 ) style = 0; if( style >= MAX_LIGHTSTYLES ) Host_Error( "SV_LightStyle: style: %i >= %d", style, MAX_LIGHTSTYLES ); + if( sv.loadgame ) return; // don't let the world overwrite our restored styles SV_SetLightStyle( style, val, 0.0f ); // set correct style } @@ -4663,11 +4690,6 @@ void SV_SpawnEntities( const char *mapname ) Cvar_Reset( "sv_skyvec_y" ); Cvar_Reset( "sv_skyvec_z" ); Cvar_Reset( "sv_skyname" ); - Cvar_Reset( "sv_skydir_x" ); - Cvar_Reset( "sv_skydir_y" ); - Cvar_Reset( "sv_skydir_z" ); - Cvar_Reset( "sv_skyangle" ); - Cvar_Reset( "sv_skyspeed" ); ent = EDICT_NUM( 0 ); if( ent->free ) SV_InitEdict( ent ); diff --git a/engine/server/sv_init.c b/engine/server/sv_init.c index 53e06f2a..0c1a5ab2 100644 --- a/engine/server/sv_init.c +++ b/engine/server/sv_init.c @@ -928,9 +928,8 @@ void SV_ExecLoadGame( void ) { if( SV_SpawnServer( GameState->levelName, NULL, false )) { - if( !SV_LoadGameState( GameState->levelName, false )) + if( !SV_LoadGameState( GameState->levelName )) SV_SpawnEntities( GameState->levelName ); - sv.loadgame = sv.paused = true; // pause until all clients connect SV_ActivateServer( false ); } } diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index 7aab9e95..cc3182a3 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -92,12 +92,6 @@ CVAR_DEFINE_AUTO( sv_skyvec_x, "0", FCVAR_MOVEVARS|FCVAR_UNLOGGED, "skylight dir CVAR_DEFINE_AUTO( sv_skyvec_y, "0", FCVAR_MOVEVARS|FCVAR_UNLOGGED, "skylight direction by y-axis" ); CVAR_DEFINE_AUTO( sv_skyvec_z, "0", FCVAR_MOVEVARS|FCVAR_UNLOGGED, "skylight direction by z-axis" ); CVAR_DEFINE_AUTO( sv_wateralpha, "1", FCVAR_MOVEVARS|FCVAR_UNLOGGED, "world surfaces water transparency factor. 1.0 - solid, 0.0 - fully transparent" ); -CVAR_DEFINE_AUTO( sv_skydir_x, "0", FCVAR_MOVEVARS|FCVAR_UNLOGGED, "sky rotation factor around x-axis" ); -CVAR_DEFINE_AUTO( sv_skydir_y, "0", FCVAR_MOVEVARS|FCVAR_UNLOGGED, "sky rotation factor around y-axis" ); -CVAR_DEFINE_AUTO( sv_skydir_z, "1", FCVAR_MOVEVARS|FCVAR_UNLOGGED, "sky rotation factor around z-axis" ); // g-cont. add default sky rotate direction -CVAR_DEFINE_AUTO( sv_skyangle, "0", FCVAR_MOVEVARS|FCVAR_UNLOGGED, "skybox rotational angle (in degrees)" ); -CVAR_DEFINE_AUTO( sv_skyspeed, "0", 0, "skybox rotational speed" ); - CVAR_DEFINE_AUTO( showtriggers, "0", FCVAR_LATCH, "debug cvar shows triggers" ); CVAR_DEFINE_AUTO( sv_airmove, "1", FCVAR_SERVER, "obsolete, compatibility issues" ); CVAR_DEFINE_AUTO( sv_version, "", FCVAR_READ_ONLY, "engine version string" ); @@ -199,10 +193,6 @@ void SV_UpdateMovevars( qboolean initialize ) svgame.movevars.skyvec_x = sv_skyvec_x.value; svgame.movevars.skyvec_y = sv_skyvec_y.value; svgame.movevars.skyvec_z = sv_skyvec_z.value; - svgame.movevars.skydir_x = sv_skydir_x.value; - svgame.movevars.skydir_y = sv_skydir_y.value; - svgame.movevars.skydir_z = sv_skydir_z.value; - svgame.movevars.skyangle = sv_skyangle.value; svgame.movevars.wateralpha = sv_wateralpha.value; svgame.movevars.features = host.features; // just in case. not really need svgame.movevars.entgravity = 1.0f; @@ -528,10 +518,14 @@ qboolean SV_IsSimulating( void ) return true; // force simulating for background map } - if( FBitSet( sv.hostflags, SVF_PLAYERSONLY )) + if( !SV_HasActivePlayers( )) return false; - if( !SV_HasActivePlayers()) + if( host.type == HOST_DEDICATED ) + return true; // always active for dedicated servers + + // allow to freeze everything in singleplayer + if( svs.maxclients <= 1 && sv.playersonly ) return false; if( !sv.paused && CL_IsInGame( )) @@ -772,11 +766,6 @@ void SV_Init( void ) Cvar_RegisterVariable (&sv_skyvec_y); Cvar_RegisterVariable (&sv_skyvec_z); Cvar_RegisterVariable (&sv_skyname); - Cvar_RegisterVariable (&sv_skydir_x); - Cvar_RegisterVariable (&sv_skydir_y); - Cvar_RegisterVariable (&sv_skydir_z); - Cvar_RegisterVariable (&sv_skyangle); - Cvar_RegisterVariable (&sv_skyspeed); Cvar_RegisterVariable (&sv_footsteps); Cvar_RegisterVariable (&sv_wateralpha); Cvar_RegisterVariable (&sv_cheats); @@ -846,11 +835,12 @@ void SV_Init( void ) // when we in developer-mode automatically turn cheats on if( host_developer.value ) Cvar_SetValue( "sv_cheats", 1.0f ); - SV_ClearSaveDir (); // delete all temporary *.hl files MSG_Init( &net_message, "NetMessage", net_message_buffer, sizeof( net_message_buffer )); Q_snprintf( versionString, sizeof( versionString ), "%s: %.2f,%i,%i", "Xash3D", XASH_VERSION, PROTOCOL_VERSION, Q_buildnum() ); Cvar_FullSet( "sv_version", versionString, FCVAR_READ_ONLY ); + + SV_ClearGameState (); // delete all temporary *.hl files } /* diff --git a/engine/server/sv_phys.c b/engine/server/sv_phys.c index 95972c67..4d393a31 100644 --- a/engine/server/sv_phys.c +++ b/engine/server/sv_phys.c @@ -1806,13 +1806,6 @@ void SV_Physics( void ) // increase framecount sv.framecount++; - if( sv_skyspeed.value ) - { - // evaluate sky rotation. - float skyAngle = sv_skyangle.value + sv_skyspeed.value * sv.frametime; - Cvar_SetValue( "sv_skyangle", anglemod( skyAngle )); - } - // decrement svgame.numEntities if the highest number entities died for( ; EDICT_NUM( svgame.numEntities - 1 )->free; svgame.numEntities-- ); } diff --git a/engine/server/sv_pmove.c b/engine/server/sv_pmove.c index f04aa84c..7f5c74c9 100644 --- a/engine/server/sv_pmove.c +++ b/engine/server/sv_pmove.c @@ -1083,7 +1083,7 @@ void SV_RunCmd( sv_client_t *cl, usercmd_t *ucmd, int random_seed ) // copy results back to client SV_FinishPMove( svgame.pmove, cl ); - if( clent->v.solid != SOLID_NOT && !FBitSet( sv.hostflags, SVF_PLAYERSONLY )) + if( clent->v.solid != SOLID_NOT && !sv.playersonly ) { if( svgame.physFuncs.PM_PlayerTouch != NULL ) { diff --git a/engine/server/sv_save.c b/engine/server/sv_save.c index 3f9195b5..bed760e2 100644 --- a/engine/server/sv_save.c +++ b/engine/server/sv_save.c @@ -29,40 +29,14 @@ half-life implementation of saverestore system */ #define SAVEFILE_HEADER (('V'<<24)+('L'<<16)+('A'<<8)+'V') // little-endian "VALV" #define SAVEGAME_HEADER (('V'<<24)+('A'<<16)+('S'<<8)+'J') // little-endian "JSAV" -#define SAVEGAME_VERSION 0x0065 // Version 0.65 -#define CLIENT_SAVEGAME_VERSION 0x0070 // Version 0.70 +#define SAVEGAME_VERSION 0x0071 // Version 0.71 GoldSrc compatible +#define CLIENT_SAVEGAME_VERSION 0x0065 // Version 0.65 +#define SAVE_HEAPSIZE 0x400000 // reserve 4Mb for now +#define SAVE_HASHSTRINGS 0xFFF // 4095 unique strings #define SAVE_AGED_COUNT 2 -#define SAVENAME_LENGTH 128 // matches with MAX_OSPATH - -#define LUMP_DECALS_OFFSET 0 -#define LUMP_STATIC_OFFSET 1 -#define LUMP_SOUNDS_OFFSET 2 -#define LUMP_MUSIC_OFFSET 3 -#define NUM_CLIENT_LUMPS 8 - -void (__cdecl *pfnSaveGameComment)( char *buffer, int max_length ) = NULL; - -typedef struct -{ - int nBytesSymbols; - int nSymbols; - int nBytesDataHeaders; - int nBytesData; -} SaveFileSectionsInfo_t; - -typedef struct -{ - char *pSymbols; - char *pDataHeaders; - char *pData; -} SaveFileSections_t; - -typedef struct -{ - int offsets[NUM_CLIENT_LUMPS]; -} ClientSections_t; +// savedata headers typedef struct { char mapName[32]; @@ -85,16 +59,22 @@ typedef struct float skyVec_x; float skyVec_y; float skyVec_z; - int viewentity; // Xash3D added - int serverflags; // converted to float and back - float wateralpha; - float skyDir_x; // rotating sky support - float skyDir_y; - float skyDir_z; - float skyAngle; - float skySpeed; } SAVE_HEADER; +typedef struct +{ + int decalCount; // render decals count + int entityCount; // static entity count + int soundCount; // sounds count + int tempEntsCount; // not used + char introTrack[64]; + char mainTrack[64]; + int trackPosition; + short viewentity; // Xash3D added + float wateralpha; + float wateramp; // world waves +} SAVE_CLIENT; + typedef struct { int index; @@ -102,6 +82,8 @@ typedef struct float time; } SAVE_LIGHTSTYLE; +void (__cdecl *pfnSaveGameComment)( char *buffer, int max_length ) = NULL; + static TYPEDESCRIPTION gGameHeader[] = { DEFINE_ARRAY( GAME_HEADER, mapName, FIELD_CHARACTER, 32 ), @@ -124,16 +106,8 @@ static TYPEDESCRIPTION gSaveHeader[] = DEFINE_FIELD( SAVE_HEADER, skyVec_x, FIELD_FLOAT ), DEFINE_FIELD( SAVE_HEADER, skyVec_y, FIELD_FLOAT ), DEFINE_FIELD( SAVE_HEADER, skyVec_z, FIELD_FLOAT ), - DEFINE_FIELD( SAVE_HEADER, viewentity, FIELD_SHORT ), - DEFINE_FIELD( SAVE_HEADER, serverflags, FIELD_INTEGER ), - DEFINE_FIELD( SAVE_HEADER, wateralpha, FIELD_FLOAT ), - DEFINE_FIELD( SAVE_HEADER, skyDir_x, FIELD_FLOAT ), - DEFINE_FIELD( SAVE_HEADER, skyDir_y, FIELD_FLOAT ), - DEFINE_FIELD( SAVE_HEADER, skyDir_z, FIELD_FLOAT ), - DEFINE_FIELD( SAVE_HEADER, skyAngle, FIELD_FLOAT ), - DEFINE_FIELD( SAVE_HEADER, skySpeed, FIELD_FLOAT ), }; - + static TYPEDESCRIPTION gAdjacency[] = { DEFINE_ARRAY( LEVELLIST, mapName, FIELD_CHARACTER, 32 ), @@ -158,191 +132,84 @@ static TYPEDESCRIPTION gEntityTable[] = DEFINE_FIELD( ENTITYTABLE, classname, FIELD_STRING ), }; -TYPEDESCRIPTION gEntvarsDescription[] = +static TYPEDESCRIPTION gSaveClient[] = +{ + DEFINE_FIELD( SAVE_CLIENT, decalCount, FIELD_INTEGER ), + DEFINE_FIELD( SAVE_CLIENT, entityCount, FIELD_INTEGER ), + DEFINE_FIELD( SAVE_CLIENT, soundCount, FIELD_INTEGER ), + DEFINE_FIELD( SAVE_CLIENT, tempEntsCount, FIELD_INTEGER ), + DEFINE_ARRAY( SAVE_CLIENT, introTrack, FIELD_CHARACTER, 64 ), + DEFINE_ARRAY( SAVE_CLIENT, mainTrack, FIELD_CHARACTER, 64 ), + DEFINE_FIELD( SAVE_CLIENT, trackPosition, FIELD_INTEGER ), + DEFINE_FIELD( SAVE_CLIENT, viewentity, FIELD_SHORT ), + DEFINE_FIELD( SAVE_CLIENT, wateralpha, FIELD_FLOAT ), + DEFINE_FIELD( SAVE_CLIENT, wateramp, FIELD_FLOAT ), +}; + +static TYPEDESCRIPTION gDecalEntry[] = +{ + DEFINE_FIELD( decallist_t, position, FIELD_VECTOR ), + DEFINE_ARRAY( decallist_t, name, FIELD_CHARACTER, 64 ), + DEFINE_FIELD( decallist_t, entityIndex, FIELD_SHORT ), + DEFINE_FIELD( decallist_t, depth, FIELD_CHARACTER ), + DEFINE_FIELD( decallist_t, flags, FIELD_CHARACTER ), + DEFINE_FIELD( decallist_t, scale, FIELD_FLOAT ), + DEFINE_FIELD( decallist_t, impactPlaneNormal, FIELD_VECTOR ), + DEFINE_ARRAY( decallist_t, studio_state, FIELD_CHARACTER, sizeof( modelstate_t )), +}; + +static TYPEDESCRIPTION gStaticEntry[] = +{ + DEFINE_ARRAY( sv_static_entity_t, model, FIELD_CHARACTER, 64 ), + DEFINE_FIELD( sv_static_entity_t, origin, FIELD_VECTOR ), + DEFINE_FIELD( sv_static_entity_t, angles, FIELD_VECTOR ), + DEFINE_FIELD( sv_static_entity_t, sequence, FIELD_SHORT ), + DEFINE_FIELD( sv_static_entity_t, frame, FIELD_SHORT ), + DEFINE_FIELD( sv_static_entity_t, colormap, FIELD_SHORT ), + DEFINE_FIELD( sv_static_entity_t, skin, FIELD_CHARACTER ), + DEFINE_FIELD( sv_static_entity_t, body, FIELD_CHARACTER ), + DEFINE_FIELD( sv_static_entity_t, scale, FIELD_FLOAT ), + DEFINE_FIELD( sv_static_entity_t, rendermode, FIELD_CHARACTER ), + DEFINE_FIELD( sv_static_entity_t, renderamt, FIELD_CHARACTER ), + DEFINE_ARRAY( sv_static_entity_t, rendercolor, FIELD_CHARACTER, sizeof( color24 )), + DEFINE_FIELD( sv_static_entity_t, renderfx, FIELD_CHARACTER ), +}; + +static TYPEDESCRIPTION gSoundEntry[] = +{ + DEFINE_ARRAY( soundlist_t, name, FIELD_CHARACTER, 64 ), + DEFINE_FIELD( soundlist_t, entnum, FIELD_SHORT ), + DEFINE_FIELD( soundlist_t, origin, FIELD_VECTOR ), + DEFINE_FIELD( soundlist_t, volume, FIELD_FLOAT ), + DEFINE_FIELD( soundlist_t, attenuation, FIELD_FLOAT ), + DEFINE_FIELD( soundlist_t, looping, FIELD_BOOLEAN ), + DEFINE_FIELD( soundlist_t, channel, FIELD_CHARACTER ), + DEFINE_FIELD( soundlist_t, pitch, FIELD_CHARACTER ), + DEFINE_FIELD( soundlist_t, wordIndex, FIELD_CHARACTER ), + DEFINE_ARRAY( soundlist_t, samplePos, FIELD_CHARACTER, sizeof( double )), + DEFINE_ARRAY( soundlist_t, forcedEnd, FIELD_CHARACTER, sizeof( double )), +}; + +static TYPEDESCRIPTION gTempEntvars[] = { DEFINE_ENTITY_FIELD( classname, FIELD_STRING ), DEFINE_ENTITY_GLOBAL_FIELD( globalname, FIELD_STRING ), }; -int SumBytes( SaveFileSectionsInfo_t *section ) -{ - return ( section->nBytesSymbols + section->nBytesDataHeaders + section->nBytesData ); -} - -void SV_InitSaveRestore( void ) -{ - pfnSaveGameComment = COM_GetProcAddress( svgame.hInstance, "SV_SaveGameComment" ); -} - /* ----------------------------------------------------------- - SaveRestore helpers +============= +SaveBuildComment - assume pSaveData is valid ----------------------------------------------------------- +build commentary for each savegame +typically it writes world message and level time +============= */ -void SaveRestore_Init( SAVERESTOREDATA *pSaveData, void *pNewBase, int nBytes ) +static void SaveBuildComment( char *text, int maxlength ) { - pSaveData->pCurrentData = pSaveData->pBaseData = (char *)pNewBase; - pSaveData->bufferSize = nBytes; - pSaveData->size = 0; -} + const char *pName; -void SaveRestore_MoveCurPos( SAVERESTOREDATA *pSaveData, int nBytes ) -{ - pSaveData->pCurrentData += nBytes; - pSaveData->size += nBytes; -} + text[0] = '\0'; // clear -void SaveRestore_Rebase( SAVERESTOREDATA *pSaveData ) -{ - pSaveData->pBaseData = pSaveData->pCurrentData; - pSaveData->bufferSize -= pSaveData->size; - pSaveData->size = 0; -} - -void SaveRestore_Rewind( SAVERESTOREDATA *pSaveData, int nBytes ) -{ - if( pSaveData->size < nBytes ) - nBytes = pSaveData->size; - - SaveRestore_MoveCurPos( pSaveData, -nBytes ); -} - -char *SaveRestore_GetBuffer( SAVERESTOREDATA *pSaveData ) -{ - return pSaveData->pBaseData; -} - -int SaveRestore_BytesAvailable( SAVERESTOREDATA *pSaveData ) -{ - return (pSaveData->bufferSize - pSaveData->size); -} - -int SaveRestore_SizeBuffer( SAVERESTOREDATA *pSaveData ) -{ - return pSaveData->bufferSize; -} - -qboolean SaveRestore_Write( SAVERESTOREDATA *pSaveData, const void *pData, int nBytes ) -{ - if( nBytes > SaveRestore_BytesAvailable( pSaveData )) - { - pSaveData->size = pSaveData->bufferSize; - return false; - } - - memcpy( pSaveData->pCurrentData, pData, nBytes ); - SaveRestore_MoveCurPos( pSaveData, nBytes ); - - return true; -} - -qboolean SaveRestore_Read( SAVERESTOREDATA *pSaveData, void *pOutput, int nBytes ) -{ - if( !SaveRestore_BytesAvailable( pSaveData )) - return false; - - if( nBytes > SaveRestore_BytesAvailable( pSaveData )) - { - pSaveData->size = pSaveData->bufferSize; - return false; - } - - if( pOutput ) memcpy( pOutput, pSaveData->pCurrentData, nBytes ); - SaveRestore_MoveCurPos( pSaveData, nBytes ); - - return true; -} - -int SaveRestore_GetCurPos( SAVERESTOREDATA *pSaveData ) -{ - return pSaveData->size; -} - -char *SaveRestore_AccessCurPos( SAVERESTOREDATA *pSaveData ) -{ - return pSaveData->pCurrentData; -} - -qboolean SaveRestore_Seek( SAVERESTOREDATA *pSaveData, int absPosition ) -{ - if( absPosition < 0 || absPosition >= pSaveData->bufferSize ) - return false; - - pSaveData->size = absPosition; - pSaveData->pCurrentData = pSaveData->pBaseData + pSaveData->size; - - return true; -} - -void SaveRestore_InitEntityTable( SAVERESTOREDATA *pSaveData, ENTITYTABLE *pNewTable, int entityCount ) -{ - ENTITYTABLE *pTable; - int i; - - Assert( pSaveData->pTable == NULL ); - - pSaveData->tableCount = entityCount; - pSaveData->pTable = pNewTable; - - // setup entitytable - for( i = 0; i < entityCount; i++ ) - { - pTable = &pSaveData->pTable[i]; - pTable->pent = EDICT_NUM( i ); - } -} - -ENTITYTABLE *SaveRestore_DetachEntityTable( SAVERESTOREDATA *pSaveData ) -{ - ENTITYTABLE *pReturn = pSaveData->pTable; - - pSaveData->pTable = NULL; - pSaveData->tableCount = 0; - - return pReturn; -} - -void SaveRestore_InitSymbolTable( SAVERESTOREDATA *pSaveData, char **pNewTokens, int sizeTable ) -{ - Assert( pSaveData->pTokens == NULL ); - - pSaveData->tokenCount = sizeTable; - pSaveData->pTokens = pNewTokens; -} - -char **SaveRestore_DetachSymbolTable( SAVERESTOREDATA *pSaveData ) -{ - char **pResult = pSaveData->pTokens; - - pSaveData->tokenCount = 0; - pSaveData->pTokens = NULL; - - return pResult; -} - -qboolean SaveRestore_DefineSymbol( SAVERESTOREDATA *pSaveData, const char *pszToken, int token ) -{ - if( pSaveData->pTokens[token] == NULL ) - { - pSaveData->pTokens[token] = (char *)pszToken; - return true; - } - - Assert( 0 ); - return false; -} - -const char *SaveRestore_StringFromSymbol( SAVERESTOREDATA *pSaveData, int token ) -{ - if( token >= 0 && token < pSaveData->tokenCount ) - return pSaveData->pTokens[token]; - return "<>"; -} - -void SV_BuildSaveComment( char *text, int maxlength ) -{ if( pfnSaveGameComment != NULL ) { // get save comment from gamedll @@ -350,14 +217,10 @@ void SV_BuildSaveComment( char *text, int maxlength ) } else { - const char *pName; - edict_t *pWorld = EDICT_NUM( 0 ); - float time = sv.time; - - if( pWorld && pWorld->v.message ) + if( svgame.edicts->v.message != 0 ) { // trying to extract message from the world - pName = STRING( pWorld->v.message ); + pName = STRING( svgame.edicts->v.message ); } else { @@ -365,17 +228,25 @@ void SV_BuildSaveComment( char *text, int maxlength ) pName = STRING( svgame.globals->mapname ); } - Q_snprintf( text, maxlength, "%-64.64s %02d:%02d", pName, (int)(time / 60.0f ), (int)fmod( time, 60.0f )); + Q_snprintf( text, maxlength, "%-64.64s %02d:%02d", pName, (int)(sv.time / 60.0 ), (int)fmod( sv.time, 60.0 )); } } -int SV_MapCount( const char *pPath ) +/* +============= +DirectoryCount + +counting all the files with HL1-HL3 extension +in save folder +============= +*/ +static int DirectoryCount( const char *pPath ) { + int count; search_t *t; - int count = 0; - + t = FS_Search( pPath, true, true ); // lookup only in gamedir - if( !t ) return count; // empty + if( !t ) return 0; // empty count = t->numfilenames; Mem_Free( t ); @@ -383,13 +254,42 @@ int SV_MapCount( const char *pPath ) return count; } -int EntryInTable( SAVERESTOREDATA *pSaveData, const char *pMapName, int index ) +/* +============= +InitEntityTable + +reserve space for ETABLE's +============= +*/ +static void InitEntityTable( SAVERESTOREDATA *pSaveData, int entityCount ) +{ + ENTITYTABLE *pTable; + int i; + + pSaveData->pTable = Mem_Alloc( host.mempool, sizeof( ENTITYTABLE ) * entityCount ); + pSaveData->tableCount = entityCount; + + // setup entitytable + for( i = 0; i < entityCount; i++ ) + { + pTable = &pSaveData->pTable[i]; + pTable->pent = EDICT_NUM( i ); + pTable->id = i; + } +} + +/* +============= +EntryInTable + +check level in transition list +============= +*/ +static int EntryInTable( SAVERESTOREDATA *pSaveData, const char *pMapName, int index ) { int i; - index++; - - for( i = index; i < pSaveData->connectionCount; i++ ) + for( i = index + 1; i < pSaveData->connectionCount; i++ ) { if ( !Q_stricmp( pSaveData->levelList[i].mapName, pMapName )) return i; @@ -398,7 +298,32 @@ int EntryInTable( SAVERESTOREDATA *pSaveData, const char *pMapName, int index ) return -1; } -void LandmarkOrigin( SAVERESTOREDATA *pSaveData, vec3_t output, const char *pLandmarkName ) +/* +============= +EdictFromTable + +get edict from table +============= +*/ +static edict_t *EdictFromTable( SAVERESTOREDATA *pSaveData, int entityIndex ) +{ + if( pSaveData && pSaveData->pTable ) + { + entityIndex = bound( 0, entityIndex, pSaveData->tableCount - 1 ); + return pSaveData->pTable[entityIndex].pent; + } + + return NULL; +} + +/* +============= +LandmarkOrigin + +find global offset for a given landmark +============= +*/ +static void LandmarkOrigin( SAVERESTOREDATA *pSaveData, vec3_t output, const char *pLandmarkName ) { int i; @@ -414,12 +339,21 @@ void LandmarkOrigin( SAVERESTOREDATA *pSaveData, vec3_t output, const char *pLan VectorClear( output ); } -int EntityInSolid( edict_t *pent ) +/* +============= +EntityInSolid + +some moved edicts on a next level cause stuck +outside of world. Find them and remove +============= +*/ +static int EntityInSolid( edict_t *pent ) { + edict_t *aiment = pent->v.aiment; vec3_t point; // if you're attached to a client, always go through - if( pent->v.movetype == MOVETYPE_FOLLOW && SV_IsValidEdict( pent->v.aiment ) && FBitSet( pent->v.aiment->v.flags, FL_CLIENT )) + if( pent->v.movetype == MOVETYPE_FOLLOW && SV_IsValidEdict( aiment ) && FBitSet( aiment->v.flags, FL_CLIENT )) return 0; VectorAverage( pent->v.absmin, pent->v.absmax, point ); @@ -428,152 +362,21 @@ int EntityInSolid( edict_t *pent ) return (SV_PointContents( point ) == CONTENTS_SOLID); } -void ReapplyDecal( SAVERESTOREDATA *pSaveData, decallist_t *entry, qboolean adjacent ) -{ - int flags = entry->flags; - int decalIndex, entityIndex = 0; - int modelIndex = 0; - edict_t *pEdict; +/* +============= +ClearSaveDir - // never transition permanent decals - if( adjacent && ( flags & FDECAL_PERMANENT )) - return; - - // restore entity and model index - pEdict = pSaveData->pTable[entry->entityIndex].pent; - if( SV_IsValidEdict( pEdict )) modelIndex = pEdict->v.modelindex; - if( SV_IsValidEdict( pEdict )) entityIndex = NUM_FOR_EDICT( pEdict ); - - if( SV_RestoreCustomDecal( entry, pEdict, adjacent )) - return; // decal was sucessfully restored at the game-side - - // studio decals are handled at game-side - if( FBitSet( flags, FDECAL_STUDIO )) - return; - - // NOTE: at this point all decal indexes is valid - decalIndex = pfnDecalIndex( entry->name ); - - if( adjacent && entityIndex != 0 && !SV_IsValidEdict( pEdict )) - { - vec3_t testspot, testend; - trace_t tr; - - // these entities might not exist over transitions, - // so we'll use the saved plane and do a traceline instead - MsgDev( D_ERROR, "couldn't restore entity index %i, do trace for decal\n", entityIndex ); - - VectorCopy( entry->position, testspot ); - VectorMA( testspot, 5.0f, entry->impactPlaneNormal, testspot ); - - VectorCopy( entry->position, testend ); - VectorMA( testend, -5.0f, entry->impactPlaneNormal, testend ); - - tr = SV_Move( testspot, vec3_origin, vec3_origin, testend, MOVE_NOMONSTERS, NULL, false ); - - // NOTE: this code may does wrong result on moving brushes e.g. func_tracktrain - if( tr.fraction != 1.0f && !tr.allsolid ) - { - // check impact plane normal - float dot = DotProduct( entry->impactPlaneNormal, tr.plane.normal ); - - if( dot >= 0.95f ) - { - entityIndex = pfnIndexOfEdict( tr.ent ); - if( entityIndex > 0 ) modelIndex = tr.ent->v.modelindex; - SV_CreateDecal( &sv.signon, tr.endpos, decalIndex, entityIndex, modelIndex, flags, entry->scale ); - } - } - } - else - { - // global entity is exist on new level so we can apply decal in local space - // NOTE: this case also used for transition world decals - SV_CreateDecal( &sv.signon, entry->position, decalIndex, entityIndex, modelIndex, flags, entry->scale ); - } -} - -void RestoreSound( soundlist_t *entry ) -{ - int soundIndex; - int flags = 0; - edict_t *ent; - - // this can happens if serialized map contain 4096 static decals... - if( MSG_GetNumBytesLeft( &sv.signon ) < 36 ) - return; - - if( entry->name[0] == '!' && Q_isdigit( entry->name + 1 )) - { - flags |= SND_SENTENCE; - soundIndex = Q_atoi( entry->name + 1 ); - } - else if( entry->name[0] == '#' && Q_isdigit( entry->name + 1 )) - { - flags |= SND_SENTENCE|SND_SEQUENCE; - soundIndex = Q_atoi( entry->name + 1 ); - } - else - { - // precache_sound can be used twice: cache sounds when loading - // and return sound index when server is active - soundIndex = SV_SoundIndex( entry->name ); - } - - ent = EDICT_NUM( entry->entnum ); - - if( entry->attenuation < 0.0f || entry->attenuation > 4.0f ) - { - MsgDev( D_ERROR, "SV_RestoreSound: attenuation %g must be in range 0-4\n", entry->attenuation ); - return; - } - - if( entry->channel < 0 || entry->channel > 7 ) - { - MsgDev( D_ERROR, "SV_RestoreSound: channel must be in range 0-7\n" ); - return; - } - - if( !SV_IsValidEdict( ent )) - { - // died entity make a sound. Ignore - if( entry->channel != CHAN_STATIC ) - return; - - // just get world for static channel - ent = EDICT_NUM( 0 ); - } - - if( entry->volume != VOL_NORM ) flags |= SND_VOLUME; - if( entry->attenuation != ATTN_NONE ) flags |= SND_ATTENUATION; - if( entry->pitch != PITCH_NORM ) flags |= SND_PITCH; - if( !entry->looping ) flags |= SND_STOP_LOOPING; // just in case - - MSG_BeginServerCmd( &sv.signon, svc_restoresound ); - MSG_WriteUBitLong( &sv.signon, flags, MAX_SND_FLAGS_BITS ); - MSG_WriteUBitLong( &sv.signon, soundIndex, MAX_SOUND_BITS ); - MSG_WriteUBitLong( &sv.signon, entry->channel, MAX_SND_CHAN_BITS ); - - if( flags & SND_VOLUME ) MSG_WriteByte( &sv.signon, entry->volume * 255 ); - if( flags & SND_ATTENUATION ) MSG_WriteByte( &sv.signon, entry->attenuation * 64 ); - if( flags & SND_PITCH ) MSG_WriteByte( &sv.signon, entry->pitch ); - - MSG_WriteUBitLong( &sv.signon, entry->entnum, MAX_ENTITY_BITS ); - MSG_WriteVec3Coord( &sv.signon, entry->origin ); - MSG_WriteByte( &sv.signon, entry->wordIndex ); - - // send two doubles as raw-data - MSG_WriteBytes( &sv.signon, &entry->samplePos, sizeof( entry->samplePos )); - MSG_WriteBytes( &sv.signon, &entry->forcedEnd, sizeof( entry->forcedEnd )); -} - -void SV_ClearSaveDir( void ) +remove all the temp files HL1-HL3 +(it will be extracted again from another .sav file) +============= +*/ +static void ClearSaveDir( void ) { search_t *t; int i; // just delete all HL? files - t = FS_Search( "save/*.HL?", true, true ); // lookup only in gamedir + t = FS_Search( va( "%s*.HL?", DEFAULT_SAVE_DIRECTORY ), true, true ); if( !t ) return; // already empty for( i = 0; i < t->numfilenames; i++ ) @@ -582,21 +385,25 @@ void SV_ClearSaveDir( void ) Mem_Free( t ); } -int SV_IsValidSave( void ) +/* +============= +IsValidSave + +savegame is allowed? +============= +*/ +static int IsValidSave( void ) { - if( sv.background ) - return 0; - - // don't parse autosave/transition save/restores during playback! - if( CL_IsPlaybackDemo( )) - return 0; - if( !svs.initialized || sv.state != ss_active ) { Con_Printf( "Not playing a local game.\n" ); return 0; } + // ignore autosave during background + if( sv.background ) + return 0; + if( svgame.physFuncs.SV_AllowSaveGame != NULL ) { if( !svgame.physFuncs.SV_AllowSaveGame( )) @@ -649,227 +456,375 @@ int SV_IsValidSave( void ) return 0; } -void SV_AgeSaveList( const char *pName, int count ) +/* +============= +AgeSaveList + +scroll the name list down +============= +*/ +static void AgeSaveList( const char *pName, int count ) { - string newName, oldName, newImage, oldImage; + char newName[MAX_OSPATH], oldName[MAX_OSPATH]; + char newShot[MAX_OSPATH], oldShot[MAX_OSPATH]; // delete last quick/autosave (e.g. quick05.sav) - Q_snprintf( newName, sizeof( newName ), "save/%s%02d.sav", pName, count ); - Q_snprintf( newImage, sizeof( newImage ), "save/%s%02d.bmp", pName, count ); + Q_snprintf( newName, sizeof( newName ), "%s%s%02d.sav", DEFAULT_SAVE_DIRECTORY, pName, count ); + Q_snprintf( newShot, sizeof( newShot ), "%s%s%02d.bmp", DEFAULT_SAVE_DIRECTORY, pName, count ); // only delete from game directory, basedir is read-only FS_Delete( newName ); - FS_Delete( newImage ); + FS_Delete( newShot ); - GL_FreeImage( newImage ); + // unloading the shot footprint + GL_FreeImage( newShot ); while( count > 0 ) { if( count == 1 ) { // quick.sav - Q_snprintf( oldName, sizeof( oldName ), "save/%s.sav", pName ); - Q_snprintf( oldImage, sizeof( oldImage ), "save/%s.bmp", pName ); + Q_snprintf( oldName, sizeof( oldName ), "%s%s.sav", DEFAULT_SAVE_DIRECTORY, pName ); + Q_snprintf( oldShot, sizeof( oldShot ), "%s%s.bmp", DEFAULT_SAVE_DIRECTORY, pName ); } else { // quick04.sav, etc. - Q_snprintf( oldName, sizeof( oldName ), "save/%s%02d.sav", pName, count - 1 ); - Q_snprintf( oldImage, sizeof( oldImage ), "save/%s%02d.bmp", pName, count - 1 ); + Q_snprintf( oldName, sizeof( oldName ), "%s%s%02d.sav", DEFAULT_SAVE_DIRECTORY, pName, count - 1 ); + Q_snprintf( oldShot, sizeof( oldShot ), "%s%s%02d.bmp", DEFAULT_SAVE_DIRECTORY, pName, count - 1 ); } - Q_snprintf( newName, sizeof( newName ), "save/%s%02d.sav", pName, count ); - Q_snprintf( newImage, sizeof( newImage ), "save/%s%02d.bmp", pName, count ); + Q_snprintf( newName, sizeof( newName ), "%s%s%02d.sav", DEFAULT_SAVE_DIRECTORY, pName, count ); + Q_snprintf( newShot, sizeof( newShot ), "%s%s%02d.bmp", DEFAULT_SAVE_DIRECTORY, pName, count ); - GL_FreeImage( oldImage ); + // unloading the oldshot footprint too + GL_FreeImage( oldShot ); - // scroll the name list down (rename quick04.sav to quick05.sav) + // scroll the name list down (e.g. rename quick04.sav to quick05.sav) FS_Rename( oldName, newName ); - FS_Rename( oldImage, newImage ); + FS_Rename( oldShot, newShot ); count--; } } -void SV_DirectoryCopy( const char *pPath, file_t *pFile ) +/* +================== +SaveGetName + +build the savename +================== +*/ +static qboolean SaveGetName( int lastnum, char *filename ) { - search_t *t; - file_t *pCopy; + int a, b, c; + + if( !COM_CheckString( filename )) + return false; + + if( lastnum < 0 || lastnum > 999 ) + return false; + + a = lastnum / 100; + lastnum -= a * 100; + b = lastnum / 10; + c = lastnum % 10; + + Q_sprintf( filename, "save%i%i%i", a, b, c ); + return true; +} + +/* +============= +DirectoryCopy + +put the HL1-HL3 files into .sav file +============= +*/ +static void DirectoryCopy( const char *pPath, file_t *pFile ) +{ + char szName[MAX_OSPATH]; int i, fileSize; - char szName[SAVENAME_LENGTH]; + file_t *pCopy; + search_t *t; t = FS_Search( pPath, true, true ); - if( !t ) return; + if( !t ) return; // nothing to copy ? for( i = 0; i < t->numfilenames; i++ ) { - fileSize = FS_FileSize( t->filenames[i], true ); pCopy = FS_Open( t->filenames[i], "rb", true ); + fileSize = FS_FileLength( pCopy ); - // filename can only be as long as a map name + extension - Q_strncpy( szName, COM_FileWithoutPath( t->filenames[i] ), SAVENAME_LENGTH ); - FS_Write( pFile, szName, SAVENAME_LENGTH ); + memset( szName, 0, sizeof( szName )); // clearing the string to prevent garbage in output file + Q_strncpy( szName, COM_FileWithoutPath( t->filenames[i] ), MAX_OSPATH ); + FS_Write( pFile, szName, MAX_OSPATH ); FS_Write( pFile, &fileSize, sizeof( int )); FS_FileCopy( pFile, pCopy, fileSize ); FS_Close( pCopy ); } - Mem_Free( t ); } -void SV_DirectoryExtract( file_t *pFile, int fileCount ) +/* +============= +DirectoryExtract + +extract the HL1-HL3 files from the .sav file +============= +*/ +static void DirectoryExtract( file_t *pFile, int fileCount ) { - char szName[SAVENAME_LENGTH]; - char fileName[SAVENAME_LENGTH]; + char szName[MAX_OSPATH]; + char fileName[MAX_OSPATH]; int i, fileSize; file_t *pCopy; for( i = 0; i < fileCount; i++ ) { // filename can only be as long as a map name + extension - FS_Read( pFile, fileName, SAVENAME_LENGTH ); + FS_Read( pFile, szName, MAX_OSPATH ); FS_Read( pFile, &fileSize, sizeof( int )); - Q_snprintf( szName, sizeof( szName ), "save/%s", fileName ); + Q_snprintf( fileName, sizeof( fileName ), "%s%s", DEFAULT_SAVE_DIRECTORY, szName ); + COM_FixSlashes( fileName ); - pCopy = FS_Open( szName, "wb", true ); + pCopy = FS_Open( fileName, "wb", true ); FS_FileCopy( pCopy, pFile, fileSize ); FS_Close( pCopy ); } } -void SV_SaveFinish( SAVERESTOREDATA *pSaveData ) -{ - char **pTokens; - ENTITYTABLE *pEntityTable; +/* +============= +SaveInit - if( !pSaveData ) return; - - pTokens = SaveRestore_DetachSymbolTable( pSaveData ); - if( pTokens ) Mem_Free( pTokens ); - - pEntityTable = SaveRestore_DetachEntityTable( pSaveData ); - if( pEntityTable ) Mem_Free( pEntityTable ); - - if( pSaveData ) Mem_Free( pSaveData ); - - svgame.globals->pSaveData = NULL; -} - -SAVERESTOREDATA *SV_SaveInit( int size ) +initialize global save-restore buffer +============= +*/ +static SAVERESTOREDATA *SaveInit( int size, int tokenCount ) { SAVERESTOREDATA *pSaveData; - const int nTokens = 0xfff; // Assume a maximum of 4K-1 symbol table entries(each of some length) - int numents; - if( size <= 0 ) size = 0x400000; // Reserve 4Mb for now - numents = svgame.numEntities; + pSaveData = Mem_Alloc( host.mempool, sizeof( SAVERESTOREDATA ) + size ); + pSaveData->pTokens = (char **)Mem_Alloc( host.mempool, tokenCount * sizeof( char* )); + pSaveData->tokenCount = tokenCount; - pSaveData = Mem_Alloc( host.mempool, sizeof(SAVERESTOREDATA) + ( sizeof(ENTITYTABLE) * numents ) + size ); - SaveRestore_Init( pSaveData, (char *)(pSaveData + 1), size ); // skip the save structure - SaveRestore_InitSymbolTable( pSaveData, (char **)Mem_Alloc( host.mempool, nTokens * sizeof( char* )), nTokens ); + pSaveData->pBaseData = (char *)(pSaveData + 1); // skip the save structure); + pSaveData->pCurrentData = pSaveData->pBaseData; // reset the pointer + pSaveData->bufferSize = size; pSaveData->time = svgame.globals->time; // Use DLL time - VectorClear( pSaveData->vecLandmarkOffset ); - pSaveData->fUseLandmark = false; - pSaveData->connectionCount = 0; - + // shared with dlls svgame.globals->pSaveData = pSaveData; return pSaveData; } -void SV_SaveGameStateGlobals( SAVERESTOREDATA *pSaveData ) +/* +============= +SaveClear + +clearing buffer for reuse +============= +*/ +static void SaveClear( SAVERESTOREDATA *pSaveData ) { - sv_client_t *cl; - SAVE_HEADER header; - SAVE_LIGHTSTYLE light; - int i; - - // write global data - header.skillLevel = Cvar_VariableValue( "skill" ); // This is created from an int even though it's a float - header.connectionCount = pSaveData->connectionCount; - header.time = svgame.globals->time; + memset( pSaveData->pTokens, 0, pSaveData->tokenCount * sizeof( char* )); - if( sv_skyname.string[0] ) - Q_strncpy( header.skyName, sv_skyname.string, sizeof( header.skyName )); - else Q_strncpy( header.skyName, "", sizeof( header.skyName )); + pSaveData->pBaseData = (char *)(pSaveData + 1); // skip the save structure); + pSaveData->pCurrentData = pSaveData->pBaseData; // reset the pointer + pSaveData->time = svgame.globals->time; // Use DLL time + pSaveData->tokenSize = 0; // reset the hashtable + pSaveData->size = 0; // reset the pointer - Q_strncpy( header.mapName, sv.name, sizeof( header.mapName )); - header.lightStyleCount = 0; - header.entityCount = svgame.numEntities; + // shared with dlls + svgame.globals->pSaveData = pSaveData; +} - for( i = 0; i < MAX_LIGHTSTYLES; i++ ) +/* +============= +SaveInit + +release global save-restore buffer +============= +*/ +static void SaveFinish( SAVERESTOREDATA *pSaveData ) +{ + if( !pSaveData ) return; + + if( pSaveData->pTokens ) { - if( sv.lightstyles[i].pattern[0] ) - header.lightStyleCount++; + Mem_Free( pSaveData->pTokens ); + pSaveData->pTokens = NULL; + pSaveData->tokenCount = 0; } - // sky variables - header.skyColor_r = Cvar_VariableValue( "sv_skycolor_r" ); - header.skyColor_g = Cvar_VariableValue( "sv_skycolor_g" ); - header.skyColor_b = Cvar_VariableValue( "sv_skycolor_b" ); - header.skyVec_x = Cvar_VariableValue( "sv_skyvec_x" ); - header.skyVec_y = Cvar_VariableValue( "sv_skyvec_y" ); - header.skyVec_z = Cvar_VariableValue( "sv_skyvec_z" ); - header.skyDir_x = Cvar_VariableValue( "sv_skydir_x" ); - header.skyDir_y = Cvar_VariableValue( "sv_skydir_y" ); - header.skyDir_z = Cvar_VariableValue( "sv_skydir_z" ); - header.skyAngle = Cvar_VariableValue( "sv_skyangle" ); - header.skySpeed = Cvar_VariableValue( "sv_skyspeed" ); - - // save viewentity to allow camera works after save\restore - if(( cl = SV_ClientFromEdict( EDICT_NUM( 1 ), true )) != NULL ) + if( pSaveData->pTable ) { - if( cl->pViewEntity ) - header.viewentity = NUM_FOR_EDICT( cl->pViewEntity ); - else header.viewentity = 1; + Mem_Free( pSaveData->pTable ); + pSaveData->pTable = NULL; + pSaveData->tableCount = 0; } - else header.viewentity = 1; - header.serverflags = (int)svgame.globals->serverflags; - header.wateralpha = Cvar_VariableValue( "sv_wateralpha" ); + svgame.globals->pSaveData = NULL; + Mem_Free( pSaveData ); +} - pSaveData->time = 0.0f; // prohibits rebase of header.time (why not just save time as a field_float and ditch this hack?) - svgame.dllFuncs.pfnSaveWriteFields( pSaveData, "Save Header", &header, gSaveHeader, ARRAYSIZE( gSaveHeader )); - pSaveData->time = header.time; +/* +============= +DumpHashStrings - // write entity table - for( i = 0; i < pSaveData->tableCount; i++ ) - svgame.dllFuncs.pfnSaveWriteFields( pSaveData, "ETABLE", pSaveData->pTable + i, gEntityTable, ARRAYSIZE( gEntityTable )); +debug thing +============= +*/ +static void DumpHashStrings( SAVERESTOREDATA *pSaveData, const char *pMessage ) +{ + int i, count = 0; - // write adjacency list - for( i = 0; i < pSaveData->connectionCount; i++ ) - svgame.dllFuncs.pfnSaveWriteFields( pSaveData, "ADJACENCY", pSaveData->levelList + i, gAdjacency, ARRAYSIZE( gAdjacency )); - - // write the lightstyles - for( i = 0; i < MAX_LIGHTSTYLES; i++ ) + if( pSaveData && pSaveData->pTokens ) { - if( sv.lightstyles[i].pattern[0] ) + Con_Printf( "%s\n", pMessage ); + + for( i = 0; i < pSaveData->tokenCount; i++ ) { - light.index = i; - light.time = sv.lightstyles[i].time; - Q_strncpy( light.style, sv.lightstyles[i].pattern, sizeof( light.style )); - svgame.dllFuncs.pfnSaveWriteFields( pSaveData, "LIGHTSTYLE", &light, gLightStyle, ARRAYSIZE( gLightStyle )); + if( !pSaveData->pTokens[i] ) + continue; + + Con_Printf( "#%i %s\n", count, pSaveData->pTokens[i] ); + count++; } + Con_Printf( "total %i actual %i\n", pSaveData->tokenCount, count ); } } -SAVERESTOREDATA *SV_LoadSaveData( const char *level ) -{ - string name; - file_t *pFile; - SaveFileSectionsInfo_t sectionsInfo; - SAVERESTOREDATA *pSaveData; - char *pszTokenList; - int i, id, size, version; - - Q_snprintf( name, sizeof( name ), "save/%s.HL1", level ); - Con_DPrintf( "Loading game from %s...\n", name ); +/* +============= +StoreHashTable - pFile = FS_Open( name, "rb", true ); - if( !pFile ) +write the stringtable into file +============= +*/ +static char *StoreHashTable( SAVERESTOREDATA *pSaveData ) +{ + char *pTokenData = pSaveData->pCurrentData; + int i; + + // Write entity string token table + if( pSaveData->pTokens ) { - Con_DPrintf( S_ERROR "couldn't open.\n" ); + for( i = 0; i < pSaveData->tokenCount; i++ ) + { + char *pszToken = pSaveData->pTokens[i] ? pSaveData->pTokens[i] : ""; + + // just copy the token byte-by-byte + while( *pszToken ) + *pSaveData->pCurrentData++ = *pszToken++; + *pSaveData->pCurrentData++ = 0; // Write the term + } + } + + pSaveData->tokenSize = pSaveData->pCurrentData - pTokenData; + + return pTokenData; +} + +/* +============= +BuildHashTable + +build the stringtable from buffer +============= +*/ +static void BuildHashTable( SAVERESTOREDATA *pSaveData, file_t *pFile ) +{ + char *pszTokenList = pSaveData->pBaseData; + int i; + + // Parse the symbol table + if( pSaveData->tokenSize > 0 ) + { + FS_Read( pFile, pszTokenList, pSaveData->tokenSize ); + + // make sure the token strings pointed to by the pToken hashtable. + for( i = 0; i < pSaveData->tokenCount; i++ ) + { + pSaveData->pTokens[i] = *pszTokenList ? pszTokenList : NULL; + while( *pszTokenList++ ); // Find next token (after next null) + } + } + + // rebase the data pointer + pSaveData->pBaseData = pszTokenList; // pszTokenList now points after token data + pSaveData->pCurrentData = pSaveData->pBaseData; +} + +/* +============= +GetClientDataSize + +g-cont: this routine is redundant +i'm write it just for more readable code +============= +*/ +static int GetClientDataSize( const char *level ) +{ + int tokenCount, tokenSize; + int size, id, version; + char name[MAX_QPATH]; + file_t *pFile; + + Q_snprintf( name, sizeof( name ), "%s%s.HL2", DEFAULT_SAVE_DIRECTORY, level ); + + if(( pFile = FS_Open( name, "rb", true )) == NULL ) + return 0; + + FS_Read( pFile, &id, sizeof( id )); + if( id != SAVEGAME_HEADER ) + { + FS_Close( pFile ); + return 0; + } + + FS_Read( pFile, &version, sizeof( version )); + if( version != CLIENT_SAVEGAME_VERSION ) + { + FS_Close( pFile ); + return 0; + } + + FS_Read( pFile, &size, sizeof( int )); + FS_Read( pFile, &tokenCount, sizeof( int )); + FS_Read( pFile, &tokenSize, sizeof( int )); + FS_Close( pFile ); + + return ( size + tokenSize ); +} + +/* +============= +LoadSaveData + +fill the save resore buffer +parse hash strings +============= +*/ +static SAVERESTOREDATA *LoadSaveData( const char *level ) +{ + int tokenSize, tableCount; + int size, tokenCount; + char name[MAX_OSPATH]; + int id, version; + int clientSize; + SAVERESTOREDATA *pSaveData; + int totalSize; + file_t *pFile; + + Q_snprintf( name, sizeof( name ), "%s%s.HL1", DEFAULT_SAVE_DIRECTORY, level ); + Con_Printf( "Loading game from %s...\n", name ); + + if(( pFile = FS_Open( name, "rb", true )) == NULL ) + { + Con_Printf( S_ERROR "couldn't open.\n" ); return NULL; } @@ -885,121 +840,103 @@ SAVERESTOREDATA *SV_LoadSaveData( const char *level ) } // Read the sections info and the data - FS_Read( pFile, §ionsInfo, sizeof( sectionsInfo )); + FS_Read( pFile, &size, sizeof( int )); // total size of all data to initialize read buffer + FS_Read( pFile, &tableCount, sizeof( int )); // entities count to right initialize entity table + FS_Read( pFile, &tokenCount, sizeof( int )); // num hash tokens to prepare token table + FS_Read( pFile, &tokenSize, sizeof( int )); // total size of hash tokens + + // determine highest size of seve-restore buffer + // because it's used twice: for HL1 and HL2 restore + clientSize = GetClientDataSize( level ); + totalSize = Q_max( clientSize, ( size + tokenSize )); + + // init the read buffer + pSaveData = SaveInit( totalSize, tokenCount ); - pSaveData = Mem_Alloc( host.mempool, sizeof(SAVERESTOREDATA) + SumBytes( §ionsInfo )); Q_strncpy( pSaveData->szCurrentMapName, level, sizeof( pSaveData->szCurrentMapName )); - - FS_Read( pFile, (char *)(pSaveData + 1), SumBytes( §ionsInfo )); - FS_Close( pFile ); - + pSaveData->tableCount = tableCount; // count ETABLE entries + pSaveData->tokenCount = tokenCount; + pSaveData->tokenSize = tokenSize; + // Parse the symbol table - pszTokenList = (char *)(pSaveData + 1); // Skip past the CSaveRestoreData structure + BuildHashTable( pSaveData, pFile ); - if( sectionsInfo.nBytesSymbols > 0 ) - { - SaveRestore_InitSymbolTable( pSaveData, (char **)Mem_Alloc( host.mempool, sectionsInfo.nSymbols * sizeof( char* )), sectionsInfo.nSymbols ); - - // make sure the token strings pointed to by the pToken hashtable. - for( i = 0; i < sectionsInfo.nSymbols; i++ ) - { - if( *pszTokenList ) - SaveRestore_DefineSymbol( pSaveData, pszTokenList, i ); - // find next token (after next null) - while( *pszTokenList++ ); - } - } - else - { - SaveRestore_InitSymbolTable( pSaveData, NULL, 0 ); - } - - Assert( pszTokenList - (char *)(pSaveData + 1) == sectionsInfo.nBytesSymbols ); - - // set up the restore basis - size = SumBytes( §ionsInfo ) - sectionsInfo.nBytesSymbols; - - // the point pszTokenList was incremented to the end of the tokens - SaveRestore_Init( pSaveData, (char *)(pszTokenList), size ); - - pSaveData->connectionCount = 0; + // Set up the restore basis pSaveData->fUseLandmark = true; pSaveData->time = 0.0f; - VectorClear( pSaveData->vecLandmarkOffset ); - // shared with dlls - svgame.globals->pSaveData = pSaveData; + // now reading all the rest of data + FS_Read( pFile, pSaveData->pBaseData, size ); + FS_Close( pFile ); // data is sucessfully moved into SaveRestore buffer (ETABLE will be init later) return pSaveData; } -void SV_ReadEntityTable( SAVERESTOREDATA *pSaveData ) -{ - ENTITYTABLE *pEntityTable; - int i; +/* +============= +ParseSaveTables - pEntityTable = (ENTITYTABLE *)Mem_Alloc( host.mempool, sizeof( ENTITYTABLE ) * pSaveData->tableCount ); - SaveRestore_InitEntityTable( pSaveData, pEntityTable, pSaveData->tableCount ); - - for( i = 0; i < pSaveData->tableCount; i++ ) - svgame.dllFuncs.pfnSaveReadFields( pSaveData, "ETABLE", pSaveData->pTable + i, gEntityTable, ARRAYSIZE( gEntityTable )); -} - -void SV_ParseSaveTables( SAVERESTOREDATA *pSaveData, SAVE_HEADER *pHeader, int setupLightstyles ) +reading global data, setup ETABLE's +============= +*/ +static void ParseSaveTables( SAVERESTOREDATA *pSaveData, SAVE_HEADER *pHeader, int updateGlobals ) { SAVE_LIGHTSTYLE light; int i; + // Re-base the savedata since we re-ordered the entity/table / restore fields + InitEntityTable( pSaveData, pSaveData->tableCount ); + + for( i = 0; i < pSaveData->tableCount; i++ ) + svgame.dllFuncs.pfnSaveReadFields( pSaveData, "ETABLE", &pSaveData->pTable[i], gEntityTable, ARRAYSIZE( gEntityTable )); + + pSaveData->pBaseData = pSaveData->pCurrentData; + pSaveData->size = 0; + // process SAVE_HEADER svgame.dllFuncs.pfnSaveReadFields( pSaveData, "Save Header", pHeader, gSaveHeader, ARRAYSIZE( gSaveHeader )); pSaveData->connectionCount = pHeader->connectionCount; + VectorClear( pSaveData->vecLandmarkOffset ); pSaveData->time = pHeader->time; pSaveData->fUseLandmark = true; - VectorClear( pSaveData->vecLandmarkOffset ); - pSaveData->tableCount = pHeader->entityCount; - - SV_ReadEntityTable( pSaveData ); // read adjacency list for( i = 0; i < pSaveData->connectionCount; i++ ) - { - LEVELLIST *pList = &pSaveData->levelList[i]; - svgame.dllFuncs.pfnSaveReadFields( pSaveData, "ADJACENCY", pList, gAdjacency, ARRAYSIZE( gAdjacency )); - } + svgame.dllFuncs.pfnSaveReadFields( pSaveData, "ADJACENCY", &pSaveData->levelList[i], gAdjacency, ARRAYSIZE( gAdjacency )); - if( setupLightstyles ) + if( updateGlobals ) memset( sv.lightstyles, 0, sizeof( sv.lightstyles )); for( i = 0; i < pHeader->lightStyleCount; i++ ) { svgame.dllFuncs.pfnSaveReadFields( pSaveData, "LIGHTSTYLE", &light, gLightStyle, ARRAYSIZE( gLightStyle )); - if( setupLightstyles ) SV_SetLightStyle( light.index, light.style, light.time ); + if( updateGlobals ) SV_SetLightStyle( light.index, light.style, light.time ); } } /* ============= -SV_EntityPatchWrite +EntityPatchWrite write out the list of entities that are no longer in the save file for this level (they've been moved to another level) ============= */ -void SV_EntityPatchWrite( SAVERESTOREDATA *pSaveData, const char *level ) +static void EntityPatchWrite( SAVERESTOREDATA *pSaveData, const char *level ) { - string name; - file_t *pFile; - int i, size; + char name[MAX_QPATH]; + int i, size = 0; + file_t *pFile; - Q_snprintf( name, sizeof( name ), "save/%s.HL3", level ); + Q_snprintf( name, sizeof( name ), "%s%s.HL3", DEFAULT_SAVE_DIRECTORY, level ); - pFile = FS_Open( name, "wb", true ); - if( !pFile ) return; + if(( pFile = FS_Open( name, "wb", true )) == NULL ) + return; - for( i = size = 0; i < pSaveData->tableCount; i++ ) + for( i = 0; i < pSaveData->tableCount; i++ ) { - if( pSaveData->pTable[i].flags & FENTTABLE_REMOVED ) + if( FBitSet( pSaveData->pTable[i].flags, FENTTABLE_REMOVED )) size++; } @@ -1008,7 +945,7 @@ void SV_EntityPatchWrite( SAVERESTOREDATA *pSaveData, const char *level ) for( i = 0; i < pSaveData->tableCount; i++ ) { - if( pSaveData->pTable[i].flags & FENTTABLE_REMOVED ) + if( FBitSet( pSaveData->pTable[i].flags, FENTTABLE_REMOVED )) FS_Write( pFile, &i, sizeof( int )); } @@ -1017,22 +954,22 @@ void SV_EntityPatchWrite( SAVERESTOREDATA *pSaveData, const char *level ) /* ============= -SV_EntityPatchRead +EntityPatchRead read the list of entities that are no longer in the save file for this level (they've been moved to another level) ============= */ -void SV_EntityPatchRead( SAVERESTOREDATA *pSaveData, const char *level ) +static void EntityPatchRead( SAVERESTOREDATA *pSaveData, const char *level ) { - string name; - file_t *pFile; + char name[MAX_QPATH]; int i, size, entityId; + file_t *pFile; - Q_snprintf( name, sizeof( name ), "save/%s.HL3", level ); + Q_snprintf( name, sizeof( name ), "%s%s.HL3", DEFAULT_SAVE_DIRECTORY, level ); - pFile = FS_Open( name, "rb", true ); - if( !pFile ) return; + if(( pFile = FS_Open( name, "rb", true )) == NULL ) + return; // patch count FS_Read( pFile, &size, sizeof( int )); @@ -1048,499 +985,551 @@ void SV_EntityPatchRead( SAVERESTOREDATA *pSaveData, const char *level ) /* ============= -SV_SaveClientState +RestoreDecal + +restore decal\move across transition +============= +*/ +static void RestoreDecal( SAVERESTOREDATA *pSaveData, decallist_t *entry, qboolean adjacent ) +{ + int decalIndex, entityIndex = 0; + int flags = entry->flags; + int modelIndex = 0; + edict_t *pEdict; + + // never move permanent decals + if( adjacent && FBitSet( flags, FDECAL_PERMANENT )) + return; + + // restore entity and model index + pEdict = EdictFromTable( pSaveData, entry->entityIndex ); + + if( SV_RestoreCustomDecal( entry, pEdict, adjacent )) + return; // decal was sucessfully restored at the game-side + + // studio decals are handled at game-side + if( FBitSet( flags, FDECAL_STUDIO )) + return; + + if( SV_IsValidEdict( pEdict )) + modelIndex = pEdict->v.modelindex; + + if( SV_IsValidEdict( pEdict )) + entityIndex = NUM_FOR_EDICT( pEdict ); + + decalIndex = pfnDecalIndex( entry->name ); + + // this can happens if brush entity from previous level was turned into world geometry + if( adjacent && entry->entityIndex != 0 && !SV_IsValidEdict( pEdict )) + { + vec3_t testspot, testend; + trace_t tr; + + Con_Printf( S_ERROR "RestoreDecal: couldn't restore entity index %i\n", entry->entityIndex ); + + VectorCopy( entry->position, testspot ); + VectorMA( testspot, 5.0f, entry->impactPlaneNormal, testspot ); + + VectorCopy( entry->position, testend ); + VectorMA( testend, -5.0f, entry->impactPlaneNormal, testend ); + + tr = SV_Move( testspot, vec3_origin, vec3_origin, testend, MOVE_NOMONSTERS, NULL, false ); + + // NOTE: this code may does wrong result on moving brushes e.g. func_tracktrain + if( tr.fraction != 1.0f && !tr.allsolid ) + { + // check impact plane normal + float dot = DotProduct( entry->impactPlaneNormal, tr.plane.normal ); + + if( dot >= 0.95f ) + { + entityIndex = pfnIndexOfEdict( tr.ent ); + if( entityIndex > 0 ) modelIndex = tr.ent->v.modelindex; + SV_CreateDecal( &sv.signon, tr.endpos, decalIndex, entityIndex, modelIndex, flags, entry->scale ); + } + } + } + else + { + // global entity is exist on new level so we can apply decal in local space + SV_CreateDecal( &sv.signon, entry->position, decalIndex, entityIndex, modelIndex, flags, entry->scale ); + } +} + +/* +============= +RestoreSound + +continue playing sound from saved position +============= +*/ +static void RestoreSound( SAVERESTOREDATA *pSaveData, soundlist_t *snd ) +{ + edict_t *ent = EdictFromTable( pSaveData, snd->entnum ); + int flags = SND_RESTORE_POSITION; + + // this can happens if serialized map contain 4096 static decals... + if( MSG_GetNumBytesLeft( &sv.signon ) < 36 ) + return; + + if( !snd->looping ) + SetBits( flags, SND_STOP_LOOPING ); + + if( SV_BuildSoundMsg( &sv.signon, ent, snd->channel, snd->name, snd->volume * 255, snd->attenuation, flags, snd->pitch, snd->origin )) + { + // write extradata for svc_restoresound + MSG_WriteByte( &sv.signon, snd->wordIndex ); + MSG_WriteBytes( &sv.signon, &snd->samplePos, sizeof( snd->samplePos )); + MSG_WriteBytes( &sv.signon, &snd->forcedEnd, sizeof( snd->forcedEnd )); + } +} + +/* +============= +SaveClientState write out the list of premanent decals for this level ============= */ -void SV_SaveClientState( SAVERESTOREDATA *pSaveData, const char *level, int changelevel ) +static void SaveClientState( SAVERESTOREDATA *pSaveData, const char *level, int changelevel ) { - string name; - file_t *pFile; - decallist_t *decalList; - ClientSections_t sections; - int i, decalCount; - int id, version; - long header_offset, position; soundlist_t soundInfo[MAX_CHANNELS]; - string curtrack, looptrack; - int soundCount = 0; - byte decalFlags; + sv_client_t *cl = svs.clients; + char name[MAX_QPATH]; + int i, id, version; + char *pTokenData; + decallist_t *decalList; + SAVE_CLIENT header; + file_t *pFile; - Q_snprintf( name, sizeof( name ), "save/%s.HL2", level ); + // clearing the saving buffer to reuse + SaveClear( pSaveData ); - pFile = FS_Open( name, "wb", true ); - if( !pFile ) return; - - id = SAVEFILE_HEADER; - version = CLIENT_SAVEGAME_VERSION; - - // write the header - FS_Write( pFile, &id, sizeof( int )); - FS_Write( pFile, &version, sizeof( int )); - - memset( §ions, -1, sizeof( sections )); - header_offset = FS_Tell( pFile ); // save header offset - - // write offsets (will be merged later) - FS_Write( pFile, §ions, sizeof( sections )); + memset( &header, 0, sizeof( header )); // g-cont. add space for studiodecals if present decalList = (decallist_t *)Z_Malloc( sizeof( decallist_t ) * MAX_RENDER_DECALS * 2 ); - decalCount = R_CreateDecalList( decalList ); - // DECALS SECTION - sections.offsets[LUMP_DECALS_OFFSET] = FS_Tell( pFile ); - FS_Write( pFile, &decalCount, sizeof( int )); + // initialize client header + header.decalCount = R_CreateDecalList( decalList ); + header.entityCount = sv.num_static_entities; - // we can't use SaveRestore system here... - for( i = 0; i < decalCount; i++ ) + if( !changelevel ) { - vec3_t localPos; - decallist_t *entry; - word decalScale; - byte nameSize; - - entry = &decalList[i]; - - if( pSaveData->fUseLandmark && ( entry->flags & FDECAL_USE_LANDMARK )) - VectorSubtract( entry->position, pSaveData->vecLandmarkOffset, localPos ); - else VectorCopy( entry->position, localPos ); - - nameSize = Q_strlen( entry->name ) + 1; - decalScale = (entry->scale * 4096); - decalFlags = entry->flags; - - FS_Write( pFile, localPos, sizeof( localPos )); - FS_Write( pFile, &nameSize, sizeof( nameSize )); - FS_Write( pFile, entry->name, nameSize ); - FS_Write( pFile, &entry->entityIndex, sizeof( entry->entityIndex )); - FS_Write( pFile, &decalFlags, sizeof( decalFlags )); - FS_Write( pFile, &decalScale, sizeof( decalScale )); - FS_Write( pFile, entry->impactPlaneNormal, sizeof( entry->impactPlaneNormal )); - - if( entry->flags & FDECAL_STUDIO ) - { - // write additional data for studio decals - FS_Write( pFile, &entry->studio_state, sizeof( entry->studio_state )); - } + // sounds won't going across transition + header.soundCount = S_GetCurrentDynamicSounds( soundInfo, MAX_CHANNELS ); + // music not reqiured to save position: it's just continue playing on a next level + S_StreamGetCurrentState( header.introTrack, header.mainTrack, &header.trackPosition ); } + // save viewentity to allow camera works after save\restore + if( SV_IsValidEdict( cl->pViewEntity ) && cl->pViewEntity != cl->edict ) + header.viewentity = NUM_FOR_EDICT( cl->pViewEntity ); + + header.wateralpha = sv_wateralpha.value; + header.wateramp = sv_wateramp.value; + + // Store the client header + svgame.dllFuncs.pfnSaveWriteFields( pSaveData, "ClientHeader", &header, gSaveClient, ARRAYSIZE( gSaveClient )); + + // store decals + for( i = 0; i < header.decalCount; i++ ) + { + // NOTE: apply landmark offset only for brush entities without origin brushes + if( pSaveData->fUseLandmark && FBitSet( decalList[i].flags, FDECAL_USE_LANDMARK )) + VectorSubtract( decalList[i].position, pSaveData->vecLandmarkOffset, decalList[i].position ); + + svgame.dllFuncs.pfnSaveWriteFields( pSaveData, "DECALLIST", &decalList[i], gDecalEntry, ARRAYSIZE( gDecalEntry )); + } Z_Free( decalList ); - // STATIC ENTITIES SECTION - if( sv.num_static_entities != 0 ) - { - sections.offsets[LUMP_STATIC_OFFSET] = FS_Tell( pFile ); - FS_Write( pFile, &sv.num_static_entities, sizeof( int )); - } + // write client entities + for( i = 0; i < header.entityCount; i++ ) + svgame.dllFuncs.pfnSaveWriteFields( pSaveData, "STATICENTITY", &sv.static_entities[i], gStaticEntry, ARRAYSIZE( gStaticEntry )); - for( i = 0; i < sv.num_static_entities; i++ ) - { - sv_static_entity_t *entry; - byte nameSize; - word entScale; + // write sounds + for( i = 0; i < header.soundCount; i++ ) + svgame.dllFuncs.pfnSaveWriteFields( pSaveData, "SOUNDLIST", &soundInfo[i], gSoundEntry, ARRAYSIZE( gSoundEntry )); - entry = &sv.static_entities[i]; + // Write entity string token table + pTokenData = StoreHashTable( pSaveData ); - nameSize = Q_strlen( entry->model ) + 1; - entScale = (entry->scale * 4096); + Q_snprintf( name, sizeof( name ), "%s%s.HL2", DEFAULT_SAVE_DIRECTORY, level ); - FS_Write( pFile, &nameSize, sizeof( nameSize )); - FS_Write( pFile, entry->model, nameSize ); - FS_Write( pFile, entry->origin, sizeof( entry->origin )); - FS_Write( pFile, entry->angles, sizeof( entry->angles )); - FS_Write( pFile, &entry->sequence, sizeof( entry->sequence )); - FS_Write( pFile, &entry->frame, sizeof( entry->frame )); - FS_Write( pFile, &entry->colormap, sizeof( entry->colormap )); - FS_Write( pFile, &entry->skin, sizeof( entry->skin )); - FS_Write( pFile, &entry->body, sizeof( entry->body )); - FS_Write( pFile, &entScale, sizeof( entScale )); - FS_Write( pFile, &entry->rendermode, sizeof( entry->rendermode )); + // output to disk + if(( pFile = FS_Open( name, "wb", true )) == NULL ) + return; // something bad is happens - if( entry->rendermode != kRenderNormal ) - { - // write additional render data - FS_Write( pFile, &entry->renderamt, sizeof( entry->renderamt )); - FS_Write( pFile, &entry->rendercolor, sizeof( entry->rendercolor )); - FS_Write( pFile, &entry->renderfx, sizeof( entry->renderfx )); - } - } + version = CLIENT_SAVEGAME_VERSION; + id = SAVEGAME_HEADER; - // DYNAMIC SOUNDS SECTION (don't go across transition) - if( !changelevel && ( soundCount = S_GetCurrentDynamicSounds( soundInfo, MAX_CHANNELS )) != 0 ) - { - sections.offsets[LUMP_SOUNDS_OFFSET] = FS_Tell( pFile ); - FS_Write( pFile, &soundCount, sizeof( int )); - } - - for( i = 0; i < soundCount; i++ ) - { - soundlist_t *entry; - byte nameSize; - - entry = &soundInfo[i]; - - nameSize = Q_strlen( entry->name ) + 1; - - FS_Write( pFile, &nameSize, sizeof( nameSize )); - FS_Write( pFile, entry->name, nameSize ); - FS_Write( pFile, entry->origin, sizeof( entry->origin )); - FS_Write( pFile, &entry->entnum, sizeof( entry->entnum )); - FS_Write( pFile, &entry->volume, sizeof( entry->volume )); - FS_Write( pFile, &entry->attenuation, sizeof( entry->attenuation )); - FS_Write( pFile, &entry->looping, sizeof( entry->looping )); - FS_Write( pFile, &entry->channel, sizeof( entry->channel )); - FS_Write( pFile, &entry->pitch, sizeof( entry->pitch )); - FS_Write( pFile, &entry->wordIndex, sizeof( entry->wordIndex )); - FS_Write( pFile, &entry->samplePos, sizeof( entry->samplePos )); - FS_Write( pFile, &entry->forcedEnd, sizeof( entry->forcedEnd )); - } - - // BACKGROUND MUSIC SECTION (don't go across transition) - if( !changelevel && S_StreamGetCurrentState( curtrack, looptrack, &position )) - { - byte nameSize; - - sections.offsets[LUMP_MUSIC_OFFSET] = FS_Tell( pFile ); - - // write current track - nameSize = Q_strlen( curtrack ) + 1; - FS_Write( pFile, &nameSize, sizeof( nameSize )); - FS_Write( pFile, curtrack, nameSize ); - - // write loop track - nameSize = Q_strlen( looptrack ) + 1; - FS_Write( pFile, &nameSize, sizeof( nameSize )); - FS_Write( pFile, looptrack, nameSize ); - - // write current track position - FS_Write( pFile, &position, sizeof( position )); - } - - // AT END - FS_Seek( pFile, header_offset, SEEK_SET ); - FS_Write( pFile, §ions, sizeof( sections )); // write real sections info + FS_Write( pFile, &id, sizeof( id )); + FS_Write( pFile, &version, sizeof( version )); + FS_Write( pFile, &pSaveData->size, sizeof( int )); // does not include token table + // write out the tokens first so we can load them before we load the entities + FS_Write( pFile, &pSaveData->tokenCount, sizeof( int )); + FS_Write( pFile, &pSaveData->tokenSize, sizeof( int )); + FS_Write( pFile, pTokenData, pSaveData->tokenSize ); + FS_Write( pFile, pSaveData->pBaseData, pSaveData->size ); // header and globals FS_Close( pFile ); } /* ============= -SV_LoadClientState +LoadClientState read the list of decals and reapply them again ============= */ -void SV_LoadClientState( SAVERESTOREDATA *pSaveData, const char *level, qboolean adjacent ) +static void LoadClientState( SAVERESTOREDATA *pSaveData, const char *level, qboolean changelevel, qboolean adjacent ) { - string name; + int tokenCount, tokenSize; + int i, size, id, version; + sv_client_t *cl = svs.clients; + char name[MAX_QPATH]; + sv_static_entity_t staticEntry; + soundlist_t soundEntry; + decallist_t decalEntry; + SAVE_CLIENT header; file_t *pFile; - int i, tag; - decallist_t *decalList; - int decalCount; - ClientSections_t sections; - soundlist_t soundInfo[MAX_CHANNELS]; - int soundCount; - byte decalFlags; - - Q_snprintf( name, sizeof( name ), "save/%s.HL2", level ); - pFile = FS_Open( name, "rb", true ); - if( !pFile ) return; + Q_snprintf( name, sizeof( name ), "%s%s.HL2", DEFAULT_SAVE_DIRECTORY, level ); - FS_Read( pFile, &tag, sizeof( int )); - if( tag != SAVEFILE_HEADER ) + if(( pFile = FS_Open( name, "rb", true )) == NULL ) + return; // something bad is happens + + FS_Read( pFile, &id, sizeof( id )); + if( id != SAVEGAME_HEADER ) { FS_Close( pFile ); return; } - FS_Read( pFile, &tag, sizeof( int )); - if( tag != CLIENT_SAVEGAME_VERSION ) + FS_Read( pFile, &version, sizeof( version )); + if( version != CLIENT_SAVEGAME_VERSION ) { FS_Close( pFile ); return; } - // read offsets - FS_Read( pFile, §ions, sizeof( sections )); + FS_Read( pFile, &size, sizeof( int )); + FS_Read( pFile, &tokenCount, sizeof( int )); + FS_Read( pFile, &tokenSize, sizeof( int )); - if( adjacent ) Con_Printf( "Loading decals from %s\n", level ); + // sanity check + ASSERT( pSaveData->bufferSize >= ( size + tokenSize )); - if( sections.offsets[LUMP_DECALS_OFFSET] != -1 ) - { - // jump to decals description - FS_Seek( pFile, sections.offsets[LUMP_DECALS_OFFSET], SEEK_SET ); + // clearing the restore buffer to reuse + SaveClear( pSaveData ); + pSaveData->tokenCount = tokenCount; + pSaveData->tokenSize = tokenSize; - // read the decalCount - FS_Read( pFile, &decalCount, sizeof( int )); - decalList = (decallist_t *)Z_Malloc( sizeof( decallist_t ) * decalCount ); - - // we can't use SaveRestore system here... - for( i = 0; i < decalCount; i++ ) - { - decallist_t *entry; - vec3_t localPos; - word decalScale; - byte nameSize; - - entry = &decalList[i]; - - FS_Read( pFile, localPos, sizeof( localPos )); - FS_Read( pFile, &nameSize, sizeof( nameSize )); - FS_Read( pFile, entry->name, nameSize ); - FS_Read( pFile, &entry->entityIndex, sizeof( entry->entityIndex )); - FS_Read( pFile, &decalFlags, sizeof( decalFlags )); - FS_Read( pFile, &decalScale, sizeof( decalScale )); - FS_Read( pFile, entry->impactPlaneNormal, sizeof( entry->impactPlaneNormal )); - - if( pSaveData->fUseLandmark && ( entry->flags & FDECAL_USE_LANDMARK )) - VectorAdd( localPos, pSaveData->vecLandmarkOffset, entry->position ); - else VectorCopy( localPos, entry->position ); - - entry->scale = ((float)decalScale / 4096.0f); - entry->flags = decalFlags; - - if( entry->flags & FDECAL_STUDIO ) - { - // read additional data for studio decals - FS_Read( pFile, &entry->studio_state, sizeof( entry->studio_state )); - } - - ReapplyDecal( pSaveData, entry, adjacent ); - } - - Z_Free( decalList ); - } - - // NOTE: static entities can't be moved across the levels because they are static :-) - if( sections.offsets[LUMP_STATIC_OFFSET] != -1 && !adjacent ) - { - // jump to static entities description - FS_Seek( pFile, sections.offsets[LUMP_STATIC_OFFSET], SEEK_SET ); - - // put static entities back to global array so we can save it again - FS_Read( pFile, &sv.num_static_entities, sizeof( int )); - - // clear old entities - memset( sv.static_entities, 0, sizeof( sv.static_entities )); - - for( i = 0; i < sv.num_static_entities; i++ ) - { - sv_static_entity_t *entry; - byte nameSize; - word entScale; - - if( i >= MAX_STATIC_ENTITIES ) - { - MsgDev( D_ERROR, "SV_LoadClientState: too many static entities %i\n", sv.num_static_entities ); - break; - } - - entry = &sv.static_entities[i]; - - FS_Read( pFile, &nameSize, sizeof( nameSize )); - FS_Read( pFile, entry->model, nameSize ); - FS_Read( pFile, entry->origin, sizeof( entry->origin )); - FS_Read( pFile, entry->angles, sizeof( entry->angles )); - FS_Read( pFile, &entry->sequence, sizeof( entry->sequence )); - FS_Read( pFile, &entry->frame, sizeof( entry->frame )); - FS_Read( pFile, &entry->colormap, sizeof( entry->colormap )); - FS_Read( pFile, &entry->skin, sizeof( entry->skin )); - FS_Read( pFile, &entry->body, sizeof( entry->body )); - FS_Read( pFile, &entScale, sizeof( entScale )); - FS_Read( pFile, &entry->rendermode, sizeof( entry->rendermode )); - - entry->scale = ((float)entScale / 4096.0f); - - if( entry->rendermode != kRenderNormal ) - { - // write additional render data - FS_Read( pFile, &entry->renderamt, sizeof( entry->renderamt )); - FS_Read( pFile, &entry->rendercolor, sizeof( entry->rendercolor )); - FS_Read( pFile, &entry->renderfx, sizeof( entry->renderfx )); - } - - SV_CreateStaticEntity( &sv.signon, entry ); - } - } - - // NOTE: sounds can't be moved across level - if( sections.offsets[LUMP_SOUNDS_OFFSET] != -1 && !adjacent ) - { - // jump to sounds description - FS_Seek( pFile, sections.offsets[LUMP_SOUNDS_OFFSET], SEEK_SET ); - - FS_Read( pFile, &soundCount, sizeof( int )); - - for( i = 0; i < Q_min( soundCount, MAX_CHANNELS ); i++ ) - { - soundlist_t *entry; - byte nameSize; - - entry = &soundInfo[i]; - - FS_Read( pFile, &nameSize, sizeof( nameSize )); - FS_Read( pFile, entry->name, nameSize ); - FS_Read( pFile, entry->origin, sizeof( entry->origin )); - FS_Read( pFile, &entry->entnum, sizeof( entry->entnum )); - FS_Read( pFile, &entry->volume, sizeof( entry->volume )); - FS_Read( pFile, &entry->attenuation, sizeof( entry->attenuation )); - FS_Read( pFile, &entry->looping, sizeof( entry->looping )); - FS_Read( pFile, &entry->channel, sizeof( entry->channel )); - FS_Read( pFile, &entry->pitch, sizeof( entry->pitch )); - FS_Read( pFile, &entry->wordIndex, sizeof( entry->wordIndex )); - FS_Read( pFile, &entry->samplePos, sizeof( entry->samplePos )); - FS_Read( pFile, &entry->forcedEnd, sizeof( entry->forcedEnd )); - - RestoreSound( entry ); - } - } - - // NOTE: music automatically goes across transition, never restore it on changelevel - if( sections.offsets[LUMP_MUSIC_OFFSET] != -1 && !adjacent ) - { - string curtrack, looptrack; - int position; - byte nameSize; - - // jump to music description - FS_Seek( pFile, sections.offsets[LUMP_MUSIC_OFFSET], SEEK_SET ); - - // read current track - FS_Read( pFile, &nameSize, sizeof( nameSize )); - FS_Read( pFile, curtrack, nameSize ); - - // read loop track - FS_Read( pFile, &nameSize, sizeof( nameSize )); - FS_Read( pFile, looptrack, nameSize ); - - // read current track position - FS_Read( pFile, &position, sizeof( position )); - - MSG_BeginServerCmd( &sv.signon, svc_stufftext ); - MSG_WriteString( &sv.signon, va( "music \"%s\" \"%s\" %i\n", curtrack, looptrack, position )); - } + // Parse the symbol table + BuildHashTable( pSaveData, pFile ); + FS_Read( pFile, pSaveData->pBaseData, size ); FS_Close( pFile ); + + // Read the client header + svgame.dllFuncs.pfnSaveReadFields( pSaveData, "ClientHeader", &header, gSaveClient, ARRAYSIZE( gSaveClient )); + + // restore decals + for( i = 0; i < header.decalCount; i++ ) + { + svgame.dllFuncs.pfnSaveReadFields( pSaveData, "DECALLIST", &decalEntry, gDecalEntry, ARRAYSIZE( gDecalEntry )); + + // NOTE: apply landmark offset only for brush entities without origin brushes + if( pSaveData->fUseLandmark && FBitSet( decalEntry.flags, FDECAL_USE_LANDMARK )) + VectorAdd( decalEntry.position, pSaveData->vecLandmarkOffset, decalEntry.position ); + RestoreDecal( pSaveData, &decalEntry, adjacent ); + } + + // clear old entities + if( !adjacent ) + { + memset( sv.static_entities, 0, sizeof( sv.static_entities )); + sv.num_static_entities = 0; + } + + // restore client entities + for( i = 0; i < header.entityCount; i++ ) + { + svgame.dllFuncs.pfnSaveReadFields( pSaveData, "STATICENTITY", &staticEntry, gStaticEntry, ARRAYSIZE( gStaticEntry )); + if( adjacent ) continue; // static entities won't loading from adjacent levels + + if( i >= MAX_STATIC_ENTITIES ) + continue; // silently overflowed + + SV_CreateStaticEntity( &sv.signon, &staticEntry ); + sv.static_entities[i] = staticEntry; + sv.num_static_entities++; + } + + // restore sounds + for( i = 0; i < header.soundCount; i++ ) + { + svgame.dllFuncs.pfnSaveReadFields( pSaveData, "SOUNDLIST", &soundEntry, gSoundEntry, ARRAYSIZE( gSoundEntry )); + if( adjacent ) continue; // sounds don't going across the levels + + RestoreSound( pSaveData, &soundEntry ); + } + + if( !adjacent ) + { + // restore camera view here + edict_t *pent = pSaveData->pTable[bound( 0, (word)header.viewentity, pSaveData->tableCount )].pent; + + if( Q_strlen( header.introTrack )) + { + // NOTE: music is automatically goes across transition, never restore it on changelevel + MSG_BeginServerCmd( &sv.signon, svc_stufftext ); + MSG_WriteString( &sv.signon, va( "music \"%s\" \"%s\" %i\n", header.introTrack, header.mainTrack, header.trackPosition )); + } + + // don't go camera across the levels + if( header.viewentity > svs.maxclients && !changelevel ) + cl->pViewEntity = pent; + + // restore some client cvars + Cvar_SetValue( "sv_wateralpha", header.wateralpha ); + Cvar_SetValue( "sv_wateramp", header.wateramp ); + } } /* ============= -SV_SaveGameState +CreateEntitiesInRestoreList + +alloc private data for restored entities +============= +*/ +static void CreateEntitiesInRestoreList( SAVERESTOREDATA *pSaveData, int levelMask, qboolean create_world ) +{ + int i, active; + ENTITYTABLE *pTable; + edict_t *pent; + + // create entity list + if( svgame.physFuncs.pfnCreateEntitiesInRestoreList != NULL ) + { + svgame.physFuncs.pfnCreateEntitiesInRestoreList( pSaveData, levelMask, create_world ); + } + else + { + for( i = 0; i < pSaveData->tableCount; i++ ) + { + pTable = &pSaveData->pTable[i]; + pent = NULL; + + if( pTable->classname && pTable->size && ( !FBitSet( pTable->flags, FENTTABLE_REMOVED ) || !create_world )) + { + if( !create_world ) + active = FBitSet( pTable->flags, levelMask ) ? 1 : 0; + else active = 1; + + if( pTable->id == 0 && create_world ) // worldspawn + { + pent = EDICT_NUM( 0 ); + SV_InitEdict( pent ); + pent = SV_CreateNamedEntity( pent, pTable->classname ); + } + else if(( pTable->id > 0 ) && ( pTable->id < svs.maxclients + 1 )) + { + edict_t *ed = EDICT_NUM( pTable->id ); + + if( !FBitSet( pTable->flags, FENTTABLE_PLAYER )) + Con_Printf( S_ERROR "ENTITY IS NOT A PLAYER: %d\n", i ); + + // create the player + if( active && SV_IsValidEdict( ed )) + pent = SV_CreateNamedEntity( ed, pTable->classname ); + } + else if( active ) + { + pent = SV_CreateNamedEntity( NULL, pTable->classname ); + } + } + + pTable->pent = pent; + } + } +} + +/* +============= +SaveGameState save current game state ============= */ -SAVERESTOREDATA *SV_SaveGameState( int changelevel ) +static SAVERESTOREDATA *SaveGameState( int changelevel ) { - SaveFileSectionsInfo_t sectionsInfo; - SaveFileSections_t sections; - SAVERESTOREDATA *pSaveData; - ENTITYTABLE *pTable; - file_t *pFile; - int i, numents; - int id, version; + char name[MAX_QPATH]; + int i, id, version; + char *pTableData; + char *pTokenData; + SAVERESTOREDATA *pSaveData; + int tableSize; + int dataSize; + ENTITYTABLE *pTable; + SAVE_HEADER header; + SAVE_LIGHTSTYLE light; + file_t *pFile; if( !svgame.dllFuncs.pfnParmsChangeLevel ) return NULL; - pSaveData = SV_SaveInit( 0 ); + pSaveData = SaveInit( SAVE_HEAPSIZE, SAVE_HASHSTRINGS ); - // Save the data - sections.pData = SaveRestore_AccessCurPos( pSaveData ); + Q_snprintf( name, sizeof( name ), "%s%s.HL1", DEFAULT_SAVE_DIRECTORY, sv.name ); + COM_FixSlashes( name ); - numents = svgame.numEntities; + // initialize entity table to count moved entities + InitEntityTable( pSaveData, svgame.numEntities ); - SaveRestore_InitEntityTable( pSaveData, Mem_Alloc( host.mempool, sizeof(ENTITYTABLE) * numents ), numents ); - - // Build the adjacent map list (after entity table build by game in presave) + // Build the adjacent map list svgame.dllFuncs.pfnParmsChangeLevel(); - // write entity descriptions - for( i = 0; i < svgame.numEntities; i++ ) + // Write the global data + header.skillLevel = (int)skill.value; // this is created from an int even though it's a float + header.entityCount = pSaveData->tableCount; + header.connectionCount = pSaveData->connectionCount; + header.time = svgame.globals->time; // use DLL time + Q_strncpy( header.mapName, sv.name, sizeof( header.mapName )); + Q_strncpy( header.skyName, sv_skyname.string, sizeof( header.skyName )); + header.skyColor_r = sv_skycolor_r.value; + header.skyColor_g = sv_skycolor_g.value; + header.skyColor_b = sv_skycolor_b.value; + header.skyVec_x = sv_skyvec_x.value; + header.skyVec_y = sv_skyvec_y.value; + header.skyVec_z = sv_skyvec_z.value; + header.lightStyleCount = 0; + + // counting the lightstyles + for( i = 0; i < MAX_LIGHTSTYLES; i++ ) { - edict_t *pent = EDICT_NUM( i ); - - pTable = &pSaveData->pTable[pSaveData->currentIndex]; - - svgame.dllFuncs.pfnSave( pent, pSaveData ); - - if( pent->v.flags & FL_CLIENT ) // mark client - pTable->flags |= FENTTABLE_PLAYER; - - if( pTable->classname && pTable->size ) - pTable->id = NUM_FOR_EDICT( pent ); - - pSaveData->currentIndex++; // move pointer + if( sv.lightstyles[i].pattern[0] ) + header.lightStyleCount++; } - sectionsInfo.nBytesData = SaveRestore_AccessCurPos( pSaveData ) - sections.pData; - - // Save necessary tables/dictionaries/directories - sections.pDataHeaders = SaveRestore_AccessCurPos( pSaveData ); + // Write the main header + pSaveData->time = 0.0f; // prohibits rebase of header.time (keep compatibility with old saves) + svgame.dllFuncs.pfnSaveWriteFields( pSaveData, "Save Header", &header, gSaveHeader, ARRAYSIZE( gSaveHeader )); + pSaveData->time = header.time; - SV_SaveGameStateGlobals( pSaveData ); + // Write the adjacency list + for( i = 0; i < pSaveData->connectionCount; i++ ) + svgame.dllFuncs.pfnSaveWriteFields( pSaveData, "ADJACENCY", &pSaveData->levelList[i], gAdjacency, ARRAYSIZE( gAdjacency )); - sectionsInfo.nBytesDataHeaders = SaveRestore_AccessCurPos( pSaveData ) - sections.pDataHeaders; - - // Write the save file symbol table - sections.pSymbols = SaveRestore_AccessCurPos( pSaveData ); - for( i = 0; i < pSaveData->tokenCount; i++ ) + // Write the lightstyles + for( i = 0; i < MAX_LIGHTSTYLES; i++ ) { - const char *pszToken = (SaveRestore_StringFromSymbol( pSaveData, i )); + if( !sv.lightstyles[i].pattern[0] ) + continue; - if( !pszToken ) pszToken = ""; + Q_strncpy( light.style, sv.lightstyles[i].pattern, sizeof( light.style )); + light.time = sv.lightstyles[i].time; + light.index = i; - if( !SaveRestore_Write( pSaveData, pszToken, Q_strlen( pszToken ) + 1 )) - break; - } + svgame.dllFuncs.pfnSaveWriteFields( pSaveData, "LIGHTSTYLE", &light, gLightStyle, ARRAYSIZE( gLightStyle )); + } - sectionsInfo.nBytesSymbols = SaveRestore_AccessCurPos( pSaveData ) - sections.pSymbols; - sectionsInfo.nSymbols = pSaveData->tokenCount; + // build the table of entities + // this is used to turn pointers into savable indices + // build up ID numbers for each entity, for use in pointer conversions + // if an entity requires a certain edict number upon restore, save that as well + for( i = 0; i < svgame.numEntities; i++ ) + { + pTable = &pSaveData->pTable[i]; + pTable->location = pSaveData->size; + pSaveData->currentIndex = i; + pTable->size = 0; - id = SAVEFILE_HEADER; - version = SAVEGAME_VERSION; + if( !SV_IsValidEdict( pTable->pent )) + continue; + + svgame.dllFuncs.pfnSave( pTable->pent, pSaveData ); + + if( FBitSet( pTable->pent->v.flags, FL_CLIENT )) + SetBits( pTable->flags, FENTTABLE_PLAYER ); + } + + // total data what includes: + // 1. save header + // 2. adjacency list + // 3. lightstyles + // 4. all the entity data + dataSize = pSaveData->size; + + // Write entity table + pTableData = pSaveData->pCurrentData; + + for( i = 0; i < pSaveData->tableCount; i++ ) + svgame.dllFuncs.pfnSaveWriteFields( pSaveData, "ETABLE", &pSaveData->pTable[i], gEntityTable, ARRAYSIZE( gEntityTable )); + + tableSize = pSaveData->size - dataSize; + + // Write entity string token table + pTokenData = StoreHashTable( pSaveData ); // output to disk - pFile = FS_Open( va( "save/%s.HL1", sv.name ), "wb", true ); - if( !pFile ) return NULL; + if(( pFile = FS_Open( name, "wb", true )) == NULL ) + { + // something bad is happens + SaveFinish( pSaveData ); + return NULL; + } + + // Write the header -- THIS SHOULD NEVER CHANGE STRUCTURE, USE SAVE_HEADER FOR NEW HEADER INFORMATION + // THIS IS ONLY HERE TO IDENTIFY THE FILE AND GET IT'S SIZE. + version = SAVEGAME_VERSION; + id = SAVEFILE_HEADER; // write the header - FS_Write( pFile, &id, sizeof( int )); - FS_Write( pFile, &version, sizeof( int )); + FS_Write( pFile, &id, sizeof( id )); + FS_Write( pFile, &version, sizeof( version )); - // Write out the tokens and table FIRST so they are loaded in the right order, - // then write out the rest of the data in the file. - FS_Write( pFile, §ionsInfo, sizeof( sectionsInfo )); - FS_Write( pFile, sections.pSymbols, sectionsInfo.nBytesSymbols ); - FS_Write( pFile, sections.pDataHeaders, sectionsInfo.nBytesDataHeaders ); - FS_Write( pFile, sections.pData, sectionsInfo.nBytesData ); + // Write out the tokens and table FIRST so they are loaded in the right order, then write out the rest of the data in the file. + FS_Write( pFile, &pSaveData->size, sizeof( int )); // total size of all data to initialize read buffer + FS_Write( pFile, &pSaveData->tableCount, sizeof( int )); // entities count to right initialize entity table + FS_Write( pFile, &pSaveData->tokenCount, sizeof( int )); // num hash tokens to prepare token table + FS_Write( pFile, &pSaveData->tokenSize, sizeof( int )); // total size of hash tokens + FS_Write( pFile, pTokenData, pSaveData->tokenSize ); // write tokens into the file + FS_Write( pFile, pTableData, tableSize ); // dump ETABLE structures + FS_Write( pFile, pSaveData->pBaseData, dataSize ); // and finally store all the other data FS_Close( pFile ); - SV_EntityPatchWrite( pSaveData, sv.name ); + EntityPatchWrite( pSaveData, sv.name ); - SV_SaveClientState( pSaveData, sv.name, changelevel ); + SaveClientState( pSaveData, sv.name, changelevel ); return pSaveData; } -int SV_LoadGameState( char const *level, qboolean changelevel ) +/* +============= +LoadGameState + +load current game state +============= +*/ +static int LoadGameState( char const *level, qboolean changelevel ) { - sv_client_t *cl = svs.clients; SAVERESTOREDATA *pSaveData; - ENTITYTABLE *pEntInfo; + ENTITYTABLE *pTable; SAVE_HEADER header; edict_t *pent; int i; - pSaveData = SV_LoadSaveData( level ); + pSaveData = LoadSaveData( level ); if( !pSaveData ) return 0; // couldn't load the file - SV_ParseSaveTables( pSaveData, &header, true ); + ParseSaveTables( pSaveData, &header, true ); + EntityPatchRead( pSaveData, level ); - SV_EntityPatchRead( pSaveData, level ); + // pause until all clients connect + sv.loadgame = sv.paused = true; Cvar_SetValue( "skill", header.skillLevel ); Q_strncpy( sv.name, header.mapName, sizeof( sv.name )); @@ -1554,106 +1543,37 @@ int SV_LoadGameState( char const *level, qboolean changelevel ) Cvar_SetValue( "sv_skyvec_x", header.skyVec_x ); Cvar_SetValue( "sv_skyvec_y", header.skyVec_y ); Cvar_SetValue( "sv_skyvec_z", header.skyVec_z ); - Cvar_SetValue( "sv_skydir_x", header.skyDir_x ); - Cvar_SetValue( "sv_skydir_y", header.skyDir_y ); - Cvar_SetValue( "sv_skydir_z", header.skyDir_z ); - Cvar_SetValue( "sv_skyangle", header.skyAngle ); - Cvar_SetValue( "sv_skyspeed", header.skySpeed ); - - // restore serverflags - svgame.globals->serverflags = header.serverflags; - - if( header.wateralpha <= 0.0f ) header.wateralpha = 1.0f; // make compatibility with old saves - Cvar_SetValue( "sv_wateralpha", header.wateralpha ); - - // re-base the savedata since we re-ordered the entity/table / restore fields - SaveRestore_Rebase( pSaveData ); // create entity list - if( svgame.physFuncs.pfnCreateEntitiesInRestoreList != NULL ) - { - svgame.physFuncs.pfnCreateEntitiesInRestoreList( pSaveData ); - } - else - { - for( i = 0; i < pSaveData->tableCount; i++ ) - { - pEntInfo = &pSaveData->pTable[i]; - - if( pEntInfo->classname != 0 && pEntInfo->size && !FBitSet( pEntInfo->flags, FENTTABLE_REMOVED )) - { - if( pEntInfo->id == 0 ) // worldspawn - { - Assert( i == 0 ); - - pent = EDICT_NUM( 0 ); - - SV_InitEdict( pent ); - pent = SV_CreateNamedEntity( pent, pEntInfo->classname ); - } - else if(( pEntInfo->id > 0 ) && ( pEntInfo->id < svs.maxclients + 1 )) - { - edict_t *ed; - - if( !FBitSet( pEntInfo->flags, FENTTABLE_PLAYER )) - { - MsgDev( D_WARN, "ENTITY IS NOT A PLAYER: %d\n", i ); - Assert( 0 ); - } - - ed = EDICT_NUM( pEntInfo->id ); - - if( ed != NULL ) - { - ASSERT( ed->free == false ); - // create the player - pent = SV_CreateNamedEntity( ed, pEntInfo->classname ); - } - else pent = NULL; - } - else - { - pent = SV_CreateNamedEntity( NULL, pEntInfo->classname ); - } - - pEntInfo->pent = pent; - } - else - { - pEntInfo->pent = NULL; // invalid - } - } - } + CreateEntitiesInRestoreList( pSaveData, 0, true ); // now spawn entities for( i = 0; i < pSaveData->tableCount; i++ ) { - pEntInfo = &pSaveData->pTable[i]; + pTable = &pSaveData->pTable[i]; + pSaveData->pCurrentData = pSaveData->pBaseData + pTable->location; + pSaveData->size = pTable->location; + pSaveData->currentIndex = i; + pent = pTable->pent; - pent = pEntInfo->pent; - SaveRestore_Seek( pSaveData, pEntInfo->location ); - - if( pent ) + if( pent != NULL ) { if( svgame.dllFuncs.pfnRestore( pent, pSaveData, false ) < 0 ) { - pEntInfo->pent = NULL; - pent->v.flags |= FL_KILLME; + SetBits( pent->v.flags, FL_KILLME ); + pTable->pent = NULL; + } + else + { + // force the entity to be relinked +// SV_LinkEdict( pent, false ); } } } - // restore camera view here - pent = pSaveData->pTable[bound( 0, (word)header.viewentity, pSaveData->tableCount )].pent; + LoadClientState( pSaveData, level, changelevel, false ); - // don't go camera across the levels - if( SV_IsValidEdict( pent ) && ( cl->edict != pent ) && !changelevel ) - cl->pViewEntity = pent; - else cl->pViewEntity = NULL; - - SV_LoadClientState( pSaveData, level, false ); - - SV_SaveFinish( pSaveData ); + SaveFinish( pSaveData ); // restore server time sv.time = header.time; @@ -1663,160 +1583,214 @@ int SV_LoadGameState( char const *level, qboolean changelevel ) /* ============= -SV_ClearGameState +SaveGameSlot -clear current game state +do a save game ============= */ -void SV_ClearGameState( void ) +static int SaveGameSlot( const char *pSaveName, const char *pSaveComment ) { - SV_ClearSaveDir(); + char hlPath[MAX_QPATH]; + char name[MAX_QPATH]; + int id, version; + char *pTokenData; + SAVERESTOREDATA *pSaveData; + GAME_HEADER gameHeader; + file_t *pFile; - if( svgame.dllFuncs.pfnResetGlobalState != NULL ) - svgame.dllFuncs.pfnResetGlobalState(); -} + pSaveData = SaveGameState( false ); + if( !pSaveData ) return 0; -// ripped out from the hl.dll -edict_t *SV_FindGlobalEntity( string_t classname, string_t globalname ) -{ - edict_t *pent = SV_FindEntityByString( NULL, "globalname", STRING( globalname )); + SaveFinish( pSaveData ); + pSaveData = SaveInit( SAVE_HEAPSIZE, SAVE_HASHSTRINGS ); // re-init the buffer - if( SV_IsValidEdict( pent )) + Q_snprintf( hlPath, sizeof( hlPath ), "%s*.HL?", DEFAULT_SAVE_DIRECTORY ); + Q_strncpy( gameHeader.mapName, sv.name, sizeof( gameHeader.mapName )); // get the name of level where a player + Q_strncpy( gameHeader.comment, pSaveComment, sizeof( gameHeader.comment )); + gameHeader.mapCount = DirectoryCount( hlPath ); // counting all the adjacency maps + + // Store the game header + svgame.dllFuncs.pfnSaveWriteFields( pSaveData, "GameHeader", &gameHeader, gGameHeader, ARRAYSIZE( gGameHeader )); + + // Write the game globals + svgame.dllFuncs.pfnSaveGlobalState( pSaveData ); + + // Write entity string token table + pTokenData = StoreHashTable( pSaveData ); + + Q_snprintf( name, sizeof( name ), "%s%s.sav", DEFAULT_SAVE_DIRECTORY, pSaveName ); + COM_FixSlashes( name ); + + // output to disk + if( !Q_stricmp( pSaveName, "quick" ) || !Q_stricmp( pSaveName, "autosave" )) + AgeSaveList( pSaveName, SAVE_AGED_COUNT ); + + // output to disk + if(( pFile = FS_Open( name, "wb", true )) == NULL ) { - if( Q_strcmp( SV_ClassName( pent ), STRING( classname ))) - { - MsgDev( D_ERROR, "Global entity found %s, wrong class %s\n", STRING( globalname ), SV_ClassName( pent )); - pent = NULL; - } + // something bad is happens + SaveFinish( pSaveData ); + return 0; } - return pent; + // pending the preview image for savegame + Cbuf_AddText( va( "saveshot \"%s\"\n", pSaveName )); + Con_Printf( "Saving game to %s...\n", name ); + + version = SAVEGAME_VERSION; + id = SAVEGAME_HEADER; + + FS_Write( pFile, &id, sizeof( id )); + FS_Write( pFile, &version, sizeof( version )); + FS_Write( pFile, &pSaveData->size, sizeof( int )); // does not include token table + + // write out the tokens first so we can load them before we load the entities + FS_Write( pFile, &pSaveData->tokenCount, sizeof( int )); + FS_Write( pFile, &pSaveData->tokenSize, sizeof( int )); + FS_Write( pFile, pTokenData, pSaveData->tokenSize ); + FS_Write( pFile, pSaveData->pBaseData, pSaveData->size ); // header and globals + + DirectoryCopy( hlPath, pFile ); + SaveFinish( pSaveData ); + FS_Close( pFile ); + + return 1; } -//----------------------------------------------------------------------------- -int SV_CreateEntityTransitionList( SAVERESTOREDATA *pSaveData, int levelMask ) +/* +============= +SaveReadHeader + +read header of .sav file +============= +*/ +static int SaveReadHeader( file_t *pFile, GAME_HEADER *pHeader ) { + int tokenCount, tokenSize; + int size, id, version; + SAVERESTOREDATA *pSaveData; + + FS_Read( pFile, &id, sizeof( id )); + if( id != SAVEGAME_HEADER ) + { + FS_Close( pFile ); + return 0; + } + + FS_Read( pFile, &version, sizeof( version )); + if( version != SAVEGAME_VERSION ) + { + FS_Close( pFile ); + return 0; + } + + FS_Read( pFile, &size, sizeof( int )); + FS_Read( pFile, &tokenCount, sizeof( int )); + FS_Read( pFile, &tokenSize, sizeof( int )); + + pSaveData = SaveInit( size + tokenSize, tokenCount ); + pSaveData->tokenCount = tokenCount; + pSaveData->tokenSize = tokenSize; + + // Parse the symbol table + BuildHashTable( pSaveData, pFile ); + + // Set up the restore basis + pSaveData->fUseLandmark = false; + pSaveData->time = 0.0f; + + FS_Read( pFile, pSaveData->pBaseData, size ); + + svgame.dllFuncs.pfnSaveReadFields( pSaveData, "GameHeader", pHeader, gGameHeader, ARRAYSIZE( gGameHeader )); + + svgame.dllFuncs.pfnRestoreGlobalState( pSaveData ); + + SaveFinish( pSaveData ); + + return 1; +} + +/* +============= +CreateEntityTransitionList + +moving edicts to another level +============= +*/ +static int CreateEntityTransitionList( SAVERESTOREDATA *pSaveData, int levelMask ) +{ + int i, movedCount; + ENTITYTABLE *pTable; edict_t *pent; - ENTITYTABLE *pEntInfo; - int i, movedCount, active; movedCount = 0; // create entity list - if( svgame.physFuncs.pfnCreateEntitiesInTransitionList != NULL ) - { - svgame.physFuncs.pfnCreateEntitiesInTransitionList( pSaveData, levelMask ); - } - else - { - for( i = 0; i < pSaveData->tableCount; i++ ) - { - pEntInfo = &pSaveData->pTable[i]; - pent = NULL; + CreateEntitiesInRestoreList( pSaveData, levelMask, false ); - if( pEntInfo->size && pEntInfo->id != 0 ) - { - if( pEntInfo->classname != 0 ) - { - active = (pEntInfo->flags & levelMask) ? 1 : 0; - - // spawn players - if(( pEntInfo->id > 0 ) && ( pEntInfo->id < svs.maxclients + 1 )) - { - edict_t *ed = EDICT_NUM( pEntInfo->id ); - - if( active && ed && !ed->free ) - { - if( !FBitSet( pEntInfo->flags, FENTTABLE_PLAYER )) - { - MsgDev( D_WARN, "ENTITY IS NOT A PLAYER: %d\n", i ); - Assert( 0 ); - } - - pent = SV_CreateNamedEntity( ed, pEntInfo->classname ); - } - } - else if( active ) - { - // create named entity - pent = SV_CreateNamedEntity( NULL, pEntInfo->classname ); - } - } - else - { - MsgDev( D_WARN, "Entity with data saved, but with no classname\n" ); - } - } - - pEntInfo->pent = pent; - } - } - - // re-base the savedata since we re-ordered the entity/table / restore fields - SaveRestore_Rebase( pSaveData ); - // now spawn entities for( i = 0; i < pSaveData->tableCount; i++ ) { - pEntInfo = &pSaveData->pTable[i]; - pent = pEntInfo->pent; + pTable = &pSaveData->pTable[i]; + pSaveData->pCurrentData = pSaveData->pBaseData + pTable->location; + pSaveData->size = pTable->location; pSaveData->currentIndex = i; - SaveRestore_Seek( pSaveData, pEntInfo->location ); - - if( SV_IsValidEdict( pent ) && ( pEntInfo->flags & levelMask )) // screen out the player if he's not to be spawned + pent = pTable->pent; + + if( SV_IsValidEdict( pent ) && FBitSet( pTable->flags, levelMask )) // screen out the player if he's not to be spawned { - if( pEntInfo->flags & FENTTABLE_GLOBAL ) + if( FBitSet( pTable->flags, FENTTABLE_GLOBAL )) { entvars_t tmpVars; edict_t *pNewEnt; - // NOTE: we need to update table pointer so decals on the global entities with brush models can be correctly moved - // found the classname and the globalname for our globalentity - svgame.dllFuncs.pfnSaveReadFields( pSaveData, "ENTVARS", &tmpVars, gEntvarsDescription, ARRAYSIZE( gEntvarsDescription )); + // NOTE: we need to update table pointer so decals on the global entities with brush models can be + // correctly moved. found the classname and the globalname for our globalentity + svgame.dllFuncs.pfnSaveReadFields( pSaveData, "ENTVARS", &tmpVars, gTempEntvars, ARRAYSIZE( gTempEntvars )); // reset the save pointers, so dll can read this too - pSaveData->size = pSaveData->pTable[pSaveData->currentIndex].location; - pSaveData->pCurrentData = pSaveData->pBaseData + pSaveData->size; + pSaveData->pCurrentData = pSaveData->pBaseData + pTable->location; + pSaveData->size = pTable->location; // IMPORTANT: we should find the already spawned or local restored global entity pNewEnt = SV_FindGlobalEntity( tmpVars.classname, tmpVars.globalname ); - Con_DPrintf( "Merging changes for global: %s\n", STRING( pEntInfo->classname )); + Con_DPrintf( "Merging changes for global: %s\n", STRING( pTable->classname )); // ------------------------------------------------------------------------- // Pass the "global" flag to the DLL to indicate this entity should only override // a matching entity, not be spawned - if( svgame.dllFuncs.pfnRestore( pent, pSaveData, true ) > 0 ) + if( svgame.dllFuncs.pfnRestore( pent, pSaveData, 1 ) > 0 ) { movedCount++; } else { - if( SV_IsValidEdict( pNewEnt )) // update the table so decals can find entity - pSaveData->pTable[pSaveData->currentIndex].pent = pNewEnt; - pent->v.flags |= FL_KILLME; + if( SV_IsValidEdict( pNewEnt )) // update the table so decals can find parent entity + pTable->pent = pNewEnt; + SetBits( pent->v.flags, FL_KILLME ); } } else { - Con_DPrintf( "Transferring %s (%d)\n", STRING( pEntInfo->classname ), NUM_FOR_EDICT( pent )); + Con_DPrintf( "Transferring %s (%d)\n", STRING( pTable->classname ), NUM_FOR_EDICT( pent )); - if( svgame.dllFuncs.pfnRestore( pent, pSaveData, false ) < 0 ) + if( svgame.dllFuncs.pfnRestore( pent, pSaveData, 0 ) < 0 ) { - pent->v.flags |= FL_KILLME; + SetBits( pent->v.flags, FL_KILLME ); } else { - if( !( pEntInfo->flags & FENTTABLE_PLAYER ) && EntityInSolid( pent )) + if( !FBitSet( pTable->flags, FENTTABLE_PLAYER ) && EntityInSolid( pent )) { // this can happen during normal processing - PVS is just a guess, // some map areas won't exist in the new map - Con_DPrintf( "Suppressing %s\n", STRING( pEntInfo->classname )); - pent->v.flags |= FL_KILLME; + Con_DPrintf( "Suppressing %s\n", STRING( pTable->classname )); + SetBits( pent->v.flags, FL_KILLME ); } else { - pEntInfo->flags = FENTTABLE_REMOVED; + pTable->flags = FENTTABLE_REMOVED; movedCount++; } } @@ -1831,7 +1805,14 @@ int SV_CreateEntityTransitionList( SAVERESTOREDATA *pSaveData, int levelMask ) return movedCount; } -void SV_LoadAdjacentEnts( const char *pOldLevel, const char *pLandmarkName ) +/* +============= +LoadAdjacentEnts + +loading edicts from adjacency levels +============= +*/ +static void LoadAdjacentEnts( const char *pOldLevel, const char *pLandmarkName ) { SAVE_HEADER header; SAVERESTOREDATA currentLevelData, *pSaveData; @@ -1841,6 +1822,7 @@ void SV_LoadAdjacentEnts( const char *pOldLevel, const char *pLandmarkName ) memset( ¤tLevelData, 0, sizeof( SAVERESTOREDATA )); svgame.globals->pSaveData = ¤tLevelData; + sv.loadgame = sv.paused = true; // build the adjacent map list svgame.dllFuncs.pfnParmsChangeLevel(); @@ -1850,9 +1832,7 @@ void SV_LoadAdjacentEnts( const char *pOldLevel, const char *pLandmarkName ) // make sure the previous level is in the connection list so we can // bring over the player. if( !Q_stricmp( currentLevelData.levelList[i].mapName, pOldLevel )) - { foundprevious = true; - } for( test = 0; test < i; test++ ) { @@ -1864,24 +1844,23 @@ void SV_LoadAdjacentEnts( const char *pOldLevel, const char *pLandmarkName ) // map was already in the list if( test < i ) continue; - pSaveData = SV_LoadSaveData( currentLevelData.levelList[i].mapName ); + pSaveData = LoadSaveData( currentLevelData.levelList[i].mapName ); if( pSaveData ) { - SV_ParseSaveTables( pSaveData, &header, false ); - SV_EntityPatchRead( pSaveData, currentLevelData.levelList[i].mapName ); + ParseSaveTables( pSaveData, &header, false ); + EntityPatchRead( pSaveData, currentLevelData.levelList[i].mapName ); pSaveData->time = sv.time; // - header.time; pSaveData->fUseLandmark = true; + flags = movedCount = 0; + index = -1; // calculate landmark offset LandmarkOrigin( ¤tLevelData, landmarkOrigin, pLandmarkName ); LandmarkOrigin( pSaveData, pSaveData->vecLandmarkOffset, pLandmarkName ); VectorSubtract( landmarkOrigin, pSaveData->vecLandmarkOffset, pSaveData->vecLandmarkOffset ); - flags = 0; - index = -1; - if( !Q_stricmp( currentLevelData.levelList[i].mapName, pOldLevel )) SetBits( flags, FENTTABLE_PLAYER ); @@ -1892,15 +1871,15 @@ void SV_LoadAdjacentEnts( const char *pOldLevel, const char *pLandmarkName ) SetBits( flags, BIT( index )); } - if( flags ) movedCount = SV_CreateEntityTransitionList( pSaveData, flags ); + if( flags ) movedCount = CreateEntityTransitionList( pSaveData, flags ); // if ents were moved, rewrite entity table to save file - if( movedCount ) SV_EntityPatchWrite( pSaveData, currentLevelData.levelList[i].mapName ); + if( movedCount ) EntityPatchWrite( pSaveData, currentLevelData.levelList[i].mapName ); // move the decals from another level - SV_LoadClientState( pSaveData, currentLevelData.levelList[i].mapName, true ); + LoadClientState( pSaveData, currentLevelData.levelList[i].mapName, true, true ); - SV_SaveFinish( pSaveData ); + SaveFinish( pSaveData ); } } @@ -1910,6 +1889,33 @@ void SV_LoadAdjacentEnts( const char *pOldLevel, const char *pLandmarkName ) Host_Error( "Level transition ERROR\nCan't find connection to %s from %s\n", pOldLevel, sv.name ); } +/* +============= +SV_LoadGameState + +loading entities from the savegame +============= +*/ +int SV_LoadGameState( char const *level ) +{ + return LoadGameState( level, false ); +} + +/* +============= +SV_ClearGameState + +clear current game state +============= +*/ +void SV_ClearGameState( void ) +{ + ClearSaveDir(); + + if( svgame.dllFuncs.pfnResetGlobalState != NULL ) + svgame.dllFuncs.pfnResetGlobalState(); +} + /* ============= SV_ChangeLevel @@ -1944,7 +1950,7 @@ void SV_ChangeLevel( qboolean loadfromsavedgame, const char *mapname, const char svgame.globals->changelevel = true; // save the current level's state - pSaveData = SV_SaveGameState( true ); + pSaveData = SaveGameState( true ); } SV_InactivateClients (); @@ -1957,15 +1963,14 @@ void SV_ChangeLevel( qboolean loadfromsavedgame, const char *mapname, const char if( loadfromsavedgame ) { // finish saving gamestate - SV_SaveFinish( pSaveData ); + SaveFinish( pSaveData ); - if( !SV_LoadGameState( level, true )) + if( !LoadGameState( level, true )) SV_SpawnEntities( level ); - SV_LoadAdjacentEnts( oldlevel, startspot ); - sv.loadgame = sv.paused = true; // pause until all clients connect + LoadAdjacentEnts( oldlevel, startspot ); if( sv_newunit.value ) - SV_ClearSaveDir(); + ClearSaveDir(); SV_ActivateServer( false ); } else @@ -1977,146 +1982,11 @@ void SV_ChangeLevel( qboolean loadfromsavedgame, const char *mapname, const char } } -int SV_SaveGameSlot( const char *pSaveName, const char *pSaveComment ) -{ - string hlPath, name; - char *pTokenData; - SAVERESTOREDATA *pSaveData; - GAME_HEADER gameHeader; - int i, tag, tokenSize; - file_t *pFile; - - pSaveData = SV_SaveGameState( false ); - if( !pSaveData ) return 0; - - SV_SaveFinish( pSaveData ); - - pSaveData = SV_SaveInit( 0 ); - - Q_strncpy( hlPath, "save/*.HL?", sizeof( hlPath )); - gameHeader.mapCount = SV_MapCount( hlPath ); - Q_strncpy( gameHeader.mapName, sv.name, sizeof( gameHeader.mapName )); - Q_strncpy( gameHeader.comment, pSaveComment, sizeof( gameHeader.comment )); - - svgame.dllFuncs.pfnSaveWriteFields( pSaveData, "GameHeader", &gameHeader, gGameHeader, ARRAYSIZE( gGameHeader )); - svgame.dllFuncs.pfnSaveGlobalState( pSaveData ); - - // write entity string token table - pTokenData = SaveRestore_AccessCurPos( pSaveData ); - for( i = 0; i < pSaveData->tokenCount; i++ ) - { - const char *pszToken = (SaveRestore_StringFromSymbol( pSaveData, i )); - if( !pszToken ) pszToken = ""; - - if( !SaveRestore_Write( pSaveData, pszToken, Q_strlen( pszToken ) + 1 )) - { - MsgDev( D_ERROR, "Token Table Save/Restore overflow!\n" ); - break; - } - } - - tokenSize = SaveRestore_AccessCurPos( pSaveData ) - pTokenData; - SaveRestore_Rewind( pSaveData, tokenSize ); - - Q_snprintf( name, sizeof( name ), "save/%s.sav", pSaveName ); - Con_Printf( "Saving game to %s...\n", name ); - - Cbuf_AddText( va( "saveshot \"%s\"\n", pSaveName )); - - // output to disk - if( !Q_stricmp( pSaveName, "quick" ) || !Q_stricmp( pSaveName, "autosave" )) - SV_AgeSaveList( pSaveName, SAVE_AGED_COUNT ); - - pFile = FS_Open( name, "wb", true ); - - tag = SAVEGAME_HEADER; - FS_Write( pFile, &tag, sizeof( int )); - tag = SAVEGAME_VERSION; - FS_Write( pFile, &tag, sizeof( int )); - tag = SaveRestore_GetCurPos( pSaveData ); - FS_Write( pFile, &tag, sizeof( int )); // does not include token table - - // write out the tokens first so we can load them before we load the entities - tag = pSaveData->tokenCount; - FS_Write( pFile, &tag, sizeof( int )); - FS_Write( pFile, &tokenSize, sizeof( int )); - FS_Write( pFile, pTokenData, tokenSize ); - - // save gamestate - FS_Write( pFile, SaveRestore_GetBuffer( pSaveData ), SaveRestore_GetCurPos( pSaveData )); - - SV_DirectoryCopy( hlPath, pFile ); - FS_Close( pFile ); - SV_SaveFinish( pSaveData ); - - return 1; -} - -int SV_SaveReadHeader( file_t *pFile, GAME_HEADER *pHeader ) -{ - int i, tag, size, tokenCount, tokenSize; - char *pszTokenList; - SAVERESTOREDATA *pSaveData; - - FS_Read( pFile, &tag, sizeof( int )); - if( tag != SAVEGAME_HEADER ) - { - FS_Close( pFile ); - return 0; - } - - FS_Read( pFile, &tag, sizeof( int )); - if( tag != SAVEGAME_VERSION ) - { - FS_Close( pFile ); - return 0; - } - - FS_Read( pFile, &size, sizeof( int )); - FS_Read( pFile, &tokenCount, sizeof( int )); - FS_Read( pFile, &tokenSize, sizeof( int )); - - pSaveData = Mem_Alloc( host.mempool, sizeof( SAVERESTOREDATA ) + tokenSize + size ); - pSaveData->connectionCount = 0; - pszTokenList = (char *)(pSaveData + 1); - - if( tokenSize > 0 ) - { - FS_Read( pFile, pszTokenList, tokenSize ); - - SaveRestore_InitSymbolTable( pSaveData, (char **)Mem_Alloc( host.mempool, tokenCount * sizeof( char* )), tokenCount ); - - // make sure the token strings pointed to by the pToken hashtable. - for( i = 0; i < tokenCount; i++ ) - { - if( *pszTokenList ) - SaveRestore_DefineSymbol( pSaveData, pszTokenList, i ); - while( *pszTokenList++ ); // find next token (after next null) - } - } - else - { - SaveRestore_InitSymbolTable( pSaveData, NULL, 0 ); - } - - pSaveData->fUseLandmark = false; - pSaveData->time = 0.0f; - - // pszTokenList now points after token data - SaveRestore_Init( pSaveData, (char *)(pszTokenList), size ); - FS_Read( pFile, SaveRestore_GetBuffer( pSaveData ), size ); - - svgame.dllFuncs.pfnResetGlobalState(); - - svgame.dllFuncs.pfnSaveReadFields( pSaveData, "GameHeader", pHeader, gGameHeader, ARRAYSIZE( gGameHeader )); - - svgame.dllFuncs.pfnRestoreGlobalState( pSaveData ); - - SV_SaveFinish( pSaveData ); - - return 1; -} - +/* +============= +SV_LoadGame +============= +*/ qboolean SV_LoadGame( const char *pPath ) { qboolean validload = false; @@ -2144,32 +2014,35 @@ qboolean SV_LoadGame( const char *pPath ) { SV_ClearGameState(); - if( SV_SaveReadHeader( pFile, &gameHeader )) + if( SaveReadHeader( pFile, &gameHeader )) { - SV_DirectoryExtract( pFile, gameHeader.mapCount ); + DirectoryExtract( pFile, gameHeader.mapCount ); validload = true; } FS_Close( pFile ); - // now check for map problems - flags = SV_MapIsValid( gameHeader.mapName, GI->sp_entity, NULL ); + if( validload ) + { + // now check for map problems + flags = SV_MapIsValid( gameHeader.mapName, GI->sp_entity, NULL ); - if( FBitSet( flags, MAP_INVALID_VERSION )) - { - MsgDev( D_ERROR, "map %s is invalid or not supported\n", gameHeader.mapName ); - validload = false; - } + if( FBitSet( flags, MAP_INVALID_VERSION )) + { + Con_Printf( S_ERROR "map %s is invalid or not supported\n", gameHeader.mapName ); + validload = false; + } - if( !FBitSet( flags, MAP_IS_EXIST )) - { - MsgDev( D_ERROR, "map %s doesn't exist\n", gameHeader.mapName ); - validload = false; + if( !FBitSet( flags, MAP_IS_EXIST )) + { + Con_Printf( S_ERROR "map %s doesn't exist\n", gameHeader.mapName ); + validload = false; + } } } if( !validload ) { - MsgDev( D_ERROR, "Couldn't load %s\n", pPath ); + Con_Printf( S_ERROR "Couldn't load %s\n", pPath ); return false; } @@ -2184,52 +2057,30 @@ qboolean SV_LoadGame( const char *pPath ) /* ================== -SV_SaveGetName +SV_SaveGame ================== */ -void SV_SaveGetName( int lastnum, char *filename ) -{ - int a, b, c; - - if( !COM_CheckString( filename )) - return; - - if( lastnum < 0 || lastnum > 999 ) - { - // bound - Q_strcpy( filename, "error" ); - return; - } - - a = lastnum / 100; - lastnum -= a * 100; - b = lastnum / 10; - c = lastnum % 10; - - Q_sprintf( filename, "save%i%i%i", a, b, c ); -} - void SV_SaveGame( const char *pName ) { char comment[80]; + int n, result; string savename; - int n; if( !COM_CheckString( pName )) return; // can we save at this point? - if( !SV_IsValidSave( )) - return; + if( !IsValidSave( )) return; if( !Q_stricmp( pName, "new" )) { // scan for a free filename for( n = 0; n < 1000; n++ ) { - SV_SaveGetName( n, savename ); + if( !SaveGetName( n, savename )) + return; - if( !FS_FileExists( va( "save/%s.sav", savename ), true )) + if( !FS_FileExists( va( "%s%s.sav", DEFAULT_SAVE_DIRECTORY, savename ), true )) break; } @@ -2242,13 +2093,12 @@ void SV_SaveGame( const char *pName ) else Q_strncpy( savename, pName, sizeof( savename )); // unload previous image from memory (it's will be overwritten) - GL_FreeImage( va( "save/%s.bmp", savename )); + GL_FreeImage( va( "%s%s.bmp", DEFAULT_SAVE_DIRECTORY, savename )); - comment[0] = '\0'; - SV_BuildSaveComment( comment, sizeof( comment )); - SV_SaveGameSlot( savename, comment ); + SaveBuildComment( comment, sizeof( comment )); + result = SaveGameSlot( savename, comment ); - if( !FBitSet( host.features, ENGINE_QUAKE_COMPATIBLE )) + if( result && !FBitSet( host.features, ENGINE_QUAKE_COMPATIBLE )) CL_HudMessage( "GAMESAVED" ); // defined in titles.txt } @@ -2261,16 +2111,17 @@ used for reload game after player death */ const char *SV_GetLatestSave( void ) { - search_t *f = FS_Search( "save/*.sav", true, true ); // lookup only in gamedir - char savename[MAX_QPATH]; - long newest = 0, ft; - int i, found = 0; + static char savename[MAX_QPATH]; + long newest = 0, ft; + int i, found = 0; + search_t *t; - if( !f ) return NULL; + if(( t = FS_Search( va( "%s*.sav", DEFAULT_SAVE_DIRECTORY ), true, true )) == NULL ) + return NULL; - for( i = 0; i < f->numfilenames; i++ ) + for( i = 0; i < t->numfilenames; i++ ) { - ft = FS_FileTime( f->filenames[i], true ); + ft = FS_FileTime( t->filenames[i], true ); // found a match? if( ft > 0 ) @@ -2278,29 +2129,35 @@ const char *SV_GetLatestSave( void ) // should we use the matched? if( !found || Host_CompareFileTime( newest, ft ) < 0 ) { + Q_strncpy( savename, t->filenames[i], sizeof( savename )); newest = ft; - Q_strncpy( savename, f->filenames[i], MAX_STRING ); found = 1; } } } - Mem_Free( f ); // release search + Mem_Free( t ); // release search if( found ) - return va( "%s", savename ); // move to static memory + return savename; return NULL; } -qboolean SV_GetComment( const char *savename, char *comment ) +/* +================== +SV_GetSaveComment + +check savegame for valid +================== +*/ +qboolean SV_GetSaveComment( const char *savename, char *comment ) { int i, tag, size, nNumberOfFields, nFieldSize, tokenSize, tokenCount; char *pData, *pSaveData, *pFieldName, **pTokenList; string name, description; file_t *f; - f = FS_Open( savename, "rb", true ); - if( !f ) + if(( f = FS_Open( savename, "rb", true )) == NULL ) { // just not exist - clear comment Q_strncpy( comment, "", MAX_STRING ); @@ -2318,9 +2175,9 @@ qboolean SV_GetComment( const char *savename, char *comment ) FS_Read( f, &tag, sizeof( int )); - if( tag == 0x0071 ) + if( tag == 0x0065 ) { - Q_strncpy( comment, "Gold Source ", MAX_STRING ); + Q_strncpy( comment, "old version Xash3D ", MAX_STRING ); FS_Close( f ); return 0; } @@ -2335,7 +2192,7 @@ qboolean SV_GetComment( const char *savename, char *comment ) if( tag > SAVEGAME_VERSION ) { // old xash version ? - Q_strncpy( comment, "", MAX_STRING ); + Q_strncpy( comment, "", MAX_STRING ); FS_Close( f ); return 0; } @@ -2349,16 +2206,16 @@ qboolean SV_GetComment( const char *savename, char *comment ) size += tokenSize; // sanity check. - if( tokenCount < 0 || tokenCount > ( 1024 * 1024 * 32 )) + if( tokenCount < 0 || tokenCount > SAVE_HASHSTRINGS ) { - Q_strncpy( comment, "", MAX_STRING ); + Q_strncpy( comment, "", MAX_STRING ); FS_Close( f ); return 0; } - if( tokenSize < 0 || tokenSize > ( 1024 * 1024 * 32 )) + if( tokenSize < 0 || tokenSize > SAVE_HEAPSIZE ) { - Q_strncpy( comment, "", MAX_STRING ); + Q_strncpy( comment, "", MAX_STRING ); FS_Close( f ); return 0; } @@ -2384,7 +2241,6 @@ qboolean SV_GetComment( const char *savename, char *comment ) // short, short (size, index of field name) nFieldSize = *(short *)pData; pData += sizeof( short ); - pFieldName = pTokenList[*(short *)pData]; if( Q_stricmp( pFieldName, "GameHeader" )) @@ -2432,7 +2288,8 @@ qboolean SV_GetComment( const char *savename, char *comment ) if( pSaveData ) Mem_Free( pSaveData ); FS_Close( f ); - if( Q_strlen( name ) > 0 && Q_strlen( description ) > 0 ) + // at least mapname should be filled + if( Q_strlen( name ) > 0 ) { time_t fileTime; const struct tm *file_tm; @@ -2459,4 +2316,9 @@ qboolean SV_GetComment( const char *savename, char *comment ) Q_strncpy( comment, "", MAX_STRING ); return 0; +} + +void SV_InitSaveRestore( void ) +{ + pfnSaveGameComment = COM_GetProcAddress( svgame.hInstance, "SV_SaveGameComment" ); } \ No newline at end of file diff --git a/engine/server/sv_world.c b/engine/server/sv_world.c index a65db305..eddc5213 100644 --- a/engine/server/sv_world.c +++ b/engine/server/sv_world.c @@ -564,7 +564,7 @@ void SV_TouchLinks( edict_t *ent, areanode_t *node ) } // never touch the triggers when "playersonly" is active - if( !FBitSet( sv.hostflags, SVF_PLAYERSONLY )) + if( !sv.playersonly ) { svgame.globals->time = sv.time; svgame.dllFuncs.pfnTouch( touch, ent ); diff --git a/pm_shared/pm_movevars.h b/pm_shared/pm_movevars.h index 64d9baeb..ea959038 100644 --- a/pm_shared/pm_movevars.h +++ b/pm_shared/pm_movevars.h @@ -43,10 +43,6 @@ struct movevars_s int features; // engine features that shared across network int fog_settings; // Global fog settings (packed color+density) float wateralpha; // World water alpha 1.0 - solid 0.0 - transparent - float skydir_x; // skybox rotate direction - float skydir_y; // - float skydir_z; // - float skyangle; // skybox rotate angle }; extern movevars_t movevars;