#! /usr/bin/env python #A client for the crossfire rpg game import socket import select import sys import struct import threading import Queue import re import pygtk pygtk.require("2.0") import gtk import gtk.gdk invfacelist = {} facelist = {} faceimage = {} mapfaces = {} stats = {} blah = 0 class gtkui: def drawinfo(self, color, info): #FIXME: use a gui #print info self.showinfo(info) def reply(self, reply): self.client.send() def queryresponse(self, dialog, response, reply): if response == gtk.RESPONSE_OK: self.client.send("reply " + reply()) dialog.destroy() def query(self, type, message): print "b1" dialog = gtk.Dialog("Question", None, gtk.DIALOG_MODAL, (gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE, gtk.STOCK_OK, gtk.RESPONSE_OK)) label = gtk.Label(message) print "b2" dialog.vbox.pack_start(label, False, True, 0) label.show() print "b3" textbox = gtk.Entry() # handle password boxes if type == 4: textbox.set_visibility(0) print "b4" dialog.vbox.pack_start(textbox, False, True, 1) dialog.connect("response", self.queryresponse, textbox.get_text) print "b5" textbox.show() print "b6" dialog.show() print "b7" def showinfo(self, info, type = None): dialog = gtk.Dialog("Info", None, 0, (gtk.STOCK_CLOSE, 0, gtk.STOCK_OK, 2)) label = gtk.Label(info) dialog.vbox.pack_start(label, False, False, 1) label.show() dialog.show() def cmd_box_keypress(self, textbox, event, gettext, sender): keyname = gtk.gdk.keyval_name(event.keyval) if ((keyname == "Return") or (keyname == "KP_Enter")): sender("command 1 " + gettext()) textbox.set_text("") def maparea_configure_event(self, widget, event): x, y, width, height = widget.get_allocation() self.mappixmap = gtk.gdk.Pixmap(widget.window, width, height) self.mappixmap.draw_rectangle(widget.get_style().black_gc, True, 0, 0, width, height) return True def maparea_expose_event(self, widget, event): x , y, width, height = event.area widget.window.draw_drawable(widget.get_style().fg_gc[gtk.STATE_NORMAL], self.mappixmap, x, y, x, y, width, height) return False def update_map_pix(self): y = 0 while (y <= 64): blah = "" x = 0 while(x <= 64): curface = 0 lay=0 while(lay <= 3): if (mapfaces.has_key(struct.pack("!3h",x,y,lay))): #we have a face curface=mapfaces[struct.pack("!3h",x,y,lay)] asd= lay lay+=1 if (curface != 0): blah+=str(asd)+" " else: blah+=" " x+=1 y+=1 #print " " #print blah def getfaceimage(self, face): filename = "pics/" + facelist[face].replace(".", ".base.") + ".png" pixbuf = gtk.gdk.pixbuf_new_from_file(filename) height = pixbuf.get_height() width = pixbuf.get_width() pixbuf = pixbuf.scale_simple((width/2),(height/2), gtk.gdk.INTERP_TILES) faceimage[face] = pixbuf def updatespace(self, x, y): #update face lay = 0 anyontile = 0 facepic = None self.mappixmap.draw_rectangle(self.map_area.get_style().black_gc, True, x*16, y*16, 16, 16) while(lay <= 3): packloc=struct.pack("!3h",x,y,lay) if (mapfaces.has_key(packloc)): #we have a face face=mapfaces[packloc] if (face): #face is non-blank anyontile = 1 if (mapfaces.has_key(packloc+"a")): if (mapfaces[packloc+"a"] == "0"): #we have a small face self.mappixmap.draw_pixbuf(None, faceimage[face], 0, 0, x*16, y*16) else: #we have a large face coords=mapfaces[packloc+"a"].split("|") self.mappixmap.draw_pixbuf(None, faceimage[face], 0, 0, int(coords[0])*16, int(coords[1])*16) #self.mappixmap.draw_pixbuf(None, faceimage[face].scale_simple(16,16, gtk.gdk.INTERP_TILES), 0, 0, x*16, y*16) else: #we have a small face self.mappixmap.draw_pixbuf(None, faceimage[face], 0, 0, x*16, y*16) lay+=1 def __init__(self, queue): self.__sendqueue = queue self.window = gtk.Window(gtk.WINDOW_TOPLEVEL) #window.setname("Test") vbox = gtk.VBox(False, 0) self.window.add(vbox) vbox.show() self.mappixmap = None self.map_area = gtk.DrawingArea() self.map_area.set_size_request(640, 640) self.map_area.set_events(gtk.gdk.EXPOSURE_MASK) self.map_area.connect("expose_event", self.maparea_expose_event) self.map_area.connect("configure_event", self.maparea_configure_event) vbox.pack_start(self.map_area, True, False, 0) self.map_area.show() self.cmd_box = gtk.Entry() self.cmd_box.set_events(gtk.gdk.EXPOSURE_MASK | gtk.gdk.KEY_PRESS_MASK | gtk.gdk.KEY_RELEASE_MASK) self.cmd_box.connect("key_press_event", self.cmd_box_keypress, self.cmd_box.get_text, self.__sendqueue.put) vbox.pack_start(self.cmd_box, True, False, 1) self.cmd_box.show() self.window.connect("destroy", lambda w: gtk.main_quit()) self.window.show() #FIXME: move some of this to another file? class crossfire(threading.Thread): #Is there a premade function for this? def sockgetbytes(self, bytes): data = "" while len(data) < bytes: for socket in select.select([self.socket], [self.socket], [self.socket])[0]: data = data + socket.recv(bytes - len(data)) return data def kill(self): quit def send(self, data): print "send" self.socket.send(struct.pack("!H", len(data)) + data) print "sent: " + data def recv(self): return self.sockgetbytes(struct.unpack("!H", self.sockgetbytes(2))[0]) def __init__(self, cfui, server, port = 13327): print "cfinit" self.cfui = cfui self.server = server self.port = port self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) threading.Thread.__init__(self) print "2a" def addotherfaces(self, face, x, y, layer, x1, y1): #look at nearby faces to search for other faces to set up height = (faceimage[face].get_height() / 16)-1 width = (faceimage[face].get_width() / 16)-1 curw = 0 while (curw >= -width): curh = 0 while (curh >= -height): packloc = struct.pack("!3h",x+curw,y+curh,layer) #print str(struct.unpack("!3h",packloc)) mapfaces[packloc] = face mapfaces[packloc+"a"] = str(x1)+"|"+str(y1) #print mapfaces[packloc+"a"] #print "aof1:" + str(struct.unpack("!3h",packloc)) #print "aof2: " + str(x)+"|"+str(y) curh-=1 curw-=1 def delotherfaces(self, face, x, y, layer): #look at nearby faces to search for other faces to clear height = (faceimage[face].get_height() / 16)-1 width = (faceimage[face].get_width() / 16)-1 curw = 0 while (curw >= -width): curh = 0 while (curh >= -height): if ((curw != 0) or (curh != 0)): packloc = struct.pack("!3h",x+curw,y+curh,layer) if mapfaces.has_key(packloc+"a"): if (mapfaces[packloc+"a"] != "0"): mapfaces[packloc] = 0 mapfaces[packloc+"a"] = "0" curh-=1 curw-=1 def run(self): blah=0 print "cfrun" self.socket.connect((self.server, self.port)) self.send("version 1023 1027 pyclient") print "sent version" #FIXME: needs to be configurable self.send("setup map1cmd 1 map1acmd 1 sound 0 exp64 1 sexp 0 darkness 0 newmapcmd 1 faceset 0 facecache 1 itemcmd 1 mapsize 20 X 20") self.send("addme") print "addme sent" self.socket.setblocking(0) print "starting main loop" self.curx = 10 self.cury = 10 while True: read, write, error = select.select([self.socket], [self.socket], [self.socket]) for socket in read: data = re.split(" ", self.recv(), 1) if data[0] == "drawinfo": data = re.split(" ", data[1], 1) # gtk.threads_enter() # self.cfui.drawinfo(data[0], data[1]) # gtk.threads_leave() print data[1] elif data[0] == "query": data = re.split(" ", data[1], 1) print data[1] blah=blah+1 if (blah <= 1): self.send("reply RedTemp") else: self.send("reply Yes") # gtk.threads_enter() # self.cfui.query(data[0], data [1]) # gtk.threads_leave() elif data[0] == "addme_success": # gtk.threads_enter() # self.cfui.drawinfo(data[0]) # gtk.threads_leave() pass elif data[0] == "face2": #data format: (short)(byte)(long)(string) #FIXME: This is a bit of a hack to get the right string length. faceinfo = struct.unpack("!HxL" + str(len(data[1]) - struct.calcsize("!HxL")) + "s",data[1]) facelist[faceinfo[0]] = faceinfo[2] invfacelist[faceinfo[2]] = faceinfo[0] self.cfui.getfaceimage(faceinfo[0]) #print "face: " + str(faceinfo[0]) + " " + faceinfo[2] elif (data[0] == "map_scroll"): #data format: (plaintextint) (plaintextint) print "map_scroll: " + data[1] vals = data[1].split(" ") self.curx += int(vals[0]) self.cury += int(vals[1]) elif (data[0] == "map1a") or (data[0] == "map1"): #data format: (6bit int)(6bit int)(4 padding bits)(byte)(short)(short)(short) #FIXME: This is a bit of a hack to get the right string length in unpack. #struct.unpack("!HxL" + str(len(data[1]) - struct.calcsize("!HxL")) + "s",data[1]) xylist = {} while (0 < len(data[1])): #recursively strip bytes with unpack. data = struct.unpack("!H" + str(len(data[1]) - struct.calcsize("!H")) + "s", data[1]) mask = data[0] x = (mask >>10) & 0x3f y = (mask >>4) & 0x3f if((mask & 0xf) != 0) : #square not blocked if(mask & 0x8) : #has darkness byte. Currently do nothing with it except cut that byte out data = struct.unpack("!x" + str(len(data[1]) - struct.calcsize("!x")) + "s", data[1]) #layer loop laypos=3 while (laypos>=0): if (mask & (1 << laypos)): #update face data = struct.unpack("!H" + str(len(data[1]) - struct.calcsize("!H")) + "s", data[1]) packloc = struct.pack("!3h",x+self.curx,y+self.cury,(3-laypos)) if (data[0] != 0): #store face in a dictionary indexed by a "!3h" packing of x, y, and the layer. mapfaces[packloc] = data[0] height = faceimage[data[0]].get_height() width = faceimage[data[0]].get_width() if ((height != 16) or (width != 16)): mapfaces[packloc+"a"] = str(x+1+self.curx-(width/16))+"|"+str(y+1+self.cury-(height/16)) self.addotherfaces(data[0], x+self.curx, y+self.cury, (3-laypos), (x+1+self.curx-(width/16)), (y+1+self.cury-(height/16))) else: mapfaces[packloc+"a"] = "0" else: #clear layer if (mapfaces.has_key(packloc)): height = faceimage[mapfaces[packloc]].get_height() width = faceimage[mapfaces[packloc]].get_width() if (mapfaces[packloc+"a"] == str(x+1+self.curx-(width/16))+"|"+str(y+1+self.cury-(height/16))): #is big face mapfaces[packloc+"a"] = "0" self.delotherfaces(mapfaces[packloc], x+self.curx, y+self.cury, (3-laypos)) mapfaces[packloc] = 0 laypos -= 1 xylist[struct.pack("!2h", x+self.curx, y+self.cury)] = struct.pack("!2h", x+self.curx, y+self.cury) gtk.threads_enter() for xypack in xylist: newxy = struct.unpack("!2h", xypack) self.cfui.updatespace(newxy[0], newxy[1]) self.cfui.map_area.queue_draw() gtk.threads_leave() elif (data[0] == "newmap"): #enter new map mapfaces.clear() gtk.threads_enter() self.cfui.mappixmap.draw_rectangle(self.cfui.map_area.get_style().black_gc, True, 0, 0, 640, 640) self.cfui.map_area.queue_draw() gtk.threads_leave() self.curx = 10 self.cury = 10 elif (data[0] == "stats"): #Here come some stats while (0 < len(data[1])): data = struct.unpack("!B" + str(len(data[1]) - struct.calcsize("!B")) + "s", data[1]) if (data[0] == 1): #hp data = struct.unpack("!H" + str(len(data[1]) - struct.calcsize("!H")) + "s", data[1]) stats["hp"] = data[0] print "HP: " + str(data[0]) elif (data[0] == 2): #maxhp data = struct.unpack("!H" + str(len(data[1]) - struct.calcsize("!H")) + "s", data[1]) stats["maxhp"] = data[0] print "MAXHP: " + str(data[0]) elif (data[0] == 3): #sp data = struct.unpack("!H" + str(len(data[1]) - struct.calcsize("!H")) + "s", data[1]) stats["sp"] = data[0] print "SP: " + str(data[0]) elif (data[0] == 4): #maxsp data = struct.unpack("!H" + str(len(data[1]) - struct.calcsize("!H")) + "s", data[1]) stats["maxsp"] = data[0] print "MAXSP: " + str(data[0]) elif (data[0] == 12): #level data = struct.unpack("!H" + str(len(data[1]) - struct.calcsize("!H")) + "s", data[1]) stats["level"] = data[0] print "LEVEL: " + str(data[0]) elif (data[0] == 18): #food data = struct.unpack("!H" + str(len(data[1]) - struct.calcsize("!H")) + "s", data[1]) stats["food"] = data[0] print "FOOD: " + str(data[0]) elif (data[0] == 23): #grace data = struct.unpack("!H" + str(len(data[1]) - struct.calcsize("!H")) + "s", data[1]) stats["grace"] = data[0] print "GRACE: " + str(data[0]) elif (data[0] == 24): #maxgrace data = struct.unpack("!H" + str(len(data[1]) - struct.calcsize("!H")) + "s", data[1]) stats["maxgrace"] = data[0] print "MAXGRACE: " + str(data[0]) elif (data[0] == 26): #weightlimit data = struct.unpack("!l" + str(len(data[1]) - struct.calcsize("!l")) + "s", data[1]) elif (data[0] == 17): #speed data = struct.unpack("!l" + str(len(data[1]) - struct.calcsize("!l")) + "s", data[1]) elif (data[0] == 19): #weapon_sp data = struct.unpack("!l" + str(len(data[1]) - struct.calcsize("!l")) + "s", data[1]) elif (data[0] == 20): #range data = struct.unpack("!B" + str(len(data[1]) - struct.calcsize("!B")) + "s", data[1]) data = struct.unpack("!" + str(data[0]) + "s" + str(len(data[1]) - struct.calcsize("!" + str(data[0]) + "s")) + "s", data[1]) elif (data[0] == 21): #title data = struct.unpack("!B" + str(len(data[1]) - struct.calcsize("!B")) + "s", data[1]) data = struct.unpack("!" + str(data[0]) + "s" + str(len(data[1]) - struct.calcsize("!" + str(data[0]) + "s")) + "s", data[1]) elif ((data[0] >= 100) and (data[0] <= 117)): #resist data = struct.unpack("!H" + str(len(data[1]) - struct.calcsize("!H")) + "s", data[1]) elif ((data[0] >= 140) and (data[0] < 190)): #exp stat data = struct.unpack("!B" + str(len(data[1]) - struct.calcsize("!B")) + "s", data[1]) data = struct.unpack("!d" + str(len(data[1]) - struct.calcsize("!d")) + "s", data[1]) elif (data[0] == 11): #32bit exp data = struct.unpack("!l" + str(len(data[1]) - struct.calcsize("!l")) + "s", data[1]) elif (data[0] == 28): #64bit exp data = struct.unpack("!d" + str(len(data[1]) - struct.calcsize("!d")) + "s", data[1]) else: #currently unhandled stats of 16bit format data = struct.unpack("!H" + str(len(data[1]) - struct.calcsize("!H")) + "s", data[1]) elif (data[0] == "player"): print "player info" else: print data[0] for socket in error: drawinfo("SOCKET ERROR!!!") class sender(threading.Thread): def __init__(self, queue, client): self.__queue = queue self.__client = client threading.Thread.__init__(self) def run(self): while 1: self.__client.send(self.__queue.get()) def kill(self): quit class gtkthreader(threading.Thread): def __init__(self): threading.Thread.__init__(self) def run(self): gtk.threads_enter() gtk.main() gtk.threads_leave() def kill(self): quit gtk.threads_init() sendqueue = Queue.Queue(0) #gmainthread = gtkthreader() #gmainthread.start() ui = gtkui(sendqueue) print "1" ui.client = crossfire(ui, "crossfire.metalforge.net") print "2" ui.client.start() print "3" sendthread = sender(sendqueue, ui.client) sendthread.start() gtk.threads_enter() gtk.main() gtk.threads_leave() print "4" sendthread.kill() ui.client.kill()