Diablo 2 hacking (packets, d2hackit etc.) guides
Packets Guide by EvilCheese
Packets Guide by After-Death
Diablo II Packet Lists & Info
Diablo II 1.10 Skills List
D2Hackitv2 Tutorial
Battlenet Packetlist v1.11 Compilant
D2Hackit v2.00 API Reference
D2HackIt | D2HackIt Modules | D2HackIt Bots
D2Hackit v2 Module Tutorial by Lemmin
This tutorial is designed to teach you how to create a module to be used with d2h v2. This is NOT a c++ tutorial although you can very easily learn some by reading this tutorial. I'm actually still learning c++ myself, so please correct me if i say something that is completely wrong.
For this tutorial we will create a follow mod, why not?
first thing to know is that d2h modules are dynamic link libraries (dll)s. if you use visual c you can create a new project for a dll and put the include folder in the project folder. otherwise you can do it with notepad and boreland compiler. (do people need a visual c tutorial too?)
I am going to go through all the segments of code and have the whole thing posted at the bottom.
Code:
#include "includes\ClientCore.cpp"
First thing we need is the #include because we are calling definitions from d2h files. if you are using d2 v.80 (or whatever) its just "ClientCore.cpp".
Code:
CLIENTINFO
(
0,1,
"LemminMan",
"lemminman.home.comcast.net",
"Lemmin Follower",
""
)
This functuion displays the information you put into it, as well as saving the info for the help functions (i think). this will be general version information about your module. you don't need to put this function in, but its a good way to personalize it.
LemminMan = Your name!
lemminman.home.comcast.net = your website without the http protocal
Lemmin Follower = the name of your mod.
(i think the numbers are version numbers or sometihng, but i never cared about them and i dont remember what that last string is supposed to be. Some tutorial huh?)
Code:
bool on = false;
GAMEUNIT guTarget;
char chTargetName[24];
DWORD dTarget[4];
Next we declare all the variables:
1. first we have a booleen variable 'on' that is set to false if you want your mod to require a command to turn it on at start, and true if you want a command to turn it off at start.
2. And because this is a follow module, we will need a gameunit for the target that we will follow (gameunit can only be used in d2h v2 (i think)).
3. We also want an array of chars to create a string for the name of the character that we will follow. this is used for whatever reason, but in this tutorial we will have a hud showing the target.
4. And last, we need a dword for the targets id when we grab it.
Code:
BOOL PRIVATE Seton(char** argv, int argc)
{
on = true;
server->GamePrintInfo("?c4Lemmin Follower ?c3:: ?c2ON");
return true;
}
BOOL PRIVATE Setoff(char** argv, int argc)
{
on = false;
server->GamePrintInfo("?c4Lemmin Follower ?c3:: ?c1OFF");
return true;
}
These are two commands that can be executed in your module (they are implemented in the next section of code). There are on and off, which are self explainatory. whatever is put in these functions will execute whenever the respective command is typed in.
Code:
MODULECOMMANDSTRUCT ModuleCommands[]=
{
{"on", Seton, "Turn Follower On"},
{"off", Setoff, "Turn Module Off"},
};
This struct sets up the commands you want to be in your module. for now we have on and off. they corrispond to seton and setoff respectively.
Code:
VOID EXPORT OnGameJoin(THISGAMESTRUCT* thisgame)
{
server->GamePrintInfo("?c4Lemmin Follower ?c3:: ?c2Loaded!");
return;
}
This code is executed whenever you enter a game. it is also executed whenever the module is loaded (which is kind of anoying for some reasons, but there are ways around it).
Theres nothing here i want to do accept announce that its loaded.
Code:
DWORD EXPORT OnGamePacketBeforeSent(BYTE* aPacket, DWORD aLen)
{
if (aPacket[0] == 0x5d && aPacket[1] == 0x02 && aPacket[2] == 0x00)
{
memcpy(&dTarget[0], aPacket+3, 4);
guTarget.dwUnitID = dTarget[0];
server->GetUnitName(&guTarget, &chTargetName[0], 24);
if (server->VerifyUnit(&guTarget))
{
server->GameInfof("?c4Lemmin Follower ?c3:: ?c2Target set?c5: ?c3%s", chTargetName);
}
else
{
server->GamePrintInfo("?c4Lemmin Follower ?c3:: ?c2Target to far away to get name");
}
}
return aLen;
}
This code is executed just before a packet is sent, aPacket[] being the packet in question. lots there, lets break it down by line.
Code:
if (aPacket[0] == 0x5d && aPacket[1] == 0x02 && aPacket[2] == 0x00){}
This is unclicking the lil ear button next to a player (the squelch inverse thingy). so i am telling d2, if the packet starts with 5d and continues on with 02 and 00 (5d0200). basically, you click the ear button in the player menu twice to select the character you want to target.
Code:
memcpy(&dTarget[0], aPacket+3, 4);
memcpy is the function we will use to copy one dwords adress to another. aPacket to dTarget in this one.
i want all 4 bytes of the dTarget to be filled starting with the first one which is [0] in an array.
then i want to start from the 4th byte in aPacket and copy 4 bytes total. its plus 3 because it is starting at 1, so the 4th is plus 3 more.
and like i said, i want 4 bytes so the 4 at the end is the length.
Code:
guTarget.dwUnitID = dTarget[0];
and now those 4 bytes are in our gameunit struct and we can do all sorts of stuff with it. (im sure theres a way to memcpy straight to the struct, but i cant figure it out )
Code:
server->GetUnitName(&guTarget, &chTargetName[0], 24);
since we have a valid gameunit all set up, we can use all the neeto functions d2h gives us. lets start by finding the name of the gameunit we just got. we put it in the array of chars that we defined earlier. remember it was defined to have 24 chars, so thats what the next item is, the length.
Code:
if (server->VerifyUnit(&guTarget)){}
heres another neet function. this will return a non-zero if the gameunit is found in the game at the time it is called.
Code:
{
server->GameInfof("?c4Lemmin Follower ?c3:: ?c2Target set?c5: ?c3%s", chTargetName);
}
else
{
server->GamePrintInfo("?c4Lemmin Follower ?c3:: ?c2Target to far away to get name");
}
if its found we send a lil message to the game. infof can be used just as printf in c in that you use a %[letter] as the format of the variable that is being printed. %s is string.
if its not found we say its not found :p
Code:
return aLen;
everything executed? so go ahead and send that packet to the server.
Code:
DWORD EXPORT OnGamePacketBeforeReceived(BYTE* aPacket, DWORD aLen)
{
return aLen;
}
here is another useful function that is used the same as OnGamePacketBeforeSent. this is not needed for this module though.
Code:
DWORD EXPORT OnGameTimerTick(void)
{
if (on == true )
{
me->MoveToUnit(&guTarget, false);
Sleep(500);
}
return 0;
}
this next lil d2function is called errrrrtime the game loops. so you should be careful when putting code in here not to make stuff ultra spam.
anyway, if on is true, which means if you typed .mod on, than it will execute the followed code. MoveToUnit is another one of those magic d2h v2 functions that does just what its called, so we go to the target, yay! and false is to not be put in a job queue, if its true, it wont go to the next movement untill it reaches the spot from the previous MoveTo.
then so as not to spam the crap out of the d2 server with movement packets, we put about a half second pause between moving. (i dont know what the return changes, sorry )
Code:
VOID EXPORT OnDraw(CGameDC* pDC, LPCRECT lpScreenRect)
{
// Then we draw a transparent white rectangle outlines by a
// yellow frame.
pDC->SetPenColor(0xEA); // 0xA8 = yellow
pDC->SetPenWidth(1); // Frame width is 1
pDC->SetBrushColor(0x20); // 0x20 = white
pDC->SetBrushTransparency(1); // Nice-looking translucent.
RECT rect = { 325, 1, 475, 26};
pDC->Rectangle(&rect);
// Finally we draw some text at center of the rectangle, using
// a thin font.
pDC->SetFont(D2FONT_THIN);
if (server->VerifyUnit(&guTarget))
{
char chColorName[27] = "?c3";
strcat(&chColorName[0], &chTargetName[0]);
pDC->DrawText(chColorName, &rect, DT_CENTER | DT_VCENTER);
}
else
{
pDC->DrawText("?c1NO TARGET", &rect, DT_CENTER | DT_VCENTER);
}
}
And here is our hud! the drawing is confusing, so i just left the original comments by the d2h creator.
The rect coords are left, top, bottom, right.
below i will explain the code that isnt commented:
Code:
if (server->VerifyUnit(&guTarget))
{
char chColorName[27] = "?c3";
strcat(&chColorName[0], &chTargetName[0]);
pDC->DrawText(chColorName, &rect, DT_CENTER | DT_VCENTER);
}
else
{
pDC->DrawText("?c1NO TARGET", &rect, DT_CENTER | DT_VCENTER);
}
here we check to see if the unit is found in the game just like before. if it is, we do the 3 lines there.
we declare a char just like the chTargetName here, but with 3 extra chars for the d2 color code. setting it equal to "?c3" fills up the first 3 chars with the color code for blue.
now strcat, which is a fucntion similar to memcpy, that will append the string from chTargetName to chColorName. we dont need to use +2 or anything because it is appending, not copying.
now that we have a colorful string, we pop it into the DrawText function, put it in rect, and center it. (thats about as well as i can explain that, i didnt read up on the draw functions much.)
and if the unit isnt found, we just say no target.
And thats it!!
here is the code all together for you copy pasters:
Code:
#include "includes\ClientCore.cpp"
CLIENTINFO
(
0,1,
"LemminMan",
"lemminman.home.comcast.net",
"Lemmin Follower",
""
)
bool on = false;
GAMEUNIT guTarget;
char chTargetName[24];
DWORD dTarget[4];
BOOL PRIVATE Seton(char** argv, int argc)
{
on = true;
server->GamePrintInfo("?c4Lemmin Follower ?c3:: ?c2ON");
return true;
}
BOOL PRIVATE Setoff(char** argv, int argc)
{
on = false;
server->GamePrintInfo("?c4Lemmin Follower ?c3:: ?c1OFF");
return true;
}
MODULECOMMANDSTRUCT ModuleCommands[]=
{
{"on", Seton, ""},
{"off", Setoff, ""},
};
VOID EXPORT OnGameJoin(THISGAMESTRUCT* thisgame)
{
server->GamePrintInfo("?c4Lemmin Follower ?c3:: ?c2Loaded!");
return;
}
DWORD EXPORT OnGamePacketBeforeSent(BYTE* aPacket, DWORD aLen)
{
if (aPacket[0] == 0x5d && aPacket[1] == 0x02 && aPacket[2] == 0x00)
{
memcpy(&dTarget[0], aPacket+3, 4);
guTarget.dwUnitID = dTarget[0];
guTarget.dwUnitType = UNIT_TYPE_PLAYER;
server->GetUnitName(&guTarget, &chTargetName[0], 24);
if (server->VerifyUnit(&guTarget))
{
server->GameInfof("?c4Lemmin Follower ?c3:: ?c2Target set?c5: ?c3%s", chTargetName);
}
else
{
server->GamePrintInfo("?c4Lemmin Follower ?c3:: ?c2Target to far away to get name");
}
}
return aLen;
}
DWORD EXPORT OnGameTimerTick(void)
{
if (on == true )
{
me->MoveToUnit(&guTarget, false);
Sleep(1000);
}
return 0;
}
/*DWORD EXPORT OnGamePacketBeforeReceived(BYTE* aPacket, DWORD aLen)
{
return aLen;
}*/
VOID EXPORT OnDraw(CGameDC* pDC, LPCRECT lpScreenRect)
{
// Then we draw a transparent white rectangle outlines by a
// yellow frame.
pDC->SetPenColor(0xEA); // 0xA8 = yellow
pDC->SetPenWidth(1); // Frame width is 1
pDC->SetBrushColor(0x20); // 0x20 = white
pDC->SetBrushTransparency(1); // Nice-looking translucent.
RECT rect = { 325, 1, 475, 26};
pDC->Rectangle(&rect);
// Finally we draw some text at center of the rectangle, using
// a thin font.
pDC->SetFont(D2FONT_THIN);
if (server->VerifyUnit(&guTarget))
{
char chColorName[27] = "?c3";
strcat(&chColorName[0], &chTargetName[0]);
pDC->DrawText(chColorName, &rect, DT_CENTER | DT_VCENTER);
}
else
{
pDC->DrawText("?c1NO TARGET", &rect, DT_CENTER | DT_VCENTER);
}
}
hope this helped.
Buy Diablo 2 Cd Keys, FRESH from BOX!

Diablo 2 Newsletter
Questions, ideas, problems, wishes?
Be informed whenever something new comes up
(or any important problems are fixed.).
You can unsubscribe from this newsletter at any time.
|