docs: Adding missing rest pages to mercurial.

This commit is contained in:
Griatch 2012-06-26 18:26:59 +02:00
parent f95fac1f51
commit ae65438c9b
16 changed files with 2114 additions and 0 deletions

View 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.

View 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.*

View 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!

View 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.

View 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.

View 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!

View 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!

View 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.

View 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.

View 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.

View 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.

View 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.

View 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>`_

View 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).

View 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.

View 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.