There's a variable that is on the struct. It's the delay variable, but I'm not sure what it's called. wait_state maybe? You'll need to set that to 0, but also where commands are delayed (interp.c?) make an ifcheck where if the command is "stop" then ignore the wait.
The problem is that the commands aren't really properly stacked. Rather the data is left on the socket buffer until wait_state goes to 0, then the next command is processed and interpreted.
What you'd actually need to do, then, would be to check the entire socket buffer for all of its commands, and if ever you find the clear command, then you would clear the entire buffer. It's unfortunately somewhat complicated.
But in C++, this would all be much easier. :-) You would just maintain a stack of commands, and as soon as input comes on the socket (whether or not the character has wait_state) you would stick it onto the stack. And if that command happened to be the clear command, you'd simply clear the stack.
Ahh, that's kind of what I was thinking. It could get pretty laggy, and use up quite an unnecessary amount of CPU for the amount of use the players would get out of it.
As for switching to C++... eek. I can only imagine the number of problems I'd have with that :D.
Perhaps the best thing for me to do in C would be to replace wait_state with timers, and then simply discard all commands while the player has their timer on.
Of course, command stacking is useful in its own way...
Damn ;P.
The CPU overhead is actually relatively minimal. In fact I think you'd hardly notice it. The update function is where the vast majority of CPU usage is, because it is not very smart about how it handles things. (For instance, it iterates over all mobs, not just mobs that have something to update.)
You actually don't need C++ to do the stack stuff, it's just easier because you don't have to get your own stack code. But I realize that most people probably don't want to switch to C++... but really, it's not all that hard. Heh, I had one person tell me once that they didn't want to switch to C++ because of the performance hit... that was pretty funny. :-)
The wait_state model is really quite poor. I'm not sure if timers would work; it would mean a fair amount of work since you would need multiple timers going on. But, it would probably work. I think it would be much easier to just write your own little linked list code that queues up commands, and is processed whenever wait_state hits 0.
I have done what you are talking about, however I have a small drawback I have yet to fix as I am not good at manipulating strings.
void read_from_buffer( DESCRIPTOR_DATA *d )
{
// char log_buf[MAX_STRING_LENGTH];
int i, j, k;
/*
* Hold horses if pending command already.
*/
if ( d->incomm[0] != '\0' )
return;
if ( !str_infix_n( "forget", d->inbuf ) )
{
d->inbuf[0] = '\0';
stc( "You shake your head in a daze.....what was it you were trying to do again?\n\r", d->character );
return;
}
As for that str_infix_n() function I modified str_infix() because I needed to search for other things:
The problem I am having is if someone types in anything that has the word forget at the end of their sentence the function kicks and and clears the command stack, not letting that sentence get though. if anyone can comment on how to fix that problem please do.
You probably want to check for 'forget' being the only content of a command. To do that you would need to check if it is preceeded by a \n, \r or other whitespace, and followed by \n, \r with whitespace in between (potentially). Regular expressions would be incredibly useful here, but in the meantime you can write up your own fairly easily.
I am writing up a very quick example function and will provide it shortly.
Here is my code. I provide this with the disclaimer that I have only very briefly tested it and am not 100% sure that it is 100% correct. But it seems to do the job...
What this function is doing is checking if a buffer (buf, arg1) contains the single command (cmd, arg2).
This condition is considered satisfied when there is a newline, followed by some amount of whitespace, followed by the command, followed by some amount of whitespace, followed by a newline.
If there is enough demand, I will clean this up and release it as a proper snippet, perhaps in the general programming section.
Please give me your feedback. Enjoy. :)
EDIT:
Regarding implementation notes. The astute will notice that this is really just a very simple state machine. I use a trick to be able to have some arbitrarily large command, by having the command states after the fixed ones. If you want more details on how this works, it would probably be best to start a new thread. But the comments I provide might help at least a little bit...
// Does 'buf' contain the single command 'cmd'?
// Defined as: \n or \r then whitespace
// then: cmd
// finally: whitespace then \n or \r
bool str_has_command(const char * buf, const char * cmd)
{
int pos = 0;
int state = 0;
// States:
// 0: newline (\n or \r) (on success --> 1)
// 1: any whitespace (on success --> 3)
// 2: whitespace until newline (on success --> success)
// 3: cmd[0] (on success --> 4)
// 4: cmd[1] (on success --> 5)
// ...: ...
// 3+n: cmd[n] (on success --> 2)
char c;
while ( (c = buf[pos++]) != '\0' )
{
switch ( state )
{
// find initial newline
case 0:
if ( c == '\r' || c == '\n' )
state = 1;
break;
// munch whitespace
case 1:
if ( isspace(c) )
; // munch
else
{
state = 3;
pos--; // reconsider this character
}
break;
// whitespace; stop at newline.
// if we find a non-whitespace, go back to state 0
case 2:
if ( ! isspace(c) )
{
state = 0;
}
if ( c == '\r' || c == '\n' )
return true;
break;
// at state 3, we need to find cmd[0]
// at state 4, we need to find cmd[1]
// etc.
default:
// is this not what we wanted? then go back to zero
if ( c != cmd[state-3] )
{
state = 0;
}
else
{
state++;
// are we at the end of the cmd string?
// if so, move on to final whitespace search.
if ( cmd[state-3] == '\0' )
{
state = 2;
}
}
break;
}
}
return false;
}
That's a pretty nice little trick - definately wouldn't have thought of it myself.
Seems to work well, too - and I think you really should release it; I'm sure plenty of muds would get use out of it.
Just a quick thought - maybe the default case to look for a \n, \r, or whitespace after the cmd, just in case the mud decides to use a command for their stop cmd that has another similar command.
E.g. : Stop is used as stop cmd, whereas they have a command named.. er.. stopfighting, or something.
It's a little bit of a redundant check, but I'm sure will stop the inevitable question popping up.
That's actually taken care of already. :) After completing the cmd match, it moves on to state 2, which only returns true if it hits a newline. It loops over whitespace, and aborts back to the beginning of the match if it ever finds something that is not whitespace.
So if the command we're looking for is 'stop', then 'stopfighting' will not match because 'f' is not whitespace.
Basically, the progression of states is:
0 --> 1 --> 3 --> 4 --> ... --> 3+n --> 2 --> successful termination
where n is the length of the cmd string.
if ( str_has_command( d->inbuf, "forget" ) )
{
d->inbuf[0] = '\0';
stc( "You shake your head in a daze.....what was it you were trying to do again?\n\r", d->character );
return;
}
Thats how it looks in my code, but it does not work. Note, I simply replaced the line where my str_infix_n() function was, so I know its in the right place, and my str_infix_n() function worked before, so I know it should be working where it is, however it doesn't.
I did however fix my previous problem with my str_infix_n() function, here is the working version of it where it doesn't cut off any sentence ending in forget:
bool str_infix_n( const char *astr, const char *bstr )
{
int sstr1;
int sstr2;
int ichar;
char c0;
if ( ( c0 = LOWER(astr[0]) ) == '\0' )
return FALSE;
sstr1 = strlen(astr);
sstr2 = strlen(bstr);
for ( ichar = 0; ichar <= sstr2 - sstr1; ichar++ )
{
if ( c0 == LOWER(bstr[ichar]) && !str_prefix( astr, bstr + ichar ) )
{
if ( bstr[ichar+sstr1] == '\n' || bstr[ichar+sstr1] == '\r' )
{
if ( ichar == 0 ) // No possibility of something coming before it
return FALSE;
else if ( bstr[ichar-1] == '\n' || bstr[ichar-1] == '\r' )
return FALSE;
else if ( bstr[ichar-1] == '\0' ) // just in case
return FALSE;
}
}
}
return TRUE;
}
usage:
if ( !str_infix_n( "forget", d->inbuf ) )
{
d->inbuf[0] = '\0';
stc( "You shake your head in a daze.....what was it you were trying to do again?\n\r", d->character );
return;
}
I'm not sure why it doesn't work because I don't know where you've put it in the code. I tested it on some sample data, and it returns correctly for the four or five test strings I gave it.
By the way, it's a little weird for your function to return false on success and true on failure. The reason that str_cmp returns 0 on success is because it returns -1, 0 or 1. The idiom of writing 'if ( !strcmp("a", "a") )' is actually very horrible because it messes with your mind regarding 'true' and 'false'. That being said, most of the SMAUG functions like str_prefix return false on success, so I guess that at least it's consistently weird... :-)
Also, you could improve the efficiency of this code by jumping ahead by however many characters of prefix there actually were. In your case, if I was searching for the string "aab" in "aaaaaaab", it would waste all kinds of time by examining every character after the first twice. Still, it's probably not worth it for the relatively minor gain you'll see in practice, given how small the search buffers are.
Also, you could improve the efficiency of this code by jumping ahead by however many characters of prefix there actually were. In your case, if I was searching for the string "aab" in "aaaaaaab", it would waste all kinds of time by examining every character after the first twice. Still, it's probably not worth it for the relatively minor gain you'll see in practice, given how small the search buffers are.
Sorry, not exactly sure what you are saying. How is the code I am using inefficient. From what I gathered you are saying, in the case of your example with "aab", I should jump ahead 3 characters each time, and since that sounds stupid I am going to assume that I misinterpreted what you wrote.
And to be honest, its not my code, i just modified str_infix() which was already in my code from smaug.
Also, what does it matter where I put it, I said in the previous post I just commented out where I called my str_infix_n() function and called yours instead. However for arguments sake I wrote a command: do_test()
What I was saying is that the for loop doesn't need to advance one character at a time. It needs to advance, rather, by one character, plus the number of characters that were also matched in the prefix search.
It's a little hard to explain quickly. Basically the problem is that it's potentially examining characters twice. But like I said it's not a big deal in practice.
Anyhow, as for why the function is not working for you is very strange because it's working all happy scrappy for me. Did you copy/paste the code exactly or did you make modifications? How are 'bool', 'true' and 'false' defined in your code, which I assume is C and not C++?
And yes I did a fresh copy/paste before replying just to be sure. As to the other thing, I think I kind of understand what you are trying to say, but its still beyond me. I am a capable mud owner and implementor but advanced progammer I am not, I can't even get a decent grasp on creating a 3dmmorpg!
That's FALSE and TRUE; my code uses false and true. Are those defined for your MUD, and if so, how? Or did you change false and true in my code to FALSE and TRUE?
And for what it's worth, going from a MUD to a 3D MMORPG is a pretty big step! Besides, before going to a 3D MMORPG, you need to try implementing even "just" a MUD code base from scratch. It's a pretty hard task.
If you're curious about the text searching stuff, check out these slides:
http://www.stanford.edu/class/linguist139p/FSAMotivation.pdf
They explain the bit about how to avoid checking letters twice.
Implementing the search as a state machine solves that, except for one little hack that I used because I was lazy (and I noted it in comments).