From: Jeremy Stanley Date: Sun, 11 Sep 2005 20:45:34 +0000 (+0000) Subject: Imported from archive. X-Git-Tag: 0.0.1~334 X-Git-Url: https://mudpy.org/gitweb?a=commitdiff_plain;h=37f01be8b012457eb739658f1e1febf51b1b2334;p=mudpy.git Imported from archive. * command (command:create, command:delete, command:destroy) (command:set), mudpy.py (command_create, command_delete) (command_destroy, command_set): Added the admin commands create, delete, destroy and set, as a foundation for future on-line creation/World-building systems. * example: Included sample data from the Example project. * mudpy.py (Element.delete): Renamed to destroy and added a new delete method used for removing individual facets, making them consistent with the new admin commands. (User.delete): Renamed to destroy for consistency with the Element class' method names. (User.can_run): New method acting as a rudimentary access control system for commands flagged with an administrative bool facet in command elements. (command_help): Modified to filter out commands the user is disallowed from running, and now highlights administrative command names when they're available. (handler_active): Standardized on command_name instead of command. --- diff --git a/command b/command index a9ec05e..bf12bed 100644 --- a/command +++ b/command @@ -1,33 +1,60 @@ [control] read_only = yes -[command:quit] -action = command_quit(user, command, parameters) -description = Leave Example. -help = This will save your account and disconnect your client connection. +[command:create] +action = command_create(user, parameters) +administrative = yes +description = Create a new element in the universe. +help = Ways to create an element:$(eol)$(eol) create actor:fred$(eol) create other:garply foo/bar/baz + +[command:delete] +action = command_delete(user, parameters) +administrative = yes +description = Delete an existing facet from an element. +help = You can delete any facet of an element as follows:$(eol)$(eol) delete location:boardroom terrain + +[command:destroy] +action = command_destroy(user, parameters) +administrative = yes +description = Destroy an existing element in the universe. +help = You can destroy any element in the universe as follows:$(eol)$(eol) destroy prop:dagger [command:halt] -action = command_halt(user, command, parameters) +action = command_halt(user, parameters) +administrative = yes description = Shut down the world. help = This will save all active accounts, disconnect all clients and stop the entire program. [command:help] -action = command_help(user, command, parameters) +action = command_help(user, parameters) description = List commands or get help on one. help = This will list all comand words available to you along with a brief description or, alternatively, give you detailed information on one command. -[command:show] -action = command_show(user, command, parameters) -description = Show program data. -help = For now, this is used to show things like "avatars", "time" and "universe". +[command:quit] +action = user.state = "main_utility" +description = Leave Example. +help = This will save your account and disconnect your client connection. [command:reload] -action = command_reload(user, command, parameters) +action = command_reload(user) +administrative = yes description = Reload code modules and data. help = This will reload all python code modules, reload configuration files and re-read data files. [command:say] -action = command_say(user, command, parameters) +action = command_say(user, parameters) description = State something out loud. help = This allows you to speak to other characters within the same room. If you end your sentence with specific punctuation, the aparent speech action (ask, exclaim, et cetera) will be adapted accordingly. It will also add punctuation and capitalize your message where needed. +[command:set] +action = command_set(user, parameters) +administrative = yes +description = Set a facet of an element. +help = Invoke it like this:$(eol)$(eol) set actor:dominique description You see nothing special. + +[command:show] +action = command_show(user, parameters) +administrative = yes +description = Show element data. +help = Here are the possible incantations:$(eol)$(eol) show categories$(eol) show category actor$(eol) show element location:1:2:3:4$(eol) show files$(eol) show time + diff --git a/example b/example new file mode 100644 index 0000000..a086a5f --- /dev/null +++ b/example @@ -0,0 +1,559 @@ +[location:101] +pickproof_n = yes +name = East Sixth Avenue +closeable_n = yes +description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. Sixth Avenue runs East and West here while a merchant's booth lies to the North. +terrain = city +link_e = location:102 +keywords_n = ['booth'] +description_n = This is a simple merchant's booth. +link_w = location:161 +link_n = location:159 + +[location:106] +keywords_e = ['shop'] +description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. Fifth Street runs North and South here while a storefront beckons to the East. +closeable_e = yes +pickproof_e = yes +terrain = city +link_e = location:152 +name = North Fifth Street +description_e = This is the entrance to a shop. +link_s = location:105 +link_n = location:107 + +[location:118] +pickproof_n = yes +name = East Fourth Avenue +closeable_n = yes +description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. Fourth Avenue runs East and West here while a merchant's booth lies to the North. +terrain = city +link_e = location:119 +keywords_n = ['booth'] +description_n = This is a simple merchant's booth. +link_w = location:116 +link_n = location:147 + +[location:159] +link_s = location:101 +terrain = inside +name = Tanner's Booth +description = The smell of leather goods fills your nostrils. Tanned, finished and embroidered leather is hung and piled everywhere. + +[location:158] +name = Cobbler's Booth +terrain = inside +closeable_s = yes +keywords_s = ['booth'] +link_s = location:129 +pickproof_s = yes +description = Shoes, shoes and more shoes... You need footwear, we have footwear. Look around, see what you like. Best prices in town! So you gonna buy something, stranger? + +[location:104] +keywords_e = ['gate'] +name = East Market Gate +closeable_e = yes +pickproof_e = yes +terrain = city +link_e = location:0 +description_e = This gate leads from the market to the city proper. +link_w = location:103 +description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. To the West, Sixth Avenue heads into the market while the rest of the city lies trough the gate to the East. + +[location:151] +terrain = inside +name = New BuildWalk Room +link_e = location:105 +description = This unfinished room was created by Imp. + +[location:150] +terrain = inside +link_w = location:122 +name = New BuildWalk Room +description = This unfinished room was created by Imp. + +[location:153] +link_n = location:108 +terrain = inside +description = This unfinished room was created by Imp. +name = New BuildWalk Room + +[location:152] +terrain = inside +link_w = location:106 +name = New BuildWalk Room +description = This unfinished room was created by Imp. + +[location:155] +terrain = inside +link_w = location:114 +name = New BuildWalk Room +description = This unfinished room was created by Imp. + +[location:154] +link_s = location:109 +terrain = inside +name = New BuildWalk Room +description = This unfinished room was created by Imp. + +[location:157] +link_n = location:130 +terrain = inside +description = This booth sells cloth garments of every description. Fine silks and linens line the walls, draped from every protrusion and stacked on every surface. +name = Tailor's Booth + +[location:156] +terrain = inside +name = New BuildWalk Room +link_e = location:115 +description = This unfinished room was created by Imp. + +[location:115] +link_n = location:114 +terrain = city +name = South Seventh Street +keywords_w = ['booth'] +closeable_w = yes +description_w = This is a simple merchant's booth. +link_s = location:116 +pickproof_w = yes +link_w = location:156 +description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. Seventh Street runs North and South here while a merchant's booth lies to the West. + +[location:114] +keywords_e = ['booth'] +description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. Seventh Street runs North and South here while a merchant's booth lies to the East. +closeable_e = yes +pickproof_e = yes +terrain = city +link_e = location:155 +name = South Seventh Street +description_e = This is a simple merchant's booth. +link_s = location:115 +link_n = location:161 + +[location:117] +description_s = This gate leads from the market to the city proper. +description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. To the North, Seventh Street heads into the market while the rest of the city lies trough the gate to the South. +terrain = city +name = South Market Gate +closeable_s = yes +keywords_s = ['gate'] +link_s = location:0 +pickproof_s = yes +link_n = location:116 + +[location:116] +link_n = location:115 +terrain = city +link_e = location:118 +name = South Seventh and Fourth +link_s = location:117 +link_w = location:123 +description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. To the East and West lies Fourth Avenue while Seventh Street runs North and South. + +[location:111] +pickproof_n = yes +name = North Market Gate +closeable_n = yes +description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. To the South, Seventh Street heads into the market while the rest of the city lies trough the gate to the North. +terrain = city +keywords_n = ['gate'] +link_s = location:110 +description_n = This gate leads from the market to the city proper. +link_n = location:0 + +[location:110] +link_n = location:111 +terrain = city +link_e = location:109 +name = North Seventh and Third +link_s = location:112 +link_w = location:135 +description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. To the East and West lies Third Avenue while Seventh Street runs North and South. + +[location:113] +link_n = location:112 +terrain = city +name = North Seventh Street +keywords_w = ['booth'] +closeable_w = yes +description_w = This is a simple merchant's booth. +link_s = location:161 +pickproof_w = yes +link_w = location:137 +description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. Seventh Street runs North and South here while a merchant's booth lies to the West. + +[location:112] +keywords_e = ['booth'] +description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. Seventh Street runs North and South here while a merchant's booth lies to the East. +closeable_e = yes +pickproof_e = yes +terrain = city +link_e = location:138 +name = North Seventh Street +description_e = This is a simple merchant's booth. +link_s = location:113 +link_n = location:110 + +[location:137] +terrain = inside +name = New BuildWalk Room +link_e = location:113 +description = This unfinished room was created by Imp. + +[location:136] +description_w = This gate leads from the market to the city proper. +terrain = city +link_e = location:128 +name = West Market Gate +keywords_w = ['gate'] +closeable_w = yes +pickproof_w = yes +link_w = location:0 +description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. To the East, Sixth Avenue heads into the market while the rest of the city lies trough the gate to the West. + +[location:135] +description_s = This is a simple merchant's booth. +name = West Third Avenue +terrain = city +link_e = location:110 +closeable_s = yes +keywords_s = ['booth'] +link_s = location:139 +link_w = location:134 +pickproof_s = yes +description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. Third Avenue runs East and West here while a merchant's booth lies to the South. + +[location:134] +pickproof_n = yes +name = West Third Avenue +closeable_n = yes +description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. Third Avenue runs East and West here while a storefront beckons to the North. +terrain = city +link_e = location:135 +keywords_n = ['shop'] +description_n = This is the entrance to a shop. +link_w = location:133 +link_n = location:140 + +[location:119] +description_s = This is the entrance to a shop. +name = East Fourth Avenue +terrain = city +link_e = location:120 +closeable_s = yes +keywords_s = ['shop'] +link_s = location:148 +link_w = location:118 +pickproof_s = yes +description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. Fourth Avenue runs East and West here while a storefront beckons to the South. + +[location:132] +keywords_e = ['booth'] +description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. First Street runs North and South here while a merchant's booth lies to the East. +closeable_e = yes +pickproof_e = yes +terrain = city +link_e = location:141 +name = North First Street +description_e = This is a simple merchant's booth. +link_s = location:131 +link_n = location:133 + +[location:131] +link_n = location:132 +terrain = city +name = North First Street +keywords_w = ['shop'] +closeable_w = yes +description_w = This is the entrance to a shop. +link_s = location:128 +pickproof_w = yes +link_w = location:142 +description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. First Street runs North and South here while a storefront beckons to the West. + +[location:130] +description_s = This is a simple merchant's booth. +name = West Sixth Avenue +terrain = city +link_e = location:161 +closeable_s = yes +keywords_s = ['booth'] +link_s = location:157 +link_w = location:129 +pickproof_s = yes +description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. Sixth Avenue runs East and West here while a merchant's booth lies to the South. + +[location:105] +link_n = location:106 +terrain = city +name = North Fifth Street +keywords_w = ['booth'] +closeable_w = yes +description_w = This is a simple merchant's booth. +link_s = location:103 +pickproof_w = yes +link_w = location:151 +description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. Fifth Street runs North and South here while a merchant's booth lies to the West. + +[location:138] +terrain = inside +link_w = location:112 +name = New BuildWalk Room +description = This unfinished room was created by Imp. + +[location:148] +link_n = location:119 +terrain = inside +description = This unfinished room was created by Imp. +name = New BuildWalk Room + +[location:149] +terrain = inside +name = New BuildWalk Room +link_e = location:121 +description = This unfinished room was created by Imp. + +[location:146] +link_n = location:123 +terrain = inside +description = This unfinished room was created by Imp. +name = New BuildWalk Room + +[location:147] +link_s = location:118 +terrain = inside +name = New BuildWalk Room +description = This unfinished room was created by Imp. + +[location:144] +terrain = inside +name = New BuildWalk Room +link_e = location:126 +description = This unfinished room was created by Imp. + +[location:145] +link_s = location:124 +terrain = inside +name = New BuildWalk Room +description = This unfinished room was created by Imp. + +[location:142] +terrain = inside +name = New BuildWalk Room +link_e = location:131 +description = This unfinished room was created by Imp. + +[location:143] +terrain = inside +link_w = location:127 +name = New BuildWalk Room +description = This unfinished room was created by Imp. + +[location:140] +link_s = location:134 +terrain = inside +name = New BuildWalk Room +description = This unfinished room was created by Imp. + +[location:141] +terrain = inside +link_w = location:132 +name = New BuildWalk Room +description = This unfinished room was created by Imp. + +[location:160] +link_n = location:102 +terrain = inside +description = You find yourself amidst cold, hard steel and iron equipment of every possible description. A slight breeze brings tinkle and clank sounds from all around you. +name = Blacksmith's Booth + +[location:161] +link_n = location:113 +terrain = city +link_e = location:101 +name = Second Square +link_s = location:114 +link_w = location:130 +link_u = location:100 +description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. To the East and West lies Sixth Avenue while Seventh Street runs North and South. A short flight of stairs leads up to the bandstand. + +[location:108] +description_s = This is a simple merchant's booth. +name = East Third Avenue +terrain = city +link_e = location:107 +closeable_s = yes +keywords_s = ['booth'] +link_s = location:153 +link_w = location:109 +pickproof_s = yes +description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. Third Avenue runs East and West here while a merchant's booth lies to the South. + +[location:109] +pickproof_n = yes +name = East Third Avenue +closeable_n = yes +description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. Third Avenue runs East and West here while a storefront beckons to the North. +terrain = city +link_e = location:108 +keywords_n = ['shop'] +description_n = This is the entrance to a shop. +link_w = location:110 +link_n = location:154 + +[location:128] +link_n = location:131 +terrain = city +link_e = location:129 +name = First and West Sixth +link_s = location:127 +link_w = location:136 +description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. To the East and West lies Sixth Avenue while First Street runs North and South. + +[location:139] +link_n = location:135 +terrain = inside +description = This unfinished room was created by Imp. +name = New BuildWalk Room + +[location:102] +description_s = This is a simple merchant's booth. +name = East Sixth Avenue +terrain = city +link_e = location:103 +closeable_s = yes +keywords_s = ['booth'] +link_s = location:160 +link_w = location:101 +pickproof_s = yes +description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. Sixth Avenue runs East and West here while a merchant's booth lies to the South. + +[location:103] +link_n = location:105 +terrain = city +link_e = location:104 +name = Fifth and East Sixth +link_s = location:122 +link_w = location:102 +description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. To the East and West lies Sixth Avenue while Fifth Street runs North and South. + +[location:100] +terrain = inside +description = The stand is currently not in use for a performance, but shoppers gather here to rest and chat with one another. A pleasantly cool breeze coupled with a nice view of the market make this a good place to relax and recover energy. +name = The Bandstand +link_d = location:161 + +[location:127] +keywords_e = ['booth'] +description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. First Street runs North and South here while a merchant's booth lies to the East. +closeable_e = yes +pickproof_e = yes +terrain = city +link_e = location:143 +name = South First Street +description_e = This is a simple merchant's booth. +link_s = location:126 +link_n = location:128 + +[location:120] +description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. To the West lies Fourth Avenue while Fifth Street leads North. +terrain = city +name = The Corner of Fifth and Fourth +link_w = location:119 +link_n = location:121 + +[location:121] +link_n = location:122 +terrain = city +name = South Fifth Street +keywords_w = ['booth'] +closeable_w = yes +description_w = This is a simple merchant's booth. +link_s = location:120 +pickproof_w = yes +link_w = location:149 +description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. Fifth Street runs North and South here while a merchant's booth lies to the West. + +[location:122] +keywords_e = ['shop'] +description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. Fifth Street runs North and South here while a storefront beckons to the East. +closeable_e = yes +pickproof_e = yes +terrain = city +link_e = location:150 +name = South Fifth Street +description_e = This is the entrance to a shop. +link_s = location:121 +link_n = location:103 + +[location:123] +description_s = This is the entrance to a shop. +name = West Fourth Avenue +terrain = city +link_e = location:116 +closeable_s = yes +keywords_s = ['shop'] +link_s = location:146 +link_w = location:124 +pickproof_s = yes +description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. Fourth Avenue runs East and West here while a storefront beckons to the South. + +[location:124] +pickproof_n = yes +name = West Fourth Avenue +closeable_n = yes +description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. Fourth Avenue runs East and West here while a merchant's booth lies to the North. +terrain = city +link_e = location:123 +keywords_n = ['booth'] +description_n = This is a simple merchant's booth. +link_w = location:125 +link_n = location:145 + +[location:133] +name = The Corner of First and Third +terrain = city +link_e = location:134 +link_s = location:132 +description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. To the East lies Third Avenue while First Street leads South. + +[location:129] +pickproof_n = yes +name = West Sixth Avenue +closeable_n = yes +description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. Sixth Avenue runs East and West here while a merchant's booth lies to the North. +terrain = city +link_e = location:130 +keywords_n = ['booth'] +description_n = This is a simple merchant's booth. +link_w = location:128 +link_n = location:158 + +[location:125] +description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. To the East lies Fourth Avenue while First Street leads North. +terrain = city +link_e = location:124 +name = The Corner of First and Fourth +link_n = location:126 + +[location:107] +name = The Corner of Fifth and Third +terrain = city +link_s = location:106 +link_w = location:108 +description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. To the West lies Third Avenue while Fifth Street leads South. + +[location:126] +link_n = location:127 +terrain = city +name = South First Street +keywords_w = ['shop'] +closeable_w = yes +description_w = This is the entrance to a shop. +link_s = location:125 +pickproof_w = yes +link_w = location:144 +description = You find the hustle and bustle of this place remarkably distracting. The streets are filled with people of all races, buying and selling goods of every variety. First Street runs North and South here while a storefront beckons to the West. + diff --git a/menu b/menu index 951df54..c96399b 100644 --- a/menu +++ b/menu @@ -16,8 +16,8 @@ prompt = Whom would you like to awaken? prompt = > [menu:checking_new_account_name] -action_d = user.account.delete() -action_g = user.account.delete() +action_d = user.account.destroy() +action_g = user.account.destroy() branch_d = disconnecting branch_g = entering_account_name branch_n = entering_new_password @@ -58,7 +58,7 @@ description = Your new avatar needs a name. This will be the name with which $(t prompt = Choose a name for $(tpop): [menu:delete_account] -action_y = user.delete() +action_y = user.destroy() branch_n = main_utility branch_y = disconnecting choice_n = no, don't delete my account diff --git a/mudpy.conf b/mudpy.conf index c349956..5aba6b1 100644 --- a/mudpy.conf +++ b/mudpy.conf @@ -1,5 +1,6 @@ [control] default_files = {"account": "account", "actor": "actor", "command": "command", "internal": "internal", "location": "location", "menu": "menu", "other": "other" } +include_files = example private_files = account read_only = yes diff --git a/mudpy.py b/mudpy.py index 006b6a8..936d3ba 100644 --- a/mudpy.py +++ b/mudpy.py @@ -34,12 +34,17 @@ class Element: DataFile(self.origin, universe) if not universe.files[self.origin].data.has_section(self.key): universe.files[self.origin].data.add_section(self.key) - def delete(self): - log("Deleting: " + self.key + ".") + def destroy(self): + """Remove an element from the universe and destroy it.""" + log("Destroying: " + self.key + ".") universe.files[self.origin].data.remove_section(self.key) del universe.categories[self.category][self.subkey] del universe.contents[self.key] del self + def delete(self, facet): + """Delete a facet from the element.""" + if universe.files[self.origin].data.has_option(self.key, facet): + universe.files[self.origin].data.remove_option(self.key, facet) def facets(self): """Return a list of facets for this element.""" return universe.files[self.origin].data.options(self.key) @@ -118,7 +123,7 @@ class DataFile: DataFile(include_file, universe) def save(self): if ( self.data.sections() or exists(self.filename) ) and not ( self.data.has_option("control", "read_only") and self.data.getboolean("control", "read_only") ): - if not exists(dirname(self.filename)): makedirs(dirname) + if not exists(dirname(self.filename)): makedirs(dirname(self.filename)) file_descriptor = file(self.filename, "w") if self.filename in universe.private_files and oct(S_IMODE(stat(self.filename)[ST_MODE])) != 0600: chmod(self.filename, 0600) @@ -404,6 +409,24 @@ class User: # put on the end of the queue self.input_queue.append(line) + def can_run(self, command): + """Check if the user can run this command object.""" + + # has to be in the commands category + if command not in universe.categories["command"].values(): result = False + + # administrators can run any command + elif self.account.getboolean("administrator"): result = True + + # everyone can run non-administrative commands + elif not command.getboolean("administrative"): result = True + + # otherwise the command cannot be run by this user + else: result = False + + # pass back the result + return result + def new_avatar(self): """Instantiate a new, unconfigured avatar for this user.""" counter = 0 @@ -416,15 +439,15 @@ class User: def delete_avatar(self, avatar): """Remove an avatar from the world and from the user's list.""" if self.avatar is universe.contents[avatar]: self.avatar = None - universe.contents[avatar].delete() + universe.contents[avatar].destroy() avatars = self.account.getlist("avatars") avatars.remove(avatar) self.account.set("avatars", avatars) - def delete(self): - """Delete the user and associated avatars.""" + def destroy(self): + """Destroy the user and associated avatars.""" for avatar in self.account.getlist("avatars"): self.delete_avatar(avatar) - self.account.delete() + self.account.destroy() def list_avatar_names(self): """List names of assigned avatars.""" @@ -616,6 +639,10 @@ def replace_macros(user, text, is_input=False): return text +def escape_macros(text): + """Escapes replacement macros in text.""" + return text.replace("$(", "$_(") + def check_time(frequency): """Check for a factor of the current increment count.""" if type(frequency) is str: @@ -948,7 +975,7 @@ def handler_entering_new_password(user): # too many tries, so adios else: user.send("$(eol)$(red)Too many failed password attempts...$(nrm)$(eol)") - user.account.delete() + user.account.destroy() user.state = "disconnecting" def handler_verifying_new_password(user): @@ -974,7 +1001,7 @@ def handler_verifying_new_password(user): # otherwise, sayonara else: user.send("$(eol)$(red)Too many failed password attempts...$(nrm)$(eol)") - user.account.delete() + user.account.destroy() user.state = "disconnecting" def handler_active(user): @@ -985,22 +1012,26 @@ def handler_active(user): # split out the command (first word) and parameters (everything else) if input_data.find(" ") > 0: - command, parameters = input_data.split(" ", 1) + command_name, parameters = input_data.split(" ", 1) else: - command = input_data + command_name = input_data parameters = "" # lowercase the command - command = command.lower() + command_name = command_name.lower() # the command matches a command word for which we have data - if command in universe.categories["command"]: - exec(universe.categories["command"][command].get("action")) + if command_name in universe.categories["command"]: + command = universe.categories["command"][command_name] + else: command = None - # no data matching the entered command word - elif command: command_error(user, command, parameters) + # if it's allowed, do it + if user.can_run(command): exec(command.get("action")) -def command_halt(user, command="", parameters=""): + # otherwise, give an error + elif command_name: command_error(user, input_data) + +def command_halt(user, parameters): """Halt the world.""" # see if there's a message or use a generic one @@ -1014,7 +1045,7 @@ def command_halt(user, command="", parameters=""): # set a flag to terminate the world universe.terminate_world = True -def command_reload(user, command="", parameters=""): +def command_reload(user): """Reload all code modules, configs and data.""" # let the user know and log @@ -1024,11 +1055,7 @@ def command_reload(user, command="", parameters=""): # set a flag to reload universe.reload_modules = True -def command_quit(user, command="", parameters=""): - """Quit the world.""" - user.state = "main_utility" - -def command_help(user, command="", parameters=""): +def command_help(user, parameters): """List available commands and provide help for commands.""" # did the user ask for help on a specific command word? @@ -1036,15 +1063,22 @@ def command_help(user, command="", parameters=""): # is the command word one for which we have data? if parameters in universe.categories["command"]: + command = universe.categories["command"][parameters] + else: command = None + + # only for allowed commands + if user.can_run(command): # add a description if provided - description = universe.categories["command"][parameters].get("description") + description = command.get("description") if not description: description = "(no short description provided)" - output = "$(grn)" + parameters + "$(nrm) - " + description + "$(eol)$(eol)" + if command.getboolean("administrative"): output = "$(red)" + else: output = "$(grn)" + output += parameters + "$(nrm) - " + description + "$(eol)$(eol)" # add the help text if provided - help_text = universe.categories["command"][parameters].get("help") + help_text = command.get("help") if not help_text: help_text = "No help is provided for this command." output += help_text @@ -1061,16 +1095,20 @@ def command_help(user, command="", parameters=""): sorted_commands = universe.categories["command"].keys() sorted_commands.sort() for item in sorted_commands: - description = universe.categories["command"][item].get("description") - if not description: - description = "(no short description provided)" - output += " $(grn)" + item + "$(nrm) - " + description + "$(eol)" + command = universe.categories["command"][item] + if user.can_run(command): + description = command.get("description") + if not description: + description = "(no short description provided)" + if command.getboolean("administrative"): output += " $(red)" + else: output += " $(grn)" + output += item + "$(nrm) - " + description + "$(eol)" output += "$(eol)Enter \"help COMMAND\" for help on a command named \"COMMAND\"." # send the accumulated output to the user user.send(output) -def command_say(user, command="", parameters=""): +def command_say(user, parameters): """Speak to others in the same room.""" # check for replacement macros @@ -1111,44 +1149,120 @@ def command_say(user, command="", parameters=""): # tell the room # TODO: we won't be using broadcast once there are actual rooms - broadcast(user.account.get("name") + " " + action + "s, \"" + message + "\"") + broadcast(user.avatar.get("name") + " " + action + "s, \"" + message + "\"") # there was no message else: user.send("What do you want to say?") -def command_show(user, command="", parameters=""): +def command_show(user, parameters): """Show program data.""" - if parameters == "avatars": - message = "These are the avatars managed by your account:$(eol)" - avatars = user.list_avatar_names() - avatars.sort() - for avatar in avatars: message += "$(eol) $(grn)" + avatar + "$(nrm)" - elif parameters == "files": - message = "These are the current files containing the universe:$(eol)" - keys = universe.files.keys() - keys.sort() - for key in keys: message += "$(eol) $(grn)" + key + "$(nrm)" - elif parameters == "universe": - message = "These are the current elements in the universe:$(eol)" - keys = universe.contents.keys() - keys.sort() - for key in keys: message += "$(eol) $(grn)" + key + "$(nrm)" - elif parameters == "time": - message = universe.categories["internal"]["counters"].get("elapsed") + " increments elapsed since the world was created." - elif parameters: message = "I don't know what \"" + parameters + "\" is." - else: message = "What do you want to show?" + message = "" + if parameters.find(" ") < 1: + if parameters == "time": + message = universe.categories["internal"]["counters"].get("elapsed") + " increments elapsed since the world was created." + elif parameters == "categories": + message = "These are the element categories:$(eol)" + categories = universe.categories.keys() + categories.sort() + for category in categories: message += "$(eol) $(grn)" + category + "$(nrm)" + elif parameters == "files": + message = "These are the current files containing the universe:$(eol)" + filenames = universe.files.keys() + filenames.sort() + for filename in filenames: message += "$(eol) $(grn)" + filename + "$(nrm)" + else: message = "" + else: + arguments = parameters.split() + if arguments[0] == "category": + if arguments[1] in universe.categories: + message = "These are the elements in the \"" + arguments[1] + "\" category:$(eol)" + elements = universe.categories[arguments[1]].keys() + elements.sort() + for element in elements: + message += "$(eol) $(grn)" + universe.categories[arguments[1]][element].key + "$(nrm)" + elif arguments[0] == "element": + if arguments[1] in universe.contents: + message = "These are the properties of the \"" + arguments[1] + "\" element:$(eol)" + element = universe.contents[arguments[1]] + facets = element.facets() + facets.sort() + for facet in facets: + message += "$(eol) $(grn)" + facet + ": $(red)" + escape_macros(element.get(facet)) + "$(nrm)" + if not message: + if parameters: message = "I don't know what \"" + parameters + "\" is." + else: message = "What do you want to show?" + user.send(message) + +def command_create(user, parameters): + """Create an element if it does not exist.""" + if not parameters: message = "You must at least specify an element to create." + else: + arguments = parameters.split() + if len(arguments) == 1: arguments.append("") + if len(arguments) == 2: + element, filename = arguments + if element in universe.contents: message = "The \"" + element + "\" element already exists." + else: + message = "You create \"" + element + "\" within the universe." + logline = user.account.get("name") + " created an element: " + element + if filename: + logline += " in file " + filename + if filename not in universe.files: + message += " Warning: \"" + filename + "\" is not yet included in any other file and will not be read on startup unless this is remedied." + Element(element, universe, filename) + log(logline) + elif len(arguments) > 2: message = "You can only specify an element and a filename." + user.send(message) + +def command_destroy(user, parameters): + """Destroy an element if it exists.""" + if not parameters: message = "You must specify an element to destroy." + else: + if parameters not in universe.contents: message = "The \"" + parameters + "\" element does not exist." + else: + universe.contents[parameters].destroy() + message = "You destroy \"" + parameters + "\" within the universe." + log(user.account.get("name") + " destroyed an element: " + parameters) + user.send(message) + +def command_set(user, parameters): + """Set a facet of an element.""" + if not parameters: message = "You must specify an element, a facet and a value." + else: + arguments = parameters.split(" ", 2) + if len(arguments) == 1: message = "What facet of element \"" + arguments[0] + "\" would you like to set?" + elif len(arguments) == 2: message = "What value would you like to set for the \"" + arguments[1] + "\" facet of the \"" + arguments[0] + "\" element?" + else: + element, facet, value = arguments + if element not in universe.contents: message = "The \"" + element + "\" element does not exist." + else: + universe.contents[element].set(facet, value) + message = "You have successfully (re)set the \"" + facet + "\" facet of element \"" + element + "\". Try \"show element " + element + "\" for verification." + user.send(message) + +def command_delete(user, parameters): + """Delete a facet from an element.""" + if not parameters: message = "You must specify an element and a facet." + else: + arguments = parameters.split(" ") + if len(arguments) == 1: message = "What facet of element \"" + arguments[0] + "\" would you like to delete?" + elif len(arguments) != 2: message = "You may only specify an element and a facet." + else: + element, facet = arguments + if element not in universe.contents: message = "The \"" + element + "\" element does not exist." + elif facet not in universe.contents[element].facets(): message = "The \"" + element + "\" element has no \"" + facet + "\" facet." + else: + universe.contents[element].delete(facet) + message = "You have successfully deleted the \"" + facet + "\" facet of element \"" + element + "\". Try \"show element " + element + "\" for verification." user.send(message) -def command_error(user, command="", parameters=""): +def command_error(user, input_data): """Generic error for an unrecognized command word.""" # 90% of the time use a generic error if randrange(10): - message = "I'm not sure what \"" + command - if parameters: - message += " " + parameters - message += "\" means..." + message = "I'm not sure what \"" + input_data + "\" means..." # 10% of the time use the classic diku error else: