AI Serverın oluşturduğu map bilgilerini alıyor.Bu maplar dosyaların çalıştığı bilgisayarda paylaşılan hafıza ( shared memory ) olarak geçiyor ve bütün dosyalar tarafından kullanabilir olarak açılıyor.
AI Server dosyaları yüklememişse,yüklüyor.Yüklenmiş ise yüklü bilgileri almayı deniyor.Eğer onuda yapamazsa Shared Memory Open Fail hatasını verdiriyor ve kendini kapatıyor : )
Bu kod ile npc pid lerini yüklüyor ..
Aşağıda AI Serverın yüklediği gibi hafızasına ITEM tablosunu yüklüyor.
Hata mesajları için detaylı bilgileri açıklıyor ..
SQLINTEGER = Sayı
SQLSMALLINT = Ufak Sayı
SQLCHAR = Bağlantı Durumu[6],Mesaj[1024]
SQLRETURN = Döndürülecek Değer
char = Kayıt[512]
SQL da tanımladığımız gibi değişken tanımlıyor ve byte değerlerini veriyor.1024 byte 1 kb tarzı ..
rc2 yi (dönen değeri) almak için SQLGetDiagRec fonksiyonu kullanılıyor.Bu yukarılarda bi yerlerde tanımlanmış ama bulamadım
Bu fonksiyon sonunda eğer alınan bir bilgi yoksa ( ' ! ' ifadesini anlatmıştım.Veri yoksa anlamına gelmekteydi ) SQL_NO_DATA verisi rc2 ye alınıyor ve ona göre işlem yapılıyor : )
Diğer bölümlerdede logstr yani tanımladığı Kayıt değişkenini log a yazdırıyor.
Kodda 10 sn içinde ebenezere bağlanılamazsa yapılacaklar anlatılıyor ..
Aujard birçok db ye bağlanmaya çalışıyor.Bunlar sql da kayıtlı olan db ler değil,kendi hafızasında oluşturduğu paylaşılan veritabanları.Bunlar için yeride sizin verdiğiniz bilgilerdeki sql a bağlanarak yapıyor ama siz oluşturduğunu görmüyorsunuz : )
Bu döngüde ınventoryniz hakkında bilgiler alınıyor.Bunlar db den değil,hafızadan alınıyor.
14 + 28 = 42 işlemini sanırım inventorydeki slot sayısı için koymuş.Sadece açıklama amaçlı oyuna bir etkisi yok.Düzeltilerek oyuna fazla inventory eklenemez : )
Iteminizin kalan dayanıklılığı,sayısı ve item kodu yükleniyor ..
Burada gene değişkenler tanımlanıyor ..
SQLCHAR Nation, Race, HairColor, Rank, Title, Level;
SQLINTEGER Exp, Loyalty, Gold, PX, PZ, PY, dwTime;
SQLCHAR Face, City, Fame, Authority, Points;
SQLSMALLINT Hp, Mp, Sp, sRet, Class, Bind, Knights;
SQLCHAR Str, Sta, Dex, Intel, Cha, Zone;
Bu işlem karakter yüklenmeye başlanırken yapılıyor.Bunun yanında bizim decode etmeden okuyamayacağımız :
TCHAR strSkill[10], strItem[400], strSerial[400];
memset( strSkill, 0x00, 10 );
memset( strItem, 0x00, 400 );
memset( strSerial, 0x00, 400 );
Skill , Item ve Serial bilgileride alınıyor.
memset = Memory Set ( Hafıza Ayarlama )
Yani bilgileri gene hafızasına alıyor : )
Aujard DBProcessNumber ile veritabanının işlendiği zamanı bölüyor.Yani DBProcessumber(1) işleminde item bilgisi yükleniyordu.Eğer orada bir hata olursa item yüklenemedi hatası veriyor.DBProcessNumber(1) deki işlemleri bitince
DBProcessNumber(2) ye geçiyor ve karakter bilgilerini yüklemeye başlıyor.Hata ve log için yapıldığını zannediyorum : )
SQL Varlığı kontrol ediliyor ..
retcode = Return Code ( Geri Döndüğünde Alınacak Veri )
Eğer retcode boş ise karakteri yükleyemiyor ve hata veriyor.Bunu mesaj oalrak değil,loga yazıyor tabiki : ) Buradaki != ifadesi eşit değil anlamında kullanılmış ..
Aujard strSerial kısmında itemlerin serial numarasını kontrol eder.Bizde dupeleri bu sistemi çözerek %100 kapatabiliriz : )
Aujard class a göre başlangıç itemi veriyor : ) Benim açıkladığım sourcelar 1098 olduğu için 1098 tablolarını inceledim.Ve 180050000 item numarasına karşılık gelen item ITEM_BASIC tablosunda Wooden Staff olarak gözüküyor : ) İşte bir örnek :
Her class a göre veriliyor .. Tabi 1098 db lerde race ve class numarası farklı.1299 larda 100 ve 200 küsürlerle başlıyor, 1098 lerde 1 den bile başlayabiliyor ..
Aşağıda karakterde bir değişiklik olduğunda ai serverdan alınan bilgi ile UPDATE_USER_DATA prosedürü çalıştırılıyor ve bilgiler ekleniyor.
Biz oyundayken aynı bu şekilde yaptığımızda kaydedildi gözüküyor ama oyunda karaktere işlemiyor.Ama aujard yapınca tak diye işliyor .. Olacak iş değil : )
Oyun veritabanı varsa binary değerlere (strSkill,strItem,strSerial) birşeyler yapılıyor ama çözemedim : )
Aujard login isteklerini kontrol eder.
AccountLogInReq = HesapGirişIstegi ( Req = Request = Istek )
Bu doğrultuda değişkenlerde tanımlanmış oluyor.Gelen istek doğrultusunda ACCOUNT_LOGIN prosedürüne bilgiler gönderiliyor ve account_login de yazanlar uygulanıyor ..
Aşağıda ırk seçimi belirleniyor ..
Yine değişkenler tanımlanıp NATION_SELECT prosedürüne bilgiler gönderiliyor .. Bunu 1098 de SiliconShadow fixlemişti : )
Yeni karakter açılmak istendiğinde client aujard a istek gönderiyor ..
Aujard da CREATE_NEW_CHAR prosedürünü çalıştırıp gerekenleri yapıyor saolsun : )
Bakın şimdi buraya dikkat .. 1098 sourcelarında karakter silme aktif,1299 sourcelarındada aktif.Yani hex ler bize açık olduğunu söylüyor çünkü DELETE_CHAR prosedürü çalıştırılıyor.
Tek sorun client in karakter silmek istendiğinde istek yerine hata mesajı göndermesi : )
DELETE_CHAR prosedürü çalıştırılıp gerekenler yapılıyor ...
Birde buraya dikkat : ) 1098 db lerde CHECK_COUPON_EVENT diye bir prosedür bulunmuyor.Ama 1098 aujard bunu okuyor.Yani buda sadece 1299 ların değil,1098 db lerinde eksik olduğunu gösteriyor : )
Sadece hesap adı gönderiliyor.Ne amaçla gönderilmiş belli değil ..
Sanırım yukarıda o hesabın eventa katılıp,katılmadığı kontrol ediliyor.Aşağıda ise event güncelleştiriliyor ..
Hesap Adı,Karakter Adı,Pid(Görünüş),Item Kodu ve Sayısı güncelleştiriliyor veya ekleniyor.Bunu sadece prosedürden anlayabiliriz ..
: Aujard ın yaptığı bazı işlemleri gösteren bir kod :
Hesabı oyuna sokar,
Karakter açar,
Karakter siler,
Karakter seçer,
Irk seçer,
Karakter bilgilerini yollar,
Hesabı düşürür,
Oyuncuyu kaydeder,
Clanları kontrol eder,( Salak 2 kere etmiş
)
User kickler,
Event yönetir ..
Aşağıda zaman aralıklarıyla yaptığı işleri siliyor ..
Shared Memory Open Fail hatasını mapları okuyamadığı için vermişti.Burada da yükleyemediği için hata vermesini sağlıyor ..
hMMFile değişkenine OpenFileMapping den gelen değer yazdırılıyor.Bu büyük ihtimal eğer harita dosyası yüklenmişse 1 , yüklenmemişse boş gönderiyor.
Eğer bu hMMFile değişkeni boş ise Shared Memory Load Fail hatası verdiriliyor ..
Burada zaman aralıklı yapacağı işlerin süreleri belirleniyor.Saniye olarak değil,milisaniye olarak ölçülüyor.Yani 1000 ile çarpılarak : ) 4000 = 4 Saniye ise 400 = 0.4 saniye yani ...
Kod:
#define PROCESS_CHECK 100 #define CONCURRENT_CHECK 200 #define SERIAL_TIME 300 #define PACKET_CHECK 400
Burada bir dosya boyutunu userdatadaki oyuncu sayısını 4000 ile çarpıp belirliyor
Tabi çıkan sayı megabyte değil,byte cinsinden ölçülüyor .. Hangi dosyaya yapıyor bulamadım ^^
AI Serverda yaptığı gibi tablonun varlığı ve açılıp,açılmaması kontrol ediliyor ..
Başlangıcı ile Sonu arasında veri yoksa ( IsEOF l l IsBOF ) boş,açılışta veri alınamazsa ( gene ! işareti ) Açılamadı hatası veriliyor ..
Burada ise hafizasına itemleri yüklüyor.AI Serverda olduğu gibi ..
Tablo sonuna kadar bilgileri yüklüyor.Biraz uzun olduğu için onlar kopyalamadım : )
Clanlara ait bilgileri yüklüyor,ırkına göre ..
Coupon Event olayına kafamı taktım : ) Buradada birkaç bilgi var belki incelersiniz .
Ne yapıldığını anlayamadım
İşte buradanda COUPON_EVENT tablosunun sekme isimlerini alıyoruz : )
Kod:
if( bCreate ) m_hMMFile = CreateFileMapping( (HANDLE)0xFFFFFFFF, NULL, PAGE_READWRITE, 0, dwfullsize, lpname ); else m_hMMFile = OpenFileMapping( FILE_MAP_ALL_ACCESS, TRUE, lpname ); if( m_hMMFile == NULL ) { strcpy( logstr , "Shared Memory Open Fail!!\r\n" ); LogFileWrite( logstr ); return FALSE; }
Bu kod ile npc pid lerini yüklüyor ..
Kod:
m_pHeader->ReadPid = _getpid();
Aşağıda AI Serverın yüklediği gibi hafızasına ITEM tablosunu yüklüyor.
Kod:
void CItemTableSet::DoFieldExchange(CFieldExchange* pFX) { //{{AFX_FIELD_MAP(CItemTableSet) pFX->SetFieldType(CFieldExchange::outputColumn); RFX_Long(pFX, _T("[Num]"), m_Num); RFX_Text(pFX, _T("[strName]"), m_strName); RFX_Byte(pFX, _T("[Kind]"), m_Kind); RFX_Byte(pFX, _T("[Slot]"), m_Slot); RFX_Byte(pFX, _T("[Race]"), m_Race); RFX_Byte(pFX, _T("[Class]"), m_Class); RFX_Int(pFX, _T("[Damage]"), m_Damage); RFX_Int(pFX, _T("[Delay]"), m_Delay); RFX_Int(pFX, _T("[Range]"), m_Range); RFX_Int(pFX, _T("[Weight]"), m_Weight); RFX_Int(pFX, _T("[Duration]"), m_Duration); RFX_Long(pFX, _T("[BuyPrice]"), m_BuyPrice); RFX_Long(pFX, _T("[SellPrice]"), m_SellPrice); RFX_Int(pFX, _T("[Ac]"), m_Ac); RFX_Byte(pFX, _T("[Countable]"), m_Countable); RFX_Long(pFX, _T("[Effect1]"), m_Effect1); RFX_Long(pFX, _T("[Effect2]"), m_Effect2); RFX_Byte(pFX, _T("[ReqLevel]"), m_ReqLevel); RFX_Byte(pFX, _T("[ReqRank]"), m_ReqRank); RFX_Byte(pFX, _T("[ReqTitle]"), m_ReqTitle); RFX_Byte(pFX, _T("[ReqStr]"), m_ReqStr); RFX_Byte(pFX, _T("[ReqSta]"), m_ReqSta); RFX_Byte(pFX, _T("[ReqDex]"), m_ReqDex); RFX_Byte(pFX, _T("[ReqIntel]"), m_ReqIntel); RFX_Byte(pFX, _T("[ReqCha]"), m_ReqCha); RFX_Byte(pFX, _T("[SellingGroup]"), m_SellingGroup); RFX_Byte(pFX, _T("[ItemType]"), m_ItemType); RFX_Int(pFX, _T("[Hitrate]"), m_Hitrate); RFX_Int(pFX, _T("[Evasionrate]"), m_Evasionrate); RFX_Int(pFX, _T("[DaggerAc]"), m_DaggerAc); RFX_Int(pFX, _T("[SwordAc]"), m_SwordAc); RFX_Int(pFX, _T("[MaceAc]"), m_MaceAc); RFX_Int(pFX, _T("[AxeAc]"), m_AxeAc); RFX_Int(pFX, _T("[SpearAc]"), m_SpearAc); RFX_Int(pFX, _T("[BowAc]"), m_BowAc); RFX_Byte(pFX, _T("[FireDamage]"), m_FireDamage); RFX_Byte(pFX, _T("[IceDamage]"), m_IceDamage); RFX_Byte(pFX, _T("[LightningDamage]"), m_LightningDamage); RFX_Byte(pFX, _T("[PoisonDamage]"), m_PoisonDamage); RFX_Byte(pFX, _T("[HPDrain]"), m_HPDrain); RFX_Byte(pFX, _T("[MPDamage]"), m_MPDamage); RFX_Byte(pFX, _T("[MPDrain]"), m_MPDrain); RFX_Byte(pFX, _T("[MirrorDamage]"), m_MirrorDamage); RFX_Byte(pFX, _T("[Droprate]"), m_Droprate); RFX_Int(pFX, _T("[StrB]"), m_StrB); RFX_Int(pFX, _T("[StaB]"), m_StaB); RFX_Int(pFX, _T("[DexB]"), m_DexB); RFX_Int(pFX, _T("[IntelB]"), m_IntelB); RFX_Int(pFX, _T("[ChaB]"), m_ChaB); RFX_Int(pFX, _T("[MaxHpB]"), m_MaxHpB); RFX_Int(pFX, _T("[MaxMpB]"), m_MaxMpB); RFX_Int(pFX, _T("[FireR]"), m_FireR); RFX_Int(pFX, _T("[ColdR]"), m_ColdR); RFX_Int(pFX, _T("[LightningR]"), m_LightningR); RFX_Int(pFX, _T("[MagicR]"), m_MagicR); RFX_Int(pFX, _T("[PoisonR]"), m_PoisonR); RFX_Int(pFX, _T("[CurseR]"), m_CurseR); //}}AFX_FIELD_MAP }
Hata mesajları için detaylı bilgileri açıklıyor ..
Kod:
void DisplayErrorMsg(SQLHANDLE hstmt) { SQLCHAR SqlState[6], Msg[1024]; SQLINTEGER NativeError; SQLSMALLINT i, MsgLen; SQLRETURN rc2; char logstr[512]; memset( logstr, NULL, 512 ); i = 1; while ((rc2 = SQLGetDiagRec(SQL_HANDLE_STMT, hstmt, i, SqlState, &NativeError, Msg, sizeof(Msg), &MsgLen)) != SQL_NO_DATA) { sprintf( logstr, "*** %s, %d, %s, %d ***\r\n", SqlState,NativeError,Msg,MsgLen ); LogFileWrite( logstr ); // TRACE("*** %s, %d, %s, %d ***\n", SqlState,NativeError,Msg,MsgLen); i++; } }
SQLSMALLINT = Ufak Sayı
SQLCHAR = Bağlantı Durumu[6],Mesaj[1024]
SQLRETURN = Döndürülecek Değer
char = Kayıt[512]
SQL da tanımladığımız gibi değişken tanımlıyor ve byte değerlerini veriyor.1024 byte 1 kb tarzı ..
rc2 yi (dönen değeri) almak için SQLGetDiagRec fonksiyonu kullanılıyor.Bu yukarılarda bi yerlerde tanımlanmış ama bulamadım
Bu fonksiyon sonunda eğer alınan bir bilgi yoksa ( ' ! ' ifadesini anlatmıştım.Veri yoksa anlamına gelmekteydi ) SQL_NO_DATA verisi rc2 ye alınıyor ve ona göre işlem yapılıyor : )
Diğer bölümlerdede logstr yani tanımladığı Kayıt değişkenini log a yazdırıyor.
Kodda 10 sn içinde ebenezere bağlanılamazsa yapılacaklar anlatılıyor ..
Kod:
m_GameDB.SetLoginTimeout (10); if( !m_GameDB.Open(NULL,FALSE,FALSE,strConnect) ) { AfxMessageBox("GameDB SQL Connection Fail..."); return FALSE; }
Aujard birçok db ye bağlanmaya çalışıyor.Bunlar sql da kayıtlı olan db ler değil,kendi hafızasında oluşturduğu paylaşılan veritabanları.Bunlar için yeride sizin verdiğiniz bilgilerdeki sql a bağlanarak yapıyor ama siz oluşturduğunu görmüyorsunuz : )
Bu döngüde ınventoryniz hakkında bilgiler alınıyor.Bunlar db den değil,hafızadan alınıyor.
Kod:
for(i = 0; i < SLOT_MAX+HAVE_MAX; i++) {// Âø¿ë°¹¼ö + ¼ÒÀ¯°¹¼ö(14+28=42) pUser->m_sItemArray[i].nNum = 0; pUser->m_sItemArray[i].sDuration = 0; pUser->m_sItemArray[i].sCount = 0; }
14 + 28 = 42 işlemini sanırım inventorydeki slot sayısı için koymuş.Sadece açıklama amaçlı oyuna bir etkisi yok.Düzeltilerek oyuna fazla inventory eklenemez : )
Iteminizin kalan dayanıklılığı,sayısı ve item kodu yükleniyor ..
Burada gene değişkenler tanımlanıyor ..
SQLCHAR Nation, Race, HairColor, Rank, Title, Level;
SQLINTEGER Exp, Loyalty, Gold, PX, PZ, PY, dwTime;
SQLCHAR Face, City, Fame, Authority, Points;
SQLSMALLINT Hp, Mp, Sp, sRet, Class, Bind, Knights;
SQLCHAR Str, Sta, Dex, Intel, Cha, Zone;
Bu işlem karakter yüklenmeye başlanırken yapılıyor.Bunun yanında bizim decode etmeden okuyamayacağımız :
TCHAR strSkill[10], strItem[400], strSerial[400];
memset( strSkill, 0x00, 10 );
memset( strItem, 0x00, 400 );
memset( strSerial, 0x00, 400 );
Skill , Item ve Serial bilgileride alınıyor.
memset = Memory Set ( Hafıza Ayarlama )
Yani bilgileri gene hafızasına alıyor : )
Aujard DBProcessNumber ile veritabanının işlendiği zamanı bölüyor.Yani DBProcessumber(1) işleminde item bilgisi yükleniyordu.Eğer orada bir hata olursa item yüklenemedi hatası veriyor.DBProcessNumber(1) deki işlemleri bitince
DBProcessNumber(2) ye geçiyor ve karakter bilgilerini yüklemeye başlıyor.Hata ve log için yapıldığını zannediyorum : )
SQL Varlığı kontrol ediliyor ..
Kod:
retcode = SQLAllocHandle( (SQLSMALLINT)SQL_HANDLE_STMT, m_GameDB.m_hdbc, &hstmt ); if (retcode != SQL_SUCCESS) { memset( logstr, 0x00, 256); sprintf( logstr, "LoadUserData Fail 000 : name=%s\r\n", userid ); // m_pMain->m_LogFile.Write(logstr, strlen(logstr)); return FALSE; }
Eğer retcode boş ise karakteri yükleyemiyor ve hata veriyor.Bunu mesaj oalrak değil,loga yazıyor tabiki : ) Buradaki != ifadesi eşit değil anlamında kullanılmış ..
Aujard strSerial kısmında itemlerin serial numarasını kontrol eder.Bizde dupeleri bu sistemi çözerek %100 kapatabiliriz : )
Aujard class a göre başlangıç itemi veriyor : ) Benim açıkladığım sourcelar 1098 olduğu için 1098 tablolarını inceledim.Ve 180050000 item numarasına karşılık gelen item ITEM_BASIC tablosunda Wooden Staff olarak gözüküyor : ) İşte bir örnek :
Kod:
180050000 22 Wooden Staff Basic Magician Item 18021000 18021000 330 341 110 0 3 0 0 40 200 10 30 5000 50 1 0 0 0 0 1 0 0 0 0 0 0 0 0
Aşağıda karakterde bir değişiklik olduğunda ai serverdan alınan bilgi ile UPDATE_USER_DATA prosedürü çalıştırılıyor ve bilgiler ekleniyor.
Kod:
wsprintf( szSQL, TEXT( "{call UPDATE_USER_DATA ( \'%s\', %d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,?,?,?)}" ), pUser->m_id, pUser->m_bNation, pUser->m_bRace, pUser->m_sClass, pUser->m_bHairColor, pUser->m_bRank, pUser->m_bTitle, pUser->m_bLevel, pUser->m_iExp, pUser->m_iLoyalty, pUser->m_bFace, pUser->m_bCity, pUser->m_bKnights, pUser->m_bFame, pUser->m_sHp, pUser->m_sMp, pUser->m_sSp, pUser->m_bStr, pUser->m_bSta, pUser->m_bDex, pUser->m_bIntel, pUser->m_bCha, pUser->m_bAuthority, pUser->m_bPoints, pUser->m_iGold, pUser->m_bZone, pUser->m_sBind, (int)(pUser->m_curx*100), (int)(pUser->m_curz*100), (int)(pUser->m_cury*100), pUser->m_dwTime );
Oyun veritabanı varsa binary değerlere (strSkill,strItem,strSerial) birşeyler yapılıyor ama çözemedim : )
Kod:
retcode = SQLAllocHandle( (SQLSMALLINT)SQL_HANDLE_STMT, m_GameDB.m_hdbc, &hstmt ); if (retcode == SQL_SUCCESS) { retcode = SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(strSkill),0, strSkill,0, &sStrSkill ); retcode = SQLBindParameter(hstmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(strItem),0, strItem,0, &sStrItem ); retcode = SQLBindParameter(hstmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(strSerial),0, strSerial,0, &sStrSerial );
Aujard login isteklerini kontrol eder.
Kod:
int CDBAgent::AccountLogInReq( char *id, char *pw ) { SQLHSTMT hstmt = NULL; SQLRETURN retcode; TCHAR szSQL[1024]; memset( szSQL, 0x00, 1024 ); SQLSMALLINT sParmRet; SQLINTEGER cbParmRet=SQL_NTS; wsprintf( szSQL, TEXT( "{call ACCOUNT_LOGIN( \'%s\', \'%s\', ?)}" ), id, pw);
Bu doğrultuda değişkenlerde tanımlanmış oluyor.Gelen istek doğrultusunda ACCOUNT_LOGIN prosedürüne bilgiler gönderiliyor ve account_login de yazanlar uygulanıyor ..
Aşağıda ırk seçimi belirleniyor ..
Kod:
BOOL CDBAgent::NationSelect(char *id, int nation) { SQLHSTMT hstmt = NULL; SQLRETURN retcode; TCHAR szSQL[1024]; memset( szSQL, 0x00, 1024 ); SQLSMALLINT sParmRet; SQLINTEGER cbParmRet=SQL_NTS; wsprintf( szSQL, TEXT( "{call NATION_SELECT ( ?, \'%s\', %d)}" ), id, nation);
Yeni karakter açılmak istendiğinde client aujard a istek gönderiyor ..
Kod:
int CDBAgent::CreateNewChar(char *accountid, int index, char *charid, int race, int Class, int hair, int face, int str, int sta, int dex, int intel, int cha) { SQLHSTMT hstmt = NULL; SQLRETURN retcode; TCHAR szSQL[1024]; memset( szSQL, 0x00, 1024 ); SQLSMALLINT sParmRet; SQLINTEGER cbParmRet=SQL_NTS; wsprintf( szSQL, TEXT( "{call CREATE_NEW_CHAR ( ?, \'%s\', %d, \'%s\', %d,%d,%d,%d,%d,%d,%d,%d,%d)}" ), accountid, index, charid, race, Class, hair, face, str, sta, dex, intel, cha );
Bakın şimdi buraya dikkat .. 1098 sourcelarında karakter silme aktif,1299 sourcelarındada aktif.Yani hex ler bize açık olduğunu söylüyor çünkü DELETE_CHAR prosedürü çalıştırılıyor.
Tek sorun client in karakter silmek istendiğinde istek yerine hata mesajı göndermesi : )
Kod:
BOOL CDBAgent::DeleteChar(int index, char *id, char *charid, char* socno) { SQLHSTMT hstmt = NULL; SQLRETURN retcode; TCHAR szSQL[1024]; memset( szSQL, 0x00, 1024 ); SQLSMALLINT sParmRet; SQLINTEGER cbParmRet=SQL_NTS; wsprintf( szSQL, TEXT( "{ call DELETE_CHAR ( \'%s\', %d, \'%s\', \'%s\', ? )}" ), id, index, charid, socno );
Birde buraya dikkat : ) 1098 db lerde CHECK_COUPON_EVENT diye bir prosedür bulunmuyor.Ama 1098 aujard bunu okuyor.Yani buda sadece 1299 ların değil,1098 db lerinde eksik olduğunu gösteriyor : )
Kod:
BOOL CDBAgent::CheckCouponEvent( const char* accountid ) { SQLHSTMT hstmt = NULL; SQLRETURN retcode; BOOL bData = TRUE, retval = FALSE; TCHAR szSQL[1024]; memset( szSQL, 0x00, 1024 ); SQLINTEGER Indexind = SQL_NTS; SQLSMALLINT sRet = 0; wsprintf(szSQL, TEXT("{call CHECK_COUPON_EVENT (\'%s\', ?)}"), accountid);
Sanırım yukarıda o hesabın eventa katılıp,katılmadığı kontrol ediliyor.Aşağıda ise event güncelleştiriliyor ..
Kod:
BOOL CDBAgent::UpdateCouponEvent( const char* accountid, char* charid, char* cpid, int itemid, int count ) { SQLHSTMT hstmt = NULL; SQLRETURN retcode; BOOL bData = TRUE, retval = FALSE; TCHAR szSQL[1024]; memset( szSQL, 0x00, 1024 ); SQLINTEGER Indexind = SQL_NTS; SQLSMALLINT sRet = 0; wsprintf(szSQL, TEXT("{call UPDATE_COUPON_EVENT (\'%s\', \'%s\', \'%s\', %d, %d)}"), accountid, charid, cpid, itemid, count);
: Aujard ın yaptığı bazı işlemleri gösteren bir kod :
Kod:
case WIZ_LOGIN:
pMain->AccountLogIn( recv_buff+index );
break;
case WIZ_NEW_CHAR:
pMain->CreateNewChar( recv_buff+index );
break;
case WIZ_DEL_CHAR:
pMain->DeleteChar( recv_buff+index );
break;
case WIZ_SEL_CHAR:
pMain->SelectCharacter( recv_buff+index );
break;
case WIZ_SEL_NATION:
pMain->SelectNation( recv_buff+index );
break;
case WIZ_ALLCHAR_INFO_REQ:
pMain->AllCharInfoReq( recv_buff+index );
break;
case WIZ_LOGOUT:
pMain->UserLogOut( recv_buff+index );
break;
case WIZ_DATASAVE:
pMain->UserDataSave( recv_buff+index );
break;
case WIZ_KNIGHTS_PROCESS:
pMain->KnightsPacket( recv_buff+index );
break;
case WIZ_CLAN_PROCESS:
pMain->KnightsPacket( recv_buff+index );
break;
case WIZ_LOGIN_INFO:
pMain->SetLogInInfo( recv_buff+index );
break;
case WIZ_KICKOUT:
pMain->UserKickOut( recv_buff+index );
break;
case WIZ_BATTLE_EVENT:
pMain->BattleEventResult( recv_buff+index );
break;
case DB_COUPON_EVENT:
pMain->CouponEvent( recv_buff+index );
break;
}
Hesabı oyuna sokar,
Karakter açar,
Karakter siler,
Karakter seçer,
Irk seçer,
Karakter bilgilerini yollar,
Hesabı düşürür,
Oyuncuyu kaydeder,
Clanları kontrol eder,( Salak 2 kere etmiş
User kickler,
Event yönetir ..
Aşağıda zaman aralıklarıyla yaptığı işleri siliyor ..
Kod:
KillTimer( PROCESS_CHECK ); KillTimer( CONCURRENT_CHECK ); KillTimer( SERIAL_TIME ); KillTimer( PACKET_CHECK );
Shared Memory Open Fail hatasını mapları okuyamadığı için vermişti.Burada da yükleyemediği için hata vermesini sağlıyor ..
Kod:
m_hMMFile = OpenFileMapping( FILE_MAP_ALL_ACCESS, TRUE, "KNIGHT_DB" ); if( m_hMMFile == NULL ) { logstr = "Shared Memory Load Fail!!"; m_hMMFile = INVALID_HANDLE_VALUE; return FALSE; }
Eğer bu hMMFile değişkeni boş ise Shared Memory Load Fail hatası verdiriliyor ..
Burada zaman aralıklı yapacağı işlerin süreleri belirleniyor.Saniye olarak değil,milisaniye olarak ölçülüyor.Yani 1000 ile çarpılarak : ) 4000 = 4 Saniye ise 400 = 0.4 saniye yani ...
Kod:
#define PROCESS_CHECK 100 #define CONCURRENT_CHECK 200 #define SERIAL_TIME 300 #define PACKET_CHECK 400
Burada bir dosya boyutunu userdatadaki oyuncu sayısını 4000 ile çarpıp belirliyor
Kod:
DWORD filesize = MAX_USER * 4000;
AI Serverda yaptığı gibi tablonun varlığı ve açılıp,açılmaması kontrol ediliyor ..
Kod:
if( !ItemTableSet.Open() ) { AfxMessageBox(_T("ItemTable Open Fail!")); return FALSE; } if(ItemTableSet.IsBOF() || ItemTableSet.IsEOF()) { AfxMessageBox(_T("ItemTable Empty!")); return FALSE; }
Burada ise hafizasına itemleri yüklüyor.AI Serverda olduğu gibi ..
Kod:
while( !ItemTableSet.IsEOF() ) { _ITEM_TABLE* pTableItem = new _ITEM_TABLE;
Clanlara ait bilgileri yüklüyor,ırkına göre ..
Kod:
m_DBAgent.LoadKnightsAllList( nation );
Coupon Event olayına kafamı taktım : ) Buradada birkaç bilgi var belki incelersiniz .
Kod:
void CAujardDlg::CouponEvent( char* pData ) { int nSid = 0, nEventNum = 0, nLen = 0, nCharLen=0, nCouponLen=0, index = 0, nType = 0, nResult = 0, send_index = 0, count = 0; int nItemID = 0, nItemCount = 0, nMessageNum = 0; char strAccountName[MAX_ID_SIZE+1]; memset( strAccountName, 0x00, MAX_ID_SIZE+1 ); char strCharName[MAX_ID_SIZE+1]; memset( strCharName, 0x00, MAX_ID_SIZE+1 ); char strCouponID[MAX_ID_SIZE+1]; memset( strCouponID, 0x00, MAX_ID_SIZE+1 ); char send_buff[256]; memset( send_buff, 0x00, 256 );
İşte buradanda COUPON_EVENT tablosunun sekme isimlerini alıyoruz : )
Kod:
nResult = m_DBAgent.UpdateCouponEvent( strAccountName, strCharName, strCouponID, nItemID, nIte
AlıntıHiFi