1 Star 0 Fork 46

Alex / Engine

forked from leo / Engine 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README
MIT

Engine

C++服务器编程底层库

特点

  1. Windows,Linux双平台(Windows下为静态库,主要方便开发者调试;Linux下为动态库,用于生产环境部署)
  2. 基本包含集成服务器常用模块(数学、文件系统、配置、日志、网络、脚本、时间、多线程等)
  3. 二次开发无平台配置,无其他依赖
  4. 基于C++11开发

使用

项目使用xmake管理,使用方法详见xmake手册

注:Linux下建议使用GCC的-Wl,-rpath,.连接选项指定运行期动态连接库的优先查找目录,以方便分发部署

集成第三方说明

  1. Zip使用miniz v2.0.7.
  2. Lua v5.3.4.
  3. 集成Jsoncpp v1.8.4.
  4. 使用hiredis v0.13.3.

模块

程序模型

#include	<Application.h>

class GameApp : public Application {
public:
	GameApp() {}

	virtual bool OnInit(const Commandline & cmd) override {
		if (cmd.Has("--debug")) {
			Logger::Instance().Initialize("game", "logs", ELog::Debug);
		} else {
			Logger::Instance().Initialize("game", "logs", ELog::Info);
		}

		/// 锁帧
		LockFPS(20);

		/// 填写其他初始化逻辑,当返回false时程序直接退出
		return true;
	}

	virtual void OnBreath() override {
		/// 这里填写需要每帧更新的逻辑
	}
};

RUN_APP(GameApp)

网络模型

头文件Socket.h,通常结合IOListener.h,一个简单的客户端实现:

class Client : public IOListener {
public:
	Client() : _socket(new Socket()) {
	}

	~Client() {
		Close();
		delete _socket;
	}

	bool Connect(const std::string & ip, int port) {
		bool succ = _socket->Connect(ip, port);
		if (succ) BindIO(_socket->ctx, IO_READ); //! 我们只需要网络有数据来时通知
		return succ;
	}

	/// 实现数据可读时消息处理
	virtual void OnReadable() override {
		char buf[1024] = {0};

		int readed = 0;
		int recv = 0;

		while (true) {
			recv = _socket->Recv(buf, 1024);
			if (recv > 0) {
				//! TODO: process received data.
			} else if (recv == 0) {
				break;
			} else {
				Close(recv);
				break;
			}
		}
	}

	void Close(int reason = 0) {
		UnbindIO(); //! 关闭之前请取消IO事件监听
		_socket->Close();
	}

	bool Send(const char * p, size_t s) {
		return _socket->Send(p, s);
	}

private:
	Socket * _socket;
}

脚本

  1. 设计原则:Lua只负责逻辑,对象生存管理交由C++(可以注册管理到Lua)
  2. 涉及到Get操作,需要try...catch以捕获类型异常(C++注册到Lua的接口内Get不需要,调用函数不需要)
  3. Property可以为地址方式,也可以为Getter(TG (void))、Setter(void (TS))方式注册
  4. Method必须为int (*f)(LuaState &)
  5. Lua不可用于多线程,只能在主线程中使用,但可以使用协程。
#include	<Script.h>

/// 注册公共变量或函数到Lua
GLua.Register("GameSetting")	//! 所有下面注册的属性或函数放在GameSetting中
	.Property("nPlayerCounter", &GPlayerCount, false)	//! 以地址方式注册属性,同时设置不可写
	.Property("nTime", &GetTime)	//! 以Getter方式注册属性,同时不注册属性的写方法(不可写)
	.Method("GetAById", &GetAById);	//! 注册全局Lua方法

/// 注册C++类到Lua
GLua.Register<A>("LuaA")
	.Property("nId", &A::id, false)
	.Property("sName", &A::GetName, &A::SetName)
	.Method("Msg", &A::SendMessage);

try {
	A * p = GLua.Get<A *>("me");	// 需要使用try,因为可能类型不匹配
} catch (...) {}

if (GLua.Is<A *>("me")) {} // 不需要try
GLua.Set<A *>("me", new A) // 不需要try
GLua.Call("GameSetting", "GetAById", false, 100); // 不需要try

int GetAById(LuaState & r) {
	int n = r.Get<int>(1);	// 不需要try
	...
}

LUA中扩展C++注册的类或名空间(注只能扩展方法,不可扩展属性)


-- 扩展名空间的方法
function XXX.yyy()
end

-- 扩展类静态方法
function LuaA.Test()
	print("hehe")
end

-- 扩展类成员方法,注意:这里用的是':',因为需要使用self
function LuaA.apis:YYY()
end

内置的其他基本函数

函数 功能
print(...) 使用Logger重载的print接口[Logger::Level::Info]
print_err(...) 使用Logger重载的print接口[Logger::Level::Error]
loadbits(n, start, end) -> integer 读取一个int32中[start, end]字节表示的值
setbits(n, start, end, v) -> integer 设置一个int32中[start, end]字节表示的值
json.encode(v) -> string 将lua变量序列化成json字串
json.decode(s) -> var 将json字串反序列化成lua值
scheduler.timer(delay, func[, is_loop]) -> integer(id) 注册一个定时器
scheduler.task(hour, min, sec, func) -> integer(id) 注册一个每天hour:min:sec执行的操作
scheduler.is_valid(id) -> bool 测试一个定时器或计划是否存在
scheduler.remain(id) -> double 返回一个定时器或计划还需要多少毫秒运行
scheduler.cancel(id) 取消一个定时器或计划任务

