/*
* Copyright 2013 MovingBlocks
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.terasology.world;
import com.google.common.base.Objects;
import com.google.common.collect.Lists;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.terasology.TerasologyTestingEnvironment;
import org.terasology.assets.ResourceUrn;
import org.terasology.assets.management.AssetManager;
import org.terasology.engine.GameThread;
import org.terasology.entitySystem.Component;
import org.terasology.entitySystem.entity.EntityRef;
import org.terasology.entitySystem.entity.internal.EngineEntityManager;
import org.terasology.entitySystem.entity.lifecycleEvents.BeforeDeactivateComponent;
import org.terasology.entitySystem.entity.lifecycleEvents.BeforeRemoveComponent;
import org.terasology.entitySystem.entity.lifecycleEvents.OnActivatedComponent;
import org.terasology.entitySystem.entity.lifecycleEvents.OnAddedComponent;
import org.terasology.entitySystem.entity.lifecycleEvents.OnChangedComponent;
import org.terasology.entitySystem.event.Event;
import org.terasology.entitySystem.event.ReceiveEvent;
import org.terasology.entitySystem.event.internal.EventReceiver;
import org.terasology.entitySystem.event.internal.EventSystem;
import org.terasology.entitySystem.prefab.Prefab;
import org.terasology.entitySystem.prefab.PrefabData;
import org.terasology.entitySystem.stubs.ForceBlockActiveComponent;
import org.terasology.entitySystem.stubs.IntegerComponent;
import org.terasology.entitySystem.stubs.RetainedOnBlockChangeComponent;
import org.terasology.entitySystem.stubs.StringComponent;
import org.terasology.entitySystem.systems.BaseComponentSystem;
import org.terasology.math.geom.Vector3i;
import org.terasology.network.NetworkComponent;
import org.terasology.testUtil.WorldProviderCoreStub;
import org.terasology.world.block.Block;
import org.terasology.world.block.BlockComponent;
import org.terasology.world.block.BlockManager;
import org.terasology.world.block.family.BlockFamily;
import org.terasology.world.block.family.HorizontalBlockFamilyFactory;
import org.terasology.world.block.family.SymmetricBlockFamilyFactory;
import org.terasology.world.block.loader.BlockFamilyDefinition;
import org.terasology.world.block.loader.BlockFamilyDefinitionData;
import org.terasology.world.internal.EntityAwareWorldProvider;
import java.util.Iterator;
import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
/**
*/
public class EntityAwareWorldProviderTest extends TerasologyTestingEnvironment {
private EntityAwareWorldProvider worldProvider;
private WorldProviderCoreStub worldStub;
private EngineEntityManager entityManager;
private Prefab prefabWithString;
private Block airBlock;
private Block plainBlock;
private Block blockWithString;
private Block blockWithDifferentString;
private Block blockWithRetainedComponent;
private Block keepActiveBlock;
private Block blockInFamilyOne;
private Block blockInFamilyTwo;
@Before
public void setup() throws Exception {
super.setup();
GameThread.setToCurrentThread();
this.entityManager = context.get(EngineEntityManager.class);
AssetManager assetManager = context.get(AssetManager.class);
BlockManager blockManager = context.get(BlockManager.class);
airBlock = blockManager.getBlock(BlockManager.AIR_ID);
worldStub = new WorldProviderCoreStub(airBlock, null);
worldProvider = new EntityAwareWorldProvider(worldStub, context);
plainBlock = createBlock("test:plainblock", assetManager, blockManager);
prefabWithString = createPrefabWithString("test:prefabWithString", "Test", assetManager);
blockWithString = createBlockWithPrefab("test:blockWithString", prefabWithString, false, assetManager, blockManager);
keepActiveBlock = createBlockWithPrefab("test:keepActiveBlock", prefabWithString, true, assetManager, blockManager);
Prefab prefabWithDifferentString = createPrefabWithString("test:prefabWithDifferentString", "Test2", assetManager);
blockWithDifferentString = createBlockWithPrefab("test:prefabWithDifferentString", prefabWithDifferentString, false, assetManager, blockManager);
BlockFamily blockFamily = createBlockFamily("test:blockFamily", prefabWithString, assetManager, blockManager);
Iterator<Block> iterator = blockFamily.getBlocks().iterator();
blockInFamilyOne = iterator.next();
blockInFamilyTwo = iterator.next();
PrefabData retainedPrefabData = new PrefabData();
retainedPrefabData.addComponent(new RetainedOnBlockChangeComponent(3));
Prefab retainedPrefab = assetManager.loadAsset(new ResourceUrn("test:retainedPrefab"), retainedPrefabData, Prefab.class);
blockWithRetainedComponent = createBlockWithPrefab("test:blockWithRetainedComponent", retainedPrefab, false, assetManager, blockManager);
worldProvider.initialise();
}
private Block createBlockWithPrefab(String urn, Prefab prefab, boolean keepActive, AssetManager assetManager, BlockManager blockManager) {
BlockFamilyDefinitionData data = new BlockFamilyDefinitionData();
data.setFamilyFactory(new SymmetricBlockFamilyFactory());
data.getBaseSection().getEntity().setPrefab(prefab);
data.getBaseSection().getEntity().setKeepActive(keepActive);
assetManager.loadAsset(new ResourceUrn(urn), data, BlockFamilyDefinition.class);
return blockManager.getBlock(urn);
}
private Prefab createPrefabWithString(String urn, String text, AssetManager assetManager) {
PrefabData prefabData = new PrefabData();
prefabData.addComponent(new StringComponent(text));
return assetManager.loadAsset(new ResourceUrn(urn), prefabData, Prefab.class);
}
private Block createBlock(String urn, AssetManager assetManager, BlockManager blockManager) {
BlockFamilyDefinitionData data = new BlockFamilyDefinitionData();
data.setFamilyFactory(new SymmetricBlockFamilyFactory());
assetManager.loadAsset(new ResourceUrn(urn), data, BlockFamilyDefinition.class);
return blockManager.getBlock(urn);
}
private BlockFamily createBlockFamily(String urn, Prefab prefab, AssetManager assetManager, BlockManager blockManager) {
BlockFamilyDefinitionData data = new BlockFamilyDefinitionData();
data.setFamilyFactory(new HorizontalBlockFamilyFactory());
data.getBaseSection().getEntity().setKeepActive(true);
data.getBaseSection().getEntity().setPrefab(prefab);
assetManager.loadAsset(new ResourceUrn(urn), data, BlockFamilyDefinition.class);
return blockManager.getBlockFamily(urn);
}
@Test
public void testGetTemporaryBlockSendsNoEvent() {
BlockEventChecker checker = new BlockEventChecker();
entityManager.getEventSystem().registerEventHandler(checker);
EntityRef blockEntity = worldProvider.getBlockEntityAt(new Vector3i(0, 0, 0));
assertTrue(blockEntity.exists());
assertFalse(checker.addedReceived);
assertFalse(checker.activateReceived);
assertFalse(checker.deactivateReceived);
assertFalse(checker.removedReceived);
}
@Test
public void testTemporaryCleanedUpWithNoEvent() {
BlockEventChecker checker = new BlockEventChecker();
entityManager.getEventSystem().registerEventHandler(checker);
EntityRef blockEntity = worldProvider.getBlockEntityAt(new Vector3i(0, 0, 0));
worldProvider.update(1.0f);
assertFalse(blockEntity.exists());
assertFalse(checker.addedReceived);
assertFalse(checker.activateReceived);
assertFalse(checker.deactivateReceived);
assertFalse(checker.removedReceived);
}
@Test
public void testActiveBlockNotCleanedUp() {
Block testBlock = new Block();
testBlock.setKeepActive(true);
// BlockFamily blockFamily = new SymmetricFamily(new BlockUri("test:keepActive"), testBlock);
//blockManager.addBlockFamily(blockFamily, true);
worldStub.setBlock(Vector3i.zero(), testBlock);
BlockEventChecker checker = new BlockEventChecker();
entityManager.getEventSystem().registerEventHandler(checker);
EntityRef blockEntity = worldProvider.getBlockEntityAt(new Vector3i(0, 0, 0));
worldProvider.update(1.0f);
assertTrue(blockEntity.exists());
assertTrue(blockEntity.isActive());
assertTrue(checker.addedReceived);
assertTrue(checker.activateReceived);
}
@Ignore("Failing due to #2625. TODO: fix to match new behaviour")
@Test
public void testComponentsAddedAndActivatedWhenBlockChanged() {
LifecycleEventChecker checker = new LifecycleEventChecker(entityManager.getEventSystem(), StringComponent.class);
worldProvider.setBlock(Vector3i.zero(), blockWithString);
EntityRef blockEntity = worldProvider.getBlockEntityAt(Vector3i.zero());
assertTrue(blockEntity.exists());
assertEquals(Lists.newArrayList(new EventInfo(OnAddedComponent.newInstance(), blockEntity), new EventInfo(OnActivatedComponent.newInstance(), blockEntity)),
checker.receivedEvents);
}
@Ignore("Failing due to #2625. TODO: fix to match new behaviour")
@Test
public void testComponentsDeactivatedAndRemovedWhenBlockChanged() {
worldProvider.setBlock(Vector3i.zero(), blockWithString);
LifecycleEventChecker checker = new LifecycleEventChecker(entityManager.getEventSystem(), StringComponent.class);
worldProvider.setBlock(Vector3i.zero(), airBlock);
EntityRef blockEntity = worldProvider.getBlockEntityAt(new Vector3i(0, 0, 0));
assertTrue(blockEntity.exists());
assertEquals(Lists.newArrayList(new EventInfo(BeforeDeactivateComponent.newInstance(), blockEntity), new EventInfo(BeforeRemoveComponent.newInstance(), blockEntity)),
checker.receivedEvents);
}
@Ignore("Failing due to #2625. TODO: fix to match new behaviour")
@Test
public void testComponentsUpdatedWhenBlockChanged() {
worldProvider.setBlock(Vector3i.zero(), blockWithString);
LifecycleEventChecker checker = new LifecycleEventChecker(entityManager.getEventSystem(), StringComponent.class);
worldProvider.setBlock(Vector3i.zero(), blockWithDifferentString);
EntityRef blockEntity = worldProvider.getBlockEntityAt(new Vector3i(0, 0, 0));
assertTrue(blockEntity.exists());
assertEquals(Lists.newArrayList(new EventInfo(OnChangedComponent.newInstance(), blockEntity)), checker.receivedEvents);
}
@Test
public void testPrefabUpdatedWhenBlockChanged() {
worldProvider.setBlock(Vector3i.zero(), blockWithString);
assertEquals(blockWithString.getPrefab().get().getName(), worldProvider.getBlockEntityAt(new Vector3i(0, 0, 0)).getParentPrefab().getName());
worldProvider.setBlock(Vector3i.zero(), blockWithDifferentString);
assertEquals(blockWithDifferentString.getPrefab().get().getName(), worldProvider.getBlockEntityAt(new Vector3i(0, 0, 0)).getParentPrefab().getName());
}
@Test
public void testEntityNotRemovedIfForceBlockActiveComponentAdded() {
EntityRef blockEntity = worldProvider.getBlockEntityAt(new Vector3i(0, 0, 0));
blockEntity.addComponent(new ForceBlockActiveComponent());
worldProvider.update(1.0f);
assertTrue(blockEntity.exists());
assertTrue(blockEntity.isActive());
}
@Ignore("Failing due to #2625. TODO: fix to match new behaviour")
@Test
public void testEntityCeasesToBeTemporaryIfBlockChangedToKeepActive() {
worldProvider.setBlock(Vector3i.zero(), keepActiveBlock);
worldProvider.update(1.0f);
LifecycleEventChecker checker = new LifecycleEventChecker(entityManager.getEventSystem(), StringComponent.class);
worldProvider.getBlockEntityAt(new Vector3i(0, 0, 0));
assertTrue(checker.receivedEvents.isEmpty());
}
@Test
public void testEntityBecomesTemporaryWhenChangedFromAKeepActiveBlock() {
worldProvider.setBlock(Vector3i.zero(), keepActiveBlock);
EntityRef blockEntity = worldProvider.getBlockEntityAt(new Vector3i(0, 0, 0));
worldProvider.setBlock(Vector3i.zero(), airBlock);
worldProvider.update(1.0f);
assertFalse(blockEntity.isActive());
}
@Test
public void testEntityBecomesTemporaryIfForceBlockActiveComponentRemoved() {
EntityRef blockEntity = worldProvider.getBlockEntityAt(new Vector3i(0, 0, 0));
blockEntity.addComponent(new ForceBlockActiveComponent());
worldProvider.update(1.0f);
blockEntity.removeComponent(ForceBlockActiveComponent.class);
worldProvider.update(1.0f);
assertFalse(blockEntity.exists());
assertFalse(blockEntity.isActive());
}
@Test
public void testEntityExtraComponentsRemovedBeforeCleanUp() {
EntityRef entity = worldProvider.getBlockEntityAt(new Vector3i(0, 0, 0));
entity.addComponent(new StringComponent("test"));
LifecycleEventChecker checker = new LifecycleEventChecker(entityManager.getEventSystem(), StringComponent.class);
worldProvider.update(1.0f);
assertEquals(Lists.newArrayList(new EventInfo(BeforeDeactivateComponent.newInstance(), entity), new EventInfo(BeforeRemoveComponent.newInstance(), entity)),
checker.receivedEvents);
}
@Test
public void testEntityExtraComponentsRemovedBeforeCleanUpForBlocksWithPrefabs() {
worldStub.setBlock(Vector3i.zero(), blockWithString);
EntityRef entity = worldProvider.getBlockEntityAt(new Vector3i(0, 0, 0));
entity.addComponent(new IntegerComponent(1));
LifecycleEventChecker checker = new LifecycleEventChecker(entityManager.getEventSystem(), IntegerComponent.class);
worldProvider.update(1.0f);
assertEquals(Lists.newArrayList(new EventInfo(BeforeDeactivateComponent.newInstance(), entity), new EventInfo(BeforeRemoveComponent.newInstance(), entity)),
checker.receivedEvents);
}
@Test
public void testEntityMissingComponentsAddedBeforeCleanUp() {
worldStub.setBlock(Vector3i.zero(), blockWithString);
EntityRef entity = worldProvider.getBlockEntityAt(new Vector3i(0, 0, 0));
entity.removeComponent(StringComponent.class);
LifecycleEventChecker checker = new LifecycleEventChecker(entityManager.getEventSystem(), StringComponent.class);
worldProvider.update(1.0f);
assertEquals(Lists.newArrayList(new EventInfo(OnAddedComponent.newInstance(), entity), new EventInfo(OnActivatedComponent.newInstance(), entity)),
checker.receivedEvents);
}
@Test
public void testChangedComponentsRevertedBeforeCleanUp() {
worldStub.setBlock(Vector3i.zero(), blockWithString);
EntityRef entity = worldProvider.getBlockEntityAt(new Vector3i(0, 0, 0));
StringComponent comp = entity.getComponent(StringComponent.class);
comp.value = "Moo";
entity.saveComponent(comp);
LifecycleEventChecker checker = new LifecycleEventChecker(entityManager.getEventSystem(), StringComponent.class);
worldProvider.update(1.0f);
assertEquals(Lists.newArrayList(new EventInfo(OnChangedComponent.newInstance(), entity)), checker.receivedEvents);
}
@Test
public void testAllComponentsNotMarkedAsRetainedRemovedOnBlockChange() {
worldStub.setBlock(Vector3i.zero(), blockWithString);
EntityRef entity = worldProvider.getBlockEntityAt(new Vector3i(0, 0, 0));
entity.addComponent(new ForceBlockActiveComponent());
entity.addComponent(new RetainedOnBlockChangeComponent(2));
worldProvider.setBlock(Vector3i.zero(), airBlock);
assertTrue(entity.hasComponent(RetainedOnBlockChangeComponent.class));
assertFalse(entity.hasComponent(ForceBlockActiveComponent.class));
}
@Test
public void testRetainedComponentsNotAltered() {
EntityRef entity = worldProvider.getBlockEntityAt(new Vector3i(0, 0, 0));
entity.addComponent(new RetainedOnBlockChangeComponent(2));
worldProvider.setBlock(Vector3i.zero(), blockWithRetainedComponent);
assertEquals(2, entity.getComponent(RetainedOnBlockChangeComponent.class).value);
}
@Test
public void testMetworkComponentAddedWhenChangedToNonTemporary() {
LifecycleEventChecker checker = new LifecycleEventChecker(entityManager.getEventSystem(), NetworkComponent.class);
EntityRef entity = worldProvider.getBlockEntityAt(new Vector3i(0, 0, 0));
entity.addComponent(new RetainedOnBlockChangeComponent(2));
assertEquals(Lists.newArrayList(new EventInfo(OnAddedComponent.newInstance(), entity), new EventInfo(OnActivatedComponent.newInstance(), entity)),
checker.receivedEvents);
assertTrue(entity.hasComponent(NetworkComponent.class));
}
@Test
public void testNetworkComponentRemovedWhenTemporaryCleanedUp() {
EntityRef entity = worldProvider.getBlockEntityAt(new Vector3i(0, 0, 0));
entity.addComponent(new RetainedOnBlockChangeComponent(2));
LifecycleEventChecker checker = new LifecycleEventChecker(entityManager.getEventSystem(), NetworkComponent.class);
entity.removeComponent(RetainedOnBlockChangeComponent.class);
worldProvider.update(1.0f);
assertEquals(Lists.newArrayList(new EventInfo(BeforeDeactivateComponent.newInstance(), entity), new EventInfo(BeforeRemoveComponent.newInstance(), entity)),
checker.receivedEvents);
}
@Test
public void testComponentsNotAlteredIfBlockInSameFamily() {
worldProvider.setBlock(Vector3i.zero(), blockInFamilyOne);
EntityRef entity = worldProvider.getBlockEntityAt(Vector3i.zero());
entity.addComponent(new IntegerComponent());
worldProvider.setBlock(Vector3i.zero(), blockInFamilyTwo);
assertNotNull(entity.getComponent(IntegerComponent.class));
}
@Test
public void testComponentsAlteredIfBlockInSameFamilyWhenForced() {
worldProvider.setBlock(Vector3i.zero(), blockInFamilyOne);
EntityRef entity = worldProvider.getBlockEntityAt(Vector3i.zero());
entity.addComponent(new IntegerComponent());
worldProvider.setBlockForceUpdateEntity(Vector3i.zero(), blockInFamilyTwo);
assertNull(entity.getComponent(IntegerComponent.class));
}
@Test
public void testComponentUntouchedIfRetainRequested() {
worldProvider.setBlock(Vector3i.zero(), blockInFamilyOne);
EntityRef entity = worldProvider.getBlockEntityAt(Vector3i.zero());
entity.addComponent(new IntegerComponent());
worldProvider.setBlockRetainComponent(Vector3i.zero(), blockWithString, IntegerComponent.class);
assertNotNull(entity.getComponent(IntegerComponent.class));
}
@Ignore("Failing due to #2625. TODO: fix to match new behaviour")
@Test
public void testBlockEntityPrefabCorrectlyAlteredOnChangeToDifferentPrefab() {
worldProvider.setBlock(Vector3i.zero(), blockWithString);
EntityRef entity = worldProvider.getBlockEntityAt(Vector3i.zero());
worldProvider.setBlock(Vector3i.zero(), blockWithDifferentString);
assertEquals(blockWithDifferentString.getPrefab().get().getUrn(), entity.getParentPrefab().getUrn());
}
@Ignore("Failing due to #2625. TODO: fix to match new behaviour")
@Test
public void testBlockEntityPrefabCorrectlyRemovedOnChangeToBlockWithNoPrefab() {
worldProvider.setBlock(Vector3i.zero(), blockWithString);
EntityRef entity = worldProvider.getBlockEntityAt(Vector3i.zero());
worldProvider.setBlock(Vector3i.zero(), plainBlock);
assertEquals(null, entity.getParentPrefab());
}
@Ignore("Failing due to #2625. TODO: fix to match new behaviour")
@Test
public void testBlockEntityPrefabCorrectlyAddedOnChangeToBlockWithPrefab() {
worldProvider.setBlock(Vector3i.zero(), plainBlock);
EntityRef entity = worldProvider.getBlockEntityAt(Vector3i.zero());
worldProvider.setBlock(Vector3i.zero(), blockWithString);
assertEquals(blockWithString.getPrefab().get().getUrn().toString(), entity.getParentPrefab().getUrn().toString());
}
public static class LifecycleEventChecker {
public List<EventInfo> receivedEvents = Lists.newArrayList();
public LifecycleEventChecker(EventSystem eventSystem, Class<? extends Component> forComponent) {
eventSystem.registerEventReceiver(new LifecycleEventReceiver<>(), OnAddedComponent.class, forComponent);
eventSystem.registerEventReceiver(new LifecycleEventReceiver<>(), OnActivatedComponent.class, forComponent);
eventSystem.registerEventReceiver(new LifecycleEventReceiver<>(), OnChangedComponent.class, forComponent);
eventSystem.registerEventReceiver(new LifecycleEventReceiver<>(), BeforeDeactivateComponent.class, forComponent);
eventSystem.registerEventReceiver(new LifecycleEventReceiver<>(), BeforeRemoveComponent.class, forComponent);
}
private class LifecycleEventReceiver<T extends Event> implements EventReceiver<T> {
@Override
public void onEvent(T event, EntityRef entity) {
receivedEvents.add(new EventInfo(event, entity));
}
}
}
public static class BlockEventChecker extends BaseComponentSystem {
public boolean addedReceived;
public boolean activateReceived;
public boolean deactivateReceived;
public boolean removedReceived;
@ReceiveEvent(components = BlockComponent.class)
public void onAdded(OnAddedComponent event, EntityRef entity) {
addedReceived = true;
}
@ReceiveEvent(components = BlockComponent.class)
public void onActivated(OnActivatedComponent event, EntityRef entity) {
activateReceived = true;
}
@ReceiveEvent(components = BlockComponent.class)
public void onDeactivated(BeforeDeactivateComponent event, EntityRef entity) {
deactivateReceived = true;
}
@ReceiveEvent(components = BlockComponent.class)
public void onRemoved(BeforeRemoveComponent event, EntityRef entity) {
removedReceived = true;
}
}
public static class EventInfo {
public EntityRef targetEntity;
public Event event;
public EventInfo(Event event, EntityRef target) {
this.event = event;
this.targetEntity = target;
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj instanceof EventInfo) {
EventInfo other = (EventInfo) obj;
return Objects.equal(other.targetEntity, targetEntity) && Objects.equal(other.event, event);
}
return false;
}
@Override
public int hashCode() {
return Objects.hashCode(targetEntity, event);
}
}
}