Index: source/lobby/XmppClient.h =================================================================== --- source/lobby/XmppClient.h +++ source/lobby/XmppClient.h @@ -99,6 +99,17 @@ void SendStunEndpointToHost(const StunClient::StunEndpoint& stunEndpoint, const std::string& hostJID); + /** + * Convert gloox values to string or time. + */ + static void GetPresenceString(const gloox::Presence::PresenceType p, std::string& presence); + static void GetRoleString(const gloox::MUCRoomRole r, std::string& role); + static std::string StanzaErrorToString(gloox::StanzaError err); + static std::string RegistrationResultToString(gloox::RegistrationResult res); + static std::string ConnectionErrorToString(gloox::ConnectionError err); + static std::string CertificateErrorToString(gloox::CertStatus status); + static std::time_t ComputeTimestamp(const glooxwrapper::Message& msg); + protected: /* Xmpp handlers */ /* MUC handlers */ @@ -138,15 +149,6 @@ virtual void handleSessionAction(gloox::Jingle::Action action, glooxwrapper::Jingle::Session& session, const glooxwrapper::Jingle::Session::Jingle& jingle); virtual void handleSessionInitiation(glooxwrapper::Jingle::Session& session, const glooxwrapper::Jingle::Session::Jingle& jingle); - // Helpers - void GetPresenceString(const gloox::Presence::PresenceType p, std::string& presence) const; - void GetRoleString(const gloox::MUCRoomRole r, std::string& role) const; - std::string StanzaErrorToString(gloox::StanzaError err) const; - std::string ConnectionErrorToString(gloox::ConnectionError err) const; - std::string CertificateErrorToString(gloox::CertStatus status) const; - std::string RegistrationResultToString(gloox::RegistrationResult res) const; - std::time_t ComputeTimestamp(const glooxwrapper::Message& msg) const; - public: bool GuiPollPresenceStatusUpdate(); JS::Value GuiPollNewMessage(const ScriptInterface& scriptInterface); @@ -162,8 +164,24 @@ Args const&... args); private: + struct SPlayer { + + SPlayer() + : m_Presence(gloox::Presence::PresenceType::Invalid), m_Rating(), m_Role(gloox::MUCRoomRole::RoleInvalid) + { + } + + SPlayer(const gloox::Presence::PresenceType presence, const glooxwrapper::string& rating, const gloox::MUCRoomRole& role) + : m_Presence(presence), m_Rating(rating), m_Role(role) + { + } + + gloox::Presence::PresenceType m_Presence; + glooxwrapper::string m_Rating; + gloox::MUCRoomRole m_Role; + }; /// Map of players - std::map > m_PlayerMap; + std::map m_PlayerMap; /// List of games std::vector m_GameList; /// List of rankings Index: source/lobby/XmppClient.cpp =================================================================== --- source/lobby/XmppClient.cpp +++ source/lobby/XmppClient.cpp @@ -25,6 +25,7 @@ #endif #include "i18n/L10n.h" +#include "lobby/scripting/GlooxScriptConversions.cpp" #include "lib/external_libraries/enet.h" #include "lib/utf8.h" #include "network/NetServer.h" @@ -37,7 +38,7 @@ #include //debug -#if 1 +#if 0 #define DbgXMPP(x) #else #define DbgXMPP(x) std::cout << x << std::endl; @@ -283,8 +284,8 @@ "system", "disconnected", std::time(nullptr), - "reason", ConnectionErrorToString(error), - "certificate_status", CertificateErrorToString(m_certStatus)); + "reason", error, + "certificate_status", m_certStatus); } /** @@ -316,7 +317,7 @@ */ void XmppClient::handleMUCError(glooxwrapper::MUCRoom*, gloox::StanzaError err) { - CreateGUIMessage("system", "error", std::time(nullptr), "text", StanzaErrorToString(err)); + CreateGUIMessage("system", "error", std::time(nullptr), "text", err); } /***************************************************** @@ -503,7 +504,7 @@ if (result == gloox::RegistrationSuccess) CreateGUIMessage("system", "registered", std::time(nullptr)); else - CreateGUIMessage("system", "error", std::time(nullptr), "text", RegistrationResultToString(result)); + CreateGUIMessage("system", "error", std::time(nullptr), "text", result); disconnect(); } @@ -540,16 +541,16 @@ scriptInterface.CreateArray(ret); int j = 0; - for (const std::pair >& p : m_PlayerMap) + for (const std::pair& p : m_PlayerMap) { JS::RootedValue player(cx); scriptInterface.CreateObject( &player, - "name", wstring_from_utf8(p.first), - "presence", wstring_from_utf8(p.second[0]), - "rating", wstring_from_utf8(p.second[1]), - "role", wstring_from_utf8(p.second[2])); + "name", p.first, + "presence", p.second.m_Presence, + "rating", p.second.m_Rating, + "role", p.second.m_Role); scriptInterface.SetPropertyInt(ret, j++, player); } @@ -739,8 +740,8 @@ "chat", priv ? "private-message" : "room-message", ComputeTimestamp(msg), - "from", msg.from().resource().to_string(), - "text", msg.body().to_string()); + "from", msg.from().resource(), + "text", msg.body()); } /** @@ -755,8 +756,8 @@ "chat", "private-message", ComputeTimestamp(msg), - "from", msg.from().resource().to_string(), - "text", msg.body().to_string()); + "from", msg.from().resource(), + "text", msg.body()); } /** @@ -799,9 +800,9 @@ { for (const glooxwrapper::Tag* const& t : bq->m_StanzaBoardList) { - std::string name = t->findAttribute("name").to_string(); + glooxwrapper::string name = t->findAttribute("name"); if (m_PlayerMap.find(name) != m_PlayerMap.end()) - m_PlayerMap[name][1] = t->findAttribute("rating").to_string(); + m_PlayerMap.at(name).m_Rating = t->findAttribute("rating"); } CreateGUIMessage("game", "ratinglist", std::time(nullptr)); @@ -836,10 +837,10 @@ } } else if (iq.subtype() == gloox::IQ::Error) - CreateGUIMessage("system", "error", std::time(nullptr), "text", StanzaErrorToString(iq.error_error())); + CreateGUIMessage("system", "error", std::time(nullptr), "text", iq.error_error()); else { - CreateGUIMessage("system", "error", std::time(nullptr), "text", g_L10n.Translate("unknown subtype (see logs)")); + CreateGUIMessage("system", "error", std::time(nullptr), "text", wstring_from_utf8(g_L10n.Translate("unknown subtype (see logs)"))); LOGMESSAGE("unknown subtype '%s'", tag_name(iq).c_str()); } return true; @@ -850,39 +851,52 @@ */ void XmppClient::handleMUCParticipantPresence(glooxwrapper::MUCRoom*, const glooxwrapper::MUCRoomParticipant participant, const glooxwrapper::Presence& presence) { - std::string nick = participant.nick->resource().to_string(); - gloox::Presence::PresenceType presenceType = presence.presence(); - std::string presenceString, roleString; - GetPresenceString(presenceType, presenceString); - GetRoleString(participant.role, roleString); + const glooxwrapper::string& nick = participant.nick->resource(); - if (presenceType == gloox::Presence::Unavailable) + if (presence.presence() == gloox::Presence::Unavailable) { if (!participant.newNick.empty() && (participant.flags & (gloox::UserNickChanged | gloox::UserSelf))) { // we have a nick change - std::string newNick = participant.newNick.to_string(); - m_PlayerMap[newNick].resize(3); - m_PlayerMap[newNick][0] = presenceString; - m_PlayerMap[newNick][2] = roleString; + m_PlayerMap[nick].m_Presence = presence.presence(); + m_PlayerMap[nick].m_Role = participant.role; - DbgXMPP(nick << " is now known as " << participant.newNick.to_string()); - CreateGUIMessage("chat", "nick", std::time(nullptr), "oldnick", nick, "newnick", participant.newNick.to_string()); + DbgXMPP(nick << " is now known as " << participant.newNick); + CreateGUIMessage( + "chat", + "nick", + std::time(nullptr), + "oldnick", nick, + "newnick", participant.newNick); } else if (participant.flags & gloox::UserKicked) { - DbgXMPP(nick << " was kicked. Reason: " << participant.reason.to_string()); - CreateGUIMessage("chat", "kicked", std::time(nullptr), "nick", nick, "reason", participant.reason.to_string()); + DbgXMPP(nick << " was kicked. Reason: " << participant.reason); + CreateGUIMessage( + "chat", + "kicked", + std::time(nullptr), + "nick", nick, + "reason", participant.reason); } else if (participant.flags & gloox::UserBanned) { - DbgXMPP(nick << " was banned. Reason: " << participant.reason.to_string()); - CreateGUIMessage("chat", "banned", std::time(nullptr), "nick", nick, "reason", participant.reason.to_string()); + DbgXMPP(nick << " was banned. Reason: " << participant.reason); + CreateGUIMessage( + "chat", + "banned", + std::time(nullptr), + "nick", nick, + "reason", participant.reason); } else { DbgXMPP(nick << " left the room (flags " << participant.flags << ")"); - CreateGUIMessage("chat", "leave", std::time(nullptr), "nick", nick); + CreateGUIMessage( + "chat", + "leave", + std::time(nullptr), + "nick", nick); } m_PlayerMap.erase(nick); } @@ -894,23 +908,36 @@ */ if (!m_initialLoadComplete) { - if (m_mucRoom->nick().to_string() == nick) + if (m_mucRoom->nick() == nick) m_initialLoadComplete = true; } - else if (m_PlayerMap.find(nick) == m_PlayerMap.end()) - CreateGUIMessage("chat", "join", std::time(nullptr), "nick", nick); - else if (m_PlayerMap[nick][2] != roleString) - CreateGUIMessage("chat", "role", std::time(nullptr), "nick", nick, "oldrole", m_PlayerMap[nick][2], "newrole", roleString); + else if (m_PlayerMap.count(nick) == 0) + { + CreateGUIMessage( + "chat", + "join", + std::time(nullptr), + "nick", nick); + } + else if (m_PlayerMap.at(nick).m_Role != participant.role) + { + CreateGUIMessage( + "chat", + "role", + std::time(nullptr), + "nick", nick, + "oldrole", m_PlayerMap.at(nick).m_Role, + "newrole", participant.role); + } else // Don't create a GUI message for regular presence changes, because // several hundreds of them accumulate during a match, impacting performance terribly and // the only way they are used is to determine whether to update the playerlist. m_PresenceUpdate = true; - DbgXMPP(nick << " is in the room, presence : " << (int)presenceType); - m_PlayerMap[nick].resize(3); - m_PlayerMap[nick][0] = presenceString; - m_PlayerMap[nick][2] = roleString; + DbgXMPP(nick << " is in the room, presence : " << static_cast(presence.presence())); + m_PlayerMap[nick].m_Presence = presence.presence(); + m_PlayerMap[nick].m_Role = participant.role; } } @@ -919,8 +946,14 @@ */ void XmppClient::handleMUCSubject(glooxwrapper::MUCRoom*, const glooxwrapper::string& nick, const glooxwrapper::string& subject) { - m_Subject = subject.c_str(); - CreateGUIMessage("chat", "subject", std::time(nullptr), "nick", nick.c_str(), "subject", m_Subject); + m_Subject = subject.to_string(); + + CreateGUIMessage( + "chat", + "subject", + std::time(nullptr), + "nick", nick, + "subject", subject); } /** @@ -1002,7 +1035,7 @@ void XmppClient::GetPresence(const std::string& nick, std::string& presence) { if (m_PlayerMap.find(nick) != m_PlayerMap.end()) - presence = m_PlayerMap[nick][0]; + GetPresenceString(m_PlayerMap.at(nick).m_Presence, presence); else presence = "offline"; } @@ -1016,7 +1049,7 @@ void XmppClient::GetRole(const std::string& nick, std::string& role) { if (m_PlayerMap.find(nick) != m_PlayerMap.end()) - role = m_PlayerMap[nick][2]; + GetRoleString(m_PlayerMap.at(nick).m_Role, role); else role = ""; } @@ -1032,7 +1065,7 @@ * * @returns Seconds since the epoch. */ -std::time_t XmppClient::ComputeTimestamp(const glooxwrapper::Message& msg) const +std::time_t XmppClient::ComputeTimestamp(const glooxwrapper::Message& msg) { // Only historic messages contain a timestamp! if (!msg.when()) @@ -1055,7 +1088,7 @@ * @param p Presence to be converted * @param presence Variable to store the converted presence string in */ -void XmppClient::GetPresenceString(const gloox::Presence::PresenceType p, std::string& presence) const +void XmppClient::GetPresenceString(const gloox::Presence::PresenceType p, std::string& presence) { switch(p) { @@ -1082,7 +1115,7 @@ * @param p Role to be converted * @param presence Variable to store the converted role string in */ -void XmppClient::GetRoleString(const gloox::MUCRoomRole r, std::string& role) const +void XmppClient::GetRoleString(const gloox::MUCRoomRole r, std::string& role) { switch(r) { @@ -1103,7 +1136,7 @@ * Translates a gloox certificate error codes, i.e. gloox certificate statuses except CertOk. * Keep in sync with specifications. */ -std::string XmppClient::CertificateErrorToString(gloox::CertStatus status) const +std::string XmppClient::CertificateErrorToString(gloox::CertStatus status) { std::map certificateErrorStrings = { { gloox::CertInvalid, g_L10n.Translate("The certificate is not trusted.") }, @@ -1131,7 +1164,7 @@ * @param err Error to be converted * @return Converted error string */ -std::string XmppClient::StanzaErrorToString(gloox::StanzaError err) const +std::string XmppClient::StanzaErrorToString(gloox::StanzaError err) { #define CASE(X, Y) case gloox::X: return Y #define DEBUG_CASE(X, Y) case gloox::X: return g_L10n.Translate("Error") + " (" + Y + ")" @@ -1175,7 +1208,7 @@ * @param err Error to be converted * @return Converted error string */ -std::string XmppClient::ConnectionErrorToString(gloox::ConnectionError err) const +std::string XmppClient::ConnectionErrorToString(gloox::ConnectionError err) { #define CASE(X, Y) case gloox::X: return Y #define DEBUG_CASE(X, Y) case gloox::X: return g_L10n.Translate("Error") + " (" + Y + ")" @@ -1214,7 +1247,7 @@ * @param err Enum to be converted * @return Converted string */ -std::string XmppClient::RegistrationResultToString(gloox::RegistrationResult res) const +std::string XmppClient::RegistrationResultToString(gloox::RegistrationResult res) { #define CASE(X, Y) case gloox::X: return Y #define DEBUG_CASE(X, Y) case gloox::X: return g_L10n.Translate("Error") + " (" + Y + ")" Index: source/lobby/glooxwrapper/glooxwrapper.h =================================================================== --- source/lobby/glooxwrapper/glooxwrapper.h +++ source/lobby/glooxwrapper/glooxwrapper.h @@ -161,6 +161,9 @@ glooxwrapper_free(m_Data); } + /** + * Gloox strings are UTF encoded, so don't forget to decode it before passing it to the GUI! + */ std::string to_string() const { return std::string(m_Data, m_Size); @@ -181,10 +184,20 @@ return strcmp(m_Data, str) == 0; } + bool operator==(const string& str) const + { + return strcmp(m_Data, str.m_Data) == 0; + } + bool operator!=(const char* str) const { return strcmp(m_Data, str) != 0; } + + bool operator<(const string& str) const + { + return strcmp(m_Data, str.m_Data) < 0; + } }; static inline std::ostream& operator<<(std::ostream& stream, const string& string) Index: source/lobby/scripting/GlooxScriptConversions.cpp =================================================================== --- /dev/null +++ source/lobby/scripting/GlooxScriptConversions.cpp @@ -0,0 +1,60 @@ +/* Copyright (C) 2019 Wildfire Games. + * This file is part of 0 A.D. + * + * 0 A.D. is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * 0 A.D. is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 0 A.D. If not, see . + */ + +#include "precompiled.h" + +#include "lobby/XmppClient.h" +#include "scriptinterface/ScriptInterface.h" + +template<> void ScriptInterface::ToJSVal(JSContext* cx, JS::MutableHandleValue ret, const glooxwrapper::string& val) +{ + ToJSVal(cx, ret, wstring_from_utf8(val.to_string())); +} + +template<> void ScriptInterface::ToJSVal(JSContext* cx, JS::MutableHandleValue ret, const gloox::Presence::PresenceType& val) +{ + std::string presence; + XmppClient::GetPresenceString(val, presence); + ToJSVal(cx, ret, wstring_from_utf8(presence)); +} + +template<> void ScriptInterface::ToJSVal(JSContext* cx, JS::MutableHandleValue ret, const gloox::MUCRoomRole& val) +{ + std::string role; + XmppClient::GetRoleString(val, role); + ToJSVal(cx, ret, wstring_from_utf8(role)); +} + +template<> void ScriptInterface::ToJSVal(JSContext* cx, JS::MutableHandleValue ret, const gloox::StanzaError& val) +{ + ToJSVal(cx, ret, wstring_from_utf8(XmppClient::StanzaErrorToString(val))); +} + +template<> void ScriptInterface::ToJSVal(JSContext* cx, JS::MutableHandleValue ret, const gloox::ConnectionError& val) +{ + ToJSVal(cx, ret, wstring_from_utf8(XmppClient::ConnectionErrorToString(val))); +} + +template<> void ScriptInterface::ToJSVal(JSContext* cx, JS::MutableHandleValue ret, const gloox::RegistrationResult& val) +{ + ToJSVal(cx, ret, wstring_from_utf8(XmppClient::RegistrationResultToString(val))); +} + +template<> void ScriptInterface::ToJSVal(JSContext* cx, JS::MutableHandleValue ret, const gloox::CertStatus& val) +{ + ToJSVal(cx, ret, wstring_from_utf8(XmppClient::CertificateErrorToString(val))); +}