a.实现基本服务器连接管理,数据协议通讯.建立一个简单的服务器框架.
客户端和 gate连接, gate 负责 转发客户端的请求 给其他服务器。 这个游戏做来耍的就不做复杂了。
login 负责处理账户数据、
game 负责基本游戏逻辑。
通许协议直接用C++结构体了,懒的去弄其他协议了。
玩家登录:
协议定义:
JMSG_PROTOCOL_(verify_login_account,jdot_string, username, jdot_string, password, anubis_net::net_ident_t, custom_key);
verify_login_account : 协议名称
username: 用户名
password: 密码
custom_key:附加数据,服务器原样返回。
登录流程:
客户端 发起 登录请求:
bool LoginModule::VerifyLoginAccount(const jdot_string&username, const jdot_string&password)
{
anubis::login_proto::verify_login_account msg;
msg.username = username;
msg.password = password;
return GetClient()->SendNetMsg(msg);
}
gate 转发登录请求:
void LoginProxy::OnNetMessage(anubis::login_proto::verify_login_account&msg, Agent*agent, NetConnection*session)
{
//--
if (m_state != kIdle) {
//++
anubis::login_proto::verify_login_account_ack ack;
ack.result_code = proto::kBusy;
session->SendMsg(ack);
return;
}
m_reqClientCustomKey = msg.custom_key;
m_username = msg.username;
m_custom_params = msg.password;
if (GetLoginStub()->VerifyLoginAccount(agent, msg)) {
m_state = kVerifing;
}
}
bool LoginStub::VerifyLoginAccount(Agent*agent, anubis::login_proto::verify_login_account&msg)
{
if (!m_session || m_session->IsClosed()) {
anubis::login_proto::verify_login_account_ack ack;
ack.username = msg.username;
ack.custom_key = msg.custom_key;
ack.result_code = proto::kInternalError;
agent->SendMsg(ack);
return false;
}
msg.custom_key.ident = agent->GetGateSaveKey();
m_session->SendMsg(msg);
return true;
}
login server 注册消息:
void LoginServer::DispatchMessage(NetConnection*session, const net::msg_header&header, net::unique_buffer_t::unpacker_t&unpacker, const net::unique_buffer_t&msg_buf)
{
bool is_handled = false;
bool unpack_success = true;
if (header.msg_type != anubis::basic_proto::set_svr_component::PROTOCOL_ID
&&session->GetTag() != 1) {
JDOT_DEBUG("session not verify,<%s,%d>", session->GetRemoteAddress().c_str(), session->GetRemotePort());
session->KickSession();
return;
}
ANUBIS_NET_MSG_SWITCH_BEGIN(is_handled, unpack_success, unpacker, header.msg_type)
ANUBIS_CASE_ON_NET_MESSAGE(anubis::basic_proto::set_svr_component, session);
ANUBIS_CASE_ON_NET_MESSAGE(anubis::login_proto::create_login_account, session);
ANUBIS_CASE_ON_NET_MESSAGE(anubis::login_proto::verify_login_account, session);
ANUBIS_CASE_ON_NET_MESSAGE(anubis::login_proto::create_role, session);
ANUBIS_NET_MSG_SWITCH_END();
if (!unpack_success) {
JDOT_ERROR("unpack message error--%d", (int)header.msg_type);
}
if (!is_handled) {
JDOT_ERROR("unhandle message --%d", (int)header.msg_type);
}
//++
}
login server 转发 到 数据库服务器:
void LoginServer::OnNetMessage(anubis::login_proto::verify_login_account&msg, NetConnection*session)
{
m_accountdb.VerifyAccount(session, msg);
}
数据库,就用 sqlite了,反正是 做来玩的:
数据库验证帐号:
void AccountDB::VerifyAccount(NetConnection*session, db_proto::verify_login_account&msg)
{
// 暂时同步处理.
jdot_nodes::login_account_nodes_v1::LoginAccount node;
node.SetUserName(msg.username);
auto ret_code = m_accountTable->ReadDB(&node, decltype(node)::FLD_UserNameName);
anubis::db_proto::verify_login_account_ack ack;
ack.username = msg.username;
ack.userid = node.GetDBID();
ack.custom_key = msg.custom_key;
if (ret_code == jdot_nodes::SqliteTable::kSuccess) {
ack.result_code =node.GetDBID()!=0 && node.GetUserPassword() == msg.password ? proto::result_code::kSuccess : proto::result_code::kFail;
}
else if(ret_code==jdot_nodes::SqliteTable::kNotExists) {
ack.result_code = proto::result_code::kFail;
}
else {
ack.result_code = proto::result_code::kInternalError;
}
if (ret_code == jdot_nodes::SqliteTable::kSuccess && node.GetDBID() != 0 && node.GetUserPassword() == msg.password) {
if (!m_dbServer->GetBaseDB()->LoginQueryRole(ack)) {
ack.result_code = proto::result_code::kInternalError;// 读数据库出错.
}
}
else {
ack.roleid = 0;
}
session->SendMsg(ack);
NET_DEBUG("check account:<%s,%s,%d>", msg.username.c_str(), msg.password.c_str(), ack.result_code);
}
数据库验证后在一路返回到客户端:
客户端消息注册:
void LoginModule::SetupHandlers()
{
ANUBIS_BEGIN_PROTO_HANDLER_MAP(GetClient()->NetDispatcher.GetMapping())
ANUBIS_USER_PROTO_HANDLER(anubis::login_proto::verify_login_account_ack, OnNetMessage);
ANUBIS_USER_PROTO_HANDLER(anubis::login_proto::create_login_account_ack, OnNetMessage);
ANUBIS_USER_PROTO_HANDLER(anubis::login_proto::create_role_ack, OnNetMessage);
ANUBIS_USER_PROTO_HANDLER(anubis::game_proto::game_load_role, OnNetMessage);
ANUBIS_USER_PROTO_HANDLER(anubis::game_proto::game_load_role_failed, OnNetMessage);
ANUBIS_END_PROTO_HANDLER_MAP();
}
客户端用的cocos开发,简单分为逻辑层,和显示控制层 2层。逻辑层不知道 显示层的存在。显示层可以直接调用逻辑层。
客户端收到验证回调后,把消息转化为事件 发出去,感兴趣的ui层 会做相应展示:
void LoginModule::OnNetMessage(ClientApp*client, anubis::login_proto::verify_login_account_ack&msg)
{
if (msg.result_code != anubis::proto::kSuccess) {
client->Publish<EVT_NetAccountLogined>(client,false);
return;
}
m_userid = msg.userid;
m_roleid = msg.roleid;
m_rolename = msg.rolename;
//++
client->Publish<EVT_NetAccountLogined>(client,true);
//++ net message .measure time|-_-!! this need connect to the lobby to do ?
if (msg.roleid != 0) {
JDOT_INFO("verify success:<%d,%s>", (int)msg.roleid, (const char*)msg.rolename.c_str());
client->MeasuredTime();
}
}
服务器基本框架就这样了,接着搞 游戏服务器逻辑。