changeset 102:361178f45eb6

updated to pygnetic module
author Szymon Wróblewski <bluex0@gmail.com>
date Thu, 06 Sep 2012 21:35:41 +0200
parents 5801c0e86e3d
children 616421834e19
files zrpn/board.py zrpn/client.py zrpn/main.py zrpn/messages.py zrpn/packets.py zrpn/pygame_network/__init__.py zrpn/pygame_network/client.py zrpn/pygame_network/connection.py zrpn/pygame_network/event.py zrpn/pygame_network/packet.py zrpn/pygame_network/receiver.py zrpn/pygame_network/server.py zrpn/pygame_network/syncobject.py zrpn/pygnetic/__init__.py zrpn/pygnetic/_utils.py zrpn/pygnetic/client.py zrpn/pygnetic/connection.py zrpn/pygnetic/discovery.py zrpn/pygnetic/event.py zrpn/pygnetic/handler.py zrpn/pygnetic/message.py zrpn/pygnetic/network/__init__.py zrpn/pygnetic/network/enet_adapter.py zrpn/pygnetic/network/socket_adapter.py zrpn/pygnetic/serialization/__init__.py zrpn/pygnetic/serialization/json_adapter.py zrpn/pygnetic/serialization/msgpack_adapter.py zrpn/pygnetic/server.py zrpn/pygnetic/syncobject.py zrpn/server.py
diffstat 30 files changed, 1304 insertions(+), 668 deletions(-) [+]
line wrap: on
line diff
--- a/zrpn/board.py	Sun Jul 08 18:00:38 2012 +0200
+++ b/zrpn/board.py	Thu Sep 06 21:35:41 2012 +0200
@@ -45,14 +45,11 @@
                     n[0] = xy[0]
                     n[1] = xy[1]
                 new_net_adds.append(n)
-            
 
             if not killed:
                 self.printit()
                 return animations, extra, new_net_adds
 
-
-
     def printit(self):
         print ("cells")
         for c in self.cells:
@@ -65,7 +62,7 @@
         for y, line in enumerate(self.cells):
             for x, c in enumerate(line):
                 if c == cell:
-                    return x,y
+                    return x, y
 
     def apply_gravity(self, turn):
         animations = []
--- a/zrpn/client.py	Sun Jul 08 18:00:38 2012 +0200
+++ b/zrpn/client.py	Thu Sep 06 21:35:41 2012 +0200
@@ -7,10 +7,10 @@
 
 import sgc
 import pygame
-import pygame_network as net
+import pygnetic as net
 
 import display
-import packets
+import messages
 from skelutil import data_dir
 
 
@@ -45,26 +45,25 @@
                     self.connection.net_click(x, y)
                 elif event.type == net.event.NETWORK:
                     if event.net_type == net.event.NET_RECEIVED:
-                        if event.p_type == packets.INIT:
-                            self.display.load_board(event.packet.board,
-                                    event.packet.sprites)
-                        elif event.p_type == packets.ANIMATIONS:
-                            self.display.do_animations(event.packet.animations)
-                        elif event.p_type == packets.SCORE:
-                            if event.packet.score_num is not None:
-                                self.lbl_score.config(text="score=\n" + str(event.packet.score_num))
-                            self.lbl_highscore.config(text="first=\n" + str(event.packet.highscore))
-                        elif event.p_type == packets.ADD:
-                            self.display.add_zrpn(*event.packet)
-            self.client.step()
+                        if event.msg_type == messages.INIT:
+                            self.display.load_board(event.message.board,
+                                    event.message.sprites)
+                        elif event.msg_type == messages.ANIMATIONS:
+                            self.display.do_animations(event.message.animations)
+                        elif event.msg_type == messages.SCORE:
+                            if event.message.score_num is not None:
+                                self.lbl_score.config(text="score=\n" + str(event.message.score_num))
+                            self.lbl_highscore.config(text="first=\n" + str(event.message.highscore))
+                        elif event.msg_type == messages.ADD:
+                            self.display.add_zrpn(*event.message)
+            self.client.update()
             t = time.time() - self.start
             self.lbl_time.config(text="time\nwasted=\n" + str(round(t, 5)))
             self.display.frame()
 
 
 def main():
-    net.event.init()
-    logging.basicConfig(level=logging.DEBUG)
+    net.init(True, logging_lvl=logging.DEBUG)
     Client('localhost', 10000).run()
 
 if __name__ == '__main__':
--- a/zrpn/main.py	Sun Jul 08 18:00:38 2012 +0200
+++ b/zrpn/main.py	Thu Sep 06 21:35:41 2012 +0200
@@ -53,9 +53,8 @@
     def multi(self):
         self.remove()
 
-        import pygame_network as net
-        net.event.init()
-        logging.basicConfig(level=logging.DEBUG)
+        import pygnetic as net
+        net.init(True, logging_lvl=logging.DEBUG)
 
         ip = self["ip"].text
         ip = str(ip)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/zrpn/messages.py	Thu Sep 06 21:35:41 2012 +0200
