Page 5 of 8 FirstFirst 12345678 LastLast
Results 41 to 50 of 79

Thread: Stats

  1. Registered TeamPlayer
    Join Date
    10-29-07
    Posts
    4,953
    Post Thanks / Like
    Stat Links

    Stats
    #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.

  2. Registered TeamPlayer
    Join Date
    10-29-07
    Posts
    4,953
    Post Thanks / Like
    Stat Links

    Stats
    #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
    I didn't do anything for TF2 or L4D besides make a stub subclass, so those will eventually have to be worked on. I was focusing on showing how to neutralize the differences between CSS and DoD.

    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.

  3. Registered TeamPlayer
    Join Date
    10-29-07
    Posts
    4,953
    Post Thanks / Like
    Stat Links

    Stats
    #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.

  4. Registered TeamPlayer
    Join Date
    10-29-07
    Posts
    4,953
    Post Thanks / Like
    Stat Links

    Stats
    #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

  5. Administrator Bunni's Avatar
    Join Date
    08-29-07
    Posts
    14,279
    Post Thanks / Like
    Blog Entries
    7
    Stat Links

    Stats Stats Stats Stats Stats
    Gamer IDs

    Steam ID: bunni Bunni's Originid: Dr_Bunni
    #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.

  6. Registered TeamPlayer
    Join Date
    10-29-07
    Posts
    4,953
    Post Thanks / Like
    Stat Links

    Stats
    #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.

  7. Administrator Bunni's Avatar
    Join Date
    08-29-07
    Posts
    14,279
    Post Thanks / Like
    Blog Entries
    7
    Stat Links

    Stats Stats Stats Stats Stats
    Gamer IDs

    Steam ID: bunni Bunni's Originid: Dr_Bunni
    #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-

  8. Registered TeamPlayer
    Join Date
    11-18-08
    Posts
    486
    Post Thanks / Like
    #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.

  9. Administrator Bunni's Avatar
    Join Date
    08-29-07
    Posts
    14,279
    Post Thanks / Like
    Blog Entries
    7
    Stat Links

    Stats Stats Stats Stats Stats
    Gamer IDs

    Steam ID: bunni Bunni's Originid: Dr_Bunni
    #49

    Re: Stats

    Quote Originally Posted by xavier7
    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.
    'Fraid not, Cod4 is not open source and thus there exists no legal way for any programmer outside of activison to find the problem. Though id guess it has something to do with the same logic that displays the winning and loosing stats on the scoreboard after everyround (not the one at the end of the map). On another note, there have been many and i mean many players notifying activation of this problem, yet its apparent that eaither: they dont care, fixing it involves a complicated solution, or they are able to honestly not notice thousands of complaints everywhere (forums, emails, on game reviews, etc) while remaining ignorant of such a obvious problem during beta testing for TWO games (cod4 and cod5)....


    :3

    Either way its lazy and sloppy on their part...

  10. Registered TeamPlayer
    Join Date
    10-29-07
    Posts
    4,953
    Post Thanks / Like
    Stat Links

    Stats
    #50

    Re: Stats

    Quote Originally Posted by Bunni
    It seemed as though you were completely against any duplicate code, my mistake . I shall continue with stats once i have finished helping w4chosen...
    Don't worry about it; I'm glad were on the same page now. And yes helping out chosen is definitely higher priority.

Page 5 of 8 FirstFirst 12345678 LastLast

Thread Information

Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)

Tags for this Thread

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
Title