Login
User Name:

Password:



Register

Forgot your password?
Changes list / Addchange
Author: Khonsu
Submitted by: Khonsu
6Dragons mp3 sound pack
Author: Vladaar
Submitted by: Vladaar
AFKMud 2.2.3
Author: AFKMud Team
Submitted by: Samson
SWFOTEFUSS 1.5
Author: Various
Submitted by: Samson
SWRFUSS 1.4
Author: Various
Submitted by: Samson
Users Online
Sogou, AhrefsBot

Members: 0
Guests: 25
Stats
Files
Topics
Posts
Members
Newest Member
488
3,788
19,631
595
Khonsu

Today's Birthdays
There are no member birthdays today.
» SmaugMuds » Codebases » AFKMud Support & Development » Pager Output Problems
Forum Rules | Mark all | Recent Posts

Pager Output Problems
< Newer Topic :: Older Topic >

Pages:<< prev 1 next >>
Post is unread #1 Dec 3, 2014 11:26 pm   
Go to the top of the page
Go to the bottom of the page

Samson
Black Hand
GroupAdministrators
Posts3,685
JoinedJan 1, 2002

 
Working up to another update release, but I've got at least one set of problems I'd like to get worked out before that happens: https://github.com/Arthmoor/AFKMud/issues/11

The pager_output() code in descriptor.cpp has several issues:

It bleeds color after the prompt.
It repeats the last line it just showed you before resuming.
The "N" prompt does not work.
Using the "R" prompt refreshes but loses one line each time this is done.
Using the "B" prompt goes back 1 less line than it showed.

I've looked it over and I don't really get what's going on. It's either been too long since I did this part of our std::string conversion or someone else wrote all this for us and we never had the chance to properly test it. Needless to say, all the problems are quite annoying :P

Anyone willing to lend a hand with getting this set right?

Post is unread #2 Dec 4, 2014 1:28 am   
Go to the top of the page
Go to the bottom of the page

Quixadhal
Conjurer
GroupMembers
Posts398
JoinedMar 8, 2005

 
Not sure about the way SmaugFUSS does pagers, but I can offer the way I changed WileyMUD's pager to be more sensible....

Instead of futzing around with string pointers from hell, in WileyMUD, i just changed the system so it keeps an array of lines. Whenever there's a line break in the pager output (usually via pager_printf), I NULL terminate the current line string and start a new one.

Then, on paged output to the socket, I simply emit lines N to M, with CRLF appended to each, followed by whatever prompt should be active. If the user input is to go back, I re-send lines N-M to N-1, etc, etc...

I would also throw a color RESET sequence at the end of each line, but WileyMUD doesn't have color. :)

Post is unread #3 Dec 4, 2014 4:16 am   
Go to the top of the page
Go to the bottom of the page

Samson
Black Hand
GroupAdministrators
Posts3,685
JoinedJan 1, 2002

 
May help to post the function:
bool descriptor_data::pager_output(  )
{
   char_data *ch;
   int pclines, start, end;
   register int lines;
   bool ret;

   if( !this || this->pagebuf.empty(  ) || this->pagecmd == -1 )
      return true;
   ch = this->original ? this->original : this->character;
   pclines = UMAX( ch->pcdata->pagerlen, 5 ) - 1;

   switch ( LOWER( this->pagecmd ) )
   {
      default:
         lines = 0;
         break;
      case 'b':
         lines = -1 - ( pclines * 2 );
         break;
      case 'r':
         lines = -1 - pclines;
         break;
      case 'n':
         lines = 0;
         pclines = 0x7FFFFFFF;   /* As many lines as possible */
         break;
      case 'q':
         this->flush_buffer( true );
         this->pagebuf.clear(  );
         this->pageindex = 0;
         this->pagecmd = 0;
         return true;
   }

   // This is going to get seriously messed up if people use the wrong line termination.
   vector < string > pagelines = string_explode( this->pagebuf, '\n' );

   if( lines < 0 )
      start = this->pageindex + lines;
   else
      start = this->pageindex;
   end = start + pclines;
   for( int x = start; x < end; ++x )
   {
      if( x < 0 )
         continue;

      if( ( size_t ) x >= pagelines.size(  ) )
      {
         this->flush_buffer( true );
         this->pagebuf.clear(  );
         this->pageindex = 0;
         return true;
      }

      this->write( pagelines[x].c_str(  ) );
      this->pageindex = x;
   }

   this->pagecmd = -1;
   if( ch->has_pcflag( PCFLAG_ANSI ) )
      if( !this->write( ANSI_LBLUE ) )
         return false;

   if( ( ret = this->write( "(C)ontinue, (N)on-stop, (R)efresh, (B)ack, (Q)uit: © " ) ) == false )
      return false;

   /*
    * Telnet GA bit here suggested by Garil 
    */
   if( ch->has_pcflag( PCFLAG_TELNET_GA ) )
      this->write_to_buffer( (const char*)go_ahead_str );
   if( ch->has_pcflag( PCFLAG_ANSI ) )
   {
      char buf[32];

      snprintf( buf, 32, "%s", ch->color_str( this->pagecolor ) );
      ret = this->write( buf );
   }
   return ret;
}


