All pastes #495293 Raw Edit

robertj

public text v1 · immutable
·published 2007-05-18 17:56 UTC
rendered paste body
#!/usr/bin/pythonMAXTRANS = 5000DMF = 16DVELF = 1 # enums ganked from sauerbraten/src/fpsgame/game.h for the most partganked_enums = """    SV_INITS2C, SV_INITC2S, SV_POS, SV_TEXT, SV_SOUND, SV_CDIS,    SV_DIED, SV_DAMAGE, SV_SHOT, SV_FRAGS, SV_GUNSELECT,    SV_MAPCHANGE, SV_MAPVOTE, SV_ITEMSPAWN, SV_ITEMPICKUP, SV_DENIED,    SV_PING, SV_PONG, SV_CLIENTPING,    SV_TIMEUP, SV_MAPRELOAD, SV_ITEMACC,    SV_SERVMSG, SV_ITEMLIST, SV_RESUME,    SV_EDITENT, SV_EDITF, SV_EDITT, SV_EDITM, SV_FLIP, SV_COPY, SV_PASTE, SV_ROTATE, SV_REPLACE, SV_DELCUBE, SV_NEWMAP, SV_GETMAP,    SV_MASTERMODE, SV_KICK, SV_CURRENTMASTER, SV_SPECTATOR, SV_SETMASTER, SV_SETTEAM,    SV_BASES, SV_BASEINFO, SV_TEAMSCORE, SV_REPAMMO, SV_FORCEINTERMISSION, SV_ANNOUNCE,    SV_CLIENT"""sauerConst = {}i = 0for var in ganked_enums.split(","):    sauerConst[var.strip()] = i     i += 1import sauerServerimport enetimport stuffimport timefrom threading import Thread# needed for sauerBufferfrom array import array# needed for exception loggingimport sys, tracebacksauer_players = {}def appendPacket(a,b):    a.data += b.dataclass sauerBuffer:    def __init__(self, ent=None):        if ent is not None:            intro = posPacket(ent).data            self.data = array('B', intro)        else:            self.data = array('B')    def putint(self, value):        if (-127 < value) and (value < 128):            self.data.insert(0, value & 0xFF)        elif (-0x8000 <= value) and (value < 0x8000):            self.data.insert(0, 0x80)            self.data.insert(0, value & 0xFF)            self.data.insert(0, (value >> 8) & 0xFF)        else:            self.data.insert(0, 0x81)            self.data.insert(0, value & 0xFF)            self.data.insert(0, (value >> 8) & 0xFF)            self.data.insert(0, (value >> 16) & 0xFF)            self.data.insert(0, (value >> 24) & 0xFF)    def getint(self):        a = self.data.pop()        if (0x80 == a):            a = self.data.pop()            a |= self.data.pop() << 8            if (0x8000 & a):                # fix negative values                a = - (0xFFFF ^ (a-1))        elif (0x81 == a):            a = self.data.pop()            a |= self.data.pop() << 8            a |= self.data.pop() << 16            a |= self.data.pop() << 24            if (0x80000000 & a):                # fix negative values                a = - (0xFFFFFFFF ^ (a-1))        elif (0x80 & a):            # fix negative values            a = - (0xFF ^ (a-1))        return a    def putuint(self, value):        """much smaller encoding for unsigned integers up to 28 bits, but can handle sign smaller encoding for unsigned integers up to 28 bits, but can handle signed"""        if ((1 << 28) <= value) or (value < -(1 << 27)):            raise ValueError("more than 28 bits used")        if (value < 0) or ((1<<21) <= value):            self.data.insert(0, 0x80 | (value & 0x7F))            self.data.insert(0, 0x80 | ((value >> 7) & 0x7F))            self.data.insert(0, 0x80 | ((value >> 14) & 0x7F))            self.data.insert(0, 0xFF & (value >> 21))        elif (value < (1 << 7)):            self.data.insert(0, 0xFF & value)        elif (value < (1 << 14)):            self.data.insert(0, 0x80 | (value & 0x7F))            self.data.insert(0, 0xFF & (value >> 7))        else:            self.data.insert(0, 0x80 | (value & 0x7F))            self.data.insert(0, 0x80 | ((value >> 7) & 0x7F))            self.data.insert(0, 0xFF & (value >> 14))    def getuint(self):        a = self.data.pop()        if (0x80 & a):            a += (self.data.pop() << 7) - 0x80            if (a & (1<<14)):                a += (self.data.pop() << 14) - (1<<14)            if (a & (1<<21)):                a += (self.data.pop() << 21) - (1<<21)            if(a & (1<<28)):                # fix negative values                a |= 0xF0000000                a = - (0xFFFFFFFF ^ (a-1))        return a    def hasData(self):        if len(self.data) > 0: return True        else: return False    def putstr(self, msg):        for char in msg: self.putint(ord(char))        self.putint(0)    def getstr(self):        buffer = ""        x = self.getint()        while x != 0:            buffer += chr(x)            x = self.getint()        return buffer    def test_int(self, exhaustive = 0):        """test putint and getint"""        print "test_int: border cases ... "        for value in [0, 1, -1, 127, -126, 128, -127, 0x7FFF,                -0x7FFF, -0x8000, 0x8000, -0x8001,                0x7ABCDEF9, -0x7ABCDEF9, 0x7FFFFFFF, -0x7FFFFFFE,                1 << 27, (1 << 27) - 1, -(1 << 27), 1-(1 << 27),                1 << 28, (1 << 28) - 1, -(1 << 28), 1-(1 << 28)]:            p = sauerBuffer()            try:                p.putint(value)            except:                print "could not putint ", value                continue            if (p.getint() != value) or (0 != len(p.data)):                print "FAILED ", value        if(exhaustive):            print "test_int: exhaustive ... (this is going to take quite a bit)"            b = 2 ** 31            lower_bound = -b            upper_bound = b            i = lower_bound            while i < upper_bound:                p = sauerBuffer()                try:                    p.putint(i)                except:                    print "could not putint ", i                    continue                if(p.getint() != i):                    print "FAILED ", i                i+= 1        print "DONE"        def test_uint(self, exhaustive = 0):        """test putuint and getuint"""        print "test_uint: border cases ... "        for value in [0, 1, -1, 127, -126, 128, -127, 0x7FFF,                -0x7FFF, -0x8000, 0x8000, -0x8001,                1 << 27, (1 << 27) - 1, -(1 << 27), 1-(1 << 27), (1 << 28) - 1]:            p = sauerBuffer()            try:                p.putuint(value)            except:                print "could not putuint ", value                continue            if (p.getuint() != value) or (0 != len(p.data)):                print "FAILED ", value, "(", result, ")"        if(exhaustive):            print "test_uint: exhaustive ... (this is going to take quite a bit)"            b = (1 << 28) - 1            lower_bound = -0xFFFFF            upper_bound = b            i = lower_bound            while i < upper_bound:                p = sauerBuffer()                try:                    p.putuint(i)                except:                    print "could not putuint ", i                    continue                if (p.getuint() != i) or (0 != len(p.data)):                    print "FAILED ", i                i+= 1        print "DONE"class sauerAdvertiserThread(Thread):    __stop = False    def stop(self):        self.__stop = True    def run(self):        # register        print "advertising server presence to sauerbraten master server"        while not self.__stop:          # TODO  cant sleep this long or takes forever to shutdown when we wait for this thread to quit          #time.sleep(60 * 15)          passclass sauerServerThread(Thread):    __stop = False    buffer = sauerBuffer()    def stop(self):        self.__stop = True    def run(self):        serverAddress = enet.Address(enet.HOST_ANY, 28785) # SAUERBRATEN_SERVER_PORT        server = enet.Host(serverAddress, 32, 0, 0)        print "now listening for sauerbraten connections on port ", serverAddress.port        while not self.__stop:          event = server.service(1000)          #print event.type          print "event type ", event.type, " got ", id(event.peer)          if event.type == 1:              new_player = sauerPlayer(event.peer)          elif event.type == 2:              leaving_player = sauer_players[id(event.peer)]              leaving_player.onDisconnect()          elif event.type == 3:              # why is this showing a different peer id              self.handleInput(sauer_players[id(event.peer)], event.packet)          for player in sauer_players:            sauer_players[player].sendQueuedUpdates()          #time.sleep(.03)    def handleInput(self, player, packet):        pw = self.buffer        pw.data += packet.data        if pw.debug is True: print packet.data        while pw.hasData():            type = pw.getint()               if pw.debug is True: print "got ", type            if type == 2:                 #print "POS packet                pw.getint() # client num                #print "xyz yaw"                player.o = (pw.getuint() / DMF, pw.getuint() / DMF, pw.getuint() / DMF) # xyz                player.yaw = pw.getuint() # yaw                player.pitch = pw.getint() # pitch                player.roll = pw.getint() # roll                player.v = (pw.getint()/DVELF, pw.getint()/DVELF, pw.getint()/DVELF) # velocity xyz                #print "State Info"                tmp = pw.getint()                if tmp & 0x20: # physstate pack                    if pw.debug is True: print " -------  found x,y gravity vectors"                    player.g = (pw.getint()/DVELF, pw.getint()/DVELF, player.g[2]) # gravity x y plus current                elif tmp & 0x10: # physstate pack                     if pw.debug is True: print " -------  found z gravity vector"                    player.g = (player.g[0], player.g[1], pw.getint()/DVELF) #gravity z                pw.getint() # state pack                           player.setPosition(player.o, player.yaw, player.pitch, player.roll, player.v)                if player.waitingForMapChange == True:                    player.onMapChange()                    player.waitingForMapChange = False            elif type == 1:                # player init info                u_name = pw.getStr()                # ignore name changes from registered players                if player.dbID == -1: player.name = u_name                team = pw.getStr()                if team == "VILLE": player.enhanced = True                player.lifesequence = pw.getint()                #pw.getint()                #pw.getint()                print "got name ", u_name, " team ", team            elif type == 3:                # text from player, command or chat                text = pw.getStr().strip()                try:                    player.cmd(text)                except:                    t, v, tb = sys.exc_info()                    print "EXCEPTION:\n".join(traceback.format_exception(t,v,tb))            elif type == 7:                victim_num = pw.getint()                dmg = pw.getint() # damage                pw.getint() # lifesequence of victim                pw.getint() #x                 pw.getint() #y                pw.getint() #z                victim = None                if victim_num == -1: victim = player                elif stuff.ents.has_key(victim_num): victim = stuff.ents[victim_num]                if victim != None:                    if dmg == 50: player.attack(victim)                    else: player.select(victim)            elif type == 10:                # map name                pw.getStr()                name = pw.getint()            elif type == 16:                # ping                pw.getint()            elif type == 21:                # server message, needs to be here to avoid crashing on forged packet becasue its a fixed length message                pw.getStr()            elif type == 23:                #item list                while pw.getint() != -1: continue            elif type == 24:                # entity type                print "i ", pw.getint()                print "x ", pw.getint()                print "y ", pw.getint()                print "z ", pw.getint()                print "type ", pw.getint()                print "p1 ", pw.getint()                print "p2 ", pw.getint()                print "p3 ", pw.getint()                print "p4 ", pw.getint()            elif type == 29 or type == 31:                # copy from edit mode                x1 = pw.getint() # x1                y1 = pw.getint() # y1                z1 = pw.getint() # z1                x2 = pw.getint() # x2                y2 = pw.getint() # y2                z2 = pw.getint() # z2                gridsize = pw.getint() #grid                pw.getint() #grid orientation                pw.getint() #cx                pw.getint() #cxs                pw.getint() #cy                pw.getint() #cys                pw.getint() #corner                if(type == 31):                    pw.getint() # tex                    pw.getint() # newtex                    pw.getint() # orient                # shove selection coordinates into a structure so we can organize them properly                tselect = [x1,y1,z1,x1 + (x2 * gridsize),y1 + (y2 * gridsize),z1 + (z2 * gridsize)]                psA  = [0,0,0]                psB = [0,0,0]                for i in (0,1,2):                    if tselect[i] < tselect[i+3]:                        psA[i] = tselect[i]                        psB[i] = tselect[i+3]                    else:                        psB[i] = tselect[i+3]                        psA[i] = tselect[i]                    player.selectionA = (psA[0], psA[1], psA[2])                    player.selectionB = (psB[0], psB[1], psB[2])                player.msg("Area selected.")            elif type == 38:                # ctf bases                while pw.getint() != -1:                    getint(p)                    getint(p)            elif type == 39:                # ctf base info                pw.getint()                pw.getStr()                pw.getStr()                pw.getint()            elif type == 40:                # team score                pw.getStr()                pw.getint()            else:                print "Unknown packet type %d." % type                print "We should never get a packet type %d!" % type                print "Disconnecting ", player.name                player.onDisconnect()                # empty the buffer so we don't proccess any other packets                 self.buffer.data = ""                #raise SystemExit                #if(x > 0 & pw.hasData()):                #    if(type < 1):                #    for a in range(x): pw.getint()                #    print "Recovered, skipped %d integer" % x                #else:                #    print "Non-fixed packet size, thus non-recoverable."                 #raise SystemExitdef newGuiPacket(name, contents):    b = sauerBuffer()    b.putint(sauerConst['SV_NEWGUI'])    b.putstr(name)    b.putstr(contents)    return bdef showGuiPacket(name):    b = sauerBuffer()    b.putint(sauerConst['SV_SHOWGUI'])    b.putstr(name)    return bdef hideGuiPacket(name):    b = sauerBuffer()    b.putint(sauerConst['SV_HIDEGUI'])    b.putstr(name)    return bdef statsPacket(ent):    b = sauerBuffer()    b.putint(sauerConst['SV_STATS'])    b.putint(ent.lifesequence)    b.putint(ent.hp)    return bdef mapChangePacket(map, mode):    b = sauerBuffer()    b.putint(sauerConst['SV_MAPCHANGE'])    b.putstr("maps/" + map)    b.putint(mode)    return bdef welcomePacket(ent):    b = sauerBuffer()    b.putint(sauerConst['SV_INITS2C'])    b.putint(ent.entnum)    b.putint(252) # Protocol Version    # send game mode 4 to start so players cant do dmg to self with grenade launcher    b.putint(4)    return bdef msgPacket(msg):    b = sauerBuffer()    b.putint(sauerConst['SV_SERVMSG'])    b.putstr(msg)    return bdef itemListPacket():    b = sauerBuffer()    b.putint(sauerConst['SV_ITEMLIST'])    b.putint(-1)    return bdef posPacket(ent):    b = sauerBuffer()    b.putint(sauerConst['SV_POS'])    b.putint(ent.entnum)    b.putuint(ent.o[0] * DMF)    b.putuint(ent.o[1] * DMF)    b.putuint(ent.o[2] * DMF)    b.putuint(ent.yaw)    b.putint(ent.pitch)    b.putint(ent.roll)    b.putint(ent.v[0] * DVELF)    b.putint(ent.v[1] * DVELF)    b.putint(ent.v[2] * DVELF)    # TODO: should be gravity x or gravity Y    if False or False:        b.putint(self.g[0] * DVELF)        b.putint(self.g[1] * DVELF)    # TODO: should be gravity 0     if False: b.putint(self.g[0] * DVELF)    b.putint(ent.move_pack)    return bdef infoPacket(ent):    # even though we don't need immortal objects to have hp in sauerville, sauerbraten does    if hasattr(ent, "maxhp"): maxhp = ent.maxhp    else: maxhp = 1    b = sauerBuffer()    b.putint(sauerConst['SV_INITC2S'])    b.putstr(ent.name)    b.putstr(ent.model) # used by sauer for frags    b.putstr(ent.lifesequence)    b.putstr(maxhp)    b.putstr(0) # frags    return bdef damagePacket(ent, damage):    b = sauerBuffer(ent)    b.putint(sauerConst['SV_DAMAGE'])    b.putint(self.entnum)    putint(damage)    putint(ent.lifesequence) # frags    putint(0)    putint(0)    putint(0)    return bdef editEntPacket(experiencer, ent, itemnum, o, type, a, b, c, d):    b = sauerBuffer(experiencer)    b.data += ent.posPacket().data    b.putint(sauerConst['SV_EDITENT'])    b.putint(itemnum)    b.putint(o[0] * DMF)    b.putint(p[1] * DMF)    b.putint(o[2] * DMF)    b.putint(type)    # these 4 have different meanings depending on enttype    b.putint(a)    b.putint(b)    b.putint(c)    b.putint(d)    return bdef removeEntPacket(experiencer, ent):    b = sauerBuffer()    b.putint(CV_DIS)    b.putint(ent.entnum)    b.putint()class sauerPlayer(stuff.player):    locked = False    debug = True    def __init__(self, peer):        self.chanPos = []         self.chanServ = []         self.chanClient = []         self.peer = peer        sauer_players[id(self.peer)] = self        super(sauerPlayer, self).__init__()        #if self.debug is not True:        self.queue(welcomePacket(self), desc="welcome")        self.msg("register <password> to create a character or use login if you already have an account.")        self.waitingForMapChange = True    def queue(self, packet, ent=None, isPosUpdate=False, desc=None):        if self.debug is True:            if desc is not None:                print "sending ", desc                g = sauerBuffer()                g.data = packet.data                 while g.hasData(): print g.getint()            else:                print "sending unknown packet"        while self.locked: pass        if ent is None:            if isPosUpdate: self.chanPos.append(packet)            else: self.chanServ.append(packet)        else:            self.chanClient.append((ent, packet))    def menu(self, name):        return sauerMenu(self, name)    def sendQueuedUpdates(self):        while self.locked: pass        self.locked = True        for channel in [(self.chanPos, 1), (self.chanServ, 2)]:          if channel[0] != []:              data = ""              for packet in channel[0]:                  data += packet.data              p = enet.Packet(data, flags=enet.PACKET_FLAG_RELIABLE)              self.peer.send(channel[1], p)        if self.chanClient != []:            data = ""            for ent,unintroduced_packet in self.chanClient:                data += introductionPacket(ent, unintroduced_packet).data                data += unintroduced_packet.data            p = enet.Packet(data, flags=enet.PACKET_FLAG_RELIABLE)            self.peer.send(2, p)        self.chanPos = []        self.chanServ = []        self.chanClient = []         self.locked = False    def hearEquipment(self):        self.msg("EQUIPMENT")        menu = self.menu("Equipment")        for slot in self.slots:            if self.slots[slot] is not None: name = self.slots[slot].name            else: name = "<nothing>"            submenu = self.menu("Equiped Item")            submenu.button("drop", "say drop")            submenu.button("sell", "say sell")            menu.button(slot + ": " + name, "newgui item [ " + submenu.txt + "]; showgui item;")        menu.fire()    def hearInventory(self):        menu = self.menu("Inventory")        for item in self.inventory:            submenu = self.menu("item")            submenu.button("drop", "say drop " + item.name)            submenu.button("sell", "say sell " + item.name)            menu.button(item.name, "newgui item [ " + submenu.txt + "]; showgui item;")        if len(self.inventory) == 0: menu.txt = "<nothing>"        menu.button("status", "select " + self.name + "; say examine;")        menu.fire()    def hearExamine(self, ent):        menu = self.menu("Examine")        sale_set = set()        if hasattr(ent, "selling"): sale_set = ent.selling        if hasattr(ent, "making"): sale_set = sale_set.union(ent.making)        if hasattr(ent, "selling") and sale_set != []:            menu.tab("selling")            for x in sale_set:                menu.button(x.name, "say buy " + x.name)        menu.tab("info")        menu.text(ent.name)        if hasattr(ent, "maxhp"): menu.text("hp: " + str(ent.hp) + " / " + str(ent.maxhp))        if hasattr(ent, "xp"): menu.text("xp: " + str(ent.xp))        if hasattr(ent, "level"): menu.text("level: " + str(ent.level))        if hasattr(ent, "gold"): menu.text("gold: " + str(ent.gold))        if hasattr(ent, "lootable"):            if ent.lootable == "auto":                menu.button("say take " + ent.name, "get")            else:                if len(ent.contents) == 0: menu.text(ent.name + " is empty.")                else:                    for x in ent.contents:                         menu.button("say take " + x.name, "take " + x.name)        if self.admin:            menu.tab("admin")            if(hasattr(self.selection, "face")): menu.button("face", "say face")            if(hasattr(self.selection, "setPosition")): menu.button("summon", "say summon")            if(hasattr(self.selection, "name")): menu.button("rename", "saycommand [/say foo ]")            if(hasattr(self.selection, "save")): menu.button("save", "say save")        menu.fire()    def hearStats(self):        if self.debug is True: return        self.queue(statsPacket(self), desc="hearStats")    def introductionPacket(ent, unintroduced_packet):        b = sauerBuffer()        l = len(unintroduced_packet.data)        b.data += struct.pack("qqq", ent.entnum, length & 0xFF, length >> 8)        return b    def hearPoof(self, ent):        if self.debug is True: return         self.queue(removeEntPacket(ent), desc="hearPoof")    def hearMapChange(self, map, mode):        #if self.debug is True: return        self.queue(mapChangePacket(map, mode), desc="hearMapChange")    def hearLocation(self, ent):        #if self.debug is True: return        if ent is not self:            #self.msg("I heard a " + ent.name + "move to " + str(ent.o[0]) + ", " + str(ent.o[1]))            self.queue(posPacket(ent), isPosUpdate=True, desc="hearLocation")    def hearInfo(self, ent):        if self.debug is True: return        if ent is not self:            self.queue(infoPacket(ent), ent=ent, desc="hearInfo")    def msg(self, text):        if self.debug is True: return        self.queue(messagePacket(text), desc="msg")    def select(self, ent):        super(sauerPlayer, self).select(ent)        self.cmd("examine")    def onDisconnect(self):        super(sauerPlayer, self).onDisconnect()        del sauer_players[self.peer]        self.peer.disconnect()    def teleport(self, nexto):        #kinda hacky to have the editEntPacket not be a self method        #but in the long term we shouldn't need it because we should        #be able to remove the pos from the start of editEntPacket         if self.debug is True: return        super(sauerPlayer, self).onDisconnect()        placeTeleDestPacket = editEntPacket(self, 100, nexto, 16, 27, 1, 0, 0)        # place teleporter on top of player        placeTeleDoorPacket = editEntPacket(self, 101, nexto, 15, 1, 0, 0, 0)        self.queue(placeTeleDestPacket, ent=self, desc="teleport place destination")        self.queue(placeTeleDoorPacket, ent=self, desc="teleport place entrance")        super(sauerPlayer, self).teleport(nexto)        # we have never seen our entnum connect so we believe its another player        # make us believe we have disconnected         removeGhostPacket = removeEntPacket(self)         self.queue(removeGhostPacket(), desc="teleport remove door")    def onMapChange(self):        if(self.dbID != -1 and self.nextMapLoc != False and self.nextMapLoc != (0,0,0)):            # try to avoid infinite teleporting loops :)            #print "teleporting ", self.name            #if self.distance(self.nextMapLoc) > 20:  self.teleport(self.nextMapLoc)            self.nextMapLoc = False        super(sauerPlayer, self).onMapChange()    def setMap(self, new_map, nextloc = False):        self.waitingForMapChange = True        old_ents = self.world.ents        # remove all the old ents        for i in old_ents:             self.hearPoof(old_ents[i])        # add all the new ents        world = worlds.getWorldByMapName(new_map)        new_ents = self.world.ents        for i in new_ents:            self.hearInfo(new_ents[i])        # let admins have edit privs, tell client to change the map        if self.admin: mode = 1        else: mode = 4        self.hearMapChange(new_map, mode)        # this should eventually go away entirely        self.queue(itemListPacket(), desc="itemListPacket from setMap")        # send hp, etc        self.hearStats()        # actually do the real map change in the stuff        super(sauerPlayer, self).setMap(new_map, nextloc)class sauerMenu:    txt = ""    def __init__(self, player, name):        self.name = name        self.player = player    def title(self, text):        self.txt += "guititle [" + self.sauerfy(text) + "];"    def tab(self, text):        self.txt += "guitab [" + self.sauerfy(text) + "];"    def text(self, text):        self.txt += "guitext [" + self.sauerfy(text) + "];"    def button(self, text, cmd):        self.txt += "guibutton [" + self.sauerfy(text) + "] [" + self.sauerfy(cmd) + "];"    def fire(self):        pass        self.player.queue(newGuiPacket("A", "B"), desc="new gui from fire")        self.player.queue(showGuiPacket("B"), desc="show gui from fire")    def sauerfy(self, text):        return textif __name__ == '__main__':    n = sauerBuffer()    #n.test_int()    #n.test_uint()    n.putint(0)    #print repr(n.data)    #print n.getint()    print "to see the real deal run server.py NOT this file"