mirror of
https://github.com/evennia/evennia.git
synced 2026-03-29 03:57:17 +02:00
Evennia now runs on its own Twisted webserver (no need for testserver or Apache if you don't want to). Evennia now also has an ajax long-polling web client running from Twisted. The web client requires no extra dependencies beyond jQuery which is included. The src/server structure has been r
cleaned up and rewritten to make it easier to add new protocols in the future - all new protocols need to inherit from server.session.Session, whi ch implements a set of hooks that Evennia uses to communicate. The current web client protocol is functional but does not implement any of rcaskey 's suggestions as of yet - it uses a separate data object passed through msg() to communicate between the server and the various protocols. Also the client itself could probably need cleanup and 'prettification'. The fact that the system runs a hybrid of Django and Twisted, getting the best of both worlds should allow for many possibilities in the future. /Griatch
This commit is contained in:
parent
ecefbfac01
commit
251f94aa7a
118 changed files with 9049 additions and 593 deletions
|
|
@ -72,7 +72,7 @@ class ANSIParser(object):
|
|||
|
||||
# MUX-style mappings %cr %cn etc
|
||||
|
||||
mux_ansi_map = [
|
||||
self.mux_ansi_map = [
|
||||
(r'%r', ANSITable.ansi["return"]),
|
||||
(r'%t', ANSITable.ansi["tab"]),
|
||||
(r'%b', ANSITable.ansi["space"]),
|
||||
|
|
@ -102,7 +102,7 @@ class ANSIParser(object):
|
|||
|
||||
hilite = ANSITable.ansi['hilite']
|
||||
normal = ANSITable.ansi['normal']
|
||||
ext_ansi_map = [
|
||||
self.ext_ansi_map = [
|
||||
(r'{r', hilite + ANSITable.ansi['red']),
|
||||
(r'{R', normal + ANSITable.ansi['red']),
|
||||
(r'{g', hilite + ANSITable.ansi['green']),
|
||||
|
|
@ -121,8 +121,8 @@ class ANSIParser(object):
|
|||
(r'{X', normal + ANSITable.ansi['black']), #pure black
|
||||
(r'{n', normal) #reset
|
||||
]
|
||||
|
||||
self.ansi_map = mux_ansi_map + ext_ansi_map
|
||||
|
||||
self.ansi_map = self.mux_ansi_map + self.ext_ansi_map
|
||||
|
||||
# prepare regex matching
|
||||
self.ansi_sub = [(re.compile(sub[0], re.DOTALL), sub[1])
|
||||
|
|
@ -141,13 +141,15 @@ class ANSIParser(object):
|
|||
if not string:
|
||||
return ''
|
||||
string = str(string)
|
||||
for sub in self.ansi_sub:
|
||||
for sub in self.ansi_sub:
|
||||
# go through all available mappings and translate them
|
||||
string = sub[0].sub(sub[1], string)
|
||||
if strip_ansi:
|
||||
# remove all ANSI escape codes
|
||||
string = self.ansi_regex.sub("", string)
|
||||
return string
|
||||
|
||||
|
||||
|
||||
ANSI_PARSER = ANSIParser()
|
||||
|
||||
|
|
@ -161,3 +163,5 @@ def parse_ansi(string, strip_ansi=False, parser=ANSI_PARSER):
|
|||
|
||||
"""
|
||||
return parser.parse_ansi(string, strip_ansi=strip_ansi)
|
||||
|
||||
|
||||
|
|
|
|||
137
src/utils/ansi2html.py
Normal file
137
src/utils/ansi2html.py
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
|
||||
"""
|
||||
ANSI -> html converter
|
||||
|
||||
Credit for original idea and implementation
|
||||
goes to Muhammad Alkarouri and his
|
||||
snippet #577349 on http://code.activestate.com.
|
||||
|
||||
(extensively modified by Griatch 2010)
|
||||
"""
|
||||
|
||||
import re
|
||||
import cgi
|
||||
from src.utils import ansi
|
||||
|
||||
class ANSItoHTMLparser(object):
|
||||
"""
|
||||
This class describes a parser for converting from ansi to html.
|
||||
"""
|
||||
|
||||
# mapping html color name <-> ansi code.
|
||||
# Obs order matters - longer ansi codes are replaced first.
|
||||
colorcodes = [('white', '\033[1m\033[37m'),
|
||||
('cyan', '\033[1m\033[36m'),
|
||||
('blue', '\033[1m\033[34m'),
|
||||
('red', '\033[1m\033[31m'),
|
||||
('magenta', '\033[1m\033[35m'),
|
||||
('lime', '\033[1m\033[32m'),
|
||||
('yellow', '\033[1m\033[33m'),
|
||||
('gray', '\033[37m'),
|
||||
('teal', '\033[36m'),
|
||||
('navy', '\033[34m'),
|
||||
('maroon', '\033[31m'),
|
||||
('purple', '\033[35m'),
|
||||
('green', '\033[32m'),
|
||||
('olive', '\033[33m')]
|
||||
normalcode = '\033[0m'
|
||||
tabstop = 4
|
||||
|
||||
re_string = re.compile(r'(?P<htmlchars>[<&>])|(?P<space>^[ \t]+)|(?P<lineend>\r\n|\r|\n)|(?P<protocal>(^|\s)((http|ftp)://.*?))(\s|$)',
|
||||
re.S|re.M|re.I)
|
||||
|
||||
def re_color(self, text):
|
||||
"Replace ansi colors with html color tags"
|
||||
for colorname, code in self.colorcodes:
|
||||
regexp = "(?:%s)(.*?)(?:%s)" % (code, self.normalcode)
|
||||
regexp = regexp.replace('[', r'\[')
|
||||
text = re.sub(regexp, r'''<span style="color: %s">\1</span>''' % colorname, text)
|
||||
return text
|
||||
|
||||
def re_bold(self, text):
|
||||
"Replace ansi hilight with bold text"
|
||||
regexp = "(?:%s)(.*?)(?:%s)" % ('\033[1m', self.normalcode)
|
||||
regexp = regexp.replace('[', r'\[')
|
||||
return re.sub(regexp, r'<span style="font-weight:bold">\1</span>', text)
|
||||
|
||||
def re_underline(self, text):
|
||||
"Replace ansi underline with html equivalent"
|
||||
regexp = "(?:%s)(.*?)(?:%s)" % ('\033[4m', self.normalcode)
|
||||
regexp = regexp.replace('[', r'\[')
|
||||
return re.sub(regexp, r'<span style="text-decoration: underline">\1</span>', text)
|
||||
|
||||
def remove_bells(self, text):
|
||||
"Remove ansi specials"
|
||||
return text.replace('\07', '')
|
||||
|
||||
def remove_backspaces(self, text):
|
||||
"Removes special escape sequences"
|
||||
backspace_or_eol = r'(.\010)|(\033\[K)'
|
||||
n = 1
|
||||
while n > 0:
|
||||
text, n = re.subn(backspace_or_eol, '', text, 1)
|
||||
return text
|
||||
|
||||
def convert_linebreaks(self, text):
|
||||
"Extra method for cleaning linebreaks"
|
||||
return text.replace(r'\n', r'<br>')
|
||||
|
||||
def do_sub(self, m):
|
||||
"Helper method to be passed to re.sub."
|
||||
c = m.groupdict()
|
||||
if c['htmlchars']:
|
||||
return cgi.escape(c['htmlchars'])
|
||||
if c['lineend']:
|
||||
return '<br>'
|
||||
elif c['space']:
|
||||
t = m.group().replace('\t', ' '*self.tabstop)
|
||||
t = t.replace(' ', ' ')
|
||||
return t
|
||||
elif c['space'] == '\t':
|
||||
return ' '*self.tabstop
|
||||
else:
|
||||
url = m.group('protocal')
|
||||
if url.startswith(' '):
|
||||
prefix = ' '
|
||||
url = url[1:]
|
||||
else:
|
||||
prefix = ''
|
||||
last = m.groups()[-1]
|
||||
if last in ['\n', '\r', '\r\n']:
|
||||
last = '<br>'
|
||||
return '%s%s' % (prefix, url)
|
||||
|
||||
def parse(self, text):
|
||||
"""
|
||||
Main access function, converts a text containing
|
||||
ansi codes into html statements.
|
||||
"""
|
||||
|
||||
# parse everything to ansi first
|
||||
text = ansi.parse_ansi(text)
|
||||
|
||||
# convert all ansi to html
|
||||
result = re.sub(self.re_string, self.do_sub, text)
|
||||
result = self.re_color(result)
|
||||
result = self.re_bold(result)
|
||||
result = self.re_underline(result)
|
||||
result = self.remove_bells(result)
|
||||
result = self.convert_linebreaks(result)
|
||||
result = self.remove_backspaces(result)
|
||||
|
||||
# clean out eventual ansi that was missed
|
||||
result = ansi.parse_ansi(result, strip_ansi=True)
|
||||
|
||||
return result
|
||||
|
||||
HTML_PARSER = ANSItoHTMLparser()
|
||||
|
||||
#
|
||||
# Access function
|
||||
#
|
||||
|
||||
def parse_html(string, parser=HTML_PARSER):
|
||||
"""
|
||||
Parses a string, replace ansi markup with html
|
||||
"""
|
||||
return parser.parse(string)
|
||||
|
|
@ -22,11 +22,8 @@ Models covered:
|
|||
from django.conf import settings
|
||||
from django.contrib.auth.models import User
|
||||
from django.db import IntegrityError
|
||||
from src.players.models import PlayerDB
|
||||
from src.help.models import HelpEntry
|
||||
from src.comms.models import Msg, Channel
|
||||
from src.comms import channelhandler
|
||||
from src.comms.managers import to_object
|
||||
|
||||
|
||||
from src.permissions.permissions import set_perm
|
||||
from src.permissions.models import PermissionGroup
|
||||
from src.utils import logger
|
||||
|
|
@ -212,6 +209,8 @@ def create_help_entry(key, entrytext, category="General", permissions=None):
|
|||
general help on the game, more extensive info, in-game setting information
|
||||
and so on.
|
||||
"""
|
||||
|
||||
from src.help.models import HelpEntry
|
||||
try:
|
||||
new_help = HelpEntry()
|
||||
new_help.key = key
|
||||
|
|
@ -293,7 +292,9 @@ def create_message(senderobj, message, channels=None,
|
|||
at the same time, it's up to the command definitions to limit this as
|
||||
desired.
|
||||
"""
|
||||
|
||||
from src.comms.models import Msg
|
||||
from src.comms.managers import to_object
|
||||
|
||||
def to_player(obj):
|
||||
"Make sure the object is a player object"
|
||||
if hasattr(obj, 'user'):
|
||||
|
|
@ -345,6 +346,9 @@ def create_channel(key, aliases=None, description=None,
|
|||
listen/send/admin permissions are strings if permissions separated
|
||||
by commas.
|
||||
"""
|
||||
|
||||
from src.comms.models import Channel
|
||||
from src.comms import channelhandler
|
||||
try:
|
||||
new_channel = Channel()
|
||||
new_channel.key = key
|
||||
|
|
@ -408,6 +412,8 @@ def create_player(name, email, password,
|
|||
# isn't already registered, and that the password is ok before
|
||||
# getting here.
|
||||
|
||||
from src.players.models import PlayerDB
|
||||
|
||||
if user:
|
||||
new_user = user
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -18,14 +18,16 @@ def is_iter(iterable):
|
|||
they are actually iterable), since string iterations
|
||||
are usually not what we want to do with a string.
|
||||
"""
|
||||
if isinstance(iterable, basestring):
|
||||
# skip all forms of strings (str, unicode etc)
|
||||
return False
|
||||
try:
|
||||
# check if object implements iter protocol
|
||||
return iter(iterable)
|
||||
except TypeError:
|
||||
return False
|
||||
return hasattr(iterable, '__iter__')
|
||||
|
||||
# if isinstance(iterable, basestring):
|
||||
# # skip all forms of strings (str, unicode etc)
|
||||
# return False
|
||||
# try:
|
||||
# # check if object implements iter protocol
|
||||
# return iter(iterable)
|
||||
# except TypeError:
|
||||
# return False
|
||||
|
||||
def fill(text, width=78):
|
||||
"""
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue