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