Fixed bugs, made server start. Still cannot reload.

This commit is contained in:
Griatch 2015-01-08 19:56:54 +01:00
parent 6441859e61
commit ab052b8301
9 changed files with 394 additions and 327 deletions

View File

@ -291,6 +291,22 @@ ERROR_NODJANGO = \
#
#------------------------------------------------------------
def evennia_version():
"""
Get the Evennia version info from the main package.
"""
version = "Unknown"
with open(os.path.join(EVENNIA_ROOT, "VERSION.txt"), 'r') as f:
version = f.read().strip()
try:
version = "%s (rev %s)" % (version, check_output("git rev-parse --short HEAD", shell=True, cwd=EVENNIA_ROOT).strip())
except IOError:
pass
return version
EVENNIA_VERSION = evennia_version()
def check_main_evennia_dependencies():
"""
Checks and imports the Evennia dependencies. This must be done
@ -354,22 +370,6 @@ def set_gamedir(path):
sys.exit()
def evennia_version():
"""
Get the Evennia version info from the main package.
"""
version = "Unknown"
with open(os.path.join(EVENNIA_ROOT, "VERSION.txt"), 'r') as f:
version = f.read().strip()
try:
version = "%s (rev %s)" % (version, check_output("git rev-parse --short HEAD", shell=True, cwd=EVENNIA_ROOT).strip())
except IOError:
pass
return version
EVENNIA_VERSION = evennia_version()
def create_secret_key():
"""
Randomly create the secret key for the settings file
@ -422,6 +422,166 @@ def create_game_directory(dirname):
create_settings_file()
def create_superuser():
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 and has a superuser
"""
# 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
# 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="reload"):
"""
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':
if sys.version < "2.7":
print "Windows requires Python 2.7 or higher for this operation."
return
os.remove(pidfile)
# set restart/norestart flag
if restart == 'reload':
django.core.management.call_command('collectstatic', interactive=False, verbosity=0)
with open(restart_file, 'w') as f:
f.write(str(restart))
try:
os.kill(int(pid), signal)
except OSError:
print "Process %(pid)s could not be signalled. The PID file '%(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):
"""
Try to analyze the given path to find settings.py - this defines
@ -450,12 +610,8 @@ def init_game_directory(path):
print ERROR_SETTINGS
sys.exit()
# testing the main library import. If there are errors in importing
# the main library, it should show here.
evennia = importlib.import_module("evennia")
if check_database():
evennia.init()
# this will both check the database and initialize the evennia dir.
check_database()
# set up the Evennia executables and log file locations
global SERVER_PY_FILE, PORTAL_PY_FILE
@ -517,107 +673,6 @@ def init_game_directory(path):
print INFO_WINDOWS_BATFILE.format(twistd_path=twistd_path)
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 create_database():
print "\nCreating a database ...\n"
django.core.management.call_command("migrate", interactive=False)
print "\n ... database initialized.\n"
def create_superuser():
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 so a database exists and is accessible
from django.db import DatabaseError
from evennia.players.models import PlayerDB
try:
PlayerDB.objects.get(id=1)
except DatabaseError, e:
if exit_on_error:
print ERROR_DATABASE.format(traceback=e)
sys.exit()
return False
except PlayerDB.DoesNotExist:
# no superuser yet. We need to create it.
create_superuser()
return True
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="reload"):
"""
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':
if sys.version < "2.7":
print "Windows requires Python 2.7 or higher for this operation."
return
os.remove(pidfile)
# set restart/norestart flag
if restart == 'reload':
django.core.management.call_command('collectstatic', interactive=False, verbosity=0)
f = open(restart_file, 'w')
f.write(str(restart))
f.close()
try:
os.kill(int(pid), signal)
except OSError:
print "Process %(pid)s could not be signalled. The PID file '%(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 run_menu():
"""
This launches an interactive menu.
@ -668,19 +723,19 @@ def run_menu():
if os.name == 'nt':
print "This operation is not supported under Windows. Log into the game to restart/reload the server."
return
kill(SERVER_PIDFILE, SIG, "Server reloaded.", errmsg % "Server", restart="reload")
kill(SERVER_PIDFILE, SIG, "Server reloaded.", errmsg % "Server", SERVER_RESTART, restart="reload")
elif inp == 6:
if os.name == 'nt':
print "This operation is not supported under Windows."
return
kill(PORTAL_PIDFILE, SIG, "Portal reloaded (or stopped if in daemon mode).", errmsg % "Portal", restart=True)
kill(PORTAL_PIDFILE, SIG, "Portal reloaded (or stopped if in daemon mode).", errmsg % "Portal", PORTAL_RESTART, restart=True)
elif inp == 7:
kill(SERVER_PIDFILE, SIG, "Stopped Portal.", errmsg % "Portal", PORTAL_RESTART, restart=False)
kill(PORTAL_PIDFILE, SIG, "Stopped Server.", errmsg % "Server", restart="shutdown")
kill(PORTAL_PIDFILE, SIG, "Stopped Portal.", errmsg % "Portal", PORTAL_RESTART, restart=False)
kill(SERVER_PIDFILE, SIG, "Stopped Server.", errmsg % "Server", SERVER_RESTART, restart="shutdown")
elif inp == 8:
kill(PORTAL_PIDFILE, SIG, "Stopped Server.", errmsg % "Server", restart="shutdown")
kill(SERVER_PIDFILE, SIG, "Stopped Server.", errmsg % "Server", SERVER_RESTART, restart="shutdown")
elif inp == 9:
kill(SERVER_PIDFILE, SIG, "Stopped Portal.", errmsg % "Portal", PORTAL_RESTART, restart=False)
kill(PORTAL_PIDFILE, SIG, "Stopped Portal.", errmsg % "Portal", PORTAL_RESTART, restart=False)
return
else:
print "Not a valid option."
@ -736,81 +791,27 @@ def server_operation(mode, service, interactive, profiler):
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', restart="reload")
kill(SERVER_PIDFILE, SIG, "Server reloaded.", errmsg % 'Server', SERVER_RESTART, restart="reload")
elif service == 'portal':
print """
Note: Portal usually don'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)
kill(PORTAL_PIDFILE, SIG, "Portal reloaded (or stopped, if it was in daemon mode).", errmsg % 'Portal', PORTAL_RESTART, restart="reload")
else: # all
# default mode, only restart server
kill(SERVER_PIDFILE, SIG, "Server reload.", errmsg % 'Server', restart="reload")
kill(SERVER_PIDFILE, SIG, "Server reload.", errmsg % 'Server', SERVER_RESTART, restart="reload")
elif mode == 'stop':
# stop processes, avoiding reload
if service == 'server':
kill(SERVER_PIDFILE, SIG, "Server stopped.", errmsg % 'Server', restart="shutdown")
kill(SERVER_PIDFILE, SIG, "Server stopped.", errmsg % 'Server', SERVER_RESTART, restart="shutdown")
elif service == 'portal':
kill(PORTAL_PIDFILE, SIG, "Portal stopped.", errmsg % 'Portal', PORTAL_RESTART, restart=False)
else:
kill(PORTAL_PIDFILE, SIG, "Portal stopped.", errmsg % 'Portal', PORTAL_RESTART, restart=False)
kill(SERVER_PIDFILE, SIG, "Server stopped.", errmsg % 'Server', restart="shutdown")
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)
kill(SERVER_PIDFILE, SIG, "Server stopped.", errmsg % 'Server', SERVER_RESTART, restart="shutdown")

