Results 41 to 50 of 79
Thread: Stats
-
01-06-09, 05:57 PM #41
Re: Stats
Regarding reading from the config, LS now has cvar support, however to get that you need to update to the latest SVN version, which changes the binary and would require you to rebuild that.
Until your ready to do a binary rebuild, you can just make a global variable in that file set to the value you want as a placeholder until you add in cvar support.
mapvoting.py has an example of how to use cvars.
-
01-08-09, 01:58 AM #42
Re: Stats
Here's a proof of concept of how you can use subclassing to get around a lot of the differences between the mods. Every difference was hidden behind a key or method can can be overridden by the subclass in order to change the behavior. When a new difference is found, we can just make a new method or key to abstract away from it. This way there is no complicated if/else branching and we don't have duplicate code.
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 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): pass class GameStats(object): def __init__(self): pass _hitgroups = { 1: "S_head", 2: "S_chest", 3: "S_stomach", 4: "S_leftarm", 5: "S_rightarm", 6: "S_leftleg", 7: "S_rightleg", } _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._weaponFirePlayerKey = "userid" self._playerHurtVictimKey = "userid" events.addListener(events.EVENT_PLAYER_CONNECTED, self._playerConnected) events.addListener(events.EVENT_PLAYER_DISCONNECTED, self._playerDisconnected) sdk.addGameEventListener("player_death", self._determineSourceOfDeath) sdk.addGameEventListener(self._getPlayerHurtEventName(), self._playerHurt) sdk.addGameEventListener(self._getWeaponFireEventName(), self._weaponFire) sdk.addGameEventListener(self._getRoundEndEventName(), self._roundEnd) def getPlayerStats(self, player): return player.data[_PDATA_KEY_STATS] 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 _getPlayerHurtEventName(self): return "player_hurt" def _getWeaponFireEventName(self): return "weapon_fire" def _getRoundEndEventName(self): return "round_end" def _isHeadshot(self, event): return False def _getTotalDamage(self, event): return event.getInt("dmg_health") def _getWeapon(self, event): return event.getString("weapon") def _determineSourceOfDeath(self, event): victimId = event.getInt("userid") attackerId = event.getInt("attacker") if victimId == attackerId: self._playerSuicide(victimId, event) else: self._playerKilled(victimId, attackerId, event) def _playerKilled(self, victimId, attackerId, event): weapon = self._getWeapon(event) victim = playerManager.findPlayers(victimId)[0] victimStats = self.getPlayerStats(victim).getStatsForWeapon(weapon) attacker = playerManager.findPlayers(attackerId)[0] attackerStats = self.getPlayerStats(attacker).getStatsForWeapon(weapon) if victim.getTeamId() != attacker.getTeamId(): victimStats.S_deaths += 1 attackerStats.S_kills += 1 if self._isHeadshot(event): attackerStats.S_headshots += 1 else: attackerStats.S_tks += 1 def _playerSuicide(self, victimId, event): weapon = self._getWeapon(event) victim = playerManager.findPlayers(victimId)[0] victimStats = self.getPlayerStats(victim).getStatsForWeapon(weapon) victimStats.S_deaths += 1 def _playerHurt(self, event): weapon = self._getWeapon(event) attacker = playerManager.findPlayers(event.getInt(self._playerHurtVictimKey))[0] attackerStats = self.getPlayerStats(attacker).getStatsForWeapon(weapon) attackerStats.S_damage += self._getTotalDamage(event) attackerStats.S_hits += 1 try: hitgroup = _hitgroups[event.getInt("hitgroup")] value = getattr(attackerStats, hitgroup) value += 1 setattr(attackerStats, hitgroup, value) except KeyError: logger.error("Stats returned invalid hitgroup: %d" % event.getInt("hitgroup")) def _weaponFire(self, event): shooter = playerManager.findPlayers(event.getInt(self._weaponFirePlayerKey))[0] weapon = self._getWeapon(event) 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 _printPlayerStats(self): for player in playerManager.getPlayerSubset(client.SUBGROUP_IN_GAME_PEOPLE): self._printWeaponStats(player) player.data[_PDATA_KEY_STATS] = PlayerStats() def _roundEnd(self, event): self._printPlayerStats() class CssStatisticsDaemon(StatisticsDaemon): def __init__(self): StatisticsDaemon.__init__(self) def _isHeadshot(self, event): return event.getBool("headshot") def _getTotalDamage(self, event): return event.getInt("dmg_health") + event.getInt("dmg_armor") class DodStatisticsDaemon(StatisticsDaemon): def __init__(self): StatisticsDaemon.__init__(self) self._weaponFirePlayerKey = "attacker" self._playerHurtVictimKey = "victim" def _getTotalDamage(self, event): return event.getInt("damage_given") def _getPlayerHurtEventName(self): return "dod_stats_player_damage" def _getWeaponFireEventName(self): return "dod_stats_weapon_attack" def _getRoundEndEventName(self): return "dod_round_win" def _getWeapon(self, event): return event.getInt("weapon") class Tf2StatisticsDaemon(StatisticsDaemon): def __init__(self): StatisticsDaemon.__init__(self) class L4dStatisticsDaemon(StatisticsDaemon): def __init__(self): StatisticsDaemon.__init__(self) if modinfo.modName == "cstrike": statisticsDaemon = CssStatisticsDaemon() elif modinfo.modName == "dod": statisticsDaemon = DodStatisticsDaemon() elif modinfo.modName == "tf": statisticsDaemon = Tf2StatisticsDaemon() elif modinfo.modName == "left4dead": statisticsDaemon = L4dStatisticsDaemon() else: statisticsDaemon = StatisticsDaemon() # generic
Btw an interesting difference between the two that still needs to be accounted for in the logging is that in CSS the weapons are strings but in DoD they are numbers. Your log line uses %s, which won't work for numbers. We can avoid this by using string.format instead of using the % operator.
-
01-08-09, 11:16 AM #43
Re: Stats
Whoops I forgot to add in sdk timer support...
Tonight after I get home from work I'll add that in. Additionally I'll make a new download that has the new binary that supports cvars so we can get that working as well.
-
01-08-09, 11:40 PM #44
Re: Stats
Here's that same example, but changed to use the sdk timer to print stats every so many minutes. It also demonstrates how to make and use a cvar
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 from lonestar import cvars statsDumpMode = cvars.createCvar("ls_stats_dump", "round", 0, "When to dump the stats to the log. " + "If it's \"round\", then it will occur at the end of the round. If its a number, then it will " + "occur after that many minutes. If its 0, it will occur every map change.") 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): pass class GameStats(object): def __init__(self): pass _hitgroups = { 1: "S_head", 2: "S_chest", 3: "S_stomach", 4: "S_leftarm", 5: "S_rightarm", 6: "S_leftleg", 7: "S_rightleg", } _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._weaponFirePlayerKey = "userid" self._playerHurtVictimKey = "userid" events.addListener(events.EVENT_MAP_STARTED, self._mapStarted) events.addListener(events.EVENT_MAP_ENDED, self._mapEnded) events.addListener(events.EVENT_PLAYER_CONNECTED, self._playerConnected) events.addListener(events.EVENT_PLAYER_DISCONNECTED, self._playerDisconnected) sdk.createGameEventListener("player_death", self._determineSourceOfDeath) sdk.createGameEventListener(self._getPlayerHurtEventName(), self._playerHurt) sdk.createGameEventListener(self._getWeaponFireEventName(), self._weaponFire) self._statsTimer = None self._roundListener = None def getPlayerStats(self, player): return player.data[_PDATA_KEY_STATS] 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 _mapStarted(self, data, mapName): if statsDumpMode.stringValue == "round": self._roundListener = sdk.createGameEventListener(self._getRoundEndEventName(), self._roundEnd) else: minutes = statsDumpMode.intValue if minutes > 0: self._statsTimer = sdk.createTimer(float(minutes), lambda d, t: self._printPlayerStats) def _mapEnded(self, data, args): if self._roundListener: sdk.destroyGameEventListener(self._roundListener) self._roundListener = None if self._statsTimer: sdk.destroyTimer(self._statsTimer) self._statsTimer = None def _getPlayerHurtEventName(self): return "player_hurt" def _getWeaponFireEventName(self): return "weapon_fire" def _getRoundEndEventName(self): return "round_end" def _isHeadshot(self, event): return False def _getTotalDamage(self, event): return event.getInt("dmg_health") def _getWeapon(self, event): return event.getString("weapon") def _determineSourceOfDeath(self, event): victimId = event.getInt("userid") attackerId = event.getInt("attacker") if victimId == attackerId: self._playerSuicide(victimId, event) else: self._playerKilled(victimId, attackerId, event) def _playerKilled(self, victimId, attackerId, event): weapon = self._getWeapon(event) victim = playerManager.findPlayers(victimId)[0] victimStats = self.getPlayerStats(victim).getStatsForWeapon(weapon) attacker = playerManager.findPlayers(attackerId)[0] attackerStats = self.getPlayerStats(attacker).getStatsForWeapon(weapon) if victim.getTeamId() != attacker.getTeamId(): victimStats.S_deaths += 1 attackerStats.S_kills += 1 if self._isHeadshot(event): attackerStats.S_headshots += 1 else: attackerStats.S_tks += 1 def _playerSuicide(self, victimId, event): weapon = self._getWeapon(event) victim = playerManager.findPlayers(victimId)[0] victimStats = self.getPlayerStats(victim).getStatsForWeapon(weapon) victimStats.S_deaths += 1 def _playerHurt(self, event): weapon = self._getWeapon(event) attacker = playerManager.findPlayers(event.getInt(self._playerHurtVictimKey))[0] attackerStats = self.getPlayerStats(attacker).getStatsForWeapon(weapon) attackerStats.S_damage += self._getTotalDamage(event) attackerStats.S_hits += 1 try: hitgroup = _hitgroups[event.getInt("hitgroup")] value = getattr(attackerStats, hitgroup) value += 1 setattr(attackerStats, hitgroup, value) except KeyError: logger.error("Stats returned invalid hitgroup: %d" % event.getInt("hitgroup")) def _weaponFire(self, event): shooter = playerManager.findPlayers(event.getInt(self._weaponFirePlayerKey))[0] weapon = self._getWeapon(event) 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 _printPlayerStats(self): for player in playerManager.getPlayerSubset(client.SUBGROUP_IN_GAME_PEOPLE): self._printWeaponStats(player) player.data[_PDATA_KEY_STATS] = PlayerStats() def _roundEnd(self, event): self._printPlayerStats() class CssStatisticsDaemon(StatisticsDaemon): def __init__(self): StatisticsDaemon.__init__(self) def _isHeadshot(self, event): return event.getBool("headshot") def _getTotalDamage(self, event): return event.getInt("dmg_health") + event.getInt("dmg_armor") class DodStatisticsDaemon(StatisticsDaemon): def __init__(self): StatisticsDaemon.__init__(self) self._weaponFirePlayerKey = "attacker" self._playerHurtVictimKey = "victim" def _getTotalDamage(self, event): return event.getInt("damage_given") def _getPlayerHurtEventName(self): return "dod_stats_player_damage" def _getWeaponFireEventName(self): return "dod_stats_weapon_attack" def _getRoundEndEventName(self): return "dod_round_win" def _getWeapon(self, event): return event.getInt("weapon") class Tf2StatisticsDaemon(StatisticsDaemon): def __init__(self): StatisticsDaemon.__init__(self) class L4dStatisticsDaemon(StatisticsDaemon): def __init__(self): StatisticsDaemon.__init__(self) if modinfo.modName == "cstrike": statisticsDaemon = CssStatisticsDaemon() elif modinfo.modName == "dod": statisticsDaemon = DodStatisticsDaemon() elif modinfo.modName == "tf": statisticsDaemon = Tf2StatisticsDaemon() elif modinfo.modName == "left4dead": statisticsDaemon = L4dStatisticsDaemon() else: statisticsDaemon = StatisticsDaemon() # generic
-
01-09-09, 10:01 PM #45
Re: Stats
Just so we are on the same page, let me explain my programming history so you can understand where I am coming from on my points...
I may be self taught on some of my programming aspects but I have taken many programming classes as well (i want to say years of classes, but i crammed them all into two semesters), (though none on python). I believe both of us come from a different side of programming necessities.
Let me tell a lil story about my past. Few semesters ago, i had some (active (used within last month or two) c++, c#, semiactive (2-4 months) vb, dead (4-6 months) java, php) programming in my belt (‘think i butchered that saying'). Up to that point, I had learned that flexibility, robust-ability, and redundancy (frr) to be the most treasured traits of any code... then i took a class on simulating basic optics in a cg environment: combining physics, calculus, and programming into a single task. My view on programming changed entirely.
No longer, was I focused on FRR, efficiency became top priority, on a function executing several hundred or even thousand times a second (ray tracing or ray casting for example) simplicity was most important.
Keeping the code in a state that's easy to update was even more important. If I needed to provide functionality for many different yet similar aspects, putting things into a single, dynamic function was the way to go, but to just implement a few similar yet uniquely different functions was a lot of extra and uneeded work. If I want to use the same engine later down the road to say... implement only indirect caustics into an all encompassing function would not only create extra work for the cpu to calculate but unnecessary work for the programmer... All to what? Just save the task of copy and pasting, provide flexability for future changes that I knew were not coming? Or as my prof put it “to dress the code up in a suite incase another programmer should come along?”?
I learned that what ever gets the job done, in the most efficient manner was prized: simplicity was a trade off for: flexibility, robust-ability, and redundancy and the situation always warranted which should be used.
_________________________________________________
I understand the importance of controlling duplicate code and create dynamic and all encompassing functions for... functions that are executed on a relatively minimal basis and encompass a large range of very similar functions ( don’t know how else to say it, let me give an example).
Lets say, the function called to initialize a sophisticated program, it needs to have many aspects encompassed into its functionality, having duplicate functions for lets say... every type of video card would be unethical...(not a very good example but you get the point) . What if I knew ahead of time that I only needed to encompass 4 video cards? Why spend time creating flexability for the others if I knew I did not need to?
Now back to my example about ray tracing: if i tried to encompass every type of object or medium, a simulated ray of light might encounter id have a extremely complex function with numerous branches and sophisticated loops and such but when the time comes to render, and that functions going to be called... i.e 600,000 times... Every single extra branch, extra calculation, evaluations will start to bog things down...
Or if i tried to encompass all types interactions a scene or model of light might encounter when i calculate bidirectional reflectance distribution, 1 extra loop might make the difference between rendering for a hour or rendering for 3 hours. Its true a given branch might evaluate the type of interaction and avoid having a duplicate function but that evaluation's minute difference in calculation speed will clearly show later down the road when the function is executed hundreds of thousands of times...
(ignore the fact that for both examples i can burro evaluations on mediums at the pre step stage, im referring to the step eval stage)
Ive learned that just because you can avoid duplication, doesn't mean you should. Doing something the simplistic way, is often the most prized route when efficiency is a necessity...
_________________________________________________
So back to lonestar stats.
I think we need to keep two things in mind, how many things we need to encompass; is it worth building a flexable system when we know future implimentations will be limited yet unique enough to change the entire system (to provide flexability for future flexability of similar changes). AND efficiency, keeping functions that are evaluated many times, simple.
I can see youve taken something simplistic and made all the functions... well all encompassing... Which programming wise, is flexible but i believe in this case some of the functions to be unethical and unnecessary flexability. Take css and dod for example. When compared both have minute differences and similar gameplay your scheme works (approach in which I originally favored) but when we try to implement games like tf2 or l4d, which have aspects that completely change everything (ex for player_hurt: tf2 has crits, l4d needs to attacker type and weapon dmg type (might not be player_hurt that im thinking of for dmg type… O.o)).
Trying to add these into the scheme means I have to encorporate code which compensates for these differences, in css vs dod, the differences were small, however with dod + css + tf2 + l4d I have to encompass evals for all there differences. Why should css need to check for crits? Why should dod have to check for attacker type? You’ve taken a very specific function and made it broad and ambiguous to allow us to add in newer games, but while doing so you’ve complicated the code, added unnecessary evals, no redundancy (if one function brakes while adding support for a new game, they all break), in order to compensate for a new game’s differences.
For example, I love the way you’ve sculpted the function evaluating when to print stats, the flexibility to determine all ranges of print times is important and since the function is called very few times. Its only ethical to build an independent evaluation system around the function and encompass branches inside of it.
If we look at your player_hurt function. For a function that may be executed many times a minute, tf2 for example ( two heavies with medics on them will force this function to be called many many times) we need to avoid having extraneous and unnecessarily evaluations just as we need to do for weaponfire. Where you’re getting the victim or attacker key and the call to check weapon, over and over and over every time the function is called… Sure it’s a minute difference in eval time but, you do have to admit unnecessary both logically and resource wise just to encompass 4 games into one dynamic function. Sure if we had… lets say 200 games, each with minute yet unique differences, to encompass into these stats, then yes I absolutely see the reason and justification behind your method. If that was the case the purpose behind creating such a dynamic and all encompassing scheme would be to save time that would have been spent copy and pasting for 200 games.
_________________________________________________
As for my point regarding updating , what if a game suddenly (like dod previously had) decided to add or remove a hit box ie 3 is no longer the stomach and became the left shoulder... and it only applied to one game?
Sure i can implement it into the scheme but in the end, it will be a load of extra work, decreased functionality. What happens if i wanted to implement l4d into the your current scheme, ya its definitly doable but a lot of extra work, for what?
Besides flexibility, do we have any increased functionality? Can the code do something that it could not before? Did we save time coding it? Is it more efficient?
Personally I feel that with such a limited range of games all with numerous, unique differences between the games that using your scheme would not only be more work, in my hit box example why spend all that time creating a system which can incorporate games having different hitbox vals, when only 1 does? Why go through the frustration and future troubleshooting of that scheme? Why spend time implementing a system to check for attacker type when only one does?
Maybe later down the road if more games get released on the same version of the source engine we could definitely use and justify implementing such a scheme. Unfortunatly should we not take into account that the source engine is reaching its limits and losing its competitive edge in the market (meaning less games released on the source engine)? All that time valve spent on L4d and there’s only a few minor graphical improvements (more like a small handful of gimics)! The source engine is reaching its limit. Valve already announced they were considering starting from scratch on the engine for cs2 and its very apparent they have, from the unofficial screenshots of hl3, so we know there are no more games coming for the source engine so why spend all the time creating flexibility for more games that will not be developed?
Trust me, I can truly appreciate the art of creating unique, dynamic, all encompassing programming but I believe that the time we would have saved ourselves from implementing new games by creating a flexible scheme as you have done now. Will be more work than just copy and pasting. I added dod support in just a few minutes, and I know it works (save hitbox’s since I have yet to find any dev saying they are the same as css value wise (ex 1=head)).
If you really believe there to be a need to use such a scheme as the one you’ve proposed then ill get started with it right after I’m done helping out w4chosen.
-
01-09-09, 10:49 PM #46
Re: Stats
I think the key here is recognizing three things:
1) There really isn't a right or wrong design, just a series of tradeoffs
2) The designs are not mutually exclusive
3) To use a famous programmer's quote: Premature optimization is the root of all evil
When doing system design, it's a balance to make something flexible and yet keep efficiency where it matters. A style that works well is to start with a generic and flexible system, and then when paths are discovered that need to be more efficient, they are redone to be more flexible. Trying to make every path as efficient as possible creates a lot of extra work (not just in intiial development, but also in maintenance) that can many times be avoided. It's also important to recognize that different kinds of software require different efficiencies.
The trick is to identify paths that need to be efficient, and redo those to be such, and don't invent extra work by optimizing things that really don't need to be optimized.
For instance you already identified one such path: player_hurt. It very well might fire so often that rather than it being generic, every mod should have a specialized version just to make sure its efficient. However going back to points #2 and #3, just because one event could benefit from being specialized, it doesn't mean all of the events need to have that kind of special handling.
For example, we can keep the subclassing going on, which provides a generic stats implementation for each mod, and a nice and flexible base. Then for events that occur very rapidly, the stats subclasses rewrite those to be mod-specific and more efficient. You can even change the base constructor to not even hook the hurt event, relying on the subclass to do all the work there. But by still using the subclasses, every mod has a nice and generic implementation of printing the stats, handling round end events, etc.
The art of programming is finding a balance of using every technique you know about, and I think in this case if we carefully blend subclassing with a couple subclass-specific event handlers we'll come out with both a flexible yet efficient solution.
-
01-09-09, 11:13 PM #47
Re: Stats
Exactly my point, from your previous post you had lead me into believing that your programing principles where damn close to my sophomore c++ professors: i wont go into details but he loved the analogy of a function which could be broken up or made dynamic to a grammatical runon or (his next favorite) "rambling".
I may have twined my point around in my previous post (it was my 3rd post of the day over 4 pages long :3) I had intended to draw to the conclusion that we needed to decide what needed flexibility and what needed to be focused on efficiency (though i can see now that i babbled a bit too much between my points and made my intentions a little unclear). Having small function which will be called many times should be kept specific to a game, while those like the function to determine when to prints stats should be kept dynamic. We should focus our efforts on creating flexibility in areas that will be changed alot and remain similar amongst all games. For example going back to when to print stats, its the same destination for all games we will ever implement: stats print. When is the only var that changes amongs the games yet possible times remain similar (round end, map end, every x minutes, etc).
It seemed as though you were completely against any duplicate code, my mistake. I shall continue with stats once i have finished helping w4chosen...
Bun-
-
01-09-09, 11:30 PM #48
Re: Stats
You guys seem to know alot about programming, I have only taken two classes on programming and plan to take more. Is there any way you can find the glitch in cod4 with the victorys and defeat report to them and let them fix it.
-
01-09-09, 11:37 PM #49
Re: Stats
Originally Posted by xavier7
:3
Either way its lazy and sloppy on their part...
-
01-10-09, 02:00 AM #50
Re: Stats
Originally Posted by Bunni
Thread Information
Users Browsing this Thread
There are currently 1 users browsing this thread. (0 members and 1 guests)
Bookmarks