28 Mar, 2010, Sorressean wrote in the 1st comment:
Votes: 0
Hello all,
I've got a bit of a design issue. My server class uses select to find out which sockets sent data, then it receives that data and passes it to the command parser. I wanted a way to show menus and etc, so what I did was create a command handler, which is just a function pointer. If that is set on the socket, command input will be sent to the callback. Now, this works fine for something like an editor, but I'm having a bit of trouble with setting something up..an input handler that will get called for yes/no questions, or one-line input questions. I can set the handler and make it set the text on a pointer that is passed, (so I'd just pass a string as the arg), but here's the problem. Wihle I'm waiting for input, I can't just run in a single loop waiting for the player to input something, because that affectively locks up the rest of the server. Is there a cleaner way of doing this?
28 Mar, 2010, flumpy wrote in the 2nd comment:
Votes: 0
I think before anyone actually offers a solution we need a bit more info. What language are you using? Is this your own implementation or some well-known code base?

I am guessing this is a general problem with any non-threaded socket implementation. I am also tempted to say you can use a separate thread (or whatever is the equivalent in your language) to wait in. The main problem with this is then you have to watch for concurrency errors and the like. The other solution is to use non-blocking IO. But before I say "this is the solution" I think I'll wait for your reply..

(edit to include nonblockingio, i forgot about that.)
28 Mar, 2010, David Haley wrote in the 3rd comment:
Votes: 0
Don't use concurrent threads with blocking if you can avoid it: you will only make your life more complicated and it's more work to get it right. Non-blocking I/O is essentially concurrency, and I'm not sure what benefit it brings here. What you're really trying to achieve is to block some portion of your MUD's logic until more input arrives.

Some languages have support for cooperative multithreading where threads (typically called coroutines or something similar) explicitly hand off control to each other. This makes it very easy and clean to implement these "input handlers" you're speaking of.

In Pythonish pseudo-code leaving off details like making sure the input is correct etc.:
while True:
player.send("What is your name?")
name = player.getInput()
player.send("Your name is %s, is that correct? [Y/N]" % name)
if player.getInput() == "Y":
break
else:
player.send("OK, let's try again…")


Behind the scenes getInput relinquishes control back to the main coroutine, which marks the connection's handler as being in a waiting state; the next time input comes in, it notes that the connection is waiting for input and so resumes the coroutine with that input as the return result to getInput.

You can of course implement this with concurrent threads, you just need to be very careful that as soon as you wake up one thread, you start waiting. You really don't want to have to deal with true concurrency unless it's what you actually need (and in this instance, it's not what you need).

If you don't have these approaches available or don't want to use them, you need to store the state yourself in your connections. This is basically what SMAUG (and perhaps the rest of the Diku family, I haven't looked) does to solve this problem; it has this notion of a substate, and when you call a function that cares about state it looks at your substate. The do_search function is an example of this: when you search the first time, it sets up your substate so that the next time you 'search' (which is actually a timer firing calling the do_search function again) it sees that your substate is such that it should be completing the search. Functions having to do with OLC work similarly: the first time you call the buffer editing function, it sets up your substate such that the second time you call it, it pulls the text from your buffer and puts it wherever.
(As a caveat, the way they handle this in terms of implementation is fairly dirty, but the concept is a useful one.)
28 Mar, 2010, donky wrote in the 4th comment:
Votes: 0
Sorressean said:
I've got a bit of a design issue. My server class uses select to find out which sockets sent data, then it receives that data and passes it to the command parser. I wanted a way to show menus and etc, so what I did was create a command handler, which is just a function pointer. If that is set on the socket, command input will be sent to the callback. Now, this works fine for something like an editor, but I'm having a bit of trouble with setting something up..an input handler that will get called for yes/no questions, or one-line input questions. I can set the handler and make it set the text on a pointer that is passed, (so I'd just pass a string as the arg), but here's the problem. Wihle I'm waiting for input, I can't just run in a single loop waiting for the player to input something, because that affectively locks up the rest of the server. Is there a cleaner way of doing this?

I use something similar to coroutines as well. But I personally believe that the code a programmer writes should be as natural as possible.

In order to do this, I have replacement socket functionality. It looks exactly like the standard socket functionality, but instead of doing the networking functionality directly, what it does is set aside the current thread of execution until the networking operation has completed. The replacement socket functionality provides an interface to my handler, within which select runs. So a call to recv for instance will set aside the current thread of execution as a blocked microthread, and will register that microthread as needing to be awakened when the socket the recv call is associated with has incoming data.

The new Go language from Google takes a very similar approach, but it has a custom and different networking interface aimed at being more usable than the standard socket one.
0.0/4