Updating ReST docs.

This commit is contained in:
Griatch 2012-03-15 15:26:07 +01:00
parent f46a9a1280
commit 2eb5c4fc8c
39 changed files with 410 additions and 1203 deletions

View file

@ -12,6 +12,7 @@ Installation and Early Life
- `Starting, stopping, reloading and resetting - `Starting, stopping, reloading and resetting
Evennia <StartStopReload.html>`_ Evennia <StartStopReload.html>`_
- `Keeping your game up to date <UpdatingYourGame.html>`_ - `Keeping your game up to date <UpdatingYourGame.html>`_
- `Making your game available online <OnlineSetup.html>`_
Customizing the server Customizing the server
---------------------- ----------------------

View file

@ -113,8 +113,5 @@ are trouble.
:: ::
<Directory "/home/<yourname>/evennia/game/web"> <Directory "/home/<yourname>/evennia/game/web"> Options +ExecCGI Allow from all </Directory>
Options +ExecCGI
Allow from all
</Directory>

View file

@ -17,9 +17,7 @@ Consider this piece of code:
:: ::
print "before call ..." print "before call ..." long_running_function() print "after call ..."
long_running_function()
print "after call ..."
When run, this will print ``"before call ..."``, after which the When run, this will print ``"before call ..."``, after which the
``long_running_function`` gets to work for however long time. Only once ``long_running_function`` gets to work for however long time. Only once
@ -45,10 +43,7 @@ use of the ``run_async()`` function in ``src/utils/utils.py``.
:: ::
from src.utils import utils from src.utils import utils print "before call ..." utils.run_async(long_running_function) print "after call ..."
print "before call ..."
utils.run_async(long_running_function)
print "after call ..."
Now, when running this you will find that the program will not wait Now, when running this you will find that the program will not wait
around for ``long_running_function`` to finish. Infact you will see around for ``long_running_function`` to finish. Infact you will see
@ -79,8 +74,7 @@ called automatically.
:: ::
def at_return(r): def at_return(r): print r
print r
- ``at_err(e)`` (the *errback*) is called if the asynchronous function - ``at_err(e)`` (the *errback*) is called if the asynchronous function
fails and raises an exception. This exception is passed to the fails and raises an exception. This exception is passed to the
@ -99,19 +93,7 @@ An example of making an asynchronous call from inside a
:: ::
from src.utils import utils from src.utils import utils from game.gamesrc.commands.basecommand import Command class CmdAsync(Command): key = "asynccommand" def func(self): def long_running_function(): #[... lots of time-consuming code return final_value def at_return(r): self.caller.msg("The final value is %s" % r) def at_err(e): self.caller.msg("There was an error: %s" % e) # do the async call, setting all callbacks utils.run_async(long_running_function, at_return, at_err)
from game.gamesrc.commands.basecommand import Command
class CmdAsync(Command): key = "asynccommand" def func(self):
def long_running_function():
#[... lots of time-consuming code
return final_value
def at_return(r):
self.caller.msg("The final value is %s" % r) def at_err(e):
self.caller.msg("There was an error: %s" % e) # do the async call, setting all callbacks
utils.run_async(long_running_function, at_return, at_err)
That's it - from here on we can forget about ``long_running_function`` That's it - from here on we can forget about ``long_running_function``
and go on with what else need to be done. *Whenever* it finishes, the and go on with what else need to be done. *Whenever* it finishes, the
@ -121,12 +103,19 @@ for us to see. If not we will see an error message.
Assorted notes Assorted notes
-------------- --------------
Be careful with choosing when to use asynchronous calls. It is mainly Note that the ``run_async`` will try to launch a separate thread behind
useful for large administration operations that has no direct influence the scenes. Some databases, notably our default database SQLite3, does
on the game world (imports and backup operations come to mind). Since *not* allow concurrent read/writes. So if you do a lot of database
there is no telling exactly when an asynchronous call actually ends, access (like saving to an Attribute) in your function, your code might
using them for in-game commands is to potentially invite confusion and actually run *slower* using this functionality if you are not careful.
inconsistencies (and very hard-to-reproduce bugs). Extensive real-world testing is your friend here.
Overall, be careful with choosing when to use asynchronous calls. It is
mainly useful for large administration operations that has no direct
influence on the game world (imports and backup operations come to
mind). Since there is no telling exactly when an asynchronous call
actually ends, using them for in-game commands is to potentially invite
confusion and inconsistencies (and very hard-to-reproduce bugs).
The very first synchronous example above is not *really* correct in the The very first synchronous example above is not *really* correct in the
case of Twisted, which is inherently an asynchronous server. Notably you case of Twisted, which is inherently an asynchronous server. Notably you

View file

@ -29,9 +29,7 @@ To save persistent data on a Typeclassed object you normally use the
:: ::
# saving # saving rose.db.has_thorns = True # getting it back is_ouch = rose.db.has_thorns
rose.db.has_thorns = True # getting it back
is_ouch = rose.db.has_thorns
This looks like any normal Python assignment, but that ``db`` makes sure This looks like any normal Python assignment, but that ``db`` makes sure
that an *Attribute* is created behind the scenes and is stored in the that an *Attribute* is created behind the scenes and is stored in the
@ -44,9 +42,7 @@ way:
:: ::
# saving # saving rose.ndb.has_thorns = True # getting it back is_ouch = rose.ndb.has_thorns
rose.ndb.has_thorns = True # getting it back
is_ouch = rose.ndb.has_thorns
Strictly speaking, ``ndb`` has nothing to do with ``Attributes``, Strictly speaking, ``ndb`` has nothing to do with ``Attributes``,
despite how similar they look. No ``Attribute`` object is created behind despite how similar they look. No ``Attribute`` object is created behind
@ -68,9 +64,7 @@ Attributes like you would any normal Python property:
:: ::
# saving # saving rose.has_thorns = True# getting it back is_ouch = rose.has_thorns
rose.has_thorns = True# getting it back
is_ouch = rose.has_thorns
This looks like any normal Python assignment, but calls ``db`` behind This looks like any normal Python assignment, but calls ``db`` behind
the scenes for you. the scenes for you.
@ -85,6 +79,13 @@ uses ``msg()`` a lot to send text to you). Using
the safer bet. And it also makes it visually clear at all times when you the safer bet. And it also makes it visually clear at all times when you
are saving to the database and not. are saving to the database and not.
Another drawback of this shorter form is that it will handle a non-found
Attribute as it would any non-found property on the object. The ``db``
operator will instead return ``None`` if no matching Attribute is found.
So if an object has no attribute (or property) named ``test``, doing
``obj.test`` will raise an ``AttributeException`` error, whereas
``obj.db.test`` will return ``None``.
Persistent vs non-persistent Persistent vs non-persistent
---------------------------- ----------------------------
@ -151,96 +152,33 @@ Examples of valid attribute data:
:: ::
# a single value # a single value obj.db.test1 = 23 obj.db.test1 = False # a database object (will be stored as dbref) obj.db.test2 = myobj # a list of objects obj.db.test3 = [obj1, 45, obj2, 67] # a dictionary obj.db.test4 = 'str':34, 'dex':56, 'agi':22, 'int':77 # a mixed dictionary/list obj.db.test5 = 'members': [obj1,obj2,obj3], 'enemies':[obj4,obj5] # a tuple with a list in it obj.db.test6 = (1,3,4,8, ["test", "test2"], 9) # a set will still be stored and returned as a list [1,2,3,4,5]! obj.db.test7 = set([1,2,3,4,5])
obj.db.test1 = 23
obj.db.test1 = False
# a database object (will be stored as dbref)
obj.db.test2 = myobj
# a list of objects
obj.db.test3 = [obj1, 45, obj2, 67]
# a dictionary
obj.db.test4 = 'str':34, 'dex':56, 'agi':22, 'int':77
# a mixed dictionary/list
obj.db.test5 = 'members': [obj1,obj2,obj3], 'enemies':[obj4,obj5]
# a tuple with a list in it
obj.db.test6 = (1,3,4,8, ["test", "test2"], 9)
# a set will still be stored and returned as a list [1,2,3,4,5]!
obj.db.test7 = set([1,2,3,4,5])
Example of non-supported save: Example of non-supported save:
:: ::
# this will fool the dbobj-check since myobj (a database object) is "hidden" # this will fool the dbobj-check since myobj (a database object) is "hidden" # inside a custom object. This is unsupported and will lead to unexpected # results! class BadStorage(object): pass bad = BadStorage() bad.dbobj = myobj obj.db.test8 = bad # this will likely lead to a traceback
# inside a custom object. This is unsupported and will lead to unexpected
# results!
class BadStorage(object):
pass
bad = BadStorage()
bad.dbobj = myobj
obj.db.test8 = bad # this will likely lead to a traceback
Storing nested data directly on the variable Retrieving Mutable objects
-------------------------------------------- --------------------------
Evennia needs to do a lot of work behind the scenes in order to save and A side effect of the way Evennia stores Attributes is that Python Lists
retrieve data from the database. Most of the time, things work just like and Dictionaries (only )are handled by custom objects called PackedLists
normal Python, but there is one further exception except the one about and !PackedDicts. These have the special property that they save to the
storing database objects above. It is related to updating already database whenever new data gets assigned to them. This allows you to do
existing attributes in-place. Normally this works just as it should. For things like self.db.mylist`4 <4.html>`_
example, you can do
:: val without having to extract the mylist Attribute into a temporary
variable first.
# saving data There is however an important thing to remember. If you retrieve this
obj.db.mydict = "key":"test0" data into another variable, e.g. ``mylist2 = obj.db.mylist``, your new
obj.db.mydict["key"] = "test1" variable will *still* be a PackedList, and if you assign things to it,
obj.db.mylist = [0,1,2,3] it will save to the database! To "disconnect" it from the database
obj.db.mylist[3] = "test2" system, you need to convert it to a normal list with mylist2
obj.db.mylist.append("test3")
# retrieving data
obj.db.mydict["key"] # returns "test1"
obj.db.mylist[3] # returns "test2
obj.db.mylist[-1] # returns "test3"
and it will work fine, thanks to a lot of magic happening behind the list(mylist2).
scenes. What will *not* work however is *assigning nested
lists/dictionaries in-place*. This is due to the way Python referencing
works, no way around it alas. Consider the following:
::
obj.db.mydict = 1:2:3
This is a perfectly valid nested dictionary and Evennia will store it
just fine. Retrieving this data will also work normally:
::
val = obj.db.mydict[1][2] # correctly returns 3
However:
::
obj.db.mydict[1][2] = "test" # silently fails!
val = obj.db.mydict[1][2] # still returns 3
will not work - trying to edit the nested structure will fail silently
and nothing will have changed. No, this is not consistent with normal
Python operation, it's where the database magic fails. Sorry, but there
does not seem to be a way around this (if you know one, let us know!)
All is not lost however. In order to change a nested structure, you
simply need to use a temporary variable to update:
::
temp = obj.db.mydict
temp[1][2] = "test"
obj.db.mydict = temp
val = obj.db.mydict[1][2] # now correctly returns "test"
This is cumbersome, but always works as expected.
Notes Notes
----- -----

