1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include "patat_subscribe_manager.h"
22 #include "game_share/ryzom_entity_id.h"
23 #include "game_share/synchronised_message.h"
25 #include "nel/misc/command.h"
26 #include "nel/pacs/u_move_container.h"
27 #include "nel/pacs/u_collision_desc.h"
30 using namespace NLMISC
;
31 using namespace NLNET
;
32 using namespace NLLIGO
;
33 using namespace NLPACS
;
38 CPatatSubscribeManager::CPatatSubscribeManager()
46 CPatatSubscribeManager::~CPatatSubscribeManager()
54 void CPatatSubscribeManager::init()
58 _SubscriberMap
.clear();
60 _ModifiedPatats
.clear();
64 * Serialize a manager file (no subscription saved, only triggers/patats saved)
66 void CPatatSubscribeManager::serial(NLMISC::IStream
&f
)
74 f
.serialCont(_PatatMap
);
75 f
.serialCont(_TriggerMap
);
82 void CPatatSubscribeManager::usePrim(const string
&primFile
)
85 _PatatGrid
.usePrim(primFile
, prims
);
88 for (i
=0; i
<prims
.size(); ++i
)
90 // checks if patat exists in subscribed patats (creates and inits if not)
91 TPatatMap::iterator itp
= _PatatMap
.find(prims
[i
]);
92 if (itp
== _PatatMap
.end())
94 pair
<TPatatMap::iterator
, bool> res
= _PatatMap
.insert(TPatatMap::value_type(prims
[i
], CPatat()));
96 (*itp
).second
.Name
= _PatatGrid
.getZoneName(prims
[i
]);
97 (*itp
).second
.InternalPatatId
= prims
[i
];
99 _TriggerMap
.insert(TTriggerIdMap::value_type((*itp
).second
.Name
, (*itp
).second
.InternalPatatId
));
105 * Register a pacs trigger id
107 void CPatatSubscribeManager::usePacsTrigger(sint32 id
, const std::string
&name
)
109 // checks if patat exists in subscribed patats (creates and inits if not)
110 TPatatMap::iterator itp
= _PatatMap
.find(id
);
111 if (itp
== _PatatMap
.end())
113 pair
<TPatatMap::iterator
, bool> res
= _PatatMap
.insert(TPatatMap::value_type(id
, CPatat()));
115 (*itp
).second
.Name
= name
;
116 (*itp
).second
.InternalPatatId
= id
;
118 _TriggerMap
.insert(TTriggerIdMap::value_type((*itp
).second
.Name
, (*itp
).second
.InternalPatatId
));
127 * Subscribe to a patat
129 void CPatatSubscribeManager::subscribe(NLNET::TServiceId service
, const TPatatSubscription
&patat
)
131 nldebug("Subscribe service %d to patat %s (#id %d)", service
.get(), patat
.first
.c_str(), patat
.second
);
133 // checks the patat is referenced in patatgrid/pacs triggers
134 TTriggerIdMap::iterator it
= _TriggerMap
.find(patat
.first
);
136 if (it
== _TriggerMap
.end())
138 nlwarning("Can't subscribe service %d to patat %s, not referenced in PatatGrid", service
.get(), patat
.first
.c_str());
142 sint32 patatId
= (*it
).second
;
144 // checks if patat exists in subscribed patats (creates and inits if not)
145 TPatatMap::iterator itp
= _PatatMap
.find(patatId
);
146 if (itp
== _PatatMap
.end())
148 pair
<TPatatMap::iterator
, bool> res
= _PatatMap
.insert(TPatatMap::value_type(patatId
, CPatat()));
151 (*itp
).second
.Name
= patat
.first
;
152 (*itp
).second
.InternalPatatId
= patatId
;
155 // checks if subscriber exists in subscribers (creates and inits if not)
156 TSubscriberMap::iterator its
= _SubscriberMap
.find(service
);
157 if (its
== _SubscriberMap
.end())
159 pair
<TSubscriberMap::iterator
, bool> res
= _SubscriberMap
.insert(TSubscriberMap::value_type(service
, CSubscriber()));
162 (*its
).second
.Service
= service
;
167 // checks if service not yet in patat's subscribers
169 for (i
=0; i
<(*itp
).second
.Subscribers
.size(); ++i
)
170 if ((*itp
).second
.Subscribers
[i
].Service
== service
)
173 if (i
== (*itp
).second
.Subscribers
.size())
174 (*itp
).second
.Subscribers
.resize(i
+1);
176 (*itp
).second
.Subscribers
[i
].Service
= service
;
177 (*itp
).second
.Subscribers
[i
].SubscriberIterator
= its
;
178 (*itp
).second
.Subscribers
[i
].PatatId
= patat
.second
;
180 // checks if patat not yet in service's subscribed patats
181 for (i
=0; i
<(*its
).second
.Patats
.size(); ++i
)
182 if ((*its
).second
.Patats
[i
].InternalPatatId
== patatId
)
185 if (i
== (*its
).second
.Patats
.size())
186 (*its
).second
.Patats
.resize(i
+1);
188 (*its
).second
.Patats
[i
].InternalPatatId
= patatId
;
189 (*its
).second
.Patats
[i
].PatatId
= patat
.second
;
190 (*its
).second
.Patats
[i
].PatatIterator
= itp
;
193 if (!(*itp
).second
.StillIns
.empty())
195 CMessage
msg("TRIGGER_IN");
196 msg
.serial(const_cast<uint16
&>(patat
.second
));
197 msg
.serialCont((*itp
).second
.StillIns
);
202 * Unsubscribe to a patat
204 void CPatatSubscribeManager::unsubscribe(NLNET::TServiceId service
, TPatatId patat
)
206 nldebug("Unsubscribe service %d to patat #id %d", service
.get(), patat
);
207 // checks the patat is referenced in patatgrid
211 // find patat and remove it
212 TSubscriberMap::iterator its
= _SubscriberMap
.find(service
);
213 TPatatMap::iterator itp
= _PatatMap
.end();
214 if (its
!= _SubscriberMap
.end())
216 vector
<CSubscribedPatat
>::iterator itsp
;
217 for (itsp
=(*its
).second
.Patats
.begin(); itsp
!=(*its
).second
.Patats
.end(); ++itp
)
219 if ((*itsp
).PatatId
== patat
)
221 patatId
= (*itsp
).InternalPatatId
;
222 itp
= (*itsp
).PatatIterator
;
223 (*its
).second
.Patats
.erase(itsp
);
229 // find subscriber and remove it
230 if (itp
!= _PatatMap
.end())
232 vector
<CPatatSubscriber
>::iterator its
;
233 for (its
=(*itp
).second
.Subscribers
.begin(); its
!=(*itp
).second
.Subscribers
.end(); ++its
)
235 if ((*its
).Service
== service
)
237 (*itp
).second
.Subscribers
.erase(its
);
245 * Unsubscribe a whole service
247 void CPatatSubscribeManager::unsubscribe(NLNET::TServiceId service
)
249 nldebug("Unsubscribe service %d to all subscribed patats", service
.get());
251 // find patat and remove it
252 TSubscriberMap::iterator its
= _SubscriberMap
.find(service
);
253 if (its
!= _SubscriberMap
.end())
255 // for all subscribed patats
256 vector
<CSubscribedPatat
>::iterator itp
;
257 for (itp
=(*its
).second
.Patats
.begin(); itp
!=(*its
).second
.Patats
.end(); ++itp
)
259 // remove in patats subscriber list all reference to subcriber
260 vector
<CPatatSubscriber
> &subscribers
= (*((*itp
).PatatIterator
)).second
.Subscribers
;
261 vector
<CPatatSubscriber
>::iterator it
;
262 for (it
=subscribers
.begin(); it
!=subscribers
.end(); )
263 if ((*it
).Service
== service
)
264 it
= subscribers
.erase(it
);
274 * Get the new entry index for an entity, given its position
276 uint32
CPatatSubscribeManager::getNewEntryIndex(const CEntityId
&id
, const CVector
&pos
, uint32 previousEntryIndex
)
278 // get the entry index for the position
279 sint32 newEntryIndex
= _PatatGrid
.getEntryIndex(pos
);
281 setNewEntryIndex(id
, newEntryIndex
, previousEntryIndex
);
283 return newEntryIndex
;
287 * set the new entry index for an entity
289 void CPatatSubscribeManager::setNewEntryIndex(const CEntityId
&id
, uint32 newEntryIndex
, uint32 previousEntryIndex
)
291 vector
<uint32
> in
, out
;
293 // compute the patatId differences between previous and new entry indexes
294 if (!_PatatGrid
.diff((CPatatGrid::TEntryIndex
)previousEntryIndex
, (CPatatGrid::TEntryIndex
)newEntryIndex
, in
, out
))
298 // for each patat left, notify patat the entity left it
299 for (i
=0; i
<out
.size(); ++i
)
301 nldebug("Notfied %s left patat %s", id
.toString().c_str(), _PatatGrid
.getZoneName(out
[i
]).c_str());
303 // find the left patat
304 TPatatMap::iterator it
= _PatatMap
.find((sint32
)out
[i
]);
305 if (it
== _PatatMap
.end())
306 continue; // not referenced
308 // adds the entity to the outs list
309 (*it
).second
.Outs
.push_back(id
);
310 (*it
).second
.StillIns
.erase(id
);
312 // notify it as modified
313 if (!(*it
).second
.Modified
)
315 (*it
).second
.Modified
= true;
316 _ModifiedPatats
.push_back(it
);
320 // same job, but for entered patats
321 for (i
=0; i
<in
.size(); ++i
)
323 nldebug("Notfied %s entered patat %s", id
.toString().c_str(), _PatatGrid
.getZoneName(in
[i
]).c_str());
325 TPatatMap::iterator it
= _PatatMap
.find((sint32
)in
[i
]);
326 if (it
== _PatatMap
.end())
329 (*it
).second
.Ins
.push_back(id
);
330 (*it
).second
.StillIns
.insert(id
);
332 if (!(*it
).second
.Modified
)
334 (*it
).second
.Modified
= true;
335 _ModifiedPatats
.push_back(it
);
341 * Process pacs trigger collisions
343 void CPatatSubscribeManager::processPacsTriggers(UMoveContainer
*moveContainer
)
345 uint num
= moveContainer
->getNumTriggerInfo();
348 for (i
=0; i
<num
; ++i
)
350 const UTriggerInfo
&info
= moveContainer
->getTriggerInfo(i
);
352 CEntityId trigger
= CEntityId(info
.Object0
);
353 CEntityId entity
= CEntityId(info
.Object1
);
355 if (trigger
.getType() != RYZOMID::trigger
)
356 swap(trigger
, entity
);
358 // don't warn of non trigger collision
359 if (trigger
.getType() != RYZOMID::trigger
)
362 sint32 triggerId
= (sint32
)trigger
.getShortId();
364 TPatatMap::iterator it
= _PatatMap
.find(triggerId
);
365 if (it
== _PatatMap
.end())
368 switch (info
.CollisionType
)
370 case UTriggerInfo::In
:
372 nldebug("Notified %s entered trigger %s", entity
.toString().c_str(), (*it
).second
.Name
.c_str());
373 // adds the entity to the ins list
374 (*it
).second
.Ins
.push_back(entity
);
375 // don't insert in still list, cause when primitive is deleted, no trigger out event occurs
376 // and that might fill still list quickly !
377 //(*it).second.StillIns.insert(entity);
379 // adds to the modified list
380 if (!(*it
).second
.Modified
)
382 (*it
).second
.Modified
= true;
383 _ModifiedPatats
.push_back(it
);
387 case UTriggerInfo::Out
:
389 nldebug("Notified %s entered trigger %s", entity
.toString().c_str(), (*it
).second
.Name
.c_str());
390 // adds the entity to the outs list
391 (*it
).second
.Outs
.push_back(entity
);
392 //(*it).second.StillIns.erase(entity);
394 // notify it as modified
395 if (!(*it
).second
.Modified
)
397 (*it
).second
.Modified
= true;
398 _ModifiedPatats
.push_back(it
);
402 case UTriggerInfo::Inside
:
412 void CPatatSubscribeManager::emitChanges()
414 // initialize subscribers messages
415 // for each subscriber, reset in and out messages
416 TSubscriberMap::iterator its
;
417 for (its
=_SubscriberMap
.begin(); its
!=_SubscriberMap
.end(); ++its
)
420 CMessage
&msgouts
= (*its
).second
.OutsMessage
;
422 /*if (msgouts.isReading())
424 msgouts
.setType("TRIGGER_OUT");
425 (*its
).second
.OutsMsgSize
= msgouts
.length();
428 CMessage
&msgins
= (*its
).second
.InsMessage
;
430 /*if (msgins.isReading())
432 msgins
.setType("TRIGGER_IN");
433 (*its
).second
.InsMsgSize
= msgins
.length();
436 // for each modified patat, add the patatId and the list of entities that entered/left the patat
437 TModifiedPatats::iterator itm
;
438 for (itm
=_ModifiedPatats
.begin(); itm
!=_ModifiedPatats
.end(); ++itm
)
440 CPatat
&patat
= (*(*itm
)).second
;
443 // if entities left the patat
444 // notify all subscribers -- serializes in OutsMessage patatId and vector of entity ids
445 if (!patat
.Outs
.empty())
447 for (i
=0; i
<patat
.Subscribers
.size(); ++i
)
449 its
= patat
.Subscribers
[i
].SubscriberIterator
;
450 // serialize patatId for this subscriber (different from internal patatid)
451 (*its
).second
.OutsMessage
.serial(patat
.Subscribers
[i
].PatatId
);
452 // serialize vector of CEntityId
453 (*its
).second
.OutsMessage
.serialCont(patat
.Outs
);
457 // if entities entered the patat
458 // notify all subscribers -- serializes in InsMessage patatId and vector of entity ids
459 if (!patat
.Ins
.empty())
461 for (i
=0; i
<patat
.Subscribers
.size(); ++i
)
463 its
= patat
.Subscribers
[i
].SubscriberIterator
;
464 // serialize patatId for this subscriber (different from internal patatid)
465 (*its
).second
.InsMessage
.serial(patat
.Subscribers
[i
].PatatId
);
466 // serialize vector of CEntityId
467 (*its
).second
.InsMessage
.serialCont(patat
.Ins
);
471 // reset modify state
472 patat
.Modified
= false;
473 // and clear temp in/out vectors
478 // reset the list of modified patat
479 _ModifiedPatats
.clear();
481 // and send messages to subscribers (if needed)
482 for (its
=_SubscriberMap
.begin(); its
!=_SubscriberMap
.end(); ++its
)
484 if ((*its
).second
.InsMessage
.length() > (*its
).second
.InsMsgSize
)
485 sendMessageViaMirror((*its
).second
.Service
, (*its
).second
.InsMessage
);
487 if ((*its
).second
.OutsMessage
.length() > (*its
).second
.OutsMsgSize
)
488 sendMessageViaMirror((*its
).second
.Service
, (*its
).second
.OutsMessage
);
496 void CPatatSubscribeManager::displayTriggers(NLMISC::CLog
*log
)
498 log
->displayNL("Registered triggers:");
500 TPatatMap::iterator it
;
501 for (it
=_PatatMap
.begin(); it
!=_PatatMap
.end(); ++it
)
503 CPatat
&patat
= (*it
).second
;
504 log
->displayNL(" - '%s', internal #%d, %d subscribers, %d entities in patat [0 for PACS triggers]", patat
.Name
.c_str(), patat
.InternalPatatId
, patat
.Subscribers
.size(), patat
.StillIns
.size());
507 log
->displayNL("End of registered triggers");
511 * Display info for trigger
513 void CPatatSubscribeManager::displayTriggerInfo(const string
&name
, NLMISC::CLog
*log
)
515 TTriggerIdMap::iterator it
= _TriggerMap
.find(name
);
516 if (it
== _TriggerMap
.end())
518 log
->displayNL("No trigger '%s' registered", name
.c_str());
522 sint32 id
= (*it
).second
;
524 log
->displayNL("Trigger info: '%s', internal #%d", name
.c_str(), id
);
526 TPatatMap::iterator itp
= _PatatMap
.find(id
);
527 if (itp
== _PatatMap
.end())
529 log
->displayNL("Trigger not referenced in patat map, data not consistent !");
530 log
->displayNL("End of trigger info");
534 CPatat
&patat
= (*itp
).second
;
538 log
->displayNL("Subscribers:");
539 for (i
=0; i
<patat
.Subscribers
.size(); ++i
)
540 log
->displayNL(" - %d, userId #d", patat
.Subscribers
[i
].Service
.get(), patat
.Subscribers
[i
].PatatId
);
542 log
->displayNL("Ins:");
543 for (i
=0; i
<patat
.Ins
.size(); ++i
)
544 log
->displayNL(" - %s", patat
.Ins
[i
].toString().c_str());
546 log
->displayNL("Outs:");
547 for (i
=0; i
<patat
.Ins
.size(); ++i
)
548 log
->displayNL(" - %s", patat
.Outs
[i
].toString().c_str());
550 log
->displayNL("Outs:");
551 for (i
=0; i
<patat
.Ins
.size(); ++i
)
552 log
->displayNL(" - %s", patat
.Outs
[i
].toString().c_str());
554 log
->displayNL("StillIns:");
555 set
<NLMISC::CEntityId
>::iterator its
;
556 for (its
=patat
.StillIns
.begin(); its
!=patat
.StillIns
.end(); ++its
)
557 log
->displayNL(" - %s", (*its
).toString().c_str());
559 log
->displayNL("End of trigger info");
563 * Display subscribers
565 void CPatatSubscribeManager::displaySubscribers(NLMISC::CLog
*log
)
567 log
->displayNL("Registered subscribers:");
569 TSubscriberMap::iterator it
;
570 for (it
=_SubscriberMap
.begin(); it
!=_SubscriberMap
.end(); ++it
)
572 CSubscriber
&subscriber
= (*it
).second
;
573 log
->displayNL(" - %d, %d subscribed triggers", subscriber
.Service
.get(), subscriber
.Patats
.size());
576 log
->displayNL("End of registered subscribers");
580 * Display info for subscriber
582 void CPatatSubscribeManager::displaySubscriberInfo(NLNET::TServiceId service
, NLMISC::CLog
*log
)
584 TSubscriberMap::iterator it
= _SubscriberMap
.find(service
);
585 if (it
== _SubscriberMap
.end())
587 log
->displayNL("Service %d no registered", service
.get());
591 log
->displayNL("Subscriber info: %d", service
.get());
593 CSubscriber
&subscriber
= (*it
).second
;
597 log
->displayNL("Subscribed triggers:");
598 for (i
=0; i
<subscriber
.Patats
.size(); ++i
)
599 log
->displayNL(" - internal #%d, subscribed as userId #d", subscriber
.Patats
[i
].InternalPatatId
, subscriber
.Patats
[i
].PatatId
);
601 log
->displayNL("End of subscriber info");