$30 off During Our Annual Pro Sale. View Details »

Building multiplayer game using Streams (Devoxx UK 2016)

Building multiplayer game using Streams (Devoxx UK 2016)

In this talk you will learn how stream-based programming can be used to implement web frontend and multiplayer backend of the classic game: Snake.

Building dynamic applications using imperative approach tends to create lots of unmaintainable code. Stream-based programming tries to solve this problem by introducing fully declarative way of defining application logic. While using streams, you will focus on WHAT needs to be done, not HOW and WHEN.

The talk is divided into 3 parts. In the first part you will learn how to create a frontend of the Snake web game using streams as building blocks. Then, we will move to the server side and use Scala and Akka Stream library to create backend service that will allow the game to be played by multiple players. In the third part, we will discuss reactive streams and how they make asynchronous communication safe.

Michał Płachta

June 09, 2016
Tweet

More Decks by Michał Płachta

Other Decks in Programming

Transcript

  1. Devoxx UK / 09.06.2016
    @miciek
    Building multiplayer game
    using Streams
    Michał
    Płachta

    View Slide

  2. Devoxx UK / 09.06.2016
    @miciek
    Streams?
    ● way of defining application logic
    ● focused on data transformations
    ● explicit time dependencies
    ● declarative

    View Slide

  3. Devoxx UK / 09.06.2016
    @miciek
    What’s inside?
    ● how frontend web app written
    in streams looks like
    ● how backend app written using
    streams looks like
    ● how do Reactive Streams keep
    our streams safe?

    View Slide

  4. Devoxx UK / 09.06.2016
    @miciek
    Meet Snake & Fruit
    Snake
    Fruit

    View Slide

  5. Devoxx UK / 09.06.2016
    @miciek
    Our First Stream: “Ticks” stream

    View Slide

  6. Devoxx UK / 09.06.2016
    @miciek
    “Directions” stream

    View Slide

  7. Devoxx UK / 09.06.2016
    @miciek
    “Snake’s Head Positions” stream

    View Slide

  8. Devoxx UK / 09.06.2016
    @miciek
    Stream operators: map & scan
    stream.map(f)
    ● transforms values from
    stream using function f
    ● returns a new stream
    with transformed values
    stream.scan(init, f)
    ● accumulates values
    from stream using
    function f
    ○ f(acc,val)=>new_acc
    ● returns a new stream
    that outputs
    accumulator value

    View Slide

  9. Devoxx UK / 09.06.2016
    @miciek
    Once again: the code

    View Slide

  10. Devoxx UK / 09.06.2016
    @miciek
    Displaying values from the stream

    View Slide

  11. Devoxx UK / 09.06.2016
    @miciek
    Streams are reusable

    View Slide

  12. Devoxx UK / 09.06.2016
    @miciek
    Streams & Operators are lazy
    No elements
    or
    No terminal
    function

    View Slide

  13. Devoxx UK / 09.06.2016
    @miciek
    “Key presses” stream

    View Slide

  14. Devoxx UK / 09.06.2016
    @miciek
    “Left Key Presses” stream

    View Slide

  15. Devoxx UK / 09.06.2016
    @miciek
    “Right Key Presses” stream

    View Slide

  16. Devoxx UK / 09.06.2016
    @miciek
    Stream operators: filter
    stream.filter(p)
    ● checks each value from
    stream against the
    predicate passed as p
    ● returns a new stream
    that outputs only values
    that satisfy condition p
    Already used:
    ● map(f)
    ● scan(init, f(acc, val))

    View Slide

  17. Devoxx UK / 09.06.2016
    @miciek
    “Left Rotations” stream

    View Slide

  18. Devoxx UK / 09.06.2016
    @miciek
    “Right Rotations” stream

    View Slide

  19. Devoxx UK / 09.06.2016
    @miciek
    “Actions” stream

    View Slide

  20. Devoxx UK / 09.06.2016
    @miciek
    Stream operators: merge
    stream.merge(stream2)
    ● stream is merged with
    stream2
    ● returns a new stream that
    outputs values from both
    stream and stream2 as
    they appear
    Already used:
    ● map(f)
    ● scan(init, f(acc, val))
    ● filter(p)

    View Slide

  21. Devoxx UK / 09.06.2016
    @miciek
    New “Directions” stream based on user input

    View Slide

  22. Devoxx UK / 09.06.2016
    @miciek
    Recap: Old “Directions” stream

    View Slide

  23. Devoxx UK / 09.06.2016
    @miciek
    “Directions” stream: Better, but no timing!
    vs

    View Slide

  24. Devoxx UK / 09.06.2016
    @miciek
    Stream operators: sampledBy
    stream.sampledBy(stream2)
    ● last value from stream is
    taken when any value
    appears in stream2
    ● returns a new stream that
    outputs values from
    stream sampled by values
    from stream2
    Already used:
    ● map(f)
    ● scan(init, f(acc, val))
    ● filter(p)
    ● merge(stream2)

    View Slide

  25. Devoxx UK / 09.06.2016
    @miciek
    “Directions” stream: user input & timing

    View Slide

  26. Devoxx UK / 09.06.2016
    @miciek
    “Snake Head Positions”: no change needed

    View Slide

  27. Devoxx UK / 09.06.2016
    @miciek
    User moves the snake: the code

    View Slide

  28. Devoxx UK / 09.06.2016
    @miciek
    Current implementation
    ● “ticks”
    ● “key presses”
    ● “left key presses”
    ● “right key presses”
    ● “left rotations”
    ● “right rotations”
    ● “actions”
    ● “dir changes”
    ● “directions”
    ● “snake head
    positions”
    + map, scan,
    filter, merge &
    sampledBy
    10 streams

    View Slide

  29. Devoxx UK / 09.06.2016
    @miciek
    “Snakes” stream

    View Slide

  30. Devoxx UK / 09.06.2016
    @miciek
    Stream operators: slidingWindow
    stream.slidingWindow(n)
    ● aggregate last n values
    ● returns a new stream that
    outputs arrays of last n
    values from the stream
    ● can be implemented using
    scan
    Already used:
    ● map(f)
    ● scan(init, f(acc, val))
    ● filter(p)
    ● merge(stream2)
    ● sampledBy(stream2)

    View Slide

  31. Devoxx UK / 09.06.2016
    @miciek
    Quiz: Eating a fruit?
    using 6 operators
    ● map(f)
    ● scan(init, f(acc, val))
    ● filter(p)
    ● merge(stream2)
    ● sampledBy(stream2)
    ● slidingWindow(n)
    & 11 streams
    ● “ticks”
    ● “key presses”
    ● “left key presses”
    ● “right key presses”
    ● “left rotations”
    ● “right rotations”
    ● “actions”
    ● “direction changes”
    ● “directions”
    ● “snake head positions”
    ● “snakes”

    View Slide

  32. Devoxx UK / 09.06.2016
    @miciek
    “Fruit eaten events” stream

    View Slide

  33. Devoxx UK / 09.06.2016
    @miciek
    Frontend in action!

    View Slide

  34. Devoxx UK / 09.06.2016
    @miciek
    Architecture: 11 streams, 2 ins & 2 outs

    View Slide

  35. Devoxx UK / 09.06.2016
    @miciek
    Snake’s going multiplayer

    View Slide

  36. Devoxx UK / 09.06.2016
    @miciek
    Snake Server App Requirements
    ● client must send snake positions to the server each time it
    changes
    ● server must generate fruits
    ● server must keep scores
    ● server must broadcast:
    ○ positions of all snakes to other players
    ○ positions of fruits to all players
    ○ current scoreboard to all players

    View Slide

  37. Devoxx UK / 09.06.2016
    @miciek
    Let’s design Server App Flow!
    one player flow Game Event

    View Slide

  38. Devoxx UK / 09.06.2016
    @miciek
    WebSocket connected to the Flow

    View Slide

  39. Devoxx UK / 09.06.2016
    @miciek
    WebSocket as a stream: Server App

    View Slide

  40. Devoxx UK / 09.06.2016
    @miciek
    Snake App Flow (one player)

    View Slide

  41. Devoxx UK / 09.06.2016
    @miciek
    Let’s implement Server Flow for one player

    View Slide

  42. Devoxx UK / 09.06.2016
    @miciek
    Moved to server: “Fruit eaten events” stream

    View Slide

  43. Devoxx UK / 09.06.2016
    @miciek
    “Fruits” stream

    View Slide

  44. Devoxx UK / 09.06.2016
    @miciek
    “Fruits” stream using Akka Stream

    View Slide

  45. Devoxx UK / 09.06.2016
    @miciek
    “Score updates” stream

    View Slide

  46. Devoxx UK / 09.06.2016
    @miciek
    “Scores” stream

    View Slide

  47. Devoxx UK / 09.06.2016
    @miciek
    “Scores” stream using Akka Stream

    View Slide

  48. Devoxx UK / 09.06.2016
    @miciek
    Game Logic Server Flow

    View Slide

  49. Devoxx UK / 09.06.2016
    @miciek
    Game Logic Flow using Akka Stream

    View Slide

  50. Devoxx UK / 09.06.2016
    @miciek
    Merging Player Flows outputs

    View Slide

  51. Devoxx UK / 09.06.2016
    @miciek
    Server app flow using Akka Stream

    View Slide

  52. Devoxx UK / 09.06.2016
    @miciek
    Backend in action!

    View Slide

  53. Devoxx UK / 09.06.2016
    @miciek
    What if there are thousands of players?

    View Slide

  54. Devoxx UK / 09.06.2016
    @miciek
    Crossing Async Boundary
    Push Model
    ● not safe when
    Subscriber slower
    ● perfect when
    Subscriber faster
    ● too slow when
    Subscriber faster
    ● perfect when
    Subscriber slower
    Pull Model

    View Slide

  55. Devoxx UK / 09.06.2016
    @miciek
    Reactive Streams
    Streams
    with
    supply & demand
    ● “as fast as possible, but not faster”
    ● consumer wants to pull elements (= demand)
    ● only then publisher can push
    ○ it supplies elements until demand is satisfied

    View Slide

  56. Devoxx UK / 09.06.2016
    @miciek
    Reactive Streams
    Lack of demand
    =
    Backpressure

    View Slide

  57. Devoxx UK / 09.06.2016
    @miciek
    Example of Reactive Streams

    View Slide

  58. Devoxx UK / 09.06.2016
    @miciek
    Reactive Streams

    View Slide

  59. Devoxx UK / 09.06.2016
    @miciek
    Reactive Streams API
    public interface Publisher {
    public void subscribe(Subscriber super T> s);
    }
    public interface Subscriber {
    public void onSubscribe(Subscription s);
    public void onNext(T t);
    public void onError(Throwable t);
    public void onComplete();
    }
    public interface Subscription {
    public void request(long n);
    public void cancel();
    }
    public interface Processor extends Subscriber, Publisher {}

    View Slide

  60. Devoxx UK / 09.06.2016
    @miciek
    Simple API, hard implementation...
    https://github.com/reactive-streams/reactive-streams-jvm/blob/v1.0.0/README.md#specification

    View Slide

  61. Devoxx UK / 09.06.2016
    @miciek
    Reactive Streams API implementations
    ● Project Reactor
    ● Ratpack
    ● Vert.x
    ● Slick
    ● Akka Stream
    ● JDK 9
    ● Integrations:
    ○ Kafka,
    ○ Mongo,
    ○ Cassandra

    View Slide

  62. Devoxx UK / 09.06.2016
    @miciek
    Akka Stream model
    Publisher => Source
    Processor => Flow
    Subscriber => Sink
    Our Snake implementation uses
    Reactive Streams!

    View Slide

  63. Devoxx UK / 09.06.2016
    @miciek
    ● NO
    ○ frontend is pushing every 100ms
    ● Possible solutions:
    ○ Reactive Streams web frameworks?
    ○ conflate/expand?
    ○ buffer between frontend & backend?
    Is Snake Multiplayer safe?

    View Slide

  64. Devoxx UK / 09.06.2016
    @miciek
    Links
    Build YOUR OWN Snake using Streams - blog post (step-by-step tutorial)
    michalplachta.com/2016/05/11/reactive-ui-by-example/
    Client Side Code github.com/miciek/web-snake-react-bacon
    Server Side Code github.com/miciek/snake-multiplayer-akka-streams
    Play with streams and operators! rxmarbles.com/
    Akka Streams doc.akka.io/docs/akka/2.4.4/scala/stream/
    BaconJS - streams in JavaScript baconjs.github.io/

    View Slide

  65. Devoxx UK / 09.06.2016
    @miciek
    Building Snake using Streams
    Michał
    Płachta
    THANK
    YOU
    www.michalplachta.com

    View Slide