Added Sphinx (reST-style) conversion of Evennia documentation to docs/. This is an auto-generated conversion directly from the Wiki, so it's not custom-written in any way (will also make it easy to update). You need Sphinx to compile the sources into fancy pages. Supporting sphinx is to make documentation easier to print and view offline. Currently no sphinx src-code viewing is activated by default, it gives too many spurious errors (the converters are in the repo though if you're interested in experimenting). So for offline autodocs, doxygen is still to recommend.
This commit is contained in:
parent
5a2b9e27a0
commit
bd0079a39d
65 changed files with 9394 additions and 143 deletions
372
docs/sphinx/source/wiki/EvenniaDevel.rst
Normal file
372
docs/sphinx/source/wiki/EvenniaDevel.rst
Normal file
|
|
@ -0,0 +1,372 @@
|
|||
Summary of changes in devel-branch
|
||||
|
||||
<wiki:toc max\_depth
|
||||
|
||||
"3" />
|
||||
|
||||
*Note: The devel branch merged with trunk as of r970. So if you are new
|
||||
to Evennia, this page is of no real interest to you.*
|
||||
|
||||
Introduction
|
||||
|
||||
The Evennia that has been growing in trunk for the last few years is a
|
||||
wonderful piece of software, with which you can do very nice coding
|
||||
work. It has however grown 'organically', adding features here and there
|
||||
by different coders at different times, and some features (such as my
|
||||
State system) were bolted onto an underlying structure for which it was
|
||||
never originally intended. Meanwhile Evennia is still in an alpha stage
|
||||
and not yet largely used. If one needs to do a cleanup/refactoring and
|
||||
homogenization of the code, now is the time time to do it. So I set out
|
||||
to do just that.
|
||||
|
||||
The "devel-branch" of Evennia is a clean rework of Evennia based on
|
||||
trunk. I should point out that the main goal has been to make system
|
||||
names consistent, to add all features in a fully integrated way, and to
|
||||
give all subsystems a more common API for the admin to work against.
|
||||
This means that in the choice between a cleaner implementation and
|
||||
backwards-compatability with trunk, the latter has had to stand back.
|
||||
However, you'll hopefully find that converting old codes shouldn't be
|
||||
too hard. Another goal is to further push Evennia as a full-fledged
|
||||
barebones system for *any* type of mud, not just MUX. So you'll find far
|
||||
more are now user-configurability now than ever before (MUX remains the
|
||||
default though).
|
||||
|
||||
Devel is now almost ready for merging with the main trunk, but it needs
|
||||
some more eyes to look at it first. If you are brave and want to help
|
||||
report bugs, you can get it from the *griatch* branch with
|
||||
|
||||
``svn checkout http://evennia.googlecode.com/svn/branches/griatch evennia-devel``
|
||||
|
||||
Concepts changed from trunk to devel=
|
||||
|
||||
Script parent -> Typeclasses
|
||||
----------------------------
|
||||
|
||||
The biggest change is probably that script parents have been replaced by
|
||||
*typeclasses*. Both handle the abstraction of in-game objects without
|
||||
having to create a separate database model for each (i.e. it allows
|
||||
objects to be anything from players to apples, rooms and swords all with
|
||||
the same django database model). A script parent in trunk was a class
|
||||
stored in a separate module together with a 'factory' function that the
|
||||
engine called. The admin had to always remember if they were calling a
|
||||
function on the database model or if it in fact sat on the script parent
|
||||
(the call was made through something called the "scriptlink").
|
||||
|
||||
By contrast, a typeclass is a normal python class that inherits from the
|
||||
*!TypeClass* parent. There are no other required functions to define.
|
||||
This class uses **getattribute** and **setattr** transparently behind
|
||||
the scenes to store data onto the persistent django object. Also the
|
||||
django model is aware of the typeclass in the reverse direction. The
|
||||
admin don't really have to worry about this connection, they can usually
|
||||
consider the two objects (typeclass and django model) to be one.
|
||||
|
||||
So if you have your 'apple' typeclass, accessing, say the 'location',
|
||||
which is stored as a persistent field on the django model, you can now
|
||||
just do ``loc = apple.location`` without caring where it is stored.
|
||||
|
||||
The main drawback with any typeclass/parent system is that it adds an
|
||||
overhead to all calls, and this overhead might be slightly larger with
|
||||
typeclasses than with trunk's script parents although I've not done any
|
||||
testing. You also need to use Evennia's supplied ``create`` methods to
|
||||
create the objects rather than to create objects with plain Django by
|
||||
instantiating the model class; this so that the rather complex
|
||||
relationships can be instantiated safely behind the scenes.
|
||||
|
||||
Command functions + !StateCommands-> Command classes + CmdSets
|
||||
--------------------------------------------------------------
|
||||
|
||||
In trunk, there was one default group of commands in a list
|
||||
GLOBAL*CMD*TABLE. Every player in game used this. There was a second
|
||||
dictionary GLOBAL*STATE*TABLE that held commands valid only for certain
|
||||
*states* the player might end up in - like entering a dark room, a text
|
||||
editor, or whatever. The problem with this state system, was that it was
|
||||
limited in its use - every player could ever only be in one state at a
|
||||
time for example, never two at the same time. The way the system was set
|
||||
up also explicitly made states something unique to players - an object
|
||||
could not offer different commands dependent on its state, for example.
|
||||
|
||||
In devel, *every* command definition is grouped in what's called a
|
||||
*!CmdSet* (this is, like most things in Devel, defined as a class). A
|
||||
command can exist in any number of cmdsets at the same time. Also the
|
||||
'default' group of commands belong to a cmdset. These command sets are
|
||||
no longer stored globally, but instead locally on each object capable of
|
||||
launching commands. You can add and new cmdsets to an object in a
|
||||
stack-like way. The cmdsets support set operations (Union, Difference
|
||||
etc) and will merge together into one cmdset with a unique set of
|
||||
commands. Removing a cmdset will re-calculate those available commands.
|
||||
This allows you to do things like the following (impossible in trunk): A
|
||||
player is walking down a corridor. The 'default' cmdset is in play. Now
|
||||
he meets an enemy. The 'combat' cmdset is merged onto (and maybe
|
||||
replacing part of) the default cmdset, giving him new combat-related
|
||||
commands only available during combat. The enemy hits him over the head,
|
||||
dazing him. The "Dazed" cmdset is now added on top of the previous ones
|
||||
- maybe he now can't use certain commands, or might even get a garbled
|
||||
message if trying to use 'look'. After a few moments the dazed state is
|
||||
over, and the 'Dazed' cmdset is removed, returning us to the combat mode
|
||||
we were in before. And so on.
|
||||
|
||||
Command definitions used to be functions, but are now classes. Instead
|
||||
of relying on input arguments, all relevant variables are stored
|
||||
directly on the command object at run-time. Also parsing and function
|
||||
execution have been split into two methods that are very suitable for
|
||||
subclassing (an example is all the commands in the default set which
|
||||
inherits from the MuxCommand class - that's the one knowing about MUX's
|
||||
special syntax with /switches, '=' and so on, Evennia's core don't deal
|
||||
with this at all!).
|
||||
|
||||
Example of new command definition:
|
||||
|
||||
::
|
||||
|
||||
class CmdTest(Command): def func(self): self.caller.msg("This is the test!")
|
||||
|
||||
Events + States -> Scripts
|
||||
--------------------------
|
||||
|
||||
The Event system of Evennia used to be a non-persistent affair; python
|
||||
objects that needed to be explicitly called from code when starting.
|
||||
States allowed for mapping different groups of commands to a certain
|
||||
situations (see CmdSets above for how commands are now always grouped).
|
||||
|
||||
*Scripts* (warning: Not to be confused with the old *script parents*!)
|
||||
are persistent database objects now and are only deleted on a server
|
||||
restart if explicitly marked as non-persistent.
|
||||
|
||||
A script can have a time-component, like Events used to have, but it can
|
||||
also work like an 'Action' or a 'State' since a script constantly checks
|
||||
if it is still 'valid' and if not will delete itself. A script handles
|
||||
everything that changes with time in Evennia. For example, all players
|
||||
have a script attached to them that assigns them the default cmdset when
|
||||
logging in.
|
||||
|
||||
Oh, and Scripts have typeclasses too, just like Objects, and carries all
|
||||
the same flexibility of the Typeclass system.
|
||||
|
||||
User + player -> User + Player + character
|
||||
------------------------------------------
|
||||
|
||||
In trunk there is no clear separation between the User (which is the
|
||||
django model representing the player connecting to the mud) and the
|
||||
player object. They are both forced to the same dbref and are
|
||||
essentially the same for most purposes. This has its advantages, but the
|
||||
problem is configurability for different game types - the in-game player
|
||||
object becomes the place to store also OOC info, and allowing a player
|
||||
to have many characters is a hassle (although doable, I have coded such
|
||||
a system for trunk privately). Devel-branch instead separate a "player
|
||||
character" into three tiers:
|
||||
|
||||
- The User (Django object)
|
||||
- The PlayerDB (User profile + Player typeclass)
|
||||
- The ObjectDB (+ Character typeclass)
|
||||
|
||||
User is not something we can get out of without changing Django; this is
|
||||
a permission/password sensitive object through which all Django users
|
||||
connect. It is not configurable to any great extent except through it's
|
||||
*profile*, a django feature that allows you to have a separate model
|
||||
that configures the User. We call this profile 'PlayerDB', and for
|
||||
almost all situations we deal with this rather than User. PlayerDB can
|
||||
hold attributes and is typeclassed just like Objects and Scripts
|
||||
(normally with a typeclass named simply *Player*) allowing very big
|
||||
configurability options (although you can probably get away with just
|
||||
the default setup and use attributes for all but the most exotic
|
||||
designs). The Player is an OOC entity, it is what chats on channels but
|
||||
is not visible in a room. The last stage is the in-game ObjectDB model,
|
||||
typeclassed with a class called 'Character' by default. This is the
|
||||
in-game object that the player controls.
|
||||
|
||||
The neat thing with this separation is that the Player object can easily
|
||||
switch its Character object if desired - the two are just linking to
|
||||
each other through attributes. This makes implementing multi-character
|
||||
game types much easier and less contrived than in the old system.
|
||||
|
||||
Help database -> command help + help database
|
||||
---------------------------------------------
|
||||
|
||||
Trunk stores all help entries in the database, including those created
|
||||
dynamically from the command's doc strings. This forced a system where
|
||||
the auto-help creation could be turned off so as to not overwrite later
|
||||
changes made by hand. There was also a mini-language that allowed for
|
||||
creating multiple help entries from the ``__doc__`` string.
|
||||
|
||||
Devel-branch is simpler in this regard. All commands are *always* using
|
||||
``__doc__`` on the fly at run time without hitting the database (this
|
||||
makes use of cmdsets to only show help for commands actually available
|
||||
to you). The help database is stand-alone and you can add entries to it
|
||||
as you like, the help command will look through both sources of help
|
||||
entries to match your query.
|
||||
|
||||
django-perms + locks -> permission/locks
|
||||
----------------------------------------
|
||||
|
||||
Trunk relies on Django's user-permissions. These are powerful but have
|
||||
the disadvantage of being 'app-centric' in a way that makes sense for a
|
||||
web app, not so much for a mud. The devel-branch thus implements a
|
||||
completely stand-alone permission system that incoorperate both
|
||||
permissions and locks into one go - the system uses a mini-language that
|
||||
has a permission string work as a keystring in one situation and as a
|
||||
complex lock (calling python lock functions you can define yourself) in
|
||||
another.
|
||||
|
||||
The permission system is working on a fundamental level, but the default
|
||||
setup probably needs some refinements still.
|
||||
|
||||
Mux-like comms -> Generic comms
|
||||
-------------------------------
|
||||
|
||||
The trunk comm system is decidedly MUX-like. This is fine, but the
|
||||
problem is that much of that mux-likeness is hard-coded in the engine.
|
||||
|
||||
Devel just defines three objects, Channel and Msg and an object to track
|
||||
connections between players and channels (this is needed to easily
|
||||
delete/break connections). How they interact with each other is up to
|
||||
the commands that use them, making the system completely configurable by
|
||||
the admin.
|
||||
|
||||
All ooc messages - to channels or to players or both at the same time,
|
||||
are sent through use of the Msg object. This means a full log of all
|
||||
communications become possible to keep. Other uses could be an e-mail
|
||||
like in/out box for every player. The default setup is still mux-like
|
||||
though.
|
||||
|
||||
Hard-coded parsing -> user customized parsing
|
||||
---------------------------------------------
|
||||
|
||||
Essentially all parts of parsing a command from the command line can be
|
||||
customized. The main parser can be replaced, as well as error messages
|
||||
for multiple-search matches. There is also a considerable difference in
|
||||
handling exits and channels - they are handled as commands with their
|
||||
separate cmdsets and searched with the same mechanisms as any command
|
||||
(almost any, anyway).
|
||||
|
||||
Aliases -> Nicks
|
||||
----------------
|
||||
|
||||
Aliases (that is, you choosing to for yourself rename something without
|
||||
actually changing the object itself) used to be a separate database
|
||||
table. It is now a dictionary 'nicks' on the Character object - that
|
||||
replace input commands, object names and channel names on the fly. And
|
||||
due to the separation between Player and Character, it means each
|
||||
character can have its own aliases (making this a suitable start for a
|
||||
recog system too, coincidentally).
|
||||
|
||||
Attributes -> properties
|
||||
------------------------
|
||||
|
||||
To store data persistently in trunk requires you to call the methods
|
||||
``get_attribute_value(attr)`` and ``set_attribute(attr, value)``. This
|
||||
is available for in-game Objects only (which is really the only data
|
||||
type that makes sense anyway in Trunk).
|
||||
|
||||
Devel allows attribute storage on both Objects, Scripts and Player
|
||||
objects. The attribute system works the same but now offers the option
|
||||
of using the ``db`` (for database) directly. So in devel you could now
|
||||
just do:
|
||||
|
||||
::
|
||||
|
||||
obj.db.attr = value value = obj.db.attr
|
||||
|
||||
And for storing something non-persistently (stored only until the server
|
||||
reboots) you can just do
|
||||
|
||||
::
|
||||
|
||||
obj.attr = value value = obj.attr
|
||||
|
||||
The last example may sound trivial, but it's actually impossible to do
|
||||
in trunk since django objects are not guaranteed to remain the same
|
||||
between calls (only stuff stored to the database is guaranteed to
|
||||
remain). Devel makes use of the third-party ``idmapper`` functionality
|
||||
to offer this functionality. This used to be a very confusing thing to
|
||||
new Evennia admins.
|
||||
|
||||
*All* database fields in Devel are now accessed through properties that
|
||||
handle in/out data storage. There is no need to save() explicitly
|
||||
anymore; indeed you should ideally not need to know the actual Field
|
||||
names.
|
||||
|
||||
Always full persistence -> Semi/Full persistence
|
||||
------------------------------------------------
|
||||
|
||||
In Evennia trunk, everything has to be saved back/from the database at
|
||||
all times, also if you just need a temporary storage that you'll use
|
||||
only once, one second from now. This enforced full persistency is a good
|
||||
thing for most cases - especially for web-integration, where you want
|
||||
the world to be consistent regardless of from where you are accessing
|
||||
it. Devel offer the ability to yourself decide this; since
|
||||
semi-persistent variables can be stored on objects (see previous
|
||||
section). What actually happens is that such variables are stored on a
|
||||
normal python object called ``ndb`` (non-database), which is
|
||||
transparently accessed. This does not touch the database at all.
|
||||
|
||||
Evennia-devel offers a setting ``FULL_PERSISTENCE`` that switches how
|
||||
the server operates. With this off, you have to explicitly assign
|
||||
attributes to database storage with e.g. ``obj.db.attr = value``,
|
||||
whereas normal assignment (``obj.attr = value``) will be stored
|
||||
non-persistent. With ``FULL_PERSISTENT`` on however, the roles are
|
||||
reversed. Doing ``obj.attr = value`` will now actually be saving to
|
||||
database, and you have to explicitly do ``obj.ndb.attr = value`` if you
|
||||
want non-persistence. In the end it's a matter of taste and of what kind
|
||||
of game/features you are implementing. Default is to use full
|
||||
persistence (but all of the engine explicitly put out ``db`` and ``ndb``
|
||||
making it work the same with both).
|
||||
|
||||
Commonly used functions/concept that changed names
|
||||
==================================================
|
||||
|
||||
There used to be that sending data to a player object used a method
|
||||
``emit_to()``, whereas sending data to a session used a method
|
||||
``msg()``. Both are now called ``msg()``. Since there are situations
|
||||
where it might be unclear if you receive a session or a player object
|
||||
(especially during login/logout), you can now use simply use ``msg()``
|
||||
without having to check (however, you *can* still use ``emit_to`` for
|
||||
legacy code, it's an alias to msg() now). Same is true with
|
||||
emit*to*contents() -> msg*to*contents().
|
||||
|
||||
``source_object`` in default commands are now consistently named
|
||||
*caller* instead.
|
||||
|
||||
``obj.get_attribute_value(attr)`` is now just
|
||||
``obj.get_attribute(attr)`` (but see the section on Attributes above,
|
||||
you should just use ``obj.db.attr`` to access your attribute).
|
||||
|
||||
How hard is it to convert from trunk to devel?
|
||||
==============================================
|
||||
|
||||
It depends. Any game logic game modules you have written (AI codes,
|
||||
whatever) should ideally not do much more than take input/output from
|
||||
evennia. These can usually be used straight off.
|
||||
|
||||
Commands and Script parents take more work but translate over quite
|
||||
cleanly since the idea is the same. For commands, you need to make the
|
||||
function into a class and add the parse(self) and func(self) methods
|
||||
(parse should be moved into a parent class so you don't have to use as
|
||||
much double code), as well as learn what variable names is made
|
||||
available (see the commands in ``gamesrc/commands/default`` for
|
||||
guidance). You can make States into CmdSets very easy - just listing the
|
||||
commands needed for the state in a new !CmdSet.
|
||||
|
||||
Script parents are made into Typeclasses by deleting the factory
|
||||
function and making them inherit from a TypeClassed object (such as
|
||||
Object or Player) like the ones in ``gamesrc/typeclasses/basetypes.py``,
|
||||
and then removing all code explicitly dealing with script parents.
|
||||
|
||||
Converting to the new Scripts (again, don't confuse with the old *script
|
||||
parents*!) is probably the trickiest, since they are a more powerful
|
||||
incarnation of what used to be two separate things; States and Events.
|
||||
See the examples in the ``gamesrc/scripts/`` for some ideas.
|
||||
|
||||
Better docs on all of this will be forthcoming.
|
||||
|
||||
Things not working/not implemented in devel (Aug 2010)
|
||||
======================================================
|
||||
|
||||
All features planned to go into Devel are finished. There are a few
|
||||
features available in Trunk that is not going to work in Devel until
|
||||
after it merges with Trunk:
|
||||
|
||||
- IMC2/IRC support is not implemented.
|
||||
- Attribute-level permissions are not formalized in the default cmdset.
|
||||
- Some of the more esoteric commands are not converted.
|
||||
|
||||
Please play with it and report bugs to our bug tracker!
|
||||
Loading…
Add table
Add a link
Reference in a new issue