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";
61 return sigc::connection();
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);
273 EntityDeleted.emit(entity);
274 entity->BeingDeleted.emit();
276 auto children = I->second.entity->getContent();
278 for (
auto& child : children) {
279 deleteEntity(child->getId());
284 if (isPending(eid)) {
285 m_pending[eid].sightAction = SightAction::DISCARD;
287 warning() <<
"got delete for unknown entity " << eid;
292 std::unique_ptr<ViewEntity> View::createEntity(
const RootEntity& gent) {
296 auto F = m_factories.begin();
297 for (; F != m_factories.end(); ++F) {
298 if ((*F)->accept(gent, type)) {
299 return (*F)->instantiate(gent, type, *
this);
303 throw std::runtime_error(
"Could not find entity factory suitable for creating new entity.");
306 void View::unseen(
const std::string& eid) {
311 m_pending.erase(eid);
315 return m_pending.find(eid) != m_pending.end();
319 return m_owner.getConnection();
322 void View::getEntityFromServer(
const std::string& eid) {
323 if (isPending(eid)) {
328 if (!eid.empty() && (m_pending.size() >= m_maxPendingCount)) {
329 m_lookQueue.push_back(eid);
330 m_pending[eid].sightAction = SightAction::QUEUED;
337 size_t View::pruneAbandonedPendingEntities() {
339 auto now = std::chrono::steady_clock::now();
340 for (
auto I = m_pending.begin(); I != m_pending.end();) {
341 if (I->second.sightAction != SightAction::QUEUED && (now - I->second.registrationTime) > std::chrono::seconds(20)) {
342 warning() <<
"Didn't receive any response for entity " << I->first <<
" within 20 seconds, will remove it from pending list.";
343 I = m_pending.erase(I);
356 auto pending = m_pending.find(eid);
357 if (pending != m_pending.end()) {
358 switch (pending->second.sightAction) {
359 case SightAction::QUEUED:
361 pending->second.sightAction = SightAction::APPEAR;
364 case SightAction::DISCARD:
365 case SightAction::HIDE:
366 if (m_notifySights.count(eid) == 0) {
368 m_pending.erase(pending);
376 case SightAction::APPEAR:
389 m_pending.emplace(eid, PendingStatus{SightAction::APPEAR, std::chrono::steady_clock::now()});
395 look->setArgs1(what);
398 look->setFrom(m_owner.getId());
399 getConnection().send(look);
402 void View::setTopLevelEntity(
Entity* newTopLevel) {
403 if (newTopLevel == m_topLevel) {
408 if (m_topLevel->isVisible() && (m_topLevel->getLocation() ==
nullptr)) {
409 error() <<
"old top-level entity is visible, but has no location";
413 m_simulationSpeedConnection.disconnect();
417 m_simulationSpeedConnection = newTopLevel->
observe(
"simulation_speed", sigc::mem_fun(
this, &View::parseSimulationSpeed),
true);
419 m_topLevel = newTopLevel;
420 TopLevelEntityChanged.emit();
424 void View::parseSimulationSpeed(
const Atlas::Message::Element& element) {
425 if (element.isFloat()) {
426 m_simulationSpeed = element.Float();
430 void View::issueQueuedLook() {
431 if (m_lookQueue.empty()) {
434 std::string eid = std::move(m_lookQueue.front());
435 m_lookQueue.pop_front();
439 void View::dumpLookQueue() {
440 debug() <<
"look queue:";
441 for (
const auto& lookOp : m_lookQueue) {
442 debug() <<
"\t" << lookOp;
446 void View::eraseFromLookQueue(
const std::string& eid) {
447 std::deque<std::string>::iterator it;
448 for (it = m_lookQueue.begin(); it != m_lookQueue.end(); ++it) {
450 m_lookQueue.erase(it);
455 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...
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.