da ich vor einigen Monaten mal ein Video gefunden habe in dem es einem User gelungen ist die Boni eines Items der Typen USE_AFFECT&USE_BLEND zu verdoppeln und bis jetzt noch kein akzeptabler Lösungsweg veröffentlicht wurde, dachte ich mir, dass es mal wieder an der Zeit ist etwas anständiges zu veröffentlichen.
Hier mal eine Anleitung wie man den Bug ausnutzt (um die ganzen Leute mit einem gamecore < 40k mal ein bisschen in Zugzwang zu bringen
Funktioniert mit:
- Drachengott-Angriff, -Verteidigung, ...
- Taus
- Gebraute Tränke
- Kritischer Treffer
- evtl. EXP-Ringe (?)
- Medallie des Glücks
- Handschuhe des Diebes (?)
- [...]
- Benutze ein Item des Typen ITEM_BLEND oder ITEM_AFFECT (bspw. Drachengott-Angriff oder Taus)
- Optional: Lege das verwendete Item in deine Quickslot Leiste
- Begib dich in die Charakterauswahl und wähle deinen Charakter aus
- Spame bereits im Ladebildschirm die zugewiesene Taste des Items in deiner Quickslot Leiste
- Herzlichen Glückwunsch! Du hast erfolgreich deinen Bonus verdoppelt.
Also, dann lasst uns das ganze mal reparieren.
Ich nehme nur mal eine Sache vorweg. Ich gebe euch keinen fertigen Code, wenn ihr das ganze lösen wollt, dann könnt ihr euch das hier durchlesen und ein bisschen euer Hirn anstrengen.
Als erstes sollten wir uns mal überlegen, wieso dieser Bug überhaupt auftritt.
Beim Verwenden eines Items der zuvor genannten Typen sollte ja eigentlich überprüft werden, ob der Affect bereits auf unseren Charakter wirkt. Wenn wir ein bisschen nach USE_AFFECT suchen, werden wir feststellen, dass bereits überprüft wird, ob der Affect des Items bereits wirkt. Falls dem so ist, returned die Funktion ja scheinbar false. Wieso ist es also trotzdem möglich, Boni zu verdoppeln?
Hmm.. Wenn wir uns die FindAffect Funktion anschauen, dann werden wir feststellen, dass wir durch eine Liste iterieren und dann, je nachdem ob der Affect bereits wirkt oder nicht, einen Pointer zu dem Affect oder einen nullpointer returnen. Naja.. Scheint ja zu passen, oder?
Dann muss es wohl daran liegen, dass die Liste zu dem Zeitpunkt der Abfrage noch leer ist. Demnach muss die Ladefunktion der Affects zu dem Zeitpunkt wohl noch nicht gecallt worden sein.
Wir erinnern uns zurück - der Bug funktioniert nur, wenn wir das Item sofort(!) nach dem Relog nochmals verwenden.
Die Affects sollten, wenn wir logisch darüber nachdenken, bei jedem Login automatisch geladen werden, da sie ja sonst nach jedem Relog oder Serverdown verschwunden wären. Wenn wir uns ein bisschen umschauen, und uns die Ladefunktion der Affects raussuchen (CHARACTER::LoadAffect) dann werden wir ein ein Problem feststellen, denn die LoadAffect-Methode wird nicht direkt beim Login aufgerufen. Hmm.. Das ist ziemlich Blöd und der Grund, wieso der Bug überhaupt möglich ist, da ansonsten ja alle Affects direkt geladen wären, die FindAffect Funktion einen validen Pointer auf ein CAffect Object returnen würde und demnach das Verwenden des Items nicht möglich wäre.
Wir sollten also die Nutzung von AFFECT Items unterbinden, solange die LoadAffect-Methode noch nicht aufgerufen wurde. Natürlich sticht uns da direkt m_bIsLoadedAffect ins Auge, da diese Variable am Ende der LoadAffect-Methode auf true gesetzt wird.
Was für ein Zufall. YMIR hat uns sogar schon eine Methode gebaut die wir verwenden können:
bool CHARACTER::IsLoadedAffect();
Gehen wir mal zurück in den Item-Part der CHARACTER Klasse und suchen einfach mal nach einem Fall, in den wir diesen Check einbauen müssen. Nach einer kurzen Suche nach USE_AFFECT werden wir auch direkt fündig (case USE_AFFECT).
Wir sehen einen FindAffect-Check und sollten demnach einfach davor checken, ob die LoadAffect-Methode bereits ausgeführt wurde.
Ich packe also direkt unter den jeweiligen Case (in meinem Beispiel USE_AFFECT) den IsLoaded Check.
Code:
[...]
case USE_AFFECT:
{
if(!IsLoadedAffect())
{
//evtl. noch eine Ausgabe an den User einbauen ♥
return false;
}
[...]
}
Nein.
Wenn wir uns ein bisschen weiter mit m_bIsLoadedAffect beschäftigen, werden wir sehen, dass es standardmäßig auf false gesetzt ist und die LoadAffect-Methode ausgeführt werden muss, damit m_bIsLoadedAffect auf true gesetzt wird.
Damit dem so ist, müssen wir dafür sorgen, dass die LoadAffect Methodeimmer beim Login ausgeführt wird. Wenn wir uns ein bisschen im Source umschauen, werden wir feststellen, dass die LoadAffect-Methode durch ein eintreffendes Paket des dbcache ausgeführt wird (CInputDB::AffectLoad(LPDESC d, const char* c_pData). Es handelt sich hierbei um das Paket HEADER_DG_AFFECT_LOAD, welches der dbcache jedoch nur versendet, wenn mindestes ein Affect in der player.affect Tabelle gefunden wird. Ansonsten returned er und sendet das Paket nicht.
-> Die LoadAffect-Methode wird also nie aufgerufen!
Naja.. Das ist für uns ein bisschen unvorteilhaft, da wir ja das Verwenden von USE_AFFECT-Items verbieten, wenn die LoadAffect-Methode nicht ausgeführt wurde. Um das zu Lösen, müssen wir das AFFECT_LOAD Paket auch versenden, wenn kein Affect gefunden wurde. Wir senden also einfach ein leeres Paket in dem wir nur die PlayerID und die Anzahl der gefunden Affects (also 0) mitsenden.
Dazu nehmen wir einfach das schon bestehende if-Statement in der CClientManager::RESULT_AFFECT_LOAD und versenden ein "leeres" Paket.
Code:
if ((iNumRows = mysql_num_rows(pRes)) == 0)
{
DWORD count = 0;
sys_log(0, "AFFECT_LOAD: NOT LOADING AFFECT FOR %d - NO RESULTS FOUND!", dwPID);
peer->EncodeHeader(HEADER_DG_AFFECT_LOAD, dwHandle, sizeof(DWORD) + sizeof(DWORD));
peer->Encode(&dwPID, sizeof(DWORD));
peer->Encode(&count, sizeof(DWORD));
return;
}
[...]
Code:
void CClientManager::RESULT_AFFECT_LOAD(CPeer* peer, MYSQL_RES* pRes, DWORD dwHandle) // wird zu void CClientManager::RESULT_AFFECT_LOAD(CPeer* peer, MYSQL_RES* pRes, DWORD dwHandle, DWORD dwPID)
Code:
RESULT_AFFECT_LOAD(peer, pSQLResult, info->dwHandle); //wird zu RESULT_AFFECT_LOAD(peer, pSQLResult, info->dwHandle, info->player_id);
Wenn ihr euch bis hierhin durchgequält habt, habt ihr das Problem gelöst!
Glückwunsch.
WICHTIG:
Diese Änderung bitte ebenfalls durchführen. Betrifft die ClientManagerPlayer.cpp:
Code:
CDBManager::instance().ReturnQuery(szQuery, QID_AFFECT, peer->GetHandle(), new ClientHandleInfo(dwHandle)); //ändern zu CDBManager::instance().ReturnQuery(szQuery, QID_AFFECT, peer->GetHandle(), new ClientHandleInfo(dwHandle, pTab->id));
Greetz,
Socialized