Not for SmaugFUSS btw, for AFKMud. It looks simple enough, but I guess I just don't know what's actually happening here.

Post is unread #4 Dec 4, 2014 12:00 pm   
Go to the top of the page
Go to the bottom of the page

Quixadhal
Conjurer
GroupMembers
Posts398
JoinedMar 8, 2005

 
pclines = UMAX( ch->pcdata->pagerlen, 5 ) - 1;


Why? wouldn't you normally want to show pagerlen lines at a time? Maybe -1 for the prompt? I bet that really should be UMIN(), not UMAX().

switch mess...


I would probably make the default case just set start to be pageindex, and end to be pageindex + pclines, and then after displaying the set of lines in question, move pageindex to pageindex + pclines. That way the default paging action will display N lines with the last line of the previous screen as the first line of the new screen.

The 'b' case, pageindex = pageindex - pclines, same idea... keep an extra line for context. Of course, don't let it go below 0.

The 'r' case, don't reset pageindex at the bottom of the loop, just display the same set again.

The 'n' case, the start value is pageindex, as usual... the end value is the last line of the buffer. Since you hit the end of the buffer, you can just exit after this if you like.

The 'q' case seems fine.

Post is unread #5 Dec 4, 2014 4:35 pm   
Go to the top of the page
Go to the bottom of the page

Samson
Black Hand
GroupAdministrators
Posts3,685
JoinedJan 1, 2002

 
So are you saying dump all this stuff where it figures out where start is and just have the switch statement do it itself?

Also, changing to UMIN broke it. It was only spitting out 5 lines at a time.

Post is unread #6 Dec 4, 2014 6:48 pm   
Go to the top of the page
Go to the bottom of the page

Quixadhal
Conjurer
GroupMembers
Posts398
JoinedMar 8, 2005

 
Heh, yeah you're right on the UMAX thing... reading it backwards. Sorry. :)

I'm just thinking in terms of what you want the pager to accomplish.

Paged output gets queued up as lines of text, to be output to the user a "page" at a time, giving them the option
to move up or down, abort the display early, or dump it all out at once.

Therefore, all you really need to know is:

What line is the current top-of-page for the user?
What pager command did they issue last?
How many lines fit on a "page" for them?

Basically, the code that prints stuff to the pager will just keep appending lines until the pager is cleared, at which point it becomes a new empty buffer.

So, when the show_pager code gets called, initialy, the top-of-page line is line 0, the implied command is 'r' for refresh (display the current page without advancing), and the page length is whatever the user has set (likely defaulting to 20).

If the user enters 'b', you decrement the top-of-page line by their page length, and set it to be 0 if it would have gone below 0, then call the refresh code.

If the user enters 'f', you increment the top-of-page line by their page length, setting it to the number of lines in the buffer - pagelen if it would have gone over, then call the refresh code.

If the user enters 'q', you just clean up, empty the buffer, and return to normal operations.

If the user enters 'n', you just dump ALL the lines, then do what you would do for 'q'.

I think the temp variables actually make it more confusing and convoluted.

Here's a stab at it, untested of course.... see if it makes any sense.
bool descriptor_data::pager_output(  )
{
    char_data *ch;
    int page_length, lines_to_display;
    bool flush_and_exit = false;

    if( !this || this->pagebuf.empty(  ) || this->pagecmd == -1 )
        return true;
    ch = this->original ? this->original : this->character;

    // This is going to get seriously messed up if people use the wrong line termination.
    vector < string > pagelines = string_explode( this->pagebuf, '\n' );

    lines_in_buffer = pagelines.size();
    page_length = UMAX( ch->pcdata->pagerlen, 5 ) - 1;

    if( this->pagecmd == -1 ) // We want the very first time to be a "refresh"
        this->pagecmd = 'r';

    switch ( LOWER( this->pagecmd ) )
    {
        case 'q':
            lines_to_display = 0;
            flush_and_exit = true;
            break;
        case 'b':
           this->pageindex = UMAX(0, this->pageindex - page_length);
           lines_to_display = page_length - 1;
           break;
        default:
           this->pageindex = UMIN(pagelines.size() - 1, this->pageindex + page_length - 1);
           lines_to_display = page_length - 1;
           break;
        case 'r':
           lines_to_display = page_length - 1;
           break;
        case 'n':
           lines_to_display = pagelines.size() - this->pageindex - 1;
           break;
    }

    for( size_t x = this->pageindex; x < this->pageindex + lines_to_display; ++x )
    {
        if( x >= pagelines.size() )
        {
            flush_and_exit = true;
            break;
        }
        this->write( pagelines[x].c_str(  ) );
    }

   if( flush_and_exit ) {
      this->flush_buffer( true );
      this->pagebuf.clear(  );
      this->pageindex = 0;
      this->pagecmd = 0;
      return true;
   }

   if( ch->has_pcflag( PCFLAG_ANSI ) )
      if( !this->write( ANSI_LBLUE ) )
         return false;

   if( ( ret = this->write( "(C)ontinue, (N)on-stop, (R)efresh, (B)ack, (Q)uit: © " ) ) == false )
      return false;

   /*
    * Telnet GA bit here suggested by Garil
    */
   if( ch->has_pcflag( PCFLAG_TELNET_GA ) )
      this->write_to_buffer( (const char*)go_ahead_str );
   if( ch->has_pcflag( PCFLAG_ANSI ) )
   {
      char buf[32];

      snprintf( buf, 32, "%s", ch->color_str( this->pagecolor ) );
      ret = this->write( buf );
   }
   return ret;
}

