Skip to content

Commit

Permalink
Added initial support for Global Chat feature (still incomplete)
Browse files Browse the repository at this point in the history
This is just the raw engine. Clients can connect and change online/offline status, but still not supporting messages and friend list. Still a long way to go until this thing get fully working.

Global Chat is the new cross-server chat engine added on clients >= 7.0.62.2, which works sending Jabber/XMPP XML texts through packet 0xF9. On OSI, shards probably resend all Jabber stuff to an external Jabber server which makes the cross-server integration, but since Sphere is a single-shard server, maybe should be enough use it as the Jabber server itself, adding just the basic support to make it work locally

The problem is: Jabber XML language is complex and honestly doesn't worth learn it just to implement an UO feature. And to make things even worse, the new client packet 0xF9 works using unknown flags and nonsense values. This commit is just to make this raw work public for documenting purposes and future improvements
  • Loading branch information
coruja747 committed Aug 28, 2018
1 parent 374ff52 commit 722606c
Show file tree
Hide file tree
Showing 17 changed files with 352 additions and 26 deletions.
5 changes: 4 additions & 1 deletion docs/REVISIONS-56-SERIES.TXT
Original file line number Diff line number Diff line change
Expand Up @@ -1194,4 +1194,7 @@ Changed: Improved 'update' packets handling, making the server send smaller pack
19-08-2018, Coruja
Fixed: Corpses on ground not showing equipped items correctly.
Fixed: Buff/debuff bar not showing some buff icons when client log-in.
Fixed: Tooltips not showing on items with ATTR=attr_static set.
Fixed: Tooltips not showing on items with ATTR=attr_static set.

27-08-2018, Coruja
[sphere.ini]: Added new CHATF_GLOBALCHAT chat flag to enable Global Chat feature on clients >= 7.0.62.2 (still incomplete yet)
7 changes: 4 additions & 3 deletions src/common/grayproto.h
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,7 @@ enum XCMD_TYPE // XCMD_* messages are unique in both directions.
XCMD_MoveShip = 0xf6,
XCMD_PacketCont = 0xf7,
XCMD_CreateHS = 0xf8,
XCMD_GlobalChat = 0xf9,
XCMD_UltimaStoreButton = 0xfa
};

Expand Down Expand Up @@ -1042,10 +1043,10 @@ enum NOTO_TYPE
//#define MINCLIVER_CLILOCS 1260202 // minimum client to use clilocs (1.26.2b)
#define MINCLIVER_PADCHARLIST 3000010 // minimum client to pad character list to at least 5 characters
#define MINCLIVER_CLOSEDIALOG 4000400 // minimum client where close dialog does not trigger a client response
#define MINCLIVER_NEWVERSIONING 5000605 // minimum client to use the new versioning format (after 5.0.6e it change to 5.0.6.5)
#define MINCLIVER_NEWVERSIONING 5000605 // minimum client to use new versioning format (after 5.0.6e it change to 5.0.6.5)
#define MINCLIVER_CONTAINERGRID 6000107 // minimum client to use container grid index (6.0.1.7)
#define MINCLIVER_NEWCHATSYSTEM 7000401 // minimum client to use the new chat system (7.0.4.1) - classic client
#define MINCLIVER_NEWCHATSYSTEM_EC 4000400 // minimum client to use the new chat system (4.0.4.0) - enhanced client
#define MINCLIVER_NEWCHATSYSTEM 7000400 // minimum client to use new chat system (7.0.4.0)
#define MINCLIVER_GLOBALCHAT 7006202 // minimum client to use global chat system (7.0.62.2)

// Client versions (packets)
#define MINCLIVER_STATLOCKS 4000100 // minimum client to receive 0xBF.0x19.0x02 packet
Expand Down
1 change: 1 addition & 0 deletions src/graysvr/CChar.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ struct CCharPlayer
WORD m_wDeaths; // Death count
BYTE m_speedMode;
bool m_bRefuseTrades;
bool m_bRefuseGlobalChatRequests;
bool m_bKrToolbarEnabled;

