Responsible for data presentation and interaction with the user. This is accomplished by connecting to the server and reacting to the received messages.
If there is no object id specified, className is treated as a Command, but this does not apply to the client. Each message is terminated with ; and line-feed character.
Note: Message structure is about to change. Both client and server should keep track of request number since the session began. Responses should be in the format:
Anything containing url field - the client is responsible for loading and interpreting the url. Current client implementation caches all messages for object until the url loads. It also removes objects from the scene when it receives the Remove message, but this is not mandatory. It just specfies that the server will not forward any changes to that object. Also, Add message will be followed by all the object fields and their values.
Note: url field structure is about to change. It should not specify only one url, but a list. Then, client should try to load the first url, and if it does not succeed, the second, and so on. Suggested format: [ "url1", "url2", ..., "urlN" ]. And Message(line) constructor should parse url's too.
The Client connects using org.vrspace.util.Connection
class. This class should incorporate methods for reading/writing raw bytes
and compressed data, thus replacing
BinaryConnection and
GZipConnection classes.
The current client implementation is still pure Code-Fu and very experimental,
so let's leave the details out for now. Eventually, have a look at
org.vrspace.util package, as it is common to both client and server.
Handles user authentication, maintains the scene, distributes events to client, maintains permanent storage. In short, it does everything but presentation.
When starting, the main server class, imaginatively called Server, looks up the configuration, connects to the database (btw this DB interface really needs getAll( String className ) method), and starts listening on a network socket. It also start some daemons (services). When a client connects, it starts a new Session. Session sends "login;" and reads response, than sends "password;" and reads the response. You can start org.vrspace.util.Console class to get the feeling of sessions and the protocol. Then, it asks the Dispatcher for a Client with this login and user name. If Dispatcher returns the client, the sessions begins.
This Dispatcher class is important. It is responsible for client login and logout, data retrieval from the database, and dispatching of events - all the main server functionality. Dispatcher.login looks for an AuthInfo object having the appropriate login and password. If it doesn't fiend the user, it creates a new AuthInfo, and a new client object of class specified by vrspace.user.class property.
Authentication fails if:
Daemon logins and sessions are intended for file transfer and other session types which are not interested in the state of the world. When authentication passes, Dispatcher constructs a new Scene for the client. (if daemon is true, Scene is not constructed). Scene looks in the database, adding interesting object to the client's view (by calling Client.addObject( VRObject )), and removing objects of no interest from the Client's view. This implementation uses client's current coordinates within the space to determine which objects are of interest. However, there should be more scene types, and Client will have a sceneClass field.
Now suppose a Client and a Session are up and running. When Session receives a request from Client, it calls the Client.request(String) method. (this is wrong and should be replaced by Client.request( Request ), but client looks up the scene for the originating object. Client constructs new Request , and looks up the scene for the destination object. Finding it or not, it calls Dispatcher.update( Client, Request ) method. Dispatcher.update executes a command or sets the field in the destination object. It also looks up the object if the client could not find it in the scene.
Where's the event forwarding? Dispatcher uses VRObject. sendEvent( Request ) to set the field value with the reflection api, and calls VRObject.setValue( Request ), which notifies Observers about the change. Client is an Observer and observers all objects in the scene. Look at java.util.Observable and java.util.Observer for details.
The basic object type is VRObject . Its subclasses can be stored to the database (look at TextDB for an implementation example), and can override the setValue() method to notify Observers of changes. However, for performance reasons, changes to the object are not stored to the database. Another important thing about VRObject: it can receive and forward events even if destination field/method does not exist to allow changes on client-side design without the need to change server-side class. Thus VRObjects are treated mostly as transient objects.DBObjects reflects changes to the database immediately, and requires the destination field/method to exist.
VRObject - Generic VRObject class. It extends Observable interface and notifies observers of any state changes. All changes are encapsulated inside Request object. Objects of subclasses of this class can't store state changes to DB and can't change its fields values, just exist in memory while Server is up and store those changes at Checkpoint time or when admin shuts down the Server.
DBObject - This class defines methods for database storage. Objects of subclasses of this class can be stored in underlying DB and set their fields' values. When processing a Request, objects first store changes in DB and then distribute Request.
There are 4 main types of VRObject and DBObject:
Type Of Object |
Send |
Receive |
Description |
Example |
Passive |
Can't send events |
Can't receive events |
This class is used for objects within the world that cannot be changed |
landscape |
Public |
send events to anybody |
receive events from anybody |
General public object |
unlocked door |
Private |
does not send events |
receive events from anybody |
This class is used to trigger some events within the world, to collect statistics, etc. |
Camera (Logger) |
Owned |
send events to anybody |
receives events from owners |
Check ownership before processing |
James, ALICE |
Clients are observers, and monitor VRObjects state changes. Ownership is implemented by implementing Owner and Owned interfaces. Objects can be the owner and the owned at the same time, but ownership is not delegated: if object O1 is owner of object O2, and object O2 is owner of object O3, object O1 is not owner of object O3.
Classes
that implement Owner interface:
Classes
that implement Owned interface:
Of course, there can be other object types, these are intended to be used by subclasses. See a diagram of the VRObjects class hierarchy here.