Patched wiki2html to give correct source code snippet output.

This commit is contained in:
Griatch 2011-09-11 12:17:33 +02:00
parent 22b23be095
commit eae89eabc0
31 changed files with 1028 additions and 175 deletions

View file

@ -113,5 +113,8 @@ 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>

View file

@ -17,7 +17,9 @@ 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
@ -43,7 +45,10 @@ 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
@ -74,7 +79,8 @@ 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
@ -93,7 +99,19 @@ 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

View file

@ -29,7 +29,9 @@ assign data to it. Let's try to save some data to a *Rose* (an
::
# 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
Whether this data is saved *persistently* to the database or not (i.e.
if it survives a server reboot) depends on the setting of the variable
@ -41,7 +43,9 @@ of ``FULL_PERSISTENCE``, use the ``db`` (!DataBase) interface.
::
# 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 creates a new ``Attribute`` object and links it uniquely to
``rose``. Using ``db`` ``will`` always save data to the database.
@ -51,7 +55,9 @@ It works in the same 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
(Using ``ndb`` like this will **NEVER** use the database.)
@ -156,7 +162,18 @@ 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 will stored and returned as a list [1,2,3,4,5]! obj.db.test6 = (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 will stored and returned as a list [1,2,3,4,5]!
obj.db.test6 = (1,2,3,4,5)
Notes
-----

View file

@ -75,7 +75,13 @@ 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.
@ -137,7 +143,11 @@ 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

View file

@ -60,7 +60,21 @@ Below is a version of the example file found in
::
# # 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 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

View file

@ -101,7 +101,8 @@ you log in as another user than #1 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

View file

@ -26,7 +26,8 @@ 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).

View file

@ -15,7 +15,9 @@ 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.
@ -31,7 +33,12 @@ 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
-----------------------
@ -41,7 +48,8 @@ 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
@ -50,7 +58,9 @@ 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
@ -63,7 +73,8 @@ 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
@ -74,7 +85,13 @@ 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

View file

@ -124,17 +124,42 @@ to take one example.
pre-parsed input to actually do whatever the command is supposed to do.
This is the main body of the command.
Finally, you should always make an informative ```__doc__``
Finally, you should always make an informative `doc
string <http://www.python.org/dev/peps/pep-0257/#what-is-a-docstring>`_
at the top of your class. This string is dynamically read by the `Help
system <HelpSystem.html>`_ to create the help entry for this command.
You should decide on a way to format your help and stick to that.
(``__doc__``) at the top of your class. This string is dynamically read
by the `Help system <HelpSystem.html>`_ to create the help entry for
this command. You should decide on a way to format your help and stick
to that.
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
@ -191,7 +216,17 @@ 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
@ -199,7 +234,10 @@ 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
@ -261,7 +299,19 @@ 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
@ -278,7 +328,10 @@ 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
@ -342,7 +395,15 @@ 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.
@ -431,7 +492,8 @@ 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
@ -439,7 +501,8 @@ 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
@ -447,7 +510,8 @@ 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
@ -456,7 +520,8 @@ 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:
@ -483,7 +548,17 @@ 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
---------------
@ -534,7 +609,12 @@ 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!")
How commands actually work
--------------------------

View file

@ -80,7 +80,12 @@ 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

View file

@ -8,7 +8,11 @@ tells you how to connect.
::
==============================================================
Welcome to Evennia, version SVN-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 SVN-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.

File diff suppressed because it is too large Load diff

View file

@ -61,7 +61,24 @@ the server.
game/
evennia.py
manage.py gamesrc/ commands/ basecommand.py basecmdset.py examples/ cmdset_red_button.py scripts/ basescript.py examples/ red_button_sripts.py objects/ baseobjects.py examples/ red_button.py world/ examples/ batch_cmds.ev batch_code.py
manage.py gamesrc/
commands/
basecommand.py
basecmdset.py
examples/
cmdset_red_button.py
scripts/
basescript.py
examples/
red_button_sripts.py
objects/
baseobjects.py
examples/
red_button.py
world/
examples/
batch_cmds.ev
batch_code.py
``game/gamesrc/``
~~~~~~~~~~~~~~~~~
@ -137,7 +154,17 @@ 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

View file

@ -116,7 +116,9 @@ 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
--------------------------
@ -262,14 +264,16 @@ 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

View file

@ -10,7 +10,8 @@ entrust to just anybody.
::
@py 1+2 <<< 3
@py 1+2
<<< 3
Available variables
-------------------
@ -36,7 +37,8 @@ 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
@ -46,7 +48,9 @@ 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
@ -63,7 +67,10 @@ 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
@ -72,7 +79,8 @@ 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
@ -81,7 +89,8 @@ 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
@ -92,7 +101,8 @@ 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
@ -104,7 +114,13 @@ 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
========================================
@ -129,5 +145,8 @@ 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>, ...]

View file

@ -288,14 +288,18 @@ isolated new folder *mudenv*:
::
python virtualenv mudenv --no-site-packages cd mudenv
python virtualenv mudenv --no-site-packages
cd mudenv
Now we should be in our new directory *mudenv*. Next we activate the
virtual environment in here.
::
# for Linux: source bin/activate # for Windows: <path_to_this_place>\bin\activate.bat
# for Linux:
source bin/activate
# for Windows:
<path_to_this_place>\bin\activate.bat
In here you can play around and install python packages of any version
without affecting your normal system installation at all. Next we get

