Commits

Szymon Wróblewski  committed a499450

removed msg_id from messagge, added base network adapter classes

  • Participants
  • Parent commits 3eb6f0d

Comments (0)

Files changed (16)

File pygame_network/__init__.py

 import message
 import syncobject
 from handler import Handler
+from network.base_adapter import State
 
+_network_module = None
 register = message.MessageFactory.register
 
 
     Note: Because of the dynamic loading of network library adapter, Client,
         Server and State classes will only be available after initialization.
     """
-    global Client, Server, State
+    global _network_module
     _logger = logging.getLogger(__name__)
     if logging_lvl is not None:
         logging.basicConfig(level=logging_lvl,
-                            format='%(asctime)s:%(levelname)s:%(message)s',
+                            format='%(asctime)-8s %(levelname)-8s %(message)s',
                             datefmt='%H:%M:%S')
     if isinstance(network, basestring):
         network = (network,)
     for name in network:
         try:
             _logger.debug("Trying to import network.%s_adapter", name)
-            nmod = import_module('pygame_network.network.%s_adapter' % name)
+            _network_module = import_module('pygame_network.network.%s_adapter' % name)
             break
         except ImportError as e:
             _logger.debug("%s: %s", e.__class__.__name__, e.message)
     else:
         _logger.critical("Can't find any network module")
         return
-    Client = nmod.Client
-    Server = nmod.Server
-    State = nmod.State
     _logger.info("Using %s network module", name)
     for name in serialization:
         try:
         _logger.info("Enabling pygame events")
         import event
         event.init(event_val)
+
+
+def Client(*args, **kwargs):
+    return _network_module.Client(*args, **kwargs)
+
+
+def Server(*args, **kwargs):
+    return _network_module.Server(*args, **kwargs)

File pygame_network/event.py

 NET_RECEIVED = 2
 
 
+def connected(connection):
+    pass
+
+
+def disconnected(connection):
+    pass
+
+
+def received(connection, message, channel):
+    pass
+
+
 def init(event_val=1):
     global NETWORK, connected, disconnected, received
     import pygame
         }))
     disconnected = _disconnected
 
-    def _received(connection, channel, message, message_id):
+    def _received(connection, message, channel=0):
         pygame.event.post(Event(NETWORK, {
             'net_type': NET_RECEIVED,
             #'connection': proxy(connection),
             'connection': connection,
             'channel': channel,
             'message': message,
-            'msg_id': message_id,
             'msg_type': message.__class__
         }))
     received = _received
-
-
-def connected(connection):
-    pass
-
-
-def disconnected(connection):
-    pass
-
-
-def received(connection, channel, message, message_id):
-    pass

File pygame_network/handler.py

 class Handler(object):
     """Base class for objects handling packets through
-    net_packet_name(channel, message_id, message) functions
+    net_packet_name(message, channel) methods
     """
     def on_connect(self):
         pass
     def on_disconnect(self):
         pass
 
-    def on_recive(self, channel, message_id, message):
+    def on_recive(self, message, channel):
         pass

File pygame_network/message.py

 import logging
 from collections import namedtuple
 from weakref import WeakKeyDictionary, WeakValueDictionary
-import enet
 
 __all__ = ('MessageFactory', 'MessageError')
 _logger = logging.getLogger(__name__)
         return packet
 
     @classmethod
-    def pack(cls, message_id, message):
+    def pack(cls, message, sys_data=[]):
         """Pack data to string
 
         MessageFactory.pack(packet_id, packet_obj): return string
 
