Cleaning some unnecessary whitespace, overall cleanup of various source codes.
This commit is contained in:
parent
d4c97d7df8
commit
c0322c9eae
27 changed files with 1342 additions and 1318 deletions
|
|
@ -1,5 +1,5 @@
|
||||||
# This specifies file types that mercurial should
|
# This specifies file types that mercurial should
|
||||||
# ignore, defined on glob format.
|
# ignore, defined on glob format.
|
||||||
|
|
||||||
syntax: glob
|
syntax: glob
|
||||||
|
|
||||||
|
|
|
||||||
64
CODING_STYLE
64
CODING_STYLE
|
|
@ -1,59 +1,63 @@
|
||||||
Evennia Code Style
|
Evennia Code Style
|
||||||
------------------
|
------------------
|
||||||
All code submitted or committed to the Evennia project needs to follow the
|
All code submitted or committed to the Evennia project needs to follow
|
||||||
guidelines outlined in Python PEP 8, which may be found at:
|
the guidelines outlined in Python PEP 8, which may be found at:
|
||||||
|
|
||||||
http://www.python.org/dev/peps/pep-0008/
|
http://www.python.org/dev/peps/pep-0008/
|
||||||
|
|
||||||
A quick list of code style points
|
A quick list of code style points
|
||||||
---------------------------------
|
---------------------------------
|
||||||
* 4-space indendation, NO TABS!
|
* 4-space indendation, NO TABS!
|
||||||
* Unix line endings.
|
* Unix line endings.
|
||||||
* CamelCase is only used for classes, nothing else.
|
* CamelCase is only used for classes, nothing else.
|
||||||
* All non-global variable names and all function names are to be lowercase,
|
* All non-global variable names and all function names are to be
|
||||||
words separated by underscores. Variable names should always be more than
|
lowercase, words separated by underscores. Variable names should
|
||||||
two letters long.
|
always be more than two letters long.
|
||||||
* Module-level global variables (only) are to be in CAPITAL letters.
|
* Module-level global variables (only) are to be in CAPITAL letters.
|
||||||
* Imports are to be done in this order:
|
* (Evennia-specific): Imports should normally be done in this order:
|
||||||
- Python modules (builtins and modules otherwise unrelated to Evennia)
|
- Python modules (builtins and standard library)
|
||||||
- Twisted
|
- Twisted modules
|
||||||
- Django
|
- Django modules
|
||||||
- Evennia src/ modules
|
- Evennia src/ modules
|
||||||
- Evennia game/ modules
|
- Evennia game/ modules
|
||||||
|
- Evennia 'ev' API imports
|
||||||
|
|
||||||
Documentation
|
Documentation
|
||||||
-------------
|
-------------
|
||||||
Remember that Evennia's source code is intended to be read - and will be read - by
|
Remember that Evennia's source code is intended to be read - and will
|
||||||
game admins trying to implement various features. Evennia prides itself with being
|
be read - by game admins trying to implement their game. Evennia
|
||||||
extensively documented. Modules, functions, classes and class methods should all
|
prides itself with being extensively documented. Modules, functions,
|
||||||
start with at least one line of docstring summing up the function's purpose. Ideally
|
classes and class methods should all start with at least one line of
|
||||||
also explain eventual arguments and caveats. Add comments where appropriate.
|
docstring summing up the function's purpose. Ideally also explain
|
||||||
|
eventual arguments and caveats. Add comments where appropriate.
|
||||||
|
|
||||||
Pylint
|
Pylint
|
||||||
------
|
------
|
||||||
The program 'pylint' (http://www.logilab.org/857) is a useful tool for checking
|
The program 'pylint' (http://www.logilab.org/857) is a useful tool for
|
||||||
your Python code for errors. It will also check how well your code adheres to
|
checking your Python code for errors. It will also check how well your
|
||||||
the PEP 8 guidelines (such as lack of docstrings) and tells you what can be improved.
|
code adheres to the PEP 8 guidelines (such as lack of docstrings) and
|
||||||
|
tells you what can be improved.
|
||||||
|
|
||||||
Since pylint cannot catch dynamically created variables used in commands and
|
Since pylint cannot catch dynamically created variables used in
|
||||||
elsewhere in Evennia, one needs to reduce some checks to avoid false errors and
|
commands and elsewhere in Evennia, one needs to reduce some checks to
|
||||||
warnings. For best results, run pylint like this:
|
avoid false errors and warnings. For best results, run pylint like
|
||||||
|
this:
|
||||||
|
|
||||||
> pylint --disable=E1101,E0102,F0401,W0232,R0903 filename.py
|
> pylint --disable=E1101,E0102,F0401,W0232,R0903 filename.py
|
||||||
|
|
||||||
To avoid entering the options every time, you can auto-create a pylintrc file by
|
To avoid entering the options every time, you can auto-create a
|
||||||
using the option --generate-rcfile. You need to dump this output into a
|
pylintrc file by using the option --generate-rcfile. You need to dump
|
||||||
file .pylintrc, for example like this (linux):
|
this output into a file .pylintrc, for example like this (linux):
|
||||||
|
|
||||||
> pylint --disable=E1101,E0102,F0401,W0232,R0903 --generate-rcfile > ~/.pylintrc
|
> pylint --disable=E1101,E0102,F0401,W0232,R0903 --generate-rcfile > ~/.pylintrc
|
||||||
|
|
||||||
From now on you can then just run
|
From now on you can then just run
|
||||||
|
|
||||||
> pylint filename.py
|
> pylint filename.py
|
||||||
|
|
||||||
Ask Questions!
|
Ask Questions!
|
||||||
--------------
|
--------------
|
||||||
If any of the rules outlined in PEP 8 or in the sections above doesn't make sense, please
|
If any of the rules outlined in PEP 8 or in the sections above doesn't
|
||||||
don't hesitate to ask on the Evennia mailing list at http://evennia.com.
|
make sense, please don't hesitate to ask on the Evennia mailing list
|
||||||
Keeping our code style uniform makes this project much easier for a wider group
|
at http://evennia.com. Keeping our code style uniform makes this
|
||||||
of people to participate in.
|
project much easier for a wider group of people to participate in.
|
||||||
|
|
|
||||||
57
INSTALL
57
INSTALL
|
|
@ -1,9 +1,9 @@
|
||||||
|
|
||||||
-------------
|
-------------
|
||||||
Evennia Setup
|
Evennia Setup
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
You can find the updated and more detailed version of this page on
|
You can find the updated and more detailed version of this page on
|
||||||
http://code.google.com/p/evennia/wiki/GettingStarted
|
http://code.google.com/p/evennia/wiki/GettingStarted
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -12,45 +12,46 @@ Installation
|
||||||
|
|
||||||
* Make sure you have/install the prerequsites with minimum versions
|
* Make sure you have/install the prerequsites with minimum versions
|
||||||
listed on http://code.google.com/p/evennia/wiki/GettingStarted:
|
listed on http://code.google.com/p/evennia/wiki/GettingStarted:
|
||||||
- python
|
|
||||||
|
- python
|
||||||
- django
|
- django
|
||||||
- twisted + PIL
|
- twisted + PIL
|
||||||
- mercurial
|
- mercurial
|
||||||
- django-south (optional)
|
- django-south (optional, but highly recommended)
|
||||||
|
|
||||||
|
* Go to a directory on your harddrive where you want the 'evennia'
|
||||||
|
directory to be created, for example mud/.
|
||||||
|
|
||||||
* Go to a directory on your harddrive where you want the "evennia"
|
|
||||||
directory to be created.
|
|
||||||
|
|
||||||
$ cd mud/
|
$ cd mud/
|
||||||
|
|
||||||
* Get a copy of the Evennia source:
|
* Get a copy of the Evennia source:
|
||||||
|
|
||||||
$ hg clone https://code.google.com/p/evennia/ evennia
|
$ hg clone https://code.google.com/p/evennia/ evennia
|
||||||
|
|
||||||
* Change to the evennia/game directory and run the setup scripts.
|
* Change to the evennia/game directory and run the setup scripts.
|
||||||
|
|
||||||
$ cd evennia/game
|
$ cd evennia/game
|
||||||
|
|
||||||
$ python manage.py
|
$ python manage.py
|
||||||
|
|
||||||
* Edit the new game/settings.py if needed, then run
|
* Edit the new game/settings.py if needed, then run
|
||||||
(make sure to create an admin account when asked):
|
(make sure to create an admin account when asked):
|
||||||
|
|
||||||
$ python manage.py syncdb
|
$ python manage.py syncdb
|
||||||
|
|
||||||
* If you use django-south you need to also run
|
* If you use django-south you need to also run
|
||||||
|
|
||||||
$ python manage.py migrate
|
$ python manage.py migrate
|
||||||
|
|
||||||
|
|
||||||
Starting Evennia
|
Starting Evennia
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
$ python evennia.py -i start
|
* Start the server with
|
||||||
or
|
|
||||||
$ python evennia.py
|
$ python evennia.py -i start
|
||||||
|
|
||||||
for a menu of launch options.
|
or run without arguments for a menu of launch options.
|
||||||
See http://code.google.com/p/evennia/wiki/StartStopReload for more info.
|
See http://code.google.com/p/evennia/wiki/StartStopReload for more info.
|
||||||
|
|
||||||
* Start up your MUD client of choice and point it to your server and port 4000.
|
* Start up your MUD client of choice and point it to your server and port 4000.
|
||||||
|
|
@ -59,8 +60,14 @@ Starting Evennia
|
||||||
* Alternatively, you can find the web interface and webclient by
|
* Alternatively, you can find the web interface and webclient by
|
||||||
pointing your web browser to http://localhost:8000.
|
pointing your web browser to http://localhost:8000.
|
||||||
|
|
||||||
* Login with the email address and password you provided to the syncdb script.
|
* Login with the email address and password you provided when setting up the server.
|
||||||
Welcome to Evennia!
|
|
||||||
|
|
||||||
See also "Getting Started" on www.evennia.com for more verbose instructions and
|
|
||||||
the documentation wiki for further help.
|
Welcome to Evennia!
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
* See www.evennia.com for more information and help with how to
|
||||||
|
proceed from here.
|
||||||
|
|
||||||
|
* For questions, see the discussion group or the chat. Report bugs or
|
||||||
|
request features via the Issue Tracker.
|
||||||
4
LICENSE
4
LICENSE
|
|
@ -57,7 +57,7 @@ Licence Regulations
|
||||||
them, or by allowing the Copyright Holder to include your
|
them, or by allowing the Copyright Holder to include your
|
||||||
modifications in the Standard Version of the Package.
|
modifications in the Standard Version of the Package.
|
||||||
2. make other distribution arrangements with the Copyright Holder.
|
2. make other distribution arrangements with the Copyright Holder.
|
||||||
|
|
||||||
4. You may distribute the programs of this Package in object code or
|
4. You may distribute the programs of this Package in object code or
|
||||||
executable form, provided that you do at least ONE of the following:
|
executable form, provided that you do at least ONE of the following:
|
||||||
1. distribute a Standard Version of the executables and library
|
1. distribute a Standard Version of the executables and library
|
||||||
|
|
@ -117,7 +117,7 @@ Licence Regulations
|
||||||
9. The name of the Copyright Holder may not be used to endorse or
|
9. The name of the Copyright Holder may not be used to endorse or
|
||||||
promote products derived from this software without specific prior
|
promote products derived from this software without specific prior
|
||||||
written permission.
|
written permission.
|
||||||
|
|
||||||
10. Credits and attributions in the headers of all source and
|
10. Credits and attributions in the headers of all source and
|
||||||
documentation files must be left intact.
|
documentation files must be left intact.
|
||||||
|
|
||||||
|
|
|
||||||
136
README
136
README
|
|
@ -1,6 +1,6 @@
|
||||||
|
|
||||||
-----------------------------------
|
-----------------------------------
|
||||||
Evennia README
|
Evennia README
|
||||||
(http://evennia.com)
|
(http://evennia.com)
|
||||||
Beta hg (mercurial) version
|
Beta hg (mercurial) version
|
||||||
-----------------------------------
|
-----------------------------------
|
||||||
|
|
@ -9,39 +9,37 @@
|
||||||
About Evennia
|
About Evennia
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
Evennia is a MUD/MUX/MU* server that aims to provide a functional
|
Evennia is a MUD/MUX/MU* development system and server that aims to
|
||||||
bare-bones base for developers. Some of our main features are:
|
provide a functional bare-bones codebase for developers. Some of our main
|
||||||
|
features are:
|
||||||
|
|
||||||
* Coded and extended using normal Python modules.
|
* Coded and extended using normal Python modules.
|
||||||
* Extensive web integration due to our use of Django.
|
* Reload code without players logging off
|
||||||
* Runs its own Twisted webserver. Comes with game website and ajax web-browser mud client.
|
* Database handling and network connectivity are abstracted away
|
||||||
* Extensive current and potential connectivity and protocol-support through Twisted.
|
* Extensive web integration due to our use of Django.
|
||||||
* Extremely easy-to-manipulate SQL database back-end via Django
|
* Server runs game website and ajax web-browser mud client out of the box.
|
||||||
(djangoproject.com)
|
* Supports a slew of different connection protocols with Twisted.
|
||||||
* Powerful an extremely extendable bare-bones base system
|
* Extremely extendable to almost any sort of text-based multiplayer game
|
||||||
|
|
||||||
The Django framework has database abstraction abilities that give us
|
See the INSTALL file for help on setting up and running Evennia.
|
||||||
many features free, such as:
|
|
||||||
|
|
||||||
* The codebase will run transparently on MySQL, SQLite, or Postgres
|
|
||||||
* At the time of this document's writing, our SQL-backed application here
|
|
||||||
contains 0 lines of SQL. Django's database abstraction layer is absolutely
|
|
||||||
simple yet very powerful.
|
|
||||||
* For any model we outline for the server's use, we have the ability to
|
|
||||||
more or less automatically generate a web-based admin interface for it with
|
|
||||||
two lines of code. This lets you Create, Update, or Delete entries, as well
|
|
||||||
limit permissions for those abilities.
|
|
||||||
* On the web-based side of things, features such as automatic form validation,
|
|
||||||
abstraction of sessions and cookies, and access to whatever game data you
|
|
||||||
desire are all attractive.
|
|
||||||
|
|
||||||
See the INSTALL file for help on setting up and running Evennia.
|
|
||||||
|
|
||||||
|
|
||||||
Current Status
|
Current Status
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
Nov 2011:
|
March 2012:
|
||||||
|
Evennia's API has changed and simplified slightly in that the
|
||||||
|
base-modules where removed from game/gamesrc. Instead admins are
|
||||||
|
encouraged to explicitly create new modules under game/gamesrc/ when
|
||||||
|
they want to implement their game - gamesrc/ is empty by default
|
||||||
|
except for the example folders that contain template files to use for
|
||||||
|
this purpose. We also added the ev.py file, implementing a new, flat
|
||||||
|
API. Work is ongoing to add support for mud-specific telnet
|
||||||
|
extensions, notably the MSDP and GMCP out-of-band extensions. On the
|
||||||
|
community side, evennia's dev blog was started and linked on planet
|
||||||
|
Mud-dev aggregator.
|
||||||
|
|
||||||
|
Nov 2011:
|
||||||
After creating several different proof-of-concept game systems (in
|
After creating several different proof-of-concept game systems (in
|
||||||
contrib and privately) as well testing lots of things to make sure the
|
contrib and privately) as well testing lots of things to make sure the
|
||||||
implementation is basically sound, we are declaring Evennia out of
|
implementation is basically sound, we are declaring Evennia out of
|
||||||
|
|
@ -59,12 +57,12 @@ hackish, flakey and unstable code. With the Portal-Server split, the
|
||||||
Server can simply be rebooted while players connected to the Portal
|
Server can simply be rebooted while players connected to the Portal
|
||||||
remain connected. The two communicates over twisted's AMP protocol.
|
remain connected. The two communicates over twisted's AMP protocol.
|
||||||
|
|
||||||
May 2011:
|
May 2011:
|
||||||
The new version of Evennia, originally hitting trunk in Aug2010, is
|
The new version of Evennia, originally hitting trunk in Aug2010, is
|
||||||
maturing. All commands from the pre-Aug version, including IRC/IMC2
|
maturing. All commands from the pre-Aug version, including IRC/IMC2
|
||||||
support works again. An ajax web-client was added earlier in the year,
|
support works again. An ajax web-client was added earlier in the year,
|
||||||
including moving Evennia to be its own webserver (no more need for
|
including moving Evennia to be its own webserver (no more need for
|
||||||
Apache or django-testserver). Contrib-folder added.
|
Apache or django-testserver). Contrib-folder added.
|
||||||
|
|
||||||
Aug 2010:
|
Aug 2010:
|
||||||
Evennia-griatch-branch is ready for merging with trunk. This marks a
|
Evennia-griatch-branch is ready for merging with trunk. This marks a
|
||||||
|
|
@ -74,16 +72,16 @@ ScriptParents and Events) but should hopefully bring everything
|
||||||
together into one consistent package as code development continues.
|
together into one consistent package as code development continues.
|
||||||
|
|
||||||
May 2010:
|
May 2010:
|
||||||
Evennia is currently being heavily revised and cleaned from
|
Evennia is currently being heavily revised and cleaned from
|
||||||
the years of gradual piecemeal development. It is thus in a very
|
the years of gradual piecemeal development. It is thus in a very
|
||||||
'Alpha' stage at the moment. This means that old code snippets
|
'Alpha' stage at the moment. This means that old code snippets
|
||||||
will not be backwards compatabile. Changes touch almost all
|
will not be backwards compatabile. Changes touch almost all
|
||||||
parts of Evennia's innards, from the way Objects are handled
|
parts of Evennia's innards, from the way Objects are handled
|
||||||
to Events, Commands and Permissions.
|
to Events, Commands and Permissions.
|
||||||
|
|
||||||
April 2010:
|
April 2010:
|
||||||
Griatch takes over Maintainership of the Evennia project from
|
Griatch takes over Maintainership of the Evennia project from
|
||||||
the original creator Greg Taylor.
|
the original creator Greg Taylor.
|
||||||
|
|
||||||
(Earlier revisions, with previous maintainer, go back to 2005)
|
(Earlier revisions, with previous maintainer, go back to 2005)
|
||||||
|
|
||||||
|
|
@ -98,67 +96,67 @@ appreciate all help! Visit either of the following resources:
|
||||||
|
|
||||||
* Evennia Webpage
|
* Evennia Webpage
|
||||||
http://evennia.com
|
http://evennia.com
|
||||||
|
|
||||||
* Evennia manual (wiki)
|
* Evennia manual (wiki)
|
||||||
http://code.google.com/p/evennia/wiki/Index
|
http://code.google.com/p/evennia/wiki/Index
|
||||||
|
|
||||||
* Evennia Code Page (See INSTALL text for installation)
|
* Evennia Code Page (See INSTALL text for installation)
|
||||||
http://code.google.com/p/evennia/source/checkout
|
http://code.google.com/p/evennia/source/checkout
|
||||||
|
|
||||||
* Bug tracker
|
* Bug tracker
|
||||||
http://code.google.com/p/evennia/issues/list
|
http://code.google.com/p/evennia/issues/list
|
||||||
|
|
||||||
* IRC channel
|
* IRC channel
|
||||||
visit channel #evennia on the Freenode IRC network
|
visit channel #evennia on the Freenode IRC network
|
||||||
|
|
||||||
|
|
||||||
Directory structure
|
Directory structure
|
||||||
-------------------
|
-------------------
|
||||||
evennia
|
evennia
|
||||||
|
|
|
|
||||||
|_______src
|
| ev.py
|
||||||
| |___(engine-related dirs)
|
|_______game (start the server, settings)
|
||||||
|
|
|
||||||
|_______game (start the server)
|
|
||||||
| |___gamesrc
|
| |___gamesrc
|
||||||
| |___(game-related dirs)
|
| |___(game-related dirs)
|
||||||
|
|
|_______src
|
||||||
|_______contrib
|
| |___(engine-related dirs)
|
||||||
|
| |
|
||||||
|
|_______contrib
|
||||||
|
|
|
|
||||||
|_______docs
|
|_______docs
|
||||||
|
|
|
|
||||||
|_______locales
|
|_______locales
|
||||||
|
|
||||||
The two main directories you will spend most of your time in
|
ev.py is the API file. It contains easy shortcuts to most
|
||||||
are src/ and game/ (probably mostly game/).
|
of Evennia's functionality. Import ev into a python interpreter
|
||||||
|
(like ipython) and explore what's available.
|
||||||
|
|
||||||
Basically src/ contains everything related to
|
The game/ folder is where you develop your game. The root
|
||||||
running the gritty stuff behind the scenes. Unless you are an
|
of this directory contains the settings file and the executables
|
||||||
Evennia developer you should normally make sure never to edit
|
to start the server. Under game/gamesrc you will create the
|
||||||
things in src/, since this is where we push new revisions that
|
modules that will define your game.
|
||||||
may overwrite your changes when you update. You will however
|
|
||||||
need to have a good feeling for the resources supplied by
|
|
||||||
the functions in src, since accessing them correctly is the key
|
|
||||||
to making your dream game come true.
|
|
||||||
|
|
||||||
If src/ is the Evennia developer's domain, the game/ directory
|
src/ contains the Evennia library. As a normal user you should
|
||||||
on the other hand contains YOUR game. This is where you will
|
not edit anything in this folder - you will run into mercurial
|
||||||
define and extend the commands, objects and systems of Evennia
|
conflicts as we update things from our end. If you see code
|
||||||
to make your dream game. game/ contains the main server settings
|
you like (such as that of a default command), copy&paste it
|
||||||
and the actual evennia executable to start things. game/gamesrc/
|
into a new module in game/gamesrc/ instead. If you find that
|
||||||
holds all the templates for creating objects in your virtual world.
|
src/ doesn't support a functionality you need, issue a Feature
|
||||||
|
request or a bug report appropriately.
|
||||||
|
If you do add functionality or fix bugs in src yourself, please
|
||||||
|
consider contributing it to Evennia main to help us improve!
|
||||||
|
|
||||||
contrib/ contains optional code snippets. These are potentially useful
|
contrib/ contains optional code snippets. These are potentially useful
|
||||||
but deemed to be too game-specific to be part of the server itself.
|
but are deemed to be too game-specific to be part of the server itself.
|
||||||
Modules in contrib are not used unless you yourself decide to import
|
Modules in contrib are not used unless you yourself decide to import
|
||||||
and use them.
|
and use them.
|
||||||
|
|
||||||
docs/ contain offline versions of the documentation, you can use
|
docs/ contain offline versions of the documentation, you can use
|
||||||
python-sphinx to convert the raw data to nice-looking output for
|
python-sphinx to convert the raw data to nice-looking output for
|
||||||
printing etc. The online wiki is otherwise first to be updated.
|
printing etc. The online wiki is however the most updated version
|
||||||
|
of the documentation.
|
||||||
|
|
||||||
locales/ holds translations of the server strings to other languages
|
locales/ holds translations of the server strings to other languages
|
||||||
than English.
|
than English.
|
||||||
|
|
||||||
With this little first orientation, you should head into the online
|
Enjoy!
|
||||||
Evennia wiki documentation to get going with the codebase.
|
|
||||||
64
ev.py
64
ev.py
|
|
@ -2,42 +2,42 @@
|
||||||
|
|
||||||
Central API for the Evennia MUD/MUX/MU* creation system.
|
Central API for the Evennia MUD/MUX/MU* creation system.
|
||||||
|
|
||||||
This basically a set of shortcuts to the main modules in src/. Import this
|
This basically a set of shortcuts to the main modules in src/. Import this
|
||||||
from your code or explore it interactively from ./manage.py shell (or a normal
|
from your code or explore it interactively from ./manage.py shell (or a normal
|
||||||
python shell if you set DJANGO_SETTINGS_MODULE manually).
|
python shell if you set DJANGO_SETTINGS_MODULE manually).
|
||||||
|
|
||||||
Notes:
|
Notes:
|
||||||
|
|
||||||
1) You should import things explicitly from the root of this module - you can not use
|
1) You should import things explicitly from the root of this module - you can not use
|
||||||
dot-notation to import deeper. Hence, to access a default command, you can do the
|
dot-notation to import deeper. Hence, to access a default command, you can do the
|
||||||
following:
|
following:
|
||||||
|
|
||||||
import ev
|
import ev
|
||||||
ev.default_cmds.CmdLook
|
ev.default_cmds.CmdLook
|
||||||
or
|
or
|
||||||
from ev import default_cmds
|
from ev import default_cmds
|
||||||
default_cmds.CmdLook
|
default_cmds.CmdLook
|
||||||
|
|
||||||
But trying to import CmdLook directly with "from ev.default_cmds import CmdLook" will
|
But trying to import CmdLook directly with "from ev.default_cmds import CmdLook" will
|
||||||
not work since default_cmds is a property on the "ev" module, not a module of its own.
|
not work since default_cmds is a property on the "ev" module, not a module of its own.
|
||||||
2) db_* are shortcuts to initiated versions of Evennia's django database managers (e.g.
|
2) db_* are shortcuts to initiated versions of Evennia's django database managers (e.g.
|
||||||
db_objects is an alias for ObjectDB.objects). These allows for exploring the database in
|
db_objects is an alias for ObjectDB.objects). These allows for exploring the database in
|
||||||
various ways. Please note that the evennia-specific methods in the managers return
|
various ways. Please note that the evennia-specific methods in the managers return
|
||||||
typeclasses (or lists of typeclasses), whereas the default django ones (filter etc)
|
typeclasses (or lists of typeclasses), whereas the default django ones (filter etc)
|
||||||
return database objects. You can convert between the two easily via dbobj.typeclass and
|
return database objects. You can convert between the two easily via dbobj.typeclass and
|
||||||
typeclass.dbobj, but it's worth to remember this difference.
|
typeclass.dbobj, but it's worth to remember this difference.
|
||||||
3) You -have- to use the create_* functions (shortcuts to src.utils.create) to create new
|
3) You -have- to use the create_* functions (shortcuts to src.utils.create) to create new
|
||||||
Typeclassed game entities (Objects, Scripts or Players). Just initializing e.g. the Player class will
|
Typeclassed game entities (Objects, Scripts or Players). Just initializing e.g. the Player class will
|
||||||
-not- set up Typeclasses correctly and will lead to errors. Other types of database objects
|
-not- set up Typeclasses correctly and will lead to errors. Other types of database objects
|
||||||
can be created normally, but there are conveniant create_* functions for those too, making
|
can be created normally, but there are conveniant create_* functions for those too, making
|
||||||
some more error checking.
|
some more error checking.
|
||||||
4) "settings" links to Evennia's game/settings file. "settings_full" shows all of django's available
|
4) "settings" links to Evennia's game/settings file. "settings_full" shows all of django's available
|
||||||
settings. Note that you cannot change settings from here in a meaningful way, you need to update
|
settings. Note that you cannot change settings from here in a meaningful way, you need to update
|
||||||
game/settings.py and restart the server.
|
game/settings.py and restart the server.
|
||||||
5) The API accesses all relevant and most-neeeded functions/classes from src/, but might not
|
5) The API accesses all relevant and most-neeeded functions/classes from src/, but might not
|
||||||
always include all helper-functions referenced from each such entity. To get to those, access
|
always include all helper-functions referenced from each such entity. To get to those, access
|
||||||
the modules in src/ directly. You can always do this anyway, if you do not want to go through
|
the modules in src/ directly. You can always do this anyway, if you do not want to go through
|
||||||
this API.
|
this API.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
@ -53,7 +53,7 @@ if __name__ == "__main__":
|
||||||
| not be run on its own, but be imported and accessed as described
|
| not be run on its own, but be imported and accessed as described
|
||||||
| above.
|
| above.
|
||||||
|
|
|
|
||||||
| To start the Evennia server, see game/manage.py and game/evennia.py.
|
| To start the Evennia server, see game/manage.py and game/evennia.py.
|
||||||
| More help can be found at http://www.evennia.com.
|
| More help can be found at http://www.evennia.com.
|
||||||
"""
|
"""
|
||||||
print info
|
print info
|
||||||
|
|
@ -86,11 +86,11 @@ del sys, os
|
||||||
|
|
||||||
README = __doc__
|
README = __doc__
|
||||||
|
|
||||||
# help entries
|
# help entries
|
||||||
from src.help.models import HelpEntry
|
from src.help.models import HelpEntry
|
||||||
db_helpentries = HelpEntry.objects
|
db_helpentries = HelpEntry.objects
|
||||||
|
|
||||||
# players
|
# players
|
||||||
from src.players.player import Player
|
from src.players.player import Player
|
||||||
from src.players.models import PlayerDB, PlayerAttribute, PlayerNick
|
from src.players.models import PlayerDB, PlayerAttribute, PlayerNick
|
||||||
db_players = PlayerDB.objects
|
db_players = PlayerDB.objects
|
||||||
|
|
@ -106,8 +106,8 @@ from src.commands import default as default_cmds
|
||||||
class SystemCmds(object):
|
class SystemCmds(object):
|
||||||
"""
|
"""
|
||||||
Creating commands with keys set to these constants will make
|
Creating commands with keys set to these constants will make
|
||||||
them system commands called as a replacement by the parser when
|
them system commands called as a replacement by the parser when
|
||||||
special situations occur. If not defined, the hard-coded
|
special situations occur. If not defined, the hard-coded
|
||||||
responses in the server are used.
|
responses in the server are used.
|
||||||
|
|
||||||
CMD_NOINPUT - no input was given on command line
|
CMD_NOINPUT - no input was given on command line
|
||||||
|
|
@ -116,7 +116,7 @@ class SystemCmds(object):
|
||||||
CMD_CHANNEL - the command name is a channel name
|
CMD_CHANNEL - the command name is a channel name
|
||||||
CMD_LOGINSTART - this command will be called as the very
|
CMD_LOGINSTART - this command will be called as the very
|
||||||
first command when a player connects to
|
first command when a player connects to
|
||||||
the server.
|
the server.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from src.commands import cmdhandler
|
from src.commands import cmdhandler
|
||||||
|
|
@ -125,13 +125,13 @@ class SystemCmds(object):
|
||||||
CMD_MULTIMATCH = cmdhandler.CMD_MULTIMATCH
|
CMD_MULTIMATCH = cmdhandler.CMD_MULTIMATCH
|
||||||
CMD_CHANNEL = cmdhandler.CMD_CHANNEL
|
CMD_CHANNEL = cmdhandler.CMD_CHANNEL
|
||||||
CMD_LOGINSTART = cmdhandler.CMD_LOGINSTART
|
CMD_LOGINSTART = cmdhandler.CMD_LOGINSTART
|
||||||
del cmdhandler
|
del cmdhandler
|
||||||
syscmdkeys = SystemCmds()
|
syscmdkeys = SystemCmds()
|
||||||
|
|
||||||
# locks
|
# locks
|
||||||
from src.locks import lockfuncs
|
from src.locks import lockfuncs
|
||||||
|
|
||||||
# scripts
|
# scripts
|
||||||
from src.scripts.scripts import Script
|
from src.scripts.scripts import Script
|
||||||
from src.scripts.models import ScriptDB, ScriptAttribute
|
from src.scripts.models import ScriptDB, ScriptAttribute
|
||||||
db_scripts = ScriptDB.objects
|
db_scripts = ScriptDB.objects
|
||||||
|
|
@ -154,7 +154,7 @@ db_objects = ObjectDB.objects
|
||||||
#db_objattrs = ObjAttribute.objects
|
#db_objattrs = ObjAttribute.objects
|
||||||
del ObjAttribute, Alias, ObjectNick, ObjectDB
|
del ObjAttribute, Alias, ObjectNick, ObjectDB
|
||||||
|
|
||||||
# server
|
# server
|
||||||
from src.server.models import ServerConfig
|
from src.server.models import ServerConfig
|
||||||
db_serverconfigs = ServerConfig.objects
|
db_serverconfigs = ServerConfig.objects
|
||||||
del ServerConfig
|
del ServerConfig
|
||||||
|
|
|
||||||
|
|
@ -4,5 +4,5 @@
|
||||||
|
|
||||||
django >= 1.2
|
django >= 1.2
|
||||||
twisted >= 10.0
|
twisted >= 10.0
|
||||||
pil
|
pil
|
||||||
south >= 0.7
|
south >= 0.7
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
"""
|
"""
|
||||||
This special Python config file sets the default encoding for
|
This special Python config file sets the default encoding for
|
||||||
the codebase to UTF-8 instead of ascii. This allows for just
|
the codebase to UTF-8 instead of ascii. This allows for just
|
||||||
about any language to be used in-game.
|
about any language to be used in-game.
|
||||||
|
|
||||||
It is not advisable to change the value set below, as
|
It is not advisable to change the value set below, as
|
||||||
|
|
|
||||||
|
|
@ -2,35 +2,35 @@
|
||||||
Command handler
|
Command handler
|
||||||
|
|
||||||
This module contains the infrastructure for accepting commands on the
|
This module contains the infrastructure for accepting commands on the
|
||||||
command line. The process is as follows:
|
command line. The process is as follows:
|
||||||
|
|
||||||
1) The calling object (caller) inputs a string and triggers the command parsing system.
|
1) The calling object (caller) inputs a string and triggers the command parsing system.
|
||||||
2) The system checks the state of the caller - loggedin or not
|
2) The system checks the state of the caller - loggedin or not
|
||||||
3) If no command string was supplied, we search the merged cmdset for system command CMD_NOINPUT
|
3) If no command string was supplied, we search the merged cmdset for system command CMD_NOINPUT
|
||||||
and branches to execute that. --> Finished
|
and branches to execute that. --> Finished
|
||||||
4) Cmdsets are gathered from different sources (in order of dropping priority):
|
4) Cmdsets are gathered from different sources (in order of dropping priority):
|
||||||
channels - all available channel names are auto-created into a cmdset, to allow
|
channels - all available channel names are auto-created into a cmdset, to allow
|
||||||
for giving the channel name and have the following immediately
|
for giving the channel name and have the following immediately
|
||||||
sent to the channel. The sending is performed by the CMD_CHANNEL
|
sent to the channel. The sending is performed by the CMD_CHANNEL
|
||||||
system command.
|
system command.
|
||||||
object cmdsets - all objects at caller's location are scanned for non-empty
|
object cmdsets - all objects at caller's location are scanned for non-empty
|
||||||
cmdsets. This includes cmdsets on exits.
|
cmdsets. This includes cmdsets on exits.
|
||||||
caller - the caller is searched for its own currently active cmdset.
|
caller - the caller is searched for its own currently active cmdset.
|
||||||
player - lastly the cmdsets defined on caller.player are added.
|
player - lastly the cmdsets defined on caller.player are added.
|
||||||
5) All the gathered cmdsets (if more than one) are merged into one using the cmdset priority rules.
|
5) All the gathered cmdsets (if more than one) are merged into one using the cmdset priority rules.
|
||||||
6) If merged cmdset is empty, raise NoCmdSet exception (this should not happen, at least the
|
6) If merged cmdset is empty, raise NoCmdSet exception (this should not happen, at least the
|
||||||
player should have a default cmdset available at all times). --> Finished
|
player should have a default cmdset available at all times). --> Finished
|
||||||
7) The raw input string is parsed using the parser defined by settings.COMMAND_PARSER. It
|
7) The raw input string is parsed using the parser defined by settings.COMMAND_PARSER. It
|
||||||
uses the available commands from the merged cmdset to know which commands to look for and
|
uses the available commands from the merged cmdset to know which commands to look for and
|
||||||
returns one or many matches.
|
returns one or many matches.
|
||||||
8) If match list is empty, branch to system command CMD_NOMATCH --> Finished
|
8) If match list is empty, branch to system command CMD_NOMATCH --> Finished
|
||||||
9) If match list has more than one element, branch to system command CMD_MULTIMATCH --> Finished
|
9) If match list has more than one element, branch to system command CMD_MULTIMATCH --> Finished
|
||||||
10) A single match was found. If this is a channel-command (i.e. the command name is that of a channel),
|
10) A single match was found. If this is a channel-command (i.e. the command name is that of a channel),
|
||||||
branch to CMD_CHANNEL --> Finished
|
branch to CMD_CHANNEL --> Finished
|
||||||
11) At this point we have found a normal command. We assign useful variables to it that
|
11) At this point we have found a normal command. We assign useful variables to it that
|
||||||
will be available to the command coder at run-time.
|
will be available to the command coder at run-time.
|
||||||
12) We have a unique cmdobject, primed for use. Call all hooks:
|
12) We have a unique cmdobject, primed for use. Call all hooks:
|
||||||
at_pre_cmd(), cmdobj.parse(), cmdobj.func() and finally at_post_cmd().
|
at_pre_cmd(), cmdobj.parse(), cmdobj.func() and finally at_post_cmd().
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
@ -41,19 +41,19 @@ from twisted.internet.defer import inlineCallbacks, returnValue
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from src.comms.channelhandler import CHANNELHANDLER
|
from src.comms.channelhandler import CHANNELHANDLER
|
||||||
from src.commands.cmdsethandler import import_cmdset
|
from src.commands.cmdsethandler import import_cmdset
|
||||||
from src.utils import logger, utils
|
from src.utils import logger, utils
|
||||||
from src.commands.cmdparser import at_multimatch_cmd
|
from src.commands.cmdparser import at_multimatch_cmd
|
||||||
|
|
||||||
#This switches the command parser to a user-defined one.
|
#This switches the command parser to a user-defined one.
|
||||||
# You have to restart the server for this to take effect.
|
# You have to restart the server for this to take effect.
|
||||||
COMMAND_PARSER = utils.mod_import(*settings.COMMAND_PARSER.rsplit('.', 1))
|
COMMAND_PARSER = utils.mod_import(*settings.COMMAND_PARSER.rsplit('.', 1))
|
||||||
|
|
||||||
# There are a few system-hardcoded command names. These
|
# There are a few system-hardcoded command names. These
|
||||||
# allow for custom behaviour when the command handler hits
|
# allow for custom behaviour when the command handler hits
|
||||||
# special situations -- it then calls a normal Command
|
# special situations -- it then calls a normal Command
|
||||||
# that you can customize!
|
# that you can customize!
|
||||||
# Import these variables and use them rather than trying
|
# Import these variables and use them rather than trying
|
||||||
# to remember the actual string constants.
|
# to remember the actual string constants.
|
||||||
|
|
||||||
CMD_NOINPUT = "__noinput_command"
|
CMD_NOINPUT = "__noinput_command"
|
||||||
CMD_NOMATCH = "__nomatch_command"
|
CMD_NOMATCH = "__nomatch_command"
|
||||||
|
|
@ -61,12 +61,12 @@ CMD_MULTIMATCH = "__multimatch_command"
|
||||||
CMD_CHANNEL = "__send_to_channel_command"
|
CMD_CHANNEL = "__send_to_channel_command"
|
||||||
# this is the name of the command the engine calls when the player
|
# this is the name of the command the engine calls when the player
|
||||||
# connects. It is expected to show the login screen.
|
# connects. It is expected to show the login screen.
|
||||||
CMD_LOGINSTART = "__unloggedin_look_command"
|
CMD_LOGINSTART = "__unloggedin_look_command"
|
||||||
|
|
||||||
|
|
||||||
class NoCmdSets(Exception):
|
class NoCmdSets(Exception):
|
||||||
"No cmdsets found. Critical error."
|
"No cmdsets found. Critical error."
|
||||||
pass
|
pass
|
||||||
class ExecSystemCommand(Exception):
|
class ExecSystemCommand(Exception):
|
||||||
"Run a system command"
|
"Run a system command"
|
||||||
def __init__(self, syscmd, sysarg):
|
def __init__(self, syscmd, sysarg):
|
||||||
|
|
@ -77,10 +77,10 @@ class ExecSystemCommand(Exception):
|
||||||
@inlineCallbacks
|
@inlineCallbacks
|
||||||
def get_and_merge_cmdsets(caller):
|
def get_and_merge_cmdsets(caller):
|
||||||
"""
|
"""
|
||||||
Gather all relevant cmdsets and merge them. Note
|
Gather all relevant cmdsets and merge them. Note
|
||||||
that this is only relevant for logged-in callers.
|
that this is only relevant for logged-in callers.
|
||||||
"""
|
"""
|
||||||
# The calling object's cmdset
|
# The calling object's cmdset
|
||||||
try:
|
try:
|
||||||
yield caller.at_cmdset_get()
|
yield caller.at_cmdset_get()
|
||||||
except Exception:
|
except Exception:
|
||||||
|
|
@ -89,17 +89,17 @@ def get_and_merge_cmdsets(caller):
|
||||||
caller_cmdset = caller.cmdset.current
|
caller_cmdset = caller.cmdset.current
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
caller_cmdset = None
|
caller_cmdset = None
|
||||||
|
|
||||||
# Create cmdset for all player's available channels
|
# Create cmdset for all player's available channels
|
||||||
channel_cmdset = None
|
channel_cmdset = None
|
||||||
if not caller_cmdset.no_channels:
|
if not caller_cmdset.no_channels:
|
||||||
channel_cmdset = yield CHANNELHANDLER.get_cmdset(caller)
|
channel_cmdset = yield CHANNELHANDLER.get_cmdset(caller)
|
||||||
|
|
||||||
# Gather cmdsets from location, objects in location or carried
|
# Gather cmdsets from location, objects in location or carried
|
||||||
local_objects_cmdsets = [None]
|
local_objects_cmdsets = [None]
|
||||||
location = None
|
location = None
|
||||||
if hasattr(caller, "location"):
|
if hasattr(caller, "location"):
|
||||||
location = caller.location
|
location = caller.location
|
||||||
if location and not caller_cmdset.no_objs:
|
if location and not caller_cmdset.no_objs:
|
||||||
# Gather all cmdsets stored on objects in the room and
|
# Gather all cmdsets stored on objects in the room and
|
||||||
# also in the caller's inventory and the location itself
|
# also in the caller's inventory and the location itself
|
||||||
|
|
@ -113,19 +113,19 @@ def get_and_merge_cmdsets(caller):
|
||||||
local_objects_cmdsets = yield [obj.cmdset.current for obj in local_objlist
|
local_objects_cmdsets = yield [obj.cmdset.current for obj in local_objlist
|
||||||
if (obj.cmdset.current and obj.locks.check(caller, 'call', no_superuser_bypass=True))]
|
if (obj.cmdset.current and obj.locks.check(caller, 'call', no_superuser_bypass=True))]
|
||||||
for cset in local_objects_cmdsets:
|
for cset in local_objects_cmdsets:
|
||||||
#This is necessary for object sets, or we won't be able to separate
|
#This is necessary for object sets, or we won't be able to separate
|
||||||
#the command sets from each other in a busy room.
|
#the command sets from each other in a busy room.
|
||||||
cset.old_duplicates = cset.duplicates
|
cset.old_duplicates = cset.duplicates
|
||||||
cset.duplicates = True
|
cset.duplicates = True
|
||||||
|
|
||||||
# Player object's commandsets
|
# Player object's commandsets
|
||||||
try:
|
try:
|
||||||
player_cmdset = caller.player.cmdset.current
|
player_cmdset = caller.player.cmdset.current
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
player_cmdset = None
|
player_cmdset = None
|
||||||
|
|
||||||
cmdsets = [caller_cmdset] + [player_cmdset] + [channel_cmdset] + local_objects_cmdsets
|
cmdsets = [caller_cmdset] + [player_cmdset] + [channel_cmdset] + local_objects_cmdsets
|
||||||
# weed out all non-found sets
|
# weed out all non-found sets
|
||||||
cmdsets = yield [cmdset for cmdset in cmdsets if cmdset]
|
cmdsets = yield [cmdset for cmdset in cmdsets if cmdset]
|
||||||
# sort cmdsets after reverse priority (highest prio are merged in last)
|
# sort cmdsets after reverse priority (highest prio are merged in last)
|
||||||
cmdsets = yield sorted(cmdsets, key=lambda x: x.priority)
|
cmdsets = yield sorted(cmdsets, key=lambda x: x.priority)
|
||||||
|
|
@ -134,9 +134,9 @@ def get_and_merge_cmdsets(caller):
|
||||||
# Merge all command sets into one, beginning with the lowest-prio one
|
# Merge all command sets into one, beginning with the lowest-prio one
|
||||||
cmdset = cmdsets.pop(0)
|
cmdset = cmdsets.pop(0)
|
||||||
for merging_cmdset in cmdsets:
|
for merging_cmdset in cmdsets:
|
||||||
#print "<%s(%s,%s)> onto <%s(%s,%s)>" % (merging_cmdset.key, merging_cmdset.priority, merging_cmdset.mergetype,
|
#print "<%s(%s,%s)> onto <%s(%s,%s)>" % (merging_cmdset.key, merging_cmdset.priority, merging_cmdset.mergetype,
|
||||||
# cmdset.key, cmdset.priority, cmdset.mergetype)
|
# cmdset.key, cmdset.priority, cmdset.mergetype)
|
||||||
cmdset = yield merging_cmdset + cmdset
|
cmdset = yield merging_cmdset + cmdset
|
||||||
else:
|
else:
|
||||||
cmdset = None
|
cmdset = None
|
||||||
|
|
||||||
|
|
@ -146,26 +146,26 @@ def get_and_merge_cmdsets(caller):
|
||||||
returnValue(cmdset)
|
returnValue(cmdset)
|
||||||
|
|
||||||
|
|
||||||
# Main command-handler function
|
# Main command-handler function
|
||||||
|
|
||||||
@inlineCallbacks
|
@inlineCallbacks
|
||||||
def cmdhandler(caller, raw_string, testing=False):
|
def cmdhandler(caller, raw_string, testing=False):
|
||||||
"""
|
"""
|
||||||
This is the main function to handle any string sent to the engine.
|
This is the main function to handle any string sent to the engine.
|
||||||
|
|
||||||
caller - calling object
|
caller - calling object
|
||||||
raw_string - the command string given on the command line
|
raw_string - the command string given on the command line
|
||||||
testing - if we should actually execute the command or not.
|
testing - if we should actually execute the command or not.
|
||||||
if True, the command instance will be returned instead.
|
if True, the command instance will be returned instead.
|
||||||
|
|
||||||
Note that this function returns a deferred!
|
Note that this function returns a deferred!
|
||||||
"""
|
"""
|
||||||
try: # catch bugs in cmdhandler itself
|
try: # catch bugs in cmdhandler itself
|
||||||
try: # catch special-type commands
|
try: # catch special-type commands
|
||||||
|
|
||||||
cmdset = yield get_and_merge_cmdsets(caller)
|
cmdset = yield get_and_merge_cmdsets(caller)
|
||||||
if not cmdset:
|
if not cmdset:
|
||||||
# this is bad and shouldn't happen.
|
# this is bad and shouldn't happen.
|
||||||
raise NoCmdSets
|
raise NoCmdSets
|
||||||
|
|
||||||
raw_string = raw_string.strip()
|
raw_string = raw_string.strip()
|
||||||
|
|
@ -182,7 +182,7 @@ def cmdhandler(caller, raw_string, testing=False):
|
||||||
if not matches:
|
if not matches:
|
||||||
# No commands match our entered command
|
# No commands match our entered command
|
||||||
syscmd = yield cmdset.get(CMD_NOMATCH)
|
syscmd = yield cmdset.get(CMD_NOMATCH)
|
||||||
if syscmd:
|
if syscmd:
|
||||||
sysarg = raw_string
|
sysarg = raw_string
|
||||||
else:
|
else:
|
||||||
sysarg = "Huh? (Type \"help\" for help)"
|
sysarg = "Huh? (Type \"help\" for help)"
|
||||||
|
|
@ -197,43 +197,43 @@ def cmdhandler(caller, raw_string, testing=False):
|
||||||
else:
|
else:
|
||||||
sysarg = yield at_multimatch_cmd(caller, matches)
|
sysarg = yield at_multimatch_cmd(caller, matches)
|
||||||
raise ExecSystemCommand(syscmd, sysarg)
|
raise ExecSystemCommand(syscmd, sysarg)
|
||||||
|
|
||||||
# At this point, we have a unique command match.
|
# At this point, we have a unique command match.
|
||||||
match = matches[0]
|
match = matches[0]
|
||||||
cmdname, args, cmd = match[0], match[1], match[2]
|
cmdname, args, cmd = match[0], match[1], match[2]
|
||||||
|
|
||||||
# Check if this is a Channel match.
|
# Check if this is a Channel match.
|
||||||
if hasattr(cmd, 'is_channel') and cmd.is_channel:
|
if hasattr(cmd, 'is_channel') and cmd.is_channel:
|
||||||
# even if a user-defined syscmd is not defined, the
|
# even if a user-defined syscmd is not defined, the
|
||||||
# found cmd is already a system command in its own right.
|
# found cmd is already a system command in its own right.
|
||||||
syscmd = yield cmdset.get(CMD_CHANNEL)
|
syscmd = yield cmdset.get(CMD_CHANNEL)
|
||||||
if syscmd:
|
if syscmd:
|
||||||
# replace system command with custom version
|
# replace system command with custom version
|
||||||
cmd = syscmd
|
cmd = syscmd
|
||||||
sysarg = "%s:%s" % (cmdname, args)
|
sysarg = "%s:%s" % (cmdname, args)
|
||||||
raise ExecSystemCommand(cmd, sysarg)
|
raise ExecSystemCommand(cmd, sysarg)
|
||||||
|
|
||||||
# A normal command.
|
# A normal command.
|
||||||
|
|
||||||
# Assign useful variables to the instance
|
# Assign useful variables to the instance
|
||||||
cmd.caller = caller
|
cmd.caller = caller
|
||||||
cmd.cmdstring = cmdname
|
cmd.cmdstring = cmdname
|
||||||
cmd.args = args
|
cmd.args = args
|
||||||
cmd.cmdset = cmdset
|
cmd.cmdset = cmdset
|
||||||
|
|
||||||
if hasattr(cmd, 'obj') and hasattr(cmd.obj, 'scripts'):
|
if hasattr(cmd, 'obj') and hasattr(cmd.obj, 'scripts'):
|
||||||
# cmd.obj are automatically made available.
|
# cmd.obj are automatically made available.
|
||||||
# we make sure to validate its scripts.
|
# we make sure to validate its scripts.
|
||||||
yield cmd.obj.scripts.validate()
|
yield cmd.obj.scripts.validate()
|
||||||
|
|
||||||
if testing:
|
if testing:
|
||||||
# only return the command instance
|
# only return the command instance
|
||||||
returnValue(cmd)
|
returnValue(cmd)
|
||||||
|
|
||||||
# pre-command hook
|
# pre-command hook
|
||||||
yield cmd.at_pre_cmd()
|
yield cmd.at_pre_cmd()
|
||||||
|
|
||||||
# Parse and execute
|
# Parse and execute
|
||||||
yield cmd.parse()
|
yield cmd.parse()
|
||||||
# (return value is normally None)
|
# (return value is normally None)
|
||||||
ret = yield cmd.func()
|
ret = yield cmd.func()
|
||||||
|
|
@ -246,15 +246,15 @@ def cmdhandler(caller, raw_string, testing=False):
|
||||||
# accessible by the next command.
|
# accessible by the next command.
|
||||||
caller.ndb.last_cmd = yield copy(cmd)
|
caller.ndb.last_cmd = yield copy(cmd)
|
||||||
else:
|
else:
|
||||||
caller.ndb.last_cmd = None
|
caller.ndb.last_cmd = None
|
||||||
|
|
||||||
# Done! This returns a deferred. By default, Evennia does
|
# Done! This returns a deferred. By default, Evennia does
|
||||||
# not use this at all.
|
# not use this at all.
|
||||||
returnValue(ret)
|
returnValue(ret)
|
||||||
|
|
||||||
except ExecSystemCommand, exc:
|
except ExecSystemCommand, exc:
|
||||||
# Not a normal command: run a system command, if available,
|
# Not a normal command: run a system command, if available,
|
||||||
# or fall back to a return string.
|
# or fall back to a return string.
|
||||||
syscmd = exc.syscmd
|
syscmd = exc.syscmd
|
||||||
sysarg = exc.sysarg
|
sysarg = exc.sysarg
|
||||||
if syscmd:
|
if syscmd:
|
||||||
|
|
@ -265,9 +265,9 @@ def cmdhandler(caller, raw_string, testing=False):
|
||||||
|
|
||||||
if hasattr(syscmd, 'obj') and hasattr(syscmd.obj, 'scripts'):
|
if hasattr(syscmd, 'obj') and hasattr(syscmd.obj, 'scripts'):
|
||||||
# cmd.obj is automatically made available.
|
# cmd.obj is automatically made available.
|
||||||
# we make sure to validate its scripts.
|
# we make sure to validate its scripts.
|
||||||
yield syscmd.obj.scripts.validate()
|
yield syscmd.obj.scripts.validate()
|
||||||
|
|
||||||
if testing:
|
if testing:
|
||||||
# only return the command instance
|
# only return the command instance
|
||||||
returnValue(syscmd)
|
returnValue(syscmd)
|
||||||
|
|
@ -282,21 +282,21 @@ def cmdhandler(caller, raw_string, testing=False):
|
||||||
except NoCmdSets:
|
except NoCmdSets:
|
||||||
# Critical error.
|
# Critical error.
|
||||||
string = "No command sets found! This is a sign of a critical bug.\n"
|
string = "No command sets found! This is a sign of a critical bug.\n"
|
||||||
string += "The error was logged.\n"
|
string += "The error was logged.\n"
|
||||||
string += "If logging out/in doesn't solve the problem, try to "
|
string += "If logging out/in doesn't solve the problem, try to "
|
||||||
string += "contact the server admin through some other means "
|
string += "contact the server admin through some other means "
|
||||||
string += "for assistance."
|
string += "for assistance."
|
||||||
caller.msg(string)
|
caller.msg(string)
|
||||||
logger.log_errmsg("No cmdsets found: %s" % caller)
|
logger.log_errmsg("No cmdsets found: %s" % caller)
|
||||||
|
|
||||||
except Exception:
|
except Exception:
|
||||||
# We should not end up here. If we do, it's a programming bug.
|
# We should not end up here. If we do, it's a programming bug.
|
||||||
string = "%s\nAbove traceback is from an untrapped error."
|
string = "%s\nAbove traceback is from an untrapped error."
|
||||||
string += " Please file a bug report."
|
string += " Please file a bug report."
|
||||||
logger.log_trace(string)
|
logger.log_trace(string)
|
||||||
caller.msg(string % format_exc())
|
caller.msg(string % format_exc())
|
||||||
|
|
||||||
except Exception:
|
except Exception:
|
||||||
# This catches exceptions in cmdhandler exceptions themselves
|
# This catches exceptions in cmdhandler exceptions themselves
|
||||||
string = "%s\nAbove traceback is from a Command handler bug."
|
string = "%s\nAbove traceback is from a Command handler bug."
|
||||||
string += " Please contact an admin and/or file a bug report."
|
string += " Please contact an admin and/or file a bug report."
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,9 @@ from src.utils.logger import log_trace
|
||||||
|
|
||||||
def cmdparser(raw_string, cmdset, caller, match_index=None):
|
def cmdparser(raw_string, cmdset, caller, match_index=None):
|
||||||
"""
|
"""
|
||||||
This function is called by the cmdhandler once it has
|
This function is called by the cmdhandler once it has
|
||||||
gathered all valid cmdsets for the calling player. raw_string
|
gathered all valid cmdsets for the calling player. raw_string
|
||||||
is the unparsed text entered by the caller.
|
is the unparsed text entered by the caller.
|
||||||
|
|
||||||
The cmdparser understand the following command combinations (where
|
The cmdparser understand the following command combinations (where
|
||||||
[] marks optional parts.
|
[] marks optional parts.
|
||||||
|
|
@ -19,20 +19,20 @@ def cmdparser(raw_string, cmdset, caller, match_index=None):
|
||||||
[cmdname[ cmdname2 cmdname3 ...] [the rest]
|
[cmdname[ cmdname2 cmdname3 ...] [the rest]
|
||||||
|
|
||||||
A command may consist of any number of space-separated words of any
|
A command may consist of any number of space-separated words of any
|
||||||
length, and contain any character.
|
length, and contain any character.
|
||||||
|
|
||||||
The parser makes use of the cmdset to find command candidates. The
|
The parser makes use of the cmdset to find command candidates. The
|
||||||
parser return a list of matches. Each match is a tuple with its
|
parser return a list of matches. Each match is a tuple with its
|
||||||
first three elements being the parsed cmdname (lower case),
|
first three elements being the parsed cmdname (lower case),
|
||||||
the remaining arguments, and the matched cmdobject from the cmdset.
|
the remaining arguments, and the matched cmdobject from the cmdset.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def create_match(cmdname, string, cmdobj):
|
def create_match(cmdname, string, cmdobj):
|
||||||
"""
|
"""
|
||||||
Evaluates the quality of a match by counting how many chars of cmdname
|
Evaluates the quality of a match by counting how many chars of cmdname
|
||||||
matches string (counting from beginning of string). We also calculate
|
matches string (counting from beginning of string). We also calculate
|
||||||
a ratio from 0-1 describing how much cmdname matches string.
|
a ratio from 0-1 describing how much cmdname matches string.
|
||||||
We return a tuple (cmdname, count, ratio, args, cmdobj).
|
We return a tuple (cmdname, count, ratio, args, cmdobj).
|
||||||
|
|
||||||
"""
|
"""
|
||||||
cmdlen, strlen = len(cmdname), len(string)
|
cmdlen, strlen = len(cmdname), len(string)
|
||||||
|
|
@ -41,13 +41,13 @@ def cmdparser(raw_string, cmdset, caller, match_index=None):
|
||||||
return (cmdname, args, cmdobj, cmdlen, mratio)
|
return (cmdname, args, cmdobj, cmdlen, mratio)
|
||||||
|
|
||||||
if not raw_string:
|
if not raw_string:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
matches = []
|
matches = []
|
||||||
|
|
||||||
# match everything that begins with a matching cmdname.
|
# match everything that begins with a matching cmdname.
|
||||||
l_raw_string = raw_string.lower()
|
l_raw_string = raw_string.lower()
|
||||||
for cmd in cmdset:
|
for cmd in cmdset:
|
||||||
try:
|
try:
|
||||||
matches.extend([create_match(cmdname, raw_string, cmd)
|
matches.extend([create_match(cmdname, raw_string, cmd)
|
||||||
for cmdname in [cmd.key] + cmd.aliases
|
for cmdname in [cmd.key] + cmd.aliases
|
||||||
|
|
@ -58,13 +58,13 @@ def cmdparser(raw_string, cmdset, caller, match_index=None):
|
||||||
log_trace()
|
log_trace()
|
||||||
|
|
||||||
if not matches:
|
if not matches:
|
||||||
# no matches found.
|
# no matches found.
|
||||||
if '-' in raw_string:
|
if '-' in raw_string:
|
||||||
# This could be due to the user trying to identify the
|
# This could be due to the user trying to identify the
|
||||||
# command with a #num-<command> style syntax.
|
# command with a #num-<command> style syntax.
|
||||||
mindex, new_raw_string = raw_string.split("-", 1)
|
mindex, new_raw_string = raw_string.split("-", 1)
|
||||||
if mindex.isdigit():
|
if mindex.isdigit():
|
||||||
mindex = int(mindex) - 1
|
mindex = int(mindex) - 1
|
||||||
# feed result back to parser iteratively
|
# feed result back to parser iteratively
|
||||||
return cmdparser(new_raw_string, cmdset, caller, match_index=mindex)
|
return cmdparser(new_raw_string, cmdset, caller, match_index=mindex)
|
||||||
|
|
||||||
|
|
@ -85,22 +85,22 @@ def cmdparser(raw_string, cmdset, caller, match_index=None):
|
||||||
if len(matches) > 1:
|
if len(matches) > 1:
|
||||||
# still multiple matches. Fall back to ratio-based quality.
|
# still multiple matches. Fall back to ratio-based quality.
|
||||||
matches = sorted(matches, key=lambda m: m[4])
|
matches = sorted(matches, key=lambda m: m[4])
|
||||||
# only pick the highest rated ratio match
|
# only pick the highest rated ratio match
|
||||||
quality = [mat[4] for mat in matches]
|
quality = [mat[4] for mat in matches]
|
||||||
matches = matches[-quality.count(quality[-1]):]
|
matches = matches[-quality.count(quality[-1]):]
|
||||||
|
|
||||||
if len(matches) > 1 and match_index != None and 0 <= match_index < len(matches):
|
if len(matches) > 1 and match_index != None and 0 <= match_index < len(matches):
|
||||||
# We couldn't separate match by quality, but we have an index argument to
|
# We couldn't separate match by quality, but we have an index argument to
|
||||||
# tell us which match to use.
|
# tell us which match to use.
|
||||||
matches = [matches[match_index]]
|
matches = [matches[match_index]]
|
||||||
|
|
||||||
# no matter what we have at this point, we have to return it.
|
# no matter what we have at this point, we have to return it.
|
||||||
return matches
|
return matches
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
# Search parsers and support methods
|
# Search parsers and support methods
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
#
|
#
|
||||||
# Default functions for formatting and processing searches.
|
# Default functions for formatting and processing searches.
|
||||||
|
|
@ -109,7 +109,7 @@ def cmdparser(raw_string, cmdset, caller, match_index=None):
|
||||||
# replace from the settings file by setting the variables
|
# replace from the settings file by setting the variables
|
||||||
#
|
#
|
||||||
# SEARCH_AT_RESULTERROR_HANDLER
|
# SEARCH_AT_RESULTERROR_HANDLER
|
||||||
# SEARCH_MULTIMATCH_PARSER
|
# SEARCH_MULTIMATCH_PARSER
|
||||||
#
|
#
|
||||||
# The the replacing modules must have the same inputs and outputs as
|
# The the replacing modules must have the same inputs and outputs as
|
||||||
# those in this module.
|
# those in this module.
|
||||||
|
|
@ -118,57 +118,57 @@ def cmdparser(raw_string, cmdset, caller, match_index=None):
|
||||||
def at_search_result(msg_obj, ostring, results, global_search=False):
|
def at_search_result(msg_obj, ostring, results, global_search=False):
|
||||||
"""
|
"""
|
||||||
Called by search methods after a result of any type has been found.
|
Called by search methods after a result of any type has been found.
|
||||||
|
|
||||||
Takes a search result (a list) and
|
Takes a search result (a list) and
|
||||||
formats eventual errors.
|
formats eventual errors.
|
||||||
|
|
||||||
msg_obj - object to receive feedback.
|
msg_obj - object to receive feedback.
|
||||||
ostring - original search string
|
ostring - original search string
|
||||||
results - list of found matches (0, 1 or more)
|
results - list of found matches (0, 1 or more)
|
||||||
global_search - if this was a global_search or not
|
global_search - if this was a global_search or not
|
||||||
(if it is, there might be an idea of supplying
|
(if it is, there might be an idea of supplying
|
||||||
dbrefs instead of only numbers)
|
dbrefs instead of only numbers)
|
||||||
|
|
||||||
Multiple matches are returned to the searching object
|
Multiple matches are returned to the searching object
|
||||||
as
|
as
|
||||||
1-object
|
1-object
|
||||||
2-object
|
2-object
|
||||||
3-object
|
3-object
|
||||||
etc
|
etc
|
||||||
|
|
||||||
"""
|
"""
|
||||||
string = ""
|
string = ""
|
||||||
if not results:
|
if not results:
|
||||||
# no results.
|
# no results.
|
||||||
string = "Could not find '%s'." % ostring
|
string = "Could not find '%s'." % ostring
|
||||||
results = None
|
results = None
|
||||||
|
|
||||||
elif len(results) > 1:
|
elif len(results) > 1:
|
||||||
# we have more than one match. We will display a
|
# we have more than one match. We will display a
|
||||||
# list of the form 1-objname, 2-objname etc.
|
# list of the form 1-objname, 2-objname etc.
|
||||||
|
|
||||||
# check if the msg_object may se dbrefs
|
# check if the msg_object may se dbrefs
|
||||||
show_dbref = global_search
|
show_dbref = global_search
|
||||||
|
|
||||||
string += "More than one match for '%s'" % ostring
|
string += "More than one match for '%s'" % ostring
|
||||||
string += " (please narrow target):"
|
string += " (please narrow target):"
|
||||||
for num, result in enumerate(results):
|
for num, result in enumerate(results):
|
||||||
invtext = ""
|
invtext = ""
|
||||||
dbreftext = ""
|
dbreftext = ""
|
||||||
if hasattr(result, "location") and result.location == msg_obj:
|
if hasattr(result, "location") and result.location == msg_obj:
|
||||||
invtext = " (carried)"
|
invtext = " (carried)"
|
||||||
if show_dbref:
|
if show_dbref:
|
||||||
dbreftext = "(#%i)" % result.id
|
dbreftext = "(#%i)" % result.id
|
||||||
string += "\n %i-%s%s%s" % (num+1, result.name,
|
string += "\n %i-%s%s%s" % (num+1, result.name,
|
||||||
dbreftext, invtext)
|
dbreftext, invtext)
|
||||||
results = None
|
results = None
|
||||||
else:
|
else:
|
||||||
# we have exactly one match.
|
# we have exactly one match.
|
||||||
results = results[0]
|
results = results[0]
|
||||||
|
|
||||||
if string:
|
if string:
|
||||||
msg_obj.msg(string.strip())
|
msg_obj.msg(string.strip())
|
||||||
return results
|
return results
|
||||||
|
|
||||||
def at_multimatch_input(ostring):
|
def at_multimatch_input(ostring):
|
||||||
"""
|
"""
|
||||||
|
|
@ -186,9 +186,9 @@ def at_multimatch_input(ostring):
|
||||||
the lowest number, rather than 0 as in Python).
|
the lowest number, rather than 0 as in Python).
|
||||||
|
|
||||||
This parser version will identify search strings on the following
|
This parser version will identify search strings on the following
|
||||||
forms
|
forms
|
||||||
|
|
||||||
2-object
|
2-object
|
||||||
|
|
||||||
This will be parsed to (2, "object") and, if applicable, will tell
|
This will be parsed to (2, "object") and, if applicable, will tell
|
||||||
the engine to pick the second from a list of same-named matches of
|
the engine to pick the second from a list of same-named matches of
|
||||||
|
|
@ -197,7 +197,7 @@ def at_multimatch_input(ostring):
|
||||||
Ex for use in a game session:
|
Ex for use in a game session:
|
||||||
|
|
||||||
> look
|
> look
|
||||||
You see: ball, ball, ball and ball.
|
You see: ball, ball, ball and ball.
|
||||||
> get ball
|
> get ball
|
||||||
There where multiple matches for ball:
|
There where multiple matches for ball:
|
||||||
1-ball
|
1-ball
|
||||||
|
|
@ -205,7 +205,7 @@ def at_multimatch_input(ostring):
|
||||||
3-ball
|
3-ball
|
||||||
4-ball
|
4-ball
|
||||||
> get 3-ball
|
> get 3-ball
|
||||||
You get the ball.
|
You get the ball.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
@ -213,7 +213,7 @@ def at_multimatch_input(ostring):
|
||||||
return (None, ostring)
|
return (None, ostring)
|
||||||
if not '-' in ostring:
|
if not '-' in ostring:
|
||||||
return (None, ostring)
|
return (None, ostring)
|
||||||
try:
|
try:
|
||||||
index = ostring.find('-')
|
index = ostring.find('-')
|
||||||
number = int(ostring[:index])-1
|
number = int(ostring[:index])-1
|
||||||
return (number, ostring[index+1:])
|
return (number, ostring[index+1:])
|
||||||
|
|
@ -229,7 +229,7 @@ def at_multimatch_cmd(caller, matches):
|
||||||
Format multiple command matches to a useful error.
|
Format multiple command matches to a useful error.
|
||||||
"""
|
"""
|
||||||
string = "There where multiple matches:"
|
string = "There where multiple matches:"
|
||||||
for num, match in enumerate(matches):
|
for num, match in enumerate(matches):
|
||||||
# each match is a tuple (candidate, cmd)
|
# each match is a tuple (candidate, cmd)
|
||||||
cmdname, arg, cmd, dum, dum = match
|
cmdname, arg, cmd, dum, dum = match
|
||||||
|
|
||||||
|
|
@ -237,7 +237,7 @@ def at_multimatch_cmd(caller, matches):
|
||||||
if is_channel:
|
if is_channel:
|
||||||
is_channel = " (channel)"
|
is_channel = " (channel)"
|
||||||
else:
|
else:
|
||||||
is_channel = ""
|
is_channel = ""
|
||||||
if cmd.is_exit and cmd.destination:
|
if cmd.is_exit and cmd.destination:
|
||||||
is_exit = " (exit to %s)" % cmd.destination
|
is_exit = " (exit to %s)" % cmd.destination
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -10,14 +10,14 @@ on-the-fly CmdSet that is some combination of the
|
||||||
previous ones. Their function are borrowed to a large parts from mathematical
|
previous ones. Their function are borrowed to a large parts from mathematical
|
||||||
Set theory, it should not be much of a problem to understand.
|
Set theory, it should not be much of a problem to understand.
|
||||||
|
|
||||||
See CmdHandler for practical examples on how to apply cmdsets
|
See CmdHandler for practical examples on how to apply cmdsets
|
||||||
together to create interesting in-game effects.
|
together to create interesting in-game effects.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import copy
|
import copy
|
||||||
from src.utils.utils import inherits_from, is_iter
|
from src.utils.utils import inherits_from, is_iter
|
||||||
|
|
||||||
RECURSIVE_PROTECTION = False
|
RECURSIVE_PROTECTION = False
|
||||||
|
|
||||||
class CmdSetMeta(type):
|
class CmdSetMeta(type):
|
||||||
"""
|
"""
|
||||||
|
|
@ -29,14 +29,14 @@ class CmdSetMeta(type):
|
||||||
Fixes some things in the cmdclass
|
Fixes some things in the cmdclass
|
||||||
"""
|
"""
|
||||||
# by default we key the cmdset the same as the
|
# by default we key the cmdset the same as the
|
||||||
# name of its class.
|
# name of its class.
|
||||||
if not hasattr(mcs, 'key') or not mcs.key:
|
if not hasattr(mcs, 'key') or not mcs.key:
|
||||||
mcs.key = mcs.__name__
|
mcs.key = mcs.__name__
|
||||||
mcs.path = "%s.%s" % (mcs.__module__, mcs.__name__)
|
mcs.path = "%s.%s" % (mcs.__module__, mcs.__name__)
|
||||||
|
|
||||||
if not type(mcs.key_mergetypes) == dict:
|
if not type(mcs.key_mergetypes) == dict:
|
||||||
mcs.key_mergetypes = {}
|
mcs.key_mergetypes = {}
|
||||||
|
|
||||||
super(CmdSetMeta, mcs).__init__(*args, **kwargs)
|
super(CmdSetMeta, mcs).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -46,12 +46,12 @@ def union(cmdset_a, cmdset_b, duplicates=False):
|
||||||
"C = A U B. CmdSet A is assumed to have higher priority"
|
"C = A U B. CmdSet A is assumed to have higher priority"
|
||||||
cmdset_c = cmdset_a.copy_this()
|
cmdset_c = cmdset_a.copy_this()
|
||||||
# we make copies, not refs by use of [:]
|
# we make copies, not refs by use of [:]
|
||||||
cmdset_c.commands = cmdset_a.commands[:]
|
cmdset_c.commands = cmdset_a.commands[:]
|
||||||
if duplicates and cmdset_a.priority == cmdset_b.priority:
|
if duplicates and cmdset_a.priority == cmdset_b.priority:
|
||||||
cmdset_c.commands.extend(cmdset_b.commands)
|
cmdset_c.commands.extend(cmdset_b.commands)
|
||||||
else:
|
else:
|
||||||
cmdset_c.commands.extend([cmd for cmd in cmdset_b if not cmd in cmdset_a])
|
cmdset_c.commands.extend([cmd for cmd in cmdset_b if not cmd in cmdset_a])
|
||||||
return cmdset_c
|
return cmdset_c
|
||||||
|
|
||||||
def intersect(cmdset_a, cmdset_b, duplicates=False):
|
def intersect(cmdset_a, cmdset_b, duplicates=False):
|
||||||
"C = A (intersect) B. A is assumed higher priority"
|
"C = A (intersect) B. A is assumed higher priority"
|
||||||
|
|
@ -62,7 +62,7 @@ def intersect(cmdset_a, cmdset_b, duplicates=False):
|
||||||
cmdset_c.add(cmdset_b.get(cmd))
|
cmdset_c.add(cmdset_b.get(cmd))
|
||||||
else:
|
else:
|
||||||
cmdset_c.commands = [cmd for cmd in cmdset_a if cmd in cmdset_b]
|
cmdset_c.commands = [cmd for cmd in cmdset_a if cmd in cmdset_b]
|
||||||
return cmdset_c
|
return cmdset_c
|
||||||
|
|
||||||
def replace(cmdset_a, cmdset_b, cmdset_c):
|
def replace(cmdset_a, cmdset_b, cmdset_c):
|
||||||
"C = A + B where the result is A."
|
"C = A + B where the result is A."
|
||||||
|
|
@ -80,24 +80,24 @@ def instantiate(cmd):
|
||||||
"""
|
"""
|
||||||
checks so that object is an instantiated command
|
checks so that object is an instantiated command
|
||||||
and not, say a cmdclass. If it is, instantiate it.
|
and not, say a cmdclass. If it is, instantiate it.
|
||||||
Other types, like strings, are passed through.
|
Other types, like strings, are passed through.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
return cmd()
|
return cmd()
|
||||||
except TypeError:
|
except TypeError:
|
||||||
return cmd
|
return cmd
|
||||||
|
|
||||||
class CmdSet(object):
|
class CmdSet(object):
|
||||||
"""
|
"""
|
||||||
This class describes a unique cmdset that understands priorities. CmdSets
|
This class describes a unique cmdset that understands priorities. CmdSets
|
||||||
can be merged and made to perform various set operations on each other.
|
can be merged and made to perform various set operations on each other.
|
||||||
CmdSets have priorities that affect which of their ingoing commands gets used.
|
CmdSets have priorities that affect which of their ingoing commands gets used.
|
||||||
|
|
||||||
In the examples, cmdset A always have higher priority than cmdset B.
|
|
||||||
|
|
||||||
key - the name of the cmdset. This can be used on its own for game operations
|
In the examples, cmdset A always have higher priority than cmdset B.
|
||||||
|
|
||||||
mergetype (partly from Set theory):
|
key - the name of the cmdset. This can be used on its own for game operations
|
||||||
|
|
||||||
|
mergetype (partly from Set theory):
|
||||||
|
|
||||||
Union - The two command sets are merged so that as many
|
Union - The two command sets are merged so that as many
|
||||||
commands as possible of each cmdset ends up in the
|
commands as possible of each cmdset ends up in the
|
||||||
|
|
@ -125,13 +125,13 @@ class CmdSet(object):
|
||||||
excempt from all merge operations - they are
|
excempt from all merge operations - they are
|
||||||
ALWAYS included across mergers and only affected
|
ALWAYS included across mergers and only affected
|
||||||
if same-named system commands replace them.
|
if same-named system commands replace them.
|
||||||
|
|
||||||
priority- All cmdsets are always merged in pairs of two so that
|
priority- All cmdsets are always merged in pairs of two so that
|
||||||
the higher set's mergetype is applied to the
|
the higher set's mergetype is applied to the
|
||||||
lower-priority cmdset. Default commands have priority 0,
|
lower-priority cmdset. Default commands have priority 0,
|
||||||
high-priority ones like Exits and Channels have 10 and 9. Priorities
|
high-priority ones like Exits and Channels have 10 and 9. Priorities
|
||||||
can be negative as well to give default commands preference.
|
can be negative as well to give default commands preference.
|
||||||
|
|
||||||
duplicates - determines what happens when two sets of equal
|
duplicates - determines what happens when two sets of equal
|
||||||
priority merge. Default has the first of them in the
|
priority merge. Default has the first of them in the
|
||||||
merger (i.e. A above) automatically taking
|
merger (i.e. A above) automatically taking
|
||||||
|
|
@ -146,7 +146,7 @@ class CmdSet(object):
|
||||||
select which ball to kick ... Allowing duplicates
|
select which ball to kick ... Allowing duplicates
|
||||||
only makes sense for Union and Intersect, the setting
|
only makes sense for Union and Intersect, the setting
|
||||||
is ignored for the other mergetypes.
|
is ignored for the other mergetypes.
|
||||||
|
|
||||||
key_mergetype (dict) - allows the cmdset to define a unique
|
key_mergetype (dict) - allows the cmdset to define a unique
|
||||||
mergetype for particular cmdsets. Format is
|
mergetype for particular cmdsets. Format is
|
||||||
{CmdSetkeystring:mergetype}. Priorities still apply.
|
{CmdSetkeystring:mergetype}. Priorities still apply.
|
||||||
|
|
@ -155,14 +155,14 @@ class CmdSet(object):
|
||||||
Myevilcmdset no matter what overall mergetype this set
|
Myevilcmdset no matter what overall mergetype this set
|
||||||
has.
|
has.
|
||||||
|
|
||||||
no_objs - don't include any commands from nearby objects
|
no_objs - don't include any commands from nearby objects
|
||||||
when searching for suitable commands
|
when searching for suitable commands
|
||||||
no_exits - ignore the names of exits when matching against
|
no_exits - ignore the names of exits when matching against
|
||||||
commands
|
commands
|
||||||
no_channels - ignore the name of channels when matching against
|
no_channels - ignore the name of channels when matching against
|
||||||
commands (WARNING- this is dangerous since the
|
commands (WARNING- this is dangerous since the
|
||||||
player can then not even ask staff for help if
|
player can then not even ask staff for help if
|
||||||
something goes wrong)
|
something goes wrong)
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
@ -176,14 +176,14 @@ class CmdSet(object):
|
||||||
no_exits = False
|
no_exits = False
|
||||||
no_objs = False
|
no_objs = False
|
||||||
no_channels = False
|
no_channels = False
|
||||||
permanent = False
|
permanent = False
|
||||||
|
|
||||||
def __init__(self, cmdsetobj=None, key=None):
|
def __init__(self, cmdsetobj=None, key=None):
|
||||||
"""
|
"""
|
||||||
Creates a new CmdSet instance.
|
Creates a new CmdSet instance.
|
||||||
|
|
||||||
cmdsetobj - this is the database object to which this particular
|
cmdsetobj - this is the database object to which this particular
|
||||||
instance of cmdset is related. It is often a player but may also be a
|
instance of cmdset is related. It is often a player but may also be a
|
||||||
regular object.
|
regular object.
|
||||||
"""
|
"""
|
||||||
if key:
|
if key:
|
||||||
|
|
@ -194,12 +194,12 @@ class CmdSet(object):
|
||||||
# initialize system
|
# initialize system
|
||||||
self.at_cmdset_creation()
|
self.at_cmdset_creation()
|
||||||
|
|
||||||
def at_cmdset_creation(self):
|
def at_cmdset_creation(self):
|
||||||
"""
|
"""
|
||||||
Hook method - this should be overloaded in the inheriting
|
Hook method - this should be overloaded in the inheriting
|
||||||
class, and should take care of populating the cmdset
|
class, and should take care of populating the cmdset
|
||||||
by use of self.add().
|
by use of self.add().
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def add(self, cmd):
|
def add(self, cmd):
|
||||||
|
|
@ -207,20 +207,20 @@ class CmdSet(object):
|
||||||
Add a command, a list of commands or a cmdset to this cmdset.
|
Add a command, a list of commands or a cmdset to this cmdset.
|
||||||
|
|
||||||
Note that if cmd already exists in set,
|
Note that if cmd already exists in set,
|
||||||
it will replace the old one (no priority checking etc
|
it will replace the old one (no priority checking etc
|
||||||
at this point; this is often used to overload
|
at this point; this is often used to overload
|
||||||
default commands).
|
default commands).
|
||||||
|
|
||||||
If cmd is another cmdset class or -instance, the commands
|
If cmd is another cmdset class or -instance, the commands
|
||||||
of that command set is added to this one, as if they were part
|
of that command set is added to this one, as if they were part
|
||||||
of the original cmdset definition. No merging or priority checks
|
of the original cmdset definition. No merging or priority checks
|
||||||
are made, rather later added commands will simply replace
|
are made, rather later added commands will simply replace
|
||||||
existing ones to make a unique set.
|
existing ones to make a unique set.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if inherits_from(cmd, "src.commands.cmdset.CmdSet"):
|
if inherits_from(cmd, "src.commands.cmdset.CmdSet"):
|
||||||
# cmd is a command set so merge all commands in that set
|
# cmd is a command set so merge all commands in that set
|
||||||
# to this one. We raise a visible error if we created
|
# to this one. We raise a visible error if we created
|
||||||
# an infinite loop (adding cmdset to itself somehow)
|
# an infinite loop (adding cmdset to itself somehow)
|
||||||
try:
|
try:
|
||||||
cmd = instantiate(cmd)
|
cmd = instantiate(cmd)
|
||||||
|
|
@ -234,46 +234,46 @@ class CmdSet(object):
|
||||||
else:
|
else:
|
||||||
cmds = [instantiate(cmd)]
|
cmds = [instantiate(cmd)]
|
||||||
for cmd in cmds:
|
for cmd in cmds:
|
||||||
# add all commands
|
# add all commands
|
||||||
if not hasattr(cmd, 'obj'):
|
if not hasattr(cmd, 'obj'):
|
||||||
cmd.obj = self.cmdsetobj
|
cmd.obj = self.cmdsetobj
|
||||||
try:
|
try:
|
||||||
ic = self.commands.index(cmd)
|
ic = self.commands.index(cmd)
|
||||||
self.commands[ic] = cmd # replace
|
self.commands[ic] = cmd # replace
|
||||||
except ValueError:
|
except ValueError:
|
||||||
self.commands.append(cmd)
|
self.commands.append(cmd)
|
||||||
# extra run to make sure to avoid doublets
|
# extra run to make sure to avoid doublets
|
||||||
self.commands = list(set(self.commands))
|
self.commands = list(set(self.commands))
|
||||||
#print "In cmdset.add(cmd):", self.key, cmd
|
#print "In cmdset.add(cmd):", self.key, cmd
|
||||||
|
|
||||||
def remove(self, cmd):
|
def remove(self, cmd):
|
||||||
"""
|
"""
|
||||||
Remove a command instance from the cmdset.
|
Remove a command instance from the cmdset.
|
||||||
cmd can be either a cmd instance or a key string.
|
cmd can be either a cmd instance or a key string.
|
||||||
"""
|
"""
|
||||||
cmd = instantiate(cmd)
|
cmd = instantiate(cmd)
|
||||||
self.commands = [oldcmd for oldcmd in self.commands if oldcmd != cmd]
|
self.commands = [oldcmd for oldcmd in self.commands if oldcmd != cmd]
|
||||||
|
|
||||||
def get(self, cmd):
|
def get(self, cmd):
|
||||||
"""
|
"""
|
||||||
Return the command in this cmdset that matches the
|
Return the command in this cmdset that matches the
|
||||||
given command. cmd may be either a command instance or
|
given command. cmd may be either a command instance or
|
||||||
a key string.
|
a key string.
|
||||||
"""
|
"""
|
||||||
cmd = instantiate(cmd)
|
cmd = instantiate(cmd)
|
||||||
for thiscmd in self.commands:
|
for thiscmd in self.commands:
|
||||||
if thiscmd == cmd:
|
if thiscmd == cmd:
|
||||||
return thiscmd
|
return thiscmd
|
||||||
|
|
||||||
def count(self):
|
def count(self):
|
||||||
"Return number of commands in set"
|
"Return number of commands in set"
|
||||||
return len(self.commands)
|
return len(self.commands)
|
||||||
|
|
||||||
def get_system_cmds(self):
|
def get_system_cmds(self):
|
||||||
"""
|
"""
|
||||||
Return system commands in the cmdset, defined as
|
Return system commands in the cmdset, defined as
|
||||||
commands starting with double underscore __.
|
commands starting with double underscore __.
|
||||||
These are excempt from merge operations.
|
These are excempt from merge operations.
|
||||||
"""
|
"""
|
||||||
return [cmd for cmd in self.commands if cmd.key.startswith('__')]
|
return [cmd for cmd in self.commands if cmd.key.startswith('__')]
|
||||||
|
|
||||||
|
|
@ -293,11 +293,11 @@ class CmdSet(object):
|
||||||
cmdset.duplicates = self.duplicates
|
cmdset.duplicates = self.duplicates
|
||||||
cmdset.key_mergetypes = self.key_mergetypes.copy() #copy.deepcopy(self.key_mergetypes)
|
cmdset.key_mergetypes = self.key_mergetypes.copy() #copy.deepcopy(self.key_mergetypes)
|
||||||
return cmdset
|
return cmdset
|
||||||
|
|
||||||
def make_unique(self, caller):
|
def make_unique(self, caller):
|
||||||
"""
|
"""
|
||||||
This is an unsafe command meant to clean out a cmdset of
|
This is an unsafe command meant to clean out a cmdset of
|
||||||
doublet commands after it has been created. It is useful
|
doublet commands after it has been created. It is useful
|
||||||
for commands inheriting cmdsets from the cmdhandler where
|
for commands inheriting cmdsets from the cmdhandler where
|
||||||
obj-based cmdsets always are added double. Doublets will
|
obj-based cmdsets always are added double. Doublets will
|
||||||
be weeded out with preference to commands defined on caller,
|
be weeded out with preference to commands defined on caller,
|
||||||
|
|
@ -313,14 +313,14 @@ class CmdSet(object):
|
||||||
else:
|
else:
|
||||||
unique[cmd.key] = cmd
|
unique[cmd.key] = cmd
|
||||||
self.commands = unique.values()
|
self.commands = unique.values()
|
||||||
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
"""
|
"""
|
||||||
Show all commands in cmdset when printing it.
|
Show all commands in cmdset when printing it.
|
||||||
"""
|
"""
|
||||||
return ", ".join([str(cmd) for cmd in sorted(self.commands, key=lambda o:o.key)])
|
return ", ".join([str(cmd) for cmd in sorted(self.commands, key=lambda o:o.key)])
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
"""
|
"""
|
||||||
Allows for things like 'for cmd in cmdset':
|
Allows for things like 'for cmd in cmdset':
|
||||||
|
|
@ -331,14 +331,14 @@ class CmdSet(object):
|
||||||
"""
|
"""
|
||||||
Returns True if this cmdset contains the given command (as defined
|
Returns True if this cmdset contains the given command (as defined
|
||||||
by command name and aliases). This allows for things like 'if cmd in cmdset'
|
by command name and aliases). This allows for things like 'if cmd in cmdset'
|
||||||
"""
|
"""
|
||||||
return any(cmd == othercmd for cmd in self.commands)
|
return any(cmd == othercmd for cmd in self.commands)
|
||||||
|
|
||||||
def __add__(self, cmdset_b):
|
def __add__(self, cmdset_b):
|
||||||
"""
|
"""
|
||||||
Merge this cmdset (A) with another cmdset (B) using the + operator,
|
Merge this cmdset (A) with another cmdset (B) using the + operator,
|
||||||
|
|
||||||
C = A + B
|
C = A + B
|
||||||
|
|
||||||
Here, we (by convention) say that 'A is merged onto B to form
|
Here, we (by convention) say that 'A is merged onto B to form
|
||||||
C'. The actual merge operation used in the 'addition' depends
|
C'. The actual merge operation used in the 'addition' depends
|
||||||
|
|
@ -356,9 +356,9 @@ class CmdSet(object):
|
||||||
# preserve system __commands
|
# preserve system __commands
|
||||||
sys_commands = self.get_system_cmds() + cmdset_b.get_system_cmds()
|
sys_commands = self.get_system_cmds() + cmdset_b.get_system_cmds()
|
||||||
|
|
||||||
if self.priority >= cmdset_b.priority:
|
if self.priority >= cmdset_b.priority:
|
||||||
# A higher or equal priority than B
|
# A higher or equal priority than B
|
||||||
mergetype = self.key_mergetypes.get(cmdset_b.key, self.mergetype)
|
mergetype = self.key_mergetypes.get(cmdset_b.key, self.mergetype)
|
||||||
if mergetype == "Intersect":
|
if mergetype == "Intersect":
|
||||||
cmdset_c = intersect(self, cmdset_b, cmdset_b.duplicates)
|
cmdset_c = intersect(self, cmdset_b, cmdset_b.duplicates)
|
||||||
elif mergetype == "Replace":
|
elif mergetype == "Replace":
|
||||||
|
|
@ -369,7 +369,7 @@ class CmdSet(object):
|
||||||
cmdset_c = union(self, cmdset_b, cmdset_b.duplicates)
|
cmdset_c = union(self, cmdset_b, cmdset_b.duplicates)
|
||||||
else:
|
else:
|
||||||
# B higher priority than A
|
# B higher priority than A
|
||||||
mergetype = cmdset_b.key_mergetypes.get(self.key, cmdset_b.mergetype)
|
mergetype = cmdset_b.key_mergetypes.get(self.key, cmdset_b.mergetype)
|
||||||
if mergetype == "Intersect":
|
if mergetype == "Intersect":
|
||||||
cmdset_c = intersect(cmdset_b, self, self.duplicates)
|
cmdset_c = intersect(cmdset_b, self, self.duplicates)
|
||||||
elif mergetype == "Replace":
|
elif mergetype == "Replace":
|
||||||
|
|
@ -382,9 +382,9 @@ class CmdSet(object):
|
||||||
# we store actual_mergetype since key_mergetypes
|
# we store actual_mergetype since key_mergetypes
|
||||||
# might be different from the main mergetype.
|
# might be different from the main mergetype.
|
||||||
# This is used for diagnosis.
|
# This is used for diagnosis.
|
||||||
cmdset_c.actual_mergetype = mergetype
|
cmdset_c.actual_mergetype = mergetype
|
||||||
|
|
||||||
# return the system commands to the cmdset
|
# return the system commands to the cmdset
|
||||||
cmdset_c.add(sys_commands)
|
cmdset_c.add(sys_commands)
|
||||||
|
|
||||||
return cmdset_c
|
return cmdset_c
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,7 @@ can then implement separate sets for different situations. For
|
||||||
example, you can have a 'On a boat' set, onto which you then tack on
|
example, you can have a 'On a boat' set, onto which you then tack on
|
||||||
the 'Fishing' set. Fishing from a boat? No problem!
|
the 'Fishing' set. Fishing from a boat? No problem!
|
||||||
"""
|
"""
|
||||||
import traceback
|
import traceback
|
||||||
from src.utils import logger, utils
|
from src.utils import logger, utils
|
||||||
from src.commands.cmdset import CmdSet
|
from src.commands.cmdset import CmdSet
|
||||||
from src.server.models import ServerConfig
|
from src.server.models import ServerConfig
|
||||||
|
|
@ -74,21 +74,21 @@ def import_cmdset(python_path, cmdsetobj, emit_to_obj=None, no_logging=False):
|
||||||
"""
|
"""
|
||||||
This helper function is used by the cmdsethandler to load a cmdset
|
This helper function is used by the cmdsethandler to load a cmdset
|
||||||
instance from a python module, given a python_path. It's usually accessed
|
instance from a python module, given a python_path. It's usually accessed
|
||||||
through the cmdsethandler's add() and add_default() methods.
|
through the cmdsethandler's add() and add_default() methods.
|
||||||
python_path - This is the full path to the cmdset object.
|
python_path - This is the full path to the cmdset object.
|
||||||
cmdsetobj - the database object/typeclass on which this cmdset is to be assigned
|
cmdsetobj - the database object/typeclass on which this cmdset is to be assigned
|
||||||
(this can be also channels and exits, as well as players but there will
|
(this can be also channels and exits, as well as players but there will
|
||||||
always be such an object)
|
always be such an object)
|
||||||
emit_to_obj - if given, error is emitted to this object (in addition to logging)
|
emit_to_obj - if given, error is emitted to this object (in addition to logging)
|
||||||
no_logging - don't log/send error messages. This can be useful if import_cmdset is just
|
no_logging - don't log/send error messages. This can be useful if import_cmdset is just
|
||||||
used to check if this is a valid python path or not.
|
used to check if this is a valid python path or not.
|
||||||
function returns None if an error was encountered or path not found.
|
function returns None if an error was encountered or path not found.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
try:
|
try:
|
||||||
#print "importing %s: CACHED_CMDSETS=%s" % (python_path, CACHED_CMDSETS)
|
#print "importing %s: CACHED_CMDSETS=%s" % (python_path, CACHED_CMDSETS)
|
||||||
wanted_cache_key = python_path
|
wanted_cache_key = python_path
|
||||||
cmdsetclass = CACHED_CMDSETS.get(wanted_cache_key, None)
|
cmdsetclass = CACHED_CMDSETS.get(wanted_cache_key, None)
|
||||||
errstring = ""
|
errstring = ""
|
||||||
if not cmdsetclass:
|
if not cmdsetclass:
|
||||||
|
|
@ -96,11 +96,11 @@ def import_cmdset(python_path, cmdsetobj, emit_to_obj=None, no_logging=False):
|
||||||
# Not in cache. Reload from disk.
|
# Not in cache. Reload from disk.
|
||||||
modulepath, classname = python_path.rsplit('.', 1)
|
modulepath, classname = python_path.rsplit('.', 1)
|
||||||
module = __import__(modulepath, fromlist=[True])
|
module = __import__(modulepath, fromlist=[True])
|
||||||
cmdsetclass = module.__dict__[classname]
|
cmdsetclass = module.__dict__[classname]
|
||||||
CACHED_CMDSETS[wanted_cache_key] = cmdsetclass
|
CACHED_CMDSETS[wanted_cache_key] = cmdsetclass
|
||||||
#instantiate the cmdset (and catch its errors)
|
#instantiate the cmdset (and catch its errors)
|
||||||
if callable(cmdsetclass):
|
if callable(cmdsetclass):
|
||||||
cmdsetclass = cmdsetclass(cmdsetobj)
|
cmdsetclass = cmdsetclass(cmdsetobj)
|
||||||
return cmdsetclass
|
return cmdsetclass
|
||||||
|
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
|
@ -111,20 +111,20 @@ def import_cmdset(python_path, cmdsetobj, emit_to_obj=None, no_logging=False):
|
||||||
errstring = "Error in loading cmdset: No cmdset class '%s' in %s."
|
errstring = "Error in loading cmdset: No cmdset class '%s' in %s."
|
||||||
errstring = errstring % (classname, modulepath)
|
errstring = errstring % (classname, modulepath)
|
||||||
raise
|
raise
|
||||||
except Exception:
|
except Exception:
|
||||||
errstring = "\n%s\nCompile/Run error when loading cmdset '%s'."
|
errstring = "\n%s\nCompile/Run error when loading cmdset '%s'."
|
||||||
errstring = errstring % (traceback.format_exc(), python_path)
|
errstring = errstring % (traceback.format_exc(), python_path)
|
||||||
raise
|
raise
|
||||||
except Exception:
|
except Exception:
|
||||||
if errstring and not no_logging:
|
if errstring and not no_logging:
|
||||||
print errstring
|
print errstring
|
||||||
logger.log_trace()
|
logger.log_trace()
|
||||||
if emit_to_obj and not ServerConfig.objects.conf("server_starting_mode"):
|
if emit_to_obj and not ServerConfig.objects.conf("server_starting_mode"):
|
||||||
object.__getattribute__(emit_to_obj, "msg")(errstring)
|
object.__getattribute__(emit_to_obj, "msg")(errstring)
|
||||||
logger.log_errmsg("Error: %s" % errstring)
|
logger.log_errmsg("Error: %s" % errstring)
|
||||||
#cannot raise - it kills the server if no base cmdset exists!
|
#cannot raise - it kills the server if no base cmdset exists!
|
||||||
|
|
||||||
# classes
|
# classes
|
||||||
|
|
||||||
class CmdSetHandler(object):
|
class CmdSetHandler(object):
|
||||||
"""
|
"""
|
||||||
|
|
@ -134,35 +134,35 @@ class CmdSetHandler(object):
|
||||||
This is the set the game engine will retrieve when determining which
|
This is the set the game engine will retrieve when determining which
|
||||||
commands are available to the object. The cmdset_stack holds a history of all CmdSets
|
commands are available to the object. The cmdset_stack holds a history of all CmdSets
|
||||||
to allow the handler to remove/add cmdsets at will. Doing so will re-calculate
|
to allow the handler to remove/add cmdsets at will. Doing so will re-calculate
|
||||||
the 'current' cmdset.
|
the 'current' cmdset.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, obj):
|
def __init__(self, obj):
|
||||||
"""
|
"""
|
||||||
This method is called whenever an object is recreated.
|
This method is called whenever an object is recreated.
|
||||||
|
|
||||||
obj - this is a reference to the game object this handler
|
obj - this is a reference to the game object this handler
|
||||||
belongs to.
|
belongs to.
|
||||||
"""
|
"""
|
||||||
self.obj = obj
|
self.obj = obj
|
||||||
|
|
||||||
# the id of the "merged" current cmdset for easy access.
|
# the id of the "merged" current cmdset for easy access.
|
||||||
self.key = None
|
self.key = None
|
||||||
# this holds the "merged" current command set
|
# this holds the "merged" current command set
|
||||||
self.current = None
|
self.current = None
|
||||||
# this holds a history of CommandSets
|
# this holds a history of CommandSets
|
||||||
self.cmdset_stack = [CmdSet(cmdsetobj=self.obj, key="Empty")]
|
self.cmdset_stack = [CmdSet(cmdsetobj=self.obj, key="Empty")]
|
||||||
# this tracks which mergetypes are actually in play in the stack
|
# this tracks which mergetypes are actually in play in the stack
|
||||||
self.mergetype_stack = ["Union"]
|
self.mergetype_stack = ["Union"]
|
||||||
|
|
||||||
# the subset of the cmdset_paths that are to be stored in the database
|
# the subset of the cmdset_paths that are to be stored in the database
|
||||||
self.permanent_paths = [""]
|
self.permanent_paths = [""]
|
||||||
|
|
||||||
#self.update(init_mode=True) is then called from the object __init__.
|
#self.update(init_mode=True) is then called from the object __init__.
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
"Display current commands"
|
"Display current commands"
|
||||||
|
|
||||||
string = ""
|
string = ""
|
||||||
mergelist = []
|
mergelist = []
|
||||||
if len(self.cmdset_stack) > 1:
|
if len(self.cmdset_stack) > 1:
|
||||||
|
|
@ -176,18 +176,18 @@ class CmdSetHandler(object):
|
||||||
if cmdset.permanent:
|
if cmdset.permanent:
|
||||||
permstring = "perm"
|
permstring = "perm"
|
||||||
if mergetype != cmdset.mergetype:
|
if mergetype != cmdset.mergetype:
|
||||||
mergetype = "%s^" % (mergetype)
|
mergetype = "%s^" % (mergetype)
|
||||||
string += "\n %i: <%s (%s, prio %i, %s)>: %s" % \
|
string += "\n %i: <%s (%s, prio %i, %s)>: %s" % \
|
||||||
(snum, cmdset.key, mergetype,
|
(snum, cmdset.key, mergetype,
|
||||||
cmdset.priority, permstring, cmdset)
|
cmdset.priority, permstring, cmdset)
|
||||||
mergelist.append(str(snum))
|
mergelist.append(str(snum))
|
||||||
string += "\n"
|
string += "\n"
|
||||||
|
|
||||||
# Display the currently active cmdset, limited by self.obj's permissions
|
# Display the currently active cmdset, limited by self.obj's permissions
|
||||||
mergetype = self.mergetype_stack[-1]
|
mergetype = self.mergetype_stack[-1]
|
||||||
if mergetype != self.current.mergetype:
|
if mergetype != self.current.mergetype:
|
||||||
merged_on = self.cmdset_stack[-2].key
|
merged_on = self.cmdset_stack[-2].key
|
||||||
mergetype = "custom %s on cmdset '%s'" % (mergetype, merged_on)
|
mergetype = "custom %s on cmdset '%s'" % (mergetype, merged_on)
|
||||||
if mergelist:
|
if mergelist:
|
||||||
string += " <Merged %s (%s, prio %i)>: %s" % ("+".join(mergelist), mergetype, self.current.priority, self.current)
|
string += " <Merged %s (%s, prio %i)>: %s" % ("+".join(mergelist), mergetype, self.current.priority, self.current)
|
||||||
else:
|
else:
|
||||||
|
|
@ -196,74 +196,74 @@ class CmdSetHandler(object):
|
||||||
permstring = "perm"
|
permstring = "perm"
|
||||||
string += " <%s (%s, prio %i, %s)>: %s" % (self.current.key, mergetype, self.current.priority, permstring,
|
string += " <%s (%s, prio %i, %s)>: %s" % (self.current.key, mergetype, self.current.priority, permstring,
|
||||||
", ".join(cmd.key for cmd in sorted(self.current, key=lambda o:o.key)))
|
", ".join(cmd.key for cmd in sorted(self.current, key=lambda o:o.key)))
|
||||||
return string.strip()
|
return string.strip()
|
||||||
|
|
||||||
def update(self, init_mode=False):
|
def update(self, init_mode=False):
|
||||||
"""
|
"""
|
||||||
Re-adds all sets in the handler to have an updated
|
Re-adds all sets in the handler to have an updated
|
||||||
current set.
|
current set.
|
||||||
|
|
||||||
init_mode is used right after this handler was
|
init_mode is used right after this handler was
|
||||||
created; it imports all permanent cmdsets from db.
|
created; it imports all permanent cmdsets from db.
|
||||||
"""
|
"""
|
||||||
if init_mode:
|
if init_mode:
|
||||||
# reimport all permanent cmdsets
|
# reimport all permanent cmdsets
|
||||||
storage = self.obj.cmdset_storage
|
storage = self.obj.cmdset_storage
|
||||||
#print "cmdset_storage:", self.obj.cmdset_storage
|
#print "cmdset_storage:", self.obj.cmdset_storage
|
||||||
if storage:
|
if storage:
|
||||||
self.cmdset_stack = []
|
self.cmdset_stack = []
|
||||||
for pos, path in enumerate(storage):
|
for pos, path in enumerate(storage):
|
||||||
if pos == 0 and not path:
|
if pos == 0 and not path:
|
||||||
self.cmdset_stack = [CmdSet(cmdsetobj=self.obj, key="Empty")]
|
self.cmdset_stack = [CmdSet(cmdsetobj=self.obj, key="Empty")]
|
||||||
elif path:
|
elif path:
|
||||||
cmdset = self.import_cmdset(path)
|
cmdset = self.import_cmdset(path)
|
||||||
if cmdset:
|
if cmdset:
|
||||||
cmdset.permanent = True
|
cmdset.permanent = True
|
||||||
self.cmdset_stack.append(cmdset)
|
self.cmdset_stack.append(cmdset)
|
||||||
|
|
||||||
# merge the stack into a new merged cmdset
|
# merge the stack into a new merged cmdset
|
||||||
new_current = None
|
new_current = None
|
||||||
self.mergetype_stack = []
|
self.mergetype_stack = []
|
||||||
for cmdset in self.cmdset_stack:
|
for cmdset in self.cmdset_stack:
|
||||||
try:
|
try:
|
||||||
# for cmdset's '+' operator, order matters.
|
# for cmdset's '+' operator, order matters.
|
||||||
new_current = cmdset + new_current
|
new_current = cmdset + new_current
|
||||||
except TypeError:
|
except TypeError:
|
||||||
continue
|
continue
|
||||||
self.mergetype_stack.append(new_current.actual_mergetype)
|
self.mergetype_stack.append(new_current.actual_mergetype)
|
||||||
self.current = new_current
|
self.current = new_current
|
||||||
|
|
||||||
def import_cmdset(self, cmdset_path, emit_to_obj=None):
|
def import_cmdset(self, cmdset_path, emit_to_obj=None):
|
||||||
"""
|
"""
|
||||||
Method wrapper for import_cmdset.
|
Method wrapper for import_cmdset.
|
||||||
load a cmdset from a module.
|
load a cmdset from a module.
|
||||||
cmdset_path - the python path to an cmdset object.
|
cmdset_path - the python path to an cmdset object.
|
||||||
emit_to_obj - object to send error messages to
|
emit_to_obj - object to send error messages to
|
||||||
"""
|
"""
|
||||||
if not emit_to_obj:
|
if not emit_to_obj:
|
||||||
emit_to_obj = self.obj
|
emit_to_obj = self.obj
|
||||||
return import_cmdset(cmdset_path, self.obj, emit_to_obj)
|
return import_cmdset(cmdset_path, self.obj, emit_to_obj)
|
||||||
|
|
||||||
def add(self, cmdset, emit_to_obj=None, permanent=False):
|
def add(self, cmdset, emit_to_obj=None, permanent=False):
|
||||||
"""
|
"""
|
||||||
Add a cmdset to the handler, on top of the old ones.
|
Add a cmdset to the handler, on top of the old ones.
|
||||||
Default is to not make this permanent, i.e. the set
|
Default is to not make this permanent, i.e. the set
|
||||||
will not survive a server reset.
|
will not survive a server reset.
|
||||||
|
|
||||||
cmdset - can be a cmdset object or the python path to
|
cmdset - can be a cmdset object or the python path to
|
||||||
such an object.
|
such an object.
|
||||||
emit_to_obj - an object to receive error messages.
|
emit_to_obj - an object to receive error messages.
|
||||||
permanent - this cmdset will remain across a server reboot
|
permanent - this cmdset will remain across a server reboot
|
||||||
|
|
||||||
Note: An interesting feature of this method is if you were to
|
Note: An interesting feature of this method is if you were to
|
||||||
send it an *already instantiated cmdset* (i.e. not a class),
|
send it an *already instantiated cmdset* (i.e. not a class),
|
||||||
the current cmdsethandler's obj attribute will then *not* be
|
the current cmdsethandler's obj attribute will then *not* be
|
||||||
transferred over to this already instantiated set (this is
|
transferred over to this already instantiated set (this is
|
||||||
because it might be used elsewhere and can cause strange effects).
|
because it might be used elsewhere and can cause strange effects).
|
||||||
This means you could in principle have the handler
|
This means you could in principle have the handler
|
||||||
launch command sets tied to a *different* object than the
|
launch command sets tied to a *different* object than the
|
||||||
handler. Not sure when this would be useful, but it's a 'quirk'
|
handler. Not sure when this would be useful, but it's a 'quirk'
|
||||||
that has to be documented.
|
that has to be documented.
|
||||||
"""
|
"""
|
||||||
if callable(cmdset):
|
if callable(cmdset):
|
||||||
if not utils.inherits_from(cmdset, CmdSet):
|
if not utils.inherits_from(cmdset, CmdSet):
|
||||||
|
|
@ -278,13 +278,13 @@ class CmdSetHandler(object):
|
||||||
cmdset.permanent = True
|
cmdset.permanent = True
|
||||||
storage = self.obj.cmdset_storage
|
storage = self.obj.cmdset_storage
|
||||||
if not storage:
|
if not storage:
|
||||||
storage = ["", cmdset.path]
|
storage = ["", cmdset.path]
|
||||||
else:
|
else:
|
||||||
storage.append(cmdset.path)
|
storage.append(cmdset.path)
|
||||||
self.obj.cmdset_storage = storage
|
self.obj.cmdset_storage = storage
|
||||||
else:
|
else:
|
||||||
cmdset.permanent = False
|
cmdset.permanent = False
|
||||||
self.cmdset_stack.append(cmdset)
|
self.cmdset_stack.append(cmdset)
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
def add_default(self, cmdset, emit_to_obj=None, permanent=True):
|
def add_default(self, cmdset, emit_to_obj=None, permanent=True):
|
||||||
|
|
@ -292,11 +292,11 @@ class CmdSetHandler(object):
|
||||||
Add a new default cmdset. If an old default existed,
|
Add a new default cmdset. If an old default existed,
|
||||||
it is replaced. If permanent is set, the set will survive a reboot.
|
it is replaced. If permanent is set, the set will survive a reboot.
|
||||||
cmdset - can be a cmdset object or the python path to
|
cmdset - can be a cmdset object or the python path to
|
||||||
an instance of such an object.
|
an instance of such an object.
|
||||||
emit_to_obj - an object to receive error messages.
|
emit_to_obj - an object to receive error messages.
|
||||||
permanent - save cmdset across reboots
|
permanent - save cmdset across reboots
|
||||||
See also the notes for self.add(), which applies here too.
|
See also the notes for self.add(), which applies here too.
|
||||||
"""
|
"""
|
||||||
if callable(cmdset):
|
if callable(cmdset):
|
||||||
if not utils.inherits_from(cmdset, CmdSet):
|
if not utils.inherits_from(cmdset, CmdSet):
|
||||||
raise Exception("Only CmdSets can be added to the cmdsethandler!")
|
raise Exception("Only CmdSets can be added to the cmdsethandler!")
|
||||||
|
|
@ -311,9 +311,9 @@ class CmdSetHandler(object):
|
||||||
else:
|
else:
|
||||||
self.cmdset_stack = [cmdset]
|
self.cmdset_stack = [cmdset]
|
||||||
self.mergetype_stack = [cmdset.mergetype]
|
self.mergetype_stack = [cmdset.mergetype]
|
||||||
|
|
||||||
if permanent:
|
if permanent:
|
||||||
cmdset.permanent = True
|
cmdset.permanent = True
|
||||||
storage = self.obj.cmdset_storage
|
storage = self.obj.cmdset_storage
|
||||||
if storage:
|
if storage:
|
||||||
storage[0] = cmdset.path
|
storage[0] = cmdset.path
|
||||||
|
|
@ -321,12 +321,12 @@ class CmdSetHandler(object):
|
||||||
storage = [cmdset.path]
|
storage = [cmdset.path]
|
||||||
self.obj.cmdset_storage = storage
|
self.obj.cmdset_storage = storage
|
||||||
else:
|
else:
|
||||||
cmdset.permanent = False
|
cmdset.permanent = False
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
def delete(self, cmdset=None):
|
def delete(self, cmdset=None):
|
||||||
"""
|
"""
|
||||||
Remove a cmdset from the handler.
|
Remove a cmdset from the handler.
|
||||||
|
|
||||||
cmdset can be supplied either as a cmdset-key,
|
cmdset can be supplied either as a cmdset-key,
|
||||||
an instance of the CmdSet or a python path
|
an instance of the CmdSet or a python path
|
||||||
|
|
@ -338,17 +338,17 @@ class CmdSetHandler(object):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if len(self.cmdset_stack) < 2:
|
if len(self.cmdset_stack) < 2:
|
||||||
# don't allow deleting default cmdsets here.
|
# don't allow deleting default cmdsets here.
|
||||||
return
|
return
|
||||||
|
|
||||||
if not cmdset:
|
if not cmdset:
|
||||||
# remove the last one in the stack
|
# remove the last one in the stack
|
||||||
cmdset = self.cmdset_stack.pop()
|
cmdset = self.cmdset_stack.pop()
|
||||||
if cmdset.permanent:
|
if cmdset.permanent:
|
||||||
storage = self.obj.cmdset_storage
|
storage = self.obj.cmdset_storage
|
||||||
storage.pop()
|
storage.pop()
|
||||||
self.obj.cmdset_storage = storage
|
self.obj.cmdset_storage = storage
|
||||||
else:
|
else:
|
||||||
# try it as a callable
|
# try it as a callable
|
||||||
if callable(cmdset) and hasattr(cmdset, 'path'):
|
if callable(cmdset) and hasattr(cmdset, 'path'):
|
||||||
delcmdsets = [cset for cset in self.cmdset_stack[1:] if cset.path == cmdset.path]
|
delcmdsets = [cset for cset in self.cmdset_stack[1:] if cset.path == cmdset.path]
|
||||||
|
|
@ -358,21 +358,21 @@ class CmdSetHandler(object):
|
||||||
storage = []
|
storage = []
|
||||||
|
|
||||||
if any(cset.permanent for cset in delcmdsets):
|
if any(cset.permanent for cset in delcmdsets):
|
||||||
# only hit database if there's need to
|
# only hit database if there's need to
|
||||||
storage = self.obj.cmdset_storage
|
storage = self.obj.cmdset_storage
|
||||||
for cset in delcmdsets:
|
for cset in delcmdsets:
|
||||||
if cset.permanent:
|
if cset.permanent:
|
||||||
try:
|
try:
|
||||||
storage.remove(cset.path)
|
storage.remove(cset.path)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
pass
|
||||||
for cset in delcmdsets:
|
for cset in delcmdsets:
|
||||||
# clean the in-memory stack
|
# clean the in-memory stack
|
||||||
try:
|
try:
|
||||||
self.cmdset_stack.remove(cset)
|
self.cmdset_stack.remove(cset)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
pass
|
||||||
# re-sync the cmdsethandler.
|
# re-sync the cmdsethandler.
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
def delete_default(self):
|
def delete_default(self):
|
||||||
|
|
@ -385,9 +385,9 @@ class CmdSetHandler(object):
|
||||||
storage[0] = ""
|
storage[0] = ""
|
||||||
else:
|
else:
|
||||||
storage = [""]
|
storage = [""]
|
||||||
self.cmdset_storage = storage
|
self.cmdset_storage = storage
|
||||||
self.cmdset_stack[0] = CmdSet(cmdsetobj=self.obj, key="Empty")
|
self.cmdset_stack[0] = CmdSet(cmdsetobj=self.obj, key="Empty")
|
||||||
else:
|
else:
|
||||||
self.cmdset_stack = [CmdSet(cmdsetobj=self.obj, key="Empty")]
|
self.cmdset_stack = [CmdSet(cmdsetobj=self.obj, key="Empty")]
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
|
|
@ -405,21 +405,21 @@ class CmdSetHandler(object):
|
||||||
self.cmdset_stack = [self.cmdset_stack[0]]
|
self.cmdset_stack = [self.cmdset_stack[0]]
|
||||||
self.mergetype_stack = [self.cmdset_stack[0].mergetype]
|
self.mergetype_stack = [self.cmdset_stack[0].mergetype]
|
||||||
storage = self.obj.cmdset_storage
|
storage = self.obj.cmdset_storage
|
||||||
if storage:
|
if storage:
|
||||||
storage = storage[0]
|
storage = storage[0]
|
||||||
self.obj.cmdset_storage = storage
|
self.obj.cmdset_storage = storage
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
def has_cmdset(self, cmdset_key, must_be_default=False):
|
def has_cmdset(self, cmdset_key, must_be_default=False):
|
||||||
"""
|
"""
|
||||||
checks so the cmdsethandler contains a cmdset with the given key.
|
checks so the cmdsethandler contains a cmdset with the given key.
|
||||||
must_be_default - only match against the default cmdset.
|
must_be_default - only match against the default cmdset.
|
||||||
"""
|
"""
|
||||||
if must_be_default:
|
if must_be_default:
|
||||||
return self.cmdset_stack and self.cmdset_stack[0].key == cmdset_key
|
return self.cmdset_stack and self.cmdset_stack[0].key == cmdset_key
|
||||||
else:
|
else:
|
||||||
return any([cmdset.key == cmdset_key for cmdset in self.cmdset_stack])
|
return any([cmdset.key == cmdset_key for cmdset in self.cmdset_stack])
|
||||||
|
|
||||||
|
|
||||||
def all(self):
|
def all(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -429,16 +429,16 @@ class CmdSetHandler(object):
|
||||||
|
|
||||||
def reset(self):
|
def reset(self):
|
||||||
"""
|
"""
|
||||||
Force reload of all cmdsets in handler. This should be called
|
Force reload of all cmdsets in handler. This should be called
|
||||||
after CACHED_CMDSETS have been cleared (normally by @reload).
|
after CACHED_CMDSETS have been cleared (normally by @reload).
|
||||||
"""
|
"""
|
||||||
new_cmdset_stack = []
|
new_cmdset_stack = []
|
||||||
new_mergetype_stack = []
|
new_mergetype_stack = []
|
||||||
for cmdset in self.cmdset_stack:
|
for cmdset in self.cmdset_stack:
|
||||||
if cmdset.key == "Empty":
|
if cmdset.key == "Empty":
|
||||||
new_cmdset_stack.append(cmdset)
|
new_cmdset_stack.append(cmdset)
|
||||||
new_mergetype_stack.append("Union")
|
new_mergetype_stack.append("Union")
|
||||||
else:
|
else:
|
||||||
new_cmdset_stack.append(self.import_cmdset(cmdset.path))
|
new_cmdset_stack.append(self.import_cmdset(cmdset.path))
|
||||||
new_mergetype_stack.append(cmdset.mergetype)
|
new_mergetype_stack.append(cmdset.mergetype)
|
||||||
self.cmdset_stack = new_cmdset_stack
|
self.cmdset_stack = new_cmdset_stack
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
"""
|
"""
|
||||||
The base Command class.
|
The base Command class.
|
||||||
|
|
||||||
All commands in Evennia inherit from the 'Command' class in this module.
|
All commands in Evennia inherit from the 'Command' class in this module.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
@ -12,19 +12,19 @@ from src.utils.utils import is_iter
|
||||||
class CommandMeta(type):
|
class CommandMeta(type):
|
||||||
"""
|
"""
|
||||||
This metaclass makes some minor on-the-fly convenience fixes to the command
|
This metaclass makes some minor on-the-fly convenience fixes to the command
|
||||||
class in case the admin forgets to put things in lowercase etc.
|
class in case the admin forgets to put things in lowercase etc.
|
||||||
"""
|
"""
|
||||||
def __init__(mcs, *args, **kwargs):
|
def __init__(mcs, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Simply make sure all data are stored as lowercase and
|
Simply make sure all data are stored as lowercase and
|
||||||
do checking on all properties that should be in list form.
|
do checking on all properties that should be in list form.
|
||||||
Sets up locks to be more forgiving.
|
Sets up locks to be more forgiving.
|
||||||
"""
|
"""
|
||||||
mcs.key = mcs.key.lower()
|
mcs.key = mcs.key.lower()
|
||||||
if mcs.aliases and not is_iter(mcs.aliases):
|
if mcs.aliases and not is_iter(mcs.aliases):
|
||||||
try:
|
try:
|
||||||
mcs.aliases = mcs.aliases.split(',')
|
mcs.aliases = mcs.aliases.split(',')
|
||||||
except Exception:
|
except Exception:
|
||||||
mcs.aliases = []
|
mcs.aliases = []
|
||||||
mcs.aliases = [str(alias).strip() for alias in mcs.aliases]
|
mcs.aliases = [str(alias).strip() for alias in mcs.aliases]
|
||||||
if not hasattr(mcs, "save_for_next"):
|
if not hasattr(mcs, "save_for_next"):
|
||||||
|
|
@ -61,19 +61,19 @@ class CommandMeta(type):
|
||||||
# define their own parser method to handle the input. The
|
# define their own parser method to handle the input. The
|
||||||
# advantage of this is inheritage; commands that have similar
|
# advantage of this is inheritage; commands that have similar
|
||||||
# structure can parse the input string the same way, minimizing
|
# structure can parse the input string the same way, minimizing
|
||||||
# parsing errors.
|
# parsing errors.
|
||||||
|
|
||||||
class Command(object):
|
class Command(object):
|
||||||
"""
|
"""
|
||||||
Base command
|
Base command
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
command [args]
|
command [args]
|
||||||
|
|
||||||
This is the base command class. Inherit from this
|
This is the base command class. Inherit from this
|
||||||
to create new commands.
|
to create new commands.
|
||||||
|
|
||||||
The cmdhandler makes the following variables available to the
|
The cmdhandler makes the following variables available to the
|
||||||
command methods (so you can always assume them to be there):
|
command methods (so you can always assume them to be there):
|
||||||
self.caller - the game object calling the command
|
self.caller - the game object calling the command
|
||||||
self.cmdstring - the command name used to trigger this command (allows
|
self.cmdstring - the command name used to trigger this command (allows
|
||||||
|
|
@ -84,10 +84,20 @@ class Command(object):
|
||||||
seldomly, notably for help-type commands, to create dynamic
|
seldomly, notably for help-type commands, to create dynamic
|
||||||
help entries and lists)
|
help entries and lists)
|
||||||
cmd.obj - the object on which this command is defined. If a default command,
|
cmd.obj - the object on which this command is defined. If a default command,
|
||||||
this is usually the same as caller.
|
this is usually the same as caller.
|
||||||
|
|
||||||
(Note that this initial string is also used by the system to create the help
|
The following class properties can/should be defined on your child class:
|
||||||
entry for the command, so it's a good idea to format it similar to this one)
|
|
||||||
|
key - identifier for command (e.g. "look")
|
||||||
|
aliases - (optional) list of aliases (e.g. ["l", "loo"])
|
||||||
|
locks - lock string (default is "cmd:all()")
|
||||||
|
help_category - how to organize this help entry in help system (default is "General")
|
||||||
|
auto_help - defaults to True. Allows for turning off auto-help generation
|
||||||
|
arg_regex - (optional) raw string regex defining how the argument part of the command should look
|
||||||
|
in order to match for this command (e.g. must it be a space between cmdname and arg?)
|
||||||
|
|
||||||
|
(Note that if auto_help is on, this initial string is also used by the system
|
||||||
|
to create the help entry for the command, so it's a good idea to format it similar to this one)
|
||||||
"""
|
"""
|
||||||
# Tie our metaclass, for some convenience cleanup
|
# Tie our metaclass, for some convenience cleanup
|
||||||
__metaclass__ = CommandMeta
|
__metaclass__ = CommandMeta
|
||||||
|
|
@ -103,20 +113,20 @@ class Command(object):
|
||||||
|
|
||||||
# this normally does not need to be changed. It allows to turn off
|
# this normally does not need to be changed. It allows to turn off
|
||||||
# auto-help entry creation for individual commands.
|
# auto-help entry creation for individual commands.
|
||||||
auto_help = True
|
auto_help = True
|
||||||
# There is also the property 'obj'. This gets set by the system
|
# There is also the property 'obj'. This gets set by the system
|
||||||
# on the fly to tie this particular command to a certain in-game entity.
|
# on the fly to tie this particular command to a certain in-game entity.
|
||||||
# self.obj should NOT be defined here since it will not be overwritten
|
# self.obj should NOT be defined here since it will not be overwritten
|
||||||
# if it already exists.
|
# if it already exists.
|
||||||
|
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.lockhandler = LockHandler(self)
|
self.lockhandler = LockHandler(self)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
"Print the command"
|
"Print the command"
|
||||||
return self.key
|
return self.key
|
||||||
|
|
||||||
def __eq__(self, cmd):
|
def __eq__(self, cmd):
|
||||||
"""
|
"""
|
||||||
Compare two command instances to each other by matching their
|
Compare two command instances to each other by matching their
|
||||||
|
|
@ -132,7 +142,7 @@ class Command(object):
|
||||||
"""
|
"""
|
||||||
This implements searches like 'if query in cmd'. It's a fuzzy matching
|
This implements searches like 'if query in cmd'. It's a fuzzy matching
|
||||||
used by the help system, returning True if query can be found
|
used by the help system, returning True if query can be found
|
||||||
as a substring of the commands key or its aliases.
|
as a substring of the commands key or its aliases.
|
||||||
|
|
||||||
input can be either a command object or a command name.
|
input can be either a command object or a command name.
|
||||||
"""
|
"""
|
||||||
|
|
@ -156,11 +166,11 @@ class Command(object):
|
||||||
This hook is called by the cmdhandler to determine if srcobj
|
This hook is called by the cmdhandler to determine if srcobj
|
||||||
is allowed to execute this command. It should return a boolean
|
is allowed to execute this command. It should return a boolean
|
||||||
value and is not normally something that need to be changed since
|
value and is not normally something that need to be changed since
|
||||||
it's using the Evennia permission system directly.
|
it's using the Evennia permission system directly.
|
||||||
"""
|
"""
|
||||||
return self.lockhandler.check(srcobj, access_type, default=default)
|
return self.lockhandler.check(srcobj, access_type, default=default)
|
||||||
|
|
||||||
# Common Command hooks
|
# Common Command hooks
|
||||||
|
|
||||||
def at_pre_cmd(self):
|
def at_pre_cmd(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -170,7 +180,7 @@ class Command(object):
|
||||||
|
|
||||||
def at_post_cmd(self):
|
def at_post_cmd(self):
|
||||||
"""
|
"""
|
||||||
This hook is called after the command has finished executing
|
This hook is called after the command has finished executing
|
||||||
(after self.func()).
|
(after self.func()).
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
@ -181,31 +191,31 @@ class Command(object):
|
||||||
want, this function is run. If many of your commands have
|
want, this function is run. If many of your commands have
|
||||||
a similar syntax (for example 'cmd arg1 = arg2') you should simply
|
a similar syntax (for example 'cmd arg1 = arg2') you should simply
|
||||||
define this once and just let other commands of the same form
|
define this once and just let other commands of the same form
|
||||||
inherit from this. See the docstring of this module for
|
inherit from this. See the docstring of this module for
|
||||||
which object properties are available to use
|
which object properties are available to use
|
||||||
(notably self.args).
|
(notably self.args).
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"""
|
"""
|
||||||
This is the actual executing part of the command.
|
This is the actual executing part of the command.
|
||||||
It is called directly after self.parse(). See the docstring
|
It is called directly after self.parse(). See the docstring
|
||||||
of this module for which object properties are available
|
of this module for which object properties are available
|
||||||
(beyond those set in self.parse())
|
(beyond those set in self.parse())
|
||||||
"""
|
"""
|
||||||
# a simple test command to show the available properties
|
# a simple test command to show the available properties
|
||||||
string = "-" * 50
|
string = "-" * 50
|
||||||
string += "\n{w%s{n - Command variables from evennia:\n" % self.key
|
string += "\n{w%s{n - Command variables from evennia:\n" % self.key
|
||||||
string += "-" * 50
|
string += "-" * 50
|
||||||
string += "\nname of cmd (self.key): {w%s{n\n" % self.key
|
string += "\nname of cmd (self.key): {w%s{n\n" % self.key
|
||||||
string += "cmd aliases (self.aliases): {w%s{n\n" % self.aliases
|
string += "cmd aliases (self.aliases): {w%s{n\n" % self.aliases
|
||||||
string += "cmd perms (self.permissions): {w%s{n\n" % self.permissions
|
string += "cmd perms (self.permissions): {w%s{n\n" % self.permissions
|
||||||
string += "help category (self.help_category): {w%s{n\n" % self.help_category
|
string += "help category (self.help_category): {w%s{n\n" % self.help_category
|
||||||
string += "object calling (self.caller): {w%s{n\n" % self.caller
|
string += "object calling (self.caller): {w%s{n\n" % self.caller
|
||||||
string += "object storing cmdset (self.obj): {w%s{n\n" % self.obj
|
string += "object storing cmdset (self.obj): {w%s{n\n" % self.obj
|
||||||
string += "command string given (self.cmdstring): {w%s{n\n" % self.cmdstring
|
string += "command string given (self.cmdstring): {w%s{n\n" % self.cmdstring
|
||||||
# show cmdset.key instead of cmdset to shorten output
|
# show cmdset.key instead of cmdset to shorten output
|
||||||
string += utils.fill("current cmdset (self.cmdset): {w%s{n\n" % self.cmdset)
|
string += utils.fill("current cmdset (self.cmdset): {w%s{n\n" % self.cmdset)
|
||||||
|
|
||||||
self.caller.msg(string)
|
self.caller.msg(string)
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
#
|
#
|
||||||
# This is Evennia's default connection screen. It is imported
|
# This is Evennia's default connection screen. It is imported
|
||||||
# and run from game/gamesrc/world/connection_screens.py.
|
# and run from game/gamesrc/world/connection_screens.py.
|
||||||
#
|
#
|
||||||
|
|
||||||
from src.utils import utils
|
from src.utils import utils
|
||||||
|
|
||||||
DEFAULT_SCREEN = \
|
DEFAULT_SCREEN = \
|
||||||
"""{b=============================================================={n
|
"""{b=============================================================={n
|
||||||
Welcome to {gEvennia{n, version %s!
|
Welcome to {gEvennia{n, version %s!
|
||||||
|
|
||||||
If you have an existing account, connect to it by typing:
|
If you have an existing account, connect to it by typing:
|
||||||
{wconnect <email> <password>{n
|
{wconnect <email> <password>{n
|
||||||
|
|
|
||||||
|
|
@ -23,9 +23,9 @@ class HelpEntryAdmin(admin.ModelAdmin):
|
||||||
list_display_links = ('id', 'db_key')
|
list_display_links = ('id', 'db_key')
|
||||||
search_fields = ['^db_key', 'db_entrytext']
|
search_fields = ['^db_key', 'db_entrytext']
|
||||||
ordering = ['db_help_category', 'db_key']
|
ordering = ['db_help_category', 'db_key']
|
||||||
save_as = True
|
save_as = True
|
||||||
save_on_top = True
|
save_on_top = True
|
||||||
list_select_related = True
|
list_select_related = True
|
||||||
|
|
||||||
form = HelpEntryForm
|
form = HelpEntryForm
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,10 @@ from src.utils import logger, utils
|
||||||
|
|
||||||
class HelpEntryManager(models.Manager):
|
class HelpEntryManager(models.Manager):
|
||||||
"""
|
"""
|
||||||
This HelpEntryManager implements methods for searching
|
This HelpEntryManager implements methods for searching
|
||||||
and manipulating HelpEntries directly from the database.
|
and manipulating HelpEntries directly from the database.
|
||||||
|
|
||||||
These methods will all return database objects
|
These methods will all return database objects
|
||||||
(or QuerySets) directly.
|
(or QuerySets) directly.
|
||||||
|
|
||||||
Evennia-specific:
|
Evennia-specific:
|
||||||
|
|
@ -24,33 +24,33 @@ class HelpEntryManager(models.Manager):
|
||||||
def find_topicmatch(self, topicstr, exact=False):
|
def find_topicmatch(self, topicstr, exact=False):
|
||||||
"""
|
"""
|
||||||
Searches for matching topics based on player's input.
|
Searches for matching topics based on player's input.
|
||||||
"""
|
"""
|
||||||
if utils.dbref(topicstr):
|
if utils.dbref(topicstr):
|
||||||
return self.filter(id=utils.dbref(topicstr))
|
return self.filter(id=utils.dbref(topicstr))
|
||||||
topics = self.filter(db_key__iexact=topicstr)
|
topics = self.filter(db_key__iexact=topicstr)
|
||||||
if not topics and not exact:
|
if not topics and not exact:
|
||||||
topics = self.filter(db_key__istartswith=topicstr)
|
topics = self.filter(db_key__istartswith=topicstr)
|
||||||
if not topics:
|
if not topics:
|
||||||
topics = self.filter(db_key__icontains=topicstr)
|
topics = self.filter(db_key__icontains=topicstr)
|
||||||
return topics
|
return topics
|
||||||
|
|
||||||
def find_apropos(self, topicstr):
|
def find_apropos(self, topicstr):
|
||||||
"""
|
"""
|
||||||
Do a very loose search, returning all help entries containing
|
Do a very loose search, returning all help entries containing
|
||||||
the search criterion in their titles.
|
the search criterion in their titles.
|
||||||
"""
|
"""
|
||||||
return self.filter(db_key__icontains=topicstr)
|
return self.filter(db_key__icontains=topicstr)
|
||||||
|
|
||||||
def find_topicsuggestions(self, topicstr):
|
def find_topicsuggestions(self, topicstr):
|
||||||
"""
|
"""
|
||||||
Do a fuzzy match, preferably within the category of the
|
Do a fuzzy match, preferably within the category of the
|
||||||
current topic.
|
current topic.
|
||||||
"""
|
"""
|
||||||
topics = self.find_apropos(topicstr)
|
topics = self.find_apropos(topicstr)
|
||||||
# we need to clean away the given help entry.
|
# we need to clean away the given help entry.
|
||||||
return [topic for topic in topics
|
return [topic for topic in topics
|
||||||
if topic.key.lower() != topicstr.lower()]
|
if topic.key.lower() != topicstr.lower()]
|
||||||
|
|
||||||
def find_topics_with_category(self, help_category):
|
def find_topics_with_category(self, help_category):
|
||||||
"""
|
"""
|
||||||
Search topics having a particular category
|
Search topics having a particular category
|
||||||
|
|
@ -76,7 +76,7 @@ class HelpEntryManager(models.Manager):
|
||||||
Shifts all help entries in database to default_category.
|
Shifts all help entries in database to default_category.
|
||||||
This action cannot be reverted. It is used primarily by
|
This action cannot be reverted. It is used primarily by
|
||||||
the engine when importing a default help database, making
|
the engine when importing a default help database, making
|
||||||
sure this ends up in one easily separated category.
|
sure this ends up in one easily separated category.
|
||||||
"""
|
"""
|
||||||
topics = self.all()
|
topics = self.all()
|
||||||
for topic in topics:
|
for topic in topics:
|
||||||
|
|
@ -92,7 +92,7 @@ class HelpEntryManager(models.Manager):
|
||||||
ostring - the help topic to look for
|
ostring - the help topic to look for
|
||||||
category - limit the search to a particular help topic
|
category - limit the search to a particular help topic
|
||||||
"""
|
"""
|
||||||
ostring = ostring.strip().lower()
|
ostring = ostring.strip().lower()
|
||||||
help_entries = self.filter(db_topicstr=ostring)
|
help_entries = self.filter(db_topicstr=ostring)
|
||||||
if help_category:
|
if help_category:
|
||||||
help_entries.filter(db_help_category=help_category)
|
help_entries.filter(db_help_category=help_category)
|
||||||
|
|
|
||||||
|
|
@ -41,20 +41,20 @@ class HelpEntry(SharedMemoryModel):
|
||||||
# These database fields are all set using their corresponding properties,
|
# These database fields are all set using their corresponding properties,
|
||||||
# named same as the field, but withtout the db_* prefix.
|
# named same as the field, but withtout the db_* prefix.
|
||||||
|
|
||||||
# title of the help
|
# title of the help
|
||||||
db_key = models.CharField('help key', max_length=255, unique=True, help_text='key to search for')
|
db_key = models.CharField('help key', max_length=255, unique=True, help_text='key to search for')
|
||||||
# help category
|
# help category
|
||||||
db_help_category = models.CharField("help category", max_length=255, default="General",
|
db_help_category = models.CharField("help category", max_length=255, default="General",
|
||||||
help_text='organizes help entries in lists')
|
help_text='organizes help entries in lists')
|
||||||
# the actual help entry text, in any formatting.
|
# the actual help entry text, in any formatting.
|
||||||
db_entrytext = models.TextField('help entry', blank=True, help_text='the main body of help text')
|
db_entrytext = models.TextField('help entry', blank=True, help_text='the main body of help text')
|
||||||
# a string of permissionstrings, separated by commas. Not used by help entries.
|
# a string of permissionstrings, separated by commas. Not used by help entries.
|
||||||
db_permissions = models.CharField('permissions', max_length=255, blank=True)
|
db_permissions = models.CharField('permissions', max_length=255, blank=True)
|
||||||
# lock string storage
|
# lock string storage
|
||||||
db_lock_storage = models.CharField('locks', max_length=512, blank=True, help_text='normally view:all().')
|
db_lock_storage = models.CharField('locks', max_length=512, blank=True, help_text='normally view:all().')
|
||||||
# (deprecated, only here to allow MUX helpfile load (don't use otherwise)).
|
# (deprecated, only here to allow MUX helpfile load (don't use otherwise)).
|
||||||
# TODO: remove this when not needed anymore.
|
# TODO: remove this when not needed anymore.
|
||||||
db_staff_only = models.BooleanField(default=False)
|
db_staff_only = models.BooleanField(default=False)
|
||||||
|
|
||||||
# Database manager
|
# Database manager
|
||||||
objects = HelpEntryManager()
|
objects = HelpEntryManager()
|
||||||
|
|
@ -62,7 +62,7 @@ class HelpEntry(SharedMemoryModel):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
SharedMemoryModel.__init__(self, *args, **kwargs)
|
SharedMemoryModel.__init__(self, *args, **kwargs)
|
||||||
self.locks = LockHandler(self)
|
self.locks = LockHandler(self)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"Define Django meta options"
|
"Define Django meta options"
|
||||||
verbose_name = "Help Entry"
|
verbose_name = "Help Entry"
|
||||||
|
|
@ -72,10 +72,10 @@ class HelpEntry(SharedMemoryModel):
|
||||||
# @property decorators that allows to access these fields using
|
# @property decorators that allows to access these fields using
|
||||||
# normal python operations (without having to remember to save()
|
# normal python operations (without having to remember to save()
|
||||||
# etc). So e.g. a property 'attr' has a get/set/del decorator
|
# etc). So e.g. a property 'attr' has a get/set/del decorator
|
||||||
# defined that allows the user to do self.attr = value,
|
# defined that allows the user to do self.attr = value,
|
||||||
# value = self.attr and del self.attr respectively (where self
|
# value = self.attr and del self.attr respectively (where self
|
||||||
# is the object in question).
|
# is the object in question).
|
||||||
|
|
||||||
# key property (wraps db_key)
|
# key property (wraps db_key)
|
||||||
#@property
|
#@property
|
||||||
def key_get(self):
|
def key_get(self):
|
||||||
|
|
@ -137,7 +137,7 @@ class HelpEntry(SharedMemoryModel):
|
||||||
if is_iter(value):
|
if is_iter(value):
|
||||||
value = ",".join([str(val).strip().lower() for val in value])
|
value = ",".join([str(val).strip().lower() for val in value])
|
||||||
self.db_permissions = value
|
self.db_permissions = value
|
||||||
self.save()
|
self.save()
|
||||||
#@permissions.deleter
|
#@permissions.deleter
|
||||||
def permissions_del(self):
|
def permissions_del(self):
|
||||||
"Deleter. Allows for del self.permissions"
|
"Deleter. Allows for del self.permissions"
|
||||||
|
|
@ -146,7 +146,7 @@ class HelpEntry(SharedMemoryModel):
|
||||||
permissions = property(permissions_get, permissions_set, permissions_del)
|
permissions = property(permissions_get, permissions_set, permissions_del)
|
||||||
|
|
||||||
# lock_storage property (wraps db_lock_storage)
|
# lock_storage property (wraps db_lock_storage)
|
||||||
#@property
|
#@property
|
||||||
def lock_storage_get(self):
|
def lock_storage_get(self):
|
||||||
"Getter. Allows for value = self.lock_storage"
|
"Getter. Allows for value = self.lock_storage"
|
||||||
return self.db_lock_storage
|
return self.db_lock_storage
|
||||||
|
|
@ -161,13 +161,13 @@ class HelpEntry(SharedMemoryModel):
|
||||||
logger.log_errmsg("Lock_Storage (on %s) cannot be deleted. Use obj.lock.delete() instead." % self)
|
logger.log_errmsg("Lock_Storage (on %s) cannot be deleted. Use obj.lock.delete() instead." % self)
|
||||||
lock_storage = property(lock_storage_get, lock_storage_set, lock_storage_del)
|
lock_storage = property(lock_storage_get, lock_storage_set, lock_storage_del)
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# HelpEntry main class methods
|
# HelpEntry main class methods
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.key
|
return self.key
|
||||||
|
|
||||||
|
|
@ -180,5 +180,5 @@ class HelpEntry(SharedMemoryModel):
|
||||||
accessing_obj - object trying to access this one
|
accessing_obj - object trying to access this one
|
||||||
access_type - type of access sought
|
access_type - type of access sought
|
||||||
default - what to return if no lock of access_type was found
|
default - what to return if no lock of access_type was found
|
||||||
"""
|
"""
|
||||||
return self.locks.check(accessing_obj, access_type=access_type, default=default)
|
return self.locks.check(accessing_obj, access_type=access_type, default=default)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
# This sets up how models are displayed
|
# This sets up how models are displayed
|
||||||
# in the web admin interface.
|
# in the web admin interface.
|
||||||
#
|
#
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
|
|
@ -35,38 +35,38 @@ class ObjectCreateForm(forms.ModelForm):
|
||||||
db_typeclass_path = forms.CharField(label="Typeclass",initial="Change to (for example) %s or %s." % (settings.BASE_OBJECT_TYPECLASS, settings.BASE_CHARACTER_TYPECLASS),
|
db_typeclass_path = forms.CharField(label="Typeclass",initial="Change to (for example) %s or %s." % (settings.BASE_OBJECT_TYPECLASS, settings.BASE_CHARACTER_TYPECLASS),
|
||||||
widget=forms.TextInput(attrs={'size':'78'}),
|
widget=forms.TextInput(attrs={'size':'78'}),
|
||||||
help_text="This defines what 'type' of entity this is. This variable holds a Python path to a module with a valid Evennia Typeclass. If you are creating a Character you should use the typeclass defined by settings.BASE_CHARACTER_TYPECLASS or one derived from that.")
|
help_text="This defines what 'type' of entity this is. This variable holds a Python path to a module with a valid Evennia Typeclass. If you are creating a Character you should use the typeclass defined by settings.BASE_CHARACTER_TYPECLASS or one derived from that.")
|
||||||
db_permissions = forms.CharField(label="Permissions",
|
db_permissions = forms.CharField(label="Permissions",
|
||||||
initial=settings.PERMISSION_PLAYER_DEFAULT,
|
initial=settings.PERMISSION_PLAYER_DEFAULT,
|
||||||
required=False,
|
required=False,
|
||||||
widget=forms.TextInput(attrs={'size':'78'}),
|
widget=forms.TextInput(attrs={'size':'78'}),
|
||||||
help_text="a comma-separated list of text strings checked by certain locks. They are mainly of use for Character objects. Character permissions overload permissions defined on a controlling Player. Most objects normally don't have any permissions defined.")
|
help_text="a comma-separated list of text strings checked by certain locks. They are mainly of use for Character objects. Character permissions overload permissions defined on a controlling Player. Most objects normally don't have any permissions defined.")
|
||||||
db_cmdset_storage = forms.CharField(label="CmdSet",
|
db_cmdset_storage = forms.CharField(label="CmdSet",
|
||||||
initial=settings.CMDSET_DEFAULT,
|
initial=settings.CMDSET_DEFAULT,
|
||||||
required=False,
|
required=False,
|
||||||
widget=forms.TextInput(attrs={'size':'78'}),
|
widget=forms.TextInput(attrs={'size':'78'}),
|
||||||
help_text="Most non-character objects don't need a cmdset and can leave this field blank.")
|
help_text="Most non-character objects don't need a cmdset and can leave this field blank.")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ObjectEditForm(ObjectCreateForm):
|
class ObjectEditForm(ObjectCreateForm):
|
||||||
"Form used for editing. Extends the create one with more fields"
|
"Form used for editing. Extends the create one with more fields"
|
||||||
|
|
||||||
db_lock_storage = forms.CharField(label="Locks",
|
db_lock_storage = forms.CharField(label="Locks",
|
||||||
required=False,
|
required=False,
|
||||||
widget=forms.Textarea(attrs={'cols':'100', 'rows':'2'}),
|
widget=forms.Textarea(attrs={'cols':'100', 'rows':'2'}),
|
||||||
help_text="In-game lock definition string. If not given, defaults will be used. This string should be on the form <i>type:lockfunction(args);type2:lockfunction2(args);...")
|
help_text="In-game lock definition string. If not given, defaults will be used. This string should be on the form <i>type:lockfunction(args);type2:lockfunction2(args);...")
|
||||||
|
|
||||||
|
|
||||||
class ObjectDBAdmin(admin.ModelAdmin):
|
class ObjectDBAdmin(admin.ModelAdmin):
|
||||||
|
|
||||||
list_display = ('id', 'db_key', 'db_location', 'db_player', 'db_typeclass_path')
|
list_display = ('id', 'db_key', 'db_location', 'db_player', 'db_typeclass_path')
|
||||||
list_display_links = ('id', 'db_key')
|
list_display_links = ('id', 'db_key')
|
||||||
ordering = ['db_player', 'db_typeclass_path', 'id']
|
ordering = ['db_player', 'db_typeclass_path', 'id']
|
||||||
search_fields = ['^db_key', 'db_typeclass_path']
|
search_fields = ['^db_key', 'db_typeclass_path']
|
||||||
|
|
||||||
save_as = True
|
save_as = True
|
||||||
save_on_top = True
|
save_on_top = True
|
||||||
list_select_related = True
|
list_select_related = True
|
||||||
list_filter = ('db_permissions', 'db_location', 'db_typeclass_path')
|
list_filter = ('db_permissions', 'db_location', 'db_typeclass_path')
|
||||||
|
|
||||||
# editing fields setup
|
# editing fields setup
|
||||||
|
|
@ -74,7 +74,7 @@ class ObjectDBAdmin(admin.ModelAdmin):
|
||||||
form = ObjectEditForm
|
form = ObjectEditForm
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
(None, {
|
(None, {
|
||||||
'fields': (('db_key','db_typeclass_path'), ('db_permissions', 'db_lock_storage'),
|
'fields': (('db_key','db_typeclass_path'), ('db_permissions', 'db_lock_storage'),
|
||||||
('db_location', 'db_home'), 'db_destination','db_cmdset_storage'
|
('db_location', 'db_home'), 'db_destination','db_cmdset_storage'
|
||||||
)}),
|
)}),
|
||||||
)
|
)
|
||||||
|
|
@ -88,7 +88,7 @@ class ObjectDBAdmin(admin.ModelAdmin):
|
||||||
add_form = ObjectCreateForm
|
add_form = ObjectCreateForm
|
||||||
add_fieldsets = (
|
add_fieldsets = (
|
||||||
(None, {
|
(None, {
|
||||||
'fields': (('db_key','db_typeclass_path'), 'db_permissions',
|
'fields': (('db_key','db_typeclass_path'), 'db_permissions',
|
||||||
('db_location', 'db_home'), 'db_destination','db_cmdset_storage'
|
('db_location', 'db_home'), 'db_destination','db_cmdset_storage'
|
||||||
)}),
|
)}),
|
||||||
)
|
)
|
||||||
|
|
@ -116,8 +116,8 @@ class ObjectDBAdmin(admin.ModelAdmin):
|
||||||
obj = obj.typeclass
|
obj = obj.typeclass
|
||||||
obj.basetype_setup()
|
obj.basetype_setup()
|
||||||
obj.basetype_posthook_setup()
|
obj.basetype_posthook_setup()
|
||||||
obj.at_object_creation()
|
obj.at_object_creation()
|
||||||
obj.at_init()
|
obj.at_init()
|
||||||
|
|
||||||
|
|
||||||
admin.site.register(ObjectDB, ObjectDBAdmin)
|
admin.site.register(ObjectDB, ObjectDBAdmin)
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ Custom manager for Objects.
|
||||||
"""
|
"""
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.db.models.fields import exceptions
|
from django.db.models.fields import exceptions
|
||||||
from src.typeclasses.managers import TypedObjectManager
|
from src.typeclasses.managers import TypedObjectManager
|
||||||
from src.typeclasses.managers import returns_typeclass, returns_typeclass_list
|
from src.typeclasses.managers import returns_typeclass, returns_typeclass_list
|
||||||
from src.utils import utils
|
from src.utils import utils
|
||||||
|
|
@ -17,11 +17,11 @@ AT_MULTIMATCH_INPUT = utils.mod_import(*settings.SEARCH_AT_MULTIMATCH_INPUT.rspl
|
||||||
class ObjectManager(TypedObjectManager):
|
class ObjectManager(TypedObjectManager):
|
||||||
"""
|
"""
|
||||||
This ObjectManager implementes methods for searching
|
This ObjectManager implementes methods for searching
|
||||||
and manipulating Objects directly from the database.
|
and manipulating Objects directly from the database.
|
||||||
|
|
||||||
Evennia-specific search methods (will return Typeclasses or
|
Evennia-specific search methods (will return Typeclasses or
|
||||||
lists of Typeclasses, whereas Django-general methods will return
|
lists of Typeclasses, whereas Django-general methods will return
|
||||||
Querysets or database objects).
|
Querysets or database objects).
|
||||||
|
|
||||||
dbref (converter)
|
dbref (converter)
|
||||||
dbref_search
|
dbref_search
|
||||||
|
|
@ -41,19 +41,19 @@ class ObjectManager(TypedObjectManager):
|
||||||
copy_object
|
copy_object
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
#
|
#
|
||||||
# ObjectManager Get methods
|
# ObjectManager Get methods
|
||||||
#
|
#
|
||||||
|
|
||||||
# user/player related
|
# user/player related
|
||||||
|
|
||||||
@returns_typeclass
|
@returns_typeclass
|
||||||
def get_object_with_user(self, user):
|
def get_object_with_user(self, user):
|
||||||
"""
|
"""
|
||||||
Matches objects with obj.player.user matching the argument.
|
Matches objects with obj.player.user matching the argument.
|
||||||
A player<->user is a one-to-relationship, so this always
|
A player<->user is a one-to-relationship, so this always
|
||||||
returns just one result or None.
|
returns just one result or None.
|
||||||
|
|
||||||
user - may be a user object or user id.
|
user - may be a user object or user id.
|
||||||
"""
|
"""
|
||||||
|
|
@ -61,26 +61,26 @@ class ObjectManager(TypedObjectManager):
|
||||||
uid = int(user)
|
uid = int(user)
|
||||||
except TypeError:
|
except TypeError:
|
||||||
try:
|
try:
|
||||||
uid = user.id
|
uid = user.id
|
||||||
except:
|
except:
|
||||||
return None
|
return None
|
||||||
try:
|
try:
|
||||||
return self.get(db_player__user__id=uid)
|
return self.get(db_player__user__id=uid)
|
||||||
except Exception:
|
except Exception:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# This returns typeclass since get_object_with_user and get_dbref does.
|
# This returns typeclass since get_object_with_user and get_dbref does.
|
||||||
def get_object_with_player(self, search_string):
|
def get_object_with_player(self, search_string):
|
||||||
"""
|
"""
|
||||||
Search for an object based on its player's name or dbref.
|
Search for an object based on its player's name or dbref.
|
||||||
This search
|
This search
|
||||||
is sometimes initiated by appending a * to the beginning of
|
is sometimes initiated by appending a * to the beginning of
|
||||||
the search criterion (e.g. in local_and_global_search).
|
the search criterion (e.g. in local_and_global_search).
|
||||||
search_string: (string) The name or dbref to search for.
|
search_string: (string) The name or dbref to search for.
|
||||||
"""
|
"""
|
||||||
search_string = to_unicode(search_string).lstrip('*')
|
search_string = to_unicode(search_string).lstrip('*')
|
||||||
dbref = self.dbref(search_string)
|
dbref = self.dbref(search_string)
|
||||||
if not dbref:
|
if not dbref:
|
||||||
# not a dbref. Search by name.
|
# not a dbref. Search by name.
|
||||||
player_matches = User.objects.filter(username__iexact=search_string)
|
player_matches = User.objects.filter(username__iexact=search_string)
|
||||||
if player_matches:
|
if player_matches:
|
||||||
|
|
@ -105,17 +105,17 @@ class ObjectManager(TypedObjectManager):
|
||||||
from src.objects.models import ObjAttribute
|
from src.objects.models import ObjAttribute
|
||||||
lstring = ""
|
lstring = ""
|
||||||
if location:
|
if location:
|
||||||
lstring = ", db_obj__db_location=location"
|
lstring = ", db_obj__db_location=location"
|
||||||
attrs = eval("ObjAttribute.objects.filter(db_key=attribute_name%s)" % lstring)
|
attrs = eval("ObjAttribute.objects.filter(db_key=attribute_name%s)" % lstring)
|
||||||
return [attr.obj for attr in attrs]
|
return [attr.obj for attr in attrs]
|
||||||
|
|
||||||
@returns_typeclass_list
|
@returns_typeclass_list
|
||||||
def get_objs_with_attr_match(self, attribute_name, attribute_value, location=None, exact=False):
|
def get_objs_with_attr_match(self, attribute_name, attribute_value, location=None, exact=False):
|
||||||
"""
|
"""
|
||||||
Returns all objects having the valid
|
Returns all objects having the valid
|
||||||
attrname set to the given value. Note that no conversion is made
|
attrname set to the given value. Note that no conversion is made
|
||||||
to attribute_value, and so it can accept also non-strings.
|
to attribute_value, and so it can accept also non-strings.
|
||||||
"""
|
"""
|
||||||
from src.objects.models import ObjAttribute
|
from src.objects.models import ObjAttribute
|
||||||
lstring = ""
|
lstring = ""
|
||||||
if location:
|
if location:
|
||||||
|
|
@ -123,27 +123,27 @@ class ObjectManager(TypedObjectManager):
|
||||||
attrs = eval("ObjAttribute.objects.filter(db_key=attribute_name%s)" % lstring)
|
attrs = eval("ObjAttribute.objects.filter(db_key=attribute_name%s)" % lstring)
|
||||||
# since attribute values are pickled in database, we cannot search directly, but
|
# since attribute values are pickled in database, we cannot search directly, but
|
||||||
# must loop through the results. .
|
# must loop through the results. .
|
||||||
if exact:
|
if exact:
|
||||||
return [attr.obj for attr in attrs if attribute_value == attr.value]
|
return [attr.obj for attr in attrs if attribute_value == attr.value]
|
||||||
else:
|
else:
|
||||||
return [attr.obj for attr in attrs if to_unicode(attribute_value) in str(attr.value)]
|
return [attr.obj for attr in attrs if to_unicode(attribute_value) in str(attr.value)]
|
||||||
|
|
||||||
@returns_typeclass_list
|
@returns_typeclass_list
|
||||||
def get_objs_with_db_property(self, property_name, location=None):
|
def get_objs_with_db_property(self, property_name, location=None):
|
||||||
"""
|
"""
|
||||||
Returns all objects having a given db field property.
|
Returns all objects having a given db field property.
|
||||||
property_name = search string
|
property_name = search string
|
||||||
location - actual location object to restrict to
|
location - actual location object to restrict to
|
||||||
|
|
||||||
"""
|
"""
|
||||||
lstring = ""
|
lstring = ""
|
||||||
if location:
|
if location:
|
||||||
lstring = ".filter(db_location=location)"
|
lstring = ".filter(db_location=location)"
|
||||||
try:
|
try:
|
||||||
return eval("self.exclude(db_%s=None)%s" % (property_name, lstring))
|
return eval("self.exclude(db_%s=None)%s" % (property_name, lstring))
|
||||||
except exceptions.FieldError:
|
except exceptions.FieldError:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
@returns_typeclass_list
|
@returns_typeclass_list
|
||||||
def get_objs_with_db_property_match(self, property_name, property_value, location, exact=False):
|
def get_objs_with_db_property_match(self, property_name, property_value, location, exact=False):
|
||||||
"""
|
"""
|
||||||
|
|
@ -165,7 +165,7 @@ class ObjectManager(TypedObjectManager):
|
||||||
def get_objs_with_key_or_alias(self, ostring, location, exact=False):
|
def get_objs_with_key_or_alias(self, ostring, location, exact=False):
|
||||||
"""
|
"""
|
||||||
Returns objects based on key or alias match
|
Returns objects based on key or alias match
|
||||||
"""
|
"""
|
||||||
lstring_key, lstring_alias, estring = "", "", "icontains"
|
lstring_key, lstring_alias, estring = "", "", "icontains"
|
||||||
if location:
|
if location:
|
||||||
lstring_key = ", db_location=location"
|
lstring_key = ", db_location=location"
|
||||||
|
|
@ -181,7 +181,7 @@ class ObjectManager(TypedObjectManager):
|
||||||
return matches
|
return matches
|
||||||
|
|
||||||
# main search methods and helper functions
|
# main search methods and helper functions
|
||||||
|
|
||||||
@returns_typeclass_list
|
@returns_typeclass_list
|
||||||
def get_contents(self, location, excludeobj=None):
|
def get_contents(self, location, excludeobj=None):
|
||||||
"""
|
"""
|
||||||
|
|
@ -192,31 +192,40 @@ class ObjectManager(TypedObjectManager):
|
||||||
if excludeobj:
|
if excludeobj:
|
||||||
estring = ".exclude(db_key=excludeobj)"
|
estring = ".exclude(db_key=excludeobj)"
|
||||||
return eval("self.filter(db_location__id=location.id)%s" % estring)
|
return eval("self.filter(db_location__id=location.id)%s" % estring)
|
||||||
|
|
||||||
@returns_typeclass_list
|
@returns_typeclass_list
|
||||||
def object_search(self, ostring, caller=None,
|
def object_search(self, ostring, caller=None,
|
||||||
global_search=False,
|
global_search=False,
|
||||||
attribute_name=None, location=None):
|
attribute_name=None, location=None):
|
||||||
"""
|
"""
|
||||||
Search as an object and return results. The result is always an Object.
|
Search as an object and return results. The result is always an Object.
|
||||||
If * is appended (player search, a Character controlled by this Player
|
If * is appended (player search, a Character controlled by this Player
|
||||||
is looked for. The Character is returned, not the Player. Use player_search
|
is looked for. The Character is returned, not the Player. Use player_search
|
||||||
to find Player objects. Always returns a list.
|
to find Player objects. Always returns a list.
|
||||||
|
|
||||||
|
Arguments:
|
||||||
ostring: (string) The string to compare names against.
|
ostring: (string) The string to compare names against.
|
||||||
Can be a dbref. If name is appended by *, a player is searched for.
|
Can be a dbref. If name is appended by *, a player is searched for.
|
||||||
caller: (Object) The object performing the search.
|
caller: (Object) The optional object performing the search.
|
||||||
global_search: Search all objects, not just the current location/inventory
|
global_search (bool). Defaults to False. If a caller is defined, search will
|
||||||
attribute_name: (string) Which attribute to search in each object.
|
be restricted to the contents of caller.location unless global_search
|
||||||
If None, the default 'key' attribute is used.
|
is True. If no caller is given (or the caller has no location), a
|
||||||
location: If None, character.location will be used.
|
global search is assumed automatically.
|
||||||
|
attribute_name: (string) Which object attribute to match ostring against. If not
|
||||||
|
set, the "key" and "aliases" properties are searched in order.
|
||||||
|
location (Object): If set, this location's contents will be used to limit the search instead
|
||||||
|
of the callers. global_search will override this argument
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A list of matching objects (or a list with one unique match)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
ostring = to_unicode(ostring, force_string=True)
|
ostring = to_unicode(ostring, force_string=True)
|
||||||
|
|
||||||
if not ostring:
|
if not ostring:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
# Easiest case - dbref matching (always exact)
|
# Easiest case - dbref matching (always exact)
|
||||||
dbref = self.dbref(ostring)
|
dbref = self.dbref(ostring)
|
||||||
if dbref:
|
if dbref:
|
||||||
dbref_match = self.dbref_search(dbref)
|
dbref_match = self.dbref_search(dbref)
|
||||||
|
|
@ -229,12 +238,12 @@ class ObjectManager(TypedObjectManager):
|
||||||
# Test some common self-references
|
# Test some common self-references
|
||||||
|
|
||||||
if location and ostring == 'here':
|
if location and ostring == 'here':
|
||||||
return [location]
|
return [location]
|
||||||
if caller and ostring in ('me', 'self'):
|
if caller and ostring in ('me', 'self'):
|
||||||
return [caller]
|
return [caller]
|
||||||
if caller and ostring in ('*me', '*self'):
|
if caller and ostring in ('*me', '*self'):
|
||||||
return [caller]
|
return [caller]
|
||||||
|
|
||||||
# Test if we are looking for an object controlled by a
|
# Test if we are looking for an object controlled by a
|
||||||
# specific player
|
# specific player
|
||||||
|
|
||||||
|
|
@ -244,24 +253,24 @@ class ObjectManager(TypedObjectManager):
|
||||||
player_match = self.get_object_with_player(ostring)
|
player_match = self.get_object_with_player(ostring)
|
||||||
if player_match is not None:
|
if player_match is not None:
|
||||||
return [player_match]
|
return [player_match]
|
||||||
|
|
||||||
# Search for keys, aliases or other attributes
|
# Search for keys, aliases or other attributes
|
||||||
|
|
||||||
search_locations = [None] # this means a global search
|
search_locations = [None] # this means a global search
|
||||||
if not global_search and location:
|
if not global_search and location:
|
||||||
# Test if we are referring to the current room
|
# Test if we are referring to the current room
|
||||||
if location and (ostring.lower() == location.key.lower()
|
if location and (ostring.lower() == location.key.lower()
|
||||||
or ostring.lower() in [alias.lower() for alias in location.aliases]):
|
or ostring.lower() in [alias.lower() for alias in location.aliases]):
|
||||||
return [location]
|
return [location]
|
||||||
# otherwise, setup the locations to search in
|
# otherwise, setup the locations to search in
|
||||||
search_locations = [location]
|
search_locations = [location]
|
||||||
if caller:
|
if caller:
|
||||||
search_locations.append(caller)
|
search_locations.append(caller)
|
||||||
|
|
||||||
def local_and_global_search(ostring, exact=False):
|
def local_and_global_search(ostring, exact=False):
|
||||||
"Helper method for searching objects"
|
"Helper method for searching objects"
|
||||||
matches = []
|
matches = []
|
||||||
for location in search_locations:
|
for location in search_locations:
|
||||||
if attribute_name:
|
if attribute_name:
|
||||||
# Attribute/property search. First, search for db_<attrname> matches on the model
|
# Attribute/property search. First, search for db_<attrname> matches on the model
|
||||||
matches.extend(self.get_objs_with_db_property_match(attribute_name, ostring, location, exact))
|
matches.extend(self.get_objs_with_db_property_match(attribute_name, ostring, location, exact))
|
||||||
|
|
@ -269,14 +278,14 @@ class ObjectManager(TypedObjectManager):
|
||||||
# Next, try Attribute matches
|
# Next, try Attribute matches
|
||||||
matches.extend(self.get_objs_with_attr_match(attribute_name, ostring, location, exact))
|
matches.extend(self.get_objs_with_attr_match(attribute_name, ostring, location, exact))
|
||||||
else:
|
else:
|
||||||
# No attribute/property named. Do a normal key/alias-search
|
# No attribute/property named. Do a normal key/alias-search
|
||||||
matches.extend(self.get_objs_with_key_or_alias(ostring, location, exact))
|
matches.extend(self.get_objs_with_key_or_alias(ostring, location, exact))
|
||||||
return matches
|
return matches
|
||||||
|
|
||||||
# Search through all possibilities.
|
# Search through all possibilities.
|
||||||
|
|
||||||
match_number = None
|
match_number = None
|
||||||
matches = local_and_global_search(ostring, exact=True)
|
matches = local_and_global_search(ostring, exact=True)
|
||||||
if not matches:
|
if not matches:
|
||||||
# if we have no match, check if we are dealing with an "N-keyword" query - if so, strip it.
|
# if we have no match, check if we are dealing with an "N-keyword" query - if so, strip it.
|
||||||
match_number, ostring = AT_MULTIMATCH_INPUT(ostring)
|
match_number, ostring = AT_MULTIMATCH_INPUT(ostring)
|
||||||
|
|
@ -289,7 +298,7 @@ class ObjectManager(TypedObjectManager):
|
||||||
elif len(matches) > 1:
|
elif len(matches) > 1:
|
||||||
# multiple matches already. Run a fuzzy search. This catches partial matches (suggestions)
|
# multiple matches already. Run a fuzzy search. This catches partial matches (suggestions)
|
||||||
matches = local_and_global_search(ostring, exact=False)
|
matches = local_and_global_search(ostring, exact=False)
|
||||||
|
|
||||||
# deal with the result
|
# deal with the result
|
||||||
if len(matches) > 1 and match_number != None:
|
if len(matches) > 1 and match_number != None:
|
||||||
# We have multiple matches, but a N-type match number is available to separate them.
|
# We have multiple matches, but a N-type match number is available to separate them.
|
||||||
|
|
@ -299,21 +308,21 @@ class ObjectManager(TypedObjectManager):
|
||||||
pass
|
pass
|
||||||
# This is always a list.
|
# This is always a list.
|
||||||
return matches
|
return matches
|
||||||
|
|
||||||
#
|
#
|
||||||
# ObjectManager Copy method
|
# ObjectManager Copy method
|
||||||
#
|
#
|
||||||
|
|
||||||
def copy_object(self, original_object, new_key=None,
|
def copy_object(self, original_object, new_key=None,
|
||||||
new_location=None, new_player=None, new_home=None,
|
new_location=None, new_player=None, new_home=None,
|
||||||
new_permissions=None, new_locks=None, new_aliases=None, new_destination=None):
|
new_permissions=None, new_locks=None, new_aliases=None, new_destination=None):
|
||||||
"""
|
"""
|
||||||
Create and return a new object as a copy of the original object. All will
|
Create and return a new object as a copy of the original object. All will
|
||||||
be identical to the original except for the arguments given specifically
|
be identical to the original except for the arguments given specifically
|
||||||
to this method.
|
to this method.
|
||||||
|
|
||||||
original_object (obj) - the object to make a copy from
|
original_object (obj) - the object to make a copy from
|
||||||
new_key (str) - name the copy differently from the original.
|
new_key (str) - name the copy differently from the original.
|
||||||
new_location (obj) - if not None, change the location
|
new_location (obj) - if not None, change the location
|
||||||
new_home (obj) - if not None, change the Home
|
new_home (obj) - if not None, change the Home
|
||||||
new_aliases (list of strings) - if not None, change object aliases.
|
new_aliases (list of strings) - if not None, change object aliases.
|
||||||
|
|
@ -322,7 +331,7 @@ class ObjectManager(TypedObjectManager):
|
||||||
|
|
||||||
# get all the object's stats
|
# get all the object's stats
|
||||||
typeclass_path = original_object.typeclass_path
|
typeclass_path = original_object.typeclass_path
|
||||||
if not new_key:
|
if not new_key:
|
||||||
new_key = original_object.key
|
new_key = original_object.key
|
||||||
if not new_location:
|
if not new_location:
|
||||||
new_location = original_object.location
|
new_location = original_object.location
|
||||||
|
|
@ -331,36 +340,36 @@ class ObjectManager(TypedObjectManager):
|
||||||
if not new_player:
|
if not new_player:
|
||||||
new_player = original_object.player
|
new_player = original_object.player
|
||||||
if not new_aliases:
|
if not new_aliases:
|
||||||
new_aliases = original_object.aliases
|
new_aliases = original_object.aliases
|
||||||
if not new_locks:
|
if not new_locks:
|
||||||
new_locks = original_object.db_lock_storage
|
new_locks = original_object.db_lock_storage
|
||||||
if not new_permissions:
|
if not new_permissions:
|
||||||
new_permissions = original_object.permissions
|
new_permissions = original_object.permissions
|
||||||
if not new_destination:
|
if not new_destination:
|
||||||
new_destination = original_object.destination
|
new_destination = original_object.destination
|
||||||
|
|
||||||
# create new object
|
# create new object
|
||||||
from src.utils import create
|
from src.utils import create
|
||||||
from src.scripts.models import ScriptDB
|
from src.scripts.models import ScriptDB
|
||||||
new_object = create.create_object(typeclass_path, key=new_key, location=new_location,
|
new_object = create.create_object(typeclass_path, key=new_key, location=new_location,
|
||||||
home=new_home, player=new_player, permissions=new_permissions,
|
home=new_home, player=new_player, permissions=new_permissions,
|
||||||
locks=new_locks, aliases=new_aliases, destination=new_destination)
|
locks=new_locks, aliases=new_aliases, destination=new_destination)
|
||||||
if not new_object:
|
if not new_object:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# copy over all attributes from old to new.
|
# copy over all attributes from old to new.
|
||||||
for attr in original_object.get_all_attributes():
|
for attr in original_object.get_all_attributes():
|
||||||
new_object.set_attribute(attr.key, attr.value)
|
new_object.set_attribute(attr.key, attr.value)
|
||||||
|
|
||||||
# copy over all cmdsets, if any
|
# copy over all cmdsets, if any
|
||||||
for icmdset, cmdset in enumerate(original_object.cmdset.all()):
|
for icmdset, cmdset in enumerate(original_object.cmdset.all()):
|
||||||
if icmdset == 0:
|
if icmdset == 0:
|
||||||
new_object.cmdset.add_default(cmdset)
|
new_object.cmdset.add_default(cmdset)
|
||||||
else:
|
else:
|
||||||
new_object.cmdset.add(cmdset)
|
new_object.cmdset.add(cmdset)
|
||||||
|
|
||||||
# copy over all scripts, if any
|
# copy over all scripts, if any
|
||||||
for script in original_object.scripts.all():
|
for script in original_object.scripts.all():
|
||||||
ScriptDB.objects.copy_script(script, new_obj=new_object.dbobj)
|
ScriptDB.objects.copy_script(script, new_obj=new_object.dbobj)
|
||||||
|
|
||||||
return new_object
|
return new_object
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
"""
|
"""
|
||||||
This module defines the database models for all in-game objects, that
|
This module defines the database models for all in-game objects, that
|
||||||
is, all objects that has an actual existence in-game.
|
is, all objects that has an actual existence in-game.
|
||||||
|
|
||||||
Each database object is 'decorated' with a 'typeclass', a normal
|
Each database object is 'decorated' with a 'typeclass', a normal
|
||||||
python class that implements all the various logics needed by the game
|
python class that implements all the various logics needed by the game
|
||||||
|
|
@ -66,11 +66,11 @@ class Alias(SharedMemoryModel):
|
||||||
This model holds a range of alternate names for an object.
|
This model holds a range of alternate names for an object.
|
||||||
These are intrinsic properties of the object. The split
|
These are intrinsic properties of the object. The split
|
||||||
is so as to allow for effective global searches also by
|
is so as to allow for effective global searches also by
|
||||||
alias.
|
alias.
|
||||||
"""
|
"""
|
||||||
db_key = models.CharField('alias', max_length=255, db_index=True)
|
db_key = models.CharField('alias', max_length=255, db_index=True)
|
||||||
db_obj = models.ForeignKey("ObjectDB", verbose_name='object')
|
db_obj = models.ForeignKey("ObjectDB", verbose_name='object')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"Define Django meta options"
|
"Define Django meta options"
|
||||||
verbose_name = "Object alias"
|
verbose_name = "Object alias"
|
||||||
|
|
@ -79,8 +79,8 @@ class Alias(SharedMemoryModel):
|
||||||
return u"%s" % self.db_key
|
return u"%s" % self.db_key
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return str(self.db_key)
|
return str(self.db_key)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
#
|
#
|
||||||
|
|
@ -90,11 +90,11 @@ class Alias(SharedMemoryModel):
|
||||||
|
|
||||||
class ObjectNick(TypeNick):
|
class ObjectNick(TypeNick):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
The default nick types used by Evennia are:
|
The default nick types used by Evennia are:
|
||||||
inputline (default) - match against all input
|
inputline (default) - match against all input
|
||||||
player - match against player searches
|
player - match against player searches
|
||||||
obj - match against object searches
|
obj - match against object searches
|
||||||
channel - used to store own names for channels
|
channel - used to store own names for channels
|
||||||
"""
|
"""
|
||||||
db_obj = models.ForeignKey("ObjectDB", verbose_name='object')
|
db_obj = models.ForeignKey("ObjectDB", verbose_name='object')
|
||||||
|
|
@ -108,7 +108,7 @@ class ObjectNick(TypeNick):
|
||||||
class ObjectNickHandler(TypeNickHandler):
|
class ObjectNickHandler(TypeNickHandler):
|
||||||
"""
|
"""
|
||||||
Handles nick access and setting. Accessed through ObjectDB.nicks
|
Handles nick access and setting. Accessed through ObjectDB.nicks
|
||||||
"""
|
"""
|
||||||
NickClass = ObjectNick
|
NickClass = ObjectNick
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -126,7 +126,7 @@ class ObjectDB(TypedObject):
|
||||||
|
|
||||||
Note that the base objectdb is very simple, with
|
Note that the base objectdb is very simple, with
|
||||||
few defined fields. Use attributes to extend your
|
few defined fields. Use attributes to extend your
|
||||||
type class with new database-stored variables.
|
type class with new database-stored variables.
|
||||||
|
|
||||||
The TypedObject supplies the following (inherited) properties:
|
The TypedObject supplies the following (inherited) properties:
|
||||||
key - main name
|
key - main name
|
||||||
|
|
@ -134,11 +134,11 @@ class ObjectDB(TypedObject):
|
||||||
typeclass_path - the path to the decorating typeclass
|
typeclass_path - the path to the decorating typeclass
|
||||||
typeclass - auto-linked typeclass
|
typeclass - auto-linked typeclass
|
||||||
date_created - time stamp of object creation
|
date_created - time stamp of object creation
|
||||||
permissions - perm strings
|
permissions - perm strings
|
||||||
locks - lock definitions (handler)
|
locks - lock definitions (handler)
|
||||||
dbref - #id of object
|
dbref - #id of object
|
||||||
db - persistent attribute storage
|
db - persistent attribute storage
|
||||||
ndb - non-persistent attribute storage
|
ndb - non-persistent attribute storage
|
||||||
|
|
||||||
The ObjectDB adds the following properties:
|
The ObjectDB adds the following properties:
|
||||||
player - optional connected player
|
player - optional connected player
|
||||||
|
|
@ -148,7 +148,7 @@ class ObjectDB(TypedObject):
|
||||||
scripts - scripts assigned to object (handler from typeclass)
|
scripts - scripts assigned to object (handler from typeclass)
|
||||||
cmdset - active cmdset on object (handler from typeclass)
|
cmdset - active cmdset on object (handler from typeclass)
|
||||||
aliases - aliases for this object (property)
|
aliases - aliases for this object (property)
|
||||||
nicks - nicknames for *other* things in Evennia (handler)
|
nicks - nicknames for *other* things in Evennia (handler)
|
||||||
sessions - sessions connected to this object (see also player)
|
sessions - sessions connected to this object (see also player)
|
||||||
has_player - bool if an active player is currently connected
|
has_player - bool if an active player is currently connected
|
||||||
contents - other objects having this object as location
|
contents - other objects having this object as location
|
||||||
|
|
@ -160,7 +160,7 @@ class ObjectDB(TypedObject):
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# inherited fields (from TypedObject):
|
# inherited fields (from TypedObject):
|
||||||
# db_key (also 'name' works), db_typeclass_path, db_date_created,
|
# db_key (also 'name' works), db_typeclass_path, db_date_created,
|
||||||
# db_permissions
|
# db_permissions
|
||||||
#
|
#
|
||||||
# These databse fields (including the inherited ones) are all set
|
# These databse fields (including the inherited ones) are all set
|
||||||
|
|
@ -168,11 +168,11 @@ class ObjectDB(TypedObject):
|
||||||
# but withtout the db_* prefix.
|
# but withtout the db_* prefix.
|
||||||
|
|
||||||
# If this is a character object, the player is connected here.
|
# If this is a character object, the player is connected here.
|
||||||
db_player = models.ForeignKey("players.PlayerDB", blank=True, null=True, verbose_name='player',
|
db_player = models.ForeignKey("players.PlayerDB", blank=True, null=True, verbose_name='player',
|
||||||
help_text='a Player connected to this object, if any.')
|
help_text='a Player connected to this object, if any.')
|
||||||
# The location in the game world. Since this one is likely
|
# The location in the game world. Since this one is likely
|
||||||
# to change often, we set this with the 'location' property
|
# to change often, we set this with the 'location' property
|
||||||
# to transparently handle Typeclassing.
|
# to transparently handle Typeclassing.
|
||||||
db_location = models.ForeignKey('self', related_name="locations_set",db_index=True,
|
db_location = models.ForeignKey('self', related_name="locations_set",db_index=True,
|
||||||
blank=True, null=True, verbose_name='game location')
|
blank=True, null=True, verbose_name='game location')
|
||||||
# a safety location, this usually don't change much.
|
# a safety location, this usually don't change much.
|
||||||
|
|
@ -193,24 +193,24 @@ class ObjectDB(TypedObject):
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
"Parent must be initialized first."
|
"Parent must be initialized first."
|
||||||
TypedObject.__init__(self, *args, **kwargs)
|
TypedObject.__init__(self, *args, **kwargs)
|
||||||
# handlers
|
# handlers
|
||||||
self.cmdset = CmdSetHandler(self)
|
self.cmdset = CmdSetHandler(self)
|
||||||
self.cmdset.update(init_mode=True)
|
self.cmdset.update(init_mode=True)
|
||||||
self.scripts = ScriptHandler(self)
|
self.scripts = ScriptHandler(self)
|
||||||
self.nicks = ObjectNickHandler(self)
|
self.nicks = ObjectNickHandler(self)
|
||||||
# store the attribute class
|
# store the attribute class
|
||||||
|
|
||||||
# Wrapper properties to easily set database fields. These are
|
# Wrapper properties to easily set database fields. These are
|
||||||
# @property decorators that allows to access these fields using
|
# @property decorators that allows to access these fields using
|
||||||
# normal python operations (without having to remember to save()
|
# normal python operations (without having to remember to save()
|
||||||
# etc). So e.g. a property 'attr' has a get/set/del decorator
|
# etc). So e.g. a property 'attr' has a get/set/del decorator
|
||||||
# defined that allows the user to do self.attr = value,
|
# defined that allows the user to do self.attr = value,
|
||||||
# value = self.attr and del self.attr respectively (where self
|
# value = self.attr and del self.attr respectively (where self
|
||||||
# is the object in question).
|
# is the object in question).
|
||||||
|
|
||||||
# aliases property (wraps (db_aliases)
|
# aliases property (wraps (db_aliases)
|
||||||
#@property
|
#@property
|
||||||
def aliases_get(self):
|
def aliases_get(self):
|
||||||
"Getter. Allows for value = self.aliases"
|
"Getter. Allows for value = self.aliases"
|
||||||
try:
|
try:
|
||||||
|
|
@ -218,10 +218,10 @@ class ObjectDB(TypedObject):
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
aliases = list(Alias.objects.filter(db_obj=self).values_list("db_key", flat=True))
|
aliases = list(Alias.objects.filter(db_obj=self).values_list("db_key", flat=True))
|
||||||
SA(self, "_cached_aliases", aliases)
|
SA(self, "_cached_aliases", aliases)
|
||||||
return aliases
|
return aliases
|
||||||
#@aliases.setter
|
#@aliases.setter
|
||||||
def aliases_set(self, aliases):
|
def aliases_set(self, aliases):
|
||||||
"Setter. Allows for self.aliases = value"
|
"Setter. Allows for self.aliases = value"
|
||||||
for alias in make_iter(aliases):
|
for alias in make_iter(aliases):
|
||||||
new_alias = Alias(db_key=alias, db_obj=self)
|
new_alias = Alias(db_key=alias, db_obj=self)
|
||||||
new_alias.save()
|
new_alias.save()
|
||||||
|
|
@ -258,121 +258,121 @@ class ObjectDB(TypedObject):
|
||||||
player = property(player_get, player_set, player_del)
|
player = property(player_get, player_set, player_del)
|
||||||
|
|
||||||
# location property (wraps db_location)
|
# location property (wraps db_location)
|
||||||
#@property
|
#@property
|
||||||
def location_get(self):
|
def location_get(self):
|
||||||
"Getter. Allows for value = self.location."
|
"Getter. Allows for value = self.location."
|
||||||
loc = get_cache(self, "location")
|
loc = get_cache(self, "location")
|
||||||
if loc:
|
if loc:
|
||||||
return loc.typeclass
|
return loc.typeclass
|
||||||
return None
|
return None
|
||||||
#@location.setter
|
#@location.setter
|
||||||
def location_set(self, location):
|
def location_set(self, location):
|
||||||
"Setter. Allows for self.location = location"
|
"Setter. Allows for self.location = location"
|
||||||
try:
|
try:
|
||||||
if location == None or type(location) == ObjectDB:
|
if location == None or type(location) == ObjectDB:
|
||||||
# location is None or a valid object
|
# location is None or a valid object
|
||||||
loc = location
|
loc = location
|
||||||
elif ObjectDB.objects.dbref(location):
|
elif ObjectDB.objects.dbref(location):
|
||||||
# location is a dbref; search
|
# location is a dbref; search
|
||||||
loc = ObjectDB.objects.dbref_search(location)
|
loc = ObjectDB.objects.dbref_search(location)
|
||||||
if loc and hasattr(loc,'dbobj'):
|
if loc and hasattr(loc,'dbobj'):
|
||||||
loc = loc.dbobj
|
loc = loc.dbobj
|
||||||
else:
|
else:
|
||||||
loc = location.dbobj
|
loc = location.dbobj
|
||||||
else:
|
else:
|
||||||
loc = location.dbobj
|
loc = location.dbobj
|
||||||
set_cache(self, "location", loc)
|
set_cache(self, "location", loc)
|
||||||
except Exception:
|
except Exception:
|
||||||
string = "Cannot set location: "
|
string = "Cannot set location: "
|
||||||
string += "%s is not a valid location."
|
string += "%s is not a valid location."
|
||||||
self.msg(string % location)
|
self.msg(string % location)
|
||||||
logger.log_trace(string)
|
logger.log_trace(string)
|
||||||
raise
|
raise
|
||||||
#@location.deleter
|
#@location.deleter
|
||||||
def location_del(self):
|
def location_del(self):
|
||||||
"Deleter. Allows for del self.location"
|
"Deleter. Allows for del self.location"
|
||||||
self.db_location = None
|
self.db_location = None
|
||||||
self.save()
|
self.save()
|
||||||
del_cache()
|
del_cache()
|
||||||
location = property(location_get, location_set, location_del)
|
location = property(location_get, location_set, location_del)
|
||||||
|
|
||||||
# home property (wraps db_home)
|
# home property (wraps db_home)
|
||||||
#@property
|
#@property
|
||||||
def home_get(self):
|
def home_get(self):
|
||||||
"Getter. Allows for value = self.home"
|
"Getter. Allows for value = self.home"
|
||||||
home = get_cache(self, "home")
|
home = get_cache(self, "home")
|
||||||
if home:
|
if home:
|
||||||
return home.typeclass
|
return home.typeclass
|
||||||
return None
|
return None
|
||||||
#@home.setter
|
#@home.setter
|
||||||
def home_set(self, home):
|
def home_set(self, home):
|
||||||
"Setter. Allows for self.home = value"
|
"Setter. Allows for self.home = value"
|
||||||
try:
|
try:
|
||||||
if home == None or type(home) == ObjectDB:
|
if home == None or type(home) == ObjectDB:
|
||||||
hom = home
|
hom = home
|
||||||
elif ObjectDB.objects.dbref(home):
|
elif ObjectDB.objects.dbref(home):
|
||||||
hom = ObjectDB.objects.dbref_search(home)
|
hom = ObjectDB.objects.dbref_search(home)
|
||||||
if hom and hasattr(hom,'dbobj'):
|
if hom and hasattr(hom,'dbobj'):
|
||||||
hom = hom.dbobj
|
hom = hom.dbobj
|
||||||
else:
|
else:
|
||||||
hom = home.dbobj
|
hom = home.dbobj
|
||||||
else:
|
else:
|
||||||
hom = home.dbobj
|
hom = home.dbobj
|
||||||
set_cache(self, "home", hom)
|
set_cache(self, "home", hom)
|
||||||
except Exception:
|
except Exception:
|
||||||
string = "Cannot set home: "
|
string = "Cannot set home: "
|
||||||
string += "%s is not a valid home."
|
string += "%s is not a valid home."
|
||||||
self.msg(string % home)
|
self.msg(string % home)
|
||||||
logger.log_trace(string)
|
logger.log_trace(string)
|
||||||
#raise
|
#raise
|
||||||
#@home.deleter
|
#@home.deleter
|
||||||
def home_del(self):
|
def home_del(self):
|
||||||
"Deleter. Allows for del self.home."
|
"Deleter. Allows for del self.home."
|
||||||
self.db_home = None
|
self.db_home = None
|
||||||
self.save()
|
self.save()
|
||||||
del_cache(self, "home")
|
del_cache(self, "home")
|
||||||
home = property(home_get, home_set, home_del)
|
home = property(home_get, home_set, home_del)
|
||||||
|
|
||||||
# destination property (wraps db_destination)
|
# destination property (wraps db_destination)
|
||||||
#@property
|
#@property
|
||||||
def destination_get(self):
|
def destination_get(self):
|
||||||
"Getter. Allows for value = self.destination."
|
"Getter. Allows for value = self.destination."
|
||||||
dest = get_cache(self, "destination")
|
dest = get_cache(self, "destination")
|
||||||
if dest:
|
if dest:
|
||||||
return dest.typeclass
|
return dest.typeclass
|
||||||
return None
|
return None
|
||||||
#@destination.setter
|
#@destination.setter
|
||||||
def destination_set(self, destination):
|
def destination_set(self, destination):
|
||||||
"Setter. Allows for self.destination = destination"
|
"Setter. Allows for self.destination = destination"
|
||||||
try:
|
try:
|
||||||
if destination == None or type(destination) == ObjectDB:
|
if destination == None or type(destination) == ObjectDB:
|
||||||
# destination is None or a valid object
|
# destination is None or a valid object
|
||||||
dest = destination
|
dest = destination
|
||||||
elif ObjectDB.objects.dbref(destination):
|
elif ObjectDB.objects.dbref(destination):
|
||||||
# destination is a dbref; search
|
# destination is a dbref; search
|
||||||
dest = ObjectDB.objects.dbref_search(destination)
|
dest = ObjectDB.objects.dbref_search(destination)
|
||||||
if dest and hasattr(dest,'dbobj'):
|
if dest and hasattr(dest,'dbobj'):
|
||||||
dest = dest.dbobj
|
dest = dest.dbobj
|
||||||
else:
|
else:
|
||||||
dest = destination.dbobj
|
dest = destination.dbobj
|
||||||
else:
|
else:
|
||||||
dest = destination.dbobj
|
dest = destination.dbobj
|
||||||
set_cache(self, "destination", dest)
|
set_cache(self, "destination", dest)
|
||||||
except Exception:
|
except Exception:
|
||||||
string = "Cannot set destination: "
|
string = "Cannot set destination: "
|
||||||
string += "%s is not a valid destination."
|
string += "%s is not a valid destination."
|
||||||
self.msg(string % destination)
|
self.msg(string % destination)
|
||||||
logger.log_trace(string)
|
logger.log_trace(string)
|
||||||
raise
|
raise
|
||||||
#@destination.deleter
|
#@destination.deleter
|
||||||
def destination_del(self):
|
def destination_del(self):
|
||||||
"Deleter. Allows for del self.destination"
|
"Deleter. Allows for del self.destination"
|
||||||
self.db_destination = None
|
self.db_destination = None
|
||||||
self.save()
|
self.save()
|
||||||
del_cache(self, "destination")
|
del_cache(self, "destination")
|
||||||
destination = property(destination_get, destination_set, destination_del)
|
destination = property(destination_get, destination_set, destination_del)
|
||||||
|
|
||||||
# cmdset_storage property.
|
# cmdset_storage property.
|
||||||
# This seems very sensitive to caching, so leaving it be for now. /Griatch
|
# This seems very sensitive to caching, so leaving it be for now. /Griatch
|
||||||
#@property
|
#@property
|
||||||
def cmdset_storage_get(self):
|
def cmdset_storage_get(self):
|
||||||
|
|
@ -385,7 +385,7 @@ class ObjectDB(TypedObject):
|
||||||
"Setter. Allows for self.name = value. Stores as a comma-separated string."
|
"Setter. Allows for self.name = value. Stores as a comma-separated string."
|
||||||
value = ",".join(str(val).strip() for val in make_iter(value))
|
value = ",".join(str(val).strip() for val in make_iter(value))
|
||||||
self.db_cmdset_storage = value
|
self.db_cmdset_storage = value
|
||||||
self.save()
|
self.save()
|
||||||
#@cmdset_storage.deleter
|
#@cmdset_storage.deleter
|
||||||
def cmdset_storage_del(self):
|
def cmdset_storage_del(self):
|
||||||
"Deleter. Allows for del self.name"
|
"Deleter. Allows for del self.name"
|
||||||
|
|
@ -400,12 +400,12 @@ class ObjectDB(TypedObject):
|
||||||
|
|
||||||
#
|
#
|
||||||
# ObjectDB class access methods/properties
|
# ObjectDB class access methods/properties
|
||||||
#
|
#
|
||||||
|
|
||||||
# this is required to properly handle attributes and typeclass loading.
|
# this is required to properly handle attributes and typeclass loading.
|
||||||
#attribute_model_path = "src.objects.models"
|
#attribute_model_path = "src.objects.models"
|
||||||
#attribute_model_name = "ObjAttribute"
|
#attribute_model_name = "ObjAttribute"
|
||||||
typeclass_paths = settings.OBJECT_TYPECLASS_PATHS
|
typeclass_paths = settings.OBJECT_TYPECLASS_PATHS
|
||||||
attribute_class = ObjAttribute
|
attribute_class = ObjAttribute
|
||||||
db_model_name = "objectdb" # used by attributes to safely store objects
|
db_model_name = "objectdb" # used by attributes to safely store objects
|
||||||
|
|
||||||
|
|
@ -420,13 +420,13 @@ class ObjectDB(TypedObject):
|
||||||
"""
|
"""
|
||||||
Retrieve sessions connected to this object.
|
Retrieve sessions connected to this object.
|
||||||
"""
|
"""
|
||||||
# if the player is not connected, this will simply be an empty list.
|
# if the player is not connected, this will simply be an empty list.
|
||||||
if self.player:
|
if self.player:
|
||||||
return self.player.sessions
|
return self.player.sessions
|
||||||
return []
|
return []
|
||||||
sessions = property(sessions_get)
|
sessions = property(sessions_get)
|
||||||
|
|
||||||
#@property
|
#@property
|
||||||
def has_player_get(self):
|
def has_player_get(self):
|
||||||
"""
|
"""
|
||||||
Convenience function for checking if an active player is
|
Convenience function for checking if an active player is
|
||||||
|
|
@ -436,13 +436,13 @@ class ObjectDB(TypedObject):
|
||||||
has_player = property(has_player_get)
|
has_player = property(has_player_get)
|
||||||
is_player = property(has_player_get)
|
is_player = property(has_player_get)
|
||||||
|
|
||||||
#@property
|
#@property
|
||||||
def is_superuser_get(self):
|
def is_superuser_get(self):
|
||||||
"Check if user has a player, and if so, if it is a superuser."
|
"Check if user has a player, and if so, if it is a superuser."
|
||||||
return any(self.sessions) and self.player.is_superuser
|
return any(self.sessions) and self.player.is_superuser
|
||||||
is_superuser = property(is_superuser_get)
|
is_superuser = property(is_superuser_get)
|
||||||
|
|
||||||
#@property
|
#@property
|
||||||
def contents_get(self, exclude=None):
|
def contents_get(self, exclude=None):
|
||||||
"""
|
"""
|
||||||
Returns the contents of this object, i.e. all
|
Returns the contents of this object, i.e. all
|
||||||
|
|
@ -461,11 +461,11 @@ class ObjectDB(TypedObject):
|
||||||
if exi.destination]
|
if exi.destination]
|
||||||
exits = property(exits_get)
|
exits = property(exits_get)
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Main Search method
|
# Main Search method
|
||||||
#
|
#
|
||||||
|
|
||||||
def search(self, ostring,
|
def search(self, ostring,
|
||||||
global_search=False,
|
global_search=False,
|
||||||
attribute_name=None,
|
attribute_name=None,
|
||||||
|
|
@ -474,49 +474,49 @@ class ObjectDB(TypedObject):
|
||||||
"""
|
"""
|
||||||
Perform a standard object search in the database, handling
|
Perform a standard object search in the database, handling
|
||||||
multiple results and lack thereof gracefully.
|
multiple results and lack thereof gracefully.
|
||||||
|
|
||||||
ostring: (str) The string to match object names against.
|
ostring: (str) The string to match object names against.
|
||||||
Obs - To find a player, append * to the
|
Obs - To find a player, append * to the
|
||||||
start of ostring.
|
start of ostring.
|
||||||
global_search: Search all objects, not just the current
|
global_search: Search all objects, not just the current
|
||||||
location/inventory
|
location/inventory
|
||||||
attribute_name: (string) Which attribute to match
|
attribute_name: (string) Which attribute to match
|
||||||
(if None, uses default 'name')
|
(if None, uses default 'name')
|
||||||
use_nicks : Use nickname replace (off by default)
|
use_nicks : Use nickname replace (off by default)
|
||||||
location : If None, use caller's current location
|
location : If None, use caller's current location
|
||||||
ignore_errors : Don't display any error messages even
|
ignore_errors : Don't display any error messages even
|
||||||
if there are none/multiple matches -
|
if there are none/multiple matches -
|
||||||
just return the result as a list.
|
just return the result as a list.
|
||||||
player : Don't search for an Object but a Player.
|
player : Don't search for an Object but a Player.
|
||||||
This will also find players that don't
|
This will also find players that don't
|
||||||
currently have a character.
|
currently have a character.
|
||||||
|
|
||||||
Returns - a unique Object/Player match or None. All error
|
Returns - a unique Object/Player match or None. All error
|
||||||
messages are handled by system-commands and the parser-handlers
|
messages are handled by system-commands and the parser-handlers
|
||||||
specified in settings.
|
specified in settings.
|
||||||
|
|
||||||
Use *<string> to search for objects controlled by a specific
|
Use *<string> to search for objects controlled by a specific
|
||||||
player. Note that the object controlled by the player will be
|
player. Note that the object controlled by the player will be
|
||||||
returned, not the player object itself. This also means that
|
returned, not the player object itself. This also means that
|
||||||
this will not find Players without a character. Use the keyword
|
this will not find Players without a character. Use the keyword
|
||||||
player=True to find player objects.
|
player=True to find player objects.
|
||||||
|
|
||||||
Note - for multiple matches, the engine accepts a number
|
Note - for multiple matches, the engine accepts a number
|
||||||
linked to the key in order to separate the matches from
|
linked to the key in order to separate the matches from
|
||||||
each other without showing the dbref explicitly. Default
|
each other without showing the dbref explicitly. Default
|
||||||
syntax for this is 'N-searchword'. So for example, if there
|
syntax for this is 'N-searchword'. So for example, if there
|
||||||
are three objects in the room all named 'ball', you could
|
are three objects in the room all named 'ball', you could
|
||||||
address the individual ball as '1-ball', '2-ball', '3-ball'
|
address the individual ball as '1-ball', '2-ball', '3-ball'
|
||||||
etc.
|
etc.
|
||||||
"""
|
"""
|
||||||
if use_nicks:
|
if use_nicks:
|
||||||
if ostring.startswith('*') or player:
|
if ostring.startswith('*') or player:
|
||||||
# player nick replace
|
# player nick replace
|
||||||
ostring = self.nicks.get(ostring.lstrip('*'), nick_type="player")
|
ostring = self.nicks.get(ostring.lstrip('*'), nick_type="player")
|
||||||
if not player:
|
if not player:
|
||||||
ostring = "*%s" % ostring
|
ostring = "*%s" % ostring
|
||||||
else:
|
else:
|
||||||
# object nick replace
|
# object nick replace
|
||||||
ostring = self.nicks.get(ostring, nick_type="object")
|
ostring = self.nicks.get(ostring, nick_type="object")
|
||||||
|
|
||||||
if player:
|
if player:
|
||||||
|
|
@ -525,11 +525,11 @@ class ObjectDB(TypedObject):
|
||||||
else:
|
else:
|
||||||
results = PlayerDB.objects.player_search(ostring.lstrip('*'))
|
results = PlayerDB.objects.player_search(ostring.lstrip('*'))
|
||||||
else:
|
else:
|
||||||
results = ObjectDB.objects.object_search(ostring, caller=self,
|
results = ObjectDB.objects.object_search(ostring, caller=self,
|
||||||
global_search=global_search,
|
global_search=global_search,
|
||||||
attribute_name=attribute_name,
|
attribute_name=attribute_name,
|
||||||
location=location)
|
location=location)
|
||||||
|
|
||||||
if ignore_errors:
|
if ignore_errors:
|
||||||
return results
|
return results
|
||||||
# this import is cache after the first call.
|
# this import is cache after the first call.
|
||||||
|
|
@ -538,50 +538,50 @@ class ObjectDB(TypedObject):
|
||||||
#
|
#
|
||||||
# Execution/action methods
|
# Execution/action methods
|
||||||
#
|
#
|
||||||
|
|
||||||
def execute_cmd(self, raw_string):
|
def execute_cmd(self, raw_string):
|
||||||
"""
|
"""
|
||||||
Do something as this object. This command transparently
|
Do something as this object. This command transparently
|
||||||
lets its typeclass execute the command. Evennia also calls
|
lets its typeclass execute the command. Evennia also calls
|
||||||
this method whenever the player sends a command on the command line.
|
this method whenever the player sends a command on the command line.
|
||||||
|
|
||||||
Argument:
|
Argument:
|
||||||
raw_string (string) - raw command input
|
raw_string (string) - raw command input
|
||||||
|
|
||||||
Returns Deferred - this is an asynchronous Twisted object that will
|
Returns Deferred - this is an asynchronous Twisted object that will
|
||||||
not fire until the command has actually finished executing. To overload
|
not fire until the command has actually finished executing. To overload
|
||||||
this one needs to attach callback functions to it, with addCallback(function).
|
this one needs to attach callback functions to it, with addCallback(function).
|
||||||
This function will be called with an eventual return value from the command
|
This function will be called with an eventual return value from the command
|
||||||
execution.
|
execution.
|
||||||
|
|
||||||
This return is not used at all by Evennia by default, but might be useful
|
This return is not used at all by Evennia by default, but might be useful
|
||||||
for coders intending to implement some sort of nested command structure.
|
for coders intending to implement some sort of nested command structure.
|
||||||
"""
|
"""
|
||||||
# nick replacement - we require full-word matching.
|
# nick replacement - we require full-word matching.
|
||||||
|
|
||||||
# do text encoding conversion
|
# do text encoding conversion
|
||||||
raw_string = to_unicode(raw_string)
|
raw_string = to_unicode(raw_string)
|
||||||
|
|
||||||
raw_list = raw_string.split(None)
|
raw_list = raw_string.split(None)
|
||||||
raw_list = [" ".join(raw_list[:i+1]) for i in range(len(raw_list)) if raw_list[:i+1]]
|
raw_list = [" ".join(raw_list[:i+1]) for i in range(len(raw_list)) if raw_list[:i+1]]
|
||||||
for nick in ObjectNick.objects.filter(db_obj=self, db_type__in=("inputline","channel")):
|
for nick in ObjectNick.objects.filter(db_obj=self, db_type__in=("inputline","channel")):
|
||||||
if nick.db_nick in raw_list:
|
if nick.db_nick in raw_list:
|
||||||
raw_string = raw_string.replace(nick.db_nick, nick.db_real, 1)
|
raw_string = raw_string.replace(nick.db_nick, nick.db_real, 1)
|
||||||
break
|
break
|
||||||
return cmdhandler.cmdhandler(self.typeclass, raw_string)
|
return cmdhandler.cmdhandler(self.typeclass, raw_string)
|
||||||
|
|
||||||
def msg(self, message, from_obj=None, data=None):
|
def msg(self, message, from_obj=None, data=None):
|
||||||
"""
|
"""
|
||||||
Emits something to any sessions attached to the object.
|
Emits something to any sessions attached to the object.
|
||||||
|
|
||||||
message (str): The message to send
|
message (str): The message to send
|
||||||
from_obj (obj): object that is sending.
|
from_obj (obj): object that is sending.
|
||||||
data (object): an optional data object that may or may not
|
data (object): an optional data object that may or may not
|
||||||
be used by the protocol.
|
be used by the protocol.
|
||||||
"""
|
"""
|
||||||
# This is an important function that must always work.
|
# This is an important function that must always work.
|
||||||
# we use a different __getattribute__ to avoid recursive loops.
|
# we use a different __getattribute__ to avoid recursive loops.
|
||||||
|
|
||||||
if object.__getattribute__(self, 'player'):
|
if object.__getattribute__(self, 'player'):
|
||||||
object.__getattribute__(self, 'player').msg(message, from_obj=from_obj, data=data)
|
object.__getattribute__(self, 'player').msg(message, from_obj=from_obj, data=data)
|
||||||
|
|
||||||
|
|
@ -589,7 +589,7 @@ class ObjectDB(TypedObject):
|
||||||
"Deprecated. Alias for msg"
|
"Deprecated. Alias for msg"
|
||||||
logger.log_depmsg("emit_to() is deprecated. Use msg() instead.")
|
logger.log_depmsg("emit_to() is deprecated. Use msg() instead.")
|
||||||
self.msg(message, from_obj, data)
|
self.msg(message, from_obj, data)
|
||||||
|
|
||||||
def msg_contents(self, message, exclude=None, from_obj=None, data=None):
|
def msg_contents(self, message, exclude=None, from_obj=None, data=None):
|
||||||
"""
|
"""
|
||||||
Emits something to all objects inside an object.
|
Emits something to all objects inside an object.
|
||||||
|
|
@ -608,7 +608,7 @@ class ObjectDB(TypedObject):
|
||||||
"Deprecated. Alias for msg_contents"
|
"Deprecated. Alias for msg_contents"
|
||||||
logger.log_depmsg("emit_to_contents() is deprecated. Use msg_contents() instead.")
|
logger.log_depmsg("emit_to_contents() is deprecated. Use msg_contents() instead.")
|
||||||
self.msg_contents(message, exclude=exclude, from_obj=from_obj, data=data)
|
self.msg_contents(message, exclude=exclude, from_obj=from_obj, data=data)
|
||||||
|
|
||||||
def move_to(self, destination, quiet=False,
|
def move_to(self, destination, quiet=False,
|
||||||
emit_to_obj=None, use_destination=True):
|
emit_to_obj=None, use_destination=True):
|
||||||
"""
|
"""
|
||||||
|
|
@ -618,19 +618,19 @@ class ObjectDB(TypedObject):
|
||||||
exit object (i.e. it has "destination"!=None), the move_to will
|
exit object (i.e. it has "destination"!=None), the move_to will
|
||||||
happen to this destination and -not- into the exit object itself, unless
|
happen to this destination and -not- into the exit object itself, unless
|
||||||
use_destination=False. Note that no lock checks are done by this function,
|
use_destination=False. Note that no lock checks are done by this function,
|
||||||
such things are assumed to have been handled before calling move_to.
|
such things are assumed to have been handled before calling move_to.
|
||||||
|
|
||||||
destination: (Object) Reference to the object to move to. This
|
destination: (Object) Reference to the object to move to. This
|
||||||
can also be an exit object, in which case the destination
|
can also be an exit object, in which case the destination
|
||||||
property is used as destination.
|
property is used as destination.
|
||||||
quiet: (bool) If true, don't emit left/arrived messages.
|
quiet: (bool) If true, don't emit left/arrived messages.
|
||||||
emit_to_obj: (Object) object to receive error messages
|
emit_to_obj: (Object) object to receive error messages
|
||||||
use_destination (bool): Default is for objects to use the "destination" property
|
use_destination (bool): Default is for objects to use the "destination" property
|
||||||
of destinations as the target to move to. Turning off this
|
of destinations as the target to move to. Turning off this
|
||||||
keyword allows objects to move "inside" exit objects.
|
keyword allows objects to move "inside" exit objects.
|
||||||
|
|
||||||
Returns True/False depending on if there were problems with the move. This method
|
Returns True/False depending on if there were problems with the move. This method
|
||||||
may also return various error messages to the emit_to_obj.
|
may also return various error messages to the emit_to_obj.
|
||||||
"""
|
"""
|
||||||
def logerr(string=""):
|
def logerr(string=""):
|
||||||
trc = traceback.format_exc()
|
trc = traceback.format_exc()
|
||||||
|
|
@ -644,7 +644,7 @@ class ObjectDB(TypedObject):
|
||||||
|
|
||||||
if not destination:
|
if not destination:
|
||||||
emit_to_obj.msg("The destination doesn't exist.")
|
emit_to_obj.msg("The destination doesn't exist.")
|
||||||
return
|
return
|
||||||
if destination.destination:
|
if destination.destination:
|
||||||
# traverse exits
|
# traverse exits
|
||||||
destination = destination.destination
|
destination = destination.destination
|
||||||
|
|
@ -658,8 +658,8 @@ class ObjectDB(TypedObject):
|
||||||
#emit_to_obj.msg(errtxt % "at_before_move()")
|
#emit_to_obj.msg(errtxt % "at_before_move()")
|
||||||
#logger.log_trace()
|
#logger.log_trace()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Save the old location
|
# Save the old location
|
||||||
source_location = self.location
|
source_location = self.location
|
||||||
if not source_location:
|
if not source_location:
|
||||||
# there was some error in placing this room.
|
# there was some error in placing this room.
|
||||||
|
|
@ -678,16 +678,16 @@ class ObjectDB(TypedObject):
|
||||||
#emit_to_obj.msg(errtxt % "at_object_leave()")
|
#emit_to_obj.msg(errtxt % "at_object_leave()")
|
||||||
#logger.log_trace()
|
#logger.log_trace()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if not quiet:
|
if not quiet:
|
||||||
#tell the old room we are leaving
|
#tell the old room we are leaving
|
||||||
try:
|
try:
|
||||||
self.announce_move_from(destination)
|
self.announce_move_from(destination)
|
||||||
except Exception:
|
except Exception:
|
||||||
logerr(errtxt % "at_announce_move()")
|
logerr(errtxt % "at_announce_move()")
|
||||||
#emit_to_obj.msg(errtxt % "at_announce_move()" )
|
#emit_to_obj.msg(errtxt % "at_announce_move()" )
|
||||||
#logger.log_trace()
|
#logger.log_trace()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Perform move
|
# Perform move
|
||||||
try:
|
try:
|
||||||
|
|
@ -695,18 +695,18 @@ class ObjectDB(TypedObject):
|
||||||
except Exception:
|
except Exception:
|
||||||
emit_to_obj.msg(errtxt % "location change")
|
emit_to_obj.msg(errtxt % "location change")
|
||||||
logger.log_trace()
|
logger.log_trace()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if not quiet:
|
if not quiet:
|
||||||
# Tell the new room we are there.
|
# Tell the new room we are there.
|
||||||
try:
|
try:
|
||||||
self.announce_move_to(source_location)
|
self.announce_move_to(source_location)
|
||||||
except Exception:
|
except Exception:
|
||||||
logerr(errtxt % "announce_move_to()")
|
logerr(errtxt % "announce_move_to()")
|
||||||
#emit_to_obj.msg(errtxt % "announce_move_to()")
|
#emit_to_obj.msg(errtxt % "announce_move_to()")
|
||||||
#logger.log_trace()
|
#logger.log_trace()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Perform eventual extra commands on the receiving location
|
# Perform eventual extra commands on the receiving location
|
||||||
# (the object has already arrived at this point)
|
# (the object has already arrived at this point)
|
||||||
try:
|
try:
|
||||||
|
|
@ -715,7 +715,7 @@ class ObjectDB(TypedObject):
|
||||||
logerr(errtxt % "at_object_receive()")
|
logerr(errtxt % "at_object_receive()")
|
||||||
#emit_to_obj.msg(errtxt % "at_object_receive()")
|
#emit_to_obj.msg(errtxt % "at_object_receive()")
|
||||||
#logger.log_trace()
|
#logger.log_trace()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Execute eventual extra commands on this object after moving it
|
# Execute eventual extra commands on this object after moving it
|
||||||
# (usually calling 'look')
|
# (usually calling 'look')
|
||||||
|
|
@ -725,13 +725,13 @@ class ObjectDB(TypedObject):
|
||||||
logerr(errtxt % "at_after_move")
|
logerr(errtxt % "at_after_move")
|
||||||
#emit_to_obj.msg(errtxt % "at_after_move()")
|
#emit_to_obj.msg(errtxt % "at_after_move()")
|
||||||
#logger.log_trace()
|
#logger.log_trace()
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
#
|
#
|
||||||
# Object Swap, Delete and Cleanup methods
|
# Object Swap, Delete and Cleanup methods
|
||||||
#
|
#
|
||||||
|
|
||||||
def clear_exits(self):
|
def clear_exits(self):
|
||||||
"""
|
"""
|
||||||
Destroys all of the exits and any exits pointing to this
|
Destroys all of the exits and any exits pointing to this
|
||||||
|
|
@ -745,7 +745,7 @@ class ObjectDB(TypedObject):
|
||||||
def clear_contents(self):
|
def clear_contents(self):
|
||||||
"""
|
"""
|
||||||
Moves all objects (players/things) to their home
|
Moves all objects (players/things) to their home
|
||||||
location or to default home.
|
location or to default home.
|
||||||
"""
|
"""
|
||||||
# Gather up everything that thinks this is its location.
|
# Gather up everything that thinks this is its location.
|
||||||
objs = ObjectDB.objects.filter(db_location=self)
|
objs = ObjectDB.objects.filter(db_location=self)
|
||||||
|
|
@ -754,29 +754,29 @@ class ObjectDB(TypedObject):
|
||||||
default_home = ObjectDB.objects.get(id=default_home_id)
|
default_home = ObjectDB.objects.get(id=default_home_id)
|
||||||
if default_home.id == self.id:
|
if default_home.id == self.id:
|
||||||
# we are deleting default home!
|
# we are deleting default home!
|
||||||
default_home = None
|
default_home = None
|
||||||
except Exception:
|
except Exception:
|
||||||
string = "Could not find default home '(#%d)'."
|
string = "Could not find default home '(#%d)'."
|
||||||
logger.log_errmsg(string % default_home_id)
|
logger.log_errmsg(string % default_home_id)
|
||||||
default_home = None
|
default_home = None
|
||||||
|
|
||||||
for obj in objs:
|
for obj in objs:
|
||||||
home = obj.home
|
home = obj.home
|
||||||
# Obviously, we can't send it back to here.
|
# Obviously, we can't send it back to here.
|
||||||
if not home or (home and home.id == self.id):
|
if not home or (home and home.id == self.id):
|
||||||
obj.home = default_home
|
obj.home = default_home
|
||||||
|
|
||||||
# If for some reason it's still None...
|
# If for some reason it's still None...
|
||||||
if not obj.home:
|
if not obj.home:
|
||||||
string = "Missing default home, '%s(#%d)' "
|
string = "Missing default home, '%s(#%d)' "
|
||||||
string += "now has a null location."
|
string += "now has a null location."
|
||||||
obj.location = None
|
obj.location = None
|
||||||
obj.msg("Something went wrong! You are dumped into nowhere. Contact an admin.")
|
obj.msg("Something went wrong! You are dumped into nowhere. Contact an admin.")
|
||||||
logger.log_errmsg(string % (obj.name, obj.id))
|
logger.log_errmsg(string % (obj.name, obj.id))
|
||||||
return
|
return
|
||||||
|
|
||||||
if obj.has_player:
|
if obj.has_player:
|
||||||
if home:
|
if home:
|
||||||
string = "Your current location has ceased to exist,"
|
string = "Your current location has ceased to exist,"
|
||||||
string += " moving you to %s(#%d)."
|
string += " moving you to %s(#%d)."
|
||||||
obj.msg(string % (home.name, home.id))
|
obj.msg(string % (home.name, home.id))
|
||||||
|
|
@ -787,22 +787,22 @@ class ObjectDB(TypedObject):
|
||||||
obj.move_to(home)
|
obj.move_to(home)
|
||||||
|
|
||||||
def copy(self, new_key=None):
|
def copy(self, new_key=None):
|
||||||
"""
|
"""
|
||||||
Makes an identical copy of this object. If you want to customize the copy by
|
Makes an identical copy of this object. If you want to customize the copy by
|
||||||
changing some settings, use ObjectDB.object.copy_object() directly.
|
changing some settings, use ObjectDB.object.copy_object() directly.
|
||||||
|
|
||||||
new_key (string) - new key/name of copied object. If new_key is not specified, the copy will be named
|
new_key (string) - new key/name of copied object. If new_key is not specified, the copy will be named
|
||||||
<old_key>_copy by default.
|
<old_key>_copy by default.
|
||||||
Returns: Object (copy of this one)
|
Returns: Object (copy of this one)
|
||||||
"""
|
"""
|
||||||
if not new_key:
|
if not new_key:
|
||||||
new_key = "%s_copy" % self.key
|
new_key = "%s_copy" % self.key
|
||||||
return ObjectDB.objects.copy_object(self, new_key=new_key)
|
return ObjectDB.objects.copy_object(self, new_key=new_key)
|
||||||
|
|
||||||
delete_iter = 0
|
delete_iter = 0
|
||||||
def delete(self):
|
def delete(self):
|
||||||
"""
|
"""
|
||||||
Deletes this object.
|
Deletes this object.
|
||||||
Before deletion, this method makes sure to move all contained
|
Before deletion, this method makes sure to move all contained
|
||||||
objects to their respective home locations, as well as clean
|
objects to their respective home locations, as well as clean
|
||||||
up all exits to/from the object.
|
up all exits to/from the object.
|
||||||
|
|
@ -815,7 +815,7 @@ class ObjectDB(TypedObject):
|
||||||
if not self.at_object_delete():
|
if not self.at_object_delete():
|
||||||
# this is an extra pre-check
|
# this is an extra pre-check
|
||||||
# run before deletion mechanism
|
# run before deletion mechanism
|
||||||
# is kicked into gear.
|
# is kicked into gear.
|
||||||
self.delete_iter == 0
|
self.delete_iter == 0
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
@ -825,17 +825,17 @@ class ObjectDB(TypedObject):
|
||||||
|
|
||||||
for session in self.sessions:
|
for session in self.sessions:
|
||||||
session.msg("Your character %s has been destroyed." % self.name)
|
session.msg("Your character %s has been destroyed." % self.name)
|
||||||
# no need to disconnect, Player just jumps to OOC mode.
|
# no need to disconnect, Player just jumps to OOC mode.
|
||||||
# sever the connection (important!)
|
# sever the connection (important!)
|
||||||
if object.__getattribute__(self, 'player') and self.player:
|
if object.__getattribute__(self, 'player') and self.player:
|
||||||
self.player.character = None
|
self.player.character = None
|
||||||
self.player = None
|
self.player = None
|
||||||
|
|
||||||
for script in self.scripts.all():
|
for script in self.scripts.all():
|
||||||
script.stop()
|
script.stop()
|
||||||
|
|
||||||
# if self.player:
|
# if self.player:
|
||||||
# self.player.user.is_active = False
|
# self.player.user.is_active = False
|
||||||
# self.player.user.save(
|
# self.player.user.save(
|
||||||
|
|
||||||
# Destroy any exits to and from this room, if any
|
# Destroy any exits to and from this room, if any
|
||||||
|
|
@ -844,4 +844,4 @@ class ObjectDB(TypedObject):
|
||||||
self.clear_contents()
|
self.clear_contents()
|
||||||
# Perform the deletion of the object
|
# Perform the deletion of the object
|
||||||
super(ObjectDB, self).delete()
|
super(ObjectDB, self).delete()
|
||||||
return True
|
return True
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
"""
|
"""
|
||||||
This is the basis of the typeclass system.
|
This is the basis of the typeclass system.
|
||||||
|
|
||||||
The idea is have the object as a normal class with the
|
The idea is have the object as a normal class with the
|
||||||
database-connection tied to itself through a property.
|
database-connection tied to itself through a property.
|
||||||
|
|
@ -19,15 +19,15 @@ from src.typeclasses.typeclass import TypeClass
|
||||||
from src.commands import cmdset, command
|
from src.commands import cmdset, command
|
||||||
|
|
||||||
#
|
#
|
||||||
# Base class to inherit from.
|
# Base class to inherit from.
|
||||||
#
|
#
|
||||||
|
|
||||||
class Object(TypeClass):
|
class Object(TypeClass):
|
||||||
"""
|
"""
|
||||||
This is the base class for all in-game objects.
|
This is the base class for all in-game objects.
|
||||||
Inherit from this to create different types of
|
Inherit from this to create different types of
|
||||||
objects in the game.
|
objects in the game.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, dbobj):
|
def __init__(self, dbobj):
|
||||||
"""
|
"""
|
||||||
|
|
@ -39,18 +39,18 @@ class Object(TypeClass):
|
||||||
seen in src.object.objects).
|
seen in src.object.objects).
|
||||||
|
|
||||||
|
|
||||||
Object Typeclass API:
|
Object Typeclass API:
|
||||||
|
|
||||||
* Available properties (only available on initiated typeclass objects)
|
* Available properties (only available on initiated typeclass objects)
|
||||||
|
|
||||||
key (string) - name of object
|
key (string) - name of object
|
||||||
name (string)- same as key
|
name (string)- same as key
|
||||||
aliases (list of strings) - aliases to the object. Will be saved to database as AliasDB entries but returned as strings.
|
aliases (list of strings) - aliases to the object. Will be saved to database as AliasDB entries but returned as strings.
|
||||||
dbref (int, read-only) - unique #id-number. Also "id" can be used.
|
dbref (int, read-only) - unique #id-number. Also "id" can be used.
|
||||||
dbobj (Object, read-only) - link to database model. dbobj.typeclass points back to this class
|
dbobj (Object, read-only) - link to database model. dbobj.typeclass points back to this class
|
||||||
typeclass (Object, read-only) - this links back to this class as an identified only. Use self.swap_typeclass() to switch.
|
typeclass (Object, read-only) - this links back to this class as an identified only. Use self.swap_typeclass() to switch.
|
||||||
date_created (string) - time stamp of object creation
|
date_created (string) - time stamp of object creation
|
||||||
permissions (list of strings) - list of permission strings
|
permissions (list of strings) - list of permission strings
|
||||||
|
|
||||||
player (Player) - controlling player (will also return offline player)
|
player (Player) - controlling player (will also return offline player)
|
||||||
location (Object) - current location. Is None if this is a room
|
location (Object) - current location. Is None if this is a room
|
||||||
|
|
@ -59,19 +59,19 @@ class Object(TypeClass):
|
||||||
has_player (bool, read-only)- will only return *connected* players
|
has_player (bool, read-only)- will only return *connected* players
|
||||||
contents (list of Objects, read-only) - returns all objects inside this object (including exits)
|
contents (list of Objects, read-only) - returns all objects inside this object (including exits)
|
||||||
exits (list of Objects, read-only) - returns all exits from this object, if any
|
exits (list of Objects, read-only) - returns all exits from this object, if any
|
||||||
destination (Object) - only set if this object is an exit.
|
destination (Object) - only set if this object is an exit.
|
||||||
is_superuser (bool, read-only) - True/False if this user is a superuser
|
is_superuser (bool, read-only) - True/False if this user is a superuser
|
||||||
|
|
||||||
* Handlers available
|
* Handlers available
|
||||||
|
|
||||||
locks - lock-handler: use locks.add() to add new lock strings
|
locks - lock-handler: use locks.add() to add new lock strings
|
||||||
db - attribute-handler: store/retrieve database attributes on this self.db.myattr=val, val=self.db.myattr
|
db - attribute-handler: store/retrieve database attributes on this self.db.myattr=val, val=self.db.myattr
|
||||||
ndb - non-persistent attribute handler: same as db but does not create a database entry when storing data
|
ndb - non-persistent attribute handler: same as db but does not create a database entry when storing data
|
||||||
scripts - script-handler. Add new scripts to object with scripts.add()
|
scripts - script-handler. Add new scripts to object with scripts.add()
|
||||||
cmdset - cmdset-handler. Use cmdset.add() to add new cmdsets to object
|
cmdset - cmdset-handler. Use cmdset.add() to add new cmdsets to object
|
||||||
nicks - nick-handler. New nicks with nicks.add().
|
nicks - nick-handler. New nicks with nicks.add().
|
||||||
|
|
||||||
* Helper methods (see src.objects.objects.py for full headers)
|
* Helper methods (see src.objects.objects.py for full headers)
|
||||||
|
|
||||||
search(ostring, global_search=False, attribute_name=None, use_nicks=False, location=None, ignore_errors=False, player=False)
|
search(ostring, global_search=False, attribute_name=None, use_nicks=False, location=None, ignore_errors=False, player=False)
|
||||||
execute_cmd(raw_string)
|
execute_cmd(raw_string)
|
||||||
|
|
@ -90,15 +90,15 @@ class Object(TypeClass):
|
||||||
basetype_setup() - only called once, used for behind-the-scenes setup. Normally not modified.
|
basetype_setup() - only called once, used for behind-the-scenes setup. Normally not modified.
|
||||||
basetype_posthook_setup() - customization in basetype, after the object has been created; Normally not modified.
|
basetype_posthook_setup() - customization in basetype, after the object has been created; Normally not modified.
|
||||||
|
|
||||||
at_object_creation() - only called once, when object is first created. Object customizations go here.
|
at_object_creation() - only called once, when object is first created. Object customizations go here.
|
||||||
at_object_delete() - called just before deleting an object. If returning False, deletion is aborted. Note that all objects
|
at_object_delete() - called just before deleting an object. If returning False, deletion is aborted. Note that all objects
|
||||||
inside a deleted object are automatically moved to their <home>, they don't need to be removed here.
|
inside a deleted object are automatically moved to their <home>, they don't need to be removed here.
|
||||||
|
|
||||||
at_init() - called whenever typeclass is cached from memory, at least once every server restart/reload
|
at_init() - called whenever typeclass is cached from memory, at least once every server restart/reload
|
||||||
at_cmdset_get() - this is called just before the command handler requests a cmdset from this object
|
at_cmdset_get() - this is called just before the command handler requests a cmdset from this object
|
||||||
at_first_login() - (player-controlled objects only) called once, the very first time user logs in.
|
at_first_login() - (player-controlled objects only) called once, the very first time user logs in.
|
||||||
at_pre_login() - (player-controlled objects only) called every time the user connects, after they have identified, before other setup
|
at_pre_login() - (player-controlled objects only) called every time the user connects, after they have identified, before other setup
|
||||||
at_post_login() - (player-controlled objects only) called at the end of login, just before setting the player loose in the world.
|
at_post_login() - (player-controlled objects only) called at the end of login, just before setting the player loose in the world.
|
||||||
at_disconnect() - (player-controlled objects only) called just before the user disconnects (or goes linkless)
|
at_disconnect() - (player-controlled objects only) called just before the user disconnects (or goes linkless)
|
||||||
at_server_reload() - called before server is reloaded
|
at_server_reload() - called before server is reloaded
|
||||||
at_server_shutdown() - called just before server is fully shut down
|
at_server_shutdown() - called just before server is fully shut down
|
||||||
|
|
@ -111,19 +111,19 @@ class Object(TypeClass):
|
||||||
at_object_receive(obj, source_location) - called when this object receives another object
|
at_object_receive(obj, source_location) - called when this object receives another object
|
||||||
|
|
||||||
at_before_traverse(traversing_object) - (exit-objects only) called just before an object traverses this object
|
at_before_traverse(traversing_object) - (exit-objects only) called just before an object traverses this object
|
||||||
at_after_traverse(traversing_object, source_location) - (exit-objects only) called just after a traversal has happened.
|
at_after_traverse(traversing_object, source_location) - (exit-objects only) called just after a traversal has happened.
|
||||||
at_failed_traverse(traversing_object) - (exit-objects only) called if traversal fails and property err_traverse is not defined.
|
at_failed_traverse(traversing_object) - (exit-objects only) called if traversal fails and property err_traverse is not defined.
|
||||||
|
|
||||||
at_msg_receive(self, msg, from_obj=None, data=None) - called when a message (via self.msg()) is sent to this obj.
|
at_msg_receive(self, msg, from_obj=None, data=None) - called when a message (via self.msg()) is sent to this obj.
|
||||||
If returns false, aborts send.
|
If returns false, aborts send.
|
||||||
at_msg_send(self, msg, to_obj=None, data=None) - called when this objects sends a message to someone via self.msg().
|
at_msg_send(self, msg, to_obj=None, data=None) - called when this objects sends a message to someone via self.msg().
|
||||||
|
|
||||||
return_appearance(looker) - describes this object. Used by "look" command by default
|
return_appearance(looker) - describes this object. Used by "look" command by default
|
||||||
at_desc(looker=None) - called by 'look' whenever the appearance is requested.
|
at_desc(looker=None) - called by 'look' whenever the appearance is requested.
|
||||||
at_get(getter) - called after object has been picked up. Does not stop pickup.
|
at_get(getter) - called after object has been picked up. Does not stop pickup.
|
||||||
at_drop(dropper) - called when this object has been dropped.
|
at_drop(dropper) - called when this object has been dropped.
|
||||||
at_say(speaker, message) - by default, called if an object inside this object speaks
|
at_say(speaker, message) - by default, called if an object inside this object speaks
|
||||||
|
|
||||||
"""
|
"""
|
||||||
super(Object, self).__init__(dbobj)
|
super(Object, self).__init__(dbobj)
|
||||||
|
|
||||||
|
|
@ -132,84 +132,84 @@ class Object(TypeClass):
|
||||||
def search(self, ostring,
|
def search(self, ostring,
|
||||||
global_search=False,
|
global_search=False,
|
||||||
attribute_name=None,
|
attribute_name=None,
|
||||||
use_nicks=False,
|
use_nicks=False,
|
||||||
location=None,
|
location=None,
|
||||||
ignore_errors=False,
|
ignore_errors=False,
|
||||||
player=False):
|
player=False):
|
||||||
"""
|
"""
|
||||||
Perform a standard object search in the database, handling
|
Perform a standard object search in the database, handling
|
||||||
multiple results and lack thereof gracefully.
|
multiple results and lack thereof gracefully.
|
||||||
|
|
||||||
ostring: (str) The string to match object names against.
|
ostring: (str) The string to match object names against.
|
||||||
Obs - To find a player, append * to the
|
Obs - To find a player, append * to the
|
||||||
start of ostring.
|
start of ostring.
|
||||||
global_search(bool): Search all objects, not just the current
|
global_search(bool): Search all objects, not just the current
|
||||||
location/inventory
|
location/inventory
|
||||||
attribute_name (string) Which attribute to match
|
attribute_name (string) Which attribute to match
|
||||||
(if None, uses default 'name')
|
(if None, uses default 'name')
|
||||||
use_nicks (bool) : Use nickname replace (off by default)
|
use_nicks (bool) : Use nickname replace (off by default)
|
||||||
location (Object): If None, use caller's current location
|
location (Object): If None, use caller's current location
|
||||||
ignore_errors (bool): Don't display any error messages even
|
ignore_errors (bool): Don't display any error messages even
|
||||||
if there are none/multiple matches -
|
if there are none/multiple matches -
|
||||||
just return the result as a list.
|
just return the result as a list.
|
||||||
player (Objectt): Don't search for an Object but a Player.
|
player (Objectt): Don't search for an Object but a Player.
|
||||||
This will also find players that don't
|
This will also find players that don't
|
||||||
currently have a character.
|
currently have a character.
|
||||||
|
|
||||||
Returns - a unique Object/Player match or None. All error
|
Returns - a unique Object/Player match or None. All error
|
||||||
messages are handled by system-commands and the parser-handlers
|
messages are handled by system-commands and the parser-handlers
|
||||||
specified in settings.
|
specified in settings.
|
||||||
|
|
||||||
Use *<string> to search for objects controlled by a specific
|
Use *<string> to search for objects controlled by a specific
|
||||||
player. Note that the object controlled by the player will be
|
player. Note that the object controlled by the player will be
|
||||||
returned, not the player object itself. This also means that
|
returned, not the player object itself. This also means that
|
||||||
this will not find Players without a character. Use the keyword
|
this will not find Players without a character. Use the keyword
|
||||||
player=True to find player objects.
|
player=True to find player objects.
|
||||||
|
|
||||||
Note - for multiple matches, the engine accepts a number
|
Note - for multiple matches, the engine accepts a number
|
||||||
linked to the key in order to separate the matches from
|
linked to the key in order to separate the matches from
|
||||||
each other without showing the dbref explicitly. Default
|
each other without showing the dbref explicitly. Default
|
||||||
syntax for this is 'N-searchword'. So for example, if there
|
syntax for this is 'N-searchword'. So for example, if there
|
||||||
are three objects in the room all named 'ball', you could
|
are three objects in the room all named 'ball', you could
|
||||||
address the individual ball as '1-ball', '2-ball', '3-ball'
|
address the individual ball as '1-ball', '2-ball', '3-ball'
|
||||||
etc.
|
etc.
|
||||||
"""
|
"""
|
||||||
return self.dbobj.search(ostring,
|
return self.dbobj.search(ostring,
|
||||||
global_search=global_search,
|
global_search=global_search,
|
||||||
attribute_name=attribute_name,
|
attribute_name=attribute_name,
|
||||||
use_nicks=use_nicks,
|
use_nicks=use_nicks,
|
||||||
location=location,
|
location=location,
|
||||||
ignore_errors=ignore_errors,
|
ignore_errors=ignore_errors,
|
||||||
player=player)
|
player=player)
|
||||||
|
|
||||||
def execute_cmd(self, raw_string):
|
def execute_cmd(self, raw_string):
|
||||||
"""
|
"""
|
||||||
Do something as this object. This command transparently
|
Do something as this object. This command transparently
|
||||||
lets its typeclass execute the command. Evennia also calls
|
lets its typeclass execute the command. Evennia also calls
|
||||||
this method whenever the player sends a command on the command line.
|
this method whenever the player sends a command on the command line.
|
||||||
|
|
||||||
Argument:
|
Argument:
|
||||||
raw_string (string) - raw command input
|
raw_string (string) - raw command input
|
||||||
|
|
||||||
Returns Deferred - this is an asynchronous Twisted object that will
|
Returns Deferred - this is an asynchronous Twisted object that will
|
||||||
not fire until the command has actually finished executing. To overload
|
not fire until the command has actually finished executing. To overload
|
||||||
this one needs to attach callback functions to it, with addCallback(function).
|
this one needs to attach callback functions to it, with addCallback(function).
|
||||||
This function will be called with an eventual return value from the command
|
This function will be called with an eventual return value from the command
|
||||||
execution.
|
execution.
|
||||||
|
|
||||||
This return is not used at all by Evennia by default, but might be useful
|
This return is not used at all by Evennia by default, but might be useful
|
||||||
for coders intending to implement some sort of nested command structure.
|
for coders intending to implement some sort of nested command structure.
|
||||||
"""
|
"""
|
||||||
return self.dbobj.execute_cmd(raw_string)
|
return self.dbobj.execute_cmd(raw_string)
|
||||||
|
|
||||||
def msg(self, message, from_obj=None, data=None):
|
def msg(self, message, from_obj=None, data=None):
|
||||||
"""
|
"""
|
||||||
Emits something to any sessions attached to the object.
|
Emits something to any sessions attached to the object.
|
||||||
|
|
||||||
message (str): The message to send
|
message (str): The message to send
|
||||||
from_obj (obj): object that is sending.
|
from_obj (obj): object that is sending.
|
||||||
data (object): an optional data object that may or may not
|
data (object): an optional data object that may or may not
|
||||||
be used by the protocol.
|
be used by the protocol.
|
||||||
"""
|
"""
|
||||||
self.dbobj.msg(message, from_obj=from_obj, data=data)
|
self.dbobj.msg(message, from_obj=from_obj, data=data)
|
||||||
|
|
||||||
|
|
@ -228,44 +228,44 @@ class Object(TypeClass):
|
||||||
exit object (i.e. it has "destination"!=None), the move_to will
|
exit object (i.e. it has "destination"!=None), the move_to will
|
||||||
happen to this destination and -not- into the exit object itself, unless
|
happen to this destination and -not- into the exit object itself, unless
|
||||||
use_destination=False. Note that no lock checks are done by this function,
|
use_destination=False. Note that no lock checks are done by this function,
|
||||||
such things are assumed to have been handled before calling move_to.
|
such things are assumed to have been handled before calling move_to.
|
||||||
|
|
||||||
destination: (Object) Reference to the object to move to. This
|
destination: (Object) Reference to the object to move to. This
|
||||||
can also be an exit object, in which case the destination
|
can also be an exit object, in which case the destination
|
||||||
property is used as destination.
|
property is used as destination.
|
||||||
quiet: (bool) If true, don't emit left/arrived messages.
|
quiet: (bool) If true, don't emit left/arrived messages.
|
||||||
emit_to_obj: (Object) object to receive error messages
|
emit_to_obj: (Object) object to receive error messages
|
||||||
use_destination (bool): Default is for objects to use the "destination" property
|
use_destination (bool): Default is for objects to use the "destination" property
|
||||||
of destinations as the target to move to. Turning off this
|
of destinations as the target to move to. Turning off this
|
||||||
keyword allows objects to move "inside" exit objects.
|
keyword allows objects to move "inside" exit objects.
|
||||||
Returns True/False depending on if there were problems with the move. This method
|
Returns True/False depending on if there were problems with the move. This method
|
||||||
may also return various error messages to the emit_to_obj.
|
may also return various error messages to the emit_to_obj.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return self.dbobj.move_to(destination, quiet=quiet,
|
return self.dbobj.move_to(destination, quiet=quiet,
|
||||||
emit_to_obj=emit_to_obj, use_destination=use_destination)
|
emit_to_obj=emit_to_obj, use_destination=use_destination)
|
||||||
|
|
||||||
def copy(self, new_key=None):
|
def copy(self, new_key=None):
|
||||||
"""
|
"""
|
||||||
Makes an identical copy of this object. If you want to customize the copy by
|
Makes an identical copy of this object. If you want to customize the copy by
|
||||||
changing some settings, use ObjectDB.object.copy_object() directly.
|
changing some settings, use ObjectDB.object.copy_object() directly.
|
||||||
|
|
||||||
new_key (string) - new key/name of copied object. If new_key is not specified, the copy will be named
|
new_key (string) - new key/name of copied object. If new_key is not specified, the copy will be named
|
||||||
<old_key>_copy by default.
|
<old_key>_copy by default.
|
||||||
Returns: Object (copy of this one)
|
Returns: Object (copy of this one)
|
||||||
"""
|
"""
|
||||||
return self.dbobj.copy(new_key=new_key)
|
return self.dbobj.copy(new_key=new_key)
|
||||||
|
|
||||||
def delete(self):
|
def delete(self):
|
||||||
"""
|
"""
|
||||||
Deletes this object.
|
Deletes this object.
|
||||||
Before deletion, this method makes sure to move all contained
|
Before deletion, this method makes sure to move all contained
|
||||||
objects to their respective home locations, as well as clean
|
objects to their respective home locations, as well as clean
|
||||||
up all exits to/from the object.
|
up all exits to/from the object.
|
||||||
|
|
||||||
Returns: boolean True if deletion succeded, False if there
|
Returns: boolean True if deletion succeded, False if there
|
||||||
were errors during deletion or deletion otherwise
|
were errors during deletion or deletion otherwise
|
||||||
failed.
|
failed.
|
||||||
"""
|
"""
|
||||||
return self.dbobj.delete()
|
return self.dbobj.delete()
|
||||||
|
|
||||||
|
|
@ -277,16 +277,16 @@ class Object(TypeClass):
|
||||||
Returns true if this object has this type
|
Returns true if this object has this type
|
||||||
OR has a typeclass which is an subclass of
|
OR has a typeclass which is an subclass of
|
||||||
the given typeclass.
|
the given typeclass.
|
||||||
|
|
||||||
typeclass - can be a class object or the
|
typeclass - can be a class object or the
|
||||||
python path to such an object to match against.
|
python path to such an object to match against.
|
||||||
|
|
||||||
exact - returns true only if the object's
|
exact - returns true only if the object's
|
||||||
type is exactly this typeclass, ignoring
|
type is exactly this typeclass, ignoring
|
||||||
parents.
|
parents.
|
||||||
|
|
||||||
Returns: Boolean
|
Returns: Boolean
|
||||||
"""
|
"""
|
||||||
return self.dbobj.is_typeclass(typeclass, exact=exact)
|
return self.dbobj.is_typeclass(typeclass, exact=exact)
|
||||||
|
|
||||||
def swap_typeclass(self, new_typeclass, clean_attributes=False, no_default=True):
|
def swap_typeclass(self, new_typeclass, clean_attributes=False, no_default=True):
|
||||||
|
|
@ -294,18 +294,18 @@ class Object(TypeClass):
|
||||||
This performs an in-situ swap of the typeclass. This means
|
This performs an in-situ swap of the typeclass. This means
|
||||||
that in-game, this object will suddenly be something else.
|
that in-game, this object will suddenly be something else.
|
||||||
Player will not be affected. To 'move' a player to a different
|
Player will not be affected. To 'move' a player to a different
|
||||||
object entirely (while retaining this object's type), use
|
object entirely (while retaining this object's type), use
|
||||||
self.player.swap_object().
|
self.player.swap_object().
|
||||||
|
|
||||||
Note that this might be an error prone operation if the
|
Note that this might be an error prone operation if the
|
||||||
old/new typeclass was heavily customized - your code
|
old/new typeclass was heavily customized - your code
|
||||||
might expect one and not the other, so be careful to
|
might expect one and not the other, so be careful to
|
||||||
bug test your code if using this feature! Often its easiest
|
bug test your code if using this feature! Often its easiest
|
||||||
to create a new object and just swap the player over to
|
to create a new object and just swap the player over to
|
||||||
that one instead.
|
that one instead.
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
new_typeclass (path/classobj) - type to switch to
|
new_typeclass (path/classobj) - type to switch to
|
||||||
clean_attributes (bool/list) - will delete all attributes
|
clean_attributes (bool/list) - will delete all attributes
|
||||||
stored on this object (but not any
|
stored on this object (but not any
|
||||||
of the database fields such as name or
|
of the database fields such as name or
|
||||||
|
|
@ -317,10 +317,10 @@ class Object(TypeClass):
|
||||||
no_default - if this is active, the swapper will not allow for
|
no_default - if this is active, the swapper will not allow for
|
||||||
swapping to a default typeclass in case the given
|
swapping to a default typeclass in case the given
|
||||||
one fails for some reason. Instead the old one
|
one fails for some reason. Instead the old one
|
||||||
will be preserved.
|
will be preserved.
|
||||||
Returns:
|
Returns:
|
||||||
boolean True/False depending on if the swap worked or not.
|
boolean True/False depending on if the swap worked or not.
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
self.dbobj.swap_typeclass(new_typeclass, clean_attributes=clean_attributes, no_default=no_default)
|
self.dbobj.swap_typeclass(new_typeclass, clean_attributes=clean_attributes, no_default=no_default)
|
||||||
|
|
@ -332,14 +332,14 @@ class Object(TypeClass):
|
||||||
accessing_obj (Object)- object trying to access this one
|
accessing_obj (Object)- object trying to access this one
|
||||||
access_type (string) - type of access sought
|
access_type (string) - type of access sought
|
||||||
default (bool) - what to return if no lock of access_type was found
|
default (bool) - what to return if no lock of access_type was found
|
||||||
"""
|
"""
|
||||||
return self.dbobj.access(accessing_obj, access_type=access_type, default=default)
|
return self.dbobj.access(accessing_obj, access_type=access_type, default=default)
|
||||||
|
|
||||||
def check_permstring(self, permstring):
|
def check_permstring(self, permstring):
|
||||||
"""
|
"""
|
||||||
This explicitly checks the given string against this object's
|
This explicitly checks the given string against this object's
|
||||||
'permissions' property without involving any locks.
|
'permissions' property without involving any locks.
|
||||||
|
|
||||||
permstring (string) - permission string that need to match a permission on the object.
|
permstring (string) - permission string that need to match a permission on the object.
|
||||||
(example: 'Builders')
|
(example: 'Builders')
|
||||||
"""
|
"""
|
||||||
|
|
@ -355,7 +355,7 @@ class Object(TypeClass):
|
||||||
"""
|
"""
|
||||||
result = self.id == other
|
result = self.id == other
|
||||||
if not result and hasattr(other, "id"):
|
if not result and hasattr(other, "id"):
|
||||||
result = self.id == other.id
|
result = self.id == other.id
|
||||||
if not result:
|
if not result:
|
||||||
try:
|
try:
|
||||||
result = other and self.user.id == other.user.id
|
result = other and self.user.id == other.user.id
|
||||||
|
|
@ -366,14 +366,14 @@ class Object(TypeClass):
|
||||||
|
|
||||||
## hooks called by the game engine
|
## hooks called by the game engine
|
||||||
|
|
||||||
|
|
||||||
def basetype_setup(self):
|
def basetype_setup(self):
|
||||||
"""
|
"""
|
||||||
This sets up the default properties of an Object,
|
This sets up the default properties of an Object,
|
||||||
just before the more general at_object_creation.
|
just before the more general at_object_creation.
|
||||||
|
|
||||||
Don't change this, instead edit at_object_creation() to
|
Don't change this, instead edit at_object_creation() to
|
||||||
overload the defaults (it is called after this one).
|
overload the defaults (it is called after this one).
|
||||||
"""
|
"""
|
||||||
# the default security setup fallback for a generic
|
# the default security setup fallback for a generic
|
||||||
# object. Overload in child for a custom setup. Also creation
|
# object. Overload in child for a custom setup. Also creation
|
||||||
|
|
@ -383,13 +383,13 @@ class Object(TypeClass):
|
||||||
dbref = self.dbobj.dbref
|
dbref = self.dbobj.dbref
|
||||||
|
|
||||||
self.locks.add("control:id(%s) or perm(Immortals)" % dbref) # edit locks/permissions, delete
|
self.locks.add("control:id(%s) or perm(Immortals)" % dbref) # edit locks/permissions, delete
|
||||||
self.locks.add("examine:perm(Builders)") # examine properties
|
self.locks.add("examine:perm(Builders)") # examine properties
|
||||||
self.locks.add("view:all()") # look at object (visibility)
|
self.locks.add("view:all()") # look at object (visibility)
|
||||||
self.locks.add("edit:perm(Wizards)") # edit properties/attributes
|
self.locks.add("edit:perm(Wizards)") # edit properties/attributes
|
||||||
self.locks.add("delete:perm(Wizards)") # delete object
|
self.locks.add("delete:perm(Wizards)") # delete object
|
||||||
self.locks.add("get:all()") # pick up object
|
self.locks.add("get:all()") # pick up object
|
||||||
self.locks.add("call:true()") # allow to call commands on this object
|
self.locks.add("call:true()") # allow to call commands on this object
|
||||||
self.locks.add("puppet:id(%s) or perm(Immortals) or pperm(Immortals)" % dbref) # restricts puppeting of this object
|
self.locks.add("puppet:id(%s) or perm(Immortals) or pperm(Immortals)" % dbref) # restricts puppeting of this object
|
||||||
|
|
||||||
def basetype_posthook_setup(self):
|
def basetype_posthook_setup(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -403,27 +403,27 @@ class Object(TypeClass):
|
||||||
def at_object_creation(self):
|
def at_object_creation(self):
|
||||||
"""
|
"""
|
||||||
Called once, when this object is first created.
|
Called once, when this object is first created.
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def at_object_delete(self):
|
def at_object_delete(self):
|
||||||
"""
|
"""
|
||||||
Called just before the database object is
|
Called just before the database object is
|
||||||
permanently delete()d from the database. If
|
permanently delete()d from the database. If
|
||||||
this method returns False, deletion is aborted.
|
this method returns False, deletion is aborted.
|
||||||
"""
|
"""
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def at_init(self):
|
def at_init(self):
|
||||||
"""
|
"""
|
||||||
This is always called whenever this object is initiated --
|
This is always called whenever this object is initiated --
|
||||||
that is, whenever it its typeclass is cached from memory. This
|
that is, whenever it its typeclass is cached from memory. This
|
||||||
happens on-demand first time the object is used or activated
|
happens on-demand first time the object is used or activated
|
||||||
in some way after being created but also after each server
|
in some way after being created but also after each server
|
||||||
restart or reload.
|
restart or reload.
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def at_cmdset_get(self):
|
def at_cmdset_get(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -450,7 +450,7 @@ class Object(TypeClass):
|
||||||
"""
|
"""
|
||||||
Called at the end of the login
|
Called at the end of the login
|
||||||
process, just before letting
|
process, just before letting
|
||||||
them loose.
|
them loose.
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
@ -463,16 +463,16 @@ class Object(TypeClass):
|
||||||
|
|
||||||
def at_server_reload(self):
|
def at_server_reload(self):
|
||||||
"""
|
"""
|
||||||
This hook is called whenever the server is shutting down for restart/reboot.
|
This hook is called whenever the server is shutting down for restart/reboot.
|
||||||
If you want to, for example, save non-persistent properties across a restart,
|
If you want to, for example, save non-persistent properties across a restart,
|
||||||
this is the place to do it.
|
this is the place to do it.
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def at_server_shutdown(self):
|
def at_server_shutdown(self):
|
||||||
"""
|
"""
|
||||||
This hook is called whenever the server is shutting down fully (i.e. not for
|
This hook is called whenever the server is shutting down fully (i.e. not for
|
||||||
a restart).
|
a restart).
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
@ -482,63 +482,63 @@ class Object(TypeClass):
|
||||||
def at_before_move(self, destination):
|
def at_before_move(self, destination):
|
||||||
"""
|
"""
|
||||||
Called just before starting to move
|
Called just before starting to move
|
||||||
this object to destination.
|
this object to destination.
|
||||||
|
|
||||||
destination - the object we are moving to
|
destination - the object we are moving to
|
||||||
|
|
||||||
If this method returns False/None, the move
|
If this method returns False/None, the move
|
||||||
is cancelled before it is even started.
|
is cancelled before it is even started.
|
||||||
"""
|
"""
|
||||||
#return has_perm(self, destination, "can_move")
|
#return has_perm(self, destination, "can_move")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def announce_move_from(self, destination):
|
def announce_move_from(self, destination):
|
||||||
"""
|
"""
|
||||||
Called if the move is to be announced. This is
|
Called if the move is to be announced. This is
|
||||||
called while we are still standing in the old
|
called while we are still standing in the old
|
||||||
location.
|
location.
|
||||||
|
|
||||||
destination - the place we are going to.
|
destination - the place we are going to.
|
||||||
"""
|
"""
|
||||||
if not self.location:
|
if not self.location:
|
||||||
return
|
return
|
||||||
name = self.name
|
name = self.name
|
||||||
loc_name = ""
|
loc_name = ""
|
||||||
loc_name = self.location.name
|
loc_name = self.location.name
|
||||||
dest_name = destination.name
|
dest_name = destination.name
|
||||||
string = "%s is leaving %s, heading for %s."
|
string = "%s is leaving %s, heading for %s."
|
||||||
self.location.msg_contents(string % (name, loc_name, dest_name), exclude=self)
|
self.location.msg_contents(string % (name, loc_name, dest_name), exclude=self)
|
||||||
|
|
||||||
def announce_move_to(self, source_location):
|
def announce_move_to(self, source_location):
|
||||||
"""
|
"""
|
||||||
Called after the move if the move was not quiet. At this
|
Called after the move if the move was not quiet. At this
|
||||||
point we are standing in the new location.
|
point we are standing in the new location.
|
||||||
|
|
||||||
source_location - the place we came from
|
source_location - the place we came from
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name = self.name
|
name = self.name
|
||||||
if not source_location and self.location.has_player:
|
if not source_location and self.location.has_player:
|
||||||
# This was created from nowhere and added to a player's
|
# This was created from nowhere and added to a player's
|
||||||
# inventory; it's probably the result of a create command.
|
# inventory; it's probably the result of a create command.
|
||||||
string = "You now have %s in your possession." % name
|
string = "You now have %s in your possession." % name
|
||||||
self.location.msg(string)
|
self.location.msg(string)
|
||||||
return
|
return
|
||||||
|
|
||||||
src_name = "nowhere"
|
src_name = "nowhere"
|
||||||
loc_name = self.location.name
|
loc_name = self.location.name
|
||||||
if source_location:
|
if source_location:
|
||||||
src_name = source_location.name
|
src_name = source_location.name
|
||||||
string = "%s arrives to %s from %s."
|
string = "%s arrives to %s from %s."
|
||||||
self.location.msg_contents(string % (name, loc_name, src_name), exclude=self)
|
self.location.msg_contents(string % (name, loc_name, src_name), exclude=self)
|
||||||
|
|
||||||
|
|
||||||
def at_after_move(self, source_location):
|
def at_after_move(self, source_location):
|
||||||
"""
|
"""
|
||||||
Called after move has completed, regardless of quiet mode or not.
|
Called after move has completed, regardless of quiet mode or not.
|
||||||
Allows changes to the object due to the location it is now in.
|
Allows changes to the object due to the location it is now in.
|
||||||
|
|
||||||
source_location - where we came from
|
source_location - where we came from
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
@ -554,92 +554,92 @@ class Object(TypeClass):
|
||||||
|
|
||||||
def at_object_receive(self, moved_obj, source_location):
|
def at_object_receive(self, moved_obj, source_location):
|
||||||
"""
|
"""
|
||||||
Called after an object has been moved into this object.
|
Called after an object has been moved into this object.
|
||||||
|
|
||||||
moved_obj - the object moved into this one
|
moved_obj - the object moved into this one
|
||||||
source_location - where moved_object came from.
|
source_location - where moved_object came from.
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def at_before_traverse(self, traversing_object):
|
def at_before_traverse(self, traversing_object):
|
||||||
"""
|
"""
|
||||||
Called just before an object uses this object to
|
Called just before an object uses this object to
|
||||||
traverse to another object (i.e. this object is a type of Exit)
|
traverse to another object (i.e. this object is a type of Exit)
|
||||||
|
|
||||||
The target location should normally be available as self.destination.
|
The target location should normally be available as self.destination.
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def at_after_traverse(self, traversing_object, source_location):
|
def at_after_traverse(self, traversing_object, source_location):
|
||||||
"""
|
"""
|
||||||
Called just after an object successfully used this object to
|
Called just after an object successfully used this object to
|
||||||
traverse to another object (i.e. this object is a type of Exit)
|
traverse to another object (i.e. this object is a type of Exit)
|
||||||
|
|
||||||
The target location should normally be available as self.destination.
|
The target location should normally be available as self.destination.
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def at_failed_traverse(self, traversing_object):
|
def at_failed_traverse(self, traversing_object):
|
||||||
"""
|
"""
|
||||||
This is called if an object fails to traverse this object for some
|
This is called if an object fails to traverse this object for some
|
||||||
reason. It will not be called if the attribute err_traverse is defined,
|
reason. It will not be called if the attribute err_traverse is defined,
|
||||||
that attribute will then be echoed back instead.
|
that attribute will then be echoed back instead.
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def at_msg_receive(self, msg, from_obj=None, data=None):
|
def at_msg_receive(self, msg, from_obj=None, data=None):
|
||||||
"""
|
"""
|
||||||
This hook is called whenever someone
|
This hook is called whenever someone
|
||||||
sends a message to this object.
|
sends a message to this object.
|
||||||
|
|
||||||
Note that from_obj may be None if the sender did
|
Note that from_obj may be None if the sender did
|
||||||
not include itself as an argument to the obj.msg()
|
not include itself as an argument to the obj.msg()
|
||||||
call - so you have to check for this. .
|
call - so you have to check for this. .
|
||||||
|
|
||||||
Consider this a pre-processing method before
|
Consider this a pre-processing method before
|
||||||
msg is passed on to the user sesssion. If this
|
msg is passed on to the user sesssion. If this
|
||||||
method returns False, the msg will not be
|
method returns False, the msg will not be
|
||||||
passed on.
|
passed on.
|
||||||
|
|
||||||
msg = the message received
|
msg = the message received
|
||||||
from_obj = the one sending the message
|
from_obj = the one sending the message
|
||||||
"""
|
"""
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def at_msg_send(self, msg, to_obj=None, data=None):
|
def at_msg_send(self, msg, to_obj=None, data=None):
|
||||||
"""
|
"""
|
||||||
This is a hook that is called when /this/ object
|
This is a hook that is called when /this/ object
|
||||||
sends a message to another object with obj.msg()
|
sends a message to another object with obj.msg()
|
||||||
while also specifying that it is the one sending.
|
while also specifying that it is the one sending.
|
||||||
|
|
||||||
Note that this method is executed on the object
|
Note that this method is executed on the object
|
||||||
passed along with the msg() function (i.e. using
|
passed along with the msg() function (i.e. using
|
||||||
obj.msg(msg, caller) will then launch caller.at_msg())
|
obj.msg(msg, caller) will then launch caller.at_msg())
|
||||||
and if no object was passed, it will never be called.
|
and if no object was passed, it will never be called.
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
# hooks called by the default cmdset.
|
|
||||||
|
# hooks called by the default cmdset.
|
||||||
|
|
||||||
def return_appearance(self, pobject):
|
def return_appearance(self, pobject):
|
||||||
"""
|
"""
|
||||||
This is a convenient hook for a 'look'
|
This is a convenient hook for a 'look'
|
||||||
command to call.
|
command to call.
|
||||||
"""
|
"""
|
||||||
if not pobject:
|
if not pobject:
|
||||||
return
|
return
|
||||||
string = "{c%s{n" % self.name
|
string = "{c%s{n" % self.name
|
||||||
desc = self.attr("desc")
|
desc = self.attr("desc")
|
||||||
if desc:
|
if desc:
|
||||||
string += "\n %s" % desc
|
string += "\n %s" % desc
|
||||||
exits = []
|
exits = []
|
||||||
users = []
|
users = []
|
||||||
things = []
|
things = []
|
||||||
for content in [con for con in self.contents if con.access(pobject, 'view')]:
|
for content in [con for con in self.contents if con.access(pobject, 'view')]:
|
||||||
if content == pobject:
|
if content == pobject:
|
||||||
continue
|
continue
|
||||||
name = content.name
|
name = content.name
|
||||||
if content.destination:
|
if content.destination:
|
||||||
exits.append(name)
|
exits.append(name)
|
||||||
|
|
@ -651,17 +651,17 @@ class Object(TypeClass):
|
||||||
string += "\n{wExits:{n " + ", ".join(exits)
|
string += "\n{wExits:{n " + ", ".join(exits)
|
||||||
if users or things:
|
if users or things:
|
||||||
string += "\n{wYou see: {n"
|
string += "\n{wYou see: {n"
|
||||||
if users:
|
if users:
|
||||||
string += "{c" + ", ".join(users) + "{n "
|
string += "{c" + ", ".join(users) + "{n "
|
||||||
if things:
|
if things:
|
||||||
string += ", ".join(things)
|
string += ", ".join(things)
|
||||||
return string
|
return string
|
||||||
|
|
||||||
def at_desc(self, looker=None):
|
def at_desc(self, looker=None):
|
||||||
"""
|
"""
|
||||||
This is called whenever someone looks
|
This is called whenever someone looks
|
||||||
at this object. Looker is the looking
|
at this object. Looker is the looking
|
||||||
object.
|
object.
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
@ -685,8 +685,8 @@ class Object(TypeClass):
|
||||||
def at_say(self, speaker, message):
|
def at_say(self, speaker, message):
|
||||||
"""
|
"""
|
||||||
Called on this object if an object inside this object speaks.
|
Called on this object if an object inside this object speaks.
|
||||||
The string returned from this method is the final form
|
The string returned from this method is the final form
|
||||||
of the speech. Obs - you don't have to add things like
|
of the speech. Obs - you don't have to add things like
|
||||||
'you say: ' or similar, that is handled by the say command.
|
'you say: ' or similar, that is handled by the say command.
|
||||||
|
|
||||||
speaker - the object speaking
|
speaker - the object speaking
|
||||||
|
|
@ -695,7 +695,7 @@ class Object(TypeClass):
|
||||||
return message
|
return message
|
||||||
|
|
||||||
#
|
#
|
||||||
# Base Player object
|
# Base Player object
|
||||||
#
|
#
|
||||||
|
|
||||||
class Character(Object):
|
class Character(Object):
|
||||||
|
|
@ -703,24 +703,24 @@ class Character(Object):
|
||||||
This is just like the Object except it implements its own
|
This is just like the Object except it implements its own
|
||||||
version of the at_object_creation to set up the script
|
version of the at_object_creation to set up the script
|
||||||
that adds the default cmdset to the object.
|
that adds the default cmdset to the object.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def basetype_setup(self):
|
def basetype_setup(self):
|
||||||
"""
|
"""
|
||||||
Setup character-specific security
|
Setup character-specific security
|
||||||
|
|
||||||
Don't change this, instead edit at_object_creation() to
|
Don't change this, instead edit at_object_creation() to
|
||||||
overload the defaults (it is called after this one).
|
overload the defaults (it is called after this one).
|
||||||
"""
|
"""
|
||||||
super(Character, self).basetype_setup()
|
super(Character, self).basetype_setup()
|
||||||
self.locks.add("get:false()") # noone can pick up the character
|
self.locks.add("get:false()") # noone can pick up the character
|
||||||
self.locks.add("call:false()") # no commands can be called on character from outside
|
self.locks.add("call:false()") # no commands can be called on character from outside
|
||||||
|
|
||||||
# add the default cmdset
|
# add the default cmdset
|
||||||
from settings import CMDSET_DEFAULT
|
from settings import CMDSET_DEFAULT
|
||||||
self.cmdset.add_default(CMDSET_DEFAULT, permanent=True)
|
self.cmdset.add_default(CMDSET_DEFAULT, permanent=True)
|
||||||
# no other character should be able to call commands on the Character.
|
# no other character should be able to call commands on the Character.
|
||||||
self.cmdset.outside_access = False
|
self.cmdset.outside_access = False
|
||||||
|
|
||||||
def at_object_creation(self):
|
def at_object_creation(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -728,8 +728,8 @@ class Character(Object):
|
||||||
the script is permanently stored to this object (the permanent
|
the script is permanently stored to this object (the permanent
|
||||||
keyword creates a script to do this), we should never need to
|
keyword creates a script to do this), we should never need to
|
||||||
do this again for as long as this object exists.
|
do this again for as long as this object exists.
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def at_after_move(self, source_location):
|
def at_after_move(self, source_location):
|
||||||
"Default is to look around after a move."
|
"Default is to look around after a move."
|
||||||
|
|
@ -737,34 +737,34 @@ class Character(Object):
|
||||||
|
|
||||||
def at_disconnect(self):
|
def at_disconnect(self):
|
||||||
"""
|
"""
|
||||||
We stove away the character when logging off, otherwise the character object will
|
We stove away the character when logging off, otherwise the character object will
|
||||||
remain in the room also after the player logged off ("headless", so to say).
|
remain in the room also after the player logged off ("headless", so to say).
|
||||||
"""
|
"""
|
||||||
if self.location: # have to check, in case of multiple connections closing
|
if self.location: # have to check, in case of multiple connections closing
|
||||||
self.location.msg_contents("%s has left the game." % self.name, exclude=[self])
|
self.location.msg_contents("%s has left the game." % self.name, exclude=[self])
|
||||||
self.db.prelogout_location = self.location
|
self.db.prelogout_location = self.location
|
||||||
self.location = None
|
self.location = None
|
||||||
|
|
||||||
def at_post_login(self):
|
def at_post_login(self):
|
||||||
"""
|
"""
|
||||||
This recovers the character again after having been "stoved away" at disconnect.
|
This recovers the character again after having been "stoved away" at disconnect.
|
||||||
"""
|
"""
|
||||||
if self.db.prelogout_location:
|
if self.db.prelogout_location:
|
||||||
# try to recover
|
# try to recover
|
||||||
self.location = self.db.prelogout_location
|
self.location = self.db.prelogout_location
|
||||||
if self.location == None:
|
if self.location == None:
|
||||||
# make sure location is never None (home should always exist)
|
# make sure location is never None (home should always exist)
|
||||||
self.location = self.home
|
self.location = self.home
|
||||||
# save location again to be sure
|
# save location again to be sure
|
||||||
self.db.prelogout_location = self.location
|
self.db.prelogout_location = self.location
|
||||||
|
|
||||||
self.location.msg_contents("%s has entered the game." % self.name, exclude=[self])
|
self.location.msg_contents("%s has entered the game." % self.name, exclude=[self])
|
||||||
self.location.at_object_receive(self, self.location)
|
self.location.at_object_receive(self, self.location)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Base Room object
|
# Base Room object
|
||||||
#
|
#
|
||||||
|
|
||||||
class Room(Object):
|
class Room(Object):
|
||||||
|
|
@ -778,16 +778,16 @@ class Room(Object):
|
||||||
(since default is None anyway)
|
(since default is None anyway)
|
||||||
|
|
||||||
Don't change this, instead edit at_object_creation() to
|
Don't change this, instead edit at_object_creation() to
|
||||||
overload the defaults (it is called after this one).
|
overload the defaults (it is called after this one).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
super(Room, self).basetype_setup()
|
super(Room, self).basetype_setup()
|
||||||
self.locks.add("get:false();puppet:false()") # would be weird to puppet a room ...
|
self.locks.add("get:false();puppet:false()") # would be weird to puppet a room ...
|
||||||
self.location = None
|
self.location = None
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Exits
|
# Exits
|
||||||
#
|
#
|
||||||
|
|
||||||
class Exit(Object):
|
class Exit(Object):
|
||||||
|
|
@ -795,16 +795,16 @@ class Exit(Object):
|
||||||
This is the base exit object - it connects a location to
|
This is the base exit object - it connects a location to
|
||||||
another. This is done by the exit assigning a "command" on itself
|
another. This is done by the exit assigning a "command" on itself
|
||||||
with the same name as the exit object (to do this we need to
|
with the same name as the exit object (to do this we need to
|
||||||
remember to re-create the command when the object is cached since it must be
|
remember to re-create the command when the object is cached since it must be
|
||||||
created dynamically depending on what the exit is called). This
|
created dynamically depending on what the exit is called). This
|
||||||
command (which has a high priority) will thus allow us to traverse exits
|
command (which has a high priority) will thus allow us to traverse exits
|
||||||
simply by giving the exit-object's name on its own.
|
simply by giving the exit-object's name on its own.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Helper classes and methods to implement the Exit. These need not
|
# Helper classes and methods to implement the Exit. These need not
|
||||||
# be overloaded unless one want to change the foundation for how
|
# be overloaded unless one want to change the foundation for how
|
||||||
# Exits work. See the end of the class for hook methods to overload.
|
# Exits work. See the end of the class for hook methods to overload.
|
||||||
|
|
||||||
def create_exit_cmdset(self, exidbobj):
|
def create_exit_cmdset(self, exidbobj):
|
||||||
"""
|
"""
|
||||||
|
|
@ -812,8 +812,8 @@ class Exit(Object):
|
||||||
|
|
||||||
The command of this cmdset has the same name as the Exit object
|
The command of this cmdset has the same name as the Exit object
|
||||||
and allows the exit to react when the player enter the exit's name,
|
and allows the exit to react when the player enter the exit's name,
|
||||||
triggering the movement between rooms.
|
triggering the movement between rooms.
|
||||||
|
|
||||||
Note that exitdbobj is an ObjectDB instance. This is necessary
|
Note that exitdbobj is an ObjectDB instance. This is necessary
|
||||||
for handling reloads and avoid tracebacks if this is called while
|
for handling reloads and avoid tracebacks if this is called while
|
||||||
the typeclass system is rebooting.
|
the typeclass system is rebooting.
|
||||||
|
|
@ -821,9 +821,9 @@ class Exit(Object):
|
||||||
class ExitCommand(command.Command):
|
class ExitCommand(command.Command):
|
||||||
"""
|
"""
|
||||||
This is a command that simply cause the caller
|
This is a command that simply cause the caller
|
||||||
to traverse the object it is attached to.
|
to traverse the object it is attached to.
|
||||||
"""
|
"""
|
||||||
locks = "cmd:all()" # should always be set to this.
|
locks = "cmd:all()" # should always be set to this.
|
||||||
obj = None
|
obj = None
|
||||||
arg_regex=r"\s.*?|$"
|
arg_regex=r"\s.*?|$"
|
||||||
|
|
||||||
|
|
@ -831,15 +831,15 @@ class Exit(Object):
|
||||||
"Default exit traverse if no syscommand is defined."
|
"Default exit traverse if no syscommand is defined."
|
||||||
|
|
||||||
if self.obj.access(self.caller, 'traverse'):
|
if self.obj.access(self.caller, 'traverse'):
|
||||||
# we may traverse the exit.
|
# we may traverse the exit.
|
||||||
|
|
||||||
old_location = None
|
old_location = None
|
||||||
if hasattr(self.caller, "location"):
|
if hasattr(self.caller, "location"):
|
||||||
old_location = self.caller.location
|
old_location = self.caller.location
|
||||||
|
|
||||||
# call pre/post hooks and move object.
|
# call pre/post hooks and move object.
|
||||||
self.obj.at_before_traverse(self.caller)
|
self.obj.at_before_traverse(self.caller)
|
||||||
self.caller.move_to(self.obj.destination)
|
self.caller.move_to(self.obj.destination)
|
||||||
self.obj.at_after_traverse(self.caller, old_location)
|
self.obj.at_after_traverse(self.caller, old_location)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
|
@ -853,7 +853,7 @@ class Exit(Object):
|
||||||
# create an exit command.
|
# create an exit command.
|
||||||
cmd = ExitCommand()
|
cmd = ExitCommand()
|
||||||
cmd.key = exidbobj.db_key.strip().lower()
|
cmd.key = exidbobj.db_key.strip().lower()
|
||||||
cmd.obj = exidbobj
|
cmd.obj = exidbobj
|
||||||
cmd.aliases = exidbobj.aliases
|
cmd.aliases = exidbobj.aliases
|
||||||
cmd.locks = str(exidbobj.locks)
|
cmd.locks = str(exidbobj.locks)
|
||||||
cmd.destination = exidbobj.db_destination
|
cmd.destination = exidbobj.db_destination
|
||||||
|
|
@ -861,18 +861,18 @@ class Exit(Object):
|
||||||
exit_cmdset = cmdset.CmdSet(None)
|
exit_cmdset = cmdset.CmdSet(None)
|
||||||
exit_cmdset.key = '_exitset'
|
exit_cmdset.key = '_exitset'
|
||||||
exit_cmdset.priority = 9
|
exit_cmdset.priority = 9
|
||||||
exit_cmdset.duplicates = True
|
exit_cmdset.duplicates = True
|
||||||
# add command to cmdset
|
# add command to cmdset
|
||||||
exit_cmdset.add(cmd)
|
exit_cmdset.add(cmd)
|
||||||
return exit_cmdset
|
return exit_cmdset
|
||||||
|
|
||||||
# Command hooks
|
# Command hooks
|
||||||
def basetype_setup(self):
|
def basetype_setup(self):
|
||||||
"""
|
"""
|
||||||
Setup exit-security
|
Setup exit-security
|
||||||
|
|
||||||
Don't change this, instead edit at_object_creation() to
|
Don't change this, instead edit at_object_creation() to
|
||||||
overload the default locks (it is called after this one).
|
overload the default locks (it is called after this one).
|
||||||
"""
|
"""
|
||||||
super(Exit, self).basetype_setup()
|
super(Exit, self).basetype_setup()
|
||||||
|
|
||||||
|
|
@ -880,34 +880,34 @@ class Exit(Object):
|
||||||
self.locks.add("puppet:false()") # would be weird to puppet an exit ...
|
self.locks.add("puppet:false()") # would be weird to puppet an exit ...
|
||||||
self.locks.add("traverse:all()") # who can pass through exit by default
|
self.locks.add("traverse:all()") # who can pass through exit by default
|
||||||
self.locks.add("get:false()") # noone can pick up the exit
|
self.locks.add("get:false()") # noone can pick up the exit
|
||||||
|
|
||||||
# an exit should have a destination (this is replaced at creation time)
|
# an exit should have a destination (this is replaced at creation time)
|
||||||
if self.dbobj.location:
|
if self.dbobj.location:
|
||||||
self.destination = self.dbobj.location
|
self.destination = self.dbobj.location
|
||||||
|
|
||||||
def at_cmdset_get(self):
|
def at_cmdset_get(self):
|
||||||
"""
|
"""
|
||||||
Called when the cmdset is requested from this object, just before the cmdset is
|
Called when the cmdset is requested from this object, just before the cmdset is
|
||||||
actually extracted. If no Exit-cmdset is cached, create it now.
|
actually extracted. If no Exit-cmdset is cached, create it now.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if self.ndb.exit_reset or not self.cmdset.has_cmdset("_exitset", must_be_default=True):
|
if self.ndb.exit_reset or not self.cmdset.has_cmdset("_exitset", must_be_default=True):
|
||||||
# we are resetting, or no exit-cmdset was set. Create one dynamically.
|
# we are resetting, or no exit-cmdset was set. Create one dynamically.
|
||||||
self.cmdset.add_default(self.create_exit_cmdset(self.dbobj), permanent=False)
|
self.cmdset.add_default(self.create_exit_cmdset(self.dbobj), permanent=False)
|
||||||
self.ndb.exit_reset = False
|
self.ndb.exit_reset = False
|
||||||
|
|
||||||
# this and other hooks are what usually can be modified safely.
|
# this and other hooks are what usually can be modified safely.
|
||||||
|
|
||||||
def at_object_creation(self):
|
def at_object_creation(self):
|
||||||
"Called once, when object is first created (after basetype_setup)."
|
"Called once, when object is first created (after basetype_setup)."
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def at_failed_traverse(self, traversing_object):
|
def at_failed_traverse(self, traversing_object):
|
||||||
"""
|
"""
|
||||||
This is called if an object fails to traverse this object for some
|
This is called if an object fails to traverse this object for some
|
||||||
reason. It will not be called if the attribute "err_traverse" is defined,
|
reason. It will not be called if the attribute "err_traverse" is defined,
|
||||||
that attribute will then be echoed back instead as a convenient shortcut.
|
that attribute will then be echoed back instead as a convenient shortcut.
|
||||||
|
|
||||||
(See also hooks at_before_traverse and at_after_traverse).
|
(See also hooks at_before_traverse and at_after_traverse).
|
||||||
"""
|
"""
|
||||||
traversing_object.msg("You cannot go there.")
|
traversing_object.msg("You cannot go there.")
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ try:
|
||||||
from django.utils.unittest import TestCase
|
from django.utils.unittest import TestCase
|
||||||
except ImportError:
|
except ImportError:
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
try:
|
try:
|
||||||
from django.utils import unittest
|
from django.utils import unittest
|
||||||
except ImportError:
|
except ImportError:
|
||||||
import unittest
|
import unittest
|
||||||
|
|
@ -47,10 +47,10 @@ class TestObjAttrs(TestCase):
|
||||||
self.obj1.db.testattr = self.obj2
|
self.obj1.db.testattr = self.obj2
|
||||||
self.assertEqual(self.obj2 ,self.obj1.db.testattr)
|
self.assertEqual(self.obj2 ,self.obj1.db.testattr)
|
||||||
self.assertEqual(self.obj2.location, self.obj1.db.testattr.location)
|
self.assertEqual(self.obj2.location, self.obj1.db.testattr.location)
|
||||||
|
|
||||||
def suite():
|
def suite():
|
||||||
"""
|
"""
|
||||||
This function is called automatically by the django test runner.
|
This function is called automatically by the django test runner.
|
||||||
This also runs the command tests defined in src/commands/default/tests.py.
|
This also runs the command tests defined in src/commands/default/tests.py.
|
||||||
"""
|
"""
|
||||||
tsuite = unittest.TestSuite()
|
tsuite = unittest.TestSuite()
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
"""
|
"""
|
||||||
This implements the common managers that are used by the
|
This implements the common managers that are used by the
|
||||||
abstract models in dbobjects.py (and which are thus shared by
|
abstract models in dbobjects.py (and which are thus shared by
|
||||||
all Attributes and TypedObjects).
|
all Attributes and TypedObjects).
|
||||||
"""
|
"""
|
||||||
from functools import update_wrapper
|
from functools import update_wrapper
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
@ -9,15 +9,15 @@ from src.utils import idmapper
|
||||||
from src.utils.utils import make_iter
|
from src.utils.utils import make_iter
|
||||||
#from src.typeclasses import idmap
|
#from src.typeclasses import idmap
|
||||||
|
|
||||||
# Managers
|
# Managers
|
||||||
|
|
||||||
class AttributeManager(models.Manager):
|
class AttributeManager(models.Manager):
|
||||||
"Manager for handling Attributes."
|
"Manager for handling Attributes."
|
||||||
|
|
||||||
def attr_namesearch(self, searchstr, obj, exact_match=True):
|
def attr_namesearch(self, searchstr, obj, exact_match=True):
|
||||||
"""
|
"""
|
||||||
Searches the object's attributes for name matches.
|
Searches the object's attributes for name matches.
|
||||||
|
|
||||||
searchstr: (str) A string to search for.
|
searchstr: (str) A string to search for.
|
||||||
"""
|
"""
|
||||||
# Retrieve the list of attributes for this object.
|
# Retrieve the list of attributes for this object.
|
||||||
|
|
@ -29,21 +29,21 @@ class AttributeManager(models.Manager):
|
||||||
db_key__icontains=searchstr)
|
db_key__icontains=searchstr)
|
||||||
|
|
||||||
#
|
#
|
||||||
# helper functions for the TypedObjectManager.
|
# helper functions for the TypedObjectManager.
|
||||||
#
|
#
|
||||||
|
|
||||||
def returns_typeclass_list(method):
|
def returns_typeclass_list(method):
|
||||||
"""
|
"""
|
||||||
Decorator: Chantes return of the decorated method (which are
|
Decorator: Chantes return of the decorated method (which are
|
||||||
TypeClassed objects) into object_classes(s) instead. Will always
|
TypeClassed objects) into object_classes(s) instead. Will always
|
||||||
return a list (may be empty).
|
return a list (may be empty).
|
||||||
"""
|
"""
|
||||||
def func(self, *args, **kwargs):
|
def func(self, *args, **kwargs):
|
||||||
"decorator. Returns a list."
|
"decorator. Returns a list."
|
||||||
self.__doc__ = method.__doc__
|
self.__doc__ = method.__doc__
|
||||||
matches = method(self, *args, **kwargs)
|
matches = method(self, *args, **kwargs)
|
||||||
return [(hasattr(dbobj, "typeclass") and dbobj.typeclass) or dbobj for dbobj in make_iter(matches)]
|
return [(hasattr(dbobj, "typeclass") and dbobj.typeclass) or dbobj for dbobj in make_iter(matches)]
|
||||||
return update_wrapper(func, method)
|
return update_wrapper(func, method)
|
||||||
|
|
||||||
def returns_typeclass(method):
|
def returns_typeclass(method):
|
||||||
"""
|
"""
|
||||||
|
|
@ -55,8 +55,8 @@ def returns_typeclass(method):
|
||||||
rfunc = returns_typeclass_list(method)
|
rfunc = returns_typeclass_list(method)
|
||||||
try:
|
try:
|
||||||
return rfunc(self, *args, **kwargs)[0]
|
return rfunc(self, *args, **kwargs)[0]
|
||||||
except IndexError:
|
except IndexError:
|
||||||
return None
|
return None
|
||||||
return update_wrapper(func, method)
|
return update_wrapper(func, method)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -64,20 +64,20 @@ def returns_typeclass(method):
|
||||||
#class TypedObjectManager(models.Manager):
|
#class TypedObjectManager(models.Manager):
|
||||||
class TypedObjectManager(idmapper.manager.SharedMemoryManager):
|
class TypedObjectManager(idmapper.manager.SharedMemoryManager):
|
||||||
"""
|
"""
|
||||||
Common ObjectManager for all dbobjects.
|
Common ObjectManager for all dbobjects.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def dbref(self, dbref):
|
def dbref(self, dbref):
|
||||||
"""
|
"""
|
||||||
Valid forms of dbref (database reference number)
|
Valid forms of dbref (database reference number)
|
||||||
are either a string '#N' or an integer N.
|
are either a string '#N' or an integer N.
|
||||||
Output is the integer part.
|
Output is the integer part.
|
||||||
"""
|
"""
|
||||||
if isinstance(dbref, basestring):
|
if isinstance(dbref, basestring):
|
||||||
dbref = dbref.lstrip('#')
|
dbref = dbref.lstrip('#')
|
||||||
try:
|
try:
|
||||||
if int(dbref) < 1:
|
if int(dbref) < 1:
|
||||||
return None
|
return None
|
||||||
except Exception:
|
except Exception:
|
||||||
return None
|
return None
|
||||||
return dbref
|
return dbref
|
||||||
|
|
@ -92,7 +92,7 @@ class TypedObjectManager(idmapper.manager.SharedMemoryManager):
|
||||||
"""
|
"""
|
||||||
self.filter()
|
self.filter()
|
||||||
|
|
||||||
|
|
||||||
@returns_typeclass
|
@returns_typeclass
|
||||||
def dbref_search(self, dbref):
|
def dbref_search(self, dbref):
|
||||||
"""
|
"""
|
||||||
|
|
@ -131,25 +131,24 @@ class TypedObjectManager(idmapper.manager.SharedMemoryManager):
|
||||||
"""
|
"""
|
||||||
Returns a dictionary with all the typeclasses active in-game
|
Returns a dictionary with all the typeclasses active in-game
|
||||||
as well as the number of such objects defined (i.e. the number
|
as well as the number of such objects defined (i.e. the number
|
||||||
of database object having that typeclass set on themselves).
|
of database object having that typeclass set on themselves).
|
||||||
"""
|
"""
|
||||||
dbtotals = {}
|
dbtotals = {}
|
||||||
typeclass_paths = set(self.values_list('db_typeclass_path', flat=True))
|
typeclass_paths = set(self.values_list('db_typeclass_path', flat=True))
|
||||||
for typeclass_path in typeclass_paths:
|
for typeclass_path in typeclass_paths:
|
||||||
dbtotals[typeclass_path] = \
|
dbtotals[typeclass_path] = \
|
||||||
self.filter(db_typeclass_path=typeclass_path).count()
|
self.filter(db_typeclass_path=typeclass_path).count()
|
||||||
return dbtotals
|
return dbtotals
|
||||||
|
|
||||||
@returns_typeclass_list
|
@returns_typeclass_list
|
||||||
def typeclass_search(self, typeclass):
|
def typeclass_search(self, typeclass):
|
||||||
"""
|
"""
|
||||||
Searches through all objects returning those which has a certain
|
Searches through all objects returning those which has a certain
|
||||||
typeclass. If location is set, limit search to objects in
|
typeclass. If location is set, limit search to objects in
|
||||||
that location.
|
that location.
|
||||||
"""
|
"""
|
||||||
if callable(typeclass):
|
if callable(typeclass):
|
||||||
cls = typeclass.__class__
|
cls = typeclass.__class__
|
||||||
typeclass = "%s.%s" % (cls.__module__, cls.__name__)
|
typeclass = "%s.%s" % (cls.__module__, cls.__name__)
|
||||||
o_query = self.filter(db_typeclass_path__exact=typeclass)
|
o_query = self.filter(db_typeclass_path__exact=typeclass)
|
||||||
return o_query
|
return o_query
|
||||||
|
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -7,7 +7,7 @@ with a 'normal' Python class. The only restrictions is that
|
||||||
the typeclass must inherit from TypeClass and not reimplement
|
the typeclass must inherit from TypeClass and not reimplement
|
||||||
the get/setters defined below. There are also a few properties
|
the get/setters defined below. There are also a few properties
|
||||||
that are protected, so as to not overwrite property names
|
that are protected, so as to not overwrite property names
|
||||||
used by the typesystem or django itself.
|
used by the typesystem or django itself.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from src.utils.logger import log_trace, log_errmsg
|
from src.utils.logger import log_trace, log_errmsg
|
||||||
|
|
@ -45,12 +45,9 @@ class MetaTypeClass(type):
|
||||||
mcs.typename = mcs.__name__
|
mcs.typename = mcs.__name__
|
||||||
mcs.path = "%s.%s" % (mcs.__module__, mcs.__name__)
|
mcs.path = "%s.%s" % (mcs.__module__, mcs.__name__)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def __str__(cls):
|
def __str__(cls):
|
||||||
return "%s" % cls.__name__
|
return "%s" % cls.__name__
|
||||||
|
|
||||||
class TypeClass(object):
|
class TypeClass(object):
|
||||||
"""
|
"""
|
||||||
This class implements a 'typeclass' object. This is connected
|
This class implements a 'typeclass' object. This is connected
|
||||||
|
|
@ -58,14 +55,14 @@ class TypeClass(object):
|
||||||
the TypeClass allows for all customization.
|
the TypeClass allows for all customization.
|
||||||
Most of the time this means that the admin never has to
|
Most of the time this means that the admin never has to
|
||||||
worry about database access but only deal with extending
|
worry about database access but only deal with extending
|
||||||
TypeClasses to create diverse objects in the game.
|
TypeClasses to create diverse objects in the game.
|
||||||
|
|
||||||
The ObjectType class has all functionality for wrapping a
|
The ObjectType class has all functionality for wrapping a
|
||||||
database object transparently.
|
database object transparently.
|
||||||
|
|
||||||
It's up to its child classes to implement eventual custom hooks
|
It's up to its child classes to implement eventual custom hooks
|
||||||
and other functions called by the engine.
|
and other functions called by the engine.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
__metaclass__ = MetaTypeClass
|
__metaclass__ = MetaTypeClass
|
||||||
|
|
||||||
|
|
@ -73,8 +70,8 @@ class TypeClass(object):
|
||||||
"""
|
"""
|
||||||
Initialize the object class. There are two ways to call this class.
|
Initialize the object class. There are two ways to call this class.
|
||||||
o = object_class(dbobj) : this is used to initialize dbobj with the class name
|
o = object_class(dbobj) : this is used to initialize dbobj with the class name
|
||||||
o = dbobj.object_class(dbobj) : this is used when dbobj.object_class is already set.
|
o = dbobj.object_class(dbobj) : this is used when dbobj.object_class is already set.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
# typecheck of dbobj - we can't allow it to be added here
|
# typecheck of dbobj - we can't allow it to be added here
|
||||||
# unless it's really a TypedObject.
|
# unless it's really a TypedObject.
|
||||||
|
|
@ -84,7 +81,7 @@ class TypeClass(object):
|
||||||
raise Exception("dbobj is not a TypedObject: %s: %s" % (dbobj_cls, dbobj_mro))
|
raise Exception("dbobj is not a TypedObject: %s: %s" % (dbobj_cls, dbobj_mro))
|
||||||
|
|
||||||
# store the reference to the database model instance
|
# store the reference to the database model instance
|
||||||
SA(self, 'dbobj', dbobj)
|
SA(self, 'dbobj', dbobj)
|
||||||
|
|
||||||
def __getattribute__(self, propname):
|
def __getattribute__(self, propname):
|
||||||
"""
|
"""
|
||||||
|
|
@ -93,7 +90,7 @@ class TypeClass(object):
|
||||||
self.dbobj. Note that dbobj properties have
|
self.dbobj. Note that dbobj properties have
|
||||||
priority, so if you define a same-named
|
priority, so if you define a same-named
|
||||||
property on the class, it will NOT be
|
property on the class, it will NOT be
|
||||||
accessible through getattr.
|
accessible through getattr.
|
||||||
"""
|
"""
|
||||||
if propname == 'dbobj':
|
if propname == 'dbobj':
|
||||||
return GA(self, 'dbobj')
|
return GA(self, 'dbobj')
|
||||||
|
|
@ -101,7 +98,7 @@ class TypeClass(object):
|
||||||
# python specials are parsed as-is (otherwise things like
|
# python specials are parsed as-is (otherwise things like
|
||||||
# isinstance() fail to identify the typeclass)
|
# isinstance() fail to identify the typeclass)
|
||||||
return GA(self, propname)
|
return GA(self, propname)
|
||||||
#print "get %s (dbobj:%s)" % (propname, type(dbobj))
|
#print "get %s (dbobj:%s)" % (propname, type(dbobj))
|
||||||
try:
|
try:
|
||||||
return GA(self, propname)
|
return GA(self, propname)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
|
|
@ -109,7 +106,7 @@ class TypeClass(object):
|
||||||
dbobj = GA(self, 'dbobj')
|
dbobj = GA(self, 'dbobj')
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
log_trace("Typeclass CRITICAL ERROR! dbobj not found for Typeclass %s!" % self)
|
log_trace("Typeclass CRITICAL ERROR! dbobj not found for Typeclass %s!" % self)
|
||||||
raise
|
raise
|
||||||
try:
|
try:
|
||||||
return GA(dbobj, propname)
|
return GA(dbobj, propname)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
|
|
@ -118,31 +115,31 @@ class TypeClass(object):
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
string = "Object: '%s' not found on %s(%s), nor on its typeclass %s."
|
string = "Object: '%s' not found on %s(%s), nor on its typeclass %s."
|
||||||
raise AttributeError(string % (propname, dbobj, dbobj.dbref, dbobj.typeclass_path))
|
raise AttributeError(string % (propname, dbobj, dbobj.dbref, dbobj.typeclass_path))
|
||||||
|
|
||||||
def __setattr__(self, propname, value):
|
def __setattr__(self, propname, value):
|
||||||
"""
|
"""
|
||||||
Transparently save data to the dbobj object in
|
Transparently save data to the dbobj object in
|
||||||
all situations. Note that this does not
|
all situations. Note that this does not
|
||||||
necessarily mean storing it to the database
|
necessarily mean storing it to the database
|
||||||
unless data is stored into a propname
|
unless data is stored into a propname
|
||||||
corresponding to a field on ObjectDB model.
|
corresponding to a field on ObjectDB model.
|
||||||
"""
|
"""
|
||||||
#print "set %s -> %s" % (propname, value)
|
#print "set %s -> %s" % (propname, value)
|
||||||
if propname in PROTECTED:
|
if propname in PROTECTED:
|
||||||
string = "%s: '%s' is a protected attribute name."
|
string = "%s: '%s' is a protected attribute name."
|
||||||
string += " (protected: [%s])" % (", ".join(PROTECTED))
|
string += " (protected: [%s])" % (", ".join(PROTECTED))
|
||||||
log_errmsg(string % (self.name, propname))
|
log_errmsg(string % (self.name, propname))
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
dbobj = GA(self, 'dbobj')
|
dbobj = GA(self, 'dbobj')
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
dbobj = None
|
dbobj = None
|
||||||
log_trace("This is probably due to an unsafe reload.")
|
log_trace("This is probably due to an unsafe reload.")
|
||||||
|
|
||||||
if dbobj:
|
if dbobj:
|
||||||
try:
|
try:
|
||||||
# only set value on propname if propname already exists
|
# only set value on propname if propname already exists
|
||||||
# on dbobj. __getattribute__ will raise attribute error otherwise.
|
# on dbobj. __getattribute__ will raise attribute error otherwise.
|
||||||
GA(dbobj, propname)
|
GA(dbobj, propname)
|
||||||
SA(dbobj, propname, value)
|
SA(dbobj, propname, value)
|
||||||
|
|
@ -154,25 +151,25 @@ class TypeClass(object):
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
"""
|
"""
|
||||||
dbobj-recognized comparison
|
dbobj-recognized comparison
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
return other == self or other == GA(self, dbobj) or other == GA(self, dbobj).user
|
return other == self or other == GA(self, dbobj) or other == GA(self, dbobj).user
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
# if self.dbobj.user fails it means the two previous comparisons failed already
|
# if self.dbobj.user fails it means the two previous comparisons failed already
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def __delattr__(self, propname):
|
def __delattr__(self, propname):
|
||||||
"""
|
"""
|
||||||
Transparently deletes data from the typeclass or dbobj by first searching on the typeclass,
|
Transparently deletes data from the typeclass or dbobj by first searching on the typeclass,
|
||||||
secondly on the dbobj.db.
|
secondly on the dbobj.db.
|
||||||
Will not allow deletion of properties stored directly on dbobj.
|
Will not allow deletion of properties stored directly on dbobj.
|
||||||
"""
|
"""
|
||||||
if propname in PROTECTED:
|
if propname in PROTECTED:
|
||||||
string = "%s: '%s' is a protected attribute name."
|
string = "%s: '%s' is a protected attribute name."
|
||||||
string += " (protected: [%s])" % (", ".join(PROTECTED))
|
string += " (protected: [%s])" % (", ".join(PROTECTED))
|
||||||
log_errmsg(string % (self.name, propname))
|
log_errmsg(string % (self.name, propname))
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
DA(self, propname)
|
DA(self, propname)
|
||||||
|
|
@ -181,10 +178,10 @@ class TypeClass(object):
|
||||||
try:
|
try:
|
||||||
dbobj = GA(self, 'dbobj')
|
dbobj = GA(self, 'dbobj')
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
log_trace("This is probably due to an unsafe reload.")
|
log_trace("This is probably due to an unsafe reload.")
|
||||||
return # ignore delete
|
return # ignore delete
|
||||||
try:
|
try:
|
||||||
dbobj.del_attribute_raise(propname)
|
dbobj.del_attribute_raise(propname)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
string = "Object: '%s' not found on %s(%s), nor on its typeclass %s."
|
string = "Object: '%s' not found on %s(%s), nor on its typeclass %s."
|
||||||
raise AttributeError(string % (propname, dbobj,
|
raise AttributeError(string % (propname, dbobj,
|
||||||
|
|
|
||||||
|
|
@ -7,19 +7,19 @@ a higher layer module.
|
||||||
"""
|
"""
|
||||||
from traceback import format_exc
|
from traceback import format_exc
|
||||||
from twisted.python import log
|
from twisted.python import log
|
||||||
from src.utils import utils
|
from src.utils import utils
|
||||||
|
|
||||||
def log_trace(errmsg=None):
|
def log_trace(errmsg=None):
|
||||||
"""
|
"""
|
||||||
Log a traceback to the log. This should be called
|
Log a traceback to the log. This should be called
|
||||||
from within an exception. errmsg is optional and
|
from within an exception. errmsg is optional and
|
||||||
adds an extra line with added info.
|
adds an extra line with added info.
|
||||||
"""
|
"""
|
||||||
tracestring = format_exc()
|
tracestring = format_exc()
|
||||||
try:
|
try:
|
||||||
if tracestring:
|
if tracestring:
|
||||||
for line in tracestring.splitlines():
|
for line in tracestring.splitlines():
|
||||||
log.msg('[::] %s' % line)
|
log.msg('[::] %s' % line)
|
||||||
if errmsg:
|
if errmsg:
|
||||||
try:
|
try:
|
||||||
errmsg = utils.to_str(errmsg)
|
errmsg = utils.to_str(errmsg)
|
||||||
|
|
@ -29,7 +29,7 @@ def log_trace(errmsg=None):
|
||||||
log.msg('[EE] %s' % line)
|
log.msg('[EE] %s' % line)
|
||||||
except Exception:
|
except Exception:
|
||||||
log.msg('[EE] %s' % errmsg )
|
log.msg('[EE] %s' % errmsg )
|
||||||
|
|
||||||
def log_errmsg(errmsg):
|
def log_errmsg(errmsg):
|
||||||
"""
|
"""
|
||||||
Prints/logs an error message to the server log.
|
Prints/logs an error message to the server log.
|
||||||
|
|
@ -37,7 +37,7 @@ def log_errmsg(errmsg):
|
||||||
errormsg: (string) The message to be logged.
|
errormsg: (string) The message to be logged.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
errmsg = utils.to_str(errmsg)
|
errmsg = utils.to_str(errmsg)
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
errmsg = str(e)
|
errmsg = str(e)
|
||||||
for line in errmsg.splitlines():
|
for line in errmsg.splitlines():
|
||||||
|
|
@ -47,7 +47,7 @@ def log_errmsg(errmsg):
|
||||||
def log_warnmsg(warnmsg):
|
def log_warnmsg(warnmsg):
|
||||||
"""
|
"""
|
||||||
Prints/logs any warnings that aren't critical but should be noted.
|
Prints/logs any warnings that aren't critical but should be noted.
|
||||||
|
|
||||||
warnmsg: (string) The message to be logged.
|
warnmsg: (string) The message to be logged.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
General helper functions that don't fit neatly under any given category.
|
General helper functions that don't fit neatly under any given category.
|
||||||
|
|
||||||
They provide some useful string and conversion methods that might
|
They provide some useful string and conversion methods that might
|
||||||
be of use when designing your own game.
|
be of use when designing your own game.
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
@ -27,7 +27,7 @@ def is_iter(iterable):
|
||||||
def make_iter(obj):
|
def make_iter(obj):
|
||||||
"Makes sure that the object is always iterable."
|
"Makes sure that the object is always iterable."
|
||||||
if not hasattr(obj, '__iter__'): return [obj]
|
if not hasattr(obj, '__iter__'): return [obj]
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
def fill(text, width=78, indent=0):
|
def fill(text, width=78, indent=0):
|
||||||
"""
|
"""
|
||||||
|
|
@ -53,8 +53,8 @@ def crop(text, width=78, suffix="[...]"):
|
||||||
ltext = len(to_str(text))
|
ltext = len(to_str(text))
|
||||||
if ltext <= width:
|
if ltext <= width:
|
||||||
return text
|
return text
|
||||||
else:
|
else:
|
||||||
lsuffix = len(suffix)
|
lsuffix = len(suffix)
|
||||||
return "%s%s" % (text[:width-lsuffix], suffix)
|
return "%s%s" % (text[:width-lsuffix], suffix)
|
||||||
|
|
||||||
def dedent(text):
|
def dedent(text):
|
||||||
|
|
@ -63,7 +63,7 @@ def dedent(text):
|
||||||
of a paragraph. This is useful for preserving
|
of a paragraph. This is useful for preserving
|
||||||
triple-quoted string indentation while still
|
triple-quoted string indentation while still
|
||||||
shifting it all to be next to the left edge of
|
shifting it all to be next to the left edge of
|
||||||
the display.
|
the display.
|
||||||
"""
|
"""
|
||||||
if not text:
|
if not text:
|
||||||
return ""
|
return ""
|
||||||
|
|
@ -92,11 +92,11 @@ def wildcard_to_regexp(instring):
|
||||||
regexp_string += "$"
|
regexp_string += "$"
|
||||||
|
|
||||||
return regexp_string
|
return regexp_string
|
||||||
|
|
||||||
def time_format(seconds, style=0):
|
def time_format(seconds, style=0):
|
||||||
"""
|
"""
|
||||||
Function to return a 'prettified' version of a value in seconds.
|
Function to return a 'prettified' version of a value in seconds.
|
||||||
|
|
||||||
Style 0: 1d 08:30
|
Style 0: 1d 08:30
|
||||||
Style 1: 1d
|
Style 1: 1d
|
||||||
Style 2: 1 day, 8 hours, 30 minutes, 10 seconds
|
Style 2: 1 day, 8 hours, 30 minutes, 10 seconds
|
||||||
|
|
@ -105,15 +105,15 @@ def time_format(seconds, style=0):
|
||||||
seconds = 0
|
seconds = 0
|
||||||
else:
|
else:
|
||||||
# We'll just use integer math, no need for decimal precision.
|
# We'll just use integer math, no need for decimal precision.
|
||||||
seconds = int(seconds)
|
seconds = int(seconds)
|
||||||
|
|
||||||
days = seconds / 86400
|
days = seconds / 86400
|
||||||
seconds -= days * 86400
|
seconds -= days * 86400
|
||||||
hours = seconds / 3600
|
hours = seconds / 3600
|
||||||
seconds -= hours * 3600
|
seconds -= hours * 3600
|
||||||
minutes = seconds / 60
|
minutes = seconds / 60
|
||||||
seconds -= minutes * 60
|
seconds -= minutes * 60
|
||||||
|
|
||||||
if style is 0:
|
if style is 0:
|
||||||
"""
|
"""
|
||||||
Standard colon-style output.
|
Standard colon-style output.
|
||||||
|
|
@ -122,7 +122,7 @@ def time_format(seconds, style=0):
|
||||||
retval = '%id %02i:%02i' % (days, hours, minutes,)
|
retval = '%id %02i:%02i' % (days, hours, minutes,)
|
||||||
else:
|
else:
|
||||||
retval = '%02i:%02i' % (hours, minutes,)
|
retval = '%02i:%02i' % (hours, minutes,)
|
||||||
|
|
||||||
return retval
|
return retval
|
||||||
elif style is 1:
|
elif style is 1:
|
||||||
"""
|
"""
|
||||||
|
|
@ -155,8 +155,8 @@ def time_format(seconds, style=0):
|
||||||
if minutes == 1:
|
if minutes == 1:
|
||||||
minutes_str = '%i minute ' % minutes
|
minutes_str = '%i minute ' % minutes
|
||||||
else:
|
else:
|
||||||
minutes_str = '%i minutes ' % minutes
|
minutes_str = '%i minutes ' % minutes
|
||||||
retval = '%s%s%s' % (days_str, hours_str, minutes_str)
|
retval = '%s%s%s' % (days_str, hours_str, minutes_str)
|
||||||
elif style is 3:
|
elif style is 3:
|
||||||
"""
|
"""
|
||||||
Full-detailed, long-winded format. Includes seconds.
|
Full-detailed, long-winded format. Includes seconds.
|
||||||
|
|
@ -176,20 +176,20 @@ def time_format(seconds, style=0):
|
||||||
if minutes == 1:
|
if minutes == 1:
|
||||||
minutes_str = '%i minute ' % minutes
|
minutes_str = '%i minute ' % minutes
|
||||||
else:
|
else:
|
||||||
minutes_str = '%i minutes ' % minutes
|
minutes_str = '%i minutes ' % minutes
|
||||||
if minutes or seconds > 0:
|
if minutes or seconds > 0:
|
||||||
if seconds == 1:
|
if seconds == 1:
|
||||||
seconds_str = '%i second ' % seconds
|
seconds_str = '%i second ' % seconds
|
||||||
else:
|
else:
|
||||||
seconds_str = '%i seconds ' % seconds
|
seconds_str = '%i seconds ' % seconds
|
||||||
retval = '%s%s%s%s' % (days_str, hours_str, minutes_str, seconds_str)
|
retval = '%s%s%s%s' % (days_str, hours_str, minutes_str, seconds_str)
|
||||||
|
|
||||||
return retval
|
return retval
|
||||||
|
|
||||||
def datetime_format(dtobj):
|
def datetime_format(dtobj):
|
||||||
"""
|
"""
|
||||||
Takes a datetime object instance (e.g. from django's DateTimeField)
|
Takes a datetime object instance (e.g. from django's DateTimeField)
|
||||||
and returns a string describing how long ago that date was.
|
and returns a string describing how long ago that date was.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
year, month, day = dtobj.year, dtobj.month, dtobj.day
|
year, month, day = dtobj.year, dtobj.month, dtobj.day
|
||||||
|
|
@ -197,7 +197,7 @@ def datetime_format(dtobj):
|
||||||
now = datetime.datetime.now()
|
now = datetime.datetime.now()
|
||||||
|
|
||||||
if year < now.year:
|
if year < now.year:
|
||||||
# another year
|
# another year
|
||||||
timestring = str(dtobj.date())
|
timestring = str(dtobj.date())
|
||||||
elif dtobj.date() < now.date():
|
elif dtobj.date() < now.date():
|
||||||
# another date, same year
|
# another date, same year
|
||||||
|
|
@ -205,9 +205,9 @@ def datetime_format(dtobj):
|
||||||
elif hour < now.hour - 1:
|
elif hour < now.hour - 1:
|
||||||
# same day, more than 1 hour ago
|
# same day, more than 1 hour ago
|
||||||
timestring = "%02i:%02i" % (hour, minute)
|
timestring = "%02i:%02i" % (hour, minute)
|
||||||
else:
|
else:
|
||||||
# same day, less than 1 hour ago
|
# same day, less than 1 hour ago
|
||||||
timestring = "%02i:%02i:%02i" % (hour, minute, second)
|
timestring = "%02i:%02i:%02i" % (hour, minute, second)
|
||||||
return timestring
|
return timestring
|
||||||
|
|
||||||
def host_os_is(osname):
|
def host_os_is(osname):
|
||||||
|
|
@ -223,12 +223,12 @@ def get_evennia_version():
|
||||||
Check for the evennia version info.
|
Check for the evennia version info.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
with open(settings.BASE_PATH + os.sep + "VERSION") as f:
|
with open(settings.BASE_PATH + os.sep + "VERSION") as f:
|
||||||
return "%s-r%s" % (f.read().strip(), os.popen("hg id -i").read().strip())
|
return "%s-r%s" % (f.read().strip(), os.popen("hg id -i").read().strip())
|
||||||
return
|
return
|
||||||
except IOError:
|
except IOError:
|
||||||
return "Unknown version"
|
return "Unknown version"
|
||||||
|
|
||||||
def pypath_to_realpath(python_path, file_ending='.py'):
|
def pypath_to_realpath(python_path, file_ending='.py'):
|
||||||
"""
|
"""
|
||||||
Converts a path on dot python form (e.g. 'src.objects.models') to
|
Converts a path on dot python form (e.g. 'src.objects.models') to
|
||||||
|
|
@ -238,17 +238,17 @@ def pypath_to_realpath(python_path, file_ending='.py'):
|
||||||
pathsplit = python_path.strip().split('.')
|
pathsplit = python_path.strip().split('.')
|
||||||
if not pathsplit:
|
if not pathsplit:
|
||||||
return python_path
|
return python_path
|
||||||
path = settings.BASE_PATH
|
path = settings.BASE_PATH
|
||||||
for directory in pathsplit:
|
for directory in pathsplit:
|
||||||
path = os.path.join(path, directory)
|
path = os.path.join(path, directory)
|
||||||
if file_ending:
|
if file_ending:
|
||||||
return "%s%s" % (path, file_ending)
|
return "%s%s" % (path, file_ending)
|
||||||
return path
|
return path
|
||||||
|
|
||||||
def dbref(dbref):
|
def dbref(dbref):
|
||||||
"""
|
"""
|
||||||
Converts/checks if input is a valid dbref Valid forms of dbref
|
Converts/checks if input is a valid dbref Valid forms of dbref
|
||||||
(database reference number) are either a string '#N' or
|
(database reference number) are either a string '#N' or
|
||||||
an integer N. Output is the integer part.
|
an integer N. Output is the integer part.
|
||||||
"""
|
"""
|
||||||
if isinstance(dbref, basestring):
|
if isinstance(dbref, basestring):
|
||||||
|
|
@ -256,11 +256,11 @@ def dbref(dbref):
|
||||||
try:
|
try:
|
||||||
dbref = int(dbref)
|
dbref = int(dbref)
|
||||||
if dbref < 1:
|
if dbref < 1:
|
||||||
return None
|
return None
|
||||||
except Exception:
|
except Exception:
|
||||||
return None
|
return None
|
||||||
return dbref
|
return dbref
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def to_unicode(obj, encoding='utf-8', force_string=False):
|
def to_unicode(obj, encoding='utf-8', force_string=False):
|
||||||
"""
|
"""
|
||||||
|
|
@ -268,7 +268,7 @@ def to_unicode(obj, encoding='utf-8', force_string=False):
|
||||||
one needs to encode it back to utf-8 before writing to disk or
|
one needs to encode it back to utf-8 before writing to disk or
|
||||||
printing. Note that non-string objects are let through without
|
printing. Note that non-string objects are let through without
|
||||||
conversion - this is important for e.g. Attributes. Use
|
conversion - this is important for e.g. Attributes. Use
|
||||||
force_string to enforce conversion of objects to string. .
|
force_string to enforce conversion of objects to string. .
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if force_string and not isinstance(obj, basestring):
|
if force_string and not isinstance(obj, basestring):
|
||||||
|
|
@ -279,28 +279,28 @@ def to_unicode(obj, encoding='utf-8', force_string=False):
|
||||||
elif hasattr(obj, '__unicode__'):
|
elif hasattr(obj, '__unicode__'):
|
||||||
obj = obj.__unicode__()
|
obj = obj.__unicode__()
|
||||||
else:
|
else:
|
||||||
# last resort
|
# last resort
|
||||||
obj = str(obj)
|
obj = str(obj)
|
||||||
|
|
||||||
if isinstance(obj, basestring) and not isinstance(obj, unicode):
|
if isinstance(obj, basestring) and not isinstance(obj, unicode):
|
||||||
try:
|
try:
|
||||||
obj = unicode(obj, encoding)
|
obj = unicode(obj, encoding)
|
||||||
return obj
|
return obj
|
||||||
except UnicodeDecodeError:
|
except UnicodeDecodeError:
|
||||||
for alt_encoding in ENCODINGS:
|
for alt_encoding in ENCODINGS:
|
||||||
try:
|
try:
|
||||||
obj = unicode(obj, alt_encoding)
|
obj = unicode(obj, alt_encoding)
|
||||||
return obj
|
return obj
|
||||||
except UnicodeDecodeError:
|
except UnicodeDecodeError:
|
||||||
pass
|
pass
|
||||||
raise Exception("Error: '%s' contains invalid character(s) not in %s." % (obj, encoding))
|
raise Exception("Error: '%s' contains invalid character(s) not in %s." % (obj, encoding))
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
def to_str(obj, encoding='utf-8', force_string=False):
|
def to_str(obj, encoding='utf-8', force_string=False):
|
||||||
"""
|
"""
|
||||||
This encodes a unicode string back to byte-representation,
|
This encodes a unicode string back to byte-representation,
|
||||||
for printing, writing to disk etc. Note that non-string
|
for printing, writing to disk etc. Note that non-string
|
||||||
objects are let through without modification - this is
|
objects are let through without modification - this is
|
||||||
required e.g. for Attributes. Use force_string to force
|
required e.g. for Attributes. Use force_string to force
|
||||||
conversion of objects to strings.
|
conversion of objects to strings.
|
||||||
"""
|
"""
|
||||||
|
|
@ -313,7 +313,7 @@ def to_str(obj, encoding='utf-8', force_string=False):
|
||||||
elif hasattr(obj, '__unicode__'):
|
elif hasattr(obj, '__unicode__'):
|
||||||
obj = obj.__unicode__()
|
obj = obj.__unicode__()
|
||||||
else:
|
else:
|
||||||
# last resort
|
# last resort
|
||||||
obj = str(obj)
|
obj = str(obj)
|
||||||
|
|
||||||
if isinstance(obj, basestring) and isinstance(obj, unicode):
|
if isinstance(obj, basestring) and isinstance(obj, unicode):
|
||||||
|
|
@ -334,14 +334,14 @@ def validate_email_address(emailaddress):
|
||||||
"""
|
"""
|
||||||
Checks if an email address is syntactically correct.
|
Checks if an email address is syntactically correct.
|
||||||
|
|
||||||
(This snippet was adapted from
|
(This snippet was adapted from
|
||||||
http://commandline.org.uk/python/email-syntax-check.)
|
http://commandline.org.uk/python/email-syntax-check.)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
emailaddress = r"%s" % emailaddress
|
emailaddress = r"%s" % emailaddress
|
||||||
|
|
||||||
domains = ("aero", "asia", "biz", "cat", "com", "coop",
|
domains = ("aero", "asia", "biz", "cat", "com", "coop",
|
||||||
"edu", "gov", "info", "int", "jobs", "mil", "mobi", "museum",
|
"edu", "gov", "info", "int", "jobs", "mil", "mobi", "museum",
|
||||||
"name", "net", "org", "pro", "tel", "travel")
|
"name", "net", "org", "pro", "tel", "travel")
|
||||||
|
|
||||||
# Email address must be more than 7 characters in total.
|
# Email address must be more than 7 characters in total.
|
||||||
|
|
@ -372,11 +372,11 @@ def validate_email_address(emailaddress):
|
||||||
|
|
||||||
def inherits_from(obj, parent):
|
def inherits_from(obj, parent):
|
||||||
"""
|
"""
|
||||||
Takes an object and tries to determine if it inherits at any distance
|
Takes an object and tries to determine if it inherits at any distance
|
||||||
from parent. What differs this function from e.g. isinstance()
|
from parent. What differs this function from e.g. isinstance()
|
||||||
is that obj may be both an instance and a class, and parent
|
is that obj may be both an instance and a class, and parent
|
||||||
< may be an instance, a class, or the python path to a class (counting
|
< may be an instance, a class, or the python path to a class (counting
|
||||||
from the evennia root directory).
|
from the evennia root directory).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if callable(obj):
|
if callable(obj):
|
||||||
|
|
@ -384,7 +384,7 @@ def inherits_from(obj, parent):
|
||||||
obj_paths = ["%s.%s" % (mod.__module__, mod.__name__) for mod in obj.mro()]
|
obj_paths = ["%s.%s" % (mod.__module__, mod.__name__) for mod in obj.mro()]
|
||||||
else:
|
else:
|
||||||
obj_paths = ["%s.%s" % (mod.__module__, mod.__name__) for mod in obj.__class__.mro()]
|
obj_paths = ["%s.%s" % (mod.__module__, mod.__name__) for mod in obj.__class__.mro()]
|
||||||
|
|
||||||
if isinstance(parent, basestring):
|
if isinstance(parent, basestring):
|
||||||
# a given string path, for direct matching
|
# a given string path, for direct matching
|
||||||
parent_path = parent
|
parent_path = parent
|
||||||
|
|
@ -400,56 +400,56 @@ def format_table(table, extra_space=1):
|
||||||
"""
|
"""
|
||||||
Takes a table of collumns: [[val,val,val,...], [val,val,val,...], ...]
|
Takes a table of collumns: [[val,val,val,...], [val,val,val,...], ...]
|
||||||
where each val will be placed on a separate row in the column. All
|
where each val will be placed on a separate row in the column. All
|
||||||
collumns must have the same number of rows (some positions may be
|
collumns must have the same number of rows (some positions may be
|
||||||
empty though).
|
empty though).
|
||||||
|
|
||||||
The function formats the columns to be as wide as the widest member
|
The function formats the columns to be as wide as the widest member
|
||||||
of each column.
|
of each column.
|
||||||
|
|
||||||
extra_space defines how much extra padding should minimum be left between
|
|
||||||
collumns.
|
|
||||||
|
|
||||||
print the resulting list e.g. with
|
extra_space defines how much extra padding should minimum be left between
|
||||||
|
collumns.
|
||||||
|
|
||||||
|
print the resulting list e.g. with
|
||||||
|
|
||||||
for ir, row in enumarate(ftable):
|
for ir, row in enumarate(ftable):
|
||||||
if ir == 0:
|
if ir == 0:
|
||||||
# make first row white
|
# make first row white
|
||||||
string += "\n{w" + ""join(row) + "{n"
|
string += "\n{w" + ""join(row) + "{n"
|
||||||
else:
|
else:
|
||||||
string += "\n" + "".join(row)
|
string += "\n" + "".join(row)
|
||||||
print string
|
print string
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if not table:
|
if not table:
|
||||||
return [[]]
|
return [[]]
|
||||||
|
|
||||||
max_widths = [max([len(str(val)) for val in col]) for col in table]
|
max_widths = [max([len(str(val)) for val in col]) for col in table]
|
||||||
ftable = []
|
ftable = []
|
||||||
for irow in range(len(table[0])):
|
for irow in range(len(table[0])):
|
||||||
ftable.append([str(col[irow]).ljust(max_widths[icol]) + " " * extra_space
|
ftable.append([str(col[irow]).ljust(max_widths[icol]) + " " * extra_space
|
||||||
for icol, col in enumerate(table)])
|
for icol, col in enumerate(table)])
|
||||||
return ftable
|
return ftable
|
||||||
|
|
||||||
def run_async(async_func, at_return=None, at_err=None):
|
def run_async(async_func, at_return=None, at_err=None):
|
||||||
"""
|
"""
|
||||||
This wrapper will use Twisted's asynchronous features to run a slow
|
This wrapper will use Twisted's asynchronous features to run a slow
|
||||||
function using a separate reactor thread. In effect this means that
|
function using a separate reactor thread. In effect this means that
|
||||||
the server will not be blocked while the slow process finish.
|
the server will not be blocked while the slow process finish.
|
||||||
|
|
||||||
Use this function with restrain and only for features/commands
|
Use this function with restrain and only for features/commands
|
||||||
that you know has no influence on the cause-and-effect order of your
|
that you know has no influence on the cause-and-effect order of your
|
||||||
game (commands given after the async function might be executed before
|
game (commands given after the async function might be executed before
|
||||||
it has finished).
|
it has finished).
|
||||||
|
|
||||||
async_func() - function that should be run asynchroneously
|
async_func() - function that should be run asynchroneously
|
||||||
at_return(r) - if given, this function will be called when async_func returns
|
at_return(r) - if given, this function will be called when async_func returns
|
||||||
value r at the end of a successful execution
|
value r at the end of a successful execution
|
||||||
at_err(e) - if given, this function is called if async_func fails with an exception e.
|
at_err(e) - if given, this function is called if async_func fails with an exception e.
|
||||||
use e.trap(ExceptionType1, ExceptionType2)
|
use e.trap(ExceptionType1, ExceptionType2)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
# create deferred object
|
# create deferred object
|
||||||
|
|
||||||
deferred = threads.deferToThread(async_func)
|
deferred = threads.deferToThread(async_func)
|
||||||
if at_return:
|
if at_return:
|
||||||
deferred.addCallback(at_return)
|
deferred.addCallback(at_return)
|
||||||
|
|
@ -458,7 +458,7 @@ def run_async(async_func, at_return=None, at_err=None):
|
||||||
# always add a logging errback as a last catch
|
# always add a logging errback as a last catch
|
||||||
def default_errback(e):
|
def default_errback(e):
|
||||||
from src.utils import logger
|
from src.utils import logger
|
||||||
logger.log_trace(e)
|
logger.log_trace(e)
|
||||||
deferred.addErrback(default_errback)
|
deferred.addErrback(default_errback)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -491,7 +491,7 @@ def check_evennia_dependencies():
|
||||||
import twisted
|
import twisted
|
||||||
tversion = twisted.version.short()
|
tversion = twisted.version.short()
|
||||||
if tversion < twisted_min:
|
if tversion < twisted_min:
|
||||||
errstring += "\n WARNING: Twisted %s found. Evennia recommends version %s or higher." % (twisted.version.short(), twisted_min)
|
errstring += "\n WARNING: Twisted %s found. Evennia recommends version %s or higher." % (twisted.version.short(), twisted_min)
|
||||||
except ImportError:
|
except ImportError:
|
||||||
errstring += "\n ERROR: Twisted does not seem to be installed."
|
errstring += "\n ERROR: Twisted does not seem to be installed."
|
||||||
no_error = False
|
no_error = False
|
||||||
|
|
@ -508,12 +508,12 @@ def check_evennia_dependencies():
|
||||||
# South
|
# South
|
||||||
try:
|
try:
|
||||||
import south
|
import south
|
||||||
sversion = south.__version__
|
sversion = south.__version__
|
||||||
if sversion < south_min:
|
if sversion < south_min:
|
||||||
errstring += "\n WARNING: South version %s found. Evennia recommends version %s or higher." % (sversion, south_min)
|
errstring += "\n WARNING: South version %s found. Evennia recommends version %s or higher." % (sversion, south_min)
|
||||||
except ImportError:
|
except ImportError:
|
||||||
pass
|
pass
|
||||||
# IRC support
|
# IRC support
|
||||||
if settings.IRC_ENABLED:
|
if settings.IRC_ENABLED:
|
||||||
try:
|
try:
|
||||||
import twisted.words
|
import twisted.words
|
||||||
|
|
@ -521,7 +521,7 @@ def check_evennia_dependencies():
|
||||||
errstring += "\n ERROR: IRC is enabled, but twisted.words is not installed. Please install it."
|
errstring += "\n ERROR: IRC is enabled, but twisted.words is not installed. Please install it."
|
||||||
errstring += "\n Linux Debian/Ubuntu users should install package 'python-twisted-words', others"
|
errstring += "\n Linux Debian/Ubuntu users should install package 'python-twisted-words', others"
|
||||||
errstring += "\n can get it from http://twistedmatrix.com/trac/wiki/TwistedWords."
|
errstring += "\n can get it from http://twistedmatrix.com/trac/wiki/TwistedWords."
|
||||||
no_error = False
|
no_error = False
|
||||||
errstring = errstring.strip()
|
errstring = errstring.strip()
|
||||||
if errstring:
|
if errstring:
|
||||||
print "%s\n %s\n%s" % ("-"*78, errstring, '-'*78)
|
print "%s\n %s\n%s" % ("-"*78, errstring, '-'*78)
|
||||||
|
|
@ -534,21 +534,21 @@ def has_parent(basepath, obj):
|
||||||
if basepath == "%s.%s" % (cls.__module__, cls.__name__))
|
if basepath == "%s.%s" % (cls.__module__, cls.__name__))
|
||||||
except (TypeError, AttributeError):
|
except (TypeError, AttributeError):
|
||||||
# this can occur if we tried to store a class object, not an
|
# this can occur if we tried to store a class object, not an
|
||||||
# instance. Not sure if one should defend against this.
|
# instance. Not sure if one should defend against this.
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def mod_import(mod_path, propname=None):
|
def mod_import(mod_path, propname=None):
|
||||||
"""
|
"""
|
||||||
Takes filename of a module (a python path or a full pathname)
|
Takes filename of a module (a python path or a full pathname)
|
||||||
and imports it. If property is given, return the named
|
and imports it. If property is given, return the named
|
||||||
property from this module instead of the module itself.
|
property from this module instead of the module itself.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def log_trace(errmsg=None):
|
def log_trace(errmsg=None):
|
||||||
"""
|
"""
|
||||||
Log a traceback to the log. This should be called
|
Log a traceback to the log. This should be called
|
||||||
from within an exception. errmsg is optional and
|
from within an exception. errmsg is optional and
|
||||||
adds an extra line with added info.
|
adds an extra line with added info.
|
||||||
"""
|
"""
|
||||||
from traceback import format_exc
|
from traceback import format_exc
|
||||||
from twisted.python import log
|
from twisted.python import log
|
||||||
|
|
@ -557,7 +557,7 @@ def mod_import(mod_path, propname=None):
|
||||||
tracestring = format_exc()
|
tracestring = format_exc()
|
||||||
if tracestring:
|
if tracestring:
|
||||||
for line in tracestring.splitlines():
|
for line in tracestring.splitlines():
|
||||||
log.msg('[::] %s' % line)
|
log.msg('[::] %s' % line)
|
||||||
if errmsg:
|
if errmsg:
|
||||||
try:
|
try:
|
||||||
errmsg = to_str(errmsg)
|
errmsg = to_str(errmsg)
|
||||||
|
|
@ -569,10 +569,10 @@ def mod_import(mod_path, propname=None):
|
||||||
if not mod_path:
|
if not mod_path:
|
||||||
return None
|
return None
|
||||||
# first try to import as a python path
|
# first try to import as a python path
|
||||||
try:
|
try:
|
||||||
mod = __import__(mod_path, fromlist=["None"])
|
mod = __import__(mod_path, fromlist=["None"])
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
|
||||||
# try absolute path import instead
|
# try absolute path import instead
|
||||||
|
|
||||||
if not os.path.isabs(mod_path):
|
if not os.path.isabs(mod_path):
|
||||||
|
|
@ -583,13 +583,13 @@ def mod_import(mod_path, propname=None):
|
||||||
try:
|
try:
|
||||||
result = imp.find_module(modname, [path])
|
result = imp.find_module(modname, [path])
|
||||||
except ImportError:
|
except ImportError:
|
||||||
log_trace("Could not find module '%s' (%s.py) at path '%s'" % (modname, modname, path))
|
log_trace("Could not find module '%s' (%s.py) at path '%s'" % (modname, modname, path))
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
mod = imp.load_module(modname, *result)
|
mod = imp.load_module(modname, *result)
|
||||||
except ImportError:
|
except ImportError:
|
||||||
log_trace("Could not find or import module %s at path '%s'" % (modname, path))
|
log_trace("Could not find or import module %s at path '%s'" % (modname, path))
|
||||||
mod = None
|
mod = None
|
||||||
# we have to close the file handle manually
|
# we have to close the file handle manually
|
||||||
result[0].close()
|
result[0].close()
|
||||||
|
|
||||||
|
|
@ -598,16 +598,16 @@ def mod_import(mod_path, propname=None):
|
||||||
try:
|
try:
|
||||||
mod_prop = mod.__dict__[to_str(propname)]
|
mod_prop = mod.__dict__[to_str(propname)]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
log_trace("Could not import property '%s' from module %s." % (propname, mod_path))
|
log_trace("Could not import property '%s' from module %s." % (propname, mod_path))
|
||||||
return None
|
return None
|
||||||
return mod_prop
|
return mod_prop
|
||||||
return mod
|
return mod
|
||||||
|
|
||||||
def variable_from_module(modpath, variable, default=None):
|
def variable_from_module(modpath, variable, default=None):
|
||||||
"""
|
"""
|
||||||
Retrieve a given variable from a module. The variable must be
|
Retrieve a given variable from a module. The variable must be
|
||||||
defined globally in the module. This can be used to implement
|
defined globally in the module. This can be used to implement
|
||||||
arbitrary plugin imports in the server.
|
arbitrary plugin imports in the server.
|
||||||
|
|
||||||
If module cannot be imported or variable not found, default
|
If module cannot be imported or variable not found, default
|
||||||
is returned.
|
is returned.
|
||||||
|
|
@ -627,7 +627,7 @@ def string_from_module(modpath, variable=None, default=None):
|
||||||
|
|
||||||
This obtains a string from a given module python path. Using a
|
This obtains a string from a given module python path. Using a
|
||||||
specific variable name will also retrieve non-strings.
|
specific variable name will also retrieve non-strings.
|
||||||
|
|
||||||
The variable must be global within that module - that is, defined
|
The variable must be global within that module - that is, defined
|
||||||
in the outermost scope of the module. The value of the variable
|
in the outermost scope of the module. The value of the variable
|
||||||
will be returned. If not found, default is returned. If no variable is
|
will be returned. If not found, default is returned. If no variable is
|
||||||
|
|
@ -640,8 +640,8 @@ def string_from_module(modpath, variable=None, default=None):
|
||||||
if variable:
|
if variable:
|
||||||
return mod.__dict__.get(variable, default)
|
return mod.__dict__.get(variable, default)
|
||||||
else:
|
else:
|
||||||
mvars = [val for key, val in mod.__dict__.items()
|
mvars = [val for key, val in mod.__dict__.items()
|
||||||
if not key.startswith('_') and isinstance(val, basestring)]
|
if not key.startswith('_') and isinstance(val, basestring)]
|
||||||
if not mvars:
|
if not mvars:
|
||||||
return default
|
return default
|
||||||
return mvars[random.randint(0, len(mvars)-1)]
|
return mvars[random.randint(0, len(mvars)-1)]
|
||||||
|
|
@ -651,8 +651,8 @@ def init_new_player(player):
|
||||||
Helper method to call all hooks, set flags etc on a newly created
|
Helper method to call all hooks, set flags etc on a newly created
|
||||||
player (and potentially their character, if it exists already)
|
player (and potentially their character, if it exists already)
|
||||||
"""
|
"""
|
||||||
# the FIRST_LOGIN flags are necessary for the system to call
|
# the FIRST_LOGIN flags are necessary for the system to call
|
||||||
# the relevant first-login hooks.
|
# the relevant first-login hooks.
|
||||||
if player.character:
|
if player.character:
|
||||||
player.character.db.FIRST_LOGIN = True
|
player.character.db.FIRST_LOGIN = True
|
||||||
player.db.FIRST_LOGIN = True
|
player.db.FIRST_LOGIN = True
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue