Up to Main Index                             Up to Journal for April, 2019

                    JOURNAL FOR THURSDAY 25TH APRIL, 2019
______________________________________________________________________________

SUBJECT: Inventory overhaul and other nice things
   DATE: Thu 25 Apr 20:43:35 BST 2019

Recently I found another idle distraction while working on WolfMUD. While
recoding the GET and DROP commands I had spotted a comment I had made in the
code for the Inventory type:


  TODO: A slice for contents is fine for convenience and simplicity but maybe
  a linked list would be better? This would possibly save reslicing in Remove.


“Hrm, I bet I could sort that out now…” I thought to myself ;) Since then I
have reworked the Inventory type to use no less than 4 linked lists. One list
each for Players, Contents, Narratives and Disabled Thing. One improvement of
using linked lists is that most of the time you are just moving a node from
one list to another — which reduces allocations and garbage a lot. Another
benefit is that the code and all of the associated bookkeeping is simplified.

For the implementation I’ve used a doubly linked list with a sentinel head and
tail node. Using sentinel nodes there is no special handling required if the
first or last items are removed. I’ve kept the lists really simple and they
are specifically tuned for use by Inventory. There is a newList function and
only five methods:


  add    - add an item to the head of a list
  remove - removes an item from a list
  move   - move an item from one list and add it to another list
  free   - unlinks the cyclic head and tail references of an empty list
  list   - for debugging only, logs the current internal state of a list


Iterating over a list is simple:


  for n := list.head.next; n.item != nil; n = n.next {
    // forwards
  }

  for n := list.tail.prev; n.item != nil; n = n.prev {
    // reverse
  }


This has removed the need for the constant slicing, re-slicing and split
bookkeeping that previously existed. The ‘Inventory.Compact’ configuration
value is no longer needed and has been dropped[1]. It should be noted that
Inventory.Contents previously returned both players and items, only items are
returned now. Players can be retrieved using Inventory.Players. This posed an
issue as the Players method previously returned true/false if there were any
players in an Inventory. A new Inventory.Occupied method now provides that
functionality.

I’ve also improved the server statistics to create fewer allocations. The
stats now record the number of allocations in a new ‘a[ ]’ section. This is
the server quietly idling with the stock zones loaded, mobiles wandering
around and events firing now and again:


  U[ 2Mb +0b ] A[ +565] O[ 6251  +1] T[ 222  +0] G[ 26  +0] P 0/0
  U[ 2Mb +0b ] A[ +714] O[ 6252  +1] T[ 222  +0] G[ 26  +0] P 0/0
  U[ 2Mb +0b ] A[ +289] O[ 6252  +0] T[ 222  +0] G[ 26  +0] P 0/0
  U[ 2Mb +0b ] A[ +528] O[ 6252  +0] T[ 222  +0] G[ 26  +0] P 0/0
  U[ 2Mb +0b ] A[ +391] O[ 6252  +0] T[ 222  +0] G[ 26  +0] P 0/0
  U[ 2Mb +0b ] A[ +455] O[ 6252  +0] T[ 222  +0] G[ 26  +0] P 0/0

  ^            ^        ^            ^           ^          ^
  |            |        |            |           |          |
  |            |        |            |           |          Current/Max players
  |            |        |            |           Number of Goroutines
  |            |        |            Number of Things in the world
  |            |        Number of Objects
  |            Number of allocations
  Memory usage


For most of the values there is a total and a ±change since the last stat line
displayed. For the allocations, it just shows the number since the last stat
line. In this small sample, which is generating stats every 10 seconds, we are
averaging around 50 allocations a second.

A small cache (buffered channel) has been added to the events code to reduce
the number of new time.Timer created. These seem to be quite expensive to
create so reusing them makes sense. Only a small cache is needed as most of
the time the Timer are in flight and only need caching when an event fires.

A bug has been fixed in the Thing.Collectable method. Players should not be
flagged as collectable by other players :) The GET command has also been
updated so you cannot GET another player anymore :( Part of my commit message:


  Yes, it's fun to pick up another player and put them in the iron bound chest
  or into the pond or the crack in the wall by the shed :) However, it also
  causes a logistical nightmare - you pick up a player and quit, then the mess
  has to be sorted out, a bigger mess if the player was put into a container
  you are carrying :(


If you are feeling brave, all of the changes are now on the public dev branch.

If you have any issues, especially with the new Inventory implementation, then
please let me know: diddymus@wolfmud.org

--
Diddymus

  [1] If you see “Unknown setting INVENTORY.COMPACT:” in the logs and it
      bothers you just delete the Inventory.Compact line from data/config.wrj


  Up to Main Index                             Up to Journal for April, 2019