|
1. CircleMUD: Getting Input from the Player (Any ideas?)
|
|
Fri Jan 29, 2010 [5:25 PM]
|
Krysis
keno2500@yahoo.com
member since: Dec 8, 2005
|
Reply
|
|
Hey all,
I can't for the life of me figure out how to collect input from the player (and eventually use this). I've been scanning over the various pieces of code bits to try and understand what exactly is going on when the player provides input during character creation (ie. Your Name:, Password: , etc.) as well as when an immortal is adding room descriptions (eg. Enter room description:)... but, I haven't figured out how to use the various functions to wait for input from the player, place that input in a buffer/char array, and use it.
I've taken a look at the following functions/structs (which essentially had me running in circles):
int process_input(struct descriptor_data *t); (which led me to) int get_from_q(struct txt_q *queue, char *dest, int *aliased); (which led to...) struct txt_q (which led to...) struct txt_block (which led to...) void write_to_q(const char *txt, struct txt_q *queue, int aliased);
I also tried my hand at understanding how these file descriptors were being used: FD_ZERO(&input_set); // Which I'm assuming clears the &input_set file descriptor, where input_set is // of type fd_set. FD_SET(d->descriptor, &input_set); // I didn't quite understand what this did.. but I figured I'd need it.
Anyway, all I'm really trying to do is ask the player a series of questions that require input (for example: where "->" denotes the player's input to the game)
"Do you want to start this random adventure: (Yes/No)" -> Yes "What adventure do you wish to begin? " -> The super Cool adventure "Alright, starting adventure: The Super Cool Adventure"
Anyway, let me post some of the structs/function code that I mentioned earlier so that you can have a deeper understanding of what they do.
/////// START CODE SNIPPETS //////////////// int get_from_q(struct txt_q *queue, char *dest, int *aliased) { struct txt_block *tmp;
/* queue empty? */ if (!queue->head) return 0;
tmp = queue->head; strcpy(dest, queue->head->text); *aliased = queue->head->aliased; queue->head = queue->head->next;
free(tmp->text); free(tmp);
return 1; }
struct txt_block { char *text; int aliased; struct txt_block *next; };
struct txt_q { struct txt_block *head; struct txt_block *tail; };
void write_to_q(const char *txt, struct txt_q *queue, int aliased) { struct txt_block *newt;
CREATE(newt, struct txt_block, 1); CREATE(newt->text, char, strlen(txt) + 1); strcpy(newt->text, txt); newt->aliased = aliased;
/* queue empty? */ if (!queue->head) { newt->next = NULL; queue->head = queue->tail = newt; } else { queue->tail->next = newt; queue->tail = newt; newt->next = NULL; } }
#ifdef __CXREF__ #undef FD_ZERO #undef FD_SET #undef FD_ISSET #undef FD_CLR #define FD_ZERO(x) #define FD_SET(x, y) 0 #define FD_ISSET(x, y) 0 #define FD_CLR(x, y) #endif
/////// END CODE SNIPPETS ////////////////
The process_input() snippet is rather lengthy, and if you would like for me to post that as well... let me know! Or email me at: keno2500@yahoo.com or we can AIM or talk on MSN! I'll be happy to solve this FRUSTRATING problem! Thanks!
|
|
|
|
|
2. RE: CircleMUD: Getting Input from the Player (Any ideas?)
|
|
Fri Jan 29, 2010 [10:40 PM]
|
Pymeus
Email not supplied
member since: Oct 3, 2008
|
In Reply To
Reply
|
|
Most kinds of server programs use input polling to control input. In C this is usually accomplished with either the select() or poll() system calls. Most programs, like Circle, use select(). Catch up on the select(2) manpage for operational details and an explanation of the FD_ series of macros. Note that the descriptor struct defined by Circle is not a file descriptor, but it does contain one.
Select and poll functions, with the help of their macros, stop the program and wait for state changes in a set of file descriptors, or for a certain amount of time to pass. You can tune what state changes it looks for, but normally you're waiting for a file descriptor to receive new data or to send enough data that you can write() more out. The other important state changes are usually network error conditions.
The queues in Circle are text buffers. When a file descriptor is too full to send more data, due to network congestion for example, the queues hold on to text until more can be sent.
As far as asking questions, there's a function pointer in Circle that points to the current parser. This technique is called a function callback. Normally it's the main command parser, but the are also separate parsers for the questions asked at login and char creation. Every time the game gets text from a player, it feeds that text through the player's current parser.
I hope that gets you started, and if you have more specific questions I'll try to answer them.
(Comment added by Pymeus on Sat Jan 30 0:48:01 2010)
From the high level programmer's perspective you don't so much request the input from the player, it is passed to your parser in function calls which then passes it to other functions.
Oh, and process_input is not one I remember from my Circle days, sorry.
|
|
|
|
|
3. RE: CircleMUD: Getting Input from the Player (Any ideas?)
|
|
Sat Jan 30, 2010 [12:18 AM]
|
Drizzt1216
Email not supplied
member since: Aug 12, 2005
|
In Reply To
Reply
|
Oh, and process_input is not one I remember from my Circle days, sorry.
That's probably to be expected, it's in comm.c along with game_loop and several other rarely edited functions. I'm going to assume that this is what he has, though the below is actually from tbaMUD.
static int process_input(struct descriptor_data *t)
{
int buf_length, failed_subst;
ssize_t bytes_read;
size_t space_left;
char *ptr, *read_point, *write_point, *nl_pos = NULL;
char tmp[MAX_INPUT_LENGTH];
/* first, find the point where we left off reading data */
buf_length = strlen(t->inbuf);
read_point = t->inbuf + buf_length;
space_left = MAX_RAW_INPUT_LENGTH - buf_length - 1;
do {
if (space_left <= 0) {
log("WARNING: process_input: about to close connection: input overflow");
return (-1);
}
bytes_read = perform_socket_read(t->descriptor, read_point, space_left);
if (bytes_read < 0) /* Error, disconnect them. */
return (-1);
else if (bytes_read == 0) /* Just blocking, no problems. */
return (0);
/* at this point, we know we got some data from the read */
*(read_point + bytes_read) = '\0'; /* terminate the string */
/* search for a newline in the data we just read */
for (ptr = read_point; *ptr && !nl_pos; ptr++)
if (ISNEWL(*ptr))
nl_pos = ptr;
read_point += bytes_read;
space_left -= bytes_read;
/* on some systems such as AIX, POSIX-standard nonblocking I/O is broken,
* causing the MUD to hang when it encounters input not terminated by a
* newline. This was causing hangs at the Password: prompt, for example.
* I attempt to compensate by always returning after the _first_ read, instead
* of looping forever until a read returns -1. This simulates non-blocking
* I/O because the result is we never call read unless we know from select()
* that data is ready (process_input is only called if select indicates that
* this descriptor is in the read set). JE 2/23/95. */
#if !defined(POSIX_NONBLOCK_BROKEN)
} while (nl_pos == NULL);
#else
} while (0);
if (nl_pos == NULL)
return (0);
#endif /* POSIX_NONBLOCK_BROKEN */
/* okay, at this point we have at least one newline in the string; now we
* can copy the formatted data to a new array for further processing. */
read_point = t->inbuf;
while (nl_pos != NULL) {
write_point = tmp;
space_left = MAX_INPUT_LENGTH - 1;
/* The '> 1' reserves room for a '$ => $$' expansion. */
for (ptr = read_point; (space_left > 1) && (ptr < nl_pos); ptr++) {
if (*ptr == '\b' || *ptr == 127) { /* handle backspacing or delete key */
if (write_point > tmp) {
if (*(--write_point) == '$') {
write_point--;
space_left += 2;
} else
space_left++;
}
} else if (isascii(*ptr) && isprint(*ptr)) {
if ((*(write_point++) = *ptr) == '$') { /* copy one character */
*(write_point++) = '$'; /* if it's a $, double it */
space_left -= 2;
} else
space_left--;
}
}
*write_point = '\0';
if ((space_left <= 0) && (ptr < nl_pos)) {
char buffer[MAX_INPUT_LENGTH + 64];
snprintf(buffer, sizeof(buffer), "Line too long. Truncated to:\r\n%s\r\n", tmp);
if (write_to_descriptor(t->descriptor, buffer) < 0)
return (-1);
}
if (t->snoop_by)
write_to_output(t->snoop_by, "%% %s\r\n", tmp);
failed_subst = 0;
if (*tmp == '!' && !(*(tmp + 1))) /* Redo last command. */
strcpy(tmp, t->last_input); /* strcpy: OK (by mutual MAX_INPUT_LENGTH) */
else if (*tmp == '!' && *(tmp + 1)) {
char *commandln = (tmp + 1);
int starting_pos = t->history_pos,
cnt = (t->history_pos == 0 ? HISTORY_SIZE - 1 : t->history_pos - 1);
skip_spaces(&commandln);
for (; cnt != starting_pos; cnt--) {
if (t->history[cnt] && is_abbrev(commandln, t->history[cnt])) {
strcpy(tmp, t->history[cnt]); /* strcpy: OK (by mutual MAX_INPUT_LENGTH) */
strcpy(t->last_input, tmp); /* strcpy: OK (by mutual MAX_INPUT_LENGTH) */
write_to_output(t, "%s\r\n", tmp);
break;
}
if (cnt == 0) /* At top, loop to bottom. */
cnt = HISTORY_SIZE;
}
} else if (*tmp == '^') {
if (!(failed_subst = perform_subst(t, t->last_input, tmp)))
strcpy(t->last_input, tmp); /* strcpy: OK (by mutual MAX_INPUT_LENGTH) */
} else {
strcpy(t->last_input, tmp); /* strcpy: OK (by mutual MAX_INPUT_LENGTH) */
if (t->history[t->history_pos])
free(t->history[t->history_pos]); /* Clear the old line. */
t->history[t->history_pos] = strdup(tmp); /* Save the new. */
if (++t->history_pos >= HISTORY_SIZE) /* Wrap to top. */
t->history_pos = 0;
}
/* The '--' command flushes the queue. */
if ( (*tmp == '-') && (*(tmp+1) == '-') && !(*(tmp+2)) )
{
write_to_output(t, "All queued commands cancelled.\r\n");
flush_queues(t); /* Flush the command queue */
failed_subst = 1; /* Allow the read point to be moved, but don't add to queue */
}
if (!failed_subst)
write_to_q(tmp, &t->input, 0);
/* find the end of this line */
while (ISNEWL(*nl_pos))
nl_pos++;
/* see if there's another newline in the input buffer */
read_point = ptr = nl_pos;
for (nl_pos = NULL; *ptr && !nl_pos; ptr++)
if (ISNEWL(*ptr))
nl_pos = ptr;
}
/* now move the rest of the buffer up to the beginning for the next pass */
write_point = t->inbuf;
while (*read_point)
*(write_point++) = *(read_point++);
*write_point = '\0';
return (1);
}
|
|
|
|
|
4. RE: CircleMUD: Getting Input from the Player (Any ideas?)
|
|
Sat Jan 30, 2010 [5:20 AM]
|
welcor
Email not supplied
member since: Mar 9, 2000
|
In Reply To
Reply
|
|
This is actually not as easy to accomplish as one might imagine, since things need to happen in a multiplayer environment.
The function you need to look in, is called "nanny()" and can be found in interpreter.c.
Here, input is parsed, based on the current connection state (CON_GET_NAME for new players, CON_OEDIT for people in the object editor, and so on). If people are in a non-playing state, game_loop() in comm.c will ask nanny() to handle their input, otherwise the input goes to command_interpreter().
So, what you need to do, is to add some new states in structs.h (ie. CON_GET_WANT_ADVENTURE, CON_GET_ADVENTURE_NAME, etc.), and then add the corresponding cases in nanny().
If you feel like it, you could write an entire "substate"-tree, like it is done in the OasisOLC functions.
|
|
|
|
|
5. RE: CircleMUD: Getting Input from the Player (Any ideas?)
|
|
Sat Jan 30, 2010 [11:52 AM]
|
Krysis
Email not supplied
member since: Dec 8, 2005
|
In Reply To
Reply
|
|
Ahh ha,
this helps out immensely! Thank you all for taking your time to reply to this query. I did start to head in the direction of using the nanny() function then for some odd reason - I abandoned it! Heh, thanks for setting me straight.
Have a good one!
|
|
|
|
|