From 8ac011bf662293e3f22566c97710fa165b660bd8 Mon Sep 17 00:00:00 2001 From: Griatch Date: Mon, 2 Feb 2015 16:15:39 +0100 Subject: [PATCH] Added .gitignore file to template --- .gitignore | 7 - bin/evennia.py | 905 +++++++++++++++++++++++++++++++ evennia/game_template/.gitignore | 51 ++ 3 files changed, 956 insertions(+), 7 deletions(-) create mode 100755 bin/evennia.py create mode 100644 evennia/game_template/.gitignore diff --git a/.gitignore b/.gitignore index 93408a842a..7f6c06873e 100644 --- a/.gitignore +++ b/.gitignore @@ -10,7 +10,6 @@ dist build eggs parts -bin var sdist develop-eggs @@ -26,12 +25,6 @@ __pycache__ *.restart *.db3 -# Installation-specific -game/settings.py -game/logs/*.log.* -game/gamesrc/web/static/* -game/gamesrc/web/media/* - # Installer logs pip-log.txt diff --git a/bin/evennia.py b/bin/evennia.py new file mode 100755 index 0000000000..59b1e81864 --- /dev/null +++ b/bin/evennia.py @@ -0,0 +1,905 @@ +#!/usr/bin/env python +""" +EVENNIA SERVER STARTUP SCRIPT + +This is the start point for running Evennia. + +Sets the appropriate environmental variables and launches the server +and portal through the evennia_runner. Run without arguments to get a +menu. Run the script with the -h flag to see usage information. + +""" +import os +import sys +import signal +import shutil +import importlib +from argparse import ArgumentParser +from subprocess import Popen, check_output, call, CalledProcessError, STDOUT +import django + +# Signal processing +SIG = signal.SIGINT + +# Set up the main python paths to Evennia +EVENNIA_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +EVENNIA_BIN = os.path.join(EVENNIA_ROOT, "bin") + +import evennia +EVENNIA_LIB = os.path.join(os.path.dirname(os.path.abspath(evennia.__file__))) +EVENNIA_RUNNER = os.path.join(EVENNIA_BIN, "evennia_runner.py") +EVENNIA_TEMPLATE = os.path.join(EVENNIA_LIB, "game_template") +EVENNIA_BINTESTING = os.path.join(EVENNIA_BIN, "testing") +EVENNIA_DUMMYRUNNER = os.path.join(EVENNIA_BINTESTING, "dummyrunner.py") + +TWISTED_BINARY = "twistd" + +# Game directory structure +SETTINGFILE = "settings.py" +SERVERDIR = "server" +CONFDIR = os.path.join(SERVERDIR, "conf") +SETTINGS_PATH = os.path.join(CONFDIR, SETTINGFILE) +SETTINGS_DOTPATH = "server.conf.settings" +CURRENT_DIR = os.getcwd() +GAMEDIR = CURRENT_DIR + +# Operational setup +SERVER_LOGFILE = None +PORTAL_LOGFILE = None +HTTP_LOGFILE = None +SERVER_PIDFILE = None +PORTAL_PIDFILE = None +SERVER_RESTART = None +PORTAL_RESTART = None +SERVER_PY_FILE = None +PORTAL_PY_FILE = None + +PYTHON_MIN = '2.7' +TWISTED_MIN = '12.0' +DJANGO_MIN = '1.7' +DJANGO_REC = '1.7' + +# add Evennia root to PYTHONPATH note that bin/evennia.py is +# automatically added to sys.modules and will be imported first, which +# is not what we want. So we remove it manually and set the path so +# the root/evennia package is found first instead. +del sys.modules["evennia"] +sys.path[0] = EVENNIA_ROOT + +#------------------------------------------------------------ +# +# Messages +# +#------------------------------------------------------------ + +CREATED_NEW_GAMEDIR = \ + """ + Welcome to Evennia! + Created a new Evennia game directory '{gamedir}'. + + You can now optionally edit your new settings file + at {settings_path}. If you don't, the defaults + will work out of the box. When ready to continue, 'cd' to your + game directory and run: + + evennia migrate + + This initializes the database. To start the server for the first + time, run: + + evennia -i start + + Make sure to create a superuser when asked for it. You should now + be able to (by default) connect to your server on server + 'localhost', port 4000 using a telnet/mud client or + http://localhost:8000 using your web browser. If things don't + work, check so those ports are open. + + """ + +ERROR_NO_GAMEDIR = \ + """ + No Evennia settings file was found. You must run this command from + inside a valid game directory first created with --init. + """ + +WARNING_RUNSERVER = \ + """ + WARNING: There is no need to run the Django development + webserver to test out Evennia web features (the web client + will in fact not work since the Django test server knows + nothing about MUDs). Instead, just start Evennia with the + webserver component active (this is the default). + """ + +ERROR_SETTINGS = \ + """ + There was an error importing Evennia's config file {settingspath}. There is usually + one of three reasons for this: + 1) You are not running this command from your game directory. + Change directory to your game directory and try again (or + create a new game directory using evennia --init ) + 2) The settings file contains a syntax error. If you see a + traceback above, review it, resolve the problem and try again. + 3) Django is not correctly installed. This usually shows as + errors mentioning 'DJANGO_SETTINGS_MODULE'. If you run a + virtual machine, it might be worth to restart it to see if + this resolves the issue. + """.format(settingsfile=SETTINGFILE, settingspath=SETTINGS_PATH) + +ERROR_DATABASE = \ + """ + Your database does not seem to be set up correctly. + (error was '{traceback}') + + Standing in your game directory, try to run + + evennia migrate + + to initialize/update the database according to your settings. + """ + +ERROR_WINDOWS_WIN32API = \ + """ + ERROR: Unable to import win32api, which Twisted requires to run. + You may download it from: + + http://sourceforge.net/projects/pywin32 + or + http://starship.python.net/crew/mhammond/win32/Downloads.html + """ + +INFO_WINDOWS_BATFILE = \ + """ + INFO: Since you are running Windows, a file 'twistd.bat' was + created for you. This is a simple batch file that tries to call + the twisted executable. Evennia determined this to be: + + %(twistd_path)s + + If you run into errors at startup you might need to edit + twistd.bat to point to the actual location of the Twisted + executable (usually called twistd.py) on your machine. + + This procedure is only done once. Run evennia.py again when you + are ready to start the server. + """ + +CMDLINE_HELP = \ + """ + Starts or operates the Evennia MU* server. Also allows for + initializing a new game directory and managing the game's + database. You can also pass django manage.py arguments through + this launcher. If you need manage.py --options, use djangoadmin + directly instead. + """ + + +VERSION_INFO = \ + """ + {about} + Evennia {version} + OS: {os} + Python: {python} + Twisted: {twisted} + Django: {django} + """ + +ABOUT_INFO= \ + """ + Evennia MUD/MUX/MU* development system + + Licence: BSD 3-Clause Licence + Web: http://www.evennia.com + Irc: #evennia on FreeNode + Forum: http://www.evennia.com/discussions + Maintainer (2010-): Griatch (griatch AT gmail DOT com) + Maintainer (2006-10): Greg Taylor + + Use -h for command line options. + """ + +HELP_ENTRY = \ + """ + Enter 'evennia -h' for command-line options. + + Use option (1) in a production environment. During development (2) is + usually enough, portal debugging is usually only useful if you are + adding new protocols or are debugging Evennia itself. + + Reload with (5) to update the server with your changes without + disconnecting any players. + + Note: Reload and stop are sometimes poorly supported in Windows. If you have + issues, log into the game to stop or restart the server instead. + """ + +MENU = \ + """ + +----Evennia Launcher-------------------------------------------+ + | | + +--- Starting --------------------------------------------------+ + | | + | 1) (normal): All output to logfiles | + | 2) (server devel): Server logs to terminal (-i option) | + | 3) (portal devel): Portal logs to terminal | + | 4) (full devel): Both Server and Portal logs to terminal | + | | + +--- Restarting ------------------------------------------------+ + | | + | 5) Reload the Server | + | 6) Reload the Portal (only works with portal/full debug) | + | | + +--- Stopping --------------------------------------------------+ + | | + | 7) Stopping both Portal and Server | + | 8) Stopping only Server | + | 9) Stopping only Portal | + | | + +---------------------------------------------------------------+ + | h) Help i) About info q) Abort | + +---------------------------------------------------------------+ + """ + +ERROR_PYTHON_VERSION = \ + """ + ERROR: Python {pversion} used. Evennia requires version + {python_min} or higher (but not 3.x). + """ + +WARNING_TWISTED_VERSION = \ + """ + WARNING: Twisted {tversion} found. Evennia recommends + v{twisted_min} or higher." + """ + +ERROR_NOTWISTED = \ + """ + ERROR: Twisted does not seem to be installed. + """ + +ERROR_DJANGO_MIN = \ + """ + ERROR: Django {dversion} found. Evennia requires version + {django_min} or higher. + """ + +NOTE_DJANGO_MIN = \ + """ + NOTE: Django {dversion} found. This will work, but v{django_rec} + is recommended for production. + """ + +NOTE_DJANGO_NEW = \ + """ + NOTE: Django {dversion} found. This is newer than Evennia's + recommended version (v{django_rec}). It will probably work, but + may be new enough not to be fully tested yet. Report any issues." + """ + +ERROR_NODJANGO = \ + """ + ERROR: Django does not seem to be installed. + """ + +#------------------------------------------------------------ +# +# Functions +# +#------------------------------------------------------------ + +def evennia_version(): + """ + Get the Evennia version info from the main package. + """ + version = "Unknown" + try: + import evennia + version = evennia.__version__ + except ImportError: + pass + try: + version = "%s (rev %s)" % (version, check_output("git rev-parse --short HEAD", shell=True, cwd=EVENNIA_ROOT, stderr=STDOUT).strip()) + except (IOError, CalledProcessError): + pass + return version + +EVENNIA_VERSION = evennia_version() + + +def check_main_evennia_dependencies(): + """ + Checks and imports the Evennia dependencies. This must be done + already before the paths are set up. + """ + error = False + + # Python + pversion = ".".join(str(num) for num in sys.version_info if type(num) == int) + if pversion < PYTHON_MIN: + print ERROR_PYTHON_VERSION.format(pversion=pversion, python_min=PYTHON_MIN) + error = True + # Twisted + try: + import twisted + tversion = twisted.version.short() + if tversion < TWISTED_MIN: + print WARNING_TWISTED_VERSION.format(tversion=tversion, twisted_min=TWISTED_MIN) + except ImportError: + print ERROR_NOTWISTED + error = True + # Django + try: + dversion = ".".join(str(num) for num in django.VERSION if type(num) == int) + # only the main version (1.5, not 1.5.4.0) + dversion_main = ".".join(dversion.split(".")[:2]) + if dversion < DJANGO_MIN: + print ERROR_DJANGO_MIN.format(dversion=dversion_main, django_min=DJANGO_MIN) + error = True + elif DJANGO_MIN <= dversion < DJANGO_REC: + print NOTE_DJANGO_MIN.format(dversion=dversion_main, django_rec=DJANGO_REC) + elif DJANGO_REC < dversion_main: + print NOTE_DJANGO_NEW.format(dversion=dversion_main, django_rec=DJANGO_REC) + except ImportError: + print ERROR_NODJANGO + error = True + if error: + sys.exit() + + +def set_gamedir(path): + """ + Set GAMEDIR based on path, by figuring out where the setting file + is inside the directory tree. + """ + + global GAMEDIR + if os.path.exists(os.path.join(path, SETTINGS_PATH)): + # path at root of game dir + GAMEDIR = os.path.abspath(path) + elif os.path.exists(os.path.join(path, os.path.pardir, SETTINGS_PATH)): + # path given to somewhere one level down + GAMEDIR = os.path.dirname(path) + elif os.path.exists(os.path.join(path, os.path.pardir, os.path.pardir, SETTINGS_PATH)): + # path given to somwhere two levels down + GAMEDIR = os.path.dirname(os.path.dirname(path)) + elif os.path.exists(os.path.join(path, os.path.pardir, os.path. pardir, os.path.pardir, SETTINGS_PATH)): + # path given to somewhere three levels down (custom directories) + GAMEDIR = os.path.dirname(os.path.dirname(os.path.dirname(path))) + else: + # we don't look further down than this ... + print ERROR_NO_GAMEDIR + sys.exit() + + +def create_secret_key(): + """ + Randomly create the secret key for the settings file + """ + import random + import string + secret_key = list((string.letters + + string.digits + string.punctuation).replace("\\", "").replace("'", '"')) + random.shuffle(secret_key) + secret_key = "".join(secret_key[:40]) + return secret_key + + +def create_settings_file(): + """ + Uses the template settings file to build a working + settings file. + """ + settings_path = os.path.join(GAMEDIR, "server", "conf", "settings.py") + with open(settings_path, 'r') as f: + settings_string = f.read() + + # tweak the settings + setting_dict = {"settings_default": os.path.join(EVENNIA_LIB, "settings_default.py"), + "servername":"\"%s\"" % GAMEDIR.rsplit(os.path.sep, 1)[1].capitalize(), + "game_dir":"\"%s\"" % GAMEDIR, + "secret_key":"\'%s\'" % create_secret_key()} + + # modify the settings + settings_string = settings_string.format(**setting_dict) + + with open(settings_path, 'w') as f: + f.write(settings_string) + + +def create_game_directory(dirname): + """ + Initialize a new game directory named dirname + at the current path. This means copying the + template directory from evennia's root. + """ + global GAMEDIR + GAMEDIR = os.path.abspath(os.path.join(CURRENT_DIR, dirname)) + if os.path.exists(GAMEDIR): + print "Cannot create new Evennia game dir: '%s' already exists." % dirname + sys.exit() + # copy template directory + shutil.copytree(EVENNIA_TEMPLATE, GAMEDIR) + # pre-build settings file in the new GAMEDIR + create_settings_file() + + +def create_superuser(): + "Create the superuser player" + print "\nCreate a superuser below. The superuser is Player #1, the 'owner' account of the server.\n" + django.core.management.call_command("createsuperuser", interactive=True) + + +def check_database(exit_on_error=False): + """ + Check database exists + """ + # Check so a database exists and is accessible + from django.db import connection + tables = connection.introspection.get_table_list(connection.cursor()) + if tables and u'players_playerdb' in tables: + # database exists and seems set up. Initialize evennia. + import evennia + evennia.init() + else: + if exit_on_error: + print ERROR_DATABASE.format(traceback=e) + sys.exit() + return False + return True + # Try to get Player#1 + from evennia.players.models import PlayerDB + try: + PlayerDB.objects.get(id=1) + except PlayerDB.DoesNotExist: + # no superuser yet. We need to create it. + create_superuser() + return True + + +def getenv(): + """ + Get current environment and add PYTHONPATH + """ + sep = ";" if os.name == 'nt' else ":" + env = os.environ.copy() + env['PYTHONPATH'] = sep.join(sys.path) + return env + + +def get_pid(pidfile): + """ + Get the PID (Process ID) by trying to access + an PID file. + """ + pid = None + if os.path.exists(pidfile): + f = open(pidfile, 'r') + pid = f.read() + return pid + + +def del_pid(pidfile): + """ + The pidfile should normally be removed after a process has + finished, but when sending certain signals they remain, so we need + to clean them manually. + """ + if os.path.exists(pidfile): + os.remove(pidfile) + + +def kill(pidfile, signal=SIG, succmsg="", errmsg="", restart_file=SERVER_RESTART, restart=False): + """ + Send a kill signal to a process based on PID. A customized + success/error message will be returned. If clean=True, the system + will attempt to manually remove the pid file. + """ + pid = get_pid(pidfile) + if pid: + if os.name == 'nt': + os.remove(pidfile) + # set restart/norestart flag + if restart: + django.core.management.call_command('collectstatic', interactive=False, verbosity=0) + with open(restart_file, 'w') as f: + f.write("reload") + else: + with open(restart_file, 'w') as f: + f.write("shutdown") + try: + os.kill(int(pid), signal) + except OSError: + print "Process %(pid)s cannot be stopped. "\ + "The PID file 'server/%(pidfile)s' seems stale. "\ + "Try removing it." % {'pid': pid, 'pidfile': pidfile} + return + print "Evennia:", succmsg + return + print "Evennia:", errmsg + + +def show_version_info(about=False): + """ + Display version info + """ + import os, sys + import twisted + import django + + return VERSION_INFO.format(version=EVENNIA_VERSION, + about=ABOUT_INFO if about else "", + os=os.name, python=sys.version.split()[0], + twisted=twisted.version.short(), + django=django.get_version()) + + +def error_check_python_modules(): + """ + Import settings modules in settings. This will raise exceptions on + pure python-syntax issues which are hard to catch gracefully + with exceptions in the engine (since they are formatting errors in + the python source files themselves). Best they fail already here + before we get any further. + """ + from django.conf import settings + def imp(path, split=True): + mod, fromlist = path, "None" + if split: + mod, fromlist = path.rsplit('.', 1) + __import__(mod, fromlist=[fromlist]) + + # core modules + imp(settings.COMMAND_PARSER) + imp(settings.SEARCH_AT_RESULT) + imp(settings.SEARCH_AT_MULTIMATCH_INPUT) + imp(settings.CONNECTION_SCREEN_MODULE) + #imp(settings.AT_INITIAL_SETUP_HOOK_MODULE, split=False) + for path in settings.LOCK_FUNC_MODULES: + imp(path, split=False) + # cmdsets + + deprstring = "settings.%s should be renamed to %s. If defaults are used, " \ + "their path/classname must be updated (see evennia/settings_default.py)." + if hasattr(settings, "CMDSET_DEFAULT"): + raise DeprecationWarning(deprstring % ("CMDSET_DEFAULT", "CMDSET_CHARACTER")) + if hasattr(settings, "CMDSET_OOC"): + raise DeprecationWarning(deprstring % ("CMDSET_OOC", "CMDSET_PLAYER")) + if settings.WEBSERVER_ENABLED and not isinstance(settings.WEBSERVER_PORTS[0], tuple): + raise DeprecationWarning("settings.WEBSERVER_PORTS must be on the form [(proxyport, serverport), ...]") + if hasattr(settings, "BASE_COMM_TYPECLASS"): + raise DeprecationWarning(deprstring % ("BASE_COMM_TYPECLASS", "BASE_CHANNEL_TYPECLASS")) + if hasattr(settings, "COMM_TYPECLASS_PATHS"): + raise DeprecationWarning(deprstring % ("COMM_TYPECLASS_PATHS", "CHANNEL_TYPECLASS_PATHS")) + if hasattr(settings, "CHARACTER_DEFAULT_HOME"): + raise DeprecationWarning("settings.CHARACTER_DEFAULT_HOME should be renamed to DEFAULT_HOME. " \ + "See also settings.START_LOCATION (see evennia/settings_default.py).") + + from evennia.commands import cmdsethandler + if not cmdsethandler.import_cmdset(settings.CMDSET_UNLOGGEDIN, None): print "Warning: CMDSET_UNLOGGED failed to load!" + if not cmdsethandler.import_cmdset(settings.CMDSET_CHARACTER, None): print "Warning: CMDSET_CHARACTER failed to load" + if not cmdsethandler.import_cmdset(settings.CMDSET_PLAYER, None): print "Warning: CMDSET_PLAYER failed to load" + # typeclasses + imp(settings.BASE_PLAYER_TYPECLASS) + imp(settings.BASE_OBJECT_TYPECLASS) + imp(settings.BASE_CHARACTER_TYPECLASS) + imp(settings.BASE_ROOM_TYPECLASS) + imp(settings.BASE_EXIT_TYPECLASS) + imp(settings.BASE_SCRIPT_TYPECLASS) + + +def init_game_directory(path, check_db=True): + """ + Try to analyze the given path to find settings.py - this defines + the game directory and also sets PYTHONPATH as well as the + django path. + """ + # set the GAMEDIR path + set_gamedir(path) + + # Add gamedir to python path + sys.path.insert(1, GAMEDIR) + + # Prepare django; set the settings location + os.environ['DJANGO_SETTINGS_MODULE'] = SETTINGS_DOTPATH + + # required since django1.7 + django.setup() + + # test existence of the settings module + try: + from django.conf import settings + except Exception, ex: + if not str(ex).startswith("No module named"): + import traceback + print traceback.format_exc().strip() + print ERROR_SETTINGS + sys.exit() + + # this will both check the database and initialize the evennia dir. + if check_db: + check_database() + + # set up the Evennia executables and log file locations + global SERVER_PY_FILE, PORTAL_PY_FILE + global SERVER_LOGFILE, PORTAL_LOGFILE, HTTP_LOGFILE + global SERVER_PIDFILE, PORTAL_PIDFILE + global SERVER_RESTART, PORTAL_RESTART + global EVENNIA_VERSION + + SERVER_PY_FILE = os.path.join(EVENNIA_LIB, "server", "server.py") + PORTAL_PY_FILE = os.path.join(EVENNIA_LIB, "portal", "portal", "portal.py") + + SERVER_PIDFILE = os.path.join(GAMEDIR, SERVERDIR, "server.pid") + PORTAL_PIDFILE = os.path.join(GAMEDIR, SERVERDIR, "portal.pid") + + SERVER_RESTART = os.path.join(GAMEDIR, SERVERDIR, "server.restart") + PORTAL_RESTART = os.path.join(GAMEDIR, SERVERDIR, "portal.restart") + + SERVER_LOGFILE = settings.SERVER_LOG_FILE + PORTAL_LOGFILE = settings.PORTAL_LOG_FILE + HTTP_LOGFILE = settings.HTTP_LOG_FILE + + if os.name == 'nt': + # We need to handle Windows twisted separately. We create a + # batchfile in game/server, linking to the actual binary + + global TWISTED_BINARY + TWISTED_BINARY = "twistd.bat" + + # add path so system can find the batfile + sys.path.insert(1, os.path.join(GAMEDIR, SERVERDIR)) + + try: + importlib.import_module("win32api") + except ImportError: + print ERROR_WINDOWS_WIN32API + sys.exit() + + if not os.path.exists(os.path.join(EVENNIA_BIN, TWISTED_BINARY)): + # Test for executable twisted batch file. This calls the + # twistd.py executable that is usually not found on the + # path in Windows. It's not enough to locate + # scripts.twistd, what we want is the executable script + # C:\PythonXX/Scripts/twistd.py. Alas we cannot hardcode + # this location since we don't know if user has Python in + # a non-standard location. So we try to figure it out. + twistd = importlib.import_module("twisted.scripts.twistd") + twistd_dir = os.path.dirname(twistd.__file__) + + # note that we hope the twistd package won't change here, since we + # try to get to the executable by relative path. + twistd_path = os.path.abspath(os.path.join(twistd_dir, + os.pardir, os.pardir, os.pardir, os.pardir, + 'scripts', 'twistd.py')) + + with open('twistd.bat', 'w') as bat_file: + # build a custom bat file for windows + bat_file.write("@\"%s\" \"%s\" %%*" % (sys.executable, twistd_path)) + + print INFO_WINDOWS_BATFILE.format(twistd_path=twistd_path) + +def run_dummyrunner(number_of_dummies): + """ + Start an instance of the dummyrunner + + The dummy players' behavior can be customized by adding a + dummyrunner_settings.py config file in the game's conf directory. + """ + number_of_dummies = str(int(number_of_dummies)) if number_of_dummies else 1 + cmdstr = [sys.executable, EVENNIA_DUMMYRUNNER, "-N", number_of_dummies] + config_file = os.path.join(SETTINGS_PATH, "dummyrunner_settings.py") + if os.path.exists(config_file): + cmdstr.extend(["--config", config_file]) + try: + call(cmdstr, env=getenv()) + except KeyboardInterrupt: + pass + +def run_menu(): + """ + This launches an interactive menu. + """ + while True: + # menu loop + + print MENU + inp = raw_input(" option > ") + + # quitting and help + if inp.lower() == 'q': + return + elif inp.lower() == 'h': + print HELP_ENTRY + raw_input("press to continue ...") + continue + elif inp.lower() in ('v', 'i', 'a'): + print show_version_info(about=True) + raw_input("press to continue ...") + continue + + # options + try: + inp = int(inp) + except ValueError: + print "Not a valid option." + continue + if inp == 1: + # start everything, log to log files + server_operation("start", "all", False, False) + elif inp == 2: + # start everything, server interactive start + server_operation("start", "all", True, False) + elif inp == 3: + # start everything, portal interactive start + server_operation("start", "server", False, False) + server_operation("start", "portal", True, False) + elif inp == 4: + # start both server and portal interactively + server_operation("start", "server", True, False) + server_operation("start", "portal", True, False) + elif inp == 5: + # reload the server + server_operation("reload", "server", None, None) + elif inp == 6: + # reload the portal + server_operation("reload", "portal", None, None) + elif inp == 7: + # stop server and portal + server_operation("stop", "all", None, None) + elif inp == 8: + # stop server + server_operation("stop", "server", None, None) + elif inp == 9: + # stop portal + server_operation("stop", "portal", None, None) + else: + print "Not a valid option." + continue + return + + +def server_operation(mode, service, interactive, profiler): + """ + Handle argument options given on the command line. + + mode - str; start/stop etc + service - str; server, portal or all + interactive - bool; use interactive mode or daemon + profiler - run the service under the profiler + """ + + cmdstr = [sys.executable, EVENNIA_RUNNER] + errmsg = "The %s does not seem to be running." + + if mode == 'start': + + # launch the error checker. Best to catch the errors already here. + error_check_python_modules() + + # starting one or many services + if service == 'server': + if profiler: + cmdstr.append('--pserver') + if interactive: + cmdstr.append('--iserver') + cmdstr.append('--noportal') + elif service == 'portal': + if profiler: + cmdstr.append('--pportal') + if interactive: + cmdstr.append('--iportal') + cmdstr.append('--noserver') + django.core.management.call_command('collectstatic', verbosity=1, interactive=False) + else: # all + # for convenience we don't start logging of + # portal, only of server with this command. + if profiler: + cmdstr.append('--pserver') # this is the common case + if interactive: + cmdstr.append('--iserver') + django.core.management.call_command('collectstatic', verbosity=1, interactive=False) + cmdstr.extend([GAMEDIR, TWISTED_BINARY, SERVER_LOGFILE, PORTAL_LOGFILE, HTTP_LOGFILE]) + # start the server + Popen(cmdstr, env=getenv()) + + elif mode == 'reload': + # restarting services + if os.name == 'nt': + print "Restarting from command line is not supported under Windows. Log into the game to restart." + return + if service == 'server': + kill(SERVER_PIDFILE, SIG, "Server reloaded.", errmsg % 'Server', SERVER_RESTART, restart=True) + elif service == 'portal': + print """ + Note: Portal usually doesnt't need to be reloaded unless you are debugging in interactive mode. + If Portal was running in default Daemon mode, it cannot be restarted. In that case you have + to restart it manually with 'evennia.py start portal' + """ + kill(PORTAL_PIDFILE, SIG, "Portal reloaded (or stopped, if it was in daemon mode).", errmsg % 'Portal', PORTAL_RESTART, restart=True) + else: # all + # default mode, only restart server + kill(SERVER_PIDFILE, SIG, "Server reload.", errmsg % 'Server', SERVER_RESTART, restart=True) + + elif mode == 'stop': + # stop processes, avoiding reload + if service == 'server': + kill(SERVER_PIDFILE, SIG, "Server stopped.", errmsg % 'Server', SERVER_RESTART) + elif service == 'portal': + kill(PORTAL_PIDFILE, SIG, "Portal stopped.", errmsg % 'Portal', PORTAL_RESTART) + else: + kill(PORTAL_PIDFILE, SIG, "Portal stopped.", errmsg % 'Portal', PORTAL_RESTART) + kill(SERVER_PIDFILE, SIG, "Server stopped.", errmsg % 'Server', SERVER_RESTART) + + + +def main(): + """ + Run the evennia main program. + """ + + # set up argument parser + + parser = ArgumentParser(description=CMDLINE_HELP) + parser.add_argument('-v', '--version', action='store_true', + dest='show_version', default=False, + help="Show version info.") + parser.add_argument('-i', '--interactive', action='store_true', + dest='interactive', default=False, + help="Start given processes in interactive mode.") + parser.add_argument('--init', action='store', dest="init", metavar="name", + help="Creates a new game directory 'name' at the current location.") + parser.add_argument('--profiler', action='store_true', dest='profiler', default=False, + help="Start given server component under the Python profiler.") + parser.add_argument('--dummyrunner', nargs=1, action='store', dest='dummyrunner', metavar="N", + help="Tests a running server by connecting N dummy players to it.") + parser.add_argument("mode", metavar="option", nargs='?', default="help", + help="Operational mode or management option. Commonly start, stop, reload, migrate, or menu (default).") + parser.add_argument("service", metavar="component", nargs='?', choices=["all", "server", "portal"], default="all", + help="Which server component to operate on. One of server, portal or all (default).") + + args = parser.parse_args() + + # handle arguments + + mode, service = args.mode, args.service + + check_main_evennia_dependencies() + + if args.init: + create_game_directory(args.init) + print CREATED_NEW_GAMEDIR.format(gamedir=args.init, + settings_path=os.path.join(args.init, SETTINGS_PATH)) + sys.exit() + + if args.show_version: + print show_version_info(mode=="help") + sys.exit() + if mode == "help" and not args.dummyrunner: + print ABOUT_INFO + sys.exit() + check_db = not mode == "migrate" + + # this must be done first - it sets up all the global properties + # and initializes django for the game directory + init_game_directory(CURRENT_DIR, check_db=check_db) + + if args.dummyrunner: + # launch the dummy runner + run_dummyrunner(args.dummyrunner[0]) + elif mode == 'menu': + # launch menu for operation + run_menu() + elif mode in ('start', 'reload', 'stop'): + # operate the server directly + server_operation(mode, service, args.interactive, args.profiler) + else: + # pass-through to django manager + if mode in ('runserver', 'testserver'): + print WARNING_RUNSERVER + django.core.management.call_command(mode) + + +if __name__ == '__main__': + # start Evennia from the command line + main() diff --git a/evennia/game_template/.gitignore b/evennia/game_template/.gitignore new file mode 100644 index 0000000000..905ce44bc6 --- /dev/null +++ b/evennia/game_template/.gitignore @@ -0,0 +1,51 @@ +*.py[cod] + +# C extensions +*.so + +# Packages +*.egg +*.egg-info +dist +build +eggs +parts +var +sdist +develop-eggs +.installed.cfg +lib +lib64 +__pycache__ + +# Other +*.swp +*.log +*.pid +*.restart +*.db3 + +# Installation-specific +server/conf/settings.py +server/logs/*.log.* +web/static/* +web/media/* + +# Installer logs +pip-log.txt + +# Unit test / coverage reports +.coverage +.tox +nosetests.xml + +# Translations +*.mo + +# Mr Developer +.mr.developer.cfg +.project +.pydevproject + +# PyCharm config +.idea