-        message_id - identifier of message
         message - object of class created by register
 
         Returns message packed in string, ready to send.
         """
         type_id = cls._message_params[message.__class__][0]
-        message = (type_id, message_id) + message
-        cls._message_cnt = message_id
+        message = (type_id,) + tuple(sys_data) + message
         data = s_lib.pack(message)
         _logger.debug("Packing message (length: %d)", len(data))
         return data
 
     @classmethod
-    def unpack(cls, data):
+    def unpack(cls, data, sys_data=[]):
         """Unpack data from string, return message_id and message
 
         MessageFactory.unpack(data): return (message_id, message)
         """
         _logger.debug("Unpacking message (length: %d)", len(data))
         message = s_lib.unpack(data)
+        sys_data_l = len(sys_data)
         try:
-            type_id, packet_id = message[:2]
-            return packet_id, cls._message_types[type_id](*message[2:])
+            type_id = message[0]
+            sys_data[:] = message[1:1 + sys_data_l]
+            return cls._message_types[type_id](*message[1 + sys_data_l:])
         except KeyError:
             _logger.warning('Unknown message type_id: %s', type_id)
         except:
             _logger.warning('Message unpacking error: %s', message)
-        return None, None
+        return None
 
     @classmethod
     def get_by_name(cls, name):

File pygame_network/network/base_adapter.py

+import re
+import logging
+from weakref import proxy
+from functools import partial
+from ..message import MessageFactory
+from .. import event
+
+__all__ = ('Connection', 'State')
+_logger = logging.getLogger(__name__)
+
+
+class State(object):
+    (CONNECTED,
+    CONNECTING,
+    DISCONNECTED,
+    DISCONNECTING) = range(4)
+
+
+class Connection(object):
+    """Class allowing to send messages
+
+    It's created by by Client or Server, shouldn't be created manually.
+
+    Sending is possible in two ways:
+    * using net_<message_name> methods, where <message_name>
+      is name of message registered in MessageFactory
+    * using send method with message as argument
+
+    """
+
+    def __init__(self, parent, message_factory=MessageFactory):
+        self.parent = proxy(parent)
+        self._message_cnt = 0
+        self._message_factory = message_factory
+        self._handlers = []
+
+    def __getattr__(self, name):
+        parts = name.split('_', 1)
+        if len(parts) == 2 and parts[0] == 'net' and\
+                parts[1] in self._message_factory._message_names:
+            p = partial(self._send_message, self._message_factory.get_by_name(parts[1]))
+            p.__doc__ = "Send %s message to remote host\n\nHost.net_%s" % (
+                parts[1],
+                self._message_factory._message_names[parts[1]].__doc__
+            )
+            # add new method so __getattr__ is no longer needed
+            setattr(self, name, p)
+            return p
+        else:
+            raise AttributeError("'%s' object has no attribute '%s'" %
+                                 (type(self).__name__, name))
+
+    def send(self, message, *args, **kwargs):
+        """Send message to remote host
+
+        Connection.send(message, *args, **kwargs): return int
+
+        message - class created by MessageFactory.register or message name
+
+        args and kwargs are used to initialize message object.
+        Returns message id which can be used to retrieve response from
+        Pygame event queue if sending was successful.
+        """
+        if isinstance(message, basestring):
+            message = self._message_factory.get_by_name(message)
+        self._send_message(message, *args, **kwargs)
+
+    def _send_message(self, message, *args, **kwargs):
+        name = message.__name__
+        params = self._message_factory.get_params(message)[1]
+        try:
+            message_ = message(*args, **kwargs)
+        except TypeError, e:
+            e, f = re.findall(r'\d', e.message)
+            raise TypeError('%s takes exactly %d arguments (%d given)' %
+                (message.__doc__, int(e) - 1, int(f) - 1))
+        data = self._message_factory.pack(message_)
+        _logger.info('Sent %s message to %s:%s', name, *self.address)
+        return self._send_data(data, **params)
+
+    def _receive(self, data, channel=0):
+        message = self._message_factory.unpack(data)
+        name = message.__class__.__name__
+        _logger.info('Received %s message from %s:%s', name, *self.address)
+        event.received(self, message, channel)
+        for h in self._handlers:
+            getattr(h, 'net_' + name, h.on_recive)(message, channel)
+
+    def _connect(self):
+        _logger.info('Connected to %s:%s', *self.address)
+        event.connected(self)
+        for h in self._handlers:
+            h.on_connect()
+
+    def _disconnect(self):
+        _logger.info('Disconnected from %s:%s', *self.address)
+        event.disconnected(self)
+        for h in self._handlers:
+            h.on_disconnect()
+
+    def disconnect(self, *args):
+        """Request a disconnection."""
+        pass
+
+    def add_handler(self, handler):
+        """Add new Handler to handle messages.
+
+        Connection.add_handler(handler)
+
+        handler - instance of Receiver subclass
+        """
+        self._handlers.append(handler)
+        handler.connection = proxy(self)
+
+    @property
+    def state(self):
+        """Connection state."""
+        return State.DISCONNECTED
+
+    @property
+    def address(self):
+        """Connection address."""
+        return None, None
+
+
+class Server(object):
+    message_factory = MessageFactory
+    handler_cls = None
+
+    def __init__(self, address='', port=0, connections_limit=4, *args, **kwargs):
+        _logger.debug('Server created %s, connections limit: %d', address, connections_limit)
+        self.message_factory._frozen = True
+        _logger.debug('MessageFactory frozen')
+        self.peers = {}
+
+    def step(self, timeout=0):
+        pass
+
+    def connections(self, exclude=None):
+        if exclude is None:
+            return self.peers.itervalues()
+        else:
+            return (c for c in self.peers.itervalues() if c not in exclude)
+
+    def handlers(self, exclude=None):
+        if exclude is None:
+            return (c._handlers[0] for c in self.peers.itervalues())
+        else:
+            return (c._handlers[0] for c in self.peers.itervalues() if c not in exclude)
+

File pygame_network/network/enet_adapter/__init__.py

 from client import Client
 from server import Server
-from connection import State

File pygame_network/network/enet_adapter/client.py

         event = host.service(timeout)
         while event is not None:
             if event.type == enet.EVENT_TYPE_CONNECT:
-                _logger.info('Connected to %s', event.peer.address)
                 self._peers[event.peer.data]._connect()
             elif event.type == enet.EVENT_TYPE_DISCONNECT:
-                _logger.info('Disconnected from %s', event.peer.address)
                 self._peers[event.peer.data]._disconnect()
                 del self._peers[event.peer.data]
             elif event.type == enet.EVENT_TYPE_RECEIVE:
-                _logger.info('Received data from %s', event.peer.address)
                 self._peers[event.peer.data]._receive(event.packet.data, event.channelID)
             event = host.check_events()

File pygame_network/network/enet_adapter/connection.py

-import logging
-from weakref import proxy
-from functools import partial
 import enet
 from ...message import MessageFactory
-from ... import event
+from .. import base_adapter
 
-__all__ = ('Connection', 'State')
-_logger = logging.getLogger(__name__)
+_state_mapping = {
+    enet.PEER_STATE_ACKNOWLEDGING_CONNECT: base_adapter.State.CONNECTING,
+    enet.PEER_STATE_ACKNOWLEDGING_DISCONNECT: base_adapter.State.DISCONNECTING,
+    enet.PEER_STATE_CONNECTED: base_adapter.State.CONNECTED,
+    enet.PEER_STATE_CONNECTING: base_adapter.State.CONNECTING,
+    enet.PEER_STATE_CONNECTION_PENDING: base_adapter.State.CONNECTING,
+    enet.PEER_STATE_CONNECTION_SUCCEEDED: base_adapter.State.CONNECTING,
+    enet.PEER_STATE_DISCONNECTED: base_adapter.State.DISCONNECTED,
+    enet.PEER_STATE_DISCONNECTING: base_adapter.State.DISCONNECTING,
+    enet.PEER_STATE_DISCONNECT_LATER: base_adapter.State.DISCONNECTING,
+    enet.PEER_STATE_ZOMBIE: base_adapter.State.DISCONNECTING
+}
 
 
-class State(object):
-    ACKNOWLEDGING_CONNECT = enet.PEER_STATE_ACKNOWLEDGING_CONNECT
-    ACKNOWLEDGING_DISCONNECT = enet.PEER_STATE_ACKNOWLEDGING_DISCONNECT
-    CONNECTED = enet.PEER_STATE_CONNECTED
-    CONNECTING = enet.PEER_STATE_CONNECTING
-    CONNECTION_PENDING = enet.PEER_STATE_CONNECTION_PENDING
-    CONNECTION_SUCCEEDED = enet.PEER_STATE_CONNECTION_SUCCEEDED
-    DISCONNECTED = enet.PEER_STATE_DISCONNECTED
-    DISCONNECTING = enet.PEER_STATE_DISCONNECTING
-    DISCONNECT_LATER = enet.PEER_STATE_DISCONNECT_LATER
-    ZOMBIE = enet.PEER_STATE_ZOMBIE
-
-
-class Connection(object):
+class Connection(base_adapter.Connection):
     """Class allowing to send messages
 
     It's created by by Client or Server, shouldn't be created manually.
     """
 
     def __init__(self, parent, peer, message_factory=MessageFactory):
-        self.parent = proxy(parent)
+        super(Connection, self).__init__(parent, message_factory)
         self.peer = peer
-        self._message_cnt = 0
-        self._message_factory = message_factory
-        self._handlers = []
 
     def __del__(self):
         self.peer.disconnect_now()
 
-    def __getattr__(self, name):
-        parts = name.split('_', 1)
-        if len(parts) == 2 and parts[0] == 'net' and\
-                parts[1] in self._message_factory._message_names:
-            p = partial(self._send, self._message_factory.get_by_name(parts[1]))
-            p.__doc__ = "Send %s message to remote host\n\n"\
-                "Host.net_%s: return message_id" % \
-                (parts[1], self._message_factory._message_names[parts[1]].__doc__)
-            # add new method so __getattr__ is no longer needed
-            setattr(self, name, p)
-            return p
+    def _send_data(self, data, channel=0, flags=enet.PACKET_FLAG_RELIABLE):
+        self.peer.send(channel, enet.Packet(data, flags))
+
+    def disconnect(self, when=0):
+        """Request a disconnection.
+
+        when - type of disconnection
+               0  - disconnect with acknowledge
+               -1 - disconnect after sending all messages
+               1  - disconnect without acknowledge
+        """
+        if when == 0:
+            self.peer.disconnect()
+        elif when == -1:
+            self.peer.disconnect_later()
         else:
-            raise AttributeError("'%s' object has no attribute '%s'" %
-                                 (type(self).__name__, name))
-
-    def send(self, message, *args, **kwargs):
-        """Send message to remote host
-
-        Connection.send(message, *args, **kwargs): return int
-
-        message - class created by MessageFactory.register or message name
-
-        args and kwargs are used to initialize message object.
-        Returns message id which can be used to retrieve response from
-        Pygame event queue if sending was successful.
-        """
-        if isinstance(message, basestring):
-            message = self._message_factory.get_by_name(message)
-        self._send(message, *args, **kwargs)
-
-    def _send(self, message, *args, **kwargs):
-        params = self._message_factory.get_params(message)[1]
-        flags = kwargs.get('flags', params.get('flags', enet.PACKET_FLAG_RELIABLE))
-        channel = kwargs.get('channel', params.get('channel', 0))
-        message_id = self._message_cnt = self._message_cnt + 1
-        name = message.__name__
-        message = message(*args, **kwargs)
-        data = self._message_factory.pack(message_id, message)
-        if self.peer.send(channel, enet.Packet(data, flags)) == 0:
-            _logger.info('Sent %s message on channel %d', name, channel)
-            return message_id
-
-    def _receive(self, data, channel):
-        message_id, message = self._message_factory.unpack(data)
-        name = message.__class__.__name__
-        _logger.info('Received %s message on channel %d', name, channel)
-        name = 'net_' + name
-        event.received(self, channel, message, message_id)
-        for r in self._handlers:
-            getattr(r, name, r.on_recive)(channel, message_id, message)
-
-    def _connect(self):
-        event.connected(self)
-        for r in self._handlers:
-            r.on_connect()
-
-    def _disconnect(self):
-        event.disconnected(self)
-        for r in self._handlers:
-            r.on_disconnect()
-
-    def disconnect(self):
-        """Request a disconnection.
-        """
-        self.peer.disconnect()
-
-    def disconnect_later(self):
-        """Request a disconnection from a peer, but only after all queued
-        outgoing messages are sent.
-        """
-        self.peer.disconnect_later()
-
-    def disconnect_now(self):
-        """Force an immediate disconnection.
-        """
-        self.peer.disconnect_now()
-
-    def add_handler(self, handler):
-        """Add new Receiver to handle messages.
-
-        Connection.add_handler(handler)
-
-        handler - instance of Receiver subclass
-        """
-        self._handlers.append(handler)
-        handler.connection = proxy(self)
+            self.peer.disconnect_now()
 
     @property
     def state(self):
         """Connection state."""
-        return self.peer.state
+        return _state_mapping[self.peer.state]
 
     @property
     def address(self):
         """Connection address."""
-        return self.peer.address
+        address = self.peer.address
+        return address.host, address.port

File pygame_network/network/enet_adapter/server.py

                                     ' hash incorrect', event.peer.address)
                     event.peer.disconnect_now()
             elif event.type == enet.EVENT_TYPE_DISCONNECT:
-                _logger.info('Disconnected from %s', event.peer.address)
                 self.peers[event.peer.data]._disconnect()
                 del self.peers[event.peer.data]
             elif event.type == enet.EVENT_TYPE_RECEIVE:
-                _logger.info('Received data from %s', event.peer.address)
                 self.peers[event.peer.data]._receive(event.packet.data, event.channelID)
             event = host.check_events()
 

File pygame_network/network/socket_adapter/__init__.py

Empty file added.

File pygame_network/network/socket_adapter/connection.py

+import logging
+from collections import deque
+from asyncore import dispatcher
+from ...message import MessageFactory
+
+_logger = logging.getLogger(__name__)
+
+
+class Connection(dispatcher):
+    __send = dispatcher.send
+
+    def __init__(self, parent, socket, message_factory=MessageFactory):
+        super(Connection, self).__init__(parent, message_factory)
+        self._buffer = deque()
+
+    def _send_data(self, data, **kwargs):
+        pass
+
+    def disconnect(self, *args):
+        """Request a disconnection."""
+        pass
+
+    @property
+    def state(self):
+        """Connection state."""
+        return None
+
+    @property
+    def address(self):
+        """Connection address."""
+        return None, None
+
+    def handle_read(self):
+        self.log_info('unhandled read event', 'warning')
+
+    def handle_write(self):
+        self.log_info('unhandled write event', 'warning')
+
+    def handle_connect(self):
+        self.log_info('unhandled connect event', 'warning')
+
+    def handle_accept(self):
+        self.log_info('unhandled accept event', 'warning')
+
+    def handle_close(self):
+        self.log_info('unhandled close event', 'warning')
+        self.close()
+
+    def log_info(self, message, type='info'):
+        return getattr(_logger, type)(message)

File pygame_network/syncobject.py

 from weakref import WeakKeyDictionary
 from message import *
 
-SYNCOBJECT_MODE_AUTO = 0
-SYNCOBJECT_MODE_MANUAL = 1
+class Mode(object):
+    AUTO = 0
+    MANUAL = 1
 
 
 class SyncObject(object):
     to specify a tuple of variable names for synchronization.
     Each assignment to a variable defined in sync_var will notify
     SyncObjectManager which, depending on sync_mode, will either
-    prepare update message (SYNCOBJECT_MODE_AUTO) or
-    wait with preparation for send_changes call (SYNCOBJECT_MODE_MANUAL).
+    prepare update message (Mode.AUTO) or
+    wait with preparation for send_changes call (Mode.MANUAL).
     sync_flags overrides default enet sending flags.
     """
     sync_var = ()
