Cleanups and bug fixes. Fixed the @unlink command and also made it overally more stable. Resolves issue 161. Added more string conversion routines to handle non-ascii variables being stored in an Attribute. Resolves issue 160.

This commit is contained in:
Griatch 2011-04-16 22:26:22 +00:00
parent 14db4bea4d
commit 7d30b337d9
27 changed files with 873 additions and 1048 deletions

View file

@ -66,6 +66,7 @@ the 'Fishing' set. Fishing from a boat? No problem!
import traceback
from src.utils import logger
from src.commands.cmdset import CmdSet
from src.server.models import ServerConfig
CACHED_CMDSETS = {}
@ -118,8 +119,8 @@ def import_cmdset(python_path, cmdsetobj, emit_to_obj=None, no_logging=False):
if errstring and not no_logging:
print errstring
logger.log_trace()
if emit_to_obj:
emit_to_obj.msg(errstring)
if emit_to_obj and not ServerConfig.objects.conf("server_starting_mode"):
object.__getattribute__(emit_to_obj, "msg")(errstring)
raise # have to raise, or we will not see any errors in some situations!
# classes

View file

@ -371,7 +371,7 @@ class CmdCreate(ObjManipCommand):
# create object (if not a valid typeclass, the default
# object typeclass will automatically be used)
lockstring = "owner:id(%s);examine:perm(Builders);delete:id(%s) or perm(Wizards);get:all()" % (caller.id, caller.id)
lockstring = "control:id(%s);examine:perm(Builders);delete:id(%s) or perm(Wizards);get:all()" % (caller.id, caller.id)
obj = create.create_object(typeclass, name, caller,
home=caller, aliases=aliases, locks=lockstring)
if not obj:
@ -489,7 +489,7 @@ class CmdDesc(MuxCommand):
obj = caller
desc = self.args
# storing the description
obj.db.desc = desc
obj.db.desc = desc
caller.msg("The description was set on %s." % obj.key)
@ -607,7 +607,10 @@ class CmdDig(ObjManipCommand):
new_room = create.create_object(typeclass, room["name"],
aliases=room["aliases"])
new_room.locks.add(lockstring)
room_string = "Created room '%s' of type %s." % (new_room.name, typeclass)
alias_string = ""
if new_room.aliases:
alias_string = " (%s)" % ", ".join(new_room_aliases)
room_string = "Created room %s(%s)%s of type %s." % (new_room, new_room.dbref, alias_string, typeclass)
exit_to_string = ""
exit_back_string = ""
@ -637,9 +640,12 @@ class CmdDig(ObjManipCommand):
aliases=to_exit["aliases"])
new_to_exit.destination = new_room
new_to_exit.locks.add(lockstring)
exit_to_string = "\nCreated new Exit to new room: %s (aliases: %s)."
exit_to_string = exit_to_string % (new_to_exit.name,
new_to_exit.aliases)
alias_string = ""
if new_to_exit.aliases:
alias_string = " (%s)" % ", ".join(new_to_exit.aliases)
exit_to_string = "\nCreated Exit from %s to %s: %s(%s)%s."
exit_to_string = exit_to_string % (location.name, new_room.name, new_to_exit,
new_to_exit.dbref, alias_string)
if len(self.rhs_objs) > 1:
# Building the exit back to the current room
@ -666,9 +672,12 @@ class CmdDig(ObjManipCommand):
aliases=back_exit["aliases"])
new_back_exit.destination = location
new_back_exit.locks.add(lockstring)
exit_back_string = "\nExit back from new room: %s (aliases: %s)."
exit_back_string = exit_back_string % (new_back_exit.name,
new_back_exit.aliases)
alias_string = ""
if new_back_exit.aliases:
alias_string = " (%s)" % ", ".join(new_back_exit.aliases)
exit_back_string = "\nCreated Exit back from %s to %s: %s(%s)%s."
exit_back_string = exit_back_string % (new_room.name, location.name,
new_back_exit, new_back_exit.dbref, alias_string)
caller.msg("%s%s%s" % (room_string, exit_to_string, exit_back_string))
if new_room and 'teleport' in self.switches:
caller.move_to(new_room)
@ -682,17 +691,16 @@ class CmdLink(MuxCommand):
@link[/switches] <object> = <target>
@link[/switches] <object> =
@link[/switches] <object>
Switches:
twoway - this is only useful when both <object>
and <target> are Exits. If so, a link back
from <target> to <object> will also be created.
Switch:
twoway - connect two exits. For this to, BOTH <object>
and <target> must be exit objects.
If <object> is an exit, set its destination. For all other object types, this
command sets the object's Home.
The second form sets the destination/home to None and the third form inspects
the current value of destination/home on <object>.
If <object> is an exit, set its destination to <target>. Two-way operation
instead sets the destination to the *locations* of the respective given
arguments.
The second form (a lone =) sets the destination to None (same as the @unlink command)
and the third form (without =) just shows the currently set destination.
"""
key = "@link"
@ -719,25 +727,25 @@ class CmdLink(MuxCommand):
# this means a target name was given
target = caller.search(self.rhs, global_search=True)
if not target:
return
# if obj is an exit (has property destination),
# set that, otherwise set home.
if obj.destination:
obj.destination = target
if "twoway" in self.switches:
if target.destination:
target.destination = obj
string = "Link created %s <-> %s (two-way)." % (obj.name, target.name)
else:
string = "Cannot create two-way link to non-exit."
string += " Link created %s -> %s (one way)." % (obj.name, target.name)
else:
string = "Link created %s -> %s (one way)." % (obj.name, target.name)
return
string = ""
if not obj.destination:
string += "Note: %s(%s) did not have a destination set before. Make sure you linked the right thing." % (obj.name,obj.dbref)
if "twoway" in self.switches:
if not (target.location and obj.location):
string = "To create a two-way link, %s and %s must both have a location" % (obj, target)
string += " (i.e. they cannot be rooms, but should be exits)."
self.caller.msg(string)
return
if not target.destination:
string += "\nNote: %s(%s) did not have a destination set before. Make sure you linked the right thing." % (target.name, target.dbref)
obj.destination = target.location
target.destination = obj.location
string += "\nLink created %s (in %s) <-> %s (in %s) (two-way)." % (obj.name, obj.location, target.name, target.location)
else:
# obj is not an exit (has not property destination),
# so set home instead
obj.home = target
string = "Set %s's home to %s." % (obj.name, target.name)
obj.destination = target
string += "\nLink created %s -> %s (one way)." % (obj.name, target)
elif self.rhs == None:
# this means that no = was given (otherwise rhs
@ -745,20 +753,20 @@ class CmdLink(MuxCommand):
# the home/destination on object
dest = obj.destination
if dest:
"%s is an exit to %s." % (obj.name, dest.name)
string = "%s is an exit to %s." % (obj.name, dest.name)
else:
string = "%s has home %s." % (obj.name, obj.home)
string = "%s is not an exit. Its home location is %s." % obj.home
else:
# We gave the command @link 'obj = ' which means we want to
# clear destination or set home to None.
# clear destination.
if obj.destination:
del obj.destination
string = "Exit %s no longer links anywhere." % obj.name
obj.destination = None
string = "Former exit %s no longer links anywhere." % obj.name
else:
del obj.home
string = "%s no longer has a home." % obj.name
string = "%s had no destination to unlink." % obj.name
# give feedback
caller.msg(string)
caller.msg(string.strip())
class CmdUnLink(CmdLink):
"""
@ -794,6 +802,54 @@ class CmdUnLink(CmdLink):
# call the @link functionality
super(CmdUnLink, self).func()
class CmdHome(CmdLink):
"""
@home - control an object's home location
Usage:
@home <obj> [= home_location]
The "home" location is a "safety" location for objects; they
will be moved there if their current location ceases to exist. All
objects should always have a home location for this reason.
It is also a convenient target of the "home" command.
If no location is given, just view the object's home location.
"""
key = "@home"
locks = "cmd:perm(@home) or perm(Builders)"
help_category = "Building"
def func(self):
"implement the command"
if not self.args:
string = "Usage: @home <obj> [= home_location]"
self.caller.msg(string)
return
obj = self.caller.search(self.lhs, global_search=True)
if not obj:
return
if not self.rhs:
# just view
home = obj.home
if not home:
string = "This object has no home location set!"
else:
string = "%s's home is set to %s(%s)." % (obj, home, home.dbref)
else:
# set a home location
new_home = self.caller.search(self.rhs, global_search=True)
if not new_home:
return
old_home = obj.home
obj.home = new_home
if old_home:
string = "%s's home location was changed from %s(%s) to %s(%s)." % (old_home, old_home.dbref, new_home, new_home.dbref)
else:
string = "%s' home location was set to %s(%s)." % (new_home, new_home.dbref)
self.caller.msg(string)
class CmdListCmdSets(MuxCommand):
"""
@ -1454,7 +1510,7 @@ class CmdExamine(ObjManipCommand):
perms = ["<Superuser>"]
elif not perms:
perms = ["<None>"]
string += "\n{wPlayer Perms/Locks{n: %s" % (", ".join(perms))
string += "\n{wPlayer Perms{n: %s" % (", ".join(perms))
string += "\n{wTypeclass{n: %s (%s)" % (obj.typeclass, obj.typeclass_path)
string += "\n{wLocation{n: %s" % obj.location

View file

@ -78,6 +78,7 @@ class DefaultCmdSet(CmdSet):
self.add(building.CmdTypeclass())
self.add(building.CmdLock())
self.add(building.CmdScript())
self.add(building.CmdHome())
# Comm commands
self.add(comms.CmdAddCom())
@ -88,6 +89,7 @@ class DefaultCmdSet(CmdSet):
self.add(comms.CmdCdesc())
self.add(comms.CmdPage())
self.add(comms.CmdIRC2Chan())
self.add(comms.CmdIMC2Chan())
# Batchprocessor commands
self.add(batchprocess.CmdBatchCommands())

View file

@ -3,7 +3,7 @@ Comsys command module.
"""
from django.conf import settings
from src.comms.models import Channel, Msg, PlayerChannelConnection, ExternalChannelConnection
from src.comms import irc
from src.comms import irc, imc2
from src.comms.channelhandler import CHANNELHANDLER
from src.utils import create, utils
from src.commands.default.muxcommand import MuxCommand
@ -892,16 +892,20 @@ class CmdIRC2Chan(MuxCommand):
except Exception:
string = "IRC bot definition '%s' is not valid." % self.rhs
self.caller.msg(string)
return
return
if 'disconnect' in self.switches or 'remove' in self.switches or 'delete' in self.switches:
chanmatch = find_channel(self.caller, channel, silent=True)
if chanmatch:
channel = chanmatch.key
ok = irc.delete_connection(irc_network, irc_port, irc_channel, irc_botname)
if not ok:
self.caller.msg("IRC connection/bot could not be removed, does it exist?")
else:
self.caller.msg("IRC connection destroyed.")
return
channel = find_channel(self.caller, channel)
if not channel:
return
@ -910,3 +914,92 @@ class CmdIRC2Chan(MuxCommand):
self.caller.msg("This IRC connection already exists.")
return
self.caller.msg("Connection created. Starting IRC bot.")
class CmdIMC2Chan(MuxCommand):
"""
imc2chan - link an evennia channel to imc2
Usage:
@imc2chan[/switches] <evennia_channel> = <imc2network> <port> <imc2channel> <imc2_client_pwd> <imc2_server_pwd>
Switches:
/disconnect - this will delete the bot and remove the imc2 connection to the channel.
/remove - "
/list - show all imc2<->evennia mappings
Example:
@imc2chan myimcchan = server02.mudbytes.net 9000 ievennia Gjds8372 LAKdf84e
Connect an existing evennia channel to an IMC2 network and channel. You must have registered with the network
beforehand and obtained valid server- and client passwords. You will always connect using the name of your
mud, as defined by settings.SERVERNAME, so make sure this was the name you registered to the imc2 network.
"""
key = "@imc2chan"
locks = "cmd:serversetting(IMC2_ENABLED) and perm(Wizards)"
help_category = "Comms"
def func(self):
"Setup the imc-channel mapping"
if 'list' in self.switches:
# show all connections
connections = ExternalChannelConnection.objects.filter(db_external_key__startswith='imc2_')
if connections:
cols = [["Evennia channel"], ["IMC channel"]]
for conn in connections:
cols[0].append(conn.channel.key)
cols[1].append(" ".join(conn.external_config.split('|')))
ftable = utils.format_table(cols)
string = ""
for ir, row in enumerate(ftable):
if ir == 0:
string += "{w%s{n" % "".join(row)
else:
string += "\n" + "".join(row)
self.caller.msg(string)
else:
self.caller.msg("No connections found.")
return
if not settings.IMC2_ENABLED:
string = """IMC2 is not enabled. You need to activate it in game/settings.py."""
self.caller.msg(string)
return
if not self.args or not self.rhs:
string = "Usage: @imc2chan[/switches] <evennia_channel> = <imc2network> <port> <imc2channel> <client_pwd> <server_pwd>"
self.caller.msg(string)
return
channel = self.lhs
try:
imc2_network, imc2_port, imc2_channel, imc2_client_pwd, imc2_server_pwd = [part.strip() for part in self.rhs.split(None, 4)]
except Exception:
string = "IMC2 connnection definition '%s' is not valid." % self.rhs
self.caller.msg(string)
return
# get the name to use for connecting
mudname = settings.SERVERNAME
if 'disconnect' in self.switches or 'remove' in self.switches or 'delete' in self.switches:
chanmatch = find_channel(self.caller, channel, silent=True)
if chanmatch:
channel = chanmatch.key
ok = imc2.delete_connection(channel, imc2_network, imc2_port, imc2_channel, mudname)
if not ok:
self.caller.msg("IMC2 connection could not be removed, does it exist?")
else:
self.caller.msg("IMC2 connection destroyed.")
return
channel = find_channel(self.caller, channel)
if not channel:
return
ok = imc2.create_connection(channel, imc2_network, imc2_port, imc2_channel, mudname, imc2_client_pwd, imc2_server_pwd)
if not ok:
self.caller.msg("This IMC2 connection already exists.")
return
self.caller.msg("Connection created. Connecting to IMC2 server.")

View file

@ -109,103 +109,103 @@ def cmd_imclist(command):
source_object.emit_to(retval)
GLOBAL_CMD_TABLE.add_command("imclist", cmd_imclist, help_category="Comms")
def cmd_imcstatus(command):
"""
imcstatus
# def cmd_imcstatus(command):
# """
# imcstatus
Usage:
imcstatus
# Usage:
# imcstatus
Shows some status information for your IMC2 connection.
"""
source_object = command.source_object
# This manages our game's plugged in services.
collection = command.session.server.service_collection
# Retrieve the IMC2 service.
service = collection.getServiceNamed('IMC2')
# Shows some status information for your IMC2 connection.
# """
# source_object = command.source_object
# # This manages our game's plugged in services.
# collection = command.session.server.service_collection
# # Retrieve the IMC2 service.
# service = collection.getServiceNamed('IMC2')
if service.running == 1:
status_string = 'Running'
else:
status_string = 'Inactive'
# if service.running == 1:
# status_string = 'Running'
# else:
# status_string = 'Inactive'
# Build the output to emit to the player.
retval = '-' * 50
retval += '\n\r'
retval += 'IMC Status\n\r'
retval += ' * MUD Name: %s\n\r' % (settings.IMC2_MUDNAME)
retval += ' * Status: %s\n\r' % (status_string)
retval += ' * Debugging Mode: %s\n\r' % (settings.IMC2_DEBUG)
retval += ' * IMC Network Address: %s\n\r' % (settings.IMC2_SERVER_ADDRESS)
retval += ' * IMC Network Port: %s\n\r' % (settings.IMC2_SERVER_PORT)
retval += '-' * 50
# # Build the output to emit to the player.
# retval = '-' * 50
# retval += '\n\r'
# retval += 'IMC Status\n\r'
# retval += ' * MUD Name: %s\n\r' % (settings.IMC2_MUDNAME)
# retval += ' * Status: %s\n\r' % (status_string)
# retval += ' * Debugging Mode: %s\n\r' % (settings.IMC2_DEBUG)
# retval += ' * IMC Network Address: %s\n\r' % (settings.IMC2_SERVER_ADDRESS)
# retval += ' * IMC Network Port: %s\n\r' % (settings.IMC2_SERVER_PORT)
# retval += '-' * 50
source_object.emit_to(retval)
GLOBAL_CMD_TABLE.add_command("imcstatus", cmd_imcstatus,
priv_tuple=('imc2.admin_imc_channels',), help_category="Comms")
# source_object.emit_to(retval)
# GLOBAL_CMD_TABLE.add_command("imcstatus", cmd_imcstatus,
# priv_tuple=('imc2.admin_imc_channels',), help_category="Comms")
def cmd_IMC2chan(command):
"""
@imc2chan
# def cmd_IMC2chan(command):
# """
# @imc2chan
Usage:
@imc2chan <IMCServer> : <IMCchannel> <channel>
# Usage:
# @imc2chan <IMCServer> : <IMCchannel> <channel>
Links an IMC channel to an existing evennia
channel. You can link as many existing
evennia channels as you like to the
IMC channel this way. Running the command with an
existing mapping will re-map the channels.
# Links an IMC channel to an existing evennia
# channel. You can link as many existing
# evennia channels as you like to the
# IMC channel this way. Running the command with an
# existing mapping will re-map the channels.
Use 'imcchanlist' to get a list of IMC channels and
servers. Note that both are case sensitive.
"""
source_object = command.source_object
if not settings.IMC2_ENABLED:
s = """IMC is not enabled. You need to activate it in game/settings.py."""
source_object.emit_to(s)
return
args = command.command_argument
if not args or len(args.split()) != 2 :
source_object.emit_to("Usage: @imc2chan IMCServer:IMCchannel channel")
return
#identify the server-channel pair
imcdata, channel = args.split()
if not ":" in imcdata:
source_object.emit_to("You need to supply an IMC Server:Channel pair.")
return
imclist = IMC2_CHANLIST.get_channel_list()
imc_channels = filter(lambda c: c.name == imcdata, imclist)
if not imc_channels:
source_object.emit_to("IMC server and channel '%s' not found." % imcdata)
return
else:
imc_server_name, imc_channel_name = imcdata.split(":")
# Use 'imcchanlist' to get a list of IMC channels and
# servers. Note that both are case sensitive.
# """
# source_object = command.source_object
# if not settings.IMC2_ENABLED:
# s = """IMC is not enabled. You need to activate it in game/settings.py."""
# source_object.emit_to(s)
# return
# args = command.command_argument
# if not args or len(args.split()) != 2 :
# source_object.emit_to("Usage: @imc2chan IMCServer:IMCchannel channel")
# return
# #identify the server-channel pair
# imcdata, channel = args.split()
# if not ":" in imcdata:
# source_object.emit_to("You need to supply an IMC Server:Channel pair.")
# return
# imclist = IMC2_CHANLIST.get_channel_list()
# imc_channels = filter(lambda c: c.name == imcdata, imclist)
# if not imc_channels:
# source_object.emit_to("IMC server and channel '%s' not found." % imcdata)
# return
# else:
# imc_server_name, imc_channel_name = imcdata.split(":")
#find evennia channel
try:
chanobj = comsys.get_cobj_from_name(channel)
except CommChannel.DoesNotExist:
source_object.emit_to("Local channel '%s' not found (use real name, not alias)." % channel)
return
# #find evennia channel
# try:
# chanobj = comsys.get_cobj_from_name(channel)
# except CommChannel.DoesNotExist:
# source_object.emit_to("Local channel '%s' not found (use real name, not alias)." % channel)
# return
#create the mapping.
outstring = ""
mapping = IMC2ChannelMapping.objects.filter(channel__name=channel)
if mapping:
mapping = mapping[0]
outstring = "Replacing %s. New " % mapping
else:
mapping = IMC2ChannelMapping()
# #create the mapping.
# outstring = ""
# mapping = IMC2ChannelMapping.objects.filter(channel__name=channel)
# if mapping:
# mapping = mapping[0]
# outstring = "Replacing %s. New " % mapping
# else:
# mapping = IMC2ChannelMapping()
mapping.imc2_server_name = imc_server_name
mapping.imc2_channel_name = imc_channel_name
mapping.channel = chanobj
mapping.save()
outstring += "Mapping set: %s." % mapping
source_object.emit_to(outstring)
# mapping.imc2_server_name = imc_server_name
# mapping.imc2_channel_name = imc_channel_name
# mapping.channel = chanobj
# mapping.save()
# outstring += "Mapping set: %s." % mapping
# source_object.emit_to(outstring)
GLOBAL_CMD_TABLE.add_command("@imc2chan",cmd_IMC2chan,
priv_tuple=("imc2.admin_imc_channels",), help_category="Comms")
# GLOBAL_CMD_TABLE.add_command("@imc2chan",cmd_IMC2chan,
# priv_tuple=("imc2.admin_imc_channels",), help_category="Comms")

