From 495ddec8ecfe5db686696428a92465b296f83232 Mon Sep 17 00:00:00 2001 From: Vincent Le Goff Date: Mon, 29 Jul 2019 13:23:40 +0200 Subject: [PATCH 001/164] [clothing contrib] Allow to create new Clothing classes --- evennia/contrib/clothing.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/evennia/contrib/clothing.py b/evennia/contrib/clothing.py index 608fa0aa054..657a5345914 100644 --- a/evennia/contrib/clothing.py +++ b/evennia/contrib/clothing.py @@ -368,7 +368,7 @@ def func(self): wearstyle = True if not clothing: return - if not clothing.is_typeclass("evennia.contrib.clothing.Clothing"): + if not clothing.is_typeclass("evennia.contrib.clothing.Clothing", exact=False): self.caller.msg("That's not clothes!") return @@ -460,10 +460,10 @@ def func(self): cover_with = self.caller.search(self.arglist[1], candidates=self.caller.contents) if not to_cover or not cover_with: return - if not to_cover.is_typeclass("evennia.contrib.clothing.Clothing"): + if not to_cover.is_typeclass("evennia.contrib.clothing.Clothing", exact=False): self.caller.msg("%s isn't clothes!" % to_cover.name) return - if not cover_with.is_typeclass("evennia.contrib.clothing.Clothing"): + if not cover_with.is_typeclass("evennia.contrib.clothing.Clothing", exact=False): self.caller.msg("%s isn't clothes!" % cover_with.name) return if cover_with.db.clothing_type: From 82fc5c81b9cf3eaa906bf13d32420c76801d2600 Mon Sep 17 00:00:00 2001 From: Brenden Tuck Date: Thu, 8 Aug 2019 20:30:16 -0400 Subject: [PATCH 002/164] Add a multimedia plugin to respond to images/audio/video server commands --- .../static/webclient/js/plugins/multimedia.js | 53 +++++++++++++++++++ .../webclient/templates/webclient/base.html | 1 + 2 files changed, 54 insertions(+) create mode 100644 evennia/web/webclient/static/webclient/js/plugins/multimedia.js diff --git a/evennia/web/webclient/static/webclient/js/plugins/multimedia.js b/evennia/web/webclient/static/webclient/js/plugins/multimedia.js new file mode 100644 index 00000000000..59cdc3a4c83 --- /dev/null +++ b/evennia/web/webclient/static/webclient/js/plugins/multimedia.js @@ -0,0 +1,53 @@ +/* + * + * Evennia Webclient multimedia outputs plugin + * + * in evennia python code: + * + * target.msg( image="URL" ) + * target.msg( audio="URL" ) + * target.msg( video="URL" ) + * + */ +let multimedia_plugin = (function () { + // + var image = function (args, kwargs) { + var mwin = $("#messagewindow"); + mwin.append(""); + mwin.scrollTop(mwin[0].scrollHeight); + } + + var audio = function (args, kwargs) { + // create an HTML5 audio control (only .mp3 is fully compatible with all major browsers) + var mwin = $("#messagewindow"); + mwin.append(""); + mwin.scrollTop(mwin[0].scrollHeight); + } + + var video = function (args, kwargs) { + // create an HTML5 video element (only h264 .mp4 is compatible with all major browsers) + var mwin = $("#messagewindow"); + mwin.append(""); + mwin.scrollTop(mwin[0].scrollHeight); + } + + // + // Mandatory plugin init function + var init = function () { + Evennia = window.Evennia; + Evennia.emitter.on('image', image); // capture "image" commands + Evennia.emitter.on('audio', audio); // capture "audio" commands + Evennia.emitter.on('video', video); // capture "video" commands + console.log('Multimedia plugin initialized'); + } + + return { + init: init, + } +})(); +plugin_handler.add('multimedia_plugin', multimedia_plugin); + diff --git a/evennia/web/webclient/templates/webclient/base.html b/evennia/web/webclient/templates/webclient/base.html index fa16a152ee8..399389f15eb 100644 --- a/evennia/web/webclient/templates/webclient/base.html +++ b/evennia/web/webclient/templates/webclient/base.html @@ -93,6 +93,7 @@ --> + {% endblock %} From 33dfe7ea68de32be93d9e98508baa7df7958ac55 Mon Sep 17 00:00:00 2001 From: Brenden Tuck Date: Fri, 9 Aug 2019 16:59:09 +0000 Subject: [PATCH 003/164] Make single input window the default instead of dual inputs --- .../js/plugins/goldenlayout_default_config.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/evennia/web/webclient/static/webclient/js/plugins/goldenlayout_default_config.js b/evennia/web/webclient/static/webclient/js/plugins/goldenlayout_default_config.js index 04cc5246b3e..1ef7a959f31 100644 --- a/evennia/web/webclient/static/webclient/js/plugins/goldenlayout_default_config.js +++ b/evennia/web/webclient/static/webclient/js/plugins/goldenlayout_default_config.js @@ -37,17 +37,17 @@ var goldenlayout_config = { // Global Variable used in goldenlayout.js init() // id: "inputComponent", // mark 'ignore this component during output message processing' // height: 6, // isClosable: false, +// }, { +// type: "component", +// componentName: "input", +// id: "inputComponent", // mark for ignore +// height: 12, // percentage +// tooltip: "Input - The last input in the layout is always the default.", }, { type: "component", componentName: "input", id: "inputComponent", // mark for ignore - height: 12, // percentage - tooltip: "Input - The last input in the layout is always the default.", - }, { - type: "component", - componentName: "input", - id: "inputComponent", // mark for ignore - height: 12, // percentage + height: 20, // percentage isClosable: false, // remove the 'x' control to close this tooltip: "Input - The last input in the layout is always the default.", }] From 0ce67a65b08ba84f874a9307d5d6c4e3d8d955d9 Mon Sep 17 00:00:00 2001 From: Brenden Tuck Date: Fri, 9 Aug 2019 13:42:55 -0400 Subject: [PATCH 004/164] rework prompts to show up in input panes --- .../static/webclient/css/goldenlayout.css | 11 +++++++---- .../static/webclient/js/plugins/default_out.js | 17 +++++++++++++---- .../static/webclient/js/plugins/goldenlayout.js | 10 ++++++++-- 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/evennia/web/webclient/static/webclient/css/goldenlayout.css b/evennia/web/webclient/static/webclient/css/goldenlayout.css index 8096f8a89b8..4c0e77d254f 100644 --- a/evennia/web/webclient/static/webclient/css/goldenlayout.css +++ b/evennia/web/webclient/static/webclient/css/goldenlayout.css @@ -116,11 +116,16 @@ label { height: 100%; } +.inputfieldwrapper { + width: 100%; + height: 100%; +} + .inputsend { position: absolute; right: 0; z-index: 1; - height: inherit; + height: 100%; width: 30px; color: white; background-color: black; @@ -128,9 +133,7 @@ label { } .inputfield { - position: absolute; - top: 0; - height: inherit; + height: 100%; width: calc(100% - 30px); background-color: black; color: white; diff --git a/evennia/web/webclient/static/webclient/js/plugins/default_out.js b/evennia/web/webclient/static/webclient/js/plugins/default_out.js index 01be2f9c625..ff6eb2f34b6 100644 --- a/evennia/web/webclient/static/webclient/js/plugins/default_out.js +++ b/evennia/web/webclient/static/webclient/js/plugins/default_out.js @@ -21,10 +21,19 @@ let defaultout_plugin = (function () { // // By default just show the prompt. var onPrompt = function (args, kwargs) { - // show prompt - $('#prompt') - .addClass("out") - .html(args[0]); + // show prompt on every input pane + var prompts = $('.prompt'); + + for( var x=0; x < prompts.length; x++ ) { + var prmpt = $( prompts[x] ); + var sibling = prmpt.siblings().first(); + + prmpt.addClass("out") + .html(args[0]) + .css({'height':'1.5em'}); + + sibling.css({'height':'calc(100% - 1.5em)'}); + } return true; } diff --git a/evennia/web/webclient/static/webclient/js/plugins/goldenlayout.js b/evennia/web/webclient/static/webclient/js/plugins/goldenlayout.js index 917d075ed36..d1409ab3f7e 100644 --- a/evennia/web/webclient/static/webclient/js/plugins/goldenlayout.js +++ b/evennia/web/webclient/static/webclient/js/plugins/goldenlayout.js @@ -476,6 +476,7 @@ let goldenlayout = (function () { myLayout = new GoldenLayout( window.goldenlayout_config, mainsub ); + $("#prompt").remove(); // remove the HTML-defined prompt div $("#inputcontrol").remove(); // remove the cluttered, HTML-defined input divs // register our component and replace the default messagewindow with the Main component @@ -486,11 +487,16 @@ let goldenlayout = (function () { // register our new input component myLayout.registerComponent( "input", function (container, componentState) { - var inputfield = $(""); + var promptfield = $("
"); + var formcontrol = $(""); var button = $(""); + + var inputfield = $("
") + .append( button ) + .append( formcontrol ); $("
") - .append( button ) + .append( promptfield ) .append( inputfield ) .appendTo( container.getElement() ); From ec833c21d6e069614e4229d28c7d5cf7ef02dde8 Mon Sep 17 00:00:00 2001 From: Griatch Date: Fri, 9 Aug 2019 22:41:37 +0200 Subject: [PATCH 005/164] Extend new unit tests in main cmdparser --- evennia/commands/cmdparser.py | 8 +++- evennia/commands/tests.py | 86 +++++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+), 2 deletions(-) diff --git a/evennia/commands/cmdparser.py b/evennia/commands/cmdparser.py index 2c78a247ab5..5bbc12878b6 100644 --- a/evennia/commands/cmdparser.py +++ b/evennia/commands/cmdparser.py @@ -59,11 +59,11 @@ def build_matches(raw_string, cmdset, include_prefixes=False): matches (list) A list of match tuples created by `cmdparser.create_match`. """ - l_raw_string = raw_string.lower() matches = [] try: if include_prefixes: # use the cmdname as-is + l_raw_string = raw_string.lower() for cmd in cmdset: matches.extend([create_match(cmdname, raw_string, cmd, cmdname) for cmdname in [cmd.key] + cmd.aliases @@ -72,9 +72,13 @@ def build_matches(raw_string, cmdset, include_prefixes=False): cmd.arg_regex.match(l_raw_string[len(cmdname):]))]) else: # strip prefixes set in settings + raw_string = (raw_string.lstrip(_CMD_IGNORE_PREFIXES) + if len(raw_string) > 1 else l_raw_string) + l_raw_string = raw_string.lower() for cmd in cmdset: for raw_cmdname in [cmd.key] + cmd.aliases: - cmdname = raw_cmdname.lstrip(_CMD_IGNORE_PREFIXES) if len(raw_cmdname) > 1 else raw_cmdname + cmdname = (raw_cmdname.lstrip(_CMD_IGNORE_PREFIXES) + if len(raw_cmdname) > 1 else raw_cmdname) if cmdname and l_raw_string.startswith(cmdname.lower()) and \ (not cmd.arg_regex or cmd.arg_regex.match(l_raw_string[len(cmdname):])): matches.append(create_match(cmdname, raw_string, cmd, raw_cmdname)) diff --git a/evennia/commands/tests.py b/evennia/commands/tests.py index b2f903996ec..64fd8cebd72 100644 --- a/evennia/commands/tests.py +++ b/evennia/commands/tests.py @@ -3,9 +3,11 @@ """ +from django.test import override_settings from evennia.utils.test_resources import EvenniaTest, TestCase from evennia.commands.cmdset import CmdSet from evennia.commands.command import Command +from evennia.commands import cmdparser # Testing-command sets @@ -376,3 +378,87 @@ def _callback(cmdset): self.assertEqual(len(cmdset.commands), 9) deferred.addCallback(_callback) return deferred + + +class AccessableCommand(Command): + def access(*args, **kwargs): + return True + +class _CmdTest1(AccessableCommand): + key = "test1" + + +class _CmdTest2(AccessableCommand): + key = "another command" + + +class _CmdTest3(AccessableCommand): + key = "&the third command" + + +class _CmdTest4(AccessableCommand): + key = "test2" + + +class _CmdSetTest(CmdSet): + key = "test_cmdset" + def at_cmdset_creation(self): + self.add(_CmdTest1) + self.add(_CmdTest2) + self.add(_CmdTest3) + + +class TestCmdParser(TestCase): + + def test_create_match(self): + class DummyCmd: + pass + dummy = DummyCmd() + + self.assertEqual( + cmdparser.create_match("look at", "look at target", dummy, "look"), + ("look at", " target", dummy, 7, 0.5, "look")) + + @override_settings(CMD_IGNORE_PREFIXES="@&/+") + def test_build_matches(self): + a_cmdset = _CmdSetTest() + bcmd = [cmd for cmd in a_cmdset.commands if cmd.key == "test1"][0] + + # normal parsing + self.assertEqual( + cmdparser.build_matches("test1 rock", a_cmdset, include_prefixes=False), + [("test1", " rock", bcmd, 5, 0.5, 'test1')] + ) + + # test prefix exclusion + bcmd = [cmd for cmd in a_cmdset.commands if cmd.key == "another command"][0] + self.assertEqual( + cmdparser.build_matches("@another command smiles to me ", + a_cmdset, include_prefixes=False), + [("another command", " smiles to me ", bcmd, 15, 0.5, 'another command')] + ) + # test prefix exclusion on the cmd class + bcmd = [cmd for cmd in a_cmdset.commands if cmd.key == "&the third command"][0] + self.assertEqual( + cmdparser.build_matches("the third command", + a_cmdset, include_prefixes=False), + [("the third command", "", bcmd, 17, 1.0, '&the third command')] + ) + + @override_settings(SEARCH_MULTIMATCH_REGEX=r"(?P[0-9]+)-(?P.*)") + def test_num_prefixes(self): + self.assertEqual(cmdparser.try_num_prefixes("look me"), + (None, None)) + self.assertEqual(cmdparser.try_num_prefixes("3-look me"), + ('3', "look me")) + self.assertEqual(cmdparser.try_num_prefixes("567-look me"), + ('567', "look me")) + + @override_settings(SEARCH_MULTIMATCH_REGEX=r"(?P[0-9]+)-(?P.*)", + CMD_IGNORE_PREFIXES="@&/+") + def test_cmdparser(self): + a_cmdset = _CmdSetTest() + bcmd = [cmd for cmd in a_cmdset.commands if cmd.key == "test1"][0] + + self.assertEqual(cmdparser.cmdparser("test1hello", a_cmdset, None), + [("test1", "hello", bcmd, 5, 0.5, 'test1')]) From 1e6d6f59d128b5563afeea2833c7d6681bba67c6 Mon Sep 17 00:00:00 2001 From: Griatch Date: Fri, 9 Aug 2019 23:35:34 +0200 Subject: [PATCH 006/164] Change prompt bg-color. Default client to no help-popup --- evennia/settings_default.py | 2 +- evennia/web/webclient/static/webclient/css/webclient.css | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/evennia/settings_default.py b/evennia/settings_default.py index 114747ddcf9..2d5d96cfb23 100644 --- a/evennia/settings_default.py +++ b/evennia/settings_default.py @@ -820,7 +820,7 @@ WEBCLIENT_OPTIONS = { "gagprompt": True, # Gags prompt from the output window and keep them # together with the input bar - "helppopup": True, # Shows help files in a new popup window + "helppopup": False, # Shows help files in a new popup window "notification_popup": False, # Shows notifications of new messages as # popup windows "notification_sound": False # Plays a sound for notifications of new diff --git a/evennia/web/webclient/static/webclient/css/webclient.css b/evennia/web/webclient/static/webclient/css/webclient.css index 00281d82d48..c0dc43c4901 100644 --- a/evennia/web/webclient/static/webclient/css/webclient.css +++ b/evennia/web/webclient/static/webclient/css/webclient.css @@ -47,7 +47,10 @@ div {margin:0px;} .inp { color: #555 } /* Messages returned from the server (most messages) */ -.out { color: #aaa } +.out { + color: #aaa; + background-color: #000; +} /* Error messages (red) */ .err { color: #f00; } From 472de0ece82ec20d411419af8734fbc77296721f Mon Sep 17 00:00:00 2001 From: Griatch Date: Sat, 10 Aug 2019 00:21:21 +0200 Subject: [PATCH 007/164] Updated CHANGELOG --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f185d9dfa0..b94758ab75f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ - `py` command now reroutes stdout to output results in-game client. `py` without arguments starts a full interactive Python console. +- Webclient default to a single input pane instead of two. Default to no help-popup. +- Webclient fix of prompt display +- Webclient multimedia support for relaying images, video and sounds via `.msg(image=URL)`, `.msg(video=URL)` + and `.msg(audio=URL)` ## Evennia 0.9 (2018-2019) From 960086289d88d58ecdf3e4ac53b301a2156af4fd Mon Sep 17 00:00:00 2001 From: Griatch Date: Sat, 10 Aug 2019 12:15:26 +0200 Subject: [PATCH 008/164] Make MXP links more visible in webclient --- evennia/web/webclient/static/webclient/css/webclient.css | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/evennia/web/webclient/static/webclient/css/webclient.css b/evennia/web/webclient/static/webclient/css/webclient.css index c0dc43c4901..856931501aa 100644 --- a/evennia/web/webclient/static/webclient/css/webclient.css +++ b/evennia/web/webclient/static/webclient/css/webclient.css @@ -47,7 +47,7 @@ div {margin:0px;} .inp { color: #555 } /* Messages returned from the server (most messages) */ -.out { +.out { color: #aaa; background-color: #000; } @@ -330,6 +330,13 @@ div {margin:0px;} float: left; } + +/* MXP links */ +#mxplink { + text-decoration: underline; +} + + /* XTERM256 colors */ .color-000 { color: #000000; } From 22109da978bbff2605eaca6120f34da88b8a3aa6 Mon Sep 17 00:00:00 2001 From: Griatch Date: Wed, 14 Aug 2019 21:05:44 +0200 Subject: [PATCH 009/164] Make comm-commands act on the default Channel class. Resolves #1569 --- evennia/commands/default/comms.py | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/evennia/commands/default/comms.py b/evennia/commands/default/comms.py index 8beaf9956f4..dee65b0efc1 100644 --- a/evennia/commands/default/comms.py +++ b/evennia/commands/default/comms.py @@ -19,6 +19,8 @@ from evennia.utils.utils import make_iter, class_from_module COMMAND_DEFAULT_CLASS = class_from_module(settings.COMMAND_DEFAULT_CLASS) +CHANNEL_DEFAULT_TYPECLASS = class_from_module(settings.BASE_CHANNEL_TYPECLASS) + # limit symbol import for API __all__ = ("CmdAddCom", "CmdDelCom", "CmdAllCom", @@ -33,10 +35,10 @@ def find_channel(caller, channelname, silent=False, noaliases=False): Helper function for searching for a single channel with some error handling. """ - channels = ChannelDB.objects.channel_search(channelname) + channels = CHANNEL_DEFAULT_TYPECLASS.objects.channel_search(channelname) if not channels: if not noaliases: - channels = [chan for chan in ChannelDB.objects.get_all_channels() + channels = [chan for chan in CHANNEL_DEFAULT_TYPECLASS.objects.get_all_channels() if channelname in chan.aliases.all()] if channels: return channels[0] @@ -198,9 +200,9 @@ class CmdAllCom(COMMAND_DEFAULT_CLASS): Usage: allcom [on | off | who | destroy] - Allows the user to universally turn off or on all channels they are on, - as well as perform a 'who' for all channels they are on. Destroy deletes - all channels that you control. + Allows the user to universally turn off or on all channels they are on, as + well as perform a 'who' for all channels they are on. Destroy deletes all + channels that you control. Without argument, works like comlist. """ @@ -225,25 +227,25 @@ def func(self): if args == "on": # get names of all channels available to listen to # and activate them all - channels = [chan for chan in ChannelDB.objects.get_all_channels() + channels = [chan for chan in CHANNEL_DEFAULT_TYPECLASS.objects.get_all_channels() if chan.access(caller, 'listen')] for channel in channels: self.execute_cmd("addcom %s" % channel.key) elif args == "off": # get names all subscribed channels and disconnect from them all - channels = ChannelDB.objects.get_subscriptions(caller) + channels = CHANNEL_DEFAULT_TYPECLASS.objects.get_subscriptions(caller) for channel in channels: self.execute_cmd("delcom %s" % channel.key) elif args == "destroy": # destroy all channels you control - channels = [chan for chan in ChannelDB.objects.get_all_channels() + channels = [chan for chan in CHANNEL_DEFAULT_TYPECLASS.objects.get_all_channels() if chan.access(caller, 'control')] for channel in channels: self.execute_cmd("cdestroy %s" % channel.key) elif args == "who": # run a who, listing the subscribers on visible channels. string = "\n|CChannel subscriptions|n" - channels = [chan for chan in ChannelDB.objects.get_all_channels() + channels = [chan for chan in CHANNEL_DEFAULT_TYPECLASS.objects.get_all_channels() if chan.access(caller, 'listen')] if not channels: string += "No channels." @@ -282,13 +284,13 @@ def func(self): caller = self.caller # all channels we have available to listen to - channels = [chan for chan in ChannelDB.objects.get_all_channels() + channels = [chan for chan in CHANNEL_DEFAULT_TYPECLASS.objects.get_all_channels() if chan.access(caller, 'listen')] if not channels: self.msg("No channels available.") return # all channel we are already subscribed to - subs = ChannelDB.objects.get_subscriptions(caller) + subs = CHANNEL_DEFAULT_TYPECLASS.objects.get_subscriptions(caller) if self.cmdstring == "comlist": # just display the subscribed channels with no extra info @@ -561,7 +563,7 @@ def func(self): if ';' in lhs: channame, aliases = lhs.split(';', 1) aliases = [alias.strip().lower() for alias in aliases.split(';')] - channel = ChannelDB.objects.channel_search(channame) + channel = CHANNEL_DEFAULT_TYPECLASS.objects.channel_search(channame) if channel: self.msg("A channel with that name already exists.") return From 0336003d0fb9e72cf78cd106125e46af1ec1df25 Mon Sep 17 00:00:00 2001 From: Griatch Date: Thu, 15 Aug 2019 01:06:10 +0200 Subject: [PATCH 010/164] Add Sites/Pages listing to admin, as per #1744 --- evennia/settings_default.py | 4 +- .../templates/website/evennia_admin.html | 58 ++++++++++++++----- 2 files changed, 46 insertions(+), 16 deletions(-) diff --git a/evennia/settings_default.py b/evennia/settings_default.py index 2d5d96cfb23..6c69eabcacf 100644 --- a/evennia/settings_default.py +++ b/evennia/settings_default.py @@ -763,8 +763,8 @@ # Absolute path to the directory that holds file uploads from web apps. # Example: "/home/media/media.lawrence.com" MEDIA_ROOT = os.path.join(GAME_DIR, "web", "media") -# It's safe to dis-regard this, as it's a Django feature we only half use as a -# dependency, not actually what it's primarily meant for. +# If using Sites/Pages from the web admin, this value must be set to the +# database-id of the Site (domain) we want to use with this game's Pages. SITE_ID = 1 # The age for sessions. # Default: 1209600 (2 weeks, in seconds) diff --git a/evennia/web/website/templates/website/evennia_admin.html b/evennia/web/website/templates/website/evennia_admin.html index e01aee23802..4d52266ee91 100644 --- a/evennia/web/website/templates/website/evennia_admin.html +++ b/evennia/web/website/templates/website/evennia_admin.html @@ -9,40 +9,70 @@

Admin

- Welcome to the Evennia Admin Page. Here, you can edit many facets of accounts, characters, and other parts of the game. + The Evennia admin page allows for modifying database entities using a + graphical interface.

-
+

Game entities

+ +

-

Accounts

- Accounts can have several characters under them. A user's login and password information can be changed here. +

Accounts

+ Accounts can have several characters under them. A user's login and + password information can be changed here.

-

Objects

- Objects include everything from characters to rooms to exits. +

Objects

+ Objects include everything with an in-game location, from characters + to rooms, exits, swords and monsters.

-

Scripts

- Scripts are meta objects used to store game information, handle special functionality or perform timed actions. +

Scripts

+ Scripts are meta objects used to store game information and state, + handle special functionality or perform timed actions. They have no + in-game location.

-

Channels

- Channels are used for player communications. +

Channels

+ Channels are used to redirect and organize player communications.

-

Help Topics

- Help entries are custom entries added to the player help system. +

Help Topics

+ Extend the automatic command-help with custom help topics.

+ +
+ +

Extra website pages

+ +

+

Sites (domains)

+ Configure domain name for your pages. For local-only testing, create a + domain named localhost:4001. Then manually add + SITE_ID=<id> to your settings, where <id> + is the database-id of the domain you created (the <id> can be + found from the url of the domain in the admin, so + /admin/sites/site/3/change means that <id> is 3). +

+ +

+

Pages

+ Create, edit and publish new web pages without needing to know how to + code. Select the domain specified by SITE_ID above. +

+

- If you are an advanced user who needs access to the raw Django Admin, it is available here. - You can make this the default my changing EVENNIA_ADMIN to False in settings.py and reload. + If you are an advanced user who needs access to the raw Django Admin, + it is available here. You can + make this the default by changing EVENNIA_ADMIN to + False in settings.py and reload.

From 0ef4ea4d649e3a92effeff64f4d48a7d12d1c1ec Mon Sep 17 00:00:00 2001 From: Timothy Carlisle Date: Thu, 15 Aug 2019 02:19:31 -0500 Subject: [PATCH 011/164] Fixed Issue #1789: Help popup in web client not clearing after close I modified line 140: Replacing the .append with .prepend. This allows for a running log of previous help requests to be pushed to the bottom. --- evennia/web/webclient/static/webclient/js/plugins/options.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evennia/web/webclient/static/webclient/js/plugins/options.js b/evennia/web/webclient/static/webclient/js/plugins/options.js index d3b6d131584..1a92e94e6d6 100644 --- a/evennia/web/webclient/static/webclient/js/plugins/options.js +++ b/evennia/web/webclient/static/webclient/js/plugins/options.js @@ -137,7 +137,7 @@ let options_plugin = (function () { // is helppopup set? and if so, does this Text have type 'help'? if ('helppopup' in options && options['helppopup'] ) { if (kwargs && ('type' in kwargs) && (kwargs['type'] == 'help') ) { - $('#helpdialogcontent').append('
'+ args + '
'); + $('#helpdialogcontent').prepend('
'+ args + '
'); plugins['popups'].togglePopup("#helpdialog"); return true; } From 8961baa0a5b9b5419f864a144f080acc68a7ad0f Mon Sep 17 00:00:00 2001 From: fermuch Date: Fri, 16 Aug 2019 16:15:27 -0300 Subject: [PATCH 012/164] added translation to spanish --- evennia/locale/es/LC_MESSAGES/django.mo | Bin 0 -> 7313 bytes evennia/locale/es/LC_MESSAGES/django.po | 348 ++++++++++++++++++++++++ 2 files changed, 348 insertions(+) create mode 100644 evennia/locale/es/LC_MESSAGES/django.mo create mode 100644 evennia/locale/es/LC_MESSAGES/django.po diff --git a/evennia/locale/es/LC_MESSAGES/django.mo b/evennia/locale/es/LC_MESSAGES/django.mo new file mode 100644 index 0000000000000000000000000000000000000000..c63d414c7f7b271d9ea040d7c31d5a72dc82fed7 GIT binary patch literal 7313 zcmchbU2Ggz6~_nKQrNbXhL-P-o20IHsdsIsO`1AQ(=>5NO46jUQ(90P?#^6$Z!$Zx zojc>r#&IQh0)d27sXQV@2th^jfGSnA5+K#$5h3w{gsQ4YsDd{hsKiSpe&^2adi@op zLSp3ge`n`@oO?e0=bU+G$IUMXTo3d45}&_+BnVD}uinHzu7BJd1Si0Mf?oz7xFra_ z4L${a0A2$t;IBRw1fKz42W724f(OA{Zw-RG!C6qAe+$gP7y9?_fuH661MsWhJs%H( zuYh|&*>4`)4QAjc!JmM#-p~8^CU`IRe*!1Le}f9V_Y*-7g0tWw;49!5_#TMJ!R-iL z1$TgT@BnxOv|t0g)_?vs_%-ftxh)8u2d6-p_hax`@HgOIaL4UIa3^>i6gi#&*(&%x z_zmzS@F@5Wcptd?jv&|xJ^{*kV^H+}L-14JFF_gi1}O5p3yS=AqJ(j9FL)359QaYt zf!_g_z?0x#zzDn-p>|=@AArZXf0e~$zxP26-hCJMJYND)UGVab^7&7}8SZ}ziXHz0 ziaqvyy43eP_#*c`@N@DU{5*IU#u0nn4~kt*gEBAg|Nat)Xu<0sf59L4_XY5;-~-_M z;32Ta;Y80Cbl^*%$n!Crs16FHl_aGnf7qP{|dm8Zv!)a4&?bwjYk9Dsd70 znMzDoUFps)nIy6LN>XoX$9{{tZFZ`st+Tmm<#}gjaK`w(V2>p8GegEozlIg@yBi( z(c_pq6sUQ&RJ%mGySxj?w?;7L)UgZ;XoYjMZGt|+*doNlP;EMPsVbZ(uG7jesY(}A zyNGk!iH$-vksBBgH;YpSh&ejiIeDlsPm~r-#=cObxp9qPV4J29M~h5syuOZ<`t+`= zk~BxmB0-NwTB$6G1=CJ$Qy|01dum*cFwXP7xy0r14nI{dX&Y-c9X@t5>XEidivA*x zPAHi^n7sAV2}HK}$e0aN;MSvAQF|smFCnL@v02FL&Z1#l zzT6A{GVM;@+jd9RfVgdh+ThKFBH=h`GTf3gSV&6T*!!yp>3Z1sk}^Wo!+|orKA2vP z-R#?PjN?#M4D+z!ST${4sNu`yDU?42iDu+bl5m8$#G(rl`8;-zcWC4Z_TqjqmCQ4) z(2}=2&f$le*qLsNB+^hv*W2op1lA^3lwdTZ5rmU)wrvOlrzO}WghLIfMB>=~;0CxorkhD7hFG!s6N;7^S5w)FJnl~-j!A*4j3x~_tF7foK9sRTH?q{qy9!e|7i z6B6T*b~I;l$Hs|*A9aM+$%%!nO4no?QRU8HPWM!|rSn6
    aZ`eaSaR->kkYOW?O zG0<-)QG~`&{2+~2Yvr*-pPY|}jbM(r&s!3dU9_OOS(+^FRL_$6a7`5z?G6Hw`cu@^ z*YoD^pG&i>Hz6v?g_SeeS`@8g@>oRrM~kq;cEiX|lBl57d5(>e2sZ8AF-naS8Zwti zRQ@qSb+Psu*_WY&i_W*$k-u{+Mv83&gF}d&`$1ePx1S`v1Fs(@3C!l=3>?@Q$+rYF zCLpJ-4aZMf8ag69k;jyTOVb!q3X*4OPYx1N2H?mexl&@GPj=9pXT6p2z-~o5L7D~2 z`GD_MDkPH*)HQjw==&vpz;^4|bd4)_O;m1xEk`#f>3f^?KI~$1o;k`TM{B(6%6uc6 zoXOG)5OMuPRDarJ4!W9AQXEo%)la!a8`U2#7F~Tl<>xb}>u1dp^igHmWjb*SCacd<+)ApOQTv-VubhdCOvm+Oq*9nL>G)fBaL)l% zemz)C)Xsxyx^@7G9Q(;Sv8Ed2ECU(q^G3J1f2RKRGJna_GpUIvV)r!mG^Q*6LkVwl zQ(ITTpYSKw5pe+5Qq5D$3lncF;i-jX5SdIXf~4=_;+_<>HZx&pS6@rintNh0vQ?}v zLu`^QCChI})eTPaI29k|xQ!l@LJ%e|%u-4z|3qOHQ$-msyRuEcwC`8*db@JoSxR}% zU~}y%x>rQn7fDbFUy3VZr z8pmaav~6RmP#ObDbn?lO(weSRAFYzKB9Z2SXx>MHlzT#PWeiZ|#&%J})fDul+ozH{ zy;lr618l`s-uHdIAWOJ>^{SMboiwq7{y;`--2q7Lzx~r;?LQbZ=nwi+M*0IP67IeH`tQv8~CCxGuWwj5m!g_l=f!3Bvx0+F)R+dMP!MkP%& zRoRx4hg*%5>8gr7Vo?rGfnM!pBHN%Ye~&C9VwEnvq{G!~DQ(I1_mGYPVIWKvZk8uk zORHDuSTun+oXnnOt5U|651;as1T{6epHH@;aj~QG{Q8oFMwZ_`_D1^`++^6l@NU7M z+-t=5BqO?!)+E&JrY+TTy#{6uTZk~8qahTe5|T?P9aUTqU34<*UE;~@gTXV9r;lNd z5(&sfbTWA4Ad5BB9IrIU#n@c78Zs2!jBqpp2yKPDxC-el`Nx|TN$FlsRf+cny(H;n z^%dBa8OC)C)?Kh+D&5T-MS^%T?TxoiBY5lT-0Ev_I&5(~TT@90!Na-Y{B#H#yBtXy zY!TTDk}?iKsjBRmwrY2TAiP=7_tyPR#LF!292{iW^SpKSa7+#1peR(-*6~jA5fsd! zeA(h@e(;uKJx86v-!a|<$Cp@YH>F8JcMjfQs~)MuTce*fNKF#cH#)0qg!~lFjXG(B zr12{IA3D$DMN?XN-VsXGvSus1ofAOS93iFyDy5Mo=*fP1@l}qH4BjCx)P_0C5Ajh1 zr}*{ay^?X20Q>, YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-08-16 15:54-0300\n" +"PO-Revision-Date: 2019-08-16 16:14-0300\n" +"Language: es\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"Last-Translator: \n" +"Language-Team: \n" +"X-Generator: Poedit 2.2.1\n" + +#: evennia/accounts/accounts.py:784 +msgid "Account being deleted." +msgstr "Cuenta siendo eliminada." + +#: evennia/commands/cmdhandler.py:680 +msgid "There were multiple matches." +msgstr "Hubo varias coincidencias." + +#: evennia/commands/cmdhandler.py:703 +#, python-format +msgid "Command '%s' is not available." +msgstr "El comando '%s' no está disponible." + +#: evennia/commands/cmdhandler.py:708 +#, python-format +msgid " Maybe you meant %s?" +msgstr " ¿Quizás quisiste decir %s?" + +#: evennia/commands/cmdhandler.py:708 +msgid "or" +msgstr "o" + +#: evennia/commands/cmdhandler.py:710 +msgid " Type \"help\" for help." +msgstr " Escribe \"ayuda\" para obtener ayuda." + +#: evennia/commands/cmdsethandler.py:89 +#, python-brace-format +msgid "" +"{traceback}\n" +"Error loading cmdset '{path}'\n" +"(Traceback was logged {timestamp})" +msgstr "" +"{traceback}\n" +"Error cargando cmdset '{path}'\n" +"(Traceback fue guardado en {timestamp})" + +#: evennia/commands/cmdsethandler.py:94 +#, python-brace-format +msgid "" +"Error loading cmdset: No cmdset class '{classname}' in '{path}'.\n" +"(Traceback was logged {timestamp})" +msgstr "" +"Error cargando cmdset: No existe cmdset con la clase '{classname}' en " +"'{path}'.\n" +"(Traceback guardado en {timestamp})" + +#: evennia/commands/cmdsethandler.py:98 +#, python-brace-format +msgid "" +"{traceback}\n" +"SyntaxError encountered when loading cmdset '{path}'.\n" +"(Traceback was logged {timestamp})" +msgstr "" +"{traceback}\n" +"SyntaxError encontrado al cargar cmdset '{path}'.\n" +"(Traceback guardado en {timestamp})" + +#: evennia/commands/cmdsethandler.py:103 +#, python-brace-format +msgid "" +"{traceback}\n" +"Compile/Run error when loading cmdset '{path}'.\",\n" +"(Traceback was logged {timestamp})" +msgstr "" +"{traceback}\n" +"Error al compilar/ejecutar cmdset '{path}'.\",\n" +"(Traceback guardado en {timestamp})" + +#: evennia/commands/cmdsethandler.py:108 +#, python-brace-format +msgid "" +"\n" +"Error encountered for cmdset at path '{path}'.\n" +"Replacing with fallback '{fallback_path}'.\n" +msgstr "" +"\n" +"Error encontrado para el cmdset en la dirección '{path}'.\n" +"Reemplazando con '{fallback_path}'.\n" + +#: evennia/commands/cmdsethandler.py:114 +#, python-brace-format +msgid "Fallback path '{fallback_path}' failed to generate a cmdset." +msgstr "El fallback '{fallback_path}' falló al generar un cmdset." + +#: evennia/commands/cmdsethandler.py:182 evennia/commands/cmdsethandler.py:192 +#, python-format +msgid "" +"\n" +"(Unsuccessfully tried '%s')." +msgstr "" +"\n" +"(Intentado sin sucesso '%s')." + +#: evennia/commands/cmdsethandler.py:311 +#, python-brace-format +msgid "custom {mergetype} on cmdset '{cmdset}'" +msgstr "{mergetype} personalizado en cmdset '{cmdset}'" + +#: evennia/commands/cmdsethandler.py:314 +#, python-brace-format +msgid " : {current}" +msgstr " : {current}" + +#: evennia/commands/cmdsethandler.py:322 +#, python-brace-format +msgid "" +" <{key} ({mergetype}, prio {prio}, {permstring})>:\n" +" {keylist}" +msgstr "" +" <{key} ({mergetype}, prio {prio}, {permstring})>:\n" +" {keylist}" + +#: evennia/commands/cmdsethandler.py:426 +msgid "Only CmdSets can be added to the cmdsethandler!" +msgstr "¡Sólo CmdSets pueden ser añadidos a cmdsethandler!" + +#: evennia/comms/channelhandler.py:100 +msgid "Say what?" +msgstr "¿Qué dijiste?" + +#: evennia/comms/channelhandler.py:105 +#, python-format +msgid "Channel '%s' not found." +msgstr "Canal '%s' no encontrado." + +#: evennia/comms/channelhandler.py:108 +#, python-format +msgid "You are not connected to channel '%s'." +msgstr "No estás conectado al canal '%s'." + +#: evennia/comms/channelhandler.py:112 +#, python-format +msgid "You are not permitted to send to channel '%s'." +msgstr "No tienes permitido enviar al canal '%s'." + +#: evennia/comms/channelhandler.py:155 +msgid " (channel)" +msgstr " (canal)" + +#: evennia/locks/lockhandler.py:236 +#, python-format +msgid "Lock: lock-function '%s' is not available." +msgstr "Bloqueo: función de bloqueo '%s' no está disponible." + +#: evennia/locks/lockhandler.py:249 +#, python-format +msgid "Lock: definition '%s' has syntax errors." +msgstr "Bloqueo: la definición '%s' tiene errores de sintaxis." + +#: evennia/locks/lockhandler.py:253 +#, python-format +msgid "" +"LockHandler on %(obj)s: access type '%(access_type)s' changed from " +"'%(source)s' to '%(goal)s' " +msgstr "" +"LockHandler en %(obj)s: tipo de acceso '%(access_type)s' cambiado " +"de'%(source)s' to '%(goal)s' " + +#: evennia/locks/lockhandler.py:320 +#, python-brace-format +msgid "Lock: '{lockdef}' contains no colon (:)." +msgstr "Bloqueo: '{lockdef}' no tiene dos puntos (:)." + +#: evennia/locks/lockhandler.py:328 +#, python-brace-format +msgid "Lock: '{lockdef}' has no access_type (left-side of colon is empty)." +msgstr "" +"Bloqueo: '{lockdef}' no tiene access_type (el lado de la izquierda de los " +"dos puntos está vacío)." + +#: evennia/locks/lockhandler.py:336 +#, python-brace-format +msgid "Lock: '{lockdef}' has mismatched parentheses." +msgstr "Bloqueo: '{lockdef}' tiene paréntesis que no se relacionan." + +#: evennia/locks/lockhandler.py:343 +#, python-brace-format +msgid "Lock: '{lockdef}' has no valid lock functions." +msgstr "Bloqueo: '{lockdef}' no tiene funciones de bloqueo válidas." + +#: evennia/objects/objects.py:744 +#, python-format +msgid "Couldn't perform move ('%s'). Contact an admin." +msgstr "No se puede realizar el movimiento ('%s'). Contacta a un admin." + +#: evennia/objects/objects.py:754 +msgid "The destination doesn't exist." +msgstr "El destino no existe." + +#: evennia/objects/objects.py:845 +#, python-format +msgid "Could not find default home '(#%d)'." +msgstr "No se puede encontrar el hogar por defecto '(#%d)'." + +#: evennia/objects/objects.py:861 +msgid "Something went wrong! You are dumped into nowhere. Contact an admin." +msgstr "¡Algo salió mal! Estás en la nada. Contacta a un admin." + +#: evennia/objects/objects.py:1003 +#, python-format +msgid "Your character %s has been destroyed." +msgstr "Tu personaje %s ha sido destruído." + +#: evennia/scripts/scripthandler.py:53 +#, python-format +msgid "" +"\n" +" '%(key)s' (%(next_repeat)s/%(interval)s, %(repeats)s repeats): %(desc)s" +msgstr "" +"\n" +" '%(key)s' (%(next_repeat)s/%(interval)s, %(repeats)s repite): %(desc)s" + +#: evennia/scripts/scripts.py:198 +#, python-format +msgid "" +"Script %(key)s(#%(dbid)s) of type '%(cname)s': at_repeat() error '%(err)s'." +msgstr "" +"El script %(key)s(#%(dbid)s) del tipo '%(cname)s': at_repeat() dio error " +"'%(err)s'." + +#: evennia/server/initial_setup.py:28 +msgid "" +"\n" +"Welcome to your new |wEvennia|n-based game! Visit http://www.evennia.com if " +"you need\n" +"help, want to contribute, report issues or just join the community.\n" +"As Account #1 you can create a demo/tutorial area with |w@batchcommand " +"tutorial_world.build|n.\n" +" " +msgstr "" +"\n" +"Bienvenid@ a tu nuevo |wJuego basado en Evennia! Visita http://www.evennia." +"com si necesitas\n" +"ayuda, quieres contribuir, reportar un error o simplemente unirte a la " +"comunidad.\n" +"Como cuenta #1 puedes crear un área de demostración/tutorial con |" +"w@batchcommand tutorial_world.build|n.\n" +" " + +#: evennia/server/initial_setup.py:92 +msgid "This is User #1." +msgstr "Este es el Usuario #1." + +#: evennia/server/initial_setup.py:105 +msgid "Limbo" +msgstr "Limbo" + +#: evennia/server/server.py:141 +msgid "idle timeout exceeded" +msgstr "tiempo sin actividad excedido" + +#: evennia/server/sessionhandler.py:392 +msgid " ... Server restarted." +msgstr " ... El servidor se reinició." + +#: evennia/server/sessionhandler.py:620 +msgid "Logged in from elsewhere. Disconnecting." +msgstr "Sesión iniciada desde otro lugar. Desconectando." + +#: evennia/server/sessionhandler.py:648 +msgid "Idle timeout exceeded, disconnecting." +msgstr "Tiempo sin actividad excedido, desconectando." + +#: evennia/server/validators.py:29 +msgid "Sorry, that username is reserved." +msgstr "Perdón, ese nombre de usuario ya está reservado." + +#: evennia/server/validators.py:37 +msgid "Sorry, that username is already taken." +msgstr "Perdón, ese nombre de usuario ya está siendo usado." + +#: evennia/server/validators.py:89 +#, python-format +msgid "" +"%s From a terminal client, you can also use a phrase of multiple words if " +"you enclose the password in double quotes." +msgstr "" +"%s Desde un cliente de terminal, puedes usar una frase con múltiples " +"palabras si encierras la contraseña en comillas dobles." + +#: evennia/utils/evmenu.py:192 +#, python-brace-format +msgid "" +"Menu node '{nodename}' is either not implemented or caused an error. Make " +"another choice." +msgstr "" +"Nodo del menú '{nodename}' no está implementado o causó un error. " +"Selecciona otra opción." + +#: evennia/utils/evmenu.py:194 +#, python-brace-format +msgid "Error in menu node '{nodename}'." +msgstr "Error en el nodo del menú '{nodename}'." + +#: evennia/utils/evmenu.py:195 +msgid "No description." +msgstr "Sin descripción." + +#: evennia/utils/evmenu.py:196 +msgid "Commands: , help, quit" +msgstr "Comandos: , help, quit" + +#: evennia/utils/evmenu.py:197 +msgid "Commands: , help" +msgstr "Comandos: , help" + +#: evennia/utils/evmenu.py:198 +msgid "Commands: help, quit" +msgstr "Comandos: help, quit" + +#: evennia/utils/evmenu.py:199 +msgid "Commands: help" +msgstr "Comandos: help" + +#: evennia/utils/evmenu.py:200 +msgid "Choose an option or try 'help'." +msgstr "Elige una opción o intenta 'help'." + +#: evennia/utils/utils.py:1877 +#, python-format +msgid "Could not find '%s'." +msgstr "No se pudo encontrar '%s'." + +#: evennia/utils/utils.py:1884 +#, python-format +msgid "More than one match for '%s' (please narrow target):\n" +msgstr "Más de una coincidencia para '%s' (por favor sé más específico):\n" From 0ec85aa47fa349dbdbd2e85c14438c81315ca0c0 Mon Sep 17 00:00:00 2001 From: Brenden Tuck Date: Sun, 18 Aug 2019 19:49:58 -0400 Subject: [PATCH 013/164] fix shift-up focus issue and enter-key always clearing focus --- .../static/webclient/js/plugins/default_in.js | 14 ++++++++++++-- .../static/webclient/js/plugins/history.js | 3 +++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/evennia/web/webclient/static/webclient/js/plugins/default_in.js b/evennia/web/webclient/static/webclient/js/plugins/default_in.js index 56e6dc6c8aa..cfde4120438 100644 --- a/evennia/web/webclient/static/webclient/js/plugins/default_in.js +++ b/evennia/web/webclient/static/webclient/js/plugins/default_in.js @@ -34,8 +34,18 @@ let defaultin_plugin = (function () { } inputfield.val(""); // Clear this inputfield event.preventDefault(); - } - inputfield.blur(); + + // enter key by itself should toggle focus + if( inputfield.length < 1 ) { + inputfield = $(".inputfield:last"); + inputfield.focus(); + if( inputfield.length < 1 ) { // non-goldenlayout backwards compatibility + $("#inputfield").focus(); + } + } else { + inputfield.blur(); + } + } // else allow building a multi-line input command break; // Anything else, focus() a textarea if needed, and allow the default event diff --git a/evennia/web/webclient/static/webclient/js/plugins/history.js b/evennia/web/webclient/static/webclient/js/plugins/history.js index b6130a235d0..2aa6f0991f4 100644 --- a/evennia/web/webclient/static/webclient/js/plugins/history.js +++ b/evennia/web/webclient/static/webclient/js/plugins/history.js @@ -63,6 +63,9 @@ let history = (function () { // Doing a history navigation; replace the text in the input and // move the cursor to the end of the new value var inputfield = $(".inputfield:focus"); + if( inputfield.length < 1 ) { // focus the default (last), if nothing focused + inputfield = $(".inputfield:last"); + } if( inputfield.length < 1 ) { // pre-goldenlayout backwards compatibility inputfield = $("#inputfield"); } From 705592e753f56d6c5330bf2345d431a4296390b6 Mon Sep 17 00:00:00 2001 From: Griatch Date: Mon, 19 Aug 2019 08:11:51 +0200 Subject: [PATCH 014/164] Update CHANGELOG with Spanish translation --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b94758ab75f..291bcb04cda 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ without arguments starts a full interactive Python console. - Webclient fix of prompt display - Webclient multimedia support for relaying images, video and sounds via `.msg(image=URL)`, `.msg(video=URL)` and `.msg(audio=URL)` +- Add Spanish translation (fermuch) ## Evennia 0.9 (2018-2019) From 3da8ad6d287dced12423b93a6b7ae71e40e08a4d Mon Sep 17 00:00:00 2001 From: Alessandro Ogier Date: Wed, 21 Aug 2019 18:10:29 +0200 Subject: [PATCH 015/164] fix ssh server --- evennia/server/portal/ssh.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/evennia/server/portal/ssh.py b/evennia/server/portal/ssh.py index 7701cc9a0d6..f1cb8242297 100644 --- a/evennia/server/portal/ssh.py +++ b/evennia/server/portal/ssh.py @@ -490,8 +490,8 @@ def chainProtocolFactory(username=None): try: # create/get RSA keypair publicKey, privateKey = getKeyPair(_PUBLIC_KEY_FILE, _PRIVATE_KEY_FILE) - factory.publicKeys = {'ssh-rsa': publicKey} - factory.privateKeys = {'ssh-rsa': privateKey} + factory.publicKeys = {b'ssh-rsa': publicKey} + factory.privateKeys = {b'ssh-rsa': privateKey} except Exception as err: print(_NO_AUTOGEN.format(err=err)) From 5d54c6c54ae279aa1472ada876c10c24f60877bc Mon Sep 17 00:00:00 2001 From: Alessandro Ogier Date: Wed, 21 Aug 2019 22:47:08 +0200 Subject: [PATCH 016/164] do not duplicate whispers --- evennia/commands/default/general.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evennia/commands/default/general.py b/evennia/commands/default/general.py index a6200549ff4..bc61a366193 100644 --- a/evennia/commands/default/general.py +++ b/evennia/commands/default/general.py @@ -574,7 +574,7 @@ def func(self): receivers = [recv.strip() for recv in self.lhs.split(",")] - receivers = [caller.search(receiver) for receiver in receivers] + receivers = [caller.search(receiver) for receiver in set(receivers)] receivers = [recv for recv in receivers if recv] speech = self.rhs From 1afc6e31c8943d7e6a885dccc2b67d9e7ea8173f Mon Sep 17 00:00:00 2001 From: Alessandro Ogier Date: Thu, 22 Aug 2019 18:19:38 +0200 Subject: [PATCH 017/164] fix ssh import error message --- evennia/server/portal/ssh.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evennia/server/portal/ssh.py b/evennia/server/portal/ssh.py index f1cb8242297..5a0e573614b 100644 --- a/evennia/server/portal/ssh.py +++ b/evennia/server/portal/ssh.py @@ -20,7 +20,7 @@ _SSH_IMPORT_ERROR = """ ERROR: Missing crypto library for SSH. Install it with - pip install cryptography pyasn1 + pip install cryptography pyasn1 bcrypt (On older Twisted versions you may have to do 'pip install pycrypto pyasn1' instead). From 7531c1217c2cbab6993426801dd2d3ae74ff9641 Mon Sep 17 00:00:00 2001 From: Alessandro Ogier Date: Thu, 22 Aug 2019 19:20:25 +0200 Subject: [PATCH 018/164] no longer needed --- evennia/settings_default.py | 1 - 1 file changed, 1 deletion(-) diff --git a/evennia/settings_default.py b/evennia/settings_default.py index 6c69eabcacf..e1586ac7973 100644 --- a/evennia/settings_default.py +++ b/evennia/settings_default.py @@ -12,7 +12,6 @@ always be sure of what you have changed and what is default behaviour. """ -from builtins import range from django.contrib.messages import constants as messages from django.urls import reverse_lazy From 3d842e8a92de00e0c5d6f8bfde3a9e9df5dc44f9 Mon Sep 17 00:00:00 2001 From: Griatch Date: Sat, 24 Aug 2019 16:55:46 +0200 Subject: [PATCH 019/164] Change tutorial typeclass to Tutorial Readable and TutorialClimbable. Improve error report for failed typeclass import. --- evennia/accounts/accounts.py | 2 +- evennia/contrib/tests.py | 18 +++++++++--------- evennia/contrib/tutorial_world/build.ev | 8 ++++---- evennia/contrib/tutorial_world/objects.py | 8 ++++---- evennia/utils/utils.py | 13 +++++++++---- 5 files changed, 27 insertions(+), 22 deletions(-) diff --git a/evennia/accounts/accounts.py b/evennia/accounts/accounts.py index caf6b5d5a06..d1b5749dd99 100644 --- a/evennia/accounts/accounts.py +++ b/evennia/accounts/accounts.py @@ -702,7 +702,7 @@ def create(cls, *args, **kwargs): errors.append(string) return None, errors - # everything's ok. Create the new account account. + # everything's ok. Create the new account. try: try: account = create.create_account(username, email, password, permissions=permissions, typeclass=typeclass) diff --git a/evennia/contrib/tests.py b/evennia/contrib/tests.py index 3293cab4e1c..e8651199cf3 100644 --- a/evennia/contrib/tests.py +++ b/evennia/contrib/tests.py @@ -253,7 +253,7 @@ def test_return_detail(self): def test_cmdextendedlook(self): rid = self.room1.id - self.call(extended_room.CmdExtendedRoomLook(), "here", + self.call(extended_room.CmdExtendedRoomLook(), "here", "Room(#{})\n{}".format(rid, self.SPRING_DESC)) self.call(extended_room.CmdExtendedRoomLook(), "testdetail", self.DETAIL_DESC) self.call(extended_room.CmdExtendedRoomLook(), "nonexistent", "Could not find 'nonexistent'.") @@ -726,7 +726,7 @@ def test_mail(self): self.call(mail.CmdMail(), "2", "'2' is not a valid mail id.", caller=self.account) self.call(mail.CmdMail(), "test", "'test' is not a valid mail id.", caller=self.account) self.call(mail.CmdMail(), "", "There are no messages in your inbox.", caller=self.account) - self.call(mail.CmdMailCharacter(), "Char=Message 1", + self.call(mail.CmdMailCharacter(), "Char=Message 1", "You have received a new @mail from Char|You sent your message.", caller=self.char1) self.call(mail.CmdMailCharacter(), "Char=Message 2", "You sent your message.", caller=self.char2) self.call(mail.CmdMail(), "TestAccount2=Message 2", @@ -881,13 +881,13 @@ def test_tutorialobj(self): self.assertEqual(obj1.location, obj1.home) def test_readable(self): - readable = create_object(tutobjects.Readable, key="book", location=self.room1) + readable = create_object(tutobjects.TutorialReadable, key="book", location=self.room1) readable.db.readable_text = "Text to read" self.call(tutobjects.CmdRead(), "book", "You read book:\n Text to read", obj=readable) def test_climbable(self): - climbable = create_object(tutobjects.Climbable, key="tree", location=self.room1) + climbable = create_object(tutobjects.TutorialClimbable, key="tree", location=self.room1) self.call(tutobjects.CmdClimb(), "tree", "You climb tree. Having looked around, you climb down again.", obj=climbable) self.assertEqual(self.char1.tags.get("tutorial_climbed_tree", category="tutorial_world"), "tutorial_climbed_tree") @@ -923,7 +923,7 @@ def test_crumblingwall(self): # we patch out the delay, so these are closed immediately self.assertFalse(wall.db.button_exposed) self.assertFalse(wall.db.exit_open) - + def test_weapon(self): weapon = create_object(tutobjects.Weapon, key="sword", location=self.char1) self.call(tutobjects.CmdAttack(), "Char", "You stab with sword.", obj=weapon, cmdstring="stab") @@ -1759,15 +1759,15 @@ def setUp(self): super(TestPuzzles, self).setUp() self.steel = create_object( self.object_typeclass, - key='steel', + key='steel', location=self.char1.location) self.flint = create_object( self.object_typeclass, - key='flint', + key='flint', location=self.char1.location) self.fire = create_object( self.object_typeclass, - key='fire', + key='fire', location=self.char1.location) self.steel.tags.add('tag-steel') self.steel.tags.add('tag-steel', category='tagcat') @@ -2045,7 +2045,7 @@ def _puzzleedit(swt, dbref, args, expmsg): _puzzleedit('', '1', '', "A puzzle recipe's #dbref must be specified.\nUsage: @puzzleedit") _puzzleedit('', '', '', "A puzzle recipe's #dbref must be specified.\nUsage: @puzzleedit") _puzzleedit('', recipe_dbref, 'dummy', "A puzzle recipe's #dbref must be specified.\nUsage: @puzzleedit") - _puzzleedit('', self.script.dbref, '', + _puzzleedit('', self.script.dbref, '', 'Script(#{}) is not a puzzle'.format(sid)) # edit use_success_message and use_success_location_message diff --git a/evennia/contrib/tutorial_world/build.ev b/evennia/contrib/tutorial_world/build.ev index df454ad9375..8780400e6c8 100644 --- a/evennia/contrib/tutorial_world/build.ev +++ b/evennia/contrib/tutorial_world/build.ev @@ -265,7 +265,7 @@ start you get a customized error message when trying to pick it up (that is checked and echoed by the 'get' command). # -@create/drop Wooden sign;sign : tutorial_world.objects.Readable +@create/drop Wooden sign;sign : tutorial_world.objects.TutorialReadable # @desc sign = The wooden sign sits at the end of a small eastward path. Beyond it @@ -287,14 +287,14 @@ start # @set sign/tutorial_info = This is a readable object, of the Typeclass - evennia.contrib.tutorial_world.objects.Readable. The sign has a cmdset + evennia.contrib.tutorial_world.objects.TutorialReadable. The sign has a cmdset defined on itself, containing only one command, namely 'read'. This command is what allows you to 'read sign'. Doing so returns the contents of the Attribute 'readable_sign', containing the information on the sign. # Set a climbable object for discovering a hidden exit # -@create/drop gnarled old trees;tree;trees;gnarled : tutorial_world.objects.Climbable +@create/drop gnarled old trees;tree;trees;gnarled : tutorial_world.objects.TutorialClimbable # @desc trees = Only the sturdiest of trees survive at the edge of the moor. A small group of huddling black things has dug in near the @@ -310,7 +310,7 @@ start @set trees/tutorial_info = These are climbable objects; they make for a small puzzle for accessing a hidden exit. Climbing the trees allows the - Climbable typeclass to assign an Attribute on the character + TutorialClimbable typeclass to assign an Attribute on the character that an exit is then looking for. # # The text to echo to player if trying 'climb tree'. What diff --git a/evennia/contrib/tutorial_world/objects.py b/evennia/contrib/tutorial_world/objects.py index 85eceb9e14d..7a9fe096ba6 100644 --- a/evennia/contrib/tutorial_world/objects.py +++ b/evennia/contrib/tutorial_world/objects.py @@ -9,8 +9,8 @@ TutorialObject -Readable -Climbable +TutorialReadable +TutorialClimbable Obelisk LightSource CrumblingWall @@ -113,7 +113,7 @@ def at_cmdset_creation(self): self.add(CmdRead()) -class Readable(TutorialObject): +class TutorialReadable(TutorialObject): """ This simple object defines some attributes and """ @@ -183,7 +183,7 @@ def at_cmdset_creation(self): self.add(CmdClimb()) -class Climbable(TutorialObject): +class TutorialClimbable(TutorialObject): """ A climbable object. All that is special about it is that it has the "climb" command available on it. diff --git a/evennia/utils/utils.py b/evennia/utils/utils.py index 86f385c7c41..6f0685a3ab6 100644 --- a/evennia/utils/utils.py +++ b/evennia/utils/utils.py @@ -17,6 +17,7 @@ import textwrap import random import inspect +import traceback from twisted.internet.task import deferLater from twisted.internet.defer import returnValue # noqa - used as import target from os.path import join as osjoin @@ -1453,8 +1454,10 @@ class (Class): An uninstatiated class recovered from path. """ cls = None + err = "" if defaultpaths: - paths = [path] + ["%s.%s" % (dpath, path) for dpath in make_iter(defaultpaths)] if defaultpaths else [] + paths = [path] + ["%s.%s" % (dpath, path) + for dpath in make_iter(defaultpaths)] if defaultpaths else [] else: paths = [path] @@ -1473,6 +1476,7 @@ class (Class): An uninstatiated class recovered from path. try: mod = import_module(testpath, package='evennia') except ModuleNotFoundError: + err = traceback.format_exc(30) break try: @@ -1481,10 +1485,11 @@ class (Class): An uninstatiated class recovered from path. except AttributeError: if len(trace()) > 2: # AttributeError within the module, don't hide it - exc = sys.exc_info() - raise_(exc[1], None, exc[2]) + err = traceback.format_exc(30) + break if not cls: - err = "Could not load typeclass '%s'" % path + err = "\nCould not load typeclass '{}'{}".format( + path, " with the following traceback:\n" + err if err else "") if defaultpaths: err += "\nPaths searched:\n %s" % "\n ".join(paths) else: From c7ccc847be41799ee1855e5d35a48c973e713bb2 Mon Sep 17 00:00:00 2001 From: Griatch Date: Mon, 26 Aug 2019 08:35:50 +0200 Subject: [PATCH 020/164] Add commands missing from `account.__all__`. Resolves #1900. --- evennia/commands/default/account.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evennia/commands/default/account.py b/evennia/commands/default/account.py index 1a54bbc131e..97b229bd1c9 100644 --- a/evennia/commands/default/account.py +++ b/evennia/commands/default/account.py @@ -34,7 +34,7 @@ # limit symbol import for API __all__ = ("CmdOOCLook", "CmdIC", "CmdOOC", "CmdPassword", "CmdQuit", "CmdCharCreate", "CmdOption", "CmdSessions", "CmdWho", - "CmdColorTest", "CmdQuell") + "CmdColorTest", "CmdQuell", "CmdCharDelete", "CmdStyle") class MuxAccountLookCommand(COMMAND_DEFAULT_CLASS): From d5a09c5e65b042bbbba1b6850e9f7425f55874cb Mon Sep 17 00:00:00 2001 From: Griatch Date: Mon, 26 Aug 2019 08:44:25 +0200 Subject: [PATCH 021/164] Fix regression error on one-character input. Resolves #1901 --- evennia/commands/cmdparser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evennia/commands/cmdparser.py b/evennia/commands/cmdparser.py index 5bbc12878b6..091f1798a73 100644 --- a/evennia/commands/cmdparser.py +++ b/evennia/commands/cmdparser.py @@ -72,9 +72,9 @@ def build_matches(raw_string, cmdset, include_prefixes=False): cmd.arg_regex.match(l_raw_string[len(cmdname):]))]) else: # strip prefixes set in settings + l_raw_string = raw_string.lower() raw_string = (raw_string.lstrip(_CMD_IGNORE_PREFIXES) if len(raw_string) > 1 else l_raw_string) - l_raw_string = raw_string.lower() for cmd in cmdset: for raw_cmdname in [cmd.key] + cmd.aliases: cmdname = (raw_cmdname.lstrip(_CMD_IGNORE_PREFIXES) From 6f3949d67a6119d2f84d9a4f55599987ac98a89e Mon Sep 17 00:00:00 2001 From: Griatch Date: Mon, 26 Aug 2019 20:03:38 +0200 Subject: [PATCH 022/164] Fix that caused whisper to never allow overriding text --- evennia/objects/objects.py | 1 - 1 file changed, 1 deletion(-) diff --git a/evennia/objects/objects.py b/evennia/objects/objects.py index d908cc0b66d..a8c5060ee0d 100644 --- a/evennia/objects/objects.py +++ b/evennia/objects/objects.py @@ -1846,7 +1846,6 @@ def at_say(self, message, msg_self=None, msg_location=None, # whisper mode msg_type = 'whisper' msg_self = '{self} whisper to {all_receivers}, "{speech}"' if msg_self is True else msg_self - msg_receivers = '{object} whispers: "{speech}"' msg_receivers = msg_receivers or '{object} whispers: "{speech}"' msg_location = None else: From 758c365c9c864182423bd64503905e8b2c6fd57c Mon Sep 17 00:00:00 2001 From: Griatch Date: Mon, 26 Aug 2019 20:12:18 +0200 Subject: [PATCH 023/164] Fix typo in objects command. Resolve #1892. --- evennia/commands/default/system.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evennia/commands/default/system.py b/evennia/commands/default/system.py index 268fa66d096..c7248f11764 100644 --- a/evennia/commands/default/system.py +++ b/evennia/commands/default/system.py @@ -528,7 +528,7 @@ def func(self): totaltable.align = 'l' totaltable.add_row("Characters", "(BASE_CHARACTER_TYPECLASS + children)", nchars, "%.2f" % ((float(nchars) / nobjs) * 100)) - totaltable.add_row("Rooms", "(BASE_ROOM_TYPECKLASS + children)", + totaltable.add_row("Rooms", "(BASE_ROOM_TYPECLASS + children)", nrooms, "%.2f" % ((float(nrooms) / nobjs) * 100)) totaltable.add_row("Exits", "(BASE_EXIT_TYPECLASS + children)", nexits, "%.2f" % ((float(nexits) / nobjs) * 100)) From d3192431956d0ccfedaa5ffed1c96e8918c6f640 Mon Sep 17 00:00:00 2001 From: Griatch Date: Wed, 28 Aug 2019 20:07:03 +0200 Subject: [PATCH 024/164] Forbid /media and /static file browsing. Resolve #1746. --- evennia/server/server.py | 6 +++--- evennia/server/webserver.py | 13 ++++++++++++- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/evennia/server/server.py b/evennia/server/server.py index 8b269674f0e..ba9df8add6c 100644 --- a/evennia/server/server.py +++ b/evennia/server/server.py @@ -546,7 +546,7 @@ def at_server_cold_stop(self): # Start a django-compatible webserver. - from evennia.server.webserver import DjangoWebRoot, WSGIWebServer, Website, LockableThreadPool + from evennia.server.webserver import DjangoWebRoot, WSGIWebServer, Website, LockableThreadPool, PrivateStaticRoot # start a thread pool and define the root url (/) as a wsgi resource # recognized by Django @@ -555,9 +555,9 @@ def at_server_cold_stop(self): web_root = DjangoWebRoot(threads) # point our media resources to url /media - web_root.putChild(b"media", static.File(settings.MEDIA_ROOT)) + web_root.putChild(b"media", PrivateStaticRoot(settings.MEDIA_ROOT)) # point our static resources to url /static - web_root.putChild(b"static", static.File(settings.STATIC_ROOT)) + web_root.putChild(b"static", PrivateStaticRoot(settings.STATIC_ROOT)) EVENNIA.web_root = web_root if WEB_PLUGINS_MODULE: diff --git a/evennia/server/webserver.py b/evennia/server/webserver.py index 884daabfe75..4a8ff58ed51 100644 --- a/evennia/server/webserver.py +++ b/evennia/server/webserver.py @@ -14,7 +14,7 @@ """ import urllib.parse from urllib.parse import quote as urlquote -from twisted.web import resource, http, server +from twisted.web import resource, http, server, static from twisted.internet import reactor from twisted.application import internet from twisted.web.proxy import ReverseProxyResource @@ -268,3 +268,14 @@ def stopService(self): """ super().stopService() self.pool.stop() + + +class PrivateStaticRoot(static.File): + """ + This overrides the default static file resource so as to not make the + directory listings public (that is, if you go to /media or /static you + won't see an index of all static/media files on the server). + + """ + def directoryListing(self): + return resource.ForbiddenResource() From c48b9d645ae3776b6d3f623d6f5d59cc90fd7d05 Mon Sep 17 00:00:00 2001 From: Griatch Date: Wed, 28 Aug 2019 20:11:35 +0200 Subject: [PATCH 025/164] Another fix to cmdparser --- evennia/commands/cmdparser.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/evennia/commands/cmdparser.py b/evennia/commands/cmdparser.py index 091f1798a73..f22a927ccc0 100644 --- a/evennia/commands/cmdparser.py +++ b/evennia/commands/cmdparser.py @@ -72,9 +72,9 @@ def build_matches(raw_string, cmdset, include_prefixes=False): cmd.arg_regex.match(l_raw_string[len(cmdname):]))]) else: # strip prefixes set in settings - l_raw_string = raw_string.lower() raw_string = (raw_string.lstrip(_CMD_IGNORE_PREFIXES) - if len(raw_string) > 1 else l_raw_string) + if len(raw_string) > 1 else raw_string) + l_raw_string = raw_string.lower() for cmd in cmdset: for raw_cmdname in [cmd.key] + cmd.aliases: cmdname = (raw_cmdname.lstrip(_CMD_IGNORE_PREFIXES) From a34fb250dc6b820058f8ce836bfa71c707dbc1cd Mon Sep 17 00:00:00 2001 From: Griatch Date: Wed, 28 Aug 2019 20:49:11 +0200 Subject: [PATCH 026/164] Avoid next-confusion when logging out from website. Resolve #1745. --- .../web/website/templates/website/base.html | 8 ++--- .../website/registration/logged_out.html | 33 ++++++++++++------- .../templates/website/registration/login.html | 16 ++++----- 3 files changed, 33 insertions(+), 24 deletions(-) diff --git a/evennia/web/website/templates/website/base.html b/evennia/web/website/templates/website/base.html index d465a17171d..0f9a122de50 100644 --- a/evennia/web/website/templates/website/base.html +++ b/evennia/web/website/templates/website/base.html @@ -30,7 +30,7 @@ {% block body %} - + {% include "website/_menu.html" %}
    @@ -43,10 +43,10 @@ {% endif %}
    @@ -59,7 +59,7 @@
{% endblock %} - + {% endblock %} diff --git a/evennia/web/website/templates/website/registration/logged_out.html b/evennia/web/website/templates/website/registration/logged_out.html index 2eadfd61ecc..fc96f21ca6c 100644 --- a/evennia/web/website/templates/website/registration/logged_out.html +++ b/evennia/web/website/templates/website/registration/logged_out.html @@ -1,25 +1,36 @@ {% extends "website/base.html" %} + {% block header_ext %} {% endblock %} {% block titleblock %}Logged Out{% endblock %} -{% block content %} -
-
-
-
-

Logged Out

-

You have been logged out.

-

Redirecting in 2 seconds...

+{% block body %} + +{% load addclass %} +
+
+
+
+
+

Logged Out

+
+
You have been logged out.
+
+
+
Redirecting in 2 seconds ...
+
+
- + + {% endblock %} + diff --git a/evennia/web/website/templates/website/registration/login.html b/evennia/web/website/templates/website/registration/login.html index dab2a82be20..df506c641c9 100644 --- a/evennia/web/website/templates/website/registration/login.html +++ b/evennia/web/website/templates/website/registration/login.html @@ -1,8 +1,6 @@ {% extends "website/base.html" %} -{% block titleblock %} -Login -{% endblock %} +{% block titleblock %}Login{% endblock %} {% block body %} @@ -22,34 +20,34 @@

Login

{% endif %} {% endif %} - + {% if not user.is_authenticated %}
{% csrf_token %} - +
{{ form.username | addclass:"form-control" }}
- +
{{ form.password | addclass:"form-control" }}
- +
- +
- + {% endif %}
From 083459a6e6b1c6c2e2a79166e8becd4a44ef0b53 Mon Sep 17 00:00:00 2001 From: Griatch Date: Wed, 28 Aug 2019 22:49:41 +0200 Subject: [PATCH 027/164] Fix bug in DefaultObject.at_traverse. Resolve #1832. --- evennia/objects/objects.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evennia/objects/objects.py b/evennia/objects/objects.py index a8c5060ee0d..9fe152513ea 100644 --- a/evennia/objects/objects.py +++ b/evennia/objects/objects.py @@ -2438,7 +2438,7 @@ def at_traverse(self, traversing_object, target_location, **kwargs): else: if self.db.err_traverse: # if exit has a better error message, let's use it. - self.caller.msg(self.db.err_traverse) + traversing_object.msg(self.db.err_traverse) else: # No shorthand error message. Call hook. self.at_failed_traverse(traversing_object) From 878b387ced6b2ef8b535dc74c72728b17c8bc551 Mon Sep 17 00:00:00 2001 From: Griatch Date: Wed, 28 Aug 2019 23:31:08 +0200 Subject: [PATCH 028/164] Make manual Script.pause state survive full shutdown. Resolve #1838. --- evennia/server/server.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/evennia/server/server.py b/evennia/server/server.py index ba9df8add6c..093d97f2052 100644 --- a/evennia/server/server.py +++ b/evennia/server/server.py @@ -382,7 +382,8 @@ def shutdown(self, mode='reload', _reactor_stopping=False): yield [(p.unpuppet_all(), p.at_server_shutdown()) for p in AccountDB.get_all_cached_instances()] yield ObjectDB.objects.clear_all_sessids() - yield [(s.pause(manual_pause=False), s.at_server_shutdown()) for s in ScriptDB.get_all_cached_instances()] + yield [(s.pause(manual_pause=s.attributes.get("_manual_pause", False)), + s.at_server_shutdown()) for s in ScriptDB.get_all_cached_instances()] ServerConfig.objects.conf("server_restart_mode", "reset") self.at_server_cold_stop() From 43d58158823ceca6d665c7f9b15c54bcf404a53e Mon Sep 17 00:00:00 2001 From: Griatch Date: Thu, 29 Aug 2019 00:10:46 +0200 Subject: [PATCH 029/164] Fix time format Windows does not support. Resolve #1881. Resolve #1880 --- evennia/contrib/mail.py | 10 ++++++---- evennia/utils/tests/test_utils.py | 17 +++++++++++++++++ evennia/utils/utils.py | 4 ++-- 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/evennia/contrib/mail.py b/evennia/contrib/mail.py index 9c458b8419a..7e9b8fafb59 100644 --- a/evennia/contrib/mail.py +++ b/evennia/contrib/mail.py @@ -87,7 +87,7 @@ class CmdMail(default_cmds.MuxAccountCommand): def parse(self): """ - Add convenience check to know if caller is an Account or not since this cmd + Add convenience check to know if caller is an Account or not since this cmd will be able to add to either Object- or Account level. """ @@ -276,7 +276,9 @@ def func(self): if message: messageForm.append(_HEAD_CHAR * _WIDTH) messageForm.append("|wFrom:|n %s" % (message.senders[0].get_display_name(self.caller))) - messageForm.append("|wSent:|n %s" % message.db_date_created.strftime("%b %-d, %Y - %H:%M:%S")) + # note that we cannot use %-d format here since Windows does not support it + day = message.db_date_created.day + messageForm.append("|wSent:|n %s" % message.db_date_created.strftime(f"%b {day}, %Y - %H:%M:%S")) messageForm.append("|wSubject:|n %s" % message.header) messageForm.append(_SUB_HEAD_CHAR * _WIDTH) messageForm.append(message.message) @@ -286,7 +288,7 @@ def func(self): message.tags.add("-", category="mail") else: - # list messages + # list messages messages = self.get_all_mail() if messages: @@ -300,7 +302,7 @@ def func(self): if status == "NEW": status = "|gNEW|n" - table.add_row(index, message.senders[0].get_display_name(self.caller), + table.add_row(index, message.senders[0].get_display_name(self.caller), message.header, datetime_format(message.db_date_created), status) diff --git a/evennia/utils/tests/test_utils.py b/evennia/utils/tests/test_utils.py index d2e42c4169d..36b32318b31 100644 --- a/evennia/utils/tests/test_utils.py +++ b/evennia/utils/tests/test_utils.py @@ -5,7 +5,9 @@ """ +import mock from django.test import TestCase +from datetime import datetime from evennia.utils.ansi import ANSIString from evennia.utils import utils @@ -186,3 +188,18 @@ def test_unknown_format(self): """Test that unknown formats raise exceptions.""" self.assertRaises(ValueError, utils.time_format, 0, 5) self.assertRaises(ValueError, utils.time_format, 0, "u") + + +@mock.patch("evennia.utils.utils.timezone.now", + new=mock.MagicMock(return_value=datetime(2019, 8, 28, 21, 56))) +class TestDateTimeFormat(TestCase): + + def test_datetimes(self): + dtobj = datetime(2017, 7, 26, 22, 54) + self.assertEqual(utils.datetime_format(dtobj), "Jul 26, 2017") + dtobj = datetime(2019, 7, 26, 22, 54) + self.assertEqual(utils.datetime_format(dtobj), "Jul 26") + dtobj = datetime(2019, 8, 28, 19, 54) + self.assertEqual(utils.datetime_format(dtobj), "19:54") + dtobj = datetime(2019, 8, 28, 21, 32) + self.assertEqual(utils.datetime_format(dtobj), "21:32:00") diff --git a/evennia/utils/utils.py b/evennia/utils/utils.py index 6f0685a3ab6..6c45bd8d18f 100644 --- a/evennia/utils/utils.py +++ b/evennia/utils/utils.py @@ -577,10 +577,10 @@ def datetime_format(dtobj): if dtobj.year < now.year: # another year (Apr 5, 2019) - timestring = dtobj.strftime("%b %-d, %Y") + timestring = dtobj.strftime(f"%b {dtobj.day}, %Y") elif dtobj.date() < now.date(): # another date, same year (Apr 5) - timestring = dtobj.strftime("%b %-d") + timestring = dtobj.strftime(f"%b {dtobj.day}") elif dtobj.hour < now.hour - 1: # same day, more than 1 hour ago (10:45) timestring = dtobj.strftime("%H:%M") From 8036ba8bfb23062f5762e76832cc8b2ded63376b Mon Sep 17 00:00:00 2001 From: Griatch Date: Mon, 2 Sep 2019 08:23:22 +0200 Subject: [PATCH 030/164] Remove typo. Resolve #1908. --- evennia/game_template/typeclasses/objects.py | 1 - 1 file changed, 1 deletion(-) diff --git a/evennia/game_template/typeclasses/objects.py b/evennia/game_template/typeclasses/objects.py index 12468bbd50a..721385b2055 100644 --- a/evennia/game_template/typeclasses/objects.py +++ b/evennia/game_template/typeclasses/objects.py @@ -37,7 +37,6 @@ class Object(DefaultObject): aliases (list of strings) - aliases to the object. Will be saved to database as AliasDB entries but returned as strings. dbref (int, read-only) - unique #id-number. Also "id" can be used. - back to this class date_created (string) - time stamp of object creation permissions (list of strings) - list of permission strings From 740de269ff6a7ccc08cc24cfa00becc507e788d0 Mon Sep 17 00:00:00 2001 From: Griatch Date: Mon, 2 Sep 2019 23:01:44 +0200 Subject: [PATCH 031/164] Fix leftover errors in game template object doctstring --- evennia/game_template/typeclasses/objects.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/evennia/game_template/typeclasses/objects.py b/evennia/game_template/typeclasses/objects.py index 721385b2055..b8f14dac519 100644 --- a/evennia/game_template/typeclasses/objects.py +++ b/evennia/game_template/typeclasses/objects.py @@ -34,11 +34,8 @@ class Object(DefaultObject): key (string) - name of object name (string)- same as key - aliases (list of strings) - aliases to the object. Will be saved to - database as AliasDB entries but returned as strings. dbref (int, read-only) - unique #id-number. Also "id" can be used. date_created (string) - time stamp of object creation - permissions (list of strings) - list of permission strings account (Account) - controlling account (if any, only set together with sessid below) @@ -47,8 +44,6 @@ class Object(DefaultObject): Sessions directly. location (Object) - current location. Is None if this is a room home (Object) - safety start-location - sessions (list of Sessions, read-only) - returns all sessions connected - to this object has_account (bool, read-only)- will only return *connected* accounts contents (list of Objects, read-only) - returns all objects inside this object (including exits) @@ -59,16 +54,20 @@ class Object(DefaultObject): * Handlers available + aliases - alias-handler: use aliases.add/remove/get() to use. + permissions - permission-handler: use permissions.add/remove() to + add/remove new perms. locks - lock-handler: use locks.add() to add new lock strings - db - attribute-handler: store/retrieve database attributes on this - self.db.myattr=val, val=self.db.myattr - ndb - non-persistent attribute handler: same as db but does not create - a database entry when storing data scripts - script-handler. Add new scripts to object with scripts.add() cmdset - cmdset-handler. Use cmdset.add() to add new cmdsets to object nicks - nick-handler. New nicks with nicks.add(). sessions - sessions-handler. Get Sessions connected to this object with sessions.get() + attributes - attribute-handler. Use attributes.add/remove/get. + db - attribute-handler: Shortcut for attribute-handler. Store/retrieve + database attributes using self.db.myattr=val, val=self.db.myattr + ndb - non-persistent attribute handler: same as db but does not create + a database entry when storing data * Helper methods (see src.objects.objects.py for full headers) From 69aa28fc9a40303f7dea7be207060c369385e7cb Mon Sep 17 00:00:00 2001 From: Griatch Date: Wed, 4 Sep 2019 20:35:08 +0200 Subject: [PATCH 032/164] Better output for evennia --initmissing. Fix object copy with tags (Resolve #1909) --- evennia/objects/manager.py | 2 +- evennia/server/evennia_launcher.py | 17 +++++++++++++---- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/evennia/objects/manager.py b/evennia/objects/manager.py index b65bc09a04f..0ee239377e7 100644 --- a/evennia/objects/manager.py +++ b/evennia/objects/manager.py @@ -507,7 +507,7 @@ def copy_object(self, original_object, new_key=None, # copy over all tags, if any for tag in original_object.tags.get(return_tagobj=True, return_list=True): - new_object.tags.add(tag=tag.key, category=tag.category, data=tag.data) + new_object.tags.add(tag=tag.db_key, category=tag.db_category, data=tag.db_data) return new_object diff --git a/evennia/server/evennia_launcher.py b/evennia/server/evennia_launcher.py index 43b374913f7..c9fbef49dfa 100644 --- a/evennia/server/evennia_launcher.py +++ b/evennia/server/evennia_launcher.py @@ -147,7 +147,7 @@ ERROR_NO_GAMEDIR = \ """ ERROR: No Evennia settings file was found. Evennia looks for the - file in your game directory as server/conf/settings.py. + file in your game directory as ./server/conf/settings.py. You must run this command from somewhere inside a valid game directory first created with @@ -238,10 +238,11 @@ ERROR_DATABASE = \ """ - ERROR: Your database does not seem to be set up correctly. + ERROR: Your database does not exist or is not set up correctly. (error was '{traceback}') - Standing in your game directory, run + If you think your database should work, make sure you are running your + commands from inside your game directory. If this error persists, run evennia migrate @@ -2052,16 +2053,24 @@ def main(): sys.exit() if args.initmissing: + created = False try: log_path = os.path.join(SERVERDIR, "logs") if not os.path.exists(log_path): os.makedirs(log_path) + print(f" ... Created missing log dir {log_path}.") + created = True settings_path = os.path.join(CONFDIR, "secret_settings.py") if not os.path.exists(settings_path): create_settings_file(init=False, secret_settings=True) + print(f" ... Created missing secret_settings.py file as {settings_path}.") + created = True - print(RECREATED_MISSING) + if created: + print(RECREATED_MISSING) + else: + print(" ... No missing resources to create/init. You are good to go.") except IOError: print(ERROR_INITMISSING) sys.exit() From a90cda1b367980e3e645d140df6ccb4f9fd7dc6d Mon Sep 17 00:00:00 2001 From: Griatch Date: Wed, 4 Sep 2019 20:57:44 +0200 Subject: [PATCH 033/164] Make search_account also search by account alias. Resolve #1910 --- evennia/accounts/manager.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/evennia/accounts/manager.py b/evennia/accounts/manager.py index e8be5e41a77..25f93ffddcc 100644 --- a/evennia/accounts/manager.py +++ b/evennia/accounts/manager.py @@ -169,9 +169,16 @@ def search_account(self, ostring, exact=True, typeclass=None): typeclass = "%s" % typeclass query["db_typeclass_path"] = typeclass if exact: - return self.filter(**query) + matches = self.filter(**query) else: - return self.filter(**query) + matches = self.filter(**query) + if not matches: + # try alias match + matches = self.filter( + db_tags__db_tagtype__iexact="alias", + **{"db_tags__db_key__iexact" if exact else "db_tags__db_key__icontains": ostring}) + return matches + # back-compatibility alias account_search = search_account From 81d97c668cc9bed85561896e33a5377d54cfae28 Mon Sep 17 00:00:00 2001 From: Griatch Date: Thu, 5 Sep 2019 23:54:07 +0200 Subject: [PATCH 034/164] Resolve duplicate script execution for global script. Make paused script fire at_server_reload. Resolve #1911. --- evennia/scripts/scripts.py | 10 +++++++--- evennia/server/server.py | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/evennia/scripts/scripts.py b/evennia/scripts/scripts.py index 0e1573c4126..b7e32963194 100644 --- a/evennia/scripts/scripts.py +++ b/evennia/scripts/scripts.py @@ -163,7 +163,8 @@ def _start_task(self): Start task runner. """ - + if self.ndb._task: + return self.ndb._task = ExtendedLoopingCall(self._step_task) if self.db._paused_time: @@ -429,7 +430,8 @@ def start(self, force_restart=False): """ if self.is_active and not force_restart: - # The script is already running, but make sure we have a _task if this is after a cache flush + # The script is already running, but make sure we have a _task if + # this is after a cache flush if not self.ndb._task and self.db_interval >= 0: self.ndb._task = ExtendedLoopingCall(self._step_task) try: @@ -440,7 +442,9 @@ def start(self, force_restart=False): now = not self.db_start_delay start_delay = None callcount = 0 - self.ndb._task.start(self.db_interval, now=now, start_delay=start_delay, count_start=callcount) + self.ndb._task.start(self.db_interval, now=now, + start_delay=start_delay, + count_start=callcount) return 0 obj = self.obj diff --git a/evennia/server/server.py b/evennia/server/server.py index 093d97f2052..5a8f88f2573 100644 --- a/evennia/server/server.py +++ b/evennia/server/server.py @@ -363,7 +363,7 @@ def shutdown(self, mode='reload', _reactor_stopping=False): yield [o.at_server_reload() for o in ObjectDB.get_all_cached_instances()] yield [p.at_server_reload() for p in AccountDB.get_all_cached_instances()] yield [(s.pause(manual_pause=False), s.at_server_reload()) - for s in ScriptDB.get_all_cached_instances() if s.is_active] + for s in ScriptDB.get_all_cached_instances() if s.is_active or s.attributes.has("_manual_pause")] yield self.sessions.all_sessions_portal_sync() self.at_server_reload_stop() # only save monitor state on reload, not on shutdown/reset From 70cdac9cb063f0eec3156a4dda98bb47e5f31f3a Mon Sep 17 00:00:00 2001 From: Griatch Date: Fri, 6 Sep 2019 21:50:00 +0200 Subject: [PATCH 035/164] Give better instructions in clothing contrib --- evennia/contrib/clothing.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/evennia/contrib/clothing.py b/evennia/contrib/clothing.py index 657a5345914..410055e9a13 100644 --- a/evennia/contrib/clothing.py +++ b/evennia/contrib/clothing.py @@ -51,18 +51,25 @@ class Character(ClothedCharacter): -And do the same with the ClothedCharacterCmdSet in your game's -default_cmdsets.py: +And then add ClothedCharacterCmdSet in your character set in your +game's commands/default_cmdsets.py: from evennia.contrib.clothing import ClothedCharacterCmdSet class CharacterCmdSet(default_cmds.CharacterCmdSet): + ... + at_cmdset_creation(self): + + super().at_cmdset_creation() + ... + self.add(ClothedCharacterCmdSet) # <-- add this From here, you can use the default builder commands to create clothes with which to test the system: @create a pretty shirt : evennia.contrib.clothing.Clothing @set shirt/clothing_type = 'top' + wear shirt """ @@ -367,6 +374,7 @@ def func(self): clothing = self.caller.search(self.arglist[0], candidates=self.caller.contents) wearstyle = True if not clothing: + self.caller.msg("Thing to wear must be in your inventory.") return if not clothing.is_typeclass("evennia.contrib.clothing.Clothing", exact=False): self.caller.msg("That's not clothes!") @@ -420,6 +428,7 @@ def func(self): """ clothing = self.caller.search(self.args, candidates=self.caller.contents) if not clothing: + self.caller.msg("Thing to remove must be carried or worn.") return if not clothing.db.worn: self.caller.msg("You're not wearing that!") From 9a0dcb2ae922f76b223fd8daaaed2f89175646bf Mon Sep 17 00:00:00 2001 From: Griatch Date: Sat, 7 Sep 2019 18:13:54 +0200 Subject: [PATCH 036/164] Make GLOBAL_SCRIPTS auto-start timed scripts; list all global scripts --- evennia/__init__.py | 3 ++ evennia/utils/containers.py | 97 +++++++++++++++++++++++++------------ 2 files changed, 69 insertions(+), 31 deletions(-) diff --git a/evennia/__init__.py b/evennia/__init__.py index 0a5db983720..c26da5f8efe 100644 --- a/evennia/__init__.py +++ b/evennia/__init__.py @@ -390,6 +390,9 @@ class SystemCmds(_EvContainer): BASE_GUEST_TYPECLASS = class_from_module(settings.BASE_GUEST_TYPECLASS) del class_from_module + # delayed starts + GLOBAL_SCRIPTS.start() + def set_trace(term_size=(140, 40), debugger="auto"): """ diff --git a/evennia/utils/containers.py b/evennia/utils/containers.py index 615b22ebc1b..09fec4c18bd 100644 --- a/evennia/utils/containers.py +++ b/evennia/utils/containers.py @@ -16,6 +16,9 @@ from evennia.utils import logger +SCRIPTDB = None + + class Container(object): """ Base container class. A container is simply a storage object whose @@ -87,8 +90,8 @@ class OptionContainer(Container): class GlobalScriptContainer(Container): """ Simple Handler object loaded by the Evennia API to contain and manage a - game's Global Scripts. Scripts to start are defined by - `settings.GLOBAL_SCRIPTS`. + game's Global Scripts. This will list global Scripts created on their own + but will also auto-(re)create scripts defined in `settings.GLOBAL_SCRIPTS`. Example: import evennia @@ -101,36 +104,27 @@ class GlobalScriptContainer(Container): """ def __init__(self): """ - Initialize the container by preparing scripts. Lazy-load only when the - script is requested. - Note: We must delay loading of typeclasses since this module may get initialized before Scripts are actually initialized. """ + self.typeclass_storage = None self.loaded_data = {key: {} if data is None else data for key, data in settings.GLOBAL_SCRIPTS.items()} - self.script_storage = {} - self.typeclass_storage = None - - def load_data(self): - """ - This delayed import avoids trying to load Scripts before they are - initialized. - """ - if self.typeclass_storage is None: - self.typeclass_storage = {} - for key, data in self.loaded_data.items(): - try: - typeclass = data.get('typeclass', settings.BASE_SCRIPT_TYPECLASS) - self.typeclass_storage[key] = class_from_module(typeclass) - except ImportError as err: - logger.log_err( - f"GlobalScriptContainer could not start global script {key}: {err}") + def _get_scripts(self, key=None, default=None): + global SCRIPTDB + if not SCRIPTDB: + from evennia.scripts.models import ScriptDB as SCRIPTDB + if key: + try: + return SCRIPTDB.objects.get(db_key__exact=key, db_obj__isnull=True) + except SCRIPTDB.DoesNotExist: + return default + else: + return SCRIPTDB.objects.filter(db_obj__isnull=True) def _load_script(self, key): - self.load_data() typeclass = self.typeclass_storage[key] @@ -141,6 +135,7 @@ def _load_script(self, key): desc = self.loaded_data[key].get('desc', '') if not found: + logger.log_info(f"GLOBAL_SCRIPTS: (Re)creating {key} ({typeclass}).") new_script, errors = typeclass.create(key=key, persistent=True, interval=interval, start_delay=start_delay, @@ -150,7 +145,6 @@ def _load_script(self, key): return None new_script.start() - self.script_storage[key] = new_script return new_script if ((found.interval != interval) or @@ -159,12 +153,45 @@ def _load_script(self, key): found.restart(interval=interval, start_delay=start_delay, repeats=repeats) if found.desc != desc: found.desc = desc - self.script_storage[key] = found return found + def start(self): + """ + Called last in evennia.__init__ to initialize the container late + (after script typeclasses have finished loading). + + We include all global scripts in the handler and + make sure to auto-load time-based scripts. + + """ + # populate self.typeclass_storage + self.load_data() + + # start registered scripts + for key in self.loaded_data: + self._load_script(key) + + def load_data(self): + """ + This delayed import avoids trying to load Scripts before they are + initialized. + + """ + if self.typeclass_storage is None: + self.typeclass_storage = {} + for key, data in self.loaded_data.items(): + try: + typeclass = data.get('typeclass', settings.BASE_SCRIPT_TYPECLASS) + self.typeclass_storage[key] = class_from_module(typeclass) + except ImportError as err: + logger.log_err( + f"GlobalScriptContainer could not start global script {key}: {err}") + def get(self, key, default=None): """ - Retrive data by key (in case of not knowing it beforehand). + Retrive data by key (in case of not knowing it beforehand). Any + scripts that are in settings.GLOBAL_SCRIPTS that are not found + will be recreated on-demand. Args: key (str): The name of the script. @@ -174,20 +201,28 @@ def get(self, key, default=None): Returns: any (any): The data loaded on this container. """ - - if key not in self.loaded_data: + res = self._get_scripts(key) + if not res: + if key in self.loaded_data: + # recreate if we have the info + return self._load_script(key) or default return default - return self.script_storage.get(key) or self._load_script(key) + return res def all(self): """ - Get all scripts. + Get all global scripts. Note that this will not auto-start + scripts defined in settings. Returns: scripts (list): All global script objects stored on the container. """ - return [self.__getattr__(key) for key in self.loaded_data] + self.typeclass_storage = None + self.load_data() + for key in self.loaded_data: + self._load_script(key) + return self._get_scripts(None) # Create all singletons From e4fc07316447c8de9a3f3b80d194757dae41c376 Mon Sep 17 00:00:00 2001 From: Griatch Date: Sat, 7 Sep 2019 22:51:23 +0200 Subject: [PATCH 037/164] Use lists in settings. Resolve #1912 --- CHANGELOG.md | 9 +++++++-- evennia/settings_default.py | 20 ++++++++++---------- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 291bcb04cda..5ec914a5cf2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,11 +6,16 @@ - `py` command now reroutes stdout to output results in-game client. `py` without arguments starts a full interactive Python console. -- Webclient default to a single input pane instead of two. Default to no help-popup. +- Webclient default to a single input pane instead of two. Now defaults to no help-popup. - Webclient fix of prompt display -- Webclient multimedia support for relaying images, video and sounds via `.msg(image=URL)`, `.msg(video=URL)` +- Webclient multimedia support for relaying images, video and sounds via + `.msg(image=URL)`, `.msg(video=URL)` and `.msg(audio=URL)` - Add Spanish translation (fermuch) +- Expand `GLOBAL_SCRIPTS` container to always start scripts and to include all + global scripts regardless of how they were created. +- Change settings to always use lists instead of tuples, to make mutable + settings easier to add to. (#1912) ## Evennia 0.9 (2018-2019) diff --git a/evennia/settings_default.py b/evennia/settings_default.py index e1586ac7973..a7cc4c56f56 100644 --- a/evennia/settings_default.py +++ b/evennia/settings_default.py @@ -154,8 +154,8 @@ # Activate time zone in datetimes USE_TZ = True # Authentication backends. This is the code used to authenticate a user. -AUTHENTICATION_BACKENDS = ( - 'evennia.web.utils.backends.CaseInsensitiveModelBackend',) +AUTHENTICATION_BACKENDS = [ + 'evennia.web.utils.backends.CaseInsensitiveModelBackend'] # Language code for this installation. All choices can be found here: # http://www.w3.org/TR/REC-html40/struct/dirlang.html#langcodes LANGUAGE_CODE = 'en-us' @@ -806,11 +806,11 @@ # Location of static data to overload the defaults from # evennia/web/webclient and evennia/web/website's static/ dirs. -STATICFILES_DIRS = ( - os.path.join(GAME_DIR, "web", "static_overrides"),) +STATICFILES_DIRS = [ + os.path.join(GAME_DIR, "web", "static_overrides")] # Patterns of files in the static directories. Used here to make sure that # its readme file is preserved but unused. -STATICFILES_IGNORE_PATTERNS = ('README.md',) +STATICFILES_IGNORE_PATTERNS = ['README.md'] # The name of the currently selected web template. This corresponds to the # directory names shown in the templates directory. WEBSITE_TEMPLATE = 'website' @@ -856,7 +856,7 @@ # MiddleWare are semi-transparent extensions to Django's functionality. # see http://www.djangoproject.com/documentation/middleware/ for a more detailed # explanation. -MIDDLEWARE = ( +MIDDLEWARE = [ 'django.middleware.common.CommonMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', # 1.4? @@ -864,7 +864,7 @@ 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.admindocs.middleware.XViewMiddleware', 'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware', - 'evennia.web.utils.middleware.SharedLoginMiddleware',) + 'evennia.web.utils.middleware.SharedLoginMiddleware'] ###################################################################### # Evennia components @@ -872,7 +872,7 @@ # Global and Evennia-specific apps. This ties everything together so we can # refer to app models and perform DB syncs. -INSTALLED_APPS = ( +INSTALLED_APPS = [ 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', @@ -892,7 +892,7 @@ 'evennia.help', 'evennia.scripts', 'evennia.web.website', - 'evennia.web.webclient') + 'evennia.web.webclient'] # The user profile extends the User object with more functionality; # This should usually not be changed. AUTH_USER_MODEL = "accounts.AccountDB" @@ -933,7 +933,7 @@ # always included in the default django distro. try: import django_extensions # noqa - INSTALLED_APPS = INSTALLED_APPS + ('django_extensions',) + INSTALLED_APPS = INSTALLED_APPS.append('django_extensions') except ImportError: # Django extensions are not installed in all distros. pass From f0e5347683d841ffc4ed88f714d06a6f35fd6df4 Mon Sep 17 00:00:00 2001 From: Griatch Date: Sun, 8 Sep 2019 00:09:00 +0200 Subject: [PATCH 038/164] Make DEFAULT_CHANNELS auto-recreate missing channels. Add new CHANNEL_MUDINFO setting. --- CHANGELOG.md | 3 +++ evennia/server/deprecations.py | 6 ++++++ evennia/server/initial_setup.py | 11 ++++++++++ evennia/server/server.py | 24 +++++++++++++++++++++ evennia/server/serversession.py | 2 +- evennia/settings_default.py | 38 +++++++++++++++++---------------- 6 files changed, 65 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ec914a5cf2..7e080f5576b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,9 @@ without arguments starts a full interactive Python console. global scripts regardless of how they were created. - Change settings to always use lists instead of tuples, to make mutable settings easier to add to. (#1912) +- Make new `CHANNEL_MUDINFO` setting for specifying the mudinfo channel +- Make `CHANNEL_CONNECTINFO` take full channel definition +- Make `DEFAULT_CHANNELS` list auto-create channels missing at reload ## Evennia 0.9 (2018-2019) diff --git a/evennia/server/deprecations.py b/evennia/server/deprecations.py index 3920c508793..976040a8e90 100644 --- a/evennia/server/deprecations.py +++ b/evennia/server/deprecations.py @@ -76,6 +76,12 @@ def check_errors(settings): if hasattr(settings, "GAME_DIRECTORY_LISTING"): raise DeprecationWarning(game_directory_deprecation) + chan_connectinfo = settings.CHANNEL_CONNECTINFO + if chan_connectinfo is not None and not isinstance(chan_connectinfo, dict): + raise DeprecationWarning("settings.CHANNEL_CONNECTINFO has changed. It " + "must now be either None or a dict " + "specifying the properties of the channel to create.") + def check_warnings(settings): """ diff --git a/evennia/server/initial_setup.py b/evennia/server/initial_setup.py index 759e15694a6..793bc7b5417 100644 --- a/evennia/server/initial_setup.py +++ b/evennia/server/initial_setup.py @@ -124,6 +124,17 @@ def create_channels(): logger.log_info("Initial setup: Creating default channels ...") goduser = get_god_account() + + channel_mudinfo = settings.CHANNEL_MUDINFO + if not channel_mudinfo: + raise RuntimeError("settings.CHANNEL_MUDINFO must be defined.") + channel = create.create_channel(**channel_mudinfo) + channel.connect(goduser) + + channel_connectinfo = settings.CHANNEL_CONNECTINFO + if channel_connectinfo: + channel = create.create_channel(**channel_connectinfo) + for channeldict in settings.DEFAULT_CHANNELS: channel = create.create_channel(**channeldict) channel.connect(goduser) diff --git a/evennia/server/server.py b/evennia/server/server.py index 5a8f88f2573..f5fea192e55 100644 --- a/evennia/server/server.py +++ b/evennia/server/server.py @@ -459,6 +459,30 @@ def at_post_portal_sync(self, mode): TASK_HANDLER.load() TASK_HANDLER.create_delays() + # check so default channels exist + from evennia.comms.models import ChannelDB + from evennia.accounts.models import AccountDB + from evennia.utils.create import create_channel + + god_account = AccountDB.objects.get(id=1) + # mudinfo + mudinfo_chan = settings.CHANNEL_MUDINFO + if not mudinfo_chan: + raise RuntimeError("settings.CHANNEL_MUDINFO must be defined.") + if not ChannelDB.objects.filter(db_key=mudinfo_chan['key']): + channel = create_channel(**mudinfo_chan) + channel.connect(god_account) + # connectinfo + connectinfo_chan = settings.CHANNEL_MUDINFO + if connectinfo_chan: + if not ChannelDB.objects.filter(db_key=mudinfo_chan['key']): + channel = create_channel(**connectinfo_chan) + # default channels + for chan_info in settings.DEFAULT_CHANNELS: + if not ChannelDB.objects.filter(db_key=chan_info['key']): + channel = create_channel(**chan_info) + channel.connect(god_account) + # delete the temporary setting ServerConfig.objects.conf("server_restart_mode", delete=True) diff --git a/evennia/server/serversession.py b/evennia/server/serversession.py index 5ce1fdbe401..539d573f6fa 100644 --- a/evennia/server/serversession.py +++ b/evennia/server/serversession.py @@ -304,7 +304,7 @@ def log(self, message, channel=True): cchan = channel and settings.CHANNEL_CONNECTINFO if cchan: try: - cchan = ChannelDB.objects.get_channel(cchan[0]) + cchan = ChannelDB.objects.get_channel(cchan['key']) cchan.msg("[%s]: %s" % (cchan.key, message)) except Exception: logger.log_trace() diff --git a/evennia/settings_default.py b/evennia/settings_default.py index a7cc4c56f56..8820ba58f65 100644 --- a/evennia/settings_default.py +++ b/evennia/settings_default.py @@ -661,30 +661,32 @@ # In-game Channels created from server start ###################################################################### -# This is a list of global channels created by the -# initialization script the first time Evennia starts. -# The superuser (user #1) will be automatically subscribed -# to all channels in this list. Each channel is described by -# a dictionary keyed with the same keys valid as arguments -# to the evennia.create.create_channel() function. -# Note: Evennia will treat the first channel in this list as -# the general "public" channel and the second as the -# general "mud info" channel. Other channels beyond that -# are up to the admin to design and call appropriately. +# The mudinfo channel must always exist; it is used by Evennia itself to +# relay status messages, connection info etc to staff. The superuser will be +# automatically subscribed to this channel and it will be recreated on a +# reload if deleted. This is a dict specifying the kwargs needed to create +# the channel . +CHANNEL_MUDINFO = { + "key": "MudInfo", + "aliases": "", + "desc": "Connection log", + "locks": "control:perm(Developer);listen:perm(Admin);send:false()"} +# These are additional channels to offer. Usually, at least 'public' +# should exist. The superuser will automatically be subscribed to all channels +# in this list. New entries will be created on the next reload. But +# removing or updating a same-key channel from this list will NOT automatically +# change/remove it in the game, that needs to be done manually. DEFAULT_CHANNELS = [ # public channel {"key": "Public", - "aliases": ('ooc', 'pub'), + "aliases": ('pub'), "desc": "Public discussion", "locks": "control:perm(Admin);listen:all();send:all()"}, - # connection/mud info - {"key": "MudInfo", - "aliases": "", - "desc": "Connection log", - "locks": "control:perm(Developer);listen:perm(Admin);send:false()"} ] -# Extra optional channel for receiving connection messages (" has (dis)connected"). -# While the MudInfo channel will also receieve this, this channel is meant for non-staffers. +# Optional channel info (same form as CHANNEL_MUDINFO) for the channel to +# receive connection messages (" has (dis)connected"). While the +# MudInfo channel will also receieve this info, this channel is meant for +# non-staffers. CHANNEL_CONNECTINFO = None ###################################################################### From 1517bb654b9477c95b6e3cbc952da34746cd9273 Mon Sep 17 00:00:00 2001 From: Griatch Date: Sun, 8 Sep 2019 00:46:02 +0200 Subject: [PATCH 039/164] Fix pager output for spawn/list with a very long list. Resolve #1829 --- evennia/commands/default/building.py | 3 ++- evennia/utils/evmore.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/evennia/commands/default/building.py b/evennia/commands/default/building.py index f7ef0c41521..ff601bded3a 100644 --- a/evennia/commands/default/building.py +++ b/evennia/commands/default/building.py @@ -2941,7 +2941,8 @@ def _search_show_prototype(query, prototypes=None): # import pudb; pudb.set_trace() EvMore(caller, str(protlib.list_prototypes(caller, - tags=self.lhslist)), exit_on_lastpage=True) + tags=self.lhslist)), exit_on_lastpage=True, + justify_kwargs=False) return if 'save' in self.switches: diff --git a/evennia/utils/evmore.py b/evennia/utils/evmore.py index 547c2bcda80..1530e335851 100644 --- a/evennia/utils/evmore.py +++ b/evennia/utils/evmore.py @@ -137,7 +137,8 @@ def __init__(self, caller, text, always_page=False, session=None, to determine the screen width and will receive all output. justify_kwargs (dict, bool or None, optional): If given, this should be valid keyword arguments to the utils.justify() function. If False, - no justification will be done. + no justification will be done (especially important for handling + fixed-width text content, like tables!). exit_on_lastpage (bool, optional): If reaching the last page without the page being completely filled, exit pager immediately. If unset, another move forward is required to exit. If set, the pager From 694399d31a6c056384061bdaec1342f2f172a7d5 Mon Sep 17 00:00:00 2001 From: Griatch Date: Sun, 8 Sep 2019 18:26:23 +0200 Subject: [PATCH 040/164] Fix ANSI->HTML conversion in webclient; Resolve #1792 --- evennia/accounts/accounts.py | 13 ++++---- evennia/utils/text2html.py | 33 ++++++++++++++++--- .../static/webclient/css/webclient.css | 2 +- 3 files changed, 37 insertions(+), 11 deletions(-) diff --git a/evennia/accounts/accounts.py b/evennia/accounts/accounts.py index d1b5749dd99..9929ca6e786 100644 --- a/evennia/accounts/accounts.py +++ b/evennia/accounts/accounts.py @@ -48,7 +48,7 @@ _MULTISESSION_MODE = settings.MULTISESSION_MODE _MAX_NR_CHARACTERS = settings.MAX_NR_CHARACTERS _CMDSET_ACCOUNT = settings.CMDSET_ACCOUNT -_CONNECT_CHANNEL = None +_MUDINFO_CHANNEL = None # Create throttles for too many account-creations and login attempts CREATION_THROTTLE = Throttle(limit=2, timeout=10 * 60) @@ -1145,17 +1145,18 @@ def _send_to_connect_channel(self, message): message (str): A message to send to the connect channel. """ - global _CONNECT_CHANNEL - if not _CONNECT_CHANNEL: + global _MUDINFO_CHANNEL + if not _MUDINFO_CHANNEL: try: - _CONNECT_CHANNEL = ChannelDB.objects.filter(db_key=settings.DEFAULT_CHANNELS[1]["key"])[0] + _MUDINFO_CHANNEL = ChannelDB.objects.filter( + db_key=settings.CHANNEL_MUDINFO["key"])[0] except Exception: logger.log_trace() now = timezone.now() now = "%02i-%02i-%02i(%02i:%02i)" % (now.year, now.month, now.day, now.hour, now.minute) - if _CONNECT_CHANNEL: - _CONNECT_CHANNEL.tempmsg("[%s, %s]: %s" % (_CONNECT_CHANNEL.key, now, message)) + if _MUDINFO_CHANNEL: + _MUDINFO_CHANNEL.tempmsg("[%s, %s]: %s" % (_MUDINFO_CHANNEL.key, now, message)) else: logger.log_info("[%s]: %s" % (now, message)) diff --git a/evennia/utils/text2html.py b/evennia/utils/text2html.py index 88f2101d3c9..fc757808099 100644 --- a/evennia/utils/text2html.py +++ b/evennia/utils/text2html.py @@ -80,12 +80,18 @@ class TextToHTMLparser(object): bg_colormap = dict((code, clr) for clr, code in colorback) # create stop markers - fgstop = "(?:\033\[1m|\033\[22m)*\033\[3[0-8].*?m|\033\[0m|$" - bgstop = "(?:\033\[1m|\033\[22m)*\033\[4[0-8].*?m|\033\[0m|$" + fgstop = "(?:\033\[1m|\033\[22m){0,1}\033\[3[0-8].*?m|\033\[0m|$" + bgstop = "(?:\033\[1m|\033\[22m){0,1}\033\[4[0-8].*?m|\033\[0m|$" + bgfgstop = bgstop[:-2] + r"(\s*)" + fgstop + + fgstart = "((?:\033\[1m|\033\[22m){0,1}\033\[3[0-8].*?m)" + bgstart = "((?:\033\[1m|\033\[22m){0,1}\033\[4[0-8].*?m)" + bgfgstart = bgstart + r"(\s*)" + "((?:\033\[1m|\033\[22m){0,1}\033\[[3-4][0-8].*?m){0,1}" # extract color markers, tagging the start marker and the text marked - re_fgs = re.compile("((?:\033\[1m|\033\[22m)*\033\[3[0-8].*?m)(.*?)(?=" + fgstop + ")") - re_bgs = re.compile("((?:\033\[1m|\033\[22m)*\033\[4[0-8].*?m)(.*?)(?=" + bgstop + ")") + re_fgs = re.compile(fgstart + "(.*?)(?=" + fgstop + ")") + re_bgs = re.compile(bgstart + "(.*?)(?=" + bgstop + ")") + re_bgfg = re.compile(bgfgstart + "(.*?)(?=" + bgfgstop + ")") re_normal = re.compile(normal.replace("[", r"\[")) re_hilite = re.compile("(?:%s)(.*)(?=%s|%s)" % (hilite.replace("[", r"\["), fgstop, bgstop)) @@ -97,6 +103,24 @@ class TextToHTMLparser(object): re_url = re.compile(r'((?:ftp|www|https?)\W+(?:(?!\.(?:\s|$)|&\w+;)[^"\',;$*^\\(){}<>\[\]\s])+)(\.(?:\s|$)|&\w+;|)') re_mxplink = re.compile(r'\|lc(.*?)\|lt(.*?)\|le', re.DOTALL) + def _sub_bgfg(self, colormatch): + # print("colormatch.groups()", colormatch.groups()) + bgcode, prespace, fgcode, text, postspace = colormatch.groups() + if not fgcode: + ret = r'''%s%s%s''' % ( + self.bg_colormap.get(bgcode, self.fg_colormap.get(bgcode, "err")), + prespace and " " * len(prespace) or "", + postspace and " " * len(postspace) or "", + text) + else: + ret = r'''%s%s%s''' % ( + self.bg_colormap.get(bgcode, self.fg_colormap.get(bgcode, "err")), + self.fg_colormap.get(fgcode, self.bg_colormap.get(fgcode, "err")), + prespace and " " * len(prespace) or "", + postspace and " " * len(postspace) or "", + text) + return ret + def _sub_fg(self, colormatch): code, text = colormatch.groups() return r'''%s''' % (self.fg_colormap.get(code, "err"), text) @@ -117,6 +141,7 @@ def re_color(self, text): text (str): Re-colored text. """ + text = self.re_bgfg.sub(self._sub_bgfg, text) text = self.re_fgs.sub(self._sub_fg, text) text = self.re_bgs.sub(self._sub_bg, text) text = self.re_normal.sub("", text) diff --git a/evennia/web/webclient/static/webclient/css/webclient.css b/evennia/web/webclient/static/webclient/css/webclient.css index 856931501aa..fe15af45f1c 100644 --- a/evennia/web/webclient/static/webclient/css/webclient.css +++ b/evennia/web/webclient/static/webclient/css/webclient.css @@ -17,7 +17,7 @@ body { color: #ccc; font-size: .9em; font-family: 'DejaVu Sans Mono', Consolas, Inconsolata, 'Lucida Console', monospace; - line-height: 1.6em; + line-height: 1.1em; overflow: hidden; } @media screen and (max-width: 480px) { From 497be8ecf702218f10206bdd4f3b4f74f6003dae Mon Sep 17 00:00:00 2001 From: Griatch Date: Sun, 8 Sep 2019 19:18:19 +0200 Subject: [PATCH 041/164] Fix AttributeHandler.get with no Attributes. Also make return_list=True return [] rather than [None]. Resolves #1866 --- CHANGELOG.md | 20 ++++++++++++-------- evennia/commands/default/system.py | 2 +- evennia/typeclasses/attributes.py | 27 ++++++++------------------- 3 files changed, 21 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e080f5576b..1305c857c85 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,14 +2,14 @@ ## Evennia 1.0 (2019-) (WIP) -### Already in master +### Already in master - `py` command now reroutes stdout to output results in-game client. `py` -without arguments starts a full interactive Python console. +without arguments starts a full interactive Python console. - Webclient default to a single input pane instead of two. Now defaults to no help-popup. - Webclient fix of prompt display - Webclient multimedia support for relaying images, video and sounds via - `.msg(image=URL)`, `.msg(video=URL)` + `.msg(image=URL)`, `.msg(video=URL)` and `.msg(audio=URL)` - Add Spanish translation (fermuch) - Expand `GLOBAL_SCRIPTS` container to always start scripts and to include all @@ -19,6 +19,10 @@ without arguments starts a full interactive Python console. - Make new `CHANNEL_MUDINFO` setting for specifying the mudinfo channel - Make `CHANNEL_CONNECTINFO` take full channel definition - Make `DEFAULT_CHANNELS` list auto-create channels missing at reload +- Webclient `ANSI->HTML` parser updated. Webclient line width changed from 1.6em to 1.1em + to better make ANSI graphics look the same as for third-party clients +- `AttributeHandler.get(return_list=True)` will return `[]` if there are no + Attributes instead of `[None]`. ## Evennia 0.9 (2018-2019) @@ -57,12 +61,12 @@ without arguments starts a full interactive Python console. - Change webclient from old txws version to use more supported/feature-rich Autobahn websocket library -#### Evennia game index +#### Evennia game index - Made Evennia game index client a part of core - now configured from settings file (old configs need to be moved) - The `evennia connections` command starts a wizard that helps you connect your game to the game index. -- The game index now accepts games with no public telnet/webclient info (for early prototypes). +- The game index now accepts games with no public telnet/webclient info (for early prototypes). #### New golden-layout based Webclient UI (@friarzen) - Features @@ -197,9 +201,9 @@ without arguments starts a full interactive Python console. ### Contribs -- Evscaperoom - a full puzzle engine for making multiplayer escape rooms in Evennia. Used to make - the entry for the MUD-Coder's Guild's 2019 Game Jam with the theme "One Room", where it ranked #1. -- Evennia game-index client no longer a contrib - moved into server core and configured with new +- Evscaperoom - a full puzzle engine for making multiplayer escape rooms in Evennia. Used to make + the entry for the MUD-Coder's Guild's 2019 Game Jam with the theme "One Room", where it ranked #1. +- Evennia game-index client no longer a contrib - moved into server core and configured with new setting `GAME_INDEX_ENABLED`. - The `extended_room` contrib saw some backwards-incompatible refactoring: + All commands now begin with `CmdExtendedRoom`. So before it was `CmdExtendedLook`, now diff --git a/evennia/commands/default/system.py b/evennia/commands/default/system.py index c7248f11764..c408493248f 100644 --- a/evennia/commands/default/system.py +++ b/evennia/commands/default/system.py @@ -223,7 +223,7 @@ def write(self, string): sys.stdout = old_stdout sys.stderr = old_stderr - if not ret: + if ret is None: return for session in sessions: diff --git a/evennia/typeclasses/attributes.py b/evennia/typeclasses/attributes.py index 278eff6a9e2..3b0a91e2f9e 100644 --- a/evennia/typeclasses/attributes.py +++ b/evennia/typeclasses/attributes.py @@ -420,10 +420,11 @@ def get(self, key=None, default=None, category=None, return_obj=False, return_list (bool, optional): Returns: - result (any or list): One or more matches for keys and/or categories. Each match will be - the value of the found Attribute(s) unless `return_obj` is True, at which point it - will be the attribute object itself or None. If `return_list` is True, this will - always be a list, regardless of the number of elements. + result (any or list): One or more matches for keys and/or + categories. Each match will be the value of the found Attribute(s) + unless `return_obj` is True, at which point it will be the + attribute object itself or None. If `return_list` is True, this + will always be a list, regardless of the number of elements. Raises: AttributeError: If `raise_exception` is set and no matching Attribute @@ -431,15 +432,6 @@ def get(self, key=None, default=None, category=None, return_obj=False, """ - class RetDefault(object): - """Holds default values""" - - def __init__(self): - self.key = None - self.value = default - self.category = None - self.strvalue = str(default) if default is not None else None - ret = [] for keystr in make_iter(key): # it's okay to send a None key @@ -450,13 +442,12 @@ def __init__(self): raise AttributeError elif return_obj: ret.append(None) - else: - ret.append(RetDefault()) if accessing_obj: # check 'attrread' locks ret = [attr for attr in ret if attr.access(accessing_obj, - self._attrread, default=default_access)] + self._attrread, + default=default_access)] if strattr: ret = ret if return_obj else [attr.strvalue for attr in ret if attr] else: @@ -464,9 +455,7 @@ def __init__(self): if return_list: return ret if ret else [default] if default is not None else [] - elif not ret: - return ret if len(key) > 1 else default - return ret[0] if len(ret) == 1 else ret + return ret[0] if ret and len(ret) == 1 else ret or default def add(self, key, value, category=None, lockstring="", strattr=False, accessing_obj=None, default_access=True): From 20f59846c4cae03735a9b0cfa6e48d1bea256b3c Mon Sep 17 00:00:00 2001 From: Griatch Date: Sun, 8 Sep 2019 19:41:14 +0200 Subject: [PATCH 042/164] Fix traceback in rpsystem contrib. Resolve #1871 --- evennia/contrib/rpsystem.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evennia/contrib/rpsystem.py b/evennia/contrib/rpsystem.py index 9b9a8daab35..8cb2997ff85 100644 --- a/evennia/contrib/rpsystem.py +++ b/evennia/contrib/rpsystem.py @@ -487,7 +487,7 @@ def send_emote(sender, receivers, emote, anonymous_add="first"): emote, language_mapping = parse_language(sender, emote) except (EmoteError, LanguageError) as err: # handle all error messages, don't hide actual coding errors - sender.msg(err.message) + sender.msg(str(err)) return # we escape the object mappings since we'll do the language ones first # (the text could have nested object mappings). From e5e8addd09f22f3fb513cb3fbd35dca2188d1908 Mon Sep 17 00:00:00 2001 From: Griatch Date: Sun, 8 Sep 2019 20:17:04 +0200 Subject: [PATCH 043/164] Handle drop with drop:holds() lock. Default to pass for backwards-compatibilty while on Evennia 0.9. See #1868 --- CHANGELOG.md | 3 +++ evennia/objects/objects.py | 18 +++++++++++++----- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1305c857c85..2a9bc3ed658 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## Evennia 1.0 (2019-) (WIP) +- new `drop:holds()` lock default to limit dropping nonsensical things. Access check + defaults to True for backwards-compatibility in 0.9, will be False in 1.0 + ### Already in master - `py` command now reroutes stdout to output results in-game client. `py` diff --git a/evennia/objects/objects.py b/evennia/objects/objects.py index 9fe152513ea..bb6f6b5c644 100644 --- a/evennia/objects/objects.py +++ b/evennia/objects/objects.py @@ -194,7 +194,8 @@ class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)): """ # lockstring of newly created objects, for easy overloading. # Will be formatted with the appropriate attributes. - lockstring = "control:id({account_id}) or perm(Admin);delete:id({account_id}) or perm(Admin)" + lockstring = ("control:id({account_id}) or perm(Admin);" + "delete:id({account_id}) or perm(Admin)") objects = ObjectManager() @@ -1132,13 +1133,14 @@ def basetype_setup(self): self.locks.add(";".join([ "control:perm(Developer)", # edit locks/permissions, delete - "examine:perm(Builder)", # examine properties + "examine:perm(Builder)", # examine properties "view:all()", # look at object (visibility) - "edit:perm(Admin)", # edit properties/attributes - "delete:perm(Admin)", # delete object + "edit:perm(Admin)", # edit properties/attributes + "delete:perm(Admin)", # delete object "get:all()", # pick up object + "drop:holds()" # drop only that which you hold "call:true()", # allow to call commands on this object - "tell:perm(Admin)", # allow emits to this object + "tell:perm(Admin)", # allow emits to this object "puppet:pperm(Developer)"])) # lock down puppeting only to staff by default def basetype_posthook_setup(self): @@ -1752,6 +1754,12 @@ def at_before_drop(self, dropper, **kwargs): before it is even started. """ + if not self.locks.get("drop"): + # TODO: This if-statment will be removed in Evennia 1.0 + return True + if not self.access(dropper, 'drop', default=False): + dropper.msg(f"You cannot drop {self.get_display_name(dropper)}") + return False return True def at_drop(self, dropper, **kwargs): From 5d6b9bc226bb15a67e1b0322008fa41a1104a54b Mon Sep 17 00:00:00 2001 From: Griatch Date: Sun, 8 Sep 2019 20:19:25 +0200 Subject: [PATCH 044/164] Fix typo in lock defintion --- evennia/objects/objects.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evennia/objects/objects.py b/evennia/objects/objects.py index bb6f6b5c644..ad3a0be68ea 100644 --- a/evennia/objects/objects.py +++ b/evennia/objects/objects.py @@ -1138,7 +1138,7 @@ def basetype_setup(self): "edit:perm(Admin)", # edit properties/attributes "delete:perm(Admin)", # delete object "get:all()", # pick up object - "drop:holds()" # drop only that which you hold + "drop:holds()", # drop only that which you hold "call:true()", # allow to call commands on this object "tell:perm(Admin)", # allow emits to this object "puppet:pperm(Developer)"])) # lock down puppeting only to staff by default From 7af066712232d8dc008dea0734ea8af10d7fc516 Mon Sep 17 00:00:00 2001 From: Griatch Date: Sun, 8 Sep 2019 21:17:55 +0200 Subject: [PATCH 045/164] Fix unit tests --- evennia/prototypes/tests.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/evennia/prototypes/tests.py b/evennia/prototypes/tests.py index 45b452452fa..9c841a25160 100644 --- a/evennia/prototypes/tests.py +++ b/evennia/prototypes/tests.py @@ -87,6 +87,7 @@ def test_prototype_from_object(self): 'call:true()', 'control:perm(Developer)', 'delete:perm(Admin)', + 'drop:holds()', 'edit:perm(Admin)', 'examine:perm(Builder)', 'get:all()', @@ -137,6 +138,7 @@ def test_update_objects_from_prototypes(self): 'home': Something, 'location': Something, 'locks': 'call:true();control:perm(Developer);delete:perm(Admin);' + 'drop:holds();' 'edit:perm(Admin);examine:perm(Builder);get:all();' 'puppet:pperm(Developer);tell:perm(Admin);view:all()', 'prototype_desc': 'Built from Obj', @@ -153,6 +155,7 @@ def test_update_objects_from_prototypes(self): 'key': 'Obj', 'location': Something, 'locks': 'call:true();control:perm(Developer);delete:perm(Admin);' + 'drop:holds();' 'edit:perm(Admin);examine:perm(Builder);get:all();' 'puppet:pperm(Developer);tell:perm(Admin);view:all()', 'new': 'new_val', @@ -172,9 +175,9 @@ def test_update_objects_from_prototypes(self): 'prototype_key': (Something, Something, 'UPDATE'), 'location': (Something, Something, 'KEEP'), 'locks': ('call:true();control:perm(Developer);delete:perm(Admin);' - 'edit:perm(Admin);examine:perm(Builder);get:all();' - 'puppet:pperm(Developer);tell:perm(Admin);view:all()', - 'call:true();control:perm(Developer);delete:perm(Admin);' + 'drop:holds();edit:perm(Admin);examine:perm(Builder);' + 'get:all();puppet:pperm(Developer);tell:perm(Admin);view:all()', + 'call:true();control:perm(Developer);delete:perm(Admin);drop:holds();' 'edit:perm(Admin);examine:perm(Builder);get:all();' 'puppet:pperm(Developer);tell:perm(Admin);view:all()', 'KEEP'), 'prototype_tags': {}, @@ -232,6 +235,7 @@ def test_update_objects_from_prototypes(self): 'call:true()', 'control:perm(Developer)', 'delete:perm(Admin)', + 'drop:holds()', 'edit:perm(Admin)', 'examine:perm(Builder)', 'get:all()', @@ -253,7 +257,7 @@ def setUp(self): super(TestProtLib, self).setUp() self.obj1.attributes.add("testattr", "testval") self.prot = spawner.prototype_from_object(self.obj1) - + def test_prototype_to_str(self): prstr = protlib.prototype_to_str(self.prot) From fceb7a985d71422d5843a7bb1e680915f9f92944 Mon Sep 17 00:00:00 2001 From: Griatch Date: Mon, 9 Sep 2019 07:18:28 +0200 Subject: [PATCH 046/164] Properly encode FileLogger's readline output. Resolve #1875. --- evennia/utils/logger.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evennia/utils/logger.py b/evennia/utils/logger.py index 9c834431dd7..cbe88f5b9bc 100644 --- a/evennia/utils/logger.py +++ b/evennia/utils/logger.py @@ -334,7 +334,7 @@ def readlines(self, *args, **kwargs): Returns: lines (list): lines from our _file attribute. """ - return self._file.readlines(*args, **kwargs) + return [line.decode('utf-8') for line in self._file.readlines(*args, **kwargs)] _LOG_FILE_HANDLES = {} # holds open log handles From 7b829a8d871dc0128a47326a3b41ed7b7961ef28 Mon Sep 17 00:00:00 2001 From: Kovitikus Date: Tue, 10 Sep 2019 10:31:49 -0400 Subject: [PATCH 047/164] Updated to fstrings. --- evennia/accounts/accounts.py | 64 +++++++++++++++++------------------- 1 file changed, 31 insertions(+), 33 deletions(-) diff --git a/evennia/accounts/accounts.py b/evennia/accounts/accounts.py index 9929ca6e786..8a41763169e 100644 --- a/evennia/accounts/accounts.py +++ b/evennia/accounts/accounts.py @@ -269,7 +269,7 @@ def puppet_object(self, session, obj): return if not obj.access(self, 'puppet'): # no access - self.msg("You don't have permission to puppet '%s'." % obj.key) + self.msg(f"You don't have permission to puppet '{obj.key}'.") return if obj.account: # object already puppeted @@ -278,19 +278,19 @@ def puppet_object(self, session, obj): # we may take over another of our sessions # output messages to the affected sessions if _MULTISESSION_MODE in (1, 3): - txt1 = "Sharing |c%s|n with another of your sessions." - txt2 = "|c%s|n|G is now shared from another of your sessions.|n" - self.msg(txt1 % obj.name, session=session) - self.msg(txt2 % obj.name, session=obj.sessions.all()) + txt1 = f"Sharing |c{obj.name}|n with another of your sessions." + txt2 = f"|c{obj.name}|n|G is now shared from another of your sessions.|n" + self.msg(txt1, session=session) + self.msg(txt2, session=obj.sessions.all()) else: - txt1 = "Taking over |c%s|n from another of your sessions." - txt2 = "|c%s|n|R is now acted from another of your sessions.|n" - self.msg(txt1 % obj.name, session=session) - self.msg(txt2 % obj.name, session=obj.sessions.all()) + txt1 = f"Taking over |c{obj.name}|n from another of your sessions." + txt2 = f"|c{obj.name}|n|R is now acted from another of your sessions.|n" + self.msg(txt1, session=session) + self.msg(txt2, session=obj.sessions.all()) self.unpuppet_object(obj.sessions.get()) elif obj.account.is_connected: # controlled by another account - self.msg("|c%s|R is already puppeted by another Account." % obj.key) + self.msg(f"|c{obj.key}|R is already puppeted by another Account.") return # do the puppeting @@ -439,9 +439,9 @@ def get_username_validators( try: klass = import_string(validator['NAME']) except ImportError: - msg = ("The module in NAME could not be imported: %s. " + msg = (f"The module in NAME could not be imported: {validator['NAME']}. " "Check your AUTH_USERNAME_VALIDATORS setting.") - raise ImproperlyConfigured(msg % validator['NAME']) + raise ImproperlyConfigured(msg) objs.append(klass(**validator.get('OPTIONS', {}))) return objs @@ -494,7 +494,7 @@ def authenticate(cls, username, password, ip='', **kwargs): # this is a banned IP or name! errors.append("|rYou have been banned and cannot continue from here." "\nIf you feel this ban is in error, please email an admin.|x") - logger.log_sec('Authentication Denied (Banned): %s (IP: %s).' % (username, ip)) + logger.log_sec(f'Authentication Denied (Banned): {username} (IP: {ip}).') LOGIN_THROTTLE.update(ip, 'Too many sightings of banned artifact.') return None, errors @@ -505,7 +505,7 @@ def authenticate(cls, username, password, ip='', **kwargs): errors.append('Username and/or password is incorrect.') # Log auth failures while throttle is inactive - logger.log_sec('Authentication Failure: %s (IP: %s).' % (username, ip)) + logger.log_sec(f'Authentication Failure: {username} (IP: {ip}).') # Update throttle if ip: @@ -521,7 +521,7 @@ def authenticate(cls, username, password, ip='', **kwargs): return None, errors # Account successfully authenticated - logger.log_sec('Authentication Success: %s (IP: %s).' % (account, ip)) + logger.log_sec(f'Authentication Success: {account} (IP: {ip}).') return account, errors @classmethod @@ -629,7 +629,7 @@ def set_password(self, password, **kwargs): """ super(DefaultAccount, self).set_password(password) - logger.log_sec("Password successfully changed for %s." % self) + logger.log_sec(f"Password successfully changed for {self}.") self.at_password_change() @classmethod @@ -706,7 +706,7 @@ def create(cls, *args, **kwargs): try: try: account = create.create_account(username, email, password, permissions=permissions, typeclass=typeclass) - logger.log_sec('Account Created: %s (IP: %s).' % (account, ip)) + logger.log_sec(f'Account Created: {account} (IP: {ip}).') except Exception as e: errors.append("There was an error creating the Account. If this problem persists, contact an admin.") @@ -725,7 +725,7 @@ def create(cls, *args, **kwargs): # join the new account to the public channel pchannel = ChannelDB.objects.get_channel(settings.DEFAULT_CHANNELS[0]["key"]) if not pchannel or not pchannel.connect(account): - string = "New account '%s' could not connect to public channel!" % account.key + string = f"New account '{account.key}' could not connect to public channel!" errors.append(string) logger.log_err(string) @@ -1034,7 +1034,7 @@ def at_first_save(self): updates = [] if not cdict.get("key"): if not self.db_key: - self.db_key = "#%i" % self.dbid + self.db_key = f"#{self.dbid}" updates.append("db_key") elif self.key != cdict.get("key"): updates.append("db_key") @@ -1156,9 +1156,9 @@ def _send_to_connect_channel(self, message): now = "%02i-%02i-%02i(%02i:%02i)" % (now.year, now.month, now.day, now.hour, now.minute) if _MUDINFO_CHANNEL: - _MUDINFO_CHANNEL.tempmsg("[%s, %s]: %s" % (_MUDINFO_CHANNEL.key, now, message)) + _MUDINFO_CHANNEL.tempmsg(f"[{_MUDINFO_CHANNEL.key}, {now}]: {message}") else: - logger.log_info("[%s]: %s" % (now, message)) + logger.log_info(f"[{now}]: {message}") def at_post_login(self, session=None, **kwargs): """ @@ -1185,7 +1185,7 @@ def at_post_login(self, session=None, **kwargs): if session: session.msg(logged_in={}) - self._send_to_connect_channel("|G%s connected|n" % self.key) + self._send_to_connect_channel(f"|G{self.key} connected|n") if _MULTISESSION_MODE == 0: # in this mode we should have only one character available. We # try to auto-connect to our last conneted object, if any @@ -1235,8 +1235,8 @@ def at_disconnect(self, reason=None, **kwargs): """ - reason = " (%s)" % reason if reason else "" - self._send_to_connect_channel("|R%s disconnected%s|n" % (self.key, reason)) + reason = f" ({reason if reason else ''})" + self._send_to_connect_channel(f"|R{self.key} disconnected{reason}|n") def at_post_disconnect(self, **kwargs): """ @@ -1353,12 +1353,12 @@ def at_look(self, target=None, session=None, **kwargs): is_su = self.is_superuser # text shown when looking in the ooc area - result = ["Account |g%s|n (you are Out-of-Character)" % self.key] + result = [f"Account |g{self.key}|n (you are Out-of-Character)"] nsess = len(sessions) result.append(nsess == 1 and "\n\n|wConnected session:|n" or - "\n\n|wConnected sessions (%i):|n" % nsess) + f"\n\n|wConnected sessions ({nsess}):|n") for isess, sess in enumerate(sessions): csessid = sess.sessid addr = "%s (%s)" % (sess.protocol_key, isinstance(sess.address, tuple) and @@ -1385,7 +1385,7 @@ def at_look(self, target=None, session=None, **kwargs): string_s_ending = len(characters) > 1 and "s" or "" result.append("\n |w@ic |n - enter the game (|w@ooc|n to get back here)") if is_su: - result.append("\n\nAvailable character%s (%i/unlimited):" % (string_s_ending, len(characters))) + result.append(f"\n\nAvailable character{string_s_ending} ({len(characters)}/unlimited):") else: result.append("\n\nAvailable character%s%s:" % (string_s_ending, charmax > 1 and " (%i/%i)" % (len(characters), charmax) or "")) @@ -1397,14 +1397,12 @@ def at_look(self, target=None, session=None, **kwargs): # character is already puppeted sid = sess in sessions and sessions.index(sess) + 1 if sess and sid: - result.append("\n - |G%s|n [%s] (played by you in session %i)" - % (char.key, ", ".join(char.permissions.all()), sid)) + result.append(f"\n - |G{char.key}|n [{', '.join(char.permissions.all())}] (played by you in session {sid})") else: - result.append("\n - |R%s|n [%s] (played by someone else)" - % (char.key, ", ".join(char.permissions.all()))) + result.append(f"\n - |R{char.key}|n [{', '.join(char.permissions.all())}] (played by someone else)") else: # character is "free to puppet" - result.append("\n - %s [%s]" % (char.key, ", ".join(char.permissions.all()))) + result.append(f"\n - {char.key} [{', '.join(char.permissions.all())}]") look_string = ("-" * 68) + "\n" + "".join(result) + "\n" + ("-" * 68) return look_string @@ -1499,7 +1497,7 @@ def at_post_login(self, session=None, **kwargs): overriding the call (unused by default). """ - self._send_to_connect_channel("|G%s connected|n" % self.key) + self._send_to_connect_channel(f"|G{self.key} connected|n") self.puppet_object(session, self.db._last_puppet) def at_server_shutdown(self): From 80d09e1b6fc20d05437c0cd334639669987a247a Mon Sep 17 00:00:00 2001 From: Kovitikus Date: Tue, 10 Sep 2019 11:22:43 -0400 Subject: [PATCH 048/164] Fixed a trailing whitespace. --- evennia/accounts/accounts.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evennia/accounts/accounts.py b/evennia/accounts/accounts.py index 8a41763169e..4920f734a7d 100644 --- a/evennia/accounts/accounts.py +++ b/evennia/accounts/accounts.py @@ -1235,7 +1235,7 @@ def at_disconnect(self, reason=None, **kwargs): """ - reason = f" ({reason if reason else ''})" + reason = f" ({reason if reason else ''})" self._send_to_connect_channel(f"|R{self.key} disconnected{reason}|n") def at_post_disconnect(self, **kwargs): From 14401ae2f26676f13a99571d68f5c083de7db0fc Mon Sep 17 00:00:00 2001 From: Kovitikus Date: Tue, 10 Sep 2019 12:47:03 -0400 Subject: [PATCH 049/164] Updated bots.py to fstrings. --- evennia/accounts/bots.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/evennia/accounts/bots.py b/evennia/accounts/bots.py index 35e73d4aaad..ca33e6a299d 100644 --- a/evennia/accounts/bots.py +++ b/evennia/accounts/bots.py @@ -104,7 +104,7 @@ def basetype_setup(self): "boot:perm(Admin);msg:false();noidletimeout:true()" self.locks.add(lockstring) # set the basics of being a bot - script_key = "%s" % self.key + script_key = f"{self.key}" self.scripts.add(BotStarter, key=script_key) self.is_bot = True @@ -187,7 +187,7 @@ def start(self, ev_channel=None, irc_botname=None, irc_channel=None, # connect to Evennia channel channel = search.channel_search(ev_channel) if not channel: - raise RuntimeError("Evennia Channel '%s' not found." % ev_channel) + raise RuntimeError(f"Evennia Channel '{ev_channel}' not found.") channel = channel[0] channel.connect(self) self.db.ev_channel = channel @@ -307,19 +307,19 @@ def execute_cmd(self, session=None, txt=None, **kwargs): if kwargs["type"] == "nicklist": # the return of a nicklist request if hasattr(self, "_nicklist_callers") and self._nicklist_callers: - chstr = "%s (%s:%s)" % (self.db.irc_channel, self.db.irc_network, self.db.irc_port) + chstr = f"{self.db.irc_channel} ({self.db.irc_network}:{self.db.irc_port})" nicklist = ", ".join(sorted(kwargs["nicklist"], key=lambda n: n.lower())) for obj in self._nicklist_callers: - obj.msg("Nicks at %s:\n %s" % (chstr, nicklist)) + obj.msg(f"Nicks at {chstr}:\n {nicklist}") self._nicklist_callers = [] return elif kwargs["type"] == "ping": # the return of a ping if hasattr(self, "_ping_callers") and self._ping_callers: - chstr = "%s (%s:%s)" % (self.db.irc_channel, self.db.irc_network, self.db.irc_port) + chstr = f"{self.db.irc_channel} ({self.db.irc_network}:{self.db.irc_port})" for obj in self._ping_callers: - obj.msg("IRC ping return from %s took %ss." % (chstr, kwargs["timing"])) + obj.msg(f"IRC ping return from {chstr} took {kwargs['timing']}s.") self._ping_callers = [] return @@ -341,10 +341,10 @@ def execute_cmd(self, session=None, txt=None, **kwargs): whos.append("%s (%s/%s)" % (utils.crop("|w%s|n" % account.name, width=25), utils.time_format(delta_conn, 0), utils.time_format(delta_cmd, 1))) - text = "Who list (online/idle): %s" % ", ".join(sorted(whos, key=lambda w: w.lower())) + text = f"Who list (online/idle): {', '.join(sorted(whos, key=lambda w: w.lower()))}" elif txt.lower().startswith("about"): # some bot info - text = "This is an Evennia IRC bot connecting from '%s'." % settings.SERVERNAME + text = f"This is an Evennia IRC bot connecting from '{settings.SERVERNAME}'." else: text = "I understand 'who' and 'about'." super().msg(privmsg=((text,), {"user": user})) @@ -352,10 +352,10 @@ def execute_cmd(self, session=None, txt=None, **kwargs): # something to send to the main channel if kwargs["type"] == "action": # An action (irc pose) - text = "%s@%s %s" % (kwargs["user"], kwargs["channel"], txt) + text = f"{kwargs['user']}@{kwargs['channel']} {txt}" else: # msg - A normal channel message - text = "%s@%s: %s" % (kwargs["user"], kwargs["channel"], txt) + text = f"{kwargs['user']}@{kwargs['channel']}: {txt}" if not self.ndb.ev_channel and self.db.ev_channel: # cache channel lookup @@ -401,7 +401,7 @@ def start(self, ev_channel=None, rss_url=None, rss_rate=None): # connect to Evennia channel channel = search.channel_search(ev_channel) if not channel: - raise RuntimeError("Evennia Channel '%s' not found." % ev_channel) + raise RuntimeError(f"Evennia Channel '{ev_channel}' not found.") channel = channel[0] self.db.ev_channel = channel if rss_url: @@ -463,7 +463,7 @@ def start(self, ev_channel=None, grapevine_channel=None): # connect to Evennia channel channel = search.channel_search(ev_channel) if not channel: - raise RuntimeError("Evennia Channel '%s' not found." % ev_channel) + raise RuntimeError(f"Evennia Channel '{ev_channel}' not found.") channel = channel[0] channel.connect(self) self.db.ev_channel = channel From ef9653a04ce1c77024755d3846d427adc8050e32 Mon Sep 17 00:00:00 2001 From: Kovitikus Date: Tue, 10 Sep 2019 13:22:05 -0400 Subject: [PATCH 050/164] Updated manager.py with f-strings. --- evennia/accounts/manager.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/evennia/accounts/manager.py b/evennia/accounts/manager.py index 25f93ffddcc..1b29803f956 100644 --- a/evennia/accounts/manager.py +++ b/evennia/accounts/manager.py @@ -164,9 +164,9 @@ def search_account(self, ostring, exact=True, typeclass=None): if typeclass: # we accept both strings and actual typeclasses if callable(typeclass): - typeclass = "%s.%s" % (typeclass.__module__, typeclass.__name__) + typeclass = f"{typeclass.__module__}.{typeclass.__name__}" else: - typeclass = "%s" % typeclass + typeclass = f"{typeclass}" query["db_typeclass_path"] = typeclass if exact: matches = self.filter(**query) From 11266f59c194df891d0f06d80c27424f717782da Mon Sep 17 00:00:00 2001 From: Kovitikus Date: Tue, 10 Sep 2019 13:24:16 -0400 Subject: [PATCH 051/164] Updated models.py with f-strings. --- evennia/accounts/models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/evennia/accounts/models.py b/evennia/accounts/models.py index a0283e2aef5..79da54db340 100644 --- a/evennia/accounts/models.py +++ b/evennia/accounts/models.py @@ -137,10 +137,10 @@ def __cmdset_storage_del(self): # def __str__(self): - return smart_str("%s(account %s)" % (self.name, self.dbid)) + return smart_str(f"{self.name}(account {self.dbid})") def __repr__(self): - return "%s(account#%s)" % (self.name, self.dbid) + return f"{self.name}(account#{self.dbid})" #@property def __username_get(self): From a7cc5c0c2db315810e3ea9ebfdcad590b5e2ded5 Mon Sep 17 00:00:00 2001 From: Kovitikus Date: Tue, 10 Sep 2019 13:29:21 -0400 Subject: [PATCH 052/164] Updated tests.py with f-strings. --- evennia/accounts/tests.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/evennia/accounts/tests.py b/evennia/accounts/tests.py index 67cca232465..240cd0500dc 100644 --- a/evennia/accounts/tests.py +++ b/evennia/accounts/tests.py @@ -20,7 +20,7 @@ class TestAccountSessionHandler(TestCase): def setUp(self): self.account = create.create_account( - "TestAccount%s" % randint(0, 999999), email="test@test.com", + f"TestAccount{randint(0, 999999)}", email="test@test.com", password="testpassword", typeclass=DefaultAccount) self.handler = AccountSessionHandler(self.account) @@ -118,7 +118,7 @@ def setUp(self): self.password = "testpassword" self.account.delete() - self.account = create.create_account("TestAccount%s" % randint(100000, 999999), email="test@test.com", password=self.password, typeclass=DefaultAccount) + self.account = create.create_account(f"TestAccount{randint(100000, 999999)}", email="test@test.com", password=self.password, typeclass=DefaultAccount) def test_authentication(self): "Confirm Account authentication method is authenticating/denying users." @@ -170,7 +170,7 @@ def test_username_validation(self): def test_password_validation(self): "Check password validators deny bad passwords" - account = create.create_account("TestAccount%s" % randint(100000, 999999), + account = create.create_account(f"TestAccount{randint(100000, 999999)}", email="test@test.com", password="testpassword", typeclass=DefaultAccount) for bad in ('', '123', 'password', 'TestAccount', '#', 'xyzzy'): self.assertFalse(account.validate_password(bad, account=self.account)[0]) @@ -182,7 +182,7 @@ def test_password_validation(self): def test_password_change(self): "Check password setting and validation is working as expected" - account = create.create_account("TestAccount%s" % randint(100000, 999999), + account = create.create_account(f"TestAccount{randint(100000, 999999)}", email="test@test.com", password="testpassword", typeclass=DefaultAccount) from django.core.exceptions import ValidationError @@ -228,7 +228,7 @@ def test_puppet_object_already_puppeting(self): import evennia.server.sessionhandler account = create.create_account( - "TestAccount%s" % randint(0, 999999), email="test@test.com", + f"TestAccount{randint(0, 999999)}", email="test@test.com", password="testpassword", typeclass=DefaultAccount) self.s1.uid = account.uid evennia.server.sessionhandler.SESSIONS[self.s1.uid] = self.s1 @@ -247,7 +247,7 @@ def test_puppet_object_no_permission(self): import evennia.server.sessionhandler - account = create.create_account("TestAccount%s" % randint(0, 999999), email="test@test.com", password="testpassword", typeclass=DefaultAccount) + account = create.create_account(f"TestAccount{randint(0, 999999)}", email="test@test.com", password="testpassword", typeclass=DefaultAccount) self.s1.uid = account.uid evennia.server.sessionhandler.SESSIONS[self.s1.uid] = self.s1 @@ -266,7 +266,7 @@ def test_puppet_object_joining_other_session(self): import evennia.server.sessionhandler - account = create.create_account("TestAccount%s" % randint(0, 999999), email="test@test.com", password="testpassword", typeclass=DefaultAccount) + account = create.create_account(f"TestAccount{randint(0, 999999)}", email="test@test.com", password="testpassword", typeclass=DefaultAccount) self.s1.uid = account.uid evennia.server.sessionhandler.SESSIONS[self.s1.uid] = self.s1 @@ -289,7 +289,7 @@ def test_puppet_object_already_puppeted(self): import evennia.server.sessionhandler - account = create.create_account("TestAccount%s" % randint(0, 999999), email="test@test.com", password="testpassword", typeclass=DefaultAccount) + account = create.create_account(f"TestAccount{randint(0, 999999)}", email="test@test.com", password="testpassword", typeclass=DefaultAccount) self.account = account self.s1.uid = account.uid evennia.server.sessionhandler.SESSIONS[self.s1.uid] = self.s1 @@ -325,7 +325,7 @@ def test_puppet_deletion(self): self.char1.delete() # Playable char list should be empty. self.assertFalse(self.account.db._playable_characters, - 'Playable character list is not empty! %s' % self.account.db._playable_characters) + f'Playable character list is not empty! {self.account.db._playable_characters}') class TestDefaultAccountEv(EvenniaTest): From 8e03156ffe99e5d6d6256abba3dcace6fc09a07b Mon Sep 17 00:00:00 2001 From: Griatch Date: Tue, 10 Sep 2019 19:57:22 +0200 Subject: [PATCH 053/164] Fix mismatching help query in OOC mode. Resolve #1914. --- evennia/commands/default/help.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/evennia/commands/default/help.py b/evennia/commands/default/help.py index f8d212f0e5d..f03773eb898 100644 --- a/evennia/commands/default/help.py +++ b/evennia/commands/default/help.py @@ -267,7 +267,8 @@ def func(self): return # no exact matches found. Just give suggestions. - self.msg((self.format_help_entry("", "No help entry found for '%s'" % query, None, suggested=suggestions), {"type": "help"})) + self.msg(self.format_help_entry("", f"No help entry found for '{query}'", + None, suggested=suggestions), options={"type": "help"}) def _loadhelp(caller): From 6b47c9a9e80122f58e931a3f9b266d2a40bd0372 Mon Sep 17 00:00:00 2001 From: Kovitikus Date: Tue, 10 Sep 2019 14:42:01 -0400 Subject: [PATCH 054/164] Converions to string types. --- evennia/accounts/bots.py | 2 +- evennia/accounts/manager.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/evennia/accounts/bots.py b/evennia/accounts/bots.py index ca33e6a299d..18043a9a523 100644 --- a/evennia/accounts/bots.py +++ b/evennia/accounts/bots.py @@ -104,7 +104,7 @@ def basetype_setup(self): "boot:perm(Admin);msg:false();noidletimeout:true()" self.locks.add(lockstring) # set the basics of being a bot - script_key = f"{self.key}" + script_key = f"{str(self.key)}" self.scripts.add(BotStarter, key=script_key) self.is_bot = True diff --git a/evennia/accounts/manager.py b/evennia/accounts/manager.py index 1b29803f956..f5f34f1aac2 100644 --- a/evennia/accounts/manager.py +++ b/evennia/accounts/manager.py @@ -166,7 +166,7 @@ def search_account(self, ostring, exact=True, typeclass=None): if callable(typeclass): typeclass = f"{typeclass.__module__}.{typeclass.__name__}" else: - typeclass = f"{typeclass}" + typeclass = f"{str(typeclass)}" query["db_typeclass_path"] = typeclass if exact: matches = self.filter(**query) From ee7e27631599bdf127c08e90e0e58e8c6c2474c6 Mon Sep 17 00:00:00 2001 From: Kovitikus Date: Tue, 10 Sep 2019 15:17:21 -0400 Subject: [PATCH 055/164] Fixed string conversions. --- evennia/accounts/bots.py | 2 +- evennia/accounts/manager.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/evennia/accounts/bots.py b/evennia/accounts/bots.py index 18043a9a523..680d8bcbd45 100644 --- a/evennia/accounts/bots.py +++ b/evennia/accounts/bots.py @@ -104,7 +104,7 @@ def basetype_setup(self): "boot:perm(Admin);msg:false();noidletimeout:true()" self.locks.add(lockstring) # set the basics of being a bot - script_key = f"{str(self.key)}" + script_key = str(self.key) self.scripts.add(BotStarter, key=script_key) self.is_bot = True diff --git a/evennia/accounts/manager.py b/evennia/accounts/manager.py index f5f34f1aac2..c617b97a38c 100644 --- a/evennia/accounts/manager.py +++ b/evennia/accounts/manager.py @@ -166,7 +166,7 @@ def search_account(self, ostring, exact=True, typeclass=None): if callable(typeclass): typeclass = f"{typeclass.__module__}.{typeclass.__name__}" else: - typeclass = f"{str(typeclass)}" + typeclass = fstr(typeclass) query["db_typeclass_path"] = typeclass if exact: matches = self.filter(**query) From 210325c7949f5fba98d76c35619e4a2d244320f4 Mon Sep 17 00:00:00 2001 From: Kovitikus Date: Tue, 10 Sep 2019 15:17:56 -0400 Subject: [PATCH 056/164] Fixed string conversions. --- evennia/accounts/manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evennia/accounts/manager.py b/evennia/accounts/manager.py index c617b97a38c..7771280fe91 100644 --- a/evennia/accounts/manager.py +++ b/evennia/accounts/manager.py @@ -166,7 +166,7 @@ def search_account(self, ostring, exact=True, typeclass=None): if callable(typeclass): typeclass = f"{typeclass.__module__}.{typeclass.__name__}" else: - typeclass = fstr(typeclass) + typeclass = str(typeclass) query["db_typeclass_path"] = typeclass if exact: matches = self.filter(**query) From af42c1da7f7432581ea9ddaaf6dcfa2c16df1c19 Mon Sep 17 00:00:00 2001 From: Griatch Date: Wed, 11 Sep 2019 08:42:34 +0200 Subject: [PATCH 057/164] Fix line breaks from fixed-width lines in webclient. Resolve #1837. --- evennia/utils/text2html.py | 9 +++++---- evennia/web/webclient/static/webclient/css/webclient.css | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/evennia/utils/text2html.py b/evennia/utils/text2html.py index fc757808099..e35dc700326 100644 --- a/evennia/utils/text2html.py +++ b/evennia/utils/text2html.py @@ -99,7 +99,8 @@ class TextToHTMLparser(object): re_uline = re.compile("(?:%s)(.*?)(?=%s|%s)" % (underline.replace("[", r"\["), fgstop, bgstop)) re_blink = re.compile("(?:%s)(.*?)(?=%s|%s)" % (blink.replace("[", r"\["), fgstop, bgstop)) re_inverse = re.compile("(?:%s)(.*?)(?=%s|%s)" % (inverse.replace("[", r"\["), fgstop, bgstop)) - re_string = re.compile(r'(?P[<&>])|(?P [ \t]+)|(?P^ )|(?P\r\n|\r|\n)', re.S | re.M | re.I) + re_string = re.compile(r'(?P[<&>])|(?P(?<=\S)\s+)|(?P\s[ \t]+)|' + r'(?P^ )|(?P\r\n|\r|\n)', re.S | re.M | re.I) re_url = re.compile(r'((?:ftp|www|https?)\W+(?:(?!\.(?:\s|$)|&\w+;)[^"\',;$*^\\(){}<>\[\]\s])+)(\.(?:\s|$)|&\w+;|)') re_mxplink = re.compile(r'\|lc(.*?)\|lt(.*?)\|le', re.DOTALL) @@ -291,11 +292,11 @@ def sub_text(self, match): cdict = match.groupdict() if cdict['htmlchars']: return cgi.escape(cdict['htmlchars']) - if cdict['lineend']: + elif cdict['lineend']: return '
' elif cdict['space'] == '\t': return ' ' * self.tabstop - elif cdict['space'] or cdict["spacestart"]: + elif cdict['space'] or cdict["spacestart"] or cdict['firstspace']: text = match.group().replace('\t', ' ' * self.tabstop) text = text.replace(' ', ' ') return text @@ -328,7 +329,7 @@ def parse(self, text, strip_ansi=False): result = self.remove_backspaces(result) result = self.convert_urls(result) # clean out eventual ansi that was missed - #result = parse_ansi(result, strip_ansi=True) + # result = parse_ansi(result, strip_ansi=True) return result diff --git a/evennia/web/webclient/static/webclient/css/webclient.css b/evennia/web/webclient/static/webclient/css/webclient.css index fe15af45f1c..312f321595f 100644 --- a/evennia/web/webclient/static/webclient/css/webclient.css +++ b/evennia/web/webclient/static/webclient/css/webclient.css @@ -17,7 +17,7 @@ body { color: #ccc; font-size: .9em; font-family: 'DejaVu Sans Mono', Consolas, Inconsolata, 'Lucida Console', monospace; - line-height: 1.1em; + line-height: 1.4em; overflow: hidden; } @media screen and (max-width: 480px) { From 9d05e7305f144f933fc1cda110418895623fd6cf Mon Sep 17 00:00:00 2001 From: Griatch Date: Wed, 11 Sep 2019 19:58:28 +0200 Subject: [PATCH 058/164] Use literal space rather than \s in text2html parser. Resolve #1916. --- evennia/utils/text2html.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evennia/utils/text2html.py b/evennia/utils/text2html.py index e35dc700326..9c075a22dc4 100644 --- a/evennia/utils/text2html.py +++ b/evennia/utils/text2html.py @@ -99,7 +99,7 @@ class TextToHTMLparser(object): re_uline = re.compile("(?:%s)(.*?)(?=%s|%s)" % (underline.replace("[", r"\["), fgstop, bgstop)) re_blink = re.compile("(?:%s)(.*?)(?=%s|%s)" % (blink.replace("[", r"\["), fgstop, bgstop)) re_inverse = re.compile("(?:%s)(.*?)(?=%s|%s)" % (inverse.replace("[", r"\["), fgstop, bgstop)) - re_string = re.compile(r'(?P[<&>])|(?P(?<=\S)\s+)|(?P\s[ \t]+)|' + re_string = re.compile(r'(?P[<&>])|(?P(?<=\S) +)|(?P [ \t]+)|' r'(?P^ )|(?P\r\n|\r|\n)', re.S | re.M | re.I) re_url = re.compile(r'((?:ftp|www|https?)\W+(?:(?!\.(?:\s|$)|&\w+;)[^"\',;$*^\\(){}<>\[\]\s])+)(\.(?:\s|$)|&\w+;|)') re_mxplink = re.compile(r'\|lc(.*?)\|lt(.*?)\|le', re.DOTALL) From c6aa94365bdb14aa94489f8b8e756234c58f7808 Mon Sep 17 00:00:00 2001 From: Griatch Date: Wed, 11 Sep 2019 20:02:24 +0200 Subject: [PATCH 059/164] ... and another fix to handle line breaks in web client --- evennia/utils/text2html.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/evennia/utils/text2html.py b/evennia/utils/text2html.py index 9c075a22dc4..af12a39e98f 100644 --- a/evennia/utils/text2html.py +++ b/evennia/utils/text2html.py @@ -99,7 +99,7 @@ class TextToHTMLparser(object): re_uline = re.compile("(?:%s)(.*?)(?=%s|%s)" % (underline.replace("[", r"\["), fgstop, bgstop)) re_blink = re.compile("(?:%s)(.*?)(?=%s|%s)" % (blink.replace("[", r"\["), fgstop, bgstop)) re_inverse = re.compile("(?:%s)(.*?)(?=%s|%s)" % (inverse.replace("[", r"\["), fgstop, bgstop)) - re_string = re.compile(r'(?P[<&>])|(?P(?<=\S) +)|(?P [ \t]+)|' + re_string = re.compile(r'(?P[<&>])|(?P(?<=\S) {2,})|(?P [ \t]+)|' r'(?P^ )|(?P\r\n|\r|\n)', re.S | re.M | re.I) re_url = re.compile(r'((?:ftp|www|https?)\W+(?:(?!\.(?:\s|$)|&\w+;)[^"\',;$*^\\(){}<>\[\]\s])+)(\.(?:\s|$)|&\w+;|)') re_mxplink = re.compile(r'\|lc(.*?)\|lt(.*?)\|le', re.DOTALL) @@ -294,6 +294,9 @@ def sub_text(self, match): return cgi.escape(cdict['htmlchars']) elif cdict['lineend']: return '
' + elif cdict['firstspace']: + return '  ' + elif cdict['space'] == '\t': return ' ' * self.tabstop elif cdict['space'] or cdict["spacestart"] or cdict['firstspace']: From 1cd9063e01c9e6ead58702938cf187569ba5d26a Mon Sep 17 00:00:00 2001 From: Greg Taylor Date: Fri, 13 Sep 2019 22:14:06 -0700 Subject: [PATCH 060/164] Remove EGI mention in contrib README.md EGI was moved into core. --- evennia/contrib/README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/evennia/contrib/README.md b/evennia/contrib/README.md index 1748e868e11..402c34170a7 100644 --- a/evennia/contrib/README.md +++ b/evennia/contrib/README.md @@ -68,8 +68,6 @@ things you want from here into your game folder and change them there. ## Contrib packages -* EGI_Client (gtaylor 2016) - Client for reporting game status - to the Evennia game index (games.evennia.com). * In-game Python (Vincent Le Goff 2017) - Allow trusted builders to script objects and events using Python from in-game. * Turnbattle (FlutterSprite 2017) - A turn-based combat engine meant From 6588a4ea2ca2efc4c7c215016aff9630ae363538 Mon Sep 17 00:00:00 2001 From: Griatch Date: Sat, 14 Sep 2019 20:18:07 +0200 Subject: [PATCH 061/164] More fixes to text2ansi regexes. Resolve #1920. Resolve #1924. --- evennia/objects/objects.py | 4 ++-- evennia/utils/text2html.py | 21 +++++++++++++++++---- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/evennia/objects/objects.py b/evennia/objects/objects.py index ad3a0be68ea..72bd9ceb368 100644 --- a/evennia/objects/objects.py +++ b/evennia/objects/objects.py @@ -1946,6 +1946,7 @@ def create(cls, key, account, **kwargs): Kwargs: description (str): Brief description for this object. ip (str): IP address of creator (for object auditing). + All other kwargs will be passed into the create_object call. Returns: character (Object): A newly created Character of the given typeclass. @@ -1953,8 +1954,7 @@ def create(cls, key, account, **kwargs): """ errors = [] - obj = None - + obj = Noneo # Get IP address of creator, if available ip = kwargs.pop('ip', '') diff --git a/evennia/utils/text2html.py b/evennia/utils/text2html.py index af12a39e98f..4933edd9abd 100644 --- a/evennia/utils/text2html.py +++ b/evennia/utils/text2html.py @@ -99,8 +99,9 @@ class TextToHTMLparser(object): re_uline = re.compile("(?:%s)(.*?)(?=%s|%s)" % (underline.replace("[", r"\["), fgstop, bgstop)) re_blink = re.compile("(?:%s)(.*?)(?=%s|%s)" % (blink.replace("[", r"\["), fgstop, bgstop)) re_inverse = re.compile("(?:%s)(.*?)(?=%s|%s)" % (inverse.replace("[", r"\["), fgstop, bgstop)) - re_string = re.compile(r'(?P[<&>])|(?P(?<=\S) {2,})|(?P [ \t]+)|' + re_string = re.compile(r'(?P[<&>])|(?P(?<=\S) )|(?P [ \t]+)|' r'(?P^ )|(?P\r\n|\r|\n)', re.S | re.M | re.I) + re_dblspace = re.compile(r' {2,}', re.M) re_url = re.compile(r'((?:ftp|www|https?)\W+(?:(?!\.(?:\s|$)|&\w+;)[^"\',;$*^\\(){}<>\[\]\s])+)(\.(?:\s|$)|&\w+;|)') re_mxplink = re.compile(r'\|lc(.*?)\|lt(.*?)\|le', re.DOTALL) @@ -259,6 +260,13 @@ def convert_urls(self, text): # change pages (and losing our webclient session). return self.re_url.sub(r'\1\2', text) + def re_double_space(self, text): + """ + HTML will swallow any normal space after the first, so if any slipped + through we must make sure to replace them with "  " + """ + return self.re_dblspace.sub(self.sub_dblspace, text) + def sub_mxp_links(self, match): """ Helper method to be passed to re.sub, @@ -296,15 +304,19 @@ def sub_text(self, match): return '
' elif cdict['firstspace']: return '  ' - elif cdict['space'] == '\t': - return ' ' * self.tabstop - elif cdict['space'] or cdict["spacestart"] or cdict['firstspace']: + text = match.group() + return ' ' if tabstop == 1 else ' ' + " " * tabstop + elif cdict['space'] or cdict["spacestart"]: text = match.group().replace('\t', ' ' * self.tabstop) text = text.replace(' ', ' ') return text return None + def sub_dblspace(self, match): + "clean up double-spaces" + return ' ' + ' ' * (len(match.group()) - 1) + def parse(self, text, strip_ansi=False): """ Main access function, converts a text containing ANSI codes @@ -331,6 +343,7 @@ def parse(self, text, strip_ansi=False): result = self.convert_linebreaks(result) result = self.remove_backspaces(result) result = self.convert_urls(result) + result = self.re_double_space(result) # clean out eventual ansi that was missed # result = parse_ansi(result, strip_ansi=True) From 05fd75198e59c1683eabeaec363e973de3e27635 Mon Sep 17 00:00:00 2001 From: Griatch Date: Sat, 14 Sep 2019 20:45:58 +0200 Subject: [PATCH 062/164] Fix prototype_tag issue in olc. Resolve #1918 --- evennia/prototypes/menus.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/evennia/prototypes/menus.py b/evennia/prototypes/menus.py index 5f38ab8f69c..89646ff0b9c 100644 --- a/evennia/prototypes/menus.py +++ b/evennia/prototypes/menus.py @@ -1748,6 +1748,7 @@ def node_prototype_desc(caller): def _caller_prototype_tags(caller): prototype = _get_menu_prototype(caller) tags = prototype.get("prototype_tags", []) + tags = [tag[0] if isinstance(tag, tuple) else tag for tag in tags] return tags @@ -1771,8 +1772,7 @@ def _add_prototype_tag(caller, tag_string, **kwargs): tag = tag_string.strip().lower() if tag: - prot = _get_menu_prototype(caller) - tags = prot.get('prototype_tags', []) + tags = _caller_prototype_tags(caller) exists = tag in tags if 'delete' in kwargs: From be44882143d6d1f1c9c024162fecb9dd408ba704 Mon Sep 17 00:00:00 2001 From: Griatch Date: Sat, 14 Sep 2019 20:56:36 +0200 Subject: [PATCH 063/164] Make DefaultObject echo confirmation when being puppeted, as per #1923 --- evennia/objects/objects.py | 1 + 1 file changed, 1 insertion(+) diff --git a/evennia/objects/objects.py b/evennia/objects/objects.py index 72bd9ceb368..d6be67e7161 100644 --- a/evennia/objects/objects.py +++ b/evennia/objects/objects.py @@ -1227,6 +1227,7 @@ def at_post_puppet(self, **kwargs): puppeting this Object. """ + self.msg(f"You become |w{self.key}|n.") self.account.db._last_puppet = self def at_pre_unpuppet(self, **kwargs): From a3b466039302839f012168cb8df11cd4ff5c0fcc Mon Sep 17 00:00:00 2001 From: Greg Taylor Date: Sun, 15 Sep 2019 00:05:22 -0700 Subject: [PATCH 064/164] Fix breaking typo in evennia.objects.objects Noneo->Noneo --- evennia/objects/objects.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evennia/objects/objects.py b/evennia/objects/objects.py index d6be67e7161..fd76fae6457 100644 --- a/evennia/objects/objects.py +++ b/evennia/objects/objects.py @@ -1955,7 +1955,7 @@ def create(cls, key, account, **kwargs): """ errors = [] - obj = Noneo + obj = None # Get IP address of creator, if available ip = kwargs.pop('ip', '') From 32492c149259d086e03c806b345044a896f80310 Mon Sep 17 00:00:00 2001 From: Greg Taylor Date: Sat, 14 Sep 2019 12:02:23 -0700 Subject: [PATCH 065/164] Remove unneeded __futures__ imports No longer needed with Python 2 support dropped. --- bin/player-account-step1.patch | 5 ----- evennia/accounts/migrations/0008_auto_20190128_1820.py | 1 - evennia/comms/migrations/0017_auto_20190128_1820.py | 1 - evennia/help/migrations/0003_auto_20190128_1820.py | 2 -- evennia/objects/migrations/0010_auto_20190128_1820.py | 1 - evennia/scripts/migrations/0012_auto_20190128_1820.py | 1 - evennia/server/migrations/0002_auto_20190128_2311.py | 1 - evennia/typeclasses/migrations/0011_auto_20190128_1820.py | 1 - .../migrations/0012_attrs_to_picklev4_may_be_slow.py | 1 - 9 files changed, 14 deletions(-) diff --git a/bin/player-account-step1.patch b/bin/player-account-step1.patch index ede1d2e9b5d..1f540c7c994 100644 --- a/bin/player-account-step1.patch +++ b/bin/player-account-step1.patch @@ -4,7 +4,6 @@ index ec5fc29..62b7936 100644 +++ b/evennia/comms/migrations/0015_auto_20170706_2041.py @@ -2,7 +2,12 @@ # Generated by Django 1.11.2 on 2017-07-06 20:41 - from __future__ import unicode_literals -from django.db import migrations +from django.db import migrations, connection @@ -60,7 +59,6 @@ index b27c75c..6e40252 100644 +++ b/evennia/objects/migrations/0007_objectdb_db_account.py @@ -2,21 +2,31 @@ # Generated by Django 1.11.2 on 2017-07-05 17:27 - from __future__ import unicode_literals -from django.db import migrations, models +from django.db import migrations, models, connection @@ -104,7 +102,6 @@ index 80161a1..10fb225 100644 +++ b/evennia/objects/migrations/0009_remove_objectdb_db_player.py @@ -2,7 +2,12 @@ # Generated by Django 1.11.2 on 2017-07-06 20:41 - from __future__ import unicode_literals -from django.db import migrations +from django.db import migrations, connection @@ -144,7 +141,6 @@ index 99baf70..23f6df9 100644 +++ b/evennia/scripts/migrations/0009_scriptdb_db_account.py @@ -2,21 +2,31 @@ # Generated by Django 1.11.2 on 2017-07-05 17:27 - from __future__ import unicode_literals -from django.db import migrations, models +from django.db import migrations, models, connection @@ -188,7 +184,6 @@ index d3746a5..20fa63f 100644 +++ b/evennia/scripts/migrations/0011_remove_scriptdb_db_player.py @@ -2,7 +2,12 @@ # Generated by Django 1.11.2 on 2017-07-06 20:41 - from __future__ import unicode_literals -from django.db import migrations +from django.db import migrations, connection diff --git a/evennia/accounts/migrations/0008_auto_20190128_1820.py b/evennia/accounts/migrations/0008_auto_20190128_1820.py index dbef6f41fdc..6ae63761977 100644 --- a/evennia/accounts/migrations/0008_auto_20190128_1820.py +++ b/evennia/accounts/migrations/0008_auto_20190128_1820.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- # Generated by Django 1.11.16 on 2019-01-28 18:20 -from __future__ import unicode_literals import django.contrib.auth.validators from django.db import migrations, models diff --git a/evennia/comms/migrations/0017_auto_20190128_1820.py b/evennia/comms/migrations/0017_auto_20190128_1820.py index 3937ff9da04..d07f3cfb658 100644 --- a/evennia/comms/migrations/0017_auto_20190128_1820.py +++ b/evennia/comms/migrations/0017_auto_20190128_1820.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- # Generated by Django 1.11.16 on 2019-01-28 18:20 -from __future__ import unicode_literals from django.conf import settings from django.db import migrations, models diff --git a/evennia/help/migrations/0003_auto_20190128_1820.py b/evennia/help/migrations/0003_auto_20190128_1820.py index 659ec951cb4..d87067fd220 100644 --- a/evennia/help/migrations/0003_auto_20190128_1820.py +++ b/evennia/help/migrations/0003_auto_20190128_1820.py @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- # Generated by Django 1.11.16 on 2019-01-28 18:20 -from __future__ import unicode_literals - from django.db import migrations, models diff --git a/evennia/objects/migrations/0010_auto_20190128_1820.py b/evennia/objects/migrations/0010_auto_20190128_1820.py index 3b23d72e58e..fa161894cf7 100644 --- a/evennia/objects/migrations/0010_auto_20190128_1820.py +++ b/evennia/objects/migrations/0010_auto_20190128_1820.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- # Generated by Django 1.11.16 on 2019-01-28 18:20 -from __future__ import unicode_literals from django.conf import settings import django.core.validators diff --git a/evennia/scripts/migrations/0012_auto_20190128_1820.py b/evennia/scripts/migrations/0012_auto_20190128_1820.py index 6b1f71b159e..80b3e3184eb 100644 --- a/evennia/scripts/migrations/0012_auto_20190128_1820.py +++ b/evennia/scripts/migrations/0012_auto_20190128_1820.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- # Generated by Django 1.11.16 on 2019-01-28 18:20 -from __future__ import unicode_literals from django.conf import settings from django.db import migrations, models diff --git a/evennia/server/migrations/0002_auto_20190128_2311.py b/evennia/server/migrations/0002_auto_20190128_2311.py index b6b8db0f556..5a3e46c0093 100644 --- a/evennia/server/migrations/0002_auto_20190128_2311.py +++ b/evennia/server/migrations/0002_auto_20190128_2311.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- # Generated by Django 1.11.16 on 2019-01-28 23:11 -from __future__ import unicode_literals from base64 import b64encode from django.db import migrations diff --git a/evennia/typeclasses/migrations/0011_auto_20190128_1820.py b/evennia/typeclasses/migrations/0011_auto_20190128_1820.py index 1d7bc4fb5de..d172e821242 100644 --- a/evennia/typeclasses/migrations/0011_auto_20190128_1820.py +++ b/evennia/typeclasses/migrations/0011_auto_20190128_1820.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- # Generated by Django 1.11.16 on 2019-01-28 18:20 -from __future__ import unicode_literals from django.db import migrations, models import evennia.utils.picklefield diff --git a/evennia/typeclasses/migrations/0012_attrs_to_picklev4_may_be_slow.py b/evennia/typeclasses/migrations/0012_attrs_to_picklev4_may_be_slow.py index 2575ce064cc..ed9de5ff2e3 100644 --- a/evennia/typeclasses/migrations/0012_attrs_to_picklev4_may_be_slow.py +++ b/evennia/typeclasses/migrations/0012_attrs_to_picklev4_may_be_slow.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- # Generated by Django 1.11.16 on 2019-01-30 20:56 -from __future__ import unicode_literals from copy import deepcopy from base64 import b64encode, b64decode From c62cc1f5303b3f6df2626de0093cfde6472f4f9f Mon Sep 17 00:00:00 2001 From: Greg Taylor Date: Sat, 14 Sep 2019 22:41:52 -0700 Subject: [PATCH 066/164] Remove unused utils.clean_object_caches func Sounds like this used to have a purpose but no longer does. --- evennia/utils/utils.py | 41 ----------------------------------------- 1 file changed, 41 deletions(-) diff --git a/evennia/utils/utils.py b/evennia/utils/utils.py index 6c45bd8d18f..cef71afe05d 100644 --- a/evennia/utils/utils.py +++ b/evennia/utils/utils.py @@ -6,8 +6,6 @@ be of use when designing your own game. """ -from future.utils import viewkeys, raise_ - import os import sys import imp @@ -1052,45 +1050,6 @@ def delay(timedelay, callback, *args, **kwargs): return _TASK_HANDLER.add(timedelay, callback, *args, **kwargs) -_TYPECLASSMODELS = None -_OBJECTMODELS = None - - -def clean_object_caches(obj): - """ - Clean all object caches on the given object. - - Args: - obj (Object instace): An object whose caches to clean. - - Notes: - This is only the contents cache these days. - - """ - global _TYPECLASSMODELS, _OBJECTMODELS - if not _TYPECLASSMODELS: - from evennia.typeclasses import models as _TYPECLASSMODELS - - if not obj: - return - # contents cache - try: - _SA(obj, "_contents_cache", None) - except AttributeError: - # if the cache cannot be reached, move on anyway - pass - - # on-object property cache - [_DA(obj, cname) for cname in viewkeys(obj.__dict__) - if cname.startswith("_cached_db_")] - try: - hashid = _GA(obj, "hashid") - _TYPECLASSMODELS._ATTRIBUTE_CACHE[hashid] = {} - except AttributeError: - # skip caching - pass - - _PPOOL = None _PCMD = None _PROC_ERR = "A process has ended with a probable error condition: process ended by signal 9." From 8eae0c13b9ef4bd39e7111e8e622d64caa10da94 Mon Sep 17 00:00:00 2001 From: Greg Taylor Date: Sat, 14 Sep 2019 22:42:58 -0700 Subject: [PATCH 067/164] Remove py3.6 handler for ModuleImportError We require 3.7+ now, no need. --- evennia/utils/utils.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/evennia/utils/utils.py b/evennia/utils/utils.py index cef71afe05d..8608dfe5464 100644 --- a/evennia/utils/utils.py +++ b/evennia/utils/utils.py @@ -33,14 +33,6 @@ _MULTIMATCH_TEMPLATE = settings.SEARCH_MULTIMATCH_TEMPLATE _EVENNIA_DIR = settings.EVENNIA_DIR _GAME_DIR = settings.GAME_DIR - - -# ModuleNotFoundError only in py3.6, handle both -try: - from builtins import ModuleNotFoundError -except ImportError: - ModuleNotFoundError = ImportError - ENCODINGS = settings.ENCODINGS _GA = object.__getattribute__ _SA = object.__setattr__ From aa43da1cf3a95f3b33d2dafa51bfe32e85708014 Mon Sep 17 00:00:00 2001 From: Greg Taylor Date: Sat, 14 Sep 2019 22:44:33 -0700 Subject: [PATCH 068/164] Clean up utils gc/sys imports Move to top and import the modules instead of funcs from them. Keeps the scope cleaner. --- evennia/utils/utils.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/evennia/utils/utils.py b/evennia/utils/utils.py index 8608dfe5464..88b69258d18 100644 --- a/evennia/utils/utils.py +++ b/evennia/utils/utils.py @@ -7,6 +7,7 @@ """ import os +import gc import sys import imp import types @@ -1644,10 +1645,6 @@ def get_evennia_pids(): return None, None -from gc import get_referents -from sys import getsizeof - - def deepsize(obj, max_depth=4): """ Get not only size of the given object, but also the size of @@ -1673,14 +1670,14 @@ def deepsize(obj, max_depth=4): def _recurse(o, dct, depth): if 0 <= max_depth < depth: return - for ref in get_referents(o): + for ref in gc.get_referents(o): idr = id(ref) if idr not in dct: - dct[idr] = (ref, getsizeof(ref, default=0)) + dct[idr] = (ref, sys.getsizeof(ref, default=0)) _recurse(ref, dct, depth + 1) sizedict = {} _recurse(obj, sizedict, 0) - size = getsizeof(obj) + sum([p[1] for p in sizedict.values()]) + size = sys.getsizeof(obj) + sum([p[1] for p in sizedict.values()]) return size From 97f8bf8a6f54436c9bdf5111cbb26a193ac69169 Mon Sep 17 00:00:00 2001 From: Greg Taylor Date: Sat, 14 Sep 2019 23:47:00 -0700 Subject: [PATCH 069/164] Remove uses of the 'future' py2->3 module This is no longer needed, now that we are Py3.7+ only. One layer of indirection removed, and one less dependency. --- evennia/accounts/accounts.py | 3 +-- evennia/commands/cmdset.py | 6 ++---- evennia/commands/cmdsethandler.py | 13 ++++++------- evennia/commands/command.py | 4 +--- evennia/comms/comms.py | 4 ++-- evennia/contrib/tutorial_world/objects.py | 3 +-- evennia/objects/objects.py | 3 +-- evennia/scripts/scripts.py | 3 +-- evennia/server/inputfuncs.py | 1 - evennia/server/portal/irc.py | 7 +++---- evennia/server/sessionhandler.py | 3 +-- evennia/utils/ansi.py | 3 +-- evennia/utils/evtable.py | 6 ++---- evennia/utils/idmapper/models.py | 9 ++++----- requirements.txt | 1 - win_requirements.txt | 1 - 16 files changed, 26 insertions(+), 44 deletions(-) diff --git a/evennia/accounts/accounts.py b/evennia/accounts/accounts.py index 4920f734a7d..5214a58fe2b 100644 --- a/evennia/accounts/accounts.py +++ b/evennia/accounts/accounts.py @@ -37,7 +37,6 @@ from evennia.utils.optionhandler import OptionHandler from django.utils.translation import ugettext as _ -from future.utils import with_metaclass from random import getrandbits __all__ = ("DefaultAccount",) @@ -113,7 +112,7 @@ def count(self): return len(self.get()) -class DefaultAccount(with_metaclass(TypeclassBase, AccountDB)): +class DefaultAccount(AccountDB, metaclass=TypeclassBase): """ This is the base Typeclass for all Accounts. Accounts represent the person playing the game and tracks account info, password diff --git a/evennia/commands/cmdset.py b/evennia/commands/cmdset.py index 95bbf9da225..596c01f745e 100644 --- a/evennia/commands/cmdset.py +++ b/evennia/commands/cmdset.py @@ -26,8 +26,6 @@ to affect the low-priority cmdset. Ex: A1,A3 + B1,B2,B4,B5 = B2,B4,B5 """ -from future.utils import listvalues, with_metaclass - from weakref import WeakKeyDictionary from django.utils.translation import ugettext as _ from evennia.utils.utils import inherits_from, is_iter @@ -57,7 +55,7 @@ def __init__(cls, *args, **kwargs): super().__init__(*args, **kwargs) -class CmdSet(with_metaclass(_CmdSetMeta, object)): +class CmdSet(object, metaclass=_CmdSetMeta): """ This class describes a unique cmdset that understands priorities. CmdSets can be merged and made to perform various set operations @@ -585,7 +583,7 @@ def make_unique(self, caller): unique[cmd.key] = cmd else: unique[cmd.key] = cmd - self.commands = listvalues(unique) + self.commands = list(unique.values()) def get_all_cmd_keys_and_aliases(self, caller=None): """ diff --git a/evennia/commands/cmdsethandler.py b/evennia/commands/cmdsethandler.py index d332c7fcd28..bc0b759031d 100644 --- a/evennia/commands/cmdsethandler.py +++ b/evennia/commands/cmdsethandler.py @@ -64,7 +64,6 @@ the 'Fishing' set. Fishing from a boat? No problem! """ from builtins import object -from future.utils import raise_ import sys from traceback import format_exc from importlib import import_module @@ -172,22 +171,22 @@ def import_cmdset(path, cmdsetobj, emit_to_obj=None, no_logging=False): if not cmdsetclass: try: module = import_module(modpath, package="evennia") - except ImportError: + except ImportError as exc: if len(trace()) > 2: # error in module, make sure to not hide it. - exc = sys.exc_info() - raise_(exc[1], None, exc[2]) + _, _, tb = sys.exc_info() + raise exc.with_traceback(tb) else: # try next suggested path errstring += _("\n(Unsuccessfully tried '%s')." % python_path) continue try: cmdsetclass = getattr(module, classname) - except AttributeError: + except AttributeError as exc: if len(trace()) > 2: # Attribute error within module, don't hide it - exc = sys.exc_info() - raise_(exc[1], None, exc[2]) + _, _, tb = sys.exc_info() + raise exc.with_traceback(tb) else: errstring += _("\n(Unsuccessfully tried '%s')." % python_path) continue diff --git a/evennia/commands/command.py b/evennia/commands/command.py index ce9fbb1885c..6858d24671e 100644 --- a/evennia/commands/command.py +++ b/evennia/commands/command.py @@ -16,8 +16,6 @@ from evennia.utils.evtable import EvTable from evennia.utils.ansi import ANSIString -from future.utils import with_metaclass - def _init_command(cls, **kwargs): """ @@ -99,7 +97,7 @@ def __init__(cls, *args, **kwargs): # parsing errors. -class Command(with_metaclass(CommandMeta, object)): +class Command(object, metaclass=CommandMeta): """ Base command diff --git a/evennia/comms/comms.py b/evennia/comms/comms.py index 8c885d3458d..309f3cd9ae4 100644 --- a/evennia/comms/comms.py +++ b/evennia/comms/comms.py @@ -11,11 +11,11 @@ from evennia.comms.managers import ChannelManager from evennia.utils import create, logger from evennia.utils.utils import make_iter -from future.utils import with_metaclass + _CHANNEL_HANDLER = None -class DefaultChannel(with_metaclass(TypeclassBase, ChannelDB)): +class DefaultChannel(ChannelDB, metaclass=TypeclassBase): """ This is the base class for all Channel Comms. Inherit from this to create different types of communication channels. diff --git a/evennia/contrib/tutorial_world/objects.py b/evennia/contrib/tutorial_world/objects.py index 7a9fe096ba6..a4580fb5181 100644 --- a/evennia/contrib/tutorial_world/objects.py +++ b/evennia/contrib/tutorial_world/objects.py @@ -18,7 +18,6 @@ WeaponRack """ -from future.utils import listvalues import random @@ -528,7 +527,7 @@ def func(self): self.obj.db.root_pos = root_pos # Check victory condition - if listvalues(root_pos).count(0) == 0: # no roots in middle position + if list(root_pos.values()).count(0) == 0: # no roots in middle position # This will affect the cmd: lock of CmdPressButton self.obj.db.button_exposed = True self.caller.msg("Holding aside the root you think you notice something behind it ...") diff --git a/evennia/objects/objects.py b/evennia/objects/objects.py index fd76fae6457..61dd3aed44a 100644 --- a/evennia/objects/objects.py +++ b/evennia/objects/objects.py @@ -8,7 +8,6 @@ import time import inflect from builtins import object -from future.utils import with_metaclass from collections import defaultdict from django.conf import settings @@ -179,7 +178,7 @@ def count(self): # Base class to inherit from. -class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)): +class DefaultObject(ObjectDB, metaclass=TypeclassBase): """ This is the root typeclass object, representing all entities that have an actual presence in-game. DefaultObjects generally have a diff --git a/evennia/scripts/scripts.py b/evennia/scripts/scripts.py index b7e32963194..72f46d8e216 100644 --- a/evennia/scripts/scripts.py +++ b/evennia/scripts/scripts.py @@ -13,7 +13,6 @@ from evennia.scripts.models import ScriptDB from evennia.scripts.manager import ScriptManager from evennia.utils import create, logger -from future.utils import with_metaclass __all__ = ["DefaultScript", "DoNothing", "Store"] @@ -144,7 +143,7 @@ def next_call_time(self): return None -class ScriptBase(with_metaclass(TypeclassBase, ScriptDB)): +class ScriptBase(ScriptDB, metaclass=TypeclassBase): """ Base class for scripts. Don't inherit from this, inherit from the class `DefaultScript` below instead. diff --git a/evennia/server/inputfuncs.py b/evennia/server/inputfuncs.py index 4eee9911881..08427658b9f 100644 --- a/evennia/server/inputfuncs.py +++ b/evennia/server/inputfuncs.py @@ -19,7 +19,6 @@ settings.INPUT_FUNC_MODULES. """ -from future.utils import viewkeys import importlib from codecs import lookup as codecs_lookup diff --git a/evennia/server/portal/irc.py b/evennia/server/portal/irc.py index 2b616f2ce11..e4273e4cc50 100644 --- a/evennia/server/portal/irc.py +++ b/evennia/server/portal/irc.py @@ -3,7 +3,6 @@ The bot then pipes what is being said between the IRC channel and one or more Evennia channels. """ -from future.utils import viewkeys, viewvalues, viewitems import re from twisted.application import internet @@ -90,15 +89,15 @@ )) # ansi->irc RE_ANSI_COLOR = re.compile(r"|".join( - [re.escape(key) for key in viewkeys(IRC_COLOR_MAP)]), re.DOTALL) + [re.escape(key) for key in IRC_COLOR_MAP.keys()]), re.DOTALL) RE_MXP = re.compile(r'\|lc(.*?)\|lt(.*?)\|le', re.DOTALL) RE_ANSI_ESCAPES = re.compile(r"(%s)" % "|".join(("{{", "%%", "\\\\")), re.DOTALL) # irc->ansi _CLR_LIST = [re.escape(val) - for val in sorted(viewvalues(IRC_COLOR_MAP), key=len, reverse=True) if val.strip()] + for val in sorted(IRC_COLOR_MAP.values(), key=len, reverse=True) if val.strip()] _CLR_LIST = _CLR_LIST[-2:] + _CLR_LIST[:-2] RE_IRC_COLOR = re.compile(r"|".join(_CLR_LIST), re.DOTALL) -ANSI_COLOR_MAP = dict((tup[1], tup[0]) for tup in viewitems(IRC_COLOR_MAP) if tup[1].strip()) +ANSI_COLOR_MAP = dict((tup[1], tup[0]) for tup in IRC_COLOR_MAP.items() if tup[1].strip()) def parse_ansi_to_irc(string): diff --git a/evennia/server/sessionhandler.py b/evennia/server/sessionhandler.py index cdb9a1ab1d0..cfded919065 100644 --- a/evennia/server/sessionhandler.py +++ b/evennia/server/sessionhandler.py @@ -14,7 +14,6 @@ """ import time from builtins import object -from future.utils import listvalues from django.conf import settings from evennia.commands.cmdhandler import CMD_LOGINSTART @@ -148,7 +147,7 @@ def get_sessions(self, include_unloggedin=False): """ if include_unloggedin: - return listvalues(self) + return list(self.values()) else: return [session for session in self.values() if session.logged_in] diff --git a/evennia/utils/ansi.py b/evennia/utils/ansi.py index a10c6a4e2ca..7eec5f95b31 100644 --- a/evennia/utils/ansi.py +++ b/evennia/utils/ansi.py @@ -25,7 +25,6 @@ from evennia.utils import logger from evennia.utils.utils import to_str -from future.utils import with_metaclass # ANSI definitions @@ -631,7 +630,7 @@ def __init__(cls, *args, **kwargs): super().__init__(*args, **kwargs) -class ANSIString(with_metaclass(ANSIMeta, str)): +class ANSIString(str, metaclass=ANSIMeta): """ Unicode-like object that is aware of ANSI codes. diff --git a/evennia/utils/evtable.py b/evennia/utils/evtable.py index 29c4340d005..90cf1d2f440 100644 --- a/evennia/utils/evtable.py +++ b/evennia/utils/evtable.py @@ -111,8 +111,6 @@ """ -from future.utils import listitems - from django.conf import settings from textwrap import TextWrapper from copy import deepcopy, copy @@ -1464,7 +1462,7 @@ def add_column(self, *args, **kwargs): """ # this will replace default options with new ones without changing default - options = dict(listitems(self.options) + listitems(kwargs)) + options = dict(list(self.options.items()) + list(kwargs.items())) xpos = kwargs.get("xpos", None) column = EvColumn(*args, **options) @@ -1529,7 +1527,7 @@ def add_row(self, *args, **kwargs): """ # this will replace default options with new ones without changing default row = list(args) - options = dict(listitems(self.options) + listitems(kwargs)) + options = dict(list(self.options.items()) + list(kwargs.items())) ypos = kwargs.get("ypos", None) wtable = self.ncols diff --git a/evennia/utils/idmapper/models.py b/evennia/utils/idmapper/models.py index 57a7cbaacd4..b8f2db04449 100644 --- a/evennia/utils/idmapper/models.py +++ b/evennia/utils/idmapper/models.py @@ -8,7 +8,6 @@ """ from builtins import object -from future.utils import listitems, listvalues, with_metaclass import os import threading @@ -197,7 +196,7 @@ def fdel(cls): return _del(cls, fieldname) if editable else _del_nonedit(cls, fi return # dynamically create the wrapper properties for all fields not already handled # (manytomanyfields are always handlers) - for fieldname, field in ((fname, field) for fname, field in listitems(attrs) + for fieldname, field in ((fname, field) for fname, field in list(attrs.items()) if fname.startswith("db_") and type(field).__name__ != "ManyToManyField"): foreignkey = type(field).__name__ == "ForeignKey" wrappername = "dbid" if fieldname == "id" else fieldname.replace("db_", "", 1) @@ -208,7 +207,7 @@ def fdel(cls): return _del(cls, fieldname) if editable else _del_nonedit(cls, fi return super().__new__(cls, name, bases, attrs) -class SharedMemoryModel(with_metaclass(SharedMemoryModelBase, Model)): +class SharedMemoryModel(Model, metaclass=SharedMemoryModelBase): """ Base class for idmapped objects. Inherit from `this`. """ @@ -291,7 +290,7 @@ def get_all_cached_instances(cls): Return the objects so far cached by idmapper for this class. """ - return listvalues(cls.__dbclass__.__instance_cache__) + return list(cls.__dbclass__.__instance_cache__.values()) @classmethod def _flush_cached_by_key(cls, key, force=True): @@ -457,7 +456,7 @@ def _prepare(cls): cls.__dbclass__.__instance_cache__ = WeakValueDictionary() -class WeakSharedMemoryModel(with_metaclass(WeakSharedMemoryModelBase, SharedMemoryModel)): +class WeakSharedMemoryModel(SharedMemoryModel, metaclass=WeakSharedMemoryModelBase): """ Uses a WeakValue dictionary for caching instead of a regular one diff --git a/requirements.txt b/requirements.txt index 4e3e79ab8a1..63e7bdc70ca 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,7 +5,6 @@ django >= 2.2.4, < 2.3 twisted >= 19.2.1, < 20.0.0 pillow == 5.2.0 pytz -future >= 0.15.2 django-sekizai inflect autobahn >= 17.9.3 diff --git a/win_requirements.txt b/win_requirements.txt index ea3bb46fb75..895da341bc4 100644 --- a/win_requirements.txt +++ b/win_requirements.txt @@ -8,7 +8,6 @@ django >= 2.2.4, < 2.3 twisted >= 19.2.1, < 20.0.0 pillow == 5.2.0 pytz -future >= 0.15.2 django-sekizai autobahn >= 17.9.3 inflect From efaa4ee5355b74707c5f8036c2ba5e7095283ebb Mon Sep 17 00:00:00 2001 From: Greg Taylor Date: Sun, 15 Sep 2019 00:37:30 -0700 Subject: [PATCH 070/164] Condense requirements files down to one PEP 508 defines a way to specify platform-specific requirements. Rather than having to manage two different requirements files, let's crunch them down to one and use that facility. For more details on how this works, see: https://www.python.org/dev/peps/pep-0508/ --- requirements.txt | 5 ++++- setup.py | 6 ++---- win_requirements.txt | 19 ------------------- 3 files changed, 6 insertions(+), 24 deletions(-) delete mode 100644 win_requirements.txt diff --git a/requirements.txt b/requirements.txt index 4e3e79ab8a1..c265a346395 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -# Evennia dependencies, for Linux/Mac platforms +# Evennia dependencies # general django >= 2.2.4, < 2.3 @@ -14,3 +14,6 @@ model_mommy # testing mock >= 1.0.1 anything + +# windows-specific +pypiwin32;platform_system=="Windows" diff --git a/setup.py b/setup.py index 68d8bdbeb82..8b077a9775f 100644 --- a/setup.py +++ b/setup.py @@ -10,11 +10,9 @@ def get_requirements(): """ - To update the requirements for Evennia, edit the requirements.txt - file, or win_requirements.txt for Windows platforms. + To update the requirements for Evennia, edit the requirements.txt file. """ - filename = 'win_requirements.txt' if OS_WINDOWS else 'requirements.txt' - with open(filename, 'r') as f: + with open('requirements.txt', 'r') as f: req_lines = f.readlines() reqs = [] for line in req_lines: diff --git a/win_requirements.txt b/win_requirements.txt deleted file mode 100644 index ea3bb46fb75..00000000000 --- a/win_requirements.txt +++ /dev/null @@ -1,19 +0,0 @@ -# Evennia dependencies, for Windows platform - -# windows specific -pypiwin32 - -# general -django >= 2.2.4, < 2.3 -twisted >= 19.2.1, < 20.0.0 -pillow == 5.2.0 -pytz -future >= 0.15.2 -django-sekizai -autobahn >= 17.9.3 -inflect -model_mommy - -# testing -mock >= 1.0.1 -anything From b026b65b084834c03d2aa6a84013fe8c549a1cad Mon Sep 17 00:00:00 2001 From: Greg Taylor Date: Sat, 14 Sep 2019 11:26:07 -0700 Subject: [PATCH 071/164] Bump min Django version to 2.2.5 Bug fix release. For more details, see: https://docs.djangoproject.com/en/2.2/releases/2.2.5/ --- evennia/server/evennia_launcher.py | 2 +- requirements.txt | 2 +- win_requirements.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/evennia/server/evennia_launcher.py b/evennia/server/evennia_launcher.py index c9fbef49dfa..86dd99aa439 100644 --- a/evennia/server/evennia_launcher.py +++ b/evennia/server/evennia_launcher.py @@ -92,7 +92,7 @@ PYTHON_MIN = '3.7' TWISTED_MIN = '18.0.0' DJANGO_MIN = '2.1' -DJANGO_REC = '2.2.4' +DJANGO_REC = '2.2.5' try: sys.path[1] = EVENNIA_ROOT diff --git a/requirements.txt b/requirements.txt index 4e3e79ab8a1..f9284a2859c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ # Evennia dependencies, for Linux/Mac platforms # general -django >= 2.2.4, < 2.3 +django >= 2.2.5, < 2.3 twisted >= 19.2.1, < 20.0.0 pillow == 5.2.0 pytz diff --git a/win_requirements.txt b/win_requirements.txt index ea3bb46fb75..99e5f6abfa6 100644 --- a/win_requirements.txt +++ b/win_requirements.txt @@ -4,7 +4,7 @@ pypiwin32 # general -django >= 2.2.4, < 2.3 +django >= 2.2.5, < 2.3 twisted >= 19.2.1, < 20.0.0 pillow == 5.2.0 pytz From 5cb98e5a928508da9f0f39643eba9a9c56ab41ed Mon Sep 17 00:00:00 2001 From: Griatch Date: Sun, 15 Sep 2019 13:03:38 +0200 Subject: [PATCH 072/164] Messed up the merge; now removed win-req file --- win_requirements.txt | 18 ------------------ 1 file changed, 18 deletions(-) delete mode 100644 win_requirements.txt diff --git a/win_requirements.txt b/win_requirements.txt deleted file mode 100644 index c3a7396ae3c..00000000000 --- a/win_requirements.txt +++ /dev/null @@ -1,18 +0,0 @@ -# Evennia dependencies, for Windows platform - -# windows specific -pypiwin32 - -# general -django >= 2.2.5, < 2.3 -twisted >= 19.2.1, < 20.0.0 -pillow == 5.2.0 -pytz -django-sekizai -autobahn >= 17.9.3 -inflect -model_mommy - -# testing -mock >= 1.0.1 -anything From cdb9e3290ab0ea4b13bae33ecc1062fac8c380a1 Mon Sep 17 00:00:00 2001 From: Griatch Date: Sun, 15 Sep 2019 14:09:47 +0200 Subject: [PATCH 073/164] Fix MSSP connect traceback. Resolve #1930. Also add mock external_discord_hello inputfunc for Mudlet. --- evennia/server/inputfuncs.py | 10 ++++++++++ evennia/server/portal/mssp.py | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/evennia/server/inputfuncs.py b/evennia/server/inputfuncs.py index 08427658b9f..ed1e7782970 100644 --- a/evennia/server/inputfuncs.py +++ b/evennia/server/inputfuncs.py @@ -541,3 +541,13 @@ def msdp_unreport(session, *args, **kwargs): """ unmonitor(session, *args, **kwargs) + + +# client specific + +def external_discord_hello(session, *args, **kwargs): + """ + Sent by Mudlet as a greeting; added here to avoid + logging a missing inputfunc for it. + """ + pass diff --git a/evennia/server/portal/mssp.py b/evennia/server/portal/mssp.py index 78dd12b699a..7bec2bb725a 100644 --- a/evennia/server/portal/mssp.py +++ b/evennia/server/portal/mssp.py @@ -115,7 +115,7 @@ def do_mssp(self, option): if MSSPTable_CUSTOM: self.mssp_table.update(MSSPTable_CUSTOM) - varlist = '' + varlist = b'' for variable, value in self.mssp_table.items(): if callable(value): value = value() From 013c5a1884ebe74ac5dedd7df03c31ba2d3c3dc8 Mon Sep 17 00:00:00 2001 From: Greg Taylor Date: Sun, 15 Sep 2019 17:28:10 -0700 Subject: [PATCH 074/164] Remove pillow from requirements This was originally suggested due to an imagefield, but that is no longer present. Since Pillow is not required to run Evennia, we should remove it and let the users opt into it if they end up wanting/needing it for their custom code. --- requirements.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 1abc64a5b6f..cc23261fa14 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,7 +3,6 @@ # general django >= 2.2.5, < 2.3 twisted >= 19.2.1, < 20.0.0 -pillow == 5.2.0 pytz django-sekizai inflect From 3e152a3b5721e4db4cd6bb5e3882a508c99a8b7f Mon Sep 17 00:00:00 2001 From: Greg Taylor Date: Sun, 15 Sep 2019 18:21:33 -0700 Subject: [PATCH 075/164] Refactor mod_import to use importlib Switch from the deprecated imp to importlib. Also add tests and clean up logic flow. This should be quite a bit faster than the old implementation as well. --- evennia/utils/tests/test_utils.py | 26 ++++++++++++ evennia/utils/utils.py | 68 +++++++++++++++---------------- 2 files changed, 60 insertions(+), 34 deletions(-) diff --git a/evennia/utils/tests/test_utils.py b/evennia/utils/tests/test_utils.py index 36b32318b31..699fdb7756b 100644 --- a/evennia/utils/tests/test_utils.py +++ b/evennia/utils/tests/test_utils.py @@ -5,6 +5,8 @@ """ +import os.path + import mock from django.test import TestCase from datetime import datetime @@ -203,3 +205,27 @@ def test_datetimes(self): self.assertEqual(utils.datetime_format(dtobj), "19:54") dtobj = datetime(2019, 8, 28, 21, 32) self.assertEqual(utils.datetime_format(dtobj), "21:32:00") + + +class TestImportFunctions(TestCase): + def _t_dir_file(self, filename): + testdir = os.path.dirname(os.path.abspath(__file__)) + return os.path.join(testdir, filename) + + def test_mod_import(self): + loaded_mod = utils.mod_import('evennia.utils.ansi') + self.assertIsNotNone(loaded_mod) + + def test_mod_import_invalid(self): + loaded_mod = utils.mod_import('evennia.utils.invalid_module') + self.assertIsNone(loaded_mod) + + def test_mod_import_from_path(self): + test_path = self._t_dir_file('test_eveditor.py') + loaded_mod = utils.mod_import_from_path(test_path) + self.assertIsNotNone(loaded_mod) + + def test_mod_import_from_path_invalid(self): + test_path = self._t_dir_file('invalid_filename.py') + loaded_mod = utils.mod_import_from_path(test_path) + self.assertIsNone(loaded_mod) diff --git a/evennia/utils/utils.py b/evennia/utils/utils.py index 88b69258d18..ad2a500eabf 100644 --- a/evennia/utils/utils.py +++ b/evennia/utils/utils.py @@ -9,7 +9,6 @@ import os import gc import sys -import imp import types import math import re @@ -17,6 +16,7 @@ import random import inspect import traceback +import importlib.machinery from twisted.internet.task import deferLater from twisted.internet.defer import returnValue # noqa - used as import target from os.path import join as osjoin @@ -1166,6 +1166,30 @@ def has_parent(basepath, obj): return False +def mod_import_from_path(path): + """ + Load a Python module at the specified path. + + Args: + path (str): An absolute path to a Python module to load. + + Returns: + (module or None): An imported module if the path was a valid + Python module. Returns `None` if the import failed. + + """ + if not os.path.isabs(path): + path = os.path.abspath(path) + dirpath, filename = path.rsplit(os.path.sep, 1) + modname = filename.rstrip('.py') + + try: + return importlib.machinery.SourceFileLoader(modname, path).load_module() + except OSError: + logger.log_trace(f"Could not find module '{modname}' ({modname}.py) at path '{dirpath}'") + return None + + def mod_import(module): """ A generic Python module loader. @@ -1173,52 +1197,28 @@ def mod_import(module): Args: module (str, module): This can be either a Python path (dot-notation like `evennia.objects.models`), an absolute path - (e.g. `/home/eve/evennia/evennia/objects.models.py`) or an + (e.g. `/home/eve/evennia/evennia/objects/models.py`) or an already imported module object (e.g. `models`) Returns: - module (module or None): An imported module. If the input argument was + (module or None): An imported module. If the input argument was already a module, this is returned as-is, otherwise the path is parsed and imported. Returns `None` and logs error if import failed. """ - if not module: return None if isinstance(module, types.ModuleType): # if this is already a module, we are done - mod = module - else: - # first try to import as a python path - try: - mod = __import__(module, fromlist=["None"]) - except ImportError as ex: - # check just where the ImportError happened (it could have been - # an erroneous import inside the module as well). This is the - # trivial way to do it ... - if not str(ex).startswith("No module named "): - raise + return module - # error in this module. Try absolute path import instead + if module.endswith('.py') and os.path.exists(module): + return mod_import_from_path(module) - if not os.path.isabs(module): - module = os.path.abspath(module) - path, filename = module.rsplit(os.path.sep, 1) - modname = re.sub(r"\.py$", "", filename) - - try: - result = imp.find_module(modname, [path]) - except ImportError: - logger.log_trace("Could not find module '%s' (%s.py) at path '%s'" % (modname, modname, path)) - return None - try: - mod = imp.load_module(modname, *result) - except ImportError: - logger.log_trace("Could not find or import module %s at path '%s'" % (modname, path)) - mod = None - # we have to close the file handle manually - result[0].close() - return mod + try: + return import_module(module) + except ImportError: + return None def all_from_module(module): From bb3db818cf922f422df33a383c4f7d6fe0d7a176 Mon Sep 17 00:00:00 2001 From: Greg Taylor Date: Sun, 15 Sep 2019 18:24:48 -0700 Subject: [PATCH 076/164] Switch to module imports for importlib in utils The present day guidance is to lean towards module imports for the stdlib modules. Switch importlib imports to this instead of plucking out the functions that we need. This makes it more immediately apparent as to where the functions are coming from in the application logic. --- evennia/utils/utils.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/evennia/utils/utils.py b/evennia/utils/utils.py index ad2a500eabf..64aa827aeb6 100644 --- a/evennia/utils/utils.py +++ b/evennia/utils/utils.py @@ -16,12 +16,12 @@ import random import inspect import traceback +import importlib +import importlib.util import importlib.machinery from twisted.internet.task import deferLater from twisted.internet.defer import returnValue # noqa - used as import target from os.path import join as osjoin -from importlib import import_module -from importlib.util import find_spec from inspect import ismodule, trace, getmembers, getmodule, getmro from collections import defaultdict, OrderedDict from twisted.internet import threads, reactor @@ -1216,7 +1216,7 @@ def mod_import(module): return mod_import_from_path(module) try: - return import_module(module) + return importlib.import_module(module) except ImportError: return None @@ -1378,7 +1378,7 @@ def fuzzy_import_from_module(path, variable, default=None, defaultpaths=None): paths = [path] + make_iter(defaultpaths) for modpath in paths: try: - mod = import_module(modpath) + mod = importlib.import_module(modpath) except ImportError as ex: if not str(ex).startswith("No module named %s" % modpath): # this means the module was found but it @@ -1420,13 +1420,13 @@ class (Class): An uninstatiated class recovered from path. raise ImportError("the path '%s' is not on the form modulepath.Classname." % path) try: - if not find_spec(testpath, package='evennia'): + if not importlib.util.find_spec(testpath, package='evennia'): continue except ModuleNotFoundError: continue try: - mod = import_module(testpath, package='evennia') + mod = importlib.import_module(testpath, package='evennia') except ModuleNotFoundError: err = traceback.format_exc(30) break From 095ef9df93f07f57e3d78573aa5228fc85f8ac94 Mon Sep 17 00:00:00 2001 From: Greg Taylor Date: Sun, 15 Sep 2019 20:29:59 -0700 Subject: [PATCH 077/164] Remove builtins imports This module is not intended to be used directly in most cases, per: https://docs.python.org/3/library/builtins.html None of our usages warrant the explicit import. We also avoid some confusion as folks dig to see what we are doing to require importing builtins directly. --- evennia/accounts/admin.py | 2 -- evennia/accounts/models.py | 2 -- evennia/commands/cmdsethandler.py | 1 - evennia/commands/command.py | 2 -- evennia/commands/default/account.py | 2 -- evennia/commands/default/batchprocess.py | 1 - evennia/commands/default/building.py | 2 -- evennia/comms/channelhandler.py | 2 -- evennia/comms/models.py | 2 -- evennia/contrib/barter.py | 2 -- evennia/contrib/dice.py | 2 -- evennia/contrib/rplanguage.py | 2 -- evennia/contrib/rpsystem.py | 2 -- evennia/help/admin.py | 2 -- evennia/help/models.py | 2 -- evennia/locks/lockhandler.py | 2 -- evennia/objects/admin.py | 2 -- evennia/objects/manager.py | 1 - evennia/objects/models.py | 2 -- evennia/objects/objects.py | 1 - evennia/scripts/models.py | 2 -- evennia/scripts/monitorhandler.py | 1 - evennia/scripts/scripthandler.py | 2 -- evennia/scripts/tickerhandler.py | 1 - evennia/server/portal/mccp.py | 1 - evennia/server/portal/mssp.py | 1 - evennia/server/portal/mxp.py | 1 - evennia/server/portal/naws.py | 1 - evennia/server/portal/portal.py | 2 -- evennia/server/portal/rss.py | 2 -- evennia/server/portal/ssh.py | 1 - evennia/server/portal/suppress_ga.py | 1 - evennia/server/portal/telnet_oob.py | 1 - evennia/server/portal/ttype.py | 1 - evennia/server/profiling/dummyrunner.py | 2 -- evennia/server/server.py | 1 - evennia/server/serversession.py | 2 -- evennia/server/session.py | 1 - evennia/server/sessionhandler.py | 1 - evennia/typeclasses/attributes.py | 1 - evennia/typeclasses/models.py | 2 -- evennia/typeclasses/tags.py | 1 - evennia/utils/ansi.py | 1 - evennia/utils/batchprocessors.py | 2 -- evennia/utils/dbserialize.py | 2 -- evennia/utils/eveditor.py | 2 -- evennia/utils/evform.py | 2 -- evennia/utils/evmenu.py | 1 - evennia/utils/evmore.py | 2 -- evennia/utils/idmapper/models.py | 2 -- evennia/utils/idmapper/tests.py | 2 -- evennia/utils/picklefield.py | 1 - evennia/utils/text2html.py | 2 -- 53 files changed, 84 deletions(-) diff --git a/evennia/accounts/admin.py b/evennia/accounts/admin.py index 9986e2bf7cb..2cdbb013d1b 100644 --- a/evennia/accounts/admin.py +++ b/evennia/accounts/admin.py @@ -2,8 +2,6 @@ # This sets up how models are displayed # in the web admin interface. # -from builtins import object - from django import forms from django.conf import settings from django.contrib import admin diff --git a/evennia/accounts/models.py b/evennia/accounts/models.py index 79da54db340..05f1f9327c9 100644 --- a/evennia/accounts/models.py +++ b/evennia/accounts/models.py @@ -15,8 +15,6 @@ account info and OOC account configuration variables etc. """ -from builtins import object - from django.conf import settings from django.db import models from django.contrib.auth.models import AbstractUser diff --git a/evennia/commands/cmdsethandler.py b/evennia/commands/cmdsethandler.py index bc0b759031d..4e99f6ebf5b 100644 --- a/evennia/commands/cmdsethandler.py +++ b/evennia/commands/cmdsethandler.py @@ -63,7 +63,6 @@ example, you can have a 'On a boat' set, onto which you then tack on the 'Fishing' set. Fishing from a boat? No problem! """ -from builtins import object import sys from traceback import format_exc from importlib import import_module diff --git a/evennia/commands/command.py b/evennia/commands/command.py index 6858d24671e..6938f6c81a6 100644 --- a/evennia/commands/command.py +++ b/evennia/commands/command.py @@ -4,8 +4,6 @@ All commands in Evennia inherit from the 'Command' class in this module. """ -from builtins import range - import re import math diff --git a/evennia/commands/default/account.py b/evennia/commands/default/account.py index 97b229bd1c9..cd88d39e958 100644 --- a/evennia/commands/default/account.py +++ b/evennia/commands/default/account.py @@ -18,8 +18,6 @@ method. Otherwise all text will be returned to all connected sessions. """ -from builtins import range - import time from codecs import lookup as codecs_lookup from django.conf import settings diff --git a/evennia/commands/default/batchprocess.py b/evennia/commands/default/batchprocess.py index 2c4d7966349..9209a89f92a 100644 --- a/evennia/commands/default/batchprocess.py +++ b/evennia/commands/default/batchprocess.py @@ -18,7 +18,6 @@ """ import re -from builtins import range from django.conf import settings from evennia.utils.batchprocessors import BATCHCMD, BATCHCODE diff --git a/evennia/commands/default/building.py b/evennia/commands/default/building.py index ff601bded3a..8222297fb4e 100644 --- a/evennia/commands/default/building.py +++ b/evennia/commands/default/building.py @@ -1,8 +1,6 @@ """ Building and world design commands """ -from builtins import range - import re from django.conf import settings from django.db.models import Q diff --git a/evennia/comms/channelhandler.py b/evennia/comms/channelhandler.py index 297aa7268e2..e10914dd56d 100644 --- a/evennia/comms/channelhandler.py +++ b/evennia/comms/channelhandler.py @@ -23,8 +23,6 @@ does this for you. """ -from builtins import object - from django.conf import settings from evennia.commands import cmdset, command from evennia.utils.logger import tail_log_file diff --git a/evennia/comms/models.py b/evennia/comms/models.py index ee7fd176dfa..064d780b27e 100644 --- a/evennia/comms/models.py +++ b/evennia/comms/models.py @@ -17,8 +17,6 @@ connect to channels by use of a ChannelConnect object (this object is necessary to easily be able to delete connections on the fly). """ -from builtins import object - from django.conf import settings from django.utils import timezone from django.db import models diff --git a/evennia/contrib/barter.py b/evennia/contrib/barter.py index 3171d4e297c..9bd1294cb4a 100644 --- a/evennia/contrib/barter.py +++ b/evennia/contrib/barter.py @@ -94,8 +94,6 @@ """ -from builtins import object - from evennia import Command, DefaultScript, CmdSet TRADE_TIMEOUT = 60 # timeout for B to accept trade diff --git a/evennia/contrib/dice.py b/evennia/contrib/dice.py index 374acc01f43..9eb0d283eca 100644 --- a/evennia/contrib/dice.py +++ b/evennia/contrib/dice.py @@ -29,8 +29,6 @@ After a reload the dice (or roll) command will be available in-game. """ -from builtins import range - import re from random import randint from evennia import default_cmds, CmdSet diff --git a/evennia/contrib/rplanguage.py b/evennia/contrib/rplanguage.py index b77ccc97a9a..4bdff922abb 100644 --- a/evennia/contrib/rplanguage.py +++ b/evennia/contrib/rplanguage.py @@ -90,8 +90,6 @@ that never change (if this is desired). """ -from builtins import range - import re from random import choice, randint from collections import defaultdict diff --git a/evennia/contrib/rpsystem.py b/evennia/contrib/rpsystem.py index 8cb2997ff85..db0e68d7a5d 100644 --- a/evennia/contrib/rpsystem.py +++ b/evennia/contrib/rpsystem.py @@ -94,8 +94,6 @@ @type/reset/force me = typeclasses.characters.Character """ -from builtins import object - import re from re import escape as re_escape import itertools diff --git a/evennia/help/admin.py b/evennia/help/admin.py index 9e31fc3656c..bf3e13b086c 100644 --- a/evennia/help/admin.py +++ b/evennia/help/admin.py @@ -1,8 +1,6 @@ """ This defines how to edit help entries in Admin. """ -from builtins import object - from django import forms from django.contrib import admin from evennia.help.models import HelpEntry diff --git a/evennia/help/models.py b/evennia/help/models.py index 2d11c02bb34..ada28b72d8e 100644 --- a/evennia/help/models.py +++ b/evennia/help/models.py @@ -9,8 +9,6 @@ game world, policy info, rules and similar. """ -from builtins import object - from django.contrib.contenttypes.models import ContentType from django.db import models from django.urls import reverse diff --git a/evennia/locks/lockhandler.py b/evennia/locks/lockhandler.py index 1a0cdd8d0fe..8a01f63885d 100644 --- a/evennia/locks/lockhandler.py +++ b/evennia/locks/lockhandler.py @@ -104,8 +104,6 @@ """ -from builtins import object - import re from django.conf import settings from evennia.utils import logger, utils diff --git a/evennia/objects/admin.py b/evennia/objects/admin.py index afb43a1b23f..618dd8569e7 100644 --- a/evennia/objects/admin.py +++ b/evennia/objects/admin.py @@ -2,8 +2,6 @@ # This sets up how models are displayed # in the web admin interface. # -from builtins import object - from django import forms from django.conf import settings from django.contrib import admin diff --git a/evennia/objects/manager.py b/evennia/objects/manager.py index 0ee239377e7..69af88bf786 100644 --- a/evennia/objects/manager.py +++ b/evennia/objects/manager.py @@ -8,7 +8,6 @@ from django.db.models.fields import exceptions from evennia.typeclasses.managers import TypedObjectManager, TypeclassManager from evennia.utils.utils import is_iter, make_iter, string_partial_matching -from builtins import int __all__ = ("ObjectManager",) _GA = object.__getattribute__ diff --git a/evennia/objects/models.py b/evennia/objects/models.py index 884cc28d809..e394323a6f2 100644 --- a/evennia/objects/models.py +++ b/evennia/objects/models.py @@ -13,8 +13,6 @@ the database object. Like everything else, they can be accessed transparently through the decorating TypeClass. """ -from builtins import object - from django.conf import settings from django.db import models from django.core.exceptions import ObjectDoesNotExist diff --git a/evennia/objects/objects.py b/evennia/objects/objects.py index 61dd3aed44a..0e8f84dd448 100644 --- a/evennia/objects/objects.py +++ b/evennia/objects/objects.py @@ -7,7 +7,6 @@ """ import time import inflect -from builtins import object from collections import defaultdict from django.conf import settings diff --git a/evennia/scripts/models.py b/evennia/scripts/models.py index bc892110af0..9d426394e10 100644 --- a/evennia/scripts/models.py +++ b/evennia/scripts/models.py @@ -24,8 +24,6 @@ - Give the account/object a time-limited bonus/effect """ -from builtins import object - from django.conf import settings from django.db import models from django.core.exceptions import ObjectDoesNotExist diff --git a/evennia/scripts/monitorhandler.py b/evennia/scripts/monitorhandler.py index 64af43a1aed..7324486ab43 100644 --- a/evennia/scripts/monitorhandler.py +++ b/evennia/scripts/monitorhandler.py @@ -11,7 +11,6 @@ """ import inspect -from builtins import object from collections import defaultdict from evennia.server.models import ServerConfig diff --git a/evennia/scripts/scripthandler.py b/evennia/scripts/scripthandler.py index 375f602d2b3..263290522d3 100644 --- a/evennia/scripts/scripthandler.py +++ b/evennia/scripts/scripthandler.py @@ -5,8 +5,6 @@ `scripts` on the game object. """ -from builtins import object - from evennia.scripts.models import ScriptDB from evennia.utils import create from evennia.utils import logger diff --git a/evennia/scripts/tickerhandler.py b/evennia/scripts/tickerhandler.py index b840bf2d9c5..457b4f217ba 100644 --- a/evennia/scripts/tickerhandler.py +++ b/evennia/scripts/tickerhandler.py @@ -66,7 +66,6 @@ class MyTickerHandler(TickerHandler): """ import inspect -from builtins import object from twisted.internet.defer import inlineCallbacks from django.core.exceptions import ObjectDoesNotExist diff --git a/evennia/server/portal/mccp.py b/evennia/server/portal/mccp.py index 7890c5cf8f9..2cf3e229954 100644 --- a/evennia/server/portal/mccp.py +++ b/evennia/server/portal/mccp.py @@ -14,7 +14,6 @@ This protocol is implemented by the telnet protocol importing mccp_compress and calling it from its write methods. """ -from builtins import object import zlib # negotiations for v1 and v2 of the protocol diff --git a/evennia/server/portal/mssp.py b/evennia/server/portal/mssp.py index 7bec2bb725a..1382a0b2ff1 100644 --- a/evennia/server/portal/mssp.py +++ b/evennia/server/portal/mssp.py @@ -10,7 +10,6 @@ """ -from builtins import object from django.conf import settings from evennia.utils import utils diff --git a/evennia/server/portal/mxp.py b/evennia/server/portal/mxp.py index 30c3ce99498..b7fb053b78c 100644 --- a/evennia/server/portal/mxp.py +++ b/evennia/server/portal/mxp.py @@ -13,7 +13,6 @@ http://www.gammon.com.au/mushclient/addingservermxp.htm """ -from builtins import object import re LINKS_SUB = re.compile(r'\|lc(.*?)\|lt(.*?)\|le', re.DOTALL) diff --git a/evennia/server/portal/naws.py b/evennia/server/portal/naws.py index 0663231d6b5..8a1acbc0339 100644 --- a/evennia/server/portal/naws.py +++ b/evennia/server/portal/naws.py @@ -10,7 +10,6 @@ """ from codecs import encode as codecs_encode -from builtins import object from django.conf import settings NAWS = b'\x1f' diff --git a/evennia/server/portal/portal.py b/evennia/server/portal/portal.py index 3a632d23b8d..6cf1c1e3440 100644 --- a/evennia/server/portal/portal.py +++ b/evennia/server/portal/portal.py @@ -7,8 +7,6 @@ by game/evennia.py). """ -from builtins import object - import sys import os import time diff --git a/evennia/server/portal/rss.py b/evennia/server/portal/rss.py index 351a5d3f5fc..d7b1c3967c1 100644 --- a/evennia/server/portal/rss.py +++ b/evennia/server/portal/rss.py @@ -5,8 +5,6 @@ to the channel whenever the feed updates. """ -from builtins import object - from twisted.internet import task, threads from django.conf import settings from evennia.server.session import Session diff --git a/evennia/server/portal/ssh.py b/evennia/server/portal/ssh.py index 5a0e573614b..a0e732c1cbf 100644 --- a/evennia/server/portal/ssh.py +++ b/evennia/server/portal/ssh.py @@ -9,7 +9,6 @@ """ -from builtins import object import os import re diff --git a/evennia/server/portal/suppress_ga.py b/evennia/server/portal/suppress_ga.py index bed633531e0..2d0cd97eaf3 100644 --- a/evennia/server/portal/suppress_ga.py +++ b/evennia/server/portal/suppress_ga.py @@ -13,7 +13,6 @@ http://www.faqs.org/rfcs/rfc858.html """ -from builtins import object SUPPRESS_GA = b'\x03' # default taken from telnet specification diff --git a/evennia/server/portal/telnet_oob.py b/evennia/server/portal/telnet_oob.py index f8a11d1beee..e20b8146c3a 100644 --- a/evennia/server/portal/telnet_oob.py +++ b/evennia/server/portal/telnet_oob.py @@ -25,7 +25,6 @@ header where applicable. """ -from builtins import object import re import json from evennia.utils.utils import is_iter diff --git a/evennia/server/portal/ttype.py b/evennia/server/portal/ttype.py index 4e37d8eaaf6..ef08c7f4abb 100644 --- a/evennia/server/portal/ttype.py +++ b/evennia/server/portal/ttype.py @@ -10,7 +10,6 @@ All data will be stored on the protocol's protocol_flags dictionary, under the 'TTYPE' key. """ -from builtins import object # telnet option codes TTYPE = b'\x18' diff --git a/evennia/server/profiling/dummyrunner.py b/evennia/server/profiling/dummyrunner.py index 40bbb6785bf..7ee024943af 100644 --- a/evennia/server/profiling/dummyrunner.py +++ b/evennia/server/profiling/dummyrunner.py @@ -32,8 +32,6 @@ """ -from builtins import range - import sys import time import random diff --git a/evennia/server/server.py b/evennia/server/server.py index f5fea192e55..f98d853fdb1 100644 --- a/evennia/server/server.py +++ b/evennia/server/server.py @@ -7,7 +7,6 @@ by evennia/server/server_runner.py). """ -from builtins import object import time import sys import os diff --git a/evennia/server/serversession.py b/evennia/server/serversession.py index 539d573f6fa..666534dbacb 100644 --- a/evennia/server/serversession.py +++ b/evennia/server/serversession.py @@ -6,8 +6,6 @@ It is stored on the Server side (as opposed to protocol-specific sessions which are stored on the Portal side) """ -from builtins import object - import weakref import time from django.utils import timezone diff --git a/evennia/server/session.py b/evennia/server/session.py index f06bcac3f63..bb267c3cb48 100644 --- a/evennia/server/session.py +++ b/evennia/server/session.py @@ -3,7 +3,6 @@ (both on Portal and Server side) should inherit from this class. """ -from builtins import object from django.conf import settings import time diff --git a/evennia/server/sessionhandler.py b/evennia/server/sessionhandler.py index cfded919065..c23a669f8d0 100644 --- a/evennia/server/sessionhandler.py +++ b/evennia/server/sessionhandler.py @@ -13,7 +13,6 @@ """ import time -from builtins import object from django.conf import settings from evennia.commands.cmdhandler import CMD_LOGINSTART diff --git a/evennia/typeclasses/attributes.py b/evennia/typeclasses/attributes.py index 3b0a91e2f9e..b3c22f6f3ce 100644 --- a/evennia/typeclasses/attributes.py +++ b/evennia/typeclasses/attributes.py @@ -8,7 +8,6 @@ """ -from builtins import object import re import fnmatch import weakref diff --git a/evennia/typeclasses/models.py b/evennia/typeclasses/models.py index 7c24496220b..25328966651 100644 --- a/evennia/typeclasses/models.py +++ b/evennia/typeclasses/models.py @@ -25,8 +25,6 @@ class needs to supply a ForeignKey field attr_object pointing to the kind these to create custom managers. """ -from builtins import object - from django.db.models import signals from django.db.models.base import ModelBase diff --git a/evennia/typeclasses/tags.py b/evennia/typeclasses/tags.py index a486835e50b..58b09a29e82 100644 --- a/evennia/typeclasses/tags.py +++ b/evennia/typeclasses/tags.py @@ -9,7 +9,6 @@ respective handlers. """ -from builtins import object from collections import defaultdict from django.conf import settings diff --git a/evennia/utils/ansi.py b/evennia/utils/ansi.py index 7eec5f95b31..748c8c81e55 100644 --- a/evennia/utils/ansi.py +++ b/evennia/utils/ansi.py @@ -14,7 +14,6 @@ """ import functools -from builtins import object, range import re from collections import OrderedDict diff --git a/evennia/utils/batchprocessors.py b/evennia/utils/batchprocessors.py index 913b9041bbc..45790b882cb 100644 --- a/evennia/utils/batchprocessors.py +++ b/evennia/utils/batchprocessors.py @@ -170,8 +170,6 @@ script = create.create_script() ``` """ -from builtins import object - import re import codecs import traceback diff --git a/evennia/utils/dbserialize.py b/evennia/utils/dbserialize.py index 6be5bebe5ad..374872c6a42 100644 --- a/evennia/utils/dbserialize.py +++ b/evennia/utils/dbserialize.py @@ -18,8 +18,6 @@ be out of sync with the database. """ -from builtins import object, int - from functools import update_wrapper from collections import defaultdict, MutableSequence, MutableSet, MutableMapping from collections import OrderedDict, deque diff --git a/evennia/utils/eveditor.py b/evennia/utils/eveditor.py index d2955c65008..490061cdf95 100644 --- a/evennia/utils/eveditor.py +++ b/evennia/utils/eveditor.py @@ -42,8 +42,6 @@ and offers basic handling of indentation. """ -from builtins import object - import re from django.conf import settings diff --git a/evennia/utils/evform.py b/evennia/utils/evform.py index f8affb7eb74..3c9d65cb8ba 100644 --- a/evennia/utils/evform.py +++ b/evennia/utils/evform.py @@ -129,8 +129,6 @@ """ -from builtins import object, range - import re import copy from evennia.utils.evtable import EvCell, EvTable diff --git a/evennia/utils/evmenu.py b/evennia/utils/evmenu.py index 8e4eb065683..2e8a9a5df26 100644 --- a/evennia/utils/evmenu.py +++ b/evennia/utils/evmenu.py @@ -166,7 +166,6 @@ def node3(caller): import random import inspect -from builtins import object, range from inspect import isfunction, getargspec from django.conf import settings diff --git a/evennia/utils/evmore.py b/evennia/utils/evmore.py index 1530e335851..0dcdc2794b2 100644 --- a/evennia/utils/evmore.py +++ b/evennia/utils/evmore.py @@ -27,8 +27,6 @@ caller.msg() construct every time the page is updated. """ -from builtins import object, range - from django.conf import settings from evennia import Command, CmdSet from evennia.commands import cmdhandler diff --git a/evennia/utils/idmapper/models.py b/evennia/utils/idmapper/models.py index b8f2db04449..5f16b9ea5f8 100644 --- a/evennia/utils/idmapper/models.py +++ b/evennia/utils/idmapper/models.py @@ -7,8 +7,6 @@ Also adds `cache_size()` for monitoring the size of the cache. """ -from builtins import object - import os import threading import gc diff --git a/evennia/utils/idmapper/tests.py b/evennia/utils/idmapper/tests.py index 29c3cf7ba2c..fa6de8a69b5 100644 --- a/evennia/utils/idmapper/tests.py +++ b/evennia/utils/idmapper/tests.py @@ -1,6 +1,4 @@ -from builtins import range - from django.test import TestCase from .models import SharedMemoryModel diff --git a/evennia/utils/picklefield.py b/evennia/utils/picklefield.py index 66362077b85..ef487bbae9a 100644 --- a/evennia/utils/picklefield.py +++ b/evennia/utils/picklefield.py @@ -28,7 +28,6 @@ Modified for Evennia by Griatch and the Evennia community. """ -from builtins import object from ast import literal_eval from datetime import datetime diff --git a/evennia/utils/text2html.py b/evennia/utils/text2html.py index 4933edd9abd..cdfc5f42050 100644 --- a/evennia/utils/text2html.py +++ b/evennia/utils/text2html.py @@ -9,8 +9,6 @@ (extensively modified by Griatch 2010) """ -from builtins import object - import re import cgi from .ansi import * From 9ace617cf78401e320985bf53ca69acf9dd99c90 Mon Sep 17 00:00:00 2001 From: Greg Taylor Date: Sun, 15 Sep 2019 20:41:42 -0700 Subject: [PATCH 078/164] Remove a couple of lingering u strings These are not needed in Python 3. --- evennia/web/website/views.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/evennia/web/website/views.py b/evennia/web/website/views.py index 9a0bbc85bb0..1aee00ec958 100644 --- a/evennia/web/website/views.py +++ b/evennia/web/website/views.py @@ -341,7 +341,7 @@ def get_object(self, queryset=None): # Check if this object was requested in a valid manner if slugify(obj.name) != self.kwargs.get(self.slug_url_kwarg): raise HttpResponseBadRequest( - u"No %(verbose_name)s found matching the query" % + "No %(verbose_name)s found matching the query" % {'verbose_name': queryset.model._meta.verbose_name}) # Check if the requestor account has permissions to access object @@ -937,7 +937,7 @@ def get_object(self, queryset=None): # Check if this object was requested in a valid manner if not obj: raise HttpResponseBadRequest( - u"No %(verbose_name)s found matching the query" % + "No %(verbose_name)s found matching the query" % {'verbose_name': queryset.model._meta.verbose_name}) return obj @@ -1082,7 +1082,7 @@ def get_object(self, queryset=None): # Check if this object was requested in a valid manner if not obj: raise HttpResponseBadRequest( - u"No %(verbose_name)s found matching the query" % + "No %(verbose_name)s found matching the query" % {'verbose_name': queryset.model._meta.verbose_name}) return obj From ad5007c288ed7752ea494c48b38631289911d3f9 Mon Sep 17 00:00:00 2001 From: Griatch Date: Thu, 19 Sep 2019 19:26:42 +0200 Subject: [PATCH 079/164] Update changelog with removal of pillow lib requirement --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a9bc3ed658..45cd4cc9416 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ without arguments starts a full interactive Python console. to better make ANSI graphics look the same as for third-party clients - `AttributeHandler.get(return_list=True)` will return `[]` if there are no Attributes instead of `[None]`. +- Remove `pillow` requirement (install especially if using imagefield) ## Evennia 0.9 (2018-2019) From 8cb5b4cefe422831447c5ed0eff4833247dc8d44 Mon Sep 17 00:00:00 2001 From: Greg Taylor Date: Sun, 15 Sep 2019 21:02:40 -0700 Subject: [PATCH 080/164] Add auto-formatting via black Introduce black, a Python code formatter. Also a Makefile that includes two common targets: * fmt (auto-format the code) * lint (lint the code and return non-zero if out of spec) We can re-use the latter in CI. By introducing a code formatter, we can cut down on stylistic variations across a now-large codebase. We can also avoid most style discussions in PR if we just have this thing enforce it for us. --- Makefile | 12 ++++++++++++ requirements.txt | 3 ++- 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 Makefile diff --git a/Makefile b/Makefile new file mode 100644 index 00000000000..450c30c9b55 --- /dev/null +++ b/Makefile @@ -0,0 +1,12 @@ +default: install + +BLACK_FORMAT_CONFIGS = --target-version py37 --line-length 100 + +install: + python setup.py develop + +fmt: + black $(BLACK_FORMAT_CONFIGS) evennia + +lint: + black --check $(BLACK_FORMAT_CONFIGS) evennia diff --git a/requirements.txt b/requirements.txt index cc23261fa14..cfbf6e5af30 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,9 +9,10 @@ inflect autobahn >= 17.9.3 model_mommy -# testing +# testing and development mock >= 1.0.1 anything +black # windows-specific pypiwin32;platform_system=="Windows" From 6fbcb941f9c91317dfc0de07d41c7c10570c27d5 Mon Sep 17 00:00:00 2001 From: Griatch Date: Sun, 22 Sep 2019 13:25:19 +0200 Subject: [PATCH 081/164] Clarify command-help for set command. Resolve #1944. --- evennia/commands/default/building.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/evennia/commands/default/building.py b/evennia/commands/default/building.py index 8222297fb4e..6712aa8a7f6 100644 --- a/evennia/commands/default/building.py +++ b/evennia/commands/default/building.py @@ -1469,10 +1469,10 @@ class CmdSetAttribute(ObjManipCommand): char: Setting an attribute on a character (global search) character: Alias for char, as above. - Sets attributes on objects. The second form clears - a previously set attribute while the last form - inspects the current value of the attribute - (if any). + Sets attributes on objects. The second example form above clears a + previously set attribute while the third form inspects the current value of + the attribute (if any). The last one (with the star) is a shortcut for + operatin on a player Account rather than an Object. The most common data to save with this command are strings and numbers. You can however also set Python primitives such as lists, From e4407fd33b571bc35309ff4f0bf67ec97afa257e Mon Sep 17 00:00:00 2001 From: Griatch Date: Sun, 22 Sep 2019 13:32:12 +0200 Subject: [PATCH 082/164] Change about command to put relevant version info first, as per #1945 --- evennia/commands/default/system.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/evennia/commands/default/system.py b/evennia/commands/default/system.py index c408493248f..088503df41a 100644 --- a/evennia/commands/default/system.py +++ b/evennia/commands/default/system.py @@ -768,8 +768,13 @@ def func(self): """Display information about server or target""" string = """ - |cEvennia|n {version}|n - MU* development system + |cEvennia|n MU* development system + + |wEvennia version|n: {version} + |wOS|n: {os} + |wPython|n: {python} + |wTwisted|n: {twisted} + |wDjango|n: {django} |wLicence|n https://opensource.org/licenses/BSD-3-Clause |wWeb|n http://www.evennia.com @@ -778,10 +783,6 @@ def func(self): |wMaintainer|n (2010-) Griatch (griatch AT gmail DOT com) |wMaintainer|n (2006-10) Greg Taylor - |wOS|n {os} - |wPython|n {python} - |wTwisted|n {twisted} - |wDjango|n {django} """.format(version=utils.get_evennia_version(), os=os.name, python=sys.version.split()[0], From b98d377a2645d7adc110b1a5791319548d89bcf3 Mon Sep 17 00:00:00 2001 From: Griatch Date: Sun, 22 Sep 2019 14:00:05 +0200 Subject: [PATCH 083/164] Update issue templates --- .github/ISSUE_TEMPLATE/bug-report.md | 27 +++++++++++++++++++ .github/ISSUE_TEMPLATE/bug_report.md | 27 +++++++++++++++++++ .github/ISSUE_TEMPLATE/documentation-issue.md | 14 ++++++++++ .github/ISSUE_TEMPLATE/feature-request.md | 20 ++++++++++++++ 4 files changed, 88 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug-report.md create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/documentation-issue.md create mode 100644 .github/ISSUE_TEMPLATE/feature-request.md diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md new file mode 100644 index 00000000000..d7bd11e964f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug-report.md @@ -0,0 +1,27 @@ +--- +name: Bug report +about: Create a report to help us improve +title: "[BUG] Enter a brief description here" +labels: bug +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. +2. +3. +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Environment, Evennia version, OS** +If unsure, run `evennia -v` or get the first few lines of the `about` command in-game. + +**Additional context** +Any other context about the problem, or ideas on how to solve. diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000000..d7bd11e964f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,27 @@ +--- +name: Bug report +about: Create a report to help us improve +title: "[BUG] Enter a brief description here" +labels: bug +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. +2. +3. +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Environment, Evennia version, OS** +If unsure, run `evennia -v` or get the first few lines of the `about` command in-game. + +**Additional context** +Any other context about the problem, or ideas on how to solve. diff --git a/.github/ISSUE_TEMPLATE/documentation-issue.md b/.github/ISSUE_TEMPLATE/documentation-issue.md new file mode 100644 index 00000000000..66a73840dd6 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/documentation-issue.md @@ -0,0 +1,14 @@ +--- +name: Documentation issue +about: Documentation problems and suggestions +title: '' +labels: documentation +assignees: '' + +--- + +**Documentation issue** +Describe what the issue is and where it can/should be found. + +**Suggested change** +The suggested change. diff --git a/.github/ISSUE_TEMPLATE/feature-request.md b/.github/ISSUE_TEMPLATE/feature-request.md new file mode 100644 index 00000000000..b09d6e3afe5 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature-request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: "[Feature Request] Enter a brief description here" +labels: feature-request +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. From 68cd1f5584e422a02034048ff93c962d57713616 Mon Sep 17 00:00:00 2001 From: Griatch Date: Sun, 22 Sep 2019 14:02:33 +0200 Subject: [PATCH 084/164] Remove unnecessary issue template --- .github/ISSUE_TEMPLATE/bug_report.md | 27 --------------------------- 1 file changed, 27 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/bug_report.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index d7bd11e964f..00000000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,27 +0,0 @@ ---- -name: Bug report -about: Create a report to help us improve -title: "[BUG] Enter a brief description here" -labels: bug -assignees: '' - ---- - -**Describe the bug** -A clear and concise description of what the bug is. - -**To Reproduce** -Steps to reproduce the behavior: -1. -2. -3. -4. See error - -**Expected behavior** -A clear and concise description of what you expected to happen. - -**Environment, Evennia version, OS** -If unsure, run `evennia -v` or get the first few lines of the `about` command in-game. - -**Additional context** -Any other context about the problem, or ideas on how to solve. From e4a04af46d654fd8d905fe3b6233ebeb18387425 Mon Sep 17 00:00:00 2001 From: Griatch Date: Sun, 22 Sep 2019 14:39:44 +0200 Subject: [PATCH 085/164] Some more fixes to issue templates --- .github/ISSUE_TEMPLATE/bug-report.md | 10 +++++----- .github/ISSUE_TEMPLATE/documentation-issue.md | 4 ++-- .github/ISSUE_TEMPLATE/feature-request.md | 8 ++++---- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md index d7bd11e964f..9dfdd7cb540 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.md +++ b/.github/ISSUE_TEMPLATE/bug-report.md @@ -7,21 +7,21 @@ assignees: '' --- -**Describe the bug** +#### Describe the bug A clear and concise description of what the bug is. -**To Reproduce** +####To Reproduce Steps to reproduce the behavior: 1. 2. 3. 4. See error -**Expected behavior** +####Expected behavior A clear and concise description of what you expected to happen. -**Environment, Evennia version, OS** +####Environment, Evennia version, OS etc If unsure, run `evennia -v` or get the first few lines of the `about` command in-game. -**Additional context** +####Additional context Any other context about the problem, or ideas on how to solve. diff --git a/.github/ISSUE_TEMPLATE/documentation-issue.md b/.github/ISSUE_TEMPLATE/documentation-issue.md index 66a73840dd6..6c0ef8a2cf4 100644 --- a/.github/ISSUE_TEMPLATE/documentation-issue.md +++ b/.github/ISSUE_TEMPLATE/documentation-issue.md @@ -7,8 +7,8 @@ assignees: '' --- -**Documentation issue** +####Documentation issue Describe what the issue is and where it can/should be found. -**Suggested change** +####Suggested change The suggested change. diff --git a/.github/ISSUE_TEMPLATE/feature-request.md b/.github/ISSUE_TEMPLATE/feature-request.md index b09d6e3afe5..b342169ee9d 100644 --- a/.github/ISSUE_TEMPLATE/feature-request.md +++ b/.github/ISSUE_TEMPLATE/feature-request.md @@ -7,14 +7,14 @@ assignees: '' --- -**Is your feature request related to a problem? Please describe.** +####Is your feature request related to a problem? Please describe. A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] -**Describe the solution you'd like** +####Describe the solution you'd like A clear and concise description of what you want to happen. -**Describe alternatives you've considered** +####Describe alternatives you've considered A clear and concise description of any alternative solutions or features you've considered. -**Additional context** +####Additional context Add any other context or screenshots about the feature request here. From a010a8716a7a6f96219aa34dd294a432a50870bd Mon Sep 17 00:00:00 2001 From: Griatch Date: Sun, 22 Sep 2019 14:41:07 +0200 Subject: [PATCH 086/164] Further issue-template fixes --- .github/ISSUE_TEMPLATE/bug-report.md | 8 ++++---- .github/ISSUE_TEMPLATE/documentation-issue.md | 4 ++-- .github/ISSUE_TEMPLATE/feature-request.md | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md index 9dfdd7cb540..580225a96a5 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.md +++ b/.github/ISSUE_TEMPLATE/bug-report.md @@ -10,18 +10,18 @@ assignees: '' #### Describe the bug A clear and concise description of what the bug is. -####To Reproduce +#### To Reproduce Steps to reproduce the behavior: 1. 2. 3. 4. See error -####Expected behavior +#### Expected behavior A clear and concise description of what you expected to happen. -####Environment, Evennia version, OS etc +#### Environment, Evennia version, OS etc If unsure, run `evennia -v` or get the first few lines of the `about` command in-game. -####Additional context +#### Additional context Any other context about the problem, or ideas on how to solve. diff --git a/.github/ISSUE_TEMPLATE/documentation-issue.md b/.github/ISSUE_TEMPLATE/documentation-issue.md index 6c0ef8a2cf4..200676bf5e5 100644 --- a/.github/ISSUE_TEMPLATE/documentation-issue.md +++ b/.github/ISSUE_TEMPLATE/documentation-issue.md @@ -7,8 +7,8 @@ assignees: '' --- -####Documentation issue +#### Documentation issue Describe what the issue is and where it can/should be found. -####Suggested change +#### Suggested change The suggested change. diff --git a/.github/ISSUE_TEMPLATE/feature-request.md b/.github/ISSUE_TEMPLATE/feature-request.md index b342169ee9d..7dc702cfad1 100644 --- a/.github/ISSUE_TEMPLATE/feature-request.md +++ b/.github/ISSUE_TEMPLATE/feature-request.md @@ -7,14 +7,14 @@ assignees: '' --- -####Is your feature request related to a problem? Please describe. +#### Is your feature request related to a problem? Please describe. A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] -####Describe the solution you'd like +#### Describe the solution you'd like A clear and concise description of what you want to happen. -####Describe alternatives you've considered +#### Describe alternatives you've considered A clear and concise description of any alternative solutions or features you've considered. -####Additional context +#### Additional context Add any other context or screenshots about the feature request here. From 8a5fbc39317e37d3b9cd770c61a6eeb203c03a1d Mon Sep 17 00:00:00 2001 From: Griatch Date: Sun, 22 Sep 2019 14:42:52 +0200 Subject: [PATCH 087/164] Add default title for doc-issues --- .github/ISSUE_TEMPLATE/documentation-issue.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/documentation-issue.md b/.github/ISSUE_TEMPLATE/documentation-issue.md index 200676bf5e5..bffee8dfb5b 100644 --- a/.github/ISSUE_TEMPLATE/documentation-issue.md +++ b/.github/ISSUE_TEMPLATE/documentation-issue.md @@ -1,7 +1,7 @@ --- name: Documentation issue about: Documentation problems and suggestions -title: '' +title: '[Documentation] Enter a brief description here' labels: documentation assignees: '' From ecae28b51d95ce7aa8b24074edde97cff3df8adb Mon Sep 17 00:00:00 2001 From: Griatch Date: Sun, 22 Sep 2019 19:22:15 +0200 Subject: [PATCH 088/164] Don't trim input whitespace from webclient, to better support python code input, as per #1935 --- .../web/webclient/static/webclient/js/plugins/default_in.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/evennia/web/webclient/static/webclient/js/plugins/default_in.js b/evennia/web/webclient/static/webclient/js/plugins/default_in.js index cfde4120438..044e4e7ec8b 100644 --- a/evennia/web/webclient/static/webclient/js/plugins/default_in.js +++ b/evennia/web/webclient/static/webclient/js/plugins/default_in.js @@ -28,9 +28,9 @@ let defaultin_plugin = (function () { case 13: // Enter key var outtext = inputfield.val() || ""; // Grab the text from which-ever inputfield is focused if ( !event.shiftKey ) { // Enter Key without shift --> send Mesg - var lines = outtext.trim().replace(/[\r]+/,"\n").replace(/[\n]+/, "\n").split("\n"); + var lines = outtext.replace(/[\r]+/,"\n").replace(/[\n]+/, "\n").split("\n"); for (var i = 0; i < lines.length; i++) { - plugin_handler.onSend( lines[i].trim() ); + plugin_handler.onSend( lines[i] ); } inputfield.val(""); // Clear this inputfield event.preventDefault(); From 725e9c3652cf9e8957cffde584123a37a82c7c9c Mon Sep 17 00:00:00 2001 From: Griatch Date: Sun, 22 Sep 2019 19:22:48 +0200 Subject: [PATCH 089/164] Enforce LINEMODE state for vanilla telnet. Resolve #1942 --- evennia/commands/default/system.py | 4 ++-- evennia/server/portal/telnet.py | 26 ++++++++++++++++++-------- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/evennia/commands/default/system.py b/evennia/commands/default/system.py index 088503df41a..fded209d901 100644 --- a/evennia/commands/default/system.py +++ b/evennia/commands/default/system.py @@ -298,8 +298,8 @@ class CmdPy(COMMAND_DEFAULT_CLASS): being parsed as HTML in the webclient but not in telnet clients) Without argument, open a Python console in-game. This is a full console, - accepting multi-line Python code for testing and debugging. Type `exit` to - return to the game. If Evennia is reloaded, thek console will be closed. + accepting multi-line Python code for testing and debugging. Type `exit()` to + return to the game. If Evennia is reloaded, the console will be closed. Enter a line of instruction after the 'py' command to execute it immediately. Separate multiple commands by ';' or open the code editor diff --git a/evennia/server/portal/telnet.py b/evennia/server/portal/telnet.py index b82754f37a8..19ac5d10d89 100644 --- a/evennia/server/portal/telnet.py +++ b/evennia/server/portal/telnet.py @@ -11,7 +11,8 @@ from twisted.internet import protocol from twisted.internet.task import LoopingCall from twisted.conch.telnet import Telnet, StatefulTelnetProtocol -from twisted.conch.telnet import IAC, NOP, LINEMODE, GA, WILL, WONT, ECHO, NULL +from twisted.conch.telnet import (IAC, NOP, LINEMODE, GA, WILL, WONT, ECHO, NULL, + MODE, LINEMODE_EDIT, LINEMODE_TRAPSIG) from django.conf import settings from evennia.server.session import Session from evennia.server.portal import ttype, mssp, telnet_oob, naws, suppress_ga @@ -51,6 +52,8 @@ def connectionMade(self): This is called when the connection is first established. """ + # important in order to work normally with standard telnet + self.do(LINEMODE) # initialize the session self.line_buffer = b"" client_address = self.transport.client @@ -146,12 +149,18 @@ def enableRemote(self, option): enable (bool): If this option should be enabled. """ - return (option == LINEMODE or - option == ttype.TTYPE or - option == naws.NAWS or - option == MCCP or - option == mssp.MSSP or - option == suppress_ga.SUPPRESS_GA) + if option == LINEMODE: + # make sure to activate line mode with local editing for all clients + self.requestNegotiation(LINEMODE, MODE + + bytes(chr(ord(LINEMODE_EDIT) + + ord(LINEMODE_TRAPSIG)), 'ascii')) + return True + else: + return (option == ttype.TTYPE or + option == naws.NAWS or + option == MCCP or + option == mssp.MSSP or + option == suppress_ga.SUPPRESS_GA) def enableLocal(self, option): """ @@ -164,7 +173,8 @@ def enableLocal(self, option): enable (bool): If this option should be enabled. """ - return (option == MCCP or + return (option == LINEMODE or + option == MCCP or option == ECHO or option == suppress_ga.SUPPRESS_GA) From 22e87ba6db9b97b6c20710b657db3235a87889ce Mon Sep 17 00:00:00 2001 From: Griatch Date: Sun, 22 Sep 2019 19:41:24 +0200 Subject: [PATCH 090/164] Add simplified Korean translation by aceamro in #1947 --- CHANGELOG.md | 1 + evennia/locale/fr/LC_MESSAGES/django.mo | Bin 5525 -> 5484 bytes evennia/locale/it/LC_MESSAGES/django.mo | Bin 6031 -> 5561 bytes evennia/locale/ko/LC_MESSAGES/django.mo | Bin 0 -> 7083 bytes evennia/locale/ko/LC_MESSAGES/django.po | 333 ++++++++++++++++++++++++ evennia/locale/pl/LC_MESSAGES/django.mo | Bin 3605 -> 3564 bytes evennia/locale/pt/LC_MESSAGES/django.mo | Bin 6370 -> 5922 bytes evennia/locale/sv/LC_MESSAGES/django.mo | Bin 3488 -> 3041 bytes evennia/locale/zh/LC_MESSAGES/django.mo | Bin 6725 -> 6684 bytes 9 files changed, 334 insertions(+) create mode 100644 evennia/locale/ko/LC_MESSAGES/django.mo create mode 100644 evennia/locale/ko/LC_MESSAGES/django.po diff --git a/CHANGELOG.md b/CHANGELOG.md index 45cd4cc9416..77785761244 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ without arguments starts a full interactive Python console. - `AttributeHandler.get(return_list=True)` will return `[]` if there are no Attributes instead of `[None]`. - Remove `pillow` requirement (install especially if using imagefield) +- Add Simplified Korean translation (user aceamro) ## Evennia 0.9 (2018-2019) diff --git a/evennia/locale/fr/LC_MESSAGES/django.mo b/evennia/locale/fr/LC_MESSAGES/django.mo index b16ad24fa011131aa7c6638e1970c2d15ca70c11..6ed877e2c7fcac7f293096f791d697ec716dcacc 100644 GIT binary patch delta 370 zcmXZYze|Eq6vpw#7Ys39Efphl3CdDQ60x5;H0Tly%^4^z4H8C>OCYxS2Q)U;76yWY zLwn%n7CA+mnb%7 delta 409 zcmXZYzb^xE7{~EXn`oNiN;x7Lt%Ukww_Ky^|g@Hib?B|X1b&^Ji#db;t+Op zON*GoCEUS5yv1>B;3K}_7+x6CEIwccKhVKQT*_kshtZF#t_h0E_CW(T$S-Y^JyMQ* zfU9_qHhy9R&EEe9ljxE+@et3^!*491m5}D~3X}MZ3)o7OqydUlpR|X0^zjaZMx&q0 zXkoDNHcnv`gTZbv_<>iP#2*afh$$W8sM&lE7owRhd-)Qv$9 z(LwP)aN(vJLA!A$sGEXCiXtj*#HHY-py>BEGl~}`pSgGL+`0FhlUs$keC1;@de>+J zTx+WQL~>ofQ^`onYH5<9K|tQj(39b2iQgbIj+OcSis+Y zzhZ?t|Ai86;K4c6ges2VGt`UGdb4iKBJWxeweSS?;6>bt_puk>Vjup(e(Z^xoy3Ev z@mVZb#q1q7D;Q{K@E6RWKD-A<@Fou78`MVP99xm<#Woy4ZTtwPaT+`EUeJGv+V}!) z#3fX4`;{DF!nm>=5@Ea;pX?9;Z&do7yHscfAhF?)3&Mfm6?nc$bIF_)AgSd!WF`o3_-;Jl~ zPonB+0f*62e#Ew8Ki#7^j@MGe|1dZ64D7=!m7|@OQ4_1E;;Nxe_!@Ph@2DKMkvBab z#vC3%9bg*k@G%ldZ;sYe8q_+uc*I)dpJ;d$3tr3ftKC!ZUY@m~4AP0Uq2pUlIqN1hVdd#6vL`W$fqTj?<9`uHa(4SGnO|>}A|s6F!(j&G=_rh!-%9_mQ>K zW0Zyd!&YqkK&jQZ3O~m&OyD_e!#{ABDiLYrhp#>iBl{lbaKj~(KfHqy$Rpf<9i+V+ z4N68AQ7ZQU8*maOk>}WgEp=hwT`2S4q0Aq~IP0s^bUN@hR^b!u!l!sWLg{COTkvqE zQcIX`t5<3r^8-js>h!E|3(n&v#<%bjj1bLd*n(1#HMj_E+=|DrBpFQ5kq4e*CpOFu zOSc|(FfL&R|H5{xZwxcZq6E4dWea{qS?DZ&f%kD6s;2OK_2CMJKVk-NG*SOObRIJy zne}oIR^dUEl3v3${1dm}|M(4VW`DlK-%tYo2jxCFiEnw0y~N?%U7{I@hI zy96RF%B7_vP4=H{eBC4)&SMaroLXrTu`~&69+zYqo1|SFPFFozcej$82Bw_C9+sZ|C%&=eqP9&+Sk>+>q7l3VF}i`NAkSnNiO* zwjS~dZf-nQbEB`Fd@kt2h`>7~9R+ ze$uRcb5qgP#RH?(ux)h0lSo)JnPqnB*|xeBt^KKP-?k*E)CMbWXLTmsxjdC#mP%)I zCX>x9YfpEk)8jkqUPPMSB<#lYw~lf0R(V&nxqM{i-PhUgt3O=T*E2GeyyU6(TH1Gp zoI>8pdwRqQ3t+j=3;m*{^d`aG-QpeF6iTv%@DUOi^uC zb=I*1K`Md=Mj8KRcJADdbN=U?JNfH%S57b-ci_Ac=Np$Xwh26R1^(b@Tp8b=0l$X( z|AIdRZ}iGF8(8vAS{5tp- z@FwsbkjA@l1!JqhJop*#QIPs8CEN$zjQb;C4>%8UFaizmnolxzC-^wn1-=4e%Ipm= z1HJ|3z$>tb^UfdNR*UjV-e{vLc3{CD#E!=Ga85!~+qZwKE3uLVE*>3AL-MAz&= z@O$77!27`yAgwP1$=<1J@D7*;zX7fRY5Z*<&G*M(8k_~c0R9=g9Q-Hv82B%6Gx!jU zV}d^i{|>IeWHy0KknDIH1|h#~0sG+NS3sKI-#!q!TR%wS9R$e^&x8BG z-+>(LzdoLq1FpmUVUX|*ATpZIwO|fh1Crl{K(gDZ^kZ^hy6PYww~ue(g4PO?DJ>%; z)B%Yy$X)3G;lozpyagvrrffp?y8>tW5Jv zSH=;${B52n3*FDkp014L_`=(%+gaJoyE5?unU%bKmB+89kVaOrRA#Fq1H%wf4h6P7 z!hO#YCc2mDtj1CsJkRyGa11w~KJjD-_eRN-!soir%er6W=}|&8t);ezvaK7IGt4U% z9t`QWUC@mkXwkmh)^5sD4-4CHOM?3@A8`YZJEFpOS2pYvj$`S&om@ee7;jiFiIw~z zOIkiJ`hGdz(^IKbG!Zw`&;z%Ir~|w&%v4d>S$z_I)hEp@Nt_Zn)&e087d+Rzj!CGTc@5JU1+WZhLt0(7OZF3C4~ z@PN)Cj*{iT9)@jU65R`C({1VUK$79gMGr3J?hr2pwr`bf!C@LxwjBtW*)AH8JIcD0 z^pL|nrW+K{_VK{=h16KvHw8gLGleabz1GL}RMO>+>tmXM13RuSy3{E==$3uU1w4p+ zgr{i#tLD;XFRw+TZ9P%@+ZRjDBN2Xia zu!y;rELozE#$3t>bO*|mB*Eodqeoq3(rpxSJY$O?KPN3%7?O|2g1$tl?2lY@MD&5g zZJ96|^5#(BU^%X{FeKJsAxV*pm0oF*as)o!#RaA6g@*KcuKjx4cB!^$8Q(>!vXObv zV^_v4Y|lc=@hZ4l1qw~aqNw0d;v$a@QRL&XMDjKSJ%PV)KkQQFNpo09c^=|WO@%E* zrHCS;@%tD1s-FUDi4-Xq1C4QXo!MY*e9Fp92a zAbR^UI z42Ex)@k};e7TFW)!$XY?AnyGl1!V}P}rklIt8G z&bk-r@BMZ*paMSbvU6+pu5-SWK_2wn?Z}_GO=j*P;YnnfJf}S!_D#8dIc%A^`+{Ma z8+7sWgAe4kh@IG$xlZnWBn%!7PmpbnAd(PcJ-IEGY(HZ8BV_zP1Sg`ZzekSa%-X<9hpgmPdCC-FE!83@z#OKR;>}9n28S0 z^XO;e;ZG;S7jgB(R8%|DnwaL%{yLA2?~e|=8Xnvq9(-Y8vmKqOg|)F{uN~DU!?U$! zV-MQS0O2!7!!Zoi@!p5F`=-JaP0jM~3~RC&!|xJ;^p)t^7DF z_9EG-HWwWmR~vG&&@bk&THe|_8&1umx9IR(>%d%eWDG0k(ZqPGK1Xl2_BMDp-iYe6 zt!F30=O=kM^UG**K74tW!|vg8Gdw)_QgnD$JsRtLv+bW{EY6zeYSHurts&ZXI+~tb zVp>&uV}~eTH-4i)t~>J7dDk)cFMM&1NA*{uC+n(7ZMyZu6js|j(+Ky~lO4QT$x#eA z*P56O&&{??p@wNo3a(Bhl}vf@NJ@Bb&mUWMk01B3?EXL5y~i!+eYL}1e4DhP=_r?7 zY`Oo3-Tp$dD=*qB62wGMX%#BvqvBlTGj9ceVr zHh6S!ve`heKz_6Po9E^)lpl|#pN*b+TE*Dj15kH#^4TO=u<*n@1hmZV9-ABg$@!TRgzMB`97diFSe08Q(uaP27A|Xs1h!#>w^lsX3 zCnA-8C=o49G|^FXh+O*`MH`j8*2L-X)XOSs#i_K}KmtYVBUdj}CuEEii)t?*VJN{a zN=ondqa!>$hHQ@|q7b}2|J;ZD>G7Vq=smtfk_rT~A%!Kj?X>Czrd;}Q(phGXj3t7h z;&Bco0DFW)0t?Py7rcN>5bnd~Vo6=X2TAnnvGBxcR9aHiMatZzexNMMTS?MlVSNlL U`Y3&)y!U_VbbjxAV4o=e19t_jK>z>% literal 0 HcmV?d00001 diff --git a/evennia/locale/ko/LC_MESSAGES/django.po b/evennia/locale/ko/LC_MESSAGES/django.po new file mode 100644 index 00000000000..dbef340a041 --- /dev/null +++ b/evennia/locale/ko/LC_MESSAGES/django.po @@ -0,0 +1,333 @@ +##The Simplified Korean translation for the Evennia server. +##Copyright (C) 2019 Ethan Kwon +##This file is distributed under the same license as the Evennia package. +##FIRST AUTHOR: Ethan Kwon , 2019- +## +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-09-21 05:00+0900\n" +"PO-Revision-Date: 2019-09-21 05:00+0900\n" +"Last-Translator: \n" +"Language-Team: \n" +"Language: korean\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .\accounts\accounts.py:784 +msgid "Account being deleted." +msgstr "계정이 삭제되었습니다." + +#: .\commands\cmdhandler.py:680 +msgid "There were multiple matches." +msgstr "여러 개의 일치 항목을 찾았습니다." + +#: .\commands\cmdhandler.py:703 +#, python-format +msgid "Command '%s' is not available." +msgstr "'%s' 명령은 사용할 수 없습니다." + +#: .\commands\cmdhandler.py:708 +#, python-format +msgid " Maybe you meant %s?" +msgstr "'%s'이 맞습니까?" + +#: .\commands\cmdhandler.py:708 +msgid "or" +msgstr "또는" + +#: .\commands\cmdhandler.py:710 +msgid " Type \"help\" for help." +msgstr " 도음말은 \"help\"를 입력하세요." + +#: .\commands\cmdsethandler.py:89 +#, python-brace-format +msgid "" +"{traceback}\n" +"Error loading cmdset '{path}'\n" +"(Traceback was logged {timestamp})" +msgstr "" +"{traceback}\n" +"Error loading cmdset '{path}'\n" +"Traceback was logged {timestamp})" + +#: .\commands\cmdsethandler.py:94 +#, python-brace-format +msgid "" +"Error loading cmdset: No cmdset class '{classname}' in '{path}'.\n" +"(Traceback was logged {timestamp})" +msgstr "" +"Error loading cmdset: No cmdset class '{classname}' in '{path}'.\n" +"(Traceback was logged {timestamp})" + +#: .\commands\cmdsethandler.py:98 +#, python-brace-format +msgid "" +"{traceback}\n" +"SyntaxError encountered when loading cmdset '{path}'.\n" +"(Traceback was logged {timestamp})" +msgstr "" +"{traceback}\n" +"SyntaxError encountered when loading cmdset '{path}'.\n" +"(Traceback was logged {timestamp})" + +#: .\commands\cmdsethandler.py:103 +#, python-brace-format +msgid "" +"{traceback}\n" +"Compile/Run error when loading cmdset '{path}'.\",\n" +"(Traceback was logged {timestamp})" +msgstr "" +"{traceback}\n" +"Compile/Run error when loading cmdset '{path}'.\",\n" +"(Traceback was logged {timestamp})" + +#: .\commands\cmdsethandler.py:108 +#, python-brace-format +msgid "" +"\n" +"Error encountered for cmdset at path '{path}'.\n" +"Replacing with fallback '{fallback_path}'.\n" +msgstr "" +"\n" +"Error encountered for cmdset at path '{path}'.\n" +"Replacing with fallback '{fallback_path}'.\n" + +#: .\commands\cmdsethandler.py:114 +#, python-brace-format +msgid "Fallback path '{fallback_path}' failed to generate a cmdset." +msgstr "Fallback path '{fallback_path}' failed to generate a cmdset." + +#: .\commands\cmdsethandler.py:182 .\commands\cmdsethandler.py:192 +#, python-format +msgid "" +"\n" +"(Unsuccessfully tried '%s')." +msgstr "" +"\n" +"(Unsuccessfully tried '%s')." + +#: .\commands\cmdsethandler.py:311 +#, python-brace-format +msgid "custom {mergetype} on cmdset '{cmdset}'" +msgstr "custom {mergetype} on cmdset '{cmdset}'" + +#: .\commands\cmdsethandler.py:314 +#, python-brace-format +msgid " : {current}" +msgstr " : {current}" + +#: .\commands\cmdsethandler.py:322 +#, python-brace-format +msgid "" +" <{key} ({mergetype}, prio {prio}, {permstring})>:\n" +" {keylist}" +msgstr "" +" <{key} ({mergetype}, prio {prio}, {permstring})>:\n" +" {keylist}" + +#: .\commands\cmdsethandler.py:426 +msgid "Only CmdSets can be added to the cmdsethandler!" +msgstr "Only CmdSets can be added to the cmdsethandler!" + +#: .\comms\channelhandler.py:100 +msgid "Say what?" +msgstr "뭐라구요?" + +#: .\comms\channelhandler.py:105 +#, python-format +msgid "Channel '%s' not found." +msgstr "'%s' 채널을 찾을 수 없습니다." + +#: .\comms\channelhandler.py:108 +#, python-format +msgid "You are not connected to channel '%s'." +msgstr "'%s' 채널에 접속하고 있지 않습니다." + +#: .\comms\channelhandler.py:112 +#, python-format +msgid "You are not permitted to send to channel '%s'." +msgstr "'%s' 채널에 보낼 수 없습니다." + +#: .\comms\channelhandler.py:155 +msgid " (channel)" +msgstr " (채널)" + +#: .\locks\lockhandler.py:236 +#, python-format +msgid "Lock: lock-function '%s' is not available." +msgstr "Lock: lock-function '%s' is not available." + +#: .\locks\lockhandler.py:249 +#, python-format +msgid "Lock: definition '%s' has syntax errors." +msgstr "Lock: definition '%s' has syntax errors." + +#: .\locks\lockhandler.py:253 +#, python-format +msgid "" +"LockHandler on %(obj)s: access type '%(access_type)s' changed from " +"'%(source)s' to '%(goal)s' " +msgstr "" +"LockHandler on %(obj)s: access type '%(access_type)s' changed from " +"'%(source)s' to '%(goal)s' " + +#: .\locks\lockhandler.py:320 +#, python-brace-format +msgid "Lock: '{lockdef}' contains no colon (:)." +msgstr "Lock: '{lockdef}' contains no colon (:)." + +#: .\locks\lockhandler.py:328 +#, python-brace-format +msgid "Lock: '{lockdef}' has no access_type (left-side of colon is empty)." +msgstr "Lock: '{lockdef}' has no access_type (left-side of colon is empty)." + +#: .\locks\lockhandler.py:336 +#, python-brace-format +msgid "Lock: '{lockdef}' has mismatched parentheses." +msgstr "Lock: '{lockdef}' has mismatched parentheses." + +#: .\locks\lockhandler.py:343 +#, python-brace-format +msgid "Lock: '{lockdef}' has no valid lock functions." +msgstr "Lock: '{lockdef}' has no valid lock functions." + +#: .\objects\objects.py:745 +#, python-format +msgid "Couldn't perform move ('%s'). Contact an admin." +msgstr "Couldn't perform move ('%s'). 운영자에게 문의하세요." + +#: .\objects\objects.py:755 +msgid "The destination doesn't exist." +msgstr "The destination doesn't exist." + +#: .\objects\objects.py:846 +#, python-format +msgid "Could not find default home '(#%d)'." +msgstr "Could not find default home '(#%d)'." + +#: .\objects\objects.py:862 +msgid "Something went wrong! You are dumped into nowhere. Contact an admin." +msgstr "Something went wrong! You are dumped into nowhere. 운영자에게 문의하세요." + +#: .\objects\objects.py:1004 +#, python-format +msgid "Your character %s has been destroyed." +msgstr "%s 캐릭터가 삭제되었습니다." + +#: .\scripts\scripthandler.py:53 +#, python-format +msgid "" +"\n" +" '%(key)s' (%(next_repeat)s/%(interval)s, %(repeats)s repeats): %(desc)s" +msgstr "" +"\n" +" '%(key)s' (%(next_repeat)s/%(interval)s, %(repeats)s repeats): %(desc)s" + +#: .\scripts\scripts.py:199 +#, python-format +msgid "" +"Script %(key)s(#%(dbid)s) of type '%(cname)s': at_repeat() error '%(err)s'." +msgstr "" +"Script %(key)s(#%(dbid)s) of type '%(cname)s': at_repeat() error '%(err)s'." + +#: .\server\initial_setup.py:28 +msgid "" +"\n" +"Welcome to your new |wEvennia|n-based game! Visit http://www.evennia.com if " +"you need\n" +"help, want to contribute, report issues or just join the community.\n" +"As Account #1 you can create a demo/tutorial area with |w@batchcommand " +"tutorial_world.build|n.\n" +" " +msgstr "" +"\n" +"Welcome to your new |wEvennia|n-based game! Visit http://www.evennia.com if " +"you need\n" +"help, want to contribute, report issues or just join the community.\n" +"#1 유저(운영자)는 |w@batchcommand tutorial_world.build|n.명령을 사용하여\n" +"데모겸 튜터리얼 월드를 생성할 수 있습니다.\n" +" " + +#: .\server\initial_setup.py:92 +msgid "This is User #1." +msgstr "This is User #1." + +#: .\server\initial_setup.py:105 +msgid "Limbo" +msgstr "림보" + +#: .\server\server.py:139 +msgid "idle timeout exceeded" +msgstr "연결 시간 초과" + +#: .\server\sessionhandler.py:392 +msgid " ... Server restarted." +msgstr " ... 서버가 재가동되었습니다." + +#: .\server\sessionhandler.py:620 +msgid "Logged in from elsewhere. Disconnecting." +msgstr "어디에선가 로그인했습니다. 접속이 끊어집니다." + +#: .\server\sessionhandler.py:648 +msgid "Idle timeout exceeded, disconnecting." +msgstr "연결 시간이 초과되었습니다. 접속이 끊어집니다." + +#: .\server\validators.py:50 +#, python-format +msgid "" +"%s From a terminal client, you can also use a phrase of multiple words if " +"you enclose the password in double quotes." +msgstr "" +"%s 터미널 클라이언트에서 암호를 큰 따옴표로 묶으면 여러 단어로 된 암호를 사용할 수 있습니다." +"" + +#: .\utils\evmenu.py:192 +#, python-brace-format +msgid "" +"Menu node '{nodename}' is either not implemented or caused an error. Make " +"another choice." +msgstr "" +"Menu node '{nodename}'가 구현되지 않았거나 오류가 발생했습니다." +"다른 선택을 해보세요." + +#: .\utils\evmenu.py:194 +#, python-brace-format +msgid "Error in menu node '{nodename}'." +msgstr "Menu node '{nodename}'에서 오류가 발생했습니다." + +#: .\utils\evmenu.py:195 +msgid "No description." +msgstr "설명이 없습니다." + +#: .\utils\evmenu.py:196 +msgid "Commands: , help, quit" +msgstr "명령: , help, quit" + +#: .\utils\evmenu.py:197 +msgid "Commands: , help" +msgstr "명령: , help" + +#: .\utils\evmenu.py:198 +msgid "Commands: help, quit" +msgstr "명령: help, quit" + +#: .\utils\evmenu.py:199 +msgid "Commands: help" +msgstr "명령: help" + +#: .\utils\evmenu.py:200 +msgid "Choose an option or try 'help'." +msgstr "다른 옵션을 선택하거나 'help'를 확인해보세요." + +#: .\utils\utils.py:1882 +#, python-format +msgid "Could not find '%s'." +msgstr "'%s'를 찾을 수 없습니다." + +#: .\utils\utils.py:1889 +#, python-format +msgid "More than one match for '%s' (please narrow target):\n" +msgstr "'%s'와 일치하는 항목을 여러 개 찾았습니다.:\n" diff --git a/evennia/locale/pl/LC_MESSAGES/django.mo b/evennia/locale/pl/LC_MESSAGES/django.mo index 021e81ea31f16dfbc0841744b8ecd63e753bd050..f6e31091d57bde327efd7f851e0a1a2aabd09ce1 100644 GIT binary patch delta 273 zcmXBP%?bfw7{&2pWCkFfLrqNqHQghcg`E3SB&7 X9Uqv)7xKSfX}a^GtP!N5y|p&K8ATY$ delta 312 zcmXBPze+-J7{>8O!#^F5QiB*7dQ36G4xV#rQF6(ga&%~t926T=5TWtxEkW2-G$?ot z(o~C^*N}@>&^LXT4-fo)ywCfa{y3YP0pAEpg@}}vR6dl>1JVOV$wNar#TPumcg*1@ z_V621coWg;6vU1FSji%{LmQ9A(AIMyk7}jd~11>69ePdG3jn2x_14G!vdIERh6zYYu*n)Z7j4P<$t(9HJR@UJvvwhf)ah$|9ypQa~ zUSKmWVmt4*0+T^*tYZfb1UNNb#7=yO8C)vgk8+$L)`Peob6ATDs29J+E%*aRv9j9i zIF6$Z^aPdZWeoFvD=^UxzoIr!U1R3O6keqq2XKV-e5jO(A~v!PkcN}kRyK>Bte0^B zS24x!8`&Lav(sU-ZFnBjcmt=YfxTp+9ThQ-RS~njm_Vg;0=2=rn8bNhEeoiU6md7! z)|FnI#8a%Vp-TA*yYVBc1pa6#!@(%|R}-bUaTc#*65n7S{=$AtQ0Pg#fZE7gR7#7e z%=D6PrgjGp;tX!ZcQ}e)@CMuo$>aPv>wIb9Yl+CTLQpU!O#x_0@qdnYo zf*U%qPN~orLR+}yQt3#X+l*j6j4npI7DR%f!zsFz&W)gqZWd@90b9Q+ET%t4h0w8h^5ei1tHXph*-NQZ5J+zv=r+9yqRgF%N}_1d!KXf zIp^Lhy^rDx4_fQb3(?21mgDP?$S5AHYX*1LMnfPTu6kFm|EEK|GD!_#O7(6WoYvNaqla zpx*xi-@`koRcu}%vJ!WqPGkaK#bY>z7jPFo#`TOZk)}%NcVi3J7B!PO)R|sHoyaW= z<1g5czhQ}Xbu?F6bd$1ZrNWH<1{&OrgH?~?My`Lr0sIpelKeq}QK^bdy)5!NdYHnK zNRQ+j)U^?At(0^FzQgqn)QQfZPV^im@FoVmMr~PmmB?Gzg*tE=_u#Qr)L(mYr~1P0 zsF}9V*eDJl>qn+gOMMu1(VfTrco%iRF6tY{1GpJap~YLM6YFL+TJmvB<2mG0x!*?p zwUke|u^J=%Xpe^QeLRCak_Tw;kLq6XePSF`Z%;qEYu)aZ$W5JwQaOl=vc72lc?KO+o7Rttmn{m^WV_Q zQ5aoAAt_yI-dmolPlT2ikBrP&*UdV`(v61ep<*f1*d8jDPc;4$dUt#{VSJmLvpJKq zpV-N~&6r8gbves=Zm*2APF=?bAA4D!C$op= z%~Z~tF`YeLE<2rdBYukW8GkzBmmaT}=r2xo_W0J#*j&WlvGh{WHN_*d&b0MSkEb(X z&`65AwQn~!ZR^?}9nM)tCl%f0_$v*X`ibF?Q4JbMOLMdp5kV0RHnxPrGg|ux0>z=R zKcNVkqR6erT3h;_+dFvu+;e@td(V0AcBC7xzeilxLQF758POh*Y3y_GMYM1e+Ze!K zTaQy@h&+r@%-Q)0j+1ZO>&KWPzrdm&g@Vv~d@LNG7JY&Xbm0uEdc?mP*JE)zxKrP?~*YE-Ldbi(HE=kO?zf3a8;RcrQ2&Z zn}cTd-#MO$X}K=N8M?WK9!6u}AM=j&F&|`Thx$e4%uo89C{lPQOlu=Z_f6=*^wbn* ns?=w0J^G4P+4<8sbyXbJm#1nSdY3!T-kPJc7ic)FM1SQEYf{5{zxF9s`w2Z*aWTve=7O-|_ z2s;xD#>9mhlO}!v+37;vk`Uc#j5|Mo|1%xp0^?0Czk8;aJLjA`-aQ5^T3M>%*MkKh*^!tW@F#DkT>M)4rAfgxP* zTvBSyf3Sfq{O|+ASi6hru>++Uy_m!#PSPa{$A~|%iKS|-s~Ev%;!c##jQH^cwiC~x zbZ!kJykEUzaUHj?4MX%)4Z<|_n{>8z;_Za5$BM`ctwDsk?Igr ziqM$S{sT-IXDUejmF${Ol8`%-`P|^_G)I4^X9{{&%UGt#%COAyYJ?3b z?OL&5xq6{!v&pqB)77<{RW!5Z&Fb+$=>C*txcp+z%xgKvn$^Nl%Q5nX8FN#tXWe|v zT{#=PnOw|-qpogdbtmQy{N1!@YKxETbY6G0s3n>3(pZY8+y6Tkyxe{#KIrIaNh&^+ zF6b$(JJHn_PxQnSNv*pl)!WmN=t?BYZ-aLO{gU){Rq0a6aMM{`E86z7W!p1`nb&s~ v7I#Z&N1HMVg><1@UH8PVs`5DJyni9t?5t{~ek}FBs@!QKGtI&OtV{g?k_ES{ diff --git a/evennia/locale/zh/LC_MESSAGES/django.mo b/evennia/locale/zh/LC_MESSAGES/django.mo index a0ec7c2c00710bf453510059e5bdc93bf02b0800..d07dd7a51714c9c0e185f4e01bfedf1137a28586 100644 GIT binary patch delta 450 zcmXZYF-SsD6vpvSq#(4cp1w-Uz#4j~SYXf)A#M>tM9`$rSVJxbImDwm9ds+Asj(rl zEgTJmc7ui>9Ne0sA*lXWgU9`yb9o%TbDQa_^xd<^eV6nyB5mMfK$^yZQ7Mlx9P&tK zI81*&CWY|=Gx&g6{KN@d9haiGjVD+@K6N=J@%!JPCC$($ExC$OY;r*c9bphpk(sLD zJl^06w$Q;}ETY5DYj}+je8oZh!X*CSA|^uppnDjnzr+Q63c1o8#RnJrXxaTi$GAmb z#dUl`KSo(|8RKaB9PZ#DnwMxGqk8L_jcpF|0x2{BvuJ|laTd!<6ei&z%! z_t%)9zxAYHe8d&}!c|Q7NrPCyG}iG5&yh=BQt~m|b`i(ucajP&0;$=P_va4J^|)(GK`QW-HISw(&AI`vbdZa}Usho!~g$%~4pwceDdEBt;>C gwxNdsHn57Ht$Os-=?n^Mhx?UUGwx Date: Sun, 22 Sep 2019 19:54:23 +0200 Subject: [PATCH 091/164] Catch tracebacks when leaving the py console with exit/exit() --- evennia/commands/default/system.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/evennia/commands/default/system.py b/evennia/commands/default/system.py index fded209d901..41490b65024 100644 --- a/evennia/commands/default/system.py +++ b/evennia/commands/default/system.py @@ -264,6 +264,7 @@ def push(self, line): """Push some code, whether complete or not.""" old_stdout = sys.stdout old_stderr = sys.stderr + class FakeStd: def __init__(self, caller): self.caller = caller @@ -274,9 +275,14 @@ def write(self, string): fake_std = FakeStd(self.caller) sys.stdout = fake_std sys.stderr = fake_std - result = super().push(line) - sys.stdout = old_stdout - sys.stderr = old_stderr + result = None + try: + result = super().push(line) + except SystemExit: + pass + finally: + sys.stdout = old_stdout + sys.stderr = old_stderr return result From 35a8282db08114a86e1d3b5a1cff5bdc58236ead Mon Sep 17 00:00:00 2001 From: Griatch Date: Mon, 23 Sep 2019 22:46:18 +0200 Subject: [PATCH 092/164] Add create_msg alias to create_message --- evennia/utils/create.py | 1 + 1 file changed, 1 insertion(+) diff --git a/evennia/utils/create.py b/evennia/utils/create.py index 36729a62eb1..b0527331b02 100644 --- a/evennia/utils/create.py +++ b/evennia/utils/create.py @@ -351,6 +351,7 @@ def create_message(senderobj, message, channels=None, receivers=None, locks=None message = create_message +create_msg = create_message def create_channel(key, aliases=None, desc=None, From 541a6fa85525f0c40d47cd0074149ba49b9b60c9 Mon Sep 17 00:00:00 2001 From: Griatch Date: Mon, 23 Sep 2019 23:22:56 +0200 Subject: [PATCH 093/164] Show warnings on start -l if settings contains production-unsafe values. Resolves #1732 --- CHANGELOG.md | 1 + evennia/server/deprecations.py | 9 +++++++-- evennia/server/evennia_launcher.py | 15 ++++++++++----- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 77785761244..9a607c34f0d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ without arguments starts a full interactive Python console. Attributes instead of `[None]`. - Remove `pillow` requirement (install especially if using imagefield) - Add Simplified Korean translation (user aceamro) +- Show warning on `start -l` if settings contains values unsafe for production. ## Evennia 0.9 (2018-2019) diff --git a/evennia/server/deprecations.py b/evennia/server/deprecations.py index 976040a8e90..d51f3e753d4 100644 --- a/evennia/server/deprecations.py +++ b/evennia/server/deprecations.py @@ -85,7 +85,12 @@ def check_errors(settings): def check_warnings(settings): """ - Check deprecations that should produce warnings but which + Check conditions and deprecations that should produce warnings but which does not stop launch. """ - pass + if settings.DEBUG: + print(" [Devel: settings.DEBUG is True. Important to turn off in production.]") + if settings.IN_GAME_ERRORS: + print(" [Devel: settings.IN_GAME_ERRORS is True. Turn off in production.]") + if settings.ALLOWED_HOSTS == ["*"]: + print(" [Devel: settings.ALLOWED_HOSTS set to '*' (all). Limit in production.]") diff --git a/evennia/server/evennia_launcher.py b/evennia/server/evennia_launcher.py index 86dd99aa439..ad73bd26a0e 100644 --- a/evennia/server/evennia_launcher.py +++ b/evennia/server/evennia_launcher.py @@ -242,7 +242,7 @@ (error was '{traceback}') If you think your database should work, make sure you are running your - commands from inside your game directory. If this error persists, run + commands from inside your game directory. If this error persists, run evennia migrate @@ -1611,7 +1611,7 @@ def show_version_info(about=False): django=django.get_version()) -def error_check_python_modules(): +def error_check_python_modules(show_warnings=False): """ Import settings modules in settings. This will raise exceptions on pure python-syntax issues which are hard to catch gracefully with @@ -1619,6 +1619,9 @@ def error_check_python_modules(): python source files themselves). Best they fail already here before we get any further. + Kwargs: + show_warnings (bool): If non-fatal warning messages should be shown. + """ from django.conf import settings @@ -1634,11 +1637,13 @@ def _imp(path, split=True): from evennia.server import deprecations try: deprecations.check_errors(settings) - deprecations.check_warnings(settings) except DeprecationWarning as err: print(err) sys.exit() + if show_warnings: + deprecations.check_warnings(settings) + # core modules _imp(settings.COMMAND_PARSER) _imp(settings.SEARCH_AT_RESULT) @@ -2113,11 +2118,11 @@ def main(): query_info() elif option == "start": init_game_directory(CURRENT_DIR, check_db=True) - error_check_python_modules() + error_check_python_modules(show_warnings=args.tail_log) start_evennia(args.profiler, args.profiler) elif option == "istart": init_game_directory(CURRENT_DIR, check_db=True) - error_check_python_modules() + error_check_python_modules(show_warnings=args.tail_log) start_server_interactive() elif option == "ipstart": start_portal_interactive() From 50d0292291f15a392d89d79344823a41c6363cca Mon Sep 17 00:00:00 2001 From: Griatch Date: Wed, 25 Sep 2019 19:11:30 +0200 Subject: [PATCH 094/164] Set up black on travis --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 275b39a119a..5c99e5db41a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -41,6 +41,7 @@ before_script: - evennia collectstatic --noinput script: + - make fmt - coverage run --source=../evennia --omit=*/migrations/*,*/urls.py,*/test*.py,*.sh,*.txt,*.md,*.pyc,*.service ../bin/unix/evennia test --settings=settings --keepdb evennia after_success: From 21fe2c0e2c09e768c1cb97c1f36146f2f08ad2cb Mon Sep 17 00:00:00 2001 From: Griatch Date: Sat, 28 Sep 2019 18:18:11 +0200 Subject: [PATCH 095/164] Format code with black. Add makefile to run fmt/tests --- CHANGELOG.md | 1 + Makefile | 33 +- evennia/__init__.py | 49 +- evennia/accounts/accounts.py | 276 ++- evennia/accounts/admin.py | 195 +- evennia/accounts/bots.py | 94 +- evennia/accounts/manager.py | 10 +- evennia/accounts/migrations/0001_initial.py | 190 +- .../accounts/migrations/0002_move_defaults.py | 8 +- .../migrations/0003_auto_20150209_2234.py | 28 +- .../migrations/0004_auto_20150403_2339.py | 61 +- .../migrations/0005_auto_20160905_0902.py | 24 +- .../migrations/0006_auto_20170606_1731.py | 35 +- .../migrations/0007_copy_player_to_account.py | 54 +- .../migrations/0008_auto_20190128_1820.py | 102 +- evennia/accounts/models.py | 45 +- evennia/accounts/tests.py | 165 +- evennia/commands/cmdhandler.py | 141 +- evennia/commands/cmdparser.py | 46 +- evennia/commands/cmdset.py | 55 +- evennia/commands/cmdsethandler.py | 136 +- evennia/commands/command.py | 138 +- evennia/commands/default/account.py | 337 ++- evennia/commands/default/admin.py | 160 +- evennia/commands/default/batchprocess.py | 82 +- evennia/commands/default/building.py | 1068 +++++---- evennia/commands/default/cmdset_character.py | 1 + evennia/commands/default/cmdset_session.py | 1 + evennia/commands/default/cmdset_unloggedin.py | 1 + evennia/commands/default/comms.py | 341 ++- evennia/commands/default/general.py | 131 +- evennia/commands/default/help.py | 149 +- evennia/commands/default/muxcommand.py | 36 +- evennia/commands/default/syscommands.py | 12 +- evennia/commands/default/system.py | 353 ++- evennia/commands/default/tests.py | 831 +++++-- evennia/commands/default/unloggedin.py | 116 +- evennia/commands/tests.py | 85 +- evennia/comms/admin.py | 46 +- evennia/comms/channelhandler.py | 40 +- evennia/comms/comms.py | 132 +- evennia/comms/managers.py | 102 +- evennia/comms/migrations/0001_initial.py | 92 +- .../0002_msg_db_hide_from_objects.py | 15 +- .../migrations/0003_auto_20140917_0756.py | 103 +- .../migrations/0004_auto_20150118_1631.py | 8 +- .../migrations/0005_auto_20150223_1517.py | 8 +- .../0006_channeldb_db_object_subscriptions.py | 19 +- evennia/comms/migrations/0007_msg_db_tags.py | 16 +- .../migrations/0008_auto_20160905_0902.py | 13 +- .../migrations/0009_auto_20160921_1731.py | 117 +- .../migrations/0010_auto_20161206_1912.py | 30 +- .../migrations/0011_auto_20170217_2039.py | 30 +- .../migrations/0011_auto_20170606_1731.py | 137 +- .../migrations/0012_merge_20170617_2017.py | 8 +- .../migrations/0013_auto_20170705_1726.py | 131 +- .../migrations/0014_auto_20170705_1736.py | 17 +- .../migrations/0015_auto_20170706_2041.py | 18 +- .../migrations/0016_auto_20180925_1735.py | 13 +- .../migrations/0017_auto_20190128_1820.py | 266 ++- evennia/comms/models.py | 247 +- evennia/comms/tests.py | 9 +- evennia/contrib/__init__.py | 8 +- evennia/contrib/barter.py | 94 +- evennia/contrib/building_menu.py | 250 +- evennia/contrib/chargen.py | 22 +- evennia/contrib/clothing.py | 106 +- evennia/contrib/color_markups.py | 189 +- evennia/contrib/custom_gametime.py | 58 +- evennia/contrib/dice.py | 32 +- evennia/contrib/email_login.py | 75 +- evennia/contrib/evscaperoom/commands.py | 77 +- evennia/contrib/evscaperoom/menu.py | 159 +- evennia/contrib/evscaperoom/objects.py | 147 +- evennia/contrib/evscaperoom/room.py | 35 +- evennia/contrib/evscaperoom/scripts.py | 1 - evennia/contrib/evscaperoom/state.py | 35 +- .../evscaperoom/states/state_001_start.py | 20 +- evennia/contrib/evscaperoom/tests.py | 67 +- evennia/contrib/evscaperoom/utils.py | 42 +- evennia/contrib/extended_room.py | 48 +- evennia/contrib/fieldfill.py | 171 +- evennia/contrib/gendersub.py | 27 +- evennia/contrib/health_bar.py | 57 +- .../contrib/ingame_python/callbackhandler.py | 31 +- evennia/contrib/ingame_python/commands.py | 159 +- evennia/contrib/ingame_python/scripts.py | 90 +- evennia/contrib/ingame_python/tests.py | 192 +- evennia/contrib/ingame_python/typeclasses.py | 87 +- evennia/contrib/ingame_python/utils.py | 16 +- evennia/contrib/mail.py | 82 +- evennia/contrib/mapbuilder.py | 163 +- evennia/contrib/menu_login.py | 100 +- evennia/contrib/multidescer.py | 25 +- evennia/contrib/puzzles.py | 286 +-- evennia/contrib/random_string_generator.py | 15 +- evennia/contrib/rplanguage.py | 129 +- evennia/contrib/rpsystem.py | 270 ++- evennia/contrib/security/auditing/outputs.py | 4 +- evennia/contrib/security/auditing/server.py | 124 +- evennia/contrib/security/auditing/tests.py | 101 +- evennia/contrib/simpledoor.py | 18 +- evennia/contrib/slow_exit.py | 13 +- evennia/contrib/talking_npc.py | 41 +- evennia/contrib/tests.py | 2042 ++++++++++------- evennia/contrib/tree_select.py | 122 +- evennia/contrib/turnbattle/tb_basic.py | 66 +- evennia/contrib/turnbattle/tb_equip.py | 218 +- evennia/contrib/turnbattle/tb_items.py | 336 +-- evennia/contrib/turnbattle/tb_magic.py | 383 ++-- evennia/contrib/turnbattle/tb_range.py | 176 +- .../tutorial_examples/bodyfunctions.py | 6 +- .../tutorial_examples/cmdset_red_button.py | 45 +- .../tutorial_examples/example_batch_code.py | 7 +- .../tutorial_examples/red_button_scripts.py | 7 +- evennia/contrib/tutorial_examples/tests.py | 6 +- evennia/contrib/tutorial_world/mob.py | 34 +- evennia/contrib/tutorial_world/objects.py | 257 ++- evennia/contrib/tutorial_world/rooms.py | 233 +- evennia/contrib/unixcommand.py | 9 +- evennia/contrib/wilderness.py | 68 +- evennia/game_template/commands/command.py | 3 + .../game_template/commands/default_cmdsets.py | 4 + .../server/conf/connection_screens.py | 5 +- evennia/game_template/server/conf/mssp.py | 55 +- .../server/conf/serversession.py | 1 + evennia/game_template/typeclasses/accounts.py | 2 + evennia/game_template/typeclasses/channels.py | 1 + .../game_template/typeclasses/characters.py | 1 + evennia/game_template/typeclasses/exits.py | 1 + evennia/game_template/typeclasses/objects.py | 1 + evennia/game_template/typeclasses/rooms.py | 1 + evennia/game_template/typeclasses/scripts.py | 1 + evennia/game_template/world/prototypes.py | 8 +- evennia/help/admin.py | 35 +- evennia/help/manager.py | 4 +- evennia/help/migrations/0001_initial.py | 67 +- .../migrations/0002_auto_20170606_1731.py | 16 +- .../migrations/0003_auto_20190128_1820.py | 49 +- evennia/help/models.py | 75 +- evennia/locks/lockfuncs.py | 88 +- evennia/locks/lockhandler.py | 140 +- evennia/locks/tests.py | 191 +- evennia/objects/admin.py | 116 +- evennia/objects/manager.py | 227 +- evennia/objects/migrations/0001_initial.py | 113 +- .../migrations/0002_auto_20140917_0756.py | 26 +- ...r_defaultexit_defaultobject_defaultroom.py | 39 +- .../migrations/0004_auto_20150118_1622.py | 8 +- .../migrations/0005_auto_20150403_2339.py | 20 +- .../migrations/0006_auto_20170606_1731.py | 40 +- .../migrations/0007_objectdb_db_account.py | 18 +- .../migrations/0008_auto_20170705_1736.py | 16 +- .../0009_remove_objectdb_db_player.py | 11 +- .../migrations/0010_auto_20190128_1820.py | 137 +- evennia/objects/models.py | 82 +- evennia/objects/objects.py | 508 ++-- evennia/objects/tests.py | 50 +- evennia/prototypes/menus.py | 1129 +++++---- evennia/prototypes/protfuncs.py | 33 +- evennia/prototypes/prototypes.py | 387 ++-- evennia/prototypes/spawner.py | 280 ++- evennia/prototypes/tests.py | 1129 +++++---- evennia/scripts/admin.py | 38 +- evennia/scripts/manager.py | 22 +- evennia/scripts/migrations/0001_initial.py | 121 +- .../migrations/0002_auto_20150118_1625.py | 8 +- ...techannelhandler_validateidmappercache_.py | 80 +- .../migrations/0004_auto_20150306_1354.py | 23 +- .../migrations/0005_auto_20150306_1441.py | 20 +- .../migrations/0006_auto_20150310_2249.py | 22 +- .../migrations/0007_auto_20150403_2339.py | 20 +- .../migrations/0008_auto_20170606_1731.py | 22 +- .../migrations/0009_scriptdb_db_account.py | 19 +- .../migrations/0010_auto_20170705_1736.py | 16 +- .../0011_remove_scriptdb_db_player.py | 13 +- .../migrations/0012_auto_20190128_1820.py | 127 +- evennia/scripts/models.py | 47 +- evennia/scripts/monitorhandler.py | 19 +- evennia/scripts/scripthandler.py | 27 +- evennia/scripts/scripts.py | 38 +- evennia/scripts/taskhandler.py | 24 +- evennia/scripts/tests.py | 4 +- evennia/scripts/tickerhandler.py | 62 +- evennia/server/admin.py | 9 +- evennia/server/amp_client.py | 21 +- evennia/server/connection_wizard.py | 116 +- evennia/server/deprecations.py | 77 +- evennia/server/evennia_launcher.py | 594 +++-- evennia/server/evennia_runner.py | 174 +- evennia/server/game_index_client/client.py | 61 +- evennia/server/game_index_client/service.py | 13 +- evennia/server/initial_setup.py | 34 +- evennia/server/inputfuncs.py | 153 +- evennia/server/migrations/0001_initial.py | 22 +- .../migrations/0002_auto_20190128_2311.py | 21 +- evennia/server/models.py | 31 +- evennia/server/portal/amp.py | 135 +- evennia/server/portal/amp_server.py | 115 +- evennia/server/portal/grapevine.py | 85 +- evennia/server/portal/irc.py | 128 +- evennia/server/portal/mccp.py | 14 +- evennia/server/portal/mssp.py | 25 +- evennia/server/portal/mxp.py | 16 +- evennia/server/portal/naws.py | 14 +- evennia/server/portal/portal.py | 120 +- evennia/server/portal/portalsessionhandler.py | 84 +- evennia/server/portal/rss.py | 17 +- evennia/server/portal/ssh.py | 78 +- evennia/server/portal/ssl.py | 15 +- evennia/server/portal/suppress_ga.py | 2 +- evennia/server/portal/telnet.py | 79 +- evennia/server/portal/telnet_oob.py | 92 +- evennia/server/portal/telnet_ssl.py | 27 +- evennia/server/portal/tests.py | 165 +- evennia/server/portal/ttype.py | 89 +- evennia/server/portal/webclient.py | 14 +- evennia/server/portal/webclient_ajax.py | 49 +- evennia/server/profiling/dummyrunner.py | 52 +- .../server/profiling/dummyrunner_settings.py | 61 +- evennia/server/profiling/memplot.py | 73 +- evennia/server/profiling/settings_mixin.py | 4 +- evennia/server/profiling/test_queries.py | 13 +- evennia/server/profiling/tests.py | 87 +- evennia/server/server.py | 234 +- evennia/server/serversession.py | 34 +- evennia/server/session.py | 52 +- evennia/server/sessionhandler.py | 166 +- evennia/server/signals.py | 20 +- evennia/server/tests/test_amp_connection.py | 11 +- evennia/server/tests/test_initial_setup.py | 1 - evennia/server/tests/test_launcher.py | 41 +- evennia/server/tests/test_misc.py | 43 +- evennia/server/tests/test_server.py | 247 +- evennia/server/tests/testrunner.py | 4 +- evennia/server/throttle.py | 23 +- evennia/server/validators.py | 25 +- evennia/server/webserver.py | 32 +- evennia/settings_default.py | 338 +-- evennia/typeclasses/admin.py | 131 +- evennia/typeclasses/attributes.py | 268 ++- evennia/typeclasses/managers.py | 103 +- .../typeclasses/migrations/0001_initial.py | 162 +- .../migrations/0002_auto_20150109_0913.py | 19 +- ...ltplayer_defaultroom_defaultscript_dono.py | 117 +- .../migrations/0004_auto_20151101_1759.py | 20 +- .../migrations/0005_auto_20160625_1812.py | 10 +- ...o_add_dbmodel_value_for_tags_attributes.py | 20 +- .../0007_tag_migrations_may_be_slow.py | 82 +- .../migrations/0008_lock_and_perm_rename.py | 36 +- .../0009_rename_player_cmdsets_typeclasses.py | 67 +- .../0010_delete_old_player_tables.py | 10 +- .../migrations/0011_auto_20190128_1820.py | 185 +- .../0012_attrs_to_picklev4_may_be_slow.py | 10 +- evennia/typeclasses/models.py | 193 +- evennia/typeclasses/tags.py | 130 +- evennia/typeclasses/tests.py | 33 +- evennia/utils/ansi.py | 285 +-- evennia/utils/batchprocessors.py | 29 +- evennia/utils/containers.py | 41 +- evennia/utils/create.py | 147 +- evennia/utils/dbserialize.py | 99 +- evennia/utils/eveditor.py | 208 +- evennia/utils/evform.py | 99 +- evennia/utils/evmenu.py | 433 ++-- evennia/utils/evmore.py | 53 +- evennia/utils/evtable.py | 288 ++- evennia/utils/gametime.py | 34 +- evennia/utils/idmapper/manager.py | 6 +- evennia/utils/idmapper/models.py | 124 +- evennia/utils/idmapper/tests.py | 21 +- evennia/utils/inlinefuncs.py | 40 +- evennia/utils/logger.py | 64 +- evennia/utils/optionclasses.py | 30 +- evennia/utils/optionhandler.py | 12 +- evennia/utils/picklefield.py | 37 +- evennia/utils/search.py | 34 +- evennia/utils/test_resources.py | 47 +- .../utils/tests/data/prototypes_example.py | 12 +- evennia/utils/tests/test_create_functions.py | 27 +- evennia/utils/tests/test_eveditor.py | 202 +- evennia/utils/tests/test_evform.py | 82 +- evennia/utils/tests/test_evmenu.py | 125 +- evennia/utils/tests/test_tagparsing.py | 265 ++- evennia/utils/tests/test_utils.py | 60 +- evennia/utils/text2html.py | 155 +- evennia/utils/utils.py | 359 +-- evennia/utils/validatorfuncs.py | 62 +- evennia/web/urls.py | 8 +- evennia/web/utils/general_context.py | 43 +- evennia/web/utils/middleware.py | 1 + evennia/web/utils/tests.py | 66 +- evennia/web/webclient/urls.py | 3 +- evennia/web/webclient/views.py | 5 +- evennia/web/website/forms.py | 37 +- evennia/web/website/templatetags/addclass.py | 6 +- evennia/web/website/tests.py | 146 +- evennia/web/website/urls.py | 98 +- evennia/web/website/views.py | 271 ++- 299 files changed, 19107 insertions(+), 11681 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a607c34f0d..12001f08777 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ without arguments starts a full interactive Python console. - Remove `pillow` requirement (install especially if using imagefield) - Add Simplified Korean translation (user aceamro) - Show warning on `start -l` if settings contains values unsafe for production. +- Make code auto-formatted with Black. ## Evennia 0.9 (2018-2019) diff --git a/Makefile b/Makefile index 450c30c9b55..b7b67250a54 100644 --- a/Makefile +++ b/Makefile @@ -1,12 +1,39 @@ -default: install +# This is used with `make