-    sync_mode = SYNCOBJECT_MODE_AUTO
+    sync_mode = Mode.AUTO
     sync_flags = None
 
     def __init__(self, *args, **kwargs):
             cls._known_types[obj.__class__] = type_id
             cls._type_id_cnt = type_id
         obj_id = cls._obj_id_cnt + 1
-        cls._sync_objs[obj] = (type_id, obj_id, [False] * len(obj.sync_var))
+        cls._sync_objs[obj] = (type_id, obj_id, set())
         cls._obj_id_cnt = obj_id
 
     @classmethod
     def changed(cls, obj, var_name):
         """Prepares update message
         """
-        print '%s@%s updated' % (var_name, obj)
+        cls._sync_objs[obj][2].add(var_name)
         # TODO: finish this
         # TODO: push update message to queue if in auto mode
 

File test_client_1.py

 
 def main():
     net.init(logging_lvl=logging.DEBUG)
-    net.register('echo', ('msg',))
+    net.register('echo', ('msg', 'msg_id'))
     client = net.Client()
     connection = client.connect("localhost", 54301)
     counter = 0
         if counter < 10 and connection.state == net.State.CONNECTED:
             msg = ''.join(random.sample('abcdefghijklmnopqrstuvwxyz', 10))
             logging.info('Sending: %s', msg)