View file

@ -1,130 +0,0 @@
"""
IRC-related commands
"""
from twisted.application import internet
from django.conf import settings
from src.irc.connection import IRC_CHANNELS
from src.irc.models import IRCChannelMapping
from src import comsys
from src.cmdtable import GLOBAL_CMD_TABLE
from src.channels.models import CommChannel
def cmd_IRC2chan(command):
"""
@irc2chan - link irc to ingame channel
Usage:
@irc2chan <#IRCchannel> <local channel>
Links an IRC channel (including #) to an existing
evennia channel. You can link as many existing
evennia channels as you like to the
IRC channel this way. Running the command with an
existing mapping will re-map the channels.
"""
source_object = command.source_object
if not settings.IRC_ENABLED:
s = """IRC is not enabled. You need to activate it in game/settings.py."""
source_object.emit_to(s)
return
args = command.command_argument
if not args or len(args.split()) != 2 :
source_object.emit_to("Usage: @irc2chan IRCchannel channel")
return
irc_channel, channel = args.split()
if irc_channel not in [o.factory.channel for o in IRC_CHANNELS]:
source_object.emit_to("IRC channel '%s' not found." % irc_channel)
return
try:
chanobj = comsys.get_cobj_from_name(channel)
except CommChannel.DoesNotExist:
source_object.emit_to("Local channel '%s' not found (use real name, not alias)." % channel)
return
#create the mapping.
outstring = ""
mapping = IRCChannelMapping.objects.filter(channel__name=channel)
if mapping:
mapping = mapping[0]
outstring = "Replacing %s. New " % mapping
else:
mapping = IRCChannelMapping()
mapping.irc_server_name = settings.IRC_NETWORK
mapping.irc_channel_name = irc_channel
mapping.channel = chanobj
mapping.save()
outstring += "Mapping set: %s." % mapping
source_object.emit_to(outstring)
GLOBAL_CMD_TABLE.add_command("@irc2chan",cmd_IRC2chan,
priv_tuple=("irc.admin_irc_channels",),
help_category="Comms")
def cmd_IRCjoin(command):
"""
@ircjoin - join a new irc channel
Usage:
@ircjoin <#IRCchannel>
Attempts to connect a bot to a new IRC channel (don't forget that
IRC channels begin with a #).
The bot uses the connection details defined in the main settings.
Observe that channels added using this command does not survive a reboot.
"""
source_object = command.source_object
arg = command.command_argument
if not arg:
source_object.emit_to("Usage: @ircjoin #irc_channel")
return
channel = arg.strip()
if channel[0] != "#": channel = "#%s" % channel
if not settings.IRC_ENABLED:
source_object.emit_to("IRC services are not active. You need to turn them on in preferences.")
return
#direct creation of bot (do not add to services)
from src.irc.connection import connect_to_IRC
connect_to_IRC(settings.IRC_NETWORK,
settings.IRC_PORT,
channel, settings.IRC_NICKNAME)
# ---below should be checked so as to add subequent IRC bots to Services.
# it adds just fine, but the bot does not connect. /Griatch
# from src.irc.connection import IRC_BotFactory
# from src.server import mud_service
# irc = internet.TCPClient(settings.IRC_NETWORK,
# settings.IRC_PORT,
# IRC_BotFactory(channel,
# settings.IRC_NETWORK,
# settings.IRC_NICKNAME))
# irc.setName("%s:%s" % ("IRC",channel))
# irc.setServiceParent(mud_service.service_collection)
GLOBAL_CMD_TABLE.add_command("@ircjoin",cmd_IRCjoin,
priv_tuple=("irc.admin_irc_channels",),
help_category="Comms")
def cmd_IRCchanlist(command):
"""
ircchanlist
Usage:
ircchanlist
Lists all externally available IRC channels.
"""
source_object = command.source_object
s = "Available IRC channels:"
for c in IRC_CHANNELS:
s += "\n %s \t(nick '%s') on %s" % (c.factory.channel,
c.factory.nickname,
c.factory.network,)
source_object.emit_to(s)
GLOBAL_CMD_TABLE.add_command("ircchanlist", cmd_IRCchanlist,
help_category="Comms")

View file

@ -1,263 +0,0 @@
"""
These commands typically are to do with building or modifying Objects.
"""
from django.conf import settings
from src.permissions.permissions import has_perm, has_perm_string
from src.objects.models import ObjectDB, ObjAttribute
from game.gamesrc.commands.default.muxcommand import MuxCommand
from src.utils import create, utils
##
## def cmd_chown(command):
## """
## @chown - change ownerships
## Usage:
## @chown <Object> = <NewOwner>
## Changes the ownership of an object. The new owner specified must be a
## player object.
## """
## caller = command.caller
## if not command.command_argument:
## caller.msg("Usage: @chown <object> = <newowner>")
## return
## eq_args = command.command_argument.split('=', 1)
## target_name = eq_args[0]
## owner_name = eq_args[1]
## if len(target_name) == 0:
## caller.msg("Change the ownership of what?")
## return
## if len(eq_args) > 1:
## target_obj = caller.search_for_object(target_name)
## # Use search_for_object to handle duplicate/nonexistant results.
## if not target_obj:
## return
## if not caller.controls_other(target_obj) and not caller.has_perm("objects.admin_ownership"):
## caller.msg(defines_global.NOCONTROL_MSG)
## return
## owner_obj = caller.search_for_object(owner_name)
## # Use search_for_object to handle duplicate/nonexistant results.
## if not owner_obj:
## return
## if not owner_obj.is_player():
## caller.msg("Only players may own objects.")
## return
## if target_obj.is_player():
## caller.msg("You may not change the ownership of player objects.")
## return
## target_obj.set_owner(owner_obj)
## caller.msg("%s now owns %s." % (owner_obj, target_obj))
## else:
## # We haven't provided a target.
## caller.msg("Who should be the new owner of the object?")
## return
## GLOBAL_CMD_TABLE.add_command("@chown", cmd_chown, priv_tuple=("objects.modify_attributes",
## "objects.admin_ownership"),
## help_category="Building" )
#NOT VALID IN NEW SYSTEM!
## def cmd_lock(command):
## """
## @lock - limit use of objects
## Usage:
## @lock[/switch] <obj> [:type] [= <key>[,key2,key3,...]]
## Switches:
## add - add a lock (default) from object
## del - remove a lock from object
## list - view all locks on object (default)
## type:
## DefaultLock - the default lock type (default)
## UseLock - prevents usage of objects' commands
## EnterLock - blocking objects from entering the object
## Locks an object for everyone except those matching the keys.
## The keys can be of the following types (and searched in this order):
## - a user #dbref (#2, #45 etc)
## - a Group name (Builder, Immortal etc, case sensitive)
## - a Permission string (genperms.get, etc)
## - a Function():return_value pair. (ex: alliance():Red). The
## function() is called on the locked object (if it exists) and
## if its return value matches the Key is passed. If no
## return_value is given, matches against True.
## - an Attribute:return_value pair (ex: key:yellow_key). The
## Attribute is the name of an attribute defined on the locked
## object. If this attribute has a value matching return_value,
## the lock is passed. If no return_value is given,
## attributes will be searched, requiring a True
## value.
## If no keys at all are given, the object is locked for everyone.
## When the lock blocks a user, you may customize which error is given by
## storing error messages in an attribute. For DefaultLocks, UseLocks and
## EnterLocks, these attributes are called lock_msg, use_lock_msg and
## enter_lock_msg respectively.
## [[lock_types]]
## Lock types:
## Name: Affects: Effect:
## -----------------------------------------------------------------------
## DefaultLock: Exits: controls who may traverse the exit to
## its destination.
## Rooms: controls whether the player sees a failure
## message after the room description when
## looking at the room.
## Players/Things: controls who may 'get' the object.
## UseLock: All but Exits: controls who may use commands defined on
## the locked object.
## EnterLock: Players/Things: controls who may enter/teleport into
## the object.
## VisibleLock: Players/Things: controls if the object is visible to
## someone using the look command.
## Fail messages echoed to the player are stored in the attributes 'lock_msg',
## 'use_lock_msg', 'enter_lock_msg' and 'visible_lock_msg' on the locked object
## in question. If no such message is stored, a default will be used (or none at
## all in some cases).
## """
## caller = command.caller
## arg = command.command_argument
## switches = command.command_switches
## if not arg:
## caller.msg("Usage: @lock[/switch] <obj> [:type] [= <key>[,key2,key3,...]]")
## return
## keys = ""
## #deal with all possible arguments.
## try:
## lside, keys = arg.split("=",1)
## except ValueError:
## lside = arg
## lside, keys = lside.strip(), keys.strip()
## try:
## obj_name, ltype = lside.split(":",1)
## except:
## obj_name = lside
## ltype = "DefaultLock"
## obj_name, ltype = obj_name.strip(), ltype.strip()
## if ltype not in ["DefaultLock","UseLock","EnterLock","VisibleLock"]:
## caller.msg("Lock type '%s' not recognized." % ltype)
## return
## obj = caller.search_for_object(obj_name)
## if not obj:
## return
## obj_locks = obj.LOCKS
## if "list" in switches:
## if not obj_locks:
## s = "There are no locks on %s." % obj.name
## else:
## s = "Locks on %s:" % obj.name
## s += obj_locks.show()
## caller.msg(s)
## return
## # we are trying to change things. Check permissions.
## if not caller.controls_other(obj):
## caller.msg(defines_global.NOCONTROL_MSG)
## return
## if "del" in switches:
## # clear a lock
## if obj_locks:
## if not obj_locks.has_type(ltype):
## caller.msg("No %s set on this object." % ltype)
## else:
## obj_locks.del_type(ltype)
## obj.LOCKS = obj_locks
## caller.msg("Cleared lock %s on %s." % (ltype, obj.name))
## else:
## caller.msg("No %s set on this object." % ltype)
## return
## else:
## #try to add a lock
## if not obj_locks:
## obj_locks = locks.Locks()
## if not keys:
## #add an impassable lock
## obj_locks.add_type(ltype, locks.Key())
## caller.msg("Added impassable '%s' lock to %s." % (ltype, obj.name))
## else:
## keys = [k.strip() for k in keys.split(",")]
## obj_keys, group_keys, perm_keys = [], [], []
## func_keys, attr_keys = [], []
## allgroups = [g.name for g in Group.objects.all()]
## allperms = ["%s.%s" % (p.content_type.app_label, p.codename)
## for p in Permission.objects.all()]
## for key in keys:
## #differentiate different type of keys
## if Object.objects.is_dbref(key):
## # this is an object key, like #2, #6 etc
## obj_keys.append(key)
## elif key in allgroups:
## # a group key
## group_keys.append(key)
## elif key in allperms:
## # a permission string
## perm_keys.append(key)
## elif '()' in key:
## # a function()[:returnvalue] tuple.
## # Check if we also request a return value
## funcname, rvalue = [k.strip() for k in key.split('()',1)]
## if not funcname:
## funcname = "lock_func"
## rvalue = rvalue.lstrip(':')
## if not rvalue:
## rvalue = True
## # pack for later adding.
## func_keys.append((funcname, rvalue))
## elif ':' in key:
## # an attribute[:returnvalue] tuple.
## attr_name, rvalue = [k.strip() for k in key.split(':',1)]
## # pack for later adding
## attr_keys.append((attr_name, rvalue))
## else:
## caller.msg("Key '%s' is not recognized as a valid dbref, group or permission." % key)
## return
## # Create actual key objects from the respective lists
## keys = []
## if obj_keys:
## keys.append(locks.ObjKey(obj_keys))
## if group_keys:
## keys.append(locks.GroupKey(group_keys))
## if perm_keys:
## keys.append(locks.PermKey(perm_keys))
## if func_keys:
## keys.append(locks.FuncKey(func_keys, obj.dbref))
## if attr_keys:
## keys.append(locks.AttrKey(attr_keys))
## #store the keys in the lock
## obj_locks.add_type(ltype, keys)
## kstring = " "
## for key in keys:
## kstring += " %s," % key
## kstring = kstring[:-1]
## caller.msg("Added lock '%s' to %s with keys%s." % (ltype, obj.name, kstring))
## obj.LOCKS = obj_locks
## GLOBAL_CMD_TABLE.add_command("@lock", cmd_lock, priv_tuple=("objects.create",), help_category="Building")

444
src/comms/imc2.py Normal file
View file

@ -0,0 +1,444 @@
"""
IMC2 client module. Handles connecting to and communicating with an IMC2 server.
"""
from time import time
from twisted.application import internet
from twisted.internet import protocol
from twisted.conch import telnet
from django.conf import settings
from src.utils import logger, create, search, utils
from src.server.sessionhandler import SESSIONS
from src.scripts.scripts import Script
from src.comms.models import Channel, ExternalChannelConnection
from src.comms.imc2lib import imc2_packets as pck
from src.comms.imc2lib.imc2_trackers import IMC2MudList, IMC2ChanList
from src.comms.imc2lib.imc2_listeners import handle_whois_reply
# channel to send info to
INFOCHANNEL = Channel.objects.channel_search(settings.CHANNEL_MUDINFO[0])
# all linked channel connection
IMC2_CHANNELS = []
# IMC2 debug mode
IMC2_DEBUG = True
# Use this instance to keep track of the other games on the network.
IMC2_MUDLIST = IMC2MudList()
# Tracks the list of available channels on the network.
IMC2_CHANLIST = IMC2ChanList()
#
# Helper method
#
def msg_info(message):
"""
Send info to default info channel
"""
message = '[%s][IMC2]: %s' % (INFOCHANNEL[0].key, message)
try:
INFOCHANNEL[0].msg(message)
except AttributeError:
logger.log_infomsg("MUDinfo (imc2): %s" % message)
#
# Regular scripts
#
class Send_IsAlive(Script):
"""
Sends periodic keepalives to network neighbors. This lets the other
games know that our game is still up and connected to the network. Also
provides some useful information about the client game.
"""
def at_script_creation(self):
self.key = 'IMC2_Send_IsAlive'
self.interval = 900
self.desc = "Send an IMC2 is-alive packet"
self.persistent = True
def at_repeat(self):
for channel in IMC2_CHANNELS:
channel.send_packet(pck.IMC2PacketIsAlive())
def is_valid(self):
"Is only valid as long as there are channels to update"
return IMC2_CHANNELS
class Send_Keepalive_Request(Script):
"""
Event: Sends a keepalive-request to connected games in order to see who
is connected.
"""
def at_script_creation(self):
self.key = "IMC2_Send_Keepalive_Request"
self.interval = 3500
self.desc = "Send an IMC2 keepalive-request packet"
self.persistent = True
def at_repeat(self):
for channel in IMC2_CHANNELS:
channel.send_packet(pck.IMC2PacketKeepAliveRequest())
def is_valid(self):
"Is only valid as long as there are channels to update"
return IMC2_CHANNELS
class Prune_Inactive_Muds(Script):
"""
Prunes games that have not sent is-alive packets for a while. If
we haven't heard from them, they're probably not connected or don't
implement the protocol correctly. In either case, good riddance to them.
"""
def at_script_creation(self):
self.key = "IMC2_Prune_Inactive_Muds"
self.interval = 1800
self.desc = "Check IMC2 list for inactive games"
self.persistent = True
self.inactive_threshold = 3599
def at_repeat(self):
for name, mudinfo in IMC2_MUDLIST.mud_list.items():
if time() - mudinfo.last_updated > self.inactive_threshold:
del IMC2_MUDLIST.mud_list[name]
def is_valid(self):
"Is only valid as long as there are channels to update"
return IMC2_CHANNELS
class Sync_Server_Channel_List(Script):
"""
Re-syncs the network's channel list. This will
cause a cascade of reply packets of a certain type
from the network. These are handled by the protocol,
gradually updating the channel cache.
"""
def at_script_creation(self):
self.key = "IMC2_Sync_Server_Channel_List"
self.interval = 24 * 3600 # once every day
self.desc = "Re-sync IMC2 network channel list"
self.persistent = True
def at_repeat(self):
checked_networks = []
for channel in self.IMC2_CHANNELS:
network = channel.external_config.split['|'][0]
if not network in checked_networks:
channel.send_packet(pkg.IMC2PacketIceRefresh())
checked_networks.append(network)
#
# IMC2 protocol
#
class IMC2Protocol(telnet.StatefulTelnetProtocol):
"""
Provides the abstraction for the IMC2 protocol. Handles connection,
authentication, and all necessary packets.
"""
def __init__(self):
global IMC2_CHANNELS
IMC2_CHANNELS.append(self)
self.is_authenticated = False
self.auth_type = None
self.server_name = None
self.network_name = None
self.sequence = None
def connectionMade(self):
"""
Triggered after connecting to the IMC2 network.
"""
self.auth_type = "plaintext"
logger.log_infomsg("IMC2: Connected to network server.")
logger.log_infomsg("IMC2: Sending authentication packet.")
self.send_packet(pck.IMC2PacketAuthPlaintext())
def send_packet(self, packet):
"""
Given a sub-class of IMC2Packet, assemble the packet and send it
on its way to the IMC2 server.
Evennia -> IMC2
"""
if self.sequence:
# This gets incremented with every command.
self.sequence += 1
packet.imc2_protocol = self
packet_str = utils.to_str(packet.assemble(self.factory.mudname, self.factory.client_pwd, self.factory.server_pwd))
if IMC2_DEBUG:
logger.log_infomsg("IMC2: SENT> %s" % packet_str)
self.sendLine(packet_str)
def _parse_auth_response(self, line):
"""
Parses the IMC2 network authentication packet.
"""
if self.auth_type == "plaintext":
# Plain text passwords.
# SERVER Sends: PW <servername> <serverpw> version=<version#> <networkname>
if IMC2_DEBUG:
logger.log_infomsg("IMC2: AUTH< %s" % line)
line_split = line.split(' ')
pw_present = line_split[0] == 'PW'
autosetup_present = line_split[0] == 'autosetup'
if "reject" in line_split:
auth_message = "IMC2 server rejected connection."
logger.log_infomsg(auth_message)
msg_info(auth_message)
return
if pw_present:
self.server_name = line_split[1]
self.network_name = line_split[4]
elif autosetup_present:
logger.log_infomsg("IMC2: Autosetup response found.")
self.server_name = line_split[1]
self.network_name = line_split[3]
self.is_authenticated = True
self.sequence = int(time())
# Log to stdout and notify over MUDInfo.
auth_message = "Successfully authenticated to the '%s' network." % self.network_name
logger.log_infomsg('IMC2: %s' % auth_message)
msg_info(auth_message)
# Ask to see what other MUDs are connected.
self.send_packet(pck.IMC2PacketKeepAliveRequest())
# IMC2 protocol states that KeepAliveRequests should be followed
# up by the requester sending an IsAlive packet.
self.send_packet(pck.IMC2PacketIsAlive())
# Get a listing of channels.
self.send_packet(pck.IMC2PacketIceRefresh())
def _msg_evennia(self, packet):
"""
Handle the sending of packet data to Evennia channel
(Message from IMC2 -> Evennia)
"""
conn_name = packet.optional_data.get('channel', None)
# If the packet lacks the 'echo' key, don't bother with it.
has_echo = packet.optional_data.get('echo', None)
if conn_name and has_echo:
# The second half of this is the channel name: Server:Channel
chan_name = conn_name.split(':', 1)[1]
key = "imc2_%s" % conn_name
# Look for matching IMC2 channel maps.
conns = ExternalChannelConnection.objects.filter(db_external_key=self.factory.key)
if not conns:
return
# Format the message to send to local channel.
message = '[%s] %s@%s: %s' % (self.factory.evennia_channel, packet.sender, packet.origin, packet.optional_data.get('text'))
for conn in conns:
if conn.channel:
conn.to_channel(message)
def _format_tell(self, packet):
"""
Handle tells over IMC2 by formatting the text properly
"""
return "%s@%s IMC tells: %s" % (packet.sender, packet.origin,
packet.optional_data.get('text', 'ERROR: No text provided.'))
def lineReceived(self, line):
"""
Triggered when text is received from the IMC2 network. Figures out
what to do with the packet.
IMC2 -> Evennia
"""
if not self.is_authenticated:
self._parse_auth_response(line)
else:
if IMC2_DEBUG and not 'is-alive' in line:
# if IMC2_DEBUG mode is on, print the contents of the packet
# to stdout.
logger.log_infomsg("PACKET: %s" % line)
# Parse the packet and encapsulate it for easy access
packet = pck.IMC2Packet(self.factory.mudname, packet_str=line)
if IMC2_DEBUG and packet.packet_type not in ['is-alive', 'keepalive-request']:
# Print the parsed packet's __str__ representation.
# is-alive and keepalive-requests happen pretty frequently.
# Don't bore us with them in stdout.
logger.log_infomsg(str(packet))
# Figure out what kind of packet we're dealing with and hand it
# off to the correct handler.
if packet.packet_type == 'is-alive':
IMC2_MUDLIST.update_mud_from_packet(packet)
elif packet.packet_type == 'keepalive-request':
# Don't need to check the destination, we only receive these
# packets when they are intended for us.
self.send_packet(pck.IMC2PacketIsAlive())
elif packet.packet_type == 'ice-msg-b':
self._msg_evennia(packet)
elif packet.packet_type == 'whois-reply':
handle_whois_reply(packet)
elif packet.packet_type == 'close-notify':
IMC2_MUDLIST.remove_mud_from_packet(packet)
elif packet.packet_type == 'ice-update':
IMC2_CHANLIST.update_channel_from_packet(packet)
elif packet.packet_type == 'ice-destroy':
IMC2_CHANLIST.remove_channel_from_packet(packet)
elif packet.packet_type == 'tell':
player = search.players(packet.target)
if not player:
return
player.msg(self._format_tell(packet))
def msg_imc2(self, message, from_obj=None):
"""
Called by Evennia to send a message through the imc2 connection
"""
conns = ExternalChannelConnection.objects.filter(db_external_key=self.factory.key)
if not conns:
return
if from_obj:
if hasattr(from_obj, 'key'):
from_name = from_obj.key
else:
from_name = from_obj
else:
from_name = self.factory.mudname
# send the packet
self.send_packet(pck.IMC2PacketIceMsgBroadcasted(self.factory.network, self.factory.channel,
from_name, message))
class IMC2Factory(protocol.ClientFactory):
"""
Creates instances of the IMC2Protocol. Should really only ever create one
in our particular instance. Tied in via src/server.py.
"""
protocol = IMC2Protocol
def __init__(self, key, channel, network, port, mudname, client_pwd, server_pwd, evennia_channel):
self.key = key
self.mudname = mudname
self.channel = channel
self.pretty_key = "%s:%s/%s (%s)" % (network, port, channel, mudname)
self.network = network
self.protocol_version = '2'
self.client_pwd = client_pwd
self.server_pwd = server_pwd
self.evennia_channel = evennia_channel
def clientConnectionFailed(self, connector, reason):
message = 'Connection failed: %s' % reason.getErrorMessage()
msg_info(message)
logger.log_errmsg('IMC2: %s' % message)
def clientConnectionLost(self, connector, reason):
message = 'Connection lost: %s' % reason.getErrorMessage()
msg_info(message)
logger.log_errmsg('IMC2: %s' % message)
def build_connection_key(channel, imc2_network, imc2_port, imc2_channel, imc2_mudname):
"Build an id hash for the connection"
if hasattr(channel, 'key'):
channel = channel.key
return "imc2_%s:%s/%s(%s)<>%s" % (imc2_network, imc2_port, imc2_channel, imc2_mudname, channel)
def build_service_key(key):
return "IMC2:%s" % key
def start_scripts(validate=False):
"""
Start all the needed scripts
"""
if validate:
from src.utils import reloads
reloads.reload_scripts()
return
if not search.scripts("IMC2_Send_IsAlive"):
create.create_script(Send_IsAlive)
if not search.scripts("IMC2_Send_Keepalive_Request"):
create.create_script(Send_Keepalive_Request)
if not search.scripts("IMC2_Prune_Inactive_Muds"):
create.create_script(Prune_Inactive_Muds)
if not search.scripts("IMC2_Sync_Server_Channel_List"):
create.create_script(Sync_Server_Channel_List)
def create_connection(channel, imc2_network, imc2_port, imc2_channel, imc2_mudname, imc2_client_pwd, imc2_server_pwd):
"""
This will create a new IMC2<->channel connection.
"""
if not type(channel) == Channel:
new_channel = Channel.objects.filter(db_key=channel)
if not new_channel:
logger.log_errmsg("Cannot attach IMC2<->Evennia: Evennia Channel '%s' not found" % channel)
return False
channel = new_channel[0]
key = build_connection_key(channel, imc2_network, imc2_port, imc2_channel, imc2_mudname)
old_conns = ExternalChannelConnection.objects.filter(db_external_key=key)
if old_conns:
return False
config = "%s|%s|%s|%s|%s|%s" % (imc2_network, imc2_port, imc2_channel, imc2_mudname, imc2_client_pwd, imc2_server_pwd)
# how the channel will be able to contact this protocol
send_code = "from src.comms.imc2 import IMC2_CHANNELS\n"
send_code += "matched_imc2s = [imc2 for imc2 in IMC2_CHANNELS if imc2.factory.key == '%s']\n" % key
send_code += "[imc2.msg_imc2(message, from_obj=from_obj) for imc2 in matched_imc2s]\n"
conn = ExternalChannelConnection(db_channel=channel, db_external_key=key, db_external_send_code=send_code,
db_external_config=config)
conn.save()
# connect
connect_to_imc2(conn)
# start scripts (if needed)
start_scripts()
return True
def delete_connection(channel, imc2_network, imc2_port, imc2_channel, mudname):
"Destroy a connection"
if hasattr(channel, 'key'):
channel = channel.key
key = build_connection_key(channel, imc2_network, imc2_port, imc2_channel, mudname)
service_key = build_service_key(key)
try:
conn = ExternalChannelConnection.objects.get(db_external_key=key)
except Exception:
return False
conn.delete()
try:
service = SESSIONS.server.services.getServiceNamed(service_key)
except Exception:
return True
if service.running:
SESSIONS.server.services.removeService(service)
# validate scripts
start_scripts(validate=True)
return True
def connect_to_imc2(connection):
"Create the imc instance and connect to the IMC2 network and channel."
# get config
key = utils.to_str(connection.external_key)
service_key = build_service_key(key)
imc2_network, imc2_port, imc2_channel, imc2_mudname, imc2_client_pwd, imc2_server_pwd = \
[utils.to_str(conf) for conf in connection.external_config.split('|')]
# connect
imc = internet.TCPClient(imc2_network, int(imc2_port), IMC2Factory(key, imc2_channel, imc2_network, imc2_port, imc2_mudname,
imc2_client_pwd, imc2_server_pwd, connection.channel.key))
imc.setName(service_key)
SESSIONS.server.services.addService(imc)
def connect_all():
"""
Activate all imc2 bots.
Returns a list of (key, TCPClient) tuples for server to properly set services.
"""
connections = ExternalChannelConnection.objects.filter(db_external_key__startswith='imc2_')
for connection in connections:
connect_to_imc2(connection)
if connections:
start_scripts()

