diff --git a/src/Client/DefScriptInterface.cpp b/src/Client/DefScriptInterface.cpp index 5709a3f..949fb43 100644 --- a/src/Client/DefScriptInterface.cpp +++ b/src/Client/DefScriptInterface.cpp @@ -110,7 +110,25 @@ DefReturnResult DefScriptPackage::SCemote(CmdSet& Set){ logerror("Invalid Script call: SCEmote: WorldSession not valid"); 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); return true; } diff --git a/src/Client/SCPDatabase.cpp b/src/Client/SCPDatabase.cpp index 0475dab..420d7db 100644 --- a/src/Client/SCPDatabase.cpp +++ b/src/Client/SCPDatabase.cpp @@ -62,14 +62,21 @@ bool SCPDatabase::HasField(uint32 id) bool SCPField::HasEntry(std::string e) { 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 false; } 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! @@ -78,7 +85,7 @@ uint32 SCPDatabase::GetFieldByValue(std::string entry, std::string value) for(SCPFieldMap::iterator fm = _map.begin(); fm != _map.end(); fm++) if(fm->second.HasEntry(entry) && fm->second.GetString(entry)==value) return fm->first; - return 0; + return uint32(-1); } bool SCPDatabaseMgr::HasDB(std::string n) diff --git a/src/Client/World/CMSGConstructor.cpp b/src/Client/World/CMSGConstructor.cpp index 789456b..55efad0 100644 --- a/src/Client/World/CMSGConstructor.cpp +++ b/src/Client/World/CMSGConstructor.cpp @@ -6,7 +6,8 @@ #include "WorldSession.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()) return; WorldPacket packet; @@ -34,7 +35,8 @@ void WorldSession::SendChatMessage(uint32 type, uint32 lang, std::string msg, st SendWorldPacket(packet); } -void WorldSession::SendQueryPlayerName(uint64 guid){ +void WorldSession::SendQueryPlayerName(uint64 guid) +{ if((!_logged) || guid==0) return; 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 } -void WorldSession::SendPing(uint32 ping){ +void WorldSession::SendPing(uint32 ping) +{ if(!_logged) return; WorldPacket packet; @@ -53,11 +56,13 @@ void WorldSession::SendPing(uint32 ping){ SendWorldPacket(packet); } -void WorldSession::SendEmote(uint32 id){ +void WorldSession::SendEmote(uint32 id) +{ if(!_logged) return; WorldPacket packet; - packet << id << id << GetMyChar()->GetTarget(); + int32 variation = 0; // randomized usually + packet << id << (uint32)variation << GetMyChar()->GetTarget(); packet.SetOpcode(CMSG_TEXT_EMOTE); SendWorldPacket(packet); } diff --git a/src/Client/World/CacheHandler.cpp b/src/Client/World/CacheHandler.cpp index afe1cc1..2666010 100644 --- a/src/Client/World/CacheHandler.cpp +++ b/src/Client/World/CacheHandler.cpp @@ -20,7 +20,16 @@ bool PlayerNameCache::AddInfo(uint64 guid, std::string name){ return AddInfo(cacheItem); } -bool PlayerNameCache::AddInfo(PlayerNameCacheItem* cacheItem){ +bool PlayerNameCache::IsKnown(uint64 guid) +{ + for(std::vector::iterator i=_cache.begin(); i!=_cache.end(); i++) + if(guid==(*i)->_guid) + return true; + return false; +} + +bool PlayerNameCache::AddInfo(PlayerNameCacheItem* cacheItem) +{ for(std::vector::iterator i=_cache.begin(); i!=_cache.end(); i++) if(cacheItem->_guid==(*i)->_guid) return false; @@ -28,21 +37,24 @@ bool PlayerNameCache::AddInfo(PlayerNameCacheItem* cacheItem){ return true; } -std::string PlayerNameCache::GetName(uint64 guid){ +std::string PlayerNameCache::GetName(uint64 guid) +{ for(std::vector::iterator i=_cache.begin(); i!=_cache.end(); i++) if(guid==(*i)->_guid) return (*i)->_name; return ""; } -uint64 PlayerNameCache::GetGuid(std::string name){ +uint64 PlayerNameCache::GetGuid(std::string name) +{ for(std::vector::iterator i=_cache.begin(); i!=_cache.end(); i++) if(name==(*i)->_name) return (*i)->_guid; return 0; } -bool PlayerNameCache::SaveToFile(void){ +bool PlayerNameCache::SaveToFile(void) +{ log("Saving PlayerNameCache..."); char *fn="./cache/playernames.cache"; std::fstream fh; @@ -71,7 +83,8 @@ bool PlayerNameCache::SaveToFile(void){ return true; } -bool PlayerNameCache::ReadFromFile(void){ +bool PlayerNameCache::ReadFromFile(void) +{ char *fn="./cache/playernames.cache"; log("Loading PlayerNameCache..."); bool success=true; @@ -115,7 +128,8 @@ bool PlayerNameCache::ReadFromFile(void){ return success; } -uint32 PlayerNameCache::GetSize(void){ +uint32 PlayerNameCache::GetSize(void) +{ return _cache.size(); } diff --git a/src/Client/World/CacheHandler.h b/src/Client/World/CacheHandler.h index 3e1432d..e7977b6 100644 --- a/src/Client/World/CacheHandler.h +++ b/src/Client/World/CacheHandler.h @@ -9,6 +9,7 @@ struct PlayerNameCacheItem { class PlayerNameCache { public: std::string GetName(uint64); + bool IsKnown(uint64); uint64 GetGuid(std::string); bool AddInfo(uint64 guid, std::string name); bool AddInfo(PlayerNameCacheItem*); diff --git a/src/Client/World/WorldSession.cpp b/src/Client/World/WorldSession.cpp index 3b8ff77..3faed00 100644 --- a/src/Client/World/WorldSession.cpp +++ b/src/Client/World/WorldSession.cpp @@ -103,25 +103,17 @@ void WorldSession::Update(void) OpcodeHandler *table = _GetOpcodeHandlerTable(); + + uint16 hpos; bool known=false; while(pktQueue.size()) { 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; break; } @@ -140,8 +132,22 @@ void WorldSession::Update(void) || (GetInstance()->GetConf()->showopcodes==3) ) { 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; known=false; } @@ -194,6 +200,8 @@ OpcodeHandler *WorldSession::_GetOpcodeHandlerTable() const {SMSG_LEARNED_SPELL, &WorldSession::_HandleLearnedSpellOpcode}, {SMSG_REMOVED_SPELL, &WorldSession::_HandleLearnedSpellOpcode}, {SMSG_CHANNEL_LIST, &WorldSession::_HandleChannelListOpcode}, + {SMSG_EMOTE, &WorldSession::_HandleEmoteOpcode}, + {SMSG_TEXT_EMOTE, &WorldSession::_HandleTextEmoteOpcode}, // table termination { 0, NULL } @@ -687,3 +695,123 @@ void WorldSession::_HandleChannelListOpcode(WorldPacket& 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); + } + +} + diff --git a/src/Client/World/WorldSession.h b/src/Client/World/WorldSession.h index 6ccbcf9..b00a86b 100644 --- a/src/Client/World/WorldSession.h +++ b/src/Client/World/WorldSession.h @@ -85,6 +85,8 @@ private: void _HandleLearnedSpellOpcode(WorldPacket& recvPacket); void _HandleRemovedSpellOpcode(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 _ValuesUpdate(uint64 uguid, WorldPacket& recvPacket); // ... diff --git a/src/tools/stuffextract/StuffExtract.cpp b/src/tools/stuffextract/StuffExtract.cpp index e63121d..755b2d1 100644 --- a/src/tools/stuffextract/StuffExtract.cpp +++ b/src/tools/stuffextract/StuffExtract.cpp @@ -132,7 +132,19 @@ bool ConvertDBC(void) { 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; } }