diff --git a/docs/src/progress/renderertodo.md b/docs/src/progress/renderertodo.md index 21311047..76e73968 100644 --- a/docs/src/progress/renderertodo.md +++ b/docs/src/progress/renderertodo.md @@ -1488,6 +1488,10 @@ Sound effect on swinging fists/weapons Queue loading audio file on creating virtual spatial source Scaffold grid alignment data for entities Remove old title menu items +Fix first person tree animation not nullchecking +Fix yoga not applying on item drop window creation +Fix client not destroying item on remove from inventory +Tests for above bugs diff --git a/src/main/java/electrosphere/client/scene/ClientSceneWrapper.java b/src/main/java/electrosphere/client/scene/ClientSceneWrapper.java index c7b6ece5..11a3218e 100644 --- a/src/main/java/electrosphere/client/scene/ClientSceneWrapper.java +++ b/src/main/java/electrosphere/client/scene/ClientSceneWrapper.java @@ -82,6 +82,7 @@ public class ClientSceneWrapper { */ public int mapClientToServerId(int clientId){ if(clientToServerIdMap.get(clientId) == null){ + LoggerInterface.loggerNetworking.ERROR(new Error("Failed to map client entity " + clientId + " to server entity!")); return -1; } return clientToServerIdMap.get(clientId); diff --git a/src/main/java/electrosphere/client/ui/menu/WindowUtils.java b/src/main/java/electrosphere/client/ui/menu/WindowUtils.java index f85830c6..57ce3060 100644 --- a/src/main/java/electrosphere/client/ui/menu/WindowUtils.java +++ b/src/main/java/electrosphere/client/ui/menu/WindowUtils.java @@ -229,8 +229,13 @@ public class WindowUtils { WindowUtils.replaceMainMenuContents(MenuGeneratorsTitleMenu.createTitleMenu()); } + /** + * Creates the window to receive item drop events + */ static void initItemDropWindow(){ - Globals.elementService.registerWindow(WindowStrings.WINDDOW_ITEM_DROP, MenuGeneratorsInventory.worldItemDropCaptureWindow()); + Element itemDropWindow = MenuGeneratorsInventory.worldItemDropCaptureWindow(); + Globals.elementService.registerWindow(WindowStrings.WINDDOW_ITEM_DROP, itemDropWindow); + Globals.signalSystem.post(SignalType.YOGA_APPLY, itemDropWindow); } static void initItemDragContainerWindow(){ diff --git a/src/main/java/electrosphere/client/ui/menu/ingame/MenuGeneratorsInventory.java b/src/main/java/electrosphere/client/ui/menu/ingame/MenuGeneratorsInventory.java index 50a3e878..1666645b 100644 --- a/src/main/java/electrosphere/client/ui/menu/ingame/MenuGeneratorsInventory.java +++ b/src/main/java/electrosphere/client/ui/menu/ingame/MenuGeneratorsInventory.java @@ -6,6 +6,7 @@ import electrosphere.client.ui.menu.WindowStrings; import electrosphere.client.ui.menu.WindowUtils; import electrosphere.engine.Globals; import electrosphere.engine.assetmanager.AssetDataStrings; +import electrosphere.engine.signal.Signal.SignalType; import electrosphere.entity.state.inventory.InventoryUtils; import electrosphere.entity.types.item.ItemUtils; import electrosphere.game.data.item.Item; @@ -51,6 +52,7 @@ public class MenuGeneratorsInventory { div.setPositionY(0); div.setWidth(Globals.WINDOW_WIDTH); div.setHeight(Globals.WINDOW_HEIGHT); + div.setFlexGrow(1); rVal.addChild(div); return rVal; } diff --git a/src/main/java/electrosphere/entity/state/client/firstPerson/FirstPersonTree.java b/src/main/java/electrosphere/entity/state/client/firstPerson/FirstPersonTree.java index c0337250..dc429c48 100644 --- a/src/main/java/electrosphere/entity/state/client/firstPerson/FirstPersonTree.java +++ b/src/main/java/electrosphere/entity/state/client/firstPerson/FirstPersonTree.java @@ -164,7 +164,7 @@ public class FirstPersonTree implements BehaviorTree { * @param priority The priority of the animation */ public static void conditionallyPlayAnimation(Entity entity, String animationName, int priority, double offset){ - if(entity != null && entity == Globals.playerEntity && FirstPersonTree.hasTree(Globals.firstPersonEntity)){ + if(entity != null && entity == Globals.playerEntity && Globals.firstPersonEntity != null && FirstPersonTree.hasTree(Globals.firstPersonEntity)){ FirstPersonTree.getTree(Globals.firstPersonEntity).playAnimation(animationName, priority); } } @@ -185,7 +185,7 @@ public class FirstPersonTree implements BehaviorTree { * @param animationName the name of the animation */ public static void conditionallyPlayAnimation(Entity entity, TreeDataAnimation animation){ - if(entity != null && entity == Globals.playerEntity && FirstPersonTree.hasTree(Globals.firstPersonEntity)){ + if(entity != null && entity == Globals.playerEntity && Globals.firstPersonEntity != null && FirstPersonTree.hasTree(Globals.firstPersonEntity)){ FirstPersonTree.getTree(Globals.firstPersonEntity).playAnimation(animation); } } diff --git a/src/main/java/electrosphere/entity/state/inventory/ClientInventoryState.java b/src/main/java/electrosphere/entity/state/inventory/ClientInventoryState.java index 68c4e46e..67c9e155 100644 --- a/src/main/java/electrosphere/entity/state/inventory/ClientInventoryState.java +++ b/src/main/java/electrosphere/entity/state/inventory/ClientInventoryState.java @@ -52,9 +52,14 @@ public class ClientInventoryState implements BehaviorTree { WindowUtils.attemptRedrawInventoryWindows(); } break; case REMOVEITEMFROMINVENTORY: { - InventoryUtils.removeItemFromInventories(parent, Globals.clientSceneWrapper.getEntityFromServerId(message.getentityId())); - //attempt re-render ui - WindowUtils.attemptRedrawInventoryWindows(); + Entity item = Globals.clientSceneWrapper.getEntityFromServerId(message.getentityId()); + if(item != null){ + InventoryUtils.removeItemFromInventories(parent, item); + //attempt re-render ui + WindowUtils.attemptRedrawInventoryWindows(); + //destroy the item + ClientEntityUtils.destroyEntity(item); + } } break; case SERVERCOMMANDMOVEITEMCONTAINER: { //this is a command to switch an item from one inventory to another (ie equip->natural or vice-versa) diff --git a/src/main/java/electrosphere/entity/state/inventory/InventoryUtils.java b/src/main/java/electrosphere/entity/state/inventory/InventoryUtils.java index 55bf5f14..ca2adfd1 100644 --- a/src/main/java/electrosphere/entity/state/inventory/InventoryUtils.java +++ b/src/main/java/electrosphere/entity/state/inventory/InventoryUtils.java @@ -339,6 +339,12 @@ public class InventoryUtils { * @param item The item to eject */ public static void serverAttemptEjectItemTransform(Entity creature, Entity item){ + if(creature == null){ + throw new Error("Provided null creature!"); + } + if(item == null){ + throw new Error("Provided null item!"); + } //verify creature is creature, item is item, inventory exists, and item is in inventory boolean creatureIsCreature = CreatureUtils.isCreature(creature); boolean itemIsItem = ItemUtils.isItem(item); @@ -403,6 +409,12 @@ public class InventoryUtils { //need creature so we can figure out where to drop the item public static void serverAttemptEjectItem(Entity creature, Entity item){ + if(creature == null){ + throw new Error("Provided null creature!"); + } + if(item == null){ + throw new Error("Provided null item!"); + } //if we're the server, immediately attempt the transform InventoryUtils.serverAttemptEjectItemTransform(creature,item); } @@ -434,6 +446,12 @@ public class InventoryUtils { * @param item The item to remove */ public static void removeItemFromInventories(Entity creature, Entity item){ + if(creature == null){ + throw new Error("Creature is null!"); + } + if(item == null){ + throw new Error("Item is null!"); + } //verify creature is creature, item is item, inventory exists, and item is in inventory boolean creatureIsCreature = CreatureUtils.isCreature(creature); boolean itemIsItem = ItemUtils.isItem(item); diff --git a/src/test/java/electrosphere/client/ui/menu/WindowUtilsIntegrationTests.java b/src/test/java/electrosphere/client/ui/menu/WindowUtilsIntegrationTests.java new file mode 100644 index 00000000..97d36ce4 --- /dev/null +++ b/src/test/java/electrosphere/client/ui/menu/WindowUtilsIntegrationTests.java @@ -0,0 +1,32 @@ +package electrosphere.client.ui.menu; + +import static org.junit.jupiter.api.Assertions.assertNotEquals; + +import electrosphere.engine.Globals; +import electrosphere.renderer.ui.elements.Div; +import electrosphere.renderer.ui.elements.Window; +import electrosphere.test.annotations.IntegrationTest; +import electrosphere.test.template.EntityTestTemplate; +import electrosphere.test.testutils.TestEngineUtils; + +/** + * Integration tests for WindowUtils + */ +public class WindowUtilsIntegrationTests extends EntityTestTemplate { + + /** + * Make sure item drop event capture window size accounts for whole screen + */ + @IntegrationTest + public void test_ItemDropWindow_size(){ + //warm up engine + TestEngineUtils.simulateFrames(1); + + Window window = (Window)Globals.elementService.getWindow(WindowStrings.WINDDOW_ITEM_DROP); + Div captureDiv = (Div)window.getChildren().get(0); + + assertNotEquals(captureDiv.getWidth(),0); + assertNotEquals(captureDiv.getHeight(),0); + } + +} diff --git a/src/test/java/electrosphere/entity/state/inventory/InventoryUtilsTests.java b/src/test/java/electrosphere/entity/state/inventory/InventoryUtilsTests.java new file mode 100644 index 00000000..2be0ada6 --- /dev/null +++ b/src/test/java/electrosphere/entity/state/inventory/InventoryUtilsTests.java @@ -0,0 +1,139 @@ +package electrosphere.entity.state.inventory; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Set; + +import org.joml.Vector3d; + +import electrosphere.engine.Globals; +import electrosphere.entity.Entity; +import electrosphere.entity.EntityTags; +import electrosphere.entity.types.item.ItemUtils; +import electrosphere.test.annotations.IntegrationTest; +import electrosphere.test.template.EntityTestTemplate; +import electrosphere.test.testutils.TestEngineUtils; + +/** + * Tests for the inventory utils functions + */ +public class InventoryUtilsTests extends EntityTestTemplate { + + /** + * Make sure clientAttemptStoreItem performs its intended function + */ + @IntegrationTest + public void test_clientAttemptStoreItem(){ + //warm up engine + TestEngineUtils.simulateFrames(1); + + //spawn entities + TestEngineUtils.spawnPlayerEntity(); + Entity katana = ItemUtils.serverSpawnBasicItem(Globals.realmManager.first(), new Vector3d(0,0,0), "Katana2H"); + + //wait for entities to propagate to client + TestEngineUtils.simulateFrames(1); + + //verify the client got the extra entities + Set clientSideCreatures = Globals.clientSceneWrapper.getScene().getEntitiesWithTag(EntityTags.CREATURE); + assertEquals(1, clientSideCreatures.size()); + Set clientSideItems = Globals.clientSceneWrapper.getScene().getEntitiesWithTag(EntityTags.ITEM); + assertEquals(1, clientSideItems.size()); + + //grab player entity + Entity clientCreature = clientSideCreatures.iterator().next(); + Entity clientKatana = TestEngineUtils.getClientEquivalent(katana); + Globals.playerEntity = clientCreature; + + //attempt to store + InventoryUtils.clientAttemptStoreItem(clientCreature, clientKatana); + + //propagate to client + TestEngineUtils.simulateFrames(2); + + //verify we still have everything + clientSideCreatures = Globals.clientSceneWrapper.getScene().getEntitiesWithTag(EntityTags.CREATURE); + assertEquals(1, clientSideCreatures.size()); + clientSideItems = Globals.clientSceneWrapper.getScene().getEntitiesWithTag(EntityTags.ITEM); + assertEquals(1, clientSideItems.size()); + + //grab the item in particular + Entity child = clientSideItems.iterator().next(); + + // + //verify was created properly + assertTrue(ItemUtils.isItem(child)); + assertTrue(ItemUtils.isWeapon(child)); + assertNotNull(ItemUtils.getContainingParent(child)); + + // + //verify the item is stored in the inventory properly + UnrelationalInventoryState naturalInventory = InventoryUtils.getNaturalInventory(clientCreature); + assertEquals(naturalInventory.getItems().size(),1); + } + + /** + * Make sure clientAttemptEjectItem performs its intended function + */ + @IntegrationTest + public void test_clientAttemptEjectItem(){ + //warm up engine + TestEngineUtils.simulateFrames(1); + + //spawn entities + TestEngineUtils.spawnPlayerEntity(); + Entity katana = ItemUtils.serverSpawnBasicItem(Globals.realmManager.first(), new Vector3d(0,0,0), "Katana2H"); + + //wait for entities to propagate to client + TestEngineUtils.simulateFrames(1); + + //verify the client got the extra entities + Set clientSideCreatures = Globals.clientSceneWrapper.getScene().getEntitiesWithTag(EntityTags.CREATURE); + assertEquals(1, clientSideCreatures.size()); + Set clientSideItems = Globals.clientSceneWrapper.getScene().getEntitiesWithTag(EntityTags.ITEM); + assertEquals(1, clientSideItems.size()); + + //grab player entity + Entity clientCreature = clientSideCreatures.iterator().next(); + Entity clientKatana = TestEngineUtils.getClientEquivalent(katana); + Globals.playerEntity = clientCreature; + + //attempt to store + InventoryUtils.clientAttemptStoreItem(clientCreature, clientKatana); + + //allow time for client->server->client communication + TestEngineUtils.simulateFrames(2); + + //attempt to eject + clientSideItems = Globals.clientSceneWrapper.getScene().getEntitiesWithTag(EntityTags.ITEM); + clientKatana = clientSideItems.iterator().next(); + InventoryUtils.clientAttemptEjectItem(clientCreature, clientKatana); + + //allow time for client->server->client communication + TestEngineUtils.simulateFrames(2); + + //verify we still have everything + clientSideCreatures = Globals.clientSceneWrapper.getScene().getEntitiesWithTag(EntityTags.CREATURE); + assertEquals(1, clientSideCreatures.size()); + clientSideItems = Globals.clientSceneWrapper.getScene().getEntitiesWithTag(EntityTags.ITEM); + assertEquals(1, clientSideItems.size()); + + //grab the item in particular + Entity child = clientSideItems.iterator().next(); + + // + //verify was created properly + assertTrue(ItemUtils.isItem(child)); + assertTrue(ItemUtils.isWeapon(child)); + assertNull(ItemUtils.getContainingParent(child)); + + // + //verify the item is stored in the inventory properly + UnrelationalInventoryState naturalInventory = InventoryUtils.getNaturalInventory(clientCreature); + assertEquals(naturalInventory.getItems().size(),0); + } + +} diff --git a/src/test/java/electrosphere/test/testutils/TestEngineUtils.java b/src/test/java/electrosphere/test/testutils/TestEngineUtils.java index 07a6e3a8..a91a5442 100644 --- a/src/test/java/electrosphere/test/testutils/TestEngineUtils.java +++ b/src/test/java/electrosphere/test/testutils/TestEngineUtils.java @@ -12,6 +12,9 @@ import electrosphere.engine.Globals; import electrosphere.engine.Main; import electrosphere.engine.loadingthreads.LoadingThread; import electrosphere.entity.Entity; +import electrosphere.entity.types.creature.CreatureTemplate; +import electrosphere.net.server.ServerConnectionHandler; +import electrosphere.server.character.PlayerCharacterCreation; /** * Utils for testing the engine @@ -131,4 +134,15 @@ public class TestEngineUtils { } } + /** + * Spawns an entity for the player + * @return The entity + */ + public static void spawnPlayerEntity(){ + CreatureTemplate creatureTemplate = CreatureTemplate.createDefault("human"); + ServerConnectionHandler serverConnection = Globals.server.getFirstConnection(); + serverConnection.setCreatureTemplate(creatureTemplate); + PlayerCharacterCreation.spawnPlayerCharacter(serverConnection); + } + }