summaryrefslogtreecommitdiff
path: root/asciifarmclient/gameclient.py
diff options
context:
space:
mode:
authortroido <troido@protonmail.com>2020-03-06 11:26:59 +0100
committertroido <troido@protonmail.com>2020-03-06 11:26:59 +0100
commitdd07ff4d686f07cdc9736627dd0ef099ef5e4e4f (patch)
treef43310b3779a95efd74a40ba8139cf146ad49051 /asciifarmclient/gameclient.py
parentc9366616079240cd7ee3d243c9f6897d40b4267d (diff)
new directory structure for the separate client repo
Diffstat (limited to 'asciifarmclient/gameclient.py')
-rw-r--r--asciifarmclient/gameclient.py173
1 files changed, 173 insertions, 0 deletions
diff --git a/asciifarmclient/gameclient.py b/asciifarmclient/gameclient.py
new file mode 100644
index 0000000..d677636
--- /dev/null
+++ b/asciifarmclient/gameclient.py
@@ -0,0 +1,173 @@
+
+
+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 asciifarmclient.common import messages
+
+class Client:
+
+ def __init__(self, display, name, connection, keybindings, logFile=None):
+
+ self.display = display
+ self.name = name
+ self.keepalive = True
+ self.connection = connection
+ self.logFile = logFile
+ self.closeMessage = None
+
+ self.inputHandler = InputHandler(self, keybindings["actions"])
+
+ self.controlsString = keybindings.get("help", "")
+
+ self.display.showInfo(self.controlsString)
+ self.queue = Queue()
+
+
+ def sendMessage(self, message):
+ self.connection.send(message.to_json_bytes())
+
+ def sendInput(self, inp):
+ message = messages.InputMessage(inp)
+ self.sendMessage(message)
+
+ def sendChat(self, text):
+ try:
+ self.sendMessage(messages.ChatMessage(text))
+ except messages.InvalidMessageError as e:
+ self.log(e.description)
+
+ def start(self):
+ self.sendMessage(messages.NameMessage(self.name))
+ threading.Thread(target=self.listen, daemon=True).start()
+ threading.Thread(target=self.getInput, daemon=True).start()
+
+ 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):
+ while True:
+ key = ratuil.inputs.get_key()
+ self.queue.put(("input", key))
+
+ 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')
+ msg = json.loads(datastr)
+ message = messages.messages[msg[0]].from_json(msg)
+ if isinstance(message, messages.ErrorMessage):
+ error = message.errType
+ if error == "nametaken":
+ self.close("error: name is already taken")
+ return
+ if error == "invalidname":
+ self.close("Invalid name error: "+ str(message.description))
+ return
+ self.log(message.errType + ": " + message.description)
+ elif isinstance(message, messages.MessageMessage):
+ self.log(message.text, message.type)
+ elif isinstance(message, messages.WorldMessage):
+ for msg in message.updates:
+ self.handleWorldUpdate(msg)
+
+ def handleWorldUpdate(self, msg):
+ msgType = msg[0]
+ 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.display.setInventory(msg[1])
+ if msgType == "equipment":
+ self.display.setEquipment(msg[1])
+ if msgType == "ground":
+ self.display.setGround(msg[1])
+ if msgType == "message":
+ type, text = msg[1][:2]
+ self.log(text, type)
+ 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)))
+
+
+
+