bw logo

Chapter 12. Proxies and Players

12.1. Proxies

The class BigWorld.Proxy on the BaseApp extends BigWorld.Base to support player-controlled entities. By deriving an entity from BigWorld.Proxy, you can implement player characters, their accounts, and any other relevant player-controlled objects on the server.

An entity derived from BigWorld.Proxy is created from the database whenever a client logs in to the server. For details on how BigWorld determines which Proxy to load, see User Authentication and Billing System Integration. Proxies created in this way (see below for other ways of creating proxies) which have a property called password, will have the value of that property set to the login password.

An instance of BigWorld.Proxy needs neither a cell entity, nor a client one. A proxy entity can be created using the method BigWorld.createBase, just like any other entity. For more detail on this method, see Entity Instantiation on the BaseApp.

Like other base entities, saving and loading proxy entities from the database is possible. Initially, these reloaded proxy entities will be created without an attached client. An existing proxy can later hand its client over to the reloaded one, in which case the reloaded proxy will be the one handling the client connection.

This allows you to have, for example, an Account entity that people can log in to, and a Character entity that people can select from a menu in order to use in the game.

To pass the control of a client from one proxy to another, use the method giveClientTo, as in the example below:

clientControlledProxy.giveClientTo( nonClientControlledProxy )

Whenever a client moves between proxies, or the cell entity of the proxy that a client is attached to is destroyed, the client receives a call on onEntitiesReset to clear out its current knowledge of the world. This effectively interrupts all game communications, and forces the client to refresh. If only the cell entity has been destroyed, then the client's knowledge of its proxy is retained.

If giveClientTo does not succeed because of a problem with the destination proxy, onGiveClientToFailure will be called on the origin proxy.

BaseApp managing bases and proxies

12.2. Witnesses

Whenever a proxy with an attached client has a corresponding cell entity, an extra object called witness is attached to the cell entity.

This object manages the entity's AoI, and sends updates to the proxy, which forwards them on to the client.

The updates consist of the bulk of game-related messages, such as:

  • Entity position updates.

  • Entity property updates.

  • Method calls.

  • Space data changes.

  • Notifications of entities entering and leaving the AoI.

12.3. Entity Control

By default, every cell entity is considered to be controlled by the server. When an entity incorporates a witness object, it is considered to be controlled by the client attached to the corresponding proxy.

However, the control of an entity may be explicitly assigned and queried, using the entity attribute .controlledBy. This attribute may be set to None, to indicate server control, or to a BaseEntityMailBox to indicate control by the client attached to that proxy. Within this context, control implies ownership of and responsibility for the entity's position and direction. Clients (and proxies) are informed of changes to the set of entities that they are allowed to control. Proxies may read this set through their attribute wards.

12.4. Physics Correction

When an entity is controlled by a client, setting the attribute Entity.topSpeed to a value greater than zero enables the physics checking. By default the topSpeed enables physics checking on all 3 axis, however this may not always be appropriate. For example, if the gravity of your game environment enables a Y-axis acceleration that results in a top speed exceeding the maximum allowable X/Z-axis top speed. In order to accommodate this there is a secondary attribute named Entity.topSpeedY which takes precedence when set to a value greater than zero. topSpeedY will only be used when both topSpeed and topSpeedY are greater than zero.

Entity movement is validated in the following ways:

  • Speed

    The first check is regarding the speed, to ensure that it does not exceed topSpeed and topSpeedY. There is a small amount of variance allowed in speed to account for up to 150ms of network jitter. Care is taken so that this latency debt is not exploited by allowing the player to travel faster than the top speed for a brief period of time.

  • Geometry of the scene

    The second check is made against the geometry of the scene, to ensure that the entity only leaves its current chunk through a well-defined portal.

    In spite of being very fast, this check does have consequences for level design. Barriers that control character mobility must be represented at the level of chunks. For example, a chunk with a wall across it, and a door giving access to the other side, is not protected by this physics checking system. In order to implement this in a manner to enable physics validation, two chunks should be used one on each side of the wall, with the door as a portal between them.

  • Custom physics validator

    If a custom physics validator has been developed, it will then be called between the top speed and the geometry checks. The custom physics validator is called with the following parameters:

    • Pointer to the entity.

    • Pointer to the vehicle (NULL if the entity is not on a vehicle).

    • The position to which the entity wants to move.

    • The time elapsed since the last physics check.

    The custom physics validator should return true if the entity is allowed to move to the new position, or false if it is not allowed.

    To install a custom physics validator, a CellApp extension module must be written. During the initialisation of the extension module, the global function pointer g_customPhysicsValidator (declared in bigworld/src/server/cellapp/entity.hpp) should be set to point to the custom physics validator function. For more details, see Extending BigWorld Server.

Other scenarios in which physics checking is applied include:

  • Control of multiple entities by the same client (such as wards).

  • Movement between vehicles (the ghost methods onPassengerAlightAttempt and onPassengerBoardAttempt are additionally called).

  • Movement to another space.

When a server script directly sets the position or direction of an entity that is controlled by a client, the CellApp treats that as a physics correction. A new position and direction (sometimes referred to as the pose) are forced down to the client, and no future position and direction updates are accepted until the correction is acknowledged. This feature can be very useful for teleporting a player in response to some action or event on the server. Notably, the methods Entity.teleport, Entity.boardVehicle and Entity.alightVehicle also adhere to this mechanism. Server-side teleports are the only way to move an entity between spaces.

While in most cases movement controllers would also result in downstream-forced positions, their use on client-controlled entities is neither recommended nor supported, since they continually set the position via a system intended for one-off adjustments.

12.4.1. Avoiding Y-axis rubber-banding.

Due to the manner in which physics validation occurs, if a top speed has been exceeded, the server will force a position update to the client with the last known valid position. This however has the unfortunate side effect of producing an entity which doesn't fall if the top speed has been exceeded in the Y-axis. Setting the topSpeedY to be higher than topSpeed will help in this situation, but eventually, the Y-velocity will be greater than topSpeedY due to acceleration due to gravity when falling for long periods.

In order to produce a work around for this, it is recommended to write a custom physics validator while setting a large value for topSpeedY. The custom physics validator could then perform its own validation and update the entity position with a decreasing Y-position prior to returning false, and the updated position would then be forced to the client.