static LPCTSTR const sm_szLoadKeys[];
Expand Down
9 changes: 9 additions & 0 deletions src/graysvr/CCharNPC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ CCharPlayer::CCharPlayer(CChar *pChar, CAccount *pAccount) : m_pAccount(pAccount
m_wDeaths = 0;
m_speedMode = SPEEDMODE_DEFAULT;
m_bRefuseTrades = false;
m_bRefuseGlobalChatRequests = false;
m_bKrToolbarEnabled = false;
m_timeLastUsed.Init();

Expand Down Expand Up @@ -334,6 +335,9 @@ bool CCharPlayer::r_WriteVal( CChar * pChar, LPCTSTR pszKey, CGString & sVal )
sVal = szLine;
}
return( true );
case CPC_REFUSEGLOBALCHATREQUESTS:
sVal.FormatVal(m_bRefuseGlobalChatRequests);
return true;
case CPC_REFUSETRADES:
sVal.FormatVal(m_bRefuseTrades);
return true;
Expand Down Expand Up @@ -463,6 +467,9 @@ bool CCharPlayer::r_LoadVal( CChar * pChar, CScript &s )
case CPC_PROFILE:
m_sProfile = Str_MakeFiltered( s.GetArgStr());
return( true );
case CPC_REFUSEGLOBALCHATREQUESTS:
m_bRefuseGlobalChatRequests = (s.GetArgVal() != 0);
return true;
case CPC_REFUSETRADES:
m_bRefuseTrades = (s.GetArgVal() != 0);
return true;
Expand Down Expand Up @@ -532,6 +539,8 @@ void CCharPlayer::r_WriteChar( CChar * pChar, CScript & s )
s.WriteKeyVal("SPEEDMODE", m_speedMode);
if ( m_bRefuseTrades )
s.WriteKeyVal("REFUSETRADES", m_bRefuseTrades);
if ( m_bRefuseGlobalChatRequests )
s.WriteKeyVal("REFUSEGLOBALCHATREQUESTS", m_bRefuseGlobalChatRequests);
if ( (m_pAccount->GetResDisp() >= RDS_KR) && m_bKrToolbarEnabled )
s.WriteKeyVal("KRTOOLBARSTATUS", m_bKrToolbarEnabled);

Expand Down
46 changes: 46 additions & 0 deletions src/graysvr/CChat.h
Original file line number Diff line number Diff line change
Expand Up @@ -178,4 +178,50 @@ class CChatMember // This is member of CClient
LPCTSTR GetChatName();
};

class CGlobalChat // This is member of CClient
{
public:
static const char *m_sClassName;

CGlobalChat() { };
~CGlobalChat() { };

private:
DWORD m_dwID; // client connection ID
LPCTSTR m_pszJID; // client Jabber ID
bool m_fVisible; // client visibility status (online/offline)

public:
void SetID(DWORD dwID)
{
m_dwID = dwID;
}
DWORD GetID() const
{
return m_dwID;
}

void SetJID(LPCTSTR pszJID)
{
m_pszJID = pszJID;
}
LPCTSTR GetJID() const
{
return m_pszJID;
}

void SetVisible(bool fSet)
{
m_fVisible = fSet;
}
bool IsVisible() const
{
return m_fVisible;
}

private:
CGlobalChat(const CChatMember &copy);
CGlobalChat &operator=(const CChatMember &other);
};

#endif // _INC_CCHAT_H
9 changes: 8 additions & 1 deletion src/graysvr/CClient.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ enum CLIMODE_TYPE // What mode is the client to server connection in? (waiting f
CLIMODE_TARG_STONE_RECRUIT, // recruit members for a stone
CLIMODE_TARG_STONE_RECRUITFULL, // recruit/make a member and set abbrev show
CLIMODE_TARG_PARTY_ADD,
CLIMODE_TARG_GLOBALCHAT_ADD,

CLIMODE_TARG_QTY
};
Expand Down Expand Up @@ -344,7 +345,7 @@ class PacketSend;
class PacketServerRelay;
struct VendorItem;

