Beispiel #1
0
	def test_get_update(self):
		""" Test raw execution performance of the 'get_update'"""
		self.connection = Connection(self.world, self.cache)
		archers = list()
		for i in range(50):
			archer = Archer(self.world, reactor=self.clock)
			archer.spawn(self.spawn_point)
			archer.want_move(directions['south'])
			archers.append(archer)
		self.pr.enable()
		for i in range(100):
			self.connection.get_update()
		self.pr.disable()
		print self.process_stats('test_get_update.txt')
Beispiel #2
0
class TestArcher(BaseTestCase):
	def setUp(self):
		super(TestArcher, self).setUp()
		path = os.path.dirname(os.path.os.path.realpath(__file__))
		path = os.path.join(path, 'assets/test1.tmx')
		self.world = World(path)
		self.spawn_point = self.world.get_spawn_points()[0]
		self.world_update_task = task.LoopingCall(self.world.step)
		self.world_update_task.clock = self.clock
		self.world_update_task.start(settings.TIME_STEP)
		self.archer = Archer(self.world, reactor=self.clock)
		self.archer.spawn(self.spawn_point)

	def tearDown(self):
		self.archer.destroy()
		self.world_update_task.stop()

	def count_archer_arrows(self, archer):
		arrows = self.world.get_objects_by_type('arrow')
		count = 0
		for arrow in arrows:
			if(arrow.owner == archer):
				count = count + 1
		return count

	def get_archer_arrows(self, archer):
		arrows = self.world.get_objects_by_type('arrow')
		archer_arrows = list()
		for arrow in arrows:
			if(arrow.owner == archer):
				archer_arrows.append(arrow)
		return archer_arrows

	def test_archer_spawned(self):
		self.assertEqual(self.spawn_point.x, self.archer.physics.position.x)
		self.assertEqual(self.spawn_point.y, self.archer.physics.position.y)

	def test_archer_moved(self):
		self.archer.want_move(directions['north'])
		self.advance_clock(1)
		self.assertLess(self.archer.physics.position.y, self.spawn_point.y)

	def test_archer_collides(self):
		self.archer.want_move(directions['east'])
		self.advance_clock(100)
		self.assertLess(self.archer.physics.position.x, 6.0)
		self.assertGreater(self.archer.physics.position.x, self.spawn_point.x)

	def test_archer_shoots(self):
		self.archer.want_attack(directions['south'])
		self.advance_clock(40)
		self.assertEqual(self.count_archer_arrows(self.archer), 1)
		self.advance_clock(1000)
		self.assertEqual(self.count_archer_arrows(self.archer), 0)

	def test_arrow_flies(self):
		self.archer.want_attack(directions['south'])
		self.advance_clock(40)
		arrow = self.get_archer_arrows(self.archer)[0]
		self.assertEqual(self.archer.physics.position.x, arrow.physics.position.x)
		archer_position_plus_2m = self.archer.physics.position + directions['south']*2
		self.assertGreater(arrow.physics.position.y, archer_position_plus_2m.y)

	def test_arrow_collides(self):
		self.archer.want_attack(directions['east'])
		self.advance_clock(40)
		arrow = self.get_archer_arrows(self.archer)[0]
		self.assertLess(arrow.physics.position.x, 6.0)
		self.assertGreater(arrow.physics.position.x, self.archer.physics.position.x)

	def test_archer_kills_archer(self):
		attacker_spawn = self.world.get_object_by_name('spawn2')
		defender_spawn = self.world.get_object_by_name('spawn3')
		attacker = Archer(self.world, name="attacker", reactor=self.clock)
		defender = Archer(self.world, name="defender", reactor=self.clock)
		attacker.spawn(attacker_spawn)
		defender.spawn(defender_spawn)

		attacker.want_attack(directions['south'])
		self.advance_clock(1)
		self.assertEqual(attacker.state, "shooting")
		self.assertEqual(defender.state, "standing")
		self.advance_clock(50)
		self.assertEqual(attacker.state, "standing")
		self.assertEqual(defender.state, "dying")
		self.advance_clock(250)
		self.assertEqual(defender.state, "dead")
