Hi Oskar,

I've recently been working on a similar problem. I've had some success with
FRP (functional reactive programming), and I've written a library,
Reagi<https://github.com/weavejester/reagi>,
to implement what is hopefully a style of FRP that remains true to
Clojure's ideology.

First, let's represent an event that causes damage as a data structure.

{:type :damage
 :amount 10
 :target player-id}


I'll leave the player identifier as a symbol for the purposes of this
example. When coded for real, this could be anything that uniquely
identifies the target. A UUID might be a reasonable choice.

In Reagi, we can represent a stream of events in an area like so:

(def area-events (r/events))


We can then filter out only the damage events that apply to the player:

(def player-damage-events

  (->> area-events

       (r/filter #(= (:type %) :damage))

       (r/filter #{= (:target %) player-id))))


Once we have a stream of damage events, we can represent the health of the
player by subtracting the damage from an initial value:

(def player
  {:id player-id
   :hp (->> player-damage-events
            (r/map :damage)
            (r/reduce - max-hp))})


To get the current health of the player, we can just deref:

@(:hp player)


This approach is thread-safe and asynchronous, relying on core.async behind
the scenes. It's also declarative, and you can handle all game logic
without requiring a central loop. Instead you can just rely on a render
loop that derefs your game state every time it needs to display.

Another advantage is that once you have a game set up around this
principle, making it multiplayer is a matter of hooking up your main event
stream to a TCP socket.

- James



On 13 November 2013 17:16, Oskar Kvist <[email protected]> wrote:

> Hi!
>
> I'm making an MMORPG. For those of you not into games, MMORPG is short
> for massively multiplayer online role-playing game. That means that
> hundreds of players can connect to the same server and play a role
> playing game simultaneously. There is of course a big game state on the
> server that changes all the time, and the clients need to be aware of at
> least some of the changes.
>
> My problem is the following: It's inefficient to send the whole new
> state each frame to the clients. So the server needs to send changes,
> updates about events, to the clients. For example, if the server's game
> logic decides that a monster should spawn somewhere, the server needs to
> somehow remember that event so it can notify the clients about it.
>
> What I do now is: Do something to the state (e.g. spawn a new monster),
> then generate an event (piece of data) that describes what happened (a
> monster spawned), that can be passed to other parts of the system (e.g.
> the networking part) so that those other parts can react to what
> happened. But that seems stupid. Once something happens, the events
> should ideally be implicitly generated and taken care of without me
> writing code that explicitly handles events.
>
> It could be done by comparing the new state to the old one, but that is
> also inefficient.
>
> To make it a little more concrete: I don't want to write (in code) "when
> a player is attacked, update the players health, and generate a
> 'modified-health-event'" and "when a monster spawns, insert the new
> monster into the game state, and generate a 'monster-spawned-event'". If
> the functions are pure, not only do I need to generate all these events,
> which are not really part of the core logic of the game, but I also need
> to pass them around: Say the main loop calls `let-characters-attack`,
> and that function calls `take-damage` (several times), and `take-damage`
> generates an event, then that event needs to be passed through
> `let-characters-attack` back to the game loop so that the networking
> system knows what updates need to be sent to the clients.
>
> I thought about some ideas:
>
> Instead of returning events, and having to pass them through calling
> functions, I could just put them on a queue somewhere. That results in
> less code (that pass events around) but make the functions impure. And
> ideally I don't even want to have to explicitly generate events.
>
> Maybe I could make a macro or use something similar to the state monad,
> i.e. something that takes care of the plumbing behind the scenes, but
> I'm not sure how that would work. A problem with that is that the
> functions that generate events can be very dissimilar to each other.
> `take-damage` for example only operaters on one single character, while
> other functions may operate on a larger portion of the game state, or
> the terrain, or whatever. The events can be dissimilar to each other
> too. This heterogeneousness seems like somewhat of a problem.
>
> Let's say, for the sake of argument, that `take-damage` does essentially
> `(update-in character [:health] - damage)` and the event that should be
> generated is `[:damage id-of-character amount-of-damage]`; and
> `spawn-monster` is essentially `(assoc-in game-state [:characters]
> (new-id) (new-monster))` and the event is `[:new-monster id monster]`.
> It's kind of hard to make a general function f: what happened to the
> game state -> event, that is called implicitly when something happens to
> the game state.
>
> justin_smith suggested spatial partitioning to make things more
> efficient. But even if partitioned, sending the whole partition or
> comparting the previous partition of the state to the new one is still
> way less efficient than having these events, and of course it does not
> take care of (make more implicit) the events stuff. Of course, when I
> say make more implicit, what that really means is that I want to
> separate, decomplect, the updating of the game state from the generation
> of events so I don't have to care about events when updating the game
> state.
>
> Any ideas?
>
>  --
> --
> 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.
>

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