View file

@ -48,7 +48,16 @@ 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"
...
So the text at the very top of the command class definition is the
class' ``__doc__``-string and what will be shown to users looking for
@ -87,7 +96,10 @@ 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.

View file

@ -85,7 +85,8 @@ 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:

View file

@ -22,7 +22,8 @@ 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

View file

@ -54,7 +54,9 @@ 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
--------------
@ -77,7 +79,9 @@ some much nicer examples:
::
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
So, a lockstring consists of the type of restriction (the
``access_type``), a colon (``:``) and then a list of function calls that
@ -174,7 +178,11 @@ 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
@ -197,14 +205,9 @@ Some useful default lockfuncs (see lockfuncs.py for a full list):
- ``attr(attrname, value)`` - checks so an attribute exists on
accessing*object*and has the given value.
- ``attr_gt(attrname, value)`` - checks so accessingobject has a value
larger (>) than the given value.
- ``attr_ge, attr_lt, attr_le, attr_ne`` - corresponding for >
, <, <
======
and !=.
larger (``>``) than the given value.
- ``attr_ge, attr_lt, attr_le, attr_ne`` - corresponding for ``>=``,
``<``, ``<=`` and ``!=``.
- ``holds(objid)`` - checks so the accessing objects contains an object
of given name or dbref.
- ``pperm(perm)``, ``pid(num)/pdbref(num)`` - same as ``perm``,
@ -247,7 +250,11 @@ 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
@ -260,7 +267,8 @@ 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
----------
@ -330,7 +338,8 @@ 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
@ -349,7 +358,12 @@ 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
@ -367,7 +381,7 @@ checks if attributes have a value greater than a given value. Luckily
there is already such a one included in evennia (see
``src/permissions/lockfuncs.py``), called``attr_gt``.
So the lock string will look like this: "``get:attr_gt(strength, 50)``".
So the lock string will look like this: ``get:attr_gt(strength, 50)``.
We put this on the box now:
::
@ -383,7 +397,9 @@ 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
=============================

View file

@ -83,7 +83,13 @@ 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

View file

@ -19,7 +19,14 @@ 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 """ 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
"""
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
@ -34,7 +41,8 @@ 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

View file

@ -41,7 +41,15 @@ Here's how to define a new Player typeclass in code:
::
from src.players.player import Playerclass 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 our game needs to know
from src.players.player import Playerclass 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 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

View file

@ -34,7 +34,10 @@ 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>`_.
@ -74,7 +77,11 @@ 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
@ -100,7 +107,22 @@ 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
@ -108,7 +130,14 @@ 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

View file

@ -132,7 +132,24 @@ find longer descriptions of these in ``gamesrc/scripts/basescript.py``.
::
import random
from game.gamesrc.scripts.basescript import Scriptclass Weather(Script): "Displays weather info. Meant to be attached to a room." def at_script_creation(self): "Called once, during initial creation" self.key = "weather_script" self.desc = "Gives random weather messages." self.interval = 60 * 5 # every 5 minutes self.persistent = True self.at_repeat(self): "called every self.interval seconds." rand = random.random() if rand < 0.5: weather = "A faint breeze is felt." elif rand < 0.7: weather = "Clouds sweep across the sky." else: weather = "There is a light drizzle of rain." # send this message to everyone inside the object this # script is attached to (likely a room) self.obj.msg_contents(weather)
from game.gamesrc.scripts.basescript import Scriptclass Weather(Script):
"Displays weather info. Meant to be attached to a room." def at_script_creation(self):
"Called once, during initial creation"
self.key = "weather_script"
self.desc = "Gives random weather messages."
self.interval = 60 * 5 # every 5 minutes
self.persistent = True self.at_repeat(self):
"called every self.interval seconds."
rand = random.random()
if rand < 0.5:
weather = "A faint breeze is felt."
elif rand < 0.7:
weather = "Clouds sweep across the sky."
else:
weather = "There is a light drizzle of rain."
# send this message to everyone inside the object this
# script is attached to (likely a room)
self.obj.msg_contents(weather)
This is a simple weather script that we can put on an object. Every 5
minutes it will tell everyone inside that object how the weather is.

View file

@ -51,7 +51,8 @@ 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

View file

@ -43,7 +43,8 @@ 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.
@ -84,7 +85,8 @@ 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
-------------

View file

@ -238,7 +238,8 @@ 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

View file

@ -57,7 +57,25 @@ 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

View file

@ -0,0 +1,14 @@
Index: wiki2html/lib/wiki_syntax.rb
===================================================================
--- wiki2html/lib/wiki_syntax.rb (revision 1961)
+++ wiki2html/lib/wiki_syntax.rb (working copy)
@@ -227,7 +227,8 @@
block
else
# remove newlines within normal (non-code) blocks of text
- "<p>" + block.gsub(/\n/, ' ') + "</p>"
+ #"<p>" + block.gsub(/\n/, ' ') + "</p>"
+ "<p>" + block + "</p>"
end
end.join
end

View file

@ -11,6 +11,11 @@
#
# This is a ruby program! Sorry, it was the best match I could find to do this.
# So if you don't have ruby, you need that too.
#
# You also need to patch a bug in above program to make code snippets work. From the wiki2rest folder,
# apply the patch like this:
#
# patch -p0 -i wiki2html.patch
#
# 2) Install pandoc:
#