Short: Evermore: bad stack at F_RETURN Date: Sat, 27 May 2000 17:35:48 +0200 From: Heiko Kopp <hkopp@informatik.uni-rostock.de> Type: Bug State: Abandoned - no longer appears in 3.2.9-dev.390 Driver: dev.203 The problem lies further back than the instruction trace shows. Hallo Lars, Evermore hat uns heute bei dev203 mit einem Crash gegruesst. Folgende Infos hab ich: 2000.05.27 13:30:30 Bad stack at F_RETURN, 2 values too high 2000.05.27 13:30:30 Current object was domains/races/drow/quest/cl/M/apprentice#349 global/server/events global/server/events.c line 113 2b7510: 103 1 aggregate (3) line 113 2b7513: 105 258 m_caggregate (3) 2b7516: 89 3 push_local_variable_lvalue (1) 2b7518: 60 (void)+= (2) 2b7519: 7 8 cstring0 (0) line 114 2b751b: 7 9 cstring0 (1) 2b751d: 7 12 cstring0 (2) 2b751f: 5 1 identifier (3) 2b7521: 121 call_other (4) etc/shared etc/shared.c line 22 19745f: 82 call_function_by_address (0) line 22 197427: 106 previous_object0 (0) line 6 197428: 178 object_name (1) 197429: 22 7429 switch (1) 19742c: 13 const1 (0) line 15 19742d: 19 return (1) 197463: 79 276 branch_when_non_zero (1) line 22 197466: 12 const0 (0) line 24 197467: 398 get_extra_wizinfo (1) kernel/master kernel/master.c line 1345 29fbdf: 24 0 local (0) line 1345 29fbe1: 22 switch (1) 29fc07: 24 1 local (0) line 1362 29fc09: 178 object_name (1) 29fc0a: 22 2312 switch (1) 29fc0d: 13 const1 (0) line 1365 29fc0e: 19 return (1) etc/shared etc/shared.c line 24 197469: 169 mappingp (1) line 24 19746a: 79 branch_when_non_zero (1) 197472: 24 1 local (0) line 26 197474: 12 const0 (1) 197475: 398 get_extra_wizinfo (2) kernel/master kernel/master.c line 1345 29fbdf: 24 0 local (0) line 1345 29fbe1: 22 switch (1) 29fc07: 24 1 local (0) line 1362 29fc09: 178 object_name (1) 29fc0a: 22 2312 switch (1) 29fc0d: 13 const1 (0) line 1365 29fc0e: 19 return (1) etc/shared etc/shared.c line 26 197477: 24 0 local (2) line 26 197479: 90 push_indexed_lvalue (3) 19747a: 35 (void)= (2) 19747b: 20 return0 (0) line 27 global/server/events global/server/events.c line 114 2b7523: 71 pop_value (1) line 114 2b7524: 20 return0 (0) line 115 domains/races/drow/quest/cl/M/apprentice#349 <lambda ?> line 0 ffbea98a: 19 return (1) line 0 kernel/simul_efun kernel/simul/events.c line 47 9e4105: 71 pop_value (1) line 47 9e4106: 20 return0 (0) line 48 domains/races/drow/quest/cl/M/apprentice#349 basic/living/skills.c line 423 2b701b: 71 pop_value (3) line 423 2b701c: 105 1 m_caggregate (2) line 424 2b701f: 84 1 push_identifier_lvalue (3) 2b7021: 35 (void)= (4) 2b7022: 105 1 m_caggregate (2) line 425 2b7025: 84 3 push_identifier_lvalue (3) 2b7027: 35 (void)= (4) 2b7028: 12 const0 (2) line 426 2b7029: 12 const0 (3) 2b702a: 12 const0 (4) 2b702b: 103 3 aggregate (5) 2b702e: 84 4 push_identifier_lvalue (3) 2b7030: 35 (void)= (4) 2b7031: 20 return0 (2) line 427 2b7032: 25 8 0 54 59 238 1 0 ' _call_out_stub' in 'basic/living/addons/walker.c' ('domains/races/drow/guardserver/patrolmaintainer')line 52 ' do_walk' in 'domains/races/drow/quest/cl/M/walkingtower.c' ('domains/races/drow/quest/cl/M/walkingtower')line 152 <lambda 0x1c0c91> in 'domains/races/drow/quest/cl/M/walkingtower.c' ('domains/races/drow/quest/cl/M/walkingtower')offset 75 ' reset' in ' std/room.c' ('domains/races/drow/quest/cl/R/movingtower1')line 153 ' reset' in ' basic/create.c' ('domains/races/drow/quest/cl/R/movingtower1')line 75 ' refresh' in 'domains/races/drow/inherits/room_town.c' ('domains/races/drow/quest/cl/R/movingtower1')line 91 ' refresh' in ' std/room.c' ('domains/races/drow/quest/cl/R/movingtower1')line 143 ' populate' in 'domains/races/drow/quest/cl/R/movingtower1.c' ('domains/races/drow/quest/cl/R/movingtower1')line 74 <lambda 0x197831> in 'domains/races/drow/quest/cl/R/movingtower1.c' ('domains/races/drow/quest/cl/R/movingtower1')offset 128 ' reset' in 'domains/races/drow/quest/cl/M/apprentice.c' ('domains/races/drow/quest/cl/M/apprentice#349')line 57 ' reset' in ' std/monster.c' ('domains/races/drow/quest/cl/M/apprentice#349')line 114 ' reset' in ' basic/create.c' ('domains/races/drow/quest/cl/M/apprentice#349')line 74 ' create' in 'domains/races/drow/quest/cl/M/apprentice.c' ('domains/races/drow/quest/cl/M/apprentice#349')line 17 ' create' in ' std/npc.c' ('domains/races/drow/quest/cl/M/apprentice#349')line 15 ' create' in ' std/monster.c' ('domains/races/drow/quest/cl/M/apprentice#349')line 89 ' create' in ' basic/living.c' ('domains/races/drow/quest/cl/M/apprentice#349')line 622 ' create' in 'basic/living/skills.c' ('domains/races/drow/quest/cl/M/apprentice#349')line 427 Gruss Heiko --__________________________________________ Archwizard Bardioc@Evermore The Lands of Evermore - a fantasy mud Come and join us at telnet://mud.evermore.de Write me a mail at bardioc@evermore.de ---------------------------------- apprentice.c: /* * apprentice of evilwiz * */ #pragma strong_types #pragma combine_strings #include <domains.h> #include DROW_PATH "/paths.h" #include D_CLACKER "/clacker.h" inherit "/std/npc"; #include <alignment.h> void create() { if (!clonep()) return; ::create(); set_id(({"apprentice"})); set_name("apprentice"); set_level(20); set_skill("punch",70,1); set_skill("blindfight",90,1); set_skill("kick",70,1); set_skill("dodge",70,1); set_skill("parry",60,1); set_skill("sword",90,1); set_skill("whip",80,1); set_skill("erinn",100,1); set_skill("common",0,1); set_race("human"); set_vision(2); set_al(AL_EVIL); set_tongue("erinn"); set_short("wizard's apprentice"); set_long("\ The evil apprentice of the evil wizard. He looks like he will protect his master \ with his life.\n"); set_male(); set_resistence(([ "magic" : 70, "fire" : 70, "psionic" : 90, "unholy" : 80, "holy" : -10, "poison" : 90, "electric" : 50, ])); set_aggressive(1); // its too dark !!!! } void reset(int arg) { ::reset(arg); if (!present("whip")) { move_object(clone_object(D_OBJW "whip"),this_object()); } init_command("wield all"); if (!present("robe")) { move_object(clone_object(D_OBJA "robe"),this_object()); } init_command("wear all"); } ---------------------------------- npc.c: /* Copyright TubMud 1997 */ /* Many or some changes for Evermore 1997 */ inherit "/basic/living/chatter"; inherit "/std/monster"; inherit "/basic/living/feeling"; inherit "/basic/living/chat"; inherit "/basic/living/reaction"; inherit "/basic/living/mood"; inherit "/basic/living/sequence"; inherit "/basic/living/magic"; void create() { monster::create(); set_name("somenpc"); } void refresh() { monster::refresh(); mood::refresh(); chatter::refresh(); } status query_busy() { return // chatter::query_busy() || sequence::query_busy() || monster::query_busy(); } // 'query_busy' means that the monster shouldn't do the default actions // (like running away) and 'query_blocking' shound mean that even no chat/ // feeling-reactions are allowed. status query_blocking() { return // chatter::query_busy() || sequence::query_busy() || monster::query_busy(); } set_defaults(int a) { return chatter::set_defaults(a); } void heart_beat() { object ob; chatter::heart_beat(); sequence::heart_beat(); monster::heart_beat(); } void catch_tell(string str) { reaction::catch_tell(str); // monster::catch_tell(str); } void init() { reaction::init(); monster::init(); } ---------------------------------- skills.c: /* Copyright Evermore 1997 */ /* * /basic/living/skills.c */ #pragma strong_types #include <server.h> #include <event.h> #include <skills.h> #include <wizlevels.h> #include <libs.h> #include <commands.h> // How much points does it need to learn a point by seeing if there // are no other modifiers #define WATCHING_QUOTIENT 3 // Bandwidth for learning skills. Result of test_and_improve_skill // must be withing -bandwidth and +bandwidth #define L_BANDWIDTH 20 // Maximum learning value on learning by doing #define MAX_LEARN 20 inherit "basic/create"; int query_age(); int query_skill_value(string skill); int query_level(); string query_profession(); string query_race(); int query_skill_value(string skillid); // Skill ID -> Learned level (0..100000) see x do x teach mapping skills; // Skill ID -> talent in % mapping talents; static mapping last_learn; static int *skill_slots; // Skill slots in use static int *slot_maxima; // own personalized version of slot maxima private void __recalculate_skill(string id,int see,int by_do,int teach) { skill_slots[SE_SKILL->query_skill_category(id)]++; // Really in correct order. Pass 2 will decrement slot count on killing. if(!sizeof(SE_SKILL->query_skill_data(id))) see = by_do = teach = 0; } void clear_skill(string skill); void __ditch_cleared_skill(string skill) { if(!query_skill_value(skill)) clear_skill(skill); } // Pass 1 counts skill slots anew and zeroes all skills which are deleted. // Pass 2 will ditch zeroed skills. varargs void recalculate_skills(int *limits) { skill_slots = ({ 0, 0, 0 }); walk_mapping(skills,#'__recalculate_skill); map_indices(copy_mapping(skills),#'__ditch_cleared_skill); } varargs int raw_learn_skill(string skillid,int value,int what, status ignore_lims) { int *learn_limits; int i; if(!(learn_limits = SE_SKILL->get_learn_limits(skillid,this_object(),ignore_lims))) return 0; if(!skills) skills = ([ ]); // Unknown skills won't be learned if(!member(skills,skillid)) return 0; if(value <= 0) return 0; if(value+skills[skillid,0]+skills[skillid,1]+skills[skillid,2] > learn_limits[0]) value = learn_limits[0]-(skills[skillid,0]+skills[skillid,1]+skills[skillid,2]); // Over 100%. Simulate set_skill(skill,100). if(skills[skillid,0]+skills[skillid,1]+skills[skillid,2] > 100000) { skills[skillid,0] = 0; skills[skillid,1] = 0; skills[skillid,2] = 0; value = 100000; what = -1; } if(what >= 0 && what < 3) { if(value+skills[skillid,what] > learn_limits[what+1]) value = learn_limits[what+1] - skills[skillid,what]; if (value < 0) return 0; skills[skillid,what] += value; } else { int newvalue; newvalue = value; // Looks like a 'bottle and different sized glasses'-problem: // You have to divide something in equally shared portions, as long // as the portions fit into their containers. If not, divide the surplus // among the remaining containers. while(newvalue > 0) { skills[skillid,0] += newvalue/3; if(skills[skillid,0] > learn_limits[1]) { newvalue += (skills[skillid,0] - learn_limits[1])*3/2; skills[skillid,0] = learn_limits[1]; } skills[skillid,1] += newvalue/3; if(skills[skillid,1] > learn_limits[2]) { newvalue += skills[skillid,1] - learn_limits[2]*3; skills[skillid,1] = learn_limits[2]; } skills[skillid,2] += newvalue/3; newvalue = 0; if(skills[skillid,2] > learn_limits[3]) { newvalue = skills[skillid,2] - learn_limits[3]; skills[skillid,2] = learn_limits[3]; } } } return 0; } /* * Queries the value triple of one skill */ int *query_skill_values(string skillid) { if(!member(skills,skillid)) return 0; return ({ skills[skillid,0], skills[skillid,1],skills[skillid,2] }); } /* * Queries the skill value of one skill */ int query_skill_value(string skillid) { if(!member(skills,skillid)) return 0; return skills[skillid,0]+skills[skillid,1]+skills[skillid,2]; } /* * Same as above goes for percentages -- please use these one */ int *query_skill_percents(string skillid) { int *h,i; mixed test; if(!(h = query_skill_values(skillid))) return 0; return map(h, (: ($1 + 500 ) / 1000 :)); } int query_skill_percent(string skillid) { return (query_skill_value(skillid)+500)/1000; } /* * Sets the talent value in percent. If somebody has a high talent, * he can learn the skill faster than others. */ void set_talent_value(string skillid,int value) { if(!talents) talents = ([ ]); talents[skillid] = value; } /* * Queries the talent value. * If the talent is not registered, it will be regarded as default 100. */ int query_talent_value(string skillid) { if(!talents || !member(talents,skillid)) return 100; return talents[skillid]; } /* * Receive a learn event. * If it isn't the own one, the player learns a wee bit by watching. */ void receive_learn_event(mapping e) { mixed *skilldata; int value, how; string skillid; if(!living(this_object())) return; skillid = e[E_SKILLID]; value = e[E_VALUE]; how = e[E_HOW]; if(this_object() != e[E_AGENT]) { if(how == 2) return; // Can't see if someone did it by trainer value /=WATCHING_QUOTIENT; how = 0; if(random(5)) return; } skilldata = SE_SKILL->query_skill_data(skillid); if(!skilldata) return; value = (value*query_talent_value(skillid))/100; // Talent value = (value*100)/SE_SKILL->get_learning_speed(skillid,this_object()); raw_learn_skill(skillid,value,how); } /* * That does nothing else than sending the event, waiting for the own * person and the surroundings to be caught. */ void learn_skill(string skillid,int value,int how) { send_event(E_LEARNED, ([ E_AGENT: this_object(), E_SKILLID: skillid, E_VALUE: value, E_HOW: how ]), environment(this_object())); } varargs string query_skill_tree(int displaywidth) { return "/global/commands/score"->query_skill_tree(displaywidth); } mapping query_skills() { return skills; } // Return own personalized version of slot maxima int *query_slot_maxima() { return slot_maxima; } varargs void recalculate_slots(int *limits) { string prof; if(!limits) limits = LIB_PROFESSION->query_slot_maxima(prof = query_profession()); // Enforce copying of the array limits += ({ }); if(limits[SKC_COMBAT] < 0) limits[SKC_COMBAT] += sizeof(SE_SKILL->query_leaves("combat")); if(limits[SKC_MAGIC] < 0) { int bonus, h; bonus = (prof != "Priest") && sizeof(SE_SKILL->query_leaves("magic/arts")); h = bonus + sizeof(SE_MAGIC->may_have_spells(this_object(),1)); limits[SKC_MAGIC] = h*(-limits[SKC_MAGIC])/100; } if(limits[SKC_OTHER] < 0) limits[SKC_OTHER] += sizeof(SE_SKILL->query_leaves()) - sizeof(SE_SKILL->query_leaves("combat")) - sizeof(SE_SKILL->query_leaves("magic")); slot_maxima = limits; } int query_used_slots(int what) { if(what == SKC_ALL) return skill_slots[SKC_COMBAT] + skill_slots[SKC_MAGIC] + skill_slots[SKC_OTHER]; else return skill_slots[what]; } int query_max_slots(int what) { int base,low,high; int race_bonus; int h; if(what == SKC_ALL) return query_max_slots(SKC_COMBAT) + query_max_slots(SKC_MAGIC) + query_max_slots(SKC_OTHER); if (is_wizard(this_object())) return 1000; race_bonus = ((DSE_RACEDATA-> get_race_data(query_race(),"slot_boni")) || ({ 0,0,0 }))[what]; low = (LIB_PROFESSION->query_slot_minima(query_profession()))[what]; // We need to calculate the maxima personalized to the player. high = (query_slot_maxima())[what]; // Linear functions between two points, round down and up to avoid // getting the very last slots only on reaching Level 49 base = low + ((high-low)*(query_level()-1)+(PL_MAX_PLAYER_LEVEL-1)/2)/ (PL_MAX_PLAYER_LEVEL-1); return base ? (((h = base + race_bonus) < 0) ? 0 : h) : 0; } int query_free_slots(int what) { return query_max_slots(what) - query_used_slots(what); } /* * Maybe the heart of the skill system. * Gets a skill ID and a value from 0 (easy for everyone) to * 100 (Even masters have problems), the bandwidth * (measurement upto which success/failure a player may learn from that * attempt) * and returns * -99 (complete failure) over 0 (almost worked) to 99 (wonderful) */ varargs int test_and_improve_skill(string skillid,int difficulty,int bandwidth) { int res; int learn_value; int age; if(!skillid) raise_error("No Skill ID given.\n"); // Fails if you don't have the skill if(!query_skill_value(skillid)) return -99; if(!bandwidth) bandwidth = L_BANDWIDTH; res = query_skill_percent(skillid)-difficulty-19+random(40); if(res < -99) res = -99; if(res > 99) res = 99; // Success/failure within bandwidth? if(res > -bandwidth && res < bandwidth) { if(!member(last_learn,skillid) || (last_learn[skillid]+2 < (age = query_age()))) { // is a parabola with maximum at zero learn_value = -(res-bandwidth)*(res+bandwidth); // scale down learn_value = learn_value*MAX_LEARN/(bandwidth*bandwidth); learn_skill(skillid,learn_value,1); // Learning by doing last_learn[skillid] = age; } } return res; } #define GET_LEAVES(x) (SE_SKILL->query_leaves(x)) void clear_skill(string skillid) { if(member(skills,skillid)) { m_delete(skills,skillid); skill_slots[SE_SKILL->query_skill_category(skillid)]--; } } varargs void set_skill(string skillid,int level) { clear_skill(skillid); if(level) { // raw_learn_skill requires the skill to be present skills += ([ skillid: 0; 0; 0 ]); // Let set_skill ignore the limits raw_learn_skill(skillid,level*1000,-1,1); skill_slots[SE_SKILL->query_skill_category(skillid)]++; } } void set_skills(int level) { level = (level-1)*19/10+5; map(GET_LEAVES("combat/unarmed") || ({ }),#'set_skill,level); map(GET_LEAVES("combat/defense") || ({ }),#'set_skill,level); set_skill("twohanded",level); } void clear_skills() { skills = ([ ]); skill_slots = ({ 0, 0, 0 }); } void create() { listen_event(E_LEARNED,0,#'receive_learn_event); skills = ([ ]); last_learn = ([ ]); skill_slots = ({ 0, 0, 0 }); } ----------------------------------