bw logo

Chapter 7. Inheritance in BigWorld

Class-based inheritance is a useful design technique in object-orientated software, and is implemented in most object-orientated languages.

BigWorld uses three separate classes (for the cell, base, and client entity parts) to implement an entity, and a definition file (<res>/scripts/entity_defs/<entity>.def) to tie them all together. As such, there are a variety of ways that entities, or parts of entities, may use inheritance in their specification and implementation.

There are three different ways to declare inheritance relationships in BigWorld, all fulfilling different needs.

7.1. Python Class Inheritance

The Python language allows classes to be derived from each other.

For example, to define a class B, derived from A, and methods for each of them, you can have the code below:

class A:
  def f( self ):
    print "A.f was called"

class B( A ):
  def g( self ):
    print "B.g was called"

Declaring Python class A and its derived class B

Then suppose you have a program with the code below:

x = B()
x.f()
x.g()

Program using Python class inheritance

The output of this program will be as illustrated below:

A.f was called
B.g was called

Example program output

When used in entities, this form of inheritance allows the sharing of common implementation details between entity types. Multiple inheritance is allowed, so that you can use many Python classes to help implement disparate features in some entities.

This concept is illustrated in the diagram and code fragments below:

Python class inheritance

The code fragments below show how the Python class CommonBase could be used in an entity DerivedEntity[12]

  1. If a base class' cell script (<res>/scripts/cell/CommonBase.py) is defined as below:

    # note that this class is not derived from BigWorld.Entity
    # so it is just an ordinary Python class
    class CommonBase:
      ...
      def readyForAction( self ):
        # implement method's logic
        return True
      ...
  2. If a derived entity's cell script (<res>/scripts/cell/DerivedEntity1.py) is defined as below:

    import BigWorld
    from common import CommonBase
    ...
    # derive from CommonBase, so you can use the method readyForAction
    class DerivedEntity1( BigWorld.Entity, CommonBase ):
      ...
      def __init__( self ):
        BigWorld.Entity.__init__( self )
        CommonBase.__init__( self )
      ...
      def someAction( self ):
        if self.readyForAction():
          print "action performed"
      ...
  3. Then you can call methods from the base class, as illustrated below:

    DerivedEntity1.readyForAction()

7.2. Entity Interfaces

BigWorld also supports inheritance in a form similar to Java's interface system. There can be a folder <res>/scripts/defs/interfaces that can be used to declare common parts of entities. This allows the definition in one place of often-used declarations.

This concept is illustrated below:

Python entity interfaces

The format of entity interface definition files is similar to the format of entity definition files, except that interface definition files do not have the section <Parent>. For more details on entity definition files, see The Entity Definition File.

The outline of an interface definition file is described below (all sections are optional):

<root>

  <Implements>
    <!-- interface references -->
  </Implements>
  
  <Properties> 1
    <!-- properties -->
  </Properties>

  <ClientMethods> 2
    <!-- client methods -->
  </ClientMethods>

  <CellMethods> 3
    <!-- cell methods -->
  </CellMethods>

  <BaseMethods> 4
    <!-- base methods -->
  </BaseMethods>

  <LoDLevels> 5
    <!-- levels of detail -->
  </LODLevels>

</root>

<res>/scripts/entity_defs/interfaces/<entity>.def Minimal entity definition file

1

For details, see Properties.

2

For details, see Methods.

3

For details, see Methods.

4

For details, see Methods.

5

For details, see LOD (Level of Detail) on Properties.

Unlike entities, entity interfaces do not need to have associated Python implementation files, although this can be a good idea.

The code fragments below illustrate the result of using an interface in an entity definition file:

  1. If an entity is defined implementing an interface (<res>/scripts/entity_defs/someEntity.def), as below:

    <!-- someEntity -->
    <root>
      ...
      <Implements>
        <Interface> someInterface </Interface>
      </Implements>
      ...
    </root>
  2. And if the implemented interface is defined (<res>/scripts/entity_defs/interfaces/someInterface.def) as below:

    <!-- someInterface -->
    <root>
      <Properties>
        <name>
          <Type>  STRING       </Type>
          <Flags> ALL_CLIENTS  </Flags>
        </name>
      </Properties>
    </root>
  3. Then conceptually, the resulting entity definition is as defined as below:

    <!-- someEntity -->
    <root>
      ...
      <Properties>
        <name>
          <Type>  STRING       </Type>
          <Flags> ALL_CLIENTS  </Flags>
        </name>
      </Properties>
      ...
    </root>

A property from an interface can be overridden if the description needs to be changed. In this case, the entire property description is replaced with the new one, so all appropriate fields need to be specified.

7.3. Entity Parents

It is often possible to define an entity that provides functionality common to other entity types as a single base entity. For example, a collection of NPCs may share most of their implementation, but need some specific tuning to turn them into a guard or a shopkeeper.

This concept is illustrated below:

Python entity parents

The code fragments below demonstrate this form of inheritance.

  1. Define the base entity GenericEntity (<res>/scripts/entity_defs/GenericEntity.def):

    <!-- GenericEntity -->
    <root>
      <!-- common properties and methods -->
    </root>
  2. Define GenericEntity's base script:

    import BigWorld
    
    class GenericEntity( BigWorld.Base ):
      ...
      def __init__( self ):
        BigWorld.Base.__init__( self )
      ...
  3. Define GenericEntity's cell script:

    import BigWorld
    
    class GenericEntity( BigWorld.Entity ):
      ...
      def __init__( self ):
        BigWorld.Entity.__init__( self )
      ...
  4. Define derived entity SpecificEntity:

    <!-- SpecificEntity -->
    <root>
      <!-- inheritance is defined in this tag -->
      <Parent> GenericEntity </Parent>
    
      <!-- add more properties and methods here -->
    </root>
  5. Define SpecificEntity's base script:

    import BigWorld
    import GenericEntity
    
    class SpecificEntity( GenericEntity.GenericEntity ):
      ...
      def __init__( self ):
        GenericEntity.GenericEntity.__init__( self )
     ...
  6. Define SpecificEntity's cell script:

    import BigWorld
    import GenericEntity
    
    class SpecificEntity( GenericEntity.GenericEntity ):
      ...
      def __init__( self ):
        GenericEntity.GenericEntity.__init__( self )
      ...

7.4. Client Entity Reuse

There may be times when an entity type only needs to be specialised on the server. Using the optional section <ClientName> in a .def file allows a different (usually parent) entity type to be used for the client entity.

For example, if NPC is derived from Avatar, and NPC contains additional properties that the client does not need to access, NPC objects can be sent to clients as Avatar objects. This means that the client does not need a specific script to handle NPCs.

7.5. User Data Object Interfaces and Parents

The inheritance of interfaces and parents described for entities also apply to User Data Objects. Due to the similarity of User Data Objects to regular Entities, for further details, please refer to sections Entity Interfaces and Entity Parents

For an example of inheritance in User Data Objects see <res>/scripts/user_data_object_defs/testItem.def and <res>/scripts/user_data_object_defs/testParent.def.



[12] Although this example is implemented on the cell, this technique is also useful for base and client scripts.