-            connection.net_echo(msg)
+            connection.net_echo(msg, counter)
             counter += 1
+            if counter == 10:
+                connection.disconnect()
 
 
 if __name__ == '__main__':

File test_client_2.py

 
 class EchoHandler(net.Handler):
     def __init__(self):
-        self.connected = None
         self.counter = 10
 
-    def net_echo(self, channel, message_id, message):
-        logging.info('Received message #%d @ch%d: %s', message_id, channel, message)
-
-    def on_connect(self):
-        self.connected = True
-
-    def on_disconnect(self):
-        self.connected = False
+    def net_echo(self, message, channel):
+        logging.info('Received message @ch%d: %s', channel, message)
 
     def step(self):
-        if self.counter > 0 and self.connected == True:
+        if self.counter > 0 and self.connection.state == net.State.CONNECTED:
             msg = ''.join(random.sample('abcdefghijklmnopqrstuvwxyz', 10))
             logging.info('Sending: %s', msg)
-            self.connection.net_echo(msg)
+            self.connection.net_echo(msg, self.counter)
             self.counter -= 1
+            if self.counter == 0:
+                self.connection.disconnect()
 
 
 def main():
     net.init(logging_lvl=logging.DEBUG)
-    net.register('echo', ('msg',))
+    net.register('echo', ('msg', 'msg_id'))
     client = net.Client()
     connection = client.connect("localhost", 54301)
     handler = EchoHandler()
     connection.add_handler(handler)
