Удаление func_tank_laser. FL_KILLME и DispatchThink
Поймал краш в движке на моменте, уничтожения func_tank_laser. Сетап таков: func_breakable после разрушения вызывает trigger_relay, у которого в KillTarget стоит имя func_tank_laser и в Target - имя другого trigger_relay, у которого в KillTarget уже имя энтити env_laser. Задержка везде нулевая, так что по идее всё должно быть вызвано в пределах одного кадра сервера.
Вот такой бэктрейс в gdb:
code:
#0 SetMinMaxSize (e=0x0, min=0xb29ed94, max=0xb29eda0, rotate=false) at ../engine/pr_cmds.c:151
#1 0xf6374f05 in PF_setsize_I (e=0x0, rgflMin=0xb29ed94, rgflMax=0xb29eda0) at ../engine/pr_cmds.c:171
#2 0xbae7a3c1 in UTIL_SetSize (pev=0xb29ecb8, vecMin=..., vecMax=...) at /home/roman/git_projects/hlsdk-xash3d/dlls/util.cpp:1042
#3 0xbad793af in CBeam::RelinkBeam (this=0xb29ead0) at /home/roman/git_projects/hlsdk-xash3d/dlls/effects.cpp:303
#4 0xbad7a5bc in CBeam::BeamDamage (this=0xb29ead0, ptr=0xffffc588, pevAttacker=0xeced6168) at /home/roman/git_projects/hlsdk-xash3d/dlls/effects.cpp:730
#5 0xbad7bb6c in CLaser::FireAtPoint (this=0xb29ead0, tr=..., pevAttacker=0xeced6168) at /home/roman/git_projects/hlsdk-xash3d/dlls/effects.cpp:1065
#6 0xbad994c8 in CFuncTankLaser::Fire (this=0xb505e70, barrelEnd=..., forward=..., pevAttacker=0xeced6168) at /home/roman/git_projects/hlsdk-xash3d/dlls/func_tank.cpp:1024
#7 0xbad985f0 in CFuncTank::TrackTarget (this=0xb505e70) at /home/roman/git_projects/hlsdk-xash3d/dlls/func_tank.cpp:746
#8 0xbad97c40 in CFuncTank::Think (this=0xb505e70) at /home/roman/git_projects/hlsdk-xash3d/dlls/func_tank.cpp:591
#9 0xbad9932a in CFuncTankLaser::Think (this=0xb505e70) at /home/roman/git_projects/hlsdk-xash3d/dlls/func_tank.cpp:995
#10 0xbad5cff7 in DispatchThink (pent=0xeced60e8) at /home/roman/git_projects/hlsdk-xash3d/dlls/cbase.cpp:237
#11 0xf63aa858 in SV_Physics () at ../engine/sv_phys.c:2038
#12 0xf63a2236 in SV_Frame () at ../engine/sv_main.c:9355
#13 0xf63617c6 in _Host_Frame (time=0.0166792665) at ../engine/host.c:1430
#14 0xf6361c52 in Host_Frame (time=0.0166792665, iState=1, stateInfo=0xffffc89c) at ../engine/host.c:1548
#15 0xf638eb04 in CEngine::Frame (this=0xf65a8aa0 <g_Engine> ) at ../engine/sys_engine.cpp:245
#16 0xf638c58b in RunListenServer (instance=0x0, basedir=0x804b220 <szBaseDir> "/home/roman/.local/share/Steam/steamapps/common/Half-Life",
cmdline=0x8053680 "/home/roman/.local/share/Steam/steamapps/common/Half-Life/hl_linux -game field_intensity +developer 4 +sv_cheats 1",
postRestartCmdLineArgs=0x804d360 <main::szNewCommandParams> "", launcherFactory=0x8049350 <CreateInterfaceLocal(char const*, int*)>, filesystemFactory=
0xf75c9d40 <CreateInterface(char const*, int*)> ) at ../engine/sys_dll2.cpp:955
#17 0x08048d67 in main (argc=7, argv=0xffffcad4) at ../launcher/launcher.cpp:439
Поисследовал фреймы бэктрейса и обнаружил, что у CFuncTankLaser на момент краша выставлен FL_KILLME (значение 1073741824), а вот у CLaser pev->pContainingEntity уже нулевой.
Т.е. DispatchThink вызвался на помеченном для удаления func_tank_laser и уже после очищения env_laser? (напомню, удаляются они одновременно).
Я конечно могу вставить дополнительную проверочку и принудительное выставление m_pLaser у танка в null, но хотелось бы разобраться, как описанная ситуация вообще возможна, ибо вполне вероятно, что в коде есть и другие места, которые могут быть подвержены проблеме.
FreeSlave а попробуй вместо CLaser *m_pLaser у func_tank_laser использовать EHANDLE m_pLaser. Так должно быть безопасно. Даже коммент в коде есть:
C++ Source Code:
//
// EHANDLE. Safe way to point to CBaseEntities who may die between frames
//
Добавлено 23-12-2021 в 02:25:
Цитата:
FreeSlave писал: в коде есть и другие места, которые могут быть подвержены проблеме
Любые места, где есть указатели на энтити, которые могут быть удалены. Например на РПГ в коде его ракеты. При дисконнекте игрока пропадёт его РПГ. Ракета обратиться к указателю на РПГ, а попадёт или на другую энтитю, или вообще выцепит мусор из памяти. Итог - крэш.
EHANDLE это один из вариантов избавления от симптома.
Но главный вопрос тут - почему у энтити, помеченной для удаления, зовётся Think? Это в порядке вещей?
FreeSlave писал: Но главный вопрос тут - почему у энтити, помеченной для удаления, зовётся Think? Это в порядке вещей?
Загляни в SV_Physics_Entity и все вопросы отпадут.
Добавлено 22-12-2021 в 23:27:
Это ради совместимости с голдсорсом сделано, это вообще неправильно.
Энтити, помеченные этим флагом надо удалять вообще все, по завершении кадра, а не посреди него.
Дядя Миша, посмотрел. Я так понимаю, для MOVETYPE_PUSH энтить нет проверки на FL_KILLME до вызова pfnThink. И если env_laser в списке энтить окажется раньше func_tank_laser, то в тчинке танка будет уже невалидный указатель на лазер.