mirror of
https://github.com/evennia/evennia.git
synced 2026-04-05 07:27:17 +02:00
Updating ReST docs.
This commit is contained in:
parent
f46a9a1280
commit
2eb5c4fc8c
39 changed files with 410 additions and 1203 deletions
|
|
@ -12,6 +12,7 @@ Installation and Early Life
|
|||
- `Starting, stopping, reloading and resetting
|
||||
Evennia <StartStopReload.html>`_
|
||||
- `Keeping your game up to date <UpdatingYourGame.html>`_
|
||||
- `Making your game available online <OnlineSetup.html>`_
|
||||
|
||||
Customizing the server
|
||||
----------------------
|
||||
|
|
|
|||
|
|
@ -113,8 +113,5 @@ are trouble.
|
|||
|
||||
::
|
||||
|
||||
<Directory "/home/<yourname>/evennia/game/web">
|
||||
Options +ExecCGI
|
||||
Allow from all
|
||||
</Directory>
|
||||
<Directory "/home/<yourname>/evennia/game/web"> Options +ExecCGI Allow from all </Directory>
|
||||
|
||||
|
|
|
|||
|
|
@ -17,9 +17,7 @@ Consider this piece of code:
|
|||
|
||||
::
|
||||
|
||||
print "before call ..."
|
||||
long_running_function()
|
||||
print "after call ..."
|
||||
print "before call ..." long_running_function() print "after call ..."
|
||||
|
||||
When run, this will print ``"before call ..."``, after which the
|
||||
``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
|
||||
print "before call ..."
|
||||
utils.run_async(long_running_function)
|
||||
print "after call ..."
|
||||
from src.utils import utils 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
|
||||
around for ``long_running_function`` to finish. Infact you will see
|
||||
|
|
@ -79,8 +74,7 @@ called automatically.
|
|||
|
||||
::
|
||||
|
||||
def at_return(r):
|
||||
print r
|
||||
def at_return(r): print r
|
||||
|
||||
- ``at_err(e)`` (the *errback*) is called if the asynchronous function
|
||||
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 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 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)
|
||||
|
||||
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
|
||||
|
|
@ -121,12 +103,19 @@ for us to see. If not we will see an error message.
|
|||
Assorted notes
|
||||
--------------
|
||||
|
||||
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).
|
||||
Note that the ``run_async`` will try to launch a separate thread behind
|
||||
the scenes. Some databases, notably our default database SQLite3, does
|
||||
*not* allow concurrent read/writes. So if you do a lot of database
|
||||
access (like saving to an Attribute) in your function, your code might
|
||||
actually run *slower* using this functionality if you are not careful.
|
||||
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
|
||||
case of Twisted, which is inherently an asynchronous server. Notably you
|
||||
|
|
|
|||
|
|
@ -29,9 +29,7 @@ To save persistent data on a Typeclassed object you normally use the
|
|||
|
||||
::
|
||||
|
||||
# saving
|
||||
rose.db.has_thorns = True # getting it back
|
||||
is_ouch = rose.db.has_thorns
|
||||
# saving 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
|
||||
that an *Attribute* is created behind the scenes and is stored in the
|
||||
|
|
@ -44,9 +42,7 @@ way:
|
|||
|
||||
::
|
||||
|
||||
# saving
|
||||
rose.ndb.has_thorns = True # getting it back
|
||||
is_ouch = rose.ndb.has_thorns
|
||||
# saving rose.ndb.has_thorns = True # getting it back is_ouch = rose.ndb.has_thorns
|
||||
|
||||
Strictly speaking, ``ndb`` has nothing to do with ``Attributes``,
|
||||
despite how similar they look. No ``Attribute`` object is created behind
|
||||
|
|
@ -68,9 +64,7 @@ Attributes like you would any normal Python property:
|
|||
|
||||
::
|
||||
|
||||
# saving
|
||||
rose.has_thorns = True# getting it back
|
||||
is_ouch = rose.has_thorns
|
||||
# saving rose.has_thorns = True# getting it back is_ouch = rose.has_thorns
|
||||
|
||||
This looks like any normal Python assignment, but calls ``db`` behind
|
||||
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
|
||||
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
|
||||
----------------------------
|
||||
|
||||
|
|
@ -151,96 +152,33 @@ Examples of valid attribute data:
|
|||
|
||||
::
|
||||
|
||||
# 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])
|
||||
# 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])
|
||||
|
||||
Example of non-supported save:
|
||||
|
||||
::
|
||||
|
||||
# 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
|
||||
# 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
|
||||
|
||||
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
|
||||
retrieve data from the database. Most of the time, things work just like
|
||||
normal Python, but there is one further exception except the one about
|
||||
storing database objects above. It is related to updating already
|
||||
existing attributes in-place. Normally this works just as it should. For
|
||||
example, you can do
|
||||
A side effect of the way Evennia stores Attributes is that Python Lists
|
||||
and Dictionaries (only )are handled by custom objects called PackedLists
|
||||
and !PackedDicts. These have the special property that they save to the
|
||||
database whenever new data gets assigned to them. This allows you to do
|
||||
things like self.db.mylist`4 <4.html>`_
|
||||
|
||||
::
|
||||
val without having to extract the mylist Attribute into a temporary
|
||||
variable first.
|
||||
|
||||
# saving data
|
||||
obj.db.mydict = "key":"test0"
|
||||
obj.db.mydict["key"] = "test1"
|
||||
obj.db.mylist = [0,1,2,3]
|
||||
obj.db.mylist[3] = "test2"
|
||||
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"
|
||||
There is however an important thing to remember. If you retrieve this
|
||||
data into another variable, e.g. ``mylist2 = obj.db.mylist``, your new
|
||||
variable will *still* be a PackedList, and if you assign things to it,
|
||||
it will save to the database! To "disconnect" it from the database
|
||||
system, you need to convert it to a normal list with mylist2
|
||||
|
||||
and it will work fine, thanks to a lot of magic happening behind the
|
||||
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.
|
||||
list(mylist2).
|
||||
|
||||
Notes
|
||||
-----
|
||||
|
|
|
|||
|
|
@ -35,21 +35,23 @@ The batch file
|
|||
--------------
|
||||
|
||||
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
|
||||
wrapped into *blocks* using a special syntax. These blocks allow the
|
||||
separating a batch file from any standard Python module is that the code
|
||||
is wrapped into *blocks* using a special syntax. These blocks allow the
|
||||
batch processor more control over execution, especially when using the
|
||||
processor's *interactive* mode, where they allow the default
|
||||
``@batchcommand`` to pause and only execute certain blocks at a time.
|
||||
processor's *interactive* mode. In interactive mode these blocs allow
|
||||
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.
|
||||
|
||||
- ``#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
|
||||
of use for for other blocks. All python code defined in a header
|
||||
block will always be inserted at the top of all ``#CODE`` blocks in
|
||||
the file. You may have more than one ``#HEADER`` block, but that is
|
||||
equivalent to having just one big one. Comments in ``#HEADER`` blocks
|
||||
are stripped out before merging.
|
||||
of use for other blocks. All python code defined in a header block
|
||||
will always be inserted at the top of all ``#CODE`` blocks in the
|
||||
file. You may have more than one ``#HEADER`` block, but that is
|
||||
equivalent to having one big one. Comments in ``#HEADER`` blocks are
|
||||
stripped out before merging.
|
||||
- ``#CODE`` as the first on a line marks the start of a *code* block.
|
||||
Code blocks contain functional python code. ``#HEADER`` blocks are
|
||||
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
|
||||
going on in the block and is displayed by the batch processor. 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.
|
||||
- A new ``#HEADER`` or ``#CODE`` (or the end of the file) ends the
|
||||
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.
|
||||
##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))
|
||||
##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))
|
||||
|
||||
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*
|
||||
actually been executed at this point!
|
||||
|
||||
To take a look at the full command you are about to run, use ``ll`` (a
|
||||
batch-processor version of ``look``).
|
||||
To take a look at the full code snippet you are about to run, use ``ll``
|
||||
(a batch-processor version of ``look``).
|
||||
|
||||
::
|
||||
|
||||
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 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.")
|
||||
|
||||
Compare with the example code given earlier. Notice how the content of
|
||||
``#HEADER`` has been pasted at the top of the ``#CODE`` block. Use
|
||||
|
|
|
|||
|
|
@ -26,13 +26,13 @@ relative to a folder you define to hold your batch files, set with
|
|||
|
||||
> @batchcommand examples.batch_cmds
|
||||
|
||||
A batch-command file contains a list of Evennia commands that you have
|
||||
previously entered. The processor will run the batch file from beginning
|
||||
to end. Note that *it will not stop if commands in it fail* (there is no
|
||||
universal way for the processor to know what a failure looks like for
|
||||
all different commands). So keep a close watch on the output, or use
|
||||
*Interactive mode* (see below) to run the file in a more controlled,
|
||||
gradual manner.
|
||||
A batch-command file contains a list of Evennia in-game commands
|
||||
separated by comments. The processor will run the batch file from
|
||||
beginning to end. Note that *it will not stop if commands in it fail*
|
||||
(there is no universal way for the processor to know what a failure
|
||||
looks like for all different commands). So keep a close watch on the
|
||||
output, or use *Interactive mode* (see below) to run the file in a more
|
||||
controlled, gradual manner.
|
||||
|
||||
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
|
||||
with line breaks.
|
||||
|
||||
Here's the rules of syntax of an ``*.ev`` file. You'll find it's really,
|
||||
really simple:
|
||||
Here are the rules of syntax of an ``*.ev`` file. You'll find it's
|
||||
really, really simple:
|
||||
|
||||
- All lines having the # (hash)-symbol *as the first one on the line*
|
||||
are considered *comments*.
|
||||
- Comments also have an actual function -- they mark the *end of the
|
||||
- All lines having the ``#`` (hash)-symbol *as the first one on the
|
||||
line* are considered *comments*. All non-comment lines are treated as
|
||||
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
|
||||
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
|
||||
(regardless, using plenty of comments is a good practice anyway).
|
||||
- Extra whitespace in a command definition are ignored. If you want a
|
||||
line break in texts, leave an empty line. Two empty lines thus means
|
||||
a new paragraph (for commands accepting formatting, that is).
|
||||
second of the two will be considered an argument to the first one.
|
||||
Besides, using plenty of comments is good practice anyway.
|
||||
- Extra whitespace in a command definition is *ignored*.
|
||||
- A completely empty line translates in to a line break in texts. Two
|
||||
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
|
||||
``game/gamesrc/commands/examples/batch_cmds.ev``.
|
||||
|
||||
::
|
||||
|
||||
#
|
||||
# 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
|
||||
# # 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
|
||||
|
||||
To test this, run ``@batchcommand`` on the file. A button will be
|
||||
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
|
||||
the processor should not be able to do all that much damage (this is the
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -124,8 +124,7 @@ and try to get the box now:
|
|||
|
||||
::
|
||||
|
||||
> get box
|
||||
You can't get that.
|
||||
> get box You can't get that.
|
||||
|
||||
Think the default error message looks dull? The ``get`` command looks
|
||||
for an `Attribute <Attributes.html>`_ named ``get_err_msg`` for
|
||||
|
|
|
|||
|
|
@ -9,32 +9,29 @@ about supported
|
|||
Databases <http://docs.djangoproject.com/en/dev/ref/databases/#ref-databases>`_
|
||||
page.
|
||||
|
||||
SQLite
|
||||
------
|
||||
SQLite3
|
||||
-------
|
||||
|
||||
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
|
||||
server process is needed, the administrative overhead is tiny (as is
|
||||
installs it will probably be more than adequate or even the best choice.
|
||||
No server process is needed, the administrative overhead is tiny (as is
|
||||
resource consumption). The database will appear as a simple file
|
||||
(``game/evennia.db3``). SQLite is excellent for development and easy
|
||||
testing. The database is however hampered in speed by not allowing
|
||||
concurrent reads. For a full production game with many users accessing
|
||||
the database, a more fully featured database engine (MySQL, Postgres
|
||||
etc) is probably better.
|
||||
(``game/evennia.db3``) and since we run SQLite as an in-memory process
|
||||
without any socket overhead, it might well be faster than Postgres/MySQL
|
||||
unless your database is huge.
|
||||
|
||||
**Note:** If you run Windows and for some reason need to use a
|
||||
third-party web server like Apache rather than Evennia's internal web
|
||||
server, sqlite is probably also not be the best choice. This is due to
|
||||
the possibility of clashes with file-locking of the database file under
|
||||
Windows.
|
||||
**Note:** If you for some reason need to use a third-party web server
|
||||
like Apache rather than Evennia's internal web server, SQLite is
|
||||
probably not be the best choice. This is due to the possibility of
|
||||
clashes with file-locking when using SQLite from more than one process.
|
||||
|
||||
Postgres
|
||||
--------
|
||||
|
||||
This is Django's recommended database engine, usable for all sites
|
||||
aspiring to grow to a larger size. While not as fast as SQLite for
|
||||
simple purposes, it will scale infinitely better than SQLite, especially
|
||||
if your game has an extensive web presence.
|
||||
This is Django's recommended database engine, While not as fast as
|
||||
SQLite for normal usage, it will scale better than SQLite, especially if
|
||||
your game has an very large database and/or extensive web presence
|
||||
through a separate server process.
|
||||
|
||||
**Warning:** Postgres has issues with Evennia on some installs at the
|
||||
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 **may** be slightly faster than Postgres depending on your setup
|
||||
and software versions involved. Older versions of MySQL had some
|
||||
MySQL *may* be slightly faster than Postgres depending on your setup and
|
||||
software versions involved. Older versions of MySQL had some
|
||||
peculiarities though, so check out Django's `Notes about supported
|
||||
Databases <http://docs.djangoproject.com/en/dev/ref/databases/#ref-databases>`_
|
||||
to make sure you use the correct version.
|
||||
|
|
|
|||
|
|
@ -26,8 +26,7 @@ mark colour:
|
|||
|
||||
::
|
||||
|
||||
This is a %crRed text%cn This is normal text again.
|
||||
%cRThis text has red background%cn this is normal text.
|
||||
This is a %crRed text%cn This is normal text again. %cRThis text has red background%cn this is normal text.
|
||||
|
||||
``%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).
|
||||
|
|
|
|||
|
|
@ -15,9 +15,7 @@ of the look command, followed by the prompt. As an example:
|
|||
|
||||
::
|
||||
|
||||
> look
|
||||
You see nothing special.
|
||||
HP:10, SP:20, MP: 5
|
||||
> look You see nothing special. HP:10, SP:20, MP: 5
|
||||
|
||||
MUD clients can be set to detect prompts like this and display them in
|
||||
various client-specific ways.
|
||||
|
|
@ -33,12 +31,7 @@ administration for example).
|
|||
|
||||
::
|
||||
|
||||
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))
|
||||
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))
|
||||
|
||||
Prompt on the same line
|
||||
-----------------------
|
||||
|
|
@ -48,8 +41,7 @@ return of every command, on the same line:
|
|||
|
||||
::
|
||||
|
||||
> look
|
||||
HP: 10, SP:20, MP:5 -- You see nothing special.
|
||||
> look HP: 10, SP:20, MP:5 -- You see nothing special.
|
||||
|
||||
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
|
||||
|
|
@ -58,9 +50,7 @@ before* the function return:
|
|||
|
||||
::
|
||||
|
||||
> look
|
||||
HP:10, SP:20, MP: 5
|
||||
You see nothing special.
|
||||
> look HP:10, SP:20, MP: 5 You see nothing special.
|
||||
|
||||
... 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
|
||||
|
|
@ -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``,
|
||||
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):
|
||||
|
||||
# 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)
|
||||
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)
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -111,24 +111,46 @@ Defining your own command classes
|
|||
Beyond the properties Evennia always assigns to the command at runtime
|
||||
(listed above), your job is to define the following class properties:
|
||||
|
||||
- ``key`` - the identifier for the command, like ``look``. This should
|
||||
(ideally) be unique. it can be more than one word long in a string,
|
||||
like "press button". Maximum number of space-separated words that can
|
||||
be part of a command name is given by ``settings.CMD_MAXLEN``.
|
||||
- ``aliases`` (optional) - a list of alternate names for the command
|
||||
(``["l", "glance", "see"]``). Same name rules as for ``key`` applies.
|
||||
- ``locks`` - a `lock definition <Locks.html>`_, usually on the form
|
||||
``cmd:<lockfuncs>``. Locks is a rather big topic, so until you learn
|
||||
more about locks, stick to giving the lockstring ``"cmd:all()"`` to
|
||||
make the command available to everyone.
|
||||
- ``help_category`` (optional) - setting this helps to structure the
|
||||
auto-help into categories. If none is set, this will be set to
|
||||
- ``key`` (string) - the identifier for the command, like ``look``.
|
||||
This should (ideally) be unique. it can be more than one word long in
|
||||
a string, like "press button". Maximum number of space-separated
|
||||
words that can be part of a command name is given by
|
||||
``settings.CMD_MAXLEN``.
|
||||
- ``aliases`` (optional list) - a list of alternate names for the
|
||||
command (``["l", "glance", "see"]``). Same name rules as for ``key``
|
||||
applies.
|
||||
- ``locks`` (string) - a `lock definition <Locks.html>`_, usually on
|
||||
the form ``cmd:<lockfuncs>``. Locks is a rather big topic, so until
|
||||
you learn more about locks, stick to giving the lockstring
|
||||
``"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*.
|
||||
- ``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
|
||||
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
|
||||
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
|
||||
``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):
|
||||
"""
|
||||
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'...")
|
||||
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'...")
|
||||
|
||||
The power of having commands as classes and to separate ``parse()`` and
|
||||
``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 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 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())
|
||||
|
||||
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
|
||||
|
|
@ -254,10 +242,7 @@ as if you added them line by line:
|
|||
|
||||
::
|
||||
|
||||
at_cmdset_creation():
|
||||
...
|
||||
self.add(AdditionalCmdSet) # adds all command from this set
|
||||
...
|
||||
at_cmdset_creation(): ... self.add(AdditionalCmdSet) # adds all command from this set ...
|
||||
|
||||
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
|
||||
|
|
@ -320,19 +305,7 @@ look:
|
|||
|
||||
::
|
||||
|
||||
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)
|
||||
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)
|
||||
|
||||
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
|
||||
|
|
@ -349,10 +322,7 @@ This is what we have now:
|
|||
::
|
||||
|
||||
from game.gamesrc.commands.basecmdset import CmdSet
|
||||
from game.gamesrc.commands import mycommandclass MyCmdSet(CmdSet):
|
||||
|
||||
key = "MyCmdSet" def at_cmdset_creation(self):
|
||||
self.add(mycommand.MyCommand())
|
||||
from game.gamesrc.commands import mycommandclass MyCmdSet(CmdSet): key = "MyCmdSet" def at_cmdset_creation(self): self.add(mycommand.MyCommand())
|
||||
|
||||
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
|
||||
|
|
@ -416,15 +386,7 @@ class and you will in fact append it to the existing command set.
|
|||
|
||||
::
|
||||
|
||||
# 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())
|
||||
# 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())
|
||||
|
||||
Again, you need to run the ``@reload`` command to make these changes
|
||||
available.
|
||||
|
|
@ -513,8 +475,7 @@ Same-key commands are merged by priority.
|
|||
|
||||
::
|
||||
|
||||
# Union
|
||||
A1,A2 + B1,B2,B3,B4 = A1,A2,B3,B4
|
||||
# Union A1,A2 + B1,B2,B3,B4 = A1,A2,B3,B4
|
||||
|
||||
**Intersect** - Only commands found in *both* cmdsets (i.e. which have
|
||||
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
|
||||
A1,A3,A5 + B1,B2,B4,B5 = A1,A5
|
||||
# Intersect A1,A3,A5 + B1,B2,B4,B5 = A1,A5
|
||||
|
||||
**Replace** - The commands of the higher-prio cmdset completely replaces
|
||||
the lower-priority cmdset's commands, regardless of if same-key commands
|
||||
|
|
@ -531,8 +491,7 @@ exist or not.
|
|||
|
||||
::
|
||||
|
||||
# Replace
|
||||
A1,A3 + B1,B2,B4,B5 = A1,A3
|
||||
# Replace A1,A3 + B1,B2,B4,B5 = A1,A3
|
||||
|
||||
**Remove** - The high-priority command sets removes same-key commands
|
||||
from the lower-priority cmdset. They are not replaced with anything, so
|
||||
|
|
@ -541,8 +500,7 @@ high-prio one as a template.
|
|||
|
||||
::
|
||||
|
||||
# Remove
|
||||
A1,A3 + B1,B2,B3,B4,B5 = B2,B4,B5
|
||||
# Remove A1,A3 + B1,B2,B3,B4,B5 = B2,B4,B5
|
||||
|
||||
Besides ``priority`` and ``mergetype``, a command set also takes a few
|
||||
other variables to control how they merge:
|
||||
|
|
@ -569,17 +527,7 @@ More advanced cmdset example:
|
|||
|
||||
::
|
||||
|
||||
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())
|
||||
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())
|
||||
|
||||
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 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 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!")
|
||||
|
||||
Exits
|
||||
-----
|
||||
|
|
|
|||
|
|
@ -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
|
||||
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)
|
||||
# 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)
|
||||
|
||||
As a more advanced note, sending text to channels is a "special
|
||||
exception" as far as commands are concerned, and you may completely
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
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.
|
||||
==============================================================
|
||||
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. ==============================================================
|
||||
|
||||
Effective, but not very exciting. You will most likely want to change
|
||||
this to be more unique for your game.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
typos is a great help. To edit the wiki yourself you need contributor
|
||||
access. Otherwise, it goes a long way just pointing out wiki errors so
|
||||
devs can fix them.
|
||||
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
|
||||
-------------------------------------------------
|
||||
|
|
@ -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
|
||||
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
|
||||
people's opinions or make an online backup), *push* your local code
|
||||
up to your online repository with Mercurial.
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -74,6 +74,7 @@ Programming Evennia
|
|||
Game implementation hints
|
||||
-------------------------
|
||||
|
||||
- `Planning your own Evennia game <GamePlanning.html>`_
|
||||
- `Creating a Zoning system <Zones.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.*
|
||||
|
||||
- `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
|
||||
Evennia (pre aug2010)
|
||||
Evennia (implemented in aug2010)
|
||||
|
||||
|
|
|
|||
|
|
@ -76,16 +76,7 @@ the server.
|
|||
|
||||
game/
|
||||
evennia.py
|
||||
manage.py gamesrc/
|
||||
commands/
|
||||
examples/
|
||||
scripts/
|
||||
examples/
|
||||
objects/
|
||||
examples/
|
||||
world/
|
||||
examples/
|
||||
conf/
|
||||
manage.py gamesrc/ commands/ examples/ scripts/ examples/ objects/ examples/ world/ examples/ conf/
|
||||
|
||||
``game/gamesrc/``
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
|
@ -151,12 +142,12 @@ syntax.
|
|||
``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
|
||||
in order to allow you to do this without modifying the server itself, it
|
||||
imports files from this directory. Modifying these are optionally and
|
||||
you can usually change a variable in ``game/settings.py`` to change
|
||||
which module Evennia is looking for. There are dummy example files in
|
||||
imports files from this directory. Modifying these are optional and you
|
||||
can usually change a variable in ``game/settings.py`` to change exactly
|
||||
which module Evennia actually uses. There are dummy example files in
|
||||
here, read their headers for usage instructions.
|
||||
|
||||
The ``src/`` directory
|
||||
|
|
@ -171,17 +162,7 @@ bugs or features missing, file a bug report or send us a message.
|
|||
::
|
||||
|
||||
src/
|
||||
settings_defaults.py commands/
|
||||
comms/
|
||||
help/
|
||||
objects/
|
||||
locks/
|
||||
players/
|
||||
scripts/
|
||||
server/
|
||||
typeclasses/
|
||||
utils/
|
||||
web/
|
||||
settings_defaults.py commands/ comms/ help/ objects/ locks/ players/ scripts/ server/ typeclasses/ utils/ web/
|
||||
|
||||
Most of the folders in ``src/`` are technically "Django apps",
|
||||
identified by containing a file ``models.py`` and usually
|
||||
|
|
|
|||
|
|
@ -116,9 +116,7 @@ Example of new command definition:
|
|||
|
||||
::
|
||||
|
||||
class CmdTest(Command):
|
||||
def func(self):
|
||||
self.caller.msg("This is the test!")
|
||||
class CmdTest(Command): def func(self): self.caller.msg("This is the test!")
|
||||
|
||||
Events + States -> Scripts
|
||||
--------------------------
|
||||
|
|
@ -264,16 +262,14 @@ just do:
|
|||
|
||||
::
|
||||
|
||||
obj.db.attr = value
|
||||
value = obj.db.attr
|
||||
obj.db.attr = value value = obj.db.attr
|
||||
|
||||
And for storing something non-persistently (stored only until the server
|
||||
reboots) you can just do
|
||||
|
||||
::
|
||||
|
||||
obj.attr = value
|
||||
value = obj.attr
|
||||
obj.attr = value value = obj.attr
|
||||
|
||||
The last example may sound trivial, but it's actually impossible to do
|
||||
in trunk since django objects are not guaranteed to remain the same
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ created for you automatically if you use the defaults).
|
|||
|
||||
Using the full power of Python throughout the server offers some
|
||||
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
|
||||
script the game in the same high-level language that you code it in
|
||||
allows for very powerful and custom game implementations indeed.
|
||||
|
|
|
|||
|
|
@ -10,8 +10,7 @@ entrust to just anybody.
|
|||
|
||||
::
|
||||
|
||||
@py 1+2
|
||||
<<< 3
|
||||
@py 1+2 <<< 3
|
||||
|
||||
Available variables
|
||||
-------------------
|
||||
|
|
@ -37,8 +36,7 @@ found in ``src/utils/utils.py``:
|
|||
|
||||
::
|
||||
|
||||
@py from src.utils import utils; utils.time_format(33333)
|
||||
<<< Done.
|
||||
@py from src.utils import utils; utils.time_format(33333) <<< Done.
|
||||
|
||||
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
|
||||
|
|
@ -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))
|
||||
09:15
|
||||
<<< Done.
|
||||
@py from src.utils import utils; self.msg(utils.time_format(33333)) 09:15 <<< Done.
|
||||
|
||||
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
|
||||
|
|
@ -67,10 +63,7 @@ Locating an object is best done using ``self.search()``:
|
|||
|
||||
::
|
||||
|
||||
@py self.search("red_ball")
|
||||
<<< Ball @py self.search("red_ball").db.color = "red"
|
||||
<<< Done. @py self.search("red_ball").db.color
|
||||
<<< red
|
||||
@py self.search("red_ball") <<< 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
|
||||
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"))
|
||||
<<< [<src.utils.gametime.GameTime object at 0x852be2c>]
|
||||
@py from src.utils import search; self.msg(search.scripts("sys_game_time")) <<< [<src.utils.gametime.GameTime object at 0x852be2c>]
|
||||
|
||||
You can also use the database model managers directly (accessible
|
||||
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")
|
||||
<<< [<src.utils.gametime.GameTime object at 0x852be2c>]
|
||||
@py ScriptDB.objects.script_search("sys_game_time") <<< [<src.utils.gametime.GameTime object at 0x852be2c>]
|
||||
|
||||
(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
|
||||
|
|
@ -101,8 +92,7 @@ contents of the database using normal Django query operations:
|
|||
|
||||
::
|
||||
|
||||
@py ConfigValue.objects.all()
|
||||
<<< [<ConfigValue: default_home]>, <ConfigValue:site_name>, ...]
|
||||
@py ConfigValue.objects.all() <<< [<ConfigValue: default_home]>, <ConfigValue:site_name>, ...]
|
||||
|
||||
In doing so however, keep in mind the difference between `Typeclasses
|
||||
and Database Objects <Typeclasses.html>`_: Using the search commands in
|
||||
|
|
@ -114,13 +104,7 @@ most situations.
|
|||
|
||||
::
|
||||
|
||||
# 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'>
|
||||
# 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'>
|
||||
|
||||
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
|
||||
...In [1]: from src.objects.models import ObjectDB
|
||||
In [2]: ObjectDB.objects.all()
|
||||
Out[3]: [<ObjectDB: Harry>, <ObjectDB: Limbo>, ...]
|
||||
$ 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>, ...]
|
||||
|
||||
|
|
|
|||
|
|
@ -67,7 +67,8 @@ Twisted also requires:
|
|||
**Django** (http://www.djangoproject.com)
|
||||
|
||||
- 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:
|
||||
|
||||
|
|
@ -129,17 +130,19 @@ or all of these instead:
|
|||
``easy_install`` or ``pip`` like Linux users do. There are however
|
||||
reports that you might need to get the
|
||||
`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
|
||||
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
|
||||
`ActivePython <http://www.activestate.com/activepython/downloads>`_
|
||||
instead of the usual Python. If ActivePython is installed, you can use
|
||||
`pypm <http://docs.activestate.com/activepython/2.6/pypm.html>`_ in the
|
||||
same manner as ``easy_install``/``pip`` above. This *greatly* simplifies
|
||||
getting started on Windows since that platform is otherwise missing many
|
||||
of the background developer systems that Linux users take for granted.
|
||||
getting started on Windows since that platform is by default missing
|
||||
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
|
||||
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`` is the chemical abbreviation of mercury, hence the use of ``hg``
|
||||
for ``mercurial``)
|
||||
(Mercurial is abbreviated ``hg`` since this is the chemical symbol for
|
||||
mercury).
|
||||
|
||||
In the future, you just do
|
||||
|
||||
::
|
||||
|
||||
hg pull
|
||||
hg update
|
||||
hg pull hg update
|
||||
|
||||
from your ``evennia/`` directory to obtain the latest updates.
|
||||
|
||||
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
|
||||
-----------------------------
|
||||
|
|
@ -324,10 +328,7 @@ virtual environment in here.
|
|||
|
||||
::
|
||||
|
||||
# for Linux/Unix:
|
||||
source bin/activate
|
||||
# for Windows:
|
||||
<path_to_this_place>\Scripts\activate.bat
|
||||
# for Linux/Unix: source bin/activate # for Windows: <path_to_this_place>\Scripts\activate.bat
|
||||
|
||||
The virtual environment within our *mudenv* folder is now active. Next
|
||||
we get all the requirements with *pip*, which is included with
|
||||
|
|
|
|||
|
|
@ -49,16 +49,7 @@ Example (from a module with command definitions):
|
|||
|
||||
::
|
||||
|
||||
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"
|
||||
...
|
||||
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" ...
|
||||
|
||||
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
|
||||
|
|
@ -98,10 +89,7 @@ You can create new help entries in code by using
|
|||
|
||||
::
|
||||
|
||||
from src.utils import create
|
||||
entry = create.create_help_entry("emote",
|
||||
"Emoting is important because ...",
|
||||
category="Roleplaying", locks="view:all()"):
|
||||
from src.utils import create 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
|
||||
``@sethelp`` command to add and modify help entries.
|
||||
|
|
|
|||
|
|
@ -6,17 +6,18 @@ How to *get* Help
|
|||
If you cannot find what you are looking for in the `online
|
||||
documentation <Index.html>`_, here's what to do:
|
||||
|
||||
- If you don't understand a concept or think the docs are not clear
|
||||
enough, fill in our quick little
|
||||
"https://docs.google.com/spreadsheet/viewform?hl
|
||||
- If you think the documentation is not clear enough, fill in our quick
|
||||
little "https://docs.google.com/spreadsheet/viewform?hl
|
||||
|
||||
en*US&formkey*
|
||||
|
||||
dGN0VlJXMWpCT3VHaHpscDEzY1RoZGc6MQ#gid
|
||||
======================================
|
||||
|
||||
0.html">online form and say so - maybe the docs need to be improved
|
||||
or a tutorial added!
|
||||
0.html">online form and say so (no login required). Maybe the docs
|
||||
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
|
||||
a bug, go to the `issue
|
||||
|
|
|
|||
|
|
@ -85,8 +85,7 @@ Write something in the Evennia channel *irc*.
|
|||
|
||||
::
|
||||
|
||||
irc Hello, World!
|
||||
[irc] Anna: Hello, World!
|
||||
irc Hello, World! [irc] Anna: Hello, World!
|
||||
|
||||
If you are viewing your IRC channel with a separate IRC client you
|
||||
should see your text appearing there, spoken by the bot:
|
||||
|
|
|
|||
|
|
@ -25,8 +25,7 @@ your ``game/settings.py`` file:
|
|||
|
||||
::
|
||||
|
||||
USE_I18N = True
|
||||
LANGUAGE_CODE = 'en'
|
||||
USE_I18N = True LANGUAGE_CODE = 'en'
|
||||
|
||||
Here ``'en'`` should be changed to the abbreviation for one of the
|
||||
supported languages found in ``locale/``. Restart the server to activate
|
||||
|
|
|
|||
|
|
@ -54,9 +54,7 @@ how it would (and do) look from inside the ``@delete`` command:
|
|||
|
||||
::
|
||||
|
||||
if not obj.access(accessing_obj, 'delete'):
|
||||
accessing_obj.msg("Sorry, you may not delete that.")
|
||||
return
|
||||
if not obj.access(accessing_obj, 'delete'): accessing_obj.msg("Sorry, you may not delete that.") return
|
||||
|
||||
Defining locks
|
||||
--------------
|
||||
|
|
@ -69,9 +67,7 @@ Here are some examples of lock strings:
|
|||
|
||||
::
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
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):
|
||||
if args:
|
||||
wanted_id = args[0]
|
||||
return accessing_obj.id == wanted_id
|
||||
return False
|
||||
# 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
|
||||
|
||||
(Using the ``*`` and ``**`` syntax causes Python to magically put all
|
||||
extra arguments into a list ``args`` and all keyword arguments into a
|
||||
|
|
@ -250,11 +242,7 @@ default permission hierarchy is as follows:
|
|||
|
||||
::
|
||||
|
||||
Immortals
|
||||
Wizards
|
||||
Builders
|
||||
PlayerHelpers
|
||||
Players # this is what all new Players start with by default
|
||||
Immortals 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()``
|
||||
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"]
|
||||
obj2.locks.add("enter:perm_above(Players) and perm(cool_guy)")obj2.access(obj1, "enter") # this returns True!
|
||||
obj1.permissions = ["Builders", "cool_guy"] obj2.locks.add("enter:perm_above(Players) and perm(cool_guy)")obj2.access(obj1, "enter") # this returns True!
|
||||
|
||||
Superusers
|
||||
----------
|
||||
|
|
@ -339,8 +326,7 @@ other is an `Object <Objects.html>`_ called ``box``.
|
|||
|
||||
::
|
||||
|
||||
> @create/drop box
|
||||
> @desc box = "This is a very big and heavy box."
|
||||
> @create/drop 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
|
||||
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 obj.db.get_err_msg:
|
||||
caller.msg(obj.db.get_err_msg)
|
||||
else:
|
||||
caller.msg("You can't get that.")
|
||||
return
|
||||
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
|
||||
|
||||
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
|
||||
|
|
@ -398,9 +379,7 @@ like this:
|
|||
|
||||
::
|
||||
|
||||
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...
|
||||
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...
|
||||
|
||||
On Django's permission system
|
||||
=============================
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
||||
|
|
@ -83,13 +83,7 @@ checking, searches and conversion.
|
|||
|
||||
::
|
||||
|
||||
# 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")
|
||||
# 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")
|
||||
|
||||
In a command definition you can reach the nick handler through
|
||||
``self.caller.nicks``. See the ``nick`` command in
|
||||
|
|
|
|||
|
|
@ -19,14 +19,7 @@ Here's how to define a new Object typeclass in code:
|
|||
|
||||
::
|
||||
|
||||
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."
|
||||
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."
|
||||
|
||||
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
|
||||
|
|
@ -41,8 +34,7 @@ To create a new object in code, use the method
|
|||
|
||||
::
|
||||
|
||||
from src.utils import create
|
||||
new_rose = create.create_object("game.gamesrc.objects.flowers.Rose", key="MyRose")
|
||||
from src.utils import create 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 -
|
||||
``create.create_object`` is a powerful function that should be used for
|
||||
|
|
|
|||
|
|
@ -41,19 +41,7 @@ Here's how to define a new Player typeclass in code:
|
|||
|
||||
::
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
There is no pre-made folder in ``game/gamesrc`` to store custom player
|
||||
typeclasses. Either make your own folder or store it in
|
||||
|
|
|
|||
|
|
@ -34,10 +34,7 @@ inheriting from ``game.gamesrc.objects.baseobjecs.Character``.
|
|||
|
||||
::
|
||||
|
||||
from game.gamesrc.objects.baseobjects import Characterclass ColourableCharacter(Character):
|
||||
at_object_creation(self):
|
||||
# set a colour config value
|
||||
self.db.config_colour = True
|
||||
from game.gamesrc.objects.baseobjects import Characterclass ColourableCharacter(Character): 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>`_.
|
||||
|
||||
|
|
@ -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
|
||||
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):
|
||||
"our custom msg()"
|
||||
if not self.db.config_colour:
|
||||
message = ansi.parse_ansi(message, strip_ansi=True)
|
||||
self.dbobj.msg(message, from_obj, data)
|
||||
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)
|
||||
|
||||
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
|
||||
|
|
@ -93,7 +86,13 @@ Since we put this custom ``msg()`` in our typeclass
|
|||
the default method on ``ObjectDB`` (which we now instead call manually).
|
||||
|
||||
There we go! Just flip the attribute ``config_colour`` to False and your
|
||||
users will not see any colour.
|
||||
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
|
||||
----------------------------
|
||||
|
|
@ -107,22 +106,7 @@ for configuration down the line).
|
|||
|
||||
::
|
||||
|
||||
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)
|
||||
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)
|
||||
|
||||
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
|
||||
|
|
@ -130,14 +114,7 @@ default command set. Easiest is to add it to the end of
|
|||
|
||||
::
|
||||
|
||||
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())
|
||||
from game.gamesrc.commands import configcmds class DefaultCmdSet(cmdset_default.DefaultCmdSet): key = "DefaultMUX" def at_cmdset_creation(self): super(DefaultCmdSet, self).at_cmdset_creation() self.add(configcmds.ConfigColourCmd())
|
||||
|
||||
When adding a new command to a cmdset like this you need to run the
|
||||
``@reload`` command (or reboot the server). From here on out, your users
|
||||
|
|
|
|||
|
|
@ -72,11 +72,7 @@ care of all initialization and startup of the script for you.
|
|||
|
||||
::
|
||||
|
||||
# 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)
|
||||
# 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)
|
||||
|
||||
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
|
||||
|
|
@ -88,9 +84,7 @@ Just don't supply an object to store it on.
|
|||
|
||||
::
|
||||
|
||||
# adding a global script
|
||||
from src.utils.create import create_script
|
||||
create_script("game.gamesrc.scripts.globals.MyGlobalEconomy", key="economy", obj=None)
|
||||
# adding a global script 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``
|
||||
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
|
||||
create_script('game.gamesrc.scripts.weather.Weather', obj=myroom)
|
||||
from src.utils.create import create_script create_script('game.gamesrc.scripts.weather.Weather', obj=myroom)
|
||||
|
||||
Or, from in-game, use the ``@script`` command:
|
||||
|
||||
|
|
|
|||
|
|
@ -160,20 +160,7 @@ create a module there with the following functions:
|
|||
|
||||
::
|
||||
|
||||
# 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")
|
||||
# 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")
|
||||
|
||||
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
|
||||
|
|
@ -181,21 +168,7 @@ the portal protocol:
|
|||
|
||||
::
|
||||
|
||||
# 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)
|
||||
# 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)
|
||||
|
||||
The Server will properly accept this and call the relevant functions to
|
||||
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):
|
||||
# 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)
|
||||
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)
|
||||
|
||||
As seen, ``oob_data`` takes the values and formats into a form the
|
||||
protocol understands before sending it off.
|
||||
|
|
|
|||
|
|
@ -51,8 +51,7 @@ retrieved when emitting:
|
|||
|
||||
::
|
||||
|
||||
&HELLO_VALUE.D me=Hello World
|
||||
&HELLO_WORLD.C me=$hello:@pemit %#=[v(HELLO_VALUE.D)]
|
||||
&HELLO_VALUE.D me=Hello World &HELLO_WORLD.C me=$hello:@pemit %#=[v(HELLO_VALUE.D)]
|
||||
|
||||
The v() function returns the HELLO\_VALUE.D attribute on the object that
|
||||
the command resides (``me``, which is yourself in this case). This
|
||||
|
|
|
|||
|
|
@ -43,8 +43,7 @@ You can also start the two components one at a time.
|
|||
|
||||
::
|
||||
|
||||
python evennia.py start server
|
||||
python evennia.py start portal
|
||||
python evennia.py start server python evennia.py start portal
|
||||
|
||||
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.
|
||||
|
|
@ -85,8 +84,7 @@ A reset is equivalent to
|
|||
|
||||
::
|
||||
|
||||
python evennia.py stop server
|
||||
python evennia.py start server
|
||||
python evennia.py stop server python evennia.py start server
|
||||
|
||||
Shutting down
|
||||
-------------
|
||||
|
|
|
|||
|
|
@ -56,9 +56,10 @@ see what happens in detail.
|
|||
|
||||
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
|
||||
superuser and will thus not work as normal. Create a new, non-superuser
|
||||
character for playing instead. As superuser you can of course examine
|
||||
things "under the hood" later if you want.
|
||||
superuser and will thus not work as normal. Log out, then reconnect.
|
||||
From the login screen, create a new, non-superuser character for playing
|
||||
instead. As superuser you can of course examine things "under the hood"
|
||||
later if you want.
|
||||
|
||||
Gameplay
|
||||
--------
|
||||
|
|
@ -102,8 +103,7 @@ and objects it consists of. First, move out of the tutorial area.
|
|||
|
||||
::
|
||||
|
||||
@find tut#01
|
||||
@find tut#17
|
||||
@find tut#01 @find tut#17
|
||||
|
||||
This should locate the first and last rooms created by ``build.ev`` -
|
||||
*Intro* and *Outro*. If you installed normally, everything created
|
||||
|
|
|
|||
|
|
@ -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(1) # standard Django. Returns a Django model object.
|
||||
obj = ObjectDB.objects.get_id(1) # custom evennia manager method. This returns the typeclass. obj = ObjectDB.objects.get(1) # standard Django. Returns a Django model object.
|
||||
|
||||
Even more important to know for Django affectionados: Evennia's custom
|
||||
methods return *lists* where you with normal Django methods would expect
|
||||
|
|
|
|||
|
|
@ -57,25 +57,7 @@ Example of a ``TestCase`` class (inside a file ``tests.py``):
|
|||
|
||||
::
|
||||
|
||||
# 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)
|
||||
# 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)
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -16,8 +16,7 @@ root directory and type:
|
|||
|
||||
::
|
||||
|
||||
hg pull
|
||||
hg update
|
||||
hg pull hg update
|
||||
|
||||
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
|
||||
|
|
@ -77,8 +76,7 @@ database is named "Evennia":
|
|||
|
||||
::
|
||||
|
||||
mysql> DROP DATABASE Evennia;
|
||||
mysql> exit
|
||||
mysql> DROP DATABASE Evennia; mysql> exit
|
||||
|
||||
A Note on Schema Migration
|
||||
--------------------------
|
||||
|
|
|
|||
|
|
@ -1,10 +1,18 @@
|
|||
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
|
||||
============
|
||||
|
||||
This is an initiative to create a "base" game system to be shipped with
|
||||
Evennia in a "contrib" folder. The game is an independent
|
||||
This is(was?) an initiative to create a "base" game system to be shipped
|
||||
with Evennia in a "contrib" folder. The game is an independent
|
||||
re-implementation of the basic stuff of the
|
||||
`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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue