/*
* 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;
}