View file

@ -1,12 +1,13 @@
"""
ANSI parser - this adds colour to text according to
special markup strings.
This is a IMC2 complacent version.
"""
from src.utils import ansi
from ansi import BaseParser, ANSITable
from src.utils.ansi import ANSIParser, ANSITable
class IMCANSIParser(BaseParser):
class IMCANSIParser(ANSIParser):
"""
This parser is per the IMC2 specification.
"""

View file

@ -1,13 +1,12 @@
"""
This module handles some of the -reply packets like whois-reply.
"""
#TODO: This is deprecated!
from src.objects.models import Object
from src.imc2 import imc_ansi
from src.objects.models import ObjectDB
from src.comms.imc2lib import imc2_ansi
def handle_whois_reply(packet):
try:
pobject = Object.objects.get(id=packet.target)
pobject = ObjectDB.objects.get(id=packet.target)
response_text = imc_ansi.parse_ansi(packet.optional_data.get('text',
'Unknown'))
pobject.emit_to('Whois reply from %s: %s' % (packet.origin,

View file

@ -1,13 +1,10 @@
"""
IMC2 packets. These are pretty well documented at:
http://www.mudbytes.net/index.php?a=articles&s=imc2_protocol
"""
import shlex
if __name__ == "__main__":
class settings:
IMC2_MUDNAME = "BLAH"
else:
from django.conf import settings
from django.conf import settings
class Lexxer(shlex.shlex):
"""
@ -26,16 +23,18 @@ class IMC2Packet(object):
Base IMC2 packet class. This is generally sub-classed, aside from using it
to parse incoming packets from the IMC2 network server.
"""
def __init__(self, packet_str=None):
def __init__(self, mudname=None, packet_str=None):
"""
Optionally, parse a packet and load it up.
"""
# The following fields are all according to the basic packet format of:
# <sender>@<origin> <sequence> <route> <packet-type> <target>@<destination> <data...>
self.sender = None
self.origin = settings.IMC2_MUDNAME
if not mudname:
mudname = settings.SERVERNAME
self.origin = mudname
self.sequence = None
self.route = settings.IMC2_MUDNAME
self.route = mudname
self.packet_type = None
self.target = None
self.destination = None
@ -153,9 +152,11 @@ class IMC2Packet(object):
# None value. Do something or other.
return 'Unknown'
def assemble(self):
def assemble(self, mudname=None, client_pwd=None, server_pwd=None):
"""
Assembles the packet and returns the ready-to-send string.
Note that the arguments are not used, they are there for
consistency across all packets.
"""
self.sequence = self.imc2_protocol.sequence
packet = "%s@%s %s %s %s %s@%s %s\n" % (
@ -184,17 +185,13 @@ class IMC2PacketAuthPlaintext(object):
client will be expected in SHA256-AUTH format if the server
supports it.
"""
def assemble(self):
def assemble(self, mudname=None, client_pwd=None, server_pwd=None):
"""
This is one of two strange packets, just assemble the packet manually
and go.
"""
return 'PW %s %s version=%s autosetup %s\n' %(
settings.IMC2_MUDNAME,
settings.IMC2_CLIENT_PW,
settings.IMC2_PROTOCOL_VERSION,
settings.IMC2_SERVER_PW)
return 'PW %s %s version=2 autosetup %s\n' %(mudname, client_pwd, server_pwd)
class IMC2PacketKeepAliveRequest(IMC2Packet):
"""
Description:
@ -260,7 +257,7 @@ class IMC2PacketIsAlive(IMC2Packet):
self.target = '*'
self.destination = '*'
self.optional_data = {'versionid': 'Evennia IMC2',
'url': '"http://evennia.com"',
'url': '"http://www.evennia.com"',
'host': 'test.com',
'port': '5555'}
@ -755,3 +752,4 @@ if __name__ == "__main__":
packstr = "Kayle@MW 1234567 MW!Server02!Server01 ice-msg-b *@* channel=Server01:ichat text=\"*they're going woot\" emote=0 echo=1"
packstr = "*@Lythelian 1234567 Lythelian!Server01 is-alive *@* versionid=\"Tim's LPC IMC2 client 30-Jan-05 / Dead Souls integrated\" networkname=Mudbytes url=http://dead-souls.net host=70.32.76.142 port=6666 sha256=0"
print IMC2Packet(packstr)

View file

@ -102,8 +102,3 @@ class IMC2ChanList(object):
except KeyError:
# No matching entry, no big deal.
pass
# Use this instance to keep track of the other games on the network.
IMC2_MUDLIST = IMC2MudList()
# Tracks the list of available channels on the network.
IMC2_CHANLIST = IMC2ChanList()

View file

@ -36,12 +36,12 @@ class IRC_Bot(irc.IRCClient):
nickname = property(_get_nickname)
def signedOn(self):
global IRC_CHANNELS
self.join(self.factory.channel)
# This is the first point the protocol is instantiated.
# add this protocol instance to the global list so we
# can access it later to send data.
global IRC_CHANNELS
self.join(self.factory.channel)
IRC_CHANNELS.append(self)
#msg_info("Client connecting to %s.'" % (self.factory.channel))
@ -78,7 +78,7 @@ class IRC_Bot(irc.IRCClient):
"""
self.msg(utils.to_str(self.factory.channel), utils.to_str(msg))
class Factory(protocol.ClientFactory):
class IRCbotFactory(protocol.ClientFactory):
protocol = IRC_Bot
def __init__(self, key, channel, network, port, nickname, evennia_channel):
self.key = key
@ -101,9 +101,11 @@ class Factory(protocol.ClientFactory):
msg_info(msg)
logger.log_errmsg(msg)
def build_connection_key(irc_network, irc_port, irc_channel, irc_bot_nick):
def build_connection_key(channel, irc_network, irc_port, irc_channel, irc_bot_nick):
"Build an id hash for the connection"
return "irc_%s:%s%s(%s)" % (irc_network, irc_port, irc_channel, irc_bot_nick)
if hasattr(channel, 'key'):
channel = channel.key
return "irc_%s:%s%s(%s)<>%s" % (irc_network, irc_port, irc_channel, irc_bot_nick, channel)
def build_service_key(key):
return "IRCbot:%s" % key
@ -118,7 +120,7 @@ def create_connection(channel, irc_network, irc_port, irc_channel, irc_bot_nick)
logger.log_errmsg("Cannot attach IRC<->Evennia: Evennia Channel '%s' not found" % channel)
return False
channel = new_channel[0]
key = build_connection_key(irc_network, irc_port, irc_channel, irc_bot_nick)
key = build_connection_key(channel, irc_network, irc_port, irc_channel, irc_bot_nick)
old_conns = ExternalChannelConnection.objects.filter(db_external_key=key)
if old_conns:
@ -136,9 +138,12 @@ def create_connection(channel, irc_network, irc_port, irc_channel, irc_bot_nick)
connect_to_irc(conn)
return True
def delete_connection(irc_network, irc_port, irc_channel, irc_bot_nick):
def delete_connection(channel, irc_network, irc_port, irc_channel, irc_bot_nick):
"Destroy a connection"
key = build_connection_key(irc_network, irc_port, irc_channel, irc_bot_nick)
if hasattr(channel, 'key'):
channel = channel.key
key = build_connection_key(channel, irc_network, irc_port, irc_channel, irc_bot_nick)
service_key = build_service_key(key)
try:
conn = ExternalChannelConnection.objects.get(db_external_key=key)
@ -161,7 +166,7 @@ def connect_to_irc(connection):
service_key = build_service_key(key)
irc_network, irc_port, irc_channel, irc_bot_nick = [utils.to_str(conf) for conf in connection.external_config.split('|')]
# connect
bot = internet.TCPClient(irc_network, int(irc_port), Factory(key, irc_channel, irc_network, irc_port, irc_bot_nick,
bot = internet.TCPClient(irc_network, int(irc_port), IRCbotFactory(key, irc_channel, irc_network, irc_port, irc_bot_nick,
connection.channel.key))
bot.setName(service_key)
SESSIONS.server.services.addService(bot)

View file

@ -1,7 +0,0 @@
from src.imc2.models import IMC2ChannelMapping
from django.contrib import admin
class IMC2ChannelMappingAdmin(admin.ModelAdmin):
list_display = ('channel', 'imc2_server_name',
'imc2_channel_name', 'is_enabled')
admin.site.register(IMC2ChannelMapping, IMC2ChannelMappingAdmin)

View file

@ -1,201 +0,0 @@
"""
IMC2 client module. Handles connecting to and communicating with an IMC2 server.
"""
import telnetlib
from time import time
from twisted.internet.protocol import ClientFactory
from twisted.protocols.basic import LineReceiver
from twisted.internet import reactor, task
from twisted.conch.telnet import StatefulTelnetProtocol
from django.conf import settings
from src.utils import logger
from src.server import sessionhandler
from src.imc2.packets import *
from src.imc2.trackers import *
from src.imc2 import reply_listener
from src.imc2.models import IMC2ChannelMapping
#from src import comsys
# The active instance of IMC2Protocol. Set at server startup.
IMC2_PROTOCOL_INSTANCE = None
def cemit_info(message):
"""
Channel emits info to the appropriate info channel. By default, this
is MUDInfo.
"""
comsys.send_cmessage(settings.COMMCHAN_IMC2_INFO, 'IMC: %s' % message,
from_external="IMC2")
class IMC2Protocol(StatefulTelnetProtocol):
"""
Provides the abstraction for the IMC2 protocol. Handles connection,
authentication, and all necessary packets.
"""
def __init__(self):
message = "Client connecting to %s:%s..." % (
settings.IMC2_SERVER_ADDRESS,
settings.IMC2_SERVER_PORT)
logger.log_infomsg('IMC2: %s' % message)
cemit_info(message)
global IMC2_PROTOCOL_INSTANCE
IMC2_PROTOCOL_INSTANCE = self
self.is_authenticated = False
self.auth_type = None
self.server_name = None
self.network_name = None
self.sequence = None
def connectionMade(self):
"""
Triggered after connecting to the IMC2 network.
"""
logger.log_infomsg("IMC2: Connected to network server.")
self.auth_type = "plaintext"
logger.log_infomsg("IMC2: Sending authentication packet.")
self.send_packet(IMC2PacketAuthPlaintext())
def send_packet(self, packet):
"""
Given a sub-class of IMC2Packet, assemble the packet and send it
on its way.
"""
if self.sequence:
# This gets incremented with every command.
self.sequence += 1
packet.imc2_protocol = self
packet_str = str(packet.assemble())
#logger.log_infomsg("IMC2: SENT> %s" % packet_str)
self.sendLine(packet_str)
def _parse_auth_response(self, line):
"""
Parses the IMC2 network authentication packet.
"""
if self.auth_type == "plaintext":
"""
SERVER Sends: PW <servername> <serverpw> version=<version#> <networkname>
"""
line_split = line.split(' ')
pw_present = line_split[0] == 'PW'
autosetup_present = line_split[0] == 'autosetup'
if pw_present:
self.server_name = line_split[1]
self.network_name = line_split[4]
elif autosetup_present:
logger.log_infomsg("IMC2: Autosetup response found.")
self.server_name = line_split[1]
self.network_name = line_split[3]
self.is_authenticated = True
self.sequence = int(time())
# Log to stdout and notify over MUDInfo.
auth_message = "Successfully authenticated to the '%s' network." % self.network_name
logger.log_infomsg('IMC2: %s' % auth_message)
cemit_info(auth_message)
# Ask to see what other MUDs are connected.
self.send_packet(IMC2PacketKeepAliveRequest())
# IMC2 protocol states that KeepAliveRequests should be followed
# up by the requester sending an IsAlive packet.
self.send_packet(IMC2PacketIsAlive())
# Get a listing of channels.
self.send_packet(IMC2PacketIceRefresh())
def _handle_channel_mappings(self, packet):
"""
Received a message. Look for an IMC2 channel mapping and
route it accordingly.
"""
chan_name = packet.optional_data.get('channel', None)
# If the packet lacks the 'echo' key, don't bother with it.
has_echo = packet.optional_data.get('echo', None)
if chan_name and has_echo:
# The second half of this is the channel name: Server:Channel
chan_name = chan_name.split(':', 1)[1]
try:
# Look for matching IMC2 channel maps.
mappings = IMC2ChannelMapping.objects.filter(imc2_channel_name=chan_name)
# Format the message to cemit to the local channel.
message = '%s@%s: %s' % (packet.sender,
packet.origin,
packet.optional_data.get('text'))
# Bombs away.
for mapping in mappings:
if mapping.channel:
comsys.send_cmessage(mapping.channel, message,
from_external="IMC2")
except IMC2ChannelMapping.DoesNotExist:
# No channel mapping found for this message, ignore it.
pass
def lineReceived(self, line):
"""
Triggered when text is received from the IMC2 network. Figures out
what to do with the packet.
"""
if not self.is_authenticated:
self._parse_auth_response(line)
else:
if settings.IMC2_DEBUG and 'is-alive' not in line:
# if IMC2_DEBUG mode is on, print the contents of the packet
# to stdout.
logger.log_infomsg("PACKET: %s" % line)
# Parse the packet and encapsulate it for easy access
packet = IMC2Packet(packet_str = line)
if settings.IMC2_DEBUG and packet.packet_type not in ['is-alive', 'keepalive-request']:
# Print the parsed packet's __str__ representation.
# is-alive and keepalive-requests happen pretty frequently.
# Don't bore us with them in stdout.
logger.log_infomsg(packet)
"""
Figure out what kind of packet we're dealing with and hand it
off to the correct handler.
"""
if packet.packet_type == 'is-alive':
IMC2_MUDLIST.update_mud_from_packet(packet)
elif packet.packet_type == 'keepalive-request':
# Don't need to check the destination, we only receive these
# packets when they are intended for us.
self.send_packet(IMC2PacketIsAlive())
elif packet.packet_type == 'ice-msg-b':
self._handle_channel_mappings(packet)
elif packet.packet_type == 'whois-reply':
reply_listener.handle_whois_reply(packet)
elif packet.packet_type == 'close-notify':
IMC2_MUDLIST.remove_mud_from_packet(packet)
elif packet.packet_type == 'ice-update':
IMC2_CHANLIST.update_channel_from_packet(packet)
elif packet.packet_type == 'ice-destroy':
IMC2_CHANLIST.remove_channel_from_packet(packet)
elif packet.packet_type == 'tell':
sessions = sessionhandler.find_sessions_from_username(packet.target)
for session in sessions:
session.msg("%s@%s IMC tells: %s" %
(packet.sender,
packet.origin,
packet.optional_data.get('text',
'ERROR: No text provided.')))
class IMC2ClientFactory(ClientFactory):
"""
Creates instances of the IMC2Protocol. Should really only ever create one
in our particular instance. Tied in via src/server.py.
"""
protocol = IMC2Protocol
def clientConnectionFailed(self, connector, reason):
message = 'Connection failed: %s' % reason.getErrorMessage()
cemit_info(message)
logger.log_errmsg('IMC2: %s' % message)
def clientConnectionLost(self, connector, reason):
message = 'Connection lost: %s' % reason.getErrorMessage()
cemit_info(message)
logger.log_errmsg('IMC2: %s' % message)

View file

@ -1,87 +0,0 @@
"""
This module contains all IMC2 events that are triggered periodically.
Most of these are used to maintain the existing connection and keep various
lists/caches up to date.
"""
# TODO: This is deprecated!
from time import time
#from src import events
#from src import scheduler
from src.imc2 import connection as imc2_conn
from src.imc2.packets import *
from src.imc2.trackers import IMC2_MUDLIST
class IEvt_IMC2_Send_IsAlive(events.IntervalEvent):
"""
Event: Send periodic keepalives to network neighbors. This lets the other
games know that our game is still up and connected to the network. Also
provides some useful information about the client game.
"""
def __init__(self):
super(IEvt_IMC2_Send_IsAlive, self).__init__()
self.name = 'IEvt_IMC2_Send_IsAlive'
# Send keep-alive packets every 15 minutes.
self.interval = 900
self.description = "Send an IMC2 is-alive packet."
def event_function(self):
"""
This is the function that is fired every self.interval seconds.
"""
try:
imc2_conn.IMC2_PROTOCOL_INSTANCE.send_packet(IMC2PacketIsAlive())
except AttributeError:
#this will happen if we are not online in the first place
#(like during development) /Griatch
pass
class IEvt_IMC2_Send_Keepalive_Request(events.IntervalEvent):
"""
Event: Sends a keepalive-request to connected games in order to see who
is connected.
"""
def __init__(self):
super(IEvt_IMC2_Send_Keepalive_Request, self).__init__()
self.name = 'IEvt_IMC2_Send_Keepalive_Request'
self.interval = 3500
self.description = "Send an IMC2 keepalive-request packet."
def event_function(self):
"""
This is the function that is fired every self.interval seconds.
"""
imc2_conn.IMC2_PROTOCOL_INSTANCE.send_packet(IMC2PacketKeepAliveRequest())
class IEvt_IMC2_Prune_Inactive_Muds(events.IntervalEvent):
"""
Event: Prunes games that have not sent is-alive packets for a while. If
we haven't heard from them, they're probably not connected or don't
implement the protocol correctly. In either case, good riddance to them.
"""
def __init__(self):
super(IEvt_IMC2_Prune_Inactive_Muds, self).__init__()
self.name = 'IEvt_IMC2_Prune_Inactive_Muds'
# Check every 30 minutes.
self.interval = 1800
self.description = "Check IMC2 list for inactive games."
# Threshold for game inactivity (in seconds).
self.inactive_thresh = 3599
def event_function(self):
"""
This is the function that is fired every self.interval seconds.
"""
for name, mudinfo in IMC2_MUDLIST.mud_list.items():
# If we haven't heard from the game within our threshold time,
# we assume that they're dead.
if time() - mudinfo.last_updated > self.inactive_thresh:
del IMC2_MUDLIST.mud_list[name]
def add_events():
"""
Adds the IMC2 events to the scheduler.
"""
scheduler.add_event(IEvt_IMC2_Send_IsAlive())
scheduler.add_event(IEvt_IMC2_Prune_Inactive_Muds())
scheduler.add_event(IEvt_IMC2_Send_Keepalive_Request())

View file

@ -1,21 +0,0 @@
from django.db import models
from django.conf import settings
from src.comms.models import Channel
class IMC2ChannelMapping(models.Model):
"""
Each IMC2ChannelMapping object determines which in-game channel incoming
IMC2 messages are routed to.
"""
channel = models.ForeignKey(Channel)
imc2_server_name = models.CharField(max_length=78)
imc2_channel_name = models.CharField(max_length=78)
is_enabled = models.BooleanField(default=True)
class Meta:
verbose_name = "IMC2 Channel mapping"
verbose_name_plural = "IMC2 Channel mappings"
#permissions = settings.PERM_IMC2
def __str__(self):
return "%s <-> %s" % (self.channel, self.imc2_channel_name)

View file

@ -169,7 +169,7 @@ class LockHandler(object):
elif hasattr(self.obj, 'msg'):
self.obj.msg(message)
else:
logger.log_trace("%s: %s" % (self.obj, message))
logger.log_errmsg("%s: %s" % (self.obj, message))
def _parse_lockstring(self, storage_lockstring):
"""
@ -230,7 +230,8 @@ class LockHandler(object):
def add(self, lockstring, log_obj=None):
"""
Add a new, single lockstring on the form '<access_type>:<functions>'
Add a new lockstring on the form '<access_type>:<functions>'. Multiple
access types should be separated by semicolon (;).
If log_obj is given, it will be fed error information.
"""

View file

@ -250,7 +250,7 @@ class PlayerDB(TypedObject):
from_obj.at_msg_send(outgoing_string, to_obj=self, data=data)
except Exception:
pass
if self.character:
if object.__getattribute__(self, "character"):
if self.character.at_msg_receive(outgoing_string, from_obj=from_obj, data=data):
for session in object.__getattribute__(self, 'sessions'):
session.msg(outgoing_string, data)

View file

@ -174,6 +174,9 @@ class Evennia(object):
#
#------------------------------------------------------------
# Tell the system the server is starting up; some things are not available yet
ServerConfig.objects.conf("server_starting_mode", True)
# twistd requires us to define the variable 'application' so it knows
# what to execute from.
application = service.Application('Evennia')
@ -225,24 +228,19 @@ if WEBSERVER_ENABLED:
webserver.setName('EvenniaWebServer%s' % port)
EVENNIA.services.addService(webserver)
if IMC2_ENABLED:
# IMC2 channel connections
from src.imc2.connection import IMC2ClientFactory
from src.imc2 import events as imc2_events
imc2_factory = IMC2ClientFactory()
svc = internet.TCPClient(settings.IMC2_SERVER_ADDRESS,
settings.IMC2_SERVER_PORT,
imc2_factory)
svc.setName('IMC2')
EVENNIA.services.addService(svc)
imc2_events.add_events()
if IRC_ENABLED:
# IRC channel connections
from src.comms import irc
irc.connect_all()
if IMC2_ENABLED:
# IMC2 channel connections
from src.comms import imc2
imc2.connect_all()
# clear server startup mode
ServerConfig.objects.conf("server_starting_mode", delete=True)

View file

@ -10,7 +10,7 @@ sessions etc.
from twisted.conch.telnet import StatefulTelnetProtocol
from django.conf import settings
from src.server import session
from src.utils import ansi, utils
from src.utils import ansi, utils, logger
ENCODINGS = settings.ENCODINGS
@ -107,47 +107,21 @@ class TelnetProtocol(StatefulTelnetProtocol, session.Session):
"""
Data Evennia -> Player access hook. 'data' argument is ignored.
"""
if self.encoding:
try:
string = utils.to_str(string, encoding=self.encoding)
self.lineSend(ansi.parse_ansi(string, strip_ansi=not self.telnet_markup))
return
except Exception:
pass
# malformed/wrong encoding defined on player - try some defaults
for encoding in ENCODINGS:
try:
string = utils.to_str(string, encoding=encoding)
err = None
break
except Exception, e:
err = str(e)
continue
if err:
self.lineSend(err)
else:
self.lineSend(ansi.parse_ansi(string, strip_ansi=not self.telnet_markup))
try:
string = utils.to_str(string, encoding=self.encoding)
except Exception, e:
self.lineSend(str(e))
return
self.lineSend(ansi.parse_ansi(string, strip_ansi=not self.telnet_markup))
def at_data_in(self, string, data=None):
"""
Line from Player -> Evennia. 'data' argument is not used.
"""
if self.encoding:
try:
string = utils.to_unicode(string, encoding=self.encoding)
self.execute_cmd(string)
return
except Exception, e:
err = str(e)
print err
# malformed/wrong encoding defined on player - try some defaults
for encoding in ENCODINGS:
try:
string = utils.to_unicode(string, encoding=encoding)
err = None
break
except Exception, e:
err = str(e)
continue
self.execute_cmd(string)
try:
string = utils.to_unicode(string, encoding=self.encoding)
self.execute_cmd(string)
return
except Exception, e:
logger.log_errmsg(str(e))

View file

@ -26,7 +26,7 @@ from django.utils import simplejson
from django.utils.functional import Promise
from django.utils.encoding import force_unicode
from django.conf import settings
from src.utils import utils
from src.utils import utils, logger
from src.utils.text2html import parse_html
from src.server import session
from src.server.sessionhandler import SESSIONS
@ -258,27 +258,13 @@ class WebClientSession(session.Session):
pass
# string handling is similar to telnet
if self.encoding:
try:
string = utils.to_str(string, encoding=self.encoding)
self.client.lineSend(self.suid, parse_html(string))
return
except Exception:
pass
# malformed/wrong encoding defined on player - try some defaults
for encoding in ENCODINGS:
try:
string = utils.to_str(string, encoding=encoding)
err = None
break
except Exception, e:
err = str(e)
continue
if err:
self.client.lineSend(self.suid, err)
else:
#self.client.lineSend(self.suid, ansi.parse_ansi(string, strip_ansi=True))
try:
string = utils.to_str(string, encoding=self.encoding)
self.client.lineSend(self.suid, parse_html(string))
return
except Exception, e:
logger.log_trace()
def at_data_in(self, string, data=None):
"""
Input from Player -> Evennia (called by client protocol).
@ -290,22 +276,9 @@ class WebClientSession(session.Session):
pass
# the string part is identical to telnet
if self.encoding:
try:
string = utils.to_unicode(string, encoding=self.encoding)
self.execute_cmd(string)
return
except Exception, e:
err = str(e)
print err
# malformed/wrong encoding defined on player - try some defaults
for encoding in ENCODINGS:
try:
string = utils.to_unicode(string, encoding=encoding)
err = None
break
except Exception, e:
err = str(e)
continue
self.execute_cmd(string)
try:
string = utils.to_unicode(string, encoding=self.encoding)
self.execute_cmd(string)
return
except Exception, e:
logger.log_trace()

View file

@ -19,8 +19,7 @@ import os
# Evennia base server config
###################################################
# This is the name of your server and/or site.
# Can be anything.
# This is the name of your game. Make it catchy!
SERVERNAME = "Evennia"
# Activate telnet service
TELNET_ENABLED = True
@ -95,11 +94,8 @@ IDLE_COMMAND = "idle"
# given, this list is tried, in order, aborting on the first match.
# Add sets for languages/regions your players are likely to use.
# (see http://en.wikipedia.org/wiki/Character_encoding)
ENCODINGS = ["utf-8", "latin-1", "ISO-8859-1"]
# The module holding text strings for the connection screen.
# This module should contain one ore more variables
# with strings defining the look of the screen.
CONNECTION_SCREEN_MODULE = "game.gamesrc.world.connection_screens"
ENCODINGS = ["utf-8", "latin-1", "ISO-8859-1"]
###################################################
# Evennia Database config
@ -155,6 +151,10 @@ ALTERNATE_OBJECT_SEARCH_ERROR_HANDLER = ""
# objects without using dbrefs). (If not set, uses
# src.objects.object_search_funcs.object_multimatch_parser).
ALTERNATE_OBJECT_SEARCH_MULTIMATCH_PARSER = ""
# The module holding text strings for the connection screen.
# This module should contain one or more variables
# with strings defining the look of the screen.
CONNECTION_SCREEN_MODULE = "game.gamesrc.world.connection_screens"
###################################################
# Default command sets
@ -209,9 +209,9 @@ BASE_BATCHPROCESS_PATH = 'game.gamesrc.world'
###################################################
# You don't actually have to use this, but it affects the routines in
# src.utils.gametime.py and allows for a convenient measure to determine the
# current in-game time. You can of course read "week", "month" etc
# as your own in-game time units as desired.
# src.utils.gametime.py and allows for a convenient measure to
# determine the current in-game time. You can of course read "week",
# "month" etc as your own in-game time units as desired.
#The time factor dictates if the game world runs faster (timefactor>1)
# or slower (timefactor<1) than the real world.
@ -229,9 +229,9 @@ TIME_MONTH_PER_YEAR = 12
###################################################
# In-Game access
###################################################
# The access hiearchy, in climbing order. A higher
# permission in the hierarchy includes access of all
# levels below it.
# The access hiearchy, in climbing order. A higher permission in the
# hierarchy includes access of all levels below it.
PERMISSION_HIERARCHY = ("Players","PlayerHelpers","Builders", "Wizards", "Immortals")
# The default permission given to all new players
PERMISSION_PLAYER_DEFAULT = "Players"
@ -242,6 +242,7 @@ LOCK_FUNC_MODULES = ("src.locks.lockfuncs",)
###################################################
# In-game Channels created from server start
###################################################
# Defines a dict with one key for each from-start
# channel. Each key points to a tuple containing
# (name, aliases, description, locks)
@ -262,47 +263,29 @@ CHANNEL_CONNECTINFO = ("MUDconnections", ('connections, mud_conns'),
# External Channel connections
###################################################
# Note: You do *not* have to make your MUD open to
# the public to use the external connections, they
# operate as long as you have an internet connection,
# just like stand-alone chat clients.
# Evennia can connect to external IRC channels and
# echo what is said on the channel to IRC and vice
# versa. Obs - make sure the IRC network allows bots.
# If disabled, the default @irc2chan command won't be
# available in-game.
IRC_ENABLED = True
# OBS: IMC is not implemented at this point!
# IMC (Inter-MUD communication) allows for an evennia chat channel
# that connects to people on other MUDs also using the IMC. Your
# evennia server do *not* have to be open to the public to use IMC; it
# works as a stand-alone chat client. Evennia's IMC2 client was
# developed against MudByte's network. You must register your MUD on
# the network before you can use it, go to
# http://www.mudbytes.net/imc2-intermud-join-network. Choose 'Other
# unsupported IMC2 version' from the choices and and enter your
# information there. You have to enter the same 'short mud name',
# 'client password' and 'server password' as you define in this file.
# The Evennia discussion channel is on server02.mudbytes.net:9000.
# Change to True if you want IMC active at all.
IMC2_ENABLED = False
# Which channel to tie to the imc2 connection
COMMCHAN_IMC2_INFO = 'MUDInfo'
# The hostname/ip address of your IMC2 server of choice.
IMC2_SERVER_ADDRESS = 'server02.mudbytes.net'
#IMC2_SERVER_ADDRESS = None
# The port to connect to on your IMC2 server.
IMC2_SERVER_PORT = 9000
#IMC2_SERVER_PORT = None
# This is your game's IMC2 name on the network (e.g. "MyMUD").
IMC2_MUDNAME = None
# Your IMC2 client-side password. Used to authenticate with your network.
IMC2_CLIENT_PW = None
# Your IMC2 server-side password. Used to verify your network's identity.
IMC2_SERVER_PW = None
# Emit additional debugging info to log.
IMC2_DEBUG = False
# This isn't something you should generally change.
IMC2_PROTOCOL_VERSION = '2'
# When enabled, command @irc2chan will be available in-game
IRC_ENABLED = False
# IMC (Inter-MUD communication) allows to connect an Evennia channel
# to an IMC2 server. This lets them talk to people on other MUDs also
# using IMC. Evennia's IMC2 client was developed against MudByte's
# network. You must register your MUD on the network before you can
# use it, go to http://www.mudbytes.net/imc2-intermud-join-network.
# Choose 'Other unsupported IMC2 version' from the choices and and
# enter your information there. You should enter the same 'short mud
# name' as your SERVERNAME above. When enabled, the command @imc2chan
# becomes available in-game. It will take 'client password' and
# 'server password' as chosen when registering on mudbytes. The
# Evennia discussion channel 'ievennia' is on
# server02.mudbytes.net:9000.
IMC2_ENABLED = False # OBS! DON'T CHANGE - IMC2 is not implemented yet!
###################################################

View file

@ -34,7 +34,7 @@ from django.contrib.contenttypes.models import ContentType
from src.utils.idmapper.models import SharedMemoryModel
from src.typeclasses import managers
from src.locks.lockhandler import LockHandler
from src.utils import logger
from src.utils import logger, utils
from src.utils.utils import is_iter, has_parent
PERMISSION_HIERARCHY = [p.lower() for p in settings.PERMISSION_HIERARCHY]
@ -187,13 +187,13 @@ class Attribute(SharedMemoryModel):
Getter. Allows for value = self.value.
"""
try:
return self.validate_data(pickle.loads(str(self.db_value)))
return utils.to_unicode(self.validate_data(pickle.loads(utils.to_str(self.db_value))))
except pickle.UnpicklingError:
return self.db_value
#@value.setter
def value_set(self, new_value):
"Setter. Allows for self.value = value"
self.db_value = pickle.dumps(self.validate_data(new_value))
self.db_value = utils.to_unicode(pickle.dumps(utils.to_str(self.validate_data(new_value))))
self.save()
#@value.deleter
def value_del(self):

View file

@ -48,9 +48,8 @@ def start_reload_loop():
"default callback"
cemit_info(" Asynchronous server reload finished.\n" + '-'*50)
def at_err(e):
"error callback"
string = "%s\n reload: Asynchronous reset loop exited with an error." % e
string += "\n This might be harmless. Wait a moment then reload again to see if the problem persists."
"error callback"
string = "Reload: Asynchronous reset exited with an error:\n{r%s{n" % e.getErrorMessage()
cemit_info(string)
utils.run_async(run_loop, at_return, at_err)

View file

@ -12,6 +12,8 @@ from twisted.internet import threads
from django.conf import settings
from src.utils import ansi
ENCODINGS = settings.ENCODINGS
def is_iter(iterable):
"""
Checks if an object behaves iterably. However,
@ -225,29 +227,39 @@ def to_unicode(obj, encoding='utf-8'):
needs to encode it back to utf-8
before writing to disk or printing.
"""
if isinstance(obj, basestring) \
and not isinstance(obj, unicode):
if isinstance(obj, basestring) and not isinstance(obj, unicode):
try:
obj = unicode(obj, encoding)
return obj
except UnicodeDecodeError:
raise Exception("Error: '%s' contains invalid character(s) not in %s." % (obj, encoding))
return obj
for alt_encoding in ENCODINGS:
try:
obj = unicode(obj, alt_encoding)
return obj
except UnicodeDecodeError:
pass
raise Exception("Error: '%s' contains invalid character(s) not in %s." % (obj, encoding))
return obj
def to_str(obj, encoding='utf-8'):
"""
This encodes a unicode string
back to byte-representation,
This encodes a unicode string back to byte-representation,
for printing, writing to disk etc.
"""
if isinstance(obj, basestring) \
and isinstance(obj, unicode):
if isinstance(obj, basestring) and isinstance(obj, unicode):
try:
obj = obj.encode(encoding)
return obj
except UnicodeEncodeError:
raise Exception("Error: Unicode could not encode unicode string '%s'(%s) to a bytestring. " % (obj, encoding))
for alt_encoding in ENCODINGS:
try:
obj = obj.encode(encoding)
return obj
except UnicodeEncodeError:
pass
raise Exception("Error: Unicode could not encode unicode string '%s'(%s) to a bytestring. " % (obj, encoding))
return obj
def validate_email_address(emailaddress):
"""
Checks if an email address is syntactically correct.