Akka HTTP Websocket, how to identify connections within an actor

I am working on a simple mulitplayer game in scala which I would like to open via websockets for JS clients.

Here is my WebsocketServer class

class WebsocketServer(actorRef: ActorRef, protocol: Protocol, system: ActorSystem, materializer: ActorMaterializer) extends Directives { val route = get { pathEndOrSingleSlash { handleWebSocketMessages(websocketFlow) } } def websocketFlow: Flow[Message, Message, Any] = Flow[Message] .map { case TextMessage.Strict(textMessage) => protocol.hydrate(textMessage) } .via(actorFlow) .map(event => TextMessage.Strict(protocol.serialize(event))) def actorFlow : Flow[Protocol.Message, Protocol.Event, Any] = { val sink = Flow[Protocol.Message] .to(Sink.actorRef[Protocol.Message](actorRef, Protocol.CloseConnection())) val source = Source.actorRef[Protocol.Event](1, OverflowStrategy.fail) .mapMaterializedValue(actor => actorRef ! Protocol.OpenConnection(actor)) Flow.fromSinkAndSource(sink, source) } } 

This is the simplified code of my actor, which should receive messages from the websocket server.

 class GameActor() extends Actor { private var connections: List[ActorRef] = List() override def receive: Receive = { case message: Protocol.OpenConnection => { this.connections = message.connection :: this.connections message.connection ! Protocol.ConnectionEstablished() } case message: Protocol.CloseConnection => { // how can I remove actor from this.connections ? } case message: Protocol.DoSomething => { // how can I identify from which connection this message came in? } } } 

So far, so good that I can currently respond with a simple WelcomeMessage client, but I still don't know how:

  • remove a member from the connection list whenever an actor receives a CloseConnection message?
  • identify which message connected to the actor?
+5
source share
2 answers

I think you need to have some kind of key or id to match the connection with your player.

 def websocketFlow: Flow[Message, Message, Any] = val randomKey = Random.nextInt() Flow[Message] .map { case TextMessage.Strict(textMessage) => protocol.hydrate(textMessage) } .via(actorFlow(randomKey)) .map(event => TextMessage.Strict(protocol.serialize(event))) def actorFlow(flowID: Int) : Flow[Protocol.Message, Protocol.Event, Any] = { val sink = Flow[Protocol.Message] .to(Sink.actorRef[Protocol.Message](actorRef, Protocol.CloseConnection(flowID))) val source = Source.actorRef[Protocol.Event](1, OverflowStrategy.fail) .mapMaterializedValue(actor => actorRef ! Protocol.OpenConnection(actor, flowID)) Flow.fromSinkAndSource(sink, source) } 

then in your actor you can save the connections on the map instead of the list, which will also be more efficient for deletion.

+2
source

This question has already been given. For Java people there is a java version:

 public class WebsocketRoutes extends AllDirectives { private final ActorSystem actorSystem; private final ActorRef connectionManager; public WebsocketRoutes(final ActorSystem actorSystem, final ActorRef connectionManager) { this.actorSystem = actorSystem; this.connectionManager = connectionManager; } public Route handleWebsocket() { return path(PathMatchers.segment(compile("router_v\\d+")).slash(PathMatchers.segment("websocket")).slash(PathMatchers.segment(compile("[^\\\\/\\s]+"))), (version, routerId) -> handleWebSocketMessages(createWebsocketFlow(routerId)) ); } private Flow<Message, Message, NotUsed> createWebsocketFlow(final String routerId) { final ActorRef connection = actorSystem.actorOf(WebsocketConnectionActor.props(connectionManager)); final Source<Message, NotUsed> source = Source.<RouterWireMessage.Outbound>actorRef(5, OverflowStrategy.fail()) .map((outbound) -> (Message) TextMessage.create(new String(outbound.message, "utf-8"))) .throttle(5, FiniteDuration.create(1, TimeUnit.SECONDS), 10, ThrottleMode.shaping()) .mapMaterializedValue(destinationRef -> { connection.tell(new RouterConnected(routerId, destinationRef), ActorRef.noSender()); return NotUsed.getInstance(); }); final Sink<Message, NotUsed> sink = Flow.<Message>create() .map((inbound) -> new RouterWireMessage.Inbound(inbound.asTextMessage().getStrictText().getBytes())) .throttle(5, FiniteDuration.create(1, TimeUnit.SECONDS), 10, ThrottleMode.shaping()) .to(Sink.actorRef(connection, PoisonPill.getInstance())); return Flow.fromSinkAndSource(sink, source); } } 
+2
source

Source: https://habr.com/ru/post/1260640/


All Articles