plain framework 商业版 开发总结1 updated

每天对着不同的计划,多多少少有一种无形的压力。特别是对技术不好的我来说,过程中遇到的问题实在不少,时常纠结良久。时间慢慢流逝,最后虽然感觉有些不足,但是也不至于差强人意。商业版的PF核心已经升级到1.0.5版本,增添和完善了许多功能。核心主要完善了网络模块、脚本模块、文件模块、引擎模块、缓存模块等等,并制作了基本的场景插件和AI插件。未来的PF将支持更多的功能,将安装和使用更加的自动化,执行一个脚本就能完成许多不必要的步骤。所以未来的脚本工具是一个必须完成的功能,在此我们将迎来一个个热爱开源项目的同仁和期待你们的加入。5月商业版服务器的开发总结,将全面将制作原理和遇到的麻烦分享,希望大家能够一起进步。原理好比是渔网和捕鱼的技术,虽然没有源代码,我想比源代码来的更加有价值。如果大家愿意,可以加入我们的群共同讨论。

计划表

  每日都在计划表中纠结,一步步辛酸,一步步艰难的前行。

 

CODE

  部分商业核心代码,暂时不予开源。

#include "pf/base/string.h"
#include "pf/base/log.h"
#include "pf/base/time_manager.h"
#include "pf/net/packet/factorymanager.h"
#include "pf/sys/thread.h"
#include "common/define/enum.h"
#include "common/define/macros.h"
#include "common/define/net/packet/id/clientlogin.h"
#include "common/define/net/packet/id/billinglogin.h"
#include "common/define/net/packet/id/logincenter.h"
#include "common/net/packet/client_tologin/ask_login.h"
#include "common/net/packet/login_toclient/ask_login.h"
#include "common/net/packet/login_tocenter/role_kick.h"
#include "common/net/packet/login_tobilling/auth.h"
#include "common/net/packet/billing_tologin/auth.h"
#include "common/net/packet/login_toclient/turn_status.h"
#include "connection/login.h"
#include "engine/system.h"
#include "connection/queue/turn.h"
#include "logic/login/controllers/account.h"

using namespace logic::login;
using namespace common::define::net::packet;

AccountController::AccountController() {
  core_ = NULL;
}

AccountController::~AccountController() {
  //do nothing
}

bool AccountController::setuplayout() {
  return true;
}

int32_t AccountController::request(uint8_t type, void *data) {
  __ENTER_FUNCTION
    int32_t result = -1;
    switch (type) {
      case kLogicRequestNet: {
        logic_requestnet_t *netdata = static_cast<logic_requestnet_t *>(data);
        result = net_handle(netdata);
        break;
      }
      case kLogicRequestUICommand:
        break;
      case kLogicRequestDB:
        break;
      default:
        break;
    }
    return result;
  __LEAVE_FUNCTION
    return -1;
}

void AccountController::set_core(logic::Interface *core) {
  core_ = core;
}

logic::Interface *AccountController::get_core() {
  return core_;
}

int32_t AccountController::ask_login(logic_requestnet_t *netdata) {
  __ENTER_FUNCTION
    using namespace pf_base::string;
    using namespace common::net::packet;
    char account[ACCOUNT_LENGTH_MAX] = {0}; 
    connection::Login *connection = 
      dynamic_cast<connection::Login *>(netdata->connection);
    client_tologin::AskLogin *packet = 
      dynamic_cast<client_tologin::AskLogin *>(netdata->packet);
    safecopy(account, packet->get_account(), sizeof(account));
    if (0 == strlen(account) || !checkstr(account, sizeof(account))) {
      login_toclient::AskLogin msg;
      msg.set_result(kLoginResultAuthFail);
      connection->sendpacket(&msg);
      SLOW_LOG(LOGIC_MODULENAME,
               "[logic.login] (AccountController::ask_login) account is empty."
               " account: %s, version: %d",
               packet->get_account(),
               _MAIN_VERSION);
      return kPacketExecuteStatusContinue;
    }
    connection->setaccount(packet->get_account());
    connection->setstatus(kConnectionStatusLoginWaitingAuth);
    connection->set_billingtime(TIME_MANAGER_POINTER->get_saved_time());
    login_tobilling::Auth *msg = dynamic_cast<login_tobilling::Auth *>( 
      NET_PACKET_FACTORYMANAGER_POINTER
      ->createpacket(id::login_tobilling::kAuth));
    Assert(msg);
    msg->set_account(packet->get_account());
    msg->set_password(packet->get_password());
    msg->set_connectionid(connection->getid());
    msg->set_token(packet->get_token());
    msg->set_ip(connection->getsocket()->host_);
    CONNECTION_MANAGER_SERVER_POINTER
      ->syncpacket(msg, kConnectServerTypeBilling);
    return kPacketExecuteStatusContinue;
  __LEAVE_FUNCTION
    return -1;
}

