Getting player properties slow?

Please post any questions about developing your plugin here. Please use the search function before posting!
inf
Junior Member
Posts: 20
Joined: Mon Aug 07, 2017 2:46 am

Getting player properties slow?

Postby inf » Wed Jan 03, 2018 12:11 am

I have an OnTick listener which gets each player's origin, eye_location, view_angle, and velocity every tick. I've been dealing with performance issues with my plugin which get worse with every active player. I did a test with python's time module (on windows):

Syntax: Select all

start_time = time.clock()
player.origin,
player.eye_location,
player.view_angle,
player.velocity
total_time = time.clock() - start_time
if server.tick % 100 == 0:
print('tested time: {} ({})'.format('%.6f' % total_time, server.tick))


Getting these properties of a player takes on average 0.2 ms, which when done with a full server of 64 players takes 12.8 ms which is way too slow for an ontick listener. It's very important that this data is recorded every tick so I can't change the frequency of these calls. Is there any implementation I could be using to increase the execution time?

Thanks
inf
Junior Member
Posts: 20
Joined: Mon Aug 07, 2017 2:46 am

Re: Getting player properties slow?

Postby inf » Wed Jan 03, 2018 5:38 am

Syntax: Select all

player_info = client.player.playerinfo
origin = player_info.origin
eye_loc = Vector(origin.x, origin.y, origin.z + 64)
angle = player_info.angles
velocity = player.velocity


After more testing I found even implementing this way is much faster, but still a bit sluggish :/. Any other suggestions or should I just try to minimize my calls to entity property retrieving functions?
User avatar
Ayuto
Project Leader
Posts: 2193
Joined: Sat Jul 07, 2012 8:17 am
Location: Germany

Re: Getting player properties slow?

Postby Ayuto » Wed Jan 03, 2018 6:48 am

Yes, getting those properties is slow due to the current implementation. You can speed up things by using BaseEntity. I can show you an example when I come home today.
User avatar
Ayuto
Project Leader
Posts: 2193
Joined: Sat Jul 07, 2012 8:17 am
Location: Germany

Re: Getting player properties slow?

Postby Ayuto » Wed Jan 03, 2018 4:52 pm

Syntax: Select all

from timeit import Timer

ITERATIONS = 10000

result = Timer("""
get_origin(player)
get_eye_location(player)
get_view_angle(player)
get_velocity(player)
""",
"""
from mathlib import QAngle
from entities.entity import BaseEntity

player = BaseEntity(1)

def get_eye_location(player):
return get_view_offset(player) + get_origin(player)

def get_origin(player):
return player.get_datamap_property_vector('m_vecOrigin')

def get_view_offset(player):
return player.get_network_property_vector('localdata.m_vecViewOffset[0]')

def get_view_angle(player):
eye_angle = get_eye_angle(player)
eye_angle_y = eye_angle.y
eye_angle_y = (eye_angle_y + 360) if eye_angle_y < 0 else eye_angle_y
return QAngle(eye_angle.x, eye_angle_y, get_rotation(player).z)

def get_eye_angle(player):
return player.get_network_property_vector('m_angEyeAngles[0]')

def get_rotation(player):
return player.get_datamap_property_vector('m_angRotation')

def get_velocity(player):
return player.get_key_value_vector('velocity')
""").timeit(ITERATIONS)

print(result)

result = Timer("""
player.origin
player.eye_location
player.view_angle
player.velocity
""",
"""
from players.entity import Player

player = Player(1)
""").timeit(ITERATIONS)

print(result)

Result:

Code: Select all

0.11486220925974067
1.4469872402428336
As you can see, it's >10x faster.

Note: Right now the origin is retrieved twice (get_origin and get_eye_location). You can speed it up a little bit more by only retrieving it once.
inf
Junior Member
Posts: 20
Joined: Mon Aug 07, 2017 2:46 am

Re: Getting player properties slow?

Postby inf » Wed Jan 03, 2018 6:30 pm

Thanks for this!

