diff --git a/pyogp/lib/base/message/circuitdata.py b/pyogp/lib/base/message/circuitdata.py index 075416b..1a51685 100644 --- a/pyogp/lib/base/message/circuitdata.py +++ b/pyogp/lib/base/message/circuitdata.py @@ -23,12 +23,12 @@ class Circuit(object): self.allow_timeout = True self.last_packet_out_id = 0 #id of the packet we last sent self.last_packet_in_id = 0 #id of the packet we last received - #packets waiting to be acked, can be resent - self.unacked_packets = {} #map of packet_id to packet + + self.acks = [] #packets we need to ack + self.unacked_packets = {} #packets we want acked, can be resent self.unack_packet_count = 0 - self.unack_packet_bytes = 9 - #packets waiting to be acked, can't be resent - self.final_retry_packets = {} #map of packet_id to packet + self.unack_packet_bytes = 0 + self.final_retry_packets = {} #packets we want acked, can't be resent self.final_packet_count = 0 def next_packet_id(self): @@ -39,7 +39,15 @@ class Circuit(object): #go through the packets waiting to be acked, and set them as acked pass + def collect_ack(self, packet_id): + """ set a packet_id that this circuit needs to eventually ack + (need to send ack out)""" + self.acks.append(packet_id) + + def add_reliable_packet(self, sock, message_buffer, buffer_length, **kwds): + """ add a packet that we want to be acked + (want an incoming ack) """ packet = Packet(sock, message_buffer, buffer_length, kwds) self.unack_packet_count += 1 self.unack_packet_bytes += buffer_length @@ -61,11 +69,17 @@ class CircuitManager(object): #to a list pass + def get_circuit(self, host): + if host in self.circuit_map: + return self.circuit_map[host] + + return None def add_circuit(self, host, packet_in_id): circuit = Circuit(host, packet_in_id) self.circuit_map[host] = circuit + return circuit def remove_circuit_data(self, host): pass diff --git a/pyogp/lib/base/message/data_unpacker.py b/pyogp/lib/base/message/data_unpacker.py index b0f3fc0..6857db0 100644 --- a/pyogp/lib/base/message/data_unpacker.py +++ b/pyogp/lib/base/message/data_unpacker.py @@ -1,7 +1,7 @@ import struct from uuid import UUID -from pyogp.lib.base.message.message_types import MsgType +from pyogp.lib.base.message.message_types import MsgType, sizeof class DataUnpacker(object): def __init__(self): @@ -24,9 +24,12 @@ class DataUnpacker(object): self.unpacker[MsgType.MVT_LLUUID] = self.__unpack_uuid self.unpacker[MsgType.MVT_BOOL] = '>B' self.unpacker[MsgType.MVT_IP_ADDR] = self.__unpack_string - self.unpacker[MsgType.MVT_IP_PORT] = '>H' + self.unpacker[MsgType.MVT_IP_PORT] = '>H' - def unpack_data(self, data, data_type): + def unpack_data(self, data, data_type, start_index = 0): + if start_index != 0: + data = data[start_index:start_index+sizeof(data_type)] + if data_type in self.unpacker: unpack = self.unpacker[data_type] if callable(unpack): diff --git a/pyogp/lib/base/message/message_system.py b/pyogp/lib/base/message/message_system.py index 23087ce..b5d5a76 100644 --- a/pyogp/lib/base/message/message_system.py +++ b/pyogp/lib/base/message/message_system.py @@ -8,17 +8,24 @@ from pyogp.lib.base.message.message_template_builder import MessageTemplateBuild from pyogp.lib.base.message.message_template_reader import MessageTemplateReader from pyogp.lib.base.message.message_template_dict import TemplateDictionary from pyogp.lib.base.message.message_dict import MessageDictionary +from pyogp.lib.base.message.circuitdata import CircuitManager +from pyogp.lib.base.message.message_types import PackFlags, sizeof +from pyogp.lib.base.message.data_unpacker import DataUnpacker +from pyogp.lib.base.message.net import * class MessageSystem(object): - def __init__(self): + def __init__(self, port): #holds the details of the message, or how the messages should be sent, #built, and read - self.message_details = None - self.builder = None - self.reader = None - self.circuit_info = None - self.socket = None - + self.message_details = None + self.builder = None + self.reader = None + self.circuit_manager = CircuitManager() + self.port = port + self.socket = None + #the ID of the packet we most recently received + self.receive_packet_id = -1 + self.llsd_builder = LLSDMessageBuilder() #self.llsd_reader = LLSDMessageReader() @@ -26,6 +33,10 @@ class MessageSystem(object): self.template_builder = MessageTemplateBuilder(template_dict) self.template_reader = MessageTemplateReader(template_dict) + self.socket = start_udp_connection(self.port) + + self.unpacker = DataUnpacker() + def load_template(self, template_file, details_file): #use the parser to load the message_template.msg message templates parser = MessageTemplateParser(msg_tmpl) @@ -33,10 +44,100 @@ class MessageSystem(object): return MessageDictionary(details_file), TemplateDictionary(template_list) + def find_circuit(self, host): + circuit = self.circuit_manager.get_circuit(host) + if circuit == None: + #there is a case where we want to return None, + #when the last packet was protected + circuit = self.circuit_manager.add_circuit(host, self.receive_packet_id) + + return circuit + def receive_check(self): #determine if we have any messages that can be received through UDP #also, check and decode the message we have received - pass + + #just sets it to the last reader we used + self.reader = self.template_reader + valid_packet = False + acks = 0 + recv_reliable = False + + while True: + recv_reliable = False + msg_buf, msg_size = receive_packet(self.socket) + + #we have a message + if msg_size > 0: + #determine packet flags + flag = ord(msg_buf[0]) + self.receive_packet_id = \ + self.unpacker.unpack_data(msg_buf[1:1+sizeof(MsgType.U32)], MsgType.U32) + + #determine sender + host = get_sender() + circuit = self.find_circuit(host) + + #ACK_FLAG - means the incoming packet is acking some old packets of ours + if flag & PackFlags.LL_ACK_FLAG: + #apparently, the number of acks is stored at the end + #msg_size -= 1 + #acks += msg_buf[msg_size] + #2 == packet ID size, 6 = min packet size + #msg_size -= acks * 2 + 6 + + #looop + #read the packet ID of the packets that the incoming packet is acking + #tell the circuit that the packet with ID has been acked + #end loop + #if the circuit has no unacked packets, remove it from unacked circuits + pass + + #RELIABLE - means the message wants to be acked by us + if flag & PackFlags.LL_RELIABLE_FLAG: + recv_reliable = True + + #RESENT - packet that wasn't previously acked was resent + if flag & PackFlags.LL_RESENT_FLAG: + #check if its a duplicate and the sender messed up somewhere + #case - ack we sent wasn't received by the sender + pass + + valid_packet = self.template_reader.validate_message(msg_buf, msg_size) + + #make sure packet validated correctly + if valid_packet == True: + #Case - UseCircuitCode - only packet allowed to be valid on an unprotected circuit + if circuit == None: + valid_packet = False + continue + #Case - trusted packets can only come in over trusted circuits + elif circuit.is_trusted() and \ + self.template_reader.is_trusted() == False: + valid_packet = False + continue + #Case - make sure its not a banned packet + #... + + valid_packet = self.template_reader.read_message(msg_buf) + + #make sure packet was read correctly (still valid) + if valid_packet == True: + if recv_reliable == True: + circuit.collect_ack(self.receive_packet_id) + + #we are attempting to get a single packet, so break once we get it + #or we have no more messages to read + if valid_packet == True and msg_size > 0: + break + + #now determine if the packet we got was valid (and therefore is stored + #in the reader) + if valid_packet == False: + self.template_reader.clear_message() + + return valid_packet + def send_reliable(self, host): """ Wants to be acked """ diff --git a/pyogp/lib/base/message/net.py b/pyogp/lib/base/message/net.py index 7dd5e9b..4f92431 100644 --- a/pyogp/lib/base/message/net.py +++ b/pyogp/lib/base/message/net.py @@ -1,4 +1,5 @@ import socket +#from pyogp.lib.base.message.zerocode import * #maybe put this isnt' a class @@ -8,7 +9,9 @@ def send_packet(socket, send_buffer, size, ip_addr, port): #returns message and size, or None if error def receive_packet(socket): - pass + buf = 10000 + data, addr = socket.recvfrom(buf) + return data, len(data) def start_udp_connection(port): """ Starts a udp connection, returning socket and port. """ diff --git a/pyogp/lib/base/tests/test_message_system.py b/pyogp/lib/base/tests/test_message_system.py index 6569892..7d73bc2 100644 --- a/pyogp/lib/base/tests/test_message_system.py +++ b/pyogp/lib/base/tests/test_message_system.py @@ -11,7 +11,7 @@ class TestMessageSystem(unittest.TestCase): pass def setUp(self): - self.message_system = MessageSystem() + self.message_system = MessageSystem(80) def test_init(self): assert self.message_system.message_dict.get_message_flavor('UseCircuitCode') \