~alaxa27/ultimate-smash-friends/mirror_trunk

« back to all changes in this revision

Viewing changes to pkg/ultimate-smash-friends_1.0-beta-1/usr/lib/usf_modules/network.py

  • Committer: gaby
  • Date: 2009-11-30 17:03:16 UTC
  • Revision ID: gaby@ks22672.kimsufi.com-20091130170316-6lm3v7q0torulfab
adding code package

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
################################################################################
 
2
# copyright 2008 Gabriel Pettier <gabriel.pettier@gmail.com>                   #
 
3
#                                                                              #
 
4
# This file is part of ultimate-smash-friends                                  #
 
5
#                                                                              #
 
6
# ultimate-smash-friends is free software: you can redistribute it and/or      #
 
7
# modify it under the terms of the GNU General Public License as published by  #
 
8
# the Free Software Foundation, either version 3 of the License, or (at your   #
 
9
# option) any later version.                                                   #
 
10
#                                                                              #
 
11
# ultimate-smash-friends is distributed in the hope that it will be useful, but#
 
12
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or#
 
13
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for    #
 
14
# more details.                                                                #
 
15
#                                                                              #
 
16
# You should have received a copy of the GNU General Public License along with #
 
17
# ultimate-smash-friends.  If not, see <http://www.gnu.org/licenses/>.         #
 
18
################################################################################
 
19
 
 
20
# standart imports
 
21
import socket
 
22
from threading import Thread, Semaphore
 
23
from time import sleep, time
 
24
 
 
25
# my imports
 
26
from config import config
 
27
from debug_utils import LOG
 
28
 
 
29
def test_client():
 
30
    a=Client()
 
31
    b=Client()
 
32
    a.connect(nick='1', character='stick-tiny', votemap='baselevel')
 
33
    b.connect(nick='2', character='stick-red', votemap='maryoland')
 
34
 
 
35
class NetworkError (RuntimeError):
 
36
    pass
 
37
 
 
38
key_dict = {
 
39
                'U': 'UP',
 
40
                'D': 'DOWN',
 
41
                'L': 'LEFT',
 
42
                'R': 'RIGHT',
 
43
                'A': 'A',
 
44
                'B': 'B'
 
45
           }
 
46
 
 
47
MSGLEN = 2
 
48
 
 
49
def server_thread(client_socket, server, num, game_instance):
 
50
    """
 
51
    This fonction is called by the serverside to deal with a connected client,
 
52
    it will translate recieved messages to the game object.
 
53
 
 
54
    """
 
55
    prec_time = time()
 
56
    while not server.quit:
 
57
        # 10 network updates per seconds is already a lot.
 
58
        while time() < prec_time + 0.10:
 
59
            sleep(.02)
 
60
        prec_time = time()
 
61
        # we use Fixed Lenght (2 chars) messages, as we know this is always a
 
62
        # player number on the client + a key (A, B, U=UP, D=Down, L=Left,
 
63
        # R=Right)
 
64
        msg = ''
 
65
        while len(msg) < MSGLEN:
 
66
            chunk = client_socket.recv(MSGLEN - len(msg))
 
67
            if chunk == '':
 
68
                LOG().log('error client, link broken')
 
69
                LOG().log(msg)
 
70
                server.quit = True
 
71
                raise NetworkError
 
72
            else:
 
73
                msg += chunk
 
74
        if msg == 'AU' and game_instance.accepting:
 
75
            auth_msg = ''
 
76
            while True:
 
77
                chunk = client_socket.recv(5)
 
78
                if chunk == '':
 
79
                    LOG().log('error client, link broken')
 
80
                    LOG().log(msg)
 
81
                    raise NetworkError
 
82
                else:
 
83
                    msg += chunk
 
84
 
 
85
                if msg[-1] == '':
 
86
                    player = server.new_player_from_string(msg[2:])
 
87
                    server.players[num] = (
 
88
                                            player['nick']+
 
89
                                            ','+
 
90
                                            os.sep.join((
 
91
                                                           'characters',
 
92
                                                           player['character']
 
93
                                                        ))
 
94
                                          )
 
