bw logo

Chapter 8. Entity Instantiation and Destruction

Due to the way that BigWorld entities must be set up and linked to each other, they must be instantiated differently to how other objects are instantiated in Python. Similarly, because the parts must be unlinked at destruction time, there are special ways of accomplishing this.

As mentioned in section The Entity Script Files, an entity can have a part located on the cell (in both real and ghost forms), a part on the base, and another on clients that have the entity in their AoI. Different entity types may support their instances being only on one, two, or all three of these. Also, it is possible for instances of entity types to have less parts than their type supports.

Most commonly, the base part of an entity is created first and then, if appropriate, its cell part. There are a number of reasons for this.

  • The base entity can be created directly from the database, while the cell entity cannot.

  • The base entity can create its cell part, but the reverse is not true.

  • The cell entity needs an associated base entity to be fault-tolerant.

  • The cell entity needs an associated base entity to write itself to the database.

For entity types that have base and cell parts, the base part is always created before the cell part, and destroyed after it. It is also possible to create a cell entity that does not have a base part.

8.1. Entity Instantiation on the BaseApp

The base entity can be created in the following ways:

  • Directly from script, using the methods BigWorld.createBaseAnywhere [13], BigWorld.createBaseLocally, or BigWorld.createBaseRemotely

  • From the database, using the methods BigWorld.createBaseFromDBID or BigWorld.createBaseFromDB.

For more details on instantiating entities from the data stored in the database, see The Database Layer.

The method BigWorld.createBaseAnywhere can specify both the base and cell entity properties, and has the following signature:

def createBaseAnywhere( entityTypeName, *args, **kwargs ):

Method BigWorld.createBaseAnywhere's signature

The parameter entityTypeName is a string containing the name of the entity type to instantiate. For example, to instantiate an entity ExampleEntity, this parameter would be "ExampleEntity".

In its simplest form, it creates the entity with all default values, and is invoked as in the example below:

newEntity = BigWorld.createBaseAnywhere( "ExampleEntity" )

Example of method BigWorld.createBaseAnywhere

This method can optionally take a list of other parameters that are searched to create base and cell entity values. These parameters can be:

  • Keyword arguments

  • Dictionaries

  • ResMgr.DataSection

The keyword arguments are searched first, then the dictionaries, and finally the DataSection. If a value is not found for any of the entity's properties, the default value for that property / data type is assigned.

Keyword arguments and dictionary values not found in the entity's definition are set as base entity properties.

8.1.1. ServiceApps

A ServiceApp is a specialised type of BaseApp. It will not have player entities created on it as part of the login process or base entities created with BigWorld.createBaseAnywhere().

Although rare, entities can be created on ServiceApps using BigWorld.createBaseLocally(). This can be useful, for example, to associate an entity with a Service fragment in order to store state. Entities can also be created using BigWorld.createBaseRemotely().

8.2. Cell Entity Creation From BaseApp

The method BigWorld.createBaseAnywhere creates only the base representation of the entity. If a cell entity is required, it is the base entity's duty to instantiate its associated cell entity.

To create the associated cell entity, the following methods are used:

  • Base.createCellEntity

  • Base.createInNewSpace

  • Base.createInDefaultSpace

These methods read the base entity's special variable Base.cellData (which is initialised with the cell entity's data when the base entity is created) to get the initialisation values for the cell entity. If the entity type does not support a cell entity, the base entity will not have cellData.

The variable cellData is behaves like a dictionary containing all cell properties defined in the entity's definition file (<res>/scripts/entity_defs/<entity>.def).

It also has three additional members:

  • position Sequence of three floats (x, y, z), or a Vector3 with position to create the new entity at.

  • direction Sequence of three floats (roll, pitch and yaw) with direction for the new cell entity.

  • spaceID ID of the space for the cell entity to be created in, if space is not specified in a different way.

Once the cell entity is successfully created, the following steps take place:

  1. The variable cellData is deleted.

  2. A variable called cell is created, with the mailbox of the cell entity.

  3. The callback Base.onGetCell is invoked.

8.2.1. Creation Near an Existing Cell Entity

The diagram below illustrates the creation of the cell entity using the method Base.createCellEntity of the BigWorld module. This method cannot be used when the cell entity has already been created.

Creation of the cell entity using the method createCellEntity of BigWorld.Base.

The declaration of method createCellEntity in Python would look like this[14]:

class Base:
  ...
  def createCellEntity( self, mailbox = None ):
    ...

<res>/scripts/base/Base.py Declaration of method createCellEntity

The parameter mailbox is a cell entity mailbox. The new cell entity is created in the same space and cell as the mailbox references (if mailbox is not None). Ideally, the two entities are close, as this increases the likelihood of the entity starting on the correct cell.

The diagram below shows the flow of communications if the entity is created on the correct cell:

Flow of communication when cell entity is created on correct cell.

The diagram below shows the flow of communications if the entity is created on an incorrect cell:

Flow of communication when cell entity is created on an incorrect cell.

8.2.2. Creation in a Numbered Space

It is also possible to create the cell entity by having an appropriate value for spaceID in the property cellData. This should be avoided, as it requires the request to go via the CellAppMgr, which can cause a bottleneck.

Once the cell entity has been created, the notification method onGetCell is called on the base entity. This is the signal that it is now safe to start using the mailbox to the cell entity self.cell.

For entity someEntity, the method onGetCell can be defined as illustrated below:

import BigWorld

class someEntity( BigWorld.Base ):
  ...
  def onGetCell( self ):
    # this method was called, that means cell entity has been created.
  ...

<res>/scripts/base/someEntity.py Definition of method onGetCell

8.2.3. Creation in a New Space

The method Base.createInNewSpace dispatches a request to the CellAppMgr to create a new space, and the entity on it.

The resulting message trace is illustrated below:

Flow of communication when creating cell entity on a new space.

8.2.4. Creation in Default Space

The method Base.createInDefaultSpace is similar to method Base.createInNewSpace, except that a new space is not created.

This is only available if flag <useDefaultSpace> is set to true in the configuration file <res>/server/bw.xml.

8.3. Entity Destruction

The base entity is always created before the cell entity and is destroyed after it.

The sequence of events ensued by the destruction of a cell entity is described below:

Step Base Cell
1 Calls method destroyCellEntity. Calls method destroy.
2   Has method onDestroy automatically called.
3 Has method onLoseCell automatically called. If base is to be destroyed, this is a good place to call method destroy.  
4 cell property is lost[a].  
5 cellData property is restored, with values it had when destroyed[b].  

[a] For details on this property, see Cell Entity Creation From BaseApp.

[b] For details on this property, see Cell Entity Creation From BaseApp.

Sequence of events during entity destruction on the cell.

The method Base.destroy has two Boolean keyword arguments:

  • deleteFromDB The default value is false.

  • writeToDB The default value is true if the entity has previously been written to the database.

8.4. Entity Instantiation From The CellApp

When creating a cell entity, it can be created either with its base counterpart or not. The following sub-sections describe both approaches.

8.4.1. Instantiation With No Base Counterpart

The method BigWorld.createEntity can be called to create a cell entity with no associated base entity.

This scenario is illustrated below:

Creation of cell entity without base counterpart.

The method BigWorld.createEntity has the following signature:

def createEntity( entityTypeName, spaceID, position, direction, properties ):

Method BigWorld.createEntity's signature.

For details on the parameters for this method, see the CellApp Python API's entry Main Page Cell BigWorld Function createEntity.

8.4.2. Instantiation With Base Counterpart

The method BigWorld.createEntityOnBase allows the CellApp to create base entities. It has the following signature:

def createEntityOnBaseApp( entityTypeName, properties ):

Method BigWorld.createEntityOnBaseApp's signature

This function takes the following parameters:

  • entityTypeName Name of the entity type to create.

  • properties A dictionary of properties on the base as listed in the entity's definition file.

This function dispatches a message to a BaseApp to create a base entity, which can later call method createCellEntity to create the cell entity.

Creation of cell entity with its base counterpart

8.5. Loading Entities From Chunk Files

World Editor can be used to insert entity placeholders into chunks. These placeholders can be read by Python script on the server to load these entities into the game world using the BigWorld.fetchEntitiesFromChunks method on BaseApps.

The following example code is taken from fantasydemo/res/scripts/base/TeleportPoint.py:

class TeleportPoint( BigWorld.Base ):
  ...
  BigWorld.fetchEntitiesFromChunks( self.geometry, EntityLoader( self ) )
                                                                                
class EntityLoader:
  def __init__( self, dstEntity ):
    self.dstEntity = dstEntity
                                                                                
  def onSection( self, entityDataSection, matrix ):
    e = BigWorld.createEntity( 
      entityDataSection.readString( "type" ),
      entityDataSection[ "properties" ],
      createOnCell = self.dstEntity.cell,
      position = matrix.applyToOrigin(),
      direction = (matrix.roll, matrix.pitch, matrix.yaw))

fantasydemo/res/scripts/base/TeleportPoint.py

The BigWorld.fetchEntitiesFromChunks method causes all chunks in the space to be loaded in a loading thread. Each <entity> data section in the loaded chunks causes the method onSection to be called on the handler object. This method can then use the data section to create an appropriate entity.

For more details on loading data section information in a thread-safe way, see both the document How To Avoid Files Being Loaded in the Main Thread and the BaseApp Python API's entries Main Page Base BigWorld Function BigWorld.fetchDataSection, BigWorld.fetchEntitiesFromChunks, and BigWorld.fetchFromChunks.



[13] BigWorld.createBaseAnywhere is the recommended method of creating an instance of a base entity as it allows BaseAppMgr to select the least loaded BaseApp to be created on.

[14] The method createCellEntity is implemented in C++ the example declares it in Python just for explanation purposes.