Skip to content

Instantly share code, notes, and snippets.

@adamcunnington
Created November 5, 2013 01:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save adamcunnington/2c6840502ddcacae37e7 to your computer and use it in GitHub Desktop.
Save adamcunnington/2c6840502ddcacae37e7 to your computer and use it in GitHub Desktop.
# <path to game directory>/addons/eventscripts/_engines/python/Lib/esutils/
# players.py
# by Adam Cunnington
"""Provide a simple API to access player attributes in a single uniform
manner. Also provide a highly versatile filter system to get a set of player
objects.
"""
import itertools
import functools
import math
from esutils import tools, weapons
import es
__all__ = (
"UNASSIGNED",
"SPECTATOR",
"TERRORIST",
"COUNTER_TERRORIST",
"STEAM_PENDING",
"PlayersError",
"Player",
"all_players",
"get_player_from_handle",
"get_player_from_name",
"get_player_from_steam_ID",
)
_EXISTS_USER_ID = "userid"
# To avoid passing meaningless integers throughout the library and usage,
# declare them once as constants.
UNASSIGNED = 0
SPECTATOR = 1
TERRORIST = 2
COUNTER_TERRORIST = 3
STEAM_PENDING = "Steam_ID_PENDING"
class PlayersError(Exception):
"""General error encountered relating to esutils.players"""
class _PlayerAttribute(object):
"""A common player information type that is obtained through
es.createplayerlist(<userid>)[<userid>]. These items are read-only.
"""
def __init__(self, name):
"""Instantiate a _PlayerAttribute object.
Arguments:
name - the key value for the information item.
"""
self.name = name
def get(self, player):
"""Return the value of the player's attribute. If it has been obtained
before and the attribute values don't require updating, return a
cached value for speed. This method should generally only be
implicitly called by python.
Arguments:
player - the relevant player object.
"""
if player._attributes is None or player.auto_update_attributes:
player.update_attributes()
return player._attributes[self.name]
class _PlayerEntity(object):
"""A common player information type that is obtained
es.getplayerprop(<userid>, <entity property>). These items can be writable
as well as readable.
"""
def __init__(self, name):
"""Instantiate a _PlayerAttribute object.
Arguments:
name - the full entity property name for the information item.
"""
self.name = name
def get(self, user_ID):
"""Return the value of the player's entity property. Cacheing is not
possible and it's also unnecessary as es.getplayerprop is fast. This
method should generally only be implicitly called by python.
Arguments:
user_ID - the relevant user ID.
"""
return es.getplayerprop(user_ID, self.name)
def set(self, user_ID, value):
"""Set the value of the player's entity property. This
method should generally only be implicitly called by python.
Arguments:
user_ID - the relevant user ID.
value - the new value to set the property at.
"""
es.setplayerprop(user_ID, self.name, value)
def _address_transformer(value):
hostname, port = value.split(":")
return hostname, int(port)
def _bit_transformer(value):
return bool(value & 1)
def _frozen_transformer(value):
return value not in (2, 4, 8)
def _get_colour(player):
return tools.get_colour(player.index)
def _get_eye_angles(user_ID):
return (es.getplayerprop(user_ID, "CCSPlayer.m_angEyeAngles[%s]" % index)
for index in range(2))
def _get_eye_location(player):
for index, coordinate in enumerate(player.location):
yield es.getplayerprop(player.user_ID,
"CBasePlayer.localdata.m_vecViewOffset[%s]"
% index) + coordinate
def _get_flame_indexes(player):
for index in es.createentityindexlist("entityflame"):
ownership = es.getindexprop(index, "CEntityFlame.m_hEntAttached")
if ownership == player.handle:
yield index
def _get_index(player):
return es.getindexfromhandle(player.handle)
def _get_language(user_ID):
return es.getclientvar(user_ID, "cl_language")
def _get_knife(player):
return _get_player_weapon(player.handle, 3)
def _get_player_weapon(handle, slot):
for index in weapons.indexes_by_slot(slot):
if es.getindexprop(index, "CBaseCombatWeapon.m_hOwner") == handle:
return weapons.Weapon(index)
return None
def _get_primary(player):
return _get_player_weapon(player.handle, 1)
def _get_secondary(player):
return _get_player_weapon(player.handle, 2)
def _get_vector(user_ID):
return (es.getplayerprop(user_ID,
"CBasePlayer.localdata.m_vecVelocity[0]"),
es.getplayerprop(user_ID,
"CBasePlayer.localdata.m_vecVelocity[1]"),
es.getplayerprop(user_ID,
"CBasePlayer.localdata.m_vecVelocity[2]"))
def _get_view_angle(user_ID):
eye_angle0, eye_angle1 = _get_eye_angles(user_ID)
if eye_angle1 < 0:
eye_angle1 += 360
roll = es.getplayerprop(user_ID, "CBaseEntity.m_angRotation").split(",")[2]
return (eye_angle0, eye_angle1, float(roll))
def _get_view_coordinates(user_ID):
model = "props_c17/tv_monitor01_screen.mdl"
es.server.cmd("es_xprop_dynamic_create %s %s" %(user_ID, model))
last_give = int(es.ServerVar("eventscripts_lastgive"))
location = es.getindexprop(last_give, "CBaseEntity.m_vecOrigin")
es.server.insertcmd("es_xremove %s" % last_give)
return location
def _get_view_vector(user_ID):
eye_angle0, eye_angle1 = _get_eye_angles(user_ID)
eye1 = math.radians(eye_angle1)
return (math.cos(eye1), math.sin(eye1),
math.sin(math.radians(eye_angle0)) * -1)
def _get_weapon(player):
if player._attributes is None or player.auto_update_attributes:
player.update_attributes()
weapon = player._attributes["weapon"]
for index in list(weapons.weapon_types_by_name(weapon)).indexes:
ownership = es.getindexprop(index, "CBaseCombatWeapon.m_hOwner")
if ownership == player.handle:
return weapons.Weapon(index)
return None
def _has_c4(player):
for index in weapons.weapon_types_by_name("weapon_c4").indexes:
ownership = es.getindexprop(index, "CBaseCombatWeapon.m_hOwner")
if ownership == player.handle:
return True
return False
def _immortal_transformer(value):
return value == 0
def _jetpack_transformer(value):
return value == 4
def _is_burning(player):
return bool(_get_flame_indexes(player))
def _is_connected(user_ID):
"""Return whether or not a given unique session ID is active.
Arguments:
user_ID - the unique session ID of the user.
"""
return bool(es.exists(_EXISTS_USER_ID, user_ID))
def _is_validated(user_ID):
"""Return whether ot not a given user's network ID has been validated by
the server.
Arguments:
user_ID - the unique session ID of the user.
"""
return es.getplayersteamid(user_ID) != STEAM_PENDING
def _modulus_transformer(value):
return bool(value % 2)
def _no_block_transformer(value):
return value == 2
def _no_clip_transformer(value):
return value == 8
def _player_property(getter, get_transformer=None, pass_instance_get=False,
setter=None, set_transformer=None,
pass_instance_set=False):
"""Create an instance property representing specific information about a
player. Common information types include player attributes and player
entity propeties but custom items are also possible.
Arguments:
getter - the callable to be used to return the value when the property is
queried.
get_transformer (Keyword Default: None) - a callable that return values
pass through before they reach the user. This should be predominantly used
to ensure the end type is valid by raising an exception if not.
pass_instance_get (Keyword Default: False) - whether or not the player
instance should be passed to the getter callable instead of the user's ID.
setter (Keyword Default: None) - the callable to be used to set the value
when the property is queried.
set_transformer (Keyword Default: None) - a callable that return values
pass through before they reach the setter callable. This should be
predominantly used to ensure the end type is valid by raising an exception
if not.
pass_instance_set (Keyword Default: False) - whether or not the player
instance should be passed to the setter callable instead of the user's ID.
"""
def get(self):
if not pass_instance_get:
parameter = self.user_ID
else:
parameter = self
value = getter(parameter)
if get_transformer is None:
return value
return get_transformer(value)
def set(self, value):
if not pass_instance_set:
parameter = self.user_ID
else:
parameter = self
if set_transformer is None:
setter(parameter, value)
return
setter(parameter, set_transformer(value))
if setter is None:
return property(get)
return property(get, set)
def _set_colour(player, value):
tools.set_colour(player.index, value)
def _set_location(user_ID, value):
es.setpos(user_ID, *value)
def _set_model(player, value):
colour = tools.get_colour(player.index)
es.setplayerprop(player.index, "CBaseEntity.m_nModelIndex",
es.precachemodel("models/%s" % value))
tools.set_colour(player.index, colour)
def _set_score(user_ID, value):
es.server.queuecmd("score set %s %s" %(user_ID, value))
def _set_view_angle(user_ID, value):
es.setang(user_ID, *value)
def _set_view_coordinates(player, value):
x, y, z = (value1 - value2 for value1, value2
in itertools.izip(_get_eye_location(player.user_ID), value))
new_y = math.degrees(math.atan(y / x))
if x < 0:
new_y += 180
elif y < 0:
new_y += 360
x = 0 - math.degrees(math.atan(z / math.sqrt(pow(y, 2) + pow(x, 2))))
_set_view_angle(player.user_ID, (x, y, _get_view_angle[2]))
def _set_weapon(user_ID, value):
es.sexec(user_ID, "use %s" % value)
def _str_transformer(value):
return bool(int(value))
def _transform_immortal(value):
if value:
return 0
return 512
def _transform_jetpack(value):
if value:
return 4
return 2
def _transform_no_block(value):
if value:
return 2
return 0
def _transform_no_clip(value):
if value:
return 8
return 2
_armor = _PlayerEntity("CCSPlayer.m_ArmorValue")
_cash = _PlayerEntity("CCSPlayer.m_iAccount")
_defuser = _PlayerEntity("CCSPlayer.m_bHasDefuser")
_ducked = _PlayerEntity("CBasePlayer.localdata.m_Local.m_bDucked")
_flash_alpha = _PlayerEntity("CCSPlayer.m_flFlashMaxAlpha")
_flash_bang = _PlayerEntity("CBasePlayer.localdata.m_iAmmo.012")
_flash_duration = _PlayerEntity("CCSPlayer.m_flFlashDuration")
_he_grenade = _PlayerEntity("CBasePlayer.localdata.m_iAmmo.011")
_health = _PlayerEntity("CBasePlayer.m_iHealth")
_helmet = _PlayerEntity("CCSPlayer.m_bHasHelmet")
_immortal = _PlayerEntity("CBasePlayer.m_lifeState")
_in_rescue_zone = _PlayerEntity("CCSPlayer.m_bInHostageRescueZone")
_move_type = _PlayerEntity("CBaseEntity.movetype")
_nightvision = _PlayerEntity("CCSPlayer.m_bHasNightVision")
_nightvision_on = _PlayerEntity("CCSPlayer.m_bNightVisionOn")
_no_block = _PlayerEntity("CBaseEntity.m_CollisionGroup")
_smoke_grenade = _PlayerEntity("CBasePlayer.localdata.m_iAmmo.013")
_speed = _PlayerEntity("CBasePlayer.localdata.m_flLaggedMovementValue")
class Player(object):
"""Effectively create data store for information about a specific player.
Each attribute can be queried and a dynamic value will be returned. Some
attributes are also writable.
"""
active_weapon = _player_property(_get_weapon, pass_instance_get=True,
setter=_set_weapon, set_transformer=str)
address = _player_property(_PlayerAttribute("address").get,
_address_transformer, True)
armor = _player_property(_armor.get, setter=_armor.set,
set_transformer=int)
bot = _player_property(es.isbot, bool)
burning = _player_property(_is_burning, pass_instance_get=True)
c4 = _player_property(_has_c4, pass_instance_get=True)
cash = _player_property(_cash.get, setter=_cash.set, set_transformer=int)
colour = _player_property(_get_colour, pass_instance_get=True,
setter=_set_colour, set_transformer=tuple,
pass_instance_set=True)
connected = _player_property(_is_connected)
dead = _player_property(_PlayerEntity("CBasePlayer.pl.deadflag").get, bool)
deaths = _player_property(_PlayerAttribute("deaths").get, int,
pass_instance_get=True)
defuser = _player_property(_defuser.get, _modulus_transformer,
setter=_defuser.set, set_transformer=int)
defusing = _player_property(_PlayerEntity("CCSPlayer.m_bIsDefusing").get,
_modulus_transformer)
ducked = _player_property(_ducked.get, _modulus_transformer)
flash_alpha = _player_property(_flash_alpha.get, int, _flash_alpha.set, int)
flash_bang = _player_property(_flash_bang.get, setter=_flash_bang.set,
set_transformer=int)
flash_duration = _player_property(_flash_duration.get,
setter=_flash_duration.set,
set_transformer=int)
frozen = _player_property(_move_type.get, _frozen_transformer)
handle = _player_property(es.getplayerhandle)
he_grenade = _player_property(_he_grenade.get, setter=_he_grenade.set,
set_transformer=int)
health = _player_property(_health.get, setter=_health.set,
set_transformer=int)
helmet = _player_property(_helmet.get, _modulus_transformer,
setter=_helmet.set, set_transformer=int)
immortal = _player_property(_immortal.get, _immortal_transformer,
setter=_immortal.set,
set_transformer=_transform_immortal)
in_bomb_zone = _player_property(
_PlayerEntity("CCSPlayer.m_bInBombZone").get, _modulus_transformer)
in_buy_zone = _player_property(
_PlayerEntity("CCSPlayer.m_bInBuyZone").get, _modulus_transformer)
in_rescue_zone = _player_property(_in_rescue_zone.get,
_modulus_transformer)
index = _player_property(_get_index, pass_instance_get=True)
jetpack = _player_property(_move_type.get, _jetpack_transformer,
setter=_move_type.set,
set_transformer=_transform_jetpack)
kills = _player_property(_PlayerAttribute("kills").get, int, True,
_set_score, int)
knife = _player_property(_get_knife, pass_instance_get=True)
language = _player_property(_get_language)
location = _player_property(es.getplayerlocation, setter=_set_location,
set_transformer=tuple)
model = _player_property(_PlayerAttribute("model").get,
pass_instance_get=True, setter=_set_model,
set_transformer=str)
name = _player_property(es.getplayername)
nightvision = _player_property(_nightvision.get, _modulus_transformer,
setter=_nightvision.set,
set_transformer=int)
nightvision_on = _player_property(_nightvision_on.get,
_modulus_transformer,
setter=_nightvision_on.set,
set_transformer=int)
no_block = _player_property(_no_block.get, _no_block_transformer,
setter=_no_block.set,
set_transformer=_transform_no_block)
no_clip = _player_property(_move_type.get, _no_clip_transformer,
setter=_move_type.set,
set_transformer=_transform_no_clip)
on_ground = _player_property(_PlayerEntity("CBasePlayer.m_fFlags").get,
_bit_transformer)
packet_loss = _player_property(_PlayerAttribute("packetloss").get, int,
True)
ping = _player_property(_PlayerAttribute("ping").get, int, True)
primary = _player_property(_get_primary, pass_instance_get=True)
secondary = _player_property(_get_secondary, pass_instance_get=True)
serial_number = _player_property(_PlayerAttribute("serialnumber").get,
int, True)
smoke_grenade = _player_property(_smoke_grenade.get,
setter=_smoke_grenade.set,
set_transformer=int)
spectator = _player_property(_PlayerAttribute("isobserver").get,
_str_transformer, True)
speed = _player_property(_speed.get, setter=_speed.set,
set_transformer=float)
steam_ID = _player_property(es.getplayersteamid)
team_ID = _player_property(es.getplayerteam)
time_connected = _player_property(_PlayerAttribute("timeconnected").get,
float, True)
tv = _player_property(_PlayerAttribute("ishltv").get, _str_transformer,
True)
validated = _player_property(_is_validated)
vector = _player_property(_get_vector)
view_angle = _player_property(_get_view_angle, setter=_set_view_angle,
set_transformer=tuple)
view_coordinates = _player_property(_get_view_coordinates,
setter=_set_view_coordinates,
set_transformer=tuple)
view_vector = _player_property(_get_view_vector)
def __init__(self, user_ID):
"""Instantiate a Player object.
Arguments:
user_ID - the user ID of the player to store information about.
"""
if not _is_connected(user_ID):
raise PlayersError("user ID %s does not exist on the server."
% user_ID)
self.user_ID = user_ID
self._attributes = None
self.auto_update_attributes = True
def ban_address(self, time, reason=None):
"""Ban and kick the player by their ip.
Arguments:
time - how long the player should be banned by ip for.
reason (Keyword Default: None) - why they should be banned.
"""
es.server.queuecmd("addip %s %s" %(time, self.address))
self.kick(reason)
def ban_steam_ID(self, time, reason=None):
"""Ban and kick the player by their steam ID.
Arguments:
time - how long the player should be banned by steam ID for.
reason (Keyword Default: None) - why they should be banned.
"""
es.server.queuecmd("banid %s %s" %(self.user_ID, time))
self.kick(reason)
def burn(self):
"""Set the player on fire, causing them to take damage."""
es.server.cmd("es_xfire %s !self ignite" % self.user_ID)
def change_team(self, team):
"""Move the player to a different team.
Arguments:
team - the integer of the team to change the player to. Rather than
directly passing an integer, players.SPECTATOR, players.TERRORIST
and players.COUNTER_TERRORIST should be passed.
"""
es.server.queuecmd("es_xchangeteam %s %s" %(self.user_ID, team))
def drop(self, weapon_name=None):
"""Force the player to drop their weapon. If a specific weapon is
passed, cause them to switch to it first.
Arguments:
weapon_name (Keyword Default: None) - the full name of the weapon to
drop.
"""
weapon_name = weapons.transform_weapon_name(weapon_name)
if weapon_name is not None:
if weapon_name not in (self.primary.weapon_type.name,
self.secondary.weapon_type.name,
self.c4.weapon_type.name):
raise PlayersError("weapon %s is not owned by player user ID,"
" %s" %(weapon_name, self.user_ID))
_set_weapon(self.user_ID, weapon_name)
es.sexec(self.user_ID, "drop")
def extinguish(self):
"""Stop the player from being on fire and taking damage from
burning.
"""
for index in _get_flame_indexes(self):
es.setindexprop(index, "CEntityFlame.m_flLifetime", 0)
def give(self, entity_classname):
"""Give the player an entity by it's classname.
Arguments:
entity_classname - the full keyvalue name of the weapon as it appears
in es.createentityindexlist.
"""
es.delayed(0, "es_xgive %s %s" %(self.user_ID, entity_classname))
def flash(self, alpha, duration):
"""Forcefully flash the player a given brightness for a given time.
Arguments:
alpha - the brightness to flash them.
duration - the time in seconds to flash them for including the
brightness fade time.
"""
self.flash_alpha = 0
self.flash_duration = 0
self.flash_alpha = alpha
self.flash_duration = duration
def freeze(self):
"""Freeze the player, stopping them from being able to move. If they
were moving before, footstep sounds will continue to emit and weapon
fire will resemble any inaccuracies.
"""
es.setplayerprop(self.user_ID, "CBaseEntity.movetype", 0)
def kick(self, reason=None):
"""Forcefully remove the player from the server.
Arguments:
reason (Keyword Default: None) - why the should be kicked.
"""
if reason is None:
es.server.queuecmd("kickid %s" % self.user_ID)
else:
es.server.queuecmd("kickid %s %s" %(self.user_ID, reason))
def kill(self):
"""Force the player to commit suicide and die."""
es.sexec(self.user_ID, "kill")
def push(self, horizontal, vertical, vertical_override=False):
"""Forcefully move the player in a given direction.
Keyword Arguments:
horizontal - the horizontal component value to move the player.
vertical - the vertical component value to move the player.
vertical_override (Keyword Default: False) - whether or not the
vertical value should take precedence in calculating the vector to
move the player through.
"""
x, y, z = _get_vector(self.user_ID)
horizontal_x = float(horizontal * x)
horizontal_y = float(horizontal * y)
if not vertical_override:
vertical_z = 0
else:
vertical_z = vertical
es.setplayerprop(self.user_ID,
"CBasePlayer.localdata.m_vecBaseVelocity", "%f,%f,%f"
%(horizontal_x, horizontal_y, vertical_z))
def strip(self):
"""Strip the player of all their weapons."""
strip_entity = "player_weaponstrip"
self.give(strip_entity)
es.delayed(0, "es_xfire %s %s strip" %(self.user_ID, strip_entity))
def unfreeze(self):
"""Unfreeze the player, allowing them to move again."""
es.setplayerprop(self.user_ID, "CBaseEntity.movetype", 2)
def update_attributes(self):
"""Update the cached attribute values for the player."""
self._attributes = es.createplayerlist(self.user_ID)[self.user_ID]
# Create a single public way of obtaining a base object of all players which
# can be further filtered.
all_players = functools.partial(tools.Filter, (Player(user_ID) for user_ID
in es.getUseridList()))
def get_player_from_handle(handle):
"""Return the user's unique session ID from a handle ID.
Arguments:
handle - the handle ID to get a user's unique session ID from.
"""
for player in all_players():
if handle == player.handle:
return player
return None
def get_player_from_name(name):
"""Return the user's unique session ID from a name. Full-name matches have
a higher order of precendence than partial matches.
Arguments:
name - the name to get a user's unique session ID from.
"""
user_data = dict((player.name, player)
for player in all_players())
player = user_data.get(name)
if player:
return player
for user_name in user_data:
if name in user_name:
return user_data[user_name]
raise KeyError(name)
def get_player_from_steam_ID(steam_ID):
"""Return the user's unique session ID from a steam ID.
Arguments:
steam_ID - the steam ID to get a user's unique session ID from.
"""
for player in all_players():
es.msg("steam_ID = %s" % player.steam_ID)
if steam_ID == player.steam_ID:
return player
raise KeyError(steam_ID)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment