PEP8 cleanup of the entire codebase. Unchanged are many cases of too-long lines, partly because of the rewrite they would require but also because splitting many lines up would make the code harder to read. Also the third-party libraries (idmapper, prettytable etc) were not cleaned.
This commit is contained in:
parent
30b7d2a405
commit
1ae17bcbe4
154 changed files with 5613 additions and 4054 deletions
|
|
@ -0,0 +1 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
@ -96,7 +96,7 @@ in-game.
|
||||||
|
|
||||||
from ev import Command, Script, CmdSet
|
from ev import Command, Script, CmdSet
|
||||||
|
|
||||||
TRADE_TIMEOUT = 60 # timeout for B to accept trade
|
TRADE_TIMEOUT = 60 # timeout for B to accept trade
|
||||||
|
|
||||||
|
|
||||||
class TradeTimeout(Script):
|
class TradeTimeout(Script):
|
||||||
|
|
@ -111,15 +111,18 @@ class TradeTimeout(Script):
|
||||||
self.start_delay = True
|
self.start_delay = True
|
||||||
self.repeats = 1
|
self.repeats = 1
|
||||||
self.persistent = False
|
self.persistent = False
|
||||||
|
|
||||||
def at_repeat(self):
|
def at_repeat(self):
|
||||||
"called once"
|
"called once"
|
||||||
if self.ndb.tradeevent:
|
if self.ndb.tradeevent:
|
||||||
self.obj.ndb.tradeevent.finish(force=True)
|
self.obj.ndb.tradeevent.finish(force=True)
|
||||||
self.obj.msg("Trade request timed out.")
|
self.obj.msg("Trade request timed out.")
|
||||||
|
|
||||||
def is_valid(self):
|
def is_valid(self):
|
||||||
"Only valid if the trade has not yet started"
|
"Only valid if the trade has not yet started"
|
||||||
return self.obj.ndb.tradeevent and not self.obj.ndb.tradeevent.trade_started
|
return self.obj.ndb.tradeevent and not self.obj.ndb.tradeevent.trade_started
|
||||||
|
|
||||||
|
|
||||||
class TradeHandler(object):
|
class TradeHandler(object):
|
||||||
"""
|
"""
|
||||||
Objects of this class handles the ongoing trade, notably storing the current
|
Objects of this class handles the ongoing trade, notably storing the current
|
||||||
|
|
@ -131,7 +134,8 @@ class TradeHandler(object):
|
||||||
a trade with part B. The trade will not start until part B repeats
|
a trade with part B. The trade will not start until part B repeats
|
||||||
this command (B will then call the self.join() command)
|
this command (B will then call the self.join() command)
|
||||||
|
|
||||||
We also store the back-reference from the respective party to this object.
|
We also store the back-reference from the respective party to
|
||||||
|
this object.
|
||||||
"""
|
"""
|
||||||
# parties
|
# parties
|
||||||
self.partA = partA
|
self.partA = partA
|
||||||
|
|
@ -145,7 +149,7 @@ class TradeHandler(object):
|
||||||
self.partB_offers = []
|
self.partB_offers = []
|
||||||
self.partA_accepted = False
|
self.partA_accepted = False
|
||||||
self.partB_accepted = False
|
self.partB_accepted = False
|
||||||
# start a timer
|
|
||||||
def msg(self, party, string):
|
def msg(self, party, string):
|
||||||
"""
|
"""
|
||||||
Relay a message to the other party. This allows
|
Relay a message to the other party. This allows
|
||||||
|
|
@ -159,6 +163,7 @@ class TradeHandler(object):
|
||||||
else:
|
else:
|
||||||
# no match, relay to oneself
|
# no match, relay to oneself
|
||||||
self.party.msg(string)
|
self.party.msg(string)
|
||||||
|
|
||||||
def get_other(self, party):
|
def get_other(self, party):
|
||||||
"Returns the other party of the trade"
|
"Returns the other party of the trade"
|
||||||
if self.partA == party:
|
if self.partA == party:
|
||||||
|
|
@ -171,13 +176,14 @@ class TradeHandler(object):
|
||||||
"""
|
"""
|
||||||
This is used once B decides to join the trade
|
This is used once B decides to join the trade
|
||||||
"""
|
"""
|
||||||
print "join:", self.partB, partB, self.partB == partB, type(self.partB),type(partB)
|
print "join:", self.partB, partB, self.partB == partB, type(self.partB), type(partB)
|
||||||
if self.partB == partB:
|
if self.partB == partB:
|
||||||
self.partB.ndb.tradehandler = self
|
self.partB.ndb.tradehandler = self
|
||||||
self.partB.cmdset.add(CmdsetTrade())
|
self.partB.cmdset.add(CmdsetTrade())
|
||||||
self.trade_started = True
|
self.trade_started = True
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def unjoin(self, partB):
|
def unjoin(self, partB):
|
||||||
"""
|
"""
|
||||||
This is used if B decides not to join the trade
|
This is used if B decides not to join the trade
|
||||||
|
|
@ -203,6 +209,7 @@ class TradeHandler(object):
|
||||||
self.partB_offers = list(args)
|
self.partB_offers = list(args)
|
||||||
else:
|
else:
|
||||||
raise ValueError
|
raise ValueError
|
||||||
|
|
||||||
def list(self):
|
def list(self):
|
||||||
"""
|
"""
|
||||||
Returns two lists of objects on offer, separated by partA/B.
|
Returns two lists of objects on offer, separated by partA/B.
|
||||||
|
|
@ -244,7 +251,8 @@ class TradeHandler(object):
|
||||||
self.partB_accepted = True
|
self.partB_accepted = True
|
||||||
else:
|
else:
|
||||||
raise ValueError
|
raise ValueError
|
||||||
return self.finish() # try to close the deal
|
return self.finish() # try to close the deal
|
||||||
|
|
||||||
def decline(self, party):
|
def decline(self, party):
|
||||||
"""
|
"""
|
||||||
Remove an previously accepted status (changing ones mind)
|
Remove an previously accepted status (changing ones mind)
|
||||||
|
|
@ -264,6 +272,7 @@ class TradeHandler(object):
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
raise ValueError
|
raise ValueError
|
||||||
|
|
||||||
def finish(self, force=False):
|
def finish(self, force=False):
|
||||||
"""
|
"""
|
||||||
Conclude trade - move all offers and clean up
|
Conclude trade - move all offers and clean up
|
||||||
|
|
@ -282,11 +291,13 @@ class TradeHandler(object):
|
||||||
self.partB.cmdset.delete("cmdset_trade")
|
self.partB.cmdset.delete("cmdset_trade")
|
||||||
self.partA_offers = None
|
self.partA_offers = None
|
||||||
self.partB_offers = None
|
self.partB_offers = None
|
||||||
del self.partA.ndb.tradehandler # this will kill it also from partB
|
# this will kill it also from partB
|
||||||
|
del self.partA.ndb.tradehandler
|
||||||
if self.partB.ndb.tradehandler:
|
if self.partB.ndb.tradehandler:
|
||||||
del self.partB.ndb.tradehandler
|
del self.partB.ndb.tradehandler
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
# trading commands (will go into CmdsetTrade, initialized by the
|
# trading commands (will go into CmdsetTrade, initialized by the
|
||||||
# CmdTrade command further down).
|
# CmdTrade command further down).
|
||||||
|
|
||||||
|
|
@ -320,6 +331,7 @@ class CmdTradeBase(Command):
|
||||||
else:
|
else:
|
||||||
self.str_other = '%s says, "' % self.caller.key + self.emote + '"\n [%s]'
|
self.str_other = '%s says, "' % self.caller.key + self.emote + '"\n [%s]'
|
||||||
|
|
||||||
|
|
||||||
# trade help
|
# trade help
|
||||||
|
|
||||||
class CmdTradeHelp(CmdTradeBase):
|
class CmdTradeHelp(CmdTradeBase):
|
||||||
|
|
@ -342,24 +354,30 @@ class CmdTradeHelp(CmdTradeBase):
|
||||||
Trading commands
|
Trading commands
|
||||||
|
|
||||||
{woffer <objects> [:emote]{n
|
{woffer <objects> [:emote]{n
|
||||||
offer one or more objects for trade. The emote can be used for RP/arguments.
|
offer one or more objects for trade. The emote can be used for
|
||||||
A new offer will require both parties to re-accept it again.
|
RP/arguments. A new offer will require both parties to re-accept
|
||||||
|
it again.
|
||||||
{waccept [:emote]{n
|
{waccept [:emote]{n
|
||||||
accept the currently standing offer from both sides. Also 'agree' works.
|
accept the currently standing offer from both sides. Also 'agree'
|
||||||
Once both have accepted, the deal is finished and goods will change hands.
|
works. Once both have accepted, the deal is finished and goods
|
||||||
|
will change hands.
|
||||||
{wdecline [:emote]{n
|
{wdecline [:emote]{n
|
||||||
change your mind and remove a previous accept (until other has also accepted)
|
change your mind and remove a previous accept (until other
|
||||||
|
has also accepted)
|
||||||
{wstatus{n
|
{wstatus{n
|
||||||
show the current offers on each side of the deal. Also 'offers' and 'deal' works.
|
show the current offers on each side of the deal. Also 'offers'
|
||||||
|
and 'deal' works.
|
||||||
{wevaluate <nr> or <offer>{n
|
{wevaluate <nr> or <offer>{n
|
||||||
examine any offer in the deal. List them with the 'status' command.
|
examine any offer in the deal. List them with the 'status' command.
|
||||||
{wend trade{n
|
{wend trade{n
|
||||||
end the negotiations prematurely. No trade will take place.
|
end the negotiations prematurely. No trade will take place.
|
||||||
|
|
||||||
You can also use {wemote{n, {wsay{n etc to discuss without making a decision or offer.
|
You can also use {wemote{n, {wsay{n etc to discuss
|
||||||
|
without making a decision or offer.
|
||||||
"""
|
"""
|
||||||
self.caller.msg(string)
|
self.caller.msg(string)
|
||||||
|
|
||||||
|
|
||||||
# offer
|
# offer
|
||||||
|
|
||||||
class CmdOffer(CmdTradeBase):
|
class CmdOffer(CmdTradeBase):
|
||||||
|
|
@ -406,6 +424,7 @@ class CmdOffer(CmdTradeBase):
|
||||||
caller.msg(self.str_caller % ("You offer %s" % objnames))
|
caller.msg(self.str_caller % ("You offer %s" % objnames))
|
||||||
self.msg_other(caller, self.str_other % ("They offer %s" % objnames))
|
self.msg_other(caller, self.str_other % ("They offer %s" % objnames))
|
||||||
|
|
||||||
|
|
||||||
# accept
|
# accept
|
||||||
|
|
||||||
class CmdAccept(CmdTradeBase):
|
class CmdAccept(CmdTradeBase):
|
||||||
|
|
@ -441,6 +460,7 @@ class CmdAccept(CmdTradeBase):
|
||||||
caller.msg(self.str_caller % "You {Gaccept{n the offer. %s must now also accept." % self.other.key)
|
caller.msg(self.str_caller % "You {Gaccept{n the offer. %s must now also accept." % self.other.key)
|
||||||
self.msg_other(caller, self.str_other % "%s {Gaccepts{n the offer. You must now also accept." % caller.key)
|
self.msg_other(caller, self.str_other % "%s {Gaccepts{n the offer. You must now also accept." % caller.key)
|
||||||
|
|
||||||
|
|
||||||
# decline
|
# decline
|
||||||
|
|
||||||
class CmdDecline(CmdTradeBase):
|
class CmdDecline(CmdTradeBase):
|
||||||
|
|
@ -521,6 +541,7 @@ class CmdEvaluate(CmdTradeBase):
|
||||||
# show the description
|
# show the description
|
||||||
caller.msg(offer.db.desc)
|
caller.msg(offer.db.desc)
|
||||||
|
|
||||||
|
|
||||||
# status
|
# status
|
||||||
|
|
||||||
class CmdStatus(CmdTradeBase):
|
class CmdStatus(CmdTradeBase):
|
||||||
|
|
@ -571,6 +592,7 @@ class CmdStatus(CmdTradeBase):
|
||||||
string += "\n Use 'offer', 'eval' and 'accept'/'decline' to trade. See also 'trade help'."
|
string += "\n Use 'offer', 'eval' and 'accept'/'decline' to trade. See also 'trade help'."
|
||||||
caller.msg(string)
|
caller.msg(string)
|
||||||
|
|
||||||
|
|
||||||
# finish
|
# finish
|
||||||
|
|
||||||
class CmdFinish(CmdTradeBase):
|
class CmdFinish(CmdTradeBase):
|
||||||
|
|
@ -596,6 +618,7 @@ class CmdFinish(CmdTradeBase):
|
||||||
caller.msg(self.str_caller % "You {raborted{n trade. No deal was made.")
|
caller.msg(self.str_caller % "You {raborted{n trade. No deal was made.")
|
||||||
self.msg_other(caller, self.str_other % "%s {raborted{n trade. No deal was made." % caller.key)
|
self.msg_other(caller, self.str_other % "%s {raborted{n trade. No deal was made." % caller.key)
|
||||||
|
|
||||||
|
|
||||||
# custom Trading cmdset
|
# custom Trading cmdset
|
||||||
|
|
||||||
class CmdsetTrade(CmdSet):
|
class CmdsetTrade(CmdSet):
|
||||||
|
|
@ -616,8 +639,8 @@ class CmdsetTrade(CmdSet):
|
||||||
self.add(CmdFinish())
|
self.add(CmdFinish())
|
||||||
|
|
||||||
|
|
||||||
# access command - once both have given this, this will create the trading cmdset
|
# access command - once both have given this, this will create the
|
||||||
# to start trade.
|
# trading cmdset to start trade.
|
||||||
|
|
||||||
class CmdTrade(Command):
|
class CmdTrade(Command):
|
||||||
"""
|
"""
|
||||||
|
|
@ -662,9 +685,9 @@ class CmdTrade(Command):
|
||||||
else:
|
else:
|
||||||
theiremote = '%s says, "%s"\n ' % (self.caller.key, emote)
|
theiremote = '%s says, "%s"\n ' % (self.caller.key, emote)
|
||||||
|
|
||||||
# for the sake of this command, the caller is always partA; this might not
|
# for the sake of this command, the caller is always partA; this
|
||||||
# match the actual name in tradehandler (in the case of using this command
|
# might not match the actual name in tradehandler (in the case of
|
||||||
# to accept/decline a trade invitation).
|
# using this command to accept/decline a trade invitation).
|
||||||
partA = self.caller
|
partA = self.caller
|
||||||
accept = 'accept' in self.args
|
accept = 'accept' in self.args
|
||||||
decline = 'decline' in self.args
|
decline = 'decline' in self.args
|
||||||
|
|
|
||||||
|
|
@ -122,9 +122,11 @@ class CmdOOCLook(default_cmds.CmdLook):
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# not ooc mode - leave back to normal look
|
# not ooc mode - leave back to normal look
|
||||||
self.caller = self.character # we have to put this back for normal look to work.
|
# we have to put this back for normal look to work.
|
||||||
|
self.caller = self.character
|
||||||
super(CmdOOCLook, self).func()
|
super(CmdOOCLook, self).func()
|
||||||
|
|
||||||
|
|
||||||
class CmdOOCCharacterCreate(Command):
|
class CmdOOCCharacterCreate(Command):
|
||||||
"""
|
"""
|
||||||
creates a character
|
creates a character
|
||||||
|
|
@ -179,9 +181,9 @@ class CmdOOCCharacterCreate(Command):
|
||||||
else:
|
else:
|
||||||
avail_chars = [new_character.id]
|
avail_chars = [new_character.id]
|
||||||
self.caller.db._character_dbrefs = avail_chars
|
self.caller.db._character_dbrefs = avail_chars
|
||||||
|
|
||||||
self.caller.msg("{gThe Character {c%s{g was successfully created!" % charname)
|
self.caller.msg("{gThe Character {c%s{g was successfully created!" % charname)
|
||||||
|
|
||||||
|
|
||||||
class OOCCmdSetCharGen(default_cmds.OOCCmdSet):
|
class OOCCmdSetCharGen(default_cmds.OOCCmdSet):
|
||||||
"""
|
"""
|
||||||
Extends the default OOC cmdset.
|
Extends the default OOC cmdset.
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ import re
|
||||||
from random import randint
|
from random import randint
|
||||||
from ev import default_cmds
|
from ev import default_cmds
|
||||||
|
|
||||||
|
|
||||||
def roll_dice(dicenum, dicetype, modifier=None, conditional=None, return_tuple=False):
|
def roll_dice(dicenum, dicetype, modifier=None, conditional=None, return_tuple=False):
|
||||||
"""
|
"""
|
||||||
This is a standard dice roller.
|
This is a standard dice roller.
|
||||||
|
|
@ -41,18 +42,22 @@ def roll_dice(dicenum, dicetype, modifier=None, conditional=None, return_tuple=F
|
||||||
Input:
|
Input:
|
||||||
dicenum - number of dice to roll (the result to be added)
|
dicenum - number of dice to roll (the result to be added)
|
||||||
dicetype - number of sides of the dice to be rolled
|
dicetype - number of sides of the dice to be rolled
|
||||||
modifier - tuple (operator, value), where operator is a character string with one of +,-,/ or *. The
|
modifier - tuple (operator, value), where operator is a character string
|
||||||
entire result of the dice rolls will be modified by this value.
|
with one of +,-,/ or *. The entire result of the dice rolls will
|
||||||
conditional - tuple (conditional, value), where conditional is a character string with one of ==,<,>,>=,<= or !=.
|
be modified by this value.
|
||||||
|
conditional - tuple (conditional, value), where conditional is a character
|
||||||
|
string with one of ==,<,>,>=,<= or !=.
|
||||||
return_tuple - return result as a tuple containing all relevant info
|
return_tuple - return result as a tuple containing all relevant info
|
||||||
return_tuple - (default False) - return a tuple with all individual roll results
|
return_tuple - (default False) - return a tuple with all individual roll
|
||||||
|
results
|
||||||
All input numbers are converted to integers.
|
All input numbers are converted to integers.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
normally returns the result
|
normally returns the result
|
||||||
if return_tuple=True, returns a tuple (result, outcome, diff, rolls)
|
if return_tuple=True, returns a tuple (result, outcome, diff, rolls)
|
||||||
In this tuple, outcome and diff will be None if conditional is not set. rolls is itself
|
In this tuple, outcome and diff will be None if conditional is
|
||||||
a tuple holding all the individual rolls in the case of multiple die-rolls.
|
not set. rolls is itself a tuple holding all the individual
|
||||||
|
rolls in the case of multiple die-rolls.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
TypeError if non-supported modifiers or conditionals are given.
|
TypeError if non-supported modifiers or conditionals are given.
|
||||||
|
|
@ -62,7 +67,7 @@ def roll_dice(dicenum, dicetype, modifier=None, conditional=None, return_tuple=F
|
||||||
dicetype = int(dicetype)
|
dicetype = int(dicetype)
|
||||||
|
|
||||||
# roll all dice, remembering each roll
|
# roll all dice, remembering each roll
|
||||||
rolls= tuple([randint(1, dicetype) for roll in range(dicenum)])
|
rolls = tuple([randint(1, dicetype) for roll in range(dicenum)])
|
||||||
result = sum(rolls)
|
result = sum(rolls)
|
||||||
|
|
||||||
if modifier:
|
if modifier:
|
||||||
|
|
@ -70,7 +75,7 @@ def roll_dice(dicenum, dicetype, modifier=None, conditional=None, return_tuple=F
|
||||||
mod, modvalue = modifier
|
mod, modvalue = modifier
|
||||||
if not mod in ('+', '-', '*', '/'):
|
if not mod in ('+', '-', '*', '/'):
|
||||||
raise TypeError("Non-supported dice modifier: %s" % mod)
|
raise TypeError("Non-supported dice modifier: %s" % mod)
|
||||||
modvalue = int(modvalue) # for safety
|
modvalue = int(modvalue) # for safety
|
||||||
result = eval("%s %s %s" % (result, mod, modvalue))
|
result = eval("%s %s %s" % (result, mod, modvalue))
|
||||||
outcome, diff = None, None
|
outcome, diff = None, None
|
||||||
if conditional:
|
if conditional:
|
||||||
|
|
@ -78,19 +83,19 @@ def roll_dice(dicenum, dicetype, modifier=None, conditional=None, return_tuple=F
|
||||||
cond, condvalue = conditional
|
cond, condvalue = conditional
|
||||||
if not cond in ('>', '<', '>=', '<=', '!=', '=='):
|
if not cond in ('>', '<', '>=', '<=', '!=', '=='):
|
||||||
raise TypeError("Non-supported dice result conditional: %s" % conditional)
|
raise TypeError("Non-supported dice result conditional: %s" % conditional)
|
||||||
condvalue = int(condvalue) # for safety
|
condvalue = int(condvalue) # for safety
|
||||||
outcome = eval("%s %s %s" % (result, cond, condvalue)) # gives True/False
|
outcome = eval("%s %s %s" % (result, cond, condvalue)) # True/False
|
||||||
diff = abs(result - condvalue)
|
diff = abs(result - condvalue)
|
||||||
if return_tuple:
|
if return_tuple:
|
||||||
return (result, outcome, diff, rolls)
|
return (result, outcome, diff, rolls)
|
||||||
else:
|
else:
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
RE_PARTS = re.compile(r"(d|\+|-|/|\*|<|>|<=|>=|!=|==)")
|
RE_PARTS = re.compile(r"(d|\+|-|/|\*|<|>|<=|>=|!=|==)")
|
||||||
RE_MOD = re.compile(r"(\+|-|/|\*)")
|
RE_MOD = re.compile(r"(\+|-|/|\*)")
|
||||||
RE_COND = re.compile(r"(<|>|<=|>=|!=|==)")
|
RE_COND = re.compile(r"(<|>|<=|>=|!=|==)")
|
||||||
|
|
||||||
|
|
||||||
class CmdDice(default_cmds.MuxCommand):
|
class CmdDice(default_cmds.MuxCommand):
|
||||||
"""
|
"""
|
||||||
roll dice
|
roll dice
|
||||||
|
|
@ -106,14 +111,16 @@ class CmdDice(default_cmds.MuxCommand):
|
||||||
dice 3d6 + 4
|
dice 3d6 + 4
|
||||||
dice 1d100 - 2 < 50
|
dice 1d100 - 2 < 50
|
||||||
|
|
||||||
This will roll the given number of dice with given sides and modifiers. So e.g. 2d6 + 3
|
This will roll the given number of dice with given sides and modifiers.
|
||||||
means to 'roll a 6-sided die 2 times and add the result, then add 3 to the total'.
|
So e.g. 2d6 + 3 means to 'roll a 6-sided die 2 times and add the result,
|
||||||
|
then add 3 to the total'.
|
||||||
Accepted modifiers are +, -, * and /.
|
Accepted modifiers are +, -, * and /.
|
||||||
A success condition is given as normal Python conditionals (<,>,<=,>=,==,!=).
|
A success condition is given as normal Python conditionals
|
||||||
So e.g. 2d6 + 3 > 10 means that the roll will succeed only if the final result is above 8.
|
(<,>,<=,>=,==,!=). So e.g. 2d6 + 3 > 10 means that the roll will succeed
|
||||||
If a success condition is given, the outcome (pass/fail) will be echoed along with how
|
only if the final result is above 8. If a success condition is given, the
|
||||||
much it succeeded/failed with. The hidden/secret switches will hide all or parts of the roll
|
outcome (pass/fail) will be echoed along with how much it succeeded/failed
|
||||||
from everyone but the person rolling.
|
with. The hidden/secret switches will hide all or parts of the roll from
|
||||||
|
everyone but the person rolling.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
key = "dice"
|
key = "dice"
|
||||||
|
|
@ -145,9 +152,9 @@ class CmdDice(default_cmds.MuxCommand):
|
||||||
pass
|
pass
|
||||||
elif lparts == 5:
|
elif lparts == 5:
|
||||||
# either e.g. 1d6 + 3 or something like 1d6 > 3
|
# either e.g. 1d6 + 3 or something like 1d6 > 3
|
||||||
if parts[3] in ('+','-','*','/'):
|
if parts[3] in ('+', '-', '*', '/'):
|
||||||
modifier = (parts[3], parts[4])
|
modifier = (parts[3], parts[4])
|
||||||
else: #assume it is a conditional
|
else: # assume it is a conditional
|
||||||
conditional = (parts[3], parts[4])
|
conditional = (parts[3], parts[4])
|
||||||
elif lparts == 7:
|
elif lparts == 7:
|
||||||
# the whole sequence, e.g. 1d6 + 3 > 5
|
# the whole sequence, e.g. 1d6 + 3 > 5
|
||||||
|
|
@ -159,7 +166,11 @@ class CmdDice(default_cmds.MuxCommand):
|
||||||
return
|
return
|
||||||
# do the roll
|
# do the roll
|
||||||
try:
|
try:
|
||||||
result, outcome, diff, rolls = roll_dice(ndice, nsides, modifier=modifier, conditional=conditional, return_tuple=True)
|
result, outcome, diff, rolls = roll_dice(ndice,
|
||||||
|
nsides,
|
||||||
|
modifier=modifier,
|
||||||
|
conditional=conditional,
|
||||||
|
return_tuple=True)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
self.caller.msg("You need to enter valid integer numbers, modifiers and operators. {w%s{n was not understood." % self.args)
|
self.caller.msg("You need to enter valid integer numbers, modifiers and operators. {w%s{n was not understood." % self.args)
|
||||||
return
|
return
|
||||||
|
|
@ -168,7 +179,7 @@ class CmdDice(default_cmds.MuxCommand):
|
||||||
rolls = ", ".join(str(roll) for roll in rolls[:-1]) + " and " + str(rolls[-1])
|
rolls = ", ".join(str(roll) for roll in rolls[:-1]) + " and " + str(rolls[-1])
|
||||||
else:
|
else:
|
||||||
rolls = rolls[0]
|
rolls = rolls[0]
|
||||||
if outcome == None:
|
if outcome is None:
|
||||||
outcomestring = ""
|
outcomestring = ""
|
||||||
elif outcome:
|
elif outcome:
|
||||||
outcomestring = " This is a {gsuccess{n (by %s)." % diff
|
outcomestring = " This is a {gsuccess{n (by %s)." % diff
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,8 @@ from src.commands.default.muxcommand import MuxCommand
|
||||||
from src.commands.cmdhandler import CMD_LOGINSTART
|
from src.commands.cmdhandler import CMD_LOGINSTART
|
||||||
|
|
||||||
# limit symbol import for API
|
# limit symbol import for API
|
||||||
__all__ = ("CmdUnconnectedConnect", "CmdUnconnectedCreate", "CmdUnconnectedQuit", "CmdUnconnectedLook", "CmdUnconnectedHelp")
|
__all__ = ("CmdUnconnectedConnect", "CmdUnconnectedCreate",
|
||||||
|
"CmdUnconnectedQuit", "CmdUnconnectedLook", "CmdUnconnectedHelp")
|
||||||
|
|
||||||
CONNECTION_SCREEN_MODULE = settings.CONNECTION_SCREEN_MODULE
|
CONNECTION_SCREEN_MODULE = settings.CONNECTION_SCREEN_MODULE
|
||||||
CONNECTION_SCREEN = ""
|
CONNECTION_SCREEN = ""
|
||||||
|
|
@ -57,6 +58,7 @@ except Exception:
|
||||||
if not CONNECTION_SCREEN:
|
if not CONNECTION_SCREEN:
|
||||||
CONNECTION_SCREEN = "\nEvennia: Error in CONNECTION_SCREEN MODULE (randomly picked connection screen variable is not a string). \nEnter 'help' for aid."
|
CONNECTION_SCREEN = "\nEvennia: Error in CONNECTION_SCREEN MODULE (randomly picked connection screen variable is not a string). \nEnter 'help' for aid."
|
||||||
|
|
||||||
|
|
||||||
class CmdUnconnectedConnect(MuxCommand):
|
class CmdUnconnectedConnect(MuxCommand):
|
||||||
"""
|
"""
|
||||||
Connect to the game.
|
Connect to the game.
|
||||||
|
|
@ -68,7 +70,7 @@ class CmdUnconnectedConnect(MuxCommand):
|
||||||
"""
|
"""
|
||||||
key = "connect"
|
key = "connect"
|
||||||
aliases = ["conn", "con", "co"]
|
aliases = ["conn", "con", "co"]
|
||||||
locks = "cmd:all()" # not really needed
|
locks = "cmd:all()" # not really needed
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -104,7 +106,7 @@ class CmdUnconnectedConnect(MuxCommand):
|
||||||
|
|
||||||
# Check IP and/or name bans
|
# Check IP and/or name bans
|
||||||
bans = ServerConfig.objects.conf("server_bans")
|
bans = ServerConfig.objects.conf("server_bans")
|
||||||
if bans and (any(tup[0]==player.name for tup in bans)
|
if bans and (any(tup[0] == player.name for tup in bans)
|
||||||
or
|
or
|
||||||
any(tup[2].match(session.address[0]) for tup in bans if tup[2])):
|
any(tup[2].match(session.address[0]) for tup in bans if tup[2])):
|
||||||
# this is a banned IP or name!
|
# this is a banned IP or name!
|
||||||
|
|
@ -160,7 +162,7 @@ class CmdUnconnectedCreate(MuxCommand):
|
||||||
else:
|
else:
|
||||||
playername, email, password = self.arglist
|
playername, email, password = self.arglist
|
||||||
|
|
||||||
playername = playername.replace('"', '') # remove "
|
playername = playername.replace('"', '') # remove "
|
||||||
playername = playername.replace("'", "")
|
playername = playername.replace("'", "")
|
||||||
self.playerinfo = (playername, email, password)
|
self.playerinfo = (playername, email, password)
|
||||||
|
|
||||||
|
|
@ -214,17 +216,20 @@ its and @/./+/-/_ only.") # this echoes the restrictions made by django's auth m
|
||||||
permissions = settings.PERMISSION_PLAYER_DEFAULT
|
permissions = settings.PERMISSION_PLAYER_DEFAULT
|
||||||
|
|
||||||
try:
|
try:
|
||||||
new_character = create.create_player(playername, email, password,
|
new_character = create.create_player(playername,
|
||||||
|
email,
|
||||||
|
password,
|
||||||
permissions=permissions,
|
permissions=permissions,
|
||||||
character_typeclass=typeclass,
|
character_typeclass=typeclass,
|
||||||
character_location=default_home,
|
character_location=default_home,
|
||||||
character_home=default_home)
|
character_home=default_home)
|
||||||
except Exception, e:
|
except Exception:
|
||||||
session.msg("There was an error creating the default Character/Player:\n%s\n If this problem persists, contact an admin.")
|
session.msg("There was an error creating the default Character/Player:\n%s\n If this problem persists, contact an admin.")
|
||||||
return
|
return
|
||||||
new_player = new_character.player
|
new_player = new_character.player
|
||||||
|
|
||||||
# This needs to be called so the engine knows this player is logging in for the first time.
|
# This needs to be called so the engine knows this player is
|
||||||
|
# logging in for the first time.
|
||||||
# (so it knows to call the right hooks during login later)
|
# (so it knows to call the right hooks during login later)
|
||||||
utils.init_new_player(new_player)
|
utils.init_new_player(new_player)
|
||||||
|
|
||||||
|
|
@ -236,11 +241,11 @@ its and @/./+/-/_ only.") # this echoes the restrictions made by django's auth m
|
||||||
string = "New player '%s' could not connect to public channel!" % new_player.key
|
string = "New player '%s' could not connect to public channel!" % new_player.key
|
||||||
logger.log_errmsg(string)
|
logger.log_errmsg(string)
|
||||||
|
|
||||||
# allow only the character itself and the player to puppet this character (and Immortals).
|
# allow only the character itself and the player to puppet
|
||||||
|
# this character (and Immortals).
|
||||||
new_character.locks.add("puppet:id(%i) or pid(%i) or perm(Immortals) or pperm(Immortals)" %
|
new_character.locks.add("puppet:id(%i) or pid(%i) or perm(Immortals) or pperm(Immortals)" %
|
||||||
(new_character.id, new_player.id))
|
(new_character.id, new_player.id))
|
||||||
|
|
||||||
|
|
||||||
# set a default description
|
# set a default description
|
||||||
new_character.db.desc = "This is a Player."
|
new_character.db.desc = "This is a Player."
|
||||||
|
|
||||||
|
|
@ -250,12 +255,14 @@ its and @/./+/-/_ only.") # this echoes the restrictions made by django's auth m
|
||||||
session.msg(string % (playername, email, email))
|
session.msg(string % (playername, email, email))
|
||||||
|
|
||||||
except Exception:
|
except Exception:
|
||||||
# We are in the middle between logged in and -not, so we have to handle tracebacks
|
# We are in the middle between logged in and -not, so we have to
|
||||||
# ourselves at this point. If we don't, we won't see any errors at all.
|
# handle tracebacks ourselves at this point. If we don't, we won't
|
||||||
|
# see any errors at all.
|
||||||
string = "%s\nThis is a bug. Please e-mail an admin if the problem persists."
|
string = "%s\nThis is a bug. Please e-mail an admin if the problem persists."
|
||||||
session.msg(string % (traceback.format_exc()))
|
session.msg(string % (traceback.format_exc()))
|
||||||
logger.log_errmsg(traceback.format_exc())
|
logger.log_errmsg(traceback.format_exc())
|
||||||
|
|
||||||
|
|
||||||
class CmdUnconnectedQuit(MuxCommand):
|
class CmdUnconnectedQuit(MuxCommand):
|
||||||
"""
|
"""
|
||||||
We maintain a different version of the quit command
|
We maintain a different version of the quit command
|
||||||
|
|
@ -272,6 +279,7 @@ class CmdUnconnectedQuit(MuxCommand):
|
||||||
session.msg("Good bye! Disconnecting ...")
|
session.msg("Good bye! Disconnecting ...")
|
||||||
session.session_disconnect()
|
session.session_disconnect()
|
||||||
|
|
||||||
|
|
||||||
class CmdUnconnectedLook(MuxCommand):
|
class CmdUnconnectedLook(MuxCommand):
|
||||||
"""
|
"""
|
||||||
This is an unconnected version of the look command for simplicity.
|
This is an unconnected version of the look command for simplicity.
|
||||||
|
|
@ -287,6 +295,7 @@ class CmdUnconnectedLook(MuxCommand):
|
||||||
"Show the connect screen."
|
"Show the connect screen."
|
||||||
self.caller.msg(CONNECTION_SCREEN)
|
self.caller.msg(CONNECTION_SCREEN)
|
||||||
|
|
||||||
|
|
||||||
class CmdUnconnectedHelp(MuxCommand):
|
class CmdUnconnectedHelp(MuxCommand):
|
||||||
"""
|
"""
|
||||||
This is an unconnected version of the help command,
|
This is an unconnected version of the help command,
|
||||||
|
|
@ -328,6 +337,7 @@ You can use the {wlook{n command if you want to see the connect screen again.
|
||||||
"""
|
"""
|
||||||
self.caller.msg(string)
|
self.caller.msg(string)
|
||||||
|
|
||||||
|
|
||||||
# command set for the mux-like login
|
# command set for the mux-like login
|
||||||
|
|
||||||
class UnloggedinCmdSet(CmdSet):
|
class UnloggedinCmdSet(CmdSet):
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
@ -15,6 +15,7 @@ from ev import utils
|
||||||
from ev import default_cmds
|
from ev import default_cmds
|
||||||
from src.utils import prettytable
|
from src.utils import prettytable
|
||||||
|
|
||||||
|
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
# Evlang-related commands
|
# Evlang-related commands
|
||||||
#
|
#
|
||||||
|
|
@ -86,13 +87,17 @@ class CmdCode(default_cmds.MuxCommand):
|
||||||
|
|
||||||
if not self.rhs:
|
if not self.rhs:
|
||||||
if codetype:
|
if codetype:
|
||||||
scripts = [(name, tup[1], utils.crop(tup[0])) for name, tup in evlang_scripts.items() if name==codetype]
|
scripts = [(name, tup[1], utils.crop(tup[0]))
|
||||||
scripts.extend([(name, "--", "--") for name in evlang_locks if name not in evlang_scripts if name==codetype])
|
for name, tup in evlang_scripts.items() if name==codetype]
|
||||||
|
scripts.extend([(name, "--", "--") for name in evlang_locks
|
||||||
|
if name not in evlang_scripts if name==codetype])
|
||||||
else:
|
else:
|
||||||
# no type specified. List all scripts/slots on object
|
# no type specified. List all scripts/slots on object
|
||||||
print evlang_scripts
|
print evlang_scripts
|
||||||
scripts = [(name, tup[1], utils.crop(tup[0])) for name, tup in evlang_scripts.items()]
|
scripts = [(name, tup[1], utils.crop(tup[0]))
|
||||||
scripts.extend([(name, "--", "--") for name in evlang_locks if name not in evlang_scripts])
|
for name, tup in evlang_scripts.items()]
|
||||||
|
scripts.extend([(name, "--", "--") for name in evlang_locks
|
||||||
|
if name not in evlang_scripts])
|
||||||
scripts = sorted(scripts, key=lambda p: p[0])
|
scripts = sorted(scripts, key=lambda p: p[0])
|
||||||
|
|
||||||
table = prettytable.PrettyTable(["{wtype", "{wcreator", "{wcode"])
|
table = prettytable.PrettyTable(["{wtype", "{wcreator", "{wcode"])
|
||||||
|
|
@ -114,7 +119,7 @@ class CmdCode(default_cmds.MuxCommand):
|
||||||
# we have code access to this type.
|
# we have code access to this type.
|
||||||
oldcode = None
|
oldcode = None
|
||||||
if codetype in evlang_scripts:
|
if codetype in evlang_scripts:
|
||||||
oldcode = str(evlang_scripts[codetype][0])
|
oldcode = str(evlang_scripts[codetype][0])
|
||||||
# this updates the database right away too
|
# this updates the database right away too
|
||||||
obj.ndb.evlang.add(codetype, codestring, scripter=caller)
|
obj.ndb.evlang.add(codetype, codestring, scripter=caller)
|
||||||
if oldcode:
|
if oldcode:
|
||||||
|
|
|
||||||
|
|
@ -98,19 +98,25 @@ _LOGGER = None
|
||||||
|
|
||||||
# specifically forbidden symbols
|
# specifically forbidden symbols
|
||||||
_EV_UNALLOWED_SYMBOLS = ["attr", "attributes", "delete"]
|
_EV_UNALLOWED_SYMBOLS = ["attr", "attributes", "delete"]
|
||||||
try: _EV_UNALLOWED_SYMBOLS.expand(settings.EVLANG_UNALLOWED_SYMBOLS)
|
try:
|
||||||
except AttributeError: pass
|
_EV_UNALLOWED_SYMBOLS.expand(settings.EVLANG_UNALLOWED_SYMBOLS)
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
|
||||||
# safe methods (including self in args) to make available on
|
# safe methods (including self in args) to make available on
|
||||||
# the evl object
|
# the evl object
|
||||||
_EV_SAFE_METHODS = {}
|
_EV_SAFE_METHODS = {}
|
||||||
try: _EV_SAFE_METHODS.update(settings.EVLANG_SAFE_METHODS)
|
try:
|
||||||
except AttributeError: pass
|
_EV_SAFE_METHODS.update(settings.EVLANG_SAFE_METHODS)
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
|
||||||
# symbols to make available directly in code
|
# symbols to make available directly in code
|
||||||
_EV_SAFE_CONTEXT = {"testvar": "This is a safe var!"}
|
_EV_SAFE_CONTEXT = {"testvar": "This is a safe var!"}
|
||||||
try: _EV_SAFE_CONTEXT.update(settings.EVLANG_SAFE_CONTEXT)
|
try:
|
||||||
except AttributeError: pass
|
_EV_SAFE_CONTEXT.update(settings.EVLANG_SAFE_CONTEXT)
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
|
|
@ -146,8 +152,9 @@ class Evl(object):
|
||||||
"""
|
"""
|
||||||
# must do it this way since __dict__ is restricted
|
# must do it this way since __dict__ is restricted
|
||||||
members = [mtup for mtup in inspect.getmembers(Evl, predicate=inspect.ismethod)
|
members = [mtup for mtup in inspect.getmembers(Evl, predicate=inspect.ismethod)
|
||||||
if not mtup[0].startswith("_")]
|
if not mtup[0].startswith("_")]
|
||||||
string = "\n".join(["{w%s{n\n %s" % (mtup[0], mtup[1].func_doc.strip()) for mtup in members])
|
string = "\n".join(["{w%s{n\n %s" % (mtup[0], mtup[1].func_doc.strip())
|
||||||
|
for mtup in members])
|
||||||
return string
|
return string
|
||||||
|
|
||||||
def msg(self, string, obj=None):
|
def msg(self, string, obj=None):
|
||||||
|
|
@ -200,18 +207,24 @@ class Evl(object):
|
||||||
errobj = kwargs["errobj"]
|
errobj = kwargs["errobj"]
|
||||||
del kwargs["errobj"]
|
del kwargs["errobj"]
|
||||||
# set up some callbacks for delayed execution
|
# set up some callbacks for delayed execution
|
||||||
|
|
||||||
def errback(f, errobj):
|
def errback(f, errobj):
|
||||||
|
"error callback"
|
||||||
if errobj:
|
if errobj:
|
||||||
try: f = f.getErrorMessage()
|
try:
|
||||||
except: pass
|
f = f.getErrorMessage()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
errobj.msg("EVLANG delay error: " + str(f))
|
errobj.msg("EVLANG delay error: " + str(f))
|
||||||
|
|
||||||
def runfunc(func, *args, **kwargs):
|
def runfunc(func, *args, **kwargs):
|
||||||
|
"threaded callback"
|
||||||
threads.deferToThread(func, *args, **kwargs).addErrback(errback, errobj)
|
threads.deferToThread(func, *args, **kwargs).addErrback(errback, errobj)
|
||||||
# get things going
|
# get things going
|
||||||
if seconds <= 120:
|
if seconds <= 120:
|
||||||
task.deferLater(reactor, seconds, runfunc, function, *args, **kwargs).addErrback(errback, errobj)
|
task.deferLater(reactor, seconds, runfunc, function, *args, **kwargs).addErrback(errback, errobj)
|
||||||
else:
|
else:
|
||||||
raise EvlangError("delay() can only delay for a maximum of 120 seconds (got %ss)." % seconds )
|
raise EvlangError("delay() can only delay for a maximum of 120 seconds (got %ss)." % seconds)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def attr(self, obj, attrname=None, value=None, delete=False):
|
def attr(self, obj, attrname=None, value=None, delete=False):
|
||||||
|
|
@ -244,6 +257,7 @@ class EvlangError(Exception):
|
||||||
"Error for evlang handler"
|
"Error for evlang handler"
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class Evlang(object):
|
class Evlang(object):
|
||||||
"""
|
"""
|
||||||
This is a handler for launching limited execution Python scripts.
|
This is a handler for launching limited execution Python scripts.
|
||||||
|
|
@ -265,17 +279,23 @@ class Evlang(object):
|
||||||
assumed to be granted.
|
assumed to be granted.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
def __init__(self, obj=None, scripts=None, storage_attr="evlang_scripts", safe_context=None, safe_timeout=2):
|
def __init__(self, obj=None, scripts=None, storage_attr="evlang_scripts",
|
||||||
|
safe_context=None, safe_timeout=2):
|
||||||
"""
|
"""
|
||||||
Setup of the Evlang handler.
|
Setup of the Evlang handler.
|
||||||
|
|
||||||
Input:
|
Input:
|
||||||
obj - a reference to the object this handler is defined on. If not set, handler will operate stand-alone.
|
obj - a reference to the object this handler is defined on. If not
|
||||||
scripts = dictionary {scriptname, (codestring, callerobj), ...} where callerobj can be None.
|
set, handler will operate stand-alone.
|
||||||
evlang_storage_attr - if obj is given, will look for a dictionary {scriptname, (codestring, callerobj)...}
|
scripts = dictionary {scriptname, (codestring, callerobj), ...}
|
||||||
stored in this given attribute name on that object.
|
where callerobj can be Noneevlang_storage_attr - if obj
|
||||||
safe_funcs - dictionary of {funcname:funcobj, ...} to make available for the execution environment
|
is given, will look for a dictionary
|
||||||
safe_timeout - the time we let a script run. If it exceeds this time, it will be blocked from running again.
|
{scriptname, (codestring, callerobj)...}
|
||||||
|
stored in this given attribute name on that object.
|
||||||
|
safe_funcs - dictionary of {funcname:funcobj, ...} to make available
|
||||||
|
for the execution environment
|
||||||
|
safe_timeout - the time we let a script run. If it exceeds this
|
||||||
|
time, it will be blocked from running again.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
self.obj = obj
|
self.obj = obj
|
||||||
|
|
@ -286,7 +306,7 @@ class Evlang(object):
|
||||||
self.evlang_scripts.update(scripts)
|
self.evlang_scripts.update(scripts)
|
||||||
if self.obj:
|
if self.obj:
|
||||||
self.evlang_scripts.update(obj.attributes.get(storage_attr))
|
self.evlang_scripts.update(obj.attributes.get(storage_attr))
|
||||||
self.safe_context = _EV_SAFE_CONTEXT # set by default + settings
|
self.safe_context = _EV_SAFE_CONTEXT # set by default + settings
|
||||||
if safe_context:
|
if safe_context:
|
||||||
self.safe_context.update(safe_context)
|
self.safe_context.update(safe_context)
|
||||||
self.timedout_codestrings = []
|
self.timedout_codestrings = []
|
||||||
|
|
@ -322,12 +342,16 @@ class Evlang(object):
|
||||||
_LOGGER.log_errmsg("EVLANG time exceeded: caller: %s, scripter: %s, code: %s" % (caller, scripter, codestring))
|
_LOGGER.log_errmsg("EVLANG time exceeded: caller: %s, scripter: %s, code: %s" % (caller, scripter, codestring))
|
||||||
if not self.msg(err, scripter, caller):
|
if not self.msg(err, scripter, caller):
|
||||||
raise EvlangError(err)
|
raise EvlangError(err)
|
||||||
|
|
||||||
def errback(f):
|
def errback(f):
|
||||||
"We need an empty errback, to catch the traceback of defer.cancel()"
|
"We need an empty errback, to catch the traceback of defer.cancel()"
|
||||||
pass
|
pass
|
||||||
return task.deferLater(reactor, timeout, alarm, codestring).addErrback(errback)
|
return task.deferLater(reactor, timeout, alarm, codestring).addErrback(errback)
|
||||||
|
|
||||||
def stop_timer(self, _, deferred):
|
def stop_timer(self, _, deferred):
|
||||||
"Callback for stopping a previously started timer. Cancels the given deferred."
|
"""Callback for stopping a previously started timer.
|
||||||
|
Cancels the given deferred.
|
||||||
|
"""
|
||||||
deferred.cancel()
|
deferred.cancel()
|
||||||
|
|
||||||
@inlineCallbacks
|
@inlineCallbacks
|
||||||
|
|
@ -337,7 +361,8 @@ class Evlang(object):
|
||||||
|
|
||||||
codestring - the actual code to execute.
|
codestring - the actual code to execute.
|
||||||
scripter - the creator of the script. Preferentially sees error messages
|
scripter - the creator of the script. Preferentially sees error messages
|
||||||
caller - the object triggering the script - sees error messages if no scripter is given
|
caller - the object triggering the script - sees error messages if
|
||||||
|
no scripter is given
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# catching previously detected long-running code
|
# catching previously detected long-running code
|
||||||
|
|
@ -391,7 +416,6 @@ class Evlang(object):
|
||||||
# execute code
|
# execute code
|
||||||
self.run(codestring, caller, scripter)
|
self.run(codestring, caller, scripter)
|
||||||
|
|
||||||
|
|
||||||
def add(self, scriptname, codestring, scripter=None):
|
def add(self, scriptname, codestring, scripter=None):
|
||||||
"""
|
"""
|
||||||
Add a new script to the handler. This will also save the
|
Add a new script to the handler. This will also save the
|
||||||
|
|
@ -401,7 +425,8 @@ class Evlang(object):
|
||||||
self.evlang_scripts[scriptname] = (codestring, scripter)
|
self.evlang_scripts[scriptname] = (codestring, scripter)
|
||||||
if self.obj:
|
if self.obj:
|
||||||
# save to database
|
# save to database
|
||||||
self.obj.attributes.add(self.evlang_storage_attr, self.evlang_scripts)
|
self.obj.attributes.add(self.evlang_storage_attr,
|
||||||
|
self.evlang_scripts)
|
||||||
|
|
||||||
def delete(self, scriptname):
|
def delete(self, scriptname):
|
||||||
"""
|
"""
|
||||||
|
|
@ -411,7 +436,8 @@ class Evlang(object):
|
||||||
del self.evlang_scripts[scriptname]
|
del self.evlang_scripts[scriptname]
|
||||||
if self.obj:
|
if self.obj:
|
||||||
# update change to database
|
# update change to database
|
||||||
self.obj.attributes.add(self.evlang_storage_attr, self.evlang_scripts)
|
self.obj.attributes.add(self.evlang_storage_attr,
|
||||||
|
self.evlang_scripts)
|
||||||
|
|
||||||
|
|
||||||
#----------------------------------------------------------------------
|
#----------------------------------------------------------------------
|
||||||
|
|
@ -436,8 +462,6 @@ class Evlang(object):
|
||||||
# to create an infinite loop.
|
# to create an infinite loop.
|
||||||
#----------------------------------------------------------------------
|
#----------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#----------------------------------------------------------------------
|
#----------------------------------------------------------------------
|
||||||
# Module globals.
|
# Module globals.
|
||||||
#----------------------------------------------------------------------
|
#----------------------------------------------------------------------
|
||||||
|
|
@ -555,25 +579,31 @@ UNALLOWED_BUILTINS = set([
|
||||||
# in with new unsafe things
|
# in with new unsafe things
|
||||||
SAFE_BUILTINS = set([
|
SAFE_BUILTINS = set([
|
||||||
'False', 'None', 'True', 'abs', 'all', 'any', 'apply', 'basestring',
|
'False', 'None', 'True', 'abs', 'all', 'any', 'apply', 'basestring',
|
||||||
'bin', 'bool', 'buffer', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod',
|
'bin', 'bool', 'buffer', 'bytearray', 'bytes', 'callable', 'chr',
|
||||||
|
'classmethod',
|
||||||
'cmp', 'coerce', 'complex', 'dict', 'divmod', 'enumerate', 'filter',
|
'cmp', 'coerce', 'complex', 'dict', 'divmod', 'enumerate', 'filter',
|
||||||
'float', 'format', 'frozenset', 'hash', 'hex', 'id', 'int',
|
'float', 'format', 'frozenset', 'hash', 'hex', 'id', 'int',
|
||||||
'isinstance', 'issubclass', 'iter', 'len', 'list', 'long', 'map', 'max', 'min',
|
'isinstance', 'issubclass', 'iter', 'len', 'list', 'long', 'map',
|
||||||
'next', 'object', 'oct', 'ord', 'pow', 'print', 'property', 'range', 'reduce',
|
'max', 'min',
|
||||||
'repr', 'reversed', 'round', 'set', 'slice', 'sorted', 'staticmethod', 'str',
|
'next', 'object', 'oct', 'ord', 'pow', 'print', 'property', 'range',
|
||||||
'sum', 'tuple', 'unichr', 'unicode', 'xrange', 'zip' ])
|
'reduce',
|
||||||
|
'repr', 'reversed', 'round', 'set', 'slice', 'sorted', 'staticmethod',
|
||||||
|
'str',
|
||||||
|
'sum', 'tuple', 'unichr', 'unicode', 'xrange', 'zip'])
|
||||||
|
|
||||||
for ast_name in UNALLOWED_AST_NODES:
|
for ast_name in UNALLOWED_AST_NODES:
|
||||||
assert(is_valid_ast_node(ast_name))
|
assert(is_valid_ast_node(ast_name))
|
||||||
for name in UNALLOWED_BUILTINS:
|
for name in UNALLOWED_BUILTINS:
|
||||||
assert(is_valid_builtin(name))
|
assert(is_valid_builtin(name))
|
||||||
|
|
||||||
|
|
||||||
def _cross_match_whitelist():
|
def _cross_match_whitelist():
|
||||||
"check the whitelist's completeness"
|
"check the whitelist's completeness"
|
||||||
available = ALL_BUILTINS - UNALLOWED_BUILTINS
|
available = ALL_BUILTINS - UNALLOWED_BUILTINS
|
||||||
diff = available.difference(SAFE_BUILTINS)
|
diff = available.difference(SAFE_BUILTINS)
|
||||||
assert not diff, diff # check so everything not disallowed is in safe
|
assert not diff, diff # check so everything not disallowed is in safe
|
||||||
diff = SAFE_BUILTINS.difference(available)
|
diff = SAFE_BUILTINS.difference(available)
|
||||||
assert not diff, diff # check so everything everything in safe is in not-disallowed
|
assert not diff, diff # check so everything in safe is in not-disallowed
|
||||||
_cross_match_whitelist()
|
_cross_match_whitelist()
|
||||||
|
|
||||||
def is_unallowed_ast_node(kind):
|
def is_unallowed_ast_node(kind):
|
||||||
|
|
@ -595,6 +625,7 @@ UNALLOWED_ATTR = [
|
||||||
'f_exc_type', 'f_exc_value', 'f_globals', 'f_locals']
|
'f_exc_type', 'f_exc_value', 'f_globals', 'f_locals']
|
||||||
UNALLOWED_ATTR.extend(_EV_UNALLOWED_SYMBOLS)
|
UNALLOWED_ATTR.extend(_EV_UNALLOWED_SYMBOLS)
|
||||||
|
|
||||||
|
|
||||||
def is_unallowed_attr(name):
|
def is_unallowed_attr(name):
|
||||||
return (name[:2] == '__' and name[-2:] == '__') or \
|
return (name[:2] == '__' and name[-2:] == '__') or \
|
||||||
(name in UNALLOWED_ATTR)
|
(name in UNALLOWED_ATTR)
|
||||||
|
|
@ -614,19 +645,26 @@ class LimitedExecError(object):
|
||||||
"""
|
"""
|
||||||
def __init__(self, errmsg, lineno):
|
def __init__(self, errmsg, lineno):
|
||||||
self.errmsg, self.lineno = errmsg, lineno
|
self.errmsg, self.lineno = errmsg, lineno
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "line %d : %s" % (self.lineno, self.errmsg)
|
return "line %d : %s" % (self.lineno, self.errmsg)
|
||||||
|
|
||||||
|
|
||||||
class LimitedExecASTNodeError(LimitedExecError):
|
class LimitedExecASTNodeError(LimitedExecError):
|
||||||
"Expression/statement in AST evaluates to a restricted AST node type."
|
"Expression/statement in AST evaluates to a restricted AST node type."
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class LimitedExecBuiltinError(LimitedExecError):
|
class LimitedExecBuiltinError(LimitedExecError):
|
||||||
"Expression/statement in tried to access a restricted builtin."
|
"Expression/statement in tried to access a restricted builtin."
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class LimitedExecAttrError(LimitedExecError):
|
class LimitedExecAttrError(LimitedExecError):
|
||||||
"Expression/statement in tried to access a restricted attribute."
|
"Expression/statement in tried to access a restricted attribute."
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class LimitedExecVisitor(object):
|
class LimitedExecVisitor(object):
|
||||||
"""
|
"""
|
||||||
Data-driven visitor which walks the AST for some code and makes
|
Data-driven visitor which walks the AST for some code and makes
|
||||||
|
|
@ -672,7 +710,8 @@ class LimitedExecVisitor(object):
|
||||||
def visit(self, node, *args):
|
def visit(self, node, *args):
|
||||||
"Recursively validate node and all of its children."
|
"Recursively validate node and all of its children."
|
||||||
fn = getattr(self, 'visit' + classname(node))
|
fn = getattr(self, 'visit' + classname(node))
|
||||||
if DEBUG: self.trace(node)
|
if DEBUG:
|
||||||
|
self.trace(node)
|
||||||
fn(node, *args)
|
fn(node, *args)
|
||||||
for child in node.getChildNodes():
|
for child in node.getChildNodes():
|
||||||
self.visit(child, *args)
|
self.visit(child, *args)
|
||||||
|
|
@ -682,10 +721,10 @@ class LimitedExecVisitor(object):
|
||||||
name = node.getChildren()[0]
|
name = node.getChildren()[0]
|
||||||
lineno = get_node_lineno(node)
|
lineno = get_node_lineno(node)
|
||||||
if is_unallowed_builtin(name):
|
if is_unallowed_builtin(name):
|
||||||
self.errors.append(LimitedExecBuiltinError( \
|
self.errors.append(LimitedExecBuiltinError(
|
||||||
"access to builtin '%s' is denied" % name, lineno))
|
"access to builtin '%s' is denied" % name, lineno))
|
||||||
elif is_unallowed_attr(name):
|
elif is_unallowed_attr(name):
|
||||||
self.errors.append(LimitedExecAttrError( \
|
self.errors.append(LimitedExecAttrError(
|
||||||
"access to attribute '%s' is denied" % name, lineno))
|
"access to attribute '%s' is denied" % name, lineno))
|
||||||
|
|
||||||
def visitGetattr(self, node, *args):
|
def visitGetattr(self, node, *args):
|
||||||
|
|
@ -696,10 +735,10 @@ class LimitedExecVisitor(object):
|
||||||
except Exception:
|
except Exception:
|
||||||
name = ""
|
name = ""
|
||||||
lineno = get_node_lineno(node)
|
lineno = get_node_lineno(node)
|
||||||
if attrname == 'attr' and name =='evl':
|
if attrname == 'attr' and name == 'evl':
|
||||||
pass
|
pass
|
||||||
elif is_unallowed_attr(attrname):
|
elif is_unallowed_attr(attrname):
|
||||||
self.errors.append(LimitedExecAttrError( \
|
self.errors.append(LimitedExecAttrError(
|
||||||
"access to attribute '%s' is denied" % attrname, lineno))
|
"access to attribute '%s' is denied" % attrname, lineno))
|
||||||
|
|
||||||
def visitAssName(self, node, *args):
|
def visitAssName(self, node, *args):
|
||||||
|
|
@ -710,8 +749,8 @@ class LimitedExecVisitor(object):
|
||||||
def visitPower(self, node, *args):
|
def visitPower(self, node, *args):
|
||||||
"Make sure power-of operations don't get too big"
|
"Make sure power-of operations don't get too big"
|
||||||
if node.left.value > 1000000 or node.right.value > 10:
|
if node.left.value > 1000000 or node.right.value > 10:
|
||||||
lineno = get_node_lineno(node)
|
lineno = get_node_lineno(node)
|
||||||
self.errors.append(LimitedExecAttrError( \
|
self.errors.append(LimitedExecAttrError(
|
||||||
"power law solution too big - restricted", lineno))
|
"power law solution too big - restricted", lineno))
|
||||||
|
|
||||||
def ok(self, node, *args):
|
def ok(self, node, *args):
|
||||||
|
|
@ -721,7 +760,7 @@ class LimitedExecVisitor(object):
|
||||||
def fail(self, node, *args):
|
def fail(self, node, *args):
|
||||||
"Default callback for unallowed AST nodes."
|
"Default callback for unallowed AST nodes."
|
||||||
lineno = get_node_lineno(node)
|
lineno = get_node_lineno(node)
|
||||||
self.errors.append(LimitedExecASTNodeError( \
|
self.errors.append(LimitedExecASTNodeError(
|
||||||
"execution of '%s' statements is denied" % classname(node),
|
"execution of '%s' statements is denied" % classname(node),
|
||||||
lineno))
|
lineno))
|
||||||
|
|
||||||
|
|
@ -732,6 +771,7 @@ class LimitedExecVisitor(object):
|
||||||
if attr[:2] != '__':
|
if attr[:2] != '__':
|
||||||
print ' ' * 4, "%-15.15s" % attr, getattr(node, attr)
|
print ' ' * 4, "%-15.15s" % attr, getattr(node, attr)
|
||||||
|
|
||||||
|
|
||||||
#----------------------------------------------------------------------
|
#----------------------------------------------------------------------
|
||||||
# Safe 'eval' replacement.
|
# Safe 'eval' replacement.
|
||||||
#----------------------------------------------------------------------
|
#----------------------------------------------------------------------
|
||||||
|
|
@ -740,6 +780,7 @@ class LimitedExecException(Exception):
|
||||||
"Base class for all safe-eval related errors."
|
"Base class for all safe-eval related errors."
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class LimitedExecCodeException(LimitedExecException):
|
class LimitedExecCodeException(LimitedExecException):
|
||||||
"""
|
"""
|
||||||
Exception class for reporting all errors which occured while
|
Exception class for reporting all errors which occured while
|
||||||
|
|
@ -754,6 +795,7 @@ class LimitedExecCodeException(LimitedExecException):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return '\n'.join([str(err) for err in self.errors])
|
return '\n'.join([str(err) for err in self.errors])
|
||||||
|
|
||||||
|
|
||||||
class LimitedExecContextException(LimitedExecException):
|
class LimitedExecContextException(LimitedExecException):
|
||||||
"""
|
"""
|
||||||
Exception class for reporting unallowed objects found in the dict
|
Exception class for reporting unallowed objects found in the dict
|
||||||
|
|
@ -769,6 +811,7 @@ class LimitedExecContextException(LimitedExecException):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return '\n'.join([str(err) for err in self.errors])
|
return '\n'.join([str(err) for err in self.errors])
|
||||||
|
|
||||||
|
|
||||||
class LimitedExecTimeoutException(LimitedExecException):
|
class LimitedExecTimeoutException(LimitedExecException):
|
||||||
"""
|
"""
|
||||||
Exception class for reporting that code evaluation execeeded
|
Exception class for reporting that code evaluation execeeded
|
||||||
|
|
@ -798,6 +841,7 @@ def validate_context(context):
|
||||||
raise LimitedExecContextException(ctx_errkeys, ctx_errors)
|
raise LimitedExecContextException(ctx_errkeys, ctx_errors)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def validate_code(codestring):
|
def validate_code(codestring):
|
||||||
"validate a code string"
|
"validate a code string"
|
||||||
# prepare the code tree for checking
|
# prepare the code tree for checking
|
||||||
|
|
@ -809,6 +853,7 @@ def validate_code(codestring):
|
||||||
raise LimitedExecCodeException(codestring, checker.errors)
|
raise LimitedExecCodeException(codestring, checker.errors)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def limited_exec(code, context = {}, timeout_secs=2, retobj=None, procpool_async=None):
|
def limited_exec(code, context = {}, timeout_secs=2, retobj=None, procpool_async=None):
|
||||||
"""
|
"""
|
||||||
Validate source code and make sure it contains no unauthorized
|
Validate source code and make sure it contains no unauthorized
|
||||||
|
|
@ -824,8 +869,8 @@ def limited_exec(code, context = {}, timeout_secs=2, retobj=None, procpool_async
|
||||||
retobj - only used if procpool_async is also given. Defines an Object
|
retobj - only used if procpool_async is also given. Defines an Object
|
||||||
(which must define a msg() method), for receiving returns from
|
(which must define a msg() method), for receiving returns from
|
||||||
the execution.
|
the execution.
|
||||||
procpool_async - a run_async function alternative to the one in src.utils.utils.
|
procpool_async - a run_async function alternative to the one in
|
||||||
this must accept the keywords
|
src.utils.utils. This must accept the keywords
|
||||||
proc_timeout (will be set to timeout_secs
|
proc_timeout (will be set to timeout_secs
|
||||||
at_return - a callback
|
at_return - a callback
|
||||||
at_err - an errback
|
at_err - an errback
|
||||||
|
|
@ -842,7 +887,10 @@ def limited_exec(code, context = {}, timeout_secs=2, retobj=None, procpool_async
|
||||||
if retobj:
|
if retobj:
|
||||||
callback = lambda r: retobj.msg(r)
|
callback = lambda r: retobj.msg(r)
|
||||||
errback = lambda e: retobj.msg(e)
|
errback = lambda e: retobj.msg(e)
|
||||||
procpool_async(code, *context, proc_timeout=timeout_secs, at_return=callback, at_err=errback)
|
procpool_async(code, *context,
|
||||||
|
proc_timeout=timeout_secs,
|
||||||
|
at_return=callback,
|
||||||
|
at_err=errback)
|
||||||
else:
|
else:
|
||||||
procpool_async(code, *context, proc_timeout=timeout_secs)
|
procpool_async(code, *context, proc_timeout=timeout_secs)
|
||||||
else:
|
else:
|
||||||
|
|
@ -864,41 +912,41 @@ class TestLimitedExec(unittest.TestCase):
|
||||||
|
|
||||||
def test_getattr(self):
|
def test_getattr(self):
|
||||||
# attempt to get arround direct attr access
|
# attempt to get arround direct attr access
|
||||||
self.assertRaises(LimitedExecException, \
|
self.assertRaises(LimitedExecException,
|
||||||
limited_exec, "getattr(int, '__abs__')")
|
limited_exec, "getattr(int, '__abs__')")
|
||||||
|
|
||||||
def test_func_globals(self):
|
def test_func_globals(self):
|
||||||
# attempt to access global enviroment where fun was defined
|
# attempt to access global enviroment where fun was defined
|
||||||
self.assertRaises(LimitedExecException, \
|
self.assertRaises(LimitedExecException,
|
||||||
limited_exec, "def x(): pass; print x.func_globals")
|
limited_exec, "def x(): pass; print x.func_globals")
|
||||||
|
|
||||||
def test_lowlevel(self):
|
def test_lowlevel(self):
|
||||||
# lowlevel tricks to access 'object'
|
# lowlevel tricks to access 'object'
|
||||||
self.assertRaises(LimitedExecException, \
|
self.assertRaises(LimitedExecException,
|
||||||
limited_exec, "().__class__.mro()[1].__subclasses__()")
|
limited_exec, "().__class__.mro()[1].__subclasses__()")
|
||||||
|
|
||||||
def test_timeout_ok(self):
|
def test_timeout_ok(self):
|
||||||
# attempt to exectute 'slow' code which finishes within timelimit
|
# attempt to exectute 'slow' code which finishes within timelimit
|
||||||
def test(): time.sleep(2)
|
def test(): time.sleep(2)
|
||||||
env = {'test':test}
|
env = {'test': test}
|
||||||
limited_exec("test()", env, timeout_secs = 5)
|
limited_exec("test()", env, timeout_secs=5)
|
||||||
|
|
||||||
def test_timeout_exceed(self):
|
def test_timeout_exceed(self):
|
||||||
# attempt to exectute code which never teminates
|
# attempt to exectute code which never teminates
|
||||||
self.assertRaises(LimitedExecException, \
|
self.assertRaises(LimitedExecException,
|
||||||
limited_exec, "while 1: pass")
|
limited_exec, "while 1: pass")
|
||||||
|
|
||||||
def test_invalid_context(self):
|
def test_invalid_context(self):
|
||||||
# can't pass an enviroment with modules or builtins
|
# can't pass an enviroment with modules or builtins
|
||||||
env = {'f' : __builtins__.open, 'g' : time}
|
env = {'f': __builtins__.open, 'g': time}
|
||||||
self.assertRaises(LimitedExecException, \
|
self.assertRaises(LimitedExecException,
|
||||||
limited_exec, "print 1", env)
|
limited_exec, "print 1", env)
|
||||||
|
|
||||||
def test_callback(self):
|
def test_callback(self):
|
||||||
# modify local variable via callback
|
# modify local variable via callback
|
||||||
self.value = 0
|
self.value = 0
|
||||||
def test(): self.value = 1
|
def test(): self.value = 1
|
||||||
env = {'test':test}
|
env = {'test': test}
|
||||||
limited_exec("test()", env)
|
limited_exec("test()", env)
|
||||||
self.assertEqual(self.value, 1)
|
self.assertEqual(self.value, 1)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -53,13 +53,16 @@ if applicable. An extended @desc command is used to set details.
|
||||||
CmdExtendedLook - look command supporting room details
|
CmdExtendedLook - look command supporting room details
|
||||||
CmdExtendedDesc - @desc command allowing to add seasonal descs and details,
|
CmdExtendedDesc - @desc command allowing to add seasonal descs and details,
|
||||||
as well as listing them
|
as well as listing them
|
||||||
CmdGameTime - A simple "time" command, displaying the current time and season.
|
CmdGameTime - A simple "time" command, displaying the current
|
||||||
|
time and season.
|
||||||
|
|
||||||
|
|
||||||
Installation/testing:
|
Installation/testing:
|
||||||
|
|
||||||
1) Add CmdExtendedLook, CmdExtendedDesc and CmdGameTime to the default cmdset (see wiki how to do this).
|
1) Add CmdExtendedLook, CmdExtendedDesc and CmdGameTime to the default cmdset
|
||||||
2) @dig a room of type contrib.extended_room.ExtendedRoom (or make it the default room type)
|
(see wiki how to do this).
|
||||||
|
2) @dig a room of type contrib.extended_room.ExtendedRoom (or make it the
|
||||||
|
default room type)
|
||||||
3) Use @desc and @detail to customize the room, then play around!
|
3) Use @desc and @detail to customize the room, then play around!
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
@ -90,16 +93,18 @@ REGEXMAP = {"morning": (RE_MORNING, RE_AFTERNOON, RE_EVENING, RE_NIGHT),
|
||||||
# beginning of the year (so month 1 is equivalent to January), and that
|
# beginning of the year (so month 1 is equivalent to January), and that
|
||||||
# one CAN divive the game's year into four seasons in the first place ...
|
# one CAN divive the game's year into four seasons in the first place ...
|
||||||
MONTHS_PER_YEAR = settings.TIME_MONTH_PER_YEAR
|
MONTHS_PER_YEAR = settings.TIME_MONTH_PER_YEAR
|
||||||
SEASONAL_BOUNDARIES = (3/12.0, 6/12.0, 9/12.0)
|
SEASONAL_BOUNDARIES = (3 / 12.0, 6 / 12.0, 9 / 12.0)
|
||||||
HOURS_PER_DAY = settings.TIME_HOUR_PER_DAY
|
HOURS_PER_DAY = settings.TIME_HOUR_PER_DAY
|
||||||
DAY_BOUNDARIES = (0, 6/24.0, 12/24.0, 18/24.0)
|
DAY_BOUNDARIES = (0, 6 / 24.0, 12 / 24.0, 18 / 24.0)
|
||||||
|
|
||||||
|
|
||||||
# implements the Extended Room
|
# implements the Extended Room
|
||||||
|
|
||||||
class ExtendedRoom(Room):
|
class ExtendedRoom(Room):
|
||||||
"""
|
"""
|
||||||
This room implements a more advanced look functionality depending on time. It also
|
This room implements a more advanced look functionality depending on
|
||||||
allows for "details", together with a slightly modified look command.
|
time. It also allows for "details", together with a slightly modified
|
||||||
|
look command.
|
||||||
"""
|
"""
|
||||||
def at_object_creation(self):
|
def at_object_creation(self):
|
||||||
"Called when room is first created only."
|
"Called when room is first created only."
|
||||||
|
|
@ -107,10 +112,12 @@ class ExtendedRoom(Room):
|
||||||
self.db.summer_desc = ""
|
self.db.summer_desc = ""
|
||||||
self.db.autumn_desc = ""
|
self.db.autumn_desc = ""
|
||||||
self.db.winter_desc = ""
|
self.db.winter_desc = ""
|
||||||
# the general desc is used as a fallback if a given seasonal one is not set
|
# the general desc is used as a fallback if a seasonal one is not set
|
||||||
self.db.general_desc = ""
|
self.db.general_desc = ""
|
||||||
self.db.raw_desc = "" # will be set dynamically. Can contain raw timeslot codes
|
# will be set dynamically. Can contain raw timeslot codes
|
||||||
self.db.desc = "" # this will be set dynamically at first look. Parsed for timeslot codes
|
self.db.raw_desc = ""
|
||||||
|
# this will be set dynamically at first look. Parsed for timeslot codes
|
||||||
|
self.db.desc = ""
|
||||||
# these will be filled later
|
# these will be filled later
|
||||||
self.ndb.last_season = None
|
self.ndb.last_season = None
|
||||||
self.ndb.last_timeslot = None
|
self.ndb.last_timeslot = None
|
||||||
|
|
@ -122,28 +129,37 @@ class ExtendedRoom(Room):
|
||||||
Calculate the current time and season ids
|
Calculate the current time and season ids
|
||||||
"""
|
"""
|
||||||
# get the current time as parts of year and parts of day
|
# get the current time as parts of year and parts of day
|
||||||
time = gametime.gametime(format=True) # returns a tuple (years,months,weeks,days,hours,minutes,sec)
|
# returns a tuple (years,months,weeks,days,hours,minutes,sec)
|
||||||
|
time = gametime.gametime(format=True)
|
||||||
month, hour = time[1], time[4]
|
month, hour = time[1], time[4]
|
||||||
season = float(month) / MONTHS_PER_YEAR
|
season = float(month) / MONTHS_PER_YEAR
|
||||||
timeslot = float(hour) / HOURS_PER_DAY
|
timeslot = float(hour) / HOURS_PER_DAY
|
||||||
|
|
||||||
# figure out which slots these represent
|
# figure out which slots these represent
|
||||||
if SEASONAL_BOUNDARIES[0] <= season < SEASONAL_BOUNDARIES[1]: curr_season = "spring"
|
if SEASONAL_BOUNDARIES[0] <= season < SEASONAL_BOUNDARIES[1]:
|
||||||
elif SEASONAL_BOUNDARIES[1] <= season < SEASONAL_BOUNDARIES[2]: curr_season = "summer"
|
curr_season = "spring"
|
||||||
elif SEASONAL_BOUNDARIES[2] <= season < 1.0 + SEASONAL_BOUNDARIES[0]: curr_season = "autumn"
|
elif SEASONAL_BOUNDARIES[1] <= season < SEASONAL_BOUNDARIES[2]:
|
||||||
else: curr_season = "winter"
|
curr_season = "summer"
|
||||||
|
elif SEASONAL_BOUNDARIES[2] <= season < 1.0 + SEASONAL_BOUNDARIES[0]:
|
||||||
|
curr_season = "autumn"
|
||||||
|
else:
|
||||||
|
curr_season = "winter"
|
||||||
|
|
||||||
if DAY_BOUNDARIES[0] <= timeslot < DAY_BOUNDARIES[1]: curr_timeslot = "night"
|
if DAY_BOUNDARIES[0] <= timeslot < DAY_BOUNDARIES[1]:
|
||||||
elif DAY_BOUNDARIES[1] <= timeslot < DAY_BOUNDARIES[2]: curr_timeslot = "morning"
|
curr_timeslot = "night"
|
||||||
elif DAY_BOUNDARIES[2] <= timeslot < DAY_BOUNDARIES[3]: curr_timeslot = "afternoon"
|
elif DAY_BOUNDARIES[1] <= timeslot < DAY_BOUNDARIES[2]:
|
||||||
else: curr_timeslot = "evening"
|
curr_timeslot = "morning"
|
||||||
|
elif DAY_BOUNDARIES[2] <= timeslot < DAY_BOUNDARIES[3]:
|
||||||
|
curr_timeslot = "afternoon"
|
||||||
|
else:
|
||||||
|
curr_timeslot = "evening"
|
||||||
|
|
||||||
return curr_season, curr_timeslot
|
return curr_season, curr_timeslot
|
||||||
|
|
||||||
def replace_timeslots(self, raw_desc, curr_time):
|
def replace_timeslots(self, raw_desc, curr_time):
|
||||||
"""
|
"""
|
||||||
Filter so that only time markers <timeslot>...</timeslot> of the correct timeslot
|
Filter so that only time markers <timeslot>...</timeslot> of the
|
||||||
remains in the description.
|
correct timeslot remains in the description.
|
||||||
"""
|
"""
|
||||||
if raw_desc:
|
if raw_desc:
|
||||||
regextuple = REGEXMAP[curr_time]
|
regextuple = REGEXMAP[curr_time]
|
||||||
|
|
@ -158,8 +174,9 @@ class ExtendedRoom(Room):
|
||||||
This will attempt to match a "detail" to look for in the room. A detail
|
This will attempt to match a "detail" to look for in the room. A detail
|
||||||
is a way to offer more things to look at in a room without having to
|
is a way to offer more things to look at in a room without having to
|
||||||
add new objects. For this to work, we require a custom look command that
|
add new objects. For this to work, we require a custom look command that
|
||||||
allows for "look <detail>" - the look command should defer to this method
|
allows for "look <detail>" - the look command should defer to this
|
||||||
on the current location (if it exists) before giving up on finding the target.
|
method on the current location (if it exists) before giving up on
|
||||||
|
finding the target.
|
||||||
|
|
||||||
Details are not season-sensitive, but are parsed for timeslot markers.
|
Details are not season-sensitive, but are parsed for timeslot markers.
|
||||||
"""
|
"""
|
||||||
|
|
@ -188,10 +205,14 @@ class ExtendedRoom(Room):
|
||||||
|
|
||||||
if curr_season != last_season:
|
if curr_season != last_season:
|
||||||
# season changed. Load new desc, or a fallback.
|
# season changed. Load new desc, or a fallback.
|
||||||
if curr_season == 'spring': new_raw_desc = self.db.spring_desc
|
if curr_season == 'spring':
|
||||||
elif curr_season == 'summer': new_raw_desc = self.db.summer_desc
|
new_raw_desc = self.db.spring_desc
|
||||||
elif curr_season == 'autumn': new_raw_desc = self.db.autumn_desc
|
elif curr_season == 'summer':
|
||||||
else: new_raw_desc = self.db.winter_desc
|
new_raw_desc = self.db.summer_desc
|
||||||
|
elif curr_season == 'autumn':
|
||||||
|
new_raw_desc = self.db.autumn_desc
|
||||||
|
else:
|
||||||
|
new_raw_desc = self.db.winter_desc
|
||||||
if new_raw_desc:
|
if new_raw_desc:
|
||||||
raw_desc = new_raw_desc
|
raw_desc = new_raw_desc
|
||||||
else:
|
else:
|
||||||
|
|
@ -207,14 +228,16 @@ class ExtendedRoom(Room):
|
||||||
update = True
|
update = True
|
||||||
|
|
||||||
if update:
|
if update:
|
||||||
# if anything changed we have to re-parse the raw_desc for time markers
|
# if anything changed we have to re-parse
|
||||||
|
# the raw_desc for time markers
|
||||||
# and re-save the description again.
|
# and re-save the description again.
|
||||||
self.db.desc = self.replace_timeslots(self.db.raw_desc, curr_timeslot)
|
self.db.desc = self.replace_timeslots(self.db.raw_desc, curr_timeslot)
|
||||||
# run the normal return_appearance method, now that desc is updated.
|
# run the normal return_appearance method, now that desc is updated.
|
||||||
return super(ExtendedRoom, self).return_appearance(looker)
|
return super(ExtendedRoom, self).return_appearance(looker)
|
||||||
|
|
||||||
|
|
||||||
# Custom Look command supporting Room details. Add this to the Default cmdset to use.
|
# Custom Look command supporting Room details. Add this to
|
||||||
|
# the Default cmdset to use.
|
||||||
|
|
||||||
class CmdExtendedLook(default_cmds.CmdLook):
|
class CmdExtendedLook(default_cmds.CmdLook):
|
||||||
"""
|
"""
|
||||||
|
|
@ -237,7 +260,8 @@ class CmdExtendedLook(default_cmds.CmdLook):
|
||||||
if args:
|
if args:
|
||||||
looking_at_obj = caller.search(args, use_nicks=True, ignore_errors=True)
|
looking_at_obj = caller.search(args, use_nicks=True, ignore_errors=True)
|
||||||
if not looking_at_obj:
|
if not looking_at_obj:
|
||||||
# no object found. Check if there is a matching detail at location.
|
# no object found. Check if there is a matching
|
||||||
|
# detail at location.
|
||||||
location = caller.location
|
location = caller.location
|
||||||
if location and hasattr(location, "return_detail") and callable(location.return_detail):
|
if location and hasattr(location, "return_detail") and callable(location.return_detail):
|
||||||
detail = location.return_detail(args)
|
detail = location.return_detail(args)
|
||||||
|
|
@ -269,7 +293,8 @@ class CmdExtendedLook(default_cmds.CmdLook):
|
||||||
looking_at_obj.at_desc(looker=caller)
|
looking_at_obj.at_desc(looker=caller)
|
||||||
|
|
||||||
|
|
||||||
# Custom build commands for setting seasonal descriptions and detailing extended rooms.
|
# Custom build commands for setting seasonal descriptions
|
||||||
|
# and detailing extended rooms.
|
||||||
|
|
||||||
class CmdExtendedDesc(default_cmds.CmdDesc):
|
class CmdExtendedDesc(default_cmds.CmdDesc):
|
||||||
"""
|
"""
|
||||||
|
|
@ -358,7 +383,10 @@ class CmdExtendedDesc(default_cmds.CmdDesc):
|
||||||
string += " {wgeneral:{n %s" % location.db.general_desc
|
string += " {wgeneral:{n %s" % location.db.general_desc
|
||||||
caller.msg(string)
|
caller.msg(string)
|
||||||
return
|
return
|
||||||
if self.switches and self.switches[0] in ("spring", "summer", "autumn", "winter"):
|
if self.switches and self.switches[0] in ("spring",
|
||||||
|
"summer",
|
||||||
|
"autumn",
|
||||||
|
"winter"):
|
||||||
# a seasonal switch was given
|
# a seasonal switch was given
|
||||||
if self.rhs:
|
if self.rhs:
|
||||||
caller.msg("Seasonal descs only works with rooms, not objects.")
|
caller.msg("Seasonal descs only works with rooms, not objects.")
|
||||||
|
|
@ -367,10 +395,14 @@ class CmdExtendedDesc(default_cmds.CmdDesc):
|
||||||
if not location:
|
if not location:
|
||||||
caller.msg("No location was found!")
|
caller.msg("No location was found!")
|
||||||
return
|
return
|
||||||
if switch == 'spring': location.db.spring_desc = self.args
|
if switch == 'spring':
|
||||||
elif switch == 'summer': location.db.summer_desc = self.args
|
location.db.spring_desc = self.args
|
||||||
elif switch == 'autumn': location.db.autumn_desc = self.args
|
elif switch == 'summer':
|
||||||
elif switch == 'winter': location.db.winter_desc = self.args
|
location.db.summer_desc = self.args
|
||||||
|
elif switch == 'autumn':
|
||||||
|
location.db.autumn_desc = self.args
|
||||||
|
elif switch == 'winter':
|
||||||
|
location.db.winter_desc = self.args
|
||||||
# clear flag to force an update
|
# clear flag to force an update
|
||||||
self.reset_times(location)
|
self.reset_times(location)
|
||||||
caller.msg("Seasonal description was set on %s." % location.key)
|
caller.msg("Seasonal description was set on %s." % location.key)
|
||||||
|
|
@ -384,9 +416,10 @@ class CmdExtendedDesc(default_cmds.CmdDesc):
|
||||||
else:
|
else:
|
||||||
text = self.args
|
text = self.args
|
||||||
obj = location
|
obj = location
|
||||||
obj.db.desc = self.rhs # this is set as a compatability fallback
|
obj.db.desc = self.rhs # a compatability fallback
|
||||||
if utils.inherits_from(obj, ExtendedRoom):
|
if utils.inherits_from(obj, ExtendedRoom):
|
||||||
# this is an extendedroom, we need to reset times and set general_desc
|
# this is an extendedroom, we need to reset
|
||||||
|
# times and set general_desc
|
||||||
obj.db.general_desc = text
|
obj.db.general_desc = text
|
||||||
self.reset_times(obj)
|
self.reset_times(obj)
|
||||||
caller.msg("General description was set on %s." % obj.key)
|
caller.msg("General description was set on %s." % obj.key)
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ CMD_NOINPUT = syscmdkeys.CMD_NOINPUT
|
||||||
|
|
||||||
RE_GROUP = re.compile(r"\".*?\"|\'.*?\'|\S*")
|
RE_GROUP = re.compile(r"\".*?\"|\'.*?\'|\S*")
|
||||||
|
|
||||||
|
|
||||||
class CmdEditorBase(Command):
|
class CmdEditorBase(Command):
|
||||||
"""
|
"""
|
||||||
Base parent for editor commands
|
Base parent for editor commands
|
||||||
|
|
@ -44,7 +45,8 @@ class CmdEditorBase(Command):
|
||||||
:cmd [li] [w] [txt]
|
:cmd [li] [w] [txt]
|
||||||
|
|
||||||
Where all arguments are optional.
|
Where all arguments are optional.
|
||||||
li - line number (int), starting from 1. This could also be a range given as <l>:<l>
|
li - line number (int), starting from 1. This could also
|
||||||
|
be a range given as <l>:<l>
|
||||||
w - word(s) (string), could be encased in quotes.
|
w - word(s) (string), could be encased in quotes.
|
||||||
txt - extra text (string), could be encased in quotes
|
txt - extra text (string), could be encased in quotes
|
||||||
"""
|
"""
|
||||||
|
|
@ -63,7 +65,8 @@ class CmdEditorBase(Command):
|
||||||
arglist = [part for part in RE_GROUP.findall(self.args) if part]
|
arglist = [part for part in RE_GROUP.findall(self.args) if part]
|
||||||
temp = []
|
temp = []
|
||||||
for arg in arglist:
|
for arg in arglist:
|
||||||
# we want to clean the quotes, but only one type, in case we are nesting.
|
# we want to clean the quotes, but only one type,
|
||||||
|
# in case we are nesting.
|
||||||
if arg.startswith('"'):
|
if arg.startswith('"'):
|
||||||
arg.strip('"')
|
arg.strip('"')
|
||||||
elif arg.startswith("'"):
|
elif arg.startswith("'"):
|
||||||
|
|
@ -71,7 +74,6 @@ class CmdEditorBase(Command):
|
||||||
temp.append(arg)
|
temp.append(arg)
|
||||||
arglist = temp
|
arglist = temp
|
||||||
|
|
||||||
|
|
||||||
# A dumb split, without grouping quotes
|
# A dumb split, without grouping quotes
|
||||||
words = self.args.split()
|
words = self.args.split()
|
||||||
|
|
||||||
|
|
@ -106,8 +108,8 @@ class CmdEditorBase(Command):
|
||||||
else:
|
else:
|
||||||
lstr = "lines %i-%i" % (lstart + 1, lend)
|
lstr = "lines %i-%i" % (lstart + 1, lend)
|
||||||
|
|
||||||
|
# arg1 and arg2 is whatever arguments. Line numbers or -ranges are
|
||||||
# arg1 and arg2 is whatever arguments. Line numbers or -ranges are never included here.
|
# never included here.
|
||||||
args = " ".join(arglist)
|
args = " ".join(arglist)
|
||||||
arg1, arg2 = "", ""
|
arg1, arg2 = "", ""
|
||||||
if len(arglist) > 1:
|
if len(arglist) > 1:
|
||||||
|
|
@ -141,6 +143,7 @@ class CmdLineInput(CmdEditorBase):
|
||||||
"""
|
"""
|
||||||
key = CMD_NOMATCH
|
key = CMD_NOMATCH
|
||||||
aliases = [CMD_NOINPUT]
|
aliases = [CMD_NOINPUT]
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"Adds the line without any formatting changes."
|
"Adds the line without any formatting changes."
|
||||||
# add a line of text
|
# add a line of text
|
||||||
|
|
@ -150,9 +153,11 @@ class CmdLineInput(CmdEditorBase):
|
||||||
buf = self.editor.buffer + "\n%s" % self.args
|
buf = self.editor.buffer + "\n%s" % self.args
|
||||||
self.editor.update_buffer(buf)
|
self.editor.update_buffer(buf)
|
||||||
if self.editor.echo_mode:
|
if self.editor.echo_mode:
|
||||||
cline = len(self.editor.buffer.split('\n')) # need to do it here or we will be off one line
|
# need to do it here or we will be off one line
|
||||||
|
cline = len(self.editor.buffer.split('\n'))
|
||||||
self.caller.msg("{b%02i|{n %s" % (cline, self.args))
|
self.caller.msg("{b%02i|{n %s" % (cline, self.args))
|
||||||
|
|
||||||
|
|
||||||
class CmdEditorGroup(CmdEditorBase):
|
class CmdEditorGroup(CmdEditorBase):
|
||||||
"""
|
"""
|
||||||
Commands for the editor
|
Commands for the editor
|
||||||
|
|
@ -165,8 +170,9 @@ class CmdEditorGroup(CmdEditorBase):
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"""
|
"""
|
||||||
This command handles all the in-editor :-style commands. Since each command
|
This command handles all the in-editor :-style commands. Since
|
||||||
is small and very limited, this makes for a more efficient presentation.
|
each command is small and very limited, this makes for a more
|
||||||
|
efficient presentation.
|
||||||
"""
|
"""
|
||||||
caller = self.caller
|
caller = self.caller
|
||||||
editor = self.editor
|
editor = self.editor
|
||||||
|
|
@ -187,7 +193,9 @@ class CmdEditorGroup(CmdEditorBase):
|
||||||
# Echo buffer without the line numbers and syntax parsing
|
# Echo buffer without the line numbers and syntax parsing
|
||||||
if self.linerange:
|
if self.linerange:
|
||||||
buf = linebuffer[lstart:lend]
|
buf = linebuffer[lstart:lend]
|
||||||
string = editor.display_buffer(buf=buf, offset=lstart, linenums=False)
|
string = editor.display_buffer(buf=buf,
|
||||||
|
offset=lstart,
|
||||||
|
linenums=False)
|
||||||
else:
|
else:
|
||||||
string = editor.display_buffer(linenums=False)
|
string = editor.display_buffer(linenums=False)
|
||||||
self.caller.msg(string, raw=True)
|
self.caller.msg(string, raw=True)
|
||||||
|
|
@ -242,7 +250,7 @@ class CmdEditorGroup(CmdEditorBase):
|
||||||
if not self.linerange:
|
if not self.linerange:
|
||||||
lstart = 0
|
lstart = 0
|
||||||
lend = self.cline + 1
|
lend = self.cline + 1
|
||||||
string = "Removed %s for lines %i-%i." % (self.arg1, lstart + 1 , lend + 1)
|
string = "Removed %s for lines %i-%i." % (self.arg1, lstart + 1, lend + 1)
|
||||||
else:
|
else:
|
||||||
string = "Removed %s for %s." % (self.arg1, self.lstr)
|
string = "Removed %s for %s." % (self.arg1, self.lstr)
|
||||||
sarea = "\n".join(linebuffer[lstart:lend])
|
sarea = "\n".join(linebuffer[lstart:lend])
|
||||||
|
|
@ -279,7 +287,7 @@ class CmdEditorGroup(CmdEditorBase):
|
||||||
if not new_lines:
|
if not new_lines:
|
||||||
string = "You need to enter a new line and where to insert it."
|
string = "You need to enter a new line and where to insert it."
|
||||||
else:
|
else:
|
||||||
buf = linebuffer[:lstart] + new_lines + linebuffer[lstart:]
|
buf = linebuffer[:lstart] + new_lines + linebuffer[lstart:]
|
||||||
editor.update_buffer(buf)
|
editor.update_buffer(buf)
|
||||||
string = "Inserted %i new line(s) at %s." % (len(new_lines), self.lstr)
|
string = "Inserted %i new line(s) at %s." % (len(new_lines), self.lstr)
|
||||||
elif cmd == ":r":
|
elif cmd == ":r":
|
||||||
|
|
@ -308,7 +316,8 @@ class CmdEditorGroup(CmdEditorBase):
|
||||||
editor.update_buffer(buf)
|
editor.update_buffer(buf)
|
||||||
string = "Appended text to end of %s." % self.lstr
|
string = "Appended text to end of %s." % self.lstr
|
||||||
elif cmd == ":s":
|
elif cmd == ":s":
|
||||||
# :s <li> <w> <txt> - search and replace words in entire buffer or on certain lines
|
# :s <li> <w> <txt> - search and replace words
|
||||||
|
# in entire buffer or on certain lines
|
||||||
if not self.arg1 or not self.arg2:
|
if not self.arg1 or not self.arg2:
|
||||||
string = "You must give a search word and something to replace it with."
|
string = "You must give a search word and something to replace it with."
|
||||||
else:
|
else:
|
||||||
|
|
@ -376,6 +385,7 @@ class EditorCmdSet(CmdSet):
|
||||||
key = "editorcmdset"
|
key = "editorcmdset"
|
||||||
mergetype = "Replace"
|
mergetype = "Replace"
|
||||||
|
|
||||||
|
|
||||||
class LineEditor(object):
|
class LineEditor(object):
|
||||||
"""
|
"""
|
||||||
This defines a line editor object. It creates all relevant commands
|
This defines a line editor object. It creates all relevant commands
|
||||||
|
|
@ -391,17 +401,21 @@ class LineEditor(object):
|
||||||
"""
|
"""
|
||||||
caller - who is using the editor
|
caller - who is using the editor
|
||||||
|
|
||||||
loadfunc - this will be called as func(*loadfunc_args) when the editor is first started, e.g. for pre-loading text into it.
|
loadfunc - this will be called as func(*loadfunc_args) when the
|
||||||
|
editor is first started, e.g. for pre-loading text into it.
|
||||||
loadfunc_args - optional tuple of arguments to supply to loadfunc.
|
loadfunc_args - optional tuple of arguments to supply to loadfunc.
|
||||||
savefunc - this will be called as func(*savefunc_args) when the save-command is given and is
|
savefunc - this will be called as func(*savefunc_args) when the
|
||||||
used to actually determine where/how result is saved. It should return True if save was successful and
|
save-command is given and is used to actually determine
|
||||||
also handle any feedback to the user.
|
where/how result is saved. It should return True if save
|
||||||
|
was successful and also handle any feedback to the user.
|
||||||
savefunc_args - optional tuple of arguments to supply to savefunc.
|
savefunc_args - optional tuple of arguments to supply to savefunc.
|
||||||
quitfunc - this will optionally e called as func(*quitfunc_args) when the editor is exited. If defined, it should
|
quitfunc - this will optionally e called as func(*quitfunc_args) when
|
||||||
handle all wanted feedback to the user.
|
the editor is exited. If defined, it should handle all
|
||||||
|
wanted feedback to the user.
|
||||||
quitfunc_args - optional tuple of arguments to supply to quitfunc.
|
quitfunc_args - optional tuple of arguments to supply to quitfunc.
|
||||||
|
|
||||||
key = an optional key for naming this session (such as which attribute is being edited)
|
key = an optional key for naming this session (such as which attribute
|
||||||
|
is being edited)
|
||||||
"""
|
"""
|
||||||
self.key = key
|
self.key = key
|
||||||
self.caller = caller
|
self.caller = caller
|
||||||
|
|
@ -420,13 +434,12 @@ class LineEditor(object):
|
||||||
# If no save function is defined, save an error-reporting function
|
# If no save function is defined, save an error-reporting function
|
||||||
err = "{rNo save function defined. Buffer cannot be saved.{n"
|
err = "{rNo save function defined. Buffer cannot be saved.{n"
|
||||||
caller.msg(err)
|
caller.msg(err)
|
||||||
savefunc = lambda: self.caller.msg(err)
|
savefunc = lambda: self.caller.msg(err)
|
||||||
self.savefunc = savefunc
|
self.savefunc = savefunc
|
||||||
self.savefunc_args = savefunc_args or ()
|
self.savefunc_args = savefunc_args or ()
|
||||||
self.quitfunc = quitfunc
|
self.quitfunc = quitfunc
|
||||||
self.quitfunc_args = quitfunc_args or ()
|
self.quitfunc_args = quitfunc_args or ()
|
||||||
|
|
||||||
|
|
||||||
# Create the commands we need
|
# Create the commands we need
|
||||||
cmd1 = CmdLineInput()
|
cmd1 = CmdLineInput()
|
||||||
cmd1.editor = self
|
cmd1.editor = self
|
||||||
|
|
@ -494,8 +507,9 @@ class LineEditor(object):
|
||||||
if self.unsaved:
|
if self.unsaved:
|
||||||
try:
|
try:
|
||||||
if self.savefunc(*self.savefunc_args):
|
if self.savefunc(*self.savefunc_args):
|
||||||
# Save codes should return a true value to indicate save worked.
|
# Save codes should return a true value to indicate
|
||||||
# The saving function is responsible for any status messages.
|
# save worked. The saving function is responsible for
|
||||||
|
# any status messages.
|
||||||
self.unsaved = False
|
self.unsaved = False
|
||||||
return ""
|
return ""
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
|
|
@ -555,7 +569,7 @@ class LineEditor(object):
|
||||||
"""
|
"""
|
||||||
Shows the help entry for the editor.
|
Shows the help entry for the editor.
|
||||||
"""
|
"""
|
||||||
string = self.sep*78 + """
|
string = self.sep * 78 + """
|
||||||
<txt> - any non-command is appended to the end of the buffer.
|
<txt> - any non-command is appended to the end of the buffer.
|
||||||
: <l> - view buffer or only line <l>
|
: <l> - view buffer or only line <l>
|
||||||
:: <l> - view buffer without line numbers or other parsing
|
:: <l> - view buffer without line numbers or other parsing
|
||||||
|
|
@ -578,7 +592,7 @@ class LineEditor(object):
|
||||||
:y <l> - yank (copy) line <l> to the copy buffer
|
:y <l> - yank (copy) line <l> to the copy buffer
|
||||||
:x <l> - cut line <l> and store it in the copy buffer
|
:x <l> - cut line <l> and store it in the copy buffer
|
||||||
:p <l> - put (paste) previously copied line directly after <l>
|
:p <l> - put (paste) previously copied line directly after <l>
|
||||||
:i <l> <txt> - insert new text <txt> at line <l>. Old line will be shifted down
|
:i <l> <txt> - insert new text <txt> at line <l>. Old line will move down
|
||||||
:r <l> <txt> - replace line <l> with text <txt>
|
:r <l> <txt> - replace line <l> with text <txt>
|
||||||
:I <l> <txt> - insert text at the beginning of line <l>
|
:I <l> <txt> - insert text at the beginning of line <l>
|
||||||
:A <l> <txt> - append text after the end of line <l>
|
:A <l> <txt> - append text after the end of line <l>
|
||||||
|
|
@ -627,7 +641,8 @@ class CmdEditor(Command):
|
||||||
if not self.args or not '/' in self.args:
|
if not self.args or not '/' in self.args:
|
||||||
self.caller.msg("Usage: @editor <obj>/<attrname>")
|
self.caller.msg("Usage: @editor <obj>/<attrname>")
|
||||||
return
|
return
|
||||||
self.objname, self.attrname = [part.strip() for part in self.args.split("/", 1)]
|
self.objname, self.attrname = [part.strip()
|
||||||
|
for part in self.args.split("/", 1)]
|
||||||
self.obj = self.caller.search(self.objname)
|
self.obj = self.caller.search(self.objname)
|
||||||
if not self.obj:
|
if not self.obj:
|
||||||
return
|
return
|
||||||
|
|
@ -636,20 +651,28 @@ class CmdEditor(Command):
|
||||||
def load_attr():
|
def load_attr():
|
||||||
"inital loading of buffer data from given attribute."
|
"inital loading of buffer data from given attribute."
|
||||||
target = self.obj.attributes.get(self.attrname)
|
target = self.obj.attributes.get(self.attrname)
|
||||||
if target != None and not isinstance(target, basestring):
|
if target is not None and not isinstance(target, basestring):
|
||||||
typ = type(target).__name__
|
typ = type(target).__name__
|
||||||
self.caller.msg("{RWARNING! Saving this buffer will overwrite the current attribute (of type %s) with a string!{n" % typ)
|
self.caller.msg("{RWARNING! Saving this buffer will overwrite the current attribute (of type %s) with a string!{n" % typ)
|
||||||
return target and str(target) or ""
|
return target and str(target) or ""
|
||||||
|
|
||||||
def save_attr():
|
def save_attr():
|
||||||
"Save line buffer to given attribute name. This should return True if successful and also report its status."
|
"""
|
||||||
|
Save line buffer to given attribute name. This should
|
||||||
|
return True if successful and also report its status.
|
||||||
|
"""
|
||||||
self.obj.attributes.add(self.attrname, self.editor.buffer)
|
self.obj.attributes.add(self.attrname, self.editor.buffer)
|
||||||
self.caller.msg("Saved.")
|
self.caller.msg("Saved.")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def quit_hook():
|
def quit_hook():
|
||||||
"Example quit hook. Since it's given, it's responsible for giving feedback messages."
|
"Example quit hook. Since it's given, it's responsible for giving feedback messages."
|
||||||
self.caller.msg("Exited Editor.")
|
self.caller.msg("Exited Editor.")
|
||||||
|
|
||||||
|
|
||||||
editor_key = "%s/%s" % (self.objname, self.attrname)
|
editor_key = "%s/%s" % (self.objname, self.attrname)
|
||||||
# start editor, it will handle things from here.
|
# start editor, it will handle things from here.
|
||||||
self.editor = LineEditor(self.caller, loadfunc=load_attr, savefunc=save_attr, quitfunc=quit_hook, key=editor_key)
|
self.editor = LineEditor(self.caller,
|
||||||
|
loadfunc=load_attr,
|
||||||
|
savefunc=save_attr,
|
||||||
|
quitfunc=quit_hook,
|
||||||
|
key=editor_key)
|
||||||
|
|
@ -46,8 +46,9 @@ CMD_NOMATCH = syscmdkeys.CMD_NOMATCH
|
||||||
CONNECTION_SCREEN_MODULE = settings.CONNECTION_SCREEN_MODULE
|
CONNECTION_SCREEN_MODULE = settings.CONNECTION_SCREEN_MODULE
|
||||||
|
|
||||||
|
|
||||||
# Commands run on the unloggedin screen. Note that this is not using settings.UNLOGGEDIN_CMDSET but
|
# Commands run on the unloggedin screen. Note that this is not using
|
||||||
# the menu system, which is why some are named for the numbers in the menu.
|
# settings.UNLOGGEDIN_CMDSET but the menu system, which is why some are
|
||||||
|
# named for the numbers in the menu.
|
||||||
#
|
#
|
||||||
# Also note that the menu system will automatically assign all
|
# Also note that the menu system will automatically assign all
|
||||||
# commands used in its structure a property "menutree" holding a reference
|
# commands used in its structure a property "menutree" holding a reference
|
||||||
|
|
@ -63,10 +64,12 @@ class CmdBackToStart(Command):
|
||||||
"""
|
"""
|
||||||
key = CMD_NOINPUT
|
key = CMD_NOINPUT
|
||||||
locks = "cmd:all()"
|
locks = "cmd:all()"
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"Execute the command"
|
"Execute the command"
|
||||||
self.menutree.goto("START")
|
self.menutree.goto("START")
|
||||||
|
|
||||||
|
|
||||||
class CmdUsernameSelect(Command):
|
class CmdUsernameSelect(Command):
|
||||||
"""
|
"""
|
||||||
Handles the entering of a username and
|
Handles the entering of a username and
|
||||||
|
|
@ -74,6 +77,7 @@ class CmdUsernameSelect(Command):
|
||||||
"""
|
"""
|
||||||
key = CMD_NOMATCH
|
key = CMD_NOMATCH
|
||||||
locks = "cmd:all()"
|
locks = "cmd:all()"
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"Execute the command"
|
"Execute the command"
|
||||||
player = managers.players.get_player_from_name(self.args)
|
player = managers.players.get_player_from_name(self.args)
|
||||||
|
|
@ -81,9 +85,11 @@ class CmdUsernameSelect(Command):
|
||||||
self.caller.msg("{rThis account name couldn't be found. Did you create it? If you did, make sure you spelled it right (case doesn't matter).{n")
|
self.caller.msg("{rThis account name couldn't be found. Did you create it? If you did, make sure you spelled it right (case doesn't matter).{n")
|
||||||
self.menutree.goto("node1a")
|
self.menutree.goto("node1a")
|
||||||
else:
|
else:
|
||||||
self.menutree.player = player # store the player so next step can find it
|
# store the player so next step can find it
|
||||||
|
self.menutree.player = player
|
||||||
self.menutree.goto("node1b")
|
self.menutree.goto("node1b")
|
||||||
|
|
||||||
|
|
||||||
# Menu entry 1b - Entering a Password
|
# Menu entry 1b - Entering a Password
|
||||||
|
|
||||||
class CmdPasswordSelectBack(Command):
|
class CmdPasswordSelectBack(Command):
|
||||||
|
|
@ -92,10 +98,12 @@ class CmdPasswordSelectBack(Command):
|
||||||
"""
|
"""
|
||||||
key = CMD_NOINPUT
|
key = CMD_NOINPUT
|
||||||
locks = "cmd:all()"
|
locks = "cmd:all()"
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"Execute the command"
|
"Execute the command"
|
||||||
self.menutree.goto("node1a")
|
self.menutree.goto("node1a")
|
||||||
|
|
||||||
|
|
||||||
class CmdPasswordSelect(Command):
|
class CmdPasswordSelect(Command):
|
||||||
"""
|
"""
|
||||||
Handles the entering of a password and logs into the game.
|
Handles the entering of a password and logs into the game.
|
||||||
|
|
@ -117,9 +125,10 @@ class CmdPasswordSelect(Command):
|
||||||
|
|
||||||
# before going on, check eventual bans
|
# before going on, check eventual bans
|
||||||
bans = managers.serverconfigs.conf("server_bans")
|
bans = managers.serverconfigs.conf("server_bans")
|
||||||
if bans and (any(tup[0]==player.name for tup in bans)
|
if bans and (any(tup[0] == player.name for tup in bans)
|
||||||
or
|
or
|
||||||
any(tup[2].match(player.sessions[0].address[0]) for tup in bans if tup[2])):
|
any(tup[2].match(player.sessions[0].address[0])
|
||||||
|
for tup in bans if tup[2])):
|
||||||
# this is a banned IP or name!
|
# this is a banned IP or name!
|
||||||
string = "{rYou have been banned and cannot continue from here."
|
string = "{rYou have been banned and cannot continue from here."
|
||||||
string += "\nIf you feel this ban is in error, please email an admin.{x"
|
string += "\nIf you feel this ban is in error, please email an admin.{x"
|
||||||
|
|
@ -142,6 +151,7 @@ class CmdPasswordSelect(Command):
|
||||||
# we have no character yet; use player's look, if it exists
|
# we have no character yet; use player's look, if it exists
|
||||||
player.execute_cmd("look")
|
player.execute_cmd("look")
|
||||||
|
|
||||||
|
|
||||||
# Menu entry 2a - Creating a Username
|
# Menu entry 2a - Creating a Username
|
||||||
|
|
||||||
class CmdUsernameCreate(Command):
|
class CmdUsernameCreate(Command):
|
||||||
|
|
@ -169,16 +179,19 @@ its and @/./+/-/_ only.{n") # this echoes the restrictions made by django's auth
|
||||||
self.menutree.playername = playername
|
self.menutree.playername = playername
|
||||||
self.menutree.goto("node2b")
|
self.menutree.goto("node2b")
|
||||||
|
|
||||||
|
|
||||||
# Menu entry 2b - Creating a Password
|
# Menu entry 2b - Creating a Password
|
||||||
|
|
||||||
class CmdPasswordCreateBack(Command):
|
class CmdPasswordCreateBack(Command):
|
||||||
"Step back from the password creation"
|
"Step back from the password creation"
|
||||||
key = CMD_NOINPUT
|
key = CMD_NOINPUT
|
||||||
locks = "cmd:all()"
|
locks = "cmd:all()"
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"Execute the command"
|
"Execute the command"
|
||||||
self.menutree.goto("node2a")
|
self.menutree.goto("node2a")
|
||||||
|
|
||||||
|
|
||||||
class CmdPasswordCreate(Command):
|
class CmdPasswordCreate(Command):
|
||||||
"Handle the creation of a password. This also creates the actual Player/User object."
|
"Handle the creation of a password. This also creates the actual Player/User object."
|
||||||
key = CMD_NOMATCH
|
key = CMD_NOMATCH
|
||||||
|
|
@ -195,12 +208,14 @@ class CmdPasswordCreate(Command):
|
||||||
if len(password) < 3:
|
if len(password) < 3:
|
||||||
# too short password
|
# too short password
|
||||||
string = "{rYour password must be at least 3 characters or longer."
|
string = "{rYour password must be at least 3 characters or longer."
|
||||||
string += "\n\rFor best security, make it at least 8 characters long, "
|
string += "\n\rFor best security, make it at least 8 characters "
|
||||||
string += "avoid making it a real word and mix numbers into it.{n"
|
string += "long, avoid making it a real word and mix numbers "
|
||||||
|
string += "into it.{n"
|
||||||
self.caller.msg(string)
|
self.caller.msg(string)
|
||||||
self.menutree.goto("node2b")
|
self.menutree.goto("node2b")
|
||||||
return
|
return
|
||||||
# everything's ok. Create the new player account. Don't create a Character here.
|
# everything's ok. Create the new player account. Don't create
|
||||||
|
# a Character here.
|
||||||
try:
|
try:
|
||||||
permissions = settings.PERMISSION_PLAYER_DEFAULT
|
permissions = settings.PERMISSION_PLAYER_DEFAULT
|
||||||
typeclass = settings.BASE_PLAYER_TYPECLASS
|
typeclass = settings.BASE_PLAYER_TYPECLASS
|
||||||
|
|
@ -227,8 +242,9 @@ class CmdPasswordCreate(Command):
|
||||||
self.caller.msg(string % (playername))
|
self.caller.msg(string % (playername))
|
||||||
self.menutree.goto("START")
|
self.menutree.goto("START")
|
||||||
except Exception:
|
except Exception:
|
||||||
# We are in the middle between logged in and -not, so we have to handle tracebacks
|
# We are in the middle between logged in and -not, so we have
|
||||||
# ourselves at this point. If we don't, we won't see any errors at all.
|
# to handle tracebacks ourselves at this point. If we don't, we
|
||||||
|
# won't see any errors at all.
|
||||||
string = "%s\nThis is a bug. Please e-mail an admin if the problem persists."
|
string = "%s\nThis is a bug. Please e-mail an admin if the problem persists."
|
||||||
self.caller.msg(string % (traceback.format_exc()))
|
self.caller.msg(string % (traceback.format_exc()))
|
||||||
logger.log_errmsg(traceback.format_exc())
|
logger.log_errmsg(traceback.format_exc())
|
||||||
|
|
@ -259,6 +275,8 @@ LOGIN_SCREEN_HELP = \
|
||||||
|
|
||||||
|
|
||||||
(return to go back)""" % settings.SERVERNAME
|
(return to go back)""" % settings.SERVERNAME
|
||||||
|
|
||||||
|
|
||||||
# Menu entry 4
|
# Menu entry 4
|
||||||
|
|
||||||
class CmdUnloggedinQuit(Command):
|
class CmdUnloggedinQuit(Command):
|
||||||
|
|
@ -285,7 +303,7 @@ START = MenuNode("START", text=utils.string_from_module(CONNECTION_SCREEN_MODULE
|
||||||
linktexts=["Log in with an existing account",
|
linktexts=["Log in with an existing account",
|
||||||
"Create a new account",
|
"Create a new account",
|
||||||
"Help",
|
"Help",
|
||||||
"Quit",],
|
"Quit"],
|
||||||
selectcmds=[None, None, None, CmdUnloggedinQuit])
|
selectcmds=[None, None, None, CmdUnloggedinQuit])
|
||||||
|
|
||||||
node1a = MenuNode("node1a", text="Please enter your account name (empty to abort).",
|
node1a = MenuNode("node1a", text="Please enter your account name (empty to abort).",
|
||||||
|
|
@ -325,13 +343,17 @@ class UnloggedInCmdSet(CmdSet):
|
||||||
"Cmdset for the unloggedin state"
|
"Cmdset for the unloggedin state"
|
||||||
key = "UnloggedinState"
|
key = "UnloggedinState"
|
||||||
priority = 0
|
priority = 0
|
||||||
|
|
||||||
def at_cmdset_creation(self):
|
def at_cmdset_creation(self):
|
||||||
|
"Called when cmdset is first created"
|
||||||
self.add(CmdUnloggedinLook())
|
self.add(CmdUnloggedinLook())
|
||||||
|
|
||||||
|
|
||||||
class CmdUnloggedinLook(Command):
|
class CmdUnloggedinLook(Command):
|
||||||
"""
|
"""
|
||||||
An unloggedin version of the look command. This is called by the server when the player
|
An unloggedin version of the look command. This is called by the server
|
||||||
first connects. It sets up the menu before handing off to the menu's own look command..
|
when the player first connects. It sets up the menu before handing off
|
||||||
|
to the menu's own look command..
|
||||||
"""
|
"""
|
||||||
key = CMD_LOGINSTART
|
key = CMD_LOGINSTART
|
||||||
aliases = ["look", "l"]
|
aliases = ["look", "l"]
|
||||||
|
|
@ -339,5 +361,7 @@ class CmdUnloggedinLook(Command):
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"Execute the menu"
|
"Execute the menu"
|
||||||
menu = MenuTree(self.caller, nodes=(START, node1a, node1b, node2a, node2b, node3), exec_end=None)
|
menu = MenuTree(self.caller, nodes=(START, node1a, node1b,
|
||||||
|
node2a, node2b, node3),
|
||||||
|
exec_end=None)
|
||||||
menu.start()
|
menu.start()
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,7 @@ class CmdMenuNode(Command):
|
||||||
else:
|
else:
|
||||||
self.caller.msg("{rThis option is not available.{n")
|
self.caller.msg("{rThis option is not available.{n")
|
||||||
|
|
||||||
|
|
||||||
class CmdMenuLook(default_cmds.CmdLook):
|
class CmdMenuLook(default_cmds.CmdLook):
|
||||||
"""
|
"""
|
||||||
ooc look
|
ooc look
|
||||||
|
|
@ -92,6 +93,7 @@ class CmdMenuLook(default_cmds.CmdLook):
|
||||||
# otherwise we use normal look
|
# otherwise we use normal look
|
||||||
super(CmdMenuLook, self).func()
|
super(CmdMenuLook, self).func()
|
||||||
|
|
||||||
|
|
||||||
class CmdMenuHelp(default_cmds.CmdHelp):
|
class CmdMenuHelp(default_cmds.CmdHelp):
|
||||||
"""
|
"""
|
||||||
help
|
help
|
||||||
|
|
@ -118,6 +120,7 @@ class CmdMenuHelp(default_cmds.CmdHelp):
|
||||||
# otherwise we use normal help
|
# otherwise we use normal help
|
||||||
super(CmdMenuHelp, self).func()
|
super(CmdMenuHelp, self).func()
|
||||||
|
|
||||||
|
|
||||||
class MenuCmdSet(CmdSet):
|
class MenuCmdSet(CmdSet):
|
||||||
"""
|
"""
|
||||||
Cmdset for the menu. Will replace all other commands.
|
Cmdset for the menu. Will replace all other commands.
|
||||||
|
|
@ -129,10 +132,12 @@ class MenuCmdSet(CmdSet):
|
||||||
key = "menucmdset"
|
key = "menucmdset"
|
||||||
priority = 1
|
priority = 1
|
||||||
mergetype = "Replace"
|
mergetype = "Replace"
|
||||||
|
|
||||||
def at_cmdset_creation(self):
|
def at_cmdset_creation(self):
|
||||||
"populate cmdset"
|
"populate cmdset"
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Menu Node system
|
# Menu Node system
|
||||||
#
|
#
|
||||||
|
|
@ -153,7 +158,8 @@ class MenuTree(object):
|
||||||
'START' and 'END' respectively.
|
'START' and 'END' respectively.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
def __init__(self, caller, nodes=None, startnode="START", endnode="END", exec_end="look"):
|
def __init__(self, caller, nodes=None,
|
||||||
|
startnode="START", endnode="END", exec_end="look"):
|
||||||
"""
|
"""
|
||||||
We specify startnode/endnode so that the system knows where to
|
We specify startnode/endnode so that the system knows where to
|
||||||
enter and where to exit the menu tree. If nodes is given, it
|
enter and where to exit the menu tree. If nodes is given, it
|
||||||
|
|
@ -194,7 +200,7 @@ class MenuTree(object):
|
||||||
# if we was given the END node key, we clean up immediately.
|
# if we was given the END node key, we clean up immediately.
|
||||||
self.caller.cmdset.delete("menucmdset")
|
self.caller.cmdset.delete("menucmdset")
|
||||||
del self.caller.db._menu_data
|
del self.caller.db._menu_data
|
||||||
if self.exec_end != None:
|
if self.exec_end is not None:
|
||||||
self.caller.execute_cmd(self.exec_end)
|
self.caller.execute_cmd(self.exec_end)
|
||||||
return
|
return
|
||||||
# not exiting, look for a valid code.
|
# not exiting, look for a valid code.
|
||||||
|
|
@ -205,13 +211,14 @@ class MenuTree(object):
|
||||||
# node. self.caller is available at this point.
|
# node. self.caller is available at this point.
|
||||||
try:
|
try:
|
||||||
exec(node.code)
|
exec(node.code)
|
||||||
except Exception, e:
|
except Exception:
|
||||||
self.caller.msg("{rCode could not be executed for node %s. Continuing anyway.{n" % key)
|
self.caller.msg("{rCode could not be executed for node %s. Continuing anyway.{n" % key)
|
||||||
# clean old menu cmdset and replace with the new one
|
# clean old menu cmdset and replace with the new one
|
||||||
self.caller.cmdset.delete("menucmdset")
|
self.caller.cmdset.delete("menucmdset")
|
||||||
self.caller.cmdset.add(node.cmdset)
|
self.caller.cmdset.add(node.cmdset)
|
||||||
# set the menu flag data for the default commands
|
# set the menu flag data for the default commands
|
||||||
self.caller.db._menu_data = {"help":node.helptext, "look":str(node.text)}
|
self.caller.db._menu_data = {"help": node.helptext,
|
||||||
|
"look": str(node.text)}
|
||||||
# display the node
|
# display the node
|
||||||
self.caller.msg(node.text)
|
self.caller.msg(node.text)
|
||||||
else:
|
else:
|
||||||
|
|
@ -226,27 +233,39 @@ class MenuNode(object):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
def __init__(self, key, text="", links=None, linktexts=None,
|
def __init__(self, key, text="", links=None, linktexts=None,
|
||||||
keywords=None, cols=1, helptext=None, selectcmds=None, code="", nodefaultcmds=False, separator=""):
|
keywords=None, cols=1, helptext=None,
|
||||||
|
selectcmds=None, code="", nodefaultcmds=False, separator=""):
|
||||||
"""
|
"""
|
||||||
key - the unique identifier of this node.
|
key - the unique identifier of this node.
|
||||||
text - is the text that will be displayed at top when viewing this node.
|
text - is the text that will be displayed at top when viewing this
|
||||||
links - a list of keys for unique menunodes this is connected to. The actual keys will not be
|
node.
|
||||||
printed - keywords will be used (or a number)
|
links - a list of keys for unique menunodes this is connected to.
|
||||||
linktexts - an optional list of texts to describe the links. Must match link list if defined. Entries can be None
|
The actual keys will not printed - keywords will be used
|
||||||
to not generate any extra text for a particular link.
|
(or a number)
|
||||||
keywords - an optional list of unique keys for choosing links. Must match links list. If not given, index numbers
|
linktexts - an optional list of texts to describe the links. Must
|
||||||
will be used. Also individual list entries can be None and will be replaed by indices.
|
match link list if defined. Entries can be None to not
|
||||||
If CMD_NOMATCH or CMD_NOENTRY, no text will be generated to indicate the option exists.
|
generate any extra text for a particular link.
|
||||||
|
keywords - an optional list of unique keys for choosing links. Must
|
||||||
|
match links list. If not given, index numbers will be used.
|
||||||
|
Also individual list entries can be None and will be replaed
|
||||||
|
by indices. If CMD_NOMATCH or CMD_NOENTRY, no text will be
|
||||||
|
generated to indicate the option exists.
|
||||||
cols - how many columns to use for displaying options.
|
cols - how many columns to use for displaying options.
|
||||||
helptext - if defined, this is shown when using the help command instead of the normal help index.
|
helptext - if defined, this is shown when using the help command
|
||||||
selectcmds- a list of custom cmdclasses for handling each option. Must match links list, but some entries
|
instead of the normal help index.
|
||||||
may be set to None to use default menu cmds. The given command's key will be used for the menu
|
selectcmds- a list of custom cmdclasses for handling each option.
|
||||||
list entry unless it's CMD_NOMATCH or CMD_NOENTRY, in which case no text will be generated. These
|
Must match links list, but some entries may be set to None
|
||||||
commands have access to self.menutree and so can be used to select nodes.
|
to use default menu cmds. The given command's key will be
|
||||||
code - functional code. This will be executed just before this node is loaded (i.e.
|
used for the menu list entry unless it's CMD_NOMATCH or
|
||||||
as soon after it's been selected from another node). self.caller is available
|
CMD_NOENTRY, in which case no text will be generated. These
|
||||||
to call from this code block, as well as ev.
|
commands have access to self.menutree and so can be used to
|
||||||
nodefaultcmds - if true, don't offer the default help and look commands in the node
|
select nodes.
|
||||||
|
code - functional code. This will be executed just before this
|
||||||
|
node is loaded (i.e. as soon after it's been selected from
|
||||||
|
another node). self.caller is available to call from this
|
||||||
|
code block, as well as ev.
|
||||||
|
nodefaultcmds - if true, don't offer the default help and look commands
|
||||||
|
in the node
|
||||||
separator - this string will be put on the line between menu nodes5B.
|
separator - this string will be put on the line between menu nodes5B.
|
||||||
"""
|
"""
|
||||||
self.key = key
|
self.key = key
|
||||||
|
|
@ -311,13 +330,14 @@ class MenuNode(object):
|
||||||
break
|
break
|
||||||
ftable = utils.format_table(cols)
|
ftable = utils.format_table(cols)
|
||||||
for row in ftable:
|
for row in ftable:
|
||||||
string +="\n" + "".join(row)
|
string += "\n" + "".join(row)
|
||||||
# store text
|
# store text
|
||||||
self.text = self.separator + "\n" + string.rstrip()
|
self.text = self.separator + "\n" + string.rstrip()
|
||||||
|
|
||||||
def init(self, menutree):
|
def init(self, menutree):
|
||||||
"""
|
"""
|
||||||
Called by menu tree. Initializes the commands needed by the menutree structure.
|
Called by menu tree. Initializes the commands needed by
|
||||||
|
the menutree structure.
|
||||||
"""
|
"""
|
||||||
# Create the relevant cmdset
|
# Create the relevant cmdset
|
||||||
self.cmdset = MenuCmdSet()
|
self.cmdset = MenuCmdSet()
|
||||||
|
|
@ -362,7 +382,8 @@ def prompt_yesno(caller, question="", yescode="", nocode="", default="N"):
|
||||||
cmdyes = CmdMenuNode()
|
cmdyes = CmdMenuNode()
|
||||||
cmdyes.key = "yes"
|
cmdyes.key = "yes"
|
||||||
cmdyes.aliases = ["y"]
|
cmdyes.aliases = ["y"]
|
||||||
# this will be executed in the context of the yes command (so self.caller will be available)
|
# this will be executed in the context of the yes command (so
|
||||||
|
# self.caller will be available)
|
||||||
cmdyes.code = yescode + "\nself.caller.cmdset.delete('menucmdset')\ndel self.caller.db._menu_data"
|
cmdyes.code = yescode + "\nself.caller.cmdset.delete('menucmdset')\ndel self.caller.db._menu_data"
|
||||||
|
|
||||||
cmdno = CmdMenuNode()
|
cmdno = CmdMenuNode()
|
||||||
|
|
@ -387,8 +408,8 @@ def prompt_yesno(caller, question="", yescode="", nocode="", default="N"):
|
||||||
yesnocmdset.add(defaultcmd)
|
yesnocmdset.add(defaultcmd)
|
||||||
|
|
||||||
# assinging menu data flags to caller.
|
# assinging menu data flags to caller.
|
||||||
caller.db._menu_data = {"help":"Please select Yes or No.",
|
caller.db._menu_data = {"help": "Please select Yes or No.",
|
||||||
"look":"Please select Yes or No."}
|
"look": "Please select Yes or No."}
|
||||||
# assign cmdset and ask question
|
# assign cmdset and ask question
|
||||||
caller.cmdset.add(yesnocmdset)
|
caller.cmdset.add(yesnocmdset)
|
||||||
if default == "Y":
|
if default == "Y":
|
||||||
|
|
@ -398,6 +419,7 @@ def prompt_yesno(caller, question="", yescode="", nocode="", default="N"):
|
||||||
prompt = "%s %s: " % (question, prompt)
|
prompt = "%s %s: " % (question, prompt)
|
||||||
caller.msg(prompt)
|
caller.msg(prompt)
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Menu command test
|
# Menu command test
|
||||||
#
|
#
|
||||||
|
|
@ -419,6 +441,7 @@ class CmdMenuTest(Command):
|
||||||
key = "menu"
|
key = "menu"
|
||||||
locks = "cmd:all()"
|
locks = "cmd:all()"
|
||||||
help_category = "Menu"
|
help_category = "Menu"
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"Testing the menu system"
|
"Testing the menu system"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
@ -36,6 +36,7 @@ from src.utils.utils import clean_object_caches, to_str
|
||||||
from src.utils import logger
|
from src.utils import logger
|
||||||
from src import PROC_MODIFIED_OBJS
|
from src import PROC_MODIFIED_OBJS
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Multiprocess command for communication Server<->Client, relaying
|
# Multiprocess command for communication Server<->Client, relaying
|
||||||
# data for remote Python execution
|
# data for remote Python execution
|
||||||
|
|
@ -64,6 +65,7 @@ class ExecuteCode(amp.Command):
|
||||||
response = [('response', amp.String()),
|
response = [('response', amp.String()),
|
||||||
('recached', amp.String())]
|
('recached', amp.String())]
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Multiprocess AMP client-side factory, for executing remote Python code
|
# Multiprocess AMP client-side factory, for executing remote Python code
|
||||||
#
|
#
|
||||||
|
|
@ -118,8 +120,7 @@ class PythonProcPoolChild(AMPChild):
|
||||||
return ""
|
return ""
|
||||||
_return = Ret()
|
_return = Ret()
|
||||||
|
|
||||||
|
available_vars = {'_return': _return}
|
||||||
available_vars = {'_return':_return}
|
|
||||||
if environment:
|
if environment:
|
||||||
# load environment
|
# load environment
|
||||||
try:
|
try:
|
||||||
|
|
@ -141,7 +142,8 @@ class PythonProcPoolChild(AMPChild):
|
||||||
# get the list of affected objects to recache
|
# get the list of affected objects to recache
|
||||||
objs = list(set(PROC_MODIFIED_OBJS))
|
objs = list(set(PROC_MODIFIED_OBJS))
|
||||||
# we need to include the locations too, to update their content caches
|
# we need to include the locations too, to update their content caches
|
||||||
objs = objs + list(set([o.location for o in objs if hasattr(o, "location") and o.location]))
|
objs = objs + list(set([o.location for o in objs
|
||||||
|
if hasattr(o, "location") and o.location]))
|
||||||
#print "objs:", objs
|
#print "objs:", objs
|
||||||
#print "to_pickle", to_pickle(objs, emptypickle=False, do_pickle=False)
|
#print "to_pickle", to_pickle(objs, emptypickle=False, do_pickle=False)
|
||||||
if objs not in (None, [], ()):
|
if objs not in (None, [], ()):
|
||||||
|
|
@ -156,13 +158,15 @@ class PythonProcPoolChild(AMPChild):
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Procpool run_async - Server-side access function for executing code in another process
|
# Procpool run_async - Server-side access function for executing
|
||||||
|
# code in another process
|
||||||
#
|
#
|
||||||
|
|
||||||
_PPOOL = None
|
_PPOOL = None
|
||||||
_SESSIONS = None
|
_SESSIONS = None
|
||||||
_PROC_ERR = "A process has ended with a probable error condition: process ended by signal 9."
|
_PROC_ERR = "A process has ended with a probable error condition: process ended by signal 9."
|
||||||
|
|
||||||
|
|
||||||
def run_async(to_execute, *args, **kwargs):
|
def run_async(to_execute, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Runs a function or executes a code snippet asynchronously.
|
Runs a function or executes a code snippet asynchronously.
|
||||||
|
|
@ -227,9 +231,9 @@ def run_async(to_execute, *args, **kwargs):
|
||||||
Use this function with restrain and only for features/commands
|
Use this function with restrain and only for features/commands
|
||||||
that you know has no influence on the cause-and-effect order of your
|
that you know has no influence on the cause-and-effect order of your
|
||||||
game (commands given after the async function might be executed before
|
game (commands given after the async function might be executed before
|
||||||
it has finished). Accessing the same property from different threads/processes
|
it has finished). Accessing the same property from different
|
||||||
can lead to unpredicted behaviour if you are not careful (this is called a
|
threads/processes can lead to unpredicted behaviour if you are not
|
||||||
"race condition").
|
careful (this is called a "race condition").
|
||||||
|
|
||||||
Also note that some databases, notably sqlite3, don't support access from
|
Also note that some databases, notably sqlite3, don't support access from
|
||||||
multiple threads simultaneously, so if you do heavy database access from
|
multiple threads simultaneously, so if you do heavy database access from
|
||||||
|
|
@ -243,7 +247,7 @@ def run_async(to_execute, *args, **kwargs):
|
||||||
# get the procpool name, if set in kwargs
|
# get the procpool name, if set in kwargs
|
||||||
procpool_name = kwargs.get("procpool_name", "PythonProcPool")
|
procpool_name = kwargs.get("procpool_name", "PythonProcPool")
|
||||||
|
|
||||||
if _PPOOL == None:
|
if _PPOOL is None:
|
||||||
# Try to load process Pool
|
# Try to load process Pool
|
||||||
from src.server.sessionhandler import SESSIONS as _SESSIONS
|
from src.server.sessionhandler import SESSIONS as _SESSIONS
|
||||||
try:
|
try:
|
||||||
|
|
@ -260,8 +264,10 @@ def run_async(to_execute, *args, **kwargs):
|
||||||
reca = ret["recached"] and from_pickle(do_unpickle(ret["recached"]))
|
reca = ret["recached"] and from_pickle(do_unpickle(ret["recached"]))
|
||||||
# recache all indicated objects
|
# recache all indicated objects
|
||||||
[clean_object_caches(obj) for obj in reca]
|
[clean_object_caches(obj) for obj in reca]
|
||||||
if f: return f(rval, *args, **kwargs)
|
if f:
|
||||||
else: return rval
|
return f(rval, *args, **kwargs)
|
||||||
|
else:
|
||||||
|
return rval
|
||||||
return func
|
return func
|
||||||
def convert_err(f):
|
def convert_err(f):
|
||||||
def func(err, *args, **kwargs):
|
def func(err, *args, **kwargs):
|
||||||
|
|
@ -287,18 +293,22 @@ def run_async(to_execute, *args, **kwargs):
|
||||||
# process pool is running
|
# process pool is running
|
||||||
if isinstance(to_execute, basestring):
|
if isinstance(to_execute, basestring):
|
||||||
# run source code in process pool
|
# run source code in process pool
|
||||||
cmdargs = {"_timeout":use_timeout}
|
cmdargs = {"_timeout": use_timeout}
|
||||||
cmdargs["source"] = to_str(to_execute)
|
cmdargs["source"] = to_str(to_execute)
|
||||||
if kwargs: cmdargs["environment"] = do_pickle(to_pickle(kwargs))
|
if kwargs:
|
||||||
else: cmdargs["environment"] = ""
|
cmdargs["environment"] = do_pickle(to_pickle(kwargs))
|
||||||
|
else:
|
||||||
|
cmdargs["environment"] = ""
|
||||||
# defer to process pool
|
# defer to process pool
|
||||||
deferred = _PPOOL.doWork(ExecuteCode, **cmdargs)
|
deferred = _PPOOL.doWork(ExecuteCode, **cmdargs)
|
||||||
elif callable(to_execute):
|
elif callable(to_execute):
|
||||||
# execute callable in process
|
# execute callable in process
|
||||||
callname = to_execute.__name__
|
callname = to_execute.__name__
|
||||||
cmdargs = {"_timeout":use_timeout}
|
cmdargs = {"_timeout": use_timeout}
|
||||||
cmdargs["source"] = "_return(%s(*args,**kwargs))" % callname
|
cmdargs["source"] = "_return(%s(*args,**kwargs))" % callname
|
||||||
cmdargs["environment"] = do_pickle(to_pickle({callname:to_execute, "args":args, "kwargs":kwargs}))
|
cmdargs["environment"] = do_pickle(to_pickle({callname: to_execute,
|
||||||
|
"args": args,
|
||||||
|
"kwargs": kwargs}))
|
||||||
deferred = _PPOOL.doWork(ExecuteCode, **cmdargs)
|
deferred = _PPOOL.doWork(ExecuteCode, **cmdargs)
|
||||||
else:
|
else:
|
||||||
raise RuntimeError("'%s' could not be handled by the process pool" % to_execute)
|
raise RuntimeError("'%s' could not be handled by the process pool" % to_execute)
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,7 @@ PROCPOOL_DIRECTORY = None
|
||||||
# don't need to change normally
|
# don't need to change normally
|
||||||
SERVICE_NAME = "PythonProcPool"
|
SERVICE_NAME = "PythonProcPool"
|
||||||
|
|
||||||
|
|
||||||
# plugin hook
|
# plugin hook
|
||||||
|
|
||||||
def start_plugin_services(server):
|
def start_plugin_services(server):
|
||||||
|
|
@ -87,8 +88,8 @@ def start_plugin_services(server):
|
||||||
os.path.join(os.pardir, "contrib", "procpools", "ampoule"),
|
os.path.join(os.pardir, "contrib", "procpools", "ampoule"),
|
||||||
os.path.join(os.pardir, "ev"),
|
os.path.join(os.pardir, "ev"),
|
||||||
"settings")
|
"settings")
|
||||||
aenv = {"DJANGO_SETTINGS_MODULE":"settings",
|
aenv = {"DJANGO_SETTINGS_MODULE": "settings",
|
||||||
"DATABASE_NAME":settings.DATABASES.get("default", {}).get("NAME") or settings.DATABASE_NAME}
|
"DATABASE_NAME": settings.DATABASES.get("default", {}).get("NAME") or settings.DATABASE_NAME}
|
||||||
if PROCPOOL_DEBUG:
|
if PROCPOOL_DEBUG:
|
||||||
_BOOTSTRAP = _BOOTSTRAP % "log.startLogging(sys.stderr)"
|
_BOOTSTRAP = _BOOTSTRAP % "log.startLogging(sys.stderr)"
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -54,8 +54,9 @@ class CmdTalk(default_cmds.MuxCommand):
|
||||||
|
|
||||||
self.caller.msg("(You walk up and talk to %s.)" % self.obj.key)
|
self.caller.msg("(You walk up and talk to %s.)" % self.obj.key)
|
||||||
|
|
||||||
# conversation is a dictionary of keys, each pointing to a dictionary defining
|
# conversation is a dictionary of keys, each pointing to
|
||||||
# the keyword arguments to the MenuNode constructor.
|
# a dictionary defining the keyword arguments to the MenuNode
|
||||||
|
# constructor.
|
||||||
conversation = obj.db.conversation
|
conversation = obj.db.conversation
|
||||||
if not conversation:
|
if not conversation:
|
||||||
self.caller.msg("%s says: 'Sorry, I don't have time to talk right now.'" % (self.obj.key))
|
self.caller.msg("%s says: 'Sorry, I don't have time to talk right now.'" % (self.obj.key))
|
||||||
|
|
@ -67,9 +68,11 @@ class CmdTalk(default_cmds.MuxCommand):
|
||||||
menu.add(menusystem.MenuNode(key, **kwargs))
|
menu.add(menusystem.MenuNode(key, **kwargs))
|
||||||
menu.start()
|
menu.start()
|
||||||
|
|
||||||
|
|
||||||
class TalkingCmdSet(CmdSet):
|
class TalkingCmdSet(CmdSet):
|
||||||
"Stores the talk command."
|
"Stores the talk command."
|
||||||
key = "talkingcmdset"
|
key = "talkingcmdset"
|
||||||
|
|
||||||
def at_cmdset_creation(self):
|
def at_cmdset_creation(self):
|
||||||
"populates the cmdset"
|
"populates the cmdset"
|
||||||
self.add(CmdTalk())
|
self.add(CmdTalk())
|
||||||
|
|
@ -79,33 +82,34 @@ class TalkingCmdSet(CmdSet):
|
||||||
# (This could be in a separate module too)
|
# (This could be in a separate module too)
|
||||||
#
|
#
|
||||||
|
|
||||||
CONV = {"START":{"text": "Hello there, how can I help you?",
|
CONV = {"START": {"text": "Hello there, how can I help you?",
|
||||||
"links":["info1", "info2"],
|
"links": ["info1", "info2"],
|
||||||
"linktexts":["Hey, do you know what this 'Evennia' thing is all about?",
|
"linktexts": ["Hey, do you know what this 'Evennia' thing is all about?",
|
||||||
"What's your name, little NPC?"],
|
"What's your name, little NPC?"],
|
||||||
"keywords":None,
|
"keywords": None,
|
||||||
"code":None},
|
"code": None},
|
||||||
"info1":{"text": "Oh, Evennia is where you are right now! Don't you feel the power?",
|
"info1": {"text": "Oh, Evennia is where you are right now! Don't you feel the power?",
|
||||||
"links":["info3", "info2", "END"],
|
"links": ["info3", "info2", "END"],
|
||||||
"linktexts":["Sure, *I* do, not sure how you do though. You are just an NPC.",
|
"linktexts":["Sure, *I* do, not sure how you do though. You are just an NPC.",
|
||||||
"Sure I do. What's yer name, NPC?",
|
"Sure I do. What's yer name, NPC?",
|
||||||
"Ok, bye for now then."],
|
"Ok, bye for now then."],
|
||||||
"keywords":None,
|
"keywords": None,
|
||||||
"code":None},
|
"code": None},
|
||||||
"info2":{"text":"My name is not really important ... I'm just an NPC after all.",
|
"info2": {"text": "My name is not really important ... I'm just an NPC after all.",
|
||||||
"links":["info3", "info1"],
|
"links": ["info3", "info1"],
|
||||||
"linktexts":["I didn't really want to know it anyhow.",
|
"linktexts": ["I didn't really want to know it anyhow.",
|
||||||
"Okay then, so what's this 'Evennia' thing about?"],
|
"Okay then, so what's this 'Evennia' thing about?"],
|
||||||
"keywords":None,
|
"keywords": None,
|
||||||
"code":None},
|
"code": None},
|
||||||
"info3":{"text":"Well ... I'm sort of busy so, have to go. NPC business. Important stuff. You wouldn't understand.",
|
"info3": {"text": "Well ... I'm sort of busy so, have to go. NPC business. Important stuff. You wouldn't understand.",
|
||||||
"links":["END", "info2"],
|
"links": ["END", "info2"],
|
||||||
"linktexts":["Oookay ... I won't keep you. Bye.",
|
"linktexts": ["Oookay ... I won't keep you. Bye.",
|
||||||
"Wait, why don't you tell me your name first?"],
|
"Wait, why don't you tell me your name first?"],
|
||||||
"keywords":None,
|
"keywords": None,
|
||||||
"code":None},
|
"code": None},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class TalkingNPC(Object):
|
class TalkingNPC(Object):
|
||||||
"""
|
"""
|
||||||
This implements a simple Object using the talk command and using the
|
This implements a simple Object using the talk command and using the
|
||||||
|
|
@ -118,4 +122,4 @@ class TalkingNPC(Object):
|
||||||
self.db.conversation = CONV
|
self.db.conversation = CONV
|
||||||
self.db.desc = "This is a talkative NPC."
|
self.db.desc = "This is a talkative NPC."
|
||||||
# assign the talk command to npc
|
# assign the talk command to npc
|
||||||
self.cmdset.add_default(TalkingCmdSet, permanent=True)
|
self.cmdset.add_default(TalkingCmdSet, permanent=True)
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
@ -14,6 +14,7 @@ from contrib.tutorial_world import scripts as tut_scripts
|
||||||
|
|
||||||
BASE_CHARACTER_TYPECLASS = settings.BASE_CHARACTER_TYPECLASS
|
BASE_CHARACTER_TYPECLASS = settings.BASE_CHARACTER_TYPECLASS
|
||||||
|
|
||||||
|
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
#
|
#
|
||||||
# Mob - mobile object
|
# Mob - mobile object
|
||||||
|
|
@ -52,15 +53,18 @@ class Mob(tut_objects.TutorialObject):
|
||||||
def update_irregular(self):
|
def update_irregular(self):
|
||||||
"Called at irregular intervals. Moves the mob."
|
"Called at irregular intervals. Moves the mob."
|
||||||
if self.roam_mode:
|
if self.roam_mode:
|
||||||
exits = [ex for ex in self.location.exits if ex.access(self, "traverse")]
|
exits = [ex for ex in self.location.exits
|
||||||
|
if ex.access(self, "traverse")]
|
||||||
if exits:
|
if exits:
|
||||||
# Try to make it so the mob doesn't backtrack.
|
# Try to make it so the mob doesn't backtrack.
|
||||||
new_exits = [ex for ex in exits if ex.destination != self.db.last_location]
|
new_exits = [ex for ex in exits
|
||||||
|
if ex.destination != self.db.last_location]
|
||||||
if new_exits:
|
if new_exits:
|
||||||
exits = new_exits
|
exits = new_exits
|
||||||
self.db.last_location = self.location
|
self.db.last_location = self.location
|
||||||
# execute_cmd() allows the mob to respect exit and exit-command locks,
|
# execute_cmd() allows the mob to respect exit and
|
||||||
# but may pose a problem if there is more than one exit with the same name.
|
# exit-command locks, but may pose a problem if there is more
|
||||||
|
# than one exit with the same name.
|
||||||
# - see Enemy example for another way to move
|
# - see Enemy example for another way to move
|
||||||
self.execute_cmd("%s" % exits[random.randint(0, len(exits) - 1)].key)
|
self.execute_cmd("%s" % exits[random.randint(0, len(exits) - 1)].key)
|
||||||
|
|
||||||
|
|
@ -100,7 +104,8 @@ class AttackTimer(Script):
|
||||||
"Called every self.interval seconds."
|
"Called every self.interval seconds."
|
||||||
if self.obj.db.inactive:
|
if self.obj.db.inactive:
|
||||||
return
|
return
|
||||||
#print "attack timer: at_repeat", self.dbobj.id, self.ndb.twisted_task, id(self.ndb.twisted_task)
|
#print "attack timer: at_repeat", self.dbobj.id, self.ndb.twisted_task,
|
||||||
|
# id(self.ndb.twisted_task)
|
||||||
if self.obj.db.roam_mode:
|
if self.obj.db.roam_mode:
|
||||||
self.obj.roam()
|
self.obj.roam()
|
||||||
#return
|
#return
|
||||||
|
|
@ -119,10 +124,12 @@ class AttackTimer(Script):
|
||||||
if (time.time() - self.obj.db.dead_at) > self.obj.db.dead_timer:
|
if (time.time() - self.obj.db.dead_at) > self.obj.db.dead_timer:
|
||||||
self.obj.reset()
|
self.obj.reset()
|
||||||
|
|
||||||
|
|
||||||
class Enemy(Mob):
|
class Enemy(Mob):
|
||||||
"""
|
"""
|
||||||
This is a ghostly enemy with health (hit points). Their chance to hit, damage etc is
|
This is a ghostly enemy with health (hit points). Their chance to hit,
|
||||||
determined by the weapon they are wielding, same as characters.
|
damage etc is determined by the weapon they are wielding, same as
|
||||||
|
characters.
|
||||||
|
|
||||||
An enemy can be in four modes:
|
An enemy can be in four modes:
|
||||||
roam (inherited from Mob) - where it just moves around randomly
|
roam (inherited from Mob) - where it just moves around randomly
|
||||||
|
|
@ -133,12 +140,16 @@ class Enemy(Mob):
|
||||||
Upon creation, the following attributes describe the enemy's actions
|
Upon creation, the following attributes describe the enemy's actions
|
||||||
desc - description
|
desc - description
|
||||||
full_health - integer number > 0
|
full_health - integer number > 0
|
||||||
defeat_location - unique name or #dbref to the location the player is taken when defeated. If not given, will remain in room.
|
defeat_location - unique name or #dbref to the location the player is
|
||||||
defeat_text - text to show player when they are defeated (just before being whisped away to defeat_location)
|
taken when defeated. If not given, will remain in room.
|
||||||
defeat_text_room - text to show other players in room when a player is defeated
|
defeat_text - text to show player when they are defeated (just before
|
||||||
|
being whisped away to defeat_location)
|
||||||
|
defeat_text_room - text to show other players in room when a player
|
||||||
|
is defeated
|
||||||
win_text - text to show player when defeating the enemy
|
win_text - text to show player when defeating the enemy
|
||||||
win_text_room - text to show room when a player defeates the enemy
|
win_text_room - text to show room when a player defeates the enemy
|
||||||
respawn_text - text to echo to room when the mob is reset/respawn in that room.
|
respawn_text - text to echo to room when the mob is reset/respawn in
|
||||||
|
that room.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
def at_object_creation(self):
|
def at_object_creation(self):
|
||||||
|
|
@ -157,7 +168,8 @@ class Enemy(Mob):
|
||||||
self.db.health = 20
|
self.db.health = 20
|
||||||
self.db.dead_at = time.time()
|
self.db.dead_at = time.time()
|
||||||
self.db.dead_timer = 100 # how long to stay dead
|
self.db.dead_timer = 100 # how long to stay dead
|
||||||
self.db.inactive = True # this is used during creation to make sure the mob doesn't move away
|
# this is used during creation to make sure the mob doesn't move away
|
||||||
|
self.db.inactive = True
|
||||||
# store the last player to hit
|
# store the last player to hit
|
||||||
self.db.last_attacker = None
|
self.db.last_attacker = None
|
||||||
# where to take defeated enemies
|
# where to take defeated enemies
|
||||||
|
|
@ -185,10 +197,12 @@ class Enemy(Mob):
|
||||||
|
|
||||||
elif random.random() < 0.2:
|
elif random.random() < 0.2:
|
||||||
# no players to attack, move about randomly.
|
# no players to attack, move about randomly.
|
||||||
exits = [ex.destination for ex in self.location.exits if ex.access(self, "traverse")]
|
exits = [ex.destination for ex in self.location.exits
|
||||||
|
if ex.access(self, "traverse")]
|
||||||
if exits:
|
if exits:
|
||||||
# Try to make it so the mob doesn't backtrack.
|
# Try to make it so the mob doesn't backtrack.
|
||||||
new_exits = [ex for ex in exits if ex.destination != self.db.last_location]
|
new_exits = [ex for ex in exits
|
||||||
|
if ex.destination != self.db.last_location]
|
||||||
if new_exits:
|
if new_exits:
|
||||||
exits = new_exits
|
exits = new_exits
|
||||||
self.db.last_location = self.location
|
self.db.last_location = self.location
|
||||||
|
|
@ -224,7 +238,8 @@ class Enemy(Mob):
|
||||||
|
|
||||||
# analyze result.
|
# analyze result.
|
||||||
if target.db.health <= 0:
|
if target.db.health <= 0:
|
||||||
# we reduced enemy to 0 health. Whisp them off to the prison room.
|
# we reduced enemy to 0 health. Whisp them off to
|
||||||
|
# the prison room.
|
||||||
tloc = search_object(self.db.defeat_location)
|
tloc = search_object(self.db.defeat_location)
|
||||||
tstring = self.db.defeat_text
|
tstring = self.db.defeat_text
|
||||||
if not tstring:
|
if not tstring:
|
||||||
|
|
@ -235,7 +250,8 @@ class Enemy(Mob):
|
||||||
if tloc:
|
if tloc:
|
||||||
if not ostring:
|
if not ostring:
|
||||||
ostring = "\n%s envelops the fallen ... and then their body is suddenly gone!" % self.key
|
ostring = "\n%s envelops the fallen ... and then their body is suddenly gone!" % self.key
|
||||||
# silently move the player to defeat location (we need to call hook manually)
|
# silently move the player to defeat location
|
||||||
|
# (we need to call hook manually)
|
||||||
target.location = tloc[0]
|
target.location = tloc[0]
|
||||||
tloc[0].at_object_receive(target, self.location)
|
tloc[0].at_object_receive(target, self.location)
|
||||||
elif not ostring:
|
elif not ostring:
|
||||||
|
|
@ -246,7 +262,8 @@ class Enemy(Mob):
|
||||||
self.roam_mode = False
|
self.roam_mode = False
|
||||||
self.pursue_mode = True
|
self.pursue_mode = True
|
||||||
else:
|
else:
|
||||||
# no players found, this could mean they have fled. Switch to pursue mode.
|
# no players found, this could mean they have fled.
|
||||||
|
# Switch to pursue mode.
|
||||||
self.battle_mode = False
|
self.battle_mode = False
|
||||||
self.roam_mode = False
|
self.roam_mode = False
|
||||||
self.pursue_mode = True
|
self.pursue_mode = True
|
||||||
|
|
@ -259,20 +276,24 @@ class Enemy(Mob):
|
||||||
last_attacker = self.db.last_attacker
|
last_attacker = self.db.last_attacker
|
||||||
players = [obj for obj in self.location.contents if utils.inherits_from(obj, BASE_CHARACTER_TYPECLASS) and not obj.is_superuser]
|
players = [obj for obj in self.location.contents if utils.inherits_from(obj, BASE_CHARACTER_TYPECLASS) and not obj.is_superuser]
|
||||||
if players:
|
if players:
|
||||||
# we found players in the room. Maybe we caught up with some, or some walked in on us
|
# we found players in the room. Maybe we caught up with some,
|
||||||
# before we had time to pursue them. Switch to battle mode.
|
# or some walked in on us before we had time to pursue them.
|
||||||
|
# Switch to battle mode.
|
||||||
self.battle_mode = True
|
self.battle_mode = True
|
||||||
self.roam_mode = False
|
self.roam_mode = False
|
||||||
self.pursue_mode = False
|
self.pursue_mode = False
|
||||||
else:
|
else:
|
||||||
# find all possible destinations.
|
# find all possible destinations.
|
||||||
destinations = [ex.destination for ex in self.location.exits if ex.access(self, "traverse")]
|
destinations = [ex.destination for ex in self.location.exits
|
||||||
# find all players in the possible destinations. OBS-we cannot just use the player's
|
if ex.access(self, "traverse")]
|
||||||
# current position to move the Enemy; this might have changed when the move is performed,
|
# find all players in the possible destinations. OBS-we cannot
|
||||||
# causing the enemy to teleport out of bounds.
|
# just use the player's current position to move the Enemy; this
|
||||||
|
# might have changed when the move is performed, causing the enemy
|
||||||
|
# to teleport out of bounds.
|
||||||
players = {}
|
players = {}
|
||||||
for dest in destinations:
|
for dest in destinations:
|
||||||
for obj in [o for o in dest.contents if utils.inherits_from(o, BASE_CHARACTER_TYPECLASS)]:
|
for obj in [o for o in dest.contents
|
||||||
|
if utils.inherits_from(o, BASE_CHARACTER_TYPECLASS)]:
|
||||||
players[obj] = dest
|
players[obj] = dest
|
||||||
if players:
|
if players:
|
||||||
# we found targets. Move to intercept.
|
# we found targets. Move to intercept.
|
||||||
|
|
@ -335,7 +356,8 @@ class Enemy(Mob):
|
||||||
string += "You fear it's only a matter of time before it materializes somewhere again."
|
string += "You fear it's only a matter of time before it materializes somewhere again."
|
||||||
self.location.msg_contents(string, exclude=[attacker])
|
self.location.msg_contents(string, exclude=[attacker])
|
||||||
|
|
||||||
# put enemy in dead mode and hide it from view. AttackTimer will bring it back later.
|
# put mob in dead mode and hide it from view.
|
||||||
|
# AttackTimer will bring it back later.
|
||||||
self.db.dead_at = time.time()
|
self.db.dead_at = time.time()
|
||||||
self.db.roam_mode = False
|
self.db.roam_mode = False
|
||||||
self.db.pursue_mode = False
|
self.db.pursue_mode = False
|
||||||
|
|
@ -347,7 +369,9 @@ class Enemy(Mob):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def reset(self):
|
def reset(self):
|
||||||
"If the mob was 'dead', respawn it to its home position and reset all modes and damage."
|
"""
|
||||||
|
If the mob was 'dead', respawn it to its home position and reset
|
||||||
|
all modes and damage."""
|
||||||
if self.db.dead_mode:
|
if self.db.dead_mode:
|
||||||
self.db.health = self.db.full_health
|
self.db.health = self.db.full_health
|
||||||
self.db.roam_mode = True
|
self.db.roam_mode = True
|
||||||
|
|
@ -358,4 +382,4 @@ class Enemy(Mob):
|
||||||
string = self.db.respawn_text
|
string = self.db.respawn_text
|
||||||
if not string:
|
if not string:
|
||||||
string = "%s fades into existence from out of thin air. It's looking pissed." % self.key
|
string = "%s fades into existence from out of thin air. It's looking pissed." % self.key
|
||||||
self.location.msg_contents(string)
|
self.location.msg_contents(string)
|
||||||
|
|
@ -19,9 +19,10 @@ WeaponRack
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import time, random
|
import time
|
||||||
|
import random
|
||||||
|
|
||||||
from ev import utils, create_object
|
from ev import create_object
|
||||||
from ev import Object, Exit, Command, CmdSet, Script
|
from ev import Object, Exit, Command, CmdSet, Script
|
||||||
|
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
|
|
@ -91,12 +92,14 @@ class CmdRead(Command):
|
||||||
string = "There is nothing to read on %s." % obj.key
|
string = "There is nothing to read on %s." % obj.key
|
||||||
self.caller.msg(string)
|
self.caller.msg(string)
|
||||||
|
|
||||||
|
|
||||||
class CmdSetReadable(CmdSet):
|
class CmdSetReadable(CmdSet):
|
||||||
"CmdSet for readables"
|
"CmdSet for readables"
|
||||||
def at_cmdset_creation(self):
|
def at_cmdset_creation(self):
|
||||||
"called when object is created."
|
"called when object is created."
|
||||||
self.add(CmdRead())
|
self.add(CmdRead())
|
||||||
|
|
||||||
|
|
||||||
class Readable(TutorialObject):
|
class Readable(TutorialObject):
|
||||||
"""
|
"""
|
||||||
This object defines some attributes and defines a read method on itself.
|
This object defines some attributes and defines a read method on itself.
|
||||||
|
|
@ -147,6 +150,7 @@ class CmdClimb(Command):
|
||||||
self.caller.msg(ostring)
|
self.caller.msg(ostring)
|
||||||
self.caller.db.last_climbed = self.obj
|
self.caller.db.last_climbed = self.obj
|
||||||
|
|
||||||
|
|
||||||
class CmdSetClimbable(CmdSet):
|
class CmdSetClimbable(CmdSet):
|
||||||
"Climbing cmdset"
|
"Climbing cmdset"
|
||||||
def at_cmdset_creation(self):
|
def at_cmdset_creation(self):
|
||||||
|
|
@ -182,6 +186,7 @@ OBELISK_DESCS = ["You can briefly make out the image of {ba woman with a blue bi
|
||||||
"You think you can see the outline of {ba flaming shield{n in the stone.",
|
"You think you can see the outline of {ba flaming shield{n in the stone.",
|
||||||
"The surface for a moment seems to portray {ba woman fighting a beast{n."]
|
"The surface for a moment seems to portray {ba woman fighting a beast{n."]
|
||||||
|
|
||||||
|
|
||||||
class Obelisk(TutorialObject):
|
class Obelisk(TutorialObject):
|
||||||
"""
|
"""
|
||||||
This object changes its description randomly.
|
This object changes its description randomly.
|
||||||
|
|
@ -196,7 +201,7 @@ class Obelisk(TutorialObject):
|
||||||
|
|
||||||
def return_appearance(self, caller):
|
def return_appearance(self, caller):
|
||||||
"Overload the default version of this hook."
|
"Overload the default version of this hook."
|
||||||
clueindex = random.randint(0, len(OBELISK_DESCS)-1)
|
clueindex = random.randint(0, len(OBELISK_DESCS) - 1)
|
||||||
# set this description
|
# set this description
|
||||||
string = "The surface of the obelisk seem to waver, shift and writhe under your gaze, with "
|
string = "The surface of the obelisk seem to waver, shift and writhe under your gaze, with "
|
||||||
string += "different scenes and structures appearing whenever you look at it. "
|
string += "different scenes and structures appearing whenever you look at it. "
|
||||||
|
|
@ -206,6 +211,7 @@ class Obelisk(TutorialObject):
|
||||||
# call the parent function as normal (this will use db.desc we just set)
|
# call the parent function as normal (this will use db.desc we just set)
|
||||||
return super(Obelisk, self).return_appearance(caller)
|
return super(Obelisk, self).return_appearance(caller)
|
||||||
|
|
||||||
|
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
#
|
#
|
||||||
# LightSource
|
# LightSource
|
||||||
|
|
@ -237,6 +243,7 @@ class StateLightSourceOn(Script):
|
||||||
self.db.script_started = time.time()
|
self.db.script_started = time.time()
|
||||||
|
|
||||||
def at_repeat(self):
|
def at_repeat(self):
|
||||||
|
"Called at self.interval seconds"
|
||||||
# this is only called when torch has burnt out
|
# this is only called when torch has burnt out
|
||||||
self.obj.db.burntime = -1
|
self.obj.db.burntime = -1
|
||||||
self.obj.reset()
|
self.obj.reset()
|
||||||
|
|
@ -262,13 +269,14 @@ class StateLightSourceOn(Script):
|
||||||
"This script is only valid as long as the lightsource burns."
|
"This script is only valid as long as the lightsource burns."
|
||||||
return self.obj.db.is_active
|
return self.obj.db.is_active
|
||||||
|
|
||||||
|
|
||||||
class CmdLightSourceOn(Command):
|
class CmdLightSourceOn(Command):
|
||||||
"""
|
"""
|
||||||
Switches on the lightsource.
|
Switches on the lightsource.
|
||||||
"""
|
"""
|
||||||
key = "on"
|
key = "on"
|
||||||
aliases = ["switch on", "turn on", "light"]
|
aliases = ["switch on", "turn on", "light"]
|
||||||
locks = "cmd:holds()" # only allow if command.obj is carried by caller.
|
locks = "cmd:holds()" # only allow if command.obj is carried by caller.
|
||||||
help_category = "TutorialWorld"
|
help_category = "TutorialWorld"
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
|
|
@ -283,18 +291,19 @@ class CmdLightSourceOn(Command):
|
||||||
self.obj.scripts.add(StateLightSourceOn)
|
self.obj.scripts.add(StateLightSourceOn)
|
||||||
self.caller.msg("{gYou light {C%s.{n" % self.obj.key)
|
self.caller.msg("{gYou light {C%s.{n" % self.obj.key)
|
||||||
self.caller.location.msg_contents("%s lights %s!" % (self.caller, self.obj.key), exclude=[self.caller])
|
self.caller.location.msg_contents("%s lights %s!" % (self.caller, self.obj.key), exclude=[self.caller])
|
||||||
# we run script validation on the room to make light/dark states tick.
|
# run script validation on the room to make light/dark states tick.
|
||||||
self.caller.location.scripts.validate()
|
self.caller.location.scripts.validate()
|
||||||
# look around
|
# look around
|
||||||
self.caller.execute_cmd("look")
|
self.caller.execute_cmd("look")
|
||||||
|
|
||||||
|
|
||||||
class CmdLightSourceOff(Command):
|
class CmdLightSourceOff(Command):
|
||||||
"""
|
"""
|
||||||
Switch off the lightsource.
|
Switch off the lightsource.
|
||||||
"""
|
"""
|
||||||
key = "off"
|
key = "off"
|
||||||
aliases = ["switch off", "turn off", "dowse"]
|
aliases = ["switch off", "turn off", "dowse"]
|
||||||
locks = "cmd:holds()" # only allow if command.obj is carried by caller.
|
locks = "cmd:holds()" # only allow if command.obj is carried by caller.
|
||||||
help_category = "TutorialWorld"
|
help_category = "TutorialWorld"
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
|
|
@ -311,38 +320,39 @@ class CmdLightSourceOff(Command):
|
||||||
self.caller.location.msg_contents("%s dowses %s." % (self.caller, self.obj.key), exclude=[self.caller])
|
self.caller.location.msg_contents("%s dowses %s." % (self.caller, self.obj.key), exclude=[self.caller])
|
||||||
self.caller.location.scripts.validate()
|
self.caller.location.scripts.validate()
|
||||||
self.caller.execute_cmd("look")
|
self.caller.execute_cmd("look")
|
||||||
# we run script validation on the room to make light/dark states tick.
|
|
||||||
|
|
||||||
|
|
||||||
class CmdSetLightSource(CmdSet):
|
class CmdSetLightSource(CmdSet):
|
||||||
"CmdSet for the lightsource commands"
|
"CmdSet for the lightsource commands"
|
||||||
key = "lightsource_cmdset"
|
key = "lightsource_cmdset"
|
||||||
|
|
||||||
def at_cmdset_creation(self):
|
def at_cmdset_creation(self):
|
||||||
"called at cmdset creation"
|
"called at cmdset creation"
|
||||||
self.add(CmdLightSourceOn())
|
self.add(CmdLightSourceOn())
|
||||||
self.add(CmdLightSourceOff())
|
self.add(CmdLightSourceOff())
|
||||||
|
|
||||||
|
|
||||||
class LightSource(TutorialObject):
|
class LightSource(TutorialObject):
|
||||||
"""
|
"""
|
||||||
This implements a light source object.
|
This implements a light source object.
|
||||||
|
|
||||||
When burned out, lightsource will be moved to its home - which by default is the
|
When burned out, lightsource will be moved to its home - which by
|
||||||
location it was first created at.
|
default is the location it was first created at.
|
||||||
"""
|
"""
|
||||||
def at_object_creation(self):
|
def at_object_creation(self):
|
||||||
"Called when object is first created."
|
"Called when object is first created."
|
||||||
super(LightSource, self).at_object_creation()
|
super(LightSource, self).at_object_creation()
|
||||||
self.db.tutorial_info = "This object can be turned on off and has a timed script controlling it."
|
self.db.tutorial_info = "This object can be turned on off and has a timed script controlling it."
|
||||||
self.db.is_active = False
|
self.db.is_active = False
|
||||||
self.db.burntime = 60*3 # 3 minutes
|
self.db.burntime = 60 * 3 # 3 minutes
|
||||||
self.db.desc = "A splinter of wood with remnants of resin on it, enough for burning."
|
self.db.desc = "A splinter of wood with remnants of resin on it, enough for burning."
|
||||||
# add commands
|
# add commands
|
||||||
self.cmdset.add_default(CmdSetLightSource, permanent=True)
|
self.cmdset.add_default(CmdSetLightSource, permanent=True)
|
||||||
|
|
||||||
def reset(self):
|
def reset(self):
|
||||||
"""
|
"""
|
||||||
Can be called by tutorial world runner, or by the script when the lightsource
|
Can be called by tutorial world runner, or by the script when
|
||||||
has burned out.
|
the lightsource has burned out.
|
||||||
"""
|
"""
|
||||||
if self.db.burntime <= 0:
|
if self.db.burntime <= 0:
|
||||||
# light burned out. Since the lightsources's "location" should be
|
# light burned out. Since the lightsources's "location" should be
|
||||||
|
|
@ -360,10 +370,11 @@ class LightSource(TutorialObject):
|
||||||
# maybe it was dropped, try validating at current location.
|
# maybe it was dropped, try validating at current location.
|
||||||
try:
|
try:
|
||||||
self.location.scripts.validate()
|
self.location.scripts.validate()
|
||||||
except AttributeError,e:
|
except AttributeError:
|
||||||
pass
|
pass
|
||||||
self.delete()
|
self.delete()
|
||||||
|
|
||||||
|
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
#
|
#
|
||||||
# Crumbling wall - unique exit
|
# Crumbling wall - unique exit
|
||||||
|
|
@ -473,7 +484,7 @@ class CmdShiftRoot(Command):
|
||||||
root_pos["green"] += 1
|
root_pos["green"] += 1
|
||||||
self.caller.msg("The green weedy root falls down.")
|
self.caller.msg("The green weedy root falls down.")
|
||||||
elif direction == "down":
|
elif direction == "down":
|
||||||
root_pos[color] = min(1, root_pos[color] +1)
|
root_pos[color] = min(1, root_pos[color] + 1)
|
||||||
self.caller.msg("You shove the root adorned with small yellow flowers downwards.")
|
self.caller.msg("You shove the root adorned with small yellow flowers downwards.")
|
||||||
if root_pos[color] != 0 and root_pos[color] == root_pos["green"]:
|
if root_pos[color] != 0 and root_pos[color] == root_pos["green"]:
|
||||||
root_pos["green"] -= 1
|
root_pos["green"] -= 1
|
||||||
|
|
@ -502,13 +513,15 @@ class CmdShiftRoot(Command):
|
||||||
self.caller.db.crumbling_wall_found_button = True
|
self.caller.db.crumbling_wall_found_button = True
|
||||||
self.caller.msg("Holding aside the root you think you notice something behind it ...")
|
self.caller.msg("Holding aside the root you think you notice something behind it ...")
|
||||||
|
|
||||||
|
|
||||||
class CmdPressButton(Command):
|
class CmdPressButton(Command):
|
||||||
"""
|
"""
|
||||||
Presses a button.
|
Presses a button.
|
||||||
"""
|
"""
|
||||||
key = "press"
|
key = "press"
|
||||||
aliases = ["press button", "button", "push", "push button"]
|
aliases = ["press button", "button", "push", "push button"]
|
||||||
locks = "cmd:attr(crumbling_wall_found_button) and not locattr(is_dark)" # only accessible if the button was found and there is light.
|
# only accessible if the button was found and there is light.
|
||||||
|
locks = "cmd:attr(crumbling_wall_found_button) and not locattr(is_dark)"
|
||||||
help_category = "TutorialWorld"
|
help_category = "TutorialWorld"
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
|
|
@ -534,14 +547,17 @@ class CmdPressButton(Command):
|
||||||
self.obj.destination = eloc
|
self.obj.destination = eloc
|
||||||
self.caller.msg(string)
|
self.caller.msg(string)
|
||||||
|
|
||||||
|
|
||||||
class CmdSetCrumblingWall(CmdSet):
|
class CmdSetCrumblingWall(CmdSet):
|
||||||
"Group the commands for crumblingWall"
|
"Group the commands for crumblingWall"
|
||||||
key = "crumblingwall_cmdset"
|
key = "crumblingwall_cmdset"
|
||||||
|
|
||||||
def at_cmdset_creation(self):
|
def at_cmdset_creation(self):
|
||||||
"called when object is first created."
|
"called when object is first created."
|
||||||
self.add(CmdShiftRoot())
|
self.add(CmdShiftRoot())
|
||||||
self.add(CmdPressButton())
|
self.add(CmdPressButton())
|
||||||
|
|
||||||
|
|
||||||
class CrumblingWall(TutorialObject, Exit):
|
class CrumblingWall(TutorialObject, Exit):
|
||||||
"""
|
"""
|
||||||
The CrumblingWall can be examined in various
|
The CrumblingWall can be examined in various
|
||||||
|
|
@ -559,24 +575,28 @@ class CrumblingWall(TutorialObject, Exit):
|
||||||
"called when the object is first created."
|
"called when the object is first created."
|
||||||
super(CrumblingWall, self).at_object_creation()
|
super(CrumblingWall, self).at_object_creation()
|
||||||
|
|
||||||
self.aliases.add(["secret passage", "passage", "crack", "opening", "secret door"])
|
self.aliases.add(["secret passage", "passage",
|
||||||
# this is assigned first when pushing button, so assign this at creation time!
|
"crack", "opening", "secret door"])
|
||||||
|
# this is assigned first when pushing button, so assign
|
||||||
|
# this at creation time!
|
||||||
|
|
||||||
self.db.destination = 2
|
self.db.destination = 2
|
||||||
# locks on the object directly transfer to the exit "command"
|
# locks on the object directly transfer to the exit "command"
|
||||||
self.locks.add("cmd:not locattr(is_dark)")
|
self.locks.add("cmd:not locattr(is_dark)")
|
||||||
|
|
||||||
self.db.tutorial_info = "This is an Exit with a conditional traverse-lock. Try to shift the roots around."
|
self.db.tutorial_info = "This is an Exit with a conditional traverse-lock. Try to shift the roots around."
|
||||||
# the lock is important for this exit; we only allow passage if we "found exit".
|
# the lock is important for this exit; we only allow passage
|
||||||
|
# if we "found exit".
|
||||||
self.locks.add("traverse:attr(crumbling_wall_found_exit)")
|
self.locks.add("traverse:attr(crumbling_wall_found_exit)")
|
||||||
# set cmdset
|
# set cmdset
|
||||||
self.cmdset.add(CmdSetCrumblingWall, permanent=True)
|
self.cmdset.add(CmdSetCrumblingWall, permanent=True)
|
||||||
|
|
||||||
# starting root positions. H1/H2 are the horizontally hanging roots, V1/V2 the
|
# starting root positions. H1/H2 are the horizontally hanging roots,
|
||||||
# vertically hanging ones. Each can have three positions: (-1, 0, 1) where
|
# V1/V2 the vertically hanging ones. Each can have three positions:
|
||||||
# 0 means the middle position. yellow/green are horizontal roots and red/blue vertical.
|
# (-1, 0, 1) where 0 means the middle position. yellow/green are
|
||||||
# all may have value 0, but never any other identical value.
|
# horizontal roots and red/blue vertical, all may have value 0, but n
|
||||||
self.db.root_pos = {"yellow":0, "green":0, "red":0, "blue":0}
|
# ever any other identical value.
|
||||||
|
self.db.root_pos = {"yellow": 0, "green": 0, "red": 0, "blue": 0}
|
||||||
|
|
||||||
def _translate_position(self, root, ipos):
|
def _translate_position(self, root, ipos):
|
||||||
"Translates the position into words"
|
"Translates the position into words"
|
||||||
|
|
@ -598,7 +618,10 @@ class CrumblingWall(TutorialObject, Exit):
|
||||||
return string
|
return string
|
||||||
|
|
||||||
def return_appearance(self, caller):
|
def return_appearance(self, caller):
|
||||||
"This is called when someone looks at the wall. We need to echo the current root positions."
|
"""
|
||||||
|
This is called when someone looks at the wall. We need to echo the
|
||||||
|
current root positions.
|
||||||
|
"""
|
||||||
if caller.db.crumbling_wall_found_button:
|
if caller.db.crumbling_wall_found_button:
|
||||||
string = "Having moved all the roots aside, you find that the center of the wall, "
|
string = "Having moved all the roots aside, you find that the center of the wall, "
|
||||||
string += "previously hidden by the vegetation, hid a curious square depression. It was maybe once "
|
string += "previously hidden by the vegetation, hid a curious square depression. It was maybe once "
|
||||||
|
|
@ -615,7 +638,10 @@ class CrumblingWall(TutorialObject, Exit):
|
||||||
return super(CrumblingWall, self).return_appearance(caller)
|
return super(CrumblingWall, self).return_appearance(caller)
|
||||||
|
|
||||||
def at_after_traverse(self, traverser, source_location):
|
def at_after_traverse(self, traverser, source_location):
|
||||||
"This is called after we traversed this exit. Cleans up and resets the puzzle."
|
"""
|
||||||
|
This is called after we traversed this exit. Cleans up and resets
|
||||||
|
the puzzle.
|
||||||
|
"""
|
||||||
del traverser.db.crumbling_wall_found_button
|
del traverser.db.crumbling_wall_found_button
|
||||||
del traverser.db.crumbling_wall_found_exit
|
del traverser.db.crumbling_wall_found_exit
|
||||||
self.reset()
|
self.reset()
|
||||||
|
|
@ -625,11 +651,14 @@ class CrumblingWall(TutorialObject, Exit):
|
||||||
traverser.msg("No matter how you try, you cannot force yourself through %s." % self.key)
|
traverser.msg("No matter how you try, you cannot force yourself through %s." % self.key)
|
||||||
|
|
||||||
def reset(self):
|
def reset(self):
|
||||||
"Called by tutorial world runner, or whenever someone successfully traversed the Exit."
|
"""
|
||||||
|
Called by tutorial world runner, or whenever someone successfully
|
||||||
|
traversed the Exit.
|
||||||
|
"""
|
||||||
self.location.msg_contents("The secret door closes abruptly, roots falling back into place.")
|
self.location.msg_contents("The secret door closes abruptly, roots falling back into place.")
|
||||||
for obj in self.location.contents:
|
for obj in self.location.contents:
|
||||||
# clear eventual puzzle-solved attribues on everyone that didn't get out in time. They
|
# clear eventual puzzle-solved attribues on everyone that didn't
|
||||||
# have to try again.
|
# get out in time. They have to try again.
|
||||||
del obj.db.crumbling_wall_found_exit
|
del obj.db.crumbling_wall_found_exit
|
||||||
|
|
||||||
# Reset the roots with some random starting positions for the roots:
|
# Reset the roots with some random starting positions for the roots:
|
||||||
|
|
@ -641,6 +670,7 @@ class CrumblingWall(TutorialObject, Exit):
|
||||||
self.db.root_pos = start_pos[random.randint(0, 4)]
|
self.db.root_pos = start_pos[random.randint(0, 4)]
|
||||||
self.destination = None
|
self.destination = None
|
||||||
|
|
||||||
|
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
#
|
#
|
||||||
# Weapon - object type
|
# Weapon - object type
|
||||||
|
|
@ -667,15 +697,17 @@ class CmdAttack(Command):
|
||||||
|
|
||||||
stab - (thrust) makes a lot of damage but is harder to hit with.
|
stab - (thrust) makes a lot of damage but is harder to hit with.
|
||||||
slash - is easier to land, but does not make as much damage.
|
slash - is easier to land, but does not make as much damage.
|
||||||
parry - forgoes your attack but will make you harder to hit on next enemy attack.
|
parry - forgoes your attack but will make you harder to hit on next
|
||||||
|
enemy attack.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# this is an example of implementing many commands as a single command class,
|
# this is an example of implementing many commands as a single
|
||||||
# using the given command alias to separate between them.
|
# command class, using the given command alias to separate between them.
|
||||||
|
|
||||||
key = "attack"
|
key = "attack"
|
||||||
aliases = ["hit","kill", "fight", "thrust", "pierce", "stab", "slash", "chop", "parry", "defend"]
|
aliases = ["hit","kill", "fight", "thrust", "pierce", "stab",
|
||||||
|
"slash", "chop", "parry", "defend"]
|
||||||
locks = "cmd:all()"
|
locks = "cmd:all()"
|
||||||
help_category = "TutorialWorld"
|
help_category = "TutorialWorld"
|
||||||
|
|
||||||
|
|
@ -684,7 +716,6 @@ class CmdAttack(Command):
|
||||||
|
|
||||||
cmdstring = self.cmdstring
|
cmdstring = self.cmdstring
|
||||||
|
|
||||||
|
|
||||||
if cmdstring in ("attack", "fight"):
|
if cmdstring in ("attack", "fight"):
|
||||||
string = "How do you want to fight? Choose one of 'stab', 'slash' or 'defend'."
|
string = "How do you want to fight? Choose one of 'stab', 'slash' or 'defend'."
|
||||||
self.caller.msg(string)
|
self.caller.msg(string)
|
||||||
|
|
@ -709,15 +740,15 @@ class CmdAttack(Command):
|
||||||
tstring = ""
|
tstring = ""
|
||||||
ostring = ""
|
ostring = ""
|
||||||
if cmdstring in ("thrust", "pierce", "stab"):
|
if cmdstring in ("thrust", "pierce", "stab"):
|
||||||
hit = float(self.obj.db.hit) * 0.7 # modified due to stab
|
hit = float(self.obj.db.hit) * 0.7 # modified due to stab
|
||||||
damage = self.obj.db.damage * 2 # modified due to stab
|
damage = self.obj.db.damage * 2 # modified due to stab
|
||||||
string = "You stab with %s. " % self.obj.key
|
string = "You stab with %s. " % self.obj.key
|
||||||
tstring = "%s stabs at you with %s. " % (self.caller.key, self.obj.key)
|
tstring = "%s stabs at you with %s. " % (self.caller.key, self.obj.key)
|
||||||
ostring = "%s stabs at %s with %s. " % (self.caller.key, target.key, self.obj.key)
|
ostring = "%s stabs at %s with %s. " % (self.caller.key, target.key, self.obj.key)
|
||||||
self.caller.db.combat_parry_mode = False
|
self.caller.db.combat_parry_mode = False
|
||||||
elif cmdstring in ("slash", "chop"):
|
elif cmdstring in ("slash", "chop"):
|
||||||
hit = float(self.obj.db.hit) # un modified due to slash
|
hit = float(self.obj.db.hit) # un modified due to slash
|
||||||
damage = self.obj.db.damage # un modified due to slash
|
damage = self.obj.db.damage # un modified due to slash
|
||||||
string = "You slash with %s. " % self.obj.key
|
string = "You slash with %s. " % self.obj.key
|
||||||
tstring = "%s slash at you with %s. " % (self.caller.key, self.obj.key)
|
tstring = "%s slash at you with %s. " % (self.caller.key, self.obj.key)
|
||||||
ostring = "%s slash at %s with %s. " % (self.caller.key, target.key, self.obj.key)
|
ostring = "%s slash at %s with %s. " % (self.caller.key, target.key, self.obj.key)
|
||||||
|
|
@ -753,12 +784,14 @@ class CmdAttack(Command):
|
||||||
target.msg(tstring + "{gThey miss you.{n")
|
target.msg(tstring + "{gThey miss you.{n")
|
||||||
self.caller.location.msg_contents(ostring + "They miss.", exclude=[target, self.caller])
|
self.caller.location.msg_contents(ostring + "They miss.", exclude=[target, self.caller])
|
||||||
|
|
||||||
|
|
||||||
class CmdSetWeapon(CmdSet):
|
class CmdSetWeapon(CmdSet):
|
||||||
"Holds the attack command."
|
"Holds the attack command."
|
||||||
def at_cmdset_creation(self):
|
def at_cmdset_creation(self):
|
||||||
"called at first object creation."
|
"called at first object creation."
|
||||||
self.add(CmdAttack())
|
self.add(CmdAttack())
|
||||||
|
|
||||||
|
|
||||||
class Weapon(TutorialObject):
|
class Weapon(TutorialObject):
|
||||||
"""
|
"""
|
||||||
This defines a bladed weapon.
|
This defines a bladed weapon.
|
||||||
|
|
@ -766,7 +799,8 @@ class Weapon(TutorialObject):
|
||||||
Important attributes (set at creation):
|
Important attributes (set at creation):
|
||||||
hit - chance to hit (0-1)
|
hit - chance to hit (0-1)
|
||||||
parry - chance to parry (0-1)
|
parry - chance to parry (0-1)
|
||||||
damage - base damage given (modified by hit success and type of attack) (0-10)
|
damage - base damage given (modified by hit success and
|
||||||
|
type of attack) (0-10)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
def at_object_creation(self):
|
def at_object_creation(self):
|
||||||
|
|
@ -779,13 +813,17 @@ class Weapon(TutorialObject):
|
||||||
self.cmdset.add_default(CmdSetWeapon, permanent=True)
|
self.cmdset.add_default(CmdSetWeapon, permanent=True)
|
||||||
|
|
||||||
def reset(self):
|
def reset(self):
|
||||||
"When reset, the weapon is simply deleted, unless it has a place to return to."
|
"""
|
||||||
|
When reset, the weapon is simply deleted, unless it has a place
|
||||||
|
to return to.
|
||||||
|
"""
|
||||||
if self.location.has_player and self.home == self.location:
|
if self.location.has_player and self.home == self.location:
|
||||||
self.location.msg_contents("%s suddenly and magically fades into nothingness, as if it was never there ..." % self.key)
|
self.location.msg_contents("%s suddenly and magically fades into nothingness, as if it was never there ..." % self.key)
|
||||||
self.delete()
|
self.delete()
|
||||||
else:
|
else:
|
||||||
self.location = self.home
|
self.location = self.home
|
||||||
|
|
||||||
|
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
#
|
#
|
||||||
# Weapon rack - spawns weapons
|
# Weapon rack - spawns weapons
|
||||||
|
|
@ -833,18 +871,23 @@ class CmdSetWeaponRack(CmdSet):
|
||||||
"group the rack cmd"
|
"group the rack cmd"
|
||||||
key = "weaponrack_cmdset"
|
key = "weaponrack_cmdset"
|
||||||
mergemode = "Replace"
|
mergemode = "Replace"
|
||||||
|
|
||||||
def at_cmdset_creation(self):
|
def at_cmdset_creation(self):
|
||||||
|
"Called at first creation of cmdset"
|
||||||
self.add(CmdGetWeapon())
|
self.add(CmdGetWeapon())
|
||||||
|
|
||||||
|
|
||||||
class WeaponRack(TutorialObject):
|
class WeaponRack(TutorialObject):
|
||||||
"""
|
"""
|
||||||
This will spawn a new weapon for the player unless the player already has one from this rack.
|
This will spawn a new weapon for the player unless the player already has
|
||||||
|
one from this rack.
|
||||||
|
|
||||||
attribute to set at creation:
|
attribute to set at creation:
|
||||||
min_dmg - the minimum damage of objects from this rack
|
min_dmg - the minimum damage of objects from this rack
|
||||||
max_dmg - the maximum damage of objects from this rack
|
max_dmg - the maximum damage of objects from this rack
|
||||||
magic - if weapons should be magical (have the magic flag set)
|
magic - if weapons should be magical (have the magic flag set)
|
||||||
get_text - the echo text to return when getting the weapon. Give '%s' to include the name of the weapon.
|
get_text - the echo text to return when getting the weapon. Give '%s'
|
||||||
|
to include the name of the weapon.
|
||||||
"""
|
"""
|
||||||
def at_object_creation(self):
|
def at_object_creation(self):
|
||||||
"called at creation"
|
"called at creation"
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ from ev import utils, create_object, search_object
|
||||||
from contrib.tutorial_world import scripts as tut_scripts
|
from contrib.tutorial_world import scripts as tut_scripts
|
||||||
from contrib.tutorial_world.objects import LightSource, TutorialObject
|
from contrib.tutorial_world.objects import LightSource, TutorialObject
|
||||||
|
|
||||||
|
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
#
|
#
|
||||||
# Tutorial room - parent room class
|
# Tutorial room - parent room class
|
||||||
|
|
@ -45,7 +46,7 @@ class CmdTutorial(Command):
|
||||||
caller = self.caller
|
caller = self.caller
|
||||||
|
|
||||||
if not self.args:
|
if not self.args:
|
||||||
target = self.obj # this is the room object the command is defined on
|
target = self.obj # this is the room the command is defined on
|
||||||
else:
|
else:
|
||||||
target = caller.search(self.args.strip())
|
target = caller.search(self.args.strip())
|
||||||
if not target:
|
if not target:
|
||||||
|
|
@ -56,13 +57,16 @@ class CmdTutorial(Command):
|
||||||
else:
|
else:
|
||||||
caller.msg("{RSorry, there is no tutorial help available here.{n")
|
caller.msg("{RSorry, there is no tutorial help available here.{n")
|
||||||
|
|
||||||
|
|
||||||
class TutorialRoomCmdSet(CmdSet):
|
class TutorialRoomCmdSet(CmdSet):
|
||||||
"Implements the simple tutorial cmdset"
|
"Implements the simple tutorial cmdset"
|
||||||
key = "tutorial_cmdset"
|
key = "tutorial_cmdset"
|
||||||
|
|
||||||
def at_cmdset_creation(self):
|
def at_cmdset_creation(self):
|
||||||
"add the tutorial cmd"
|
"add the tutorial cmd"
|
||||||
self.add(CmdTutorial())
|
self.add(CmdTutorial())
|
||||||
|
|
||||||
|
|
||||||
class TutorialRoom(Room):
|
class TutorialRoom(Room):
|
||||||
"""
|
"""
|
||||||
This is the base room type for all rooms in the tutorial world.
|
This is the base room type for all rooms in the tutorial world.
|
||||||
|
|
@ -78,7 +82,6 @@ class TutorialRoom(Room):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
#
|
#
|
||||||
# Weather room - scripted room
|
# Weather room - scripted room
|
||||||
|
|
@ -89,7 +92,6 @@ class TutorialRoom(Room):
|
||||||
#
|
#
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
class WeatherRoom(TutorialRoom):
|
class WeatherRoom(TutorialRoom):
|
||||||
"""
|
"""
|
||||||
This should probably better be called a rainy room...
|
This should probably better be called a rainy room...
|
||||||
|
|
@ -107,6 +109,7 @@ class WeatherRoom(TutorialRoom):
|
||||||
self.scripts.add(tut_scripts.IrregularEvent)
|
self.scripts.add(tut_scripts.IrregularEvent)
|
||||||
self.db.tutorial_info = \
|
self.db.tutorial_info = \
|
||||||
"This room has a Script running that has it echo a weather-related message at irregular intervals."
|
"This room has a Script running that has it echo a weather-related message at irregular intervals."
|
||||||
|
|
||||||
def update_irregular(self):
|
def update_irregular(self):
|
||||||
"create a tuple of possible texts to return."
|
"create a tuple of possible texts to return."
|
||||||
strings = (
|
strings = (
|
||||||
|
|
@ -122,21 +125,23 @@ class WeatherRoom(TutorialRoom):
|
||||||
"You hear the distant howl of what sounds like some sort of dog or wolf.",
|
"You hear the distant howl of what sounds like some sort of dog or wolf.",
|
||||||
"Large clouds rush across the sky, throwing their load of rain over the world.")
|
"Large clouds rush across the sky, throwing their load of rain over the world.")
|
||||||
|
|
||||||
# get a random value so we can select one of the strings above. Send this to the room.
|
# get a random value so we can select one of the strings above.
|
||||||
|
# Send this to the room.
|
||||||
irand = random.randint(0, 15)
|
irand = random.randint(0, 15)
|
||||||
if irand > 10:
|
if irand > 10:
|
||||||
return # don't return anything, to add more randomness
|
return # don't return anything, to add more randomness
|
||||||
self.msg_contents("{w%s{n" % strings[irand])
|
self.msg_contents("{w%s{n" % strings[irand])
|
||||||
|
|
||||||
|
|
||||||
#-----------------------------------------------------------------------------------
|
#------------------------------------------------------------------------------
|
||||||
#
|
#
|
||||||
# Dark Room - a scripted room
|
# Dark Room - a scripted room
|
||||||
#
|
#
|
||||||
# This room limits the movemenets of its denizens unless they carry a and active
|
# This room limits the movemenets of its denizens unless they carry a and active
|
||||||
# LightSource object (LightSource is defined in tutorialworld.objects.LightSource)
|
# LightSource object (LightSource is defined in
|
||||||
|
# tutorialworld.objects.LightSource)
|
||||||
#
|
#
|
||||||
#-----------------------------------------------------------------------------------
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
class CmdLookDark(Command):
|
class CmdLookDark(Command):
|
||||||
"""
|
"""
|
||||||
|
|
@ -169,13 +174,15 @@ class CmdLookDark(Command):
|
||||||
caller.msg(messages[irand])
|
caller.msg(messages[irand])
|
||||||
else:
|
else:
|
||||||
# check so we don't already carry a lightsource.
|
# check so we don't already carry a lightsource.
|
||||||
carried_lights = [obj for obj in caller.contents if utils.inherits_from(obj, LightSource)]
|
carried_lights = [obj for obj in caller.contents
|
||||||
|
if utils.inherits_from(obj, LightSource)]
|
||||||
if carried_lights:
|
if carried_lights:
|
||||||
string = "You don't want to stumble around in blindness anymore. You already found what you need. Let's get light already!"
|
string = "You don't want to stumble around in blindness anymore. You already found what you need. Let's get light already!"
|
||||||
caller.msg(string)
|
caller.msg(string)
|
||||||
return
|
return
|
||||||
#if we are lucky, we find the light source.
|
#if we are lucky, we find the light source.
|
||||||
lightsources = [obj for obj in self.obj.contents if utils.inherits_from(obj, LightSource)]
|
lightsources = [obj for obj in self.obj.contents
|
||||||
|
if utils.inherits_from(obj, LightSource)]
|
||||||
if lightsources:
|
if lightsources:
|
||||||
lightsource = lightsources[0]
|
lightsource = lightsources[0]
|
||||||
else:
|
else:
|
||||||
|
|
@ -186,6 +193,7 @@ class CmdLookDark(Command):
|
||||||
string += "\nYou pick it up, holding it firmly. Now you just need to {wlight{n it using the flint and steel you carry with you."
|
string += "\nYou pick it up, holding it firmly. Now you just need to {wlight{n it using the flint and steel you carry with you."
|
||||||
caller.msg(string)
|
caller.msg(string)
|
||||||
|
|
||||||
|
|
||||||
class CmdDarkHelp(Command):
|
class CmdDarkHelp(Command):
|
||||||
"""
|
"""
|
||||||
Help command for the dark state.
|
Help command for the dark state.
|
||||||
|
|
@ -193,27 +201,34 @@ class CmdDarkHelp(Command):
|
||||||
key = "help"
|
key = "help"
|
||||||
locks = "cmd:all()"
|
locks = "cmd:all()"
|
||||||
help_category = "TutorialWorld"
|
help_category = "TutorialWorld"
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"Implements the help command."
|
"Implements the help command."
|
||||||
string = "Can't help you until you find some light! Try feeling around for something to burn."
|
string = "Can't help you until you find some light! Try feeling around for something to burn."
|
||||||
string += " You cannot give up even if you don't find anything right away."
|
string += " You cannot give up even if you don't find anything right away."
|
||||||
self.caller.msg(string)
|
self.caller.msg(string)
|
||||||
|
|
||||||
# the nomatch system command will give a suitable error when we cannot find the normal commands.
|
# the nomatch system command will give a suitable error when we cannot find
|
||||||
|
# the normal commands.
|
||||||
from src.commands.default.syscommands import CMD_NOMATCH
|
from src.commands.default.syscommands import CMD_NOMATCH
|
||||||
from src.commands.default.general import CmdSay
|
from src.commands.default.general import CmdSay
|
||||||
|
|
||||||
|
|
||||||
class CmdDarkNoMatch(Command):
|
class CmdDarkNoMatch(Command):
|
||||||
"This is called when there is no match"
|
"This is called when there is no match"
|
||||||
key = CMD_NOMATCH
|
key = CMD_NOMATCH
|
||||||
locks = "cmd:all()"
|
locks = "cmd:all()"
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"Implements the command."
|
"Implements the command."
|
||||||
self.caller.msg("Until you find some light, there's not much you can do. Try feeling around.")
|
self.caller.msg("Until you find some light, there's not much you can do. Try feeling around.")
|
||||||
|
|
||||||
|
|
||||||
class DarkCmdSet(CmdSet):
|
class DarkCmdSet(CmdSet):
|
||||||
"Groups the commands."
|
"Groups the commands."
|
||||||
key = "darkroom_cmdset"
|
key = "darkroom_cmdset"
|
||||||
mergetype = "Replace" # completely remove all other commands
|
mergetype = "Replace" # completely remove all other commands
|
||||||
|
|
||||||
def at_cmdset_creation(self):
|
def at_cmdset_creation(self):
|
||||||
"populates the cmdset."
|
"populates the cmdset."
|
||||||
self.add(CmdTutorial())
|
self.add(CmdTutorial())
|
||||||
|
|
@ -221,6 +236,7 @@ class DarkCmdSet(CmdSet):
|
||||||
self.add(CmdDarkHelp())
|
self.add(CmdDarkHelp())
|
||||||
self.add(CmdDarkNoMatch())
|
self.add(CmdDarkNoMatch())
|
||||||
self.add(CmdSay)
|
self.add(CmdSay)
|
||||||
|
|
||||||
#
|
#
|
||||||
# Darkness room two-state system
|
# Darkness room two-state system
|
||||||
#
|
#
|
||||||
|
|
@ -238,6 +254,7 @@ class DarkState(Script):
|
||||||
self.key = "tutorial_darkness_state"
|
self.key = "tutorial_darkness_state"
|
||||||
self.desc = "A dark room"
|
self.desc = "A dark room"
|
||||||
self.persistent = True
|
self.persistent = True
|
||||||
|
|
||||||
def at_start(self):
|
def at_start(self):
|
||||||
"called when the script is first starting up."
|
"called when the script is first starting up."
|
||||||
for char in [char for char in self.obj.contents if char.has_player]:
|
for char in [char for char in self.obj.contents if char.has_player]:
|
||||||
|
|
@ -246,9 +263,11 @@ class DarkState(Script):
|
||||||
else:
|
else:
|
||||||
char.cmdset.add(DarkCmdSet)
|
char.cmdset.add(DarkCmdSet)
|
||||||
char.msg("The room is pitch dark! You are likely to be eaten by a Grue.")
|
char.msg("The room is pitch dark! You are likely to be eaten by a Grue.")
|
||||||
|
|
||||||
def is_valid(self):
|
def is_valid(self):
|
||||||
"is valid only as long as noone in the room has lit the lantern."
|
"is valid only as long as noone in the room has lit the lantern."
|
||||||
return not self.obj.is_lit()
|
return not self.obj.is_lit()
|
||||||
|
|
||||||
def at_stop(self):
|
def at_stop(self):
|
||||||
"Someone turned on a light. This state dies. Switch to LightState."
|
"Someone turned on a light. This state dies. Switch to LightState."
|
||||||
for char in [char for char in self.obj.contents if char.has_player]:
|
for char in [char for char in self.obj.contents if char.has_player]:
|
||||||
|
|
@ -256,23 +275,31 @@ class DarkState(Script):
|
||||||
self.obj.db.is_dark = False
|
self.obj.db.is_dark = False
|
||||||
self.obj.scripts.add(LightState)
|
self.obj.scripts.add(LightState)
|
||||||
|
|
||||||
|
|
||||||
class LightState(Script):
|
class LightState(Script):
|
||||||
"""
|
"""
|
||||||
This is the counterpart to the Darkness state. It is active when the lantern is on.
|
This is the counterpart to the Darkness state. It is active when the
|
||||||
|
lantern is on.
|
||||||
"""
|
"""
|
||||||
def at_script_creation(self):
|
def at_script_creation(self):
|
||||||
"Called when script is first created."
|
"Called when script is first created."
|
||||||
self.key = "tutorial_light_state"
|
self.key = "tutorial_light_state"
|
||||||
self.desc = "A room lit up"
|
self.desc = "A room lit up"
|
||||||
self.persistent = True
|
self.persistent = True
|
||||||
|
|
||||||
def is_valid(self):
|
def is_valid(self):
|
||||||
"This state is only valid as long as there is an active light source in the room."
|
"""
|
||||||
|
This state is only valid as long as there is an active light
|
||||||
|
source in the room.
|
||||||
|
"""
|
||||||
return self.obj.is_lit()
|
return self.obj.is_lit()
|
||||||
|
|
||||||
def at_stop(self):
|
def at_stop(self):
|
||||||
"Light disappears. This state dies. Return to DarknessState."
|
"Light disappears. This state dies. Return to DarknessState."
|
||||||
self.obj.db.is_dark = True
|
self.obj.db.is_dark = True
|
||||||
self.obj.scripts.add(DarkState)
|
self.obj.scripts.add(DarkState)
|
||||||
|
|
||||||
|
|
||||||
class DarkRoom(TutorialRoom):
|
class DarkRoom(TutorialRoom):
|
||||||
"""
|
"""
|
||||||
A dark room. This tries to start the DarkState script on all
|
A dark room. This tries to start the DarkState script on all
|
||||||
|
|
@ -287,20 +314,24 @@ class DarkRoom(TutorialRoom):
|
||||||
"""
|
"""
|
||||||
return any([any([True for obj in char.contents
|
return any([any([True for obj in char.contents
|
||||||
if utils.inherits_from(obj, LightSource) and obj.db.is_active])
|
if utils.inherits_from(obj, LightSource) and obj.db.is_active])
|
||||||
for char in self.contents if char.has_player])
|
for char in self.contents if char.has_player])
|
||||||
|
|
||||||
def at_object_creation(self):
|
def at_object_creation(self):
|
||||||
"Called when object is first created."
|
"Called when object is first created."
|
||||||
super(DarkRoom, self).at_object_creation()
|
super(DarkRoom, self).at_object_creation()
|
||||||
self.db.tutorial_info = "This is a room with custom command sets on itself."
|
self.db.tutorial_info = "This is a room with custom command sets on itself."
|
||||||
# this variable is set by the scripts. It makes for an easy flag to look for
|
# this variable is set by the scripts. It makes for an easy flag to
|
||||||
# by other game elements (such as the crumbling wall in the tutorial)
|
# look for by other game elements (such as the crumbling wall in
|
||||||
|
# the tutorial)
|
||||||
self.db.is_dark = True
|
self.db.is_dark = True
|
||||||
# the room starts dark.
|
# the room starts dark.
|
||||||
self.scripts.add(DarkState)
|
self.scripts.add(DarkState)
|
||||||
|
|
||||||
def at_object_receive(self, character, source_location):
|
def at_object_receive(self, character, source_location):
|
||||||
"Called when an object enters the room. We crank the wheels to make sure scripts are synced."
|
"""
|
||||||
|
Called when an object enters the room. We crank the wheels to make
|
||||||
|
sure scripts are synced.
|
||||||
|
"""
|
||||||
if character.has_player:
|
if character.has_player:
|
||||||
if not self.is_lit() and not character.is_superuser:
|
if not self.is_lit() and not character.is_superuser:
|
||||||
character.cmdset.add(DarkCmdSet)
|
character.cmdset.add(DarkCmdSet)
|
||||||
|
|
@ -313,10 +344,14 @@ class DarkRoom(TutorialRoom):
|
||||||
self.scripts.validate()
|
self.scripts.validate()
|
||||||
|
|
||||||
def at_object_leave(self, character, target_location):
|
def at_object_leave(self, character, target_location):
|
||||||
"In case people leave with the light, we make sure to update the states accordingly."
|
"""
|
||||||
character.cmdset.delete(DarkCmdSet) # in case we are teleported away
|
In case people leave with the light, we make sure to update the
|
||||||
|
states accordingly.
|
||||||
|
"""
|
||||||
|
character.cmdset.delete(DarkCmdSet) # in case we are teleported away
|
||||||
self.scripts.validate()
|
self.scripts.validate()
|
||||||
|
|
||||||
|
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
#
|
#
|
||||||
# Teleport room - puzzle room
|
# Teleport room - puzzle room
|
||||||
|
|
@ -347,23 +382,27 @@ class TeleportRoom(TutorialRoom):
|
||||||
super(TeleportRoom, self).at_object_creation()
|
super(TeleportRoom, self).at_object_creation()
|
||||||
# what character.db.puzzle_clue must be set to, to avoid teleportation.
|
# what character.db.puzzle_clue must be set to, to avoid teleportation.
|
||||||
self.db.puzzle_value = 1
|
self.db.puzzle_value = 1
|
||||||
# the target of the success teleportation. Can be a dbref or a unique room name.
|
# target of successful teleportation. Can be a dbref or a
|
||||||
|
# unique room name.
|
||||||
self.db.success_teleport_to = "treasure room"
|
self.db.success_teleport_to = "treasure room"
|
||||||
# the target of the failure teleportation.
|
# the target of the failure teleportation.
|
||||||
self.db.failure_teleport_to = "dark cell"
|
self.db.failure_teleport_to = "dark cell"
|
||||||
|
|
||||||
def at_object_receive(self, character, source_location):
|
def at_object_receive(self, character, source_location):
|
||||||
"This hook is called by the engine whenever the player is moved into this room."
|
"""
|
||||||
|
This hook is called by the engine whenever the player is moved into
|
||||||
|
this room.
|
||||||
|
"""
|
||||||
if not character.has_player:
|
if not character.has_player:
|
||||||
# only act on player characters.
|
# only act on player characters.
|
||||||
return
|
return
|
||||||
#print character.db.puzzle_clue, self.db.puzzle_value
|
#print character.db.puzzle_clue, self.db.puzzle_value
|
||||||
if character.db.puzzle_clue != self.db.puzzle_value:
|
if character.db.puzzle_clue != self.db.puzzle_value:
|
||||||
# we didn't pass the puzzle. See if we can teleport.
|
# we didn't pass the puzzle. See if we can teleport.
|
||||||
teleport_to = self.db.failure_teleport_to # this is a room name
|
teleport_to = self.db.failure_teleport_to # this is a room name
|
||||||
else:
|
else:
|
||||||
# passed the puzzle
|
# passed the puzzle
|
||||||
teleport_to = self.db.success_teleport_to # this is a room name
|
teleport_to = self.db.success_teleport_to # this is a room name
|
||||||
|
|
||||||
results = search_object(teleport_to)
|
results = search_object(teleport_to)
|
||||||
if not results or len(results) > 1:
|
if not results or len(results) > 1:
|
||||||
|
|
@ -376,7 +415,7 @@ class TeleportRoom(TutorialRoom):
|
||||||
return
|
return
|
||||||
# teleport
|
# teleport
|
||||||
character.execute_cmd("look")
|
character.execute_cmd("look")
|
||||||
character.location = results[0] # stealth move
|
character.location = results[0] # stealth move
|
||||||
character.location.at_object_receive(character, self)
|
character.location.at_object_receive(character, self)
|
||||||
|
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
|
|
@ -412,7 +451,8 @@ class CmdEast(Command):
|
||||||
bridge_step = min(5, caller.db.tutorial_bridge_position + 1)
|
bridge_step = min(5, caller.db.tutorial_bridge_position + 1)
|
||||||
|
|
||||||
if bridge_step > 4:
|
if bridge_step > 4:
|
||||||
# we have reached the far east end of the bridge. Move to the east room.
|
# we have reached the far east end of the bridge.
|
||||||
|
# Move to the east room.
|
||||||
eexit = search_object(self.obj.db.east_exit)
|
eexit = search_object(self.obj.db.east_exit)
|
||||||
if eexit:
|
if eexit:
|
||||||
caller.move_to(eexit[0])
|
caller.move_to(eexit[0])
|
||||||
|
|
@ -423,6 +463,7 @@ class CmdEast(Command):
|
||||||
caller.location.msg_contents("%s steps eastwards across the bridge." % caller.name, exclude=caller)
|
caller.location.msg_contents("%s steps eastwards across the bridge." % caller.name, exclude=caller)
|
||||||
caller.execute_cmd("look")
|
caller.execute_cmd("look")
|
||||||
|
|
||||||
|
|
||||||
# go back across the bridge
|
# go back across the bridge
|
||||||
class CmdWest(Command):
|
class CmdWest(Command):
|
||||||
"""
|
"""
|
||||||
|
|
@ -440,7 +481,8 @@ class CmdWest(Command):
|
||||||
bridge_step = max(-1, caller.db.tutorial_bridge_position - 1)
|
bridge_step = max(-1, caller.db.tutorial_bridge_position - 1)
|
||||||
|
|
||||||
if bridge_step < 0:
|
if bridge_step < 0:
|
||||||
# we have reached the far west end of the bridge. Move to the west room.
|
# we have reached the far west end of the bridge.#
|
||||||
|
# Move to the west room.
|
||||||
wexit = search_object(self.obj.db.west_exit)
|
wexit = search_object(self.obj.db.west_exit)
|
||||||
if wexit:
|
if wexit:
|
||||||
caller.move_to(wexit[0])
|
caller.move_to(wexit[0])
|
||||||
|
|
@ -451,6 +493,7 @@ class CmdWest(Command):
|
||||||
caller.location.msg_contents("%s steps westwartswards across the bridge." % caller.name, exclude=caller)
|
caller.location.msg_contents("%s steps westwartswards across the bridge." % caller.name, exclude=caller)
|
||||||
caller.execute_cmd("look")
|
caller.execute_cmd("look")
|
||||||
|
|
||||||
|
|
||||||
class CmdLookBridge(Command):
|
class CmdLookBridge(Command):
|
||||||
"""
|
"""
|
||||||
looks around at the bridge.
|
looks around at the bridge.
|
||||||
|
|
@ -486,7 +529,8 @@ class CmdLookBridge(Command):
|
||||||
|
|
||||||
self.caller.msg(message)
|
self.caller.msg(message)
|
||||||
|
|
||||||
# there is a chance that we fall if we are on the western or central part of the bridge.
|
# there is a chance that we fall if we are on the western or central
|
||||||
|
# part of the bridge.
|
||||||
if bridge_position < 3 and random.random() < 0.05 and not self.caller.is_superuser:
|
if bridge_position < 3 and random.random() < 0.05 and not self.caller.is_superuser:
|
||||||
# we fall on 5% of the times.
|
# we fall on 5% of the times.
|
||||||
fexit = search_object(self.obj.db.fall_exit)
|
fexit = search_object(self.obj.db.fall_exit)
|
||||||
|
|
@ -504,6 +548,7 @@ class CmdLookBridge(Command):
|
||||||
self.caller.location = fexit[0] # stealth move, without any other hook calls.
|
self.caller.location = fexit[0] # stealth move, without any other hook calls.
|
||||||
self.obj.msg_contents("A plank gives way under %s's feet and they fall from the bridge!" % self.caller.key)
|
self.obj.msg_contents("A plank gives way under %s's feet and they fall from the bridge!" % self.caller.key)
|
||||||
|
|
||||||
|
|
||||||
# custom help command
|
# custom help command
|
||||||
class CmdBridgeHelp(Command):
|
class CmdBridgeHelp(Command):
|
||||||
"""
|
"""
|
||||||
|
|
@ -521,33 +566,38 @@ class CmdBridgeHelp(Command):
|
||||||
string += "or try to get back to the mainland {wwest{n)."
|
string += "or try to get back to the mainland {wwest{n)."
|
||||||
self.caller.msg(string)
|
self.caller.msg(string)
|
||||||
|
|
||||||
|
|
||||||
class BridgeCmdSet(CmdSet):
|
class BridgeCmdSet(CmdSet):
|
||||||
"This groups the bridge commands. We will store it on the room."
|
"This groups the bridge commands. We will store it on the room."
|
||||||
key = "Bridge commands"
|
key = "Bridge commands"
|
||||||
priority = 1 # this gives it precedence over the normal look/help commands.
|
priority = 1 # this gives it precedence over the normal look/help commands.
|
||||||
def at_cmdset_creation(self):
|
def at_cmdset_creation(self):
|
||||||
|
"Called at first cmdset creation"
|
||||||
self.add(CmdTutorial())
|
self.add(CmdTutorial())
|
||||||
self.add(CmdEast())
|
self.add(CmdEast())
|
||||||
self.add(CmdWest())
|
self.add(CmdWest())
|
||||||
self.add(CmdLookBridge())
|
self.add(CmdLookBridge())
|
||||||
self.add(CmdBridgeHelp())
|
self.add(CmdBridgeHelp())
|
||||||
|
|
||||||
|
|
||||||
class BridgeRoom(TutorialRoom):
|
class BridgeRoom(TutorialRoom):
|
||||||
"""
|
"""
|
||||||
The bridge room implements an unsafe bridge. It also enters the player into a
|
The bridge room implements an unsafe bridge. It also enters the player into
|
||||||
state where they get new commands so as to try to cross the bridge.
|
a state where they get new commands so as to try to cross the bridge.
|
||||||
|
|
||||||
We want this to result in the player getting a special set of
|
We want this to result in the player getting a special set of
|
||||||
commands related to crossing the bridge. The result is that it will take several
|
commands related to crossing the bridge. The result is that it will
|
||||||
steps to cross it, despite it being represented by only a single room.
|
take several steps to cross it, despite it being represented by only a
|
||||||
|
single room.
|
||||||
|
|
||||||
We divide the bridge into steps:
|
We divide the bridge into steps:
|
||||||
|
|
||||||
self.db.west_exit - - | - - self.db.east_exit
|
self.db.west_exit - - | - - self.db.east_exit
|
||||||
0 1 2 3 4
|
0 1 2 3 4
|
||||||
|
|
||||||
The position is handled by a variable stored on the player when entering and giving
|
The position is handled by a variable stored on the player when entering
|
||||||
special move commands will increase/decrease the counter until the bridge is crossed.
|
and giving special move commands will increase/decrease the counter
|
||||||
|
until the bridge is crossed.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
def at_object_creation(self):
|
def at_object_creation(self):
|
||||||
|
|
@ -617,8 +667,8 @@ class BridgeRoom(TutorialRoom):
|
||||||
#
|
#
|
||||||
# Intro Room - unique room
|
# Intro Room - unique room
|
||||||
#
|
#
|
||||||
# This room marks the start of the tutorial. It sets up properties on the player char
|
# This room marks the start of the tutorial. It sets up properties on
|
||||||
# that is needed for the tutorial.
|
# the player char that is needed for the tutorial.
|
||||||
#
|
#
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
|
|
||||||
|
|
@ -652,6 +702,7 @@ class IntroRoom(TutorialRoom):
|
||||||
string += "-"*78
|
string += "-"*78
|
||||||
character.msg("{r%s{n" % string)
|
character.msg("{r%s{n" % string)
|
||||||
|
|
||||||
|
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
#
|
#
|
||||||
# Outro room - unique room
|
# Outro room - unique room
|
||||||
|
|
@ -683,5 +734,6 @@ class OutroRoom(TutorialRoom):
|
||||||
del character.db.puzzle_clue
|
del character.db.puzzle_clue
|
||||||
del character.db.combat_parry_mode
|
del character.db.combat_parry_mode
|
||||||
del character.db.tutorial_bridge_position
|
del character.db.tutorial_bridge_position
|
||||||
for tut_obj in [obj for obj in character.contents if utils.inherits_from(obj, TutorialObject)]:
|
for tut_obj in [obj for obj in character.contents
|
||||||
|
if utils.inherits_from(obj, TutorialObject)]:
|
||||||
tut_obj.reset()
|
tut_obj.reset()
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ This defines some generally useful scripts for the tutorial world.
|
||||||
import random
|
import random
|
||||||
from ev import Script
|
from ev import Script
|
||||||
|
|
||||||
|
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
#
|
#
|
||||||
# IrregularEvent - script firing at random intervals
|
# IrregularEvent - script firing at random intervals
|
||||||
|
|
@ -28,8 +29,9 @@ class IrregularEvent(Script):
|
||||||
|
|
||||||
self.key = "update_irregular"
|
self.key = "update_irregular"
|
||||||
self.desc = "Updates at irregular intervals"
|
self.desc = "Updates at irregular intervals"
|
||||||
self.interval = random.randint(30, 70) # interval to call.
|
self.interval = random.randint(30, 70) # interval to call.
|
||||||
self.start_delay = True # wait at least self.interval seconds before calling at_repeat the first time
|
self.start_delay = True # wait at least self.interval seconds before
|
||||||
|
# calling at_repeat the first time
|
||||||
self.persistent = True
|
self.persistent = True
|
||||||
|
|
||||||
# this attribute determines how likely it is the
|
# this attribute determines how likely it is the
|
||||||
|
|
@ -47,11 +49,13 @@ class IrregularEvent(Script):
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class FastIrregularEvent(IrregularEvent):
|
class FastIrregularEvent(IrregularEvent):
|
||||||
"A faster updating irregular event"
|
"A faster updating irregular event"
|
||||||
def at_script_creation(self):
|
def at_script_creation(self):
|
||||||
|
"Called at initial script creation"
|
||||||
super(FastIrregularEvent, self).at_script_creation()
|
super(FastIrregularEvent, self).at_script_creation()
|
||||||
self.interval = 5 # every 5 seconds, 1/5 chance of firing
|
self.interval = 5 # every 5 seconds, 1/5 chance of firing
|
||||||
|
|
||||||
|
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
|
|
@ -64,11 +68,13 @@ class FastIrregularEvent(IrregularEvent):
|
||||||
|
|
||||||
# #
|
# #
|
||||||
# # This sets up a reset system -- it resets the entire tutorial_world domain
|
# # This sets up a reset system -- it resets the entire tutorial_world domain
|
||||||
# # and all objects inheriting from it back to an initial state, MORPG style. This is useful in order for
|
# # and all objects inheriting from it back to an initial state, MORPG style.
|
||||||
# # different players to explore it without finding things missing.
|
# This is useful in order for different players to explore it without finding
|
||||||
|
# # things missing.
|
||||||
# #
|
# #
|
||||||
# # Note that this will of course allow a single player to end up with multiple versions of objects if
|
# # Note that this will of course allow a single player to end up with
|
||||||
# # they just wait around between resets; In a real game environment this would have to be resolved e.g.
|
# # multiple versions of objects if they just wait around between resets;
|
||||||
|
# # In a real game environment this would have to be resolved e.g.
|
||||||
# # with custom versions of the 'get' command not accepting doublets.
|
# # with custom versions of the 'get' command not accepting doublets.
|
||||||
# #
|
# #
|
||||||
|
|
||||||
|
|
@ -77,7 +83,8 @@ class FastIrregularEvent(IrregularEvent):
|
||||||
# UPDATE_INTERVAL = 60 * 10 # Measured in seconds
|
# UPDATE_INTERVAL = 60 * 10 # Measured in seconds
|
||||||
|
|
||||||
|
|
||||||
# #This is a list of script parent objects that subscribe to the reset functionality.
|
# #This is a list of script parent objects that subscribe to the reset
|
||||||
|
# functionality.
|
||||||
# RESET_SUBSCRIBERS = ["examples.tutorial_world.p_weapon_rack",
|
# RESET_SUBSCRIBERS = ["examples.tutorial_world.p_weapon_rack",
|
||||||
# "examples.tutorial_world.p_mob"]
|
# "examples.tutorial_world.p_mob"]
|
||||||
|
|
||||||
|
|
@ -104,7 +111,4 @@ class FastIrregularEvent(IrregularEvent):
|
||||||
# try:
|
# try:
|
||||||
# obj.scriptlink.reset()
|
# obj.scriptlink.reset()
|
||||||
# except:
|
# except:
|
||||||
# logger.log_errmsg(traceback.print_exc())
|
# logger.log_errmsg(traceback.print_exc())
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
83
ev.py
83
ev.py
|
|
@ -2,53 +2,63 @@
|
||||||
|
|
||||||
Central API for the Evennia MUD/MUX/MU* creation system.
|
Central API for the Evennia MUD/MUX/MU* creation system.
|
||||||
|
|
||||||
This is basically a set of shortcuts for accessing things in src/ with less boiler plate.
|
This is basically a set of shortcuts for accessing things in src/ with less
|
||||||
Import this from your code, use it with @py from in-game or explore it interactively from
|
boiler plate. Import this from your code, use it with @py from in-game or
|
||||||
a python shell.
|
explore it interactively from a python shell.
|
||||||
|
|
||||||
Notes:
|
Notes:
|
||||||
|
|
||||||
0) Use ev.help(), ev.managers.help(), ev.default_cmds.help() and syscmdkeys.help() to
|
0) Use ev.help(), ev.managers.help(), ev.default_cmds.help() and
|
||||||
view the API structure and explore which variables/methods are available.
|
syscmdkeys.help() to view the API structure and explore which
|
||||||
|
variables/methods are available.
|
||||||
|
|
||||||
1) You should import things explicitly from the root of this module - you can not use
|
1) You should import things explicitly from the root of this module - you
|
||||||
dot-notation to import deeper. Hence, to access a default command, you can do
|
can not use ot-notation to import deeper. Hence, to access a default c
|
||||||
|
ommand, you can do
|
||||||
import ev
|
import ev
|
||||||
ev.default_cmds.CmdLook
|
ev.default_cmds.CmdLook
|
||||||
or
|
or
|
||||||
from ev import default_cmds
|
from ev import default_cmds
|
||||||
default_cmds.CmdLook
|
default_cmds.CmdLook
|
||||||
But trying to import CmdLook directly with "from ev.default_cmds import CmdLook" will
|
But trying to import CmdLook directly with
|
||||||
not work since default_cmds is a property on the "ev" module, not a module of its own.
|
from ev.default_cmds import CmdLook
|
||||||
|
will not work since default_cmds is a property on the "ev" module,
|
||||||
|
not a module of its own.
|
||||||
|
|
||||||
2) "managers" is a container object that contains shortcuts to initiated versions of Evennia's django
|
2) "managers" is a container object that contains shortcuts to initiated
|
||||||
database managers (e.g. managers.objects is an alias for ObjectDB.objects). These allow
|
versions of Evennia's django database managers (e.g. managers.objects
|
||||||
for exploring the database in various ways. To use in code, do 'from ev import managers', then
|
is an alias for ObjectDB.objects). These allow for exploring the database
|
||||||
access the managers on the managers object. Please note that the evennia-specific methods in
|
in various ways. To use in code, do 'from ev import managers', then access
|
||||||
managers return typeclasses (or lists of typeclasses), whereas the default django ones (filter etc)
|
the managers on the managers object. Please note that the evennia-specific
|
||||||
return database objects. You can convert between the two easily via dbobj.typeclass and
|
methods inmanagers return typeclasses (or lists of typeclasses), whereas
|
||||||
typeclass.dbobj, but it's worth to remember this difference.
|
the default django ones (filter etc) return database objects. You can
|
||||||
|
convert between the two easily via dbobj.typeclass and typeclass.dbobj,
|
||||||
|
but it's worth to remember this difference.
|
||||||
|
|
||||||
3) "syscmdkeys" is a container object holding the names of system commands. Import with
|
3) "syscmdkeys" is a container object holding the names of system commands.
|
||||||
'from ev import syscmdkeys', then access the variables on the syscmdkeys object.
|
Import with 'from ev import syscmdkeys', then access the variables on
|
||||||
|
the syscmdkeys object.
|
||||||
|
|
||||||
4) You -have- to use the create_* functions (shortcuts to src.utils.create) to create new
|
4) You -have- to use the create_* functions (shortcuts to src.utils.create)
|
||||||
Typeclassed game entities (Objects, Scripts or Players). Just initializing e.g. the Player class will
|
to create new ypeclassed game entities (Objects, Scripts, Channels or
|
||||||
-not- set up Typeclasses correctly and will lead to errors. Other types of database objects
|
Players). Just initializing e.g. the Player class will -not- set up
|
||||||
can be created normally, but there are conveniant create_* functions for those too, making
|
Typeclasses correctly and will lead to errors. Other types of database
|
||||||
some more error checking.
|
objects can be created normally, but there are conveniant create_*
|
||||||
|
functions for those too, making some more error checking.
|
||||||
|
|
||||||
5) "settings" links to Evennia's game/settings file. "settings_full" shows all of django's available
|
5) "settings" links to Evennia's game/settings file. "settings_full" shows
|
||||||
settings. Note that this is for viewing only - you cannot *change* settings from here in a meaningful
|
all of django's available settings. Note that this is for viewing only -
|
||||||
way but have to update game/settings.py and restart the server.
|
you cannot *change* settings from here in a meaningful way but have to
|
||||||
|
update game/settings.py and restart the server.
|
||||||
|
|
||||||
6) The API accesses all relevant and most-neeeded functions/classes from src/ but might not
|
6) The API accesses all relevant and most-neeeded functions/classes from
|
||||||
always include all helper-functions referenced from each such entity. To get to those, access
|
src/ but might not always include all helper-functions referenced from
|
||||||
the modules in src/ directly. You can always do this anyway, if you do not want to go through
|
each such entity. To get to those, access the modules in src/ directly.
|
||||||
this API.
|
You can always do this anyway, if you do not want to go through this API.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import sys, os
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
######################################################################
|
######################################################################
|
||||||
# set Evennia version in __version__ property
|
# set Evennia version in __version__ property
|
||||||
|
|
@ -147,6 +157,7 @@ from src.utils import utils
|
||||||
from src.utils import gametime
|
from src.utils import gametime
|
||||||
from src.utils import ansi
|
from src.utils import ansi
|
||||||
|
|
||||||
|
|
||||||
######################################################################
|
######################################################################
|
||||||
# API containers and helper functions
|
# API containers and helper functions
|
||||||
######################################################################
|
######################################################################
|
||||||
|
|
@ -166,6 +177,7 @@ def help(header=False):
|
||||||
names = [var for var in ev.__dict__ if not var.startswith('_')]
|
names = [var for var in ev.__dict__ if not var.startswith('_')]
|
||||||
return ", ".join(names)
|
return ", ".join(names)
|
||||||
|
|
||||||
|
|
||||||
class _EvContainer(object):
|
class _EvContainer(object):
|
||||||
"""
|
"""
|
||||||
Parent for other containers
|
Parent for other containers
|
||||||
|
|
@ -175,7 +187,8 @@ class _EvContainer(object):
|
||||||
"Returns list of contents"
|
"Returns list of contents"
|
||||||
names = [name for name in self.__class__.__dict__ if not name.startswith('_')]
|
names = [name for name in self.__class__.__dict__ if not name.startswith('_')]
|
||||||
names += [name for name in self.__dict__ if not name.startswith('_')]
|
names += [name for name in self.__dict__ if not name.startswith('_')]
|
||||||
return self.__doc__ + "-"*60 + "\n" + ", ".join(names)
|
return self.__doc__ + "-" * 60 + "\n" + ", ".join(names)
|
||||||
|
|
||||||
|
|
||||||
class DBmanagers(_EvContainer):
|
class DBmanagers(_EvContainer):
|
||||||
"""
|
"""
|
||||||
|
|
@ -213,6 +226,7 @@ class DBmanagers(_EvContainer):
|
||||||
managers = DBmanagers()
|
managers = DBmanagers()
|
||||||
del DBmanagers
|
del DBmanagers
|
||||||
|
|
||||||
|
|
||||||
class DefaultCmds(_EvContainer):
|
class DefaultCmds(_EvContainer):
|
||||||
"""
|
"""
|
||||||
This container holds direct shortcuts to all default commands in Evennia.
|
This container holds direct shortcuts to all default commands in Evennia.
|
||||||
|
|
@ -235,7 +249,9 @@ class DefaultCmds(_EvContainer):
|
||||||
cmdlist = utils.variable_from_module(module, module.__all__)
|
cmdlist = utils.variable_from_module(module, module.__all__)
|
||||||
self.__dict__.update(dict([(c.__name__, c) for c in cmdlist]))
|
self.__dict__.update(dict([(c.__name__, c) for c in cmdlist]))
|
||||||
|
|
||||||
from src.commands.default import admin, batchprocess, building, comms, general, player, help, system, unloggedin
|
from src.commands.default import (admin, batchprocess,
|
||||||
|
building, comms, general,
|
||||||
|
player, help, system, unloggedin)
|
||||||
add_cmds(admin)
|
add_cmds(admin)
|
||||||
add_cmds(building)
|
add_cmds(building)
|
||||||
add_cmds(batchprocess)
|
add_cmds(batchprocess)
|
||||||
|
|
@ -249,6 +265,7 @@ class DefaultCmds(_EvContainer):
|
||||||
default_cmds = DefaultCmds()
|
default_cmds = DefaultCmds()
|
||||||
del DefaultCmds
|
del DefaultCmds
|
||||||
|
|
||||||
|
|
||||||
class SystemCmds(_EvContainer):
|
class SystemCmds(_EvContainer):
|
||||||
"""
|
"""
|
||||||
Creating commands with keys set to these constants will make
|
Creating commands with keys set to these constants will make
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
@ -10,7 +10,8 @@ menu. Run the script with the -h flag to see usage information.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
import os
|
import os
|
||||||
import sys, signal
|
import sys
|
||||||
|
import signal
|
||||||
from optparse import OptionParser
|
from optparse import OptionParser
|
||||||
from subprocess import Popen
|
from subprocess import Popen
|
||||||
|
|
||||||
|
|
@ -132,7 +133,7 @@ from django.db import DatabaseError
|
||||||
from src.players.models import PlayerDB
|
from src.players.models import PlayerDB
|
||||||
try:
|
try:
|
||||||
superuser = PlayerDB.objects.get(id=1)
|
superuser = PlayerDB.objects.get(id=1)
|
||||||
except DatabaseError,e:
|
except DatabaseError, e:
|
||||||
print """
|
print """
|
||||||
Your database does not seem to be set up correctly.
|
Your database does not seem to be set up correctly.
|
||||||
(error was '%s')
|
(error was '%s')
|
||||||
|
|
@ -186,7 +187,7 @@ if os.name == 'nt':
|
||||||
os.path.join(os.path.dirname(twistd.__file__),
|
os.path.join(os.path.dirname(twistd.__file__),
|
||||||
os.pardir, os.pardir, os.pardir, os.pardir,
|
os.pardir, os.pardir, os.pardir, os.pardir,
|
||||||
'scripts', 'twistd.py'))
|
'scripts', 'twistd.py'))
|
||||||
bat_file = open('twistd.bat','w')
|
bat_file = open('twistd.bat', 'w')
|
||||||
bat_file.write("@\"%s\" \"%s\" %%*" % (sys.executable, twistd_path))
|
bat_file.write("@\"%s\" \"%s\" %%*" % (sys.executable, twistd_path))
|
||||||
bat_file.close()
|
bat_file.close()
|
||||||
print """
|
print """
|
||||||
|
|
@ -221,6 +222,7 @@ def get_pid(pidfile):
|
||||||
pid = f.read()
|
pid = f.read()
|
||||||
return pid
|
return pid
|
||||||
|
|
||||||
|
|
||||||
def del_pid(pidfile):
|
def del_pid(pidfile):
|
||||||
"""
|
"""
|
||||||
The pidfile should normally be removed after a process has finished, but
|
The pidfile should normally be removed after a process has finished, but
|
||||||
|
|
@ -229,6 +231,7 @@ def del_pid(pidfile):
|
||||||
if os.path.exists(pidfile):
|
if os.path.exists(pidfile):
|
||||||
os.remove(pidfile)
|
os.remove(pidfile)
|
||||||
|
|
||||||
|
|
||||||
def kill(pidfile, signal=SIG, succmsg="", errmsg="", restart_file=SERVER_RESTART, restart="reload"):
|
def kill(pidfile, signal=SIG, succmsg="", errmsg="", restart_file=SERVER_RESTART, restart="reload"):
|
||||||
"""
|
"""
|
||||||
Send a kill signal to a process based on PID. A customized success/error
|
Send a kill signal to a process based on PID. A customized success/error
|
||||||
|
|
@ -255,6 +258,7 @@ def kill(pidfile, signal=SIG, succmsg="", errmsg="", restart_file=SERVER_RESTART
|
||||||
return
|
return
|
||||||
print "Evennia:", errmsg
|
print "Evennia:", errmsg
|
||||||
|
|
||||||
|
|
||||||
def run_menu():
|
def run_menu():
|
||||||
"""
|
"""
|
||||||
This launches an interactive menu.
|
This launches an interactive menu.
|
||||||
|
|
@ -285,7 +289,7 @@ def run_menu():
|
||||||
errmsg = "The %s does not seem to be running."
|
errmsg = "The %s does not seem to be running."
|
||||||
if inp < 5:
|
if inp < 5:
|
||||||
if inp == 1:
|
if inp == 1:
|
||||||
pass # default operation
|
pass # default operation
|
||||||
elif inp == 2:
|
elif inp == 2:
|
||||||
cmdstr.extend(['--iserver'])
|
cmdstr.extend(['--iserver'])
|
||||||
elif inp == 3:
|
elif inp == 3:
|
||||||
|
|
@ -344,8 +348,9 @@ def handle_args(options, mode, service):
|
||||||
if inter:
|
if inter:
|
||||||
cmdstr.append('--iportal')
|
cmdstr.append('--iportal')
|
||||||
cmdstr.append('--noserver')
|
cmdstr.append('--noserver')
|
||||||
else: # all
|
else: # all
|
||||||
# for convenience we don't start logging of portal, only of server with this command.
|
# for convenience we don't start logging of
|
||||||
|
# portal, only of server with this command.
|
||||||
if inter:
|
if inter:
|
||||||
cmdstr.extend(['--iserver'])
|
cmdstr.extend(['--iserver'])
|
||||||
return cmdstr
|
return cmdstr
|
||||||
|
|
@ -429,7 +434,9 @@ def main():
|
||||||
parser = OptionParser(usage="%prog [-i] [menu|start|reload|stop [server|portal|all]]",
|
parser = OptionParser(usage="%prog [-i] [menu|start|reload|stop [server|portal|all]]",
|
||||||
description="""This is the main Evennia launcher. It handles the Portal and Server, the two services making up Evennia. Default is to operate on both services. Interactive mode sets the service to log to stdout, in the foreground. Note that when launching 'all' services with the \"--interactive\" flag, both services will be started, but only Server will actually be started in interactive mode, simply because this is the most commonly useful setup. To activate interactive mode also for Portal, use the menu or launch the two services explicitly as two separate calls to this program.""")
|
description="""This is the main Evennia launcher. It handles the Portal and Server, the two services making up Evennia. Default is to operate on both services. Interactive mode sets the service to log to stdout, in the foreground. Note that when launching 'all' services with the \"--interactive\" flag, both services will be started, but only Server will actually be started in interactive mode, simply because this is the most commonly useful setup. To activate interactive mode also for Portal, use the menu or launch the two services explicitly as two separate calls to this program.""")
|
||||||
|
|
||||||
parser.add_option('-i', '--interactive', action='store_true', dest='interactive', default=False, help="Start given processes in interactive mode.")
|
parser.add_option('-i', '--interactive', action='store_true',
|
||||||
|
dest='interactive', default=False,
|
||||||
|
help="Start given processes in interactive mode.")
|
||||||
|
|
||||||
options, args = parser.parse_args()
|
options, args = parser.parse_args()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
@ -32,6 +32,7 @@ from ev import default_cmds
|
||||||
#from contrib import misc_commands
|
#from contrib import misc_commands
|
||||||
#from contrib import chargen
|
#from contrib import chargen
|
||||||
|
|
||||||
|
|
||||||
class ExampleCmdSet(CmdSet):
|
class ExampleCmdSet(CmdSet):
|
||||||
"""
|
"""
|
||||||
Implements an empty, example cmdset.
|
Implements an empty, example cmdset.
|
||||||
|
|
@ -44,7 +45,8 @@ class ExampleCmdSet(CmdSet):
|
||||||
This is the only method defined in a cmdset, called during
|
This is the only method defined in a cmdset, called during
|
||||||
its creation. It should populate the set with command instances.
|
its creation. It should populate the set with command instances.
|
||||||
|
|
||||||
As and example we just add the empty base Command object. It prints some info.
|
As and example we just add the empty base Command object.
|
||||||
|
It prints some info.
|
||||||
"""
|
"""
|
||||||
self.add(Command())
|
self.add(Command())
|
||||||
|
|
||||||
|
|
@ -75,6 +77,7 @@ class CharacterCmdSet(default_cmds.CharacterCmdSet):
|
||||||
#self.add(lineeditor.CmdEditor())
|
#self.add(lineeditor.CmdEditor())
|
||||||
#self.add(misc_commands.CmdQuell())
|
#self.add(misc_commands.CmdQuell())
|
||||||
|
|
||||||
|
|
||||||
class UnloggedinCmdSet(default_cmds.UnloggedinCmdSet):
|
class UnloggedinCmdSet(default_cmds.UnloggedinCmdSet):
|
||||||
"""
|
"""
|
||||||
This is an example of how to overload the command set of the
|
This is an example of how to overload the command set of the
|
||||||
|
|
@ -99,6 +102,7 @@ class UnloggedinCmdSet(default_cmds.UnloggedinCmdSet):
|
||||||
# any commands you add below will overload the default ones.
|
# any commands you add below will overload the default ones.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
class PlayerCmdSet(default_cmds.PlayerCmdSet):
|
class PlayerCmdSet(default_cmds.PlayerCmdSet):
|
||||||
"""
|
"""
|
||||||
This is set is available to the player when they have no
|
This is set is available to the player when they have no
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ from ev import Command, CmdSet
|
||||||
# Commands defined on the red button
|
# Commands defined on the red button
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
class CmdNudge(Command):
|
class CmdNudge(Command):
|
||||||
"""
|
"""
|
||||||
Try to nudge the button's lid
|
Try to nudge the button's lid
|
||||||
|
|
@ -27,7 +28,7 @@ class CmdNudge(Command):
|
||||||
push the lid of the button away.
|
push the lid of the button away.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
key = "nudge lid" # two-word command name!
|
key = "nudge lid" # two-word command name!
|
||||||
aliases = ["nudge"]
|
aliases = ["nudge"]
|
||||||
locks = "cmd:all()"
|
locks = "cmd:all()"
|
||||||
|
|
||||||
|
|
@ -44,6 +45,7 @@ class CmdNudge(Command):
|
||||||
self.caller.msg("You manage to get a nail under the lid.")
|
self.caller.msg("You manage to get a nail under the lid.")
|
||||||
self.caller.execute_cmd("open lid")
|
self.caller.execute_cmd("open lid")
|
||||||
|
|
||||||
|
|
||||||
class CmdPush(Command):
|
class CmdPush(Command):
|
||||||
"""
|
"""
|
||||||
Push the red button
|
Push the red button
|
||||||
|
|
@ -82,7 +84,6 @@ class CmdPush(Command):
|
||||||
self.caller.msg(string)
|
self.caller.msg(string)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class CmdSmashGlass(Command):
|
class CmdSmashGlass(Command):
|
||||||
"""
|
"""
|
||||||
smash glass
|
smash glass
|
||||||
|
|
@ -118,7 +119,8 @@ class CmdSmashGlass(Command):
|
||||||
string += " you should just try to open the lid instead?"
|
string += " you should just try to open the lid instead?"
|
||||||
self.caller.msg(string)
|
self.caller.msg(string)
|
||||||
self.caller.location.msg_contents("%s tries to smash the glass of the button." %
|
self.caller.location.msg_contents("%s tries to smash the glass of the button." %
|
||||||
(self.caller.name), exclude=self.caller)
|
(self.caller.name), exclude=self.caller)
|
||||||
|
|
||||||
|
|
||||||
class CmdOpenLid(Command):
|
class CmdOpenLid(Command):
|
||||||
"""
|
"""
|
||||||
|
|
@ -144,12 +146,13 @@ class CmdOpenLid(Command):
|
||||||
string += "the lid will soon close again."
|
string += "the lid will soon close again."
|
||||||
self.caller.msg(string)
|
self.caller.msg(string)
|
||||||
self.caller.location.msg_contents("%s opens the lid of the button." %
|
self.caller.location.msg_contents("%s opens the lid of the button." %
|
||||||
(self.caller.name), exclude=self.caller)
|
(self.caller.name), exclude=self.caller)
|
||||||
# add the relevant cmdsets to button
|
# add the relevant cmdsets to button
|
||||||
self.obj.cmdset.add(LidClosedCmdSet)
|
self.obj.cmdset.add(LidClosedCmdSet)
|
||||||
# call object method
|
# call object method
|
||||||
self.obj.open_lid()
|
self.obj.open_lid()
|
||||||
|
|
||||||
|
|
||||||
class CmdCloseLid(Command):
|
class CmdCloseLid(Command):
|
||||||
"""
|
"""
|
||||||
close the lid
|
close the lid
|
||||||
|
|
@ -174,6 +177,7 @@ class CmdCloseLid(Command):
|
||||||
self.caller.location.msg_contents("%s closes the button's lid." %
|
self.caller.location.msg_contents("%s closes the button's lid." %
|
||||||
(self.caller.name), exclude=self.caller)
|
(self.caller.name), exclude=self.caller)
|
||||||
|
|
||||||
|
|
||||||
class CmdBlindLook(Command):
|
class CmdBlindLook(Command):
|
||||||
"""
|
"""
|
||||||
Looking around in darkness
|
Looking around in darkness
|
||||||
|
|
@ -209,7 +213,8 @@ class CmdBlindLook(Command):
|
||||||
string += "Until it wears off, all you can do is feel around blindly."
|
string += "Until it wears off, all you can do is feel around blindly."
|
||||||
self.caller.msg(string)
|
self.caller.msg(string)
|
||||||
self.caller.location.msg_contents("%s stumbles around, blinded." %
|
self.caller.location.msg_contents("%s stumbles around, blinded." %
|
||||||
(self.caller.name), exclude=self.caller)
|
(self.caller.name), exclude=self.caller)
|
||||||
|
|
||||||
|
|
||||||
class CmdBlindHelp(Command):
|
class CmdBlindHelp(Command):
|
||||||
"""
|
"""
|
||||||
|
|
@ -232,7 +237,6 @@ class CmdBlindHelp(Command):
|
||||||
# Command sets for the red button
|
# Command sets for the red button
|
||||||
#---------------------------------------------------------------
|
#---------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
# We next tuck these commands into their respective command sets.
|
# We next tuck these commands into their respective command sets.
|
||||||
# (note that we are overdoing the cdmset separation a bit here
|
# (note that we are overdoing the cdmset separation a bit here
|
||||||
# to show how it works).
|
# to show how it works).
|
||||||
|
|
@ -247,12 +251,13 @@ class DefaultCmdSet(CmdSet):
|
||||||
using obj.cmdset.add_default().
|
using obj.cmdset.add_default().
|
||||||
"""
|
"""
|
||||||
key = "RedButtonDefault"
|
key = "RedButtonDefault"
|
||||||
mergetype = "Union" # this is default, we don't really need to put it here.
|
mergetype = "Union" # this is default, we don't really need to put it here.
|
||||||
|
|
||||||
def at_cmdset_creation(self):
|
def at_cmdset_creation(self):
|
||||||
"Init the cmdset"
|
"Init the cmdset"
|
||||||
self.add(CmdPush())
|
self.add(CmdPush())
|
||||||
|
|
||||||
|
|
||||||
class LidClosedCmdSet(CmdSet):
|
class LidClosedCmdSet(CmdSet):
|
||||||
"""
|
"""
|
||||||
A simple cmdset tied to the redbutton object.
|
A simple cmdset tied to the redbutton object.
|
||||||
|
|
@ -274,6 +279,7 @@ class LidClosedCmdSet(CmdSet):
|
||||||
self.add(CmdSmashGlass())
|
self.add(CmdSmashGlass())
|
||||||
self.add(CmdOpenLid())
|
self.add(CmdOpenLid())
|
||||||
|
|
||||||
|
|
||||||
class LidOpenCmdSet(CmdSet):
|
class LidOpenCmdSet(CmdSet):
|
||||||
"""
|
"""
|
||||||
This is the opposite of the Closed cmdset.
|
This is the opposite of the Closed cmdset.
|
||||||
|
|
@ -288,6 +294,7 @@ class LidOpenCmdSet(CmdSet):
|
||||||
"setup the cmdset (just one command)"
|
"setup the cmdset (just one command)"
|
||||||
self.add(CmdCloseLid())
|
self.add(CmdCloseLid())
|
||||||
|
|
||||||
|
|
||||||
class BlindCmdSet(CmdSet):
|
class BlindCmdSet(CmdSet):
|
||||||
"""
|
"""
|
||||||
This is the cmdset added to the *player* when
|
This is the cmdset added to the *player* when
|
||||||
|
|
@ -300,8 +307,8 @@ class BlindCmdSet(CmdSet):
|
||||||
# we want to stop the player from walking around
|
# we want to stop the player from walking around
|
||||||
# in this blinded state, so we hide all exits too.
|
# in this blinded state, so we hide all exits too.
|
||||||
# (channel commands will still work).
|
# (channel commands will still work).
|
||||||
no_exits = True # keep player in the same room
|
no_exits = True # keep player in the same room
|
||||||
no_objs = True # don't allow object commands
|
no_objs = True # don't allow object commands
|
||||||
|
|
||||||
def at_cmdset_creation(self):
|
def at_cmdset_creation(self):
|
||||||
"Setup the blind cmdset"
|
"Setup the blind cmdset"
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ from ev import Command as BaseCommand
|
||||||
from ev import default_cmds
|
from ev import default_cmds
|
||||||
from ev import utils
|
from ev import utils
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
"""
|
"""
|
||||||
Inherit from this if you want to create your own
|
Inherit from this if you want to create your own
|
||||||
|
|
@ -34,7 +35,6 @@ class Command(BaseCommand):
|
||||||
# arg_regex = r"\s.*?|$" # optional regex detailing how the part after
|
# arg_regex = r"\s.*?|$" # optional regex detailing how the part after
|
||||||
# the cmdname must look to match this command.
|
# the cmdname must look to match this command.
|
||||||
|
|
||||||
|
|
||||||
# (we don't implement hook method access() here, you don't need to
|
# (we don't implement hook method access() here, you don't need to
|
||||||
# modify that unless you want to change how the lock system works
|
# modify that unless you want to change how the lock system works
|
||||||
# (in that case see src.commands.command.Command))
|
# (in that case see src.commands.command.Command))
|
||||||
|
|
@ -104,8 +104,8 @@ class MuxCommand(default_cmds.MuxCommand):
|
||||||
cmdhandler at this point, and stored in self.cmdname. The rest is stored
|
cmdhandler at this point, and stored in self.cmdname. The rest is stored
|
||||||
in self.args.
|
in self.args.
|
||||||
|
|
||||||
The MuxCommand parser breaks self.args into its constituents and stores them in the
|
The MuxCommand parser breaks self.args into its constituents and stores them
|
||||||
following variables:
|
in the following variables:
|
||||||
self.switches = optional list of /switches (without the /)
|
self.switches = optional list of /switches (without the /)
|
||||||
self.raw = This is the raw argument input, including switches
|
self.raw = This is the raw argument input, including switches
|
||||||
self.args = This is re-defined to be everything *except* the switches
|
self.args = This is re-defined to be everything *except* the switches
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
@ -20,5 +20,6 @@ does what you expect it to.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
def at_initial_setup():
|
def at_initial_setup():
|
||||||
pass
|
pass
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ at_server_cold_stop()
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
def at_server_start():
|
def at_server_start():
|
||||||
"""
|
"""
|
||||||
This is called every time the server starts up, regardless of
|
This is called every time the server starts up, regardless of
|
||||||
|
|
@ -33,6 +34,7 @@ def at_server_start():
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def at_server_stop():
|
def at_server_stop():
|
||||||
"""
|
"""
|
||||||
This is called just before a server is shut down, regardless
|
This is called just before a server is shut down, regardless
|
||||||
|
|
@ -40,18 +42,21 @@ def at_server_stop():
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def at_server_reload_start():
|
def at_server_reload_start():
|
||||||
"""
|
"""
|
||||||
This is called only when server starts back up after a reload.
|
This is called only when server starts back up after a reload.
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def at_server_reload_stop():
|
def at_server_reload_stop():
|
||||||
"""
|
"""
|
||||||
This is called only time the server stops before a reload.
|
This is called only time the server stops before a reload.
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def at_server_cold_start():
|
def at_server_cold_start():
|
||||||
"""
|
"""
|
||||||
This is called only when the server starts "cold", i.e. after a
|
This is called only when the server starts "cold", i.e. after a
|
||||||
|
|
@ -59,6 +64,7 @@ def at_server_cold_start():
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def at_server_cold_stop():
|
def at_server_cold_stop():
|
||||||
"""
|
"""
|
||||||
This is called only when the server goes down due to a shutdown or reset.
|
This is called only when the server goes down due to a shutdown or reset.
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ See many more examples of lock functions in src.locks.lockfuncs.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
def myfalse(accessing_obj, accessed_obj, *args, **kwargs):
|
def myfalse(accessing_obj, accessed_obj, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
called in lockstring with myfalse().
|
called in lockstring with myfalse().
|
||||||
|
|
|
||||||
|
|
@ -18,10 +18,11 @@
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
def testoob(character, *args, **kwargs):
|
def testoob(character, *args, **kwargs):
|
||||||
"Simple test function"
|
"Simple test function"
|
||||||
print "Called testoob: %s" % val
|
print "Called testoob: %s" % args
|
||||||
return "testoob did stuff to the input string '%s'!" % val
|
return "testoob did stuff to the input string '%s'!" % args
|
||||||
|
|
||||||
|
|
||||||
# MSDP_MAP is a standard suggestions for making it easy to create generic guis.
|
# MSDP_MAP is a standard suggestions for making it easy to create generic guis.
|
||||||
|
|
@ -62,7 +63,7 @@ MSDP_REPORTABLE = {
|
||||||
|
|
||||||
# Combat
|
# Combat
|
||||||
"OPPONENT_HEALTH": "opponent_health",
|
"OPPONENT_HEALTH": "opponent_health",
|
||||||
"OPPONENT_HEALTH_MAX":"opponent_health_max",
|
"OPPONENT_HEALTH_MAX": "opponent_health_max",
|
||||||
"OPPONENT_LEVEL": "opponent_level",
|
"OPPONENT_LEVEL": "opponent_level",
|
||||||
"OPPONENT_NAME": "opponent_name",
|
"OPPONENT_NAME": "opponent_name",
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ the Server startup process.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
def start_plugin_services(server):
|
def start_plugin_services(server):
|
||||||
"""
|
"""
|
||||||
This hook is called by Evennia, last in the Server startup process.
|
This hook is called by Evennia, last in the Server startup process.
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ the Portal startup process.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
def start_plugin_services(portal):
|
def start_plugin_services(portal):
|
||||||
"""
|
"""
|
||||||
This hook is called by Evennia, last in the Portal startup process.
|
This hook is called by Evennia, last in the Portal startup process.
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
@ -1,41 +1,42 @@
|
||||||
"""
|
"""
|
||||||
|
|
||||||
Template for Characters
|
Template for Characters
|
||||||
|
|
||||||
Copy this module up one level and name it as you like, then
|
Copy this module up one level and name it as you like, then
|
||||||
use it as a template to create your own Character class.
|
use it as a template to create your own Character class.
|
||||||
|
|
||||||
To make new logins default to creating characters
|
To make new logins default to creating characters
|
||||||
of your new type, change settings.BASE_CHARACTER_TYPECLASS to point to
|
of your new type, change settings.BASE_CHARACTER_TYPECLASS to point to
|
||||||
your new class, e.g.
|
your new class, e.g.
|
||||||
|
|
||||||
settings.BASE_CHARACTER_TYPECLASS = "game.gamesrc.objects.mychar.MyChar"
|
settings.BASE_CHARACTER_TYPECLASS = "game.gamesrc.objects.mychar.MyChar"
|
||||||
|
|
||||||
Note that objects already created in the database will not notice
|
Note that objects already created in the database will not notice
|
||||||
this change, you have to convert them manually e.g. with the
|
this change, you have to convert them manually e.g. with the
|
||||||
@typeclass command.
|
@typeclass command.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from ev import Character as DefaultCharacter
|
from ev import Character as DefaultCharacter
|
||||||
|
|
||||||
class Character(DefaultCharacter):
|
|
||||||
"""
|
class Character(DefaultCharacter):
|
||||||
The Character is like any normal Object (see example/object.py for
|
"""
|
||||||
a list of properties and methods), except it actually implements
|
The Character is like any normal Object (see example/object.py for
|
||||||
some of its hook methods to do some work:
|
a list of properties and methods), except it actually implements
|
||||||
|
some of its hook methods to do some work:
|
||||||
at_basetype_setup - always assigns the default_cmdset to this object type
|
|
||||||
(important!)sets locks so character cannot be picked up
|
at_basetype_setup - always assigns the default_cmdset to this object type
|
||||||
and its commands only be called by itself, not anyone else.
|
(important!)sets locks so character cannot be picked up
|
||||||
(to change things, use at_object_creation() instead)
|
and its commands only be called by itself, not anyone else.
|
||||||
at_after_move - launches the "look" command
|
(to change things, use at_object_creation() instead)
|
||||||
at_post_puppet(player) - when Player disconnects from the Character, we
|
at_after_move - launches the "look" command
|
||||||
store the current location, so the "unconnected" character
|
at_post_puppet(player) - when Player disconnects from the Character, we
|
||||||
object does not need to stay on grid but can be given a
|
store the current location, so the "unconnected" character
|
||||||
None-location while offline.
|
object does not need to stay on grid but can be given a
|
||||||
at_pre_puppet - just before Player re-connects, retrieves the character's old
|
None-location while offline.
|
||||||
location and puts it back on the grid with a "charname has
|
at_pre_puppet - just before Player re-connects, retrieves the character's
|
||||||
connected" message echoed to the room
|
old location and puts it back on the grid with a "charname
|
||||||
|
has connected" message echoed to the room
|
||||||
"""
|
|
||||||
pass
|
"""
|
||||||
|
pass
|
||||||
|
|
|
||||||
|
|
@ -1,43 +1,44 @@
|
||||||
"""
|
"""
|
||||||
|
|
||||||
Template module for Exits
|
Template module for Exits
|
||||||
|
|
||||||
Copy this module up one level and name it as you like, then
|
Copy this module up one level and name it as you like, then
|
||||||
use it as a template to create your own Exits.
|
use it as a template to create your own Exits.
|
||||||
|
|
||||||
To make the default commands (such as @dig/@open) default to creating exits
|
To make the default commands (such as @dig/@open) default to creating exits
|
||||||
of your new type, change settings.BASE_EXIT_TYPECLASS to point to
|
of your new type, change settings.BASE_EXIT_TYPECLASS to point to
|
||||||
your new class, e.g.
|
your new class, e.g.
|
||||||
|
|
||||||
settings.BASE_EXIT_TYPECLASS = "game.gamesrc.objects.myexit.MyExit"
|
settings.BASE_EXIT_TYPECLASS = "game.gamesrc.objects.myexit.MyExit"
|
||||||
|
|
||||||
Note that objects already created in the database will not notice
|
Note that objects already created in the database will not notice
|
||||||
this change, you have to convert them manually e.g. with the
|
this change, you have to convert them manually e.g. with the
|
||||||
@typeclass command.
|
@typeclass command.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from ev import Exit as DefaultExit
|
from ev import Exit as DefaultExit
|
||||||
|
|
||||||
class Exit(DefaultExit):
|
|
||||||
"""
|
class Exit(DefaultExit):
|
||||||
Exits are connectors between rooms. Exits are normal Objects except
|
"""
|
||||||
they defines the 'destination' property. It also does work in the
|
Exits are connectors between rooms. Exits are normal Objects except
|
||||||
following methods:
|
they defines the 'destination' property. It also does work in the
|
||||||
|
following methods:
|
||||||
basetype_setup() - sets default exit locks (to change, use at_object_creation instead)
|
|
||||||
at_cmdset_get() - this auto-creates and caches a command and a command set on itself
|
basetype_setup() - sets default exit locks (to change, use at_object_creation instead)
|
||||||
with the same name as the Exit object. This
|
at_cmdset_get() - this auto-creates and caches a command and a command set on itself
|
||||||
allows users to use the exit by only giving its
|
with the same name as the Exit object. This
|
||||||
name alone on the command line.
|
allows users to use the exit by only giving its
|
||||||
at_failed_traverse() - gives a default error message ("You cannot
|
name alone on the command line.
|
||||||
go there") if exit traversal fails and an
|
at_failed_traverse() - gives a default error message ("You cannot
|
||||||
attribute err_traverse is not defined.
|
go there") if exit traversal fails and an
|
||||||
|
attribute err_traverse is not defined.
|
||||||
Relevant hooks to overload (compared to other types of Objects):
|
|
||||||
at_before_traverse(traveller) - called just before traversing
|
Relevant hooks to overload (compared to other types of Objects):
|
||||||
at_after_traverse(traveller, source_loc) - called just after traversing
|
at_before_traverse(traveller) - called just before traversing
|
||||||
at_failed_traverse(traveller) - called if traversal failed for some reason. Will
|
at_after_traverse(traveller, source_loc) - called just after traversing
|
||||||
not be called if the attribute 'err_traverse' is
|
at_failed_traverse(traveller) - called if traversal failed for some reason. Will
|
||||||
defined, in which case that will simply be echoed.
|
not be called if the attribute 'err_traverse' is
|
||||||
"""
|
defined, in which case that will simply be echoed.
|
||||||
pass
|
"""
|
||||||
|
pass
|
||||||
|
|
|
||||||
|
|
@ -1,127 +1,167 @@
|
||||||
"""
|
"""
|
||||||
|
|
||||||
Template for Objects
|
Template for Objects
|
||||||
|
|
||||||
Copy this module up one level and name it as you like, then
|
Copy this module up one level and name it as you like, then
|
||||||
use it as a template to create your own Objects.
|
use it as a template to create your own Objects.
|
||||||
|
|
||||||
To make the default commands default to creating objects of your new
|
To make the default commands default to creating objects of your new
|
||||||
type (and also change the "fallback" object used when typeclass
|
type (and also change the "fallback" object used when typeclass
|
||||||
creation fails), change settings.BASE_OBJECT_TYPECLASS to point to
|
creation fails), change settings.BASE_OBJECT_TYPECLASS to point to
|
||||||
your new class, e.g.
|
your new class, e.g.
|
||||||
|
|
||||||
settings.BASE_OBJECT_TYPECLASS = "game.gamesrc.objects.myobj.MyObj"
|
settings.BASE_OBJECT_TYPECLASS = "game.gamesrc.objects.myobj.MyObj"
|
||||||
|
|
||||||
Note that objects already created in the database will not notice
|
Note that objects already created in the database will not notice
|
||||||
this change, you have to convert them manually e.g. with the
|
this change, you have to convert them manually e.g. with the
|
||||||
@typeclass command.
|
@typeclass command.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from ev import Object as DefaultObject
|
from ev import Object as DefaultObject
|
||||||
|
|
||||||
class Object(DefaultObject):
|
|
||||||
"""
|
class Object(DefaultObject):
|
||||||
This is the root typeclass object, implementing an in-game Evennia
|
"""
|
||||||
game object, such as having a location, being able to be
|
This is the root typeclass object, implementing an in-game Evennia
|
||||||
manipulated or looked at, etc. If you create a new typeclass, it
|
game object, such as having a location, being able to be
|
||||||
must always inherit from this object (or any of the other objects
|
manipulated or looked at, etc. If you create a new typeclass, it
|
||||||
in this file, since they all actually inherit from BaseObject, as
|
must always inherit from this object (or any of the other objects
|
||||||
seen in src.object.objects).
|
in this file, since they all actually inherit from BaseObject, as
|
||||||
|
seen in src.object.objects).
|
||||||
The BaseObject class implements several hooks tying into the game
|
|
||||||
engine. By re-implementing these hooks you can control the
|
The BaseObject class implements several hooks tying into the game
|
||||||
system. You should never need to re-implement special Python
|
engine. By re-implementing these hooks you can control the
|
||||||
methods, such as __init__ and especially never __getattribute__ and
|
system. You should never need to re-implement special Python
|
||||||
__setattr__ since these are used heavily by the typeclass system
|
methods, such as __init__ and especially never __getattribute__ and
|
||||||
of Evennia and messing with them might well break things for you.
|
__setattr__ since these are used heavily by the typeclass system
|
||||||
|
of Evennia and messing with them might well break things for you.
|
||||||
|
|
||||||
* Base properties defined/available on all Objects
|
|
||||||
|
* Base properties defined/available on all Objects
|
||||||
key (string) - name of object
|
|
||||||
name (string)- same as key
|
key (string) - name of object
|
||||||
aliases (list of strings) - aliases to the object. Will be saved to database as AliasDB entries but returned as strings.
|
name (string)- same as key
|
||||||
dbref (int, read-only) - unique #id-number. Also "id" can be used.
|
aliases (list of strings) - aliases to the object. Will be saved to
|
||||||
dbobj (Object, read-only) - link to database model. dbobj.typeclass points back to this class
|
database as AliasDB entries but returned as strings.
|
||||||
typeclass (Object, read-only) - this links back to this class as an identified only. Use self.swap_typeclass() to switch.
|
dbref (int, read-only) - unique #id-number. Also "id" can be used.
|
||||||
date_created (string) - time stamp of object creation
|
dbobj (Object, read-only) - link to database model. dbobj.typeclass points
|
||||||
permissions (list of strings) - list of permission strings
|
back to this class
|
||||||
|
typeclass (Object, read-only) - this links back to this class as an
|
||||||
player (Player) - controlling player (if any, only set together with sessid below)
|
identified only. Use self.swap_typeclass() to switch.
|
||||||
sessid (int, read-only) - session id (if any, only set together with player above)
|
date_created (string) - time stamp of object creation
|
||||||
location (Object) - current location. Is None if this is a room
|
permissions (list of strings) - list of permission strings
|
||||||
home (Object) - safety start-location
|
|
||||||
sessions (list of Sessions, read-only) - returns all sessions connected to this object
|
player (Player) - controlling player (if any, only set together with
|
||||||
has_player (bool, read-only)- will only return *connected* players
|
sessid below)
|
||||||
contents (list of Objects, read-only) - returns all objects inside this object (including exits)
|
sessid (int, read-only) - session id (if any, only set together with
|
||||||
exits (list of Objects, read-only) - returns all exits from this object, if any
|
player above)
|
||||||
destination (Object) - only set if this object is an exit.
|
location (Object) - current location. Is None if this is a room
|
||||||
is_superuser (bool, read-only) - True/False if this user is a superuser
|
home (Object) - safety start-location
|
||||||
|
sessions (list of Sessions, read-only) - returns all sessions connected
|
||||||
* Handlers available
|
to this object
|
||||||
|
has_player (bool, read-only)- will only return *connected* players
|
||||||
locks - lock-handler: use locks.add() to add new lock strings
|
contents (list of Objects, read-only) - returns all objects inside this
|
||||||
db - attribute-handler: store/retrieve database attributes on this self.db.myattr=val, val=self.db.myattr
|
object (including exits)
|
||||||
ndb - non-persistent attribute handler: same as db but does not create a database entry when storing data
|
exits (list of Objects, read-only) - returns all exits from this
|
||||||
scripts - script-handler. Add new scripts to object with scripts.add()
|
object, if any
|
||||||
cmdset - cmdset-handler. Use cmdset.add() to add new cmdsets to object
|
destination (Object) - only set if this object is an exit.
|
||||||
nicks - nick-handler. New nicks with nicks.add().
|
is_superuser (bool, read-only) - True/False if this user is a superuser
|
||||||
|
|
||||||
* Helper methods (see src.objects.objects.py for full headers)
|
* Handlers available
|
||||||
|
|
||||||
search(ostring, global_search=False, attribute_name=None, use_nicks=False, location=None, ignore_errors=False, player=False)
|
locks - lock-handler: use locks.add() to add new lock strings
|
||||||
execute_cmd(raw_string)
|
db - attribute-handler: store/retrieve database attributes on this
|
||||||
msg(text=None, **kwargs)
|
self.db.myattr=val, val=self.db.myattr
|
||||||
msg_contents(message, exclude=None, from_obj=None, **kwargs)
|
ndb - non-persistent attribute handler: same as db but does not create
|
||||||
move_to(destination, quiet=False, emit_to_obj=None, use_destination=True)
|
a database entry when storing data
|
||||||
copy(new_key=None)
|
scripts - script-handler. Add new scripts to object with scripts.add()
|
||||||
delete()
|
cmdset - cmdset-handler. Use cmdset.add() to add new cmdsets to object
|
||||||
is_typeclass(typeclass, exact=False)
|
nicks - nick-handler. New nicks with nicks.add().
|
||||||
swap_typeclass(new_typeclass, clean_attributes=False, no_default=True)
|
|
||||||
access(accessing_obj, access_type='read', default=False)
|
* Helper methods (see src.objects.objects.py for full headers)
|
||||||
check_permstring(permstring)
|
|
||||||
|
search(ostring, global_search=False, attribute_name=None,
|
||||||
* Hooks (these are class methods, so their arguments should also start with self):
|
use_nicks=False, location=None, ignore_errors=False, player=False)
|
||||||
|
execute_cmd(raw_string)
|
||||||
basetype_setup() - only called once, used for behind-the-scenes setup. Normally not modified.
|
msg(text=None, **kwargs)
|
||||||
basetype_posthook_setup() - customization in basetype, after the object has been created; Normally not modified.
|
msg_contents(message, exclude=None, from_obj=None, **kwargs)
|
||||||
|
move_to(destination, quiet=False, emit_to_obj=None, use_destination=True)
|
||||||
at_object_creation() - only called once, when object is first created. Object customizations go here.
|
copy(new_key=None)
|
||||||
at_object_delete() - called just before deleting an object. If returning False, deletion is aborted. Note that all objects
|
delete()
|
||||||
inside a deleted object are automatically moved to their <home>, they don't need to be removed here.
|
is_typeclass(typeclass, exact=False)
|
||||||
|
swap_typeclass(new_typeclass, clean_attributes=False, no_default=True)
|
||||||
at_init() - called whenever typeclass is cached from memory, at least once every server restart/reload
|
access(accessing_obj, access_type='read', default=False)
|
||||||
at_cmdset_get() - this is called just before the command handler requests a cmdset from this object
|
check_permstring(permstring)
|
||||||
at_pre_puppet(player)- (player-controlled objects only) called just before puppeting
|
|
||||||
at_post_puppet() - (player-controlled objects only) called just after completing connection player<->object
|
* Hooks (these are class methods, so args should start with self):
|
||||||
at_pre_unpuppet() - (player-controlled objects only) called just before un-puppeting
|
|
||||||
at_post_unpuppet(player) - (player-controlled objects only) called just after disconnecting player<->object link
|
basetype_setup() - only called once, used for behind-the-scenes
|
||||||
at_server_reload() - called before server is reloaded
|
setup. Normally not modified.
|
||||||
at_server_shutdown() - called just before server is fully shut down
|
basetype_posthook_setup() - customization in basetype, after the object
|
||||||
|
has been created; Normally not modified.
|
||||||
at_access_success(accessing_obj, access_type) - called if an lock access check succeeded on this object
|
|
||||||
at_access_failure(accessing_obj, access_type) - called if an lock access check failed on this object
|
at_object_creation() - only called once, when object is first created.
|
||||||
|
Object customizations go here.
|
||||||
at_before_move(destination) - called just before moving object to the destination. If returns False, move is cancelled.
|
at_object_delete() - called just before deleting an object. If returning
|
||||||
announce_move_from(destination) - called in old location, just before move, if obj.move_to() has quiet=False
|
False, deletion is aborted. Note that all objects
|
||||||
announce_move_to(source_location) - called in new location, just after move, if obj.move_to() has quiet=False
|
inside a deleted object are automatically moved
|
||||||
at_after_move(source_location) - always called after a move has been successfully performed.
|
to their <home>, they don't need to be removed here.
|
||||||
at_object_leave(obj, target_location) - called when an object leaves this object in any fashion
|
|
||||||
at_object_receive(obj, source_location) - called when this object receives another object
|
at_init() - called whenever typeclass is cached from memory,
|
||||||
|
at least once every server restart/reload
|
||||||
at_before_traverse(traversing_object) - (exit-objects only) called just before an object traverses this object
|
at_cmdset_get() - this is called just before the command handler
|
||||||
at_after_traverse(traversing_object, source_location) - (exit-objects only) called just after a traversal has happened.
|
requests a cmdset from this object
|
||||||
at_failed_traverse(traversing_object) - (exit-objects only) called if traversal fails and property err_traverse is not defined.
|
at_pre_puppet(player)- (player-controlled objects only) called just
|
||||||
|
before puppeting
|
||||||
at_msg_receive(self, msg, from_obj=None, **kwargs) - called when a message (via self.msg()) is sent to this obj.
|
at_post_puppet() - (player-controlled objects only) called just
|
||||||
If returns false, aborts send.
|
after completing connection player<->object
|
||||||
at_msg_send(self, msg, to_obj=None, **kwargs) - called when this objects sends a message to someone via self.msg().
|
at_pre_unpuppet() - (player-controlled objects only) called just
|
||||||
|
before un-puppeting
|
||||||
return_appearance(looker) - describes this object. Used by "look" command by default
|
at_post_unpuppet(player) - (player-controlled objects only) called just
|
||||||
at_desc(looker=None) - called by 'look' whenever the appearance is requested.
|
after disconnecting player<->object link
|
||||||
at_get(getter) - called after object has been picked up. Does not stop pickup.
|
at_server_reload() - called before server is reloaded
|
||||||
at_drop(dropper) - called when this object has been dropped.
|
at_server_shutdown() - called just before server is fully shut down
|
||||||
at_say(speaker, message) - by default, called if an object inside this object speaks
|
|
||||||
|
at_access_success(accessing_obj, access_type) - called if an lock access
|
||||||
"""
|
check succeeded on this object
|
||||||
pass
|
at_access_failure(accessing_obj, access_type) - called if an lock access
|
||||||
|
check failed on this object
|
||||||
|
|
||||||
|
at_before_move(destination) - called just before moving object
|
||||||
|
to the destination. If returns False, move is cancelled.
|
||||||
|
announce_move_from(destination) - called in old location, just
|
||||||
|
before move, if obj.move_to() has quiet=False
|
||||||
|
announce_move_to(source_location) - called in new location, just
|
||||||
|
after move, if obj.move_to() has quiet=False
|
||||||
|
at_after_move(source_location) - always called after a move has
|
||||||
|
been successfully performed.
|
||||||
|
at_object_leave(obj, target_location) - called when an object leaves
|
||||||
|
this object in any fashion
|
||||||
|
at_object_receive(obj, source_location) - called when this object receives
|
||||||
|
another object
|
||||||
|
|
||||||
|
at_before_traverse(traversing_object) - (exit-objects only)
|
||||||
|
called just before an object traverses this object
|
||||||
|
at_after_traverse(traversing_object, source_location) - (exit-objects only)
|
||||||
|
called just after a traversal has happened.
|
||||||
|
at_failed_traverse(traversing_object) - (exit-objects only) called if
|
||||||
|
traversal fails and property err_traverse is not defined.
|
||||||
|
|
||||||
|
at_msg_receive(self, msg, from_obj=None, **kwargs) - called when a message
|
||||||
|
(via self.msg()) is sent to this obj.
|
||||||
|
If returns false, aborts send.
|
||||||
|
at_msg_send(self, msg, to_obj=None, **kwargs) - called when this objects
|
||||||
|
sends a message to someone via self.msg().
|
||||||
|
|
||||||
|
return_appearance(looker) - describes this object. Used by "look"
|
||||||
|
command by default
|
||||||
|
at_desc(looker=None) - called by 'look' whenever the
|
||||||
|
appearance is requested.
|
||||||
|
at_get(getter) - called after object has been picked up.
|
||||||
|
Does not stop pickup.
|
||||||
|
at_drop(dropper) - called when this object has been dropped.
|
||||||
|
at_say(speaker, message) - by default, called if an object inside this
|
||||||
|
object speaks
|
||||||
|
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
|
||||||
|
|
@ -1,90 +1,91 @@
|
||||||
"""
|
"""
|
||||||
|
|
||||||
Template module for Players
|
Template module for Players
|
||||||
|
|
||||||
Copy this module up one level and name it as you like, then
|
Copy this module up one level and name it as you like, then
|
||||||
use it as a template to create your own Player class.
|
use it as a template to create your own Player class.
|
||||||
|
|
||||||
To make the default account login default to using a Player
|
To make the default account login default to using a Player
|
||||||
of your new type, change settings.BASE_PLAYER_TYPECLASS to point to
|
of your new type, change settings.BASE_PLAYER_TYPECLASS to point to
|
||||||
your new class, e.g.
|
your new class, e.g.
|
||||||
|
|
||||||
settings.BASE_PLAYER_TYPECLASS = "game.gamesrc.objects.myplayer.MyPlayer"
|
settings.BASE_PLAYER_TYPECLASS = "game.gamesrc.objects.myplayer.MyPlayer"
|
||||||
|
|
||||||
Note that objects already created in the database will not notice
|
Note that objects already created in the database will not notice
|
||||||
this change, you have to convert them manually e.g. with the
|
this change, you have to convert them manually e.g. with the
|
||||||
@typeclass command.
|
@typeclass command.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from ev import Player as DefaultPlayer
|
from ev import Player as DefaultPlayer
|
||||||
|
|
||||||
class Player(DefaultPlayer):
|
|
||||||
"""
|
class Player(DefaultPlayer):
|
||||||
This class describes the actual OOC player (i.e. the user connecting
|
"""
|
||||||
to the MUD). It does NOT have visual appearance in the game world (that
|
This class describes the actual OOC player (i.e. the user connecting
|
||||||
is handled by the character which is connected to this). Comm channels
|
to the MUD). It does NOT have visual appearance in the game world (that
|
||||||
are attended/joined using this object.
|
is handled by the character which is connected to this). Comm channels
|
||||||
|
are attended/joined using this object.
|
||||||
It can be useful e.g. for storing configuration options for your game, but
|
|
||||||
should generally not hold any character-related info (that's best handled
|
It can be useful e.g. for storing configuration options for your game, but
|
||||||
on the character level).
|
should generally not hold any character-related info (that's best handled
|
||||||
|
on the character level).
|
||||||
Can be set using BASE_PLAYER_TYPECLASS.
|
|
||||||
|
Can be set using BASE_PLAYER_TYPECLASS.
|
||||||
|
|
||||||
* available properties
|
|
||||||
|
* available properties
|
||||||
key (string) - name of player
|
|
||||||
name (string)- wrapper for user.username
|
key (string) - name of player
|
||||||
aliases (list of strings) - aliases to the object. Will be saved to database as AliasDB entries but returned as strings.
|
name (string)- wrapper for user.username
|
||||||
dbref (int, read-only) - unique #id-number. Also "id" can be used.
|
aliases (list of strings) - aliases to the object. Will be saved to database as AliasDB entries but returned as strings.
|
||||||
dbobj (Player, read-only) - link to database model. dbobj.typeclass points back to this class
|
dbref (int, read-only) - unique #id-number. Also "id" can be used.
|
||||||
typeclass (Player, read-only) - this links back to this class as an identified only. Use self.swap_typeclass() to switch.
|
dbobj (Player, read-only) - link to database model. dbobj.typeclass points back to this class
|
||||||
date_created (string) - time stamp of object creation
|
typeclass (Player, read-only) - this links back to this class as an identified only. Use self.swap_typeclass() to switch.
|
||||||
permissions (list of strings) - list of permission strings
|
date_created (string) - time stamp of object creation
|
||||||
|
permissions (list of strings) - list of permission strings
|
||||||
user (User, read-only) - django User authorization object
|
|
||||||
obj (Object) - game object controlled by player. 'character' can also be used.
|
user (User, read-only) - django User authorization object
|
||||||
sessions (list of Sessions) - sessions connected to this player
|
obj (Object) - game object controlled by player. 'character' can also be used.
|
||||||
is_superuser (bool, read-only) - if the connected user is a superuser
|
sessions (list of Sessions) - sessions connected to this player
|
||||||
|
is_superuser (bool, read-only) - if the connected user is a superuser
|
||||||
* Handlers
|
|
||||||
|
* Handlers
|
||||||
locks - lock-handler: use locks.add() to add new lock strings
|
|
||||||
db - attribute-handler: store/retrieve database attributes on this self.db.myattr=val, val=self.db.myattr
|
locks - lock-handler: use locks.add() to add new lock strings
|
||||||
ndb - non-persistent attribute handler: same as db but does not create a database entry when storing data
|
db - attribute-handler: store/retrieve database attributes on this self.db.myattr=val, val=self.db.myattr
|
||||||
scripts - script-handler. Add new scripts to object with scripts.add()
|
ndb - non-persistent attribute handler: same as db but does not create a database entry when storing data
|
||||||
cmdset - cmdset-handler. Use cmdset.add() to add new cmdsets to object
|
scripts - script-handler. Add new scripts to object with scripts.add()
|
||||||
nicks - nick-handler. New nicks with nicks.add().
|
cmdset - cmdset-handler. Use cmdset.add() to add new cmdsets to object
|
||||||
|
nicks - nick-handler. New nicks with nicks.add().
|
||||||
* Helper methods
|
|
||||||
|
* Helper methods
|
||||||
msg(text=None, **kwargs)
|
|
||||||
swap_character(new_character, delete_old_character=False)
|
msg(text=None, **kwargs)
|
||||||
execute_cmd(raw_string, sessid=None)
|
swap_character(new_character, delete_old_character=False)
|
||||||
search(ostring, global_search=False, attribute_name=None, use_nicks=False, location=None, ignore_errors=False, player=False)
|
execute_cmd(raw_string, sessid=None)
|
||||||
is_typeclass(typeclass, exact=False)
|
search(ostring, global_search=False, attribute_name=None, use_nicks=False, location=None, ignore_errors=False, player=False)
|
||||||
swap_typeclass(new_typeclass, clean_attributes=False, no_default=True)
|
is_typeclass(typeclass, exact=False)
|
||||||
access(accessing_obj, access_type='read', default=False)
|
swap_typeclass(new_typeclass, clean_attributes=False, no_default=True)
|
||||||
check_permstring(permstring)
|
access(accessing_obj, access_type='read', default=False)
|
||||||
|
check_permstring(permstring)
|
||||||
* Hook methods (when re-implementation, remember methods need to have self as first arg)
|
|
||||||
|
* Hook methods (when re-implementation, remember methods need to have self as first arg)
|
||||||
basetype_setup()
|
|
||||||
at_player_creation()
|
basetype_setup()
|
||||||
|
at_player_creation()
|
||||||
- note that the following hooks are also found on Objects and are
|
|
||||||
usually handled on the character level:
|
- note that the following hooks are also found on Objects and are
|
||||||
|
usually handled on the character level:
|
||||||
at_init()
|
|
||||||
at_cmdset_get()
|
at_init()
|
||||||
at_first_login()
|
at_cmdset_get()
|
||||||
at_post_login(sessid=None)
|
at_first_login()
|
||||||
at_disconnect()
|
at_post_login(sessid=None)
|
||||||
at_message_receive()
|
at_disconnect()
|
||||||
at_message_send()
|
at_message_receive()
|
||||||
at_server_reload()
|
at_message_send()
|
||||||
at_server_shutdown()
|
at_server_reload()
|
||||||
|
at_server_shutdown()
|
||||||
"""
|
|
||||||
pass
|
"""
|
||||||
|
pass
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ from game.gamesrc.commands.examples import cmdset_red_button as cmdsetexamples
|
||||||
# Definition of the object itself
|
# Definition of the object itself
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
class RedButton(Object):
|
class RedButton(Object):
|
||||||
"""
|
"""
|
||||||
This class describes an evil red button. It will use the script
|
This class describes an evil red button. It will use the script
|
||||||
|
|
|
||||||
|
|
@ -1,32 +1,33 @@
|
||||||
"""
|
"""
|
||||||
|
|
||||||
Template module for Rooms
|
Template module for Rooms
|
||||||
|
|
||||||
Copy this module up one level and name it as you like, then
|
Copy this module up one level and name it as you like, then
|
||||||
use it as a template to create your own Objects.
|
use it as a template to create your own Objects.
|
||||||
|
|
||||||
To make the default commands (such as @dig) default to creating rooms
|
To make the default commands (such as @dig) default to creating rooms
|
||||||
of your new type, change settings.BASE_ROOM_TYPECLASS to point to
|
of your new type, change settings.BASE_ROOM_TYPECLASS to point to
|
||||||
your new class, e.g.
|
your new class, e.g.
|
||||||
|
|
||||||
settings.BASE_ROOM_TYPECLASS = "game.gamesrc.objects.myroom.MyRoom"
|
settings.BASE_ROOM_TYPECLASS = "game.gamesrc.objects.myroom.MyRoom"
|
||||||
|
|
||||||
Note that objects already created in the database will not notice
|
Note that objects already created in the database will not notice
|
||||||
this change, you have to convert them manually e.g. with the
|
this change, you have to convert them manually e.g. with the
|
||||||
@typeclass command.
|
@typeclass command.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from ev import Room as DefaultRoom
|
from ev import Room as DefaultRoom
|
||||||
|
|
||||||
class Room(DefaultRoom):
|
|
||||||
"""
|
class Room(DefaultRoom):
|
||||||
Rooms are like any Object, except their location is None
|
"""
|
||||||
(which is default). They also use basetype_setup() to
|
Rooms are like any Object, except their location is None
|
||||||
add locks so they cannot be puppeted or picked up.
|
(which is default). They also use basetype_setup() to
|
||||||
(to change that, use at_object_creation instead)
|
add locks so they cannot be puppeted or picked up.
|
||||||
|
(to change that, use at_object_creation instead)
|
||||||
See examples/object.py for a list of
|
|
||||||
properties and methods available on all Objects.
|
See examples/object.py for a list of
|
||||||
"""
|
properties and methods available on all Objects.
|
||||||
pass
|
"""
|
||||||
|
pass
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
@ -22,9 +22,9 @@ class BodyFunctions(Script):
|
||||||
def at_script_creation(self):
|
def at_script_creation(self):
|
||||||
self.key = "bodyfunction"
|
self.key = "bodyfunction"
|
||||||
self.desc = "Adds various timed events to a character."
|
self.desc = "Adds various timed events to a character."
|
||||||
self.interval = 20 # seconds
|
self.interval = 20 # seconds
|
||||||
#self.repeats = 5 # repeat only a certain number of times
|
#self.repeats = 5 # repeat only a certain number of times
|
||||||
self.start_delay = True # wait self.interval until first call
|
self.start_delay = True # wait self.interval until first call
|
||||||
#self.persistent = True
|
#self.persistent = True
|
||||||
|
|
||||||
def at_repeat(self):
|
def at_repeat(self):
|
||||||
|
|
|
||||||
|
|
@ -112,10 +112,10 @@ class BlindedState(Script):
|
||||||
"""
|
"""
|
||||||
self.key = "temporary_blinder"
|
self.key = "temporary_blinder"
|
||||||
self.desc = "Temporarily blinds the player for a little while."
|
self.desc = "Temporarily blinds the player for a little while."
|
||||||
self.interval = 20 # seconds
|
self.interval = 20 # seconds
|
||||||
self.start_delay = True # we don't want it to stop until after 20s.
|
self.start_delay = True # we don't want it to stop until after 20s.
|
||||||
self.repeats = 1 # this will go away after interval seconds.
|
self.repeats = 1 # this will go away after interval seconds.
|
||||||
self.persistent = False # we will ditch this if server goes down
|
self.persistent = False # we will ditch this if server goes down
|
||||||
|
|
||||||
def at_start(self):
|
def at_start(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -139,8 +139,8 @@ class BlindedState(Script):
|
||||||
self.obj.location.msg_contents("%s seems to be recovering their eyesight."
|
self.obj.location.msg_contents("%s seems to be recovering their eyesight."
|
||||||
% self.obj.name,
|
% self.obj.name,
|
||||||
exclude=self.obj)
|
exclude=self.obj)
|
||||||
self.obj.cmdset.delete() # this will clear the latest added cmdset,
|
self.obj.cmdset.delete() # this will clear the latest added cmdset,
|
||||||
# (which is the blinded one).
|
# (which is the blinded one).
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
|
|
@ -169,11 +169,11 @@ class CloseLidEvent(Script):
|
||||||
"""
|
"""
|
||||||
self.key = "lid_closer"
|
self.key = "lid_closer"
|
||||||
self.desc = "Closes lid on a red buttons"
|
self.desc = "Closes lid on a red buttons"
|
||||||
self.interval = 20 # seconds
|
self.interval = 20 # seconds
|
||||||
self.start_delay = True # we want to pospone the launch.
|
self.start_delay = True # we want to pospone the launch.
|
||||||
self.repeats = 1 # we only close the lid once
|
self.repeats = 1 # we only close the lid once
|
||||||
self.persistent = True # even if the server crashes in those 20 seconds,
|
self.persistent = True # even if the server crashes in those 20 seconds,
|
||||||
# the lid will still close once the game restarts.
|
# the lid will still close once the game restarts.
|
||||||
|
|
||||||
def is_valid(self):
|
def is_valid(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -207,9 +207,9 @@ class BlinkButtonEvent(Script):
|
||||||
"""
|
"""
|
||||||
self.key = "blink_button"
|
self.key = "blink_button"
|
||||||
self.desc = "Blinks red buttons"
|
self.desc = "Blinks red buttons"
|
||||||
self.interval = 35 #seconds
|
self.interval = 35 #seconds
|
||||||
self.start_delay = False #blink right away
|
self.start_delay = False #blink right away
|
||||||
self.persistent = True #keep blinking also after server reboot
|
self.persistent = True #keep blinking also after server reboot
|
||||||
|
|
||||||
def is_valid(self):
|
def is_valid(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -239,10 +239,10 @@ class DeactivateButtonEvent(Script):
|
||||||
"""
|
"""
|
||||||
self.key = "deactivate_button"
|
self.key = "deactivate_button"
|
||||||
self.desc = "Deactivate red button temporarily"
|
self.desc = "Deactivate red button temporarily"
|
||||||
self.interval = 21 #seconds
|
self.interval = 21 #seconds
|
||||||
self.start_delay = True # wait with the first repeat for self.interval seconds.
|
self.start_delay = True # wait with the first repeat for self.interval seconds.
|
||||||
self.persistent = True
|
self.persistent = True
|
||||||
self.repeats = 1 # only do this once
|
self.repeats = 1 # only do this once
|
||||||
|
|
||||||
def at_start(self):
|
def at_start(self):
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -20,44 +20,59 @@ dropped connections etc.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from ev import Script
|
from ev import Script as BaseScript
|
||||||
|
|
||||||
|
|
||||||
class ExampleScript(BaseScript):
|
class ExampleScript(BaseScript):
|
||||||
"""
|
"""
|
||||||
A script type is customized by redefining some or all of its hook methods and variables.
|
A script type is customized by redefining some or all of its hook
|
||||||
|
methods and variables.
|
||||||
|
|
||||||
* available properties
|
* available properties
|
||||||
|
|
||||||
key (string) - name of object
|
key (string) - name of object
|
||||||
name (string)- same as key
|
name (string)- same as key
|
||||||
aliases (list of strings) - aliases to the object. Will be saved to database as AliasDB entries but returned as strings.
|
aliases (list of strings) - aliases to the object. Will be saved
|
||||||
|
to database as AliasDB entries but returned as strings.
|
||||||
dbref (int, read-only) - unique #id-number. Also "id" can be used.
|
dbref (int, read-only) - unique #id-number. Also "id" can be used.
|
||||||
dbobj (Object, read-only) - link to database model. dbobj.typeclass points back to this class
|
dbobj (Object, read-only) - link to database model. dbobj.typeclass
|
||||||
typeclass (Object, read-only) - this links back to this class as an identified only. Use self.swap_typeclass() to switch.
|
points back to this class
|
||||||
|
typeclass (Object, read-only) - this links back to this class as an
|
||||||
|
identified only. Use self.swap_typeclass() to switch.
|
||||||
date_created (string) - time stamp of object creation
|
date_created (string) - time stamp of object creation
|
||||||
permissions (list of strings) - list of permission strings
|
permissions (list of strings) - list of permission strings
|
||||||
|
|
||||||
desc (string) - optional description of script, shown in listings
|
desc (string) - optional description of script, shown in listings
|
||||||
obj (Object) - optional object that this script is connected to and acts on (set automatically by obj.scripts.add())
|
obj (Object) - optional object that this script is connected to
|
||||||
interval (int) - how often script should run, in seconds. <0 turns off ticker
|
and acts on (set automatically by obj.scripts.add())
|
||||||
start_delay (bool) - if the script should start repeating right away or wait self.interval seconds
|
interval (int) - how often script should run, in seconds. <0 turns
|
||||||
repeats (int) - how many times the script should repeat before stopping. 0 means infinite repeats
|
off ticker
|
||||||
|
start_delay (bool) - if the script should start repeating right away or
|
||||||
|
wait self.interval seconds
|
||||||
|
repeats (int) - how many times the script should repeat before
|
||||||
|
stopping. 0 means infinite repeats
|
||||||
persistent (bool) - if script should survive a server shutdown or not
|
persistent (bool) - if script should survive a server shutdown or not
|
||||||
is_active (bool) - if script is currently running
|
is_active (bool) - if script is currently running
|
||||||
|
|
||||||
* Handlers
|
* Handlers
|
||||||
|
|
||||||
locks - lock-handler: use locks.add() to add new lock strings
|
locks - lock-handler: use locks.add() to add new lock strings
|
||||||
db - attribute-handler: store/retrieve database attributes on this self.db.myattr=val, val=self.db.myattr
|
db - attribute-handler: store/retrieve database attributes on this
|
||||||
ndb - non-persistent attribute handler: same as db but does not create a database entry when storing data
|
self.db.myattr=val, val=self.db.myattr
|
||||||
|
ndb - non-persistent attribute handler: same as db but does not
|
||||||
|
create a database entry when storing data
|
||||||
|
|
||||||
* Helper methods
|
* Helper methods
|
||||||
|
|
||||||
start() - start script (this usually happens automatically at creation and obj.script.add() etc)
|
start() - start script (this usually happens automatically at creation
|
||||||
|
and obj.script.add() etc)
|
||||||
stop() - stop script, and delete it
|
stop() - stop script, and delete it
|
||||||
pause() - put the script on hold, until unpause() is called. If script is persistent, the pause state will survive a shutdown.
|
pause() - put the script on hold, until unpause() is called. If script
|
||||||
unpause() - restart a previously paused script. The script will continue as if it was never paused.
|
is persistent, the pause state will survive a shutdown.
|
||||||
time_until_next_repeat() - if a timed script (interval>0), returns time until next tick
|
unpause() - restart a previously paused script. The script will continue
|
||||||
|
from the paused timer (but at_start() will be called).
|
||||||
|
time_until_next_repeat() - if a timed script (interval>0), returns time
|
||||||
|
until next tick
|
||||||
|
|
||||||
* Hook methods (should also include self as the first argument):
|
* Hook methods (should also include self as the first argument):
|
||||||
|
|
||||||
|
|
@ -71,16 +86,17 @@ class ExampleScript(BaseScript):
|
||||||
actual combat going on).
|
actual combat going on).
|
||||||
at_start() - Called every time the script is started, which for persistent
|
at_start() - Called every time the script is started, which for persistent
|
||||||
scripts is at least once every server start. Note that this is
|
scripts is at least once every server start. Note that this is
|
||||||
unaffected by self.delay_start, which only delays the first call
|
unaffected by self.delay_start, which only delays the first
|
||||||
to at_repeat().
|
call to at_repeat().
|
||||||
at_repeat() - Called every self.interval seconds. It will be called immediately
|
at_repeat() - Called every self.interval seconds. It will be called
|
||||||
upon launch unless self.delay_start is True, which will delay
|
immediately upon launch unless self.delay_start is True, which
|
||||||
the first call of this method by self.interval seconds. If
|
will delay the first call of this method by self.interval
|
||||||
self.interval==0, this method will never be called.
|
seconds. If self.interval==0, this method will never
|
||||||
at_stop() - Called as the script object is stopped and is about to be removed from
|
be called.
|
||||||
the game, e.g. because is_valid() returned False.
|
at_stop() - Called as the script object is stopped and is about to be
|
||||||
at_server_reload() - Called when server reloads. Can be used to save temporary
|
removed from the game, e.g. because is_valid() returned False.
|
||||||
variables you want should survive a reload.
|
at_server_reload() - Called when server reloads. Can be used to
|
||||||
|
save temporary variables you want should survive a reload.
|
||||||
at_server_shutdown() - called at a full server shutdown.
|
at_server_shutdown() - called at a full server shutdown.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
@ -17,20 +17,22 @@
|
||||||
# automatically be made available for each block. Observe
|
# automatically be made available for each block. Observe
|
||||||
# that changes to these variables made in one block is not
|
# that changes to these variables made in one block is not
|
||||||
# preserved between blocks!)
|
# preserved between blocks!)
|
||||||
# #CODE (infotext) [objname, objname, ...] - This designates a code block that will be executed like a
|
# #CODE (infotext) [objname, objname, ...] - This designates a code block that
|
||||||
# stand-alone piece of code together with any #HEADER
|
# will be executed like a stand-alone piece of code together with
|
||||||
# defined.
|
# any #HEADER defined.
|
||||||
# infotext is a describing text about what goes in in this block. It will be
|
# infotext is a describing text about what goes in in this block.
|
||||||
# shown by the batchprocessing command.
|
# It will be shown by the batchprocessing command.
|
||||||
# <objname>s mark the (variable-)names of objects created in the code,
|
# <objname>s mark the (variable-)names of objects created in
|
||||||
# and which may be auto-deleted by the processor if desired (such as when
|
# the code, and which may be auto-deleted by the processor if
|
||||||
# debugging the script). E.g., if the code contains the command
|
# desired (such as when debugging the script). E.g., if the code
|
||||||
# myobj = create.create_object(...), you could put 'myobj' in the #CODE header
|
# contains the command myobj = create.create_object(...), you could
|
||||||
# regardless of what the created object is actually called in-game.
|
# put 'myobj' in the #CODE header regardless of what the created
|
||||||
# #INSERT filename - this includes another code batch file. The named file will be loaded and
|
# object is actually called in-game.
|
||||||
# run at this point. Note that code from the inserted file will NOT share #HEADERs
|
# #INSERT filename - this includes another code batch file. The named file will
|
||||||
# with the importing file, but will only use the headers in the importing file.
|
# be loaded and run at this point. Note that code from the inserted
|
||||||
# make sure to not create a cyclic import here!
|
# file will NOT share #HEADERs with the importing file, but will
|
||||||
|
# only use the headers in the importing file. Make sure to not
|
||||||
|
# create a cyclic import here!
|
||||||
|
|
||||||
# The following variable is automatically made available for the script:
|
# The following variable is automatically made available for the script:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,8 +31,10 @@ if not os.path.exists('settings.py'):
|
||||||
# basic stuff.
|
# basic stuff.
|
||||||
|
|
||||||
# make random secret_key.
|
# make random secret_key.
|
||||||
import random, string
|
import random
|
||||||
secret_key = list((string.letters + string.digits + string.punctuation).replace("\\","").replace("'",'"'))
|
import string
|
||||||
|
secret_key = list((string.letters +
|
||||||
|
string.digits + string.punctuation).replace("\\", "").replace("'", '"'))
|
||||||
random.shuffle(secret_key)
|
random.shuffle(secret_key)
|
||||||
secret_key = "".join(secret_key[:40])
|
secret_key = "".join(secret_key[:40])
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -82,6 +82,7 @@ if os.name == 'nt':
|
||||||
|
|
||||||
# Functions
|
# Functions
|
||||||
|
|
||||||
|
|
||||||
def set_restart_mode(restart_file, flag="reload"):
|
def set_restart_mode(restart_file, flag="reload"):
|
||||||
"""
|
"""
|
||||||
This sets a flag file for the restart mode.
|
This sets a flag file for the restart mode.
|
||||||
|
|
@ -89,6 +90,7 @@ def set_restart_mode(restart_file, flag="reload"):
|
||||||
with open(restart_file, 'w') as f:
|
with open(restart_file, 'w') as f:
|
||||||
f.write(str(flag))
|
f.write(str(flag))
|
||||||
|
|
||||||
|
|
||||||
def get_restart_mode(restart_file):
|
def get_restart_mode(restart_file):
|
||||||
"""
|
"""
|
||||||
Parse the server/portal restart status
|
Parse the server/portal restart status
|
||||||
|
|
@ -98,6 +100,7 @@ def get_restart_mode(restart_file):
|
||||||
return f.read()
|
return f.read()
|
||||||
return "shutdown"
|
return "shutdown"
|
||||||
|
|
||||||
|
|
||||||
def get_pid(pidfile):
|
def get_pid(pidfile):
|
||||||
"""
|
"""
|
||||||
Get the PID (Process ID) by trying to access
|
Get the PID (Process ID) by trying to access
|
||||||
|
|
@ -109,6 +112,7 @@ def get_pid(pidfile):
|
||||||
pid = f.read()
|
pid = f.read()
|
||||||
return pid
|
return pid
|
||||||
|
|
||||||
|
|
||||||
def cycle_logfile(logfile):
|
def cycle_logfile(logfile):
|
||||||
"""
|
"""
|
||||||
Rotate the old log files to <filename>.old
|
Rotate the old log files to <filename>.old
|
||||||
|
|
@ -126,13 +130,13 @@ def cycle_logfile(logfile):
|
||||||
SERVER = None
|
SERVER = None
|
||||||
PORTAL = None
|
PORTAL = None
|
||||||
|
|
||||||
|
|
||||||
def start_services(server_argv, portal_argv):
|
def start_services(server_argv, portal_argv):
|
||||||
"""
|
"""
|
||||||
This calls a threaded loop that launces the Portal and Server
|
This calls a threaded loop that launces the Portal and Server
|
||||||
and then restarts them when they finish.
|
and then restarts them when they finish.
|
||||||
"""
|
"""
|
||||||
global SERVER, PORTAL
|
global SERVER, PORTAL
|
||||||
|
|
||||||
processes = Queue.Queue()
|
processes = Queue.Queue()
|
||||||
|
|
||||||
def server_waiter(queue):
|
def server_waiter(queue):
|
||||||
|
|
@ -141,7 +145,8 @@ def start_services(server_argv, portal_argv):
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
print "Server process error: %(e)s" % {'e': e}
|
print "Server process error: %(e)s" % {'e': e}
|
||||||
return
|
return
|
||||||
queue.put(("server_stopped", rc)) # this signals the controller that the program finished
|
# this signals the controller that the program finished
|
||||||
|
queue.put(("server_stopped", rc))
|
||||||
|
|
||||||
def portal_waiter(queue):
|
def portal_waiter(queue):
|
||||||
try:
|
try:
|
||||||
|
|
@ -149,7 +154,8 @@ def start_services(server_argv, portal_argv):
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
print "Portal process error: %(e)s" % {'e': e}
|
print "Portal process error: %(e)s" % {'e': e}
|
||||||
return
|
return
|
||||||
queue.put(("portal_stopped", rc)) # this signals the controller that the program finished
|
# this signals the controller that the program finished
|
||||||
|
queue.put(("portal_stopped", rc))
|
||||||
|
|
||||||
if portal_argv:
|
if portal_argv:
|
||||||
try:
|
try:
|
||||||
|
|
@ -157,7 +163,8 @@ def start_services(server_argv, portal_argv):
|
||||||
# start portal as interactive, reloadable thread
|
# start portal as interactive, reloadable thread
|
||||||
PORTAL = thread.start_new_thread(portal_waiter, (processes, ))
|
PORTAL = thread.start_new_thread(portal_waiter, (processes, ))
|
||||||
else:
|
else:
|
||||||
# normal operation: start portal as a daemon; we don't care to monitor it for restart
|
# normal operation: start portal as a daemon;
|
||||||
|
# we don't care to monitor it for restart
|
||||||
PORTAL = Popen(portal_argv)
|
PORTAL = Popen(portal_argv)
|
||||||
except IOError, e:
|
except IOError, e:
|
||||||
print "Portal IOError: %s\nA possible explanation for this is that 'twistd' is not found." % e
|
print "Portal IOError: %s\nA possible explanation for this is that 'twistd' is not found." % e
|
||||||
|
|
@ -178,13 +185,15 @@ def start_services(server_argv, portal_argv):
|
||||||
message, rc = processes.get()
|
message, rc = processes.get()
|
||||||
|
|
||||||
# restart only if process stopped cleanly
|
# restart only if process stopped cleanly
|
||||||
if message == "server_stopped" and int(rc) == 0 and get_restart_mode(SERVER_RESTART) in ("True", "reload", "reset"):
|
if (message == "server_stopped" and int(rc) == 0 and
|
||||||
|
get_restart_mode(SERVER_RESTART) in ("True", "reload", "reset")):
|
||||||
print "Evennia Server stopped. Restarting ..."
|
print "Evennia Server stopped. Restarting ..."
|
||||||
SERVER = thread.start_new_thread(server_waiter, (processes, ))
|
SERVER = thread.start_new_thread(server_waiter, (processes, ))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# normally the portal is not reloaded since it's run as a daemon.
|
# normally the portal is not reloaded since it's run as a daemon.
|
||||||
if message == "portal_stopped" and int(rc) == 0 and get_restart_mode(PORTAL_RESTART) == "True":
|
if (message == "portal_stopped" and int(rc) == 0 and
|
||||||
|
get_restart_mode(PORTAL_RESTART) == "True"):
|
||||||
print "Evennia Portal stopped in interactive mode. Restarting ..."
|
print "Evennia Portal stopped in interactive mode. Restarting ..."
|
||||||
PORTAL = thread.start_new_thread(portal_waiter, (processes, ))
|
PORTAL = thread.start_new_thread(portal_waiter, (processes, ))
|
||||||
continue
|
continue
|
||||||
|
|
@ -194,11 +203,12 @@ def start_services(server_argv, portal_argv):
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
"""
|
"""
|
||||||
This handles the command line input of the runner (it's most often called by evennia.py)
|
This handles the command line input of the runner
|
||||||
|
(it's most often called by evennia.py)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
parser = OptionParser(usage="%prog [options] start",
|
parser = OptionParser(usage="%prog [options] start",
|
||||||
description="This runner should normally *not* be called directly - it is called automatically from the evennia.py main program. It manages the Evennia game server and portal processes an hosts a threaded loop to restart the Server whenever it is stopped (this constitues Evennia's reload mechanism).")
|
description="This runner should normally *not* be called directly - it is called automatically from the evennia.py main program. It manages the Evennia game server and portal processes an hosts a threaded loop to restart the Server whenever it is stopped (this constitues Evennia's reload mechanism).")
|
||||||
parser.add_option('-s', '--noserver', action='store_true',
|
parser.add_option('-s', '--noserver', action='store_true',
|
||||||
dest='noserver', default=False,
|
dest='noserver', default=False,
|
||||||
help='Do not start Server process')
|
help='Do not start Server process')
|
||||||
|
|
@ -267,7 +277,6 @@ def main():
|
||||||
server_argv.extend(sprof_argv)
|
server_argv.extend(sprof_argv)
|
||||||
print "\nRunning Evennia Server under cProfile."
|
print "\nRunning Evennia Server under cProfile."
|
||||||
|
|
||||||
|
|
||||||
# Portal
|
# Portal
|
||||||
|
|
||||||
pid = get_pid(PORTAL_PIDFILE)
|
pid = get_pid(PORTAL_PIDFILE)
|
||||||
|
|
@ -292,7 +301,6 @@ def main():
|
||||||
portal_argv.extend(pprof_argv)
|
portal_argv.extend(pprof_argv)
|
||||||
print "\nRunning Evennia Portal under cProfile."
|
print "\nRunning Evennia Portal under cProfile."
|
||||||
|
|
||||||
|
|
||||||
# Windows fixes (Windows don't support pidfiles natively)
|
# Windows fixes (Windows don't support pidfiles natively)
|
||||||
if os.name == 'nt':
|
if os.name == 'nt':
|
||||||
if server_argv:
|
if server_argv:
|
||||||
|
|
|
||||||
|
|
@ -1,2 +1,3 @@
|
||||||
# experimental central dictionary for models in subprocesses to report they have been changed.
|
# experimental central dictionary for models in
|
||||||
|
# subprocesses to report they have been changed.
|
||||||
PROC_MODIFIED_OBJS = []
|
PROC_MODIFIED_OBJS = []
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
@ -72,20 +72,25 @@ CMD_LOGINSTART = "__unloggedin_look_command"
|
||||||
|
|
||||||
# custom Exceptions
|
# custom Exceptions
|
||||||
|
|
||||||
|
|
||||||
class NoCmdSets(Exception):
|
class NoCmdSets(Exception):
|
||||||
"No cmdsets found. Critical error."
|
"No cmdsets found. Critical error."
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class ExecSystemCommand(Exception):
|
class ExecSystemCommand(Exception):
|
||||||
"Run a system command"
|
"Run a system command"
|
||||||
def __init__(self, syscmd, sysarg):
|
def __init__(self, syscmd, sysarg):
|
||||||
self.args = (syscmd, sysarg) # needed by exception error handling
|
self.args = (syscmd, sysarg) # needed by exception error handling
|
||||||
self.syscmd = syscmd
|
self.syscmd = syscmd
|
||||||
self.sysarg = sysarg
|
self.sysarg = sysarg
|
||||||
|
|
||||||
# Helper function
|
# Helper function
|
||||||
|
|
||||||
|
|
||||||
@inlineCallbacks
|
@inlineCallbacks
|
||||||
def get_and_merge_cmdsets(caller, session, player, obj, callertype, sessid=None):
|
def get_and_merge_cmdsets(caller, session, player, obj,
|
||||||
|
callertype, sessid=None):
|
||||||
"""
|
"""
|
||||||
Gather all relevant cmdsets and merge them.
|
Gather all relevant cmdsets and merge them.
|
||||||
|
|
||||||
|
|
@ -124,20 +129,25 @@ def get_and_merge_cmdsets(caller, session, player, obj, callertype, sessid=None)
|
||||||
if location and not obj_cmdset.no_objs:
|
if location and not obj_cmdset.no_objs:
|
||||||
# Gather all cmdsets stored on objects in the room and
|
# Gather all cmdsets stored on objects in the room and
|
||||||
# also in the caller's inventory and the location itself
|
# also in the caller's inventory and the location itself
|
||||||
local_objlist = yield location.contents_get(exclude=obj.dbobj) + obj.contents + [location]
|
local_objlist = yield (location.contents_get(exclude=obj.dbobj) +
|
||||||
|
obj.contents +
|
||||||
|
[location])
|
||||||
for lobj in local_objlist:
|
for lobj in local_objlist:
|
||||||
try:
|
try:
|
||||||
# call hook in case we need to do dynamic changing to cmdset
|
# call hook in case we need to do dynamic changing to cmdset
|
||||||
_GA(lobj, "at_cmdset_get")()
|
_GA(lobj, "at_cmdset_get")()
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.log_trace()
|
logger.log_trace()
|
||||||
# the call-type lock is checked here, it makes sure a player is not seeing e.g. the commands
|
# the call-type lock is checked here, it makes sure a player
|
||||||
# on a fellow player (which is why the no_superuser_bypass must be True)
|
# is not seeing e.g. the commands on a fellow player (which is why
|
||||||
local_obj_cmdsets = yield [lobj.cmdset.current for lobj in local_objlist
|
# the no_superuser_bypass must be True)
|
||||||
if (lobj.cmdset.current and lobj.locks.check(caller, 'call', no_superuser_bypass=True))]
|
local_obj_cmdsets = \
|
||||||
|
yield [lobj.cmdset.current for lobj in local_objlist
|
||||||
|
if (lobj.cmdset.current and
|
||||||
|
lobj.locks.check(caller, 'call', no_superuser_bypass=True))]
|
||||||
for cset in local_obj_cmdsets:
|
for cset in local_obj_cmdsets:
|
||||||
#This is necessary for object sets, or we won't be able to separate
|
#This is necessary for object sets, or we won't be able to
|
||||||
#the command sets from each other in a busy room.
|
# separate the command sets from each other in a busy room.
|
||||||
cset.old_duplicates = cset.duplicates
|
cset.old_duplicates = cset.duplicates
|
||||||
cset.duplicates = True
|
cset.duplicates = True
|
||||||
returnValue(local_obj_cmdsets)
|
returnValue(local_obj_cmdsets)
|
||||||
|
|
@ -159,8 +169,8 @@ def get_and_merge_cmdsets(caller, session, player, obj, callertype, sessid=None)
|
||||||
report_to = session
|
report_to = session
|
||||||
session_cmdset = yield _get_cmdset(session)
|
session_cmdset = yield _get_cmdset(session)
|
||||||
cmdsets = [session_cmdset]
|
cmdsets = [session_cmdset]
|
||||||
if player: # this automatically implies logged-in
|
if player: # this automatically implies logged-in
|
||||||
player_cmdset = yield _get_cmdset(player)
|
player_cmdset = yield _get_cmdset(player)
|
||||||
channel_cmdset = yield _get_channel_cmdsets(player, player_cmdset)
|
channel_cmdset = yield _get_channel_cmdsets(player, player_cmdset)
|
||||||
cmdsets.extend([player_cmdset, channel_cmdset])
|
cmdsets.extend([player_cmdset, channel_cmdset])
|
||||||
if obj:
|
if obj:
|
||||||
|
|
@ -185,21 +195,26 @@ def get_and_merge_cmdsets(caller, session, player, obj, callertype, sessid=None)
|
||||||
cmdsets = [obj_cmdset] + local_obj_cmdsets
|
cmdsets = [obj_cmdset] + local_obj_cmdsets
|
||||||
else:
|
else:
|
||||||
raise Exception("get_and_merge_cmdsets: callertype %s is not valid." % callertype)
|
raise Exception("get_and_merge_cmdsets: callertype %s is not valid." % callertype)
|
||||||
#cmdsets = yield [caller_cmdset] + [player_cmdset] + [channel_cmdset] + local_obj_cmdsets
|
#cmdsets = yield [caller_cmdset] + [player_cmdset] +
|
||||||
|
# [channel_cmdset] + local_obj_cmdsets
|
||||||
|
|
||||||
# weed out all non-found sets
|
# weed out all non-found sets
|
||||||
cmdsets = yield [cmdset for cmdset in cmdsets if cmdset and cmdset.key!="Empty"]
|
cmdsets = yield [cmdset for cmdset in cmdsets
|
||||||
|
if cmdset and cmdset.key != "Empty"]
|
||||||
# report cmdset errors to user (these should already have been logged)
|
# report cmdset errors to user (these should already have been logged)
|
||||||
yield [report_to.msg(cmdset.errmessage) for cmdset in cmdsets if cmdset.key == "_CMDSET_ERROR"]
|
yield [report_to.msg(cmdset.errmessage) for cmdset in cmdsets
|
||||||
|
if cmdset.key == "_CMDSET_ERROR"]
|
||||||
|
|
||||||
if cmdsets:
|
if cmdsets:
|
||||||
mergehash = tuple([id(cmdset) for cmdset in cmdsets]) # faster to do tuple on list than to build tuple directly
|
# faster to do tuple on list than to build tuple directly
|
||||||
|
mergehash = tuple([id(cmdset) for cmdset in cmdsets])
|
||||||
if mergehash in _CMDSET_MERGE_CACHE:
|
if mergehash in _CMDSET_MERGE_CACHE:
|
||||||
# cached merge exist; use that
|
# cached merge exist; use that
|
||||||
cmdset = _CMDSET_MERGE_CACHE[mergehash]
|
cmdset = _CMDSET_MERGE_CACHE[mergehash]
|
||||||
else:
|
else:
|
||||||
# we group and merge all same-prio cmdsets separately (this avoids order-dependent
|
# we group and merge all same-prio cmdsets separately (this avoids
|
||||||
# clashes in certain cases, such as when duplicates=True)
|
# order-dependent clashes in certain cases, such as
|
||||||
|
# when duplicates=True)
|
||||||
tempmergers = {}
|
tempmergers = {}
|
||||||
for cmdset in cmdsets:
|
for cmdset in cmdsets:
|
||||||
prio = cmdset.priority
|
prio = cmdset.priority
|
||||||
|
|
@ -241,13 +256,13 @@ def cmdhandler(called_by, raw_string, testing=False, callertype="session", sessi
|
||||||
if True, the command instance will be returned instead.
|
if True, the command instance will be returned instead.
|
||||||
callertype - this is one of "session", "player" or "object", in decending
|
callertype - this is one of "session", "player" or "object", in decending
|
||||||
order. So when the Session is the caller, it will merge its
|
order. So when the Session is the caller, it will merge its
|
||||||
own cmdset into cmdsets from both Player and eventual puppeted Object (and
|
own cmdset into cmdsets from both Player and eventual puppeted
|
||||||
cmdsets in its room etc). A Player will only include its
|
Object (and cmdsets in its room etc). A Player will only
|
||||||
own cmdset and the Objects and so on. Merge order is the
|
include its own cmdset and the Objects and so on. Merge order
|
||||||
same order, so that Object cmdsets are merged in last, giving
|
is the same order, so that Object cmdsets are merged in last,
|
||||||
them precendence for same-name and same-prio commands.
|
giving them precendence for same-name and same-prio commands.
|
||||||
sessid - Relevant if callertype is "player" - the session id will help retrieve the
|
sessid - Relevant if callertype is "player" - the session id will help
|
||||||
correct cmdsets from puppeted objects.
|
retrieve the correct cmdsets from puppeted objects.
|
||||||
|
|
||||||
Note that this function returns a deferred!
|
Note that this function returns a deferred!
|
||||||
"""
|
"""
|
||||||
|
|
@ -270,10 +285,11 @@ def cmdhandler(called_by, raw_string, testing=False, callertype="session", sessi
|
||||||
# we assign the caller with preference 'bottom up'
|
# we assign the caller with preference 'bottom up'
|
||||||
caller = obj or player or session
|
caller = obj or player or session
|
||||||
|
|
||||||
try: # catch bugs in cmdhandler itself
|
try: # catch bugs in cmdhandler itself
|
||||||
try: # catch special-type commands
|
try: # catch special-type commands
|
||||||
|
|
||||||
cmdset = yield get_and_merge_cmdsets(caller, session, player, obj, callertype, sessid)
|
cmdset = yield get_and_merge_cmdsets(caller, session, player, obj,
|
||||||
|
callertype, sessid)
|
||||||
if not cmdset:
|
if not cmdset:
|
||||||
# this is bad and shouldn't happen.
|
# this is bad and shouldn't happen.
|
||||||
raise NoCmdSets
|
raise NoCmdSets
|
||||||
|
|
@ -323,14 +339,15 @@ def cmdhandler(called_by, raw_string, testing=False, callertype="session", sessi
|
||||||
else:
|
else:
|
||||||
# fallback to default error text
|
# fallback to default error text
|
||||||
sysarg = _("Command '%s' is not available.") % raw_string
|
sysarg = _("Command '%s' is not available.") % raw_string
|
||||||
suggestions = string_suggestions(raw_string, cmdset.get_all_cmd_keys_and_aliases(caller), cutoff=0.7, maxnum=3)
|
suggestions = string_suggestions(raw_string,
|
||||||
|
cmdset.get_all_cmd_keys_and_aliases(caller),
|
||||||
|
cutoff=0.7, maxnum=3)
|
||||||
if suggestions:
|
if suggestions:
|
||||||
sysarg += _(" Maybe you meant %s?") % utils.list_to_string(suggestions, _('or'), addquote=True)
|
sysarg += _(" Maybe you meant %s?") % utils.list_to_string(suggestions, _('or'), addquote=True)
|
||||||
else:
|
else:
|
||||||
sysarg += _(" Type \"help\" for help.")
|
sysarg += _(" Type \"help\" for help.")
|
||||||
raise ExecSystemCommand(syscmd, sysarg)
|
raise ExecSystemCommand(syscmd, sysarg)
|
||||||
|
|
||||||
|
|
||||||
# Check if this is a Channel-cmd match.
|
# Check if this is a Channel-cmd match.
|
||||||
if hasattr(cmd, 'is_channel') and cmd.is_channel:
|
if hasattr(cmd, 'is_channel') and cmd.is_channel:
|
||||||
# even if a user-defined syscmd is not defined, the
|
# even if a user-defined syscmd is not defined, the
|
||||||
|
|
@ -380,7 +397,8 @@ def cmdhandler(called_by, raw_string, testing=False, callertype="session", sessi
|
||||||
for func_part in make_iter(cmd.func_parts):
|
for func_part in make_iter(cmd.func_parts):
|
||||||
err = yield func_part()
|
err = yield func_part()
|
||||||
# returning anything but a deferred/None will kill the chain
|
# returning anything but a deferred/None will kill the chain
|
||||||
if err: break
|
if err:
|
||||||
|
break
|
||||||
|
|
||||||
# post-command hook
|
# post-command hook
|
||||||
yield cmd.at_post_cmd()
|
yield cmd.at_post_cmd()
|
||||||
|
|
|
||||||
|
|
@ -51,10 +51,10 @@ def cmdparser(raw_string, cmdset, caller, match_index=None):
|
||||||
for cmd in cmdset:
|
for cmd in cmdset:
|
||||||
try:
|
try:
|
||||||
matches.extend([create_match(cmdname, raw_string, cmd)
|
matches.extend([create_match(cmdname, raw_string, cmd)
|
||||||
for cmdname in [cmd.key] + cmd.aliases
|
for cmdname in [cmd.key] + cmd.aliases
|
||||||
if cmdname and l_raw_string.startswith(cmdname.lower())
|
if cmdname and l_raw_string.startswith(cmdname.lower())
|
||||||
and (not cmd.arg_regex or
|
and (not cmd.arg_regex or
|
||||||
cmd.arg_regex.match(l_raw_string[len(cmdname):]))])
|
cmd.arg_regex.match(l_raw_string[len(cmdname):]))])
|
||||||
except Exception:
|
except Exception:
|
||||||
log_trace("cmdhandler error. raw_input:%s" % raw_string)
|
log_trace("cmdhandler error. raw_input:%s" % raw_string)
|
||||||
|
|
||||||
|
|
@ -67,7 +67,8 @@ def cmdparser(raw_string, cmdset, caller, match_index=None):
|
||||||
if mindex.isdigit():
|
if mindex.isdigit():
|
||||||
mindex = int(mindex) - 1
|
mindex = int(mindex) - 1
|
||||||
# feed result back to parser iteratively
|
# feed result back to parser iteratively
|
||||||
return cmdparser(new_raw_string, cmdset, caller, match_index=mindex)
|
return cmdparser(new_raw_string, cmdset,
|
||||||
|
caller, match_index=mindex)
|
||||||
|
|
||||||
# only select command matches we are actually allowed to call.
|
# only select command matches we are actually allowed to call.
|
||||||
matches = [match for match in matches if match[2].access(caller, 'cmd')]
|
matches = [match for match in matches if match[2].access(caller, 'cmd')]
|
||||||
|
|
@ -75,7 +76,8 @@ def cmdparser(raw_string, cmdset, caller, match_index=None):
|
||||||
if len(matches) > 1:
|
if len(matches) > 1:
|
||||||
# See if it helps to analyze the match with preserved case but only if
|
# See if it helps to analyze the match with preserved case but only if
|
||||||
# it leaves at least one match.
|
# it leaves at least one match.
|
||||||
trimmed = [match for match in matches if raw_string.startswith(match[0])]
|
trimmed = [match for match in matches
|
||||||
|
if raw_string.startswith(match[0])]
|
||||||
if trimmed:
|
if trimmed:
|
||||||
matches = trimmed
|
matches = trimmed
|
||||||
|
|
||||||
|
|
@ -94,15 +96,13 @@ def cmdparser(raw_string, cmdset, caller, match_index=None):
|
||||||
matches = matches[-quality.count(quality[-1]):]
|
matches = matches[-quality.count(quality[-1]):]
|
||||||
|
|
||||||
if len(matches) > 1 and match_index != None and 0 <= match_index < len(matches):
|
if len(matches) > 1 and match_index != None and 0 <= match_index < len(matches):
|
||||||
# We couldn't separate match by quality, but we have an index argument to
|
# We couldn't separate match by quality, but we have an
|
||||||
# tell us which match to use.
|
# index argument to tell us which match to use.
|
||||||
matches = [matches[match_index]]
|
matches = [matches[match_index]]
|
||||||
|
|
||||||
# no matter what we have at this point, we have to return it.
|
# no matter what we have at this point, we have to return it.
|
||||||
return matches
|
return matches
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
# Search parsers and support methods
|
# Search parsers and support methods
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
|
|
@ -118,7 +118,6 @@ def cmdparser(raw_string, cmdset, caller, match_index=None):
|
||||||
# The the replacing modules must have the same inputs and outputs as
|
# The the replacing modules must have the same inputs and outputs as
|
||||||
# those in this module.
|
# those in this module.
|
||||||
#
|
#
|
||||||
|
|
||||||
def at_search_result(msg_obj, ostring, results, global_search=False,
|
def at_search_result(msg_obj, ostring, results, global_search=False,
|
||||||
nofound_string=None, multimatch_string=None):
|
nofound_string=None, multimatch_string=None):
|
||||||
"""
|
"""
|
||||||
|
|
@ -176,7 +175,7 @@ def at_search_result(msg_obj, ostring, results, global_search=False,
|
||||||
invtext = _(" (carried)")
|
invtext = _(" (carried)")
|
||||||
if show_dbref:
|
if show_dbref:
|
||||||
dbreftext = "(#%i)" % result.dbid
|
dbreftext = "(#%i)" % result.dbid
|
||||||
string += "\n %i-%s%s%s" % (num+1, result.name,
|
string += "\n %i-%s%s%s" % (num + 1, result.name,
|
||||||
dbreftext, invtext)
|
dbreftext, invtext)
|
||||||
results = None
|
results = None
|
||||||
else:
|
else:
|
||||||
|
|
@ -187,6 +186,7 @@ def at_search_result(msg_obj, ostring, results, global_search=False,
|
||||||
msg_obj.msg(string.strip())
|
msg_obj.msg(string.strip())
|
||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
def at_multimatch_input(ostring):
|
def at_multimatch_input(ostring):
|
||||||
"""
|
"""
|
||||||
Parse number-identifiers.
|
Parse number-identifiers.
|
||||||
|
|
@ -231,9 +231,9 @@ def at_multimatch_input(ostring):
|
||||||
if not '-' in ostring:
|
if not '-' in ostring:
|
||||||
return (None, ostring)
|
return (None, ostring)
|
||||||
try:
|
try:
|
||||||
index = ostring.find('-')
|
index = ostring.find('-')
|
||||||
number = int(ostring[:index])-1
|
number = int(ostring[:index]) - 1
|
||||||
return (number, ostring[index+1:])
|
return (number, ostring[index + 1:])
|
||||||
except ValueError:
|
except ValueError:
|
||||||
#not a number; this is not an identifier.
|
#not a number; this is not an identifier.
|
||||||
return (None, ostring)
|
return (None, ostring)
|
||||||
|
|
@ -256,13 +256,15 @@ def at_multimatch_cmd(caller, matches):
|
||||||
else:
|
else:
|
||||||
is_channel = ""
|
is_channel = ""
|
||||||
if cmd.is_exit and cmd.destination:
|
if cmd.is_exit and cmd.destination:
|
||||||
is_exit = _(" (exit to %s)") % cmd.destination
|
is_exit = (" (exit to %s)") % cmd.destination
|
||||||
else:
|
else:
|
||||||
is_exit = ""
|
is_exit = ""
|
||||||
|
|
||||||
id1 = ""
|
id1 = ""
|
||||||
id2 = ""
|
id2 = ""
|
||||||
if not (is_channel or is_exit) and (hasattr(cmd, 'obj') and cmd.obj != caller) and hasattr(cmd.obj, "key"):
|
if (not (is_channel or is_exit) and
|
||||||
|
(hasattr(cmd, 'obj') and cmd.obj != caller) and
|
||||||
|
hasattr(cmd.obj, "key")):
|
||||||
# the command is defined on some other object
|
# the command is defined on some other object
|
||||||
id1 = "%s-%s" % (num + 1, cmdname)
|
id1 = "%s-%s" % (num + 1, cmdname)
|
||||||
id2 = " (%s)" % (cmd.obj.key)
|
id2 = " (%s)" % (cmd.obj.key)
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ from django.utils.translation import ugettext as _
|
||||||
from src.utils.utils import inherits_from, is_iter
|
from src.utils.utils import inherits_from, is_iter
|
||||||
__all__ = ("CmdSet",)
|
__all__ = ("CmdSet",)
|
||||||
|
|
||||||
|
|
||||||
class _CmdSetMeta(type):
|
class _CmdSetMeta(type):
|
||||||
"""
|
"""
|
||||||
This metaclass makes some minor on-the-fly convenience fixes to
|
This metaclass makes some minor on-the-fly convenience fixes to
|
||||||
|
|
@ -38,15 +39,18 @@ class _CmdSetMeta(type):
|
||||||
|
|
||||||
super(_CmdSetMeta, mcs).__init__(*args, **kwargs)
|
super(_CmdSetMeta, mcs).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class CmdSet(object):
|
class CmdSet(object):
|
||||||
"""
|
"""
|
||||||
This class describes a unique cmdset that understands priorities. CmdSets
|
This class describes a unique cmdset that understands priorities. CmdSets
|
||||||
can be merged and made to perform various set operations on each other.
|
can be merged and made to perform various set operations on each other.
|
||||||
CmdSets have priorities that affect which of their ingoing commands gets used.
|
CmdSets have priorities that affect which of their ingoing commands
|
||||||
|
gets used.
|
||||||
|
|
||||||
In the examples, cmdset A always have higher priority than cmdset B.
|
In the examples, cmdset A always have higher priority than cmdset B.
|
||||||
|
|
||||||
key - the name of the cmdset. This can be used on its own for game operations
|
key - the name of the cmdset. This can be used on its own for game
|
||||||
|
operations
|
||||||
|
|
||||||
mergetype (partly from Set theory):
|
mergetype (partly from Set theory):
|
||||||
|
|
||||||
|
|
@ -80,8 +84,9 @@ class CmdSet(object):
|
||||||
priority- All cmdsets are always merged in pairs of two so that
|
priority- All cmdsets are always merged in pairs of two so that
|
||||||
the higher set's mergetype is applied to the
|
the higher set's mergetype is applied to the
|
||||||
lower-priority cmdset. Default commands have priority 0,
|
lower-priority cmdset. Default commands have priority 0,
|
||||||
high-priority ones like Exits and Channels have 10 and 9. Priorities
|
high-priority ones like Exits and Channels have 10 and 9.
|
||||||
can be negative as well to give default commands preference.
|
Priorities can be negative as well to give default
|
||||||
|
commands preference.
|
||||||
|
|
||||||
duplicates - determines what happens when two sets of equal
|
duplicates - determines what happens when two sets of equal
|
||||||
priority merge. Default has the first of them in the
|
priority merge. Default has the first of them in the
|
||||||
|
|
@ -130,16 +135,17 @@ class CmdSet(object):
|
||||||
permanent = False
|
permanent = False
|
||||||
errmessage = ""
|
errmessage = ""
|
||||||
# pre-store properties to duplicate straight off
|
# pre-store properties to duplicate straight off
|
||||||
to_duplicate = ("key", "cmdsetobj", "no_exits", "no_objs", "no_channels", "permanent",
|
to_duplicate = ("key", "cmdsetobj", "no_exits", "no_objs",
|
||||||
"mergetype", "priority", "duplicates", "errmessage")
|
"no_channels", "permanent", "mergetype",
|
||||||
|
"priority", "duplicates", "errmessage")
|
||||||
|
|
||||||
def __init__(self, cmdsetobj=None, key=None):
|
def __init__(self, cmdsetobj=None, key=None):
|
||||||
"""
|
"""
|
||||||
Creates a new CmdSet instance.
|
Creates a new CmdSet instance.
|
||||||
|
|
||||||
cmdsetobj - this is the database object to which this particular
|
cmdsetobj - this is the database object to which this particular
|
||||||
instance of cmdset is related. It is often a player but may also be a
|
instance of cmdset is related. It is often a character but
|
||||||
regular object.
|
may also be a regular object.
|
||||||
"""
|
"""
|
||||||
if key:
|
if key:
|
||||||
self.key = key
|
self.key = key
|
||||||
|
|
@ -161,7 +167,8 @@ class CmdSet(object):
|
||||||
if cmdset_a.duplicates and cmdset_a.priority == cmdset_b.priority:
|
if cmdset_a.duplicates and cmdset_a.priority == cmdset_b.priority:
|
||||||
cmdset_c.commands.extend(cmdset_b.commands)
|
cmdset_c.commands.extend(cmdset_b.commands)
|
||||||
else:
|
else:
|
||||||
cmdset_c.commands.extend([cmd for cmd in cmdset_b if not cmd in cmdset_a])
|
cmdset_c.commands.extend([cmd for cmd in cmdset_b
|
||||||
|
if not cmd in cmdset_a])
|
||||||
return cmdset_c
|
return cmdset_c
|
||||||
|
|
||||||
def _intersect(self, cmdset_a, cmdset_b):
|
def _intersect(self, cmdset_a, cmdset_b):
|
||||||
|
|
@ -206,7 +213,8 @@ class CmdSet(object):
|
||||||
cmdset = CmdSet()
|
cmdset = CmdSet()
|
||||||
for key, val in ((key, getattr(self, key)) for key in self.to_duplicate):
|
for key, val in ((key, getattr(self, key)) for key in self.to_duplicate):
|
||||||
if val != getattr(cmdset, key):
|
if val != getattr(cmdset, key):
|
||||||
# only copy if different from default; avoid turning class-vars into instance vars
|
# only copy if different from default; avoid turning
|
||||||
|
# class-vars into instance vars
|
||||||
setattr(cmdset, key, val)
|
setattr(cmdset, key, val)
|
||||||
cmdset.key_mergetypes = self.key_mergetypes.copy()
|
cmdset.key_mergetypes = self.key_mergetypes.copy()
|
||||||
return cmdset
|
return cmdset
|
||||||
|
|
@ -230,10 +238,11 @@ class CmdSet(object):
|
||||||
def __contains__(self, othercmd):
|
def __contains__(self, othercmd):
|
||||||
"""
|
"""
|
||||||
Returns True if this cmdset contains the given command (as defined
|
Returns True if this cmdset contains the given command (as defined
|
||||||
by command name and aliases). This allows for things like 'if cmd in cmdset'
|
by command name and aliases). This allows for things
|
||||||
|
like 'if cmd in cmdset'
|
||||||
"""
|
"""
|
||||||
ret = self._contains_cache.get(othercmd)
|
ret = self._contains_cache.get(othercmd)
|
||||||
if ret == None:
|
if ret is None:
|
||||||
ret = othercmd in self.commands
|
ret = othercmd in self.commands
|
||||||
self._contains_cache[othercmd] = ret
|
self._contains_cache[othercmd] = ret
|
||||||
return ret
|
return ret
|
||||||
|
|
@ -264,7 +273,8 @@ class CmdSet(object):
|
||||||
# A higher or equal priority than B
|
# A higher or equal priority than B
|
||||||
|
|
||||||
# preserve system __commands
|
# preserve system __commands
|
||||||
sys_commands = sys_commands_a + [cmd for cmd in sys_commands_b if cmd not in sys_commands_a]
|
sys_commands = sys_commands_a + [cmd for cmd in sys_commands_b
|
||||||
|
if cmd not in sys_commands_a]
|
||||||
|
|
||||||
mergetype = self.key_mergetypes.get(cmdset_b.key, self.mergetype)
|
mergetype = self.key_mergetypes.get(cmdset_b.key, self.mergetype)
|
||||||
if mergetype == "Intersect":
|
if mergetype == "Intersect":
|
||||||
|
|
@ -286,7 +296,8 @@ class CmdSet(object):
|
||||||
# B higher priority than A
|
# B higher priority than A
|
||||||
|
|
||||||
# preserver system __commands
|
# preserver system __commands
|
||||||
sys_commands = sys_commands_b + [cmd for cmd in sys_commands_a if cmd not in sys_commands_b]
|
sys_commands = sys_commands_b + [cmd for cmd in sys_commands_a
|
||||||
|
if cmd not in sys_commands_b]
|
||||||
|
|
||||||
mergetype = cmdset_b.key_mergetypes.get(self.key, cmdset_b.mergetype)
|
mergetype = cmdset_b.key_mergetypes.get(self.key, cmdset_b.mergetype)
|
||||||
if mergetype == "Intersect":
|
if mergetype == "Intersect":
|
||||||
|
|
@ -295,7 +306,7 @@ class CmdSet(object):
|
||||||
cmdset_c = self._replace(cmdset_b, self)
|
cmdset_c = self._replace(cmdset_b, self)
|
||||||
elif mergetype == "Remove":
|
elif mergetype == "Remove":
|
||||||
cmdset_c = self._remove(self, cmdset_b)
|
cmdset_c = self._remove(self, cmdset_b)
|
||||||
else: # Union
|
else: # Union
|
||||||
cmdset_c = self._union(cmdset_b, self)
|
cmdset_c = self._union(cmdset_b, self)
|
||||||
cmdset_c.no_channels = cmdset_b.no_channels
|
cmdset_c.no_channels = cmdset_b.no_channels
|
||||||
cmdset_c.no_exits = cmdset_b.no_exits
|
cmdset_c.no_exits = cmdset_b.no_exits
|
||||||
|
|
@ -311,10 +322,8 @@ class CmdSet(object):
|
||||||
|
|
||||||
# return the system commands to the cmdset
|
# return the system commands to the cmdset
|
||||||
cmdset_c.add(sys_commands)
|
cmdset_c.add(sys_commands)
|
||||||
|
|
||||||
return cmdset_c
|
return cmdset_c
|
||||||
|
|
||||||
|
|
||||||
def add(self, cmd):
|
def add(self, cmd):
|
||||||
"""
|
"""
|
||||||
Add a command, a list of commands or a cmdset to this cmdset.
|
Add a command, a list of commands or a cmdset to this cmdset.
|
||||||
|
|
@ -338,9 +347,12 @@ class CmdSet(object):
|
||||||
try:
|
try:
|
||||||
cmd = self._instantiate(cmd)
|
cmd = self._instantiate(cmd)
|
||||||
except RuntimeError:
|
except RuntimeError:
|
||||||
string = "Adding cmdset %(cmd)s to %(class)s lead to an infinite loop. When adding a cmdset to another, "
|
string = "Adding cmdset %(cmd)s to %(class)s lead to an "
|
||||||
string += "make sure they are not themself cyclically added to the new cmdset somewhere in the chain."
|
string += "infinite loop. When adding a cmdset to another, "
|
||||||
raise RuntimeError(_(string) % {"cmd":cmd, "class":self.__class__})
|
string += "make sure they are not themself cyclically added to "
|
||||||
|
string += "the new cmdset somewhere in the chain."
|
||||||
|
raise RuntimeError(_(string) % {"cmd": cmd,
|
||||||
|
"class": self.__class__})
|
||||||
cmds = cmd.commands
|
cmds = cmd.commands
|
||||||
elif is_iter(cmd):
|
elif is_iter(cmd):
|
||||||
cmds = [self._instantiate(c) for c in cmd]
|
cmds = [self._instantiate(c) for c in cmd]
|
||||||
|
|
@ -354,7 +366,7 @@ class CmdSet(object):
|
||||||
cmd.obj = self.cmdsetobj
|
cmd.obj = self.cmdsetobj
|
||||||
try:
|
try:
|
||||||
ic = commands.index(cmd)
|
ic = commands.index(cmd)
|
||||||
commands[ic] = cmd # replace
|
commands[ic] = cmd # replace
|
||||||
except ValueError:
|
except ValueError:
|
||||||
commands.append(cmd)
|
commands.append(cmd)
|
||||||
# extra run to make sure to avoid doublets
|
# extra run to make sure to avoid doublets
|
||||||
|
|
@ -365,11 +377,10 @@ class CmdSet(object):
|
||||||
if cmd.key.startswith("__"):
|
if cmd.key.startswith("__"):
|
||||||
try:
|
try:
|
||||||
ic = system_commands.index(cmd)
|
ic = system_commands.index(cmd)
|
||||||
system_commands[ic] = cmd # replace
|
system_commands[ic] = cmd # replace
|
||||||
except ValueError:
|
except ValueError:
|
||||||
system_commands.append(cmd)
|
system_commands.append(cmd)
|
||||||
|
|
||||||
|
|
||||||
def remove(self, cmd):
|
def remove(self, cmd):
|
||||||
"""
|
"""
|
||||||
Remove a command instance from the cmdset.
|
Remove a command instance from the cmdset.
|
||||||
|
|
@ -431,7 +442,8 @@ class CmdSet(object):
|
||||||
"""
|
"""
|
||||||
names = []
|
names = []
|
||||||
if caller:
|
if caller:
|
||||||
[names.extend(cmd._keyaliases) for cmd in self.commands if cmd.access(caller)]
|
[names.extend(cmd._keyaliases) for cmd in self.commands
|
||||||
|
if cmd.access(caller)]
|
||||||
else:
|
else:
|
||||||
[names.extend(cmd._keyaliases) for cmd in self.commands]
|
[names.extend(cmd._keyaliases) for cmd in self.commands]
|
||||||
return names
|
return names
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,6 @@ can then implement separate sets for different situations. For
|
||||||
example, you can have a 'On a boat' set, onto which you then tack on
|
example, you can have a 'On a boat' set, onto which you then tack on
|
||||||
the 'Fishing' set. Fishing from a boat? No problem!
|
the 'Fishing' set. Fishing from a boat? No problem!
|
||||||
"""
|
"""
|
||||||
import traceback
|
|
||||||
from src.utils import logger, utils
|
from src.utils import logger, utils
|
||||||
from src.commands.cmdset import CmdSet
|
from src.commands.cmdset import CmdSet
|
||||||
from src.server.models import ServerConfig
|
from src.server.models import ServerConfig
|
||||||
|
|
@ -73,23 +72,27 @@ __all__ = ("import_cmdset", "CmdSetHandler")
|
||||||
|
|
||||||
_CACHED_CMDSETS = {}
|
_CACHED_CMDSETS = {}
|
||||||
|
|
||||||
|
|
||||||
class _ErrorCmdSet(CmdSet):
|
class _ErrorCmdSet(CmdSet):
|
||||||
"This is a special cmdset used to report errors"
|
"This is a special cmdset used to report errors"
|
||||||
key = "_CMDSET_ERROR"
|
key = "_CMDSET_ERROR"
|
||||||
errmessage = "Error when loading cmdset."
|
errmessage = "Error when loading cmdset."
|
||||||
|
|
||||||
|
|
||||||
def import_cmdset(python_path, cmdsetobj, emit_to_obj=None, no_logging=False):
|
def import_cmdset(python_path, cmdsetobj, emit_to_obj=None, no_logging=False):
|
||||||
"""
|
"""
|
||||||
This helper function is used by the cmdsethandler to load a cmdset
|
This helper function is used by the cmdsethandler to load a cmdset
|
||||||
instance from a python module, given a python_path. It's usually accessed
|
instance from a python module, given a python_path. It's usually accessed
|
||||||
through the cmdsethandler's add() and add_default() methods.
|
through the cmdsethandler's add() and add_default() methods.
|
||||||
python_path - This is the full path to the cmdset object.
|
python_path - This is the full path to the cmdset object.
|
||||||
cmdsetobj - the database object/typeclass on which this cmdset is to be assigned
|
cmdsetobj - the database object/typeclass on which this cmdset is to be
|
||||||
(this can be also channels and exits, as well as players but there will
|
assigned (this can be also channels and exits, as well as players
|
||||||
always be such an object)
|
but there will always be such an object)
|
||||||
emit_to_obj - if given, error is emitted to this object (in addition to logging)
|
emit_to_obj - if given, error is emitted to this object (in addition
|
||||||
no_logging - don't log/send error messages. This can be useful if import_cmdset is just
|
to logging)
|
||||||
used to check if this is a valid python path or not.
|
no_logging - don't log/send error messages. This can be useful
|
||||||
|
if import_cmdset is just used to check if this is a
|
||||||
|
valid python path or not.
|
||||||
function returns None if an error was encountered or path not found.
|
function returns None if an error was encountered or path not found.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
@ -117,7 +120,8 @@ def import_cmdset(python_path, cmdsetobj, emit_to_obj=None, no_logging=False):
|
||||||
raise
|
raise
|
||||||
except KeyError:
|
except KeyError:
|
||||||
errstring = _("Error in loading cmdset: No cmdset class '%(classname)s' in %(modulepath)s.")
|
errstring = _("Error in loading cmdset: No cmdset class '%(classname)s' in %(modulepath)s.")
|
||||||
errstring = errstring % {"classname":classname, "modulepath":modulepath}
|
errstring = errstring % {"classname": classname,
|
||||||
|
"modulepath": modulepath}
|
||||||
raise
|
raise
|
||||||
except Exception:
|
except Exception:
|
||||||
errstring = _("Compile/Run error when loading cmdset '%s'. Error was logged.")
|
errstring = _("Compile/Run error when loading cmdset '%s'. Error was logged.")
|
||||||
|
|
@ -135,15 +139,17 @@ def import_cmdset(python_path, cmdsetobj, emit_to_obj=None, no_logging=False):
|
||||||
|
|
||||||
# classes
|
# classes
|
||||||
|
|
||||||
|
|
||||||
class CmdSetHandler(object):
|
class CmdSetHandler(object):
|
||||||
"""
|
"""
|
||||||
The CmdSetHandler is always stored on an object, this object is supplied as an argument.
|
The CmdSetHandler is always stored on an object, this object is supplied
|
||||||
|
as an argument.
|
||||||
|
|
||||||
The 'current' cmdset is the merged set currently active for this object.
|
The 'current' cmdset is the merged set currently active for this object.
|
||||||
This is the set the game engine will retrieve when determining which
|
This is the set the game engine will retrieve when determining which
|
||||||
commands are available to the object. The cmdset_stack holds a history of all CmdSets
|
commands are available to the object. The cmdset_stack holds a history of
|
||||||
to allow the handler to remove/add cmdsets at will. Doing so will re-calculate
|
all CmdSets to allow the handler to remove/add cmdsets at will. Doing so
|
||||||
the 'current' cmdset.
|
will re-calculate the 'current' cmdset.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, obj):
|
def __init__(self, obj):
|
||||||
|
|
@ -176,10 +182,8 @@ class CmdSetHandler(object):
|
||||||
mergelist = []
|
mergelist = []
|
||||||
if len(self.cmdset_stack) > 1:
|
if len(self.cmdset_stack) > 1:
|
||||||
# We have more than one cmdset in stack; list them all
|
# We have more than one cmdset in stack; list them all
|
||||||
num = 0
|
|
||||||
#print self.cmdset_stack, self.mergetype_stack
|
#print self.cmdset_stack, self.mergetype_stack
|
||||||
for snum, cmdset in enumerate(self.cmdset_stack):
|
for snum, cmdset in enumerate(self.cmdset_stack):
|
||||||
num = snum
|
|
||||||
mergetype = self.mergetype_stack[snum]
|
mergetype = self.mergetype_stack[snum]
|
||||||
permstring = "non-perm"
|
permstring = "non-perm"
|
||||||
if cmdset.permanent:
|
if cmdset.permanent:
|
||||||
|
|
@ -196,17 +200,21 @@ class CmdSetHandler(object):
|
||||||
mergetype = self.mergetype_stack[-1]
|
mergetype = self.mergetype_stack[-1]
|
||||||
if mergetype != self.current.mergetype:
|
if mergetype != self.current.mergetype:
|
||||||
merged_on = self.cmdset_stack[-2].key
|
merged_on = self.cmdset_stack[-2].key
|
||||||
mergetype = _("custom %(mergetype)s on cmdset '%(merged_on)s'") % {"mergetype":mergetype, "merged_on":merged_on}
|
mergetype = _("custom %(mergetype)s on cmdset '%(merged_on)s'") % \
|
||||||
|
{"mergetype": mergetype, "merged_on":merged_on}
|
||||||
if mergelist:
|
if mergelist:
|
||||||
string += _(" <Merged %(mergelist)s (%(mergetype)s, prio %(prio)i)>: %(current)s") % \
|
string += _(" <Merged %(mergelist)s (%(mergetype)s, prio %(prio)i)>: %(current)s") % \
|
||||||
{"mergelist": "+".join(mergelist), "mergetype":mergetype, "prio":self.current.priority, "current":self.current}
|
{"mergelist": "+".join(mergelist),
|
||||||
|
"mergetype": mergetype, "prio": self.current.priority,
|
||||||
|
"current":self.current}
|
||||||
else:
|
else:
|
||||||
permstring = "non-perm"
|
permstring = "non-perm"
|
||||||
if self.current.permanent:
|
if self.current.permanent:
|
||||||
permstring = "perm"
|
permstring = "perm"
|
||||||
string += _(" <%(key)s (%(mergetype)s, prio %(prio)i, %(permstring)s)>: %(keylist)s") % \
|
string += _(" <%(key)s (%(mergetype)s, prio %(prio)i, %(permstring)s)>: %(keylist)s") % \
|
||||||
{"key":self.current.key, "mergetype":mergetype, "prio":self.current.priority, "permstring":permstring,
|
{"key": self.current.key, "mergetype": mergetype,
|
||||||
"keylist":", ".join(cmd.key for cmd in sorted(self.current, key=lambda o:o.key))}
|
"prio": self.current.priority, "permstring": permstring,
|
||||||
|
"keylist": ", ".join(cmd.key for cmd in sorted(self.current, key=lambda o: o.key))}
|
||||||
return string.strip()
|
return string.strip()
|
||||||
|
|
||||||
def _import_cmdset(self, cmdset_path, emit_to_obj=None):
|
def _import_cmdset(self, cmdset_path, emit_to_obj=None):
|
||||||
|
|
@ -362,10 +370,12 @@ class CmdSetHandler(object):
|
||||||
else:
|
else:
|
||||||
# try it as a callable
|
# try it as a callable
|
||||||
if callable(cmdset) and hasattr(cmdset, 'path'):
|
if callable(cmdset) and hasattr(cmdset, 'path'):
|
||||||
delcmdsets = [cset for cset in self.cmdset_stack[1:] if cset.path == cmdset.path]
|
delcmdsets = [cset for cset in self.cmdset_stack[1:]
|
||||||
|
if cset.path == cmdset.path]
|
||||||
else:
|
else:
|
||||||
# try it as a path or key
|
# try it as a path or key
|
||||||
delcmdsets = [cset for cset in self.cmdset_stack[1:] if cset.path == cmdset or cset.key == cmdset]
|
delcmdsets = [cset for cset in self.cmdset_stack[1:]
|
||||||
|
if cset.path == cmdset or cset.key == cmdset]
|
||||||
storage = []
|
storage = []
|
||||||
|
|
||||||
if any(cset.permanent for cset in delcmdsets):
|
if any(cset.permanent for cset in delcmdsets):
|
||||||
|
|
@ -387,7 +397,10 @@ class CmdSetHandler(object):
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
def delete_default(self):
|
def delete_default(self):
|
||||||
"This explicitly deletes the default cmdset. It's the only command that can."
|
"""
|
||||||
|
This explicitly deletes the default cmdset. It's the
|
||||||
|
only command that can.
|
||||||
|
"""
|
||||||
if self.cmdset_stack:
|
if self.cmdset_stack:
|
||||||
cmdset = self.cmdset_stack[0]
|
cmdset = self.cmdset_stack[0]
|
||||||
if cmdset.permanent:
|
if cmdset.permanent:
|
||||||
|
|
@ -404,7 +417,8 @@ class CmdSetHandler(object):
|
||||||
|
|
||||||
def all(self):
|
def all(self):
|
||||||
"""
|
"""
|
||||||
Returns the list of cmdsets. Mostly useful to check if stack if empty or not.
|
Returns the list of cmdsets. Mostly useful to check
|
||||||
|
if stack if empty or not.
|
||||||
"""
|
"""
|
||||||
return self.cmdset_stack
|
return self.cmdset_stack
|
||||||
|
|
||||||
|
|
@ -431,13 +445,6 @@ class CmdSetHandler(object):
|
||||||
else:
|
else:
|
||||||
return any([cmdset.key == cmdset_key for cmdset in self.cmdset_stack])
|
return any([cmdset.key == cmdset_key for cmdset in self.cmdset_stack])
|
||||||
|
|
||||||
|
|
||||||
def all(self):
|
|
||||||
"""
|
|
||||||
Returns all cmdsets.
|
|
||||||
"""
|
|
||||||
return self.cmdset_stack
|
|
||||||
|
|
||||||
def reset(self):
|
def reset(self):
|
||||||
"""
|
"""
|
||||||
Force reload of all cmdsets in handler. This should be called
|
Force reload of all cmdsets in handler. This should be called
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import re
|
||||||
from src.locks.lockhandler import LockHandler
|
from src.locks.lockhandler import LockHandler
|
||||||
from src.utils.utils import is_iter, fill
|
from src.utils.utils import is_iter, fill
|
||||||
|
|
||||||
|
|
||||||
def _init_command(mcs, **kwargs):
|
def _init_command(mcs, **kwargs):
|
||||||
"""
|
"""
|
||||||
Helper command.
|
Helper command.
|
||||||
|
|
@ -17,20 +18,23 @@ def _init_command(mcs, **kwargs):
|
||||||
Sets up locks to be more forgiving. This is used both by the metaclass
|
Sets up locks to be more forgiving. This is used both by the metaclass
|
||||||
and (optionally) at instantiation time.
|
and (optionally) at instantiation time.
|
||||||
|
|
||||||
If kwargs are given, these are set as instance-specific properties on the command.
|
If kwargs are given, these are set as instance-specific properties
|
||||||
|
on the command.
|
||||||
"""
|
"""
|
||||||
for i in range(len(kwargs)):
|
for i in range(len(kwargs)):
|
||||||
# used for dynamic creation of commands
|
# used for dynamic creation of commands
|
||||||
key, value = kwargs.popitem()
|
key, value = kwargs.popitem()
|
||||||
setattr(mcs, key, value)
|
setattr(mcs, key, value)
|
||||||
|
|
||||||
mcs.key = mcs.key.lower()
|
mcs.key = mcs.key.lower()
|
||||||
if mcs.aliases and not is_iter(mcs.aliases):
|
if mcs.aliases and not is_iter(mcs.aliases):
|
||||||
try:
|
try:
|
||||||
mcs.aliases = [str(alias).strip().lower() for alias in mcs.aliases.split(',')]
|
mcs.aliases = [str(alias).strip().lower()
|
||||||
|
for alias in mcs.aliases.split(',')]
|
||||||
except Exception:
|
except Exception:
|
||||||
mcs.aliases = []
|
mcs.aliases = []
|
||||||
mcs.aliases = list(set(alias for alias in mcs.aliases if alias and alias != mcs.key))
|
mcs.aliases = list(set(alias for alias in mcs.aliases
|
||||||
|
if alias and alias != mcs.key))
|
||||||
|
|
||||||
# optimization - a set is much faster to match against than a list
|
# optimization - a set is much faster to match against than a list
|
||||||
mcs._matchset = set([mcs.key] + mcs.aliases)
|
mcs._matchset = set([mcs.key] + mcs.aliases)
|
||||||
|
|
@ -84,6 +88,7 @@ class CommandMeta(type):
|
||||||
# structure can parse the input string the same way, minimizing
|
# structure can parse the input string the same way, minimizing
|
||||||
# parsing errors.
|
# parsing errors.
|
||||||
|
|
||||||
|
|
||||||
class Command(object):
|
class Command(object):
|
||||||
"""
|
"""
|
||||||
Base command
|
Base command
|
||||||
|
|
@ -112,13 +117,16 @@ class Command(object):
|
||||||
key - identifier for command (e.g. "look")
|
key - identifier for command (e.g. "look")
|
||||||
aliases - (optional) list of aliases (e.g. ["l", "loo"])
|
aliases - (optional) list of aliases (e.g. ["l", "loo"])
|
||||||
locks - lock string (default is "cmd:all()")
|
locks - lock string (default is "cmd:all()")
|
||||||
help_category - how to organize this help entry in help system (default is "General")
|
help_category - how to organize this help entry in help system
|
||||||
|
(default is "General")
|
||||||
auto_help - defaults to True. Allows for turning off auto-help generation
|
auto_help - defaults to True. Allows for turning off auto-help generation
|
||||||
arg_regex - (optional) raw string regex defining how the argument part of the command should look
|
arg_regex - (optional) raw string regex defining how the argument part of
|
||||||
in order to match for this command (e.g. must it be a space between cmdname and arg?)
|
the command should look in order to match for this command
|
||||||
|
(e.g. must it be a space between cmdname and arg?)
|
||||||
|
|
||||||
(Note that if auto_help is on, this initial string is also used by the system
|
(Note that if auto_help is on, this initial string is also used by the
|
||||||
to create the help entry for the command, so it's a good idea to format it similar to this one)
|
system to create the help entry for the command, so it's a good idea to
|
||||||
|
format it similar to this one)
|
||||||
"""
|
"""
|
||||||
# Tie our metaclass, for some convenience cleanup
|
# Tie our metaclass, for some convenience cleanup
|
||||||
__metaclass__ = CommandMeta
|
__metaclass__ = CommandMeta
|
||||||
|
|
@ -127,7 +135,8 @@ class Command(object):
|
||||||
key = "command"
|
key = "command"
|
||||||
# alternative ways to call the command (e.g. 'l', 'glance', 'examine')
|
# alternative ways to call the command (e.g. 'l', 'glance', 'examine')
|
||||||
aliases = []
|
aliases = []
|
||||||
# a list of lock definitions on the form cmd:[NOT] func(args) [ AND|OR][ NOT] func2(args)
|
# a list of lock definitions on the form
|
||||||
|
# cmd:[NOT] func(args) [ AND|OR][ NOT] func2(args)
|
||||||
locks = ""
|
locks = ""
|
||||||
# used by the help system to group commands in lists.
|
# used by the help system to group commands in lists.
|
||||||
help_category = "general"
|
help_category = "general"
|
||||||
|
|
@ -136,7 +145,8 @@ class Command(object):
|
||||||
auto_help = True
|
auto_help = True
|
||||||
# auto-set (by Evennia on command instantiation) are:
|
# auto-set (by Evennia on command instantiation) are:
|
||||||
# obj - which object this command is defined on
|
# obj - which object this command is defined on
|
||||||
# sessid - which session-id (if any) is responsible for triggering this command
|
# sessid - which session-id (if any) is responsible for
|
||||||
|
# triggering this command
|
||||||
#
|
#
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
|
|
@ -206,20 +216,22 @@ class Command(object):
|
||||||
"""
|
"""
|
||||||
return self.lockhandler.check(srcobj, access_type, default=default)
|
return self.lockhandler.check(srcobj, access_type, default=default)
|
||||||
|
|
||||||
def msg(self, msg="", to_obj=None, from_obj=None, sessid=None, all_sessions=False, **kwargs):
|
def msg(self, msg="", to_obj=None, from_obj=None,
|
||||||
|
sessid=None, all_sessions=False, **kwargs):
|
||||||
"""
|
"""
|
||||||
This is a shortcut instad of calling msg() directly on an object - it will
|
This is a shortcut instad of calling msg() directly on an object - it
|
||||||
detect if caller is an Object or a Player and also appends self.sessid
|
will detect if caller is an Object or a Player and also appends
|
||||||
automatically.
|
self.sessid automatically.
|
||||||
|
|
||||||
msg - text string of message to send
|
msg - text string of message to send
|
||||||
to_obj - target object of message. Defaults to self.caller
|
to_obj - target object of message. Defaults to self.caller
|
||||||
from_obj - source of message. Defaults to to_obj
|
from_obj - source of message. Defaults to to_obj
|
||||||
data - optional dictionary of data
|
data - optional dictionary of data
|
||||||
sessid - supply data only to a unique sessid (normally not used - this is only potentially useful if
|
sessid - supply data only to a unique sessid (normally not used -
|
||||||
to_obj is a Player object different from self.caller or self.caller.player)
|
this is only potentially useful if to_obj is a Player object
|
||||||
all_sessions (bool) - default is to send only to the session connected to
|
different from self.caller or self.caller.player)
|
||||||
the target object
|
all_sessions (bool) - default is to send only to the session
|
||||||
|
connected to the target object
|
||||||
"""
|
"""
|
||||||
from_obj = from_obj or self.caller
|
from_obj = from_obj or self.caller
|
||||||
to_obj = to_obj or from_obj
|
to_obj = to_obj or from_obj
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,8 @@ Admin commands
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import time, re
|
import time
|
||||||
|
import re
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from src.server.sessionhandler import SESSIONS
|
from src.server.sessionhandler import SESSIONS
|
||||||
|
|
@ -15,8 +16,8 @@ from src.commands.default.muxcommand import MuxCommand
|
||||||
PERMISSION_HIERARCHY = [p.lower() for p in settings.PERMISSION_HIERARCHY]
|
PERMISSION_HIERARCHY = [p.lower() for p in settings.PERMISSION_HIERARCHY]
|
||||||
|
|
||||||
# limit members for API inclusion
|
# limit members for API inclusion
|
||||||
__all__ = ("CmdBoot", "CmdBan", "CmdUnban", "CmdDelPlayer", "CmdEmit", "CmdNewPassword",
|
__all__ = ("CmdBoot", "CmdBan", "CmdUnban", "CmdDelPlayer",
|
||||||
"CmdPerm", "CmdWall")
|
"CmdEmit", "CmdNewPassword", "CmdPerm", "CmdWall")
|
||||||
|
|
||||||
|
|
||||||
class CmdBoot(MuxCommand):
|
class CmdBoot(MuxCommand):
|
||||||
|
|
@ -98,6 +99,7 @@ class CmdBoot(MuxCommand):
|
||||||
# regex matching IP addresses with wildcards, eg. 233.122.4.*
|
# regex matching IP addresses with wildcards, eg. 233.122.4.*
|
||||||
IPREGEX = re.compile(r"[0-9*]{1,3}\.[0-9*]{1,3}\.[0-9*]{1,3}\.[0-9*]{1,3}")
|
IPREGEX = re.compile(r"[0-9*]{1,3}\.[0-9*]{1,3}\.[0-9*]{1,3}\.[0-9*]{1,3}")
|
||||||
|
|
||||||
|
|
||||||
def list_bans(banlist):
|
def list_bans(banlist):
|
||||||
"""
|
"""
|
||||||
Helper function to display a list of active bans. Input argument
|
Helper function to display a list of active bans. Input argument
|
||||||
|
|
@ -108,12 +110,13 @@ def list_bans(banlist):
|
||||||
|
|
||||||
table = prettytable.PrettyTable(["{wid", "{wname/ip", "{wdate", "{wreason"])
|
table = prettytable.PrettyTable(["{wid", "{wname/ip", "{wdate", "{wreason"])
|
||||||
for inum, ban in enumerate(banlist):
|
for inum, ban in enumerate(banlist):
|
||||||
table.add_row([str(inum+1),
|
table.add_row([str(inum + 1),
|
||||||
ban[0] and ban[0] or ban[1],
|
ban[0] and ban[0] or ban[1],
|
||||||
ban[3], ban[4]])
|
ban[3], ban[4]])
|
||||||
string = "{wActive bans:{n\n%s" % table
|
string = "{wActive bans:{n\n%s" % table
|
||||||
return string
|
return string
|
||||||
|
|
||||||
|
|
||||||
class CmdBan(MuxCommand):
|
class CmdBan(MuxCommand):
|
||||||
"""
|
"""
|
||||||
ban a player from the server
|
ban a player from the server
|
||||||
|
|
@ -152,7 +155,7 @@ class CmdBan(MuxCommand):
|
||||||
key = "@ban"
|
key = "@ban"
|
||||||
aliases = ["@bans"]
|
aliases = ["@bans"]
|
||||||
locks = "cmd:perm(ban) or perm(Immortals)"
|
locks = "cmd:perm(ban) or perm(Immortals)"
|
||||||
help_category="Admin"
|
help_category = "Admin"
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -172,14 +175,15 @@ class CmdBan(MuxCommand):
|
||||||
banlist = []
|
banlist = []
|
||||||
|
|
||||||
if not self.args or (self.switches
|
if not self.args or (self.switches
|
||||||
and not any(switch in ('ip', 'name') for switch in self.switches)):
|
and not any(switch in ('ip', 'name')
|
||||||
|
for switch in self.switches)):
|
||||||
self.caller.msg(list_bans(banlist))
|
self.caller.msg(list_bans(banlist))
|
||||||
return
|
return
|
||||||
|
|
||||||
now = time.ctime()
|
now = time.ctime()
|
||||||
reason = ""
|
reason = ""
|
||||||
if ':' in self.args:
|
if ':' in self.args:
|
||||||
ban, reason = self.args.rsplit(':',1)
|
ban, reason = self.args.rsplit(':', 1)
|
||||||
else:
|
else:
|
||||||
ban = self.args
|
ban = self.args
|
||||||
ban = ban.lower()
|
ban = ban.lower()
|
||||||
|
|
@ -193,7 +197,7 @@ class CmdBan(MuxCommand):
|
||||||
typ = "IP"
|
typ = "IP"
|
||||||
ban = ipban[0]
|
ban = ipban[0]
|
||||||
# replace * with regex form and compile it
|
# replace * with regex form and compile it
|
||||||
ipregex = ban.replace('.','\.')
|
ipregex = ban.replace('.', '\.')
|
||||||
ipregex = ipregex.replace('*', '[0-9]{1,3}')
|
ipregex = ipregex.replace('*', '[0-9]{1,3}')
|
||||||
#print "regex:",ipregex
|
#print "regex:",ipregex
|
||||||
ipregex = re.compile(r"%s" % ipregex)
|
ipregex = re.compile(r"%s" % ipregex)
|
||||||
|
|
@ -203,6 +207,7 @@ class CmdBan(MuxCommand):
|
||||||
ServerConfig.objects.conf('server_bans', banlist)
|
ServerConfig.objects.conf('server_bans', banlist)
|
||||||
self.caller.msg("%s-Ban {w%s{x was added." % (typ, ban))
|
self.caller.msg("%s-Ban {w%s{x was added." % (typ, ban))
|
||||||
|
|
||||||
|
|
||||||
class CmdUnban(MuxCommand):
|
class CmdUnban(MuxCommand):
|
||||||
"""
|
"""
|
||||||
remove a ban
|
remove a ban
|
||||||
|
|
@ -218,7 +223,7 @@ class CmdUnban(MuxCommand):
|
||||||
"""
|
"""
|
||||||
key = "@unban"
|
key = "@unban"
|
||||||
locks = "cmd:perm(unban) or perm(Immortals)"
|
locks = "cmd:perm(unban) or perm(Immortals)"
|
||||||
help_category="Admin"
|
help_category = "Admin"
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"Implement unbanning"
|
"Implement unbanning"
|
||||||
|
|
@ -241,10 +246,11 @@ class CmdUnban(MuxCommand):
|
||||||
self.caller.msg("Ban id {w%s{x was not found." % self.args)
|
self.caller.msg("Ban id {w%s{x was not found." % self.args)
|
||||||
else:
|
else:
|
||||||
# all is ok, clear ban
|
# all is ok, clear ban
|
||||||
ban = banlist[num-1]
|
ban = banlist[num - 1]
|
||||||
del banlist[num-1]
|
del banlist[num - 1]
|
||||||
ServerConfig.objects.conf('server_bans', banlist)
|
ServerConfig.objects.conf('server_bans', banlist)
|
||||||
self.caller.msg("Cleared ban %s: %s" % (num, " ".join([s for s in ban[:2]])))
|
self.caller.msg("Cleared ban %s: %s" %
|
||||||
|
(num, " ".join([s for s in ban[:2]])))
|
||||||
|
|
||||||
|
|
||||||
class CmdDelPlayer(MuxCommand):
|
class CmdDelPlayer(MuxCommand):
|
||||||
|
|
@ -408,7 +414,7 @@ class CmdEmit(MuxCommand):
|
||||||
obj = caller.search(objname, global_search=True)
|
obj = caller.search(objname, global_search=True)
|
||||||
if not obj:
|
if not obj:
|
||||||
return
|
return
|
||||||
if rooms_only and not obj.location == None:
|
if rooms_only and not obj.location is None:
|
||||||
caller.msg("%s is not a room. Ignored." % objname)
|
caller.msg("%s is not a room. Ignored." % objname)
|
||||||
continue
|
continue
|
||||||
if players_only and not obj.has_player:
|
if players_only and not obj.has_player:
|
||||||
|
|
@ -425,7 +431,6 @@ class CmdEmit(MuxCommand):
|
||||||
caller.msg("You are not allowed to emit to %s." % objname)
|
caller.msg("You are not allowed to emit to %s." % objname)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class CmdNewPassword(MuxCommand):
|
class CmdNewPassword(MuxCommand):
|
||||||
"""
|
"""
|
||||||
@userpassword
|
@userpassword
|
||||||
|
|
@ -457,7 +462,8 @@ class CmdNewPassword(MuxCommand):
|
||||||
player.user.save()
|
player.user.save()
|
||||||
self.msg("%s - new password set to '%s'." % (player.name, self.rhs))
|
self.msg("%s - new password set to '%s'." % (player.name, self.rhs))
|
||||||
if player.character != caller:
|
if player.character != caller:
|
||||||
player.msg("%s has changed your password to '%s'." % (caller.name, self.rhs))
|
player.msg("%s has changed your password to '%s'." % (caller.name,
|
||||||
|
self.rhs))
|
||||||
|
|
||||||
|
|
||||||
class CmdPerm(MuxCommand):
|
class CmdPerm(MuxCommand):
|
||||||
|
|
@ -497,7 +503,7 @@ class CmdPerm(MuxCommand):
|
||||||
if playermode:
|
if playermode:
|
||||||
obj = caller.search_player(lhs)
|
obj = caller.search_player(lhs)
|
||||||
else:
|
else:
|
||||||
obj = caller.search(lhs, global_search=True)
|
obj = caller.search(lhs, global_search=True)
|
||||||
if not obj:
|
if not obj:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
@ -511,7 +517,9 @@ class CmdPerm(MuxCommand):
|
||||||
string += "<None>"
|
string += "<None>"
|
||||||
else:
|
else:
|
||||||
string += ", ".join(obj.permissions.all())
|
string += ", ".join(obj.permissions.all())
|
||||||
if hasattr(obj, 'player') and hasattr(obj.player, 'is_superuser') and obj.player.is_superuser:
|
if (hasattr(obj, 'player') and
|
||||||
|
hasattr(obj.player, 'is_superuser') and
|
||||||
|
obj.player.is_superuser):
|
||||||
string += "\n(... but this object is currently controlled by a SUPERUSER! "
|
string += "\n(... but this object is currently controlled by a SUPERUSER! "
|
||||||
string += "All access checks are passed automatically.)"
|
string += "All access checks are passed automatically.)"
|
||||||
caller.msg(string)
|
caller.msg(string)
|
||||||
|
|
@ -539,9 +547,10 @@ class CmdPerm(MuxCommand):
|
||||||
|
|
||||||
for perm in self.rhslist:
|
for perm in self.rhslist:
|
||||||
|
|
||||||
# don't allow to set a permission higher in the hierarchy than the one the
|
# don't allow to set a permission higher in the hierarchy than
|
||||||
# caller has (to prevent self-escalation)
|
# the one the caller has (to prevent self-escalation)
|
||||||
if perm.lower() in PERMISSION_HIERARCHY and not obj.locks.check_lockstring(caller, "dummy:perm(%s)" % perm):
|
if (perm.lower() in PERMISSION_HIERARCHY and not
|
||||||
|
obj.locks.check_lockstring(caller, "dummy:perm(%s)" % perm)):
|
||||||
caller.msg("You cannot assign a permission higher than the one you have yourself.")
|
caller.msg("You cannot assign a permission higher than the one you have yourself.")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -95,11 +95,12 @@ def format_header(caller, entry):
|
||||||
stacklen = len(caller.ndb.batch_stack)
|
stacklen = len(caller.ndb.batch_stack)
|
||||||
header = "{w%02i/%02i{G: %s{n" % (ptr, stacklen, header)
|
header = "{w%02i/%02i{G: %s{n" % (ptr, stacklen, header)
|
||||||
# add extra space to the side for padding.
|
# add extra space to the side for padding.
|
||||||
header = "%s%s" % (header, " "*(width - len(header)))
|
header = "%s%s" % (header, " " * (width - len(header)))
|
||||||
header = header.replace('\n', '\\n')
|
header = header.replace('\n', '\\n')
|
||||||
|
|
||||||
return header
|
return header
|
||||||
|
|
||||||
|
|
||||||
def format_code(entry):
|
def format_code(entry):
|
||||||
"""
|
"""
|
||||||
Formats the viewing of code and errors
|
Formats the viewing of code and errors
|
||||||
|
|
@ -109,6 +110,7 @@ def format_code(entry):
|
||||||
code += "\n{G>>>{n %s" % line
|
code += "\n{G>>>{n %s" % line
|
||||||
return code.strip()
|
return code.strip()
|
||||||
|
|
||||||
|
|
||||||
def batch_cmd_exec(caller):
|
def batch_cmd_exec(caller):
|
||||||
"""
|
"""
|
||||||
Helper function for executing a single batch-command entry
|
Helper function for executing a single batch-command entry
|
||||||
|
|
@ -124,6 +126,7 @@ def batch_cmd_exec(caller):
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def batch_code_exec(caller):
|
def batch_code_exec(caller):
|
||||||
"""
|
"""
|
||||||
Helper function for executing a single batch-code entry
|
Helper function for executing a single batch-code entry
|
||||||
|
|
@ -135,12 +138,13 @@ def batch_code_exec(caller):
|
||||||
|
|
||||||
caller.msg(format_header(caller, codedict['code']))
|
caller.msg(format_header(caller, codedict['code']))
|
||||||
err = BATCHCODE.code_exec(codedict,
|
err = BATCHCODE.code_exec(codedict,
|
||||||
extra_environ={"caller":caller}, debug=debug)
|
extra_environ={"caller": caller}, debug=debug)
|
||||||
if err:
|
if err:
|
||||||
caller.msg(format_code(err))
|
caller.msg(format_code(err))
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def step_pointer(caller, step=1):
|
def step_pointer(caller, step=1):
|
||||||
"""
|
"""
|
||||||
Step in stack, returning the item located.
|
Step in stack, returning the item located.
|
||||||
|
|
@ -156,7 +160,8 @@ def step_pointer(caller, step=1):
|
||||||
caller.msg("{RBeginning of batch file.")
|
caller.msg("{RBeginning of batch file.")
|
||||||
if ptr + step >= nstack:
|
if ptr + step >= nstack:
|
||||||
caller.msg("{REnd of batch file.")
|
caller.msg("{REnd of batch file.")
|
||||||
caller.ndb.batch_stackptr = max(0, min(nstack-1, ptr + step))
|
caller.ndb.batch_stackptr = max(0, min(nstack - 1, ptr + step))
|
||||||
|
|
||||||
|
|
||||||
def show_curr(caller, showall=False):
|
def show_curr(caller, showall=False):
|
||||||
"""
|
"""
|
||||||
|
|
@ -186,6 +191,7 @@ def show_curr(caller, showall=False):
|
||||||
string += "\n{G|{n %s" % line
|
string += "\n{G|{n %s" % line
|
||||||
caller.msg(string)
|
caller.msg(string)
|
||||||
|
|
||||||
|
|
||||||
def purge_processor(caller):
|
def purge_processor(caller):
|
||||||
"""
|
"""
|
||||||
This purges all effects running
|
This purges all effects running
|
||||||
|
|
@ -201,12 +207,13 @@ def purge_processor(caller):
|
||||||
# clear everything but the default cmdset.
|
# clear everything but the default cmdset.
|
||||||
caller.cmdset.delete(BatchSafeCmdSet)
|
caller.cmdset.delete(BatchSafeCmdSet)
|
||||||
caller.cmdset.clear()
|
caller.cmdset.clear()
|
||||||
caller.scripts.validate() # this will purge interactive mode
|
caller.scripts.validate() # this will purge interactive mode
|
||||||
|
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
# main access commands
|
# main access commands
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
class CmdBatchCommands(MuxCommand):
|
class CmdBatchCommands(MuxCommand):
|
||||||
"""
|
"""
|
||||||
Build from batch-command file
|
Build from batch-command file
|
||||||
|
|
@ -275,19 +282,25 @@ class CmdBatchCommands(MuxCommand):
|
||||||
procpool = False
|
procpool = False
|
||||||
if "PythonProcPool" in utils.server_services():
|
if "PythonProcPool" in utils.server_services():
|
||||||
if utils.uses_database("sqlite3"):
|
if utils.uses_database("sqlite3"):
|
||||||
caller.msg("Batchprocessor disabled ProcPool under SQLite3.")
|
caller.msg("Batchprocessor disabled ProcPool under SQLite3.")
|
||||||
else:
|
else:
|
||||||
procpool=True
|
procpool = True
|
||||||
|
|
||||||
if procpool:
|
if procpool:
|
||||||
# run in parallel process
|
# run in parallel process
|
||||||
def callback(r):
|
def callback(r):
|
||||||
caller.msg(" {GBatchfile '%s' applied." % python_path)
|
caller.msg(" {GBatchfile '%s' applied." % python_path)
|
||||||
purge_processor(caller)
|
purge_processor(caller)
|
||||||
|
|
||||||
def errback(e):
|
def errback(e):
|
||||||
caller.msg(" {RError from processor: '%s'" % e)
|
caller.msg(" {RError from processor: '%s'" % e)
|
||||||
purge_processor(caller)
|
purge_processor(caller)
|
||||||
utils.run_async(_PROCPOOL_BATCHCMD_SOURCE, commands=commands, caller=caller, at_return=callback, at_err=errback)
|
|
||||||
|
utils.run_async(_PROCPOOL_BATCHCMD_SOURCE,
|
||||||
|
commands=commands,
|
||||||
|
caller=caller,
|
||||||
|
at_return=callback,
|
||||||
|
at_err=errback)
|
||||||
else:
|
else:
|
||||||
# run in-process (might block)
|
# run in-process (might block)
|
||||||
for inum in range(len(commands)):
|
for inum in range(len(commands)):
|
||||||
|
|
@ -295,11 +308,13 @@ class CmdBatchCommands(MuxCommand):
|
||||||
if not batch_cmd_exec(caller):
|
if not batch_cmd_exec(caller):
|
||||||
return
|
return
|
||||||
step_pointer(caller, 1)
|
step_pointer(caller, 1)
|
||||||
# clean out the safety cmdset and clean out all other temporary attrs.
|
# clean out the safety cmdset and clean out all other
|
||||||
|
# temporary attrs.
|
||||||
string = " Batchfile '%s' applied." % python_path
|
string = " Batchfile '%s' applied." % python_path
|
||||||
caller.msg("{G%s" % string)
|
caller.msg("{G%s" % string)
|
||||||
purge_processor(caller)
|
purge_processor(caller)
|
||||||
|
|
||||||
|
|
||||||
class CmdBatchCode(MuxCommand):
|
class CmdBatchCode(MuxCommand):
|
||||||
"""
|
"""
|
||||||
Build from batch-code file
|
Build from batch-code file
|
||||||
|
|
@ -352,7 +367,7 @@ class CmdBatchCode(MuxCommand):
|
||||||
|
|
||||||
debug = False
|
debug = False
|
||||||
if 'debug' in switches:
|
if 'debug' in switches:
|
||||||
debug = True
|
debug = True
|
||||||
|
|
||||||
# Store work data in cache
|
# Store work data in cache
|
||||||
caller.ndb.batch_stack = codes
|
caller.ndb.batch_stack = codes
|
||||||
|
|
@ -376,18 +391,23 @@ class CmdBatchCode(MuxCommand):
|
||||||
procpool = False
|
procpool = False
|
||||||
if "PythonProcPool" in utils.server_services():
|
if "PythonProcPool" in utils.server_services():
|
||||||
if utils.uses_database("sqlite3"):
|
if utils.uses_database("sqlite3"):
|
||||||
caller.msg("Batchprocessor disabled ProcPool under SQLite3.")
|
caller.msg("Batchprocessor disabled ProcPool under SQLite3.")
|
||||||
else:
|
else:
|
||||||
procpool=True
|
procpool = True
|
||||||
if procpool:
|
if procpool:
|
||||||
# run in parallel process
|
# run in parallel process
|
||||||
def callback(r):
|
def callback(r):
|
||||||
caller.msg(" {GBatchfile '%s' applied." % python_path)
|
caller.msg(" {GBatchfile '%s' applied." % python_path)
|
||||||
purge_processor(caller)
|
purge_processor(caller)
|
||||||
|
|
||||||
def errback(e):
|
def errback(e):
|
||||||
caller.msg(" {RError from processor: '%s'" % e)
|
caller.msg(" {RError from processor: '%s'" % e)
|
||||||
purge_processor(caller)
|
purge_processor(caller)
|
||||||
utils.run_async(_PROCPOOL_BATCHCODE_SOURCE, codes=codes, caller=caller, at_return=callback, at_err=errback)
|
utils.run_async(_PROCPOOL_BATCHCODE_SOURCE,
|
||||||
|
codes=codes,
|
||||||
|
caller=caller,
|
||||||
|
at_return=callback,
|
||||||
|
at_err=errback)
|
||||||
else:
|
else:
|
||||||
# un in-process (will block)
|
# un in-process (will block)
|
||||||
for inum in range(len(codes)):
|
for inum in range(len(codes)):
|
||||||
|
|
@ -395,7 +415,8 @@ class CmdBatchCode(MuxCommand):
|
||||||
if not batch_code_exec(caller):
|
if not batch_code_exec(caller):
|
||||||
return
|
return
|
||||||
step_pointer(caller, 1)
|
step_pointer(caller, 1)
|
||||||
# clean out the safety cmdset and clean out all other temporary attrs.
|
# clean out the safety cmdset and clean out all other
|
||||||
|
# temporary attrs.
|
||||||
string = " Batchfile '%s' applied." % python_path
|
string = " Batchfile '%s' applied." % python_path
|
||||||
caller.msg("{G%s" % string)
|
caller.msg("{G%s" % string)
|
||||||
purge_processor(caller)
|
purge_processor(caller)
|
||||||
|
|
@ -423,6 +444,7 @@ class CmdStateAbort(MuxCommand):
|
||||||
purge_processor(self.caller)
|
purge_processor(self.caller)
|
||||||
self.caller.msg("Exited processor and reset out active cmdset back to the default one.")
|
self.caller.msg("Exited processor and reset out active cmdset back to the default one.")
|
||||||
|
|
||||||
|
|
||||||
class CmdStateLL(MuxCommand):
|
class CmdStateLL(MuxCommand):
|
||||||
"""
|
"""
|
||||||
ll
|
ll
|
||||||
|
|
@ -479,6 +501,7 @@ class CmdStateRR(MuxCommand):
|
||||||
caller.msg(format_code("File reloaded. Staying on same command."))
|
caller.msg(format_code("File reloaded. Staying on same command."))
|
||||||
show_curr(caller)
|
show_curr(caller)
|
||||||
|
|
||||||
|
|
||||||
class CmdStateRRR(MuxCommand):
|
class CmdStateRRR(MuxCommand):
|
||||||
"""
|
"""
|
||||||
rrr
|
rrr
|
||||||
|
|
@ -500,6 +523,7 @@ class CmdStateRRR(MuxCommand):
|
||||||
caller.msg(format_code("File reloaded. Restarting from top."))
|
caller.msg(format_code("File reloaded. Restarting from top."))
|
||||||
show_curr(caller)
|
show_curr(caller)
|
||||||
|
|
||||||
|
|
||||||
class CmdStateNN(MuxCommand):
|
class CmdStateNN(MuxCommand):
|
||||||
"""
|
"""
|
||||||
nn
|
nn
|
||||||
|
|
@ -520,6 +544,7 @@ class CmdStateNN(MuxCommand):
|
||||||
step_pointer(caller, step)
|
step_pointer(caller, step)
|
||||||
show_curr(caller)
|
show_curr(caller)
|
||||||
|
|
||||||
|
|
||||||
class CmdStateNL(MuxCommand):
|
class CmdStateNL(MuxCommand):
|
||||||
"""
|
"""
|
||||||
nl
|
nl
|
||||||
|
|
@ -541,6 +566,7 @@ class CmdStateNL(MuxCommand):
|
||||||
step_pointer(caller, step)
|
step_pointer(caller, step)
|
||||||
show_curr(caller, showall=True)
|
show_curr(caller, showall=True)
|
||||||
|
|
||||||
|
|
||||||
class CmdStateBB(MuxCommand):
|
class CmdStateBB(MuxCommand):
|
||||||
"""
|
"""
|
||||||
bb
|
bb
|
||||||
|
|
@ -562,6 +588,7 @@ class CmdStateBB(MuxCommand):
|
||||||
step_pointer(caller, step)
|
step_pointer(caller, step)
|
||||||
show_curr(caller)
|
show_curr(caller)
|
||||||
|
|
||||||
|
|
||||||
class CmdStateBL(MuxCommand):
|
class CmdStateBL(MuxCommand):
|
||||||
"""
|
"""
|
||||||
bl
|
bl
|
||||||
|
|
@ -583,6 +610,7 @@ class CmdStateBL(MuxCommand):
|
||||||
step_pointer(caller, step)
|
step_pointer(caller, step)
|
||||||
show_curr(caller, showall=True)
|
show_curr(caller, showall=True)
|
||||||
|
|
||||||
|
|
||||||
class CmdStateSS(MuxCommand):
|
class CmdStateSS(MuxCommand):
|
||||||
"""
|
"""
|
||||||
ss [steps]
|
ss [steps]
|
||||||
|
|
@ -611,6 +639,7 @@ class CmdStateSS(MuxCommand):
|
||||||
step_pointer(caller, 1)
|
step_pointer(caller, 1)
|
||||||
show_curr(caller)
|
show_curr(caller)
|
||||||
|
|
||||||
|
|
||||||
class CmdStateSL(MuxCommand):
|
class CmdStateSL(MuxCommand):
|
||||||
"""
|
"""
|
||||||
sl [steps]
|
sl [steps]
|
||||||
|
|
@ -639,6 +668,7 @@ class CmdStateSL(MuxCommand):
|
||||||
step_pointer(caller, 1)
|
step_pointer(caller, 1)
|
||||||
show_curr(caller)
|
show_curr(caller)
|
||||||
|
|
||||||
|
|
||||||
class CmdStateCC(MuxCommand):
|
class CmdStateCC(MuxCommand):
|
||||||
"""
|
"""
|
||||||
cc
|
cc
|
||||||
|
|
@ -670,6 +700,7 @@ class CmdStateCC(MuxCommand):
|
||||||
del caller.ndb.batch_batchmode
|
del caller.ndb.batch_batchmode
|
||||||
caller.msg(format_code("Finished processing batch file."))
|
caller.msg(format_code("Finished processing batch file."))
|
||||||
|
|
||||||
|
|
||||||
class CmdStateJJ(MuxCommand):
|
class CmdStateJJ(MuxCommand):
|
||||||
"""
|
"""
|
||||||
j <command number>
|
j <command number>
|
||||||
|
|
@ -684,7 +715,7 @@ class CmdStateJJ(MuxCommand):
|
||||||
caller = self.caller
|
caller = self.caller
|
||||||
arg = self.args
|
arg = self.args
|
||||||
if arg and arg.isdigit():
|
if arg and arg.isdigit():
|
||||||
number = int(self.args)-1
|
number = int(self.args) - 1
|
||||||
else:
|
else:
|
||||||
caller.msg(format_code("You must give a number index."))
|
caller.msg(format_code("You must give a number index."))
|
||||||
return
|
return
|
||||||
|
|
@ -693,6 +724,7 @@ class CmdStateJJ(MuxCommand):
|
||||||
step_pointer(caller, step)
|
step_pointer(caller, step)
|
||||||
show_curr(caller)
|
show_curr(caller)
|
||||||
|
|
||||||
|
|
||||||
class CmdStateJL(MuxCommand):
|
class CmdStateJL(MuxCommand):
|
||||||
"""
|
"""
|
||||||
jl <command number>
|
jl <command number>
|
||||||
|
|
@ -707,7 +739,7 @@ class CmdStateJL(MuxCommand):
|
||||||
caller = self.caller
|
caller = self.caller
|
||||||
arg = self.args
|
arg = self.args
|
||||||
if arg and arg.isdigit():
|
if arg and arg.isdigit():
|
||||||
number = int(self.args)-1
|
number = int(self.args) - 1
|
||||||
else:
|
else:
|
||||||
caller.msg(format_code("You must give a number index."))
|
caller.msg(format_code("You must give a number index."))
|
||||||
return
|
return
|
||||||
|
|
@ -716,6 +748,7 @@ class CmdStateJL(MuxCommand):
|
||||||
step_pointer(caller, step)
|
step_pointer(caller, step)
|
||||||
show_curr(caller, showall=True)
|
show_curr(caller, showall=True)
|
||||||
|
|
||||||
|
|
||||||
class CmdStateQQ(MuxCommand):
|
class CmdStateQQ(MuxCommand):
|
||||||
"""
|
"""
|
||||||
qq
|
qq
|
||||||
|
|
@ -730,6 +763,7 @@ class CmdStateQQ(MuxCommand):
|
||||||
purge_processor(self.caller)
|
purge_processor(self.caller)
|
||||||
self.caller.msg("Aborted interactive batch mode.")
|
self.caller.msg("Aborted interactive batch mode.")
|
||||||
|
|
||||||
|
|
||||||
class CmdStateHH(MuxCommand):
|
class CmdStateHH(MuxCommand):
|
||||||
"Help command"
|
"Help command"
|
||||||
|
|
||||||
|
|
@ -766,7 +800,6 @@ class CmdStateHH(MuxCommand):
|
||||||
self.caller.msg(string)
|
self.caller.msg(string)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
#
|
#
|
||||||
# Defining the cmdsets for the interactive batchprocessor
|
# Defining the cmdsets for the interactive batchprocessor
|
||||||
|
|
@ -781,12 +814,13 @@ class BatchSafeCmdSet(CmdSet):
|
||||||
always be available to get out of everything.
|
always be available to get out of everything.
|
||||||
"""
|
"""
|
||||||
key = "Batch_default"
|
key = "Batch_default"
|
||||||
priority = 104 # override other cmdsets.
|
priority = 104 # override other cmdsets.
|
||||||
|
|
||||||
def at_cmdset_creation(self):
|
def at_cmdset_creation(self):
|
||||||
"Init the cmdset"
|
"Init the cmdset"
|
||||||
self.add(CmdStateAbort())
|
self.add(CmdStateAbort())
|
||||||
|
|
||||||
|
|
||||||
class BatchInteractiveCmdSet(CmdSet):
|
class BatchInteractiveCmdSet(CmdSet):
|
||||||
"""
|
"""
|
||||||
The cmdset for the interactive batch processor mode.
|
The cmdset for the interactive batch processor mode.
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ except ImportError:
|
||||||
# used by @find
|
# used by @find
|
||||||
CHAR_TYPECLASS = settings.BASE_CHARACTER_TYPECLASS
|
CHAR_TYPECLASS = settings.BASE_CHARACTER_TYPECLASS
|
||||||
|
|
||||||
|
|
||||||
class ObjManipCommand(MuxCommand):
|
class ObjManipCommand(MuxCommand):
|
||||||
"""
|
"""
|
||||||
This is a parent class for some of the defining objmanip commands
|
This is a parent class for some of the defining objmanip commands
|
||||||
|
|
@ -60,7 +61,7 @@ class ObjManipCommand(MuxCommand):
|
||||||
# get all the normal parsing done (switches etc)
|
# get all the normal parsing done (switches etc)
|
||||||
super(ObjManipCommand, self).parse()
|
super(ObjManipCommand, self).parse()
|
||||||
|
|
||||||
obj_defs = ([],[]) # stores left- and right-hand side of '='
|
obj_defs = ([], []) # stores left- and right-hand side of '='
|
||||||
obj_attrs = ([], []) # "
|
obj_attrs = ([], []) # "
|
||||||
|
|
||||||
for iside, arglist in enumerate((self.lhslist, self.rhslist)):
|
for iside, arglist in enumerate((self.lhslist, self.rhslist)):
|
||||||
|
|
@ -101,7 +102,7 @@ class CmdSetObjAlias(MuxCommand):
|
||||||
by everyone.
|
by everyone.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
key = "@alias"
|
key = "@alias"
|
||||||
aliases = "@setobjalias"
|
aliases = "@setobjalias"
|
||||||
locks = "cmd:perm(setobjalias) or perm(Builders)"
|
locks = "cmd:perm(setobjalias) or perm(Builders)"
|
||||||
help_category = "Building"
|
help_category = "Building"
|
||||||
|
|
@ -121,7 +122,7 @@ class CmdSetObjAlias(MuxCommand):
|
||||||
obj = caller.search(objname)
|
obj = caller.search(objname)
|
||||||
if not obj:
|
if not obj:
|
||||||
return
|
return
|
||||||
if self.rhs == None:
|
if self.rhs is None:
|
||||||
# no =, so we just list aliases on object.
|
# no =, so we just list aliases on object.
|
||||||
aliases = obj.aliases.all()
|
aliases = obj.aliases.all()
|
||||||
if aliases:
|
if aliases:
|
||||||
|
|
@ -146,15 +147,18 @@ class CmdSetObjAlias(MuxCommand):
|
||||||
|
|
||||||
# merge the old and new aliases (if any)
|
# merge the old and new aliases (if any)
|
||||||
old_aliases = obj.aliases.all()
|
old_aliases = obj.aliases.all()
|
||||||
new_aliases = [alias.strip().lower() for alias in self.rhs.split(',') if alias.strip()]
|
new_aliases = [alias.strip().lower() for alias in self.rhs.split(',')
|
||||||
|
if alias.strip()]
|
||||||
# make the aliases only appear once
|
# make the aliases only appear once
|
||||||
old_aliases.extend(new_aliases)
|
old_aliases.extend(new_aliases)
|
||||||
aliases = list(set(old_aliases))
|
aliases = list(set(old_aliases))
|
||||||
# save back to object.
|
# save back to object.
|
||||||
obj.aliases.add(aliases)
|
obj.aliases.add(aliases)
|
||||||
# we treat this as a re-caching (relevant for exits to re-build their exit commands with the correct aliases)
|
# we treat this as a re-caching (relevant for exits to re-build their
|
||||||
|
# exit commands with the correct aliases)
|
||||||
caller.msg("Alias(es) for '%s' set to %s." % (obj.key, str(obj.aliases)))
|
caller.msg("Alias(es) for '%s' set to %s." % (obj.key, str(obj.aliases)))
|
||||||
|
|
||||||
|
|
||||||
class CmdCopy(ObjManipCommand):
|
class CmdCopy(ObjManipCommand):
|
||||||
"""
|
"""
|
||||||
@copy - copy objects
|
@copy - copy objects
|
||||||
|
|
@ -167,8 +171,8 @@ class CmdCopy(ObjManipCommand):
|
||||||
removing any changes that might have been made to the original
|
removing any changes that might have been made to the original
|
||||||
since it was first created.
|
since it was first created.
|
||||||
|
|
||||||
Create one or more copies of an object. If you don't supply any targets, one exact copy
|
Create one or more copies of an object. If you don't supply any targets,
|
||||||
of the original object will be created with the name *_copy.
|
one exact copt of the original object will be created with the name *_copy.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
key = "@copy"
|
key = "@copy"
|
||||||
|
|
@ -210,12 +214,15 @@ class CmdCopy(ObjManipCommand):
|
||||||
to_obj_aliases = objdef['aliases']
|
to_obj_aliases = objdef['aliases']
|
||||||
to_obj_location = objdef['option']
|
to_obj_location = objdef['option']
|
||||||
if to_obj_location:
|
if to_obj_location:
|
||||||
to_obj_location = caller.search(to_obj_location, global_search=True)
|
to_obj_location = caller.search(to_obj_location,
|
||||||
|
global_search=True)
|
||||||
if not to_obj_location:
|
if not to_obj_location:
|
||||||
return
|
return
|
||||||
|
|
||||||
copiedobj = ObjectDB.objects.copy_object(from_obj, new_key=to_obj_name,
|
copiedobj = ObjectDB.objects.copy_object(from_obj,
|
||||||
new_location=to_obj_location, new_aliases=to_obj_aliases)
|
new_key=to_obj_name,
|
||||||
|
new_location=to_obj_location,
|
||||||
|
new_aliases=to_obj_aliases)
|
||||||
if copiedobj:
|
if copiedobj:
|
||||||
string = "Copied %s to '%s' (aliases: %s)." % (from_obj_name, to_obj_name,
|
string = "Copied %s to '%s' (aliases: %s)." % (from_obj_name, to_obj_name,
|
||||||
to_obj_aliases)
|
to_obj_aliases)
|
||||||
|
|
@ -225,6 +232,7 @@ class CmdCopy(ObjManipCommand):
|
||||||
# we are done, echo to user
|
# we are done, echo to user
|
||||||
caller.msg(string)
|
caller.msg(string)
|
||||||
|
|
||||||
|
|
||||||
class CmdCpAttr(ObjManipCommand):
|
class CmdCpAttr(ObjManipCommand):
|
||||||
"""
|
"""
|
||||||
@cpattr - copy attributes
|
@cpattr - copy attributes
|
||||||
|
|
@ -244,8 +252,8 @@ class CmdCpAttr(ObjManipCommand):
|
||||||
copies the coolness attribute (defined on yourself), to attributes
|
copies the coolness attribute (defined on yourself), to attributes
|
||||||
on Anna and Tom.
|
on Anna and Tom.
|
||||||
|
|
||||||
Copy the attribute one object to one or more attributes on another object. If
|
Copy the attribute one object to one or more attributes on another object.
|
||||||
you don't supply a source object, yourself is used.
|
If you don't supply a source object, yourself is used.
|
||||||
"""
|
"""
|
||||||
key = "@cpattr"
|
key = "@cpattr"
|
||||||
locks = "cmd:perm(cpattr) or perm(Builders)"
|
locks = "cmd:perm(cpattr) or perm(Builders)"
|
||||||
|
|
@ -272,7 +280,8 @@ class CmdCpAttr(ObjManipCommand):
|
||||||
from_obj_attrs = lhs_objattr[0]['attrs']
|
from_obj_attrs = lhs_objattr[0]['attrs']
|
||||||
|
|
||||||
if not from_obj_attrs:
|
if not from_obj_attrs:
|
||||||
# this means the from_obj_name is actually an attribute name on self.
|
# this means the from_obj_name is actually an attribute
|
||||||
|
# name on self.
|
||||||
from_obj_attrs = [from_obj_name]
|
from_obj_attrs = [from_obj_name]
|
||||||
from_obj = self.caller
|
from_obj = self.caller
|
||||||
from_obj_name = self.caller.name
|
from_obj_name = self.caller.name
|
||||||
|
|
@ -282,7 +291,8 @@ class CmdCpAttr(ObjManipCommand):
|
||||||
caller.msg("You have to supply both source object and target(s).")
|
caller.msg("You have to supply both source object and target(s).")
|
||||||
return
|
return
|
||||||
if not from_obj.attributes.has(from_obj_attrs[0]):
|
if not from_obj.attributes.has(from_obj_attrs[0]):
|
||||||
caller.msg("%s doesn't have an attribute %s." % (from_obj_name, from_obj_attrs[0]))
|
caller.msg("%s doesn't have an attribute %s." % (from_obj_name,
|
||||||
|
from_obj_attrs[0]))
|
||||||
return
|
return
|
||||||
srcvalue = from_obj.attributes.get(from_obj_attrs[0])
|
srcvalue = from_obj.attributes.get(from_obj_attrs[0])
|
||||||
|
|
||||||
|
|
@ -291,7 +301,8 @@ class CmdCpAttr(ObjManipCommand):
|
||||||
string = "Moving "
|
string = "Moving "
|
||||||
else:
|
else:
|
||||||
string = "Copying "
|
string = "Copying "
|
||||||
string += "%s/%s (with value %s) ..." % (from_obj_name, from_obj_attrs[0], srcvalue)
|
string += "%s/%s (with value %s) ..." % (from_obj_name,
|
||||||
|
from_obj_attrs[0], srcvalue)
|
||||||
|
|
||||||
for to_obj in to_objs:
|
for to_obj in to_objs:
|
||||||
to_obj_name = to_obj['name']
|
to_obj_name = to_obj['name']
|
||||||
|
|
@ -308,15 +319,20 @@ class CmdCpAttr(ObjManipCommand):
|
||||||
# on the to_obj, we copy the original name instead.
|
# on the to_obj, we copy the original name instead.
|
||||||
to_attr = from_attr
|
to_attr = from_attr
|
||||||
to_obj.attributes.add(to_attr, srcvalue)
|
to_obj.attributes.add(to_attr, srcvalue)
|
||||||
if "move" in self.switches and not (from_obj == to_obj and from_attr == to_attr):
|
if ("move" in self.switches and not (from_obj == to_obj and
|
||||||
|
from_attr == to_attr)):
|
||||||
from_obj.del_attribute(from_attr)
|
from_obj.del_attribute(from_attr)
|
||||||
string += "\nMoved %s.%s -> %s.%s." % (from_obj.name, from_attr,
|
string += "\nMoved %s.%s -> %s.%s." % (from_obj.name,
|
||||||
|
from_attr,
|
||||||
to_obj_name, to_attr)
|
to_obj_name, to_attr)
|
||||||
else:
|
else:
|
||||||
string += "\nCopied %s.%s -> %s.%s." % (from_obj.name, from_attr,
|
string += "\nCopied %s.%s -> %s.%s." % (from_obj.name,
|
||||||
to_obj_name, to_attr)
|
from_attr,
|
||||||
|
to_obj_name,
|
||||||
|
to_attr)
|
||||||
caller.msg(string)
|
caller.msg(string)
|
||||||
|
|
||||||
|
|
||||||
class CmdMvAttr(ObjManipCommand):
|
class CmdMvAttr(ObjManipCommand):
|
||||||
"""
|
"""
|
||||||
@mvattr - move attributes
|
@mvattr - move attributes
|
||||||
|
|
@ -330,8 +346,8 @@ class CmdMvAttr(ObjManipCommand):
|
||||||
Switches:
|
Switches:
|
||||||
copy - Don't delete the original after moving.
|
copy - Don't delete the original after moving.
|
||||||
|
|
||||||
Move an attribute from one object to one or more attributes on another object. If
|
Move an attribute from one object to one or more attributes on another
|
||||||
you don't supply a source object, yourself is used.
|
object. If you don't supply a source object, yourself is used.
|
||||||
"""
|
"""
|
||||||
key = "@mvattr"
|
key = "@mvattr"
|
||||||
locks = "cmd:perm(mvattr) or perm(Builders)"
|
locks = "cmd:perm(mvattr) or perm(Builders)"
|
||||||
|
|
@ -356,6 +372,7 @@ class CmdMvAttr(ObjManipCommand):
|
||||||
else:
|
else:
|
||||||
self.caller.execute_cmd("@cpattr/move %s" % self.args)
|
self.caller.execute_cmd("@cpattr/move %s" % self.args)
|
||||||
|
|
||||||
|
|
||||||
class CmdCreate(ObjManipCommand):
|
class CmdCreate(ObjManipCommand):
|
||||||
"""
|
"""
|
||||||
@create - create new objects
|
@create - create new objects
|
||||||
|
|
@ -364,8 +381,9 @@ class CmdCreate(ObjManipCommand):
|
||||||
@create[/drop] objname[;alias;alias...][:typeclass], objname...
|
@create[/drop] objname[;alias;alias...][:typeclass], objname...
|
||||||
|
|
||||||
switch:
|
switch:
|
||||||
drop - automatically drop the new object into your current location (this is not echoed)
|
drop - automatically drop the new object into your current
|
||||||
this also sets the new object's home to the current location rather than to you.
|
location (this is not echoed). This also sets the new
|
||||||
|
object's home to the current location rather than to you.
|
||||||
|
|
||||||
Creates one or more new objects. If typeclass is given, the object
|
Creates one or more new objects. If typeclass is given, the object
|
||||||
is created as a child of this typeclass. The typeclass script is
|
is created as a child of this typeclass. The typeclass script is
|
||||||
|
|
@ -406,7 +424,8 @@ class CmdCreate(ObjManipCommand):
|
||||||
# object typeclass will automatically be used)
|
# object typeclass will automatically be used)
|
||||||
lockstring = "control: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,
|
obj = create.create_object(typeclass, name, caller,
|
||||||
home=caller, aliases=aliases, locks=lockstring, report_to=caller)
|
home=caller, aliases=aliases,
|
||||||
|
locks=lockstring, report_to=caller)
|
||||||
if not obj:
|
if not obj:
|
||||||
continue
|
continue
|
||||||
if aliases:
|
if aliases:
|
||||||
|
|
@ -423,7 +442,8 @@ class CmdCreate(ObjManipCommand):
|
||||||
obj.home = caller.location
|
obj.home = caller.location
|
||||||
obj.move_to(caller.location, quiet=True)
|
obj.move_to(caller.location, quiet=True)
|
||||||
if string:
|
if string:
|
||||||
caller.msg(string)
|
caller.msg(string)
|
||||||
|
|
||||||
|
|
||||||
class CmdDesc(MuxCommand):
|
class CmdDesc(MuxCommand):
|
||||||
"""
|
"""
|
||||||
|
|
@ -471,8 +491,8 @@ class CmdDestroy(MuxCommand):
|
||||||
@destroy[/switches] [obj, obj2, obj3, [dbref-dbref], ...]
|
@destroy[/switches] [obj, obj2, obj3, [dbref-dbref], ...]
|
||||||
|
|
||||||
switches:
|
switches:
|
||||||
override - The @destroy command will usually avoid accidentally destroying
|
override - The @destroy command will usually avoid accidentally
|
||||||
player objects. This switch overrides this safety.
|
destroying player objects. This switch overrides this safety.
|
||||||
examples:
|
examples:
|
||||||
@destroy house, roof, door, 44-78
|
@destroy house, roof, door, 44-78
|
||||||
@destroy 5-10, flower, 45
|
@destroy 5-10, flower, 45
|
||||||
|
|
@ -502,7 +522,8 @@ class CmdDestroy(MuxCommand):
|
||||||
if not obj:
|
if not obj:
|
||||||
self.caller.msg(" (Objects to destroy must either be local or specified with a unique #dbref.)")
|
self.caller.msg(" (Objects to destroy must either be local or specified with a unique #dbref.)")
|
||||||
return ""
|
return ""
|
||||||
if not "override" in self.switches and obj.dbid == int(settings.CHARACTER_DEFAULT_HOME.lstrip("#")):
|
if (not "override" in self.switches and
|
||||||
|
obj.dbid == int(settings.CHARACTER_DEFAULT_HOME.lstrip("#"))):
|
||||||
return "\nYou are trying to delete CHARACTER_DEFAULT_HOME. If you want to do this, use the /override switch."
|
return "\nYou are trying to delete CHARACTER_DEFAULT_HOME. If you want to do this, use the /override switch."
|
||||||
objname = obj.name
|
objname = obj.name
|
||||||
if not obj.access(caller, 'delete'):
|
if not obj.access(caller, 'delete'):
|
||||||
|
|
@ -529,9 +550,10 @@ class CmdDestroy(MuxCommand):
|
||||||
for objname in self.lhslist:
|
for objname in self.lhslist:
|
||||||
if '-' in objname:
|
if '-' in objname:
|
||||||
# might be a range of dbrefs
|
# might be a range of dbrefs
|
||||||
dmin, dmax = [utils.dbref(part, reqhash=False) for part in objname.split('-', 1)]
|
dmin, dmax = [utils.dbref(part, reqhash=False)
|
||||||
|
for part in objname.split('-', 1)]
|
||||||
if dmin and dmax:
|
if dmin and dmax:
|
||||||
for dbref in range(int(dmin),int(dmax+1)):
|
for dbref in range(int(dmin), int(dmax + 1)):
|
||||||
string += delobj("#" + str(dbref), True)
|
string += delobj("#" + str(dbref), True)
|
||||||
else:
|
else:
|
||||||
string += delobj(objname)
|
string += delobj(objname)
|
||||||
|
|
@ -558,9 +580,11 @@ class CmdDig(ObjManipCommand):
|
||||||
@dig house:myrooms.MyHouseTypeclass
|
@dig house:myrooms.MyHouseTypeclass
|
||||||
@dig sheer cliff;cliff;sheer = climb up, climb down
|
@dig sheer cliff;cliff;sheer = climb up, climb down
|
||||||
|
|
||||||
This command is a convenient way to build rooms quickly; it creates the new room and you can optionally
|
This command is a convenient way to build rooms quickly; it creates the
|
||||||
set up exits back and forth between your current room and the new one. You can add as many aliases as you
|
new room and you can optionally set up exits back and forth between your
|
||||||
like to the name of the room and the exits in question; an example would be 'north;no;n'.
|
current room and the new one. You can add as many aliases as you
|
||||||
|
like to the name of the room and the exits in question; an example
|
||||||
|
would be 'north;no;n'.
|
||||||
"""
|
"""
|
||||||
key = "@dig"
|
key = "@dig"
|
||||||
locks = "cmd:perm(dig) or perm(Builders)"
|
locks = "cmd:perm(dig) or perm(Builders)"
|
||||||
|
|
@ -595,13 +619,14 @@ class CmdDig(ObjManipCommand):
|
||||||
lockstring = lockstring % (caller.dbref, caller.dbref, caller.dbref)
|
lockstring = lockstring % (caller.dbref, caller.dbref, caller.dbref)
|
||||||
|
|
||||||
new_room = create.create_object(typeclass, room["name"],
|
new_room = create.create_object(typeclass, room["name"],
|
||||||
aliases=room["aliases"], report_to=caller)
|
aliases=room["aliases"],
|
||||||
|
report_to=caller)
|
||||||
new_room.locks.add(lockstring)
|
new_room.locks.add(lockstring)
|
||||||
alias_string = ""
|
alias_string = ""
|
||||||
if new_room.aliases.all():
|
if new_room.aliases.all():
|
||||||
alias_string = " (%s)" % ", ".join(new_room.aliases.all())
|
alias_string = " (%s)" % ", ".join(new_room.aliases.all())
|
||||||
room_string = "Created room %s(%s)%s of type %s." % (new_room, new_room.dbref, alias_string, typeclass)
|
room_string = "Created room %s(%s)%s of type %s." % (new_room,
|
||||||
|
new_room.dbref, alias_string, typeclass)
|
||||||
|
|
||||||
# create exit to room
|
# create exit to room
|
||||||
|
|
||||||
|
|
@ -622,15 +647,21 @@ class CmdDig(ObjManipCommand):
|
||||||
if not typeclass:
|
if not typeclass:
|
||||||
typeclass = settings.BASE_EXIT_TYPECLASS
|
typeclass = settings.BASE_EXIT_TYPECLASS
|
||||||
|
|
||||||
new_to_exit = create.create_object(typeclass, to_exit["name"], location,
|
new_to_exit = create.create_object(typeclass, to_exit["name"],
|
||||||
|
location,
|
||||||
aliases=to_exit["aliases"],
|
aliases=to_exit["aliases"],
|
||||||
locks=lockstring, destination=new_room, report_to=caller)
|
locks=lockstring,
|
||||||
|
destination=new_room,
|
||||||
|
report_to=caller)
|
||||||
alias_string = ""
|
alias_string = ""
|
||||||
if new_to_exit.aliases.all():
|
if new_to_exit.aliases.all():
|
||||||
alias_string = " (%s)" % ", ".join(new_to_exit.aliases.all())
|
alias_string = " (%s)" % ", ".join(new_to_exit.aliases.all())
|
||||||
exit_to_string = "\nCreated Exit from %s to %s: %s(%s)%s."
|
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,
|
exit_to_string = exit_to_string % (location.name,
|
||||||
new_to_exit.dbref, alias_string)
|
new_room.name,
|
||||||
|
new_to_exit,
|
||||||
|
new_to_exit.dbref,
|
||||||
|
alias_string)
|
||||||
|
|
||||||
# Create exit back from new room
|
# Create exit back from new room
|
||||||
|
|
||||||
|
|
@ -647,15 +678,22 @@ class CmdDig(ObjManipCommand):
|
||||||
typeclass = back_exit["option"]
|
typeclass = back_exit["option"]
|
||||||
if not typeclass:
|
if not typeclass:
|
||||||
typeclass = settings.BASE_EXIT_TYPECLASS
|
typeclass = settings.BASE_EXIT_TYPECLASS
|
||||||
new_back_exit = create.create_object(typeclass, back_exit["name"],
|
new_back_exit = create.create_object(typeclass,
|
||||||
new_room, aliases=back_exit["aliases"],
|
back_exit["name"],
|
||||||
locks=lockstring, destination=location, report_to=caller)
|
new_room,
|
||||||
|
aliases=back_exit["aliases"],
|
||||||
|
locks=lockstring,
|
||||||
|
destination=location,
|
||||||
|
report_to=caller)
|
||||||
alias_string = ""
|
alias_string = ""
|
||||||
if new_back_exit.aliases.all():
|
if new_back_exit.aliases.all():
|
||||||
alias_string = " (%s)" % ", ".join(new_back_exit.aliases.all())
|
alias_string = " (%s)" % ", ".join(new_back_exit.aliases.all())
|
||||||
exit_back_string = "\nCreated Exit back from %s to %s: %s(%s)%s."
|
exit_back_string = "\nCreated Exit back from %s to %s: %s(%s)%s."
|
||||||
exit_back_string = exit_back_string % (new_room.name, location.name,
|
exit_back_string = exit_back_string % (new_room.name,
|
||||||
new_back_exit, new_back_exit.dbref, alias_string)
|
location.name,
|
||||||
|
new_back_exit,
|
||||||
|
new_back_exit.dbref,
|
||||||
|
alias_string)
|
||||||
caller.msg("%s%s%s" % (room_string, exit_to_string, exit_back_string))
|
caller.msg("%s%s%s" % (room_string, exit_to_string, exit_back_string))
|
||||||
if new_room and ('teleport' in self.switches or "tel" in self.switches):
|
if new_room and ('teleport' in self.switches or "tel" in self.switches):
|
||||||
caller.move_to(new_room)
|
caller.move_to(new_room)
|
||||||
|
|
@ -693,18 +731,18 @@ class CmdTunnel(MuxCommand):
|
||||||
help_category = "Building"
|
help_category = "Building"
|
||||||
|
|
||||||
# store the direction, full name and its opposite
|
# store the direction, full name and its opposite
|
||||||
directions = {"n" : ("north", "s"),
|
directions = {"n": ("north", "s"),
|
||||||
"ne": ("northeast", "sw"),
|
"ne": ("northeast", "sw"),
|
||||||
"e" : ("east", "w"),
|
"e": ("east", "w"),
|
||||||
"se": ("southeast", "nw"),
|
"se": ("southeast", "nw"),
|
||||||
"s" : ("south", "n"),
|
"s": ("south", "n"),
|
||||||
"sw": ("southwest", "ne"),
|
"sw": ("southwest", "ne"),
|
||||||
"w" : ("west", "e"),
|
"w": ("west", "e"),
|
||||||
"nw": ("northwest", "se"),
|
"nw": ("northwest", "se"),
|
||||||
"u" : ("up", "d"),
|
"u": ("up", "d"),
|
||||||
"d" : ("down", "u"),
|
"d": ("down", "u"),
|
||||||
"i" : ("in", "o"),
|
"i": ("in", "o"),
|
||||||
"o" : ("out", "i")}
|
"o": ("out", "i")}
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"Implements the tunnel command"
|
"Implements the tunnel command"
|
||||||
|
|
@ -725,7 +763,7 @@ class CmdTunnel(MuxCommand):
|
||||||
|
|
||||||
roomname = "Some place"
|
roomname = "Some place"
|
||||||
if self.rhs:
|
if self.rhs:
|
||||||
roomname = self.rhs # this may include aliases; that's fine.
|
roomname = self.rhs # this may include aliases; that's fine.
|
||||||
|
|
||||||
telswitch = ""
|
telswitch = ""
|
||||||
if "tel" in self.switches:
|
if "tel" in self.switches:
|
||||||
|
|
@ -735,9 +773,11 @@ class CmdTunnel(MuxCommand):
|
||||||
backstring = ", %s;%s" % (backname, backshort)
|
backstring = ", %s;%s" % (backname, backshort)
|
||||||
|
|
||||||
# build the string we will use to call @dig
|
# build the string we will use to call @dig
|
||||||
digstring = "@dig%s %s = %s;%s%s" % (telswitch, roomname, exitname, exitshort, backstring)
|
digstring = "@dig%s %s = %s;%s%s" % (telswitch, roomname,
|
||||||
|
exitname, exitshort, backstring)
|
||||||
self.caller.execute_cmd(digstring)
|
self.caller.execute_cmd(digstring)
|
||||||
|
|
||||||
|
|
||||||
class CmdLink(MuxCommand):
|
class CmdLink(MuxCommand):
|
||||||
"""
|
"""
|
||||||
@link - connect objects
|
@link - connect objects
|
||||||
|
|
@ -754,8 +794,9 @@ class CmdLink(MuxCommand):
|
||||||
If <object> is an exit, set its destination to <target>. Two-way operation
|
If <object> is an exit, set its destination to <target>. Two-way operation
|
||||||
instead sets the destination to the *locations* of the respective given
|
instead sets the destination to the *locations* of the respective given
|
||||||
arguments.
|
arguments.
|
||||||
The second form (a lone =) sets the destination to None (same as the @unlink command)
|
The second form (a lone =) sets the destination to None (same as
|
||||||
and the third form (without =) just shows the currently set destination.
|
the @unlink command) and the third form (without =) just shows the
|
||||||
|
currently set destination.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
key = "@link"
|
key = "@link"
|
||||||
|
|
@ -802,7 +843,7 @@ class CmdLink(MuxCommand):
|
||||||
obj.destination = target
|
obj.destination = target
|
||||||
string += "\nLink created %s -> %s (one way)." % (obj.name, target)
|
string += "\nLink created %s -> %s (one way)." % (obj.name, target)
|
||||||
|
|
||||||
elif self.rhs == None:
|
elif self.rhs is None:
|
||||||
# this means that no = was given (otherwise rhs
|
# this means that no = was given (otherwise rhs
|
||||||
# would have been an empty string). So we inspect
|
# would have been an empty string). So we inspect
|
||||||
# the home/destination on object
|
# the home/destination on object
|
||||||
|
|
@ -823,6 +864,7 @@ class CmdLink(MuxCommand):
|
||||||
# give feedback
|
# give feedback
|
||||||
caller.msg(string.strip())
|
caller.msg(string.strip())
|
||||||
|
|
||||||
|
|
||||||
class CmdUnLink(CmdLink):
|
class CmdUnLink(CmdLink):
|
||||||
"""
|
"""
|
||||||
@unlink - unconnect objects
|
@unlink - unconnect objects
|
||||||
|
|
@ -857,6 +899,7 @@ class CmdUnLink(CmdLink):
|
||||||
# call the @link functionality
|
# call the @link functionality
|
||||||
super(CmdUnLink, self).func()
|
super(CmdUnLink, self).func()
|
||||||
|
|
||||||
|
|
||||||
class CmdSetHome(CmdLink):
|
class CmdSetHome(CmdLink):
|
||||||
"""
|
"""
|
||||||
@home - control an object's home location
|
@home - control an object's home location
|
||||||
|
|
@ -893,7 +936,8 @@ class CmdSetHome(CmdLink):
|
||||||
if not home:
|
if not home:
|
||||||
string = "This object has no home location set!"
|
string = "This object has no home location set!"
|
||||||
else:
|
else:
|
||||||
string = "%s's current home is %s(%s)." % (obj, home, home.dbref)
|
string = "%s's current home is %s(%s)." % (obj, home,
|
||||||
|
home.dbref)
|
||||||
else:
|
else:
|
||||||
# set a home location
|
# set a home location
|
||||||
new_home = self.caller.search(self.rhs, global_search=True)
|
new_home = self.caller.search(self.rhs, global_search=True)
|
||||||
|
|
@ -907,6 +951,7 @@ class CmdSetHome(CmdLink):
|
||||||
string = "%s' home location was set to %s(%s)." % (obj, new_home, new_home.dbref)
|
string = "%s' home location was set to %s(%s)." % (obj, new_home, new_home.dbref)
|
||||||
self.caller.msg(string)
|
self.caller.msg(string)
|
||||||
|
|
||||||
|
|
||||||
class CmdListCmdSets(MuxCommand):
|
class CmdListCmdSets(MuxCommand):
|
||||||
"""
|
"""
|
||||||
list command sets on an object
|
list command sets on an object
|
||||||
|
|
@ -935,6 +980,7 @@ class CmdListCmdSets(MuxCommand):
|
||||||
string = "%s" % obj.cmdset
|
string = "%s" % obj.cmdset
|
||||||
caller.msg(string)
|
caller.msg(string)
|
||||||
|
|
||||||
|
|
||||||
class CmdName(ObjManipCommand):
|
class CmdName(ObjManipCommand):
|
||||||
"""
|
"""
|
||||||
cname - change the name and/or aliases of an object
|
cname - change the name and/or aliases of an object
|
||||||
|
|
@ -1006,7 +1052,8 @@ class CmdOpen(ObjManipCommand):
|
||||||
help_category = "Building"
|
help_category = "Building"
|
||||||
|
|
||||||
# a custom member method to chug out exits and do checks
|
# a custom member method to chug out exits and do checks
|
||||||
def create_exit(self, exit_name, location, destination, exit_aliases=None, typeclass=None):
|
def create_exit(self, exit_name, location, destination,
|
||||||
|
exit_aliases=None, typeclass=None):
|
||||||
"""
|
"""
|
||||||
Helper function to avoid code duplication.
|
Helper function to avoid code duplication.
|
||||||
At this point we know destination is a valid location
|
At this point we know destination is a valid location
|
||||||
|
|
@ -1047,9 +1094,11 @@ class CmdOpen(ObjManipCommand):
|
||||||
# exit does not exist before. Create a new one.
|
# exit does not exist before. Create a new one.
|
||||||
if not typeclass:
|
if not typeclass:
|
||||||
typeclass = settings.BASE_EXIT_TYPECLASS
|
typeclass = settings.BASE_EXIT_TYPECLASS
|
||||||
exit_obj = create.create_object(typeclass, key=exit_name,
|
exit_obj = create.create_object(typeclass,
|
||||||
|
key=exit_name,
|
||||||
location=location,
|
location=location,
|
||||||
aliases=exit_aliases, report_to=caller)
|
aliases=exit_aliases,
|
||||||
|
report_to=caller)
|
||||||
if exit_obj:
|
if exit_obj:
|
||||||
# storing a destination is what makes it an exit!
|
# storing a destination is what makes it an exit!
|
||||||
exit_obj.destination = destination
|
exit_obj.destination = destination
|
||||||
|
|
@ -1095,7 +1144,11 @@ class CmdOpen(ObjManipCommand):
|
||||||
return
|
return
|
||||||
|
|
||||||
# Create exit
|
# Create exit
|
||||||
ok = self.create_exit(exit_name, location, destination, exit_aliases, exit_typeclass)
|
ok = self.create_exit(exit_name,
|
||||||
|
location,
|
||||||
|
destination,
|
||||||
|
exit_aliases,
|
||||||
|
exit_typeclass)
|
||||||
if not ok:
|
if not ok:
|
||||||
# an error; the exit was not created, so we quit.
|
# an error; the exit was not created, so we quit.
|
||||||
return
|
return
|
||||||
|
|
@ -1104,7 +1157,11 @@ class CmdOpen(ObjManipCommand):
|
||||||
back_exit_name = self.lhs_objs[1]['name']
|
back_exit_name = self.lhs_objs[1]['name']
|
||||||
back_exit_aliases = self.lhs_objs[1]['aliases']
|
back_exit_aliases = self.lhs_objs[1]['aliases']
|
||||||
back_exit_typeclass = self.lhs_objs[1]['option']
|
back_exit_typeclass = self.lhs_objs[1]['option']
|
||||||
ok = self.create_exit(back_exit_name, destination, location, back_exit_aliases, back_exit_typeclass)
|
ok = self.create_exit(back_exit_name,
|
||||||
|
destination,
|
||||||
|
location,
|
||||||
|
back_exit_aliases,
|
||||||
|
back_exit_typeclass)
|
||||||
|
|
||||||
|
|
||||||
class CmdSetAttribute(ObjManipCommand):
|
class CmdSetAttribute(ObjManipCommand):
|
||||||
|
|
@ -1126,7 +1183,8 @@ class CmdSetAttribute(ObjManipCommand):
|
||||||
numbers. You can however also set Python primities such as lists,
|
numbers. You can however also set Python primities such as lists,
|
||||||
dictionaries and tuples on objects (this might be important for
|
dictionaries and tuples on objects (this might be important for
|
||||||
the functionality of certain custom objects). This is indicated
|
the functionality of certain custom objects). This is indicated
|
||||||
by you starting your value with one of {c'{n, {c"{n, {c({n, {c[{n or {c{ {n.
|
by you starting your value with one of {c'{n, {c"{n, {c({n, {c[{n
|
||||||
|
or {c{ {n.
|
||||||
Note that you should leave a space after starting a dictionary ('{ ')
|
Note that you should leave a space after starting a dictionary ('{ ')
|
||||||
so as to not confuse the dictionary start with a colour code like \{g.
|
so as to not confuse the dictionary start with a colour code like \{g.
|
||||||
Remember that if you use Python primitives like this, you must
|
Remember that if you use Python primitives like this, you must
|
||||||
|
|
@ -1169,10 +1227,14 @@ class CmdSetAttribute(ObjManipCommand):
|
||||||
used for Python <=2.5. After that literal_eval is available.
|
used for Python <=2.5. After that literal_eval is available.
|
||||||
"""
|
"""
|
||||||
# simple types
|
# simple types
|
||||||
try: return int(obj)
|
try:
|
||||||
except ValueError: pass
|
return int(obj)
|
||||||
try: return float(obj)
|
except ValueError:
|
||||||
except ValueError: pass
|
pass
|
||||||
|
try:
|
||||||
|
return float(obj)
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
# iterables
|
# iterables
|
||||||
if obj.startswith('[') and obj.endswith(']'):
|
if obj.startswith('[') and obj.endswith(']'):
|
||||||
"A list. Traverse recursively."
|
"A list. Traverse recursively."
|
||||||
|
|
@ -1182,7 +1244,8 @@ class CmdSetAttribute(ObjManipCommand):
|
||||||
return tuple([rec_convert(val) for val in obj[1:-1].split(',')])
|
return tuple([rec_convert(val) for val in obj[1:-1].split(',')])
|
||||||
if obj.startswith('{') and obj.endswith('}') and ':' in obj:
|
if obj.startswith('{') and obj.endswith('}') and ':' in obj:
|
||||||
"A dict. Traverse recursively."
|
"A dict. Traverse recursively."
|
||||||
return dict([(rec_convert(pair.split(":",1)[0]), rec_convert(pair.split(":",1)[1]))
|
return dict([(rec_convert(pair.split(":", 1)[0]),
|
||||||
|
rec_convert(pair.split(":", 1)[1]))
|
||||||
for pair in obj[1:-1].split(',') if ":" in pair])
|
for pair in obj[1:-1].split(',') if ":" in pair])
|
||||||
# if nothing matches, return as-is
|
# if nothing matches, return as-is
|
||||||
return obj
|
return obj
|
||||||
|
|
@ -1198,7 +1261,8 @@ class CmdSetAttribute(ObjManipCommand):
|
||||||
self.caller.msg(string)
|
self.caller.msg(string)
|
||||||
return utils.to_str(strobj)
|
return utils.to_str(strobj)
|
||||||
else:
|
else:
|
||||||
# fall back to old recursive solution (does not support nested lists/dicts)
|
# fall back to old recursive solution (does not support
|
||||||
|
# nested lists/dicts)
|
||||||
return rec_convert(strobj.strip())
|
return rec_convert(strobj.strip())
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
|
|
@ -1223,17 +1287,18 @@ class CmdSetAttribute(ObjManipCommand):
|
||||||
|
|
||||||
string = ""
|
string = ""
|
||||||
if not value:
|
if not value:
|
||||||
if self.rhs == None:
|
if self.rhs is None:
|
||||||
# no = means we inspect the attribute(s)
|
# no = means we inspect the attribute(s)
|
||||||
if not attrs:
|
if not attrs:
|
||||||
attrs = [attr.key for attr in obj.get_all_attributes()]
|
attrs = [attr.key for attr in obj.get_all_attributes()]
|
||||||
for attr in attrs:
|
for attr in attrs:
|
||||||
if obj.attributes.has(attr):
|
if obj.attributes.has(attr):
|
||||||
string += "\nAttribute %s/%s = %s" % (obj.name, attr, obj.attributes.get(attr))
|
string += "\nAttribute %s/%s = %s" % (obj.name, attr,
|
||||||
|
obj.attributes.get(attr))
|
||||||
else:
|
else:
|
||||||
string += "\n%s has no attribute '%s'." % (obj.name, attr)
|
string += "\n%s has no attribute '%s'." % (obj.name, attr)
|
||||||
# we view it without parsing markup.
|
# we view it without parsing markup.
|
||||||
self.caller.msg(string.strip(), data={"raw":True})
|
self.caller.msg(string.strip(), data={"raw": True})
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
# deleting the attribute(s)
|
# deleting the attribute(s)
|
||||||
|
|
@ -1252,9 +1317,11 @@ class CmdSetAttribute(ObjManipCommand):
|
||||||
string += "\nCreated attribute %s/%s = %s" % (obj.name, attr, value)
|
string += "\nCreated attribute %s/%s = %s" % (obj.name, attr, value)
|
||||||
except SyntaxError:
|
except SyntaxError:
|
||||||
# this means literal_eval tried to parse a faulty string
|
# this means literal_eval tried to parse a faulty string
|
||||||
string = "{RCritical Python syntax error in your value. Only primitive Python structures"
|
string = "{RCritical Python syntax error in your value. "
|
||||||
string += "\nare allowed. You also need to use correct Python syntax. Remember especially"
|
string += "Only primitive Python structures are allowed. "
|
||||||
string += "\nto put quotes around all strings inside lists and dicts.{n"
|
string += "\nYou also need to use correct Python syntax. "
|
||||||
|
string += "Remember especially to put quotes around all "
|
||||||
|
string += "strings inside lists and dicts.{n"
|
||||||
# send feedback
|
# send feedback
|
||||||
caller.msg(string.strip('\n'))
|
caller.msg(string.strip('\n'))
|
||||||
|
|
||||||
|
|
@ -1314,7 +1381,8 @@ class CmdTypeclass(MuxCommand):
|
||||||
# we did not supply a new typeclass, view the
|
# we did not supply a new typeclass, view the
|
||||||
# current one instead.
|
# current one instead.
|
||||||
if hasattr(obj, "typeclass"):
|
if hasattr(obj, "typeclass"):
|
||||||
string = "%s's current typeclass is '%s' (%s)." % (obj.name, obj.typeclass.typename, obj.typeclass.path)
|
string = "%s's current typeclass is '%s' (%s)." % (obj.name,
|
||||||
|
obj.typeclass.typename, obj.typeclass.path)
|
||||||
else:
|
else:
|
||||||
string = "%s is not a typed object." % obj.name
|
string = "%s is not a typed object." % obj.name
|
||||||
caller.msg(string)
|
caller.msg(string)
|
||||||
|
|
@ -1343,8 +1411,8 @@ class CmdTypeclass(MuxCommand):
|
||||||
string = "%s updated its existing typeclass (%s).\n" % (obj.name, obj.typeclass.path)
|
string = "%s updated its existing typeclass (%s).\n" % (obj.name, obj.typeclass.path)
|
||||||
else:
|
else:
|
||||||
string = "%s changed typeclass from %s to %s.\n" % (obj.name,
|
string = "%s changed typeclass from %s to %s.\n" % (obj.name,
|
||||||
old_typeclass_path,
|
old_typeclass_path,
|
||||||
obj.typeclass_path)
|
obj.typeclass_path)
|
||||||
string += "Creation hooks were run."
|
string += "Creation hooks were run."
|
||||||
if reset:
|
if reset:
|
||||||
string += " All old attributes where deleted before the swap."
|
string += " All old attributes where deleted before the swap."
|
||||||
|
|
@ -1354,8 +1422,8 @@ class CmdTypeclass(MuxCommand):
|
||||||
else:
|
else:
|
||||||
string = obj.typeclass_last_errmsg
|
string = obj.typeclass_last_errmsg
|
||||||
string += "\nCould not swap '%s' (%s) to typeclass '%s'." % (obj.name,
|
string += "\nCould not swap '%s' (%s) to typeclass '%s'." % (obj.name,
|
||||||
old_typeclass_path,
|
old_typeclass_path,
|
||||||
typeclass)
|
typeclass)
|
||||||
caller.msg(string)
|
caller.msg(string)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1410,6 +1478,7 @@ class CmdWipe(ObjManipCommand):
|
||||||
string = string % (",".join(attrs), obj.name)
|
string = string % (",".join(attrs), obj.name)
|
||||||
caller.msg(string)
|
caller.msg(string)
|
||||||
|
|
||||||
|
|
||||||
class CmdLock(ObjManipCommand):
|
class CmdLock(ObjManipCommand):
|
||||||
"""
|
"""
|
||||||
lock - assign a lock definition to an object
|
lock - assign a lock definition to an object
|
||||||
|
|
@ -1493,6 +1562,7 @@ class CmdLock(ObjManipCommand):
|
||||||
return
|
return
|
||||||
caller.msg(obj.locks)
|
caller.msg(obj.locks)
|
||||||
|
|
||||||
|
|
||||||
class CmdExamine(ObjManipCommand):
|
class CmdExamine(ObjManipCommand):
|
||||||
"""
|
"""
|
||||||
examine - detailed info on objects
|
examine - detailed info on objects
|
||||||
|
|
@ -1545,7 +1615,9 @@ class CmdExamine(ObjManipCommand):
|
||||||
else:
|
else:
|
||||||
db_attr = [(attr.key, attr.value) for attr in obj.db_attributes.all()]
|
db_attr = [(attr.key, attr.value) for attr in obj.db_attributes.all()]
|
||||||
try:
|
try:
|
||||||
ndb_attr = [(aname, avalue) for aname, avalue in obj.ndb.__dict__.items() if not aname.startswith("_")]
|
ndb_attr = [(aname, avalue)
|
||||||
|
for aname, avalue in obj.ndb.__dict__.items()
|
||||||
|
if not aname.startswith("_")]
|
||||||
except Exception:
|
except Exception:
|
||||||
ndb_attr = None
|
ndb_attr = None
|
||||||
string = ""
|
string = ""
|
||||||
|
|
@ -1572,7 +1644,8 @@ class CmdExamine(ObjManipCommand):
|
||||||
if hasattr(obj, "sessid") and obj.sessid:
|
if hasattr(obj, "sessid") and obj.sessid:
|
||||||
string += "\n{wsession{n: %s" % obj.sessid
|
string += "\n{wsession{n: %s" % obj.sessid
|
||||||
elif hasattr(obj, "sessions") and obj.sessions:
|
elif hasattr(obj, "sessions") and obj.sessions:
|
||||||
string += "\n{wsession(s){n: %s" % (", ".join(str(sess.sessid) for sess in obj.sessions))
|
string += "\n{wsession(s){n: %s" % (", ".join(str(sess.sessid)
|
||||||
|
for sess in obj.sessions))
|
||||||
if hasattr(obj, "has_player") and obj.has_player:
|
if hasattr(obj, "has_player") and obj.has_player:
|
||||||
string += "\n{wPlayer{n: {c%s{n" % obj.player.name
|
string += "\n{wPlayer{n: {c%s{n" % obj.player.name
|
||||||
perms = obj.player.permissions.all()
|
perms = obj.player.permissions.all()
|
||||||
|
|
@ -1581,7 +1654,8 @@ class CmdExamine(ObjManipCommand):
|
||||||
elif not perms:
|
elif not perms:
|
||||||
perms = ["<None>"]
|
perms = ["<None>"]
|
||||||
string += "\n{wPlayer Perms{n: %s" % (", ".join(perms))
|
string += "\n{wPlayer Perms{n: %s" % (", ".join(perms))
|
||||||
string += "\n{wTypeclass{n: %s (%s)" % (obj.typeclass.typename, obj.typeclass_path)
|
string += "\n{wTypeclass{n: %s (%s)" % (obj.typeclass.typename,
|
||||||
|
obj.typeclass_path)
|
||||||
if hasattr(obj, "location"):
|
if hasattr(obj, "location"):
|
||||||
string += "\n{wLocation{n: %s" % obj.location
|
string += "\n{wLocation{n: %s" % obj.location
|
||||||
if obj.location:
|
if obj.location:
|
||||||
|
|
@ -1610,14 +1684,20 @@ class CmdExamine(ObjManipCommand):
|
||||||
|
|
||||||
if not (len(obj.cmdset.all()) == 1 and obj.cmdset.current.key == "Empty"):
|
if not (len(obj.cmdset.all()) == 1 and obj.cmdset.current.key == "Empty"):
|
||||||
# list the current cmdsets
|
# list the current cmdsets
|
||||||
all_cmdsets = obj.cmdset.all() + (hasattr(obj, "player") and obj.player and obj.player.cmdset.all() or [])
|
all_cmdsets = (obj.cmdset.all() +
|
||||||
all_cmdsets += hasattr(obj, "sessid") and hasattr(obj, "player") and obj.player.get_session(obj.sessid).cmdset.all()
|
(hasattr(obj, "player") and
|
||||||
all_cmdsets.sort(key=lambda x:x.priority, reverse=True)
|
obj.player and obj.player.cmdset.all() or []))
|
||||||
string += "\n{wStored Cmdset(s){n:\n %s" % ("\n ".join("%s [%s] (prio %s)" %
|
all_cmdsets += (hasattr(obj, "sessid") and
|
||||||
(cmdset.path, cmdset.key, cmdset.priority) for cmdset in all_cmdsets))
|
hasattr(obj, "player") and
|
||||||
|
obj.player.get_session(obj.sessid).cmdset.all())
|
||||||
|
all_cmdsets.sort(key=lambda x: x.priority, reverse=True)
|
||||||
|
string += "\n{wStored Cmdset(s){n:\n %s" % ("\n ".join("%s [%s] (prio %s)" % \
|
||||||
|
(cmdset.path, cmdset.key, cmdset.priority)
|
||||||
|
for cmdset in all_cmdsets))
|
||||||
|
|
||||||
# list the commands available to this object
|
# list the commands available to this object
|
||||||
avail_cmdset = sorted([cmd.key for cmd in avail_cmdset if cmd.access(obj, "cmd")])
|
avail_cmdset = sorted([cmd.key for cmd in avail_cmdset
|
||||||
|
if cmd.access(obj, "cmd")])
|
||||||
|
|
||||||
cmdsetstr = utils.fill(", ".join(avail_cmdset), indent=2)
|
cmdsetstr = utils.fill(", ".join(avail_cmdset), indent=2)
|
||||||
string += "\n{wCommands available to %s (all cmdsets + exits and external cmds){n:\n %s" % (obj.key, cmdsetstr)
|
string += "\n{wCommands available to %s (all cmdsets + exits and external cmds){n:\n %s" % (obj.key, cmdsetstr)
|
||||||
|
|
@ -1644,10 +1724,10 @@ class CmdExamine(ObjManipCommand):
|
||||||
string += "\n{wCharacters{n: %s" % ", ".join(["{c%s{n" % pobj.name for pobj in pobjs])
|
string += "\n{wCharacters{n: %s" % ", ".join(["{c%s{n" % pobj.name for pobj in pobjs])
|
||||||
if things:
|
if things:
|
||||||
string += "\n{wContents{n: %s" % ", ".join([cont.name for cont in obj.contents
|
string += "\n{wContents{n: %s" % ", ".join([cont.name for cont in obj.contents
|
||||||
if cont not in exits and cont not in pobjs])
|
if cont not in exits and cont not in pobjs])
|
||||||
separator = "-"*78
|
separator = "-" * 78
|
||||||
#output info
|
#output info
|
||||||
return '%s\n%s\n%s' % ( separator, string.strip(), separator )
|
return '%s\n%s\n%s' % (separator, string.strip(), separator)
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"Process command"
|
"Process command"
|
||||||
|
|
@ -1686,7 +1766,7 @@ class CmdExamine(ObjManipCommand):
|
||||||
obj_attrs = objdef['attrs']
|
obj_attrs = objdef['attrs']
|
||||||
|
|
||||||
self.player_mode = utils.inherits_from(caller, "src.players.player.Player") or \
|
self.player_mode = utils.inherits_from(caller, "src.players.player.Player") or \
|
||||||
"player" in self.switches or obj_name.startswith('*')
|
"player" in self.switches or obj_name.startswith('*')
|
||||||
if self.player_mode:
|
if self.player_mode:
|
||||||
try:
|
try:
|
||||||
obj = caller.search_player(obj_name.lstrip('*'))
|
obj = caller.search_player(obj_name.lstrip('*'))
|
||||||
|
|
@ -1699,7 +1779,8 @@ class CmdExamine(ObjManipCommand):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if not obj.access(caller, 'examine'):
|
if not obj.access(caller, 'examine'):
|
||||||
#If we don't have special info access, just look at the object instead.
|
#If we don't have special info access, just look
|
||||||
|
# at the object instead.
|
||||||
caller.execute_cmd('look %s' % obj_name)
|
caller.execute_cmd('look %s' % obj_name)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
|
@ -1727,7 +1808,8 @@ class CmdFind(MuxCommand):
|
||||||
Searches the database for an object of a particular name or dbref.
|
Searches the database for an object of a particular name or dbref.
|
||||||
Use *playername to search for a player. The switches allows for
|
Use *playername to search for a player. The switches allows for
|
||||||
limiting object matches to certain game entities. Dbrefmin and dbrefmax
|
limiting object matches to certain game entities. Dbrefmin and dbrefmax
|
||||||
limits matches to within the given dbrefs, or above/below if only one is given.
|
limits matches to within the given dbrefs, or above/below if only
|
||||||
|
one is given.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
key = "@find"
|
key = "@find"
|
||||||
|
|
@ -1772,11 +1854,13 @@ class CmdFind(MuxCommand):
|
||||||
if not low <= int(result.id) <= high:
|
if not low <= int(result.id) <= high:
|
||||||
string += "\n {RNo match found for '%s' within the given dbref limits.{n" % searchstring
|
string += "\n {RNo match found for '%s' within the given dbref limits.{n" % searchstring
|
||||||
else:
|
else:
|
||||||
string += "\n{g %s(%s) - %s{n" % (result.key, result.dbref, result.typeclass.path)
|
string += "\n{g %s(%s) - %s{n" % (result.key, result.dbref,
|
||||||
|
result.typeclass.path)
|
||||||
else:
|
else:
|
||||||
# Not a player/dbref search but a wider search; build a queryset.
|
# Not a player/dbref search but a wider search; build a queryset.
|
||||||
|
|
||||||
results = ObjectDB.objects.filter(db_key__istartswith=searchstring, id__gte=low, id__lte=high)
|
results = ObjectDB.objects.filter(db_key__istartswith=searchstring,
|
||||||
|
id__gte=low, id__lte=high)
|
||||||
if "room" in switches:
|
if "room" in switches:
|
||||||
results = results.filter(db_location__isnull=True)
|
results = results.filter(db_location__isnull=True)
|
||||||
if "exit" in switches:
|
if "exit" in switches:
|
||||||
|
|
@ -1909,12 +1993,14 @@ class CmdTeleport(MuxCommand):
|
||||||
use_destination = False
|
use_destination = False
|
||||||
|
|
||||||
# try the teleport
|
# try the teleport
|
||||||
if obj_to_teleport.move_to(destination, quiet=tel_quietly, emit_to_obj=caller,
|
if obj_to_teleport.move_to(destination, quiet=tel_quietly,
|
||||||
|
emit_to_obj=caller,
|
||||||
use_destination=use_destination):
|
use_destination=use_destination):
|
||||||
if obj_to_teleport == caller:
|
if obj_to_teleport == caller:
|
||||||
caller.msg("Teleported to %s." % destination)
|
caller.msg("Teleported to %s." % destination)
|
||||||
else:
|
else:
|
||||||
caller.msg("Teleported %s -> %s." % (obj_to_teleport, destination))
|
caller.msg("Teleported %s -> %s." % (obj_to_teleport,
|
||||||
|
destination))
|
||||||
|
|
||||||
|
|
||||||
class CmdScript(MuxCommand):
|
class CmdScript(MuxCommand):
|
||||||
|
|
@ -1931,9 +2017,10 @@ class CmdScript(MuxCommand):
|
||||||
If no script path/key is given, lists all scripts active on the given
|
If no script path/key is given, lists all scripts active on the given
|
||||||
object.
|
object.
|
||||||
Script path can be given from the base location for scripts as given in
|
Script path can be given from the base location for scripts as given in
|
||||||
settings. If adding a new script, it will be started automatically (no /start
|
settings. If adding a new script, it will be started automatically
|
||||||
switch is needed). Using the /start or /stop switches on an object without
|
(no /start switch is needed). Using the /start or /stop switches on an
|
||||||
specifying a script key/path will start/stop ALL scripts on the object.
|
object without specifying a script key/path will start/stop ALL scripts on
|
||||||
|
the object.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
key = "@script"
|
key = "@script"
|
||||||
|
|
@ -1970,7 +2057,8 @@ class CmdScript(MuxCommand):
|
||||||
string += "%s scripts started on %s." % (num, obj.key)
|
string += "%s scripts started on %s." % (num, obj.key)
|
||||||
elif "stop" in self.switches:
|
elif "stop" in self.switches:
|
||||||
for script in scripts:
|
for script in scripts:
|
||||||
string += "Stopping script %s on %s." % (script.key, obj.key)
|
string += "Stopping script %s on %s." % (script.key,
|
||||||
|
obj.key)
|
||||||
script.stop()
|
script.stop()
|
||||||
string = string.strip()
|
string = string.strip()
|
||||||
obj.scripts.validate()
|
obj.scripts.validate()
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,15 @@
|
||||||
"""
|
"""
|
||||||
This module ties together all the commands default Character objects have
|
This module ties together all the commands default Character objects have
|
||||||
available (i.e. IC commands). Note that some commands, such as communication-commands are
|
available (i.e. IC commands). Note that some commands, such as
|
||||||
instead put on the player level, in the Player cmdset. Player commands remain
|
communication-commands are instead put on the player level, in the
|
||||||
available also to Characters.
|
Player cmdset. Player commands remain available also to Characters.
|
||||||
"""
|
"""
|
||||||
from src.commands.cmdset import CmdSet
|
from src.commands.cmdset import CmdSet
|
||||||
from src.commands.default import general, help, admin, system
|
from src.commands.default import general, help, admin, system
|
||||||
from src.commands.default import building
|
from src.commands.default import building
|
||||||
from src.commands.default import batchprocess
|
from src.commands.default import batchprocess
|
||||||
|
|
||||||
|
|
||||||
class CharacterCmdSet(CmdSet):
|
class CharacterCmdSet(CmdSet):
|
||||||
"""
|
"""
|
||||||
Implements the default command set.
|
Implements the default command set.
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ from src.commands.cmdset import CmdSet
|
||||||
from src.commands.default import help, comms, admin, system
|
from src.commands.default import help, comms, admin, system
|
||||||
from src.commands.default import building, player
|
from src.commands.default import building, player
|
||||||
|
|
||||||
|
|
||||||
class PlayerCmdSet(CmdSet):
|
class PlayerCmdSet(CmdSet):
|
||||||
"""
|
"""
|
||||||
Implements the player command set.
|
Implements the player command set.
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ of the state instance in this module.
|
||||||
from src.commands.cmdset import CmdSet
|
from src.commands.cmdset import CmdSet
|
||||||
from src.commands.default import unloggedin
|
from src.commands.default import unloggedin
|
||||||
|
|
||||||
|
|
||||||
class UnloggedinCmdSet(CmdSet):
|
class UnloggedinCmdSet(CmdSet):
|
||||||
"""
|
"""
|
||||||
Sets up the unlogged cmdset.
|
Sets up the unlogged cmdset.
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ __all__ = ("CmdAddCom", "CmdDelCom", "CmdAllCom",
|
||||||
"CmdPage", "CmdIRC2Chan", "CmdIMC2Chan", "CmdIMCInfo",
|
"CmdPage", "CmdIRC2Chan", "CmdIMC2Chan", "CmdIMCInfo",
|
||||||
"CmdIMCTell", "CmdRSS2Chan")
|
"CmdIMCTell", "CmdRSS2Chan")
|
||||||
|
|
||||||
|
|
||||||
def find_channel(caller, channelname, silent=False, noaliases=False):
|
def find_channel(caller, channelname, silent=False, noaliases=False):
|
||||||
"""
|
"""
|
||||||
Helper function for searching for a single channel with
|
Helper function for searching for a single channel with
|
||||||
|
|
@ -30,7 +31,8 @@ def find_channel(caller, channelname, silent=False, noaliases=False):
|
||||||
channels = ChannelDB.objects.channel_search(channelname)
|
channels = ChannelDB.objects.channel_search(channelname)
|
||||||
if not channels:
|
if not channels:
|
||||||
if not noaliases:
|
if not noaliases:
|
||||||
channels = [chan for chan in ChannelDB.objects.get_all_channels() if channelname in chan.aliases.all()]
|
channels = [chan for chan in ChannelDB.objects.get_all_channels()
|
||||||
|
if channelname in chan.aliases.all()]
|
||||||
if channels:
|
if channels:
|
||||||
return channels[0]
|
return channels[0]
|
||||||
if not silent:
|
if not silent:
|
||||||
|
|
@ -43,6 +45,7 @@ def find_channel(caller, channelname, silent=False, noaliases=False):
|
||||||
return None
|
return None
|
||||||
return channels[0]
|
return channels[0]
|
||||||
|
|
||||||
|
|
||||||
class CmdAddCom(MuxPlayerCommand):
|
class CmdAddCom(MuxPlayerCommand):
|
||||||
"""
|
"""
|
||||||
addcom - subscribe to a channel with optional alias
|
addcom - subscribe to a channel with optional alias
|
||||||
|
|
@ -57,7 +60,7 @@ class CmdAddCom(MuxPlayerCommand):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
key = "addcom"
|
key = "addcom"
|
||||||
aliases = ["aliaschan","chanalias"]
|
aliases = ["aliaschan", "chanalias"]
|
||||||
help_category = "Comms"
|
help_category = "Comms"
|
||||||
locks = "cmd:not pperm(channel_banned)"
|
locks = "cmd:not pperm(channel_banned)"
|
||||||
|
|
||||||
|
|
@ -168,6 +171,7 @@ class CmdDelCom(MuxPlayerCommand):
|
||||||
else:
|
else:
|
||||||
self.msg("You had no such alias defined for this channel.")
|
self.msg("You had no such alias defined for this channel.")
|
||||||
|
|
||||||
|
|
||||||
class CmdAllCom(MuxPlayerCommand):
|
class CmdAllCom(MuxPlayerCommand):
|
||||||
"""
|
"""
|
||||||
allcom - operate on all channels
|
allcom - operate on all channels
|
||||||
|
|
@ -197,8 +201,10 @@ class CmdAllCom(MuxPlayerCommand):
|
||||||
return
|
return
|
||||||
|
|
||||||
if args == "on":
|
if args == "on":
|
||||||
# get names of all channels available to listen to and activate them all
|
# get names of all channels available to listen to
|
||||||
channels = [chan for chan in ChannelDB.objects.get_all_channels() if chan.access(caller, 'listen')]
|
# and activate them all
|
||||||
|
channels = [chan for chan in ChannelDB.objects.get_all_channels()
|
||||||
|
if chan.access(caller, 'listen')]
|
||||||
for channel in channels:
|
for channel in channels:
|
||||||
caller.execute_cmd("addcom %s" % channel.key)
|
caller.execute_cmd("addcom %s" % channel.key)
|
||||||
elif args == "off":
|
elif args == "off":
|
||||||
|
|
@ -208,13 +214,15 @@ class CmdAllCom(MuxPlayerCommand):
|
||||||
caller.execute_cmd("delcom %s" % channel.key)
|
caller.execute_cmd("delcom %s" % channel.key)
|
||||||
elif args == "destroy":
|
elif args == "destroy":
|
||||||
# destroy all channels you control
|
# destroy all channels you control
|
||||||
channels = [chan for chan in ChannelDB.objects.get_all_channels() if chan.access(caller, 'control')]
|
channels = [chan for chan in ChannelDB.objects.get_all_channels()
|
||||||
|
if chan.access(caller, 'control')]
|
||||||
for channel in channels:
|
for channel in channels:
|
||||||
caller.execute_cmd("@cdestroy %s" % channel.key)
|
caller.execute_cmd("@cdestroy %s" % channel.key)
|
||||||
elif args == "who":
|
elif args == "who":
|
||||||
# run a who, listing the subscribers on visible channels.
|
# run a who, listing the subscribers on visible channels.
|
||||||
string = "\n{CChannel subscriptions{n"
|
string = "\n{CChannel subscriptions{n"
|
||||||
channels = [chan for chan in ChannelDB.objects.get_all_channels() if chan.access(caller, 'listen')]
|
channels = [chan for chan in ChannelDB.objects.get_all_channels()
|
||||||
|
if chan.access(caller, 'listen')]
|
||||||
if not channels:
|
if not channels:
|
||||||
string += "No channels."
|
string += "No channels."
|
||||||
for channel in channels:
|
for channel in channels:
|
||||||
|
|
@ -229,6 +237,7 @@ class CmdAllCom(MuxPlayerCommand):
|
||||||
# wrong input
|
# wrong input
|
||||||
self.msg("Usage: allcom on | off | who | clear")
|
self.msg("Usage: allcom on | off | who | clear")
|
||||||
|
|
||||||
|
|
||||||
class CmdChannels(MuxPlayerCommand):
|
class CmdChannels(MuxPlayerCommand):
|
||||||
"""
|
"""
|
||||||
@clist
|
@clist
|
||||||
|
|
@ -253,7 +262,8 @@ class CmdChannels(MuxPlayerCommand):
|
||||||
caller = self.caller
|
caller = self.caller
|
||||||
|
|
||||||
# all channels we have available to listen to
|
# all channels we have available to listen to
|
||||||
channels = [chan for chan in ChannelDB.objects.get_all_channels() if chan.access(caller, 'listen')]
|
channels = [chan for chan in ChannelDB.objects.get_all_channels()
|
||||||
|
if chan.access(caller, 'listen')]
|
||||||
#print channels
|
#print channels
|
||||||
if not channels:
|
if not channels:
|
||||||
self.msg("No channels available.")
|
self.msg("No channels available.")
|
||||||
|
|
@ -264,28 +274,39 @@ class CmdChannels(MuxPlayerCommand):
|
||||||
|
|
||||||
if self.cmdstring == "comlist":
|
if self.cmdstring == "comlist":
|
||||||
# just display the subscribed channels with no extra info
|
# just display the subscribed channels with no extra info
|
||||||
comtable = prettytable.PrettyTable(["{wchannel","{wmy aliases", "{wdescription"])
|
comtable = prettytable.PrettyTable(["{wchannel",
|
||||||
|
"{wmy aliases",
|
||||||
|
"{wdescription"])
|
||||||
for chan in subs:
|
for chan in subs:
|
||||||
clower = chan.key.lower()
|
clower = chan.key.lower()
|
||||||
nicks = caller.nicks.get(category="channel")
|
nicks = caller.nicks.get(category="channel")
|
||||||
comtable.add_row(["%s%s" % (chan.key, chan.aliases.all() and "(%s)" % ",".join(chan.aliases.all()) or ""),
|
comtable.add_row(["%s%s" % (chan.key, chan.aliases.all() and
|
||||||
"%s".join(nick for nick in make_iter(nicks) if nick and nick.lower()==clower),
|
"(%s)" % ",".join(chan.aliases.all()) or ""),
|
||||||
|
"%s".join(nick for nick in make_iter(nicks)
|
||||||
|
if nick and nick.lower() == clower),
|
||||||
chan.db.desc])
|
chan.db.desc])
|
||||||
caller.msg("\n{wChannel subscriptions{n (use {w@channels{n to list all, {waddcom{n/{wdelcom{n to sub/unsub):{n\n%s" % comtable)
|
caller.msg("\n{wChannel subscriptions{n (use {w@channels{n to list all, {waddcom{n/{wdelcom{n to sub/unsub):{n\n%s" % comtable)
|
||||||
else:
|
else:
|
||||||
# full listing (of channels caller is able to listen to)
|
# full listing (of channels caller is able to listen to)
|
||||||
comtable = prettytable.PrettyTable(["{wsub","{wchannel","{wmy aliases","{wlocks","{wdescription"])
|
comtable = prettytable.PrettyTable(["{wsub",
|
||||||
|
"{wchannel",
|
||||||
|
"{wmy aliases",
|
||||||
|
"{wlocks",
|
||||||
|
"{wdescription"])
|
||||||
for chan in channels:
|
for chan in channels:
|
||||||
clower = chan.key.lower()
|
clower = chan.key.lower()
|
||||||
nicks = caller.nicks.get(category="channel")
|
nicks = caller.nicks.get(category="channel")
|
||||||
nicks = nicks or []
|
nicks = nicks or []
|
||||||
comtable.add_row([chan in subs and "{gYes{n" or "{rNo{n",
|
comtable.add_row([chan in subs and "{gYes{n" or "{rNo{n",
|
||||||
"%s%s" % (chan.key, chan.aliases.all() and "(%s)" % ",".join(chan.aliases.all()) or ""),
|
"%s%s" % (chan.key, chan.aliases.all() and
|
||||||
"%s".join(nick for nick in make_iter(nicks) if nick.lower()==clower),
|
"(%s)" % ",".join(chan.aliases.all()) or ""),
|
||||||
|
"%s".join(nick for nick in make_iter(nicks)
|
||||||
|
if nick.lower() == clower),
|
||||||
str(chan.locks),
|
str(chan.locks),
|
||||||
chan.db.desc])
|
chan.db.desc])
|
||||||
caller.msg("\n{wAvailable channels{n (use {wcomlist{n,{waddcom{n and {wdelcom{n to manage subscriptions):\n%s" % comtable)
|
caller.msg("\n{wAvailable channels{n (use {wcomlist{n,{waddcom{n and {wdelcom{n to manage subscriptions):\n%s" % comtable)
|
||||||
|
|
||||||
|
|
||||||
class CmdCdestroy(MuxPlayerCommand):
|
class CmdCdestroy(MuxPlayerCommand):
|
||||||
"""
|
"""
|
||||||
@cdestroy
|
@cdestroy
|
||||||
|
|
@ -322,6 +343,7 @@ class CmdCdestroy(MuxPlayerCommand):
|
||||||
CHANNELHANDLER.update()
|
CHANNELHANDLER.update()
|
||||||
self.msg("Channel '%s' was destroyed." % channel)
|
self.msg("Channel '%s' was destroyed." % channel)
|
||||||
|
|
||||||
|
|
||||||
class CmdCBoot(MuxPlayerCommand):
|
class CmdCBoot(MuxPlayerCommand):
|
||||||
"""
|
"""
|
||||||
@cboot
|
@cboot
|
||||||
|
|
@ -382,6 +404,7 @@ class CmdCBoot(MuxPlayerCommand):
|
||||||
channel.disconnect_from(player)
|
channel.disconnect_from(player)
|
||||||
CHANNELHANDLER.update()
|
CHANNELHANDLER.update()
|
||||||
|
|
||||||
|
|
||||||
class CmdCemit(MuxPlayerCommand):
|
class CmdCemit(MuxPlayerCommand):
|
||||||
"""
|
"""
|
||||||
@cemit - send a message to channel
|
@cemit - send a message to channel
|
||||||
|
|
@ -429,6 +452,7 @@ class CmdCemit(MuxPlayerCommand):
|
||||||
string = "Sent to channel %s: %s" % (channel.key, message)
|
string = "Sent to channel %s: %s" % (channel.key, message)
|
||||||
self.msg(string)
|
self.msg(string)
|
||||||
|
|
||||||
|
|
||||||
class CmdCWho(MuxPlayerCommand):
|
class CmdCWho(MuxPlayerCommand):
|
||||||
"""
|
"""
|
||||||
@cwho
|
@cwho
|
||||||
|
|
@ -466,6 +490,7 @@ class CmdCWho(MuxPlayerCommand):
|
||||||
string += " <None>"
|
string += " <None>"
|
||||||
self.msg(string.strip())
|
self.msg(string.strip())
|
||||||
|
|
||||||
|
|
||||||
class CmdChannelCreate(MuxPlayerCommand):
|
class CmdChannelCreate(MuxPlayerCommand):
|
||||||
"""
|
"""
|
||||||
@ccreate
|
@ccreate
|
||||||
|
|
@ -508,7 +533,10 @@ class CmdChannelCreate(MuxPlayerCommand):
|
||||||
return
|
return
|
||||||
# Create and set the channel up
|
# Create and set the channel up
|
||||||
lockstring = "send:all();listen:all();control:id(%s)" % caller.id
|
lockstring = "send:all();listen:all();control:id(%s)" % caller.id
|
||||||
new_chan = create.create_channel(channame, aliases, description, locks=lockstring)
|
new_chan = create.create_channel(channame,
|
||||||
|
aliases,
|
||||||
|
description,
|
||||||
|
locks=lockstring)
|
||||||
new_chan.connect_to(caller)
|
new_chan.connect_to(caller)
|
||||||
self.msg("Created channel %s and connected to it." % new_chan.key)
|
self.msg("Created channel %s and connected to it." % new_chan.key)
|
||||||
|
|
||||||
|
|
@ -593,7 +621,9 @@ class CmdCdesc(MuxPlayerCommand):
|
||||||
# set the description
|
# set the description
|
||||||
channel.db.desc = self.rhs
|
channel.db.desc = self.rhs
|
||||||
channel.save()
|
channel.save()
|
||||||
self.msg("Description of channel '%s' set to '%s'." % (channel.key, self.rhs))
|
self.msg("Description of channel '%s' set to '%s'." % (channel.key,
|
||||||
|
self.rhs))
|
||||||
|
|
||||||
|
|
||||||
class CmdPage(MuxPlayerCommand):
|
class CmdPage(MuxPlayerCommand):
|
||||||
"""
|
"""
|
||||||
|
|
@ -624,15 +654,16 @@ class CmdPage(MuxPlayerCommand):
|
||||||
caller = self.caller
|
caller = self.caller
|
||||||
|
|
||||||
# get the messages we've sent (not to channels)
|
# get the messages we've sent (not to channels)
|
||||||
pages_we_sent = Msg.objects.get_messages_by_sender(caller, exclude_channel_messages=True)
|
pages_we_sent = Msg.objects.get_messages_by_sender(caller,
|
||||||
|
exclude_channel_messages=True)
|
||||||
# get last messages we've got
|
# get last messages we've got
|
||||||
pages_we_got = Msg.objects.get_messages_by_receiver(caller)
|
pages_we_got = Msg.objects.get_messages_by_receiver(caller)
|
||||||
|
|
||||||
|
|
||||||
if 'last' in self.switches:
|
if 'last' in self.switches:
|
||||||
if pages_we_sent:
|
if pages_we_sent:
|
||||||
recv = ",".join(obj.key for obj in pages_we_sent[-1].receivers)
|
recv = ",".join(obj.key for obj in pages_we_sent[-1].receivers)
|
||||||
self.msg("You last paged {c%s{n:%s" % (recv, pages_we_sent[-1].message))
|
self.msg("You last paged {c%s{n:%s" % (recv,
|
||||||
|
pages_we_sent[-1].message))
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
self.msg("You haven't paged anyone yet.")
|
self.msg("You haven't paged anyone yet.")
|
||||||
|
|
@ -654,12 +685,12 @@ class CmdPage(MuxPlayerCommand):
|
||||||
lastpages = pages[-number:]
|
lastpages = pages[-number:]
|
||||||
else:
|
else:
|
||||||
lastpages = pages
|
lastpages = pages
|
||||||
|
template = "{w%s{n {c%s{n to {c%s{n: %s"
|
||||||
lastpages = "\n ".join("{w%s{n {c%s{n to {c%s{n: %s" % (utils.datetime_format(page.date_sent),
|
lastpages = "\n ".join(template %
|
||||||
",".join(obj.key for obj in page.senders),
|
(utils.datetime_format(page.date_sent),
|
||||||
"{n,{c ".join([obj.name for obj in page.receivers]),
|
",".join(obj.key for obj in page.senders),
|
||||||
page.message)
|
"{n,{c ".join([obj.name for obj in page.receivers]),
|
||||||
for page in lastpages)
|
page.message) for page in lastpages)
|
||||||
|
|
||||||
if lastpages:
|
if lastpages:
|
||||||
string = "Your latest pages:\n %s" % lastpages
|
string = "Your latest pages:\n %s" % lastpages
|
||||||
|
|
@ -668,7 +699,6 @@ class CmdPage(MuxPlayerCommand):
|
||||||
self.msg(string)
|
self.msg(string)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
# We are sending. Build a list of targets
|
# We are sending. Build a list of targets
|
||||||
|
|
||||||
if not self.lhs:
|
if not self.lhs:
|
||||||
|
|
@ -722,7 +752,7 @@ class CmdPage(MuxPlayerCommand):
|
||||||
else:
|
else:
|
||||||
received.append("{c%s{n" % pobj.name)
|
received.append("{c%s{n" % pobj.name)
|
||||||
if rstrings:
|
if rstrings:
|
||||||
self.msg(rstrings = "\n".join(rstrings))
|
self.msg(rstrings="\n".join(rstrings))
|
||||||
self.msg("You paged %s with: '%s'." % (", ".join(received), message))
|
self.msg("You paged %s with: '%s'." % (", ".join(received), message))
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -734,18 +764,20 @@ class CmdIRC2Chan(MuxCommand):
|
||||||
@irc2chan[/switches] <evennia_channel> = <ircnetwork> <port> <#irchannel> <botname>
|
@irc2chan[/switches] <evennia_channel> = <ircnetwork> <port> <#irchannel> <botname>
|
||||||
|
|
||||||
Switches:
|
Switches:
|
||||||
/disconnect - this will delete the bot and remove the irc connection to the channel.
|
/disconnect - this will delete the bot and remove the irc connection
|
||||||
|
to the channel.
|
||||||
/remove - "
|
/remove - "
|
||||||
/list - show all irc<->evennia mappings
|
/list - show all irc<->evennia mappings
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
@irc2chan myircchan = irc.dalnet.net 6667 myevennia-channel evennia-bot
|
@irc2chan myircchan = irc.dalnet.net 6667 myevennia-channel evennia-bot
|
||||||
|
|
||||||
This creates an IRC bot that connects to a given IRC network and channel. It will
|
This creates an IRC bot that connects to a given IRC network and channel.
|
||||||
relay everything said in the evennia channel to the IRC channel and vice versa. The
|
It will relay everything said in the evennia channel to the IRC channel and
|
||||||
bot will automatically connect at server start, so this comman need only be given once.
|
vice versa. The bot will automatically connect at server start, so this
|
||||||
The /disconnect switch will permanently delete the bot. To only temporarily deactivate it,
|
comman need only be given once. The /disconnect switch will permanently
|
||||||
use the @services command instead.
|
delete the bot. To only temporarily deactivate it, use the {w@services{n
|
||||||
|
command instead.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
key = "@irc2chan"
|
key = "@irc2chan"
|
||||||
|
|
@ -780,19 +812,25 @@ class CmdIRC2Chan(MuxCommand):
|
||||||
channel = self.lhs
|
channel = self.lhs
|
||||||
self.rhs = self.rhs.replace('#', ' ') # to avoid Python comment issues
|
self.rhs = self.rhs.replace('#', ' ') # to avoid Python comment issues
|
||||||
try:
|
try:
|
||||||
irc_network, irc_port, irc_channel, irc_botname = [part.strip() for part in self.rhs.split(None, 3)]
|
irc_network, irc_port, irc_channel, irc_botname = \
|
||||||
|
[part.strip() for part in self.rhs.split(None, 3)]
|
||||||
irc_channel = "#%s" % irc_channel
|
irc_channel = "#%s" % irc_channel
|
||||||
except Exception:
|
except Exception:
|
||||||
string = "IRC bot definition '%s' is not valid." % self.rhs
|
string = "IRC bot definition '%s' is not valid." % self.rhs
|
||||||
self.msg(string)
|
self.msg(string)
|
||||||
return
|
return
|
||||||
|
|
||||||
if 'disconnect' in self.switches or 'remove' in self.switches or 'delete' in self.switches:
|
if('disconnect' in self.switches or 'remove' in self.switches or
|
||||||
|
'delete' in self.switches):
|
||||||
chanmatch = find_channel(self.caller, channel, silent=True)
|
chanmatch = find_channel(self.caller, channel, silent=True)
|
||||||
if chanmatch:
|
if chanmatch:
|
||||||
channel = chanmatch.key
|
channel = chanmatch.key
|
||||||
|
|
||||||
ok = irc.delete_connection(channel, irc_network, irc_port, irc_channel, irc_botname)
|
ok = irc.delete_connection(channel,
|
||||||
|
irc_network,
|
||||||
|
irc_port,
|
||||||
|
irc_channel,
|
||||||
|
irc_botname)
|
||||||
if not ok:
|
if not ok:
|
||||||
self.msg("IRC connection/bot could not be removed, does it exist?")
|
self.msg("IRC connection/bot could not be removed, does it exist?")
|
||||||
else:
|
else:
|
||||||
|
|
@ -802,12 +840,17 @@ class CmdIRC2Chan(MuxCommand):
|
||||||
channel = find_channel(self.caller, channel)
|
channel = find_channel(self.caller, channel)
|
||||||
if not channel:
|
if not channel:
|
||||||
return
|
return
|
||||||
ok = irc.create_connection(channel, irc_network, irc_port, irc_channel, irc_botname)
|
ok = irc.create_connection(channel,
|
||||||
|
irc_network,
|
||||||
|
irc_port,
|
||||||
|
irc_channel,
|
||||||
|
irc_botname)
|
||||||
if not ok:
|
if not ok:
|
||||||
self.msg("This IRC connection already exists.")
|
self.msg("This IRC connection already exists.")
|
||||||
return
|
return
|
||||||
self.msg("Connection created. Starting IRC bot.")
|
self.msg("Connection created. Starting IRC bot.")
|
||||||
|
|
||||||
|
|
||||||
class CmdIMC2Chan(MuxCommand):
|
class CmdIMC2Chan(MuxCommand):
|
||||||
"""
|
"""
|
||||||
imc2chan - link an evennia channel to imc2
|
imc2chan - link an evennia channel to imc2
|
||||||
|
|
@ -863,9 +906,10 @@ class CmdIMC2Chan(MuxCommand):
|
||||||
channel = self.lhs
|
channel = self.lhs
|
||||||
imc2_channel = self.rhs
|
imc2_channel = self.rhs
|
||||||
|
|
||||||
if 'disconnect' in self.switches or 'remove' in self.switches or 'delete' in self.switches:
|
if('disconnect' in self.switches or 'remove' in self.switches or
|
||||||
# we don't search for channels before this since we want to clear the link
|
'delete' in self.switches):
|
||||||
# also if the channel no longer exists.
|
# we don't search for channels before this since we want
|
||||||
|
# to clear the link also if the channel no longer exists.
|
||||||
ok = imc2.delete_connection(channel, imc2_channel)
|
ok = imc2.delete_connection(channel, imc2_channel)
|
||||||
if not ok:
|
if not ok:
|
||||||
self.msg("IMC2 connection could not be removed, does it exist?")
|
self.msg("IMC2 connection could not be removed, does it exist?")
|
||||||
|
|
@ -932,7 +976,8 @@ class CmdIMCInfo(MuxCommand):
|
||||||
IMC2_CLIENT.send_packet(pck.IMC2PacketIceRefresh())
|
IMC2_CLIENT.send_packet(pck.IMC2PacketIceRefresh())
|
||||||
self.msg("IMC2 lists were re-synced.")
|
self.msg("IMC2 lists were re-synced.")
|
||||||
|
|
||||||
elif "games" in self.switches or "muds" in self.switches or self.cmdstring == "@imclist":
|
elif("games" in self.switches or "muds" in self.switches
|
||||||
|
or self.cmdstring == "@imclist"):
|
||||||
# list muds
|
# list muds
|
||||||
from src.comms.imc2 import IMC2_MUDLIST
|
from src.comms.imc2 import IMC2_MUDLIST
|
||||||
|
|
||||||
|
|
@ -956,9 +1001,13 @@ class CmdIMCInfo(MuxCommand):
|
||||||
return
|
return
|
||||||
from src.comms.imc2 import IMC2_CLIENT
|
from src.comms.imc2 import IMC2_CLIENT
|
||||||
self.msg("Sending IMC whois request. If you receive no response, no matches were found.")
|
self.msg("Sending IMC whois request. If you receive no response, no matches were found.")
|
||||||
IMC2_CLIENT.msg_imc2(None, from_obj=self.caller, packet_type="imcwhois", target=self.args)
|
IMC2_CLIENT.msg_imc2(None,
|
||||||
|
from_obj=self.caller,
|
||||||
|
packet_type="imcwhois",
|
||||||
|
target=self.args)
|
||||||
|
|
||||||
elif not self.switches or "channels" in self.switches or self.cmdstring == "@imcchanlist":
|
elif(not self.switches or "channels" in self.switches or
|
||||||
|
self.cmdstring == "@imcchanlist"):
|
||||||
# show channels
|
# show channels
|
||||||
from src.comms.imc2 import IMC2_CHANLIST, IMC2_CLIENT
|
from src.comms.imc2 import IMC2_CHANLIST, IMC2_CLIENT
|
||||||
|
|
||||||
|
|
@ -968,7 +1017,8 @@ class CmdIMCInfo(MuxCommand):
|
||||||
table = prettytable.PrettyTable(["Full name", "Name", "Owner", "Perm", "Policy"])
|
table = prettytable.PrettyTable(["Full name", "Name", "Owner", "Perm", "Policy"])
|
||||||
for chan in channels:
|
for chan in channels:
|
||||||
nchans += 1
|
nchans += 1
|
||||||
table.add_row([chan.name, chan.localname, chan.owner, chan.level, chan.policy])
|
table.add_row([chan.name, chan.localname, chan.owner,
|
||||||
|
chan.level, chan.policy])
|
||||||
string += "\n{wChannels on %s:{n\n%s" % (IMC2_CLIENT.factory.network, table)
|
string += "\n{wChannels on %s:{n\n%s" % (IMC2_CLIENT.factory.network, table)
|
||||||
string += "\n%i Channels found." % nchans
|
string += "\n%i Channels found." % nchans
|
||||||
self.msg(string)
|
self.msg(string)
|
||||||
|
|
@ -977,6 +1027,7 @@ class CmdIMCInfo(MuxCommand):
|
||||||
string = "Usage: imcinfo|imcchanlist|imclist"
|
string = "Usage: imcinfo|imcchanlist|imclist"
|
||||||
self.msg(string)
|
self.msg(string)
|
||||||
|
|
||||||
|
|
||||||
# unclear if this is working ...
|
# unclear if this is working ...
|
||||||
class CmdIMCTell(MuxCommand):
|
class CmdIMCTell(MuxCommand):
|
||||||
"""
|
"""
|
||||||
|
|
@ -1028,19 +1079,21 @@ class CmdRSS2Chan(MuxCommand):
|
||||||
@rss2chan[/switches] <evennia_channel> = <rss_url>
|
@rss2chan[/switches] <evennia_channel> = <rss_url>
|
||||||
|
|
||||||
Switches:
|
Switches:
|
||||||
/disconnect - this will stop the feed and remove the connection to the channel.
|
/disconnect - this will stop the feed and remove the connection to the
|
||||||
|
channel.
|
||||||
/remove - "
|
/remove - "
|
||||||
/list - show all rss->evennia mappings
|
/list - show all rss->evennia mappings
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
@rss2chan rsschan = http://code.google.com/feeds/p/evennia/updates/basic
|
@rss2chan rsschan = http://code.google.com/feeds/p/evennia/updates/basic
|
||||||
|
|
||||||
This creates an RSS reader that connects to a given RSS feed url. Updates will be
|
This creates an RSS reader that connects to a given RSS feed url. Updates
|
||||||
echoed as a title and news link to the given channel. The rate of updating is set
|
will be echoed as a title and news link to the given channel. The rate of
|
||||||
with the RSS_UPDATE_INTERVAL variable in settings (default is every 10 minutes).
|
updating is set with the RSS_UPDATE_INTERVAL variable in settings (default
|
||||||
|
is every 10 minutes).
|
||||||
|
|
||||||
When disconnecting you need to supply both the channel and url again so as to identify
|
When disconnecting you need to supply both the channel and url again so as
|
||||||
the connection uniquely.
|
to identify the connection uniquely.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
key = "@rss2chan"
|
key = "@rss2chan"
|
||||||
|
|
@ -1075,7 +1128,8 @@ class CmdRSS2Chan(MuxCommand):
|
||||||
channel = self.lhs
|
channel = self.lhs
|
||||||
url = self.rhs
|
url = self.rhs
|
||||||
|
|
||||||
if 'disconnect' in self.switches or 'remove' in self.switches or 'delete' in self.switches:
|
if('disconnect' in self.switches or 'remove' in self.switches or
|
||||||
|
'delete' in self.switches):
|
||||||
chanmatch = find_channel(self.caller, channel, silent=True)
|
chanmatch = find_channel(self.caller, channel, silent=True)
|
||||||
if chanmatch:
|
if chanmatch:
|
||||||
channel = chanmatch.key
|
channel = chanmatch.key
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ __all__ = ("CmdHome", "CmdLook", "CmdNick",
|
||||||
|
|
||||||
AT_SEARCH_RESULT = utils.variable_from_module(*settings.SEARCH_AT_RESULT.rsplit('.', 1))
|
AT_SEARCH_RESULT = utils.variable_from_module(*settings.SEARCH_AT_RESULT.rsplit('.', 1))
|
||||||
|
|
||||||
|
|
||||||
class CmdHome(MuxCommand):
|
class CmdHome(MuxCommand):
|
||||||
"""
|
"""
|
||||||
home
|
home
|
||||||
|
|
@ -38,6 +39,7 @@ class CmdHome(MuxCommand):
|
||||||
caller.move_to(home)
|
caller.move_to(home)
|
||||||
caller.msg("There's no place like home ...")
|
caller.msg("There's no place like home ...")
|
||||||
|
|
||||||
|
|
||||||
class CmdLook(MuxCommand):
|
class CmdLook(MuxCommand):
|
||||||
"""
|
"""
|
||||||
look
|
look
|
||||||
|
|
@ -126,7 +128,9 @@ class CmdNick(MuxCommand):
|
||||||
nicks = caller.nicks.get(category="channel")
|
nicks = caller.nicks.get(category="channel")
|
||||||
|
|
||||||
if 'list' in switches:
|
if 'list' in switches:
|
||||||
table = prettytable.PrettyTable(["{wNickType", "{wNickname", "{wTranslates-to"])
|
table = prettytable.PrettyTable(["{wNickType",
|
||||||
|
"{wNickname",
|
||||||
|
"{wTranslates-to"])
|
||||||
for nick in nicks:
|
for nick in nicks:
|
||||||
table.add_row([nick.db_category, nick.db_key, nick.db_data])
|
table.add_row([nick.db_category, nick.db_key, nick.db_data])
|
||||||
string = "{wDefined Nicks:{n\n%s" % table
|
string = "{wDefined Nicks:{n\n%s" % table
|
||||||
|
|
@ -170,6 +174,7 @@ class CmdNick(MuxCommand):
|
||||||
caller.nicks.add(nick, real, category=switch)
|
caller.nicks.add(nick, real, category=switch)
|
||||||
caller.msg(string)
|
caller.msg(string)
|
||||||
|
|
||||||
|
|
||||||
class CmdInventory(MuxCommand):
|
class CmdInventory(MuxCommand):
|
||||||
"""
|
"""
|
||||||
inventory
|
inventory
|
||||||
|
|
@ -198,6 +203,7 @@ class CmdInventory(MuxCommand):
|
||||||
string = "{wYou are carrying:\n%s" % table
|
string = "{wYou are carrying:\n%s" % table
|
||||||
self.caller.msg(string)
|
self.caller.msg(string)
|
||||||
|
|
||||||
|
|
||||||
class CmdGet(MuxCommand):
|
class CmdGet(MuxCommand):
|
||||||
"""
|
"""
|
||||||
get
|
get
|
||||||
|
|
@ -327,7 +333,6 @@ class CmdGive(MuxCommand):
|
||||||
target.msg("%s gives you %s." % (caller.key, to_give.key))
|
target.msg("%s gives you %s." % (caller.key, to_give.key))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class CmdSay(MuxCommand):
|
class CmdSay(MuxCommand):
|
||||||
"""
|
"""
|
||||||
say
|
say
|
||||||
|
|
@ -365,6 +370,7 @@ class CmdSay(MuxCommand):
|
||||||
caller.location.msg_contents(emit_string,
|
caller.location.msg_contents(emit_string,
|
||||||
exclude=caller)
|
exclude=caller)
|
||||||
|
|
||||||
|
|
||||||
class CmdPose(MuxCommand):
|
class CmdPose(MuxCommand):
|
||||||
"""
|
"""
|
||||||
pose - strike a pose
|
pose - strike a pose
|
||||||
|
|
@ -407,6 +413,7 @@ class CmdPose(MuxCommand):
|
||||||
msg = "%s%s" % (self.caller.name, self.args)
|
msg = "%s%s" % (self.caller.name, self.args)
|
||||||
self.caller.location.msg_contents(msg)
|
self.caller.location.msg_contents(msg)
|
||||||
|
|
||||||
|
|
||||||
class CmdAccess(MuxCommand):
|
class CmdAccess(MuxCommand):
|
||||||
"""
|
"""
|
||||||
access - show access groups
|
access - show access groups
|
||||||
|
|
@ -440,5 +447,4 @@ class CmdAccess(MuxCommand):
|
||||||
string += "\nCharacter {c%s{n: %s" % (caller.key, cperms)
|
string += "\nCharacter {c%s{n: %s" % (caller.key, cperms)
|
||||||
if hasattr(caller, 'player'):
|
if hasattr(caller, 'player'):
|
||||||
string += "\nPlayer {c%s{n: %s" % (caller.player.key, pperms)
|
string += "\nPlayer {c%s{n: %s" % (caller.player.key, pperms)
|
||||||
caller.msg(string)
|
caller.msg(string)
|
||||||
|
|
||||||
|
|
@ -18,7 +18,8 @@ from src.commands.default.muxcommand import MuxCommand
|
||||||
__all__ = ("CmdHelp", "CmdSetHelp")
|
__all__ = ("CmdHelp", "CmdSetHelp")
|
||||||
|
|
||||||
|
|
||||||
SEP = "{C" + "-"*78 + "{n"
|
SEP = "{C" + "-" * 78 + "{n"
|
||||||
|
|
||||||
|
|
||||||
def format_help_entry(title, help_text, aliases=None, suggested=None):
|
def format_help_entry(title, help_text, aliases=None, suggested=None):
|
||||||
"""
|
"""
|
||||||
|
|
@ -38,6 +39,7 @@ def format_help_entry(title, help_text, aliases=None, suggested=None):
|
||||||
string += "\n" + SEP
|
string += "\n" + SEP
|
||||||
return string
|
return string
|
||||||
|
|
||||||
|
|
||||||
def format_help_list(hdict_cmds, hdict_db):
|
def format_help_list(hdict_cmds, hdict_db):
|
||||||
"""
|
"""
|
||||||
Output a category-ordered list. The input are the
|
Output a category-ordered list. The input are the
|
||||||
|
|
@ -57,6 +59,7 @@ def format_help_list(hdict_cmds, hdict_db):
|
||||||
string += "{G" + fill(", ".join(sorted([str(topic) for topic in hdict_db[category]]))) + "{n"
|
string += "{G" + fill(", ".join(sorted([str(topic) for topic in hdict_db[category]]))) + "{n"
|
||||||
return string
|
return string
|
||||||
|
|
||||||
|
|
||||||
class CmdHelp(Command):
|
class CmdHelp(Command):
|
||||||
"""
|
"""
|
||||||
The main help command
|
The main help command
|
||||||
|
|
@ -129,13 +132,18 @@ class CmdHelp(Command):
|
||||||
# try an exact command auto-help match
|
# try an exact command auto-help match
|
||||||
match = [cmd for cmd in all_cmds if cmd == query]
|
match = [cmd for cmd in all_cmds if cmd == query]
|
||||||
if len(match) == 1:
|
if len(match) == 1:
|
||||||
self.msg(format_help_entry(match[0].key, match[0].__doc__, aliases=match[0].aliases, suggested=suggestions))
|
self.msg(format_help_entry(match[0].key,
|
||||||
|
match[0].__doc__,
|
||||||
|
aliases=match[0].aliases,
|
||||||
|
suggested=suggestions))
|
||||||
return
|
return
|
||||||
|
|
||||||
# try an exact database help entry match
|
# try an exact database help entry match
|
||||||
match = list(HelpEntry.objects.find_topicmatch(query, exact=True))
|
match = list(HelpEntry.objects.find_topicmatch(query, exact=True))
|
||||||
if len(match) == 1:
|
if len(match) == 1:
|
||||||
self.msg(format_help_entry(match[0].key, match[0].entrytext, suggested=suggestions))
|
self.msg(format_help_entry(match[0].key,
|
||||||
|
match[0].entrytext,
|
||||||
|
suggested=suggestions))
|
||||||
return
|
return
|
||||||
|
|
||||||
# try to see if a category name was entered
|
# try to see if a category name was entered
|
||||||
|
|
@ -147,6 +155,7 @@ class CmdHelp(Command):
|
||||||
# no exact matches found. Just give suggestions.
|
# no exact matches found. Just give suggestions.
|
||||||
self.msg(format_help_entry("", "No help entry found for '%s'" % query, None, suggested=suggestions))
|
self.msg(format_help_entry("", "No help entry found for '%s'" % query, None, suggested=suggestions))
|
||||||
|
|
||||||
|
|
||||||
class CmdSetHelp(MuxCommand):
|
class CmdSetHelp(MuxCommand):
|
||||||
"""
|
"""
|
||||||
@help - edit the help database
|
@help - edit the help database
|
||||||
|
|
@ -169,9 +178,9 @@ class CmdSetHelp(MuxCommand):
|
||||||
@sethelp/append pickpocketing, ,attr(is_thief) = This steals ...
|
@sethelp/append pickpocketing, ,attr(is_thief) = This steals ...
|
||||||
|
|
||||||
This command manipulates the help database. A help entry can be created,
|
This command manipulates the help database. A help entry can be created,
|
||||||
appended/merged to and deleted. If you don't assign a category, the "General"
|
appended/merged to and deleted. If you don't assign a category, the
|
||||||
category will be used. If no lockstring is specified, default is to let everyone read
|
"General" category will be used. If no lockstring is specified, default
|
||||||
the help file.
|
is to let everyone read the help file.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
key = "@help"
|
key = "@help"
|
||||||
|
|
|
||||||
|
|
@ -74,8 +74,8 @@ class MuxCommand(Command):
|
||||||
|
|
||||||
The 'name[ with several words]' part is already dealt with by the
|
The 'name[ with several words]' part is already dealt with by the
|
||||||
cmdhandler at this point, and stored in self.cmdname (we don't use
|
cmdhandler at this point, and stored in self.cmdname (we don't use
|
||||||
it here). The rest of the command is stored in self.args, which can start
|
it here). The rest of the command is stored in self.args, which can
|
||||||
with the switch indicator /.
|
start with the switch indicator /.
|
||||||
|
|
||||||
This parser breaks self.args into its constituents and stores them in the
|
This parser breaks self.args into its constituents and stores them in the
|
||||||
following variables:
|
following variables:
|
||||||
|
|
|
||||||
|
|
@ -24,8 +24,9 @@ from src.utils import utils, create, search, prettytable
|
||||||
|
|
||||||
from settings import MAX_NR_CHARACTERS, MULTISESSION_MODE
|
from settings import MAX_NR_CHARACTERS, MULTISESSION_MODE
|
||||||
# limit symbol import for API
|
# limit symbol import for API
|
||||||
__all__ = ("CmdOOCLook", "CmdIC", "CmdOOC", "CmdPassword", "CmdQuit", "CmdCharCreate",
|
__all__ = ("CmdOOCLook", "CmdIC", "CmdOOC", "CmdPassword", "CmdQuit",
|
||||||
"CmdEncoding", "CmdSessions", "CmdWho", "CmdColorTest", "CmdQuell")
|
"CmdCharCreate", "CmdEncoding", "CmdSessions", "CmdWho",
|
||||||
|
"CmdColorTest", "CmdQuell")
|
||||||
|
|
||||||
# force max nr chars to 1 if mode is 0 or 1
|
# force max nr chars to 1 if mode is 0 or 1
|
||||||
MAX_NR_CHARACTERS = MULTISESSION_MODE < 2 and 1 or MAX_NR_CHARACTERS
|
MAX_NR_CHARACTERS = MULTISESSION_MODE < 2 and 1 or MAX_NR_CHARACTERS
|
||||||
|
|
@ -58,7 +59,8 @@ class CmdOOCLook(MuxPlayerCommand):
|
||||||
"Hook method for when an argument is given."
|
"Hook method for when an argument is given."
|
||||||
player = self.caller
|
player = self.caller
|
||||||
key = self.args.lower()
|
key = self.args.lower()
|
||||||
chars = dict((utils.to_str(char.key.lower()), char) for char in player.db._playable_characters)
|
chars = dict((utils.to_str(char.key.lower()), char)
|
||||||
|
for char in player.db._playable_characters)
|
||||||
looktarget = chars.get(key)
|
looktarget = chars.get(key)
|
||||||
if looktarget:
|
if looktarget:
|
||||||
self.msg(looktarget.return_appearance(player))
|
self.msg(looktarget.return_appearance(player))
|
||||||
|
|
@ -101,7 +103,7 @@ class CmdOOCLook(MuxPlayerCommand):
|
||||||
string += "\n\nAvailable character%s (%i/unlimited):" % (string_s_ending, len(characters))
|
string += "\n\nAvailable character%s (%i/unlimited):" % (string_s_ending, len(characters))
|
||||||
else:
|
else:
|
||||||
string += "\n\nAvailable character%s%s:" % (string_s_ending,
|
string += "\n\nAvailable character%s%s:" % (string_s_ending,
|
||||||
MAX_NR_CHARACTERS > 1 and " (%i/%i)" % (len(characters), MAX_NR_CHARACTERS) or "")
|
MAX_NR_CHARACTERS > 1 and " (%i/%i)" % (len(characters), MAX_NR_CHARACTERS) or "")
|
||||||
|
|
||||||
for char in characters:
|
for char in characters:
|
||||||
csessid = char.sessid
|
csessid = char.sessid
|
||||||
|
|
@ -171,8 +173,10 @@ class CmdCharCreate(MuxPlayerCommand):
|
||||||
typeclass = settings.BASE_CHARACTER_TYPECLASS
|
typeclass = settings.BASE_CHARACTER_TYPECLASS
|
||||||
permissions = settings.PERMISSION_PLAYER_DEFAULT
|
permissions = settings.PERMISSION_PLAYER_DEFAULT
|
||||||
|
|
||||||
new_character = create.create_object(typeclass, key=key, location=default_home,
|
new_character = create.create_object(typeclass, key=key,
|
||||||
home=default_home, permissions=permissions)
|
location=default_home,
|
||||||
|
home=default_home,
|
||||||
|
permissions=permissions)
|
||||||
# only allow creator (and immortals) to puppet this char
|
# only allow creator (and immortals) to puppet this char
|
||||||
new_character.locks.add("puppet:id(%i) or pid(%i) or perm(Immortals) or pperm(Immortals)" %
|
new_character.locks.add("puppet:id(%i) or pid(%i) or perm(Immortals) or pperm(Immortals)" %
|
||||||
(new_character.id, player.id))
|
(new_character.id, player.id))
|
||||||
|
|
@ -203,7 +207,8 @@ class CmdIC(MuxPlayerCommand):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
key = "@ic"
|
key = "@ic"
|
||||||
locks = "cmd:all()" # must be all() or different puppeted objects won't be able to access it.
|
# lockmust be all() for different puppeted objects to access it.
|
||||||
|
locks = "cmd:all()"
|
||||||
aliases = "@puppet"
|
aliases = "@puppet"
|
||||||
help_category = "General"
|
help_category = "General"
|
||||||
|
|
||||||
|
|
@ -235,7 +240,8 @@ class CmdIC(MuxPlayerCommand):
|
||||||
if new_character.player:
|
if new_character.player:
|
||||||
# may not puppet an already puppeted character
|
# may not puppet an already puppeted character
|
||||||
if new_character.sessid and new_character.player == player:
|
if new_character.sessid and new_character.player == player:
|
||||||
# as a safeguard we allow "taking over chars from your own sessions.
|
# as a safeguard we allow "taking over chars from
|
||||||
|
# your own sessions.
|
||||||
player.msg("{c%s{n{R is now acted from another of your sessions.{n" % (new_character.name), sessid=new_character.sessid)
|
player.msg("{c%s{n{R is now acted from another of your sessions.{n" % (new_character.name), sessid=new_character.sessid)
|
||||||
player.unpuppet_object(new_character.sessid)
|
player.unpuppet_object(new_character.sessid)
|
||||||
self.msg("Taking over {c%s{n from another of your sessions." % new_character.name)
|
self.msg("Taking over {c%s{n from another of your sessions." % new_character.name)
|
||||||
|
|
@ -251,6 +257,7 @@ class CmdIC(MuxPlayerCommand):
|
||||||
else:
|
else:
|
||||||
self.msg("{rYou cannot become {C%s{n." % new_character.name)
|
self.msg("{rYou cannot become {C%s{n." % new_character.name)
|
||||||
|
|
||||||
|
|
||||||
class CmdOOC(MuxPlayerCommand):
|
class CmdOOC(MuxPlayerCommand):
|
||||||
"""
|
"""
|
||||||
go ooc
|
go ooc
|
||||||
|
|
@ -264,7 +271,8 @@ class CmdOOC(MuxPlayerCommand):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
key = "@ooc"
|
key = "@ooc"
|
||||||
locks = "cmd:all()" # this must be all(), or different puppeted objects won't be able to access it.
|
# lock must be all(), for different puppeted objects to access it.
|
||||||
|
locks = "cmd:all()"
|
||||||
aliases = "@unpuppet"
|
aliases = "@unpuppet"
|
||||||
help_category = "General"
|
help_category = "General"
|
||||||
|
|
||||||
|
|
@ -311,17 +319,22 @@ class CmdSessions(MuxPlayerCommand):
|
||||||
player = self.caller
|
player = self.caller
|
||||||
sessions = player.get_all_sessions()
|
sessions = player.get_all_sessions()
|
||||||
|
|
||||||
table = prettytable.PrettyTable(["{wsessid", "{wprotocol", "{whost", "{wpuppet/character", "{wlocation"])
|
table = prettytable.PrettyTable(["{wsessid",
|
||||||
for sess in sorted(sessions, key=lambda x:x.sessid):
|
"{wprotocol",
|
||||||
|
"{whost",
|
||||||
|
"{wpuppet/character",
|
||||||
|
"{wlocation"])
|
||||||
|
for sess in sorted(sessions, key=lambda x: x.sessid):
|
||||||
sessid = sess.sessid
|
sessid = sess.sessid
|
||||||
char = player.get_puppet(sessid)
|
char = player.get_puppet(sessid)
|
||||||
table.add_row([str(sessid), str(sess.protocol_key),
|
table.add_row([str(sessid), str(sess.protocol_key),
|
||||||
type(sess.address)==tuple and sess.address[0] or sess.address,
|
type(sess.address) == tuple and sess.address[0] or sess.address,
|
||||||
char and str(char) or "None",
|
char and str(char) or "None",
|
||||||
char and str(char.location) or "N/A"])
|
char and str(char.location) or "N/A"])
|
||||||
string = "{wYour current session(s):{n\n%s" % table
|
string = "{wYour current session(s):{n\n%s" % table
|
||||||
self.msg(string)
|
self.msg(string)
|
||||||
|
|
||||||
|
|
||||||
class CmdWho(MuxPlayerCommand):
|
class CmdWho(MuxPlayerCommand):
|
||||||
"""
|
"""
|
||||||
who
|
who
|
||||||
|
|
@ -355,7 +368,13 @@ class CmdWho(MuxPlayerCommand):
|
||||||
|
|
||||||
nplayers = (SESSIONS.player_count())
|
nplayers = (SESSIONS.player_count())
|
||||||
if show_session_data:
|
if show_session_data:
|
||||||
table = prettytable.PrettyTable(["{wPlayer Name","{wOn for", "{wIdle", "{wRoom", "{wCmds", "{wProtocol", "{wHost"])
|
table = prettytable.PrettyTable(["{wPlayer Name",
|
||||||
|
"{wOn for",
|
||||||
|
"{wIdle",
|
||||||
|
"{wRoom",
|
||||||
|
"{wCmds",
|
||||||
|
"{wProtocol",
|
||||||
|
"{wHost"])
|
||||||
for session in session_list:
|
for session in session_list:
|
||||||
if not session.logged_in: continue
|
if not session.logged_in: continue
|
||||||
delta_cmd = time.time() - session.cmd_last_visible
|
delta_cmd = time.time() - session.cmd_last_visible
|
||||||
|
|
@ -372,7 +391,8 @@ class CmdWho(MuxPlayerCommand):
|
||||||
else:
|
else:
|
||||||
table = prettytable.PrettyTable(["{wPlayer name", "{wOn for", "{wIdle"])
|
table = prettytable.PrettyTable(["{wPlayer name", "{wOn for", "{wIdle"])
|
||||||
for session in session_list:
|
for session in session_list:
|
||||||
if not session.logged_in: continue
|
if not session.logged_in:
|
||||||
|
continue
|
||||||
delta_cmd = time.time() - session.cmd_last_visible
|
delta_cmd = time.time() - session.cmd_last_visible
|
||||||
delta_conn = time.time() - session.conn_time
|
delta_conn = time.time() - session.conn_time
|
||||||
plr_pobject = session.get_puppet()
|
plr_pobject = session.get_puppet()
|
||||||
|
|
@ -397,14 +417,16 @@ class CmdEncoding(MuxPlayerCommand):
|
||||||
clear - clear your custom encoding
|
clear - clear your custom encoding
|
||||||
|
|
||||||
|
|
||||||
This sets the text encoding for communicating with Evennia. This is mostly an issue only if
|
This sets the text encoding for communicating with Evennia. This is mostly
|
||||||
you want to use non-ASCII characters (i.e. letters/symbols not found in English). If you see
|
an issue only if you want to use non-ASCII characters (i.e. letters/symbols
|
||||||
that your characters look strange (or you get encoding errors), you should use this command
|
not found in English). If you see that your characters look strange (or you
|
||||||
to set the server encoding to be the same used in your client program.
|
get encoding errors), you should use this command to set the server
|
||||||
|
encoding to be the same used in your client program.
|
||||||
|
|
||||||
Common encodings are utf-8 (default), latin-1, ISO-8859-1 etc.
|
Common encodings are utf-8 (default), latin-1, ISO-8859-1 etc.
|
||||||
|
|
||||||
If you don't submit an encoding, the current encoding will be displayed instead.
|
If you don't submit an encoding, the current encoding will be displayed
|
||||||
|
instead.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
key = "@encoding"
|
key = "@encoding"
|
||||||
|
|
@ -444,6 +466,7 @@ class CmdEncoding(MuxPlayerCommand):
|
||||||
string = "Your custom text encoding was changed from '%s' to '%s'." % (old_encoding, encoding)
|
string = "Your custom text encoding was changed from '%s' to '%s'." % (old_encoding, encoding)
|
||||||
self.msg(string.strip())
|
self.msg(string.strip())
|
||||||
|
|
||||||
|
|
||||||
class CmdPassword(MuxPlayerCommand):
|
class CmdPassword(MuxPlayerCommand):
|
||||||
"""
|
"""
|
||||||
@password - set your password
|
@password - set your password
|
||||||
|
|
@ -463,8 +486,8 @@ class CmdPassword(MuxPlayerCommand):
|
||||||
if not self.rhs:
|
if not self.rhs:
|
||||||
self.msg("Usage: @password <oldpass> = <newpass>")
|
self.msg("Usage: @password <oldpass> = <newpass>")
|
||||||
return
|
return
|
||||||
oldpass = self.lhslist[0] # this is already stripped by parse()
|
oldpass = self.lhslist[0] # this is already stripped by parse()
|
||||||
newpass = self.rhslist[0] # ''
|
newpass = self.rhslist[0] # ''
|
||||||
if not player.check_password(oldpass):
|
if not player.check_password(oldpass):
|
||||||
self.msg("The specified old password isn't correct.")
|
self.msg("The specified old password isn't correct.")
|
||||||
elif len(newpass) < 3:
|
elif len(newpass) < 3:
|
||||||
|
|
@ -474,6 +497,7 @@ class CmdPassword(MuxPlayerCommand):
|
||||||
player.save()
|
player.save()
|
||||||
self.msg("Password changed.")
|
self.msg("Password changed.")
|
||||||
|
|
||||||
|
|
||||||
class CmdQuit(MuxPlayerCommand):
|
class CmdQuit(MuxPlayerCommand):
|
||||||
"""
|
"""
|
||||||
quit
|
quit
|
||||||
|
|
@ -518,10 +542,10 @@ class CmdColorTest(MuxPlayerCommand):
|
||||||
Usage:
|
Usage:
|
||||||
@color ansi|xterm256
|
@color ansi|xterm256
|
||||||
|
|
||||||
Print a color map along with in-mud color codes, while testing what is supported in your client.
|
Print a color map along with in-mud color codes, while testing what is
|
||||||
Choices are 16-color ansi (supported in most muds) or the 256-color xterm256 standard.
|
supported in your client. Choices are 16-color ansi (supported in most
|
||||||
No checking is done to determine your client supports color - if not you will
|
muds) or the 256-color xterm256 standard. No checking is done to determine
|
||||||
see rubbish appear.
|
your client supports color - if not you will see rubbish appear.
|
||||||
"""
|
"""
|
||||||
key = "@color"
|
key = "@color"
|
||||||
locks = "cmd:all()"
|
locks = "cmd:all()"
|
||||||
|
|
@ -552,10 +576,10 @@ class CmdColorTest(MuxPlayerCommand):
|
||||||
ap = ansi.ANSI_PARSER
|
ap = ansi.ANSI_PARSER
|
||||||
# ansi colors
|
# ansi colors
|
||||||
# show all ansi color-related codes
|
# show all ansi color-related codes
|
||||||
col1 = ["%s%s{n" % (code, code.replace("{","{{")) for code, _ in ap.ext_ansi_map[:-1]]
|
col1 = ["%s%s{n" % (code, code.replace("{", "{{")) for code, _ in ap.ext_ansi_map[:-1]]
|
||||||
hi = "%ch"
|
hi = "%ch"
|
||||||
col2 = ["%s%s{n" % (code, code.replace("%", "%%")) for code, _ in ap.mux_ansi_map[3:-2]]
|
col2 = ["%s%s{n" % (code, code.replace("%", "%%")) for code, _ in ap.mux_ansi_map[3:-2]]
|
||||||
col3 = ["%s%s{n" % (hi+code, (hi+code).replace("%", "%%")) for code, _ in ap.mux_ansi_map[3:-2]]
|
col3 = ["%s%s{n" % (hi + code, (hi + code).replace("%", "%%")) for code, _ in ap.mux_ansi_map[3:-2]]
|
||||||
table = utils.format_table([col1, col2, col3], extra_space=1)
|
table = utils.format_table([col1, col2, col3], extra_space=1)
|
||||||
string = "ANSI colors:"
|
string = "ANSI colors:"
|
||||||
for row in table:
|
for row in table:
|
||||||
|
|
@ -566,16 +590,16 @@ class CmdColorTest(MuxPlayerCommand):
|
||||||
|
|
||||||
elif self.args.startswith("x"):
|
elif self.args.startswith("x"):
|
||||||
# show xterm256 table
|
# show xterm256 table
|
||||||
table = [[],[],[],[],[],[],[],[],[],[],[],[]]
|
table = [[], [], [], [], [], [], [], [], [], [], [], []]
|
||||||
for ir in range(6):
|
for ir in range(6):
|
||||||
for ig in range(6):
|
for ig in range(6):
|
||||||
for ib in range(6):
|
for ib in range(6):
|
||||||
# foreground table
|
# foreground table
|
||||||
table[ir].append("%%c%i%i%i%s{n" % (ir,ig,ib, "{{%i%i%i" % (ir,ig,ib)))
|
table[ir].append("%%c%i%i%i%s{n" % (ir, ig, ib, "{{%i%i%i" % (ir, ig, ib)))
|
||||||
# background table
|
# background table
|
||||||
table[6+ir].append("%%cb%i%i%i%%c%i%i%i%s{n" % (ir,ig,ib,
|
table[6+ir].append("%%cb%i%i%i%%c%i%i%i%s{n" % (ir, ig, ib,
|
||||||
5-ir,5-ig,5-ib,
|
5 - ir, 5 - ig, 5 - ib,
|
||||||
"{{b%i%i%i" % (ir,ig,ib)))
|
"{{b%i%i%i" % (ir, ig, ib)))
|
||||||
table = self.table_format(table)
|
table = self.table_format(table)
|
||||||
string = "Xterm256 colors (if not all hues show, your client might not report that it can handle xterm256):"
|
string = "Xterm256 colors (if not all hues show, your client might not report that it can handle xterm256):"
|
||||||
for row in table:
|
for row in table:
|
||||||
|
|
@ -586,6 +610,7 @@ class CmdColorTest(MuxPlayerCommand):
|
||||||
# malformed input
|
# malformed input
|
||||||
self.msg("Usage: @color ansi|xterm256")
|
self.msg("Usage: @color ansi|xterm256")
|
||||||
|
|
||||||
|
|
||||||
class CmdQuell(MuxPlayerCommand):
|
class CmdQuell(MuxPlayerCommand):
|
||||||
"""
|
"""
|
||||||
Quelling permissions
|
Quelling permissions
|
||||||
|
|
@ -604,7 +629,7 @@ class CmdQuell(MuxPlayerCommand):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
key = "@quell"
|
key = "@quell"
|
||||||
aliases =["@unquell"]
|
aliases = ["@unquell"]
|
||||||
locks = "cmd:all()"
|
locks = "cmd:all()"
|
||||||
help_category = "General"
|
help_category = "General"
|
||||||
|
|
||||||
|
|
@ -613,8 +638,9 @@ class CmdQuell(MuxPlayerCommand):
|
||||||
if self.sessid:
|
if self.sessid:
|
||||||
char = player.get_puppet(self.sessid)
|
char = player.get_puppet(self.sessid)
|
||||||
if char:
|
if char:
|
||||||
# we are already puppeting an object. We need to reset the lock caches
|
# we are already puppeting an object. We need to reset
|
||||||
# (otherwise the superuser status change won't be visible until repuppet)
|
# the lock caches (otherwise the superuser status change
|
||||||
|
# won't be visible until repuppet)
|
||||||
char.locks.reset()
|
char.locks.reset()
|
||||||
player.locks.reset()
|
player.locks.reset()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ from src.commands.default.muxcommand import MuxCommand
|
||||||
# Command called when there is no input at line
|
# Command called when there is no input at line
|
||||||
# (i.e. an lone return key)
|
# (i.e. an lone return key)
|
||||||
|
|
||||||
|
|
||||||
class SystemNoInput(MuxCommand):
|
class SystemNoInput(MuxCommand):
|
||||||
"""
|
"""
|
||||||
This is called when there is no input given
|
This is called when there is no input given
|
||||||
|
|
@ -44,11 +45,11 @@ class SystemNoInput(MuxCommand):
|
||||||
"Do nothing."
|
"Do nothing."
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Command called when there was no match to the
|
# Command called when there was no match to the
|
||||||
# command name
|
# command name
|
||||||
#
|
#
|
||||||
|
|
||||||
class SystemNoMatch(MuxCommand):
|
class SystemNoMatch(MuxCommand):
|
||||||
"""
|
"""
|
||||||
No command was found matching the given input.
|
No command was found matching the given input.
|
||||||
|
|
@ -62,6 +63,7 @@ class SystemNoMatch(MuxCommand):
|
||||||
"""
|
"""
|
||||||
self.caller.msg("Huh?")
|
self.caller.msg("Huh?")
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Command called when there were mulitple matches to the command.
|
# Command called when there were mulitple matches to the command.
|
||||||
#
|
#
|
||||||
|
|
@ -100,7 +102,7 @@ class SystemMultimatch(MuxCommand):
|
||||||
is_channel = ""
|
is_channel = ""
|
||||||
is_exit = hasattr(cmd, "is_exit") and cmd.is_exit
|
is_exit = hasattr(cmd, "is_exit") and cmd.is_exit
|
||||||
if is_exit and cmd.destination:
|
if is_exit and cmd.destination:
|
||||||
is_exit = " (exit to %s)" % cmd.destination
|
is_exit = " (exit to %s)" % cmd.destination
|
||||||
else:
|
else:
|
||||||
is_exit = ""
|
is_exit = ""
|
||||||
|
|
||||||
|
|
@ -124,6 +126,7 @@ class SystemMultimatch(MuxCommand):
|
||||||
string = self.format_multimatches(self.caller, self.matches)
|
string = self.format_multimatches(self.caller, self.matches)
|
||||||
self.caller.msg(string)
|
self.caller.msg(string)
|
||||||
|
|
||||||
|
|
||||||
# Command called when the command given at the command line
|
# Command called when the command given at the command line
|
||||||
# was identified as a channel name, like there existing a
|
# was identified as a channel name, like there existing a
|
||||||
# channel named 'ooc' and the user wrote
|
# channel named 'ooc' and the user wrote
|
||||||
|
|
@ -167,4 +170,4 @@ class SystemSendToChannel(MuxCommand):
|
||||||
return
|
return
|
||||||
msg = "[%s] %s: %s" % (channel.key, caller.name, msg)
|
msg = "[%s] %s: %s" % (channel.key, caller.name, msg)
|
||||||
msgobj = create.create_message(caller, msg, channels=[channel])
|
msgobj = create.create_message(caller, msg, channels=[channel])
|
||||||
channel.msg(msgobj)
|
channel.msg(msgobj)
|
||||||
|
|
@ -5,10 +5,13 @@ System commands
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import traceback
|
import traceback
|
||||||
import os, datetime, time
|
import os
|
||||||
from time import time as timemeasure
|
import datetime
|
||||||
|
import time
|
||||||
import sys
|
import sys
|
||||||
import django, twisted
|
import django
|
||||||
|
import twisted
|
||||||
|
from time import time as timemeasure
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from src.server.caches import get_cache_sizes
|
from src.server.caches import get_cache_sizes
|
||||||
|
|
@ -30,6 +33,7 @@ __all__ = ("CmdReload", "CmdReset", "CmdShutdown", "CmdPy",
|
||||||
"CmdScripts", "CmdObjects", "CmdService", "CmdAbout",
|
"CmdScripts", "CmdObjects", "CmdService", "CmdAbout",
|
||||||
"CmdTime", "CmdServerLoad")
|
"CmdTime", "CmdServerLoad")
|
||||||
|
|
||||||
|
|
||||||
class CmdReload(MuxCommand):
|
class CmdReload(MuxCommand):
|
||||||
"""
|
"""
|
||||||
Reload the system
|
Reload the system
|
||||||
|
|
@ -55,6 +59,7 @@ class CmdReload(MuxCommand):
|
||||||
SESSIONS.announce_all(" Server restarting %s..." % reason)
|
SESSIONS.announce_all(" Server restarting %s..." % reason)
|
||||||
SESSIONS.server.shutdown(mode='reload')
|
SESSIONS.server.shutdown(mode='reload')
|
||||||
|
|
||||||
|
|
||||||
class CmdReset(MuxCommand):
|
class CmdReset(MuxCommand):
|
||||||
"""
|
"""
|
||||||
Reset and reboot the system
|
Reset and reboot the system
|
||||||
|
|
@ -110,6 +115,7 @@ class CmdShutdown(MuxCommand):
|
||||||
SESSIONS.portal_shutdown()
|
SESSIONS.portal_shutdown()
|
||||||
SESSIONS.server.shutdown(mode='shutdown')
|
SESSIONS.server.shutdown(mode='shutdown')
|
||||||
|
|
||||||
|
|
||||||
class CmdPy(MuxCommand):
|
class CmdPy(MuxCommand):
|
||||||
"""
|
"""
|
||||||
Execute a snippet of python code
|
Execute a snippet of python code
|
||||||
|
|
@ -157,18 +163,17 @@ class CmdPy(MuxCommand):
|
||||||
|
|
||||||
# import useful variables
|
# import useful variables
|
||||||
import ev
|
import ev
|
||||||
available_vars = {'self':caller,
|
available_vars = {'self': caller,
|
||||||
'me':caller,
|
'me': caller,
|
||||||
'here':hasattr(caller, "location") and caller.location or None,
|
'here': hasattr(caller, "location") and caller.location or None,
|
||||||
'ev':ev,
|
'ev': ev,
|
||||||
'inherits_from':utils.inherits_from}
|
'inherits_from': utils.inherits_from}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.msg(">>> %s" % pycode, raw=True, sessid=self.sessid)
|
self.msg(">>> %s" % pycode, raw=True, sessid=self.sessid)
|
||||||
except TypeError:
|
except TypeError:
|
||||||
self.msg(">>> %s" % pycode, raw=True)
|
self.msg(">>> %s" % pycode, raw=True)
|
||||||
|
|
||||||
|
|
||||||
mode = "eval"
|
mode = "eval"
|
||||||
try:
|
try:
|
||||||
try:
|
try:
|
||||||
|
|
@ -195,7 +200,7 @@ class CmdPy(MuxCommand):
|
||||||
errlist = errlist[4:]
|
errlist = errlist[4:]
|
||||||
ret = "\n".join("{n<<< %s" % line for line in errlist if line)
|
ret = "\n".join("{n<<< %s" % line for line in errlist if line)
|
||||||
|
|
||||||
if ret != None:
|
if ret is not None:
|
||||||
try:
|
try:
|
||||||
self.msg(ret, sessid=self.sessid)
|
self.msg(ret, sessid=self.sessid)
|
||||||
except TypeError:
|
except TypeError:
|
||||||
|
|
@ -210,7 +215,16 @@ def format_script_list(scripts):
|
||||||
if not scripts:
|
if not scripts:
|
||||||
return "<No scripts>"
|
return "<No scripts>"
|
||||||
|
|
||||||
table = prettytable.PrettyTable(["{wid","{wobj","{wkey","{wintval","{wnext","{wrept","{wdb"," {wtypeclass","{wdesc"],align='r')
|
table = prettytable.PrettyTable(["{wid",
|
||||||
|
"{wobj",
|
||||||
|
"{wkey",
|
||||||
|
"{wintval",
|
||||||
|
"{wnext",
|
||||||
|
"{wrept",
|
||||||
|
"{wdb",
|
||||||
|
"{wtypeclass",
|
||||||
|
"{wdesc"],
|
||||||
|
align='r')
|
||||||
table.align = 'r'
|
table.align = 'r'
|
||||||
for script in scripts:
|
for script in scripts:
|
||||||
nextrep = script.time_until_next_repeat()
|
nextrep = script.time_until_next_repeat()
|
||||||
|
|
@ -322,7 +336,6 @@ class CmdScripts(MuxCommand):
|
||||||
caller.msg(string)
|
caller.msg(string)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class CmdObjects(MuxCommand):
|
class CmdObjects(MuxCommand):
|
||||||
"""
|
"""
|
||||||
@objects - Give a summary of object types in database
|
@objects - Give a summary of object types in database
|
||||||
|
|
@ -357,32 +370,37 @@ class CmdObjects(MuxCommand):
|
||||||
nother = nobjs - nchars - nrooms - nexits
|
nother = nobjs - nchars - nrooms - nexits
|
||||||
|
|
||||||
# total object sum table
|
# total object sum table
|
||||||
totaltable = prettytable.PrettyTable(["{wtype","{wcomment","{wcount", "{w%%"])
|
totaltable = prettytable.PrettyTable(["{wtype", "{wcomment", "{wcount", "{w%%"])
|
||||||
totaltable.align = 'l'
|
totaltable.align = 'l'
|
||||||
totaltable.add_row(["Characters", "(BASE_CHARACTER_TYPECLASS)", nchars, "%.2f" % ((float(nchars)/nobjs)*100)])
|
totaltable.add_row(["Characters", "(BASE_CHARACTER_TYPECLASS)", nchars, "%.2f" % ((float(nchars) / nobjs) * 100)])
|
||||||
totaltable.add_row(["Rooms", "(location=None)", nrooms, "%.2f" % ((float(nrooms)/nobjs)*100)])
|
totaltable.add_row(["Rooms", "(location=None)", nrooms, "%.2f" % ((float(nrooms) / nobjs) * 100)])
|
||||||
totaltable.add_row(["Exits", "(destination!=None)", nexits, "%.2f" % ((float(nexits)/nobjs)*100)])
|
totaltable.add_row(["Exits", "(destination!=None)", nexits, "%.2f" % ((float(nexits) / nobjs) * 100)])
|
||||||
totaltable.add_row(["Other", "", nother, "%.2f" % ((float(nother)/nobjs)*100)])
|
totaltable.add_row(["Other", "", nother, "%.2f" % ((float(nother) / nobjs) * 100)])
|
||||||
|
|
||||||
# typeclass table
|
# typeclass table
|
||||||
typetable = prettytable.PrettyTable(["{wtypeclass","{wcount", "{w%%"])
|
typetable = prettytable.PrettyTable(["{wtypeclass", "{wcount", "{w%%"])
|
||||||
typetable.align = 'l'
|
typetable.align = 'l'
|
||||||
dbtotals = ObjectDB.objects.object_totals()
|
dbtotals = ObjectDB.objects.object_totals()
|
||||||
for path, count in dbtotals.items():
|
for path, count in dbtotals.items():
|
||||||
typetable.add_row([path, count, "%.2f" % ((float(count)/nobjs)*100)])
|
typetable.add_row([path, count, "%.2f" % ((float(count) / nobjs) * 100)])
|
||||||
|
|
||||||
# last N table
|
# last N table
|
||||||
objs = ObjectDB.objects.all().order_by("db_date_created")[max(0, nobjs - nlim):]
|
objs = ObjectDB.objects.all().order_by("db_date_created")[max(0, nobjs - nlim):]
|
||||||
latesttable = prettytable.PrettyTable(["{wcreated","{wdbref","{wname","{wtypeclass"])
|
latesttable = prettytable.PrettyTable(["{wcreated",
|
||||||
|
"{wdbref",
|
||||||
|
"{wname",
|
||||||
|
"{wtypeclass"])
|
||||||
latesttable.align = 'l'
|
latesttable.align = 'l'
|
||||||
for obj in objs:
|
for obj in objs:
|
||||||
latesttable.add_row([utils.datetime_format(obj.date_created), obj.dbref, obj.key, obj.typeclass.path])
|
latesttable.add_row([utils.datetime_format(obj.date_created),
|
||||||
|
obj.dbref, obj.key, obj.typeclass.path])
|
||||||
|
|
||||||
string = "\n{wObject subtype totals (out of %i Objects):{n\n%s" % (nobjs, totaltable)
|
string = "\n{wObject subtype totals (out of %i Objects):{n\n%s" % (nobjs, totaltable)
|
||||||
string += "\n{wObject typeclass distribution:{n\n%s" % typetable
|
string += "\n{wObject typeclass distribution:{n\n%s" % typetable
|
||||||
string += "\n{wLast %s Objects created:{n\n%s" % (min(nobjs, nlim), latesttable)
|
string += "\n{wLast %s Objects created:{n\n%s" % (min(nobjs, nlim), latesttable)
|
||||||
caller.msg(string)
|
caller.msg(string)
|
||||||
|
|
||||||
|
|
||||||
class CmdPlayers(MuxCommand):
|
class CmdPlayers(MuxCommand):
|
||||||
"""
|
"""
|
||||||
@players - give a summary of all registed Players
|
@players - give a summary of all registed Players
|
||||||
|
|
@ -397,6 +415,7 @@ class CmdPlayers(MuxCommand):
|
||||||
key = "@players"
|
key = "@players"
|
||||||
aliases = ["@listplayers"]
|
aliases = ["@listplayers"]
|
||||||
locks = "cmd:perm(listplayers) or perm(Wizards)"
|
locks = "cmd:perm(listplayers) or perm(Wizards)"
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"List the players"
|
"List the players"
|
||||||
|
|
||||||
|
|
@ -413,10 +432,10 @@ class CmdPlayers(MuxCommand):
|
||||||
typetable = prettytable.PrettyTable(["{wtypeclass", "{wcount", "{w%%"])
|
typetable = prettytable.PrettyTable(["{wtypeclass", "{wcount", "{w%%"])
|
||||||
typetable.align = 'l'
|
typetable.align = 'l'
|
||||||
for path, count in dbtotals.items():
|
for path, count in dbtotals.items():
|
||||||
typetable.add_row([path, count, "%.2f" % ((float(count)/nplayers)*100)])
|
typetable.add_row([path, count, "%.2f" % ((float(count) / nplayers) * 100)])
|
||||||
# last N table
|
# last N table
|
||||||
plyrs = PlayerDB.objects.all().order_by("db_date_created")[max(0, nplayers - nlim):]
|
plyrs = PlayerDB.objects.all().order_by("db_date_created")[max(0, nplayers - nlim):]
|
||||||
latesttable = prettytable.PrettyTable(["{wcreated", "{wdbref","{wname","{wtypeclass"])
|
latesttable = prettytable.PrettyTable(["{wcreated", "{wdbref", "{wname", "{wtypeclass"])
|
||||||
latesttable.align = 'l'
|
latesttable.align = 'l'
|
||||||
for ply in plyrs:
|
for ply in plyrs:
|
||||||
latesttable.add_row([utils.datetime_format(ply.date_created), ply.dbref, ply.key, ply.typeclass.path])
|
latesttable.add_row([utils.datetime_format(ply.date_created), ply.dbref, ply.key, ply.typeclass.path])
|
||||||
|
|
@ -425,6 +444,7 @@ class CmdPlayers(MuxCommand):
|
||||||
string += "\n{wLast %s Players created:{n\n%s" % (min(nplayers, nlim), latesttable)
|
string += "\n{wLast %s Players created:{n\n%s" % (min(nplayers, nlim), latesttable)
|
||||||
caller.msg(string)
|
caller.msg(string)
|
||||||
|
|
||||||
|
|
||||||
class CmdService(MuxCommand):
|
class CmdService(MuxCommand):
|
||||||
"""
|
"""
|
||||||
@service - manage services
|
@service - manage services
|
||||||
|
|
@ -441,7 +461,8 @@ class CmdService(MuxCommand):
|
||||||
Service management system. Allows for the listing,
|
Service management system. Allows for the listing,
|
||||||
starting, and stopping of services. If no switches
|
starting, and stopping of services. If no switches
|
||||||
are given, services will be listed. Note that to operate on the
|
are given, services will be listed. Note that to operate on the
|
||||||
service you have to supply the full (green or red) name as given in the list.
|
service you have to supply the full (green or red) name as given
|
||||||
|
in the list.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
key = "@service"
|
key = "@service"
|
||||||
|
|
@ -520,6 +541,7 @@ class CmdService(MuxCommand):
|
||||||
caller.msg("Starting service '%s'." % self.args)
|
caller.msg("Starting service '%s'." % self.args)
|
||||||
service.startService()
|
service.startService()
|
||||||
|
|
||||||
|
|
||||||
class CmdAbout(MuxCommand):
|
class CmdAbout(MuxCommand):
|
||||||
"""
|
"""
|
||||||
@about - game engine info
|
@about - game engine info
|
||||||
|
|
@ -567,6 +589,7 @@ class CmdAbout(MuxCommand):
|
||||||
sversion)
|
sversion)
|
||||||
self.caller.msg(string)
|
self.caller.msg(string)
|
||||||
|
|
||||||
|
|
||||||
class CmdTime(MuxCommand):
|
class CmdTime(MuxCommand):
|
||||||
"""
|
"""
|
||||||
@time
|
@time
|
||||||
|
|
@ -591,6 +614,7 @@ class CmdTime(MuxCommand):
|
||||||
table.add_row(["Server time stamp", datetime.datetime.now()])
|
table.add_row(["Server time stamp", datetime.datetime.now()])
|
||||||
self.caller.msg(str(table))
|
self.caller.msg(str(table))
|
||||||
|
|
||||||
|
|
||||||
class CmdServerLoad(MuxCommand):
|
class CmdServerLoad(MuxCommand):
|
||||||
"""
|
"""
|
||||||
server load and memory statistics
|
server load and memory statistics
|
||||||
|
|
@ -648,20 +672,20 @@ class CmdServerLoad(MuxCommand):
|
||||||
pid = os.getpid()
|
pid = os.getpid()
|
||||||
rmem = float(os.popen('ps -p %d -o %s | tail -1' % (pid, "rss")).read()) / 1024.0 # resident memory
|
rmem = float(os.popen('ps -p %d -o %s | tail -1' % (pid, "rss")).read()) / 1024.0 # resident memory
|
||||||
vmem = float(os.popen('ps -p %d -o %s | tail -1' % (pid, "vsz")).read()) / 1024.0 # virtual memory
|
vmem = float(os.popen('ps -p %d -o %s | tail -1' % (pid, "vsz")).read()) / 1024.0 # virtual memory
|
||||||
pmem = float(os.popen('ps -p %d -o %s | tail -1' % (pid, "%mem")).read()) # percent of resident memory to total
|
pmem = float(os.popen('ps -p %d -o %s | tail -1' % (pid, "%mem")).read()) # percent of resident memory to total
|
||||||
rusage = resource.getrusage(resource.RUSAGE_SELF)
|
rusage = resource.getrusage(resource.RUSAGE_SELF)
|
||||||
|
|
||||||
# load table
|
# load table
|
||||||
loadtable = prettytable.PrettyTable(["property", "statistic"])
|
loadtable = prettytable.PrettyTable(["property", "statistic"])
|
||||||
loadtable.align = 'l'
|
loadtable.align = 'l'
|
||||||
loadtable.add_row(["Server load (1 min)","%g" % loadavg[0]])
|
loadtable.add_row(["Server load (1 min)", "%g" % loadavg[0]])
|
||||||
loadtable.add_row(["Process ID","%g" % pid]),
|
loadtable.add_row(["Process ID", "%g" % pid]),
|
||||||
loadtable.add_row(["Bytes per page","%g " % psize])
|
loadtable.add_row(["Bytes per page", "%g " % psize])
|
||||||
loadtable.add_row(["CPU time used (total)", "%s (%gs)" % (utils.time_format(rusage.ru_utime), rusage.ru_utime)])
|
loadtable.add_row(["CPU time used (total)", "%s (%gs)" % (utils.time_format(rusage.ru_utime), rusage.ru_utime)])
|
||||||
loadtable.add_row(["CPU time used (user)", "%s (%gs)" % (utils.time_format(rusage.ru_stime), rusage.ru_stime)])
|
loadtable.add_row(["CPU time used (user)", "%s (%gs)" % (utils.time_format(rusage.ru_stime), rusage.ru_stime)])
|
||||||
loadtable.add_row(["Memory usage","%g MB (%g%%)" % (rmem, pmem)])
|
loadtable.add_row(["Memory usage","%g MB (%g%%)" % (rmem, pmem)])
|
||||||
loadtable.add_row(["Virtual address space\n {x(resident+swap+caching){n", "%g MB" % vmem])
|
loadtable.add_row(["Virtual address space\n {x(resident+swap+caching){n", "%g MB" % vmem])
|
||||||
loadtable.add_row(["Page faults","%g hard, %g soft, %g swapouts" % (rusage.ru_majflt, rusage.ru_minflt, rusage.ru_nswap)])
|
loadtable.add_row(["Page faults", "%g hard, %g soft, %g swapouts" % (rusage.ru_majflt, rusage.ru_minflt, rusage.ru_nswap)])
|
||||||
loadtable.add_row(["Disk I/O", "%g reads, %g writes" % (rusage.ru_inblock, rusage.ru_oublock)])
|
loadtable.add_row(["Disk I/O", "%g reads, %g writes" % (rusage.ru_inblock, rusage.ru_oublock)])
|
||||||
loadtable.add_row(["Network I/O", "%g in, %g out" % (rusage.ru_msgrcv, rusage.ru_msgsnd)])
|
loadtable.add_row(["Network I/O", "%g in, %g out" % (rusage.ru_msgrcv, rusage.ru_msgsnd)])
|
||||||
loadtable.add_row(["Context switching", "%g vol, %g forced, %g signals" % (rusage.ru_nvcsw, rusage.ru_nivcsw, rusage.ru_nsignals)])
|
loadtable.add_row(["Context switching", "%g vol, %g forced, %g signals" % (rusage.ru_nvcsw, rusage.ru_nivcsw, rusage.ru_nsignals)])
|
||||||
|
|
@ -669,17 +693,24 @@ class CmdServerLoad(MuxCommand):
|
||||||
string = "{wServer CPU and Memory load:{n\n%s" % loadtable
|
string = "{wServer CPU and Memory load:{n\n%s" % loadtable
|
||||||
|
|
||||||
if not is_pypy:
|
if not is_pypy:
|
||||||
# Cache size measurements are not available on PyPy because it lacks sys.getsizeof
|
# Cache size measurements are not available on PyPy
|
||||||
|
# because it lacks sys.getsizeof
|
||||||
|
|
||||||
# object cache size
|
# object cache size
|
||||||
cachedict = _idmapper.cache_size()
|
cachedict = _idmapper.cache_size()
|
||||||
totcache = cachedict["_total"]
|
totcache = cachedict["_total"]
|
||||||
sorted_cache = sorted([(key, tup[0], tup[1]) for key, tup in cachedict.items() if key !="_total" and tup[0] > 0],
|
sorted_cache = sorted([(key, tup[0], tup[1]) for key, tup in cachedict.items() if key !="_total" and tup[0] > 0],
|
||||||
key=lambda tup: tup[2], reverse=True)
|
key=lambda tup: tup[2], reverse=True)
|
||||||
memtable = prettytable.PrettyTable(["entity name", "number", "cache (MB)", "idmapper %%"])
|
memtable = prettytable.PrettyTable(["entity name",
|
||||||
|
"number",
|
||||||
|
"cache (MB)",
|
||||||
|
"idmapper %%"])
|
||||||
memtable.align = 'l'
|
memtable.align = 'l'
|
||||||
for tup in sorted_cache:
|
for tup in sorted_cache:
|
||||||
memtable.add_row([tup[0], "%i" % tup [1], "%5.2f" % tup[2], "%.2f" % (float(tup[2]/totcache[1])*100)])
|
memtable.add_row([tup[0],
|
||||||
|
"%i" % tup[1],
|
||||||
|
"%5.2f" % tup[2],
|
||||||
|
"%.2f" % (float(tup[2] / totcache[1]) * 100)])
|
||||||
|
|
||||||
# get sizes of other caches
|
# get sizes of other caches
|
||||||
attr_cache_info, field_cache_info, prop_cache_info = get_cache_sizes()
|
attr_cache_info, field_cache_info, prop_cache_info = get_cache_sizes()
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ from django.utils.unittest import TestCase
|
||||||
from src.server.serversession import ServerSession
|
from src.server.serversession import ServerSession
|
||||||
from src.objects.objects import Object, Character
|
from src.objects.objects import Object, Character
|
||||||
from src.players.player import Player
|
from src.players.player import Player
|
||||||
from src.utils import create, utils, ansi
|
from src.utils import create, ansi
|
||||||
from src.server.sessionhandler import SESSIONS
|
from src.server.sessionhandler import SESSIONS
|
||||||
|
|
||||||
from django.db.models.signals import pre_save
|
from django.db.models.signals import pre_save
|
||||||
|
|
@ -33,16 +33,20 @@ _RE = re.compile(r"^\+|-+\+|\+-+|--*|\|", re.MULTILINE)
|
||||||
# Command testing
|
# Command testing
|
||||||
# ------------------------------------------------------------
|
# ------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
def dummy(self, *args, **kwargs):
|
def dummy(self, *args, **kwargs):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
SESSIONS.data_out = dummy
|
SESSIONS.data_out = dummy
|
||||||
SESSIONS.disconnect = dummy
|
SESSIONS.disconnect = dummy
|
||||||
|
|
||||||
|
|
||||||
class TestObjectClass(Object):
|
class TestObjectClass(Object):
|
||||||
def msg(self, text="", **kwargs):
|
def msg(self, text="", **kwargs):
|
||||||
"test message"
|
"test message"
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class TestCharacterClass(Character):
|
class TestCharacterClass(Character):
|
||||||
def msg(self, text="", **kwargs):
|
def msg(self, text="", **kwargs):
|
||||||
"test message"
|
"test message"
|
||||||
|
|
@ -52,17 +56,21 @@ class TestCharacterClass(Character):
|
||||||
if not self.ndb.stored_msg:
|
if not self.ndb.stored_msg:
|
||||||
self.ndb.stored_msg = []
|
self.ndb.stored_msg = []
|
||||||
self.ndb.stored_msg.append(text)
|
self.ndb.stored_msg.append(text)
|
||||||
|
|
||||||
|
|
||||||
class TestPlayerClass(Player):
|
class TestPlayerClass(Player):
|
||||||
def msg(self, text="", **kwargs):
|
def msg(self, text="", **kwargs):
|
||||||
"test message"
|
"test message"
|
||||||
if not self.ndb.stored_msg:
|
if not self.ndb.stored_msg:
|
||||||
self.ndb.stored_msg = []
|
self.ndb.stored_msg = []
|
||||||
self.ndb.stored_msg.append(text)
|
self.ndb.stored_msg.append(text)
|
||||||
|
|
||||||
def _get_superuser(self):
|
def _get_superuser(self):
|
||||||
"test with superuser flag"
|
"test with superuser flag"
|
||||||
return self.ndb.is_superuser
|
return self.ndb.is_superuser
|
||||||
is_superuser = property(_get_superuser)
|
is_superuser = property(_get_superuser)
|
||||||
|
|
||||||
|
|
||||||
class CommandTest(TestCase):
|
class CommandTest(TestCase):
|
||||||
"""
|
"""
|
||||||
Tests a command
|
Tests a command
|
||||||
|
|
@ -93,6 +101,7 @@ class CommandTest(TestCase):
|
||||||
SESSIONS.portal_connect(session.get_sync_data())
|
SESSIONS.portal_connect(session.get_sync_data())
|
||||||
SESSIONS.login(SESSIONS.session_from_sessid(self.CID), self.player, testmode=True)
|
SESSIONS.login(SESSIONS.session_from_sessid(self.CID), self.player, testmode=True)
|
||||||
|
|
||||||
|
|
||||||
def call(self, cmdobj, args, msg=None, cmdset=None, noansi=True, caller=None):
|
def call(self, cmdobj, args, msg=None, cmdset=None, noansi=True, caller=None):
|
||||||
"""
|
"""
|
||||||
Test a command by assigning all the needed
|
Test a command by assigning all the needed
|
||||||
|
|
@ -141,6 +150,7 @@ class CommandTest(TestCase):
|
||||||
from src.commands.default import general
|
from src.commands.default import general
|
||||||
class TestGeneral(CommandTest):
|
class TestGeneral(CommandTest):
|
||||||
CID = 1
|
CID = 1
|
||||||
|
|
||||||
def test_cmds(self):
|
def test_cmds(self):
|
||||||
self.call(general.CmdLook(), "here", "Room1\n room_desc")
|
self.call(general.CmdLook(), "here", "Room1\n room_desc")
|
||||||
self.call(general.CmdHome(), "", "You are already home")
|
self.call(general.CmdHome(), "", "You are already home")
|
||||||
|
|
@ -158,6 +168,7 @@ class TestGeneral(CommandTest):
|
||||||
self.call(general.CmdSay(), "Testing", "You say, \"Testing\"")
|
self.call(general.CmdSay(), "Testing", "You say, \"Testing\"")
|
||||||
self.call(general.CmdAccess(), "", "Permission Hierarchy (climbing):")
|
self.call(general.CmdAccess(), "", "Permission Hierarchy (climbing):")
|
||||||
|
|
||||||
|
|
||||||
from src.commands.default import help
|
from src.commands.default import help
|
||||||
from src.commands.default.cmdset_character import CharacterCmdSet
|
from src.commands.default.cmdset_character import CharacterCmdSet
|
||||||
class TestHelp(CommandTest):
|
class TestHelp(CommandTest):
|
||||||
|
|
@ -167,6 +178,7 @@ class TestHelp(CommandTest):
|
||||||
self.call(help.CmdSetHelp(), "testhelp, General = This is a test", "Topic 'testhelp' was successfully created.")
|
self.call(help.CmdSetHelp(), "testhelp, General = This is a test", "Topic 'testhelp' was successfully created.")
|
||||||
self.call(help.CmdHelp(), "testhelp", "Help topic for testhelp", cmdset=CharacterCmdSet())
|
self.call(help.CmdHelp(), "testhelp", "Help topic for testhelp", cmdset=CharacterCmdSet())
|
||||||
|
|
||||||
|
|
||||||
from src.commands.default import system
|
from src.commands.default import system
|
||||||
class TestSystem(CommandTest):
|
class TestSystem(CommandTest):
|
||||||
CID = 3
|
CID = 3
|
||||||
|
|
@ -179,6 +191,7 @@ class TestSystem(CommandTest):
|
||||||
self.call(system.CmdAbout(), "", None)
|
self.call(system.CmdAbout(), "", None)
|
||||||
self.call(system.CmdServerLoad(), "", "Server CPU and Memory load:")
|
self.call(system.CmdServerLoad(), "", "Server CPU and Memory load:")
|
||||||
|
|
||||||
|
|
||||||
from src.commands.default import admin
|
from src.commands.default import admin
|
||||||
class TestAdmin(CommandTest):
|
class TestAdmin(CommandTest):
|
||||||
CID = 4
|
CID = 4
|
||||||
|
|
@ -190,6 +203,7 @@ class TestAdmin(CommandTest):
|
||||||
self.call(admin.CmdPerm(), "Char4b = Builders","Permission 'Builders' given to Char4b.")
|
self.call(admin.CmdPerm(), "Char4b = Builders","Permission 'Builders' given to Char4b.")
|
||||||
self.call(admin.CmdBan(), "Char4", "NameBan char4 was added.")
|
self.call(admin.CmdBan(), "Char4", "NameBan char4 was added.")
|
||||||
|
|
||||||
|
|
||||||
from src.commands.default import player
|
from src.commands.default import player
|
||||||
class TestPlayer(CommandTest):
|
class TestPlayer(CommandTest):
|
||||||
CID = 5
|
CID = 5
|
||||||
|
|
@ -209,6 +223,7 @@ class TestPlayer(CommandTest):
|
||||||
self.call(player.CmdCharCreate(), "Test1=Test char","Created new character Test1. Use @ic Test1 to enter the game", caller=self.player)
|
self.call(player.CmdCharCreate(), "Test1=Test char","Created new character Test1. Use @ic Test1 to enter the game", caller=self.player)
|
||||||
self.call(player.CmdQuell(), "", "Quelling Player permissions (immortals). Use @unquell to get them back.", caller=self.player)
|
self.call(player.CmdQuell(), "", "Quelling Player permissions (immortals). Use @unquell to get them back.", caller=self.player)
|
||||||
|
|
||||||
|
|
||||||
from src.commands.default import building
|
from src.commands.default import building
|
||||||
class TestBuilding(CommandTest):
|
class TestBuilding(CommandTest):
|
||||||
CID = 6
|
CID = 6
|
||||||
|
|
@ -239,6 +254,7 @@ class TestBuilding(CommandTest):
|
||||||
self.call(building.CmdScript(), "Obj6 = src.scripts.scripts.Script", "Script src.scripts.scripts.Script successfully added")
|
self.call(building.CmdScript(), "Obj6 = src.scripts.scripts.Script", "Script src.scripts.scripts.Script successfully added")
|
||||||
self.call(building.CmdTeleport(), "TestRoom1", "TestRoom1\nExits: back|Teleported to TestRoom1.")
|
self.call(building.CmdTeleport(), "TestRoom1", "TestRoom1\nExits: back|Teleported to TestRoom1.")
|
||||||
|
|
||||||
|
|
||||||
from src.commands.default import comms
|
from src.commands.default import comms
|
||||||
class TestComms(CommandTest):
|
class TestComms(CommandTest):
|
||||||
CID = 7
|
CID = 7
|
||||||
|
|
@ -257,6 +273,7 @@ class TestComms(CommandTest):
|
||||||
self.call(comms.CmdCBoot(), "", "Usage: @cboot[/quiet] <channel> = <player> [:reason]") # noone else connected to boot
|
self.call(comms.CmdCBoot(), "", "Usage: @cboot[/quiet] <channel> = <player> [:reason]") # noone else connected to boot
|
||||||
self.call(comms.CmdCdestroy(), "testchan" ,"Channel 'testchan' was destroyed.")
|
self.call(comms.CmdCdestroy(), "testchan" ,"Channel 'testchan' was destroyed.")
|
||||||
|
|
||||||
|
|
||||||
from src.commands.default import batchprocess
|
from src.commands.default import batchprocess
|
||||||
class TestBatchProcess(CommandTest):
|
class TestBatchProcess(CommandTest):
|
||||||
CID = 8
|
CID = 8
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,8 @@ from src.commands.default.muxcommand import MuxCommand
|
||||||
from src.commands.cmdhandler import CMD_LOGINSTART
|
from src.commands.cmdhandler import CMD_LOGINSTART
|
||||||
|
|
||||||
# limit symbol import for API
|
# limit symbol import for API
|
||||||
__all__ = ("CmdUnconnectedConnect", "CmdUnconnectedCreate", "CmdUnconnectedQuit", "CmdUnconnectedLook", "CmdUnconnectedHelp")
|
__all__ = ("CmdUnconnectedConnect", "CmdUnconnectedCreate",
|
||||||
|
"CmdUnconnectedQuit", "CmdUnconnectedLook", "CmdUnconnectedHelp")
|
||||||
|
|
||||||
MULTISESSION_MODE = settings.MULTISESSION_MODE
|
MULTISESSION_MODE = settings.MULTISESSION_MODE
|
||||||
CONNECTION_SCREEN_MODULE = settings.CONNECTION_SCREEN_MODULE
|
CONNECTION_SCREEN_MODULE = settings.CONNECTION_SCREEN_MODULE
|
||||||
|
|
@ -26,6 +27,7 @@ except Exception:
|
||||||
if not CONNECTION_SCREEN:
|
if not CONNECTION_SCREEN:
|
||||||
CONNECTION_SCREEN = "\nEvennia: Error in CONNECTION_SCREEN MODULE (randomly picked connection screen variable is not a string). \nEnter 'help' for aid."
|
CONNECTION_SCREEN = "\nEvennia: Error in CONNECTION_SCREEN MODULE (randomly picked connection screen variable is not a string). \nEnter 'help' for aid."
|
||||||
|
|
||||||
|
|
||||||
class CmdUnconnectedConnect(MuxCommand):
|
class CmdUnconnectedConnect(MuxCommand):
|
||||||
"""
|
"""
|
||||||
Connect to the game.
|
Connect to the game.
|
||||||
|
|
@ -40,7 +42,7 @@ class CmdUnconnectedConnect(MuxCommand):
|
||||||
"""
|
"""
|
||||||
key = "connect"
|
key = "connect"
|
||||||
aliases = ["conn", "con", "co"]
|
aliases = ["conn", "con", "co"]
|
||||||
locks = "cmd:all()" # not really needed
|
locks = "cmd:all()" # not really needed
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -92,12 +94,13 @@ class CmdUnconnectedConnect(MuxCommand):
|
||||||
|
|
||||||
# actually do the login. This will call all other hooks:
|
# actually do the login. This will call all other hooks:
|
||||||
# session.at_login()
|
# session.at_login()
|
||||||
# player.at_init() # always called when object is loaded from disk
|
# player.at_init() # always called when object is loaded from disk
|
||||||
# player.at_pre_login()
|
# player.at_pre_login()
|
||||||
# player.at_first_login() # only once
|
# player.at_first_login() # only once
|
||||||
# player.at_post_login(sessid=sessid)
|
# player.at_post_login(sessid=sessid)
|
||||||
session.sessionhandler.login(session, player)
|
session.sessionhandler.login(session, player)
|
||||||
|
|
||||||
|
|
||||||
class CmdUnconnectedCreate(MuxCommand):
|
class CmdUnconnectedCreate(MuxCommand):
|
||||||
"""
|
"""
|
||||||
Create a new account.
|
Create a new account.
|
||||||
|
|
@ -134,8 +137,9 @@ class CmdUnconnectedCreate(MuxCommand):
|
||||||
|
|
||||||
# sanity checks
|
# sanity checks
|
||||||
if not re.findall('^[\w. @+-]+$', playername) or not (0 < len(playername) <= 30):
|
if not re.findall('^[\w. @+-]+$', playername) or not (0 < len(playername) <= 30):
|
||||||
# this echoes the restrictions made by django's auth module (except not
|
# this echoes the restrictions made by django's auth
|
||||||
# allowing spaces, for convenience of logging in).
|
# module (except not allowing spaces, for convenience of
|
||||||
|
# logging in).
|
||||||
string = "\n\r Playername can max be 30 characters or fewer. Letters, spaces, digits and @/./+/-/_ only."
|
string = "\n\r Playername can max be 30 characters or fewer. Letters, spaces, digits and @/./+/-/_ only."
|
||||||
session.msg(string)
|
session.msg(string)
|
||||||
return
|
return
|
||||||
|
|
@ -163,14 +167,14 @@ class CmdUnconnectedCreate(MuxCommand):
|
||||||
new_player = create.create_player(playername, None, password,
|
new_player = create.create_player(playername, None, password,
|
||||||
permissions=permissions)
|
permissions=permissions)
|
||||||
|
|
||||||
|
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
session.msg("There was an error creating the default Player/Character:\n%s\n If this problem persists, contact an admin." % e)
|
session.msg("There was an error creating the default Player/Character:\n%s\n If this problem persists, contact an admin." % e)
|
||||||
logger.log_trace()
|
logger.log_trace()
|
||||||
return
|
return
|
||||||
|
|
||||||
# This needs to be called so the engine knows this player is logging in for the first time.
|
# This needs to be called so the engine knows this player is
|
||||||
# (so it knows to call the right hooks during login later)
|
# logging in for the first time. (so it knows to call the right
|
||||||
|
# hooks during login later)
|
||||||
utils.init_new_player(new_player)
|
utils.init_new_player(new_player)
|
||||||
|
|
||||||
# join the new player to the public channel
|
# join the new player to the public channel
|
||||||
|
|
@ -181,7 +185,6 @@ class CmdUnconnectedCreate(MuxCommand):
|
||||||
string = "New player '%s' could not connect to public channel!" % new_player.key
|
string = "New player '%s' could not connect to public channel!" % new_player.key
|
||||||
logger.log_errmsg(string)
|
logger.log_errmsg(string)
|
||||||
|
|
||||||
|
|
||||||
if MULTISESSION_MODE < 2:
|
if MULTISESSION_MODE < 2:
|
||||||
# if we only allow one character, create one with the same name as Player
|
# if we only allow one character, create one with the same name as Player
|
||||||
# (in mode 2, the character must be created manually once logging in)
|
# (in mode 2, the character must be created manually once logging in)
|
||||||
|
|
@ -210,12 +213,14 @@ class CmdUnconnectedCreate(MuxCommand):
|
||||||
session.msg(string % (playername, playername))
|
session.msg(string % (playername, playername))
|
||||||
|
|
||||||
except Exception:
|
except Exception:
|
||||||
# We are in the middle between logged in and -not, so we have to handle tracebacks
|
# We are in the middle between logged in and -not, so we have
|
||||||
# ourselves at this point. If we don't, we won't see any errors at all.
|
# to handle tracebacks ourselves at this point. If we don't,
|
||||||
|
# we won't see any errors at all.
|
||||||
string = "%s\nThis is a bug. Please e-mail an admin if the problem persists."
|
string = "%s\nThis is a bug. Please e-mail an admin if the problem persists."
|
||||||
session.msg(string % (traceback.format_exc()))
|
session.msg(string % (traceback.format_exc()))
|
||||||
logger.log_errmsg(traceback.format_exc())
|
logger.log_errmsg(traceback.format_exc())
|
||||||
|
|
||||||
|
|
||||||
class CmdUnconnectedQuit(MuxCommand):
|
class CmdUnconnectedQuit(MuxCommand):
|
||||||
"""
|
"""
|
||||||
We maintain a different version of the quit command
|
We maintain a different version of the quit command
|
||||||
|
|
@ -230,7 +235,8 @@ class CmdUnconnectedQuit(MuxCommand):
|
||||||
"Simply close the connection."
|
"Simply close the connection."
|
||||||
session = self.caller
|
session = self.caller
|
||||||
#session.msg("Good bye! Disconnecting ...")
|
#session.msg("Good bye! Disconnecting ...")
|
||||||
session.sessionhandler.disconnect(session, "Good bye! Disconnecting ...")
|
session.sessionhandler.disconnect(session, "Good bye! Disconnecting.")
|
||||||
|
|
||||||
|
|
||||||
class CmdUnconnectedLook(MuxCommand):
|
class CmdUnconnectedLook(MuxCommand):
|
||||||
"""
|
"""
|
||||||
|
|
@ -247,6 +253,7 @@ class CmdUnconnectedLook(MuxCommand):
|
||||||
"Show the connect screen."
|
"Show the connect screen."
|
||||||
self.caller.msg(CONNECTION_SCREEN)
|
self.caller.msg(CONNECTION_SCREEN)
|
||||||
|
|
||||||
|
|
||||||
class CmdUnconnectedHelp(MuxCommand):
|
class CmdUnconnectedHelp(MuxCommand):
|
||||||
"""
|
"""
|
||||||
This is an unconnected version of the help command,
|
This is an unconnected version of the help command,
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,14 @@
|
||||||
"""
|
"""
|
||||||
Makes it easier to import by grouping all relevant things already at this level.
|
Makes it easier to import by grouping all relevant things already at this
|
||||||
|
level.
|
||||||
|
|
||||||
You can henceforth import most things directly from src.comms
|
You can henceforth import most things directly from src.comms
|
||||||
Also, the initiated object manager is available as src.comms.msgmanager and src.comms.channelmanager.
|
Also, the initiated object manager is available as src.comms.msgmanager and
|
||||||
|
src.comms.channelmanager.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from src.comms.models import *
|
from src.comms.models import *
|
||||||
|
|
||||||
msgmanager = Msg.objects
|
msgmanager = Msg.objects
|
||||||
channelmanager = ChannelDB.objects
|
channelmanager = ChannelDB.objects
|
||||||
|
|
|
||||||
|
|
@ -1,51 +1,57 @@
|
||||||
#
|
#
|
||||||
# This sets up how models are displayed
|
# This sets up how models are displayed
|
||||||
# in the web admin interface.
|
# in the web admin interface.
|
||||||
#
|
#
|
||||||
|
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from src.comms.models import ChannelDB, Msg, PlayerChannelConnection, ExternalChannelConnection
|
from src.comms.models import ChannelDB, Msg, PlayerChannelConnection, ExternalChannelConnection
|
||||||
|
|
||||||
class MsgAdmin(admin.ModelAdmin):
|
|
||||||
list_display = ('id', 'db_date_sent', 'db_sender', 'db_receivers', 'db_channels', 'db_message', 'db_lock_storage')
|
class MsgAdmin(admin.ModelAdmin):
|
||||||
list_display_links = ("id",)
|
list_display = ('id', 'db_date_sent', 'db_sender', 'db_receivers',
|
||||||
ordering = ["db_date_sent", 'db_sender', 'db_receivers', 'db_channels']
|
'db_channels', 'db_message', 'db_lock_storage')
|
||||||
#readonly_fields = ['db_message', 'db_sender', 'db_receivers', 'db_channels']
|
list_display_links = ("id",)
|
||||||
search_fields = ['id', '^db_date_sent', '^db_message']
|
ordering = ["db_date_sent", 'db_sender', 'db_receivers', 'db_channels']
|
||||||
save_as = True
|
#readonly_fields = ['db_message', 'db_sender', 'db_receivers', 'db_channels']
|
||||||
save_on_top = True
|
search_fields = ['id', '^db_date_sent', '^db_message']
|
||||||
list_select_related = True
|
save_as = True
|
||||||
#admin.site.register(Msg, MsgAdmin)
|
save_on_top = True
|
||||||
|
list_select_related = True
|
||||||
class PlayerChannelConnectionInline(admin.TabularInline):
|
#admin.site.register(Msg, MsgAdmin)
|
||||||
model = PlayerChannelConnection
|
|
||||||
fieldsets = (
|
|
||||||
(None, {
|
class PlayerChannelConnectionInline(admin.TabularInline):
|
||||||
'fields':(('db_player', 'db_channel')),
|
model = PlayerChannelConnection
|
||||||
'classes':('collapse',)}),)
|
fieldsets = (
|
||||||
extra = 1
|
(None, {
|
||||||
|
'fields':(('db_player', 'db_channel')),
|
||||||
class ExternalChannelConnectionInline(admin.StackedInline):
|
'classes':('collapse',)}),)
|
||||||
model = ExternalChannelConnection
|
extra = 1
|
||||||
fieldsets = (
|
|
||||||
(None, {
|
|
||||||
'fields':(('db_is_enabled','db_external_key', 'db_channel'), 'db_external_send_code', 'db_external_config'),
|
class ExternalChannelConnectionInline(admin.StackedInline):
|
||||||
'classes':('collapse',)
|
model = ExternalChannelConnection
|
||||||
}),)
|
fieldsets = (
|
||||||
extra = 1
|
(None, {
|
||||||
|
'fields': (('db_is_enabled','db_external_key', 'db_channel'),
|
||||||
class ChannelAdmin(admin.ModelAdmin):
|
'db_external_send_code', 'db_external_config'),
|
||||||
inlines = (PlayerChannelConnectionInline, ExternalChannelConnectionInline)
|
'classes': ('collapse',)
|
||||||
|
}),)
|
||||||
list_display = ('id', 'db_key', 'db_lock_storage')
|
extra = 1
|
||||||
list_display_links = ("id", 'db_key')
|
|
||||||
ordering = ["db_key"]
|
|
||||||
search_fields = ['id', 'db_key', 'db_aliases']
|
class ChannelAdmin(admin.ModelAdmin):
|
||||||
save_as = True
|
inlines = (PlayerChannelConnectionInline, ExternalChannelConnectionInline)
|
||||||
save_on_top = True
|
|
||||||
list_select_related = True
|
list_display = ('id', 'db_key', 'db_lock_storage')
|
||||||
fieldsets = (
|
list_display_links = ("id", 'db_key')
|
||||||
(None, {'fields':(('db_key',),'db_lock_storage',)}),
|
ordering = ["db_key"]
|
||||||
)
|
search_fields = ['id', 'db_key', 'db_aliases']
|
||||||
|
save_as = True
|
||||||
|
save_on_top = True
|
||||||
|
list_select_related = True
|
||||||
|
fieldsets = (
|
||||||
|
(None, {'fields': (('db_key',), 'db_lock_storage',)}),
|
||||||
|
)
|
||||||
|
|
||||||
admin.site.register(ChannelDB, ChannelAdmin)
|
admin.site.register(ChannelDB, ChannelAdmin)
|
||||||
|
|
@ -23,9 +23,9 @@ update() on the channelhandler. Or use Channel.objects.delete() which
|
||||||
does this for you.
|
does this for you.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from src.comms.models import ChannelDB, Msg
|
from src.comms.models import ChannelDB
|
||||||
from src.commands import cmdset, command
|
from src.commands import cmdset, command
|
||||||
from src.utils import utils
|
|
||||||
|
|
||||||
class ChannelCommand(command.Command):
|
class ChannelCommand(command.Command):
|
||||||
"""
|
"""
|
||||||
|
|
@ -51,7 +51,8 @@ class ChannelCommand(command.Command):
|
||||||
"""
|
"""
|
||||||
Simple parser
|
Simple parser
|
||||||
"""
|
"""
|
||||||
channelname, msg = self.args.split(":", 1) # cmdhandler sends channame:msg here.
|
# cmdhandler sends channame:msg here.
|
||||||
|
channelname, msg = self.args.split(":", 1)
|
||||||
self.args = (channelname.strip(), msg.strip())
|
self.args = (channelname.strip(), msg.strip())
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
|
|
@ -128,7 +129,7 @@ class ChannelHandler(object):
|
||||||
help_category="Channel names",
|
help_category="Channel names",
|
||||||
obj=channel,
|
obj=channel,
|
||||||
is_channel=True)
|
is_channel=True)
|
||||||
cmd.__doc__= self._format_help(channel)
|
cmd.__doc__ = self._format_help(channel)
|
||||||
self.cached_channel_cmds.append(cmd)
|
self.cached_channel_cmds.append(cmd)
|
||||||
self.cached_cmdsets = {}
|
self.cached_cmdsets = {}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,6 @@ class Comm(TypeClass):
|
||||||
else:
|
else:
|
||||||
return '%s: %s' % (sender_string, message)
|
return '%s: %s' % (sender_string, message)
|
||||||
|
|
||||||
|
|
||||||
def format_external(self, msg, senders, emit=False):
|
def format_external(self, msg, senders, emit=False):
|
||||||
"""
|
"""
|
||||||
Used for formatting external messages. This is needed as a separate
|
Used for formatting external messages. This is needed as a separate
|
||||||
|
|
@ -71,7 +70,6 @@ class Comm(TypeClass):
|
||||||
senders = ', '.join(senders)
|
senders = ', '.join(senders)
|
||||||
return self.pose_transform(msg, senders)
|
return self.pose_transform(msg, senders)
|
||||||
|
|
||||||
|
|
||||||
def format_message(self, msg, emit=False):
|
def format_message(self, msg, emit=False):
|
||||||
"""
|
"""
|
||||||
Formats a message body for display.
|
Formats a message body for display.
|
||||||
|
|
@ -169,30 +167,39 @@ class Comm(TypeClass):
|
||||||
conn.player.msg(msg.message, from_obj=msg.senders)
|
conn.player.msg(msg.message, from_obj=msg.senders)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
try:
|
try:
|
||||||
conn.to_external(msg.message, senders=msg.senders, from_channel=self)
|
conn.to_external(msg.message,
|
||||||
|
senders=msg.senders, from_channel=self)
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.log_trace("Cannot send msg to connection '%s'" % conn)
|
logger.log_trace("Cannot send msg to connection '%s'" % conn)
|
||||||
|
|
||||||
|
|
||||||
def msg(self, msgobj, header=None, senders=None, sender_strings=None,
|
def msg(self, msgobj, header=None, senders=None, sender_strings=None,
|
||||||
persistent=True, online=False, emit=False, external=False):
|
persistent=True, online=False, emit=False, external=False):
|
||||||
"""
|
"""
|
||||||
Send the given message to all players connected to channel. Note that
|
Send the given message to all players connected to channel. Note that
|
||||||
no permission-checking is done here; it is assumed to have been
|
no permission-checking is done here; it is assumed to have been
|
||||||
done before calling this method. The optional keywords are not used if persistent is False.
|
done before calling this method. The optional keywords are not used if
|
||||||
|
persistent is False.
|
||||||
|
|
||||||
msgobj - a Msg/TempMsg instance or a message string. If one of the former, the remaining
|
msgobj - a Msg/TempMsg instance or a message string. If one of the
|
||||||
keywords will be ignored. If a string, this will either be sent as-is (if persistent=False) or
|
former, the remaining keywords will be ignored. If a string,
|
||||||
it will be used together with header and senders keywords to create a Msg instance on the fly.
|
this will either be sent as-is (if persistent=False) or it
|
||||||
senders - an object, player or a list of objects or players. Optional if persistent=False.
|
will be used together with header and senders keywords to
|
||||||
sender_strings - Name strings of senders. Used for external connections where the sender
|
create a Msg instance on the fly.
|
||||||
is not a player or object. When this is defined, external will be assumed.
|
senders - an object, player or a list of objects or players.
|
||||||
|
Optional if persistent=False.
|
||||||
|
sender_strings - Name strings of senders. Used for external
|
||||||
|
connections where the sender is not a player or object. When
|
||||||
|
this is defined, external will be assumed.
|
||||||
external - Treat this message agnostic of its sender.
|
external - Treat this message agnostic of its sender.
|
||||||
persistent (bool) - ignored if msgobj is a Msg or TempMsg. If True, a Msg will be created, using
|
persistent (bool) - ignored if msgobj is a Msg or TempMsg. If True,
|
||||||
header and senders keywords. If False, other keywords will be ignored.
|
a Msg will be created, using header and senders keywords. If
|
||||||
online (bool) - If this is set true, only messages people who are online. Otherwise, messages all players
|
False, other keywords will be ignored.
|
||||||
connected. This can make things faster, but may not trigger listeners on players that are offline.
|
online (bool) - If this is set true, only messages people who are
|
||||||
emit (bool) - Signals to the message formatter that this message is not to be directly associated with a name.
|
online. Otherwise, messages all players connected. This can
|
||||||
|
make things faster, but may not trigger listeners on players
|
||||||
|
that are offline.
|
||||||
|
emit (bool) - Signals to the message formatter that this message is
|
||||||
|
not to be directly associated with a name.
|
||||||
"""
|
"""
|
||||||
if senders:
|
if senders:
|
||||||
senders = make_iter(senders)
|
senders = make_iter(senders)
|
||||||
|
|
@ -209,7 +216,7 @@ class Comm(TypeClass):
|
||||||
msgobj = TempMsg()
|
msgobj = TempMsg()
|
||||||
msgobj.header = header
|
msgobj.header = header
|
||||||
msgobj.message = msg
|
msgobj.message = msg
|
||||||
msgobj.channels = [self.dbobj] # add this channel
|
msgobj.channels = [self.dbobj] # add this channel
|
||||||
|
|
||||||
if not msgobj.senders:
|
if not msgobj.senders:
|
||||||
msgobj.senders = senders
|
msgobj.senders = senders
|
||||||
|
|
|
||||||
|
|
@ -65,11 +65,14 @@ class Send_IsAlive(Script):
|
||||||
self.interval = 900
|
self.interval = 900
|
||||||
self.desc = _("Send an IMC2 is-alive packet")
|
self.desc = _("Send an IMC2 is-alive packet")
|
||||||
self.persistent = True
|
self.persistent = True
|
||||||
|
|
||||||
def at_repeat(self):
|
def at_repeat(self):
|
||||||
IMC2_CLIENT.send_packet(pck.IMC2PacketIsAlive())
|
IMC2_CLIENT.send_packet(pck.IMC2PacketIsAlive())
|
||||||
|
|
||||||
def is_valid(self):
|
def is_valid(self):
|
||||||
"Is only valid as long as there are channels to update"
|
"Is only valid as long as there are channels to update"
|
||||||
return any(service for service in SESSIONS.server.services if service.name.startswith("imc2_"))
|
return any(service for service in SESSIONS.server.services
|
||||||
|
if service.name.startswith("imc2_"))
|
||||||
|
|
||||||
class Send_Keepalive_Request(Script):
|
class Send_Keepalive_Request(Script):
|
||||||
"""
|
"""
|
||||||
|
|
@ -81,11 +84,14 @@ class Send_Keepalive_Request(Script):
|
||||||
self.interval = 3500
|
self.interval = 3500
|
||||||
self.desc = _("Send an IMC2 keepalive-request packet")
|
self.desc = _("Send an IMC2 keepalive-request packet")
|
||||||
self.persistent = True
|
self.persistent = True
|
||||||
|
|
||||||
def at_repeat(self):
|
def at_repeat(self):
|
||||||
IMC2_CLIENT.channel.send_packet(pck.IMC2PacketKeepAliveRequest())
|
IMC2_CLIENT.channel.send_packet(pck.IMC2PacketKeepAliveRequest())
|
||||||
|
|
||||||
def is_valid(self):
|
def is_valid(self):
|
||||||
"Is only valid as long as there are channels to update"
|
"Is only valid as long as there are channels to update"
|
||||||
return any(service for service in SESSIONS.server.services if service.name.startswith("imc2_"))
|
return any(service for service in SESSIONS.server.services
|
||||||
|
if service.name.startswith("imc2_"))
|
||||||
|
|
||||||
class Prune_Inactive_Muds(Script):
|
class Prune_Inactive_Muds(Script):
|
||||||
"""
|
"""
|
||||||
|
|
@ -99,13 +105,17 @@ class Prune_Inactive_Muds(Script):
|
||||||
self.desc = _("Check IMC2 list for inactive games")
|
self.desc = _("Check IMC2 list for inactive games")
|
||||||
self.persistent = True
|
self.persistent = True
|
||||||
self.inactive_threshold = 3599
|
self.inactive_threshold = 3599
|
||||||
|
|
||||||
def at_repeat(self):
|
def at_repeat(self):
|
||||||
for name, mudinfo in IMC2_MUDLIST.mud_list.items():
|
for name, mudinfo in IMC2_MUDLIST.mud_list.items():
|
||||||
if time() - mudinfo.last_updated > self.inactive_threshold:
|
if time() - mudinfo.last_updated > self.inactive_threshold:
|
||||||
del IMC2_MUDLIST.mud_list[name]
|
del IMC2_MUDLIST.mud_list[name]
|
||||||
|
|
||||||
def is_valid(self):
|
def is_valid(self):
|
||||||
"Is only valid as long as there are channels to update"
|
"Is only valid as long as there are channels to update"
|
||||||
return any(service for service in SESSIONS.server.services if service.name.startswith("imc2_"))
|
return any(service for service in SESSIONS.server.services
|
||||||
|
if service.name.startswith("imc2_"))
|
||||||
|
|
||||||
|
|
||||||
class Sync_Server_Channel_List(Script):
|
class Sync_Server_Channel_List(Script):
|
||||||
"""
|
"""
|
||||||
|
|
@ -116,21 +126,25 @@ class Sync_Server_Channel_List(Script):
|
||||||
"""
|
"""
|
||||||
def at_script_creation(self):
|
def at_script_creation(self):
|
||||||
self.key = "IMC2_Sync_Server_Channel_List"
|
self.key = "IMC2_Sync_Server_Channel_List"
|
||||||
self.interval = 24 * 3600 # once every day
|
self.interval = 24 * 3600 # once every day
|
||||||
self.desc = _("Re-sync IMC2 network channel list")
|
self.desc = _("Re-sync IMC2 network channel list")
|
||||||
self.persistent = True
|
self.persistent = True
|
||||||
|
|
||||||
def at_repeat(self):
|
def at_repeat(self):
|
||||||
checked_networks = []
|
checked_networks = []
|
||||||
network = IMC2_CLIENT.factory.network
|
network = IMC2_CLIENT.factory.network
|
||||||
if not network in checked_networks:
|
if not network in checked_networks:
|
||||||
channel.send_packet(pkg.IMC2PacketIceRefresh())
|
channel.send_packet(pkg.IMC2PacketIceRefresh())
|
||||||
checked_networks.append(network)
|
checked_networks.append(network)
|
||||||
|
|
||||||
def is_valid(self):
|
def is_valid(self):
|
||||||
return any(service for service in SESSIONS.server.services if service.name.startswith("imc2_"))
|
return any(service for service in SESSIONS.server.services
|
||||||
|
if service.name.startswith("imc2_"))
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# IMC2 protocol
|
# IMC2 protocol
|
||||||
#
|
#
|
||||||
|
|
||||||
class IMC2Protocol(telnet.StatefulTelnetProtocol):
|
class IMC2Protocol(telnet.StatefulTelnetProtocol):
|
||||||
"""
|
"""
|
||||||
Provides the abstraction for the IMC2 protocol. Handles connection,
|
Provides the abstraction for the IMC2 protocol. Handles connection,
|
||||||
|
|
@ -145,7 +159,6 @@ class IMC2Protocol(telnet.StatefulTelnetProtocol):
|
||||||
self.network_name = None
|
self.network_name = None
|
||||||
self.sequence = None
|
self.sequence = None
|
||||||
|
|
||||||
|
|
||||||
def connectionMade(self):
|
def connectionMade(self):
|
||||||
"""
|
"""
|
||||||
Triggered after connecting to the IMC2 network.
|
Triggered after connecting to the IMC2 network.
|
||||||
|
|
@ -179,8 +192,10 @@ class IMC2Protocol(telnet.StatefulTelnetProtocol):
|
||||||
# This gets incremented with every command.
|
# This gets incremented with every command.
|
||||||
self.sequence += 1
|
self.sequence += 1
|
||||||
packet.imc2_protocol = self
|
packet.imc2_protocol = self
|
||||||
packet_str = utils.to_str(packet.assemble(self.factory.mudname, self.factory.client_pwd, self.factory.server_pwd))
|
packet_str = utils.to_str(packet.assemble(self.factory.mudname,
|
||||||
if IMC2_DEBUG and not (hasattr(packet, 'packet_type') and packet.packet_type == "is-alive"):
|
self.factory.client_pwd, self.factory.server_pwd))
|
||||||
|
if IMC2_DEBUG and not (hasattr(packet, 'packet_type') and
|
||||||
|
packet.packet_type == "is-alive"):
|
||||||
logger.log_infomsg("IMC2: SENT> %s" % packet_str)
|
logger.log_infomsg("IMC2: SENT> %s" % packet_str)
|
||||||
logger.log_infomsg(str(packet))
|
logger.log_infomsg(str(packet))
|
||||||
self.sendLine(packet_str)
|
self.sendLine(packet_str)
|
||||||
|
|
@ -257,9 +272,9 @@ class IMC2Protocol(telnet.StatefulTelnetProtocol):
|
||||||
"""
|
"""
|
||||||
Handle tells over IMC2 by formatting the text properly
|
Handle tells over IMC2 by formatting the text properly
|
||||||
"""
|
"""
|
||||||
return _("{c%(sender)s@%(origin)s{n {wpages (over IMC):{n %(msg)s") % {"sender":packet.sender,
|
return _("{c%(sender)s@%(origin)s{n {wpages (over IMC):{n %(msg)s") % {"sender": packet.sender,
|
||||||
"origin":packet.origin,
|
"origin": packet.origin,
|
||||||
"msg":packet.optional_data.get('text', 'ERROR: No text provided.')}
|
"msg": packet.optional_data.get('text', 'ERROR: No text provided.')}
|
||||||
|
|
||||||
def lineReceived(self, line):
|
def lineReceived(self, line):
|
||||||
"""
|
"""
|
||||||
|
|
@ -349,6 +364,7 @@ class IMC2Protocol(telnet.StatefulTelnetProtocol):
|
||||||
target = data.get("target", "Unknown")
|
target = data.get("target", "Unknown")
|
||||||
self.send_packet(pck.IMC2PacketWhois(from_obj.id, target))
|
self.send_packet(pck.IMC2PacketWhois(from_obj.id, target))
|
||||||
|
|
||||||
|
|
||||||
class IMC2Factory(protocol.ClientFactory):
|
class IMC2Factory(protocol.ClientFactory):
|
||||||
"""
|
"""
|
||||||
Creates instances of the IMC2Protocol. Should really only ever
|
Creates instances of the IMC2Protocol. Should really only ever
|
||||||
|
|
@ -382,7 +398,9 @@ def build_connection_key(channel, imc2_channel):
|
||||||
"Build an id hash for the connection"
|
"Build an id hash for the connection"
|
||||||
if hasattr(channel, "key"):
|
if hasattr(channel, "key"):
|
||||||
channel = channel.key
|
channel = channel.key
|
||||||
return "imc2_%s:%s(%s)%s<>%s" % (IMC2_NETWORK, IMC2_PORT, IMC2_MUDNAME, imc2_channel, channel)
|
return "imc2_%s:%s(%s)%s<>%s" % (IMC2_NETWORK, IMC2_PORT,
|
||||||
|
IMC2_MUDNAME, imc2_channel, channel)
|
||||||
|
|
||||||
|
|
||||||
def start_scripts(validate=False):
|
def start_scripts(validate=False):
|
||||||
"""
|
"""
|
||||||
|
|
@ -402,6 +420,7 @@ def start_scripts(validate=False):
|
||||||
if not search.scripts("IMC2_Sync_Server_Channel_List"):
|
if not search.scripts("IMC2_Sync_Server_Channel_List"):
|
||||||
create.create_script(Sync_Server_Channel_List)
|
create.create_script(Sync_Server_Channel_List)
|
||||||
|
|
||||||
|
|
||||||
def create_connection(channel, imc2_channel):
|
def create_connection(channel, imc2_channel):
|
||||||
"""
|
"""
|
||||||
This will create a new IMC2<->channel connection.
|
This will create a new IMC2<->channel connection.
|
||||||
|
|
@ -417,7 +436,8 @@ def create_connection(channel, imc2_channel):
|
||||||
|
|
||||||
old_conns = ExternalChannelConnection.objects.filter(db_external_key=key)
|
old_conns = ExternalChannelConnection.objects.filter(db_external_key=key)
|
||||||
if old_conns:
|
if old_conns:
|
||||||
# this evennia channel is already connected to imc. Check if imc2_channel is different.
|
# this evennia channel is already connected to imc. Check
|
||||||
|
# if imc2_channel is different.
|
||||||
# connection already exists. We try to only connect a new channel
|
# connection already exists. We try to only connect a new channel
|
||||||
old_config = old_conns[0].db_external_config.split(",")
|
old_config = old_conns[0].db_external_config.split(",")
|
||||||
if imc2_channel in old_config:
|
if imc2_channel in old_config:
|
||||||
|
|
@ -432,9 +452,9 @@ def create_connection(channel, imc2_channel):
|
||||||
# no old connection found; create a new one.
|
# no old connection found; create a new one.
|
||||||
config = imc2_channel
|
config = imc2_channel
|
||||||
# how the evennia channel will be able to contact this protocol in reverse
|
# how the evennia channel will be able to contact this protocol in reverse
|
||||||
send_code = "from src.comms.imc2 import IMC2_CLIENT\n"
|
send_code = "from src.comms.imc2 import IMC2_CLIENT\n"
|
||||||
send_code += "data={'channel':from_channel}\n"
|
send_code += "data={'channel':from_channel}\n"
|
||||||
send_code += "IMC2_CLIENT.msg_imc2(message, senders=[self])\n"
|
send_code += "IMC2_CLIENT.msg_imc2(message, senders=[self])\n"
|
||||||
conn = ExternalChannelConnection(db_channel=channel, db_external_key=key, db_external_send_code=send_code,
|
conn = ExternalChannelConnection(db_channel=channel, db_external_key=key, db_external_send_code=send_code,
|
||||||
db_external_config=config)
|
db_external_config=config)
|
||||||
conn.save()
|
conn.save()
|
||||||
|
|
@ -453,15 +473,22 @@ def delete_connection(channel, imc2_channel):
|
||||||
conn.delete()
|
conn.delete()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def connect_to_imc2():
|
def connect_to_imc2():
|
||||||
"Create the imc instance and connect to the IMC2 network."
|
"Create the imc instance and connect to the IMC2 network."
|
||||||
|
|
||||||
# connect
|
# connect
|
||||||
imc = internet.TCPClient(IMC2_NETWORK, int(IMC2_PORT), IMC2Factory(IMC2_NETWORK, IMC2_PORT, IMC2_MUDNAME,
|
imc = internet.TCPClient(IMC2_NETWORK,
|
||||||
IMC2_CLIENT_PWD, IMC2_SERVER_PWD))
|
int(IMC2_PORT),
|
||||||
|
IMC2Factory(IMC2_NETWORK,
|
||||||
|
IMC2_PORT,
|
||||||
|
IMC2_MUDNAME,
|
||||||
|
IMC2_CLIENT_PWD,
|
||||||
|
IMC2_SERVER_PWD))
|
||||||
imc.setName("imc2_%s:%s(%s)" % (IMC2_NETWORK, IMC2_PORT, IMC2_MUDNAME))
|
imc.setName("imc2_%s:%s(%s)" % (IMC2_NETWORK, IMC2_PORT, IMC2_MUDNAME))
|
||||||
SESSIONS.server.services.addService(imc)
|
SESSIONS.server.services.addService(imc)
|
||||||
|
|
||||||
|
|
||||||
def connect_all():
|
def connect_all():
|
||||||
"""
|
"""
|
||||||
Activates the imc2 system. Called by the server if IMC2_ENABLED=True.
|
Activates the imc2 system. Called by the server if IMC2_ENABLED=True.
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
@ -1,59 +1,60 @@
|
||||||
"""
|
"""
|
||||||
ANSI parser - this adds colour to text according to
|
ANSI parser - this adds colour to text according to
|
||||||
special markup strings.
|
special markup strings.
|
||||||
|
|
||||||
This is a IMC2 complacent version.
|
This is a IMC2 complacent version.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import re
|
import re
|
||||||
from src.utils import ansi
|
from src.utils import ansi
|
||||||
|
|
||||||
class IMCANSIParser(ansi.ANSIParser):
|
|
||||||
"""
|
class IMCANSIParser(ansi.ANSIParser):
|
||||||
This parser is per the IMC2 specification.
|
"""
|
||||||
"""
|
This parser is per the IMC2 specification.
|
||||||
def __init__(self):
|
"""
|
||||||
normal = ansi.ANSI_NORMAL
|
def __init__(self):
|
||||||
hilite = ansi.ANSI_HILITE
|
normal = ansi.ANSI_NORMAL
|
||||||
self.ansi_map = [
|
hilite = ansi.ANSI_HILITE
|
||||||
(r'~Z', normal), # Random
|
self.ansi_map = [
|
||||||
(r'~x', normal + ansi.ANSI_BLACK), # Black
|
(r'~Z', normal), # Random
|
||||||
(r'~D', hilite + ansi.ANSI_BLACK), # Dark Grey
|
(r'~x', normal + ansi.ANSI_BLACK), # Black
|
||||||
(r'~z', hilite + ansi.ANSI_BLACK),
|
(r'~D', hilite + ansi.ANSI_BLACK), # Dark Grey
|
||||||
(r'~w', normal + ansi.ANSI_WHITE), # Grey
|
(r'~z', hilite + ansi.ANSI_BLACK),
|
||||||
(r'~W', hilite + ansi.ANSI_WHITE), # White
|
(r'~w', normal + ansi.ANSI_WHITE), # Grey
|
||||||
(r'~g', normal + ansi.ANSI_GREEN), # Dark Green
|
(r'~W', hilite + ansi.ANSI_WHITE), # White
|
||||||
(r'~G', hilite + ansi.ANSI_GREEN), # Green
|
(r'~g', normal + ansi.ANSI_GREEN), # Dark Green
|
||||||
(r'~p', normal + ansi.ANSI_MAGENTA), # Dark magenta
|
(r'~G', hilite + ansi.ANSI_GREEN), # Green
|
||||||
(r'~m', normal + ansi.ANSI_MAGENTA),
|
(r'~p', normal + ansi.ANSI_MAGENTA), # Dark magenta
|
||||||
(r'~M', hilite + ansi.ANSI_MAGENTA), # Magenta
|
(r'~m', normal + ansi.ANSI_MAGENTA),
|
||||||
(r'~P', hilite + ansi.ANSI_MAGENTA),
|
(r'~M', hilite + ansi.ANSI_MAGENTA), # Magenta
|
||||||
(r'~c', normal + ansi.ANSI_CYAN), # Cyan
|
(r'~P', hilite + ansi.ANSI_MAGENTA),
|
||||||
(r'~y', normal + ansi.ANSI_YELLOW), # Dark Yellow (brown)
|
(r'~c', normal + ansi.ANSI_CYAN), # Cyan
|
||||||
(r'~Y', hilite + ansi.ANSI_YELLOW), # Yellow
|
(r'~y', normal + ansi.ANSI_YELLOW), # Dark Yellow (brown)
|
||||||
(r'~b', normal + ansi.ANSI_BLUE), # Dark Blue
|
(r'~Y', hilite + ansi.ANSI_YELLOW), # Yellow
|
||||||
(r'~B', hilite + ansi.ANSI_BLUE), # Blue
|
(r'~b', normal + ansi.ANSI_BLUE), # Dark Blue
|
||||||
(r'~C', hilite + ansi.ANSI_BLUE),
|
(r'~B', hilite + ansi.ANSI_BLUE), # Blue
|
||||||
(r'~r', normal + ansi.ANSI_RED), # Dark Red
|
(r'~C', hilite + ansi.ANSI_BLUE),
|
||||||
(r'~R', hilite + ansi.ANSI_RED), # Red
|
(r'~r', normal + ansi.ANSI_RED), # Dark Red
|
||||||
|
(r'~R', hilite + ansi.ANSI_RED), # Red
|
||||||
## Formatting
|
|
||||||
(r'~L', hilite), # Bold/hilite
|
## Formatting
|
||||||
(r'~!', normal), # reset
|
(r'~L', hilite), # Bold/hilite
|
||||||
(r'\\r', normal),
|
(r'~!', normal), # reset
|
||||||
(r'\\n', ansi.ANSI_RETURN),
|
(r'\\r', normal),
|
||||||
]
|
(r'\\n', ansi.ANSI_RETURN),
|
||||||
# prepare regex matching
|
]
|
||||||
self.ansi_sub = [(re.compile(sub[0], re.DOTALL), sub[1])
|
# prepare regex matching
|
||||||
for sub in self.ansi_map]
|
self.ansi_sub = [(re.compile(sub[0], re.DOTALL), sub[1])
|
||||||
# prepare matching ansi codes overall
|
for sub in self.ansi_map]
|
||||||
self.ansi_regex = re.compile("\033\[[0-9;]+m")
|
# prepare matching ansi codes overall
|
||||||
|
self.ansi_regex = re.compile("\033\[[0-9;]+m")
|
||||||
|
|
||||||
ANSI_PARSER = IMCANSIParser()
|
ANSI_PARSER = IMCANSIParser()
|
||||||
|
|
||||||
def parse_ansi(string, strip_ansi=False, parser=ANSI_PARSER):
|
|
||||||
"""
|
def parse_ansi(string, strip_ansi=False, parser=ANSI_PARSER):
|
||||||
Shortcut to use the IMC2 ANSI parser.
|
"""
|
||||||
"""
|
Shortcut to use the IMC2 ANSI parser.
|
||||||
return parser.parse_ansi(string, strip_ansi=strip_ansi)
|
"""
|
||||||
|
return parser.parse_ansi(string, strip_ansi=strip_ansi)
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,24 @@
|
||||||
"""
|
"""
|
||||||
This module handles some of the -reply packets like whois-reply.
|
This module handles some of the -reply packets like whois-reply.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from src.objects.models import ObjectDB
|
from src.objects.models import ObjectDB
|
||||||
from src.comms.imc2lib import imc2_ansi
|
from src.comms.imc2lib import imc2_ansi
|
||||||
|
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
def handle_whois_reply(packet):
|
def handle_whois_reply(packet):
|
||||||
"""
|
"""
|
||||||
When the player sends an imcwhois <playername> request, the outgoing
|
When the player sends an imcwhois <playername> request, the outgoing
|
||||||
packet contains the id of the one asking. This handler catches the
|
packet contains the id of the one asking. This handler catches the
|
||||||
(possible) reply from the server, parses the id back to the
|
(possible) reply from the server, parses the id back to the
|
||||||
original asker and tells them the result.
|
original asker and tells them the result.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
pobject = ObjectDB.objects.get(id=packet.target)
|
pobject = ObjectDB.objects.get(id=packet.target)
|
||||||
response_text = imc2_ansi.parse_ansi(packet.optional_data.get('text', 'Unknown'))
|
response_text = imc2_ansi.parse_ansi(packet.optional_data.get('text', 'Unknown'))
|
||||||
string = _('Whois reply from %(origin)s: %(msg)s') % {"origin":packet.origin, "msg":response_text}
|
string = _('Whois reply from %(origin)s: %(msg)s') % {"origin":packet.origin, "msg":response_text}
|
||||||
pobject.msg(string.strip())
|
pobject.msg(string.strip())
|
||||||
except ObjectDB.DoesNotExist:
|
except ObjectDB.DoesNotExist:
|
||||||
# No match found for whois sender. Ignore it.
|
# No match found for whois sender. Ignore it.
|
||||||
pass
|
pass
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,104 +1,108 @@
|
||||||
"""
|
"""
|
||||||
Certain periodic packets are sent by connected MUDs (is-alive, user-cache,
|
Certain periodic packets are sent by connected MUDs (is-alive, user-cache,
|
||||||
etc). The IMC2 protocol assumes that each connected MUD will capture these and
|
etc). The IMC2 protocol assumes that each connected MUD will capture these and
|
||||||
populate/maintain their own lists of other servers connected. This module
|
populate/maintain their own lists of other servers connected. This module
|
||||||
contains stuff like this.
|
contains stuff like this.
|
||||||
"""
|
"""
|
||||||
from time import time
|
from time import time
|
||||||
|
|
||||||
class IMC2Mud(object):
|
|
||||||
"""
|
class IMC2Mud(object):
|
||||||
Stores information about other games connected to our current IMC2 network.
|
"""
|
||||||
"""
|
Stores information about other games connected to our current IMC2 network.
|
||||||
def __init__(self, packet):
|
"""
|
||||||
self.name = packet.origin
|
def __init__(self, packet):
|
||||||
self.versionid = packet.optional_data.get('versionid', None)
|
self.name = packet.origin
|
||||||
self.networkname = packet.optional_data.get('networkname', None)
|
self.versionid = packet.optional_data.get('versionid', None)
|
||||||
self.url = packet.optional_data.get('url', None)
|
self.networkname = packet.optional_data.get('networkname', None)
|
||||||
self.host = packet.optional_data.get('host', None)
|
self.url = packet.optional_data.get('url', None)
|
||||||
self.port = packet.optional_data.get('port', None)
|
self.host = packet.optional_data.get('host', None)
|
||||||
self.sha256 = packet.optional_data.get('sha256', None)
|
self.port = packet.optional_data.get('port', None)
|
||||||
# This is used to determine when a Mud has fallen into inactive status.
|
self.sha256 = packet.optional_data.get('sha256', None)
|
||||||
self.last_updated = time()
|
# This is used to determine when a Mud has fallen into inactive status.
|
||||||
|
self.last_updated = time()
|
||||||
class IMC2MudList(object):
|
|
||||||
"""
|
|
||||||
Keeps track of other MUDs connected to the IMC network.
|
class IMC2MudList(object):
|
||||||
"""
|
"""
|
||||||
def __init__(self):
|
Keeps track of other MUDs connected to the IMC network.
|
||||||
# Mud list is stored in a dict, key being the IMC Mud name.
|
"""
|
||||||
self.mud_list = {}
|
def __init__(self):
|
||||||
|
# Mud list is stored in a dict, key being the IMC Mud name.
|
||||||
def get_mud_list(self):
|
self.mud_list = {}
|
||||||
"""
|
|
||||||
Returns a sorted list of connected Muds.
|
def get_mud_list(self):
|
||||||
"""
|
"""
|
||||||
muds = self.mud_list.items()
|
Returns a sorted list of connected Muds.
|
||||||
muds.sort()
|
"""
|
||||||
return [value for key, value in muds]
|
muds = self.mud_list.items()
|
||||||
|
muds.sort()
|
||||||
def update_mud_from_packet(self, packet):
|
return [value for key, value in muds]
|
||||||
"""
|
|
||||||
This grabs relevant info from the packet and stuffs it in the
|
def update_mud_from_packet(self, packet):
|
||||||
Mud list for later retrieval.
|
"""
|
||||||
"""
|
This grabs relevant info from the packet and stuffs it in the
|
||||||
mud = IMC2Mud(packet)
|
Mud list for later retrieval.
|
||||||
self.mud_list[mud.name] = mud
|
"""
|
||||||
|
mud = IMC2Mud(packet)
|
||||||
def remove_mud_from_packet(self, packet):
|
self.mud_list[mud.name] = mud
|
||||||
"""
|
|
||||||
Removes a mud from the Mud list when given a packet.
|
def remove_mud_from_packet(self, packet):
|
||||||
"""
|
"""
|
||||||
mud = IMC2Mud(packet)
|
Removes a mud from the Mud list when given a packet.
|
||||||
try:
|
"""
|
||||||
del self.mud_list[mud.name]
|
mud = IMC2Mud(packet)
|
||||||
except KeyError:
|
try:
|
||||||
# No matching entry, no big deal.
|
del self.mud_list[mud.name]
|
||||||
pass
|
except KeyError:
|
||||||
|
# No matching entry, no big deal.
|
||||||
class IMC2Channel(object):
|
pass
|
||||||
"""
|
|
||||||
Stores information about channels available on the network.
|
|
||||||
"""
|
class IMC2Channel(object):
|
||||||
def __init__(self, packet):
|
"""
|
||||||
self.localname = packet.optional_data.get('localname', None)
|
Stores information about channels available on the network.
|
||||||
self.name = packet.optional_data.get('channel', None)
|
"""
|
||||||
self.level = packet.optional_data.get('level', None)
|
def __init__(self, packet):
|
||||||
self.owner = packet.optional_data.get('owner', None)
|
self.localname = packet.optional_data.get('localname', None)
|
||||||
self.policy = packet.optional_data.get('policy', None)
|
self.name = packet.optional_data.get('channel', None)
|
||||||
self.last_updated = time()
|
self.level = packet.optional_data.get('level', None)
|
||||||
|
self.owner = packet.optional_data.get('owner', None)
|
||||||
class IMC2ChanList(object):
|
self.policy = packet.optional_data.get('policy', None)
|
||||||
"""
|
self.last_updated = time()
|
||||||
Keeps track of other MUDs connected to the IMC network.
|
|
||||||
"""
|
|
||||||
def __init__(self):
|
class IMC2ChanList(object):
|
||||||
# Chan list is stored in a dict, key being the IMC Mud name.
|
"""
|
||||||
self.chan_list = {}
|
Keeps track of other MUDs connected to the IMC network.
|
||||||
|
"""
|
||||||
def get_channel_list(self):
|
def __init__(self):
|
||||||
"""
|
# Chan list is stored in a dict, key being the IMC Mud name.
|
||||||
Returns a sorted list of cached channels.
|
self.chan_list = {}
|
||||||
"""
|
|
||||||
channels = self.chan_list.items()
|
def get_channel_list(self):
|
||||||
channels.sort()
|
"""
|
||||||
return [value for key, value in channels]
|
Returns a sorted list of cached channels.
|
||||||
|
"""
|
||||||
def update_channel_from_packet(self, packet):
|
channels = self.chan_list.items()
|
||||||
"""
|
channels.sort()
|
||||||
This grabs relevant info from the packet and stuffs it in the
|
return [value for key, value in channels]
|
||||||
channel list for later retrieval.
|
|
||||||
"""
|
def update_channel_from_packet(self, packet):
|
||||||
channel = IMC2Channel(packet)
|
"""
|
||||||
self.chan_list[channel.name] = channel
|
This grabs relevant info from the packet and stuffs it in the
|
||||||
|
channel list for later retrieval.
|
||||||
def remove_channel_from_packet(self, packet):
|
"""
|
||||||
"""
|
channel = IMC2Channel(packet)
|
||||||
Removes a channel from the Channel list when given a packet.
|
self.chan_list[channel.name] = channel
|
||||||
"""
|
|
||||||
channel = IMC2Channel(packet)
|
def remove_channel_from_packet(self, packet):
|
||||||
try:
|
"""
|
||||||
del self.chan_list[channel.name]
|
Removes a channel from the Channel list when given a packet.
|
||||||
except KeyError:
|
"""
|
||||||
# No matching entry, no big deal.
|
channel = IMC2Channel(packet)
|
||||||
pass
|
try:
|
||||||
|
del self.chan_list[channel.name]
|
||||||
|
except KeyError:
|
||||||
|
# No matching entry, no big deal.
|
||||||
|
pass
|
||||||
|
|
|
||||||
423
src/comms/irc.py
423
src/comms/irc.py
|
|
@ -1,205 +1,218 @@
|
||||||
"""
|
"""
|
||||||
This connects to an IRC network/channel and launches an 'bot' onto it.
|
This connects to an IRC network/channel and launches an 'bot' onto it.
|
||||||
The bot then pipes what is being said between the IRC channel and one or
|
The bot then pipes what is being said between the IRC channel and one or
|
||||||
more Evennia channels.
|
more Evennia channels.
|
||||||
"""
|
"""
|
||||||
from twisted.application import internet
|
from twisted.application import internet
|
||||||
from twisted.words.protocols import irc
|
from twisted.words.protocols import irc
|
||||||
from twisted.internet import protocol
|
from twisted.internet import protocol
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from src.comms.models import ExternalChannelConnection, ChannelDB
|
from src.comms.models import ExternalChannelConnection, ChannelDB
|
||||||
from src.utils import logger, utils
|
from src.utils import logger, utils
|
||||||
from src.server.sessionhandler import SESSIONS
|
from src.server.sessionhandler import SESSIONS
|
||||||
|
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
INFOCHANNEL = ChannelDB.objects.channel_search(settings.CHANNEL_MUDINFO[0])
|
INFOCHANNEL = ChannelDB.objects.channel_search(settings.CHANNEL_MUDINFO[0])
|
||||||
IRC_CHANNELS = []
|
IRC_CHANNELS = []
|
||||||
|
|
||||||
def msg_info(message):
|
|
||||||
"""
|
def msg_info(message):
|
||||||
Send info to default info channel
|
"""
|
||||||
"""
|
Send info to default info channel
|
||||||
message = '[%s][IRC]: %s' % (INFOCHANNEL[0].key, message)
|
"""
|
||||||
try:
|
message = '[%s][IRC]: %s' % (INFOCHANNEL[0].key, message)
|
||||||
INFOCHANNEL[0].msg(message)
|
try:
|
||||||
except AttributeError:
|
INFOCHANNEL[0].msg(message)
|
||||||
logger.log_infomsg("MUDinfo (irc): %s" % message)
|
except AttributeError:
|
||||||
|
logger.log_infomsg("MUDinfo (irc): %s" % message)
|
||||||
class IRC_Bot(irc.IRCClient):
|
|
||||||
"""
|
|
||||||
This defines an IRC bot that connects to an IRC channel
|
class IRC_Bot(irc.IRCClient):
|
||||||
and relays data to and from an evennia game.
|
"""
|
||||||
"""
|
This defines an IRC bot that connects to an IRC channel
|
||||||
|
and relays data to and from an evennia game.
|
||||||
def _get_nickname(self):
|
"""
|
||||||
"required for correct nickname setting"
|
|
||||||
return self.factory.nickname
|
def _get_nickname(self):
|
||||||
nickname = property(_get_nickname)
|
"required for correct nickname setting"
|
||||||
|
return self.factory.nickname
|
||||||
def signedOn(self):
|
nickname = property(_get_nickname)
|
||||||
# This is the first point the protocol is instantiated.
|
|
||||||
# add this protocol instance to the global list so we
|
def signedOn(self):
|
||||||
# can access it later to send data.
|
# This is the first point the protocol is instantiated.
|
||||||
global IRC_CHANNELS
|
# add this protocol instance to the global list so we
|
||||||
self.join(self.factory.channel)
|
# can access it later to send data.
|
||||||
|
global IRC_CHANNELS
|
||||||
IRC_CHANNELS.append(self)
|
self.join(self.factory.channel)
|
||||||
#msg_info("Client connecting to %s.'" % (self.factory.channel))
|
|
||||||
|
IRC_CHANNELS.append(self)
|
||||||
def joined(self, channel):
|
#msg_info("Client connecting to %s.'" % (self.factory.channel))
|
||||||
msg = _("joined %s.") % self.factory.pretty_key
|
|
||||||
msg_info(msg)
|
def joined(self, channel):
|
||||||
logger.log_infomsg(msg)
|
msg = _("joined %s.") % self.factory.pretty_key
|
||||||
|
msg_info(msg)
|
||||||
def get_mesg_info(self, user, irc_channel, msg):
|
logger.log_infomsg(msg)
|
||||||
"""
|
|
||||||
Get basic information about a message posted in IRC.
|
def get_mesg_info(self, user, irc_channel, msg):
|
||||||
"""
|
"""
|
||||||
#find irc->evennia channel mappings
|
Get basic information about a message posted in IRC.
|
||||||
conns = ExternalChannelConnection.objects.filter(db_external_key=self.factory.key)
|
"""
|
||||||
if not conns:
|
#find irc->evennia channel mappings
|
||||||
return
|
conns = ExternalChannelConnection.objects.filter(db_external_key=self.factory.key)
|
||||||
#format message:
|
if not conns:
|
||||||
user = user.split("!")[0]
|
return
|
||||||
if user:
|
#format message:
|
||||||
user.strip()
|
user = user.split("!")[0]
|
||||||
else:
|
if user:
|
||||||
user = _("Unknown")
|
user.strip()
|
||||||
msg = msg.strip()
|
else:
|
||||||
sender_strings = ["%s@%s" % (user, irc_channel)]
|
user = _("Unknown")
|
||||||
return conns, msg, sender_strings
|
msg = msg.strip()
|
||||||
|
sender_strings = ["%s@%s" % (user, irc_channel)]
|
||||||
def privmsg(self, user, irc_channel, msg):
|
return conns, msg, sender_strings
|
||||||
"Someone has written something in irc channel. Echo it to the evennia channel"
|
|
||||||
conns, msg, sender_strings = self.get_mesg_info(user, irc_channel, msg)
|
def privmsg(self, user, irc_channel, msg):
|
||||||
#logger.log_infomsg("<IRC: " + msg)
|
"Someone has written something in irc channel. Echo it to the evennia channel"
|
||||||
for conn in conns:
|
conns, msg, sender_strings = self.get_mesg_info(user, irc_channel, msg)
|
||||||
if conn.channel:
|
#logger.log_infomsg("<IRC: " + msg)
|
||||||
conn.to_channel(msg, sender_strings=sender_strings)
|
for conn in conns:
|
||||||
|
if conn.channel:
|
||||||
def action(self, user, irc_channel, msg):
|
conn.to_channel(msg, sender_strings=sender_strings)
|
||||||
"Someone has performed an action, e.g. using /me <pose>"
|
|
||||||
#find irc->evennia channel mappings
|
def action(self, user, irc_channel, msg):
|
||||||
conns = ExternalChannelConnection.objects.filter(db_external_key=self.factory.key)
|
"Someone has performed an action, e.g. using /me <pose>"
|
||||||
if not conns:
|
#find irc->evennia channel mappings
|
||||||
return
|
conns = ExternalChannelConnection.objects.filter(db_external_key=self.factory.key)
|
||||||
conns, msg, sender_strings = self.get_mesg_info(user, irc_channel, msg)
|
if not conns:
|
||||||
# Transform this into a pose.
|
return
|
||||||
msg = ':' + msg
|
conns, msg, sender_strings = self.get_mesg_info(user, irc_channel, msg)
|
||||||
#logger.log_infomsg("<IRC: " + msg)
|
# Transform this into a pose.
|
||||||
for conn in conns:
|
msg = ':' + msg
|
||||||
if conn.channel:
|
#logger.log_infomsg("<IRC: " + msg)
|
||||||
conn.to_channel(msg, sender_strings=sender_strings)
|
for conn in conns:
|
||||||
|
if conn.channel:
|
||||||
def msg_irc(self, msg, senders=None):
|
conn.to_channel(msg, sender_strings=sender_strings)
|
||||||
"""
|
|
||||||
Called by evennia when sending something to mapped IRC channel.
|
def msg_irc(self, msg, senders=None):
|
||||||
|
"""
|
||||||
Note that this cannot simply be called msg() since that's the
|
Called by evennia when sending something to mapped IRC channel.
|
||||||
name of of the twisted irc hook as well, this leads to some
|
|
||||||
initialization messages to be sent without checks, causing loops.
|
Note that this cannot simply be called msg() since that's the
|
||||||
"""
|
name of of the twisted irc hook as well, this leads to some
|
||||||
self.msg(utils.to_str(self.factory.channel), utils.to_str(msg))
|
initialization messages to be sent without checks, causing loops.
|
||||||
|
"""
|
||||||
class IRCbotFactory(protocol.ClientFactory):
|
self.msg(utils.to_str(self.factory.channel), utils.to_str(msg))
|
||||||
protocol = IRC_Bot
|
|
||||||
def __init__(self, key, channel, network, port, nickname, evennia_channel):
|
|
||||||
self.key = key
|
class IRCbotFactory(protocol.ClientFactory):
|
||||||
self.pretty_key = "%s:%s%s ('%s')" % (network, port, channel, nickname)
|
protocol = IRC_Bot
|
||||||
self.network = network
|
|
||||||
self.port = port
|
def __init__(self, key, channel, network, port, nickname, evennia_channel):
|
||||||
self.channel = channel
|
self.key = key
|
||||||
self.nickname = nickname
|
self.pretty_key = "%s:%s%s ('%s')" % (network, port, channel, nickname)
|
||||||
self.evennia_channel = evennia_channel
|
self.network = network
|
||||||
|
self.port = port
|
||||||
def clientConnectionLost(self, connector, reason):
|
self.channel = channel
|
||||||
from twisted.internet.error import ConnectionDone
|
self.nickname = nickname
|
||||||
if type(reason.type) == type(ConnectionDone):
|
self.evennia_channel = evennia_channel
|
||||||
msg_info(_("Connection closed."))
|
|
||||||
else:
|
def clientConnectionLost(self, connector, reason):
|
||||||
msg_info(_("Lost connection %(key)s. Reason: '%(reason)s'. Reconnecting.") % {"key":self.pretty_key, "reason":reason})
|
from twisted.internet.error import ConnectionDone
|
||||||
connector.connect()
|
if type(reason.type) == type(ConnectionDone):
|
||||||
def clientConnectionFailed(self, connector, reason):
|
msg_info(_("Connection closed."))
|
||||||
msg = _("Could not connect %(key)s Reason: '%(reason)s'") % {"key":self.pretty_key, "reason":reason}
|
else:
|
||||||
msg_info(msg)
|
msg_info(_("Lost connection %(key)s. Reason: '%(reason)s'. Reconnecting.") % {"key":self.pretty_key, "reason":reason})
|
||||||
logger.log_errmsg(msg)
|
connector.connect()
|
||||||
|
|
||||||
def build_connection_key(channel, irc_network, irc_port, irc_channel, irc_bot_nick):
|
def clientConnectionFailed(self, connector, reason):
|
||||||
"Build an id hash for the connection"
|
msg = _("Could not connect %(key)s Reason: '%(reason)s'") % {"key":self.pretty_key, "reason":reason}
|
||||||
if hasattr(channel, 'key'):
|
msg_info(msg)
|
||||||
channel = channel.key
|
logger.log_errmsg(msg)
|
||||||
return "irc_%s:%s%s(%s)<>%s" % (irc_network, irc_port, irc_channel, irc_bot_nick, channel)
|
|
||||||
|
|
||||||
def build_service_key(key):
|
def build_connection_key(channel, irc_network, irc_port, irc_channel, irc_bot_nick):
|
||||||
return "IRCbot:%s" % key
|
"Build an id hash for the connection"
|
||||||
|
if hasattr(channel, 'key'):
|
||||||
def create_connection(channel, irc_network, irc_port, irc_channel, irc_bot_nick):
|
channel = channel.key
|
||||||
"""
|
return "irc_%s:%s%s(%s)<>%s" % (irc_network, irc_port,
|
||||||
This will create a new IRC<->channel connection.
|
irc_channel, irc_bot_nick, channel)
|
||||||
"""
|
|
||||||
if not type(channel) == ChannelDB:
|
|
||||||
new_channel = ChannelDB.objects.filter(db_key=channel)
|
def build_service_key(key):
|
||||||
if not new_channel:
|
return "IRCbot:%s" % key
|
||||||
logger.log_errmsg(_("Cannot attach IRC<->Evennia: Evennia Channel '%s' not found") % channel)
|
|
||||||
return False
|
|
||||||
channel = new_channel[0]
|
def create_connection(channel, irc_network, irc_port,
|
||||||
key = build_connection_key(channel, irc_network, irc_port, irc_channel, irc_bot_nick)
|
irc_channel, irc_bot_nick):
|
||||||
|
"""
|
||||||
old_conns = ExternalChannelConnection.objects.filter(db_external_key=key)
|
This will create a new IRC<->channel connection.
|
||||||
if old_conns:
|
"""
|
||||||
return False
|
if not type(channel) == ChannelDB:
|
||||||
config = "%s|%s|%s|%s" % (irc_network, irc_port, irc_channel, irc_bot_nick)
|
new_channel = ChannelDB.objects.filter(db_key=channel)
|
||||||
# how the channel will be able to contact this protocol
|
if not new_channel:
|
||||||
send_code = "from src.comms.irc import IRC_CHANNELS\n"
|
logger.log_errmsg(_("Cannot attach IRC<->Evennia: Evennia Channel '%s' not found") % channel)
|
||||||
send_code += "matched_ircs = [irc for irc in IRC_CHANNELS if irc.factory.key == '%s']\n" % key
|
return False
|
||||||
send_code += "[irc.msg_irc(message, senders=[self]) for irc in matched_ircs]\n"
|
channel = new_channel[0]
|
||||||
conn = ExternalChannelConnection(db_channel=channel, db_external_key=key, db_external_send_code=send_code,
|
key = build_connection_key(channel, irc_network, irc_port,
|
||||||
db_external_config=config)
|
irc_channel, irc_bot_nick)
|
||||||
conn.save()
|
|
||||||
|
old_conns = ExternalChannelConnection.objects.filter(db_external_key=key)
|
||||||
# connect
|
if old_conns:
|
||||||
connect_to_irc(conn)
|
return False
|
||||||
return True
|
config = "%s|%s|%s|%s" % (irc_network, irc_port, irc_channel, irc_bot_nick)
|
||||||
|
# how the channel will be able to contact this protocol
|
||||||
def delete_connection(channel, irc_network, irc_port, irc_channel, irc_bot_nick):
|
send_code = "from src.comms.irc import IRC_CHANNELS\n"
|
||||||
"Destroy a connection"
|
send_code += "matched_ircs = [irc for irc in IRC_CHANNELS if irc.factory.key == '%s']\n" % key
|
||||||
if hasattr(channel, 'key'):
|
send_code += "[irc.msg_irc(message, senders=[self]) for irc in matched_ircs]\n"
|
||||||
channel = channel.key
|
conn = ExternalChannelConnection(db_channel=channel,
|
||||||
|
db_external_key=key,
|
||||||
key = build_connection_key(channel, irc_network, irc_port, irc_channel, irc_bot_nick)
|
db_external_send_code=send_code,
|
||||||
service_key = build_service_key(key)
|
db_external_config=config)
|
||||||
try:
|
conn.save()
|
||||||
conn = ExternalChannelConnection.objects.get(db_external_key=key)
|
|
||||||
except Exception:
|
# connect
|
||||||
return False
|
connect_to_irc(conn)
|
||||||
conn.delete()
|
return True
|
||||||
|
|
||||||
try:
|
def delete_connection(channel, irc_network, irc_port, irc_channel, irc_bot_nick):
|
||||||
service = SESSIONS.server.services.getServiceNamed(service_key)
|
"Destroy a connection"
|
||||||
except Exception:
|
if hasattr(channel, 'key'):
|
||||||
return True
|
channel = channel.key
|
||||||
if service.running:
|
|
||||||
SESSIONS.server.services.removeService(service)
|
key = build_connection_key(channel, irc_network, irc_port, irc_channel, irc_bot_nick)
|
||||||
return True
|
service_key = build_service_key(key)
|
||||||
|
try:
|
||||||
def connect_to_irc(connection):
|
conn = ExternalChannelConnection.objects.get(db_external_key=key)
|
||||||
"Create the bot instance and connect to the IRC network and channel."
|
except Exception:
|
||||||
# get config
|
return False
|
||||||
key = utils.to_str(connection.external_key)
|
conn.delete()
|
||||||
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('|')]
|
try:
|
||||||
# connect
|
service = SESSIONS.server.services.getServiceNamed(service_key)
|
||||||
bot = internet.TCPClient(irc_network, int(irc_port), IRCbotFactory(key, irc_channel, irc_network, irc_port, irc_bot_nick,
|
except Exception:
|
||||||
connection.channel.key))
|
return True
|
||||||
bot.setName(service_key)
|
if service.running:
|
||||||
SESSIONS.server.services.addService(bot)
|
SESSIONS.server.services.removeService(service)
|
||||||
|
return True
|
||||||
def connect_all():
|
|
||||||
"""
|
def connect_to_irc(connection):
|
||||||
Activate all irc bots.
|
"Create the bot instance and connect to the IRC network and channel."
|
||||||
"""
|
# get config
|
||||||
for connection in ExternalChannelConnection.objects.filter(db_external_key__startswith='irc_'):
|
key = utils.to_str(connection.external_key)
|
||||||
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), IRCbotFactory(key, irc_channel, irc_network, irc_port, irc_bot_nick,
|
||||||
|
connection.channel.key))
|
||||||
|
bot.setName(service_key)
|
||||||
|
SESSIONS.server.services.addService(bot)
|
||||||
|
|
||||||
|
def connect_all():
|
||||||
|
"""
|
||||||
|
Activate all irc bots.
|
||||||
|
"""
|
||||||
|
for connection in ExternalChannelConnection.objects.filter(db_external_key__startswith='irc_'):
|
||||||
|
connect_to_irc(connection)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,10 +18,12 @@ _User = None
|
||||||
|
|
||||||
# error class
|
# error class
|
||||||
|
|
||||||
|
|
||||||
class CommError(Exception):
|
class CommError(Exception):
|
||||||
"Raise by comm system, to allow feedback to player when caught."
|
"Raise by comm system, to allow feedback to player when caught."
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# helper functions
|
# helper functions
|
||||||
#
|
#
|
||||||
|
|
@ -43,6 +45,7 @@ def dbref(dbref, reqhash=True):
|
||||||
return None
|
return None
|
||||||
return dbref
|
return dbref
|
||||||
|
|
||||||
|
|
||||||
def identify_object(inp):
|
def identify_object(inp):
|
||||||
"identify if an object is a player or an object; return its database model"
|
"identify if an object is a player or an object; return its database model"
|
||||||
# load global stores
|
# load global stores
|
||||||
|
|
@ -61,18 +64,25 @@ def identify_object(inp):
|
||||||
return inp, None
|
return inp, None
|
||||||
# try to identify the type
|
# try to identify the type
|
||||||
try:
|
try:
|
||||||
obj = _GA(inp, "dbobj") # this works for all typeclassed entities
|
obj = _GA(inp, "dbobj") # this works for all typeclassed entities
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
obj = inp
|
obj = inp
|
||||||
typ = type(obj)
|
typ = type(obj)
|
||||||
if typ == _PlayerDB: return obj, "player"
|
if typ == _PlayerDB:
|
||||||
elif typ == _ObjectDB: return obj, "object"
|
return obj, "player"
|
||||||
elif typ == _ChannelDB: return obj, "channel"
|
elif typ == _ObjectDB:
|
||||||
elif dbref(obj): return dbref(obj), "dbref"
|
return obj, "object"
|
||||||
elif typ == basestring: return obj, "string"
|
elif typ == _ChannelDB:
|
||||||
elif typ == _ExternalConnection: return obj, "external"
|
return obj, "channel"
|
||||||
|
elif dbref(obj):
|
||||||
|
return dbref(obj), "dbref"
|
||||||
|
elif typ == basestring:
|
||||||
|
return obj, "string"
|
||||||
|
elif typ == _ExternalConnection:
|
||||||
|
return obj, "external"
|
||||||
return obj, None # Something else
|
return obj, None # Something else
|
||||||
|
|
||||||
|
|
||||||
def to_object(inp, objtype='player'):
|
def to_object(inp, objtype='player'):
|
||||||
"""
|
"""
|
||||||
Locates the object related to the given
|
Locates the object related to the given
|
||||||
|
|
@ -85,28 +95,39 @@ def to_object(inp, objtype='player'):
|
||||||
if typ == objtype:
|
if typ == objtype:
|
||||||
return obj
|
return obj
|
||||||
if objtype == 'player':
|
if objtype == 'player':
|
||||||
if typ == 'object': return obj.player
|
if typ == 'object':
|
||||||
if typ == 'string': return _PlayerDB.objects.get(user_username__iexact=obj)
|
return obj.player
|
||||||
if typ == 'dbref': return _PlayerDB.objects.get(id=obj)
|
if typ == 'string':
|
||||||
|
return _PlayerDB.objects.get(user_username__iexact=obj)
|
||||||
|
if typ == 'dbref':
|
||||||
|
return _PlayerDB.objects.get(id=obj)
|
||||||
print objtype, inp, obj, typ, type(inp)
|
print objtype, inp, obj, typ, type(inp)
|
||||||
raise CommError()
|
raise CommError()
|
||||||
elif objtype == 'object':
|
elif objtype == 'object':
|
||||||
if typ == 'player': return obj.obj
|
if typ == 'player':
|
||||||
if typ == 'string': return _ObjectDB.objects.get(db_key__iexact=obj)
|
return obj.obj
|
||||||
if typ == 'dbref': return _ObjectDB.objects.get(id=obj)
|
if typ == 'string':
|
||||||
|
return _ObjectDB.objects.get(db_key__iexact=obj)
|
||||||
|
if typ == 'dbref':
|
||||||
|
return _ObjectDB.objects.get(id=obj)
|
||||||
print objtype, inp, obj, typ, type(inp)
|
print objtype, inp, obj, typ, type(inp)
|
||||||
raise CommError()
|
raise CommError()
|
||||||
elif objtype == 'channel':
|
elif objtype == 'channel':
|
||||||
if typ == 'string': return _ChannelDB.objects.get(db_key__iexact=obj)
|
if typ == 'string':
|
||||||
if typ == 'dbref': return _ChannelDB.objects.get(id=obj)
|
return _ChannelDB.objects.get(db_key__iexact=obj)
|
||||||
|
if typ == 'dbref':
|
||||||
|
return _ChannelDB.objects.get(id=obj)
|
||||||
print objtype, inp, obj, typ, type(inp)
|
print objtype, inp, obj, typ, type(inp)
|
||||||
raise CommError()
|
raise CommError()
|
||||||
elif objtype == 'external':
|
elif objtype == 'external':
|
||||||
if typ == 'string': return _ExternalConnection.objects.get(db_key=inp)
|
if typ == 'string':
|
||||||
if typ == 'dbref': return _ExternalConnection.objects.get(id=obj)
|
return _ExternalConnection.objects.get(db_key=inp)
|
||||||
|
if typ == 'dbref':
|
||||||
|
return _ExternalConnection.objects.get(id=obj)
|
||||||
print objtype, inp, obj, typ, type(inp)
|
print objtype, inp, obj, typ, type(inp)
|
||||||
raise CommError()
|
raise CommError()
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Msg manager
|
# Msg manager
|
||||||
#
|
#
|
||||||
|
|
@ -146,17 +167,21 @@ class MsgManager(models.Manager):
|
||||||
|
|
||||||
def get_messages_by_sender(self, obj, exclude_channel_messages=False):
|
def get_messages_by_sender(self, obj, exclude_channel_messages=False):
|
||||||
"""
|
"""
|
||||||
Get all messages sent by one entity - this could be either a player or an object
|
Get all messages sent by one entity - this could be either a
|
||||||
|
player or an object
|
||||||
|
|
||||||
only_non_channel: only return messages -not- aimed at a channel (e.g. private tells)
|
only_non_channel: only return messages -not- aimed at a channel
|
||||||
|
(e.g. private tells)
|
||||||
"""
|
"""
|
||||||
obj, typ = identify_object(obj)
|
obj, typ = identify_object(obj)
|
||||||
if exclude_channel_messages:
|
if exclude_channel_messages:
|
||||||
# explicitly exclude channel recipients
|
# explicitly exclude channel recipients
|
||||||
if typ == 'player':
|
if typ == 'player':
|
||||||
return list(self.filter(db_sender_players=obj, db_receivers_channels__isnull=True).exclude(db_hide_from_players=obj))
|
return list(self.filter(db_sender_players=obj,
|
||||||
|
db_receivers_channels__isnull=True).exclude(db_hide_from_players=obj))
|
||||||
elif typ == 'object':
|
elif typ == 'object':
|
||||||
return list(self.filter(db_sender_objects=obj, db_receivers_channels__isnull=True).exclude(db_hide_from_objects=obj))
|
return list(self.filter(db_sender_objects=obj,
|
||||||
|
db_receivers_channels__isnull=True).exclude(db_hide_from_objects=obj))
|
||||||
else:
|
else:
|
||||||
raise CommError
|
raise CommError
|
||||||
else:
|
else:
|
||||||
|
|
@ -208,9 +233,10 @@ class MsgManager(models.Manager):
|
||||||
if msg:
|
if msg:
|
||||||
return msg[0]
|
return msg[0]
|
||||||
|
|
||||||
# We use Q objects to gradually build up the query - this way we only need to do one
|
# We use Q objects to gradually build up the query - this way we only
|
||||||
# database lookup at the end rather than gradually refining with multiple filter:s.
|
# need to do one database lookup at the end rather than gradually
|
||||||
# Django Note: Q objects can be combined with & and | (=AND,OR). ~ negates the queryset
|
# refining with multiple filter:s. Django Note: Q objects can be
|
||||||
|
# combined with & and | (=AND,OR). ~ negates the queryset
|
||||||
|
|
||||||
# filter by sender
|
# filter by sender
|
||||||
sender, styp = identify_object(sender)
|
sender, styp = identify_object(sender)
|
||||||
|
|
@ -238,6 +264,7 @@ class MsgManager(models.Manager):
|
||||||
# execute the query
|
# execute the query
|
||||||
return list(self.filter(sender_restrict & receiver_restrict & fulltext_restrict))
|
return list(self.filter(sender_restrict & receiver_restrict & fulltext_restrict))
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Channel manager
|
# Channel manager
|
||||||
#
|
#
|
||||||
|
|
@ -350,9 +377,12 @@ class ChannelManager(models.Manager):
|
||||||
channels = self.filter(db_key__iexact=ostring)
|
channels = self.filter(db_key__iexact=ostring)
|
||||||
if not channels:
|
if not channels:
|
||||||
# still no match. Search by alias.
|
# still no match. Search by alias.
|
||||||
channels = [channel for channel in self.all() if ostring.lower() in [a.lower for a in channel.aliases.all()]]
|
channels = [channel for channel in self.all()
|
||||||
|
if ostring.lower() in [a.lower
|
||||||
|
for a in channel.aliases.all()]]
|
||||||
return channels
|
return channels
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# PlayerChannelConnection manager
|
# PlayerChannelConnection manager
|
||||||
#
|
#
|
||||||
|
|
@ -419,6 +449,7 @@ class PlayerChannelConnectionManager(models.Manager):
|
||||||
for conn in conns:
|
for conn in conns:
|
||||||
conn.delete()
|
conn.delete()
|
||||||
|
|
||||||
|
|
||||||
class ExternalChannelConnectionManager(models.Manager):
|
class ExternalChannelConnectionManager(models.Manager):
|
||||||
"""
|
"""
|
||||||
This ExternalChannelConnectionManager implements methods for searching
|
This ExternalChannelConnectionManager implements methods for searching
|
||||||
|
|
|
||||||
|
|
@ -30,12 +30,14 @@ from src.locks.lockhandler import LockHandler
|
||||||
from src.utils import logger
|
from src.utils import logger
|
||||||
from src.utils.utils import is_iter, to_str, crop, make_iter
|
from src.utils.utils import is_iter, to_str, crop, make_iter
|
||||||
|
|
||||||
__all__ = ("Msg", "TempMsg", "ChannelDB", "PlayerChannelConnection", "ExternalChannelConnection")
|
__all__ = ("Msg", "TempMsg", "ChannelDB",
|
||||||
|
"PlayerChannelConnection", "ExternalChannelConnection")
|
||||||
|
|
||||||
_GA = object.__getattribute__
|
_GA = object.__getattribute__
|
||||||
_SA = object.__setattr__
|
_SA = object.__setattr__
|
||||||
_DA = object.__delattr__
|
_DA = object.__delattr__
|
||||||
|
|
||||||
|
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
#
|
#
|
||||||
# Msg
|
# Msg
|
||||||
|
|
@ -66,9 +68,9 @@ class Msg(SharedMemoryModel):
|
||||||
# These databse fields are all set using their corresponding properties,
|
# These databse fields are all set using their corresponding properties,
|
||||||
# named same as the field, but withtout the db_* prefix.
|
# named same as the field, but withtout the db_* prefix.
|
||||||
|
|
||||||
# Sender is either a player, an object or an external sender, like an IRC channel
|
# Sender is either a player, an object or an external sender, like
|
||||||
# normally there is only one, but if co-modification of a message is allowed, there
|
# an IRC channel; normally there is only one, but if co-modification of
|
||||||
# may be more than one "author"
|
# a message is allowed, there may be more than one "author"
|
||||||
db_sender_players = models.ManyToManyField("players.PlayerDB", related_name='sender_player_set', null=True, verbose_name='sender(player)', db_index=True)
|
db_sender_players = models.ManyToManyField("players.PlayerDB", related_name='sender_player_set', null=True, verbose_name='sender(player)', db_index=True)
|
||||||
db_sender_objects = models.ManyToManyField("objects.ObjectDB", related_name='sender_object_set', null=True, verbose_name='sender(object)', db_index=True)
|
db_sender_objects = models.ManyToManyField("objects.ObjectDB", related_name='sender_object_set', null=True, verbose_name='sender(object)', db_index=True)
|
||||||
db_sender_external = models.CharField('external sender', max_length=255, null=True, db_index=True,
|
db_sender_external = models.CharField('external sender', max_length=255, null=True, db_index=True,
|
||||||
|
|
@ -80,8 +82,8 @@ class Msg(SharedMemoryModel):
|
||||||
db_receivers_objects = models.ManyToManyField('objects.ObjectDB', related_name='receiver_object_set', null=True, help_text="object receivers")
|
db_receivers_objects = models.ManyToManyField('objects.ObjectDB', related_name='receiver_object_set', null=True, help_text="object receivers")
|
||||||
db_receivers_channels = models.ManyToManyField("ChannelDB", related_name='channel_set', null=True, help_text="channel recievers")
|
db_receivers_channels = models.ManyToManyField("ChannelDB", related_name='channel_set', null=True, help_text="channel recievers")
|
||||||
|
|
||||||
# header could be used for meta-info about the message if your system needs it, or as a separate
|
# header could be used for meta-info about the message if your system needs
|
||||||
# store for the mail subject line maybe.
|
# it, or as a separate store for the mail subject line maybe.
|
||||||
db_header = models.TextField('header', null=True, blank=True)
|
db_header = models.TextField('header', null=True, blank=True)
|
||||||
# the message body itself
|
# the message body itself
|
||||||
db_message = models.TextField('messsage')
|
db_message = models.TextField('messsage')
|
||||||
|
|
@ -124,6 +126,7 @@ class Msg(SharedMemoryModel):
|
||||||
list(self.db_sender_players.all()) +
|
list(self.db_sender_players.all()) +
|
||||||
list(self.db_sender_objects.all()) +
|
list(self.db_sender_objects.all()) +
|
||||||
self.extra_senders]
|
self.extra_senders]
|
||||||
|
|
||||||
#@sender.setter
|
#@sender.setter
|
||||||
def __senders_set(self, value):
|
def __senders_set(self, value):
|
||||||
"Setter. Allows for self.sender = value"
|
"Setter. Allows for self.sender = value"
|
||||||
|
|
@ -143,6 +146,7 @@ class Msg(SharedMemoryModel):
|
||||||
else:
|
else:
|
||||||
raise ValueError(obj)
|
raise ValueError(obj)
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
#@sender.deleter
|
#@sender.deleter
|
||||||
def __senders_del(self):
|
def __senders_del(self):
|
||||||
"Deleter. Clears all senders"
|
"Deleter. Clears all senders"
|
||||||
|
|
@ -173,12 +177,19 @@ class Msg(SharedMemoryModel):
|
||||||
# receivers property
|
# receivers property
|
||||||
#@property
|
#@property
|
||||||
def __receivers_get(self):
|
def __receivers_get(self):
|
||||||
"Getter. Allows for value = self.receivers. Returns three lists of receivers: players, objects and channels."
|
"""
|
||||||
|
Getter. Allows for value = self.receivers.
|
||||||
|
Returns three lists of receivers: players, objects and channels.
|
||||||
|
"""
|
||||||
return [hasattr(o, "typeclass") and o.typeclass or o for o in
|
return [hasattr(o, "typeclass") and o.typeclass or o for o in
|
||||||
list(self.db_receivers_players.all()) + list(self.db_receivers_objects.all())]
|
list(self.db_receivers_players.all()) + list(self.db_receivers_objects.all())]
|
||||||
|
|
||||||
#@receivers.setter
|
#@receivers.setter
|
||||||
def __receivers_set(self, value):
|
def __receivers_set(self, value):
|
||||||
"Setter. Allows for self.receivers = value. This appends a new receiver to the message."
|
"""
|
||||||
|
Setter. Allows for self.receivers = value.
|
||||||
|
This appends a new receiver to the message.
|
||||||
|
"""
|
||||||
for val in (v for v in make_iter(value) if v):
|
for val in (v for v in make_iter(value) if v):
|
||||||
obj, typ = identify_object(val)
|
obj, typ = identify_object(val)
|
||||||
if typ == 'player':
|
if typ == 'player':
|
||||||
|
|
@ -190,6 +201,7 @@ class Msg(SharedMemoryModel):
|
||||||
else:
|
else:
|
||||||
raise ValueError
|
raise ValueError
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
#@receivers.deleter
|
#@receivers.deleter
|
||||||
def __receivers_del(self):
|
def __receivers_del(self):
|
||||||
"Deleter. Clears all receivers"
|
"Deleter. Clears all receivers"
|
||||||
|
|
@ -215,11 +227,15 @@ class Msg(SharedMemoryModel):
|
||||||
def __channels_get(self):
|
def __channels_get(self):
|
||||||
"Getter. Allows for value = self.channels. Returns a list of channels."
|
"Getter. Allows for value = self.channels. Returns a list of channels."
|
||||||
return self.db_receivers_channels.all()
|
return self.db_receivers_channels.all()
|
||||||
|
|
||||||
#@channels.setter
|
#@channels.setter
|
||||||
def __channels_set(self, value):
|
def __channels_set(self, value):
|
||||||
"Setter. Allows for self.channels = value. Requires a channel to be added."
|
"""
|
||||||
|
Setter. Allows for self.channels = value.
|
||||||
|
Requires a channel to be added."""
|
||||||
for val in (v.dbobj for v in make_iter(value) if v):
|
for val in (v.dbobj for v in make_iter(value) if v):
|
||||||
self.db_receivers_channels.add(val)
|
self.db_receivers_channels.add(val)
|
||||||
|
|
||||||
#@channels.deleter
|
#@channels.deleter
|
||||||
def __channels_del(self):
|
def __channels_del(self):
|
||||||
"Deleter. Allows for del self.channels"
|
"Deleter. Allows for del self.channels"
|
||||||
|
|
@ -228,8 +244,12 @@ class Msg(SharedMemoryModel):
|
||||||
channels = property(__channels_get, __channels_set, __channels_del)
|
channels = property(__channels_get, __channels_set, __channels_del)
|
||||||
|
|
||||||
def __hide_from_get(self):
|
def __hide_from_get(self):
|
||||||
"Getter. Allows for value = self.hide_from. Returns 3 lists of players, objects and channels"
|
"""
|
||||||
|
Getter. Allows for value = self.hide_from.
|
||||||
|
Returns 3 lists of players, objects and channels
|
||||||
|
"""
|
||||||
return self.db_hide_from_players.all(), self.db_hide_from_objects.all(), self.db_hide_from_channels.all()
|
return self.db_hide_from_players.all(), self.db_hide_from_objects.all(), self.db_hide_from_channels.all()
|
||||||
|
|
||||||
#@hide_from_sender.setter
|
#@hide_from_sender.setter
|
||||||
def __hide_from_set(self, value):
|
def __hide_from_set(self, value):
|
||||||
"Setter. Allows for self.hide_from = value. Will append to hiders"
|
"Setter. Allows for self.hide_from = value. Will append to hiders"
|
||||||
|
|
@ -243,6 +263,7 @@ class Msg(SharedMemoryModel):
|
||||||
else:
|
else:
|
||||||
raise ValueError
|
raise ValueError
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
#@hide_from_sender.deleter
|
#@hide_from_sender.deleter
|
||||||
def __hide_from_del(self):
|
def __hide_from_del(self):
|
||||||
"Deleter. Allows for del self.hide_from_senders"
|
"Deleter. Allows for del self.hide_from_senders"
|
||||||
|
|
@ -275,7 +296,6 @@ class TempMsg(object):
|
||||||
temporary messages that will not be stored.
|
temporary messages that will not be stored.
|
||||||
It mimics the "real" Msg object, but don't require
|
It mimics the "real" Msg object, but don't require
|
||||||
sender to be given.
|
sender to be given.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
def __init__(self, senders=None, receivers=None, channels=None, message="", header="", type="", lockstring="", hide_from=None):
|
def __init__(self, senders=None, receivers=None, channels=None, message="", header="", type="", lockstring="", hide_from=None):
|
||||||
self.senders = senders and make_iter(senders) or []
|
self.senders = senders and make_iter(senders) or []
|
||||||
|
|
@ -301,7 +321,7 @@ class TempMsg(object):
|
||||||
try:
|
try:
|
||||||
self.senders.remove(o)
|
self.senders.remove(o)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pass # nothing to remove
|
pass # nothing to remove
|
||||||
|
|
||||||
def remove_receiver(self, obj):
|
def remove_receiver(self, obj):
|
||||||
"Remove a sender or a list of senders"
|
"Remove a sender or a list of senders"
|
||||||
|
|
@ -309,11 +329,13 @@ class TempMsg(object):
|
||||||
try:
|
try:
|
||||||
self.senders.remove(o)
|
self.senders.remove(o)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pass # nothing to remove
|
pass # nothing to remove
|
||||||
|
|
||||||
def access(self, accessing_obj, access_type='read', default=False):
|
def access(self, accessing_obj, access_type='read', default=False):
|
||||||
"checks lock access"
|
"checks lock access"
|
||||||
return self.locks.check(accessing_obj, access_type=access_type, default=default)
|
return self.locks.check(accessing_obj,
|
||||||
|
access_type=access_type, default=default)
|
||||||
|
|
||||||
|
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
#
|
#
|
||||||
|
|
@ -347,7 +369,6 @@ class ChannelDB(TypedObject):
|
||||||
_SA(self, "aliases", AliasHandler(self, category_prefix="comm_"))
|
_SA(self, "aliases", AliasHandler(self, category_prefix="comm_"))
|
||||||
_SA(self, "attributes", AttributeHandler(self))
|
_SA(self, "attributes", AttributeHandler(self))
|
||||||
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"Define Django meta options"
|
"Define Django meta options"
|
||||||
verbose_name = "Channel"
|
verbose_name = "Channel"
|
||||||
|
|
@ -415,6 +436,7 @@ class ChannelDB(TypedObject):
|
||||||
"""
|
"""
|
||||||
return self.locks.check(accessing_obj, access_type=access_type, default=default)
|
return self.locks.check(accessing_obj, access_type=access_type, default=default)
|
||||||
|
|
||||||
|
|
||||||
class PlayerChannelConnection(SharedMemoryModel):
|
class PlayerChannelConnection(SharedMemoryModel):
|
||||||
"""
|
"""
|
||||||
This connects a player object to a particular comm channel.
|
This connects a player object to a particular comm channel.
|
||||||
|
|
@ -435,11 +457,13 @@ class PlayerChannelConnection(SharedMemoryModel):
|
||||||
def player_get(self):
|
def player_get(self):
|
||||||
"Getter. Allows for value = self.player"
|
"Getter. Allows for value = self.player"
|
||||||
return self.db_player
|
return self.db_player
|
||||||
|
|
||||||
#@player.setter
|
#@player.setter
|
||||||
def player_set(self, value):
|
def player_set(self, value):
|
||||||
"Setter. Allows for self.player = value"
|
"Setter. Allows for self.player = value"
|
||||||
self.db_player = value
|
self.db_player = value
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
#@player.deleter
|
#@player.deleter
|
||||||
def player_del(self):
|
def player_del(self):
|
||||||
"Deleter. Allows for del self.player. Deletes connection."
|
"Deleter. Allows for del self.player. Deletes connection."
|
||||||
|
|
@ -451,11 +475,13 @@ class PlayerChannelConnection(SharedMemoryModel):
|
||||||
def channel_get(self):
|
def channel_get(self):
|
||||||
"Getter. Allows for value = self.channel"
|
"Getter. Allows for value = self.channel"
|
||||||
return self.db_channel.typeclass
|
return self.db_channel.typeclass
|
||||||
|
|
||||||
#@channel.setter
|
#@channel.setter
|
||||||
def channel_set(self, value):
|
def channel_set(self, value):
|
||||||
"Setter. Allows for self.channel = value"
|
"Setter. Allows for self.channel = value"
|
||||||
self.db_channel = value.dbobj
|
self.db_channel = value.dbobj
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
#@channel.deleter
|
#@channel.deleter
|
||||||
def channel_del(self):
|
def channel_del(self):
|
||||||
"Deleter. Allows for del self.channel. Deletes connection."
|
"Deleter. Allows for del self.channel. Deletes connection."
|
||||||
|
|
@ -507,10 +533,12 @@ class ExternalChannelConnection(SharedMemoryModel):
|
||||||
"Getter. Allows for value = self.channel"
|
"Getter. Allows for value = self.channel"
|
||||||
return self.db_channel
|
return self.db_channel
|
||||||
#@channel.setter
|
#@channel.setter
|
||||||
|
|
||||||
def channel_set(self, value):
|
def channel_set(self, value):
|
||||||
"Setter. Allows for self.channel = value"
|
"Setter. Allows for self.channel = value"
|
||||||
self.db_channel = value
|
self.db_channel = value
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
#@channel.deleter
|
#@channel.deleter
|
||||||
def channel_del(self):
|
def channel_del(self):
|
||||||
"Deleter. Allows for del self.channel. Deletes connection."
|
"Deleter. Allows for del self.channel. Deletes connection."
|
||||||
|
|
@ -522,11 +550,13 @@ class ExternalChannelConnection(SharedMemoryModel):
|
||||||
def external_key_get(self):
|
def external_key_get(self):
|
||||||
"Getter. Allows for value = self.external_key"
|
"Getter. Allows for value = self.external_key"
|
||||||
return self.db_external_key
|
return self.db_external_key
|
||||||
|
|
||||||
#@external_key.setter
|
#@external_key.setter
|
||||||
def external_key_set(self, value):
|
def external_key_set(self, value):
|
||||||
"Setter. Allows for self.external_key = value"
|
"Setter. Allows for self.external_key = value"
|
||||||
self.db_external_key = value
|
self.db_external_key = value
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
#@external_key.deleter
|
#@external_key.deleter
|
||||||
def external_key_del(self):
|
def external_key_del(self):
|
||||||
"Deleter. Allows for del self.external_key. Deletes connection."
|
"Deleter. Allows for del self.external_key. Deletes connection."
|
||||||
|
|
@ -538,11 +568,13 @@ class ExternalChannelConnection(SharedMemoryModel):
|
||||||
def external_send_code_get(self):
|
def external_send_code_get(self):
|
||||||
"Getter. Allows for value = self.external_send_code"
|
"Getter. Allows for value = self.external_send_code"
|
||||||
return self.db_external_send_code
|
return self.db_external_send_code
|
||||||
|
|
||||||
#@external_send_code.setter
|
#@external_send_code.setter
|
||||||
def external_send_code_set(self, value):
|
def external_send_code_set(self, value):
|
||||||
"Setter. Allows for self.external_send_code = value"
|
"Setter. Allows for self.external_send_code = value"
|
||||||
self.db_external_send_code = value
|
self.db_external_send_code = value
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
#@external_send_code.deleter
|
#@external_send_code.deleter
|
||||||
def external_send_code_del(self):
|
def external_send_code_del(self):
|
||||||
"Deleter. Allows for del self.external_send_code. Deletes connection."
|
"Deleter. Allows for del self.external_send_code. Deletes connection."
|
||||||
|
|
@ -555,11 +587,13 @@ class ExternalChannelConnection(SharedMemoryModel):
|
||||||
def external_config_get(self):
|
def external_config_get(self):
|
||||||
"Getter. Allows for value = self.external_config"
|
"Getter. Allows for value = self.external_config"
|
||||||
return self.db_external_config
|
return self.db_external_config
|
||||||
|
|
||||||
#@external_config.setter
|
#@external_config.setter
|
||||||
def external_config_set(self, value):
|
def external_config_set(self, value):
|
||||||
"Setter. Allows for self.external_config = value"
|
"Setter. Allows for self.external_config = value"
|
||||||
self.db_external_config = value
|
self.db_external_config = value
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
#@external_config.deleter
|
#@external_config.deleter
|
||||||
def external_config_del(self):
|
def external_config_del(self):
|
||||||
"Deleter. Allows for del self.external_config. Deletes connection."
|
"Deleter. Allows for del self.external_config. Deletes connection."
|
||||||
|
|
@ -572,11 +606,13 @@ class ExternalChannelConnection(SharedMemoryModel):
|
||||||
def is_enabled_get(self):
|
def is_enabled_get(self):
|
||||||
"Getter. Allows for value = self.is_enabled"
|
"Getter. Allows for value = self.is_enabled"
|
||||||
return self.db_is_enabled
|
return self.db_is_enabled
|
||||||
|
|
||||||
#@is_enabled.setter
|
#@is_enabled.setter
|
||||||
def is_enabled_set(self, value):
|
def is_enabled_set(self, value):
|
||||||
"Setter. Allows for self.is_enabled = value"
|
"Setter. Allows for self.is_enabled = value"
|
||||||
self.db_is_enabled = value
|
self.db_is_enabled = value
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
#@is_enabled.deleter
|
#@is_enabled.deleter
|
||||||
def is_enabled_del(self):
|
def is_enabled_del(self):
|
||||||
"Deleter. Allows for del self.is_enabled. Deletes connection."
|
"Deleter. Allows for del self.is_enabled. Deletes connection."
|
||||||
|
|
@ -589,8 +625,8 @@ class ExternalChannelConnection(SharedMemoryModel):
|
||||||
|
|
||||||
def to_channel(self, message, *args, **kwargs):
|
def to_channel(self, message, *args, **kwargs):
|
||||||
"Send external -> channel"
|
"Send external -> channel"
|
||||||
if 'from_obj' in kwargs and kwargs.pop('from_obj'):
|
#if 'from_obj' in kwargs and kwargs.pop('from_obj'):
|
||||||
from_obj = self.external_key
|
# from_obj = self.external_key
|
||||||
self.channel.msg(message, senders=[self], *args, **kwargs)
|
self.channel.msg(message, senders=[self], *args, **kwargs)
|
||||||
|
|
||||||
def to_external(self, message, senders=None, from_channel=None):
|
def to_external(self, message, senders=None, from_channel=None):
|
||||||
|
|
@ -599,12 +635,13 @@ class ExternalChannelConnection(SharedMemoryModel):
|
||||||
# make sure we are not echoing back our own message to ourselves
|
# make sure we are not echoing back our own message to ourselves
|
||||||
# (this would result in a nasty infinite loop)
|
# (this would result in a nasty infinite loop)
|
||||||
#print senders
|
#print senders
|
||||||
if self in make_iter(senders):#.external_key:
|
if self in make_iter(senders): #.external_key:
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# we execute the code snippet that should make it possible for the
|
# we execute the code snippet that should make it possible for the
|
||||||
# connection to contact the protocol correctly (as set by the protocol).
|
# connection to contact the protocol correctly (as set by the
|
||||||
|
# protocol).
|
||||||
# Note that the code block has access to the variables here, such
|
# Note that the code block has access to the variables here, such
|
||||||
# as message, from_obj and from_channel.
|
# as message, from_obj and from_channel.
|
||||||
exec(to_str(self.external_send_code))
|
exec(to_str(self.external_send_code))
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ RETAG = re.compile(r'<[^>]*?>')
|
||||||
# holds rss readers they can be shut down at will.
|
# holds rss readers they can be shut down at will.
|
||||||
RSS_READERS = {}
|
RSS_READERS = {}
|
||||||
|
|
||||||
|
|
||||||
def msg_info(message):
|
def msg_info(message):
|
||||||
"""
|
"""
|
||||||
Send info to default info channel
|
Send info to default info channel
|
||||||
|
|
@ -37,6 +38,7 @@ if RSS_ENABLED:
|
||||||
except ImportError:
|
except ImportError:
|
||||||
raise ImportError("RSS requires python-feedparser to be installed. Install or set RSS_ENABLED=False.")
|
raise ImportError("RSS requires python-feedparser to be installed. Install or set RSS_ENABLED=False.")
|
||||||
|
|
||||||
|
|
||||||
class RSSReader(object):
|
class RSSReader(object):
|
||||||
"""
|
"""
|
||||||
Reader script used to connect to each individual RSS feed
|
Reader script used to connect to each individual RSS feed
|
||||||
|
|
@ -50,7 +52,7 @@ class RSSReader(object):
|
||||||
self.key = key
|
self.key = key
|
||||||
self.url = url
|
self.url = url
|
||||||
self.interval = interval
|
self.interval = interval
|
||||||
self.entries = {} # stored feeds
|
self.entries = {} # stored feeds
|
||||||
self.task = None
|
self.task = None
|
||||||
# first we do is to load the feed so we don't resend
|
# first we do is to load the feed so we don't resend
|
||||||
# old entries whenever the reader starts.
|
# old entries whenever the reader starts.
|
||||||
|
|
@ -63,7 +65,8 @@ class RSSReader(object):
|
||||||
feed = feedparser.parse(self.url)
|
feed = feedparser.parse(self.url)
|
||||||
new = []
|
new = []
|
||||||
for entry in (e for e in feed['entries'] if e['id'] not in self.entries):
|
for entry in (e for e in feed['entries'] if e['id'] not in self.entries):
|
||||||
txt = "[RSS] %s: %s" % (RETAG.sub("", entry['title']), entry['link'].replace('\n','').encode('utf-8'))
|
txt = "[RSS] %s: %s" % (RETAG.sub("", entry['title']),
|
||||||
|
entry['link'].replace('\n','').encode('utf-8'))
|
||||||
self.entries[entry['id']] = txt
|
self.entries[entry['id']] = txt
|
||||||
new.append(txt)
|
new.append(txt)
|
||||||
return new
|
return new
|
||||||
|
|
@ -92,12 +95,14 @@ class RSSReader(object):
|
||||||
self.task.start(self.interval, now=False)
|
self.task.start(self.interval, now=False)
|
||||||
RSS_READERS[self.key] = self
|
RSS_READERS[self.key] = self
|
||||||
|
|
||||||
|
|
||||||
def build_connection_key(channel, url):
|
def build_connection_key(channel, url):
|
||||||
"This is used to id the connection"
|
"This is used to id the connection"
|
||||||
if hasattr(channel, 'key'):
|
if hasattr(channel, 'key'):
|
||||||
channel = channel.key
|
channel = channel.key
|
||||||
return "rss_%s>%s" % (url, channel)
|
return "rss_%s>%s" % (url, channel)
|
||||||
|
|
||||||
|
|
||||||
def create_connection(channel, url, interval):
|
def create_connection(channel, url, interval):
|
||||||
"""
|
"""
|
||||||
This will create a new RSS->channel connection
|
This will create a new RSS->channel connection
|
||||||
|
|
@ -113,13 +118,17 @@ def create_connection(channel, url, interval):
|
||||||
if old_conns:
|
if old_conns:
|
||||||
return False
|
return False
|
||||||
config = "%s|%i" % (url, interval)
|
config = "%s|%i" % (url, interval)
|
||||||
# There is no sendback from evennia to the rss, so we need not define any sendback code.
|
# There is no sendback from evennia to the rss, so we need not define
|
||||||
conn = ExternalChannelConnection(db_channel=channel, db_external_key=key, db_external_config=config)
|
# any sendback code.
|
||||||
|
conn = ExternalChannelConnection(db_channel=channel,
|
||||||
|
db_external_key=key,
|
||||||
|
db_external_config=config)
|
||||||
conn.save()
|
conn.save()
|
||||||
|
|
||||||
connect_to_rss(conn)
|
connect_to_rss(conn)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def delete_connection(channel, url):
|
def delete_connection(channel, url):
|
||||||
"""
|
"""
|
||||||
Delete rss connection between channel and url
|
Delete rss connection between channel and url
|
||||||
|
|
@ -135,6 +144,7 @@ def delete_connection(channel, url):
|
||||||
reader.task.stop()
|
reader.task.stop()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def connect_to_rss(connection):
|
def connect_to_rss(connection):
|
||||||
"""
|
"""
|
||||||
Create the parser instance and connect to RSS feed and channel
|
Create the parser instance and connect to RSS feed and channel
|
||||||
|
|
@ -145,6 +155,7 @@ def connect_to_rss(connection):
|
||||||
# Create reader (this starts the running task and stores a reference in RSS_TASKS)
|
# Create reader (this starts the running task and stores a reference in RSS_TASKS)
|
||||||
RSSReader(key, url, int(interval))
|
RSSReader(key, url, int(interval))
|
||||||
|
|
||||||
|
|
||||||
def connect_all():
|
def connect_all():
|
||||||
"""
|
"""
|
||||||
Activate all rss feed parsers
|
Activate all rss feed parsers
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
"""
|
"""
|
||||||
Makes it easier to import by grouping all relevant things already at this level.
|
Makes it easier to import by grouping all relevant things already at this level.
|
||||||
|
|
||||||
You can henceforth import most things directly from src.help
|
You can henceforth import most things directly from src.help
|
||||||
Also, the initiated object manager is available as src.help.manager.
|
Also, the initiated object manager is available as src.help.manager.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from src.help.models import *
|
from src.help.models import *
|
||||||
|
|
||||||
manager = HelpEntry.objects
|
manager = HelpEntry.objects
|
||||||
|
|
|
||||||
|
|
@ -1,36 +1,37 @@
|
||||||
"""
|
"""
|
||||||
This defines how to edit help entries in Admin.
|
This defines how to edit help entries in Admin.
|
||||||
"""
|
"""
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from src.help.models import HelpEntry
|
from src.help.models import HelpEntry
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class HelpEntryForm(forms.ModelForm):
|
class HelpEntryForm(forms.ModelForm):
|
||||||
"Defines how to display the help entry"
|
"Defines how to display the help entry"
|
||||||
class Meta:
|
class Meta:
|
||||||
model = HelpEntry
|
model = HelpEntry
|
||||||
db_help_category = forms.CharField(label="Help category", initial='General',
|
db_help_category = forms.CharField(label="Help category", initial='General',
|
||||||
help_text="organizes help entries in lists")
|
help_text="organizes help entries in lists")
|
||||||
db_lock_storage = forms.CharField(label="Locks", initial='view:all()',required=False,
|
db_lock_storage = forms.CharField(label="Locks", initial='view:all()',required=False,
|
||||||
widget=forms.TextInput(attrs={'size':'40'}),)
|
widget=forms.TextInput(attrs={'size':'40'}),)
|
||||||
|
|
||||||
class HelpEntryAdmin(admin.ModelAdmin):
|
class HelpEntryAdmin(admin.ModelAdmin):
|
||||||
"Sets up the admin manaager for help entries"
|
"Sets up the admin manaager for help entries"
|
||||||
|
|
||||||
list_display = ('id', 'db_key', 'db_help_category', 'db_lock_storage')
|
list_display = ('id', 'db_key', 'db_help_category', 'db_lock_storage')
|
||||||
list_display_links = ('id', 'db_key')
|
list_display_links = ('id', 'db_key')
|
||||||
search_fields = ['^db_key', 'db_entrytext']
|
search_fields = ['^db_key', 'db_entrytext']
|
||||||
ordering = ['db_help_category', 'db_key']
|
ordering = ['db_help_category', 'db_key']
|
||||||
save_as = True
|
save_as = True
|
||||||
save_on_top = True
|
save_on_top = True
|
||||||
list_select_related = True
|
list_select_related = True
|
||||||
|
|
||||||
form = HelpEntryForm
|
form = HelpEntryForm
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
(None, {'fields':(('db_key', 'db_help_category'), 'db_entrytext', 'db_lock_storage'),
|
(None, {'fields':(('db_key', 'db_help_category'),
|
||||||
'description':"Sets a Help entry. Set lock to <i>view:all()</I> unless you want to restrict it."}),)
|
'db_entrytext', 'db_lock_storage'),
|
||||||
|
'description':"Sets a Help entry. Set lock to <i>view:all()</I> unless you want to restrict it."}),)
|
||||||
|
|
||||||
admin.site.register(HelpEntry, HelpEntryAdmin)
|
|
||||||
|
admin.site.register(HelpEntry, HelpEntryAdmin)
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ from django.db import models
|
||||||
from src.utils import logger, utils
|
from src.utils import logger, utils
|
||||||
__all__ = ("HelpEntryManager",)
|
__all__ = ("HelpEntryManager",)
|
||||||
|
|
||||||
|
|
||||||
class HelpEntryManager(models.Manager):
|
class HelpEntryManager(models.Manager):
|
||||||
"""
|
"""
|
||||||
This HelpEntryManager implements methods for searching
|
This HelpEntryManager implements methods for searching
|
||||||
|
|
@ -48,7 +49,7 @@ class HelpEntryManager(models.Manager):
|
||||||
Do a fuzzy match, preferably within the category of the
|
Do a fuzzy match, preferably within the category of the
|
||||||
current topic.
|
current topic.
|
||||||
"""
|
"""
|
||||||
return self.filter(db_key__icontains=topicstring).exclude(db_key__iexact=topicstring)
|
return self.filter(db_key__icontains=topicstr).exclude(db_key__iexact=topicstr)
|
||||||
|
|
||||||
def find_topics_with_category(self, help_category):
|
def find_topics_with_category(self, help_category):
|
||||||
"""
|
"""
|
||||||
|
|
@ -92,6 +93,7 @@ class HelpEntryManager(models.Manager):
|
||||||
"""
|
"""
|
||||||
ostring = ostring.strip().lower()
|
ostring = ostring.strip().lower()
|
||||||
if help_category:
|
if help_category:
|
||||||
return self.filter(db_key__iexact=ostring, db_help_category__iexact=help_category)
|
return self.filter(db_key__iexact=ostring,
|
||||||
|
db_help_category__iexact=help_category)
|
||||||
else:
|
else:
|
||||||
return self.filter(db_key__iexact=ostring)
|
return self.filter(db_key__iexact=ostring)
|
||||||
|
|
|
||||||
|
|
@ -12,12 +12,11 @@ game world, policy info, rules and similar.
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from src.utils.idmapper.models import SharedMemoryModel
|
from src.utils.idmapper.models import SharedMemoryModel
|
||||||
from src.help.manager import HelpEntryManager
|
from src.help.manager import HelpEntryManager
|
||||||
from src.utils import ansi
|
|
||||||
from src.typeclasses.models import Tag, TagHandler
|
from src.typeclasses.models import Tag, TagHandler
|
||||||
from src.locks.lockhandler import LockHandler
|
from src.locks.lockhandler import LockHandler
|
||||||
from src.utils.utils import is_iter
|
|
||||||
__all__ = ("HelpEntry",)
|
__all__ = ("HelpEntry",)
|
||||||
|
|
||||||
|
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
#
|
#
|
||||||
# HelpEntry
|
# HelpEntry
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
@ -86,6 +86,7 @@ from src.utils import utils
|
||||||
|
|
||||||
_PERMISSION_HIERARCHY = [p.lower() for p in settings.PERMISSION_HIERARCHY]
|
_PERMISSION_HIERARCHY = [p.lower() for p in settings.PERMISSION_HIERARCHY]
|
||||||
|
|
||||||
|
|
||||||
def _to_player(accessing_obj):
|
def _to_player(accessing_obj):
|
||||||
"Helper function. Makes sure an accessing object is a player object"
|
"Helper function. Makes sure an accessing object is a player object"
|
||||||
if utils.inherits_from(accessing_obj, "src.objects.objects.Object"):
|
if utils.inherits_from(accessing_obj, "src.objects.objects.Object"):
|
||||||
|
|
@ -99,14 +100,21 @@ def _to_player(accessing_obj):
|
||||||
def true(*args, **kwargs):
|
def true(*args, **kwargs):
|
||||||
"Always returns True."
|
"Always returns True."
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def all(*args, **kwargs):
|
def all(*args, **kwargs):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def false(*args, **kwargs):
|
def false(*args, **kwargs):
|
||||||
"Always returns False"
|
"Always returns False"
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def none(*args, **kwargs):
|
def none(*args, **kwargs):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def self(accessing_obj, accessed_obj, *args, **kwargs):
|
def self(accessing_obj, accessed_obj, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Check if accessing_obj is the same as accessed_obj
|
Check if accessing_obj is the same as accessed_obj
|
||||||
|
|
@ -172,7 +180,8 @@ def perm(accessing_obj, accessed_obj, *args, **kwargs):
|
||||||
else:
|
else:
|
||||||
return hpos_target <= hpos_player
|
return hpos_target <= hpos_player
|
||||||
elif not is_quell and perm in perms_player:
|
elif not is_quell and perm in perms_player:
|
||||||
# if we get here, check player perms first, otherwise continue as normal
|
# if we get here, check player perms first, otherwise
|
||||||
|
# continue as normal
|
||||||
return True
|
return True
|
||||||
|
|
||||||
if perm in perms_object:
|
if perm in perms_object:
|
||||||
|
|
@ -185,6 +194,7 @@ def perm(accessing_obj, accessed_obj, *args, **kwargs):
|
||||||
if hperm in perms_object and hpos_target < hpos)
|
if hperm in perms_object and hpos_target < hpos)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def perm_above(accessing_obj, accessed_obj, *args, **kwargs):
|
def perm_above(accessing_obj, accessed_obj, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Only allow objects with a permission *higher* in the permission
|
Only allow objects with a permission *higher* in the permission
|
||||||
|
|
@ -193,7 +203,8 @@ def perm_above(accessing_obj, accessed_obj, *args, **kwargs):
|
||||||
this function has no meaning and returns False.
|
this function has no meaning and returns False.
|
||||||
"""
|
"""
|
||||||
kwargs["_greater_than"] = True
|
kwargs["_greater_than"] = True
|
||||||
return perm(accessing_obj,accessed_obj, *args, **kwargs)
|
return perm(accessing_obj, accessed_obj, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def pperm(accessing_obj, accessed_obj, *args, **kwargs):
|
def pperm(accessing_obj, accessed_obj, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
|
@ -209,6 +220,7 @@ def pperm(accessing_obj, accessed_obj, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
return perm(_to_player(accessing_obj), accessed_obj, *args, **kwargs)
|
return perm(_to_player(accessing_obj), accessed_obj, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def pperm_above(accessing_obj, accessed_obj, *args, **kwargs):
|
def pperm_above(accessing_obj, accessed_obj, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Only allow Player objects with a permission *higher* in the permission
|
Only allow Player objects with a permission *higher* in the permission
|
||||||
|
|
@ -218,6 +230,7 @@ def pperm_above(accessing_obj, accessed_obj, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
return perm_above(_to_player(accessing_obj), accessed_obj, *args, **kwargs)
|
return perm_above(_to_player(accessing_obj), accessed_obj, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def dbref(accessing_obj, accessed_obj, *args, **kwargs):
|
def dbref(accessing_obj, accessed_obj, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Usage:
|
Usage:
|
||||||
|
|
@ -238,16 +251,19 @@ def dbref(accessing_obj, accessed_obj, *args, **kwargs):
|
||||||
return dbref == accessing_obj.dbid
|
return dbref == accessing_obj.dbid
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def pdbref(accessing_obj, accessed_obj, *args, **kwargs):
|
def pdbref(accessing_obj, accessed_obj, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Same as dbref, but making sure accessing_obj is a player.
|
Same as dbref, but making sure accessing_obj is a player.
|
||||||
"""
|
"""
|
||||||
return dbref(_to_player(accessing_obj), accessed_obj, *args, **kwargs)
|
return dbref(_to_player(accessing_obj), accessed_obj, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def id(accessing_obj, accessed_obj, *args, **kwargs):
|
def id(accessing_obj, accessed_obj, *args, **kwargs):
|
||||||
"Alias to dbref"
|
"Alias to dbref"
|
||||||
return dbref(accessing_obj, accessed_obj, *args, **kwargs)
|
return dbref(accessing_obj, accessed_obj, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def pid(accessing_obj, accessed_obj, *args, **kwargs):
|
def pid(accessing_obj, accessed_obj, *args, **kwargs):
|
||||||
"Alias to dbref, for Players"
|
"Alias to dbref, for Players"
|
||||||
return dbref(_to_player(accessing_obj), accessed_obj, *args, **kwargs)
|
return dbref(_to_player(accessing_obj), accessed_obj, *args, **kwargs)
|
||||||
|
|
@ -262,6 +278,7 @@ CF_MAPPING = {'eq': lambda val1, val2: val1 == val2 or int(val1) == int(val2),
|
||||||
'ne': lambda val1, val2: int(val1) != int(val2),
|
'ne': lambda val1, val2: int(val1) != int(val2),
|
||||||
'default': lambda val1, val2: False}
|
'default': lambda val1, val2: False}
|
||||||
|
|
||||||
|
|
||||||
def attr(accessing_obj, accessed_obj, *args, **kwargs):
|
def attr(accessing_obj, accessed_obj, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Usage:
|
Usage:
|
||||||
|
|
@ -297,22 +314,26 @@ def attr(accessing_obj, accessed_obj, *args, **kwargs):
|
||||||
try:
|
try:
|
||||||
return CF_MAPPING.get(typ, 'default')(val1, val2)
|
return CF_MAPPING.get(typ, 'default')(val1, val2)
|
||||||
except Exception:
|
except Exception:
|
||||||
# this might happen if we try to compare two things that cannot be compared
|
# this might happen if we try to compare two things
|
||||||
|
# that cannot be compared
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# first, look for normal properties on the object trying to gain access
|
# first, look for normal properties on the object trying to gain access
|
||||||
if hasattr(accessing_obj, attrname):
|
if hasattr(accessing_obj, attrname):
|
||||||
if value:
|
if value:
|
||||||
return valcompare(str(getattr(accessing_obj, attrname)), value, compare)
|
return valcompare(str(getattr(accessing_obj, attrname)), value, compare)
|
||||||
return bool(getattr(accessing_obj, attrname)) # will return Fail on False value etc
|
# will return Fail on False value etc
|
||||||
|
return bool(getattr(accessing_obj, attrname))
|
||||||
# check attributes, if they exist
|
# check attributes, if they exist
|
||||||
if (hasattr(accessing_obj, 'attributes') and accessing_obj.attributes.has(attrname)):
|
if (hasattr(accessing_obj, 'attributes') and accessing_obj.attributes.has(attrname)):
|
||||||
if value:
|
if value:
|
||||||
return (hasattr(accessing_obj, 'attributes')
|
return (hasattr(accessing_obj, 'attributes')
|
||||||
and valcompare(accessing_obj.attributes.get(attrname), value, compare))
|
and valcompare(accessing_obj.attributes.get(attrname), value, compare))
|
||||||
return bool(accessing_obj.attributes.get(attrname)) # fails on False/None values
|
# fails on False/None values
|
||||||
|
return bool(accessing_obj.attributes.get(attrname))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def objattr(accessing_obj, accessed_obj, *args, **kwargs):
|
def objattr(accessing_obj, accessed_obj, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Usage:
|
Usage:
|
||||||
|
|
@ -328,6 +349,7 @@ def objattr(accessing_obj, accessed_obj, *args, **kwargs):
|
||||||
if hasattr(accessing_obj, "obj"):
|
if hasattr(accessing_obj, "obj"):
|
||||||
return attr(accessing_obj.obj, accessed_obj, *args, **kwargs)
|
return attr(accessing_obj.obj, accessed_obj, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def locattr(accessing_obj, accessed_obj, *args, **kwargs):
|
def locattr(accessing_obj, accessed_obj, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Usage:
|
Usage:
|
||||||
|
|
@ -350,6 +372,7 @@ def attr_eq(accessing_obj, accessed_obj, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
return attr(accessing_obj, accessed_obj, *args, **kwargs)
|
return attr(accessing_obj, accessed_obj, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def attr_gt(accessing_obj, accessed_obj, *args, **kwargs):
|
def attr_gt(accessing_obj, accessed_obj, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Usage:
|
Usage:
|
||||||
|
|
@ -357,7 +380,9 @@ def attr_gt(accessing_obj, accessed_obj, *args, **kwargs):
|
||||||
|
|
||||||
Only true if access_obj's attribute > the value given.
|
Only true if access_obj's attribute > the value given.
|
||||||
"""
|
"""
|
||||||
return attr(accessing_obj, accessed_obj, *args, **{'compare':'gt'})
|
return attr(accessing_obj, accessed_obj, *args, **{'compare': 'gt'})
|
||||||
|
|
||||||
|
|
||||||
def attr_ge(accessing_obj, accessed_obj, *args, **kwargs):
|
def attr_ge(accessing_obj, accessed_obj, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Usage:
|
Usage:
|
||||||
|
|
@ -365,7 +390,9 @@ def attr_ge(accessing_obj, accessed_obj, *args, **kwargs):
|
||||||
|
|
||||||
Only true if access_obj's attribute >= the value given.
|
Only true if access_obj's attribute >= the value given.
|
||||||
"""
|
"""
|
||||||
return attr(accessing_obj, accessed_obj, *args, **{'compare':'ge'})
|
return attr(accessing_obj, accessed_obj, *args, **{'compare': 'ge'})
|
||||||
|
|
||||||
|
|
||||||
def attr_lt(accessing_obj, accessed_obj, *args, **kwargs):
|
def attr_lt(accessing_obj, accessed_obj, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Usage:
|
Usage:
|
||||||
|
|
@ -373,7 +400,9 @@ def attr_lt(accessing_obj, accessed_obj, *args, **kwargs):
|
||||||
|
|
||||||
Only true if access_obj's attribute < the value given.
|
Only true if access_obj's attribute < the value given.
|
||||||
"""
|
"""
|
||||||
return attr(accessing_obj, accessed_obj, *args, **{'compare':'lt'})
|
return attr(accessing_obj, accessed_obj, *args, **{'compare': 'lt'})
|
||||||
|
|
||||||
|
|
||||||
def attr_le(accessing_obj, accessed_obj, *args, **kwargs):
|
def attr_le(accessing_obj, accessed_obj, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Usage:
|
Usage:
|
||||||
|
|
@ -381,7 +410,9 @@ def attr_le(accessing_obj, accessed_obj, *args, **kwargs):
|
||||||
|
|
||||||
Only true if access_obj's attribute <= the value given.
|
Only true if access_obj's attribute <= the value given.
|
||||||
"""
|
"""
|
||||||
return attr(accessing_obj, accessed_obj, *args, **{'compare':'le'})
|
return attr(accessing_obj, accessed_obj, *args, **{'compare': 'le'})
|
||||||
|
|
||||||
|
|
||||||
def attr_ne(accessing_obj, accessed_obj, *args, **kwargs):
|
def attr_ne(accessing_obj, accessed_obj, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Usage:
|
Usage:
|
||||||
|
|
@ -389,18 +420,22 @@ def attr_ne(accessing_obj, accessed_obj, *args, **kwargs):
|
||||||
|
|
||||||
Only true if access_obj's attribute != the value given.
|
Only true if access_obj's attribute != the value given.
|
||||||
"""
|
"""
|
||||||
return attr(accessing_obj, accessed_obj, *args, **{'compare':'ne'})
|
return attr(accessing_obj, accessed_obj, *args, **{'compare': 'ne'})
|
||||||
|
|
||||||
|
|
||||||
def holds(accessing_obj, accessed_obj, *args, **kwargs):
|
def holds(accessing_obj, accessed_obj, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Usage:
|
Usage:
|
||||||
holds() # checks if accessed_obj or accessed_obj.obj is held by accessing_obj
|
holds() checks if accessed_obj or accessed_obj.obj
|
||||||
holds(key/dbref) # checks if accessing_obj holds an object with given key/dbref
|
is held by accessing_obj
|
||||||
holds(attrname, value) # checks if accessing_obj holds an object with the given attrname and value
|
holds(key/dbref) checks if accessing_obj holds an object
|
||||||
|
with given key/dbref
|
||||||
|
holds(attrname, value) checks if accessing_obj holds an
|
||||||
|
object with the given attrname and value
|
||||||
|
|
||||||
This is passed if accessed_obj is carried by accessing_obj (that is,
|
This is passed if accessed_obj is carried by accessing_obj (that is,
|
||||||
accessed_obj.location == accessing_obj), or if accessing_obj itself holds an
|
accessed_obj.location == accessing_obj), or if accessing_obj itself holds
|
||||||
object matching the given key.
|
an object matching the given key.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
# commands and scripts don't have contents, so we are usually looking
|
# commands and scripts don't have contents, so we are usually looking
|
||||||
|
|
@ -412,6 +447,7 @@ def holds(accessing_obj, accessed_obj, *args, **kwargs):
|
||||||
contents = accessing_obj.obj.contents
|
contents = accessing_obj.obj.contents
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def check_holds(objid):
|
def check_holds(objid):
|
||||||
# helper function. Compares both dbrefs and keys/aliases.
|
# helper function. Compares both dbrefs and keys/aliases.
|
||||||
objid = str(objid)
|
objid = str(objid)
|
||||||
|
|
@ -449,9 +485,11 @@ def superuser(*args, **kwargs):
|
||||||
"""
|
"""
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def serversetting(accessing_obj, accessed_obj, *args, **kwargs):
|
def serversetting(accessing_obj, accessed_obj, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Only returns true if the Evennia settings exists, alternatively has a certain value.
|
Only returns true if the Evennia settings exists, alternatively has
|
||||||
|
a certain value.
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
serversetting(IRC_ENABLED)
|
serversetting(IRC_ENABLED)
|
||||||
|
|
|
||||||
|
|
@ -67,14 +67,14 @@ Here, the lock-function perm() will be called with the string
|
||||||
'Builders' (accessing_obj and accessed_obj are added automatically,
|
'Builders' (accessing_obj and accessed_obj are added automatically,
|
||||||
you only need to add the args/kwargs, if any).
|
you only need to add the args/kwargs, if any).
|
||||||
|
|
||||||
If we wanted to make sure the accessing object was BOTH a Builders and a GoodGuy, we
|
If we wanted to make sure the accessing object was BOTH a Builders and a
|
||||||
could use AND:
|
GoodGuy, we could use AND:
|
||||||
|
|
||||||
'edit:perm(Builders) AND perm(GoodGuy)'
|
'edit:perm(Builders) AND perm(GoodGuy)'
|
||||||
|
|
||||||
To allow EITHER Builders and GoodGuys, we replace AND with OR. perm() is just one example,
|
To allow EITHER Builders and GoodGuys, we replace AND with OR. perm() is just
|
||||||
the lock function can do anything and compare any properties of the calling object to
|
one example, the lock function can do anything and compare any properties of
|
||||||
decide if the lock is passed or not.
|
the calling object to decide if the lock is passed or not.
|
||||||
|
|
||||||
'lift:attrib(very_strong) AND NOT attrib(bad_back)'
|
'lift:attrib(very_strong) AND NOT attrib(bad_back)'
|
||||||
|
|
||||||
|
|
@ -89,7 +89,8 @@ object would do something like this:
|
||||||
if not target_obj.lockhandler.has_perm(caller, 'edit'):
|
if not target_obj.lockhandler.has_perm(caller, 'edit'):
|
||||||
caller.msg("Sorry, you cannot edit that.")
|
caller.msg("Sorry, you cannot edit that.")
|
||||||
|
|
||||||
All objects also has a shortcut called 'access' that is recommended to use instead:
|
All objects also has a shortcut called 'access' that is recommended to
|
||||||
|
use instead:
|
||||||
|
|
||||||
if not target_obj.access(caller, 'edit'):
|
if not target_obj.access(caller, 'edit'):
|
||||||
caller.msg("Sorry, you cannot edit that.")
|
caller.msg("Sorry, you cannot edit that.")
|
||||||
|
|
@ -104,13 +105,15 @@ to any other identifier you can use.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import re, inspect
|
import re
|
||||||
|
import inspect
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from src.utils import logger, utils
|
from src.utils import logger, utils
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
__all__ = ("LockHandler", "LockException")
|
__all__ = ("LockHandler", "LockException")
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Exception class
|
# Exception class
|
||||||
#
|
#
|
||||||
|
|
@ -119,6 +122,7 @@ class LockException(Exception):
|
||||||
"raised during an error in a lock."
|
"raised during an error in a lock."
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Cached lock functions
|
# Cached lock functions
|
||||||
#
|
#
|
||||||
|
|
@ -186,15 +190,16 @@ class LockHandler(object):
|
||||||
"""
|
"""
|
||||||
Helper function. This is normally only called when the
|
Helper function. This is normally only called when the
|
||||||
lockstring is cached and does preliminary checking. locks are
|
lockstring is cached and does preliminary checking. locks are
|
||||||
stored as a string 'atype:[NOT] lock()[[ AND|OR [NOT] lock()[...]];atype...
|
stored as a string
|
||||||
|
'atype:[NOT] lock()[[ AND|OR [NOT] lock()[...]];atype...
|
||||||
|
|
||||||
"""
|
"""
|
||||||
locks = {}
|
locks = {}
|
||||||
if not storage_lockstring:
|
if not storage_lockstring:
|
||||||
return locks
|
return locks
|
||||||
duplicates = 0
|
duplicates = 0
|
||||||
elist = [] # errors
|
elist = [] # errors
|
||||||
wlist = [] # warnings
|
wlist = [] # warnings
|
||||||
for raw_lockstring in storage_lockstring.split(';'):
|
for raw_lockstring in storage_lockstring.split(';'):
|
||||||
lock_funcs = []
|
lock_funcs = []
|
||||||
try:
|
try:
|
||||||
|
|
@ -234,7 +239,8 @@ class LockHandler(object):
|
||||||
{"access_type":access_type, "source":locks[access_type][2], "goal":raw_lockstring}))
|
{"access_type":access_type, "source":locks[access_type][2], "goal":raw_lockstring}))
|
||||||
locks[access_type] = (evalstring, tuple(lock_funcs), raw_lockstring)
|
locks[access_type] = (evalstring, tuple(lock_funcs), raw_lockstring)
|
||||||
if wlist and self.log_obj:
|
if wlist and self.log_obj:
|
||||||
# a warning text was set, it's not an error, so only report if log_obj is available.
|
# a warning text was set, it's not an error, so only report
|
||||||
|
# if log_obj is available.
|
||||||
self._log_error("\n".join(wlist))
|
self._log_error("\n".join(wlist))
|
||||||
if elist:
|
if elist:
|
||||||
# an error text was set, raise exception.
|
# an error text was set, raise exception.
|
||||||
|
|
@ -252,10 +258,12 @@ class LockHandler(object):
|
||||||
|
|
||||||
def cache_lock_bypass(self, obj):
|
def cache_lock_bypass(self, obj):
|
||||||
"""
|
"""
|
||||||
We cache superuser bypass checks here for efficiency. This needs to be re-run when a player is assigned to a character.
|
We cache superuser bypass checks here for efficiency. This needs to
|
||||||
We need to grant access to superusers. We need to check both directly on the object (players), through obj.player and using the
|
be re-run when a player is assigned to a character.
|
||||||
get_player method (this sits on serversessions, in some rare cases where a check is done
|
We need to grant access to superusers. We need to check both directly
|
||||||
before the login process has yet been fully finalized)
|
on the object (players), through obj.player and using the get_player()
|
||||||
|
method (this sits on serversessions, in some rare cases where a
|
||||||
|
check is done before the login process has yet been fully finalized)
|
||||||
"""
|
"""
|
||||||
self.lock_bypass = hasattr(obj, "is_superuser") and obj.is_superuser
|
self.lock_bypass = hasattr(obj, "is_superuser") and obj.is_superuser
|
||||||
|
|
||||||
|
|
@ -308,7 +316,7 @@ class LockHandler(object):
|
||||||
def get(self, access_type=None):
|
def get(self, access_type=None):
|
||||||
"get the full lockstring or the lockstring of a particular access type."
|
"get the full lockstring or the lockstring of a particular access type."
|
||||||
if access_type:
|
if access_type:
|
||||||
return self.locks.get(access_type, ["","",""])[2]
|
return self.locks.get(access_type, ["", "", ""])[2]
|
||||||
return str(self)
|
return str(self)
|
||||||
|
|
||||||
def delete(self, access_type):
|
def delete(self, access_type):
|
||||||
|
|
@ -342,7 +350,7 @@ class LockHandler(object):
|
||||||
access_type - the type of access wanted
|
access_type - the type of access wanted
|
||||||
default - if no suitable lock type is found, use this
|
default - if no suitable lock type is found, use this
|
||||||
no_superuser_bypass - don't use this unless you really, really need to,
|
no_superuser_bypass - don't use this unless you really, really need to,
|
||||||
it makes supersusers susceptible to the lock check.
|
it makes supersusers susceptible to the lock check.
|
||||||
|
|
||||||
A lock is executed in the follwoing way:
|
A lock is executed in the follwoing way:
|
||||||
|
|
||||||
|
|
@ -403,9 +411,11 @@ class LockHandler(object):
|
||||||
locks = self._parse_lockstring(lockstring)
|
locks = self._parse_lockstring(lockstring)
|
||||||
for access_type in locks:
|
for access_type in locks:
|
||||||
evalstring, func_tup, raw_string = locks[access_type]
|
evalstring, func_tup, raw_string = locks[access_type]
|
||||||
true_false = tuple(tup[0](accessing_obj, self.obj, *tup[1], **tup[2]) for tup in func_tup)
|
true_false = tuple(tup[0](accessing_obj, self.obj, *tup[1],**tup[2])
|
||||||
|
for tup in func_tup)
|
||||||
return eval(evalstring % true_false)
|
return eval(evalstring % true_false)
|
||||||
|
|
||||||
|
|
||||||
def _test():
|
def _test():
|
||||||
# testing
|
# testing
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ except ImportError:
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from src.locks import lockhandler, lockfuncs
|
from src.locks import lockfuncs
|
||||||
from src.utils import create
|
from src.utils import create
|
||||||
|
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue