import os import sys import threading import json import getpass import argparse import string from queue import Queue import ratuil.inputs from .inputhandler import InputHandler from .listselector import ListSelector from .switchselector import SwitchSelector class Client: def __init__(self, display, name, connection, keybindings, logFile=None): #self.stdscr = stdscr self.display = display self.name = name self.keepalive = True self.connection = connection self.logFile = logFile self.closeMessage = None # temporary, until these have a better place self.inventory = ListSelector(self.display.getWidget("inventory")) self.equipment = ListSelector(self.display.getWidget("equipment")) self.ground = ListSelector(self.display.getWidget("ground")) self.switch = SwitchSelector(self.display.getWidget("switch")) #self.switch.setItems([ #(self.inventory, None, "Inventory"), #(self.equipment, None, "Equipment"), #(self.ground, None, "Ground") #]) self.inputHandler = InputHandler(self, keybindings["actions"]) self.controlsString = keybindings.get("help", "") self.display.showInfo(self.controlsString) self.queue = Queue() def send(self, data): text = json.dumps(data) self.connection.send(text) def start(self): threading.Thread(target=self.listen, daemon=True).start() threading.Thread(target=self.getInput, daemon=True).start() self.connection.send(json.dumps(["name", self.name])) self.command_loop() def listen(self): self.connection.listen(self.pushMessage, self.onConnectionError) def pushMessage(self, databytes): self.queue.put(("message", databytes)) def onConnectionError(self, error): self.queue.put(("error", error)) def getInput(self): #try: while True: key = ratuil.inputs.get_key() #key = self.stdscr.getch() self.queue.put(("input", key)) #except Exception as e: #self.queue.put(("error", e)) def close(self, msg=None): self.keepalive = False self.closeMessage = msg def update(self, databytes): if len(databytes) == 0: self.close("Connection closed by server") return datastr = databytes.decode('utf-8') data = json.loads(datastr) if len(data) and isinstance(data[0], str): data = [data] for msg in data: msgType = msg[0] if msgType == 'error': error = msg[1] if error == "nametaken": self.close("error: name is already taken") return if error == "invalidname": self.close("error: "+ msg[2]) return self.log(error) if msgType == 'field': field = msg[1] fieldWidth = field['width'] fieldHeight = field['height'] self.display.resizeField((fieldWidth, fieldHeight)) fieldCells = field['field'] mapping = field['mapping'] self.display.drawFieldCells( ( tuple(reversed(divmod(i, fieldWidth))), mapping[spr] ) for i, spr in enumerate(fieldCells)) if msgType == 'changecells' and len(msg[1]): self.display.drawFieldCells(msg[1]) if msgType == "playerpos": self.display.setFieldCenter(msg[1]) if msgType == "health": health, maxHealth = msg[1] self.display.setHealth(health, maxHealth) if maxHealth is None: self.log("You have died. Restart the client to respawn") if msgType == "inventory": self.inventory.setItems(msg[1]) #invbox = self.display.getWidget("inventory") #invbox.setInventory(self.inventory.items) #invbox.select(self.inventory.selector) #self.display.setInventory(msg[1]) if msgType == "equipment": #self.display.setEquipment(msg[1]) self.equipment.setItems(msg[1]) #eqbox = self.display.getWidget("equipment") #eqbox.setInventory(self.inventory.items) #eqbox.select(self.equipment.selector) if msgType == "ground": #self.display.setGround(msg[1]) self.ground.setItems(msg[1]) #grbox = self.display.getWidget("ground") #grbox.setInventory(self.ground.items) #grbox.select(self.ground.selector) if msgType == "message": self.log(*msg[1:]) if msgType == "options": if msg[1] != None: description, options = msg[1] self.log(description) for option in options: self.log(option) def log(self, text, type=None): if not isinstance(text, str): text = str(text) self.display.addMessage(text, type) if self.logFile: with(open(self.logFile, 'a')) as f: f.write("[{}] {}\n".format(type or "", text)) def command_loop(self): while self.keepalive: self.display.update() action = self.queue.get() if action[0] == "message": self.update(action[1]) elif action[0] == "input": if action[1] == "^C": raise KeyboardInterrupt self.inputHandler.onInput(action[1]) elif action[0] == "error": raise action[1] elif action[0] == "sigwinch": self.display.update_size() else: raise Exception("invalid action in queue") def onSigwinch(self, signum, frame): self.queue.put(("sigwinch", (signum, frame)))