mirror of
https://github.com/barronwaffles/dwc_network_server_emulator.git
synced 2026-04-23 00:57:40 -05:00
588 lines
23 KiB
Python
588 lines
23 KiB
Python
import logging
|
|
import time
|
|
import traceback
|
|
|
|
from twisted.internet.protocol import Factory
|
|
from twisted.internet.endpoints import serverFromString
|
|
from twisted.protocols.basic import LineReceiver
|
|
from twisted.internet import reactor
|
|
from twisted.internet.error import ReactorAlreadyRunning
|
|
|
|
import gamespy.gs_database as gs_database
|
|
import gamespy.gs_query as gs_query
|
|
import gamespy.gs_utility as gs_utils
|
|
import other.utils as utils
|
|
|
|
|
|
# Logger settings
|
|
logger_output_to_console = True
|
|
logger_output_to_file = True
|
|
logger_name = "GameSpyProfileServer"
|
|
logger_filename = "gamespy_profile_server.log"
|
|
logger = utils.create_logger(logger_name, logger_filename, -1, logger_output_to_console, logger_output_to_file)
|
|
|
|
address = ("0.0.0.0", 29900)
|
|
class GameSpyProfileServer(object):
|
|
def __init__(self):
|
|
pass
|
|
|
|
def start(self):
|
|
endpoint = serverFromString(reactor, "tcp:%d:interface=%s" % (address[1], address[0]))
|
|
conn = endpoint.listen(PlayerFactory())
|
|
|
|
try:
|
|
if reactor.running == False:
|
|
reactor.run(installSignalHandlers=0)
|
|
except ReactorAlreadyRunning:
|
|
pass
|
|
|
|
class PlayerFactory(Factory):
|
|
def __init__(self):
|
|
# Instead of storing the sessions in the database, it might make more sense to store them in the PlayerFactory.
|
|
logger.log(logging.INFO, "Now listening for connections on %s:%d...", address[0], address[1])
|
|
self.sessions = {}
|
|
|
|
def buildProtocol(self, address):
|
|
return PlayerSession(self.sessions, address)
|
|
|
|
class PlayerSession(LineReceiver):
|
|
def __init__(self, sessions, address):
|
|
self.setRawMode() # We're dealing with binary data so set to raw mode
|
|
|
|
self.db = gs_database.GamespyDatabase()
|
|
|
|
self.sessions = sessions
|
|
self.address = address
|
|
self.remaining_message = "" # Stores any unparsable/incomplete commands until the next rawDataReceived
|
|
|
|
self.profileid = 0
|
|
self.gameid = ""
|
|
|
|
self.buddies = []
|
|
self.blocked = []
|
|
|
|
self.status = ""
|
|
self.statstring = ""
|
|
self.locstring = ""
|
|
|
|
self.keepalive = int(time.time())
|
|
self.sesskey = ""
|
|
|
|
def log(self, level, message):
|
|
if self.profileid == 0:
|
|
if self.gameid == "":
|
|
logger.log(level, "[%s:%d] %s", self.address.host, self.address.port,message)
|
|
else:
|
|
logger.log(level, "[%s:%d | %s] %s", self.address.host, self.address.port, self.gameid, message)
|
|
else:
|
|
if self.gameid == "":
|
|
logger.log(level, "[%s:%d | %d] %s", self.address.host, self.address.port, self.profileid, message)
|
|
else:
|
|
logger.log(level, "[%s:%d | %d | %s] %s", self.address.host, self.address.port, self.profileid, self.gameid, message)
|
|
|
|
def get_ip_as_int(self, address):
|
|
ipaddress = 0
|
|
|
|
if address != None:
|
|
for n in address.split('.'):
|
|
ipaddress = (ipaddress << 8) | int(n)
|
|
|
|
return ipaddress
|
|
|
|
def connectionMade(self):
|
|
try:
|
|
self.transport.setTcpKeepAlive(1)
|
|
|
|
self.log(logging.INFO, "Received connection from %s:%d" % (self.address.host, self.address.port))
|
|
|
|
# Create new session id
|
|
self.session = ""
|
|
|
|
# Generate a random challenge string
|
|
self.challenge = utils.generate_random_str(10, "ABCDEFGHIJKLMNOPQRSTUVWXYZ")
|
|
|
|
# The first command sent to the client is always a login challenge containing the server challenge key.
|
|
msg = gs_query.create_gamespy_message([
|
|
('__cmd__', "lc"),
|
|
('__cmd_val__', "1"),
|
|
('challenge', self.challenge),
|
|
('id', "1"),
|
|
])
|
|
|
|
self.log(logging.DEBUG, "SENDING: '%s'..." % msg)
|
|
self.transport.write(bytes(msg))
|
|
except:
|
|
self.log(logging.ERROR, "Unknown exception: %s" % traceback.format_exc())
|
|
|
|
def connectionLost(self, reason):
|
|
try:
|
|
self.log(logging.INFO, "Client disconnected")
|
|
|
|
self.status = "0"
|
|
self.statstring = "Offline"
|
|
self.locstring = ""
|
|
self.send_status_to_friends()
|
|
|
|
if self.profileid in self.sessions:
|
|
del self.sessions[self.profileid]
|
|
|
|
self.db.delete_session(self.session)
|
|
self.log(logging.INFO, "Deleted session " + self.session)
|
|
except:
|
|
self.log(logging.ERROR, "Unknown exception: %s" % traceback.format_exc())
|
|
|
|
def rawDataReceived(self, data):
|
|
try:
|
|
self.log(logging.DEBUG, "RESPONSE: '%s'..." % data)
|
|
|
|
# In the case where command string is too big to fit into one read, any parts that could not be successfully
|
|
# parsed are stored in the variable remaining_message. On the next rawDataReceived command, the remaining
|
|
# message and the data are combined to create a full command.
|
|
data = self.remaining_message + data
|
|
|
|
# Check to make sure the data buffer starts with a valid command.
|
|
if len(data) > 0 and data[0] != '\\':
|
|
# There is data in the buffer but it doesn't start with a \ so there's no chance of it being valid.
|
|
# Look for the first instance of \final\ and remove everything before it.
|
|
# If \final\ is not in the command string then ignore it.
|
|
final = "\\final\\"
|
|
data = data[data.index(final) + len(final):] if final in data else ""
|
|
|
|
commands, self.remaining_message = gs_query.parse_gamespy_message(data)
|
|
|
|
cmds = {
|
|
"login": self.perform_login,
|
|
"logout": self.perform_logout,
|
|
"getprofile": self.perform_getprofile,
|
|
"updatepro": self.perform_updatepro,
|
|
"ka": self.perform_ka,
|
|
"status": self.perform_status,
|
|
"bm": self.perform_bm,
|
|
"addbuddy": self.perform_addbuddy,
|
|
"delbuddy": self.perform_delbuddy,
|
|
"authadd": self.perform_authadd,
|
|
}
|
|
def cmd_err(data_parsed):
|
|
# Maybe write unknown commands to a separate file later so new data can be collected more easily?
|
|
self.log(logging.ERROR, "Found unknown command, don't know how to handle '%s'." % data_parsed['__cmd__'])
|
|
|
|
for data_parsed in commands:
|
|
#self.log(-1, data_parsed)
|
|
self.log(logging.DEBUG, data_parsed)
|
|
cmds.get(data_parsed['__cmd__'], cmd_err)(data_parsed)
|
|
except:
|
|
self.log(logging.ERROR, "Unknown exception: %s" % traceback.format_exc())
|
|
|
|
def perform_login(self, data_parsed):
|
|
authtoken_parsed = gs_utils.parse_authtoken(data_parsed['authtoken'], self.db)
|
|
#print authtoken_parsed
|
|
|
|
# Track what console is connecting and save it in the database during user creation just in case we can use
|
|
# the information in the future.
|
|
console = 0 # 0 = NDS, 1 = Wii
|
|
|
|
# get correct information
|
|
userid = authtoken_parsed['userid']
|
|
|
|
# The Wii does not use passwd, so take another uniquely generated string as the password.
|
|
if "passwd" in authtoken_parsed:
|
|
password = authtoken_parsed['passwd']
|
|
else:
|
|
password = authtoken_parsed['gsbrcd']
|
|
console = 1
|
|
|
|
gsbrcd = authtoken_parsed['gsbrcd']
|
|
gameid = gsbrcd[:4]
|
|
uniquenick = utils.base32_encode(int(userid)) + gsbrcd
|
|
email = uniquenick + "@nds" # The Wii also seems to use @nds.
|
|
|
|
# Wii: Serial number
|
|
if "csnum" in authtoken_parsed:
|
|
csnum = authtoken_parsed['csnum']
|
|
console = 1
|
|
else:
|
|
csnum = ""
|
|
|
|
# Wii: Friend code
|
|
if "cfc" in authtoken_parsed:
|
|
cfc = authtoken_parsed['cfc']
|
|
console = 1
|
|
else:
|
|
cfc = ""
|
|
|
|
# NDS: Wifi network's BSSID
|
|
if "bssid" in authtoken_parsed:
|
|
bssid = authtoken_parsed['bssid']
|
|
else:
|
|
bssid = ""
|
|
|
|
# NDS: Device name
|
|
if "devname" in authtoken_parsed:
|
|
devname = authtoken_parsed['devname']
|
|
else:
|
|
devname = ""
|
|
|
|
# NDS: User's birthday
|
|
if "birth" in authtoken_parsed:
|
|
birth = authtoken_parsed['birth']
|
|
else:
|
|
birth = ""
|
|
|
|
# Verify the client's response
|
|
valid_response = gs_utils.generate_response(self.challenge, authtoken_parsed['challenge'], data_parsed['challenge'], data_parsed['authtoken'])
|
|
if data_parsed['response'] != valid_response:
|
|
self.log(logging.ERROR, "ERROR: Got invalid response. Got %s, expected %s" % (data_parsed['response'], valid_response))
|
|
|
|
proof = gs_utils.generate_proof(self.challenge, authtoken_parsed['challenge'], data_parsed['challenge'], data_parsed['authtoken'])
|
|
|
|
valid_user = self.db.check_user_exists(userid, gsbrcd)
|
|
if valid_user == False:
|
|
profileid = self.db.create_user(userid, password, email, uniquenick, gsbrcd, console, csnum, cfc, bssid, devname, birth, gameid)
|
|
else:
|
|
profileid = self.db.perform_login(userid, password, gsbrcd)
|
|
|
|
if profileid == None:
|
|
# Handle case where the user is invalid
|
|
self.log(logging.ERROR, "Invalid password")
|
|
|
|
if profileid != None:
|
|
# Successfully logged in or created account, continue creating session.
|
|
loginticket = gs_utils.base64_encode(utils.generate_random_str(16))
|
|
self.sesskey = self.db.create_session(profileid, loginticket)
|
|
|
|
self.sessions[profileid] = self
|
|
|
|
msg = gs_query.create_gamespy_message([
|
|
('__cmd__', "lc"),
|
|
('__cmd_val__', "2"),
|
|
('sesskey', self.sesskey),
|
|
('proof', proof),
|
|
('userid', userid),
|
|
('profileid', profileid),
|
|
('uniquenick', uniquenick),
|
|
# Some kind of token... don't know it gets used or generated, but it doesn't seem to have any negative effects if it's not properly generated.
|
|
('lt', loginticket),
|
|
('id', data_parsed['id']),
|
|
])
|
|
|
|
# Take the first 4 letters of gsbrcd instead of gamecd because they should be consistent across game
|
|
# regions. For example, the US version of Metroid Prime Hunters has the gamecd "AMHE" and the first 4 letters
|
|
# of gsbrcd are "AMHE". However, the Japanese version of Metroid Prime Hunters has the gamecd "AMHJ" with
|
|
# the first 4 letters of bsbrcd as "AMHE". Tetris DS is the other way, with the first 4 letters as the
|
|
# Japanese version (ATRJ) while the gamecd is region specific (ATRE for US and ATRJ for JP).
|
|
# gameid is used to send all people on the player's friends list a status updates, so don't make it region
|
|
# specific.
|
|
self.gameid = gsbrcd[:4]
|
|
self.profileid = int(profileid)
|
|
|
|
self.log(logging.DEBUG, "SENDING: %s" % msg)
|
|
self.transport.write(bytes(msg))
|
|
|
|
self.buddies = self.db.get_buddy_list(self.profileid)
|
|
self.blocked = self.db.get_blocked_list(self.profileid)
|
|
|
|
# Get pending messages.
|
|
self.get_pending_messages()
|
|
|
|
# Send any friend statuses when the user logs in.
|
|
# This will allow the user to see if their friends are hosting a game as soon as they log in.
|
|
self.get_status_from_friends()
|
|
self.send_status_to_friends()
|
|
|
|
# profile = self.db.get_profile_from_profileid(profileid)
|
|
# if profile != None:
|
|
# self.statstring = profile['stat']
|
|
# self.locstring = profile['loc']
|
|
|
|
def perform_logout(self, data_parsed):
|
|
self.log(logging.INFO, "Session %s has logged off" % (data_parsed['sesskey']))
|
|
self.db.delete_session(data_parsed['sesskey'])
|
|
|
|
if self.profileid in self.sessions:
|
|
del self.sessions[self.profileid]
|
|
|
|
self.transport.loseConnection()
|
|
|
|
def perform_getprofile(self, data_parsed):
|
|
#profile = self.db.get_profile_from_session_key(data_parsed['sesskey'])
|
|
profile = self.db.get_profile_from_profileid(data_parsed['profileid'])
|
|
|
|
# Wii example: \pi\\profileid\474888031\nick\5pde5vhn1WR9E2g1t533\userid\442778352\email\5pde5vhn1WR9E2g1t533@nds\sig\b126556e5ee62d4da9629dfad0f6b2a8\uniquenick\5pde5vhn1WR9E2g1t533\pid\11\lon\0.000000\lat\0.000000\loc\\id\2\final\
|
|
sig = utils.generate_random_hex_str(32)
|
|
|
|
msg_d = [
|
|
('__cmd__', "pi"),
|
|
('__cmd_val__', ""),
|
|
('profileid', profile['profileid']),
|
|
('nick', profile['uniquenick']),
|
|
('userid', profile['userid']),
|
|
('email', profile['email']),
|
|
('sig', sig),
|
|
('uniquenick', profile['uniquenick']),
|
|
('pid', profile['pid']),
|
|
]
|
|
|
|
if profile['firstname'] != "":
|
|
msg_d.append(('firstname', profile['firstname'])) # Wii gets a firstname
|
|
|
|
if profile['lastname'] != "":
|
|
msg_d.append(('lastname', profile['lastname']))
|
|
|
|
msg_d.extend([
|
|
('lon', profile['lon']),
|
|
('lat', profile['lat']),
|
|
('loc', profile['loc']),
|
|
('id', data_parsed['id']),
|
|
])
|
|
msg = gs_query.create_gamespy_message(msg_d)
|
|
|
|
self.log(logging.DEBUG, "SENDING: %s" % msg)
|
|
self.transport.write(bytes(msg))
|
|
|
|
def perform_updatepro(self, data_parsed):
|
|
# Wii example: \updatepro\\sesskey\199714190\firstname\Wii:2555151656076614@WR9E\partnerid\11\final\
|
|
|
|
# Remove any fields not related to what we should be updating.
|
|
# To avoid any crashes, make sure the key is actually in the dictionary before removing it.
|
|
if "__cmd__" in data_parsed:
|
|
data_parsed.pop('__cmd__')
|
|
if "__cmd_val__" in data_parsed:
|
|
data_parsed.pop('__cmd_val__')
|
|
if "updatepro" in data_parsed:
|
|
data_parsed.pop('updatepro')
|
|
if "partnerid" in data_parsed:
|
|
data_parsed.pop('partnerid')
|
|
if "sesskey" in data_parsed:
|
|
data_parsed.pop('sesskey')
|
|
|
|
# Create a list of fields to be updated.
|
|
for f in data_parsed:
|
|
self.db.update_profile(self.profileid, (f, data_parsed[f]))
|
|
|
|
|
|
def perform_ka(self, data_parsed):
|
|
self.keepalive = int(time.time())
|
|
|
|
msg = gs_query.create_gamespy_message([
|
|
('__cmd__', "ka"),
|
|
('__cmd_val__', ""),
|
|
])
|
|
self.transport.write(msg)
|
|
|
|
|
|
def perform_status(self, data_parsed):
|
|
self.sesskey = data_parsed['sesskey']
|
|
|
|
|
|
self.status = data_parsed['__cmd_val__']
|
|
self.statstring = data_parsed['statstring']
|
|
self.locstring = data_parsed['locstring']
|
|
|
|
# fields = [
|
|
# #("status", self.status),
|
|
# ("stat", self.statstring),
|
|
# ("loc", self.locstring),
|
|
# ]
|
|
#
|
|
# for f in fields:
|
|
# self.db.update_profile(self.sesskey, f)
|
|
|
|
# Send authorization requests to client
|
|
self.get_buddy_requests()
|
|
|
|
# Send authorizationed message to client
|
|
self.get_buddy_authorized()
|
|
|
|
self.send_status_to_friends()
|
|
|
|
|
|
def perform_bm(self, data_parsed):
|
|
if data_parsed['__cmd_val__'] in ("1", "5", "102", "103"): # Message to/from clients?
|
|
if "t" in data_parsed:
|
|
# Send message to the profile id in "t"
|
|
dest_profileid = int(data_parsed['t'])
|
|
dest_profile_buddies = self.db.get_buddy_list(dest_profileid)
|
|
dest_msg = data_parsed['msg']
|
|
|
|
not_buddies = False
|
|
|
|
# Check if the user is buddies with the target user before sending message.
|
|
if not_buddies:
|
|
for buddy in self.buddies:
|
|
if buddy['userProfileId'] == dest_profileid:
|
|
not_buddies = True
|
|
break
|
|
|
|
if not_buddies:
|
|
for buddy in dest_profile_buddies:
|
|
if buddy['userProfileId'] == self.profile:
|
|
not_buddies = True
|
|
break
|
|
|
|
# Send error to user if they tried to send a message to someone who isn't a buddy.
|
|
if not_buddies:
|
|
msg = gs_query.create_gamespy_message([
|
|
('__cmd__', "error"),
|
|
('__cmd_val__', ""),
|
|
('err', 2305),
|
|
('errmsg', "The profile the message was to be sent to is not a buddy."),
|
|
('id', 1),
|
|
])
|
|
logger.log(logging.DEBUG, "Trying to send message to someone who isn't a buddy: %s" % msg)
|
|
self.transport.write(msg)
|
|
return
|
|
|
|
msg = gs_query.create_gamespy_message([
|
|
('__cmd__', "bm"),
|
|
('__cmd_val__', "1"),
|
|
('f', self.profileid),
|
|
('msg', dest_msg),
|
|
])
|
|
|
|
if dest_profileid in self.sessions:
|
|
self.log(logging.DEBUG, "SENDING TO %s:%s: %s" % (self.sessions[dest_profileid].address.host, self.sessions[dest_profileid].address.port, msg))
|
|
self.sessions[dest_profileid].transport.write(bytes(msg))
|
|
else:
|
|
if data_parsed['__cmd_val__'] == "1":
|
|
self.log(logging.DEBUG, "Saving message to %d: %s" % (dest_profileid, msg))
|
|
self.db.save_pending_message(self.profileid, dest_profileid, msg)
|
|
else:
|
|
msg = gs_query.create_gamespy_message([
|
|
('__cmd__', "error"),
|
|
('__cmd_val__', ""),
|
|
('err', 2307),
|
|
('errmsg', "The buddy to send a message to is offline."),
|
|
('id', 1),
|
|
])
|
|
logger.log(logging.DEBUG, "Trying to send message to someone who isn't online: %s" % msg)
|
|
self.transport.write(msg)
|
|
|
|
|
|
def perform_addbuddy(self, data_parsed):
|
|
newprofileid = int(data_parsed['newprofileid'])
|
|
if newprofileid == self.profileid:
|
|
logger.log(logging.DEBUG, "Can't add self as friend: %d == %d", newprofileid, self.profileid)
|
|
return
|
|
|
|
# Sample: \addbuddy\\sesskey\231601763\newprofileid\476756820\reason\\final\
|
|
self.buddies = self.db.get_buddy_list(self.profileid)
|
|
|
|
buddy_exists = False
|
|
for buddy in self.buddies:
|
|
if buddy['buddyProfileId'] == newprofileid:
|
|
buddy_exists = True
|
|
break
|
|
|
|
if buddy_exists == False:
|
|
self.db.add_buddy(self.profileid, newprofileid)
|
|
|
|
if newprofileid in self.sessions:
|
|
logger.log(logging.DEBUG, "User is online, sending direct request from profile id %d to profile id %d..." % (self.profileid, newprofileid))
|
|
self.send_buddy_request(self.sessions[newprofileid], self.profileid)
|
|
|
|
|
|
def perform_delbuddy(self, data_parsed):
|
|
# Sample: \delbuddy\\sesskey\61913621\delprofileid\1\final\
|
|
self.db.delete_buddy(self.profileid, int(data_parsed['delprofileid']))
|
|
|
|
def perform_authadd(self, data_parsed):
|
|
# Sample: \authadd\\sesskey\231587549\fromprofileid\217936895\sig\f259f26d3273f8bda23c7c5e4bd8c5aa\final\
|
|
# Authorize the other person's friend request.
|
|
self.db.auth_buddy(int(data_parsed['fromprofileid']), self.profileid)
|
|
|
|
self.get_buddy_authorized()
|
|
|
|
def send_status_to_friends(self):
|
|
# TODO: Cache buddy list so we don't have to query the database every time
|
|
self.buddies = self.db.get_buddy_list(self.profileid)
|
|
|
|
if self.status == "0" and self.statstring == "Offline":
|
|
# Going offline, don't need to send the other information.
|
|
status_msg = "|s|%s|ss|%s" % (self.status, self.statstring)
|
|
else:
|
|
status_msg = "|s|%s|ss|%s|ls|%s|ip|%d|p|0|qm|0" % (self.status, self.statstring, self.locstring, self.get_ip_as_int(self.address.host))
|
|
|
|
msg = gs_query.create_gamespy_message([
|
|
('__cmd__', "bm"),
|
|
('__cmd_val__', "100"),
|
|
('f', self.profileid),
|
|
('msg', status_msg),
|
|
])
|
|
|
|
for buddy in self.buddies:
|
|
if buddy['buddyProfileId'] in self.sessions:
|
|
self.sessions[buddy['buddyProfileId']].transport.write(bytes(msg))
|
|
|
|
def get_status_from_friends(self):
|
|
# This will be called when the player logs in. Grab the player's buddy list and check the current sessions to
|
|
# see if anyone is online. If they are online, make them send an update to the calling client.
|
|
self.buddies = self.db.get_buddy_list(self.profileid)
|
|
|
|
for buddy in self.buddies:
|
|
if buddy['status'] != 1:
|
|
continue
|
|
|
|
if buddy['buddyProfileId'] in self.sessions and self.sessions[buddy['buddyProfileId']].gameid == self.gameid:
|
|
status_msg = "|s|%s|ss|%s|ls|%s|ip|%d|p|0|qm|0" % (self.sessions[buddy['buddyProfileId']].status, self.sessions[buddy['buddyProfileId']].statstring, self.sessions[buddy['buddyProfileId']].locstring, self.get_ip_as_int(self.sessions[buddy['buddyProfileId']].address.host))
|
|
else:
|
|
status_msg = "|s|0|ss|Offline"
|
|
|
|
msg = gs_query.create_gamespy_message([
|
|
('__cmd__', "bm"),
|
|
('__cmd_val__', "100"),
|
|
('f', buddy['buddyProfileId']),
|
|
('msg', status_msg),
|
|
])
|
|
|
|
self.transport.write(bytes(msg))
|
|
|
|
def get_buddy_authorized(self):
|
|
buddies = self.db.buddy_need_auth_message(self.profileid)
|
|
|
|
for buddy in buddies:
|
|
msg = gs_query.create_gamespy_message([
|
|
('__cmd__', "bm"),
|
|
('__cmd_val__', "1"),
|
|
('f', buddy['userProfileId']),
|
|
('msg', "I have authorized your request to add me to your list"),
|
|
])
|
|
|
|
self.transport.write(bytes(msg))
|
|
self.db.buddy_sent_auth_message(buddy['userProfileId'], buddy['buddyProfileId'])
|
|
|
|
def get_buddy_requests(self):
|
|
# Get list people who have added the user but haven't been accepted yet.
|
|
buddies = self.db.get_pending_buddy_requests(self.profileid)
|
|
|
|
for buddy in buddies:
|
|
self.send_buddy_request(self, buddy['userProfileId'], buddy['time'])
|
|
|
|
def send_buddy_request(self, session, profileid, senttime = None):
|
|
sig = utils.generate_random_hex_str(32)
|
|
msg = "\r\n\r\n"
|
|
msg += "|signed|" + sig
|
|
|
|
if senttime == None:
|
|
senttime = int(time.time())
|
|
|
|
msg = gs_query.create_gamespy_message([
|
|
('__cmd__', "bm"),
|
|
('__cmd_val__', "2"),
|
|
('f', profileid),
|
|
('date', senttime),
|
|
('msg', msg),
|
|
])
|
|
|
|
session.transport.write(bytes(msg))
|
|
|
|
def get_pending_messages(self):
|
|
messages = self.db.get_pending_messages(self.profileid)
|
|
|
|
for message in messages:
|
|
if message['sourceid'] not in self.blocked:
|
|
self.transport.write(message['msg'])
|
|
|
|
if __name__ == "__main__":
|
|
gsps = GameSpyProfileServer()
|
|
gsps.start()
|