13 Nov, 2009, Kayle wrote in the 1st comment:
Votes: 0
Alright, So someone on SW:TSW asked for an option to be able to have the prompt show up only after player input. So I was all for that and went about adding in support for it. But now I get odd results. Everything works fine, except for the fact that it keeps adding a blank line at the end of the output. Here's my game_loop and flush_buffer functions. Everything else to do with descriptors and what not is still pretty stock.

/*
* Low level output function.
*/
bool flush_buffer( DESCRIPTOR_DATA * d, bool fPrompt )
{
char buf[MIL];

/*
* If buffer has more than 4K inside, spit out .5K at a time -Thoric
*/
if( !mud_down && d->outtop > 4096 )
{
memcpy( buf, d->outbuf, 512 );
d->outtop -= 512;
memmove( d->outbuf, d->outbuf + 512, d->outtop );
if( d->snoop_by )
{
char snoopbuf[MIL];

buf[512] = '\0';
if( d->character && d->character->name )
{
if( d->original && d->original->name )
snprintf( snoopbuf, MIL, "%s (%s)", d->character->name, d->original->name );
else
snprintf( snoopbuf, MIL, "%s", d->character->name );
write_to_buffer( d->snoop_by, snoopbuf, 0 );
}
write_to_buffer( d->snoop_by, "% ", 2 );
write_to_buffer( d->snoop_by, buf, 0 );
}
if( !write_to_descriptor( d, buf, 512 ) )
{
d->outtop = 0;
return false;
}
return true;
}


/*
* Bust a prompt.
*/
if( fPrompt && !mud_down && d->connected == CON_PLAYING )
{
Character *ch;

ch = d->original ? d->original : d->character;
if( ch && ch->pcdata && ch->pcdata->flags.test( PCFLAG_BLANK ) && ( d->character->pcdata->flags.test( PCFLAG_AUTOPROMPT ) || who_fighting( d->character ) != NULL ) )
write_to_buffer( d, "\r\n", 2 );

if( ch && ch->pcdata && ch->pcdata->flags.test( PCFLAG_PROMPT ) && ( d->character->pcdata->flags.test( PCFLAG_AUTOPROMPT ) || who_fighting( d->character ) != NULL ) )
display_prompt( d );

if( ch && !IS_NPC( ch ) && ch->pcdata && ch->pcdata->flags.test( PCFLAG_ANSI ) )
{
write_to_buffer( d, ANSI_RESET, 0 );
d->prevcolor = 0x08;
}

if( ch && ch->pcdata && ch->pcdata->flags.test( PCFLAG_TELNET_GA ) )
write_to_buffer( d, go_ahead_str, 0 );
}

/*
* Short-circuit if nothing to write.
*/
if( d->outtop == 0 )
return true;

/*
* Snoop-o-rama.
*/
if( d->snoop_by )
{
/*
* without check, 'force mortal quit' while snooped caused crash, -h
*/
if( d->character && d->character->name )
{
/*
* Show original snooped names. – Altrag
*/
if( d->original && d->original->name )
snprintf( buf, MIL, "%s (%s)", d->character->name, d->original->name );
else
snprintf( buf, MIL, "%s", d->character->name );
write_to_buffer( d->snoop_by, buf, 0 );
}
write_to_buffer( d->snoop_by, "% ", 2 );
write_to_buffer( d->snoop_by, d->outbuf, d->outtop );
}

/*
* OS-dependent output.
*/
if( !write_to_descriptor( d, d->outbuf, d->outtop ) )
{
d->outtop = 0;
return false;
}
else
{
d->outtop = 0;
return true;
}
}

