Changeset View
Changeset View
Standalone View
Standalone View
source/tools/XpartaMuPP/EcheLOn.py
Show First 20 Lines • Show All 68 Lines • ▼ Show 20 Lines | class LeaderboardList(): | ||||
def getOrCreatePlayer(self, JID): | def getOrCreatePlayer(self, JID): | ||||
""" | """ | ||||
Stores a player(JID) in the database if they don't yet exist. | Stores a player(JID) in the database if they don't yet exist. | ||||
Returns either the newly created instance of | Returns either the newly created instance of | ||||
the Player model, or the one that already | the Player model, or the one that already | ||||
exists in the database. | exists in the database. | ||||
""" | """ | ||||
players = db.query(Player).filter_by(jid=str(JID)) | players = db.query(Player).filter(Player.jid.ilike(str(JID))) | ||||
if not players.first(): | if not players.first(): | ||||
player = Player(jid=str(JID), rating=-1) | player = Player(jid=str(JID), rating=-1) | ||||
db.add(player) | db.add(player) | ||||
db.commit() | db.commit() | ||||
return player | return player | ||||
return players.first() | return players.first() | ||||
def removePlayer(self, JID): | def removePlayer(self, JID): | ||||
""" | """ | ||||
Remove a player(JID) from database. | Remove a player(JID) from database. | ||||
Returns the player that was removed, or None | Returns the player that was removed, or None | ||||
if that player didn't exist. | if that player didn't exist. | ||||
""" | """ | ||||
players = db.query(Player).filter_by(jid=JID) | players = db.query(Player).filter(Player.jid.ilike(str(JID))) | ||||
player = players.first() | player = players.first() | ||||
if not player: | if not player: | ||||
return None | return None | ||||
players.delete() | players.delete() | ||||
return player | return player | ||||
def addGame(self, gamereport): | def addGame(self, gamereport): | ||||
""" | """ | ||||
▲ Show 20 Lines • Show All 148 Lines • ▼ Show 20 Lines | """ | ||||
Returns a dictionary of player rankings to | Returns a dictionary of player rankings to | ||||
JIDs for sending. | JIDs for sending. | ||||
""" | """ | ||||
board = {} | board = {} | ||||
players = db.query(Player).filter(Player.rating != -1).order_by(Player.rating.desc()).limit(100).all() | players = db.query(Player).filter(Player.rating != -1).order_by(Player.rating.desc()).limit(100).all() | ||||
for rank, player in enumerate(players): | for rank, player in enumerate(players): | ||||
board[player.jid] = {'name': '@'.join(player.jid.split('@')[:-1]), 'rating': str(player.rating)} | board[player.jid] = {'name': '@'.join(player.jid.split('@')[:-1]), 'rating': str(player.rating)} | ||||
return board | return board | ||||
def getRatingList(self, nicks): | def getRatingList(self, nicks): | ||||
""" | """ | ||||
Returns a rating list of players | Returns a rating list of players | ||||
currently in the lobby by nick | currently in the lobby by nick | ||||
because the client can't link | because the client can't link | ||||
JID to nick conveniently. | JID to nick conveniently. | ||||
""" | """ | ||||
ratinglist = {} | ratinglist = {} | ||||
▲ Show 20 Lines • Show All 99 Lines • ▼ Show 20 Lines | def getNumPlayers(self, rawGameReport): | ||||
""" | """ | ||||
# Find a key in the report which holds values for multiple players. | # Find a key in the report which holds values for multiple players. | ||||
for key in rawGameReport: | for key in rawGameReport: | ||||
if rawGameReport[key].find(",") != -1: | if rawGameReport[key].find(",") != -1: | ||||
# Count the number of values, minus one for the false split positive. | # Count the number of values, minus one for the false split positive. | ||||
return len(rawGameReport[key].split(","))-1 | return len(rawGameReport[key].split(","))-1 | ||||
# Return -1 in case of failure. | # Return -1 in case of failure. | ||||
return -1 | return -1 | ||||
## Class for custom player stanza extension ## | ## Class for custom player stanza extension ## | ||||
class PlayerXmppPlugin(ElementBase): | class PlayerXmppPlugin(ElementBase): | ||||
name = 'query' | name = 'query' | ||||
namespace = 'jabber:iq:player' | namespace = 'jabber:iq:player' | ||||
interfaces = set(('game', 'online')) | interfaces = set(('game', 'online')) | ||||
sub_interfaces = interfaces | sub_interfaces = interfaces | ||||
plugin_attrib = 'player' | plugin_attrib = 'player' | ||||
▲ Show 20 Lines • Show All 63 Lines • ▼ Show 20 Lines | class EcheLOn(sleekxmpp.ClientXMPP): | ||||
""" | """ | ||||
A simple list provider | A simple list provider | ||||
""" | """ | ||||
def __init__(self, sjid, password, room, nick): | def __init__(self, sjid, password, room, nick): | ||||
sleekxmpp.ClientXMPP.__init__(self, sjid, password) | sleekxmpp.ClientXMPP.__init__(self, sjid, password) | ||||
self.sjid = sjid | self.sjid = sjid | ||||
self.room = room | self.room = room | ||||
self.nick = nick | self.nick = nick | ||||
self.ratingListCache = {} | |||||
self.ratingCacheReload = True | |||||
self.boardListCache = {} | |||||
self.boardCacheReload = True | |||||
# Init leaderboard object | # Init leaderboard object | ||||
self.leaderboard = LeaderboardList(room) | self.leaderboard = LeaderboardList(room) | ||||
# gameReport to leaderboard abstraction | # gameReport to leaderboard abstraction | ||||
self.reportManager = ReportManager(self.leaderboard) | self.reportManager = ReportManager(self.leaderboard) | ||||
# Store mapping of nicks and XmppIDs, attached via presence stanza | # Store mapping of nicks and XmppIDs, attached via presence stanza | ||||
▲ Show 20 Lines • Show All 65 Lines • ▼ Show 20 Lines | def iqhandler(self, iq): | ||||
""" | """ | ||||
if iq['type'] == 'error': | if iq['type'] == 'error': | ||||
logging.error('iqhandler error' + iq['error']['condition']) | logging.error('iqhandler error' + iq['error']['condition']) | ||||
#self.disconnect() | #self.disconnect() | ||||
elif iq['type'] == 'get': | elif iq['type'] == 'get': | ||||
""" | """ | ||||
Request lists. | Request lists. | ||||
""" | """ | ||||
if 'boardlist' in iq.plugins: | iq_attribute = list(iq.plugins.items())[0][0][0] | ||||
Dunedan: The solution from D1182 is was cleaner in this regard. | |||||
if iq_attribute == 'boardlist': | |||||
command = iq['boardlist']['command'] | command = iq['boardlist']['command'] | ||||
recipient = iq['boardlist']['recipient'] | recipient = iq['boardlist']['recipient'] | ||||
if command == 'getleaderboard': | if command == 'getleaderboard': | ||||
try: | try: | ||||
self.leaderboard.getOrCreatePlayer(iq['from']) | |||||
self.sendBoardList(iq['from'], recipient) | self.sendBoardList(iq['from'], recipient) | ||||
except: | except: | ||||
traceback.print_exc() | traceback.print_exc() | ||||
logging.error("Failed to process leaderboardlist request from %s" % iq['from'].bare) | logging.error("Failed to process leaderboardlist request from %s" % iq['from'].bare) | ||||
elif command == 'getratinglist': | elif command == 'getratinglist': | ||||
try: | try: | ||||
self.sendRatingList(iq['from']); | self.sendRatingList(iq['from']); | ||||
except: | except: | ||||
traceback.print_exc() | traceback.print_exc() | ||||
else: | else: | ||||
logging.error("Failed to process boardlist request from %s" % iq['from'].bare) | logging.error("Failed to process boardlist request from %s" % iq['from'].bare) | ||||
elif 'profile' in iq.plugins: | elif iq_attribute == 'profile': | ||||
command = iq['profile']['command'] | command = iq['profile']['command'] | ||||
recipient = iq['profile']['recipient'] | recipient = iq['profile']['recipient'] | ||||
try: | try: | ||||
self.sendProfile(iq['from'], command, recipient) | self.sendProfile(iq['from'], command, recipient) | ||||
except: | except: | ||||
try: | try: | ||||
self.sendProfileNotFound(iq['from'], command, recipient) | self.sendProfileNotFound(iq['from'], command, recipient) | ||||
except: | except: | ||||
logging.debug("No record found for %s" % command) | logging.debug("No record found for %s" % command) | ||||
else: | else: | ||||
logging.error("Unknown 'get' type stanza request from %s" % iq['from'].bare) | logging.error("Unknown 'get' type stanza request from %s" % iq['from'].bare) | ||||
elif iq['type'] == 'result': | elif iq['type'] == 'result': | ||||
""" | """ | ||||
Iq successfully received | Iq successfully received | ||||
""" | """ | ||||
pass | pass | ||||
elif iq['type'] == 'set': | elif iq['type'] == 'set': | ||||
if 'gamereport' in iq.plugins: | iq_attribute = list(iq.plugins.items())[0][0][0] | ||||
if iq_attribute == 'gamereport': | |||||
""" | """ | ||||
Client is reporting end of game statistics | Client is reporting end of game statistics | ||||
""" | """ | ||||
try: | try: | ||||
self.leaderboard.getOrCreatePlayer(iq['gamereport']['sender']) | |||||
self.reportManager.addReport(iq['gamereport']['sender'], iq['gamereport']['game']) | self.reportManager.addReport(iq['gamereport']['sender'], iq['gamereport']['game']) | ||||
if self.leaderboard.getLastRatedMessage() != "": | if self.leaderboard.getLastRatedMessage() != "": | ||||
self.ratingCacheReload = True | |||||
self.boardCacheReload = True | |||||
self.send_message(mto=self.room, mbody=self.leaderboard.getLastRatedMessage(), mtype="groupchat", | self.send_message(mto=self.room, mbody=self.leaderboard.getLastRatedMessage(), mtype="groupchat", | ||||
mnick=self.nick) | mnick=self.nick) | ||||
self.sendRatingList(iq['from']) | self.sendRatingList(iq['from']) | ||||
except: | except: | ||||
traceback.print_exc() | traceback.print_exc() | ||||
logging.error("Failed to update game statistics for %s" % iq['from'].bare) | logging.error("Failed to update game statistics for %s" % iq['from'].bare) | ||||
elif 'player' in iq.plugins: | elif iq_attribute == 'player': | ||||
player = iq['player']['online'] | player = iq['player']['online'] | ||||
#try: | #try: | ||||
self.leaderboard.getOrCreatePlayer(player) | self.leaderboard.getOrCreatePlayer(player) | ||||
#except: | #except: | ||||
#logging.debug("Could not create new user %s" % player) | #logging.debug("Could not create new user %s" % player) | ||||
else: | else: | ||||
logging.error("Failed to process stanza type '%s' received from %s" % iq['type'], iq['from'].bare) | logging.error("Failed to process stanza type '%s' received from %s" % iq['type'], iq['from'].bare) | ||||
def sendBoardList(self, to, recipient): | def sendBoardList(self, to, recipient): | ||||
""" | """ | ||||
Send the whole leaderboard list. | Send the whole leaderboard list. | ||||
If no target is passed the boardlist is broadcasted | If no target is passed the boardlist is broadcasted | ||||
to all clients. | to all clients. | ||||
""" | """ | ||||
## Pull leaderboard data and add it to the stanza | ## See if we can squeak by with the cached version. | ||||
board = self.leaderboard.getBoard() | # Leaderboard cache is reloaded upon a new rated game being rated. | ||||
if self.boardCacheReload: | |||||
self.boardListCache = self.leaderboard.getBoard() | |||||
self.boardCacheReload = False | |||||
stz = BoardListXmppPlugin() | stz = BoardListXmppPlugin() | ||||
iq = self.Iq() | iq = self.Iq() | ||||
iq['type'] = 'result' | iq['type'] = 'result' | ||||
for i in board: | for i in self.boardListCache: | ||||
stz.addItem(board[i]['name'], board[i]['rating']) | stz.addItem(self.boardListCache[i]['name'], self.boardListCache[i]['rating']) | ||||
stz.addCommand('boardlist') | stz.addCommand('boardlist') | ||||
stz.addRecipient(recipient) | stz.addRecipient(recipient) | ||||
iq.setPayload(stz) | iq.setPayload(stz) | ||||
## Check recipient exists | ## Check recipient exists | ||||
if str(to) not in self.nicks: | if str(to) not in self.nicks: | ||||
logging.error("No player with the XmPP ID '%s' known to send boardlist to" % str(to)) | logging.error("No player with the XmPP ID '%s' known to send boardlist to" % str(to)) | ||||
return | return | ||||
## Set additional IQ attributes | ## Set additional IQ attributes | ||||
iq['to'] = to | iq['to'] = to | ||||
## Try sending the stanza | ## Try sending the stanza | ||||
try: | try: | ||||
iq.send(block=False, now=True) | iq.send(block=False, now=True) | ||||
except: | except: | ||||
logging.error("Failed to send leaderboard list") | logging.error("Failed to send leaderboard list") | ||||
def sendRatingList(self, to): | def sendRatingList(self, to): | ||||
""" | """ | ||||
Send the rating list. | Send the rating list. | ||||
""" | """ | ||||
## Pull rating list data and add it to the stanza | ## Attempt to use the cache. | ||||
ratinglist = self.leaderboard.getRatingList(self.nicks) | # Cache is invalidated when a new game is rated or a uncached player | ||||
# comes online. | |||||
if self.ratingCacheReload: | |||||
self.ratingListCache = self.leaderboard.getRatingList(self.nicks) | |||||
self.ratingCacheReload = False | |||||
else: | |||||
for JID in list(self.nicks): | |||||
if JID not in self.ratingListCache: | |||||
self.ratingListCache = self.leaderboard.getRatingList(self.nicks) | |||||
self.ratingCacheReload = False | |||||
break | |||||
stz = BoardListXmppPlugin() | stz = BoardListXmppPlugin() | ||||
iq = self.Iq() | iq = self.Iq() | ||||
iq['type'] = 'result' | iq['type'] = 'result' | ||||
for i in ratinglist: | for i in self.ratingListCache: | ||||
stz.addItem(ratinglist[i]['name'], ratinglist[i]['rating']) | stz.addItem(self.ratingListCache[i]['name'], self.ratingListCache[i]['rating']) | ||||
stz.addCommand('ratinglist') | stz.addCommand('ratinglist') | ||||
iq.setPayload(stz) | iq.setPayload(stz) | ||||
## Check recipient exists | ## Check recipient exists | ||||
if str(to) not in self.nicks: | if str(to) not in self.nicks: | ||||
logging.error("No player with the XmPP ID '%s' known to send ratinglist to" % str(to)) | logging.error("No player with the XmPP ID '%s' known to send ratinglist to" % str(to)) | ||||
return | return | ||||
## Set additional IQ attributes | ## Set additional IQ attributes | ||||
iq['to'] = to | iq['to'] = to | ||||
▲ Show 20 Lines • Show All 126 Lines • Show Last 20 Lines |
Wildfire Games · Phabricator
The solution from D1182 is was cleaner in this regard.