Beispiel #3
0
class TestPlayer(BaseTestCase):
    def setUp(self):
        super(TestPlayer, self).setUp()
        path = os.path.dirname(os.path.os.path.realpath(__file__))
        path = os.path.join(path, 'assets/test1.tmx')
        self.world = World(path)
        self.spawn_point = self.world.get_spawn_points()[0]
        self.world_update_task = task.LoopingCall(self.world.processing_step)
        self.world_update_task.clock = self.clock
        self.world_update_task.start(1.0 / 30)
        self.player = Archer(self.world, reactor=self.clock)
        self.player.spawn(self.spawn_point)

    def tearDown(self):
        self.player.destroy()
        self.world_update_task.stop()

    def count_player_arrows(self, player):
        arrows = self.world.get_objects_by_type('arrow')
        count = 0
        for arrow in arrows:
            if (arrow.owner == player):
                count = count + 1
        return count

    def get_player_arrows(self, player):
        arrows = self.world.get_objects_by_type('arrow')
        player_arrows = list()
        for arrow in arrows:
            if (arrow.owner == player):
                player_arrows.append(arrow)
        return player_arrows

    def test_player_spawned(self):
        self.assertEqual(self.spawn_point.x, self.player.physics.position.x)
        self.assertEqual(self.spawn_point.y, self.player.physics.position.y)

    def test_player_moved(self):
        self.player.want_move(directions['north'])
        self.advance_clock(1)
        self.assertLess(self.player.physics.position.y, self.spawn_point.y)

    def test_player_collides(self):
        self.player.want_move(directions['east'])
        self.advance_clock(100)
        self.assertLess(self.player.physics.position.x, 6.0)
        self.assertGreater(self.player.physics.position.x, self.spawn_point.x)

    def test_player_shoots(self):
        self.player.want_attack(directions['south'])
        self.advance_clock(40)
        self.assertEqual(self.count_player_arrows(self.player), 1)
        self.advance_clock(1000)
        self.assertEqual(self.count_player_arrows(self.player), 0)

    def test_arrow_flies(self):
        self.player.want_attack(directions['south'])
        self.advance_clock(40)
        arrow = self.get_player_arrows(self.player)[0]
        self.assertEqual(self.player.physics.position.x,
                         arrow.physics.position.x)
        player_position_plus_2m = self.player.physics.position + directions[
            'south'] * 2
        self.assertGreater(arrow.physics.position.y, player_position_plus_2m.y)

    def test_arrow_collides(self):
        self.player.want_attack(directions['south'])
        self.advance_clock(40)
        arrow = self.get_player_arrows(self.player)[0]
        self.assertLess(arrow.physics.position.x, 6.0)
        self.assertGreater(arrow.physics.position.y,
                           self.player.physics.position.y)

    def test_player_kills_player(self):
        attacker_spawn = self.world.get_object_by_name('spawn2')
        defender_spawn = self.world.get_object_by_name('spawn3')
        attacker = Archer(self.world, name="attacker", reactor=self.clock)
        defender = Archer(self.world, name="defender", reactor=self.clock)
        attacker.spawn(attacker_spawn)
        defender.spawn(defender_spawn)

        attacker.want_attack(directions['south'])
        self.advance_clock(1)
        self.assertEqual(attacker.state, "shooting")
        self.assertEqual(defender.state, "standing")
        self.advance_clock(50)
        self.assertEqual(attacker.state, "standing")
        self.assertEqual(defender.state, "dying")
        self.advance_clock(250)
        self.assertEqual(defender.state, "dead")
