async def _process_event(self, row_i: int, col_i: int, event: ZoneEvent, socket: web.WebSocketResponse) -> None: try: event_processor = self._event_processor_factory.get_processor( event.type) except UnknownEvent: server_logger.warning( f"Unknown received event type '{event.type}'") return try: await event_processor.process(row_i, col_i, event, sender_socket=socket) except UnableToProcessEvent as exc: server_logger.debug( f"Unable to process event {event.type}: {str(exc)}") exception_event = exc.event exception_event_str = self._event_serializer_factory.get_serializer( exception_event.type).dump_json(exception_event) # FIXME: do kept this feature ? await socket.send_str(exception_event_str)
async def _listen(self, socket: web.WebSocketResponse, row_i: int, col_i: int) -> None: server_logger.info(f"Listen websocket for zone {row_i},{col_i}") async for msg in socket: server_logger.debug( f"Receive message on websocket for zone {row_i},{col_i}: {msg}" ) if msg.type == aiohttp.WSMsgType.ERROR: server_logger.error( f"Zone websocket closed with exception {socket.exception()}" ) else: try: await self._process_msg(row_i, col_i, msg, socket) except DisconnectClient: await socket.send_str( self._event_serializer_factory.get_serializer( ZoneEventType.SERVER_PERMIT_CLOSE).dump_json( ZoneEvent( type=ZoneEventType.SERVER_PERMIT_CLOSE, data=EmptyData()))) return server_logger.info(f"Websocket of zone {row_i},{col_i} closed")
async def get_new_socket(self, request: Request, row_i: int, col_i: int) -> web.WebSocketResponse: server_logger.info(f"Create websocket for zone {row_i},{col_i}") # Create socket socket = web.WebSocketResponse() await socket.prepare(request) # TODO BS 2019-01-23: Implement a heartbeat to close sockets where client disapear # see https://github.com/aio-libs/aiohttp/issues/961#issuecomment-239647597 # Something lik asyncio.ensure_future(self._heartbeat(ws)) # Make it available for send job self._sockets.setdefault((row_i, col_i), []).append(socket) # Start to listen client messages try: await self._listen(socket, row_i, col_i) except CancelledError: server_logger.debug(f"websocket ({row_i},{col_i}) seems cancelled") # If this code reached: ws is disconnected server_logger.debug(f"remove websocket ({row_i},{col_i})") self._sockets[(row_i, col_i)].remove(socket) return socket
async def _process( self, row_i: int, col_i: int, event: ZoneEvent[ClickActionData], sender_socket: web.WebSocketResponse, ) -> None: # FIXME Experimental way to identify action ...for now, only manage build ... path = event.data.base_url.split("?")[0] parts = path.split("/") character_id: str = parts[2] build_description_id: str = parts[5] character = self._kernel.character_lib.get(character_id) # FIXME BS: use dicts instead list for action descriptions description = next( ad for ad in self._kernel.game.config.actions[ActionType.BUILD] if ad.id == build_description_id ) action = self._kernel.action_factory.get_build_action(description) input_ = action.input_model_serializer.load(event.data.to_dict()) try: action.check_request_is_possible(character, input_) zone_events, sender_events = action.perform_from_event(character, input_) except ImpossibleAction as exc: # TODO BS: send event error server_logger.error(f"impossible action {build_description_id}: {str(exc)}") return for zone_event in zone_events: event_str = self._event_serializer_factory.get_serializer(zone_event.type).dump_json( zone_event ) for socket in self._zone_events_manager.get_sockets(row_i, col_i): server_logger.debug(f"Send event on socket: {event_str}") try: await socket.send_str(event_str) except Exception as exc: server_logger.exception(exc) for sender_event in sender_events: event_str = self._event_serializer_factory.get_serializer(sender_event.type).dump_json( sender_event ) server_logger.debug(f"Send event on socket: {event_str}") await sender_socket.send_str(event_str)
async def _process( self, row_i: int, col_i: int, event: ZoneEvent[PlayerMoveData], sender_socket: web.WebSocketResponse, ) -> None: # FIXME BS 2019-01-23: Check what move is possible (tile can be a rock, or water ...) # TODO BS 2019-01-23: Check given character id is authenticated used (security) # FIXME BS 2020-07-04: Check there is no build with traversable false # FIXME BS 2020-07-04: check move is just near previous position + refuse move event character = self._character_lib.get(event.data.character_id) self._character_lib.move_on_zone( character, to_row_i=event.data.to_row_i, to_col_i=event.data.to_col_i ) event_str = self._event_serializer_factory.get_serializer(event.type).dump_json(event) for socket in self._zone_events_manager.get_sockets(row_i, col_i): server_logger.debug(f"Send event on socket: {event_str}") try: await socket.send_str(event_str) except Exception as exc: server_logger.exception(exc)
def build_from_validation_error( self, error: ProcessValidationError) -> DefaultErrorSchema: server_logger.debug(str(error)) return super().build_from_validation_error(error)