* implemented emotes support & display

-> requires emote.scp extracted from client
* updated: show incoming opcodes _before_ parsing them
* updated DefScript: "emote" command will now also accept emote names (defined in emote.scp, field "name=...")
* updated: replace %1$s in emote texts extracted from (non-english) dbc files with %s, this simplifies emote text displaying.
* misc stuff
This commit is contained in:
False.Genesis 2007-05-14 13:49:37 +00:00
parent 5731cfa03b
commit c2938bf222
8 changed files with 216 additions and 29 deletions

View File

@ -110,7 +110,25 @@ DefReturnResult DefScriptPackage::SCemote(CmdSet& Set){
logerror("Invalid Script call: SCEmote: WorldSession not valid"); logerror("Invalid Script call: SCEmote: WorldSession not valid");
DEF_RETURN_ERROR; DEF_RETURN_ERROR;
} }
uint32 id=atoi(Set.defaultarg.c_str());
// check if the given name exists in the database, if it does, use its record id; if not, convert emote name into a number.
// this supports calls like "emote 126" and "emote ready"
uint32 id = uint32(-1);
SCPDatabaseMgr& dbmgr = ((PseuInstance*)parentMethod)->dbmgr;
if(dbmgr.HasDB("emote"))
{
SCPDatabase& db = dbmgr.GetDB("emote");
id = db.GetFieldByValue("name",DefScriptTools::stringToUpper(Set.defaultarg)); // emote names are always uppercased
}
if(id == uint32(-1))
{
id=atoi(Set.defaultarg.c_str());
if(!id)
{
logerror("SCEmote: Invalid emote!");
return true;
}
}
((PseuInstance*)parentMethod)->GetWSession()->SendEmote(id); ((PseuInstance*)parentMethod)->GetWSession()->SendEmote(id);
return true; return true;
} }

View File

@ -62,14 +62,21 @@ bool SCPDatabase::HasField(uint32 id)
bool SCPField::HasEntry(std::string e) bool SCPField::HasEntry(std::string e)
{ {
for(SCPEntryMap::iterator i = _map.begin(); i != _map.end(); i++) for(SCPEntryMap::iterator i = _map.begin(); i != _map.end(); i++)
if(i->first == e) {
std::string ch = i->first;
if(ch == e)
return true; return true;
}
return false; return false;
} }
std::string SCPField::GetString(std::string entry) std::string SCPField::GetString(std::string entry)
{ {
return HasEntry(entry) ? _map[entry] : ""; //return HasEntry(entry) ? _map[entry] : "";
if(HasEntry(entry))
return _map[entry];
else
return "";
} }
// note that this can take a while depending on the size of the database! // note that this can take a while depending on the size of the database!
@ -78,7 +85,7 @@ uint32 SCPDatabase::GetFieldByValue(std::string entry, std::string value)
for(SCPFieldMap::iterator fm = _map.begin(); fm != _map.end(); fm++) for(SCPFieldMap::iterator fm = _map.begin(); fm != _map.end(); fm++)
if(fm->second.HasEntry(entry) && fm->second.GetString(entry)==value) if(fm->second.HasEntry(entry) && fm->second.GetString(entry)==value)
return fm->first; return fm->first;
return 0; return uint32(-1);
} }
bool SCPDatabaseMgr::HasDB(std::string n) bool SCPDatabaseMgr::HasDB(std::string n)

View File

@ -6,7 +6,8 @@
#include "WorldSession.h" #include "WorldSession.h"
#include "Channel.h" #include "Channel.h"
void WorldSession::SendChatMessage(uint32 type, uint32 lang, std::string msg, std::string to){ void WorldSession::SendChatMessage(uint32 type, uint32 lang, std::string msg, std::string to)
{
if((!_logged) || msg.empty()) if((!_logged) || msg.empty())
return; return;
WorldPacket packet; WorldPacket packet;
@ -34,7 +35,8 @@ void WorldSession::SendChatMessage(uint32 type, uint32 lang, std::string msg, st
SendWorldPacket(packet); SendWorldPacket(packet);
} }
void WorldSession::SendQueryPlayerName(uint64 guid){ void WorldSession::SendQueryPlayerName(uint64 guid)
{
if((!_logged) || guid==0) if((!_logged) || guid==0)
return; return;
WorldPacket packet; WorldPacket packet;
@ -44,7 +46,8 @@ void WorldSession::SendQueryPlayerName(uint64 guid){
// to prevent opcode spam, we need to make a list with already requested names // to prevent opcode spam, we need to make a list with already requested names
} }
void WorldSession::SendPing(uint32 ping){ void WorldSession::SendPing(uint32 ping)
{
if(!_logged) if(!_logged)
return; return;
WorldPacket packet; WorldPacket packet;
@ -53,11 +56,13 @@ void WorldSession::SendPing(uint32 ping){
SendWorldPacket(packet); SendWorldPacket(packet);
} }
void WorldSession::SendEmote(uint32 id){ void WorldSession::SendEmote(uint32 id)
{
if(!_logged) if(!_logged)
return; return;
WorldPacket packet; WorldPacket packet;
packet << id << id << GetMyChar()->GetTarget(); int32 variation = 0; // randomized usually
packet << id << (uint32)variation << GetMyChar()->GetTarget();
packet.SetOpcode(CMSG_TEXT_EMOTE); packet.SetOpcode(CMSG_TEXT_EMOTE);
SendWorldPacket(packet); SendWorldPacket(packet);
} }

View File

@ -20,7 +20,16 @@ bool PlayerNameCache::AddInfo(uint64 guid, std::string name){
return AddInfo(cacheItem); return AddInfo(cacheItem);
} }
bool PlayerNameCache::AddInfo(PlayerNameCacheItem* cacheItem){ bool PlayerNameCache::IsKnown(uint64 guid)
{
for(std::vector<PlayerNameCacheItem*>::iterator i=_cache.begin(); i!=_cache.end(); i++)
if(guid==(*i)->_guid)
return true;
return false;
}
bool PlayerNameCache::AddInfo(PlayerNameCacheItem* cacheItem)
{
for(std::vector<PlayerNameCacheItem*>::iterator i=_cache.begin(); i!=_cache.end(); i++) for(std::vector<PlayerNameCacheItem*>::iterator i=_cache.begin(); i!=_cache.end(); i++)
if(cacheItem->_guid==(*i)->_guid) if(cacheItem->_guid==(*i)->_guid)
return false; return false;
@ -28,21 +37,24 @@ bool PlayerNameCache::AddInfo(PlayerNameCacheItem* cacheItem){
return true; return true;
} }
std::string PlayerNameCache::GetName(uint64 guid){ std::string PlayerNameCache::GetName(uint64 guid)
{
for(std::vector<PlayerNameCacheItem*>::iterator i=_cache.begin(); i!=_cache.end(); i++) for(std::vector<PlayerNameCacheItem*>::iterator i=_cache.begin(); i!=_cache.end(); i++)
if(guid==(*i)->_guid) if(guid==(*i)->_guid)
return (*i)->_name; return (*i)->_name;
return ""; return "";
} }
uint64 PlayerNameCache::GetGuid(std::string name){ uint64 PlayerNameCache::GetGuid(std::string name)
{
for(std::vector<PlayerNameCacheItem*>::iterator i=_cache.begin(); i!=_cache.end(); i++) for(std::vector<PlayerNameCacheItem*>::iterator i=_cache.begin(); i!=_cache.end(); i++)
if(name==(*i)->_name) if(name==(*i)->_name)
return (*i)->_guid; return (*i)->_guid;
return 0; return 0;
} }
bool PlayerNameCache::SaveToFile(void){ bool PlayerNameCache::SaveToFile(void)
{
log("Saving PlayerNameCache..."); log("Saving PlayerNameCache...");
char *fn="./cache/playernames.cache"; char *fn="./cache/playernames.cache";
std::fstream fh; std::fstream fh;
@ -71,7 +83,8 @@ bool PlayerNameCache::SaveToFile(void){
return true; return true;
} }
bool PlayerNameCache::ReadFromFile(void){ bool PlayerNameCache::ReadFromFile(void)
{
char *fn="./cache/playernames.cache"; char *fn="./cache/playernames.cache";
log("Loading PlayerNameCache..."); log("Loading PlayerNameCache...");
bool success=true; bool success=true;
@ -115,7 +128,8 @@ bool PlayerNameCache::ReadFromFile(void){
return success; return success;
} }
uint32 PlayerNameCache::GetSize(void){ uint32 PlayerNameCache::GetSize(void)
{
return _cache.size(); return _cache.size();
} }

View File

@ -9,6 +9,7 @@ struct PlayerNameCacheItem {
class PlayerNameCache { class PlayerNameCache {
public: public:
std::string GetName(uint64); std::string GetName(uint64);
bool IsKnown(uint64);
uint64 GetGuid(std::string); uint64 GetGuid(std::string);
bool AddInfo(uint64 guid, std::string name); bool AddInfo(uint64 guid, std::string name);
bool AddInfo(PlayerNameCacheItem*); bool AddInfo(PlayerNameCacheItem*);

View File

@ -103,25 +103,17 @@ void WorldSession::Update(void)
OpcodeHandler *table = _GetOpcodeHandlerTable(); OpcodeHandler *table = _GetOpcodeHandlerTable();
uint16 hpos;
bool known=false; bool known=false;
while(pktQueue.size()) while(pktQueue.size())
{ {
WorldPacket *packet = pktQueue.next(); WorldPacket *packet = pktQueue.next();
for (uint16 i = 0; table[i].handler != NULL; i++) for (hpos = 0; table[hpos].handler != NULL; hpos++)
{ {
if (table[i].opcode == packet->GetOpcode()) if (table[hpos].opcode == packet->GetOpcode())
{ {
try
{
(this->*table[i].handler)(*packet);
}
catch (...)
{
logerror("Exception while handling opcode %u!",packet->GetOpcode());
logerror("Data: pktsize=%u, handler=0x%X queuesize=%u",packet->size(),table[i].handler,pktQueue.size());
}
known=true; known=true;
break; break;
} }
@ -140,8 +132,22 @@ void WorldSession::Update(void)
|| (GetInstance()->GetConf()->showopcodes==3) ) || (GetInstance()->GetConf()->showopcodes==3) )
{ {
if(!(GetInstance()->GetConf()->hidefreqopcodes && hideOpcode)) if(!(GetInstance()->GetConf()->hidefreqopcodes && hideOpcode))
logcustom(1,YELLOW,">> Opcode %u [%s] (%s)", packet->GetOpcode(), GetOpcodeName(packet->GetOpcode()), known ? "Known" : "UNKNOWN"); logcustom(1,YELLOW,">> Opcode %u [%s] (%s, %u bytes)", packet->GetOpcode(), GetOpcodeName(packet->GetOpcode()), known ? "Known" : "UNKNOWN", packet->size());
} }
if(known)
{
try
{
(this->*table[hpos].handler)(*packet);
}
catch (...)
{
logerror("Exception while handling opcode %u!",packet->GetOpcode());
logerror("Data: pktsize=%u, handler=0x%X queuesize=%u",packet->size(),table[hpos].handler,pktQueue.size());
}
}
delete packet; delete packet;
known=false; known=false;
} }
@ -194,6 +200,8 @@ OpcodeHandler *WorldSession::_GetOpcodeHandlerTable() const
{SMSG_LEARNED_SPELL, &WorldSession::_HandleLearnedSpellOpcode}, {SMSG_LEARNED_SPELL, &WorldSession::_HandleLearnedSpellOpcode},
{SMSG_REMOVED_SPELL, &WorldSession::_HandleLearnedSpellOpcode}, {SMSG_REMOVED_SPELL, &WorldSession::_HandleLearnedSpellOpcode},
{SMSG_CHANNEL_LIST, &WorldSession::_HandleChannelListOpcode}, {SMSG_CHANNEL_LIST, &WorldSession::_HandleChannelListOpcode},
{SMSG_EMOTE, &WorldSession::_HandleEmoteOpcode},
{SMSG_TEXT_EMOTE, &WorldSession::_HandleTextEmoteOpcode},
// table termination // table termination
{ 0, NULL } { 0, NULL }
@ -687,3 +695,123 @@ void WorldSession::_HandleChannelListOpcode(WorldPacket& recvPacket)
_channels->HandleListRequest(recvPacket); _channels->HandleListRequest(recvPacket);
} }
void WorldSession::_HandleEmoteOpcode(WorldPacket& recvPacket)
{
std::string plrname;
uint32 anim; // animation id?
uint64 guid; // guid of the unit performing the emote
recvPacket >> anim >> guid;
// TODO: check if the emote came from a player or a mob, and query mob name if it was a mob
if(guid)
{
plrname=plrNameCache.GetName(guid);
if(plrname.empty())
{
SendQueryPlayerName(guid);
plrname="Unknown Entity";
}
}
// TODO: check for mobs
logdebug(I64FMT " / %s performing emote; anim=%u",guid,plrname.c_str(),anim);
// TODO: show emote in GUI :P
}
void WorldSession::_HandleTextEmoteOpcode(WorldPacket& recvPacket)
{
std::string name; // name of emote target
std::string name_from; // name of the unit performing the emote
uint32 emotetext; // emote id
int32 emotev; // variation (like different texts on /flirt and so on)
uint32 namelen; // length of name of emote target (without '\0' )
uint64 guid; // guid of the unit performing the emote
uint8 c; // temp
recvPacket >> guid >> emotetext >> *((uint32*)&emotev) >> namelen;
// get the target name, which is NOT null-terminated
for(uint32 i = 0; i < namelen; i++)
{
recvPacket >> c;
if(c)
name += c;
}
logdebug(I64FMT " Emote: name=%s text=%u variation=%i len=%u",guid,name.c_str(),emotetext,emotev,namelen);
SCPDatabaseMgr& dbmgr = GetInstance()->dbmgr;
if(dbmgr.HasDB("emote"))
{
SCPDatabase& db = dbmgr.GetDB("emote");
std::string target,target2;
bool targeted=false; // if the emote is directed to anyone around or a specific target
bool targeted_me=false; // if the emote was targeted to us if it was targeted
bool from_me=false; // if we did the emote
bool female=false; // if emote causer is female
if(GetMyChar()->GetGUID() == guid) // we caused the emote
from_me=true;
if(name.length()) // is it directed to someone?
{
targeted=true; // if yes, we have a target
if(GetMyChar()->GetName() == name) // if the name is ours, its directed to us
targeted_me=true;
}
Unit *u = (Unit*)objmgr.GetObj(guid);
if(u)
{
if(u->GetGender() != 0) // female
female=true;
name_from = u->GetName();
}
// if we targeted ourself, the general emote is used!
if(targeted && from_me && targeted_me)
targeted_me=false;
// now build the string that is used to lookup the text in the database
if(from_me)
target += "me";
else
target += "one";
if(targeted)
{
target += "to";
if(targeted_me)
target += "me";
else
target += "one";
}
else
target += "general";
// not all emotes have a female version, so check if there is one in the database
if(female && db.GetField(emotetext).HasEntry(target + "female"))
target += "female";
logdebug("Looking up 'emote' SCP field %u entry '%s'",emotetext,target.c_str());
std::string etext;
etext = db.GetField(emotetext).GetString(target);
char out[300]; // should be enough
if(from_me)
sprintf(out,etext.c_str(),name.c_str());
else
sprintf(out,etext.c_str(),name_from.c_str(),name.c_str());
logcustom(0,WHITE,"EMOTE: %s",out);
}
else
{
logerror("Can't display emote text %u, SCP database \"emote\" not loaded.",emotetext);
}
}

View File

@ -85,6 +85,8 @@ private:
void _HandleLearnedSpellOpcode(WorldPacket& recvPacket); void _HandleLearnedSpellOpcode(WorldPacket& recvPacket);
void _HandleRemovedSpellOpcode(WorldPacket& recvPacket); void _HandleRemovedSpellOpcode(WorldPacket& recvPacket);
void _HandleChannelListOpcode(WorldPacket& recvPacket); void _HandleChannelListOpcode(WorldPacket& recvPacket);
void _HandleEmoteOpcode(WorldPacket& recvPacket);
void _HandleTextEmoteOpcode(WorldPacket& recvPacket);
void _MovementUpdate(uint8 objtypeid, uint64 guid, WorldPacket& recvPacket); // Helper for _HandleUpdateObjectOpcode void _MovementUpdate(uint8 objtypeid, uint64 guid, WorldPacket& recvPacket); // Helper for _HandleUpdateObjectOpcode
void _ValuesUpdate(uint64 uguid, WorldPacket& recvPacket); // ... void _ValuesUpdate(uint64 uguid, WorldPacket& recvPacket); // ...

View File

@ -132,7 +132,19 @@ bool ConvertDBC(void)
{ {
if((*ix).getInt(stringpos)) // find out which field is used, 0 if not used if((*ix).getInt(stringpos)) // find out which field is used, 0 if not used
{ {
EmoteDataStorage[em].push_back( fname + "=" + (*ix).getString(stringpos) ); std::string tx = (*ix).getString(stringpos);
uint32 fpos=0;
// the following block replaces %x$s (where x is a number) with %s
do
{
if(fpos+4 < tx.length() && tx[fpos]=='%' && tx[fpos+2]=='$' && tx[fpos+3]=='s' && isdigit(tx[fpos+1]))
{
tx.erase(fpos,4);
tx.insert(fpos,"%s");
}
fpos++;
} while(fpos < tx.length());
EmoteDataStorage[em].push_back( fname + "=" + tx );
break; break;
} }
} }