Skip to content

Commit

Permalink
GameSocket early state, introduced GSC and CGS constants
Browse files Browse the repository at this point in the history
  • Loading branch information
keabard committed Mar 16, 2012
1 parent bfc854f commit 599f799
Show file tree
Hide file tree
Showing 5 changed files with 143 additions and 10 deletions.
52 changes: 52 additions & 0 deletions lib/honcore/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,58 @@ def _chat_connect(self):
attempts += 1
raise ChatServerError(200) # Server did not respond to the authentication request

def _chat_disconnect(self):
""" Disconnect gracefully from the chat server and close & remove the socket."""
if self.__chat_socket is not None:
self.__chat_socket.connected = False # Safer to stop the thread with this first.
try:
self.__chat_socket.socket.shutdown(socket.SHUT_RDWR)
self.__chat_socket.socket.close()
except socket.error:
raise ChatServerError(209)

""" Gameserver related functions"""
def _game_connect(self):
""" Sends the initial authentication request to a gameserver via the game socket object.
Ensures the user information required for authentication is available, otherwise raises
a GameServerError #205 (No session key/auth hash provided)
If for some reason a GameSocket does not exist then one is created.
Connects that game socket to the correct address and port. Any exceptions are raised to the top method.
Finally sends a valid authentication packet. Any exceptions are raised to the top method.
"""
if self.account == None or self.account.cookie == None or self.account.game_session_key == None or self.account.auth_hash == None:
raise GameServerError(205)

if self.__game_socket is None:
self.__game_socket = GameSocket()
try:
self.__game_socket.connect(self.account.game_ip, self.account.game_port) # Basic connection to the socket
except HoNCoreError as e:
if e.code == 10: # Socket error.
raise GameServerError(208) # Could not connect to the game server.
elif e.code == 11: # Socket timed out.
raise GameServerError(201)

# Send initial authentication request to the game server.
try:
self.__game_socket.send_auth_info(self.account.account_id, self.account.cookie, self.account.ip, self.account.auth_hash, self.config['protocol'], self.config['invis'])
except ChatServerError:
raise # Re-raise the exception.

# The idea is to give 10 seconds for the chat server to respond to the authentication request.
# If it is accepted, then the `is_authenticated` flag will be set to true.
# NOTE: Lag will make this sort of iffy....
attempts = 1
while attempts is not 10:
if self.__chat_socket.is_authenticated:
return True
else:
time.sleep(1)
attempts += 1
raise ChatServerError(200) # Server did not respond to the authentication request

def _chat_disconnect(self):
""" Disconnect gracefully from the chat server and close & remove the socket."""
if self.__chat_socket is not None:
Expand Down
4 changes: 4 additions & 0 deletions lib/honcore/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,17 @@ def __init__(self, super_id, account_id, nickname, cookie, auth_hash, chat_url,
self.nickname = nickname
self.cookie = cookie
self.auth_hash = auth_hash
self.game_session_key = None
self.game_ip = None
self.game_port = None
self.chat_url = chat_url
self.ip = ip
self.buddy_list = {}
self.clan_member_list = {}
self.ban_list = {}
self.ignore_list = {}
self.logged_in = False
self.in_game = False

def __repr__(self):
return "<User Account: #%s %s>" % (self.account_id, self.nickname)
Expand Down
8 changes: 8 additions & 0 deletions lib/honcore/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@

HON_SC_GAME_INVITE = 0x25

""" GameServer -> Client """

HON_GSC_PACKET_RECV = "HON_GSC_PACKET_RECV"

""" Client -> Server """
HON_CS_PONG = 0x2A01
HON_CS_CHANNEL_MSG = 0x03
Expand Down Expand Up @@ -99,6 +103,10 @@
HON_CS_GAME_SERVER_IP = 0xf00
HON_CS_GAME_SERVER_INFO = 0x1000

""" Client -> GameServer """

HON_CGS_PONG = 0

# Dummy Events / Custom events?
HON_SC_PACKET_RECV = "HON_SC_PACKET_RECV"

Expand Down
9 changes: 8 additions & 1 deletion lib/honcore/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,14 @@ class MasterServerError(HoNException):
pass

class ChatServerError(HoNException):
""" Ecxceptions related to the chat server.
""" Exceptions related to the chat server.
Can be raised if invalid data is received or if the socket times out and the
connection to the server is lost.
"""
pass

class GameServerError(HoNException):
""" Exceptions related to a game server.
Can be raised if invalid data is received or if the socket times out and the
connection to the server is lost.
"""
Expand Down
80 changes: 71 additions & 9 deletions lib/honcore/networking.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,25 @@ class SocketListener(threading.Thread):
Receives the packet and in an addition thread parses the packet
and triggers any event handlers.
"""
def __init__(self, chat_socket):
def __init__(self, socket):
threading.Thread.__init__(self, name='SocketListener')
self.chat_socket = chat_socket
self.socket = socket
self.stopped = False

def __repr__(self):
return "<SocketListener on socket %s>" % self.chat_socket.socket
return "<SocketListener on socket %s>" % self.socket.socket

def run(self):
while not self.stopped:
try:
packet = self.chat_socket.recv()
packet = self.socket.recv()
if not packet:
#print "Empty packet received, socket terminated."
self.stopped = True
break
#print "Packet 0x%x on socket %s" % (struct.unpack('H', packet[2:4])[0], self.chat_socket.socket)
threading.Thread(target=self.chat_socket.parse_packet, name='PacketParser', args=(packet,)).start()
#self.chat_socket.parse_packet(packet)
#print "Packet 0x%x on socket %s" % (struct.unpack('H', packet[2:4])[0], self.socket.socket)
threading.Thread(target=self.socket.parse_packet, name='PacketParser', args=(packet,)).start()
#self.socket.parse_packet(packet)
except socket.timeout, e:
#print "Socket.timeout: %s" % e
continue
Expand Down Expand Up @@ -801,7 +801,7 @@ def __init__(self, client_events):
self.socket = None
self.connected = False
self.listener = None
self.packet_parser = PacketParser()
self.packet_parser = GamePacketParser()
self.events = client_events

# Transparently connect the ping event to the pong sender.
Expand Down Expand Up @@ -904,7 +904,7 @@ def parse_packet(self, packet):

# Trigger a general event on all packets. Passes the raw packet.
try:
self.events[HON_SC_PACKET_RECV].trigger(**{'packet_id': packet_id, 'packet': packet})
self.events[HON_GSC_PACKET_RECV].trigger(**{'packet_id': packet_id, 'packet': packet})
except KeyError:
pass

Expand Down Expand Up @@ -938,4 +938,66 @@ def send_game_message(self, message):
packet = c.build(Container(id=HON_CS_CHANNEL_MSG, message=unicode(message)))
self.send(packet)

class GamePacketParser:
""" A class to handle raw packet parsing. """
def __init__(self):
self.__packet_parsers = {}
self.__setup_parsers()

def __setup_parsers(self):
""" Add every known packet parser to the list of availble parsers. """
self.__add_parser(HON_SC_PING, self.parse_ping)
self.__add_parser(HON_SC_CHANNEL_MSG, self.parse_channel_message)

def __add_parser(self, packet_id, function):
""" Registers a parser function for the specified packet.
Ensures that only one parser exists for each packet.
"""
if packet_id in self.__packet_parsers:
return False
self.__packet_parsers[packet_id] = function

def parse_id(self, packet):
""" Returns the packet's ID.
The ID is an unsigned short, or a 2 byte integer, which is located at bytes 3 and 4
within the packet.
"""
return struct.unpack('H', packet[2:4])[0]

def parse_data(self, packet_id, packet):
""" Pushes the packet through to a matching registered packet parser, which extracts any useful data
into a dict of kwargs which can then be handed to any matching registered event handler.
Passes the packet to a packet parser so it can be parsed for data. The returned data
is then passed to each event handler that requests it as a list of named keywords which
are taken as arguments.
"""
if packet_id in self.__packet_parsers:
parser = self.__packet_parsers[packet_id]
data = parser(packet)
return data
else:
raise HoNCoreError(12) # Unknown packet received.

def parse_ping(self, packet):
""" Pings sent every minute. Respond with pong.
Packet ID:
"""
return {}

def parse_game_message(self, packet):
""" Triggered when a message is sent to the game lobby.
Returns the following:
`message` The message sent.
Packet ID:
"""
c = Struct('channel_message',
ULInt32('account_id'),
ULInt32('channel_id'),
CString('message')
)
r = c.parse(packet)
return {
'message' : r.message
}

0 comments on commit 599f799

Please sign in to comment.