Beispiel #4
0
class Connection(EventsMixins):
    def __str__(self):
        return self.meta['username']

    def __init__(self, world, cache):
        super(Connection, self).__init__()
        self.session_id = uuid.UUID(bytes=urandom(16))
        self.world = world
        self.cache = cache
        self.known = dict()
        self.last_world_index = 0
        self.last_frame_index = 0
        self.archer = Archer(self.world, player=self)

        self.meta = {
            "id": self.archer.id,
            "username": "******",
            "gender": "male",
            "slots": {},
            "kills": 0,
            "deaths": 0,
            "score": 0,
            "budget": settings.initial_budget,
            "attributes": self.archer.attributes
        }

        self.archer.interface = self

        logging.info("New connection %s" % self.session_id)

        self.world.on('destroy_object', self.on_destroy)
        self.world.on('step', self.on_step)
        self.on('useraction', self.on_user_action)
        self.on('disconnect', self.on_disconnect)
        self.on('usermsg', self.on_user_message)
        self.on('kill', self.on_kill)
        self.on('die', self.on_die)
        self.on('mob', self.on_mob)
        self.on('pickup', self.on_pickup)
        self.on('spawn', self.on_spawn)

    def on_spawn(self):
        self.meta['budget'] = settings.initial_budget
        self.trigger('meta', self.meta)

    def on_kill(self, prey):
        self.meta['kills'] = self.meta['kills'] + 1
        self.meta['score'] = self.meta['score'] + settings.score['kill']
        logging.info("%s fragged %s" %
                     (self.meta['username'], prey.meta['username']))
        self.trigger('meta', self.meta)

    def on_die(self, killer=None):
        self.meta['deaths'] = self.meta['deaths'] + 1
        logging.info("%s died by the hand of %s" %
                     (self.meta['username'], killer or "[unknown]"))
        self.trigger('meta', self.meta)

    def on_mob(self):
        self.meta['score'] = self.meta['score'] + settings.score['mob']
        self.trigger('meta', self.meta)

    def on_pickup(self, bonus):
        self.meta['budget'] = self.meta["budget"] + bonus.value
        self.trigger('meta', self.meta)

    def on_user_message(self, meta):
        changed = False
        if ('username' in meta):
            new_username = meta['username'][0:10]
            if (new_username != self.meta['username']):
                self.meta['username'] = new_username
                logging.info("%s is now known as %s" %
                             (self.session_id, self.meta['username']))
                changed = True

        if ('gender' in meta and meta['gender'] != self.meta['gender']):
            self.meta['gender'] = meta['gender']
            changed = True

        if ('slots' in meta and meta['slots'] != self.meta['slots']):
            if (verify_slots(meta['slots'], self.meta['budget'])):
                self.meta['slots'] = meta['slots']
                changed = True
            else:
                logging.info(
                    "%s provided incorrect meta msg (%s). Discarding" %
                    (self.meta['username'], meta['slots']))

        if (changed):
            self.archer.update_attributes()
            # self.trigger('meta', self.meta)

    def on_disconnect(self):
        self.world.off('destroy_object', self.on_destroy)
        self.world.off('step', self.on_step)
        self.off()
        self.archer.destroy()

    def on_user_action(self, message):
        spawn_points = self.world.get_spawn_points()
        shuffle(spawn_points)
        logging.info(message)
        if (message['action'] == 'spawn'):
            if (self.archer.can_spawn()):
                if (verify_slots(self.meta['slots'], self.meta['budget'])):
                    self.archer.spawn(spawn_points[0])
                else:
                    logging.info('denying %s spawn, incorrect slots %s' %
                                 (self.meta['username'], self.meta['slots']))
        if (message['action'] == 'suicide'):
            self.archer.kill()
        if (message['action'] == 'stop'):
            self.archer.want_stop()
        if (message['action'] == 'move'):
            self.archer.want_move(message['direction'])
        if (message['action'] == 'attack'):
            self.archer.want_attack(message['direction'])

    def on_destroy(self, world_object):
        messages = list()
        if (hasattr(world_object, 'get_destroy_message')):
            msg = world_object.get_destroy_message()
            if (msg):
                messages.append(msg)
        self.trigger('remove', messages)

    def on_step(self, world):
        self.trigger('update', self.get_update())
        self.trigger('frame', self.get_frame())

    def get_update(self):
        messages = list()
        if (self.last_world_index != self.world.object_index.index):
            for index in range(self.last_world_index,
                               self.world.object_index.index):
                index = index + 1
                try:
                    world_object = self.world.get_object_by_id(index)
                    if (hasattr(world_object, 'get_update_message')):
                        msg = world_object.get_update_message(recipient=self)
                        if (msg):
                            messages.append(msg)
                except KeyError:
                    pass
            self.last_world_index = index
        return messages

    def get_frame(self):
        items = self.world.object_index.values()
        update = self.cache.get_frame_message_from_cache(self.world.step)
        if (update):
            return update

        update = list()
        for item in items:
            if (hasattr(item, 'get_frame_message')):
                data = item.get_frame_message()
                update.append(data)
                # if data and not(item in self.known.keys() and self.known[item] == data):
                # self.known[item] = data
        self.cache.cache_frame_messages(self.world.step, update)
        return update