class CClient : public CGObListRec, public CScriptObj, public CChatMember, public CTextConsole
class CClient : public CGObListRec, public CScriptObj, public CChatMember, public CGlobalChat, public CTextConsole
{
// TCP/IP connection to the player or console
public:
Expand Down Expand Up @@ -526,6 +527,7 @@ class CClient : public CGObListRec, public CScriptObj, public CChatMember, publi
bool OnTarg_Use_Deed(CItem *pDeed, CPointMap &pt);
bool OnTarg_Use_Item(CObjBase *pObjTarg, CPointMap &pt, ITEMID_TYPE id);
bool OnTarg_Party_Add(CChar *pChar);
bool OnTarg_GlobalChat_Add(CChar *pChar);
CItem *OnTarg_Use_Multi(const CItemBase *pItemDef, CPointMap &pt, DWORD dwAttr, HUE_TYPE wHue);

int OnSkill_AnimalLore(CGrayUID uid, int iSkillLevel, bool fTest);
Expand Down Expand Up @@ -780,6 +782,9 @@ class CClient : public CGObListRec, public CScriptObj, public CChatMember, publi
void addLoginComplete();
void addChatSystemMessage(CHATMSG_TYPE type, LPCTSTR pszName1 = NULL, LPCTSTR pszName2 = NULL, CLanguageID lang = 0);

void addGlobalChatConnect();
void addGlobalChatStatusToggle();

void addCharPaperdoll(const CChar *pChar);

void addAOSTooltip(const CObjBase *pObj, bool fRequested = false, bool fShop = false);
Expand All @@ -798,6 +803,8 @@ class CClient : public CGObListRec, public CScriptObj, public CChatMember, publi
#define POPUP_TRADE_ALLOW 15
#define POPUP_TRADE_REFUSE 16
#define POPUP_TRADE_OPEN 17
#define POPUP_GLOBALCHAT_ALLOW 18
#define POPUP_GLOBALCHAT_REFUSE 19
#define POPUP_BANKBOX 21
#define POPUP_VENDORBUY 31
#define POPUP_VENDORSELL 32
Expand Down
18 changes: 18 additions & 0 deletions src/graysvr/CClientEvent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2046,6 +2046,7 @@ void CClient::Event_Target(CLIMODE_TYPE context, CGrayUID uid, CPointMap pt, BYT
case CLIMODE_TARG_STONE_RECRUIT: OnTarg_Stone_Recruit(uid.CharFind()); break;
case CLIMODE_TARG_STONE_RECRUITFULL:OnTarg_Stone_Recruit(uid.CharFind(), true); break;
case CLIMODE_TARG_PARTY_ADD: OnTarg_Party_Add(uid.CharFind()); break;
case CLIMODE_TARG_GLOBALCHAT_ADD: OnTarg_GlobalChat_Add(uid.CharFind()); break;

default: break;
}
Expand Down Expand Up @@ -2168,6 +2169,13 @@ void CClient::Event_AOSPopupMenuRequest(CGrayUID uid) //construct packet after a
else
m_pPopupPacket->addOption(POPUP_TRADE_REFUSE, 1154113);
}
if ( m_NetState->isClientVersion(MINCLIVER_GLOBALCHAT) && (g_Cfg.m_iChatFlags & CHATF_GLOBALCHAT) )
{
if ( pChar->m_pPlayer->m_bRefuseGlobalChatRequests )
m_pPopupPacket->addOption(POPUP_GLOBALCHAT_ALLOW, 1158415);
else
m_pPopupPacket->addOption(POPUP_GLOBALCHAT_REFUSE, 1158416);
}
}
else
{
Expand Down Expand Up @@ -2337,6 +2345,16 @@ void CClient::Event_AOSPopupMenuSelect(CGrayUID uid, WORD EntryTag) //do somethi
case POPUP_TRADE_OPEN:
Cmd_SecureTrade(pChar, NULL);
break;

case POPUP_GLOBALCHAT_ALLOW:
if ( m_pChar->m_pPlayer )
m_pChar->m_pPlayer->m_bRefuseGlobalChatRequests = false;
break;

case POPUP_GLOBALCHAT_REFUSE:
if ( m_pChar->m_pPlayer )
m_pChar->m_pPlayer->m_bRefuseGlobalChatRequests = true;
break;
}
}

Expand Down
60 changes: 60 additions & 0 deletions src/graysvr/CClientMsg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1413,6 +1413,12 @@ void CClient::addPlayerStart(CChar *pChar)
addSpeedMode(m_pChar->m_pPlayer->m_speedMode);
addKRToolbar(m_pChar->m_pPlayer->m_bKrToolbarEnabled);
resendBuffs();

if ( g_Cfg.m_iChatFlags & CHATF_GLOBALCHAT )
{
addGlobalChatConnect();
addGlobalChatStatusToggle();
}
}

void CClient::addPlayerWarMode()
Expand Down Expand Up @@ -2306,6 +2312,60 @@ void CClient::addChatSystemMessage(CHATMSG_TYPE type, LPCTSTR pszName1, LPCTSTR
new PacketChatMessage(this, type, pszName1, pszName2, lang);
}

void CClient::addGlobalChatConnect()
{
ADDTOCALLSTACK("CClient::addGlobalChatConnect");
// Connect on Global Chat
if ( !m_pChar || !PacketGlobalChat::CanSendTo(m_NetState) )
return;

// Set connection ID
CGlobalChat::SetID(static_cast<DWORD>(CGTime::GetCurrentTime().GetTime())); // must have length=10 or client will crash

// Set Jabber ID (syntax: CharName_CharUID@ServerID)
TCHAR *pszJID = Str_GetTemp();
sprintf(pszJID, "%s_%.7lu@%.2hhu", m_pChar->GetName(), static_cast<DWORD>(m_pChar->GetUID()), 0); // CharUID must have length=7 and ServerID must have length=2 or client will crash
CGlobalChat::SetJID(pszJID);

// Send xml to client
TCHAR *pszXML = Str_GetTemp();
sprintf(pszXML, "<iq to=\"%s\" id=\"iq_%lu\" type=\"6\" version=\"1\" jid=\"%s\" />", CGlobalChat::GetJID(), CGlobalChat::GetID(), CGlobalChat::GetJID());

CGlobalChat::SetVisible(false);
new PacketGlobalChat(this, 0, PacketGlobalChat::Connect, PacketGlobalChat::InfoQuery, pszXML);
SysMessage("Global Chat is now connected.");
}

void CClient::addGlobalChatStatusToggle()
{
ADDTOCALLSTACK("CClient::addGlobalChatStatusToggle");
// Toggle client visibility status (online/offline) on Global Chat
if ( !m_pChar || !PacketGlobalChat::CanSendTo(m_NetState) )
return;

int iShow;
LPCTSTR pszMsg;
if ( CGlobalChat::IsVisible() )
{
iShow = 0;
pszMsg = "Global Chat Offline";
}
else
{
iShow = 1;
pszMsg = "Global Chat Online";
}

TCHAR *pszXML = Str_GetTemp();
sprintf(pszXML, "<presence from=\"%s\" id=\"pres_%lu\" name=\"%s\" show=\"%d\" version=\"1\" />", CGlobalChat::GetJID(), CGlobalChat::GetID(), m_pChar->GetName(), iShow);

CGlobalChat::SetVisible(static_cast<bool>(iShow));
new PacketGlobalChat(this, 0, PacketGlobalChat::Connect, PacketGlobalChat::Presence, pszXML);
SysMessage(pszMsg);

// TO-DO: also send the status change to all clients on friend list
}

void CClient::addGumpTextDisp(const CObjBase *pObj, GUMP_TYPE gump, LPCTSTR pszName, LPCTSTR pszText)
{
ADDTOCALLSTACK("CClient::addGumpTextDisp");
Expand Down
50 changes: 50 additions & 0 deletions src/graysvr/CClientTarg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2357,3 +2357,53 @@ bool CClient::OnTarg_Party_Add(CChar *pChar)
new PacketPartyInvite(pChar->m_pClient, m_pChar);
return true;
}

bool CClient::OnTarg_GlobalChat_Add(CChar *pChar)
{
ADDTOCALLSTACK("CClient::OnTarg_GlobalChat_Add");
// CLIMODE_TARG_GLOBALCHAT_ADD
// Invite this person to join our global chat friend list

if ( !CGlobalChat::IsVisible() )
{
SysMessage("You must enable Global Chat to request a friend.");
return false;
}
if ( !pChar || !pChar->m_pPlayer || (pChar == m_pChar) )
{
SysMessage("Invalid target.");
return false;
}
if ( !pChar->m_pClient )
{
SysMessage("Player currently unavailable.");
return false;
}
if ( pChar->m_pPlayer->m_bRefuseGlobalChatRequests ) // TO-DO: also check if pChar is online on global chat -> CGlobalChat::IsVisible()
{
SysMessage("This user is not accepting Global Chat friend requests at this time.");
return false;
}
/*if ( iFriendsCount >= 50 ) // TO-DO
{
SysMessage("You have reached your global chat friend limit.");
return false;
}*/

if ( IsPriv(PRIV_GM) && (pChar->m_pClient->GetPrivLevel() < GetPrivLevel()) )
{
// TO-DO: auto-accept the request without send 'friend request' dialog
return true;
}

CVarDefCont *pTagInviteTime = m_pChar->m_TagDefs.GetKey("GLOBALCHAT_LASTINVITETIME");
if ( pTagInviteTime && (g_World.GetCurrentTime().GetTimeRaw() < static_cast<UINT64>(pTagInviteTime->GetValNum())) )
{
SysMessage("You are unable to add new friends at this time. Please try again in a moment.");
return false;
}
m_pChar->SetKeyNum("GLOBALCHAT_LASTINVITETIME", g_World.GetCurrentTime().GetTimeRaw() + (30 * TICK_PER_SEC));

// TO-DO: send 'friend request' dialog
return true;
}
9 changes: 5 additions & 4 deletions src/graysvr/CResource.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,11 @@ enum REVEALFLAGS_TYPE

enum CHATFLAGS_TYPE
{
CHATF_AUTOJOIN = 0x1, // Auto join first static channel available (new chat system: join after client login / old chat system: join after open chat window)
CHATF_CHANNELCREATION = 0x2, // Enable channel creation
CHATF_CHANNELMODERATION = 0x4, // Enable channel moderation (old chat system only)
CHATF_CUSTOMNAMES = 0x8 // Enable custom name selection when open chat window for the first time (old chat system only)
CHATF_AUTOJOIN = 0x01, // Auto join first static channel available (new chat system: join after client login / old chat system: join after open chat window)
CHATF_CHANNELCREATION = 0x02, // Enable channel creation
CHATF_CHANNELMODERATION = 0x04, // Enable channel moderation (old chat system only)
CHATF_CUSTOMNAMES = 0x08, // Enable custom name selection when open chat window for the first time (old chat system only)
CHATF_GLOBALCHAT = 0x10 // Enable global chat system on clients >= 7.0.62.2 (INCOMPLETE)
};

enum RACIALFLAGS_TYPE
Expand Down
1 change: 1 addition & 0 deletions src/network/network.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -652,6 +652,7 @@ void PacketManager::registerStandardPackets(void)
registerPacket(XCMD_CrashReport, new PacketCrashReport()); //
registerPacket(XCMD_CreateHS, new PacketCreateHS()); // create character (HS)
registerPacket(XCMD_UltimaStoreButton, new PacketUltimaStoreButton()); // ultima store button (SA)
registerPacket(XCMD_GlobalChat, new PacketGlobalChatReq()); //

// extended packets (0xBF)
registerExtended(EXTDATA_ScreenSize, new PacketScreenSize()); // client screen size
Expand Down
Loading

0 comments on commit 722606c

Please sign in to comment.