Results 31 to 40 of 79
Thread: Stats
-
01-05-09, 02:47 PM #31
Re: Stats
Bunni, another reason is that between those two log sets DoD was migrated from the old Source engine (the one CSS uses) to the Orange Box Source engine (the one TF2) uses. Because of the game engine upgrade, the log entries might be different/updated to use different terms.
-
01-05-09, 06:02 PM #32
Re: Stats
Originally Posted by Ewok
As for hs tracking... the problem is that the player_death event for dod does not return a bool for a headshot... Thus the only way i could think of to track headshots should have to be for every headshot hitgroup on player_hurt check to see if a player_death event follows with the same victim and attacker... But even then that's a little resource heavy...
Edit and as im looking through these 5000+ lined logs, it appears that mani was not able to track headshots eaither, seeing as i have yet to come across a weaponstat with a headshot hit group logged to it...
-
01-05-09, 11:21 PM #33
Re: Stats
It's just pstats 3.1, so you should be able to just download it normally.
Since DoD doesn't properly support headshot tracking, I'm thinking we should go after more lower-hanging fruit.
-
01-06-09, 12:06 PM #34
Re: Stats
Originally Posted by Ewok
antoher notable issue with DoD is that it doesn't distinguish between the chest and stomach like CSS does. It's just the torso.
-
01-06-09, 01:37 PM #35
Re: Stats
Originally Posted by ...bigdog...
Pseudo for DOD:
Noted Differences:
CounterStrike SourceDay Of Defeat Source
Originally Posted by CSSOriginally Posted by DODOriginally Posted by WeaponFiredod_stats_weapon_attack
Note: When a player attacks with a weapon
Name: dod_stats_weapon_attack
Structure:
short attacker
byte weaponplayer_hurt
Note: When a client is damaged
Name: player_hurt
Structure:
short userid
short attacker
byte health
byte armor
string weapon
byte dmg_health
byte dmg_armor
byte hitgroupdod_stats_player_damage
Note: When a player damages another
Name: dod_stats_player_damage
Structure:
short attacker
short victim
byte weapon
short damage
byte damage_given
float distance
byte hitgroupround_end
Note: The round ended
Name: round_end
Structure:
byte winner
byte reason
string messagedod_round_win
Note: When a round is won
Name: dod_round_win
Structure:
byte team
Either, will need to make a new function for each dod event or will need to check modinfo for every event save player_death
Im in favor of creating a new event entirely to save the server the hastle of the extra if loop, though the codes length will significantly increase. :3
Thoughts?
_________________________________________________
Code:Check mod info: watch for event weapon_fire if triggered go to weaponfired: watch for event dod_stats_weapon_attack if triggered to go weaponfired: weaponfired: if modinfo == dod: get eventstring attacker (inplace of userid for css) if modinfo == css get eventstring userid
New Function for each:
Code:watch for event weapon_fire if triggered go to cssweaponfired: watch for event dod_stats_weapon_attack if triggered to go dodweaponfired: cssweaponfired: get eventstring userid .... dodweaponfired: get eventstring attacker
_________________________________________________
As i said before, Im in favor of the latter of the two, saves resources at the cost of length...
Bun-
-
01-06-09, 01:51 PM #36
Re: Stats
dod_stats_player_damage
Note: When a player damages another
Name: dod_stats_player_damage
Structure:
short attacker
short victim
byte weapon
short damage
byte damage_given
float distance
byte hitgroup
makes me worry, might need to have a hook which prints the byte below the player_death default log output, for a few days inorder to get all the numbers matched with weapon names... :3
edit:
fucking hell ima need to buy this game, unless i can get bots to fight and print test lines on a dedicated server.... :3
edit2
Ewok, im going to need to add vars in the cfg files for stats now, since dod will need to print weaponstats at a set time, which cfg do you want them in?
-
01-06-09, 02:36 PM #37
Re: Stats
support for dod added, all thats left is to add in the method to check the cfgs for a specified time to run print stats...
Code:import hl2sdk as sdk from lonestar import events from lonestar.logging import logger from lonestar.client import playerManager from lonestar.client import Player from lonestar import client from lonestar import modinfo from lonestar.modinfo import teamManager from lonestar.modinfo import discoverModInfo from lonestar import hud import time import threading class TimerDOD(threading.Thread): def __init__(self, seconds): self.runTime = seconds threading.Thread.__init__(self) def run(self, funcToExecute): time.sleep(self.runTime) StatisticsDaemon._forceprintWeaponStatsDOD() class PlayerStats(object): def __init__(self): self.allWeaponStats = {} def getStatsForWeapon(self, weaponName): try: weaponStats = self.allWeaponStats[weaponName] except KeyError: weaponStats = WeaponStats(weaponName) self.allWeaponStats[weaponName] = weaponStats return weaponStats class WeaponStats(object): def __init__(self, weaponName): self.weaponName = weaponName self.S_shotsFired = 0 self.S_hits = 0 self.S_kills = 0 self.S_headshots = 0 self.S_tks = 0 self.S_damage = 0 self.S_deaths = 0 self.S_head = 0 self.S_chest = 0 self.S_stomach = 0 self.S_leftarm = 0 self.S_rightarm = 0 self.S_leftleg = 0 self.S_rightleg = 0 class TeamStats(object): def __init__(self): self.roundsWon = 0 class GameStats(object): def __init__(self): self.someGameStat = 0 _PDATA_KEY_STATS = "stats" class StatisticsDaemon(object): def __init__(self): self.gameStats = GameStats() for team in teamManager.getTeams(): team.data[_PDATA_KEY_STATS] = TeamStats() self.offlinePlayers = {} events.addListener(events.EVENT_PLAYER_CONNECTED, self._playerConnected) events.addListener(events.EVENT_PLAYER_DISCONNECTED, self._playerDisconnected) if modinfo.modName == "cstrike": sdk.addGameEventListener("player_death", self._determineSourceOfDeathCSS) sdk.addGameEventListener("player_hurt", self._playerHurtCSS) sdk.addGameEventListener("weapon_fire", self._weaponFireCSS) sdk.addGameEventListener("round_end", self._roundendCSS) if modinfo.modName == "dod": sdk.addGameEventListener("player_death", self._determineSourceOfDeathDOD) sdk.addGameEventListener("dod_stats_player_damage", self._playerHurtDOD) sdk.addGameEventListener("dod_stats_weapon_attack", self._weaponFireDOD) sdk.addGameEventListener("dod_round_win", self._roundendDOD) #... add other event listeners... def getPlayerStats(self, player): return player.data[_PDATA_KEY_STATS] def getPlayerWeaponStats(self, player, weaponName): return player.data[_PDATA_KEY_STATS].getStatsForWeapon(weaponName) def _playerConnected(self, data, player): # Check if the player has existing stats, and use them if he does, otherwise make new stats player.data[_PDATA_KEY_STATS] = PlayerStats() def _playerDisconnected(self, data, player): # Store the stats in the offline database self._printWeaponStats(player) del player.data[_PDATA_KEY_STATS] def _determineSourceOfDeathCSS(self, event): if event.getInt("userid") == event.getInt("attacker"): self._playerSuicideCSS(event) if event.getInt("userid") != event.getInt("attacker"): self._playerKilledCSS(event) def _determineSourceOfDeathDOD(self, event): if event.getInt("userid") == event.getInt("attacker"): self._playerSuicideDOD(event) if event.getInt("userid") != event.getInt("attacker"): self._playerKilledDOD(event) def _playerKilledCSS(self, event): weapon = event.getString("weapon") victim = playerManager.findPlayers(event.getInt("userid"))[0] victimStats = self.getPlayerStats(victim).getStatsForWeapon(weapon) attacker = playerManager.findPlayers(event.getInt("attacker"))[0] attackerStats = self.getPlayerStats(attacker).getStatsForWeapon(weapon) if (victim.getTeamId() == attacker.getTeamId()): attackerStats.S_tks += 1 elif (victim.getTeamId() != attacker.getTeamId()): victimStats.S_deaths += 1 attackerStats.S_kills += 1 if (event.getBool("headshot") == True): attackerStats.S_headshots += 1 def _playerKilledDOD(self, event): weapon = event.getString("weapon") victim = playerManager.findPlayers(event.getInt("userid"))[0] victimStats = self.getPlayerStats(victim).getStatsForWeapon(weapon) attacker = playerManager.findPlayers(event.getInt("attacker"))[0] attackerStats = self.getPlayerStats(attacker).getStatsForWeapon(weapon) if (victim.getTeamId() == attacker.getTeamId()): attackerStats.S_tks += 1 elif (victim.getTeamId() != attacker.getTeamId()): victimStats.S_deaths += 1 attackerStats.S_kills += 1 if (event.getBool("headshot") == True): attackerStats.S_headshots += 1 def _playerSuicideCSS(self, event): weapon = event.getString("weapon") victim = playerManager.findPlayers(event.getInt("userid"))[0] victimStats = self.getPlayerStats(victim).getStatsForWeapon(weapon) victimStats.S_deaths += 1 def _playerSuicideDOD(self, event): weapon = event.getString("weapon") victim = playerManager.findPlayers(event.getInt("userid"))[0] victimStats = self.getPlayerStats(victim).getStatsForWeapon(weapon) victimStats.S_deaths += 1 def _playerHurtCSS(self, event): weapon = event.getString("weapon") attacker = playerManager.findPlayers(event.getInt("attacker"))[0] attackerStats = self.getPlayerStats(attacker).getStatsForWeapon(weapon) attackerStats.S_damage += (event.getInt("dmg_health") + event.getInt("dmg_armor")) attackerStats.S_hits += 1 hitgroup = event.getInt("hitgroup") if (hitgroup == 1): attackerStats.S_head += 1 elif (hitgroup == 2): attackerStats.S_chest += 1 elif (hitgroup == 3): attackerStats.S_stomach += 1 elif (hitgroup == 4): attackerStats.S_leftarm += 1 elif (hitgroup == 5): attackerStats.S_rightarm += 1 elif (hitgroup == 6): attackerStats.S_leftleg += 1 elif (hitgroup == 7): attackerStats.S_rightleg += 1 else: logger.error("Stats returned invalid hitgroup: %d" % (event.getInt("hitgroup"))) def _playerHurtDOD(self, event): weapon = event.getString("weapon") attacker = playerManager.findPlayers(event.getInt("attacker"))[0] attackerStats = self.getPlayerStats(attacker).getStatsForWeapon(weapon) attackerStats.S_damage += (event.getInt("damage_given")) attackerStats.S_hits += 1 hitgroup = event.getInt("hitgroup") if (hitgroup == 1): attackerStats.S_head += 1 elif (hitgroup == 2): attackerStats.S_chest += 1 elif (hitgroup == 3): attackerStats.S_stomach += 1 elif (hitgroup == 4): attackerStats.S_leftarm += 1 elif (hitgroup == 5): attackerStats.S_rightarm += 1 elif (hitgroup == 6): attackerStats.S_leftleg += 1 elif (hitgroup == 7): attackerStats.S_rightleg += 1 else: logger.error("Stats returned invalid hitgroup: %d" % (event.getInt("hitgroup"))) def _weaponFireCSS(self, event): #need to make a new weapon stats line for player shooter = playerManager.findPlayers(event.getInt("userid"))[0] weapon = event.getString("weapon") shooterStats = self.getPlayerStats(shooter).getStatsForWeapon(weapon) shooterStats.S_shotsFired += 1 def _weaponFireDOD(self, event): shooter = playerManager.findPlayers(event.getInt("attacker"))[0] weapon = event.getString("weapon") shooterStats = self.getPlayerStats(shooter).getStatsForWeapon(weapon) shooterStats.S_shotsFired += 1 def _printWeaponStats(self, player): for weaponStats in self.getPlayerStats(player).allWeaponStats.values(): sdk.log("\"%s<%d><%s>\" triggered \"weaponstats\" (weapon \"%s\") (shots \"%d\") (hits \"%d\") (kills \"%d\") (headshots \"%d\") (tks \"%d\") (damage \"%d\") (deaths \"%d\")" % (player.name, player.uid, player.steamId, weaponStats.weaponName, weaponStats.S_shotsFired, weaponStats.S_hits, weaponStats.S_kills, weaponStats.S_headshots, weaponStats.S_tks, weaponStats.S_damage, weaponStats.S_deaths)) sdk.log("\"%s<%d><%s>\" triggered \"weaponstats2\" (weapon \"%s\") (head \"%d\") (chest \"%d\") (stomach \"%d\") (leftarm \"%d\") (rightarm \"%d\") (leftleg \"%d\") (rightleg \"%d\")" % (player.name, player.uid, player.steamId, weaponStats.weaponName, weaponStats.S_head, weaponStats.S_chest, weaponStats.S_stomach, weaponStats.S_leftarm, weaponStats.S_rightarm, weaponStats.S_leftleg, weaponStats.S_rightleg)) def _roundendCSS(self, event): #for player in playerManager.players: for player in playerManager.getPlayerSubset(client.SUBGROUP_IN_GAME_PEOPLE): self._printWeaponStats(player) player.data[_PDATA_KEY_STATS] = PlayerStats() def _roundendDOD(self, event): #for player in playerManager.players: for player in playerManager.getPlayerSubset(client.SUBGROUP_IN_GAME_PEOPLE): self._printWeaponStats(player) player.data[_PDATA_KEY_STATS] = PlayerStats() def _forceprintWeaponStatsDOD(self): #for player in playerManager.players: for player in playerManager.getPlayerSubset(client.SUBGROUP_IN_GAME_PEOPLE): self._printWeaponStats(player) player.data[_PDATA_KEY_STATS] = PlayerStats() statisticsDaemon = StatisticsDaemon()
-
01-06-09, 02:51 PM #38
Re: Stats
I really don't think we need to duplicate all of that code; it leads to a maintenence nightmare of needing to change two places every time a fix or feature goes in.
The differences bewteen DoD and CSS are not that large, most of the logic is the same. Using if statements is more appropriate. To speed it up, you can compare the mod type ahead of time so your just doing a boolean check.
as in:
Code:class StatisticsDaemon(object): def __init__(self): self.isDoD = False if modinfo.modName != "dod" else True ...
-
01-06-09, 03:22 PM #39
Re: Stats
Originally Posted by Ewok
Even so doing the check for lets say... every weapon fired event (think of how many times 2 heavy's spraying in tf2 trigger weapon_fired O.o), seems a bit... unnecessary, no?
As i said a few posts ago, could if-state it up, but then when more games are added more checks will be needed, event wise css, dod, tf2, and l4d all have similar yet different syntex
Originally Posted by tf2Originally Posted by l4dOriginally Posted by cssOriginally Posted by dod
With seperate events we do not need to change the code for the would-be same function everytime we add a new game. Whats worse, If the single method breaks, they all break. No redundancy. With separate functions if say a game pulled a similar move like dod did to orange box engine then that single function gets updated and not the single one for every game. So we can avoid, in a case similar to that, having to (after a update to the code) check and test it out in every game its implimented in. Last thing we need is to find out a l4d stats update broke tf2s stats... or something similar...
true, it adds a maintenance nightmare, but saves the hassle of numerous unnecessary checks and either way (single or multi functions) if pstats does a update and changes syntex they all have to get updated anyways. This way, css is done, we know its working as long as its remains untouched...
Thoughts?
_________________________________________________
(btw whats the best way for me to grab a setting from a cfg file?)
-
01-06-09, 05:56 PM #40
Re: Stats
In software, duplicate code is something to always be avoided. The costs of if statements is small, and doing a couple of extra ones doesn't really justify all of the duplicate code and issues that will create.
The idea isn't to have simplistic functions, it's to create code that doesn't do unnecessary work. Because the contents of the events are inherently different, we don't have of a choice in that
In practice, there are multiple techniques to use that get rid of the need for duplicate code, and you end up using all of them, not just one of them
1) Single path with branches inside of it, but as you noted this can get messy
2) Multiple paths, where a function pointer is given as the choice of which path to take.
3) Break the work into smaller units, and use subclasses to implement the mod-specific work.
4) Use variables to hold the dynamic aspects, initialize them early on, and then just use them in place of hard-coded choices. For instance, if the only difference is the name of the field in the event, you can store that in a variable early on, initialize that when the daemon first is created based on the mod, and then forever more just use the variable.
All I'm saying is that we can solve this problem without duplicating the code using a combination of the above items. Just using #2 leads to too much code duplication, while just using #1 leads to messy code.
It actually might work out really well to have a generic stats deamon, and then have mod specific subclasses of it that override key parts that are mod specific. That way if a mod uses the generic path, it works as needed, but if a mod has specific functionality needed, it overrides it. The other techniques can then be used on top of this base.
Thread Information
Users Browsing this Thread
There are currently 1 users browsing this thread. (0 members and 1 guests)
Bookmarks