int32_t AccountController::auth(logic_requestnet_t *netdata) {
  __ENTER_FUNCTION
    uint64_t current_threadid = pf_sys::get_current_thread_id();
    int32_t result = kPacketExecuteStatusContinue;
    if (current_threadid == CONNECTION_MANAGER_INCOMING_POINTER->threadid_) {
      result = auth_toincomming(netdata);
    } else if (current_threadid == 
        CONNECTION_MANAGER_LOGIN_POINTER->threadid_) {
      result = auth_tologin(netdata);
    }
    return result;
  __LEAVE_FUNCTION
    return kPacketExecuteStatusError;
}

int32_t AccountController::auth_toincomming(logic_requestnet_t *netdata) {
  __ENTER_FUNCTION
    using namespace common::net::packet;
    billing_tologin::Auth *packet = 
      dynamic_cast<billing_tologin::Auth *>(netdata->packet);
    connection::Login *connection = 
      dynamic_cast<connection::Login *>(netdata->connection);
    if (strcmp(connection->getaccount(), packet->get_account()) != 0) {
      SLOW_ERRORLOG(LOGIC_MODULENAME,
                    "[logic.login] (AccountController::auth_toincomming)"
                    " account error."
                    " account: %s, packet account: %s",
                    connection->getaccount(),
                    packet->get_account());
      return kPacketExecuteStatusContinue;
    }
    uint32_t time = connection->get_billingtime();
    enum { kBillingTimeMax = 600000, };
    if (TIME_MANAGER_POINTER->get_saved_time() > time + kBillingTimeMax) {
      SLOW_DEBUGLOG(LOGIC_MODULENAME,
                    "[logic.login] (AccountController::auth) time out.");
      return kPacketExecuteStatusContinue;
    }
    if (kConnectionStatusLoginWaitingAuth == connection->getstatus()) {
      if (kLoginResultSuccess == packet->get_result() && 
          GLOBAL_VALUES["app_status"] != kAppStatusStop) {
        connection->setstatus(kConnectionStatusLoginAuthed);
        CONNECTION_MANAGER_INCOMING_POINTER->erase(connection);
        CONNECTION_MANAGER_LOGIN_POINTER //发送到登陆管理器线程
          ->sendpacket(packet, connection->getid());
        connection->setstatus(kConnectionStatusLoginProcessTurn);
      } else {
        if (kLoginResultOtherOnline == packet->get_result()) {
          //这个暂时用不到
          login_tocenter::RoleKick *msg = 
            dynamic_cast<login_tocenter::RoleKick *>(
                NET_PACKET_FACTORYMANAGER_POINTER
                ->createpacket(id::login_tocenter::kRoleKick));
          Assert(msg);
          msg->set_account(packet->get_account());
          CONNECTION_MANAGER_SERVER_POINTER
            ->syncpacket(msg, kConnectServerTypeCenter);
        }
        login_toclient::AskLogin msg;
        msg.set_result(packet->get_result());
        if (GLOBAL_VALUES["app_status"] == kAppStatusStop) {
          msg.set_result(kLoginResultStopService);
        }
        CONNECTION_MANAGER_LOGIN_POINTER->sendpacket(&msg, connection->getid());
      }
    }
    return kPacketExecuteStatusContinue;
  __LEAVE_FUNCTION
    return kPacketExecuteStatusError;
}
   
int32_t AccountController::auth_tologin(logic_requestnet_t *netdata) {
  __ENTER_FUNCTION
    using namespace common::net::packet;
    billing_tologin::Auth *packet = 
      dynamic_cast<billing_tologin::Auth *>(netdata->packet);
    connection::Login *connection = 
      dynamic_cast<connection::Login *>(netdata->connection);
    if (strcmp(connection->getaccount(), packet->get_account()) != 0) {
      SLOW_ERRORLOG(LOGIC_MODULENAME,
                    "[logic.login] (AccountController::auth_tologin)"
                    " account error."
                    " account: %s, packet account: %s",
                    connection->getaccount(),
                    packet->get_account());
      return kPacketExecuteStatusContinue;
    }
    CONNECTION_MANAGER_LOGIN_POINTER->add(connection);
    login_toclient::AskLogin msg;
    msg.set_result(kLoginResultSuccess);
    CONNECTION_MANAGER_LOGIN_POINTER->sendpacket(&msg, connection->getid());
    uint16_t queuepos = 0;
    CONNECTION_QUEUE_TURN_POINTER->erase(connection->getaccount(), 
                                         connection->getid());
    if (CONNECTION_QUEUE_TURN_POINTER->addin(
          connection->getid(), connection->getaccount(), queuepos)) {
      connection->set_queueposition(queuepos);
      connection->set_last_sendmessage_turntime(
          TIME_MANAGER_POINTER->get_tickcount());
      login_toclient::TurnStatus msg;
      msg.set_status(kLoginTurnStatusInTurn);
      msg.set_number(
          CONNECTION_QUEUE_TURN_POINTER->calculate_turnnumber(queuepos));
      connection->sendpacket(&msg);
    } else {
      SLOW_WARNINGLOG(LOGIC_MODULENAME,
                      "[logic.login] (AccountController::auth_tologin)"
                      " the turn is full. account: %s",
                      connection->getaccount());
      CONNECTION_MANAGER_LOGIN_POINTER->remove(connection);
      return kPacketExecuteStatusError;
    }
    return kPacketExecuteStatusContinue;
  __LEAVE_FUNCTION
    return kPacketExecuteStatusError;
}

