mirror of
https://github.com/evennia/evennia.git
synced 2026-03-16 21:06:30 +01:00
docs: Adding missing rest pages to mercurial.
This commit is contained in:
parent
f95fac1f51
commit
ae65438c9b
16 changed files with 2114 additions and 0 deletions
114
docs/sphinx/source/wiki/AddingCommandTutorial.rst
Normal file
114
docs/sphinx/source/wiki/AddingCommandTutorial.rst
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
Adding a new command - a step by step guide
|
||||
===========================================
|
||||
|
||||
This is a quick first-time tutorial expanding on the
|
||||
`Commands <Commands.html>`_ documentation.
|
||||
|
||||
Let's assume you have just downloaded Evennia and want to try to add a
|
||||
new command. This is the fastest way to do it.
|
||||
|
||||
Tell Evennia where to look for custom commands/cmdsets
|
||||
------------------------------------------------------
|
||||
|
||||
We will tell Evennia that you want to override the default cmdset with
|
||||
new additional commands of your own.
|
||||
|
||||
#. Go to ``game/gamesrc/commands``.
|
||||
#. There is a subfolder here named ``examples``. *Copy* the files
|
||||
``examples/command.py`` and ``examples/cmdset.py`` to where you are.
|
||||
You can rename them as you please, but in this example we assume you
|
||||
don't.
|
||||
#. Edit ``game/settings.py``, adding the following line:
|
||||
|
||||
``CMDSET_DEFAULT="game.gamesrc.commands.cmdset.DefaultCmdSet"``
|
||||
|
||||
Evennia will now look for default commands in the ``DefaultCmdSet``
|
||||
class of your newly copied module. You only need to do this once.
|
||||
|
||||
Creating a custom command
|
||||
-------------------------
|
||||
|
||||
#. Edit your newly copied ``game/gamesrc/commands/command.py``. This
|
||||
template already imports everything you need.
|
||||
#. Create a new class in ``command.py`` that inherits from
|
||||
``MuxCommand``. Let's call it ``CmdEcho`` in this example.
|
||||
#. Set the class variable ``key`` to a good command name, like ``echo``.
|
||||
#. Set the ``locks`` property on the command to a suitable
|
||||
[Locks#Defining\_locks lockstring]. If you are unsure, use
|
||||
``"cmd:all()"``.
|
||||
#. Give your class a useful *\_doc\_* string, this acts as the help
|
||||
entry for the command.
|
||||
#. Define a class method ``func()`` that does stuff. Below is an example
|
||||
how it all could look.
|
||||
|
||||
::
|
||||
|
||||
# file game/gamesrc/commands/command.py
|
||||
#[...]
|
||||
class CmdEcho(MuxCommand):
|
||||
"""
|
||||
Simple command example
|
||||
|
||||
Usage:
|
||||
echo <text>
|
||||
|
||||
This command simply echoes text back to the caller.
|
||||
"""
|
||||
|
||||
key = "echo"
|
||||
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)
|
||||
|
||||
Adding the Command to a Cmdset
|
||||
------------------------------
|
||||
|
||||
The command is not available to use until it is part of a Command Set.
|
||||
In this example we will go the easiest route and add it to the default
|
||||
command set we already prepared.
|
||||
|
||||
#. Edit your recently copied ``game/gamesrc/commands/cmdset.py``
|
||||
#. In this copied module you will find the ``DefaultCmdSet`` class
|
||||
already imported and prepared for you. Import your new command module
|
||||
here with ``from game.gamesrc.commands import echo``.
|
||||
#. Add a line ``self.add(echo.CmdEcho())`` to ``DefaultCmdSet``, in the
|
||||
``at_cmdset_creation`` method (the template tells you where). This is
|
||||
approximately how it should look at this point:
|
||||
|
||||
::
|
||||
|
||||
# file gamesrc/commands/examples/cmdset.py
|
||||
#[...]
|
||||
from game.gamesrc.commands import echo
|
||||
#[...]
|
||||
class DefaultCmdSet(default_cmds.DefaultCmdSet):
|
||||
|
||||
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(echo.CmdEcho())
|
||||
|
||||
#. Reboot/restart Evennia (``@reload`` from inside the game). You should
|
||||
now be able to use your new ``echo`` command from inside the game.
|
||||
Use ``help echo`` to see the documentation for the command.
|
||||
|
||||
If you have trouble, make sure to check the log for error messages
|
||||
(probably due to syntax errors in your command definition).
|
||||
|
||||
Adding new commands to the default cmdset in the future now only
|
||||
involves creating the function class and adding it to the same place. If
|
||||
you want to overload existing default commands (such as ``look`` or
|
||||
``get``), just add your new command with the same key as the old one -
|
||||
it will overload the default one. Just remember that you must
|
||||
``@reload`` the server before you see any changes.
|
||||
132
docs/sphinx/source/wiki/Banning.rst
Normal file
132
docs/sphinx/source/wiki/Banning.rst
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
Sometimes it's just not worth the grief ...
|
||||
===========================================
|
||||
|
||||
Whether due to abuse, blatant breaking of your rules, or some other
|
||||
reason you will eventually find no other recourse but to kick out a
|
||||
particularly troublesome player. The default command set has admin tools
|
||||
to handle this, primarily ``@ban, @unban`` and ``@boot``.
|
||||
|
||||
Creating a ban
|
||||
==============
|
||||
|
||||
Say we have a troublesome player "YouSuck" - this is a guy that refuse
|
||||
common courtesy - an abusive and spammy account that is clearly created
|
||||
by some bored internet hooligan only to cause grief. You have tried to
|
||||
be nice. Now you just want this troll gone.
|
||||
|
||||
Name ban
|
||||
--------
|
||||
|
||||
The easiest is to block the account YouSuck from ever connecting again.
|
||||
|
||||
::
|
||||
|
||||
@ban YouSuck
|
||||
|
||||
This will lock the name YouSuck (as well as 'yousuck' and any other
|
||||
combination), and next time they try to log in with this name the server
|
||||
will not let them!
|
||||
|
||||
You can also give a reason so you remember later why this was a good
|
||||
thing (the banned player will never see this)
|
||||
|
||||
::
|
||||
|
||||
@ban YouSuck:This is just a troll.
|
||||
|
||||
If you are sure this is just a spam account, you might even consider
|
||||
deleting the player account outright:
|
||||
|
||||
::
|
||||
|
||||
@delplayer YouSuck
|
||||
|
||||
Generally banning the name is the easier and safer way to stop the use
|
||||
of an account -- if you change your mind you can always remove the block
|
||||
later whereas a deletion is permanent.
|
||||
|
||||
IP ban
|
||||
------
|
||||
|
||||
Just because you block YouSuck's name might not mean the trolling human
|
||||
behind that account gives up. They can just create a new account
|
||||
YouSuckMore and be back at it. One way to make things harder for them is
|
||||
to tell the server to not allow connections from their particular IP
|
||||
address.
|
||||
|
||||
First, when the offending player is online, check which IP address they
|
||||
use. This you can do with the ``who`` command, which will show you
|
||||
something like this:
|
||||
|
||||
::
|
||||
|
||||
Player Name On for Idle Room Cmds Host
|
||||
YouSuck 01:12 2m 22 212 237.333.0.223
|
||||
|
||||
The "Host" bit is the IP address from which the player is connecting.
|
||||
Use this to define the ban instead of the name:
|
||||
|
||||
::
|
||||
|
||||
@ban 237.333.0.223
|
||||
|
||||
This will stop YouSuck connecting from his computer. Note however that
|
||||
IP addresses might change easily - either due to how the player's
|
||||
Internet Service Provider operates or by the user simply changing
|
||||
computer. You can make a more general ban by putting asterisks ``*`` as
|
||||
wildcards for the groups of three digits in the address. So if you
|
||||
figure out that YouSuck mainly connects from 237.333.0.223,
|
||||
237.333.0.225 and 237.333.0.256 (only changes in the local subnet), it
|
||||
might be an idea to put down a ban like this to include any number in
|
||||
that subnet:
|
||||
|
||||
::
|
||||
|
||||
@ban 237.333.0.*
|
||||
|
||||
You should combine the IP ban with a name-ban too of course, so the
|
||||
account YouSuck is truly locked regardless of from where they connect.
|
||||
|
||||
Be careful with too general IP bans however (more asterisks above). If
|
||||
you are unlucky you could be blocking out innocent players who just
|
||||
happen to connect from the same subnet as the offender.
|
||||
|
||||
Booting
|
||||
=======
|
||||
|
||||
YouSuck is not really noticing all this banning yet though - and won't
|
||||
until having logged out and tries to log back in again. Let's help the
|
||||
troll along.
|
||||
|
||||
::
|
||||
|
||||
@boot YouSuck
|
||||
|
||||
Good riddance. You can give a reason for booting too (to be echoed to
|
||||
the player before getting kicked out).
|
||||
|
||||
::
|
||||
|
||||
@boot YouSuck:Go troll somewhere else.
|
||||
|
||||
Lifting a ban
|
||||
=============
|
||||
|
||||
Give the ``@unban`` (or ``@ban``) command without any arguments and you
|
||||
will see a list of all currently active bans:
|
||||
|
||||
::
|
||||
|
||||
Active bans
|
||||
id name/ip date reason
|
||||
1 yousuck Fri Jan 3 23:00:22 2020 This is just a Troll.
|
||||
2 237.333.0.* Fri Jan 3 23:01:03 2020 YouSuck's IP.
|
||||
|
||||
Use the ``id`` from this list to find out which ban to lift.
|
||||
|
||||
::
|
||||
|
||||
@unban 2
|
||||
|
||||
Cleared ban 2: 237.333.0.*
|
||||
|
||||
63
docs/sphinx/source/wiki/CodingIntroduction.rst
Normal file
63
docs/sphinx/source/wiki/CodingIntroduction.rst
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
Evennia coding introduction
|
||||
===========================
|
||||
|
||||
Evennia allows for a lot of freedom when designing your game - but to
|
||||
code efficiently you still need to adopt some best practices as well as
|
||||
find a good place to start to learn.
|
||||
|
||||
Here are some pointers to get you going.
|
||||
|
||||
Code in \`game/gamesrc\`, not in \`src/\`
|
||||
-----------------------------------------
|
||||
|
||||
You will create and code your game by adding Python modules in
|
||||
``game/gamesrc/`` (see the `directory
|
||||
overview <DirectoryOverview.html>`_). This is your home. You should
|
||||
*never* need to modify anything under ``src/`` (anything you download
|
||||
from us, really). Treat ``src/`` as a kind of library. You import useful
|
||||
functionality from here. If you see code you like, copy&paste it out
|
||||
into ``game/gamesrc`` and edit it there.
|
||||
|
||||
If you find that ``src/`` *doesn't* support some functionality you need,
|
||||
make a `Feature
|
||||
Request <https://code.google.com/p/evennia/issues/list>`_ about it. Same
|
||||
goes for `bugs <https://code.google.com/p/evennia/issues/list>`_. If you
|
||||
add features or fix bugs yourself, please consider
|
||||
`contributing <Contributing.html>`_ your changes upstream!
|
||||
|
||||
Learn with \`ev\`
|
||||
-----------------
|
||||
|
||||
Learn the `ev interface <evAPI.html>`_. This is a great way to explore
|
||||
what Evennia has to offer. For example, start an interactive python
|
||||
shell, import ``ev`` and just look around.
|
||||
|
||||
You can compliment your exploration by peeking at the sections of the
|
||||
much more detailed `Developer Central <DeveloperCentral.html>`_. The
|
||||
`Tutorials <Tutorials.html>`_ section also contains a growing collection
|
||||
of system- or implementation-specific help.
|
||||
|
||||
Plan before you code
|
||||
--------------------
|
||||
|
||||
Before you start coding away at your dream game, take a look at our
|
||||
`game planning hints and tips <GamePlanning.html>`_ page. It might
|
||||
hopefully help you avoid some common pitfalls and time sinks.
|
||||
|
||||
Docs are here to help you
|
||||
-------------------------
|
||||
|
||||
Some people find reading documentation extremely dull and shun it out of
|
||||
principle. That's your call, but reading docs really *does* help you,
|
||||
promise! Evennia's documentation is pretty thorough and knowing what is
|
||||
possible can often give you a lot of new cool game ideas. That said, if
|
||||
you can't find the answer in the docs, don't be shy to ask questions!
|
||||
The `discussion
|
||||
group <https://sites.google.com/site/evenniaserver/discussions>`_ and
|
||||
the `irc chat <http://webchat.freenode.net/?channels=evennia>`_ are
|
||||
there for you.
|
||||
|
||||
The most important point
|
||||
------------------------
|
||||
|
||||
And finally, of course, have fun!
|
||||
245
docs/sphinx/source/wiki/CodingUtils.rst
Normal file
245
docs/sphinx/source/wiki/CodingUtils.rst
Normal file
|
|
@ -0,0 +1,245 @@
|
|||
Utils
|
||||
=====
|
||||
|
||||
Evennia comes with many utilities to help with common coding tasks. Some
|
||||
of these are part of the command interface but most can be found in the
|
||||
``src/utils/`` folder. They are used all over the server, but offer help
|
||||
for many common tasks when coding your own game as well. This is not a
|
||||
complete list, check the module for more goodies.
|
||||
|
||||
Search
|
||||
------
|
||||
|
||||
A common thing to do is to search for objects. The most common time one
|
||||
needs to do this is inside a command body. There it's easiest to use the
|
||||
``search`` method defined on all objects. This will search for objects
|
||||
in the same location and inside the caller:
|
||||
|
||||
::
|
||||
|
||||
obj = self.caller.search(objname)
|
||||
|
||||
Give the keyword ``global_search=True`` to extend search to encompass
|
||||
entire database. Also aliases with be matched by this search. You will
|
||||
find multiple examples of this functionality in the default command set.
|
||||
|
||||
If you need to search for objects in a code module you can use the
|
||||
functions in ``src.utils.search``. You can access these as shortcuts
|
||||
``ev.search_*``.
|
||||
|
||||
::
|
||||
|
||||
from ev import search_object
|
||||
obj = search_object(objname)
|
||||
|
||||
``utils.search`` contains properties to the relevant database search
|
||||
method. They are really just shortcuts to the django-database search
|
||||
methods, like ``ObjectDB.objects.search()``.
|
||||
|
||||
**Note:** If you are a Django wizz, you might be tempted to use Django's
|
||||
database search functions directly (using ``filter``, ``get`` etc). Just
|
||||
remember that such operations will give you a django model whereas
|
||||
Evennia's manager methods will give you a typeclass. It's easy to
|
||||
convert between them with ``dbobj.typeclass`` and ´typeclass.dbobj´,
|
||||
but you should remember this distinction. If you stick with Evennia's
|
||||
search methods you will always get typeclasses back.
|
||||
|
||||
Create
|
||||
------
|
||||
|
||||
Apart from the in-game build commands (``@create`` etc), you can also
|
||||
build all of Evennia's game entities directly in code (for example when
|
||||
defining new create commands). This *must* be done using
|
||||
``src.utils.create`` or their shortcuts ``ev.create_*``- these functions
|
||||
are responsible for setting up all the background intricacies of the
|
||||
typeclass system and other things. Creating database instances using raw
|
||||
Django will *not* work. Examples:
|
||||
|
||||
::
|
||||
|
||||
import ev
|
||||
#
|
||||
myobj = ev.create_objects("game.gamesrc.objects.myobj.MyObj", key="MyObj")
|
||||
myscr = ev.create_script("game.gamesrc.scripts.myscripts.MyScript", obj=myobj)
|
||||
help = ev.create_help_entry("Emoting", "Emoting means that ...")
|
||||
msg = ev.create_message(senderobj, [receiverobj], "Hello ...")
|
||||
chan = ev.create_channel("news")
|
||||
player = ev.create_player("Henry", "henry@test.com", "H@passwd")
|
||||
|
||||
Each of these create functions have a host of arguments to further
|
||||
customize the created entity. See ``src/utils/create.py`` for more
|
||||
information.
|
||||
|
||||
Logging
|
||||
-------
|
||||
|
||||
Normally you can use Python ``print`` statements to see output to the
|
||||
terminal (if you started Evennia in *interactive mode* with the -i
|
||||
switch). This should only be used for debugging though, for real output,
|
||||
use the logger - it will log to the terminal in interactive mode, to the
|
||||
log file otherwise.
|
||||
|
||||
::
|
||||
|
||||
from ev import logger
|
||||
#
|
||||
logger.log_errmsg("This is an Error!")
|
||||
logger.log_warnmsg("This is a Warning!")
|
||||
logger.log_infomsg("This is normal information")
|
||||
logger.log_depmsg("This feature is deprecated")
|
||||
|
||||
There is also a special log-message type that is intended to be called
|
||||
from inside a traceback - this can be very useful for relaying the
|
||||
traceback message back to log without having it kill the server.
|
||||
|
||||
::
|
||||
|
||||
try:
|
||||
# [some code that may fail...]
|
||||
except Exception:
|
||||
logger.log_trace("This text will be appended to the traceback info")
|
||||
|
||||
inherits\_from()
|
||||
----------------
|
||||
|
||||
This useful function takes two arguments - an object to check and a
|
||||
parent. It returns ``True`` if object inherits from parent *at any
|
||||
distance* (as opposed to Python's ``is_instance()`` that will only catch
|
||||
immediate dependence. This function also accepts any combination of
|
||||
classes, instances or python paths to classes as inputs.
|
||||
|
||||
Note that Python code should usually work with `duck
|
||||
typing <http://en.wikipedia.org/wiki/Duck_typing>`_. But in Evennia's
|
||||
case it can sometimes be useful to check if an object inherits from a
|
||||
given `Typeclass <Typelasses.html>`_ as a way of identification. Say for
|
||||
example that we have a typeclass *Animal*. This has a subclass *Felines*
|
||||
which in turns is a parent to *HouseCat*. Maybe there are a bunch of
|
||||
other animal types too, like horses and dogs. Using ``inherits_from``
|
||||
will allow you to check for all animals in one go:
|
||||
|
||||
::
|
||||
|
||||
from ev import utils
|
||||
if (utils.inherits_from(obj, "game.gamesrc.objects.animals.Animal"):
|
||||
obj.msg("The bouncer stops you in the door. He says: 'No talking animals allowed.'")
|
||||
|
||||
Some text utilities
|
||||
-------------------
|
||||
|
||||
In a text game, you are naturally doing a lot of work shuffling text
|
||||
back and forth. Here is a *non-complete* selection of text utilities
|
||||
found in ``src/utils/utils.py`` (shortcut ``ev.utils``). If nothing else
|
||||
it can be good to look here before starting to develop a solution of
|
||||
your own.
|
||||
|
||||
fill()
|
||||
~~~~~~
|
||||
|
||||
This flood-fills a text to a given width (shuffles the words to make
|
||||
each line evenly wide). It also indents as needed.
|
||||
|
||||
::
|
||||
|
||||
outtxt = fill(intxt, width=78, indent=4)
|
||||
|
||||
crop()
|
||||
~~~~~~
|
||||
|
||||
This function will crop a very long line, adding a suffix to show the
|
||||
line actually continues. This can be useful in listings when showing
|
||||
multiple lines would mess up things.
|
||||
|
||||
::
|
||||
|
||||
intxt = "This is a long text that we want to crop."
|
||||
outtxt = crop(intxt, width=19, suffix="[...]")
|
||||
# outtxt is now "This is a long text[...]"
|
||||
|
||||
dedent()
|
||||
~~~~~~~~
|
||||
|
||||
This solves what may at first glance appear to be a trivial problem with
|
||||
text - removing indentations. It is used to shift entire paragraphs to
|
||||
the left, without disturbing any further formatting they may have. A
|
||||
common case for this is when using Python triple-quoted strings in code
|
||||
- they will retain whichever indentation they have in the code, and to
|
||||
make easily-readable source code one usually don't want to shift the
|
||||
string to the left edge.
|
||||
|
||||
::
|
||||
|
||||
#python code is at this indentation
|
||||
intxt = """
|
||||
This is an example text that will end
|
||||
up with a lot of whitespace on the left.
|
||||
It also has indentations of
|
||||
its own."""
|
||||
outtxt = dedent(intxt)
|
||||
# outtxt will now retain all internal indentation
|
||||
# but be shifted all the way to the left.
|
||||
|
||||
Normally you do the dedent in the display code (this is the way the help
|
||||
system homogenizes help entries).
|
||||
|
||||
time\_format()
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
This function takes a number of seconds as input and converts it to a
|
||||
nice text output in days, hours etc. It's useful when you want to show
|
||||
how old something is. It converts to four different styles of output
|
||||
using the *style* keyword:
|
||||
|
||||
- style 0 - ``5d:45m:12s`` (standard colon output)
|
||||
- style 1 - ``5d`` (shows only the longest time unit)
|
||||
- style 2 - ``5 days, 45 minutes`` (full format, ignores seconds)
|
||||
- style 3 - ``5 days, 45 minutes, 12 seconds`` (full format, with
|
||||
seconds)
|
||||
|
||||
text conversion()
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
Evennia supplies two utility functions for converting text to the
|
||||
correct encodings. ``to_str()`` and ``to_unicode()``. The difference
|
||||
from Python's in-built ``str()`` and ``unicode()`` operators are that
|
||||
the Evennia ones makes use of the ``ENCODINGS`` setting and will try
|
||||
very hard to never raise a traceback but instead echo errors through
|
||||
logging. See `TextEncodings <TextEncodings.html>`_ for more info.
|
||||
|
||||
format\_table()
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
This function creates nicely formatted tables - columns of text all
|
||||
lined up. It will automatically widen each column so all entries fit.
|
||||
|
||||
To use it, you need to create a list of lists - each sublist contains
|
||||
the content of one column. The result will be a list of ready-formatted
|
||||
strings to print.
|
||||
|
||||
::
|
||||
|
||||
# title line
|
||||
cols = [["num"],["x"],["y"]]
|
||||
# creating a dummy table with integers
|
||||
for i in range(3):
|
||||
cols[0].append(i)
|
||||
cols[1].append(i+1)
|
||||
cols[2].append(i+2)
|
||||
# format the table (returns list with rows)
|
||||
ftable = format_table(cols, extra_space=3)
|
||||
# print the rows, making header bright white
|
||||
for irow, row in enumerate(ftable):
|
||||
if irow == 0: # header
|
||||
print "{w%s{x" % row
|
||||
else:
|
||||
print row
|
||||
# Output (no colors shown):
|
||||
#
|
||||
# num x y
|
||||
# 1 2 3
|
||||
# 2 3 4
|
||||
# 3 4 5
|
||||
#
|
||||
|
||||
Note that you cannot add colour codes to the input to ``format_table`` -
|
||||
these would mess up the width of each column. Instead you can add this
|
||||
to the output when printing.
|
||||
92
docs/sphinx/source/wiki/CommandCooldown.rst
Normal file
92
docs/sphinx/source/wiki/CommandCooldown.rst
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
Cooldowns
|
||||
=========
|
||||
|
||||
Some types of games want to limit how often a command can be run. If a
|
||||
character casts the spell *Firestorm*, you might not want them to spam
|
||||
that command over and over. Or in an advanced combat system, a massive
|
||||
swing may offer a chance of lots of damage at the cost of not being able
|
||||
to re-do it for a while. Such effects are called *cooldowns*.
|
||||
|
||||
Evennia allows for many ways to implement cooldowns. Here are some
|
||||
ideas.
|
||||
|
||||
Simplest way - single-command, non-persistent cooldown
|
||||
------------------------------------------------------
|
||||
|
||||
This little recipe will limit how often a particular command can be run.
|
||||
Since Commands are class instances, and those are cached in memory, a
|
||||
command instance will remember things you store on it. So just store the
|
||||
current time of execution! Next time the command is run, it just needs
|
||||
to check if it has that time stored, and compare it with the current
|
||||
time to see if a desired delay has passed.
|
||||
|
||||
::
|
||||
|
||||
import time
|
||||
from ev import default_cmds
|
||||
|
||||
class CmdSpellFirestorm(default_cmds.MuxCommand):
|
||||
"""
|
||||
Spell - Firestorm
|
||||
|
||||
Usage:
|
||||
cast firestorm <target>
|
||||
|
||||
This will unleash a storm of flame. You can only release one
|
||||
firestorm every five minutes (assuming you have the mana).
|
||||
"""
|
||||
key = "cast firestorm"
|
||||
locks = "cmd:isFireMage()"
|
||||
|
||||
def func(self):
|
||||
"Implement the spell"
|
||||
|
||||
# check cooldown (5 minute cooldown)
|
||||
if hasattr(self, "lastcast") and time.time()-self.lastcast < 5*60:
|
||||
self.caller.msg("You need to wait before casting this spell again.")
|
||||
return
|
||||
|
||||
#[the spell effect is implemented]
|
||||
|
||||
# if the spell was successfully cast, store the casting time
|
||||
self.lastcast = time.time()
|
||||
|
||||
We just check the ``lastcast`` flag, and update it if everything works
|
||||
out. Simple and very effective since everything is just stored in
|
||||
memory. The drawback of this simple scheme is that it's non-persistent.
|
||||
If you do ``@reload``, the cache is cleaned and all such ongoing
|
||||
cooldowns will be forgotten. It is also limited only to this one
|
||||
command, other commands cannot (easily) check for this value.
|
||||
|
||||
Persistent cooldown
|
||||
-------------------
|
||||
|
||||
This is essentially the same mechanism as the simple one above, except
|
||||
we use the database to store the information which means the cooldown
|
||||
will survive a server reload/reboot. Since commands themselves have no
|
||||
representation in the database, you need to use the caster for the
|
||||
storage.
|
||||
|
||||
::
|
||||
|
||||
#[...]
|
||||
# check cooldown (5 minute cooldown)
|
||||
lastcall = self.caller.db.firestorm_lastcast # returns None if not exist yet
|
||||
if lastcast and time.time() - lastcast < 5*60:
|
||||
self.caller.msg("You need to wait before casting this spell again.")
|
||||
return
|
||||
|
||||
#[the spell effect is implemented]
|
||||
|
||||
# if the spell was successfully cast, store the casting time
|
||||
self.caller.db.firestorm_lastcast = time.time()
|
||||
|
||||
Since we are storing as an `Attribute <Attributes.html>`_, we need to
|
||||
identify the variable as ``firestorm_lastcast`` so we are sure we get
|
||||
the right one (we'll likely have other skills with cooldowns after all).
|
||||
But this method of using cooldowns also has the advantage of working
|
||||
*between* commands - you can for example let all fire-related spells
|
||||
check the same cooldown to make sure the casting of *Firestorm* blocks
|
||||
all fire-related spells for a while. Or, in the case of taking that big
|
||||
swing with the sword, this could now block all other types of attacks
|
||||
for a while before the warrior can recover.
|
||||
185
docs/sphinx/source/wiki/GamePlanning.rst
Normal file
185
docs/sphinx/source/wiki/GamePlanning.rst
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
Game development tips and tricks
|
||||
================================
|
||||
|
||||
So you have Evennia up and running. You have a great game idea in mind.
|
||||
Now it's time to start cracking! But where to start? Here are some ideas
|
||||
for a workflow. Note that the suggestions on this page are just that -
|
||||
suggestions. Also, they are primarily aimed at a lone hobby designer or
|
||||
a small team developing a game in their free time.
|
||||
|
||||
Phases of Evennia game development
|
||||
==================================
|
||||
|
||||
Below are some minimal steps for getting the first version of a new game
|
||||
world going with players. It's worth to at least make the attempt to do
|
||||
these steps in order even if you are itching to jump ahead in the
|
||||
development cycle. On the other hand, you should also make sure to keep
|
||||
your work fun for you, or motivation will falter. Making a full game is
|
||||
a lot of work as it is, you'll need all your motivation to make it a
|
||||
reality.
|
||||
|
||||
Remember that *99.99999% of all great game ideas never leads to an
|
||||
online game*. So your first all overshadowing goal is to beat those odds
|
||||
and get *something* out the door! *Even* if it's a scaled-down version
|
||||
of your dream game, lacking many "must-have" features! It's better to
|
||||
get it out there and expand on it later than to code in isolation
|
||||
forever until you burn out, loose interest or your hard drive crashes.
|
||||
|
||||
Like is common with online games, getting a game out the door does not
|
||||
mean you are going to be "finished" with the game - most MUDs add
|
||||
features gradually over the course of years - it's often part of the
|
||||
fun!
|
||||
|
||||
1: Planning
|
||||
-----------
|
||||
|
||||
This is what you do before having coded a single line or built a single
|
||||
room. Many prospective game developers are very good at *parts* of this
|
||||
process, namely in defining what their world is "about": The theme, the
|
||||
world concept, cool monsters and so on. This is by all means important -
|
||||
yes critical to the appeal of your game. But it's unfortunately not
|
||||
enough to make your game a reality. To do that you must have an idea of
|
||||
how to actually map those great ideas onto Evennia.
|
||||
|
||||
A good start is to begin by planning out the basic primitives of the
|
||||
game and what they need to be able to do.
|
||||
|
||||
- **Rooms** - consider the most basic room in your game. How "big" is
|
||||
it in a game sense? What should Players be able to do inside it? Is a
|
||||
simple description enough? Can it be dark (description
|
||||
changed/hidden)? Should it have smells, sounds? Weather? Different
|
||||
terrain? How are those to be conveyed? Are there special "magic"
|
||||
rooms that does things to people entering? Can a person hide in the
|
||||
room? Should all rooms have the ability to be this complex or should
|
||||
there be different types of rooms? Evennia allows you to change the
|
||||
very concept of rooms should you be very ambitious, but is that a
|
||||
road you really want to go down for your project?
|
||||
- **Objects** - consider the most basic (non-player-controlled) object
|
||||
in your game. What should a Player be able to do with it? Smash it?
|
||||
If so, will it need some measure of its health? Does it have weight
|
||||
or volume (so you cannot carry an infinite amount of them)? How do
|
||||
you handle multiple identical objects? Try to give rough
|
||||
classifications. Is a weapon a different type of object or are you
|
||||
supposed to be able to fight with a chair as well? What about
|
||||
NPCs/mobs, should they have some sort of AI?
|
||||
- **Systems** - These are the behind-the-scenes features that exist in
|
||||
your game, often without being represented by a specific in-game
|
||||
object. For a role playing game, you need to define chances of
|
||||
success ("rolls") for example. Will weather messages be random in
|
||||
every room or should it follow some sort of realistic pattern over
|
||||
all rooms? Do you have an game-wide economy - if so, how is that
|
||||
supposed to work? If magic is dependent on the position of the
|
||||
planets, the planets must change with time. What about spreading
|
||||
rumors? Mail boxes? Bulletin boards?
|
||||
- **Characters** - to do all those things with the rooms, objects and
|
||||
systems in the game, what will the Characters need to have? What
|
||||
skill will decide if they can "hide" in a room? Wield a chair as a
|
||||
weapon? How to tell how much they can carry or which objects they can
|
||||
smash? Can they gain experience and how? How about skills, classes,
|
||||
attributes?
|
||||
|
||||
A MUD's a lot more involved than you would think and these things hang
|
||||
together in a complex web. It can easily become overwhelming and it's
|
||||
tempting to want *all* functionality right out of the door. Try to
|
||||
identify the basic things that "make" your game and focus on them for
|
||||
the first release. Make a list. Keep future expansions in mind but limit
|
||||
yourself.
|
||||
|
||||
2: Coding
|
||||
---------
|
||||
|
||||
This is the actual work of creating the "game" part of your game. Many
|
||||
"game-designer" types tend to gloss over this bit and jump directly to
|
||||
**Building**. Vice-versa, many "game-coder" types tend to jump directly
|
||||
to this part without doing the **Planning** first. Neither is good and
|
||||
*will* lead to you having to redo all your hard work at least once,
|
||||
probably more.
|
||||
|
||||
Evennia's `Developer Central <DeveloperCentral.html>`_ is focused on how
|
||||
to perform this bit of the development. Evennia tries hard to make this
|
||||
part easier for you, but there is no way around the fact that if you
|
||||
want anything but a very basic Talker-type game you *will* have to bite
|
||||
the bullet and code your game (or find a coder willing to do it for
|
||||
you). Even if you won't code anything yourself, as a designer you need
|
||||
to at least understand the basic paradigms of Evennia, such as objects,
|
||||
commands and scripts and how they hang together. We recommend you go
|
||||
through the `Tutorial World <TutorialWorldIntroduction.html>`_ in detail
|
||||
(as well as skimming its code) to get at least a feel for what is
|
||||
involved behind the scenes.
|
||||
|
||||
During Coding you look back at the things you wanted during the
|
||||
**Planning** phase and try to implement them. Don't be shy to update
|
||||
your plans if you find things easier/harder than you thought. The
|
||||
earlier you revise problems, the easier they will be to fix.
|
||||
`Here <VersionControl.html>`_ are some hints for setting up a sane
|
||||
coding environment.
|
||||
|
||||
"Tech Demo" Building
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This is an integral part of your Coding. It might seem obvious to
|
||||
experienced coders, but it cannot be emphasized enough that you should
|
||||
*test* things on a *small* scale before putting your untested code into
|
||||
a large game-world. The earlier you test, the easier and cheaper it will
|
||||
be to fix bugs and even rework things that didn't work out the way you
|
||||
thought they would. You might even have to go back to the **Planning**
|
||||
phase if your ideas can't handle their meet with reality.
|
||||
|
||||
This means building singular in-game examples. Make one room and one
|
||||
object of each important type and test they work as they should in
|
||||
isolation, then add more if they are supposed to interact with each
|
||||
other in some way. Build a small series of rooms to test how mobs move
|
||||
around ... and so on. In short, a test-bed for your growing code. It
|
||||
should be done gradually until you have a fully functioning (if not
|
||||
guaranteed bug-free) miniature tech demo that shows *all* the features
|
||||
you want in the first release of your game. There does not need to be
|
||||
any game play or even a theme to your tests, but the more testing you do
|
||||
on this small scale, the less headaches you will have in the next phase.
|
||||
|
||||
3: World Building
|
||||
-----------------
|
||||
|
||||
Up until this point we've only had a few tech-demo objects in the
|
||||
database. This step is the act of populating the database with a larger,
|
||||
thematic world. Too many would-be developers jump to this stage too soon
|
||||
and then have to go back and rework things on already existing objects.
|
||||
Evennia's typeclass system does allow you to edit the properties of
|
||||
existing objects, but some hooks are only called at object creation, and
|
||||
you are in for a *lot* of unnecessary work if you build stuff en masse
|
||||
without having the underlying code systems in some reasonable shape
|
||||
first.
|
||||
|
||||
So, at this point the "game" bit (Coding + Testing) should be more or
|
||||
less complete, *at least to the level of your initial release*. Building
|
||||
often involves non-coders, so you also get to test whatever custom build
|
||||
systems you have made at this point. You don't have to complete your
|
||||
entire world in one go - just enough to make the game's "feel" come
|
||||
across - an actual world where the intended game play can be tested and
|
||||
roughly balanced. You can always add new areas later, so limit yourself.
|
||||
|
||||
Alpha Release
|
||||
-------------
|
||||
|
||||
As mentioned, don't hold onto your world more than necessary. *Get it
|
||||
out there* with a huge *Alpha* flag and let people try it! Call upon
|
||||
your alpha-players to try everything - they *will* find ways to break
|
||||
your game in ways that you never could have imagined. In Alpha you might
|
||||
be best off to focus on inviting friends and maybe other MUD developers,
|
||||
people who you can pester to give proper feedback and bug reports (there
|
||||
*will* be bugs, there is no way around it). Follow the quick
|
||||
instructions `here <OnlineSetup.html>`_ to make your game visible
|
||||
online.
|
||||
|
||||
Beta Release/Perpetual Beta
|
||||
---------------------------
|
||||
|
||||
Once things stabilize in Alpha you can move to *Beta* and let more
|
||||
people in. Many MUDs are in `perpetual
|
||||
beta <http://en.wikipedia.org/wiki/Perpetual_beta>`_, meaning they are
|
||||
never considered "finished", but just repeat the cycle of Planning,
|
||||
Coding, Testing and Building over and over as new features get
|
||||
implemented or Players come with suggestions. As the game designer it's
|
||||
up to you to perfect your vision.
|
||||
|
||||
Congratulations, at this point you have joined the small, exclusive
|
||||
crowd who have made their dream game a reality!
|
||||
15
docs/sphinx/source/wiki/GettingHelp.rst
Normal file
15
docs/sphinx/source/wiki/GettingHelp.rst
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
Getting Help
|
||||
============
|
||||
|
||||
The best way to get help is via our `Google
|
||||
Group <http://evennia.com>`_. Simply post a message with a clear
|
||||
question and you are more than likely to receive a response. This way
|
||||
issues can be tracked over time too.
|
||||
|
||||
If you want more direct discussions with developers and other users,
|
||||
consider dropping into our IRC chat channel
|
||||
`#evennia <http://webchat.freenode.net/?channels=evennia>`_ on the
|
||||
Freenode network. Please note however that you have to be patient if you
|
||||
don't get any response immediately; we are all in very different time
|
||||
zones and many have busy personal lifes. So you might have to lurk about
|
||||
for a while - but if you are patient you'll get noticed eventually!
|
||||
199
docs/sphinx/source/wiki/OnlineSetup.rst
Normal file
199
docs/sphinx/source/wiki/OnlineSetup.rst
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
Making your game available online
|
||||
=================================
|
||||
|
||||
Evennia development can be made also without any internet connection
|
||||
(except to download updates). At some point however, you are likely to
|
||||
want to make your game visible online, either as part of making it
|
||||
public or to allow other developers or beta testers access to it.
|
||||
|
||||
Using your own computer as a server
|
||||
-----------------------------------
|
||||
|
||||
By far the simplest and probably cheapest option. Evennia will run on
|
||||
your own, home computer. Moreover, since Evennia is its own web server,
|
||||
you don't need to install anything extra to also run its website.
|
||||
|
||||
**Advantages**
|
||||
|
||||
- Free (except for internet cost and electrical bill)
|
||||
- Full control over the server/hardware (it sits right there!)
|
||||
- Easy to set up.
|
||||
- Also suitable for quick setups - e.g. to briefly show off results to
|
||||
your collaborators.
|
||||
|
||||
**Disadvantages**
|
||||
|
||||
- You need a good internet connection, ideally without any
|
||||
upload/download limits/costs.
|
||||
- If you want to run a full game this way, your computer needs to
|
||||
always be on. It could be noisy, and as mentioned, the electrical
|
||||
bill is worth considering.
|
||||
- No support or safety - if your house burns down, so will your game.
|
||||
Also, you are yourself responsible for backups etc (some would
|
||||
consider this an advantage).
|
||||
- Home IP numbers are often dynamically allocated, so for permanent
|
||||
online time you need to set up a DNS to always re-point to the right
|
||||
place (see below).
|
||||
|
||||
Set up your own machine as a server
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Making Evennia available from your own machine is mainly a matter of
|
||||
configuring eventual firewalls to let Evennia's ports through. With
|
||||
Evennia running, note which ports it is using (defaults are 4000 for
|
||||
telnet and 8000 for webclient, we assume them below).
|
||||
|
||||
#. Go to `http://www.whatismyip.com/ <http://www.whatismyip.com/>`_ (or
|
||||
similar site). They should tell you which IP address you are
|
||||
connecting from, e.g. ``230.450.0.222``.
|
||||
#. In your web browser, go to ``http://230.450.0.222:8000``, where the
|
||||
last ``:8000`` is the webclient port Evennia uses. If you see
|
||||
Evennia's website and can connect to the webclient - -congrats,
|
||||
that's it! Try to connect with a traditional MUD-client to the telnet
|
||||
port too, just to make sure.
|
||||
#. Most likely you won't see the Evennia website right away though. This
|
||||
is probably because you have a firewall blocking the ports we need.
|
||||
There could also be a hardware-router between your computer and the
|
||||
Internet - in that case the IP address we see "from the outside" is
|
||||
actually the router's IP, not that of your computer on your local
|
||||
network.
|
||||
|
||||
- You need to let Evennia data out through your router/firewall. How
|
||||
you do that varies with manufacturer and software. But in
|
||||
principle you should look for something called "Port forwarding"
|
||||
or similar. You want to route port 8000/4000 from your computer to
|
||||
an "outgoing port" that the world can see. That latter port does
|
||||
*not* have to have the same number as the internal port! For
|
||||
example, you might want to connect port 8000 to an outgoing port
|
||||
80 - this is the port HTTP requests use and web browsers
|
||||
automatically look for. If you use port 80 you won't have to
|
||||
specify the port number in the url of your browser. If you run
|
||||
other web servers on your machine, that could be an issue though.
|
||||
- I found that I had to reboot my router for this to take effect, so
|
||||
worth trying if you still cannot get to the Evennia website
|
||||
through your browser.
|
||||
|
||||
#. At this point you should be able to invite people to play your game
|
||||
on ``http://230.450.0.222:8000`` or via telnet to ``230.450.0.222``
|
||||
on port ``4000``.
|
||||
|
||||
A complication with using a specific IP address like this is that your
|
||||
home IP might not remain the same. Many ISPs (Internet Service
|
||||
Providers) allocates a dynamic IP to you which could change at any time.
|
||||
When that happens, that IP you told people to go to will be worthless.
|
||||
Also, that long string of numbers is not very pretty, is it? It's hard
|
||||
to remember and not easy to use in marketing your game. What you need is
|
||||
to alias it to a more sensible domain name - an alias that follows you
|
||||
around also when the IP changes.
|
||||
|
||||
#. To set up a domain name alias, we recommend starting with a free
|
||||
domain name from `FreeDNS <http://freedns.afraid.org/>`_. Once you
|
||||
register there (it's free) you have access to tens of thousands
|
||||
domain names that people have "donated" to allow you to use for your
|
||||
own sub domain. For example, ``strangled.net`` is one of those
|
||||
available domains. So tying our IP address to ``strangled.net`` using
|
||||
the subdomain ``evennia`` would mean that one could henceforth direct
|
||||
people to
|
||||
`http://evennia.strangled.net:8000 <http://evennia.strangled.net:8000>`_
|
||||
for their gaming needs - far easier to remember!
|
||||
#. So how do we make this new, nice domain name follow us also if our IP
|
||||
changes? For this we need to set up a little program on our computer.
|
||||
It will check whenever our ISP decides to change our IP and tell
|
||||
FreeDNS that. There are many alternatives to be found from FreeDNS:s
|
||||
homepage, one that works on multiple platforms is
|
||||
`inadyn <http://www.inatech.eu/inadyn/>`_. Get it from their page or,
|
||||
in Linux, through something like
|
||||
|
||||
::
|
||||
|
||||
apt-get install inadyn
|
||||
|
||||
#. Next, you login to your account on FreeDNS and go to the
|
||||
`Dynamic <http://freedns.afraid.org/dynamic/>`_ page. You should have
|
||||
a list of your subdomains. Click the ``Direct URL`` link and you'll
|
||||
get a page with a text message. Ignore that and look at the URL of
|
||||
the page. It should be ending in a lot of random letters. Everything
|
||||
after the question mark is your unique "hash". Copy this string.
|
||||
#. You now start inadyn with the following command (Linux):
|
||||
|
||||
::
|
||||
|
||||
inadyn --dyndns_system default@freedns.afraid.org -a <my.domain>,<hash> &
|
||||
|
||||
where ``<my.domain>`` would be ``evennia.strangled.net`` and
|
||||
``<hash>`` the string of numbers we copied from FreeDNS. The ``&``
|
||||
means we run in the background (might not be valid in other
|
||||
operating systems). ``inadyn`` will henceforth check for changes
|
||||
every 60 seconds. You should put the ``inadyn`` command string in a
|
||||
startup script somewhere so it kicks into gear whenever your
|
||||
computer starts.
|
||||
|
||||
Remote hosting
|
||||
--------------
|
||||
|
||||
Your normal "web hotel" will probably not be enough to run Evennia. A
|
||||
web hotel is normally aimed at a very specific usage - delivering web
|
||||
pages, at the most with some dynamic content. The "Python scripts" they
|
||||
refer to on their home pages are usually only intended to be CGI-like
|
||||
scripts launched by their webserver. Even if they allow you shell access
|
||||
(so you can install the Evennia dependencies in the first place),
|
||||
resource usage will likely be very restricted. Running a full-fledged
|
||||
game server like Evennia will probably be shunned upon or be outright
|
||||
impossible. If you are unsure, contact your web hotel and ask about
|
||||
their policy on you running third-party servers that will want to open
|
||||
custom ports.
|
||||
|
||||
The options you probably need to look for are *shell account services*
|
||||
or *VPS:es*. A "Shell account" service means that you get a shell
|
||||
account on a server and can log in like any normal user. By contrast, a
|
||||
*VPS* (Virtual Private Server) service usually means that you get
|
||||
``root`` access, but in a virtual machine.
|
||||
|
||||
**Advantages**
|
||||
|
||||
- Shell accounts/VPS offer more flexibility than your average web hotel
|
||||
- it's the ability to log onto a shared computer away from home.
|
||||
- Usually runs a Linux flavor, making it easy to install Evennia.
|
||||
- Support. You don't need to maintain the server hardware. If your
|
||||
house burns down, at least your game stays online. Many services
|
||||
guarantee a certain level of up-time and might also do regular
|
||||
backups for you (this varies).
|
||||
- Gives a fixed domain name, so no need to mess with IP addresses.
|
||||
|
||||
**Disadvantages**
|
||||
|
||||
- Might be pretty expensive (more so than a web hotel)
|
||||
- Linux flavors might feel unfamiliar to users not used to ssh/PuTTy
|
||||
and the Linux command line.
|
||||
- You are probably sharing the server with many others, so you are not
|
||||
completely in charge. CPU usage might be limited. Also, if the server
|
||||
people decides to take the server down for maintenance, you have no
|
||||
choice but to sit it out (but you'll hopefully be warned ahead of
|
||||
time).
|
||||
|
||||
Set up Evennia on a remote shell account/VPS
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Assuming you know how to connect to your account over ssh/PuTTy you
|
||||
should be able to follow the `Getting Started <GettingStarted.html>`_
|
||||
instructions normally. Ports might be an issue, so make sure you know
|
||||
which ports are available to use.
|
||||
|
||||
If you don't have root access in a virtual machine (but just a normal
|
||||
user-shell account), you will probably *not* have all resources easily
|
||||
available. You need root to use ``apt-get`` for example. In that case
|
||||
you should be able set up a virtualenv install instead, see the last
|
||||
section of `Getting Started <GettingStarted.html>`_ for more info.
|
||||
|
||||
To find commercial solutions, just scour the web for shell access/VPS in
|
||||
your region. One user has for example reported some success with
|
||||
`Webfaction <http://www.webfaction.com/>`_.
|
||||
|
||||
Worth checking out is a free hosting offer especially aimed at MUDs
|
||||
`here <http://zeno.biyg.org/portal.php>`_. An account and some activity
|
||||
at `MUDbytes <http://www.mudbytes.net>`_ is required (that's a good
|
||||
forum to join anyway if you are interested in MUDs). On this
|
||||
mud-specific server you can reserve ports to use as well. From their
|
||||
page it's however unclear which resources are available (only gcc is
|
||||
listed), so one probably needs to use a virtualenv setup to get all
|
||||
dependencies.
|
||||
138
docs/sphinx/source/wiki/Quirks.rst
Normal file
138
docs/sphinx/source/wiki/Quirks.rst
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
Evennia Quirks
|
||||
==============
|
||||
|
||||
This is a list of various problems or stumbling blocks that people often
|
||||
ask about or report when using (or trying to use) Evennia. These are
|
||||
common stumbling blocks, non-intuitive behaviour and common newbie
|
||||
mistakes when working with Evennia. They are not bugs.
|
||||
|
||||
Actual Evennia bugs should be reported in
|
||||
`Issues <https://code.google.com/p/evennia/issues/list>`_.
|
||||
|
||||
Editing code directly in \`src/\`
|
||||
---------------------------------
|
||||
|
||||
Don't do this. Doing local changes to ``src`` will eventually conflict
|
||||
with changes done by the Evennia developers. Rather you should import
|
||||
``src``-modules into your own custom modules in ``game/gamesrc`` (or
|
||||
copy&paste them over if you want to expand on the defaults). Next you
|
||||
re-point the relevant links in your ``settings`` file to point to your
|
||||
custom modules instead of the default ones.
|
||||
|
||||
If you want to expand the web interface, copy the entire ``src/web``
|
||||
folder over to ``game/gamesrc`` and change the media links in your
|
||||
``settings`` file.
|
||||
|
||||
If you find that ``src`` lacks some functionality you need, make an
|
||||
`Issue <https://code.google.com/p/evennia/issues/list>`_ of the type
|
||||
*Feature Request*. Or become a part of the Evennia development and
|
||||
submit your own additions to the core.
|
||||
|
||||
Create typeclassed object by calling the typeclass
|
||||
--------------------------------------------------
|
||||
|
||||
Alas, you cannot create a new Typeclass by just initializing the
|
||||
classname. So ``obj = Object()`` won't do what you want. Whereas
|
||||
Evennia's Typeclasses behave *pretty much* like normal Python classes,
|
||||
this is one of the situations where they don't. You need to use
|
||||
Evennia's create functions to add new objects. So use e.g.
|
||||
``ev.create_object()``, ``ev.create_script()`` etc. (these are defined
|
||||
in ``src/utils/create.py``). This will set up the Typeclass database
|
||||
connection behind the scenes.
|
||||
|
||||
In the same vein, you cannot overload ``__init__()``, ``__setattr__`` or
|
||||
``__getattribute__`` on Typeclasses. Use the hook methods
|
||||
(``at_object_creation()`` etc) instead.
|
||||
|
||||
Web admin to create new Player
|
||||
------------------------------
|
||||
|
||||
If you use the default login system and is trying to use the Web admin
|
||||
to create a new Player account, you need to thread carefully. The
|
||||
default login system *requires* the Player object to already have a
|
||||
connected Character object - there is no character creation screen by
|
||||
default. If using the normal mud login screen, a Character with the same
|
||||
name is automatically created and connected to your Player. From the web
|
||||
interface you must do this manually.
|
||||
|
||||
So, when creating the Player, make sure to also create the Character
|
||||
*from the same form* as you create the Player from. This should set
|
||||
everything up for you. Otherwise you need to manually set the "player"
|
||||
property on the Character and the "character" property on the Player to
|
||||
point to each other. You must also set the lockstring of the Character
|
||||
to allow the Player to "puppet" this particular character.
|
||||
|
||||
The default login system is very simple and intended for you to easily
|
||||
get into the game. The more advanced login system in ``contrib/`` along
|
||||
with the example character-creation system does not require such an
|
||||
initial coupling (i.e. you can create the coupling in-game). For your
|
||||
initial experiments, it's easist to create your characters from the
|
||||
normal MUD connection screen instead.
|
||||
|
||||
Mutable attributes and their connection to the database
|
||||
-------------------------------------------------------
|
||||
|
||||
When storing a mutable object (usually a list or a dictionary) in an
|
||||
Attribute
|
||||
|
||||
::
|
||||
|
||||
object.db.mylist = [1,2,3]
|
||||
|
||||
you should know that the connection to the database is retained also if
|
||||
you later extract that Attribute into another variable (what is stored
|
||||
and retrieved is actually a ``PackedList`` or a ``PackedDict`` that
|
||||
works just like their namesakes except they save themselves to the
|
||||
database when changed). So if you do
|
||||
|
||||
::
|
||||
|
||||
alist = object.db.mylist
|
||||
alist.append(4)
|
||||
|
||||
this updates the database behind the scenes, so both ``alist`` and
|
||||
``object.db.mylist`` are now ``[1,2,3,4]``. If you don't want this,
|
||||
convert the mutable object to its normal Python form.
|
||||
|
||||
::
|
||||
|
||||
blist = list(object.db.mylist)
|
||||
blist.append(4)
|
||||
|
||||
The property ``blist`` is now ``[1,2,3,4]`` whereas ``object.db.mylist``
|
||||
remains unchanged. You'd need to explicitly re-assign to the ``mylist``
|
||||
Attribute in order to update the database. If you store nested mutables
|
||||
you only need to convert the "outermost" one in order to "break" the
|
||||
connection to the database like this.
|
||||
|
||||
General shakiness of the web admin
|
||||
----------------------------------
|
||||
|
||||
Since focus has been on getting the underlying python core to work
|
||||
efficiently, the web admin is not quite as stable nor easy to use as
|
||||
we'd like at this point. Also, the web-based html code is, while
|
||||
working, not very pretty or clean. These are areas where we'd much
|
||||
appreciate getting more input and help.
|
||||
|
||||
Known upstream bugs
|
||||
===================
|
||||
|
||||
These are known bugs in in the libraries Evennia uses, i.e. things out
|
||||
of our control.
|
||||
|
||||
Error during manage.py syncdb
|
||||
-----------------------------
|
||||
|
||||
This error can be seen using Django 1.4 without a *locale* set. It
|
||||
causes a traceback during the ``manage.py syncdb`` phase, just when
|
||||
trying to create the superuser.
|
||||
|
||||
::
|
||||
|
||||
TypeError: decode() argument 1 must be string, not None
|
||||
|
||||
This opaque error means no locale could be found. Not properly handling
|
||||
this is a bug in Django 1.4 reported
|
||||
`here <https://code.djangoproject.com/ticket/16017>`_. You resolve it by
|
||||
setting your locale (this is a good thing to have in any case). See the
|
||||
comments to that bug report for how to do this.
|
||||
66
docs/sphinx/source/wiki/RSS.rst
Normal file
66
docs/sphinx/source/wiki/RSS.rst
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
RSS
|
||||
===
|
||||
|
||||
`RSS <http://en.wikipedia.org/wiki/RSS>`_ is a format for easily
|
||||
tracking updates on websites. The principle is simple - whenever a site
|
||||
is updated, a small text file is updated. An RSS reader can then
|
||||
regularly go online, check this file for updates and let the user know
|
||||
what's new.
|
||||
|
||||
Evennia allows for connecting any number of RSS feeds to any number of
|
||||
in-game channels. Updates to the feed will be conveniently echoed to the
|
||||
channel. There are many potential uses for this: For example the MUD
|
||||
might use a separate website to host its forums. Through RSS, the
|
||||
players can then be notified when new posts are made. Another example is
|
||||
to let everyone know you updated your dev blog. Admins might also want
|
||||
to track the latest Evennia updates through our own RSS feed
|
||||
`here <http://code.google.com/feeds/p/evennia/updates/basic>`_.
|
||||
|
||||
Configuring RSS
|
||||
---------------
|
||||
|
||||
To use RSS, you first need to install the
|
||||
`feedparser <http://code.google.com/p/feedparser/>`_ python module. It
|
||||
should be easily available through most distributions as
|
||||
*python-feedparser*, otherwise you can download it directly.
|
||||
|
||||
Next you activate RSS support in your config file by settting
|
||||
``RSS_ENABLED=True``.
|
||||
|
||||
Start/reload Evennia as a privileged user. You should now have a new
|
||||
command available, ``@rss2chan``:
|
||||
|
||||
::
|
||||
|
||||
@rss2chan <evennia_channel> = <rss_url>
|
||||
|
||||
Setting up RSS, step by step
|
||||
----------------------------
|
||||
|
||||
You can connect RSS to any Evennia channel, but for testing, let's set
|
||||
up a new channel "rss".
|
||||
|
||||
::
|
||||
|
||||
@ccreate rss = RSS feeds are echoed to this channel!
|
||||
|
||||
Let's connect Evennia's code-update feed to this channel. Its full url
|
||||
is ``http://code.google.com/feeds/p/evennia/updates/basic``.
|
||||
|
||||
::
|
||||
|
||||
@rss2chan rss = http://code.google.com/feeds/p/evennia/updates/basic
|
||||
|
||||
That's it, really. New Evennia updates will now show up as a one-line
|
||||
title and link in the channel. Give the ``@rss2chan`` command on its own
|
||||
to show all connections. To remove a feed from a channel, you specify
|
||||
the connection again (see the list) but add the ``/delete`` switch:
|
||||
|
||||
::
|
||||
|
||||
@rss2chan/delete rss = http://code.google.com/feeds/p/evennia/updates/basic
|
||||
|
||||
You can connect any number of RSS feeds to a channel this way. You could
|
||||
also connect them to the same channels as `IRC <IRC.html>`_ and/or
|
||||
`IMC2 <IMC2.html>`_ to have the feed echo to external chat channels as
|
||||
well.
|
||||
105
docs/sphinx/source/wiki/ServerConf.rst
Normal file
105
docs/sphinx/source/wiki/ServerConf.rst
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
Evennia Server Configurations
|
||||
=============================
|
||||
|
||||
Evennia runs out of the box without any changes to its settings. But
|
||||
there are several important ways to customize the server and expand it
|
||||
with your own plugins.
|
||||
|
||||
Settings file
|
||||
-------------
|
||||
|
||||
The "Settings" file referenced throughout the documentation is the file
|
||||
``game/settings.py``. This is automatically created on the first run of
|
||||
``manage.py syncdb`` (see the `GettingStarted <GettingStarted.html>`_
|
||||
page). The settings file is actually a normal Python module. It's pretty
|
||||
much empty from the start, it just imports all default values from
|
||||
``src/settings_default.py`` into itself.
|
||||
|
||||
You should never edit ``src/settings_default.py``. Rather you should
|
||||
copy&paste the variables you want to change into ``settings.py`` and
|
||||
edit them there. This will overload the previously imported defaults.
|
||||
|
||||
In code, the settings is accessed through
|
||||
|
||||
::
|
||||
|
||||
from django.conf import settings
|
||||
# or (shorter):
|
||||
from ev import settings
|
||||
#
|
||||
servername = settings.SERVER_NAME
|
||||
|
||||
Each setting appears as a property on the imported ``settings`` object.
|
||||
You can also explore all possible options with ``ev.settings_full``
|
||||
(this also includes advanced Django defaults that are not not touched in
|
||||
default Evennia).
|
||||
|
||||
It should be pointed out that when importing ``settings`` into your code
|
||||
like this, it will be *read only*. You cannot edit your settings in
|
||||
code. The only way to change an Evennia setting is to edit
|
||||
``game/settings.py`` directly. You most often need to restart the server
|
||||
(possibly also the Portal) before a changed setting becomes available.
|
||||
|
||||
\`game/gamesrc/conf\` directory
|
||||
-------------------------------
|
||||
|
||||
The ``game/gamesrc/conf/`` directory contains module templates for
|
||||
customizing Evennia. Common for all these is that you should *copy* the
|
||||
template up one level (to ``game/gamesrc/conf/``) and edit the copy, not
|
||||
the original. You then need to change your settings file to point the
|
||||
right variable at your new module. Each template header describes
|
||||
exactly how to use it and which settings variable needs to be changed
|
||||
for Evennia to be able to locate it.
|
||||
|
||||
- ``at_initial_setup.py`` - this allows you to add a custom startup
|
||||
method to be called (only) the very first time Evennia starts (at the
|
||||
same time as user #1 and Limbo is created). It can be made to start
|
||||
your own global scripts or set up other system/world-related things
|
||||
your game needs to have running from the start.
|
||||
- ``at_server_startstop.py`` - this module contains two functions that
|
||||
Evennia will call every time the Server starts and stops respectively
|
||||
- this includes stopping due to reloading and resetting as well as
|
||||
shutting down completely. It's a useful place to put custom startup
|
||||
code for handlers and other things that must run in your game but
|
||||
which has no database persistence.
|
||||
- ``connection_screens.py`` - all global string variables in this
|
||||
module are interpreted by Evennia as a greeting screen to show when a
|
||||
Player first connects. If more than one string variable is present in
|
||||
the module a random one will be picked.
|
||||
- ``lockfuncs.py`` - this is one of many possible modules to hold your
|
||||
own "safe" *lock functions* to make available to Evennia's `lock
|
||||
system <Locks.html>`_.
|
||||
- ``mssp.py`` - this holds meta information about your game. It is used
|
||||
by MUD search engines (which you often have to register with) in
|
||||
order to display what kind of game you are running along with
|
||||
statistics such as number of online players and online status.
|
||||
|
||||
Some other Evennia systems can be customized by plugin modules but has
|
||||
no explicit template in ``conf/examples``:
|
||||
|
||||
- *command parser* - a custom module can be used to totally replace
|
||||
Evennia's default command parser. All this does is to split the
|
||||
incoming string into "command name" and "the rest". It also handles
|
||||
things like error messages for no-matches and multiple-matches among
|
||||
other things that makes this more complex than it sounds. The default
|
||||
parser is *very* generic, so you are most often best served by
|
||||
modifying things further down the line (on the command parse level)
|
||||
than here.
|
||||
- *search-return handler* - this can be used to replace how Evennia
|
||||
handles search results from most of Evennia's in-game searches (most
|
||||
importantly ``self.caller.search`` in commands). It handles the
|
||||
echoing of errors.
|
||||
- *multimatch handler* - this plugin replaces the handling of multiple
|
||||
match errors in searching. By default it allows for separating
|
||||
between same-named matches by use of numbers. Like understanding that
|
||||
"2-ball" should match the second "ball" object if there are two of
|
||||
them.
|
||||
|
||||
!ServerConf
|
||||
-----------
|
||||
|
||||
There is a special database model called ServerConf that stores server
|
||||
internal data and settings such as current player count (for interfacing
|
||||
with the webserver), startup status and many other things. It's rarely
|
||||
of use outside the server core itself but may be good to know about if
|
||||
you are an Evennia developer.
|
||||
224
docs/sphinx/source/wiki/TickerScripts.rst
Normal file
224
docs/sphinx/source/wiki/TickerScripts.rst
Normal file
|
|
@ -0,0 +1,224 @@
|
|||
Ticker Scripts ("Heartbeats")
|
||||
=============================
|
||||
|
||||
A common way to implement a dynamic MUD is by using "tickers", also
|
||||
known as "heartbeats". Tickers are very common or even unavoidable in
|
||||
other mud code bases, where many systems are hard coded to rely on the
|
||||
concept of the global 'tick'. In Evennia this is very much up to your
|
||||
game and which requirements you have. `Scripts <Scripts.html>`_ are
|
||||
powerful enough to act as any type of counter you want.
|
||||
|
||||
When \_not\_ to use tickers
|
||||
---------------------------
|
||||
|
||||
Even if you are used to habitually relying on tickers in other code
|
||||
bases, stop and think about what you really need them for. Notably you
|
||||
should think very, *very* hard before implementing a ticker to *catch
|
||||
changes in something that rarely changes*. Think about it - you might
|
||||
have to run the ticker every second to react to changes fast enough.
|
||||
This means that most of the time *nothing* will have changed. You are
|
||||
doing pointless calls (since skipping the call gives the same result as
|
||||
doing it). Making sure nothing's changed might even be a tad expensive
|
||||
depending on the complexity of your system. Not to mention that you
|
||||
might need to run the check on every object in the database. Every
|
||||
second. Just to maintain status quo.
|
||||
|
||||
Rather than checking over and over on the off-chance that something
|
||||
changed, consider a more proactive approach. Can you maybe implement
|
||||
your rarely changing system to *itself* report its change *whenever* it
|
||||
happens? It's almost always much cheaper/efficient if you can do things
|
||||
"on demand". Evennia itself uses hook methods all over for this very
|
||||
reason. The only real "ticker"-like thing in the default set is the one
|
||||
that saves the uptime (which of course *always* changes every call).
|
||||
|
||||
So, in summary, if you consider a ticker script that will fire very
|
||||
often but which you expect to do nothing 99% of the time, ponder if you
|
||||
can handle things some other way.
|
||||
|
||||
Ticker example - night/day system
|
||||
=================================
|
||||
|
||||
Let's take the example of a night/day system. The way we want to use
|
||||
this is to have all outdoor rooms echo time-related messages to the
|
||||
room. Things like "The sun sets", "The church bells strike midnight" and
|
||||
so on.
|
||||
|
||||
One could imagine every `Room <Objects.html>`_ in the game having a
|
||||
script running on themselves that fire regularly. It's however much
|
||||
better (easier to handle and using a lot less computing resources) to
|
||||
use a single, global, ticker script. You create a "global" Script the
|
||||
way you would any Script except you don't assign it to any particular
|
||||
object.
|
||||
|
||||
To let objects use this global ticker, we will utilize a *subscription
|
||||
model*. In short this means that our Script holds an internal list of
|
||||
"subscribing" rooms. Whenever the Script fires it loops through this
|
||||
list and calls a given method on the subscribed object.
|
||||
|
||||
::
|
||||
|
||||
from ev import Script
|
||||
class TimeTicker(Script):
|
||||
"""
|
||||
This implements a subscription model
|
||||
"""
|
||||
def at_script_creation(self):
|
||||
"Called when script is created"
|
||||
self.key = "daynight_ticker"
|
||||
self.interval = 60 * 60 * 2 # every two hours
|
||||
self.persistent = True
|
||||
# storage of subscriptions
|
||||
self.db.subscriptions = []
|
||||
def subscribe(self, obj):
|
||||
"add object to subscription"
|
||||
if obj not in self.db.subscriptions:
|
||||
self.db.subscriptions.append(obj)
|
||||
def unsubscribe(self, obj):
|
||||
"remove object from subscription"
|
||||
try:
|
||||
del_ind = self.db.subscriptions.index(obj)
|
||||
del self.db.subscriptions[del_ind]
|
||||
except ValueError:
|
||||
pass
|
||||
def list_subscriptions(self):
|
||||
"echo all subscriptions"
|
||||
return self.db.subscriptions
|
||||
def at_repeat(self):
|
||||
"called every self.interval seconds"
|
||||
for obj in self.db.subscriptions:
|
||||
obj.echo_daynight()
|
||||
|
||||
This depends on your subscribing weather rooms defining the
|
||||
``echo_daynight()`` method (presumably outputting some sort of message).
|
||||
|
||||
It's worth noting that this simple recipe can be used for all sorts of
|
||||
tickers objects. Rooms are maybe not likely to unsubscribe very often,
|
||||
but consider a mob that "deactivates" when Characters are not around for
|
||||
example.
|
||||
|
||||
The above TimeTicker-example could be further optimized. All subscribed
|
||||
rooms are after all likely to echo the same time related text. So this
|
||||
text can be pre-set already at the Script level and echoed to each room
|
||||
directly. This way the subscribed objects won't need a custom
|
||||
``echo_daynight()`` method at all.
|
||||
|
||||
Here's the more efficient example (only showing the new stuff).
|
||||
|
||||
::
|
||||
|
||||
...
|
||||
ECHOES = ["The sun rises in the east.", "It's mid-morning",
|
||||
"It's mid-day", ...]
|
||||
class TimerTicker(Script):
|
||||
...
|
||||
def at_script_creation(self):
|
||||
...
|
||||
self.db.timeindex = 0
|
||||
...
|
||||
def at_repeat(self):
|
||||
"called every self.repeat seconds"
|
||||
echo = ECHOES[self.db.timeindex]
|
||||
# msg_contents() is a standard method, so this
|
||||
# ticker works with any object.
|
||||
for obj in self.db.subscriptions:
|
||||
obj.msg_contents(echo)
|
||||
# resetting/stepping the counter
|
||||
if self.db.timeindex == len(ECHOES) - 1:
|
||||
self.db.timeindex = 0
|
||||
else:
|
||||
self.db.timeindex += 1
|
||||
|
||||
Note that this ticker is unconnected to Evennia's default global in-game
|
||||
time script, and will thus be out of sync with that. A more advanced
|
||||
example would entail this script checking the current game time (in
|
||||
``at_script_creation()`` or in ``at_start()``) so it can pick a matching
|
||||
starting point in its cycle.
|
||||
|
||||
Testing the night/day ticker
|
||||
----------------------------
|
||||
|
||||
Tickers are really intended to be created and handled from your custom
|
||||
commands or in other coded systems. An "outdoor" room typeclass would
|
||||
probably subscribe to the ticker itself from its
|
||||
``at_object_creation()`` hook. Same would be true for mobs and other
|
||||
objects that could respond to outside stimuli (such as the presence of a
|
||||
player) in order to subscribe/unsubscribe.
|
||||
|
||||
There is no way to create a global script using non-superuser commands,
|
||||
and even if you could use ``@script`` to put it on an object just to
|
||||
test things out, you also need a way to subscribe objects to it.
|
||||
|
||||
With ``@py`` this would be something like this:
|
||||
|
||||
::
|
||||
|
||||
@py ev.create_script(TimeTicker) # if persistent=True, this only needs to be done once
|
||||
@py ev.search_script("daynight_ticker").subscribe(self.location)
|
||||
|
||||
|
||||
If you think you will use these kind of ticker scripts a lot, you might
|
||||
want to create your own command for adding/removing subscriptions to
|
||||
them. Here is a complete example:
|
||||
|
||||
::
|
||||
|
||||
import ev
|
||||
class CmdTicker(ev.default_cmds.MuxCommand):
|
||||
"""
|
||||
adds/remove an object to a given ticker
|
||||
|
||||
Usage:
|
||||
@ticker[/switches] tickerkey [= object]
|
||||
Switches:
|
||||
add (default) - subscribe object to ticker
|
||||
del - unsubscribe object from ticker
|
||||
|
||||
This adds an object to a named ticker Script,
|
||||
if such a script exists. Such a script must have
|
||||
subsribe/unsubscripe functionality. If no object is
|
||||
supplied, a list of subscribed objects for this ticker
|
||||
will be returned instead.
|
||||
"""
|
||||
key = "@ticker"
|
||||
locks = "cmd:all()"
|
||||
help_category = "Building"
|
||||
|
||||
def func(self):
|
||||
if not self.args:
|
||||
self.caller.msg("Usage: @ticker[/switches] tickerkey [= object]")
|
||||
return
|
||||
tickerkey = self.lhs
|
||||
# find script
|
||||
script = ev.search_scripts(tickerkey)
|
||||
if not script:
|
||||
self.caller.msg("Ticker %s could not be found." % tickerkey)
|
||||
return
|
||||
# all ev.search_* methods always return lists
|
||||
script = script[0]
|
||||
# check so the API is correct
|
||||
if not (hasattr(script, "subscribe")
|
||||
and hasattr(script, "unsubscribe")
|
||||
and hasattr(script, "list_subscriptions"):
|
||||
self.caller.msg("%s can not be subscribed to." % tickerkey)
|
||||
return
|
||||
if not self.rhs:
|
||||
# no '=' found, just show the subs
|
||||
subs = [o.key for o in script.list_subscripionts()]
|
||||
self.caller.msg(", ".join(subs))
|
||||
return
|
||||
# get the object to add
|
||||
obj = self.caller.search(self.rhs)
|
||||
if not obj:
|
||||
# caller.search handles error messages
|
||||
return
|
||||
elif 'del' in self.switches:
|
||||
# remove a sub
|
||||
script.unsubscribe(obj)
|
||||
self.caller.msg("Unsubscribed %s from %s." % (obj.key, tickerkey)
|
||||
else:
|
||||
# default - add subscription
|
||||
script.subscribe(obj)
|
||||
self.caller.msg("Subscribed %s to ticker %s." % (obj.key, tickerkey))
|
||||
|
||||
This looks longer than it is, most of the length comes from comments and
|
||||
the doc string.
|
||||
33
docs/sphinx/source/wiki/Tutorials.rst
Normal file
33
docs/sphinx/source/wiki/Tutorials.rst
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
Evennia Tutorials
|
||||
=================
|
||||
|
||||
This is a summary of Evennia documentation available on a step-by-step
|
||||
or tutorial-like format.
|
||||
|
||||
Building
|
||||
--------
|
||||
|
||||
- `Tutorial: Building Quick-start <BuildingQuickstart.html>`_
|
||||
|
||||
Coding basics
|
||||
-------------
|
||||
|
||||
- `Tutorial: Adding a new default
|
||||
command <AddingCommandTutorial.html>`_
|
||||
|
||||
Implementation ideas
|
||||
--------------------
|
||||
|
||||
- `Tutorial: Removing Colour from your game (typeclass method
|
||||
overloading) <RemovingColour.html>`_
|
||||
- `Tutorial: Adding a Command prompt <CommandPrompt.html>`_
|
||||
- `Tutorial: Creating a Zoning system <Zones.html>`_
|
||||
- `Hints: Implementing cooldowns for commands <CommandCooldown.html>`_
|
||||
- `Hints: Ticker Scripts <TickerScripts.html>`_
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
- `The Tutorial
|
||||
World <http://code.google.com/p/evennia/wiki/TutorialWorldIntroduction>`_
|
||||
|
||||
257
docs/sphinx/source/wiki/VersionControl.rst
Normal file
257
docs/sphinx/source/wiki/VersionControl.rst
Normal file
|
|
@ -0,0 +1,257 @@
|
|||
Setting up a coding environment with version control
|
||||
====================================================
|
||||
|
||||
Version control software allows you to easily backtrack changes to your
|
||||
code, help with sharing your development efforts and more. Even if you
|
||||
are not contributing to Evennia itself, but is "just" developing your
|
||||
own game, having a version control system in place is a good idea. If
|
||||
you want more info, start with the wikipedia article about it
|
||||
`here <http://en.wikipedia.org/wiki/Version_control>`_. Note that this
|
||||
page deals with commands in the Linux operating system. Details may vary
|
||||
for other systems.
|
||||
|
||||
Note: This is only one suggested way to use Mercurial by using separate
|
||||
local clones. You could set up any number of different workflows if it
|
||||
suited you better. See `here <http://mercurial.selenic.com/guide/>`_ for
|
||||
some more examples.
|
||||
|
||||
Using Mercurial
|
||||
===============
|
||||
|
||||
`Mercurial <http://mercurial.selenic.com/>`_ (abbreviated as ``hg``
|
||||
after the chemical symbol for mercury) is a version control system
|
||||
written mainly in Python. It's available for all major platforms.
|
||||
|
||||
First, identify to mercurial by creating a new file ``.hgrc`` in your
|
||||
home directory and put the following content in it:
|
||||
|
||||
::
|
||||
|
||||
[ui]
|
||||
username = MyName <myemail@mail.com>
|
||||
|
||||
You can put a nickname here too if you want. This is just so the system
|
||||
knows how to credit new revisions.
|
||||
|
||||
Setting up
|
||||
----------
|
||||
|
||||
We will here assume you are downloading Evennia for the first time. We
|
||||
will set up a simple environment for hacking your game in. In the end it
|
||||
will look like this:
|
||||
|
||||
::
|
||||
|
||||
evennia/
|
||||
evennia-main
|
||||
evennia-mygame
|
||||
|
||||
Create a new folder ``evennia`` and clone Evennia into it as
|
||||
``evennia-main``:
|
||||
|
||||
::
|
||||
|
||||
hg clone https://code.google.com/p/evennia/ evennia-main
|
||||
|
||||
A new folder ``evennia-main`` has appeared. In it you will find the
|
||||
entire Evennia source repository, including all its commit history -
|
||||
it's really a full copy of what's available on the web.
|
||||
|
||||
We'll let ``evennia-main`` only contain the "clean" Evennia install -
|
||||
it's a good debugging tool to tell you if a bug you find is due to your
|
||||
changes or also visible in the core server. We'll develop our game in
|
||||
another repository instead:
|
||||
|
||||
::
|
||||
|
||||
hg clone evennia-main evennia-mygame
|
||||
|
||||
This will create a new repository ``evennia-mygame`` on your machine. In
|
||||
this directory you now code away, adding and editing things to your
|
||||
heart's content to make your dream game.
|
||||
|
||||
Example work flow
|
||||
-----------------
|
||||
|
||||
First we make sure our copy of Evennia is up-to-date. Go to
|
||||
``evennia-main``:
|
||||
|
||||
::
|
||||
|
||||
cd evennia-main
|
||||
hg pull
|
||||
|
||||
Mercurial goes online and gets the latest Evennia revision from the
|
||||
central repository, merging it automatically into your repository. It
|
||||
will tell you that you need to ``update`` to incoorporate the latest
|
||||
changes. Do so.
|
||||
|
||||
::
|
||||
|
||||
hg update
|
||||
|
||||
So ``evennia-main`` is now up-to-date. If you want, you can review the
|
||||
changes and make sure things work as they should. Finally go to
|
||||
``evennia-mygame`` and pull the changes into that as well.
|
||||
|
||||
::
|
||||
|
||||
|
||||
cd ../evennia-mygame
|
||||
hg commit # (only if you had any changes)
|
||||
hg pull ../evennia-main
|
||||
hg update
|
||||
|
||||
You can now continue to hack away in ``evennia-mygame`` to build your
|
||||
game. Maybe you define new commands, economic systems, create batchfiles
|
||||
or what have you. If you create any new files, you must tell Mercurial
|
||||
to track them by using the ``add`` command:
|
||||
|
||||
::
|
||||
|
||||
hg add <filename(s)>
|
||||
|
||||
Check the current status of the version control with
|
||||
|
||||
::
|
||||
|
||||
hg status
|
||||
|
||||
If you don't get any return value, you haven't made any changes since
|
||||
last commit. Otherwise you will get a list of modified files.
|
||||
|
||||
It's usually a good idea to commit your changes often - it's fast and
|
||||
only local - you will never commit anything online. This gives you a
|
||||
"save" snapshot of your work that you can get back to.
|
||||
|
||||
::
|
||||
|
||||
hg commit
|
||||
|
||||
This will open a text editor where you can add a message detailing your
|
||||
changes. These are the messages you see in the Evennia update/log list.
|
||||
If you don't want to use the editor you can set the message right away
|
||||
with the ``-m`` flag:
|
||||
|
||||
::
|
||||
|
||||
hg commit -m "This should fix the bug Sarah talked about."
|
||||
|
||||
If you did changes that you wish you hadn't, you can easily get rid of
|
||||
everything since your latest commit:
|
||||
|
||||
::
|
||||
|
||||
hg revert --all
|
||||
|
||||
Instead of ``--all`` you can also choose to revert individual files.
|
||||
|
||||
You can view the full log of committed changes with
|
||||
|
||||
::
|
||||
|
||||
hg log
|
||||
|
||||
See the Mercurial manuals for learning more about useful day-to-day
|
||||
commands, and special situations such as dealing with text collisions
|
||||
etc.
|
||||
|
||||
Sharing your code with the world
|
||||
================================
|
||||
|
||||
The most common case of this is when you have fixed an Evennia bug and
|
||||
want to make the fix available to Evennia maintainers. But you can also
|
||||
share your work with other people on your game-development team if you
|
||||
don't worry about the changes being publicly visible.
|
||||
|
||||
Let's take the example of debugging Evennia. Go online and create an
|
||||
"online clone" of Evennia as described `here <Contributing.html>`_. Pull
|
||||
this repo to your local machine -- so if your clone is named
|
||||
``my-evennia-fixes``, you do something like this:
|
||||
|
||||
::
|
||||
|
||||
hg clone https://<yourname>@code.google.com/r/my-evennia-fixes evennia-fixes
|
||||
|
||||
You will now have a new folder ``evennia-fixes``. Let's assume we want
|
||||
to use this to push bug fixes to Evennia. It works like any other
|
||||
mercurial repository except you also have push-rights to your online
|
||||
clone from it. When working, you'd first update it to the latest
|
||||
upstream Evennia version:
|
||||
|
||||
::
|
||||
|
||||
cd evennia-main
|
||||
hg pull
|
||||
hg update
|
||||
cd ../evennia-fixes
|
||||
hg pull ../evennia-main
|
||||
hg update
|
||||
|
||||
Now you fix things in ``evennia-fixes``. Commit your changes as
|
||||
described above. Make sure to make clear and descriptive commit messages
|
||||
so it's easy to see what you intended. You can do any number of commits
|
||||
as you work. Once you are at a stage where you want to show what you did
|
||||
to the world, you push all the so-far committed changes to your online
|
||||
clone:
|
||||
|
||||
::
|
||||
|
||||
hg push
|
||||
|
||||
(You'd next need to tell Evennia devs that they should merge your
|
||||
brilliant changes into Evennia proper. Create a new
|
||||
`Issue <https://code.google.com/p/evennia/issues/list>`_ of type *Merge
|
||||
Request*, informing them of this.)
|
||||
|
||||
Apart from supporting Evennia itself you can have any number of online
|
||||
clones for different purposes, such as sharing game code or collaborate
|
||||
on solutions. Just pull stuff from whichever relevant local repository
|
||||
you have (like ``evennia-mygame``) and push to a suitably named online
|
||||
clone so people can get to it.
|
||||
|
||||
Sharing your code only with a small coding team
|
||||
===============================================
|
||||
|
||||
Creating a publicly visible online clone might not be what you want for
|
||||
all parts of your development process - you may prefer a more private
|
||||
venue when sharing your revolutionary work with your team.
|
||||
|
||||
An online hosting provider offering private repositories is probably
|
||||
your best bet. For example, if all your contributors are registered on
|
||||
`BitBucket <https://bitbucket.org/>`_, that service offers free
|
||||
"private" repositories that you could use for this.
|
||||
|
||||
An alternative simple way to share your work with a limited number of
|
||||
people is to use mercurial's own simple webserver and let them connect
|
||||
directly to your machine:
|
||||
|
||||
::
|
||||
|
||||
cd evennia-mygame
|
||||
hg serve -p 8500
|
||||
|
||||
(the port was changed because the default port is 8000 and that is
|
||||
normally used by Evennia's own webserver). Find out the IP address of
|
||||
your machine visible to the net (make sure you know your firewall setup
|
||||
etc). Your collaborators will then be able to first review the changes
|
||||
in their browser:
|
||||
|
||||
::
|
||||
|
||||
firefox http://192.168.178.100:8500
|
||||
|
||||
and pull if they like what they see:
|
||||
|
||||
::
|
||||
|
||||
hg pull http://192.168.178.100:8500
|
||||
|
||||
See `here <http://mercurial.selenic.com/wiki/hgserve>`_ for more
|
||||
information on using ``hg serve``.
|
||||
|
||||
Mercurial's in-built webserver is *very* simplistic and not particularly
|
||||
robust. It only allows one connection at a time, lacks authorization and
|
||||
doesn't even allow your collaborators to ``push`` data to you (there is
|
||||
nothing stopping them to set up a server of their own so you can pull
|
||||
from them though).
|
||||
141
docs/sphinx/source/wiki/Zones.rst
Normal file
141
docs/sphinx/source/wiki/Zones.rst
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
Zones
|
||||
=====
|
||||
|
||||
Problem
|
||||
-------
|
||||
|
||||
Say you create a room named *Meadow* in your nice big forest MUD. That's
|
||||
all nice and dandy, but what if you, in the other end of that forest
|
||||
want another *Meadow*? As a game creator, this can cause all sorts of
|
||||
confusion. For example, teleporting to *Meadow* will now give you a
|
||||
warning that there are two *Meadow* s and you have to select which one.
|
||||
It's no problem to do that, you just choose for example to go to
|
||||
``2-meadow``, but unless you examine them you couldn't be sure which of
|
||||
the two sat in the magical part of the forest and which didn't.
|
||||
|
||||
Another issue is if you want to group rooms in geographic regions for
|
||||
example. Let's say the "normal" part of the forest should have separate
|
||||
weather patterns from the magical part. Or maybe a magical disturbance
|
||||
echoes through all magical-forest rooms. It would then be convenient to
|
||||
be able to simply find all rooms that are "magical" so you could send
|
||||
messages to them.
|
||||
|
||||
Zones in Evennia
|
||||
----------------
|
||||
|
||||
*Zones* try to separate rooms by global location. In our example we
|
||||
would separate the forest into two parts - the magical and the
|
||||
non-magical part. Each have a *Meadow* and rooms belonging to each part
|
||||
should be easy to retrieve.
|
||||
|
||||
Many MUD codebases hardcode zones as part of the engine and database.
|
||||
Evennia does no such distinction due to the fact that rooms themselves
|
||||
are meant to be customized to any level anyway. Below is just *one*
|
||||
example of how one could add zone-like functionality to a game.
|
||||
|
||||
All objects have a *key* property, stored in the database. This is the
|
||||
primary name of the object. But it can also have any number of *Aliases*
|
||||
connected to itself. This allows Players to refer to the object using
|
||||
both key and alias - a "big red door" can also be referred to as the
|
||||
alias "door". Aliases are actually separate database entities and are as
|
||||
such very fast to search for in the database, about as fast as searching
|
||||
for the object's primary key in fact.
|
||||
|
||||
This makes Aliases prime candiates for implementing zones. All you need
|
||||
to do is to come up with a consistent aliasing scheme. Here's one
|
||||
suggestion:
|
||||
|
||||
::
|
||||
|
||||
#zonename|key
|
||||
|
||||
So say we (arbitrarily) divide our forest example into the zones
|
||||
``magicforest`` and ``normalforest``. These are the added aliases we use
|
||||
for the respective *Meadow* 's:
|
||||
|
||||
::
|
||||
|
||||
#magicforest|meadow
|
||||
#normalforest|meadow
|
||||
|
||||
The primary key of each will still be *Meadow*, and players will still
|
||||
see that name. We can also add any number of other Aliases to each
|
||||
meadow if we want. But we will also henceforth always be able to
|
||||
uniquely identify the right meadow by prepending its primary key name
|
||||
with ``#zonename|``.
|
||||
|
||||
Enforcing zones
|
||||
---------------
|
||||
|
||||
Maybe you feel that this usage of aliases for zones is somehow loose and
|
||||
ad-hoc. It is, and there is no guarantee that a builder would follow the
|
||||
naming convention - unless you force them. And you can do that easily by
|
||||
changing for example the ``@dig`` `Command <Commands.html>`_ to require
|
||||
the zone to be given:
|
||||
|
||||
::
|
||||
|
||||
@dig zone|roomname:typeclass = north;n, south;s
|
||||
|
||||
Just have the ``@dig`` command auto-add an alias of the correct format
|
||||
and hey-presto! A functioning zone system! An even more convenient way
|
||||
to enforce zones would be for the new room to inherit the zone from the
|
||||
room we are building from.
|
||||
|
||||
Overload the default ``search`` method on a typeclass for further
|
||||
functionality:
|
||||
|
||||
::
|
||||
|
||||
def search(self, ostring, zone=None, *args, **kwargs):
|
||||
if zone:
|
||||
ostring = "#%s|%s" % (ostring, zone)
|
||||
return self.dbobj.search(ostring, *args, **kwargs)
|
||||
|
||||
You will then be able to do, from commands:
|
||||
|
||||
::
|
||||
|
||||
meadow_obj = self.caller.search("meadow", zone="magicforest")
|
||||
|
||||
and be sure you are getting the magical meadow, not the normal one.
|
||||
|
||||
You could also easily build search queries searching only for rooms with
|
||||
aliases starting with ``#magicforest|``. This would allow for easy
|
||||
grouping and retrieving of all rooms in a zone for whatever need to
|
||||
have.
|
||||
|
||||
Evennia's open solution to zones means that you have much more power
|
||||
than in most MUD systems. There is for example no reason why you have to
|
||||
group and organize only rooms with this scheme.
|
||||
|
||||
Using typeclasses and inheritance for zoning
|
||||
--------------------------------------------
|
||||
|
||||
The above alias scheme is basically a way to easily find and group
|
||||
objects together at run-time - a way to label, if you will. It doesn't
|
||||
instill any sort of functional difference between a magical forest room
|
||||
and a normal one. For this you will need to use Typeclasses. If you know
|
||||
that a certain typeclass of room will always be in a certain zone you
|
||||
could even hard-code the zone in the typeclass rather than enforce the
|
||||
``@dig`` command to do it:
|
||||
|
||||
::
|
||||
|
||||
class MagicalForestRoom(Room)
|
||||
def at_object_creation(self):
|
||||
...
|
||||
self.aliases = "#magicforest|%s" % self.key
|
||||
...
|
||||
class NormalForestRoom(Room)
|
||||
def at_object_creation(self):
|
||||
...
|
||||
self.aliases = "#normalforest|%s" % self.key
|
||||
...
|
||||
|
||||
Of course, an alternative way to implement zones themselves is to have
|
||||
all rooms/objects in a zone inherit from a given typeclass parent - and
|
||||
then limit your searches to objects inheriting from that given parent.
|
||||
The effect would be the same and you wouldn't need to implement any
|
||||
ad-hoc aliasing scheme; but you'd need to expand the search
|
||||
functionality to properly search the inheritance tree.
|
||||
105
docs/sphinx/source/wiki/evAPI.rst
Normal file
105
docs/sphinx/source/wiki/evAPI.rst
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
\`ev\` - Evennia's flat API
|
||||
===========================
|
||||
|
||||
Evennia consists of many components, some of which interact in rather
|
||||
complex ways. One such example is the Typeclass system which is
|
||||
implemented across four different folders in ``src/``. This is for
|
||||
efficiency reasons and to avoid code duplication, but it means that it
|
||||
can be a bit of a hurdle to understand just what connects to what and
|
||||
which properties are actually available/inherited on a particular game
|
||||
entity you want to use.
|
||||
|
||||
Evennia's ``ev`` API (Application Programming Interface) tries to help
|
||||
with this. ``ev.py`` sits in evennia's root directory which means you
|
||||
can import it from your code simply with ``import ev``. The ``ev``
|
||||
module basically implements shortcuts to the innards of ``src/``. The
|
||||
goal is to give a very "flat" structure (as opposed to a deeply nested
|
||||
one). Not only is this a Python recommendation, it also makes it much
|
||||
easier to see what you have.
|
||||
|
||||
Exploring \`ev\`
|
||||
----------------
|
||||
|
||||
To check out evennia interactively, it's recommended you use a more
|
||||
advanced Python interpreter, like `ipython <http://ipython.org/>`_. With
|
||||
ipython you can easily read module headers and help texts as well as
|
||||
list possible completions.
|
||||
|
||||
Start a python interactive shell, then get ``ev``:
|
||||
|
||||
::
|
||||
|
||||
import ev
|
||||
|
||||
In ipython we can now do for example ``ev?`` to read the API's help
|
||||
text. Using eg. ``ev.Object?`` will read the documentation for the
|
||||
``Object`` typeclass. Use ``??`` to see source code. Tab on ``ev.`` to
|
||||
see what ``ev`` contains.
|
||||
|
||||
Some highlights
|
||||
---------------
|
||||
|
||||
- ``Object, Player, Script, Room, Character, Exit`` - direct links to
|
||||
the most common base classes in Evennia.
|
||||
- ``search_*`` - shortcuts to the search functions in
|
||||
``src.utils.search``, such as ``search_object()`` or
|
||||
``search_script()``
|
||||
- ``create_*`` - are convenient links to all object-creation functions.
|
||||
Note that all Typeclassed objects *must* be created with methods such
|
||||
as these (or their parents in ``src.utils.create``) to make
|
||||
Typeclasses work.
|
||||
- ``managers`` - this is a container object that groups shortcuts to
|
||||
initiated versions of Evennia's django *managers*. So
|
||||
``managers.objects`` is in fact equivalent to ``ObjectDB.objects``
|
||||
and you can do ``managers.objects.all()`` to get a list of all
|
||||
database objects. The managers allows to explore the database in
|
||||
various ways. To use, do ``from ev import manager`` and access the
|
||||
desired manager on the imported ``managers`` object.
|
||||
- default\_cmds - this is a container on ``ev`` that groups all default
|
||||
commands and command sets under itself. Do
|
||||
``from ev import default_cmds`` and you can then access any default
|
||||
command from the imported ``default_cmds`` object.
|
||||
- ``utils, logger, gametime, ansi`` are various utilities. Especially
|
||||
utils contains many useful functions described
|
||||
`here <CodingUtils.html>`_.
|
||||
- ``syscmdkeys`` is a container that holds all the system-command keys
|
||||
needed to define system commands. Similar to the ``managers``
|
||||
container, you import this and can then access the keys on the
|
||||
imported ``syscmdkeys`` object.
|
||||
|
||||
To remember when importing from \`ev\`
|
||||
--------------------------------------
|
||||
|
||||
Properties on ``ev`` are *not* modules in their own right. They are just
|
||||
shortcut properties stored in the ``ev.py`` module. That means that you
|
||||
cannot use dot-notation to ``import`` nested module-names over ``ev``.
|
||||
The rule of thumb is that you cannot use ``import`` for more than one
|
||||
level down. Hence you can do
|
||||
|
||||
::
|
||||
|
||||
import ev
|
||||
print ev.default_cmds.CmdLook
|
||||
|
||||
or import one level down
|
||||
|
||||
::
|
||||
|
||||
from ev import default_cmds
|
||||
print default_cmds.CmdLook
|
||||
|
||||
but you *cannot* import two levels down
|
||||
|
||||
::
|
||||
|
||||
from ev.default_cmds import CmdLook # error!
|
||||
|
||||
This will give you an ``ImportError`` telling you that the module
|
||||
``default_cmds`` cannot be found. This is not so strange -
|
||||
``default_cmds`` is just a variable name in the ``ev.py`` module, it
|
||||
does not exist outside of it.
|
||||
|
||||
As long as you keep this in mind, you should be fine. If you really want
|
||||
full control over which level of package you import you can always
|
||||
bypass ``ev`` and import directly from ``src/``. If so, look at
|
||||
``ev.py`` to see where it imports from.
|
||||
Loading…
Add table
Add a link
Reference in a new issue