12#include "TypeService.h"
15#include "EntityRouter.h"
17#include <Atlas/Objects/Entity.h>
18#include <Atlas/Objects/Operation.h>
20using namespace Atlas::Objects::Operation;
21using Atlas::Objects::Root;
22using Atlas::Objects::Entity::RootEntity;
23using Atlas::Objects::smart_dynamic_cast;
27View::View(Avatar& av) :
30 m_simulationSpeed(1.0),
31 m_maxPendingCount(10) {
36 deleteEntity(m_topLevel->getId());
41 auto contents = std::move(m_contents);
45ViewEntity* View::getEntity(
const std::string& eid)
const {
46 auto E = m_contents.find(eid);
47 if (E == m_contents.end()) {
51 return E->second.entity.get();
54void View::registerFactory(std::unique_ptr<Factory> f) {
55 m_factories.insert(std::move(f));
58sigc::connection View::notifyWhenEntitySeen(
const std::string& eid,
const EntitySightSlot& slot) {
59 if (m_contents.count(eid)) {
60 error() <<
"notifyWhenEntitySeen: entity " << eid <<
" already in View";
64 sigc::connection c = m_notifySights[eid].connect(slot);
65 getEntityFromServer(eid);
70 return m_owner.getConnection().getTypeService();
74 return m_owner.getConnection().getTypeService();
78 return m_owner.getConnection().getEventService();
82 return m_owner.getConnection().getEventService();
85double View::getSimulationSpeed()
const {
86 return m_simulationSpeed;
91 auto pruned = pruneAbandonedPendingEntities();
92 for (
size_t i = 0; i < pruned; ++i) {
96 WFMath::TimeStamp t(WFMath::TimeStamp::now());
99 for (
auto& it : m_moving) {
100 it->updatePredictedState(t, m_simulationSpeed);
104 if (!m_lastUpdateTime.isValid()) {
105 m_lastUpdateTime = t;
107 WFMath::TimeDiff dt = t - m_lastUpdateTime;
109 for (
auto& m_progressingTask : m_progressingTasks) {
110 m_progressingTask->updatePredictedProgress(dt);
113 m_lastUpdateTime = t;
115 if (m_owner.getEntity()) {
116 auto topEntity = m_owner.getEntity()->getTopEntity();
117 setTopLevelEntity(topEntity);
119 setTopLevelEntity(
nullptr);
125 assert(m_moving.count(ent) == 0);
126 m_moving.insert(ent);
129void View::removeFromPrediction(ViewEntity* ent) {
130 assert(m_moving.count(ent) == 1);
134void View::taskRateChanged(
Task* t) {
135 if (t->m_progressRate > 0.0) {
136 m_progressingTasks.insert(t);
138 m_progressingTasks.erase(t);
144void View::appear(
const std::string& eid,
double stamp) {
145 auto* ent = getEntity(eid);
147 getEntityFromServer(eid);
151 if (ent->m_recentlyCreated) {
152 EntityCreated.emit(ent);
153 ent->m_recentlyCreated =
false;
156 if (ent->isVisible())
return;
158 if ((stamp == 0) || (stamp > ent->getStamp())) {
159 if (isPending(eid)) {
160 m_pending[eid].sightAction = SightAction::APPEAR;
163 getEntityFromServer(eid);
166 ent->setVisible(
true);
171void View::disappear(
const std::string& eid) {
172 auto* ent = getEntity(eid);
176 if (isPending(eid)) {
178 m_pending[eid].sightAction = SightAction::DISCARD;
180 warning() <<
"got disappear for unknown entity " << eid;
185void View::sight(
const RootEntity& gent) {
187 std::string eid = gent->getId();
188 auto pending = m_pending.find(eid);
191 if (pending != m_pending.end()) {
192 switch (pending->second.sightAction) {
193 case SightAction::APPEAR:
197 case SightAction::DISCARD:
198 m_pending.erase(pending);
202 case SightAction::HIDE:
206 case SightAction::QUEUED:
207 error() <<
"got sight of queued entity " << eid <<
" somehow";
208 eraseFromLookQueue(eid);
212 throw InvalidOperation(
"got bad pending action for entity");
215 m_pending.erase(pending);
219 auto* ent = getEntity(eid);
222 ent->firstSight(gent);
224 ent = initialSight(gent);
225 EntitySeen.emit(ent);
228 ent->setVisible(visible);
232ViewEntity* View::initialSight(
const RootEntity& gent) {
233 assert(m_contents.count(gent->getId()) == 0);
235 auto entity = createEntity(gent);
236 auto router = std::make_unique<EntityRouter>(*entity, *
this);
238 auto entityPtr = entity.get();
240 entity->Moving.connect([
this, entityPtr](
bool startedMoving) {
242 addToPrediction(entityPtr);
244 removeFromPrediction(entityPtr);
248 auto I = m_contents.emplace(gent->getId(), EntityEntry{std::move(entity), std::move(router)});
249 auto& insertedEntry = I.first->second;
250 auto insertedEntity = insertedEntry.entity.get();
251 insertedEntity->init(gent,
false);
253 InitialSightEntity.emit(insertedEntity);
255 auto it = m_notifySights.find(gent->getId());
256 if (it != m_notifySights.end()) {
257 it->second.emit(insertedEntity);
258 m_notifySights.erase(it);
261 return insertedEntity;
264void View::deleteEntity(
const std::string& eid) {
265 auto I = m_contents.find(eid);
266 if (I != m_contents.end()) {
268 auto entity = I->second.entity.get();
269 if (entity->m_moving) {
270 removeFromPrediction(entity);
274 if (entity->isAncestorTo(*m_owner.getEntity())) {
275 Entity* nearestAncestor = m_owner.getEntity();
276 while (nearestAncestor->getLocation() != entity) {
277 nearestAncestor = nearestAncestor->getLocation();
280 nearestAncestor->setLocation(
nullptr,
true);
282 setTopLevelEntity(nearestAncestor);
284 if (entity->getLocation()) {
285 auto entityLocation = entity->getLocation();
287 entity->setLocation(
nullptr,
true);
289 deleteEntity(entityLocation->getTopEntity()->getId());
295 EntityDeleted.emit(entity);
296 entity->BeingDeleted.emit();
298 auto children = I->second.entity->getContent();
300 for (
auto& child : children) {
301 deleteEntity(child->getId());
306 if (isPending(eid)) {
307 m_pending[eid].sightAction = SightAction::DISCARD;
309 warning() <<
"got delete for unknown entity " << eid;
314std::unique_ptr<ViewEntity> View::createEntity(
const RootEntity& gent) {
315 TypeInfo* type = getConnection().getTypeService().getTypeForAtlas(gent);
316 assert(type->isBound());
318 auto F = m_factories.begin();
319 for (; F != m_factories.end(); ++F) {
320 if ((*F)->accept(gent, type)) {
321 return (*F)->instantiate(gent, type, *
this);
325 throw std::runtime_error(
"Could not find entity factory suitable for creating new entity.");
328void View::unseen(
const std::string& eid) {
333 m_pending.erase(eid);
336bool View::isPending(
const std::string& eid)
const {
337 return m_pending.find(eid) != m_pending.end();
341 return m_owner.getConnection();
344void View::getEntityFromServer(
const std::string& eid) {
345 if (isPending(eid)) {
350 if (!eid.empty() && (m_pending.size() >= m_maxPendingCount)) {
351 m_lookQueue.push_back(eid);
352 m_pending[eid].sightAction = SightAction::QUEUED;
359size_t View::pruneAbandonedPendingEntities() {
361 auto now = std::chrono::steady_clock::now();
362 for (
auto I = m_pending.begin(); I != m_pending.end();) {
363 if (I->second.sightAction != SightAction::QUEUED && (now - I->second.registrationTime) > std::chrono::seconds(20)) {
364 warning() <<
"Didn't receive any response for entity " << I->first <<
" within 20 seconds, will remove it from pending list.";
365 I = m_pending.erase(I);
375void View::sendLookAt(
const std::string& eid) {
378 auto pending = m_pending.find(eid);
379 if (pending != m_pending.end()) {
380 switch (pending->second.sightAction) {
381 case SightAction::QUEUED:
383 pending->second.sightAction = SightAction::APPEAR;
386 case SightAction::DISCARD:
387 case SightAction::HIDE:
388 if (m_notifySights.count(eid) == 0) {
390 m_pending.erase(pending);
398 case SightAction::APPEAR:
411 m_pending.emplace(eid, PendingStatus{SightAction::APPEAR, std::chrono::steady_clock::now()});
417 look->setArgs1(what);
420 look->setFrom(m_owner.getId());
421 getConnection().send(look);
424void View::setTopLevelEntity(
Entity* newTopLevel) {
425 if (newTopLevel == m_topLevel) {
429 m_simulationSpeedConnection.disconnect();
433 m_simulationSpeedConnection = newTopLevel->
observe(
"simulation_speed", sigc::mem_fun(
this, &View::parseSimulationSpeed),
true);
435 m_topLevel = newTopLevel;
436 TopLevelEntityChanged.emit();
440void View::parseSimulationSpeed(
const Atlas::Message::Element& element) {
441 if (element.isFloat()) {
442 m_simulationSpeed = element.Float();
446void View::issueQueuedLook() {
447 if (m_lookQueue.empty()) {
450 std::string eid = std::move(m_lookQueue.front());
451 m_lookQueue.pop_front();
455void View::dumpLookQueue() {
456 debug() <<
"look queue:";
457 for (
const auto& lookOp : m_lookQueue) {
458 debug() <<
"\t" << lookOp;
462void View::eraseFromLookQueue(
const std::string& eid) {
463 std::deque<std::string>::iterator it;
464 for (it = m_lookQueue.begin(); it != m_lookQueue.end(); ++it) {
466 m_lookQueue.erase(it);
471 error() <<
"entity " << eid <<
" not present in the look queue";
Entity is a concrete (instantiable) class representing one game entity.
bool isMoving() const
Test if this entity has a non-zero velocity vector.
Entity * getLocation() const
The containing entity, or null if this is a top-level visible entity.
sigc::connection observe(const std::string &propertyName, const PropertyChangedSlot &aslot, bool evaluateNow)
Setup an observer so that the specified slot is fired when the named property's value changes.
Handles polling of the IO system as well as making sure that registered handlers are run on the main ...
An entity which is bound to an Eris::View. This subclass of Eris::Entity is intimately bound to a Vie...