diff --git a/docs/pylib/create_toctree.py b/docs/pylib/create_toctree.py index a6c87ad4d4..8e80cc4d01 100644 --- a/docs/pylib/create_toctree.py +++ b/docs/pylib/create_toctree.py @@ -6,40 +6,69 @@ directive somewhere. """ import re +from collections import defaultdict from sphinx.errors import DocumentError from pathlib import Path -from os.path import abspath, dirname, join as pathjoin, sep +from os.path import abspath, dirname, join as pathjoin, sep, relpath _IGNORE_FILES = [] -_SOURCE_DIR = pathjoin(dirname(dirname(abspath(__file__))), "source") +_SOURCEDIR_NAME = "source" +_SOURCE_DIR = pathjoin(dirname(dirname(abspath(__file__))), _SOURCEDIR_NAME) _TOC_FILE = pathjoin(_SOURCE_DIR, "toc.md") +_CURRFILE = None def create_toctree(): """ Create source/toc.md file - """ - docref_map = {} + """ + global _CURRFILE + + def _get_rel_source_ref(path): + """Get the path relative the source/ dir""" + pathparts = path.split("/") + # we allow a max of 4 levels of nesting in the source dir + ind = pathparts[-4:].index(_SOURCEDIR_NAME) + # get the part after source/ + pathparts = pathparts[-4 + 1 + ind:] + url = "/".join(pathparts) + # get the reference, without .md + url = url.rsplit(".", 1)[0] + return url + + toc_map = {} + docref_map = defaultdict(dict) for path in Path(_SOURCE_DIR).rglob("*.md"): # find the source/ part of the path and strip it out - # support nesting of 3 within source/ dir - fname = path.name - if fname in _IGNORE_FILES: + + if path.name in _IGNORE_FILES: # this is the name including .md continue - ind = path.parts[-4:].index("source") - pathparts = path.parts[-4 + 1 + ind:] - url = "/".join(pathparts) - url = url.rsplit(".", 1)[0] - fname = fname.rsplit(".", 1)[0] - if fname in docref_map: + + sourcepath = path.as_posix() + # get name and url relative to source/ + fname = path.name.rsplit(".", 1)[0] + src_url = _get_rel_source_ref(sourcepath) + + # check for duplicate files + if fname in toc_map: + duplicate_src_url = toc_map[fname] raise DocumentError( - f" Tried to add '{url}.md' when '{docref_map[fname]}.md' already exists.\n" + f" Tried to add {src_url}.md, but a file {duplicate_src_url}.md already exists.\n" " Evennia's auto-link-corrector does not accept doc-files with the same \n" " name, even in different folders. Rename one.\n") - docref_map[fname] = url + toc_map[fname] = src_url + + # find relative links to all other files + for targetpath in Path(_SOURCE_DIR).rglob("*.md"): + + targetname = targetpath.name.rsplit(".", 1)[0] + targetpath = targetpath.as_posix() + url = relpath(targetpath, dirname(sourcepath)) + docref_map[sourcepath][targetname] = url.rsplit(".", 1)[0] + # normal reference-links [txt](urls) ref_regex = re.compile(r"\[(?P[\w -\[\]]+?)\]\((?P.+?)\)", re.I + re.S + re.U) @@ -57,8 +86,8 @@ def create_toctree(): fname = part[0] if part else fname fname = fname.rsplit(".", 1)[0] fname, *anchor = fname.rsplit("#", 1) - if fname in docref_map: - urlout = docref_map[fname] + ('#' + anchor[0] if anchor else '') + if _CURRFILE in docref_map and fname in docref_map[_CURRFILE]: + urlout = docref_map[_CURRFILE][fname] + ('#' + anchor[0] if anchor else '') if urlout != url: print(f" Remapped link [{txt}]({url}) -> [{txt}]({urlout})") else: @@ -76,8 +105,8 @@ def create_toctree(): fname = part[0] if part else fname fname = fname.rsplit(".", 1)[0] fname, *anchor = fname.rsplit("#", 1) - if fname in docref_map: - urlout = docref_map[fname] + ('#' + anchor[0] if anchor else '') + if _CURRFILE in docref_map and fname in docref_map[_CURRFILE]: + urlout = docref_map[_CURRFILE][fname] + ('#' + anchor[0] if anchor else '') if urlout != url: print(f" Remapped link [{txt}]: {url} -> [{txt}]: {urlout}") else: @@ -86,7 +115,11 @@ def create_toctree(): # replace / correct links in all files count = 0 - for path in Path(_SOURCE_DIR).rglob("*.md"): + for path in sorted(Path(_SOURCE_DIR).rglob("*.md"), key=lambda p: p.name): + + # from pudb import debugger;debugger.Debugger().set_trace() + _CURRFILE = path.as_posix() + with open(path, 'r') as fil: intxt = fil.read() outtxt = ref_regex.sub(_sub, intxt) @@ -95,7 +128,7 @@ def create_toctree(): with open(path, 'w') as fil: fil.write(outtxt) count += 1 - print(f"Auto-relinked links in {path.name}") + print(f" ----- Auto-relinked links in {path.name} ------") if count > 0: print(f"Auto-corrected links in {count} documents.") @@ -104,7 +137,7 @@ def create_toctree(): with open(_TOC_FILE, "w") as fil: fil.write("# Toc\n") - for ref in sorted(docref_map.values()): + for ref in sorted(toc_map.values()): if ref == "toc": continue diff --git a/docs/source/Coding/Evennia-API.md b/docs/source/Coding/Flat-API.md similarity index 100% rename from docs/source/Coding/Evennia-API.md rename to docs/source/Coding/Flat-API.md diff --git a/docs/source/Evennia-API.md b/docs/source/Evennia-API.md new file mode 100644 index 0000000000..0deb49bad0 --- /dev/null +++ b/docs/source/Evennia-API.md @@ -0,0 +1,4 @@ +# API + +- [evennia.accounts](api:evennia.accounts) +- diff --git a/docs/source/Howto/StartingTutorial/Add-a-simple-new-web-page.md b/docs/source/Howto/Starting/Add-a-simple-new-web-page.md similarity index 100% rename from docs/source/Howto/StartingTutorial/Add-a-simple-new-web-page.md rename to docs/source/Howto/Starting/Add-a-simple-new-web-page.md diff --git a/docs/source/Howto/StartingTutorial/Adding-Command-Tutorial.md b/docs/source/Howto/Starting/Adding-Command-Tutorial.md similarity index 95% rename from docs/source/Howto/StartingTutorial/Adding-Command-Tutorial.md rename to docs/source/Howto/Starting/Adding-Command-Tutorial.md index 0b92d4fe01..f4724b4167 100644 --- a/docs/source/Howto/StartingTutorial/Adding-Command-Tutorial.md +++ b/docs/source/Howto/Starting/Adding-Command-Tutorial.md @@ -1,6 +1,6 @@ # Adding Command Tutorial -This is a quick first-time tutorial expanding on the [Commands](Component/Commands) documentation. +This is a quick first-time tutorial expanding on the [Commands](../../Component/Commands) documentation. Let's assume you have just downloaded Evennia, installed it and created your game folder (let's call it just `mygame` here). Now you want to try to add a new command. This is the fastest way to do it. @@ -16,7 +16,7 @@ example code. 1. Give your class a useful _docstring_. A docstring is the string at the very top of a class or function/method. The docstring at the top of the command class is read by Evennia to become the help entry for the Command (see - [Command Auto-help](Component/Help-System#command-auto-help-system)). + [Command Auto-help](../../Component/Help-System#command-auto-help-system)). 1. Define a class method `func(self)` that echoes your input back to you. Below is an example how this all could look for the echo command: @@ -47,7 +47,7 @@ Below is an example how this all could look for the echo command: ## Step 2: Adding the Command to a default Cmdset -The command is not available to use until it is part of a [Command Set](Component/Command-Sets). In this +The command is not available to use until it is part of a [Command Set](../../Component/Command-Sets). In this example we will go the easiest route and add it to the default Character commandset that already exists. @@ -93,7 +93,7 @@ If you want to overload existing default commands (such as `look` or `get`), jus command with the same key as the old one - it will then replace it. Just remember that you must use `@reload` to see any changes. -See [Commands](Component/Commands) for many more details and possibilities when defining Commands and using +See [Commands](../../Component/Commands) for many more details and possibilities when defining Commands and using Cmdsets in various ways. @@ -131,7 +131,7 @@ only make the new merged cmdset permanent on that *single* object. Often you wan this particular class to have this cmdset. To make sure all new created objects get your new merged set, put the `cmdset.add` call in your -custom [Typeclasses](Component/Typeclasses)' `at_object_creation` method: +custom [Typeclasses](../../Component/Typeclasses)' `at_object_creation` method: ```python # e.g. in mygame/typeclasses/objects.py diff --git a/docs/source/Howto/StartingTutorial/Adding-Object-Typeclass-Tutorial.md b/docs/source/Howto/Starting/Adding-Object-Typeclass-Tutorial.md similarity index 88% rename from docs/source/Howto/StartingTutorial/Adding-Object-Typeclass-Tutorial.md rename to docs/source/Howto/Starting/Adding-Object-Typeclass-Tutorial.md index 6dbc665d54..cb33c2d12e 100644 --- a/docs/source/Howto/StartingTutorial/Adding-Object-Typeclass-Tutorial.md +++ b/docs/source/Howto/Starting/Adding-Object-Typeclass-Tutorial.md @@ -13,9 +13,9 @@ When you create a new Evennia game (with for example `evennia --init mygame`) Ev automatically create empty child classes `Object`, `Character`, `Room` and `Exit` respectively. They are found `mygame/typeclasses/objects.py`, `mygame/typeclasses/rooms.py` etc. -> Technically these are all [Typeclassed](Component/Typeclasses), which can be ignored for now. In +> Technically these are all [Typeclassed](../../Component/Typeclasses), which can be ignored for now. In > `mygame/typeclasses` are also base typeclasses for out-of-character things, notably -> [Channels](Component/Communications), [Accounts](Component/Accounts) and [Scripts](Component/Scripts). We don't cover those in +> [Channels](../../Component/Communications), [Accounts](../../Component/Accounts) and [Scripts](../../Component/Scripts). We don't cover those in > this tutorial. For your own game you will most likely want to expand on these very simple beginnings. It's normal @@ -62,13 +62,13 @@ up. you will find the traceback. The most common error is that you have some sort of syntax error in your class. -Note that the [Locks](Component/Locks) and [Attribute](Component/Attributes) which are set in the typeclass could just +Note that the [Locks](../../Component/Locks) and [Attribute](../../Component/Attributes) which are set in the typeclass could just as well have been set using commands in-game, so this is a *very* simple example. ## Storing data on initialization The `at_object_creation` is only called once, when the object is first created. This makes it ideal -for database-bound things like [Attributes](Component/Attributes). But sometimes you want to create temporary +for database-bound things like [Attributes](../../Component/Attributes). But sometimes you want to create temporary properties (things that are not to be stored in the database but still always exist every time the object is created). Such properties can be initialized in the `at_init` method on the object. `at_init` is called every time the object is loaded into memory. @@ -86,7 +86,7 @@ def at_init(self): self.ndb.mylist = [] ``` -> Note: As mentioned in the [Typeclasses](Component/Typeclasses) documentation, `at_init` replaces the use of +> Note: As mentioned in the [Typeclasses](../../Component/Typeclasses) documentation, `at_init` replaces the use of > the standard `__init__` method of typeclasses due to how the latter may be called in situations > other than you'd expect. So use `at_init` where you would normally use `__init__`. diff --git a/docs/source/Howto/StartingTutorial/Building-Quickstart.md b/docs/source/Howto/Starting/Building-Quickstart.md similarity index 92% rename from docs/source/Howto/StartingTutorial/Building-Quickstart.md rename to docs/source/Howto/Starting/Building-Quickstart.md index f95cbb639b..71c61c6c9b 100644 --- a/docs/source/Howto/StartingTutorial/Building-Quickstart.md +++ b/docs/source/Howto/Starting/Building-Quickstart.md @@ -1,8 +1,8 @@ # Building Quickstart -The [default command](Component/Default-Command-Help) definitions coming with Evennia -follows a style [similar](Concept/Using-MUX-as-a-Standard) to that of MUX, so the +The [default command](../../Component/Default-Command-Help) definitions coming with Evennia +follows a style [similar](../../Concept/Using-MUX-as-a-Standard) to that of MUX, so the commands should be familiar if you used any such code bases before. > Throughout the larger documentation you may come across commands prefixed @@ -85,14 +85,14 @@ dropped in the room, then try this: lock box = get:false() -Locks represent a rather [big topic](Component/Locks), but for now that will do what we want. This will lock +Locks represent a rather [big topic](../../Component/Locks), but for now that will do what we want. This will lock the box so noone can lift it. The exception is superusers, they override all locks and will pick it up anyway. Make sure you are quelling your superuser powers and try to get the box now: > get box You can't get that. -Think thís default error message looks dull? The `get` command looks for an [Attribute](Component/Attributes) +Think thís default error message looks dull? The `get` command looks for an [Attribute](../../Component/Attributes) named `get_err_msg` for returning a nicer error message (we just happen to know this, you would need to peek into the [code](https://github.com/evennia/evennia/blob/master/evennia/commands/default/general.py#L235) for @@ -114,7 +114,7 @@ Commands tutorial](Adding-Command-Tutorial) for help with creating your first ow ## Get a Personality -[Scripts](Component/Scripts) are powerful out-of-character objects useful for many "under the hood" things. +[Scripts](../../Component/Scripts) are powerful out-of-character objects useful for many "under the hood" things. One of their optional abilities is to do things on a timer. To try out a first script, let's put one on ourselves. There is an example script in `evennia/contrib/tutorial_examples/bodyfunctions.py` that is called `BodyFunctions`. To add this to us we will use the `script` command: @@ -137,14 +137,14 @@ When you are tired of your character's "insights", kill the script with script/stop self = tutorial_examples.bodyfunctions.BodyFunctions You create your own scripts in Python, outside the game; the path you give to `script` is literally -the Python path to your script file. The [Scripts](Component/Scripts) page explains more details. +the Python path to your script file. The [Scripts](../../Component/Scripts) page explains more details. ## Pushing Your Buttons If we get back to the box we made, there is only so much fun you can do with it at this point. It's just a dumb generic object. If you renamed it to `stone` and changed its description noone would be -the wiser. However, with the combined use of custom [Typeclasses](Component/Typeclasses), [Scripts](Component/Scripts) -and object-based [Commands](Component/Commands), you could expand it and other items to be as unique, complex +the wiser. However, with the combined use of custom [Typeclasses](../../Component/Typeclasses), [Scripts](../../Component/Scripts) +and object-based [Commands](../../Component/Commands), you could expand it and other items to be as unique, complex and interactive as you want. Let's take an example. So far we have only created objects that use the default object typeclass @@ -161,7 +161,7 @@ sure to look in`evennia/contrib/` so you don't have to write the full path every - one red button. The RedButton is an example object intended to show off a few of Evennia's features. You will find -that the [Typeclass](Component/Typeclasses) and [Commands](Component/Commands) controlling it are inside +that the [Typeclass](../../Component/Typeclasses) and [Commands](../../Component/Commands) controlling it are inside `evennia/contrib/tutorial_examples/`. If you wait for a while (make sure you dropped it!) the button will blink invitingly. Why don't you diff --git a/docs/source/Howto/StartingTutorial/Coding-Introduction.md b/docs/source/Howto/Starting/Coding-Introduction.md similarity index 95% rename from docs/source/Howto/StartingTutorial/Coding-Introduction.md rename to docs/source/Howto/Starting/Coding-Introduction.md index f7fbe4f1ae..bbef668d6a 100644 --- a/docs/source/Howto/StartingTutorial/Coding-Introduction.md +++ b/docs/source/Howto/Starting/Coding-Introduction.md @@ -10,7 +10,7 @@ Here are some pointers to get you going. Evennia is developed using Python. Even if you are more of a designer than a coder, it is wise to learn how to read and understand basic Python code. If you are new to Python, or need a refresher, -take a look at our two-part [Python introduction](Howto/StartingTutorial/Python-basic-introduction). +take a look at our two-part [Python introduction](Python-basic-introduction). ### Explore Evennia interactively @@ -31,7 +31,7 @@ This will open an Evennia-aware python shell (using ipython). From within this s evennia. That is, enter `evennia.` and press the `` key. This will show you all the resources made -available at the top level of Evennia's "flat API". See the [flat API](Coding/Evennia-API) page for more +available at the top level of Evennia's "flat API". See the [flat API](../../Evennia-API) page for more info on how to explore it efficiently. You can complement your exploration by peeking at the sections of the much more detailed [Developer @@ -52,7 +52,7 @@ using such a checker can be a good start to weed out the simple problems. ### Plan before you code -Before you start coding away at your dream game, take a look at our [Game Planning](Howto/Game-Planning) +Before you start coding away at your dream game, take a look at our [Game Planning](../Game-Planning) page. It might hopefully help you avoid some common pitfalls and time sinks. ### Code in your game folder, not in the evennia/ repository @@ -64,7 +64,7 @@ it out into your game folder and edit it there. If you find that Evennia doesn't support some functionality you need, make a [Feature Request](feature-request) about it. Same goes for [bugs][bug]. If you add features or fix bugs -yourself, please consider [Contributing](Contributing) your changes upstream! +yourself, please consider [Contributing](../../Contributing) your changes upstream! ### Learn to read tracebacks diff --git a/docs/source/Howto/StartingTutorial/Execute-Python-Code.md b/docs/source/Howto/Starting/Execute-Python-Code.md similarity index 94% rename from docs/source/Howto/StartingTutorial/Execute-Python-Code.md rename to docs/source/Howto/Starting/Execute-Python-Code.md index 62a5a095b5..95a24fb1a7 100644 --- a/docs/source/Howto/StartingTutorial/Execute-Python-Code.md +++ b/docs/source/Howto/Starting/Execute-Python-Code.md @@ -16,11 +16,11 @@ system. - **self** / **me** - the calling object (i.e. you) - **here** - the current caller's location -- **obj** - a dummy [Object](Component/Objects) instance -- **evennia** - Evennia's [flat API](Coding/Evennia-API) - through this you can access all of Evennia. +- **obj** - a dummy [Object](../../Component/Objects) instance +- **evennia** - Evennia's [flat API](../../Evennia-API) - through this you can access all of Evennia. For accessing other objects in the same room you need to use `self.search(name)`. For objects in -other locations, use one of the `evennia.search_*` methods. See [below](Howto/StartingTutorial/Execute-Python-Code#finding- +other locations, use one of the `evennia.search_*` methods. See [below](Execute-Python-Code#finding- objects). ## Returning output @@ -117,4 +117,4 @@ of other editing features, such as tab-completion and `__doc__`-string reading. In [2]: evennia.managers.objects.all() Out[3]: [, , ...] -See the page about the [Evennia-API](Coding/Evennia-API) for more things to explore. \ No newline at end of file +See the page about the [Evennia-API](../../Evennia-API) for more things to explore. \ No newline at end of file diff --git a/docs/source/Howto/StartingTutorial/First-Steps-Coding.md b/docs/source/Howto/Starting/First-Steps-Coding.md similarity index 92% rename from docs/source/Howto/StartingTutorial/First-Steps-Coding.md rename to docs/source/Howto/Starting/First-Steps-Coding.md index d8cd8b2241..d915d782cb 100644 --- a/docs/source/Howto/StartingTutorial/First-Steps-Coding.md +++ b/docs/source/Howto/Starting/First-Steps-Coding.md @@ -10,7 +10,7 @@ Started](Getting-Started) instructions. You should have initialized a new game f `evennia --init foldername` command. We will in the following assume this folder is called "mygame". -It might be a good idea to eye through the brief [Coding Introduction](Howto/StartingTutorial/Coding-Introduction) too +It might be a good idea to eye through the brief [Coding Introduction](Coding-Introduction) too (especially the recommendations in the section about the evennia "flat" API and about using `evennia shell` will help you here and in the future). @@ -57,13 +57,13 @@ up with a new command to view those attributes. return self.db.strength, self.db.agility, self.db.magic ``` -1. [Reload](Setup/Start-Stop-Reload) the server (you will still be connected to the game after doing +1. [Reload](../../Setup/Start-Stop-Reload) the server (you will still be connected to the game after doing this). Note that if you examine *yourself* you will *not* see any new Attributes appear yet. Read the next section to understand why. #### Updating Yourself -It's important to note that the new [Attributes](Component/Attributes) we added above will only be stored on +It's important to note that the new [Attributes](../../Component/Attributes) we added above will only be stored on *newly* created characters. The reason for this is simple: The `at_object_creation` method, where we added those Attributes, is per definition only called when the object is *first created*, then never again. This is usually a good thing since those Attributes may change over time - calling that hook @@ -112,8 +112,8 @@ what the `@update` command does under the hood). From in-game you can do the sam MyClass.objects.all()] ``` -See the [Object Typeclass tutorial](Howto/StartingTutorial/Adding-Object-Typeclass-Tutorial) for more help and the -[Typeclasses](Component/Typeclasses) and [Attributes](Component/Attributes) page for detailed documentation about +See the [Object Typeclass tutorial](Adding-Object-Typeclass-Tutorial) for more help and the +[Typeclasses](../../Component/Typeclasses) and [Attributes](../../Component/Attributes) page for detailed documentation about Typeclasses and Attributes. #### Troubleshooting: Updating Yourself @@ -159,7 +159,7 @@ tracebacks and you'll be able to resolve the vast majority of common errors easi ### Add a New Default Command The `@py` command used above is only available to privileged users. We want any player to be able to -see their stats. Let's add a new [command](Component/Commands) to list the abilities we added in the previous +see their stats. Let's add a new [command](../../Component/Commands) to list the abilities we added in the previous section. 1. Open `mygame/commands/command.py`. You could in principle put your command anywhere but this @@ -201,7 +201,7 @@ the bottom of this file: self.add(CmdAbilities()) ``` -1. [Reload](Setup/Start-Stop-Reload) the server (noone will be disconnected by doing this). +1. [Reload](../../Setup/Start-Stop-Reload) the server (noone will be disconnected by doing this). You (and anyone else) should now be able to use `abilities` (or its alias `abi`) as part of your normal commands in-game: @@ -211,8 +211,8 @@ abilities STR: 5, AGI: 4, MAG: 2 ``` -See the [Adding a Command tutorial](Howto/StartingTutorial/Adding-Command-Tutorial) for more examples and the -[Commands](Component/Commands) section for detailed documentation about the Command system. +See the [Adding a Command tutorial](Adding-Command-Tutorial) for more examples and the +[Commands](../../Component/Commands) section for detailed documentation about the Command system. ### Make a New Type of Object @@ -263,7 +263,7 @@ functionality. Here is an example of how the file could look: 1. Check your code for bugs. Tracebacks will appear on your command line or log. If you have a grave Syntax Error in your code, the source file itself will fail to load which can cause issues with the -entire cmdset. If so, fix your bug and [reload the server from the command line](Setup/Start-Stop-Reload) +entire cmdset. If so, fix your bug and [reload the server from the command line](../../Setup/Start-Stop-Reload) (noone will be disconnected by doing this). 1. Use `@create/drop stone:wiseobject.WiseObject` to create a talkative stone. If the `@create` command spits out a warning or cannot find the typeclass (it will tell you which paths it searched), diff --git a/docs/source/Howto/StartingTutorial/Parsing-command-arguments,-theory-and-best-practices.md b/docs/source/Howto/Starting/Parsing-command-arguments,-theory-and-best-practices.md similarity index 99% rename from docs/source/Howto/StartingTutorial/Parsing-command-arguments,-theory-and-best-practices.md rename to docs/source/Howto/Starting/Parsing-command-arguments,-theory-and-best-practices.md index 7fc50b4722..034df143d9 100644 --- a/docs/source/Howto/StartingTutorial/Parsing-command-arguments,-theory-and-best-practices.md +++ b/docs/source/Howto/Starting/Parsing-command-arguments,-theory-and-best-practices.md @@ -2,7 +2,7 @@ This tutorial will elaborate on the many ways one can parse command arguments. The first step after -[adding a command](Howto/StartingTutorial/Adding-Command-Tutorial) usually is to parse its arguments. There are lots of +[adding a command](Adding-Command-Tutorial) usually is to parse its arguments. There are lots of ways to do it, but some are indeed better than others and this tutorial will try to present them. If you're a Python beginner, this tutorial might help you a lot. If you're already familiar with @@ -652,7 +652,7 @@ about... what is this `"book"`? To get an object from a string, we perform an Evennia search. Evennia provides a `search` method on all typeclassed objects (you will most likely use the one on characters or accounts). This method -supports a very wide array of arguments and has [its own tutorial](Howto/StartingTutorial/Tutorial-Searching-For-Objects). +supports a very wide array of arguments and has [its own tutorial](Tutorial-Searching-For-Objects). Some examples of useful cases follow: ### Local searches diff --git a/docs/source/Howto/StartingTutorial/Python-basic-introduction.md b/docs/source/Howto/Starting/Python-basic-introduction.md similarity index 94% rename from docs/source/Howto/StartingTutorial/Python-basic-introduction.md rename to docs/source/Howto/Starting/Python-basic-introduction.md index 1c17fc301f..ad3f45ebbb 100644 --- a/docs/source/Howto/StartingTutorial/Python-basic-introduction.md +++ b/docs/source/Howto/Starting/Python-basic-introduction.md @@ -7,14 +7,14 @@ and low on detail. There are countless Python guides and tutorials, books and vi learning more in-depth - use them! **Contents:** -- [Evennia Hello world](Howto/StartingTutorial/Python-basic-introduction#evennia-hello-world) -- [Importing modules](Howto/StartingTutorial/Python-basic-introduction#importing-modules) -- [Parsing Python errors](Howto/StartingTutorial/Python-basic-introduction#parsing-python-errors) -- [Our first function](Howto/StartingTutorial/Python-basic-introduction#our-first-function) -- [Looking at the log](Howto/StartingTutorial/Python-basic-introduction#looking-at-the-log) -- (continued in [part 2](Howto/StartingTutorial/Python-basic-tutorial-part-two)) +- [Evennia Hello world](Python-basic-introduction#evennia-hello-world) +- [Importing modules](Python-basic-introduction#importing-modules) +- [Parsing Python errors](Python-basic-introduction#parsing-python-errors) +- [Our first function](Python-basic-introduction#our-first-function) +- [Looking at the log](Python-basic-introduction#looking-at-the-log) +- (continued in [part 2](Python-basic-tutorial-part-two)) -This quickstart assumes you have [gotten Evennia started](Setup/Getting-Started). You should make sure +This quickstart assumes you have [gotten Evennia started](../../Setup/Getting-Started). You should make sure that you are able to see the output from the server in the console from which you started it. Log into the game either with a mud client on `localhost:4000` or by pointing a web browser to `localhost:4001/webclient`. Log in as your superuser (the user you created during install). @@ -263,5 +263,5 @@ enter `Ctrl-C` or `Cmd-C` depending on your system. As a game dev it is importa log output when working in Evennia - many errors will only appear with full details here. You may sometimes have to scroll up in the history if you miss it. -This tutorial is continued in [Part 2](Howto/StartingTutorial/Python-basic-tutorial-part-two), where we'll start learning +This tutorial is continued in [Part 2](Python-basic-tutorial-part-two), where we'll start learning about objects and to explore the Evennia library. \ No newline at end of file diff --git a/docs/source/Howto/StartingTutorial/Python-basic-tutorial-part-two.md b/docs/source/Howto/Starting/Python-basic-tutorial-part-two.md similarity index 95% rename from docs/source/Howto/StartingTutorial/Python-basic-tutorial-part-two.md rename to docs/source/Howto/Starting/Python-basic-tutorial-part-two.md index d99d2314ee..fc270b001d 100644 --- a/docs/source/Howto/StartingTutorial/Python-basic-tutorial-part-two.md +++ b/docs/source/Howto/Starting/Python-basic-tutorial-part-two.md @@ -1,16 +1,16 @@ # Python basic tutorial part two -[In the first part](Howto/StartingTutorial/Python-basic-introduction) of this Python-for-Evennia basic tutorial we learned +[In the first part](Python-basic-introduction) of this Python-for-Evennia basic tutorial we learned how to run some simple Python code from inside the game. We also made our first new *module* containing a *function* that we called. Now we're going to start exploring the very important subject of *objects*. **Contents:** -- [On the subject of objects](Howto/StartingTutorial/Python-basic-tutorial-part-two#on-the-subject-of-objects) -- [Exploring the Evennia library](Howto/StartingTutorial/Python-basic-tutorial-part-two#exploring-the-evennia-library) -- [Tweaking our Character class](Howto/StartingTutorial/Python-basic-tutorial-part-two#tweaking-our-character-class) -- [The Evennia shell](Howto/StartingTutorial/Python-basic-tutorial-part-two#the-evennia-shell) -- [Where to go from here](Howto/StartingTutorial/Python-basic-tutorial-part-two#where-to-go-from-here) +- [On the subject of objects](Python-basic-tutorial-part-two#on-the-subject-of-objects) +- [Exploring the Evennia library](Python-basic-tutorial-part-two#exploring-the-evennia-library) +- [Tweaking our Character class](Python-basic-tutorial-part-two#tweaking-our-character-class) +- [The Evennia shell](Python-basic-tutorial-part-two#the-evennia-shell) +- [Where to go from here](Python-basic-tutorial-part-two#where-to-go-from-here) ### On the subject of objects @@ -171,7 +171,7 @@ There are lots of things in there. There are some docs but most of those have to distribution of Evennia and does not concern us right now. The `evennia` subfolder is what we are looking for. *This* is what you are accessing when you do `from evennia import ...`. It's set up by Evennia as a good place to find modules when the server starts. The exact layout of the Evennia -library [is covered by our directory overview](Coding/Directory-Overview#evennia-library-layout). You can +library [is covered by our directory overview](../../Coding/Directory-Overview#evennia-library-layout). You can also explore it [online on github](https://github.com/evennia/evennia/tree/master/evennia). The structure of the library directly reflects how you import from it. @@ -224,7 +224,7 @@ is the same thing, just a little easier to remember. > To access the shortcuts of the flat API you *must* use `from evennia import > ...`. Using something like `import evennia.DefaultCharacter` will not work. -> See [more about the Flat API here](Coding/Evennia-API). +> See [more about the Flat API here](../../Evennia-API). ### Tweaking our Character class @@ -283,8 +283,8 @@ brief summary of the methods we find in `DefaultCharacter` (follow in the code t roughly where things happen):: - `basetype_setup` is called by Evennia only once, when a Character is first created. In the -`DefaultCharacter` class it sets some particular [Locks](Component/Locks) so that people can't pick up and -puppet Characters just like that. It also adds the [Character Cmdset](Component/Command-Sets) so that +`DefaultCharacter` class it sets some particular [Locks](../../Component/Locks) so that people can't pick up and +puppet Characters just like that. It also adds the [Character Cmdset](../../Component/Command-Sets) so that Characters always can accept command-input (this should usually not be modified - the normal hook to override is `at_object_creation`, which is called after `basetype_setup` (it's in the parent)). - `at_after_move` makes it so that every time the Character moves, the `look` command is @@ -484,7 +484,7 @@ convenient for quickly exploring code without having to go digging through the f This should give you a running start using Python with Evennia. If you are completely new to programming or Python you might want to look at a more formal Python tutorial. You can find links -and resources [on our link page](Links). +and resources [on our link page](../../Links). We have touched upon many of the concepts here but to use Evennia and to be able to follow along in the code, you will need basic understanding of Python diff --git a/docs/source/Howto/Starting/Starting-Introduction.md b/docs/source/Howto/Starting/Starting-Introduction.md new file mode 100644 index 0000000000..3dfea1a12b --- /dev/null +++ b/docs/source/Howto/Starting/Starting-Introduction.md @@ -0,0 +1,3 @@ +# Evennia Starting Tutorial + +This starts your path towards making your own game in Evennia. diff --git a/docs/source/Howto/StartingTutorial/Turn-based-Combat-System.md b/docs/source/Howto/Starting/Turn-based-Combat-System.md similarity index 96% rename from docs/source/Howto/StartingTutorial/Turn-based-Combat-System.md rename to docs/source/Howto/Starting/Turn-based-Combat-System.md index b8564a2b1c..8bb075ffaa 100644 --- a/docs/source/Howto/StartingTutorial/Turn-based-Combat-System.md +++ b/docs/source/Howto/Starting/Turn-based-Combat-System.md @@ -31,7 +31,7 @@ allows for emoting as part of combat which is an advantage for roleplay-heavy ga To implement a freeform combat system all you need is a dice roller and a roleplaying rulebook. See [contrib/dice.py](https://github.com/evennia/evennia/blob/master/evennia/contrib/dice.py) for an example dice roller. To implement at twitch-based system you basically need a few combat -[commands](Component/Commands), possibly ones with a [cooldown](Howto/Command-Cooldown). You also need a [game rule +[commands](../../Component/Commands), possibly ones with a [cooldown](../Command-Cooldown). You also need a [game rule module](Implementing-a-game-rule-system) that makes use of it. We will focus on the turn-based variety here. @@ -61,22 +61,22 @@ reported. A new turn then begins. For creating the combat system we will need the following components: -- A combat handler. This is the main mechanic of the system. This is a [Script](Component/Scripts) object +- A combat handler. This is the main mechanic of the system. This is a [Script](../../Component/Scripts) object created for each combat. It is not assigned to a specific object but is shared by the combating characters and handles all the combat information. Since Scripts are database entities it also means that the combat will not be affected by a server reload. -- A combat [command set](Component/Command-Sets) with the relevant commands needed for combat, such as the +- A combat [command set](../../Component/Command-Sets) with the relevant commands needed for combat, such as the various attack/defend options and the `flee/disengage` command to leave the combat mode. - A rule resolution system. The basics of making such a module is described in the [rule system tutorial](Implementing-a-game-rule-system). We will only sketch such a module here for our end-turn combat resolution. -- An `attack` [command](Component/Commands) for initiating the combat mode. This is added to the default +- An `attack` [command](../../Component/Commands) for initiating the combat mode. This is added to the default command set. It will create the combat handler and add the character(s) to it. It will also assign the combat command set to the characters. ## The combat handler -The _combat handler_ is implemented as a stand-alone [Script](Component/Scripts). This Script is created when +The _combat handler_ is implemented as a stand-alone [Script](../../Component/Scripts). This Script is created when the first Character decides to attack another and is deleted when no one is fighting any more. Each handler represents one instance of combat and one combat only. Each instance of combat can hold any number of characters but each character can only be part of one combat at a time (a player would @@ -89,7 +89,7 @@ don't use this very much here this might allow the combat commands on the charac update the combat handler state directly. _Note: Another way to implement a combat handler would be to use a normal Python object and handle -time-keeping with the [TickerHandler](Component/TickerHandler). This would require either adding custom hook +time-keeping with the [TickerHandler](../../Component/TickerHandler). This would require either adding custom hook methods on the character or to implement a custom child of the TickerHandler class to track turns. Whereas the TickerHandler is easy to use, a Script offers more power in this case._ @@ -506,7 +506,7 @@ class CmdAttack(Command): ``` The `attack` command will not go into the combat cmdset but rather into the default cmdset. See e.g. -the [Adding Command Tutorial](Howto/StartingTutorial/Adding-Command-Tutorial) if you are unsure about how to do this. +the [Adding Command Tutorial](Adding-Command-Tutorial) if you are unsure about how to do this. ## Expanding the example diff --git a/docs/source/Howto/StartingTutorial/Tutorial-Searching-For-Objects.md b/docs/source/Howto/Starting/Tutorial-Searching-For-Objects.md similarity index 94% rename from docs/source/Howto/StartingTutorial/Tutorial-Searching-For-Objects.md rename to docs/source/Howto/Starting/Tutorial-Searching-For-Objects.md index a10162f092..db33675f19 100644 --- a/docs/source/Howto/StartingTutorial/Tutorial-Searching-For-Objects.md +++ b/docs/source/Howto/Starting/Tutorial-Searching-For-Objects.md @@ -9,8 +9,8 @@ explains Evennia's tools for searching. ## Things to search for The first thing to consider is the base type of the thing you are searching for. Evennia organizes -its database into a few main tables: [Objects](Component/Objects), [Accounts](Component/Accounts), [Scripts](Component/Scripts), -[Channels](Component/Communications#channels), [Messages](Communication#Msg) and [Help Entries](Component/Help-System). +its database into a few main tables: [Objects](../../Component/Objects), [Accounts](../../Component/Accounts), [Scripts](../../Component/Scripts), +[Channels](../../Component/Communications#channels), [Messages](Communication#Msg) and [Help Entries](../../Component/Help-System). Most of the time you'll likely spend your time searching for Objects and the occasional Accounts. So to find an entity, what can be searched for? @@ -22,20 +22,20 @@ the database field for `.key` is instead named `username` (this is a Django requ don't specify search-type, you'll usually search based on key. *Aliases* are extra names given to Objects using something like `@alias` or `obj.aliases.add('name')`. The main search functions (see below) will automatically search for aliases whenever you search by-key. - - [Tags](Component/Tags) are the main way to group and identify objects in Evennia. Tags can most often be + - [Tags](../../Component/Tags) are the main way to group and identify objects in Evennia. Tags can most often be used (sometimes together with keys) to uniquely identify an object. For example, even though you have two locations with the same name, you can separate them by their tagging (this is how Evennia implements 'zones' seen in other systems). Tags can also have categories, to further organize your data for quick lookups. - - An object's [Attributes](Component/Attributes) can also used to find an object. This can be very useful but + - An object's [Attributes](../../Component/Attributes) can also used to find an object. This can be very useful but since Attributes can store almost any data they are far less optimized to search for than Tags or keys. -- The object's [Typeclass](Component/Typeclasses) indicate the sub-type of entity. A Character, Flower or +- The object's [Typeclass](../../Component/Typeclasses) indicate the sub-type of entity. A Character, Flower or Sword are all types of Objects. A Bot is a kind of Account. The database field is called `typeclass_path` and holds the full Python-path to the class. You can usually specify the `typeclass` as an argument to Evennia's search functions as well as use the class directly to limit queries. -- The `location` is only relevant for [Objects](Component/Objects) but is a very common way to weed down the +- The `location` is only relevant for [Objects](../../Component/Objects) but is a very common way to weed down the number of candidates before starting to search. The reason is that most in-game commands tend to operate on things nearby (in the same room) so the choices can be limited from the start. - The database id or the '#dbref' is unique (and never re-used) within each database table. So while @@ -50,7 +50,7 @@ around and searching by Tags and/or keys will usually get you what you need. ## Getting objects inside another -All in-game [Objects](Component/Objects) have a `.contents` property that returns all objects 'inside' them +All in-game [Objects](../../Component/Objects) have a `.contents` property that returns all objects 'inside' them (that is, all objects which has its `.location` property set to that object. This is a simple way to get everything in a room and is also faster since this lookup is cached and won't hit the database. @@ -64,7 +64,7 @@ location except `obj`. ## Searching using `Object.search` -Say you have a [command](Component/Commands), and you want it to do something to a target. You might be +Say you have a [command](../../Component/Commands), and you want it to do something to a target. You might be wondering how you retrieve that target in code, and that's where Evennia's search utilities come in. In the most common case, you'll often use the `search` method of the `Object` or `Account` typeclasses. In a command, the `.caller` property will refer back to the object using the command @@ -133,7 +133,7 @@ class CmdListHangouts(default_cmds.MuxCommand): ", ".join(str(ob) for ob in hangouts))) ``` -This uses the `search_tag` function to find all objects previously tagged with [Tags](Component/Tags) +This uses the `search_tag` function to find all objects previously tagged with [Tags](../../Component/Tags) "hangout" and with category "location tags". Other important search methods in `utils.search` are @@ -303,7 +303,7 @@ nice enough to alias the `db_key` field so you can normally just do `char.key` t name, the database field is actually called `db_key` and the real name must be used for the purpose of building a query. -> Don't confuse database fields with [Attributes](Component/Attributes) you set via `obj.db.attr = 'foo'` or +> Don't confuse database fields with [Attributes](../../Component/Attributes) you set via `obj.db.attr = 'foo'` or `obj.attributes.add()`. Attributes are custom database entities *linked* to an object. They are not separate fields *on* that object like `db_key` or `db_location` are. You can get attached Attributes manually through the `db_attributes` many-to-many field in the same way as `db_tags` above. diff --git a/docs/source/Howto/StartingTutorial/Tutorial-for-basic-MUSH-like-game.md b/docs/source/Howto/Starting/Tutorial-for-basic-MUSH-like-game.md similarity index 95% rename from docs/source/Howto/StartingTutorial/Tutorial-for-basic-MUSH-like-game.md rename to docs/source/Howto/Starting/Tutorial-for-basic-MUSH-like-game.md index ab9d08f9c3..2b3b226eaf 100644 --- a/docs/source/Howto/StartingTutorial/Tutorial-for-basic-MUSH-like-game.md +++ b/docs/source/Howto/Starting/Tutorial-for-basic-MUSH-like-game.md @@ -7,7 +7,7 @@ focused on free form storytelling. Even if you are not interested in MUSH:es, th first game-type to try since it's not so code heavy. You will be able to use the same principles for building other types of games. -The tutorial starts from scratch. If you did the [First Steps Coding](Howto/StartingTutorial/First-Steps-Coding) tutorial +The tutorial starts from scratch. If you did the [First Steps Coding](First-Steps-Coding) tutorial already you should have some ideas about how to do some of the steps already. The following are the (very simplistic and cut-down) features we will implement (this was taken from @@ -61,7 +61,7 @@ class Character(DefaultCharacter): self.db.combat_score = 1 ``` -We defined two new [Attributes](Component/Attributes) `power` and `combat_score` and set them to default +We defined two new [Attributes](../../Component/Attributes) `power` and `combat_score` and set them to default values. Make sure to `@reload` the server if you had it already running (you need to reload every time you update your python code, don't worry, no accounts will be disconnected by the reload). @@ -94,8 +94,8 @@ check it. Using this method however will make it easy to add more functionality What we need are the following: -- One character generation [Command](Component/Commands) to set the "Power" on the `Character`. -- A chargen [CmdSet](Component/Command-Sets) to hold this command. Lets call it `ChargenCmdset`. +- One character generation [Command](../../Component/Commands) to set the "Power" on the `Character`. +- A chargen [CmdSet](../../Component/Command-Sets) to hold this command. Lets call it `ChargenCmdset`. - A custom `ChargenRoom` type that makes this set of commands available to players in such rooms. - One such room to test things in. @@ -104,7 +104,7 @@ What we need are the following: For this tutorial we will add all our new commands to `mygame/commands/command.py` but you could split your commands into multiple module if you prefered. -For this tutorial character generation will only consist of one [Command](Component/Commands) to set the +For this tutorial character generation will only consist of one [Command](../../Component/Commands) to set the Character s "power" stat. It will be called on the following MUSH-like form: +setpower 4 @@ -156,7 +156,7 @@ This is a pretty straightforward command. We do some error checking, then set th We use a `help_category` of "mush" for all our commands, just so they are easy to find and separate in the help list. -Save the file. We will now add it to a new [CmdSet](Component/Command-Sets) so it can be accessed (in a full +Save the file. We will now add it to a new [CmdSet](../../Component/Command-Sets) so it can be accessed (in a full chargen system you would of course have more than one command here). Open `mygame/commands/default_cmdsets.py` and import your `command.py` module at the top. We also @@ -210,7 +210,7 @@ class ChargenRoom(Room): ``` Note how new rooms created with this typeclass will always start with `ChargenCmdset` on themselves. Don't forget the `permanent=True` keyword or you will lose the cmdset after a server reload. For -more information about [Command Sets](Component/Command-Sets) and [Commands](Component/Commands), see the respective +more information about [Command Sets](../../Component/Command-Sets) and [Commands](../../Component/Commands), see the respective links. ### Testing chargen @@ -242,7 +242,7 @@ between fixes. Don't continue until the creation seems to have worked okay. This should bring you to the chargen room. Being in there you should now have the `+setpower` command available, so test it out. When you leave (via the `finish` exit), the command will go away and trying `+setpower` should now give you a command-not-found error. Use `ex me` (as a privileged -user) to check so the `Power` [Attribute](Component/Attributes) has been set correctly. +user) to check so the `Power` [Attribute](../../Component/Attributes) has been set correctly. If things are not working, make sure your typeclasses and commands are free of bugs and that you have entered the paths to the various command sets and commands correctly. Check the logs or command @@ -391,7 +391,7 @@ There are a few ways to define the NPC class. We could in theory create a custom and put a custom NPC-specific cmdset on all NPCs. This cmdset could hold all manipulation commands. Since we expect NPC manipulation to be a common occurrence among the user base however, we will instead put all relevant NPC commands in the default command set and limit eventual access with -[Permissions and Locks](Component/Locks#Permissions). +[Permissions and Locks](../../Component/Locks#Permissions). ### Creating an NPC with +createNPC @@ -443,13 +443,13 @@ class CmdCreateNPC(Command): exclude=caller) ``` Here we define a `+createnpc` (`+createNPC` works too) that is callable by everyone *not* having the -`nonpcs` "[permission](Component/Locks#Permissions)" (in Evennia, a "permission" can just as well be used to +`nonpcs` "[permission](../../Component/Locks#Permissions)" (in Evennia, a "permission" can just as well be used to block access, it depends on the lock we define). We create the NPC object in the caller's current location, using our custom `Character` typeclass to do so. We set an extra lock condition on the NPC, which we will use to check who may edit the NPC later -- we allow the creator to do so, and anyone with the Builders permission (or higher). See -[Locks](Component/Locks) for more information about the lock system. +[Locks](../../Component/Locks) for more information about the lock system. Note that we just give the object default permissions (by not specifying the `permissions` keyword to the `create_object()` call). In some games one might want to give the NPC the same permissions @@ -464,7 +464,7 @@ Since we re-used our custom character typeclass, our new NPC already has a *Powe defaults to 1. How do we change this? There are a few ways we can do this. The easiest is to remember that the `power` attribute is just a -simple [Attribute](Component/Attributes) stored on the NPC object. So as a Builder or Admin we could set this +simple [Attribute](../../Component/Attributes) stored on the NPC object. So as a Builder or Admin we could set this right away with the default `@set` command: @set mynpc/power = 6 @@ -649,6 +649,6 @@ The simple "Power" game mechanic should be easily expandable to something more f useful, same is true for the combat score principle. The `+attack` could be made to target a specific player (or npc) and automatically compare their relevant attributes to determine a result. -To continue from here, you can take a look at the [Tutorial World](Contrib/Tutorial-World-Introduction). For +To continue from here, you can take a look at the [Tutorial World](../../Contrib/Tutorial-World-Introduction). For more specific ideas, see the [other tutorials and hints](Tutorials) as well as the [Developer Central](Developer-Central). \ No newline at end of file diff --git a/docs/source/Howto/StartingTutorial/Web-Tutorial.md b/docs/source/Howto/Starting/Web-Tutorial.md similarity index 100% rename from docs/source/Howto/StartingTutorial/Web-Tutorial.md rename to docs/source/Howto/Starting/Web-Tutorial.md diff --git a/docs/source/api/modules.rst b/docs/source/api/modules.rst new file mode 100644 index 0000000000..346ade5719 --- /dev/null +++ b/docs/source/api/modules.rst @@ -0,0 +1,7 @@ +evennia +======= + +.. toctree:: + :maxdepth: 4 + + evennia diff --git a/docs/source/evennia-api.md.py b/docs/source/evennia-api.md.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/evennia/accounts/accounts.py b/evennia/accounts/accounts.py index 305b841956..94502427db 100644 --- a/evennia/accounts/accounts.py +++ b/evennia/accounts/accounts.py @@ -40,7 +40,7 @@ from evennia.utils.optionhandler import OptionHandler from django.utils.translation import gettext as _ from random import getrandbits -__all__ = ("DefaultAccount",) +__all__ = ("DefaultAccount", "DefaultGuest") _SESSIONS = None