Merge branch 'develop' into accounts
This commit is contained in:
commit
030a83bf9c
26 changed files with 338 additions and 115 deletions
21
CHANGELOG.md
21
CHANGELOG.md
|
|
@ -1,7 +1,27 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## Evennia 0.9 (2018-2019)
|
||||||
|
|
||||||
|
### Commands
|
||||||
|
|
||||||
|
- Removed default `@delaccount` command, incorporating as `@account/delete` instead. Added confirmation
|
||||||
|
question.
|
||||||
|
- Add new `@force` command to have another object perform a command.
|
||||||
|
- Add the Portal uptime to the `@time` command.
|
||||||
|
- Make the `@link` command first make a local search before a global search.
|
||||||
|
|
||||||
|
### Utils
|
||||||
|
|
||||||
|
- Added more unit tests.
|
||||||
|
|
||||||
|
|
||||||
## Evennia 0.8 (2018)
|
## Evennia 0.8 (2018)
|
||||||
|
|
||||||
|
### Requirements
|
||||||
|
|
||||||
|
- Up requirements to Django 1.11.x, Twisted 18 and pillow 5.2.0
|
||||||
|
- Add `inflect` dependency for automatic pluralization of object names.
|
||||||
|
|
||||||
### Server/Portal
|
### Server/Portal
|
||||||
|
|
||||||
- Removed `evennia_runner`, completely refactor `evennia_launcher.py` (the 'evennia' program)
|
- Removed `evennia_runner`, completely refactor `evennia_launcher.py` (the 'evennia' program)
|
||||||
|
|
@ -85,7 +105,6 @@
|
||||||
|
|
||||||
### General
|
### General
|
||||||
|
|
||||||
- Up requirements to Django 1.11.x, Twisted 18 and pillow 5.2.0
|
|
||||||
- Start structuring the `CHANGELOG` to list features in more detail.
|
- Start structuring the `CHANGELOG` to list features in more detail.
|
||||||
- Docker image `evennia/evennia:develop` is now auto-built, tracking the develop branch.
|
- Docker image `evennia/evennia:develop` is now auto-built, tracking the develop branch.
|
||||||
- Inflection and grouping of multiple objects in default room (an box, three boxes)
|
- Inflection and grouping of multiple objects in default room (an box, three boxes)
|
||||||
|
|
|
||||||
|
|
@ -1318,7 +1318,10 @@ class DefaultAccount(with_metaclass(TypeclassBase, AccountDB)):
|
||||||
|
|
||||||
if target and not is_iter(target):
|
if target and not is_iter(target):
|
||||||
# single target - just show it
|
# single target - just show it
|
||||||
return target.return_appearance(self)
|
if hasattr(target, "return_appearance"):
|
||||||
|
return target.return_appearance(self)
|
||||||
|
else:
|
||||||
|
return "{} has no in-game appearance.".format(target)
|
||||||
else:
|
else:
|
||||||
# list of targets - make list to disconnect from db
|
# list of targets - make list to disconnect from db
|
||||||
characters = list(tar for tar in target if tar) if target else []
|
characters = list(tar for tar in target if tar) if target else []
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ from evennia.accounts.accounts import DefaultAccount, DefaultGuest
|
||||||
from evennia.server.session import Session
|
from evennia.server.session import Session
|
||||||
from evennia.utils.test_resources import EvenniaTest
|
from evennia.utils.test_resources import EvenniaTest
|
||||||
from evennia.utils import create
|
from evennia.utils import create
|
||||||
|
from evennia.utils.test_resources import EvenniaTest
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
|
|
@ -268,3 +269,20 @@ class TestDefaultAccount(EvenniaTest):
|
||||||
account.puppet_object(self.s1, obj)
|
account.puppet_object(self.s1, obj)
|
||||||
self.assertTrue(self.s1.data_out.call_args[1]['text'].endswith("is already puppeted by another Account."))
|
self.assertTrue(self.s1.data_out.call_args[1]['text'].endswith("is already puppeted by another Account."))
|
||||||
self.assertIsNone(obj.at_post_puppet.call_args)
|
self.assertIsNone(obj.at_post_puppet.call_args)
|
||||||
|
|
||||||
|
|
||||||
|
class TestAccountPuppetDeletion(EvenniaTest):
|
||||||
|
|
||||||
|
@override_settings(MULTISESSION_MODE=2)
|
||||||
|
def test_puppet_deletion(self):
|
||||||
|
# Check for existing chars
|
||||||
|
self.assertFalse(self.account.db._playable_characters, 'Account should not have any chars by default.')
|
||||||
|
|
||||||
|
# Add char1 to account's playable characters
|
||||||
|
self.account.db._playable_characters.append(self.char1)
|
||||||
|
self.assertTrue(self.account.db._playable_characters, 'Char was not added to account.')
|
||||||
|
|
||||||
|
# See what happens when we delete char1.
|
||||||
|
self.char1.delete()
|
||||||
|
# Playable char list should be empty.
|
||||||
|
self.assertFalse(self.account.db._playable_characters, 'Playable character list is not empty! %s' % self.account.db._playable_characters)
|
||||||
|
|
@ -16,8 +16,8 @@ COMMAND_DEFAULT_CLASS = class_from_module(settings.COMMAND_DEFAULT_CLASS)
|
||||||
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", "CmdDelAccount",
|
__all__ = ("CmdBoot", "CmdBan", "CmdUnban",
|
||||||
"CmdEmit", "CmdNewPassword", "CmdPerm", "CmdWall")
|
"CmdEmit", "CmdNewPassword", "CmdPerm", "CmdWall", "CmdForce")
|
||||||
|
|
||||||
|
|
||||||
class CmdBoot(COMMAND_DEFAULT_CLASS):
|
class CmdBoot(COMMAND_DEFAULT_CLASS):
|
||||||
|
|
@ -133,7 +133,7 @@ class CmdBan(COMMAND_DEFAULT_CLASS):
|
||||||
reason to be able to later remember why the ban was put in place.
|
reason to be able to later remember why the ban was put in place.
|
||||||
|
|
||||||
It is often preferable to ban an account from the server than to
|
It is often preferable to ban an account from the server than to
|
||||||
delete an account with @delaccount. If banned by name, that account
|
delete an account with @accounts/delete. If banned by name, that account
|
||||||
account can no longer be logged into.
|
account can no longer be logged into.
|
||||||
|
|
||||||
IP (Internet Protocol) address banning allows blocking all access
|
IP (Internet Protocol) address banning allows blocking all access
|
||||||
|
|
@ -256,78 +256,6 @@ class CmdUnban(COMMAND_DEFAULT_CLASS):
|
||||||
logger.log_sec('Unbanned: %s (Caller: %s, IP: %s).' % (value.strip(), self.caller, self.session.address))
|
logger.log_sec('Unbanned: %s (Caller: %s, IP: %s).' % (value.strip(), self.caller, self.session.address))
|
||||||
|
|
||||||
|
|
||||||
class CmdDelAccount(COMMAND_DEFAULT_CLASS):
|
|
||||||
"""
|
|
||||||
delete an account from the server
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
@delaccount[/switch] <name> [: reason]
|
|
||||||
|
|
||||||
Switch:
|
|
||||||
delobj - also delete the account's currently
|
|
||||||
assigned in-game object.
|
|
||||||
|
|
||||||
Completely deletes a user from the server database,
|
|
||||||
making their nick and e-mail again available.
|
|
||||||
"""
|
|
||||||
|
|
||||||
key = "@delaccount"
|
|
||||||
switch_options = ("delobj",)
|
|
||||||
locks = "cmd:perm(delaccount) or perm(Developer)"
|
|
||||||
help_category = "Admin"
|
|
||||||
|
|
||||||
def func(self):
|
|
||||||
"""Implements the command."""
|
|
||||||
|
|
||||||
caller = self.caller
|
|
||||||
args = self.args
|
|
||||||
|
|
||||||
if hasattr(caller, 'account'):
|
|
||||||
caller = caller.account
|
|
||||||
|
|
||||||
if not args:
|
|
||||||
self.msg("Usage: @delaccount <account/user name or #id> [: reason]")
|
|
||||||
return
|
|
||||||
|
|
||||||
reason = ""
|
|
||||||
if ':' in args:
|
|
||||||
args, reason = [arg.strip() for arg in args.split(':', 1)]
|
|
||||||
|
|
||||||
# We use account_search since we want to be sure to find also accounts
|
|
||||||
# that lack characters.
|
|
||||||
accounts = search.account_search(args)
|
|
||||||
|
|
||||||
if not accounts:
|
|
||||||
self.msg('Could not find an account by that name.')
|
|
||||||
return
|
|
||||||
|
|
||||||
if len(accounts) > 1:
|
|
||||||
string = "There were multiple matches:\n"
|
|
||||||
string += "\n".join(" %s %s" % (account.id, account.key) for account in accounts)
|
|
||||||
self.msg(string)
|
|
||||||
return
|
|
||||||
|
|
||||||
# one single match
|
|
||||||
|
|
||||||
account = accounts.first()
|
|
||||||
|
|
||||||
if not account.access(caller, 'delete'):
|
|
||||||
string = "You don't have the permissions to delete that account."
|
|
||||||
self.msg(string)
|
|
||||||
return
|
|
||||||
|
|
||||||
uname = account.username
|
|
||||||
# boot the account then delete
|
|
||||||
self.msg("Informing and disconnecting account ...")
|
|
||||||
string = "\nYour account '%s' is being *permanently* deleted.\n" % uname
|
|
||||||
if reason:
|
|
||||||
string += " Reason given:\n '%s'" % reason
|
|
||||||
account.msg(string)
|
|
||||||
logger.log_sec('Account Deleted: %s (Reason: %s, Caller: %s, IP: %s).' % (account, reason, caller, self.session.address))
|
|
||||||
account.delete()
|
|
||||||
self.msg("Account %s was successfully deleted." % uname)
|
|
||||||
|
|
||||||
|
|
||||||
class CmdEmit(COMMAND_DEFAULT_CLASS):
|
class CmdEmit(COMMAND_DEFAULT_CLASS):
|
||||||
"""
|
"""
|
||||||
admin command for emitting message to multiple objects
|
admin command for emitting message to multiple objects
|
||||||
|
|
@ -585,3 +513,33 @@ class CmdWall(COMMAND_DEFAULT_CLASS):
|
||||||
message = "%s shouts \"%s\"" % (self.caller.name, self.args)
|
message = "%s shouts \"%s\"" % (self.caller.name, self.args)
|
||||||
self.msg("Announcing to all connected sessions ...")
|
self.msg("Announcing to all connected sessions ...")
|
||||||
SESSIONS.announce_all(message)
|
SESSIONS.announce_all(message)
|
||||||
|
|
||||||
|
|
||||||
|
class CmdForce(COMMAND_DEFAULT_CLASS):
|
||||||
|
"""
|
||||||
|
forces an object to execute a command
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
@force <object>=<command string>
|
||||||
|
|
||||||
|
Example:
|
||||||
|
@force bob=get stick
|
||||||
|
"""
|
||||||
|
key = "@force"
|
||||||
|
locks = "cmd:perm(spawn) or perm(Builder)"
|
||||||
|
help_category = "Building"
|
||||||
|
perm_used = "edit"
|
||||||
|
|
||||||
|
def func(self):
|
||||||
|
"""Implements the force command"""
|
||||||
|
if not self.lhs or not self.rhs:
|
||||||
|
self.caller.msg("You must provide a target and a command string to execute.")
|
||||||
|
return
|
||||||
|
targ = self.caller.search(self.lhs)
|
||||||
|
if not targ:
|
||||||
|
return
|
||||||
|
if not targ.access(self.caller, self.perm_used):
|
||||||
|
self.caller.msg("You don't have permission to force them to execute commands.")
|
||||||
|
return
|
||||||
|
targ.execute_cmd(self.rhs)
|
||||||
|
self.caller.msg("You have forced %s to: %s" % (targ, self.rhs))
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ from evennia.objects.models import ObjectDB
|
||||||
from evennia.locks.lockhandler import LockException
|
from evennia.locks.lockhandler import LockException
|
||||||
from evennia.commands.cmdhandler import get_and_merge_cmdsets
|
from evennia.commands.cmdhandler import get_and_merge_cmdsets
|
||||||
from evennia.utils import create, utils, search
|
from evennia.utils import create, utils, search
|
||||||
from evennia.utils.utils import inherits_from, class_from_module, get_all_typeclasses
|
from evennia.utils.utils import inherits_from, class_from_module, get_all_typeclasses, variable_from_module
|
||||||
from evennia.utils.eveditor import EvEditor
|
from evennia.utils.eveditor import EvEditor
|
||||||
from evennia.utils.evmore import EvMore
|
from evennia.utils.evmore import EvMore
|
||||||
from evennia.prototypes import spawner, prototypes as protlib, menus as olc_menus
|
from evennia.prototypes import spawner, prototypes as protlib, menus as olc_menus
|
||||||
|
|
@ -612,12 +612,12 @@ class CmdDesc(COMMAND_DEFAULT_CLASS):
|
||||||
self.edit_handler()
|
self.edit_handler()
|
||||||
return
|
return
|
||||||
|
|
||||||
if self.rhs:
|
if '=' in self.args:
|
||||||
# We have an =
|
# We have an =
|
||||||
obj = caller.search(self.lhs)
|
obj = caller.search(self.lhs)
|
||||||
if not obj:
|
if not obj:
|
||||||
return
|
return
|
||||||
desc = self.rhs
|
desc = self.rhs or ''
|
||||||
else:
|
else:
|
||||||
obj = caller.location or self.msg("|rYou can't describe oblivion.|n")
|
obj = caller.location or self.msg("|rYou can't describe oblivion.|n")
|
||||||
if not obj:
|
if not obj:
|
||||||
|
|
@ -737,12 +737,11 @@ class CmdDestroy(COMMAND_DEFAULT_CLASS):
|
||||||
confirm += ", ".join(["#{}".format(obj.id) for obj in objs])
|
confirm += ", ".join(["#{}".format(obj.id) for obj in objs])
|
||||||
confirm += " [yes]/no?" if self.default_confirm == 'yes' else " yes/[no]"
|
confirm += " [yes]/no?" if self.default_confirm == 'yes' else " yes/[no]"
|
||||||
answer = ""
|
answer = ""
|
||||||
while answer.strip().lower() not in ("y", "yes", "n", "no"):
|
answer = yield(confirm)
|
||||||
answer = yield(confirm)
|
answer = self.default_confirm if answer == '' else answer
|
||||||
answer = self.default_confirm if answer == '' else answer
|
|
||||||
|
|
||||||
if answer.strip().lower() in ("n", "no"):
|
if answer.strip().lower() in ("n", "no"):
|
||||||
caller.msg("Cancelled: no object was destroyed.")
|
caller.msg("Canceled: no object was destroyed.")
|
||||||
delete = False
|
delete = False
|
||||||
|
|
||||||
if delete:
|
if delete:
|
||||||
|
|
@ -1023,10 +1022,17 @@ class CmdLink(COMMAND_DEFAULT_CLASS):
|
||||||
|
|
||||||
object_name = self.lhs
|
object_name = self.lhs
|
||||||
|
|
||||||
# get object
|
# try to search locally first
|
||||||
obj = caller.search(object_name, global_search=True)
|
results = caller.search(object_name, quiet=True)
|
||||||
if not obj:
|
if len(results) > 1: # local results was a multimatch. Inform them to be more specific
|
||||||
return
|
_AT_SEARCH_RESULT = variable_from_module(*settings.SEARCH_AT_RESULT.rsplit('.', 1))
|
||||||
|
return _AT_SEARCH_RESULT(results, caller, query=object_name)
|
||||||
|
elif len(results) == 1: # A unique local match
|
||||||
|
obj = results[0]
|
||||||
|
else: # No matches. Search globally
|
||||||
|
obj = caller.search(object_name, global_search=True)
|
||||||
|
if not obj:
|
||||||
|
return
|
||||||
|
|
||||||
if self.rhs:
|
if self.rhs:
|
||||||
# this means a target name was given
|
# this means a target name was given
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,6 @@ class AccountCmdSet(CmdSet):
|
||||||
self.add(system.CmdPy())
|
self.add(system.CmdPy())
|
||||||
|
|
||||||
# Admin commands
|
# Admin commands
|
||||||
self.add(admin.CmdDelAccount())
|
|
||||||
self.add(admin.CmdNewPassword())
|
self.add(admin.CmdNewPassword())
|
||||||
|
|
||||||
# Comm commands
|
# Comm commands
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,7 @@ class CharacterCmdSet(CmdSet):
|
||||||
self.add(admin.CmdEmit())
|
self.add(admin.CmdEmit())
|
||||||
self.add(admin.CmdPerm())
|
self.add(admin.CmdPerm())
|
||||||
self.add(admin.CmdWall())
|
self.add(admin.CmdWall())
|
||||||
|
self.add(admin.CmdForce())
|
||||||
|
|
||||||
# Building and world manipulation
|
# Building and world manipulation
|
||||||
self.add(building.CmdTeleport())
|
self.add(building.CmdTeleport())
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,8 @@ make sure to homogenize self.caller to always be the account object
|
||||||
for easy handling.
|
for easy handling.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
import hashlib
|
||||||
|
import time
|
||||||
from past.builtins import cmp
|
from past.builtins import cmp
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from evennia.comms.models import ChannelDB, Msg
|
from evennia.comms.models import ChannelDB, Msg
|
||||||
|
|
@ -921,8 +923,9 @@ class CmdIRC2Chan(COMMAND_DEFAULT_CLASS):
|
||||||
self.msg("Account '%s' already exists and is not a bot." % botname)
|
self.msg("Account '%s' already exists and is not a bot." % botname)
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
|
password = hashlib.md5(str(time.time())).hexdigest()[:11]
|
||||||
try:
|
try:
|
||||||
bot = create.create_account(botname, None, None, typeclass=botclass)
|
bot = create.create_account(botname, None, password, typeclass=botclass)
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
self.msg("|rError, could not create the bot:|n '%s'." % err)
|
self.msg("|rError, could not create the bot:|n '%s'." % err)
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ from evennia.server.sessionhandler import SESSIONS
|
||||||
from evennia.scripts.models import ScriptDB
|
from evennia.scripts.models import ScriptDB
|
||||||
from evennia.objects.models import ObjectDB
|
from evennia.objects.models import ObjectDB
|
||||||
from evennia.accounts.models import AccountDB
|
from evennia.accounts.models import AccountDB
|
||||||
from evennia.utils import logger, utils, gametime, create
|
from evennia.utils import logger, utils, gametime, create, search
|
||||||
from evennia.utils.eveditor import EvEditor
|
from evennia.utils.eveditor import EvEditor
|
||||||
from evennia.utils.evtable import EvTable
|
from evennia.utils.evtable import EvTable
|
||||||
from evennia.utils.utils import crop, class_from_module
|
from evennia.utils.utils import crop, class_from_module
|
||||||
|
|
@ -460,17 +460,22 @@ class CmdObjects(COMMAND_DEFAULT_CLASS):
|
||||||
|
|
||||||
class CmdAccounts(COMMAND_DEFAULT_CLASS):
|
class CmdAccounts(COMMAND_DEFAULT_CLASS):
|
||||||
"""
|
"""
|
||||||
list all registered accounts
|
Manage registered accounts
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
@accounts [nr]
|
@accounts [nr]
|
||||||
|
@accounts/delete <name or #id> [: reason]
|
||||||
|
|
||||||
Lists statistics about the Accounts registered with the game.
|
Switches:
|
||||||
|
delete - delete an account from the server
|
||||||
|
|
||||||
|
By default, lists statistics about the Accounts registered with the game.
|
||||||
It will list the <nr> amount of latest registered accounts
|
It will list the <nr> amount of latest registered accounts
|
||||||
If not given, <nr> defaults to 10.
|
If not given, <nr> defaults to 10.
|
||||||
"""
|
"""
|
||||||
key = "@accounts"
|
key = "@accounts"
|
||||||
aliases = ["@listaccounts"]
|
aliases = ["@account", "@listaccounts"]
|
||||||
|
switch_options = ("delete", )
|
||||||
locks = "cmd:perm(listaccounts) or perm(Admin)"
|
locks = "cmd:perm(listaccounts) or perm(Admin)"
|
||||||
help_category = "System"
|
help_category = "System"
|
||||||
|
|
||||||
|
|
@ -478,6 +483,56 @@ class CmdAccounts(COMMAND_DEFAULT_CLASS):
|
||||||
"""List the accounts"""
|
"""List the accounts"""
|
||||||
|
|
||||||
caller = self.caller
|
caller = self.caller
|
||||||
|
args = self.args
|
||||||
|
|
||||||
|
if "delete" in self.switches:
|
||||||
|
account = getattr(caller, "account")
|
||||||
|
if not account or not account.check_permstring("Developer"):
|
||||||
|
caller.msg("You are not allowed to delete accounts.")
|
||||||
|
return
|
||||||
|
if not args:
|
||||||
|
caller.msg("Usage: @accounts/delete <name or #id> [: reason]")
|
||||||
|
return
|
||||||
|
reason = ""
|
||||||
|
if ":" in args:
|
||||||
|
args, reason = [arg.strip() for arg in args.split(":", 1)]
|
||||||
|
# We use account_search since we want to be sure to find also accounts
|
||||||
|
# that lack characters.
|
||||||
|
accounts = search.account_search(args)
|
||||||
|
if not accounts:
|
||||||
|
self.msg("Could not find an account by that name.")
|
||||||
|
return
|
||||||
|
if len(accounts) > 1:
|
||||||
|
string = "There were multiple matches:\n"
|
||||||
|
string += "\n".join(" %s %s" % (account.id, account.key) for account in accounts)
|
||||||
|
self.msg(string)
|
||||||
|
return
|
||||||
|
account = accounts.first()
|
||||||
|
if not account.access(caller, "delete"):
|
||||||
|
self.msg("You don't have the permissions to delete that account.")
|
||||||
|
return
|
||||||
|
username = account.username
|
||||||
|
# ask for confirmation
|
||||||
|
confirm = ("It is often better to block access to an account rather than to delete it. "
|
||||||
|
"|yAre you sure you want to permanently delete "
|
||||||
|
"account '|n{}|y'|n yes/[no]?".format(username))
|
||||||
|
answer = yield(confirm)
|
||||||
|
if answer.lower() not in ('y', 'yes'):
|
||||||
|
caller.msg("Canceled deletion.")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Boot the account then delete it.
|
||||||
|
self.msg("Informing and disconnecting account ...")
|
||||||
|
string = "\nYour account '%s' is being *permanently* deleted.\n" % username
|
||||||
|
if reason:
|
||||||
|
string += " Reason given:\n '%s'" % reason
|
||||||
|
account.msg(string)
|
||||||
|
logger.log_sec("Account Deleted: %s (Reason: %s, Caller: %s, IP: %s)." % (account, reason, caller, self.session.address))
|
||||||
|
account.delete()
|
||||||
|
self.msg("Account %s was successfully deleted." % username)
|
||||||
|
return
|
||||||
|
|
||||||
|
# No switches, default to displaying a list of accounts.
|
||||||
if self.args and self.args.isdigit():
|
if self.args and self.args.isdigit():
|
||||||
nlim = int(self.args)
|
nlim = int(self.args)
|
||||||
else:
|
else:
|
||||||
|
|
@ -655,6 +710,7 @@ class CmdTime(COMMAND_DEFAULT_CLASS):
|
||||||
"""Show server time data in a table."""
|
"""Show server time data in a table."""
|
||||||
table1 = EvTable("|wServer time", "", align="l", width=78)
|
table1 = EvTable("|wServer time", "", align="l", width=78)
|
||||||
table1.add_row("Current uptime", utils.time_format(gametime.uptime(), 3))
|
table1.add_row("Current uptime", utils.time_format(gametime.uptime(), 3))
|
||||||
|
table1.add_row("Portal uptime", utils.time_format(gametime.portal_uptime(), 3))
|
||||||
table1.add_row("Total runtime", utils.time_format(gametime.runtime(), 2))
|
table1.add_row("Total runtime", utils.time_format(gametime.runtime(), 2))
|
||||||
table1.add_row("First start", datetime.datetime.fromtimestamp(gametime.server_epoch()))
|
table1.add_row("First start", datetime.datetime.fromtimestamp(gametime.server_epoch()))
|
||||||
table1.add_row("Current time", datetime.datetime.now())
|
table1.add_row("Current time", datetime.datetime.now())
|
||||||
|
|
|
||||||
|
|
@ -243,6 +243,9 @@ class TestAdmin(CommandTest):
|
||||||
def test_ban(self):
|
def test_ban(self):
|
||||||
self.call(admin.CmdBan(), "Char", "Name-Ban char was added.")
|
self.call(admin.CmdBan(), "Char", "Name-Ban char was added.")
|
||||||
|
|
||||||
|
def test_force(self):
|
||||||
|
self.call(admin.CmdForce(), "Char2=say test", 'Char2(#7) says, "test"|You have forced Char2 to: say test')
|
||||||
|
|
||||||
|
|
||||||
class TestAccount(CommandTest):
|
class TestAccount(CommandTest):
|
||||||
|
|
||||||
|
|
@ -315,6 +318,24 @@ class TestBuilding(CommandTest):
|
||||||
def test_desc(self):
|
def test_desc(self):
|
||||||
self.call(building.CmdDesc(), "Obj2=TestDesc", "The description was set on Obj2(#5).")
|
self.call(building.CmdDesc(), "Obj2=TestDesc", "The description was set on Obj2(#5).")
|
||||||
|
|
||||||
|
def test_empty_desc(self):
|
||||||
|
"""
|
||||||
|
empty desc sets desc as ''
|
||||||
|
"""
|
||||||
|
o2d = self.obj2.db.desc
|
||||||
|
r1d = self.room1.db.desc
|
||||||
|
self.call(building.CmdDesc(), "Obj2=", "The description was set on Obj2(#5).")
|
||||||
|
assert self.obj2.db.desc == '' and self.obj2.db.desc != o2d
|
||||||
|
assert self.room1.db.desc == r1d
|
||||||
|
|
||||||
|
def test_desc_default_to_room(self):
|
||||||
|
"""no rhs changes room's desc"""
|
||||||
|
o2d = self.obj2.db.desc
|
||||||
|
r1d = self.room1.db.desc
|
||||||
|
self.call(building.CmdDesc(), "Obj2", "The description was set on Room(#1).")
|
||||||
|
assert self.obj2.db.desc == o2d
|
||||||
|
assert self.room1.db.desc == 'Obj2' and self.room1.db.desc != r1d
|
||||||
|
|
||||||
def test_wipe(self):
|
def test_wipe(self):
|
||||||
confirm = building.CmdDestroy.confirm
|
confirm = building.CmdDestroy.confirm
|
||||||
building.CmdDestroy.confirm = False
|
building.CmdDestroy.confirm = False
|
||||||
|
|
@ -334,6 +355,14 @@ class TestBuilding(CommandTest):
|
||||||
self.call(building.CmdOpen(), "TestExit1=Room2", "Created new Exit 'TestExit1' from Room to Room2")
|
self.call(building.CmdOpen(), "TestExit1=Room2", "Created new Exit 'TestExit1' from Room to Room2")
|
||||||
self.call(building.CmdLink(), "TestExit1=Room", "Link created TestExit1 -> Room (one way).")
|
self.call(building.CmdLink(), "TestExit1=Room", "Link created TestExit1 -> Room (one way).")
|
||||||
self.call(building.CmdUnLink(), "TestExit1", "Former exit TestExit1 no longer links anywhere.")
|
self.call(building.CmdUnLink(), "TestExit1", "Former exit TestExit1 no longer links anywhere.")
|
||||||
|
self.char1.location = self.room2
|
||||||
|
self.call(building.CmdOpen(), "TestExit2=Room", "Created new Exit 'TestExit2' from Room2 to Room.")
|
||||||
|
# ensure it matches locally first
|
||||||
|
self.call(building.CmdLink(), "TestExit=Room2", "Link created TestExit2 -> Room2 (one way).")
|
||||||
|
# ensure can still match globally when not a local name
|
||||||
|
self.call(building.CmdLink(), "TestExit1=Room2", "Note: TestExit1(#8) did not have a destination set before. "
|
||||||
|
"Make sure you linked the right thing.\n"
|
||||||
|
"Link created TestExit1 -> Room2 (one way).")
|
||||||
|
|
||||||
def test_set_home(self):
|
def test_set_home(self):
|
||||||
self.call(building.CmdSetHome(), "Obj = Room2", "Obj's home location was changed from Room")
|
self.call(building.CmdSetHome(), "Obj = Room2", "Obj's home location was changed from Room")
|
||||||
|
|
|
||||||
|
|
@ -475,14 +475,14 @@ class CmdShiftRoot(Command):
|
||||||
root_pos["blue"] -= 1
|
root_pos["blue"] -= 1
|
||||||
self.caller.msg("The root with blue flowers gets in the way and is pushed to the left.")
|
self.caller.msg("The root with blue flowers gets in the way and is pushed to the left.")
|
||||||
else:
|
else:
|
||||||
self.caller.msg("You cannot move the root in that direction.")
|
self.caller.msg("The root hangs straight down - you can only move it left or right.")
|
||||||
elif color == "blue":
|
elif color == "blue":
|
||||||
if direction == "left":
|
if direction == "left":
|
||||||
root_pos[color] = max(-1, root_pos[color] - 1)
|
root_pos[color] = max(-1, root_pos[color] - 1)
|
||||||
self.caller.msg("You shift the root with small blue flowers to the left.")
|
self.caller.msg("You shift the root with small blue flowers to the left.")
|
||||||
if root_pos[color] != 0 and root_pos[color] == root_pos["red"]:
|
if root_pos[color] != 0 and root_pos[color] == root_pos["red"]:
|
||||||
root_pos["red"] += 1
|
root_pos["red"] += 1
|
||||||
self.caller.msg("The reddish root is to big to fit as well, so that one falls away to the left.")
|
self.caller.msg("The reddish root is too big to fit as well, so that one falls away to the left.")
|
||||||
elif direction == "right":
|
elif direction == "right":
|
||||||
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 blue flowers to the right.")
|
self.caller.msg("You shove the root adorned with small blue flowers to the right.")
|
||||||
|
|
@ -490,7 +490,7 @@ class CmdShiftRoot(Command):
|
||||||
root_pos["red"] -= 1
|
root_pos["red"] -= 1
|
||||||
self.caller.msg("The thick reddish root gets in the way and is pushed back to the left.")
|
self.caller.msg("The thick reddish root gets in the way and is pushed back to the left.")
|
||||||
else:
|
else:
|
||||||
self.caller.msg("You cannot move the root in that direction.")
|
self.caller.msg("The root hangs straight down - you can only move it left or right.")
|
||||||
|
|
||||||
# now the horizontal roots (yellow/green). They can be moved up/down
|
# now the horizontal roots (yellow/green). They can be moved up/down
|
||||||
elif color == "yellow":
|
elif color == "yellow":
|
||||||
|
|
@ -507,7 +507,7 @@ class CmdShiftRoot(Command):
|
||||||
root_pos["green"] -= 1
|
root_pos["green"] -= 1
|
||||||
self.caller.msg("The weedy green root is shifted upwards to make room.")
|
self.caller.msg("The weedy green root is shifted upwards to make room.")
|
||||||
else:
|
else:
|
||||||
self.caller.msg("You cannot move the root in that direction.")
|
self.caller.msg("The root hangs across the wall - you can only move it up or down.")
|
||||||
elif color == "green":
|
elif color == "green":
|
||||||
if direction == "up":
|
if direction == "up":
|
||||||
root_pos[color] = max(-1, root_pos[color] - 1)
|
root_pos[color] = max(-1, root_pos[color] - 1)
|
||||||
|
|
@ -522,7 +522,7 @@ class CmdShiftRoot(Command):
|
||||||
root_pos["yellow"] -= 1
|
root_pos["yellow"] -= 1
|
||||||
self.caller.msg("The root with yellow flowers gets in the way and is pushed upwards.")
|
self.caller.msg("The root with yellow flowers gets in the way and is pushed upwards.")
|
||||||
else:
|
else:
|
||||||
self.caller.msg("You cannot move the root in that direction.")
|
self.caller.msg("The root hangs across the wall - you can only move it up or down.")
|
||||||
|
|
||||||
# we have moved the root. Store new position
|
# we have moved the root. Store new position
|
||||||
self.obj.db.root_pos = root_pos
|
self.obj.db.root_pos = root_pos
|
||||||
|
|
|
||||||
|
|
@ -747,9 +747,16 @@ class CmdLookDark(Command):
|
||||||
"""
|
"""
|
||||||
caller = self.caller
|
caller = self.caller
|
||||||
|
|
||||||
if random.random() < 0.75:
|
# count how many searches we've done
|
||||||
|
nr_searches = caller.ndb.dark_searches
|
||||||
|
if nr_searches is None:
|
||||||
|
nr_searches = 0
|
||||||
|
caller.ndb.dark_searches = nr_searches
|
||||||
|
|
||||||
|
if nr_searches < 4 and random.random() < 0.90:
|
||||||
# we don't find anything
|
# we don't find anything
|
||||||
caller.msg(random.choice(DARK_MESSAGES))
|
caller.msg(random.choice(DARK_MESSAGES))
|
||||||
|
caller.ndb.dark_searches += 1
|
||||||
else:
|
else:
|
||||||
# we could have found something!
|
# we could have found something!
|
||||||
if any(obj for obj in caller.contents if utils.inherits_from(obj, LightSource)):
|
if any(obj for obj in caller.contents if utils.inherits_from(obj, LightSource)):
|
||||||
|
|
@ -791,7 +798,8 @@ class CmdDarkNoMatch(Command):
|
||||||
|
|
||||||
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, maybe you'll find something helpful!")
|
||||||
|
|
||||||
|
|
||||||
class DarkCmdSet(CmdSet):
|
class DarkCmdSet(CmdSet):
|
||||||
|
|
@ -814,7 +822,9 @@ class DarkCmdSet(CmdSet):
|
||||||
self.add(CmdLookDark())
|
self.add(CmdLookDark())
|
||||||
self.add(CmdDarkHelp())
|
self.add(CmdDarkHelp())
|
||||||
self.add(CmdDarkNoMatch())
|
self.add(CmdDarkNoMatch())
|
||||||
self.add(default_cmds.CmdSay)
|
self.add(default_cmds.CmdSay())
|
||||||
|
self.add(default_cmds.CmdQuit())
|
||||||
|
self.add(default_cmds.CmdHome())
|
||||||
|
|
||||||
|
|
||||||
class DarkRoom(TutorialRoom):
|
class DarkRoom(TutorialRoom):
|
||||||
|
|
|
||||||
|
|
@ -980,8 +980,12 @@ class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)):
|
||||||
# no need to disconnect, Account just jumps to OOC mode.
|
# no need to disconnect, Account just jumps to OOC mode.
|
||||||
# sever the connection (important!)
|
# sever the connection (important!)
|
||||||
if self.account:
|
if self.account:
|
||||||
|
# Remove the object from playable characters list
|
||||||
|
if self in self.account.db._playable_characters:
|
||||||
|
self.account.db._playable_characters = [x for x in self.account.db._playable_characters if x != self]
|
||||||
for session in self.sessions.all():
|
for session in self.sessions.all():
|
||||||
self.account.unpuppet_object(session)
|
self.account.unpuppet_object(session)
|
||||||
|
|
||||||
self.account = None
|
self.account = None
|
||||||
|
|
||||||
for script in _ScriptDB.objects.get_all_scripts_on_obj(self):
|
for script in _ScriptDB.objects.get_all_scripts_on_obj(self):
|
||||||
|
|
|
||||||
|
|
@ -107,9 +107,10 @@ for mod in settings.PROTOTYPE_MODULES:
|
||||||
# internally we store as (key, desc, locks, tags, prototype_dict)
|
# internally we store as (key, desc, locks, tags, prototype_dict)
|
||||||
prots = []
|
prots = []
|
||||||
for variable_name, prot in all_from_module(mod).items():
|
for variable_name, prot in all_from_module(mod).items():
|
||||||
if "prototype_key" not in prot:
|
if isinstance(prot, dict):
|
||||||
prot['prototype_key'] = variable_name.lower()
|
if "prototype_key" not in prot:
|
||||||
prots.append((prot['prototype_key'], homogenize_prototype(prot)))
|
prot['prototype_key'] = variable_name.lower()
|
||||||
|
prots.append((prot['prototype_key'], homogenize_prototype(prot)))
|
||||||
# assign module path to each prototype_key for easy reference
|
# assign module path to each prototype_key for easy reference
|
||||||
_MODULE_PROTOTYPE_MODULES.update({prototype_key.lower(): mod for prototype_key, _ in prots})
|
_MODULE_PROTOTYPE_MODULES.update({prototype_key.lower(): mod for prototype_key, _ in prots})
|
||||||
# make sure the prototype contains all meta info
|
# make sure the prototype contains all meta info
|
||||||
|
|
|
||||||
|
|
@ -716,7 +716,7 @@ def spawn(*prototypes, **kwargs):
|
||||||
val = prot.pop("tags", [])
|
val = prot.pop("tags", [])
|
||||||
tags = []
|
tags = []
|
||||||
for (tag, category, data) in val:
|
for (tag, category, data) in val:
|
||||||
tags.append((init_spawn_value(val, str), category, data))
|
tags.append((init_spawn_value(tag, str), category, data))
|
||||||
|
|
||||||
prototype_key = prototype.get('prototype_key', None)
|
prototype_key = prototype.get('prototype_key', None)
|
||||||
if prototype_key:
|
if prototype_key:
|
||||||
|
|
|
||||||
|
|
@ -221,6 +221,7 @@ class AMPServerClientProtocol(amp.AMPMultiConnectionProtocol):
|
||||||
server_restart_mode = kwargs.get("server_restart_mode", "shutdown")
|
server_restart_mode = kwargs.get("server_restart_mode", "shutdown")
|
||||||
self.factory.server.run_init_hooks(server_restart_mode)
|
self.factory.server.run_init_hooks(server_restart_mode)
|
||||||
server_sessionhandler.portal_sessions_sync(kwargs.get("sessiondata"))
|
server_sessionhandler.portal_sessions_sync(kwargs.get("sessiondata"))
|
||||||
|
server_sessionhandler.portal_start_time = kwargs.get("portal_start_time")
|
||||||
|
|
||||||
elif operation == amp.SRELOAD: # server reload
|
elif operation == amp.SRELOAD: # server reload
|
||||||
# shut down in reload mode
|
# shut down in reload mode
|
||||||
|
|
|
||||||
|
|
@ -325,7 +325,7 @@ MENU = \
|
||||||
| 7) Kill Server only (send kill signal to process) |
|
| 7) Kill Server only (send kill signal to process) |
|
||||||
| 8) Kill Portal + Server |
|
| 8) Kill Portal + Server |
|
||||||
+--- Information -----------------------------------------------+
|
+--- Information -----------------------------------------------+
|
||||||
| 9) Tail log files (quickly see errors) |
|
| 9) Tail log files (quickly see errors - Ctrl-C to exit) |
|
||||||
| 10) Status |
|
| 10) Status |
|
||||||
| 11) Port info |
|
| 11) Port info |
|
||||||
+--- Testing ---------------------------------------------------+
|
+--- Testing ---------------------------------------------------+
|
||||||
|
|
|
||||||
|
|
@ -428,7 +428,8 @@ class AMPServerProtocol(amp.AMPMultiConnectionProtocol):
|
||||||
self.send_AdminPortal2Server(amp.DUMMYSESSION,
|
self.send_AdminPortal2Server(amp.DUMMYSESSION,
|
||||||
amp.PSYNC,
|
amp.PSYNC,
|
||||||
server_restart_mode=server_restart_mode,
|
server_restart_mode=server_restart_mode,
|
||||||
sessiondata=sessdata)
|
sessiondata=sessdata,
|
||||||
|
portal_start_time=self.factory.portal.start_time)
|
||||||
self.factory.portal.sessions.at_server_connection()
|
self.factory.portal.sessions.at_server_connection()
|
||||||
|
|
||||||
if self.factory.server_connection:
|
if self.factory.server_connection:
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ from builtins import object
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
|
import time
|
||||||
|
|
||||||
from os.path import dirname, abspath
|
from os.path import dirname, abspath
|
||||||
from twisted.application import internet, service
|
from twisted.application import internet, service
|
||||||
|
|
@ -114,6 +115,8 @@ class Portal(object):
|
||||||
self.server_restart_mode = "shutdown"
|
self.server_restart_mode = "shutdown"
|
||||||
self.server_info_dict = {}
|
self.server_info_dict = {}
|
||||||
|
|
||||||
|
self.start_time = time.time()
|
||||||
|
|
||||||
# in non-interactive portal mode, this gets overwritten by
|
# in non-interactive portal mode, this gets overwritten by
|
||||||
# cmdline sent by the evennia launcher
|
# cmdline sent by the evennia launcher
|
||||||
self.server_twistd_cmd = self._get_backup_server_twistd_cmd()
|
self.server_twistd_cmd = self._get_backup_server_twistd_cmd()
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,7 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
|
||||||
|
|
||||||
from evennia.utils.utils import delay
|
from evennia.utils.utils import delay
|
||||||
# timeout the handshakes in case the client doesn't reply at all
|
# timeout the handshakes in case the client doesn't reply at all
|
||||||
delay(2, callback=self.handshake_done, timeout=True)
|
self._handshake_delay = delay(2, callback=self.handshake_done, timeout=True)
|
||||||
|
|
||||||
# TCP/IP keepalive watches for dead links
|
# TCP/IP keepalive watches for dead links
|
||||||
self.transport.setTcpKeepAlive(1)
|
self.transport.setTcpKeepAlive(1)
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,24 @@ try:
|
||||||
except ImportError:
|
except ImportError:
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
from mock import Mock
|
||||||
import string
|
import string
|
||||||
from evennia.server.portal import irc
|
from evennia.server.portal import irc
|
||||||
|
|
||||||
|
from twisted.conch.telnet import IAC, WILL, DONT, SB, SE, NAWS, DO
|
||||||
|
from twisted.test import proto_helpers
|
||||||
|
from twisted.trial.unittest import TestCase as TwistedTestCase
|
||||||
|
|
||||||
|
from .telnet import TelnetServerFactory, TelnetProtocol
|
||||||
|
from .portal import PORTAL_SESSIONS
|
||||||
|
from .suppress_ga import SUPPRESS_GA
|
||||||
|
from .naws import DEFAULT_HEIGHT, DEFAULT_WIDTH
|
||||||
|
from .ttype import TTYPE, IS
|
||||||
|
from .mccp import MCCP
|
||||||
|
from .mssp import MSSP
|
||||||
|
from .mxp import MXP
|
||||||
|
from .telnet_oob import MSDP, MSDP_VAL, MSDP_VAR
|
||||||
|
|
||||||
|
|
||||||
class TestIRC(TestCase):
|
class TestIRC(TestCase):
|
||||||
|
|
||||||
|
|
@ -73,3 +88,64 @@ class TestIRC(TestCase):
|
||||||
s = r'|wthis|Xis|gis|Ma|C|complex|*string'
|
s = r'|wthis|Xis|gis|Ma|C|complex|*string'
|
||||||
|
|
||||||
self.assertEqual(irc.parse_irc_to_ansi(irc.parse_ansi_to_irc(s)), s)
|
self.assertEqual(irc.parse_irc_to_ansi(irc.parse_ansi_to_irc(s)), s)
|
||||||
|
|
||||||
|
|
||||||
|
class TestTelnet(TwistedTestCase):
|
||||||
|
def setUp(self):
|
||||||
|
super(TestTelnet, self).setUp()
|
||||||
|
factory = TelnetServerFactory()
|
||||||
|
factory.protocol = TelnetProtocol
|
||||||
|
factory.sessionhandler = PORTAL_SESSIONS
|
||||||
|
factory.sessionhandler.portal = Mock()
|
||||||
|
self.proto = factory.buildProtocol(("localhost", 0))
|
||||||
|
self.transport = proto_helpers.StringTransport()
|
||||||
|
self.addCleanup(factory.sessionhandler.disconnect_all)
|
||||||
|
|
||||||
|
def test_mudlet_ttype(self):
|
||||||
|
self.transport.client = ["localhost"]
|
||||||
|
self.transport.setTcpKeepAlive = Mock()
|
||||||
|
d = self.proto.makeConnection(self.transport)
|
||||||
|
# test suppress_ga
|
||||||
|
self.assertTrue(self.proto.protocol_flags["NOGOAHEAD"])
|
||||||
|
self.proto.dataReceived(IAC + DONT + SUPPRESS_GA)
|
||||||
|
self.assertFalse(self.proto.protocol_flags["NOGOAHEAD"])
|
||||||
|
self.assertEqual(self.proto.handshakes, 7)
|
||||||
|
# test naws
|
||||||
|
self.assertEqual(self.proto.protocol_flags['SCREENWIDTH'], {0: DEFAULT_WIDTH})
|
||||||
|
self.assertEqual(self.proto.protocol_flags['SCREENHEIGHT'], {0: DEFAULT_HEIGHT})
|
||||||
|
self.proto.dataReceived(IAC + WILL + NAWS)
|
||||||
|
self.proto.dataReceived([IAC, SB, NAWS, '', 'x', '', 'd', IAC, SE])
|
||||||
|
self.assertEqual(self.proto.protocol_flags['SCREENWIDTH'][0], 120)
|
||||||
|
self.assertEqual(self.proto.protocol_flags['SCREENHEIGHT'][0], 100)
|
||||||
|
self.assertEqual(self.proto.handshakes, 6)
|
||||||
|
# test ttype
|
||||||
|
self.assertTrue(self.proto.protocol_flags["FORCEDENDLINE"])
|
||||||
|
self.assertFalse(self.proto.protocol_flags["TTYPE"])
|
||||||
|
self.assertTrue(self.proto.protocol_flags["ANSI"])
|
||||||
|
self.proto.dataReceived(IAC + WILL + TTYPE)
|
||||||
|
self.proto.dataReceived([IAC, SB, TTYPE, IS, "MUDLET", IAC, SE])
|
||||||
|
self.assertTrue(self.proto.protocol_flags["XTERM256"])
|
||||||
|
self.assertEqual(self.proto.protocol_flags["CLIENTNAME"], "MUDLET")
|
||||||
|
self.proto.dataReceived([IAC, SB, TTYPE, IS, "XTERM", IAC, SE])
|
||||||
|
self.proto.dataReceived([IAC, SB, TTYPE, IS, "MTTS 137", IAC, SE])
|
||||||
|
self.assertEqual(self.proto.handshakes, 5)
|
||||||
|
# test mccp
|
||||||
|
self.proto.dataReceived(IAC + DONT + MCCP)
|
||||||
|
self.assertFalse(self.proto.protocol_flags['MCCP'])
|
||||||
|
self.assertEqual(self.proto.handshakes, 4)
|
||||||
|
# test mssp
|
||||||
|
self.proto.dataReceived(IAC + DONT + MSSP)
|
||||||
|
self.assertEqual(self.proto.handshakes, 3)
|
||||||
|
# test oob
|
||||||
|
self.proto.dataReceived(IAC + DO + MSDP)
|
||||||
|
self.proto.dataReceived([IAC, SB, MSDP, MSDP_VAR, "LIST", MSDP_VAL, "COMMANDS", IAC, SE])
|
||||||
|
self.assertTrue(self.proto.protocol_flags['OOB'])
|
||||||
|
self.assertEqual(self.proto.handshakes, 2)
|
||||||
|
# test mxp
|
||||||
|
self.proto.dataReceived(IAC + DONT + MXP)
|
||||||
|
self.assertFalse(self.proto.protocol_flags['MXP'])
|
||||||
|
self.assertEqual(self.proto.handshakes, 1)
|
||||||
|
# clean up to prevent Unclean reactor
|
||||||
|
self.proto.nop_keep_alive.stop()
|
||||||
|
self.proto._handshake_delay.cancel()
|
||||||
|
return d
|
||||||
|
|
|
||||||
|
|
@ -13,14 +13,14 @@ import time
|
||||||
# TODO!
|
# TODO!
|
||||||
#sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))))
|
#sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))))
|
||||||
#os.environ['DJANGO_SETTINGS_MODULE'] = 'game.settings'
|
#os.environ['DJANGO_SETTINGS_MODULE'] = 'game.settings'
|
||||||
import ev
|
import evennia
|
||||||
from evennia.utils.idmapper import base as _idmapper
|
from evennia.utils.idmapper import models as _idmapper
|
||||||
|
|
||||||
LOGFILE = "logs/memoryusage.log"
|
LOGFILE = "logs/memoryusage.log"
|
||||||
INTERVAL = 30 # log every 30 seconds
|
INTERVAL = 30 # log every 30 seconds
|
||||||
|
|
||||||
|
|
||||||
class Memplot(ev.Script):
|
class Memplot(evennia.DefaultScript):
|
||||||
"""
|
"""
|
||||||
Describes a memory plotting action.
|
Describes a memory plotting action.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from mock import Mock
|
from mock import Mock, patch, mock_open
|
||||||
from .dummyrunner_settings import (c_creates_button, c_creates_obj, c_digs, c_examines, c_help, c_idles, c_login,
|
from .dummyrunner_settings import (c_creates_button, c_creates_obj, c_digs, c_examines, c_help, c_idles, c_login,
|
||||||
c_login_nodig, c_logout, c_looks, c_moves, c_moves_n, c_moves_s, c_socialize)
|
c_login_nodig, c_logout, c_looks, c_moves, c_moves_n, c_moves_s, c_socialize)
|
||||||
|
import memplot
|
||||||
|
|
||||||
|
|
||||||
class TestDummyrunnerSettings(TestCase):
|
class TestDummyrunnerSettings(TestCase):
|
||||||
|
|
@ -91,3 +92,21 @@ class TestDummyrunnerSettings(TestCase):
|
||||||
|
|
||||||
def test_c_move_s(self):
|
def test_c_move_s(self):
|
||||||
self.assertEqual(c_moves_s(self.client), "south")
|
self.assertEqual(c_moves_s(self.client), "south")
|
||||||
|
|
||||||
|
|
||||||
|
class TestMemPlot(TestCase):
|
||||||
|
@patch.object(memplot, "_idmapper")
|
||||||
|
@patch.object(memplot, "os")
|
||||||
|
@patch.object(memplot, "open", new_callable=mock_open, create=True)
|
||||||
|
@patch.object(memplot, "time")
|
||||||
|
def test_memplot(self, mock_time, mocked_open, mocked_os, mocked_idmapper):
|
||||||
|
from evennia.utils.create import create_script
|
||||||
|
mocked_idmapper.cache_size.return_value = (9, 5000)
|
||||||
|
mock_time.time = Mock(return_value=6000.0)
|
||||||
|
script = create_script(memplot.Memplot)
|
||||||
|
script.db.starttime = 0.0
|
||||||
|
mocked_os.popen.read.return_value = 5000.0
|
||||||
|
script.at_repeat()
|
||||||
|
handle = mocked_open()
|
||||||
|
handle.write.assert_called_with('100.0, 0.001, 0.001, 9\n')
|
||||||
|
script.stop()
|
||||||
|
|
|
||||||
|
|
@ -407,7 +407,7 @@ class ServerSession(Session):
|
||||||
else:
|
else:
|
||||||
self.data_out(**kwargs)
|
self.data_out(**kwargs)
|
||||||
|
|
||||||
def execute_cmd(self, raw_string, **kwargs):
|
def execute_cmd(self, raw_string, session=None, **kwargs):
|
||||||
"""
|
"""
|
||||||
Do something as this object. This method is normally never
|
Do something as this object. This method is normally never
|
||||||
called directly, instead incoming command instructions are
|
called directly, instead incoming command instructions are
|
||||||
|
|
@ -417,6 +417,9 @@ class ServerSession(Session):
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
raw_string (string): Raw command input
|
raw_string (string): Raw command input
|
||||||
|
session (Session): This is here to make API consistent with
|
||||||
|
Account/Object.execute_cmd. If given, data is passed to
|
||||||
|
that Session, otherwise use self.
|
||||||
Kwargs:
|
Kwargs:
|
||||||
Other keyword arguments will be added to the found command
|
Other keyword arguments will be added to the found command
|
||||||
object instace as variables before it executes. This is
|
object instace as variables before it executes. This is
|
||||||
|
|
@ -426,7 +429,7 @@ class ServerSession(Session):
|
||||||
"""
|
"""
|
||||||
# inject instruction into input stream
|
# inject instruction into input stream
|
||||||
kwargs["text"] = ((raw_string,), {})
|
kwargs["text"] = ((raw_string,), {})
|
||||||
self.sessionhandler.data_in(self, **kwargs)
|
self.sessionhandler.data_in(session or self, **kwargs)
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
"""Handle session comparisons"""
|
"""Handle session comparisons"""
|
||||||
|
|
|
||||||
|
|
@ -280,6 +280,8 @@ class ServerSessionHandler(SessionHandler):
|
||||||
super(ServerSessionHandler, self).__init__(*args, **kwargs)
|
super(ServerSessionHandler, self).__init__(*args, **kwargs)
|
||||||
self.server = None # set at server initialization
|
self.server = None # set at server initialization
|
||||||
self.server_data = {"servername": _SERVERNAME}
|
self.server_data = {"servername": _SERVERNAME}
|
||||||
|
# will be set on psync
|
||||||
|
self.portal_start_time = 0.0
|
||||||
|
|
||||||
def _run_cmd_login(self, session):
|
def _run_cmd_login(self, session):
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -107,6 +107,17 @@ def uptime():
|
||||||
return time.time() - SERVER_START_TIME
|
return time.time() - SERVER_START_TIME
|
||||||
|
|
||||||
|
|
||||||
|
def portal_uptime():
|
||||||
|
"""
|
||||||
|
Get the current uptime of the portal.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
time (float): The uptime of the portal.
|
||||||
|
"""
|
||||||
|
from evennia.server.sessionhandler import SESSIONS
|
||||||
|
return time.time() - SESSIONS.portal_start_time
|
||||||
|
|
||||||
|
|
||||||
def game_epoch():
|
def game_epoch():
|
||||||
"""
|
"""
|
||||||
Get the game epoch.
|
Get the game epoch.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue