Ticket #2504: profile.patch
File profile.patch, 28.2 KB (added by , 10 years ago) |
---|
-
binaries/data/mods/public/gui/lobby/lobby.js
191 191 } 192 192 193 193 /** 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 */ 198 function 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 */ 225 function 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 /** 194 244 * Update the leaderboard from data cached in C++. 195 245 */ 196 246 function updateLeaderboard() … … 575 625 case "ratinglist updated": 576 626 updatePlayerList(); 577 627 break; 628 case "profile updated": 629 updateProfile(); 630 break; 578 631 } 579 632 break 580 633 } -
binaries/data/mods/public/gui/lobby/lobby.xml
19 19 </action> 20 20 21 21 <!-- Left panel: Player list. --> 22 <object name="leftPanel" size="20 30 20% 100%- 50">22 <object name="leftPanel" size="20 30 20% 100%-250"> 23 23 <object name="playersBox" style="ModernList" type="olist" size="0 0 100% 100%" font="sans-bold-stroke-13"> 24 24 <def id="status" width="26%"> 25 25 <translatableAttribute id="heading">Status</translatableAttribute> … … 30 30 <def id="rating" width="24%"> 31 31 <translatableAttribute id="heading">Rating</translatableAttribute> 32 32 </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> 33 42 </object> 34 43 </object> 35 44 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 36 78 <object name="leftButtonPanel" size="20 100%-45 20% 100%-20"> 37 79 <object type="button" style="ModernButtonRed" size="0 0 100% 100%"> 38 80 <translatableAttribute id="caption">Leaderboard</translatableAttribute> -
source/gui/scripting/ScriptFunctions.cpp
960 960 scriptInterface.RegisterFunction<void, &JSI_Lobby::SendGetGameList>("SendGetGameList"); 961 961 scriptInterface.RegisterFunction<void, &JSI_Lobby::SendGetBoardList>("SendGetBoardList"); 962 962 scriptInterface.RegisterFunction<void, &JSI_Lobby::SendGetRatingList>("SendGetRatingList"); 963 scriptInterface.RegisterFunction<void, std::wstring, &JSI_Lobby::SendGetProfile>("SendGetProfile"); 963 964 scriptInterface.RegisterFunction<void, CScriptVal, &JSI_Lobby::SendRegisterGame>("SendRegisterGame"); 964 965 scriptInterface.RegisterFunction<void, CScriptVal, &JSI_Lobby::SendGameReport>("SendGameReport"); 965 966 scriptInterface.RegisterFunction<void, &JSI_Lobby::SendUnregisterGame>("SendUnregisterGame"); … … 967 968 scriptInterface.RegisterFunction<CScriptVal, &JSI_Lobby::GetPlayerList>("GetPlayerList"); 968 969 scriptInterface.RegisterFunction<CScriptVal, &JSI_Lobby::GetGameList>("GetGameList"); 969 970 scriptInterface.RegisterFunction<CScriptVal, &JSI_Lobby::GetBoardList>("GetBoardList"); 971 scriptInterface.RegisterFunction<CScriptVal, &JSI_Lobby::GetProfile>("GetProfile"); 970 972 scriptInterface.RegisterFunction<CScriptVal, &JSI_Lobby::LobbyGuiPollMessage>("LobbyGuiPollMessage"); 971 973 scriptInterface.RegisterFunction<void, std::wstring, &JSI_Lobby::LobbySendMessage>("LobbySendMessage"); 972 974 scriptInterface.RegisterFunction<void, std::wstring, &JSI_Lobby::LobbySetPlayerPresence>("LobbySetPlayerPresence"); -
source/lobby/IXmppClient.h
34 34 virtual void SendIqGetGameList() = 0; 35 35 virtual void SendIqGetBoardList() = 0; 36 36 virtual void SendIqGetRatingList() = 0; 37 virtual void SendIqGetProfile(const std::string& player) = 0; 37 38 virtual void SendIqGameReport(ScriptInterface& scriptInterface, CScriptVal data) = 0; 38 39 virtual void SendIqRegisterGame(ScriptInterface& scriptInterface, CScriptVal data) = 0; 39 40 virtual void SendIqUnregisterGame() = 0; … … 50 51 virtual CScriptValRooted GUIGetPlayerList(ScriptInterface& scriptInterface) = 0; 51 52 virtual CScriptValRooted GUIGetGameList(ScriptInterface& scriptInterface) = 0; 52 53 virtual CScriptValRooted GUIGetBoardList(ScriptInterface& scriptInterface) = 0; 54 virtual CScriptValRooted GUIGetProfile(ScriptInterface& scriptInterface) = 0; 53 55 54 56 virtual CScriptValRooted GuiPollMessage(ScriptInterface& scriptInterface) = 0; 55 57 virtual void SendMUCMessage(const std::string& message) = 0; -
source/lobby/StanzaExtensions.cpp
185 185 glooxwrapper::Tag::free(*it); 186 186 m_GameList.clear(); 187 187 } 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 */ 196 ProfileQuery::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 */ 215 const 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 */ 224 glooxwrapper::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 239 glooxwrapper::StanzaExtension* ProfileQuery::clone() const 240 { 241 ProfileQuery* q = new ProfileQuery(); 242 return q; 243 } 244 245 ProfileQuery::~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
31 31 #define ExtGameReport 1405 32 32 #define XMLNS_GAMEREPORT "jabber:iq:gamereport" 33 33 34 /// Global Profile Extension 35 #define ExtProfileQuery 1406 36 #define XMLNS_PROFILE "jabber:iq:profile" 37 34 38 class GameReport : public glooxwrapper::StanzaExtension 35 39 { 36 40 public: … … 87 91 glooxwrapper::string m_Command; 88 92 std::vector<const glooxwrapper::Tag*> m_StanzaBoardList; 89 93 }; 94 95 class ProfileQuery : public glooxwrapper::StanzaExtension 96 { 97 public: 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 }; 90 114 #endif -
source/lobby/XmppClient.cpp
102 102 const int mechs = gloox::SaslMechAll ^ gloox::SaslMechPlain; 103 103 m_client->setSASLMechanisms(mechs); 104 104 105 m_client->registerConnectionListener( this);105 m_client->registerConnectionListener(this); 106 106 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"); 109 109 m_client->setCompression(false); 110 110 111 m_client->registerStanzaExtension( new GameListQuery());112 m_client->registerIqHandler( 111 m_client->registerStanzaExtension(new GameListQuery()); 112 m_client->registerIqHandler(this, ExtGameListQuery); 113 113 114 m_client->registerStanzaExtension( new BoardListQuery());115 m_client->registerIqHandler( 114 m_client->registerStanzaExtension(new BoardListQuery()); 115 m_client->registerIqHandler(this, ExtBoardListQuery); 116 116 117 m_client->registerMessageHandler( this ); 117 m_client->registerStanzaExtension(new ProfileQuery()); 118 m_client->registerIqHandler(this, ExtProfileQuery); 118 119 120 m_client->registerMessageHandler(this); 121 119 122 // Uncomment to see the raw stanzas 120 123 //m_client->getWrapped()->logInstance().registerLogHandler( gloox::LogLevelDebug, gloox::LogAreaAll, this ); 121 124 … … 152 155 glooxwrapper::Tag::free(*it); 153 156 for (std::vector<const glooxwrapper::Tag*>::const_iterator it = m_BoardList.begin(); it != m_BoardList.end(); ++it) 154 157 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); 155 160 } 156 161 157 162 /// Network … … 212 217 glooxwrapper::Tag::free(*it); 213 218 for (std::vector<const glooxwrapper::Tag*>::const_iterator it = m_BoardList.begin(); it != m_BoardList.end(); ++it) 214 219 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); 215 222 m_BoardList.clear(); 216 223 m_GameList.clear(); 217 224 m_PlayerMap.clear(); 225 m_Profile.clear(); 218 226 219 227 if(error == gloox::ConnAuthenticationFailed) 220 228 CreateSimpleMessage("system", "authentication failed", "error"); … … 284 292 } 285 293 286 294 /** 295 * Request the profile data from the server. 296 */ 297 void 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 /** 287 311 * Request the rating data from the server. 288 312 */ 289 313 void XmppClient::SendIqGetRatingList() … … 512 536 scriptInterface.Eval("({})", game); 513 537 514 538 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++) 517 540 scriptInterface.SetProperty(game.get(), stats[i], wstring_from_utf8((*it)->findAttribute(stats[i]).to_string())); 518 541 519 542 scriptInterface.CallFunctionVoid(gameList.get(), "push", game); … … 537 560 scriptInterface.Eval("({})", board); 538 561 539 562 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++) 542 564 scriptInterface.SetProperty(board.get(), attributes[i], wstring_from_utf8((*it)->findAttribute(attributes[i]).to_string())); 543 565 544 566 scriptInterface.CallFunctionVoid(boardList.get(), "push", board); … … 547 569 return boardList; 548 570 } 549 571 572 /** 573 * Handle requests from the GUI for profile data. 574 * 575 * @return A JS array containing the specific user's profile data 576 */ 577 CScriptValRooted 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 550 596 /***************************************************** 551 597 * Message interfaces * 552 598 *****************************************************/ … … 636 682 { 637 683 const GameListQuery* gq = iq.findExtension<GameListQuery>( ExtGameListQuery ); 638 684 const BoardListQuery* bq = iq.findExtension<BoardListQuery>( ExtBoardListQuery ); 685 const ProfileQuery* pq = iq.findExtension<ProfileQuery>( ExtProfileQuery ); 639 686 if(gq) 640 687 { 641 688 for(std::vector<const glooxwrapper::Tag*>::const_iterator it = m_GameList.begin(); it != m_GameList.end(); ++it ) … … 672 719 CreateSimpleMessage("system", "ratinglist updated", "internal"); 673 720 } 674 721 } 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 } 675 733 } 676 else if (iq.subtype() == gloox::IQ::Error)734 else if (iq.subtype() == gloox::IQ::Error) 677 735 { 678 736 gloox::StanzaError err = iq.error_error(); 679 737 std::string msg = StanzaErrorToString(err); -
source/lobby/XmppClient.h
61 61 void SendIqGetGameList(); 62 62 void SendIqGetBoardList(); 63 63 void SendIqGetRatingList(); 64 void SendIqGetProfile(const std::string& player); 64 65 void SendIqGameReport(ScriptInterface& scriptInterface, CScriptVal data); 65 66 void SendIqRegisterGame(ScriptInterface& scriptInterface, CScriptVal data); 66 67 void SendIqUnregisterGame(); … … 77 78 CScriptValRooted GUIGetPlayerList(ScriptInterface& scriptInterface); 78 79 CScriptValRooted GUIGetGameList(ScriptInterface& scriptInterface); 79 80 CScriptValRooted GUIGetBoardList(ScriptInterface& scriptInterface); 81 CScriptValRooted GUIGetProfile(ScriptInterface& scriptInterface); 80 82 //Script 81 83 ScriptInterface& GetScriptInterface(); 82 84 … … 143 145 std::vector<const glooxwrapper::Tag*> m_GameList; 144 146 /// List of rankings 145 147 std::vector<const glooxwrapper::Tag*> m_BoardList; 148 /// Profile data 149 std::vector<const glooxwrapper::Tag*> m_Profile; 146 150 /// Queue of messages for the GUI 147 151 std::deque<GUIMessage> m_GuiMessageQueue; 148 152 /// Current room subject/topic. -
source/lobby/scripting/JSInterface_Lobby.cpp
101 101 g_XmppClient->SendIqGetRatingList(); 102 102 } 103 103 104 void 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 104 111 void JSI_Lobby::SendGameReport(ScriptInterface::CxPrivate* pCxPrivate, CScriptVal data) 105 112 { 106 113 if (!g_XmppClient) … … 161 168 return boardList.get(); 162 169 } 163 170 171 CScriptVal 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 164 181 CScriptVal JSI_Lobby::LobbyGuiPollMessage(ScriptInterface::CxPrivate* pCxPrivate) 165 182 { 166 183 if (!g_XmppClient) -
source/lobby/scripting/JSInterface_Lobby.h
37 37 void SendGetGameList(ScriptInterface::CxPrivate* pCxPrivate); 38 38 void SendGetBoardList(ScriptInterface::CxPrivate* pCxPrivate); 39 39 void SendGetRatingList(ScriptInterface::CxPrivate* pCxPrivate); 40 void SendGetProfile(ScriptInterface::CxPrivate* pCxPrivate, std::wstring player); 40 41 void SendGameReport(ScriptInterface::CxPrivate* pCxPrivate, CScriptVal data); 41 42 void SendRegisterGame(ScriptInterface::CxPrivate* pCxPrivate, CScriptVal data); 42 43 void SendUnregisterGame(ScriptInterface::CxPrivate* pCxPrivate); … … 44 45 CScriptVal GetPlayerList(ScriptInterface::CxPrivate* pCxPrivate); 45 46 CScriptVal GetGameList(ScriptInterface::CxPrivate* pCxPrivate); 46 47 CScriptVal GetBoardList(ScriptInterface::CxPrivate* pCxPrivate); 48 CScriptVal GetProfile(ScriptInterface::CxPrivate* pCxPrivate); 47 49 CScriptVal LobbyGuiPollMessage(ScriptInterface::CxPrivate* pCxPrivate); 48 50 void LobbySendMessage(ScriptInterface::CxPrivate* pCxPrivate, std::wstring message); 49 51 void LobbySetPlayerPresence(ScriptInterface::CxPrivate* pCxPrivate, std::wstring presence); -
source/tools/XpartaMuPP/LobbyRanking.py
33 33 id = Column(Integer, primary_key=True) 34 34 jid = Column(String(255)) 35 35 rating = Column(Integer) 36 highest_rating = Column(Integer) 36 37 games = relationship('Game', secondary='players_info') 37 38 # These two relations really only exist to satisfy the linkage 38 39 # between PlayerInfo and Player and Game and player. -
source/tools/XpartaMuPP/XpartaMuPP.py
37 37 def __init__(self, room): 38 38 self.room = room 39 39 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 40 61 def getOrCreatePlayer(self, JID): 41 62 """ 42 63 Stores a player(JID) in the database if they don't yet exist. … … 180 201 name2, player2.rating, player2.rating + rating_adjustment2) 181 202 player1.rating += rating_adjustment1 182 203 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 183 212 db.commit() 184 213 return self 185 214 … … 426 455 data[key] = item 427 456 return data 428 457 458 ## Class for custom profile ## 459 class 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 429 474 ## Main class which handles IQ data and sends new data ## 430 475 class XpartaMuPP(sleekxmpp.ClientXMPP): 431 476 """ … … 454 499 register_stanza_plugin(Iq, GameListXmppPlugin) 455 500 register_stanza_plugin(Iq, BoardListXmppPlugin) 456 501 register_stanza_plugin(Iq, GameReportXmppPlugin) 502 register_stanza_plugin(Iq, ProfileXmppPlugin) 457 503 458 504 self.register_handler(Callback('Iq Gamelist', 459 505 StanzaPath('iq/gamelist'), … … 467 513 StanzaPath('iq/gamereport'), 468 514 self.iqhandler, 469 515 instream=True)) 516 517 self.register_handler(Callback('Iq Profile', 518 StanzaPath('iq/profile'), 519 self.iqhandler, 520 instream=True)) 470 521 471 522 self.add_event_handler("session_start", self.start) 472 523 self.add_event_handler("muc::%s::got_online" % self.room, self.muc_online) … … 566 617 logging.error("Failed to process ratinglist request from %s" % iq['from'].bare) 567 618 else: 568 619 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) 569 626 else: 570 627 logging.error("Unknown 'get' type stanza request from %s" % iq['from'].bare) 571 628 elif iq['type'] == 'result': … … 755 812 except: 756 813 logging.error("Failed to send rating list") 757 814 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 758 851 ## Main Program ## 759 852 if __name__ == '__main__': 760 853 # Setup the command line arguments.