1
0
forked from 0ad/0ad

Displays ratings in the userlist. Fixes #2371.

This was SVN commit r14650.
This commit is contained in:
scythetwirler 2014-01-23 23:13:13 +00:00
parent d82282e358
commit e5e634f768
13 changed files with 267 additions and 82 deletions

View File

@ -36,6 +36,7 @@ function init(attribs)
Engine.LobbySetPlayerPresence("available");
Engine.SendGetGameList();
Engine.SendGetBoardList();
Engine.SendGetRatingList();
updatePlayerList();
resetFilters();
@ -105,24 +106,37 @@ function displayGame(g, mapSizeFilter, playersNumberFilter, mapTypeFilter, showF
return true;
}
// Do a full update of the player listing **Only call on init**
// Do a full update of the player listing, including ratings.
function updatePlayerList()
{
var playersBox = Engine.GetGUIObjectByName("playersBox");
[playerList, presenceList, nickList] = [[],[],[]];
[playerList, presenceList, nickList, ratingList] = [[],[],[],[]];
var ratingsmap = Engine.GetRatingList();
var defaultrating;
for each (var p in Engine.GetPlayerList())
{
var [name, status] = formatPlayerListEntry(p.name, p.presence);
defaultrating = ' -'; //Start with unrated. If no rating is found, keep it unrated.
for each (var user in ratingsmap)
{
if (user.name.toLowerCase() == p.name.toLowerCase())
{
defaultrating = user.rating;
break;
}
}
var [name, status, rating] = formatPlayerListEntry(p.name, p.presence, defaultrating);
playerList.push(name);
presenceList.push(status);
nickList.push(p.name);
ratingList.push(String(" " + rating));
}
playersBox.list_name = playerList;
playersBox.list_status = presenceList;
playersBox.list = nickList;
playersBox.list_rating = ratingList;
playersBox.list = nickList;
if (playersBox.selected >= playersBox.list.length)
playersBox.selected = -1;
return [playerList, presenceList, nickList];
return [playerList, presenceList, nickList, ratingList];
}
// Update leaderboard listing
@ -229,40 +243,41 @@ function updateGameList()
}
// The following function colorizes and formats the entries in the player list.
function formatPlayerListEntry(nickname, presence)
function formatPlayerListEntry(nickname, presence, rating)
{
// Set colors based on player status
var color;
var color_close = '[/color]';
switch (presence)
{
case "playing":
var color = '[color="125 0 0"]';
color = '[color="125 0 0"]';
var status = color + "Busy" + color_close;
break;
case "gone":
case "away":
var color = '[color="229 76 13"]';
color = '[color="229 76 13"]';
var status = color + "Away" + color_close;
break;
case "available":
var color = '[color="0 125 0"]';
color = '[color="0 125 0"]';
var status = color + "Online" + color_close;
break;
case "offline":
var color = '[color="0 0 0"]';
color = '[color="0 0 0"]';
var status = color + "Offline" + color_close;
break;
default:
warn("Unknown presence '"+presence+"'");
var color = '[color="178 178 178"]';
color = '[color="178 178 178"]';
var status = color + "Unknown" + color_close;
break;
}
var elo = color + rating + color_close;
var name = colorPlayerName(nickname);
// Push this player's name and status onto the list
return [name, status];
return [name, status, elo];
}
function selectGame(selected)
@ -349,6 +364,11 @@ function twoDigits(n)
return n < 10 ? "0" + n : n;
}
function stripColorCodes(input)
{
return input.replace(/\[(\w+)[^w]*?](.*?)\[\/\1]/g, '$2');
}
////////////////////////////////////////////////////////////////////////////////////////////////
// GUI event handlers
////////////////////////////////////////////////////////////////////////////////////////////////
@ -384,6 +404,7 @@ function onTick()
var playerList = playersBox.list_name;
var presenceList = playersBox.list_status;
var nickList = playersBox.list;
var ratingList = playersBox.list_rating;
var nickIndex = nickList.indexOf(nick);
switch(message.level)
{
@ -391,13 +412,15 @@ function onTick()
if (nick == g_Name)
{
// We just joined, we need to get the full player list
[playerList, presenceList, nickList] = updatePlayerList();
[playerList, presenceList, nickList, ratingList] = updatePlayerList();
break;
}
var [name, status] = formatPlayerListEntry(nick, presence);
var [name, status, rating] = formatPlayerListEntry(nick, presence, " -");
playerList.push(name);
presenceList.push(status);
nickList.push(nick);
ratingList.push(String(rating));
Engine.SendGetRatingList();
addChatMessage({ "text": "/special " + nick + " has joined.", "key": g_specialKey });
break;
case "leave":
@ -406,6 +429,7 @@ function onTick()
playerList.splice(nickIndex, 1);
presenceList.splice(nickIndex, 1);
nickList.splice(nickIndex, 1);
ratingList.splice(nickIndex, 1);
addChatMessage({ "text": "/special " + nick + " has left.", "key": g_specialKey });
break;
case "nick":
@ -416,18 +440,20 @@ function onTick()
addChatMessage({ "from": "system", "text": "Invalid nickname: " + message.data });
break;
}
var [name, status] = formatPlayerListEntry(message.data, presence); // TODO: actually we don't want to change the presence here, so use what was used before
var [name, status, rating] = formatPlayerListEntry(message.data, presence, stripColorCodes(ratingList[nickIndex])); // TODO: actually we don't want to change the presence here, so use what was used before
playerList[nickIndex] = name;
// presence stays the same
nickList[nickIndex] = message.data;
addChatMessage({ "text": "/special " + nick + " is now known as " + message.data + ".", "key": g_specialKey });
Engine.SendGetRatingList();
break;
case "presence":
if (nickIndex == -1) // This shouldn't ever happen
break;
var [name, status] = formatPlayerListEntry(nick, presence);
var [name, status, rating] = formatPlayerListEntry(nick, presence, stripColorCodes(ratingList[nickIndex]));
presenceList[nickIndex] = status;
playerList[nickIndex] = name;
ratingList[nickIndex] = rating;
break;
default:
warn("Unknown message.level '" + message.level + "'");
@ -436,7 +462,8 @@ function onTick()
// Push new data to GUI
playersBox.list_name = playerList;
playersBox.list_status = presenceList;
playersBox.list = nickList;
playersBox.list_rating = ratingList;
playersBox.list = nickList;
if (playersBox.selected >= playersBox.list.length)
playersBox.selected = -1;
break;
@ -471,6 +498,9 @@ function onTick()
case "boardlist updated":
updateBoardList();
break;
case "ratinglist updated":
updatePlayerList();
break;
}
break
}
@ -542,14 +572,6 @@ function handleSpecialCommand(text)
case "back":
Engine.LobbySetPlayerPresence("available");
break;
case "nick":
if (g_spammers[g_Name] != undefined)
break;
// Strip invalid characters.
nick = sanitizePlayerName(nick, true, true);
Engine.LobbySetNick(nick);
g_Name = nick;
break;
case "kick": // TODO: Split reason from nick and pass it too, for now just support "/kick nick"
// also allow quoting nicks (and/or prevent users from changing it here, but that doesn't help if the spammer uses a different client)
Engine.LobbyKick(nick, "");

View File

@ -19,14 +19,15 @@
</action>
<!-- Left panel: Player list. -->
<object name="leftPanel" size="20 30 15% 100%-50">
<object name="playersBox" style="ModernList" type="olist" size="0 0 100% 100%">
<def id="status" heading="Status" width="40%"/>
<def id="name" heading="Name" width="60%"/>
<object name="leftPanel" size="20 30 20% 100%-50">
<object name="playersBox" style="ModernList" type="olist" size="0 0 100% 100%" font="serif-bold-13">
<def id="status" heading="Status" width="28%"/>
<def id="name" heading="Name" width="47%"/>
<def id="rating" heading="Rating" width="25%" />
</object>
</object>
<object name="leftButtonPanel" size="20 100%-45 15% 100%-20">
<object name="leftButtonPanel" size="20 100%-45 20% 100%-20">
<object type="button" style="StoneButton" size="0 0 100% 100%">
Leaderboard
<action on="Press">Engine.GetGUIObjectByName("leaderboard").hidden = false;Engine.GetGUIObjectByName("leaderboardFade").hidden = false;</action>
@ -34,7 +35,7 @@
</object>
<!-- Right panel: Game details. -->
<object name="rightPanel" size="100%-300 30 100%-20 100%-20">
<object name="rightPanel" size="100%-250 30 100%-20 100%-20" >
<object name="gameInfoEmpty" size="0 0 100% 100%-60" type="image" sprite="ModernDarkBoxGold" hidden="false">
<object size="50%-110 50%-50 50%+110 50%+50" type="image" sprite="productLogo"/>
</object>
@ -106,7 +107,7 @@
</object>
<!-- Middle panel: Filters, game list, chat box. -->
<object name="middlePanel" size="15%+5 5% 100%-305 97.2%">
<object name="middlePanel" size="20%+5 5% 100%-255 97.2%">
<object name="gamesBox" style="ModernList" type="olist" size="0 25 100% 48%">
<action on="SelectionChange">selectGame(this.selected);</action>
<def id="name" heading="Name" color="0 60 0" width="25%"/>
@ -121,21 +122,24 @@
<object name="mapSizeFilter"
type="dropdown"
style="ModernDropDown"
size="49.7% 0 62% 100%">
size="49.7% 0 62% 100%"
font="serif-bold-13">
<action on="SelectionChange">applyFilters();</action>
</object>
<object name="mapTypeFilter"
type="dropdown"
style="ModernDropDown"
size="69.3% 0 82% 100%">
size="69.3% 0 82% 100%"
font="serif-bold-13">
<action on="SelectionChange">applyFilters();</action>
</object>
<object name="playersNumberFilter"
type="dropdown"
style="ModernDropDown"
size="89% 0 100% 100%">
size="89% 0 100% 100%"
font="serif-bold-13">
<action on="SelectionChange">applyFilters();</action>
</object>
@ -144,14 +148,15 @@
type="checkbox"
checked="true"
style="ModernTickBox"
size="0% 0 20 100%">
size="0% 0 20 100%"
font="serif-bold-13">
<action on="Press">applyFilters();</action>
</object>
</object>
<object name="chatPanel" size="0 49% 100% 100%" type="image" sprite="ModernDarkBoxGold">
<object name="chatText" size="0 0 100% 94%" type="text" style="ChatPanel"/>
<object name="chatInput" size="0 94% 100% 100%" type="input" style="ModernInput">
<object name="chatText" size="0 0 100% 94%" type="text" style="ChatPanel" font="serif-13"/>
<object name="chatInput" size="0 94% 100% 100%" type="input" style="ModernInput" font="serif-13">
<action on="Press">submitChatInput();</action>
<action on="Tab">completeNick();</action>
</object>

