找传奇、传世资源到传世资源站!

骑士sf-源码研究笔记

2021.5.16 技术教程 2342 编辑

骑士online源代码[端游源码]是用vc6.0编写的,采用c++/c语言。

使用了完成端口这样通讯方式,并且使用共享内存的交互方式

当然还涉及到sql2000的数据库操作。

开始分析

1.添加Aujard.ini文件及内容

根据网络上下载的要么没有这个文件要么不全,我补充了一下

[ODBC]

ACCOUNT_DSN=KN_online

ACCOUNT_UID=knight

ACCOUNT_PWD=knight

GAME_DSN=KN_online

GAME_UID=knight

GAME_PWD=knight

LOG_DSN=KN_online

LOG_UID=knight

LOG_PWD=knight

[ZONE_INFO]

GROUP_INFO=1

ZONE_INFO=1

2.程序分析

程序的入口是:BOOL CAujardDlg::OnInitDialog()函数,也就是我们主界面的初始化函数,这个函数是创建MFC Dialog界面的时候自 带的,但是主要的功能都在这里做了,所以服务端程序Aujard.exe只需要运行就可以不需要做其他操作就能工作。

下面我们一步一步分析OnInitDialog()函数里面的功能。

//------------------------------------------------------------------

// Logfile initialize 主要是记录运行日志

//-----------------------------------------------------------------

CTime time=CTime::GetCurrentTime();

char strLogFile[50];  memset(strLogFile, 0x00, 50);

wsprintf(strLogFile, "AujardLog-%d-%d-%d.txt", time.GetYear(), time.GetMonth(), time.GetDay());

m_LogFile.Open( strLogFile, CFile::modeWrite | CFile::modeCreate | CFile::modeNoTruncate | CFile::shareDenyNone );

m_LogFile.SeekToEnd();

m_iLogFileDay = time.GetDay();

 

//------------------------------------------------------------------

// 创建共享内存 需要三块共享内存

// KNIGHT_DB 保存在内存中的玩家数据最大用户3000人,每个用户4000BYTE

// KNIGHT_SEND

// KNIGHT_RECV

// 由于骑士服务端把用户以及物品等相关信息都是保存在内存中的,为了其他游戏的运

// 行速度采取的办法。(至于什么是共享内存,自己去学习吧,这里不作解释。),然

// 后每10秒钟将内存中的玩家信息保存到数据库中,更新数据库。

//-----------------------------------------------------------------

m_LoggerRecvQueue.InitailizeMMF( MAX_PKTSIZE, MAX_COUNT, SMQ_LOGGERSEND, FALSE ); // 分配 Send Queue 虚拟内存共享方式

m_LoggerSendQueue.InitailizeMMF( MAX_PKTSIZE, MAX_COUNT, SMQ_LOGGERRECV, FALSE ); // 分配 Read Queue 虚拟内存共享方式

//初始化用户KNIGHT_DB共享内存

if( !InitializeMMF() ) {

AfxMessageBox("Main Shared Memory Initialize Fail");

AfxPostQuitMessage(0);

return FALSE;

}

 

//------------------------------------------------------------------

// 读取配置文件Aujard.ini 并初始化数据库,连接数据库,使用CDBAgent类

//------------------------------------------------------------------

CString inipath;

inipath.Format( "%s\\\\Aujard.ini", GetProgPath() );

GetPrivateProfileString( "ODBC", "ACCOUNT_DSN", "", m_strAccountDSN, 24, inipath );

GetPrivateProfileString( "ODBC", "ACCOUNT_UID", "", m_strAccountUID, 24, inipath );

GetPrivateProfileString( "ODBC", "ACCOUNT_PWD", "", m_strAccountPWD, 24, inipath );

GetPrivateProfileString( "ODBC", "GAME_DSN", "", m_strGameDSN, 24, inipath );

GetPrivateProfileString( "ODBC", "GAME_UID", "", m_strGameUID, 24, inipath );

GetPrivateProfileString( "ODBC", "GAME_PWD", "", m_strGamePWD, 24, inipath );

GetPrivateProfileString( "ODBC", "LOG_DSN", "", m_strLogDSN, 24, inipath );

GetPrivateProfileString( "ODBC", "LOG_UID", "", m_strLogUID, 24, inipath );

GetPrivateProfileString( "ODBC", "LOG_PWD", "", m_strLogPWD, 24, inipath );

m_nServerNo = GetPrivateProfileInt("ZONE_INFO", "GROUP_INFO", 1, inipath);

m_nZoneNo = GetPrivateProfileInt("ZONE_INFO", "ZONE_INFO", 1, inipath);

//连接数据库使用CDBAgent类

if( !m_DBAgent.DatabaseInit() ) {

AfxPostQuitMessage(0);

return FALSE;

}

 

//------------------------------------------------------------------

// 前提:连接上数据库后

// 从数据库中加载ItemTable表保存在m_ItemtableArray队列中

//------------------------------------------------------------------

if( !LoadItemTable() ) {

AfxMessageBox("Load ItemTable Fail!!");

AfxPostQuitMessage(0);

return FALSE;

}

 

//------------------------------------------------------------------

// 创建定时器

// 三种不同时间的定时器

//------------------------------------------------------------------

SetTimer( PROCESS_CHECK, 40000, NULL );

SetTimer( CONCURRENT_CHECK, 300000, NULL );

SetTimer( PACKET_CHECK, 120000, NULL);

//定时器函数

void CAujardDlg::OnTimer(UINT nIDEvent)

{

HANDLE hProcess = NULL;

switch( nIDEvent ) {

case PROCESS_CHECK:

hProcess = OpenProcess( PROCESS_ALL_ACCESS | PROCESS_VM_READ, FALSE, m_LoggerSendQueue.GetProcessId() );

if( hProcess == NULL )

AllSaveRoutine(); // 如果SendQueue队列执行县城推出了,就保存现在内存中的数据

break;

case CONCURRENT_CHECK:

break;

case SERIAL_TIME:

g_increase_serial = 50001;

break;

case PACKET_CHECK:

WritePacketLog();

break;

}

 

CDialog::OnTimer(nIDEvent);

}

 

 

//------------------------------------------------------------------

//这个函数的主要功能:时刻读取共享内存中的数据(应该是从E文件共享内存中获取

// 来的,他们两个程序共享内存,E文件时主要的通讯文件,接受到客户端的消息后更

// 新内存,而Augard.exe就是直接读取内存,更新数据库),看一下线程函数

// ReadQueueThrea就能明白了

//

//------------------------------------------------------------------

DWORD id;

m_hReadQueueThread = ::CreateThread( NULL, 0, ReadQueueThread, (LPVOID)this, 0, &id);

 

// ReadQueueThread线程函数

// 这里记录用户登录、删除用户、选择用户。。。

while(TRUE) {

if( pMain->m_LoggerRecvQueue.GetFrontMode() != R ) {

index = 0;

recvlen = pMain->m_LoggerRecvQueue.GetData( recv_buff );

if( recvlen > MAX_PKTSIZE ) {

Sleep(1);

continue;

}

command = GetByte( recv_buff, index );

switch( command ) {

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;

}

recvlen = 0;

memset( recv_buff, NULL, 1024 );

}

}

 

Aujard.exe就分析到这里,至于里面的具体实现

大家还是自己看看吧,这里有我以前分享的骑士online源码资源

我在这里只是把功能结构给大家分析,以便大家更好研究。

 

评论

发表评论必须先登陆, 您可以 登陆 或者 注册新账号 !


在线咨询: 问题反馈
客服QQ:174666394

有问题请留言,看到后及时答复