summaryrefslogtreecommitdiff
path: root/asciifarm/common/messages.py
blob: d3aed9a0e0af7a0dadff65a50f15370d24df4a79 (plain)
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
157

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")
        if name[0] != "~":
            for char in name:
                category = unicodedata.category(char)
                assert category in self.categories, InvalidNameError("all name caracters must be in these unicode categories: " + "|".join(self.categories) + " (except for tildenames)")
        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
]}