Welcome!

Join our community of MMO enthusiasts and game developers! By registering, you'll gain access to discussions on the latest developments in MMO server files and collaborate with like-minded individuals. Join us today and unlock the potential of MMO server development!

Join Today!

[HELP] Fix Mu Online HP Season 2

Initiate Mage
Joined
Dec 26, 2020
Messages
4
Reaction score
1
Hello everyone,

I am running into an issue with the HP, Mana, AG, SD. I am using Mu Server Season 2 OGC-Files (Version 1.00.18) with the sources.

First of all, the server was not optimized to allow past 32k stats. That has been optimized and now it works correctly. The issue is that the display of the bars in game is not showing the correct numbers.

I have tried the following to no avail:

1. Modified the Dataserver as the stats were being changed from 65k to -100. This is now resolved and displays the number correctly in the database.

2. Modified the database from miniint to int for STR, DEX, VIT, ENE.

3. Modify a season 2 client sources

If anyone has any input on how to fix this, I would greatly appreciate it.
 
Joined
Oct 8, 2006
Messages
740
Reaction score
289
Probably it's a miss calculation from the modification you did for stats.
If you have the client source, try to see where the Life and MaxLife are and what type of variable are those(same for Mana,MaxMana, AG, MaxAG, etc). I can't find a client source for this version, so I can't take a look inside.
 
Upvote 0
Initiate Mage
Joined
Dec 26, 2020
Messages
4
Reaction score
1
Zipper20032,

I appreciate your response. While looking inside the code of the client, I noticed that MaxLife and all the other variables are utilized multiple times. Please see below for some of the calls:

Visual.cpp
Code:
#include "StdAfx.h"

void VisualState::Load() 
{[INDENT]this -> Kill = TRUE;  [/INDENT]
[INDENT]this -> aIndex = 0;  [/INDENT]
[INDENT]this -> Life = 0;  
this -> MaxLife = 0;  
this -> Mana = 0;  
this -> MaxMana = 0;  
this -> Ag = 0;  
this -> MaxAg = 0;  
this -> Sd = 0;  
this -> MaxSd = 0;  
HookCall(0x5ED491, DrawHP);  
HookCall(0x5ED4B8, DrawMP);  
HookCall(0x5EB806, DrawAG);  
HookCall(0x5EBB96, DrawSD);  
HookCall(0x5EC5E8, DrawTabHP);  
HookCall(0x5EC6B2, DrawTabMP);  
HookCall(0x5EB863, DrawTabAG);  
HookCall(0x5EBBF0, DrawTabSD);  
HookCall(0x64992B, InfoHP);  
HookCall(0x649F27, InfoMP);[/INDENT]
}

void VisualState::Index(PMSG_JOINRESULT * Data) 
{[INDENT]this -> aIndex = MAKE_NUMBERW(Data -> NumberH, Data -> NumberL);  
this -> Kill = FALSE;}void VisualState::RecvHP(PMSG_REFILL * Data);[/INDENT]
} 

void VisualState::RecvHP(PMSG_REFILL* Data)
{[INDENT]if (Data -> IPos == 0xFF) [/INDENT]
[INDENT]{    [/INDENT]
[INDENT=2]this -> Life = Data -> Life;    [/INDENT]
[INDENT=2]this -> Sd = Data -> Shield;  [/INDENT]
[INDENT]}
 
[/INDENT]
[INDENT]else if (Data -> IPos == 0xFE) [/INDENT]
[INDENT]{    [/INDENT]
[INDENT=2]this -> MaxLife = Data -> Life;    [/INDENT]
[INDENT=2]this -> MaxSd = Data -> Shield;  [/INDENT]
[INDENT]}  

[/INDENT]
[INDENT]if (this -> Life > this -> MaxLife) [/INDENT]
[INDENT]{    [/INDENT]
[INDENT=2]this -> Life = this -> MaxLife;  [/INDENT]
[INDENT]}  

[/INDENT]
[INDENT]if (this -> Kill == TRUE) [/INDENT]
[INDENT]{    [/INDENT]
[INDENT=2]this -> Life = 0;    [/INDENT]
[INDENT=2]this -> Sd = 0;  [/INDENT]
[INDENT]}[/INDENT]
}


void VisualState::RecvMP(PMSG_MANASEND * Data) 
{[INDENT]if (Data -> IPos == 0xFF) 
{    [/INDENT]
[INDENT=2]this -> Mana = Data -> Mana;    
this -> Ag = Data -> BP;  [/INDENT]
[INDENT]} 

else if (Data -> IPos == 0xFE) 
{    [/INDENT]
[INDENT=2]this -> MaxMana = Data -> Mana;    
this -> MaxAg = Data -> BP;  [/INDENT]
[INDENT]}
  
if (this -> Mana > this -> MaxMana) 
{    [/INDENT]
[INDENT=2]this -> Mana = this -> MaxMana;  [/INDENT]
[INDENT]}[/INDENT]
}

