Felix departed
[galtack.git] / galtack / net_common.py
blob8dcf04aab05ee3a48cd78cadf598417641ee60df
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3 """
4 GalTacK networking - code common to all the GalTacK networking.
6 Currently this consists of data structures.
7 """
8 # Copyright (C) 2007 Felix Rabe <public@felixrabe.textdriven.com>
9 # Copyright (C) 2007 Michael Carter
11 # Permission is hereby granted, free of charge, to any person obtaining a
12 # copy of this software and associated documentation files (the
13 # "Software"), to deal in the Software without restriction, including
14 # without limitation the rights to use, copy, modify, merge, publish,
15 # distribute, sublicense, and/or sell copies of the Software, and to permit
16 # persons to whom the Software is furnished to do so, subject to the
17 # following conditions:
19 # The above copyright notice and this permission notice shall be included
20 # in all copies or substantial portions of the Software.
22 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
23 # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
25 # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
26 # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
27 # OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
28 # THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 # Recommended line length or text width: 75 characters.
32 import copy
35 class TableRowData(object):
36 """
37 Base class for rows of tables.
39 Actually, it is very easy to use. Take the *Info classes further down
40 this file as a starting point.
41 """
43 # COL_SPEC example to document the structure - override in derived
44 # classes:
45 # (You can use TableRowData.build_col_spec_from_string() to build
46 # COL_SPEC and COL_SPEC_DICT automatically.)
47 COL_SPEC = [
49 "name": "user_name",
50 "type": str,
51 "caption": "User",
52 "visible": True,
53 "default": "No Name (default, optional)",
56 "name": "user_password",
57 "type": str,
58 "caption": "Password",
59 "visible": False,
60 "default": "", # unset
63 "name": "logged_in",
64 "type": bool,
65 "caption": "Logged In?",
66 "visible": False,
67 "default": False,
69 # ...
72 # COL_SPEC_DICT example to document the structure - override in derived
73 # classes:
74 # (You can use TableRowData.build_col_spec_dict() to build
75 # COL_SPEC_DICT automatically.)
76 COL_SPEC_DICT = {
77 "user_name": COL_SPEC[0],
78 "user_password": COL_SPEC[1],
79 "logged_in": COL_SPEC[2],
80 # ...
83 def __init__(self, *args, **kwargs):
84 """
85 Initialize in a user-friendly way.
87 Examples:
88 TableRowData("Peter").user_name == "Peter"
89 TableRowData(user_name = "Peter")["user_name"] == "Peter"
90 """
91 self.__data = {}
92 for i, arg in enumerate(args):
93 spec = self.COL_SPEC[i]
94 self.__data[spec["name"]] = spec["type"](arg)
95 for spec in self.COL_SPEC:
96 n = spec["name"]
97 t = spec["type"]
98 if n in kwargs:
99 self.__data[n] = t(kwargs.pop(n))
100 elif n not in self.__data:
101 self.__data[n] = t(spec["default"])
102 super(TableRowData, self).__init__(self, **kwargs)
104 def __copy__(self):
106 Implement copy.copy(TableRowData_instance) properly.
108 row_data = self.__class__()
109 for key, value in self.__data.iteritems():
110 row_data[key] = copy.copy(value)
111 return row_data
113 @staticmethod
114 def build_col_spec_from_string(string):
116 Build COL_SPEC and COL_SPEC_DICT by parsing a string.
118 Return: (COL_SPEC, COL_SPEC_DICT)
120 Example:
121 TableRowData.build_col_spec_from_string(
123 user_name; str; User; No Name (default, optional)
124 user_password; str; (Password)
125 logged_in; bool; (Logged In?); False
127 ) == (TableRowData.COL_SPEC, TableRowData.COL_SPEC_DICT)
129 lines = string.strip().split("\n")
130 lines = map(lambda line:
131 map( lambda i: i.strip(), line.split(";") ),
132 lines)
133 col_spec = []
134 for line in lines:
135 # "name"
136 name = line[0]
137 # "type"
138 type = __builtins__[line[1]]
139 # "caption"
140 caption = line[2]
141 # "visible"
142 visible = True
143 if caption[0] == "(" and caption[-1] == ")":
144 caption = caption[1:-1]
145 visible = False
146 # "default"
147 default = type()
148 if len(line) > 3:
149 if type == str:
150 default = line[-1]
151 else:
152 default = type(eval(line[-1]))
153 spec_dict = {
154 "name": name,
155 "type": type,
156 "caption": caption,
157 "visible": visible,
158 "default": default,
160 col_spec.append(spec_dict)
161 return (col_spec, TableRowData.build_col_spec_dict(col_spec))
163 @staticmethod
164 def build_col_spec_dict(col_spec):
166 Build COL_SPEC_DICT from COL_SPEC.
168 Return: COL_SPEC_DICT
170 Example:
171 TableRowData.build_col_spec_dict(
172 TableRowData.COL_SPEC
173 ) == TableRowData.COL_SPEC_DICT
175 col_spec_dict = {}
176 for spec in col_spec:
177 col_spec_dict[spec["name"]] = spec
178 return col_spec_dict
180 def get_data_tuple(self):
182 Get a tuple containing all the data.
184 tup = []
185 for spec in self.COL_SPEC:
186 tup.append(self.__data[spec["name"]])
187 return tuple(tup)
189 def get_visible_data_tuple(self):
191 Get a tuple containing all the visible data.
193 tup = []
194 for i, x in enumerate(self.get_tuple()):
195 if self.COL_SPEC[i]["visible"] == True:
196 tup.append(x)
197 return tuple(tup)
199 @classmethod
200 def get_col_types(cls):
202 Get a tuple containing all the data types.
204 return tuple(s["type"] for s in cls.COL_SPEC)
206 @classmethod
207 def get_visible_col_types(cls):
209 Get a tuple containing all the visible data types.
211 tup = []
212 for i, x in enumerate(cls.get_types()):
213 if cls.COL_SPEC[i]["visible"] == True:
214 tup.append(x)
215 return tuple(tup)
217 def __getitem__(self, name):
219 Make self["user_name"] and self[0] work.
221 i = None
222 try:
223 i = int(name)
224 except: pass
225 if i is not None:
226 spec = self.COL_SPEC[i]
227 return self.__data[spec["name"]]
228 try:
229 return self.__data[name]
230 except:
231 raise AttributeError, name
233 def __getattr__(self, name):
235 Make self.user_name work.
237 return self.__getitem__(name)
239 def _get_field_spec(self, name_or_index_or_spec):
241 Return the COL_SPEC entry for the given name or index.
243 You can pass a COL_SPEC entry as well if you called
244 self._get_field_spec before.
246 if isinstance(name_or_index_or_spec, dict):
247 return name_or_index_or_spec # spec
248 i = None
249 try:
250 i = int(name_or_index_or_spec)
251 except: pass
252 if i is not None:
253 return self.COL_SPEC[i] # index
254 name = name_or_index_or_spec
255 if name not in self.__data:
256 raise AttributeError, "unknown field name '%s'" % name
257 return self.COL_SPEC_DICT[name] # name
259 def __setitem__(self, spec, value):
261 Implement self["password"] (or self[1]) = "my0dear1secret".
263 spec = self._get_field_spec(spec)
264 self.__data[spec["name"]] = spec["type"](value)
266 def __setattr__(self, name, value):
268 Make self.password = 'my0dear1secret' work.
270 if name == "_TableRowData__data":
271 return super(TableRowData, self).__setattr__(name, value)
272 return self.__setitem__(name, value)
274 def format(self, use_caption = True):
276 Format this row of data for human eyes.
278 lines = []
279 n = use_caption and "caption" or "name"
280 maxlen = max(len(s[n]) for s in self.COL_SPEC)
281 for spec in self.COL_SPEC:
282 lines.append(("%%-%us%%r" % (maxlen + 2)) %
283 (spec[n] + ":",
284 self.__data[spec["name"]]))
285 lines.append("")
286 return "\n".join(lines)
289 class ServerInfo(TableRowData):
291 Information about a GalTacK server.
294 COL_SPEC, COL_SPEC_DICT = TableRowData.build_col_spec_from_string("""
295 bots_ok; bool; (Bots Ok)
296 allowed; str; Ranks
297 owner; str; Owner
298 version; str; Version
299 num; int; Players
300 name; str; Name
301 pwd_protected; bool; Protected
302 status; str; (Status)
303 addr; str; (IP Address)
304 port; int; (Port); 9031
305 secret; str; (Secret Hash)
306 passwd; str; (Password)
307 """)
310 class UserInfo(TableRowData):
312 Information about a GalTacK user.
315 COL_SPEC, COL_SPEC_DICT = TableRowData.build_col_spec_from_string("""
316 email; str; Email
317 name; str; Name
318 passwd; str; Password
319 platform; str; Platform; linux2
320 version; str; Version; 1.2.0
321 """)
324 class PlayerPlayersInfo(TableRowData):
326 Information about a GalTacK player.
329 COL_SPEC, COL_SPEC_DICT = TableRowData.build_col_spec_from_string("""
330 num; int; Number
331 name; str; Name
332 junk1; str; Junk 1
333 junk2; str; Junk 2
334 state; str; State
335 junk3; str; Junk 3
336 junk4; str; Junk 4
337 color; str; Color
338 """)
341 class PlayerStartInfo(TableRowData):
343 In-game information about a GalTacK player.
346 COL_SPEC, COL_SPEC_DICT = TableRowData.build_col_spec_from_string("""
347 num; int; Number
348 name; str; Name
349 junk1; str; Junk 1
350 color; str; Color
351 """)
354 class OptionB64Info(TableRowData):
356 In-game, base64-encoded information about GalTacK "options".
359 COL_SPEC, COL_SPEC_DICT = TableRowData.build_col_spec_from_string("""
360 scale; float; Scale
361 junk2; float; Junk 2
362 junk3; float; Junk 3
363 junk4; float; Junk 4
364 junk5; float; Junk 5
365 junk6; float; Junk 6
366 """)
369 class PlayerB64Info(TableRowData):
371 In-game, base64-encoded information about a GalTacK player.
374 COL_SPEC, COL_SPEC_DICT = TableRowData.build_col_spec_from_string("""
375 num; int; Number
376 name; str; Name
377 junk1; str; Junk 1
378 color; str; Color
379 """)
382 class PlanetB64Info(TableRowData):
384 (In-game) information about a GalTacK planet.
387 COL_SPEC, COL_SPEC_DICT = TableRowData.build_col_spec_from_string("""
388 num; int; Number; 0
389 x; int; X Position; 0
390 y; int; Y Position; 0
391 owner_num; int; Owner (Number); 0
392 prod; int; Production; 0
393 troops; int; Troops; 0
394 junk1; str; Junk 1; 0
395 junk2; str; Junk 2; 0
396 junk3; str; Junk 3; 0
397 junk4; str; Junk 4; 0
398 junk5; str; Junk 5; 0
399 junk6; str; Junk 6; 0
400 junk7; str; Junk 7; 0
401 junk8; str; Junk 8; 0
402 junk9; str; Junk 9; 0
403 radius; float; (Radius); 0.0
404 level_scale; float; (Level Scale); 1.0
405 dispx; int; (Displayed X Pos.); -1
406 dispy; int; (Displayed Y Pos.); -1
407 """)
409 def __init__(self, *a, **kw):
410 super(PlanetB64Info, self).__init__(*a, **kw)
411 self.prod = self.prod # triggers __setitem__ (from __setattr__)
413 def __setitem__(self, spec, value):
414 spec = self._get_field_spec(spec)
415 super(PlanetB64Info, self).__setitem__(spec, value)
416 name = spec["name"]
417 if name == "prod" or name == "level_scale":
418 self.radius = int( (12 + self.prod / 10) * self.level_scale )