def wget(self, data): data = decode_hybi(data)['payload'] if data == None: return False # Check if there's a header in the data if data.find("--KOP:") > -1: if self.wComplete == -1: verbose("Previous data was incomplete, new data already arrived", 0, 3) # Start splicing the header from the body self.wLength = data.partition("--KOP:")[2] data = self.wLength.partition(":POK--")[2] self.wLength = int(self.wLength.partition(":POK--")[0]) # If the length of the data matches the specified length, return the data if len(data) == self.wLength: self.wComplete = None self.wLength = None debug(data) # Return the completed data return data else: verbose("Length does not match. Data is " + str(len(data)) + " while we wanted " + str(self.wLength), 0, 3) self.wComplete = -1 self.wData = data else: verbose("Does not contain a KOP", 0, 3) # See if wData isn't none if self.wData: self.wData = self.wData + data if len(self.wData) == self.wLength: print "\r\nCompleted!!" self.wComplete = None self.wLength = None # Return the completed data return self.wData else: self.wComplete = -2 else: # If it is, this data got to us before any head, and we won't be able to piece it together with anything if data: verbose("Received stray data: " + data, 0, 3) else: verbose("Closing connection of user " + self.uid, 0, 2) self.closeClient() # Return false if this point has been reached return False
def open_socket(self): try: # create an InterNET, STREAMing socket (aka TCP/IP) self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # allow quick restart and reuse of server socket self.server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.server.bind((self.host,self.port)) self.server.listen(5) except socket.error, (value,message): if self.server: self.server.close() verbose("\r\nCould not open socket: " + message, 0, 0) sys.exit(1)
def __init__(self, startsock, address, queue, queueIndex, serverQueue): threading.Thread.__init__(self) self.startsock = startsock self.address = address self.verbose = False self.ssl_only = False self.daemon = False self.handler_id = 1 self.cert = os.path.abspath('') self.key = self.web = self.record = '' self.size = 65536 self.queue = queue self.queueIndex = queueIndex self.serverQueue = serverQueue self.threads = [] self.wData = None # Store incomplete data here self.wComplete = None # Data receive boolean self.wLength = None # The expected length of the received data self.running = True # While this is true, the thread runs self.initiated = False # Has this user been initialized? self.uid = None global globUser global globtest """ Do something with a WebSockets client connection. """ # Initialize per client settings self.send_parts = [] self.recv_part = None self.base64 = False self.rec = None self.start_time = int(time.time()*1000) # handler process try: try: verbose("Begin handshake", 2, 4) self.client = self.do_handshake(self.startsock, self.address) verbose("Handshake finished", 2, 3) except self.EClose: _, exc, _ = sys.exc_info() # Connection was not a WebSockets connection if exc.args[0]: self.msg("%s: %s" % (address[0], exc.args[0])) except Exception: _, exc, _ = sys.exc_info() self.msg("handler exception: %s" % str(exc)) if self.verbose: self.msg(traceback.format_exc()) finally: if self.client and self.client != self.startsock: verbose("Closing client", 0, 3) self.client.close() verbose("Client init finished", 2, 4)
def closeClient(self): if(self.uid): self.updateWorld({'action': 'logoff'}) self.running = 0 # Loop through all the subthreads and close (join) them for c in self.threads: c.join() # Delete our queue from the dictionary rwQ.acquireRead() try: rwQ.acquireWrite() try: # Add a new queue to the array for the new thread debug('Globqueue length before deleting: ' + str(len(globQueue))) del globQueue[self.queueIndex] debug('Globqueue length after deleting: ' + str(len(globQueue))) finally: rwQ.release() finally: rwQ.release() # Delete our user from the dictionary if(self.uid): rwU.acquireRead() try: rwU.acquireWrite() try: del globUser[self.uid] finally: rwU.release() finally: rwU.release() self.client.close() verbose("Client disconnected", 0, 5)
def loadMaps(self, maps): """ Load all the XML maps """ verbose("Loading maps ...", 0, 3) for fileName in maps: verbose(fileName, 2, 4) globMaps[fileName] = Maps.Map(fileName, rootFolder) verbose("Finished loading maps", 0, 2)
def __init__(self, filename, dir): self.name = filename # The filename equals the mapname self.dir = dir # Where we can find the file mapTree = ET.parse(dir + filename) # Open and parse the XML file mapRoot = mapTree.getroot() # Get the root element (<map>, in this case) # Get the attributes of the <map> element self.width = int(mapRoot.attrib['width']) self.height = int(mapRoot.attrib['height']) self.tileWidth = int(mapRoot.attrib['tilewidth']) self.tileHeight = int(mapRoot.attrib['tileheight']) # Find all <property> elements within <properties> of the root mapProperties = mapRoot.findall('properties/property') # For every property, store it for property in mapProperties: self.properties[property.attrib['name']] = property.attrib['value'] # Find all the events, which are called objects in the XML mapEvents = mapRoot.findall('objectgroup/object') for event in mapEvents: beginX = int(math.floor(int(event.attrib['x']) / self.tileWidth)) beginY = int(math.floor(int(event.attrib['y']) / self.tileHeight)) endX = beginX + int(math.floor(int(event.attrib['width']) / self.tileWidth)) endY = beginY + int(math.floor(int(event.attrib['height']) / self.tileHeight)) eventProperties = {} if 'name' in event.attrib: eventName = event.attrib['name'] else: eventName = 'unknown' try: eventAction = event.attrib['type'] eventProperties['name'] = eventName eventProperties['action'] = eventAction for props in event.findall('properties/property'): eventProperties[props.attrib['name']] = props.attrib['value'] except KeyError: print "KeyError!" continue print "Storing events in tiles" countY = beginY while countY < endY: countX = beginX while countX < endX: currentTile = ((countY + 1) * self.width) + countX + 1 if currentTile not in self.events: self.events[currentTile] = [] # Store the event in the array of this tile self.events[currentTile].append(eventProperties) countX += 1 countY += 1 # Find all the layers mapLayers = mapRoot.findall('layer') # Loop through every layer for layer in mapLayers: properties = {} # We'll store all the layer properties in here layerData = [] # Bitwise decoded decodeData will be stored here i = 0 # Iterator counter used for bitwise decoding # Decode the text of the first <data> element and create an array out of it decodeData = [ord(c) for c in layer.find('data').text.decode('base64')] while i < len(decodeData): tempTile = decodeData[i] | decodeData[i + 1] << 8 | decodeData[i + 2] << 16 | decodeData[i + 3] << 24 layerData.append(tempTile) # Append it to the layerData array i += 4 # Increase the iterator by four tempProps = layer.find('properties') if tempProps != None: tempProps = tempProps.findall('property') for property in tempProps: # Best to use the .get() than the .attrib[] one properties[property.get('name')] = property.get('value') self.layers[layer.get('name')] = { 'data': layerData, 'name': layer.get('name'), 'width': layer.get('width'), 'height': layer.get('height'), 'opacity': layer.get('opacity'), 'properties': properties } # Find all the tilesets mapTilesets = mapRoot.findall('tileset') for tileset in mapTilesets: tileSetName = tileset.get('name') firstGid = int(tileset.get('firstgid')) tileWidth = int(tileset.get('tilewidth')) tileHeight = int(tileset.get('tileheight')) tileSource = tileset.find('image').get('source') verbose("Opening image for tileSetName '" + tileSetName + "'", 3, 4) # Open the image, calculate its width and such im = Image.open(self.dir + tileSource) tilesPerRow = math.floor(im.size[0] / tileWidth) tilesPerCol = math.floor(im.size[1] / tileHeight) totalTiles = int(tilesPerCol * tilesPerRow) self.tilesets[tileSetName] = { 'name': tileSetName, 'source': tileSource, 'tileWidth': tileWidth, 'tileHeight': tileHeight, 'firstgid': firstGid, 'total': totalTiles, 'tpr': tilesPerRow, 'tpc': tilesPerCol } verbose("Start looking for tileProperties", 3, 4) # Add the new tileSet to the tileProperties list self.tileProperties[tileSetName] = {} # Loop through every tile for tile in tileset.findall('tile'): # Create a temporary list for properties tempProperties = {} # calculate the ID of this tile tileGid = firstGid + int(tile.get('id')) for position, prop in enumerate(tile.findall('properties/property')): propertyName = prop.get('name') propertyValue = prop.get('value') # We define nextframes in tiled according to their order in # THAT tileset. We don't use tilegids there because these # can change as new tilesets are added or removed. if propertyName == 'nextframe': tempProperties[propertyName] = int(propertyValue) + (firstGid - 1) else: tempProperties[propertyName] = propertyValue # Store all the properties of this tile in the tileProperties array self.tileProperties[tileSetName][tileGid] = tempProperties # Get all the walkable tiles # Calculate the total ammount of tiles totalTileAmmount = self.width * self.height # Loop through the layers for layer in self.layers: # Loop through every tile in this layer for position, tile in enumerate(self.layers[layer]['data']): # If the tilenumber is actually in the tileProperties if tile in self.tileProperties[self.getTileSetName(tile)]: # Store every property in the mapProperties list # Make sure the position is defined in the list first if position not in self.mapProperties: self.mapProperties[position] = {} for property in self.tileProperties[self.getTileSetName(tile)][tile]: self.mapProperties[position][property] = self.tileProperties[self.getTileSetName(tile)][tile][property] # These 2 are deprecated! # check if it has the "impenetrable" property if 'impenetrable' in self.tileProperties[self.getTileSetName(tile)][tile]: self.walkableTiles[position] = 0; if 'terrainSpeed' in self.tileProperties[self.getTileSetName(tile)][tile]: if position not in self.eventTiles: self.eventTiles[position] = {} self.eventTiles[position]['terrainSpeed'] = self.tileProperties[self.getTileSetName(tile)][tile]['terrainSpeed']
def decode_hybi(buf, base64=False): """ Decode HyBi style WebSocket packets. Returns: {'fin' : 0_or_1, 'opcode' : number, 'mask' : 32_bit_number, 'hlen' : header_bytes_number, 'length' : payload_bytes_number, 'payload' : decoded_buffer, 'left' : bytes_left_number, 'close_code' : number, 'close_reason' : string} """ f = {'fin' : 0, 'opcode' : 0, 'mask' : 0, 'hlen' : 2, 'length' : 0, 'payload' : None, 'left' : 0, 'close_code' : None, 'close_reason' : None} blen = len(buf) f['left'] = blen if blen < f['hlen']: return f # Incomplete frame header b1, b2 = struct.unpack_from(">BB", buf) f['opcode'] = b1 & 0x0f f['fin'] = (b1 & 0x80) >> 7 has_mask = (b2 & 0x80) >> 7 f['length'] = b2 & 0x7f if f['length'] == 126: f['hlen'] = 4 if blen < f['hlen']: return f # Incomplete frame header (f['length'],) = struct.unpack_from('>xxH', buf) elif f['length'] == 127: f['hlen'] = 10 if blen < f['hlen']: return f # Incomplete frame header (f['length'],) = struct.unpack_from('>xxQ', buf) full_len = f['hlen'] + has_mask * 4 + f['length'] if blen < full_len: # Incomplete frame return f # Incomplete frame header # Number of bytes that are part of the next frame(s) f['left'] = blen - full_len # Process 1 frame if has_mask: # unmask payload f['mask'] = buf[f['hlen']:f['hlen']+4] b = c = s2b('') if f['length'] >= 4: mask = numpy.frombuffer(buf, dtype=numpy.dtype('<u4'), offset=f['hlen'], count=1) data = numpy.frombuffer(buf, dtype=numpy.dtype('<u4'), offset=f['hlen'] + 4, count=int(f['length'] / 4)) #b = numpy.bitwise_xor(data, mask).data b = numpy.bitwise_xor(data, mask).tostring() if f['length'] % 4: #print("Partial unmask") mask = numpy.frombuffer(buf, dtype=numpy.dtype('B'), offset=f['hlen'], count=(f['length'] % 4)) data = numpy.frombuffer(buf, dtype=numpy.dtype('B'), offset=full_len - (f['length'] % 4), count=(f['length'] % 4)) c = numpy.bitwise_xor(data, mask).tostring() f['payload'] = b + c else: verbose("Unmasked frame: %s" % repr(buf), 0, 2) #f['payload'] = buf[(f['hlen'] + has_mask * 4):full_len] print buf[1:len(buf)-1] f['payload'] = buf[1:len(buf)-1] if base64 and f['opcode'] in [1, 2]: try: f['payload'] = b64decode(f['payload']) except: print("Exception while b64decoding buffer: %s" % repr(buf)) raise if f['opcode'] == 0x08: if f['length'] >= 2: f['close_code'] = struct.unpack_from(">H", f['payload']) if f['length'] > 3: f['close_reason'] = f['payload'][2:] return f
def __init__(self, serverQueue): threading.Thread.__init__(self) self.serverQueue = serverQueue self.running = True global globUser verbose('EventHandler has started', 0, 1)
def run(self): queue = self.queue error = 0 # Error counter for this thread while self.running: # Receive data data = self.wget(self.client.recv(self.size)) # If data equals false after running through wget, # close the client and skip the rest of the code (by continuing) if data == False: self.closeClient() continue try: # Try to parse the JSON data data = json.loads(data) except ValueError: verbose('This is not a JSON object: "' + data + '" -- end data;', 0, 2) error += 1 data = None if error > 50: verbose("This thread is being shut down: 50 errors", 0, 2) running = 0 # Only continue if there actually is data if data: # Initiate the user if it hasn't happened yet if self.initiated == False: if data['action'] == 'logon': verbose('Initializing user: '******'username'], 0, 3) self.loginUser(data) else: # Here we decide what to do with the information received try: for case in switch(data['action']): # Timesync: sync the time between client and server if case('timesync'): t = datetime.datetime.now() self.queue.put({'action': 'timesync', 'time': int(time.time()*1000)}) del t break # Quit: start closing the connection if case('quit'): verbose("User " + self.uid + " is quitting", 0, 2) self.closeClient() break # Move: The player has moved # {"action":"move","added":1316437492646,"x":34,"y":17,"moveRequested":1316437492646} <-- old # {"action":"move","timeRequested":1316437492646,"x":34,"y":17, "targetid": "U1"} <-- new if case('move'): # Target ID should not be used, could be spoofed. data['uid'] = self.uid isWalkable = globMaps[globUser[self.uid]['map']].isTileWalkable(int(data['x']), int(data['y'])) if isWalkable == True: del globUser[self.uid]['path'][0] globUser[self.uid]['path'].append(data) globUser[self.uid]['position']['x'] = int(data['x']) globUser[self.uid]['position']['y'] = int(data['y']) data['walkable'] = isWalkable data['terrainSpeed'] = globMaps[globUser[self.uid]['map']].getTerrainSpeed(int(data['x']), int(data['y'])) # Send the data to everyone self.updateWorld(data) # Update the globuser var if it's walkable if isWalkable: globUser[self.uid]['x'] = int(data['x']) globUser[self.uid]['y'] = int(data['y']) break # Iniuser: The client does not know a specific user, send him the required data if case('iniuser'): data = globUser[data['who']] data['action'] = 'initiation' self.queue.put(data) break # Unknown command received if case(): verbose("Unknown command received from " + self.uid, 0, 2) # No need to break here, it'll stop anyway except KeyError: # Key is not present verbose("No action key found, ignoring", 0, 3) pass
def start_server(self): """ Daemonize if requested. Listen for for connections. Run do_handshake() method for each connection. If the connection is a WebSockets client then call new_client() method (which must be overridden) for each new client connection. """ # Start loading all the maps self.loadMaps(loadMaps) # Create an event handler e = EventHandler(serverQueue) # Start it e.start() # And thread it self.threads.append(e) lsock = self.socket(self.host, self.port) while True: try: try: self.client = None startsock = None pid = err = 0 try: # Wait for connection ready = select.select([lsock], [], [], 1)[0] # Did we receive anything? if lsock in ready: # Yes, store the data here startsock, address = lsock.accept() else: # No, continue to the next itteration continue except Exception: _, exc, _ = sys.exc_info() if hasattr(exc, 'errno'): err = exc.errno elif hasattr(exc, 'args'): err = exc.args[0] else: err = exc[0] if err == errno.EINTR: self.vmsg("Ignoring interrupted syscall") continue else: raise verbose("Incoming connection: " + str(address), 0, 3) # Thread it! rwQ.acquireRead() try: rwQ.acquireWrite() try: # Add a new queue to the array for the new thread globQueue.append(Queue.Queue()) finally: rwQ.release() finally: rwQ.release() # Get the index of this new queue in the array globQueueIndex = len(globQueue) - 1 # Create a new client c = Client(startsock, address, globQueue[globQueueIndex], globQueueIndex, serverQueue) verbose("New thread started. Queueindex: " + str(globQueueIndex), 2, 3) # Start it c.start() # And thread it self.threads.append(c) except KeyboardInterrupt: _, exc, _ = sys.exc_info() print("In KeyboardInterrupt") pass except SystemExit: _, exc, _ = sys.exc_info() print("In SystemExit") break except Exception: _, exc, _ = sys.exc_info() self.msg("handler exception: %s" % str(exc)) verbose(traceback.format_exc(), 0, 1) except Exception: _, exc, _ = sys.exc_info() self.msg("handler exception: %s" % str(exc)) verbose(traceback.format_exc(), 0, 1) verbose("\r\nServer is shutting down", 0, 1) # Loop through all the threads and close (join) them for c in self.threads: verbose("Closing thread", 2, 2) c.join()
def vmsg(self, msg): """ Same as msg() but only if verbose. """ verbose(msg, 0, 1)
def msg(self, msg): """ Output message with handler_id prefix. """ verbose(msg, 0, 1)