void VisualState::Respawn() 
{[INDENT]this -> Kill = FALSE;  
this -> Life = this -> MaxLife;  
this -> Mana = this -> MaxMana;  
this -> Ag = this -> MaxAg;  
this -> Sd = this -> MaxSd;[/INDENT]
}

void VisualState::Killed(PMSG_DIEPLAYER * Data) 
{[INDENT]short tIndex = MAKE_NUMBERW(Data -> NumberH, Data -> NumberL);
  
if (this -> aIndex == tIndex) 
{    [/INDENT]
[INDENT=2]this -> Life = 0;    this -> Sd = 0;    
this -> Kill = TRUE;  [/INDENT]
[INDENT]}[/INDENT]
}

void VisualState::DrawHP(float PosX, float PosY, int Value, float ScaleX, float ScaleY) 
{[INDENT]DrawBar(PosX - 13.0 f, PosY - 15.0 f, Visual.Life, ScaleX, ScaleY);[/INDENT]
}

void VisualState::DrawMP(float PosX, float PosY, int Value, float ScaleX, float ScaleY) 
{[INDENT]DrawBar(PosX - 13.0 f, PosY - 15.0 f, Visual.Mana, ScaleX, ScaleY);[/INDENT]
}

void VisualState::DrawAG(float PosX, float PosY, int Value, float ScaleX, float ScaleY) 
{[INDENT]DrawBar(PosX - 10.0 f, PosY, Visual.Ag, ScaleX, ScaleY);[/INDENT]
}

void VisualState::DrawSD(float PosX, float PosY, int Value, float ScaleX, float ScaleY) 
{[INDENT]DrawBar(PosX - 15.0 f, PosY, Visual.Sd, ScaleX, ScaleY);[/INDENT]
}

void VisualState::DrawTabHP(int PosX, int PosY, LPCSTR lpString) 
{[INDENT]sprintf_s(Visual.Buffer[0], "Life: %d/%d", Visual.Life, Visual.MaxLife);  [/INDENT]
[INDENT]DrawTab(PosX, PosY, Visual.Buffer[0]);[/INDENT]
}

void VisualState::DrawTabMP(int PosX, int PosY, LPCSTR lpString) 
{[INDENT]sprintf_s(Visual.Buffer[1], "Mana: %d/%d", Visual.Mana, Visual.MaxMana);  [/INDENT]
[INDENT]DrawTab(PosX, PosY, Visual.Buffer[1]);[/INDENT]
}

void VisualState::DrawTabAG(int PosX, int PosY, LPCSTR lpString) 
{[INDENT]sprintf_s(Visual.Buffer[2], "AG: %d/%d", Visual.Ag, Visual.MaxAg);  [/INDENT]
[INDENT]DrawTab(PosX, PosY, Visual.Buffer[2]);[/INDENT]
}

void VisualState::DrawTabSD(int PosX, int PosY, LPCSTR lpString) 
{[INDENT]sprintf_s(Visual.Buffer[3], "SD: %d/%d", Visual.Sd, Visual.MaxSd);  [/INDENT]
[INDENT]DrawTab(PosX, PosY, Visual.Buffer[3]);[/INDENT]
}

void VisualState::InfoHP(int a1, int a2, LPCSTR a3, int a4, char a5, int a6) 
{[INDENT]sprintf_s(Visual.Buffer[4], "Life: %d/%d", Visual.Life, Visual.MaxLife);  [/INDENT]
[INDENT]DrawInfo(a1, a2, Visual.Buffer[4], a4, a5, a6);[/INDENT]
}

void VisualState::InfoMP(int a1, int a2, LPCSTR a3, int a4, char a5, int a6) 
{[INDENT]sprintf_s(Visual.Buffer[5], "Mana: %d/%d", Visual.Mana, Visual.MaxMana);  [/INDENT]
[INDENT]DrawInfo(a1, a2, Visual.Buffer[5], a4, a5, a6);}VisualState Visual;[/INDENT]
}

Visual.h

Code:
class VisualState
{
public:[INDENT]void Load();[/INDENT]
[INDENT]void Index(PMSG_JOINRESULT* Data);
void RecvHP(PMSG_REFILL* Data);
void RecvMP(PMSG_MANASEND* Data);
void Respawn();
void Killed(PMSG_DIEPLAYER* Data);
static void DrawHP(float a1, float a2, int Value, float a4, float a5);
static void DrawMP(float a1, float a2, int Value, float a4, float a5);
static void DrawAG(float a1, float a2, int Value, float a4, float a5);
static void DrawSD(float a1, float a2, int Value, float a4, float a5);
static void DrawTabHP(int PosX, int PosY, LPCSTR lpString);
static void DrawTabMP(int PosX, int PosY, LPCSTR lpString);
static void DrawTabAG(int PosX, int PosY, LPCSTR lpString);
static void DrawTabSD(int PosX, int PosY, LPCSTR lpString);
static void InfoHP(int a1, int a2, LPCSTR a3, int a4, char a5, int a6);
static void InfoMP(int a1, int a2, LPCSTR a3, int a4, char a5, int a6);
[/INDENT]

public:[INDENT]bool Kill;
short aIndex;
int Life;
int MaxLife;
int Mana;
int MaxMana;
int Ag;
int MaxAg;
int Sd;
int MaxSd;
char Buffer[6][50];[/INDENT]
};

