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

Building multiplayer game using Reactive Streams

Building multiplayer game using Reactive Streams

This talk introduces streams and reactive streams. 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

February 29, 2016
Tweet

More Decks by Michał Płachta

Other Decks in Programming

Transcript

  1. KSUG / Kraków / 29.02.2016
    @miciek
    Building Multiplayer Game
    using Streams and Reactive Streams
    Michał
    Płachta

    View Slide

  2. KSUG / Kraków / 29.02.2016
    @miciek
    Streams?
    ● way of defining application logic
    ● focused on data transformations
    ● unbounded collections
    ● have hardcoded time
    ● declarative

    View Slide

  3. KSUG / Kraków / 29.02.2016
    @miciek
    What you will learn?
    ● how frontend web app written in
    streams look like
    ● how backend app written using
    streams look like
    ● how do Reactive Streams keep our
    streams safe?

    View Slide

  4. KSUG / Kraków / 29.02.2016
    @miciek
    Meet Snake & Fruit
    Snake
    Fruit

    View Slide

  5. KSUG / Kraków / 29.02.2016
    @miciek
    Our First Stream: “Ticks” stream

    View Slide

  6. KSUG / Kraków / 29.02.2016
    @miciek
    “Directions” stream

    View Slide

  7. KSUG / Kraków / 29.02.2016
    @miciek
    “Snake’s Head Positions” stream

    View Slide

  8. KSUG / Kraków / 29.02.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 on each
    new value

    View Slide

  9. KSUG / Kraków / 29.02.2016
    @miciek
    Once again: the code

    View Slide

  10. KSUG / Kraków / 29.02.2016
    @miciek
    Displaying values from the stream

    View Slide

  11. KSUG / Kraków / 29.02.2016
    @miciek
    Streams are reusable

    View Slide

  12. KSUG / Kraków / 29.02.2016
    @miciek
    Streams & Operators are lazy
    No elements
    =
    I don’t do
    anything

    View Slide

  13. KSUG / Kraków / 29.02.2016
    @miciek
    “Key presses” stream

    View Slide

  14. KSUG / Kraków / 29.02.2016
    @miciek
    “Left Key Presses” stream

    View Slide

  15. KSUG / Kraków / 29.02.2016
    @miciek
    “Right Key Presses” stream

    View Slide

  16. KSUG / Kraków / 29.02.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. KSUG / Kraków / 29.02.2016
    @miciek
    “Left Rotations” stream

    View Slide

  18. KSUG / Kraków / 29.02.2016
    @miciek
    “Right Rotations” stream

    View Slide

  19. KSUG / Kraków / 29.02.2016
    @miciek
    “Actions” stream

    View Slide

  20. KSUG / Kraków / 29.02.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. KSUG / Kraków / 29.02.2016
    @miciek
    Recap: Current “Directions” stream

    View Slide

  22. KSUG / Kraków / 29.02.2016
    @miciek
    “Directions” stream based on user input

    View Slide

  23. KSUG / Kraków / 29.02.2016
    @miciek
    “Directions” stream: Better, but no timing!
    vs

    View Slide

  24. KSUG / Kraków / 29.02.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. KSUG / Kraków / 29.02.2016
    @miciek
    “Directions” stream: user input & timing

    View Slide

  26. KSUG / Kraków / 29.02.2016
    @miciek
    “Snake Head Positions”: no change needed

    View Slide

  27. KSUG / Kraków / 29.02.2016
    @miciek
    User moves the snake: the code

    View Slide

  28. KSUG / Kraków / 29.02.2016
    @miciek
    Current implementation
    10 streams
    ● “ticks”
    ● “key presses”
    ● “left key presses”
    ● “right key presses”
    ● “left rotations”
    ● “right rotations”
    ● “actions”
    ● “direction changes”
    ● “directions”
    ● “snake head positions”
    + map, scan, filter, merge &
    sampledBy

    View Slide

  29. KSUG / Kraków / 29.02.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

  30. KSUG / Kraków / 29.02.2016
    @miciek
    “Snakes” stream

    View Slide

  31. KSUG / Kraków / 29.02.2016
    @miciek
    Quiz: Eating a fruit?
    6 operators
    ● map(f)
    ● scan(init, f(acc, val))
    ● filter(p)
    ● merge(stream2)
    ● sampledBy(stream2)
    ● slidingWindow(n)
    WHICH STREAMS AND OPERATORS
    SHOULD WE REUSE TO
    IMPLEMENT “EATING A FRUIT”
    FUNCTIONALITY?
    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. KSUG / Kraków / 29.02.2016
    @miciek
    “Fruit eaten events” stream

    View Slide

  33. KSUG / Kraków / 29.02.2016
    @miciek
    Frontend in action!

    View Slide

  34. KSUG / Kraków / 29.02.2016
    @miciek
    Architecture: 11 streams, 2 ins & 2 outs

    View Slide

  35. KSUG / Kraków / 29.02.2016
    @miciek
    Snake’s going multiplayer

    View Slide

  36. KSUG / Kraków / 29.02.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. KSUG / Kraków / 29.02.2016
    @miciek
    Let’s design Server App Flow!
    one player flow

    View Slide

  38. KSUG / Kraków / 29.02.2016
    @miciek
    WebSocket connected to the Flow

    View Slide

  39. KSUG / Kraków / 29.02.2016
    @miciek
    WebSocket as a stream: Server App

    View Slide

  40. KSUG / Kraków / 29.02.2016
    @miciek
    Snake App Flow (one player)

    View Slide

  41. KSUG / Kraków / 29.02.2016
    @miciek
    Let’s implement Server Flow for one player

    View Slide

  42. KSUG / Kraków / 29.02.2016
    @miciek
    Moved to server: “Fruit eaten events” stream

    View Slide

  43. KSUG / Kraków / 29.02.2016
    @miciek
    “Fruits” stream

    View Slide

  44. KSUG / Kraków / 29.02.2016
    @miciek
    “Fruits” stream using Akka Stream

    View Slide

  45. KSUG / Kraków / 29.02.2016
    @miciek
    “Score updates” stream

    View Slide

  46. KSUG / Kraków / 29.02.2016
    @miciek
    “Scores” stream

    View Slide

  47. KSUG / Kraków / 29.02.2016
    @miciek
    “Scores” stream using Akka Stream

    View Slide

  48. KSUG / Kraków / 29.02.2016
    @miciek
    Game Logic Server Flow

    View Slide

  49. KSUG / Kraków / 29.02.2016
    @miciek
    Game Logic Flow using Akka Stream

    View Slide

  50. KSUG / Kraków / 29.02.2016
    @miciek
    Merging Player Flows outputs

    View Slide

  51. KSUG / Kraków / 29.02.2016
    @miciek
    Server app flow using Akka Stream

    View Slide

  52. KSUG / Kraków / 29.02.2016
    @miciek
    Backend in action!

    View Slide

  53. KSUG / Kraków / 29.02.2016
    @miciek
    What if there are thousands of players?

    View Slide

  54. KSUG / Kraków / 29.02.2016
    @miciek
    Crossing Async Boundary
    Push Model
    ● not safe when
    Subscriber slower
    ● perfect when
    Subscriber faster
    Pull Model
    ● too slow when
    Subscriber faster
    ● perfect when
    Subscriber slower

    View Slide

  55. KSUG / Kraków / 29.02.2016
    @miciek
    Reactive Streams
    Streams
    with
    supply & demand
    ● start in Pull Mode
    ● Consumer asks for elements (= demand)
    ● Publisher goes into Push Mode
    ● supplies elements until demand is satisfied

    View Slide

  56. KSUG / Kraków / 29.02.2016
    @miciek
    Reactive Streams
    Lack of demand
    =
    Backpressure

    View Slide

  57. KSUG / Kraków / 29.02.2016
    @miciek
    Example of Reactive Streams

    View Slide

  58. KSUG / Kraków / 29.02.2016
    @miciek
    Reactive Streams

    View Slide

  59. KSUG / Kraków / 29.02.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. KSUG / Kraków / 29.02.2016
    @miciek
    Simple API, hard implementation...
    https://github.com/reactive-streams/reactive-streams-jvm/blob/v1.0.0/README.md#specification

    View Slide

  61. KSUG / Kraków / 29.02.2016
    @miciek
    Reactive Streams API implementations
    ● Project Reactor
    ● Ratpack
    ● Vert.x
    ● Slick
    ● Akka Stream
    ● JDK 9
    ● Integrations:
    ○ Kafka,
    ○ Mongo,
    ○ Cassandra

    View Slide

  62. KSUG / Kraków / 29.02.2016
    @miciek
    Akka Stream model
    Publisher => Source
    Processor => Flow
    Subscriber => Sink
    Our Snake implementation uses
    Reactive Streams!

    View Slide

  63. KSUG / Kraków / 29.02.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. KSUG / Kraków / 29.02.2016
    @miciek
    Links
    github.com/miciek/web-snake-react-bacon Client Side Code
    github.com/miciek/snake-multiplayer-akka-streams Server Side Code
    rxmarbles.com/ Play with streams and operators!
    doc.akka.io/docs/akka-stream-and-http-experimental/2.0.3/ Akka Stream
    baconjs.github.io/ BaconJS - streams in JavaScript

    View Slide

  65. KSUG / Kraków / 29.02.2016
    @miciek
    Building Multiplayer Game
    using Streams and Reactive Streams
    Michał Płachta
    www.michalplachta.com
    THANK YOU!

    View Slide