int32_t AccountController::net_handle(logic_requestnet_t *netdata) {
  __ENTER_FUNCTION
    if (is_null(netdata) || 
        is_null(netdata->connection) || 
        is_null(netdata->packet)) return kPacketExecuteStatusError;
    int32_t result = kPacketExecuteStatusContinue;
    uint16_t packetid = netdata->packet->getid();
    switch (packetid) {
      case id::client_tologin::kAskLogin:
        result = ask_login(netdata);
        break;
      case id::billing_tologin::kAuth:
        result = auth(netdata);
        break;
      default:
        break;
    }
    return result;
  __LEAVE_FUNCTION
    return -1;
}

开发任务

  本次商业版的开发历时两月,主要的任务是完成或至少理清一种游戏服务器构架,并完成PF核心的升级(商业版本的目前版本为1.0.5d)。

完成任务

  本次完成了一种服务器的构架,并支持构架扩展(重量级和轻量级)。未完成的任务是游戏的DEMO制作,主要原因是客户端方面引擎的不熟悉,因为本次将使用我未接触过的cocos2dx引擎。

服务器功能

  此次的服务器支持共享缓存(以共享内存作为数据缓存,并可以通过缓存进行数据库查询)。服务器分为消费服务器、共享缓存、登陆服务器、数据服务器、中心服务器、游戏服务器。游戏服务器的设计中支持跨服登陆,跨服的数据共享,跨服的数据保存。逻辑系统使用MVC模式,包括脚本都使用统一的模式管理。

  性能上此次中心服务器和游戏服务器分别有一个共享缓存服务器,一组正常的服务器至少有七个服务器(其中消费服务器可以共享)。在没有场景服务的测试和压力测试下,服务器的平均负载为0.3,即单个CPU(线程)的占用为30%。内存为游戏服务器支持5000的玩家缓存情况下,占用为1.6G。其中场景和连接的CPU占用和内存消耗占时可以计算出来,争取做到单CPU可以流畅运行,内存最大的峰值(包括脚本占用)为5G即可。

  消费服务器功能为账号验证和游戏中点卡或充值服务。

  数据服务器为多线程模式,主要提供数据库查询服务器,每个线程对应一个数据库连接器,保证数据库能够快速查询。线程使用线程池管理,分为查询和保存的线程池,将数据库进行读写分离。

  共享缓存作为以内存共享为基础,用来对数据进行缓存,主要是数据库方面的缓存。

  登陆服务器作为第一个与客户端连接的验证,扮演着网关的角色。

  中心服务器作为控制游戏服务器和登陆服务器的中心指挥,作为整个应用中的核心,在中心服务器上存储着角色的在线信息以及全局信息(排行、邮件等功能)。

  游戏服务器主要作为玩家逻辑和场景服务使用,在设计中每个游戏服务器可以承载不同的场景,进行分布式数据管理同步(压力分布)。在重量级中,场景数目和复杂度过高的情况下这是必须的。

跨服原理

  客户端请求登陆游戏服务器,服务器收到请求会先请求中心服务器进行验证,注意这里的游戏服务器所请求的中心服务器是跨服原服务器所在的服务器。验证成功后,进行共享内存加载数据,共享缓存夸服时存在一张以中心ID为HASH的连接到对应数据服务器的连接ID列表。这样登陆的时候就可以找到玩家所在的数据服务器,进行数据的加载,实现跨服功能。

项目展示

以下只展示linux部分,PF商业版同时支持windows运行。

1、  项目管理器(WEB)

  结合PF项目管理文件,可以方便的进行项目管理,主要是用于生成不同平台的编译脚本,以后的功能将会更加完善。

 

2、  协议管理器(WEB)

  这里指的是静态包协议管理,也就是说在发送的时候数据是静态的,也就是说有固定的方法设置和获取数据。在PF核心中支持使用其他协议,如protobuf、amf等。

 

3、  验证服务器

 

4、  数据服务器

 

5、  登陆服务器

 

6、  中心缓存服务器

 

7、中心服务器

 

8、游戏缓存服务器

9、游戏服务器

10、后台运行

开发总结

  明日将分享项目管理器和网络包管理器的制作原理。

PF人员招募

开篇语
  我们没有大神,只有解决问题的人。
  我们没有强悍的技术,只有一颗向往简单的心。
  我们没有惊人的理论,只有一堆不可思议的妄想。
  我们不需要复杂,只需要够简洁。
  我们不需要固定的思维,只需要你能想得到。

PF托管地址

  https://github.com/viticm/plainframework1

PF安装教程

  http://www.cnblogs.com/lianyue/p/3974342.html

PF交流QQ群

  348477824

posted @ 2015-05-07 13:03 恋月 阅读(...) 评论(...) 编辑 收藏