extern VisualState Visual;


Finally, the link below has the download for the source code of the main that I am using. In case you want to take a deeper look into the code.

 
Upvote 0
Initiate Mage
Joined
Dec 26, 2020
Messages
4
Reaction score
1
Of course. Please see pictures below:

During spawn:

TAjRknw - [HELP] Fix Mu Online HP Season 2 - RaGEZONE Forums


After a couple of minutes of spawn:

buvr39j - [HELP] Fix Mu Online HP Season 2 - RaGEZONE Forums


In party with full set:

hZUL7aH - [HELP] Fix Mu Online HP Season 2 - RaGEZONE Forums


In party with full set and swell skill:

G3en4Y3 - [HELP] Fix Mu Online HP Season 2 - RaGEZONE Forums


Database tables:

EA29Z9Y - [HELP] Fix Mu Online HP Season 2 - RaGEZONE Forums


What I find it interesting is that in the database, the life is nowhere near what reflects in game. I am not sure if it's because of its real data type.
 

Attachments

You must be registered for see attachments list
Upvote 0
Joined
Oct 8, 2006
Messages
740
Reaction score
289
Try changing those field types with Float instead of Real(if it exists in your SQL Server ofc).
Real type variable is platform independent so it doesn't have an actual min or max value cause that depenends on what version of the programming language was used. It can be 4 and 8 bytes tho. Also, float is having a better precision. (Float : min -1.79E + 308 to max 1.79E + 308)

If I'm having a calculation like:
Real a = 15000.36;
Integer b = 15000;
Integer c = a + b; The result will be "c = 30000".

The overflowing is:
UINT16 healthPoints = 64000;
UINT32 addHealthPoints = 12000;
UINT16 currentHp = healthPoints + addHealthPoints;
The result will be: "currentHp = 32478239472" cause it overflows as UINT16 max value is 65535.


Also, the max value of the attributes must be correct or it will overflow the variables when the calculation is done like:


  • Take in consideration the fact that even if you made the client accepting the max value of WORD type or UINT16(65535), it can be a problem when the calculation is done regarding other stats/spells/skills/item options, because apparently it seems the huge numbers are due to variables overflowing cause of the stats. If you changed the variable types to UINT16 for STR,AGI,VIT,ENE, also, the calculations must have minimum of UINT16 variable type. (I think it's better having UINT32 here)


  • If you have 65539 instead of max 65535, the variable could be something like 23938384892 if it's overflowing. This is UINT16(Unsigned Interger 16 bits)



  • For INT16 (Signed Integer 16 bits), the value can be -65535 to 65535. Check your modifications too inside the Dataserver if you had done this.


I think your problem is from variables used in calculation of vitality.
@guntherdoc

Check the Protocol.cpp inside the Client.

You have this:
Code:
MOV CX, Protocol.Life;
MOV[B] [COLOR=#ff0000]WORD[/COLOR][/B] PTR DS:[EAX+0x22], CX;

MOV CX, Protocol.MaxLife;
MOV [B][COLOR=#ff0000]WORD[/COLOR][/B] PTR DS:[EAX+0x26], CX;

MOV CX, Protocol.Mana;
MOV [B][COLOR=#ff0000]WORD[/COLOR][/B] PTR DS:[EAX+0x24], CX;

MOV CX, Protocol.MaxMana;
MOV [B][COLOR=#ff0000]WORD[/COLOR][/B] PTR DS:[EAX+0x28], CX;

Change WORD to DWORD. Backup your main.exe first.
Not sure about this, but worth a try. And I think you need to move the pointers + 2 bytes from actual positions. [EAX + 0x22] to [EAX + 0x26] (WORD is 2 bytes, DWORD is 4 bytes) for each MOV operation in case you have a MOV DWORD asm operation. Not really sure tho.
 
Last edited:
Upvote 0
Initiate Mage
Joined
Dec 26, 2020
Messages
4
Reaction score
1
Zipper20032,

I appreciate all of your responses. I was able to get it fixed by changing some code at the server source. I also modified from real to float and it seems more stable as you pointed out.

Thank you very much!
 
Upvote 0
Initiate Mage
Joined
Aug 29, 2020
Messages
4
Reaction score
0
can you link what server and client file do you use please? thanks in advance :)
 
Upvote 0
Back
Top