#!/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()