//Salvar inventario
//Senha 3 tentativas
//nao permitir login se jah esta logado
Here I will post somethings to this source file:
[Only registered and activated users can see links. Click Here To Register...]
Use them as a learn to keep on developing it.
__________________________________________________ _____________________________________________
Contents:
"Fixing" the mp, hp and fp recovery(it runs once in a second);
Giving Skill Points when level Up;
Ban command;
Saving on logout;
UPDATE #1 First step into fixing memory leaks problem;
UPDATE #2 Error "This id has been blocked" instead of "Your time is up";
UPDATE #2 Not crashing client when GameServer isnt online;
UPDATE #2 Login Error IDs;
UPDATE #3 Changing exes place;
__________________________________________________ _____________________________________________
"Fixing" the mp, hp and fp recovery(it runs once in a second):
go into Player.h, put this after "public:" inside "class Player: public Mover, public AbstractPlayer {":
Code:
unsigned long getLastCheck(){
return lastRecovery;
}
void setLastCheck(unsigned long value){
lastRecovery = value;
}
those functions will take when it was recovered and the other one sets a new time when it recovery again
now, inside the same class, but in "private:" add this:
Code:
unsigned long lastRecovery;
this is to store the time when it last recovered.
now, at WorldThread.cpp, in "checkStats" function, add this as the first line:
Code:
player->setLastCheck(GetTickCount());
this will update the last check each time it recoverys.
then, inside PlayerThread function, find "checkStats(thisplayer);" and put:
Code:
if (GetTickCount() - thisplayer->getLastCheck() > 2000)
before it, this code will garante to just run the functions each 2000 miliseconds(2 seconds).
END OF SECTION
__________________________________________________ _____________________________________________
Giving Skill Points when level Up:
go into Levels.cpp, into the giveExp function, add this
Code:
int player_level = player->getLevel();
int skp = player->getSp();
if (player_level <= 20)
skp += 2;
else if (player_level <= 40)
skp += 3;
else if (player_level <= 60)
skp += 4;
else if (player_level <= 80)
skp += 5;
else if (player_level <= 100)
skp += 6;
else if (player_level <= 120)
skp += 7;
player->setSp(skp);
after "player->setFp(player->getMaxFp(),true);"
it takes the player level(see it is after the setLevel, so it will get the new level of the player), then get the current SkillPoints, then checks for the player level until it find the level and add the proper sp for it level, then set it to the player.
note: you will have skillpoints but wont be able to distribute since the packet to it wasnt made yet.
END OF SECTION
__________________________________________________ _____________________________________________
Ban command:
at Players.cpp, find chatProcessor function, find the last "else if" and it last "}", after it, insert this:
Code:
else if(_stricmp(command, "ban") == 0){ //check if it is the ban command
char* who = strtok_s(NULL, " ",&next_token); //take the next part just after the space after the ban word, wich is the player name
Player* who2 = getPlayerByName(who); //take the player by player name
int acc_id;
if (who2 != NULL) //if who2 is different of NULL, the player is online
{
who2->setAccessLevel(0); //sets it accesslevel ingame to 0
acc_id = who2->getOwner(); //takes the account id of that char
MySQL::setInt("accounts", "accesslvl", acc_id, 0); //update the accounts table changing its accesslevel to 0
}
else
{
char * sAcc_id = new char[]; //variable to store the account id from the characters table
MySQL::getString("characters", "name", who, "owner", sAcc_id); //takes the account id
acc_id = strval(sAcc_id); //take account id as integer
MySQL::setInt("accounts", "accesslvl", acc_id, 0); //update the accounts table changing its accesslevel to 0
delete[] sAcc_id; //delete sAcc_id from the memory(if you dont do, nobody will do for you in this case)
}
return true; //dont let !ban <player name> appears at the screen
}
END OF SECTION
__________________________________________________ _____________________________________________
Saving on logout:
inside MySQLM.cpp, find the setInt function, after it, add this:
Code:
void MySQL::setInt64(char* table, char* wht, int id, __int64 value){
setLastAccess(GetTickCount());
char query[255];
sprintf_s(query, 255, "update %s set %s='%u' where ID='%d';", table, wht, value, id);
mysql_real_query(&maple_db, query, strlen(query));
}
void MySQL::setFloat(char* table, char* wht, int id, float value){
setLastAccess(GetTickCount());
char query[255];
sprintf_s(query, 255, "update %s set %s='%f' where ID='%d';", table, wht, value, id);
mysql_real_query(&maple_db, query, strlen(query));
}
this is some code to put at the database floats and int64 values.
now, go to MySQLM.h and after the setInt declaration, put this:
Code:
static void setInt64(char* table, char* wht, int id, __int64 value);
static void setFloat(char* table, char* wht, int id, float value);
I dont know why and glaphan didnt answer but, the save function is declared as static, change it:
go to Player.h, find
Code:
static void save();
change to
now, at WorldThread.cpp, find
change to
Code:
thisplayer->save();
finally, go to Player.cpp and find the save function, put this inside:
Code:
setLastSave(GetTickCount());
MySQL::setInt("characters", "penya", getServerId(), inv->getPenya());
MySQL::setInt("characters", "haircolor", getServerId(), getHairColor());
MySQL::setInt("characters", "hair", getServerId(), getHair());
MySQL::setInt("characters", "face", getServerId(), getFace());
MySQL::setInt("characters", "gender", getServerId(), getGender());
MySQL::setInt("characters", "class", getServerId(), getJob());
MySQL::setInt64("characters", "exp", getServerId(), getExp());
MySQL::setInt("characters", "mp", getServerId(), getMP());
MySQL::setInt("characters", "fp", getServerId(), getFP());
MySQL::setInt("characters", "hp", getServerId(), getHp());
MySQL::setInt("characters", "level", getServerId(), getLevel());
MySQL::setInt("characters", "sta", getServerId(), getSta());
MySQL::setInt("characters", "intt", getServerId(), getIntt());
MySQL::setInt("characters", "dex", getServerId(), getDex());
MySQL::setInt("characters", "str", getServerId(), getStr());
MySQL::setFloat("characters", "posx", getServerId(), getPos(true).x);
MySQL::setFloat("characters", "posy", getServerId(), getPos(true).y);
MySQL::setFloat("characters", "posz", getServerId(), getPos(true).z);
MySQL::setInt("characters", "ap", getServerId(), getAp());
MySQL::setInt("characters", "sp", getServerId(), getSp());
this will get the values and update into the database.
Note: this is not saving inventory yet, I got some problems on making it and Im trying to figure it out.
Also, you can take the first tip on this guide(The fixing mp, fp and hp thing) and put it to save from time to time, but I think you can figure it out ;]
__________________________________________________ _____________________________________________
First step into fixing memory leaks problem
There is a problem that when you use, for example: "int x = new int;", you must do when you finish using it "delete x;", that happens because using the "new" keyword, it will create a space into the memory and unless you "delete" it, it wont get out of there(maybe when closing the program it does, I dont know). There is, for example at MySQLM.cpp, at the function that gets the items, it uses Equip* equip = new Equip(); inside a while statement. Lets say each Equip variable uses 13 bytes of memory...its not that much, but wait up. Since its inside a while statement that goes into all inventory slots, there can be a max of 74(I think its 74) items, making it take 962 bytes for each players that logins with a inventory full of equips. So, its almost 1kb for each player. But, its still not THAT much. Now, take a look at how many "new"'s dont have a "delete", it may get your memory full of crap and get it out of space.
Now that you now the theory, try to fix it :) search for each "new" and see if it have a delete.
One tip to make your look for "new"'s easier:
Google for "notepad++", install and run it, open all files at once inside it, ctrl+f(search box will appear), then right "new"(without quotes) and click search all opened files. Or you can use ctrl+f inside visual C++.
P.S.: If you dont know what you are doing, dont put any delete at your source.
__________________________________________________ _____________________________________________
Error "This id has been blocked" instead of "Your time is up":
When someone account is banned and he tryes to login, he gets a "Your time is up" message instead of "This id has been blocked", its a packet error(sending the wrong error ID).
To fix it, go to your login_server, go to login.cpp at loginUser function, find
Code:
case 2: LoginPacket::loginError(player, 0x80); break;
change the 0x80 to 0x77.
__________________________________________________ _____________________________________________
Not crashing client when GameServer isnt online:
This happens because even if there is no game server, it tryes to connect to it, making client crash if there isnt any game server. Lets add a check and a message for the user:
into your loginServer, go to LoginPacket.cpp, find the showWorld function, at the end, there is:
Code:
packet.packetSendLogin(player);
change it to
Code:
if (worlds.size() != 0)
packet.packetSendLogin(player);
else
LoginPacket::loginError(player, 0x88);
see about the login errors IDs at next section and change 0x88 to the error you want to be displayed.
__________________________________________________ _____________________________________________
Login Error IDs:
I tested all numbers between 100 and 255, didnt tested any before 100 so I dont know if there is any other error message.
__________________________________________________ _____________________________________________
Changing exes place:
Open your sources folder, make a new folder called exe, copy your config file inside. Now open gameServer and loginServer. At yhe left side of the screen you will see "solution explorer", if you dont see look for it at the view menu(or press ctrl+alt+L), right click GameServer and then left click properties. Into configurations properties, click in "general" and find Output directory, change it value to "$(SolutionDir)\exe"(without quotes). Make this for both loginServer and gameServer. Now go to Build > build solution.
__________________________________________________ _____________________________________________
UPDATES
UPDATE #1: 13/11/2008(dd/mm/YYYY)
Added "fixing memory leaks problem"
UPDATE #2: 15/11/2008(dd/mm/YYYY)
Added "Correcting client error message", not crashing when no GameServer is running and Login Error IDs
UPDATE #3: 15/11/2008(dd/mm/YYYY)
How to get yours exe
__________________________________________________ _____________________________________________
Thats a good start for everyone, lets get it to 100%!
I tryied to made it clear, I wish you guys got it.
Credits: The source credits is at it pages, go to it at the begining of the post, this guide and its content is credit to me :)