More work on universe display
[galtack.git] / galtack / net_client_universe.py
blob6aaf2b0fab0e47f6fd03c4773f6aadc44e97d75d
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 self.options = None
103 for line in (li.split("\t") for li in self._raw_data.split("\n")):
104 if line[0] == "planet":
105 self.planets.append(PlanetB64Info(*line[1:]))
106 if self.options is not None:
107 self.planets[-1].level_scale = self.options.scale
108 continue
109 if line[0] == "user":
110 self.users.append(PlayerB64Info(*line[1:]))
111 continue
112 if line[0] == "options":
113 self.options = OptionB64Info(*line[1:])
114 continue
115 if line[0] == "version":
116 self.version = int(line[1])
117 continue
118 if len(line) == 1 and line[0] == "":
119 continue
120 raise Exception, "unrecognized format"
122 def _reset_stuff(self):
123 self._raw_data = ""
124 self._version_updated = False
125 self.version = None
126 self.options = None
127 self.users = []
128 self.planets = []
130 def __copy__(self):
131 d = UniverseB64Data()
132 d._raw_data = copy.copy(self._raw_data)
133 d._version_updated = copy.copy(self._version_updated)
134 d.version = copy.copy(self.version)
135 d.options = copy.copy(self.options)
136 d.users = map(copy.copy, self.users)
137 d.planets = map(copy.copy, self.planets)
138 return d
140 def update(self, b64string):
141 self._raw_data = self.__decode(b64string)
142 planet_index = 0
143 for line in (li.split("\t") for li in self._raw_data.split("\n")):
144 if len(line) == self.LINE_LEN["planet"]:
145 p = self.planets[planet_index]
146 for i, field in enumerate(line[1:]):
147 if not field: continue
148 p[i] = field
149 planet_index += 1
150 continue
151 if any(line):
152 raise Exception, "unrecognized format %r" % line
154 @staticmethod
155 def __decode(b64string):
156 return zlib.decompress(base64.b64decode(b64string))
159 class GaltackClientUniverseTrackerMixin(GaltackClientBackCallerMixin):
161 GaltackClient that keeps its idea of the GalTacK universe up-to-date.
164 def __init__(self, *a, **kw):
165 super(GaltackClientUniverseTrackerMixin, self).__init__(*a, **kw)
166 self.u64data = None
167 self.register_command_callback("start", self.__cmd_start)
168 self.register_command_callback("delta", self.__cmd_delta)
169 self.register_command_callback("stop", self.__cmd_stop)
171 @staticmethod
172 def __decode(data):
173 return zlib.decompress(base64.b64decode(data))
175 def __cmd_start(self, command):
176 if len(command) < 4: return None
177 self.players_start = [l.split(",") for l in command[3].split(";")]
178 if len(command) < 6: return None
179 self.u64data = UniverseB64Data(command[5])
180 return None
182 def __cmd_delta(self, command):
183 if self.u64data is None:
184 print "whoops, no u64data"
185 return None
186 self.u64data.update(command[3])
188 def __cmd_stop(self, command):
189 self.u64data = None