diff --git a/docs/sphinx/source/wiki/AddingCommandTutorial.rst b/docs/sphinx/source/wiki/AddingCommandTutorial.rst new file mode 100644 index 0000000000..a4d61bca46 --- /dev/null +++ b/docs/sphinx/source/wiki/AddingCommandTutorial.rst @@ -0,0 +1,114 @@ +Adding a new command - a step by step guide +=========================================== + +This is a quick first-time tutorial expanding on the +`Commands `_ 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 + + 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. diff --git a/docs/sphinx/source/wiki/Banning.rst b/docs/sphinx/source/wiki/Banning.rst new file mode 100644 index 0000000000..3993e23fe9 --- /dev/null +++ b/docs/sphinx/source/wiki/Banning.rst @@ -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.* + diff --git a/docs/sphinx/source/wiki/CodingIntroduction.rst b/docs/sphinx/source/wiki/CodingIntroduction.rst new file mode 100644 index 0000000000..79455fae28 --- /dev/null +++ b/docs/sphinx/source/wiki/CodingIntroduction.rst @@ -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 `_). 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 `_ about it. Same +goes for `bugs `_. If you +add features or fix bugs yourself, please consider +`contributing `_ your changes upstream! + +Learn with \`ev\` +----------------- + +Learn the `ev interface `_. 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 `_. The +`Tutorials `_ 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 `_ 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 `_ and +the `irc chat `_ are +there for you. + +The most important point +------------------------ + +And finally, of course, have fun! diff --git a/docs/sphinx/source/wiki/CodingUtils.rst b/docs/sphinx/source/wiki/CodingUtils.rst new file mode 100644 index 0000000000..73903ac2af --- /dev/null +++ b/docs/sphinx/source/wiki/CodingUtils.rst @@ -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 `_. But in Evennia's +case it can sometimes be useful to check if an object inherits from a +given `Typeclass `_ 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 `_ 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. diff --git a/docs/sphinx/source/wiki/CommandCooldown.rst b/docs/sphinx/source/wiki/CommandCooldown.rst new file mode 100644 index 0000000000..378637d45a --- /dev/null +++ b/docs/sphinx/source/wiki/CommandCooldown.rst @@ -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 + + 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 `_, 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. diff --git a/docs/sphinx/source/wiki/GamePlanning.rst b/docs/sphinx/source/wiki/GamePlanning.rst new file mode 100644 index 0000000000..3a4cec2358 --- /dev/null +++ b/docs/sphinx/source/wiki/GamePlanning.rst @@ -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 `_ 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 `_ 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 `_ 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 `_ 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 `_, 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! diff --git a/docs/sphinx/source/wiki/GettingHelp.rst b/docs/sphinx/source/wiki/GettingHelp.rst new file mode 100644 index 0000000000..869afd04fb --- /dev/null +++ b/docs/sphinx/source/wiki/GettingHelp.rst @@ -0,0 +1,15 @@ +Getting Help +============ + +The best way to get help is via our `Google +Group `_. 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 `_ 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! diff --git a/docs/sphinx/source/wiki/OnlineSetup.rst b/docs/sphinx/source/wiki/OnlineSetup.rst new file mode 100644 index 0000000000..2c6e85abe6 --- /dev/null +++ b/docs/sphinx/source/wiki/OnlineSetup.rst @@ -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/ `_ (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 `_. 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 `_ + 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 `_. 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 `_ 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 , & + + where ```` would be ``evennia.strangled.net`` and + ```` 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 `_ +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 `_ 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 `_. + +Worth checking out is a free hosting offer especially aimed at MUDs +`here `_. An account and some activity +at `MUDbytes `_ 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. diff --git a/docs/sphinx/source/wiki/Quirks.rst b/docs/sphinx/source/wiki/Quirks.rst new file mode 100644 index 0000000000..f0341f882b --- /dev/null +++ b/docs/sphinx/source/wiki/Quirks.rst @@ -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 `_. + +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 `_ 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 `_. 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. diff --git a/docs/sphinx/source/wiki/RSS.rst b/docs/sphinx/source/wiki/RSS.rst new file mode 100644 index 0000000000..f7d8f497dc --- /dev/null +++ b/docs/sphinx/source/wiki/RSS.rst @@ -0,0 +1,66 @@ +RSS +=== + +`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 `_. + +Configuring RSS +--------------- + +To use RSS, you first need to install the +`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 = + +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 `_ and/or +`IMC2 `_ to have the feed echo to external chat channels as +well. diff --git a/docs/sphinx/source/wiki/ServerConf.rst b/docs/sphinx/source/wiki/ServerConf.rst new file mode 100644 index 0000000000..1db0694e95 --- /dev/null +++ b/docs/sphinx/source/wiki/ServerConf.rst @@ -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 `_ +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 `_. +- ``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. diff --git a/docs/sphinx/source/wiki/TickerScripts.rst b/docs/sphinx/source/wiki/TickerScripts.rst new file mode 100644 index 0000000000..2b2cd2c549 --- /dev/null +++ b/docs/sphinx/source/wiki/TickerScripts.rst @@ -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 `_ 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 `_ 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. diff --git a/docs/sphinx/source/wiki/Tutorials.rst b/docs/sphinx/source/wiki/Tutorials.rst new file mode 100644 index 0000000000..2394f5e741 --- /dev/null +++ b/docs/sphinx/source/wiki/Tutorials.rst @@ -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 `_ + +Coding basics +------------- + +- `Tutorial: Adding a new default + command `_ + +Implementation ideas +-------------------- + +- `Tutorial: Removing Colour from your game (typeclass method + overloading) `_ +- `Tutorial: Adding a Command prompt `_ +- `Tutorial: Creating a Zoning system `_ +- `Hints: Implementing cooldowns for commands `_ +- `Hints: Ticker Scripts `_ + +Examples +-------- + +- `The Tutorial + World `_ + diff --git a/docs/sphinx/source/wiki/VersionControl.rst b/docs/sphinx/source/wiki/VersionControl.rst new file mode 100644 index 0000000000..ba43f56a4f --- /dev/null +++ b/docs/sphinx/source/wiki/VersionControl.rst @@ -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 `_. 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 `_ for +some more examples. + +Using Mercurial +=============== + +`Mercurial `_ (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 + +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 + +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 `_. Pull +this repo to your local machine -- so if your clone is named +``my-evennia-fixes``, you do something like this: + +:: + + hg clone https://@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 `_ 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 `_, 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 `_ 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). diff --git a/docs/sphinx/source/wiki/Zones.rst b/docs/sphinx/source/wiki/Zones.rst new file mode 100644 index 0000000000..0f2490223b --- /dev/null +++ b/docs/sphinx/source/wiki/Zones.rst @@ -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 `_ 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. diff --git a/docs/sphinx/source/wiki/evAPI.rst b/docs/sphinx/source/wiki/evAPI.rst new file mode 100644 index 0000000000..ea4fab58ab --- /dev/null +++ b/docs/sphinx/source/wiki/evAPI.rst @@ -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 `_. 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 `_. +- ``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.