Ticket #2504: profile.patch

File profile.patch, 28.2 KB (added by scythetwirler, 10 years ago)
  • binaries/data/mods/public/gui/lobby/lobby.js

     
    191191}
    192192
    193193/**
     194 * Display the profile of the selected player.
     195 * Displays N/A for all stats until updateProfile is called when the stats
     196 *  are actually received from the bot.
     197 */
     198function displayProfile()
     199{
     200    var playerList = Engine.GetGUIObjectByName("playersBox");
     201    Engine.SendGetProfile(playerList.list[playerList.selected]);   
     202    var title = playerList.list_name[playerList.selected];
     203    var rating = stripColorCodes(playerList.list_rating[playerList.selected]).replace(/ /g,'');;
     204    if (rating != "-")
     205        title += " (" + rating + ")";
     206    Engine.GetGUIObjectByName("usernameText").caption = title;
     207    var role = Engine.LobbyGetPlayerRole(playerList.list[playerList.selected]);
     208    // Make the role uppercase.
     209    role = role.charAt(0).toUpperCase() + role.slice(1);
     210    if (role == "Moderator")
     211        role = '[color="0 125 0"]' + translate(role) + '[/color]';
     212    Engine.GetGUIObjectByName("roleText").caption = role;
     213    Engine.GetGUIObjectByName("rankText").caption = "N/A";
     214    Engine.GetGUIObjectByName("highestRatingText").caption = "N/A";
     215    Engine.GetGUIObjectByName("totalGamesText").caption = "N/A";
     216    Engine.GetGUIObjectByName("winsText").caption = "N/A";
     217    Engine.GetGUIObjectByName("lossesText").caption = "N/A";
     218    Engine.GetGUIObjectByName("ratioText").caption = "N/A";
     219}
     220
     221/**
     222 * Update the profile of the selected player with data from the bot.
     223 *
     224 */
     225function updateProfile()
     226{
     227    var playerList = Engine.GetGUIObjectByName("playersBox");
     228    var attributes = Engine.GetProfile();
     229    // Make sure the stats we have received coincide with the selected player.
     230    if (attributes[0].player != playerList.list[playerList.selected])
     231        return;
     232    Engine.GetGUIObjectByName("rankText").caption = attributes[0].rank;
     233    Engine.GetGUIObjectByName("highestRatingText").caption = attributes[0].highestRating;
     234    Engine.GetGUIObjectByName("totalGamesText").caption = attributes[0].totalGamesPlayed;
     235    Engine.GetGUIObjectByName("winsText").caption = attributes[0].wins;
     236    Engine.GetGUIObjectByName("lossesText").caption = attributes[0].losses;
     237    if (attributes[0].losses != 0)
     238        Engine.GetGUIObjectByName("ratioText").caption = (attributes[0].wins / attributes[0].losses).toFixed(2);
     239    else
     240        Engine.GetGUIObjectByName("ratioText").caption = "∞";
     241}
     242
     243/**
    194244 * Update the leaderboard from data cached in C++.
    195245 */
    196246function updateLeaderboard()
     
    575625                case "ratinglist updated":
    576626                    updatePlayerList();
    577627                    break;
     628                case "profile updated":
     629                    updateProfile();
     630                    break;
    578631                }
    579632                break
    580633            }
  • binaries/data/mods/public/gui/lobby/lobby.xml

     
    1919        </action>
    2020
    2121        <!-- Left panel: Player list. -->
    22         <object name="leftPanel" size="20 30 20% 100%-50">
     22        <object name="leftPanel" size="20 30 20% 100%-250">
    2323            <object name="playersBox" style="ModernList" type="olist" size="0 0 100% 100%" font="sans-bold-stroke-13">
    2424                <def id="status" width="26%">
    2525                    <translatableAttribute id="heading">Status</translatableAttribute>
     
    3030                <def id="rating" width="24%">
    3131                    <translatableAttribute id="heading">Rating</translatableAttribute>
    3232                </def>
     33                <action on="SelectionChange">
     34                    if (this.list[this.selected] != null)
     35                    {
     36                        Engine.GetGUIObjectByName("profileArea").hidden = false;
     37                        displayProfile();
     38                    }
     39                    else
     40                        Engine.GetGUIObjectByName("profileArea").hidden = true;
     41                </action>
    3342            </object>
    3443        </object>
    3544
     45        <object name="profilePanel" size="20 100%-245 20% 100%-50">
     46            <object name="profileBox" type="image" sprite="ModernDarkBoxGold" size="0 0 100% 100%">
     47                <object name="profileArea" size="0 0 100% 100%" hidden="true">
     48                    <object name="usernameText" size="0 0 100% 25" type="text" style="ModernLabelText" text_align="center" font="sans-bold-16" />
     49                    <object name="roleText" size="0 25 100% 40" type="text" style="ModernLabelText" text_align="center" font="sans-bold-stroke-12" />
     50                    <object size="0 50 40%+40 70" type="text" style="ModernLabelText" text_align="right" font="sans-bold-stroke-13">
     51                        <translatableAttribute id="caption">Current Rank:</translatableAttribute>
     52                    </object>
     53                    <object name="rankText" size="40%+45 50 100% 70" type="text" style="ModernLabelText" text_align="left" font="sans-bold-stroke-12" />
     54                    <object size="0 70 40%+40 90" type="text" style="ModernLabelText" text_align="right" font="sans-bold-stroke-13">
     55                        <translatableAttribute id="caption">Highest Rating:</translatableAttribute>
     56                    </object>
     57                    <object name="highestRatingText" size="40%+45 70 100% 90" type="text" style="ModernLabelText" text_align="left" font="sans-bold-stroke-12" />
     58                    <object size="0 90 40%+40 110" type="text" style="ModernLabelText" text_align="right" font="sans-bold-stroke-13">
     59                        <translatableAttribute id="caption">Total Games:</translatableAttribute>
     60                    </object>
     61                    <object name="totalGamesText" size="40%+45 90 100% 110" type="text" style="ModernLabelText" text_align="left" font="sans-bold-stroke-12" />
     62                    <object size="0 110 40%+40 130" type="text" style="ModernLabelText" text_align="right" font="sans-bold-stroke-13">
     63                        <translatableAttribute id="caption">Wins:</translatableAttribute>
     64                    </object>
     65                    <object name="winsText" size="40%+45 110 100% 130" type="text" style="ModernLabelText" text_align="left" font="sans-bold-stroke-12" />
     66                    <object size="0 130 40%+40 150" type="text" style="ModernLabelText" text_align="right" font="sans-bold-stroke-13">
     67                        <translatableAttribute id="caption">Losses:</translatableAttribute>
     68                    </object>
     69                    <object name="lossesText" size="40%+45 130 100% 150" type="text" style="ModernLabelText" text_align="left" font="sans-bold-stroke-12" />
     70                    <object size="0 150 40%+40 170" type="text" style="ModernLabelText" text_align="right" font="sans-bold-stroke-13">
     71                        <translatableAttribute id="caption">Win/Loss Ratio:</translatableAttribute>
     72                    </object>
     73                    <object name="ratioText" size="40%+45 150 100% 170" type="text" style="ModernLabelText" text_align="left" font="sans-bold-stroke-12" />
     74                </object>
     75            </object>
     76        </object>
     77
    3678        <object name="leftButtonPanel" size="20 100%-45 20% 100%-20">
    3779            <object type="button" style="ModernButtonRed" size="0 0 100% 100%">
    3880                <translatableAttribute id="caption">Leaderboard</translatableAttribute>
  • source/gui/scripting/ScriptFunctions.cpp

     
    960960    scriptInterface.RegisterFunction<void, &JSI_Lobby::SendGetGameList>("SendGetGameList");
    961961    scriptInterface.RegisterFunction<void, &JSI_Lobby::SendGetBoardList>("SendGetBoardList");
    962962    scriptInterface.RegisterFunction<void, &JSI_Lobby::SendGetRatingList>("SendGetRatingList");
     963    scriptInterface.RegisterFunction<void, std::wstring, &JSI_Lobby::SendGetProfile>("SendGetProfile");
    963964    scriptInterface.RegisterFunction<void, CScriptVal, &JSI_Lobby::SendRegisterGame>("SendRegisterGame");
    964965    scriptInterface.RegisterFunction<void, CScriptVal, &JSI_Lobby::SendGameReport>("SendGameReport");
    965966    scriptInterface.RegisterFunction<void, &JSI_Lobby::SendUnregisterGame>("SendUnregisterGame");
     
    967968    scriptInterface.RegisterFunction<CScriptVal, &JSI_Lobby::GetPlayerList>("GetPlayerList");
    968969    scriptInterface.RegisterFunction<CScriptVal, &JSI_Lobby::GetGameList>("GetGameList");
    969970    scriptInterface.RegisterFunction<CScriptVal, &JSI_Lobby::GetBoardList>("GetBoardList");
     971    scriptInterface.RegisterFunction<CScriptVal, &JSI_Lobby::GetProfile>("GetProfile");
    970972    scriptInterface.RegisterFunction<CScriptVal, &JSI_Lobby::LobbyGuiPollMessage>("LobbyGuiPollMessage");
    971973    scriptInterface.RegisterFunction<void, std::wstring, &JSI_Lobby::LobbySendMessage>("LobbySendMessage");
    972974    scriptInterface.RegisterFunction<void, std::wstring, &JSI_Lobby::LobbySetPlayerPresence>("LobbySetPlayerPresence");
  • source/lobby/IXmppClient.h

     
    3434    virtual void SendIqGetGameList() = 0;
    3535    virtual void SendIqGetBoardList() = 0;
    3636    virtual void SendIqGetRatingList() = 0;
     37    virtual void SendIqGetProfile(const std::string& player) = 0;
    3738    virtual void SendIqGameReport(ScriptInterface& scriptInterface, CScriptVal data) = 0;
    3839    virtual void SendIqRegisterGame(ScriptInterface& scriptInterface, CScriptVal data) = 0;
    3940    virtual void SendIqUnregisterGame() = 0;
     
    5051    virtual CScriptValRooted GUIGetPlayerList(ScriptInterface& scriptInterface) = 0;
    5152    virtual CScriptValRooted GUIGetGameList(ScriptInterface& scriptInterface) = 0;
    5253    virtual CScriptValRooted GUIGetBoardList(ScriptInterface& scriptInterface) = 0;
     54    virtual CScriptValRooted GUIGetProfile(ScriptInterface& scriptInterface) = 0;
    5355
    5456    virtual CScriptValRooted GuiPollMessage(ScriptInterface& scriptInterface) = 0;
    5557    virtual void SendMUCMessage(const std::string& message) = 0;
  • source/lobby/StanzaExtensions.cpp

     
    185185        glooxwrapper::Tag::free(*it);
    186186    m_GameList.clear();
    187187}
     188
     189/******************************************************
     190 * ProfileQuery, a custom IQ Stanza useful for fetching
     191 * user profiles
     192 * Example stanza:
     193 * <profile player="foobar" highestRating="1500" rank="1895" totalGamesPlayed="50"
     194 *  wins="25" losses="25" /><command>foobar</command>
     195 */
     196ProfileQuery::ProfileQuery( const glooxwrapper::Tag* tag ):StanzaExtension( ExtProfileQuery )
     197{
     198    if( !tag || tag->name() != "query" || tag->xmlns() != XMLNS_PROFILE )
     199        return;
     200
     201    const glooxwrapper::Tag* c = tag->findTag_clone( "query/command" );
     202    if (c)
     203        m_Command = c->cdata();
     204    glooxwrapper::Tag::free(c);
     205
     206    const glooxwrapper::ConstTagList profileTags = tag->findTagList_clone( "query/profile" );
     207    glooxwrapper::ConstTagList::const_iterator it = profileTags.begin();
     208    for ( ; it != profileTags.end(); ++it )
     209        m_StanzaProfile.push_back( *it );
     210}
     211
     212/**
     213 * Required by gloox, used to find the Profile element in a received IQ.
     214 */
     215const glooxwrapper::string& ProfileQuery::filterString() const
     216{
     217    static const glooxwrapper::string filter = "/iq/query[@xmlns='" XMLNS_PROFILE "']";
     218    return filter;
     219}
     220
     221/**
     222 * Required by gloox, used to serialize the Profile request into XML for sending.
     223 */
     224glooxwrapper::Tag* ProfileQuery::tag() const
     225{
     226    glooxwrapper::Tag* t = glooxwrapper::Tag::allocate( "query" );
     227    t->setXmlns( XMLNS_PROFILE );
     228
     229    if(!m_Command.empty())
     230        t->addChild(glooxwrapper::Tag::allocate("command", m_Command));
     231
     232    std::vector<const glooxwrapper::Tag*>::const_iterator it = m_StanzaProfile.begin();
     233    for( ; it != m_StanzaProfile.end(); ++it )
     234        t->addChild( (*it)->clone() );
     235
     236    return t;
     237}
     238
     239glooxwrapper::StanzaExtension* ProfileQuery::clone() const
     240{
     241    ProfileQuery* q = new ProfileQuery();
     242    return q;
     243}
     244
     245ProfileQuery::~ProfileQuery()
     246{
     247    std::vector<const glooxwrapper::Tag*>::const_iterator it = m_StanzaProfile.begin();
     248    for( ; it != m_StanzaProfile.end(); ++it )
     249        glooxwrapper::Tag::free(*it);
     250    m_StanzaProfile.clear();
     251}
  • source/lobby/StanzaExtensions.h

     
    3131#define ExtGameReport 1405
    3232#define XMLNS_GAMEREPORT "jabber:iq:gamereport"
    3333
     34/// Global Profile Extension
     35#define ExtProfileQuery 1406
     36#define XMLNS_PROFILE "jabber:iq:profile"
     37
    3438class GameReport : public glooxwrapper::StanzaExtension
    3539{
    3640public:
     
    8791    glooxwrapper::string m_Command;
    8892    std::vector<const glooxwrapper::Tag*> m_StanzaBoardList;
    8993};
     94
     95class ProfileQuery : public glooxwrapper::StanzaExtension
     96{
     97public:
     98    ProfileQuery(const glooxwrapper::Tag* tag = 0);
     99
     100    // Following four methods are all required by gloox
     101    virtual StanzaExtension* newInstance(const glooxwrapper::Tag* tag) const
     102    {
     103        return new ProfileQuery(tag);
     104    }
     105    virtual const glooxwrapper::string& filterString() const;
     106    virtual glooxwrapper::Tag* tag() const;
     107    virtual glooxwrapper::StanzaExtension* clone() const;
     108
     109    ~ProfileQuery();
     110
     111    glooxwrapper::string m_Command;
     112    std::vector<const glooxwrapper::Tag*> m_StanzaProfile;
     113};
    90114#endif
  • source/lobby/XmppClient.cpp

     
    102102    const int mechs = gloox::SaslMechAll ^ gloox::SaslMechPlain;
    103103    m_client->setSASLMechanisms(mechs);
    104104
    105     m_client->registerConnectionListener( this );
     105    m_client->registerConnectionListener(this);
    106106    m_client->setPresence(gloox::Presence::Available, -1);
    107     m_client->disco()->setVersion( "Pyrogenesis", "0.0.17" );
    108     m_client->disco()->setIdentity( "client", "bot" );
     107    m_client->disco()->setVersion("Pyrogenesis", "0.0.17");
     108    m_client->disco()->setIdentity("client", "bot");
    109109    m_client->setCompression(false);
    110110
    111     m_client->registerStanzaExtension( new GameListQuery() );
    112     m_client->registerIqHandler( this, ExtGameListQuery);
     111    m_client->registerStanzaExtension(new GameListQuery());
     112    m_client->registerIqHandler(this, ExtGameListQuery);
    113113
    114     m_client->registerStanzaExtension( new BoardListQuery() );
    115     m_client->registerIqHandler( this, ExtBoardListQuery);
     114    m_client->registerStanzaExtension(new BoardListQuery());
     115    m_client->registerIqHandler(this, ExtBoardListQuery);
    116116
    117     m_client->registerMessageHandler( this );
     117    m_client->registerStanzaExtension(new ProfileQuery());
     118    m_client->registerIqHandler(this, ExtProfileQuery);
    118119
     120    m_client->registerMessageHandler(this);
     121
    119122    // Uncomment to see the raw stanzas
    120123    //m_client->getWrapped()->logInstance().registerLogHandler( gloox::LogLevelDebug, gloox::LogAreaAll, this );
    121124
     
    152155        glooxwrapper::Tag::free(*it);
    153156    for (std::vector<const glooxwrapper::Tag*>::const_iterator it = m_BoardList.begin(); it != m_BoardList.end(); ++it)
    154157        glooxwrapper::Tag::free(*it);
     158    for (std::vector<const glooxwrapper::Tag*>::const_iterator it = m_Profile.begin(); it != m_Profile.end(); ++it)
     159        glooxwrapper::Tag::free(*it);
    155160}
    156161
    157162/// Network
     
    212217        glooxwrapper::Tag::free(*it);
    213218    for (std::vector<const glooxwrapper::Tag*>::const_iterator it = m_BoardList.begin(); it != m_BoardList.end(); ++it)
    214219        glooxwrapper::Tag::free(*it);
     220    for (std::vector<const glooxwrapper::Tag*>::const_iterator it = m_Profile.begin(); it != m_Profile.end(); ++it)
     221        glooxwrapper::Tag::free(*it);
    215222    m_BoardList.clear();
    216223    m_GameList.clear();
    217224    m_PlayerMap.clear();
     225    m_Profile.clear();
    218226
    219227    if(error == gloox::ConnAuthenticationFailed)
    220228        CreateSimpleMessage("system", "authentication failed", "error");
     
    284292}
    285293
    286294/**
     295 * Request the profile data from the server.
     296 */
     297void XmppClient::SendIqGetProfile(const std::string& player)
     298{
     299    glooxwrapper::JID xpartamuppJid(m_xpartamuppId);
     300
     301    // Send IQ
     302    ProfileQuery* b = new ProfileQuery();
     303    b->m_Command = player;
     304    glooxwrapper::IQ iq(gloox::IQ::Get, xpartamuppJid);
     305    iq.addExtension(b);
     306    DbgXMPP("SendIqGetProfile [" << tag_xml(iq) << "]");
     307    m_client->send(iq);
     308}
     309
     310/**
    287311 * Request the rating data from the server.
    288312 */
    289313void XmppClient::SendIqGetRatingList()
     
    512536        scriptInterface.Eval("({})", game);
    513537
    514538        const char* stats[] = { "name", "ip", "state", "nbp", "tnbp", "players", "mapName", "niceMapName", "mapSize", "mapType", "victoryCondition" };
    515         short stats_length = 11;
    516         for (short i = 0; i < stats_length; i++)
     539        for (size_t i = 0; i < ARRAY_SIZE(stats); i++)
    517540            scriptInterface.SetProperty(game.get(), stats[i], wstring_from_utf8((*it)->findAttribute(stats[i]).to_string()));
    518541
    519542        scriptInterface.CallFunctionVoid(gameList.get(), "push", game);
     
    537560        scriptInterface.Eval("({})", board);
    538561
    539562        const char* attributes[] = { "name", "rank", "rating" };
    540         short attributes_length = 3;
    541         for (short i = 0; i < attributes_length; i++)
     563        for (size_t i = 0; i < ARRAY_SIZE(attributes); i++)
    542564            scriptInterface.SetProperty(board.get(), attributes[i], wstring_from_utf8((*it)->findAttribute(attributes[i]).to_string()));
    543565
    544566        scriptInterface.CallFunctionVoid(boardList.get(), "push", board);
     
    547569    return boardList;
    548570}
    549571
     572/**
     573 * Handle requests from the GUI for profile data.
     574 *
     575 * @return A JS array containing the specific user's profile data
     576 */
     577CScriptValRooted XmppClient::GUIGetProfile(ScriptInterface& scriptInterface)
     578{
     579    CScriptValRooted profileFetch;
     580    scriptInterface.Eval("([])", profileFetch);
     581    for(std::vector<const glooxwrapper::Tag*>::const_iterator it = m_Profile.begin(); it != m_Profile.end(); ++it)
     582    {
     583        CScriptValRooted profile;
     584        scriptInterface.Eval("({})", profile);
     585
     586        const char* stats[] = { "player", "totalGamesPlayed", "highestRating", "wins", "losses", "rank" };
     587        for (size_t i = 0; i < ARRAY_SIZE(stats); i++)
     588            scriptInterface.SetProperty(profile.get(), stats[i], wstring_from_utf8((*it)->findAttribute(stats[i]).to_string()));
     589
     590        scriptInterface.CallFunctionVoid(profileFetch.get(), "push", profile);
     591    }
     592
     593    return profileFetch;
     594}
     595
    550596/*****************************************************
    551597 * Message interfaces                                *
    552598 *****************************************************/
     
    636682    {
    637683        const GameListQuery* gq = iq.findExtension<GameListQuery>( ExtGameListQuery );
    638684        const BoardListQuery* bq = iq.findExtension<BoardListQuery>( ExtBoardListQuery );
     685        const ProfileQuery* pq = iq.findExtension<ProfileQuery>( ExtProfileQuery );
    639686        if(gq)
    640687        {
    641688            for(std::vector<const glooxwrapper::Tag*>::const_iterator it = m_GameList.begin(); it != m_GameList.end(); ++it )
     
    672719                CreateSimpleMessage("system", "ratinglist updated", "internal");
    673720            }
    674721        }
     722        if (pq)
     723        {
     724            for(std::vector<const glooxwrapper::Tag*>::const_iterator it = m_Profile.begin(); it != m_Profile.end(); ++it )
     725                glooxwrapper::Tag::free(*it);
     726            m_Profile.clear();
     727
     728            for(std::vector<const glooxwrapper::Tag*>::const_iterator it = pq->m_StanzaProfile.begin(); it != pq->m_StanzaProfile.end(); ++it)
     729                m_Profile.push_back( (*it)->clone() );
     730
     731            CreateSimpleMessage("system", "profile updated", "internal");
     732        }
    675733    }
    676     else if(iq.subtype() == gloox::IQ::Error)
     734    else if (iq.subtype() == gloox::IQ::Error)
    677735    {
    678736        gloox::StanzaError err = iq.error_error();
    679737        std::string msg = StanzaErrorToString(err);
  • source/lobby/XmppClient.h

     
    6161    void SendIqGetGameList();
    6262    void SendIqGetBoardList();
    6363    void SendIqGetRatingList();
     64    void SendIqGetProfile(const std::string& player);
    6465    void SendIqGameReport(ScriptInterface& scriptInterface, CScriptVal data);
    6566    void SendIqRegisterGame(ScriptInterface& scriptInterface, CScriptVal data);
    6667    void SendIqUnregisterGame();
     
    7778    CScriptValRooted GUIGetPlayerList(ScriptInterface& scriptInterface);
    7879    CScriptValRooted GUIGetGameList(ScriptInterface& scriptInterface);
    7980    CScriptValRooted GUIGetBoardList(ScriptInterface& scriptInterface);
     81    CScriptValRooted GUIGetProfile(ScriptInterface& scriptInterface);
    8082    //Script
    8183    ScriptInterface& GetScriptInterface();
    8284
     
    143145    std::vector<const glooxwrapper::Tag*> m_GameList;
    144146    /// List of rankings
    145147    std::vector<const glooxwrapper::Tag*> m_BoardList;
     148    /// Profile data
     149    std::vector<const glooxwrapper::Tag*> m_Profile;
    146150    /// Queue of messages for the GUI
    147151    std::deque<GUIMessage> m_GuiMessageQueue;
    148152    /// Current room subject/topic.
  • source/lobby/scripting/JSInterface_Lobby.cpp

     
    101101    g_XmppClient->SendIqGetRatingList();
    102102}
    103103
     104void JSI_Lobby::SendGetProfile(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), std::wstring player)
     105{
     106    if (!g_XmppClient)
     107        return;
     108    g_XmppClient->SendIqGetProfile(utf8_from_wstring(player));
     109}
     110
    104111void JSI_Lobby::SendGameReport(ScriptInterface::CxPrivate* pCxPrivate, CScriptVal data)
    105112{
    106113    if (!g_XmppClient)
     
    161168    return boardList.get();
    162169}
    163170
     171CScriptVal JSI_Lobby::GetProfile(ScriptInterface::CxPrivate* pCxPrivate)
     172{
     173    if (!g_XmppClient)
     174        return CScriptVal();
     175
     176    CScriptValRooted profileFetch = g_XmppClient->GUIGetProfile(*(pCxPrivate->pScriptInterface));
     177
     178    return profileFetch.get();
     179}
     180
    164181CScriptVal JSI_Lobby::LobbyGuiPollMessage(ScriptInterface::CxPrivate* pCxPrivate)
    165182{
    166183    if (!g_XmppClient)
  • source/lobby/scripting/JSInterface_Lobby.h

     
    3737    void SendGetGameList(ScriptInterface::CxPrivate* pCxPrivate);
    3838    void SendGetBoardList(ScriptInterface::CxPrivate* pCxPrivate);
    3939    void SendGetRatingList(ScriptInterface::CxPrivate* pCxPrivate);
     40    void SendGetProfile(ScriptInterface::CxPrivate* pCxPrivate, std::wstring player);
    4041    void SendGameReport(ScriptInterface::CxPrivate* pCxPrivate, CScriptVal data);
    4142    void SendRegisterGame(ScriptInterface::CxPrivate* pCxPrivate, CScriptVal data);
    4243    void SendUnregisterGame(ScriptInterface::CxPrivate* pCxPrivate);
     
    4445    CScriptVal GetPlayerList(ScriptInterface::CxPrivate* pCxPrivate);
    4546    CScriptVal GetGameList(ScriptInterface::CxPrivate* pCxPrivate);
    4647    CScriptVal GetBoardList(ScriptInterface::CxPrivate* pCxPrivate);
     48    CScriptVal GetProfile(ScriptInterface::CxPrivate* pCxPrivate);
    4749    CScriptVal LobbyGuiPollMessage(ScriptInterface::CxPrivate* pCxPrivate);
    4850    void LobbySendMessage(ScriptInterface::CxPrivate* pCxPrivate, std::wstring message);
    4951    void LobbySetPlayerPresence(ScriptInterface::CxPrivate* pCxPrivate, std::wstring presence);
  • source/tools/XpartaMuPP/LobbyRanking.py

     
    3333    id = Column(Integer, primary_key=True)
    3434    jid = Column(String(255))
    3535    rating = Column(Integer)
     36    highest_rating = Column(Integer)
    3637    games = relationship('Game', secondary='players_info')
    3738    # These two relations really only exist to satisfy the linkage
    3839    # between PlayerInfo and Player and Game and player.
  • source/tools/XpartaMuPP/XpartaMuPP.py

     
    3737  def __init__(self, room):
    3838    self.room = room
    3939    self.lastRated = ""
     40
     41  def getProfile(self, JID):
     42    """
     43      Retrieves the profile for the specified JID
     44    """
     45    stats = {}
     46    player = db.query(Player).filter_by(jid=str(JID))
     47    if player.first():
     48      if player.first().highest_rating != -1:
     49        stats['highestRating'] = str(player.first().highest_rating)
     50      playerID = player.first().id
     51      players = db.query(Player).order_by(Player.rating.desc()).all()
     52      for rank, user in enumerate(players):
     53        if (user.jid == JID):
     54          stats['rank'] = str(rank+1)
     55          break
     56      stats['totalGamesPlayed'] = str(db.query(PlayerInfo).filter_by(player_id=playerID).count())
     57      stats['wins'] = str(db.query(Game).filter_by(winner_id=playerID).count())
     58      stats['losses'] = str(db.query(PlayerInfo).filter_by(player_id=playerID).count() - db.query(Game).filter_by(winner_id=playerID).count())
     59    return stats
     60
    4061  def getOrCreatePlayer(self, JID):
    4162    """
    4263      Stores a player(JID) in the database if they don't yet exist.
     
    180201      name2, player2.rating, player2.rating + rating_adjustment2)
    181202    player1.rating += rating_adjustment1
    182203    player2.rating += rating_adjustment2
     204    if not player1.highest_rating:
     205      player1.highest_rating = -1
     206    if not player2.highest_rating:
     207      player2.highest_rating = -1
     208    if player1.rating > player1.highest_rating:
     209      player1.highest_rating = player1.rating
     210    if player2.rating > player2.highest_rating:
     211      player2.highest_rating = player2.rating
    183212    db.commit()
    184213    return self
    185214
     
    426455      data[key] = item
    427456    return data
    428457
     458## Class for custom profile ##
     459class ProfileXmppPlugin(ElementBase):
     460  name = 'query'
     461  namespace = 'jabber:iq:profile'
     462  interfaces = set(('profile', 'command'))
     463  sub_interfaces = interfaces
     464  plugin_attrib = 'profile'
     465  def addCommand(self, command):
     466    commandXml = ET.fromstring("<command>%s</command>" % command)
     467    self.xml.append(commandXml)
     468  def addItem(self, player, highestRating, rank, totalGamesPlayed, wins, losses):
     469    itemXml = ET.Element("profile", {"player": player, "highestRating": highestRating,
     470                                      "rank" : rank, "totalGamesPlayed" : totalGamesPlayed, "wins" : wins,
     471                                      "losses" : losses})
     472    self.xml.append(itemXml)
     473
    429474## Main class which handles IQ data and sends new data ##
    430475class XpartaMuPP(sleekxmpp.ClientXMPP):
    431476  """
     
    454499    register_stanza_plugin(Iq, GameListXmppPlugin)
    455500    register_stanza_plugin(Iq, BoardListXmppPlugin)
    456501    register_stanza_plugin(Iq, GameReportXmppPlugin)
     502    register_stanza_plugin(Iq, ProfileXmppPlugin)
    457503
    458504    self.register_handler(Callback('Iq Gamelist',
    459505                                       StanzaPath('iq/gamelist'),
     
    467513                                       StanzaPath('iq/gamereport'),
    468514                                       self.iqhandler,
    469515                                       instream=True))
     516                                       
     517    self.register_handler(Callback('Iq Profile',
     518                                       StanzaPath('iq/profile'),
     519                                       self.iqhandler,
     520                                       instream=True))
    470521
    471522    self.add_event_handler("session_start", self.start)
    472523    self.add_event_handler("muc::%s::got_online" % self.room, self.muc_online)
     
    566617            logging.error("Failed to process ratinglist request from %s" % iq['from'].bare)
    567618        else:
    568619          logging.error("Failed to process boardlist request from %s" % iq['from'].bare)
     620      elif 'profile' in iq.plugins:
     621        command = iq['profile']['command']
     622        try:
     623          self.sendProfile(iq['from'], command)
     624        except:
     625          logging.debug("No record found for %s" % command)
    569626      else:
    570627        logging.error("Unknown 'get' type stanza request from %s" % iq['from'].bare)
    571628    elif iq['type'] == 'result':
     
    755812      except:
    756813        logging.error("Failed to send rating list")
    757814
     815  def sendProfile(self, to, player):
     816    """
     817      Send the profile to a specified player.
     818    """
     819    if to == "":
     820      logging.error("Failed to send profile")
     821      return
     822
     823    ## Pull stats and add it to the stanza
     824    for JID in self.nicks.keys():
     825      if self.nicks[JID] == player:
     826        stats = self.leaderboard.getProfile(JID)
     827        break
     828
     829    stz = ProfileXmppPlugin()
     830    iq = self.Iq()
     831    iq['type'] = 'result'
     832   
     833    stz.addItem(player, stats['highestRating'], stats['rank'], stats['totalGamesPlayed'], stats['wins'], stats['losses'])
     834    stz.addCommand(player)
     835    iq.setPayload(stz)
     836    ## Check recipient exists
     837    if str(to) not in self.nicks:
     838      logging.error("No player with the XmPP ID '%s' known to send profile to" % str(to))
     839      return
     840
     841    ## Set additional IQ attributes
     842    iq['to'] = to
     843
     844    ## Try sending the stanza
     845    try:
     846      iq.send(block=False, now=True)
     847    except:
     848      traceback.print_exc()
     849      logging.error("Failed to send profile")
     850
    758851## Main Program ##
    759852if __name__ == '__main__':
    760853  # Setup the command line arguments.