The verb cache This object acts as a delegate for $root, $located, $user, and $has_verbs. It keeps track of the verb templates provided by all objects in each live container in the database, where a "live container" is a connected user or a room containing at least one connected user. This object also keeps track of remote verb templates, a fairly easy job. parent root object verb_cache var root name 'verb_cache var verb_cache remote_verb_templates #[] var verb_cache container_verbs #[] var verb_cache container_objects #[] var verb_cache object_containers #[] var verb_cache room_users #[] var verb_cache users [] eval .initialize(); . method object_defined_remote_verb arg verb; var obj; if (caller() != $has_verbs) throw(~perm, "Caller is not $has_verbs."); obj = sender(); // Add verb to our remote_verb_templates dictionary. remote_verb_templates = dict_add_elem(remote_verb_templates, verb, obj); . method object_undefined_global_verb arg verb; var obj; if (caller() != $has_verbs) throw(~perm, "Caller is not $has_verbs."); obj = sender(); // Remove verb from our remote_verb_templates dictionary. remote_verb_templates = dict_del_elem(remote_verb_templates, verb, obj); . method remote_verb_templates return dict_keys(remote_verb_templates); . method object_defined_verb arg verb; var obj, container, verbs; if (caller() != $has_verbs) throw(~perm, "Caller is not $has_verbs."); obj = sender(); // If neither the object nor its descendents are in anything's environment, // don't do anything. if (!dict_contains(object_containers, obj)) return; // Update container_verbs entry for each container the object (or a // descendent) is in. The second line of this loop is a kludge to permit // the third line to mutate rather than copy container_verbs[container]. for container in (dict_keys(object_containers[obj])) { verbs = container_verbs[container]; container_verbs = dict_add(container_verbs, container, 0); verbs = dict_add_elem(verbs, verb, obj); container_verbs = dict_add(container_verbs, container, verbs); } . method object_undefined_verb arg verb; var obj, container, verbs; if (caller() != $has_verbs) throw(~perm, "Caller is not $has_verbs."); obj = sender(); // If neither the object nor its descendents are in anything's environment, // don't do anything. if (!dict_contains(object_containers, obj)) return; // Update container_verbs entry for each container the object (or a // descendent) is in. The second line of this loop is a kludge to permit // the third line to mutate rather than copy container_verbs[container]. for container in (dict_keys(object_containers[obj])) { verbs = container_verbs[container]; container_verbs = dict_add(container_verbs, container, 0); verbs = dict_del_elem(verbs, verb, obj); container_verbs = dict_add(container_verbs, container, verbs); } . method object_entered_container arg container; var obj; if (caller() != $located && caller() != $root) throw(~perm, "Caller is not $located or $root."); obj = sender(); // If the container isn't in anything's environment, don't do anything. if (!dict_contains(container_objects[container])) return; // Add obj to our records for container. .add_object_to_container(container, obj); . method object_left_container arg container; var obj; if (caller() != $located && caller() != $root) throw(~perm, "Caller is not $located or $root."); obj = sender(); // If the container isn't in anything's environment, don't do anything. if (!dict_contains(container_objects[container])) return; .remove_object_from_container(container, obj); . method user_entered_room arg room; var user; if (caller() != $user) throw(~perm, "Caller is not $user."); user = sender(); // Update room_users[room]. room_users = dict_add_elem(room_users, room, user); . method user_left_room arg room; var user; if (caller() != $user) throw(~perm, "Caller is not $user."); user = sender(); // Update room_users[room]. room_users = dict_del_elem(room_users, room, user); // If there aren't any users in the room, stop keeping track of that room // as a container. if (!dict_contains(room_users, room)) .forget_container(room); . method user_connected var user; if (caller() != $user) throw(~perm, "Caller is not $user."); user = sender(); // Add the user to the user list, thus allowing things to ask for its verb // list. users = setadd(users, user); . method user_disconnected var user; if (caller() != $user) throw(~perm, "Caller is not $user."); user = sender(); // Remove the user from the users list. users = setremove(users, user); // Forget about the user as a container. .forget_container(user); . method container_verbs arg container; if (!(container in users) && !dict_contains(room_users, container)) throw(~perm, "Can't ask for verbs for an inactive container."); // If we're not keeping track of container, start doing so. .keep_track_of_container(container); // Return the verbs for container. return dict_keys(container_verbs[container]); . method gc var user; if (!.is_owned_by(sender())) throw(~perm, "Sender is not an owner."); // Wipe all records of containers. container_verbs = #[]; container_objects = #[]; object_containers = #[]; // Get users list from $user_db. users = $user_db.connected_users(); // Rebuild room_users. room_users = #[]; for user in (users) room_users = dict_add_elem(room_users, user.location(), user); . method keep_track_of_container arg container; var obj; if (caller() != definer()) throw(~perm, "Invalid access to private method."); // Perform no action of we were already keeping track of this container. if (dict_contains(container_objects, room)) return; // Create blank entries for container in container_objects and // container_verbs. container_objects = dict_add(container_objects, container, #[]); container_verbs = dict_add(container_verbs, container, #[]); // For each object in the container, and the container itself, add the // object to our records for the container. for obj in ([container, @contiainer.contents()]) .add_object_to_container(container, obj); . method forget_container arg container; var obj, containers; if (caller() != definer()) throw(~perm, "Invalid access to private method."); // Perform no action if we weren't keeping track of this room. if (!dict_contains(container_objects, room)) return; // Remove container from object_containers[obj] for each object in // container_objects[container]. The dict_add(..., ..., 0) is a kludge to // free the reference count on the dictionary's copy of containers. If // object_containers[obj] becomes empty after removing the association for // container, delete the association for obj from object_containers. for obj in (dict_keys(container_objects[room])) { containers = object_containers[obj]; object_containers = dict_add(object_containers, obj, 0); containers = dict_del_elem(object_containers, container, obj); if (containers) object_containers = dict_add(object_containers, obj, containers); else object_containers = dict_del(object_containers, obj); } // Remove the associations for container from container_objects and // container_verbs. container_objects = dict_del(container_objects, container); container_verbs = dict_del(container_verbs, container); . method add_object_to_container arg container, obj; var objects, ancestor, templates, verbs, verb, containers; if (caller() != definer()) throw(~perm, "Invalid access to private method."); // Update container_objects[container] to include each of obj's ancestors. // This will add obj to the container_objects[container][ancestor] // association for each ancestor. If this creates a new association, add // the verbs for that ancestor to container_verbs[container]. The // dict_add(..., ..., 0) is a kludge to free the reference count from the // dictionary. Also update object_containers[ancestor] for each ancestor. objects = container_objects[container]; container_objects = dict_add(container_objects, container, 0); for ancestor in (obj.ancestors()) { if (!dict_contains(objects, ancestor)) { // Add the verbs for this ancestor to container_verbs[container]. // Again, a kluge to free the reference count from the dictionary // before modifying the entry. templates = (| ancestor.verb_templates() |) || []; for verb in (templates) { verbs = container_verbs[container]; container_verbs = dict_add(container_verbs, container, 0); verbs = dict_add_elem(verbs, verb, ancestor); container_verbs = dict_add(container_verbs, container, verbs); } } // Update container_objects[container]. objects = dict_add_elem(objects, ancestor, obj); // Update object_containers[ancestor]. Usual kluge. object_containers // may have no association for ancestor yet, so be prepared to make // one. containers = (| object_containers[ancestor] |) || #[]; object_containers = dict_add(object_containers, ancestor, 0); containers = dict_add_elem(containers, container, obj); object_containers = dict_add(object_containers, ancestor, containers); } container_ojects = dict_add(container_objects, container, objects); . method remove_object_from_container arg container, obj; var objects, ancestor, verbs, verb; if (caller() != definer()) throw(~perm, "Invalid access to private method."); // Update container_objects[container] to include each of obj's ancestors. // This will add obj to the container_objects[container][ancestor] // association for each ancestor. If this creates a new association, add // the verbs for that ancestor to container_verbs[container]. The // dict_add(..., ..., 0) is a kludge to free the reference count from the // dictionary. objects = container_objects[container]; container_objects = dict_add(container_objects, container, 0); for ancestor in (obj.ancestors()) { if (!dict_contains(objects, ancestor)) { // Add the verbs for this ancestor to container_verbs[container]. // Again, a kluge to free the reference count from the dictionary // before modifying the entry. for verb in (ancestor.verb_templates()) { verbs = container_verbs[container]; container_verbs = dict_add(container_verbs, container, 0); verbs = dict_del_elem(verbs, verb, ancestor); container_verbs = dict_add(container_verbs, container, verbs); } } // Update container_objects[container]. objects = dict_del_elem(objects, ancestor, obj); // Update object_containers[ancestor]. Usual kluge. If // object_containers[ancestor] becomes empty, remove the association // from object_containers. containers = object_containers[ancestor]; object_containers = dict_add(object_containers, ancestor, 0); containers = dict_del_elem(containers, container, obj); if (containers) object_containers = dict_add(object_containers, ancestor, containers); else object_containers = dict_del(object_containers, ancestor); } container_ojects = dict_add(container_objects, container, objects); .