View file

@ -35,21 +35,23 @@ The batch file
-------------- --------------
A batch-code file is mostly a normal Python source file. The only thing A batch-code file is mostly a normal Python source file. The only thing
separating a batch file from any Python module is that the code are separating a batch file from any standard Python module is that the code
wrapped into *blocks* using a special syntax. These blocks allow the is wrapped into *blocks* using a special syntax. These blocks allow the
batch processor more control over execution, especially when using the batch processor more control over execution, especially when using the
processor's *interactive* mode, where they allow the default processor's *interactive* mode. In interactive mode these blocs allow
``@batchcommand`` to pause and only execute certain blocks at a time. the batchcode runner to pause and only execute certain blocks at a time.
There is however nothing stopping you from coding everything in one
single block if you don't want to split things up into chunks like this.
Here are the rules of syntax of the batch-command ``*.py`` file. Here are the rules of syntax of the batch-command ``*.py`` file.
- ``#HEADER`` as the first on a line marks the start of a *header* - ``#HEADER`` as the first on a line marks the start of a *header*
block. This is intended to hold imports and variables that might be block. This is intended to hold imports and variables that might be
of use for for other blocks. All python code defined in a header of use for other blocks. All python code defined in a header block
block will always be inserted at the top of all ``#CODE`` blocks in will always be inserted at the top of all ``#CODE`` blocks in the
the file. You may have more than one ``#HEADER`` block, but that is file. You may have more than one ``#HEADER`` block, but that is
equivalent to having just one big one. Comments in ``#HEADER`` blocks equivalent to having one big one. Comments in ``#HEADER`` blocks are
are stripped out before merging. stripped out before merging.
- ``#CODE`` as the first on a line marks the start of a *code* block. - ``#CODE`` as the first on a line marks the start of a *code* block.
Code blocks contain functional python code. ``#HEADER`` blocks are Code blocks contain functional python code. ``#HEADER`` blocks are
added to the top of code blocks at runtime. added to the top of code blocks at runtime.
@ -57,7 +59,7 @@ Here are the rules of syntax of the batch-command ``*.py`` file.
block header. The ``(info)`` field gives extra info about what's block header. The ``(info)`` field gives extra info about what's
going on in the block and is displayed by the batch processor. The going on in the block and is displayed by the batch processor. The
``obj1, obj2, ...`` parts are optional object labels used by the ``obj1, obj2, ...`` parts are optional object labels used by the
processors *debug* mode in order to auto-delete objects after a test processor's *debug* mode in order to auto-delete objects after a test
run. run.
- A new ``#HEADER`` or ``#CODE`` (or the end of the file) ends the - A new ``#HEADER`` or ``#CODE`` (or the end of the file) ends the
previous block. Text before the first block are ignored. previous block. Text before the first block are ignored.
@ -75,13 +77,7 @@ Below is a version of the example file found in
# #
# This is an example batch-code build file for Evennia. # This is an example batch-code build file for Evennia.
##HEADER# This will be included in all other #CODE blocksfrom src.utils import create, search ##HEADER# This will be included in all other #CODE blocksfrom src.utils import create, search from game.gamesrc.objects.examples import red_button from game.gamesrc.objects import baseobjectslimbo = search.objects(caller, 'Limbo', global_search=True)[0]#CODE (create red button)red_button = create.create_object(red_button.RedButton, key="Red button", location=limbo, aliases=["button"])# caller points to the one running the script caller.msg("A red button was created.")#CODE (create table and chair) table, chairtable = create.create_object(baseobjects.Object, key="Blue Table", location=limbo) chair = create.create_object(baseobjects.Object, key="Blue Chair", location=limbo)string = "A %s and %s were created. If debug was active, they were deleted again." caller.msg(string % (table, chair))
from game.gamesrc.objects.examples import red_button
from game.gamesrc.objects import baseobjectslimbo = search.objects(caller, 'Limbo', global_search=True)[0]#CODE (create red button)red_button = create.create_object(red_button.RedButton, key="Red button",
location=limbo, aliases=["button"])# caller points to the one running the script
caller.msg("A red button was created.")#CODE (create table and chair) table, chairtable = create.create_object(baseobjects.Object, key="Blue Table", location=limbo)
chair = create.create_object(baseobjects.Object, key="Blue Chair", location=limbo)string = "A %s and %s were created. If debug was active, they were deleted again."
caller.msg(string % (table, chair))
This uses Evennia's Python API to create three objects in sequence. This uses Evennia's Python API to create three objects in sequence.
@ -138,16 +134,12 @@ This shows that you are on the first ``#CODE`` block, the first of only
two commands in this batch file. Observe that the block has *not* two commands in this batch file. Observe that the block has *not*
actually been executed at this point! actually been executed at this point!
To take a look at the full command you are about to run, use ``ll`` (a To take a look at the full code snippet you are about to run, use ``ll``
batch-processor version of ``look``). (a batch-processor version of ``look``).
:: ::
from src.utils import create, search from src.utils import create, search from game.gamesrc.objects.examples import red_button from game.gamesrc.objects import baseobjectslimbo = search.objects(caller, 'Limbo', global_search=True)[0]red_button = create.create_object(red_button.RedButton, key="Red button", location=limbo, aliases=["button"])# caller points to the one running the script caller.msg("A red button was created.")
from game.gamesrc.objects.examples import red_button
from game.gamesrc.objects import baseobjectslimbo = search.objects(caller, 'Limbo', global_search=True)[0]red_button = create.create_object(red_button.RedButton, key="Red button",
location=limbo, aliases=["button"])# caller points to the one running the script
caller.msg("A red button was created.")
Compare with the example code given earlier. Notice how the content of Compare with the example code given earlier. Notice how the content of
``#HEADER`` has been pasted at the top of the ``#CODE`` block. Use ``#HEADER`` has been pasted at the top of the ``#CODE`` block. Use

View file

@ -26,13 +26,13 @@ relative to a folder you define to hold your batch files, set with
> @batchcommand examples.batch_cmds > @batchcommand examples.batch_cmds
A batch-command file contains a list of Evennia commands that you have A batch-command file contains a list of Evennia in-game commands
previously entered. The processor will run the batch file from beginning separated by comments. The processor will run the batch file from
to end. Note that *it will not stop if commands in it fail* (there is no beginning to end. Note that *it will not stop if commands in it fail*
universal way for the processor to know what a failure looks like for (there is no universal way for the processor to know what a failure
all different commands). So keep a close watch on the output, or use looks like for all different commands). So keep a close watch on the
*Interactive mode* (see below) to run the file in a more controlled, output, or use *Interactive mode* (see below) to run the file in a more
gradual manner. controlled, gradual manner.
The batch file The batch file
-------------- --------------
@ -41,40 +41,31 @@ The batch file is a simple plain-text file containing Evennia commands.
Just like you would write them in-game, except you have more freedom Just like you would write them in-game, except you have more freedom
with line breaks. with line breaks.
Here's the rules of syntax of an ``*.ev`` file. You'll find it's really, Here are the rules of syntax of an ``*.ev`` file. You'll find it's
really simple: really, really simple:
- All lines having the # (hash)-symbol *as the first one on the line* - All lines having the ``#`` (hash)-symbol *as the first one on the
are considered *comments*. line* are considered *comments*. All non-comment lines are treated as
- Comments also have an actual function -- they mark the *end of the a command and/or their arguments.
- Comment lines have an actual function -- they mark the *end of the
previous command definition*. So never put two commands directly previous command definition*. So never put two commands directly
after one another in the file - separate them with a comment, or the after one another in the file - separate them with a comment, or the
second of the two will be considered an argument to the first one second of the two will be considered an argument to the first one.
(regardless, using plenty of comments is a good practice anyway). Besides, using plenty of comments is good practice anyway.
- Extra whitespace in a command definition are ignored. If you want a - Extra whitespace in a command definition is *ignored*.
line break in texts, leave an empty line. Two empty lines thus means - A completely empty line translates in to a line break in texts. Two
a new paragraph (for commands accepting formatting, that is). empty lines thus means a new paragraph (this is obviously only
relevant for commands accepting such formatting, such as the
``@desc`` command).
- The very last command in the file is not required to end with a
comment.
Below is a version of the example file found in Below is a version of the example file found in
``game/gamesrc/commands/examples/batch_cmds.ev``. ``game/gamesrc/commands/examples/batch_cmds.ev``.
:: ::
# # # This is an example batch build file for Evennia. ## This creates a red button @create button:examples.red_button.RedButton # (This comment ends input for @create) # Next command. Let's create something. @set button/desc = This is a large red button. Now and then it flashes in an evil, yet strangely tantalizing way. A big sign sits next to it. It says:----------- Press me! ----------- ... It really begs to be pressed! You know you want to! # (This ends the @set command). Note that single line breaks # and extra whitespace in the argument are ignored. Empty lines # translate into line breaks in the output. # Now let's place the button where it belongs (let's say limbo #2 is # the evil lair in our example) @teleport #2 # (This comments ends the @teleport command.) # Now we drop it so others can see it. # The very last command in the file needs not be ended with #. drop button
# This is an example batch build file for Evennia.
## This creates a red button button@create button:examples.red_button.RedButton# (This comment ends input for @create)
# Next command. Let's create something. @set button/desc =
This is a large red button. Now and then
it flashes in an evil, yet strangely tantalizing way. A big sign sits next to it. It says:----------- Press me! ----------- ... It really begs to be pressed! You
know you want to!
# (This ends the @set command). Note that single line breaks
# and extra whitespace in the argument are ignored. Empty lines
# translate into line breaks in the output.
# Now let's place the button where it belongs (let's say limbo #2 is
# the evil lair in our example)@teleport #2# (This comments ends the @teleport command.)
# Now we drop it so others can see it.
# The very last command in the file needs not be ended with #.drop button
To test this, run ``@batchcommand`` on the file. A button will be To test this, run ``@batchcommand`` on the file. A button will be
created, described and dropped in Limbo. All commands will be executed created, described and dropped in Limbo. All commands will be executed
@ -174,3 +165,9 @@ to allow others than superusers to call the processor. Since normal
access-checks are still performed, a malevolent builder with access to access-checks are still performed, a malevolent builder with access to
the processor should not be able to do all that much damage (this is the the processor should not be able to do all that much damage (this is the
main drawback of the `batch-code processor <BatchCodeProcessor.html>`_) main drawback of the `batch-code processor <BatchCodeProcessor.html>`_)
`GNU Emacs <http://en.wikipedia.org/wiki/Emacs>`_ users might find it
interesting to use emacs' *evennia mode*. This is an Emacs major mode
found in ``src/utils/evennia-mode.el``. It offers correct syntax
highlighting and indentation with ``<tab>`` when editing ``.ev`` files
in Emacs. See the header of that file for installation instructions.

View file

@ -124,8 +124,7 @@ and try to get the box now:
:: ::
> get box > get box You can't get that.
You can't get that.
Think the default error message looks dull? The ``get`` command looks Think the default error message looks dull? The ``get`` command looks
for an `Attribute <Attributes.html>`_ named ``get_err_msg`` for for an `Attribute <Attributes.html>`_ named ``get_err_msg`` for

View file

@ -9,32 +9,29 @@ about supported
Databases <http://docs.djangoproject.com/en/dev/ref/databases/#ref-databases>`_ Databases <http://docs.djangoproject.com/en/dev/ref/databases/#ref-databases>`_
page. page.
SQLite SQLite3
------ -------
This is the default database used, and for the vast majority of Evennia This is the default database used, and for the vast majority of Evennia
installs it will probably be more than adequate for a long time. No installs it will probably be more than adequate or even the best choice.
server process is needed, the administrative overhead is tiny (as is No server process is needed, the administrative overhead is tiny (as is
resource consumption). The database will appear as a simple file resource consumption). The database will appear as a simple file
(``game/evennia.db3``). SQLite is excellent for development and easy (``game/evennia.db3``) and since we run SQLite as an in-memory process
testing. The database is however hampered in speed by not allowing without any socket overhead, it might well be faster than Postgres/MySQL
concurrent reads. For a full production game with many users accessing unless your database is huge.
the database, a more fully featured database engine (MySQL, Postgres
etc) is probably better.
**Note:** If you run Windows and for some reason need to use a **Note:** If you for some reason need to use a third-party web server
third-party web server like Apache rather than Evennia's internal web like Apache rather than Evennia's internal web server, SQLite is
server, sqlite is probably also not be the best choice. This is due to probably not be the best choice. This is due to the possibility of
the possibility of clashes with file-locking of the database file under clashes with file-locking when using SQLite from more than one process.
Windows.
Postgres Postgres
-------- --------
This is Django's recommended database engine, usable for all sites This is Django's recommended database engine, While not as fast as
aspiring to grow to a larger size. While not as fast as SQLite for SQLite for normal usage, it will scale better than SQLite, especially if
simple purposes, it will scale infinitely better than SQLite, especially your game has an very large database and/or extensive web presence
if your game has an extensive web presence. through a separate server process.
**Warning:** Postgres has issues with Evennia on some installs at the **Warning:** Postgres has issues with Evennia on some installs at the
moment. "http://code.google.com/p/evennia/issues/detail?id moment. "http://code.google.com/p/evennia/issues/detail?id
@ -44,8 +41,8 @@ moment. "http://code.google.com/p/evennia/issues/detail?id
MySQL MySQL
----- -----
MySQL **may** be slightly faster than Postgres depending on your setup MySQL *may* be slightly faster than Postgres depending on your setup and
and software versions involved. Older versions of MySQL had some software versions involved. Older versions of MySQL had some
peculiarities though, so check out Django's `Notes about supported peculiarities though, so check out Django's `Notes about supported
Databases <http://docs.djangoproject.com/en/dev/ref/databases/#ref-databases>`_ Databases <http://docs.djangoproject.com/en/dev/ref/databases/#ref-databases>`_
to make sure you use the correct version. to make sure you use the correct version.

View file

@ -26,8 +26,7 @@ mark colour:
:: ::
This is a %crRed text%cn This is normal text again. This is a %crRed text%cn This is normal text again. %cRThis text has red background%cn this is normal text.
%cRThis text has red background%cn this is normal text.
``%c#`` - markup works like a switch that is on until you actively turn ``%c#`` - markup works like a switch that is on until you actively turn
it off with ``%cn`` (this returns the text to your default setting). it off with ``%cn`` (this returns the text to your default setting).

View file

@ -15,9 +15,7 @@ of the look command, followed by the prompt. As an example:
:: ::
> look > look You see nothing special. HP:10, SP:20, MP: 5
You see nothing special.
HP:10, SP:20, MP: 5
MUD clients can be set to detect prompts like this and display them in MUD clients can be set to detect prompts like this and display them in
various client-specific ways. various client-specific ways.
@ -33,12 +31,7 @@ administration for example).
:: ::
class MyCommand(Command): [...] def at_post_cmd(self): class MyCommand(Command): [...] def at_post_cmd(self): # we assume health/stamina/magic are just stored # as simple attributes on the character. hp = self.caller.db.hp sp = self.caller.db.sp mp = self.caller.db.mp self.caller.msg("HP: %i, SP: %i, MP: %i" % (hp, sp, mp))
# we assume health/stamina/magic are just stored
# as simple attributes on the character. hp = self.caller.db.hp
sp = self.caller.db.sp
mp = self.caller.db.mp self.caller.msg("HP: %i, SP: %i, MP: %i" % (hp, sp, mp))
Prompt on the same line Prompt on the same line
----------------------- -----------------------
@ -48,8 +41,7 @@ return of every command, on the same line:
:: ::
> look > look HP: 10, SP:20, MP:5 -- You see nothing special.
HP: 10, SP:20, MP:5 -- You see nothing special.
Now, there is an ``at_pre_cmd()`` hook analogous to the hook from last Now, there is an ``at_pre_cmd()`` hook analogous to the hook from last
section except called just *before* parsing of the command. But putting section except called just *before* parsing of the command. But putting
@ -58,9 +50,7 @@ before* the function return:
:: ::
> look > look HP:10, SP:20, MP: 5 You see nothing special.
HP:10, SP:20, MP: 5
You see nothing special.
... which might be cool too, but not what we wanted. To have the prompt ... which might be cool too, but not what we wanted. To have the prompt
appear on the same line as the return this, we need to change how appear on the same line as the return this, we need to change how
@ -73,8 +63,7 @@ player. This is defined in ``src/objects/models.py``, on the
:: ::
def msg(self, outgoing_message, from_obj=None, data=None): def msg(self, outgoing_message, from_obj=None, data=None): ...
...
The only argument we are interested in here is the ``outgoing_message``, The only argument we are interested in here is the ``outgoing_message``,
which contains the text that is about to be passed on to the player. We which contains the text that is about to be passed on to the player. We
@ -85,13 +74,7 @@ custom Character typeclass add this:
:: ::
def msg(self, outgoing_message, from_obj=None, data=None): def msg(self, outgoing_message, from_obj=None, data=None): # prepend the prompt in front of the message hp = self.db.hp sp = self.db.sp mp = self.db.mp prompt = "%i, %i, %i -- " % (hp, sp, mp) outgoing_message = prompt + outgoing_message # pass this on to the original msg() method on the database object self.dbobj.msg(outgoing_message, from_obj=from_obj, data=data)
# prepend the prompt in front of the message hp = self.db.hp
sp = self.db.sp
mp = self.db.mp
prompt = "%i, %i, %i -- " % (hp, sp, mp)
outgoing_message = prompt + outgoing_message # pass this on to the original msg() method on the database object self.dbobj.msg(outgoing_message, from_obj=from_obj, data=data)
Note that this solution will *always* give you the prompt, also if you Note that this solution will *always* give you the prompt, also if you
use admin commands, which could get annoying. You might want to have use admin commands, which could get annoying. You might want to have

View file

