Commits

Szymon Wróblewski committed 2ad4f74

expanded Peer and Host, reverted structtype to previous version

  • Participants
  • Parent commits b4dea11

Comments (0)

Files changed (4)

File ppenet/host.py

         # reuse slot
         self.peers[i] = peer = Peer(self, address, i, channel_count, data)
         return peer
+
+    def broadcast(self, channel_id, packet):
+        for peer in self.peers:
+            if peer.state == State.CONNECTED:
+                peer.send(channel_id, packet)

File ppenet/peer.py

 import random
 import protocol
-from structtype import structtype
 
 DEFAULT_ROUND_TRIP_TIME = 500
 DEFAULT_PACKET_THROTTLE = 32
     ZOMBIE) = range(10)
 
 
+class PacketFlag:
+    RELIABLE = 1 << 0
+    UNSEQUENCED = 1 << 1
+    UNRELIABLE_FRAGMENT = 1 << 3
+
+
 class Channel(object):
     def __init__(self):
-        self.outgoing_reliable_sequence_number = 0
-        self.outgoing_unreliable_sequence_number = 0
+        self.outgoing_reliable_seq_num = 0
+        self.outgoing_unreliable_seq_num = 0
         self.used_reliable_windows = 0
-        self.reliable_windows = []
-        self.incoming_reliable_sequence_number = 0
-        self.incoming_unreliable_sequence_number = 0
+        self.reliable_windows = [0] * RELIABLE_WINDOWS
+        self.incoming_reliable_seq_num = 0
+        self.incoming_unreliable_seq_num = 0
         self.incoming_reliable_commands = []
         self.incoming_unreliable_commands = []
 
 
-Acknowledgement = structtype('Acknowledgement', (
-    'acknowledgement_list',
-    'sent_time',
-    'command',
-), default=0, field_defaults={'acknowledgement_list': []})
+class Acknowledgement(object):
+    def __init__(self):
+        self.ack_list = []
+        self.sent_lime = 0
+        self.command = 0
 
-OutgoingCommand = structtype('OutgoingCommand', (
-    'outgoing_command_list',
-    'reliable_sequence_number',
-    'unreliable_sequence_number',
-    'sent_time',
-    'round_trip_timeout',
-    'round_trip_timeout_limit',
-    'fragment_offset',
-    'fragment_length',
-    'send_attempts',
-    'command',
-    'packet',
-), default=0, field_defaults={'outgoing_command_list': []})
 
-IncomingCommand = structtype('IncomingCommand', (
-    'incoming_command_list',
-    'reliable_sequence_number',
-    'unreliable_sequence_number',
-    'command',
-    'fragment_count',
-    'fragments_remaining',
-    'fragments',
-    'packet',
-), default=0, field_defaults={'incoming_command_list': []})
+class OutgoingCommand(object):
+    def __init__(self):
+        self.outgoing_command_list = []
+        self.reliable_seq_num = 0
+        self.unreliable_seq_num = 0
+        self.sent_time = 0
+        self.round_trip_timeout = 0
+        self.round_trip_timeout_limit = 0
+        self.fragment_offset = 0
+        self.fragment_length = 0
+        self.send_attempts = 0
+        self.command = 0
+        self.packet = 0
+
+
+class IncomingCommand(object):
+    def __init__(self):
+        self.incoming_command_list = []
+        self.reliable_seq_num = 0
+        self.unreliable_seq_num = 0
+        self.command = 0
+        self.fragment_count = 0
+        self.fragments_remaining = 0
+        self.fragments = 0
+        self.packet = 0
 
 
 class Peer(object):
         self.mtu = host.mtu
         self.window_size = protocol.MAXIMUM_WINDOW_SIZE
         self.reliable_data_in_transit = 0
-        self.outgoing_reliable_sequence_number = 0
+        self.outgoing_reliable_seq_num = 0
         self.acknowledgements = []
         self.sent_reliable_commands = []
         self.sent_unreliable_commands = []
             self.packet_throttle_acceleration,
             self.packet_throttle_deceleration,
             self.connect_id,
-            self.data)
+            self.data
+        )
+        self._queue_outgoing_command(connect)
 
-    def _queue_outgoing_command(self, command):
-        #try:
-        #    self.channels[]
-        pass
+    def _get_channel(self, channel_id, by_user=0):
+        try:
+            channel = self.channels[channel_id]
+        except KeyError:
+            if 0 <= channel_id <= protocol.MAXIMUM_CHANNEL_COUNT - by_user:
+                self.channels[channel_id] = channel = Channel()
+            else:
+                raise ValueError('Wrong channel_id: %d' % channel_id)
+        return channel
 
+    def _queue_outgoing_command(self, command, packet=None, offset=0, length=0):
+        outgoing_command = OutgoingCommand()
+        outgoing_command.command = command
+        outgoing_command.fragment_offset = offset
+        outgoing_command.fragment_length = length
+        outgoing_command.packet = packet
+
+        self._setup_outgoing_command(outgoing_command)
+        return outgoing_command
+
+    def _setup_outgoing_command(self, outgoing_command):
+        command = outgoing_command.command
+        channel = self._get_channel(command.channel_id)
+
+        self.outgoing_data_total += command.size + outgoing_command.fragment_length
+        if command.channel_id == 0xff:
+            self.outgoing_reliable_seq_num += 1
+            outgoing_command.reliable_seq_num = self.outgoing_reliable_seq_num
+            outgoing_command.unreliable_seq_num = 0
+        elif command.command & protocol.Flag.COMMAND_ACKNOWLEDGE:
+            channel.outgoing_reliable_seq_num += 1
+            channel.outgoing_unreliable_seq_num = 0
+            outgoing_command.reliable_seq_num = channel.outgoing_reliable_seq_num
+            outgoing_command.unreliable_seq_num = 0
+        elif command.command & protocol.Flag.COMMAND_UNSEQUENCED:
+            self.outgoing_unsequenced_group += 1
+            outgoing_command.reliable_seq_num = 0
+            outgoing_command.unreliable_seq_num = 0
+        else:
+            if outgoing_command.fragment_offset == 0:
+                channel.outgoing_unreliable_seq_num += 1
+            outgoing_command.reliable_seq_num = channel.outgoing_reliable_seq_num
+            outgoing_command.unreliable_seq_num = channel.outgoing_unreliable_seq_num
+        command.reliable_seq_num = outgoing_command.reliable_seq_num
+
+        command_type = command.command & protocol.COMMAND_MASK
+        if command_type == protocol.Command.SEND_UNRELIABLE:
+            command.unreliable_seq_num = outgoing_command.unreliable_seq_num
+        elif command_type == protocol.Command.SEND_UNSEQUENCED:
+            command.unsequenced_group = self.outgoing_unsequenced_group
+
+        if command.command & protocol.Flag.COMMAND_ACKNOWLEDGE:
+            self.outgoing_reliable_commands.append(outgoing_command)
+        else:
+            self.outgoing_unreliable_commands.append(outgoing_command)
+
+    def send(self, channel_id, data, flags=0):
+        channel = self._get_channel(channel_id)
+        if self.state != State.CONNECTED:
+            raise IOError('Not connected')
+        data_len = len(data)
+        if data_len > protocol.MAXIMUM_PACKET_SIZE:
+            raise ValueError('Data too large')
+        frag_len = self.mtu - protocol.SendFragment.size
+        if self.host.checksum is not None:
+            frag_len -= 4  # 32bit unsigned int
+
+        # fragmenting
+        if data_len > frag_len:
+            fragment_cnt = (data_len + frag_len - 1) / frag_len
+            if fragment_cnt > protocol.MAXIMUM_FRAGMENT_COUNT:
+                raise ValueError('Data too large')
+            outgoing_cmd = OutgoingCommand()
+            if (flags & PacketFlag.RELIABLE | PacketFlag.UNRELIABLE_FRAGMENT) \
+                    == PacketFlag.UNRELIABLE_FRAGMENT and \
+                    channel.outgoing_unreliable_seq_num < 0xffff:
+                cmd_type = protocol.Command.SEND_UNRELIABLE_FRAGMENT
+                start_seq_num = channel.outgoing_unreliable_seq_num + 1
+            else:
+                cmd_type = protocol.Command.SEND_FRAGMENT | protocol.Flag.COMMAND_ACKNOWLEDGE
+                start_seq_num = channel.outgoing_reliable_seq_num + 1
+
+            fragments = []
+            for frag_num, frag_offset in enumerate(xrange(0, data_len, frag_len)):
+                if data_len - frag_offset < frag_len:
+                    # calculate size of last fragment
+                    frag_len = data_len - frag_offset
+
+                fragment = OutgoingCommand()
+                fragment.fragment_offset = frag_offset
+                fragment.fragment_length = frag_len
+
+