class TestInterface(BaseTestCase):

	def setUp(self):
		super(TestInterface, self).setUp()
		path = os.path.dirname(os.path.os.path.realpath(__file__))
		path = os.path.join(path, 'assets/test1.tmx')
		self.world = World(path)
		self.spawn_point = self.world.get_object_by_name('spawn1')
		
		self.world_update_task = task.LoopingCall(self.world.processing_step)
		self.world_update_task.clock = self.clock
		self.world_update_task.start(1.0/30)

		self.networking_task = task.LoopingCall(self.world.networking_step)
		self.networking_task.clock = self.clock
		self.networking_task.start(1.0/30)
		
		self.connection = Connection(self.world)
		self.last_callback_data = None

	def tearDown(self):
		self.world_update_task.stop()
		self.networking_task.stop()

	def get_items_expected(self, expected_attr):
		# items_expected = self.world.object_lookup_by_name
		items_expected = self.world.object_index
		items_expected = {k: v for k, v in items_expected.items() if hasattr(v, expected_attr)}
		return items_expected

	def callback(self, *args, **kwargs):
		self.last_callback_data = list(*args)

	def get_last_callback_arguments(self):
		tmp = self.last_callback_data
		self.last_callback_data = None
		return tmp

	def test_get_frame(self):
		self.archer = Archer(self.world, reactor=self.clock)
		self.archer.spawn(self.spawn_point)
		self.connection.on('frame', self.callback)
		self.clock.advance(1)
		frame = self.get_last_callback_arguments()
		items_expected = self.get_items_expected('get_frame_message')
		self.assertEqual(len(frame), len(items_expected))

		for message in frame:
			matching_expected_item = items_expected.pop(message['id'])
			self.assertEqual(message['x'], int(settings.PPM*matching_expected_item.get_position()['x']))
			self.assertEqual(message['y'], int(settings.PPM*matching_expected_item.get_position()['y']))
			self.assertEqual(message['direction'], matching_expected_item.get_direction())

		self.assertEqual(len(items_expected), 0)

			
		self.clock.advance(1)
		frame = self.get_last_callback_arguments()
		# Nothing has changed!
		self.assertEqual(len(frame), 0)
		self.archer.want_move(directions['south'])
		self.advance_clock(1)
		frame = self.get_last_callback_arguments()
		self.assertEqual(len(frame), 1)
		message = frame.pop()
		self.assertEqual(message['x'], int(settings.PPM*self.archer.get_position()['x']))
		self.assertEqual(message['y'], int(settings.PPM*self.archer.get_position()['y']))
		self.assertEqual(message['direction'], self.archer.get_direction())

	def test_get_update(self):
		self.connection.on('update', self.callback)
		self.clock.advance(1)
		items_expected = self.get_items_expected('get_update_message')
		update = self.get_last_callback_arguments()
		self.assertEqual(len(update), len(items_expected))
		for message in update:
			self.assertIsInstance(message, UpdateMessage)
			matching_expected_item = items_expected.pop(message['id'])
			self.assertEqual(message['id'], matching_expected_item.id)
			# self.assertEqual(message['center'], False)
			#TEST MORE
		self.assertEqual(len(items_expected), 0)

		self.clock.advance(1)
		update = self.get_last_callback_arguments()


		self.assertIsNone(update)
		self.archer = Archer(self.world, reactor=self.clock)
		self.archer.spawn(self.spawn_point)
		self.clock.advance(1)
		update = self.get_last_callback_arguments()
		self.assertEqual(len(update), 1)

	def get_fake_msg(self, x=1, y=3.33, direction=directions['south']):
		msg = FakeMessage()
		msg['x'] = x
		msg['y'] = y
		msg['direction'] = direction
		return msg

	def test_packing(self):
		msg = self.get_fake_msg()
		packed = msg.pack()
		self.assertEqual(packed[0:4], struct.pack('!I', 1))
		self.assertEqual(packed[4:8], struct.pack('!f', 3.33))
		self.assertEqual(packed[8:9], struct.pack('!B', DirectionMessageMixin.direction_lookup[directions['south']]))


	def test_dehydration(self):
		x = 12
		y = 4.44
		dir = DirectionMessageMixin.direction_lookup[directions['west']]
		dehydrated_item = [x, y, dir]
		msg = FakeMessage.from_dehydrated(dehydrated_item)

		self.assertEqual(msg['x'], x)
		self.assertEqual(msg['y'], y)
		self.assertEqual(msg['direction'], directions['west'])

	def test_unpacking(self):
		# packed = '\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x02\x02\n'
		x = 12
		y = 7.77
		dir = DirectionMessageMixin.direction_lookup[directions['west']]

		packed = struct.pack("!I", x) + struct.pack("!f", y) + struct.pack("!B", dir) + "\n"
		msg = FakeMessage.from_packed(packed)
		self.assertEqual(msg['x'], x)
		self.assertAlmostEqual(msg['y'], y, places=4)
		self.assertEqual(msg['direction'], directions['west'])

	def test_message_packing(self):
		msg = self.get_fake_msg()
		msg2 = self.get_fake_msg(x=5, direction=directions['east'])
		result = pack_messages([msg, msg2])
		expected_byte_length = 1 + 2 * msg.get_byte_length()
		self.assertEqual(len(result), expected_byte_length)

	def test_message_unpacking(self):
		x1 = 12
		y1 = 4.44
		dir1 = DirectionMessageMixin.direction_lookup[directions['west']]
		packed = struct.pack("!I", x1) + struct.pack("!f", y1) + struct.pack("!B", dir1)
		x2 = 42
		y2 = 9.987654
		dir2 = DirectionMessageMixin.direction_lookup[directions['north']]
		packed = packed + struct.pack("!I", x2) + struct.pack("!f", y2) + struct.pack("!B", dir2) + "\n"

		packed = struct.pack("!B", 255) + packed

		messages = unpack_mesages(packed, message_types=message_types)
		self.assertEqual(len(messages), 2)
		self.assertEqual(messages[0]['x'], x1)
		self.assertAlmostEqual(messages[0]['y'], y1, places=4)
		self.assertEqual(messages[0]['direction'], directions['west'])
		self.assertEqual(messages[1]['x'], x2)
		self.assertAlmostEqual(messages[1]['y'], y2, places=4)
		self.assertEqual(messages[1]['direction'], directions['north'])

	def test_eating_own_dog_food(self):
		msg = self.get_fake_msg(y=1.0) 
		packed = msg.pack()
		unpacked = FakeMessage.from_packed(packed)
		self.assertEqual(msg, unpacked)
