Szymon Wróblewski avatar Szymon Wróblewski committed cb053ce

updated readme, discovery service and MessageFactory

Comments (0)

Files changed (9)

 pygnetic --- Easy networking in Pygame
-==============================================
+======================================
 
 **pygnetic** is a library designed to help in the development of
 network games and applications in `Pygame <http://www.pygame.org>`_.
 
+
+Features
+--------
+
+* Two approaches to handle network events
+   * generating events in pygame queue
+   * using handler classes
+* Efficient packaging of data through the message system
+* Support for multiple network and serialization libraries
+
+
+Installation
+------------
+
+**pygnetic** can be installed with
+`windows installer <http://pypi.python.org/pypi/pygnetic/#downloads>`_
+or with `pip <http://www.pip-installer.org>`_::
+
+   pip install pygnetic
+
+
 Optional requirements
 ---------------------
 
 * `pyenet <http://code.google.com/p/pyenet/>`_
 
 
-Development
------------
+Resources
+---------
 
-The source code is hosted on
-`Bitbucket <https://bitbucket.org/bluex/pygnetic>`_::
-
-   hg clone https://bitbucket.org/bluex/pygnetic
-   
-Development blog can be found here: http://pygame-networking.blogspot.com
+* Package on PyPI -- http://pypi.python.org/pypi/pygnetic
+* Repository on Bitbucket -- https://bitbucket.org/bluex/pygnetic
+* Documentation -- http://pygnetic.readthedocs.org
+* Development blog -- http://pygame-networking.blogspot.com
 
 
 Credits

doc/source/index.rst

 
    pip install pygnetic
 
+
 Optional requirements
 ---------------------
 

pygnetic/_utils.py

 
 _logger = logging.getLogger(__name__)
 
-def select_adapter(a_type, names):
+
+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

pygnetic/discovery/client.py

+from weakref import proxy
+from .. import __init__ as net
+import messages
+
+
+class Handler(net.Handler):
+    def net_register_ack(self, message, **kwargs):
+        self.parent.sid = message.sid
+
+    def net_servers_list(self, message, **kwargs):
+        pass
+
+    def net_error(self, message, **kwargs):
+        if message.type == messages.Errors.TIMEOUT:
+            self._make_connection()
+            self.parent.net_register(self.parent.info)
+
+    def net_ping(self, message, **kwargs):
+        self._make_connection()
+        self.parent.net_ping(message.sid)
+
+
+class Client(object):
+    def __init__(self, host, port=5000, n_adapter=net.network.selected_adapter,
+                 s_adapter=net.serialization.selected_adapter):
+        messages.message_factory.s_adapter = s_adapter
+        self.client = n_adapter.Client()
+        self.connection = None
+        self.handler = Handler()
+        self.handler.parent = proxy(self)
+        self.address = host, port
+        self.info = ('', '', 0)
+        self.sid = 0
+
+    def _make_connection(self):
+        if self.connection is None or not self.connection.connected:
+            self.connection = self.client.connect(*self.address)
+            self.connection.add_handler(self.handler)
+
+    def register(self, name, host, port):
+        self.info = name, host, port
+        self._make_connection()
+        self.connection.net_register(name, host, port)
+
+    def ping(self):
+        if self.sid == 0:
+            return False
+        self._make_connection()
+        self.connection.net_ping(self.sid)
+        return True

pygnetic/discovery/messages.py

 from .. import message
 
 message_factory = message.MessageFactory()
-get_servers = message_factory.register('get_servers')
+# server messages
 register = message_factory.register('register', (
     'name',
-    'address',
+    'host',
     'port'
 ))
