Closed KirillMysnik closed 8 years ago
I can't reproduce the crash - work fine for me. Try disabling everything except SP and the snippet you posted above.
I did a clean install of SRCDS and installed latest SP over it. It crashes.
Does the following also crashes?
from entities.datamaps import InputData
from filters.entities import EntityIter
from memory import Convention
from memory import DataType
def load():
for entity in EntityIter('trigger_once'):
desc = entity.datamap.find('Enable')
func = desc.function.make_function(Convention.THISCALL,
(DataType.POINTER, DataType.POINTER), DataType.VOID)
func(entity, InputData())
Yes. I will try to find some logs.
crash_20160620034624_1.dmp[7582]: Uploading dump (out-of-process)
/tmp/dumps/crash_20160620034624_1.dmp
Segmentation fault (core dumped)
Add "-debug" to the ./srcds_run command line to generate a debug.log to help with solving this problem
Already added -debug to the cmd line, but I can't find said debug.log
anywhere
Edit: server version 3398447 - just downloaded SP build 374 cs_office Debian 8.5 & Ubuntu 15.10 Ubuntu GLIBC 2.21-0ubuntu4.1
What if you create the entity on your own instead of using existing ones? Also, what if you use the "Toggle" input instead of Enable/Disable?
from entities.entity import Entity
def load():
entity = Entity.create('trigger_once')
entity.spawn()
entity.toggle()
Crashes. So, yes, Toggle
crashes too. And even brushless trigger_once
crashes.
Edit: Not all entities are affected, just to make sure:
from filters.entities import EntityIter
def load():
for entity in EntityIter('func_button'):
entity.lock()
doesn't crash.
EDIT: I've added "Linux" tag not because I haven't tested it on Windows but because on WIndows it works just fine. Also, I have a feeling like this code wasn't crashing just yesterday (and I haven't updated SP since yesterday for sure). But I tested it on 2 different machines, and on a cleanly installed SRCDS too. I'm sure there weren't any updates since yesterday, were they?
What if you disable SP, and use "ent_fire trigger_once Enable" in your client console? (while sv_cheats is enabled and you are authentified by rcon)
Tried that with SP enabled, did not crash
What if you set activator/caller to a valid player?
Quick update: the following code does not crash
from entities.datamaps import InputData
from filters.entities import EntityIter
from memory import Convention, DataType, find_binary
INPUT_DISABLE_IDENTIFIER = "_ZN20CBaseVPhysicsTrigger12InputDisableER11inputdata_t"
server = find_binary('server')
input_disable = server[INPUT_DISABLE_IDENTIFIER].make_function(
Convention.THISCALL,
(
DataType.POINTER,
DataType.POINTER,
),
DataType.VOID,
)
def load():
for entity in EntityIter('trigger_once'):
input_disable(entity.pointer, InputData())
(calling InputDisable directly).
I've set up a clean Lubuntu virtual machine, clean SRCDS install with the latest (374) SP. Still crashes.
Now I will try setting activator/caller.
Yes,
from filters.entities import EntityIter
def load():
for entity in EntityIter('trigger_once'):
entity.enable(activator=entity.index, caller=entity.index)
still crashes. Sorry for not setting this to a valid player, but it's still a valid entity.
L'In20Cible, what setup are you running on? It seems to me that it crashes only for me, because decompile wasn't able to crash it, too.
CBaseVPhysicsTrigger, that class is not even into the CTriggerOnce hierarchy. It is inherited by CTriggerVPhysicsMotion, CTriggerWind, etc. but not CTriggerOnce:
CTriggerOnce → CTriggerMultiple → CBaseTrigger → CBaseToggle → CBaseEntity → ...
What is the outputs you get with the following:
from core import PLATFORM
from entities.entity import BaseEntity
from entities.datamaps import InputData
from memory import get_object_pointer
from memory import Convention
from memory import DataType
from memory.hooks import PreHook
entity = BaseEntity.find_or_create('trigger_once')
print('trigger_once found or created with index:', entity.index)
func = entity.datamap.find('Enable').function.make_function(
Convention.THISCALL,
(DataType.POINTER, DataType.POINTER), DataType.VOID)
print('InputEnable found into descriptor at address:', func.address)
ptr = get_object_pointer(entity)
vfunc = ptr.get_virtual_func(190 if PLATFORM == 'linux' else 189).make_function(
Convention.THISCALL,
(DataType.POINTER, DataType.POINTER), DataType.VOID)
print('InputEnable found into vtable at address:', vfunc.address)
@PreHook(func)
def _pre_func(stack):
print('InputEnable found into descriptor called with args:', stack[0].address, stack[1].address)
@PreHook(vfunc)
def _pre_vfunc(stack):
print('InputEnable found into vtable called with args:', stack[0].address, stack[1].address)
data = InputData()
# This should call the function found into the datamap which should jump to the vtable one, so both hooks should be called here.
func(entity, data)
# Only the vtable hook should be called here.
vfunc(entity, data)
entity.destroy()
trigger_once found or created with index: 468
InputEnable found into descriptor at address: 761
InputEnable found into vtable at address: 4062562240
Uploading dump (in-process) [proxy '']
/tmp/dumps/assert_20160620065421_1.dmp
success = no
error: libcurl.so: cannot open shared object file: No such file or directory
Segmentation fault (core dumped)
Add "-debug" to the ./srcds_run command line to generate a debug.log to help with solving this problem
761 is not a valid address - that's why it crashes. What if you replace the following line:
func = entity.datamap.find('Enable').function.make_function(
Convention.THISCALL,
(DataType.POINTER, DataType.POINTER), DataType.VOID)
WIth:
func = get_object_pointer(entity.datamap.find('Enable')).get_pointer(28).make_function(
Convention.THISCALL,
(DataType.POINTER, DataType.POINTER), DataType.VOID)
trigger_once found or created with index: 468
InputEnable found into descriptor at address: 761
InputEnable found into vtable at address: 4062562240
Uploading dump (in-process) [proxy '']
/tmp/dumps/assert_20160620085320_1.dmp
success = no
Then that means the descriptor itself is storing an invalid function address (or we are misinterpreting it?). Not sure why, this will take some time to figure out.
Out of curiousity, if you run the following:
from entities.entity import BaseEntity
from entities.datamaps import InputData
from entities.datamaps import TypeDescriptionFlags
entity = BaseEntity.find_or_create('trigger_once')
datamap = entity.datamap
while datamap:
for desc in datamap:
if not desc.flags & TypeDescriptionFlags.INPUT:
continue
print(datamap.class_name, desc.name, desc.function.address)
datamap = datamap.base
entity.destroy()
Does only the CBaseTrigger's inputs looks invalid?
EDIT: My guess here is that only the inputs that are virtual are fucked up.
I will run it when I get home (2-3 hours). I'm still wondering why this bug only happens for me.
CBaseTrigger InputEnable 761
CBaseTrigger InputDisable 765
CBaseTrigger InputDisableAndEndTouch 769
CBaseTrigger InputToggle 773
CBaseTrigger InputTouchTest 777
CBaseTrigger InputStartTouch 781
CBaseTrigger InputEndTouch 785
CBaseEntity m_iInitialTeamNum 0
CBaseEntity InputSetTeam 4059752512
CBaseEntity InputKill 4059753552
CBaseEntity InputKillHierarchy 4059753264
CBaseEntity InputUse 4059752384
CBaseEntity InputAlpha 4059769376
CBaseEntity InputAlternativeSorting 4059766464
CBaseEntity InputColor 4059768256
CBaseEntity InputSetParent 4059783376
CBaseEntity InputSetParentAttachment 4059802192
CBaseEntity InputSetParentAttachmentMaintainOffset 4059802112
CBaseEntity InputClearParent 4059752448
CBaseEntity InputSetDamageFilter 4059753040
CBaseEntity InputEnableDamageForces 4059780992
CBaseEntity InputDisableDamageForces 4059780768
CBaseEntity InputDispatchEffect 4059759616
CBaseEntity InputDispatchResponse 4059759536
CBaseEntity InputAddContext 4059832000
CBaseEntity InputRemoveContext 4059813424
CBaseEntity InputClearContext 4059752592
CBaseEntity InputDisableShadow 4059753984
CBaseEntity InputEnableShadow 4059781024
CBaseEntity InputAddOutput 4059759344
CBaseEntity InputFireUser1 4059754784
CBaseEntity InputFireUser2 4059754736
CBaseEntity InputFireUser3 4059754688
CBaseEntity InputFireUser4 4059754640
I guess I was right. Did you notice how they are sorted the same as they are ordered in the class structure and how there is 4 bytes between each others? Definitely something going on with how the addresses are set (or extracted?).
NOTE: The _miInitialTeamNum being 0 is not a bug. Simply forgot to exclude KEY descriptors in the code I posted above.
If you run the following:
from core import PLATFORM
from entities.entity import BaseEntity
from entities.datamaps import InputData
from memory import get_object_pointer
from memory import Convention
from memory import DataType
from memory.hooks import PreHook
entity = BaseEntity.find_or_create('trigger_once')
print('trigger_once found or created with index:', entity.index)
ptr = get_object_pointer(entity)
vfunc = ptr.get_virtual_func(190 if PLATFORM == 'linux' else 189).make_function(
Convention.THISCALL,
(DataType.POINTER, DataType.POINTER), DataType.VOID)
print('InputEnable found into vtable at address:', vfunc.address)
@PreHook(vfunc)
def _pre_vfunc(stack):
print('InputEnable found into vtable called with args:', stack[0].address, stack[1].address)
And use ent_fire trigger_once Enable (again, with sv_cheats on and rcon auth/autokick disabled), does the hooked function get called?
We can easily find out whether an input function is virtual or not:
print('Virtual input functions:')
entity = BaseEntity.create('trigger_once')
datamap = entity.datamap
while datamap:
for desc in datamap:
if not desc.flags & TypeDescriptionFlags.INPUT:
continue
addr = desc.function.address
if not addr & 1:
continue
vtable_index = int((addr - 1) / 4)
print(datamap.class_name, desc.name, vtable_index)
datamap = datamap.base
entity.destroy()
Output:
Virtual input functions:
CBaseTrigger InputEnable 190
CBaseTrigger InputDisable 191
CBaseTrigger InputDisableAndEndTouch 192
CBaseTrigger InputToggle 193
CBaseTrigger InputTouchTest 194
CBaseTrigger InputStartTouch 195
CBaseTrigger InputEndTouch 196
And since they are virtual that attribute can't really store a function address.
And use ent_fire trigger_once Enable (again, with sv_cheats on and rcon auth/autokick disabled), does the hooked function get called?
I tried that through server console (with sv_cheats 1) - no output. It didn't get called.
@Ayuto Awesome! Didn't realize those could have been the offsets directly. Guess windows is optimizing by setting the addresses. That's quite easy to implement.
@KirillMysnik Well, first of all that command won't work from the server console as it requires a player as caller/activator. Like mentionned, you need to set sv_cheats 1 on the server, and execute that command on your client-console while you are authentified by rcon (or you have the autokick flag removed with mp_disable_autokick, for example).
autokick is disabled for iPlayer
InputEnable found into vtable called with args: 191310432 4288164556
InputEnable found into vtable called with args: 191312224 4288164556
Guess there were two of trigger_once
's
https://github.com/Source-Python-Dev-Team/Source.Python/commit/bc0573907d9f0ddcf68a5fb17a93cc4fb5bd8f6d should fix this issue. Build 416 should be available in few minutes. I will close this issue once you confirmed it works for you too, @KirillMysnik.
I think you now broke input functions on Windows. That little snippet is only for Linux.
We should also apply the fix to the input_function
attribute.
I can confirm that the snippet from the first post does not crash anymore.
Windows SRCDS now produces the following exception
[SP] Caught an Exception:
Traceback (most recent call last):
File '..\addons\source-python\packages\source-python\plugins\manager.py', line 81, in __missing__
instance.globals['load']()
File '..\addons\source-python\plugins\input_test\input_test.py', line 5, in load
entity.enable() # entity.disable crashes too
File '..\addons\source-python\packages\source-python\entities\entity.py', line 99, in __getattr__
return getattr(make_object(server_class, self.pointer), attr)
File '..\addons\source-python\packages\source-python\entities\classes.py', line 528, in fget
func = desc.function
AttributeError: 'TypeDescription' object has no attribute 'function'
The function
attribute has been added lately. Updating your installation should fix the issue.
I think you now broke input functions on Windows. That little snippet is only for Linux.
I originally thought about adding a check against PLATFORM but thought it was useless. Virtual inputs are valid addresses (thunk?) on windows and you are going to crash whenever it passes the & 1 condition as they are not valid in such cases.
We should also apply the fix to the input_function attribute.
Good point, didn't think about that one.
Went ahead and still added the check against PLATFORM as you are right, better be safe than sorry. Also added the operator change @Mahi proposed.
Will add a fix for input_function later today. Though, if you got the time to, go ahead.
Thanks @KirillMysnik for confirming it worked for you.
Just tested input_function
and it's working fine, so there is no need to apply that fix.
However, it's another reason to work on this TODO: https://github.com/Source-Python-Dev-Team/Source.Python/blob/1fdd379f1d0f956f84f547c38584ce3226830c84/addons/source-python/packages/source-python/entities/classes.py#L534-L536
Doing so, we would lose the ability to register hooks on InputFunction instances, which is a regression.
Map to reproduce: cs_office (contains
trigger_once
that opens garage door on CT spawn) Code to reproduce:Tested on different linux servers. Also affects trigger_multipe and trigger_teleport - that's what I tested on.