void game_loop( void )
{
struct timeval last_time;
char cmdline[MIL];
DESCRIPTOR_DATA *d;

#ifndef WIN32
signal( SIGPIPE, SIG_IGN );
signal( SIGALRM, caught_alarm );
signal( SIGCHLD, clean_up_child_process );
#endif

/*
* signal( SIGSEGV, SegVio );
*/
gettimeofday( &last_time, NULL );
current_time = ( time_t ) last_time.tv_sec;

/*
* Main loop
*/
while( !mud_down )
{
accept_new( control );

/*
* Kick out descriptors with raised exceptions
* or have been idle, then check for input.
*/
for( d = first_descriptor; d; d = d_next )
{
if( d == d->next )
{
bug( "descriptor_loop: loop found & fixed" );
d->next = NULL;
}
d_next = d->next;

d->idle++; /* make it so a descriptor can idle out */
if( FD_ISSET( d->descriptor, &exc_set ) )
{
FD_CLR( d->descriptor, &in_set );
FD_CLR( d->descriptor, &out_set );
if( d->character && ( d->connected == CON_PLAYING || d->connected == CON_EDITING ) )
save_char_obj( d->character );
d->outtop = 0;
close_socket( d, true );
continue;
}
else if( ( !d->character && d->connected != CON_ACCOUNT_MENU && d->idle > 720 ) /* 2 mins */
|| ( d->connected != CON_PLAYING && d->connected != CON_ACCOUNT_MENU && d->idle > 1200 ) /* 5 mins */
|| ( ( ( d->connected == CON_ACCOUNT_MENU ) || ( d->connected == CON_ACCOUNT_CHAR_SELECT ) ) && d->idle > 12000 )
|| d->idle > 28800 ) /* 2 hrs */
{
write_to_descriptor( d, "Idle timeout… disconnecting.\r\n", 0 );
d->outtop = 0;
close_socket( d, true );
continue;
}
else
{
d->fcommand = false;

if( FD_ISSET( d->descriptor, &in_set ) )
{
d->idle = 0;
if( d->character )
d->character->timer = 0;
if( !read_from_descriptor( d ) )
{
FD_CLR( d->descriptor, &out_set );
if( d->character && ( d->connected == CON_PLAYING || d->connected == CON_EDITING ) )
save_char_obj( d->character );
d->outtop = 0;
close_socket( d, false );
continue;
}
}

/*
* check for input from the dns
*/
if( ( d->connected == CON_PLAYING || d->character != NULL ) && d->ifd != -1 && FD_ISSET( d->ifd, &in_set ) )
process_dns( d );

if( d->character && d->character->wait > 0 )
{
–d->character->wait;
continue;
}

read_from_buffer( d );
if( d->incomm[0] != '\0' )
{
d->fcommand = true;
stop_idling( d->character );

mudstrlcpy( cmdline, d->incomm, MIL );
d->incomm[0] = '\0';

if( d->character )
set_cur_char( d->character );

if( d->pagepoint )
set_pager_input( d, cmdline );
else
switch ( d->connected )
{
default:
nanny( d, cmdline );
break;
case CON_PLAYING:
interpret( d->character, cmdline );
break;
case CON_EDITING:
edit_buffer( d->character, cmdline );
break;
}
/*
* Bust a prompt.
*/
if( !mud_down && d->connected == CON_PLAYING )
{
Character *ch;

ch = d->original ? d->original : d->character;
if( ch && ch->pcdata && ch->pcdata->flags.test( PCFLAG_BLANK ) && ( !d->character->pcdata->flags.test( PCFLAG_AUTOPROMPT ) && who_fighting( d->character ) == NULL ) )
write_to_buffer( d, "\r\n", 2 );

if( ch && ch->pcdata && ch->pcdata->flags.test( PCFLAG_PROMPT ) && ( !d->character->pcdata->flags.test( PCFLAG_AUTOPROMPT ) && who_fighting( d->character ) == NULL ) )
display_prompt( d );

if( ch && !IS_NPC( ch ) && ch->pcdata && ch->pcdata->flags.test( PCFLAG_ANSI ) )
{
write_to_buffer( d, ANSI_RESET, 0 );
d->prevcolor = 0x08;
}

if( ch && ch->pcdata && ch->pcdata->flags.test( PCFLAG_TELNET_GA ) )
write_to_buffer( d, go_ahead_str, 0 );
}
}
}
if( d == last_descriptor )
break;
}

#if !defined(__CYGWIN__)
mud_recv_message( );
#endif
#ifdef IMC
imc_loop( );
#endif

// Autonomous game motion. Stops processing when there are no people at all online.
if( first_descriptor != NULL )
update_handler( );

// Event handling. Will continue to process even with nobody around. Keeps areas fresh this way.
run_events( current_time );

/*
* Output.
*/
for( d = first_descriptor; d; d = d_next )
{
d_next = d->next;

if( ( d->fcommand || d->outtop > 0 ) && FD_ISSET( d->descriptor, &out_set ) )
{
if( d->pagepoint )
{
if( !pager_output( d ) )
{
if( d->character && ( d->connected == CON_PLAYING || d->connected == CON_EDITING ) )
save_char_obj( d->character );
d->outtop = 0;
close_socket( d, false );
}
}
else if( !flush_buffer( d, true ) )
{
if( d->character && ( d->connected == CON_PLAYING || d->connected == CON_EDITING ) )
save_char_obj( d->character );
d->outtop = 0;
close_socket( d, false );
}
}
if( d == last_descriptor )
break;
}

/*
* Synchronize to a clock.
* Sleep( last_time + 1/PULSE_PER_SECOND - now ).
* Careful here of signed versus unsigned arithmetic.
*/
{
struct timeval now_time;
long secDelta;
long usecDelta;

gettimeofday( &now_time, NULL );
usecDelta = ( ( int )last_time.tv_usec ) - ( ( int )now_time.tv_usec ) + 1000000 / PULSE_PER_SECOND;
secDelta = ( ( int )last_time.tv_sec ) - ( ( int )now_time.tv_sec );
while( usecDelta < 0 )
{
usecDelta += 1000000;
secDelta -= 1;
}

while( usecDelta >= 1000000 )
{
usecDelta -= 1000000;
secDelta += 1;
}

if( secDelta > 0 || ( secDelta == 0 && usecDelta > 0 ) )
{
struct timeval stall_time;

stall_time.tv_usec = usecDelta;
stall_time.tv_sec = secDelta;
#ifdef WIN32
Sleep( ( stall_time.tv_sec * 1000L ) + ( stall_time.tv_usec / 1000L ) );
#else
if( select( 0, NULL, NULL, NULL, &stall_time ) < 0 && errno != EINTR )
{
perror( "game_loop: select: stall" );
exit( 1 );
}
#endif
}
}

gettimeofday( &last_time, NULL );
current_time = ( time_t ) last_time.tv_sec;
}
fflush( stderr ); /* make sure strerr is flushed */
return;
}


