System object
The system object has access to administrative functions and
receives messages from the server.
Informational public methods:
doing_poll() Get doing poll
users() Get a list of all users
connected_users() Get a list of connected users
new_user_class() Get the new user class
find_user(name) Search for a user by name
admins() Get a list of admins
is_admin(obj) True if obj is an admin
remote_verb_templates() Get remote verb templates
backup_interval() The backup interval, in seconds
followers(protocol) Objects which follow a protocol
agents(protocol) Agents of a protocol
Public methods to perform actions:
new_remote_template(s) Reserve a new remote verb template
removed_remote_template(s) Unreserve remote verb template
register_new_user_name() Registers name of new user
user_changed_name(old_name) Indicates user changed name
user_has_disconnected() Indicates user disconnected
Methods called from the root object:
change_sender_parents(parents) Change parents of sender
spawn_sender(num) Create a child of sender numbered num
destroy_sender() Destroy the sender
Server methods:
startup(args) Received from server
Admin methods:
set_doing_poll(s) Set doing poll
set_new_user_class(obj) Set the new user class
create_user(name, password) Create a new user (also for logins)
add_admin(obj) Add an admin
binary_dump() Do a binary dump
text_dump() Do a text dump
shutdown() Shut down the server
set_heartbeat_freq() Set the heartbeat frequency, in seconds
set_backup_interval() Set the backup interval, in seconds
Login methods:
login_starting() Indicates login is starting
login_gone() Indicates login is going away
login(obj) Indicates login successfully occurred
Private methods:
new_connection() Make a new connection object
parent root
object sys
var sys connected_users []
var sys users [$user, $builder, $programmer]
var sys user_names #[]
var sys admins [$sys, $programmer1]
var sys new_user_class $programmer
var sys starting_room $nowhere
var sys exit_starting_room $void
var sys doing_poll "Doing"
var sys remote_templates #[]
var sys server_port 0
var sys current_receiver 0
var sys task_queue []
var sys backup_interval 3600
var sys last_backup 0
var sys followers #[]
var sys agents #[]
eval
.initialize();
.set_name("System object");
.
method startup
arg args;
var ind, str, obj;
catch any {
if (sender() != 0)
throw(~perm, "Sender is not the server.");
// Get rid of any lingering connection objects.
for obj in ($connection.children())
obj.destroy();
// Look for a port specification.
ind = "-p" in sublist(args, 1, listlen(args) - 1);
server_port = ind ? toint(args[ind + 1]) | 6666;
// Bind to the port, or something close to it.
catch ~socket, ~bind {
bind(server_port, $sys);
} with handler {
log("Can't bind to server port.");
shutdown();
}
// Initialize variables and log startup message.
connected_users = [];
.new_connection();
log("Server starting on port " + tostr(server_port) + ".");
// Set up five-second heartbeat.
set_heartbeat_freq(5);
} with handler {
for str in (traceback())
log("STARTUP: " + str);
}
.
method new_connection
if (sender() != this() || caller() != definer())
throw(~perm, "Invalid call to private method.");
current_receiver = $connection.spawn("(A login object)");
bind(server_port, current_receiver);
.
method doing_poll
return doing_poll;
.
method set_doing_poll
arg s;
if (!(sender() in admins))
throw(~perm, "Sender not an admin.");
if (type(s) != 'string)
throw(~type, "Argument not a string.");
doing_poll = s;
.
method users
return users;
.
method connected_users
return connected_users;
.
method new_user_class
return new_user_class;
.
method set_new_user_class
arg obj;
if (!(sender() in admins))
throw(~perm, "Sender not an admin.");
if (type(obj) != 'dbref)
throw(~type, "First argument (" + toliteral(obj) + ") not a dbref.");
new_user_class = obj;
.
method find_user
arg name;
catch ~keynf {
return user_names[name];
} with handler {
throw(~usernf, "User not found.");
}
.
method create_user
arg name, password;
var user;
if (!(caller().is_agent('connection) || sender() in admins))
throw(~perm, "Sender is not a connection object or admin.");
user = new_user_class.spawn(name);
user.set_password(password);
user.del_owner(this());
user.move($nowhere);
users = setadd(users, user);
return user;
.
method connection_starting
if (!caller().is_agent('connection))
throw(~perm, "Caller is not an agent of connection protocol.");
.new_connection();
.
method user_logged_in
if (!caller().is_agent('user))
throw(~perm, "Sender is not an agent of user protocol.");
connected_users = setadd(connected_users, sender());
.
method user_logged_out
if (!caller().is_agent('user))
throw(~perm, "Caller is not an agent of user protocol.");
connected_users = setremove(connected_users, sender());
.
method admins
return admins;
.
method is_admin
arg obj;
return obj in admins ? 1 | 0;
.
method add_admin
arg user;
if (!$sys.is_admin(sender()))
throw(~perm, "Sender is not an admin.");
admins = [@admins, user];
.
method binary_dump
if (!$sys.is_admin(sender()))
throw(~perm, "Sender is not an admin.");
return binary_dump();
.
method text_dump
if (!$sys.is_admin(sender()))
throw(~perm, "Sender is not an admin.");
return text_dump();
.
method shutdown
if (!$sys.is_admin(sender()))
throw(~perm, "Sender is not an admin.");
return shutdown();
.
method change_sender_parents
arg parents;
var p;
if (!caller().is_agent('hierarchy))
throw(~perm, "Caller not the root object.");
// Check if objects want to be parents.
for p in (parents) {
if (!p.ok_to_inherit(sender()))
throw(~perm, toliteral(p) + " refused to be a parent.");
}
(> chparents(sender(), parents) <);
.
method change_sender_parents_back
arg parents;
// If an init after a chparents fails, we want to be able to go back to the
// old parents without checking if they want to be parents.
if (!caller().is_agent('hierarchy))
throw(~perm, "Caller not the root object.");
(> chparents(sender(), parents) <);
.
method spawn_sender
arg num, owner, name;
var last, dbref;
if (!caller().is_agent('hierarchy))
throw(~perm, "Caller not not an agent of hierarchy prototocol.");
if (type(num) != 'integer)
throw(~type, "First argument is not an integer.");
dbref = tostr(sender());
last = substr(dbref, strlen(dbref));
if (last >= "0" && last <= "9")
dbref = dbref + "_";
dbref = dbref + tostr(num);
return .create_object(todbref(dbref), [sender()], name, owner);
.
method create_object
arg dbref, parents, name, owner;
var new;
if (!(sender() in admins))
throw(~perm, "Sender not an admin");
new = create(dbref, parents);
catch any {
new.initialize();
new.set_name(name);
new.del_owner(this());
new.add_owner(owner);
} with handler {
// Failed to initialize the child; destroy it.
(| new.uninit() |);
destroy(new);
rethrow(error());
}
return new;
.
method destroy_sender
if (!caller().is_agent('hierarchy))
throw(~perm, "Caller not an agent of hierarchy protocol");
destroy(sender());
.
method remote_verb_templates
return dict_keys(remote_templates);
.
method new_remote_template
arg template;
var objects;
if (!caller().is_agent('verb))
throw(~perm, "Caller not an agent of verb protocol");
if (type(template) != 'string)
throw(~type, "Template not a string");
if (dict_contains(remote_templates, template))
objects = remote_templates[template];
else
objects = [];
objects = setadd(objects, sender());
remote_templates = dict_add(remote_templates, template, objects);
.
method removed_remote_template
arg template;
var objects;
if (!caller().is_agent('verb))
throw(~perm, "Caller not an agent of verb protocol");
if (!dict_contains(remote_templates, template))
return;
objects = remote_templates[template];
objects = setremove(ojects, sender());
if (objects)
remote_templates = dict_add(remote_templates, template, objects);
else
remote_templates = dict_del(remote_templates, template);
.
method register_new_user_name
if (!caller().is_agent('user_names))
throw(~perm, "Caller not an agent of user names protocol.");
user_names = dict_add(user_names, sender().name(), sender());
.
method user_changed_name
arg old_name;
if ((| user_names[old_name] |) != sender())
throw(~perm, "Old name doesn't belong to sender.");
user_names = dict_del(user_names, old_name);
user_names = dict_add(user_names, sender().name(), sender());
.
method log
arg str;
log(str);
.
method log_traceback
arg traceback;
var s;
for s in (traceback)
log(s);
.
method connect
arg [args];
if (!(caller() in admins))
throw(~perm, "Caller not an admin");
return (> connect(@args) <);
.
method heartbeat
var task;
if (sender() != 0)
throw(~perm, "Sender not the server");
if (time() / backup_interval > last_backup / backup_interval)
.do_backup();
while (listlen(task_queue) > 0 && time() > task_queue[1][1]) {
task = task_queue[1];
(| task[2].(task[3])(@task[4]) |);
.remove_first_task();
}
.
method do_backup
if (sender() != this() || caller() != definer())
throw(~perm, "Invalid call to private method");
text_dump();
last_backup = time();
.
method schedule_task
arg time, method, args;
var task, i;
if (type(time) != 'integer || type(method) != 'symbol || type(args) != 'list)
throw(~type, "Arguments are not an integer, symbol, and list.");
task = [time, sender(), method, args];
task_queue = task_queue + [task];
i = listlen(task_queue);
while (i > 1 && task[1] < task_queue[i / 2][1]) {
task_queue = replace(task_queue, i, task_queue[i / 2]);
i = i / 2;
}
task_queue = replace(task_queue, i, task);
.
method remove_first_task
var len, i, min;
if (sender() != this() || caller() != definer())
throw(~perm, "Invalid call to private method");
len = listlen(task_queue);
i = 1;
while (i != len) {
min = len;
if (i * 2 < len && task_queue[i * 2][1] < task_queue[min][1])
max = i * 2;
if (i * 2 + 1 < len && task_queue[i * 2 + 1][1] < task_queue[min][1])
min = i * 2 + 1;
task_queue = replace(task_queue, i, task[min]);
i = min;
}
task_queue = sublist(task_queue, 1, len - 1);
.
method ps
var output, task, line;
if (!(sender() in admins))
throw(~perm, "Sender not an admin.");
output = [pad("Seconds", 20) + pad("Object", 20) + "Method"];
output = output + [pad("-------", 20) + pad("------", 20) + "------"];
for task in (task_queue) {
line = pad(tostr(task[1] - time()), 18) + " ";
line = line + pad(toliteral(task[2]), 18) + " ";
line = line + tostr(task[3]);
output = output + [line];
}
return output;
.
method backup_interval
return backup_interval;
.
method set_backup_interval
arg val;
if (!(sender() in admins))
throw(~perm, "Sender not an admin");
backup_interval = val;
.
method followers
arg protocol;
return dict_contains(followers, protocol) ? followers[protocol] | [];
.
method agents
arg protocol;
return dict_contains(agents, protocol) ? agents[protocol] | [];
.
method trust
arg obj, protocol, type;
var l;
if (!(sender() in admins))
throw(~perm, "Sender not an admin");
if (type(obj) != 'dbref || type(protocol) != 'symbol)
throw(~type, "Object and protocol are not a dbref and a symbol.");
if (type == 'follow) {
l = setadd(.followers(protocol), obj);
followers = dict_add(followers, protocol, l);
} else if (type == 'agent) {
l = setadd(.agents(protocol), obj);
agents = dict_add(agents, protocol, l);
} else {
throw(~type, "Type is not 'agent or 'follow.");
}
.
method untrust
arg obj, protocol;
if (!(sender() in admins))
throw(~perm, "Sender not an admin");
if (obj in .followers(protocol)) {
l = setremove(followers[protocol], obj);
if (l)
followers = dict_add(followers, protocol, l);
else
followers = dict_del(followers, protocol);
}
if (obj in .agents(protocol)) {
l = setremove(agents[protocol], obj);
if (l)
agents = dict_add(agents, protocol, l);
else
agents = dict_del(agents, protocol);
}
.
method sender_data
var output, i;
if (!caller().is_agent('hierarchy))
throw(~perm, "Caller not an agent of hierarchy protocol");
return data(sender());
.
method starting_room
return starting_room;
.
method set_starting_room
arg obj;
if (!(sender() in admins))
throw(~perm, "Sender not an admin");
if (!obj.follows('starting_room))
throw(~protocol, "Sender doesn't follow starting room protocol");
starting_room = obj;
.
method exit_starting_room
return exit_starting_room;
.
method set_exit_starting_room
arg obj;
if (!(sender() in admins))
throw(~perm, "Sender not an admin");
if (!obj.follows('exit_starting_room))
throw(~protocol, "Sender doesn't follow starting room protocol");
starting_room = obj;
.
eval
.trust($user, 'user, 'agent);
.trust($root, 'hierarchy, 'agent);
.trust($sys, 'hierarchy, 'agent);
.trust($verbs, 'verb, 'agent);
.trust($container, 'container, 'follow);
.trust($room, 'room, 'follow);
.trust($located, 'located, 'follow);
.trust($located, 'movement, 'agent);
.trust($user, 'user_names, 'agent);
.trust($nowhere, 'starting_room, 'follow);
.trust($void, 'exit_starting_room, 'follow);
.trust($exit, 'exit, 'agent);
.trust($connection, 'connection, 'agent);
.