What about a map of table IDs to agents?

The map itself only needs to change if an event creates or destroys a
table. Anything else impacts a single table, and can be sent off to that
table's agent. An agent makes sense to hold each single table, as tables
undergo sequential transformations triggered by incoming external events.

Similarly, a map of user IDs to agents can track everyone's play-money.

The code that translates between incoming network events and sends to
agents is pretty much completely decoupled from the way the latter work, as
well; it won't need to be changed, for instance, if you start persisting
user information to a database. The money-handling agent for a user just
needs to now post an update to the backing database, and your startup code
needs to load the database data to initialize the agent contents on a
restart, and presto, you now have long term persistent user accounts and
money amounts, without touching the network/agent-send code, only the
functions applied to the agents.

So, sorting incoming network events and calling the appropriate business
logic becomes a concern completely separated from implementing that
business logic, and from the server-side DAL, and from the outgoing
networking. (The agents would need to also generate outgoing network
packets to update clients on the state changes they should know about. For
example, if a player gets dealt a 5 of hearts, their client needs to be
told that -- the others shouldn't, of course, as otherwise it would enable
someone with a hacked client to cheat by peeking at other players' cards.)

Timers can also be managed in a similar manner; for example, for each
connected client a fold-timeout agent can be created, and when the player
moves, the agent is sent something like this:

(defn timeout-set [[_ player]]
  (let [a *agent*]
    (future (Thread/sleep (+ 50 timeout-duration)) (send-off a
timeout-check)))
  [(+ timeout-duration (System/currentTimeMillis)) player])

which depends on this:

(defn timeout-check [[timeout-time player]]
  (if (<= timeout-time (System/currentTimeMillis))
    (do
      (send-off player timed-out)
      [Long/MAX_VALUE player])
    [timeout-time player]))

which will send "timed-out" to the player-agent if they don't move for
timeout-duration milliseconds. When the player moves, timeout-set both sets
the earliest time they may now time out (current time + timeout-duration)
and creates a sleeping thread that will wake up a little bit later than
then and send the agent a timeout-check. (Since *agent* won't be the same
in that thread and when it wakes up, it's saved into a closed-over local in
timeout-set.) The timeout-check function sees if the agent's stored timeout
value has been exceeded; if not the player must have moved again in the
interim and it returns the agent's current state, leaving it undisturbed.
Otherwise, it maxes out the timeout time and sends a timed-out signal to
the player's agent. The player can't get timed out a second time until
moving again first, which would bring the timeout time back down out of the
stratosphere. The timeout time for a player would also be set to this when
there was no hand in progress, and on first connecting.

Of course, the above could need to be adapted to your needs. Also, I'm
unsure how big a thread pool you'd get with future; very many connected
clients might swamp it with long-sleeping threads. Using the executor
framework stuff from Java might be a better idea, or even more agents (and
send-off rather than send), if you want it to scale, though using a future
makes the example code above very simple and easy to understand.

-- 
-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to [email protected]
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
--- 
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/groups/opt_out.


Reply via email to