Add evennia connectsions wizard functionality
This commit is contained in:
parent
c9893c6e65
commit
1172067afc
7 changed files with 126 additions and 81 deletions
|
|
@ -35,6 +35,13 @@
|
|||
|
||||
- Change webclient from old txws version to use more supported/feature-rich Autobahn websocket library
|
||||
|
||||
#### Evennia game index
|
||||
|
||||
- Made Evennia game index client a part of core - now configured from settings file (old configs
|
||||
need to be moved)
|
||||
- The `evennia connections` command starts a wizard that helps you connect your game to the game index.
|
||||
- The game index now accepts games with no public telnet/webclient info (for early prototypes).
|
||||
|
||||
#### New golden-layout based Webclient UI (@friarzen)
|
||||
- Features
|
||||
- Much slicker behavior and more professional look
|
||||
|
|
|
|||
|
|
@ -3,9 +3,10 @@ Link Evennia to external resources (wizard plugin for evennia_launcher)
|
|||
|
||||
"""
|
||||
import sys
|
||||
from os import path
|
||||
import pprint
|
||||
from django.conf import settings
|
||||
from evennia.utils.utils import list_to_string
|
||||
from evennia.utils.utils import list_to_string, mod_import
|
||||
|
||||
class ConnectionWizard(object):
|
||||
|
||||
|
|
@ -33,7 +34,7 @@ class ConnectionWizard(object):
|
|||
"""
|
||||
|
||||
opt_txt = "\n".join(f" {key}: {desc}" for key, (desc, _, _) in options.items())
|
||||
self.display(opt_txt)
|
||||
self.display(opt_txt + "\n")
|
||||
|
||||
while True:
|
||||
resp = input(prompt).strip()
|
||||
|
|
@ -78,7 +79,7 @@ class ConnectionWizard(object):
|
|||
elif resp.lower() in ("quit", "q"):
|
||||
sys.exit()
|
||||
|
||||
def ask_choice(self, prompt="> ", options=None, default=None):
|
||||
def ask_choice(self, prompt=" > ", options=None, default=None):
|
||||
"""
|
||||
Ask multiple-choice question, get response inline.
|
||||
|
||||
|
|
@ -91,7 +92,7 @@ class ConnectionWizard(object):
|
|||
|
||||
"""
|
||||
opt_txt = "\n".join(f" {ind + 1}: {desc}" for ind, desc in enumerate(options))
|
||||
self.display(opt_txt)
|
||||
self.display(opt_txt + "\n")
|
||||
|
||||
while True:
|
||||
resp = input(prompt).strip()
|
||||
|
|
@ -109,13 +110,16 @@ class ConnectionWizard(object):
|
|||
return selection
|
||||
self.display(" Select one of the given options.")
|
||||
|
||||
def ask_input(self, prompt="> ", default=None, verify=True, max_len=None):
|
||||
def ask_input(self, prompt=" > ", default=None, validator=None):
|
||||
"""
|
||||
Get arbitrary input inline.
|
||||
|
||||
Kwargs:
|
||||
prompt (str): The display prompt.
|
||||
default (str, optional): If empty input, use this.
|
||||
default (str): If empty input, use this.
|
||||
validator (callable): If given, the input will be passed
|
||||
into this callable. It should return True unless validation
|
||||
fails (and is expected to echo why if so).
|
||||
|
||||
Returns:
|
||||
inp (str): The input given, or default.
|
||||
|
|
@ -123,11 +127,19 @@ class ConnectionWizard(object):
|
|||
"""
|
||||
while True:
|
||||
resp = input(prompt).strip()
|
||||
|
||||
if not resp and default:
|
||||
resp = str(default)
|
||||
|
||||
if resp.lower() in ('q', 'quit'):
|
||||
sys.exit()
|
||||
|
||||
if resp.lower() == 'none':
|
||||
resp = ''
|
||||
|
||||
if validator and not validator(resp):
|
||||
continue
|
||||
|
||||
ok = input("\n Leave blank? [Y]/N: ")
|
||||
if ok.lower() in ('n', 'no'):
|
||||
continue
|
||||
|
|
@ -135,18 +147,15 @@ class ConnectionWizard(object):
|
|||
sys.exit()
|
||||
return resp
|
||||
|
||||
if verify:
|
||||
self.display(resp)
|
||||
if max_len:
|
||||
nlen = len(resp)
|
||||
if nlen > max_len:
|
||||
self.display(f" This text is {nlen} characters long. Max is {max_len}.")
|
||||
continue
|
||||
ok = input("\n Is the above looking correct? [Y]/N: ")
|
||||
if ok.lower() in ("n", "no"):
|
||||
continue
|
||||
elif ok.lower() in ('q', 'quit'):
|
||||
sys.exit()
|
||||
if validator and not validator(resp):
|
||||
continue
|
||||
|
||||
self.display(resp)
|
||||
ok = input("\n Is this correct? [Y]/N: ")
|
||||
if ok.lower() in ("n", "no"):
|
||||
continue
|
||||
elif ok.lower() in ('q', 'quit'):
|
||||
sys.exit()
|
||||
return resp
|
||||
|
||||
|
||||
|
|
@ -155,10 +164,12 @@ def node_start(wizard):
|
|||
This wizard helps activate external networks with Evennia. It will create
|
||||
a config that will be attached to the bottom of the game settings file.
|
||||
|
||||
Use `quit` at any time to abort and throw away any changes.
|
||||
Make sure you have at least started the game once before continuing!
|
||||
|
||||
Use `quit` at any time to abort and throw away unsaved changes.
|
||||
"""
|
||||
options = {
|
||||
"1": ("Add game to Evennia game index (also for closed dev games)",
|
||||
"1": ("Add game to Evennia game index (also for closed-dev games)",
|
||||
node_game_index_start, {}),
|
||||
"2": ("Add MSSP information (for mud-list crawlers)",
|
||||
node_mssp_start, {}),
|
||||
|
|
@ -231,9 +242,18 @@ def node_game_index_fields(wizard, status=None):
|
|||
{sdesc_default}
|
||||
"""
|
||||
|
||||
def sdesc_validator(inp):
|
||||
tmax = 255
|
||||
tlen = len(inp)
|
||||
if tlen > 255:
|
||||
print(f"The short desc must be shorter than {tmax} characters (was {tlen}).")
|
||||
wizard.ask_continue()
|
||||
return False
|
||||
return True
|
||||
|
||||
wizard.display(text)
|
||||
wizard.game_index_listing['short_description'] = \
|
||||
wizard.ask_input(default=sdesc_default, max_len=255)
|
||||
wizard.ask_input(default=sdesc_default, validator=sdesc_validator)
|
||||
|
||||
# long desc
|
||||
|
||||
|
|
@ -265,9 +285,16 @@ def node_game_index_fields(wizard, status=None):
|
|||
{listing_default}
|
||||
"""
|
||||
|
||||
def contact_validator(inp):
|
||||
if not inp or "@" not in inp:
|
||||
print("This should be an email and cannot be blank.")
|
||||
wizard.ask_continue()
|
||||
return False
|
||||
return True
|
||||
|
||||
wizard.display(text)
|
||||
wizard.game_index_listing['listing_contact'] = \
|
||||
wizard.ask_input(default=listing_default)
|
||||
wizard.ask_input(default=listing_default, validator=contact_validator)
|
||||
|
||||
# telnet hostname
|
||||
|
||||
|
|
@ -359,17 +386,29 @@ def node_game_index_fields(wizard, status=None):
|
|||
|
||||
def node_mssp_start(wizard):
|
||||
|
||||
mssp_module = mod_import(settings.MSSP_META_MODULE)
|
||||
filename = mssp_module.__file__
|
||||
|
||||
text = f"""
|
||||
MSSP (Mud Server Status Protocol) allows online MUD-listing sites/crawlers
|
||||
to continuously monitor your game and list information about it. Some of
|
||||
this, like active player-count, Evennia will automatically add for you,
|
||||
whereas many fields is info about your game.
|
||||
whereas many fields are manually added info about your game.
|
||||
|
||||
To use MSSP you should generally have a publicly open game that external
|
||||
players can connect to.
|
||||
players can connect to. You also need to register at a MUD listing site to
|
||||
tell them to list your game.
|
||||
|
||||
MSSP has a large number of configuration options and we found it was simply
|
||||
a lot easier to set them in a file rather than using this wizard. So to
|
||||
configure MSSP, edit the empty template listing found here:
|
||||
|
||||
'{filename}'
|
||||
"""
|
||||
|
||||
wizard.mssp_table
|
||||
wizard.display(text)
|
||||
wizard.ask_continue()
|
||||
node_start(wizard)
|
||||
|
||||
|
||||
# Admin
|
||||
|
|
@ -378,7 +417,30 @@ def _save_changes(wizard):
|
|||
"""
|
||||
Perform the save
|
||||
"""
|
||||
print("saving!")
|
||||
|
||||
# add import statement to settings file
|
||||
import_stanza = "from .connection_settings import *"
|
||||
setting_module = mod_import("server.conf.settings")
|
||||
with open(setting_module.__file__, 'r+') as f:
|
||||
txt = f.read() # moves pointer to end of file
|
||||
if import_stanza not in txt:
|
||||
# add to the end of the file
|
||||
f.write("\n\n"
|
||||
"try:\n"
|
||||
" # Created by the `evennia connections` wizard\n"
|
||||
f" {import_stanza}\n"
|
||||
"except ImportError:\n"
|
||||
" pass")
|
||||
|
||||
connect_settings_file = path.join(settings.GAME_DIR,
|
||||
"server", "conf", "connection_settings.py")
|
||||
with open(connect_settings_file, 'w') as f:
|
||||
f.write("# This file is auto-generated by the `evennia connections` wizard.\n"
|
||||
"# Don't edit manually, your changes will be overwritten.\n\n")
|
||||
|
||||
f.write(wizard.save_output)
|
||||
wizard.display(f"saving to {connect_settings_file} ...")
|
||||
|
||||
|
||||
def node_view_and_apply_settings(wizard):
|
||||
"""
|
||||
|
|
@ -393,18 +455,22 @@ def node_view_and_apply_settings(wizard):
|
|||
if wizard.game_index_listing != settings.GAME_INDEX_LISTING:
|
||||
game_index_txt = "No changes to save for Game Index."
|
||||
else:
|
||||
game_index_txt = pp.pformat(wizard.game_index_listing)
|
||||
game_index_txt = ("GAME_INDEX_ENABLED = True\n"
|
||||
"GAME_INDEX_LISTING = \\\n" +
|
||||
pp.pformat(wizard.game_index_listing))
|
||||
saves = True
|
||||
|
||||
text = game_index_txt
|
||||
|
||||
print("- Game index:\n" + text)
|
||||
wizard.display(f"Settings to save:\n\n{text}")
|
||||
|
||||
if saves:
|
||||
if wizard.ask_yesno("Do you want to save these settings?") == 'yes':
|
||||
wizard.save_output = text
|
||||
_save_changes(wizard)
|
||||
wizard.display("... saved!")
|
||||
else:
|
||||
print("Cancelled. Returning ...")
|
||||
wizard.display("... cancelled.")
|
||||
wizard.ask_continue()
|
||||
node_start(wizard)
|
||||
|
||||
|
|
|
|||
|
|
@ -50,6 +50,8 @@ def check_errors(settings):
|
|||
if hasattr(settings, "ACCOUNT_TYPECLASS_PATHS"):
|
||||
raise DeprecationWarning(deprstring % "ACCOUNT_TYPECLASS_PATHS")
|
||||
if hasattr(settings, "CHANNEL_TYPECLASS_PATHS"):
|
||||
raise DeprecationWarning(deprstring % "CHANNEL_TYPECLASS_PATHS")
|
||||
if hasattr(settings, "SEARCH_MULTIMATCH_SEPARATOR"):
|
||||
raise DeprecationWarning(
|
||||
"settings.SEARCH_MULTIMATCH_SEPARATOR was replaced by "
|
||||
"SEARCH_MULTIMATCH_REGEX and SEARCH_MULTIMATCH_TEMPLATE. "
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@ class EvenniaGameIndexClient(object):
|
|||
'Failed to send game details to Evennia Game Index. HTTP '
|
||||
'status code was %s. Message was: %s' % (status_code, response_body)
|
||||
)
|
||||
|
||||
if status_code == 400 and self._on_bad_request:
|
||||
# Improperly formed request. Defer to the callback as far as what
|
||||
# to do. Probably not a great idea to continue attempting to send
|
||||
|
|
|
|||
|
|
@ -18,6 +18,9 @@ MSSP = b'\x46'
|
|||
MSSP_VAR = b'\x01'
|
||||
MSSP_VAL = b'\x02'
|
||||
|
||||
# try to get the customized mssp info, if it exists.
|
||||
MSSPTable_CUSTOM = utils.variable_from_module(settings.MSSP_META_MODULE, "MSSPTable", default={})
|
||||
|
||||
|
||||
class Mssp(object):
|
||||
"""
|
||||
|
|
@ -109,7 +112,8 @@ class Mssp(object):
|
|||
}
|
||||
|
||||
# update the static table with the custom one
|
||||
self.mssp_table.update(settings.MSSP_TABLE)
|
||||
if MSSPTable_CUSTOM:
|
||||
self.mssp_table.update(MSSPTable_CUSTOM)
|
||||
|
||||
varlist = ''
|
||||
for variable, value in self.mssp_table.items():
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ class TestDeprecations(TestCase):
|
|||
"CHARACTER_DEFAULT_HOME", "OBJECT_TYPECLASS_PATHS", "SCRIPT_TYPECLASS_PATHS",
|
||||
"ACCOUNT_TYPECLASS_PATHS", "CHANNEL_TYPECLASS_PATHS", "SEARCH_MULTIMATCH_SEPARATOR",
|
||||
"TIME_SEC_PER_MIN", "TIME_MIN_PER_HOUR", "TIME_HOUR_PER_DAY", "TIME_DAY_PER_WEEK",
|
||||
"TIME_WEEK_PER_MONTH", "TIME_MONTH_PER_YEAR")
|
||||
"TIME_WEEK_PER_MONTH", "TIME_MONTH_PER_YEAR", "GAME_DIRECTORY_LISTING")
|
||||
|
||||
def test_check_errors(self):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -25,6 +25,9 @@ import sys
|
|||
|
||||
# This is the name of your game. Make it catchy!
|
||||
SERVERNAME = "Evennia"
|
||||
# Short one-sentence blurb describing your game. Shown under the title
|
||||
# on the website and could be used in online listings of your game etc.
|
||||
GAME_SLOGAN = "Python MU* creation system"
|
||||
# Lockdown mode will cut off the game from any external connections
|
||||
# and only allow connections from localhost. Requires a cold reboot.
|
||||
LOCKDOWN_MODE = False
|
||||
|
|
@ -343,6 +346,9 @@ SERVER_SERVICES_PLUGIN_MODULES = ["server.conf.server_services_plugins"]
|
|||
# main Evennia Portal application when the Portal is initiated.
|
||||
# It will be called last in the startup sequence.
|
||||
PORTAL_SERVICES_PLUGIN_MODULES = ["server.conf.portal_services_plugins"]
|
||||
# Module holding MSSP meta data. This is used by MUD-crawlers to determine
|
||||
# what type of game you are running, how many accounts you have etc.
|
||||
MSSP_META_MODULE = "server.conf.mssp"
|
||||
# Module for web plugins.
|
||||
WEB_PLUGINS_MODULE = "server.conf.web_plugins"
|
||||
# Tuple of modules implementing lock functions. All callable functions
|
||||
|
|
@ -683,13 +689,19 @@ CHANNEL_CONNECTINFO = None
|
|||
# External Connections
|
||||
######################################################################
|
||||
|
||||
# Note: You do *not* have to make your MUD open to
|
||||
# the public to use the external connections, they
|
||||
# operate as long as you have an internet connection,
|
||||
# just like stand-alone chat clients.
|
||||
|
||||
# The Evennia Game Index is a dynamic listing of Evennia games. You can add your game
|
||||
# to this list also if it is in closed pre-alpha development.
|
||||
GAME_INDEX_ENABLED = False
|
||||
# This dict
|
||||
GAME_INDEX_LISTING = {
|
||||
'game_status': 'closed-dev', # closed-dev, pre-alpha, pre-alpha, alpha, beta or launched
|
||||
'short_description': '',
|
||||
'game_name': SERVERNAME,
|
||||
'game_status': 'pre-alpha', # pre-alpha, alpha, beta or launched
|
||||
'short_description': GAME_SLOGAN,
|
||||
'long_description': '',
|
||||
'listing_contact': '', # email
|
||||
'telnet_hostname': '', # mygame.com
|
||||
|
|
@ -697,57 +709,11 @@ GAME_INDEX_LISTING = {
|
|||
'game_website': '', # http://mygame.com
|
||||
'web_client_url': '' # http://mygame.com/webclient
|
||||
}
|
||||
|
||||
# MSSP (Mud Server Status Protocol) is used by MUD-crawlers to determine
|
||||
# what type of game you are running, how many players you have etc. Some of
|
||||
# this (like server name and current number of players) is handled by Evennia
|
||||
# automatically, other fields are set by you.
|
||||
MSSP_TABLE = {
|
||||
"HOSTNAME": "", "PORT": "", # telnet host/port
|
||||
"CONTACT": "", "CREATED": "", # email, year of game creation
|
||||
"IP": "", "ICON": "", # ip address; url to icon 32x32or larger; <32kb.
|
||||
"LANGUAGE": "English", "LOCATION": "", # server country location, like "Sweden"
|
||||
"MINIMUM AGE": "0", # set to 0 if not applicable
|
||||
"WEBSITE": "www.evennia.com",
|
||||
"GENRE": "None", # Adult, Fantasy, Historical, Horror, Modern, None, or Science Fiction
|
||||
"GAMEPLAY": "None", # Adventure, Educational, Hack and Slash, None,
|
||||
# Player versus Player, Player versus Environment,
|
||||
# Roleplaying, Simulation, Social or Strategy
|
||||
"STATUS": "Alpha", # Alpha, Closed Beta, Open Beta, Live
|
||||
"GAMESYSTEM": "Custom", # D&D, d20 System, World of Darkness, etc. Use Custom if homebrew
|
||||
"SUBGENRE": "None", # Freeform, like LASG, Medieval Fantasy, World War II, Frankenstein,
|
||||
# Cyberpunk, Dragonlance, etc. Or None if not available.
|
||||
# use 0 if not applicable or off
|
||||
"AREAS": "0", "HELPFILES": "0", "MOBILES": "0", "OBJECTS": "0",
|
||||
"ROOMS": "0", "CLASSES": "0", "LEVELS": "0", "RACES": "0", "SKILLS": "0",
|
||||
|
||||
"PAY TO PLAY": "0", "PAY FOR PERKS": "0",
|
||||
"HIRING BUILDERS": "0", "HIRING CODERS": "0",
|
||||
|
||||
"DBSIZE": "0", "EXITS": "0", "EXTRA DESCRIPTIONS": "0",
|
||||
"MUDPROGS": "0", "MUDTRIGS": "0", "RESETS": "0",
|
||||
|
||||
"ADULT MATERIAL": "0", "MULTICLASSING": "0", "NEWBIE FRIENDLY": "0", "PLAYER CITIES": "0",
|
||||
"PLAYER CLANS": "0", "PLAYER CRAFTING": "0", "PLAYER GUILDS": "0",
|
||||
"EQUIPMENT SYSTEM": "None", # "None", "Level", "Skill", "Both"
|
||||
"MULTIPLAYING": "None", # "None", "Restricted", "Full"
|
||||
"PLAYERKILLING": "None", # "None", "Restricted", "Full"
|
||||
"QUEST SYSTEM": "None", # "None", "Immortal Run", "Automated", "Integrated"
|
||||
"ROLEPLAYING": "None", # "None", "Accepted", "Encouraged", "Enforced"
|
||||
"TRAINING SYSTEM": "None", # "None", "Level", "Skill", "Both"
|
||||
"WORLD ORIGINALITY": "None", # "All Stock", "Mostly Stock", "Mostly Original", "All Original"
|
||||
}
|
||||
|
||||
# Note: You do *not* have to make your MUD open to
|
||||
# the public to use the external connections, they
|
||||
# operate as long as you have an internet connection,
|
||||
# just like stand-alone chat clients. IRC requires
|
||||
# that you have twisted.words installed.
|
||||
|
||||
# Evennia can connect to external IRC channels and
|
||||
# echo what is said on the channel to IRC and vice
|
||||
# versa. Obs - make sure the IRC network allows bots.
|
||||
# When enabled, command @irc2chan will be available in-game
|
||||
# IRC requires that you have twisted.words installed.
|
||||
IRC_ENABLED = False
|
||||
# RSS allows to connect RSS feeds (from forum updates, blogs etc) to
|
||||
# an in-game channel. The channel will be updated when the rss feed
|
||||
|
|
@ -757,7 +723,6 @@ IRC_ENABLED = False
|
|||
# http://code.google.com/p/feedparser/)
|
||||
RSS_ENABLED = False
|
||||
RSS_UPDATE_INTERVAL = 60 * 10 # 10 minutes
|
||||
|
||||
# Grapevine (grapevine.haus) is a network for listing MUDs as well as allow
|
||||
# users of said MUDs to communicate with each other on shared channels. To use,
|
||||
# your game must first be registered by logging in and creating a game entry at
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue