Example #1
0
	def __init__(self, sock, address):
		self.socket = sock
		self.address = address
		
		self.lastNakTime = -1
		self.lastSendTime = -1
		self.lastReceiveTime = time.time()
		self.lastInOrder = -1
		self.waitingPackets = {}
		self.messageQueue = []
		self.unackedPackets = []
		self.outgoingData = []
		
		self.firstUnacked = 0
		self.currentNum = 0
		self.lastNak = -1
		
		self.fragmentBuffer = ''
		
		self.mtu = 500
		
		self.proto = Protocol()
Example #2
0
class UDPConnection:
	# headerSize = 9 # just hardcoded
	maxPacketSize = 4096
	
	def __init__(self, sock, address):
		self.socket = sock
		self.address = address
		
		self.lastNakTime = -1
		self.lastSendTime = -1
		self.lastReceiveTime = time.time()
		self.lastInOrder = -1
		self.waitingPackets = {}
		self.messageQueue = []
		self.unackedPackets = []
		self.outgoingData = []
		
		self.firstUnacked = 0
		self.currentNum = 0
		self.lastNak = -1
		
		self.fragmentBuffer = ''
		
		self.mtu = 500
		
		self.proto = Protocol()
	
	def sendData(self, data):
		if data: self.outgoingData.append(data)
	
	def peek(self, ahead):
		if ahead < len(self.messageQueue):
			return self.messageQueue[ahead]
		else:
			return False
	
	def getData(self):
		if self.messageQueue:
			return self.messageQueue.pop(0)
		else:
			return False
	
	def update(self):
		#print 'update()'
		curTime = time.time()
		force = False
		
		if self.lastInOrder < 0 and self.lastSendTime < curTime-1 and self.unackedPackets:
			self.sendRawPacket(self.unackedPackets[0], 0)
			self.lastSendTime = curTime
			force = True
		
		if self.lastSendTime < curTime-5 and self.lastInOrder >= 0:
			force = True
		elif self.lastSendTime < curTime - float(200)/1000 and self.waitingPackets:
			force = True
		
		self.flush(force)
			
	
	def processRawPacket(self, packet):
		if len(packet) < 9: return
		
		print '<- %s' % packet
		self.lastReceiveTime = time.time()
		
		# int packetNum = *(int*)packet->data; # maybe write a python function to do it like this...
		# unpack.int(data[4:]) will take return an int from the beginning... 
		# unpack.short(data) same thing... possibly make an unpack or packet class...
		# make it continue where it left off :)
		packetNum = struct.unpack('<i', packet[:4])[0]
		ack = struct.unpack('<i', packet[4:8])[0]
		nak = struct.unpack('<B', packet[8])[0]
		
		self.ackPackets(ack)
		
		if nak > 0: # we have lost $nak packets
			nak_abs = nak + self.firstUnacked - 1
			if nak_abs > self.currentNum: # we got a nak for packets we never sent, give an error message
				pass
			elif nak_abs != self.lastNak or self.lastNakTime < self.lastReceiveTime - float(100)/1000:
				self.lastNak = nak_abs
				self.lastNakTime = self.lastReceiveTime
				for i in xrange(self.firstUnacked, nak_abs+1):
					self.sendRawPacket(self.unackedPackets[i-firstUnacked], i)
		
		# need to figure out what next parts actually do and implement them
		# up till waitingPackets.insert()
		if packetNum in self.waitingPackets or packetNum < self.lastInOrder:
			print packetNum, self.lastInOrder
			print 'duplicate packet'
			return # duplicate packet
		self.waitingPackets[packetNum] = packet[9:]
		# print binascii.hexlify(packet[9:])
		
		# process half-baked packets, possibly should be its own function.. though I suppose it'll be one of the top levels of processing
		while (self.lastInOrder+1 in self.waitingPackets):
			if self.fragmentBuffer:
				packet = self.fragmentBuffer + self.waitingPackets[self.lastInOrder+1]
			else:
				packet = self.waitingPackets[self.lastInOrder+1]
			
			self.lastInOrder += 1
			del self.waitingPackets[self.lastInOrder]
			
			while packet:
				msg_id = ord(packet[0])
				print 'Message ID: %s' % msg_id
				length = self.proto.getLength(msg_id)
				if length != 0:
					if length < 0:
						if len(packet) >= -length: # do we have enough data in the buffer to read the length of the message?
							if length == -1: # length is a uchar
								struct.unpack('<B', packet[1])
							elif length == -2: # length is short
								struct.unpack('<H', packet[1:3])
						else:
							self.fragmentBuffer = packet
							break
					
					# is the complete message in the buffer?
					if len(packet) >= length:
						# yes -> add to message queue and keep going
						self.messageQueue.append(packet[:length])
						packet = packet[length:]
					else:
						# no -> store fragment and break
						self.fragmentBuffer = packet
						break
				else:
					print 'Invalid message ID: %i' % msg_id
					packet = packet[1:] # bad message id - this could cause serious problems... possibly need to drop this packet completely...
	
	def flush(self, forced):
		curTime = time.time()
		
		outgoingLength = 0
		for item in self.outgoingData:
			outgoingLength += len(item)
		if forced or (self.outgoingData and (self.lastSendTime < (curTime - .200 + float(outgoingLength) / 10))):
			self.lastSendTime = time.time()
			
			# manually fragment packets to respect mtu, to fix a bug where players drop out of the game when someone gives a large order
			
			buffer = ''
			
			while self.outgoingData:
				packet = self.outgoingData[0]
				length = min(self.mtu, len(packet))
				buffer = packet[:length]
				if length == len(packet):
					self.outgoingData.pop(0)
				else:
					self.outgoingData[0] = packet[:length]
				self.sendRawPacket(buffer, self.currentNum)
				self.unackedPackets.append(buffer)
				self.currentNum += 1
			else:
				if forced:
					buffer = ''
					self.sendRawPacket(buffer, self.currentNum)
					self.unackedPackets.append(buffer)
					self.currentNum += 1

	def checkTimeout(self):
		timeout = 45 if (self.lastInOrder < 0) else 30
		return True if (self.lastReceiveTime+timeout) < time.time() else False
	
	def setMTU(self, mtu):
		if (mtu > 300) and (mtu < self.maxPacketSize):
			self.mtu = mtu
	
	def ackPackets(self, nextAck):
		while (nextAck >= self.firstUnacked) and (self.unackedPackets):
			self.unackedPackets.pop(0)
			self.firstUnacked += 1
	
	def sendRawPacket(self, data, packetNum):
		print '-> %s' % data
		#packetNum = self.currentNum
		#self.currentNum += 1
		packet = struct.pack('<ii', packetNum, self.lastInOrder)
		if self.waitingPackets and sorted(self.waitingPackets.keys())[-1] == self.lastInOrder+1:
			nak = (sorted(self.waitingPackets.keys())[0] - 1) - self.lastInOrder
			assert nak >= 0
			packet += struct.pack('<B', nak)
		else:
			packet += struct.pack('<B', 0)
		
		packet += data
		print str(packet)
		self.socket.sendto(packet, self.address)