-    while handler.connected != False:
+    while True:
         client.step()
         handler.step()
 

File test_client_3.py

 import random
 import logging
 import pygame
-from pygame.locals import *
+from pygame.locals import KEYDOWN, QUIT, K_ESCAPE, K_SPACE, K_l
 import pygame_network as net
 
 
-def message_status(screen, position, packets):
+def message_status(screen, position, messages):
     i = 0
-    keys = packets.keys()
+    keys = messages.keys()
     keys.sort()
     screen.fill(0x000000, (position[0], position[1], 350, 15))
     for k in keys:
-        msg1, msg2 = packets[k]
+        msg1, msg2 = messages[k]
         if msg2 is None:
             color = 0xffff00
         elif msg2 == msg1.upper():
 
     # Network init
     net.init(events=True, logging_lvl=logging.DEBUG)  # enable Pygame events
-    echo = net.register('echo', ('msg',))
+    echo = net.register('echo', ('msg', 'msg_id'))
     client = net.Client()
     connection = None
 
     run = True
     limit = True
     messages = {}
+    m_id = 0
 
     while run:
         events = pygame.event.get()
                 if e.key == K_SPACE:
                     if connection is not None:
                         if connection.state == net.State.CONNECTED:
-                            connection.disconnect_later()
+                            connection.disconnect()
                             connection_status(screen, (140, 38), False)
                     else:
                         connection = client.connect("localhost", 54301)
                 elif e.net_type == net.event.NET_RECEIVED:
                     if e.msg_type == echo:
                         msg = e.message.msg
-                        messages[e.msg_id][1] = msg
+                        messages[e.message.msg_id][1] = msg
                         message_status(screen, (110, 62), messages)
             if e.type == QUIT or e.type == KEYDOWN and e.key == K_ESCAPE:
                 run = False
             msg = ''.join(random.sample('abcdefghijklmnopqrstuvwxyz', 10))
 
             # Sending messages
-            m_id = connection.net_echo(msg)
+            m_id += 1
+            connection.net_echo(msg, m_id)
 
             messages[m_id] = [msg, None]
             message_status(screen, (110, 62), messages)

File test_server.py

 
 
 class EchoHandler(net.Handler):
-    def net_echo(self, channel, message_id, message):
+    def net_echo(self, message, channel):
         msg = message.msg.upper()
-        self.connection.net_echo(msg)
-        logging.info('message #%d @ch%d: %s', message_id, channel, message)
+        self.connection.net_echo(msg, message.msg_id)
+        logging.info('message @ch%d: %s', channel, message)
 
 
 def main():
     net.init(logging_lvl=logging.DEBUG)
-    net.register('echo', ('msg',))
+    net.register('echo', ('msg', 'msg_id'))
     server = net.Server(port=54301)
     server.handler_cls = EchoHandler
     logging.info('Listening')