File ppenet/protocol.py

 MAXIMUM_PACKET_SIZE = 1024 * 1024 * 1024
 MAXIMUM_FRAGMENT_COUNT = 1024 * 1024
 
+COMMAND_MASK = 0x0F
+
 
 class Command:
     (NONE,
 CommandHeader = structtype('CommandHeader', (
     'command',
     'channel_id',
-    'reliable_sequence_number',
+    'reliable_seq_num',
 ), '!BBH', default=0)
 
 Acknowledge = structtype('Acknowledge', CommandHeader.field_names + (
-    'received_reliable_sequence_number',
+    'received_reliable_seq_num',
     'received_sent_time'
-), CommandHeader.format + 'HH', default=0)
+), CommandHeader.format + 'HH', default=0, field_defaults={
+    'command': Command.ACKNOWLEDGE
+})
 
 Connect = structtype('Connect', CommandHeader.field_names + (
     'outgoing_peer_id',
     'packet_throttle_deceleration',
     'connect_id',
     'data',
-), CommandHeader.format + 'HBB10I', default=0)
+), CommandHeader.format + 'HBB10I', default=0, field_defaults={
+    'command': Command.CONNECT
+})
 
 VerifyConnect = structtype('VerifyConnect', CommandHeader.field_names + (
     'outgoing_peer_id',
     'packet_throttle_acceleration',
     'packet_throttle_deceleration',
     'connect_id',
-), CommandHeader.format + 'HBB9I', default=0)
+), CommandHeader.format + 'HBB9I', default=0, field_defaults={
+    'command': Command.VERIFY_CONNECT
+})
 
 BandwidthLimit = structtype('BandwidthLimit', CommandHeader.field_names + (
     'incoming_bandwidth',
     'outgoing_bandwidth',
-), CommandHeader.format + 'II', default=0)
+), CommandHeader.format + 'II', default=0, field_defaults={
+    'command': Command.BANDWIDTH_LIMIT
+})
 
 ThrottleConfigure = structtype('ThrottleConfigure', CommandHeader.field_names + (
     'packet_throttle_interval',
     'packet_throttle_acceleration',
     'packet_throttle_deceleration',
-), CommandHeader.format + '3I', default=0)
+), CommandHeader.format + '3I', default=0, field_defaults={
+    'command': Command.THROTTLE_CONFIGURE
+})
 
 Disconnect = structtype('Disconnect', CommandHeader.field_names + (
     'data',
-), CommandHeader.format + 'I', default=0)
+), CommandHeader.format + 'I', default=0, field_defaults={
+    'command': Command.DISCONNECT
+})
 
 Ping = structtype('Ping', CommandHeader.field_names,
-                  CommandHeader.format, default=0)
+                  CommandHeader.format, default=0,
+                  field_defaults={'command': Command.PING})
 
 SendReliable = structtype('SendReliable', CommandHeader.field_names + (
     'data_length',
-), CommandHeader.format + 'H', default=0)
+), CommandHeader.format + 'H', default=0, field_defaults={
+    'command': Command.SEND_RELIABLE
+})
 
 SendUnreliable = structtype('SendUnreliable', CommandHeader.field_names + (
-    'unreliable_sequence_number',
+    'unreliable_seq_num',
     'data_length',
-), CommandHeader.format + 'HH', default=0)
+), CommandHeader.format + 'HH', default=0, field_defaults={
+    'command': Command.SEND_UNRELIABLE
+})
 
 SendUnsequenced = structtype('SendUnsequenced', CommandHeader.field_names + (
     'unsequenced_group',
     'data_length',
-), CommandHeader.format + 'HH', default=0)
+), CommandHeader.format + 'HH', default=0, field_defaults={
+    'command': Command.SEND_UNSEQUENCED
+})
 
 SendFragment = structtype('SendFragment', CommandHeader.field_names + (
-    'start_sequence_number',
+    'start_seq_num',
     'data_length',
     'fragment_count',
     'fragment_number',
     'total_length',
     'fragment_offset',
-), CommandHeader.format + 'HH4I', default=0)
+), CommandHeader.format + 'HH4I', default=0, field_defaults={
+    'command': Command.SEND_FRAGMENT
+})

File ppenet/structtype.py

 from keyword import iskeyword
 
 
-def structtype(typename, field_names, format=None, verbose=False, **default_kwds):
-    '''Returns a new class with named fields and abilities of struct.Struct.
+def structtype(typename, field_names, format, verbose=False, **default_kwds):
+    '''Returns a new class with named fields.
 
     @keyword field_defaults: A mapping from (a subset of) field names to
         default values.
         init_defaults = tuple(field_defaults[f] for f in default_fields)
     if default_kwds:
         raise ValueError('Invalid keyword arguments: %s' % default_kwds)
-    # Check format parameter
-    if format:
-        try:
-            struct.pack(format, *(init_defaults if init_defaults\
-                                  else (0,) * len(field_names)))
-        except:
-            raise ValueError('Invalid format arguments: %s' % format)
-
     # Create and fill-in the class template
     numfields = len(field_names)
     argtxt = ', '.join(field_names)
     inittxt = '; '.join('self.%s=%s' % (f, f) for f in field_names)
     itertxt = '; '.join('yield self.%s' % f for f in field_names)
     eqtxt = ' and '.join('self.%s==other.%s' % (f, f) for f in field_names)
-    structtxt = '''
-    __struct = struct.Struct('%(format)s')
-    format = __struct.format
-    size = __struct.size
-
-    def pack(self):
-        return self.__struct.pack(%(tupletxt)s)
-
-    def pack_into(self, buffer, offset=0):
-        return self.__struct.pack_into(buffer, offset, %(tupletxt)s)
-
-    def unpack(self, string):
-        %(tupletxt)s = self.__struct.unpack(string)
-
-    def unpack_from(self, buffer, offset=0):
-        %(tupletxt)s = self.__struct.unpack_from(buffer, offset)
-    ''' % {'format': format, 'tupletxt': tupletxt[1:-1]} if format else ''
     template = dedent('''
         class %(typename)s(object):
             '%(typename)s(%(argtxt)s)'
 
-            __slots__  = %(field_names)r
-            field_names = __slots__
+            __slots__  = field_names = %(field_names)r
+            __struct = struct.Struct('%(format)s')
+            format = __struct.format
+            size = __struct.size
 
             def __init__(self, %(argtxt)s):
                 %(inittxt)s
             def __setstate__(self, state):
                 %(tupletxt)s = state
 
-            %(structtxt)s
+            def pack(self):
+                return self.__struct.pack(*%(tupletxt)s)
+
+            def pack_into(self, buffer, offset=0):
+                return self.__struct.pack_into(buffer, offset, *%(tupletxt)s)
+
+            def unpack(self, string):
+                %(tupletxt)s =self.__struct.unpack(string)
+
+            def unpack_from(self, buffer, offset=0):
+                %(tupletxt)s = self.__struct.unpack_from(buffer, offset)
     ''') % locals()
     # Execute the template string in a temporary namespace
     namespace = {'struct': struct}