Upgrade to Pro — share decks privately, control downloads, hide ads and more …

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


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

  2. michalplachta.com @miciek Streams Streams? way of defining application logic level

    of abstraction Threads Futures Actors
  3. michalplachta.com @miciek Streams? focused on data transformations Element Our Code

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

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

  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?
  7. michalplachta.com @miciek Meet Snake & Fruit Snake Fruit

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

  9. michalplachta.com @miciek “Directions” stream

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

  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
  12. michalplachta.com @miciek Once again: the code

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

  14. michalplachta.com @miciek Streams are reusable

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

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

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

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

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

  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))
  21. michalplachta.com @miciek “Left Rotations” stream

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

  23. michalplachta.com @miciek “Actions” stream

  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)
  25. michalplachta.com @miciek New “Directions” stream based on user input

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

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

  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)
  29. michalplachta.com @miciek “Directions” stream: user input & timing

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

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

  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
  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)
  34. michalplachta.com @miciek “Snakes” stream

  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”
  36. michalplachta.com @miciek “Fruit eaten events” stream

  37. michalplachta.com @miciek Frontend in action!

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

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

  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
  41. michalplachta.com @miciek Let’s design Server App Flow! one player flow

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

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

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

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

  46. michalplachta.com @miciek “Fruits” stream

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

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

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

  50. michalplachta.com @miciek “Scores” stream

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

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

  53. michalplachta.com @miciek Game Logic Server Flow

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

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

  56. michalplachta.com @miciek Merging Player Flows outputs

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

  58. michalplachta.com @miciek Backend in action!

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

  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
  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
  62. michalplachta.com @miciek Reactive Streams Lack of demand = Backpressure

  63. michalplachta.com @miciek Example of Reactive Streams

  64. michalplachta.com @miciek Reactive Streams

  65. michalplachta.com @miciek Reactive Streams API public interface Publisher<T> { public

    void subscribe(Subscriber<? super T> s); } public interface Subscriber<T> { 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<T, R> extends Subscriber<T>, Publisher<R> {}
  66. michalplachta.com @miciek Simple API, hard implementation... https://github.com/reactive-streams/reactive-streams-jvm/blob/v1.0.0/README.md#specification

  67. michalplachta.com @miciek Reactive Streams API implementations • Project Reactor •

    Ratpack • Vert.x • Slick • Akka Stream • JDK 9 • Integrations: ◦ Kafka, ◦ Mongo, ◦ Cassandra
  68. michalplachta.com @miciek Akka Stream model Publisher => Source Processor =>

    Flow Subscriber => Sink Our Snake implementation uses Reactive Streams!
  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?
  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/
  71. michalplachta.com @miciek Michał Płachta THANK YOU www.michalplachta.com