01 Jun, 2014, syn wrote in the 1st comment:
Votes: 0
Hey Folks,

Bizarre issue, I fired up the RoT 2.0b5 release, and fixed it up a bit, got o/r progs installed and all that but..

In testing progs I noticed any prog written would convert $n or $i for instance to _n _i..

So searching around I went through GDB and stepped the input..

During the GDP stepping I saw the following:

First just a break at the top of read_from_buffer and see what it shows as input:

<1523hp 3351m 1420mv 0 Street Fighter>
say $n
Single stepping until exit from function read_from_buffer,
which has no line number information.
strcpy (dest=0xb6d04b25 " ", src=0xb6d04a25 "say $n") at strcpy.c:31
31 {
(gdb)


so far so good..

Fail: You Say: '_n'

Here it is in strcpy, copying it over..

(gdb) print *s++
$2 = 115 's'
(gdb) print *s++
$3 = 97 'a'
(gdb) print *s++
$4 = 121 'y'
(gdb) print *s++
$5 = 32 ' '
(gdb) print *s++
$6 = 36 '$'
(gdb) print *s++
$7 = 110 'n'
(gdb)

Return: You say '_n'

somehow within write_to_buffer it goes from src : src=0xb6d04a25 "say $n" dest dest=0xb6d04b25 "say $n"

to d->incomm (what it copies to) : say _n

If I have it write the incomm to buffer, this is what I see (bypassed alias code to be sure its not interfering):

say $n
You Say: '_n'
say _n

void read_from_buffers( DESCRIPTOR_DATA *d )
{
int i, j, k;

/*
* Hold horses if pending command already.
*/
if ( d->incomm[0] != '\0' )
return;

/*
* Look for at least one new line.
*/
for ( i = 0; d->inbuf[i] != '\n' && d->inbuf[i] != '\r'; i++ )
{
if ( d->inbuf[i] == '\0' )
return;
}

/*
* Canonical input processing.
*/
for ( i = 0, k = 0; d->inbuf[i] != '\n' && d->inbuf[i] != '\r'; i++ )
{
if ( k >= MAX_INPUT_LENGTH - 2 )
{
write_to_descriptor( d->descriptor, "Line too long.\n\r", 0 );

/* skip the rest of the line */
for ( ; d->inbuf[i] != '\0'; i++ )
{
if ( d->inbuf[i] == '\n' || d->inbuf[i] == '\r' )
break;
}
d->inbuf[i] = '\n';
d->inbuf[i+1] = '\0';
break;
}

if ( d->inbuf[i] == '\b' && k > 0 )
–k;
else if ( isascii(d->inbuf[i]) && isprint(d->inbuf[i]) )
d->incomm[k++] = d->inbuf[i];
}

/*
* Finish off the line.
*/
if ( k == 0 )
d->incomm[k++] = ' ';
d->incomm[k] = '\0';

/*
* Deal with bozos with #repeat 1000 …
*/

if ( k > 1 || d->incomm[0] == '!' )
{
if ( d->incomm[0] != '!' && strcmp( d->incomm, d->inlast ) )
{
d->repeat = 0;
}
else
{
if (++d->repeat >= 25 && d->character
&& d->connected == CON_PLAYING)
{
sprintf (log_buf, "%s input spamming!", d->host);
log_string (log_buf);
wiznet ("Spam spam spam $N spam spam spam spam spam!",
d->character, NULL, WIZ_SPAM, 0,
get_trust (d->character));
if (d->incomm[0] == '!')
wiznet (d->inlast, d->character, NULL, WIZ_SPAM, 0,
get_trust (d->character));
else
wiznet (d->incomm, d->character, NULL, WIZ_SPAM, 0,
get_trust (d->character));

d->repeat = 0;
/*
write_to_descriptor( d->descriptor,
"\n\r*** PUT A LID ON IT!!! ***\n\r", 0 );
strcpy( d->incomm, "quit" );
*/
}
}
}


/*
* Do '!' substitution.
*/
if ( d->incomm[0] == '!' )
{
if( d->connected == CON_PLAYING )
strcpy( d->incomm, d->inlast );
else
strcpy( d->incomm, " " );
}
else
strcpy( d->inlast, d->incomm );

/*
* Shift the input buffer.
*/
while ( d->inbuf[i] == '\n' || d->inbuf[i] == '\r' )
i++;
for ( j = 0; ( d->inbuf[j] = d->inbuf[i+j] ) != '\0'; j++ )
;
return;
}


But it looks no different from any other stock(ish) dikurivative, and it just makes no sense. The input source TO read from buffer, looks fine, then it garbles it within read from buffer doing only strcpy?? huh?
01 Jun, 2014, syn wrote in the 2nd comment:
Votes: 0
Wow.

So I was totally wrong in WHERE it was happening, GDB at one point made it appear that way, but I was wrong.

Apparently, this base's Smash Tilde function smashes a bunch of other random crap because, it can for some unknown reason..

Placing a boatload of write_to_buffers and following breadcrumbs led me to it.. So good I found it, sorry for the wrongness above..
02 Jun, 2014, Oliver wrote in the 3rd comment:
Votes: 0
Smash_tilde() smashes tildes because they terminate strings in the flatfile saving procedure. If you could put a ~ in certain fields and have it save as a ~, the game would most likely cease to boot.
04 Jun, 2014, syn wrote in the 4th comment:
Votes: 0
Hi Oliver, I understand what Smash Tilde is for, and agree with its purpose.

The problem wasnt that it was smashing tildes, it was that it was smashing tildes AND $ dollar signs.

so ~ became ` and $ became _

This made all mprogs/oprogs/rprogs in the game useless (all specifiers use $ eg $n for character name/target), and anything written for a prog had to be re-written after that was fixed.

So any output from an mprog with variable/targets would look like

You bow deeply.
Well! Thank you _n it was so nice to see you.
Be sure to come back to _i's emporium of broken _q and be sure to give me your name again..

It was _n right?
04 Jun, 2014, quixadhal wrote in the 5th comment:
Votes: 0
So, what kind of fancy expansion was the codebase author planning to implement (or did implement) when he decided to escape $'s as well? Obviously, it was intentional. The question is, was it finished, and so if you remove it, will it cause some OTHER save format to break?
05 Jun, 2014, syn wrote in the 6th comment:
Votes: 0
Looks like it was added into help saves as a delineator for a help editor.

I am not sure why they would have chosen $, its used in two places there, but has ~110 other locations of use as variables for progs, or the game itself, eg $n = char etc.

So, they broke themselves to add that in, and no one ever fixed it apparently.

Luckily I converted helps to SQL before this anyway :)
05 Jun, 2014, quixadhal wrote in the 7th comment:
Votes: 0
It's part of the whole cryptic file format, which was used back before OLC.

As I remember, $ was usually the token used to mean "end of section", so you'd read in help entries (or NPC's, or shops, etc) in a loop until you hit a $ all by itself, meaning you were done and could move on to the next kind of thing.

Whomever did that probably got confused… easy to do with such horrible spaghetti code. :)

Fortunately, in an LPMUD, I don't need to care about arbitrary delimiters. We keep all our help entries in one of two places.. either as files in /doc, which get indexed and searched by a daemon, or as embedded comments in the LPC code itself, indexed and searched by another daemon…. both of which provide search functions for the "help" or "man" commands to use.
05 Jun, 2014, Hades_Kane wrote in the 8th comment:
Votes: 0
quixadhal said:
Fortunately, in an LPMUD, I don't need to care about arbitrary delimiters. We keep all our help entries in one of two places.. either as files in /doc, which get indexed and searched by a daemon, or as embedded comments in the LPC code itself, indexed and searched by another daemon…. both of which provide search functions for the "help" or "man" commands to use.


Well… aren't YOU special?
05 Jun, 2014, quixadhal wrote in the 9th comment:
Votes: 0
Ain't I though? I'm hoping to get to ride the special bus to the special class too!
05 Jun, 2014, plamzi wrote in the 10th comment:
Votes: 0
Luckily for us, there are plenty of other ways to stop caring about delimiters. In general, the non-LPMud universe is much less incapacitated than quix seems to believe :)
06 Jun, 2014, Lyanic wrote in the 11th comment:
Votes: 0
plamzi said:
Luckily for us, there are plenty of other ways to stop caring about delimiters. In general, the non-LPMud universe is much less incapacitated than quix seems to believe :)

Very much this.
06 Jun, 2014, quixadhal wrote in the 12th comment:
Votes: 0
Pity most of the non-incapacitated MUD's are all tightly locked away where nobody can learn from them, for fear of losing their precious players to clones. Even though history has shown that clones only dilute the original MUD if the original MUD has stopped innovating, or has jerks for admins… IE: some reason for the players to seek something similar.
07 Jun, 2014, syn wrote in the 13th comment:
Votes: 0
I'm learning a great deal FROM lp©mud..

Specifically I stumbled across LEX in my quest to make a better, robust 'prog' system. Then it dawned on me what LPC does (not sure why it didnt before). I hadn't realized that its just a ridiculously huge parsing engine and then compiles the files it makes in C <- compiles it, itself, making each a unique "program" linking to the program(s) that exist already.

Pretty nifty get around of the static C constraints. I doubt anything I create would be remotely as complex, but theres a lot of lessons to take from it, and other LEXified entities. :)

So I guess C isn't so bad after all, as that is the basis for obviously itself, and LP(C) as it exists ontop of a C parsing engine
07 Jun, 2014, quixadhal wrote in the 14th comment:
Votes: 0
Except that LPC isn't C. LPMUD is named after its creator Lars Pensjo, and LPC is simply the name he chose since he made his language look like C in general structure.

What LPMUD does is take LPC code you wrote (you being the mudlib author, or any builder who's worked on your MUD), tokenize them into an internal stack machine language, and then interpret that stack code.

In particular, during each processing loop, each object is given a limit of how many "eval ticks" it can use, which more or less corresponds to how many LPC stack machine instructions it can execute, before it is halted and processing moves on to the next object. This protects the MUD from runaway code, be it in the form of a while(1) loop, or just a crazy-CPU-intense process that would cause lag for everyone else.

Note that objects may call functions from other objects, but all stack code counts towards that first object's eval limit. To work around that limitation, the driver provides a call_out() method which can be used to schedule a future function call (the delay can be 0, meaning as soon as you can schedule it). As with batch processing on mainframes, this allows the user to break their long-running process up into smaller chunks, each calling the next chunk before it runs out of time so as to complete the task without halting the entire system.

The use of LEX (and YACC/BISON) are in constructiing the LPC parser and state machine, not to compile LPC code into C (although earlier versions of MudOS *DID* try to allow for that, as a potential way to speedup CPU intensive LPC code… it never worked well).
07 Jun, 2014, Oliver wrote in the 15th comment:
Votes: 0
syn said:
I'm learning a great deal FROM lp©mud..
Pretty nifty get around of the static C constraints. I doubt anything I create would be remotely as complex, but theres a lot of lessons to take from it, and other LEXified entities. :)

So I guess C isn't so bad after all, as that is the basis for obviously itself, and LP(C) as it exists ontop of a C parsing engine


To be frank I've never quite fully understood this sentiment. Sure, C is a bit clunky to work with when it comes to things like string manipulation, but a MUD engine isn't really complicated enough to need anything more than C or C++ with a bit of clever footwork. String manipulation is literally the only thing that C "lacks" the robust features for (MUDwise), and you can implement string types in C with incredible ease if need be.

I certainly have always thought that the LPMud engine is neat conceptually, but it overthinks the solutions to nonexistent problems; I've never seen an LPMud accomplish anything that a traditionally programmed C++ Dikurivative couldn't.
07 Jun, 2014, Pymeus wrote in the 16th comment:
Votes: 0
Oliver said:
To be frank I've never quite fully understood this sentiment. Sure, C is a bit clunky to work with when it comes to things like string manipulation, but a MUD engine isn't really complicated enough to need anything more than C or C++ with a bit of clever footwork. String manipulation is literally the only thing that C "lacks" the robust features for (MUDwise), and you can implement string types in C with incredible ease if need be.

The thing is that people DON'T use or implement safe string types in C muds, and when they exist they're often horribly limited to the point that you do all of the manipulation on raw C strings anyway.

I would add to the list the lifetime problems with heap objects, and all the crap (dangling pointers) surrounding that.
07 Jun, 2014, quixadhal wrote in the 17th comment:
Votes: 0
The problem isn't that C (or C++) can't have string types… it's that you have to CONSTANTLY move things back and forth between whatever kludge you used (std::string) and char * arrays, because every OS call expects and uses char *'s.

It's also not an issue of it being "hard" or "complicated". It's a quality of life issue. I don't really enjoy debugging pointer errors. I also don't enjoy having to write lots of extra code to do really simple things, like saving object states. I worked on my old DikuMUD for many years, and I realized that the main reason I stopped working on it wasn't that I didn't like the game, or that I'd run out of ideas… it was that I didn't enjoy all the micromanagement that C forced me to do whenever I wanted to add something. I found myself spending more time writing and testing the technical parts of the code that C needed me to handle (loading/saving/allocating/freeing/etc), than I was writing the stuff I *wanted* to write. It all adds up.

So, I have advocated LPMUD because it was designed to be a multiplayer text game engine, giving you the tools to make what you want safely, and in a language designed to try to minimize the non-game coding you have to do. Some people don't like it, and that's fine. I also advocate using any modern language which isn't trying to be "portable assembly".

I see ZERO advantage to using C or C++ for a MUD. There are lots of things those languages are good for, but most of them involve high performance systems that can't afford the luxury of an interpreted system, or even a JIT compiled system. Others involve coding in a tiny environment, like the ROM of your television. A small multiplayer text game that spends most of its time working with text strings is NOT such a thing, IMHO.
07 Jun, 2014, Pymeus wrote in the 18th comment:
Votes: 0
EDIT:
Quote
The problem isn't that C (or C++) can't have string types… it's that you have to CONSTANTLY move things back and forth between whatever kludge you used (std::string) and char * arrays, because every OS call expects and uses char *'s.

It's also not an issue of it being "hard" or "complicated". It's a quality of life issue. I don't really enjoy debugging pointer errors. I also don't enjoy having to write lots of extra code to do really simple things, like saving object states. I worked on my old DikuMUD for many years, and I realized that the main reason I stopped working on it wasn't that I didn't like the game, or that I'd run out of ideas… it was that I didn't enjoy all the micromanagement that C forced me to do whenever I wanted to add something. I found myself spending more time writing and testing the technical parts of the code that C needed me to handle (loading/saving/allocating/freeing/etc), than I was writing the stuff I *wanted* to write. It all adds up.

Sorry for being unclear.

You put wrappers around your OS calls to translate between your dummy-resistant string implementation and the raw C string. This is usually trivial and most muds need only a handful of system calls that take strings anyway.

EDIT: That said, I wouldn't choose to write a mud from scratch in C either.
08 Jun, 2014, quixadhal wrote in the 19th comment:
Votes: 0
I understand how to do it. And that's just extra boiler-plate that YOU have to do… and you have to use said wrappers in EVERY bit of code you write instead of the OS call you'd normally use. Again, extra work on your part that you could have spent working on your game code if not for C.

Also, when I say "OS", I really mean libc. I always made use of libc quite heavily, and considering how many people use the printf() and scanf() family of functions in DikuMUD, I'm not alone. Of course, the C++ purists would say you should use their annoying stream library, forcing you to write even more code for less control over the output. :)
08 Jun, 2014, Pymeus wrote in the 20th comment:
Votes: 0
quixadhal said:
and you have to use said wrappers in EVERY bit of code you write instead of the OS call you'd normally use.
Either we're not talking about the same thing, or this is wrong. Why would calling Safestring_write() preclude also calling write(), even right next to each other?

quixadhal said:
And that's just extra boiler-plate that YOU have to do
Which isn't a completely trivial consideration, but it's not quite the anathema you make it out to be. We're talking about trivial pieces of code and you won't need many of them. We've already spent more time debating it than they would've taken to write.
0.0/25