6 #include "ViewEntity.h" 8 #include "Connection.h" 9 #include "Exceptions.h" 12 #include "TypeService.h" 15 #include "EntityRouter.h" 17 #include <Atlas/Objects/Entity.h> 18 #include <Atlas/Objects/Operation.h> 21 using Atlas::Objects::Root;
22 using Atlas::Objects::Entity::RootEntity;
23 using Atlas::Objects::smart_dynamic_cast;
27 View::View(Avatar& av) :
30 m_simulationSpeed(1.0),
31 m_maxPendingCount(10) {
36 deleteEntity(m_topLevel->getId());
41 auto contents = std::move(m_contents);
46 auto E = m_contents.find(eid);
47 if (E == m_contents.end()) {
51 return E->second.entity.get();
55 m_factories.insert(std::move(f));
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();
85 double 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);
129 void View::removeFromPrediction(
ViewEntity* ent) {
130 assert(m_moving.count(ent) == 1);
135 if (t->m_progressRate > 0.0) {
136 m_progressingTasks.insert(t);
138 m_progressingTasks.erase(t);
144 void View::appear(
const std::string& eid,
double stamp) {
145 auto* ent = getEntity(eid);
147 getEntityFromServer(eid);
152 EntityCreated.emit(ent);
158 if ((stamp == 0) || (stamp > ent->
getStamp())) {
159 if (isPending(eid)) {
160 m_pending[eid].sightAction = SightAction::APPEAR;
163 getEntityFromServer(eid);
171 void 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;
185 void 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);
215 m_pending.erase(pending);
219 auto* ent = getEntity(eid);
224 ent = initialSight(gent);
225 EntitySeen.emit(ent);
232 ViewEntity* 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;
264 void 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())) {
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;
314 std::unique_ptr<ViewEntity> View::createEntity(
const RootEntity& gent) {
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.");
328 void View::unseen(
const std::string& eid) {
333 m_pending.erase(eid);
337 return m_pending.find(eid) != m_pending.end();
341 return m_owner.getConnection();
344 void 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;
359 size_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);
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);
424 void 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();
440 void View::parseSimulationSpeed(
const Atlas::Message::Element& element) {
441 if (element.isFloat()) {
442 m_simulationSpeed = element.Float();
446 void View::issueQueuedLook() {
447 if (m_lookQueue.empty()) {
450 std::string eid = std::move(m_lookQueue.front());
451 m_lookQueue.pop_front();
455 void View::dumpLookQueue() {
456 debug() <<
"look queue:";
457 for (
const auto& lookOp : m_lookQueue) {
458 debug() <<
"\t" << lookOp;
462 void 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";
void firstSight(const Atlas::Objects::Entity::RootEntity &gent)
sigc::connection notifyWhenEntitySeen(const std::string &eid, const EntitySightSlot &slot)
bool isPending(const std::string &eid) const
test if the specified entity ID is pending initial sight on the View
void setVisible(bool vis)
bool isMoving() const
Test if this entity has a non-zero velocity vector.
The representation of an Atlas type (i.e a class or operation definition). This class supports effice...
virtual Entity * getEntity(const std::string &id)=0
Gets an entity with the supplied id from the system.
void setLocation(Entity *newLocation, bool removeFromOldLocation=true)
void taskRateChanged(Task *)
void sendLookAt(const std::string &eid)
Handles polling of the IO system as well as making sure that registered handlers are run on the main ...
bool m_recentlyCreated
flag set if this entity was the subject of a sight(create)
bool isBound() const
Check the bound flag for this node; if false then recursivley check parents until an authorative is f...
TypeInfo * getTypeForAtlas(const Atlas::Objects::Root &obj)
double getStamp() const
Access the current time-stamp of the 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...
An entity which is bound to an Eris::View. This subclass of Eris::Entity is intimately bound to a Vie...
TypeService & getTypeService()
Gets the TypeService attached to the view.
Entity is a concrete (instantiable) class representing one game entity.
Entity * getLocation() const
The containing entity, or null if this is a top-level visible entity.
ViewEntity * getEntity(const std::string &eid) const
void registerFactory(std::unique_ptr< Factory > factory)
EventService & getEventService()
Gets the EventService used by the view.