From a77613a0906f2b6dacdc6a51bf5f98acef98dfde Mon Sep 17 00:00:00 2001 From: Yyanu Nav Date: Mon, 9 Nov 2020 23:32:14 +0000 Subject: [PATCH] second commit --- README.md | 40 ++++ __init__.py | 6 + commands/README.md | 14 ++ commands/__init__.py | 0 commands/command.py | 188 ++++++++++++++++++ commands/default_cmdsets.py | 96 +++++++++ server/README.md | 38 ++++ server/__init__.py | 1 + server/conf/__init__.py | 1 + server/conf/at_initial_setup.py | 19 ++ server/conf/at_search.py | 54 +++++ server/conf/at_server_startstop.py | 63 ++++++ server/conf/cmdparser.py | 55 +++++ server/conf/connection_screens.py | 38 ++++ server/conf/connection_settings.py | 19 ++ server/conf/inlinefuncs.py | 51 +++++ server/conf/inputfuncs.py | 52 +++++ server/conf/lockfuncs.py | 30 +++ server/conf/mssp.py | 105 ++++++++++ server/conf/portal_services_plugins.py | 24 +++ server/conf/server_services_plugins.py | 24 +++ server/conf/serversession.py | 37 ++++ server/conf/settings.py | 51 +++++ server/conf/web_plugins.py | 41 ++++ server/logs/README.md | 15 ++ typeclasses/README.md | 16 ++ typeclasses/__init__.py | 0 typeclasses/accounts.py | 104 ++++++++++ typeclasses/channels.py | 62 ++++++ typeclasses/characters.py | 34 ++++ typeclasses/exits.py | 38 ++++ typeclasses/objects.py | 162 +++++++++++++++ typeclasses/rooms.py | 22 ++ typeclasses/scripts.py | 92 +++++++++ web/__init__.py | 0 web/static_overrides/README.md | 13 ++ web/static_overrides/webclient/css/README.md | 3 + web/static_overrides/webclient/js/README.md | 3 + web/static_overrides/website/css/README.md | 3 + web/static_overrides/website/images/README.md | 3 + web/template_overrides/README.md | 4 + web/template_overrides/webclient/README.md | 3 + web/template_overrides/website/README.md | 7 + .../website/flatpages/README.md | 3 + .../website/registration/README.md | 3 + web/urls.py | 18 ++ world/README.md | 10 + world/__init__.py | 0 world/batch_cmds.ev | 26 +++ world/prototypes.py | 90 +++++++++ 50 files changed, 1781 insertions(+) create mode 100644 README.md create mode 100644 __init__.py create mode 100644 commands/README.md create mode 100644 commands/__init__.py create mode 100644 commands/command.py create mode 100644 commands/default_cmdsets.py create mode 100644 server/README.md create mode 100644 server/__init__.py create mode 100644 server/conf/__init__.py create mode 100644 server/conf/at_initial_setup.py create mode 100644 server/conf/at_search.py create mode 100644 server/conf/at_server_startstop.py create mode 100644 server/conf/cmdparser.py create mode 100644 server/conf/connection_screens.py create mode 100644 server/conf/connection_settings.py create mode 100644 server/conf/inlinefuncs.py create mode 100644 server/conf/inputfuncs.py create mode 100644 server/conf/lockfuncs.py create mode 100644 server/conf/mssp.py create mode 100644 server/conf/portal_services_plugins.py create mode 100644 server/conf/server_services_plugins.py create mode 100644 server/conf/serversession.py create mode 100644 server/conf/settings.py create mode 100644 server/conf/web_plugins.py create mode 100644 server/logs/README.md create mode 100644 typeclasses/README.md create mode 100644 typeclasses/__init__.py create mode 100644 typeclasses/accounts.py create mode 100644 typeclasses/channels.py create mode 100644 typeclasses/characters.py create mode 100644 typeclasses/exits.py create mode 100644 typeclasses/objects.py create mode 100644 typeclasses/rooms.py create mode 100644 typeclasses/scripts.py create mode 100644 web/__init__.py create mode 100644 web/static_overrides/README.md create mode 100644 web/static_overrides/webclient/css/README.md create mode 100644 web/static_overrides/webclient/js/README.md create mode 100644 web/static_overrides/website/css/README.md create mode 100644 web/static_overrides/website/images/README.md create mode 100644 web/template_overrides/README.md create mode 100644 web/template_overrides/webclient/README.md create mode 100644 web/template_overrides/website/README.md create mode 100644 web/template_overrides/website/flatpages/README.md create mode 100644 web/template_overrides/website/registration/README.md create mode 100644 web/urls.py create mode 100644 world/README.md create mode 100644 world/__init__.py create mode 100644 world/batch_cmds.ev create mode 100644 world/prototypes.py diff --git a/README.md b/README.md new file mode 100644 index 0000000..fe62bd3 --- /dev/null +++ b/README.md @@ -0,0 +1,40 @@ +# Welcome to Evennia! + +This is your game directory, set up to let you start with +your new game right away. An overview of this directory is found here: +https://github.com/evennia/evennia/wiki/Directory-Overview#the-game-directory + +You can delete this readme file when you've read it and you can +re-arrange things in this game-directory to suit your own sense of +organisation (the only exception is the directory structure of the +`server/` directory, which Evennia expects). If you change the structure +you must however also edit/add to your settings file to tell Evennia +where to look for things. + +Your game's main configuration file is found in +`server/conf/settings.py` (but you don't need to change it to get +started). If you just created this directory (which means you'll already +have a `virtualenv` running if you followed the default instructions), +`cd` to this directory then initialize a new database using + + evennia migrate + +To start the server, stand in this directory and run + + evennia start + +This will start the server, logging output to the console. Make +sure to create a superuser when asked. By default you can now connect +to your new game using a MUD client on `localhost`, port `4000`. You can +also log into the web client by pointing a browser to +`http://localhost:4001`. + +# Getting started + +From here on you might want to look at one of the beginner tutorials: +http://github.com/evennia/evennia/wiki/Tutorials. + +Evennia's documentation is here: +https://github.com/evennia/evennia/wiki. + +Enjoy! diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..6e3dbee --- /dev/null +++ b/__init__.py @@ -0,0 +1,6 @@ +""" +This sub-package holds the template for creating a new game folder. +The new game folder (when running evennia --init) is a copy of this +folder. + +""" diff --git a/commands/README.md b/commands/README.md new file mode 100644 index 0000000..0425ce6 --- /dev/null +++ b/commands/README.md @@ -0,0 +1,14 @@ +# commands/ + +This folder holds modules for implementing one's own commands and +command sets. All the modules' classes are essentially empty and just +imports the default implementations from Evennia; so adding anything +to them will start overloading the defaults. + +You can change the organisation of this directory as you see fit, just +remember that if you change any of the default command set classes' +locations, you need to add the appropriate paths to +`server/conf/settings.py` so that Evennia knows where to find them. +Also remember that if you create new sub directories you must put +(optionally empty) `__init__.py` files in there so that Python can +find your modules. diff --git a/commands/__init__.py b/commands/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/commands/command.py b/commands/command.py new file mode 100644 index 0000000..3c7c0e2 --- /dev/null +++ b/commands/command.py @@ -0,0 +1,188 @@ +""" +Commands + +Commands describe the input the account can do to the game. + +""" + +from evennia import Command as BaseCommand + +# from evennia import default_cmds + + +class Command(BaseCommand): + """ + Inherit from this if you want to create your own command styles + from scratch. Note that Evennia's default commands inherits from + MuxCommand instead. + + Note that the class's `__doc__` string (this text) is + used by Evennia to create the automatic help entry for + the command, so make sure to document consistently here. + + Each Command implements the following methods, called + in this order (only func() is actually required): + - at_pre_cmd(): If this returns anything truthy, execution is aborted. + - parse(): Should perform any extra parsing needed on self.args + and store the result on self. + - func(): Performs the actual work. + - at_post_cmd(): Extra actions, often things done after + every command, like prompts. + + """ + + pass + + +# ------------------------------------------------------------- +# +# The default commands inherit from +# +# evennia.commands.default.muxcommand.MuxCommand. +# +# If you want to make sweeping changes to default commands you can +# uncomment this copy of the MuxCommand parent and add +# +# COMMAND_DEFAULT_CLASS = "commands.command.MuxCommand" +# +# to your settings file. Be warned that the default commands expect +# the functionality implemented in the parse() method, so be +# careful with what you change. +# +# ------------------------------------------------------------- + +# from evennia.utils import utils +# +# +# class MuxCommand(Command): +# """ +# This sets up the basis for a MUX command. The idea +# is that most other Mux-related commands should just +# inherit from this and don't have to implement much +# parsing of their own unless they do something particularly +# advanced. +# +# Note that the class's __doc__ string (this text) is +# used by Evennia to create the automatic help entry for +# the command, so make sure to document consistently here. +# """ +# def has_perm(self, srcobj): +# """ +# This is called by the cmdhandler to determine +# if srcobj is allowed to execute this command. +# We just show it here for completeness - we +# are satisfied using the default check in Command. +# """ +# return super().has_perm(srcobj) +# +# def at_pre_cmd(self): +# """ +# This hook is called before self.parse() on all commands +# """ +# pass +# +# def at_post_cmd(self): +# """ +# This hook is called after the command has finished executing +# (after self.func()). +# """ +# pass +# +# def parse(self): +# """ +# This method is called by the cmdhandler once the command name +# has been identified. It creates a new set of member variables +# that can be later accessed from self.func() (see below) +# +# The following variables are available for our use when entering this +# method (from the command definition, and assigned on the fly by the +# cmdhandler): +# self.key - the name of this command ('look') +# self.aliases - the aliases of this cmd ('l') +# self.permissions - permission string for this command +# self.help_category - overall category of command +# +# self.caller - the object calling this command +# self.cmdstring - the actual command name used to call this +# (this allows you to know which alias was used, +# for example) +# self.args - the raw input; everything following self.cmdstring. +# self.cmdset - the cmdset from which this command was picked. Not +# often used (useful for commands like 'help' or to +# list all available commands etc) +# self.obj - the object on which this command was defined. It is often +# the same as self.caller. +# +# A MUX command has the following possible syntax: +# +# name[ with several words][/switch[/switch..]] arg1[,arg2,...] [[=|,] arg[,..]] +# +# The 'name[ with several words]' part is already dealt with by the +# cmdhandler at this point, and stored in self.cmdname (we don't use +# it here). The rest of the command is stored in self.args, which can +# start with the switch indicator /. +# +# This parser breaks self.args into its constituents and stores them in the +# following variables: +# self.switches = [list of /switches (without the /)] +# self.raw = This is the raw argument input, including switches +# self.args = This is re-defined to be everything *except* the switches +# self.lhs = Everything to the left of = (lhs:'left-hand side'). If +# no = is found, this is identical to self.args. +# self.rhs: Everything to the right of = (rhs:'right-hand side'). +# If no '=' is found, this is None. +# self.lhslist - [self.lhs split into a list by comma] +# self.rhslist - [list of self.rhs split into a list by comma] +# self.arglist = [list of space-separated args (stripped, including '=' if it exists)] +# +# All args and list members are stripped of excess whitespace around the +# strings, but case is preserved. +# """ +# raw = self.args +# args = raw.strip() +# +# # split out switches +# switches = [] +# if args and len(args) > 1 and args[0] == "/": +# # we have a switch, or a set of switches. These end with a space. +# switches = args[1:].split(None, 1) +# if len(switches) > 1: +# switches, args = switches +# switches = switches.split('/') +# else: +# args = "" +# switches = switches[0].split('/') +# arglist = [arg.strip() for arg in args.split()] +# +# # check for arg1, arg2, ... = argA, argB, ... constructs +# lhs, rhs = args, None +# lhslist, rhslist = [arg.strip() for arg in args.split(',')], [] +# if args and '=' in args: +# lhs, rhs = [arg.strip() for arg in args.split('=', 1)] +# lhslist = [arg.strip() for arg in lhs.split(',')] +# rhslist = [arg.strip() for arg in rhs.split(',')] +# +# # save to object properties: +# self.raw = raw +# self.switches = switches +# self.args = args.strip() +# self.arglist = arglist +# self.lhs = lhs +# self.lhslist = lhslist +# self.rhs = rhs +# self.rhslist = rhslist +# +# # if the class has the account_caller property set on itself, we make +# # sure that self.caller is always the account if possible. We also create +# # a special property "character" for the puppeted object, if any. This +# # is convenient for commands defined on the Account only. +# if hasattr(self, "account_caller") and self.account_caller: +# if utils.inherits_from(self.caller, "evennia.objects.objects.DefaultObject"): +# # caller is an Object/Character +# self.character = self.caller +# self.caller = self.caller.account +# elif utils.inherits_from(self.caller, "evennia.accounts.accounts.DefaultAccount"): +# # caller was already an Account +# self.character = self.caller.get_puppet(self.session) +# else: +# self.character = None diff --git a/commands/default_cmdsets.py b/commands/default_cmdsets.py new file mode 100644 index 0000000..fee80c7 --- /dev/null +++ b/commands/default_cmdsets.py @@ -0,0 +1,96 @@ +""" +Command sets + +All commands in the game must be grouped in a cmdset. A given command +can be part of any number of cmdsets and cmdsets can be added/removed +and merged onto entities at runtime. + +To create new commands to populate the cmdset, see +`commands/command.py`. + +This module wraps the default command sets of Evennia; overloads them +to add/remove commands from the default lineup. You can create your +own cmdsets by inheriting from them or directly from `evennia.CmdSet`. + +""" + +from evennia import default_cmds + + +class CharacterCmdSet(default_cmds.CharacterCmdSet): + """ + The `CharacterCmdSet` contains general in-game commands like `look`, + `get`, etc available on in-game Character objects. It is merged with + the `AccountCmdSet` when an Account puppets a Character. + """ + + key = "DefaultCharacter" + + def at_cmdset_creation(self): + """ + Populates the cmdset + """ + super().at_cmdset_creation() + # + # any commands you add below will overload the default ones. + # + + +class AccountCmdSet(default_cmds.AccountCmdSet): + """ + This is the cmdset available to the Account at all times. It is + combined with the `CharacterCmdSet` when the Account puppets a + Character. It holds game-account-specific commands, channel + commands, etc. + """ + + key = "DefaultAccount" + + def at_cmdset_creation(self): + """ + Populates the cmdset + """ + super().at_cmdset_creation() + # + # any commands you add below will overload the default ones. + # + + +class UnloggedinCmdSet(default_cmds.UnloggedinCmdSet): + """ + Command set available to the Session before being logged in. This + holds commands like creating a new account, logging in, etc. + """ + + key = "DefaultUnloggedin" + + def at_cmdset_creation(self): + """ + Populates the cmdset + """ + super().at_cmdset_creation() + # + # any commands you add below will overload the default ones. + # + + +class SessionCmdSet(default_cmds.SessionCmdSet): + """ + This cmdset is made available on Session level once logged in. It + is empty by default. + """ + + key = "DefaultSession" + + def at_cmdset_creation(self): + """ + This is the only method defined in a cmdset, called during + its creation. It should populate the set with command instances. + + As and example we just add the empty base `Command` object. + It prints some info. + """ + super().at_cmdset_creation() + # + # any commands you add below will overload the default ones. + # diff --git a/server/README.md b/server/README.md new file mode 100644 index 0000000..9f530c3 --- /dev/null +++ b/server/README.md @@ -0,0 +1,38 @@ +# server/ + +This directory holds files used by and configuring the Evennia server +itself. + +Out of all the subdirectories in the game directory, Evennia does +expect this directory to exist, so you should normally not delete, +rename or change its folder structure. + +When running you will find four new files appear in this directory: + + - `server.pid` and `portal.pid`: These hold the process IDs of the + Portal and Server, so that they can be managed by the launcher. If + Evennia is shut down uncleanly (e.g. by a crash or via a kill + signal), these files might erroneously remain behind. If so Evennia + will tell you they are "stale" and they can be deleted manually. + - `server.restart` and `portal.restart`: These hold flags to tell the + server processes if it should die or start again. You never need to + modify those files. + - `evennia.db3`: This will only appear if you are using the default + SQLite3 database; it a binary file that holds the entire game + database; deleting this file will effectively reset the game for + you and you can start fresh with `evennia migrate` (useful during + development). + +## server/conf/ + +This subdirectory holds the configuration modules for the server. With +them you can change how Evennia operates and also plug in your own +functionality to replace the default. You usually need to restart the +server to apply changes done here. The most important file is the file +`settings.py` which is the main configuration file of Evennia. + +## server/logs/ + +This subdirectory holds various log files created by the running +Evennia server. It is also the default location for storing any custom +log files you might want to output using Evennia's logging mechanisms. diff --git a/server/__init__.py b/server/__init__.py new file mode 100644 index 0000000..40a96af --- /dev/null +++ b/server/__init__.py @@ -0,0 +1 @@ +# -*- coding: utf-8 -*- diff --git a/server/conf/__init__.py b/server/conf/__init__.py new file mode 100644 index 0000000..40a96af --- /dev/null +++ b/server/conf/__init__.py @@ -0,0 +1 @@ +# -*- coding: utf-8 -*- diff --git a/server/conf/at_initial_setup.py b/server/conf/at_initial_setup.py new file mode 100644 index 0000000..b394a04 --- /dev/null +++ b/server/conf/at_initial_setup.py @@ -0,0 +1,19 @@ +""" +At_initial_setup module template + +Custom at_initial_setup method. This allows you to hook special +modifications to the initial server startup process. Note that this +will only be run once - when the server starts up for the very first +time! It is called last in the startup process and can thus be used to +overload things that happened before it. + +The module must contain a global function at_initial_setup(). This +will be called without arguments. Note that tracebacks in this module +will be QUIETLY ignored, so make sure to check it well to make sure it +does what you expect it to. + +""" + + +def at_initial_setup(): + pass diff --git a/server/conf/at_search.py b/server/conf/at_search.py new file mode 100644 index 0000000..f7f8b6c --- /dev/null +++ b/server/conf/at_search.py @@ -0,0 +1,54 @@ +""" +Search and multimatch handling + +This module allows for overloading two functions used by Evennia's +search functionality: + + at_search_result: + This is called whenever a result is returned from an object + search (a common operation in commands). It should (together + with at_multimatch_input below) define some way to present and + differentiate between multiple matches (by default these are + presented as 1-ball, 2-ball etc) + at_multimatch_input: + This is called with a search term and should be able to + identify if the user wants to separate a multimatch-result + (such as that from a previous search). By default, this + function understands input on the form 1-ball, 2-ball etc as + indicating that the 1st or 2nd match for "ball" should be + used. + +This module is not called by default, to use it, add the following +line to your settings file: + + SEARCH_AT_RESULT = "server.conf.at_search.at_search_result" + +""" + + +def at_search_result(matches, caller, query="", quiet=False, **kwargs): + """ + This is a generic hook for handling all processing of a search + result, including error reporting. + + Args: + matches (list): This is a list of 0, 1 or more typeclass instances, + the matched result of the search. If 0, a nomatch error should + be echoed, and if >1, multimatch errors should be given. Only + if a single match should the result pass through. + caller (Object): The object performing the search and/or which should + receive error messages. + query (str, optional): The search query used to produce `matches`. + quiet (bool, optional): If `True`, no messages will be echoed to caller + on errors. + + Keyword Args: + nofound_string (str): Replacement string to echo on a notfound error. + multimatch_string (str): Replacement string to echo on a multimatch error. + + Returns: + processed_result (Object or None): This is always a single result + or `None`. If `None`, any error reporting/handling should + already have happened. + + """ diff --git a/server/conf/at_server_startstop.py b/server/conf/at_server_startstop.py new file mode 100644 index 0000000..98c29fa --- /dev/null +++ b/server/conf/at_server_startstop.py @@ -0,0 +1,63 @@ +""" +Server startstop hooks + +This module contains functions called by Evennia at various +points during its startup, reload and shutdown sequence. It +allows for customizing the server operation as desired. + +This module must contain at least these global functions: + +at_server_start() +at_server_stop() +at_server_reload_start() +at_server_reload_stop() +at_server_cold_start() +at_server_cold_stop() + +""" + + +def at_server_start(): + """ + This is called every time the server starts up, regardless of + how it was shut down. + """ + pass + + +def at_server_stop(): + """ + This is called just before the server is shut down, regardless + of it is for a reload, reset or shutdown. + """ + pass + + +def at_server_reload_start(): + """ + This is called only when server starts back up after a reload. + """ + pass + + +def at_server_reload_stop(): + """ + This is called only time the server stops before a reload. + """ + pass + + +def at_server_cold_start(): + """ + This is called only when the server starts "cold", i.e. after a + shutdown or a reset. + """ + pass + + +def at_server_cold_stop(): + """ + This is called only when the server goes down due to a shutdown or + reset. + """ + pass diff --git a/server/conf/cmdparser.py b/server/conf/cmdparser.py new file mode 100644 index 0000000..831990a --- /dev/null +++ b/server/conf/cmdparser.py @@ -0,0 +1,55 @@ +""" +Changing the default command parser + +The cmdparser is responsible for parsing the raw text inserted by the +user, identifying which command/commands match and return one or more +matching command objects. It is called by Evennia's cmdhandler and +must accept input and return results on the same form. The default +handler is very generic so you usually don't need to overload this +unless you have very exotic parsing needs; advanced parsing is best +done at the Command.parse level. + +The default cmdparser understands the following command combinations +(where [] marks optional parts.) + +[cmdname[ cmdname2 cmdname3 ...] [the rest] + +A command may consist of any number of space-separated words of any +length, and contain any character. It may also be empty. + +The parser makes use of the cmdset to find command candidates. The +parser return a list of matches. Each match is a tuple with its first +three elements being the parsed cmdname (lower case), the remaining +arguments, and the matched cmdobject from the cmdset. + + +This module is not accessed by default. To tell Evennia to use it +instead of the default command parser, add the following line to +your settings file: + + COMMAND_PARSER = "server.conf.cmdparser.cmdparser" + +""" + + +def cmdparser(raw_string, cmdset, caller, match_index=None): + """ + This function is called by the cmdhandler once it has + gathered and merged all valid cmdsets valid for this particular parsing. + + raw_string - the unparsed text entered by the caller. + cmdset - the merged, currently valid cmdset + caller - the caller triggering this parsing + match_index - an optional integer index to pick a given match in a + list of same-named command matches. + + Returns: + list of tuples: [(cmdname, args, cmdobj, cmdlen, mratio), ...] + where cmdname is the matching command name and args is + everything not included in the cmdname. Cmdobj is the actual + command instance taken from the cmdset, cmdlen is the length + of the command name and the mratio is some quality value to + (possibly) separate multiple matches. + + """ + # Your implementation here diff --git a/server/conf/connection_screens.py b/server/conf/connection_screens.py new file mode 100644 index 0000000..5f46b4c --- /dev/null +++ b/server/conf/connection_screens.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +""" +Connection screen + +This is the text to show the user when they first connect to the game (before +they log in). + +To change the login screen in this module, do one of the following: + +- Define a function `connection_screen()`, taking no arguments. This will be + called first and must return the full string to act as the connection screen. + This can be used to produce more dynamic screens. +- Alternatively, define a string variable in the outermost scope of this module + with the connection string that should be displayed. If more than one such + variable is given, Evennia will pick one of them at random. + +The commands available to the user when the connection screen is shown +are defined in evennia.default_cmds.UnloggedinCmdSet. The parsing and display +of the screen is done by the unlogged-in "look" command. + +""" + +from django.conf import settings +from evennia import utils + +CONNECTION_SCREEN = """ +|b==============================================================|n + Welcome to |g{}|n, version {}! + + If you have an existing account, connect to it by typing: + |wconnect |n + If you need to create an account, type (without the <>'s): + |wcreate |n + + Enter |whelp|n for more info. |wlook|n will re-show this screen. +|b==============================================================|n""".format( + settings.SERVERNAME, utils.get_evennia_version("short") +) diff --git a/server/conf/connection_settings.py b/server/conf/connection_settings.py new file mode 100644 index 0000000..eda8bda --- /dev/null +++ b/server/conf/connection_settings.py @@ -0,0 +1,19 @@ +# This file is auto-generated by the `evennia connections` wizard. +# Don't edit manually, your changes will be overwritten. + +GAME_INDEX_ENABLED = True +GAME_INDEX_LISTING = \ +{ 'game_name': 'FAOD', + 'game_status': 'pre-alpha', + 'game_website': 'http://205.166.94.150:4001/', + 'listing_contact': 'j3b@3b1.org', + 'long_description': '"Love Is the Plan the Plan Is Death" is a short story ' + 'by James Tiptree, Jr., a pen name used by American ' + 'writer Alice Sheldon. The novella won a Nebula Award ' + 'for Best Short Story in 1973. It is a great story. ' + 'This game on the other hand is not as great.', + 'short_description': 'F*ck and/or Die: Love is the game, the game is ' + 'death.', + 'telnet_hostname': '', + 'telnet_port': '', + 'web_client_url': 'http://205.166.94.150:4001/webclient/'} \ No newline at end of file diff --git a/server/conf/inlinefuncs.py b/server/conf/inlinefuncs.py new file mode 100644 index 0000000..1190597 --- /dev/null +++ b/server/conf/inlinefuncs.py @@ -0,0 +1,51 @@ +""" +Inlinefunc + +Inline functions allow for direct conversion of text users mark in a +special way. Inlinefuncs are deactivated by default. To activate, add + + INLINEFUNC_ENABLED = True + +to your settings file. The default inlinefuncs are found in +evennia.utils.inlinefunc. + +In text, usage is straightforward: + +$funcname([arg1,[arg2,...]]) + +Example 1 (using the "pad" inlinefunc): + say This is $pad("a center-padded text", 50,c,-) of width 50. + -> + John says, "This is -------------- a center-padded text--------------- of width 50." + +Example 2 (using nested "pad" and "time" inlinefuncs): + say The time is $pad($time(), 30)right now. + -> + John says, "The time is Oct 25, 11:09 right now." + +To add more inline functions, add them to this module, using +the following call signature: + + def funcname(text, *args, **kwargs) + +where `text` is always the part between {funcname(args) and +{/funcname and the *args are taken from the appropriate part of the +call. If no {/funcname is given, `text` will be the empty string. + +It is important that the inline function properly clean the +incoming `args`, checking their type and replacing them with sane +defaults if needed. If impossible to resolve, the unmodified text +should be returned. The inlinefunc should never cause a traceback. + +While the inline function should accept **kwargs, the keyword is +never accepted as a valid call - this is only intended to be used +internally by Evennia, notably to send the `session` keyword to +the function; this is the session of the object viewing the string +and can be used to customize it to each session. + +""" + +# def capitalize(text, *args, **kwargs): +# "Silly capitalize example. Used as {capitalize() ... {/capitalize" +# session = kwargs.get("session") +# return text.capitalize() diff --git a/server/conf/inputfuncs.py b/server/conf/inputfuncs.py new file mode 100644 index 0000000..6cb226f --- /dev/null +++ b/server/conf/inputfuncs.py @@ -0,0 +1,52 @@ +""" +Input functions + +Input functions are always called from the client (they handle server +input, hence the name). + +This module is loaded by being included in the +`settings.INPUT_FUNC_MODULES` tuple. + +All *global functions* included in this module are considered +input-handler functions and can be called by the client to handle +input. + +An input function must have the following call signature: + + cmdname(session, *args, **kwargs) + +Where session will be the active session and *args, **kwargs are extra +incoming arguments and keyword properties. + +A special command is the "default" command, which is will be called +when no other cmdname matches. It also receives the non-found cmdname +as argument. + + default(session, cmdname, *args, **kwargs) + +""" + +# def oob_echo(session, *args, **kwargs): +# """ +# Example echo function. Echoes args, kwargs sent to it. +# +# Args: +# session (Session): The Session to receive the echo. +# args (list of str): Echo text. +# kwargs (dict of str, optional): Keyed echo text +# +# """ +# session.msg(oob=("echo", args, kwargs)) +# +# +# def default(session, cmdname, *args, **kwargs): +# """ +# Handles commands without a matching inputhandler func. +# +# Args: +# session (Session): The active Session. +# cmdname (str): The (unmatched) command name +# args, kwargs (any): Arguments to function. +# +# """ +# pass diff --git a/server/conf/lockfuncs.py b/server/conf/lockfuncs.py new file mode 100644 index 0000000..8dac0c7 --- /dev/null +++ b/server/conf/lockfuncs.py @@ -0,0 +1,30 @@ +""" + +Lockfuncs + +Lock functions are functions available when defining lock strings, +which in turn limits access to various game systems. + +All functions defined globally in this module are assumed to be +available for use in lockstrings to determine access. See the +Evennia documentation for more info on locks. + +A lock function is always called with two arguments, accessing_obj and +accessed_obj, followed by any number of arguments. All possible +arguments should be handled with *args, **kwargs. The lock function +should handle all eventual tracebacks by logging the error and +returning False. + +Lock functions in this module extend (and will overload same-named) +lock functions from evennia.locks.lockfuncs. + +""" + +# def myfalse(accessing_obj, accessed_obj, *args, **kwargs): +# """ +# called in lockstring with myfalse(). +# A simple logger that always returns false. Prints to stdout +# for simplicity, should use utils.logger for real operation. +# """ +# print "%s tried to access %s. Access denied." % (accessing_obj, accessed_obj) +# return False diff --git a/server/conf/mssp.py b/server/conf/mssp.py new file mode 100644 index 0000000..270c8f5 --- /dev/null +++ b/server/conf/mssp.py @@ -0,0 +1,105 @@ +""" + +MSSP (Mud Server Status Protocol) meta information + +Modify this file to specify what MUD listing sites will report about your game. +All fields are static. The number of currently active players and your game's +current uptime will be added automatically by Evennia. + +You don't have to fill in everything (and most fields are not shown/used by all +crawlers anyway); leave the default if so needed. You need to reload the server +before the updated information is made available to crawlers (reloading does +not affect uptime). + +After changing the values in this file, you must register your game with the +MUD website list you want to track you. The listing crawler will then regularly +connect to your server to get the latest info. No further configuration is +needed on the Evennia side. + +""" + +MSSPTable = { + # Required fields + "NAME": "Mygame", # usually the same as SERVERNAME + # Generic + "CRAWL DELAY": "-1", # limit how often crawler may update the listing. -1 for no limit + "HOSTNAME": "", # telnet hostname + "PORT": ["4000"], # telnet port - most important port should be *last* in list! + "CODEBASE": "Evennia", + "CONTACT": "", # email for contacting the mud + "CREATED": "", # year MUD was created + "ICON": "", # url to icon 32x32 or larger; <32kb. + "IP": "", # current or new IP address + "LANGUAGE": "", # name of language used, e.g. English + "LOCATION": "", # full English name of server country + "MINIMUM AGE": "0", # set to 0 if not applicable + "WEBSITE": "", # http:// address to your game website + # Categorisation + "FAMILY": "Custom", # evennia goes under 'Custom' + "GENRE": "None", # Adult, Fantasy, Historical, Horror, Modern, None, or Science Fiction + # Gameplay: Adventure, Educational, Hack and Slash, None, + # Player versus Player, Player versus Environment, + # Roleplaying, Simulation, Social or Strategy + "GAMEPLAY": "", + "STATUS": "Open Beta", # Allowed: Alpha, Closed Beta, Open Beta, Live + "GAMESYSTEM": "Custom", # D&D, d20 System, World of Darkness, etc. Use Custom if homebrew + # Subgenre: LASG, Medieval Fantasy, World War II, Frankenstein, + # Cyberpunk, Dragonlance, etc. Or None if not applicable. + "SUBGENRE": "None", + # World + "AREAS": "0", + "HELPFILES": "0", + "MOBILES": "0", + "OBJECTS": "0", + "ROOMS": "0", # use 0 if room-less + "CLASSES": "0", # use 0 if class-less + "LEVELS": "0", # use 0 if level-less + "RACES": "0", # use 0 if race-less + "SKILLS": "0", # use 0 if skill-less + # Protocols set to 1 or 0; should usually not be changed) + "ANSI": "1", + "GMCP": "1", + "MSDP": "1", + "MXP": "1", + "SSL": "1", + "UTF-8": "1", + "MCCP": "1", + "XTERM 256 COLORS": "1", + "XTERM TRUE COLORS": "0", + "ATCP": "0", + "MCP": "0", + "MSP": "0", + "VT100": "0", + "PUEBLO": "0", + "ZMP": "0", + # Commercial set to 1 or 0) + "PAY TO PLAY": "0", + "PAY FOR PERKS": "0", + # Hiring set to 1 or 0) + "HIRING BUILDERS": "0", + "HIRING CODERS": "0", + # Extended variables + # World + "DBSIZE": "0", + "EXITS": "0", + "EXTRA DESCRIPTIONS": "0", + "MUDPROGS": "0", + "MUDTRIGS": "0", + "RESETS": "0", + # Game (set to 1 or 0, or one of the given alternatives) + "ADULT MATERIAL": "0", + "MULTICLASSING": "0", + "NEWBIE FRIENDLY": "0", + "PLAYER CITIES": "0", + "PLAYER CLANS": "0", + "PLAYER CRAFTING": "0", + "PLAYER GUILDS": "0", + "EQUIPMENT SYSTEM": "None", # "None", "Level", "Skill", "Both" + "MULTIPLAYING": "None", # "None", "Restricted", "Full" + "PLAYERKILLING": "None", # "None", "Restricted", "Full" + "QUEST SYSTEM": "None", # "None", "Immortal Run", "Automated", "Integrated" + "ROLEPLAYING": "None", # "None", "Accepted", "Encouraged", "Enforced" + "TRAINING SYSTEM": "None", # "None", "Level", "Skill", "Both" + # World originality: "All Stock", "Mostly Stock", "Mostly Original", "All Original" + "WORLD ORIGINALITY": "All Original", +} diff --git a/server/conf/portal_services_plugins.py b/server/conf/portal_services_plugins.py new file mode 100644 index 0000000..b536c56 --- /dev/null +++ b/server/conf/portal_services_plugins.py @@ -0,0 +1,24 @@ +""" +Start plugin services + +This plugin module can define user-created services for the Portal to +start. + +This module must handle all imports and setups required to start +twisted services (see examples in evennia.server.portal.portal). It +must also contain a function start_plugin_services(application). +Evennia will call this function with the main Portal application (so +your services can be added to it). The function should not return +anything. Plugin services are started last in the Portal startup +process. + +""" + + +def start_plugin_services(portal): + """ + This hook is called by Evennia, last in the Portal startup process. + + portal - a reference to the main portal application. + """ + pass diff --git a/server/conf/server_services_plugins.py b/server/conf/server_services_plugins.py new file mode 100644 index 0000000..e3d41fe --- /dev/null +++ b/server/conf/server_services_plugins.py @@ -0,0 +1,24 @@ +""" + +Server plugin services + +This plugin module can define user-created services for the Server to +start. + +This module must handle all imports and setups required to start a +twisted service (see examples in evennia.server.server). It must also +contain a function start_plugin_services(application). Evennia will +call this function with the main Server application (so your services +can be added to it). The function should not return anything. Plugin +services are started last in the Server startup process. + +""" + + +def start_plugin_services(server): + """ + This hook is called by Evennia, last in the Server startup process. + + server - a reference to the main server application. + """ + pass diff --git a/server/conf/serversession.py b/server/conf/serversession.py new file mode 100644 index 0000000..13fbf1e --- /dev/null +++ b/server/conf/serversession.py @@ -0,0 +1,37 @@ +""" +ServerSession + +The serversession is the Server-side in-memory representation of a +user connecting to the game. Evennia manages one Session per +connection to the game. So a user logged into the game with multiple +clients (if Evennia is configured to allow that) will have multiple +sessions tied to one Account object. All communication between Evennia +and the real-world user goes through the Session(s) associated with that user. + +It should be noted that modifying the Session object is not usually +necessary except for the most custom and exotic designs - and even +then it might be enough to just add custom session-level commands to +the SessionCmdSet instead. + +This module is not normally called. To tell Evennia to use the class +in this module instead of the default one, add the following to your +settings file: + + SERVER_SESSION_CLASS = "server.conf.serversession.ServerSession" + +""" + +from evennia.server.serversession import ServerSession as BaseServerSession + + +class ServerSession(BaseServerSession): + """ + This class represents a player's session and is a template for + individual protocols to communicate with Evennia. + + Each account gets one or more sessions assigned to them whenever they connect + to the game server. All communication between game and account goes + through their session(s). + """ + + pass diff --git a/server/conf/settings.py b/server/conf/settings.py new file mode 100644 index 0000000..8a47d01 --- /dev/null +++ b/server/conf/settings.py @@ -0,0 +1,51 @@ +r""" +Evennia settings file. + +The available options are found in the default settings file found +here: + +/usr/home/j3/evdev/evennia/evennia/settings_default.py + +Remember: + +Don't copy more from the default file than you actually intend to +change; this will make sure that you don't overload upstream updates +unnecessarily. + +When changing a setting requiring a file system path (like +path/to/actual/file.py), use GAME_DIR and EVENNIA_DIR to reference +your game folder and the Evennia library folders respectively. Python +paths (path.to.module) should be given relative to the game's root +folder (typeclasses.foo) whereas paths within the Evennia library +needs to be given explicitly (evennia.foo). + +If you want to share your game dir, including its settings, you can +put secret game- or server-specific settings in secret_settings.py. + +""" + +# Use the defaults from Evennia unless explicitly overridden +from evennia.settings_default import * + +###################################################################### +# Evennia base server config +###################################################################### + +# This is the name of your game. Make it catchy! +SERVERNAME = "FAOD" + + +###################################################################### +# Settings given in secret_settings.py override those in this file. +###################################################################### +try: + from server.conf.secret_settings import * +except ImportError: + print("secret_settings.py file not found or failed to import.") + + +try: + # Created by the `evennia connections` wizard + from .connection_settings import * +except ImportError: + pass \ No newline at end of file diff --git a/server/conf/web_plugins.py b/server/conf/web_plugins.py new file mode 100644 index 0000000..ec11ad7 --- /dev/null +++ b/server/conf/web_plugins.py @@ -0,0 +1,41 @@ +""" +Web plugin hooks. +""" + + +def at_webserver_root_creation(web_root): + """ + This is called as the web server has finished building its default + path tree. At this point, the media/ and static/ URIs have already + been added to the web root. + + Args: + web_root (twisted.web.resource.Resource): The root + resource of the URI tree. Use .putChild() to + add new subdomains to the tree. + + Returns: + web_root (twisted.web.resource.Resource): The potentially + modified root structure. + + Example: + from twisted.web import static + my_page = static.File("web/mypage/") + my_page.indexNames = ["index.html"] + web_root.putChild("mypage", my_page) + + """ + return web_root + + +def at_webproxy_root_creation(web_root): + """ + This function can modify the portal proxy service. + Args: + web_root (evennia.server.webserver.Website): The Evennia + Website application. Use .putChild() to add new + subdomains that are Portal-accessible over TCP; + primarily for new protocol development, but suitable + for other shenanigans. + """ + return web_root diff --git a/server/logs/README.md b/server/logs/README.md new file mode 100644 index 0000000..35ad999 --- /dev/null +++ b/server/logs/README.md @@ -0,0 +1,15 @@ +This directory contains Evennia's log files. The existence of this README.md file is also necessary +to correctly include the log directory in git (since log files are ignored by git and you can't +commit an empty directory). + +- `server.log` - log file from the game Server. +- `portal.log` - log file from Portal proxy (internet facing) + +Usually these logs are viewed together with `evennia -l`. They are also rotated every week so as not +to be too big. Older log names will have a name appended by `_month_date`. + +- `lockwarnings.log` - warnings from the lock system. +- `http_requests.log` - this will generally be empty unless turning on debugging inside the server. + +- `channel_.log` - these are channel logs for the in-game channels They are also used + by the `/history` flag in-game to get the latest message history. diff --git a/typeclasses/README.md b/typeclasses/README.md new file mode 100644 index 0000000..e114e59 --- /dev/null +++ b/typeclasses/README.md @@ -0,0 +1,16 @@ +# typeclasses/ + +This directory holds the modules for overloading all the typeclasses +representing the game entities and many systems of the game. Other +server functionality not covered here is usually modified by the +modules in `server/conf/`. + +Each module holds empty classes that just imports Evennia's defaults. +Any modifications done to these classes will overload the defaults. + +You can change the structure of this directory (even rename the +directory itself) as you please, but if you do you must add the +appropriate new paths to your settings.py file so Evennia knows where +to look. Also remember that for Python to find your modules, it +requires you to add an empty `__init__.py` file in any new sub +directories you create. diff --git a/typeclasses/__init__.py b/typeclasses/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/typeclasses/accounts.py b/typeclasses/accounts.py new file mode 100644 index 0000000..ba293c6 --- /dev/null +++ b/typeclasses/accounts.py @@ -0,0 +1,104 @@ +""" +Account + +The Account represents the game "account" and each login has only one +Account object. An Account is what chats on default channels but has no +other in-game-world existence. Rather the Account puppets Objects (such +as Characters) in order to actually participate in the game world. + + +Guest + +Guest accounts are simple low-level accounts that are created/deleted +on the fly and allows users to test the game without the commitment +of a full registration. Guest accounts are deactivated by default; to +activate them, add the following line to your settings file: + + GUEST_ENABLED = True + +You will also need to modify the connection screen to reflect the +possibility to connect with a guest account. The setting file accepts +several more options for customizing the Guest account system. + +""" + +from evennia import DefaultAccount, DefaultGuest + + +class Account(DefaultAccount): + """ + This class describes the actual OOC account (i.e. the user connecting + to the MUD). It does NOT have visual appearance in the game world (that + is handled by the character which is connected to this). Comm channels + are attended/joined using this object. + + It can be useful e.g. for storing configuration options for your game, but + should generally not hold any character-related info (that's best handled + on the character level). + + Can be set using BASE_ACCOUNT_TYPECLASS. + + + * available properties + + key (string) - name of account + name (string)- wrapper for user.username + aliases (list of strings) - aliases to the object. Will be saved to database as AliasDB entries but returned as strings. + dbref (int, read-only) - unique #id-number. Also "id" can be used. + date_created (string) - time stamp of object creation + permissions (list of strings) - list of permission strings + + user (User, read-only) - django User authorization object + obj (Object) - game object controlled by account. 'character' can also be used. + sessions (list of Sessions) - sessions connected to this account + is_superuser (bool, read-only) - if the connected user is a superuser + + * Handlers + + locks - lock-handler: use locks.add() to add new lock strings + db - attribute-handler: store/retrieve database attributes on this self.db.myattr=val, val=self.db.myattr + ndb - non-persistent attribute handler: same as db but does not create a database entry when storing data + scripts - script-handler. Add new scripts to object with scripts.add() + cmdset - cmdset-handler. Use cmdset.add() to add new cmdsets to object + nicks - nick-handler. New nicks with nicks.add(). + + * Helper methods + + msg(text=None, **kwargs) + execute_cmd(raw_string, session=None) + search(ostring, global_search=False, attribute_name=None, use_nicks=False, location=None, ignore_errors=False, account=False) + is_typeclass(typeclass, exact=False) + swap_typeclass(new_typeclass, clean_attributes=False, no_default=True) + access(accessing_obj, access_type='read', default=False) + check_permstring(permstring) + + * Hook methods (when re-implementation, remember methods need to have self as first arg) + + basetype_setup() + at_account_creation() + + - note that the following hooks are also found on Objects and are + usually handled on the character level: + + at_init() + at_cmdset_get(**kwargs) + at_first_login() + at_post_login(session=None) + at_disconnect() + at_message_receive() + at_message_send() + at_server_reload() + at_server_shutdown() + + """ + + pass + + +class Guest(DefaultGuest): + """ + This class is used for guest logins. Unlike Accounts, Guests and their + characters are deleted after disconnection. + """ + + pass diff --git a/typeclasses/channels.py b/typeclasses/channels.py new file mode 100644 index 0000000..0b943d0 --- /dev/null +++ b/typeclasses/channels.py @@ -0,0 +1,62 @@ +""" +Channel + +The channel class represents the out-of-character chat-room usable by +Accounts in-game. It is mostly overloaded to change its appearance, but +channels can be used to implement many different forms of message +distribution systems. + +Note that sending data to channels are handled via the CMD_CHANNEL +syscommand (see evennia.syscmds). The sending should normally not need +to be modified. + +""" + +from evennia import DefaultChannel + + +class Channel(DefaultChannel): + """ + Working methods: + at_channel_creation() - called once, when the channel is created + has_connection(account) - check if the given account listens to this channel + connect(account) - connect account to this channel + disconnect(account) - disconnect account from channel + access(access_obj, access_type='listen', default=False) - check the + access on this channel (default access_type is listen) + delete() - delete this channel + message_transform(msg, emit=False, prefix=True, + sender_strings=None, external=False) - called by + the comm system and triggers the hooks below + msg(msgobj, header=None, senders=None, sender_strings=None, + persistent=None, online=False, emit=False, external=False) - main + send method, builds and sends a new message to channel. + tempmsg(msg, header=None, senders=None) - wrapper for sending non-persistent + messages. + distribute_message(msg, online=False) - send a message to all + connected accounts on channel, optionally sending only + to accounts that are currently online (optimized for very large sends) + + Useful hooks: + channel_prefix(msg, emit=False) - how the channel should be + prefixed when returning to user. Returns a string + format_senders(senders) - should return how to display multiple + senders to a channel + pose_transform(msg, sender_string) - should detect if the + sender is posing, and if so, modify the string + format_external(msg, senders, emit=False) - format messages sent + from outside the game, like from IRC + format_message(msg, emit=False) - format the message body before + displaying it to the user. 'emit' generally means that the + message should not be displayed with the sender's name. + + pre_join_channel(joiner) - if returning False, abort join + post_join_channel(joiner) - called right after successful join + pre_leave_channel(leaver) - if returning False, abort leave + post_leave_channel(leaver) - called right after successful leave + pre_send_message(msg) - runs just before a message is sent to channel + post_send_message(msg) - called just after message was sent to channel + + """ + + pass diff --git a/typeclasses/characters.py b/typeclasses/characters.py new file mode 100644 index 0000000..1e45457 --- /dev/null +++ b/typeclasses/characters.py @@ -0,0 +1,34 @@ +""" +Characters + +Characters are (by default) Objects setup to be puppeted by Accounts. +They are what you "see" in game. The Character class in this module +is setup to be the "default" character type created by the default +creation commands. + +""" +from evennia import DefaultCharacter + + +class Character(DefaultCharacter): + """ + The Character defaults to reimplementing some of base Object's hook methods with the + following functionality: + + at_basetype_setup - always assigns the DefaultCmdSet to this object type + (important!)sets locks so character cannot be picked up + and its commands only be called by itself, not anyone else. + (to change things, use at_object_creation() instead). + at_after_move(source_location) - Launches the "look" command after every move. + at_post_unpuppet(account) - when Account disconnects from the Character, we + store the current location in the pre_logout_location Attribute and + move it to a None-location so the "unpuppeted" character + object does not need to stay on grid. Echoes "Account has disconnected" + to the room. + at_pre_puppet - Just before Account re-connects, retrieves the character's + pre_logout_location Attribute and move it back on the grid. + at_post_puppet - Echoes "AccountName has entered the game" to the room. + + """ + + pass diff --git a/typeclasses/exits.py b/typeclasses/exits.py new file mode 100644 index 0000000..55e091f --- /dev/null +++ b/typeclasses/exits.py @@ -0,0 +1,38 @@ +""" +Exits + +Exits are connectors between Rooms. An exit always has a destination property +set and has a single command defined on itself with the same name as its key, +for allowing Characters to traverse the exit to its destination. + +""" +from evennia import DefaultExit + + +class Exit(DefaultExit): + """ + Exits are connectors between rooms. Exits are normal Objects except + they defines the `destination` property. It also does work in the + following methods: + + basetype_setup() - sets default exit locks (to change, use `at_object_creation` instead). + at_cmdset_get(**kwargs) - this is called when the cmdset is accessed and should + rebuild the Exit cmdset along with a command matching the name + of the Exit object. Conventionally, a kwarg `force_init` + should force a rebuild of the cmdset, this is triggered + by the `@alias` command when aliases are changed. + at_failed_traverse() - gives a default error message ("You cannot + go there") if exit traversal fails and an + attribute `err_traverse` is not defined. + + Relevant hooks to overload (compared to other types of Objects): + at_traverse(traveller, target_loc) - called to do the actual traversal and calling of the other hooks. + If overloading this, consider using super() to use the default + movement implementation (and hook-calling). + at_after_traverse(traveller, source_loc) - called by at_traverse just after traversing. + at_failed_traverse(traveller) - called by at_traverse if traversal failed for some reason. Will + not be called if the attribute `err_traverse` is + defined, in which case that will simply be echoed. + """ + + pass diff --git a/typeclasses/objects.py b/typeclasses/objects.py new file mode 100644 index 0000000..21443e8 --- /dev/null +++ b/typeclasses/objects.py @@ -0,0 +1,162 @@ +""" +Object + +The Object is the "naked" base class for things in the game world. + +Note that the default Character, Room and Exit does not inherit from +this Object, but from their respective default implementations in the +evennia library. If you want to use this class as a parent to change +the other types, you can do so by adding this as a multiple +inheritance. + +""" +from evennia import DefaultObject + + +class Object(DefaultObject): + """ + This is the root typeclass object, implementing an in-game Evennia + game object, such as having a location, being able to be + manipulated or looked at, etc. If you create a new typeclass, it + must always inherit from this object (or any of the other objects + in this file, since they all actually inherit from BaseObject, as + seen in src.object.objects). + + The BaseObject class implements several hooks tying into the game + engine. By re-implementing these hooks you can control the + system. You should never need to re-implement special Python + methods, such as __init__ and especially never __getattribute__ and + __setattr__ since these are used heavily by the typeclass system + of Evennia and messing with them might well break things for you. + + + * Base properties defined/available on all Objects + + key (string) - name of object + name (string)- same as key + dbref (int, read-only) - unique #id-number. Also "id" can be used. + date_created (string) - time stamp of object creation + + account (Account) - controlling account (if any, only set together with + sessid below) + sessid (int, read-only) - session id (if any, only set together with + account above). Use `sessions` handler to get the + Sessions directly. + location (Object) - current location. Is None if this is a room + home (Object) - safety start-location + has_account (bool, read-only)- will only return *connected* accounts + contents (list of Objects, read-only) - returns all objects inside this + object (including exits) + exits (list of Objects, read-only) - returns all exits from this + object, if any + destination (Object) - only set if this object is an exit. + is_superuser (bool, read-only) - True/False if this user is a superuser + + * Handlers available + + aliases - alias-handler: use aliases.add/remove/get() to use. + permissions - permission-handler: use permissions.add/remove() to + add/remove new perms. + locks - lock-handler: use locks.add() to add new lock strings + scripts - script-handler. Add new scripts to object with scripts.add() + cmdset - cmdset-handler. Use cmdset.add() to add new cmdsets to object + nicks - nick-handler. New nicks with nicks.add(). + sessions - sessions-handler. Get Sessions connected to this + object with sessions.get() + attributes - attribute-handler. Use attributes.add/remove/get. + db - attribute-handler: Shortcut for attribute-handler. Store/retrieve + database attributes using self.db.myattr=val, val=self.db.myattr + ndb - non-persistent attribute handler: same as db but does not create + a database entry when storing data + + * Helper methods (see src.objects.objects.py for full headers) + + search(ostring, global_search=False, attribute_name=None, + use_nicks=False, location=None, ignore_errors=False, account=False) + execute_cmd(raw_string) + msg(text=None, **kwargs) + msg_contents(message, exclude=None, from_obj=None, **kwargs) + move_to(destination, quiet=False, emit_to_obj=None, use_destination=True) + copy(new_key=None) + delete() + is_typeclass(typeclass, exact=False) + swap_typeclass(new_typeclass, clean_attributes=False, no_default=True) + access(accessing_obj, access_type='read', default=False) + check_permstring(permstring) + + * Hooks (these are class methods, so args should start with self): + + basetype_setup() - only called once, used for behind-the-scenes + setup. Normally not modified. + basetype_posthook_setup() - customization in basetype, after the object + has been created; Normally not modified. + + at_object_creation() - only called once, when object is first created. + Object customizations go here. + at_object_delete() - called just before deleting an object. If returning + False, deletion is aborted. Note that all objects + inside a deleted object are automatically moved + to their , they don't need to be removed here. + + at_init() - called whenever typeclass is cached from memory, + at least once every server restart/reload + at_cmdset_get(**kwargs) - this is called just before the command handler + requests a cmdset from this object. The kwargs are + not normally used unless the cmdset is created + dynamically (see e.g. Exits). + at_pre_puppet(account)- (account-controlled objects only) called just + before puppeting + at_post_puppet() - (account-controlled objects only) called just + after completing connection account<->object + at_pre_unpuppet() - (account-controlled objects only) called just + before un-puppeting + at_post_unpuppet(account) - (account-controlled objects only) called just + after disconnecting account<->object link + at_server_reload() - called before server is reloaded + at_server_shutdown() - called just before server is fully shut down + + at_access(result, accessing_obj, access_type) - called with the result + of a lock access check on this object. Return value + does not affect check result. + + at_before_move(destination) - called just before moving object + to the destination. If returns False, move is cancelled. + announce_move_from(destination) - called in old location, just + before move, if obj.move_to() has quiet=False + announce_move_to(source_location) - called in new location, just + after move, if obj.move_to() has quiet=False + at_after_move(source_location) - always called after a move has + been successfully performed. + at_object_leave(obj, target_location) - called when an object leaves + this object in any fashion + at_object_receive(obj, source_location) - called when this object receives + another object + + at_traverse(traversing_object, source_loc) - (exit-objects only) + handles all moving across the exit, including + calling the other exit hooks. Use super() to retain + the default functionality. + at_after_traverse(traversing_object, source_location) - (exit-objects only) + called just after a traversal has happened. + at_failed_traverse(traversing_object) - (exit-objects only) called if + traversal fails and property err_traverse is not defined. + + at_msg_receive(self, msg, from_obj=None, **kwargs) - called when a message + (via self.msg()) is sent to this obj. + If returns false, aborts send. + at_msg_send(self, msg, to_obj=None, **kwargs) - called when this objects + sends a message to someone via self.msg(). + + return_appearance(looker) - describes this object. Used by "look" + command by default + at_desc(looker=None) - called by 'look' whenever the + appearance is requested. + at_get(getter) - called after object has been picked up. + Does not stop pickup. + at_drop(dropper) - called when this object has been dropped. + at_say(speaker, message) - by default, called if an object inside this + object speaks + + """ + + pass diff --git a/typeclasses/rooms.py b/typeclasses/rooms.py new file mode 100644 index 0000000..a60b173 --- /dev/null +++ b/typeclasses/rooms.py @@ -0,0 +1,22 @@ +""" +Room + +Rooms are simple containers that has no location of their own. + +""" + +from evennia import DefaultRoom + + +class Room(DefaultRoom): + """ + Rooms are like any Object, except their location is None + (which is default). They also use basetype_setup() to + add locks so they cannot be puppeted or picked up. + (to change that, use at_object_creation instead) + + See examples/object.py for a list of + properties and methods available on all Objects. + """ + + pass diff --git a/typeclasses/scripts.py b/typeclasses/scripts.py new file mode 100644 index 0000000..b36db5c --- /dev/null +++ b/typeclasses/scripts.py @@ -0,0 +1,92 @@ +""" +Scripts + +Scripts are powerful jacks-of-all-trades. They have no in-game +existence and can be used to represent persistent game systems in some +circumstances. Scripts can also have a time component that allows them +to "fire" regularly or a limited number of times. + +There is generally no "tree" of Scripts inheriting from each other. +Rather, each script tends to inherit from the base Script class and +just overloads its hooks to have it perform its function. + +""" + +from evennia import DefaultScript + + +class Script(DefaultScript): + """ + A script type is customized by redefining some or all of its hook + methods and variables. + + * available properties + + key (string) - name of object + name (string)- same as key + aliases (list of strings) - aliases to the object. Will be saved + to database as AliasDB entries but returned as strings. + dbref (int, read-only) - unique #id-number. Also "id" can be used. + date_created (string) - time stamp of object creation + permissions (list of strings) - list of permission strings + + desc (string) - optional description of script, shown in listings + obj (Object) - optional object that this script is connected to + and acts on (set automatically by obj.scripts.add()) + interval (int) - how often script should run, in seconds. <0 turns + off ticker + start_delay (bool) - if the script should start repeating right away or + wait self.interval seconds + repeats (int) - how many times the script should repeat before + stopping. 0 means infinite repeats + persistent (bool) - if script should survive a server shutdown or not + is_active (bool) - if script is currently running + + * Handlers + + locks - lock-handler: use locks.add() to add new lock strings + db - attribute-handler: store/retrieve database attributes on this + self.db.myattr=val, val=self.db.myattr + ndb - non-persistent attribute handler: same as db but does not + create a database entry when storing data + + * Helper methods + + start() - start script (this usually happens automatically at creation + and obj.script.add() etc) + stop() - stop script, and delete it + pause() - put the script on hold, until unpause() is called. If script + is persistent, the pause state will survive a shutdown. + unpause() - restart a previously paused script. The script will continue + from the paused timer (but at_start() will be called). + time_until_next_repeat() - if a timed script (interval>0), returns time + until next tick + + * Hook methods (should also include self as the first argument): + + at_script_creation() - called only once, when an object of this + class is first created. + is_valid() - is called to check if the script is valid to be running + at the current time. If is_valid() returns False, the running + script is stopped and removed from the game. You can use this + to check state changes (i.e. an script tracking some combat + stats at regular intervals is only valid to run while there is + actual combat going on). + at_start() - Called every time the script is started, which for persistent + scripts is at least once every server start. Note that this is + unaffected by self.delay_start, which only delays the first + call to at_repeat(). + at_repeat() - Called every self.interval seconds. It will be called + immediately upon launch unless self.delay_start is True, which + will delay the first call of this method by self.interval + seconds. If self.interval==0, this method will never + be called. + at_stop() - Called as the script object is stopped and is about to be + removed from the game, e.g. because is_valid() returned False. + at_server_reload() - Called when server reloads. Can be used to + save temporary variables you want should survive a reload. + at_server_shutdown() - called at a full server shutdown. + + """ + + pass diff --git a/web/__init__.py b/web/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/web/static_overrides/README.md b/web/static_overrides/README.md new file mode 100644 index 0000000..ab9a09e --- /dev/null +++ b/web/static_overrides/README.md @@ -0,0 +1,13 @@ +If you want to override one of the static files (such as a CSS or JS file) used by Evennia or a Django app installed in your Evennia project, +copy it into this directory's corresponding subdirectory, and it will be placed in the static folder when you run: + + python manage.py collectstatic + +...or when you reload the server via the command line. + +Do note you may have to reproduce any preceeding directory structures for the file to end up in the right place. + +Also note that you may need to clear out existing static files for your new ones to be gathered in some cases. Deleting files in static/ +will force them to be recollected. + +To see what files can be overridden, find where your evennia package is installed, and look in `evennia/web/static/` diff --git a/web/static_overrides/webclient/css/README.md b/web/static_overrides/webclient/css/README.md new file mode 100644 index 0000000..6ab7cbb --- /dev/null +++ b/web/static_overrides/webclient/css/README.md @@ -0,0 +1,3 @@ +You can replace the CSS files for Evennia's webclient here. + +You can find the original files in `evennia/web/static/webclient/css/` diff --git a/web/static_overrides/webclient/js/README.md b/web/static_overrides/webclient/js/README.md new file mode 100644 index 0000000..c785cb1 --- /dev/null +++ b/web/static_overrides/webclient/js/README.md @@ -0,0 +1,3 @@ +You can replace the javascript files for Evennia's webclient page here. + +You can find the original files in `evennia/web/static/webclient/js/` diff --git a/web/static_overrides/website/css/README.md b/web/static_overrides/website/css/README.md new file mode 100644 index 0000000..004fcd8 --- /dev/null +++ b/web/static_overrides/website/css/README.md @@ -0,0 +1,3 @@ +You can replace the CSS files for Evennia's homepage here. + +You can find the original files in `evennia/web/static/website/css/` diff --git a/web/static_overrides/website/images/README.md b/web/static_overrides/website/images/README.md new file mode 100644 index 0000000..2d2060c --- /dev/null +++ b/web/static_overrides/website/images/README.md @@ -0,0 +1,3 @@ +You can replace the image files for Evennia's home page here. + +You can find the original files in `evennia/web/static/website/images/` diff --git a/web/template_overrides/README.md b/web/template_overrides/README.md new file mode 100644 index 0000000..87ba6f1 --- /dev/null +++ b/web/template_overrides/README.md @@ -0,0 +1,4 @@ +Place your own version of templates into this file to override the default ones. +For instance, if there's a template at: `evennia/web/website/templates/website/index.html` +and you want to replace it, create the file `template_overrides/website/index.html` +and it will be loaded instead. diff --git a/web/template_overrides/webclient/README.md b/web/template_overrides/webclient/README.md new file mode 100644 index 0000000..b69d627 --- /dev/null +++ b/web/template_overrides/webclient/README.md @@ -0,0 +1,3 @@ +Replace Evennia's webclient django templates with your own here. + +You can find the original files in `evennia/web/webclient/templates/webclient/` diff --git a/web/template_overrides/website/README.md b/web/template_overrides/website/README.md new file mode 100644 index 0000000..589823a --- /dev/null +++ b/web/template_overrides/website/README.md @@ -0,0 +1,7 @@ +You can replace the django templates (html files) for the website +here. It uses the default "prosimii" theme. If you want to maintain +multiple themes rather than just change the default one in-place, +make new folders under `template_overrides/` and change +`settings.ACTIVE_THEME` to point to the folder name to use. + +You can find the original files under `evennia/web/website/templates/website/` diff --git a/web/template_overrides/website/flatpages/README.md b/web/template_overrides/website/flatpages/README.md new file mode 100644 index 0000000..9cd8142 --- /dev/null +++ b/web/template_overrides/website/flatpages/README.md @@ -0,0 +1,3 @@ +Flatpages require a default.html template, which can be overwritten by placing it in this folder. + +You can find the original files in `evennia/web/website/templates/website/flatpages/` diff --git a/web/template_overrides/website/registration/README.md b/web/template_overrides/website/registration/README.md new file mode 100644 index 0000000..7c0dfbe --- /dev/null +++ b/web/template_overrides/website/registration/README.md @@ -0,0 +1,3 @@ +The templates involving login/logout can be overwritten here. + +You can find the original files in `evennia/web/website/templates/website/registration/` diff --git a/web/urls.py b/web/urls.py new file mode 100644 index 0000000..741706c --- /dev/null +++ b/web/urls.py @@ -0,0 +1,18 @@ +""" +Url definition file to redistribute incoming URL requests to django +views. Search the Django documentation for "URL dispatcher" for more +help. + +""" +from django.conf.urls import url, include + +# default evennia patterns +from evennia.web.urls import urlpatterns + +# eventual custom patterns +custom_patterns = [ + # url(r'/desired/url/', view, name='example'), +] + +# this is required by Django. +urlpatterns = custom_patterns + urlpatterns diff --git a/world/README.md b/world/README.md new file mode 100644 index 0000000..0f3862d --- /dev/null +++ b/world/README.md @@ -0,0 +1,10 @@ +# world/ + +This folder is meant as a miscellanous folder for all that other stuff +related to the game. Code which are not commands or typeclasses go +here, like custom economy systems, combat code, batch-files etc. + +You can restructure and even rename this folder as best fits your +sense of organisation. Just remember that if you add new sub +directories, you must add (optionally empty) `__init__.py` files in +them for Python to be able to find the modules within. diff --git a/world/__init__.py b/world/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/world/batch_cmds.ev b/world/batch_cmds.ev new file mode 100644 index 0000000..ff5469e --- /dev/null +++ b/world/batch_cmds.ev @@ -0,0 +1,26 @@ +# +# A batch-command file is a way to build a game world +# in a programmatic way, by placing a sequence of +# build commands after one another. This allows for +# using a real text editor to edit e.g. descriptions +# rather than entering text on the command line. +# +# A batch-command file is loaded with @batchprocess in-game: +# +# @batchprocess[/interactive] tutorial_examples.batch_cmds +# +# A # as the first symbol on a line begins a comment and +# marks the end of a previous command definition. This is important, +# - every command must be separated by at least one line of comment. +# +# All supplied commands are given as normal, on their own line +# and accept arguments in any format up until the first next +# comment line begins. Extra whitespace is removed; an empty +# line in a command definition translates into a newline. +# +# See `evennia/contrib/tutorial_examples/batch_cmds.ev` for +# an example of a batch-command code. See also the batch-code +# system for loading python-code this way. +# + + diff --git a/world/prototypes.py b/world/prototypes.py new file mode 100644 index 0000000..04aba09 --- /dev/null +++ b/world/prototypes.py @@ -0,0 +1,90 @@ +""" +Prototypes + +A prototype is a simple way to create individualized instances of a +given typeclass. It is dictionary with specific key names. + +For example, you might have a Sword typeclass that implements everything a +Sword would need to do. The only difference between different individual Swords +would be their key, description and some Attributes. The Prototype system +allows to create a range of such Swords with only minor variations. Prototypes +can also inherit and combine together to form entire hierarchies (such as +giving all Sabres and all Broadswords some common properties). Note that bigger +variations, such as custom commands or functionality belong in a hierarchy of +typeclasses instead. + +A prototype can either be a dictionary placed into a global variable in a +python module (a 'module-prototype') or stored in the database as a dict on a +special Script (a db-prototype). The former can be created just by adding dicts +to modules Evennia looks at for prototypes, the latter is easiest created +in-game via the `olc` command/menu. + +Prototypes are read and used to create new objects with the `spawn` command +or directly via `evennia.spawn` or the full path `evennia.prototypes.spawner.spawn`. + +A prototype dictionary have the following keywords: + +Possible keywords are: +- `prototype_key` - the name of the prototype. This is required for db-prototypes, + for module-prototypes, the global variable name of the dict is used instead +- `prototype_parent` - string pointing to parent prototype if any. Prototype inherits + in a similar way as classes, with children overriding values in their partents. +- `key` - string, the main object identifier. +- `typeclass` - string, if not set, will use `settings.BASE_OBJECT_TYPECLASS`. +- `location` - this should be a valid object or #dbref. +- `home` - valid object or #dbref. +- `destination` - only valid for exits (object or #dbref). +- `permissions` - string or list of permission strings. +- `locks` - a lock-string to use for the spawned object. +- `aliases` - string or list of strings. +- `attrs` - Attributes, expressed as a list of tuples on the form `(attrname, value)`, + `(attrname, value, category)`, or `(attrname, value, category, locks)`. If using one + of the shorter forms, defaults are used for the rest. +- `tags` - Tags, as a list of tuples `(tag,)`, `(tag, category)` or `(tag, category, data)`. +- Any other keywords are interpreted as Attributes with no category or lock. + These will internally be added to `attrs` (eqivalent to `(attrname, value)`. + +See the `spawn` command and `evennia.prototypes.spawner.spawn` for more info. + +""" + +## example of module-based prototypes using +## the variable name as `prototype_key` and +## simple Attributes + +# from random import randint +# +# GOBLIN = { +# "key": "goblin grunt", +# "health": lambda: randint(20,30), +# "resists": ["cold", "poison"], +# "attacks": ["fists"], +# "weaknesses": ["fire", "light"], +# "tags": = [("greenskin", "monster"), ("humanoid", "monster")] +# } +# +# GOBLIN_WIZARD = { +# "prototype_parent": "GOBLIN", +# "key": "goblin wizard", +# "spells": ["fire ball", "lighting bolt"] +# } +# +# GOBLIN_ARCHER = { +# "prototype_parent": "GOBLIN", +# "key": "goblin archer", +# "attacks": ["short bow"] +# } +# +# This is an example of a prototype without a prototype +# (nor key) of its own, so it should normally only be +# used as a mix-in, as in the example of the goblin +# archwizard below. +# ARCHWIZARD_MIXIN = { +# "attacks": ["archwizard staff"], +# "spells": ["greater fire ball", "greater lighting"] +# } +# +# GOBLIN_ARCHWIZARD = { +# "key": "goblin archwizard", +# "prototype_parent" : ("GOBLIN_WIZARD", "ARCHWIZARD_MIXIN") +# }