95
 
 
96
                    server.votemap[num]=player['votemap']
 
97
 
 
98
                    break;
 
99
 
 
100
        else:
 
101
            key = 'PL' + str(num * 10 + int(msg[0])) + '_'
 
102
            # FIXME: maybe save this dict somewhere as a class property.
 
103
            key += key_dict[msg[1]]
 
104
 
 
105
            # As messages is a shared resource we need to lock it.
 
106
            server.lock.acquire()
 
107
            server.messages.append(key)
 
108
            server.lock.release()
 
109
 
 
110
class Client(object):
 
111
    """
 
112
    Using this class, a client will send any input event from the player on this
 
113
    machine, and will recieve an acknowledgment in response.
 
114
 
 
115
    """
 
116
    def __init__(self, sock=None):
 
117
        self.lock = Semaphore()
 
118
        self.messages = ''
 
119
        if sock is None:
 
120
            self.socket = socket.socket( socket.AF_INET, socket.SOCK_STREAM )
 
121
            LOG().log('client socket created')
 
122
        else:
 
123
            self.socket = sock
 
124
 
 
125
    def connect(self, host="127.0.0.1", port=config['NETWORK_PORT'], nick='',
 
126
                character='stick-tiny', votemap='', listening=True):
 
127
        """
 
128
        This attempt to connect our client socket to the server socket and
 
129
        send all information to server, about who we are and who we play.
 
130
        (there may be more than one player an a client and the server need to
 
131
        known what caracters theses players plays.
 
132
 
 
133
        this fonction accept a dict of values like.
 
134
        {'nick': ..., 'character':..., 'votemap': ...}
 
135
 
 
136
        the stream sent is terminated with a ^D character ().
 
137
 
 
138
 
 
139
        """
 
140
        LOG().log('connecting to game server')
 
141
        try:
 
142
            self.socket.connect((host,port))
 
143
        except:
 
144
            raise
 
145
        LOG().log('success')
 
146
        pass
 
147
 
 
148
        msg = ('AU'+'N'+(nick or character)+','+
 
149
                    'C'+character+','+
 
150
                    'M'+votemap+
 
151
                    'L'+listening+
 
152
                    '')
 
153
 
 
154
        self.socket.sendall(msg)
 
155
 
 
156
    def send(self, key, action='down'):
 
157
        """
 
158
        This add a key event to be sent to the game server. Action can be up or
 
159
        down, down if player pushed the key, up if he released it.
 
160
 
 
161
        """
 
162
        self.lock.acquire()
 
163
        LOG().log('sent key '+key+' to game server')
 
164
        self.messages += key
 
165
        self.lock.release()
 
166
 
 
167
    def close(self):
 
168
        """
 
169
        Close the socket connection.
 
170
 
 
171
        """
 
172
        self.socket.close()
 
173
 
 
174
    def update(self):
 
175
        """
 
176
        Send all the pending messages to the server.
 
177
 
 
178
        """
 
179
        self.lock.acquire()
 
180
        self.socket.sendall(self.messages)
 
181
        self.messages = ''
 
182
        self.lock.release()
 
183
 
 
184
    def recieve(self):
 
185
        """
 
186
        Recieve all the data from the server necessary to update our display.
 
187
 
 
188
        """
 
189
        LOG().log('recieve new data from server')
 
190
        while True:
 
191
            chunk = self.socket.recv(5)
 
192
            if chunk == '':
 
193
                LOG().log('error server, link broken')
 
194
                raise NetworkError
 
195
            else:
 
196
                msg += chunk
 
197
 
 
198
                if msg[-1] == '':
 
199
                    break
 
200
        # parse msg to extract infos about level and characters.
 
201
        # IN is for INIT: the game is beginning
 
202
        if msg[:2] == 'IN':
 
203
            infos = msg[2:].split(',')
 
204
            for i in infos:
 
205
                k,v=i.split(':')
 
206
                if k == 'LE': # LEVEL
 
207
                    self.level = v
 
208
                elif k == 'PL': # PLAYERS
 
209
                    self.players.append(v)
 
210
 
 
211
        # UP is for UPDATE this is an update of the game
 
212
        elif msg[:2] == 'UP':
 
213
            self.players = msg[2:].split(';')
 
214
 
 
215
        return
 
216
 
 
217
class Server(object):
 
218
    """
 
219
    This class maintain a server socket that will listen for client connections
 
220
    and will create client socket to deal with client connections.
 
221
 
 
222
    """
 
223
    def __init__(self, game_instance=None, sock=None):
 
224
        self.quit = False
 
225
        self.game_instance = game_instance
 
226
        if sock is None:
 
227
            self.lock = Semaphore()
 
228
            self.serversocket = socket.socket(
 
229
                                                socket.AF_INET,
 
230
                                                socket.SOCK_STREAM
 
231
                                             )
 
232
            self.serversocket.bind(
 
233
                                    (
 
234
                                        self.serversocket.getsockname()[0],
 
235
                                        config['NETWORK_PORT']
 
236
                                    )
 
237
                                  )
 
238
 
 
239
            self.serversocket.listen(5)
 
240
            self.clients = []
 
241
            self.messages = []
 
242
        else:
 
243
            self.socket = socket
 
244
 
 
245
        self.listen()
 
246
    def __del__(self):
 
247
        """
 
248
        We wait for the thread to die.
 
249
 
 
250
        """
 
251
        self.quit = True
 
252
        time.sleep(1000)
 
253
 
 
254
    def listen(self, clients=[]):
 
255
        """
 
256
        Launch the server connection socket listener.
 
257
 
 
258
        """
 
259
        Thread( target = self.listen_function, args=(self.game_instance,) ).start()
 
260
 
 
261
    def close(self):
 
262
        self.quit = True
 
263
 
 
264
    def new_player_from_string(self, data):
 
265
        """
 
266
        Data is a stream sent by the client, this function return a dict of
 
267
        params to create the client character.
 
268
 
 
269
        """
 
270
        params = data[:-1].split(',')
 
271
        for p in params:
 
272
            if p[0] == 'N':
 
273
                nick = p[1:]
 
274
 
 
275
            if p[0] == 'C':
 
276
                character = p[1:]
 
277
 
 
278
            if p[0] == 'M':
 
279
                votemap = p[1:]
 
280
 
 
281
        return {'nick': nick, 'character': character, 'votemap': votemap}
 
282
 
 
283
    def listen_function(self, game_instance):
 
284
        """
 
285
        Listen for new connexion, and fork them to threads with their socket.
 
286
 
 
287
        """
 
288
        num = 0
 
289
        self.players = []
 
290
        self.votemap = []
 
291
        while num < self.game_instance.num_players:
 
292
            (clientsocket, address) = self.serversocket.accept()
 
293
            LOG().log('new client accepted :D')
 
294
            self.players.append([])
 
295
            self.votemap.append([])
 
296
            Thread(target=server_thread, args=(clientsocket, self, num, game_instance)).start()
 
297
            num += 1
 
298
            LOG().log('client threaded away ;)')
 
299
 
 
300
        sleep(.5) # FIXME: need to be sure that all informations are recieved
 
301
        LOG().log("enougth players, launching game")
 
302
        list_level = [(self.votemap.count(a), a) for a in set(self.votemap)]
 
303
        list_level.sort(reverse=True)
 
304
        LOG().log(self.players)
 
305
        self.game_instance.begin(players_=self.players, level=list_level[0][1])
 
306
 
 
307
    def fetch(self):
 
308
        """
 
309
        Get the next key event sent by a player.
 
310
 
 
311
        """
 
312
        if len(self.messages) == 0:
 
313
            yield None
 
314
        else:
 
315
            self.lock.acquire()
 
316
            event = self.messages.pop(0)
 
317
            self.lock.release()
 
318
            yield event
 
319