23 Oct, 2008, quixadhal wrote in the 21st comment:
Votes: 0
Hehehehe, actually, that's a long-standing bug I hadn't bothered to fix because, well, nobody but me ever looked at the logs. The function used to break the time up into segments, localtime(), returns the year part as "the number of years elapsed since 1900". So, that is "correct" in that it's 108 years after 1900. It is not, however, very useful.

The version I modified for RaM uses 4-digit dates. :)

They are sequential, although those are two chunks from two different logs. I wanted to show the shutdown sequence as an example, and also some standard loading/reset messages.

The "type" tag is a field that can be grepped for if you want to look at a particular kind of event. I have macros for each of them, so entries like log_boot() show the source code file/function/line, whereas log_auth() or log_kill() show the character/victim/etc.

Putting dashes is easy enough. Back in 1995, I was using a vt220 terminal (I still have one… the vertical output transistor died in the last lightning storm we had though… they make great server console monitors). Thus, saving space was more important. How about "2008-10-23 14:20:03.011"?

It probably wouldn't be too hard to make them toggleable, certainly not by defines in the code, and probably not too hard as commands in game. Right now, the option of passing in a log filename is already in place, so you could redirect different types of events to different files. NOTE: If you are on a unix-like system, you can choose "/dev/null" as your filename, which will disable logging to a file (well, /dev/null will eat it), but still allow messages to online immortals.

All in all, my years of programming have taught me that a "bug" logger is useful, but an "event" logger is more reliable. Quite often, you don't see any cause for an error in the bug log, because the actual cause was something NOT happening that should have. For example, knowing that assigning a special-proc to mob 723611 failed is much more useful if you notice that foo.area didn't show up in the boot log, NOR did it show up as a failure during boot. That means one of your bozo underlings screwed up the area.list file again. :)
23 Oct, 2008, David Haley wrote in the 22nd comment:
Votes: 0
quixadhal said:
It probably wouldn't be too hard to make them toggleable, certainly not by defines in the code,

Another reason to use C++ – make them inline functions. Functions are generally cleaner than macros or defines whenever possible, and inlining means you don't have the overhead of a function call.
23 Oct, 2008, quixadhal wrote in the 23rd comment:
Votes: 0
I agree, although that's also supported in C as well. I'm still tallying up points to switch to C++, but I really think easier or simpler won't cut it. When we get to 'nobody is willing to hack that much C to do it', the switch will happen. *grin*

In any case, I'm just finishing up some more code to check in, and this reminded me of ONE case where #define'd macros trump functions.
#define log_error(Str, …) \
event_logger(LOG_ERROR, NULL, \
__FILE__, __PRETTY_FUNCTION__, __LINE__, \
NULL, NULL, \
GOD, (Str), ## __VA_ARGS__ )

In this particular example, using a define allows the compiler to fill in the correct values for FILE, FUNCTION, and LINE, without you having to do anything clever. You put calls like log_error( "I suck!" ), and it logs the information about where the bug happened for you. If you used inline'd functions, you wouldn't get the compiler's data, since they'd always evaluate to the place event_logger() lives, or you'd have to pass them in, which would look ugly.
23 Oct, 2008, David Haley wrote in the 24th comment:
Votes: 0
I didn't realize that inline functions were a part of C – it's probably a gcc extension?

And your criterion for change is a good one to make change very unlikely. :wink: Personally I disagree that making life easier is not a good reason to change given how much easier things could be with relatively little code change.

And yes, the __FILE__ etc. macros are pretty much the only reason to use macros in a case like this IMO.
23 Oct, 2008, quixadhal wrote in the 25th comment:
Votes: 0
Inline probably is a gnu extension, although it might be part of C99 by now.

Heh, you're preaching to the choir David. It's not MY criterion, it's my assumption about the group's criterion, given the amount of tooth pulling that had to be done just to switch us to using g++ as the default compiler. If I had my way, I'd spend a week ripping out every single instance of a char * and replacing them all with std::string's, using .cstr() wherever I have to interact with an OS call. :)

While I'm here, this is the current list of "types" I have for event logging… feel free to suggest more if there's something that you think happens often enough to warrant a category of its own.

"INFO",
"ERROR",
"FATAL",
"BOOT",
"AUTH",
"KILL",
"GAIN",
"RESET"


The old bug() stuff currently uses "ERROR", and the old log_string() stuff currently uses "INFO".
23 Oct, 2008, David Haley wrote in the 26th comment:
Votes: 0
I like the difference between logging categories, and varying levels of logging in a given category. For example, debug/info/warning/error/fatal are all levels of the same kind of message. You might only want to see errors or higher normally, but turn on less severe levels when debugging or investigating something. But you might want to always leave on your "kill" or "gain" categories – which don't make sense as part of the same category.

If a MUD has any kind of player-theft, I would have a theft category for logs as well.
23 Oct, 2008, Sandi wrote in the 27th comment:
Votes: 0
quixadhal said:
Heh, you're preaching to the choir David. It's not MY criterion, it's my assumption about the group's criterion, given the amount of tooth pulling that had to be done just to switch us to using g++ as the default compiler.


It seems that everyone that knows C++ prefers it. However, it also seems ROM is the codebase of choice for those that know little to no programing. Leaving aside the question of whether or not such people should attempt to run any MUD, would a hybrid of C and C++ be a benefit to them?



For event/error types, how about "OLC" or "PROG"?
23 Oct, 2008, David Haley wrote in the 28th comment:
Votes: 0
Well, mostly indirectly. They don't have to necessarily touch or even know about some of the C++ parts. But it means that the codebase developers (who presumably do know what they're doing) can do more work, more quickly. So the end-user MUD admin sees the benefit of a MUD with more features and quicker development time.

Also, I'm not convinced that it's really that much harder to learn simple C++ for a total newbie than simple C. Actually, sometimes simple C++ is easier, because things can be more organized (e.g. encapsulated in objects).
23 Oct, 2008, quixadhal wrote in the 29th comment:
Votes: 0
I don't actually know C++ all that well, but I've worked in quite a few languages besides C, and, for me, it's more of a matter of LOATHING the way C deals with strings and function prototypes, than really wanting to jump on the C++ bandwagon.

It makes sense to deal with strings as a real type, 99% of the time. On the rare occasions you really want an array of characters, you can convert a string into such a thing, but most of the time you're only traversing the character array to find something, or to try to work around C's limitations.

It also makes sense to be able to have multiple functions with the same name, when they do the same thing, but work with different arguments. Overloading lets you do that. In C, we need to make ch_printf, room_printf, zone_printf, socket_printf, act_printf, hackeysack_printf…. all of which are fancy versions of printf, directed at different targets.

ON THAT NOTE:

I have a small C issue, if anyone wants to help Google and I find a solution?

I have a nice function called event_logger, it looks like this:
void                    event_logger( unsigned int Type, const char *BugFile,
const char *File, const char *Func, int Line,
struct char_data *ch, struct char_data *victim,
int Level, const char *Str, … )
__attribute__ ( ( format( printf, 9, 10 ) ) );


You call it from macros that make life simpler, such as:
#define log_auth(Ch, Str, …) \      
event_logger(LOG_AUTH, NULL, \
NULL, NULL, 0, \
(Ch), NULL, \
GOD, (Str), ## __VA_ARGS__ )


All well and dandy, works great! However, you might notice that these are variadic functions… they take a variable number of arguments, and in fact there is a call to vsnprintf( Temp, MAX_STRING_LENGTH, Str, arg ); hidden away under the hood. No big deal right? What's the problem?

Well, in C, you can't call one variadic function from another. More precisely, you can't pass the variable part of the argument list. So, when one vararg function (such as….
void                    proper_exit( int code, const char *Str, … )
__attribute__ ( ( format( printf, 2, 3 ) ) );;
decides to log an error…. it has two choices.

It can parse the arguments, doing all the work with vsprintf() itself, and then pass a single string into the log function, which will then wastefully call vsprintf() again with nothing to format….

Or, we can refactor the function into a calling API and a secondary backend like:
void                    va_event_logger( unsigned int Type, const char *BugFile,
const char *File, const char *Func, int Line,
struct char_data *ch, struct char_data *victim,
int Level, const char *Str, va_list varg );


In here, we expect a va_list argument, which we internally copy via the va_copy() macro, and then pretend we had the same variable arguments we wanted to get in the first place!

Of course… as facinating as this surely is to everyone… I wouldn't be posting it if there weren't…. a problem. :)
bug.c: In function 'void va_event_logger(unsigned int, const char*, const char*, const char*, int, char_data*, char_data*, int, const char*, char*)':
bug.c:143: warning: function might be possible candidate for 'printf' format attribute


This is the place I call va_event_logger() from the API function… specifically, the complaint is that va_event_logger() uses vsnprintf(), but doesn't have a format attribute. DUH! The API wrapper function handles that, I don't get variable arguments, I get a va_list!

If I try to add the format attribute, it will fail because the function prototype is not variable. If I don't, I get this lovely error.

Of course, I can fall back to version 1 of the solution, but I hate wasting space unless I have no other solution. Soooo, wanna take a stab at it? :devil:
23 Oct, 2008, David Haley wrote in the 30th comment:
Votes: 0
Sane handling of strings is actually an extremely good reason to use C++ especially for newbie programmers. Even with fairly experienced people on these forums, C-style strings are the source of many errors.

As for the varargs thing, I'm too tired to think about that now. :tongue: My solution: use streams… :devil:
24 Oct, 2008, quixadhal wrote in the 31st comment:
Votes: 0
Well, here's the output from our little project with the new error handler and the MINIMUM amount of changes needed to get it going.

quixadhal@andropov:~/svn/ram-project/area$ ../src/rom 4000
<: 2008-10-23 19:59:43.613 - ERROR - : Fix_exits: 10525:1 -> 10535:3 -> 10534.
<: 2008-10-23 19:59:43.614 - ERROR - : Fix_exits: 3458:2 -> 3472:0 -> 10401.
<: 2008-10-23 19:59:43.615 - ERROR - : Fix_exits: 8705:4 -> 8706:5 -> 8708.
<: 2008-10-23 19:59:43.615 - ERROR - : Fix_exits: 8717:2 -> 8719:0 -> 8718.
<: 2008-10-23 19:59:43.617 - ERROR - : Read_object: vnum 3200 bad type.
<: 2008-10-23 19:59:43.618 - ERROR - : Read_object: vnum 3200 bad type.
<: 2008-10-23 19:59:43.618 - ERROR - : Read_object: vnum 3200 bad type.
<: 2008-10-23 19:59:43.619 - ERROR - : Read_object: vnum 3200 bad type.
<: 2008-10-23 19:59:43.619 - ERROR - : Read_object: vnum 3200 bad type.
<: 2008-10-23 19:59:43.619 - ERROR - : Read_object: vnum 3200 bad type.
<: 2008-10-23 19:59:43.619 - ERROR - : Read_object: vnum 3200 bad type.
<: 2008-10-23 19:59:43.619 - ERROR - : Read_object: vnum 3200 bad type.
<: 2008-10-23 19:59:43.620 - ERROR - : Read_object: vnum 3200 bad type.
<: 2008-10-23 19:59:43.620 - ERROR - : Read_object: vnum 3200 bad type.
<: 2008-10-23 19:59:43.620 - ERROR - : Read_object: vnum 3200 bad type.
<: 2008-10-23 19:59:43.620 - ERROR - : Read_object: vnum 3200 bad type.
<: 2008-10-23 19:59:43.620 - ERROR - : Read_object: vnum 3200 bad type.
<: 2008-10-23 19:59:43.622 - ERROR - (db.c;reset_area,1449)
: Err: obj an icicle (9227) – 28, mob the Ice Bandit (9228) – 24
<: 2008-10-23 19:59:43.622 - ERROR - (db.c;reset_area,1449)
: Err: obj elemental wand of wind and air (9218) – 27, mob an alchemist (9234) – 13
<: 2008-10-23 19:59:43.623 - ERROR - (db.c;reset_area,1449)
: Err: obj an ice staff (9216) – 25, mob a baby rainbow dragon (9235) – 16
<: 2008-10-23 19:59:43.623 - ERROR - (db.c;reset_area,1449)
: Err: obj an ice staff (9216) – 25, mob a puddle (9214) – 8
<: 2008-10-23 19:59:43.623 - ERROR - (db.c;reset_area,1449)
: Err: obj elemental wand of fire (9215) – 16, mob a flame (9215) – 4
<: 2008-10-23 19:59:43.623 - ERROR - (db.c;reset_area,1449)
: Err: obj elemental wand of fire (9215) – 16, mob a flame (9215) – 4
<: 2008-10-23 19:59:43.624 - ERROR - (db.c;reset_area,1449)
: Err: obj an elemental rod of earthquake (9217) – 7, mob a small rock (9217) – 3
<: 2008-10-23 19:59:43.624 - ERROR - (db.c;reset_area,1449)
: Err: obj elemental wand of wind and air (9218) – 27, mob a small spark (9218) – 4
<: 2008-10-23 19:59:43.624 - ERROR - (db.c;reset_area,1449)
: Err: obj elemental wand of wind and air (9218) – 27, mob an eddie (9225) – 2
<: 2008-10-23 19:59:43.630 - ERROR - (db.c;reset_area,1449)
: Err: obj a wet noodle (8010) – 5, mob a Futsie (8002) – 17
<: 2008-10-23 19:59:43.632 - ERROR - : Read_object: vnum 3200 bad type.
<: 2008-10-23 19:59:43.634 - ERROR - : Read_object: vnum 3200 bad type.
<: 2008-10-23 19:59:43.638 - INFO - : ROM is ready to rock on port 4000.
<: 2008-10-23 19:59:47.135 - FATAL - (comm.c;proper_exit,2159)
: Game_loop: select: stall: Interrupted system call


Hmmmm, perhaps 3 digits isn't enough precision these days? We never saw those being the same on the old 486/133. *grin*
24 Oct, 2008, David Haley wrote in the 32nd comment:
Votes: 0
Are you referring to the precision of the usec field? I think it's probably fine as-is…

But error messages like these:
<: 2008-10-23 19:59:43.630 - ERROR    - (db.c;reset_area,1449)
: Err: obj a wet noodle (8010) – 5, mob a Futsie (8002) – 17

… are not really all that clear to me. What went wrong?
24 Oct, 2008, Sandi wrote in the 33rd comment:
Votes: 0
This might be a ROM specific thing, but it's warning you a level 17 mob has a level 5 weapon. :)

I agree, it's a bit opaque to non-Builders.
24 Oct, 2008, David Haley wrote in the 34th comment:
Votes: 0
Oh, that makes sense now. But why not just say that in the message then? :wink:

E.g.

<: 2008-10-23 19:59:43.630 - ERROR    - (db.c;reset_area,1449)
: Err: mob/obj level mismatch: obj a wet noodle (8010) – 5, mob a Futsie (8002) – 17
24 Oct, 2008, quixadhal wrote in the 35th comment:
Votes: 0
Yes, well, actually fixing the error messages so the CONTENT is useful is another pass entirely. :)

Since the new style of logging already tells you that it's an error, "Err: " can go away. In fact, that should probably be replaced by:

<: 2008-10-23 19:59:43.630 - BALANCE - (db.c;reset_area,1449)
Level 5 weapon "a wet noodle"(#8010), wielded by level 17 mob "a Futsie"(#8002)

Of course, why you should care that a mob is wielding a LOWER level item, I'm not sure… but then I really don't care for the whole concept of item levels at all. It seems like a band-aid slapped on to fix a problem that was really builders not paying attention to the stats they gave equipment compared to how difficult it was to obtain.
24 Oct, 2008, David Haley wrote in the 36th comment:
Votes: 0
I think that item levels can be a pretty useful heuristic and statement of intention. It's not always easy to use them right, but the concept isn't necessarily inherently flawed. That said, I don't like it when the game strictly enforces item levels.
24 Oct, 2008, quixadhal wrote in the 37th comment:
Votes: 0
WileyMUD uses rent code, and so if you manage to get ahold of the #1 best item in the game, you are MORE than welcome to enjoy using it until you go to log out. At that point, if you're a low level character, you probably won't have even a fraction of the cash needed to actually store the item in question. So, the problem solves itself.

As for NPC's getting weapons they "shouldn't" have…. they're also free to loot corpses or pick them up off the ground (or steal them, if they're a thief and you don't have them equipped), and then equip them if they're better than what they've got. However, since mobs don't save state, that also goes away at reboot.

If a builder put a naughty weapon into the resets, THAT is the problem that needs to be fixed, not slapping arbitrary limits in the code.

If you want to do data-mining, I find it more useful to look at the actual amount of damage being done, rather than some kind of supposedly average level range. You could simply log whenever a melee hit does more than what you consider a normal range (probably level * 10 * some_class_factor, on my game)… or likewise if someone has too many hit points, or whatever other metric seems reliable.

However, I know ROM tried to make *everything* level-based, even mobs… presumably because the coders didn't really trust their builders to have a sense of balance… so you just say an orc is a level 8 mob, and it figures out the hit points, damage, experience, etc… I'm hoping that's overridden if you fill out all the details yourself, if not…. the data file and I may come to blows. :)
24 Oct, 2008, David Haley wrote in the 38th comment:
Votes: 0
Well, like I said I would use it more as a statement of intention than something to enforce via the system. I also think that a level can be useful to generate item stats, instead of trusting people to set reasonable stats. If stats are derived from level, you guarantee much more consistency. Frankly, I don't trust anybody to always correctly set values. I would much rather have a system where values are automatically populated based on a few key values, and then let people tweak if necessary.
24 Oct, 2008, Vassi wrote in the 39th comment:
Votes: 0
DavidHaley said:
Well, like I said I would use it more as a statement of intention than something to enforce via the system. I also think that a level can be useful to generate item stats, instead of trusting people to set reasonable stats. If stats are derived from level, you guarantee much more consistency. Frankly, I don't trust anybody to always correctly set values. I would much rather have a system where values are automatically populated based on a few key values, and then let people tweak if necessary.


<tangent>
for Cyra (my mud :P) we're going to try to prototype a system where damage is based on the player's skill more-so than the weapon so that the same weapon does more damage in the hands of a higher level (i.e. more skilled) player than in the hands of a lower leveled player. The concept of weapon level isn't really going away, though, instead we're going to try to go for a sort of quality based on materials. The better the quality of the material the weapon is made from the higher level of an enchantment it can hold, or the more lower level enchantments it can hold (assuming an enchantment costs points - higher quality materials have a bigger point pool). In addition, while not able to tweak damage, we're hoping to let designers tweak other things like some variation on speed, likelyhood of fumble, the weapon's weight and the weapon's balance - where balance offsets some of the weight for calculating fatigue loss per swing.

We're hoping these factors will still make it desireable to look for better weapons as you become more skilled, but since we're going to primarily be a PvP mud we want to make a player battle-effective with whatever weapons they have on hand, assuming that they'll hoard the 'good' stuff for hunting and pick up whatever for PvP raids.
</tangent>
20.0/39