mirror of
https://github.com/worldforge/cyphesis
synced 2026-08-13 12:26:04 -04:00
635 lines
27 KiB
C++
635 lines
27 KiB
C++
/*
|
|
Copyright (C) 2020 Erik Ogenvik
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*/
|
|
|
|
#include "../TestBaseWithContext.h"
|
|
#include "../DatabaseNull.h"
|
|
#include "../TestWorld.h"
|
|
#include "common/Monitors.h"
|
|
#include "common/Inheritance.h"
|
|
#include "rules/simulation/World.h"
|
|
#include "../stubs/common/stubcustom.h"
|
|
#include "../NullEntityCreator.h"
|
|
#include "../TestWorldRouter.h"
|
|
|
|
#include <Atlas/Objects/Operation.h>
|
|
#include <Atlas/Objects/Entity.h>
|
|
#include <rules/simulation/CorePropertyManager.h>
|
|
|
|
#include <memory>
|
|
#include <utility>
|
|
#include <rules/simulation/PhysicalDomain.h>
|
|
#include <rules/simulation/VoidDomain.h>
|
|
#include <rules/simulation/WorldRouter.h>
|
|
#include <common/operations/Tick.h>
|
|
#include <wfmath/atlasconv.h>
|
|
#include <rules/simulation/AdminProperty.h>
|
|
#include <rules/simulation/ContainerAccessProperty.h>
|
|
#include <rules/simulation/ContainersActiveProperty.h>
|
|
|
|
using Atlas::Objects::Operation::Set;
|
|
using Atlas::Objects::Operation::Wield;
|
|
using Atlas::Objects::Entity::Anonymous;
|
|
using Atlas::Message::MapType;
|
|
using Atlas::Message::ListType;
|
|
|
|
struct TestContext
|
|
{
|
|
Atlas::Objects::Factories factories;
|
|
DatabaseNull database;
|
|
NullEntityCreator entityCreator;
|
|
Ref<World> world;
|
|
Inheritance inheritance;
|
|
TestWorldRouter testWorld;
|
|
CorePropertyManager propertyManager;
|
|
|
|
TestContext()
|
|
: world(new World()), inheritance(factories), testWorld(world, entityCreator), propertyManager(inheritance)
|
|
{
|
|
}
|
|
|
|
~TestContext()
|
|
{
|
|
world->destroy();
|
|
}
|
|
};
|
|
|
|
namespace {
|
|
std::vector<OpQueEntry<LocatedEntity>> collectQueue(std::priority_queue<OpQueEntry<LocatedEntity>, std::vector<OpQueEntry<LocatedEntity>>, std::greater<OpQueEntry<LocatedEntity>>>& queue)
|
|
{
|
|
std::vector<OpQueEntry<LocatedEntity>> list;
|
|
list.reserve(queue.size());
|
|
while (!queue.empty()) {
|
|
list.emplace_back(queue.top());
|
|
queue.pop();
|
|
}
|
|
return list;
|
|
}
|
|
}
|
|
|
|
OpVector resIgnored;
|
|
|
|
struct Tested : public Cyphesis::TestBaseWithContext<TestContext>
|
|
{
|
|
Tested()
|
|
{
|
|
ADD_TEST(test_handleContainers);
|
|
ADD_TEST(test_sendAppearDisappearWithPrivateAndProtected);
|
|
ADD_TEST(test_sendAppearDisappear);
|
|
}
|
|
|
|
static void sendSetOp(const Ref<Thing>& entity, const std::string& propertyName, Atlas::Message::Element value)
|
|
{
|
|
Set set;
|
|
|
|
Anonymous arg1;
|
|
arg1->setAttr(propertyName, std::move(value));
|
|
set->setArgs1(arg1);
|
|
|
|
entity->operation(set, resIgnored);
|
|
}
|
|
|
|
void test_handleContainers(TestContext& context)
|
|
{
|
|
|
|
auto moveFn = [](const Ref<Thing>& thing, WFMath::Point<3> pos) -> OpVector {
|
|
OpVector res;
|
|
Atlas::Objects::Operation::Move move;
|
|
|
|
Anonymous arg1;
|
|
arg1->setId(thing->getId());
|
|
Atlas::Message::Element posElement = pos.toAtlas();
|
|
arg1->setPosAsList(posElement.List());
|
|
move->setArgs1(arg1);
|
|
thing->MoveOperation(move, res);
|
|
return res;
|
|
};
|
|
|
|
/**
|
|
* Check that entities only are allowed to look into containers if they are allowed by the __container_access
|
|
*
|
|
* All entities are placed at origo
|
|
* Hierarchy looks like this:
|
|
* T1 has a physical domain
|
|
* T2, T5 and T7 has a container domain
|
|
* T3 has sight
|
|
*
|
|
* T1#
|
|
* T2* T3
|
|
* T4 T5* T7*
|
|
* T6 T8
|
|
*/
|
|
{
|
|
auto& opsHandler = context.testWorld.getOperationsHandler();
|
|
auto& queue = opsHandler.getQueue();
|
|
|
|
WFMath::AxisBox<3> bbox(WFMath::Point<3>(-1, -1, -1), WFMath::Point<3>(1, 1, 1));
|
|
Ref<Thing> t1 = new Thing(1);
|
|
t1->setAttrValue("domain", "physical");
|
|
t1->m_location.m_pos = WFMath::Point<3>::ZERO();
|
|
context.testWorld.addEntity(t1, context.world);
|
|
Ref<Thing> t2 = new Thing(2);
|
|
t2->m_location.m_pos = WFMath::Point<3>::ZERO();
|
|
t2->m_location.setBBox(bbox);
|
|
t2->setAttrValue("domain", "container");
|
|
context.testWorld.addEntity(t2, t1);
|
|
Ref<Thing> t3 = new Thing(3);
|
|
t3->m_location.m_pos = WFMath::Point<3>::ZERO();
|
|
t3->m_location.setBBox(bbox);
|
|
t3->setAttrValue("perception_sight", 1);
|
|
context.testWorld.addEntity(t3, t1);
|
|
Ref<Thing> t4 = new Thing(4);
|
|
t4->m_location.m_pos = WFMath::Point<3>::ZERO();
|
|
t4->m_location.setBBox(bbox);
|
|
context.testWorld.addEntity(t4, t2);
|
|
Ref<Thing> t5 = new Thing(5);
|
|
t5->m_location.m_pos = WFMath::Point<3>::ZERO();
|
|
t5->m_location.setBBox(bbox);
|
|
t5->setAttrValue("domain", "container");
|
|
context.testWorld.addEntity(t5, t2);
|
|
Ref<Thing> t6 = new Thing(6);
|
|
t6->m_location.m_pos = WFMath::Point<3>::ZERO();
|
|
t6->m_location.setBBox(bbox);
|
|
context.testWorld.addEntity(t6, t5);
|
|
Ref<Thing> t7 = new Thing(7);
|
|
t7->m_location.m_pos = WFMath::Point<3>::ZERO();
|
|
t7->m_location.setBBox(bbox);
|
|
t7->setAttrValue("domain", "container");
|
|
context.testWorld.addEntity(t7, t3);
|
|
Ref<Thing> t8 = new Thing(8);
|
|
t8->m_location.m_pos = WFMath::Point<3>::ZERO();
|
|
t8->m_location.setBBox(bbox);
|
|
context.testWorld.addEntity(t8, t7);
|
|
|
|
opsHandler.clearQueues();
|
|
|
|
ASSERT_TRUE(t2->isVisibleForOtherEntity(t3.get()))
|
|
ASSERT_TRUE(t3->canReach(EntityLocation{t2}))
|
|
ASSERT_FALSE(t3->canReach(EntityLocation{t4}))
|
|
ASSERT_FALSE(t3->canReach(EntityLocation{t5}))
|
|
ASSERT_FALSE(t3->canReach(EntityLocation{t6}))
|
|
//Add t3 as container observer to t2, which allows it to view its content (t4 and t5)
|
|
t2->setAttrValue(ContainerAccessProperty::property_name, Atlas::Message::ListType{t3->getId()});
|
|
ASSERT_TRUE(t3->canReach(EntityLocation{t2}))
|
|
ASSERT_TRUE(t3->canReach(EntityLocation{t4}))
|
|
ASSERT_TRUE(t3->canReach(EntityLocation{t5}))
|
|
ASSERT_FALSE(t3->canReach(EntityLocation{t6}))
|
|
ASSERT_TRUE(t3->getPropertyClassFixed<ContainersActiveProperty>()->hasContainer(t2->getId()))
|
|
ASSERT_EQUAL(2u, queue.size()) //One Update and one Appearance
|
|
ASSERT_EQUAL(Atlas::Objects::Operation::UPDATE_NO, queue.top()->getClassNo())
|
|
queue.pop();
|
|
ASSERT_EQUAL(Atlas::Objects::Operation::APPEARANCE_NO, queue.top()->getClassNo())
|
|
ASSERT_EQUAL(t3->getId(), queue.top()->getTo())
|
|
ASSERT_EQUAL(2u, queue.top()->getArgs().size())
|
|
|
|
//Add t3 as container observer to t5, which allows it to view its content (t6)
|
|
opsHandler.clearQueues();
|
|
t5->setAttrValue(ContainerAccessProperty::property_name, Atlas::Message::ListType{t3->getId()});
|
|
ASSERT_TRUE(t3->getPropertyClassFixed<ContainersActiveProperty>()->hasContainer(t5->getId()))
|
|
ASSERT_TRUE(t3->canReach(EntityLocation{t6}))
|
|
ASSERT_EQUAL(2u, queue.size())
|
|
queue.pop();
|
|
ASSERT_EQUAL(Atlas::Objects::Operation::APPEARANCE_NO, queue.top()->getClassNo())
|
|
ASSERT_EQUAL(t3->getId(), queue.top()->getTo())
|
|
ASSERT_EQUAL(1u, queue.top()->getArgs().size())
|
|
//Remove t3 as observer...
|
|
opsHandler.clearQueues();
|
|
t5->setAttrValue(ContainerAccessProperty::property_name, Atlas::Message::ListType{});
|
|
ASSERT_FALSE(t3->getPropertyClassFixed<ContainersActiveProperty>()->hasContainer(t5->getId()))
|
|
ASSERT_FALSE(t3->canReach(EntityLocation{t6}))
|
|
ASSERT_EQUAL(2u, queue.size())
|
|
queue.pop();
|
|
ASSERT_EQUAL(Atlas::Objects::Operation::DISAPPEARANCE_NO, queue.top()->getClassNo())
|
|
ASSERT_EQUAL(t3->getId(), queue.top()->getTo())
|
|
ASSERT_EQUAL(1u, queue.top()->getArgs().size())
|
|
//...and add it back.
|
|
t5->setAttrValue(ContainerAccessProperty::property_name, Atlas::Message::ListType{t3->getId()});
|
|
ASSERT_TRUE(t3->getPropertyClassFixed<ContainersActiveProperty>()->hasContainer(t5->getId()))
|
|
ASSERT_TRUE(t3->canReach(EntityLocation{t6}))
|
|
//Remove t3 as observer from t2, which should sever the connection to t5
|
|
opsHandler.clearQueues();
|
|
t2->setAttrValue(ContainerAccessProperty::property_name, Atlas::Message::ListType{});
|
|
ASSERT_FALSE(t3->getPropertyClassFixed<ContainersActiveProperty>()->hasContainer(t2->getId()))
|
|
//Should still be able to reach t2, even if we can't reach into it.
|
|
ASSERT_TRUE(t3->canReach(EntityLocation{t2}))
|
|
ASSERT_TRUE(t2->isVisibleForOtherEntity(t3.get()));
|
|
ASSERT_FALSE(t3->canReach(EntityLocation{t5}))
|
|
ASSERT_FALSE(t5->isVisibleForOtherEntity(t3.get()));
|
|
ASSERT_FALSE(t3->canReach(EntityLocation{t6}))
|
|
ASSERT_FALSE(t6->isVisibleForOtherEntity(t3.get()));
|
|
ASSERT_EQUAL(2u, queue.size()) //Should really be 3, one from t2 and one from t5
|
|
queue.pop();
|
|
ASSERT_EQUAL(Atlas::Objects::Operation::DISAPPEARANCE_NO, queue.top()->getClassNo())
|
|
ASSERT_EQUAL(t3->getId(), queue.top()->getTo())
|
|
ASSERT_EQUAL(2u, queue.top()->getArgs().size()) //From both t4 and t5
|
|
|
|
//Add it back as observer
|
|
t2->setAttrValue(ContainerAccessProperty::property_name, Atlas::Message::ListType{t3->getId()});
|
|
ASSERT_TRUE(t3->canReach(EntityLocation{t5}))
|
|
|
|
opsHandler.clearQueues();
|
|
//Now move t3 away far enough that it can't reach t2 anymore.
|
|
moveFn(t3, {510, 0, 500});
|
|
ASSERT_FALSE(t3->canReach(EntityLocation{t2}))
|
|
ASSERT_FALSE(t3->canReach(EntityLocation{t5}))
|
|
|
|
ASSERT_EQUAL(3u, queue.size()) //Should really be 3, one Sight, and one Appearance from t2 and one Appearance from t5
|
|
queue.pop();
|
|
queue.pop();
|
|
ASSERT_EQUAL(Atlas::Objects::Operation::DISAPPEARANCE_NO, queue.top()->getClassNo())
|
|
ASSERT_EQUAL(t3->getId(), queue.top()->getTo())
|
|
ASSERT_EQUAL(2u, queue.top()->getArgs().size()) //From both t4 and t5
|
|
|
|
//Move t3 closer again
|
|
moveFn(t3, {0, 0, 0});
|
|
ASSERT_TRUE(t3->canReach(EntityLocation{t2}))
|
|
//t5 should not be reachable since we severed the "reach" connection when moving away
|
|
ASSERT_FALSE(t3->canReach(EntityLocation{t5}))
|
|
t2->setAttrValue(ContainerAccessProperty::property_name, Atlas::Message::ListType{t3->getId()});
|
|
ASSERT_TRUE(t3->canReach(EntityLocation{t5}))
|
|
|
|
//And then once again move it away
|
|
moveFn(t3, {510, 0, 500});
|
|
|
|
ASSERT_FALSE(t3->canReach(EntityLocation{t2}))
|
|
ASSERT_FALSE(t3->canReach(EntityLocation{t5}))
|
|
|
|
//Move it back, access the container, and then add a new entity and expect an Appearance op
|
|
moveFn(t3, {0, 0, 0});
|
|
t2->setAttrValue(ContainerAccessProperty::property_name, Atlas::Message::ListType{t3->getId()});
|
|
opsHandler.clearQueues();
|
|
Ref<Thing> t9 = new Thing(9);
|
|
t9->m_location.m_pos = WFMath::Point<3>::ZERO();
|
|
t9->m_location.setBBox(bbox);
|
|
context.testWorld.addEntity(t9, t2);
|
|
|
|
ASSERT_EQUAL(1u, queue.size());
|
|
ASSERT_EQUAL(Atlas::Objects::Operation::APPEARANCE_NO, queue.top()->getClassNo())
|
|
ASSERT_EQUAL(t3->getId(), queue.top()->getTo())
|
|
ASSERT_EQUAL(1u, queue.top()->getArgs().size())
|
|
ASSERT_EQUAL(t9->getId(), queue.top()->getArgs().front()->getId())
|
|
|
|
|
|
// //When we add t3 back as an observer to t2 the link to t6 should have been severed, and it should no longer be reachable
|
|
// t2->setAttrValue(ContainerAccessProperty::property_name, Atlas::Message::ListType{t3->getId()});
|
|
// ASSERT_TRUE(t3->canReach(EntityLocation{t5}))
|
|
// ASSERT_FALSE(t3->canReach(EntityLocation{t6}))
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
void test_sendAppearDisappearWithPrivateAndProtected(TestContext& context)
|
|
{
|
|
auto& opsHandler = context.testWorld.getOperationsHandler();
|
|
auto& queue = opsHandler.getQueue();
|
|
|
|
long counter = 1;
|
|
Ref<Thing> domainPhysical(new Thing(counter++));
|
|
context.testWorld.addEntity(domainPhysical, context.world);
|
|
domainPhysical->m_location.setBBox({{-512, -512, -512},
|
|
{512, 512, 512}});
|
|
domainPhysical->setDomain(std::make_unique<PhysicalDomain>(*domainPhysical));
|
|
|
|
// Clear ops queue
|
|
opsHandler.clearQueues();
|
|
|
|
// Make an observer, which we'll add to the physical domain
|
|
Ref<Thing> observer(new Thing(counter++));
|
|
observer->setAttrValue("mode", "fixed");
|
|
observer->setAttrValue("perception_sight", 1);
|
|
observer->m_location.m_pos = {0, 0, 0};
|
|
context.testWorld.addEntity(observer, domainPhysical);
|
|
|
|
// Make an admin observer, which we'll add to the physical domain
|
|
Ref<Thing> observerAdmin(new Thing(counter++));
|
|
observerAdmin->setAttrValue("mode", "fixed");
|
|
observerAdmin->setAttrValue("perception_sight", 1);
|
|
observerAdmin->setAttrValue(AdminProperty::property_name, 1);
|
|
observerAdmin->m_location.m_pos = {0, 0, 0};
|
|
context.testWorld.addEntity(observerAdmin, domainPhysical);
|
|
|
|
auto ops = collectQueue(queue);
|
|
opsHandler.clearQueues();
|
|
OpVector res;
|
|
|
|
// Create a private entity, which should only be seen by observerAdmin
|
|
Ref<Thing> objectPrivate1(new Thing(counter++));
|
|
objectPrivate1->m_location.setBBox({{-1, -1, -1},
|
|
{1, 1, 1}});
|
|
objectPrivate1->setAttrValue("mode", "fixed");
|
|
objectPrivate1->setAttrValue("visibility", "private");
|
|
objectPrivate1->m_location.m_pos = {0, 0, 0};
|
|
context.testWorld.addEntity(objectPrivate1, domainPhysical);
|
|
|
|
ops = collectQueue(queue);
|
|
|
|
// We now expect to get an Appearance op sent to the admin observer (but not the regular observer)
|
|
ASSERT_EQUAL(1u, ops.size())
|
|
ASSERT_EQUAL(Atlas::Objects::Operation::APPEARANCE_NO, ops.front()->getClassNo())
|
|
ASSERT_EQUAL(objectPrivate1->getId(), ops.front()->getArgs().front()->getId())
|
|
ASSERT_EQUAL(observerAdmin->getId(), ops.front()->getTo())
|
|
|
|
opsHandler.clearQueues();
|
|
|
|
// Move objectPrivate1 a bit; only admin observer should see it
|
|
{
|
|
Atlas::Objects::Operation::Move move;
|
|
|
|
Anonymous arg1;
|
|
arg1->setId(objectPrivate1->getId());
|
|
arg1->setPos({510, 0, 500});
|
|
move->setArgs1(arg1);
|
|
objectPrivate1->MoveOperation(move, res);
|
|
}
|
|
ASSERT_EQUAL(1u, res.size())
|
|
ASSERT_EQUAL(Atlas::Objects::Operation::SIGHT_NO, res.front()->getClassNo())
|
|
ASSERT_EQUAL(Atlas::Objects::Operation::MOVE_NO, res.front()->getArgs().front()->getClassNo())
|
|
ASSERT_EQUAL(objectPrivate1->getId(), Atlas::Objects::smart_dynamic_cast<Operation>(res.front()->getArgs().front())->getArgs().front()->getId())
|
|
ASSERT_EQUAL(observerAdmin->getId(), res.front()->getTo())
|
|
}
|
|
|
|
void test_sendAppearDisappear(TestContext& context)
|
|
{
|
|
auto& opsHandler = context.testWorld.getOperationsHandler();
|
|
auto& queue = opsHandler.getQueue();
|
|
|
|
long counter = 1;
|
|
Ref<Thing> domainPhysical(new Thing(counter++));
|
|
context.testWorld.addEntity(domainPhysical, context.world);
|
|
domainPhysical->m_location.setBBox({{-512, -512, -512},
|
|
{512, 512, 512}});
|
|
domainPhysical->setDomain(std::make_unique<PhysicalDomain>(*domainPhysical));
|
|
|
|
Ref<Thing> domainVoid(new Thing(counter++));
|
|
context.testWorld.addEntity(domainVoid, context.world);
|
|
domainVoid->setDomain(std::make_unique<VoidDomain>(*domainVoid));
|
|
|
|
auto domainTickFn = [&]() -> OpVector {
|
|
// We must send a Tick op to make the domain handle appear and disappear
|
|
OpVector res;
|
|
Atlas::Objects::Operation::Tick tick;
|
|
Atlas::Objects::Entity::Anonymous arg1;
|
|
arg1->setName("domain");
|
|
tick->setArgs1(arg1);
|
|
tick->setAttr("lastTick", 0.0f);
|
|
tick->setSeconds(2.0f); // This should trigger a visibility.
|
|
domainPhysical->getDomain()->operation(domainPhysical.get(), tick, res);
|
|
return res;
|
|
};
|
|
|
|
// Clear ops queue
|
|
opsHandler.clearQueues();
|
|
|
|
// Make an observer, which we'll add to the physical domain
|
|
Ref<Thing> observer(new Thing(counter++));
|
|
observer->setAttrValue("mode", "fixed");
|
|
observer->setAttrValue("perception_sight", 1);
|
|
observer->m_location.m_pos = {0, 0, 0};
|
|
context.testWorld.addEntity(observer, domainPhysical);
|
|
|
|
auto ops = collectQueue(queue);
|
|
// We now expect to get an Appearence op sent to the observer about the domain entity, as well as an Appearance sent to the observer itself.
|
|
ASSERT_EQUAL(2u, ops.size())
|
|
ASSERT_EQUAL(Atlas::Objects::Operation::APPEARANCE_NO, ops[0]->getClassNo())
|
|
ASSERT_EQUAL(domainPhysical->getId(), ops[0]->getArgs().front()->getId())
|
|
ASSERT_EQUAL(observer->getId(), ops[0]->getTo())
|
|
|
|
ASSERT_EQUAL(Atlas::Objects::Operation::APPEARANCE_NO, ops[1]->getClassNo())
|
|
ASSERT_EQUAL(observer->getId(), ops[1]->getArgs().front()->getId())
|
|
ASSERT_EQUAL(observer->getId(), ops[1]->getTo())
|
|
|
|
opsHandler.clearQueues();
|
|
|
|
// Make another observer, which we'll add to the void domain
|
|
Ref<Thing> observer_void(new Thing(counter++));
|
|
observer->setAttrValue("mode", "fixed");
|
|
observer->setAttrValue("perception_sight", 1);
|
|
observer->m_location.m_pos = {0, 0, 0};
|
|
context.testWorld.addEntity(observer_void, domainVoid);
|
|
|
|
// Clear ops queue
|
|
opsHandler.clearQueues();
|
|
|
|
// Create something we can look at
|
|
Ref<Thing> object1(new Thing(counter++));
|
|
object1->m_location.setBBox({{-1, -1, -1},
|
|
{1, 1, 1}});
|
|
object1->setAttrValue("mode", "fixed");
|
|
object1->m_location.m_pos = {10, 0, 10};
|
|
context.testWorld.addEntity(object1, domainPhysical);
|
|
|
|
ops = collectQueue(queue);
|
|
|
|
// We now expect to get an Appearance op sent to the observer (but not the observer in the void)
|
|
ASSERT_EQUAL(1u, ops.size())
|
|
ASSERT_EQUAL(Atlas::Objects::Operation::APPEARANCE_NO, ops.front()->getClassNo())
|
|
ASSERT_EQUAL(object1->getId(), ops.front()->getArgs().front()->getId())
|
|
ASSERT_EQUAL(observer->getId(), ops.front()->getTo())
|
|
|
|
opsHandler.clearQueues();
|
|
|
|
OpVector res;
|
|
// Move object1 to the void domain
|
|
{
|
|
Atlas::Objects::Operation::Move move;
|
|
|
|
Anonymous arg1;
|
|
arg1->setId(object1->getId());
|
|
arg1->setLoc(domainVoid->getId());
|
|
move->setArgs1(arg1);
|
|
object1->MoveOperation(move, res);
|
|
}
|
|
ops = collectQueue(queue);
|
|
|
|
// We now expect to get a Disappearance op sent to the observer (but nothing sent to the observer in the void). The first op should be a Sight op, the second an Update op.
|
|
ASSERT_EQUAL(0u, ops.size())
|
|
ASSERT_EQUAL(3u, res.size())
|
|
ASSERT_EQUAL(Atlas::Objects::Operation::SIGHT_NO, res[0]->getClassNo())
|
|
ASSERT_EQUAL(Atlas::Objects::Operation::UPDATE_NO, res[1]->getClassNo())
|
|
ASSERT_EQUAL(Atlas::Objects::Operation::DISAPPEARANCE_NO, res[2]->getClassNo())
|
|
ASSERT_EQUAL(observer->getId(), res[0]->getTo())
|
|
ASSERT_EQUAL(object1->getId(), res[2]->getArgs().front()->getId())
|
|
ASSERT_EQUAL(observer->getId(), res[2]->getTo())
|
|
|
|
res.clear();
|
|
|
|
// Move object1 back to the physical domain
|
|
{
|
|
Atlas::Objects::Operation::Move move;
|
|
|
|
Anonymous arg1;
|
|
arg1->setId(object1->getId());
|
|
arg1->setLoc(domainPhysical->getId());
|
|
arg1->setPosAsList({0, 0, 0});
|
|
move->setArgs1(arg1);
|
|
object1->MoveOperation(move, res);
|
|
}
|
|
ops = collectQueue(queue);
|
|
|
|
// We now expect to get an Appearance op sent to the observer (but nothing sent to the observer in the void). The first op should be an Update op
|
|
ASSERT_EQUAL(0u, ops.size())
|
|
ASSERT_EQUAL(2u, res.size())
|
|
ASSERT_EQUAL(Atlas::Objects::Operation::UPDATE_NO, res[0]->getClassNo())
|
|
ASSERT_EQUAL(Atlas::Objects::Operation::APPEARANCE_NO, res[1]->getClassNo())
|
|
ASSERT_EQUAL(object1->getId(), res[1]->getArgs().front()->getId())
|
|
ASSERT_EQUAL(observer->getId(), res[1]->getTo())
|
|
|
|
res.clear();
|
|
|
|
// Delete object1
|
|
{
|
|
Atlas::Objects::Operation::Delete deleteOp;
|
|
object1->DeleteOperation(deleteOp, res);
|
|
}
|
|
ops = collectQueue(queue);
|
|
|
|
// A Sight is sent to the deleted entity, but it's sent directly without being put on the queue.
|
|
ASSERT_EQUAL(0u, ops.size())
|
|
ASSERT_EQUAL(2u, res.size())
|
|
ASSERT_EQUAL(Atlas::Objects::Operation::SIGHT_NO, res[0]->getClassNo())
|
|
ASSERT_EQUAL(Atlas::Objects::Operation::DISAPPEARANCE_NO, res[1]->getClassNo())
|
|
ASSERT_EQUAL(observer->getId(), res[0]->getTo())
|
|
ASSERT_EQUAL(object1->getId(), res[1]->getArgs().front()->getId())
|
|
ASSERT_EQUAL(observer->getId(), res[1]->getTo())
|
|
|
|
// Create a new object, which we'll then move away beyond visible distance
|
|
Ref<Thing> object2(new Thing(counter++));
|
|
object2->m_location.setBBox({{-0.1, -0.1, -0.1},
|
|
{0.1, 0.1, 0.1}});
|
|
object2->setAttrValue("mode", "fixed");
|
|
object2->m_location.m_pos = {10, 0, 10};
|
|
context.testWorld.addEntity(object2, domainPhysical);
|
|
|
|
ops = collectQueue(queue);
|
|
|
|
// We now expect to get an Appearance op sent to the observer (but not the observer in the void)
|
|
ASSERT_EQUAL(1u, ops.size())
|
|
ASSERT_EQUAL(Atlas::Objects::Operation::APPEARANCE_NO, ops.front()->getClassNo())
|
|
ASSERT_EQUAL(object2->getId(), ops.front()->getArgs().front()->getId())
|
|
ASSERT_EQUAL(observer->getId(), ops.front()->getTo())
|
|
|
|
opsHandler.clearQueues();
|
|
res.clear();
|
|
domainTickFn();
|
|
// Move object2 away a great distance, outside of visible range
|
|
{
|
|
Atlas::Objects::Operation::Move move;
|
|
|
|
Anonymous arg1;
|
|
arg1->setId(object2->getId());
|
|
arg1->setPos({500, 0, 500});
|
|
move->setArgs1(arg1);
|
|
object2->MoveOperation(move, res);
|
|
}
|
|
ops = collectQueue(queue);
|
|
// We now expect to get a Disappearance op sent to the observer. The first op should be a Sight op, the second an Update op.
|
|
// The ops contains a Sight of a Set, which contains the new positional attributes as the entity moved within the PhysicalDomain.
|
|
ASSERT_EQUAL(1u, ops.size())
|
|
ASSERT_EQUAL(Atlas::Objects::Operation::SIGHT_NO, ops[0]->getClassNo())
|
|
ASSERT_EQUAL(Atlas::Objects::Operation::SET_NO, ops[0]->getArgs().front()->getClassNo())
|
|
ASSERT_EQUAL(observer->getId(), ops[0]->getTo())
|
|
ASSERT_EQUAL(1u, res.size())
|
|
ASSERT_EQUAL(Atlas::Objects::Operation::SIGHT_NO, res[0]->getClassNo())
|
|
ASSERT_EQUAL(observer->getId(), res[0]->getTo())
|
|
|
|
// We must send a Tick op to make the domain handle appear and disappear
|
|
res = domainTickFn();
|
|
ASSERT_EQUAL(2u, res.size()) // Second op is a Tick
|
|
ASSERT_EQUAL(Atlas::Objects::Operation::DISAPPEARANCE_NO, res[0]->getClassNo())
|
|
ASSERT_EQUAL(observer->getId(), res[0]->getTo())
|
|
ASSERT_EQUAL(object2->getId(), res[0]->getArgs().front()->getId())
|
|
|
|
opsHandler.clearQueues();
|
|
res.clear();
|
|
// Move object2 a bit; nothing should see it
|
|
{
|
|
Atlas::Objects::Operation::Move move;
|
|
|
|
Anonymous arg1;
|
|
arg1->setId(object2->getId());
|
|
arg1->setPos({510, 0, 500});
|
|
move->setArgs1(arg1);
|
|
object2->MoveOperation(move, res);
|
|
}
|
|
ops = collectQueue(queue);
|
|
ASSERT_TRUE(res.empty())
|
|
ASSERT_TRUE(ops.empty())
|
|
res = domainTickFn();
|
|
ASSERT_EQUAL(1u, res.size()) // Should only contain Tick
|
|
|
|
opsHandler.clearQueues();
|
|
// Make object2 very much larger, so it should appear
|
|
{
|
|
object2->setAttrValue("bbox", WFMath::AxisBox<3>{{-500, -500, -500},
|
|
{500, 500, 500}}.toAtlas());
|
|
}
|
|
ops = collectQueue(queue);
|
|
ASSERT_TRUE(ops.empty())
|
|
res = domainTickFn();
|
|
ASSERT_EQUAL(2u, res.size()) // Second op is a Tick
|
|
|
|
ASSERT_EQUAL(Atlas::Objects::Operation::APPEARANCE_NO, res[0]->getClassNo())
|
|
ASSERT_EQUAL(observer->getId(), res[0]->getTo())
|
|
ASSERT_EQUAL(object2->getId(), res[0]->getArgs().front()->getId())
|
|
|
|
opsHandler.clearQueues();
|
|
// Make object2 smaller again, so it should disappear
|
|
{
|
|
object2->setAttrValue("bbox", WFMath::AxisBox<3>{{-1, -1, -1},
|
|
{1, 1, 1}}.toAtlas());
|
|
}
|
|
res = domainTickFn();
|
|
ASSERT_EQUAL(2u, res.size()) // Second op is a Tick
|
|
|
|
ASSERT_EQUAL(Atlas::Objects::Operation::DISAPPEARANCE_NO, res[0]->getClassNo())
|
|
ASSERT_EQUAL(observer->getId(), res[0]->getTo())
|
|
ASSERT_EQUAL(object2->getId(), res[0]->getArgs().front()->getId())
|
|
|
|
|
|
opsHandler.clearQueues();
|
|
res.clear();
|
|
// Move object2 close to the observer so it appears
|
|
{
|
|
Atlas::Objects::Operation::Move move;
|
|
|
|
Anonymous arg1;
|
|
arg1->setId(object2->getId());
|
|
arg1->setPos({0, 0, 0});
|
|
move->setArgs1(arg1);
|
|
object2->MoveOperation(move, res);
|
|
}
|
|
ops = collectQueue(queue);
|
|
ASSERT_TRUE(res.empty())
|
|
ASSERT_TRUE(ops.empty())
|
|
res = domainTickFn();
|
|
ASSERT_EQUAL(2u, res.size()) // Second op is a Tick
|
|
|
|
ASSERT_EQUAL(Atlas::Objects::Operation::APPEARANCE_NO, res[0]->getClassNo())
|
|
ASSERT_EQUAL(observer->getId(), res[0]->getTo())
|
|
ASSERT_EQUAL(object2->getId(), res[0]->getArgs().front()->getId())
|
|
}
|
|
};
|
|
|
|
int main()
|
|
{
|
|
Monitors m;
|
|
Tested t;
|
|
|
|
return t.run();
|
|
}
|