It's free to join Gamasutra!|Have a question? Want to know who runs this site? Here you go.|Targeting the game development market with your product or service? Get info on advertising here.||For altering your contact information or changing email subscription preferences.
Registered members can log in here.Back to the home page.    

Search articles, jobs, buyers guide, and more.

by Rick Lambright
Gamasutra
[Author's Bio]
September 16, 2002

Net Profit, Net Loss

Object Views

Using Object Views

Printer Friendly Version

[Back To] Online Games Resource Guide

Sponsored by:

This feature originally appeared in the March 200 issue of Game Developer magazine.

 


Resource Guide

Distributing Object State for Networked Games Using Object Views

Object Views

An object view is an instance of a custom class that knows how to access one or more components of a game object and track any changes to those components. Every object view is attached to a game object, and every object view also has a remote counterpart that is attached to a game object with a similar set of components. As changes occur to the states of the tracked components, the object view is responsible for communicating those changes to its remote counterpart. The counterpart is then responsible for applying those changes to the game object to which it is attached.

The distributed-object system itself is designed to interact with object views, not game objects. How the object view interacts with each game object is strictly a contract between the object view and the game object. The distributed-object system only distributes object views. To access the gameobject components (given a reference to a game object) efficiently at run time, each object view instance is created with full knowledge of which components of the game object it needs to track and how to access them. Hence, implicit in the nature of the object view is the notion of a binding to the gameobject components that the object view will track.

The abstraction from the game object that the object view provides to the distributed object system is one of its most significant benefits. An object view and its counterpart can each be bound to a different type of object and still communicate with each other for managing state distribution. This eliminates the requirement to use identical objects on both the client and the server. For us, this was an important design consideration, since our clientside objects differ significantly from their server side counterparts.

Object view operations. Figure 3 shows how object views interact with game objects and the distributed object system at a high level. Note that there is a onetomany relationship of object views to game objects on the server, and a onetoone relationship on the client. In client/server architectures, servers maintain connections to many clients, but the client typically has only one connection to a server. The object view functions as a local proxy that remembers the state of each game object’s distributed components from the last time it was distributed to a particular client. Since state distribution will only occur when game objects are relevant to a client, the state of each object view is potentially unique.


Figure 3. Object views in action (client/server).

When an object enters the relevant set for a client, the Distributed-object system first locates the clientspecific object views for that game object, creating a new one if one does not already exist. Newly created object views on the server represent objects that will need to be created and fully initialized on the client before they can be rendered.

Either way, the process of determining exactly what state updates are needed and how the determination is made is strictly a contract between the object view and the game object. In order to ensure that the object view is granted the flexibility it needs, the Distributed-object system requires every object view to provide two basic operations: packto and unpackfrom.

The packto operation is called when the object view needs to be provided an opportunity to distribute its state. The object view determines whether or not any state updates are required, and is then responsible for marshaling those updates directly into the transmission buffer, packing them as tightly as possible in the process. Only the sending object view and its receiving counterpart on the other end of the connection can be trusted to understand the format of this data. The object view’s unpackfrom operation is called up when state updates are received. This is typically a simple process of analyzing the received data and applying the updates to the appropriate components of the target game object. This also turns out to be a great time for an object view to provide event notifications to the game object — or to anywhere else in the game — whenever one or more specific components are updated.

A third basic operation that each object view should provide is solid diagnostics. Object view operations are deliberately mysterious to the rest of the system components, and only the object views themselves may understand the format of the data they utilize to communicate state updates. Because of this, marshaling errors will have downstream effects that can be difficult to debug without good diagnostics.

Tracking state changes.When it comes time to distribute the state of the game object, each object view will need to determine whether the components it is tracking have changed since the last time the packto operation was called. This requires the object view to remember something about the previous state of those components. There are a variety of techniques that the object view can utilize to track state changes; invasive techniques require special support from the game objects, whereas game objects operate obliviously to noninvasive techniques.

The determination of which tracked components have changed state will normally take place during the packto operation, and while the game object remains relevant for a client, the packto operation for its views will be called frequently. For this reason, the packto operation must be very efficient.

The most straightforward technique is for the object view to maintain its own copy of the game object components that it is tracking. If sufficient memory is available and the tracked items can be compared very efficiently, this noninvasive mechanism is hard to beat. Since the exact previous state of each variable is always available, the object view can be certain that it is only distributing state that differs on the target.

Adding a change counter to the game object is an evasive technique we have found particularly useful. We use this for complex objects that are tested frequently but whose state changes relatively infrequently. Each object view also has a change counter, and each time the state is distributed the view’s counter is set to the current value of the game object’s counter. By comparing the two counters, a very fast check can be made to see if any new changes have occurred. This technique could be used as an optimization for any object that is tracking more than a few items, but it does require that each game object be modified to ensure that its change counter is updated every time any of the tracked components are updated. Another invasive technique that we have seen utilized involves maintaining a bit set of change flags. This technique requires that the game object be designed to manage a bit set that is stored with the game object itself. Each bit in the set corresponds to a distributed component part. The object view keeps its own copy of the bit set and checks to see if its own copy matches that of the game object during the packto operation, in order to determine which component parts have changed.

Unfortunately, this technique suffers from three drawbacks. First, you must ensure that the corresponding bit is set every time a distributed component variable is updated. Second, if a component switches back and forth between a small set of states, then there is a significant chance that a value marked as changed would be sent to the target object even though it actually switched back to being in the same state as the target. This process wastes bandwidth. The third drawback is the most serious. The “changed” component bits on each object need to be cleared as soon as possible for optimal distribution, but they can only be safely cleared when state has been distributed to all clients.

Because of that fact, this technique is really only practical for smallscale simulations where all clients need to be kept continuously up to date with the current state of all game objects.

Directionality. In client/server architectures we normally don’t distribute object state from clients to servers for game objects other than the playercharacter object. Having multiple clients send competing updates to the same game object on a server might seem like a very strange thing to do in your game, but it might make complete sense for others, especially in peertopeer architectures. Though we tend to be quite securityparanoid when developing MMP games, there are no hardandfast rules. If sufficient safeguards are in place, any client could manage state updates and distribute those updates to a server or to other clients.

Complex objects. Game objects are typically hierarchical in nature. Any gameobject component may itself be an object with its own component parts. This makes managing access to the items slightly more complicated than dealing with, for example, a set of items that are primitive types. You can generate or manually create custom object views for every game object and access each of the subcomponent items directly, one at a time. But if the same constituent objects are used as subcomponents in a wide variety of game objects, it is possible to implement your object views in a way that allows you to reuse a lot of code. To do this, you will need to create object views for the full range of component item types used by your game objects.

This includes all object types and primitive types. Once this is done, complex objects can be managed by creating hierarchical object views that mirror the component hierarchy of the object.

Lifespan of an object view. Over the course of time, a player will potentially encounter tens of thousands of objects in a large simulation. A server would need to maintain all the object views permanently for every game object if it wanted to avoid the expense of recreating them. This is memoryintensive not only for servers, but also potentially for clients as well.

Fortunately, this problem can be handled fairly effectively using an active cache of object views. Old object views are then automatically purged from the cache over time if the game objects they track are not reencountered for extended periods.

________________________________________________________

Using Object Views


join | contact us | advertise | write | my profile
news | features | companies | jobs | resumes | education | product guide | projects | store



Copyright © 2002 CMP Media LLC. All rights reserved.
privacy policy | terms of service