Seamless Code and Content Update in Connected Videogames

advertisement
Seamless Code and Content Update
in Connected Videogames
Thomas Bousquet
Georgia Tech
tbousquet@gatech.edu
Edouard Kieffer
Georgia Tech
ekieffer@gatech.edu
April 30, 2008
Abstract
With the advent of high speed Internet in the late 1990s, the videogame
industry on the PC platform has begun relying strongly on patches delivered after the official release of the game to fix bugs and make more
content available to their customers. Nowadays, with cable or ADSL
connections available virtually everywhere, and the release of “next-gen”
consoles such as the XBox 360, the Playstation 3 or the Wii, all boasting
Internet connectivity, the distribution of those patches have been largely
eased. However, to install the new update, a player must quit the game
and wait for the patch to be applied. In this paper, we present a solution to this problem, the “Dynacode” application. Built on a back-end, a
front-end, and a flexible client API, our solution enables seamless update
of both code and content in an application.
Contents
1 Introduction
2
2 Related Work
2
3 System Overview
3.1 Architecture . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.2 Use Case . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3
3
5
4 Back-End
4.1 Technologies and Architecture . . . . . . . . . . . . . . . . . . . .
4.2 Challenges . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5
5
5
5 Front-End
5.1 Technologies and Architecture . . . . . . . . . . . . . . . . . . . .
5.2 Challenges . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6
6
8
1
6 Client API
6.1 Technologies and Architecture . . . .
6.2 Dynamic Classes . . . . . . . . . . .
6.2.1 Explicit Dynamic Classes . .
6.2.2 Implicit Dynamic Classes . .
6.2.3 Dynamic Classes Migration .
6.2.4 Dynamic Classes Evaluation .
6.3 Dynamic Resources . . . . . . . . . .
6.3.1 Architecture . . . . . . . . .
6.3.2 Process Overview . . . . . . .
6.4 Challenges . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
9
9
10
10
11
11
12
12
12
13
14
7 Sample Implementation
14
8 Limitations
14
9 Conclusion
16
1
Introduction
Videogames have become a prominent media over the years, becoming in 2007
the second major source of entertainment. The last evolution of the media as
a whole are multiplayer online games and solo connected games. Both involve
the presence of a server to deliver content and code updates. In the first case,
this server is required to simply play, while in the second case, being able to
connect to the server will ensure a player has the latest updates installed when
he begins playing. In terms of development cycle, this shift in use created new
opportunities: the developers could now react to users’ feedback in matters of
hours or days. but they also had the responsibility of handling hundreds of
thousand users. The main challenge then lies in the distribution of the updates
in code and content.
In this paper, we will first present related work from the videogame industry
in section 2, before giving a brief overview of our solution in section 3. In secions
4 to 6, we will go into more details about several components of our system,
namely the back-end, the front-end and the client API. We will then present in
section 7 our sample integration with aircarrier, a 3D videogame before exposing
the limitations of our solution in section 8. We will finally conclude in section
9.
2
Related Work
Three major applications appeared in this domain in the past two years:
• World of Warcraft by Blizzard is the most popular massively online role
playing game, it gathers millions of players 24/7, most of them shaping
the universe of the game on a daily basis
2
• Steam, an online distribution service by Valve which automatically delivers
updates or additional content for a large library of online and offline games.
It is considered the best of its kind as it also integrates a social network
and advanced system such as matchmaking and achievements.
• Sam & Max, a digital distributed game series which witnesses a new
episode every month over a period of six months.
These various applications highlight three advantages of connected games:
uninterrupted service, user impacted worlds and shortest, highly dynamic development cycles of incremental game. On the whole it helps create new kind
of games, brought to the end user in new fashions.
The main challenge behind these opportunities is to deliver new content
(both in terms of software and of assets) in a way that is both tolerable to the
user but also noticeable, as new content usually means new gameplay possibilities. The current paradigm is to check for new content at startup (or in the
case of Steam, at regular interval of times), retrieve it on the client machine and
apply it when the game restarts. When it comes to art assets integration, some
game such as “Guild Wars” adopts a more elegant approach: the game world
being separated in zones, it downloads and updates the content when entering
a new zone. The other tour de force of Guild Wars is to be able to maintain all
the art content in one 1.5GB file that is never decompressed. However, updating
the software is still a stop-and-go operation. The main improvement we want to
bring with our solution is transparent software update delivery and applications
through dynamic coding.
3
System Overview
3.1
Architecture
Our Dynacode application is designed to bring dynamic code and content update
to an application with limited costs during implementation and use. To achieve
this, we divided our project in three units:
• Back-End: The back-end is connected to all clients and will respond to
client requests to get up-to-date code and content on connection, as well
as pro-actively distribute new updates as soon as they are made available.
• Front-End: A front-end is provided to enable easy administration of the
back-end.
• Client API: A flexible and easy to use API to enable easy integration of
dynamic code and content in new or existing applications.
We propose a representation of our architecture in figure 1. The whole
application is based on technologies close to the Java world.
We also present in figure 2 the data model we used to represent our objects.
An arrow should be read as “contains”.
3
Figure 1: Dynacode Overview
Figure 2: Dynacode Data Model
4
3.2
Use Case
We will quickly describe how our system is expected to be used. Developers
should first include the client API in their project and use our instantiation
of dynamic classes instead of standard instantiation (more on this topic in the
client API section). Once the development of the application is complete, the
“static” part will be packaged in a jar file.
The front-end is then used to create a new “dynamic application”, by providing the said jar file and a name. Dynamic classes must then be added to
the application using the front-end. If any content is dynamic (eg: textures,
models, etc.), it should also be uploaded through the front-end.
Once the process is over, users can download the jar file to their computer
and launch the game. At the first launch, the client will proceed to retrieve all
dynamic classes and content while the game starts running. At each subsequent
launch, only updated classes and cotent will be retrieved.
In the following sections, we will go over more details about each of our unit.
4
4.1
Back-End
Technologies and Architecture
Our back-end is a pure Java EE dynamic web application tailored for the JBoss
application server. Its architecture is shown in figure 3. The architecture is a
rather traditional Hibernate / Spring implemenation up to the DAO layer. From
there we built our services as standard POJO’s. We proceeded to expose them
as web services through XFire for all administration-related features. Spring
controllers are used in place of web services when file upload is required. Lastly,
client-specific services are exposed as RMI services to enable more efficient and
flexible communication. The latter services are used when the client requests
data from the back-end. We also need data-push-like capabilities to be able to
dispatch newer revisions of code and contents as soon as they become available.
This functionality is provided by two JMS topics, a producer – consumer queue.
We use one queue to dispatch code updates and another to dispatch content
updates.
4.2
Challenges
Although the back-end development was rather straightforward, we encountered
a few problems. First, we were planning to use AMF as a communication
protocol between our back-end and our front-end. This was supposed to be
achieved through the use Granite Data Services[2]. However the front-end didn’t
seem to manage deserializing collections from Hibernate entities.
Another problem we had due to the use of web services, is that those do
not support circular references. In our persistence model, we were planning to
use bidirectional relationships, which we had to abandon. On the same topic,
we used XFire to handle serialization between xml and Java objects. Through
5
Figure 3: Back-End Architecture
the process however, XFire was creating proxy objects instead of objects of the
persistence class. Hibernate however uses the class of the object and instrospection to determine how to handle the save. Both of these operations would fail
on a proxy object. We had to design a casting system which would enable to
recreate an object of the proper class from the proxy object, which was much
more difficult than it seems due to object nesting and nested lists of persistence
entities.
5
5.1
Front-End
Technologies and Architecture
The front-end is built using the RIA-oriented Adobe Flex 3 development language. We’re also using the Cairngorm Framework, an MVC implementation
for Flex. The figure 4 sums up important features of our front-end. Starting
from a view, which is basically the presentation layer of the code, data will be
read from the model (eg: the list of available applications, etc.). When the user
interacts with the administration interface, for example by asking to refresh
the list of available applications, the view will send an event to the controller,
which will in turn invoke a command. This command will use a delegate to
contact the back-end through a web service or a controller. The delegate then
forwards the result he received to the command, or notifies the command of an
error if applicable. If no error has occurred, the command will then update the
model with the information he received from the delegate. the view will then be
automatically updated with the new data from the model thanks to Flex data
binding.
Our front-end exposes several modules. The first and most complete one is
the dynamic classes interface, which enables creation / deletion of applications,
6
Figure 4: Front-End Architecture
7
adding or deleting dynamic classes for a given application and management of
revisions and dependencies of a dynamic class. It can also be used to enable a
batch of classes or revisions, so that all classes sent to a client are also sent with
their dependencies. An important feature of this class management interface
is its robustness. Indeed, we heavily use reflection on the server-side when
performing class modifications. For example, when uploading a new revision,
we make sure the uploaded file is a valid java bytecode file, and that the class
name of the new revision matches that of the previous one.
The second module is a simpler resource management interface. It’s also
possible to add or delete a resource, as well as adding a revision for a given
resource. Lastly, a user management module is also provided. It currently
only enables creation of administrators for the administration interface, however
in the future it will also be used to manage users’ access rights to a given
application or resource package. We give a screenshot of our interface in figure
5.
Figure 5: Front-End Interface Sample
5.2
Challenges
We had a rather smooth development process for this front-end, save for the
AMF communication peoblem we already stated previously. We did not encounter any particular problems. We have to highlight that the release of Flex
3 a few months ago was a big help to us. Indeed, Flex 3 introduces the concept
of “Type registry”. This enabled us to use a DTO layer to communicate with
the back-end very easily. In Flex 2, we would have had to convert manually
each DTO received or sent from the back, or just use basic types in our communication routine. The type registry however handles automatic instantiation
of a DTO object upon receiving data from a web service through the use of the
WSDL data. Also, thanks to the Cairngorm framework, we were able to quickly
8
adapt when we had to undergo some modification in the business part or the
interface part of the code.
6
6.1
Client API
Technologies and Architecture
The last part of our Dynacode application is the client API, which can be used
to quickly port an existing application to a dynamic code paradigm or even
more easily to build one from scratch. The figure 6 describes the architecture
of the client API. Our dynamic instantiation process is derived from Li Yang’s
excellent article[4].
Figure 6: Client Architecture
At the heart of the code update process is the DynacodeManager, which is
actually the only part of our code an application developer has to worry about
to get dynamic code in his application. This critical object is implemented using
the singleton design pattern. When created, it will try to establish a connection
to the back-end to enable dynamic update. To do this, it will connect to the JMS
services offered on the server and also prepare our RMI client. It will also set
up our custom classloader for instantiation of dynamic classes. All this process
is transparent to the developer and of course the end user. When requesting a
9
dynamic instance from the dynacode manager, the returned object will actually
be a proxy object. That’s why we need an invocation handler, which will forward
all method calls to the real object. When called, the invocation handler will
also check if the dynamic object it is hosting is up to date. If it’s not, it will ask
the classloader to reload the class and will transfer all fields from the previous
version to the new one before proceeding to the call.
The other main component of our API is the DynaContent manager. It
has a similar role when compared to the Dynacode manager, except that it
is obviously responsible for content, or resource update. It will also be using
the JMS services provided by the back-end to be notified of any change in its
resource pool. When such a change happens, it will undertake necessary actions
required for the resource to be available to the player.
Lastly, another important feature of our client API is that it is fully multithreaded. Both code and content updates are handled in separate threads so as
to minimize interference with the game engine.
6.2
Dynamic Classes
Our API offers two types of dynamic classes, that we refer to as “Explicit
Dynamic Classes” and “Implicit Dynamic Classes”. Both offer advantages and
drawbacks that we will detail in the following section. We also show in figure
7 how one would instantite an object of class Example from package net.test
conforming to the IExample interface, supposing dynMan points to the instance
of DynacodeManager.
a) Example = new Example();
b) Example = dynMan.buildNewExplicitDynamicClass(IExample.class,
‘‘net.test.Example’’);
c) Example = dynMan.buildNewImplicitDynamicClass(‘‘net.test.Example’’);
Figure 7: Class Instantiation
a)standard class – b)explicit dynamic class – c)implicit dynamic class
6.2.1
Explicit Dynamic Classes
The only prerequisite for a class to be used as an explicit dynamic class, as can
be seen in figure 7.b is that such a class should always conform to an interface:
this means that all public functions should be known and more importantly
that no public method can be added when compared to the initial revision. We
deem this to be a considerable, but sensible constraint considering that a class
is defined to complete a certain task, and that other classes should not have to
worry about how the task gets done. We also highlight the fact that interfaces
only define public methods, so the developer is free to add, delete or modify
private and protected methods as he sees fit between the revisions.
10
On the other hand, a developer can use an explicit dynamic object pretty
much as he would use a standard object. The whole dynamic process is completely hidden, and the only actual difference is the instantiation process as
stated before. For the time being, before we polish the implicit dynamic classes
implementation, we strongly advocate using explicit dynamic classes.
6.2.2
Implicit Dynamic Classes
Aware that our explicit dynamic classes might still impose too much of a limitation to developers for specific classes, we also provide a powerful tool in the
form of implicit dynamic classes. In our current implementation however, we
sacrifice usability to obtain a truly dynamic class. Indeed, those classes get rid
of the two limitations of explicit dynamic classes: they don’t need to conform
to an interface and consequently enable a developer to add a public function
when changing revision.
The cost for this flexibility is currently high however. First, an implicit
dynamic class must extend ImplicitDynamicClassImpl. In other words, it may
not inherit from another class. Moreover, to invoke a method on an implicit
dynamic object, we use a rather complicated process, as exposed in figure 8.
ReturnType result = (ReturnType)implicitInstance.invoke(
‘‘methodName’’,parameter1,parameter2,parameter3\ldots)
Figure 8: Invoking a Method on an Implicit Dynamic Class
The biggest downside to this method is the lack of autocompletion, and
even the number and types of parameters can’t be known without refering to
the source code of the class. This is however the only way to get rid of the
interface limitation. Indeed, the concept of proxy used in Java requires that
a proxy be always linked to an interface, so our implementation of implicit
dynamic classes is actually a trick: we provide the proxy with an interface only
containing a public “invoke” method, the back-end object in the proxy will be
the implicit dynamic object itself. This invoke method will then call the method
on the back-end object with the parameters as specified in the argument list.
6.2.3
Dynamic Classes Migration
While the game is running, if a new revision for a class is received, the invocation
handler will proceed to its migration. We provide two complimentary ways of
handling migration. The first is an automatic migration process which should
suit the needs of most classes. We simply proceed by recovering all public,
protected and private fields of the current revision using introspection. We then
create an instance of the new revision, and we transfer all the fields we recovered
from the old version to this new instance. If a given field has been deleted from
a revision to another, we will simply ignore it.
11
The second way the migration process can occur is by using custom migration. As implied by the name, this mode leaves the developer responsible for
the actual object migration. To enable this, the dynamic class must implement
the ICustomMigration interface. The latter includes two public methods, the
“enableAutomaticMigration”, which is simply supposed to return a boolean indicating if the developer wants to proceed to migration from a “blank” object
(returns false), or if he desires to continue the migration process where the
automatic migration left off (returns true). The second one is the “doCustomMigration” method. The parameter it receives will be the old revision of the
object. From there, the developer is free to implement the migration any way
he desires.
6.2.4
Dynamic Classes Evaluation
In figure 9, we sum up the advantages and drawbacks of our two types of dynamic
classes. As stated before, we think the constraints of the explicit dynamic classes
are worth conforming to, as they give a very clean result. It is also considered
to be a good Java development practice to have classes implement an interface
when they are subject to change (eg: a DAO usually implements an interface
in case the current Hibernate ORM is replaced later).
Figure 9: Evaluation of Explicit and Implicit Dynamic Classes
6.3
6.3.1
Dynamic Resources
Architecture
Alongside our dynamic code update, we also provide dynamic resource update
capability. We go into a bit more details about the “DynaContent manager” in
figure 10. The cornerstone of our updating system is JNotify[3]. This Linux-
12
and Windows-compatible library enables capturing OS messages related to file
access. We are thus notified of any change in our resource directory and can
then handle the updating process as soon as a new resource revision is received.
Figure 10: DynaContent Architecture
6.3.2
Process Overview
However, we could not factor as much of the functionality for resources as we did
for the code. Indeed, although a piece of code is a determined concept that we
perfectly know how to handle, and that will be consistent from one application
to another, a resource is a much broader concept that can’t be guaranteed
to be the same even within a single application: you wouldn’t have the same
updating mechanism for a texture or for a model. Even more so, the process of
updating a texture might differ from object to object, the most obvious example
being texture management for a terrain and a character model. That’s why the
framework provided is highly customisable, leaving to the developer’s discretion
how the updatable resources have to be handled.
Our framework is composed of an interface, IUpdatableResource, and two
abstract classes: ResourceListener and UpdatableResourceManager. For each
resource concept, a developer must create a new interface extending our generic
IUpdatableResource. More importantly, he must also provide an implementation of ResourceListener. The abstract class provides detection of a modified
file, handling of a new revision must then be provided. Lastly, for each application, an implementation of UpdatableResourceManager must be provided. A
manager is a singleton class used to register listeners.
13
6.4
Challenges
The client API development was a constant challenge as we were working with
technologies relatively unknown to us (JMS, RMI, etc.) and that we had to
tackle problems that have not been covered extensively. In the end however, this
proved to be more a good opportunity for learning than a frustrating experience.
7
Sample Implementation
Aircarrier[1] is a 3D dogfighting game in which a player controls a plane and
must defeat a number of AI planes. As one of the rare Java game in 3D, and
considering its fast-paced nature, we thought it would be the perfect platform for
us to demonstrate the capacity of our Dynacode application. We thus proceeded
to two modifications to showcase our work. We modified tha targetting AI using
the explicit dynamic class model, and we also enabled texture updating on one
of the planes.
The AI modification process actually took us about 10 minutes. Although
the class was not implementing any interface to begin with, we used Eclipse to
extract an interface from this class. In the main class, we added a DynacodeManager instance as a private field, and modified the instantiation process as
described in figure 7.b. It was then a simple matter of adding the new application using the front-end, and uploading our dynamic class.
The texture update process however took a bit longer, as we needed to implement the classes described in previous section. Moreover, we were modifying
an application which was not our own, and was to us somewhat cryptic at times.
Factoring the JNotify library tests, implementation process and debugging, it
took us about one day to make it workable. We deem that it should not take
more than half that time in the general case (we were still discovering JNotify
and aircarrier), especially if the developer has taken part in the development of
the application to modify. The same time requirements would hold true for an
application designed from scratch.
The most intereting fact we got out of this experiment is that the framerate
did not suffer from our dynamic class and content mechanism. Although aircarrier does not include a benchmark utility, basing our judgement on the FPS
counter displayed in game, we did not notice any noticable performance issues,
if at all. This is very encouraging for an AI routine called once per frame!
8
Limitations
Although we think our Dynacode application is pretty powerful and efficient,
we still have to note a few limitations in our current system.
First, when migrating a class from one revision to another, we need the class
to define a default constructor. For example, the AI class we had to modify
in aircarrier only defined a constructor taking several parameters. We had
to change this to a default constructor, and move the part of the constuctor
14
using the parameters in a separate function. This has more a cosmetic than a
functional impact though. The developer must however keep in mind that all
final fields of a class must be initialized in the body of the class and not in the
constructor. This limitation might be lifted in the future, as we think that the
Java reflection API is poweful enough to provide a solution.
Another limitation is the fact than an explicit dynamic class needs to implement an interface. As we explained earlier though, we think that this is more
a matter of application architecture, as we don’t think a class’s goal should
change from one revision to another. It would however be possible that following a refactoring, a method name has to change, this is not currently supported
by our system. Also, the instantiation of such a dynamic class as presented in
7.b is a bit clumsy. In the future we want to see if we could hide a bit of the
complexity by only passing the interface as a parameter to the buildNewExplicitDynamicInstance method. We’re still wondering if this can be done in a
straightforward manner though.
Linked to the previous limitation is also the fact that if a developer was to
actually change a method name and dispatch this revision, an exception would
be thrown on the client side. This could be solved by simply selecting the
previous revision as the revision to be used through the front-end, however it
would definitely be good to have a bit more error handling on the client API, and
maybe even a “rollback” feature that would cancel an update if something went
wrong so that we can guaranty the application on the client-side will always be
in a working state.
Another important limitation is our current implementation for implicit dynamic classes, which is definitely not robust enough and difficult to read. We’ve
yet to find an elegant solution to correct this problem. For now, the best solution we’ve thought of is to develop an Eclipse plugin which would be able
to help the developer know what parameters are needed through the use of
auto-completion.
For any commercial use of our solution, we would definitely need to implement some security into our application. The communication can easily be
hidden from eavesdroppers by running JBoss on https, however we would also
need to secure our services. We’ve had a quick look at Acegi Security for our
web services, and didn’t have time to check RMI security. Securing individual
applications / resource packages so that only a given group of users can access
them however seems easier, as we would only need to add a security layer at the
service level.
The dynamic resource delegates a lot a of the work to the developer when
compared to how dynamic code is handled. This is however something we can’t
really help due to the numerous types of resources that could be used in a game.
We could however design a more standardized “resource description language”,
based for example on XML that could help integrate part of the updating process
in the API.
Finally, we offer little guaranty in terms of update synchronization. Although
all connected client are likely to all be updated at a few seconds interval, there
is no mechanism that would ensure this. It is a rather minor limitation for con15
nected single player videogames, however this makes our current implementation
unsuitable for online multiplayer games.
9
Conclusion
In this paper, we’ve described an innovative distribution system for connected
videogames. It is the first system (we are aware of) that enables seamless update
of both code and content in an application without requiring a restart of the
application or any action from the player that would hinder his immersion. Our
front-end enables easy modification of any application or resource that have to
be dispatched, while the powerful and flexible client API enables developers to
quickly integrate dynamic components in their applications. We deem that with
a few more features, especially those related to security, our system could be
considered suitable for a connected single player game. The next step would
then be to ensure the scalability of our application and add some synchronization
guaranty so as to also support online multi player games.
References
[1] ”shingoki”. aircarrier (https://aircarrier.dev.java.net), 2006.
[2] Franck Wolff. Granite data services.
[3] Omry Yadan. Jnotify (http://jnotify.sourceforge.net).
[4] Li Yang.
Add dynamic java
(http://www.javaworld.com), 2006.
16
code
to
your
application
Download