#!/usr/bin/python
# Main module, handles scheduling and networking
import sys
if sys.version_info[:2] < [2,4]: print "Need 2.4"; sys.exit(1)
import cmd,event,textwrap,traceback as tb
from core import *
from socket import *
from util import *
sc = 0
colors = {
'D': "1;30", 'd': "0;30", 'R': "1;31", 'r': "0;31",
'G': "1;32", 'g': "0;32", 'Y': "1;33", 'y': "0;33",
'B': "1;34", 'b': "0;34", 'M': "1;35", 'm': "0;35",
'C': "1;36", 'c': "0;36", 'W': "1;37", 'x': "0" }
# replace colors given by the regular expr in sendl
def crepl(m):
x = m.group(1)
return (x == '{' and x) or (x in colors and "\x1b[%sm" % colors[x]) or ""
# Player connection class
class Conn(ac.dispatcher):
din = ""
out = ""
had_in = 0
p = 0
def __init__(S, s):
ac.dispatcher.__init__(S, s)
S.ui = LoginUI(S)
def sendl(S, t=""): # colorize and send with newline
S.write(t+NL)
def write(S, t): # colorize and send
S.out += re.sub(r'\{([^#])',crepl,t)
def handle_error(S):
tb.print_exc()
sys.exit(1)
# write buffers, with a prompt
def handle_write(S):
if not S._fileno: return
if S.had_in: S.write(S.ui.prompt()); S.had_in = 0
elif S.out:
S.out = NL+S.out # prepend newline
S.write(S.ui.prompt())
S.out = S.out[S.send(S.out):]
def handle_close(S):
p = S.p
if p and p.loc:
Act(p).room("$SN leaves the game.")
if p.foe:
p.unfight()
p.rpc = p.hpc = 1 # penalty
p.save()
p.goto(0) # remove from world
S.close()
def handle_read(S): S.din += S.recv(999)
# check for lines
def feed(S):
# get one line, kill telnet negotiation, don't handle IAC IAC, etc
x = re.split("\r\n|\r|\n",re.sub("\xff..","",S.din),1)
S.din = x[-1]
if len(x) > 1:
S.had_in = 1
S.ui.do(''.join([c for c in x[0] if c in printable]))
def writeable(S): return S.had_in or S.out
# Listening socket handler
class Serv(ac.dispatcher):
p = 0 # dummy
din = "" # dummy
def handle_accept(S):
c = Conn(S.accept()[0])
# decorator to help with UI prompt states
def xpro(p):
def d(f): f.pro = p; return f
return d
class UI(object):
pro = "> "
def sendl(S, t): S.c.sendl(t)
prompt = lambda S: S.pro
def sw(S, st): # switch to state, setting prompt
S.do = getattr(S, st)
S.pro = S.do.pro
# Game UI, passes off to in-game command handler
class PlayUI(UI):
def __init__(S, c, p):
from world import start
S.c = c
p.c, c.p = c, p
Act(p).world("{m>> {G$SN enters the realm.{x")
p.goto(start)
def do(S, l): cmd.do(S.c.p, l)
def prompt(S):
p = S.c.p
return Template(S.c.p.pro).safe_substitute(hpc = p.hpc,hpm = p.hpm,
rpc = p.rpc,rpm = p.rpm,cash = p.cash,nl = NL)
# Create a new account
class NewUI(UI):
pro = "New password: "
def __init__(S, c, n):
S.c = c
S.p = Player(n)
c.sendl("{b>> {BCreating a new character.{x")
@xpro("Confirm new: ")
def ver(S, l):
if l == S.p.passwd: S.c.ui = SexUI(S.c, S.p)
else: S.sendl("{RPasswords don't match.{x"); S.sw("ask")
@xpro("New password: ")
def ask(S, l):
if l: S.p.passwd = l; S.sw("ver")
do = ask # start here
# Uh, sex means gender here ;)
class SexUI(UI):
pro = "Sex ({CM{x/{MF{x/{gN{x): "
def __init__(S, c, p):
S.c, S.p = c, p
def do(S,l):
if l in 'Mm': S.p.sex = 1
if l in 'Ff': S.p.sex = 2
# anything else (blank included) is intrepreted as 'N', for the lazy
S.c.ui = PlayUI(S.c, S.p)
S.p.sendl("\r\n{GWelcome to the game!{x\r\n")
S.p.do("help intro")
# Prompts for name and password
class LoginUI(UI):
pro = "Name: "
def __init__(S,c):
S.c = c
c.sendl("{RWelcome to {g$$ Biz MUD{g $${R!{x")
def name(S,l):
l = fcap(l)
if not l.isalpha(): return S.sendl("Invalid name")
p = pload(l)
if p: S.p = p; S.sw("login")
else: S.c.ui = NewUI(S.c,l)
@xpro("Password: ")
def login(S,l):
if l == S.p.passwd:
S.c.ui = PlayUI(S.c, S.p)
for p in cplayers():
if p.name == S.p.name and p != S.p: p.c.close() # kill old
else: S.sendl("Invalid password")
do = name # start with this
# String editor
class EditUI(UI):
pro = ""
# ls = list of lines
# cb will be called when string is saved (not aborted)
# oldui will be restored when done editing
def __init__(S,c,t,cb):
S.c = c
S.ls = t.split(NL)
S.cb = cb
S.oldui = S.c.ui
c.sendl("{gType . on a blank line to finish or .h for extras.{x")
c.sendl(" "+t)
def do(S,l):
x = 0
if not l: S.ls += [l]
if l[0] == ".": # end or dot commands
if l == ".":
S.c.ui = S.oldui; S.cb(NL.join(S.ls)); return
else: x = l[1]
if x == "s": S.c.sendl(" "+NL.join(S.ls)) # show
if x == "c": S.ls = [] # clear
if x == "q": S.c.ui = S.oldui # abort edit
if x == "w": S.ls = textwrap.wrap("\n".join(S.ls), 77)
if x == "d":
if S.ls: del S.ls[-1] # delete last line
else: S.sendl("Nothing to delete.")
if x == "h": S.sendl("{gDot commands:{x\r\n"
".s show\r\n.c clear\r\n.d delete last line\r\n.w wrap\r\n"
".q abort\r\n. done")
if not x: S.ls += textwrap.wrap(l, 77) # append line
def pulse():
ac.loop(0.05,1,count=1)
# handle input: one line per connection
for c in ac.socket_map.values():
if c.din: c.feed()
sc.enter(0.1,-1,pulse,())
if __name__ == "__main__":
v = Serv()
v.create_socket(AF_INET, SOCK_STREAM)
v.set_reuse_addr()
v.bind(("",8500))
v.listen(5)
print "Running"
# sched will call the main "loop" every 0.1 seconds or so
# not very efficient (why doesn't sched use heapq?), but it's simple
sc = event.sc
sc.enter(0.1,-1,pulse,())
event.init()
sc.run()