@@ -0,0 +1,7 @@
+import pygnetic as net
+INIT = net.register('init', ('board', 'sprites'))
+ANIMATIONS = net.register('animations', ('animations',))
+CLICK = net.register('click', ('x', 'y'))
+SCORE = net.register('score', ('score_num', 'highscore'))
+ADD = net.register('add', ('x', 'y', 'cell', 'board_ids'))
+NEW_PLAYER = ('new_player', ('player_count',))
--- a/zrpn/packets.py	Sun Jul 08 18:00:38 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,7 +0,0 @@
-import pygame_network as net
-INIT = net.register('init', ('board', 'sprites'))
-ANIMATIONS = net.register('animations', ('animations',))
-CLICK = net.register('click', ('x', 'y'))
-SCORE = net.register('score', ('score_num', 'highscore'))
-ADD = net.register('add', ('x', 'y', 'cell', 'board_ids'))
-NEW_PLAYER = ('new_player', ('player_count',))
--- a/zrpn/pygame_network/__init__.py	Sun Jul 08 18:00:38 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,12 +0,0 @@
-import packet
-import connection
-import syncobject
-from client import Client
-from server import Server
-from receiver import Receiver
-try:
-    import event
-except ImportError:
-    pass
-
-register = packet.PacketManager.register
--- a/zrpn/pygame_network/client.py	Sun Jul 08 18:00:38 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,51 +0,0 @@
-import logging
-import enet
-from packet import PacketManager
-from connection import Connection
-
-_logger = logging.getLogger(__name__)
-
-
-class Client(object):
-    """Class representing network client
-
-    Example:
-        client = pygame_network.client.Client()
-        connection = client.connect("localhost", 10000)
-        while True:
-            client.step()
-    """
-    def __init__(self, connections_limit=1, channel_limit=0, in_bandwidth=0, out_bandwidth=0):
-        self.host = enet.Host(None, connections_limit, channel_limit, in_bandwidth, out_bandwidth)
-        self._peers = {}
-        self._peer_cnt = 0
-
-    def connect(self, address, port, channels=2, packet_manager=PacketManager):
-        peer_id = self._peer_cnt = self._peer_cnt + 1
-        peer_id = str(peer_id)
-        # Can't register packets after connection
-        packet_manager._frozen = True
-        address = enet.Address(address, port)
-        peer = self.host.connect(address, channels, packet_manager.get_hash())
-        peer.data = peer_id
-        connection = Connection(self, peer, packet_manager)
-        self._peers[peer_id] = connection
-        return connection
-
-    def step(self, timeout=0):
-        if len(self._peers) == 0:
-            return
-        host = self.host
-        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()
--- a/zrpn/pygame_network/connection.py	Sun Jul 08 18:00:38 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,157 +0,0 @@
-__all__ = ('Connection',
-           'STATE_ACKNOWLEDGING_CONNECT',
-           'STATE_ACKNOWLEDGING_DISCONNECT',
-           'STATE_CONNECTED',
-           'STATE_CONNECTING',
-           'STATE_CONNECTION_PENDING',
-           'STATE_CONNECTION_SUCCEEDED',
-           'STATE_DISCONNECTED',
-           'STATE_DISCONNECTING',
-           'STATE_DISCONNECT_LATER',
-           'STATE_ZOMBIE')
-
-import logging
-from weakref import proxy
-from functools import partial
-import enet
-from packet import PacketManager
-
-_logger = logging.getLogger(__name__)
-
-# dummy events
-def _connected_event(connection): pass
-def _disconnected_event(connection): pass
-def _received_event(connection, channel, packet, packet_id): pass
-def _response_event(connection, channel, packet, packet_id): pass
-
-STATE_ACKNOWLEDGING_CONNECT = enet.PEER_STATE_ACKNOWLEDGING_CONNECT
-STATE_ACKNOWLEDGING_DISCONNECT = enet.PEER_STATE_ACKNOWLEDGING_DISCONNECT
-STATE_CONNECTED = enet.PEER_STATE_CONNECTED
-STATE_CONNECTING = enet.PEER_STATE_CONNECTING
-STATE_CONNECTION_PENDING = enet.PEER_STATE_CONNECTION_PENDING
-STATE_CONNECTION_SUCCEEDED = enet.PEER_STATE_CONNECTION_SUCCEEDED
-STATE_DISCONNECTED = enet.PEER_STATE_DISCONNECTED
-STATE_DISCONNECTING = enet.PEER_STATE_DISCONNECTING
-STATE_DISCONNECT_LATER = enet.PEER_STATE_DISCONNECT_LATER
-STATE_ZOMBIE = enet.PEER_STATE_ZOMBIE
-
-
-class Connection(object):
-    """Class allowing to send packets
-
-    It's created by by Client or Server, shouldn't be created manually.
-
-    Sending is possible in two ways:
-    * using net_<packet_name> methods, where <packet_name>
-      is name of packet registered in PacketManager
-    * using send method with packet as argument
-
-    Attributes:
-        parent - proxy to Client / Server instance
-        peer - Enet peer instance
-    """
-
-    def __init__(self, parent, peer, packet_manager=PacketManager):
-        self.parent = proxy(parent)
-        self.peer = peer
-        self._packet_cnt = 0
-        self._packet_manager = packet_manager
-        self._receivers = []
-
-    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._packet_manager._packet_names:
-            p = partial(self._send, self._packet_manager.get_by_name(parts[1]))
-            p.__doc__ = "Send %s packet to remote host\n\n"\
-                "Host.net_%s: return packet_id" % \
-                (parts[1], self._packet_manager._packet_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, packet, *args, **kwargs):
-        """Send packet to remote host
-
-        Connection.send(packet, *args, **kwargs): return int
-
-        packet - class created by PacketManager.register or packet name
-
-        args and kwargs are used to initialize packet object.
-        Returns packet id which can be used to retrieve response from
-        Pygame event queue if sending was successful.
-        """
-        if isinstance(packet, basestring):
-            packet = self._packet_manager.get_by_name(packet)
-        self._send(packet, *args, **kwargs)
-
-    def _send(self, packet, *args, **kwargs):
-        _, channel, flags = self._packet_manager.get_params(packet)
-        flags = kwargs.get('flags', flags)
-        channel = kwargs.get('channel', channel)
-        packet_id = self._packet_cnt = self._packet_cnt + 1
-        packet = packet(*args, **kwargs)
-        data = self._packet_manager.pack(packet_id, packet)
-        if self.peer.send(channel, enet.Packet(data, flags)) == 0:
-            return packet_id
-
-    def _receive(self, data, channel):
-        packet_id, packet = self._packet_manager.unpack(data)
-        name = packet.__class__.__name__
-        _logger.info('Received %s packet', name)
-        name = 'net_' + name
-        _received_event(self, channel, packet, packet_id)
-        for r in self._receivers:
-            getattr(r, name, r.on_recive)(channel, packet_id, packet)
-
-    def _connect(self):
-        _connected_event(self)
-        for r in self._receivers:
-            r.on_connect()
-
-    def _disconnect(self):
-        _disconnected_event(self)
-        for r in self._receivers:
-            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 packets are sent.
-        """
-        self.peer.disconnect_later()
-
-    def disconnect_now(self):
-        """Force an immediate disconnection.
-        """
-        self.peer.disconnect_now()
-
-    def add_receiver(self, receiver):
-        """Add new Receiver to handle packets.
-
-        Connection.add_receiver(receiver)
-
-        receiver - instance of Receiver subclass
-        """
-        self._receivers.append(receiver)
-        receiver.connection = proxy(self)
-
-    @property
-    def state(self):
-        """Connection state."""
-        return self.peer.state
-
-    @property
-    def address(self):
-        """Connection address."""
-        return self.peer.address
--- a/zrpn/pygame_network/event.py	Sun Jul 08 18:00:38 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,66 +0,0 @@
-__all__ = ('NETWORK',
-           'NET_CONNECTED',
-           'NET_DISCONNECTED',
-           'NET_RECEIVED',
-           'NET_RESPONSE')
-
-from weakref import proxy
-import pygame
-from pygame.event import Event
-from pygame.locals import USEREVENT
-import connection
-
-NETWORK = USEREVENT + 1
-NET_CONNECTED = 0
-NET_DISCONNECTED = 1
-NET_RECEIVED = 2
-NET_RESPONSE = 3  # TODO: response !!
-
-
-def init(event_val=1):
-    global NETWORK
-    NETWORK = USEREVENT + event_val
-    connection._connected_event = _connected_event
-    connection._disconnected_event = _disconnected_event
-    connection._received_event = _received_event
-    connection._response_event = _response_event
-
-
-def _connected_event(connection):
-    pygame.event.post(Event(NETWORK, {
-        'net_type': NET_CONNECTED,
-        #'connection': proxy(connection)
-        'connection': connection
-    }))
-
-
-def _disconnected_event(connection):
-    pygame.event.post(Event(NETWORK, {
-        'net_type': NET_DISCONNECTED,
-        #'connection': proxy(connection)
-        'connection': connection
-    }))
-
-
-def _received_event(connection, channel, packet, packet_id):
-    pygame.event.post(Event(NETWORK, {
-        'net_type': NET_RECEIVED,
-        #'connection': proxy(connection),
-        'connection': connection,
-        'channel': channel,
-        'packet': packet,
-        'p_id': packet_id,
-        'p_type': packet.__class__
-    }))
-
-
-def _response_event(connection, channel, packet, packet_id):
-    pygame.event.post(Event(NETWORK, {
-        'net_type': NET_RESPONSE,
-        #'connection': proxy(connection),
-        'connection': connection,
-        'channel': channel,
-        'packet': packet,
-        'p_id': packet_id,
-        'p_type': packet.__class__
-    }))
--- a/zrpn/pygame_network/packet.py	Sun Jul 08 18:00:38 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,167 +0,0 @@
-import logging
-from collections import namedtuple
-from weakref import WeakKeyDictionary, WeakValueDictionary
-import enet
-try:
-    raise ImportError  # TODO: !!
-    import msgpack as s_lib
-    _packer = s_lib.Packer()
-    _unpacker = s_lib.Unpacker()
-    _pack = _packer.pack
-    _unpack = lambda data: _unpacker.feed(data) or _unpacker.unpack()
-except ImportError:
-    import json as s_lib
-    _packer = s_lib.JSONEncoder()
-    _unpacker = s_lib.JSONDecoder()
-    _pack = _packer.encode
-    _unpack = _unpacker.decode
-
-_logger = logging.getLogger(__name__)
-
-__all__ = ('PacketManager', 'PacketError')
-
-
-class PacketError(Exception):
-    pass
-
-
-class PacketManager(object):
-    """Class allowing to register new packet types and pack/unpack them.
-
-    example:
-        chat_msg = PacketManager.register('chat_msg', ('player', 'msg'),
-                                          enet.PACKET_FLAG_RELIABLE)
-        PacketManager.pack(chat_msg('Tom', 'Test message'))
-    """
-    _packet_names = {}  # mapping name -> packet
-    _packet_types = WeakValueDictionary()  # mapping type_id -> packet
-    _packet_params = WeakKeyDictionary()  # mapping packet -> type_id, send par
-    _type_id_cnt = 0
-    _frozen = False
-    _hash = None
-
-    def __init__(self):
-        # override class variables with instance variables
-        cls = self.__class__
-        self._packet_names = cls._packet_names.copy()
-        self._packet_types = cls._packet_types.copy()
-        self._packet_params = cls._packet_params.copy()
-        self._type_id_cnt = cls._type_id_cnt
-        self._frozen = False
-        self._hash = None
-
-    @classmethod
-    def register(cls, name, field_names=tuple(), channel=0, flags=enet.PACKET_FLAG_RELIABLE):
-        """Register new packet type
-
-        PacketManager.register(name, field_names, channel, flags): return class
-
-        name - name of packet class
-        field_names - names of packet fields
-
-        Warning: All packets must be registered in THE SAME ORDER in client and
-        server, BEFORE creating any connection.
-
-        Returns namedtuple class.
-        """
-        if cls._frozen == True:
-            raise PacketError("Can't register new packets after "\
-                              "connection establishment")
-        type_id = cls._type_id_cnt = cls._type_id_cnt + 1
-        packet = namedtuple(name, field_names)
-        cls._packet_names[name] = packet
-        cls._packet_types[type_id] = packet
-        cls._packet_params[packet] = (type_id, channel, flags)
-        return packet
-
-    @classmethod
-    def pack(cls, packet_id, packet):
-        """Pack data to string
-
-        PacketManager.pack(packet_id, packet_obj): return string
-
-        packet_id - identifier of packet
-        packet - object of class created by register
-
-        Returns packet packed in string, ready to send.
-        """
-        type_id = cls._packet_params[packet.__class__][0]
-        packet = (type_id, packet_id) + packet
-        cls._packet_cnt = packet_id
-        return _pack(packet)
-
-    @classmethod
-    def unpack(cls, data):
-        """Unpack data from string, return packet_id and packet
-
-        PacketManager.unpack(data): return (packet_id, packet)
-
-        data - packet data as a string
-        """
-        packet = _unpack(data)
-        try:
-            type_id, packet_id = packet[:2]
-            return packet_id, cls._packet_types[type_id](*packet[2:])
-        except KeyError:
-            _logger.warning('Unknown packet type_id: %s', type_id)
-        except:
-            _logger.warning('Packet unpacking error: %s', packet)
-        return None, None
-
-    @classmethod
-    def get_by_name(cls, name):
-        """Returns packet class with given name
-
-        PacketManager.get_by_name(name): return class
-
-        name - name of packet
-        """
-        return cls._packet_names[name]
-
-    @classmethod
-    def get_by_type(cls, type_id):
-        """Returns packet class with given type_id
-
-        PacketManager.get_by_type(name): return class
-
-        type_id - type identifier of packet
-        """
-        return cls._packet_types[type_id]
-
-    @classmethod
-    def get_params(cls, packet):
-        """Return tuple containing type_id, channel and sending flags
-
-        PacketManager.get_params(packet): return (int, int, int)
-
-        packet - packet class created by register
-        """
-        return cls._packet_params[packet]
-
-    @classmethod
-    def get_hash(cls):
-        if cls._frozen:
-            if cls._hash is None:
-                ids = cls._packet_types.keys()
-                ids.sort()
-                l = list()
-                l.append(s_lib.__name__)
-                for i in ids:
-                    p = cls._packet_types[i]
-                    l.append((i, p.__name__, p._fields))
-                # should be the same on 32 & 64 platforms
-                cls._hash = hash(tuple(l)) & 0xffffffff
-            return cls._hash
-        else:
-            _logger.warning('Attempt to get hash of not frozen PacketManager')
-
-
-update_remoteobject = PacketManager.register('update_remoteobject', (
-    'type_id',
-    'obj_id',
-    'variables'
-), 1, 0)
-chat_msg = PacketManager.register('chat_msg', (
-    'player',
-    'msg'
-))
--- a/zrpn/pygame_network/receiver.py	Sun Jul 08 18:00:38 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,12 +0,0 @@
-class Receiver(object):
-    """Base class for objects receiving packets through net_packet_name
-    functions
-    """
-    def on_connect(self):
-        pass
-
-    def on_disconnect(self):
-        pass
-
-    def on_recive(self, channel, packet_id, packet):
-        pass
--- a/zrpn/pygame_network/server.py	Sun Jul 08 18:00:38 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,62 +0,0 @@
-import logging
-from weakref import proxy
-from functools import partial
-import enet
-from receiver import Receiver
-from packet import PacketManager
-from connection import Connection
-
-_logger = logging.getLogger(__name__)
-
-
-class Server(object):
-    packet_manager = PacketManager
-    receiver_cls = None
-
-    def __init__(self, address='', port=0, connections_limit=4, # max 4095
-                 channel_limit=0, in_bandwidth=0, out_bandwidth=0):
-        address = enet.Address(address, port)
-        self.host = enet.Host(address, connections_limit, channel_limit,
-                              in_bandwidth, out_bandwidth)
-        self.peers = {}
-        self._peer_cnt = 0
-        self.packet_manager._frozen = True
-
-    def step(self, timeout=0):
-        host = self.host
-        event = host.service(timeout)
-        while event is not None:
-            if event.type == enet.EVENT_TYPE_CONNECT:
-                if event.data == self.packet_manager.get_hash():
-                    _logger.info('Connection with %s accepted', event.peer.address)
-                    peer_id = self._peer_cnt = self._peer_cnt + 1
-                    peer_id = str(peer_id)
-                    event.peer.data = peer_id
-                    connection = Connection(self, event.peer, self.packet_manager)
-                    if issubclass(self.receiver_cls, Receiver):
-                        receiver = self.receiver_cls()
-                        receiver.server = proxy(self)
-                        connection.add_receiver(receiver)
-                    self.peers[peer_id] = connection
-                    connection._connect()
-                else:
-                    _logger.warning('Connection with %s refused, PacketManager'\
-                                    ' 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()
-
-    def connections(self, exclude=[]):
-        if exclude is None:
-            return self.peers.itervalues()
-        else:
-            return (c for c in self.peers.itervalues() if c not in exclude)
-
-    def receivers(self, exclude=[]):
-        return (c._receivers[0] for c in self.peers.itervalues() if c not in exclude)
--- a/zrpn/pygame_network/syncobject.py	Sun Jul 08 18:00:38 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,99 +0,0 @@
-from weakref import WeakKeyDictionary
-from packet import *
-
-SYNCOBJECT_MODE_AUTO = 0
-SYNCOBJECT_MODE_MANUAL = 1
-
-
-class SyncObject(object):
-    """Base class for synchronized objects
-
-    The base class for local objects to be automatically
-    synchronized with remote host. Derived class should replace sync_var
-    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 packet (SYNCOBJECT_MODE_AUTO) or
-    wait with preparation for send_changes call (SYNCOBJECT_MODE_MANUAL).
-    sync_flags overrides default enet sending flags.
-    """
-    sync_var = ()
-    sync_mode = SYNCOBJECT_MODE_AUTO
-    sync_flags = None
-
-    def __init__(self, *args, **kwargs):
-        super(SyncObject, self).__init__(*args, **kwargs)
-        SyncObjectManager.register(self)
-
-    def __setattr__(self, name, value):
-        object.__setattr__(self, name, value)
-        if name in self.sync_var:
-            SyncObjectManager.changed(self, name)
-
-    def on_change(self, var_names):
-        """Callback when variable(s) was changed by remote host
-        """
-        pass
-
-    def on_reply(self, packet_id):
-        """Callback when there was reply to change from remote host
-        """
-        pass
-
-    def send_changes(self):
-        """Prepare update packet to send
-
-        When sync_mode is SYNCOBJECT_MODE_MANUAL,
-        notify SyncObjectManager to prepare update packet
-        """
-        pass
-
-    def notify_change(self, var_name):
-        """Notify SyncObjectManager that variable was changed
-        """
-        SyncObjectManager.changed(self, var_name)
-
-
-class SyncObjectManager(object):
-    """Manager of SyncObject instances, shouldn't be used by user
-
-    note:
-        Class used by SyncObject automatically, no need to use it.
-    """
-    _known_types = {}
-    _sync_objs = WeakKeyDictionary()
-    _type_id_cnt = 0
-    _obj_id_cnt = 0
-
-    @classmethod
-    def register(cls, obj):
-        """Registers new object deriving from SyncObject
-        """
-        try:
-            type_id = cls._known_types[obj.__class__]
-        except KeyError:
-            type_id = cls._type_id_cnt + 1
-            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._obj_id_cnt = obj_id
-
-    @classmethod
-    def changed(cls, obj, var_name):
-        """Prepares update packet
-        """
-        print '%s@%s updated' % (var_name, obj)
-        # TODO: finish this
-        # TODO: push update packet to queue if in auto mode
-
-# TODO: pushing update packets from SyncObject
-# TODO: mapping obj type and variables to ints
-
-
-class RemoteObject(object):
-    """Class representing remote SyncObject locally
-    """
-    pass
-
-# TODO: add methods faking SyncObject attributes
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/zrpn/pygnetic/__init__.py	Thu Sep 06 21:35:41 2012 +0200
@@ -0,0 +1,66 @@
+# -*- coding: utf-8 -*-
+"""Network library for Pygame."""
+
+import logging
+import event
+import message
+import network
+import serialization
+from handler import Handler
+from network import Server, Client
+
+__version__ = '1.0'
+_logger = logging.getLogger(__name__)
+register = message.message_factory.register
+
+
+def init(events=False, event_val=None, logging_lvl=logging.INFO,
+         n_module=('enet', 'socket'), s_module=('msgpack', 'json')):
+    """Initialize network library.
+
+    :param events: allow sending Pygame events (default False)
+    :param event_val:
+        set :const:`event.NETWORK` as
+        :const:`pygame.USEREVENT` + :attr:`event_val` (default: None)
+    :param logging_lvl:
+        level of logging messages (default :const:`logging.INFO`
+        (see: :ref:`logging-basic-tutorial`), None to skip initializing
+        logging module)
+    :param n_module:
+        name(s) of network library, first available will be used
+        (default: ['enet', 'socket'])
+    :type n_module: string or list of strings
+    :param s_module:
+        name(s) of serialization library, first available will be used
+        (default: ['msgpack', 'json'])
+    :type s_module: string or list of strings
+    :return: True if initialization ended with success
+
+    .. note::
+        Because of the dynamic loading of network library adapter,
+        :class:`Client` and :class:`Server` classes will only be
+        available after initialization.
+    """
+    global _network_module, _serialization_module
+    if logging_lvl is not None:
+        logging.basicConfig(level=logging_lvl,
+                            format='%(asctime)-8s %(levelname)-8s %(message)s',
+                            datefmt='%H:%M:%S')
+    network.select_adapter(n_module)
+    if network.selected_adapter is not None:
+        _logger.info("Using %s",
+            network.selected_adapter.__name__.split('.')[-1])
+    else:
+        _logger.critical("Can't find any network module")
+        return False
+    serialization.select_adapter(s_module)
+    if serialization.selected_adapter is not None:
+        _logger.info("Using %s",
+            serialization.selected_adapter.__name__.split('.')[-1])
+    else:
+        _logger.critical("Can't find any serialization module")
+        return False
+    if events:
+        _logger.info("Enabling pygame events")
+        event.init(event_val)
+    return True
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/zrpn/pygnetic/_utils.py	Thu Sep 06 21:35:41 2012 +0200
@@ -0,0 +1,35 @@
+# -*- coding: utf-8 -*-
+
+import logging
+from importlib import import_module
+
+_logger = logging.getLogger(__name__)
+
+
+def get_adapter(a_type, name):
+    """Return library adapter
+
+    a_type - adapter type
+    name - name of library
+    """
+    a_name = name + '_adapter'
+    try:
+        return import_module('.'.join((a_type, a_name)))
+    except ImportError as e:
+        _logger.debug("%s: %s", e.__class__.__name__, e.message)
+
+
+def find_adapter(a_type, names):
+    """Return first found adapter
+
+    a_type - adapter type
+    names - string or list of strings with names of libraries
+    """
+    if isinstance(names, basestring):
+        names = (names,)
+    for name in names:
+        a_name = name + '_adapter'
+        try:
+            return import_module('.'.join((a_type, a_name)))
+        except ImportError as e:
+            _logger.debug("%s: %s", e.__class__.__name__, e.message)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/zrpn/pygnetic/client.py	Thu Sep 06 21:35:41 2012 +0200
@@ -0,0 +1,61 @@
+# -*- coding: utf-8 -*-
+"""Module containing base class for adapters representing network clients."""
+
+import logging
+import message
+
+_logger = logging.getLogger(__name__)
+
+
+class Client(object):
+    """Class representing network client.
+
+    :param int con_limit: maximum amount of created connections (default: 1)
+    :param args: additional arguments for :term:`network adapter`
+    :param kwargs: additional keyword arguments for :term:`network adapter`
+    """
+    message_factory = message.message_factory
+
+    def __init__(self, conn_limit=1, *args, **kwargs):
+        super(Client, self).__init__(*args, **kwargs)
+        self.conn_map = {}
+        _logger.info('Client created, connections limit: %d', conn_limit)
+
+    def connect(self, host, port, message_factory=None, **kwargs):
+        """Connects to specified address.
+
+        :param string host: IP address or name of host
+        :param int port: port of host
+        :param message_factory:
+            :class:`~message.MessageFactory` used for new connection
+            (default: :attr:`message_factory`)
+        :param kwargs: additional keyword arguments for :term:`network adapter`
+        :return: new :class:`~.connection.Connection`
+        """
+        if message_factory is None:
+            message_factory = self.message_factory
+        _logger.info('Connecting to %s:%d', host, port)
+        message_factory.set_frozen()
+        connection, c_id = self._create_connection(host, port,
+            message_factory, **kwargs)
+        self.conn_map[c_id] = connection
+        return connection
+
+    def update(self, timeout=0):
+        """Process network traffic and update connections.
+
+        :param int timeout:
+            waiting time for network events in milliseconds
+            (default: 0 - no waiting)
+        """
+        pass
+
+    def _connect(self, c_id):
+        self.conn_map[c_id]._connect()
+
+    def _disconnect(self, c_id):
+        self.conn_map[c_id]._disconnect()
+        del self.conn_map[c_id]
+
+    def _receive(self, c_id, data, **kwargs):
+        self.conn_map[c_id]._receive(data, **kwargs)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/zrpn/pygnetic/connection.py	Thu Sep 06 21:35:41 2012 +0200
@@ -0,0 +1,125 @@
+# -*- coding: utf-8 -*-
+"""
+Module containing base class for adapters representing network connections.
+"""
+
+import re
+import logging
+from weakref import proxy
+from functools import partial
+import message
+import event
+
+_logger = logging.getLogger(__name__)
+
+
+class Connection(object):
+    """Class allowing to send messages
+
+    :param parent: parent :class:`~.client.Client` or :class:`~.server.Server`
+    :param conn_obj: connection object
+    :param message_factory: :class:`~.message.MessageFactory` object
+
+    .. note::
+        It's created by :class:`~.client.Client` or :class:`~.server.Server`
+        and shouldn't be created manually.
+
+    Sending is possible in two ways:
+
+    * using :meth:`net_message_name` methods, where ``message_name``
+      is name of message registered in :class:`~.message.MessageFactory`
+    * using :meth:`send` method with message as argument
+    """
+    address = ('', '')
+
+    def __init__(self, parent, conn_obj, message_factory, *args, **kwargs):
+        super(Connection, self).__init__(*args, **kwargs)
+        self.parent = proxy(parent)
+        self.message_factory = message_factory
+        self.message_factory.reset_context(self)
+        self.handlers = []
+        self.data_sent = 0
+        self.data_received = 0
+        self.messages_sent = 0
+        self.messages_received = 0
+
+    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.
+
+        :param message:
+            class created by :meth:`~.message.MessageFactory.register`
+            or message name
+        :param args: parameters used to initialize message object
+        :param kwargs: keyword parameters used to initialize message object
+        """
+        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'[^_a-z](\d+)', e.message, re.I)
+            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', name, self.address)
+        self.data_sent += len(data)
+        self.messages_sent += 1
+        return self._send_data(data, **params)
+
+    def _receive(self, data, **kwargs):
+        self.data_received += len(data)
+        for message in self.message_factory.unpack_all(data, self):
+            self.messages_received += 1
+            name = message.__class__.__name__
+            _logger.info('Received %s message from %s', name, self.address)
+            event.received(self, message)
+            for h in self.handlers:
+                getattr(h, 'net_' + name, h.on_recive)(message, **kwargs)
+
+    def _connect(self):
+        _logger.info('Connected to %s', self.address)
+        event.connected(self)
+        for h in self.handlers:
+            h.on_connect()
+
+    def _disconnect(self):
+        _logger.info('Disconnected from %s', self.address)
+        event.disconnected(self)
+        for h in self.handlers:
+            h.on_disconnect()
+
+    def disconnect(self, *args):
+        """Request a disconnection.
+
+        :param args: additional arguments for :term:`network adapter`
+        """
+        pass
+
+    def add_handler(self, handler):
+        """Add new Handler to handle messages.
+
+        :param handler: instance of :class:`~.handler.Handler` subclass
+        """
+        self.handlers.append(handler)
+        handler.connection = proxy(self)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/zrpn/pygnetic/discovery.py	Thu Sep 06 21:35:41 2012 +0200
@@ -0,0 +1,147 @@
+import logging
+import socket
+import select
+import threading
+import SocketServer
+from collections import OrderedDict
+from itertools import islice
+import message
+
+_logger = logging.getLogger(__name__)
+
+message_factory = message.MessageFactory()
+
+register = message_factory.register('register', (
+    'oid', 'name', 'host', 'port'
+))
+get_servers = message_factory.register('get_servers', (
+    'oid', 'current', 'part_size'
+))
+ping = message_factory.register('ping', (
+    'oid', 'sid'
+))
+response = message_factory.register('response', (
+    'oid', 'value', 'error'
+))
+
+
+class Errors(object):
+    NO_ERROR = 0
+    TIMEOUT = 1
+
+
+class ServerHandler(SocketServer.BaseRequestHandler):
+    def handle(self):
+        data, socket = self.request
+        _logger.debug('Received data: %r', data)
+        message = message_factory.unpack(data)
+        if message is None:
+            return
+        name = message.__class__.__name__
+        _logger.info('Received %s message from %s', name, self.client_address)
+        ret_val, err = getattr(self, 'net_' + name)(message)
+        ack_data = message_factory.pack(response(message.oid, ret_val, err))
+        cnt = socket.sendto(ack_data, self.client_address)
+        _logger.info('Sent response to %s', self.client_address)
+        _logger.debug('Sent %d bytes: %r', cnt, ack_data)
+
+    def unknown_msg(self, message):
+        name = message.__class__.__name__
+        _logger.warning('Received unknown %s message from %s',
+                        name, self.client_address)
+
+    def net_register(self, message):
+        sid = self.server.id_cnt = self.server.id_cnt + 1
+        self.server.servers[sid] = [message[1:], 0]
+        return sid, Errors.NO_ERROR
+
+    def net_get_servers(self, message):
+        s = message.current * message.part_size
+        e = s + message.part_size
+        servers = self.server.servers
+        d = {k: servers[k][0] for k in islice(servers.iterkeys(), s, e)}
+        return d, Errors.NO_ERROR
+
+    def net_ping(self, message):
+        s = self.server.servers.get(message.sid)
+        if s is not None:
+            s[1] = 0
+            return None, Errors.NO_ERROR
+        else:
+            return None, Errors.TIMEOUT
+
+
+class DiscoveryServer(SocketServer.UDPServer, object):
+    allow_reuse_address = True
+    max_packet_size = 8192
+    cleaner_period = 10
+    ping_timeout = 6
+
+    def __init__(self, host='', port=5000):
+        super(DiscoveryServer, self).__init__((host, port), ServerHandler)
+        self.servers = OrderedDict()
+        self.id_cnt = 0
+        self.c_stop = threading.Event()
+        self.c_thread = threading.Thread(target=self.cleaner)
+        self.c_thread.daemon = True
+        self.c_thread.start()
+
+    def __del__(self):
+        self.c_stop.set()
+
+    def cleaner(self):
+        while not self.c_stop.is_set():
+            keys = []
+            for k, v in self.servers.iteritems():
+                v[1] += 1
+                if v[1] > self.ping_timeout:
+                    keys.append(k)
+            for k in keys:
+                del self.servers[k]
+            self.c_stop.wait(self.cleaner_period)
+
+
+class DiscoveryClient(object):
+    max_packet_size = 8192
+
+    def __init__(self, host, port=5000):
+        self.address = socket.gethostbyname(host), port
+        self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+        self.response = None
+        self.callback = None
+        self.oid = 0
+
+    def _send_msg(self, message, *args):
+        oid = self.oid = self.oid + 1
+        data = message_factory.pack(message(oid, *args))
+        cnt = self.socket.sendto(data, self.address)
+        self.response = None
+        name = message.__class__.__name__
+        _logger.info('Sent %s message to %s', name, self.address)
+        _logger.debug('Sent %d bytes: %r', cnt, data)
+        return oid
+
+    def update(self, timeout=0):
+        r = select.select([self.socket], [], [], timeout / 1000.0)[0]
+        if len(r) > 0:
+            data, address = self.socket.recvfrom(self.max_packet_size)
+            if address == self.address:
+                _logger.debug('Received data: %r', data)
+                r = self.response = message_factory.unpack(data)
+                _logger.info('Received response from %s', address)
+                if callable(self.callback):
+                    self.callback(r)
+            else:
+                _logger.info('Unexpected data from %s', address)
+
+    def close(self):
+        self.socket.close()
+
+    def register(self, name, host, port):
+        return self._send_msg(register, name, host, port)
+
+    def ping(self, sid):
+        return self._send_msg(ping, sid)
+
+    def get_servers(self, current=0, part_size=10):
+        return self._send_msg(get_servers, current, part_size)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/zrpn/pygnetic/event.py	Thu Sep 06 21:35:41 2012 +0200
@@ -0,0 +1,73 @@
+# -*- coding: utf-8 -*-
+"""Module defining Pygame events."""
+
+__all__ = ('NETWORK',
+           'NET_DISCONNECTED',
+           'NET_CONNECTED',
+           'NET_ACCEPTED',
+           'NET_RECEIVED')
+
+NETWORK = 30
+NET_DISCONNECTED = 0
+NET_CONNECTED = 1
+NET_ACCEPTED = 2
+NET_RECEIVED = 3
+
+
+def accepted(connection):
+    pass
+
+
+def connected(connection):
+    pass
+
+
+def disconnected(connection):
+    pass
+
+
+def received(connection, message):
+    pass
+
+
+def init(event_val=None):
+    global NETWORK, connected, disconnected, received
+    import pygame
+    from pygame.event import Event
+    from pygame.locals import USEREVENT
+    if event_val is not None:
+        NETWORK = USEREVENT + event_val
+
+    def _accepted(connection):
+        pygame.event.post(Event(NETWORK, {
+            'net_type': NET_ACCEPTED,
+            #'connection': proxy(connection)
+            'connection': connection
+        }))
+    accepted = _accepted
+
+    def _connected(connection):
+        pygame.event.post(Event(NETWORK, {
+            'net_type': NET_CONNECTED,
+            #'connection': proxy(connection)
+            'connection': connection
+        }))
+    connected = _connected
+
+    def _disconnected(connection):
+        pygame.event.post(Event(NETWORK, {
+            'net_type': NET_DISCONNECTED,
+            #'connection': proxy(connection)
+            'connection': connection
+        }))
+    disconnected = _disconnected
+
+    def _received(connection, message, channel=0):
+        pygame.event.post(Event(NETWORK, {
+            'net_type': NET_RECEIVED,
+            #'connection': proxy(connection),
+            'connection': connection,
+            'message': message,
+            'msg_type': message.__class__
+        }))
+    received = _received
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/zrpn/pygnetic/handler.py	Thu Sep 06 21:35:41 2012 +0200
@@ -0,0 +1,24 @@
+# -*- coding: utf-8 -*-
+"""Module containing Handler base class."""
+
+class Handler(object):
+    """Base class for objects handling messages through
+    :meth:`net_message_name` methods
+    """
+    def on_connect(self):
+        """Called when connection is established."""
+        pass
+
+    def on_disconnect(self):
+        """Called when connection is closed."""
+        pass
+
+    def on_recive(self, message, **kwargs):
+        """Called when message is received, but no corresponding
+        net_message_name method exist.
+
+        :param message: received message
+        :param kwargs:
+            additional keyword arguments from :term:`network adapter`
+        """
+        pass
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/zrpn/pygnetic/message.py	Thu Sep 06 21:35:41 2012 +0200
@@ -0,0 +1,167 @@
+# -*- coding: utf-8 -*-
+"""Module containing MessageFactory and predefined messages"""
+
+import logging
+from collections import namedtuple
+from weakref import WeakKeyDictionary, WeakValueDictionary
+import serialization
+
+_logger = logging.getLogger(__name__)
+
+
+class MessageFactory(object):
+    """Class allowing to register new message types and pack/unpack them.
+
+    :param s_adapter:
+        :term:`serialization adapter`
+        (default: None - library selected with :func:`.init`)
+    """
+    def __init__(self, s_adapter=None):
+        self._message_names = {}  # name -> message
+        self._message_types = WeakValueDictionary()  # type_id -> message
+        self._message_params = WeakKeyDictionary()  # message -> type_id, send kwargs
+        if s_adapter is None:
+            self.s_adapter = serialization
+        else:
+            self.s_adapter = s_adapter
+        self._type_id_cnt = 0
+        self._frozen = False
+        self._hash = None
+
+    def register(self, name, field_names=tuple(), **kwargs):
+        """Register new message type.
+
+        :param name: name of message class
+        :param field_names: list of names of message fields
+        :param kwargs: additional keyword arguments for send method
+        :return: message class (namedtuple)
+        """
+        if self._frozen == True:
+            _logger.warning("Can't register new messages after connection "
+                            "establishment")
+        type_id = self._type_id_cnt = self._type_id_cnt + 1
+        packet = namedtuple(name, field_names)
+        self._message_names[name] = packet
+        self._message_types[type_id] = packet
+        self._message_params[packet] = (type_id, kwargs)
+        return packet
+
+    def pack(self, message):
+        """Pack data to string.
+
+        :param message: object of class created by register
+        :return: string
+        """
+        type_id = self._message_params[message.__class__][0]
+        message = (type_id,) + message
+        data = self.s_adapter.pack(message)
+        _logger.debug("Packing message (length: %d)", len(data))
+        return data
+
+    def set_frozen(self):
+        """Disable ability to register new messages to allow generation
+        of hash.
+        """
+        self._frozen = True
+
+    def reset_context(self, context):
+        """Prepares object to behave as context for stream unpacking.
+
+        :param context: object which will be prepared
+        """
+        context._unpacker = self.s_adapter.unpacker()
+
+    def _process_message(self, message):
+        try:
+            type_id = message[0]
+            return self._message_types[type_id](*message[1:])
+        except KeyError:
+            _logger.error('Unknown message type_id: %s', type_id)
+        except:
+            _logger.error('Message unpacking error: %s', message)
+
+    def unpack(self, data):
+        """Unpack message from string.
+
+        :param data: packed message data as a string
+        :return: message
+        """
+        _logger.debug("Unpacking message (length: %d)", len(data))
+        message = self.s_adapter.unpack(data)
+        if message is not None:
+            return self._process_message(message)
+        else:
+            _logger.error('Data corrupted')
+            _logger.debug('Data: %r', data)
+
+    def unpack_all(self, data, context):
+        """Feed unpacker with data from stream and unpack all messages.
+
+        :param data: packed message(s) data as a string
+        :param context: object previously prepared with :meth:`reset_context`
+        :return: iterator over messages
+        """
+        _logger.debug("Unpacking data (length: %d)", len(data))
+        context._unpacker.feed(data)
+        try:
+            for message in context._unpacker:
+                yield self._process_message(message)
+        except:
+            _logger.error('Data corrupted')
+            self._reset_unpacker()  # prevent from corrupting next data
+            return
+
+    def get_by_name(self, name):
+        """Returns message class with given name.
+
+        :param name: name of message
+        :return: message class (namedtuple)
+        """
+        return self._message_names[name]
+
+    def get_by_type(self, type_id):
+        """Returns message class with given type_id.
+
+        :param type_id: type identifier of message
+        :return: message class (namedtuple)
+        """
+        return self._message_types[type_id]
+
+    def get_params(self, message_cls):
+        """Return tuple containing type_id, and sending keyword arguments
+
+        :param message_cls: message class created by register
+        :return: int, dict
+        """
+        return self._message_params[message_cls]
+
+    def get_hash(self):
+        """Calculate and return hash.
+
+        Hash depends on registered messages and used serializing library.
+
+        :return: int
+        """
+        if self._frozen:
+            if self._hash is None:
+                ids = self._message_types.keys()
+                ids.sort()
+                l = list()
+                a = getattr(self.s_adapter, 'selected_adapter', self.s_adapter)
+                l.append(a.__name__)
+                for i in ids:
+                    p = self._message_types[i]
+                    l.append((i, p.__name__, p._fields))
+                # should be the same on 32 & 64 platforms
+                self._hash = hash(tuple(l)) & 0xffffffff
+            return self._hash
+        else:
+            _logger.warning('Attempt to get hash of not frozen MessageFactory')
+
+
+message_factory = MessageFactory()
+#update_remoteobject = message_factory.register('update_remoteobject', (
+#    'type_id',
+#    'obj_id',
+#    'variables'
+#), channel=1, flags=0)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/zrpn/pygnetic/network/__init__.py	Thu Sep 06 21:35:41 2012 +0200
@@ -0,0 +1,49 @@
+# -*- coding: utf-8 -*-
+"""Network adapters."""
+
+from .. import _utils
+
+selected_adapter = None
+
+
+def select_adapter(names):
+    global selected_adapter
+    selected_adapter = _utils.find_adapter(__name__, names)
+
+
+def get_adapter(name):
+    return _utils.find_adapter(__name__, name)
+
+
+class Client(object):
+    "Class creating its instance based on selected :term:`network adapter`."
+
+    def __new__(cls, *args, **kwargs):
+        "Create instance of Client class depending on selected network adapter"
+        b = cls.__bases__
+        if selected_adapter is None:
+            raise AttributeError("Client adapter is not selected")
+        if Client in b:  # creation by inheritance
+            i = b.index(Client) + 1
+            cls.__bases__ = b[:i] + (selected_adapter.Client,) + b[i:]
+            return super(cls, cls).__new__(cls, *args, **kwargs)
+        else:  # direct object creation
+            # can't assign to __bases__ - bugs.python.org/issue672115
+            return selected_adapter.Client(*args, **kwargs)
+
+
+class Server(object):
+    "Class creating its instance based on selected :term:`network adapter`."
+
+    def __new__(cls, *args, **kwargs):
+        "Create instance of Server class depending on selected network adapter"
+        b = cls.__bases__
+        if selected_adapter is None:
+            raise AttributeError("Client adapter is not selected")
+        if Server in b:  # creation by inheritance
+            i = b.index(Server) + 1
+            cls.__bases__ = b[:i] + (selected_adapter.Server,) + b[i:]
+            return super(Server, cls).__new__(cls, *args, **kwargs)
+        else:  # direct object creation
+            # can't assign to __bases__ - bugs.python.org/issue672115
+            return selected_adapter.Server(*args, **kwargs)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/zrpn/pygnetic/network/enet_adapter.py	Thu Sep 06 21:35:41 2012 +0200
@@ -0,0 +1,113 @@
+# -*- coding: utf-8 -*-
+"""Module containing network adapter for enet."""
+
+import logging
+import enet
+from .. import connection, server, client
+
+_logger = logging.getLogger(__name__)
+
+
+class Connection(connection.Connection):
+    def __init__(self, parent, peer, message_factory, *args, **kwargs):
+        super(Connection, self).__init__(parent, peer, message_factory,
+                                         *args, **kwargs)
+        self.peer = peer
+
+    def __del__(self):
+        self.peer.disconnect_now()
+
+    def _send_data(self, data, channel=0, flags=enet.PACKET_FLAG_RELIABLE,
+                   **kwargs):
+        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:
+            self.peer.disconnect_now()
+
+    @property
+    def connected(self):
+        """Connection state."""
+        return self.peer.state == enet.PEER_STATE_CONNECTED
+
+    @property
+    def address(self):
+        """Connection address."""
+        address = self.peer.address
+        return address.host, address.port
+
+
+class Server(server.Server):
+    def __init__(self, host='', port=0, conn_limit=4, *args, **kwargs):
+        super(Server, self).__init__(host, port, conn_limit, *args, **kwargs)
+        host = enet.Address(host, port)
+        self.host = enet.Host(host, conn_limit, *args, **kwargs)
+        self._peer_cnt = 0
+
+    def update(self, timeout=0):
+        host = self.host
+        event = host.service(timeout)
+        while event is not None:
+            if event.type == enet.EVENT_TYPE_CONNECT:
+                peer_id = str(self._peer_cnt + 1)
+                if self._accept(Connection, event.peer, event.peer.address,
+                                peer_id, event.data):
+                    event.peer.data = peer_id
+                    self._peer_cnt += 1
+                else:
+                    event.peer.disconnect_now()
+            elif event.type == enet.EVENT_TYPE_DISCONNECT:
+                self._disconnect(event.peer.data)
+            elif event.type == enet.EVENT_TYPE_RECEIVE:
+                self._receive(event.peer.data, event.packet.data,
+                    channel=event.channelID)
+            event = host.check_events()
+
+    @property
+    def address(self):
+        """Server address."""
+        address = self.host.address
+        return address.host, address.port
+
+
+class Client(client.Client):
+    def __init__(self, conn_limit=1, *args, **kwargs):
+        super(Client, self).__init__(*args, **kwargs)
+        self.host = enet.Host(None, conn_limit)
+        self._peer_cnt = 0
+
+    def _create_connection(self, host, port, message_factory, channels=1,
+                           **kwargs):
+        host = enet.Address(host, port)
+        peer_id = self._peer_cnt = self._peer_cnt + 1
+        peer_id = str(peer_id)
+        peer = self.host.connect(host, channels, message_factory.get_hash())
+        peer.data = peer_id
+        connection = Connection(self, peer, message_factory)
+        return connection, peer_id
+
+    def update(self, timeout=0):
+        if len(self.conn_map) == 0:
+            return
+        host = self.host
+        event = host.service(timeout)
+        while event is not None:
+            if event.type == enet.EVENT_TYPE_CONNECT:
+                self._connect(event.peer.data)
+            elif event.type == enet.EVENT_TYPE_DISCONNECT:
+                self._disconnect(event.peer.data)
+            elif event.type == enet.EVENT_TYPE_RECEIVE:
+                self._receive(event.peer.data, event.packet.data,
+                              channel=event.channelID)
+            event = host.check_events()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/zrpn/pygnetic/network/socket_adapter.py	Thu Sep 06 21:35:41 2012 +0200
@@ -0,0 +1,142 @@
+# -*- coding: utf-8 -*-
+"""Module containing network adapter for socket (asyncore.dispacher)."""
+
+import logging
+import socket
+import struct
+import asyncore
+from collections import deque
+from .. import connection, server, client
+
+_logger = logging.getLogger(__name__)
+_connect_struct = struct.Struct('!I')
+
+
+class Dispacher(asyncore.dispatcher, object):
+    pass
+
+
+class Connection(connection.Connection, Dispacher):
+    # maximum amount of data received / sent at once
+    recv_buffer_size = 4096
+
+    def __init__(self, parent, socket, message_factory, *args, **kwargs):
+        super(Connection, self).__init__(
+            parent, socket, message_factory,
+            socket, None,
+            * args, **kwargs)
+        #self.send_queue = deque()
+        self.send_buffer = bytearray()
+        #self.recv_buffer = bytearray(b'\0' * self.recv_buffer_size)
+
+    def _send_data(self, data, **kwargs):
+        self.send_buffer.extend(data)
+        self._send_part()
+
+#    def _send_data2(self, data, **kwargs):
+#        if len(self.send_buffer) == 0:
+#            self.send_buffer = data
+#        else:
+#            self.send_queue.append(data)
+#        self._send_part()
+
+    def handle_write(self):
+        self._send_part()
+
+    def _send_part(self):
+        try:
+            num_sent = asyncore.dispatcher.send(self, self.send_buffer)
+        except socket.error:
+            self.handle_error()
+            return
+        self.send_buffer = self.send_buffer[num_sent:]
+
+    def writable(self):
+        return (not self.connected) or len(self.send_buffer)
+
+    def handle_read(self):
+        data = self.recv(self.recv_buffer_size)
+        if data:
+            self._receive(data)
+
+#    def handle_read2(self):
+#        # tinkering with dispatcher internal variables,
+#        # because it doesn't support socket.recv_into
+#        try:
+#            num_rcvd = self.socket.recv_into(self.recv_buffer)
+#            if not num_rcvd:
+#                self.handle_close()
+#            else:
+#                self._receive(self.recv_buffer[:num_rcvd])
+#        except socket.error, why:
+#            if why.args[0] in asyncore._DISCONNECTED:
+#                self.handle_close()
+#            else:
+#                self.handle_error()
+#            return
+
+    def handle_connect(self):
+        self.addr = self.socket.getpeername()
+        self._connect()
+
+    def handle_close(self):
+        self.parent._disconnect(self.socket.fileno())
+        self.close()
+
+    def log_info(self, message, type='info'):
+        return getattr(_logger, type)(message)
+
+    def disconnect(self, *args):
+        self.parent._disconnect(self.socket.fileno())
+        self.close()
+
+    @property
+    def address(self):
+        return self.addr
+
+
+class Server(server.Server, Dispacher):
+    connection = Connection
+
+    def __init__(self, host='', port=0, con_limit=4, *args, **kwargs):
+        super(Server, self).__init__(
+            host, port, con_limit,
+            None, None,
+            *args, **kwargs)
+        self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
+        # disable Nagle buffering algorithm
+        self.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
+        self.set_reuse_addr()
+        self.bind((host, port))
+        self.listen(con_limit)
+
+    def handle_accept(self):
+        pair = self.accept()
+        if pair is not None:
+            sock, addr = pair
+            sock.settimeout(0.5)  # TODO: sth better?
+            mf_hash = _connect_struct.unpack(sock.recv(4))[0]
+            sock.setblocking(0)
+            if not self._accept(Connection, sock, addr, self._fileno, mf_hash):
+                sock.close()
+
+    def update(self, timeout=0):
+        asyncore.loop(timeout / 1000.0, False, None, 1)
+
+
+class Client(client.Client):
+    def __init__(self, conn_limit=0, *args, **kwargs):
+        super(Client, self).__init__(*args, **kwargs)
+        self._sock_cnt = 0
+
+    def _create_connection(self, host, port, message_factory, **kwargs):
+        conn = Connection(self, None, message_factory)
+        conn.create_socket(socket.AF_INET, socket.SOCK_STREAM)
+        # disable Nagle buffering algorithm
+        conn.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
+        conn.connect((host, port))
+        conn.socket.send(_connect_struct.pack(message_factory.get_hash()))
+        return conn, conn.socket.fileno()
+
+    def update(self, timeout=0):
+        asyncore.loop(timeout / 1000.0, False, self.conn_map, 1)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/zrpn/pygnetic/serialization/__init__.py	Thu Sep 06 21:35:41 2012 +0200
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+"""Serialization adapters."""
+
+from .. import _utils
+
+selected_adapter = None
+pack = unpack = unpacker = None
+
+
+def select_adapter(names):
+    global selected_adapter, pack, unpack, unpacker
+    selected_adapter = _utils.find_adapter(__name__, names)
+    if selected_adapter is not None:
+        pack = selected_adapter.pack
+        unpack = selected_adapter.unpack
+        unpacker = selected_adapter.unpacker
+
+
+def get_adapter(name):
+    return _utils.find_adapter(__name__, name)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/zrpn/pygnetic/serialization/json_adapter.py	Thu Sep 06 21:35:41 2012 +0200
@@ -0,0 +1,35 @@
+# -*- coding: utf-8 -*-
+"""Module containing serialization adapter for json."""
+
+import json
+
+
+class JSONDecoder(json.JSONDecoder):
+    """JSONDecoder with streaming feature"""
+    def __init__(self):
+        super(JSONDecoder, self).__init__()
+        self.buffer = bytearray()
+
+    def feed(self, data):
+        self.buffer.extend(data)
+
+    def decode(self):
+        try:
+            obj, end = self.scan_once(self.buffer.decode(), 0)
+            del self.buffer[:end]
+            return obj
+        except:
+            raise StopIteration('No more data to decode.')
+
+    next = decode
+
+
+def unpack(*args):
+    try:
+        return json.loads(*args)
+    except ValueError:
+        pass
+
+
+pack = json.dumps
+unpacker = JSONDecoder
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/zrpn/pygnetic/serialization/msgpack_adapter.py	Thu Sep 06 21:35:41 2012 +0200
@@ -0,0 +1,8 @@
+# -*- coding: utf-8 -*-
+"""Module containing serialization adapter for msgpack."""
+
+import msgpack
+
+pack = msgpack.packb
+unpack = msgpack.unpackb
+unpacker = msgpack.Unpacker
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/zrpn/pygnetic/server.py	Thu Sep 06 21:35:41 2012 +0200
@@ -0,0 +1,98 @@
+# -*- coding: utf-8 -*-
+"""Module containing base class for adapters representing network servers."""
+
+import logging
+from weakref import proxy
+import message
+import event
+from handler import Handler
+
+_logger = logging.getLogger(__name__)
+
+
+class Server(object):
+    """Class representing network server.
+
+    :param string host: IP address or name of host (default: "" - any)
+    :param int port: port of host (default: 0 - any)
+    :param int con_limit: maximum amount of created connections (default: 4)
+    :param args: additional arguments for :term:`network adapter`
+    :param kwargs: additional keyword arguments for :term:`network adapter`
+    """
+    message_factory = message.message_factory
+    handler = None
+
+    def __init__(self, host='', port=0, conn_limit=4, *args, **kwargs):
+        super(Server, self).__init__(*args, **kwargs)
+        _logger.info('Server created %s:%d, connections limit: %d',
+                     host, port, conn_limit)
+        self.message_factory.set_frozen()
+        self.conn_map = {}
+        self.conn_limit = conn_limit
+
+    def update(self, timeout=0):
+        """Process network traffic and update connections.
+
+        :param int timeout:
+            waiting time for network events in milliseconds
+            (default: 0 - no waiting)
+        """
+        pass
+
+    def _accept(self, connection, socket, address, c_id, mf_hash):
+        if mf_hash == self.message_factory.get_hash():
+            _logger.info('Connection with %s accepted', address)
+            conn = connection(self, socket, self.message_factory)
+            if self.handler is not None and issubclass(self.handler, Handler):
+                handler = self.handler()
+                handler.server = proxy(self)
+                conn.add_handler(handler)
+            self.conn_map[c_id] = conn
+            event.accepted(self)
+            conn._connect()
+            return True
+        else:
+            _logger.info('Connection with %s refused, MessageFactory'\
+                            ' hash incorrect', address)
+            return False
+
+    def _disconnect(self, c_id):
+        conn = self.conn_map.get(c_id)
+        if conn is not None:
+            conn._disconnect()
+            del self.conn_map[c_id]
+
+    def _receive(self, c_id, data, **kwargs):
+        self.conn_map[c_id]._receive(data, **kwargs)
+
+    def connections(self, exclude=None):
+        """Returns iterator over connections.
+
+        :param exclude: list of connections to exclude
+        :return:
+            iterator over
+            :class:`connections <.connection.Connection>`
+        """
+        if exclude is None:
+            return self.conn_map.itervalues()
+        else:
+            return (c for c in self.conn_map.itervalues() if c not in exclude)
+
+    def handlers(self, exclude=None):
+        """Returns iterator over handlers.
+
+        :param exclude: list of connections to exclude
+        :return:
+            iterator over
+            :class:`handlers <.handler.Handler>`
+        """
+        if exclude is None:
+            return (c.handlers[0] for c in self.conn_map.itervalues())
+        else:
+            return (c.handlers[0] for c in self.conn_map.itervalues()
+                    if c not in exclude)
+
+    @property
+    def address(self):
+        """Server address."""
+        return None, None
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/zrpn/pygnetic/syncobject.py	Thu Sep 06 21:35:41 2012 +0200
@@ -0,0 +1,104 @@
+# -*- coding: utf-8 -*-
+"""Module containing base classes for synchronized objects."""
+
+from weakref import WeakKeyDictionary
+from message import *
+
+
+class Mode(object):
+    AUTO = 0
+    MANUAL = 1
+
+
+class SyncObject(object):
+    """Base class for synchronized objects
+
+    The base class for local objects to be automatically
+    synchronized with remote host. Derived class should replace sync_var
+    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 (Mode.AUTO) or
+    wait with preparation for send_changes call (Mode.MANUAL).
+    sync_flags overrides default enet sending flags.
+    """
+    sync_var = ()
+    sync_mode = Mode.AUTO
+    sync_flags = None
+
+    def __init__(self, *args, **kwargs):
+        super(SyncObject, self).__init__(*args, **kwargs)
+        SyncObjectManager.register(self)
+
+    def __setattr__(self, name, value):
+        object.__setattr__(self, name, value)
+        if name in self.sync_var:
+            SyncObjectManager.changed(self, name)
+
+    def on_change(self, var_names):
+        """Callback when variable(s) was changed by remote host
+        """
+        pass
+
+    def on_reply(self, message_id):
+        """Callback when there was reply to change from remote host
+        """
+        pass
+
+    def send_changes(self):
+        """Prepare update message to send
+
+        When sync_mode is SYNCOBJECT_MODE_MANUAL,
+        notify SyncObjectManager to prepare update message
+        """
+        pass
+
+    def notify_change(self, var_name):
+        """Notify SyncObjectManager that variable was changed
+        """
+        SyncObjectManager.changed(self, var_name)
+
+
+class SyncObjectManager(object):
+    """Manager of SyncObject instances, shouldn't be used by user
+
+    note:
+        Class used by SyncObject automatically, no need to use it.
+    """
+    _known_types = {}
+    _sync_objs = WeakKeyDictionary()
+    _type_id_cnt = 0
+    _obj_id_cnt = 0
+
+    @classmethod
+    def register(cls, obj):
+        """Registers new object deriving from SyncObject
+        """
+        try:
+            type_id = cls._known_types[obj.__class__]
+        except KeyError:
+            type_id = cls._type_id_cnt + 1
+            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, set())
+        cls._obj_id_cnt = obj_id
+
+    @classmethod
+    def changed(cls, obj, var_name):
+        """Prepares update message
+        """
+        cls._sync_objs[obj][2].add(var_name)
+        # TODO: finish this
+        # TODO: push update message to queue if in auto mode
+
+# TODO: pushing update messages from SyncObject
+# TODO: mapping obj type and variables to ints
+
+
+class RemoteObject(object):
+    """Class representing remote SyncObject locally
+    """
+    pass
+
+# TODO: add methods faking SyncObject attributes
--- a/zrpn/server.py	Sun Jul 08 18:00:38 2012 +0200
+++ b/zrpn/server.py	Thu Sep 06 21:35:41 2012 +0200
@@ -1,23 +1,23 @@
 import time
 import logging
