dystopia2/doc/license/
dystopia2/helps/
Table of contents
-----------------
1. Introduction to linked lists
2. How to use the new linked lists
3. Benefits and drawbacks
4. Performance


1) Introduction to linked lists
-------------------------------
One of the new things in this codebase is how linked lists are handled. In 
the older versions of Dystopia, linked lists where handled inline, and 
each object kept track of the next object in the list.

This had a few drawbacks, since each object could only be part of a single 
list at a time, and should an object be removed from the list, it became a 
hazzle to update the list correctly (which could lead to some problems if 
coded incorrectly - and it is not always possibly to expect any given 
outcome).

So, instead of letting each object keep track of whatever object was next 
(or previous) in the list, we have created a list structure, which keeps 
track of the objects inside the list, and makes sure everything is updated 
correctly every single time.

2) How to use the new linked lists
----------------------------------
To loop through a linked list, we now use an iterator. First allocate the 
iterator for whatever list it is needed, then use the iterator to loop 
through the objects in the list. The following bit of code does a cast to 
the type (CHAR_DATA *) - not all compilers require this, but it certainly 
doesn't hurt, and should you ever wish to move your code to another 
platform, it is always a good idea to have made the casts anyway :)

pIter = AllocIterator(char_list);
while ((ch = (CHAR_DATA *) NextInList(pIter)) != NULL)
{
  // do something with all chars in the char_list
}

If you use GCC as your compiler, you can create a macro for this purpose. 
You will need to remove the -ansi flag in the Makefile, since this is not 
a part of the ANSI standard. In your headerfile define the following macro.

#define LOOP(v, l, i)   \
  i = AllocIterator(l); \
  while ((v = (typeof(v)) NextInList(i)) != NULL)

Now in your code, you can simply type the following to loop through a list:

LOOP(ch, char_list, pIter)
{
  // do something with all chars in char_list
}

Attaching something to a list, and detaching something has become much 
easier. Simply use the following to functions:

AttachToList(ch, char_list);
DetachFromList(ch, char_list);

AttachToList() will add the object to the FRONT of the list. Since this is 
not always what you want, you can use AttachToEndOfList(), 
AttachToListAfterItem() and AttachToListBeforeItem() instead. If you want 
to loop through the list in reverse order, you can use 
AllocReverseIterator() instead of AllocIterator().

Other than that, simply look at some of the code in the codebase to get an 
idea on how the linked lists work.

Also, another structure called a STACK has been added to keep track of the 
"free_lists". It is the same basic principle - so just read the code.

Anyone wanting to tinker with the internals of the linked lists, please 
do, and if you find a way to increase the performance of the linked list 
code, I'd appreciate an email with the details.

3) Benefits and drawbacks
-------------------------
Using the new linked lists is SAFE. Meaning there is no way you can
accidently skip out of the list (this could happen in the old lists,
just look at the following example

for (obj = object_list; obj != NULL; obj = obj->next)
{
  if (Object must be destroyed)
    extract_obj(obj);
}

When the object is extracted, its next pointer will no longer point
to something in object_list, but rather to something in the "free list"
for objects. Several halfcooked workarounds have been used to avoid
this, including the following

for (obj = object_list; obj != NULL; obj = obj_next)
{
  obj_next = obj->next;

  if (Object must be destroyed)
    extract_obj(obj);
}

Certainly now we stay within the object_list, because we know what
object comes next in the list, and we remember this even after
extracting the current object. But what if obj_next is inside
the object being extracted? extract_obj() also extracts all objects
inside the object being extracted, so again we end up in the "free
list" for objects.... Using the Iterator in the new linked lists
will make sure this never happens.

Another benifit is that each object can be part of many linked lists
at the same time. This was not possible with the old system.

The only real drawkback is the overhead with allocating the iterator
and free'ing it later on (this happens automatically btw, so no
need to worry about this).

4) Performance
--------------
currently the linked list structure has a noticable overhead over the
ordinary inline linked lists - with noticable I mean if you allocate
some 15.000 iterators and loop through various lists, it might take as
much as 1-2 seconds (the 'asave world' command does exactly that). In 
normal gametime you will not notice a difference.

To avoid allocating several thousand iterators when booting the game,
and when saving areas, the game uses something called QuickIterators.
These iterators are allocated once, and reused over and over. The
current gamecontrol makes sure that there is no clash of lists, so the
what happens now is safe, but please do not use the QuickIterators for
anything else, unless you know how the system works.

If you want to improve performance, here are a few tips:

1. If you want to get the first or last object in the list, and have no
   use for all the other objects in the list, use the FirstInList()
   and LastInList() calls. These calls work in constant time, with no
   memory usage. For instance, wanting to find the first person in a room:

   /* Version 1 */
   ch = (CHAR_DATA *) FirstInList(pRoom->people);

   /* Version 2 */
   pIter = AllocIterator(pRoom->people);
   ch = (CHAR_DATA *) NextInList(pIter);

   Even though both versions finds the first person in the room, and they
   both work in constant time (more or less), the second version allocates
   memory for an iterator (and substructures), which then needs to be
   free'd later.

2. If you want to detach an object while looping over the list, do not
   use DetachFromList() (even though you could), but instead use the call
   DetachAtIterator(). Like this

   pIter = AllocIterator(char_list);
   while ((ch = (CHAR_DATA *) NextInList(pIter)) != NULL)
   {
       /* Version 1 */
       if (!str_cmp(ch->name, "Jobo"))
           DetachAtIterator(pIter);

       /* Version 2 */
       if (!str_cmp(ch->name, "Jobo"))
           DetachFromList(ch, char_list);
   }

   In the above code, version 1 and version 2 will both result in the
   character named Jobo being removed from the list, but the first
   version works in constant time, where the second version loops through
   the entire list to find the character to remove.


Function                 |Running Time  |Notes               
-------------------------------------------------------------------------------
AttachToList             | O(1)         | attaches to the front of the list
AttachToEndOfList        | O(1)         |
AttachToListBeforeItem   | O(n)         |
AttachToListAfterItem    | O(n)         |
DetachFromList           | O(n)         |
DetachAtIterator         | O(1)         |
NextInList               | O(1)         |
PeekNextInList           | O(1)         | does not move the iterator
FirstInList              | O(1)         | does not require an iterator
LastInList               | O(1)         | does not require an iterator
AllocList                | O(1)         |
AllocIterator            | O(1)         |
AllocReverseIterator     | O(1)         |
AllocQuickIterator       | -- NEVER --  | Do not use this - EVER!
SizeOfList               | O(1)         |
FreeList                 | O(n)         | does not free content