 |
Home | Changes | Index | Search | Go
Article: Project Wonderland Software Architecture (v0.3, v0.4)
by Jordan Slott (jslott@dev.java.net)
1. Introduction
Project Wonderland is a software platform for the creation of 3D virtual
worlds, primarily enabling geographically distributed participants to
collaborate with one another. This document describes the software
architecture of Project Wonderland. The primary audience of this document
are software developers wishing to extend or modify Project Wonderland.
Project Wonderland itself is in an early stage of release, and one can
expect that the software architecture, as well as this architecture
document will change significantly over the course of time.
This architecture document applies to versions 0.3 and 0.4 of Project
Wonderland.
2. Main Components: Client-Server Architecture
Project Wonderland is a Java-based, client-server software platform. The
server software is based upon Project Darkstar, a multi-player "gaming"
technology for the Java SE platform. (The word gaming is placed in quotes
because although Project Darkstar is aimed towards the gaming world, it has
been employed in other contexts, namely, herein which is not strictly a
game).
Project Darkstar manages a collection of objects (called ManagedObjects) and
provides APIs for the synchronized update of the state of these objects in
response to events from multiple clients. Updates to ManagedObjects are
themselves transactional -- either a coherent set of state updates happen
across a set of objects or they do not happen at all. All items within a
game or virtual world are represented by ManagedObjects on the server, for
example, rooms, players, tools, weapons, or any other virtualized real-
world object that exists in the world. Details on Project Darkstar can
be found at http://www.projectdarkstar.com.
The client is a desktop Java application and is based upon two technologies
to render the 3D world: Java 3D and Looking Glass 3D (LG3D). Java 3D, a standard
extension to Java SE, renders sets of 3D objects to the screen, making
use of hardware acceleration if possible. Details on Java 3D can be found
at https://java3d.dev.java.net/. Project Looking Glass provides a 3D
desktop environment. It provides a 3D Windowing environment and a set of
API to build 3D applications. Details on Project Looking Glass
can be found at https://lg3d-core.dev.java.net/.
3. Virtual 3D World
A virtual world is composed of a collection of "cells", each of which
represents a 3D volume in the world. A world has a single, right-handed, 3D
cartesian coordinate system (hereafter referred to as "world coordinates").
From the viewer's perspective, the +X axis is towards the right, the +Y
axis is gravitationally up, and the +Z axis is towards the user. This
coordinate system is the same as defined by Java 3D. Cells may be
scenery or objects in the virtual world. Persons, called Avatars, are
also represented by cells.
Each cell has a position and bounds inside the virtual world. A cell's
origin is given in world coordinates, corresponding to the center of the
cell. A cell also has "cell coordinates", where the cell's center is at
(0, 0, 0).
A cell may contain zero of more other cells, and hence there exists the
notion of a "parent" and "children" for each cell -- a single "master" cell
represents the entire world and contains all other cells. A cell may have
multiple parents, if it spans the physical bounds of more than one parent cell.
Cells, therefore, may overlap with one cell and do not need to be entirely
contained within a single cell. (This sounds like it means that the bounds of a cell may overlap the bounds of another cell? Bernard.)
4. Server-side architecture
4.1 Darkstar Boot Class and Data Management
Project Darkstar requires a boot-strapping class to initialize the "game", (which
replaces the main() method). The org.jdesktop.lg3d.wonderland.darkstar.server.WonderlandBoot class serves this purpose
by implementing the Darkstar AppListener interface. The implementation of its initialize() method performs some very basic tasks
which kick-starts the creation of the Wonderland world. They are:
- Create an instance of the
org.jdesktop.lg3d.wonderland.darkstar.server.UserManager class which maintains the list of users (i.e. clients) which connect to the server.
- Create an instance of the
org.jdesktop.lg3d.wonderland.darkstar.server.cell.MasterCellCacheGLO class which maintains the list of classes which maintains the cache of cells visible to a user. (Not really sure what this means Bernard.)
- Create an instance of the
org.jdesktop.lg3d.wonderland.darkstar.server.ChecksumManagerGLO class which manages a series of checksums for classes representing assets in the world.
- Open a communication channel named
ChannelInfo.USER_CHANGE which communicates changes in the client's status.
When a new client opens a connection to the Wonderland server, the
WonderlandBoot.loggedIn() method is invoked, which simply creates a new
instance of the org.jdesktop.lg3d.wonderland.darkstar.server.WonderlandSessionListener class. It is this class which
handles future changes in the state of the client, and in particular
creates the avatar when a user logs in (see WonderlandSessionListener.processAvatarSetupMessage()).
4.2 Cell architecture
Project Wonderland employs the Project Darkstar gaming platform on the
server-side, where each cell is represented by a managed object. The base
abstract class for all cell managed objects is the org.jdesktop.lg3d.wonderland.darkstar.server.cell.CellGLO class. (The
suffix "GLO" is a hold-over from an earlier version of Project Darkstar
where managed objects were called "Game Logic Objects" (or GLOs)).
There are two types of cells in Wonderland: stationary cells and moveable
cells. Stationary cells represent regions which do not move, while moveable
cells represent graphics which do move. Avatars are the best (and perhaps
only) example of a moveable cell. All stationary cell classes extend
org.jdesktop.lg3d.wonderland.darkstar.server.cell.StationaryCellGLO and all moveable cell classes extend org.jdesktop.lg3d.wonderland.darkstar.server.cell.MoveableCellGLO.
Various classes are used to define stationary cells within the Wonderland architecture.
The most basic is the "world root" cell (see org.jdesktop.lg3d.wonderland.darkstar.server.cell.WorldRootCellGLO class) which
is the root of all cells in the world and is also responsible for creating the
world. At present time, the WorldRootCellGLO creates its structure of cells using
hard-coded lines of code. (See for example, WorldRootCellGLO.buildFullWorld().) In the future, a more flexible architecture to
build worlds is planned.
Currently, the following stationary classes exist to represent the following (in package org.jdesktop.lg3d.wonderland.darkstar.server.cell):
simple terrain (SimpleTerrainCellGLO), slide show (SlideShowCellGLO),
model viewer (ModelViewerCellGLO), animated (AnimatedCellGLO), audio (AudioCellGLO), and shared application (SharedApp2DCellGLO). The only moveable
cell that is exists is the avatar cell (AvatarCellGLO).
- The simple terrain cell is used to define basic surroundings in the world, taking the name of an external file which defines the geometry of the surroundings.
- A slide show cell is used to define a series of images which will be shown in sequence, taking a file name pattern, whose wild-card character ('%') will be expanded into a frame number. Images in the sequence will be advanced via user input.
- A model viewer cell needs further study. XXX
- An animated cell represents a series of images which are animated in the world, taking an array of file names composing the animation.
- An audio cell, an extension of the animated cell, represents audio playing in the world eminating from a certain origin.
- A shared application cell represents an X Windows-based application which is being displayed in the world and can be interacted with by avatars.
4.3 Features of a Cell
All cell classes extend the CellGLO class which provides most of the services
necessary for cells. Each cell has a unique ID, generated by a central
facility (see org.jdesktop.lg3d.wonderland.darkstar.server.CellIDGenerator) and also a unique name formed as "CELL_"
plus ID. (see CellGLO.getCellID() and CellGLO.getGLOName() methods). A cell's
ID is simply a number encapsulated by an instance of the org.jdesktop.lg3d.wonderland.darkstar.common.CellID class.
4.3.1 Cell origin and bounds
Each cell's position within the world is defined by its origin, as specified
by a 4x4 matrix (see Java 3D's Matrix4d class; the Matrix4d class is used
in Java 3D to define general transformations, such as rotations, scalings,
translations, and shearings. There seems to be no fundamental limitation
why a cell's origin cannot be any combination of these transformations,
however, translations and rotations seem to be the only types used in
practice). A cell's origin is stored by the 'cellOrigin' member variable
and is set directly by all of the cell subclasses. The CellGLO.getOrigin() method
returns the origin as the cell in the form of an instance of class Matrix4d.
A cell also has a bounds, represented by the Java 3D Bounds class which
describes its physical extent. The subclasses of CellGLO manage the cell's
bounds by overriding the CellGLO.getBounds() method.
4.3.2 Cell parents and children
A cell's set of zero or more children and parents are stored in hashed sets.
Adding and removing a parent cell is done via the CellGLO.addParentCell() and
CellGLO.removeParentCell() methods, respectively. The CellGLO.getParentCellIDs() method
returns an array of instances of class CellID, each of which identifies a parent cell. The
methods CellGLO.addChildCell() and CellGLO.removeChildCell() add and remove
children to and from the cell, respectively. The CellGLO.getChildren() returns
a collection of children of the cell.
At any time, some of a cell's children may be invisible, which is conditional
upon some visible bounds. The CellGLO.getVisibleCells() method returns a
collection of children cells that are visible given a certain bounds. Typically
the visible bounds depends upon the viewing frustum of the avatar.
4.3.3 Inter-client communication via a cell
Cells also provide a communication mechanism between clients, using the
Project Darkstar channel mechanism. Each cell can have a single communication
channel, identified by a unique name and created via the CellGLO.openCellChannel() method.
The CellGLO.getCellChannel() method returns the cell's channel. Clients are added as
listeners to the channel via the CellGLO.addUserToCellChannel() method and removed
via the CellGLO.removeUserFromCellChannel() method. Each of these two methods
takes the unique id of the client session as argument. See Section 6 below
regarding details of the client-server communication architecture in Project
Wonderland.
4.3.4 Client-side cell classes and setup
Each CellGLO class on the server has a corresponding Cell class on the client
which is responsible for rendering the cell. The name of the corresponding
client cell class is defined by each of the individual server-side cell subclasses via
the CellGLO.getClientCellClassName() method.
A server-side CellGLO communicates its visual contents and other setup data
via an instance of a class that implements the org.jdesktop.lg3d.wonderland.darkstar.common.CellSetup interface.
This is returned from the CellGLO.getSetupData() method that
is implemented by each of the individual server-side CellGLO subclasses.
The CellSetup interface itself is simply an empty interface which extends the
java.io.Serializable interface so that it may be sent across the wire. Each server-side
CellGLO class defines its own "setup" class that implements the CellSetup interface. Examples (in package org.jdesktop.lg3d.wonderland.darkstar.common)
include AnimatedCellSetup, AvatarCellSetup, ModelViewerCellSetup,
SharedApp2DCellSetup, SimpleCellSetup, and SlideShowCellSetup. The
SimpleCellSetup class is used, for example, by the simple terrain cell and
has methods which return the name of disk files storing the scene.
4.4 Server-side Cell Caching
The set of Cells which should be currently kept in memory by the client is
managed by the server via an instance of org.jdesktop.lg3d.wonderland.darkstar.server.cell.UserCellCacheGLO class, an instance of this
class exists for each user (which is equivalent to saying "client") and
is created when the user logs in. All instances of UserCellCacheGLO are managed
by the instance of MasterCellCacheGLO class that was created by WonderlandBoot (see section 4.1). They are managed objects within the Project
Darkstar infrastructure.
The server-side cell caches support several state changes to CellGLOs They are:
cell created, root cell created, cell moved, cell deleted, and add parent to cell.
At the present time, the UserCellCacheGLO.revalidate() method considers those
CellGLOs which are currently visible and adds those CellGLOs which are new and deletes
from the client's memory those which are no longer visible. Changes in the
state of CellGLOs are communicated via a dedicated channel (named CELL_CACHE) associated with the instance of
UserCellCacheGLO class. At present, the messages that are communicated on this channel are
serialized instances of the org.jdesktop.lg3d.wonderland.darkstar.common.messages.CellHierarchyMessage class.
The visible cells are determined by the position of the avatar.
Its visible domain is determined by an instance of class org.jdesktop.lg3d.wonderland.darkstar.common.AvatarBoundsManager via its
getProximityBounds() method. This method defines the visible area as a sphere of
a specified radius (AvatarBoundsManager.PROXIMITY_SIZE = 200.0) from the center
of the avatar.
5. Client-side Architecture
5.1 The Main class
The org.jdesktop.lg3d.wonderland.Main class, as its name implies, implements the static main() method to
start-up the client. It initializes its user interface and reads in any
mutable configuration options from an external XML-formatted configuration
file.
The initialization of the connection with the server happens via it inner class named
StartupListener, which implements the Lg3dStartupListener interface, from
Project Looking Glass -- this listener's startupComplete() method is invoked
when the initialization of the graphics is complete. In turn, the implementation
of the startupComplete() method displays a login dialog (see class org.jdesktop.lg3d.wonderland.LoginDialog)
which accepts login information from the user. It initiates a connection to
the Wonderland server and invokes the method Main.enableMainWindow() to "turn on" the
Wonderland client.
All communication with the Darkstar-based Wonderland server is managed by an instance of class
org.jdesktop.lg3d.wonderland.darkstar.client.ChannelController. (This seems to be somewhat misleadingly named as it doesn't
control channels, instead it provides a direct client-server communication mechanism.) A new connection to the server is initiated via the
ChannelController.initCommunications() method. See Section 6 below for details
of class ChannelController.
5.2 Wonderland Universe
The "universe", as created by an instance of class org.jdesktop.lg3d.wonderland.scenemanager.WonderlandUniverseFactory, creates the main
drawing canvas and initializes some basic viewing parameters. This instance is
created by an instance of class org.jdesktop.lg3d.wonderland.scenemanager.WonderlandPlatformConfig, which in turn, is specified by
the lg.platformConfigClass property.
5.3 Scene Manager
Project Wonderland defines its own scene manager, the equivalent of an X Windows
window manager in Project Looking Glass. Class org.jdesktop.lg3d.wonderland.scenemanager.WonderlandSceneManager extends
the LG3D SceneManagerBase class, an abstract class which implements the LG3D
SceneManager interface. The LG3D SceneManagerBase class handles communication with
the LG3D display server (the moral equivalent of the X Windows Server) via its
implementation of the LG3D interface DisplayServerManagerInterface.
The WonderlandSceneManager.initialize() method contains the bulk of the functionality
of this class. It creates an instance of class org.jdesktop.lg3d.wonderland.scenemanager.WorldController with an instance of the
Java 3D BranchGroup class to which the WorldController will attach all of the
visual content. It also adds the instance of the Java 3D BranchGroup class as a child
of the LG3D display server's root.
The instance of WonderlandSceneManager also creates an instance of an LG3D StandardAppContainer
class, using a null-style layout manager. The implementation of the
SceneManager.addFrame3D() and SceneManager.removeFrame3D() methods add and remove frames to
and from the instance of class StandardAppContainer. The usage of this is currently unclear.
The instance of class WonderlandSceneManger is installed according to the LG3D specification as
follows: the createSceneManager() method of org.jdesktop.lg3d.wonderland.scenemanager.WonderlandConfigControl returns an instance of
class WonderlandSceneManager. The Main class sets the "lg.configclass" property
to "org.jdesktop.lg3d.wonderland.scenemanager.WonderlandConfigControl". (Additional
configuration information for the scene manager is also handled by the
WonderlandConfigControl class, which reads an XML-formatted file of configuration
options, by default: "/org/jdesktop/lg3d/wonderland/scenemanager/resources/wonderland.lgcfg".)
5.4 World Controller
The instance of class WorldController handles all of the visible content in the Wonderland world.
As class members, it maintains instances of two important client-side classes:
org.jdesktop.lg3d.wonderland.scenemanager.UserCellCache and ChannelController. The WorldController takes as an argument a Java 3D
BranchGroup to which it adds lighting: an ambient light of color RGB = {0.7, 0.7, 0.7},
and two directional lights. One directional light is white and is down-to-the-left. The
second directional light is RGB = {0.2, 0.2, 0.3} and is up-to-the-left. (TODO: What's
the effect of this lighting descriptively?)
Details of classes UserCellCache (section 5.4) and ChannelController (section 5.x) are found below.
5.5 Client-Side Cell Caching
Visible cells are cached and maintained by an instance of UserCellCache. The loading/unloading
of cells and changing their state is controlled entirely by the server through messages
send from the server to the client, formatted as instances of class CellHierarchyMessage.
The UserCellCache.handleMessage() method handles these server events, sent over
a communication channel maintained by the Project Darkstar infrastructure and handled
by an instance of class org.jdesktop.lg3d.wonderland.darkstar.client.UserCellCacheChannelListener.
A common operation, particular at client boot-time, is the request to create new cells on the client
side via a message of type CellHierarchyMessage.LOAD_CELL. Using the configuration
information embedded in the CellHiearchyMessage, an instance of the client-side cell
class is create and initialized. The new cell is added to the HashMap cache of cells,
and the thread (an instance of inner class CellUpdateThread within class UserCellCache) is signalled to update the set of visible
cells to render (the UserCellCache.visibleCells member variable).
The instance of class UserCellCache also manages the list of cells in which the avatar is currently positioned
via the UserCellCache.avatarInCells member variable (a HashSet). This set is updated when the
CellUpdateThread is signalled that an update has occurred. Cell enter and exit
events are generated--as represented by classes CellEnterEvent and CellExitEvent (in package org.jdesktop.lg3d.wonderland.scenemanager.events),
respectively--and posted.
Class UserCellCache currently only implements a subset of the cell states: VISIBLE
and INACTIVE. The following cell states are currently not handled: ACTIVE, DISK, and
BOUNDS.
Class UserCellCache also implements the org.jdesktop.lg3d.wonderland.scenemanager.UserMotionListener interface which receives
notification when the avatar moves (via the userMoved() method). The implementation of
the UserCellCache.userMoved() method is currently basic: it simply updates the status
of the visible cells and generates cell enter/exit messages using the updated position
of the avatar.
6. Client-Server Communication
This section outlines all of the communication channels and messages sent between the
Wonderland client and server and between clients.
Each client is represented on the server by a number of objects. The objects are:
-
UserGLO -- basic user information, like name and preferred color
-
AvatarCellGLO -- the cell where the user's avatar is rendered
-
UserCellCacheGLO -- the cells that are visible to a specific user
There is one of each of these objects per user/client. As the user moves around the world, the server tracks which cells that user "knows about".
When the user moves, it notifies the server and the server examines the UserCellCacheGLO to see if that client has already loaded all the cells in its immediate area.
If the server finds a cell the client hasn't seen yet, it sends it a message, telling the client to load the new cell.
6.1 Client-Server Direct Communication
When a client connects to the server, a direct client-server communication link is
automatically set up by Project Darkstar.
The server transmits the following messages to a client over this mechanism
- An initial message giving the version of the communication protocol (in
WonderlandBoot.loggedIn()).
- Potential error messages as instances of class
org.jdesktop.lg3d.wonderland.darkstar.common.messages.ErrorMessage (ditto, and also in WonderlandSessionListener.processFileTransferMessage().
- Instances of class
org.jdesktop.lg3d.wonderland.darkstar.common.messages.NativeApplicationMessage as messages in reply to a "create application cell" message (in WonderlandSessionListener.processNativeApplicationMessage()).
- Instances of class
org.jdesktop.lg3d.wonderland.darkstar.common.messages.SoftphoneMessage (in the callStatusChanged() and processSoftphoneMessage() methods of WonderlandSessionListener.
These are handled by a client as follows:
- The protocol version and error messages are handled by
ChannelController.handleMessage().
-
NativeApplicationMessage messages are handled by the receivedMessage() method of the inner class MessageListener of class org.jdesktop.lg3d.wonderland.appshare.ServerApp2DCell.
-
SoftphoneMessage messages are also handled by ChannelController.handleMessage().
A client transmits the following messages to the server over this mechanism:
- Instances of classes
NativeApplicationMessage. These are sent by the constructor of class ServerApp2DCell.and the sendMessage() method of ChannelController.
- Instances of
org.jdesktop.lg3d.wonderland.darkstar.common.messages.SoftphoneMessage. These are sent by the connectSoftphone() and disconnectSoftphone() methods of ChannelController.
- Instances of
org.jdesktop.lg3d.wonderland.darkstar.common.messages.AvatarSetupMessage initiated by the method loggedIn() in class ChannelController.
- instances of some sublcass of
org.jdesktop.lg3d.wonderland.darkstar.common.messages.CellMessage. Currently there appear to be none.
The server receives the following messages from the client over this mechanism:
- Instances of classes
NativeApplicationMessage. These are handled by the receivedMessage() method of org.jdesktop.lg3d.wonderland.darkstar.server.WonderlandBaseSessionListener, and subsequently by its processNativeApplicationMessage() method.
- Instances of
SoftphoneMessage. These are handled by the receivedMessage() method of WonderlandSessionListener.
- Instances of
org.jdesktop.lg3d.wonderland.darkstar.common.messages.AvatarSetupMessage messages . These are also handled by the receivedMessage() method of WonderlandSessionListener.
- Other instances of some sublcass of
org.jdesktop.lg3d.wonderland.darkstar.common.messages.CellMessage messages, dispatched to instances of class org.jdesktop.lg3d.wonderland.darkstar.server.CellMessageListener.
6.2 Publish/Subscribe Communication Channels
The Wonderland server also creates publish/subscribe communication channels, which
include: USER_CHANGE, CELL_CACHE, AVATAR_P2P, AVATAR_CELL, and
SERVER_MANAGER_CHANNEL. A channel provides a communications mechanism between clients and also between a client and its server.
6.2.1 The USER_CHANGE channel
The USER_CHANGE communication channel is created by WonderlandBoot in its initialize() method, and all new clients
are automatically added to it. It is used exclusively for server-->client communication.
The server transmits the following messages to the client over this channel:
- Instances of class
org.jdesktop.lg3d.wonderland.darkstar.common.messages.UserChangedMessage when users are added (USER_ADD, in method processAvatarSetupMessage() of class WonderlandSessionListener) or when users leave (USER_LEFT, in method disconnected() of class WonderlandSessionListener).
The server does not receive any messages from the client on this channel.
The client does not transmit any messages on this channel.
The client handles these messages via the receivedMessage() method of class org.jdesktop.lg3d.wonderland.darkstar.client.UserChannelListener.
6.2.2 CELL_CACHE channels
The server creates a CELL_CACHE channel for each client connected. The name of the
channel takes the form: user + CELL_CACHE, where 'user' is the name of the user. The CELL_CACHE
channel for a client is created by the instance of class UserCellCacheGLO that corresponds to that client (in its contructor). Each new client is automatically
added to one of these channels.
Each instance of class UserCellCacheGLO has a CELL_CACHE channel. Only one client/user (the user who's cache it is) is subscribed to that CELL_CACHE channel. The subscription is managed in the login() method of class UserCellCacheGLO. This channel is used exclusively for server-->client communication.
The server transmits the following messages to the client over this channel. The messages are sent by the instance of UserCellCacheGLO.
- Instances of class
CellHiearchyMessage messages, which can be of the kind: LOAD_CELL, MOVE_CELL, CELL_INACTIVE, ADD_PARENT, REMOVE_PARENT, SET_WORLD_ROOT, and DELETE_CELL.
The server does not receive any messages from the client on this channel.
The client does not transmit any messages on this channel.
The client handles these messages via the receivedMessage() method an instance of class org.jdesktop.lg3d.wonderland.darkstar.client.UserCellCacheChannelListener.
6.2.3 AVATAR_P2P channels
The server creates an AVATAR_P2P channel for each client connected. The name of the
channel takes the form: user + AVATAR_P2P, where 'user' is the name of the user.
This channel is created by an instance of class AvatarCellGLO during its constructor, and then maintained by that instance of AvatarCellGLO. Another client may request to join an client's P2P? channel via the AvatarCellMessage(JOIN_P2P) message sent on
the AVATAR_CELL channel (see below).
Each avatar sends its motion updates over its client's AVATAR_P2P channel, so any client that cares about those updates (generally any other client in visual range of the avatar) will subscribe to it.
The server transmits the following messages to a client over this channel:
- Instances of class
org.jdesktop.lg3d.wonderland.darkstar.common.messages.AvatarP2PMessage, of which there are two kinds:
- The
SETUP message is sent by the receivedMessage() method of class AvatarCellGLO in response to an AvatarCellMessage(JOIN_P2P) message.
- The
SPEAKING message is sent by the callStatusChanged() and processAvatarCellMessage() methods of class WonderlandSessionListener and the setSpeakingStatus() method of AvatarCellGLO.
A client handles messages via the method receivedMessage() of an instance of class org.jdesktop.lg3d.wonderland.darkstar.client.AvatarP2PChannelListener.
A client transmits instances of class AvatarP2PMessage over this channel, though only of kind MOVE and CHAT:
- The
MOVE message is sent by the userMoved() method of class org.jdesktop.lg3d.wonderland.scenemanager.avatar.Avatar, in response to user interaction.
- The
CHAT message is sent by the sendChatMessage() method of class Avatar.
The server currently receives messages on this channel via the handleMessage() method of an instance of class org.jdesktop.lg3d.wonderland.darkstar.serverAvatarP2PChannelListenerGLO . (The method is only used for logging purposes.)
Thus, this channel is used for inter-client communication (for AvatarP2PMessages of kind MOVE and CHAT), and for server-->client communication (for AvatarP2PMessages of kind SETUP and SPEAKING).
6.2.4 AVATAR_CELL channels
An AVATAR_CELL channel is created by an instance of class AvatarCellGLO during its constructor. The name of the
channel takes the form: user + AVATAR_CELL, where 'user' is the name of the user.
The server transmits the following messages to the client over this channel:
- An instance of class
AvatarCellMessage, which has one basic kind: SETUP. This is sent when a an avatar's cell (i.e. AvatarCellGLO) first becomes visible.
When a cell becomes visible, the server steps through a list of all CellGLOs and
adds the client to the =CellGLO='s channel if it (the channel) exists. Currently, only the avatar
CellGLO? creates a channel for itself. At that point, a SETUP message is sent to
the client, which keeps a reference to the channel for future communication.
The server receives the following messages from the client over this channel:
- Instances of
AvatarCellMessage. It handles these messages via method receivedMessage() in an instance of class AvatarCellGLO (for messages that are of kind CELL_MOVE, CELL_ENTER, and CELL_EXIT) and also via the receivedMessage() method of an instance of WonderlandSessionListener (for messages that are of kind AVATAR_MOVE, AVATAR_MUTE and JOIN_P2P.
The client transmits instances of class AvatarCellMessage over this channel (see class org.jdesktop.lg3d.wonderland.darkstar.client.cell.AvatarCell.)
The client receives messages over this channel. It handles them via the receivedMessage() method of an instance of class org.jdesktop.lg3d.wonderland.darkstar.client.cell.AvatarCellChannelListener.
6.2.5 The SERVER_MANAGER_CHANNEL channel
The SERVER_MANAGER_CHANNEL communication channel, created by an instance of class org.jdesktop.lg3d.wonderland.darkstar.server.ServerManagerGLO,
is used to communication server management information to the client. Currently, the server itself and a special client (hardcoded to name "ServerManager") are
added to this channel.
The server transmits and receives instances of class org.jdesktop.lg3d.wonderland.darkstar.common.messages.ServerManagerMessage to the client over this channel, of which there are four kinds: STATUS, FULL_STATUS, CHANGE_UPDATE_INTERVAL, and SET_USER_LIMIT.
The only client to the SERVER_MANAGER_CHANNEL communication channel is provided by an instance of class org.jdesktop.lg3d.wonderland.management.ManagerUI which can be opened using the ANT run-manager target.
6.3 Guidelines for Managing Change at Client and Server
There is a distinction between two kinds of user interaction:
- User interaction that only changes the state of the client, perhaps in terms of the appearance of a
Cell. An example would be when a user's cursor hovers over a widget (see, for example, the sample module)
- User interaction that causes a change of state of a
CellGLO on the server. An example would be when a user clicks on a widget (see the sample module for an example)
The two cases are described below. In each case it is assumed that four clients (C1, C2, C3 and C4) are connected to a server.
6.3.1 Change of state of the client
- User interaction with
Cell on client C2 causes local state change in Cell (no change in server state)
-
Cell updates appearance
-
Cell broadcasts message to other clients on Cell's channel; message identifies "kind of change" and new state of Cell
- Other clients receive message and their corresponding
Cell updates its appearance
6.3.2 Change of state of the server
- User interaction with
Cell on client C2 causes state change in CellGLO on server
-
Cell updates local state (if necessary) and appearance
- Client C2 send message direct to server. Message describes "kind of change" and new state of
Cell
- Server identifies appropriate
CellGLO
-
CellGLO updates its state
- Server broadcasts message on
Cell's channel to all clients except originating client. Message describes "kind of change" and new state.
- Each client receives message and updates state of
Cell (if necessary) and appearance.
The above flow of control differs from conventional Model-View-Controller (MVC) in the following features:
- There is no clear separation between the model and its view. A
CellGLO determines its Cell (which is equivalent to saying that the model determines its view). This means that it is not possible to use the same CellGLO class to be rendered by multiple Cell classes. For example, a CellGLO class representing a die could not have two Cell classes that provided different renderings of a die.
- There is no clear separation between a view and its controller. The controller for a view (following the Swing etiquette) is provided by an inner class in the view class. So, for example, a controller for a
Cell is often provided by an inner class of the Cell class.
- Because multiple clients render the same
Cell, it's necessary to synchronise between the clients. This requirement is outside MVC, and is handled in Wonderland by the use of messages to the channel owned by a Cell.
Finally, the flow of control in MVC differs sublty from that of Wonderland. For example, if Wonderland used MVC, the flow of control would be
- User interaction with
Cell on client C2 causes state change in CellGLO on server
- Client C2 send message direct to server. Message describes "kind of change" and new state of
CellGLO
- Server identifies appropriate
CellGLO
-
CellGLO updates its state
- Server broadcasts message on
Cell's channel to all clients. Message describes "kind of change".
- Each client receives message and requests the state of the
CellGLO
- Each client updates state of
Cell (if necessary) and appearance.
The reason for this difference is performance. It is important that the user is notified of a change of appearance as soon as possible (i.e. before notifying the server).
7. Summary of Important Code Flow Sequences
This section summarizes the flow of control in the code for two aspects of Project
Wonderland: when a client first connects and when an avatar moves or changes its
look direction.
7.1. Sequence of Code when a Client First Connects
- The
Main class creates the main window and adds an instance of the LG3D Lg3dConnector class to the main window. This, in turn, starts the LG3D display server. Upon initialization, the L3GD display server initializes the universe, windowing system, view, creates the root scene graph, and initializes the scene manager.
- The Wonderland Scene Manager creates a new instance of class
WorldController, which creates a new instances of the ChannelController and UserCellCache classes.
- Upon completion of the LG3D initialization, the
StartupListener.startupComplete() method is called which raises the Wonderland Login dialog.
- Upon user login confirmation, a connection to the Darkstar server is opened. The client waits for login confirmation, which is sent asynchronously via a call to
WonderlandClientListener.loggedIn().
- Upon login, the client sends an
AvatarSetupMessage message to the Darkstar server via its client-server communication mechanism with information about the avatar, such as its visual model.
- Upon the conclusion of login, the main window of the Wonderland client is enabled.
7.2 Sequence of Code upon Initialization of the Server
- The
WonderlandBoot.initialize() method creates new instances of classes ServerManagerGLO, UserManager, MasterCellCacheGLO, and ChecksumManagerGLO. A publish/subscribe channel is also created named USER_CHANGE.
- The
MasterCellCacheGLO instance creates a new instance of class WorldRootCellGLO. In turn this calls the WorldRootCellGLO.buildWorld() method which creates all of the cells in the world.
- An instance of class
WonderlandSessionListener receives an AvatarSetupMessage message from the client. It creates a new instance of class UserGLO.
- The
UserGLO instance creates a new AvatarCellGLO instance, which in turn creates a new UserCellCacheGLO instance.
- The
UserCellCacheGLO instance creates a publish/subscribe channel named user+CELL_CACHE, where 'user' is the name of the user.
- The
AvatarCelLGLO instance then opens a publish/subscribe channel named AVATAR_P2P and a channel named user+AVATAR_TILE, where 'user' is the name of the user.
- The
WonderlandSessionListener instance then joins the client to the USER_CHANGE channel and sends a UserChangedMessage(USER_ADDED) message to the client. It then calls the login() method on the UserGLO instance.
- The
UserGLO.login() method adds a reference of the user to the hash map relating user ID's to user object references. It calls AvatarCellGLO.login().
- The
AvatarCellGLO.login() adds the receiving instance of AvatarCellGLO to the MasterCellCacheGLO instance. It then calls UserCellCacheGLO.login() which joins the client to the user+CELL_CACHE channel, adds itself (the UserCellCacheGLO) to the MasterCellCacheGLO instance, and sends a CellHierarchyMessage(LOAD_CELL) message to the channel named "user+CELL_CACHE", and then sends a CellHiearchyMessage(SET_WORLD_ROOT) message to the channel named "user+CELL_CACHE".
- The
UserGLO.login() method then calls the UserCellCacheGLO.avatarCellMoved() method, which computes the list of visible cells and sends CellHierarchyMessage(LOAD_CELL) messages for all visible cells on the channel named "user+CELL_CACHE". It also sends CellHierarchyMessage(DELETE_CELL or CELL_INACTIVE) messages for all newly non- visible cells on the channel named "user+CELL_CACHE".
- The
WonderlandSessionListener finally sends UserChangedMessage messages to each of the clients via the USER_CHANGE channel to notify them of the new client.
7.2 Sequence of Code when an Avatar Moves
In this code sequence we consider when the user presses one of the cursor-control keys to move
the position of the avatar.
- The key event is handled by the
keyPressed() method of class org.jdesktop.lg3d.wonderland.scenemanager.WalkBehavior. The state of the instance of the inner class Stimulus is updated to reflect the key state change.
- The method
scheduleUpdate() of the instance of class org.jdesktop.lg3d.wonderland.scenemanager.AvatarControlBehavior is called which tells it that pending updates to the avatar state exists.
- The
AvatarControlBehavior.processStimulus() method wakes up and calls the WalkBehavior.updateState() method which updates the position state of the avatar.
- Collision detection is performed based upon the new position of the Avatar. The position of the camera is then updated.
- All implementations of interface
UserMotionListener are notified of the new position of the avatar via the method userMoved(). One example of an implementation of UserMotionListener is class UserCellCache, whose userMoved() method calls the UserCellCache.updateCells() method. (Another example implementation of the interface is class AvatarCell).
- The
updateCells() method computes the visible cells based upon the user's viewing frustum. It updates the state of each cell and the list of visible cells. For all visible cells, it updates the list of cells the avatar is currently standing in. It causes events of type CellEnterEvent and CellExitEvent to be sent to the appropriate cells (within the LG3D event framework). (At this time the AvatarCellMessage types CELL_ENTER and CELL_EXIT are not sent to the server, nor does the server take any action upon receiving these events).
- The
AvatarCell.userMoved() method sends an AvatarCellMessage(AVATAR_MOVE & CELLMOVE) to the server via the AVATAR_TILE channel. The position of the avatar is updated on the server and the UserCellCacheGLO is asked to revalidate itself (which computes the list of visible and invisible cells, updates its state, and informs the client of such).
8. Future Documentation Work
This software architecture document details just the basics of the Wonderland client-server
software. Noticeably absent is documentation on avatar's and their configuration and control
behavior, application sharing, and audio.
|