class Connection(EventsMixins):

	def __str__(self):
		return self.meta['username'];

	def __init__(self, world, cache):
		super(Connection, self).__init__()
		self.session_id = uuid.UUID(bytes=urandom(16))
		self.world = world
		self.cache = cache
		self.known = dict()
		self.last_world_index = 0
		self.last_frame_index = 0
		self.archer = Archer(self.world, player=self)

		self.meta = {
			"id": self.archer.id,
			"username": "******",
			"gender": "male",
			"slots": {},
			"kills": 0,
			"deaths": 0,
			"score": 0,
			"budget": settings.initial_budget,
			"attributes": self.archer.attributes
		}

		self.archer.interface = self

		logging.info("New connection %s" % self.session_id)

		self.world.on('destroy_object', self.on_destroy)
		self.world.on('step', self.on_step)
		self.on('useraction', self.on_user_action)
		self.on('disconnect', self.on_disconnect)
		self.on('usermsg', self.on_user_message)
		self.on('kill', self.on_kill)
		self.on('die', self.on_die)
		self.on('mob', self.on_mob)
		self.on('pickup', self.on_pickup)
		self.on('spawn', self.on_spawn)


	def on_spawn(self):
		self.meta['budget'] = settings.initial_budget
		self.trigger('meta', self.meta)

	def on_kill(self, prey):
		self.meta['kills'] = self.meta['kills'] + 1
		self.meta['score'] = self.meta['score'] + settings.score['kill']
		logging.info("%s fragged %s" % (self.meta['username'], prey.meta['username']))
		self.trigger('meta', self.meta)

	def on_die(self, killer=None):
		self.meta['deaths'] = self.meta['deaths'] + 1
		logging.info("%s died by the hand of %s" % (self.meta['username'], killer or "[unknown]"))
		self.trigger('meta', self.meta)

	def on_mob(self):
		self.meta['score'] = self.meta['score'] + settings.score['mob']
		self.trigger('meta', self.meta)

	def on_pickup(self, bonus):
		self.meta['budget'] = self.meta["budget"] + bonus.value
		self.trigger('meta', self.meta)

	def on_user_message(self, meta):
		changed = False
		if('username' in meta):
			new_username = meta['username'][0:10]
			if(new_username != self.meta['username']):
				self.meta['username'] = new_username
				logging.info("%s is now known as %s" % (self.session_id, self.meta['username']))
				changed = True
				
		if('gender' in meta and meta['gender'] != self.meta['gender']):
			self.meta['gender'] = meta['gender']
			changed = True

		if('slots' in meta and meta['slots'] != self.meta['slots']):
			if(verify_slots(meta['slots'], self.meta['budget'])):
				self.meta['slots'] = meta['slots']
				changed = True
			else:
				logging.info("%s provided incorrect meta msg (%s). Discarding" 
					% (self.meta['username'], meta['slots']))

		if(changed):
			self.archer.update_attributes()
			# self.trigger('meta', self.meta)


	def on_disconnect(self):
		self.world.off('destroy_object', self.on_destroy)
		self.world.off('step', self.on_step)
		self.off()
		self.archer.destroy()

	def on_user_action(self, message):
		spawn_points = self.world.get_spawn_points()
		shuffle(spawn_points)
		logging.info(message);
		if(message['action'] == 'spawn'):
			if(self.archer.can_spawn()):
				if(verify_slots(self.meta['slots'], self.meta['budget'])):
					self.archer.spawn(spawn_points[0])
				else:
					logging.info('denying %s spawn, incorrect slots %s' % (self.meta['username'], self.meta['slots']))
		if(message['action'] == 'suicide'):
			self.archer.kill();
		if(message['action'] == 'stop'):
			self.archer.want_stop()
		if(message['action'] == 'move'):
			self.archer.want_move(message['direction'])
		if(message['action'] == 'attack'):
			self.archer.want_attack(message['direction'])


	def on_destroy(self, world_object):
		messages = list()
		if(hasattr(world_object, 'get_destroy_message')):
			msg = world_object.get_destroy_message()
			if(msg):
				messages.append(msg)
		self.trigger('remove', messages)


	def on_step(self, world):
		self.trigger('update', self.get_update())
		self.trigger('frame', self.get_frame())

	def get_update(self):
		messages = list()
		if(self.last_world_index != self.world.object_index.index):
			for index in range(self.last_world_index, self.world.object_index.index):
				index = index+1
				try:
					world_object = self.world.get_object_by_id(index)
					if(hasattr(world_object, 'get_update_message')):
						msg = world_object.get_update_message(recipient=self)
						if(msg):
							messages.append(msg)
				except KeyError:
					pass
			self.last_world_index = index
		return messages

	def get_frame(self):
		items = self.world.object_index.values()
		update = self.cache.get_frame_message_from_cache(self.world.step)
		if(update):
			return update

		update = list()
		for item in items:
			if(hasattr(item, 'get_frame_message')):
				data = item.get_frame_message()
				update.append(data)
				# if data and not(item in self.known.keys() and self.known[item] == data):
					# self.known[item] = data
		self.cache.cache_frame_messages(self.world.step, update)
		return update
