Chapter: Implementation
prev | next | index |
![]() |
The Jet Virtual Medium
by Radu Sion |
In this chapter we are entering into more
details in the area of the real implementation of the system as a whole,
and it's particular elements of main interest to the end-user. Plugins,
different system perspectives concerning implementation, system resource
handling, authentication etc. are elements that we're trying
to cover here. The complete class documentation and other implementation
details are available in the Source Docs, Listings, Online
Links chapter.
The open design is supported by the concept
of loadable classes, plugins. The communication handler plugins
allow for an easy changeable configuration by using potential alternate
custom written modules. Watchdogs are designed to offer a full system
control at the server side, for maintenance tasks like topology startup,
security breach detection etc. Providing the client as an interface
(jvm.client.Client) and using only interface callbacks inside the
system allows for a more sophisticated structure with also other potential
server types and protocols accessed via the same client interface. AUTH
Handlers implement an easy to use approach to authentication and related
issues, allowing for complex custom written AUTH protocols to be implemented
via plugin modules.
As described above in the Virtual Medium chapter, a watchdog is actually a simple piece of code implementing a certain interface (jvm.server.Watchdog) and that is able to run inside the current server, having unrestricted access to all server resources and areas. It is explicitly specified in the main server resource file and is loaded at system startup, after every task for full system run is completed.
There are two types of watchdogs, the required system watchdog and an optional user watchdog. The difference between them is that the system won't start if its unable to load the system-watchdog and that there is a certain system watchdog provided for each communication handler in place. The system watchdog is intended mainly for server maintenance tasks, overall security maintenance etc. The user watchdog is intended for end-user purposes and may be missing. A single user-watchdog may be active inside a server at a moment in time.
Watchdogs should be written and used with care as they may practically compromise all server areas. In most of the cases you should use the provided corresponding system watchdog.
Provided watchdogs include jvm.server.NullWatchdog and jvm.server.DistributedMaintenanceWatchdog, which is to be used only as a system watchdog. In order to determine which provided watchdog corresponds to which provided communication case, please see the Communication Handlers section.
The jvm.server.Watchdog interface
defines all the needed methods that a given class has to implement in order
to be able to integrate as a watchdog inside the system, either as system
watchdog or as user watchdog. To write a custom watchdog please look at
the interface docs and write a class that implements the watchdog interface.
Do not replace the recommended system watchdog for the current communication
handler, unless you're absolutely sure what you're doing or you may compromise
the full system coherency as the system watchdog sometimes performs communication
related tasks as maintaining the distributed medium server network topology.
In order to perform server-side tasks via watchdogs use the user watchdog
facility instead.
Communication Handlers are the actual core of the communication system, implementing the whole algorithm including message handling, server distributivity etc. Being changeable, they offer thus a powerful way of changing transparently the whole communication scheme, algorithm and policy. Only a single communication handler may be active in a given server at a moment in time. The handler gets loaded at server startup.
The jvm.server.CommunicationHandler interface defines the callbacks that are used inside the system, integrating a given implementation in the system as a communication handler.
Writing a custom communication handler is not an easy task. It implies a careful planning of the whole system communication algorithm and also the compatibility issues that may arise in the case of interconnecting your own communication servers with other servers running with other type of communication handlers and algorithms.
The system comes with two provided communication handlers. The distributed multi-server communication handler plugin (see jvm.server.DistributedCommunicationHandler) allows for a distributed network topology composed of many servers communicating with each other in a dynamical manner, building up a potential global-wide medium of entities far away of each other able to use together the provided medium facilities.
A single-server implementation of the medium is provided through the distributed communication handler plugin running in single-server mode or through the old and buggy local distributed communication handler plugin, which is not supported anymore. (see jvm.server.DistributedCommunicationHandler and jvm.server.LocalCommunicationHandler) Many issues that appear in the distributed multi-server case like timestamp maintenance, message forwarding, distributed grouping etc. don't appear here.
As mentioned earlier in the Watchdogs
section, each communication approach has a corresponding
needed system watchdog that has to be active for the system to
run properly. The system won't start if it is unable to load the system
watchdog but it doesn't verify if it is the needed watchdog for the current
communication case. For the distributed multi-server case
the active system watchdog should be the provided distributed
maintenance watchdog (see jvm.server.DistributedMaintenanceWatchdog) which
takes care of maintaining the distributed system network topology. In the
single-server case only the null watchdog (see jvm.server.NullWatchdog)
is required in order for the system to start.The distributed maintenance
watchdog should be compatible also with the single-server communication
case if set up properly.
Providing the client interface
(see jvm.client.Client) allows you to write apps that use
this interface and that benefit from further developments and future protocols,
without having to rewrite your application code. The interface defines
ways we might make use of a generic Message (see jvm.message.Message)
passing, Entity (see jvm.entity.Entity) based communication system.
AUTH Handlers offer ways of implementing new complex methods of authentication and authorization at client connection time. The main idea is to separate this from the system in order to be able to keep up with the latest technologies in this very sensitive area. A AUTH Handler is basically a plugin (server and client side) which takes care of the prelogin negotiation for entering the system.
In this frame the concept of a AUTH level is introduced. This is a long integer number representing some sort of credit that this client gets from the server-side AUTH Handler, after performing a successful AUTH negotiation. If it is positive or zero the client is allowed to enter the system by also having assigned the given AUTH level inside the server.
There are two types of AUTH Handlers involved
in the AUTH process, the server-side and the client-side
handler. Each server-side handler basically has a corresponding required
client-side handler that knows how to negotiate with it. The current design
allows for a client-side autodetect and load of the current
required client-side AUTH plugin that is able to negotiate with the active
server-side AUTH Plugin. This allows a full- automated approach to activating
new and improved AUTH algorithms without having to change anything at the
client?s side. For more information see the Authentication and Authorization
Section and the jvm.AuthHandler class documentation.
The system is based on a simple client-server
model. In this chapter we're trying to offer an insight into each side
general implementation and algorithm. Another important component, used
in implementing both sides (client and server), is the Entity and
it's implementation details are determinant in all system areas.
We presented earlier in the Virtual Medium chapter a brief look on a entity and it's main functional characteristics. We are trying here to offer a zoomed view on a entity's internal implementation. The Entity and the EntityID are defined by the jvm.entity.Entity, jvm.entity.EntityID classes.
The entity is designed to be used both at the server and client side. It contains two communication threads that deal with the network and related issues. One thread, the jvm.entity.TCPIPReadThread reads incoming messages from the network and inserts them into a special incoming message queue, also contained in the entity structure. The other thread, the jvm.entity.TCPIPWriteThread, takes care of sending the messages from a outgoing message queue to the network (that is to a corresponding remote entity). The idea here is of course to assure a maximum level of parallelism and also to avoid deadlocks.
The group information is also contained in the entity, defining the groups (jvm.entity.GroupID) this entity is member of.
The Entity class is used both at the server and client side. This is also enabled by its symmetrical design. Each provided client implementation uses an underlying entity object to communicate with the server after logging in. The server uses also a entity object for each connected client, object that represents that given client inside the server.
The Entity implementation might be of
interest only for programmers who intend to write internal code like custom
communication handlers, watchdogs etc. For more internal implementation
details please read the api docs.
The virtual medium is built (in the distributed case) upon several compatible servers that are started at different network coordinates, composing actually the structure. The main idea was to create a very fast solution easy to maintain, install and distribute. The communications benefits mostly of Java's multithreading and object serialization capabilities. The term compatible server in this case refers to the ability of the used communication handlers for each server to interact with each other in a compatible way.
The communication between the servers and the connected entities is done mainly using object serialization but also a human understand-able text mode only negotiation protocol. This is transparent to a normal application that uses the Client interface in order to connect, but allows for much finer control for custom written interfaces that are using a direct connection. There is a simple mandatory authentication implemented using a username/password pair but also a more complex scheme using server-side AUTH Handlers and client-side handler autoload & detection.
The server (distributed case) provides for flow control adjustment, connection speed rate detection, distributed timestamping (tolerance less than 10 sec), grouping, broadcasting (group, local, remote), timeouting, logging and accounting. (underway)
Every one of the server's features is adjustable via a resource file setting. The passwords are stored in a special separate file. A resource file setting also determines the communication plugin used in implementing the communication algorithm. For more information on resource files and other settings please see the System Resources Section.
The server is very easy configurable and runs right out of the box on any Java (1.1x and up) enabled platform. The main configuration resources are defined inside several resource files, one being the main resource file.
The actual server-client and server-server
communication is based on several predefined control message types. For
example when a application calls the logout () method defined in the client
interface, what actually happens is that the underlying client implementation
constructs a LOGOUT type message and sends it to the server waiting for
a server confirmation on this. Another example is when a application asks
to be added to a new group via the addToGroup() method defined in the client
interface, what actually happens is that a ADDTOGRP type message is constructed
underlying and sent to the server. On each request like the ones above,
the server side, implemented mostly by the current active communication
handler, has to confirm or deny the given request. This happens also by
using control messages of type OK or INVRQ. For a complete detailed description
of each message type please see the jvm.message.MessageConstants
class docs. (This information might be of interest for developers only)
The client perspective is defined through the jvm.client.Client interface. In order to enter the system a application has to use the client class to do so. The connection steps include using the connect() method to actually connect to a medium server and then login() to authenticate by providing a given username, password valid on that given server.
A client may be in three states regarding
his relation with the medium. A disconnected state is the
inactive state before calling connect() or after calling disconnect(),
no network connection exists between the client and any server in this
state. A connected state is defined as being the state after
calling connect() and before calling login() or disconnect(). In this state
the client is already connected to a given medium server and is able to
start negotiating about eventually entering the medium fully or just inquiring
about things like connection speed or other logged in entities information.
This state corresponds to an underlying text mode only human understandable
protocol between the client and the server. (See the Pre-login text mode
client-server negotiation protocol section for more details) The third
state is when the client is actually loggedin the system
by calling login() after calling connect(). From this moment on the client-server
underlying protocol is based only on object serialization and remains so
until the client calls logout(), which brings the client back into a connected
state. Calling disconnect() in any state disconnects the client from the
server as gracefully as possible. That is if the client is in a loggedin
state it sends a logout serialized control message to the server and then
exits, otherwise it just exits.
As outlined in the previous section, the
client-server communication is based on control messages. The server parses
every message that comes from the client entity in order to filter out
control messages. The client-client communication allows only messages
of the DATA type to go through. The active communication handler determines
this behavior. Custom handlers may implement other algorithms and types
of behaviors. For more details please see the jvm.message.MessageConstants
class docs and also the docs for the used active communication handler.
(This information might be of interest for developers only)
In this section we are describing the human understand-able pre-login text mode negotiation protocol. This protocol is active right after the client connects to a server that is in a connected client state. It should be used to prepare the client's login, to establish authentication details etc.
The protocol is based on a quite simple request-reply sequence. The client's requests and also the server replies are formulated using simple human understandable text string commands. The server replies start with OK or ERROR.
Bellow there is a list of the most standard client commands and corresponding server replies. The server replies are shown using a bold-italic uppercase font, the client requests are shown using an only bold uppercase font. As the server improves and changes this protocol will also change. The code given here should be treated as a simple example.
Client requests:
HELP. This client statement is intended for human users who negotiate directly with the server. It shows some help text for all the available commands.
USER username. Sets the current username used in login.
PASS password. Sets the current password used in login. The password is given in cleartext and should contain no spaces. For more details see the Authentication and Authorization Section.
CLIENTS. Provides the client with a list of all the entities logged into the current server.
RCLIENTS. This is a distributed version of CLIENTS. In the case of a distributed medium this should provide a list of all the client entities connected to all the servers forming the medium.
CLIENT entityid. This provides detailed single client info, for a client connected on the current server.
RCLIENT entityid. A distributed version of CLIENT.
STAT. Shows some status information for the current server.
QUIT. It basically disconnects.
NOP. Does nothing.
ECHO string. Echoes back the string.
VER. Displays the supported protocol version.
HANDLER. Displays the fully qualified class name of the currently active communication handler plugin.
HANDLER query. Should query the communication directly.
WATCHDOGS. Displays the fully qualified class names of the currently used system and user watchdogs.
AHANDLER. Displays the fully qualified class names of the currently active server-side system AUTH Handler (if any) and of the required client- side AUTH handler and versions.
AUTH. Requests the server to enter authentication mode, meaning to start using the server and client-side AUTH Handlers in order to negotiate AUTH information.
LOGIN. Logs into the medium. This
may be used only after a valid username and password were provided and
also a valid AUTH sequence (if any server-side AUTH Handler active) took
place.
Server replies:
ERROR [ERROR-PARAMETERS]
[ERROR-DESCRIPTION]
OK [OK - PARAMETERS] [OK - DESCRIPTION]
Client requests - server replies examples:
HELP
OK JVM 0.01 [some text]:
[... help text ...]
USER username
OK username [some text]
ERROR INVALID username [No such
username some text]
PASS password
OK [some text]
ERROR INVALID [Invalid password
some text]
CLIENTS
OK [number of clients] [some text]
[EntityID] [logintime] [GroupID-list]
...
CLIENT [EntityID-name]
OK [EntityID] [logintime] [GroupID-list]
ERROR INVALID [some text]
ERROR NOTFOUND [EntityID-name]
[some text]
STAT
OK [currenttime] [uptime] [number
of clients] [starttime] [maxclients] [communication handler class name]
JVM 0.01 [some text]
QUIT
OK Disconnecting ...
LOGIN
OK [my-accepted-entityid] [some
text]
ERROR FULL [Too many players -
system full - try again ...]
ERROR AUTH [required-client-auth-handler]
[required-client-auth- handler-version] [some text]
ERROR [some text]
VER
OK JVM 0.01
ECHO string
OK string
HANDLER
OK [fully-qualified-communication-handler-class-name]
[version]
WATCHDOGS
OK [fully-qualified-system-watchdog-class-name]
[version] [fully-qualified-user-watchdog-class-name] [version]
AHANDLER
OK [fully-qualified-server-side-auth-handler-class-name]
[version] [fully-qualified-required-client-side-auth-handler-class-name]
[version]
AUTH
ERROR No AUTH Handler active.
OK [fully-qualified-required-client-side-auth-handler-class-name]
[version] [some text]
...
OK [auth-level] [Authentication
succeeded. Your AUTH Level is right after OK]
ERROR INVALID AUTH sequence.
HANDLER text
ERROR NOTIMPLEMENTED [The feature
is not implemented yet ...]
As outlined before the server side is composed of many modularized components, each of which is controlled by a set of settings. These settings are defined inside resource files. The main resource file is passed as parameter on server startup and is used by most of the system plugins and components. Another resource file is the password resource file, which contains the usernames and passwords for the current system. (See the Authentication and Authorization Section for more details on this topic) Each plugin may use the main resource file to define for example the name of its own custom resource file. Time related settings inside a resource file are to be given in milliseconds, each string should be enclosed in double quotes (""), comments start with the character #.
Bellow we are trying to describe some of the standard resource file settings. Up to date descriptions and settings can be found in the provided main resource file example that comes with the system.
GREETING. This is a simple string data message that gets sent after a RESET type message to a entity right after login.
SERVER. The server name. This is used in constructing the current server id and also in binding and it should be a valid fully qualified hostname. The server is bound to this name, that is, no other DNS aliases will work in connecting to this server. If this setting is "" the server is listening on all valid addresses but its ServerID is not defined well. (Subject to change)
PORT. Port on which to run the server. This is a internet TCP port used by the server. The user running the system should have permissions to use that port. In the case of a Unix server that means that either the server is run as root or the portnumber is a unused portnumber above 1024.
LISTEN. Listen backlog for the server socket. This defines how long the connection queue is. It should be somewhere between 3 and 10.
PASSWORD_FILE. This defines where the password file is. The server should be able to read from it at startup. An example would be "passwords.txt". For more details on the password file please see the Authentication section.
USER_WATCHDOG. This defines the fully qualified class name of the optional user watchdog object. For more details see our Watchdogs Section. An example setting would be "jvm.server.NullWatchdog".
SYSTEM_WATCHDOG. This is the fully qualified class name of the required system watchdog object. For more details see our Watchdogs Section. An example setting would be "jvm.server.DistributedMaintenanceWatchdog".
PST_COMMUNICATION_HANDLER. This is the fully qualified class name of the required used communication handler. For more details see our Communication Handlers section. An example setting would be "jvm.server.DistributedCommunicationHandler"
PST_MAX_PLAYERS. This defines how many entities we allow inside the system at the same time. This depends on server power and load.
PST_DEFAULT_GROUP_NAME. This defines the group name of the default group in which the entities are entered upon login. It should be ?world?.
PST_ALLOW_FILTERING. This defines if the server should allow message filters to be set inside from the client. For more details on message filtering see our Message Filters section. The recommended setting is enabled.
PST_REMOVE_IF_FULL_OUTQUEUE. If a given client entity dies unexpectedly or has a very slow server connection, it's server side outgoing message queue might get filled up. In this case maybe we should remove that entity rightaway. The recommended setting is enabled.
PST_FORCE_ADD. This defines if we should allow two clients to log in using the same username, on the same current server. In the case we do the system will try to generate unique entity id's for each of the users by appending a number to the username, (being the current server uptime value) making it thus unique inside the system. The recommended setting depends strongly on the active communication handler and also on the AUTH Handlers.
AUTH_HANDLER. This defines the fully qualified class name of the used server side AUTH Handler. It may be missing.
CLIENT_AUTH_HANDLER. This defines the fully qualified class name of the required client side AUTH Handler that knows how to negotiate with the current active server-side handler. This depends on the AUTH_HANDLER resource setting. If AUTH_HANDLER is missing this is ignored.
CLIENT_AUTH_HANDLER_VERSION. This defines the minimum version for the required client side AUTH Handler, in order for it to work. If the version of the loaded client-side handler is lower than this one than the handler is not supposed to work with the current server-side handler, the client-side autoloader should reject it. The recommended setting for this depends of course on the CLIENT_AUTH_HANDLER resource setting.
Many other settings are available. Also
settings for any other particular component of the system (communication
handler, watchdog) may be inserted in the main resource file. The recommended
way of doing things is to set a resource for each component specifying
it's own resource file name and then to set its specific resources into
that file. For example the settings for the jvm.server.DistributedMaintenanceWatchdog
class, required in the case of an active distributed multi-server communication
handler running in the distributed case
(jvm.server.DistributedCommunicationHandler), are held in a file
defined by the MW_RESOURCES main resource file setting.
This section deals basically with the
way a certain client gets permission to enter the system. In the current
version of the system this feature is implemented using a two-stage
protocol scheme.
The first stage protocol is a poorly implemented feature using a cleartext username-password classic scheme. It is required also for being able to perform the second stage of authentication. This username-password authentication is based on a simple password database stored at the server's side. The system password database is a file defined by the PASSWORD_FILE main resource file setting. The password file is in itself a resource file containing as resource names the usernames and as resource settings the actual passwords. Remember enquoting the passwords. There is also a special last line that has to be defined as last line inside the file in order to be recognized as valid password file. In the case of the distributed communication plugin the password file contains also passwords that are used in interserver connections.
Bellow is a small example of a password file.
# this is a sample password file for the system ... # the format is something like # username password # # Note: Some of the passwords are of the form # hostname:port password # used mainly for interserver authentication for the distributed # communication plugin ... those passwords should be removed quickly # from here and put into some other file # # ATTN: There should be always a LAST line composed of the username # 12345.0 and the password 67890.0 !!! Otherwise the password file is # considered invalid test "test" anonymous "anonymous" radu "hello" jvm://sunsite.pub.ro:7010 "jvm://sunsite.pub.ro:7010" jvm://sunsite.pub.ro:7009 "jvm://sunsite.pub.ro:7009" 12345.0 "67890.0"As seen in this simple example, the passwords file contains also inter-server connection passwords. Those passwords are needed only for inter-server connections and are in the form of password=username=serverid of remote server. You have to define here one password like this for each server composing your distributed medium structure, that is if you start up three servers you have to have here three "username password" pairs where username=password=serverid. Remember that a serverid looks like "jvm://hostname:portnumber". This whole scheme is subject to change in the near future releases.
The second stage of AUTH is based on the AUTH Handler plugin facility. It strongly depends on the active server-side AUTH Handler. The client automatically detects and loads the required client side AUTH Handler and performs the AUTH sequence.
The final result of any AUTH negotiation
is a AUTH level. This level is a long integer number representing
some sort of credit that this client gets from the server, after performing
a successful AUTH negotiation. If it is positive or zero the client is
allowed to enter the system (subject to change).
![]() |
Visit Smart Software. |