@ -111,24 +111,46 @@ Defining your own command classes
Beyond the properties Evennia always assigns to the command at runtime Beyond the properties Evennia always assigns to the command at runtime
(listed above), your job is to define the following class properties: (listed above), your job is to define the following class properties:
- ``key`` - the identifier for the command, like ``look``. This should - ``key`` (string) - the identifier for the command, like ``look``.
(ideally) be unique. it can be more than one word long in a string, This should (ideally) be unique. it can be more than one word long in
like "press button". Maximum number of space-separated words that can a string, like "press button". Maximum number of space-separated
be part of a command name is given by ``settings.CMD_MAXLEN``. words that can be part of a command name is given by
- ``aliases`` (optional) - a list of alternate names for the command ``settings.CMD_MAXLEN``.
(``["l", "glance", "see"]``). Same name rules as for ``key`` applies. - ``aliases`` (optional list) - a list of alternate names for the
- ``locks`` - a `lock definition <Locks.html>`_, usually on the form command (``["l", "glance", "see"]``). Same name rules as for ``key``
``cmd:<lockfuncs>``. Locks is a rather big topic, so until you learn applies.
more about locks, stick to giving the lockstring ``"cmd:all()"`` to - ``locks`` (string) - a `lock definition <Locks.html>`_, usually on
make the command available to everyone. the form ``cmd:<lockfuncs>``. Locks is a rather big topic, so until
- ``help_category`` (optional) - setting this helps to structure the you learn more about locks, stick to giving the lockstring
auto-help into categories. If none is set, this will be set to ``"cmd:all()"`` to make the command available to everyone.
- ``help_category`` (optional string) - setting this helps to structure
the auto-help into categories. If none is set, this will be set to
*General*. *General*.
- ``save_for_next`` (optional). This defaults to ``False``. If - ``save_for_next`` (optional boolean). This defaults to ``False``. If
``True``, this command object (along with any changes you have done ``True``, this command object (along with any changes you have done
to it) will be stored by the system and can be accessed by the next to it) will be stored by the system and can be accessed by the next
command called by retrieving ``self.caller.ndb.last_cmd``. The next command called by retrieving ``self.caller.ndb.last_cmd``. The next
run command will either clear or replace the storage. run command will either clear or replace the storage.
- 'arg*regex' (optional raw string): This should be given as a `raw
regular expression string <http://docs.python.org/library/re.html>`_.
This will be compiled by the system at runtime. This allows you to
customize how the part*immediately following the command name (or
alias) must look in order for the parser to match for this command.
Normally the parser is highly efficient in picking out the command
name, also as the beginning of a longer word (as long as the longer
word is not a command name in it self). So ``"lookme"`` will be
parsed as the command ``"look"`` followed by the argument ``"me"``.
By using ``arg_regex`` you could for example force the parser to
require an optional space following the command name (regex string
for this would be ``r"\s.*?|$"``). In that case, ``"lookme"`` will
lead to an "command not found" error while ``"look me"`` will work as
expected.
- autohelp (optional boolean). Defaults to ``True``. This allows for
turning off the `auto-help
system <HelpSystem#Command%3Ci%3EAuto-help%3C/i%3Esystem.html>`_ on a
per-command basis. This could be useful if you either want to write
your help entries manually or hide the existence of a command from
``help``'s generated list.
You should also implement at least two methods, ``parse()`` and You should also implement at least two methods, ``parse()`` and
``func()`` (You could also implement ``perm()``, but that's not needed ``func()`` (You could also implement ``perm()``, but that's not needed
@ -155,31 +177,7 @@ Below is how you define a simple alternative "``look at``" command:
:: ::
from game.gamesrc.commands.basecommand import Commandclass CmdLookAt(Command): from game.gamesrc.commands.basecommand import Commandclass CmdLookAt(Command): """ An alternative (and silly) look command Usage: look at <what> Where <what> may only be 'here' in this example. This initial string (the __doc__ string) is also used to auto-generate the help for this command ... """ key = "look at" # this is the command name to use aliases = ["la", "look a"] # aliases to the command name locks = "cmd:all()" help_category = "General" def parse(self): "Very trivial parser" self.what = self.args.strip() def func(self): "This actually does things" caller = self.caller if not self.what: caller.msg("Look at what?") elif self.what == 'here': # look at the current location description = caller.location.db.desc caller.msg(description) else: # we don't add any more functionality in this example caller.msg("Sorry, you can only look 'here'...")
"""
An alternative (and silly) look command Usage:
look at <what> Where <what> may only be 'here' in this example. This initial string (the __doc__ string)
is also used to auto-generate the help
for this command ...
"""
key = "look at" # this is the command name to use
aliases = ["la", "look a"] # aliases to the command name
locks = "cmd:all()"
help_category = "General" def parse(self):
"Very trivial parser"
self.what = self.args.strip() def func(self):
"This actually does things"
caller = self.caller
if not self.what:
caller.msg("Look at what?")
elif self.what == 'here':
# look at the current location
description = caller.location.db.desc
caller.msg(description)
else:
# we don't add any more functionality in this example
caller.msg("Sorry, you can only look 'here'...")
The power of having commands as classes and to separate ``parse()`` and The power of having commands as classes and to separate ``parse()`` and
``func()`` lies in the ability to inherit functionality without having ``func()`` lies in the ability to inherit functionality without having
@ -236,17 +234,7 @@ rules <Commands#Merge_rules.html>`_ section).
:: ::
from src.commands.cmdset import CmdSet from src.commands.cmdset import CmdSet from game.gamesrc.commands import mycommandsclass MyCmdSet(CmdSet): def at_cmdset_creation(self): """ The only thing this method should need to do is to add commands to the set. """ self.add(mycommands.MyCommand1()) self.add(mycommands.MyCommand2()) self.add(mycommands.MyCommand3())
from game.gamesrc.commands import mycommandsclass MyCmdSet(CmdSet):
def at_cmdset_creation(self):
"""
The only thing this method should need
to do is to add commands to the set.
"""
self.add(mycommands.MyCommand1())
self.add(mycommands.MyCommand2())
self.add(mycommands.MyCommand3())
The !CmdSet's ``add()`` method can also take another CmdSet as input. In The !CmdSet's ``add()`` method can also take another CmdSet as input. In
this case all the commands from that CmdSet will be appended to this one this case all the commands from that CmdSet will be appended to this one
@ -254,10 +242,7 @@ as if you added them line by line:
:: ::
at_cmdset_creation(): at_cmdset_creation(): ... self.add(AdditionalCmdSet) # adds all command from this set ...
...
self.add(AdditionalCmdSet) # adds all command from this set
...
If you added your command to an existing cmdset (like to the default If you added your command to an existing cmdset (like to the default
cmdset), that set is already loaded into memory. You need to make the cmdset), that set is already loaded into memory. You need to make the
@ -320,19 +305,7 @@ look:
:: ::
from game.gamesrc.commands.basecommand import MuxCommandclass MyCommand(MuxCommand): from game.gamesrc.commands.basecommand import MuxCommandclass MyCommand(MuxCommand): """ Simple command example Usage: mycommand <text> This command simply echoes text back to the caller. (this string is also the help text for the command) """ key = "mycommand" locks = "cmd:all()" def func(self): "This actually does things" if not self.args: self.caller.msg("You didn't enter anything!") else: self.caller.msg("You gave the string: '%s'" % self.args)
"""
Simple command example Usage:
mycommand <text> This command simply echoes text back to the caller.
(this string is also the help text for the command)
""" key = "mycommand"
locks = "cmd:all()" def func(self):
"This actually does things"
if not self.args:
self.caller.msg("You didn't enter anything!")
else:
self.caller.msg("You gave the string: '%s'" % self.args)
Next we want to make this command available to us. There are many ways Next we want to make this command available to us. There are many ways
to do this, but all of them involves putting this command in a *Command to do this, but all of them involves putting this command in a *Command
@ -349,10 +322,7 @@ This is what we have now:
:: ::
from game.gamesrc.commands.basecmdset import CmdSet from game.gamesrc.commands.basecmdset import CmdSet
from game.gamesrc.commands import mycommandclass MyCmdSet(CmdSet): from game.gamesrc.commands import mycommandclass MyCmdSet(CmdSet): key = "MyCmdSet" def at_cmdset_creation(self): self.add(mycommand.MyCommand())
key = "MyCmdSet" def at_cmdset_creation(self):
self.add(mycommand.MyCommand())
This new command set could of course contain any number of commands. We This new command set could of course contain any number of commands. We
will now temporarily *merge* this command set to your current set. This will now temporarily *merge* this command set to your current set. This
@ -416,15 +386,7 @@ class and you will in fact append it to the existing command set.
:: ::
# file gamesrc/commands/basecmdset.py # file gamesrc/commands/basecmdset.py ... from game.gamesrc.commands import mycommandclass DefaultSet(BaseDefaultSet): key = DefaultMUX def at_cmdset_creation(self): # this first adds all default commands super(DefaultSet, self).at_cmdset_creation() # all commands added after this point will extend or # overwrite the default commands. self.add(mycommand.MyCommand())
...
from game.gamesrc.commands import mycommandclass DefaultSet(BaseDefaultSet):
key = DefaultMUX def at_cmdset_creation(self): # this first adds all default commands
super(DefaultSet, self).at_cmdset_creation() # all commands added after this point will extend or
# overwrite the default commands.
self.add(mycommand.MyCommand())
Again, you need to run the ``@reload`` command to make these changes Again, you need to run the ``@reload`` command to make these changes
available. available.
@ -513,8 +475,7 @@ Same-key commands are merged by priority.
:: ::
# Union # Union A1,A2 + B1,B2,B3,B4 = A1,A2,B3,B4
A1,A2 + B1,B2,B3,B4 = A1,A2,B3,B4
**Intersect** - Only commands found in *both* cmdsets (i.e. which have **Intersect** - Only commands found in *both* cmdsets (i.e. which have
the same keys) end up in the merged cmdset, with the higher-priority the same keys) end up in the merged cmdset, with the higher-priority
@ -522,8 +483,7 @@ cmdset replacing the lower one's commands.
:: ::
# Intersect # Intersect A1,A3,A5 + B1,B2,B4,B5 = A1,A5
A1,A3,A5 + B1,B2,B4,B5 = A1,A5
**Replace** - The commands of the higher-prio cmdset completely replaces **Replace** - The commands of the higher-prio cmdset completely replaces
the lower-priority cmdset's commands, regardless of if same-key commands the lower-priority cmdset's commands, regardless of if same-key commands
@ -531,8 +491,7 @@ exist or not.
:: ::
# Replace # Replace A1,A3 + B1,B2,B4,B5 = A1,A3
A1,A3 + B1,B2,B4,B5 = A1,A3
**Remove** - The high-priority command sets removes same-key commands **Remove** - The high-priority command sets removes same-key commands
from the lower-priority cmdset. They are not replaced with anything, so from the lower-priority cmdset. They are not replaced with anything, so
@ -541,8 +500,7 @@ high-prio one as a template.
:: ::
# Remove # Remove A1,A3 + B1,B2,B3,B4,B5 = B2,B4,B5
A1,A3 + B1,B2,B3,B4,B5 = B2,B4,B5
Besides ``priority`` and ``mergetype``, a command set also takes a few Besides ``priority`` and ``mergetype``, a command set also takes a few
other variables to control how they merge: other variables to control how they merge:
@ -569,17 +527,7 @@ More advanced cmdset example:
:: ::
class MyCmdSet(CmdSet): key = "MyCmdSet" class MyCmdSet(CmdSet): key = "MyCmdSet" priority = 4 mergetype = "Replace" key_mergetype = 'MyOtherCmdSet':'Union' def at_cmdset_creation(self): """ The only thing this method should need to do is to add commands to the set. """ self.add(mycommands.MyCommand1()) self.add(mycommands.MyCommand2()) self.add(mycommands.MyCommand3())
priority = 4
mergetype = "Replace"
key_mergetype = 'MyOtherCmdSet':'Union' def at_cmdset_creation(self):
"""
The only thing this method should need
to do is to add commands to the set.
"""
self.add(mycommands.MyCommand1())
self.add(mycommands.MyCommand2())
self.add(mycommands.MyCommand3())
System commands System commands
--------------- ---------------
@ -633,12 +581,7 @@ command must be added to a cmdset as well before it will work.
:: ::
from src.commands import cmdhandler from src.commands import cmdhandler from game.gamesrc.commands.basecommand import Commandclass MyNoInputCommand(Command): "Usage: Just press return, I dare you" key = cmdhandler.CMD_NOINPUT def func(self): self.caller.msg("Don't just press return like that, talk to me!")
from game.gamesrc.commands.basecommand import Commandclass MyNoInputCommand(Command):
"Usage: Just press return, I dare you"
key = cmdhandler.CMD_NOINPUT
def func(self):
self.caller.msg("Don't just press return like that, talk to me!")
Exits Exits
----- -----

View file

@ -76,12 +76,7 @@ send a non-persistent message, also if you send it a ``Msg`` object.
:: ::
# assume we have a 'sender' object and a channel named 'mychan'# send and store in database # assume we have a 'sender' object and a channel named 'mychan'# send and store in database from src.utils import create mymsg = create.create_message(sender, "Hello!", channels=[mychan]) mychan.msg(mymsg)# send a one-time message mychan.msg("Hello!")# send a one-time message created from a Msg object mychan.tempmsg(mymsg)
from src.utils import create
mymsg = create.create_message(sender, "Hello!", channels=[mychan])
mychan.msg(mymsg)# send a one-time message
mychan.msg("Hello!")# send a one-time message created from a Msg object
mychan.tempmsg(mymsg)
As a more advanced note, sending text to channels is a "special As a more advanced note, sending text to channels is a "special
exception" as far as commands are concerned, and you may completely exception" as far as commands are concerned, and you may completely

View file

@ -8,11 +8,7 @@ tells you how to connect.
:: ::
============================================================== ==============================================================
Welcome to Evennia, version HG-Alpha! If you have an existing account, connect to it by typing: Welcome to Evennia, version HG-Alpha! If you have an existing account, connect to it by typing: connect <email> <password> If you need to create an account, type (without the <>'s): create "<username>" <email> <password> Enter help for more info. look will re-show this screen. ==============================================================
connect <email> <password>
If you need to create an account, type (without the <>'s):
create "<username>" <email> <password> Enter help for more info. look will re-show this screen.
==============================================================
Effective, but not very exciting. You will most likely want to change Effective, but not very exciting. You will most likely want to change
this to be more unique for your game. this to be more unique for your game.

View file

@ -10,7 +10,11 @@ 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 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 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 access. Otherwise, it goes a long way just pointing out wiki errors so
devs can fix them. 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
"http://code.google.com/p/evennia/source/checkout?repo
wiki">here and then continue from point ``2`` below.
Contributing with Code through a clone repository Contributing with Code through a clone repository
------------------------------------------------- -------------------------------------------------
@ -43,7 +47,9 @@ Once you have an online clone and a local copy of it:
#. Code away on your computer, fixing bugs or whatnot (you can be #. Code away on your computer, fixing bugs or whatnot (you can be
offline for this). Commit your code to your local clone as you work, offline for this). Commit your code to your local clone as you work,
as often as you like. as often as you like. There are some suggestions for setting up a
sane local work environment with Mercurial
`here <http://code.google.com/p/evennia/wiki/VersionControl>`_.
#. When you have something you feel is worthwhile (or just want to ask #. When you have something you feel is worthwhile (or just want to ask
people's opinions or make an online backup), *push* your local code people's opinions or make an online backup), *push* your local code
up to your online repository with Mercurial. up to your online repository with Mercurial.

