<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://bf2tech.uturista.pt/index.php?action=history&amp;feed=atom&amp;title=Cookbook%3AChanging_Objects_At_Runtime</id>
	<title>Cookbook:Changing Objects At Runtime - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://bf2tech.uturista.pt/index.php?action=history&amp;feed=atom&amp;title=Cookbook%3AChanging_Objects_At_Runtime"/>
	<link rel="alternate" type="text/html" href="https://bf2tech.uturista.pt/index.php?title=Cookbook:Changing_Objects_At_Runtime&amp;action=history"/>
	<updated>2026-08-06T01:10:29Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.40.1</generator>
	<entry>
		<id>https://bf2tech.uturista.pt/index.php?title=Cookbook:Changing_Objects_At_Runtime&amp;diff=87&amp;oldid=prev</id>
		<title>Pireax: Created page with &quot;==  Problem  ==  You don&#039;t like how some of the standard Python code that comes with BF2 works, and want to change it. Maybe you want to add your own RCon commands; maybe you...&quot;</title>
		<link rel="alternate" type="text/html" href="https://bf2tech.uturista.pt/index.php?title=Cookbook:Changing_Objects_At_Runtime&amp;diff=87&amp;oldid=prev"/>
		<updated>2018-06-21T19:27:58Z</updated>

		<summary type="html">&lt;p&gt;Created page with &amp;quot;==  Problem  ==  You don&amp;#039;t like how some of the standard Python code that comes with BF2 works, and want to change it. Maybe you want to add your own RCon commands; maybe you...&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;==  Problem  ==&lt;br /&gt;
&lt;br /&gt;
You don&amp;#039;t like how some of the standard Python code that comes with BF2 works, and want to change it. Maybe you want to add your own RCon commands; maybe you want to change the way scores are kept. You &amp;#039;&amp;#039;could&amp;#039;&amp;#039; just hack the default code and make whatever changes you wanted, but if you do very much of that it becomes a maintenance nightmare, and things really could get ugly when you try to apply the next game patch that EA will be releasing &amp;quot;soon&amp;quot;, and all your changes get overwritten.&lt;br /&gt;
&lt;br /&gt;
==  Solution  ==&lt;br /&gt;
&lt;br /&gt;
Python is a [http://en.wikipedia.org/wiki/Dynamic_language dynamic language], which, among other things, means that programs can change themselves even while they are running. We can take advantage of Python&amp;#039;s dynamic nature to write code that dynamically modifies the properties of standard BF2 objects.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Example 1: Adding A New Property To An Object  ===&lt;br /&gt;
&lt;br /&gt;
Let&amp;#039;s say you&amp;#039;re working on a new mod for BF2 and need for [[Object_Reference#bf2.PlayerManager.Player|Player Objects]] to track a new property, called &amp;quot;karma&amp;quot;. If you look in the [[Object_Reference|Object Reference]], you&amp;#039;ll see that Player Objects don&amp;#039;t have a karma property, and you want to add one.&lt;br /&gt;
&lt;br /&gt;
In Python, if all you want to do is add a new attribute to an object, you can Just Do It:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt; playerObject.karma = 0 &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There--it&amp;#039;s done! If &amp;lt;code&amp;gt;playerObject&amp;lt;/code&amp;gt; is an instance of class &amp;lt;code&amp;gt;bf2.PlayerManager.Player&amp;lt;/code&amp;gt;, then we just created a new attribute called &amp;quot;karma&amp;quot; within that instance, and assigned it the value &amp;quot;0&amp;quot;. There really isn&amp;#039;t anything else to it--we can read and change that attribute elsewhere in our code at will, just as though it had been defined as part of the class from the beginning.&lt;br /&gt;
&lt;br /&gt;
One note, though: we didn&amp;#039;t add this attribute to the &amp;lt;code&amp;gt;bf2.PlayerManager.Player&amp;lt;/code&amp;gt; class--we added it to a specific instance of that class, so only that one player will have a karma attribute. If we want other players to have a karma attribute, then we need to find their playerObjects and do the same thing. Assuming we&amp;#039;ve imported &amp;lt;code&amp;gt;bf2&amp;lt;/code&amp;gt;, we could loop through and assign a karma attribute to all players by doing something like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for playerObject in bf2.playerManager.getPlayers():&lt;br /&gt;
    playerObject.karma = 0&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If we really wanted to build the karma attribute into the class itself, we could do so by dynamically replacing the class&amp;#039;s &amp;lt;code&amp;gt;__init__&amp;lt;/code&amp;gt; method using the technique shown in Example 2, below. . . but that would be overkill for most applications; just adding attributes to individual instances will be sufficient for most things you would want to do with new attributes.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===  Example 2: Adding A New Method To An Object  ===&lt;br /&gt;
&lt;br /&gt;
There is no direct way to get a player&amp;#039;s [[Player_Identity#CD_Key_Hash|CD Key Hash]] from within Python code. You can, however, do it by executing a console command and parsing the results--[[Cookbook:Accessing_CD_Key_Hash|this Cookbook entry]] show how. Let&amp;#039;s say you want to add a new method to the &amp;lt;code&amp;gt;bf2.PlayerManager.Player&amp;lt;/code&amp;gt; class, so that you can access each player&amp;#039;s CD key in an object-oriented and intuitive way.&lt;br /&gt;
&lt;br /&gt;
You can add a new &amp;lt;code&amp;gt;getCDKey&amp;lt;/code&amp;gt; method to player objects by running this code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  import bf2.PlayerManager&lt;br /&gt;
  import re&lt;br /&gt;
  import new&lt;br /&gt;
 &lt;br /&gt;
  def playerData(self):&lt;br /&gt;
      &amp;#039;&amp;#039;&amp;#039;Returns a dictionary containing player data; dictionary keys are&lt;br /&gt;
         playerIDs, values are 4-element tuples, containing player name,&lt;br /&gt;
         IP address, IP port, and cd-key hash.&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
     &lt;br /&gt;
      rawData = host.rcon_invoke(&amp;quot;admin.listplayers&amp;quot;)&lt;br /&gt;
     &lt;br /&gt;
      pattern = re.compile(r&amp;#039;&amp;#039;&amp;#039;^Id:\ +(\d+)                              # PlayerID&lt;br /&gt;
                               \ -\ (\S+)                                # Player Name&lt;br /&gt;
                               \ is\ remote\ ip:\ (\d+\.\d+\.\d+\.\d+):  # IP Address&lt;br /&gt;
                               (\d+).*?                                  # Port Number&lt;br /&gt;
                               hash:\ (\w{32})                           # CD Key Hash&lt;br /&gt;
                            &amp;#039;&amp;#039;&amp;#039;, re.DOTALL | re.MULTILINE | re.VERBOSE)&lt;br /&gt;
     &lt;br /&gt;
      result = {}&lt;br /&gt;
      for player in pattern.findall(rawData):&lt;br /&gt;
          result[int(player[0])] = player[1:]&lt;br /&gt;
     &lt;br /&gt;
      return result[self.index][3]&lt;br /&gt;
 &lt;br /&gt;
 newMethod = new.instancemethod(playerData, None, bf2.PlayerManager.Player)&lt;br /&gt;
 bf2.PlayerManager.Player.getCDKey = newMethod&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Let&amp;#039;s go through what this program is doing:&lt;br /&gt;
&lt;br /&gt;
* It imports &amp;lt;code&amp;gt;bf2.PlayerManager&amp;lt;/code&amp;gt; because the class we want to modify lives in that module.&lt;br /&gt;
* It imports &amp;lt;code&amp;gt;re&amp;lt;/code&amp;gt; because that package gives it access to regular expressions, needed by the &amp;lt;code&amp;gt;playerData&amp;lt;/code&amp;gt; function.&lt;br /&gt;
* It imports &amp;lt;code&amp;gt;new&amp;lt;/code&amp;gt;, which is where some of the magic comes from: the [http://python.org/doc/2.3.4/lib/module-new.html &amp;lt;code&amp;gt;new&amp;lt;/code&amp;gt;] module contains several functions that can be used to dynamically create code-related objects; we&amp;#039;re going to use the &amp;lt;code&amp;gt;new.instancemethod&amp;lt;/code&amp;gt; function, which binds functions to instances.&lt;br /&gt;
* Then it defines the function we&amp;#039;re going to use as our new method, &amp;lt;code&amp;gt;playerData&amp;lt;/code&amp;gt;. At this stage, &amp;lt;code&amp;gt;playerData&amp;lt;/code&amp;gt; is just a function, quite separate from any objects.&lt;br /&gt;
&lt;br /&gt;
Note, however, that the &amp;lt;code&amp;gt;playerData&amp;lt;/code&amp;gt; function shown here is slightly different from the one shown in the [[Cookbook:Accessing_CD_Key_Hash|Cookbook entry]]: here, it takes a parameter &amp;lt;code&amp;gt;self&amp;lt;/code&amp;gt;. Right now &amp;lt;code&amp;gt;playerData&amp;lt;/code&amp;gt; is &amp;quot;unbound&amp;quot;; once we turn it into a &amp;quot;bound&amp;quot; function, &amp;lt;code&amp;gt;self&amp;lt;/code&amp;gt; will automatically be set to the instance.&lt;br /&gt;
&lt;br /&gt;
* The program then assigns something unusual to the variable &amp;lt;code&amp;gt;newMethod&amp;lt;/code&amp;gt;: this is where we use the &amp;lt;code&amp;gt;new.instancemethod&amp;lt;/code&amp;gt; function to create a &amp;quot;bound&amp;quot; version of &amp;lt;code&amp;gt;playerData&amp;lt;/code&amp;gt;. The parameter &amp;lt;code&amp;gt;None&amp;lt;/code&amp;gt; specifies that we don&amp;#039;t want our new function bound to any specific instances--we want to leave the binding open, so that it will apply to every instances of the &amp;lt;code&amp;gt;bf2.PlayerManager.Player&amp;lt;/code&amp;gt; class.&lt;br /&gt;
* Finally, we create a new attribute (&amp;lt;code&amp;gt;getCDKey&amp;lt;/code&amp;gt;) for the class &amp;lt;code&amp;gt;bf2.PlayerManager.Player&amp;lt;/code&amp;gt;, and assigning to it the bound function object &amp;lt;code&amp;gt;newMethod&amp;lt;/code&amp;gt;. In Python, methods are just attributes that reference executable objects--so what we&amp;#039;ve done is to effectively add a new method to the Player class. All player objects are instances of that class, so we now, immediately, can find any player&amp;#039;s key hash just by calling a method against their player object:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt; key = playerObject.getCDKey() &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You may note that this is a spectacularly inefficient way to do things--every time you look up the key hash for a single player, it has to retrieve the key hashes for &amp;#039;&amp;#039;all&amp;#039;&amp;#039; players first--but the point here wasn&amp;#039;t to be efficient, it was to demonstrate how to modify code on the fly. We &amp;#039;&amp;#039;could&amp;#039;&amp;#039; have just changed the &amp;lt;code&amp;gt;Battlefield 2 Server/python/bf2/PlayerManager.py&amp;lt;/code&amp;gt; file to add this method, but we&amp;#039;ve demonstrated a way to get the same effect without touching the original code.&lt;br /&gt;
&lt;br /&gt;
See the related [[Cookbook:Adding_New_RCon_Commands|Adding New RCon Commands]] recipie for another example of how this method can be used.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==  Discussion  ==&lt;br /&gt;
&lt;br /&gt;
This cookbook entry just scratches the surface of what you can do with dynamic code modification. Before you start hacking around in the BF2&amp;#039;s default Python code, please consider whether it might be cleaner to use an approach like this!&lt;br /&gt;
&lt;br /&gt;
It may have occured to you, though, that &amp;#039;&amp;#039;something&amp;#039;&amp;#039; needs to be running that will make these dynamic modifications--and how does that &amp;#039;&amp;#039;something&amp;#039;&amp;#039; get to run in the first place, without itself modifying some of the standard code? The short answer is that these dynamic programming tricks are only possible if there is a &amp;quot;hook&amp;quot; you can use to cause this and other code to be run, and DICE put no such hook in their Python code. So, no matter what, at least &amp;#039;&amp;#039;&amp;#039;one&amp;#039;&amp;#039;&amp;#039; modification needs to be made to the standard BF2 code: at a minimum, an &amp;quot;import&amp;quot; statement needs to be added to one of the main files that will cause your code to be read in and executed. . . but that&amp;#039;s a subject for another time. . .&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==  Submitted By  ==&lt;br /&gt;
&lt;br /&gt;
--Woody&lt;/div&gt;</summary>
		<author><name>Pireax</name></author>
	</entry>
</feed>