From a3055a32f626400647626eac88cb7c759b904b6c Mon Sep 17 00:00:00 2001 From: SylvaniaCore deploy Date: Wed, 19 Aug 2026 10:41:15 +0200 Subject: [PATCH] Mercenaires : le mercenaire connait son monde, et sait ou il se tient Il tenait son role mais parlait dans le vide. Trois ajouts. Un socle sur Azeroth a l heure de la Legion : les Iles Brisees, Dalaran qui flotte, les armes prodigieuses, Sargeras, le retour d Illidan, les continents, les capitales des deux factions, la monnaie. Avec une contrainte qui compte autant que le reste : il n a JAMAIS entendu parler de ce qui vient apres - Kul Tiras, l Ombreterre, les Dragons. Un modele entraine sur tout le corpus de Warcraft racontait sinon des evenements qui n existent pas sur ce royaume. Le lieu reel ensuite : la zone et la sous-zone sont lues dans les tables du client (AreaName->Str[locale]) et injectees a chaque replique. Le mercenaire peut enfin rechigner sur le froid d une region qu il traverse vraiment. Enfin, la relance des refus passagers. Les paliers gratuits repondent regulierement 503 ou 429 ; renoncer au premier essai laissait le mercenaire muet pour une gene d une seconde. Trois tentatives espacees, dans le fil dedie - la boucle du monde n en sait rien. Un refus definitif, lui, n est jamais retente. Co-Authored-By: Claude Opus 5 --- src/server/game/Mercenary/MercenaryChat.cpp | 58 ++++++++++++++++++++- src/server/game/Mercenary/MercenaryChat.h | 3 ++ 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/src/server/game/Mercenary/MercenaryChat.cpp b/src/server/game/Mercenary/MercenaryChat.cpp index 664ef09..5537921 100644 --- a/src/server/game/Mercenary/MercenaryChat.cpp +++ b/src/server/game/Mercenary/MercenaryChat.cpp @@ -19,6 +19,7 @@ #include "Chat.h" #include "ChatPackets.h" #include "Config.h" +#include "DB2Stores.h" #include "Group.h" #include "Log.h" #include "ObjectAccessor.h" @@ -30,7 +31,9 @@ #include #include +#include #include +#include namespace { @@ -468,7 +471,34 @@ std::string MercenaryChatMgr::BuildSystemPrompt(Player* owner, Player* bot) cons << "!summon). Si ton employeur te demande d'agir en pleine conversation, reponds-lui en personnage " << "et rappelle-lui de crier son ordre avec le point d'exclamation."; + prompt << "\nMONDE : nous sommes en Azeroth, au temps de la troisieme invasion de la Legion " + << "ardente. Les Iles Brisees ont resurgi de la mer ; Dalaran la cite mage flotte " + << "au-dessus d elles ; les champions se battent pour des armes prodigieuses. Sargeras " + << "menace le monde, Illidan Hurlorage est revenu d entre les morts. Les continents " + << "connus sont Kalimdor, les Royaumes de l Est, Norfendre, la Pandarie et Draenor. " + << "L Alliance tient Hurlevent, Forgefer, Darnassus, l Exodar et Gilneas ; la Horde " + << "tient Orgrimmar, les Pitons du Tonnerre, Fossoyeuse, Lune-d Argent et la " + << "Baie-du-Butin. On paie en pieces de cuivre, d argent et d or ; cent pieces d or " + << "sont une somme. Tu connais les gobelins marchands, les gryphons et les zeppelins, " + << "les tavernes, les donjons hantes et les demons de la Legion. " + << "Tu n'as JAMAIS entendu parler de ce qui vient apres : ni de la guerre entre l'Alliance " + << "et la Horde a Kul Tiras ou Zandalar, ni de l Ombreterre, ni des Dragons. Ces noms ne " + << "te disent rien, et tu le dis franchement si on t en parle."; + prompt << "\nSITUATION : "; + LocaleConstant const locale = owner->GetSession() ? owner->GetSession()->GetSessionDbcLocale() : LOCALE_frFR; + if (AreaTableEntry const* area = sAreaTableStore.LookupEntry(bot->GetAreaId())) + { + prompt << "vous vous tenez a " << area->AreaName->Str[locale]; + // La sous-zone porte souvent un nom de hameau ; la zone situe la region. + if (bot->GetZoneId() != bot->GetAreaId()) + if (AreaTableEntry const* zone = sAreaTableStore.LookupEntry(bot->GetZoneId())) + prompt << ", en " << zone->AreaName->Str[locale]; + prompt << ". "; + } + else if (MapEntry const* map = sMapStore.LookupEntry(bot->GetMapId())) + prompt << "vous vous tenez en " << map->MapName->Str[locale] << ". "; + if (Group* group = owner->GetGroup()) prompt << "vous chevauchez a " << uint32(group->GetMembersCount()) << " en compagnie. "; @@ -656,7 +686,31 @@ std::string MercenaryChatMgr::BuildPayload(MercenaryChatRequest const& request) return json.str(); } +// Un fournisseur sature (503), ou qui trouve le rythme trop soutenu (429), +// se remet souvent en une seconde. On retente donc deux fois avant de rendre +// les armes, en espacant les tentatives. Ces attentes se passent dans le fil +// dedie aux appels : la boucle du monde n en sait rien. bool MercenaryChatMgr::Perform(MercenaryChatRequest const& request, std::string& answer, std::string& error) +{ + uint32 const maxAttempts = 3; + for (uint32 attempt = 0; attempt < maxAttempts; ++attempt) + { + long httpCode = 0; + if (PerformOnce(request, answer, error, httpCode)) + return true; + + // httpCode nul : la connexion elle-meme a echoue, cela aussi se retente. + bool const transient = httpCode == 0 || httpCode == 429 || httpCode == 500 + || httpCode == 502 || httpCode == 503 || httpCode == 504; + if (!transient || attempt + 1 == maxAttempts) + return false; + + std::this_thread::sleep_for(std::chrono::milliseconds(700 * (attempt + 1))); + } + return false; +} + +bool MercenaryChatMgr::PerformOnce(MercenaryChatRequest const& request, std::string& answer, std::string& error, long& httpCode) { CURL* curl = curl_easy_init(); if (!curl) @@ -706,7 +760,7 @@ bool MercenaryChatMgr::Perform(MercenaryChatRequest const& request, std::string& curl_easy_setopt(curl, CURLOPT_USERAGENT, "SylvaniaCore-Mercenary/1.0"); CURLcode const result = curl_easy_perform(curl); - long httpCode = 0; + httpCode = 0; curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &httpCode); curl_slist_free_all(headers); @@ -735,6 +789,8 @@ bool MercenaryChatMgr::Perform(MercenaryChatRequest const& request, std::string& text << "Modele introuvable pour votre cle"; else if (httpCode == 429) text << "Votre fournisseur refuse le rythme"; + else if (httpCode == 503 || httpCode == 502 || httpCode == 504) + text << "Votre fournisseur est surcharge, reessayez dans un instant"; else text << "Le fournisseur a repondu par une erreur"; diff --git a/src/server/game/Mercenary/MercenaryChat.h b/src/server/game/Mercenary/MercenaryChat.h index 7ee4b59..feba0cf 100644 --- a/src/server/game/Mercenary/MercenaryChat.h +++ b/src/server/game/Mercenary/MercenaryChat.h @@ -164,6 +164,9 @@ class TC_GAME_API MercenaryChatMgr static std::string BuildPayload(MercenaryChatRequest const& request); static std::string BuildUrl(MercenaryChatRequest const& request); static bool Perform(MercenaryChatRequest const& request, std::string& answer, std::string& error); + // Un seul aller-retour. Renseigne le code HTTP pour que l appelant + // sache si le refus vaut la peine d etre retente. + static bool PerformOnce(MercenaryChatRequest const& request, std::string& answer, std::string& error, long& httpCode); static std::string ExtractAnswer(uint8 provider, std::string const& body, std::string& error); // Identifiants par employeur.