Squick游戏服务器框架

First Post:

Last Update:

Word Count:
1.3k

Read Time:
6 min

Squick游戏服务器框架

Squick是什么?

Squick是一款可快速开发拓展的游戏服务器开发框架,轻量,插件化,易拓展,支持lua脚本。

Github: https://github.com/pwnsky/squick

目前正在极速开发Unity客户端SDK中,客户端sdk: https://github.com/i0gan/Uquick

特性

  • 易使用,采用。

  • 采用动态连链接库方式动态加载插件,开发拓展插件,让开发服务器变成开发插件。

  • 遵守谷歌C++编程规范

  • 事件和属性驱动,让开发变得更简单。

  • Excel实现服务端配置

  • 日志捕获系统

  • 支持Lua脚本开发

  • 支持部分不用停服即可热更

  • 默认拥有服务器插件:代理服务器、世界服务器、导航系统、数据库服务器、中心服务器、登录服务器

快速运行Demo

编译服务端

1
2
3
git clone https://github.com/pwnsky/squick.git
cd squick
bash install.sh

如果不能编译,可能是缺少依赖,打开third_party/install.sh 里查看需要安装的依赖。

编译

1
./build.sh

编译成功后,可执行以及插件会在{project_path}/build/server 下,如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
server
├── lib
│ ├── skcore.so
│ ├── squick_plugin_loader.so
│ └── squick_struct.so
├── plugin
│ ├── core
│ │ ├── actor.so
│ │ ├── config.so
│ │ ├── kernel.so
│ │ ├── log.so
│ │ ├── lua.so
│ │ ├── navigation.so
│ │ ├── net.so
│ │ ├── nosql.so
│ │ ├── security.so
│ │ └── test.so
│ └── extend
│ ├── db
│ │ ├── client.so
│ │ ├── logic.so
│ │ └── server.so
│ ├── game
│ │ ├── client.so
│ │ ├── logic.so
│ │ └── server.so
│ ├── login
│ │ ├── client.so
│ │ ├── http_server.so
│ │ ├── logic.so
│ │ └── server.so
│ ├── master
│ │ ├── http_server.so
│ │ ├── logic.so
│ │ └── server.so
│ ├── others
│ │ ├── chat.so
│ │ ├── consume_manager.so
│ │ └── inventory.so
│ ├── proxy
│ │ ├── client.so
│ │ ├── logic.so
│ │ └── server.so
│ └── world
│ ├── client.so
│ └── server.so
└── squick

运行服务端

通过debug.sh脚本或者start.sh启动服务,debug.sh用于方便实时查看输出信息,start.sh后台启动,主要用于发布版本的启动。

1
2
cd ./build
./debug.sh # 或 ./start.sh

搭建redis服务器

服务器的数据库默认为redis服务器,这里采用docker来进行搭建。

1
2
docker pull redis
docker run --name redis-cache -p 10086:6379 -d redis --requirepass pwnsky # pwnsky 是密码

打开: build/config/excel/side/NoSqlServer.xlsx 修改数据库连接IP和端口

但是在修改后,需要重新生成新的配置文件,执行以下命令。

1
2
cd ./build/config/tools
./gen_config.sh

tips:

redis查看

1
2
3
redis-cli -h 127.0.0.1 -p 10086
auth pwnsky # pwnsky 是密码
keys * # 查看所有key

redis常用命令: https://blog.csdn.net/Kristabo/article/details/125384059

客户端

https://github.com/i0gan/Uquick 下载下来,采用Unity打开,我采用的是2020.03.xx。

快速入门

了解Squick

制作插件

服务端教学

自定义实现登录

自定义消息ID,在src/squick/struct/define.proto, 默认已经定义

1
2
REQ_LOGIN								= 101;     	//
ACK_LOGIN = 102; //

定义消息结构,NFComm/NFMessageDefine/NFMsgPreGame.proto

1
2
3
4
5
6
7
8
9
10
11
12
13
14
message ReqAccountLogin
{
bytes account = 2;
bytes password = 3;
bytes security_code = 4;
bytes signBuff = 5;
int32 clientVersion = 6;
ELoginMode loginMode = 7;
int32 clientIP = 8;
int64 clientMAC = 9;
bytes device_info = 10;
bytes extra_info = 11;
int32 platform_type = 12;
}

修改完毕后,需要通过protoc重新生成新的C++和CS代码

1
2
cd {project_path}/src/squick/struct
./gen_code.sh

运行后会自动将生成代码文件,并将CS代码复制到客户端项目Assets/Resources/SquickConfig/

增加一个登录系统模块,默认已经实现。

打开src/server/login/plugin/server/server_module.cc

在AfterInit函数李增加回调,默认已实现

1
m_pNetModule->AddReceiveCallBack(NFMsg::REQ_LOGIN, this, &NFLoginNet_ServerModule::OnLoginProcess);

