1 # ###################################################
2 # Copyright (C) 2008 The OpenAnno Team
4 # This file is part of OpenAnno.
6 # OpenAnno is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the
18 # Free Software Foundation, Inc.,
19 # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 # ###################################################
25 from game
.network
import Socket
26 from game
.packets
import QueryPacket
, InfoPacket
30 re_ip_port
= re
.compile("^((?:[0-1]?[0-9]{1,2}|2(?:[0-4][0-9]|5[0-5]))[.](?:[0-1]?[0-9]{1,2}|2(?:[0-4][0-9]|5[0-5]))[.](?:[0-1]?[0-9]{1,2}|2(?:[0-4][0-9]|5[0-5]))[.](?:[0-1]?[0-9]{1,2}|2(?:[0-4][0-9]|5[0-5])))(?::((?:[0-5]?[0-9]{1,4}|6(?:[0-4][0-9]{3}|5(?:[0-4][0-9]{2}|5(?:[0-2][0-9]|3[0-5]))))))?$")
32 """This represents a server in the serverlist
33 @param address: the address
34 @param port: and the port :)
36 def __init__(self
, address
, port
= None):
38 match
= Server
.re_ip_port
.match(address
)
40 address
= match
.group(1)
41 port
= match
.group(2) or game
.main
.settings
.network
.port
43 port
= game
.main
.settings
.network
.port
44 self
.address
= address
46 self
.ping
, self
.map, self
.players
, self
.bots
, self
.maxplayers
, self
.timeLastQuery
, self
.timeLastResponse
= None, None, None, None, None, None, None
48 def __eq__(self
, other
):
49 """check if the server is the same (only address & port have to match)
50 @param other: the other one
52 return self
.address
== other
.address
and self
.port
== other
.port
55 """A nice representation we can show in the list
57 info
= ['timeout' if self
.ping
is None else 'ping: ' + str(self
.ping
) + 'ms']
58 if self
.map is not None:
59 info
.append('map: ' + self
.map)
60 if self
.players
is not None or self
.maxplayers
is not None or self
.bots
is not None:
61 info
.append('players: ' + str(self
.players
) + '+' + str(self
.bots
) + '/' + str(self
.maxplayers
))
62 return str(self
.address
) + ':' + str(self
.port
) + ' (' + ', '.join(info
) + ')'
64 class ServerList(object):
68 """A basic Serverlist, should be extended to implement more functionality
72 self
._socket
= Socket()
73 self
._socket
.receive
= self
._response
74 game
.main
.ext_scheduler
.add_new_object(self
._pump
, self
, self
.queryIntervall
, -1)
79 game
.main
.ext_scheduler
.rem_all_classinst_calls(self
)
80 self
._socket
.receive
= lambda x
: None
84 """internal function, regularly called to ping the contained servers etc
87 if server
.ping
is not None and int(server
.timeLastResponse
or 0) + self
.__class
__.queryTimeout
<= server
.timeLastQuery
:
90 self
._query
(server
.address
, server
.port
)
94 """remove all servers from the list
99 def _add(self
, server
):
100 """add a server to the list
101 @param server: the server to add
104 self
._servers
.remove(server
)
105 self
._servers
.append(server
)
108 def _remove(self
, server
):
109 """remove a server from the list
110 @param server: the server to remove
112 self
._servers
.remove(server
)
114 def _query(self
, address
, port
):
115 """query(ask for an InfoPacket) a address port combination and update the corresponding server
116 @param address: the address to query
117 @param port: and the port
119 tmp_server
= Server(address
, port
)
121 if server
== tmp_server
:
122 server
.timeLastQuery
= time
.time()
123 self
._request
(address
, port
)
125 def _request(self
, address
, port
):
126 """query(ask for an InfoPacket) a address port combination
127 @param address: the address to query
128 @param port: and the port
130 self
._socket
.send(QueryPacket(address
, port
))
132 def _response(self
, packet
):
133 """internal function, called when a server responded
134 @param packet: the received packet
136 if not isinstance(packet
, InfoPacket
):
139 if server
.address
== packet
.address
and server
.port
== packet
.port
:
140 server
.timeLastResponse
= time
.time()
141 server
.ping
= int(round(((server
.timeLastResponse
- server
.timeLastQuery
) * 1000)))
142 server
.map, server
.players
, server
.bots
, server
.maxplayers
= packet
.map, packet
.players
, packet
.bots
, packet
.maxplayers
146 """callback, called when the list changed
150 def __getitem__(self
, *args
, **kwargs
): return self
._servers
.__getitem
__(*args
, **kwargs
)
151 def __getslice__(self
, *args
, **kwargs
): return self
._servers
.__getslice
__(*args
, **kwargs
)
152 def __iter__(self
, *args
, **kwargs
): return self
._servers
.__iter
__(*args
, **kwargs
)
153 def __len__(self
, *args
, **kwargs
): return self
._servers
.__len
__(*args
, **kwargs
)
154 def __contains__(self
, *args
, **kwargs
): return self
._servers
.__contains
__(*args
, **kwargs
)
156 class WANServerList(ServerList
):
159 """a specialized serverlist, gets its content from an url
162 super(WANServerList
, self
).__init
__()
164 if self
.__class
__.updateIntervall
> 0:
165 game
.main
.ext_scheduler
.add_new_object(self
.update
, self
, self
.updateIntervall
, -1)
170 game
.main
.ext_scheduler
.rem_all_classinst_calls(self
)
171 super(WANServerList
, self
).end()
174 """manually update the serverlist from the url
180 wan_servers
= (urllib
.urlopen(game
.main
.settings
.network
.url_servers
))
182 game
.main
.showPopup("Network Error", "Error: "+e
.strerror
[1])
184 for server
in wan_servers
:
185 match
= Server
.re_ip_port
.match(server
)
187 server
= Server(match
.group(1), match
.group(2) or game
.main
.settings
.network
.port
)
188 if not server
in self
:
190 self
._query
(server
.address
, server
.port
)
192 class LANServerList(ServerList
):
195 """a serverlist which regularly searches the lan for servers (sends a broadcast)
198 super(LANServerList
, self
).__init
__()
200 if self
.__class
__.updateIntervall
> 0:
201 game
.main
.ext_scheduler
.add_new_object(self
.update
, self
, self
.updateIntervall
, -1)
206 game
.main
.ext_scheduler
.rem_all_classinst_calls(self
)
207 super(LANServerList
, self
).end()
210 """manually update the list, search for servers (send the broadcast)
213 server
.timeLastQuery
= time
.time()
214 self
._request
('255.255.255.255', game
.main
.settings
.network
.port
)
215 #self._request('192.168.2.255', game.main.settings.network.port)
217 def _response(self
, packet
):
218 """overwritten function of the base class, ensures that the server is in the list when a packet is received
219 @param packet: the packet
221 if not isinstance(packet
, InfoPacket
):
224 if server
.address
== packet
.address
and server
.port
== packet
.port
:
227 self
._add
(Server(packet
.address
, packet
.port
))
228 self
._query
(packet
.address
, packet
.port
)
229 super(LANServerList
, self
)._response
(packet
)
231 class FavoriteServerList(ServerList
):
232 """a specialzed serverlist, which holds a static set of servers and just regularly updates them
235 super(FavoriteServerList
, self
).__init
__()
236 for serverstr
in game
.main
.settings
.network
.favorites
:
237 server
= Server(serverstr
)
239 self
._query
(server
.address
, server
.port
)
245 self
._query
(server
.address
, server
.port
)
247 def add(self
, serverstr
):
249 @param serverstr: a string in "address[:port]" notion
251 server
= Server(serverstr
)
253 self
._query
(server
.address
, server
.port
)
254 game
.main
.settings
.network
.favorites
= game
.main
.settings
.network
.favorites
+ [serverstr
]
256 def remove(self
, serverstr
):
258 @param serverstr: a string in "address[:port]" notion
260 self
._remove
(Server(serverstr
))
261 favorites
= game
.main
.settings
.network
.favorites
262 favorites
.remove(serverstr
)
263 game
.main
.settings
.network
.favorites
= favorites
266 """make the list empty
269 game
.main
.settings
.network
.favorites
= []