Beispiel #7
0
class TestInterface(BaseTestCase):
    def setUp(self):
        super(TestInterface, self).setUp()
        path = os.path.dirname(os.path.os.path.realpath(__file__))
        path = os.path.join(path, 'assets/test1.tmx')
        self.world = World(path)
        self.spawn_point = self.world.get_object_by_name('spawn1')

        self.world_update_task = task.LoopingCall(self.world.processing_step)
        self.world_update_task.clock = self.clock
        self.world_update_task.start(1.0 / 30)

        self.networking_task = task.LoopingCall(self.world.networking_step)
        self.networking_task.clock = self.clock
        self.networking_task.start(1.0 / 30)

        self.connection = Connection(self.world)
        self.last_callback_data = None

    def tearDown(self):
        self.world_update_task.stop()
        self.networking_task.stop()

    def get_items_expected(self, expected_attr):
        # items_expected = self.world.object_lookup_by_name
        items_expected = self.world.object_index
        items_expected = {
            k: v
            for k, v in items_expected.items() if hasattr(v, expected_attr)
        }
        return items_expected

    def callback(self, *args, **kwargs):
        self.last_callback_data = list(*args)

    def get_last_callback_arguments(self):
        tmp = self.last_callback_data
        self.last_callback_data = None
        return tmp

    def test_get_frame(self):
        self.archer = Archer(self.world, reactor=self.clock)
        self.archer.spawn(self.spawn_point)
        self.connection.on('frame', self.callback)
        self.clock.advance(1)
        frame = self.get_last_callback_arguments()
        items_expected = self.get_items_expected('get_frame_message')
        self.assertEqual(len(frame), len(items_expected))

        for message in frame:
            matching_expected_item = items_expected.pop(message['id'])
            self.assertEqual(
                message['x'],
                int(settings.PPM * matching_expected_item.get_position()['x']))
            self.assertEqual(
                message['y'],
                int(settings.PPM * matching_expected_item.get_position()['y']))
            self.assertEqual(message['direction'],
                             matching_expected_item.get_direction())

        self.assertEqual(len(items_expected), 0)

        self.clock.advance(1)
        frame = self.get_last_callback_arguments()
        # Nothing has changed!
        self.assertEqual(len(frame), 0)
        self.archer.want_move(directions['south'])
        self.advance_clock(1)
        frame = self.get_last_callback_arguments()
        self.assertEqual(len(frame), 1)
        message = frame.pop()
        self.assertEqual(message['x'],
                         int(settings.PPM * self.archer.get_position()['x']))
        self.assertEqual(message['y'],
                         int(settings.PPM * self.archer.get_position()['y']))
        self.assertEqual(message['direction'], self.archer.get_direction())

    def test_get_update(self):
        self.connection.on('update', self.callback)
        self.clock.advance(1)
        items_expected = self.get_items_expected('get_update_message')
        update = self.get_last_callback_arguments()
        self.assertEqual(len(update), len(items_expected))
        for message in update:
            self.assertIsInstance(message, UpdateMessage)
            matching_expected_item = items_expected.pop(message['id'])
            self.assertEqual(message['id'], matching_expected_item.id)
            # self.assertEqual(message['center'], False)
            #TEST MORE
        self.assertEqual(len(items_expected), 0)

        self.clock.advance(1)
        update = self.get_last_callback_arguments()

        self.assertIsNone(update)
        self.archer = Archer(self.world, reactor=self.clock)
        self.archer.spawn(self.spawn_point)
        self.clock.advance(1)
        update = self.get_last_callback_arguments()
        self.assertEqual(len(update), 1)

    def get_fake_msg(self, x=1, y=3.33, direction=directions['south']):
        msg = FakeMessage()
        msg['x'] = x
        msg['y'] = y
        msg['direction'] = direction
        return msg

    def test_packing(self):
        msg = self.get_fake_msg()
        packed = msg.pack()
        self.assertEqual(packed[0:4], struct.pack('!I', 1))
        self.assertEqual(packed[4:8], struct.pack('!f', 3.33))
        self.assertEqual(
            packed[8:9],
            struct.pack(
                '!B',
                DirectionMessageMixin.direction_lookup[directions['south']]))

    def test_dehydration(self):
        x = 12
        y = 4.44
        dir = DirectionMessageMixin.direction_lookup[directions['west']]
        dehydrated_item = [x, y, dir]
        msg = FakeMessage.from_dehydrated(dehydrated_item)

        self.assertEqual(msg['x'], x)
        self.assertEqual(msg['y'], y)
        self.assertEqual(msg['direction'], directions['west'])

    def test_unpacking(self):
        # packed = '\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x02\x02\n'
        x = 12
        y = 7.77
        dir = DirectionMessageMixin.direction_lookup[directions['west']]

        packed = struct.pack("!I", x) + struct.pack("!f", y) + struct.pack(
            "!B", dir) + "\n"
        msg = FakeMessage.from_packed(packed)
        self.assertEqual(msg['x'], x)
        self.assertAlmostEqual(msg['y'], y, places=4)
        self.assertEqual(msg['direction'], directions['west'])

    def test_message_packing(self):
        msg = self.get_fake_msg()
        msg2 = self.get_fake_msg(x=5, direction=directions['east'])
        result = pack_messages([msg, msg2])
        expected_byte_length = 1 + 2 * msg.get_byte_length()
        self.assertEqual(len(result), expected_byte_length)

    def test_message_unpacking(self):
        x1 = 12
        y1 = 4.44
        dir1 = DirectionMessageMixin.direction_lookup[directions['west']]
        packed = struct.pack("!I", x1) + struct.pack("!f", y1) + struct.pack(
            "!B", dir1)
        x2 = 42
        y2 = 9.987654
        dir2 = DirectionMessageMixin.direction_lookup[directions['north']]
        packed = packed + struct.pack("!I", x2) + struct.pack(
            "!f", y2) + struct.pack("!B", dir2) + "\n"

        packed = struct.pack("!B", 255) + packed

        messages = unpack_mesages(packed, message_types=message_types)
        self.assertEqual(len(messages), 2)
        self.assertEqual(messages[0]['x'], x1)
        self.assertAlmostEqual(messages[0]['y'], y1, places=4)
        self.assertEqual(messages[0]['direction'], directions['west'])
        self.assertEqual(messages[1]['x'], x2)
        self.assertAlmostEqual(messages[1]['y'], y2, places=4)
        self.assertEqual(messages[1]['direction'], directions['north'])

    def test_eating_own_dog_food(self):
        msg = self.get_fake_msg(y=1.0)
        packed = msg.pack()
        unpacked = FakeMessage.from_packed(packed)
        self.assertEqual(msg, unpacked)