4 - C++ Development of "Fight the Landlord" - Custom Backend Services

这篇我们来研究自定义服务是如何创建和注册的,谁来调用和初始化这些需要后面解析到网络服务后再说了

IService.cpp

头文件在上一篇做过简单介绍,这里就不赘述,直接上CPP

1、可以看到在构造函数初始化时需要传入了三个参数:①TYPE类型 ②服务名 ③是否需要鉴权

  • 类型:开发者自定义的类型,要求是int,不过我自定义了一个enum枚举
  • 服务名:该服务名需唯一,用于服务之前通过名称相互获取
  • 是否需要鉴权:一般用于登录后该连接会绑定到玩家缓存,没有登录的连接没有缓存,如此参数传入false,则没有缓存的请求将被ServiceManager默认回复

2、regist成员函数

该成员函数需要两个参数,第一个为自定义action,该action在同一服务或同一type中不可重复,参数二为callback原型:std::function<void(Request& request)> 用于相应触发回调

#include "iservice.h"
#include "servicemanager.h"
#include "utils/utils.h"
IService::IService(int type, const std::string& name,bool haveLogined):m_type(type),m_name(name), m_haveLogined(haveLogined)
{
    Utils::assert(ServiceManager::getInstance()->registService(this), ServiceManager::getInstance()->last_error());
}

IService::~IService()
{
}

void IService::regist(int action, REQUEST_CALLBACK callback)
{
    ServiceManager::getInstance()->registAction(action,callback,this);
}

# 大家应该有一个疑问,就是这个callback原型有一个Request,这是什么?这其实是一个类,每个请求包到最终回调的生命周期中会创建一个,用来存储请求数据、返回结果等。其头文件如下

request.h:请求

该类由HandleCenter控制器创建和释放

<pre class="prism-highlight prism-language-cpp">#pragma once
#include "ybase/define.h"
#include "ybase/error.h"
#include "core/define.h"
#include "common.h"
#include "entity/Player.h"
class Request{
public:
    /// <summary>
    /// 构造
    /// </summary>
    /// <param name="player_id">玩家ID</param>
    /// <param name="fd">连接ID</param>
    /// <param name="index">包索引</param>
    /// <param name="data">请求数据</param>
    Request(uint64 player_id,uint64 fd,uint32 index,const ylib::json& data);
    ~Request();

    ylib::json& data() { return m_data; }
    /// <summary>
    /// 回复(仅允许调用1次)
    /// </summary>
    /// <param name="data"></param>
    void repSuccess(const ylib::json& data = ylib::json());
    void repFailed(const std::string& error_msg);
    void response(bool result,const std::string& msg,const ylib::json& data);
    /// <summary>
    /// 取Player指针
    /// </summary>
    /// <returns></returns>
    std::shared_ptr<Player> player();

    uint64 fd() { return m_fd; }
private:
    // 玩家ID
    uint64 m_player_id = 0;
    // 连接ID
    uint64 m_fd = 0;
    // 回复索引
    uint32 m_index = 0;
    // 接收数据
    ylib::json m_data;
    // 是否已回复
    bool m_responsed = false;
};

user_ns.huser_ns.cpp 做为示例来演示吧

user_ns.h

头文件很简单继承了IService,声明了两个函数:login和regist用来注册,并且都有一个参数Request

基本就是这些没什么重要的了

#pragma once
#include "core/iservice.h"
/// <summary>
/// User Not Session
/// </summary>
class UserNS :IService
{
public:
    UserNS();
    ~UserNS();
private:
    void login(Request& request);
    void regist(Request& request);
};

user_ns.cpp

构造函数可以有两个 IService::regist 函数的调用,其用于注册当前成员函数与ACTION的对应关系

#include "user_ns.h"
#include "utils/utils.h"
#include "game/playermanager.h"
#include "network/server.h"
#include "ymysql/mysql_plus.h"
#include "HPSocket/HPSocket.h"
UserNS::UserNS():IService(c2s::USER,"user_ns",false)
{
    ::IService::regist(c2s::UserAction::LOGIN, std::bind(&UserNS::login, this, std::placeholders::_1));
    ::IService::regist(c2s::UserAction::REGIST, std::bind(&UserNS::regist, this, std::placeholders::_1));
}

UserNS::~UserNS()
{
}

void UserNS::login(Request& request)
{
    

    std::string username = request.data()["username"].to<std::string>();
    std::string password = request.data()["password"].to<std::string>();
    // 校验账号
    {
        auto [result, msg] = Utils::checkUsername(username);
        if (result == false)
        {
            request.repFailed(msg);
            return;
        }
    }
    // 校验密码
    {
        auto [result, msg] = Utils::checkPassword(password);
        if (result == false)
        {
            request.repFailed(msg);
            return;
        }
    }
    
    conn_autofree conn(SQLPOOL->get());

    auto pptp = conn->setsql("SELECT id,money,sex FROM users WHERE username = ? AND password = ? LIMIT 1");
    pptp->set_string(1, username);
    pptp->set_string(2, password);
    auto result = pptp->query();
    if (result->row_count() != 1)
    {
        request.repFailed("账号或密码错误");
        return;
    }
    result->next();

    uint64 player_id = result->get_uint64("id");
    auto sex = (Sex)result->get_int32("sex");


    // 删除之前用户缓存并断线
    PlayerManager::getInstance()->remove(player_id,true);

    // 增加新的绑定
    auto player = PlayerManager::getInstance()->create(player_id);
    Server::getInstance()->hp()->SetConnectionExtra(request.fd(),(PVOID)player_id);

    player->setMoney(result->get_uint64("money"));
    player->setSex(sex);
    player->setFD(request.fd());
    
    request.repSuccess();
}

void UserNS::regist(Request& request)
{
    std::string username = request.data()["username"].to<std::string>();
    std::string password = request.data()["password"].to<std::string>();
    // 校验账号
    {
        auto [result, msg] = Utils::checkUsername(username);
        if (result == false)
        {
            request.repFailed(msg);
            return;
        }
    }
    // 校验密码
    {
        auto [result, msg] = Utils::checkPassword(password);
        if (result == false)
        {
            request.repFailed(msg);
            return;
        }
    }

    conn_autofree conn(SQLPOOL->get());

    // 校验重名
    {
        auto pptp = conn->setsql("SELECT id FROM users WHERE username = ? LIMIT 1");
        pptp->set_string(1, username);
        auto result = pptp->query();
        if (result->row_count() > 0)
        {
            request.repFailed("账号已被注册");
            return;
        }
    }
    // 注册账号
    auto pptp = conn->setsql("INSERT INTO users (username, password) VALUES (?, ?)");
    if (pptp->update() == 1)
    {
        request.repSuccess();
    }
    else
    {
        request.repFailed(pptp->last_error());
    }
}

Title of this article:<4 - C++ Development of "Fight the Landlord" - Custom Backend Services>Author:minimini
Original link:https://www.xxmjw.com/post/16.html
Unless otherwise specified, all content is original. Please indicate when reprinting.

Related

minimini

minimini