galtack_loadable.py: back to MIT license (thanks Phil)
[galtack.git] / galtack / net_client_universe.py
blob092598cdacd94f56db6ff04488e71556b893ab7d
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3 """
4 GalTacK networking - client-side universe bookkeeping.
5 """
6 # Copyright (C) 2007 Felix Rabe <public@felixrabe.textdriven.com>
7 # Copyright (C) 2007 Michael Carter
9 # Permission is hereby granted, free of charge, to any person obtaining a
10 # copy of this software and associated documentation files (the
11 # "Software"), to deal in the Software without restriction, including
12 # without limitation the rights to use, copy, modify, merge, publish,
13 # distribute, sublicense, and/or sell copies of the Software, and to permit
14 # persons to whom the Software is furnished to do so, subject to the
15 # following conditions:
17 # The above copyright notice and this permission notice shall be included
18 # in all copies or substantial portions of the Software.
20 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21 # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
23 # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
24 # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
25 # OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
26 # THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 # Recommended line length or text width: 75 characters.
30 import base64
31 import zlib
33 from galtack.net_client_base import *
36 class GaltackClientPlayerTrackerMixin(GaltackClientBackCallerMixin):
37 """
38 GaltackClient that keeps track of players.
39 """
41 def __init__(self, *a, **kw):
42 super(GaltackClientPlayerTrackerMixin, self).__init__(*a, **kw)
43 self.player_info_dict = {}
44 self.player_ingame_info_dict = {}
45 self.register_command_callback("players", self.__cmd_players)
46 self.register_command_callback("start", self.__cmd_start)
47 self.register_command_callback("stop", self.__cmd_stop)
49 def __cmd_players(self, command):
50 recv_players = command[3:]
51 self.player_info_dict = {}
52 for player in recv_players:
53 s = player.split(',')
54 self.player_info_dict[s[0]] = PlayerPlayersInfo(*s)
56 def __cmd_start(self, command):
57 self.player_ingame_info_dict = {}
58 for player in command[3].split(';'):
59 s = player.split(',')
60 self.player_ingame_info_dict[s[0]] = PlayerStartInfo(*s)
62 def __cmd_stop(self, command):
63 self.player_ingame_info_dict = {}
66 class GaltackClientGameTrackerMixin(GaltackClientBackCallerMixin):
67 """
68 GaltackClient that keeps track of the game state (running or not).
69 """
71 def __init__(self, *a, **kw):
72 super(GaltackClientGameTrackerMixin, self).__init__(*a, **kw)
73 self.game_running = False
74 self.register_command_callback("start", self.__cmd_start)
75 self.register_command_callback("stop", self.__cmd_stop)
77 def __cmd_start(self, command):
78 self.game_running = True
80 def __cmd_stop(self, command):
81 self.game_running = False
84 class UniverseB64Data(object):
85 """
86 Core data structure representing the universe data encoded in Base64,
87 and nothing more.
88 """
90 LINE_LEN = {
91 "version": 2,
92 "options": 7,
93 "user": 5,
94 "planet": 16,
97 def __init__(self, b64string = None):
98 self._reset_stuff()
99 if b64string is not None:
100 self._raw_data = self.__decode(b64string)
102 for line in (li.split("\t") for li in self._raw_data.split("\n")):
103 if line[0] == "planet":
104 self.planets.append(PlanetB64Info(*line[1:]))
105 continue
106 if line[0] == "user":
107 self.users.append(PlayerB64Info(*line[1:]))
108 continue
109 if line[0] == "options":
110 self.options = OptionB64Info(*line[1:])
111 continue
112 if line[0] == "version":
113 self.version = int(line[1])
114 continue
115 if len(line) == 1 and line[0] == "":
116 continue
117 raise Exception, "unrecognized format"
119 def _reset_stuff(self):
120 self._raw_data = ""
121 self._version_updated = False
122 self.version = None
123 self.options = None
124 self.users = []
125 self.planets = []
127 def __copy__(self):
128 d = UniverseB64Data()
129 d._raw_data = copy.copy(self._raw_data)
130 d._version_updated = copy.copy(self._version_updated)
131 d.version = copy.copy(self.version)
132 d.options = copy.copy(self.options)
133 d.users = map(copy.copy, self.users)
134 d.planets = map(copy.copy, self.planets)
135 return d
137 def update(self, b64string):
138 self._raw_data = self.__decode(b64string)
139 planet_index = 0
140 for line in (li.split("\t") for li in self._raw_data.split("\n")):
141 if len(line) == self.LINE_LEN["planet"]:
142 p = self.planets[planet_index]
143 for i, field in enumerate(line[1:]):
144 if not field: continue
145 p[i] = field
146 planet_index += 1
147 continue
148 if any(line):
149 raise Exception, "unrecognized format %r" % line
151 @staticmethod
152 def __decode(b64string):
153 return zlib.decompress(base64.b64decode(b64string))
156 class GaltackClientUniverseTrackerMixin(GaltackClientBackCallerMixin):
158 GaltackClient that keeps its idea of the GalTacK universe up-to-date.
161 def __init__(self, *a, **kw):
162 super(GaltackClientUniverseTrackerMixin, self).__init__(*a, **kw)
163 self.u64data = None
164 self.register_command_callback("start", self.__cmd_start)
165 self.register_command_callback("delta", self.__cmd_delta)
166 self.register_command_callback("stop", self.__cmd_stop)
168 @staticmethod
169 def __decode(data):
170 return zlib.decompress(base64.b64decode(data))
172 def __cmd_start(self, command):
173 if len(command) < 4: return None
174 self.players_start = [l.split(",") for l in command[3].split(";")]
175 if len(command) < 6: return None
176 self.u64data = UniverseB64Data(command[5])
177 return None
179 def __cmd_delta(self, command):
180 if self.u64data is None:
181 print "whoops, no u64data"
182 return None
183 self.u64data.update(command[3])
185 def __cmd_stop(self, command):
186 self.u64data = None