Up to Main Index                          Up to Journal for November, 2016

                   JOURNAL FOR THURSDAY 10TH NOVEMBER, 2016
______________________________________________________________________________

SUBJECT: A text regression and buffers
   DATE: Thu 10 Nov 23:09:51 GMT 2016

Some people pointed out to me that I haven't pushed out the changes to the
text package yet. The simple reason is that I found a regression introduced by
my recent changes. If there is embedded whitespace   like   this   it is lost
and reduced to a single space when it is folded. This can be an issue if
whitespace is being used for layout - the COMMANDS command does this to layout
columns. I don't have a fix ready yet so I've held back on the changes.

The fix I did push out for Windows TELNET users seems to be doing it's job and
making users happy again :)

Having thought I'd finished with the text package for now I started looking at
colours. In doing so I found a number of issues with the implementation of
buffers used to accumulate messages to be sent to the actor, participant and
observers. The buffers are pretty fundamental in WolfMUD and batch data
together so that it can be sent as a single write and you don't get messages
chopping up other messages depending on when they are sent. I've also had
some, less than glorious, comments on their implementation as well.

The buffers were originally based on a bytes.Buffer. The thinking was I could
expose the bytes.Buffer via anonymous embedding and add some custom methods.
That way developers would have ultimate flexibility to do with buffers what
they will. Turns out all people really want is ultimate simplicity :(

Therefore, I have gone back and reimplemented buffers. They are now based on a
simple []byte and have Send and Append methods for writing messages. You also
don't get to play with the internals, in fact it's been moved out of state.go
and into cmd/internal as buffer.go.

The Send method takes a number of strings - for convenience instead of having
to concatenate parts of messages together - and manages line feed handling and
everything for you, so a message will always start on its own new line. If you
need to compose a message in pieces you can use Append to add messages to the
end of another without them starting on new lines. An example of this is the
look command where we need the description, what's there and the exits.

All of the renaming has been done mechanically using gofmt. For example:


  gofmt -w -r 'a.WriteStrings->a.Send' cmd/


All of the gofmt commands I used are in the commit messages so that others can
mechanically update their code as well without losing changes.

Buffers now have a silent mode. If set to true the buffer will ignore all Send
and Append calls. This is useful when scripting commands and you don't want to
see their output. For example, the MOVE command scripts the LOOK command to
describe the new location. However observers do not get a "Diddymus starts
looking around." every time Diddymus enters a location.

Buffers will also use the silent feature automatically for crowded locations.
This means we don't needlessly store messages in the buffers just to throw
them away.

There are also Send, Append and Silent methods for groups of buffers. So
sending a message to an actor s.msg.Actor.Send("Hi!") is the same as sending a
message to all observers s.msg.Observers.Send("Hi!"). Previously you had to
iterate over Observers and send to each one individually.

Groups of buffers also have a new Filter method. For example, if you have a
group of buffers for all locations within 2 moves of the current location you
can filter those at 1 move and at 2 moves. The SNEEZE command is an example of
how to do this. The guts of SNEEZE of interest to us are:


  // Get all location inventories within 2 moves of current location
  locations := attr.FindExits(s.where.Parent()).Within(2)

  // Notify observers in near by locations
  s.msg.Observers.Filter(locations[1]...).Send("You hear a loud sneeze.")

  // Notify observers in further out locations
  s.msg.Observers.Filter(locations[2]...).Send("You hear a sneeze.")


One implementation issue that has been bugging me for a while is the fact that
a buffer may be considered empty even when it has some data in it. Consider,
when a player enters a command they press enter moving them to the next line.
Then messages can just be sent back to the player. For any participants or
observers they are still on their prompt line and we need to send a line feed
before the message is sent to move them off of it.

To accomplish this, participant and observer buffers were preloaded with a
line feed. This meant that a buffer with no messages for the initiating player
(actor) was Len() == 0 but for participants and observers was Len() == 1. This
proved a little confusing and was easy to forget. The new implementation does
away with all that and handles it automatically.

The buffer work is nearly complete and will probably hit the public dev branch
before the text changes. Just working on some remaining buffer tests first...

Yes, I know some people find it annoying when I write a journal entry like
this, but they can't actually play with the code yet because I haven't pushed
it out yet. However it's easier for me to write these types of entries while
the details are still fresh in my mind. The alternative would be to write the
entry in advance and delay publishing it until the code was ready :(

--
Diddymus


  Up to Main Index                          Up to Journal for November, 2016