1 /** @file thinkers.cpp  World map thinker management.
2  *
3  * @authors Copyright © 2003-2017 Jaakko Keränen <jaakko.keranen@iki.fi>
4  * @authors Copyright © 2006-2015 Daniel Swanson <danij@dengine.net>
5  *
6  * @par License
7  * GPL: http://www.gnu.org/licenses/gpl.html
8  *
9  * <small>This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by the
11  * Free Software Foundation; either version 2 of the License, or (at your
12  * option) any later version. This program is distributed in the hope that it
13  * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
15  * Public License for more details. You should have received a copy of the GNU
16  * General Public License along with this program; if not, see:
17  * http://www.gnu.org/licenses</small>
18  */
19 
20 #define DENG_NO_API_MACROS_THINKER
21 
22 #include "de_base.h"
23 #include "world/thinkers.h"
24 
25 #ifdef __CLIENT__
26 #  include "client/cl_mobj.h"
27 #  include "world/clientmobjthinkerdata.h"
28 #endif
29 
30 #ifdef __SERVER__
31 #  include "def_main.h"
32 #  include "server/sv_pool.h"
33 #  include <doomsday/world/mobjthinkerdata.h>
34 #endif
35 
36 #include "world/map.h"
37 #include "world/p_object.h"
38 
39 #include <de/memoryzone.h>
40 #include <QList>
41 #include <QtAlgorithms>
42 
43 using namespace de;
44 
Thinker_IsMobjFunc(thinkfunc_t func)45 bool Thinker_IsMobjFunc(thinkfunc_t func)
46 {
47     return (func && func == reinterpret_cast<thinkfunc_t>(gx.MobjThinker));
48 }
49 
Thinker_IsMobj(thinker_t const * th)50 bool Thinker_IsMobj(thinker_t const *th)
51 {
52     return (th && Thinker_IsMobjFunc(th->function));
53 }
54 
Thinker_Map(thinker_t const &)55 world::Map &Thinker_Map(thinker_t const & /*th*/)
56 {
57     /// @todo Do not assume the current map.
58     return App_World().map();
59 }
60 
61 namespace world {
62 
63 struct ThinkerList
64 {
65     bool isPublic; ///< All thinkers in this list are visible publically.
66 
67     Thinker sentinel;
68 
ThinkerListworld::ThinkerList69     ThinkerList(thinkfunc_t func, bool isPublic) : isPublic(isPublic)
70     {
71         sentinel.function = func;
72         sentinel.disable(); // Safety measure.
73 
74         sentinel.prev = sentinel.next = sentinel;
75     }
76 
reinitworld::ThinkerList77     void reinit()
78     {
79         sentinel.prev = sentinel.next = sentinel;
80     }
81 
functionworld::ThinkerList82     thinkfunc_t function() const
83     {
84         return sentinel.function;
85     }
86 
87     // Link the thinker to the list.
linkworld::ThinkerList88     void link(thinker_t &th)
89     {
90         sentinel.prev->next = &th;
91         th.next = sentinel;
92         th.prev = sentinel.prev;
93         sentinel.prev = &th;
94     }
95 
countworld::ThinkerList96     dint count(dint *numInStasis) const
97     {
98         dint num = 0;
99         thinker_t *th = sentinel.next;
100         while (th != &sentinel.base() && th)
101         {
102 #ifdef LIBDENG_FAKE_MEMORY_ZONE
103             DENG2_ASSERT(th->next);
104             DENG2_ASSERT(th->prev);
105 #endif
106             num += 1;
107             if (numInStasis && Thinker_InStasis(th))
108             {
109                 (*numInStasis) += 1;
110             }
111             th = th->next;
112         }
113         return num;
114     }
115 
releaseAllworld::ThinkerList116     void releaseAll()
117     {
118         for (thinker_t *th = sentinel.next; th != &sentinel.base() && th; th = th->next)
119         {
120             Thinker::release(*th);
121         }
122     }
123 };
124 
DENG2_PIMPL(Thinkers)125 DENG2_PIMPL(Thinkers)
126 {
127     dint idtable[2048];     ///< 65536 bits telling which IDs are in use.
128     dushort iddealer = 0;
129 
130     QList<ThinkerList *> lists;
131     QHash<thid_t, mobj_t *> mobjIdLookup;  ///< public only
132     QHash<thid_t, thinker_t *> thinkerIdLookup; ///< all thinkers with ID
133 
134     bool inited = false;
135 
136     Impl(Public *i) : Base(i)
137     {
138         clearMobjIds();
139     }
140 
141     ~Impl()
142     {
143         // Make sure the private instances of thinkers are released.
144         releaseAllThinkers();
145 
146         // Note that most thinkers are allocated from the memory zone
147         // so there is no memory leak here as this memory will be purged
148         // automatically when the map is "unloaded".
149         qDeleteAll(lists);
150     }
151 
152     void releaseAllThinkers()
153     {
154         thinkerIdLookup.clear();
155         for (ThinkerList *list : lists)
156         {
157             list->releaseAll();
158         }
159     }
160 
161     void clearMobjIds()
162     {
163         de::zap(idtable);
164         idtable[0] |= 1;  // ID zero is always "used" (it's not a valid ID).
165 
166         mobjIdLookup.clear();
167         thinkerIdLookup.clear();
168     }
169 
170     thid_t newMobjId()
171     {
172         // Increment the ID dealer until a free ID is found.
173         /// @todo fixme: What if all IDs are in use? 65535 thinkers!?
174         while (self().isUsedMobjId(++iddealer)) {}
175 
176         // Mark this ID as used.
177         self().setMobjId(iddealer);
178 
179         return iddealer;
180     }
181 
182     ThinkerList *listForThinkFunc(thinkfunc_t func, bool makePublic = true,
183                                   bool canCreate = false)
184     {
185         for (dint i = 0; i < lists.count(); ++i)
186         {
187             ThinkerList *list = lists[i];
188             if (list->function() == func && list->isPublic == makePublic)
189                 return list;
190         }
191 
192         if (!canCreate) return nullptr;
193 
194         // A new thinker type.
195         lists.append(new ThinkerList(func, makePublic));
196         return lists.last();
197     }
198 };
199 
Thinkers()200 Thinkers::Thinkers() : d(new Impl(this))
201 {}
202 
isUsedMobjId(thid_t id)203 bool Thinkers::isUsedMobjId(thid_t id)
204 {
205     return d->idtable[id >> 5] & (1 << (id & 31) /*(id % 32) */ );
206 }
207 
setMobjId(thid_t id,bool inUse)208 void Thinkers::setMobjId(thid_t id, bool inUse)
209 {
210     dint c = id >> 5, bit = 1 << (id & 31); //(id % 32);
211 
212     if (inUse) d->idtable[c] |= bit;
213     else       d->idtable[c] &= ~bit;
214 }
215 
mobjById(dint id)216 struct mobj_s *Thinkers::mobjById(dint id)
217 {
218     auto found = d->mobjIdLookup.constFind(id);
219     if (found != d->mobjIdLookup.constEnd())
220     {
221         return found.value();
222     }
223     return nullptr;
224 }
225 
find(thid_t id)226 thinker_t *Thinkers::find(thid_t id)
227 {
228     auto found = d->thinkerIdLookup.constFind(id);
229     if (found != d->thinkerIdLookup.constEnd())
230     {
231         return found.value();
232     }
233     return nullptr;
234 }
235 
add(thinker_t & th,bool makePublic)236 void Thinkers::add(thinker_t &th, bool makePublic)
237 {
238     if (!th.function)
239         throw Error("Thinkers::add", "Invalid thinker function");
240 
241     // Will it need an ID?
242     if (Thinker_IsMobj(&th))
243     {
244         // It is a mobj, give it an ID (not for client mobjs, though, they
245         // already have an id).
246 #ifdef __CLIENT__
247         if (!Cl_IsClientMobj(reinterpret_cast<mobj_t *>(&th)))
248 #endif
249         {
250             th.id = d->newMobjId();
251         }
252 
253         if (makePublic && th.id)
254         {
255             d->mobjIdLookup.insert(th.id, reinterpret_cast<mobj_t *>(&th));
256         }
257     }
258     else
259     {
260         th.id = 0;  // Zero is not a valid ID.
261     }
262 
263     if (th.id)
264     {
265         d->thinkerIdLookup.insert(th.id, &th);
266     }
267 
268     // Link the thinker to the thinker list.
269     ThinkerList *list = d->listForThinkFunc(th.function, makePublic, true /*can create*/);
270     list->link(th);
271 }
272 
remove(thinker_t & th)273 void Thinkers::remove(thinker_t &th)
274 {
275     // Has got an ID?
276     if (th.id)
277     {
278         // Flag the identifier as free.
279         setMobjId(th.id, false);
280 
281         d->mobjIdLookup.remove(th.id);
282         d->thinkerIdLookup.remove(th.id);
283 
284 #ifdef __SERVER__
285         // Then it must be a mobj.
286         auto *mob = reinterpret_cast<mobj_t *>(&th);
287 
288         // If the state of the mobj is the NULL state, this is a
289         // predictable mobj removal (result of animation reaching its
290         // end) and shouldn't be included in netGame deltas.
291         if (!mob->state || !runtimeDefs.states.indexOf(mob->state))
292         {
293             Sv_MobjRemoved(th.id);
294         }
295 #endif
296     }
297 
298     th.function = (thinkfunc_t) -1;
299 
300     Thinker::release(th);
301 }
302 
initLists(dbyte flags)303 void Thinkers::initLists(dbyte flags)
304 {
305     if (!d->inited)
306     {
307         d->lists.clear();
308     }
309     else
310     {
311         for (dint i = 0; i < d->lists.count(); ++i)
312         {
313             ThinkerList *list = d->lists[i];
314 
315             if (list->isPublic && !(flags & 0x1)) continue;
316             if (!list->isPublic && !(flags & 0x2)) continue;
317 
318             list->reinit();
319         }
320     }
321 
322     d->clearMobjIds();
323     d->inited = true;
324 }
325 
isInited() const326 bool Thinkers::isInited() const
327 {
328     return d->inited;
329 }
330 
forAll(dbyte flags,std::function<LoopResult (thinker_t *)> func) const331 LoopResult Thinkers::forAll(dbyte flags, std::function<LoopResult (thinker_t *)> func) const
332 {
333     if (!d->inited) return LoopContinue;
334 
335     for (dint i = 0; i < d->lists.count(); ++i)
336     {
337         ThinkerList *list = d->lists[i];
338 
339         if ( list->isPublic && !(flags & 0x1)) continue;
340         if (!list->isPublic && !(flags & 0x2)) continue;
341 
342         thinker_t *th = list->sentinel.next;
343         while (th != &list->sentinel.base() && th)
344         {
345 #ifdef LIBDENG_FAKE_MEMORY_ZONE
346             DENG2_ASSERT(th->next);
347             DENG2_ASSERT(th->prev);
348 #endif
349             thinker_t *next = th->next;
350 
351             if (auto result = func(th))
352                 return result;
353 
354             th = next;
355         }
356     }
357 
358     return LoopContinue;
359 }
360 
forAll(thinkfunc_t thinkFunc,dbyte flags,std::function<LoopResult (thinker_t *)> func) const361 LoopResult Thinkers::forAll(thinkfunc_t thinkFunc, dbyte flags, std::function<LoopResult (thinker_t *)> func) const
362 {
363     if (!d->inited) return LoopContinue;
364 
365     if (!thinkFunc)
366     {
367         return forAll(flags, func);
368     }
369 
370     if (flags & 0x1 /*public*/)
371     {
372         if (ThinkerList *list = d->listForThinkFunc(thinkFunc))
373         {
374             thinker_t *th = list->sentinel.next;
375             while (th != &list->sentinel.base() && th)
376             {
377 #ifdef LIBDENG_FAKE_MEMORY_ZONE
378                 DENG2_ASSERT(th->next);
379                 DENG2_ASSERT(th->prev);
380 #endif
381                 thinker_t *next = th->next;
382 
383                 if (auto result = func(th))
384                     return result;
385 
386                 th = next;
387             }
388         }
389     }
390     if (flags & 0x2 /*private*/)
391     {
392         if (ThinkerList *list = d->listForThinkFunc(thinkFunc, false /*private*/))
393         {
394             thinker_t *th = list->sentinel.next;
395             while (th != &list->sentinel.base() && th)
396             {
397 #ifdef LIBDENG_FAKE_MEMORY_ZONE
398                 DENG2_ASSERT(th->next);
399                 DENG2_ASSERT(th->prev);
400 #endif
401                 thinker_t *next = th->next;
402 
403                 if (auto result = func(th))
404                     return result;
405 
406                 th = next;
407             }
408         }
409     }
410 
411     return LoopContinue;
412 }
413 
count(dint * numInStasis) const414 dint Thinkers::count(dint *numInStasis) const
415 {
416     dint total = 0;
417     if (isInited())
418     {
419         for (dint i = 0; i < d->lists.count(); ++i)
420         {
421             ThinkerList *list = d->lists[i];
422             total += list->count(numInStasis);
423         }
424     }
425     return total;
426 }
427 
unlinkThinkerFromList(thinker_t * th)428 static void unlinkThinkerFromList(thinker_t *th)
429 {
430     th->next->prev = th->prev;
431     th->prev->next = th->next;
432 }
433 
434 }  // namespace world
435 using namespace world;
436 
Thinker_InitPrivateData(thinker_t * th,Id::Type knownId)437 void Thinker_InitPrivateData(thinker_t *th, Id::Type knownId)
438 {
439     //DENG2_ASSERT(th->d == nullptr);
440 
441     /// @todo The game should be asked to create its own private data. -jk
442 
443     if (th->d == nullptr)
444     {
445         Id const privateId = knownId? Id(knownId) : Id(/* get a new ID */);
446 
447         if (Thinker_IsMobj(th))
448         {
449 #ifdef __CLIENT__
450             th->d = new ClientMobjThinkerData(privateId);
451 #else
452             th->d = new MobjThinkerData(privateId);
453 #endif
454         }
455         else
456         {
457             // Generic thinker data (Doomsday Script namespace, etc.).
458             th->d = new ThinkerData(privateId);
459         }
460 
461         auto &thinkerData = THINKER_DATA(*th, ThinkerData);
462         thinkerData.setThinker(th);
463         thinkerData.initBindings();
464     }
465     else
466     {
467         DENG2_ASSERT(knownId != 0);
468 
469         // Change the private identifier of the existing thinker data.
470         THINKER_DATA(*th, ThinkerData).setId(knownId);
471     }
472 }
473 
474 /**
475  * Locates a mobj by it's unique identifier in the CURRENT map.
476  */
477 #undef Mobj_ById
Mobj_ById(dint id)478 DENG_EXTERN_C struct mobj_s *Mobj_ById(dint id)
479 {
480     /// @todo fixme: Do not assume the current map.
481     if (!App_World().hasMap()) return nullptr;
482     return App_World().map().thinkers().mobjById(id);
483 }
484 
485 #undef Thinker_Init
Thinker_Init()486 void Thinker_Init()
487 {
488     /// @todo fixme: Do not assume the current map.
489     if (!App_World().hasMap()) return;
490     App_World().map().thinkers().initLists(0x1);  // Init the public thinker lists.
491 }
492 
493 #undef Thinker_Run
Thinker_Run()494 void Thinker_Run()
495 {
496     /// @todo fixme: Do not assume the current map.
497     if (!App_World().hasMap()) return;
498 
499     App_World().map().thinkers().forAll(0x1 | 0x2, [] (thinker_t *th)
500     {
501         try
502         {
503             if (Thinker_InStasis(th)) return LoopContinue; // Skip.
504 
505             // Time to remove it?
506             if (th->function == (thinkfunc_t) -1)
507             {
508                 unlinkThinkerFromList(th);
509 
510                 if (th->id)
511                 {
512                     // Recycle for reduced allocation overhead.
513                     P_MobjRecycle((mobj_t *) th);
514                 }
515                 else
516                 {
517                     // Non-mobjs are just deleted right away.
518                     Thinker::destroy(th);
519                 }
520             }
521             else if (th->function)
522             {
523                 // Create a private data instance of appropriate type.
524                 if (!th->d) Thinker_InitPrivateData(th);
525 
526                 // Public thinker callback.
527                 th->function(th);
528 
529                 // Private thinking.
530                 if (th->d) THINKER_DATA(*th, Thinker::IData).think();
531             }
532         }
533         catch (const Error &er)
534         {
535             LOG_MAP_WARNING("Thinker %i: %s") << th->id << er.asText();
536         }
537         return LoopContinue;
538     });
539 }
540 
541 #undef Thinker_Add
Thinker_Add(thinker_t * th)542 void Thinker_Add(thinker_t *th)
543 {
544     if (!th) return;
545     Thinker_Map(*th).thinkers().add(*th);
546 }
547 
548 #undef Thinker_Remove
Thinker_Remove(thinker_t * th)549 void Thinker_Remove(thinker_t *th)
550 {
551     if (!th) return;
552     Thinker_Map(*th).thinkers().remove(*th);
553 }
554 
555 #undef Thinker_Iterate
Thinker_Iterate(thinkfunc_t func,dint (* callback)(thinker_t *,void *),void * context)556 dint Thinker_Iterate(thinkfunc_t func, dint (*callback) (thinker_t *, void *), void *context)
557 {
558     if (!App_World().hasMap()) return false;  // Continue iteration.
559 
560     return App_World().map().thinkers().forAll(func, 0x1, [&callback, &context] (thinker_t *th)
561     {
562         return callback(th, context);
563     });
564 }
565 
566 DENG_DECLARE_API(Thinker) =
567 {
568     { DE_API_THINKER },
569     Thinker_Init,
570     Thinker_Run,
571     Thinker_Add,
572     Thinker_Remove,
573     Thinker_Iterate
574 };
575