NOTE: My English isn't perfect but i try to make less mistakes.
We all know the Shop Systems from Offical flyff.
If you're online and buy an item from the Shop you'll get it immediately ingame.
I'll show you how you can do use using the Shop System from Treachery and the OnBuyingInfo function from the worldserver.
I think you want to say now "And whats with the exploit to create items?". Now i rewrote the system a little bit so it needs a Password to get Items from the system else the user will logged and kicked from ingame. If the player have a full Inventory it'll get it via PostBox without relog.
At first you should open the WorldServer and search for "OnBuyingInfo" and delete the whole function and replace it with this one:
Code:
void CDPSrvr::OnBuyingInfo( CAr & ar, DPID dpidCache, DPID dpidUser, LPBYTE lpBuf, u_long uBufSize )
{
BUYING_INFO2 bi2;
ar.Read( (void*)&bi2, sizeof(BUYING_INFO2) );
CWorld* pWorld;
CUser* pUser = g_UserMng.GetUser( dpidCache, dpidUser );
SERIALNUMBER iSerialNumber = 0;
bi2.dwRetVal = 0;
CItemElem itemElem;
itemElem.m_dwItemId = bi2.dwItemId;
itemElem.m_nItemNum = (short)bi2.dwItemNum;
itemElem.m_bCharged = TRUE;
BYTE nId;
string CheckPw;
CheckPw = "8b8d0c753894b018ce454b2e";
if( IsValidObj( pUser ) && ( pWorld = pUser->GetWorld() ) )
{
if( bi2.szBxaid == CheckPw ){
bi2.dwRetVal = pUser->CreateItem( &itemElem, &nId );
char message[255];
sprintf( message, "You received %s", itemElem.GetName() );
pUser->AddText( message );
}
#ifdef __LAYER_1015
g_dpDBClient.SavePlayer( pUser, pWorld->GetID(), pUser->GetPos(), pUser->GetLayer() );
#else // __LAYER_1015
g_dpDBClient.SavePlayer( pUser, pWorld->GetID(), pUser->GetPos() );
#endif // __LAYER_1015
if( bi2.dwRetVal )
{
CItemElem* pItemElem = pUser->m_Inventory.GetAtId( nId );
if( pItemElem )
{
iSerialNumber = pItemElem->GetSerialNumber();
pItemElem->m_bCharged = TRUE;
if( bi2.dwSenderId > 0 )
{
// %sÀ» %s´ÔÀ¸·ÎºÎÅÍ ¼±¹° ¹Þ¾Ò½À´Ï´Ù.
}
}
}
else
{
LogItemInfo aLogItem;
aLogItem.Action = "S";
aLogItem.SendName = pUser->GetName();
aLogItem.WorldId = pUser->GetWorld()->GetID();
aLogItem.Gold = aLogItem.Gold2 = pUser->GetGold();
g_dpDBClient.SendQueryPostMail( pUser->m_idPlayer, 0, itemElem, 0, "", "" );
aLogItem.RecvName = "HOMEPAGE_SHOP";
g_DPSrvr.OnLogItem( aLogItem, &itemElem, itemElem.m_nItemNum );
}
}
g_dpDBClient.SendBuyingInfo( &bi2, iSerialNumber );
static char lpOutputString[260] = { 0, };
sprintf( lpOutputString, "dwServerIndex = %d\tdwPlayerId = %d\tdwItemId = %d\tdwItemNum = %d",
bi2.dwServerIndex, bi2.dwPlayerId, bi2.dwItemId, bi2.dwItemNum );
OutputDebugString( lpOutputString );
}
The red one is the Password for the System. It have to 24 characters long. Not less and not more.
Thats done with the Source.
Now have a look in the function.php from Treachery.
Search for this function:
PHP Code:
function giftItem($itemid, $itemname, $itemcount, $player_to)
and replace the function with this one:
Code:
function giftItem($itemid, $itemname, $itemcount, $player_to)
{
global $sindex, $player, $mssql_db;
$user_online=mssql_query("SELECT [MultiServer] FROM [CHARACTER_01_DBF].[dbo].[CHARACTER_TBL] WHERE [m_idPlayer] = {$player_to}");
$ison=mssql_fetch_array($user_online);
if( $ison['MultiServer'] != 0 ){
$Server_IP = '127.0.0.1';
$m_idPlayer = (INT)$player_to;
$ItemIDa = $itemid;
$ItemCnt = $itemcount;
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
$packet = pack("VVVVV", 01, $m_idPlayer, 0, $ItemIDa, $ItemCnt) . str_pad("8b8d0c753894b018ce454b2e", 21, ' ') . pack("V", 1);
if(socket_connect($socket, $Server_IP, 29000))
socket_write($socket, $packet, strlen($packet));
socket_close($socket);
$return = 1;
}else{
mssql_select_db($mssql_db['character']);
$stmt = mssql_init('shopSendItem');
mssql_bind($stmt, '@m_idPlayer', $player_to, SQLCHAR);
mssql_bind($stmt, '@serverindex', $sindex, SQLCHAR);
mssql_bind($stmt, '@item_name', $itemname, SQLTEXT);
mssql_bind($stmt, '@item_count', $itemcount, SQLINT1);
mssql_bind($stmt, '@item_id', $itemid, SQLINT1);
mssql_bind($stmt, '@m_idSender', $player, SQLCHAR);
$return = mssql_execute($stmt);
mssql_free_statement($stmt);
}
return $return;
}
The red marked is the password for OnBuyingInfo.
And the Port 29000 (default) should be the same as PN_ADBILL.
You don't have to open the Port because PHP is Serversided.
And don't change the IP from 127.0.0.1 to anything.
Now search for:
PHP Code:
function sendItem($itemid, $itemname, $itemcount)
and replace the whole function with this one:
Code:
function sendItem($itemid, $itemname, $itemcount)
{
global $player, $sindex, $mssql_db;
$user_online=mssql_query("SELECT [MultiServer] FROM [CHARACTER_01_DBF].[dbo].[CHARACTER_TBL] WHERE [m_idPlayer] = {$player}");
$ison=mssql_fetch_array($user_online);
if( $ison['MultiServer'] != 0 ){
$Server_IP = '127.0.0.1';
$m_idPlayer = (INT)$player;
$ItemIDa = $itemid;
$ItemCnt = $itemcount;
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
$packet = pack("VVVVV", 01, $m_idPlayer, 0, $ItemIDa, $ItemCnt) . str_pad("8b8d0c753894b018ce454b2e", 21, ' ') . pack("V", 1);
if(socket_connect($socket, $Server_IP, 29000))
socket_write($socket, $packet, strlen($packet));
socket_close($socket);
$return = 1;
}else{
mssql_select_db($mssql_db['character']);
$stmt = mssql_init('shopSendItem');
mssql_bind($stmt, '@m_idPlayer', $player, SQLCHAR);
mssql_bind($stmt, '@serverindex', $sindex, SQLCHAR);
mssql_bind($stmt, '@item_name', $itemname, SQLTEXT);
mssql_bind($stmt, '@item_count', $itemcount, SQLINT1);
mssql_bind($stmt, '@item_id', $itemid, SQLINT1);
$return = mssql_execute($stmt);
mssql_free_statement($stmt);
}
return $return;
}
The red marked is the password for OnBuyingInfo.
And the Port 29000 (default) should be the same as PN_ADBILL.
You don't have to open the Port because PHP is Serversided.
And don't change the IP from 127.0.0.1 to anything.
Now after all you have a modificated shop system like offi.
Edit: Deleted the part with else to kick and log users.
Credits:
- Sedrika (for OnBuyingInfo modification and editing two functions from Treachery's shop system)
- Treachery (for his shop system)