File diff suppressed because it is too large Load diff

View file

@ -74,6 +74,7 @@ Programming Evennia
Game implementation hints Game implementation hints
------------------------- -------------------------
- `Planning your own Evennia game <GamePlanning.html>`_
- `Creating a Zoning system <Zones.html>`_ - `Creating a Zoning system <Zones.html>`_
- `Implementing cooldowns for commands <CommandCooldown.html>`_ - `Implementing cooldowns for commands <CommandCooldown.html>`_
@ -85,7 +86,8 @@ features and ideas. Items here may or may not make it into Evennia down
the road.* the road.*
- `Basic game system implementation <WorkshopDefaultGame.html>`_ - `Basic game system implementation <WorkshopDefaultGame.html>`_
- `Rtclient protocol <Workshop.html>`_ (inactive)
- `Rtclient protocol <Workshop.html>`_ (deprecated)
- `Summary of changes <EvenniaDevel.html>`_ of latest version vs old - `Summary of changes <EvenniaDevel.html>`_ of latest version vs old
Evennia (pre aug2010) Evennia (implemented in aug2010)

View file

@ -76,16 +76,7 @@ the server.
game/ game/
evennia.py evennia.py
manage.py gamesrc/ manage.py gamesrc/ commands/ examples/ scripts/ examples/ objects/ examples/ world/ examples/ conf/
commands/
examples/
scripts/
examples/
objects/
examples/
world/
examples/
conf/
``game/gamesrc/`` ``game/gamesrc/``
~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~
@ -151,12 +142,12 @@ syntax.
``gamesrc/conf/`` ``gamesrc/conf/``
^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^
``gamesrc/world/`` holds optional extension modules for the Evennia ``gamesrc/conf/`` holds optional extension modules for the Evennia
engine. Certain functionality in the server is meant to be extended, and engine. Certain functionality in the server is meant to be extended, and
in order to allow you to do this without modifying the server itself, it in order to allow you to do this without modifying the server itself, it
imports files from this directory. Modifying these are optionally and imports files from this directory. Modifying these are optional and you
you can usually change a variable in ``game/settings.py`` to change can usually change a variable in ``game/settings.py`` to change exactly
which module Evennia is looking for. There are dummy example files in which module Evennia actually uses. There are dummy example files in
here, read their headers for usage instructions. here, read their headers for usage instructions.
The ``src/`` directory The ``src/`` directory
@ -171,17 +162,7 @@ bugs or features missing, file a bug report or send us a message.
:: ::
src/ src/
settings_defaults.py commands/ settings_defaults.py commands/ comms/ help/ objects/ locks/ players/ scripts/ server/ typeclasses/ utils/ web/
comms/
help/
objects/
locks/
players/
scripts/
server/
typeclasses/
utils/
web/
Most of the folders in ``src/`` are technically "Django apps", Most of the folders in ``src/`` are technically "Django apps",
identified by containing a file ``models.py`` and usually identified by containing a file ``models.py`` and usually

View file

@ -116,9 +116,7 @@ Example of new command definition:
:: ::
class CmdTest(Command): class CmdTest(Command): def func(self): self.caller.msg("This is the test!")
def func(self):
self.caller.msg("This is the test!")
Events + States -> Scripts Events + States -> Scripts
-------------------------- --------------------------
@ -264,16 +262,14 @@ just do:
:: ::
obj.db.attr = value obj.db.attr = value value = obj.db.attr
value = obj.db.attr
And for storing something non-persistently (stored only until the server And for storing something non-persistently (stored only until the server
reboots) you can just do reboots) you can just do
:: ::
obj.attr = value obj.attr = value value = obj.attr
value = obj.attr
The last example may sound trivial, but it's actually impossible to do 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 in trunk since django objects are not guaranteed to remain the same

View file

@ -40,7 +40,7 @@ created for you automatically if you use the defaults).
Using the full power of Python throughout the server offers some Using the full power of Python throughout the server offers some
distinct advantages. All your coding, from object definitions and custom distinct advantages. All your coding, from object definitions and custom
commands to AI scripts and economic systems are done in normal Python commands to AI scripts and economic systems is done in normal Python
modules rather than some ad-hoc scripting language. The fact that you modules rather than some ad-hoc scripting language. The fact that you
script the game in the same high-level language that you code it in script the game in the same high-level language that you code it in
allows for very powerful and custom game implementations indeed. allows for very powerful and custom game implementations indeed.

View file

@ -10,8 +10,7 @@ entrust to just anybody.
:: ::
@py 1+2 @py 1+2 <<< 3
<<< 3
Available variables Available variables
------------------- -------------------
@ -37,8 +36,7 @@ found in ``src/utils/utils.py``:
:: ::
@py from src.utils import utils; utils.time_format(33333) @py from src.utils import utils; utils.time_format(33333) <<< Done.
<<< Done.
Note that we didn't get any return value, all we where told is that the Note that we didn't get any return value, all we where told is that the
code finished executing without error. This is often the case in more code finished executing without error. This is often the case in more
@ -48,9 +46,7 @@ system to echo it to us explicitly with ``self.msg()``.
:: ::
@py from src.utils import utils; self.msg(utils.time_format(33333)) @py from src.utils import utils; self.msg(utils.time_format(33333)) 09:15 <<< Done.
09:15
<<< Done.
If you were to use Python's standard ``print``, you will see the result If you were to use Python's standard ``print``, you will see the result
in your current ``stdout`` (your terminal by default), *if* you are in your current ``stdout`` (your terminal by default), *if* you are
@ -67,10 +63,7 @@ Locating an object is best done using ``self.search()``:
:: ::
@py self.search("red_ball") @py self.search("red_ball") <<< Ball @py self.search("red_ball").db.color = "red" <<< Done. @py self.search("red_ball").db.color <<< red
<<< Ball @py self.search("red_ball").db.color = "red"
<<< Done. @py self.search("red_ball").db.color
<<< red
``self.search()`` is by far the most used case, but you can also search ``self.search()`` is by far the most used case, but you can also search
other database tables for other Evennia entities like scripts or other database tables for other Evennia entities like scripts or
@ -79,8 +72,7 @@ entries found in ``src.utils.search``.
:: ::
@py from src.utils import search; self.msg(search.scripts("sys_game_time")) @py from src.utils import search; self.msg(search.scripts("sys_game_time")) <<< [<src.utils.gametime.GameTime object at 0x852be2c>]
<<< [<src.utils.gametime.GameTime object at 0x852be2c>]
You can also use the database model managers directly (accessible You can also use the database model managers directly (accessible
through the ``objects`` properties of database models). This is a bit through the ``objects`` properties of database models). This is a bit
@ -89,8 +81,7 @@ search methods defined in each manager.
:: ::
@py ScriptDB.objects.script_search("sys_game_time") @py ScriptDB.objects.script_search("sys_game_time") <<< [<src.utils.gametime.GameTime object at 0x852be2c>]
<<< [<src.utils.gametime.GameTime object at 0x852be2c>]
(Note that since this second example becomes a simple statement, we (Note that since this second example becomes a simple statement, we
don't have to wrap it in ``self.msg()`` to get the output). If you want don't have to wrap it in ``self.msg()`` to get the output). If you want
@ -101,8 +92,7 @@ contents of the database using normal Django query operations:
:: ::
@py ConfigValue.objects.all() @py ConfigValue.objects.all() <<< [<ConfigValue: default_home]>, <ConfigValue:site_name>, ...]
<<< [<ConfigValue: default_home]>, <ConfigValue:site_name>, ...]
In doing so however, keep in mind the difference between `Typeclasses In doing so however, keep in mind the difference between `Typeclasses
and Database Objects <Typeclasses.html>`_: Using the search commands in and Database Objects <Typeclasses.html>`_: Using the search commands in
@ -114,13 +104,7 @@ most situations.
:: ::
# this uses Evennia's manager method get_id(). # this uses Evennia's manager method get_id(). # It returns a Character typeclass instance @py ObjectDB.objects.get_id(1).__class__ <<< Character# this uses the standard Django get() query. # It returns a django database model instance. @py ObjectDB.objects.get(id=1).__class__ <<< <class 'src.objects.models.ObjectDB'>
# It returns a Character typeclass instance
@py ObjectDB.objects.get_id(1).__class__
<<< Character# this uses the standard Django get() query.
# It returns a django database model instance.
@py ObjectDB.objects.get(id=1).__class__
<<< <class 'src.objects.models.ObjectDB'>
Running a Python Parser outside the game Running a Python Parser outside the game
======================================== ========================================
@ -145,8 +129,5 @@ tab-completion and ``__doc__``-string reading.
:: ::
$ python manage.py shellIPython 0.10 -- An enhanced Interactive Python $ python manage.py shellIPython 0.10 -- An enhanced Interactive Python ...In [1]: from src.objects.models import ObjectDB In [2]: ObjectDB.objects.all() Out[3]: [<ObjectDB: Harry>, <ObjectDB: Limbo>, ...]
...In [1]: from src.objects.models import ObjectDB
In [2]: ObjectDB.objects.all()
Out[3]: [<ObjectDB: Harry>, <ObjectDB: Limbo>, ...]

View file

@ -67,7 +67,8 @@ Twisted also requires:
**Django** (http://www.djangoproject.com) **Django** (http://www.djangoproject.com)
- Version 1.2.5+ or latest development versions highly recommended. - Version 1.2.5+ or latest development versions highly recommended.
- PIL library (http://www.pythonware.com/products/pil) - PIL (Python Imaging Library) (http://www.pythonware.com/products/pil)
- not strictly required unless you use images in Django.
To download/update Evennia: To download/update Evennia:
@ -129,17 +130,19 @@ or all of these instead:
``easy_install`` or ``pip`` like Linux users do. There are however ``easy_install`` or ``pip`` like Linux users do. There are however
reports that you might need to get the reports that you might need to get the
`Xcode <https://developer.apple.com/xcode/.html>`_ development system to `Xcode <https://developer.apple.com/xcode/.html>`_ development system to
install the packages that requites extension compiling. You can also install the packages that requires extension compiling. You can also
retrieve the dependencies directly and install them through their native retrieve the dependencies directly and install them through their native
installers or python setups. installers or python setups. Some users have reported problems compiling
the ``PIL`` library on Mac, it's however not strictly required to use
Django.
**Windows** users may choose to install **Windows** users may choose to install
`ActivePython <http://www.activestate.com/activepython/downloads>`_ `ActivePython <http://www.activestate.com/activepython/downloads>`_
instead of the usual Python. If ActivePython is installed, you can use instead of the usual Python. If ActivePython is installed, you can use
`pypm <http://docs.activestate.com/activepython/2.6/pypm.html>`_ in the `pypm <http://docs.activestate.com/activepython/2.6/pypm.html>`_ in the
same manner as ``easy_install``/``pip`` above. This *greatly* simplifies same manner as ``easy_install``/``pip`` above. This *greatly* simplifies
getting started on Windows since that platform is otherwise missing many getting started on Windows since that platform is by default missing
of the background developer systems that Linux users take for granted. many of the sane developer systems that Linux users take for granted.
After installing ActivePython you may need to open a new DOS window to After installing ActivePython you may need to open a new DOS window to
make this new command available on the command line: make this new command available on the command line:
@ -171,20 +174,21 @@ trick (first place yourself in a directory where you want a new folder
hg clone https://code.google.com/p/evennia/ evennia hg clone https://code.google.com/p/evennia/ evennia
(``hg`` is the chemical abbreviation of mercury, hence the use of ``hg`` (Mercurial is abbreviated ``hg`` since this is the chemical symbol for
for ``mercurial``) mercury).
In the future, you just do In the future, you just do
:: ::
hg pull hg pull hg update
hg update
from your ``evennia/`` directory to obtain the latest updates. from your ``evennia/`` directory to obtain the latest updates.
If you use a graphical Mercurial client, use the equivalent buttons to If you use a graphical Mercurial client, use the equivalent buttons to
perform the above operations. perform the above operations. See
`here <http://code.google.com/p/evennia/wiki/VersionControl>`_ for more
advanced suggestions to set up a development environment with Mercurial.
Step 2: Setting up the Server Step 2: Setting up the Server
----------------------------- -----------------------------
@ -324,10 +328,7 @@ virtual environment in here.
:: ::
# for Linux/Unix: # for Linux/Unix: source bin/activate # for Windows: <path_to_this_place>\Scripts\activate.bat
source bin/activate
# for Windows:
<path_to_this_place>\Scripts\activate.bat
The virtual environment within our *mudenv* folder is now active. Next The virtual environment within our *mudenv* folder is now active. Next
we get all the requirements with *pip*, which is included with we get all the requirements with *pip*, which is included with

View file

@ -49,16 +49,7 @@ Example (from a module with command definitions):
:: ::
class CmdMyCmd(Command): class CmdMyCmd(Command): """ mycmd - my very own command Usage: mycmd[/switches] <args> Switches: test - test the command run - do something else This is my own command that does things to you when you supply it with arguments. """ ... help_category = "Building" ...
"""
mycmd - my very own command Usage:
mycmd[/switches] <args> Switches:
test - test the command
run - do something else This is my own command that does things to you when you
supply it with arguments. """
...
help_category = "Building"
...
The text at the very top of the command class definition is the class' The text at the very top of the command class definition is the class'
``__doc__``-string and will be shown to users looking for help. Try to ``__doc__``-string and will be shown to users looking for help. Try to
@ -98,10 +89,7 @@ You can create new help entries in code by using
:: ::
from src.utils import create from src.utils import create entry = create.create_help_entry("emote", "Emoting is important because ...", category="Roleplaying", locks="view:all()"):
entry = create.create_help_entry("emote",
"Emoting is important because ...",
category="Roleplaying", locks="view:all()"):
From inside the game those with the right permissions can use the From inside the game those with the right permissions can use the
``@sethelp`` command to add and modify help entries. ``@sethelp`` command to add and modify help entries.

View file

@ -6,17 +6,18 @@ How to *get* Help
If you cannot find what you are looking for in the `online If you cannot find what you are looking for in the `online
documentation <Index.html>`_, here's what to do: documentation <Index.html>`_, here's what to do:
- If you don't understand a concept or think the docs are not clear - If you think the documentation is not clear enough, fill in our quick
enough, fill in our quick little little "https://docs.google.com/spreadsheet/viewform?hl
"https://docs.google.com/spreadsheet/viewform?hl
en*US&formkey* en*US&formkey*
dGN0VlJXMWpCT3VHaHpscDEzY1RoZGc6MQ#gid dGN0VlJXMWpCT3VHaHpscDEzY1RoZGc6MQ#gid
====================================== ======================================
0.html">online form and say so - maybe the docs need to be improved 0.html">online form and say so (no login required). Maybe the docs
or a tutorial added! need to be improved or a new tutorial added! Note that this form will
help you by helping us improve documentation, but you cannot get
direct, specific answers back from it.
- If you have trouble with a missing feature or a problem you think is - If you have trouble with a missing feature or a problem you think is
a bug, go to the `issue a bug, go to the `issue

View file

@ -85,8 +85,7 @@ Write something in the Evennia channel *irc*.
:: ::
irc Hello, World! irc Hello, World! [irc] Anna: Hello, World!
[irc] Anna: Hello, World!
If you are viewing your IRC channel with a separate IRC client you If you are viewing your IRC channel with a separate IRC client you
should see your text appearing there, spoken by the bot: should see your text appearing there, spoken by the bot:

View file

@ -25,8 +25,7 @@ your ``game/settings.py`` file:
:: ::
USE_I18N = True USE_I18N = True LANGUAGE_CODE = 'en'
LANGUAGE_CODE = 'en'
Here ``'en'`` should be changed to the abbreviation for one of the Here ``'en'`` should be changed to the abbreviation for one of the
supported languages found in ``locale/``. Restart the server to activate supported languages found in ``locale/``. Restart the server to activate

View file

@ -54,9 +54,7 @@ how it would (and do) look from inside the ``@delete`` command:
:: ::
if not obj.access(accessing_obj, 'delete'): if not obj.access(accessing_obj, 'delete'): accessing_obj.msg("Sorry, you may not delete that.") return
accessing_obj.msg("Sorry, you may not delete that.")
return
Defining locks Defining locks
-------------- --------------
@ -69,9 +67,7 @@ Here are some examples of lock strings:
:: ::
delete:id(34) # only allow obj #34 to delete delete:id(34) # only allow obj #34 to delete edit:all() # let everyone edit get: not attr(very_weak) or perm(Wizard) # only those who are not "very_weak" or are Wizards may pick this up
edit:all() # let everyone edit
get: not attr(very_weak) or perm(Wizard) # only those who are not "very_weak" or are Wizards may pick this up
Formally, a lockstring has the following syntax: Formally, a lockstring has the following syntax:
@ -178,11 +174,7 @@ appear as extra arguments.
:: ::
# A simple example lock function. Called with e.g. id(34)def id(accessing_obj, accessed_obj, *args, **kwargs): # A simple example lock function. Called with e.g. id(34)def id(accessing_obj, accessed_obj, *args, **kwargs): if args: wanted_id = args[0] return accessing_obj.id == wanted_id return False
if args:
wanted_id = args[0]
return accessing_obj.id == wanted_id
return False
(Using the ``*`` and ``**`` syntax causes Python to magically put all (Using the ``*`` and ``**`` syntax causes Python to magically put all
extra arguments into a list ``args`` and all keyword arguments into a extra arguments into a list ``args`` and all keyword arguments into a
@ -250,11 +242,7 @@ default permission hierarchy is as follows:
:: ::
Immortals Immortals Wizards Builders PlayerHelpers Players # this is what all new Players start with by default
Wizards
Builders
PlayerHelpers
Players # this is what all new Players start with by default
The main use of this is that if you use the lock function ``perm()`` The main use of this is that if you use the lock function ``perm()``
mentioned above, a lock check for a particular permission in the mentioned above, a lock check for a particular permission in the
@ -267,8 +255,7 @@ looked for is not in the hierarchy, an exact match is required.
:: ::
obj1.permissions = ["Builders", "cool_guy"] obj1.permissions = ["Builders", "cool_guy"] obj2.locks.add("enter:perm_above(Players) and perm(cool_guy)")obj2.access(obj1, "enter") # this returns True!
obj2.locks.add("enter:perm_above(Players) and perm(cool_guy)")obj2.access(obj1, "enter") # this returns True!
Superusers Superusers
---------- ----------
@ -339,8 +326,7 @@ other is an `Object <Objects.html>`_ called ``box``.
:: ::
> @create/drop box > @create/drop box > @desc box = "This is a very big and heavy box."
> @desc box = "This is a very big and heavy box."
We want to limit which objects can pick up this heavy box. Let's say We want to limit which objects can pick up this heavy box. Let's say
that to do that we require the would-be lifter to to have an attribute that to do that we require the would-be lifter to to have an attribute
@ -359,12 +345,7 @@ this snippet:
:: ::
if not obj.access(caller, 'get'): if not obj.access(caller, 'get'): if obj.db.get_err_msg: caller.msg(obj.db.get_err_msg) else: caller.msg("You can't get that.") return
if obj.db.get_err_msg:
caller.msg(obj.db.get_err_msg)
else:
caller.msg("You can't get that.")
return
So the ``get`` command looks for a lock with the type *get* (not so So the ``get`` command looks for a lock with the type *get* (not so
surprising). It also looks for an `Attribute <Attributes.html>`_ on the surprising). It also looks for an `Attribute <Attributes.html>`_ on the
@ -398,9 +379,7 @@ like this:
:: ::
from src.utils import create from src.utils import create box = create.create_object(None, key="box", locks="get:attr_gt(strength, 50)")# or, if we don't set the locks right awaybox.locks.add("get:attr_gt(strength, 50)")# set the attributesbox.db.desc = "This is a very big and heavy box." box.db.get_err_msg = "You are not strong enough to lift this box."# one heavy box, ready to withstand all but the strongest...
box = create.create_object(None, key="box", locks="get:attr_gt(strength, 50)")# or, if we don't set the locks right awaybox.locks.add("get:attr_gt(strength, 50)")# set the attributesbox.db.desc = "This is a very big and heavy box."
box.db.get_err_msg = "You are not strong enough to lift this box."# one heavy box, ready to withstand all but the strongest...
On Django's permission system On Django's permission system
============================= =============================

View file

@ -50,7 +50,7 @@ becomes equivalent to "``look The red sports car``".
:: ::
nick/players tom = TommyBoy nick/players tom = Thomas Johnsson
This is useful for commands searching for players explicitly: This is useful for commands searching for players explicitly:
@ -83,13 +83,7 @@ checking, searches and conversion.
:: ::
# A command/channel nick: # A command/channel nick: object.nicks.add("greetjack", "tell Jack = Hello pal!")# An object nick: object.nicks.add("rose", "The red flower", nick_type="object")# An player nick: object.nicks("tom", "Tommy Hill", nick_type="player")# My own custom nick type (handled by my own game code somehow): object.nicks.add("hood", "The hooded man", nick_type="my_identsystem")# get back the translated nick: full_name = object.nicks.get("rose", nick_type="object")# delete a previous set nick object.nicks.del("rose", nick_type="object")
object.nicks.add("greetjack", "tell Jack = Hello pal!")# An object nick:
object.nicks.add("rose", "The red flower", nick_type="object")# An player nick:
object.nicks("tom", "Tommy Hill", nick_type="player")# My own custom nick type (handled by my own game code somehow):
object.nicks.add("hood", "The hooded man", nick_type="my_identsystem")# get back the translated nick:
full_name = object.nicks.get("rose", nick_type="object")# delete a previous set nick
object.nicks.del("rose", nick_type="object")
In a command definition you can reach the nick handler through In a command definition you can reach the nick handler through
``self.caller.nicks``. See the ``nick`` command in ``self.caller.nicks``. See the ``nick`` command in

View file

@ -19,14 +19,7 @@ Here's how to define a new Object typeclass in code:
:: ::
from game.gamesrc.objects.baseobjects import Objectclass Rose(Object): from game.gamesrc.objects.baseobjects import Objectclass Rose(Object): """ This creates a simple rose object """ def at_object_creation(self): "this is called only once, when object is first created" # add a persistent attribute 'desc' to object. self.db.desc = "This is a pretty rose with thorns."
"""
This creates a simple rose object
"""
def at_object_creation(self):
"this is called only once, when object is first created"
# add a persistent attribute 'desc' to object.
self.db.desc = "This is a pretty rose with thorns."
Save your class to a module under ``game/gamesrc/objects``, say Save your class to a module under ``game/gamesrc/objects``, say
``flowers.py``. Now you just need to point to the class *Rose* with the ``flowers.py``. Now you just need to point to the class *Rose* with the
@ -41,8 +34,7 @@ To create a new object in code, use the method
:: ::
from src.utils import create from src.utils import create new_rose = create.create_object("game.gamesrc.objects.flowers.Rose", key="MyRose")
new_rose = create.create_object("game.gamesrc.objects.flowers.Rose", key="MyRose")
(You have to give the full path to the class in this case - (You have to give the full path to the class in this case -
``create.create_object`` is a powerful function that should be used for ``create.create_object`` is a powerful function that should be used for

View file

@ -41,19 +41,7 @@ Here's how to define a new Player typeclass in code:
:: ::
from src.players.player import Player from src.players.player import Player class ConfigPlayer(Player): """ This creates a Player with some configuration options """ at_player_creation(self): "this is called only once, when player is first created" self.db.real_name = None # this is set later self.db.real_address = None # '' self.db.config_1 = True # default config self.db.config_2 = False # " self.db.config_3 = 1 # " # ... whatever else our game needs to know
class ConfigPlayer(Player):
"""
This creates a Player with some configuration options
"""
at_player_creation(self):
"this is called only once, when player is first created"
self.db.real_name = None # this is set later
self.db.real_address = None # ''
self.db.config_1 = True # default config
self.db.config_2 = False # "
self.db.config_3 = 1 # "
# ... whatever else our game needs to know
There is no pre-made folder in ``game/gamesrc`` to store custom player There is no pre-made folder in ``game/gamesrc`` to store custom player
typeclasses. Either make your own folder or store it in typeclasses. Either make your own folder or store it in

View file

@ -34,10 +34,7 @@ inheriting from ``game.gamesrc.objects.baseobjecs.Character``.
:: ::
from game.gamesrc.objects.baseobjects import Characterclass ColourableCharacter(Character): from game.gamesrc.objects.baseobjects import Characterclass ColourableCharacter(Character): at_object_creation(self): # set a colour config value self.db.config_colour = True
at_object_creation(self):
# set a colour config value
self.db.config_colour = True
Above we set a simple config value as an `attribute <Attributes.html>`_. Above we set a simple config value as an `attribute <Attributes.html>`_.
@ -51,7 +48,7 @@ everything works - you don't want to render your root user unusable!).
:: ::
@typeclass/reset/force mycharacter.ColourableCharacter @typeclass/reset/force Bob = mycharacter.ColourableCharacter
The ``/reset`` switch clears all attributes and properties back to the The ``/reset`` switch clears all attributes and properties back to the
default for the new typeclass and forces the object to re-run all its default for the new typeclass and forces the object to re-run all its
@ -77,11 +74,7 @@ original. Here's how it could look:
:: ::
from src.utils import ansimsg(self, message, from_obj=None, data=None): from src.utils import ansimsg(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)
"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 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 ansi characters if the config value is not set to True. Once that's
@ -93,7 +86,13 @@ Since we put this custom ``msg()`` in our typeclass
the default method on ``ObjectDB`` (which we now instead call manually). the default method on ``ObjectDB`` (which we now instead call manually).
There we go! Just flip the attribute ``config_colour`` to False and your There we go! Just flip the attribute ``config_colour`` to False and your
users will not see any colour. 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 Custom colour config command
---------------------------- ----------------------------
@ -107,22 +106,7 @@ for configuration down the line).
:: ::
from game.gamesrc.commands.basecommand import MuxCommandclass ConfigColourCmd(MuxCommand): from game.gamesrc.commands.basecommand import MuxCommandclass ConfigColourCmd(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)
"""
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 Lastly, we make this command available to the user by adding it to the
default command set. Easiest is to add it to the end of default command set. Easiest is to add it to the end of
@ -130,14 +114,7 @@ default command set. Easiest is to add it to the end of
:: ::
from game.gamesrc.commands import configcmds 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())
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 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 ``@reload`` command (or reboot the server). From here on out, your users

View file

@ -72,11 +72,7 @@ care of all initialization and startup of the script for you.
:: ::
# adding a script to an existing object 'myobj' # adding a script to an existing object 'myobj' myobj.scripts.add("game.gamesrc.scripts.myscripts.CoolScript") # alternative way from src.utils.create import create_script create_script("game.gamesrc.scripts.myscripts.CoolScript", obj=myobj)
myobj.scripts.add("game.gamesrc.scripts.myscripts.CoolScript")
# alternative way
from src.utils.create import create_script
create_script("game.gamesrc.scripts.myscripts.CoolScript", obj=myobj)
The creation method(s) takes an optional argument *key* that allows you The creation method(s) takes an optional argument *key* that allows you
to name your script uniquely before adding it. This can be useful if you to name your script uniquely before adding it. This can be useful if you
@ -88,9 +84,7 @@ Just don't supply an object to store it on.
:: ::
# adding a global script # adding a global script from src.utils.create import create_script create_script("game.gamesrc.scripts.globals.MyGlobalEconomy", key="economy", obj=None)
from src.utils.create import create_script
create_script("game.gamesrc.scripts.globals.MyGlobalEconomy", key="economy", obj=None)
Assuming the Script ``game.gamesrc.scripts.globals.MyGlobalEconomy`` Assuming the Script ``game.gamesrc.scripts.globals.MyGlobalEconomy``
exists, this will create and start it as a global script. exists, this will create and start it as a global script.
@ -218,8 +212,7 @@ locate the room you want:
:: ::
from src.utils.create import create_script from src.utils.create import create_script create_script('game.gamesrc.scripts.weather.Weather', obj=myroom)
create_script('game.gamesrc.scripts.weather.Weather', obj=myroom)
Or, from in-game, use the ``@script`` command: Or, from in-game, use the ``@script`` command:

View file

@ -160,20 +160,7 @@ create a module there with the following functions:
:: ::
# the caller is automatically added as first argument # the caller is automatically added as first argument def get_health(character): "Get health, stored as simple attribute" return character.db.health def get_stamina(character): "Get stamina level, stored as simple attribute" return character.db.stamina def get_skill(character, skillname, master=False): """we assume skills are stored as a dictionary stored in an attribute. Master skills are stored separately (for whatever reason)""" if master: return character.db.skills_master.get(skillname, "NoSkill") return character.db.skills.get(skillname, "NoSkill")
def get_health(character):
"Get health, stored as simple attribute"
return character.db.health
def get_stamina(character):
"Get stamina level, stored as simple attribute"
return character.db.stamina
def get_skill(character, skillname, master=False):
"""we assume skills are stored as a dictionary
stored in an attribute. Master skills are
stored separately (for whatever reason)"""
if master:
return character.db.skills_master.get(skillname, "NoSkill")
return character.db.skills.get(skillname, "NoSkill")
Done, the functions will return what we want assuming Characters do Done, the functions will return what we want assuming Characters do
store this information in our game. Let's finish up the first part of store this information in our game. Let's finish up the first part of
@ -181,21 +168,7 @@ the portal protocol:
:: ::
# this method could be named differently depending on the # this method could be named differently depending on the # protocol you are using (this is telnet) def lineReceived(self, string): # (does stuff to analyze the incoming string) # ... outdict = if GET_HEALTH: # call get_health(char) outdict["get_health"] = ([], ) elif GET_STAMINA: # call get_mana(char) outdict["get_stamina"] = ([], ) elif GET_MASTER_SKILL_SMITH: # call get_skill(char, "smithing", master=True) outdict["get_skill"] = (["smithing"], 'master':True) [...] self.sessionhandler.oob_data_out(outdict)
# protocol you are using (this is telnet)
def lineReceived(self, string):
# (does stuff to analyze the incoming string)
# ...
outdict =
if GET_HEALTH:
# call get_health(char)
outdict["get_health"] = ([], )
elif GET_STAMINA:
# call get_mana(char)
outdict["get_stamina"] = ([], )
elif GET_MASTER_SKILL_SMITH:
# call get_skill(char, "smithing", master=True)
outdict["get_skill"] = (["smithing"], 'master':True) [...] self.sessionhandler.oob_data_out(outdict)
The Server will properly accept this and call the relevant functions to The Server will properly accept this and call the relevant functions to
get their return values for the health, stamina and skill. The return get their return values for the health, stamina and skill. The return
@ -205,15 +178,7 @@ being passed back to the Portal. We need to define
:: ::
def oob_data_out(self, data): def oob_data_out(self, data): # the indata is a dictionary funcname:retval outstring = "" for funcname, retval in data.items(): if funcname == 'get_health': # convert to the right format for sending back to client, store # in outstring ... [...] # send off using the protocols send method (this is telnet) sendLine(outstring)
# the indata is a dictionary funcname:retval outstring = ""
for funcname, retval in data.items():
if funcname == 'get_health':
# convert to the right format for sending back to client, store
# in outstring ...
[...]
# send off using the protocols send method (this is telnet)
sendLine(outstring)
As seen, ``oob_data`` takes the values and formats into a form the As seen, ``oob_data`` takes the values and formats into a form the
protocol understands before sending it off. protocol understands before sending it off.

View file

@ -51,8 +51,7 @@ retrieved when emitting:
:: ::
&HELLO_VALUE.D me=Hello World &HELLO_VALUE.D me=Hello World &HELLO_WORLD.C me=$hello:@pemit %#=[v(HELLO_VALUE.D)]
&HELLO_WORLD.C me=$hello:@pemit %#=[v(HELLO_VALUE.D)]
The v() function returns the HELLO\_VALUE.D attribute on the object that The v() function returns the HELLO\_VALUE.D attribute on the object that
the command resides (``me``, which is yourself in this case). This the command resides (``me``, which is yourself in this case). This

View file

@ -43,8 +43,7 @@ You can also start the two components one at a time.
:: ::
python evennia.py start server python evennia.py start server python evennia.py start portal
python evennia.py start portal
Adding -i to either of these explicit commands will start that component Adding -i to either of these explicit commands will start that component
in interactive mode so it logs to the terminal rather than to log file. in interactive mode so it logs to the terminal rather than to log file.
@ -85,8 +84,7 @@ A reset is equivalent to
:: ::
python evennia.py stop server python evennia.py stop server python evennia.py start server
python evennia.py start server
Shutting down Shutting down
------------- -------------

View file

@ -56,9 +56,10 @@ see what happens in detail.
To play the tutorial "correctly", you should *not* do so as superuser. To play the tutorial "correctly", you should *not* do so as superuser.
The reason for this is that many game systems ignore the presence of a The reason for this is that many game systems ignore the presence of a
superuser and will thus not work as normal. Create a new, non-superuser superuser and will thus not work as normal. Log out, then reconnect.
character for playing instead. As superuser you can of course examine From the login screen, create a new, non-superuser character for playing
things "under the hood" later if you want. instead. As superuser you can of course examine things "under the hood"
later if you want.
Gameplay Gameplay
-------- --------
@ -102,8 +103,7 @@ and objects it consists of. First, move out of the tutorial area.
:: ::
@find tut#01 @find tut#01 @find tut#17
@find tut#17
This should locate the first and last rooms created by ``build.ev`` - This should locate the first and last rooms created by ``build.ev`` -
*Intro* and *Outro*. If you installed normally, everything created *Intro* and *Outro*. If you installed normally, everything created

View file

@ -255,8 +255,7 @@ query). You can easily convert between them with ``dbobj.typeclass`` and
:: ::
obj = ObjectDB.objects.get_id(1) # custom evennia manager method. This returns the typeclass. 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.
obj = ObjectDB.objects.get(1) # standard Django. Returns a Django model object.
Even more important to know for Django affectionados: Evennia's custom Even more important to know for Django affectionados: Evennia's custom
methods return *lists* where you with normal Django methods would expect methods return *lists* where you with normal Django methods would expect

View file

@ -57,25 +57,7 @@ Example of a ``TestCase`` class (inside a file ``tests.py``):
:: ::
# testing a simple funciontry: # testing a simple funciontry: # this is an optimized version only available in later Django versions from django.utils.unittest import TestCase except ImportError: # if the first fail, we use the old version from django.test import TestCase# the function we want to test from mypath import myfuncTestObj(unittest.TestCase): "This tests a function myfunc." def test_return_value(self): "test method. Makes sure return value is as expected." expected_return = "This is me being nice." actual_return = myfunc() # test self.assertEqual(expected_return, actual_return) def test_alternative_call(self): "test method. Calls with a keyword argument." expected_return = "This is me being baaaad." actual_return = myfunc(bad=True) # test self.assertEqual(expected_return, actual_return)
# this is an optimized version only available in later Django versions
from django.utils.unittest import TestCase
except ImportError:
# if the first fail, we use the old version
from django.test import TestCase# the function we want to test
from mypath import myfuncTestObj(unittest.TestCase):
"This tests a function myfunc." def test_return_value(self):
"test method. Makes sure return value is as expected."
expected_return = "This is me being nice."
actual_return = myfunc()
# test
self.assertEqual(expected_return, actual_return)
def test_alternative_call(self):
"test method. Calls with a keyword argument."
expected_return = "This is me being baaaad."
actual_return = myfunc(bad=True)
# test
self.assertEqual(expected_return, actual_return)
The above example is very simplistic, but you should get the idea. Look The above example is very simplistic, but you should get the idea. Look
at ``src/objects/tests.py`` for more realistic examples of tests. You at ``src/objects/tests.py`` for more realistic examples of tests. You

View file

@ -16,8 +16,7 @@ root directory and type:
:: ::
hg pull hg pull hg update
hg update
Assuming you've got the command line client. If you're using a graphical Assuming you've got the command line client. If you're using a graphical
client, you will probably want to navigate to the ``evennia`` directory client, you will probably want to navigate to the ``evennia`` directory
@ -77,8 +76,7 @@ database is named "Evennia":
:: ::
mysql> DROP DATABASE Evennia; mysql> DROP DATABASE Evennia; mysql> exit
mysql> exit
A Note on Schema Migration A Note on Schema Migration
-------------------------- --------------------------

View file

@ -1,10 +1,18 @@
Workshop: Default-game whitepage Workshop: Default-game whitepage
**Status Update**:*There does not seem to be any active development on
this by the original initiator (rcaskey). As far as I know there is no
active game code written apart from a Smaug area converter (how
complete?). If anyone is willing to continue with this particular idea,
they are welcome to do so. I will help out but I don't know anything
about Smaug myself. In the interim I will chalk this one down as being a
stalled project. /Griatch*
Introduction Introduction
============ ============
This is an initiative to create a "base" game system to be shipped with This is(was?) an initiative to create a "base" game system to be shipped
Evennia in a "contrib" folder. The game is an independent with Evennia in a "contrib" folder. The game is an independent
re-implementation of the basic stuff of the re-implementation of the basic stuff of the
`SMAUG <http://www.smaug.org>`_ system. No code from the original will `SMAUG <http://www.smaug.org>`_ system. No code from the original will
be used, and no licensed content will be included in the release. For be used, and no licensed content will be included in the release. For