|
|
|
|
@ -29,7 +29,7 @@ game. This is useful for quick testing. From the game's input line, enter the fo
|
|
|
|
|
|
|
|
|
|
```sidebar:: Command input
|
|
|
|
|
|
|
|
|
|
The lines with a `>` indicate input to enter in-game, while the lines below are the
|
|
|
|
|
The line with `>` indicates input to enter in-game, while the lines below are the
|
|
|
|
|
expected return from that input.
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
@ -41,51 +41,140 @@ You will see
|
|
|
|
|
To understand what is going on: some extra info: The `print(...)` *function* is the basic, in-built
|
|
|
|
|
way to output text in Python. We are sending "Hello World" as an _argument_ to this function. The quotes `"..."`
|
|
|
|
|
mean that you are inputting a *string* (i.e. text). You could also have used single-quotes `'...'`,
|
|
|
|
|
Python accepts both.
|
|
|
|
|
Python accepts both. A third variant is triple-quotes (`"""..."""` or `'''...'''`, which work across multiple
|
|
|
|
|
lines and are common for larger text-blocks. The way we use the `py` command right now only supports
|
|
|
|
|
single-line input however.
|
|
|
|
|
|
|
|
|
|
The `print` command is a standard Python structure. We can use that here in the `py` command since
|
|
|
|
|
we can se the output. It's great for debugging and quick testing. But if you need to send a text
|
|
|
|
|
to an actual player, `print` won't do, because it doesn't know _who_ to send to. Try this:
|
|
|
|
|
### Making some text 'graphics'
|
|
|
|
|
|
|
|
|
|
> py me.msg("Hello world!")
|
|
|
|
|
Hello world!
|
|
|
|
|
When making a text-game you will, unsurprisingly, be working a lot with text. Even if you have the occational
|
|
|
|
|
button or even graphical element, the normal process is for the user to input commands as
|
|
|
|
|
text and get text back. As we saw above, a piece of text is called a _string_ in Python and is enclosed in
|
|
|
|
|
either single- or double-quotes.
|
|
|
|
|
|
|
|
|
|
This looks the same as the `print` result, but we are now actually messaging a specific *object*,
|
|
|
|
|
`me`. The `me` is a shortcut to 'us', the one running the `py` command. It is not some special
|
|
|
|
|
Python thing, but something Evennia just makes available in the `py` command for convenience
|
|
|
|
|
(`self` is an alias).
|
|
|
|
|
Strings can be added together:
|
|
|
|
|
|
|
|
|
|
The `me` is an example of an *Object instance*. Objects are fundamental in Python and Evennia.
|
|
|
|
|
The `me` object also contains a lot of useful resources for doing
|
|
|
|
|
things with that object. We access those resources with '`.`'.
|
|
|
|
|
> py print("This is a " + "breaking change.")
|
|
|
|
|
This is a breaking change.
|
|
|
|
|
|
|
|
|
|
One such resource is `msg`, which works like `print` except it sends the text to the object it
|
|
|
|
|
is attached to. So if we, for example, had an object `you`, doing `you.msg(...)` would send a message
|
|
|
|
|
to the object `you`.
|
|
|
|
|
A string multiplied with a number will repeat that string as many times:
|
|
|
|
|
|
|
|
|
|
> py print("|" + "-" * 40 + "|")
|
|
|
|
|
|----------------------------------------|
|
|
|
|
|
|
|
|
|
|
or
|
|
|
|
|
|
|
|
|
|
> py print("A" + "a" * 5 + "rgh!")
|
|
|
|
|
Aaaaaargh!
|
|
|
|
|
|
|
|
|
|
While combining different strings is useful, even more powerful is the ability to modify the contents
|
|
|
|
|
of the string in-place. There are several ways to do this in Python and we'll show two of them here. The first
|
|
|
|
|
is to use the `.format` _method_ of the string:
|
|
|
|
|
|
|
|
|
|
```sidebar:: Functions and Methods
|
|
|
|
|
|
|
|
|
|
Function:
|
|
|
|
|
Stand-alone in a python module, like `print()`
|
|
|
|
|
Something that performs and action when you `call` it with zero or more `arguments`. A function
|
|
|
|
|
is stand-alone in a python module, like `print()`
|
|
|
|
|
Method:
|
|
|
|
|
Something that sits "on" an object, like `.msg()`
|
|
|
|
|
A function that sits "on" an object, like `<string>.format()`.
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
For now, `print` and `me.msg` behaves the same, just remember that `print` is mainly used for
|
|
|
|
|
debugging and `.msg()` will be more useful for you in the future.
|
|
|
|
|
A method can be thought of as a resource "on" another object. The method knows on which object it
|
|
|
|
|
sits and can thus affect it in various ways. You access it with the period `.`. In this case, the
|
|
|
|
|
string has a resource `format(...)` that modifies it. More specifically, it replaced the `{}` marker
|
|
|
|
|
inside the string with the value passed to the format. You can do so many times:
|
|
|
|
|
|
|
|
|
|
For fun, try printing other things. Also try this:
|
|
|
|
|
|
|
|
|
|
> py self.msg("|rThis is red text!")
|
|
|
|
|
|
|
|
|
|
> py print("This is a {} idea!".format("bad"))
|
|
|
|
|
This is a bad idea!
|
|
|
|
|
|
|
|
|
|
or
|
|
|
|
|
|
|
|
|
|
> py print("This is the {} and {} {} idea!".format("first", "second", "great"))
|
|
|
|
|
This is the first and second great idea!
|
|
|
|
|
|
|
|
|
|
Adding that `|r` at the start will turn our output red. Enter the command `color ansi` or `color xterm`
|
|
|
|
|
to see which colors are available.
|
|
|
|
|
> Note the double-parenthesis at the end - the first closes the `format(...` method and the outermost
|
|
|
|
|
closes the `print(...`. Not closing them will give you a scary `SyntaxError`. We will talk a
|
|
|
|
|
little more about errors in the next section, for now just fix until it prints as expected.
|
|
|
|
|
|
|
|
|
|
Here we passed three comma-separated strings as _arguments_ to the string's `format` method. These
|
|
|
|
|
replaced the `{}` markers in the same order as they were given.
|
|
|
|
|
|
|
|
|
|
The input does not have to be strings either:
|
|
|
|
|
|
|
|
|
|
> py print("STR: {}, DEX: {}, INT: {}".format(12, 14, 8))
|
|
|
|
|
STR: 12, DEX: 14, INT: 8
|
|
|
|
|
|
|
|
|
|
To separate two Python instructions on the same line, you use the semi-colon, `;`. Try this:
|
|
|
|
|
|
|
|
|
|
> py a = "awesome sauce" ; print("This is {}!".format(a))
|
|
|
|
|
This is awesome sauce!
|
|
|
|
|
|
|
|
|
|
```warning:: MUD clients and semi-colon
|
|
|
|
|
|
|
|
|
|
Some MUD clients use the semi-colon `;` to split client-inputs
|
|
|
|
|
into separate sends. If so, the above will give an error. Most clients allow you to
|
|
|
|
|
run in 'verbatim' mode or to remap to use some other separator than `;`. If you still have
|
|
|
|
|
trouble, just use the Evennia web client for now. In real Python code you'll pretty much never use
|
|
|
|
|
the semi-colon.
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
What happened here was that we _assigned_ the string `"awesome sauce"` to a _variable_ we chose
|
|
|
|
|
to name `a`. In the next statement, Python remembered what `a` was and we passed that into `format()`
|
|
|
|
|
to get the output. If you replaced the value of `a` with something else in between, _that_ would be printed
|
|
|
|
|
instead.
|
|
|
|
|
|
|
|
|
|
Here's the stat-example again, moving the stats to variables (here we just set them, but in a real
|
|
|
|
|
game they may be changed over time, or modified by circumstance):
|
|
|
|
|
|
|
|
|
|
> py str, dex, int = 13, 14, 8 ; print("STR: {}, DEX: {}, INT: {}".format(stren, dex, int))
|
|
|
|
|
STR: 13, DEX: 14, INT: 8
|
|
|
|
|
|
|
|
|
|
The point is that even if the values of the stats change, the print() statement would not change - it just keeps
|
|
|
|
|
pretty-printing whatever is given to it.
|
|
|
|
|
|
|
|
|
|
Using `.format()` is convenient (and there is a [lot more](https://www.w3schools.com/python/ref_string_format.asp)
|
|
|
|
|
you can do with it). But the _f-string_ can be even more convenient. An
|
|
|
|
|
f-string looks like a normal string ... except there is an `f` front of it, like this:
|
|
|
|
|
|
|
|
|
|
f"this is now an f-string."
|
|
|
|
|
|
|
|
|
|
An f-string on its own is just like any other string. But let's redo the example we did before, using an f-string:
|
|
|
|
|
|
|
|
|
|
> py a = "awesome sauce" ; print(f"This is {a}!")
|
|
|
|
|
This is awesome sauce!
|
|
|
|
|
|
|
|
|
|
We could just insert that `a` variable directly into the f-string using `{a}`. Fewer parentheses to
|
|
|
|
|
remember and arguable easier to read as well.
|
|
|
|
|
|
|
|
|
|
> py str, dex, int = 13, 14, 8 ; print(f"STR: {str}, DEX: {dex}, INT: {int}")
|
|
|
|
|
STR: 13, DEX: 14, INT: 8
|
|
|
|
|
|
|
|
|
|
We will be exploring more complex string concepts when we get to creating Commands and need to
|
|
|
|
|
parse and understand player input.
|
|
|
|
|
|
|
|
|
|
Python itself knows nothing about colored text, this is an Evennia thing. Evennia supports the
|
|
|
|
|
standard color schemes of traditional MUDs.
|
|
|
|
|
|
|
|
|
|
> py print("|rThis is red text!|n This is normal color.")
|
|
|
|
|
|
|
|
|
|
Adding that `|r` at the start will turn our output bright red. `|R` will make it dark red. `|n`
|
|
|
|
|
gives the normal text color. You can also use RGB (Red-Green-Blue) values from 0-5 (Xterm256 colors):
|
|
|
|
|
|
|
|
|
|
> py print("|043This is a blue-green color.|[530|003 This is dark blue text on orange background.")
|
|
|
|
|
|
|
|
|
|
> If you don't see the expected color, your client or terminal may not support Xterm256 (or
|
|
|
|
|
color at all). Use the Evennia webclient.
|
|
|
|
|
|
|
|
|
|
Use the commands `color ansi` or `color xterm` to see which colors are available. Experiment!
|
|
|
|
|
|
|
|
|
|
### Importing code from other modules
|
|
|
|
|
|
|
|
|
|
As we saw in the previous section, we could use `me.msg` to access the `msg` method on `me`. This
|
|
|
|
|
use of the full-stop character is used to access all sorts of resources, including that in other
|
|
|
|
|
Python modules. If you've been following along, this is what we've referred to as the "Python path".
|
|
|
|
|
As we saw in the previous sections, we used `.format` to format strings and `me.msg` to access
|
|
|
|
|
the `msg` method on `me`. This use of the full-stop character is used to access all sorts of resources,
|
|
|
|
|
including that in other Python modules.
|
|
|
|
|
|
|
|
|
|
Keep your game running, then open a text editor of your choice. If your game folder is called
|
|
|
|
|
`mygame`, create a new text file `test.py` in the subfolder `mygame/world`. This is how the file
|
|
|
|
|
@ -130,16 +219,12 @@ path - Evennia handles this for us.
|
|
|
|
|
When you import the module, the top "level" of it will execute. In this case, it will immediately
|
|
|
|
|
print "Hello World".
|
|
|
|
|
|
|
|
|
|
> If you look in the folder you'll also often find new files ending with `.pyc`. These are compiled
|
|
|
|
|
Python binaries that Python auto-creates when running code. Just ignore them, you should never edit
|
|
|
|
|
those anyway.
|
|
|
|
|
|
|
|
|
|
Now try to run this a second time:
|
|
|
|
|
|
|
|
|
|
> py import world.test
|
|
|
|
|
|
|
|
|
|
You will *not* see any output this second time or any subsequent times! This is not a bug. Rather
|
|
|
|
|
it is because Python is being clever - it stores all imported modules and to be efficient it will
|
|
|
|
|
You will *not* see any output this second time or any subsequent times! This is not a bug. Rather
|
|
|
|
|
it is because of how Python importing works - it stores all imported modules and will
|
|
|
|
|
avoid importing them more than once. So your `print` will only run the first time, when the module
|
|
|
|
|
is first imported.
|
|
|
|
|
|
|
|
|
|
@ -159,29 +244,124 @@ not very useful.
|
|
|
|
|
> We'll get back to more advanced ways to import code in later tutorial sections - this is an
|
|
|
|
|
> important topic. But for now, let's press on and resolve this particular problem.
|
|
|
|
|
|
|
|
|
|
### Parsing Python errors
|
|
|
|
|
|
|
|
|
|
Running print only on import is not too helpful. We want to print whenever we like to!
|
|
|
|
|
Go back to your `test.py` file and erase the single `print` statement you had. Replace
|
|
|
|
|
it with this instead:
|
|
|
|
|
### Our first own function
|
|
|
|
|
|
|
|
|
|
We want to be able to print our hello-world message at any time, not just once after a server
|
|
|
|
|
reload. Change your `mygame/world/test.py` file to look like this:
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
me.msg("Hello World!")
|
|
|
|
|
def hello_world():
|
|
|
|
|
print("Hello World!")
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
As you recall we used this with `py` earlier - it echoed "Hello World!" in-game.
|
|
|
|
|
Save your file and `reload` your server - this makes sure Evennia knows to re-import
|
|
|
|
|
it (with our new, fresh code this time).
|
|
|
|
|
As we are moving to multi-line Python code, there are some important things to remember:
|
|
|
|
|
|
|
|
|
|
To test it, import it using `py` as before
|
|
|
|
|
- Capitalization matters in Python. It must be `def` and not `DEF`, `who` is not the same as `Who`.
|
|
|
|
|
- Indentation matters in Python. The second line must be indented or it's not valid code. You should
|
|
|
|
|
also use a consistent indentation length. We *strongly* recommend that you, for your own sanity's sake,
|
|
|
|
|
set up your editor to always indent *4 spaces* (**not** a single tab-character) when you press the TAB key.
|
|
|
|
|
|
|
|
|
|
> py import world.test
|
|
|
|
|
So about that function. Line 1:
|
|
|
|
|
|
|
|
|
|
- `def` is short for "define" and defines a *function* (or a *method*, if sitting on an object).
|
|
|
|
|
This is a [reserved Python keyword](https://docs.python.org/2.5/ref/keywords.html); try not to use
|
|
|
|
|
these words anywhere else.
|
|
|
|
|
- A function name can not have spaces but otherwise we could have called it almost anything. We call
|
|
|
|
|
it `hello_world`. Evennia follows [Python's standard naming style](https://github.com/evennia/evennia/blob/master/CODING_STYLE.md#a-quick-list-of-code-style-points)
|
|
|
|
|
with lowercase letters and underscores. We recommend you do the same.
|
|
|
|
|
- The colon (`:`) at the end of line 1 indicates that the header of the function is complete.
|
|
|
|
|
|
|
|
|
|
Line 2:
|
|
|
|
|
|
|
|
|
|
- The indentation marks the beginning of the actual operating code of the function (the function's
|
|
|
|
|
*body*). If we wanted more lines to belong to this function those lines would all have to
|
|
|
|
|
start at least at this indentation level.
|
|
|
|
|
|
|
|
|
|
Now let's try this out. First `reload` your game to have it pick up
|
|
|
|
|
our updated Python module, then import it.
|
|
|
|
|
|
|
|
|
|
> reload
|
|
|
|
|
> py import world.test
|
|
|
|
|
|
|
|
|
|
Nothing happened! That is because the function in our module won't do anything just by importing it (this
|
|
|
|
|
is what we wanted). It will only act when we *call* it. So we need to first import the module and then access the
|
|
|
|
|
function within:
|
|
|
|
|
|
|
|
|
|
> py import world.test ; world.test.hello_world()
|
|
|
|
|
Hello world!
|
|
|
|
|
|
|
|
|
|
There is our "Hello World"! As mentioned earlier, use use semi-colon to put multiple
|
|
|
|
|
Python-statements on one line. Note also the previous warning about mud-clients using the `;` to their
|
|
|
|
|
own ends.
|
|
|
|
|
|
|
|
|
|
So what happened there? First we imported `world.test` as usual. But this time we continued and
|
|
|
|
|
accessed the `hello_world` function _inside_ the newly imported module.
|
|
|
|
|
|
|
|
|
|
By adding `()` to the `hello_world` function we _call_ it, that is we run the body of the function and
|
|
|
|
|
print our text. We can now redo this as many times as we want without having to `reload` in between:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
> py import world.test ; world.test.hello_world()
|
|
|
|
|
Hello world!
|
|
|
|
|
> py import world.test ; world.test.hello_world()
|
|
|
|
|
Hello world!
|
|
|
|
|
|
|
|
|
|
> **Extra Credit:** As an exercise, try to pass something else into `hello_world`. Try for example
|
|
|
|
|
>to pass the number `5` or the string `"foo"`. You'll get errors telling you that they don't have
|
|
|
|
|
>the attribute `msg`. They don't care about `me` itself not being a string or a number. If you are
|
|
|
|
|
>familiar with other programming languages (especially C/Java) you may be tempted to start *validating*
|
|
|
|
|
>`who` to make sure it's of the right type before you send it. This is usually not recommended in Python.
|
|
|
|
|
>Python philosophy is to [handle](https://docs.python.org/2/tutorial/errors.html) the error if it happens
|
|
|
|
|
>rather than to add a lot of code to prevent it from happening. See [duck typing](https://en.wikipedia.org/wiki/Duck_typing)
|
|
|
|
|
>and the concept of _Leap before you Look_.
|
|
|
|
|
|
|
|
|
|
### Sending text to others
|
|
|
|
|
|
|
|
|
|
The `print` command is a standard Python structure. We can use that here in the `py` command since
|
|
|
|
|
we can se the output. It's great for debugging and quick testing. But if you need to send a text
|
|
|
|
|
to an actual player, `print` won't do, because it doesn't know _who_ to send to. Try this:
|
|
|
|
|
|
|
|
|
|
> py me.msg("Hello world!")
|
|
|
|
|
Hello world!
|
|
|
|
|
|
|
|
|
|
This looks the same as the `print` result, but we are now actually messaging a specific *object*,
|
|
|
|
|
`me`. The `me` is a shortcut to 'us', the one running the `py` command. It is not some special
|
|
|
|
|
Python thing, but something Evennia just makes available in the `py` command for convenience
|
|
|
|
|
(`self` is an alias).
|
|
|
|
|
|
|
|
|
|
The `me` is an example of an *Object instance*. Objects are fundamental in Python and Evennia.
|
|
|
|
|
The `me` object also contains a lot of useful resources for doing
|
|
|
|
|
things with that object. We access those resources with '`.`'.
|
|
|
|
|
|
|
|
|
|
One such resource is `msg`, which works like `print` except it sends the text to the object it
|
|
|
|
|
is attached to. So if we, for example, had an object `you`, doing `you.msg(...)` would send a message
|
|
|
|
|
to the object `you`.
|
|
|
|
|
|
|
|
|
|
For now, `print` and `me.msg` behaves the same, just remember that `print` is mainly used for
|
|
|
|
|
debugging and `.msg()` will be more useful for you in the future.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### Parsing Python errors
|
|
|
|
|
|
|
|
|
|
Let's try this new text-sending in the function we just created. Go back to
|
|
|
|
|
your `test.py` file and Replace the function with this instead:
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
def hello_world():
|
|
|
|
|
me.msg("Hello World!")
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Save your file and `reload` your server to tell Evennia to re-import new code,
|
|
|
|
|
then run it like before:
|
|
|
|
|
|
|
|
|
|
> py import world.test ; world.test.hello_world()
|
|
|
|
|
|
|
|
|
|
No go - this time you get an error!
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
File "./world/test.py", line 1, in <module>
|
|
|
|
|
me.msg("Hello world!")
|
|
|
|
|
File "./world/test.py", line 2, in hello_world
|
|
|
|
|
me.msg("Hello World!")
|
|
|
|
|
NameError: name 'me' is not defined
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
@ -216,92 +396,258 @@ reserved word (as mentioned, it's just something Evennia came up with for conven
|
|
|
|
|
command). As far as the module is concerned `me` is an unfamiliar name, appearing out of nowhere.
|
|
|
|
|
Hence the `NameError`.
|
|
|
|
|
|
|
|
|
|
### Our first own function
|
|
|
|
|
### Passing arguments to functions
|
|
|
|
|
|
|
|
|
|
Let's see if we can resolve that `NameError` from the previous section. We know that `me` is defined
|
|
|
|
|
at the time we use the `py` command. We know this because if we do `py me.msg("Hello World!")`
|
|
|
|
|
directly in-game it works fine. What if we could *send* that `me` to the `test.py` module so it
|
|
|
|
|
knows what it is? One way to do this is with a *function*.
|
|
|
|
|
|
|
|
|
|
Change your `mygame/world/test.py` file to look like this:
|
|
|
|
|
We know that `me` exists at the point when we run the `py` command, because we can do `py me.msg("Hello World!")`
|
|
|
|
|
with no problem. So let's _pass_ that me along to the function so it knows what it should be.
|
|
|
|
|
Go back to your `test.py` and change it to this:
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
def hello_world(who):
|
|
|
|
|
who.msg("Hello World!")
|
|
|
|
|
```
|
|
|
|
|
We now added an _argument_ to the function. We could have named it anything. Whatever `who` is,
|
|
|
|
|
we will call a method `.msg()` on it.
|
|
|
|
|
|
|
|
|
|
As we are moving to multi-line Python code, there are some important things to remember:
|
|
|
|
|
|
|
|
|
|
- Capitalization matters in Python. It must be `def` and not `DEF`, `who` is not the same as `Who`.
|
|
|
|
|
- Indentation matters in Python. The second line must be indented or it's not valid code. You should
|
|
|
|
|
also use a consistent indentation length. We *strongly* recommend that you, for your own sanity's sake,
|
|
|
|
|
set up your editor to always indent *4 spaces* (**not** a single tab-character) when you press the TAB key.
|
|
|
|
|
|
|
|
|
|
So about that function. Line 1:
|
|
|
|
|
|
|
|
|
|
- `def` is short for "define" and defines a *function* (or a *method*, if sitting on an object).
|
|
|
|
|
This is a [reserved Python keyword](https://docs.python.org/2.5/ref/keywords.html); try not to use
|
|
|
|
|
these words anywhere else.
|
|
|
|
|
- A function name can not have spaces but otherwise we could have called it almost anything. We call
|
|
|
|
|
it `hello_world`. Evennia follows [Python's standard naming style](https://github.com/evennia/evennia/blob/master/CODING_STYLE.md#a-quick-list-of-code-style-points)
|
|
|
|
|
with lowercase letters and underscores. We recommend you do the same.
|
|
|
|
|
- `who` is what we call the *argument* to our function. Arguments are variables _passed_ to the
|
|
|
|
|
function. We could have named it anything and we could also have multiple arguments separated by
|
|
|
|
|
commas. What `who` is depends on what we pass to this function when we *call* it later (hint: we'll
|
|
|
|
|
pass `me` to it).
|
|
|
|
|
- The colon (`:`) at the end of line 1 indicates that the header of the function is complete.
|
|
|
|
|
|
|
|
|
|
Line 2:
|
|
|
|
|
|
|
|
|
|
- The indentation marks the beginning of the actual operating code of the function (the function's
|
|
|
|
|
*body*). If we wanted more lines to belong to this function those lines would all have to
|
|
|
|
|
start at least at this indentation level.
|
|
|
|
|
- In the function body we take the `who` argument and treat it as we would have treated `me`
|
|
|
|
|
earlier- we expect it to have a `.msg` method we can use to send "Hello World" to.
|
|
|
|
|
|
|
|
|
|
Now let's try this out. First `reload` your game to have it pick up
|
|
|
|
|
our updated Python module when we reload.
|
|
|
|
|
|
|
|
|
|
> reload
|
|
|
|
|
> py import world.test
|
|
|
|
|
|
|
|
|
|
Nothing happened! That is because the function in our module won't do anything just by importing it.
|
|
|
|
|
It will only act when we *call* it. So we need to first import the module and then access the
|
|
|
|
|
function within:
|
|
|
|
|
As usual, `reload` the server to make sure the new code is available.
|
|
|
|
|
|
|
|
|
|
> py import world.test ; world.test.hello_world(me)
|
|
|
|
|
Hello world!
|
|
|
|
|
Hello World!
|
|
|
|
|
|
|
|
|
|
Now it worked. We _passed_ `me` to our function. It will appear inside the function renamed as `who` and
|
|
|
|
|
now the function works and prints as expected. Note how the `hello_world` function doesn't care _what_ you
|
|
|
|
|
pass into it as long as it has a `.msg()` method on it. So you could reuse this function over and over for other
|
|
|
|
|
suitable targets.
|
|
|
|
|
|
|
|
|
|
There is our "Hello World"! Using `;` is the way to put multiple Python-statements on one line.
|
|
|
|
|
### Finding others to send to
|
|
|
|
|
|
|
|
|
|
```warning:: MUD clients and semi-colon
|
|
|
|
|
Let's wrap up this first Python `py` crash-course by finding someone else to send to.
|
|
|
|
|
|
|
|
|
|
A common issue is that some MUD clients use the semi-colon `;` to split client-inputs
|
|
|
|
|
into separate sends. If so, you'll get a `NameError` above, stating that
|
|
|
|
|
`world` is not defined. Check so you understand why this is! Most clients allow you to
|
|
|
|
|
remap to use some other separator than `;`. You can use the Evennia web client if this
|
|
|
|
|
problem remains.
|
|
|
|
|
In Evennia's `contrib/` folder (`evennia/contrib/tutorial_examples/mirror.py`) is a handy little
|
|
|
|
|
object called the `TutorialMirror`. The mirror will echo whatever is being sent to it to
|
|
|
|
|
the room it is in.
|
|
|
|
|
|
|
|
|
|
On the game command-line, let's create a mirror:
|
|
|
|
|
|
|
|
|
|
> create/drop mirror:contrib.tutorial_examples.mirror.TutorialMirror
|
|
|
|
|
|
|
|
|
|
```sidebar:: Creating objects
|
|
|
|
|
|
|
|
|
|
The `create` command was first used to create boxes in the
|
|
|
|
|
`Building Stuff <Building-Quickstart>`_ tutorial. Note how it
|
|
|
|
|
uses a "python-path" to describe where to load the mirror's code from.
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
So what happened there? First we imported `world.test` as usual. But this time we continued and
|
|
|
|
|
accessed the `hello_world` function _inside_ the newly imported module.
|
|
|
|
|
A mirror should appear in your location.
|
|
|
|
|
|
|
|
|
|
We *call* the function with `me`, which becomes the `who` variable we use inside
|
|
|
|
|
the `hello_function` (they are be the same object). And since `me.msg` works, so does `who.msg`
|
|
|
|
|
inside the function.
|
|
|
|
|
> look mirror
|
|
|
|
|
The mirror shows your reflection:
|
|
|
|
|
This is User #1
|
|
|
|
|
|
|
|
|
|
> **Extra Credit:** As an exercise, try to pass something else into `hello_world`. Try for example
|
|
|
|
|
>to pass the number `5` or the string `"foo"`. You'll get errors telling you that they don't have
|
|
|
|
|
>the attribute `msg`. They don't care about `me` itself not being a string or a number. If you are
|
|
|
|
|
>familiar with other programming languages (especially C/Java) you may be tempted to start *validating*
|
|
|
|
|
>`who` to make sure it's of the right type before you send it. This is usually not recommended in Python.
|
|
|
|
|
>Python philosophy is to [handle](https://docs.python.org/2/tutorial/errors.html) the error if it happens
|
|
|
|
|
>rather than to add a lot of code to prevent it from happening. See [duck typing](https://en.wikipedia.org/wiki/Duck_typing)
|
|
|
|
|
>and the concept of _Leap before you Look_.
|
|
|
|
|
What you are seeing is actually your own avatar in the game, the same thing that is available as `me` in the `py`
|
|
|
|
|
command.
|
|
|
|
|
|
|
|
|
|
What we are aiming for now is the equivalent of `mirror.msg("Mirror Mirror on the wall")`. But the first thing that
|
|
|
|
|
comes to mind will not work:
|
|
|
|
|
|
|
|
|
|
> py mirror.msg("Mirror, Mirror on the wall ...")
|
|
|
|
|
NameError: name 'mirror' is not defined.
|
|
|
|
|
|
|
|
|
|
This is not surprising: Python knows nothing about "mirrors" or locations or anything. The `me` we've been using
|
|
|
|
|
is, as mentioned, just a convenient thing the Evennia devs makes available to the `py` command. They couldn't possibly
|
|
|
|
|
predict that you wanted to talk to mirrors.
|
|
|
|
|
|
|
|
|
|
Instead we will need to _search_ for that `mirror` object before we can send to it.
|
|
|
|
|
Make sure you are in the same location as the mirror and try:
|
|
|
|
|
|
|
|
|
|
> py me.search("mirror")
|
|
|
|
|
mirror
|
|
|
|
|
|
|
|
|
|
`me.search("name")` will, by default, search and _return_ an object with the given name found in _the same location_
|
|
|
|
|
as the `me` object is. If it can't find anything you'll see an error.
|
|
|
|
|
|
|
|
|
|
```sidebar:: Function returns
|
|
|
|
|
|
|
|
|
|
Whereas a function like `print` only prints its arguments, it's very common
|
|
|
|
|
for functions/methods to `return` a result of some kind. Think of the function
|
|
|
|
|
as a machine - you put something in and out comes a result you can use. In the case
|
|
|
|
|
of `me.search`, it will perform a database search and spit out the object it finds.
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
> py me.search("dummy")
|
|
|
|
|
Could not find 'dummy'.
|
|
|
|
|
|
|
|
|
|
Wanting to find things in the same location is very common, but as we continue we'll
|
|
|
|
|
find that Evennia provides ample tools for tagging, searching and finding things from all over your game.
|
|
|
|
|
|
|
|
|
|
Now that we know how to find the 'mirror' object, we just need to use that instead of `me`!
|
|
|
|
|
|
|
|
|
|
> py mirror = self.search("mirror") ; mirror.msg("Mirror, Mirror on the wall ...")
|
|
|
|
|
The mirror echoes back to you:
|
|
|
|
|
"Mirror, Mirror on the wall ..."
|
|
|
|
|
|
|
|
|
|
The mirror is useful for testing because its `.msg` method just echoes whatever is sent to it back to the room. More common
|
|
|
|
|
would be to talk to a player character, in which case the text you sent would have appeared in their game client.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
This gives you some initial feeling for how to run Python and import Python modules. We also
|
|
|
|
|
tried to put a module in `module/world/`. Now let's look at the rest of the stuff you've got
|
|
|
|
|
inside that `mygame/` folder ...
|
|
|
|
|
### Multi-line py
|
|
|
|
|
|
|
|
|
|
So far we have use `py` in single-line mode, using `;` to separate multiple inputs. This is very convenient
|
|
|
|
|
when you want to do some quick testing. But you can also start a full multi-line Python interactive interpreter
|
|
|
|
|
inside Evennia.
|
|
|
|
|
|
|
|
|
|
> py
|
|
|
|
|
Evennia Interactive Python mode
|
|
|
|
|
Python 3.7.1 (default, Oct 22 2018, 11:21:55)
|
|
|
|
|
[GCC 8.2.0] on Linux
|
|
|
|
|
[py mode - quit() to exit]
|
|
|
|
|
|
|
|
|
|
(the details of the output will vary with your Python version and OS). You are now in python interpreter mode. It means
|
|
|
|
|
that _everything_ you insert from now on will become a line of Python (you can no longer look around or do other
|
|
|
|
|
commands).
|
|
|
|
|
|
|
|
|
|
> print("Hello World")
|
|
|
|
|
|
|
|
|
|
>>> print("Hello World")
|
|
|
|
|
Hello World
|
|
|
|
|
[py mode - quit() to exit]
|
|
|
|
|
|
|
|
|
|
Note that we didn't need to put `py` in front now. The system will also echo your input (that's the bit after
|
|
|
|
|
the `>>>`). For brevity in this tutorual we'll turn the echo off. First exit `py` and then start again with the
|
|
|
|
|
`/noecho` flag.
|
|
|
|
|
|
|
|
|
|
> quit()
|
|
|
|
|
Closing the Python console.
|
|
|
|
|
> py/noecho
|
|
|
|
|
Evennia Interactive Python mode
|
|
|
|
|
Python 3.7.1 (default, Oct 22 2018, 11:21:55)
|
|
|
|
|
[GCC 8.2.0] on Linux
|
|
|
|
|
[py mode - quit() to exit]
|
|
|
|
|
|
|
|
|
|
```sidebar:: interactive py
|
|
|
|
|
|
|
|
|
|
- Start with `py`.
|
|
|
|
|
- Use `py/noecho` if you don't want your input to be echoed for every line.
|
|
|
|
|
- All your inputs will now be interpreted as Python code.
|
|
|
|
|
- Exit with `quit()`.
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
We can now enter multi-line Python code:
|
|
|
|
|
|
|
|
|
|
> a = "Test"
|
|
|
|
|
> print(f"This is a {a}."}
|
|
|
|
|
This is a Test.
|
|
|
|
|
|
|
|
|
|
Let's try to define a function:
|
|
|
|
|
|
|
|
|
|
> def hello_world(who, txt):
|
|
|
|
|
...
|
|
|
|
|
> who.msg(txt)
|
|
|
|
|
...
|
|
|
|
|
>
|
|
|
|
|
[py mode - quit() to exit]
|
|
|
|
|
|
|
|
|
|
Some important things above:
|
|
|
|
|
|
|
|
|
|
- Definining a function with `def` means we are starting a new code block. Python works so that you mark the content
|
|
|
|
|
of the block with indention. So the next line must be manually indented (4 spaces is a good standard) in order
|
|
|
|
|
for Python to know it's part of the function body.
|
|
|
|
|
- We expand the `hello_world` function with another argument `txt`. This allows us to send any text, not just
|
|
|
|
|
"Hello World" over and over.
|
|
|
|
|
- To tell `py` that no more lines will be added to the function body, we end with an empty input. When
|
|
|
|
|
the normal prompt on how to exit returns, we know we are done.
|
|
|
|
|
|
|
|
|
|
Now we have defined a new function. Let's try it out:
|
|
|
|
|
|
|
|
|
|
> hello_world(me, "Hello world to me!")
|
|
|
|
|
Hello world to me!
|
|
|
|
|
|
|
|
|
|
The `me` is still available to us, so we pass that as the `who` argument, along with a little longer
|
|
|
|
|
string. Let's combine this with searching for the mirror.
|
|
|
|
|
|
|
|
|
|
> mirror = me.search("mirror")
|
|
|
|
|
> hello_world(mirror, "Mirror, Mirror on the wall ...")
|
|
|
|
|
The mirror echoes back to you:
|
|
|
|
|
"Mirror, Mirror on the wall ..."
|
|
|
|
|
|
|
|
|
|
Exit the `py` mode with
|
|
|
|
|
|
|
|
|
|
> quit()
|
|
|
|
|
Closing the Python console.
|
|
|
|
|
|
|
|
|
|
## Other ways to test Python code
|
|
|
|
|
|
|
|
|
|
The `py` command is very powerful for experimenting with Python in-game. It's great for quick testing.
|
|
|
|
|
But you are still limited to working over telnet or the webclient, interfaces that doesn't know anything
|
|
|
|
|
about Python per-se.
|
|
|
|
|
|
|
|
|
|
Outside the game, go to the terminal where you ran Evennia (or any terminal where the `evennia` command
|
|
|
|
|
is available).
|
|
|
|
|
|
|
|
|
|
- `cd` to your game dir.
|
|
|
|
|
- `evennia shell`
|
|
|
|
|
|
|
|
|
|
A Python shell opens. This works like `py` did inside the game, with the exception that you don't have
|
|
|
|
|
`me` available out of the box. If you want `me`, you need to first find yourself:
|
|
|
|
|
|
|
|
|
|
> import evennia
|
|
|
|
|
> me = evennia.search_object("YourChar")[0]
|
|
|
|
|
|
|
|
|
|
Here we make use of one of evennia's search functions, available by importing `evennia` directly.
|
|
|
|
|
We will cover more advanced searching later, but suffice to say, you put your own character name instead of
|
|
|
|
|
"YourChar" above.
|
|
|
|
|
|
|
|
|
|
> The `[0]` at the end is because `.search_object` returns a list of objects and we want to
|
|
|
|
|
get at the first of them (counting starts from 0).
|
|
|
|
|
|
|
|
|
|
Use `Ctrl-D` (`Cmd-D` on Mac) or `quit()` to exit the Python console.
|
|
|
|
|
|
|
|
|
|
### ipython
|
|
|
|
|
|
|
|
|
|
The default Python shell is quite limited and ugly. It's *highly* recommended to install `ipython` instead. This
|
|
|
|
|
is a much nicer, third-party Python interpreter with colors and many usability improvements.
|
|
|
|
|
|
|
|
|
|
pip install ipython
|
|
|
|
|
|
|
|
|
|
If `ipython` is installed, `evennia shell` will use it automatically.
|
|
|
|
|
|
|
|
|
|
evennia shell
|
|
|
|
|
...
|
|
|
|
|
IPython 7.4.0 -- An enhanced Interactive Python. Type '?' for help
|
|
|
|
|
In [1]:
|
|
|
|
|
|
|
|
|
|
You now have Tab-completion:
|
|
|
|
|
|
|
|
|
|
> import evennia
|
|
|
|
|
> evennia.<TAB>
|
|
|
|
|
|
|
|
|
|
That is, enter `evennia.` and then press the TAB key - you will be given a list of all the resources
|
|
|
|
|
available on the `evennia` object. This is great for exploring what Evennia has to offer. For example,
|
|
|
|
|
use your arrow keys to scroll to `search_object()` to fill it in.
|
|
|
|
|
|
|
|
|
|
> evennia.search_object?
|
|
|
|
|
|
|
|
|
|
Adding a `?` and pressing return will give you the full documentation for `.search_object`. Use `??` if you
|
|
|
|
|
want to see the entire source code.
|
|
|
|
|
|
|
|
|
|
As for the normal python interpreter, use `Ctrl-D`/`Cmd-D` or `quit()` to exit ipython.
|
|
|
|
|
|
|
|
|
|
```important:: Persistent code
|
|
|
|
|
|
|
|
|
|
Common for both `py` and `python`/`ipython` is that the code you write is not persistent - it will
|
|
|
|
|
be gone after you shut down the interpreter (but ipython will remember your input history). For making long-lasting
|
|
|
|
|
Python code, we need to save it in a Python module, like we did for `world/test.py`.
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Conclusions
|
|
|
|
|
|
|
|
|
|
This covers quite a lot of basic Python usage. We printed and formatted strings, defined our own
|
|
|
|
|
first function, fixed an error and even searched and talked to a mirror! Being able to access
|
|
|
|
|
python inside and outside of the game is an important skill for testing and debugging, but in
|
|
|
|
|
practice you will be writing most your code in Python modules.
|
|
|
|
|
|
|
|
|
|
To that end we also created a first new Python module in the `mygame/` game dir, then imported and used it.
|
|
|
|
|
Now let's look at the rest of the stuff you've got going on inside that `mygame/` folder ...
|
|
|
|
|
|
|
|
|
|
[prev lesson](Tutorial-World-Introduction) | [next lesson](Gamedir-Overview)
|
|
|
|
|
|