wot/client.py
Joscha 320dd16889 Add server
This is a first try at connecting multiple clients using a server.
The commit includes a lot of debugging messages.
I will hopefully clean up the server and some of the client code.
2017-04-13 18:10:37 +00:00

183 lines
4.9 KiB
Python

import curses
import json
import os
import string
import sys
import threading
import websocket
from websocket import WebSocketException as WSException
from maps import Map, ChunkMap
from chunks import ChunkDiff
from utils import Position
from clientchunkpool import ClientChunkPool
class Client():
def __init__(self, address):
self.stopping = False
self.chunkmap_active = False
self.address = f"ws://{address}/"
self._drawevent = threading.Event()
self.pool = ClientChunkPool(self)
#self.map_ = Map(sizex, sizey, self.pool)
#self.chunkmap = Chunkmap(sizex, sizey, self.pool) # size changeable by +/-?
#self.sock = socket.Socket(...)
def launch(self, stdscr):
# connect to server
try:
self._ws = websocket.create_connection(
self.address,
enable_multithread=True
)
except ConnectionRefusedError:
sys.stderr.write(f"Could not connect to server: {self.address!r}\n")
return
# create map etc.
sizey, sizex = stdscr.getmaxyx()
self.map_ = Map(sizex, sizey, self.pool, self)
self.chunkmap = ChunkMap(self.map_)
# start connection thread
self.connectionthread = threading.Thread(
target=self.connection_thread,
name="connectionthread"
)
self.connectionthread.start()
# start input thread
self.inputthread = threading.Thread(
target=self.input_thread,
name="inputthread",
args=(stdscr,),
daemon=True
)
self.inputthread.start()
# update screen until stopped
while not self.stopping:
self._drawevent.wait()
self._drawevent.clear()
stdscr.noutrefresh()
with self.map_ as m:
m.draw()
if self.chunkmap_active:
self.chunkmap.draw()
curses.curs_set(False)
else:
curses.curs_set(True)
#m.update_cursor()
#m.noutrefresh()
curses.doupdate()
def redraw(self):
self._drawevent.set()
def input_thread(self, scr):
while True:
i = scr.getkey()
if i == "\x1b": self.stop()
elif i == "KEY_F(2)":
self.chunkmap_active = not self.chunkmap_active
self.redraw()
elif i == "KEY_F(3)":
self.map_.alternating_colors = not self.map_.alternating_colors
self.redraw()
elif i == "KEY_F(5)": self.map_.redraw()
# scrolling the map (10 vertical, 20 horizontal)
elif i == "kUP5": self.map_.scroll(0, -10)
elif i == "kDN5": self.map_.scroll(0, 10)
elif i == "kLFT5": self.map_.scroll(-20, 0)
elif i == "kRIT5": self.map_.scroll(20, 0)
# break here if chunkmap is shown: Don't allow for cursor movement or input
elif self.chunkmap_active: pass
# quick cursor movement (5 vertical, 10 horizontal)
elif i == "KEY_SR": self.map_.move_cursor(0, -5)
elif i == "KEY_SF": self.map_.move_cursor(0, 5)
elif i == "KEY_SLEFT": self.map_.move_cursor(-10, 0)
elif i == "KEY_SRIGHT": self.map_.move_cursor(10, 0)
# normal cursor movement
elif i == "KEY_UP": self.map_.move_cursor(0, -1)
elif i == "KEY_DOWN": self.map_.move_cursor(0, 1)
elif i == "KEY_LEFT": self.map_.move_cursor(-1, 0)
elif i == "KEY_RIGHT": self.map_.move_cursor(1, 0)
# edit world
elif i in string.digits + string.ascii_letters + string.punctuation + " ":
self.map_.write(i)
elif i == "\x7f": self.map_.delete()
elif i == "\n": self.map_.newline()
else: sys.stderr.write(repr(i) + "\n")
def connection_thread(self):
while True:
try:
j = self._ws.recv()
self.handle_json(json.loads(j))
except (WSException, ConnectionResetError, OSError):
#self.stop()
return
def handle_json(self, message):
sys.stderr.write(f"message: {message}\n")
if message["type"] == "apply-changes":
changes = []
for chunk in message["data"]:
pos = Position(chunk[0][0], chunk[0][1])
change = ChunkDiff.from_dict(chunk[1])
changes.append((pos, change))
sys.stderr.write(f"Changes to apply: {changes}\n")
self.map_.apply_changes(changes)
def stop(self):
sys.stderr.write("Stopping!\n")
self.stopping = True
self._ws.close()
self.redraw()
def request_chunks(self, coords):
#sys.stderr.write(f"requested chunks: {coords}\n")
message = {"type": "request-chunks", "data": coords}
self._ws.send(json.dumps(message))
#def execute():
#changes = [(pos, ChunkDiff()) for pos in coords]
#with self.pool as pool:
#pool.apply_changes(changes)
#tx = threading.Timer(1, execute)
#tx.start()
def unload_chunks(self, coords):
#sys.stderr.write(f"unloading chunks: {coords}\n")
message = {"type": "unload-chunks", "data": coords}
self._ws.send(json.dumps(message))
def send_changes(self, changes):
#sys.stderr.write(f"sending changes: {changes}\n")
message = {"type": "save-changes", "data": changes}
self._ws.send(json.dumps(message))
def main(argv):
if len(argv) != 2:
print("Usage:")
print(f" {argv[0]} address")
return
os.environ.setdefault('ESCDELAY', '25') # only a 25 millisecond delay
client = Client(argv[1])
curses.wrapper(client.launch)
if __name__ == "__main__":
main(sys.argv)