Project Scaffold Design Goals
From Rex community wiki
Overview
---
Project Scaffold was a core refactoring process initiated by Ryan McDougall, the lead architect of the Naali project during its first year. Since his departure, a refactoring process has been started with input from Ryan's research.
---
As has oft been mentioned in my presentations and discussions, the driving theme of realXtend development in my tenure has been to try to replicate the success of "Web Applications", running on Apache (et al) and Firefox (et al), in the "Virtual World" space, using OpenSim/Taiga and our own custom viewer. Naali represents our first attempt at making a "3D Firefox".
Naali is an excellent piece of software, but in my opinion fails the test of a coherent API capable of allowing easy "mash-up" style 3D applications. This project is an attempt to do better in this regard. The result of this project should be a prototype suitable for inclusion in Naali proper.
Design Concepts
Scaffold itself is divided into two parts: the Data Model, and the Framework.
The Data Model imposes a set of assumptions on range of expressible ideas in the API. However ideally the assumptions should match real use cases so that it is both flexible for new ideas, and concrete enough to be useful. The API requires the developer to fill in the Data Model as best as he is able, and doing so is sufficient guarantee that the program will run correctly without costly modification or extension.
The Framework provides a set of services that it is expected API developers will require in making their applications, such as requesting assets from the network, managing the lifetime of sessions, creating voice or video streams, making scene queries such as ray-casting, and polling for user input.
However, with only those two concepts, our design is quite an passive place. We need actors to impel our dynamic world.
New behavior is inserted into the framework through two methods: service plugins and client modules. Service plugins provide new implementations of services, and client modules use the full API to implement real applications.
Data Model
As the DOM tree is central in web apps, the "Scene Graph" of the 3D world will be central in Scaffold.
Entities
An entity is a very simple object that has a unique identity, and exists in some space.
In many ways it is similar to the traditional "object" in OO design, except reflection is a requirement, its naming is explicit, it is composed of "components" instead of fields, and carries no default behavior (non-trivial methods are decoupled).
- Scene entities will compose through aggregation of "components", rather than traditional inheritance of classes
- Scene entities will be reflective, anyone can discover the components and even fields of an entity
- Scene entities will be observable, raising events to subscribers when components are modified, indicating what was modified
- Any "module" is capable of creating component factories, and given an opportunity to decorate existing entities with custom components
Components
Components are mostly just structured data; however their existence implies a "type". For example if an entity contains a component that specifies drawables like a mesh or texture, then implicitly the entity is "Drawable", and can be drawn by any actor that accepts the drawable component as parameter.
Components enable decoupling of data from behavior, which is in opposition to traditional OO design wisdom (in fact it's a quite "functional" design). This allows from greater flexibility in composing your objects and applying behavior without rigid type hierarchies.
Event-based communication
With the scene graph at the center of our design, we choose to implement our event-like communication as publish/subscribe notification on the scene graph. An additional event mechanism might be possible, but I feel this will be redundant.
With "pub/sub" model, and given adequate reflection on the entity, "sending and event" becomes implicit; which is to say you just write to the component you want to change, and any listeners are immediately notified of your "message". I feel this will encourage developers to keep their world state in a shared and globally intelligible location, and increase the potential for making interesting mashups.
For example, one might create a "mouse entity", with components containing the button, position, and wheel state; if you want to know if the user pressed a button, just subscribe to that entity and your handler will be called every time there is a change in state.
Even if explicit event dispatch is preferred in some cases, the messages themselves can be implemented using the same mechanics in a transparent way.
Framework
Scene events let you react to changes in the world state, and notify others of your changes. However making requests of agents for which you are depending on a reply is awkward in this model. For this reason we add the concept of a "service".
Services
Services contain 3 essential parts:
- Manager: usually a singleton that provides an exported "service-oriented" API, and delegates incoming "requests" to "provider" implementations
- Provider: an implementation of an interface required by the Manager to retire the request, these are pluggable at run-time
- Future: the reply issued for a request which represents either the desired result, or a means to be notified when the result is ready
The manager and its interfaces are defined by Scaffold and only changeable at compile-time. However implementations of its interfaces, created by independent module writers, can be loaded at any time. This allows the framework to be decoupled from any given implementation. Scaffold will ship with default implementations, but the configuration of these providers will be fully configurable.
Managers pass the request to each provider round-robin or according to a priority until a provider accepts responsibility for fulfilling the request. It then passes on a "future" to the caller, which can be redeemed for the actual result at some later point. Recipients can either poll the future with a flag, install a notification callback, or block their thread pending the result.
Modules
There are two types of "module" in Scaffold:
- Provider plugins. These provide back-end service implementations that extend the capabilities of the viewer, which are registered with the service manager.
- Client modules. These are open-ended systems that access the full API in order to realize novel and interesting functionality, such as AI bots, client-side physics, secure financial transaction engines, etc.
Logic
To have a working application it must ship with at least one client module, a "logic" module.
The job of the logic module is:
- establish a session with a remote server
- listen for incoming protocol-specific messages from the server
- build a state machine to manage session state
- build a scene graph of entities to replicate world state
- subscribe to local entities and reflect world state changes to the server
User Interface
The user interface is implemented as a provider plugin to a "UI" service.
The job of the UI plugin is:
- notify framework of requests to alter remote sessions (login/out)
- provide interface for hand-off of externally created rendering window
- provide interface for adding menu widgets
- provide interface for adding settings widgets
- provide interface for user notifications
- complete management (layout, drawing, input) of the UI
Asset Cache
The asset cache is implemented as a provider plugin to a "Asset" service.
The job of the Asset plugin is:
- accept request tokens that are potentially network protocol specific
- negotiate the correct address and format for downloading
- arrange for a network transport
- download and cache the result, updating on expiry
- convert transport format to native format where necessary
- return a future to the caller that provides partial results where possible (such as JPEG2K)
Rendering
The rendering engine is implemented as a provider to a "Render" service.
The job of a Render plugin is:
- create a render window for the UI service
- subscribe to the Data Model's scene graph and update its native render graph from the source once per frame
- provide interface for ray-casting and selection within the scene
Scripting
Scripting, such as Python or Javascript, can interact with the API in the following ways:
- As client modules; the Data Model and Framework must be wrapped and exposed to the language of choice, and a module created that loads the script's interpreter.
- As downloadable assets; the script's interpreter must be running as above, and the script must have proper authorization.
Downloadable scripts is a huge security issue, and beyond the scope of Scaffold at the moment.
Multi-threading
I believe that multi-threading is an essential consideration in modern software. However given the scope of this project, it is unlikely to be implemented immediately.
What is more important is I believe that both the Data Model and Framework can be made thread-safe without alteration to the fundamental design.
|