Now, if there are multiple lines to be displayed, it shows them without spaces. But if it's just a single line it looks like this:
Quote
<32000hp 30000m 30000mv> <#334>
Katiara says, 'test'

Katiara says, 'test'

Katiara says, 'test'

Katiara says, 'test'

Katiara says, 'test'
hmm
You Hmmmm out loud.

<32000hp 30000m 30000mv> <#334>


What I'd like it to look like is:
Quote
<32000hp 30000m 30000mv> <#334>
Katiara says, 'test'
Katiara says, 'test'
Katiara says, 'test'
Katiara says, 'test'
Katiara says, 'test'
hmm
You Hmmmm out loud.

<32000hp 30000m 30000mv> <#334>


Yellow text is the local echo for my client. >.>

Anyway, thoughts? Ideas? Suggestions? Solutions? I'm at a loss. I can't figure out where this mysterious newline is coming from.
13 Nov, 2009, Tonitrus wrote in the 2nd comment:
Votes: 0
# if( ch && ch->pcdata && ch->pcdata->flags.test( PCFLAG_BLANK ) && ( d->character->pcdata->flags.test( PCFLAG_AUTOPROMPT ) || who_fighting( d->character ) != NULL ) )
# write_to_buffer( d, "\r\n", 2 );


Lines 235 and 236
Pretty sure that's your problem. I had a similar problem with FUSS once.