-servers_list = chat_msg = message_factory.register('servers_list', (
+get_servers = message_factory.register('get_servers', (
+    'list_step'
+))
+next_servers = message_factory.register('next_servers', (
+    'current'
+))
+servers_list = message_factory.register('servers_list', (
     'servers'
 ))
+ping = message_factory.register('ping', (
+    'sid'
+))
+error = message_factory.register('error', (
+    'type'
+))
+ack = message_factory.register('ack', (
+    'value'
+))
+
+
+class Errors(object):
+    TIMEOUT = 1

pygnetic/discovery/server.py

 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
 
-import sys
 import logging
 from .. import __init__ as net
 import messages
 
 
 class Handler(net.Handler):
-    def net_register(self, message, channel):
-        self.server.append()
+    def __init__(self):
+        self.curr = 0
+        self.list_step = 10
+        self.servers = None
 
-    def net_get_servers(self, message, channel):
-        pass
+    def net_register(self, message, **kwargs):
+        sid = self.server.id_cnt = self.server.id_cnt + 1
+        self.server.servers[sid] = [message, 0]
+        self.connection.net_register_ack(sid)
+        self.connection.disconnect()
 
+    def net_get_servers(self, message, **kwargs):
+        self.list_step = int(message.list_step)
+        self.servers = tuple(m for m, _ in self.server.servers.itervalues())
+        self.connection.net_servers_list(self.servers[:self.list_step])
+        self.curr = 1
 
-class Server(net.Server):
+    def net_next_servers(self, message, **kwargs):
+        if self.servers is not None:
+            s = self.curr * self.list_step
+            e = s + self.list_step
+            self.curr += 1
+            t = self.servers[s:e]
+            self.connection.net_servers_list(t)
+            if len(t) < self.list_step:
+                self.connection.disconnect()
+
+    def net_ping(self, message, **kwargs):
+        s = self.server.servers.get(message.sid)
+        if s is not None:
+            s[1] = 0
+        else:
+            self.connection.net_error(messages.Errors.TIMEOUT)
+        self.connection.disconnect()
+
+
+class DiscoveryServer(net.Server):
     handler = Handler
     message_factory = messages.message_factory
-    servers = list()
+    servers = {}
+    id_cnt = 0
+    ping_timeout = 30
 
+    def update(self, *args):
+        super(DiscoveryServer, self).update(*args)
+        self.servers = {k: [v[0], v[1] + 1]
+                        for k, v in self.servers.iteritems()
+                        if v[1] + 1 < self.ping_timeout}
 
-def run(address, port):
-    server = Server(address, port)
+
+def run(host, port):
+    server = DiscoveryServer(host, port)
     _logger.info('Listening')
     try:
         while True:
-            server.step(1000)
+            server.update(1000)
     except KeyboardInterrupt:
         pass
 
     import argparse
     parser = argparse.ArgumentParser(description='Server discovery service.')
     parser.add_argument('-a', '--address', type=str, default='')
-    parser.add_argument('-p', '--port', type=int, default=8000)
+    parser.add_argument('-p', '--port', type=int, default=5000)
     args = parser.parse_args()
     print 'Server started (Use Control-C to exit)'
     run(args.address, args.port)

pygnetic/message.py

 
 class MessageFactory(object):
     """Class allowing to register new message types and pack/unpack them."""
-    def __init__(self):
-        self._message_names = {}  # mapping name -> message
-        self._message_types = WeakValueDictionary()  # mapping type_id -> message
-        self._message_params = WeakKeyDictionary()  # mapping message -> type_id, send par
+    def __init__(self, s_adater=serialization._selected_adapter):
+        self._message_names = {}  # name -> message
+        self._message_types = WeakValueDictionary()  # type_id -> message
+        self._message_params = WeakKeyDictionary()  # message -> type_id, send par
+        self.s_adapter = s_adater
         self._type_id_cnt = 0
         self._frozen = False
         self._hash = None
         """
         type_id = self._message_params[message.__class__][0]
         message = (type_id,) + message
-        data = serialization.pack(message)
+        data = self.s_adapter.pack(message)
         _logger.debug("Packing message (length: %d)", len(data))
         return data
 
 
         :param context: object which will be prepared
         """
-        context._unpacker = serialization.unpacker()
+        context._unpacker = self.s_adapter.unpacker()
 
     def _process_message(self, message):
         try:
         """
         _logger.debug("Unpacking message (length: %d)", len(data))
         try:
-            message = serialization.unpack(data)
+            message = self.s_adapter.unpack(data)
         except:
             _logger.error('Data corrupted')
             self._reset_unpacker()  # prevent from corrupting next data
                 ids = self._message_types.keys()
                 ids.sort()
                 l = list()
-                l.append(serialization._selected_adapter.__name__)
+                l.append(self.s_adapter.__name__)
                 for i in ids:
                     p = self._message_types[i]
                     l.append((i, p.__name__, p._fields))

pygnetic/network/__init__.py

 
 from .. import _utils
 
-_selected_adapter = None
+selected_adapter = None
 
 
 def select_adapter(names):
-    global _selected_adapter
-    _selected_adapter = _utils.select_adapter(__name__, names)
+    global selected_adapter
+    selected_adapter = _utils.find_adapter(__name__, names)
+
+
+def get_adapter(name):
+    return _utils.find_adapter(__name__, name)
 
 
 class Client(object):
         b = cls.__bases__
         if Client in b:  # creation by inheritance
             i = b.index(Client) + 1
-            cls.__bases__ = b[:i] + (_selected_adapter.Client,) + b[i:]
+            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)
+            return selected_adapter.Client(*args, **kwargs)
 
 
 class Server(object):
         b = cls.__bases__
         if Server in b:  # creation by inheritance
             i = b.index(Server) + 1
-            cls.__bases__ = b[:i] + (_selected_adapter.Server,) + b[i:]
+            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)
+            return selected_adapter.Server(*args, **kwargs)

pygnetic/serialization/__init__.py

 
 from .. import _utils
 
-_selected_adapter = None
+selected_adapter = None
 pack = unpack = unpacker = None
 
 
 def select_adapter(names):
-    global _selected_adapter, pack, unpack, unpacker
-    _selected_adapter = _utils.select_adapter(__name__, names)
-    if _selected_adapter is not None:
-        pack = _selected_adapter.pack
-        unpack = _selected_adapter.unpack
-        unpacker = _selected_adapter.unpacker
+    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)
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.