OnLoginProcess回调函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
void NFLoginNet_ServerModule::OnLoginProcess(const NFSOCK sockIndex, const int msgID, const char* msg, const uint32_t len)
{
NFGUID nPlayerID;
NFMsg::ReqAccountLogin xMsg;
if (!m_pNetModule->ReceivePB( msgID, msg, len, xMsg, nPlayerID))
{
return;
}

NetObject* pNetObject = m_pNetModule->GetNet()->GetNetObject(sockIndex);
if (pNetObject)
{
if (pNetObject->GetConnectKeyState() == 0)
{
//Normally, you could check the account and password is correct or not, but for our situation, it will correct by default as here is the tutorial code.
int loginResult = 0;//0 means successful, else means error code from account platform.
if (0 != loginResult)
{
std::ostringstream strLog;
strLog << "Check password failed, Account = " << xMsg.account() << " Password = " << xMsg.password();
m_pLogModule->LogError(NFGUID(0, sockIndex), strLog, __FUNCTION__, __LINE__);

NFMsg::AckEventResult xMsg;
xMsg.set_event_code(NFMsg::ACCOUNTPWD_INVALID);

m_pNetModule->SendMsgPB(NFMsg::EGameMsgID::ACK_LOGIN, xMsg, sockIndex);
return;
}

pNetObject->SetConnectKeyState(1);
pNetObject->SetAccount(xMsg.account());

NFMsg::AckEventResult xData;
xData.set_event_code(NFMsg::ACCOUNT_LOGIN_SUCCESS);

//The login server responds the login result to the player by sock id.
m_pNetModule->SendMsgPB(NFMsg::EGameMsgID::ACK_LOGIN, xData, sockIndex);

m_pLogModule->LogInfo(NFGUID(0, sockIndex), "Login succeeded :", xMsg.account().c_str());
}
}
}

消息系统

在同一个房间内如何让玩家之前可以互相发送消息。

NFComm/NFMessageDefine/NFDefine.proto里定义了请求响应枚举,默认已经定义了。

1
2
REQ_CHAT								= 350;
ACK_CHAT = 351;

消息结构定义在NFComm/NFMessageDefine/NFMsgShare.proto

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
message ReqAckPlayerChat
{
enum EGameChatChannel
{
EGCC_GLOBAL = 0;
EGCC_CLAN = 1;
EGCC_FRIEND = 2;
EGCC_BATTLE = 3;
EGCC_TEAM = 4;
EGCC_ROOM = 5;
}
enum EGameChatType
{
EGCT_TEXT= 0;
EGCT_VOICE = 1;
EGCT_EMOJI = 2;
EGCT_DONATE_HERO = 10;
EGCT_DONATE_BUILDING = 11;
EGCT_DONATE_ITEM = 12;
}

Ident player_id = 1;
bytes player_name = 2;
bytes player_hero_id = 3;
bytes player_hero_level = 4;
EGameChatChannel chat_channel = 5;
EGameChatType chat_type = 6;
bytes chat_info = 7;
Ident target_id = 8;
}

运行NFComm/NFMessageDefine/cpp.sh 生成对应的C++和CS代码文件

将生成的_Out/NFDataCfg/client/里生成的CS文件复制到客户端项目Assets/Resources/NFDataCfg/

增加一个模块到程序中 NFExamples/NFChatPlugin,NF已经实现了。

编辑NFExamples/NFChatPlugin/NFChatModule.cpp文件,如下(默认已经实现)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
bool NFChatModule::AfterInit()
{
m_pNetModule->AddReceiveCallBack(NFMsg::REQ_CHAT, this, &NFChatModule::OnClientChatProcess);

return true;
}


void NFChatModule::OnClientChatProcess(const NFSOCK sockIndex, const int msgID, const char* msg, const uint32_t len)
{
CLIENT_MSG_PROCESS( msgID, msg, len, NFMsg::ReqAckPlayerChat);

switch (xMsg.chat_channel())
{
case NFMsg::ReqAckPlayerChat::EGCC_GLOBAL:
{
//this code means the game server will sends a message to all players who playing game
m_pNetModule->SendMsgPBToAllClient(NFMsg::ACK_CHAT, xMsg);
}
break;
case NFMsg::ReqAckPlayerChat::EGCC_ROOM:
{
const int sceneID = m_pKernelModule->GetPropertyInt(nPlayerID, NFrame::Player::SceneID());
const int groupID = m_pKernelModule->GetPropertyInt(nPlayerID, NFrame::Player::GroupID());

//this code means the game server will sends a message to all players who in the same room
m_pGameServerNet_ServerModule->SendGroupMsgPBToGate(NFMsg::ACK_CHAT, xMsg, sceneID, groupID);
}
break;
default:
{
//this code means the game server will sends a message yourself(nPlayerID)
m_pGameServerNet_ServerModule->SendMsgPBToGate(NFMsg::ACK_CHAT, xMsg, nPlayerID);
}
break;;
}
}
打赏点小钱
支付宝 | Alipay
微信 | WeChat