views:

237

answers:

5

Ok, I have a game server running in Java/Hibernate/Spring/Quartz. The game clock ticks with a Quartz timer, and that works just fine.

However, I have many other things that need to happen at specific, tweakable intervals (in game time, not real time).

For instance, every 24 hours game time (~ 47 minutes real time, depending on the servers clock multiplier) a bunch of different once-a-day game actions happen, like resupply, or what have you.

Now, the current system is pretty rough, but works - I have a table in the database that's essentially a cron - a string key, the execution time of the next event and then hours, minutes, seconds and days until the next one after that. The time ticker checks that and then fires off a message with that code (the events string key) in it to a queue, adding the days, minutes, seconds to the current time and setting that as the next execution time.

The message listener is the grody part - it switches on the key and hits one of its methods.

Now I understand that this can work just fine, but it really doesn't sit well with me. What would your solution be to this, to have each piece of code in its own little class? What design pattern covers this? (I'm sure there is one). I have a few ideas, but I'd like to hear some opinions.

A: 

I personally wouldn't put this in the database but rather keep a separate service running in the background. Then my webservice or web application would communicate with this service through interprocess communication. Don't know how this translates into java world though.

Ender
+1  A: 

Rather than a switching on a set of codes, you could use the code as a key into a map, where the values are objects that implement a handler interface. This allows you to be much more flexible in adding new event types.

The pattern looks something like this:

private final Map<String, Handler> handlers = new TreeMap<String, Handler>();

public void register(String event, Handler handler) { 
  handlers.put(event, handler); 
}

public void handle(String event) {
  Handler handler = handler.get(event);
  if (handler == null) {
    /* Log or throw an exception for unknown event type. */
  }
  else {
    handler.execute();
  }
}

Rather than explicitly registering handlers, you could use something like Java 6's ServiceLoader to add new behaviors just by dropping JARs into the class path.

erickson
I haven't marked this as the answer because it's the no-brainer answer - sure, mapping is better than a switch, but it's really not elegant. The ServiceLoader would be the better approach, but doesn't address any of the other infrastructure already in place, such as Spring.
Jason Maskell
A: 

Conceptually I think you're doing two things;

Firstly you have a scaled version of time. As long as the relationship between this time and wall-clock time remains constant I'm fairly sure I'd just delegate this scaling behavior to a single class, that would have signatures like

DateTime getFutureTime( VirtualTimeSpan timespan)

I'd be using this to map virtual time spans to instances of real-time. Thereafter you can operate in real-time, which probably simplifies things a little since you can the use standard scheduling features.

The second part regards scheduling work for a future worker process. There's a number of core technologies working with this; Conceptually I think JMS is the java-grand-dad of a lot of these, it defines concepts much like the ones you're using and what you need. I think taking a look at JMS is fine for seeing concepts you may find interesting, it uses selectors to send tasks to specific workers, much like the ones you decribe.

Alas, JMS never seemed to fit the bill for most people. A lot of people found it was too heavyweight or the implementations too buggy. So usually people ended up with home made queue technologies. But the concepts are all there. Can't you just use quartz ?

krosenvold
+1  A: 

I would use a variant of the Command Pattern. I would extend the Command pattern to make a IIntervalCommand class. It would have a interval property, and a readonly CanExecute property in addition to the Execute method.

Then you create a CommandList Class that holds a list of IIntervalCommands. It would have a method called CheckToExecute that you pass it the current game time. The CheckToExecute method would traverse the list calling CanExecute for each command. CanExecute will return true if the elapsed time has occurred. If CanExecute return true then CheckToExecute will call the Execute Method of the object implementing IIntervalCommand.

Then adding additional game events is a matter of creating a new class implementing IIntervalClass. Instantiating the Object and adding it to the IntervalCommandList.

If the processing of the event is time consuming then the command could spawn the processing as a separate thread. It will return false to it's CanExecute property until the thread returns even if the interval has passed again. Or you have it spawn off another thread if the interval passed again.

You avoid the giant case statement. You could eliminate the database and setup the parameters when you instantiate the objects. Or keep it and use it as part of a factory that creates all your IIntervalCommands.

RS Conley
+1  A: 

Instead of switching on the key you can use a hashtable to dispatch these events. This way your timer events don't need to know about each other.

It should be possible do have something like:

timerQueue.registerHandler("key",new TimerHandler(){
   // do something timer related  
});

This way you can restart java code handling events without losing your persisted queue of events.

http://en.wikipedia.org/wiki/Priority_queue'>Priority queues are worth looking at if you have not already.

fuzzy-waffle