November 2007 - Posts

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 :)

Posted by Vicente | with no comments
Filed under: ,