Ticket #2504: profile3.patch
File profile3.patch, 38.6 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 * @param caller From which screen is the user requesting data from? 199 */ 200 function displayProfile(caller) 201 { 202 var playerList, rating; 203 if (caller == "leaderboard") 204 playerList = Engine.GetGUIObjectByName("leaderboardBox"); 205 else if (caller == "lobbylist") 206 playerList = Engine.GetGUIObjectByName("playersBox"); 207 else if (caller == "fetch") 208 { 209 Engine.SendGetProfile(Engine.GetGUIObjectByName("fetchInput").caption); 210 return; 211 } 212 else 213 return; 214 215 if (!playerList.list[playerList.selected]) 216 { 217 Engine.GetGUIObjectByName("profileArea").hidden = true; 218 return; 219 } 220 Engine.GetGUIObjectByName("profileArea").hidden = false; 221 222 Engine.SendGetProfile(playerList.list[playerList.selected]); 223 224 var user = playerList.list_name[playerList.selected]; 225 var role = Engine.LobbyGetPlayerRole(playerList.list[playerList.selected]); 226 var userList = Engine.GetGUIObjectByName("playersBox"); 227 if (role && caller == "lobbylist") 228 { 229 // Make the role uppercase. 230 role = role.charAt(0).toUpperCase() + role.slice(1); 231 if (role == "Moderator") 232 role = '[color="0 125 0"]' + translate(role) + '[/color]'; 233 } 234 else 235 role = ""; 236 237 Engine.GetGUIObjectByName("usernameText").caption = user; 238 Engine.GetGUIObjectByName("roleText").caption = translate(role); 239 Engine.GetGUIObjectByName("rankText").caption = translate("N/A"); 240 Engine.GetGUIObjectByName("highestRatingText").caption = translate("N/A"); 241 Engine.GetGUIObjectByName("totalGamesText").caption = translate("N/A"); 242 Engine.GetGUIObjectByName("winsText").caption = translate("N/A"); 243 Engine.GetGUIObjectByName("lossesText").caption = translate("N/A"); 244 Engine.GetGUIObjectByName("ratioText").caption = translate("N/A"); 245 } 246 247 /** 248 * Update the profile of the selected player with data from the bot. 249 * 250 */ 251 function updateProfile() 252 { 253 var playerList, user; 254 var attributes = Engine.GetProfile(); 255 256 if (!Engine.GetGUIObjectByName("profileFetch").hidden) 257 { 258 user = attributes[0].player; 259 if (attributes[0].rating == "-2") // Profile not found code 260 { 261 Engine.GetGUIObjectByName("profileWindowArea").hidden = true; 262 Engine.GetGUIObjectByName("profileErrorText").hidden = false; 263 return; 264 } 265 Engine.GetGUIObjectByName("profileWindowArea").hidden = false; 266 Engine.GetGUIObjectByName("profileErrorText").hidden = true; 267 268 if (attributes[0].rating != "") 269 user = sprintf(translate("%(nick)s (%(rating)s)"), { nick: user, rating: attributes[0].rating }); 270 271 Engine.GetGUIObjectByName("profileUsernameText").caption = user; 272 Engine.GetGUIObjectByName("profileRankText").caption = attributes[0].rank; 273 Engine.GetGUIObjectByName("profileHighestRatingText").caption = attributes[0].highestRating; 274 Engine.GetGUIObjectByName("profileTotalGamesText").caption = attributes[0].totalGamesPlayed; 275 Engine.GetGUIObjectByName("profileWinsText").caption = attributes[0].wins; 276 Engine.GetGUIObjectByName("profileLossesText").caption = attributes[0].losses; 277 278 var winRate = (attributes[0].wins / attributes[0].totalGamesPlayed * 100).toFixed(2); 279 if (attributes[0].totalGamesPlayed != 0) 280 Engine.GetGUIObjectByName("profileRatioText").caption = sprintf(translate("%(percentage)s%%"), { percentage: winRate }); 281 else 282 Engine.GetGUIObjectByName("profileRatioText").caption = translate("-"); 283 return; 284 } 285 else if (!Engine.GetGUIObjectByName("leaderboard").hidden) 286 playerList = Engine.GetGUIObjectByName("leaderboardBox"); 287 else 288 playerList = Engine.GetGUIObjectByName("playersBox"); 289 290 if (attributes[0].rating == "-2") 291 return; 292 // Make sure the stats we have received coincide with the selected player. 293 if (attributes[0].player != playerList.list[playerList.selected]) 294 return; 295 user = playerList.list_name[playerList.selected]; 296 if (attributes[0].rating != "") 297 user = sprintf(translate("%(nick)s (%(rating)s)"), { nick: user, rating: attributes[0].rating }); 298 299 Engine.GetGUIObjectByName("usernameText").caption = user; 300 Engine.GetGUIObjectByName("rankText").caption = attributes[0].rank; 301 Engine.GetGUIObjectByName("highestRatingText").caption = attributes[0].highestRating; 302 Engine.GetGUIObjectByName("totalGamesText").caption = attributes[0].totalGamesPlayed; 303 Engine.GetGUIObjectByName("winsText").caption = attributes[0].wins; 304 Engine.GetGUIObjectByName("lossesText").caption = attributes[0].losses; 305 306 var winRate = (attributes[0].wins / attributes[0].totalGamesPlayed * 100).toFixed(2); 307 if (attributes[0].totalGamesPlayed != 0) 308 Engine.GetGUIObjectByName("ratioText").caption = sprintf(translate("%(percentage)s%%"), { percentage: winRate }); 309 else 310 Engine.GetGUIObjectByName("ratioText").caption = translate("-"); 311 } 312 313 /** 194 314 * Update the leaderboard from data cached in C++. 195 315 */ 196 316 function updateLeaderboard() … … 592 712 case "ratinglist updated": 593 713 updatePlayerList(); 594 714 break; 715 case "profile updated": 716 updateProfile(); 717 break; 595 718 } 596 break 719 break; 597 720 } 598 721 break; 599 722 default: -
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%-280"> 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 displayProfile("lobbylist"); 35 </action> 33 36 </object> 34 37 </object> 35 38 36 <object name="leftButtonPanel" size="20 100%-45 20% 100%-20"> 37 <object type="button" style="ModernButtonRed" size="0 0 100% 100%"> 39 <object name="profilePanel" size="20 100%-275 20% 100%-80"> 40 <object name="profileBox" type="image" sprite="ModernDarkBoxGold" size="0 0 100% 100%"> 41 <object name="profileArea" size="0 0 100% 100%" hidden="true"> 42 <object name="usernameText" size="0 0 100% 45" type="text" style="ModernLabelText" text_align="center" font="sans-bold-16" /> 43 <object name="roleText" size="0 45 100% 70" type="text" style="ModernLabelText" text_align="center" font="sans-bold-stroke-12" /> 44 <object size="0 70 40%+40 90" type="text" style="ModernLabelText" text_align="right" font="sans-bold-stroke-13"> 45 <translatableAttribute id="caption">Current Rank:</translatableAttribute> 46 </object> 47 <object name="rankText" size="40%+45 70 100% 90" type="text" style="ModernLabelText" text_align="left" font="sans-bold-stroke-12" /> 48 <object size="0 90 40%+40 110" type="text" style="ModernLabelText" text_align="right" font="sans-bold-stroke-13"> 49 <translatableAttribute id="caption">Highest Rating:</translatableAttribute> 50 </object> 51 <object name="highestRatingText" size="40%+45 90 100% 110" type="text" style="ModernLabelText" text_align="left" font="sans-bold-stroke-12" /> 52 <object size="0 110 40%+40 130" type="text" style="ModernLabelText" text_align="right" font="sans-bold-stroke-13"> 53 <translatableAttribute id="caption">Total Games:</translatableAttribute> 54 </object> 55 <object name="totalGamesText" size="40%+45 110 100% 130" type="text" style="ModernLabelText" text_align="left" font="sans-bold-stroke-12" /> 56 <object size="0 130 40%+40 150" type="text" style="ModernLabelText" text_align="right" font="sans-bold-stroke-13"> 57 <translatableAttribute id="caption">Wins:</translatableAttribute> 58 </object> 59 <object name="winsText" size="40%+45 130 100% 150" type="text" style="ModernLabelText" text_align="left" font="sans-bold-stroke-12" /> 60 <object size="0 150 40%+40 170" type="text" style="ModernLabelText" text_align="right" font="sans-bold-stroke-13"> 61 <translatableAttribute id="caption">Losses:</translatableAttribute> 62 </object> 63 <object name="lossesText" size="40%+45 150 100% 170" type="text" style="ModernLabelText" text_align="left" font="sans-bold-stroke-12" /> 64 <object size="0 170 40%+40 190" type="text" style="ModernLabelText" text_align="right" font="sans-bold-stroke-13"> 65 <translatableAttribute id="caption">Win Rate:</translatableAttribute> 66 </object> 67 <object name="ratioText" size="40%+45 170 100% 190" type="text" style="ModernLabelText" text_align="left" font="sans-bold-stroke-12" /> 68 </object> 69 </object> 70 </object> 71 72 <object name="leftButtonPanel" size="20 100%-75 20% 100%-20"> 73 <object type="button" style="ModernButtonRed" size="0 0 100% 25"> 38 74 <translatableAttribute id="caption">Leaderboard</translatableAttribute> 39 <action on="Press">Engine.GetGUIObjectByName("leaderboard").hidden = false;Engine.GetGUIObjectByName("leaderboardFade").hidden = false;</action> 75 <action on="Press"> 76 Engine.GetGUIObjectByName("leaderboard").hidden = false; 77 Engine.GetGUIObjectByName("fade").hidden = false; 78 displayProfile("leaderboard"); 79 </action> 40 80 </object> 81 <object type="button" style="ModernButtonRed" size="0 30 100% 100%"> 82 <translatableAttribute id="caption">User Profile Lookup</translatableAttribute> 83 <action on="Press"> 84 Engine.GetGUIObjectByName("profileFetch").hidden = false; 85 Engine.GetGUIObjectByName("fade").hidden = false; 86 </action> 87 </object> 41 88 </object> 42 89 43 90 <!-- Right panel: Game details. --> … … 191 238 192 239 <!-- START Window for leaderboard stats --> 193 240 <!-- Add a translucent black background to fade out the menu page --> 194 <object hidden="true" name=" leaderboardFade" type="image" z="100" sprite="ModernFade"/>241 <object hidden="true" name="fade" type="image" z="100" sprite="ModernFade"/> 195 242 <object hidden="true" name="leaderboard" type="image" style="ModernDialog" size="50%-224 50%-160 50%+224 50%+160" z="101"> 196 243 <object style="ModernLabelText" type="text" size="50%-128 -18 50%+128 14"> 197 244 <translatableAttribute id="caption">Leaderboard</translatableAttribute> … … 209 256 <def id="rating" color="255 255 255" width="30%"> 210 257 <translatableAttribute id="heading">Rating</translatableAttribute> 211 258 </def> 259 <action on="SelectionChange"> 260 displayProfile("leaderboard"); 261 </action> 212 262 </object> 213 263 <object type="button" style="ModernButtonRed" size="50%-133 100%-45 50%-5 100%-17"> 214 264 <translatableAttribute id="caption">Back</translatableAttribute> 215 <action on="Press">Engine.GetGUIObjectByName("leaderboard").hidden = true;Engine.GetGUIObjectByName("leaderboardFade").hidden = true;</action> 265 <action on="Press"> 266 Engine.GetGUIObjectByName("leaderboard").hidden = true; 267 Engine.GetGUIObjectByName("fade").hidden = true; 268 displayProfile("lobbylist"); 269 </action> 216 270 </object> 217 271 <object type="button" style="ModernButtonRed" size="50%+5 100%-45 50%+133 100%-17"> 218 272 <translatableAttribute id="caption">Update</translatableAttribute> … … 220 274 </object> 221 275 </object> 222 276 <!-- END Window for leaderboard stats --> 277 <object hidden="true" name="profileFetch" type="image" style="ModernDialog" size="50%-224 50%-160 50%+224 50%+160" z="102"> 278 <object style="ModernLabelText" type="text" size="50%-128 -18 50%+128 14"> 279 <translatableAttribute id="caption">User Profile Lookup</translatableAttribute> 280 </object> 281 <object type="text" size="15 25 40% 50" text_align="right" textcolor="white"> 282 <translatableAttribute id="caption">Enter username:</translatableAttribute> 283 </object> 284 <object name="fetchInput" size="40%+10 25 100%-25 50" type="input" style="ModernInput" font="sans-13"> 285 <action on="Press">displayProfile("fetch");</action> 286 </object> 287 <object type="button" style="ModernButtonRed" size="50%-64 60 50%+64 85"> 288 <translatableAttribute id="caption">View Profile</translatableAttribute> 289 <action on="Press">displayProfile("fetch");</action> 290 </object> 291 <object name="profileWindowPanel" size="25 95 100%-25 100%-60"> 292 <object name="profileWindowBox" type="image" sprite="ModernDarkBoxGold" size="0 0 100% 100%"> 293 <object name="profileWindowArea" size="0 0 100% 100%" hidden="false"> 294 <object name="profileUsernameText" size="0 0 100% 25" type="text" style="ModernLabelText" text_align="center" font="sans-bold-16" /> 295 <object size="0 30 40%+40 50" type="text" style="ModernLabelText" text_align="right" font="sans-bold-stroke-13"> 296 <translatableAttribute id="caption">Current Rank:</translatableAttribute> 297 </object> 298 <object name="profileRankText" size="40%+45 30 100% 50" type="text" style="ModernLabelText" text_align="left" font="sans-bold-stroke-12" /> 299 <object size="0 50 40%+40 70" type="text" style="ModernLabelText" text_align="right" font="sans-bold-stroke-13"> 300 <translatableAttribute id="caption">Highest Rating:</translatableAttribute> 301 </object> 302 <object name="profileHighestRatingText" size="40%+45 50 100% 70" type="text" style="ModernLabelText" text_align="left" font="sans-bold-stroke-12" /> 303 <object size="0 70 40%+40 90" type="text" style="ModernLabelText" text_align="right" font="sans-bold-stroke-13"> 304 <translatableAttribute id="caption">Total Games:</translatableAttribute> 305 </object> 306 <object name="profileTotalGamesText" size="40%+45 70 100% 90" type="text" style="ModernLabelText" text_align="left" font="sans-bold-stroke-12" /> 307 <object size="0 90 40%+40 110" type="text" style="ModernLabelText" text_align="right" font="sans-bold-stroke-13"> 308 <translatableAttribute id="caption">Wins:</translatableAttribute> 309 </object> 310 <object name="profileWinsText" size="40%+45 90 100% 110" type="text" style="ModernLabelText" text_align="left" font="sans-bold-stroke-12" /> 311 <object size="0 110 40%+40 130" type="text" style="ModernLabelText" text_align="right" font="sans-bold-stroke-13"> 312 <translatableAttribute id="caption">Losses:</translatableAttribute> 313 </object> 314 <object name="profileLossesText" size="40%+45 110 100% 130" type="text" style="ModernLabelText" text_align="left" font="sans-bold-stroke-12" /> 315 <object size="0 130 40%+40 150" type="text" style="ModernLabelText" text_align="right" font="sans-bold-stroke-13"> 316 <translatableAttribute id="caption">Win Rate:</translatableAttribute> 317 </object> 318 <object name="profileRatioText" size="40%+45 130 100% 150" type="text" style="ModernLabelText" text_align="left" font="sans-bold-stroke-12" /> 319 </object> 320 <object name="profileErrorText" size="25% 25% 75% 75%" type="text" style="ModernLabelText" text_align="center" font="sans-bold-stroke-13" hidden="true"> 321 <translatableAttribute id="caption">Player not found.</translatableAttribute> 322 </object> 323 </object> 324 </object> 325 <object type="button" style="ModernButtonRed" size="50%-64 100%-50 50%+64 100%-25"> 326 <translatableAttribute id="caption">Back</translatableAttribute> 327 <action on="Press"> 328 Engine.GetGUIObjectByName("profileFetch").hidden = true; 329 Engine.GetGUIObjectByName("fade").hidden = true; 330 </action> 331 </object> 332 </object> 223 333 </object> 224 334 </objects> -
source/gui/scripting/ScriptFunctions.cpp
1027 1027 scriptInterface.RegisterFunction<void, &JSI_Lobby::SendGetGameList>("SendGetGameList"); 1028 1028 scriptInterface.RegisterFunction<void, &JSI_Lobby::SendGetBoardList>("SendGetBoardList"); 1029 1029 scriptInterface.RegisterFunction<void, &JSI_Lobby::SendGetRatingList>("SendGetRatingList"); 1030 scriptInterface.RegisterFunction<void, std::wstring, &JSI_Lobby::SendGetProfile>("SendGetProfile"); 1030 1031 scriptInterface.RegisterFunction<void, CScriptVal, &JSI_Lobby::SendRegisterGame>("SendRegisterGame"); 1031 1032 scriptInterface.RegisterFunction<void, CScriptVal, &JSI_Lobby::SendGameReport>("SendGameReport"); 1032 1033 scriptInterface.RegisterFunction<void, &JSI_Lobby::SendUnregisterGame>("SendUnregisterGame"); … … 1034 1035 scriptInterface.RegisterFunction<CScriptVal, &JSI_Lobby::GetPlayerList>("GetPlayerList"); 1035 1036 scriptInterface.RegisterFunction<CScriptVal, &JSI_Lobby::GetGameList>("GetGameList"); 1036 1037 scriptInterface.RegisterFunction<CScriptVal, &JSI_Lobby::GetBoardList>("GetBoardList"); 1038 scriptInterface.RegisterFunction<CScriptVal, &JSI_Lobby::GetProfile>("GetProfile"); 1037 1039 scriptInterface.RegisterFunction<CScriptVal, &JSI_Lobby::LobbyGuiPollMessage>("LobbyGuiPollMessage"); 1038 1040 scriptInterface.RegisterFunction<void, std::wstring, &JSI_Lobby::LobbySendMessage>("LobbySendMessage"); 1039 1041 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() … … 518 542 scriptInterface.Eval("({})", &game); 519 543 520 544 const char* stats[] = { "name", "ip", "state", "nbp", "tnbp", "players", "mapName", "niceMapName", "mapSize", "mapType", "victoryCondition" }; 521 short stats_length = 11; 522 for (short i = 0; i < stats_length; i++) 545 for (size_t i = 0; i < ARRAY_SIZE(stats); ++i) 523 546 scriptInterface.SetProperty(game, stats[i], wstring_from_utf8((*it)->findAttribute(stats[i]).to_string())); 524 547 525 548 scriptInterface.CallFunctionVoid(gameList, "push", game); … … 546 569 scriptInterface.Eval("({})", &board); 547 570 548 571 const char* attributes[] = { "name", "rank", "rating" }; 549 short attributes_length = 3; 550 for (short i = 0; i < attributes_length; i++) 572 for (size_t i = 0; i < ARRAY_SIZE(attributes); ++i) 551 573 scriptInterface.SetProperty(board, attributes[i], wstring_from_utf8((*it)->findAttribute(attributes[i]).to_string())); 552 574 553 575 scriptInterface.CallFunctionVoid(boardList, "push", board); … … 556 578 return CScriptValRooted(cx, boardList); 557 579 } 558 580 581 /** 582 * Handle requests from the GUI for profile data. 583 * 584 * @return A JS array containing the specific user's profile data 585 */ 586 CScriptValRooted XmppClient::GUIGetProfile(ScriptInterface& scriptInterface) 587 { 588 JSContext* cx = scriptInterface.GetContext(); 589 JSAutoRequest rq(cx); 590 591 JS::RootedValue profileFetch(cx); 592 scriptInterface.Eval("([])", &profileFetch); 593 const char* stats[] = { "player", "rating", "totalGamesPlayed", "highestRating", "wins", "losses", "rank" }; 594 for (std::vector<const glooxwrapper::Tag*>::const_iterator it = m_Profile.begin(); it != m_Profile.end(); ++it) 595 { 596 JS::RootedValue profile(cx); 597 scriptInterface.Eval("({})", &profile); 598 599 for (size_t i = 0; i < ARRAY_SIZE(stats); ++i) 600 scriptInterface.SetProperty(profile, stats[i], wstring_from_utf8((*it)->findAttribute(stats[i]).to_string())); 601 602 scriptInterface.CallFunctionVoid(profileFetch, "push", profile); 603 } 604 605 return CScriptValRooted(cx, profileFetch); 606 } 607 559 608 /***************************************************** 560 609 * Message interfaces * 561 610 *****************************************************/ … … 648 697 { 649 698 const GameListQuery* gq = iq.findExtension<GameListQuery>( ExtGameListQuery ); 650 699 const BoardListQuery* bq = iq.findExtension<BoardListQuery>( ExtBoardListQuery ); 700 const ProfileQuery* pq = iq.findExtension<ProfileQuery>( ExtProfileQuery ); 651 701 if(gq) 652 702 { 653 703 for(std::vector<const glooxwrapper::Tag*>::const_iterator it = m_GameList.begin(); it != m_GameList.end(); ++it ) … … 684 734 CreateSimpleMessage("system", "ratinglist updated", "internal"); 685 735 } 686 736 } 737 if (pq) 738 { 739 for (std::vector<const glooxwrapper::Tag*>::const_iterator it = m_Profile.begin(); it != m_Profile.end(); ++it) 740 glooxwrapper::Tag::free(*it); 741 m_Profile.clear(); 742 743 for (std::vector<const glooxwrapper::Tag*>::const_iterator it = pq->m_StanzaProfile.begin(); it != pq->m_StanzaProfile.end(); ++it) 744 m_Profile.push_back((*it)->clone()); 745 746 CreateSimpleMessage("system", "profile updated", "internal"); 747 } 687 748 } 688 else if (iq.subtype() == gloox::IQ::Error)749 else if (iq.subtype() == gloox::IQ::Error) 689 750 { 690 751 gloox::StanzaError err = iq.error_error(); 691 752 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
26 26 from sleekxmpp.xmlstream.handler import Callback 27 27 from sleekxmpp.xmlstream.matcher import StanzaPath 28 28 29 from sqlalchemy import func 30 29 31 from LobbyRanking import session as db, Game, Player, PlayerInfo 30 32 from ELO import get_rating_adjustment 31 33 # Rating that new players should be inserted into the … … 37 39 def __init__(self, room): 38 40 self.room = room 39 41 self.lastRated = "" 42 43 def getProfile(self, JID): 44 """ 45 Retrieves the profile for the specified JID 46 """ 47 stats = {} 48 player = db.query(Player).filter(Player.jid.ilike(str(JID))) 49 if not player.first(): 50 return 51 if player.first().rating != -1: 52 stats['rating'] = str(player.first().rating) 53 54 if player.first().highest_rating != -1: 55 stats['highestRating'] = str(player.first().highest_rating) 56 57 playerID = player.first().id 58 players = db.query(Player).order_by(Player.rating.desc()).all() 59 60 for rank, user in enumerate(players): 61 if (user.jid.lower() == JID.lower()): 62 stats['rank'] = str(rank+1) 63 break 64 65 stats['totalGamesPlayed'] = str(db.query(PlayerInfo).filter_by(player_id=playerID).count()) 66 stats['wins'] = str(db.query(Game).filter_by(winner_id=playerID).count()) 67 stats['losses'] = str(db.query(PlayerInfo).filter_by(player_id=playerID).count() - db.query(Game).filter_by(winner_id=playerID).count()) 68 return stats 69 40 70 def getOrCreatePlayer(self, JID): 41 71 """ 42 72 Stores a player(JID) in the database if they don't yet exist. … … 180 210 name2, player2.rating, player2.rating + rating_adjustment2) 181 211 player1.rating += rating_adjustment1 182 212 player2.rating += rating_adjustment2 213 if not player1.highest_rating: 214 player1.highest_rating = -1 215 if not player2.highest_rating: 216 player2.highest_rating = -1 217 if player1.rating > player1.highest_rating: 218 player1.highest_rating = player1.rating 219 if player2.rating > player2.highest_rating: 220 player2.highest_rating = player2.rating 183 221 db.commit() 184 222 return self 185 223 … … 426 464 data[key] = item 427 465 return data 428 466 467 ## Class for custom profile ## 468 class ProfileXmppPlugin(ElementBase): 469 name = 'query' 470 namespace = 'jabber:iq:profile' 471 interfaces = set(('profile', 'command')) 472 sub_interfaces = interfaces 473 plugin_attrib = 'profile' 474 def addCommand(self, command): 475 commandXml = ET.fromstring("<command>%s</command>" % command) 476 self.xml.append(commandXml) 477 def addItem(self, player, rating, highestRating, rank, totalGamesPlayed, wins, losses): 478 itemXml = ET.Element("profile", {"player": player, "rating": rating, "highestRating": highestRating, 479 "rank" : rank, "totalGamesPlayed" : totalGamesPlayed, "wins" : wins, 480 "losses" : losses}) 481 self.xml.append(itemXml) 482 429 483 ## Main class which handles IQ data and sends new data ## 430 484 class XpartaMuPP(sleekxmpp.ClientXMPP): 431 485 """ … … 454 508 register_stanza_plugin(Iq, GameListXmppPlugin) 455 509 register_stanza_plugin(Iq, BoardListXmppPlugin) 456 510 register_stanza_plugin(Iq, GameReportXmppPlugin) 511 register_stanza_plugin(Iq, ProfileXmppPlugin) 457 512 458 513 self.register_handler(Callback('Iq Gamelist', 459 514 StanzaPath('iq/gamelist'), … … 467 522 StanzaPath('iq/gamereport'), 468 523 self.iqhandler, 469 524 instream=True)) 525 526 self.register_handler(Callback('Iq Profile', 527 StanzaPath('iq/profile'), 528 self.iqhandler, 529 instream=True)) 470 530 471 531 self.add_event_handler("session_start", self.start) 472 532 self.add_event_handler("muc::%s::got_online" % self.room, self.muc_online) … … 566 626 logging.error("Failed to process ratinglist request from %s" % iq['from'].bare) 567 627 else: 568 628 logging.error("Failed to process boardlist request from %s" % iq['from'].bare) 629 elif 'profile' in iq.plugins: 630 command = iq['profile']['command'] 631 try: 632 self.sendProfile(iq['from'], command) 633 except: 634 try: 635 self.sendProfileNotFound(iq['from'], command) 636 except: 637 logging.debug("No record found for %s" % command) 569 638 else: 570 639 logging.error("Unknown 'get' type stanza request from %s" % iq['from'].bare) 571 640 elif iq['type'] == 'result': … … 748 817 except: 749 818 logging.error("Failed to send rating list") 750 819 820 def sendProfile(self, to, player): 821 """ 822 Send the profile to a specified player. 823 """ 824 if to == "": 825 logging.error("Failed to send profile") 826 return 827 828 online = False; 829 ## Pull stats and add it to the stanza 830 for JID in self.nicks.keys(): 831 if self.nicks[JID] == player: 832 stats = self.leaderboard.getProfile(JID) 833 online = True 834 break 835 836 if online == False: 837 stats = self.leaderboard.getProfile(player + "@" + str(to).split('@')[1]) 838 stz = ProfileXmppPlugin() 839 iq = self.Iq() 840 iq['type'] = 'result' 841 842 stz.addItem(player, stats['rating'], stats['highestRating'], stats['rank'], stats['totalGamesPlayed'], stats['wins'], stats['losses']) 843 stz.addCommand(player) 844 iq.setPayload(stz) 845 ## Check recipient exists 846 if str(to) not in self.nicks: 847 logging.error("No player with the XmPP ID '%s' known to send profile to" % str(to)) 848 return 849 850 ## Set additional IQ attributes 851 iq['to'] = to 852 853 ## Try sending the stanza 854 try: 855 iq.send(block=False, now=True) 856 except: 857 traceback.print_exc() 858 logging.error("Failed to send profile") 859 860 def sendProfileNotFound(self, to, player): 861 """ 862 Send a profile not-found error to a specified player. 863 """ 864 stz = ProfileXmppPlugin() 865 iq = self.Iq() 866 iq['type'] = 'result' 867 868 filler = str(0) 869 stz.addItem(player, str(-2), filler, filler, filler, filler, filler) 870 stz.addCommand(player) 871 iq.setPayload(stz) 872 ## Check recipient exists 873 if str(to) not in self.nicks: 874 logging.error("No player with the XmPP ID '%s' known to send profile to" % str(to)) 875 return 876 877 ## Set additional IQ attributes 878 iq['to'] = to 879 880 ## Try sending the stanza 881 try: 882 iq.send(block=False, now=True) 883 except: 884 traceback.print_exc() 885 logging.error("Failed to send profile") 886 751 887 ## Main Program ## 752 888 if __name__ == '__main__': 753 889 # Setup the command line arguments.