View File

@ -1,10 +1,10 @@
#!/usr/bin/env python
"""
This runner is controlled by evennia.py and should normally not be
launched directly. It manages the two main Evennia processes (Server
and Portal) and most importanly runs a passive, threaded loop that
makes sure to restart Server whenever it shuts down.
This runner is controlled by the evennia launcher and should normally
not be launched directly. It manages the two main Evennia processes
(Server and Portal) and most importanly runs a passive, threaded loop
that makes sure to restart Server whenever it shuts down.
Since twistd does not allow for returning an optional exit code we
need to handle the current reload state for server and portal with
@ -183,14 +183,14 @@ def start_services(server_argv, portal_argv):
message, rc = processes.get()
# restart only if process stopped cleanly
if (message == "server_stopped" and int(rc) == 0 and
if (message == "server_stopped" and not rc.returncode and
get_restart_mode(SERVER_RESTART) in ("True", "reload", "reset")):
print PROCESS_RESTART.format(component="Server")
SERVER = thread.start_new_thread(server_waiter, (processes, ))
continue
# normally the portal is not reloaded since it's run as a daemon.
if (message == "portal_stopped" and int(rc) == 0 and
if (message == "portal_stopped" and not rc.returncode and
get_restart_mode(PORTAL_RESTART) == "True"):
print PROCESS_RESTART.format(component="Portal")
PORTAL = thread.start_new_thread(portal_waiter, (processes, ))

View File

@ -21,7 +21,7 @@ DefaultObject = None
DefaultCharacter = None
DefaultRoom = None
DefaultExit = None
Channel = None
DefaultChannel = None
Script = None
# Database models
@ -80,7 +80,8 @@ def init():
mod, fromlist = path.rsplit('.', 1)
return __import__(mod, fromlist=[fromlist])
global DefaultPlayer, DefaultObject, DefaultGuest, DefaultCharacter, DefaultRoom, DefaultExit, Channel, Script
global DefaultPlayer, DefaultObject, DefaultGuest, DefaultCharacter, \
DefaultRoom, DefaultExit, DefaultChannel, Script
global ObjectDB, PlayerDB, ScriptDB, ChannelDB, Msg
global Command, default_cmds, syscmdkeys
global search_object, search_script, search_player, search_channel, search_help
@ -93,7 +94,7 @@ def init():
from objects.objects import DefaultCharacter
from objects.objects import DefaultRoom
from objects.objects import DefaultExit
from comms.comms import Channel
from comms.comms import DefaultChannel
from scripts.scripts import Script
# Database models

View File

@ -10,7 +10,7 @@ from evennia.utils import logger
from evennia.utils.utils import make_iter
class Channel(ChannelDB):
class DefaultChannel(ChannelDB):
"""
This is the base class for all Comms. Inherit from this to create different
types of communication channels.
@ -113,78 +113,6 @@ class Channel(ChannelDB):
from evennia.comms.channelhandler import CHANNELHANDLER
CHANNELHANDLER.update()
def channel_prefix(self, msg=None, emit=False):
"""
How the channel should prefix itself for users. Return a string.
"""
return '[%s] ' % self.key
def format_senders(self, senders=None):
"""
Function used to format a list of sender names.
This function exists separately so that external sources can use
it to format source names in the same manner as normal object/player
names.
"""
if not senders:
return ''
return ', '.join(senders)
def pose_transform(self, msg, sender_string):
"""
Detects if the sender is posing, and modifies the message accordingly.
"""
pose = False
message = msg.message
message_start = message.lstrip()
if message_start.startswith((':', ';')):
pose = True
message = message[1:]
if not message.startswith((':', "'", ',')):
if not message.startswith(' '):
message = ' ' + message
if pose:
return '%s%s' % (sender_string, message)
else:
return '%s: %s' % (sender_string, message)
def format_external(self, msg, senders, emit=False):
"""
Used for formatting external messages. This is needed as a separate
operation because the senders of external messages may not be in-game
objects/players, and so cannot have things like custom user
preferences.
senders should be a list of strings, each containing a sender.
msg should contain the body of the message to be sent.
"""
if not senders:
emit = True
if emit:
return msg.message
senders = ', '.join(senders)
return self.pose_transform(msg, senders)
def format_message(self, msg, emit=False):
"""
Formats a message body for display.
If emit is True, it means the message is intended to be posted detached
from an identity.
"""
# We don't want to count things like external sources as senders for
# the purpose of constructing the message string.
senders = [sender for sender in msg.senders if hasattr(sender, 'key')]
if not senders:
emit = True
if emit:
return msg.message
else:
senders = [sender.key for sender in msg.senders]
senders = ', '.join(senders)
return self.pose_transform(msg, senders)
def message_transform(self, msg, emit=False, prefix=True,
sender_strings=None, external=False):
"""
@ -199,57 +127,6 @@ class Channel(ChannelDB):
msg.message = body
return msg
def pre_join_channel(self, joiner):
"""
Run right before a channel is joined. If this returns a false value,
channel joining is aborted.
"""
return True
def post_join_channel(self, joiner):
"""
Run right after an object or player joins a channel.
"""
return True
def pre_leave_channel(self, leaver):
"""
Run right before a user leaves a channel. If this returns a false
value, leaving the channel will be aborted.
"""
return True
def post_leave_channel(self, leaver):
"""
Run right after an object or player leaves a channel.
"""
pass
def pre_send_message(self, msg):
"""
Run before a message is sent to the channel.
This should return the message object, after any transformations.
If the message is to be discarded, return a false value.
"""
return msg
def post_send_message(self, msg):
"""
Run after a message is sent to the channel.
"""
pass
def at_init(self):
"""
This is always called whenever this channel is initiated --
that is, whenever it its typeclass is cached from memory. This
happens on-demand first time the channel is used or activated
in some way after being created but also after each server
restart or reload.
"""
pass
def distribute_message(self, msg, online=False):
"""
Method for grabbing all listeners that a message should be sent to on
@ -328,3 +205,131 @@ class Channel(ChannelDB):
"""
self.msg(message, senders=senders, header=header, persistent=False)
# hooks
def channel_prefix(self, msg=None, emit=False):
"""
How the channel should prefix itself for users. Return a string.
"""
return '[%s] ' % self.key
def format_senders(self, senders=None):
"""
Function used to format a list of sender names.
This function exists separately so that external sources can use
it to format source names in the same manner as normal object/player
names.
"""
if not senders:
return ''
return ', '.join(senders)
def pose_transform(self, msg, sender_string):
"""
Detects if the sender is posing, and modifies the message accordingly.
"""
pose = False
message = msg.message
message_start = message.lstrip()
if message_start.startswith((':', ';')):
pose = True
message = message[1:]
if not message.startswith((':', "'", ',')):
if not message.startswith(' '):
message = ' ' + message
if pose:
return '%s%s' % (sender_string, message)
else:
return '%s: %s' % (sender_string, message)
def format_external(self, msg, senders, emit=False):
"""
Used for formatting external messages. This is needed as a separate
operation because the senders of external messages may not be in-game
objects/players, and so cannot have things like custom user
preferences.
senders should be a list of strings, each containing a sender.
msg should contain the body of the message to be sent.
"""
if not senders:
emit = True
if emit:
return msg.message
senders = ', '.join(senders)
return self.pose_transform(msg, senders)
def format_message(self, msg, emit=False):
"""
Formats a message body for display.
If emit is True, it means the message is intended to be posted detached
from an identity.
"""
# We don't want to count things like external sources as senders for
# the purpose of constructing the message string.
senders = [sender for sender in msg.senders if hasattr(sender, 'key')]
if not senders:
emit = True
if emit:
return msg.message
else:
senders = [sender.key for sender in msg.senders]
senders = ', '.join(senders)
return self.pose_transform(msg, senders)
def pre_join_channel(self, joiner):
"""
Run right before a channel is joined. If this returns a false value,
channel joining is aborted.
"""
return True
def post_join_channel(self, joiner):
"""
Run right after an object or player joins a channel.
"""
return True
def pre_leave_channel(self, leaver):
"""
Run right before a user leaves a channel. If this returns a false
value, leaving the channel will be aborted.
"""
return True
def post_leave_channel(self, leaver):
"""
Run right after an object or player leaves a channel.
"""
pass
def pre_send_message(self, msg):
"""
Run before a message is sent to the channel.
This should return the message object, after any transformations.
If the message is to be discarded, return a false value.
"""
return msg
def post_send_message(self, msg):
"""
Run after a message is sent to the channel.
"""
pass
def at_init(self):
"""
This is always called whenever this channel is initiated --
that is, whenever it its typeclass is cached from memory. This
happens on-demand first time the channel is used or activated
in some way after being created but also after each server
restart or reload.
"""
pass

View File

@ -34,7 +34,7 @@ PORTAL_SERVICES_PLUGIN_MODULES = [mod_import(module) for module in make_iter(set
if os.name == 'nt':
# For Windows we need to handle pid files manually.
PORTAL_PIDFILE = os.path.join(settings.GAME_DIR, 'portal.pid')
PORTAL_PIDFILE = os.path.join(settings.GAME_DIR, "server", 'portal.pid')
#------------------------------------------------------------
# Evennia Portal settings
@ -44,7 +44,7 @@ VERSION = get_evennia_version()
SERVERNAME = settings.SERVERNAME
PORTAL_RESTART = os.path.join(settings.GAME_DIR, 'portal.restart')
PORTAL_RESTART = os.path.join(settings.GAME_DIR, "server", 'portal.restart')
TELNET_PORTS = settings.TELNET_PORTS
SSL_PORTS = settings.SSL_PORTS

View File

@ -53,10 +53,10 @@ _SA = object.__setattr__
if os.name == 'nt':
# For Windows we need to handle pid files manually.
SERVER_PIDFILE = os.path.join(settings.GAME_DIR, 'server.pid')
SERVER_PIDFILE = os.path.join(settings.GAME_DIR, "server", 'server.pid')
# a file with a flag telling the server to restart after shutdown or not.
SERVER_RESTART = os.path.join(settings.GAME_DIR, 'server.restart')
SERVER_RESTART = os.path.join(settings.GAME_DIR, "server", 'server.restart')
# module containing hook methods called during start_stop
SERVER_STARTSTOP_MODULE = mod_import(settings.AT_SERVER_STARTSTOP_MODULE)

View File

@ -242,7 +242,7 @@ LOCK_FUNC_MODULES = ("evennia.locks.lockfuncs", "server.conf.lockfuncs",)
# Module holding OOB (Out of Band) hook objects. This allows for customization
# and expansion of which hooks OOB protocols are allowed to call on the server
# protocols for attaching tracker hooks for when various object field change
OOB_PLUGIN_MODULES = ["evennia.server.oob_cmds", "server.conf.oob_cmds"]
OOB_PLUGIN_MODULES = ["evennia.server.oob_cmds", "server.conf.oobfuncs"]
######################################################################
# Default command sets

View File

@ -32,5 +32,5 @@ a global function oob_error will be used as optional error management.
"""
# import the contents of the default msdp module
from src.server.oob_cmds import *
from evennia.server.oob_cmds import *

View File

@ -0,0 +1,60 @@
"""
Channel
The channel class represents the out-of-character chat-room usable by
Players 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(player) - check if the given player listens to this channel
connect(player) - connect player to this channel
disconnect(player) - disconnect player 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 players on channel, optionally sending only
to players 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