1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
|
import re
import unicodedata
import json
class InvalidMessageError(Exception):
errType = "invalidmessage"
description = ""
def __init__(self, description="", errType=None):
self.description = description
if errType is not None:
self.errType = errType
def toMessage(self):
return ErrorMessage(self.errType, self.description)
class InvalidNameError(InvalidMessageError):
errType = "invalidname"
class Message:
@classmethod
def msgType(cls):
return cls.typename
def to_json(self):
raise NotImplementedError
def to_json_bytes(self):
return bytes(json.dumps(self.to_json()), "utf-8")
@classmethod
def from_json(cls, jsonobj):
raise NotImplementedError
class ClientToServerMessage(Message):
def body(self):
raise NotImplementedError
def to_json(self):
return [self.typename, self.body()]
@classmethod
def from_json(cls, jsonlist):
assert len(jsonlist) == 2, InvalidMessageError
typename, body = jsonlist
assert typename == cls.msgType(), InvalidMessageError
return cls(body)
class NameMessage(ClientToServerMessage):
typename = "name"
categories = {"Lu", "Ll", "Lt", "Lm", "Lo", "Nd", "Nl", "No", "Pc"}
def __init__(self, name):
assert isinstance(name, str), InvalidNameError("name must be a string")
assert (len(name) > 0), InvalidNameError("name needs at least one character")
assert (len(bytes(name, "utf-8")) <= 256), InvalidNameError("name may not be longer than 256 utf8 bytes")
for char in name if name[0] != "~" else name[1:]:
category = unicodedata.category(char)
assert category in self.categories, InvalidNameError("all name caracters must be in these unicode categories: " + "|".join(self.categories) + " (except the tilde in a tildename)")
self.name = name
def body(self):
return self.name
class InputMessage(ClientToServerMessage):
typename = "input"
def __init__(self, inp):
self.inp = inp
def body(self):
return self.inp
class ChatMessage(ClientToServerMessage):
typename = "chat"
def __init__(self, text):
assert isinstance(text, str), InvalidMessageError("chat message must be a string")
assert text.isprintable(), InvalidMessageError("chat messages may only contain printable unicode characters")
self.text = text
def body(self):
return self.text
class ServerToClientMessage(Message):
msglen = 0
@classmethod
def from_json(cls, jsonlist):
assert len(jsonlist) == cls.msglen, InvalidMessageError
assert jsonlist[0] == cls.msgType(), InvalidMessageError
return cls(*jsonlist[1:])
class MessageMessage(ServerToClientMessage): # this name feels stupid
""" A message to inform the client. This is meant to be read by the user"""
typename = "message"
msglen = 3
def __init__(self, text, type=""):
self.text = text
self.type = type
def to_json(self):
return [self.typename, self.text, self.type]
class WorldMessage(ServerToClientMessage):
""" A message about the world state """
typename = "world"
msglen = 2
def __init__(self, updates):
assert isinstance(updates, list), InvalidMessageError
self.updates = updates
def to_json(self):
return [self.typename, self.updates]
class ErrorMessage(ServerToClientMessage):
typename = "error"
msglen = 3
def __init__(self, errType, description=""):
self.errType = errType
self.description = description
def to_json(self):
return [self.typename, self.errType, self.description]
messages = {message.msgType(): message for message in [
NameMessage,
InputMessage,
ChatMessage,
WorldMessage,
ErrorMessage,
MessageMessage
]}
|