Post is unread #7 Dec 4, 2014 7:43 pm   Last edited Dec 4, 2014 7:44 pm by Samson
Go to the top of the page
Go to the bottom of the page

Samson
Black Hand
GroupAdministrators
Posts3,685
JoinedJan 1, 2002

 
I tried yours, but it never paused at the page breaks, although it seemed to be doing them in the right places.

That said, I had also been working on this in the meantime and have this now:
bool descriptor_data::pager_output(  )
{
   char_data *ch;
   int pclines;
   size_t start, end;
   bool ret;

   if( !this || this->pagebuf.empty(  ) || this->pagecmd == -1 )
      return true;

   ch = this->original ? this->original : this->character;
   pclines = umax( ch->pcdata->pagerlen, 5 );

   switch ( LOWER( this->pagecmd ) )
   {
      default:
         start = this->pageindex;
         end = start + pclines;
         break;

      case 'b':
         start = this->pageindex - ( pclines * 2 );
         end = this->pageindex - pclines;
         break;

      case 'r':
         start = this->pageindex - pclines;
         end = this->pageindex - 1;
         break;

      case 'n':
         start = this->pageindex;
         end = 0x7FFFFFFF;   /* As many lines as possible */
         break;

      case 'q':
         this->flush_buffer( true );
         this->pagebuf.clear(  );
         this->pageindex = 0;
         this->pagecmd = 0;
         return true;
   }

   // This is going to get seriously messed up if people use the wrong line termination.
   vector < string > pagelines = string_explode( this->pagebuf, '\n' );

   if( start < 0 )
      start = 0;
   if( end > pagelines.size() )
      end = pagelines.size();

   for( size_t x = start; x <= end; ++x )
   {
      if( x >= pagelines.size(  ) )
      {
         this->flush_buffer( true );
         this->pagebuf.clear(  );
         this->pageindex = 0;
         this->pagecmd = 0;
         return true;
      }

      this->write( pagelines[x].c_str(  ) );
      this->pageindex = x + 1;
   }

   this->pagecmd = -1;
   if( ch->has_pcflag( PCFLAG_ANSI ) )
      if( !this->write( ANSI_LBLUE ) )
         return false;

   if( ( ret = this->write( "(C)ontinue, (N)on-stop, (R)efresh, (B)ack, (Q)uit: © " ) ) == false )
      return false;

   /*
    * Telnet GA bit here suggested by Garil 
    */
   if( ch->has_pcflag( PCFLAG_TELNET_GA ) )
      this->write_to_buffer( (const char*)go_ahead_str );
   if( ch->has_pcflag( PCFLAG_ANSI ) )
   {
      char buf[32];

      snprintf( buf, 32, "%s", ch->color_str( this->pagecolor ) );
      ret = this->write( buf );
   }
   return ret;
}

It appears to work. R refreshes the section you're looking at. C sends the next page. N correctly dumps the whole thing to you. Q cancels the output. The only problem left is that B isn't quite right. While it DOES print out the previous page, it ends up with one extra line at the end of the list than it should be getting. Plus, if you try and use it on the first page, you just get the prompt again with a blank line in between.

So this is very close. I just can't figure out how B is picking up an extra line. I'm not overly concerned with dipsticks who think they can go back from the very first page :P

Oh, and color bleeding is still happening even though the various paged output functions I'm testing all run a set_pager_color() call first. That SHOULD be setting the color to reuse but for some reason it isn't in a lot of cases.

Post is unread #8 Dec 5, 2014 1:01 am   Last edited Dec 5, 2014 2:47 am by Samson
Go to the top of the page
Go to the bottom of the page

Samson
Black Hand
GroupAdministrators
Posts3,685
JoinedJan 1, 2002

 
One less problem :P
      case 'b':
         start = this->pageindex - ( pclines * 2 );
         end = this->pageindex - ( pclines + 2 );
         break;

Strangely that seems to have fixed the B command.

Not off to find my color bleeder....

... and I found that too. The difference between if( ch->desc ) and if( !ch->desc ) is rather important :P

Pages:<< prev 1 next >>