ID:109303
 
Keywords: mud, telnet, tricks
If you're writing a telnet-enabled game on BYOND, you'll probably want to steer away from input(). There are several issues:

1) input() has somewhat of a delay, meaning chained input()'s may get 'missed', causing text to instead be sent to client/Command().

2) input() seems to occasionally cause memory leaks (I can't reproduce this, sadly)

3) input() seems to occasionally cause a 'BUG: TELNET SB malformed' or similar, I don't recall the exact error message.

The benefit to input(), of course, is that it is an easy way to control the flow of the procedure - It'll halt while input() does its thing, and then automatically resume. The same isn't quite possible without input(), not with any sort of sensible API to it.

So I decided to write a replacement system, that works under the assumption that clients can have a 'target' for their commands. If they don't, the regular MUD parser grabs the input the client sent, just like it did before.

If, however, there is a target, it receives the clients input instead. This allowed me to create an Input object, that will wait around and periodically check whether the user has given it input yet. In order to keep the API as simple as possible, it can do a variety of different checks and has a variety of different options to facilitate things.

It supports callbacks (verify input with a function), timing out, verifying repeat input (for passwords, f.ex), restricting valid input to items from a list and more stuff.

The general API to use the Input object looks like this:

var/Input/I = new(player, "Hows about that?!")
var/answer = I.getInput()


The creation of the Input object will block until it has valid input, at which point the second line will run and you will have the input you wanted. Because of this, you're probably always going to want to spawn() before this.

Because there are quite a lot of options, the Input object supports the following syntax:

var/Input/I = new(player, InputSettings object)


This object is a simple object, with procedures to set options individually. As an example of using an InputSettings object:

spawn()
var/InputSettings/IS = new InputSettings()
IS.setQuestion("Do you like this new method? (Y/N)")
IS.setAnswerType(ANSWER_TYPE_LIST)
IS.setAnswerList(list("y","n"))
IS.setCaseSensitive(false)

var/answer = ERROR_INPUT
while(answer == ERROR_INPUT)
var/Input/I = new Input(player, IS)
answer = I.getInput()


Once I'm confident the API is powerful enough to support everything one may reasonably need, and I've tidied up the code, I'll toss it up somewhere where it can be inspected/used.

Right now, my 'form' object uses these to great effect. A form is basically a sequence of questions, that you can exit out of at any time, and go back through if you want to alter an answer. As a complete example of a form that deals with character creation:

            var/form/F = new()

var/InputSettings/S = new()
S.setQuestion("\nWhat is your name? (Type #zexit#n to quit)")
S.setCallback(src, "verify name")
F.addQuestion("name", S)

var/InputSettings/S2 = new()
S2.setQuestion("\nWhat gender would you like to be? \[#zmale#y female#n\] (Hit enter for male)")
S2.setAnswerList(list("male","female"))
S2.setAnswerType(ANSWER_TYPE_LIST)
S2.setDefaultAnswer("male")
F.addQuestion("gender", S2)

var/InputSettings/S3 = new()
S3.setQuestion("\nWhat would you like your password to be?")
S3.setConfirm("\nPlease confirm by typing password again.")
S3.setPassword(TRUE)
S3.setCallback(src, "verify password")
F.addQuestion("password", S3)

var/InputSettings/S4 = new()
S4.setQuestion("\nHit enter to enter the world!")
F.addQuestion("confirm", S4)

F.begin(C)
if(F.isComplete() && C)
var/char_name = F.getAnswer("name")
var/char_pass = F.getAnswer("password")
var/char_gender = F.getAnswer("gender")
Beautiful, though nothing I'd actually use. :)
Just wanted to comment that, as Stephen 001 says, we'll be posting something discussing this soon(TM).

Now that it is nearing Library-state, the actual interface has changed a little (but not much), and the implementation is much more clean. Look out for a DevTalk on this, when the time comes :)