class Helbreath: def __init__(self, Cfg): self.Config = Cfg self.AutoMSG = 0 def Login(self): self.AutoMSG = Packets.MSGID_REQUEST_LOGIN self.LSock = XSocket( { "host": self.Config["IP"], "port": self.Config["Port"], "onConnect": self.LSock_Connect, "onDisconnect": self.LSock_Disconnect, "onRead": self.LSock_Read, } ) self.LoggedIn = False self.Status = 0 self.char_list = [] self.LSock.Active = True self.LSock.join() self.Timers = TimerManager() del self.LSock return self.Status def EnterGame(self): if not self.LoggedIn: return False self.LSock = XSocket( { "host": self.Config["IP"], "port": self.Config["Port"], "onConnect": self.LSock_Connect, "onDisconnect": self.LSock_Disconnect, "onRead": self.LSock_Read, } ) self.AutoMSG = Packets.MSGID_REQUEST_ENTERGAME self.Status = False self.LSock.Active = True self.LSock.join() del self.LSock if self.Status == False: return False else: self.LSock = XSocket( { "host": self.Status[0], "port": self.Status[1], "onConnect": self.LSock_Connect, "onDisconnect": self.LSock_Disconnect, "onRead": self.LSock_Read, } ) self.AutoMSG = Packets.MSGID_REQUEST_INITPLAYER self.Status = False self.LSock.Active = True return True def run(self): ok = Acc.Login() if ok == Packets.DEF_MSGTYPE_CONFIRM: print "Logged in." if Acc.EnterGame() == False: print "Unable to log into game server." else: print "Logged into game server." else: print "Could not login." def LSock_Connect(self, sender): # global Config # print "Connected to login server at %s:%d" % (Config['IP'], Config['Port']) if self.AutoMSG == Packets.MSGID_REQUEST_LOGIN: self.LSock.Write( Packets.MSGID_REQUEST_LOGIN, 0, struct.pack( "<10s10s30s", self.Config["Account"], self.Config["Password"], self.Config["WorldServerName"] ), ) elif self.AutoMSG == Packets.MSGID_REQUEST_ENTERGAME: chr = None for c in self.char_list: if c.CharName == self.Config["CharName"]: chr = c break self.Player = chr self.LSock.Write( Packets.MSGID_REQUEST_ENTERGAME, Packets.DEF_ENTERGAMEMSGTYPE_NEW, struct.pack( "<10s10s10s10si30s120s", self.Config["CharName"], chr.MapLoc, self.Config["Account"], self.Config["Password"], chr.Level, self.Config["WorldServerName"], "", ), ) elif self.AutoMSG == Packets.MSGID_REQUEST_INITPLAYER: self.LSock.Write( Packets.MSGID_REQUEST_INITPLAYER, 0, struct.pack( "<10s10s10s?20s", self.Player.CharName, self.Config["Account"], self.Config["Password"], False, "" ), ) def LSock_Disconnect(self, sender): pass def LSock_Read(self, sender, data): self.Status = "error" while len(data) > 0: format = "<Bh" header_size = struct.calcsize(format) if len(data) < header_size: raise Exception s = struct.unpack(format, data[:header_size]) Header = namedtuple("Header", "cKey dwSize")._make(s) size = len(data) data = data[header_size:] buffer = data[: Header.dwSize - 3] data = data[Header.dwSize - 3 :] if Header.cKey > 0: Decode = lambda buffer, dwSize, cKey: "".join( map( lambda n: (lambda asdf: chr(asdf & 255))((ord(buffer[n]) ^ (cKey ^ (dwSize - n))) - (n ^ cKey)), range(len(buffer)), ) ) buffer = Decode(buffer, Header.dwSize - 3, Header.cKey) self.Status = 0 MsgID = struct.unpack("<L", buffer[:4])[0] buffer = buffer[4:] if MsgID == Packets.MSGID_RESPONSE_LOG: self.OnAccountLogin(buffer) sender.disconnect() elif MsgID == Packets.MSGID_RESPONSE_ENTERGAME: self.OnResponseEnterGame(buffer) sender.disconnect() # elif MsgID in [Packets.MSGID_REQUEST_INITDATA, Packets.MSGID_RESPONSE_INITPLAYER]: elif MsgID == Packets.MSGID_RESPONSE_INITPLAYER: self.InitPlayerResponseHandler(buffer) elif MsgID == Packets.MSGID_RESPONSE_INITDATA: # self.RequestInitDataHandler(buffer) self.InitDataResponseHandler(buffer) elif MsgID == Packets.MSGID_RESPONSE_NOTICEMENT: self.ResponseNoticementHandler(buffer) else: # if MsgID in Packets: # print "MsgID: %s" % (Packets.reverse_lookup_without_mask(MsgID)) # else: print "MsgID: 0x%08X %db" % (MsgID, len(data)) def RequestNoticement(self): try: f = open("CONTENTS\\contents1000.txt", "r") try: fs = len(f.read()) finally: f.close() except: fs = 0 self.LSock.Write(Packets.MSGID_REQUEST_NOTICEMENT, 0, struct.pack("<L", fs)) def ResponseNoticementHandler(self, buffer): MsgType = struct.unpack("<h", buffer[:2])[0] buffer = buffer[2:] if MsgType == Packets.DEF_MSGTYPE_CONFIRM: pass elif MsgType == Packets.DEF_MSGTYPE_REJECT: print buffer f = open("CONTENTS\\contents1000.txt", "w") try: f.write(buffer) finally: f.close() def InitDataResponseHandler(self, buffer): print "Data response: %d " % len(buffer) self.RequestNoticement() self.Timers.register_timer(self.__checkconnection, "check_conn", 8.0, True) def __checkconnection(self): t = int(time.time()) self.LSock.Write(Packets.MSGID_COMMAND_CHECKCONNECTION, 0, struct.pack("<L", t)) def InitPlayerResponseHandler(self, buffer): MsgType = struct.unpack("<h", buffer[:2])[0] if MsgType == Packets.DEF_MSGTYPE_CONFIRM: self.LSock.Write( Packets.MSGID_REQUEST_INITDATA, 0, struct.pack( "<10s10s10s?20s", self.Player.CharName, self.Config["Account"], self.Config["Password"], False, "" ), ) elif MsgType == Packets.DEF_MSGTYPE_REJECT: print "Reject in init data handler" self.LSock.disconect() def OnResponseEnterGame(self, buffer): global packet_format MsgType = struct.unpack("<h", buffer[:2])[0] buffer = buffer[2:] if MsgType == Packets.DEF_ENTERGAMERESTYPE_REJECT: self.Status = False return elif MsgType == Packets.DEF_ENTERGAMERESTYPE_CONFIRM: self.Status = True fmt = "<16sh" fmt_s = struct.calcsize(fmt) self.Status = map(packet_format, struct.unpack(fmt, buffer[:fmt_s])) return else: print Packets.reverse_lookup_without_mask(MsgType) self.Status = False def OnAccountLogin(self, buffer): MsgType = struct.unpack("<h", buffer[:2])[0] buffer = buffer[2:] self.Status = MsgType if MsgType == Packets.DEF_MSGTYPE_CONFIRM: print "Account %s login ok... Retrieving character list." % self.Config["Account"] account_fmt = "<2H13xb" # 12x -> dates -> can skip account_size = struct.calcsize(account_fmt) account = struct.unpack(account_fmt, buffer[:account_size]) buffer = buffer[account_size:] account = namedtuple("Account", "LowerVersion UpperVersion CharCount")._make(account) char_format = "<10sx12xhi6h4x10x10s" char_size = struct.calcsize(char_format) character = namedtuple("Character", "CharName Level Exp Str Vit Dex Int Mag Chr MapLoc") char_list = [] for c in range(account.CharCount): tmp = buffer[:char_size] buffer = buffer[char_size:] tmp = map(packet_format, struct.unpack(char_format, tmp)) char_list += [character._make(tmp)] self.char_list = char_list self.LoggedIn = True return True elif MsgType == Packets.DEF_LOGRESMSGTYPE_REJECT: print "Account %s blocked!" % self.Config["Account"] return False elif MsgType == Packets.DEF_LOGRESMSGTYPE_PASSWORDMISMATCH: print "Account %s password mismatch!" % self.Config["Password"] return False elif MsgType == Packets.DEF_LOGRESMSGTYPE_NOTEXISTINGACCOUNT: print "Account %s does not exists!" % self.Config["Account"] return False elif MsgType == Packets.DEF_LOGRESMSGTYPE_NOTEXISTINGWORLDSERVER: print "Account %s: World server is not activated" % (self.Config["Account"]) return False else: print Packets.reverse_lookup_without_mask(MsgType) return False
class Server(object): ''' New game server 2011 Drajwer ''' def __init__(self): self.server_name = '' self.maps = [] self.clients = [] self.logsockets = [] self.serversocket = None self.noticement = '' self.event_thread_alive = False self.event_thread = False # Guards... self.clients_guard = Semaphore() def stop_and_cleanup(self): # Called when someone interrupts main thread self.event_thread_alive = False self.event_thread.join() def read_config(self): # JSON configuration reader import json, os from HBMap import HBMap config = json.load(open('settings.cfg')) self.server_name = str(config['CONFIG']['game-server-name']) self.address = str(config['CONFIG']['game-server-address']) self.port = int(config['CONFIG']['game-server-port']) self.log_server_address = str(config['CONFIG']['log-server-address']) self.gate_server_port = int(config['CONFIG']['gate-server-port']) self.maps = {} for map_name, options in config['MAPS'].items(): map_name = str(map_name) print 'Initializing map %s...' % (map_name, ) file_name = os.path.join(os.path.dirname(__file__), 'MAPDATA', options['amd']) if not os.path.exists(file_name): print 'Failure. Map %s does not exists.' % (map_name, ) continue try: amd = HBMap(file_name) except Exception as e: print 'Failure. %s.' % (e, ) continue print 'OK! SIZE(%d, %d)' % (amd.MAPSIZEX, amd.MAPSIZEY) self.maps[map_name] = config['MAPS'][map_name] self.maps[map_name]['amd'] = amd return True def initialize(self): if not self.read_config(): return False for i in range(5): socket = GateSocket(address=self.log_server_address, port=self.gate_server_port) self.setup_callbacks_gate(socket) self.logsockets += [socket] self.logsockets[0].connect() self.serversocket = ServerSocket((self.address, self.port), socketcls=ClientSocket) self.serversocket.on_listen = self.on_gameserver_listen self.serversocket.open_socket() return True def loop(self): # TODO: wrap around all clients_guard dependent code rinput = filter(lambda sock: sock.connected, self.logsockets) self.clients_guard.acquire() rinput += self.clients self.clients_guard.release() rinput += [self.serversocket] winput = filter(lambda sock: sock.connecting or sock.write_buffer, self.logsockets) winput += filter(lambda sock: sock.write_buffer, self.clients) (rlist, wlist, elist) = select.select(rinput, winput, [], 0.5) for socket in rlist: if socket in self.logsockets: n = socket.recv() if not n: socket.on_disconnect(socket) socket.close() continue while socket.pop_packet(): # Why would we even disconnect gate socket? # Just do nothing pass if socket in self.clients: n = socket.recv() if not n: self.delete_client(socket, cleanup_only=True) continue while True: try: if not socket.pop_packet(): break except: traceback.print_exc() print self.delete_client(socket, cleanup_only=True) if socket == self.serversocket: c = self.serversocket.accept(socketcls=ClientSocket) c.setblocking(False) self.clients_guard.acquire() try: c.id = max(map(lambda client: client.id, self.clients)) + 1 except ValueError as e: c.id = 0 self.clients.append(c) self.clients_guard.release() self.setup_callbacks_client(c) c.on_connect(c) for socket in wlist: if socket.connecting: socket.connecting = False socket.connected = True socket.on_connect(socket) n = socket.flush() def event_loop(self): # Event thread main entry function self.timers.process_all() def timer_connected_players(self): self.clients_guard.acquire() print '(O) Players connected:', len(self.clients) self.clients_guard.release() def on_server_registered(self): self.timers = TimerManager() self.timers.register_timer(None, self.timer_connected_players, 1.0) def thread_ep(server_instance): # Thread entry point server_instance.event_thread_alive = True while server_instance.event_thread_alive: server_instance.event_loop() time.sleep(0.1) self.event_thread = Thread(target=thread_ep, args=(self, )) self.event_thread.start() ''' Socket events ''' def on_logsocket_connected(self, logsocket): ''' Request game server registration ''' #How many connected sockets we have? connected = len(filter(lambda _: _.connected, self.logsockets)) if connected > 1: print 'Trying to register game server socket' logsocket.do_register_gameserversocket(gsid=self.gsid) else: print 'Trying to register game server %s...' % self.server_name logsocket.do_register_gameserver(server_name=self.server_name, address=self.address, port=self.port, maps=self.maps.keys()) def on_logsocket_connection_lost(self, logsocket): ''' Fired when gate server socket lost connection ''' print 'Lost connection to gate server on socket-%d' % self.logsockets.index( logsocket) if not filter(lambda _: _.connected, self.logsockets): print 'Lost connection to gate server!' def on_gameserver_listen(self): print 'Game server socket is working!' def on_client_connect(self, client): print 'Client connected!' def on_client_disconnect(self, client): print 'Client disconnected!' ''' Communication ''' def getlogsocket(self): # TODO : Filter only connected logsockets? return random.choice(self.logsockets) ''' Functions ''' def setup_callbacks_gate(self, logsocket): ''' Setup callbacks on gate socket ''' logsocket.on_response_registergameserver = self.on_response_registergameserver logsocket.on_connect = self.on_logsocket_connected logsocket.on_disconnect = self.on_logsocket_connection_lost logsocket.on_response_playerdata = self.on_response_playerdata # Configs logsocket.on_receive_config_noticement = self.on_receive_config_noticement def setup_callbacks_client(self, client): ''' Setup callbacks on client socket ''' client.on_connect = self.on_client_connect client.on_disconnect = self.on_client_disconnect client.on_request_noticement = self.client_on_request_noticement client.on_request_initplayer = self.client_on_request_initplayer client.on_request_initdata = self.client_on_request_initdata client.on_request_fullobjectdata = self.client_on_request_fullobjectdata def delete_client(self, client, cleanup_only=False): # TODO: options etc if cleanup_only: client.on_disconnect(client) client.close() self.clients_guard.acquire() self.clients.remove(client) self.clients_guard.release() return client.close() if client in self.clients: self.clients.remove(client) ''' Gate server handlers ''' def on_receive_config_noticement(self, noticement): self.noticement = noticement.rstrip() def on_response_registergameserver(self, success, gsid=None): if not success: print 'Game server %s registration rejected!' % self.server_name return self.gsid = gsid print 'Game server %s registration success! (GSID:%r)' % ( self.server_name, gsid) for i, socket in enumerate(self.logsockets): if not socket.connected: print 'Connecting gate server socket-%d!' % (i, ) socket.connect() self.on_server_registered() def on_response_playerdata(self, char_name, player_data): try: client, = filter(lambda _: _.char_name, self.clients) except ValueError as e: print '(O) Got unknown player data %r' % char_name return if not player_data: print '(HACK?) Not existing character(%s) data request! Rejected!' % ( char_name, ) self.delete_client(client) return if player_data.map_name not in self.maps: print 'Player stuck on not existing map "%s" !' % ( player_data.map_name, ) self.delete_client(client) return maploc = self.maps[player_data.map_name] if (player_data.x, player_data.y) not in maploc: player_data.x, player_data.y = random.choice( maploc['initial-points']) print '%s new initial point is', player_data.x, player_data.y client.player_data = player_data client.map = maploc client.do_response_initplayer(success=True) # TODO : when you call it with success = False client will get # 'World server full try other server'. Player limit could be # implemented. self.getlogsocket().do_entergame_confirm( account_name=client.account_name, account_password=client.account_password, server_name=self.server_name, address=client.address, level=player_data.level) print '(TestLog) Enter Game Confirm Level: %d' % player_data.level ''' Client socket handlers ''' def client_on_request_initplayer(self, char_name, account_name, account_password, is_observer_mode, client): print 'Request init player' print char_name, account_name, account_password, is_observer_mode client.char_name = char_name client.account_name = account_name client.account_password = account_password self.getlogsocket().do_request_playerdata( char_name=char_name, account_name=account_name, account_password=account_password, address=client.address) def client_on_request_initdata(self, char_name, account_name, account_password, client): if client.char_name != char_name: print '(!) Error!', self.char_name, '!=', char_name self.delete_client(client) return client.do_playercharactercontents() client.do_response_initdata() def client_on_request_noticement(self, client, file_size): # Ignore client size of noticement data stored on disk and always # send new noticement. client.send_noticement(self.noticement) def client_on_request_fullobjectdata(self, client, object_id): print 'request fullobjectdata req:%d client:%d' % (object_id, client.id) try: obj, = filter(lambda _: _.id == object_id, self.clients) except ValueError as e: print '(!) Client requests full data from unknown object.' self.delete_client(client) return self.do_event_motion('stop', obj)
class Helbreath(): def __init__(self, Cfg): self.Config = Cfg self.AutoMSG = 0 def Login(self): self.AutoMSG = Packets.MSGID_REQUEST_LOGIN self.LSock = XSocket({ 'host': self.Config['IP'], 'port': self.Config['Port'], 'onConnect': self.LSock_Connect, 'onDisconnect': self.LSock_Disconnect, 'onRead': self.LSock_Read }) self.LoggedIn = False self.Status = 0 self.char_list = [] self.LSock.Active = True self.LSock.join() self.Timers = TimerManager() del self.LSock return self.Status def EnterGame(self): if not self.LoggedIn: return False self.LSock = XSocket({ 'host': self.Config['IP'], 'port': self.Config['Port'], 'onConnect': self.LSock_Connect, 'onDisconnect': self.LSock_Disconnect, 'onRead': self.LSock_Read }) self.AutoMSG = Packets.MSGID_REQUEST_ENTERGAME self.Status = False self.LSock.Active = True self.LSock.join() del self.LSock if self.Status == False: return False else: self.LSock = XSocket({ 'host': self.Status[0], 'port': self.Status[1], 'onConnect': self.LSock_Connect, 'onDisconnect': self.LSock_Disconnect, 'onRead': self.LSock_Read }) self.AutoMSG = Packets.MSGID_REQUEST_INITPLAYER self.Status = False self.LSock.Active = True return True def run(self): ok = Acc.Login() if ok == Packets.DEF_MSGTYPE_CONFIRM: print "Logged in." if Acc.EnterGame() == False: print "Unable to log into game server." else: print "Logged into game server." else: print "Could not login." def LSock_Connect(self, sender): #global Config #print "Connected to login server at %s:%d" % (Config['IP'], Config['Port']) if self.AutoMSG == Packets.MSGID_REQUEST_LOGIN: self.LSock.Write( Packets.MSGID_REQUEST_LOGIN, 0, struct.pack('<10s10s30s', self.Config['Account'], self.Config['Password'], self.Config['WorldServerName'])) elif self.AutoMSG == Packets.MSGID_REQUEST_ENTERGAME: chr = None for c in self.char_list: if c.CharName == self.Config['CharName']: chr = c break self.Player = chr self.LSock.Write( Packets.MSGID_REQUEST_ENTERGAME, Packets.DEF_ENTERGAMEMSGTYPE_NEW, struct.pack('<10s10s10s10si30s120s', self.Config['CharName'], chr.MapLoc, self.Config['Account'], self.Config['Password'], chr.Level, self.Config['WorldServerName'], "")) elif self.AutoMSG == Packets.MSGID_REQUEST_INITPLAYER: self.LSock.Write( Packets.MSGID_REQUEST_INITPLAYER, 0, struct.pack('<10s10s10s?20s', self.Player.CharName, self.Config['Account'], self.Config['Password'], False, "")) def LSock_Disconnect(self, sender): pass def LSock_Read(self, sender, data): self.Status = "error" while len(data) > 0: format = '<Bh' header_size = struct.calcsize(format) if len(data) < header_size: raise Exception s = struct.unpack(format, data[:header_size]) Header = namedtuple('Header', 'cKey dwSize')._make(s) size = len(data) data = data[header_size:] buffer = data[:Header.dwSize - 3] data = data[Header.dwSize - 3:] if Header.cKey > 0: Decode = lambda buffer, dwSize, cKey: "".join( map( lambda n: (lambda asdf: chr(asdf & 255)) ((ord(buffer[n]) ^ (cKey ^ (dwSize - n))) - (n ^ cKey)), range(len(buffer)))) buffer = Decode(buffer, Header.dwSize - 3, Header.cKey) self.Status = 0 MsgID = struct.unpack('<L', buffer[:4])[0] buffer = buffer[4:] if MsgID == Packets.MSGID_RESPONSE_LOG: self.OnAccountLogin(buffer) sender.disconnect() elif MsgID == Packets.MSGID_RESPONSE_ENTERGAME: self.OnResponseEnterGame(buffer) sender.disconnect() #elif MsgID in [Packets.MSGID_REQUEST_INITDATA, Packets.MSGID_RESPONSE_INITPLAYER]: elif MsgID == Packets.MSGID_RESPONSE_INITPLAYER: self.InitPlayerResponseHandler(buffer) elif MsgID == Packets.MSGID_RESPONSE_INITDATA: #self.RequestInitDataHandler(buffer) self.InitDataResponseHandler(buffer) elif MsgID == Packets.MSGID_RESPONSE_NOTICEMENT: self.ResponseNoticementHandler(buffer) else: #if MsgID in Packets: # print "MsgID: %s" % (Packets.reverse_lookup_without_mask(MsgID)) #else: print "MsgID: 0x%08X %db" % (MsgID, len(data)) def RequestNoticement(self): try: f = open("CONTENTS\\contents1000.txt", "r") try: fs = len(f.read()) finally: f.close() except: fs = 0 self.LSock.Write(Packets.MSGID_REQUEST_NOTICEMENT, 0, struct.pack('<L', fs)) def ResponseNoticementHandler(self, buffer): MsgType = struct.unpack('<h', buffer[:2])[0] buffer = buffer[2:] if MsgType == Packets.DEF_MSGTYPE_CONFIRM: pass elif MsgType == Packets.DEF_MSGTYPE_REJECT: print buffer f = open("CONTENTS\\contents1000.txt", "w") try: f.write(buffer) finally: f.close() def InitDataResponseHandler(self, buffer): print "Data response: %d " % len(buffer) self.RequestNoticement() self.Timers.register_timer(self.__checkconnection, 'check_conn', 8.0, True) def __checkconnection(self): t = int(time.time()) self.LSock.Write(Packets.MSGID_COMMAND_CHECKCONNECTION, 0, struct.pack('<L', t)) def InitPlayerResponseHandler(self, buffer): MsgType = struct.unpack('<h', buffer[:2])[0] if MsgType == Packets.DEF_MSGTYPE_CONFIRM: self.LSock.Write( Packets.MSGID_REQUEST_INITDATA, 0, struct.pack('<10s10s10s?20s', self.Player.CharName, self.Config['Account'], self.Config['Password'], False, "")) elif MsgType == Packets.DEF_MSGTYPE_REJECT: print "Reject in init data handler" self.LSock.disconect() def OnResponseEnterGame(self, buffer): global packet_format MsgType = struct.unpack('<h', buffer[:2])[0] buffer = buffer[2:] if MsgType == Packets.DEF_ENTERGAMERESTYPE_REJECT: self.Status = False return elif MsgType == Packets.DEF_ENTERGAMERESTYPE_CONFIRM: self.Status = True fmt = "<16sh" fmt_s = struct.calcsize(fmt) self.Status = map(packet_format, struct.unpack(fmt, buffer[:fmt_s])) return else: print Packets.reverse_lookup_without_mask(MsgType) self.Status = False def OnAccountLogin(self, buffer): MsgType = struct.unpack('<h', buffer[:2])[0] buffer = buffer[2:] self.Status = MsgType if MsgType == Packets.DEF_MSGTYPE_CONFIRM: print "Account %s login ok... Retrieving character list." % self.Config[ 'Account'] account_fmt = "<2H13xb" #12x -> dates -> can skip account_size = struct.calcsize(account_fmt) account = struct.unpack(account_fmt, buffer[:account_size]) buffer = buffer[account_size:] account = namedtuple( 'Account', 'LowerVersion UpperVersion CharCount')._make(account) char_format = "<10sx12xhi6h4x10x10s" char_size = struct.calcsize(char_format) character = namedtuple( 'Character', 'CharName Level Exp Str Vit Dex Int Mag Chr MapLoc') char_list = [] for c in range(account.CharCount): tmp = buffer[:char_size] buffer = buffer[char_size:] tmp = map(packet_format, struct.unpack(char_format, tmp)) char_list += [character._make(tmp)] self.char_list = char_list self.LoggedIn = True return True elif MsgType == Packets.DEF_LOGRESMSGTYPE_REJECT: print "Account %s blocked!" % self.Config['Account'] return False elif MsgType == Packets.DEF_LOGRESMSGTYPE_PASSWORDMISMATCH: print "Account %s password mismatch!" % self.Config['Password'] return False elif MsgType == Packets.DEF_LOGRESMSGTYPE_NOTEXISTINGACCOUNT: print "Account %s does not exists!" % self.Config['Account'] return False elif MsgType == Packets.DEF_LOGRESMSGTYPE_NOTEXISTINGWORLDSERVER: print "Account %s: World server is not activated" % ( self.Config['Account']) return False else: print Packets.reverse_lookup_without_mask(MsgType) return False
class Server(object): ''' New game server 2011 Drajwer ''' def __init__(self): self.server_name = '' self.maps = [] self.clients = [] self.logsockets = [] self.serversocket = None self.noticement = '' self.event_thread_alive = False self.event_thread = False # Guards... self.clients_guard = Semaphore() def stop_and_cleanup(self): # Called when someone interrupts main thread self.event_thread_alive = False self.event_thread.join() def read_config(self): # JSON configuration reader import json, os from HBMap import HBMap config = json.load(open('settings.cfg')) self.server_name = str(config['CONFIG']['game-server-name']) self.address = str(config['CONFIG']['game-server-address']) self.port = int(config['CONFIG']['game-server-port']) self.log_server_address = str(config['CONFIG']['log-server-address']) self.gate_server_port = int(config['CONFIG']['gate-server-port']) self.maps = {} for map_name, options in config['MAPS'].items(): map_name = str(map_name) print 'Initializing map %s...' % (map_name, ) file_name = os.path.join(os.path.dirname(__file__), 'MAPDATA', options['amd']) if not os.path.exists(file_name): print 'Failure. Map %s does not exists.' % (map_name, ) continue try: amd = HBMap(file_name) except Exception as e: print 'Failure. %s.' % (e, ) continue print 'OK! SIZE(%d, %d)' % (amd.MAPSIZEX, amd.MAPSIZEY) self.maps[map_name] = config['MAPS'][map_name] self.maps[map_name]['amd'] = amd return True def initialize(self): if not self.read_config(): return False for i in range(5): socket = GateSocket( address = self.log_server_address, port = self.gate_server_port ) self.setup_callbacks_gate(socket) self.logsockets += [socket] self.logsockets[0].connect() self.serversocket = ServerSocket( (self.address, self.port), socketcls = ClientSocket ) self.serversocket.on_listen = self.on_gameserver_listen self.serversocket.open_socket() return True def loop(self): # TODO: wrap around all clients_guard dependent code rinput = filter(lambda sock: sock.connected, self.logsockets) self.clients_guard.acquire() rinput += self.clients self.clients_guard.release() rinput += [self.serversocket] winput = filter(lambda sock: sock.connecting or sock.write_buffer, self.logsockets) winput += filter(lambda sock: sock.write_buffer, self.clients) (rlist, wlist, elist) = select.select(rinput, winput, [], 0.5) for socket in rlist: if socket in self.logsockets: n = socket.recv() if not n: socket.on_disconnect(socket) socket.close() continue while socket.pop_packet(): # Why would we even disconnect gate socket? # Just do nothing pass if socket in self.clients: n = socket.recv() if not n: self.delete_client(socket, cleanup_only = True) continue while True: try: if not socket.pop_packet(): break except: traceback.print_exc() print self.delete_client(socket, cleanup_only = True) if socket == self.serversocket: c = self.serversocket.accept(socketcls = ClientSocket) c.setblocking(False) self.clients_guard.acquire() try: c.id = max(map(lambda client: client.id, self.clients)) + 1 except ValueError as e: c.id = 0 self.clients.append(c) self.clients_guard.release() self.setup_callbacks_client(c) c.on_connect(c) for socket in wlist: if socket.connecting: socket.connecting = False socket.connected = True socket.on_connect(socket) n = socket.flush() def event_loop(self): # Event thread main entry function self.timers.process_all() def timer_connected_players(self): self.clients_guard.acquire() print '(O) Players connected:', len(self.clients) self.clients_guard.release() def on_server_registered(self): self.timers = TimerManager() self.timers.register_timer(None, self.timer_connected_players, 1.0) def thread_ep(server_instance): # Thread entry point server_instance.event_thread_alive = True while server_instance.event_thread_alive: server_instance.event_loop() time.sleep(0.1) self.event_thread = Thread(target = thread_ep, args = (self, )) self.event_thread.start() ''' Socket events ''' def on_logsocket_connected(self, logsocket): ''' Request game server registration ''' #How many connected sockets we have? connected = len(filter(lambda _: _.connected, self.logsockets)) if connected > 1: print 'Trying to register game server socket' logsocket.do_register_gameserversocket( gsid = self.gsid ) else: print 'Trying to register game server %s...' % self.server_name logsocket.do_register_gameserver( server_name = self.server_name, address = self.address, port = self.port, maps = self.maps.keys() ) def on_logsocket_connection_lost(self, logsocket): ''' Fired when gate server socket lost connection ''' print 'Lost connection to gate server on socket-%d' % self.logsockets.index(logsocket) if not filter(lambda _: _.connected, self.logsockets): print 'Lost connection to gate server!' def on_gameserver_listen(self): print 'Game server socket is working!' def on_client_connect(self, client): print 'Client connected!' def on_client_disconnect(self, client): print 'Client disconnected!' ''' Communication ''' def getlogsocket(self): # TODO : Filter only connected logsockets? return random.choice(self.logsockets) ''' Functions ''' def setup_callbacks_gate(self, logsocket): ''' Setup callbacks on gate socket ''' logsocket.on_response_registergameserver = self.on_response_registergameserver logsocket.on_connect = self.on_logsocket_connected logsocket.on_disconnect = self.on_logsocket_connection_lost logsocket.on_response_playerdata = self.on_response_playerdata # Configs logsocket.on_receive_config_noticement = self.on_receive_config_noticement def setup_callbacks_client(self, client): ''' Setup callbacks on client socket ''' client.on_connect = self.on_client_connect client.on_disconnect = self.on_client_disconnect client.on_request_noticement = self.client_on_request_noticement client.on_request_initplayer = self.client_on_request_initplayer client.on_request_initdata = self.client_on_request_initdata client.on_request_fullobjectdata = self.client_on_request_fullobjectdata def delete_client(self, client, cleanup_only = False): # TODO: options etc if cleanup_only: client.on_disconnect(client) client.close() self.clients_guard.acquire() self.clients.remove(client) self.clients_guard.release() return client.close() if client in self.clients: self.clients.remove(client) ''' Gate server handlers ''' def on_receive_config_noticement(self, noticement): self.noticement = noticement.rstrip() def on_response_registergameserver(self, success, gsid = None): if not success: print 'Game server %s registration rejected!' % self.server_name return self.gsid = gsid print 'Game server %s registration success! (GSID:%r)' % (self.server_name, gsid) for i, socket in enumerate(self.logsockets): if not socket.connected: print 'Connecting gate server socket-%d!' % (i, ) socket.connect() self.on_server_registered() def on_response_playerdata(self, char_name, player_data): try: client, = filter(lambda _: _.char_name, self.clients) except ValueError as e: print '(O) Got unknown player data %r' % char_name return if not player_data: print '(HACK?) Not existing character(%s) data request! Rejected!' % (char_name, ) self.delete_client(client) return if player_data.map_name not in self.maps: print 'Player stuck on not existing map "%s" !' % (player_data.map_name, ) self.delete_client(client) return maploc = self.maps[player_data.map_name] if (player_data.x, player_data.y) not in maploc: player_data.x, player_data.y = random.choice(maploc['initial-points']) print '%s new initial point is', player_data.x, player_data.y client.player_data = player_data client.map = maploc client.do_response_initplayer(success = True) # TODO : when you call it with success = False client will get # 'World server full try other server'. Player limit could be # implemented. self.getlogsocket().do_entergame_confirm( account_name = client.account_name, account_password = client.account_password, server_name = self.server_name, address = client.address, level = player_data.level ) print '(TestLog) Enter Game Confirm Level: %d' % player_data.level ''' Client socket handlers ''' def client_on_request_initplayer(self, char_name, account_name, account_password, is_observer_mode, client): print 'Request init player' print char_name, account_name, account_password, is_observer_mode client.char_name = char_name client.account_name = account_name client.account_password = account_password self.getlogsocket().do_request_playerdata( char_name = char_name, account_name = account_name, account_password = account_password, address = client.address ) def client_on_request_initdata(self, char_name, account_name, account_password, client): if client.char_name != char_name: print '(!) Error!', self.char_name, '!=', char_name self.delete_client(client) return client.do_playercharactercontents() client.do_response_initdata() def client_on_request_noticement(self, client, file_size): # Ignore client size of noticement data stored on disk and always # send new noticement. client.send_noticement(self.noticement) def client_on_request_fullobjectdata(self, client, object_id): print 'request fullobjectdata req:%d client:%d' % (object_id, client.id) try: obj, = filter(lambda _: _.id == object_id, self.clients) except ValueError as e: print '(!) Client requests full data from unknown object.' self.delete_client(client) return self.do_event_motion('stop', obj)