/* * NAME: object.c * DESCRIPTION: generic MOO object */ # define DEBUG 0 inherit core "/std/core"; inherit cache "/std/cache"; inherit "/std/string"; inherit "/std/data"; # if DEBUG inherit "/std/vartext"; # else # define var2str(arg) "" # endif # include <objects.h> # include <moo/data.h> # include <moo/perms.h> # include <moo/verbinfo.h> # include <moo/command.h> # include <moo/verb.h> # include <moo/config.h> # include "object.h" # include "dbloader.h" object parent; /* parent(this) */ object *children; /* children(this) */ string b_name; /* this.name */ int b_owner; /* this.owner */ object b_location; /* this.location */ object *b_contents; /* this.contents */ int b_flags; /* object flags */ mapping properties; /* all property slots */ /* ({ owner, perms, value[, cname || 0] }) */ mixed **verbs; /* this object's defined verbs */ /* ({ owner, perms, names, prep, object }) */ mixed *bootstrap; /* bootstrapping information */ /* * NAME: create() * DESCRIPTION: called by DGD when this object is created */ static void create(void) { core::create(); cache::reset(CONFIG->query(CF_VERB_CACHE)); } /* * NAME: init() * DESCRIPTION: initialize this object with parent and owner */ void init(object nparent, int nowner) { object ob; parent = nparent; children = ({ }); b_name = ""; b_owner = nowner; b_location = 0; b_contents = ({ }); b_flags = 0; properties = parent ? parent->child_props(nowner) : ([ ]); verbs = ({ }); if ((ob = MOOOBJ(nowner)) && ob != this_object()) ob->set_owner_flag(); } /* * NAME: bootstrap() * DESCRIPTION: called by dbloader to bootstrap (instead of init) */ void bootstrap(mixed *descrip) { mixed *v; parent = 0; /* not known yet */ children = ({ }); /* not known yet */ b_name = descrip[O_NAME]; b_owner = descrip[O_OWNER]; b_location = 0; /* not known yet */ b_contents = ({ }); /* not known yet */ b_flags = (descrip[O_FLAGS] & 0xff) | F_OWNER; if (b_flags & F_PLAYER) global->set_player_flag(this_object(), 1); properties = ([ ]); /* not known yet */ verbs = 0; /* not known yet */ bootstrap = descrip; } /* * NAME: get_bootstrap_info() * DESCRIPTION: return this object's bootstrapping information */ mixed *get_bootstrap_info(void) { return bootstrap; } /* * NAME: bootstrap_links() * DESCRIPTION: trace hierarchies and link references */ void bootstrap_links(void) { mixed *obdef, *pdefs, *pvals, *info; int vc, i, sz, inher; if (parent = MOOOBJ(bootstrap[O_PARENT])) parent->gain_child(this_object()); if (b_location = MOOOBJ(bootstrap[O_LOCATION])) b_location->gain_content(this_object()); obdef = bootstrap; pdefs = obdef[O_PROPDEFS]; pvals = obdef[O_PROPVALS]; if (pvals == 0) return; vc = inher = 0; while (obdef) { if (pdefs) { for (i = 0, sz = sizeof(pdefs); i < sz; ++i, ++vc) { string name, cname; if (inher) properties[tolower(pdefs[i])] = ({ pvals[vc][P_OWNER], pvals[vc][P_PERMS], pvals[vc][P_VALUE], }); else properties[name = tolower(cname = pdefs[i])] = ({ pvals[vc][P_OWNER], pvals[vc][P_PERMS], pvals[vc][P_VALUE], cname == name ? 0 : cname, }); } } if (obdef[O_PARENT] != -1) { obdef = MOOOBJ(obdef[O_PARENT])->get_bootstrap_info(); pdefs = obdef[O_PROPDEFS]; inher = 1; } else obdef = 0; } if (vc != sizeof(pvals)) error("Inconsistent property defs/values: " + vc + "/" + sizeof(pvals)); bootstrap[O_PROPVALS] = 0; info = bootstrap[O_VERBDEFS]; verbs = allocate(sz = sizeof(info)); for (i = sz; i--; ) verbs[i] = ({ info[i][V_OWNER], info[i][V_PERMS], info[i][V_NAME], info[i][V_PREP] >= 0 ? info[i][V_PREP] + 1 : info[i][V_PREP], 0 /* vobj */, }); bootstrap[O_VERBDEFS] = 0; } /* * NAME: bootstrap_cleanup() * DESCRIPTION: erase bootstrapping info */ void bootstrap_cleanup(void) { bootstrap = 0; } /* * NAME: bootstrap_verb() * DESCRIPTION: install a verb */ void bootstrap_verb(int vnum, object vobj) { (verbs[vnum][VERB_OBJECT] = vobj)->ref(); } /* * NAME: get_flags() * DESCRIPTION: return (standard) b_flags (used by dbsaver) */ int get_flags(void) { return b_flags & 0xff; } /* * NAME: child_props() * DESCRIPTION: return a mapping of properties for children */ mapping child_props(int nowner) { mapping nprops; string *keys; mixed **data; int i; nprops = ([ ]); keys = map_indices(properties); data = map_values(properties); for (i = sizeof(keys); i--; ) { mixed *src; src = data[i]; nprops[keys[i]] = ({ (src[PROP_PERMS] & P_CHOWN) ? nowner : src[PROP_OWNER], src[PROP_PERMS], STW(0), }); } return nprops; } /* * NAME: is_player() * DESCRIPTION: return true iff this object is a player */ int is_player(void) { return b_flags & F_PLAYER; } /* * NAME: is_programmer() * DESCRIPTION: return true iff this object is a programmer */ int is_programmer(void) { return b_flags & F_PROGRAMMER; } /* * NAME: is_wizard() * DESCRIPTION: return true iff this object is a wizard */ int is_wizard(void) { return b_flags & F_WIZARD; } /* * NAME: is_owner() * DESCRIPTION: does this object own any object/property/verb besides its own? */ int is_owner(void) { return b_flags & F_OWNER; } /* * NAME: set_owner_flag() * DESCRIPTION: this object owns an object/property/verb besides its own */ void set_owner_flag(void) { b_flags |= F_OWNER; } /* * NAME: set_player_flag() * DESCRIPTION: alter the player bit */ void set_player_flag(int value) { if (value && ! (b_flags & F_PLAYER)) { b_flags |= F_PLAYER; global->set_player_flag(this_object(), 1); } else if (! value && (b_flags & F_PLAYER)) { b_flags &= ~F_PLAYER; global->set_player_flag(this_object(), 0); } } /* * NAME: get_parent() * DESCRIPTION: return the parent of this object */ object get_parent(void) { return parent; } /* * NAME: get_children() * DESCRIPTION: return the list of this object's children */ object *get_children(void) { return children; } /* * NAME: get_ancestors() * DESCRIPTION: return our entire heritage */ object *get_ancestors(void) { object ob, *ancestors; ob = parent; ancestors = ({ }); while (ob) { ancestors += ({ ob }); ob = ob->get_parent(); } return ancestors; } /* * NAME: get_prop_names() * DESCRIPTION: return a list of properties defined on _this_ object */ string *get_prop_names(void) { string *names, *props; mixed *data; int i, j, sz; names = map_indices(properties); data = map_values(properties); props = allocate(sz = sizeof(names)); for (i = 0, j = -1; i < sz; ++i) if (! INHERITED(data[i])) props[++j] = names[i]; return props[.. j]; } /* * NAME: get_prop_data() * DESCRIPTION: return the internal data for a property */ mixed *get_prop_data(string name) { return properties[name]; } /* * NAME: get_verb_data() * DESCRIPTION: return the internal data for all verbs */ mixed *get_verb_data(void) { return verbs; } /* * NAME: rec_defined_prop() * DESCRIPTION: return 1 iff this or any descendant defines the property */ int rec_defined_prop(string name) { mixed *prop; int i; if ((prop = properties[name]) && ! INHERITED(prop)) return 1; for (i = sizeof(children); i--; ) if (children[i]->rec_defined_prop(name)) return 1; return 0; } /* * NAME: rec_add_prop() * DESCRIPTION: insert the given property to this object and all descendants */ void rec_add_prop(string name, int nowner, int perms) { int i; properties[name] = ({ (perms & P_CHOWN ? b_owner : nowner), perms, STW(0), }); for (i = sizeof(children); i--; ) children[i]->rec_add_prop(name, nowner, perms); } /* * NAME: rec_del_prop() * DESCRIPTION: remove the indicated property from this and all descendants */ void rec_del_prop(string name) { int i; properties[name] = 0; for (i = sizeof(children); i--; ) children[i]->rec_del_prop(name); } /* * NAME: dump_verb_cache() * DESCRIPTION: return contents of verb cache (DEBUGGING) */ mixed dump_verb_cache(void) { return cache::dump(); } /* * NAME: do_chparent() * DESCRIPTION: atomically change our parent (locked) */ static int do_chparent(object newparent) { object oldparent, common; object *oldancestors, *newancestors; int i, oldsz, newsz, oldind, newind; if (newparent == parent) return E_NONE; oldancestors = get_ancestors(); newancestors = newparent ? ({ newparent }) + newparent->get_ancestors() : ({ }); oldsz = sizeof(oldancestors); newsz = sizeof(newancestors); /* verify that our new parent is not a descendant */ for (i = newsz; i--; ) if (newancestors[i] == this_object()) return E_INVARG; /* verify that none of our descendants defines a prop with the same name as one of our new ancestors' */ for (i = newsz; i--; ) { string *props; int j; props = newancestors[i]->get_prop_names(); for (j = sizeof(props); j--; ) if (rec_defined_prop(props[j])) return E_INVARG; } /* find our common ancestor */ for (i = 0; i < oldsz; ++i) { int j; for (j = 0; j < newsz; ++j) if (oldancestors[i] == newancestors[j]) break; if (j < newsz) { oldind = i; newind = j; common = oldancestors[i]; break; } } if (! common) { oldind = oldsz; newind = newsz; } /* remove all properties defined by oldancestors[.. oldind - 1] */ for (i = oldind; i--; ) { string *props; int j; props = oldancestors[i]->get_prop_names(); for (j = sizeof(props); j--; rec_del_prop(props[j])); } /* add all properties defined by newancestors[.. newind - 1] */ for (i = newind; i--; ) { string *props; int j; props = newancestors[i]->get_prop_names(); for (j = sizeof(props); j--; ) { mixed *data; data = newancestors[i]->get_prop_data(props[j]); rec_add_prop(props[j], data[PROP_OWNER], data[PROP_PERMS]); } } if (parent) parent->lose_child(this_object()); if (parent = newparent) parent->gain_child(this_object()); return E_NONE; } /* * NAME: chparent() * DESCRIPTION: change our parent */ int chparent(object newparent, mixed *info) { if (info && /* info == 0 called by recycle() */ PROGRAMMER(info) != b_owner && ! WIZARDP(info)) return E_PERM; return with_lock("do_chparent", newparent); } /* * NAME: lose_child() * DESCRIPTION: a child is being reparented */ void lose_child(object ob) { children -= ({ ob }); } /* * NAME: gain_child() * DESCRIPTION: a child is being reparented to us */ void gain_child(object ob) { children += ({ ob }); } /* * NAME: get_property_data() * DESCRIPTION: return the property mapping (used by dbsaver) */ mapping get_property_data(void) { return properties; } /* * NAME: get_properties() * DESCRIPTION: return a list of all properties defined on this object */ MOOVAL get_properties(mixed *info) { MOOVAL *props; string *keys; mixed **data; int i, j, sz; if (! (b_flags & F_READ) && PROGRAMMER(info) != b_owner && ! WIZARDP(info)) return STW(E_PERM); keys = map_indices(properties); data = map_values(properties); props = allocate(sz = sizeof(keys)); for (i = 0, j = -1; i < sz; ++i) { mixed *src; string name; if (! INHERITED(src = data[i])) props[++j] = (name = src[PROP_CNAME]) ? STR(name) : STR(keys[i]); } return LST(props[.. j]); } /* * NAME: get_name() * DESCRIPTION: return the builtin name of this object (SAFE) */ string get_name(void) { return b_name; } /* * NAME: get_owner() * DESCRIPTION: return the builtin owner of this object (SAFE) */ int get_owner(void) { return b_owner; } /* * NAME: get_location() * DESCRIPTION: return the builtin location of this object (SAFE) */ object get_location(void) { return b_location; } /* * NAME: get_contents() * DESCRIPTION: return the builtin contents of this object (SAFE) */ object *get_contents(void) { return b_contents; } /* * NAME: safe_get_property() * DESCRIPTION: return the value of a property regardless (SAFE) */ MOOVAL safe_get_property(string name) { mixed *prop; MOOVAL value; if (! (prop = properties[name])) return STW(E_PROPNF); value = prop[PROP_VALUE]; return STWP(value) ? parent->safe_get_property(name) : moo_int2ext(value); } /* * NAME: safe_set_property() * DESCRIPTION: set the value of a property regardless (SAFE) */ int safe_set_property(string name, MOOVAL value) { mixed *prop; if (! (prop = properties[name])) return E_PROPNF; prop[PROP_VALUE] = moo_ext2int(value); return E_NONE; } /* * NAME: get_property() * DESCRIPTION: return the value of the requested property (MUTABLE) */ MOOVAL get_property(string name, mixed *info) { switch (name = tolower(name)) { case "name": return STR(b_name); case "owner": return OBJ(b_owner); case "location": return b_location ? OBJ_OBJNUM(b_location) : OBJ(-1); case "contents": return OBJLIST2MOO(b_contents); case "programmer": return NUM((b_flags & F_PROGRAMMER) != 0); case "wizard": return NUM((b_flags & F_WIZARD) != 0); case "r": return NUM((b_flags & F_READ) != 0); case "w": return NUM((b_flags & F_WRITE) != 0); case "f": return NUM((b_flags & F_FERTILE) != 0); default: { mixed *prop; MOOVAL value; if (! (prop = properties[name])) return STW(E_PROPNF); if (! (prop[PROP_PERMS] & P_READ) && PROGRAMMER(info) != prop[PROP_OWNER] && ! WIZARDP(info)) return STW(E_PERM); value = prop[PROP_VALUE]; return STWP(value) ? parent->safe_get_property(name) : moo_int2ext(value); } } } /* * NAME: set_property() * DESCRIPTION: change the specified property */ int set_property(string name, MOOVAL value, mixed *info) { switch (name = tolower(name)) { case "name": if (! STRP(value)) return E_TYPE; if ((b_flags & F_PLAYER) && ! WIZARDP(info)) return E_PERM; b_name = STRVAL(value); return E_NONE; case "owner": { object ob; if (! OBJP(value)) return E_TYPE; if (! WIZARDP(info)) return E_PERM; b_owner = OBJVAL(value); if ((ob = MOOOBJ(b_owner)) && ob != this_object()) ob->set_owner_flag(); return E_NONE; } case "location": case "contents": return E_PERM; case "programmer": if (! WIZARDP(info)) return E_PERM; if (! (b_flags & F_PLAYER)) return E_INVARG; if (TRUTHOF(value)) b_flags |= F_PROGRAMMER; else b_flags &= ~F_PROGRAMMER; return E_NONE; case "wizard": if (! WIZARDP(info)) return E_PERM; if (! (b_flags & F_PLAYER)) return E_INVARG; if (TRUTHOF(value)) { b_flags |= F_WIZARD; global->log_msg("WIZARDED: #" + (string) OBJNUM(this_object()) + " by programmer #" + (string) PROGRAMMER(info)); /* log a traceback? */ } else b_flags &= ~F_WIZARD; return E_NONE; case "r": if (PROGRAMMER(info) != b_owner && ! WIZARDP(info)) return E_PERM; if (TRUTHOF(value)) b_flags |= F_READ; else b_flags &= ~F_READ; return E_NONE; case "w": if (PROGRAMMER(info) != b_owner && ! WIZARDP(info)) return E_PERM; if (TRUTHOF(value)) b_flags |= F_WRITE; else b_flags &= ~F_WRITE; return E_NONE; case "f": if (PROGRAMMER(info) != b_owner && ! WIZARDP(info)) return E_PERM; if (TRUTHOF(value)) b_flags |= F_FERTILE; else b_flags &= ~F_FERTILE; return E_NONE; default: { mixed *prop; if (! (prop = properties[name])) return E_PROPNF; if (PROGRAMMER(info) != prop[PROP_OWNER] && ! (prop[PROP_PERMS] & P_WRITE) && ! WIZARDP(info)) return E_PERM; prop[PROP_VALUE] = moo_ext2int(value); return E_NONE; } } } /* * NAME: strpropperms2int() * DESCRIPTION: return an int code for a property perms string */ private int strpropperms2int(string perms) { int i, intperms; for (i = strlen(perms); i--; ) { switch (perms[i]) { case 'r': case 'R': intperms |= P_READ; break; case 'w': case 'W': intperms |= P_WRITE; break; case 'c': case 'C': intperms |= P_CHOWN; break; default: return -1; } } return intperms; } /* * NAME: intpropperms2str() * DESCRIPTION: return a property perms string from an int code */ private string intpropperms2str(int perms) { return ((perms & P_READ) ? "r" : "") + ((perms & P_WRITE) ? "w" : "") + ((perms & P_CHOWN) ? "c" : ""); } /* * NAME: strverbperms2int() * DESCRIPTION: return an int code for a verb perms string */ private int strverbperms2int(string perms) { int i, intperms; for (i = strlen(perms); i--; ) { switch (perms[i]) { case 'r': case 'R': intperms |= P_READ; break; case 'w': case 'W': intperms |= P_WRITE; break; case 'x': case 'X': intperms |= P_EXECUTE; break; case 'd': case 'D': intperms |= P_DEBUG; break; default: return -1; } } return intperms; } /* * NAME: intverbperms2str() * DESCRIPTION: return a verb perms string from an int code */ private string intverbperms2str(int perms) { return ((perms & P_READ) ? "r" : "") + ((perms & P_WRITE) ? "w" : "") + ((perms & P_EXECUTE) ? "x" : "") + ((perms & P_DEBUG) ? "d" : ""); } /* * NAME: get_property_info() * DESCRIPTION: return owner/perms info on property */ MOOVAL get_property_info(string name, mixed *info) { mixed *prop; if (! (prop = properties[name = tolower(name)])) return STW(E_PROPNF); if (! (prop[PROP_PERMS] & P_READ) && PROGRAMMER(info) != prop[PROP_OWNER] && ! WIZARDP(info)) return STW(E_PERM); return LST( ({ OBJ(prop[PROP_OWNER]), STR(intpropperms2str(prop[PROP_PERMS])) }) ); } /* * NAME: set_property_info() * DESCRIPTION: set owner/perms info on a property */ int set_property_info(string name, int nowner, string nperms, mixed *info) { int perms; mixed *prop; object ob; if (! (prop = properties[name = tolower(name)])) return E_PROPNF; if (PROGRAMMER(info) != prop[PROP_OWNER] && ! WIZARDP(info) && ! (prop[PROP_PERMS] & P_WRITE)) return E_PERM; if (nowner != b_owner && ! WIZARDP(info)) return E_INVARG; perms = strpropperms2int(nperms); if (perms < 0) return E_INVARG; prop[PROP_OWNER] = nowner; prop[PROP_PERMS] = perms; if ((ob = MOOOBJ(nowner)) && ob != this_object()) ob->set_owner_flag(); return E_NONE; } /* * NAME: builtin_prop() * DESCRIPTION: return 1 iff a property is built-in */ private int builtin_prop(string name) { switch (name) { case "name": case "owner": case "location": case "contents": case "programmer": case "wizard": case "r": case "w": case "f": return 1; default: return 0; } } /* * NAME: do_add_property() * DESCRIPTION: atomically add a new property (locked) */ static int do_add_property(string cname, MOOVAL value, int nowner, string nperms) { string name; int perms; object ob; if (properties[name = tolower(cname)] || (perms = strpropperms2int(nperms)) < 0 || builtin_prop(name) || rec_defined_prop(name)) return E_INVARG; rec_add_prop(name, nowner, perms); properties[name] = ({ nowner, perms, value, cname == name ? 0 : cname }); if ((ob = MOOOBJ(nowner)) && (ob != this_object() || (ob == this_object() && sizeof(children)))) ob->set_owner_flag(); return E_NONE; } /* * NAME: add_property() * DESCRIPTION: create a new property slot on this object & all descendants */ int add_property(string cname, MOOVAL value, int nowner, string nperms, mixed *info) { if (PROGRAMMER(info) != b_owner && ! WIZARDP(info) && ! (b_flags & F_WRITE)) return E_PERM; if (nowner != b_owner && ! WIZARDP(info)) return E_INVARG; return with_lock("do_add_property", cname, value, nowner, nperms); } /* * NAME: delete_property() * DESCRIPTION: remove the given property */ int delete_property(string name, mixed *info) { mixed prop; if (PROGRAMMER(info) != b_owner && ! WIZARDP(info) && ! (b_flags & F_WRITE)) return E_PERM; if (! (prop = properties[name = tolower(name)]) || INHERITED(prop)) return E_PROPNF; with_lock("rec_del_prop", name); return E_NONE; } /* * NAME: is_clear_property() * DESCRIPTION: return NUM(1) iff property is clear */ MOOVAL is_clear_property(string name, mixed *info) { mixed *prop; MOOVAL val; if (builtin_prop(name = tolower(name))) return NUM(0); if (! (prop = properties[name])) return STW(E_PROPNF); if (! (prop[PROP_PERMS] & P_READ) && PROGRAMMER(info) != prop[PROP_OWNER] && ! WIZARDP(info)) return STW(E_PERM); val = prop[PROP_VALUE]; return STWP(val) ? NUM(1) : NUM(0); } /* * NAME: clear_property() * DESCRIPTION: make the indicated property clear */ int clear_property(string name, mixed *info) { mixed *prop; if (builtin_prop(name = tolower(name))) return E_INVARG; if (! (prop = properties[name])) return E_PROPNF; if (PROGRAMMER(info) != prop[PROP_OWNER] && ! WIZARDP(info) && ! (prop[PROP_PERMS] & P_WRITE)) return E_PERM; if (! INHERITED(prop)) return E_INVARG; prop[PROP_VALUE] = STW(0); return E_NONE; } /* * NAME: get_verbs() * DESCRIPTION: return list of object's verbs */ MOOVAL get_verbs(mixed *info) { MOOVAL *list; int i; if (! (b_flags & F_READ) && PROGRAMMER(info) != b_owner && ! WIZARDP(info)) return STW(E_PERM); for (list = allocate(i = sizeof(verbs)); i--; ) list[i] = STR(verbs[i][VERB_NAMES]); return LST(list); } /* * NAME: fertile() * DESCRIPTION: return 1 iff we are fertile to the given programmer */ int fertile(mixed *info) { return (b_flags & F_FERTILE) || PROGRAMMER(info) == b_owner || WIZARDP(info); } /* * NAME: modify_quota() * DESCRIPTION: change this object's ownership_quota property */ int modify_quota(int howmuch) { MOOVAL oq; int quota; oq = safe_get_property("ownership_quota"); if (! NUMP(oq)) return E_NONE; quota = NUMVAL(oq) + howmuch; if (howmuch < 0 && quota < 0) return E_QUOTA; safe_set_property("ownership_quota", NUM(quota)); return E_NONE; } /* * NAME: do_create_child() * DESCRIPTION: atomically create a child (locked) */ static object do_create_child(int nowner) { object obj; obj = global->create_object(this_object(), nowner); children += ({ obj }); return obj; } /* * NAME: create_child() * DESCRIPTION: make a child object */ MOOVAL create_child(JS_PROTO, int nowner) { object owner_ob, ob; JS_BEGIN; if (! fertile(info)) return STW(E_PERM); owner_ob = MOOOBJ(nowner); if (owner_ob && owner_ob->modify_quota(-1) != E_NONE) return STW(E_QUOTA); PUSH(OBJ_OBJNUM(ob = with_lock("do_create_child", nowner))); JS_PREP(1); RET = ob->call_verb(JS_DATA(1), "initialize", verb_vars(info, ob, "initialize")); JS_END; return POP(); JS_END; } /* * NAME: find_exec_verb() * DESCRIPTION: return a +x verb on this object */ static mixed *find_exec_verb(string name) { int i, sz; for (i = 0, sz = sizeof(verbs); i < sz; ++i) { mixed *verb; if (! ((verb = verbs[i])[VERB_PERMS] & P_EXECUTE)) continue; if (! verbname_match(verb[VERB_NAMES], name)) continue; return verb; } return 0; } /* * NAME: find_spec_verb() * DESCRIPTION: return a verb with specific arguments */ static mixed *find_spec_verb(string name, int spec) { string scrap; int num, i, sz; if (spec == VSPEC_ANY && (sscanf(name, "%d%s", num, scrap) != 2 || strlen(scrap))) num = -1; for (i = 0, sz = sizeof(verbs); i < sz; ++i) { mixed *verb; verb = verbs[i]; if (spec == VSPEC_ANY) { if (num == i) return verb; } else { int vspec, args; if ((vspec = verb[VERB_PREP]) != VS_ANY && vspec != VSPEC_PREP(spec)) continue; args = verb[VERB_ARGS]; if ((vspec = DOBJ(args)) != VS_ANY && vspec != VSPEC_DOBJ(spec)) continue; if ((vspec = IOBJ(args)) != VS_ANY && vspec != VSPEC_IOBJ(spec)) continue; } if (! verbname_match(verb[VERB_NAMES], name)) continue; return verb; } return 0; } # if 0 /* * NAME: find_local_verb() * DESCRIPTION: return a verb on this object */ mixed *find_local_verb(string name, int spec) { string scrap; int num, i, sz; if (spec == VSPEC_ANY && (sscanf(name, "%d%s", num, scrap) != 2 || strlen(scrap))) num = -1; for (i = 0, sz = sizeof(verbs); i < sz; ++i) { mixed *verb; if (spec == VSPEC_ANY && num == i) return verbs[i]; verb = verbs[i]; switch (spec) { case VSPEC_EXEC: if (verb[VERB_PERMS] & P_EXECUTE) break; else continue; case VSPEC_ANY: break; default: { int vspec; if ((vspec = verb[VERB_PREP]) != VS_ANY && vspec != VSPEC_PREP(spec)) continue; if ((vspec = DOBJ(verb[VERB_ARGS])) != VS_ANY && vspec != VSPEC_DOBJ(spec)) continue; if ((vspec = IOBJ(verb[VERB_ARGS])) != VS_ANY && vspec != VSPEC_IOBJ(spec)) continue; } } if (! verbname_match(tolower(verb[VERB_NAMES]), name)) continue; return verb; } return 0; } # endif /* * NAME: cache_miss() * DESCRIPTION: find a verb the hard way */ static mixed cache_miss(string key) { return find_exec_verb(key); } /* * NAME: find_verb() * DESCRIPTION: return an (inherited) +x verb */ mixed *find_verb(string name) { mixed *slot; slot = cache::fetch(name); return slot ? ({ this_object(), slot }) : (parent ? parent->find_verb(name) : 0); } /* * NAME: find_command() * DESCRIPTION: return an (inherited) verb with specific arguments */ mixed *find_command(string name, int spec) { mixed *slot; slot = find_spec_verb(name, spec); return slot ? ({ this_object(), slot }) : (parent ? parent->find_command(name, spec) : 0); } /* * NAME: get_verb_info() * DESCRIPTION: return owner, perms, and names for a verb */ MOOVAL get_verb_info(string name, mixed *info) { mixed *verb; if (! (verb = find_spec_verb(name, VSPEC_ANY))) return STW(E_VERBNF); if (! (verb[VERB_PERMS] & P_READ) && PROGRAMMER(info) != verb[VERB_OWNER] && ! WIZARDP(info)) return STW(E_PERM); return LST( ({ OBJ(verb[VERB_OWNER]), STR(intverbperms2str(verb[VERB_PERMS] & VP_PERMMASK)), STR(verb[VERB_NAMES]), }) ); } /* * NAME: set_verb_info() * DESCRIPTION: change verb owner, perms, and names */ int set_verb_info(string name, int nowner, string nperms, string nnames, mixed *info) { mixed *verb; int perms; object ob; if (! (verb = find_spec_verb(name, VSPEC_ANY))) return E_VERBNF; if (PROGRAMMER(info) != verb[VERB_OWNER] && ! WIZARDP(info) && ! (verb[VERB_PERMS] & P_WRITE)) return E_PERM; if (nowner != verb[VERB_OWNER] && ! WIZARDP(info)) return E_PERM; if ((perms = strverbperms2int(nperms)) < 0) return E_INVARG; verb[VERB_OWNER] = nowner; verb[VERB_PERMS] = (verb[VERB_PERMS] & ~VP_PERMMASK) | perms; verb[VERB_NAMES] = nnames; cache::reset(); if ((ob = MOOOBJ(nowner)) && ob != this_object()) ob->set_owner_flag(); return E_NONE; } /* * NAME: get_verb_args() * DESCRIPTION: return dobj, prep, and iobj for a verb */ MOOVAL get_verb_args(string name, mixed *info) { mixed *verb; if (! (verb = find_spec_verb(name, VSPEC_ANY))) return STW(E_VERBNF); if (! (verb[VERB_PERMS] & P_READ) && PROGRAMMER(info) != verb[VERB_OWNER] && ! WIZARDP(info)) return STW(E_PERM); return LST( ({ STR(global->vs_name(DOBJ(verb[VERB_ARGS]))), STR(global->prep_name(verb[VERB_PREP])), STR(global->vs_name(IOBJ(verb[VERB_ARGS]))), }) ); } /* * NAME: set_verb_args() * DESCRIPTION: change verb arguments */ int set_verb_args(string name, string dobj, string prep, string iobj, mixed *info) { mixed *verb; int dobjc, prepc, iobjc; if (! (verb = find_spec_verb(name, VSPEC_ANY))) return E_VERBNF; if (PROGRAMMER(info) != verb[VERB_OWNER] && ! (verb[VERB_PERMS] & P_WRITE) && ! WIZARDP(info)) return E_PERM; if (! (dobjc = global->vs_code(dobj)) || ! (prepc = global->prep_code(prep)) || ! (iobjc = global->vs_code(iobj))) return E_INVARG; verb[VERB_ARGS] = (verb[VERB_ARGS] & ~(VP_DOBJMASK | VP_IOBJMASK)) | SDOBJ(dobjc) | SIOBJ(iobjc); verb[VERB_PREP] = prepc; cache::reset(); return E_NONE; } /* * NAME: add_verb() * DESCRIPTION: create a new verb */ int add_verb(int nowner, string nperms, string names, string dobj, string prep, string iobj, mixed *info) { mixed *verb; int perms, dobjc, prepc, iobjc; object ob; if (PROGRAMMER(info) != b_owner && ! WIZARDP(info) && ! (b_flags & F_WRITE)) return E_PERM; if (PROGRAMMER(info) != nowner && ! WIZARDP(info)) return E_PERM; if ((perms = strverbperms2int(nperms)) < 0) return E_INVARG; if (! (dobjc = global->vs_code(dobj)) || ! (prepc = global->prep_code(prep)) || ! (iobjc = global->vs_code(iobj))) return E_INVARG; verb = ({ nowner, perms | SDOBJ(dobjc) | SIOBJ(iobjc), names, prepc, 0 /* object */ }); verbs += ({ verb }); cache::reset(); if ((ob = MOOOBJ(nowner)) && ob != this_object()) ob->set_owner_flag(); return E_NONE; } /* * NAME: del_verb() * DESCRIPTION: remove a verb object */ private void del_verb(mixed *verb) { object vobj; if (vobj = verb[VERB_OBJECT]) vobj->del(); } /* * NAME: delete_verb() * DESCRIPTION: remove a verb */ int delete_verb(string name, mixed *info) { mixed *verb; if (! (verb = find_spec_verb(name, VSPEC_ANY))) return E_VERBNF; if (PROGRAMMER(info) != b_owner && ! (b_flags & F_WRITE) && ! WIZARDP(info)) return E_PERM; del_verb(verb); verbs -= ({ verb }); cache::reset(); return E_NONE; } /* * NAME: set_verb_code() * DESCRIPTION: receive and compile new code for a verb */ MOOVAL set_verb_code(string name, string *code, mixed *info) { mixed *verb, *ast; if (! (verb = find_spec_verb(name, VSPEC_ANY))) return STW(E_VERBNF); if (PROGRAMMER(info) != verb[VERB_OWNER] && ! WIZARDP(info) && ! (verb[VERB_PERMS] & P_WRITE)) return STW(E_PERM); ast = parser->main(implode(code, "\n")); if (! ast[0]) return STRLIST2MOO(ast[1]); del_verb(verb); (verb[VERB_OBJECT] = global->compile_verb(ast[1], 1))->ref(); return LST(LNEW()); } /* * NAME: get_verb_code() * DESCRIPTION: return (pretty-printed) MOO code for a verb */ MOOVAL get_verb_code(string name, int full_paren, int indent, mixed *info) { mixed *verb; object vobj; if (! (verb = find_spec_verb(name, VSPEC_ANY))) return STW(E_VERBNF); if (! (verb[VERB_PERMS] & P_READ) && PROGRAMMER(info) != verb[VERB_OWNER] && ! WIZARDP(info)) return STW(E_PERM); if (! (vobj = verb[VERB_OBJECT])) return LST(LNEW()); return STRLIST2MOO(vobj->get_source(full_paren, indent)); } /* * NAME: get_disassembled_code() * DESCRIPTION: (don't) return disassembled code for a verb */ MOOVAL get_disassembled_code(string name, mixed *info) { mixed *verb; if (! (verb = find_spec_verb(name, VSPEC_ANY))) return STW(E_VERBNF); if (! (verb[VERB_PERMS] & P_READ) && PROGRAMMER(info) != verb[VERB_OWNER] && ! WIZARDP(info)) return STW(E_PERM); return LST( ({ STR("Sorry, disassemble() not supported in LPMOO.") }) ); } /* * NAME: get_verb_obj() * DESCRIPTION: return the object for a verb, compiling if necessary */ object get_verb_obj(string name) { mixed *verb; if (! (verb = find_spec_verb(name, VSPEC_ANY)) || UNPROGRAMMED(verb)) return 0; return verb[VERB_OBJECT]; } /* * NAME: command() * DESCRIPTION: call a verb as a command */ int command(string name, string realname, mixed *parsed, object player, object this) { mixed *verb, *info; int task_id, player_id, this_id, dobj, iobj, flags, owner; object vobj, host; if (! this) this = this_object(); this_id = OBJNUM(this); if (parsed[C_DOBJ] == MATCH_NOTHING) dobj = VS_NONE; else if (parsed[C_DOBJ] == this_id) dobj = VS_THIS; else dobj = VS_ANY; if (parsed[C_IOBJ] == MATCH_NOTHING) iobj = VS_NONE; else if (parsed[C_IOBJ] == this_id) iobj = VS_THIS; else iobj = VS_ANY; if (! (verb = find_command(name, VSPEC(parsed[C_PREP], dobj, iobj)))) return 0; host = verb[0]; if (UNPROGRAMMED(verb = verb[1])) return parent ? parent->command(name, realname, parsed, player, this) : 0; task_id = global->take_task(); player_id = OBJNUM(player); owner = verb[VERB_OWNER]; info = ({ ((verb[VERB_PERMS] & P_DEBUG) ? IF_DEBUG : 0) | (wizardp(owner) ? IF_WIZARD : 0), owner, ({ OBJ(player_id), /* player */ OBJ(this_id), /* this */ OBJ(player_id), /* caller */ STRLIST2MOO(parsed[C_ARGS]), /* args */ STR(parsed[C_ARGSTR]), /* argstr */ STR(realname), /* verb */ OBJ(parsed[C_DOBJ]), /* dobj */ STR(parsed[C_DOBJSTR]), /* dobjstr */ STR(parsed[C_PREPSTR]), /* prepstr */ OBJ(parsed[C_IOBJ]), /* iobj */ STR(parsed[C_IOBJSTR]), /* iobjstr */ STD_VARS }), 0, realname, this_id, player_id, task_id, 0, vobj = verb[VERB_OBJECT], host, global->get_max_depth(), 0 }); vobj->main(JS_INIT_I(info)); return 1; } /* * NAME: call_verb() * DESCRIPTION: call a verb in this object */ MOOVAL call_verb(JS_PROTO, string name, MOOVAL *vars) { mixed *verb; int task_id, depth, owner, flags; object vobj, host; JS_BEGIN; if (! (verb = find_verb(name))) return STW(E_VERBNF); host = verb[0]; if (UNPROGRAMMED(verb = verb[1])) return STW(E_VERBNF); if (info) { if (! (depth = info[I_DEPTH] - 1)) return STW(E_MAXREC); task_id = info[I_TASKID]; flags = info[I_FLAGS] & IF_FROMLPC; info[I_LINENO] = info[I_VERBOBJ]->get_lineno(); } else { depth = global->get_max_depth(); task_id = global->take_task(); } owner = verb[VERB_OWNER]; info = ({ flags | ((verb[VERB_PERMS] & P_DEBUG) ? IF_DEBUG : 0) | (wizardp(owner) ? IF_WIZARD : 0), /* flags */ owner, /* task perms */ vars, /* (initial) vars */ 0, /* vardefs */ name, /* verb name */ OBJVAL(vars[V_THIS]), /* initial `this' */ OBJVAL(vars[V_PLAYER]), /* initial `player' */ task_id, /* task id */ 0, /* line number */ vobj = verb[VERB_OBJECT], /* verb object */ host, /* host object */ depth, /* recursion depth */ info, /* previous info */ }); JS_PREP(1); RET = vobj->main(JS_DATA(1)); JS_END; return RET; JS_END; } /* * NAME: lose_content() * DESCRIPTION: remove an object from contents list */ void lose_content(object ob) { b_contents -= ({ ob }); } /* * NAME: gain_content() * DESCRIPTION: add an object to contents list */ void gain_content(object ob) { b_contents += ({ ob }); } /* * NAME: do_move() * DESCRIPTION: atomically transfer this object to another location (locked) */ static object do_move(object where) { object oldwhere; if (oldwhere = b_location) oldwhere->lose_content(this_object()); if (b_location = where) where->gain_content(this_object()); return oldwhere; } /* * NAME: move() * DESCRIPTION: go somewhere */ int move(JS_PROTO, object where) { object obj; JS_BEGIN; if (PROGRAMMER(info) != b_owner && ! WIZARDP(info)) return E_PERM; if (where) { JS_PREP(1); RET = where->call_verb(JS_DATA(1), "accept", verb_vars(info, where, "accept", OBJ_OBJNUM(this_object()))); JS_END; if (STWP(RET) && STWVAL(RET) == E_MAXREC) return E_MAXREC; if (! WIZARDP(info) && ! TRUTHOF(RET)) return E_NACC; } if (where == b_location) return E_NONE; for (obj = where; obj; obj = obj->get_location()) if (this_object() == obj) return E_RECMOVE; if (obj = with_lock("do_move", where)) { JS_PREP(2); RET = obj->call_verb(JS_DATA(2), "exitfunc", verb_vars(info, obj, "exitfunc", OBJ_OBJNUM(this_object()))); JS_END; if (STWP(RET) && STWVAL(RET) == E_MAXREC) return E_MAXREC; } if (where && b_location == where) { JS_PREP(3); RET = where->call_verb(JS_DATA(3), "enterfunc", verb_vars(info, where, "enterfunc", OBJ_OBJNUM(this_object()))); JS_END; if (STWP(RET) && STWVAL(RET) == E_MAXREC) return E_MAXREC; } return E_NONE; JS_END; } /* * NAME: renumber_owner() * DESCRIPTION: change the owner of this object or verbs or properties */ void renumber_owner(int old, int new) { mixed *props; int i; if (b_owner == old) b_owner = new; for (i = sizeof(verbs); i--; ) if (verbs[i][VERB_OWNER] == old) verbs[i][VERB_OWNER] = new; props = map_values(properties); for (i = sizeof(props); i--; ) if (props[i][PROP_OWNER] == old) props[i][PROP_OWNER] = new; } /* * NAME: recycle() * DESCRIPTION: make this object irrevocably go away */ int recycle(JS_PROTO) { int i; object owner_ob; JS_BEGIN; if (PROGRAMMER(info) != b_owner && ! WIZARDP(info)) return E_PERM; if (b_flags & F_RECYCLING) /* already recycling */ return E_NONE; b_flags |= F_RECYCLING; JS_PREP(1); RET = call_verb(JS_DATA(1), "recycle", verb_vars(info, this_object(), "recycle")); JS_END; while (sizeof(children)) if (children[0]->chparent(parent, 0) != E_NONE) error("Error during reparent of child"); if (chparent(0, 0) != E_NONE) error("Error during reparent"); while (sizeof(b_contents)) { JS_PREP(2); RET = b_contents[0]->move(JS_DATA(2), 0); JS_END; if (RET != E_NONE) error("Error during move of content"); } JS_PREP(3); RET = move(JS_DATA(3), 0); JS_END; if (RET != E_NONE) error("Error during move"); for (i = sizeof(verbs); i--; del_verb(verbs[i])); if (owner_ob = MOOOBJ(b_owner)) owner_ob->modify_quota(1); global->recycle(this_object()); destruct_object(this_object()); return E_NONE; JS_END; }