Is there anything else that's known to be slow that I should avoid in OnTick?
User avatar
Ayuto
Project Leader
Posts: 2193
Joined: Sat Jul 07, 2012 8:17 am
Location: Germany

Re: Getting player properties slow?

Postby Ayuto » Wed Jan 03, 2018 7:32 pm

EntityIter, PlayerIter, Entity and Player are the classes we really need be speed up.

If you show me your listener, I might can give you a few more hints.
inf
Junior Member
Posts: 20
Joined: Mon Aug 07, 2017 2:46 am

Re: Getting player properties slow?

Postby inf » Wed Jan 03, 2018 8:19 pm

Ayuto wrote:EntityIter, PlayerIter, Entity and Player are the classes we really need be speed up.

If you show me your listener, I might can give you a few more hints.


That's ok, I think I've got the performance to an acceptable point, thanks for the help!
User avatar
Ayuto
Project Leader
Posts: 2193
Joined: Sat Jul 07, 2012 8:17 am
Location: Germany

Re: Getting player properties slow?

Postby Ayuto » Mon Jan 15, 2018 5:55 pm

With the latest changes the performance has changed drastically. Running the above code again will result in the following output:

Code: Select all

0.15603045563281626
0.2694162790844672
Currently, the bottleneck is retrieving the view angle. If you comment out the lines that retrieve it and run the code again, you will get the following output:

Code: Select all

0.08905209688916216
0.005697254915190797
As you can see, the built-in methods are now even faster than the tweaked versions I created for you.


Edit:
This is now the fastest method:

Syntax: Select all

from timeit import Timer

ITERATIONS = 10000

result = Timer("""
player.origin
player.eye_location
player.view_angle
player.velocity
""",
"""
from players.entity import Player

player = Player(1)
""").timeit(ITERATIONS)

print(result)

# Fastest method
result = Timer("""
player.origin
player.eye_location
get_view_angle(player)
player.velocity
""",
"""
from players.entity import Player
from mathlib import QAngle

def get_eye_angle(player):
return player.get_network_property_vector('m_angEyeAngles[0]')

def get_view_angle(player):
eye_angle = get_eye_angle(player)
eye_angle_y = eye_angle.y
eye_angle_y = (eye_angle_y + 360) if eye_angle_y < 0 else eye_angle_y
return QAngle(eye_angle.x, eye_angle_y, player.rotation.z)

player = Player(1)
""").timeit(ITERATIONS)

print(result)
Running this code will print the following result:

Code: Select all

0.21171037814750093
0.026995176141468846
Comparing the initial performance (1.4469872402428336) with the fastest method (0.026995176141468846) it's now ~50 times faster.
marcowmadeira
Junior Member
Posts: 11
Joined: Wed Apr 27, 2016 11:54 am

Re: Getting player properties slow?

Postby marcowmadeira » Thu Jan 18, 2018 2:24 am

What's the best way to loop through all players in the OnTick event? Should we use a for loop and limit it to the maxplayers? Or is this a thing that we should completly avoid?
User avatar
L'In20Cible
Project Leader
Posts: 1533
Joined: Sat Jul 14, 2012 9:29 pm
Location: Québec

Re: Getting player properties slow?

Postby L'In20Cible » Thu Jan 18, 2018 5:28 am

I think the most efficient way would be to keep a synced dictionary so you don't have to iterate and instantiate every tick. Something like this for example:

Syntax: Select all

from entities.helpers import index_from_edict
from listeners import OnEntityCreated
from listeners import OnTick
from players import PlayerGenerator
from players.dictionary import PlayerDictionary

players = PlayerDictionary()

def load():
# Add all players to the dictionary on load in case the plugin is loaded
# at run-time...
for edict in PlayerGenerator():
players[index_from_edict(edict)]

@OnEntityCreated
def _on_entity_created(base_entity):
# Add newly created player to the dictionary...
if not base_entity.is_player():
return
players[base_entity.index]

@OnTick
def _on_tick():
# Loop through all player instances...
for player in players.values():
pass

Return to “Plugin Development Support”

Who is online

Users browsing this forum: No registered users and 28 guests