ValveSoftware / halflife

Half-Life 1 engine based games
Other
3.68k stars 622 forks source link

[Blue-Shift] Crash on save at ba_outro. #1014

Open x414e opened 11 years ago

x414e commented 11 years ago

After you pass through the portal at the very end of the game, if you attempt to save (quicksave or manual) the game at certain areas such as xen or during a black/green screen or credits it will just crash and drop to desktop.

I am using Linux Mint 14.

SamVanheer commented 5 years ago

Confirmed to still happen in the current public version.

Backtrace:

Thread 1 "hl_linux" received signal SIGSEGV, Segmentation fault.
0xaefb58d0 in EHANDLE::Get (this=0x9709f0c) at ../blueshift/dlls/cbase.cpp:446
446 ../blueshift/dlls/cbase.cpp: No such file or directory.
(gdb) bt
#0  0xaefb58d0 in EHANDLE::Get (this=0x9709f0c)
    at ../blueshift/dlls/cbase.cpp:446
#1  EHANDLE::operator CBaseEntity* (this=0x9709f0c)
    at ../blueshift/dlls/cbase.cpp:465
#2  0xaf059038 in CSave::WriteFields (this=0xbfffdecc, 
    pname=0xaf08e577 "CTriggerPlayerFreeze", pBaseData=0x9709ea0, 
    pFields=0xaf0c3e40 <CTriggerPlayerFreeze::m_SaveData>, fieldCount=1)
    at ../blueshift/dlls/util.cpp:2106
#3  0xaf0493ac in CTriggerPlayerFreeze::Save (save=..., this=0x9709ea0)
    at ../blueshift/dlls/triggers.cpp:2460
#4  CTriggerPlayerFreeze::Save (this=0x9709ea0, save=...)
    at ../blueshift/dlls/triggers.cpp:2456
#5  0xaefb483c in DispatchSave (pent=0xabeae3e4, pSaveData=0xa1314f8)
    at ../blueshift/dlls/cbase.cpp:278
#6  0xb6782dff in SaveGamestate () at ../engine/host_cmd.c:2539
#7  0xb67843bd in SaveGameSlot (pSaveName=0xabb83dd0 "quick", 
    pSaveComment=0xbfffe620 "#BA_OUTRO", ' ' <repeats 56 times>, "00:20")
    at ../engine/host_cmd.c:2068
#8  0xb6784dbd in Host_Savegame_f () at ../engine/host_cmd.c:2194
#9  Host_Savegame_f () at ../engine/host_cmd.c:2164
#10 0xb6765c0e in Cmd_ExecuteStringWithPrivilegeCheck (
    text=0xbfffe730 "save quick", bIsPrivileged=<optimized out>, 
    src=<optimized out>) at ../engine/cmd.c:1196
---Type <return> to continue, or q <return> to quit---
#11 0xb6765e4d in Cmd_ExecuteStringWithPrivilegeCheck (bIsPrivileged=true, 
    src=src_command, text=0xbfffe730 "save quick") at ../engine/cmd.c:1155
#12 Cbuf_ExecuteFromBuffer (buf=0xb6fccaf0 <cmd_text>, bIsPrivileged=true)
    at ../engine/cmd.c:244
#13 0xb6765e72 in Cbuf_Execute () at ../engine/cmd.c:262
#14 0xb677d093 in _Host_Frame (time=0.0743620619) at ../engine/host.c:1384
#15 0xb677d542 in Host_Frame (time=0.0743620619, iState=1, 
    stateInfo=0xbfffec5c) at ../engine/host.c:1522
#16 0xb67a9e64 in CEngine::Frame (this=0xb69c2a20 <g_Engine>)
    at ../engine/sys_engine.cpp:245
#17 0xb67a78f3 in RunListenServer (instance=0x0, 
    basedir=0x804b220 <szBaseDir> "/home/sam/.local/share/Steam/steamapps/common/Half-Life", 
    cmdline=0x80533c0 "/home/sam/.local/share/Steam/steamapps/common/Half-Life/hl_linux -game bshift +sv_cheats 1", 
    postRestartCmdLineArgs=0x804d360 <main::szNewCommandParams> "", 
    launcherFactory=0x8049350 <CreateInterfaceLocal(char const*, int*)>, 
    filesystemFactory=0xb79e6d40 <CreateInterface(char const*, int*)>)
    at ../engine/sys_dll2.cpp:946
#18 0x08048d67 in main (argc=5, argv=0xbfffee94)
    at ../launcher/launcher.cpp:439

The trigger_playerfreeze entity class looks like this:

CTriggerPlayerFreeze : CBaseDelay
{
  bool m_bUnFrozen;
};

Reverse engineering the entity reveals the problem:

TYPEDESCRIPTION CTriggerPlayerFreeze::m_SaveData[1]
_ZN20CTriggerPlayerFreeze10m_SaveDataE TYPEDESCRIPTION <FIELD_EHANDLE, offset aM_bunfrozen, 6Ch, 1, 0>

The m_bUnFrozen variable is save/restored as an EHANDLE, so it tries to save off the entity id and accesses invalid memory.

Note that using FIELD_BOOLEAN is not enough to fix this. That's intended for the 4 byte BOOL type, change the type of the variable to BOOL so it will work properly.

Opposing Force has the same entity but does not save/restore this field. It happens to work properly because the player's frozen state is separately saved and restored, and m_bUnFrozen is set to false while the player is frozen. When the game restores the entity the memory for it is zero initialized so it will still be valid despite not restoring correctly.

I suspect this is why the variable tracks whether the player is unfrozen, rather than frozen. They may have encountered the bug during development and patched it by inverting the meaning of the boolean.

Note that any changes to save data will break old saved games.