游戏礼包激活码案例分析
前言
最近我们游戏有一个通过激活码领取礼包的需求,需求大概是这样:
- 服务器收到邀请码后,能判断激活码是否过期
- 同一个激活码只能激活一次
- 一个玩家只能对一个礼包的激活码进行激活
可行性分析
- 礼包Id
- 启始日期
- 失效日期
- 随机秘钥
- 校验码
在这里,有两项数据的大小是有决定性作用的,礼包Id的大小会直接影响到后续的礼包数扩展数量限制,随机秘钥的大小范围决定了同样礼包Id与同样有效时间所能生成的最大激活码数量。至于其他的数据项可以有更多的优化空间,有效期的单位有必要的话可以适当地扩大。为了后续的灵活控制,在这我还是选择使用时间戳。校验码的大小可以适当按需求调整控制。目前划分的数据所生成的激活码长度为32个字符,我认为还是能在接受范围内的,所以这个思路可以执行下去。
案例代码
#include <string>
#include <random>
#include <memory>
#include <vector>
#include <iostream>
#include <sstream>typedef std::shared_ptr<std::vector<std::string> > StringVectorPtr;struct SFormatParam {uint16_t randomNumber;uint16_t giftId;uint32_t beginTime;uint32_t endTime;uint32_t checkSum;
};char g_hexTable[] = "0123456789ABCDEF";
char g_keyBuffer[32];void printUsage(const char* appName) {std::cout << "Usage:" << std::endl << std::endl;std::cout << "If you want to make gift code, you must like this:" << std::endl;std::cout << appName << " <gift-id> <begin-time-stamp> <end-time-stamp> <make-amount>" << std::endl << std::endl;std::cout << "And then, if you want to check gift code, you must like this:" << std::endl;std::cout << appName << " <gift-code>" << std::endl;
}uint8_t hexToNumber(char hexChar) {if (hexChar >= '0' && hexChar <= '9') {return hexChar - '0';} else if (hexChar >= 'a' && hexChar <= 'f') {return hexChar - 'a' + 10;} else if (hexChar >= 'A' && hexChar <= 'F') {return hexChar - 'A' + 10;} else {printf("[Error]hexToNumber hexChar\n");return 0;}
}bool hexToData(const char hex[], uint8_t data[], size_t len) {for (size_t i = 0; i < len; ++i) {size_t j = i << 1;int count = 2;for (int k = 0; k < count; ++k) {uint8_t byte = hex[j + k];if ((byte < '0' || byte > '9') && (byte < 'a' || byte > 'f') && (byte < 'A' || byte > 'F')) {return false;}}data[i] = static_cast<uint8_t>((hexToNumber(hex[j]) << 4) | hexToNumber(hex[j + 1]));}return true;
}void dataToHex(const uint8_t data[], char outStr[], size_t len) {for (size_t i = 0; i < len; ++i) {uint8_t byte = data[i];size_t j = i << 1;outStr[j] = g_hexTable[byte >> 4];outStr[j + 1] = g_hexTable[byte & 0x0F];}
}uint32_t getCheckSum(SFormatParam& outParam) {return ((outParam.randomNumber << 16 )+ outParam.giftId) ^ (outParam.beginTime + outParam.endTime);
}bool getCodeInfo(const char hexStr[], SFormatParam& outParam) {if (!hexToData(hexStr, reinterpret_cast<uint8_t*>(&outParam), sizeof(outParam))) {return false;}uint32_t checkSum = getCheckSum(outParam);if (outParam.checkSum != checkSum) {printf("[Error]check sum error! check sum should be %x, but now is %x\n", checkSum, outParam.checkSum);return false;}uint16_t key = ~outParam.randomNumber;outParam.giftId ^= key;outParam.beginTime ^= key;outParam.beginTime ^= key << 16;outParam.endTime ^= key;outParam.endTime ^= key << 16;return true;
}StringVectorPtr getGiftCode(uint32_t giftId, uint32_t beginTime, uint32_t endTime, uint32_t amount) {StringVectorPtr resultPtr(new std::vector<std::string>);srand(time(nullptr));while (amount--) {SFormatParam param = {.randomNumber = 0,.giftId = static_cast<uint16_t>(giftId),.beginTime = beginTime,.endTime = endTime,};param.randomNumber = static_cast<uint16_t>(rand() & ((1 << 16) - 1));uint16_t key = ~param.randomNumber;param.giftId ^= key;param.beginTime ^= key;param.beginTime ^= key << 16;param.endTime ^= key;param.endTime ^= key << 16;param.checkSum = getCheckSum(param),dataToHex(reinterpret_cast<const uint8_t*>(¶m), g_keyBuffer, sizeof(param));resultPtr->push_back(std::string(g_keyBuffer));}return resultPtr;
}int main(int argc, char* args[]) {if (argc == 5) {StringVectorPtr ptr = getGiftCode(atoi(args[1]), atoi(args[2]), atoi(args[3]), atoi(args[4]));for (auto code : *ptr) {std::cout << code << std::endl;}} else if (argc == 2) {SFormatParam param;if (getCodeInfo(args[1], param)) {std::cout << "gift id:" << param.giftId << std::endl;std::cout << "begin time:" << param.beginTime << std::endl;std::cout << "end time:" << param.endTime << std::endl;}} else {printUsage(args[0]);}
}
游戏礼包激活码案例分析相关推荐
- 如何设计和生成游戏的激活码
游戏的激活码,也叫作奖励码.兑换码,通常是由字符和数字组成的字符串,用于在游戏的推广阶段发放给玩家,玩家在下载登录游戏之后兑换获得相应的奖励. 首先设计我们激活码的规则 字符 + 数字 组成 长度待定 ...
- day22 案例 发送邮箱激活码 购物车 分析
2019独角兽企业重金招聘Python工程师标准>>> 邮箱激活码: 购物车分析: 转载于:https://my.oschina.net/u/2356966/blog/650116
- JAVA23种设计模式学习,源码+案例分析+类图讲解
本文对JAVA中23种设计模式进行了简单的讲解,幷加以实际案例进行辅助理解,每种模式都会举例说明,幷将源码开源至gitee和githbu上.JAVA目录下为源码,resources目录下的UML文件夹 ...
- 【大话设计模式-2】UML 类图的绘制(源码案例分析)
文章目录 1 UML 基本介绍 2 UML 图 3 UML 类图 4 类图-依赖关系(Dependence) 5 类图-泛化关系(generalization) 6 类图-实现关系(Implement ...
- php游戏礼包源码,php 游戏新手卡领号程序管理系统 v2.5
游戏新手卡领号程序管理系统是蓝色动力网络自主开发的一套用于游戏推广的领号程序,采用php+mysql,后台使用了ExtJs,让操作更简单,界面更漂亮!该程序适合做个人博客,实现了伪静态功能,更利于se ...
- spring事务不生效和事务不回滚的原因(源码案例分析)
1.首先需要加事务的方法不能是私有的(如果方法私有,则事务不生效) spring源码如下 在AbstractFallbackTransactionAttributeSource.computeTran ...
- 设计模式 6 - 原型模式及spring源码案例分析
目录 原型模式 1. 原型模式的引入 2. 原型模式的介绍和使用场景 3. 原理结构图UML图 4. 代码演示 5. 原型模式在spring中的使用 6.深拷贝的实现方式 7. 小结 原型模式 博主一 ...
- 如何制作流畅有力的游戏动画+Skullgirls案例分析
游戏动画与影视动画制作虽然看似有共通之处,但其实差别很大. 游戏动画是为玩家交互体验服务,同时需要与程序合作使其在设备上流畅运行,因此也有各种制作上的限制与要求. 在旧金山每年一度举办的游戏开发者大会 ...
- 当局者迷旁观者清红警游戏反杀局案例分析
粉丝问我:南天可不可以低吸?我没回复: 有朋友向我感叹:股市跌的时候心情不好,涨的时候心情也不好.我没有理会. 对于这波行情走势特别上周五的大涨,我的直观感受是因为 国脉科技和南天信息的超预期,引发踏 ...
最新文章
- Silverlight:SSL教程
- python字典一键多值_python字典中如何一键多值的写入?
- Python写各大聊天系统的屏蔽脏话功能原理
- 波士顿动力机器狗测评来了!售价堪比豪车,避障、导航、舞蹈样样都行,买不起还能租...
- cuda 核函数 for循环_【CUDA 基础】4.4 核函数可达到的带宽
- 在.net开发过程中遇到的问题种种
- C# 访问数据的时候报错 (拒绝了对对象 'XXXX' (数据库 'SHQY',架构 'dbo')的 SELECT 权限)...
- Codeforces Round #442 (Div. 2) 877E - Danil and a Part-time Job dfs序+线段树
- ollvm源码分析之控制流扁平化(3)
- java ee课程设计_javaee课程设计
- 第二章计算机应用基础试题答案,职中计算机应用基础第二章测试题及答案.doc...
- python统计套利_基于python的统计套利实战(二)之协整检验
- android微信打不开怎么办,微信打不开怎么回事 微信打不开怎么办
- 如何通过SEO思维收割各大平台的流量?
- iPad播放网页视频(h5 video)失败的处理方法(Django网站)
- 华为云k8s环境部署应用
- python定时更换mac 超美桌面背景
- 【FNN回归预测】基于matlab蝙蝠算法优化前馈神经网络数据回归预测【含Matlab源码 2070期】
- html十六进制和RGB颜色表
- Ubuntu - command checklist
热门文章
- flashback总结六之Flashback_Transaction_Query
- localtime、localtime_s、localtime_r的使用
- modbustcp测试工具怎么用_【转】年轻人不讲武德不仅白piao接口测试知识还白piao接口测试工具会员...
- JSP实用教程-JSP语法
- 【SSL 协议介绍】
- VRP和调度问题的主流精确算法和启发式算法
- MySQL性能剖析工具(pt-query-digest)
- Tekton Pipeline 教程
- Linux搭建集群、负载。
- python的request发请求报500原因