内存池

  1. 由于本人能力有限,经实际效率测试,目前仅保留非线程安全的对象Pool(Pool.h)
  2. Pool加锁后可用于多线程,但经测试效率还不及系统的new,但Linux下相差不大,如果考虑到无内存碎片的优点,可以自行添加。
  3. 如果采用Application的模型,Pool基本上是够用的。因为逻辑主要在主线程的Tick中触发

线程池模型

First. 编写线程内的具体工作类,继承IThreadJob.

#include	<Threads.h>

class DemoTask : public IThreadJob {
public:
	DemoTask(...) { ... }			//! 这里为该工作参数初始化
	virtual ~DemoTask() { ... }		//! 这里为工作结束时清理操作

	virtual void OnRun() { ... }	//! 工作的具体内容

private:
	...		//! 参数声明
};

Second. 创建线程池及工作对象容器

int main() {
	Threads workers(4);	//! 创建含有一个4个工作线程的容器

	/// 增加100个并发任务(多余的会暂时等待空闲线程)
	for (int i = 0; i < 100; ++i) {
		workers.AddJob<DemoTask>(...);	// 传入工作需要的参数,这里自动调用 new DemoTask(...);
	}

	/// 等待所有的工作结束,如果不执行该操作,mgr超出生存期时会放弃未执行的任务。
	workers.Wait();
	return 0;
}

Redis客户端

  1. 同步模型 Redis::Simple
  2. 异步模型 Redis::Client

定时任务

  1. C++接口
/// 添加一个500毫秒后执行的定时器
GScheduler.Add(500, [](uint64_t id) {
	printf("Timer's id : %llu", id);
});

/// 添加一个每500毫秒执行一次的定时器
GScheduler.Add(500, [](uint64_t id) {
	printf("Timer's id : %llu", id);
}, true);

/// 注册每天05:00:00时执行的计划任务
GScheduler.Add(5, 0, 0, [](uint64_t id) {
	printf("Task's id : %llu", id);
});

/// 是否存在定时器或计划任务
bool valid = GScheduler.IsValid(timer_id);

/// 取得一个定时器或计划多少毫秒后执行
double left = GScheduler.GetRemainTime(timer_id);

/// 取消一个定时器或计划任务
GScheduler.Cancel(timer_id);
  1. Lua接口
scheduler.timer(500, function(id) end);
scheduler.timer(500, function(id) end, true);
scheduler.task(5, 0, 0, function(id) end);
scheduler.is_valid(timer_id);
scheduler.remain(timer_id);
scheduler.cancel(timer_id);

日志

  1. 多线程安全
  2. 日志使用之前可以初始化(不是必要的,但建议初始化—)

日志生成的结构说明

RootOfLogs						指定的日志根目录
|-- 20160803					首先日志会根据“年月日”分文件夹
|	|-- main_01_00_00.000.log	其次日志会按指定大小分文件记录,文件名为指定的"Name_时_分_秒.毫秒.log"
|	|-- main_01_27_18.193.log
/// 初始化日志。日志名为main, 放在logs目录下,输出等级为DEBUG,每个文件最大为4M
Logger::Instance().Initialize("main", "logs", Logger::Debug, 4 * 1024 * 1024);

/// 写日志
LOG_INFO("Hello");
LOG_DEBUG("Hello %d", 2);
LOG_ERR("Error : %s", "Test");
LOG_WARN("You have a warning");

HASH

头文件 Crypto.h

  1. CRC32算法:uint32_t CalcCRC(const char * mem, size_t size, uint32_t pre_crc = 0)
  2. BKDRHash算法:uint32_t CalcHash(const char * mem, size_t size)
  3. MD5算法:class MD5
  4. SHA-1算法:class SHA1
  5. HMAC-SHA1算法:class HMAC_SHA1

工具库

  1. 单例模型:Singleton (Singleton.h)
  2. 生存域模型:ScopeGuard (ScopeGuard.h)
  3. ZIP压缩/解压缩算法:Zip(Zip.h)
  4. 字符串操作:Strings.h

配置库

  1. CSV文件的读取见:CsvFile(CsvFile.h)
  2. INI文件的读取见:IniFile(IniFile.h)
  3. JSON文件,使用jsoncpp:Json::Value, Json::Reader (Json.h)
  4. 命令行解析: Commandline (Commandline.h)

系统相关

头文件OS.h。包含:

  • 高精度时间:
    OS::Tick()
    OS::Now()
    OS::GetTimeZone()
    OS::ParseDataTime(year, month, day, hour, min, sec)

  • 文件系统:
    OS::Exists(path)
    OS::CreateDir(dir)
    OS::GetWorkDir()
    OS::ChangeWorkDir(path)
    OS::GetFullPath(path)
    OS::GetDirName(path)
    OS::GetFileName(path)
    OS::GetFiles(path, recursive)

  • 创建GUID/uuid
    OS::CreateID();

The MIT License (MIT) Copyright (c) 2016 leo Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

简介

通用服务器引擎(常用库封装) 展开 收起
C++
MIT
取消

发行版

暂无发行版

贡献者

全部

近期动态

加载更多
不能加载更多了
C++
1
https://gitee.com/wangyongxue1688/Engine.git
git@gitee.com:wangyongxue1688/Engine.git
wangyongxue1688
Engine
Engine
master

搜索帮助

14c37bed 8189591 565d56ea 8189591