Source-Python-Dev-Team / Source.Python

This plugin aims to use boost::python and create an easily accessible wrapper around the Source Engine API for scripter use.
http://forums.sourcepython.com
GNU General Public License v3.0
163 stars 31 forks source link

Added a set_attacker function to CBaseCSGrenadeProjectile for CS:GO. #366

Closed CookStar closed 3 years ago

CookStar commented 3 years ago

This function allows you to properly set the attacker on the Projectile.

If you think there is a problem with the name, please suggest a new name.

jordanbriere commented 3 years ago

Hmm. Is there a getter or a way to read it also? As for the name, perhaps we could override "thrower" in the CS:GO weapon class so that the behaviours are consistent with other games as well as being backward compatible.

CookStar commented 3 years ago

Hmm. Is there a getter or a way to read it also?

I'm not sure what exactly you want to read, but the thrower will be set by this function. If you want to read CSGOAttackerInfo, you have to use CustomType.

# Source.Python Imports
#   Core
import core
#   Entities
from entities.entity import Entity
#   Memory
from memory import make_object
from memory.manager import CustomType
from memory.manager import Type
from memory.manager import manager
#   Players
from players.entity import Player

class CSGOAttackerInfo(CustomType, metaclass=manager):

    need_init = manager.instance_attribute(Type.BOOL, 0, "bool m_bNeedInit")
    handle = manager.instance_attribute(Type.INT, 4, "EHANDLE m_hHndl")
    is_player = manager.instance_attribute(Type.BOOL, 8, "bool m_bIsPlayer")
    is_world = manager.instance_attribute(Type.BOOL, 9, "bool m_bIsWorld")
    client_index = manager.instance_attribute(Type.INT, 12, "int m_iClientIndex")
    survival_team = manager.instance_attribute(Type.INT, 16, "int m_nSurvivalTeam")
    team_checked = manager.instance_attribute(Type.INT, 20, "int m_iTeamChecked")
    team_num = manager.instance_attribute(Type.INT, 24, "int m_iTeamNum")
    userid = manager.instance_attribute(Type.INT, 28, "int m_iUserId")

if core.PLATFORM == "linux":
    offset = 1300
else:
    offset = 1276

attacker = Player(1)
molotov = Entity.create("molotov_projectile")
molotov.set_attacker(attacker)

attacker_info = make_object(CSGOAttackerInfo, molotov._ptr()+offset)
print(attacker_info.handle)
print(attacker_info.is_player)
print(attacker_info.client_index)

As for the name, perhaps we could override "thrower" in the CS:GO weapon class so that the behaviours are consistent with other games as well as being backward compatible.

Which class does this mean, exactly?

jordanbriere commented 3 years ago

I'm not sure what exactly you want to read, but the thrower will be set by this function. If you want to read CSGOAttackerInfo, you have to use CustomType.

We could simply export that structure from the C++ side as well and just store an offset in our data. But since the thrower is set during that call, I'm fine with the signature. Time will tell if it becomes less stable than an offset I guess.

Which class does this mean, exactly?

On other games, or before that update for CS:GO, giving ownership of a projectile to a player was as simple as setting the thrower like so:

from players.entity import Player
from weapons.entity import Weapon

player = Player(1)
weapon = Weapon.create('hegrenade_projectile')
weapon.thrower = player.inthandle
weapon.damage = 10000
weapon.teleport(player.view_coordinates)
weapon.detonate()

Now such code on CS:GO is no longer working as it used to, and solely adding a set_attacker means such existing code would need to be updated to add a call to that method. If we override thrower in the CS:GO's Weapon class to add such call, we ensure backward compatibility. It could be done like @vinci6k did for godmode into #337.

CookStar commented 3 years ago

We could simply export that structure from the C++ side as well and just store an offset in our data. But since the thrower is set during that call, I'm fine with the signature. Time will tell if it becomes less stable than an offset I guess.

I think that's an option. But if we expand the support to other entities, such as inferno(2936 on Linux and 2912 on Windows), we might have to re-examine the entire entity.

If we override thrower in the CS:GO's Weapon class to add such call, we ensure backward compatibility. It could be done like @vinci6k did for godmode into #337.

I may have agreed head-on if it was an Entity class. The problem is the some existing plugins and users (including myself) assume it will work with Entity class.

jordanbriere commented 3 years ago

I think that's an option. But if we expand the support to other entities, such as inferno(2936 on Linux and 2912 on Windows), we might have to re-examine the entire entity.

If the offset is different for some projectiles then a signature is better. Otherwise it multiplies the effort of maintaining them since if one is changed then the others are likely to as well.

I may have agreed head-on if it was an Entity class. The problem is the some existing plugins and users (including myself) assume it will work with Entity class.

Weapon would have been the ideal choice, since projectiles are internally handled as such everywhere but I get your point and I agree. Since thrower is available from Entity, this is where it should be overridden as well. The following (untested) into the CSGO's Entity class:

thrower = property(
    lambda self: self.__getattr__('thrower'),
    lambda self, value: self.set_attacker(pointer_from_inthandle(value))
)

Should theoretically do it. Would raise an attribute error for non-projectile entities, and would properly override it for projectiles. I used lambdas since we override a dynamic property but we could also use private getter/setter methods as well.

Also, is there a string in that method? If so, please add it as a reference comment in the data file so it makes it easier if someone else try to track it down.

CookStar commented 3 years ago

If the offset is different for some projectiles then a signature is better

The offset of the projectiles are equal. However, CTakeDamageInfo's pAttacker has been changed from CBaseEntity to CSGOAttackerInfo, so some entities now contain CSGOAttackerInfo internally.

Also, is there a string in that method? If so, please add it as a reference comment in the data file so it makes it easier if someone else try to track it down.

No. It is on the top of create function for projectiles, such as CMolotovProjectile::Create, but there is no clear indicator. But unless the mechanism or structure of CSGOAttackerInfo explicitly changes, I do not expect the signature to change.

Weapon would have been the ideal choice, since projectiles are internally handled as such everywhere but I get your point and I agree. Since thrower is available from Entity, this is where it should be overridden as well. The following (untested) into the CSGO's Entity class:

thrower = property(
    lambda self: self.__getattr__('thrower'),
    lambda self, value: self.set_attacker(pointer_from_inthandle(value))
)

Should theoretically do it. Would raise an attribute error for non-projectile entities, and would properly override it for projectiles. I used lambdas since we override a dynamic property but we could also use private getter/setter methods as well.

It's exactly the best way! I've added the commit.