[Edit: For reference, I was trying to do the same thing, and I didn't find a satisfactory solution before I gave up.]
13 Nov, 2009, Kayle wrote in the 3rd comment:
Votes: 0
That's not the problem, that puts a blank line before the prompt. Even disabling that you still end up with the blank line after any message displayed.
13 Nov, 2009, Zeno wrote in the 4th comment:
Votes: 0
Debug the packets with MUSHclient, it'll at least be easier to see what's going on.

To be more specific: Change some \r\n to \n\r to narrow down where stuff is coming from. For example, if you change all the mentioned \r\n to \n\r but the debug still shows \r\n that means it's coming from somewhere else.

Haven't examined the code yet, just saying something that could help.
14 Nov, 2009, Tonitrus wrote in the 5th comment:
Votes: 0
I just found the code I was working on before that I thought had this problem, and looking at it, it looks like I was trying to code something slightly different, and thus had a different problem. Looks like I was trying to make sure that a prompt wouldn't fire after every combat action, since I had them firing on random beats instead of on each tick. Since they were firing at different times, a line-feed and a prompt would be inserted after each. Looks like I solved it by adding a check so that it'd only drop a prompt on the tick. Whether or not this is related, I couldn't say. I don't mess with Smaug code anymore.
14 Nov, 2009, Kayle wrote in the 6th comment:
Votes: 0
Debugging with MUSHClient isn't really a lot of help. I don't use MUSHClient, so I'm not really all that familiar with it, or with how to read the packet debug stuff. Nothing about what I'm seeing looks any different, before or after I swapped from /r/n to /n/r.

Everything is ending in "Debugging with MUSHClient isn't really a lot of help. I don't use MUSHClient, so I'm not really all that familiar with it, or with how to read the packet debug stuff. Nothing about what I'm seeing looks any different, before or after I swapped from /r/n to /n/r.

Everything is ending in "[0m" though. I don't know exactly what that translates to. Sometimes there will be a "[0m.[0m" And of course the huge block of what looks like Hexadecimal values.
14 Nov, 2009, Zeno wrote in the 7th comment:
Votes: 0
Check the hex values. 0a is \n and 0d \r

The Check the hex values. 0a is \n and 0d \r

The [0m stuff is just colors.
14 Nov, 2009, Kayle wrote in the 8th comment:
Votes: 0
Each Hexidecimal set starts with 0d 0a while everything is set to \r\n.

Sets are still starting with 0d 0a while everything in the two posted functions is set to \n\r.

Am I supposed to read the sets as the top left being the last bit of the packet received?
14 Nov, 2009, Scandum wrote in the 9th comment:
Votes: 0
One thing to keep in mind is that you should only use GA when marking a prompt. TinTin++ will add a new line if a GA isn't followed by a newline, not sure how other clients handle it.
14 Nov, 2009, Kayle wrote in the 10th comment:
Votes: 0
It took some finagling, but I got it to work with a bit of.. manipulation. The problem ended up being in write_to_buffer. It was appending a \r\n in front of the first line after a flush. So with some finagling I made a field on the descriptor and set it to true whenever a prompt is displayed, and if a prompt is displayed it then appends the \r\n. If not, it doesn't. Now that it's working:

Quote
<32000hp 30000m 30000mv> <#330>
[Immortal] Kayle@Main: Test
[Immortal] Kayle@Main: Test
[Immortal] Kayle@Main: Test
[Immortal] Kayle@Main: Test
imm Got it.
[Immortal] You: Got it.

<32000hp 30000m 30000mv> <#330>
[Immortal] Jandice@Main: sweet?
[Immortal] Jandice@Main: and it only took you 3 hours >_>
[Immortal] Jandice@Main: :p
imm 2. one of those was spent watching SG:U >.>
[Immortal] You: 2. one of those was spent watching SG:U >.>

<32000hp 30000m 30000mv> <#330>
06 May, 2011, Dutch wrote in the 11th comment:
Votes: 0
Don't mean to dig up an old topic, but I was reading this and was wondering if someone could explain the relevance of the '\r'?
I had someone stop by thats testing a client they are working on, and he was saying that he is seeing all the \r's and that they
aren't needed. Just looking for some info about it all, because I've honestly never had a problem before.
Is it worth taking the time to strip them all out, and will there be any reprocussions for doing?
06 May, 2011, Runter wrote in the 12th comment:
Votes: 0
\n (10) is new line. \r (13) is carriage return. It *may be* true that a specific client doesn't require \r\n, but to my knowledge that's the best coverage. Some clients have been known to only respond correctly to \r\n. For what it's worth, some clients work just fine with only \r, but that's the most extreme edge case. Regardless, if his client is showing \r in the stream that's pretty interesting and funky. It shouldn't even be receiving the string "\r". It should be receiving the value 13 for a single byte. Which client is this?

Fwiw, a lot of clients only require \n, but you'll almost certainly run into problems with some of them.
06 May, 2011, Dutch wrote in the 13th comment:
Votes: 0
I think its called potato MU*?
Thanks for the info, so basically what your saying is that he should add support for \r(13) to his client instead of going through the mud and stripping out all the \r's and that there is nothing wrong with them being there?
My first thought was, wouldn't it be easier to just add support to the client for them?
But what do I know, I'm just a hobbist.
06 May, 2011, Twisol wrote in the 14th comment:
Votes: 0
I think it's simplest for the client to gsub out any "\r"s it finds (sans \r\0 if possible, that's a valid Telnet sequence for a single \r).
06 May, 2011, Runter wrote in the 15th comment:
Votes: 0
Oh, he certainly needs to strip out the \r's on the client side if he doesn't want them. Most all muds are going to have them in some places. It's totally unreasonable for him to suggest removing those from the server.
06 May, 2011, Tyche wrote in the 16th comment:
Votes: 0
Dutch said:
Don't mean to dig up an old topic, but I was reading this and was wondering if someone could explain the relevance of the '\r'?
I had someone stop by thats testing a client they are working on, and he was saying that he is seeing all the \r's and that they
aren't needed. Just looking for some info about it all, because I've honestly never had a problem before.
Is it worth taking the time to strip them all out, and will there be any reprocussions for doing?


See pages 11 and 12 of RFC 854
06 May, 2011, KaVir wrote in the 17th comment:
Votes: 0
Dutch said:
I had someone stop by thats testing a client they are working on, and he was saying that he is seeing all the \r's and that they aren't needed.

He's going to encounter the same thing with almost every mud. He really needs to fix his client to properly handle \r.

Dutch said:
Just looking for some info about it all, because I've honestly never had a problem before. Is it worth taking the time to strip them all out, and will there be any reprocussions for doing?

Without the \r, some clients won't move the cursor to the beginning of the line before displaying the next line.
It'll look like this.
I've encountered it a few times before.
It's annoying.
06 May, 2011, Twisol wrote in the 18th comment:
Votes: 0
And just FYI, "\r\n" is the standard end-of-line sequence for Telnet. It is this way because the \r control character is supposed to move the cursor back to the left edge, and the \n control character is meant to drop the cursor down one character. Hence "\r\n" means end-of-line. Of course, it's a bit of an anachronism these days, but you still need to follow the standard or funky things happen.

I recommend Elanthis' libtelnet if you want standards-compliant Telnet support out of the box. (I'd recommend my own 'anachronism' library, but it's not done yet.)
06 May, 2011, Dutch wrote in the 19th comment:
Votes: 0
Thanks for all your answers guys.
I'll let the developer know that he should take care of that on his end.
:)
07 May, 2011, Scandum wrote in the 20th comment:
Votes: 0
I think it's Windows telnet that requires \r\n or \n\r, old Macintosh systems might send \r or \r\0, and pretty much everything else handles \n correctly.
0.0/24