View File

@ -897,6 +897,7 @@ void GuiScriptingInit(ScriptInterface& scriptInterface)
scriptInterface.RegisterFunction<void, &JSI_Lobby::RecvXmppClient>("RecvXmppClient");
scriptInterface.RegisterFunction<void, &JSI_Lobby::SendGetGameList>("SendGetGameList");
scriptInterface.RegisterFunction<void, &JSI_Lobby::SendGetBoardList>("SendGetBoardList");
scriptInterface.RegisterFunction<void, &JSI_Lobby::SendGetRatingList>("SendGetRatingList");
scriptInterface.RegisterFunction<void, CScriptVal, &JSI_Lobby::SendRegisterGame>("SendRegisterGame");
scriptInterface.RegisterFunction<void, CScriptVal, &JSI_Lobby::SendGameReport>("SendGameReport");
scriptInterface.RegisterFunction<void, &JSI_Lobby::SendUnregisterGame>("SendUnregisterGame");
@ -904,6 +905,7 @@ void GuiScriptingInit(ScriptInterface& scriptInterface)
scriptInterface.RegisterFunction<CScriptVal, &JSI_Lobby::GetPlayerList>("GetPlayerList");
scriptInterface.RegisterFunction<CScriptVal, &JSI_Lobby::GetGameList>("GetGameList");
scriptInterface.RegisterFunction<CScriptVal, &JSI_Lobby::GetBoardList>("GetBoardList");
scriptInterface.RegisterFunction<CScriptVal, &JSI_Lobby::GetBoardList>("GetRatingList");
scriptInterface.RegisterFunction<CScriptVal, &JSI_Lobby::LobbyGuiPollMessage>("LobbyGuiPollMessage");
scriptInterface.RegisterFunction<void, std::wstring, &JSI_Lobby::LobbySendMessage>("LobbySendMessage");
scriptInterface.RegisterFunction<void, std::wstring, &JSI_Lobby::LobbySetPlayerPresence>("LobbySetPlayerPresence");

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2013 Wildfire Games.
/* Copyright (C) 2014 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -33,6 +33,7 @@ public:
virtual void recv() = 0;
virtual void SendIqGetGameList() = 0;
virtual void SendIqGetBoardList() = 0;
virtual void SendIqGetRatingList() = 0;
virtual void SendIqGameReport(ScriptInterface& scriptInterface, CScriptVal data) = 0;
virtual void SendIqRegisterGame(ScriptInterface& scriptInterface, CScriptVal data) = 0;
virtual void SendIqUnregisterGame() = 0;

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2013 Wildfire Games.
/* Copyright (C) 2014 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -59,22 +59,30 @@ glooxwrapper::StanzaExtension* GameReport::clone() const
}
/******************************************************
* BoardListQuery, custom IQ Stanza, used solely to
* request and receive leaderboard data from server.
* BoardListQuery, a flexible custom IQ Stanza useful for anything with ratings, used to
* request and receive leaderboard and rating data from server.
* Example stanza:
* <board player="foobar">1200</board>
*/
BoardListQuery::BoardListQuery( const glooxwrapper::Tag* tag ):StanzaExtension( ExtBoardListQuery )
{
if( !tag || tag->name() != "query" || tag->xmlns() != XMLNS_BOARDLIST )
return;
const glooxwrapper::Tag* c = tag->findTag_clone( "query/command" );
if (c)
m_Command = c->cdata();
glooxwrapper::Tag::free(c);
const glooxwrapper::ConstTagList boardTags = tag->findTagList_clone( "query/board" );
glooxwrapper::ConstTagList::const_iterator it = boardTags.begin();
for ( ; it != boardTags.end(); ++it )
m_BoardList.push_back( *it );
{
m_StanzaBoardList.push_back( *it );
}
}
/**
* Required by gloox, used to find the BoardList element in a recived IQ.
* Required by gloox, used to find the BoardList element in a received IQ.
*/
const glooxwrapper::string& BoardListQuery::filterString() const
{
@ -89,9 +97,13 @@ glooxwrapper::Tag* BoardListQuery::tag() const
{
glooxwrapper::Tag* t = glooxwrapper::Tag::allocate( "query" );
t->setXmlns( XMLNS_BOARDLIST );
// Check for ratinglist or boardlist command
if(!m_Command.empty())
t->addChild(glooxwrapper::Tag::allocate("command", m_Command));
std::vector<const glooxwrapper::Tag*>::const_iterator it = m_BoardList.begin();
for( ; it != m_BoardList.end(); ++it )
std::vector<const glooxwrapper::Tag*>::const_iterator it = m_StanzaBoardList.begin();
for( ; it != m_StanzaBoardList.end(); ++it )
t->addChild( (*it)->clone() );
return t;
@ -105,10 +117,10 @@ glooxwrapper::StanzaExtension* BoardListQuery::clone() const
BoardListQuery::~BoardListQuery()
{
std::vector<const glooxwrapper::Tag*>::const_iterator it = m_BoardList.begin();
for( ; it != m_BoardList.end(); ++it )
std::vector<const glooxwrapper::Tag*>::const_iterator it = m_StanzaBoardList.begin();
for( ; it != m_StanzaBoardList.end(); ++it )
glooxwrapper::Tag::free(*it);
m_BoardList.clear();
m_StanzaBoardList.clear();
}
/******************************************************
@ -133,7 +145,7 @@ GameListQuery::GameListQuery( const glooxwrapper::Tag* tag ):StanzaExtension( Ex
}
/**
* Required by gloox, used to find the GameList element in a recived IQ.
* Required by gloox, used to find the GameList element in a received IQ.
*/
const glooxwrapper::string& GameListQuery::filterString() const
{

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2013 Wildfire Games.
/* Copyright (C) 2014 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -27,7 +27,7 @@
#define ExtBoardListQuery 1404
#define XMLNS_BOARDLIST "jabber:iq:boardlist"
/// Global Boardlist Extension
/// Global Gamereport Extension
#define ExtGameReport 1405
#define XMLNS_GAMEREPORT "jabber:iq:gamereport"
@ -84,6 +84,7 @@ public:
~BoardListQuery();
std::vector<const glooxwrapper::Tag*> m_BoardList;
glooxwrapper::string m_Command;
std::vector<const glooxwrapper::Tag*> m_StanzaBoardList;
};
#endif

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2013 Wildfire Games.
/* Copyright (C) 2014 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -147,6 +147,8 @@ XmppClient::~XmppClient()
glooxwrapper::Tag::free(*it);
for (std::vector<const glooxwrapper::Tag*>::const_iterator it = m_BoardList.begin(); it != m_BoardList.end(); ++it)
glooxwrapper::Tag::free(*it);
for (std::vector<const glooxwrapper::Tag*>::const_iterator it = m_RatingList.begin(); it != m_RatingList.end(); ++it)
glooxwrapper::Tag::free(*it);
}
/// Network
@ -208,6 +210,7 @@ void XmppClient::onDisconnect(gloox::ConnectionError error)
for (std::vector<const glooxwrapper::Tag*>::const_iterator it = m_BoardList.begin(); it != m_BoardList.end(); ++it)
glooxwrapper::Tag::free(*it);
m_BoardList.clear();
m_RatingList.clear();
m_GameList.clear();
m_PlayerMap.clear();
@ -270,12 +273,30 @@ void XmppClient::SendIqGetBoardList()
glooxwrapper::JID xpartamuppJid(m_xpartamuppId);
// Send IQ
BoardListQuery* b = new BoardListQuery();
b->m_Command = "getleaderboard";
glooxwrapper::IQ iq(gloox::IQ::Get, xpartamuppJid);
iq.addExtension(new BoardListQuery());
iq.addExtension(b);
DbgXMPP("SendIqGetBoardList [" << tag_xml(iq) << "]");
m_client->send(iq);
}
/**
* Request the rating data from the server.
*/
void XmppClient::SendIqGetRatingList()
{
glooxwrapper::JID xpartamuppJid(m_xpartamuppId);
// Send IQ
BoardListQuery* b = new BoardListQuery();
b->m_Command = "getratinglist";
glooxwrapper::IQ iq(gloox::IQ::Get, xpartamuppJid);
iq.addExtension(b);
DbgXMPP("SendIqGetRatingList [" << tag_xml(iq) << "]");
m_client->send(iq);
}
/**
* Send game report containing numerous game properties to the server.
*
@ -523,6 +544,31 @@ CScriptValRooted XmppClient::GUIGetBoardList(ScriptInterface& scriptInterface)
return boardList;
}
/**
* Handle requests from the GUI for rating list data.
*
* @return A JS array containing all known leaderboard data
*/
CScriptValRooted XmppClient::GUIGetRatingList(ScriptInterface& scriptInterface)
{
CScriptValRooted ratingList;
scriptInterface.Eval("([])", ratingList);
for(std::vector<const glooxwrapper::Tag*>::const_iterator it = m_RatingList.begin(); it != m_RatingList.end(); ++it)
{
CScriptValRooted rating;
scriptInterface.Eval("({})", rating);
const char* attributes[] = { "name", "rank", "rating" };
short attributes_length = 3;
for (short i = 0; i < attributes_length; i++)
scriptInterface.SetProperty(rating.get(), attributes[i], wstring_from_utf8((*it)->findAttribute(attributes[i]).to_string()));
scriptInterface.CallFunctionVoid(ratingList.get(), "push", rating);
}
return ratingList;
}
/*****************************************************
* Message interfaces *
*****************************************************/
@ -625,14 +671,28 @@ bool XmppClient::handleIq(const glooxwrapper::IQ& iq)
}
if(bq)
{
for(std::vector<const glooxwrapper::Tag*>::const_iterator it = m_BoardList.begin(); it != m_BoardList.end(); ++it )
glooxwrapper::Tag::free(*it);
m_BoardList.clear();
if (bq->m_Command == "boardlist")
{
for(std::vector<const glooxwrapper::Tag*>::const_iterator it = m_BoardList.begin(); it != m_BoardList.end(); ++it )
glooxwrapper::Tag::free(*it);
m_BoardList.clear();
for(std::vector<const glooxwrapper::Tag*>::const_iterator it = bq->m_BoardList.begin(); it != bq->m_BoardList.end(); ++it)
m_BoardList.push_back( (*it)->clone() );
for(std::vector<const glooxwrapper::Tag*>::const_iterator it = bq->m_StanzaBoardList.begin(); it != bq->m_StanzaBoardList.end(); ++it)
m_BoardList.push_back( (*it)->clone() );
CreateSimpleMessage("system", "boardlist updated", "internal");
CreateSimpleMessage("system", "boardlist updated", "internal");
}
else if (bq->m_Command == "ratinglist")
{
for(std::vector<const glooxwrapper::Tag*>::const_iterator it = m_RatingList.begin(); it != m_RatingList.end(); ++it )
glooxwrapper::Tag::free(*it);
m_RatingList.clear();
for(std::vector<const glooxwrapper::Tag*>::const_iterator it = bq->m_StanzaBoardList.begin(); it != bq->m_StanzaBoardList.end(); ++it)
m_RatingList.push_back( (*it)->clone() );
CreateSimpleMessage("system", "ratinglist updated", "internal");
}
}
}
else if(iq.subtype() == gloox::IQ::Error)

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2013 Wildfire Games.
/* Copyright (C) 2014 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -60,6 +60,7 @@ public:
void recv();
void SendIqGetGameList();
void SendIqGetBoardList();
void SendIqGetRatingList();
void SendIqGameReport(ScriptInterface& scriptInterface, CScriptVal data);
void SendIqRegisterGame(ScriptInterface& scriptInterface, CScriptVal data);
void SendIqUnregisterGame();
@ -74,7 +75,7 @@ public:
CScriptValRooted GUIGetPlayerList(ScriptInterface& scriptInterface);
CScriptValRooted GUIGetGameList(ScriptInterface& scriptInterface);
CScriptValRooted GUIGetBoardList(ScriptInterface& scriptInterface);
CScriptValRooted GUIGetRatingList(ScriptInterface& scriptInterface);
//Script
ScriptInterface& GetScriptInterface();
@ -140,6 +141,8 @@ private:
std::vector<const glooxwrapper::Tag*> m_GameList;
/// List of rankings
std::vector<const glooxwrapper::Tag*> m_BoardList;
/// List of ratings
std::vector<const glooxwrapper::Tag*> m_RatingList;
/// Queue of messages
std::deque<GUIMessage> m_GuiMessageQueue;
};

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2013 Wildfire Games.
/* Copyright (C) 2014 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2013 Wildfire Games.
/* Copyright (C) 2014 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify

View File

@ -91,6 +91,13 @@ void JSI_Lobby::SendGetBoardList(ScriptInterface::CxPrivate* UNUSED(pCxPrivate))
g_XmppClient->SendIqGetBoardList();
}
void JSI_Lobby::SendGetRatingList(ScriptInterface::CxPrivate* UNUSED(pCxPrivate))
{
if (!g_XmppClient)
return;
g_XmppClient->SendIqGetRatingList();
}
void JSI_Lobby::SendGameReport(ScriptInterface::CxPrivate* pCxPrivate, CScriptVal data)
{
if (!g_XmppClient)

View File

@ -35,6 +35,7 @@ namespace JSI_Lobby
void RecvXmppClient(ScriptInterface::CxPrivate* pCxPrivate);
void SendGetGameList(ScriptInterface::CxPrivate* pCxPrivate);
void SendGetBoardList(ScriptInterface::CxPrivate* pCxPrivate);
void SendGetRatingList(ScriptInterface::CxPrivate* pCxPrivate);
void SendGameReport(ScriptInterface::CxPrivate* pCxPrivate, CScriptVal data);
void SendRegisterGame(ScriptInterface::CxPrivate* pCxPrivate, CScriptVal data);
void SendUnregisterGame(ScriptInterface::CxPrivate* pCxPrivate);
@ -42,6 +43,7 @@ namespace JSI_Lobby
CScriptVal GetPlayerList(ScriptInterface::CxPrivate* pCxPrivate);
CScriptVal GetGameList(ScriptInterface::CxPrivate* pCxPrivate);
CScriptVal GetBoardList(ScriptInterface::CxPrivate* pCxPrivate);
CScriptVal GetRatingList(ScriptInterface::CxPrivate* pCxPrivate);
CScriptVal LobbyGuiPollMessage(ScriptInterface::CxPrivate* pCxPrivate);
void LobbySendMessage(ScriptInterface::CxPrivate* pCxPrivate, std::wstring message);
void LobbySetPlayerPresence(ScriptInterface::CxPrivate* pCxPrivate, std::wstring presence);
@ -62,4 +64,4 @@ namespace JSI_Lobby
#endif // CONFIG2_LOBBY
}
#endif
#endif

View File

@ -1,6 +1,6 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Copyright (C) 2013 Wildfire Games.
"""Copyright (C) 2014 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -183,6 +183,24 @@ class LeaderboardList():
continue
board[player.jid] = {'name': '@'.join(player.jid.split('@')[:-1]), 'rating': str(player.rating)}
return board
def getRatingList(self, nicks):
"""
Returns a rating list of players
currently in the lobby by nick
because the client can't link
JID to nick conveniently.
"""
ratinglist = {}
for JID in nicks.keys():
players = db.query(Player).filter_by(jid=str(JID))
if players.first():
if players.first().rating == -1:
ratinglist[nicks[JID]] = {'name': nicks[JID], 'rating': ''}
else:
ratinglist[nicks[JID]] = {'name': nicks[JID], 'rating': str(players.first().rating)}
else:
ratinglist[nicks[JID]] = {'name': nicks[JID], 'rating': ''}
return ratinglist
## Class to tracks all games in the lobby ##
class GameList():
@ -342,14 +360,16 @@ class GameListXmppPlugin(ElementBase):
data[key] = item
return data
## Class for custom boardlist stanza extension ##
## Class for custom boardlist and ratinglist stanza extension ##
class BoardListXmppPlugin(ElementBase):
name = 'query'
namespace = 'jabber:iq:boardlist'
interfaces = ('board')
interfaces = set(('board', 'command'))
sub_interfaces = interfaces
plugin_attrib = 'boardlist'
def addCommand(self, command):
commandXml = ET.fromstring("<command>%s</command>" % command)
self.xml.append(commandXml)
def addItem(self, name, rating):
itemXml = ET.Element("board", {"name": name, "rating": rating})
self.xml.append(itemXml)
@ -488,11 +508,22 @@ class XpartaMuPP(sleekxmpp.ClientXMPP):
traceback.print_exc()
logging.error("Failed to process gamelist request from %s" % iq['from'].bare)
elif 'boardlist' in iq.plugins:
try:
self.leaderboard.getOrCreatePlayer(iq['from'])
self.sendBoardList(iq['from'])
except:
traceback.print_exc()
command = iq['boardlist']['command']
if command == 'getleaderboard':
try:
self.leaderboard.getOrCreatePlayer(iq['from'])
self.sendBoardList(iq['from'])
except:
traceback.print_exc()
logging.error("Failed to process leaderboardlist request from %s" % iq['from'].bare)
elif command == 'getratinglist':
try:
self.leaderboard.getOrCreatePlayer(iq['from'])
self.sendRatingList(iq['from'])
except:
traceback.print_exc()
logging.error("Failed to process ratinglist request from %s" % iq['from'].bare)
else:
logging.error("Failed to process boardlist request from %s" % iq['from'].bare)
else:
logging.error("Unknown 'get' type stanza request from %s" % iq['from'].bare)
@ -543,7 +574,8 @@ class XpartaMuPP(sleekxmpp.ClientXMPP):
if self.leaderboard.getLastRatedMessage() != "":
self.send_message(mto=self.room, mbody=self.leaderboard.getLastRatedMessage(), mtype="groupchat",
mnick=self.nick)
self.sendBoardList()
self.sendBoardList()
self.sendRatingList()
except:
traceback.print_exc()
logging.error("Failed to update game statistics for %s" % iq['from'].bare)
@ -557,7 +589,7 @@ class XpartaMuPP(sleekxmpp.ClientXMPP):
to all clients.
"""
games = self.gameList.getAllGames()
if to == "":
if to == "":
for JID in self.nicks.keys():
stz = GameListXmppPlugin()
@ -620,10 +652,11 @@ class XpartaMuPP(sleekxmpp.ClientXMPP):
iq['type'] = 'result'
for i in board:
stz.addItem(board[i]['name'], board[i]['rating'])
stz.addCommand('boardlist')
iq.setPayload(stz)
if to == "":
for JID in self.nicks.keys():
## Set aditional IQ attributes
## Set additional IQ attributes
iq['to'] = JID
## Try sending the stanza
try:
@ -635,13 +668,50 @@ class XpartaMuPP(sleekxmpp.ClientXMPP):
if str(to) not in self.nicks:
logging.error("No player with the XmPP ID '%s' known to send boardlist to" % str(to))
return
## Set aditional IQ attributes
## Set additional IQ attributes
iq['to'] = to
## Try sending the stanza
try:
iq.send(block=False, now=True)
except:
logging.error("Failed to send leaderboard list")
def sendRatingList(self, to = ""):
"""
Send the rating list.
If no target is passed the rating list is broadcasted
to all clients.
"""
## Pull rating list data and add it to the stanza
ratinglist = self.leaderboard.getRatingList(self.nicks)
stz = BoardListXmppPlugin()
iq = self.Iq()
iq['type'] = 'result'
for i in ratinglist:
stz.addItem(ratinglist[i]['name'], ratinglist[i]['rating'])
stz.addCommand('ratinglist')
iq.setPayload(stz)
if to == "":
for JID in self.nicks.keys():
## Set additional IQ attributes
iq['to'] = JID
## Try sending the stanza
try:
iq.send(block=False, now=True)
except:
logging.error("Failed to send rating list")
else:
## Check recipient exists
if str(to) not in self.nicks:
logging.error("No player with the XmPP ID '%s' known to send ratinglist to" % str(to))
return
## Set additional IQ attributes
iq['to'] = to
## Try sending the stanza
try:
iq.send(block=False, now=True)
except:
logging.error("Failed to send rating list")
## Main Program ##
if __name__ == '__main__':
@ -680,7 +750,7 @@ if __name__ == '__main__':
# Setup logging.
logging.basicConfig(level=opts.loglevel,
format='%(asctime)s %(levelname)-8s %(message)s', datefmt='%m-%d-%y %H:%M:%S')
format='%(asctime)s %(levelname)-8s %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
# XpartaMuPP
xmpp = XpartaMuPP(opts.xlogin+'@'+opts.xdomain+'/CC', opts.xpassword, opts.xroom+'@conference.'+opts.xdomain, opts.xnickname)