From: Christian Heller Date: Sat, 7 Nov 2020 23:42:35 +0000 (+0100) Subject: Make curses client capable of websocket _and_ raw tcp connections. X-Git-Url: https://plomlompom.com/repos/%7B%7Bprefix%7D%7D/template?a=commitdiff_plain;h=81d69377ef8309ffdfda6a744a7375006521f29e;p=plomrogue2-experiments Make curses client capable of websocket _and_ raw tcp connections. --- diff --git a/new2/plomrogue/io_tcp.py b/new2/plomrogue/io_tcp.py index f0a49a9..b030f1b 100644 --- a/new2/plomrogue/io_tcp.py +++ b/new2/plomrogue/io_tcp.py @@ -6,6 +6,7 @@ socketserver.TCPServer.allow_reuse_address = True +from plomrogue.errors import BrokenSocketConnection class PlomSocket: def __init__(self, socket): @@ -32,7 +33,6 @@ class PlomSocket: """ - from plomrogue.errors import BrokenSocketConnection escaped_message = '' for char in message: if char in ('\\', '$'): @@ -77,7 +77,11 @@ class PlomSocket: data = b'' msg = b'' while True: - data = self.socket.recv(1024) + try: + data = self.socket.recv(1024) + except OSError as err: + if err.errno == 9: # "Bad file descriptor", when connection broken + raise BrokenSocketConnection if 0 == len(data): break for c in data: diff --git a/new2/requirements.txt b/new2/requirements.txt index 4cf00d1..7d7d093 100644 --- a/new2/requirements.txt +++ b/new2/requirements.txt @@ -1 +1,2 @@ SimpleWebSocketServer +ws4py diff --git a/new2/rogue_chat_curses.py b/new2/rogue_chat_curses.py index bb3dac2..689ecc1 100755 --- a/new2/rogue_chat_curses.py +++ b/new2/rogue_chat_curses.py @@ -1,14 +1,49 @@ #!/usr/bin/env python3 import curses -import socket import queue import threading -from plomrogue.io_tcp import PlomSocket from plomrogue.game import GameBase from plomrogue.parser import Parser from plomrogue.mapping import YX, MapGeometrySquare, MapGeometryHex from plomrogue.things import ThingBase from plomrogue.misc import quote +from plomrogue.errors import BrokenSocketConnection + +from ws4py.client import WebSocketBaseClient +class WebSocketClient(WebSocketBaseClient): + + def __init__(self, recv_handler, *args, **kwargs): + super().__init__(*args, **kwargs) + self.recv_handler = recv_handler + self.connect() + + def received_message(self, message): + if message.is_text: + message = str(message) + self.recv_handler(message) + + @property + def plom_closed(self): + return self.client_terminated + +from plomrogue.io_tcp import PlomSocket +class PlomSocketClient(PlomSocket): + + def __init__(self, recv_handler, url): + import socket + self.recv_handler = recv_handler + host, port = url.split(':') + super().__init__(socket.create_connection((host, port))) + + def close(self): + self.socket.close() + + def run(self): + try: + for msg in self.recv(): + self.recv_handler(msg) + except BrokenSocketConnection: + pass # we assume socket will be known as dead by now def cmd_TURN(game, n): game.turn = n @@ -73,9 +108,7 @@ def cmd_GAME_STATE_COMPLETE(game): game.tui.query_info() player = game.get_thing(game.player_id, False) if player.position in game.portals: - #host, port = game.portals[player.position].split(':') game.tui.teleport_target_host = game.portals[player.position] - game.tui.teleport_target_port = 5000 game.tui.switch_mode('teleport') game.turn_complete = True game.tui.do_refresh = True @@ -152,11 +185,10 @@ class TUI: self.shows_info = shows_info self.is_intro = is_intro - def __init__(self, host, port): + def __init__(self, host): import os import json self.host = host - self.port = port self.mode_play = self.Mode('play') self.mode_study = self.Mode('study', shows_info=True) self.mode_edit = self.Mode('edit') @@ -206,8 +238,10 @@ class TUI: def send(self, msg): try: + if hasattr(self.socket, 'plom_closed') and self.socket.plom_closed: + raise BrokenSocketConnection self.socket.send(msg) - except BrokenPipeError: + except (BrokenPipeError, BrokenSocketConnection): self.log_msg('@ server disconnected :(') self.do_refresh = True @@ -232,8 +266,7 @@ class TUI: else: self.log_msg('@ enter username') elif self.mode.name == 'teleport': - self.log_msg("@ May teleport to %s:%s" % (self.teleport_target_host, - self.teleport_target_port)); + self.log_msg("@ May teleport to %s" % (self.teleport_target_host)), self.log_msg("@ Enter 'YES!' to enthusiastically affirm."); elif self.mode.name == 'annotate' and self.explorer in self.game.info_db: info = self.game.info_db[self.explorer] @@ -262,6 +295,7 @@ class TUI: self.log_msg(" %s - switch to play mode" % self.keys['switch_to_play']); def loop(self, stdscr): + import time def safe_addstr(y, x, line): if y < self.size.y - 1 or x + len(line) < self.size.x: @@ -275,19 +309,20 @@ class TUI: stdscr.addstr(y, x, cut) def connect(): - import time - def recv_loop(): - for msg in self.socket.recv(): - if msg == 'BYE': - break + def handle_recv(msg): + if msg == 'BYE': + self.socket.close() + else: self.queue.put(msg) + socket_client_class = PlomSocketClient + if self.host.startswith('ws://') or self.host.startswith('wss://'): + socket_client_class = WebSocketClient while True: try: - s = socket.create_connection((self.host, self.port)) - self.socket = PlomSocket(s) - self.socket_thread = threading.Thread(target=recv_loop) + self.socket = socket_client_class(handle_recv, self.host) + self.socket_thread = threading.Thread(target=self.socket.run) self.socket_thread.start() self.switch_mode('login') return @@ -299,6 +334,8 @@ class TUI: def reconnect(): self.send('QUIT') + time.sleep(0.1) # FIXME necessitated by some some strange SSL race + # conditions with ws4py, find out what exactly self.switch_mode('waiting_for_server') connect() @@ -520,7 +557,6 @@ class TUI: elif self.mode == self.mode_teleport and key == '\n': if self.input_ == 'YES!': self.host = self.teleport_target_host - self.port = self.teleport_target_port reconnect() else: self.log_msg('@ teleport aborted') @@ -548,8 +584,11 @@ class TUI: self.send('TASK:FLATTEN_SURROUNDINGS') elif key in self.movement_keys: self.send('TASK:MOVE ' + self.movement_keys[key]) + elif key == 'q': + self.log_msg('quitting') + self.send('QUIT') elif self.mode == self.mode_edit: self.send('TASK:WRITE ' + key) self.switch_mode('play') -TUI('127.0.0.1', 5000) +TUI('127.0.0.1:5000')