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

Building multiplayer game using Streams v2

Building multiplayer game using Streams v2

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

September 21, 2016
Tweet

More Decks by Michał Płachta

Other Decks in Programming

Transcript

  1. michalplachta.com
    @miciek
    Building multiplayer game
    using Streams
    Michał
    Płachta

    View Slide

  2. michalplachta.com
    @miciek
    Streams
    Streams?
    way of defining application logic
    level of abstraction
    Threads Futures Actors

    View Slide

  3. michalplachta.com
    @miciek
    Streams?
    focused on data transformations
    Element
    Our
    Code
    New
    Element

    View Slide

  4. michalplachta.com
    @miciek
    Streams?
    explicit time dependencies
    time

    View Slide

  5. michalplachta.com
    @miciek
    Streams?
    declarative programming
    Functional
    developer’s sanity
    Imperative Declarative

    View Slide

  6. michalplachta.com
    @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
    async communication safe?

    View Slide

  7. michalplachta.com
    @miciek
    Meet Snake & Fruit
    Snake
    Fruit

    View Slide

  8. michalplachta.com
    @miciek
    Our First Stream: “Ticks” stream

    View Slide

  9. michalplachta.com
    @miciek
    “Directions” stream

    View Slide

  10. michalplachta.com
    @miciek
    “Snake’s Head Positions” stream

    View Slide

  11. michalplachta.com
    @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

  12. michalplachta.com
    @miciek
    Once again: the code

    View Slide

  13. michalplachta.com
    @miciek
    Displaying values from the stream

    View Slide

  14. michalplachta.com
    @miciek
    Streams are reusable

    View Slide

  15. michalplachta.com
    @miciek
    Streams & Operators are lazy
    No elements
    or
    No terminal
    function

    View Slide

  16. michalplachta.com
    @miciek
    “You call this a game?”

    View Slide

  17. michalplachta.com
    @miciek
    “Key presses” stream

    View Slide

  18. michalplachta.com
    @miciek
    “Left Key Presses” stream

    View Slide

  19. michalplachta.com
    @miciek
    “Right Key Presses” stream

    View Slide

  20. michalplachta.com
    @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

  21. michalplachta.com
    @miciek
    “Left Rotations” stream

    View Slide

  22. michalplachta.com
    @miciek
    “Right Rotations” stream

    View Slide

  23. michalplachta.com
    @miciek
    “Actions” stream

    View Slide

  24. michalplachta.com
    @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

  25. michalplachta.com
    @miciek
    New “Directions” stream based on user input

    View Slide

  26. michalplachta.com
    @miciek
    Recap: Old “Directions” stream

    View Slide

  27. michalplachta.com
    @miciek
    “Directions” stream: Better, but no timing!
    vs

    View Slide

  28. michalplachta.com
    @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

  29. michalplachta.com
    @miciek
    “Directions” stream: user input & timing

    View Slide

  30. michalplachta.com
    @miciek
    “Snake Head Positions”: no change needed

    View Slide

  31. michalplachta.com
    @miciek
    User moves the snake: the code

    View Slide

  32. michalplachta.com
    @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

  33. michalplachta.com
    @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

  34. michalplachta.com
    @miciek
    “Snakes” stream

    View Slide

  35. michalplachta.com
    @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

  36. michalplachta.com
    @miciek
    “Fruit eaten events” stream

    View Slide

  37. michalplachta.com
    @miciek
    Frontend in action!

    View Slide

  38. michalplachta.com
    @miciek
    Architecture: 11 streams, 2 ins & 2 outs

    View Slide

  39. michalplachta.com
    @miciek
    Snake’s going multiplayer
    FINALLY
    SOME SCALA CODE

    View Slide

  40. michalplachta.com
    @miciek
    Snake Server App Requirements
    ● clients send snake positions to the server
    ● server generates fruits
    ● server keeps scores
    ● server broadcasts:
    ○ positions of all snakes
    ○ positions of fruits
    ○ current scoreboard

    View Slide

  41. michalplachta.com
    @miciek
    Let’s design Server App Flow!
    one player flow Game Event

    View Slide

  42. michalplachta.com
    @miciek
    WebSocket connected to the Flow

    View Slide

  43. michalplachta.com
    @miciek
    WebSocket as a stream: Server App

    View Slide

  44. michalplachta.com
    @miciek
    Snake App Flow (one player)

    View Slide

  45. michalplachta.com
    @miciek
    Moved to server: “Fruit eaten events” stream

    View Slide

  46. michalplachta.com
    @miciek
    “Fruits” stream

    View Slide

  47. michalplachta.com
    @miciek
    “Fruits” stream using Akka Stream

    View Slide

  48. michalplachta.com
    @miciek
    Snake App Flow (one player)

    View Slide

  49. michalplachta.com
    @miciek
    “Score updates” stream

    View Slide

  50. michalplachta.com
    @miciek
    “Scores” stream

    View Slide

  51. michalplachta.com
    @miciek
    “Scores” stream using Akka Stream

    View Slide

  52. michalplachta.com
    @miciek
    Snake App Flow (one player)

    View Slide

  53. michalplachta.com
    @miciek
    Game Logic Server Flow

    View Slide

  54. michalplachta.com
    @miciek
    Game Logic Flow using Akka Stream

    View Slide

  55. michalplachta.com
    @miciek
    Snake App Flow (one player)

    View Slide

  56. michalplachta.com
    @miciek
    Merging Player Flows outputs

    View Slide

  57. michalplachta.com
    @miciek
    Server app flow using Akka Stream

    View Slide

  58. michalplachta.com
    @miciek
    Backend in action!

    View Slide

  59. michalplachta.com
    @miciek
    What if there are thousands of players?

    View Slide

  60. michalplachta.com
    @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

  61. michalplachta.com
    @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

  62. michalplachta.com
    @miciek
    Reactive Streams
    Lack of demand
    =
    Backpressure

    View Slide

  63. michalplachta.com
    @miciek
    Example of Reactive Streams

    View Slide

  64. michalplachta.com
    @miciek
    Reactive Streams

    View Slide

  65. michalplachta.com
    @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

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

    View Slide

  67. michalplachta.com
    @miciek
    Reactive Streams API implementations
    ● Project Reactor
    ● Ratpack
    ● Vert.x
    ● Slick
    ● Akka Stream
    ● JDK 9
    ● Integrations:
    ○ Kafka,
    ○ Mongo,
    ○ Cassandra

    View Slide

  68. michalplachta.com
    @miciek
    Akka Stream model
    Publisher => Source
    Processor => Flow
    Subscriber => Sink
    Our Snake implementation uses
    Reactive Streams!

    View Slide

  69. michalplachta.com
    @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

  70. michalplachta.com
    @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.9/scala/stream/
    BaconJS - streams in JavaScript baconjs.github.io/

    View Slide

  71. michalplachta.com
    @miciek
    Michał
    Płachta
    THANK
    YOU
    www.michalplachta.com

    View Slide