mirror of
https://github.com/evennia/evennia.git
synced 2026-03-19 06:16:31 +01:00
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:
parent
14db4bea4d
commit
7d30b337d9
27 changed files with 873 additions and 1048 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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.")
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
@ -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
444
src/comms/imc2.py
Normal 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()
|
||||
|
|
@ -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.
|
||||
"""
|
||||
|
|
@ -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,
|
||||
|
|
@ -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)
|
||||
|
||||
|
|
@ -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()
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
@ -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)
|
||||
|
|
@ -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())
|
||||
|
|
@ -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)
|
||||
|
|
@ -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.
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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!
|
||||
|
||||
|
||||
###################################################
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue