From 85952f80750e7b27d91949fc1001c7fcdc64dca9 Mon Sep 17 00:00:00 2001 From: Griatch Date: Sun, 30 Sep 2012 10:32:41 +0200 Subject: [PATCH] Updated ReST documentation. --- .../source/wiki/AddingCommandTutorial.rst | 27 ++- docs/sphinx/source/wiki/AsyncProcess.rst | 89 ++++++- docs/sphinx/source/wiki/Attributes.rst | 4 + .../sphinx/source/wiki/BuildingQuickstart.rst | 6 +- docs/sphinx/source/wiki/Commands.rst | 15 +- docs/sphinx/source/wiki/ConnectionScreen.rst | 8 +- docs/sphinx/source/wiki/Contributing.rst | 12 +- .../sphinx/source/wiki/DefaultCommandHelp.rst | 41 ++-- docs/sphinx/source/wiki/DeveloperCentral.rst | 9 +- docs/sphinx/source/wiki/ExecutePythonCode.rst | 16 +- docs/sphinx/source/wiki/GamePlanning.rst | 16 +- docs/sphinx/source/wiki/GettingStarted.rst | 66 +++--- docs/sphinx/source/wiki/Licensing.rst | 2 +- docs/sphinx/source/wiki/Links.rst | 8 + docs/sphinx/source/wiki/Locks.rst | 5 +- docs/sphinx/source/wiki/RemovingColour.rst | 107 +-------- docs/sphinx/source/wiki/Scripts.rst | 49 ++-- docs/sphinx/source/wiki/ServerConf.rst | 12 +- docs/sphinx/source/wiki/SessionProtocols.rst | 66 ++++++ .../source/wiki/TutorialWorldIntroduction.rst | 5 +- docs/sphinx/source/wiki/Tutorials.rst | 14 ++ docs/sphinx/source/wiki/Typeclasses.rst | 224 ++---------------- docs/sphinx/source/wiki/VersionControl.rst | 2 +- 23 files changed, 371 insertions(+), 432 deletions(-) diff --git a/docs/sphinx/source/wiki/AddingCommandTutorial.rst b/docs/sphinx/source/wiki/AddingCommandTutorial.rst index a4d61bca46..23e47f0efa 100644 --- a/docs/sphinx/source/wiki/AddingCommandTutorial.rst +++ b/docs/sphinx/source/wiki/AddingCommandTutorial.rst @@ -15,9 +15,9 @@ new additional commands of your own. #. Go to ``game/gamesrc/commands``. #. There is a subfolder here named ``examples``. *Copy* the files - ``examples/command.py`` and ``examples/cmdset.py`` to where you are. - You can rename them as you please, but in this example we assume you - don't. + ``examples/command.py`` and ``examples/cmdset.py`` to your current + directory (game/gamesrc/commands). You can rename them as you please, + but in this example we assume you don't. #. Edit ``game/settings.py``, adding the following line: ``CMDSET_DEFAULT="game.gamesrc.commands.cmdset.DefaultCmdSet"`` @@ -45,7 +45,7 @@ Creating a custom command # file game/gamesrc/commands/command.py #[...] - class CmdEcho(MuxCommand): + class CmdEcho(default_cmds.MuxCommand): """ Simple command example @@ -75,8 +75,8 @@ command set we already prepared. #. Edit your recently copied ``game/gamesrc/commands/cmdset.py`` #. In this copied module you will find the ``DefaultCmdSet`` class already imported and prepared for you. Import your new command module - here with ``from game.gamesrc.commands import echo``. -#. Add a line ``self.add(echo.CmdEcho())`` to ``DefaultCmdSet``, in the + here with ``from game.gamesrc.commands.command import CmdEcho``. +#. Add a line ``self.add(CmdEcho())`` to ``DefaultCmdSet``, in the ``at_cmdset_creation`` method (the template tells you where). This is approximately how it should look at this point: @@ -84,7 +84,7 @@ command set we already prepared. # file gamesrc/commands/examples/cmdset.py #[...] - from game.gamesrc.commands import echo + from game.gamesrc.commands.command import CmdEcho #[...] class DefaultCmdSet(default_cmds.DefaultCmdSet): @@ -97,7 +97,7 @@ command set we already prepared. # all commands added after this point will extend or # overwrite the default commands. - self.add(echo.CmdEcho()) + self.add(CmdEcho()) #. Reboot/restart Evennia (``@reload`` from inside the game). You should now be able to use your new ``echo`` command from inside the game. @@ -107,8 +107,11 @@ If you have trouble, make sure to check the log for error messages (probably due to syntax errors in your command definition). Adding new commands to the default cmdset in the future now only -involves creating the function class and adding it to the same place. If -you want to overload existing default commands (such as ``look`` or -``get``), just add your new command with the same key as the old one - -it will overload the default one. Just remember that you must +involves creating the function class and adding it to the cmdset in the +same place. If you want to overload existing default commands (such as +``look`` or ``get``), just add your new command with the same key as the +old one - it will overload the default one. Just remember that you must ``@reload`` the server before you see any changes. + +See `Commands `_ for many more details and possibilities +when defining Commands and using Cmdsets in various ways. diff --git a/docs/sphinx/source/wiki/AsyncProcess.rst b/docs/sphinx/source/wiki/AsyncProcess.rst index 9d50e22b74..64ad968fa1 100644 --- a/docs/sphinx/source/wiki/AsyncProcess.rst +++ b/docs/sphinx/source/wiki/AsyncProcess.rst @@ -1,8 +1,7 @@ Asynchronous code ================= -*This is considered an advanced topic, probably not useful for most -users.* +*This is considered an advanced topic.* Synchronous versus Asynchronous ------------------------------- @@ -68,9 +67,7 @@ call to try to deal with the result from ``long_running_function`` above for processing any data from the function. Instead one has to use *callbacks*. -``utils.run_async`` takes two optional arguments, ``at_return`` and -``at_err``. Both of these should be function defitions. Each will be -called automatically. +``utils.run_async`` takes reserved arguments. - ``at_return(r)`` (the *callback*) is called when the asynchronous function (``long_running_function`` above) finishes successfully. The @@ -82,6 +79,8 @@ called automatically. def at_return(r): print r +- ``at_return_kwargs`` - an optional dictionary that will be fed as + keyword arguments to the ``at_return`` callback. - ``at_err(e)`` (the *errback*) is called if the asynchronous function fails and raises an exception. This exception is passed to the errback wrapped in a *Failure* object ``e``. If you do not supply an @@ -94,6 +93,9 @@ called automatically. def at_err(e): print "There was an error:", str(e) +- ``at_err_kwargs`` - an optional dictionary that will be fed as + keyword arguments to the ``at_err`` errback. + An example of making an asynchronous call from inside a `Command `_ definition: @@ -126,6 +128,83 @@ and go on with what else need to be done. *Whenever* it finishes, the ``at_return`` function will be called and the final value will pop up for us to see. If not we will see an error message. +Process Pool +------------ + +The ``ProcPool`` is an Evennia subsystem that launches a pool of +processes based on the `ampoule `_ +package (included with Evennia). When active, ``run_async`` will use +this pool to offload its commands. ``ProcPool`` is deactivated by +default, it can be turned on with ``settings.PROCPOOL_ENABLED``. *It +should be noted that the default SQLite3 database is not suitable for +for multiprocess operation. So if you use ``ProcPool`` you should +consider switching to another database such as MySQL or PostgreSQL.* + +The Process Pool makes several additional options available to +``run_async``. + +The following keyword arguments make sense when ``ProcPool`` is active: + +- ``use_thread`` - this force-reverts back to thread operation (as + above). It effectively deactivates all additional features + ``ProcPool`` offers. +- ``proc_timeout`` - this enforces a timeout for the running process in + seconds; after this time the process will be killed. +- ``at_return``, ``at_err`` - these work the same as above. + +In addition to feeding a single callable to ``run_async``, the first +argument may also be a source string. This is a piece of python source +code that will be executed in a subprocess via ``ProcPool``. Any extra +keyword arguments to ``run_async`` that are not one of the reserved ones +will be used to specify what will be available in the execution +environment. + +There is one special variable used in the remove execution: ``_return``. +This is a function, and all data fed to ``_return`` will be returned +from the execution environment and appear as input to your ``at_return`` +callback (if it is defined). You can call ``_return`` multiple times in +your code - the return value will then be a list. + +Example: + +:: + + from src.utils.utils import run_async + + source = """ + from time import sleep + sleep(5) # sleep five secs + val = testvar + 5 + _return(val) + _return(val + 5) + """ + + # we assume myobj is a character retrieved earlier + # these callbacks will just print results/errors + def callback(ret): + myobj.msg(ret) + def errback(err): + myobj.msg(err) + testvar = 3 + + # run async + run_async(source, at_return=callback, at_err=errback, testvar=testvar) + + # this will return '[8, 13]' + +You can also test the async mechanism from in-game using the ``@py`` +command: + +:: + + @py from src.utils.utils import run_async;run_async("_return(1+2)",at_return=self.msg) + +Note: The code execution runs without any security checks, so it should +not be available to unprivileged users. Try +``contrib.evlang.evlang.limited_exec`` for running a more restricted +version of Python for untrusted users. This will use ``run_async`` under +the hood. + Assorted notes -------------- diff --git a/docs/sphinx/source/wiki/Attributes.rst b/docs/sphinx/source/wiki/Attributes.rst index 19e852687d..e8aa5101e9 100644 --- a/docs/sphinx/source/wiki/Attributes.rst +++ b/docs/sphinx/source/wiki/Attributes.rst @@ -78,6 +78,10 @@ behaviour. Fast assignment --------------- +*Depracation Warning: Fast assigment is deprecated and should not be +used - it will be removed in the future. Use the ``db`` operator +explicitly when saving to the database.* + For quick testing you can in principle skip the ``db`` operator and assign Attributes like you would any normal Python property: diff --git a/docs/sphinx/source/wiki/BuildingQuickstart.rst b/docs/sphinx/source/wiki/BuildingQuickstart.rst index 2fcd2c7e96..06621f321b 100644 --- a/docs/sphinx/source/wiki/BuildingQuickstart.rst +++ b/docs/sphinx/source/wiki/BuildingQuickstart.rst @@ -215,9 +215,9 @@ button is meant to be pushed. You know you want to. Creating a room called 'house' ------------------------------ -The main command for shaping the game world is ``@dig``. If you for -example are standing in Limbo, you can in one go dig a route 'north' to -your new house location like this: +The main command for shaping the game world is ``@dig``. For example, if +you are standing in Limbo you can dig a route to your new house location +like this: :: diff --git a/docs/sphinx/source/wiki/Commands.rst b/docs/sphinx/source/wiki/Commands.rst index 9adae4e518..6428857d34 100644 --- a/docs/sphinx/source/wiki/Commands.rst +++ b/docs/sphinx/source/wiki/Commands.rst @@ -335,18 +335,19 @@ server, or you run @py self.cmdset.delete('game.gamesrc.commands.mycmdset.MyCmdSet') -For more permanent addition, read the -[Commands#Adding\_a\_new\_command\_-*a\_step\_by\_step\_guide -step-by-step guide] below. Generally you can customize which command -sets are added to your objects by using ``self.cmdset.add()`` or -``self.cmdset.add_default()``.* +For a quick tutorial on setting up things more permanently read the +`Step by step +tutorial `_ +for a different way of approaching it. Generally you can customize which +command sets are added to your objects by using ``self.cmdset.add()`` or +``self.cmdset.add_default()``. Adding and merging command sets ------------------------------- -\_Note: This is an advanced topic. It's useful to know about, but you +*Note: This is an advanced topic. It's useful to know about, but you might want to skip it if this is your first time learning about -commands. +commands.* CmdSets have the special ability that they can be *merged* together into new sets. This would happen if you, for example, did diff --git a/docs/sphinx/source/wiki/ConnectionScreen.rst b/docs/sphinx/source/wiki/ConnectionScreen.rst index 433f57d5dd..3f289e8520 100644 --- a/docs/sphinx/source/wiki/ConnectionScreen.rst +++ b/docs/sphinx/source/wiki/ConnectionScreen.rst @@ -7,14 +7,16 @@ tells you how to connect. :: + ============================================================== - Welcome to Evennia, version HG-Beta! + Welcome to Evennia, version Beta-ra4d24e8a3cab+! If you have an existing account, connect to it by typing: - connect + connect If you need to create an account, type (without the <>'s): - create "" + create + If you have spaces in your username, enclose it in quotes. Enter help for more info. look will re-show this screen. ============================================================== diff --git a/docs/sphinx/source/wiki/Contributing.rst b/docs/sphinx/source/wiki/Contributing.rst index 2bfff0cd41..30898b7427 100644 --- a/docs/sphinx/source/wiki/Contributing.rst +++ b/docs/sphinx/source/wiki/Contributing.rst @@ -8,10 +8,14 @@ Contributing with Documentation Evennia depends heavily on good documentation and we are always looking for extra eyes and hands to improve it. Even small things such as fixing -typos is a great help. To edit the wiki yourself you need contributor -access. Otherwise, it goes a long way just pointing out wiki errors so -devs can fix them (in an Issue or just over chat/forum). You can also -commit wiki changes over Mercurial - just go to the wiki repository +typos are a great help! + +The documentation is a wiki and to edit it you need wiki-contributor +access. We are happy to give this - just ask (on the forum/mailing list +or in the chat channel) if you want to help out. Otherwise, it goes a +long way just pointing out wiki errors so devs can fix them (in an Issue +or just over chat/forum). You can also commit wiki changes over +Mercurial - just go to the wiki repository `here `_ and then continue from point ``2`` below. diff --git a/docs/sphinx/source/wiki/DefaultCommandHelp.rst b/docs/sphinx/source/wiki/DefaultCommandHelp.rst index 1b4679c7b6..88aba5f18b 100644 --- a/docs/sphinx/source/wiki/DefaultCommandHelp.rst +++ b/docs/sphinx/source/wiki/DefaultCommandHelp.rst @@ -137,7 +137,7 @@ module `_ = ``cmd:perm(emit) or perm(Builders)`` - `help\_category `_ = ``Admin`` - [`HelpSystem `_\ #Auto-help\_system Auto-help] @@ -319,7 +319,7 @@ module `_ = ``cmd:perm(batchcommands) or superuser()`` - `help\_category `_ = ``Building`` - [`HelpSystem `_\ #Auto-help\_system Auto-help] @@ -510,7 +510,7 @@ module `_ = ``cmd:perm(destroy) or perm(Builders)`` - `help\_category `_ = ``Building`` - [`HelpSystem `_\ #Auto-help\_system Auto-help] @@ -570,7 +570,7 @@ module `_ = ``cmd:perm(examine) or perm(Builders)`` - `help\_category `_ = ``Building`` - [`HelpSystem `_\ #Auto-help\_system Auto-help] @@ -600,7 +600,7 @@ module `_ = ``cmd:perm(find) or perm(Builders)`` - `help\_category `_ = ``Building`` - [`HelpSystem `_\ #Auto-help\_system Auto-help] @@ -721,7 +721,7 @@ module `_ = ``cmd: perm(@locks) or perm(Builders)`` - `help\_category `_ = ``Building`` - [`HelpSystem `_\ #Auto-help\_system Auto-help] @@ -1173,7 +1173,7 @@ module `_ = ``cmd: not pperm(channel_banned)`` - `help\_category `_ = ``Comms`` - [`HelpSystem `_\ #Auto-help\_system Auto-help] @@ -1270,7 +1270,7 @@ module `_ = ``cmd: serversetting(IMC2_ENABLED) and pperm(Wizards)`` - `help\_category `_ = ``Comms`` @@ -1436,7 +1436,7 @@ imctell (OOC command) ~~~~~~~~~~~~~~~~~~~~~ - ``key`` = ``imctell`` -- ``aliases`` = ``imcpage, imc2tell, imc2page`` +- ``aliases`` = ``imc2tell, imc2page, imcpage`` - `locks `_ = ``cmd: serversetting(IMC2_ENABLED)`` - `help\_category `_ = ``Comms`` - [`HelpSystem `_\ #Auto-help\_system Auto-help] @@ -1609,7 +1609,7 @@ access ~~~~~~ - ``key`` = ``access`` -- ``aliases`` = ``groups, hierarchy`` +- ``aliases`` = ``hierarchy, groups`` - `locks `_ = ``cmd:all()`` - `help\_category `_ = ``General`` - [`HelpSystem `_\ #Auto-help\_system Auto-help] @@ -1738,7 +1738,7 @@ inventory ~~~~~~~~~ - ``key`` = ``inventory`` -- ``aliases`` = ``inv, i`` +- ``aliases`` = ``i, inv`` - `locks `_ = ``cmd:all()`` - `help\_category `_ = ``General`` - [`HelpSystem `_\ #Auto-help\_system Auto-help] @@ -1805,7 +1805,7 @@ nick ~~~~ - ``key`` = ``nick`` -- ``aliases`` = ``nickname, nicks, @nick, alias`` +- ``aliases`` = ``@nick, nicks, nickname, alias`` - `locks `_ = ``cmd:all()`` - `help\_category `_ = ``General`` - [`HelpSystem `_\ #Auto-help\_system Auto-help] @@ -1942,7 +1942,7 @@ module `_ = ``cmd:perm(listobjects) or perm(Builders)`` - `help\_category `_ = ``System`` - [`HelpSystem `_\ #Auto-help\_system Auto-help] @@ -2041,7 +2041,7 @@ module `_ = ``cmd:perm(listscripts) or perm(Wizards)`` - `help\_category `_ = ``System`` - [`HelpSystem `_\ #Auto-help\_system Auto-help] @@ -2203,7 +2203,7 @@ connect (Unloggedin command) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - ``key`` = ``connect`` -- ``aliases`` = ``conn, con, co`` +- ``aliases`` = ``co, conn, con`` - `locks `_ = ``cmd:all()`` - `help\_category `_ = ``Unloggedin`` - [`HelpSystem `_\ #Auto-help\_system Auto-help] @@ -2214,16 +2214,19 @@ connect (Unloggedin command) Connect to the game. Usage (at login screen): - connect + connect playername password + connect "player name" "pass word" Use the create command to first create an account before logging in. + + If you have spaces in your name, enclose it in quotes. create (Unloggedin command) ~~~~~~~~~~~~~~~~~~~~~~~~~~~ - ``key`` = ``create`` -- ``aliases`` = ``cre, cr`` +- ``aliases`` = ``cr, cre`` - `locks `_ = ``cmd:all()`` - `help\_category `_ = ``Unloggedin`` - [`HelpSystem `_\ #Auto-help\_system Auto-help] @@ -2234,10 +2237,12 @@ create (Unloggedin command) Create a new account. Usage (at login screen): - create "playername" + create + create "player name" "pass word" This creates a new player account. + If you have spaces in your name, enclose it in quotes. help (Unloggedin command) diff --git a/docs/sphinx/source/wiki/DeveloperCentral.rst b/docs/sphinx/source/wiki/DeveloperCentral.rst index a7a611985e..76a7a15214 100644 --- a/docs/sphinx/source/wiki/DeveloperCentral.rst +++ b/docs/sphinx/source/wiki/DeveloperCentral.rst @@ -2,8 +2,8 @@ Developer Central ================= This page serves as a central nexus for useful information regarding -coding using the Evennia codebase, and also for development of the -codebase itself. Everyone is welcome to `help +coding using the Evennia codebase or developing the codebase itself. +Everyone is welcome to `help out `_! If you have any questions, please feel free to ask them in the `Forum/Discussion Group `_. If you want more docs on a @@ -19,8 +19,8 @@ General Evennia development information - `Introduction to coding with Evennia `_ - `Evennia Licensing FAQ `_ - `Contributing to Evennia `_ -- `Evennia Code Style - Guide `_ +- `Code Style + Guide `_ (Important!) - `Policy for 'MUX-like' default commands `_ - `Setting up a Mercurial environment for @@ -47,6 +47,7 @@ Evennia Component Documentation - `Help System `_ - `Nicks `_ - `Sessions and Protocols `_ +- `Caches `_ - `Web features `_ - `Configuration and module plugins `_ diff --git a/docs/sphinx/source/wiki/ExecutePythonCode.rst b/docs/sphinx/source/wiki/ExecutePythonCode.rst index 00842b1b60..cb1ba9e59a 100644 --- a/docs/sphinx/source/wiki/ExecutePythonCode.rst +++ b/docs/sphinx/source/wiki/ExecutePythonCode.rst @@ -86,20 +86,20 @@ entries found in ``ev.search_*``. (Note that since this becomes a simple statement, we don't have to wrap it in ``self.msg()`` to get the output). You can also use the database model managers directly (accessible through the ``objects`` properties -of database models or as ``ev.db_*``). This is a bit more flexible since -it gives you access to the full range of database search methods defined -in each manager. +of database models or as ``ev.managers.*``). This is a bit more flexible +since it gives you access to the full range of database search methods +defined in each manager. :: - @py ev.db_scripts.script_search("sys_game_time") + @py ev.managers.scripts.script_search("sys_game_time") <<< [] The managers are useful for all sorts of database studies. :: - @py ev.db_configvalues.all() + @py ev.managers.configvalues.all() <<< [, , ...] In doing so however, keep in mind the difference between `Typeclasses @@ -114,12 +114,12 @@ most situations. # this uses Evennia's manager method get_id(). # It returns a Character typeclass instance - @py ev.db_objects.get_id(1).__class__ + @py ev.managers.objects.get_id(1).__class__ <<< Character # this uses the standard Django get() query. # It returns a django database model instance. - @py ev.db_objects.get(id=1).__class__ + @py ev.managers.objects.get(id=1).__class__ <<< Running a Python Parser outside the game @@ -152,6 +152,6 @@ tab-completion and ``__doc__``-string reading. ... In [1]: import ev - In [2]: ev.db_objects.all() + In [2]: ev.managers.objects.all() Out[3]: [, , ...] diff --git a/docs/sphinx/source/wiki/GamePlanning.rst b/docs/sphinx/source/wiki/GamePlanning.rst index 3a4cec2358..27950c8018 100644 --- a/docs/sphinx/source/wiki/GamePlanning.rst +++ b/docs/sphinx/source/wiki/GamePlanning.rst @@ -18,12 +18,12 @@ your work fun for you, or motivation will falter. Making a full game is a lot of work as it is, you'll need all your motivation to make it a reality. -Remember that *99.99999% of all great game ideas never leads to an -online game*. So your first all overshadowing goal is to beat those odds -and get *something* out the door! *Even* if it's a scaled-down version -of your dream game, lacking many "must-have" features! It's better to -get it out there and expand on it later than to code in isolation -forever until you burn out, loose interest or your hard drive crashes. +Remember that *99.99999% of all great game ideas never lead to an online +game*. So your first all overshadowing goal is to beat those odds and +get *something* out the door! *Even* if it's a scaled-down version of +your dream game, lacking many "must-have" features! It's better to get +it out there and expand on it later than to code in isolation forever +until you burn out, lose interest or your hard drive crashes. Like is common with online games, getting a game out the door does not mean you are going to be "finished" with the game - most MUDs add @@ -49,7 +49,7 @@ game and what they need to be able to do. simple description enough? Can it be dark (description changed/hidden)? Should it have smells, sounds? Weather? Different terrain? How are those to be conveyed? Are there special "magic" - rooms that does things to people entering? Can a person hide in the + rooms that do things to people entering? Can a person hide in the room? Should all rooms have the ability to be this complex or should there be different types of rooms? Evennia allows you to change the very concept of rooms should you be very ambitious, but is that a @@ -67,7 +67,7 @@ game and what they need to be able to do. object. For a role playing game, you need to define chances of success ("rolls") for example. Will weather messages be random in every room or should it follow some sort of realistic pattern over - all rooms? Do you have an game-wide economy - if so, how is that + all rooms? Do you have a game-wide economy - if so, how is that supposed to work? If magic is dependent on the position of the planets, the planets must change with time. What about spreading rumors? Mail boxes? Bulletin boards? diff --git a/docs/sphinx/source/wiki/GettingStarted.rst b/docs/sphinx/source/wiki/GettingStarted.rst index 3ea68494c1..ac8abdb2b2 100644 --- a/docs/sphinx/source/wiki/GettingStarted.rst +++ b/docs/sphinx/source/wiki/GettingStarted.rst @@ -49,51 +49,41 @@ platform, please let us know. You'll need the following packages and minimum versions in order to run Evennia: -- **Python** (`http://www.python.org `_) +- **`Python `_** (v2.6+, not supporting v3.x) - - Version 2.6+. Obs- Python3.x is not supported. - - Windows users are recommended to use ActivePython - (`http://www.activestate.com/activepython/downloads `_) + - Windows users are recommended to use + `ActivePython `_ + instead. -- **Twisted** (`http://twistedmatrix.com `_) +- **`Twisted `_** (v10.0+) - - Version 10.0+ - - Twisted also requires: + - `ZopeInterface `_ + (v3.0+) - usually included in Twisted packages + - Windows users might also need + `pywin32 `_. - - ZopeInterface 3.0+ - (`http://www.zope.org/Products/ZopeInterface `_) - - For Windows only: pywin32 - (`http://sourceforge.net/projects/pywin32 `_) +- **`Django `_** (v1.3+ or latest dev + build recommended) -- **Django** - (`http://www.djangoproject.com `_) - - - Version 1.3+ or latest development versions highly recommended. - - PIL (Python Imaging Library) - (`http://www.pythonware.com/products/pil `_) - - not strictly required unless you use images in Django. + - `PIL `_ (Python Image + Library) - often distributed with Django. To download/update Evennia: -- **Mercurial** - (`http://mercurial.selenic.com/ `_) - - - This is needed to download and update Evennia itself. +- **`Mercurial `_** Optional packages: -- **South** - (`http://south.aeracode.org/ `_) +- **`South `_** (v0.7+) - - Version 0.7+ - - Optional, but highly recommended. Used for database migrations. + - Optional, but highly recommended. Makes it easy to keep up with + Evennia updates to the database schema. -- **Apache2** (`http://httpd.apache.org `_) +- **`Apache2 `_** - - Optional. Most likely you'll not need to bother with this since - Evennia runs its own threaded web server based on Twisted. Other - equivalent web servers with a Python interpreter module can also - be used. + - Optional. Only use if you don't want to use Evennia's own threaded + webserver. Other equivalent web servers with a Python interpreter + module can also be used. Installing pre-requisites ~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -115,6 +105,10 @@ Debian-derived systems (such as Ubuntu) you can do something like this apt-get install python python-django python-twisted mercurial python-django-south +(Gentoo note: Gentoo (and maybe other distros?) seems to distribute +Twisted in multiple packages. Beyond the main twisted package you will +also need to get at least twisted-conch and twisted-web too).\ ** + Few distros actually keep the latest updated security updates (notably django and twisted) in their repos though. So it might be worth to use Python's @@ -159,11 +153,11 @@ circumvent this bug for now. This affects also Unix/Linux systems, but those usually have the locale set out of the box. **Windows** users should first and foremost recognize that the Evennia -server is run from the command line, something which they might not be -familiar with. In the Windows launch menu, just start *All Programs -> -Accessories -> command prompt* and you will get the Windows command line -interface. There are plenty of online tutorials on using the Windows -command line, one example is found +server is run from the command line, something which some might not be +familiar with (based on the questions we have received). In the Windows +launch menu, just start *All Programs -> Accessories -> command prompt* +and you will get the Windows command line interface. There are plenty of +online tutorials on using the Windows command line, one example is found `here `_. Windows users may want to install diff --git a/docs/sphinx/source/wiki/Licensing.rst b/docs/sphinx/source/wiki/Licensing.rst index cab80cd016..2fa91b9575 100644 --- a/docs/sphinx/source/wiki/Licensing.rst +++ b/docs/sphinx/source/wiki/Licensing.rst @@ -4,7 +4,7 @@ Evennia Licence FAQ Evennia is licensed under the very friendly *Modified Clarified Artistic License*. You can find the license as ``LICENCE`` in the Evennia root directory. You can also read the full license file -`here `_. +`here `_. You should read the full license text to know what it says exactly, but here are some answers to common questions. diff --git a/docs/sphinx/source/wiki/Links.rst b/docs/sphinx/source/wiki/Links.rst index 31ece77df4..5b6e2eb7b5 100644 --- a/docs/sphinx/source/wiki/Links.rst +++ b/docs/sphinx/source/wiki/Links.rst @@ -56,6 +56,12 @@ General mud/game development ideas and discussions Realities `_ is an e-magazine on game/MUD design which were active 1998-2001. Interesting articles. +- `Mud-dev wiki `_ is a slowly growing + resource on MUD creation +- `Nick Gammon's hints + thread `_ + holds a very useful list of things to think about when starting your + new MUD. - `Lost Garden `_ is a game development blog with long and interesting articles (not MUD-specific) @@ -66,6 +72,8 @@ General mud/game development ideas and discussions discussion about rule systems and game balance that could be applicable also for MUDs. +x + Frameworks ---------- diff --git a/docs/sphinx/source/wiki/Locks.rst b/docs/sphinx/source/wiki/Locks.rst index 601b351817..86d2222e41 100644 --- a/docs/sphinx/source/wiki/Locks.rst +++ b/docs/sphinx/source/wiki/Locks.rst @@ -87,9 +87,8 @@ special *lock functions* available to the lock system. So, a lockstring consists of the type of restriction (the ``access_type``), a colon (``:``) and then a list of function calls that determine what is needed to pass the lock. Each function returns either -``True`` or ``False``. AND/OR and NOT works like normal Python to -combine several function checks. If the total result is True, the lock -is passed. +``True`` or ``False``. AND, OR and NOT work as they do normally in +Python. If the total result is True, the lock is passed. You can create several lock types one after the other by separating them with a semicolon (``;``) in the lockstring. The string below is diff --git a/docs/sphinx/source/wiki/RemovingColour.rst b/docs/sphinx/source/wiki/RemovingColour.rst index 2de4e4b212..6ee8e60c76 100644 --- a/docs/sphinx/source/wiki/RemovingColour.rst +++ b/docs/sphinx/source/wiki/RemovingColour.rst @@ -30,7 +30,7 @@ Create a new module in ``game/gamesrc/objects`` named, for example, ``mycharacter.py``. In your new module, create a new `typeclass `_ -inheriting from ``game.gamesrc.objects.baseobjecs.Character``. +inheriting from ``ev.Character``. :: @@ -44,7 +44,7 @@ inheriting from ``game.gamesrc.objects.baseobjecs.Character``. Above we set a simple config value as an `attribute `_. Let's make sure that new characters are created of this type. Edit your -``game/settings.py`` file and change ``BASE_CHARACTER_TYPECLASS`` to +``game/settings.py`` file and add/change ``BASE_CHARACTER_TYPECLASS`` to point to your new character class. Observe that this will only affect *new* characters, not those already created. You have to convert already created characters to the new typeclass by using the ``@typeclass`` @@ -67,101 +67,8 @@ Overload the \`msg()\` method ----------------------------- Next we need to overload the ``msg()`` method. What we want is to check -the configuration value before calling the main function. The original -``msg`` method is found on ``src.objects.models.ObjectDB`` and is called -like this: - -:: - - msg(message, from_obj=None, data=None) - -As long as we define a method on our custom object with the same name -and keep the same number of arguments/keywords we will overload the -original. Here's how it could look: - -:: - - from ev import ansi - - msg(self, message, from_obj=None, data=None): - "our custom msg()" - if not self.db.config_colour: - message = ansi.parse_ansi(message, strip_ansi=True) - self.dbobj.msg(message, from_obj, data) - -Above we create a custom version of the ``msg()`` method that cleans all -ansi characters if the config value is not set to True. Once that's -done, we pass it all on to the normal ``msg()`` on the database object -(``ObjectDB``) to do its thing. - -Since we put this custom ``msg()`` in our typeclass -``ColourableCharacter``, it will be searched for and called rather than -the default method on ``ObjectDB`` (which we now instead call manually). - -There we go! Just flip the attribute ``config_colour`` to False and your -users will not see any colour. As superuser (assuming you use the -Typeclass ``ColourableCharacter``) you can test this with the ``@py`` -command: - -:: - - @py self.db.config_colour = False - -Custom colour config command ----------------------------- - -For completeness, let's add a custom command so users can turn off their -colour display themselves if they want. - -In game/gamesrc/commands, create a new file, call it for example -``configcmds.py`` (it's likely that you'll want to add other commands -for configuration down the line). - -:: - - from ev import default_cmds - class ConfigColourCmd(default_cmds.MuxCommand): - """ - Configures your colour - - Usage: - @setcolour on|off - - This turns ansii-colours on/off. - Default is on. - """ - - key = "@setcolour" - aliases = ["@setcolor"] - - def func(self): - "Implements the command" - if not self.args or not self.args in ("on", "off"): - self.caller.msg("Usage: @setcolour on|off") - return - if self.args == "on": - self.caller.db.config_colour = True - else: - self.caller.db.config_colour = False - self.caller.msg("Colour was turned %s." % self.args) - -Lastly, we make this command available to the user by adding it to the -default command set. Easiest is to add it to copy the template file from -``gamesrc/commands/examples``, set ``settings.CMDSET_DEFAULT`` to point -to, and then add your module to the end of ``DefaultCmdSet`` in that new -module. - -:: - - from game.gamesrc.commands import configcmds - class DefaultCmdSet(cmdset_default.DefaultCmdSet): - - key = "DefaultMUX" - - def at_cmdset_creation(self): - super(DefaultCmdSet, self).at_cmdset_creation() - self.add(configcmds.ConfigColourCmd()) - -When adding a new command to a cmdset like this you need to run the -``@reload`` command (or reboot the server). From here on out, your users -should be able to turn on or off their colour as they please. +the configuration value before calling the main function. The ``msg`` +method call is found in +``src/objects/objects.py' and is called like this: {{{ msg(message, from_obj=None, data=None) }}} As long as we define a method on our custom object with the same name and keep the same number of arguments/keywords we will overload the original. Here's how it could look: {{{ from ev import ansi msg(self, message, from_obj=None, data=None): "our custom msg()" if not self.db.config_colour: # if config_colour is False, strip ansi colour message = ansi.parse_ansi(message, strip_ansi=True) self.dbobj.msg(message, from_obj, data) }}} Above we create a custom version of the ``\ msg()\ `` method that cleans all ansi characters if the config value is not set to True. Once that's done, we pass it all on to the normal ``\ msg()\ `` on the database object (``\ ObjectDB\ ``) to do its thing. The colour strip is done by the ansi module itself by giving the ``\ strip\_ansi\ `` keyword to ``\ ansi.parse\_ansi\ ``. Since we put this custom ``\ msg()\ `` in our typeclass ``\ ColourableCharacter\ ``, it will be searched for and called rather than the default method on ``\ ObjectDB\ `` (which we now instead call manually). There we go! Just flip the attribute ``\ config\_colour\ `` to False and your users will not see any colour. As superuser (assuming you use the Typeclass ``\ ColourableCharacter\ ``) you can test this with the ``\ @py\ `` command: {{{ @py self.db.config_colour = False }}} ==Custom colour config command == For completeness, let's add a custom command so users can turn off their colour display themselves if they want. In ``\ game/gamesrc/commands\ ``, reate a new file, call it for example ``\ configcmds.py\ `` (it's likely that you'll want to add other commands for configuration down the line). You can also copy/rename the command template from ``\ game/gamesrc/commands/examples\ ``. {{{ from ev import default_cmds class ConfigColourCmd(default_cmds.MuxCommand): """ Configures your colour Usage: @setcolour on|off This turns ansii-colours on/off. Default is on. """ key = "@setcolour" aliases = ["@setcolor"] def func(self): "Implements the command" if not self.args or not self.args in ("on", "off"): self.caller.msg("Usage: @setcolour on|off") return if self.args == "on": self.caller.db.config_colour = True else: self.caller.db.config_colour = False self.caller.msg("Colour was turned %s." % self.args) }}} Lastly, we make this command available to the user by adding it to the default command set. Easiest is to add it to copy the template file from ``\ gamesrc/commands/examples\ ``, set ``\ settings.CMDSET\_DEFAULT\ `` to point to, and then add your module to the end of ``\ DefaultCmdSet\ `` in that new module. {{{ from game.gamesrc.commands import configcmds class DefaultCmdSet(cmdset_default.DefaultCmdSet): key = "DefaultMUX" def at_cmdset_creation(self): super(DefaultCmdSet, self).at_cmdset_creation() self.add(configcmds.ConfigColourCmd()) }}} When adding a new command to a cmdset like this you need to run the ``\ @reload\ ```` +command (or reboot the server). From here on out, your users should be +able to turn on or off their colour as they please. diff --git a/docs/sphinx/source/wiki/Scripts.rst b/docs/sphinx/source/wiki/Scripts.rst index 46c3b65777..3235df3272 100644 --- a/docs/sphinx/source/wiki/Scripts.rst +++ b/docs/sphinx/source/wiki/Scripts.rst @@ -1,8 +1,24 @@ Scripts ======= -*Scripts* are the way to implement everything in Evennia that may change -with time. +*Scripts* are the out-of-character siblings to the in-character +`Objects `_. The name "Script" might suggest that they can +only be used to script the game but this is only part of their +usefulness (in the end we had to pick a single name for them). Scripts +are full Typeclassed database entities, just like Objects - with all the +advantages this entails. Likewise they can also store arbitrary +*Attributes*. + +Scripts can be used for many different things in Evennia: + +- They can attach to Objects to influence them in various ways - or + exist independently of any one in-game entity. +- They can work as timers and tickers - anything that may change with + Time. But they can also have no time dependence at all. +- They can describe State changes. +- They can act as data stores for storing game data persistently in the + database +- They can be used to shared data between groups of objects The most obvious use of Scripts may be to use them as *timers* or *Events*. Consider a script running on the object ``Grandfather Clock``. @@ -12,16 +28,17 @@ clock must be rewound so it won't stop. Scripts may act as changeable *States*. Consider for example creating a 'dark' room. It has two scripts assigned on it - one ``DarkState`` -script, and one ``BrightState`` script. When characters enters the dark -room, it assigns a custom `Cmdset `_ to them - this -command set (say) limits their actions because of the darkness. After -the characters have stumbled around for a while, someone brings up a -torch. As a light source is now in the room, ``DarkState`` reacts to -this by shutting down itself and handing over control to the -``BrightState`` script that restores normal commands. Finally, when the -character with the torch leaves the room, the ``BrightState`` script -detects this and obediently hands control back to the ``DarkState``, -leaving the remaining poor characters in darkness once again. +script, and one ``BrightState`` script. When characters enter the dark +room, it assigns a custom `Cmdset `_ to them. This +command set defines the parameters of the state they describe. In this +case it limits their actions because of the darkness. After the +characters have stumbled around for a while, someone brings up a torch. +As a light source is now in the room, ``DarkState`` reacts to this by +shutting down itself and handing over control to the ``BrightState`` +script that restores normal commands. Finally, when the character with +the torch leaves the room, the ``BrightState`` script detects this and +obediently hands control back to the ``DarkState``, leaving the +remaining poor characters in darkness once again. By combining state-changes with timers one can make a room look different during nighttime than it does during the day. Weather and @@ -29,9 +46,11 @@ seasons might come and go. But one can also achieve more complex things such as state-AI systems that make mobs move around and possibly pursue characters between rooms. -... In short, Scripts make the game world *tick*. Scripts are -database-persistent objects and are `TypeClassed `_ -entities, with all the advantages that this entails. +Scripts are also excellent places to store game data in an OOC way. A +groupd of objects may share date by use of a Script object they all hold +references to. + +In short, Scripts can be used for a lot of things. How to create your own Script types ----------------------------------- diff --git a/docs/sphinx/source/wiki/ServerConf.rst b/docs/sphinx/source/wiki/ServerConf.rst index 1db0694e95..259c7b38fd 100644 --- a/docs/sphinx/source/wiki/ServerConf.rst +++ b/docs/sphinx/source/wiki/ServerConf.rst @@ -26,7 +26,7 @@ In code, the settings is accessed through from django.conf import settings # or (shorter): from ev import settings - # + # example: servername = settings.SERVER_NAME Each setting appears as a property on the imported ``settings`` object. @@ -73,6 +73,16 @@ for Evennia to be able to locate it. by MUD search engines (which you often have to register with) in order to display what kind of game you are running along with statistics such as number of online players and online status. +- ``portal_services_plugin.py`` - this allows for adding your own + custom servies/protocols to the Portal. It must define one particular + function that will be called by Evennia at startup. There can be any + number of service plugin modules, all will be imported and used if + defined. More info can be found + `here `_. +- ``server_services_plugin.py`` - this is equivalent to the previous + one, but used for adding new services to the Server instead. More + info can be found + `here `_. Some other Evennia systems can be customized by plugin modules but has no explicit template in ``conf/examples``: diff --git a/docs/sphinx/source/wiki/SessionProtocols.rst b/docs/sphinx/source/wiki/SessionProtocols.rst index 1c689683fb..818e37c8d5 100644 --- a/docs/sphinx/source/wiki/SessionProtocols.rst +++ b/docs/sphinx/source/wiki/SessionProtocols.rst @@ -236,6 +236,72 @@ Loop over all relevant sessions. The Server will treat this like a Portal call and data will be sent back to be handled by the portal as normal. +Adding custom Protocols +======================= + +Evennia has a plugin-system that allows you to add new custom Protocols +without editing any files in ``src/``. To do this you need to add the +protocol as a new "service" to the application. + +Take a look at for example ``src/server/portal.py``, notably the +sections towards the end of that file. These are where the various +in-built services like telnet, ssh, webclient etc are added to the +Portal (there is an equivalent but shorter list in +``src/server.server.py``. + +To add a new service of your own (for example your own custom client +protocol) to e.g. the Portal, create a new module in +``game/gamesrc/conf/``. Let's call it ``myproc_plugin.py``. We need to +tell the Server or Portal that they need to import this module. In +``game/settings.py``, add one of the following: + +:: + + # add to the Server + SERVER_SERVICES_PLUGIN_MODULES.append('game.gamesrc.conf.myproc_plugin') + # or, if you want to add to the Portal + PORTAL_SERVICES_PLUGIN_MODULES.append('game.gamesrc.conf.myproc_plugin') + +This module can contain whatever you need to define your protocol, but +it *must* contain a function ``start_plugin_services(app)``. This is +called by the Portal as part of its upstart. The function +``start_plugin_services`` must contain all startup code the server need. +The ``app`` argument is a reference to the Portal application itself so +the custom service can be added to it. The function should not return +anything. + +This is how it can look: + +:: + + # game/gamesrc/conf/myproc_plugin.py + + # here the new Portal Twisted protocol is defined + class MyOwnFactory( ... ): + [...] + + # some configs + MYPROC_ENABLED = True # convenient off-flag to avoid having to edit settings all the time + MY_PORT = 6666 + + def start_plugin_services(portal): + "This is called by the Portal during startup" + if not MYPROC_ENABLED: + return + # output to list this with the other services at startup + print " myproc: %s" % MY_PORT + + # some setup (simple example) + factory = MyOwnFactory() + my_service = internet.TCPServer(MY_PORT, factory) + # all Evennia services must be uniquely named + my_service.setName("MyService") + # add to the main portal application + portal.services.addService(my_service) + +One the module is defined and targeted in settings, just reload the +server and your new protocol/services should start with the others. + Assorted notes ============== diff --git a/docs/sphinx/source/wiki/TutorialWorldIntroduction.rst b/docs/sphinx/source/wiki/TutorialWorldIntroduction.rst index c51acfd7a9..a614340c04 100644 --- a/docs/sphinx/source/wiki/TutorialWorldIntroduction.rst +++ b/docs/sphinx/source/wiki/TutorialWorldIntroduction.rst @@ -44,7 +44,8 @@ install, log into the server as the superuser (user #1) and run: @batchcommand contrib.tutorial_world.build -The world will be built (there will be a lot of text output) and you +The world will be built (this might take a while, so don't rerun the +command even if it seems the system has frozen). After finishing you will end up back in Limbo with a new exit called ``tutorial``. An alternative is @@ -73,7 +74,7 @@ together with her powerful magical weapon - a valuable prize, if it's true. Of course this is a chance to adventure that you cannot turn down!* -*You reach the coast in the midst of a raging thunderstorm. With wind +*You reach the ocean in the midst of a raging thunderstorm. With wind and rain screaming in your face you stand where the moor meets the sea along a high, rocky coast ...* diff --git a/docs/sphinx/source/wiki/Tutorials.rst b/docs/sphinx/source/wiki/Tutorials.rst index 2394f5e741..9a91a14c50 100644 --- a/docs/sphinx/source/wiki/Tutorials.rst +++ b/docs/sphinx/source/wiki/Tutorials.rst @@ -7,17 +7,28 @@ or tutorial-like format. Building -------- +More building details are found in the `Builder +Docs `_. + - `Tutorial: Building Quick-start `_ Coding basics ------------- +More details about coding with Evennia is found in the `Developer +Central `_. + - `Tutorial: Adding a new default command `_ Implementation ideas -------------------- +Before starting to code your own game we recommend you read the +`Planning Your own game `_ page for some ideas of +organizing your workflow. There is also plenty of more information in +the `Developer Central `_. + - `Tutorial: Removing Colour from your game (typeclass method overloading) `_ - `Tutorial: Adding a Command prompt `_ @@ -28,6 +39,9 @@ Implementation ideas Examples -------- +See also ``evennia/contrib/`` and the example directories under +``game/gamesrc/``. + - `The Tutorial World `_ diff --git a/docs/sphinx/source/wiki/Typeclasses.rst b/docs/sphinx/source/wiki/Typeclasses.rst index c041bd0314..b3679e3b59 100644 --- a/docs/sphinx/source/wiki/Typeclasses.rst +++ b/docs/sphinx/source/wiki/Typeclasses.rst @@ -23,15 +23,21 @@ inherited from etc just like normal Python classes. But whenever they store data they are infact transparently storing this data into the database. -All typeclassed entities share a few very useful properties and methods. -These are described in the next section. +It's easy to work with Typeclasses - just create a new class inheriting +from one of the base Typeclasses: -See the pages for `Players `_, `Objects `_ -and `Scripts `_ for more specific usage examples. +:: + + from ev import Object + + class Furniture(Object): + # this defines what 'furniture' is Properties available to all typeclassed entities (Players, Objects, Scripts) ---------------------------------------------------------------------------- +All typeclassed entities share a few very useful properties and methods. + - ``key`` - the main identifier for the entity, say 'Rose', 'myscript' or 'Paul'. ``name`` is an alias that can also be used. - ``date_created`` - time stamp when this object was created. @@ -66,201 +72,17 @@ add/overload custom methods and inherit your own classes from them - most things you'd expect to be able to do with a Python class. There are a few things that you need to remember however: -- Evennia will look for and call especially named *hook methods* on the - typeclass in different situations. Just define a new method on the - class named correctly and Evennia will call it appropriately. Hooks - are your main way to interact with the server. Available hook methods - are listed in the resepective base modules in ``game/gamesrc/``. -- Don't use the normal ``__init__()`` to set up your typeclass. It is - used by Evennia to set up the mechanics of the Typeclass system. Use - the designated hook method instead, such as ``at_object_creation()``, - ``at_player_creation()`` or ``at_script_creation()``. -- Don't re-implement the python special class methods - ``__setattr__()``, ``__getattribute__()`` and ``__delattr__()``. - These are used extensively by the Typeclass system. -- Some property names cannot be assigned to a Typeclassed entity due to - being used for internal Typeclass operations. If you try, you will - get an error. These property names are *id*, *dbobj*, *db*, *ndb*, - *objects*, *typeclass*, *attr*, *save* and *delete*. -- Even if they are not explicitly protected, you should not redefine - the "type" of the default typeclass properties listed above and on - each typeclassed entity (such as trying to store an integer in the - ``key`` property). These properties are often called by the engine - expecting a certain type of return, and some are even tied directly - to the database and will thus return errors if given a value of the - wrong type. -- *Advanced note*: If you are doing advanced coding you might (very - rarely) find that overloading ``__init__``, ``_setattr__`` etc allows - for some functionality not possible with hooks alone. You *can* do it - if you know what you are doing, but you *must* then remember to use - Python's built-in function ``super()`` to call the parent method too, - or you *will* crash the server! You have been warned. +- Create new instances of typeclasses using ``ev.create_*`` instead of + just initializing the typeclass. So use + ``ev.create_object(MyTypeclass, ...)`` to create a new object of the + type ``MyTypeclass``. Doing obj = + MyTypeclass()\ `` will not work. * Evennia will look for and call especially named _hook methods_ on the typeclass in different situations. Just define a new method on the class named correctly and Evennia will call it appropriately. Hooks are your main way to interact with the server. Available hook methods are listed in the resepective base modules in ``\ game/gamesrc/\ ``. * Don't use the normal ``\ \_\_init\_\_()\ `` to set up your typeclass. It is used by Evennia to set up the mechanics of the Typeclass system. Use the designated hook method instead, such as ``\ at\_object\_creation()\ ``, ``\ at\_player\_creation()\ `` or ``\ at\_script\_creation()\ ``. * Don't re-implement the python special class methods ``\ \_\_setattr\_\_()\ ``, ``\ \_\_getattribute\_\_()\ `` and ``\ \_\_delattr\_\_()\ ``. These are used extensively by the Typeclass system. * Some property names cannot be assigned to a Typeclassed entity due to being used for internal Typeclass operations. If you try, you will get an error. These property names are _id_, _dbobj_, _db_, _ndb_, _objects_, _typeclass_, _attr_, _save_ and _delete_. * Even if they are not explicitly protected, you should not redefine the "type" of the default typeclass properties listed above and on each typeclassed entity (such as trying to store an integer in the ``\ key\ `` property). These properties are often called by the engine expecting a certain type of return, and some are even tied directly to the database and will thus return errors if given a value of the wrong type. * _Advanced note_: If you are doing advanced coding you might (very rarely) find that overloading ``\ \_\_init\_\_\ ``, ``\ \_setattr\_\_\ `` etc allows for some functionality not possible with hooks alone. You _can_ do it if you know what you are doing, but you _must_ then remember to use Python's built-in function ``\ super()\ `` to call the parent method too, or you _will_ crash the server! You have been warned. = How typeclasses actually work = _This is considered an advanced section. Skip it on your first read-through unless you are really interested in what's going on under the hood._ All typeclassed entities actually consist of two (three) parts: # The _Typeclass_ (a normal Python class with customized get/set behaviour) # The _Database model_ (Django model) # ([Attributes]) The _Typeclass_ is an almost normal Python class, and holds all the flexibility of such a class. This is what makes the class special, some of which was already mentioned above: * It inherits from ``\ src.typeclasses.typeclass.TypeClass\ ``. * ``\ \_\_init\_\_()\ `` is reserved for various custom startup procedures. * It always initiates a property ``\ dbobj\ `` that points to a _Database model_. * It redefines python's normal ``\ \_\_getattribute\_\_()\ ``, ``\ \_\_setattr\_\_()\ `` and ``\ \_\_delattr\_\_\ `` on itself to relay all data on itself to/from ``\ dbobj\ `` (i.e. to/from the database model). The related _Database model_ in turn communicates data in and out of the the database. The Database model holds the following (typeclass-related) features: * It inherits from ``\ src.typeclasses.models.TypedObject\ `` (this actually implements a [http://github.com/dcramer/django-idmapper idmapper]-type model. If that doesn't mean anything to you, never mind). * It has a field ``\ typelclass\_path\ `` that gives the python path to the _Typeclass_ associated with this particular model instance. * It has a property _typeclass_ that dynamically imports and loads the _Typeclass_ from ``\ typeclass\_path\ ``, and assigns itself to the Typeclass' ``\ dbobj\ `` property. * It redefines ``\ \_\_getattribute\_\_()\ `` to search its typeclass too, while avoiding loops. This means you can search either object and find also data stored on the other. The _Attributes_ are not really part of the typeclass scheme, but are very important for saving data without having to change the database object itself. They are covered in a separate entry [Attributes here]. == Why split it like this? == The _Database model_ (Django model) allows for saving data to the database and is a great place for storing persistent data an object might need during and between sessions. But it is not suitable for representing all the various objects a game needs. You _don't_ want to have to redefine a new database representation just because a ``\ CarObject\ `` needs to look and behave differently than a ``\ ChairObject\ ``. So instead we keep the database model pretty "generic", and only put database Fields on it that we know that _all_ objects would need (or that require fast and regular database searches). Examples of such fields are "key" and "location". Enter the _Typeclass_. For lack of a better word, a typeclass "decorates" a Django database model. Through the re-definition of the class' get/set methods, the typeclass constantly communicates behind the scenes with the Django model. The beauty of it is that this is all hidden from you, the coder. As long as you don't overwrite the few magic methods listed above you can deal with the typeclass almost as you would any normal Python class. You can extend it, inherit from it, and so on, mostly without caring that it is infact hiding a full persistent database representation. So you can now create a typeclass-class _Flowers_ and then inherit a bunch of other typeclass-classes from that one, like _Rose_, _Tulip_, _Sunflower_. As your classes are instantiated they will each secretly carry a reference to a database model to which all data _actually_ goes. We, however, can treat the two as if they where one. Below is a schematic of the database/typeclass structure. http://d.imagehost.org/0784/typeclasses1.png Let's see how object creation looks like in an example. # We have defined a Typeclass called _Rose_ in ``\ game.gamesrc.objects.flower.Rose\ ``. It inherits from ``\ game.gamesrc.objects.baseobjects.Object\ ``, which is a grandchild of ``\ src.typeclasses.typeclass.TypeClass\ ``. So the rose a typeclassed object, just as it should be. # Using a command we create a new _Rose_ instance _!RedRose_ (e.g. with ``\ @create + redrose:flowers.Rose\ ``). # A new database model is created and given the key _!RedRose_. Since this is an [Objects Object] typeclass (rather than a Script or Player), the database model used is ``\ src.objects.models.ObjectDB\ ``, which inherits directly from ``\ src.typeclasses.models.TypedObject\ ``). # This new Django-model instance receives the python-path to the _Rose_ typeclass and stores it as a string on itself (in a database field ``\ typeclass\_path\ ``). When the server restarts in the future, the database model will restart from this point. # The database model next _imports_ the Typeclass from its stored path and creates a new instance of it in memory. It stores a reference to this instance of _Rose_ (_!RedRose_)in a property called ``\ typeclass\ ``. # As _Rose_ is instantiated, its ``\ \_\_init\_\_()\ `` method is called. What this does it to make sure to store the back-reference to the Django model on our new _Rose_ instance. This back-reference is called ``\ dbobj\ ``. # The creation method next runs the relevant startup hooks on the typeclass, such as ``\ at\_object\_creation()\ ``. Storing properties on the typeclass-instance will in fact transparently save to the database object. So ``\ RedRose.thorns + = True\ `` is the same as ``\ RedRose.dbobj.thorns = + True\ `` (this will in fact be saved in the database as an attribute "thorns"). Doing ``\ ouch + = + RedRose.thorns\ `` is however not really as symmetric. The system will in this case _first_ check the Typeclass instance and only if no property _thorns_ was found will go on to examine the database object. So ``\ ouch + = RedRose.thorns\ `` is not necessarily the same as ``\ ouch = + RedRose.dbobj.thorns\ `` in this case. The reason we don't assume everything to be on the database object is that you are likely to customize your _Rose_ typeclass with custom parameters and methods that are intended to _overload_ the default methods on the database object. These are thus searched and run first, and you can then safely use ``\ self.dbobj\ `` from the typeclass to call the original function if you want. An example of Typeclass overloading is found [CommandPrompt#Prompt_on_the_same_line here]. Another example: http://b.imagehost.org/0023/typeclasses2.png == Caveats of the typeclass system == While there are many advantages to the typeclass system over working with Django models directly, there are also some caveats to remember. Be careful when not using Evennia's search and create methods. Almost all code in evennia (including default commands) assume that what is returned from searches or creates are Typeclasses, not Django models (i.e. the first of the two in the pair). This is what you get if you use any of the model manager methods, and also the create/search functions in ``\ src.utils.create\ `` and ``\ src.utils.search\ ``. Old Django-gurus will find it tempting to use Django's in-build database query methods, such as ``\ ObjectDB.objects.filter()\ `` to get data. This works, but the result will then of course _not_ be a typeclass but a Django model object (a query). You can easily convert between them with ``\ dbobj.typeclass\ `` and ``\ typeclass.dbobj\ ``, but you should be aware of this distinction. {{{ obj = ObjectDB.objects.get_id(1) # custom evennia manager method. This returns the typeclass. obj = ObjectDB.objects.get(1) # standard Django. Returns a Django model object. }}} Even more important to know for Django affectionados: Evennia's custom methods return _lists_ where you with normal Django methods would expect ``\ Query\ `` objects (e.g. from the ``\ filter()\ `` method). As long as you don't confuse what result type you are dealing with (for example you cannot 'link' ``\ list\ ``s together the way you can ``\ Querysets\ ``), you should be fine. Read the ``\ manager.py\ `` files in each relevant folder under ``\ src/\ ```` + to see which database access methods are available. -How typeclasses actually work -============================= - -*This is considered an advanced section. Skip it on your first -read-through unless you are really interested in what's going on under -the hood.* - -All typeclassed entities actually consist of two (three) parts: - -#. The *Typeclass* (a normal Python class with customized get/set - behaviour) -#. The *Database model* (Django model) -#. (`Attributes `_) - -The *Typeclass* is an almost normal Python class, and holds all the -flexibility of such a class. This is what makes the class special, some -of which was already mentioned above: - -- It inherits from ``src.typeclasses.typeclass.TypeClass``. -- ``__init__()`` is reserved for various custom startup procedures. -- It always initiates a property ``dbobj`` that points to a *Database - model*. -- It redefines python's normal ``__getattribute__()``, - ``__setattr__()`` and ``__delattr__`` on itself to relay all data on - itself to/from ``dbobj`` (i.e. to/from the database model). - -The related *Database model* in turn communicates data in and out of the -the database. The Database model holds the following (typeclass-related) -features: - -- It inherits from ``src.typeclasses.models.TypedObject`` (this - actually implements a - `idmapper `_-type model. - If that doesn't mean anything to you, never mind). -- It has a field ``typelclass_path`` that gives the python path to the - *Typeclass* associated with this particular model instance. -- It has a property *typeclass* that dynamically imports and loads the - *Typeclass* from ``typeclass_path``, and assigns itself to the - Typeclass' ``dbobj`` property. -- It redefines ``__getattribute__()`` to search its typeclass too, - while avoiding loops. This means you can search either object and - find also data stored on the other. - -The *Attributes* are not really part of the typeclass scheme, but are -very important for saving data without having to change the database -object itself. They are covered in a separate entry -`here `_. - -Why split it like this? ------------------------ - -The *Database model* (Django model) allows for saving data to the -database and is a great place for storing persistent data an object -might need during and between sessions. But it is not suitable for -representing all the various objects a game needs. You *don't* want to -have to redefine a new database representation just because a -``CarObject`` needs to look and behave differently than a -``ChairObject``. So instead we keep the database model pretty "generic", -and only put database Fields on it that we know that *all* objects would -need (or that require fast and regular database searches). Examples of -such fields are "key" and "location". - -Enter the *Typeclass*. For lack of a better word, a typeclass -"decorates" a Django database model. Through the re-definition of the -class' get/set methods, the typeclass constantly communicates behind the -scenes with the Django model. The beauty of it is that this is all -hidden from you, the coder. As long as you don't overwrite the few magic -methods listed above you can deal with the typeclass almost as you would -any normal Python class. You can extend it, inherit from it, and so on, -mostly without caring that it is infact hiding a full persistent -database representation. So you can now create a typeclass-class -*Flowers* and then inherit a bunch of other typeclass-classes from that -one, like *Rose*, *Tulip*, *Sunflower*. As your classes are instantiated -they will each secretly carry a reference to a database model to which -all data *actually* goes. We, however, can treat the two as if they -where one. - -Below is a schematic of the database/typeclass structure. - -|image0| - -Let's see how object creation looks like in an example. - -#. We have defined a Typeclass called *Rose* in - ``game.gamesrc.objects.flower.Rose``. It inherits from - ``game.gamesrc.objects.baseobjects.Object``, which is a grandchild of - ``src.typeclasses.typeclass.TypeClass``. So the rose a typeclassed - object, just as it should be. -#. Using a command we create a new *Rose* instance *RedRose* (e.g. with - ``@create redrose:flowers.Rose``). -#. A new database model is created and given the key *RedRose*. Since - this is an `Object `_ typeclass (rather than a Script - or Player), the database model used is - ``src.objects.models.ObjectDB``, which inherits directly from - ``src.typeclasses.models.TypedObject``). -#. This new Django-model instance receives the python-path to the *Rose* - typeclass and stores it as a string on itself (in a database field - ``typeclass_path``). When the server restarts in the future, the - database model will restart from this point. -#. The database model next *imports* the Typeclass from its stored path - and creates a new instance of it in memory. It stores a reference to - this instance of *Rose* (*RedRose*)in a property called - ``typeclass``. -#. As *Rose* is instantiated, its ``__init__()`` method is called. What - this does it to make sure to store the back-reference to the Django - model on our new *Rose* instance. This back-reference is called - ``dbobj``. -#. The creation method next runs the relevant startup hooks on the - typeclass, such as ``at_object_creation()``. - -Storing properties on the typeclass-instance will in fact transparently -save to the database object. So ``RedRose.thorns = True`` is the same as -``RedRose.dbobj.thorns = True`` (this will in fact be saved in the -database as an attribute "thorns"). - -Doing ``ouch = RedRose.thorns`` is however not really as symmetric. The -system will in this case *first* check the Typeclass instance and only -if no property *thorns* was found will go on to examine the database -object. So ``ouch = RedRose.thorns`` is not necessarily the same as -``ouch = RedRose.dbobj.thorns`` in this case. The reason we don't assume -everything to be on the database object is that you are likely to -customize your *Rose* typeclass with custom parameters and methods that -are intended to *overload* the default methods on the database object. -These are thus searched and run first, and you can then safely use -``self.dbobj`` from the typeclass to call the original function if you -want. An example of Typeclass overloading is found -[`CommandPrompt `_\ #Prompt\_on\_the\_same\_line -here]. - -Another example: - -|image1| - -Caveats of the typeclass system -------------------------------- - -While there are many advantages to the typeclass system over working -with Django models directly, there are also some caveats to remember. - -Be careful when not using Evennia's search and create methods. Almost -all code in evennia (including default commands) assume that what is -returned from searches or creates are Typeclasses, not Django models -(i.e. the first of the two in the pair). This is what you get if you use -any of the model manager methods, and also the create/search functions -in ``src.utils.create`` and ``src.utils.search``. Old Django-gurus will -find it tempting to use Django's in-build database query methods, such -as ``ObjectDB.objects.filter()`` to get data. This works, but the result -will then of course *not* be a typeclass but a Django model object (a -query). You can easily convert between them with ``dbobj.typeclass`` and -``typeclass.dbobj``, but you should be aware of this distinction. - -:: - - obj = ObjectDB.objects.get_id(1) # custom evennia manager method. This returns the typeclass. - obj = ObjectDB.objects.get(1) # standard Django. Returns a Django model object. - -Even more important to know for Django affectionados: Evennia's custom -methods return *lists* where you with normal Django methods would expect -``Query`` objects (e.g. from the ``filter()`` method). As long as you -don't confuse what result type you are dealing with (for example you -cannot 'link' ``list``\ s together the way you can ``Querysets``), you -should be fine. - -Read the ``manager.py`` files in each relevant folder under ``src/`` to -see which database access methods are available. - -.. |image0| image:: http://d.imagehost.org/0784/typeclasses1.png -.. |image1| image:: http://b.imagehost.org/0023/typeclasses2.png diff --git a/docs/sphinx/source/wiki/VersionControl.rst b/docs/sphinx/source/wiki/VersionControl.rst index ba43f56a4f..6cab2c9921 100644 --- a/docs/sphinx/source/wiki/VersionControl.rst +++ b/docs/sphinx/source/wiki/VersionControl.rst @@ -162,7 +162,7 @@ Sharing your code with the world The most common case of this is when you have fixed an Evennia bug and want to make the fix available to Evennia maintainers. But you can also share your work with other people on your game-development team if you -don't worry about the changes being publicly visible. +aren't worried about the changes being publicly visible. Let's take the example of debugging Evennia. Go online and create an "online clone" of Evennia as described `here `_. Pull