一般用Ghost主机游戏名字XXXXX #2,当此我想显示当前加入游戏中的人数,例如Dota,XXXXX #2(2/10),表示当前有2个人加入.
首先对于战网PVPGN不支持修改游戏名字,因此我还修改pvpgn,使得支持修改游戏名字,闲话少说,先看一下我的效果。
(在http://cid-4b5bdf2f7fd33dee.office.live.com/browse.aspx/pvpgn,可以下载我win32 x86环境下编译好的ghostcb,pvpgn1.99)
下面开始开工了
修改ghost
(1)下载ghost源码了http://www.codelain.com/forum/index.php?topic=13083.0
(2)bnet.h中,修改函数QueueGameRefresh,这个方法用于游戏创建,游戏更新的,我在这方法加入一个参数,player_num:游戏人数
void QueueGameRefresh( unsigned char state, string gameName, string hostName, CMap *map, CSaveGame *saveGame, uint32_t upTime, uint32_t hostCounter ,uint32_t player_num=0);
bnet.cpp修改QueueGameRefresh
{
if( hostName.empty( ) )
{
BYTEARRAY UniqueName = m_Protocol->GetUniqueName( );
hostName = string( UniqueName.begin( ), UniqueName.end( ) );
}
//moidfy gameName with player_num
gameName=gameName+"("+UTIL_ToString(player_num)+"/10)";//在游戏名字中显示人数
//下面的就不用改了
(3)game_base.cpp中,修改函数 function CBaseGame :: Update( void *fd, void *send_fd )
{
// don't queue a game refresh message if the queue contains more than 1 packet because they're very low priority
if( (*i)->GetOutPacketsQueued( ) <= 1 )
{
//add m_players.size() in parmeter list 传入参数当前加入游戏的人数
(*i)->QueueGameRefresh( m_GameState, m_GameName, string( ), m_Map, m_SaveGame, GetTime( ) - m_CreationTime, m_HostCounter ,m_Players.size());
Refreshed = true;
}
}
4 在game_base.cpp中,函数CBaseGame :: EventPlayerJoined和函数CBaseGame :: EventPlayerLeft的末尾添加以下代码,即当有玩家加入或者退出游戏时,更新游戏名字
{
// don't queue a game refresh message if the queue contains more than 1 packet because they're very low priority
if( (*i)->GetOutPacketsQueued( ) <= 1 )
{
(*i)->QueueGameRefresh( m_GameState, m_GameName, string( ), m_Map, m_SaveGame, GetTime( ) - m_CreationTime, m_HostCounter ,m_Players.size());
}
}
ghost就修改好了,下面则是要改pvpgn,支持游戏名字的修改。
(1)还是去下载pvpgn的源码包
pvpgn-199.r577.tar.gz |
至于pvpgn的编译,我用的vs平台,在pvpgn包内doc文件夹下有个编译方法,还是比较麻烦的,在这里我就不多说了,网上也有文章说编译1.99的 http://yjfyy.spaces.live.com/blog/cns!B6AD95965E3A9326!550.entry()
(2)在game.h里添加一个修改游戏名字的方法申明,
void game_set_name(t_game * game,const char * name);
如下
extern int game_get_count_by_clienttag(t_clienttag ct);
extern int game_is_ladder(t_game * game);
extern int game_discisloss(t_game * game);
extern int game_set_channel(t_game * game, t_channel * channel);
extern t_channel * game_get_channel(t_game * game);
// add function game_set_name
extern void game_set_name(t_game * game, const char * name);
添加一个CRITICAL_SECTION 临界区变量(注这个只是在windows下的,对于linux可以linux下互斥变量替换),用于多线程game_list的互斥访问。在这里只是申明,至于定义和初始化,我把它放在winmain.cpp中,这个在后面我再细讲
#define INCLUDED_GAME_TYPES
// add a CRITICAL_SECTION
#ifdef _USE_SECTION
#include < Windows.h >
extern CRITICAL_SECTION gamelist_section;
#endif
(3) game.cpp中,在#inlcude "game.h",前定义_USE_SECTION
#include " game.h "
#undef _USE_SECTION
在这里说明一下,为什么 要用_USE_SECTION。
若直接在game.h不用#ifdef _USE_SECTION,那不就省事了。
事实上这么弄会出很多编译错误,主要就是 windows.h惹的祸,因为这样别的地方#include "game.h",把windows.h也包含去了,会发生很多编译错误。
添加 game_set_name函数的定义
{
EnterCriticalSection(&gamelist_section);//临界区,互斥访问,以免产生读写冲突
if(stricmp(game->name,name)!=0)
{
xfree((void *)game->name);
game->name=xstrdup(name);
}
LeaveCriticalSection(&gamelist_section);
}
(4)CRITICAL_SECTION gamelist_section变量的定义及初始化(注在game.h只是申明,extern)
在winmain.cpp中 添加#include "bnet/game.h"
#include " bnetd/game.h "
#ifdef _USE_SECTION
CRITICAL_SECTION gamelist_section;
#endif
在int CALLBACK WinMain函数中添加 gamelist_section的初始化
{
#ifdef _USE_SECTION
InitializeCriticalSection(&gamelist_section);//初始化....
#endif
int result;
Console console;
if (cmdline_load(__argc, __argv) != 1) {
return -1;
}
(5)准备工作已经做好了,现在就可以更新游戏名字了
static int _client_startgame4(t_connection * c, t_packet const *const packet)这个方法用于处理游戏建立以及游戏更新,我们就在这个方法里面更新游戏名字
hanlde_bnet.cpp中static int _client_startgame4(t_connection * c, t_packet const *const packet)函数加入一行代码
#ifdef _SLOTS
//update the game name ########加入这行,要使得这个生效,在编译宏中加入_SLOTS,或者直接在game.cpp加入一行#define _SLOTS
game_set_name(currgame,gamename);
#endif
if ((status & CLIENT_STARTGAME4_STATUSMASK_OPEN_VALID) == status) {
if (status & CLIENT_STARTGAME4_STATUS_START)
game_set_status(currgame, game_status_started);
else if (status & CLIENT_STARTGAME4_STATUS_FULL)
game_set_status(currgame, game_status_full);
else
game_set_status(currgame, game_status_open);
} else {
eventlog(eventlog_level_error, __FUNCTION__, "[%d] unknown startgame4 status %d (clienttag: %s)", conn_get_socket(c), status, clienttag_uint_to_str(conn_get_clienttag(c)));
}
(6) 至此因该基本完成,不过还有一个bug,处理pvpgan服务端与客户端游戏更新不一致的情况
例如服务端游戏名字是XXXXX #1(1/10),而客户端没有及时更新游戏列表,XXXXX #1(0/10),此时客户端加入游戏时,就会出错. 修改gamelist_find_game方法,修改游戏名字查找游戏方法,我只匹配'('前面的部分,XXXXX #1(1/10)与XXXXX #1(0/10)认为是同一个游戏。
添加一个游戏名字匹配的辅助方法,这个只在game.cpp中添加就可以了,为内部函数。
{
int i=0;
if(strlen(str)<strlen(prex))
{
return false;
}
while(prex[i]!=0 && prex[i]!='(')
{
if(prex[i]!=str[i])
return false;
i++;
}
return true;
}
修改gamelist_find_game方法
{
t_elist *curr;
t_game *game;
//临界区
EnterCriticalSection(&gamelist_section);
elist_for_each(curr,&gamelist_head)
{
game = elist_entry(curr,t_game,glist_link);
if ((type==game_type_all || game->type==type)
&& ctag == game->clienttag
&& game->name
&&
//主要修改部分,游戏名字不一致时,只匹配(前面的部分
(!strcasecmp(name,game->name) || prefix_string(game->name,name)))
{
LeaveCriticalSection(&gamelist_section);
return game;
}
}
//退出临界区
LeaveCriticalSection(&gamelist_section);
return NULL;
}
修改 gamelist_find_game_available方法,同上
{
t_elist *curr;
t_game *game;
t_game_status status;
EnterCriticalSection(&gamelist_section);
elist_for_each(curr,&gamelist_head)
{
game = elist_entry(curr,t_game,glist_link);
status = game->status;
if ((type==game_type_all || game->type==type) && (ctag == game->clienttag) && (game->name)
&& [color=red]
//主要修改部分
((!strcasecmp(name,game->name) || prefix_string(game->name,name))) [/color] && (game->status != game_status_started) &&
(game->status != game_status_done))
{
LeaveCriticalSection(&gamelist_section);
return game;
}
}
LeaveCriticalSection(&gamelist_section);
return NULL;
}
最后说明一下,pvpgn要使得显示游戏人数这一功能生效,需要在译编译宏中加入_SLOTS的定义,或者直接在在hanlde_bnet.cpp最前面,加上
#define _SLOTS。
ok,完成
个人联系方式:
email :[email protected],
qq:370180103
如有问题请pm我.