These days I was
supposed to be working on the input but gonzo has taken over it and he is doing
quite good progress so I decided to review some general things we had around and that should be solved to
easy development in Jade2. Jader for example has
two main complaints currently:
- When an
exception is thrown inside an Invoke call the stack trace is broken so it's
very hard to debug what happened. This is related to Jade2 multithreading and
after talking with Reed about the subject I haven't come to a solution (yet
;)). I will investigate more this in the near future because it's a pretty
important issue to solve.
- While testing
in Jade we add constantly scenes, textures,... and it's a pain to synch all
those files in the different developers computers. It's also a pain for people
that want to use Jade as they have to download those files separately from the
engine and update them when we change them (and we update them quite often in
development). For Jade1.1 I did a very simple FTP sync tool that worked so-so.
So I have
decided to tackle the second point in a general way: coding a Pluto plugin
(Pluto.Synchronization) that will allow two different file repositories to sync
their files. Currently sync is one way only (server to client) and it will
continue to be like that as I think it's the common scenario in games and most apps (unless someone gives me a really good reason to code
2-way sync). The plugin works pretty
similar to the VFS plugin I wrote (more about this later). A
SynchronizationServiceClass registers several classes in Pluto to be used by
other apps:
- ISynchronizationService: a service that syncs 2 different file repositories.
- IClientUpdateService: a service that updates the file system of a client
according to a set of changes.
- IServerGetDataService: a service that gets files from a server.
- IClientUpdateServiceBuilder: service to configure a IClientUpdateService.
- IServerGetDataServiceBuilder: a service to configure a IServerGetDataService.
(if Sync was 2-way then
IClientUpdateService and IServerGetDataService would merge on a single
interface)
Currently I have
provided implementations for:
- Client: HardDisk.
- Server: HardDisk, FTP.
This means that you can
sync 2 hard disk folders or a hard disk folder and a FTP. Nice enough for a
start. But let's look the service, client and server interfaces more closely.
First the
ISynchronizationService interface:
namespace Pluto.Synchronization
{
public interface ISynchronizationService
{
#region Properties
IClientUpdateService UpdateService { get; set; }
IServerGetDataService GetDataService { get; set; }
#endregion
#region Methods
void Synchronize();
#endregion
}
}
Pretty generic, it just
holds a IClientUpdateService object, a IServerGetDataService object and a
method (Synchronize) that surprisingly syncs both of them :p Nothing here ties you to any strange requisite you
wouldn't want.
Now the
IClientUpdateService:
namespace Pluto.Synchronization
{
public interface IClientUpdateService
{
#region Properties
string HashAlgorithm { get; set; }
#endregion
#region Methods
void Start();
void End();
Catalog GetCatalog();
Stream CreateFile(string universalFileName);
void DeleteFile(string universalFileName);
Stream UpdateFile(string universalFileName);
#endregion
}
}
A little less generic
this time. First we have GetCatalog that gets the client file Catalog: a
catalog is just a collection of all the files inside the client. Each catalog
item represents a file and has a string name that uniquevocally identifies it,
the hash of the file contents and the length of the file. The important thing
here is that the name that identifies the file can be resolved equally by the
client and the server to the same file! The VFS has a similar requeriment but
we haven't done a class to enforce this, maybe it's something we should look at
in the future... Then we have a Start and
End methods: this methods are called before sync starts and after sync ends and
they client can use them to any initilization and cleanup it needs. And last we have the 3
"real" methods: CreateFile, DeleteFile and UpdateFile. DeleteFile is
pretty straightforward, but CreateFile and UpdateFile have a
"curious" return: a stream. This streams represents the file and the
Server service is supposed to be writting here to create/overwrite the client data.
And now the
IServerGetDataService:
namespace Pluto.Synchronization
{
public interface IServerGetDataService
{
#region Methods
void Start();
void End();
Catalog GetCatalog();
void TransferFile(string universalFileName, Stream targetStream);
#endregion
}
}
As with the client, we
have a GetCatalog method to get the server catalog and Start and End methods.
The interesting method is TransferFile that takes a file name and a stream where the server writes its data. Currently the HardDisk
and FTP implementations just run over the normal system IO or directly against
a socket, but the only requisite for us is that the client exposes streams and
that the server is able to write on streams.
This means Pluto.Sync could be used perfectly with Pluto.VFS :) Pluto.VFS only wraps
differents file origins to make a system independent from them and Pluto.Sync
just wants a simple way to get files and write onto them, something that
Pluto.VFS.FilesSource Pluto.VFS.WritableFilesSource provide easily. To be truth life is not so easy as Synchronizing can create folders in the file system (something not supported by the VFS right now), but it will be in the near future :)
The code is now uploaded in the last check-in of Pluto and you can play with it using the TestApp we have there. Maybe you'll have to change the configuration.xml file, but it's pretty easy, this is how the sync configuration part looks:
<Synchronization>
<ClientUpdate Type="HardDisk" Path="C:\TestUpdate" HashAlgorithm="MD5" />
<ServerGetData Type ="HardDisk" Path="C:\TestGetData" />
</Synchronization>
It just sets the type of service used in the Client and Server, their paths and the HashAlgorithm to use in the client (I haven't put that in the server but maybe I should, not sure about this one). So after a little polishing in the code (documentation, some tests and a small GUI) I'll start looking at the exceptions problem. Let's hope it gets solved quickly :)