Skip to main content
Glama
test_entity_system.pyβ€’24.5 kB
""" Tests for the MCPy entity system. """ import os import shutil import tempfile import unittest from unittest import mock import uuid import numpy as np from mcpy.core.entity_system import ( EntitySystem, Entity, PlayerEntity, MobEntity, ItemEntity, HostileMobEntity, PassiveMobEntity, VehicleEntity, FallingBlockEntity, ProjectileEntity, EntityFactory, EntityTracker, EntityError, EntityOperationError, EntityNotFoundError, EntityLimitExceededError ) from mcpy.core.world_engine import WorldEngine class MockWorld: """Mock world for testing.""" def __init__(self): """Initialize the mock world.""" self.blocks = {} def get_block(self, x, y, z): """Get a block from the world.""" return self.blocks.get((x, y, z), 0) def set_block(self, x, y, z, block_id): """Set a block in the world.""" self.blocks[(x, y, z)] = block_id return True class TestEntity(unittest.TestCase): """Test the Entity class.""" def test_initialization(self): """Test entity initialization.""" entity = Entity(0, 100.5, 64.0, 200.5) self.assertEqual(entity.entity_type, 0) self.assertEqual(entity.x, 100.5) self.assertEqual(entity.y, 64.0) self.assertEqual(entity.z, 200.5) self.assertEqual(entity.velocity_x, 0.0) self.assertEqual(entity.velocity_y, 0.0) self.assertEqual(entity.velocity_z, 0.0) self.assertTrue(entity.active) self.assertTrue(entity.affected_by_gravity) self.assertFalse(entity.on_ground) def test_to_from_data(self): """Test conversion to and from data dictionaries.""" entity = Entity(0, 100.5, 64.0, 200.5) entity.velocity_x = 0.1 entity.velocity_y = 0.2 entity.velocity_z = 0.3 entity.yaw = 45.0 entity.pitch = 30.0 entity.data = {'custom': 'data'} data = entity.to_data() self.assertEqual(data['type'], 0) self.assertEqual(data['x'], 100.5) self.assertEqual(data['y'], 64.0) self.assertEqual(data['z'], 200.5) self.assertEqual(data['velocity_x'], 0.1) self.assertEqual(data['velocity_y'], 0.2) self.assertEqual(data['velocity_z'], 0.3) self.assertEqual(data['yaw'], 45.0) self.assertEqual(data['pitch'], 30.0) self.assertEqual(data['data'], {'custom': 'data'}) new_entity = Entity.from_data(data) self.assertEqual(new_entity.entity_type, 0) self.assertEqual(new_entity.x, 100.5) self.assertEqual(new_entity.y, 64.0) self.assertEqual(new_entity.z, 200.5) self.assertEqual(new_entity.velocity_x, 0.1) self.assertEqual(new_entity.velocity_y, 0.2) self.assertEqual(new_entity.velocity_z, 0.3) self.assertEqual(new_entity.yaw, 45.0) self.assertEqual(new_entity.pitch, 30.0) self.assertEqual(new_entity.data, {'custom': 'data'}) class TestPlayerEntity(unittest.TestCase): """Test the PlayerEntity class.""" def test_initialization(self): """Test player initialization.""" player_uuid = uuid.uuid4() player = PlayerEntity("TestPlayer", player_uuid, 100.5, 64.0, 200.5) self.assertEqual(player.username, "TestPlayer") self.assertEqual(player.uuid, player_uuid) self.assertEqual(player.entity_type, 0) # PLAYER self.assertEqual(player.x, 100.5) self.assertEqual(player.y, 64.0) self.assertEqual(player.z, 200.5) self.assertEqual(player.health, 20) self.assertEqual(player.food_level, 20) self.assertEqual(player.experience, 0.0) self.assertEqual(player.level, 0) def test_update(self): """Test player update.""" player_uuid = uuid.uuid4() player = PlayerEntity("TestPlayer", player_uuid, 100.5, 64.0, 200.5) # Test regeneration at high food level player.health = 19 player.food_level = 20 player.update(80) # Multiple of 80 to trigger regeneration self.assertEqual(player.health, 20) # Test no regeneration at low food level player.health = 19 player.food_level = 17 player.update(80) self.assertEqual(player.health, 19) def test_to_from_data(self): """Test conversion to and from data dictionaries.""" player_uuid = uuid.uuid4() player = PlayerEntity("TestPlayer", player_uuid, 100.5, 64.0, 200.5) player.health = 15 player.food_level = 18 player.experience = 0.5 player.level = 5 player.inventory = {'slot_1': {'id': 1, 'count': 64}} data = player.to_data() self.assertEqual(data['username'], "TestPlayer") self.assertEqual(data['uuid'], str(player_uuid)) self.assertEqual(data['health'], 15) self.assertEqual(data['food_level'], 18) self.assertEqual(data['experience'], 0.5) self.assertEqual(data['level'], 5) self.assertEqual(data['inventory'], {'slot_1': {'id': 1, 'count': 64}}) new_player = PlayerEntity.from_data(data) self.assertEqual(new_player.username, "TestPlayer") self.assertEqual(str(new_player.uuid), str(player_uuid)) self.assertEqual(new_player.health, 15) self.assertEqual(new_player.food_level, 18) self.assertEqual(new_player.experience, 0.5) self.assertEqual(new_player.level, 5) self.assertEqual(new_player.inventory, {'slot_1': {'id': 1, 'count': 64}}) class TestMobEntity(unittest.TestCase): """Test the MobEntity class.""" def test_initialization(self): """Test mob initialization.""" mob = MobEntity(1, 100.5, 64.0, 200.5, 20, True) # ZOMBIE self.assertEqual(mob.entity_type, 1) self.assertEqual(mob.x, 100.5) self.assertEqual(mob.y, 64.0) self.assertEqual(mob.z, 200.5) self.assertEqual(mob.health, 20) self.assertEqual(mob.max_health, 20) self.assertTrue(mob.hostile) def test_damage_heal(self): """Test damaging and healing mobs.""" mob = MobEntity(1, 100.5, 64.0, 200.5, 20, True) # Test damage mob.damage(5) self.assertEqual(mob.health, 15) # Test healing mob.heal(3) self.assertEqual(mob.health, 18) # Test healing beyond max health mob.heal(10) self.assertEqual(mob.health, 20) # Test fatal damage mob.damage(30) self.assertEqual(mob.health, 0) self.assertFalse(mob.active) class TestItemEntity(unittest.TestCase): """Test the ItemEntity class.""" def test_initialization(self): """Test item initialization.""" item = ItemEntity(1, 64, 100.5, 64.0, 200.5) self.assertEqual(item.entity_type, 10) # ITEM self.assertEqual(item.x, 100.5) self.assertEqual(item.y, 64.0) self.assertEqual(item.z, 200.5) self.assertEqual(item.item_id, 1) self.assertEqual(item.count, 64) self.assertTrue(hasattr(item, 'pickup_delay')) self.assertTrue(hasattr(item, 'despawn_time')) @mock.patch('mcpy.core.entity_system.time') def test_despawn(self, mock_time): """Test item despawning.""" mock_time.return_value = 1000 # Current time item = ItemEntity(1, 64, 100.5, 64.0, 200.5) self.assertTrue(item.active) # Time hasn't advanced, so item should still be active mock_time.return_value = 1000 item.update(0) self.assertTrue(item.active) # Time has advanced, but not enough to despawn mock_time.return_value = 1200 item.update(0) self.assertTrue(item.active) # Time has advanced enough to despawn mock_time.return_value = 1500 item.update(0) self.assertFalse(item.active) class TestEntitySystem(unittest.TestCase): """Test the EntitySystem class.""" def setUp(self): """Set up test environment.""" self.world = MockWorld() self.entity_system = EntitySystem(self.world, max_entities=100) def test_spawn_entity(self): """Test spawning an entity.""" entity = self.entity_system.spawn_entity(1, 100.5, 64.0, 200.5) # ZOMBIE self.assertIsNotNone(entity) self.assertEqual(entity.entity_type, 1) self.assertEqual(entity.x, 100.5) self.assertEqual(entity.y, 64.0) self.assertEqual(entity.z, 200.5) # Check that the entity was added to tracking self.assertIn(entity.id, self.entity_system.entities) chunk_key = f"{entity.chunk_x},{entity.chunk_z}" self.assertIn(entity.id, self.entity_system.entities_by_chunk[chunk_key]) def test_add_remove_player(self): """Test adding and removing a player.""" player_uuid = uuid.uuid4() player = self.entity_system.add_player("TestPlayer", player_uuid, 100.5, 64.0, 200.5) self.assertIsNotNone(player) self.assertEqual(player.username, "TestPlayer") self.assertEqual(player.uuid, player_uuid) # Check that the player was added to tracking self.assertIn(player.id, self.entity_system.entities) self.assertIn(player.id, self.entity_system.players) chunk_key = f"{player.chunk_x},{player.chunk_z}" self.assertIn(player.id, self.entity_system.entities_by_chunk[chunk_key]) # Remove the player self.entity_system.remove_player(player.id) # Check that the player was removed from tracking self.assertNotIn(player.id, self.entity_system.entities) self.assertNotIn(player.id, self.entity_system.players) self.assertNotIn(player.id, self.entity_system.entities_by_chunk.get(chunk_key, set())) def test_get_entities_in_range(self): """Test getting entities in range.""" # Spawn entities at different positions entity1 = self.entity_system.spawn_entity(1, 100.0, 64.0, 100.0) entity2 = self.entity_system.spawn_entity(1, 105.0, 64.0, 100.0) entity3 = self.entity_system.spawn_entity(1, 120.0, 64.0, 100.0) # Get entities within a 10-block radius entities = self.entity_system.get_entities_in_range(100.0, 100.0, 10.0) self.assertEqual(len(entities), 2) self.assertIn(entity1, entities) self.assertIn(entity2, entities) self.assertNotIn(entity3, entities) def test_get_entities_in_chunks(self): """Test getting entities in specific chunks.""" # Spawn entities in different chunks entity1 = self.entity_system.spawn_entity(1, 10.0, 64.0, 10.0) # Chunk 0,0 entity2 = self.entity_system.spawn_entity(1, 25.0, 64.0, 10.0) # Chunk 1,0 # Get entities in chunk 0,0 entities = self.entity_system.get_entities_in_chunks([(0, 0)]) self.assertEqual(len(entities), 1) self.assertIn(entity1, entities) # Get entities in chunks 0,0 and 1,0 entities = self.entity_system.get_entities_in_chunks([(0, 0), (1, 0)]) self.assertEqual(len(entities), 2) self.assertIn(entity1, entities) self.assertIn(entity2, entities) def test_get_entity_by_id(self): """Test getting an entity by ID.""" entity = self.entity_system.spawn_entity(1, 100.0, 64.0, 100.0) # Get the entity by ID found_entity = self.entity_system.get_entity_by_id(entity.id) self.assertIs(found_entity, entity) # Try to get a non-existent entity found_entity = self.entity_system.get_entity_by_id(999999) self.assertIsNone(found_entity) def test_get_player_by_name(self): """Test getting a player by name.""" player_uuid = uuid.uuid4() player = self.entity_system.add_player("TestPlayer", player_uuid, 100.0, 64.0, 100.0) # Get the player by name found_player = self.entity_system.get_player_by_name("TestPlayer") self.assertIs(found_player, player) # Try case-insensitive lookup found_player = self.entity_system.get_player_by_name("testplayer") self.assertIs(found_player, player) # Try to get a non-existent player found_player = self.entity_system.get_player_by_name("NonExistentPlayer") self.assertIsNone(found_player) class TestExtendedEntities(unittest.TestCase): """Test the newly added entity classes.""" def setUp(self): """Set up the test environment.""" self.mock_world = MockWorld() self.entity_system = EntitySystem(self.mock_world) def test_hostile_mob_entity(self): """Test hostile mob entity creation and behaviors.""" # Test zombie creation zombie = HostileMobEntity(1, 100.0, 64.0, 100.0, 20) self.assertEqual(zombie.entity_type, 1) # ZOMBIE self.assertEqual(zombie.health, 20) self.assertTrue(zombie.hostile) self.assertGreater(zombie.attack_damage, 0) # Test attack cooldown self.assertTrue(zombie.can_attack(0)) # Test attack player = PlayerEntity("TestPlayer", uuid.uuid4(), 100.0, 64.0, 101.0) player.health = 20 zombie.attack(player, 0) self.assertLess(player.health, 20) # Test attack cooldown self.assertFalse(zombie.can_attack(0)) # Can't attack again immediately self.assertTrue(zombie.can_attack(30)) # Can attack after cooldown def test_passive_mob_entity(self): """Test passive mob entity creation and behaviors.""" # Test cow creation cow1 = PassiveMobEntity(51, 100.0, 64.0, 100.0, 10) self.assertEqual(cow1.entity_type, 51) # COW self.assertEqual(cow1.health, 10) self.assertFalse(cow1.hostile) self.assertFalse(cow1.is_baby) # Test breeding cow2 = PassiveMobEntity(51, 101.0, 64.0, 100.0, 10) # Should be able to breed initially self.assertTrue(cow1.can_breed(0)) self.assertTrue(cow2.can_breed(0)) # Breed cows baby_cow = cow1.breed(cow2, 0) # Verify baby properties self.assertIsNotNone(baby_cow) self.assertEqual(baby_cow.entity_type, 51) self.assertTrue(baby_cow.is_baby) self.assertEqual(baby_cow.health, 5) # Half of parent's health # Parents should have breeding cooldown self.assertFalse(cow1.can_breed(0)) self.assertFalse(cow2.can_breed(0)) # After cooldown, should be able to breed again self.assertTrue(cow1.can_breed(10000)) def test_vehicle_entity(self): """Test vehicle entity creation and behaviors.""" # Test boat creation boat = VehicleEntity(200, 100.0, 64.0, 100.0) self.assertEqual(boat.entity_type, 200) # BOAT self.assertGreater(boat.width, 0.6) # Wider than player self.assertEqual(len(boat.passengers), 0) # Add passenger player = PlayerEntity("TestPlayer", uuid.uuid4(), 100.0, 64.0, 100.0) self.assertTrue(boat.add_passenger(player)) self.assertEqual(len(boat.passengers), 1) self.assertEqual(boat.passengers[0].id, player.id) self.assertEqual(player.data.get('vehicle'), boat.id) # Remove passenger self.assertTrue(boat.remove_passenger(player)) self.assertEqual(len(boat.passengers), 0) self.assertNotIn('vehicle', player.data) def test_falling_block_entity(self): """Test falling block entity creation and behaviors.""" # Test falling sand sand_block = FallingBlockEntity(12, 0, 100.0, 70.0, 100.0) self.assertEqual(sand_block.entity_type, 12) # FALLING_BLOCK self.assertEqual(sand_block.block_id, 12) # Sand block ID self.assertEqual(sand_block.data_value, 0) self.assertEqual(sand_block.time_existed, 0) # Update should increment time sand_block.update(1) self.assertEqual(sand_block.time_existed, 1) # Simulate falling for a while sand_block.update(2) self.assertEqual(sand_block.time_existed, 2) # After 6000 ticks, should despawn sand_block.time_existed = 6000 sand_block.update(6001) self.assertFalse(sand_block.active) class TestEntityTracker(unittest.TestCase): """Test the EntityTracker class.""" def setUp(self): """Set up the test environment.""" self.tracker = EntityTracker(100) self.entity1 = Entity(0, 10.0, 64.0, 10.0) self.entity2 = Entity(1, 20.0, 64.0, 20.0) self.entity3 = Entity(2, 30.0, 64.0, 30.0) def test_add_entity(self): """Test adding entities to the tracker.""" self.tracker.add_entity(self.entity1) self.assertEqual(self.tracker.get_entity_count(), 1) self.assertTrue(self.tracker.entity_exists(self.entity1.id)) self.tracker.add_entity(self.entity2) self.assertEqual(self.tracker.get_entity_count(), 2) self.assertTrue(self.tracker.entity_exists(self.entity2.id)) with self.assertRaises(EntityOperationError): self.tracker.add_entity(None) def test_remove_entity(self): """Test removing entities from the tracker.""" # Add entities self.tracker.add_entity(self.entity1) self.tracker.add_entity(self.entity2) self.assertEqual(self.tracker.get_entity_count(), 2) # Remove entity self.tracker.remove_entity(self.entity1.id) self.assertEqual(self.tracker.get_entity_count(), 1) self.assertFalse(self.tracker.entity_exists(self.entity1.id)) self.assertTrue(self.tracker.entity_exists(self.entity2.id)) # Try to remove non-existent entity with self.assertRaises(EntityNotFoundError): self.tracker.remove_entity(999) def test_get_entities_in_range(self): """Test getting entities within a range.""" # Add entities self.tracker.add_entity(self.entity1) self.tracker.add_entity(self.entity2) self.tracker.add_entity(self.entity3) # Get entities in range entities = self.tracker.get_entities_in_range(15.0, 64.0, 15.0, 10.0) self.assertEqual(len(entities), 1) self.assertEqual(entities[0].id, self.entity1.id) # Larger range entities = self.tracker.get_entities_in_range(20.0, 64.0, 20.0, 15.0) self.assertEqual(len(entities), 2) # Even larger range entities = self.tracker.get_entities_in_range(20.0, 64.0, 20.0, 30.0) self.assertEqual(len(entities), 3) def test_get_entities_of_type(self): """Test getting entities by type.""" # Add entities self.tracker.add_entity(self.entity1) # Type 0 self.tracker.add_entity(self.entity2) # Type 1 self.tracker.add_entity(Entity(1, 25.0, 64.0, 25.0)) # Another Type 1 # Get entities by type entities = self.tracker.get_entities_of_type(0) self.assertEqual(len(entities), 1) self.assertEqual(entities[0].id, self.entity1.id) entities = self.tracker.get_entities_of_type(1) self.assertEqual(len(entities), 2) entities = self.tracker.get_entities_of_type(999) # Non-existent type self.assertEqual(len(entities), 0) def test_update_entity_chunk(self): """Test updating entity chunks.""" # Add entity self.tracker.add_entity(self.entity1) old_chunk_x, old_chunk_z = self.entity1.chunk_x, self.entity1.chunk_z # Move entity to new chunk self.entity1.x += 32.0 # Move 2 chunks over # Update chunk tracking self.tracker.update_entity_chunk(self.entity1) self.assertNotEqual(old_chunk_x, self.entity1.chunk_x) # Entity should still be tracked self.assertTrue(self.tracker.entity_exists(self.entity1.id)) # Try to update None entity with self.assertRaises(EntityOperationError): self.tracker.update_entity_chunk(None) class TestEntityFactory(unittest.TestCase): """Test the EntityFactory class.""" def setUp(self): """Set up the test environment.""" self.mock_world = MockWorld() self.factory = EntityFactory(self.mock_world) def test_create_hostile_mob(self): """Test creating hostile mobs.""" # Test zombie zombie = self.factory.create_entity(1, 100.0, 64.0, 100.0) self.assertEqual(zombie.entity_type, 1) # ZOMBIE self.assertEqual(zombie.__class__.__name__, "MobEntity") self.assertTrue(zombie.hostile) # Test creeper creeper = self.factory.create_entity(3, 100.0, 64.0, 100.0) self.assertEqual(creeper.entity_type, 3) # CREEPER self.assertTrue(creeper.hostile) # Test with additional data enderman = self.factory.create_entity(6, 100.0, 64.0, 100.0, {"held_block": 1}) self.assertEqual(enderman.entity_type, 6) # ENDERMAN self.assertTrue(enderman.hostile) self.assertEqual(enderman.data.get("held_block"), 1) def test_create_passive_mob(self): """Test creating passive mobs.""" # Test cow cow = self.factory.create_entity(51, 100.0, 64.0, 100.0) self.assertEqual(cow.entity_type, 51) # COW self.assertEqual(cow.__class__.__name__, "MobEntity") self.assertFalse(cow.hostile) # Test sheep with additional data sheep = self.factory.create_entity(52, 100.0, 64.0, 100.0, {"color": 0}) self.assertEqual(sheep.entity_type, 52) # SHEEP self.assertFalse(sheep.hostile) self.assertEqual(sheep.data.get("color"), 0) def test_create_projectile(self): """Test creating projectiles.""" # Create a shooter entity player = PlayerEntity("TestPlayer", uuid.uuid4(), 100.0, 64.0, 100.0) # Test arrow arrow = self.factory.create_entity(100, 100.0, 65.0, 100.0, { "shooter": player, "velocity_x": 1.0, "velocity_y": 0.1, "velocity_z": 0.0 }) self.assertEqual(arrow.entity_type, 100) # ARROW self.assertEqual(arrow.__class__.__name__, "ProjectileEntity") self.assertEqual(arrow.shooter, player) self.assertEqual(arrow.velocity_x, 1.0) def test_create_item(self): """Test creating item entities.""" # Test item item = self.factory.create_entity(150, 100.0, 64.0, 100.0, { "item_id": 264, # Diamond "count": 5 }) self.assertEqual(item.entity_type, 150) # ITEM self.assertEqual(item.__class__.__name__, "ItemEntity") self.assertEqual(item.item_id, 264) self.assertEqual(item.count, 5) def test_create_falling_block(self): """Test creating falling block entities.""" # Test falling block block = self.factory.create_entity(151, 100.0, 70.0, 100.0, { "block_id": 12, # Sand "data_value": 0 }) self.assertEqual(block.entity_type, 151) # FALLING_BLOCK self.assertEqual(block.data.get("block_id"), 12) self.assertEqual(block.data.get("data_value"), 0) def test_invalid_entity(self): """Test creating invalid entity types.""" # Player should return None (use add_player instead) player = self.factory.create_entity(0, 100.0, 64.0, 100.0) self.assertIsNone(player) # Invalid type should return a generic entity generic = self.factory.create_entity(999, 100.0, 64.0, 100.0) self.assertEqual(generic.entity_type, 999) self.assertEqual(generic.__class__.__name__, "Entity")

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/magi8101/Mcpy'

If you have feedback or need assistance with the MCP directory API, please join our Discord server