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