-import pygame_network as net
+import pygnetic as net
 import board
-import packets
+import messages
 
 
-class ClientReceiver(net.Receiver):
+class ClientReceiver(net.Handler):
     board = board.Board()
 
     def __init__(self):
         self.click_time = time.time()
         self.score = 0
 
-    def net_click(self, channel, packet_id, packet):
+    def net_click(self, message, **kwargs):
         t = time.time()
         if t - self.click_time < 0.5:
             return
         self.click_time = t
-        animations = self.board.kill_one(packet)
+        animations = self.board.kill_one(message)
         ans, extra, net_adds = self.board.update()
         animations.extend(ans)
         #TODO: scoring.   check for animations of kind == 'collumn_row'
@@ -32,7 +32,7 @@
 
         print self.score, id(self)
 
-        max_score = max(r.score for r in self.server.receivers())
+        max_score = max(r.score for r in self.server.handlers())
         self.connection.net_score(self.score, max_score)
         for connection in self.server.connections():
             connection.net_animations(animations)
@@ -43,22 +43,22 @@
             connection.net_score(None, max_score)
 
     def on_connect(self):
-        #logging.debug("ClientReceiver.on_connect")
+        logging.info('Connected :)')
         self.connection.net_init(self.board.board, self.board.cells)
 
 
-class Server(object):
+class Server():
     def __init__(self, port):
-        self.server = net.Server(port=port, connections_limit=10)
-        self.server.receiver_cls = ClientReceiver
+        self.server = net.Server(port=port, conn_limit=10)
+        self.server.handler = ClientReceiver
 
     def run(self):
         while True:
-            self.server.step(1000)
+            self.server.update(1000)
 
 
 def main():
-    logging.basicConfig(level=logging.DEBUG)
+    net.init(logging_lvl=logging.DEBUG)
     Server(10000).run()
 
 if __name__ == '__main__':