Revert "Updated HTML docs."

This reverts commit 06bc3c8bcd.
This commit is contained in:
Griatch 2022-11-15 20:46:50 +01:00
parent 06bc3c8bcd
commit a452434ba8
663 changed files with 61705 additions and 2 deletions

View file

@ -0,0 +1,773 @@
# Changelog
### Evennia 1.0
> Not released yet
> 2019-2022 develop branch (WIP)
Up requirements to Django 4.0+, Twisted 22+, Python 3.9 or 3.10
- New `drop:holds()` lock default to limit dropping nonsensical things. Access check
defaults to True for backwards-compatibility in 0.9, will be False in 1.0
- REST API allows you external access to db objects through HTTP requests (Tehom)
- `Object.normalize_name` and `.validate_name` added to (by default) enforce latinify
on character name and avoid potential exploits using clever Unicode chars (trhr)
- New `utils.format_grid` for easily displaying long lists of items in a block.
- Using `lunr` search indexing for better `help` matching and suggestions. Also improve
the main help command's default listing output.
- Added `content_types` indexing to DefaultObject's ContentsHandler. (volund)
- Made most of the networking classes such as Protocols and the SessionHandlers
replaceable via `settings.py` for modding enthusiasts. (volund)
- The `initial_setup.py` file can now be substituted in `settings.py` to customize
initial game database state. (volund)
- Added new Traits contrib, converted and expanded from Ainneve project.
- Added new `requirements_extra.txt` file for easily getting all optional dependencies.
- Change default multi-match syntax from 1-obj, 2-obj to obj-1, obj-2.
- Make `object.search` support 'stacks=0' keyword - if ``>0``, the method will return
N identical matches instead of triggering a multi-match error.
- Add `tags.has()` method for checking if an object has a tag or tags (PR by ChrisLR)
- Make IP throttle use Django-based cache system for optional persistence (PR by strikaco)
- Renamed Tutorial classes "Weapon" and "WeaponRack" to "TutorialWeapon" and
"TutorialWeaponRack" to prevent collisions with classes in mygame
- New `crafting` contrib, adding a full crafting subsystem (Griatch 2020)
- The `rplanguage` contrib now auto-capitalizes sentences and retains ellipsis (...). This
change means that proper nouns at the start of sentences will not be treated as nouns.
- Make MuxCommand `lhs/rhslist` always be lists, also if empty (used to be the empty string)
- Fix typo in UnixCommand contrib, where `help` was given as `--hel`.
- Latin (la) i18n translation (jamalainm)
- Made the `evennia` dir possible to use without gamedir for purpose of doc generation.
- Make Scripts' timer component independent from script object deletion; can now start/stop
timer without deleting Script. The `.persistent` flag now only controls if timer survives
reload - Script has to be removed with `.delete()` like other typeclassed entities.
- Add `utils.repeat` and `utils.unrepeat` as shortcuts to TickerHandler add/remove, similar
to how `utils.delay` is a shortcut for TaskHandler add.
- Refactor the classic `red_button` example to use `utils.delay/repeat` and modern recommended
code style and paradigms instead of relying on `Scripts` for everything.
- Expand `CommandTest` with ability to check multiple message-receivers; inspired by PR by
user davewiththenicehat. Also add new doc string.
- Add central `FuncParser` as a much more powerful replacement for the old `parse_inlinefunc`
function.
- Attribute/NAttribute got a homogenous representation, using intefaces, both
`AttributeHandler` and `NAttributeHandler` has same api now.
- Add `evennia/utils/verb_conjugation` for automatic verb conjugation (English only). This
is useful for implementing actor-stance emoting for sending a string to different targets.
- New version of Italian translation (rpolve)
- `utils.evmenu.ask_yes_no` is a helper function that makes it easy to ask a yes/no question
to the user and respond to their input. This complements the existing `get_input` helper.
- Allow sending messages with `page/tell` without a `=` if target name contains no spaces.
- New FileHelpStorage system allows adding help entries via external files.
- `sethelp` command now warns if shadowing other help-types when creating a new
entry.
- Help command now uses `view` lock to determine if cmd/entry shows in index and
`read` lock to determine if it can be read. It used to be `view` in the role
of the latter. Migration swaps these around.
- In modules given by `settings.PROTOTYPE_MODULES`, spawner will now first look for a global
list `PROTOTYPE_LIST` of dicts before loading all dicts in the module as prototypes.
- New Channel-System using the `channel` command and nicks. Removed the `ChannelHandler` and the
concept of a dynamically created `ChannelCmdSet`.
- Add `Msg.db_receiver_external` field to allowe external, string-id message-receivers.
- Renamed `app.css` to `website.css` for consistency. Removed old prosimii-css files.
- Remove `mygame/web/static_overrides` and -`template_overrides`, reorganize website/admin/client/api
into a more consistent structure for overriding. Expanded webpage documentation considerably.
- REST API list-view was shortened (#2401). New CSS/HTML. Add ReDoc for API autodoc page.
- Update and fix dummyrunner with cleaner code and setup.
- Made `iter_to_str` format prettier strings, using Oxford comma.
- Added an MXP anchor tag to also support clickable web links.
- New `tasks` command for managing tasks started with `utils.delay` (PR by davewiththenicehat)
- Make `help` index output clickable for webclient/clients with MXP (PR by davewiththenicehat)
- Custom `evennia` launcher commands (e.g. `evennia mycmd foo bar`). Add new commands as callables
accepting `*args`, as `settings.EXTRA_LAUNCHER_COMMANDS = {'mycmd': 'path.to.callable', ...}`.
- New `XYZGrid` contrib, adding x,y,z grid coordinates with in-game map and
pathfinding. Controlled outside of the game via custom evennia launcher command.
- `Script.delete` has new kwarg `stop_task=True`, that can be used to avoid
infinite recursion when wanting to set up Script to delete-on-stop.
- Command executions now done on copies to make sure `yield` don't cause crossovers. Add
`Command.retain_instance` flag for reusing the same command instance.
- The `typeclass` command will now correctly search the correct database-table for the target
obj (avoids mistakenly assigning an AccountDB-typeclass to a Character etc).
- Merged `script` and `scripts` commands into one, for both managing global- and
on-object Scripts. Moved `CmdScripts` and `CmdObjects` to `commands/default/building.py`.
- Keep GMCP function case if outputfunc starts with capital letter (so `cmd_name` -> `Cmd.Name`
but `Cmd_nAmE` -> `Cmd.nAmE`). This helps e.g Mudlet's legacy `Client_GUI` implementation)
- Prototypes now allow setting `prototype_parent` directly to a prototype-dict.
This makes it easier when dynamically building in-module prototypes.
- `RPSystem contrib` was expanded to support case, so /tall becomes 'tall man'
while /Tall becomes 'Tall man'. One can turn this off if wanting the old style.
- Change `EvTable` fixed-height rebalance algorithm to fill with empty lines at end of
column instead of inserting rows based on cell-size (could be mistaken for a bug).
- Split `return_appearance` hook with helper methods and have it use a template
string in order to make it easier to override.
- Add validation question to default account creation.
- Add `LOCALECHO` client option to add server-side echo for clients that does
not support this (useful for getting a complete log).
- Make `@lazy_property` decorator create read/delete-protected properties. This is
because it's used for handlers, and e.g. self.locks=[] is a common beginner mistake.
- Add `$pron()` inlinefunc for pronoun parsing in actor-stance strings using
`msg_contents`.
- Update defauklt website to show Telnet/SSL/SSH connect info. Added new
`SERVER_HOSTNAME` setting for use in the server:port stanza.
- Changed all `at_before/after_*` hooks to `at_pre/post_*` for consistency
across Evennia (the old names still work but are deprecated)
- Change `settings.COMMAND_DEFAULT_ARG_REGEX` default from `None` to a regex meaning that
a space or `/` must separate the cmdname and args. This better fits common expectations.
- Add confirmation question to `ban`/`unban` commands.
- Check new `teleport` and `teleport_here` lock-types in `teleport` command to optionally
allow to limit teleportation of an object or to a specific destination.
- Add `settings.MXP_ENABLED=True` and `settings.MXP_OUTGOING_ONLY=True` as sane defaults,
to avoid known security issues with players entering MXP links.
- Add browser name to webclient `CLIENT_NAME` in `session.protocol_flags`, e.g.
`"Evennia webclient (websocket:firefox)"` or `"evennia webclient (ajax:chrome)"`.
- `TagHandler.add/has(tag=...)` kwarg changed to `add/has(key=...)` for consistency
with other handlers.
- Make `DefaultScript.delete`, `DefaultChannel.delete` and `DefaultAccount.delete` return
bool True/False if deletion was successful (like `DefaultObject.delete` before them)
- `contrib.custom_gametime` days/weeks/months now always starts from 1 (to match
the standard calendar form ... there is no month 0 every year after all).
- `AttributeProperty`/`NAttributeProperty` to allow managing Attributes/NAttributes
on typeclasses in the same way as Django fields.
- Give build/system commands a `@name` to fall back to if the non-@ name is used
by another command (like `open` and `@open`. If no duplicate, @ is optional.
- Move legacy channel-management commands (`ccreate`, `addcom` etc) to a contrib
since their work is now fully handled by the single `channel` command.
- Expand `examine` command's code to much more extensible and modular. Show
attribute categories and value types (when not strings).
- `AttributeHandler.remove(key, return_exception=False, category=None, ...)` changed
to `.remove(key, category=None, return_exception=False, ...)` for consistency.
- New `command cooldown` contrib for making it easier to manage commands using
dynamic cooldowns between uses (owllex)
- Restructured `contrib/` folder, placing all contribs as separate packages under
subfolders. All imports will need to be updated.
- Made `MonitorHandler.add/remove` support `category` for monitoring Attributes
with a category (before only key was used, ignoring category entirely).
- Move `create_*` functions into db managers, leaving `utils.create` only being
wrapper functions (consistent with `utils.search`). No change of api otherwise.
- Add support for `$dbref()` and `$search` when assigning an Attribute value
with the `set` command. This allows assigning real objects from in-game.
- Add ability to examine `/script` and `/channel` entities with `examine` command.
- Homogenize manager search methods to return querysets and not lists.
- Restructure unit tests to always honor default settings; make new parents in
on location for easy use in game dir.
- The `Lunr` search engine used by help excludes common words; the settings-list
`LUNR_STOP_WORD_FILTER_EXCEPTIONS` can be extended to make sure common names are included.
- Add `.deserialize()` method to `_Saver*` structures to help completely
decouple structures from database without needing separate import.
- Add `run_in_main_thread` as a helper for those wanting to code server code
from a web view.
- Update `evennia.utils.logger` to use Twisted's new logging API. No change in Evennia API
except more standard aliases logger.error/info/exception/debug etc can now be used.
- Have `type/force` default to `update`-mode rather than `reset`mode and add more verbose
warning when using reset mode.
- Attribute storage support defaultdics (Hendher)
- Add ObjectParent mixin to default game folder template as an easy, ready-made
way to override features on all ObjectDB-inheriting objects easily.
source location, mimicking behavior of `at_pre_move` hook - returning False will abort move.
- Add `TagProperty`, `AliasProperty` and `PermissionProperty` to assign these
data in a similar way to django fields.
- New `at_pre_object_receive(obj, source_location)` method on Objects. Called on
destination, mimicking behavior of `at_pre_move` hook - returning False will abort move.
- New `at_pre_object_leave(obj, destination)` method on Objects. Called on
- The db pickle-serializer now checks for methods `__serialize_dbobjs__` and `__deserialize_dbobjs__`
to allow custom packing/unpacking of nested dbobjs, to allow storing in Attribute.
- Optimizations to rpsystem contrib performance. Breaking change: `.get_sdesc()` will
now return `None` instead of `.db.desc` if no sdesc is set; fallback in hook (inspectorCaracal)
- Reworked text2html parser to avoid problems with stateful color tags (inspectorCaracal)
- Simplified `EvMenu.options_formatter` hook to use `EvColumn` and f-strings (inspectorcaracal)
- Allow `# CODE`, `# HEADER` etc as well as `#CODE`/`#HEADER` in batchcode
files - this works better with black linting.
- Added `move_type` str kwarg to `move_to()` calls, optionally identifying the type of
move being done ('teleport', 'disembark', 'give' etc). (volund)
- Made RPSystem contrib msg calls pass `pose` or `say` as msg-`type` for use in
e.g. webclient pane filtering where desired. (volund)
- Added `Account.uses_screenreader(session=None)` as a quick shortcut for
finding if a user uses a screenreader (and adjust display accordingly).
- Fixed bug in `cmdset.remove()` where a command could not be deleted by `key`,
even though doc suggested one could (ChrisLR)
- New contrib `name_generator` for building random real-world based or fantasy-names
based on phonetic rules.
- Enable proper serialization of dict subclasses in Attributes (aogier)
- `object.search` fuzzy-matching now uses `icontains` instead of `istartswith`
to better match how search works elsewhere (volund)
- The `.at_traverse` hook now receives a `exit_obj` kwarg, linking back to the
exit triggering the hook (volund)
- Contrib `buffs` for managing temporary and permanent RPG status buffs effects (tegiminis)
- New `at_server_init()` hook called before all other startup hooks for all
startup modes. Used for more generic overriding (volund)
- New `search` lock type used to completely hide an object from being found by
the `DefaultObject.search` (`caller.search`) method. (CloudKeeper)
- Change setting `MULTISESSION_MODE` to now only control sessions, not how many
characters can be puppeted simultaneously. New settings now control that.
- Add new setting `AUTO_CREATE_CHARACTER_WITH_ACCOUNT`, a boolean deciding if
the new account should also get a matching character (legacy MUD style).
- Add new setting `AUTO_PUPPET_ON_LOGIN`, boolean deciding if one should
automatically puppet the last/available character on connection (legacy MUD style)
- Add new setting `MAX_NR_SIMULTANEUS_PUPPETS` - how many puppets the account
can run at the same time. Used to limit multi-playing.
- Make setting `MAX_NR_CHARACTERS` interact better with the new settings above.
- Allow `$search` funcparser func to search tags and to accept kwargs for more
powerful searches passed into the regular search functions.
- `spawner.spawn` and linked methods now has a kwarg `protfunc_raise_errors`
(default True) to disable strict errors on malformed/not-found protfuncs
- Improve search performance when having many DB-based prototypes via caching.
- Remove the `return_parents` kwarg of `evennia.prototypes.spawner.spawn` since it
was inefficient and unused.
- Made all id fields BigAutoField for all databases. (owllex)
- `EvForm` refactored. New `literals` mapping, for literal mappings into the
main template (e.g. for single-character replacements).
- `EvForm` `cells` kwarg now accepts `EvCells` with custom formatting options
(mainly for custom align/valign). `EvCells` now makes use of `utils.justify`.
- `utils.justify` now supports `align="a"` (absolute alignments. This keeps
the given left indent but crops/fills to the width. Used in EvCells.
- `EvTable` now supports passing `EvColumn`s as a list directly, (`EvTable(table=[colA,colB])`)
## Evennia 0.9.5
> 2019-2020
> Released 2020-11-14.
> Transitional release, including new doc system.
Backported from develop: Python 3.8, 3.9 support. Django 3.2+ support, Twisted 21+ support.
- `is_typeclass(obj (Object), exact (bool))` now defaults to exact=False
- `py` command now reroutes stdout to output results in-game client. `py`
without arguments starts a full interactive Python console.
- Webclient default to a single input pane instead of two. Now defaults to no help-popup.
- Webclient fix of prompt display
- Webclient multimedia support for relaying images, video and sounds via
`.msg(image=URL)`, `.msg(video=URL)`
and `.msg(audio=URL)`
- Add Spanish translation (fermuch)
- Expand `GLOBAL_SCRIPTS` container to always start scripts and to include all
global scripts regardless of how they were created.
- Change settings to always use lists instead of tuples, to make mutable
settings easier to add to. (#1912)
- Make new `CHANNEL_MUDINFO` setting for specifying the mudinfo channel
- Make `CHANNEL_CONNECTINFO` take full channel definition
- Make `DEFAULT_CHANNELS` list auto-create channels missing at reload
- Webclient `ANSI->HTML` parser updated. Webclient line width changed from 1.6em to 1.1em
to better make ANSI graphics look the same as for third-party clients
- `AttributeHandler.get(return_list=True)` will return `[]` if there are no
Attributes instead of `[None]`.
- Remove `pillow` requirement (install especially if using imagefield)
- Add Simplified Korean translation (aceamro)
- Show warning on `start -l` if settings contains values unsafe for production.
- Make code auto-formatted with Black.
- Make default `set` command able to edit nested structures (PR by Aaron McMillan)
- Allow running Evennia test suite from core repo with `make test`.
- Return `store_key` from `TickerHandler.add` and add `store_key` as a kwarg to
the `TickerHandler.remove` method. This makes it easier to manage tickers.
- EvMore auto-justify now defaults to False since this works better with all types
of texts (such as tables). New `justify` bool. Old `justify_kwargs` remains
but is now only used to pass extra kwargs into the justify function.
- EvMore `text` argument can now also be a list or a queryset. Querysets will be
sliced to only return the required data per page.
- Improve performance of `find` and `objects` commands on large data sets (strikaco)
- New `CHANNEL_HANDLER_CLASS` setting allows for replacing the ChannelHandler entirely.
- Made `py` interactive mode support regular quit() and more verbose.
- Made `Account.options.get` accept `default=None` kwarg to mimic other uses of get. Set
the new `raise_exception` boolean if ranting to raise KeyError on a missing key.
- Moved behavior of unmodified `Command` and `MuxCommand` `.func()` to new
`.get_command_info()` method for easier overloading and access. (Volund)
- Removed unused `CYCLE_LOGFILES` setting. Added `SERVER_LOG_DAY_ROTATION`
and `SERVER_LOG_MAX_SIZE` (and equivalent for PORTAL) to control log rotation.
- Addded `inside_rec` lockfunc - if room is locked, the normal `inside()` lockfunc will
fail e.g. for your inventory objs (since their loc is you), whereas this will pass.
- RPSystem contrib's CmdRecog will now list all recogs if no arg is given. Also multiple
bugfixes.
- Remove `dummy@example.com` as a default account email when unset, a string is no longer
required by Django.
- Fixes to `spawn`, make updating an existing prototype/object work better. Add `/raw` switch
to `spawn` command to extract the raw prototype dict for manual editing.
- `list_to_string` is now `iter_to_string` (but old name still works as legacy alias). It will
now accept any input, including generators and single values.
- EvTable should now correctly handle columns with wider asian-characters in them.
- Update Twisted requirement to >=2.3.0 to close security vulnerability
- Add `$random` inlinefunc, supports minval,maxval arguments that can be ints and floats.
- Add `evennia.utils.inlinefuncs.raw(<str>)` as a helper to escape inlinefuncs in a string.
- Make CmdGet/Drop/Give give proper error if `obj.move_to` returns `False`.
- Make `Object/Room/Exit.create`'s `account` argument optional. If not given, will set perms
to that of the object itself (along with normal Admin/Dev permission).
- Make `INLINEFUNC_STACK_MAXSIZE` default visible in `settings_default.py`.
- Change how `ic` finds puppets; non-priveleged users will use `_playable_characters` list as
candidates, Builders+ will use list, local search and only global search if no match found.
- Make `cmd.at_post_cmd()` always run after `cmd.func()`, even when the latter uses delays
with yield.
- `EvMore` support for db queries and django paginators as well as easier to override for custom
pagination (e.g. to create EvTables for every page instead of splittine one table)
- Using `EvMore pagination`, dramatically improves performance of `spawn/list` and `scripts` listings
(100x speed increase for displaying 1000+ prototypes/scripts).
- `EvMenu` now uses the more logically named `.ndb._evmenu` instead of `.ndb._menutree` to store itself.
Both still work for backward compatibility, but `_menutree` is deprecated.
- `EvMenu.msg(txt)` added as a central place to send text to the user, makes it easier to override.
Default `EvMenu.msg` sends with OOB type="menu" for use with OOB and webclient pane-redirects.
- New EvMenu templating system for quickly building simpler EvMenus without as much code.
- Add `Command.client_height()` method to match existing `.client_width` (stricako)
- Include more Web-client info in `session.protocol_flags`.
- Fixes in multi-match situations - don't allow finding/listing multimatches for 3-box when
only two boxes in location.
- Fix for TaskHandler with proper deferred returns/ability to cancel etc (PR by davewiththenicehat)
- Add `PermissionHandler.check` method for straight string perm-checks without needing lockstrings.
- Add `evennia.utils.utils.strip_unsafe_input` for removing html/newlines/tags from user input. The
`INPUT_CLEANUP_BYPASS_PERMISSIONS` is a list of perms that bypass this safety stripping.
- Make default `set` and `examine` commands aware of Attribute categories.
## Evennia 0.9
> 2018-2019
> Released Oct 2019
### Distribution
- New requirement: Python 3.7 (py2.7 support removed)
- Django 2.1
- Twisted 19.2.1
- Autobahn websockets (removed old tmwx)
- Docker image updated
### Commands
- Remove `@`-prefix from all default commands (prefixes still work, optional)
- Removed default `@delaccount` command, incorporating as `@account/delete` instead. Added confirmation
question.
- Add new `@force` command to have another object perform a command.
- Add the Portal uptime to the `@time` command.
- Make the `@link` command first make a local search before a global search.
- Have the default Unloggedin-look command look for optional `connection_screen()` callable in
`mygame/server/conf/connection_screen.py`. This allows for more flexible welcome screens
that are calculated on the fly.
- `@py` command now defaults to escaping html tags in its output when viewing in the webclient.
Use new `/clientraw` switch to get old behavior (issue #1369).
- Shorter and more informative, dynamic, listing of on-command vars if not
setting func() in child command class.
- New Command helper methods
- `.client_width()` returns client width of the session running the command.
- `.styled_table(*args, **kwargs)` returns a formatted evtable styled by user's options
- `.style_header(*args, **kwargs)` creates styled header entry
- `.style_separator(*args, **kwargs)` " separator
- `.style_footer(*args, **kwargs)` " footer
### Web
- Change webclient from old txws version to use more supported/feature-rich Autobahn websocket library
#### Evennia game index
- Made Evennia game index client a part of core - now configured from settings file (old configs
need to be moved)
- The `evennia connections` command starts a wizard that helps you connect your game to the game index.
- The game index now accepts games with no public telnet/webclient info (for early prototypes).
#### New golden-layout based Webclient UI (@friarzen)
- Features
- Much slicker behavior and more professional look
- Allows tabbing as well as click and drag of panes in any grid position
- Renaming tabs, assignments of data tags and output types are simple per-pane menus now
- Any number of input panes, with separate histories
- Button UI (disabled in JS by default)
#### Web/Django standard initiative (@strikaco)
- Features
- Adds a series of web-based forms and generic class-based views
- Accounts
- Register - Enhances registration; allows optional collection of email address
- Form - Adds a generic Django form for creating Accounts from the web
- Characters
- Create - Authenticated users can create new characters from the website (requires associated form)
- Detail - Authenticated and authorized users can view select details about characters
- List - Authenticated and authorized users can browse a list of all characters
- Manage - Authenticated users can edit or delete owned characters from the web
- Form - Adds a generic Django form for creating characters from the web
- Channels
- Detail - Authorized users can view channel logs from the web
- List - Authorized users can browse a list of all channels
- Help Entries
- Detail - Authorized users can view help entries from the web
- List - Authorized users can browse a list of all help entries from the web
- Navbar changes
- Characters - Link to character list
- Channels - Link to channel list
- Help - Link to help entry list
- Puppeting
- Users can puppet their own characters within the context of the website
- Dropdown
- Link to create characters
- Link to manage characters
- Link to quick-select puppets
- Link to password change workflow
- Functions
- Updates Bootstrap to v4 stable
- Enables use of Django Messages framework to communicate with users in browser
- Implements webclient/website `_shared_login` functionality as Django middleware
- 'account' and 'puppet' are added to all request contexts for authenticated users
- Adds unit tests for all web views
- Cosmetic
- Prettifies Django 'forgot password' workflow (requires SMTP to actually function)
- Prettifies Django 'change password' workflow
- Bugfixes
- Fixes bug on login page where error messages were not being displayed
- Remove strvalue field from admin; it made no sense to have here, being an optimization field
for internal use.
### Prototypes
- `evennia.prototypes.save_prototype` now takes the prototype as a normal
argument (`prototype`) instead of having to give it as `**prototype`.
- `evennia.prototypes.search_prototype` has a new kwarg `require_single=False` that
raises a KeyError exception if query gave 0 or >1 results.
- `evennia.prototypes.spawner` can now spawn by passing a `prototype_key`
### Typeclasses
- Add new methods on all typeclasses, useful specifically for object handling from the website/admin:
+ `web_get_admin_url()`: Returns the path to the object detail page in the Admin backend.
+ `web_get_create_url()`: Returns the path to the typeclass' creation page on the website, if implemented.
+ `web_get_absolute_url()`: Returns the path to the object's detail page on the website, if implemented.
+ `web_get_update_url()`: Returns the path to the object's update page on the website, if implemented.
+ `web_get_delete_url()`: Returns the path to the object's delete page on the website, if implemented.
- All typeclasses have new helper class method `create`, which encompasses useful functionality
that used to be embedded for example in the respective `@create` or `@connect` commands.
- DefaultAccount now has new class methods implementing many things that used to be in unloggedin
commands (these can now be customized on the class instead):
+ `is_banned()`: Checks if a given username or IP is banned.
+ `get_username_validators`: Return list of validators for username validation (see
`settings.AUTH_USERNAME_VALIDATORS`)
+ `authenticate`: Method to check given username/password.
+ `normalize_username`: Normalizes names so (for Unicode environments) users cannot mimic existing usernames by replacing select characters with visually-similar Unicode chars.
+ `validate_username`: Mechanism for validating a username based on predefined Django validators.
+ `validate_password`: Mechanism for validating a password based on predefined Django validators.
+ `set_password`: Apply password to account, using validation checks.
- `AttributeHandler.remove` and `TagHandler.remove` can now be used to delete by-category. If neither
key nor category is given, they now work the same as .clear().
### Protocols
- Support for `Grapevine` MUD-chat network ("channels" supported)
### Server
- Convert ServerConf model to store its values as a Picklefield (same as
Attributes) instead of using a custom solution.
- OOB: Add support for MSDP LIST, REPORT, UNREPORT commands (re-mapped to `msdp_list`,
`msdp_report`, `msdp_unreport`, inlinefuncs)
- Added `evennia.ANSIString` to flat API.
- Server/Portal log files now cycle to names on the form `server_.log_19_03_08_` instead of `server.log___19.3.8`, retaining
unix file sorting order.
- Django signals fire for important events: Puppet/Unpuppet, Object create/rename, Login,
Logout, Login fail Disconnect, Account create/rename
### Settings
- `GLOBAL_SCRIPTS` - dict defining typeclasses of global scripts to store on the new
`evennia.GLOBAL_SCRIPTS` container. These will auto-start when Evennia start and will always
exist.
- `OPTIONS_ACCOUNTS_DEFAULT` - option dict with option defaults and Option classes
- `OPTION_CLASS_MODULES` - classes representing an on-Account Option, on special form
- `VALIDATOR_FUNC_MODULES` - (general) text validator functions, for verifying an input
is on a specific form.
### Utils
- `evennia` launcher now fully handles all django-admin commands, like running tests in parallel.
- `evennia.utils.create.account` now also takes `tags` and `attrs` keywords.
- `evennia.utils.interactive` decorator can now allow you to use yield(secs) to pause operation
in any function, not just in Command.func. Likewise, response = yield(question) will work
if the decorated function has an argument or kwarg `caller`.
- Added many more unit tests.
- Swap argument order of `evennia.set_trace` to `set_trace(term_size=(140, 40), debugger='auto')`
since the size is more likely to be changed on the command line.
- `utils.to_str(text, session=None)` now acts as the old `utils.to_unicode` (which was removed).
This converts to the str() type (not to a byte-string as in Evennia 0.8), trying different
encodings. This function will also force-convert any object passed to it into a string (so
`force_string` flag was removed and assumed always set).
- `utils.to_bytes(text, session=None)` replaces the old `utils.to_str()` functionality and converts
str to bytes.
- `evennia.MONITOR_HANDLER.all` now takes keyword argument `obj` to only retrieve monitors from that specific
Object (rather than all monitors in the entire handler).
- Support adding `\f` in command doc strings to force where EvMore puts page breaks.
- Validation Functions now added with standard API to homogenize user input validation.
- Option Classes added to make storing user-options easier and smoother.
- `evennia.VALIDATOR_CONTAINER` and `evennia.OPTION_CONTAINER` added to load these.
### Contribs
- Evscaperoom - a full puzzle engine for making multiplayer escape rooms in Evennia. Used to make
the entry for the MUD-Coder's Guild's 2019 Game Jam with the theme "One Room", where it ranked #1.
- Evennia game-index client no longer a contrib - moved into server core and configured with new
setting `GAME_INDEX_ENABLED`.
- The `extended_room` contrib saw some backwards-incompatible refactoring:
+ All commands now begin with `CmdExtendedRoom`. So before it was `CmdExtendedLook`, now
it's `CmdExtendedRoomLook` etc.
+ The `detail` command was broken out of the `desc` command and is now a new, stand-alone command
`CmdExtendedRoomDetail`. This was done to make things easier to extend and to mimic how the detail
command works in the tutorial-world.
+ The `detail` command now also supports deleting details (like the tutorial-world version).
+ The new `ExtendedRoomCmdSet` includes all the extended-room commands and is now the recommended way
to install the extended-room contrib.
- Reworked `menu_login` contrib to use latest EvMenu standards. Now also supports guest logins.
- Mail contrib was refactored to have optional Command classes `CmdMail` for OOC+IC mail (added
to the CharacterCmdSet and `CmdMailCharacter` for IC-only mailing between chars (added to CharacterCmdSet)
### Translations
- Simplified chinese, courtesy of user MaxAlex.
## Evennia 0.8
> 2017-2018
> Released Nov 2018
### Requirements
- Up requirements to Django 1.11.x, Twisted 18 and pillow 5.2.0
- Add `inflect` dependency for automatic pluralization of object names.
### Server/Portal
- Removed `evennia_runner`, completely refactor `evennia_launcher.py` (the 'evennia' program)
with different functionality).
- Both Portal/Server are now stand-alone processes (easy to run as daemon)
- Made Portal the AMP Server for starting/restarting the Server (the AMP client)
- Dynamic logging now happens using `evennia -l` rather than by interactive mode.
- Made AMP secure against erroneous HTTP requests on the wrong port (return error messages).
- The `evennia istart` option will start/switch the Server in foreground (interactive) mode, where it logs
to terminal and can be stopped with Ctrl-C. Using `evennia reload`, or reloading in-game, will
return Server to normal daemon operation.
- For validating passwords, use safe Django password-validation backend instead of custom Evennia one.
- Alias `evennia restart` to mean the same as `evennia reload`.
### Prototype changes
- New OLC started from `olc` command for loading/saving/manipulating prototypes in a menu.
- Moved evennia/utils/spawner.py into the new evennia/prototypes/ along with all new
functionality around prototypes.
- A new form of prototype - database-stored prototypes, editable from in-game, was added. The old,
module-created prototypes remain as read-only prototypes.
- All prototypes must have a key `prototype_key` identifying the prototype in listings. This is
checked to be server-unique. Prototypes created in a module will use the global variable name they
are assigned to if no `prototype_key` is given.
- Prototype field `prototype` was renamed to `prototype_parent` to avoid mixing terms.
- All prototypes must either have `typeclass` or `prototype_parent` defined. If using
`prototype_parent`, `typeclass` must be defined somewhere in the inheritance chain. This is a
change from Evennia 0.7 which allowed 'mixin' prototypes without `typeclass`/`prototype_key`. To
make a mixin now, give it a default typeclass, like `evennia.objects.objects.DefaultObject` and just
override in the child as needed.
- Spawning an object using a prototype will automatically assign a new tag to it, named the same as
the `prototype_key` and with the category `from_prototype`.
- The spawn command was extended to accept a full prototype on one line.
- The spawn command got the /save switch to save the defined prototype and its key
- The command spawn/menu will now start an OLC (OnLine Creation) menu to load/save/edit/spawn prototypes.
### EvMenu
- Added `EvMenu.helptext_formatter(helptext)` to allow custom formatting of per-node help.
- Added `evennia.utils.evmenu.list_node` decorator for turning an EvMenu node into a multi-page listing.
- A `goto` option callable returning None (rather than the name of the next node) will now rerun the
current node instead of failing.
- Better error handling of in-node syntax errors.
- Improve dedent of default text/helptext formatter. Right-strip whitespace.
- Add `debug` option when creating menu - this turns off persistence and makes the `menudebug`
command available for examining the current menu state.
### Webclient
- Webclient now uses a plugin system to inject new components from the html file.
- Split-windows - divide input field into any number of horizontal/vertical panes and
assign different types of server messages to them.
- Lots of cleanup and bug fixes.
- Hot buttons plugin (friarzen) (disabled by default).
### Locks
- New function `evennia.locks.lockhandler.check_lockstring`. This allows for checking an object
against an arbitrary lockstring without needing the lock to be stored on an object first.
- New function `evennia.locks.lockhandler.validate_lockstring` allows for stand-alone validation
of a lockstring.
- New function `evennia.locks.lockhandler.get_all_lockfuncs` gives a dict {"name": lockfunc} for
all available lock funcs. This is useful for dynamic listings.
### Utils
- Added new `columnize` function for easily splitting text into multiple columns. At this point it
is not working too well with ansi-colored text however.
- Extend the `dedent` function with a new `baseline_index` kwarg. This allows to force all lines to
the indentation given by the given line regardless of if other lines were already a 0 indentation.
This removes a problem with the original `textwrap.dedent` which will only dedent to the least
indented part of a text.
- Added `exit_cmd` to EvMore pager, to allow for calling a command (e.g. 'look') when leaving the pager.
- `get_all_typeclasses` will return dict `{"path": typeclass, ...}` for all typeclasses available
in the system. This is used by the new `@typeclass/list` subcommand (useful for builders etc).
- `evennia.utils.dbserialize.deserialize(obj)` is a new helper function to *completely* disconnect
a mutable recovered from an Attribute from the database. This will convert all nested `_Saver*`
classes to their plain-Python counterparts.
### General
- Start structuring the `CHANGELOG` to list features in more detail.
- Docker image `evennia/evennia:develop` is now auto-built, tracking the develop branch.
- Inflection and grouping of multiple objects in default room (an box, three boxes)
- `evennia.set_trace()` is now a shortcut for launching pdb/pudb on a line in the Evennia event loop.
- Removed the enforcing of `MAX_NR_CHARACTERS=1` for `MULTISESSION_MODE` `0` and `1` by default.
- Add `evennia.utils.logger.log_sec` for logging security-related messages (marked SS in log).
### Contribs
- `Auditing` (Johnny): Log and filter server input/output for security purposes
- `Build Menu` (vincent-lg): New @edit command to edit object properties in a menu.
- `Field Fill` (Tim Ashley Jenkins): Wraps EvMenu for creating submittable forms.
- `Health Bar` (Tim Ashley Jenkins): Easily create colorful bars/meters.
- `Tree select` (Fluttersprite): Wrap EvMenu to create a common type of menu from a string.
- `Turnbattle suite` (Tim Ashley Jenkins)- the old `turnbattle.py` was moved into its own
`turnbattle/` package and reworked with many different flavors of combat systems:
- `tb_basic` - The basic turnbattle system, with initiative/turn order attack/defense/damage.
- `tb_equip` - Adds weapon and armor, wielding, accuracy modifiers.
- `tb_items` - Extends `tb_equip` with item use with conditions/status effects.
- `tb_magic` - Extends `tb_equip` with spellcasting.
- `tb_range` - Adds system for abstract positioning and movement.
- The `extended_room` contrib saw some backwards-incompatible refactoring:
- All commands now begin with `CmdExtendedRoom`. So before it was `CmdExtendedLook`, now
it's `CmdExtendedRoomLook` etc.
- The `detail` command was broken out of the `desc` command and is now a new, stand-alone command
`CmdExtendedRoomDetail`. This was done to make things easier to extend and to mimic how the detail
command works in the tutorial-world.
- The `detail` command now also supports deleting details (like the tutorial-world version).
- The new `ExtendedRoomCmdSet` includes all the extended-room commands and is now the recommended way
to install the extended-room contrib.
- Updates and some cleanup of existing contribs.
### Internationalization
- Polish translation by user ogotai
# Overview-Changelogs
> These are changelogs from a time before we used formal version numbers.
## Sept 2017:
Release of Evennia 0.7; upgrade to Django 1.11, change 'Player' to
'Account', rework the website template and a slew of other updates.
Info on what changed and how to migrate is found here:
https://groups.google.com/forum/#!msg/evennia/0JYYNGY-NfE/cDFaIwmPBAAJ
## Feb 2017:
New devel branch created, to lead up to Evennia 0.7.
## Dec 2016:
Lots of bugfixes and considerable uptick in contributors. Unittest coverage
and PEP8 adoption and refactoring.
## May 2016:
Evennia 0.6 with completely reworked Out-of-band system, making
the message path completely flexible and built around input/outputfuncs.
A completely new webclient, split into the evennia.js library and a
gui library, making it easier to customize.
## Feb 2016:
Added the new EvMenu and EvMore utilities, updated EvEdit and cleaned up
a lot of the batchcommand functionality. Started work on new Devel branch.
## Sept 2015:
Evennia 0.5. Merged devel branch, full library format implemented.
## Feb 2015:
Development currently in devel/ branch. Moved typeclasses to use
django's proxy functionality. Changed the Evennia folder layout to a
library format with a stand-alone launcher, in preparation for making
an 'evennia' pypy package and using versioning. The version we will
merge with will likely be 0.5. There is also work with an expanded
testing structure and the use of threading for saves. We also now
use Travis for automatic build checking.
## Sept 2014:
Updated to Django 1.7+ which means South dependency was dropped and
minimum Python version upped to 2.7. MULTISESSION_MODE=3 was added
and the web customization system was overhauled using the latest
functionality of django. Otherwise, mostly bug-fixes and
implementation of various smaller feature requests as we got used
to github. Many new users have appeared.
## Jan 2014:
Moved Evennia project from Google Code to github.com/evennia/evennia.
## Nov 2013:
Moved the internal webserver into the Server and added support for
out-of-band protocols (MSDP initially). This large development push
also meant fixes and cleanups of the way attributes were handled.
Tags were added, along with proper handlers for permissions, nicks
and aliases.
## May 2013:
Made players able to control more than one Character at the same
time, through the MULTISESSION_MODE=2 addition. This lead to a lot
of internal changes for the server.
## Oct 2012:
Changed Evennia from the Modified Artistic 1.0 license to the more
standard and permissive BSD license. Lots of updates and bug fixes as
more people start to use it in new ways. Lots of new caching and
speed-ups.
## 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
contrib and privately) as well testing lots of things to make sure the
implementation is basically sound, we are declaring Evennia out of
Alpha. This can mean as much or as little as you want, admittedly -
development is still heavy but the issue list is at an all-time low
and the server is slowly stabilizing as people try different things
with it. So Beta it is!
## Aug 2011:
Split Evennia into two processes: Portal and Server. After a lot of
work trying to get in-memory code-reloading to work, it's clear this
is not Python's forte - it's impossible to catch all exceptions,
especially in asynchronous code like this. Trying to do so results in
hackish, flakey and unstable code. With the Portal-Server split, the
Server can simply be rebooted while players connected to the Portal
remain connected. The two communicates over twisted's AMP protocol.
## May 2011:
The new version of Evennia, originally hitting trunk in Aug2010, is
maturing. All commands from the pre-Aug version, including IRC/IMC2
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
Apache or django-testserver). Contrib-folder added.
## Aug 2010:
Evennia-griatch-branch is ready for merging with trunk. This marks a
rather big change in the inner workings of the server, such as the
introduction of TypeClasses and Scripts (as compared to the old
ScriptParents and Events) but should hopefully bring everything
together into one consistent package as code development continues.
## May 2010:
Evennia is currently being heavily revised and cleaned from
the years of gradual piecemeal development. It is thus in a very
'Alpha' stage at the moment. This means that old code snippets
will not be backwards compatabile. Changes touch almost all
parts of Evennia's innards, from the way Objects are handled
to Events, Commands and Permissions.
## April 2010:
Griatch takes over Maintainership of the Evennia project from
the original creator Greg Taylor.
# Older
Earlier revisions, with previous maintainer, used SVN on Google Code
and have no changelogs.
First commit (Evennia's birthday) was November 20, 2006.

View file

@ -0,0 +1,141 @@
# Coding Introduction
Evennia allows for a lot of freedom when designing your game - but to code efficiently you still
need to adopt some best practices as well as find a good place to start to learn.
Here are some pointers to get you going.
## Start with the tutorial
It's highly recommended that you jump in on the [Starting Tutorial](../Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Part1-Intro.md). Even if
you only the beginning or some part of it, it covers much of the things needed to get started.
## Python
Evennia is developed using Python. Even if you are more of a designer than a coder, it is wise to
learn how to read and understand basic Python code. If you are new to Python, or need a refresher,
take a look at our [Python introduction](../Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Python-basic-introduction.md).
## Explore Evennia interactively
When new to Evennia it can be hard to find things or figure out what is available. Evennia offers a
special interactive python shell that allows you to experiment and try out things. It's recommended
to use [ipython](https://ipython.org/) for this since the vanilla python prompt is very limited. Here
are some simple commands to get started:
# [open a new console/terminal]
# [activate your evennia virtualenv in this console/terminal]
pip install -r requirements_extra.txt # install ipython etc
cd mygame
evennia shell
This will open an Evennia-aware python shell (using ipython). From within this shell, try
import evennia
evennia.<TAB>
That is, enter `evennia.` and press the `<TAB>` key. This will show you all the resources made
available at the top level of Evennia's "flat API". See the [flat API](../Evennia-API.md) page for more
info on how to explore it efficiently.
### Jupyter Notebook Support
You can also explore evennia interactively in a [Jupyter notebook](https://jupyter.readthedocs.io/en/latest/index.html#). This offers
an in-browser view of your code similar to Matlab or similar programs. There are
a few extra steps that must be taken in order for this to work:
# [open a new console/terminal]
# [activate your evennia virtualenv in this console/terminal]
cd evennia
pip install -r requirements_extra.txt # if not done already above
Next, `cd` to your game folder. _It's important that you are in the _root_ of this folder for the next command_:
evennia shell_plus --notebook &
The `&` at the end starts the process as a background process on Linux/Unix.
Skip it if your OS doesn't support this syntax. Your browser should now open
with the Jupyter interface. If not, open a browser to the link given on the
command line.
In the window, open the `new` menu in the top right and start a `Django Shell-Plus` notebook (or
open an existing one if you had one from before). In the first cell you must initialize
Evennia like so:
```python
import evennia
evennia._init()
```
_Note that the above initialization must be run every time a new new notebook/kernel is started or restarted._
After this you can import and access all of the Evennia system, same as with `evennia shell`.
### More exploration
You can complement your exploration by peeking at the sections of the much more detailed
[Evennia Component overview](../Components/Components-Overview.md). The [Tutorials](../Howtos/Howtos-Overview.md) section also contains a growing collection
of system- or implementation-specific help.
## Use a python syntax checker
Evennia works by importing your own modules and running them as part of the server. Whereas Evennia
should just gracefully tell you what errors it finds, it can nevertheless be a good idea for you to
check your code for simple syntax errors *before* you load it into the running server. There are
many python syntax checkers out there. A fast and easy one is
[pyflakes](https://pypi.python.org/pypi/pyflakes), a more verbose one is
[pylint](https://www.pylint.org/). You can also check so that your code looks up to snuff using
[pep8](https://pypi.python.org/pypi/pep8). Even with a syntax checker you will not be able to catch
every possible problem - some bugs or problems will only appear when you actually run the code. But
using such a checker can be a good start to weed out the simple problems.
## Plan before you code
Before you start coding away at your dream game, take a look at our [Game Planning](../Howtos/Beginner-Tutorial/Part2/Beginner-Tutorial-Game-Planning.md)
page. It might hopefully help you avoid some common pitfalls and time sinks.
## Code in your game folder, not in the evennia/ repository
As part of the Evennia setup you will create a game folder to host your game code. This is your
home. You should *never* need to modify anything in the `evennia` library (anything you download
from us, really). You import useful functionality from here and if you see code you like, copy&paste
it out into your game folder and edit it there.
If you find that Evennia doesn't support some functionality you need, make a [Feature
Request](github:issue) about it. Same goes for [bugs][bug]. If you add features or fix bugs
yourself, please consider [Contributing](../Contributing.md) your changes upstream!
## Learn to read tracebacks
Python is very good at reporting when and where things go wrong. A *traceback* shows everything you
need to know about crashing code. The text can be pretty long, but you usually are only interested
in the last bit, where it says what the error is and at which module and line number it happened -
armed with this info you can resolve most problems.
Evennia will usually not show the full traceback in-game though. Instead the server outputs errors
to the terminal/console from which you started Evennia in the first place. If you want more to show
in-game you can add `IN_GAME_ERRORS = True` to your settings file. This will echo most (but not all)
tracebacks both in-game as well as to the terminal/console. This is a potential security problem
though, so don't keep this active when your game goes into production.
> A common confusing error is finding that objects in-game are suddenly of the type `DefaultObject`
rather than your custom typeclass. This happens when you introduce a critical Syntax error to the
module holding your custom class. Since such a module is not valid Python, Evennia can't load it at
all. Instead of crashing, Evennia will then print the full traceback to the terminal/console and
temporarily fall back to the safe `DefaultObject` until you fix the problem and reload.
## Docs are here to help you
Some people find reading documentation extremely dull and shun it out of principle. That's your
call, but reading docs really *does* help you, promise! Evennia's documentation is pretty thorough
and knowing what is possible can often give you a lot of new cool game ideas. That said, if you
can't find the answer in the docs, don't be shy to ask questions! The [discussion
group](https://sites.google.com/site/evenniaserver/discussions) and the [irc
chat](https://webchat.freenode.net/?channels=evennia) are also there for you.
## The most important point
And finally, of course, have fun!
[feature-request]: (https://github.com/evennia/evennia/issues/new?title=Feature+Request%3a+%3Cdescriptive+title+here%3E&body=%23%23%23%23+Description+of+the+suggested+feature+and+how+it+is+supposed+to+work+for+the+admin%2fend+user%3a%0D%0A%0D%0A%0D%0A%23%23%23%23+A+list+of+arguments+for+why+you+think+this+new+feature+should+be+included+in+Evennia%3a%0D%0A%0D%0A1.%0D%0A2.%0D%0A%0D%0A%23%23%23%23+Extra+information%2c+such+as+requirements+or+ideas+on+implementation%3a%0D%0A%0D%0A
[bug](https://github.com/evennia/evennia/issues/new?title=Bug%3a+%3Cdescriptive+title+here%3E&body=%23%23%23%23+Steps+to+reproduce+the+issue%3a%0D%0A%0D%0A1.+%0D%0A2.+%0D%0A3.+%0D%0A%0D%0A%23%23%23%23+What+I+expect+to+see+and+what+I+actually+see+%28tracebacks%2c+error+messages+etc%29%3a%0D%0A%0D%0A%0D%0A%0D%0A%23%23%23%23+Extra+information%2c+such+as+Evennia+revision%2frepo%2fbranch%2c+operating+system+and+ideas+for+how+to+solve%3a%0D%0A%0D%0A)

View file

@ -0,0 +1,42 @@
# Coding and development help
This documentation aims to help you set up a sane development environment to
make your game, also if you never coded before. If you are an experienced coder, much of this will be familiar
to you, but some things may still be useful.
## Setting up a workflow
See also the [Beginner Tutorial](../Howtos/Beginner-Tutorial/Beginner-Tutorial-Intro.md).
```{toctree}
:maxdepth: 2
Version-Control.md
Updating-Your-Game.md
```
## Coding away
```{toctree}
:maxdepth: 2
Coding-Introduction.md
Debugging.md
Unit-Testing.md
Profiling.md
Quirks.md
Changelog.md
```
## Third-party integrations
```{toctree}
:maxdepth: 2
Continuous-Integration.md
Setting-up-PyCharm.md
```

View file

@ -0,0 +1,198 @@
# Continuous Integration - TeamCity (linux)
This sets up a TeamCity build integration environment on Linux.
## Prerequisites
- Follow [TeamCity](https://www.jetbrains.com/teamcity/) 's in-depth
[Setup Guide](https://confluence.jetbrains.com/display/TCD8/Installing+and+Configuring+the+TeamCity+Server).
- You need to use [Version Control](./Version-Control.md).
After meeting the preparation steps for your specific environment, log on to your teamcity interface
at `http://<your server>:8111/`.
Create a new project named "Evennia" and in it construct a new template called `continuous-integration`.
## A Quick Overview
_Templates_ are fancy objects in TeamCity that allow an administrator to define build steps that are
shared between one or more build projects. Assigning a VCS Root (Source Control) is unnecessary at
this stage, primarily you'll be worrying about the build steps and your default parameters (both
visible on the tabs to the left.)
## Template Setup
In this template, you'll be outlining the steps necessary to build your specific game. (A number of
sample scripts are provided under this section below!) Click Build Steps and prepare your general
flow. For this example, we will be doing a few basic example steps:
* Transforming the Settings.py file - We do this to update ports or other information that make your production
environment unique from your development environment.
* Making migrations and migrating the game database.
* Publishing the game files.
* Reloading the server.
For each step we'll being use the "Command Line Runner" (a fancy name for a shell script executor).
Create a build step with the name: "Transform Configuration" and add the script:
```bash
#!/bin/bash
# Replaces the game configuration with one
# appropriate for this deployment.
CONFIG="%system.teamcity.build.checkoutDir%/server/conf/settings.py"
MYCONF="%system.teamcity.build.checkoutDir%/server/conf/my.cnf"
sed -e 's/TELNET_PORTS = [4000]/TELNET_PORTS = [%game.ports%]/g' "$CONFIG" > "$CONFIG".tmp && mv
"$CONFIG".tmp "$CONFIG"
sed -e 's/WEBSERVER_PORTS = [(4001, 4002)]/WEBSERVER_PORTS = [%game.webports%]/g' "$CONFIG" >
"$CONFIG".tmp && mv "$CONFIG".tmp "$CONFIG"
``````
```bash
# settings.py MySQL DB configuration
echo Configuring Game Database...
echo "" >> "$CONFIG"
echo "######################################################################" >> "$CONFIG"
echo "# MySQL Database Configuration" >> "$CONFIG"
echo "######################################################################" >> "$CONFIG"
echo "DATABASES = {" >> "$CONFIG"
echo " 'default': {" >> "$CONFIG"
echo " 'ENGINE': 'django.db.backends.mysql'," >> "$CONFIG"
echo " 'OPTIONS': {" >> "$CONFIG"
echo " 'read_default_file': 'server/conf/my.cnf'," >> "$CONFIG"
echo " }," >> "$CONFIG"
echo " }" >> "$CONFIG"
echo "}" >> "$CONFIG"
# Create the My.CNF file.
echo "[client]" >> "$MYCONF"
echo "database = %mysql.db%" >> "$MYCONF"
echo "user = %mysql.user%" >> "$MYCONF"
echo "password = %mysql.pass%" >> "$MYCONF"
echo "default-character-set = utf8" >> "$MYCONF"
```
If you look at the parameters side of the page after saving this script, you'll notice that some new
parameters have been populated for you. This is because we've included new teamcity configuration
parameters that are populated when the build itself is ran. When creating projects that inherit this
template, we'll be able to fill in or override those parameters for project-specific configuration.
Go ahead and create another build step called "Make Database Migration"
If you're using Sqlite3 for your game (default database), it's prudent to change working directory on this
step to your game dir.
```bash
#!/bin/bash
# Update the DB migration
LOGDIR="server/logs"
. %evenv.dir%/bin/activate
# Check that the logs directory exists.
if [ ! -d "$LOGDIR" ]; then
# Control will enter here if $LOGDIR doesn't exist.
mkdir "$LOGDIR"
fi
evennia makemigrations
```
Create yet another build step, this time named: "Execute Database Migration":
If you're using Sqlite3 for your game (default database), it's prudent to change working directory on this
step to your game dir.
```bash
#!/bin/bash
# Apply the database migration.
LOGDIR="server/logs"
. %evenv.dir%/bin/activate
# Check that the logs directory exists.
if [ ! -d "$LOGDIR" ]; then
# Control will enter here if $LOGDIR doesn't exist.
mkdir "$LOGDIR"
fi
evennia migrate
```
Our next build step is where we actually publish our build. Up until now, all work on game has been
done in a 'work' directory on TeamCity's build agent. From that directory we will now copy our files
to where our game actually exists on the local server.
Create a new build step called "Publish Build". If you're using SQlite3 on your game, be sure to order this step ABOVE
the Database Migration steps. The build order will matter!
```bash
#!/bin/bash
# Publishes the build to the proper build directory.
DIRECTORY="<game_dir>"
if [ ! -d "$DIRECTORY" ]; then
# Control will enter here if $DIRECTORY doesn't exist.
mkdir "$DIRECTORY"
fi
# Copy all the files.
cp -ruv %teamcity.build.checkoutDir%/* "$DIRECTORY"
chmod -R 775 "$DIRECTORY"
```
Finally the last script will reload our game for us.
Create a new script called "Reload Game":
The working directory on this build step will be: `%game.dir%`
```bash
#!/bin/bash
# Apply the database migration.
LOGDIR="server/logs"
PIDDIR="server/server.pid"
. %evenv.dir%/bin/activate
# Check that the logs directory exists.
if [ ! -d "$LOGDIR" ]; then
# Control will enter here if $LOGDIR doesn't exist.
mkdir "$LOGDIR"
fi
# Check that the server is running.
if [ -d "$PIDDIR" ]; then
# Control will enter here if the game is running.
evennia reload
fi
```
Now the template is ready for use! It would be useful this time to revisit the parameters page and
set the evenv parameter to the directory where your virtualenv exists: IE "/srv/mush/evenv".
### Creating the Project
Now it's time for the last few steps to set up a CI environment.
* Return to the Evennia Project overview/administration page.
* Create a new Sub-Project called "Production". This will be the category that holds our actual game.
* Create a new Build Configuration in Production with the name of your MUSH. Base this configuration off of the
continuous-integration template we made earlier.
* In the build configuration, enter VCS roots and create a new VCS root that points to the
branch/version control that you are using.
* Go to the parameters page and fill in the undefined parameters for your specific configuration.
* If you wish for the CI to run every time a commit is made, go to the VCS triggers and add one for
"On Every Commit".
And you're done! At this point, you can return to the project overview page and queue a new build
for your game. If everything was set up correctly, the build will complete successfully. Additional
build steps could be added or removed at this point, adding some features like Unit Testing or more!

View file

@ -0,0 +1,39 @@
# Continuous integration with Travis
[Travis CI](https://travis-ci.org/) is an online service for checking, validating and potentially
deploying code automatically. It can check that every commit is building successfully after every
commit to its Github repository.
If your game is open source on Github you may use Travis for free.
See [the Travis docs](https://docs.travis-ci.com/user/getting- started/) for how to get started.
After logging in you will get to point Travis to your repository on github. One further thing you
need to set up yourself is a Travis config file named `.travis.yml` (note the initial period `.`).
This should be created in the root of your game directory. The idea with this file is that it
describes what Travis needs to import and build in order to create an instance of Evennia from
scratch and then run validation tests on it. Here is an example:
``` yaml
language: python
python:
- "3.10"
install:
- git clone https://github.com/evennia/evennia.git
- cd evennia
- pip install -e .
- cd $TRAVIS_BUILD_DIR
script:
- evennia migrate
- evennia test --settings settings.py .
```
This will tell travis how to download Evennia, install it, set up a database and then run
your own test suite (inside the game dir). Use `evennia test evennia` if you also want to
run the Evennia full test suite.
You need to add this file to git (`git add .travis.yml`) and then commit your changes before Travis
will be able to see it.
For properly testing your game you of course also need to write unittests.
The [Unit testing](./Unit-Testing.md) doc page gives some ideas on how to set those up for Evennia.
You should be able to refer to that for making tests fitting your game.

View file

@ -0,0 +1,36 @@
# Continuous Integration
One of the advantages of Evennia over traditional MU* development systems is that Evennia can
integrate into enterprise-level integration environments and source control.
## What is Continuous Integration (CI)?
[Continuous Integration (CI)](https://www.thoughtworks.com/continuous-integration) is a development
practice that requires developers to integrate code into a shared repository.
Each check-in is then verified by an automated build, allowing teams to detect problems early. This
can be set up to safely deploy data to a production server only after tests have passed, for example.
For Evennia, continuous integration allows an automated build process to:
* Pull down a latest build from Source Control.
* Run migrations on the backing SQL database.
* Automate additional unique tasks for that project.
* Run unit tests.
* Publish those files to the server directory
* Reload the game.
## List of continuous integration tools
There are a lot of tools and services providing CI functionality. Here are a few that people have used
with Evennia:
```{toctree}
:maxdepth: 1
Continuous-Integration-Travis.md
Continuous-Integration-TeamCity.md
```
[This is an overview of other tools](https://www.atlassian.com/continuous-delivery/continuous-integration/tools)
(external link).

View file

@ -0,0 +1,296 @@
# Debugging
Sometimes, an error is not trivial to resolve. A few simple `print` statements is not enough to find
the cause of the issue. Running a *debugger* can then be very helpful and save a lot of time.
Debugging
means running Evennia under control of a special *debugger* program. This allows you to stop the
action at a given point, view the current state and step forward through the program to see how its
logic works.
Evennia natively supports these debuggers:
- [Pdb](https://docs.python.org/2/library/pdb.html) is a part of the Python distribution and
available out-of-the-box.
- [PuDB](https://pypi.org/project/pudb/) is a third-party debugger that has a slightly more
'graphical', curses-based user interface than pdb. It is installed with `pip install pudb`.
## Debugging Evennia
To run Evennia with the debugger, follow these steps:
1. Find the point in the code where you want to have more insight. Add the following line at that
point.
```python
from evennia import set_trace;set_trace()
```
2. (Re-)start Evennia in interactive (foreground) mode with `evennia istart`. This is important -
without this step the debugger will not start correctly - it will start in this interactive
terminal.
3. Perform the steps that will trigger the line where you added the `set_trace()` call. The debugger
will start in the terminal from which Evennia was interactively started.
The `evennia.set_trace` function takes the following arguments:
```python
evennia.set_trace(debugger='auto', term_size=(140, 40))
```
Here, `debugger` is one of `pdb`, `pudb` or `auto`. If `auto`, use `pudb` if available, otherwise
use `pdb`. The `term_size` tuple sets the viewport size for `pudb` only (it's ignored by `pdb`).
## A simple example using pdb
The debugger is useful in different cases, but to begin with, let's see it working in a command.
Add the following test command (which has a range of deliberate errors) and also add it to your
default cmdset. Then restart Evennia in interactive mode with `evennia istart`.
```python
# In file commands/command.py
class CmdTest(Command):
"""
A test command just to test pdb.
Usage:
test
"""
key = "test"
def func(self):
from evennia import set_trace; set_trace() # <--- start of debugger
obj = self.search(self.args)
self.msg("You've found {}.".format(obj.get_display_name()))
```
If you type `test` in your game, everything will freeze. You won't get any feedback from the game,
and you won't be able to enter any command (nor anyone else). It's because the debugger has started
in your console, and you will find it here. Below is an example with `pdb`.
```
...
> .../mygame/commands/command.py(79)func()
-> obj = self.search(self.args)
(Pdb)
```
`pdb` notes where it has stopped execution and, what line is about to be executed (in our case, `obj
= self.search(self.args)`), and ask what you would like to do.
### Listing surrounding lines of code
When you have the `pdb` prompt `(Pdb)`, you can type in different commands to explore the code. The
first one you should know is `list` (you can type `l` for short):
```
(Pdb) l
43
44 key = "test"
45
46 def func(self):
47 from evennia import set_trace; set_trace() # <--- start of debugger
48 -> obj = self.search(self.args)
49 self.msg("You've found {}.".format(obj.get_display_name()))
50
51 # -------------------------------------------------------------
52 #
53 # The default commands inherit from
(Pdb)
```
Okay, this didn't do anything spectacular, but when you become more confident with `pdb` and find
yourself in lots of different files, you sometimes need to see what's around in code. Notice that
there is a little arrow (`->`) before the line that is about to be executed.
This is important: **about to be**, not **has just been**. You need to tell `pdb` to go on (we'll
soon see how).
### Examining variables
`pdb` allows you to examine variables (or really, to run any Python instruction). It is very useful
to know the values of variables at a specific line. To see a variable, just type its name (as if
you were in the Python interpreter:
```
(Pdb) self
<commands.command.CmdTest object at 0x045A0990>
(Pdb) self.args
u''
(Pdb) self.caller
<Character: XXX>
(Pdb)
```
If you try to see the variable `obj`, you'll get an error:
```
(Pdb) obj
*** NameError: name 'obj' is not defined
(Pdb)
```
That figures, since at this point, we haven't created the variable yet.
> Examining variable in this way is quite powerful. You can even run Python code and keep on
> executing, which can help to check that your fix is actually working when you have identified an
> error. If you have variable names that will conflict with `pdb` commands (like a `list`
> variable), you can prefix your variable with `!`, to tell `pdb` that what follows is Python code.
### Executing the current line
It's time we asked `pdb` to execute the current line. To do so, use the `next` command. You can
shorten it by just typing `n`:
```
(Pdb) n
AttributeError: "'CmdTest' object has no attribute 'search'"
> .../mygame/commands/command.py(79)func()
-> obj = self.search(self.args)
(Pdb)
```
`Pdb` is complaining that you try to call the `search` method on a command... whereas there's no
`search` method on commands. The character executing the command is in `self.caller`, so we might
change our line:
```python
obj = self.caller.search(self.args)
```
### Letting the program run
`pdb` is waiting to execute the same instruction... it provoked an error but it's ready to try
again, just in case. We have fixed it in theory, but we need to reload, so we need to enter a
command. To tell `pdb` to terminate and keep on running the program, use the `continue` (or `c`)
command:
```
(Pdb) c
...
```
You see an error being caught, that's the error we have fixed... or hope to have. Let's reload the
game and try again. You need to run `evennia istart` again and then run `test` to get into the
command again.
```
> .../mygame/commands/command.py(79)func()
-> obj = self.caller.search(self.args)
(Pdb)
```
`pdb` is about to run the line again.
```
(Pdb) n
> .../mygame/commands/command.py(80)func()
-> self.msg("You've found {}.".format(obj.get_display_name()))
(Pdb)
```
This time the line ran without error. Let's see what is in the `obj` variable:
```
(Pdb) obj
(Pdb) print obj
None
(Pdb)
```
We have entered the `test` command without parameter, so no object could be found in the search
(`self.args` is an empty string).
Let's allow the command to continue and try to use an object name as parameter (although, we should
fix that bug too, it would be better):
```
(Pdb) c
...
```
Notice that you'll have an error in the game this time. Let's try with a valid parameter. I have
another character, `barkeep`, in this room:
```test barkeep```
And again, the command freezes, and we have the debugger opened in the console.
Let's execute this line right away:
```
> .../mygame/commands/command.py(79)func()
-> obj = self.caller.search(self.args)
(Pdb) n
> .../mygame/commands/command.py(80)func()
-> self.msg("You've found {}.".format(obj.get_display_name()))
(Pdb) obj
<Character: barkeep>
(Pdb)
```
At least this time we have found the object. Let's process...
```
(Pdb) n
TypeError: 'get_display_name() takes exactly 2 arguments (1 given)'
> .../mygame/commands/command.py(80)func()
-> self.msg("You've found {}.".format(obj.get_display_name()))
(Pdb)
```
As an exercise, fix this error, reload and run the debugger again. Nothing better than some
experimenting!
Your debugging will often follow the same strategy:
1. Receive an error you don't understand.
2. Put a breaking point **BEFORE** the error occurs.
3. Run the code again and see the debugger open.
4. Run the program line by line,examining variables, checking the logic of instructions.
5. Continue and try again, each step a bit further toward the truth and the working feature.
### Stepping through a function
`n` is useful, but it will avoid stepping inside of functions if it can. But most of the time, when
we have an error we don't understand, it's because we use functions or methods in a way that wasn't
intended by the developer of the API. Perhaps using wrong arguments, or calling the function in a
situation that would cause a bug. When we have a line in the debugger that calls a function or
method, we can "step" to examine it further. For instance, in the previous example, when `pdb` was
about to execute `obj = self.caller.search(self.args)`, we may want to see what happens inside of
the `search` method.
To do so, use the `step` (or `s`) command. This command will show you the definition of the
function/method and you can then use `n` as before to see it line-by-line. In our little example,
stepping through a function or method isn't that useful, but when you have an impressive set of
commands, functions and so on, it might really be handy to examine some feature and make sure they
operate as planned.
## Cheat-sheet of pdb/pudb commands
PuDB and Pdb share the same commands. The only real difference is how it's presented. The `look`
command is not needed much in `pudb` since it displays the code directly in its user interface.
| Pdb/PuDB command | To do what |
| ----------- | ---------- |
| list (or l) | List the lines around the point of execution (not needed for `pudb`, it will show
this directly). |
| print (or p) | Display one or several variables. |
| `!` | Run Python code (using a `!` is often optional). |
| continue (or c) | Continue execution and terminate the debugger for this time. |
| next (or n) | Execute the current line and goes to the next one. |
| step (or s) | Step inside of a function or method to examine it. |
| `<RETURN>` | Repeat the last command (don't type `n` repeatedly, just type it once and then press
`<RETURN>` to repeat it). |
If you want to learn more about debugging with Pdb, you will find an [interesting tutorial on that
topic here](https://pymotw.com/3/pdb/).

View file

@ -0,0 +1,253 @@
# Profiling
*This is considered an advanced topic mainly of interest to server developers.*
## Introduction
Sometimes it can be useful to try to determine just how efficient a particular
piece of code is, or to figure out if one could speed up things more than they
are. There are many ways to test the performance of Python and the running
server.
Before digging into this section, remember Donald Knuth's
[words of wisdom](https://en.wikipedia.org/wiki/Program_optimization#When_to_optimize):
> *[...]about 97% of the time: Premature optimization is the root of all evil*.
That is, don't start to try to optimize your code until you have actually
identified a need to do so. This means your code must actually be working before
you start to consider optimization. Optimization will also often make your code
more complex and harder to read. Consider readability and maintainability and
you may find that a small gain in speed is just not worth it.
## Simple timer tests
Python's `timeit` module is very good for testing small things. For example, in
order to test if it is faster to use a `for` loop or a list comprehension you
could use the following code:
```python
import timeit
# Time to do 1000000 for loops
timeit.timeit("for i in range(100):\n a.append(i)", setup="a = []")
<<< 10.70982813835144
# Time to do 1000000 list comprehensions
timeit.timeit("a = [i for i in range(100)]")
<<< 5.358283996582031
```
The `setup` keyword is used to set up things that should not be included in the
time measurement, like `a = []` in the first call.
By default the `timeit` function will re-run the given test 1000000 times and
returns the *total time* to do so (so *not* the average per test). A hint is to
not use this default for testing something that includes database writes - for
that you may want to use a lower number of repeats (say 100 or 1000) using the
`number=100` keyword.
## Using cProfile
Python comes with its own profiler, named cProfile (this is for cPython, no
tests have been done with `pypy` at this point). Due to the way Evennia's
processes are handled, there is no point in using the normal way to start the
profiler (`python -m cProfile evennia.py`). Instead you start the profiler
through the launcher:
evennia --profiler start
This will start Evennia with the Server component running (in daemon mode) under
cProfile. You could instead try `--profile` with the `portal` argument to
profile the Portal (you would then need to
[start the Server separately](../Setup/Start-Stop-Reload.md)).
Please note that while the profiler is running, your process will use a lot more
memory than usual. Memory usage is even likely to climb over time. So don't
leave it running perpetually but monitor it carefully (for example using the
`top` command on Linux or the Task Manager's memory display on Windows).
Once you have run the server for a while, you need to stop it so the profiler
can give its report. Do *not* kill the program from your task manager or by
sending it a kill signal - this will most likely also mess with the profiler.
Instead either use `evennia.py stop` or (which may be even better), use
`@shutdown` from inside the game.
Once the server has fully shut down (this may be a lot slower than usual) you
will find that profiler has created a new file `mygame/server/logs/server.prof`.
### Analyzing the profile
The `server.prof` file is a binary file. There are many ways to analyze and
display its contents, all of which has only been tested in Linux (If you are a
Windows/Mac user, let us know what works).
You can look at the contents of the profile file with Python's in-built `pstats`
module in the evennia shell (it's recommended you install `ipython` with `pip
install ipython` in your virtualenv first, for prettier output):
evennia shell
Then in the shell
```python
import pstats
from pstats import SortKey
p = pstats.Stats('server/log/server.prof')
p.strip_dirs().sort_stats(-1).print_stats()
```
See the
[Python profiling documentation](https://docs.python.org/3/library/profile.html#instant-user-s-manual)
for more information.
You can also visualize the data in various ways.
- [Runsnake](https://pypi.org/project/RunSnakeRun/) visualizes the profile to
give a good overview. Install with `pip install runsnakerun`. Note that this
may require a C compiler and be quite slow to install.
- For more detailed listing of usage time, you can use
[KCachegrind](http://kcachegrind.sourceforge.net/html/Home.html). To make
KCachegrind work with Python profiles you also need the wrapper script
[pyprof2calltree](https://pypi.python.org/pypi/pyprof2calltree/). You can get
`pyprof2calltree` via `pip` whereas KCacheGrind is something you need to get
via your package manager or their homepage.
How to analyze and interpret profiling data is not a trivial issue and depends
on what you are profiling for. Evennia being an asynchronous server can also
confuse profiling. Ask on the mailing list if you need help and be ready to be
able to supply your `server.prof` file for comparison, along with the exact
conditions under which it was obtained.
## The Dummyrunner
It is difficult to test "actual" game performance without having players in your
game. For this reason Evennia comes with the *Dummyrunner* system. The
Dummyrunner is a stress-testing system: a separate program that logs into your
game with simulated players (aka "bots" or "dummies"). Once connected, these
dummies will semi-randomly perform various tasks from a list of possible
actions. Use `Ctrl-C` to stop the Dummyrunner.
```{warning}
You should not run the Dummyrunner on a production database. It
will spawn many objects and also needs to run with general permissions.
This is the recommended process for using the dummy runner:
```
1. Stop your server completely with `evennia stop`.
1. At _the end_ of your `mygame/server/conf.settings.py` file, add the line
from evennia.server.profiling.settings_mixin import *
This will override your settings and disable Evennia's rate limiters and
DoS-protections, which would otherwise block mass-connecting clients from
one IP. Notably, it will also change to a different (faster) password hasher.
1. (recommended): Build a new database. If you use default Sqlite3 and want to
keep your existing database, just rename `mygame/server/evennia.db3` to
`mygame/server/evennia.db3_backup` and run `evennia migrate` and `evennia
start` to create a new superuser as usual.
1. (recommended) Log into the game as your superuser. This is just so you
can manually check response. If you kept an old database, you will _not_
be able to connect with an _existing_ user since the password hasher changed!
1. Start the dummyrunner with 10 dummy users from the terminal with
evennia --dummyrunner 10
Use `Ctrl-C` (or `Cmd-C`) to stop it.
If you want to see what the dummies are actually doing you can run with a single
dummy:
evennia --dummyrunner 1
The inputs/outputs from the dummy will then be printed. By default the runner
uses the 'looker' profile, which just logs in and sends the 'look' command
over and over. To change the settings, copy the file
`evennia/server/profiling/dummyrunner_settings.py` to your `mygame/server/conf/`
directory, then add this line to your settings file to use it in the new
location:
DUMMYRUNNER_SETTINGS_MODULE = "server/conf/dummyrunner_settings.py"
The dummyrunner settings file is a python code module in its own right - it
defines the actions available to the dummies. These are just tuples of command
strings (like "look here") for the dummy to send to the server along with a
probability of them happening. The dummyrunner looks for a global variable
`ACTIONS`, a list of tuples, where the first two elements define the
commands for logging in/out of the server.
Below is a simplified minimal setup (the default settings file adds a lot more
functionality and info):
```python
# minimal dummyrunner setup file
# Time between each dummyrunner "tick", in seconds. Each dummy will be called
# with this frequency.
TIMESTEP = 1
# Chance of a dummy actually performing an action on a given tick. This
# spreads out usage randomly, like it would be in reality.
CHANCE_OF_ACTION = 0.5
# Chance of a currently unlogged-in dummy performing its login action every
# tick. This emulates not all accounts logging in at exactly the same time.
CHANCE_OF_LOGIN = 0.01
# Which telnet port to connect to. If set to None, uses the first default
# telnet port of the running server.
TELNET_PORT = None
# actions
def c_login(client):
name = f"Character-{client.gid}"
pwd = f"23fwsf23sdfw23wef23"
return (
f"create {name} {pwd}"
f"connect {name} {pwd}"
)
def c_logout(client):
return ("quit", )
def c_look(client):
return ("look here", "look me")
# this is read by dummyrunner.
ACTIONS = (
c_login,
c_logout,
(1.0, c_look) # (probability, command-generator)
)
```
At the bottom of the default file are a few default profiles you can test out
by just setting the `PROFILE` variable to one of the options.
### Dummyrunner hints
- Don't start with too many dummies. The Dummyrunner taxes the server much more
than 'real' users tend to do. Start with 10-100 to begin with.
- Stress-testing can be fun, but also consider what a 'realistic' number of
users would be for your game.
- Note in the dummyrunner output how many commands/s are being sent to the
server by all dummies. This is usually a lot higher than what you'd
realistically expect to see from the same number of users.
- The default settings sets up a 'lag' measure to measaure the round-about
message time. It updates with an average every 30 seconds. It can be worth to
have this running for a small number of dummies in one terminal before adding
more by starting another dummyrunner in another terminal - the first one will
act as a measure of how lag changes with different loads. Also verify the
lag-times by entering commands manually in-game.
- Check the CPU usage of your server using `top/htop` (linux). In-game, use the
`server` command.
- You can run the server with `--profiler start` to test it with dummies. Note
that the profiler will itself affect server performance, especially memory
consumption.
- Generally, the dummyrunner system makes for a decent test of general
performance; but it is of course hard to actually mimic human user behavior.
For this, actual real-game testing is required.

View file

@ -0,0 +1,143 @@
# Quirks
This is a list of various quirks or common stumbling blocks that people often ask about or report
when using (or trying to use) Evennia. They are not bugs.
## Forgetting to use `reload` to see changes to your typeclasses
Firstly: Reloading the server is a safe and usually quick operation which will *not* disconnect any
accounts.
New users tend to forget this step. When editing source code (such as when tweaking typeclasses and
commands or adding new commands to command sets) you need to either use the in-game `@reload`
command or, from the command line do `python evennia.py reload` before you see your changes.
## Web admin to create new Account
If you use the default login system and are trying to use the Web admin to create a new Player
account, you need to consider which `MULTIACCOUNT_MODE` you are in. If you are in
`MULTIACCOUNT_MODE` `0` or `1`, the login system expects each Account to also have a Character
object named the same as the Account - there is no character creation screen by default. If using
the normal mud login screen, a Character with the same name is automatically created and connected
to your Account. From the web interface you must do this manually.
So, when creating the Account, make sure to also create the Character *from the same form* as you
create the Account from. This should set everything up for you. Otherwise you need to manually set
the "account" property on the Character and the "character" property on the Account to point to each
other. You must also set the lockstring of the Character to allow the Account to "puppet" this
particular character.
## Mutable attributes and their connection to the database
When storing a mutable object (usually a list or a dictionary) in an Attribute
```python
object.db.mylist = [1,2,3]
```
you should know that the connection to the database is retained also if you later extract that
Attribute into another variable (what is stored and retrieved is actually a `PackedList` or a
`PackedDict` that works just like their namesakes except they save themselves to the database when
changed). So if you do
```python
alist = object.db.mylist
alist.append(4)
```
this updates the database behind the scenes, so both `alist` and `object.db.mylist` are now
`[1,2,3,4]`
If you don't want this, Evennia provides a way to stably disconnect the mutable from the database by
use of `evennia.utils.dbserialize.deserialize`:
```python
from evennia.utils.dbserialize import deserialize
blist = deserialize(object.db.mylist)
blist.append(4)
```
The property `blist` is now `[1,2,3,4]` whereas `object.db.mylist` remains unchanged. If you want to
update the database you'd need to explicitly re-assign the updated data to the `mylist` Attribute.
## Commands are matched by name *or* alias
When merging [command sets](../Components/Commands.md) it's important to remember that command objects are identified
*both* by key *or* alias. So if you have a command with a key `look` and an alias `ls`, introducing
another command with a key `ls` will be assumed by the system to be *identical* to the first one.
This usually means merging cmdsets will overload one of them depending on priority. Whereas this is
logical once you know how command objects are handled, it may be confusing if you are just looking
at the command strings thinking they are parsed as-is.
## Objects turning to `DefaultObject`
A common confusing error for new developers is finding that one or more objects in-game are suddenly
of the type `DefaultObject` rather than the typeclass you wanted it to be. This happens when you
introduce a critical Syntax error to the module holding your custom class. Since such a module is
not valid Python, Evennia can't load it at all to get to the typeclasses within. To keep on running,
Evennia will solve this by printing the full traceback to the terminal/console and temporarily fall
back to the safe `DefaultObject` until you fix the problem and reload. Most errors of this kind will
be caught by any good text editors. Keep an eye on the terminal/console during a reload to catch
such errors - you may have to scroll up if your window is small.
## Overriding of magic methods
Python implements a system of [magic
methods](https://docs.python.org/3/reference/datamodel.html#emulating-container-types), usually
prefixed and suffixed by double-underscores (`__example__`) that allow object instances to have
certain operations performed on them without needing to do things like turn them into strings or
numbers first-- for example, is `obj1` greater than or equal to `obj2`?
Neither object is a number, but given `obj1.size == "small"` and `obj2.size == "large"`, how might
one compare these two arbitrary English adjective strings to figure out which is greater than the
other? By defining the `__ge__` (greater than or equal to) magic method on the object class in which
you figure out which word has greater significance, perhaps through use of a mapping table
(`{'small':0, 'large':10}`) or other lookup and comparing the numeric values of each.
Evennia extensively makes use of magic methods on typeclasses to do things like initialize objects,
check object existence or iterate over objects in an inventory or container. If you override or
interfere with the return values from the methods Evennia expects to be both present and working, it
can result in very inconsistent and hard-to-diagnose errors.
The moral of the story-- it can be dangerous to tinker with magic methods on typeclassed objects.
Try to avoid doing so.
## Things to remember about the flat API
The flat API is a series of 'shortcuts' on the `evennia` main library root (defined in
`evennia/__init__.py`). Its componentas are documented [as part of the auto-documentation](../Evennia-API.md).
### To remember when importing from `evennia`
Properties on the root of the `evennia` package are *not* modules in their own right. They are just
shortcut properties stored in the `evennia/__init__.py` module. That means that you cannot use dot-
notation to `import` nested module-names over `evennia`. The rule of thumb is that you cannot use
`import` for more than one level down. Hence you can do
```python
import evennia
print(evennia.default_cmds.CmdLook)
```
or import one level down
```python
from evennia import default_cmds
print(default_cmds.CmdLook)
```
but you *cannot* import two levels down
```python
from evennia.default_cmds import CmdLook # error!
```
This will give you an `ImportError` telling you that the module `default_cmds` cannot be found -
this is becasue `default_cmds` is just a *variable* stored in `evennia.__init__.py`; this cannot be
imported from. If you really want full control over which level of package you import you can always
bypass the root package and import directly from from the real location. For example
`evennia.DefaultObject` is a shortcut to `evennia.objects.objects.DefaultObject`. Using this full
path will have the import mechanism work normally. See `evennia/__init__.py` to see where the
package imports from.

View file

@ -0,0 +1,114 @@
# Setting up PyCharm with Evennia
[PyCharm](https://www.jetbrains.com/pycharm/) is a Python developer's IDE from Jetbrains available
for Windows, Mac and Linux. It is a commercial product but offer free trials, a scaled-down
community edition and also generous licenses for OSS projects like Evennia.
> This page was originally tested on Windows (so use Windows-style path examples), but should work
the same for all platforms.
First, install Evennia on your local machine with [[Getting Started]]. If you're new to PyCharm,
loading your project is as easy as selecting the `Open` option when PyCharm starts, and browsing to
your game folder (the one created with `evennia --init`). We refer to it as `mygame` here.
If you want to be able to examine evennia's core code or the scripts inside your virtualenv, you'll
need to add them to your project too:
1. Go to `File > Open...`
1. Select the folder (i.e. the `evennia` root)
1. Select "Open in current window" and "Add to currently opened projects"
## Setting up the project interpreter
It's a good idea to do this before attempting anything further. The rest of this page assumes your
project is already configured in PyCharm.
1. Go to `File > Settings... > Project: \<mygame\> > Project Interpreter`
1. Click the Gear symbol `> Add local`
1. Navigate to your `evenv/scripts directory`, and select Python.exe
Enjoy seeing all your imports checked properly, setting breakpoints, and live variable watching!
## Attaching PyCharm debugger to Evennia
1. Launch Evennia in your preferred way (usually from a console/terminal)
1. Open your project in PyCharm
1. In the PyCharm menu, select `Run > Attach to Local Process...`
1. From the list, pick the `twistd` process with the `server.py` parameter (Example: `twistd.exe
--nodaemon --logfile=\<mygame\>\server\logs\server.log --python=\<evennia
repo\>\evennia\server\server.py`)
Of course you can attach to the `portal` process as well. If you want to debug the Evennia launcher
or runner for some reason (or just learn how they work!), see Run Configuration below.
> NOTE: Whenever you reload Evennia, the old Server process will die and a new one start. So when
you restart you have to detach from the old and then reattach to the new process that was created.
> To make the process less tedious you can apply a filter in settings to show only the server.py
process in the list. To do that navigate to: `Settings/Preferences | Build, Execution, Deployment |
Python Debugger` and then in `Attach to process` field put in: `twistd.exe" --nodaemon`. This is an
example for windows, I don't have a working mac/linux box.
![Example process filter configuration](https://i.imgur.com/vkSheR8.png)
## Setting up an Evennia run configuration
This configuration allows you to launch Evennia from inside PyCharm. Besides convenience, it also
allows suspending and debugging the evennia_launcher or evennia_runner at points earlier than you
could by running them externally and attaching. In fact by the time the server and/or portal are
running the launcher will have exited already.
1. Go to `Run > Edit Configutations...`
1. Click the plus-symbol to add a new configuration and choose Python
1. Add the script: `\<yourrepo\>\evenv\Scripts\evennia_launcher.py` (substitute your virtualenv if
it's not named `evenv`)
1. Set script parameters to: `start -l` (-l enables console logging)
1. Ensure the chosen interpreter is from your virtualenv
1. Set Working directory to your `mygame` folder (not evenv nor evennia)
1. You can refer to the PyCharm documentation for general info, but you'll want to set at least a
config name (like "MyMUD start" or similar).
Now set up a "stop" configuration by following the same steps as above, but set your Script
parameters to: stop (and name the configuration appropriately).
A dropdown box holding your new configurations should appear next to your PyCharm run button.
Select MyMUD start and press the debug icon to begin debugging. Depending on how far you let the
program run, you may need to run your "MyMUD stop" config to actually stop the server, before you'll
be able start it again.
## Alternative run configuration - utilizing logfiles as source of data
This configuration takes a bit different approach as instead of focusing on getting the data back
through logfiles. Reason for that is this way you can easily separate data streams, for example you
rarely want to follow both server and portal at the same time, and this will allow it. This will
also make sure to stop the evennia before starting it, essentially working as reload command (it
will also include instructions how to disable that part of functionality). We will start by defining
a configuration that will stop evennia. This assumes that `upfire` is your pycharm project name, and
also the game name, hence the `upfire/upfire` path.
1. Go to `Run > Edit Configutations...`\
1. Click the plus-symbol to add a new configuration and choose the python interpreter to use (should
be project default)
1. Name the configuration as "stop evennia" and fill rest of the fields accordingly to the image:
![Stop run configuration](https://i.imgur.com/gbkXhlG.png)
1. Press `Apply`
Now we will define the start/reload command that will make sure that evennia is not running already,
and then start the server in one go.
1. Go to `Run > Edit Configutations...`\
1. Click the plus-symbol to add a new configuration and choose the python interpreter to use (should
be project default)
1. Name the configuration as "start evennia" and fill rest of the fields accordingly to the image:
![Start run configuration](https://i.imgur.com/5YEjeHq.png)
1. Navigate to the `Logs` tab and add the log files you would like to follow. The picture shows
adding `portal.log` which will show itself in `portal` tab when running:
![Configuring logs following](https://i.imgur.com/gWYuOWl.png)
1. Skip the following steps if you don't want the launcher to stop evennia before starting.
1. Head back to `Configuration` tab and press the `+` sign at the bottom, under `Before launch....`
and select `Run another configuration` from the submenu that will pop up.
1. Click `stop evennia` and make sure that it's added to the list like on the image above.
1. Click `Apply` and close the run configuration window.
You are now ready to go, and if you will fire up `start evennia` configuration you should see
following in the bottom panel:
![Example of running alternative configuration](https://i.imgur.com/nTfpC04.png)
and you can click through the tabs to check appropriate logs, or even the console output as it is
still running in interactive mode.

View file

@ -0,0 +1,318 @@
# Unit Testing
*Unit testing* means testing components of a program in isolation from each other to make sure every
part works on its own before using it with others. Extensive testing helps avoid new updates causing
unexpected side effects as well as alleviates general code rot (a more comprehensive wikipedia
article on unit testing can be found [here](https://en.wikipedia.org/wiki/Unit_test)).
A typical unit test set calls some function or method with a given input, looks at the result and
makes sure that this result looks as expected. Rather than having lots of stand-alone test programs,
Evennia makes use of a central *test runner*. This is a program that gathers all available tests all
over the Evennia source code (called *test suites*) and runs them all in one go. Errors and
tracebacks are reported.
By default Evennia only tests itself. But you can also add your own tests to your game code and have
Evennia run those for you.
## Running the Evennia test suite
To run the full Evennia test suite, go to your game folder and issue the command
evennia test evennia
This will run all the evennia tests using the default settings. You could also run only a subset of
all tests by specifying a subpackage of the library:
evennia test evennia.commands.default
A temporary database will be instantiated to manage the tests. If everything works out you will see
how many tests were run and how long it took. If something went wrong you will get error messages.
If you contribute to Evennia, this is a useful sanity check to see you haven't introduced an
unexpected bug.
## Running tests for your game dir
If you have implemented your own tests for your game you can run them from your game dir
with
evennia test --settings settings.py .
The period (`.`) means to run all tests found in the current directory and all subdirectories. You
could also specify, say, `typeclasses` or `world` if you wanted to just run tests in those subdirs.
An important thing to note is that those tests will all be run using the _default Evennia settings_.
To run the tests with your own settings file you must use the `--settings` option:
evennia test --settings settings.py .
The `--settings` option of Evennia takes a file name in the `mygame/server/conf` folder. It is
normally used to swap settings files for testing and development. In combination with `test`, it
forces Evennia to use this settings file over the default one.
You can also test specific things by giving their path
evennia test --settings settings.py .world.tests.YourTest
## Writing new tests
Evennia's test suite makes use of Django unit test system, which in turn relies on Python's
*unittest* module.
To make the test runner find the tests, they must be put in a module named `test*.py` (so `test.py`,
`tests.py` etc). Such a test module will be found wherever it is in the package. It can be a good
idea to look at some of Evennia's `tests.py` modules to see how they look.
Inside the module you need to put a class inheriting (at any distance) from `unittest.TestCase`. Each
method on that class that starts with `test_` will be run separately as a unit test. There
are two special, optional methods `setUp` and `tearDown` that will (if you define them) run before
_every_ test. This can be useful for setting up and deleting things.
To actually test things, you use special `assert...` methods on the class. Most common on is
`assertEqual`, which makes sure a result is what you expect it to be.
Here's an example of the principle. Let's assume you put this in `mygame/world/tests.py`
and want to test a function in `mygame/world/myfunctions.py`
```python
# in a module tests.py somewhere i your game dir
import unittest
from evennia import create_object
# the function we want to test
from .myfunctions import myfunc
class TestObj(unittest.TestCase):
"This tests a function myfunc."
def setUp(self):
"""done before every of the test_ * methods below"""
self.obj = create_object("mytestobject")
def tearDown(self):
"""done after every test_* method below """
self.obj.delete()
def test_return_value(self):
"""test method. Makes sure return value is as expected."""
actual_return = myfunc(self.obj)
expected_return = "This is the good object 'mytestobject'."
# test
self.assertEqual(expected_return, actual_return)
def test_alternative_call(self):
"""test method. Calls with a keyword argument."""
actual_return = myfunc(self.obj, bad=True)
expected_return = "This is the baaad object 'mytestobject'."
# test
self.assertEqual(expected_return, actual_return)
```
To test this, run
evennia test --settings settings.py .
to run the entire test module
evennia test --settings setings.py .world.tests
or a specific class:
evennia test --settings settings.py .world.tests.TestObj
You can also run a specific test:
evennia test --settings settings.py .world.tests.TestObj.test_alternative_call
You might also want to read the [Python documentation for the unittest module](https://docs.python.org/library/unittest.html).
## Using the Evennia testing classes
Evennia offers many custom testing classes that helps with testing Evennia features.
They are all found in [evennia.utils.test_resources](evennia.utils.test_resources). Note that
these classes implement the `setUp` and `tearDown` already, so if you want to add stuff in them
yourself you should remember to use e.g. `super().setUp()` in your code.
### Classes for testing your game dir
These all use whatever setting you pass to them and works well for testing code in your game dir.
- `EvenniaTest` - this sets up a full object environment for your test. All the created entities
can be accesses as properties on the class:
- `.account` - A fake [Account](evennia.accounts.accounts.DefaultAccount) named "TestAccount".
- `.account2` - Another account named "TestAccount2"
- `char1` - A [Character](evennia.objects.objects.DefaultCharacter) linked to `.account`, named `Char`.
This has 'Developer' permissions but is not a superuser.
- `.char2` - Another character linked to `account`, named `Char2`. This has base permissions (player).
- `.obj1` - A regular [Object](evennia.objects.objects.DefaultObject) named "Obj".
- `.obj2` - Another object named "Obj2".
- `.room1` - A [Room](evennia.objects.objects.DefaultRoom) named "Room". Both characters and both
objects are located inside this room. It has a description of "room_desc".
- `.room2` - Another room named "Room2". It is empty and has no set description.
- `.exit` - An exit named "out" that leads from `.room1` to `.room2`.
- `.script` - A [Script](evennia.scripts.scripts.DefaultScript) named "Script". It's an inert script
without a timing component.
- `.session` - A fake [Session](evennia.server.serversession.ServerSession) that mimics a player
connecting to the game. It is used by `.account1` and has a sessid of 1.
- `EvenniaCommandTest` - has the same environment like `EvenniaTest` but also adds a special
[.call()](evennia.utils.test_resources.EvenniaCommandTestMixin.call) method specifically for
testing Evennia [Commands](../Components/Commands.md). It allows you to compare what the command _actually_
returns to the player with what you expect. Read the `call` api doc for more info.
- `EvenniaTestCase` - This is identical to the regular Python `TestCase` class, it's
just there for naming symmetry with `BaseEvenniaTestCase` below.
Here's an example of using `EvenniaTest`
```python
# in a test module
from evennia.utils.test_resources import EvenniaTest
class TestObject(EvenniaTest):
"""Remember that the testing class creates char1 and char2 inside room1 ..."""
def test_object_search_character(self):
"""Check that char1 can search for char2 by name"""
self.assertEqual(self.char1.search(self.char2.key), self.char2)
def test_location_search(self):
"""Check so that char1 can find the current location by name"""
self.assertEqual(self.char1.search(self.char1.location.key), self.char1.location)
# ...
```
This example tests a custom command.
```python
from evennia.commands.default.tests import EvenniaCommandTest
from commands import command as mycommand
class TestSet(EvenniaCommandTest):
"tests the look command by simple call, using Char2 as a target"
def test_mycmd_char(self):
self.call(mycommand.CmdMyLook(), "Char2", "Char2(#7)")
def test_mycmd_room(self):
"tests the look command by simple call, with target as room"
self.call(mycommand.CmdMyLook(), "Room",
"Room(#1)\nroom_desc\nExits: out(#3)\n"
"You see: Obj(#4), Obj2(#5), Char2(#7)")
```
When using `.call`, you don't need to specify the entire string; you can just give the beginning
of it and if it matches, that's enough. Use `\n` to denote line breaks and (this is a special for
the `.call` helper), `||` to indicate multiple uses of `.msg()` in the Command. The `.call` helper
has a lot of arguments for mimicing different ways of calling a Command, so make sure to
[read the API docs for .call()](evennia.utils.test_resources.EvenniaCommandTestMixin.call).
### Classes for testing Evennia core
These are used for testing Evennia itself. They provide the same resources as the classes
above but enforce Evennias default settings found in `evennia/settings_default.py`, ignoring
any settings changes in your game dir.
- `BaseEvenniaTest` - all the default objects above but with enforced default settings
- `BaseEvenniaCommandTest` - for testing Commands, but with enforced default settings
- `BaseEvenniaTestCase` - no default objects, only enforced default settings
There are also two special 'mixin' classes. These are uses in the classes above, but may also
be useful if you want to mix your own testing classes:
- `EvenniaTestMixin` - A class mixin that creates all test environment objects.
- `EvenniaCommandMixin` - A class mixin that adds the `.call()` Command-tester helper.
If you want to help out writing unittests for Evennia, take a look at Evennia's [coveralls.io
page](https://coveralls.io/github/evennia/evennia). There you see which modules have any form of
test coverage and which does not. All help is appreciated!
## Unit testing contribs with custom models
A special case is if you were to create a contribution to go to the `evennia/contrib` folder that
uses its [own database models](../Concepts/New-Models.md). The problem with this is that Evennia (and Django) will
only recognize models in `settings.INSTALLED_APPS`. If a user wants to use your contrib, they will
be required to add your models to their settings file. But since contribs are optional you cannot
add the model to Evennia's central `settings_default.py` file - this would always create your
optional models regardless of if the user wants them. But at the same time a contribution is a part
of the Evennia distribution and its unit tests should be run with all other Evennia tests using
`evennia test evennia`.
The way to do this is to only temporarily add your models to the `INSTALLED_APPS` directory when the
test runs. here is an example of how to do it.
> Note that this solution, derived from this [stackexchange
answer](http://stackoverflow.com/questions/502916/django-how-to-create-a-model-dynamically-just-for-
testing#503435) is currently untested! Please report your findings.
```python
# a file contrib/mycontrib/tests.py
from django.conf import settings
import django
from evennia.utils.test_resources import BaseEvenniaTest
OLD_DEFAULT_SETTINGS = settings.INSTALLED_APPS
DEFAULT_SETTINGS = dict(
INSTALLED_APPS=(
'contrib.mycontrib.tests',
),
DATABASES={
"default": {
"ENGINE": "django.db.backends.sqlite3"
}
},
SILENCED_SYSTEM_CHECKS=["1_7.W001"],
)
class TestMyModel(BaseEvenniaTest):
def setUp(self):
if not settings.configured:
settings.configure(**DEFAULT_SETTINGS)
django.setup()
from django.core.management import call_command
from django.db.models import loading
loading.cache.loaded = False
call_command('syncdb', verbosity=0)
def tearDown(self):
settings.configure(**OLD_DEFAULT_SETTINGS)
django.setup()
from django.core.management import call_command
from django.db.models import loading
loading.cache.loaded = False
call_command('syncdb', verbosity=0)
# test cases below ...
def test_case(self):
# test case here
```
## A note on making the test runner faster
If you have custom models with a large number of migrations, creating the test database can take a
very long time. If you don't require migrations to run for your tests, you can disable them with the
django-test-without-migrations package. To install it, simply:
```
$ pip install django-test-without-migrations
```
Then add it to your `INSTALLED_APPS` in your `server.conf.settings.py`:
```python
INSTALLED_APPS = (
# ...
'test_without_migrations',
)
```
After doing so, you can then run tests without migrations by adding the `--nomigrations` argument:
```
evennia test --settings settings.py --nomigrations .
```

View file

@ -0,0 +1,134 @@
# Updating Your Game
Fortunately, it's extremely easy to keep your Evennia server up-to-date. If you haven't already, see
the [Getting Started guide](../Setup/Installation.md) and get everything running.
## Updating with the latest Evennia code changes
Very commonly we make changes to the Evennia code to improve things. There are many ways to get told
when to update: You can subscribe to the RSS feed or manually check up on the feeds from
https://www.evennia.com. You can also simply fetch the latest regularly.
When you're wanting to apply updates, simply `cd` to your cloned `evennia` root directory and type:
git pull
assuming you've got the command line client. If you're using a graphical client, you will probably
want to navigate to the `evennia` directory and either right click and find your client's pull
function, or use one of the menus (if applicable).
You can review the latest changes with
git log
or the equivalent in the graphical client. You can also see the latest changes online
[here](https://github.com/evennia/evennia/blob/master/CHANGELOG.md).
You will always need to do `evennia reload` (or `reload` from -in-game) from your game-dir to have
the new code affect your game. If you want to be really sure you should run a full `evennia reboot`
so that both Server and Portal can restart (this will disconnect everyone though, so if you know the
Portal has had no updates you don't have to do that).
## Upgrading Evennia dependencies
On occasion we update the versions of third-party libraries Evennia depend on (or we may add a new
dependency). This will be announced on the mailing list/forum. If you run into errors when starting
Evennia, always make sure you have the latest versions of everything. In some cases, like for
Django, starting the server may also give warning saying that you are using a working, but too-old
version that should not be used in production.
Upgrading `evennia` will automatically fetch all the latest packages that it now need. First `cd` to
your cloned `evennia` folder. Make sure your `virtualenv` is active and use
pip install --upgrade -e .
Remember the period (`.`) at the end - that applies the upgrade to the current location (your
`evennia` dir).
> The `-e` means that we are _linking_ the evennia sources rather than copying them into the
environment. This means we can most of the time just update the sources (with `git pull`) and see
those changes directly applied to our installed `evennia` package. Without installing/upgrading the
package with `-e`, we would have to remember to upgrade the package every time we downloaded any new
source-code changes.
Follow the upgrade output to make sure it finishes without errors. To check what packages are
currently available in your python environment after the upgrade, use
pip list
This will show you the version of all installed packages. The `evennia` package will also show the
location of its source code.
## Migrating the Database Schema
Whenever we change the database layout of Evennia upstream (such as when we add new features) you
will need to *migrate* your existing database. When this happens it will be clearly noted in the
`git log` (it will say something to the effect of "Run migrations"). Database changes will also be
announced on the Evennia [mailing list](https://groups.google.com/forum/#!forum/evennia).
When the database schema changes, you just go to your game folder and run
evennia migrate
> Hint: If the `evennia` command is not found, you most likely need to activate your
[virtualenv](../Glossary.md#virtualenv).
## Resetting your database
Should you ever want to start over completely from scratch, there is no need to re-download Evennia
or anything like that. You just need to clear your database. Once you are done, you just rebuild it
from scratch by running
evennia migrate
The first step in wiping your database is to stop Evennia completely with
evennia stop
If you run the default `SQlite3` database (to change this you need to edit your `settings.py` file),
the database is actually just a normal file in `mygame/server/` called `evennia.db3`. *Simply delete
that file* - that's it. Now run `evennia migrate` to recreate a new, fresh one.
If you run some other database system you can instead flush the database:
evennia flush
This will empty the database. However, it will not reset the internal counters of the database, so
you will start with higher dbref values. If this is okay, this is all you need.
Django also offers an easy way to start the database's own management should we want more direct
control:
evennia dbshell
In e.g. MySQL you can then do something like this (assuming your MySQL database is named "Evennia":
mysql> DROP DATABASE Evennia;
mysql> exit
> NOTE: Under Windows OS, in order to access SQLite dbshell you need to [download the SQLite
command-line shell program](https://www.sqlite.org/download.html). It's a single executable file
(sqlite3.exe) that you should place in the root of either your MUD folder or Evennia's (it's the
same, in both cases Django will find it).
## More about schema migrations
If and when an Evennia update modifies the database *schema* (that is, the under-the-hood details as
to how data is stored in the database), you must update your existing database correspondingly to
match the change. If you don't, the updated Evennia will complain that it cannot read the database
properly. Whereas schema changes should become more and more rare as Evennia matures, it may still
happen from time to time.
One way one could handle this is to apply the changes manually to your database using the database's
command line. This often means adding/removing new tables or fields as well as possibly convert
existing data to match what the new Evennia version expects. It should be quite obvious that this
quickly becomes cumbersome and error-prone. If your database doesn't contain anything critical yet
it's probably easiest to simply reset it and start over rather than to bother converting.
Enter *migrations*. Migrations keeps track of changes in the database schema and applies them
automatically for you. Basically, whenever the schema changes we distribute small files called
"migrations" with the source. Those tell the system exactly how to implement the change so you don't
have to do so manually. When a migration has been added we will tell you so on Evennia's mailing
lists and in commit messages -
you then just run `evennia migrate` to be up-to-date again.

View file

@ -0,0 +1,469 @@
# Version Control
Version control software allows you to track the changes you make to your code, as well as being
able to easily backtrack these changes, share your development efforts and more.
It's strongly recommended that you put your game code under version control. Version
control is also the way to contribue to Evennia itself.
For an introduction to the concept, start with the Wikipedia article
[here](https://en.wikipedia.org/wiki/Version_control). Evennia uses the version
control system [Git](https://git-scm.com/) and this is what will be covered
henceforth. Note that this page primarily shows commands for Linux, but the
syntax should be the same for Windows and Mac.
For more help on using Git, please refer to the [Official GitHub
documentation](https://help.github.com/articles/set-up-git#platform-all).
## Setting up Git
You can find expanded instructions for
installation [here](https://git-scm.com/book/en/Getting-Started-Installing-Git).
### Step 1: Install Git
- **Fedora Linux**
yum install git-core
- **Debian Linux** _(Ubuntu, Linux Mint, etc.)_
apt-get install git
- **Windows**: It is recommended to use [Git for Windows](https://gitforwindows.org/).
- **Mac**: Mac platforms offer two methods for installation, one via MacPorts, which you can find
out about [here](https://git-scm.com/book/en/Getting-Started-Installing-Git#Installing-on-Mac), or
you can use the [Git OSX Installer](https://sourceforge.net/projects/git-osx-installer/).
### Step 2: Define user/e-mail Settings for Git
To avoid a common issue later, you will need to set a couple of settings; first you will need to
tell Git your username, followed by your e-mail address, so that when you commit code later you will
be properly credited.
> Note that your commit information will be visible to everyone if you ever contribute to Evennia or
use an online service like github to host your code. So if you are not comfortable with using your
real, full name online, put a nickname here.
1. Set the default name for git to use when you commit:
git config --global user.name "Your Name Here"
2. Set the default email for git to use when you commit:
git config --global user.email "your_email@example.com"
## Putting your game folder under version control
> Note: The game folder's version control is completely separate from Evennia's repository.
After you have set up your game you will have created a new folder to host your particular game
(let's call this folder `mygame` for now).
This folder is *not* under version control at this point.
git init mygame
Your mygame folder is now ready for version control! Add all the content and make a first
commit:
cd mygame
git add *
git commit -a -m "Initial commit"
In turn these commands:
- Move us into the `mygame` folder
- Tell `git` that everything `*` means everything) in this folder should be put
under version control.
- _Commit_ all (`-a`) those newly added files to git and add a message `-m` so you remember
what you did at this point. Doing a commit is like saving a snapshot of the
current state of everything.
Read on for details!
### Tracking files
When working on your code or fix bugs in your local branches you may end up creating new files. If
you do you must tell Git to track them by using the add command.
git add <filename>
You only need to do this once per file.
git status
will show if you have any modified, added or otherwise changed files. Some
files, like database files, logs and temporary PID files are usually *not*
tracked in version control. These should either not show up or have a question
mark in front of them.
```{note}
You will notice that some files are not covered by your git version control,
notably your settings file (`mygame/server/conf/settings.py`) and your sqlite3
database file `mygame/server/evennia.db3`. What is auto-ignored by is controlled
by the hidden file `mygame/.gitignore`. Evennia creates this file as part of
the creation of your game directory. Everything matched in this file will be
ignored by git. If you want to, for example, include your settings file for
collaborators to access, remove that entry in `.gitignore`.
```
```{warning}
You should *never* put your sqlite3 database file into git by removing its entry
in `.gitignore`. GIT is for backing up your code, not your database. That way
lies madness and a good chance you'll confuse yourself so that after a few
commits and reverts don't know what is in your database or not. If you want to
backup your database, do so by simply copying the file on your hard drive to a
backup-name.
```
### Committing your Code
_Committing_ your code means storing the current snapshot of your code within
git. This creates a "save point" or "history" of your development process. You
can later jump back and forth in your history, for example to figure out just
when a bug was introduced or see what results the code used to produce compared
to now. Or just wiping everything since the last commit, if you did something
stupid.
It's usually a good idea to commit your changes often. Committing is fast and
local only - you will never commit anything online at this point. To commit your
changes, use
git commit --all
Also `-a` works. This will open a text editor for you to describe your change.
Be brief but informative in your message - you'll appreciate it later. When you
save and close the editor, the commit will be saved. You can create the message
directly with
git commit -a -m "This fixes a bug in the combat code."
### Changing your mind
If you have non-committed changes that you realize you want to throw away, you
'check out' the file you want - this will re-load it from the last committed
state:
git checkout <file_to_revert>
git checkout foo/bar/dummy.py
If you want to revert _all_ changes you did since last commit, do
git checkout .
(that is, add a single `.` at the end).
### Pushing your code online
So far your code is only located on your private machine. A good idea is to back
it up online. The easiest way to do this is to push it to your own remote
repository on GitHub.
```{important}
Just to avoid confusion, be aware that Github's documentation has changed to
calling the primary branch 'main' rather than 'master'. While Evennia still
uses 'master' branch (and this is what we refer to below), you can use either
name for your personal primary branch - they are equivalent.
```
1. Make sure you have your game directory setup under git version control as
described in the previous section. Make sure to commit any changes you did.
2. Create a new, empty repository on Github. Github explains how
[here](https://help.github.com/articles/create-a-repo/) (do *not* "Initialize
the repository with a README" or else you'll create unrelated histories).
3. From your local game dir, do `git remote add origin <github URL>` where
`<github URL>` is the URL to your online repo. This tells your game dir that
it should be pushing to the remote online dir.
4. `git remote -v` to verify the online dir.
5. `git push origin master` (or `git push origin main`) now pushes your game dir
online so you can see it on github.com.
You can commit your work locally (`git commit --all -m "Make a change that
..."`) as many times as you want. When you want to push those changes to your
online repo, you do `git push`. You can also `git clone <url_to_online_repo>`
from your online repo to somewhere else (like your production server) and
henceforth do `git pull` to update that to the latest thing you pushed.
Note that GitHub's repos are, by default publicly visible by all. Creating a
publicly visible online clone might not be what you want for all parts of your
development process - you may prefer a more private venue when sharing your
revolutionary work with your team. If that's the case you can change your
repository to "Private" in the github settings. Then your code will only be
visible to those you specifically grant access.
## Forking Evennia
This helps you set up an online *fork* of the main Evennia repository so you can
easily commit fixes and help with upstream development. You can do this step
also if you _didn't_ put your game dir under version control like in the
previous section - the evennia repo and your game dir repo are completely
separate.
### Step 1: Fork the evennia/master repository
> Before proceeding with the following step, make sure you have registered and
> created an account on [GitHub.com](https://github.com/). This is necessary in order to create a fork
of Evennia's master repository, and to push your commits to your fork either for
yourself or for contributing to
Evennia.
A _fork_ is a clone of the master repository that you can make your own commits
and changes to. At the top of [this page](https://github.com/evennia/evennia),
click the "Fork" button, as it appears below.
![](https://github-images.s3.amazonaws.com/help/bootcamp/Bootcamp-Fork.png)
### Step 2: Clone your online fork of Evennia
The fork only exists online as of yet. In a terminal, change your directory to
the folder you wish to develop in. From this directory run the following
command:
git clone https://github.com/yourusername/evennia.git
This will download your fork to your computer. It creates a new folder
`evennia/` at your current location.
### Step 3: Configure remotes
Your Evennia-fork is now separate from upstream, 'official' Evennia. You will
want to set it up so that you can easily sync our updates and changes to your
fork.
We do this by setting up a new _remote_. We actually already have one remote,
that is our own github form of Evennia. This got created when you cloned the
repo and defaults to being called `origin`.
We will now create a new remote called `upstream`.
cd evennia
git remote add upstream https://github.com/evennia/evennia.git
This adds a remote to the main evennia repo.
If you also want to access Evennia's `develop` branch (the bleeding edge
development) do the following:
git fetch upstream develop
git checkout develop
Use
git checkout master
git checkout develop
to switch between the branches. If you want to contribute a fix, ask first which
branch to use. Normally `master` is for bug fixes and `develop` is for new
features, but late in the development of a new Evennia version, all changes
often go into `develop`.
## Working with your Evennia fork
_Branches_ are stand-alone editions of the same code. You make a commit to a
branch. Switching to a branch will change the code on-disk. You can easily
make a new branch off a parent branch, and then merge it back into the same
branch later (or throw it away). This is a very common way to work on new
features in safety and isolation.
### Updating to latest Evennia
When Evennia's official repository updates, first make sure to commit all your
changes to your branch and then checkout the "clean" master branch:
git checkout master
git pull upstream master
Or, if you are working against Evennia's development branch:
git checkout develop
git pull upstream develop
The `pull` command will fetch all the changes from the "upstream" remote and
merge it into your local master/develop branch. It should now be a perfect copy
of the latest Evennia changes.
### Making changes
As a rule of thumb you should _never_ work directly in Evennia's `master` or
`develop` branches. Instead you make a _new_ branch off the branch you want
and change _that_.
git checkout master (or develop)
check checkout -b strange_bug
You now have a new branch `strange_bug` that is an exact replica of the branch you
had checked out when you created it. Here you can now make your own
modifications.
git branches
will show you which branches are available and which one you are currently
using. Use `git checkout <branch>` to move between them, but remember to commit
your changes before you do.
You often want to make sure also your work-branch has the latest upstream
changes. To do this, you need to first update your copy of the
`master`/`develop` branch and then _merge_ those changes into your work branch.
Make sure you have committed everything first!
git commit -a -m "My latest changes ..." # on your strange_bug branch
git checkout master (or develop)
git pull upstream develop
git checkout strange_bug
git merge master (or develop)
If everything went well, your `strange_bug` branch will now have the latest version
of Evennia merged with whatever changes you have done.
Now work away on your code and commit with reasonable commit messages
git commit -a -m "Fixed the issue in ..."
git commit -a -m "Adding unit tests. This resolves #123."
Use
git diff
to see what you changed since last commit, and
git log
to see past commits (including those made by Evennia upstream, remember that
your branch is a copy of the upstream one, including its history!)
## Sharing your Evennia fixes on Github
Up to this point your `strange_bug` branch only exists on your local computer. No
one else can see it. If you want a copy of this branch to also appear in your
online fork on GitHub, make sure to have checked out your "myfixes" branch and
then run the following:
git push -u origin strange_bug
You only need to do this once, the `-u` makes this the default push-location. In
the future, you can just push things online like this:
git push
### Troubleshooting
If you hadn't setup a public key on GitHub or aren't asked for a
username/password, you might get an error `403: Forbidden Access` at this stage.
In that case, some users have reported that the workaround is to create a file
`.netrc` under your home directory and add your github credentials there:
```bash
machine github.com
login <my_github_username>
password <my_github_password>
```
## Making an Evennia Pull Request
If you think that the fixes you did in your `strange_bug` branch should be a
part of the regular Evennia, you should create a _Pull Request_ (PR). This is a
call for the Evennia maintainer to pull your change into an upstream branch.
> It is wise to make separate branches for every fix or series of fixes you want
to contribute.
Assuming you have followed the instructions above and have pushed your changes
online, [create a pull request](https://github.com/evennia/evennia/pulls) and
follow the instructions. Make sure to specifically select your `strange_bug`
branch to be the source of the merge and use the branch you based that branch
off (`master` or `develop`) as the target.
Evennia developers will then be able to examine your request and merge it if
it's deemed suitable. They may also come back with feedback and request you do
some changes.
Once approved and merged, your change will now be available in the upstream
branch:
git checkout master (or develope)
git pull upstream master (or develop)
Since your changes are now in upstream, your local `strange_bug` branch is now
superfluous and should be deleted:
git branch -D strange_bug
You can also safely delete your online `strange_bug` branch in your fork
(you can do this from the PR page on github).
## GIT tips and tricks
Some of the GIT commands can feel a little long and clunky if you need to do them often. Luckily you
can create aliases for those. Here are some useful commands to run:
```
# git st
# - view brief status info
git config --global alias.st 'status -s'
```
Above, you only need to ever enter the `git config ...` command once - you have then added the new
alias. Afterwards, just do `git st` to get status info. All the examples below follow the same
template.
```
# git cl
# - clone a repository
git config --global alias.cl clone
```
```
# git cma "commit message"
# - commit all changes without opening editor for message
git config --global alias.cma 'commit -a -m'
```
```
# git ca
# - amend text to your latest commit message
git config --global alias.ca 'commit --amend'
```
```
# git fl
# - file log; shows diffs of files in latest commits
git config --global alias.fl 'log -u'
```
```
# git co [branchname]
# - checkout
git config --global alias.co checkout
```
```
# git br <branchname>
# - create branch
git config --global alias.br branch
```
```
# git ls
# - view log tree
git config --global alias.ls 'log --pretty=format:"%C(green)%h\ %C(yellow)[%ad]%Cred%d\
%Creset%s%Cblue\ [%cn]" --decorate --date=relative --graph'
```
```
# git diff
# - show current uncommitted changes
git config --global alias.diff 'diff --word-diff'
```
```
# git grep <query>
# - search (grep) codebase for a search criterion
git config --global alias.grep 'grep -Ii'
```
To get a further feel for GIT there is also [a good YouTube talk about it](https://www.youtube.com/watch?v=1ffBJ4sVUb4#t=1m58s) - it's a bit long but it will help you understand the underlying ideas behind GIT
(which in turn makes it a lot more intuitive to use).