05 Oct, 2011, alcornj wrote in the 1st comment:
Votes: 0
I've been amateurly coding muds for close to 12 years, but have very little "formal" coding education…. I just downloaded a Rom mud one day and started teaching myself. I'm currently in the middle of writing two systems for a mud (my own code depository.. I don't EVER plan on making it playable, just code stuff here to practice on), one is a wow-style quest system, and the other is a simulated "instancing" system, also ala MMO style instances. My first problem is this: The aquest system has a constant table where I keep all the data. I tried to write my "aquest" data as a linked list, and copied objects as my example. I got everything in, compiled, no warnings, errors, etc. When I put my "load_aquests" code into the mud, but when I copyover it crashes, and all I get as to why is a "get_room_index: bad vnum xxxx" on a room that doesn't exist. As soon as I take it out, its fine. I checked my code again and again, and I don't have some crossed reference to room_index, or obj_index wherever I've got my aquest_index code in and I can't figure out whats happening.

Secondly, for my "instancing" code I've gotten the mud to successfully copy rooms, exits, mobs, mobprogs, etc, and for SOME reason objects crash when they try to copy. For lack of a better description, I've just copied the "oedit_copy" code verbatim (which works fine, btw). I set log messages to see if I could track down what happened, and it's supposed to log "copying obj1->name to obj2->name". But it's crashing when it tries to access obj2->name (or any property of the object). Whenever I oedit the object, or use oedit_copy, it works fine. I even tried calling oedit_copy in my function instead of doing a property by property assignment, THAT crashed. But it works for mobs and rooms perfectly so I'm pulling my hair out trying to figure out wtf's going wrong.

The last thing I can think to mention is that when I get the mud to crash through gdb, the backtrace doesn't show me anything. I"d be happy to post code here, but there would be a LOT of it to have to go through. If anybody has run into some similar problems, I'd appreciate any help I could get. Thanks for your time.
-Josh
05 Oct, 2011, Rarva.Riendf wrote in the 2nd comment:
Votes: 0
If gdb does not show anything valuable maybe you compiled with some optimizations. (every local variable will be erased as soon as they are not used anymore as an example).
You can also try to run your code through Valgrind.
A memory leak can trigger a bug in random places completely unrelated to the leak origin.
05 Oct, 2011, Exodus wrote in the 3rd comment:
Votes: 0
For the first question, it sounds like load_aquests is looping through rooms using a set number, rather than looping through each room in memory. You can try changing the load_aquests to search through only rooms that are loaded, or add a check in there somewhere to skip over bad pointers.
Example 1:

struct room_data * first_room; // Pointer to first room in list of rooms loaded into memory
struct room_data * last_room; // Pointer to last room in list of rooms loaded into memory

// Load quest data
void load_aquests( void )
{
struct room_data *room = NULL, *room_next = NULL;

for( room = first_room; room; room = room_next )
{
room_next = room->next;
// Do stuff
}
return;
}


Example 2:
struct room_data * first_room; // Pointer to first room in list of rooms loaded into memory
struct room_data * last_room; // Pointer to last room in list of rooms loaded into memory

// Load quest data
void load_aquests( void )
{
struct room_data *room = NULL, *room_next = NULL;

for( room = first_room; room; room = room_next )
{
if( !(room = get_room_index(x)) ) // Make sure room exists
continue;

// Do stuff
}
return;
}


As a general best practice with constantly modified data, it's a good idea to validate pointers before referencing them. Depending on the load order during a hotboot, it may be trying to load quests before it loads the rooms. Also another good practice. Don't reference things that don't exist yet. It will go boom every time.


As for the second question, I'm not very familiar with ROM but it could be a hash bucket problem. I know SMAUG has a quick_link function that makes a copy of a string that already exists in the hash table, ROM may have something similar. Take a look at the save/load routines, particularly where objects in a player file are read in and created.
05 Oct, 2011, Kline wrote in the 4th comment:
Votes: 0
People would be happy to help, but we need to see code to tell you anything useful. MB provides a paste bin for your large code chunks you wish to share.
05 Oct, 2011, alcornj wrote in the 5th comment:
Votes: 0
Thanks for the replies, and the code examples. Unfortunately, the code wasn't useful because aquests have NOTHING to do with rooms. They don't reference rooms, they ONLY reference the const table they're declared on. And they are the ABSOLUTE last thing ever loaded, after fix_exits, fix_mobprogs, etc. Sorry if this code paste doesn't put it in the right format. I'm very unfamiliar witht hese forums.

void load_aquests( void )
{
AQUEST_INDEX_DATA *pAquestIndex;
int i = 0;
int x = 0;

for ( i = 0; i < MAX_AQUESTS; i++ )
{
long vnum;
int iHash;

pAquestIndex = alloc_perm( sizeof(*pAquestIndex) );

vnum = aquest_table[i].vnum;

fBootDb = FALSE;
if ( get_aquest_index( vnum ) != NULL )
{
bug( "Load_aquests: vnum %d duplicated.", vnum );
exit( 1 );
}
fBootDb = TRUE;

pAquestIndex->vnum = vnum;

for (x = 0; x < MAX_AQMOBS; x++);
pAquestIndex->kills[x] = aquest_table[i].mob_kills[x];

for (x = 0; x < MAX_AQOBJS; x++);
pAquestIndex->loots[x] = aquest_table[i].obj_loots[x];

pAquestIndex->status = ELIGIBLE;

iHash = vnum % MAX_KEY_HASH;
pAquestIndex->next = aquest_index_hash[iHash];
aquest_index_hash[iHash]= pAquestIndex;
top_aquest_index++;
top_vnum_aquest = top_vnum_aquest < vnum ? vnum : top_vnum_aquest; /*$
}
}


Added code tags - Orrin
05 Oct, 2011, alcornj wrote in the 6th comment:
Votes: 0
Thank you Orrin.
Test
There's quite a bit of code involved with both of my projects, instancing AND aquests. I'd be happy to post -everything- if need be, or even upload the entire mud if somebody's willing to look at it. Also, thanks for mentioning Valgrind. I'm in class right now but I'll look into it this afternoon when I get home.
05 Oct, 2011, plamzi wrote in the 7th comment:
Votes: 0
Hi,

It seems like you're trying some pretty sophisticated mods, so I think it's time to learn to use "gdb". Compile with "-g3" so you'd have maximum debug information. Then run your binary with:

gdb -e <binary> –args <arguments>

Make it crash, and you'll have better visibility into the real reason. For instance, for your first problem, in some cases memory handling issues will not crash the game right away, so your final error may be a "red herring": the code messes up some memory which causes trouble a bit down the line in a seemingly unrelated part of the code. Backtracing with gdb should help you get to the real issue.

Good luck with your mods. Also, if you ever want to code for a live game (that's how I practice), come visit us. I already have one trainee and won't mind another.
05 Oct, 2011, alcornj wrote in the 8th comment:
Votes: 0
Thank you plamzi. I am compiling using gcc32, and I've tried running the mud and crashing it through gdb, but not with the options you mentioned. I'll try them immediately. I should have mentioned I installed Ferric's signal handler, and that might be part of the reason I don't get good logs. when it crashes, it logs his messages. Also, I code for a "live" mud currently but I wouldn't attempt to do something there I wasn't familiar with. Thats why I have this other one… to test new coding concepts and practice with them. I thank you for the invite, and may actually sign in to talk to you.

I forgot to ask, what do you mean by "compile with -g3". I tried setting my compiler to "CC = -g3"(where its set now to gcc32) in the makefile and that didn't work. Also, what gdb -arg arguments would I use?
05 Oct, 2011, plamzi wrote in the 9th comment:
Votes: 0
In my signal handler, I take care of some stuff and then do an abort (not intercepted), which gets me a core dump, debug info etc.

void signal_handler(int sig) {

bool signal_shutdown = FALSE;

switch(sig) {
case SIGBUS:
log("Received SIGBUS. Shutting down…");
signal_shutdown = TRUE;
break;
case SIGTERM:
log("Received SIGTERM. Shutting down…");
signal_shutdown = TRUE;
break;
case SIGABRT:
log("Received SIGABRT. Shutting down with core dump…");
break;
case SIGSEGV:
log("Received SIGSEGV. Shutting down…");
signal_shutdown = TRUE;
break;
case SIGINT:
log("Received SIGINT. Shutting down…");
signal_shutdown = TRUE;
break;
}

if (signal_shutdown) {
Crash_save_all();
abort();
}
}


P. S. I see you stopped by and I missed you by a few seconds. I'm online and semi-afk most of the day.
05 Oct, 2011, alcornj wrote in the 10th comment:
Votes: 0
Plamzi has been instrumental in helping me, and I appreciate the mudbytes community being SO friendly, helpful and patient with an amateur like myself. I got the instancing stuff fixed and working 110%.

Now on to aquests.

If anybody is interested, as a coder I've used snippets over the years (Now I try to write everything from scratch) and if any of these topics sound interesting, I'd be happy to post the mud. I've got tons of stuff I've coded into this mud becasue I've been working on it off and on for 10 years. I"ll post anything I have or give you the address if you want to look through the mud. Thanks again.
05 Oct, 2011, alcornj wrote in the 11th comment:
Votes: 0
Sorry for the spammy replies. I figured out what was wrong with loading my aquest stuff, and fixed it as well. Thank you guys again for being a wonderful, helpful community.
06 Oct, 2011, alcornj wrote in the 12th comment:
Votes: 0
New problem. My fread_aquests isn't right.

void fread_aquest( CHAR_DATA *ch, FILE *fp )
{
AQUEST_DATA *aq;
char *word;
bool fMatch;
bool fVnum;
bool first;
char *log = "";

fVnum = FALSE;
aq = NULL;
first = TRUE; /* used to counter fp offset */

word = feof( fp ) ? "End" : fread_word( fp );
if (!str_cmp(word,"Vnum" ))
{
log = "vnum";
long vnum;
first = FALSE; /* fp will be in right place */

vnum = fread_long( fp );
if ( get_obj_index( vnum ) == NULL )
{
bug( "Fread_aquest: bad vnum %ld.", vnum );
}
else
{
aq = create_aquest(get_aquest_index(vnum));
}
}

if (aq == NULL)
{
aq = new_aqdata();
}
fVnum = TRUE;

for ( ; ; )
{
if (first)
first = FALSE;
else
word = feof( fp ) ? "End" : fread_word( fp );
fMatch = FALSE;


switch ( UPPER(word[0]) )
{
case '*':
fMatch = TRUE;
fread_to_eol( fp );
break;

if ( !str_cmp( word, "End" ) )
{
log = "End";
extern AQUEST_DATA *aqdata_free;

if ( fVnum && aq->pIndexData == NULL )
{
bug( "Fread_aquest: incomplete aquest.", 0 );
aq->next = aqdata_free;

aqdata_free = aq;
return;
}
else
{
aquest_to_char(aq, ch, aq->status);
return;
}
}
break;

case 'K':
if ( !str_cmp( word, "Kills" ) )
{
log = "Kills";
int x;
for( x = 0; x < MAX_AQMOBS; x++)
aq->kills[x] = fread_number(fp);
fMatch = TRUE;
break;
}
break;

case 'L':
if ( !str_cmp( word, "Loots" ) )
{
log = "Loots";
int x;
for( x = 0; x < MAX_AQOBJS; x++)
aq->loots[x] = fread_number(fp);
fMatch = TRUE;
break;
}
break;

case 'S':
KEY( "Status", aq->status, fread_number( fp ) );
log = "Status";
break;

case 'V':
if ( !str_cmp( word, "Vnum" ) )
{
log = "Vnum";

int vnum;

vnum = fread_long( fp );
if ( ( aq->pIndexData = get_aquest_index( vnum ) ) == NULL )
bug( "Fread_aquest: bad vnum %ld.", vnum );
else
fVnum = TRUE;
fMatch = TRUE;
break;
}
break;
}

if ( !fMatch )
{
char log_buf[MSL];

sprintf(log_buf, "Fread_aquest: no match W(%s) L(%s)", word, log );
bug( log_buf, 0 );
fread_to_eol( fp );
}
}
}


In the log, I'm getting:

Wed Oct 5 20:29:15 2011 :: [*****] BUG: Fread_aquest: no match W(End) L(Loots)
Wed Oct 5 20:29:15 2011 :: [*****] BUG: Fread_aquest: no match W(#END) L(Loots)
Wed Oct 5 20:29:15 2011 :: [*****] BUG: Fread_aquest: no match W(End) L(Loots)
As my output, and aquests are being saved as:

#AQ
Vnum 1
Kills 0 0 0
Loots 0 0 0
Status 1
End

in my pfile. Any clue why loots is screwing it up? I appreciate any replies, thank you.
-Josh
06 Oct, 2011, kiasyn wrote in the 13th comment:
Votes: 0
case '*':
fMatch = TRUE;
fread_to_eol( fp );
break;

if ( !str_cmp( word, "End" ) )
{
log = "End";
extern AQUEST_DATA *aqdata_free;

if ( fVnum && aq->pIndexData == NULL )
{
bug( "Fread_aquest: incomplete aquest.", 0 );
aq->next = aqdata_free;

aqdata_free = aq;
return;
}
else
{
aquest_to_char(aq, ch, aq->status);
return;
}
}
break;


should be:
case '*':
fMatch = TRUE;
fread_to_eol( fp );
break;

case 'E':
if ( !str_cmp( word, "End" ) )
{
log = "End";
extern AQUEST_DATA *aqdata_free;

if ( fVnum && aq->pIndexData == NULL )
{
bug( "Fread_aquest: incomplete aquest.", 0 );
aq->next = aqdata_free;

aqdata_free = aq;
return;
}
else
{
aquest_to_char(aq, ch, aq->status);
return;
}
}
break;


notice the case 'E'
06 Oct, 2011, alcornj wrote in the 14th comment:
Votes: 0
You're brilliant. That was it. Thank you. I can't believe I missed that.
09 Oct, 2011, alcornj wrote in the 15th comment:
Votes: 0
New, wierd problem.

One of the things I keep track of in the quest are the mobs that you might need to kill. The problem is that somewhere with loading or assigning, the very first mob in the array (at index 0) isn't loading. Let me show you what I mean.

void load_aquests( void )
{
AQUEST_INDEX_DATA *pAquestIndex;
int i = 0;
int x = 0;

for ( i = 0; i < MAX_AQUESTS; i++ )
{
long vnum;
int iHash;

pAquestIndex = alloc_perm( sizeof(*pAquestIndex) );

vnum = aquest_table[i].vnum;

fBootDb = FALSE;
if ( get_aquest_index( vnum ) != NULL )
{
bug( "Load_aquests: vnum %d duplicated.", vnum );
//exit( 1 );
abort( );
}
fBootDb = TRUE;

pAquestIndex->vnum = vnum;


for (x = 0; x < MAX_AQMOBS; x++)
{
pAquestIndex->mvnums[x] = aquest_table[i].mobs[x];
pAquestIndex->kills[x] = aquest_table[i].mob_kills[x];
}

for (x = 0; x < MAX_AQOBJS; x++)
{
pAquestIndex->ovnums[x] = aquest_table[i].objs[x];
pAquestIndex->loots[x] = aquest_table[i].obj_loots[x];
}

pAquestIndex->status = ELIGIBLE;

iHash = vnum % MAX_KEY_HASH;
pAquestIndex->next = aquest_index_hash[iHash];
aquest_index_hash[iHash]= pAquestIndex;
top_aquest_index++;
top_vnum_aquest = top_vnum_aquest < vnum ? vnum : top_vnum_aquest; /* OLC */
}
}


/*
* Create an instance of an object.
*/
AQUEST_DATA *create_aquest( AQUEST_INDEX_DATA *pAquestIndex )
{
AQUEST_DATA *aq;
int x;

if ( pAquestIndex == NULL )
{
bug( "Create_aquest: NULL pAquestIndex.", 0 );
//exit( 1 );
abort( );
}

aq = new_aqdata();

for (x = 0; x < MAX_AQMOBS; x++)
{
aq->kills[x] = 0;
aq->mvnums[x] = pAquestIndex->mvnums[x];
}

for (x = 0; x < MAX_AQOBJS; x++)
{
aq->loots[x] = 0;
aq->ovnums[x] = pAquestIndex->ovnums[x];
}

aq->vnum = pAquestIndex->vnum;
aq->status = ELIGIBLE;
aq->pIndexData = pAquestIndex;
aq->next = aqdata_list;
aqdata_list = aq;
return aq;
}

void aquest_to_char( AQUEST_DATA *aquest, CHAR_DATA *ch, int status )
{
int x;

aquest->next = ch->aquest_data;
ch->aquest_data = aquest;

for (x = 0; x < MAX_AQMOBS; x++);
{
aquest->kills[x] = 0;
aquest->mvnums[x] = aquest->pIndexData->mvnums[x];
}

for (x = 0; x < MAX_AQOBJS; x++);
{
aquest->loots[x] = 0;
aquest->ovnums[x] = aquest->pIndexData->ovnums[x];
}

aquest->status = status;
return;
}

So later on, I have a brief throw-away option in my "aquest" command called "everything"

if (!str_cmp(arg1, "everything") )
{
AQUEST_DATA *alist;

for (alist = ch->aquest_data; alist != NULL; alist = alist->next)
{
chprintf(ch, "Vnum: %ld (%ld)\n\r", alist->vnum, alist->pIndexData->vnum);
chprintf(ch, "M1: %ld (%ld)\n\r", alist->mvnums[0], alist->pIndexData->mvnums[0]);
chprintf(ch, "M2: %ld (%ld)\n\r", alist->mvnums[1], alist->pIndexData->mvnums[1]);
chprintf(ch, "M3: %ld (%ld)\n\r", alist->mvnums[2], alist->pIndexData->mvnums[2]);
chprintf(ch, "O1: %ld (%ld)\n\r", alist->ovnums[0], alist->pIndexData->ovnums[0]);
chprintf(ch, "O2: %ld (%ld)\n\r", alist->ovnums[1], alist->pIndexData->ovnums[1]);
chprintf(ch, "O3: %ld (%ld)\n\r", alist->ovnums[2], alist->pIndexData->ovnums[2]);

chprintf(ch, "Status: %d (%d)\n\r", alist->status, alist->pIndexData->status);
}

return;
}
that outputs this:

OUTPUTS:
Vnum: 2 (2)
M1: 0 (1200)
M2: 1202 (1202)
M3: 1231 (1231)
O1: 1200 (1200)
O2: 1202 (1202)
O3: 1231 (1231)
Status: 1 (1)
Vnum: 1 (1)
M1: 0 (3000)
M2: 3001 (3001)
M3: 3002 (3002)
O1: 3004 (3004)
O2: 3005 (3005)
O3: 1231 (1231)
Status: 1 (1)

The M1 should match the others: 3000 (3000) and not be 0 (3000), same with the 1200. Why is it doing this?
Any help would be tremendously appreciated. Thank you.
10 Oct, 2011, alcornj wrote in the 16th comment:
Votes: 0
Nvm, got it. Thanks again to all those of you who looked.
08 Dec, 2011, klink wrote in the 17th comment:
Votes: 0
hey i just recently finished a WoW style questing system.
We have olc in a heavily modified rom2.4 code base.

i started by creating the olc side, and making qedit so you could create quests online
they save and load to area files based on vnum ranges and run as a linked list index.

Sample Quest in Qedit:
Quest Type: [Once]
Quest Title: [Begin your training…]
Prerequisite Quest(s): [20401]
Completion Mob: [20450]
Side available: [UM]
Class available: [All]
Level: [2]
Quest Description:
Seek out the captured slaves, punish 10 of them and then speak with mother.
She is pleased with those who obey her, do not let her down.

Quest Completion Description:
FOR THE HORDE!!!

Speak Quest Dialog:
I am delighted to see you have the stomach to punish those
who oppose us. Your journey has begun! pwn da n00bs!

Req# Type Amount Vnum – debug info – Quest_vnum Qreq_vnum
——- —— ——— ——— – —> – ——— ———
[0 ] [Speak ] [1 ] [20450 ] [20402 ] [20418 ]
[1 ] [Kill ] [10 ] [20416 ] [20402 ] [20417 ]

Item# Class Amount Vnum Short Description
——- ————– —— ——— —————–
[0 ] [Warlock ] [1 ] [40035 ] [the Matron Mothers sigil of Honor for Warlocks]
[1 ] [Necromancer ] [1 ] [40034 ] [the Matron Mothers sigil of Honor for Necromancers]
[2 ] [Illusionist ] [1 ] [40033 ] [the Matron Mothers sigil of Honor for Illusionists]
[3 ] [DeathKnight ] [1 ] [40032 ] [the Matron Mothers sigil of Honor for Death Knights]
[4 ] [Frost-Augur ] [1 ] [40031 ] [the Matron Mothers sigil of Honor for Frost Augurs ]
[5 ] [All ] [1 ] [1201 ] [A Restring Token]
[6 ] [Assassin ] [1 ] [40039 ] [the Matron Mothers sigil of Honor for Assassins]
[7 ] [All ] [5 ] [20417 ] [some butchered meat]
[8 ] [Barbarian ] [1 ] [40038 ] [the Matron Mothers sigil of Honor for Barbarians]
[9 ] [Warrior ] [1 ] [40037 ] [the Matron Mothers sigil of Honor for Warriors]
[10 ] [Witch ] [1 ] [40036 ] [the Matron Mothers sigil of Honor for Witches]


the quest requirements save to area files as well and load in a linked list index with a reference to the original
quest vnum, using fix_qreqs() i "attach" the qreqs to the quests in the index after everything is loaded.
same thing with item rewards in a way.

the code ive made has ability to add multiple quest requirements, multiple item rewards,
in game looks like this

Crado K'dan, the ancient Undead Archmagi needs your assistance with ….

[1] (D) [Begin your training…]

Command: Choose a quest number, list, or done



pick a number you get this
(Difficulty: XXXXXX) [Begin your training…]

+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
Seek out the captured slaves, punish 10 of them and then speak with mother.
She is pleased with those who obey her, do not let her down.
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=

[Quest Requirements:]
Speak to The Matron Mother Reborn
Kill 10 of a human slave

Do you accept or decline?


when you compelte the quest it pops out like this.

I am delighted to see you have the stomach to punish those
who oppose us. Your journey has begun! pwn da n00bs!


FOR THE HORDE!!!

+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=

You recieve A Restring Token.
You recieve the Matron Mothers sigil of Honor for Assassins.
You recieve [5] some butchered meat.
You receive 115 silver coins.

i did it on my admin so i only got coins but it pays out exp if you're in range.


quest log looks like this
[You are working on 8 quest at the moment.]
-+-+-+-+-+-+-+-+-+-+-+-+-+Quest Log+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
[1] (D) [Trouble in a half shell]
0 of 1 a giant turtle
[2] (D) [Herbs, Herbs, Herbs….]
0 of 1 a skeleton warrior
[3] (D) [Stones and Bones]
0 of 10 a tempered spirit
[4] (D) [Knight Only]
0 of 1 a giant turtle
[5] (D) [Fighter Only]
0 of 1 a giant turtle
[6] (D) [Priest Only]
0 of 1 a giant turtle
[7] (D) [Thief Only]
0 of 1 a giant turtle
[8] (D) [Wizard Only]
0 of 1 a giant turtle
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
Command: Choose a quest number, list, done


im interested to talk with you about your questing and instancing system and how you're going about it,
maybe we can get together sometime.
952-905-6355 shoot me a text sometime when you're available
0.0/17