summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristoph Egger <christoph@debian.org>2017-05-28 14:30:29 +0200
committerChristoph Egger <christoph@debian.org>2017-05-28 14:30:29 +0200
commitfc98900adb70b6316f991fe16726dc24b568cb52 (patch)
treeaf00c153db0f97c5280369fdb5f23d4636f673dc
parentc83668fff106e313beb66b8c09bdce1e9844fd3c (diff)
parent9f30a9497bb1e812acfdf1c98e3b5cf35ddd8e39 (diff)
Updated version 0.0~vcs.2017.05.26.git from '0.0_vcs.2017.05.26.git'
with Debian dir e44e7e9b159453b5fa19fb0c2508a7173ffe82a3
-rw-r--r--.gitignore95
-rw-r--r--README.md (renamed from README)37
-rw-r--r--enet.pyx64
-rw-r--r--setup.py18
-rw-r--r--test_client.py59
-rw-r--r--test_enet.py23
-rw-r--r--test_server.py37
7 files changed, 299 insertions, 34 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..01bb832
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,95 @@
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+env/
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+
+# PyInstaller
+# Usually these files are written by a python script from a template
+# before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*,cover
+.hypothesis/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# pyenv
+.python-version
+
+# celery beat schedule file
+celerybeat-schedule
+
+# dotenv
+.env
+
+# virtualenv
+.venv/
+venv/
+ENV/
+
+# Spyder project settings
+.spyderproject
+
+# Rope project settings
+.ropeproject
+
+# Pyenet specific files
+enet.c
+enet/
diff --git a/README b/README.md
index b832bb6..509a805 100644
--- a/README
+++ b/README.md
@@ -1,26 +1,21 @@
-pyenet 1.3
-
-About
-=====================
+#pyenet
pyenet is a python wrapper for the ENet library by Lee Salzman,
http://enet.bespin.org
It was originally written by Scott Robinson <scott@tranzoa.com> and is
-currently maintained by Andrew Resch <andrewresch@gmail.com> at
- http://code.google.com/p/pyenet
+currently maintained by Andrew Resch <andrewresch@gmail.com>
+##License
pyenet is licensed under the BSD license, see LICENSE for details.
enet is licensed under the MIT license, see http://enet.bespin.org/License.html
-Dependencies
-=====================
+##Dependencies
Building pyenet requires all the same dependencies as enet plus Cython and,
obviously, Python.
-Installation
-=====================
+##Installation
The first step is to download the enet sources from http://enet.bespin.org and
extract it to the enet/ directory in pyenet's source directory. You could also
@@ -29,30 +24,30 @@ check out the enet source code from their CVS repository.
This version of pyenet requires enet 1.3.
Next step is to run the setup.py build:
-
- $ python setup.py build
-
+```
+$ python setup.py build
+```
Once that is complete, install the new pyenet module:
+```
+# python setup.py install
+```
- # python setup.py install
-
-Usage
-=====================
+##Usage
Once you have installed pyenet, you only need to import the enet module to
start using enet in your project.
Example server:
-
+```
>>> import enet
>>> host = enet.Host(enet.Address("localhost", 33333), 1, 0, 0)
>>> event = host.service(0)
-
+```
Example client:
-
+```
>>> import enet
>>> host = enet.Host(None, 1, 0, 0)
>>> peer = host.connect(enet.Address("localhost", 33333), 1)
-
+```
More information on usage can be obtained from:
http://enet.bespin.org/Tutorial.html
diff --git a/enet.pyx b/enet.pyx
index 0dda8ff..52e81f7 100644
--- a/enet.pyx
+++ b/enet.pyx
@@ -1,6 +1,7 @@
import atexit
from cpython cimport bool
+from libc.stdint cimport uintptr_t
cdef extern from "enet/types.h":
ctypedef unsigned char enet_uint8
@@ -22,6 +23,14 @@ cdef extern from "enet/enet.h":
ctypedef int ENetSocket
+ ctypedef struct ENetBuffer:
+ void *data
+ size_t dataLength
+
+ ctypedef struct ENetEvent
+
+ ctypedef int (__cdecl *ENetInterceptCallback) (ENetHost *host, ENetEvent *event) except -1 # __cdecl is standard on unix and overwriten for win32
+
ctypedef struct ENetAddress:
enet_uint32 host
enet_uint16 port
@@ -107,10 +116,14 @@ cdef extern from "enet/enet.h":
ENetPeer *peers
size_t peerCount
size_t channelLimit
+ enet_uint8 *receivedData
+ size_t receivedDataLength
+ ENetAddress receivedAddress
enet_uint32 totalSentData
enet_uint32 totalSentPackets
enet_uint32 totalReceivedData
enet_uint32 totalReceivedPackets
+ ENetInterceptCallback intercept
ctypedef enum ENetEventType:
ENET_EVENT_TYPE_NONE = 0
@@ -161,6 +174,9 @@ cdef extern from "enet/enet.h":
void enet_peer_disconnect_now(ENetPeer *peer, enet_uint32 data)
void enet_peer_disconnect_later(ENetPeer *peer, enet_uint32 data)
+ # Socket functions
+ int enet_socket_send(ENetSocket socket, ENetAddress *address, ENetBuffer *buffer, size_t size)
+
cdef enum:
MAXHOSTNAME = 257
@@ -185,6 +201,8 @@ PEER_STATE_DISCONNECTING = ENET_PEER_STATE_DISCONNECTING
PEER_STATE_ACKNOWLEDGING_DISCONNECT = ENET_PEER_STATE_ACKNOWLEDGING_DISCONNECT
PEER_STATE_ZOMBIE = ENET_PEER_STATE_ZOMBIE
+cdef class Address
+
cdef class Socket:
"""
Socket (int socket)
@@ -198,6 +216,17 @@ cdef class Socket:
cdef ENetSocket _enet_socket
+ def send(self, Address address, data):
+ cdef ENetBuffer buffer
+ data = data if isinstance(data, bytes) else data.encode('cp437')
+
+ buffer.data = <char*>data
+ buffer.dataLength = len(data)
+
+ cdef int result = enet_socket_send(self._enet_socket,
+ &address._enet_address, &buffer, 1)
+ return result
+
def fileno(self):
return self._enet_socket
@@ -397,6 +426,9 @@ cdef class Peer:
return self.address != obj.address
raise NotImplementedError
+ def __hash__(self):
+ return <uintptr_t>self._enet_peer
+
def send(self, channelID, Packet packet):
"""
send (int channelID, Packet packet)
@@ -798,6 +830,9 @@ cdef class Event:
(<Packet> self._packet)._enet_packet = self._enet_event.packet
return self._packet
+from weakref import WeakValueDictionary
+cdef host_static_instances = WeakValueDictionary()
+
cdef class Host:
"""
Host (Address address, int peerCount, int channelLimit,
@@ -819,6 +854,8 @@ cdef class Host:
cdef ENetHost *_enet_host
cdef bool dealloc
+ cdef object _interceptCallback
+ cdef object __weakref__
def __init__ (self, Address address=None, peerCount=0, channelLimit=0,
incomingBandwidth=0, outgoingBandwidth=0):
@@ -832,6 +869,12 @@ cdef class Host:
raise MemoryError("Unable to create host structure!")
self.dealloc = True
+ global host_static_instances
+ host_static_instances[<uintptr_t>self._enet_host] = self
+
+ def __hash__(self):
+ return <uintptr_t>self._enet_host
+
def __cinit__(self):
self.dealloc = False
self._enet_host = NULL
@@ -1000,6 +1043,27 @@ cdef class Host:
def __set__(self, value):
self._enet_host.totalReceivedPackets = value
+ property intercept:
+ def __get__(self):
+ return self._interceptCallback
+
+ def __set__(self, value):
+ if value is None:
+ self._enet_host.intercept = NULL
+ else:
+ self._enet_host.intercept = intercept_callback
+ self._interceptCallback = value
+
+
+cdef int __cdecl intercept_callback(ENetHost *host, ENetEvent *event) except -1:
+ cdef Address address = Address(None, 0)
+ address._enet_address = host.receivedAddress
+ cdef object ret = None
+
+ if <uintptr_t>host in host_static_instances:
+ ret = host_static_instances[<uintptr_t>host].intercept(address, (<char*>host.receivedData)[:host.receivedDataLength])
+ return int(bool(ret))
+
def _enet_atexit():
enet_deinitialize()
diff --git a/setup.py b/setup.py
index 19ae57c..95d55e8 100644
--- a/setup.py
+++ b/setup.py
@@ -7,22 +7,22 @@ import sys
source_files = ["enet.pyx"]
-# _enet_files = glob.glob("enet/*.c")
+_enet_files = glob.glob("enet/*.c")
-# if not _enet_files:
-# print("You need to download and extract the enet 1.3 source to enet/")
-# print("Download the source from: http://enet.bespin.org/SourceDistro.html")
-# print("See the README for more instructions")
-# sys.exit(1)
-#
-# source_files.extend(_enet_files)
+if not _enet_files:
+ print("You need to download and extract the enet 1.3 source to enet/")
+ print("Download the source from: http://enet.bespin.org/SourceDistro.html")
+ print("See the README for more instructions")
+ sys.exit(1)
+
+source_files.extend(_enet_files)
define_macros = [('HAS_POLL', None),
('HAS_FCNTL', None),
('HAS_MSGHDR_FLAGS', None),
('HAS_SOCKLEN_T', None) ]
-libraries = ['enet']
+libraries = []
if sys.platform == 'win32':
define_macros.extend([('WIN32', None)])
diff --git a/test_client.py b/test_client.py
index 7b29101..a75bd36 100644
--- a/test_client.py
+++ b/test_client.py
@@ -1,5 +1,11 @@
import enet
-import os
+import random
+import sys
+
+try:
+ random.seed(sys.argv[1])
+except IndexError:
+ pass
SHUTDOWN_MSG = b"SHUTDOWN"
MSG_NUMBER = 10
@@ -20,7 +26,7 @@ while run:
elif event.type == enet.EVENT_TYPE_RECEIVE:
print("%s: IN: %r" % (event.peer.address, event.packet.data))
continue
- msg = os.urandom(40)
+ msg = bytes(bytearray([random.randint(0,255) for i in range(40)]))
packet = enet.Packet(msg)
peer.send(0, packet)
@@ -28,7 +34,54 @@ while run:
if counter >= MSG_NUMBER:
msg = SHUTDOWN_MSG
peer.send(0, enet.Packet(msg))
- host.service(0)
+ host.service(100)
peer.disconnect()
print("%s: OUT: %r" % (peer.address, msg))
+
+
+# Part of the test to do with intercept callback and socket.send
+peer = host.connect(enet.Address(b"localhost", 54301), 1)
+shutdown_scheduled = False
+run = True
+
+def receive_callback(address, data):
+ global shutdown_scheduled
+
+ if shutdown_scheduled:
+ return
+
+ if data == b"\xff\xff\xff\xffstatusResponse\n":
+ # if the test gets to this point, it means it has passed. Disconnect is a clean up
+ shutdown_scheduled = True
+ else:
+ # error messages are not propagating
+ # through cython
+ print("data != statusResponse. Instead of expected, got %r" % data)
+ assert(False)
+
+while run:
+ event = host.service(1000)
+ if event.type == enet.EVENT_TYPE_CONNECT:
+ print("%s: CONNECT" % event.peer.address)
+ msg = bytes(bytearray([random.randint(0,255) for i in range(40)]))
+ packet = enet.Packet(msg)
+ peer.send(0, packet)
+
+ host.intercept = receive_callback
+ elif event.type == enet.EVENT_TYPE_DISCONNECT:
+ print("%s: DISCONNECT" % event.peer.address)
+ run = False
+ continue
+ elif event.type == enet.EVENT_TYPE_RECEIVE:
+ print("%s: IN: %r" % (event.peer.address, event.packet.data))
+ continue
+
+ if shutdown_scheduled:
+ msg = SHUTDOWN_MSG
+ peer.send(0, enet.Packet(msg))
+ host.service(100)
+ peer.disconnect()
+ continue
+
+ host.socket.send(peer.address, b"\xff\xff\xff\xffgetstatus\x00")
diff --git a/test_enet.py b/test_enet.py
index b7a6820..b9adf0b 100644
--- a/test_enet.py
+++ b/test_enet.py
@@ -71,6 +71,29 @@ class TestHost(unittest.TestCase):
self.assertEquals(client_connected, True)
self.assertEquals(server_connected, True)
+ def test_socketsend(self):
+
+ self.send_done = False
+ socketsend_msg = b"\xff\xff\xff\xffgetstatus\x00"
+
+ def f(address, data):
+ if data != socketsend_msg:
+ # error messages are not propagating
+ # through cython
+ print("data != statusResponse")
+ assert(False)
+ self.send_done = True
+
+ while not self.send_done:
+
+ self.client.service(0)
+ self.client.socket.send(self.server.address, socketsend_msg)
+
+ event = self.server.service(0)
+ if event.type == enet.EVENT_TYPE_CONNECT:
+ self.server.intercept = f
+
+
def test_broadcast(self):
broadcast_done = False
broadcast_msg = b"foo\0bar\n baz!"
diff --git a/test_server.py b/test_server.py
index f147553..fbeee2a 100644
--- a/test_server.py
+++ b/test_server.py
@@ -25,5 +25,40 @@ while run:
print("%s: Error sending echo packet!" % event.peer.address)
else:
print("%s: OUT: %r" % (event.peer.address, msg))
- if event.packet.data == "SHUTDOWN":
+ if event.packet.data == b"SHUTDOWN":
+ shutdown_recv = True
+
+# Part of the test to do with intercept callback and socket.send
+
+connect_count = 0
+run = True
+shutdown_recv = False
+
+def receive_callback(address, data):
+ if data and data == b"\xff\xff\xff\xffgetstatus\x00":
+ host.socket.send(address, b"\xff\xff\xff\xffstatusResponse\n")
+
+host.intercept = receive_callback
+
+while run:
+ # Wait 1 second for an event
+ event = host.service(1000)
+ if event.type == enet.EVENT_TYPE_CONNECT:
+ print("%s: CONNECT" % event.peer.address)
+ connect_count += 1
+ elif event.type == enet.EVENT_TYPE_DISCONNECT:
+ print("%s: DISCONNECT" % event.peer.address)
+ connect_count -= 1
+ if connect_count <= 0 and shutdown_recv:
+ run = False
+ elif event.type == enet.EVENT_TYPE_RECEIVE:
+ print("%s: IN: %r" % (event.peer.address, event.packet.data))
+
+ # This packet echo is used to mimick the usual use case when packets are going back&forth while the intercept callback is used
+ msg = event.packet.data
+ if event.peer.send(0, enet.Packet(msg)) < 0:
+ print("%s: Error sending echo packet!" % event.peer.address)
+ else:
+ print("%s: OUT: %r" % (event.peer.address, msg))
+ if event.packet.data == b"SHUTDOWN":
shutdown_recv = True