Compare commits

...

2242 Commits
master ... sdk

Author SHA1 Message Date
hahwu
ad56c3deda 增加打点区分 2026-06-03 12:02:40 +08:00
hahwu
9345a7c5b7 卡牌类型断言bug修复 2026-05-21 12:06:16 +08:00
hahwu
cd46db9e95 Update .gitignore 2026-05-21 12:06:16 +08:00
hahwu
b1a442336e Merge branch 'develop' into sdk 2026-05-20 18:20:26 +08:00
hahwu
9cffc15297 修复特殊礼包保底bug 2026-05-20 18:18:55 +08:00
hahwu
f772377b9b 优惠日bug修复 2026-05-19 19:22:21 +08:00
hahwu
bdbe738da4 优惠日bug修复 2026-05-19 19:22:08 +08:00
hahwu
1a1be26ebd charge 单元测试 2026-05-19 19:21:18 +08:00
hahwu
1fd15eb363 删除重复文件 2026-05-18 20:04:03 +08:00
hahwu
f04e231931 Merge branch 'develop' into sdk 2026-05-18 16:49:24 +08:00
hahwu
f1e4b07e02 版本号+1 2026-05-18 10:29:19 +08:00
hahwu
bb2052bc98 优化停服系统标识 2026-05-18 10:28:51 +08:00
hahwu
7277b6181c Merge branch 'develop' into sdk 2026-05-18 09:49:55 +08:00
hahwu
7d55c43688 修改从暂存区取棋子的顺序 2026-05-15 17:26:10 +08:00
hahwu
f1774a44bc 棋盘不同步打点补充 2026-05-15 16:20:21 +08:00
hahwu
6c15a87ead 单元测试 2026-05-15 15:41:56 +08:00
hahwu
2c3358a0b9 GM后台增加暂存区棋子展示 2026-05-15 15:36:28 +08:00
hahwu
29d8c9a62d AI代码审核skill更新 2026-05-14 09:59:56 +08:00
hahwu
3e95aab195 初始化日志模块 2026-05-13 16:19:17 +08:00
hahwu
c665ce941e 清理技术债 2026-05-12 11:01:12 +08:00
hahwu
325693678d AI审核skill 2026-05-12 10:58:57 +08:00
hahwu
058419c571 死循环漏洞修复 2026-05-11 17:44:40 +08:00
hahwu
9e896ab874 死循环漏洞修复 2026-05-11 17:43:38 +08:00
hahwu
58967ddeae 锦标赛奖励补偿 2026-05-11 16:48:14 +08:00
hahwu
09749de671 锦标赛奖励补偿 2026-05-11 16:47:55 +08:00
hahwu
dc81001c00 锦标赛奖励补偿 2026-05-11 16:45:40 +08:00
hahwu
c7f62b500e 锦标赛奖励补偿 2026-05-11 16:45:24 +08:00
hahwu
4c3c129998 锦标赛死循环bug修复,奖励补发 2026-05-11 16:39:20 +08:00
hahwu
544314bebe 锦标赛死循环bug修复,奖励补发 2026-05-11 16:39:02 +08:00
hahwu
bacaa78dbe 活动时间bug修复 2026-05-11 11:01:25 +08:00
hahwu
a9c99da5ab 活动时间bug修复 2026-05-11 11:00:22 +08:00
hahwu
519f393736 玩家退出逻辑优化 2026-05-09 18:37:20 +08:00
hahwu
6e3ebab2d0 玩家退出逻辑优化 2026-05-09 18:17:58 +08:00
hahwu
94bfaa3c32 玩家退出逻辑优化 2026-05-09 18:17:09 +08:00
hahwu
b8a90787e7 棋盘操作打点日志 2026-05-09 16:35:13 +08:00
hahwu
98e879f32f 清理技术债:1.模块初始化显示加载,避免测试 耦合 2026-05-09 12:03:16 +08:00
hahwu
72412bc591 代码评审skill 2026-05-09 11:18:48 +08:00
hahwu
2886b4dd02 单元测试 2026-05-08 18:55:51 +08:00
hahwu
3d6bdfda5b 路由函数实现切分成小文件 2026-05-08 18:52:20 +08:00
hahwu
941a16399a 棋盘单元测试 2026-05-08 18:07:25 +08:00
hahwu
38046c1514 项目架构评审agent skill 2026-05-08 18:06:36 +08:00
hahwu
29d5a755a3 增加棋子退役协议 2026-05-08 15:29:41 +08:00
hahwu
a6b86759e8 Merge branch 'develop' into sdk 2026-04-29 11:51:09 +08:00
hahwu
b652f95de7 日志优化 2026-04-29 11:45:36 +08:00
hahwu
b8a7627e43 Merge branch 'develop' into sdk 2026-04-28 18:42:59 +08:00
hahwu
e4d5d5cef0 日志优化
Co-authored-by: Copilot <copilot@github.com>
2026-04-28 18:31:23 +08:00
hahwu
087eabc12c 日志优化 2026-04-28 15:46:15 +08:00
hahwu
dc5e730ffa 后端版本v1.0.5 2026-04-28 10:40:33 +08:00
hahwu
dc11a090bf Merge branch 'develop' into sdk 2026-04-28 10:17:35 +08:00
hahwu
85798f9032 增加日志
Co-authored-by: Copilot <copilot@github.com>
2026-04-27 12:31:42 +08:00
hahwu
61d5bcd97b 日志优化 2026-04-27 12:28:53 +08:00
hahwu
451eebc50b 优化register打点
Co-authored-by: Copilot <copilot@github.com>
2026-04-27 12:23:53 +08:00
hahwu
dd32db70bd 日志优化 2026-04-27 12:17:42 +08:00
hahwu
89b8aa5455 新增login打点
Co-authored-by: Copilot <copilot@github.com>
2026-04-27 12:10:18 +08:00
hahwu
ecfed14cbb 修复后台rpc异常
Co-authored-by: Copilot <copilot@github.com>
2026-04-24 11:39:08 +08:00
hahwu
ece9fa83c3 gob增加结构体注册 2026-04-23 15:30:48 +08:00
hahwu
e7bb0e1bc4 gob增加结构体注册
Co-authored-by: Copilot <copilot@github.com>
2026-04-23 15:21:28 +08:00
hahwu
42ed89ff59 增加配置管理器 2026-04-22 15:21:46 +08:00
hahwu
a8ada88563 ignore file 2026-04-21 15:55:04 +08:00
hahwu
114f1093d4 修改系统邮件加载方式 2026-04-21 15:53:06 +08:00
hahwu
521bd8fdd3 系统邮件增加等级限制 2026-04-21 15:51:23 +08:00
hahwu
f4a7f304f9 修改系统邮件加载方式 2026-04-21 15:50:15 +08:00
hahwu
1b7c2a4150 将消息队列持久化由redis存储 2026-04-21 12:00:21 +08:00
hahwu
70955eb5e6 将消息队列持久化由redis存储 2026-04-21 11:25:01 +08:00
hahwu
71e2a3bc49 championship net proto fix 2026-04-21 10:36:56 +08:00
hahwu
196295e58e 增加admin rpc接口 2026-04-20 15:35:00 +08:00
hahwu
3cd086b648 Merge branch 'var-redis' into develop 2026-04-20 11:42:58 +08:00
hahwu
2fb4a6e2be 修改新的var key 2026-04-20 11:38:55 +08:00
hahwu
703c46d53b 优化 2026-04-20 11:32:15 +08:00
hahwu
e87cdec240 重构玩家缓存数据 2026-04-20 11:31:25 +08:00
hahwu
a59fd1357b 存储都使用json 2026-04-20 11:22:50 +08:00
hahwu
0ef404b359 代码优化 2026-04-20 10:13:13 +08:00
hahwu
d5a0f73bf3 Reapply "Merge branch 'thrift' into develop"
This reverts commit 49a551daba.
2026-04-20 10:02:20 +08:00
hahwu
aa6902bee0 单元测试 2026-04-20 10:00:40 +08:00
hahwu
4e13f03ac5 增加日志 2026-04-17 17:38:27 +08:00
hahwu
584a45e055 增加日志 2026-04-17 17:21:34 +08:00
hahwu
9440308008 增加日志 2026-04-17 16:40:00 +08:00
hahwu
5649f3dbe7 增加日志 2026-04-17 16:40:00 +08:00
hahwu
ce81961798 notification通知限制条件优化 2026-04-17 16:40:00 +08:00
hahwu
d2d3bb601f 增加日志 2026-04-17 16:38:59 +08:00
hahwu
018893e340 增加日志 2026-04-17 16:20:59 +08:00
hahwu
f2689fe4f1 变量数据优化 2026-04-17 14:54:43 +08:00
hahwu
3fedac37e1 notification通知限制条件优化 2026-04-17 12:12:51 +08:00
hahwu
68656b6f23 优化锦标赛排行榜协议 2026-04-16 18:06:16 +08:00
hahwu
cd2a44d37a 优化锦标赛排行榜协议 2026-04-16 17:54:52 +08:00
hahwu
fd557731a2 Merge branch 'develop' into sdk 2026-04-16 17:39:30 +08:00
hahwu
d36d095873 锦标赛优化 2026-04-16 16:05:28 +08:00
hahwu
784e524c1a 调试日志 2026-04-16 15:52:47 +08:00
hahwu
05a5b5237b 调试日志 2026-04-16 15:52:14 +08:00
hahwu
00a8efc21d 锦标赛增加日志 2026-04-16 15:37:16 +08:00
hahwu
fc73d862dd 锦标赛优化 2026-04-16 15:36:13 +08:00
hahwu
f9ef827559 锦标赛增加日志 2026-04-16 15:33:25 +08:00
hahwu
4370238f94 锦标赛优化 2026-04-16 15:25:51 +08:00
hahwu
acbfd823e0 锦标赛优化 2026-04-19 01:40:38 -04:00
hahwu
c133d91ca7 锦标赛优化 2026-04-19 00:02:03 -04:00
hahwu
513736de4f 锦标赛优化 2026-04-16 11:54:17 +08:00
hahwu
41f292b190 锦标赛优化 2026-04-16 11:53:43 +08:00
hahwu
e6be6ac9ad Merge branch 'develop' of gitea.bywaystudios.com:pet_home/pet_home_server into develop 2026-04-16 11:35:58 +08:00
hahwu
530b5c8988 锦标赛优化 2026-04-16 11:35:55 +08:00
hahwu
4546bd66cf 锦标赛优化 2026-04-16 11:25:40 +08:00
hahwu
dea42c4574 锦标赛优化 2026-04-16 11:05:09 +08:00
hahwu
a80dc792e4 锦标赛优化 2026-04-16 10:53:21 +08:00
hahwu
49a551daba Revert "Merge branch 'thrift' into develop"
This reverts commit 00a30d9f13, reversing
changes made to 4253df8f13.
2026-04-16 10:35:32 +08:00
hahwu
00a30d9f13 Merge branch 'thrift' into develop 2026-04-16 10:29:01 +08:00
hahwu
79fe878ef7 playroom interact bug fix 2026-04-15 21:06:02 +08:00
hahwu
1f41975ba2 playroom interact bug fix 2026-04-15 21:06:01 +08:00
hahwu
4253df8f13 playroom interact bug fix 2026-04-15 20:59:36 +08:00
hahwu
d5faf42da0 playroom interact bug fix 2026-04-15 20:44:25 +08:00
hahwu
85a5273c10 猫猫回礼奖励聚合 2026-04-15 17:18:48 +08:00
hahwu
fb2320c4dd 猫猫回礼奖励聚合 2026-04-15 17:18:27 +08:00
hahwu
4fd3ef80d7 建造打折的宠物币向上取整 2026-04-14 22:17:40 +08:00
hahwu
f238dd1e76 建造打折的宠物币向上取整 2026-04-14 22:17:12 +08:00
hahwu
dee714e24f import package fix 2026-04-14 21:20:05 +08:00
hahwu
b479332804 被踢下线不断开socket 2026-04-14 20:49:57 +08:00
hahwu
d525a43248 被踢下线不断开socket 2026-04-14 20:44:22 +08:00
hahwu
7aa145e204 Merge branch 'develop' into sdk 2026-04-14 20:41:52 +08:00
hahwu
ea6eaa79a4 猫猫回礼奖励补发 2026-04-14 20:37:42 +08:00
hahwu
0fbebc856e 报错增加日志 2026-04-14 17:34:13 +08:00
hahwu
27c48071da Merge branch 'develop' into sdk 2026-04-14 17:15:38 +08:00
hahwu
1f3a97e323 sql语句更新 2026-04-14 14:33:00 +08:00
hahwu
9f54f3733a 猫猫回礼优化 2026-04-14 12:14:42 +08:00
hahwu
66995cc63d gm命令优化 2026-04-14 10:51:23 +08:00
hahwu
e0b73ccf6f 锦标赛优化 2026-04-14 10:03:33 +08:00
hahwu
6b379a2de5 锦标赛 2026-04-13 23:09:01 +08:00
hahwu
e9e94d37fc 锦标赛单元测试 2026-04-13 22:37:15 +08:00
hahwu
7b0fe9362e 锦标赛清理期 2026-04-13 22:37:15 +08:00
hahwu
a2189782be 锦标赛单元测试 2026-04-13 22:37:02 +08:00
hahwu
4a1c14dbbf 锦标赛清理期 2026-04-13 22:24:28 +08:00
hahwu
0f978377e5 增加锦标赛活动开始时间 2026-04-13 21:50:05 +08:00
hahwu
a203d5e485 增加锦标赛活动开始时间 2026-04-13 21:44:34 +08:00
hahwu
d2ec1b68a4 Merge branch 'develop' into sdk 2026-04-13 21:00:34 +08:00
hahwu
cef447b8dc 压力测试 2026-04-13 20:58:19 +08:00
hahwu
6360361da0 锦标赛获取上期活动id优化 2026-04-13 19:48:00 +08:00
hahwu
12efd965e9 锦标赛标题优化 2026-04-13 19:43:57 +08:00
hahwu
adc9988dad 新增锦标赛标题 2026-04-13 19:25:03 +08:00
hahwu
4c91239ff1 增加版本号 2026-04-13 19:15:41 +08:00
hahwu
af43bd827a 打点增加uid 2026-04-13 18:41:36 +08:00
hahwu
244b158563 猫猫回礼奖励bug修复 2026-04-13 18:32:26 +08:00
hahwu
0c01365f45 错误初始化服装修复增加日志 2026-04-13 18:20:31 +08:00
hahwu
06dd437e3d 修改锦标赛倒计时 2026-04-13 18:18:45 +08:00
hahwu
d117ad6912 活动配置解析优化 2026-04-13 16:19:49 +08:00
hahwu
a074fce5bd 锦标赛bug修复 2026-04-13 16:14:26 +08:00
hahwu
2bf8a710a4 猫猫回礼增加宠物币奖励 2026-04-13 15:58:00 +08:00
hahwu
82636c7dd6 championship cleartime 由活动配置决定 2026-04-13 15:53:59 +08:00
hahwu
1052fbc273 championship cleartime 由活动配置决定 2026-04-13 15:39:49 +08:00
hahwu
55d185bc04 playroom初始服装bug修复 2026-04-13 15:11:41 +08:00
hahwu
3cc3af2aff 协议消息注册优化 2026-04-13 14:49:30 +08:00
hahwu
541e966a7f 场景折扣bug修复 2026-04-13 12:06:36 +08:00
hahwu
bf7b202105 退役发射器棋子出售获得原价宠物币 2026-04-10 18:48:01 +08:00
hahwu
04496fbc81 猫猫回礼打点 2026-04-10 17:40:02 +08:00
hahwu
52cb8279de playroom装饰品时间bug修复 2026-04-10 17:38:18 +08:00
hahwu
d4ab008991 好友气泡优化 2026-04-10 17:20:46 +08:00
hahwu
00321f9016 修复好友卡牌消息bug 2026-04-10 11:48:19 +08:00
hahwu
91f6d8d9a1 优化顶号逻辑 2026-04-10 10:00:10 +08:00
hahwu
ef129ee8e0 修改协议传输方式为compact 2026-04-09 18:51:43 +08:00
hahwu
7ac99abedf 优化协议解析 2026-04-09 18:30:33 +08:00
hahwu
4d10bcb788 猫猫回礼bug修复 2026-04-09 17:01:49 +08:00
hahwu
434c4ca9da thrift协议改版 2026-04-09 16:30:43 +08:00
hahwu
ea0c5681fb 锦标赛优化,修改排行榜排序 2026-04-09 15:06:38 +08:00
hahwu
a0baaa99df 锦标赛优化 2026-04-09 15:02:26 +08:00
hahwu
41b381acd8 锦标赛排行奖励领取bug修复 2026-04-09 14:19:46 +08:00
hahwu
1062be0ffa 调试 2026-04-09 14:14:44 +08:00
hahwu
b427bdb5b7 锦标赛排行奖励领取bug修复 2026-04-09 12:23:27 +08:00
hahwu
2c3fad1680 猫猫回礼优化 2026-04-09 11:18:40 +08:00
hahwu
cf9dd92090 playroom好友优化,随机拜访好友去重 2026-04-08 18:06:13 +08:00
hahwu
6def839472 猫猫回礼优化 2026-04-08 16:49:14 +08:00
hahwu
7f634596f2 锦标赛优化 2026-04-08 16:38:15 +08:00
hahwu
cd7565473b playroom修复 2026-04-08 15:18:28 +08:00
hahwu
e26e663d91 锦标赛优化 2026-04-08 12:20:28 +08:00
hahwu
feaf228b79 playroom日常任务解锁优化 2026-04-08 11:58:38 +08:00
hahwu
880be78f3f Merge branch 'championship' into develop 2026-04-08 11:03:06 +08:00
hahwu
cfa6e5cb8c 锦标赛单元测试 2026-04-08 11:02:50 +08:00
hahwu
7e94e515e1 卡牌打点补充 2026-04-08 10:51:40 +08:00
hahwu
f4eb4d0371 playroom每日任务解锁等级 2026-04-08 10:45:00 +08:00
hahwu
c624e7f0f4 1 2026-04-07 18:41:24 +08:00
hahwu
9529e48a52 锦标赛补发奖励使用邮件补发 2026-04-07 17:33:37 +08:00
hahwu
786ce7772a 接口优化 2026-04-07 17:29:10 +08:00
hahwu
e066dd9ed5 补全国家码 2026-04-07 16:52:54 +08:00
hahwu
36723ae951 优化接口协议 2026-04-07 16:20:10 +08:00
hahwu
0874cc14c3 接口报错bug修复 2026-04-07 16:04:33 +08:00
hahwu
1694cad3e4 猫猫回礼奖励补发 2026-04-07 14:36:25 +08:00
hahwu
de4757f364 中心服启动时清空玩家登录列表 2026-04-07 12:11:12 +08:00
hahwu
a731706147 好友推荐优化 2026-04-07 11:37:50 +08:00
hahwu
b559d3878f 分表优化 2026-04-03 19:43:56 +08:00
hahwu
0c01e532e6 分表优化 2026-04-03 19:32:27 +08:00
hahwu
89e6caca29 分表bug修复 2026-04-03 19:09:18 +08:00
hahwu
52477c5ac6 数据库分表 2026-04-03 17:43:05 +08:00
hahwu
4347974b17 猫咪回礼 2026-04-03 16:25:17 +08:00
hahwu
463abeb21e 优化 2026-04-03 15:42:31 +08:00
hahwu
d1896c1695 优化日志 2026-04-03 15:06:02 +08:00
hahwu
6238719958 优化函数注册 2026-04-03 14:23:07 +08:00
hahwu
ff0d901e2a 优化日志 2026-04-03 11:04:34 +08:00
hahwu
b88272d00d 优化日志 2026-04-03 11:02:33 +08:00
hahwu
a7d4137763 好友打招呼奖励bug修复 2026-04-02 21:34:43 +08:00
hahwu
23e1cf21cd 好友打招呼奖励bug修复 2026-04-02 21:34:43 +08:00
hahwu
539b3a8738 好友打招呼奖励bug修复 2026-04-02 21:34:42 +08:00
hahwu
19233e02d8 1 2026-04-02 21:33:56 +08:00
hahwu
16bfd16d64 好友打招呼奖励bug修复 2026-04-02 17:35:45 +08:00
hahwu
73007781c6 好友打招呼奖励bug修复 2026-04-02 17:24:36 +08:00
hahwu
99ade15810 好友打招呼奖励bug修复 2026-04-02 17:16:18 +08:00
hahwu
063bad8a18 1 2026-04-02 17:04:50 +08:00
hahwu
a42e534245 gm命令优化 2026-04-02 11:09:05 +08:00
hahwu
557dec4612 1 2026-04-02 10:42:53 +08:00
hahwu
f727bf97a6 赠送卡牌打点 2026-04-01 16:31:59 +08:00
hahwu
886f818db6 赠送卡牌打点 2026-04-01 16:31:59 +08:00
hahwu
ac2f37eda5 赠送卡牌打点 2026-04-01 16:31:31 +08:00
hahwu
97b764367a 1 2026-04-01 16:30:01 +08:00
hahwu
bf20ca1a6f 赠送卡牌打点 2026-04-01 16:18:38 +08:00
hahwu
dd06f7247c 玩家信件优化 2026-04-01 14:27:57 +08:00
hahwu
40ccb028dc 玩家信件优化 2026-04-01 14:26:05 +08:00
hahwu
999709dd7a 项目分包 2026-04-01 14:25:38 +08:00
hahwu
393fb487e0 玩家信件优化 2026-04-01 14:23:22 +08:00
hahwu
77653ea35e 玩家信件优化 2026-04-01 14:06:38 +08:00
hahwu
2703593fcd 玩家信件优化 2026-04-01 12:30:31 +08:00
hahwu
7bb8e01b54 项目分包 2026-04-01 10:21:16 +08:00
hahwu
1d65646de3 Merge branch 'rpc_server' into develop 2026-03-31 21:55:33 +08:00
hahwu
8e7b1e9b06 优化日志 2026-03-31 21:49:41 +08:00
hahwu
6418c18739 1 2026-03-31 18:08:47 +08:00
hahwu
2bafc5fca6 优化日志 2026-03-31 15:01:09 +08:00
hahwu
833e0908a4 优化日志 2026-03-31 15:00:48 +08:00
hahwu
93c2e072f7 修复零件订单含自动发射器棋子的bug 2026-03-31 14:55:09 +08:00
hahwu
a0302ed264 优化接口,避免造成节点意外下线 2026-03-31 14:55:09 +08:00
hahwu
124d4468c4 修复零件订单含自动发射器棋子的bug 2026-03-31 14:47:24 +08:00
hahwu
46ba8af111 rpc服务器 2026-03-31 14:33:35 +08:00
hahwu
033dd8088e 优化接口,避免造成节点意外下线 2026-03-31 11:58:12 +08:00
hahwu
22236c0a39 优化日志 2026-03-31 11:48:09 +08:00
hahwu
656fbf78fa 优化日志 2026-03-31 11:45:09 +08:00
hahwu
a6993fa15f 新增rpc接口 2026-03-31 11:44:48 +08:00
hahwu
43811e8927 Merge branch 'develop' into sdk 2026-03-30 22:39:36 +08:00
hahwu
0f31ddd868 订单优化 2026-03-30 22:38:09 +08:00
hahwu
61e40b800e 锦标赛优化 2026-03-30 22:22:32 +08:00
hahwu
a8945997f7 时间线优化 2026-03-30 21:37:46 +08:00
hahwu
6d2729ebd0 Merge branch 'develop' into sdk 2026-03-30 21:05:30 +08:00
hahwu
aa160e397a 破产时间线优化 2026-03-30 20:57:12 +08:00
hahwu
c07cfbcf40 破产时间线优化 2026-03-30 20:45:43 +08:00
hahwu
a7b2b39fd2 破产时间线优化 2026-03-30 20:39:43 +08:00
hahwu
4dbb8de2e9 gm命令更新 2026-03-30 20:20:06 +08:00
hahwu
d1e85cb692 Merge branch 'develop' into sdk 2026-03-30 20:00:19 +08:00
hahwu
d161d06602 增加破产时间线 2026-03-30 19:59:11 +08:00
hahwu
715762e80c 增加破产时间线 2026-03-30 19:54:39 +08:00
hahwu
97342e2c60 playroom bug修复 2026-03-30 19:22:15 +08:00
hahwu
17a4e0b70d 增加破产时间线 2026-03-30 19:03:13 +08:00
hahwu
bb389218ae 破产增加时间线 2026-03-30 19:03:13 +08:00
hahwu
fbcf8080fc 补充接口 2026-03-30 19:03:13 +08:00
hahwu
2eec48d301 增加锦标赛协议返回 2026-03-30 19:03:12 +08:00
hahwu
3e049ddeb5 猫猫小金库优化 2026-03-30 19:03:12 +08:00
hahwu
3556bb41bb 锦标赛进度奖励未领0点补发 2026-03-30 19:03:12 +08:00
hahwu
d709e81adf 增加破产时间线 2026-03-30 18:57:13 +08:00
hahwu
f2da1279a7 破产增加时间线 2026-03-30 18:56:06 +08:00
hahwu
928b149d66 补充接口 2026-03-30 18:45:50 +08:00
hahwu
9d9f839a75 增加锦标赛协议返回 2026-03-30 18:16:18 +08:00
hahwu
981e1d0d57 猫猫小金库优化 2026-03-30 17:46:05 +08:00
hahwu
a89a745d8c 锦标赛进度奖励未领0点补发 2026-03-30 17:19:17 +08:00
hahwu
e3d8f645ce 锦标赛0点更新 2026-03-30 16:51:03 +08:00
hahwu
4ae8a22883 修改锦标赛结算时间 2026-03-30 16:51:03 +08:00
hahwu
48423752a1 增加日志 2026-03-30 16:51:03 +08:00
hahwu
1a6ba1ab56 gm优化 2026-03-30 16:51:03 +08:00
hahwu
c9e97c91bf 锦标赛0点更新 2026-03-30 16:40:35 +08:00
hahwu
71834aaf7e 修改锦标赛结算时间 2026-03-30 16:21:50 +08:00
hahwu
22e29b7564 增加日志 2026-03-30 16:14:26 +08:00
hahwu
578213ee50 gm优化 2026-03-30 16:04:50 +08:00
hahwu
72e21945b6 Merge branch 'develop' into sdk 2026-03-30 14:38:09 +08:00
hahwu
c0fe7338ef 接口注册 2026-03-30 13:09:35 +08:00
hahwu
57b2425569 超级订单优化 2026-03-30 12:17:24 +08:00
hahwu
b79cb08aca 修改超级订单数值 2026-03-30 11:46:03 +08:00
hahwu
ed7cd0d962 增加游戏版本号 2026-03-30 11:26:57 +08:00
hahwu
7a04463517 修改猫咪宝藏数值为四舍五入 2026-03-30 11:23:42 +08:00
hahwu
34099258a7 优化订单逻辑,直接生成订单的价值大于75 2026-03-30 11:07:58 +08:00
hahwu
19e4c292c7 增加棋子从背包放入荣誉室接口 2026-03-30 10:47:43 +08:00
hahwu
2cbc354cb3 优化打点日志 2026-03-27 18:54:45 +08:00
hahwu
47de734e17 退役清理订单不判断棋子是否解锁 2026-03-27 16:54:35 +08:00
hahwu
ae558370d5 猫咪戏法配置优化 2026-03-27 14:44:46 +08:00
hahwu
4c966b46c4 订单优化 2026-03-27 14:28:31 +08:00
hahwu
dab7c252b4 订单优化 2026-03-27 11:25:57 +08:00
hahwu
420d0d6d9e 订单数值调整 2026-03-27 10:44:21 +08:00
hahwu
2ba880d869 调试工具修改 2026-03-26 17:17:49 +08:00
hahwu
f62cd62952 虚拟体力打点 2026-03-26 15:10:50 +08:00
hahwu
820ae5bbfa 高级订单优化退役棋子的生成 2026-03-25 18:30:22 +08:00
hahwu
5a04b66a1c 优化备份对象的回收 2026-03-25 11:42:19 +08:00
hahwu
1b54ab5f43 高级订单增加12级解锁限制 2026-03-25 11:19:45 +08:00
hahwu
c090807cb3 订单bug修复 2026-03-24 20:03:54 +08:00
hahwu
9574295f8d 代码优化 2026-03-24 18:52:49 +08:00
hahwu
bc417b6301 高级订单符合条件并没有直接生效 2026-03-24 15:53:26 +08:00
hahwu
a4efd1bac1 playroom破产机制 2026-03-24 14:24:23 +08:00
hahwu
36d0eed502 系统参数抽离 2026-03-24 14:10:03 +08:00
hahwu
2aa7947dad 猫咪戏法取消显示时间 2026-03-24 14:09:27 +08:00
hahwu
6d46b9de85 猫猫小金库数值修改 2026-03-24 10:31:01 +08:00
hahwu
de33ba1221 猫咪戏法优化 2026-03-23 18:31:07 +08:00
hahwu
84cf7df5f8 调整GC参数 2026-03-23 17:43:40 +08:00
hahwu
3468f8f220 猫猫小金库数值修改 2026-03-23 17:42:55 +08:00
hahwu
381a932b06 零件订单不使用自动发射器产物 2026-03-23 17:33:27 +08:00
hahwu
186eba9d55 sql语句优化 2026-03-23 17:24:24 +08:00
hahwu
bf5eafc0e0 活动优化 2026-03-23 15:06:52 +08:00
hahwu
f3ce3249cc 活动优化 2026-03-23 14:57:32 +08:00
hahwu
92417342bf 活动配置优化 2026-03-23 14:52:31 +08:00
hahwu
d190c2edf8 备份的序列化对象使用内存池 2026-03-23 11:39:36 +08:00
hahwu
e0b255a859 调整GC参数 2026-03-23 11:20:43 +08:00
hahwu
638126b50b 保存数据优化 2026-03-20 18:56:26 +08:00
hahwu
133beb47b4 数据库优化 2026-03-20 18:56:26 +08:00
hahwu
d38bb332d6 保存数据优化 2026-03-20 18:46:59 +08:00
hahwu
aa4656da0a 数据库优化 2026-03-20 18:41:30 +08:00
hahwu
c01b594247 优化邀请奖励打点方式 2026-03-20 18:07:05 +08:00
hahwu
bb32b5b124 优化邀请奖励打点方式 2026-03-20 18:06:17 +08:00
hahwu
d24c197640 增加GM指令 2026-03-20 17:06:02 +08:00
hahwu
ef03eb4581 增加GM指令 2026-03-20 17:02:56 +08:00
hahwu
6ace9fa701 修复时间线bug 2026-03-20 16:36:34 +08:00
hahwu
72ccca583b 修复时间线bug 2026-03-20 16:36:15 +08:00
hahwu
d916df4428 优化日志打点 2026-03-20 15:50:13 +08:00
hahwu
3dda192a6f 优化日志打点 2026-03-20 15:46:15 +08:00
hahwu
7c3dda750f 增加日志打点 2026-03-20 11:49:35 +08:00
hahwu
0d1f3a5cf8 1 2026-03-20 11:49:35 +08:00
hahwu
171aab883a 增加日志打点 2026-03-20 11:48:48 +08:00
hahwu
0316e06823 1 2026-03-19 21:00:02 +08:00
hahwu
390fb0b562 打点优化 2026-03-19 19:57:05 +08:00
hahwu
e3d7370ff6 打点优化 2026-03-19 19:56:33 +08:00
hahwu
98b4e71f58 Merge branch 'develop' into sdk 2026-03-19 19:49:52 +08:00
hahwu
b1c72db334 增加打点属性 2026-03-19 19:43:54 +08:00
hahwu
3a2391769f 七日签到优化-增加等级限制 2026-03-19 19:14:44 +08:00
hahwu
0d5bfaab96 七日签到优化 2026-03-19 19:14:44 +08:00
hahwu
3fa38ea0c5 优化宠物宝藏奖励 2026-03-19 19:14:44 +08:00
hahwu
1b0b77978c 七日签到优化-增加等级限制 2026-03-19 19:10:54 +08:00
hahwu
d2d31d94ab 七日签到优化 2026-03-19 18:48:18 +08:00
hahwu
3c8f9a8eb6 优化宠物宝藏奖励 2026-03-19 18:32:41 +08:00
hahwu
ccae45ccf3 宠物宝藏宠物币上限也需乘以翻倍系数 2026-03-19 18:17:49 +08:00
hahwu
519c8a7a68 宠物宝藏宠物币上限也需乘以翻倍系数 2026-03-19 18:14:11 +08:00
hahwu
2fc50aeeaa Merge branch 'develop' into sdk 2026-03-19 15:31:37 +08:00
hahwu
852eb088fa 收藏品奖励优化 2026-03-19 15:25:13 +08:00
hahwu
57d2ab0f69 1 2026-03-19 15:06:12 +08:00
hahwu
fdbeb25af2 七日签到数值优化 2026-03-19 12:07:27 +08:00
hahwu
7b18ddbbdd 挖矿打点 2026-03-19 11:35:27 +08:00
hahwu
05c5c5dcaa 挖矿打点 2026-03-19 11:20:32 +08:00
hahwu
7270808f80 修改宠物币根据膨胀系数变化功能 2026-03-19 10:39:34 +08:00
hahwu
31e41bab0f playroom任务转盘奖励只抽取playroom 2026-03-19 09:54:53 +08:00
hahwu
ee9b19b98c 邀请好友,增加已成功判断 2026-03-19 09:41:40 +08:00
hahwu
658fd9e2b8 七日签到奖励优化 2026-03-18 18:47:33 +08:00
hahwu
c72819f253 优化GM命令 2026-03-18 17:03:28 +08:00
hahwu
32d66d96fd 1 2026-03-18 16:58:54 +08:00
hahwu
1b125ca222 每日任务周奖励优化 2026-03-18 16:32:12 +08:00
hahwu
1e7fac37b8 1 2026-03-18 16:26:05 +08:00
hahwu
2341d2d144 日志优化 2026-03-18 14:42:06 +08:00
hahwu
66f985f758 1 2026-03-18 14:41:18 +08:00
hahwu
95c4c4ee2e 买一赠一礼包优化 2026-03-18 12:24:19 +08:00
hahwu
7df724a8d6 买一赠一礼包优化 2026-03-18 12:18:19 +08:00
hahwu
01fc89f874 挖矿活动埋点 2026-03-18 10:59:28 +08:00
hahwu
7d5161a44d gm优化 2026-03-18 10:45:40 +08:00
hahwu
2a43279167 宠物宝藏奖励基础值需乘当前场景倍率 2026-03-18 10:34:01 +08:00
hahwu
469e52f984 增加gm 2026-03-17 18:59:53 +08:00
hahwu
76e18cac7b 活动配置优化 2026-03-17 18:57:01 +08:00
hahwu
9dec8fa266 每日任务数值优化 2026-03-17 15:25:25 +08:00
hahwu
b1b22e06d8 七日签到奖励优化 2026-03-17 14:57:20 +08:00
hahwu
330e107cec 修复空指针报错 2026-03-17 11:19:59 +08:00
hahwu
630064912a 修复空指针报错 2026-03-17 10:58:51 +08:00
hahwu
1f1aa56d1b 日志优化 2026-03-17 10:29:10 +08:00
hahwu
fa8e1a93d7 活动配置优化 2026-03-17 10:13:51 +08:00
hahwu
80f2225ee2 修复玩家线上playroom生理值错误 2026-03-16 19:51:54 +08:00
hahwu
47ffc52955 修复玩家线上playroom生理值错误 2026-03-16 19:37:20 +08:00
hahwu
cb5c5e91f7 bug修复 2026-03-16 17:21:13 +08:00
hahwu
a2997b3be5 修复优化bug 2026-03-16 16:58:17 +08:00
hahwu
4caadbf837 优化 2026-03-16 16:09:39 +08:00
hahwu
0020a4af8c 1 2026-03-13 18:40:42 +08:00
hahwu
b46ef82f4b Revert "七日签到奖励bug修复"
This reverts commit 53c573d7e0.
2026-03-12 20:06:57 +08:00
hahwu
4f9b7ed56d Revert "七日签到奖励bug修复"
This reverts commit 53c573d7e0.
2026-03-12 20:05:40 +08:00
hahwu
53c573d7e0 七日签到奖励bug修复 2026-03-12 19:58:07 +08:00
hahwu
5c7c0ca368 七日签到奖励bug修复 2026-03-12 19:57:54 +08:00
hahwu
bb035e974a 礼包充值打点 2026-03-12 19:26:19 +08:00
hahwu
ff3c6468cf 排行榜优化 2026-03-12 19:23:59 +08:00
hahwu
9b653d37e6 礼包充值打点 2026-03-12 17:05:32 +08:00
hahwu
6450c857ce GM 指令,映射账号 2026-03-12 16:09:45 +08:00
hahwu
47e7b0f09f 活动回收邮件优化 2026-03-12 15:45:56 +08:00
hahwu
a02d520854 邮件回收奖励优化 2026-03-12 15:45:56 +08:00
hahwu
410d918a75 活动回收邮件优化 2026-03-12 15:44:24 +08:00
hahwu
1639ee330f 邮件回收奖励优化 2026-03-12 14:24:42 +08:00
hahwu
8c90d018c2 ZADD只保存10000个排行数据 2026-03-12 11:49:02 +08:00
hahwu
a386b1e29f ZADD只保存10000个排行数据 2026-03-12 11:48:39 +08:00
hahwu
042f6339d5 socket断开自动保存数据 2026-03-12 11:26:46 +08:00
hahwu
715e88a41e socket断开自动保存数据 2026-03-12 11:23:31 +08:00
hahwu
77ff241698 Revert "活动优化,活动类型判断以时间段为条件"
This reverts commit 37ef7dd434.
2026-03-12 11:20:24 +08:00
hahwu
37ef7dd434 活动优化,活动类型判断以时间段为条件 2026-03-12 10:54:58 +08:00
hahwu
a446431de4 活动初始化增加定时器 2026-03-11 22:05:19 +08:00
hahwu
631944f2be 活动初始化增加定时器 2026-03-11 22:03:17 +08:00
hahwu
2624c1dfaa 1+1礼包优化 2026-03-11 18:20:56 +08:00
hahwu
e7a584b3f4 限时事件-单元测试 2026-03-11 18:20:56 +08:00
hahwu
7d0de60f01 后台增加信息显示 2026-03-11 18:20:56 +08:00
hahwu
22994b9cd6 message 信息修复 2026-03-11 18:20:55 +08:00
hahwu
fac56917a9 1+1礼包优化 2026-03-11 18:16:00 +08:00
hahwu
1a61c7fd1c 限时事件-单元测试 2026-03-11 17:47:17 +08:00
hahwu
a3622b43f0 后台增加信息显示 2026-03-11 17:08:27 +08:00
hahwu
0abecb446e message 信息修复 2026-03-11 17:08:00 +08:00
hahwu
83605e14f0 压力测试 2026-03-11 16:20:00 +08:00
hahwu
4bef5ae362 锦标赛优化 2026-03-11 16:20:00 +08:00
hahwu
50aa304ff1 1.修复发射器在背包生成订单奖励的bug;2.修复回收订单退役发射器判断错误的bug 2026-03-11 16:19:59 +08:00
hahwu
2d0af7a2af 锦标赛优化分组 2026-03-11 16:19:59 +08:00
hahwu
5647721a41 压力测试 2026-03-11 16:19:25 +08:00
hahwu
a54b6a4175 锦标赛优化 2026-03-11 15:28:30 +08:00
hahwu
b26297c864 1.修复发射器在背包生成订单奖励的bug;2.修复回收订单退役发射器判断错误的bug 2026-03-11 15:12:48 +08:00
hahwu
bb820093ff 锦标赛优化分组 2026-03-11 14:09:56 +08:00
hahwu
7b711cf054 Merge branch 'develop' into sdk 2026-03-10 21:52:23 +08:00
hahwu
f50c7f6e86 增加挖矿Gm 2026-03-10 21:41:55 +08:00
hahwu
beda327165 增加挖矿Gm 2026-03-10 21:40:20 +08:00
hahwu
d9eb329bea 锦标赛优化 2026-03-10 21:38:44 +08:00
hahwu
c28bf27f8f 锦标赛优化 2026-03-10 21:23:00 +08:00
hahwu
c18a8cc3ce 增加分组日志 2026-03-10 21:17:38 +08:00
hahwu
485280243a 中心服优化 2026-03-10 20:57:14 +08:00
hahwu
fefc3e6eae 【先禁用】增加订单逻辑,删除无法完成的回收订单 2026-03-10 20:11:08 +08:00
hahwu
89f9b84819 增加订单逻辑,删除无法完成的回收订单 2026-03-10 19:54:49 +08:00
hahwu
3c4824e59e 增加日志打点 2026-03-10 19:14:01 +08:00
hahwu
4f984125c5 Merge branch 'develop' into sdk 2026-03-10 18:50:55 +08:00
hahwu
ea603fd617 修改活动礼包限制 2026-03-10 18:47:05 +08:00
hahwu
37bb6f8a70 bug修复 2026-03-10 15:39:35 +08:00
hahwu
8dbefbe3d9 场景冲刺逻辑修改 2026-03-10 15:32:21 +08:00
hahwu
8a79ce2b64 锦标赛,挖矿配置调整 2026-03-10 12:07:23 +08:00
hahwu
892d81907e 退役的棋子不生成回收订单 2026-03-10 11:01:33 +08:00
hahwu
50a4a9d9c2 订单价格调整 2026-03-10 10:26:31 +08:00
hahwu
55c40cf077 logout msg优化 2026-03-09 22:30:14 +08:00
hahwu
511a69454d playroom小游戏翻牌优化 2026-03-09 22:26:48 +08:00
hahwu
e69ec1d786 订单宠物币bug修复 2026-03-09 22:22:15 +08:00
hahwu
f619449137 免广告礼包优化 2026-03-09 21:44:21 +08:00
hahwu
6547a4a9db 锦标赛机器人优化 2026-03-09 19:30:18 +08:00
hahwu
fc830c47d9 挖矿初始奖励2个代币 2026-03-09 19:00:07 +08:00
hahwu
5b0bda5291 bug修复 2026-03-09 18:21:57 +08:00
hahwu
da5b8f040b 协议信息小改 2026-03-09 18:17:27 +08:00
hahwu
65bc278a06 好友优化 2026-03-09 18:11:23 +08:00
hahwu
a882a010d3 锦标赛排行榜中机器人的头像框改成初始的 2026-03-09 16:25:22 +08:00
hahwu
ce5c64f789 翻牌游戏数值修复 2026-03-09 16:10:58 +08:00
hahwu
88f1443168 Merge branch 'develop' into sdk 2026-03-09 15:11:51 +08:00
hahwu
7c95366d59 日志优化 2026-03-09 14:04:25 +08:00
hahwu
bc55208047 日志优化 2026-03-09 12:21:22 +08:00
hahwu
d9212ee249 日志优化 2026-03-09 12:18:49 +08:00
hahwu
917c084ba1 日志打印优化 2026-03-09 12:12:19 +08:00
hahwu
6dc3be91f6 优化 2026-03-09 11:50:27 +08:00
hahwu
3a82f1cda8 锦标赛返回逻辑优化 2026-03-09 11:47:05 +08:00
hahwu
aa337c2e63 日常任务奖励,使用配置进行管理 2026-03-09 11:41:22 +08:00
hahwu
5f00cf6a14 限时事件协议优化 2026-03-09 11:39:57 +08:00
hahwu
5b70a69ca1 优化 2026-03-09 11:29:18 +08:00
hahwu
7175dd3c78 锦标赛优化 2026-03-06 19:14:49 +08:00
hahwu
e1022d475e 中心服优化 2026-03-06 18:41:28 +08:00
hahwu
df883aeea7 增加打点日志 2026-03-06 17:40:13 +08:00
hahwu
ac736dc6bd 日志优化 2026-03-06 17:32:56 +08:00
hahwu
b966557d03 优化锦标赛日志 2026-03-06 17:10:51 +08:00
hahwu
b8309ca89d 每日任务优化 2026-03-06 16:54:17 +08:00
hahwu
4fcae1b8f0 挖矿奖励优化 2026-03-06 16:14:16 +08:00
hahwu
db3a30b61d 离线消息优化 2026-03-06 15:35:35 +08:00
hahwu
0e97028a25 优化 2026-03-06 15:25:42 +08:00
hahwu
20cd54b84f 锦标赛优化,0点多进行一次分组 2026-03-06 14:58:30 +08:00
hahwu
ed7e2d7625 锦标赛优化,0点多进行一次分组 2026-03-06 14:55:25 +08:00
hahwu
ed981184d3 修复从缓冲区生成的回收订单协议报错的问题 2026-03-06 10:37:51 +08:00
hahwu
aa7ee2a381 playroom翻牌游戏优化 2026-03-05 18:48:25 +08:00
hahwu
c72761d627 RPC服务器先不启用 2026-03-05 18:33:13 +08:00
hahwu
a2a8da873e GM rpc服务器 2026-03-05 17:55:36 +08:00
hahwu
385d571c3a thrift GM服务器 2026-03-05 17:51:03 +08:00
hahwu
0e01178669 Merge branch 'thrift-backend' into develop 2026-03-05 17:49:51 +08:00
hahwu
dfc6f986bc 设置表情更新缓存 2026-03-05 16:41:17 +08:00
hahwu
e892469ff9 好友功能优化 2026-03-05 15:40:28 +08:00
hahwu
4422dbc527 七日签到-月度奖励优化 2026-03-05 15:14:29 +08:00
hahwu
850cceecaa 锦标赛奖励配置优化-根据活动类型区分 2026-03-05 11:58:08 +08:00
hahwu
5e9710d2f3 猫咪毛皮 2026-03-05 11:35:32 +08:00
hahwu
2ae819376f 单次session内重复添加同一好友会有提示,重新登陆后添加相同好友却提示发送成功 2026-03-05 11:02:25 +08:00
hahwu
33dc62db36 thrift backend server 2026-03-05 09:58:56 +08:00
hahwu
a1d97a1ab6 观看广告接口 2026-03-04 16:12:28 +08:00
hahwu
fa633cf184 修改日志 2026-03-04 15:11:07 +08:00
hahwu
3afcbdf2eb 修复商店棋子错误的bug 2026-03-04 14:52:38 +08:00
hahwu
dff655484e 好友离线信息聚合 2026-03-04 14:48:36 +08:00
hahwu
49b6a6a928 好友优化 2026-03-04 11:34:53 +08:00
hahwu
8bf48093b7 增加注册日期数据 2026-03-03 15:21:06 +08:00
hahwu
a22e318c22 消息优化 2026-03-03 12:29:48 +08:00
hahwu
1e0ca0db80 gob解析优化 2026-03-02 18:52:24 +08:00
hahwu
0b2fdf49c3 gob解析优化 2026-03-02 18:44:16 +08:00
hahwu
ee8e3b9a5f gob解析优化 2026-03-02 17:28:36 +08:00
hahwu
fd4ab739bf 添加好友机制调整 2026-03-02 17:10:10 +08:00
hahwu
b0e2c5672c 添加好友机制调整 2026-03-02 16:46:57 +08:00
hahwu
64901ca8b0 添加好友机制调整 2026-03-02 16:34:14 +08:00
hahwu
5bdd93cbef 订单奖励的猫币数量异常 2026-03-02 15:09:14 +08:00
hahwu
766bcfd30b 订单奖励的猫币数量异常 2026-03-02 14:37:53 +08:00
hahwu
698aec8556 fur数据埋点 2026-03-02 14:12:28 +08:00
hahwu
df9139cec8 增加七日登录重置GM 2026-03-02 11:34:35 +08:00
hahwu
ed57b527a3 优化订单通知显示 2026-02-28 18:43:12 +08:00
hahwu
b3689ca93d 宠物皮毛功能开发 2026-02-28 18:36:29 +08:00
hahwu
cf5f30cebb resshop数据修改 2026-02-28 15:23:12 +08:00
hahwu
a33320065c 增加日志分析工具 2026-02-28 10:54:56 +08:00
hahwu
0e391bc071 notification优化 2026-02-28 10:17:15 +08:00
hahwu
63f36aeb3a 优化notification Gm 2026-02-27 00:51:07 +08:00
hahwu
33cbdb94e0 优化notification Gm 2026-02-27 00:51:07 +08:00
hahwu
b856dfb5ff 优化notification Gm 2026-02-27 00:50:46 +08:00
hahwu
f38206184c 优化notification Gm 2026-02-27 00:45:19 +08:00
hahwu
7b6ed80126 优化notification GM 2026-02-27 00:31:24 +08:00
hahwu
d10124fdb2 优化notification GM 2026-02-27 00:31:03 +08:00
hahwu
f56121d9ee 增加notification debug 2026-02-27 00:01:54 +08:00
hahwu
1b545ea1b7 增加notification debug 2026-02-27 00:01:22 +08:00
hahwu
11baae6ea5 notification 优化 2026-02-26 20:10:03 +08:00
hahwu
2d6bcc09b9 notification 优化 2026-02-26 20:09:43 +08:00
hahwu
35f35d0a97 notification debug 2026-02-26 19:55:31 +08:00
hahwu
1e58dccf11 notification debug 2026-02-26 19:55:09 +08:00
hahwu
4fc531d9f2 gob register 2026-02-26 19:19:20 +08:00
hahwu
20dd3f6683 gob register 2026-02-26 19:18:50 +08:00
hahwu
bcbcc6a767 Merge branch 'develop' into sdk 2026-02-26 19:09:16 +08:00
hahwu
d4035acd2c Revert "Reapply "小补丁【取消邮件协议】""
This reverts commit 70f8331c0b.
2026-02-26 19:08:55 +08:00
hahwu
532dba9873 notification优化 2026-02-26 19:02:13 +08:00
hahwu
69fc58ec61 优化序列化方法 2026-02-26 14:16:49 +08:00
hahwu
72900b216c 优化序列化方法 2026-02-26 14:15:52 +08:00
hahwu
36ab0e0d00 回收日bug修复 2026-02-26 14:14:59 +08:00
hahwu
3de1b738a3 优化序列化方法 2026-02-26 14:10:13 +08:00
hahwu
b7472d7ab3 gob register优化 2026-02-26 11:41:26 +08:00
hahwu
8a6f0b4e92 notification优化 2026-02-26 11:40:00 +08:00
hahwu
776a05e959 charge bug修复 2026-02-26 11:39:22 +08:00
hahwu
abeabab66b Merge branch 'develop' of gitea.bywaystudios.com:pet_home/pet_home_server into develop 2026-02-26 11:29:31 +08:00
hahwu
295f7f9222 取消弃用的进程管理器 2026-02-26 11:29:04 +08:00
hahwu
29a466dfb2 回收日bug修复 2026-02-26 11:28:09 +08:00
hahwu
2c8af266b7 notification功能开发 2026-02-26 10:25:15 +08:00
hahwu
41ee2e48d4 魔法数字优化 2026-02-26 10:24:58 +08:00
hahwu
11f7b4960d 优化接口报错 2026-02-26 10:04:20 +08:00
hahwu
a10b2fefa7 notification功能开发 2026-02-25 18:30:26 +08:00
hahwu
c26c3086af notification功能开发 2026-02-25 17:58:16 +08:00
hahwu
2835465e0d bug修复 2026-02-25 17:30:30 +08:00
hahwu
d1111725ef notification功能开发 2026-02-25 17:24:24 +08:00
hahwu
d310ce93f6 notification功能开发 2026-02-25 17:10:09 +08:00
hahwu
5b7812c469 玩家缓存增加语言设置 2026-02-25 12:06:31 +08:00
hahwu
623baec632 修改message 2026-02-25 12:03:22 +08:00
hahwu
468e65e8da 给信息增加过期时间 2026-02-25 11:30:18 +08:00
hahwu
f9d33219f5 信箱优化 2026-02-25 11:05:03 +08:00
hahwu
1b51313cf0 信箱优化 2026-02-24 19:04:41 +08:00
hahwu
7339e3c634 代码优化 2026-02-24 18:19:10 +08:00
hahwu
15679495be 数据保存加锁 2026-02-24 12:21:29 +08:00
hahwu
e6378568b1 数据保存加锁 2026-02-24 12:19:22 +08:00
hahwu
57f2e21061 Revert "数据保存加锁"
This reverts commit f026927a10.
2026-02-24 11:41:36 +08:00
hahwu
260117bf31 Revert "数据保存加锁"
This reverts commit 437eeac1dd.
2026-02-24 11:41:18 +08:00
hahwu
260e623dfc Revert "数据保存加锁"
This reverts commit f026927a10.
2026-02-24 11:40:49 +08:00
hahwu
c9742c54ee Revert "数据保存加锁"
This reverts commit 437eeac1dd.
2026-02-24 11:40:45 +08:00
hahwu
437eeac1dd 数据保存加锁 2026-02-24 11:35:06 +08:00
hahwu
c5f59b6ceb 数据保存加锁 2026-02-24 10:41:43 +08:00
hahwu
f026927a10 数据保存加锁 2026-02-24 10:40:37 +08:00
hahwu
8f88417d01 Revert "Reapply "小补丁【取消小猪存钱罐】""
This reverts commit 0f8695664e.
2026-02-24 10:17:11 +08:00
hahwu
87c3448dce 修复订单bug 2026-02-24 10:05:14 +08:00
hahwu
738015e1d3 修复订单bug 2026-02-24 10:05:14 +08:00
hahwu
4a28d63534 修复订单bug 2026-02-24 10:04:57 +08:00
hahwu
cea4503070 修复订单bug 2026-02-24 10:04:57 +08:00
hahwu
70f8331c0b Reapply "小补丁【取消邮件协议】"
This reverts commit 1f80e667d4.
2026-02-14 15:02:14 +08:00
hahwu
0f8695664e Reapply "小补丁【取消小猪存钱罐】"
This reverts commit a28d4ca61d.
2026-02-14 15:02:00 +08:00
hahwu
67cff6a431 优化序列化 2026-02-14 14:44:34 +08:00
hahwu
a28d4ca61d Revert "小补丁【取消小猪存钱罐】"
This reverts commit 1693606a3a.
2026-02-14 14:28:15 +08:00
hahwu
1f80e667d4 Revert "小补丁【取消邮件协议】"
This reverts commit 47819ca3c9.
2026-02-14 14:24:52 +08:00
hahwu
1693606a3a 小补丁【取消小猪存钱罐】 2026-02-14 10:43:41 +08:00
hahwu
4a0788729a 途游notification 2026-02-13 19:04:52 +08:00
hahwu
47819ca3c9 小补丁【取消邮件协议】 2026-02-13 19:04:19 +08:00
hahwu
026d7a363d Reapply "小补丁,优化头像框"
This reverts commit e175e85477.
2026-02-13 13:31:46 +08:00
hahwu
e175e85477 Revert "小补丁,优化头像框"
This reverts commit b5a283b4b8.
2026-02-13 13:07:32 +08:00
hahwu
20df8f3a32 Merge branch 'develop' into sdk 2026-02-12 23:01:55 +08:00
hahwu
324ec44b1f 死循环修复 2026-02-12 23:01:32 +08:00
hahwu
e2e28082ce Merge branch 'develop' into sdk 2026-02-12 14:53:48 +08:00
hahwu
52a24f3376 增加日志打点 2026-02-12 14:52:54 +08:00
hahwu
4e8da1047f 后台邮件优化 2026-02-12 14:17:10 +08:00
hahwu
e7feae1136 头像框初始化 2026-02-12 11:21:15 +08:00
hahwu
29b55d85a3 Merge branch 'develop' into sdk 2026-02-12 11:18:35 +08:00
hahwu
b9bd656130 优化 2026-02-12 11:18:09 +08:00
hahwu
62b8959806 增加GM指令,重置trigger mail记录 2026-02-12 10:38:33 +08:00
hahwu
f3a764e2dc 邮件优化 2026-02-12 10:33:19 +08:00
hahwu
97f8e3b96d 打点日志优化 2026-02-12 10:26:00 +08:00
hahwu
8b236925c2 背包优化 2026-02-12 10:25:41 +08:00
hahwu
57c07ca2a3 Merge branch 'develop' into sdk 2026-02-11 16:22:10 +08:00
hahwu
088208a3bb GM后台优化 2026-02-11 16:19:15 +08:00
hahwu
2084223308 GM后台优化 2026-02-11 15:32:13 +08:00
hahwu
e4ba7fd004 特惠礼包bug修复 2026-02-11 14:47:41 +08:00
hahwu
c94e97dbe6 GM后台优化 2026-02-11 14:29:43 +08:00
hahwu
8142cac415 头像收藏优化 2026-02-11 14:29:31 +08:00
hahwu
13e580caaa 增加日志 2026-02-11 11:42:10 +08:00
hahwu
1c058d5546 优化 2026-02-10 18:22:51 +08:00
hahwu
b41130b6fa 无尽礼包优化 2026-02-10 18:18:46 +08:00
hahwu
8328ac6b63 无尽礼包优化 2026-02-10 17:06:26 +08:00
hahwu
60785c9909 quest playroom fix bug 2026-02-10 16:44:40 +08:00
hahwu
1674ae161f quest playroom fix bug 2026-02-10 16:28:52 +08:00
hahwu
9d4a2c9f15 playroom服装任务bug修复 2026-02-10 15:45:12 +08:00
hahwu
8967922db6 增加GM指令 2026-02-10 15:11:01 +08:00
hahwu
ceb3c93df0 playroom每日任务转盘bug修复 2026-02-10 14:49:49 +08:00
hahwu
3703b2396a 增加任务类型 2026-02-10 11:48:12 +08:00
hahwu
ccc02de315 1.好友推荐优化;2AD energy增加常量;3playroom日常任务优化 2026-02-10 11:03:46 +08:00
hahwu
05b5341a1d 回收订单优化 2026-02-09 17:55:38 +08:00
hahwu
b68d8bd4d5 回收订单优化 2026-02-09 17:48:52 +08:00
hahwu
17340f056d Revert "架构优化"
This reverts commit 806780b5b3.
2026-02-09 15:57:00 +08:00
hahwu
806780b5b3 架构优化 2026-02-09 15:51:23 +08:00
hahwu
b66bfa0209 随机数优化 2026-02-09 14:30:56 +08:00
hahwu
f42dd2dd95 推荐优化 2026-02-09 14:13:03 +08:00
hahwu
b4317cf3c4 推荐优化 2026-02-09 14:07:57 +08:00
hahwu
6172c12227 推荐优化 2026-02-09 12:19:59 +08:00
hahwu
4e832972fa 推荐优化 2026-02-09 10:41:19 +08:00
hahwu
30f32e1da0 【架构优化】提升代码质量 2026-02-06 17:39:27 +08:00
hahwu
0f2aedb150 增加翻译 2026-02-06 15:25:35 +08:00
hahwu
6560eb19ac sql语句更新 2026-02-06 10:51:33 +08:00
hahwu
69cbb39221 多语言增加西班牙语 2026-02-06 10:29:09 +08:00
hahwu
fb4367080d 【架构优化】list设置容量,避免动态扩容 2026-02-05 19:04:20 +08:00
hahwu
d18d10eee9 Revert "小补丁,优化头像框"
This reverts commit b5a283b4b8.
2026-02-05 19:00:53 +08:00
hahwu
e6f7db6cca 【架构优化】list设置容量,避免动态扩容 2026-02-05 19:00:32 +08:00
hahwu
5920d53838 Revert "小补丁,优化头像框"
This reverts commit bec7fa7ea5.
2026-02-05 16:41:55 +08:00
hahwu
e2200be3ff 【架构优化】list设置容量,避免动态扩容 2026-02-05 16:39:51 +08:00
hahwu
bec7fa7ea5 小补丁,优化头像框 2026-02-05 16:30:45 +08:00
hahwu
8345076c28 【架构优化】list设置容量,避免动态扩容 2026-02-05 16:27:34 +08:00
hahwu
b5a283b4b8 小补丁,优化头像框 2026-02-05 15:57:54 +08:00
hahwu
46a76843a9 【架构优化】list设置容量,避免动态扩容 2026-02-05 15:11:31 +08:00
hahwu
e5730c206e 【锦标赛】优化排行榜 2026-02-04 18:44:08 +08:00
hahwu
f17e2948a3 【锦标赛】优化排行榜 2026-02-04 18:35:23 +08:00
hahwu
56157dd32c 【限时事件】超级订单优化 2026-02-04 18:20:21 +08:00
hahwu
9b08cdebe6 【架构优化】1.var删除冗余数据;2.champship增加redis缓存,增加读写锁;3.message过滤过期消息,删除空信箱 2026-02-04 18:10:45 +08:00
hahwu
a80d65ccd2 【限时事件】超级订单修改回退 2026-02-04 18:01:23 +08:00
hahwu
fdcb4faef9 【限时事件】超级订单bug修复 2026-02-04 17:44:44 +08:00
hahwu
75ae03be9f 锦标赛优化 2026-02-04 16:48:48 +08:00
hahwu
c1f9d0aa20 【限时事件】宝箱雨修复 2026-02-04 16:23:59 +08:00
hahwu
5da2f68a0c 锦标赛优化 2026-02-04 15:00:04 +08:00
hahwu
00809c8484 卡牌时间线优化 2026-02-04 10:33:20 +08:00
hahwu
87aef36237 通过删除重复添加同一个好友,会重复触发好友体力,造成刷体力的情况 2026-02-03 17:40:08 +08:00
hahwu
25cfbf30a2 玩家消息bug修复 2026-02-03 17:20:00 +08:00
hahwu
6824df5a1c 玩家消息bug修复 2026-02-03 17:14:15 +08:00
hahwu
52fbce102f 猫咪闪促优化 2026-02-03 16:36:54 +08:00
hahwu
f14c0ba5d2 无尽礼包优化 2026-02-03 16:36:03 +08:00
hahwu
d9aedbdf55 锦标赛优化 2026-02-03 15:10:39 +08:00
hahwu
e26266f1d7 备份优化 2026-02-03 15:10:27 +08:00
hahwu
123f4d2e1c 架构优化 2026-02-02 18:52:58 +08:00
hahwu
bd96bfa29e 架构优化 2026-02-02 17:42:57 +08:00
hahwu
90e1115798 调试优化 2026-02-02 12:28:30 +08:00
hahwu
8d496bfbda 架构优化 2026-02-02 10:24:07 +08:00
hahwu
e59cbc9360 修改超级订单奖励逻辑 2026-01-30 10:21:09 +08:00
hahwu
6f7a9ab741 Merge branch 'develop' into sdk 2026-01-29 20:09:57 +08:00
hahwu
f621b938bd Merge branch 'develop' of gitea.bywaystudios.com:pet_home/pet_home_server into develop 2026-01-29 20:09:38 +08:00
hahwu
1c667bc066 1 2026-01-29 20:09:32 +08:00
hahwu
7f085b6b80 消息修复 2026-01-29 20:07:12 +08:00
hahwu
34126d990a 优化 2026-01-29 20:02:40 +08:00
hahwu
8bb2ac896e 竞标赛消息优化 2026-01-29 17:44:14 +08:00
hahwu
650bb1136f 竞标赛消息优化 2026-01-29 17:42:48 +08:00
hahwu
593bc75ab6 优化日志 2026-01-29 10:26:00 +08:00
hahwu
0a610a79bb 优化日志 2026-01-29 10:22:08 +08:00
hahwu
012656f944 Merge branch 'develop' into sdk 2026-01-27 18:31:17 +08:00
hahwu
cc77fff573 git记录中删除protobuf文件,修改成实时生成 2026-01-27 17:24:39 +08:00
hahwu
cbd054f050 修改项目,协议由每次打包实时生产 2026-01-27 17:17:20 +08:00
hahwu
7346f64dac 修改协议 2026-01-27 16:25:31 +08:00
hahwu
feeef9c449 修改协议 2026-01-27 16:25:15 +08:00
hahwu
35d214f62c 协议更新 2026-01-27 16:16:15 +08:00
hahwu
a2af41f8b7 Merge branch 'develop' into sdk 2026-01-27 16:09:43 +08:00
hahwu
56f400a4ef 修改发货通知 2026-01-27 15:25:53 +08:00
hahwu
3fa4187467 reqplayroom优化 2026-01-26 22:59:27 +08:00
hahwu
524a0cfa0f reqplayroom优化 2026-01-26 22:58:56 +08:00
hahwu
ce360714fe 优化,去除kafka中间件 2026-01-26 18:44:02 +08:00
hahwu
1730d81990 优化,去除kafka中间件 2026-01-26 18:29:06 +08:00
hahwu
27c7d38fba 日志优化 2026-01-26 18:22:15 +08:00
hahwu
8bf726f9a3 日志优化 2026-01-26 18:21:38 +08:00
hahwu
7cab2e6eb1 日志优化 2026-01-26 18:20:55 +08:00
hahwu
39575b61d5 日志优化 2026-01-26 18:20:39 +08:00
hahwu
b880ac07a7 Merge branch 'develop' into sdk 2026-01-26 17:35:33 +08:00
hahwu
d60c56a767 日志优化 2026-01-26 17:35:15 +08:00
hahwu
746ad76a5b 订单日志 2026-01-26 15:56:47 +08:00
hahwu
3e11991b02 增加日志 2026-01-26 12:24:21 +08:00
hahwu
22c20dd8b3 增加日志 2026-01-26 12:23:13 +08:00
hahwu
8aac8e31d0 订单优化 2026-01-26 11:48:19 +08:00
hahwu
69e4c823c4 订单优化 2026-01-26 11:47:41 +08:00
hahwu
ecf37639a5 增加日志 2026-01-26 11:46:40 +08:00
hahwu
e9d1565115 bug漏洞修复;1.processtrigger 漏加锁;2message_mgr空指针报错 2026-01-26 10:19:05 +08:00
hahwu
616bd52784 Merge branch 'develop' into sdk 2026-01-23 21:08:52 +08:00
hahwu
167e5dd28c 增加日志 2026-01-23 20:59:23 +08:00
hahwu
b0fb5d0469 增加日志打点 2026-01-23 20:43:15 +08:00
hahwu
cee1ff220b 宝箱雨优化 2026-01-23 20:09:17 +08:00
hahwu
4c9b3abae0 宝箱雨优化 2026-01-23 19:51:55 +08:00
hahwu
6acc529698 优化订单提交 2026-01-23 11:43:44 +08:00
hahwu
e622273a73 后台接口优化 2026-01-22 18:12:08 +08:00
hahwu
bd36f98954 增加接口报错日志 2026-01-22 17:27:45 +08:00
hahwu
1ed7fa71b9 零件优化 2026-01-22 17:17:41 +08:00
hahwu
94a056c18a 日志优化 2026-01-21 17:56:16 +08:00
hahwu
892b6408d3 日志优化 2026-01-21 17:25:55 +08:00
hahwu
dd90f57f70 好友小喇叭优化 2026-01-21 16:49:57 +08:00
hahwu
5586a4aa4f 日志优化 2026-01-21 16:20:28 +08:00
hahwu
f81facf4cd 日志优化 2026-01-21 16:15:53 +08:00
hahwu
edceb1cf1e 限时事件优化,连击快手 2026-01-21 11:28:54 +08:00
hahwu
dd6fdde8ce 限时事件修改 2026-01-20 19:24:29 +08:00
hahwu
21ae017443 cluster节点增加可发数据长度 2026-01-20 18:59:21 +08:00
hahwu
2e50f58a09 cluster节点增加可发数据长度 2026-01-20 18:57:41 +08:00
hahwu
3d2cfa9380 节点记录 2026-01-20 18:39:01 +08:00
hahwu
104ab707a6 日志 2026-01-20 17:38:07 +08:00
hahwu
ab269ac8a6 decorate 修复 2026-01-20 16:51:37 +08:00
hahwu
08eeeb2a40 优化goroutine,decorate part增加校验 2026-01-20 16:36:17 +08:00
hahwu
c0858e20f0 装饰优化 2026-01-20 14:11:26 +08:00
hahwu
50c42e705d 日志优化 2026-01-20 12:14:22 +08:00
hahwu
1782094f92 日志优化 2026-01-19 12:24:47 +08:00
hahwu
f435b444d7 日志优化 2026-01-19 12:07:22 +08:00
hahwu
7ed6c36052 日志优化 2026-01-16 20:59:23 +08:00
hahwu
54bf168f80 日志优化 2026-01-16 20:59:10 +08:00
hahwu
bed748319f 增加日志 2026-01-16 18:59:52 +08:00
hahwu
7e4a4c2bd4 增加日志 2026-01-16 18:53:23 +08:00
hahwu
09e2142a6c Merge branch 'develop' into sdk 2026-01-16 16:11:48 +08:00
hahwu
deb90ba749 装饰步骤改版优化-邮件文本 2026-01-16 12:29:42 +08:00
hahwu
1ce0f639ce 服务端重启增加踢出消息推送 2026-01-16 12:04:40 +08:00
hahwu
bf1b96fbb5 装饰步骤改版优化 日志 2026-01-16 11:34:33 +08:00
hahwu
19881f2d09 gm优化 2026-01-16 11:32:20 +08:00
hahwu
0a48a58317 装饰步骤改版优化 2026-01-16 11:01:38 +08:00
hahwu
6c114608cd 日志优化 2026-01-16 10:34:21 +08:00
hahwu
972284868b 日志优化 2026-01-16 10:34:21 +08:00
hahwu
f659f21654 打点日志 2026-01-16 10:34:20 +08:00
hahwu
afd9c276f3 日志优化 2026-01-16 10:33:39 +08:00
hahwu
6dfb61ada9 装饰步骤改版优化 2026-01-16 10:24:43 +08:00
hahwu
48aae65afd 日志优化 2026-01-15 18:45:52 +08:00
hahwu
a66c975707 打点日志 2026-01-15 16:23:35 +08:00
hahwu
f063cd199c 打点日志 2026-01-15 16:23:12 +08:00
hahwu
87e8b9a261 代码优化 2026-01-14 18:09:20 +08:00
hahwu
804ee3251d 商店的产物直购价格调整为:产物标准链虚拟体力/5 (原来除数为18) 2026-01-14 10:46:34 +08:00
hahwu
1b0199dc15 日志优化 2026-01-14 10:10:08 +08:00
hahwu
497efd6e2d 活动回收邮件优化 2026-01-14 10:09:54 +08:00
hahwu
59bbfcd710 日志优化 2026-01-14 10:09:36 +08:00
hahwu
bf3194b401 Merge branch 'develop' into sdk 2026-01-13 17:43:15 +08:00
hahwu
7d6a040cec 单元测试 2026-01-13 17:42:53 +08:00
hahwu
ade21206c6 消息优化 2026-01-13 17:29:43 +08:00
hahwu
eaffa63ae8 消息优化 2026-01-13 17:13:46 +08:00
hahwu
5354e1bca0 点赞优化 2026-01-13 16:55:50 +08:00
hahwu
158b7a840a 点赞优化 2026-01-13 16:50:50 +08:00
hahwu
9547325c26 Merge branch 'develop' into sdk 2026-01-13 15:29:36 +08:00
hahwu
9f8e2460ac 配置优化 2026-01-13 15:28:39 +08:00
hahwu
48ddddb680 数据安全优化 2026-01-13 15:19:43 +08:00
hahwu
0df389240e 消息优化 2026-01-13 15:11:04 +08:00
hahwu
76be08fe85 Merge branch 'develop' into sdk 2026-01-13 14:12:55 +08:00
hahwu
650db03198 数据锁优化 2026-01-13 12:09:27 +08:00
hahwu
1d0ed79b13 日志优化 2026-01-13 10:33:50 +08:00
hahwu
ce8dabf112 数据安全优化 2026-01-13 10:02:37 +08:00
hahwu
9d9c5634e2 日志优化 2026-01-13 09:58:30 +08:00
hahwu
dcb0396a73 优化日志 2026-01-12 18:38:12 +08:00
hahwu
c4039899fc 锦标赛通知优化 2026-01-12 10:09:02 +08:00
hahwu
2612647e74 锦标赛通知优化 2026-01-12 10:07:47 +08:00
hahwu
e1f086fb85 增加map读写锁 2026-01-09 18:13:31 +08:00
hahwu
248405d984 map读写优化 2026-01-09 18:13:31 +08:00
hahwu
02002e6c16 逻辑bug优化 2026-01-09 18:13:30 +08:00
hahwu
8bb76e98af 增加map读写锁 2026-01-09 18:13:19 +08:00
hahwu
a8f45d221b map读写优化 2026-01-09 18:13:19 +08:00
hahwu
53e1cc8eaa 逻辑bug优化 2026-01-09 18:12:47 +08:00
hahwu
9f28bf39e4 catnip bug修复 2026-01-07 11:51:49 +08:00
hahwu
b9f6cceda1 日志优化 2026-01-07 11:51:34 +08:00
hahwu
c734417b1c catnip bug修复 2026-01-07 11:49:45 +08:00
hahwu
8b0f1989d3 日志优化 2026-01-07 11:02:21 +08:00
hahwu
70cccf23bc Merge branch 'develop' into sdk 2026-01-06 19:01:36 +08:00
hahwu
437f5d1b5b 棋子出售也会增加活动代币 2026-01-05 15:43:13 +08:00
hahwu
739a328674 棋子出售也会增加活动代币 2026-01-05 15:42:59 +08:00
hahwu
cf8b5f9c23 服务端连接优化 2026-01-04 12:08:36 +08:00
hahwu
608d7d4168 增加GM 2025-12-31 16:27:36 +08:00
hahwu
dceed4be1e 增加GM 2025-12-31 16:27:36 +08:00
hahwu
4f9d047196 消息优化 2025-12-31 15:29:04 +08:00
hahwu
9ac20a99b6 连接优化 2025-12-31 15:06:40 +08:00
hahwu
60725fca2a 连接修复 2025-12-31 14:47:50 +08:00
hahwu
88b855d949 Merge branch 'develop' of gitea.bywaystudios.com:pet_home/pet_home_server into develop 2025-12-31 14:47:23 +08:00
hahwu
98c187869b 连接修复 2025-12-31 14:47:21 +08:00
hahwu
0a53f88af7 连接优化 2025-12-31 14:02:26 +08:00
hahwu
4b471ad95d 日志优化 2025-12-31 12:01:15 +08:00
hahwu
ed7ec40d34 日志优化 2025-12-30 18:33:06 +08:00
hahwu
59a8d692b1 监控优化 2025-12-30 16:45:37 +08:00
hahwu
d326a8c4cf 连接优化 2025-12-30 16:43:07 +08:00
hahwu
2b5f0e099f 连接优化 2025-12-30 16:35:59 +08:00
hahwu
5c0ea843a0 后台优化 2025-12-30 15:55:19 +08:00
hahwu
83568e3c23 测试脚本优化 2025-12-30 15:51:36 +08:00
hahwu
42d59026fd 消息日志优化 2025-12-30 15:39:48 +08:00
hahwu
f5fef79619 消息日志优化 2025-12-30 15:03:27 +08:00
hahwu
4cf882ecd2 消息日志优化 2025-12-30 12:17:41 +08:00
hahwu
cf11a71f03 消息日志优化 2025-12-30 12:08:29 +08:00
hahwu
2ca9f004bd 消息日志优化 2025-12-30 11:48:50 +08:00
hahwu
07935149a7 增加账号重置GM 2025-12-26 16:02:57 +08:00
hahwu
f99ece7531 增加账号重置GM 2025-12-26 15:56:35 +08:00
hahwu
dd142fceaa 增加账号重置GM 2025-12-26 15:43:58 +08:00
hahwu
eb3e1e376e 增加账号重置GM 2025-12-26 15:40:38 +08:00
hahwu
b6a3b6f0be 增加账号重置GM 2025-12-26 15:25:46 +08:00
hahwu
f962718717 登录优化 2025-12-26 10:14:53 +08:00
hahwu
caff1b207a 登录优化 2025-12-25 18:21:54 +08:00
hahwu
09a48aa1ed 登录优化 2025-12-25 18:12:03 +08:00
hahwu
a7bbfc3d59 log优化 2025-12-25 17:58:41 +08:00
hahwu
a387920c1f 优化 2025-12-25 16:34:33 +08:00
hahwu
8ed0cf8889 结构优化 2025-12-25 15:27:10 +08:00
hahwu
462f60a5da gm优化 2025-12-25 15:25:20 +08:00
hahwu
2b8ce93ab1 离线优化 2025-12-25 14:48:26 +08:00
hahwu
f8084e8baf Merge branch 'develop' of gitea.bywaystudios.com:pet_home/pet_home_server into develop 2025-12-25 14:46:12 +08:00
hahwu
e3661d11b5 登录优化 2025-12-25 14:46:03 +08:00
hahwu
798004b2a3 Merge branch 'develop' of gitea.bywaystudios.com:pet_home/pet_home_server into develop 2025-12-25 12:09:32 +08:00
hahwu
d7a1eededb 后台优化 2025-12-25 12:08:53 +08:00
hahwu
857923f268 消息服务优化 2025-12-25 11:11:55 +08:00
hahwu
0f4cae323e 消息服务优化 2025-12-25 10:57:20 +08:00
hahwu
9ec87776f0 消息服务优化 2025-12-25 10:00:48 +08:00
hahwu
e21aac1c08 消息服务优化 2025-12-25 09:52:06 +08:00
hahwu
d123fc93d5 消息服务优化 2025-12-24 17:17:57 +08:00
hahwu
5768776e56 消息服务优化 2025-12-24 17:05:39 +08:00
hahwu
a9e07f103a uid优化 2025-12-24 15:13:26 +08:00
hahwu
b336e42f21 Merge branch 'cluster2' into develop 2025-12-24 15:05:16 +08:00
hahwu
ef1125b75d gm优化 2025-12-24 15:04:20 +08:00
hahwu
30eab6db4b 7 2025-12-24 14:31:29 +08:00
hahwu
e884b20e32 7 2025-12-24 14:30:37 +08:00
hahwu
7e58fd3f4b 6 2025-12-24 11:50:23 +08:00
hahwu
68c8cfe620 6 2025-12-24 11:49:29 +08:00
hahwu
b25a10f2fe 6 2025-12-24 10:28:38 +08:00
hahwu
c0f02217c1 6 2025-12-24 10:27:56 +08:00
hahwu
0036ff96c7 5 2025-12-23 16:41:52 +08:00
hahwu
2c33c24d42 5 2025-12-23 16:21:59 +08:00
hahwu
98fcc02e38 5 2025-12-23 16:16:43 +08:00
hahwu
50c6c07140 5 2025-12-23 16:16:01 +08:00
hahwu
0e86975563 4 2025-12-23 15:19:03 +08:00
hahwu
9dfa884349 4 2025-12-23 15:17:32 +08:00
hahwu
28de534bd4 3 2025-12-23 11:52:20 +08:00
hahwu
574b9f49a1 3 2025-12-23 11:45:43 +08:00
hahwu
728656d23b 2 2025-12-22 17:28:50 +08:00
hahwu
3b02cd1241 2 2025-12-22 16:45:11 +08:00
hahwu
408bd4df5b 2 2025-12-22 16:10:03 +08:00
hahwu
d16e2b3e25 1 2025-12-22 16:06:08 +08:00
hahwu
dd9e934f2d 猫草大作战 2025-12-19 18:47:07 +08:00
hahwu
c814fac8ba 猫草大作战 2025-12-19 18:46:22 +08:00
hahwu
d3fde0949a 1 2025-12-19 14:18:53 +08:00
hahwu
bc6320d11d 1 2025-12-18 19:14:38 +08:00
hahwu
1feed48e0f 1 2025-12-18 17:17:56 +08:00
hahwu
dd48b8354c 1 2025-12-18 16:33:53 +08:00
hahwu
adaf454d36 1 2025-12-18 15:59:35 +08:00
hahwu
17ac92f5a1 1 2025-12-18 14:50:30 +08:00
hahwu
54dba4b18a 1 2025-12-18 11:48:27 +08:00
hahwu
420f0a2e2c 1 2025-12-18 10:40:06 +08:00
hahwu
11d38f9483 Merge remote-tracking branch 'origin/Cluster' into develop 2025-12-18 10:25:28 +08:00
hahwu
f64d18de82 message controller 2025-12-17 20:11:28 +08:00
hahwu
eaaad55a10 棋盘数据优化 2025-12-17 19:49:08 +08:00
hahwu
1d4fce204e 棋盘数据优化 2025-12-17 19:48:48 +08:00
hahwu
65af33758b message controll优化 2025-12-17 19:45:53 +08:00
hahwu
7adabb96b1 gm优化 2025-12-17 18:13:57 +08:00
hahwu
6f4366dffc gm优化 2025-12-17 18:13:29 +08:00
hahwu
779d032857 加好友扣广告次数 2025-12-17 17:05:09 +08:00
hahwu
d5735710cd 加好友扣广告次数 2025-12-17 17:04:22 +08:00
hahwu
bd6c3380ba 项目重构 2025-12-17 16:47:57 +08:00
hahwu
e3c6fc68a2 协议更新 2025-12-17 14:17:36 +08:00
hahwu
eb7cee0292 邮件补发 2025-12-17 11:04:35 +08:00
hahwu
b29eee3a1a 【猫草大作战】补发邮件 2025-12-17 10:45:06 +08:00
hahwu
58033e959f 猫草大作战 2025-12-17 10:28:33 +08:00
hahwu
b1347f48cb 猫草大作战 2025-12-17 10:20:56 +08:00
hahwu
1520798201 catnip修改 2025-12-16 19:47:15 +08:00
hahwu
6211e7fa80 还原 2025-12-16 19:37:26 +08:00
hahwu
9215192ed4 调试 2025-12-16 19:33:37 +08:00
hahwu
584e2563f1 调试 2025-12-16 19:33:18 +08:00
hahwu
1ba7b9c9be 调试 2025-12-16 19:30:27 +08:00
hahwu
fa06340cdc 调试 2025-12-16 19:28:09 +08:00
hahwu
2b17f28a32 调试 2025-12-16 19:22:17 +08:00
hahwu
87668bc9ed protobuf解析优化 2025-12-16 19:06:45 +08:00
hahwu
a3d3c2621f gm优化 2025-12-16 17:20:42 +08:00
hahwu
9db70ae389 gm优化 2025-12-16 17:20:42 +08:00
hahwu
5584876ce1 gm优化 2025-12-16 17:20:41 +08:00
hahwu
b8662c3afd 代码优化 2025-12-16 15:31:24 +08:00
hahwu
5dde9f9f9f 代码优化 2025-12-16 15:23:25 +08:00
hahwu
3466a7f0fb 协议更新 2025-12-15 18:37:38 +08:00
hahwu
89849854bb sql脚本 2025-12-15 17:08:27 +08:00
hahwu
8348e26ac4 增加NPC校验 2025-12-15 15:05:15 +08:00
hahwu
9512b7b0e0 删除旧的配置文件 2025-12-15 15:00:24 +08:00
hahwu
c1f32bc792 gm优化 2025-12-15 14:23:03 +08:00
hahwu
dc4f33b1f5 gm优化 2025-12-15 11:52:31 +08:00
hahwu
6fd2d3a039 gm优化 2025-12-15 10:16:06 +08:00
hahwu
865cc45c59 【猫草大作战】-修改回复协议 2025-12-12 17:08:51 +08:00
hahwu
03eca57bb5 【猫草大作战】-修改协议 2025-12-11 16:29:03 +08:00
hahwu
5a9b2a0fa8 【猫草大作战】-修改同意逻辑 2025-12-11 16:25:31 +08:00
hahwu
d1d63de3bf 用户详情增加宠物名字 2025-12-11 16:00:22 +08:00
hahwu
cc2fbb53d8 日志优化 2025-12-10 21:23:21 +08:00
hahwu
fde2821109 日志优化 2025-12-10 21:14:28 +08:00
hahwu
9ed4b2a6d1 日志优化 2025-12-10 20:54:28 +08:00
hahwu
e4e1b93a9a 【猫草大作战】-修改领奖逻辑 2025-12-10 18:56:57 +08:00
hahwu
b48aeea1d8 【猫草大作战】-修改返回数据 2025-12-10 17:33:50 +08:00
hahwu
029b963eb1 断线重连打点 2025-12-10 15:45:07 +08:00
hahwu
11da2fa04b Merge branch 'develop' into sdk 2025-12-10 14:46:21 +08:00
hahwu
88b1b3601e 【日志】优化 2025-12-10 12:12:07 +08:00
hahwu
2b54cd128f 【猫草大作战】-转盘逻辑bug 2025-12-10 11:59:55 +08:00
hahwu
1fc341b77b 【猫草大作战】-转盘逻辑bug 2025-12-10 10:23:56 +08:00
hahwu
2aa0ccbd58 【猫草大作战】-领奖逻辑bug修复 2025-12-09 18:48:19 +08:00
hahwu
6471ccda73 新增章节奖励接口 2025-12-09 17:56:02 +08:00
hahwu
822ae4c6ef 【猫草大作战】-领奖逻辑优化 2025-12-09 17:46:46 +08:00
hahwu
7dcd683997 【订单】-宠物订单的棋子不能是自动发射器棋子 2025-12-09 17:34:47 +08:00
hahwu
d17992a6f1 【猫草大作战】-领奖逻辑优化 2025-12-09 17:28:30 +08:00
hahwu
af72af6ec4 【猫草大作战】-接口协议优化 2025-12-09 14:17:42 +08:00
hahwu
f6a43e8a21 代码结构优化 2025-12-09 12:08:44 +08:00
hahwu
4245b7e1d1 【猫草大作战】-接口优化,取消数据刷新 2025-12-09 10:52:08 +08:00
hahwu
7a4586b1ef 代码优化 2025-12-09 10:50:42 +08:00
hahwu
f611eccec9 数据优化 2025-12-09 09:59:45 +08:00
hahwu
eb71e599a2 【猫草大作战】-优化 2025-12-08 19:04:48 +08:00
hahwu
f385a7f546 【猫草大作战】-bug优化 2025-12-08 19:03:04 +08:00
hahwu
8c5f31d7a8 【猫草大作战】-bug优化 2025-12-08 18:23:28 +08:00
hahwu
efab82709f 【猫草大作战】-邀请列表优化 2025-12-08 16:41:39 +08:00
hahwu
baf4914f9b 【猫草大作战】-协议修改 2025-12-08 16:05:57 +08:00
hahwu
9080c8870a 【猫草大作战】- 设置emoji 、单元测试 2025-12-08 15:15:55 +08:00
hahwu
b6915f5073 【猫草大作战】数据埋点 2025-12-08 14:21:22 +08:00
hahwu
a232698664 猫草大作战-修改协议 2025-12-08 12:28:02 +08:00
hahwu
58c36d9180 猫草大作战优化-抽奖赠送好友道具 2025-12-08 10:49:20 +08:00
hahwu
61f065747e 猫草大作战优化 2025-12-08 10:32:38 +08:00
hahwu
bcd385210a 猫草大作战优化 2025-12-08 10:13:37 +08:00
hahwu
259814ab90 猫草大作战 2025-12-05 19:39:40 +08:00
hahwu
5ad2b64fc4 日志优化 2025-12-05 11:12:51 +08:00
hahwu
30b758ad52 Merge branch 'develop' into sdk 2025-12-04 18:51:14 +08:00
hahwu
f4c9972fdf 协议解析错误直接返回 2025-12-04 18:49:46 +08:00
hahwu
f25fbb2d4e kv结构-数据安全校验 2025-12-04 18:18:11 +08:00
hahwu
3a10713337 kv结构-数据安全校验 2025-12-04 18:11:14 +08:00
hahwu
d9d025fa95 猫草大作战-发送表情 2025-12-04 18:07:42 +08:00
hahwu
f8363ff994 猫草大作战-好友同意优化 2025-12-04 17:54:41 +08:00
hahwu
745ebaced4 猫草大作战 2025-12-04 16:48:57 +08:00
hahwu
dc2e1d8ea4 日志优化 2025-12-04 15:32:18 +08:00
hahwu
1191168a19 日志优化,GC优化 2025-12-04 15:27:54 +08:00
hahwu
4421b9f613 日志优化 2025-12-03 18:10:53 +08:00
hahwu
8fabdd7137 订单通知优化 2025-12-02 12:25:45 +08:00
hahwu
91e5b48fe5 集群架构 2025-12-02 10:09:21 +08:00
hahwu
4dc6dcbee7 集群架构优化 2025-12-01 18:47:17 +08:00
hahwu
8c5b63a49a 集群架构优化 2025-12-01 18:45:22 +08:00
hahwu
ca4a593d69 集群架构优化 2025-12-01 15:19:06 +08:00
hahwu
200cbaec68 集群架构优化 2025-12-01 15:17:07 +08:00
hahwu
39cf03a4ab 邮件多语言 2025-12-01 11:28:39 +08:00
hahwu
c8f859d7e4 邮件优化 2025-11-28 16:21:00 +08:00
hahwu
b7f2ed2e50 登录优化 2025-11-28 16:21:00 +08:00
hahwu
2ba0f54cb4 邮件优化 2025-11-28 16:21:00 +08:00
hahwu
c518d5695e 邮件优化 2025-11-28 16:18:41 +08:00
hahwu
5a3d3f646a 登录优化 2025-11-28 16:12:04 +08:00
hahwu
aa4c4b154c 邮件优化 2025-11-28 15:43:30 +08:00
hahwu
0e425f0355 ga日志优化 2025-11-28 15:23:32 +08:00
hahwu
6d51318869 ga日志优化 2025-11-28 15:23:11 +08:00
hahwu
0ad26e04db 邮件优化 2025-11-28 12:01:20 +08:00
hahwu
14807a52ed 邮件优化 2025-11-28 11:57:55 +08:00
hahwu
ed99982d86 葡萄牙语邮件 2025-11-28 10:30:35 +08:00
hahwu
4a060b5b6f 葡萄牙语邮件 2025-11-28 10:30:19 +08:00
hahwu
881770275b 葡萄牙语邮件 2025-11-28 10:21:19 +08:00
hahwu
d85c048d8b 葡萄牙语邮件 2025-11-28 10:20:11 +08:00
hahwu
0c979e7ff1 减价不减量 新限时事件 2025-11-27 14:29:11 +08:00
hahwu
673a2d9b21 关闭旧的发货接口 2025-11-26 19:00:31 +08:00
hahwu
f1b0f96c07 关闭旧的发货接口 2025-11-26 19:00:13 +08:00
hahwu
0ac249ad30 猫咪闪促 2025-11-26 17:14:02 +08:00
hahwu
7c6a5b5497 Merge branch 'develop' into sdk 2025-11-26 14:44:34 +08:00
hahwu
b1af9103bf 限时事件 2025-11-26 14:43:36 +08:00
hahwu
cd303b5658 ga日志优化 2025-11-25 14:55:03 +08:00
hahwu
3bd4a88221 优化 2025-11-25 11:09:58 +08:00
hahwu
1dfa4cda64 优化 2025-11-21 18:54:50 +08:00
hahwu
d0aec519d1 日志写入采用异步方式 2025-11-21 17:45:42 +08:00
hahwu
edae9c0a5e 途游GA打点 2025-11-21 17:25:57 +08:00
hahwu
2ee93f2408 优化 2025-11-20 11:39:54 +08:00
hahwu
300d307b87 排行榜优化 2025-11-18 14:05:04 +08:00
hahwu
187742a6ca 竞标赛排行榜优化 2025-11-14 17:38:24 +08:00
hahwu
fe0fe15890 gm优化 2025-11-13 18:02:01 +08:00
hahwu
ee2a87988b 断线重连优化 2025-11-13 10:40:59 +08:00
hahwu
7c30d076fb 优化 2025-11-12 20:01:44 +08:00
hahwu
8e8c6d7ba8 优化 2025-11-12 20:01:31 +08:00
hahwu
7a95ef8cfb Add friend reply, sponsor, and retire reward features
Introduces friend reply and sponsor mechanics, including new message types, data structures, and reward handling. Adds chess retire reward claim logic and related protobuf messages. Updates login to support SDK login and account name changes. Refactors order shipping logic and admin shipping command. Multiple modules updated to support new friend and order features.
2025-11-12 15:54:43 +08:00
hahwu
5ab3f9847a 增加时间线 2025-11-12 15:46:52 +08:00
hahwu
57b7ac838c 充值数据优化 2025-11-12 12:11:45 +08:00
hahwu
513ec5597d 日志优化 2025-11-12 10:35:06 +08:00
hahwu
3ec6de9b1e 订单支付优化 2025-11-11 11:23:20 +08:00
hahwu
b749304e82 支付优化 2025-11-11 10:55:00 +08:00
hahwu
9b17d14437 获取体力优化 2025-11-11 10:21:42 +08:00
hahwu
8428678712 增加退役领取奖励接口 2025-11-10 15:07:19 +08:00
hahwu
2b816c3370 优化 2025-11-07 17:51:30 +08:00
hahwu
e229ab8f98 优化 2025-11-07 17:26:07 +08:00
hahwu
17689cab25 好友优化 2025-11-07 16:42:00 +08:00
hahwu
6fc48de7ae 优化 2025-11-06 17:00:35 +08:00
hahwu
20cb57a7f8 登录优化 2025-11-06 15:39:07 +08:00
hahwu
9d7bad07d1 优惠日数据刷新 2025-11-06 10:18:05 +08:00
hahwu
4eeeffa4d6 点击生成消耗品订单 2025-10-31 16:06:06 +08:00
hahwu
5df375128c 好友打招呼 2025-10-31 14:58:06 +08:00
hahwu
2922a9a149 好友信息增加猫咪生理值 2025-10-30 19:06:41 +08:00
hahwu
4c59b75e17 好友信息增加猫咪生理值 2025-10-30 19:05:27 +08:00
hahwu
5e113715c8 后台订单发货 2025-10-30 16:55:54 +08:00
hahwu
82daf12862 后台订单优化 2025-10-30 16:25:33 +08:00
hahwu
9839a1ee11 离线时间优化 2025-10-30 15:30:12 +08:00
hahwu
8860026ff1 离线时间优化 2025-10-30 15:30:00 +08:00
hahwu
17d71f499b 后台订单发货 2025-10-30 15:28:39 +08:00
hahwu
7300228870 Merge branch 'develop' into sdk 2025-10-29 16:29:56 +08:00
hahwu
013b666a75 优化 2025-10-28 16:55:46 +08:00
hahwu
522f7bb036 优化 2025-10-28 16:45:32 +08:00
hahwu
a6a3b44f94 优化 2025-10-28 16:17:17 +08:00
hahwu
31c17a7766 优化 2025-10-28 15:30:19 +08:00
hahwu
28135b26ad 优化 2025-10-28 14:47:08 +08:00
hahwu
c808db5db5 优化 2025-10-28 11:11:23 +08:00
hahwu
6b64677a12 活动日志优化 2025-10-23 11:11:18 +08:00
hahwu
f4fded038b 后台优化 2025-10-23 10:36:55 +08:00
hahwu
62f37065e0 延长支付校验时间 2025-10-23 09:53:22 +08:00
hahwu
42c88a9ffe 延长支付校验时间 2025-10-23 09:52:23 +08:00
hahwu
80fc8fa0c6 流失用户回归记录 2025-10-22 15:52:12 +08:00
hahwu
2af5bff831 优化 2025-10-22 15:48:35 +08:00
hahwu
e97c46a09f bug修复 2025-10-22 15:15:10 +08:00
hahwu
bd792c72b8 添加好友优化 2025-10-22 15:01:56 +08:00
hahwu
23c232d0d3 优化 2025-10-21 16:57:53 +08:00
hahwu
74ead0b4e6 每周优惠日结束时间 2025-10-21 16:26:13 +08:00
hahwu
7d3e299877 每周优惠日 2025-10-21 16:23:31 +08:00
hahwu
bf5cbc9d5d 充值优化 2025-10-21 15:26:50 +08:00
hahwu
4bfc5266b6 bug修复 2025-10-20 18:52:19 +08:00
hahwu
780a2150fb 活动通行证更新 2025-10-20 16:49:02 +08:00
hahwu
702d216a06 活动通行证 2025-10-20 16:36:03 +08:00
hahwu
099f76a1ce 增加协议 2025-10-20 15:26:27 +08:00
hahwu
627ee5c80b 增加协议 2025-10-20 15:23:40 +08:00
hahwu
e065a15028 协议更新 2025-10-20 15:07:16 +08:00
hahwu
502943250a 协议更新 2025-10-20 14:46:22 +08:00
hahwu
21e54e98fc 协议更新 2025-10-20 14:03:11 +08:00
hahwu
3010cffe03 宠物打工 2025-10-20 10:43:45 +08:00
hahwu
94ca75092b 宠物打工 2025-10-20 10:37:32 +08:00
hahwu
4305144472 棋子商店bug修改 2025-10-20 10:26:09 +08:00
hahwu
0d258ccf3e 增加零件GM 2025-10-17 17:24:41 +08:00
hahwu
380a005143 每周优惠 2025-10-17 15:30:43 +08:00
hahwu
120d83901a 每周优惠 2025-10-17 15:01:46 +08:00
hahwu
d997113f4c 棋子商店修复 2025-10-16 17:27:40 +08:00
hahwu
0da23102a7 棋子商店修复 2025-10-16 17:23:37 +08:00
hahwu
c631b4de19 订单优化 2025-10-16 15:39:02 +08:00
hahwu
78bfb4eb2d 订单优化 2025-10-16 15:38:46 +08:00
hahwu
67bd1ab43f Merge branch 'develop' into sdk 2025-10-16 10:47:59 +08:00
hahwu
2d68b7e2af 邮件优化 2025-10-16 10:36:07 +08:00
hahwu
f04b58b091 消耗品订单优化 2025-10-15 18:54:24 +08:00
hahwu
e038ca4ad4 消耗品订单优化 2025-10-15 18:35:34 +08:00
hahwu
551afa682a 棋子商店 2025-10-15 17:47:16 +08:00
hahwu
2149ee404b 消耗品订单优化 2025-10-14 10:46:23 +08:00
hahwu
b49d7dbec3 后台接口优化 2025-10-11 16:36:07 +08:00
hahwu
dfb54e70f7 后台接口优化 2025-10-11 14:23:10 +08:00
hahwu
0dc7af2515 动态难度修改 2025-10-11 11:06:13 +08:00
hahwu
808e3ebf1b 产物商店价格调整 2025-10-11 10:22:35 +08:00
hahwu
9ed0d1ac2c 增加打点 2025-10-10 16:14:44 +08:00
hahwu
78221562cf 增加打点 2025-10-10 16:08:14 +08:00
hahwu
9253bf9717 订单打点修复 2025-10-10 11:29:29 +08:00
hahwu
3992d23f46 订单打点修复 2025-10-10 11:29:18 +08:00
hahwu
04c33850c0 订单优化 2025-09-30 10:23:30 +08:00
hahwu
8ee4ed3e93 免广告礼包 2025-09-29 17:34:04 +08:00
hahwu
74a8ba6d1b 免广告礼包 2025-09-29 17:20:09 +08:00
hahwu
3492668580 日志优化 2025-09-29 16:23:43 +08:00
hahwu
ff78509bc0 免广告礼包 2025-09-29 16:23:29 +08:00
hahwu
b2ea831498 日志优化 2025-09-29 16:23:04 +08:00
hahwu
144d30df98 订单优化 2025-09-29 09:47:37 +08:00
hahwu
c65f10036f 订单优化 2025-09-29 09:47:26 +08:00
hahwu
9a332a845c Merge branch 'develop' into sdk 2025-09-28 15:25:06 +08:00
hahwu
6c62608929 订单优化 2025-09-28 12:25:47 +08:00
hahwu
bce5d809df 订单优化 2025-09-28 10:19:18 +08:00
hahwu
9510ea96bc 订单优化 2025-09-28 10:05:15 +08:00
hahwu
ae342334e8 订单优化 2025-09-28 09:59:09 +08:00
hahwu
61e69019b1 订单修复 2025-09-26 18:24:50 +08:00
hahwu
c883fb185e 支付校验通知 2025-09-26 17:53:29 +08:00
hahwu
ba33d77e1e 订单优化 2025-09-26 17:47:34 +08:00
hahwu
d3823bd9ca 飞书优化 2025-09-26 17:17:59 +08:00
hahwu
bf485f132c 订单bug修复 2025-09-26 15:50:06 +08:00
hahwu
e8d0c38d59 预热订单bug修复 2025-09-26 12:30:27 +08:00
hahwu
30314bc979 优化 2025-09-26 10:25:36 +08:00
hahwu
e7e59cf739 订单优化 2025-09-25 11:36:31 +08:00
hahwu
f07499aa6c 订单优化 2025-09-24 19:08:23 +08:00
hahwu
4566a4fa4b 订单优化 2025-09-24 14:46:55 +08:00
hahwu
1886d56013 宠物宝藏bug修复 2025-09-23 18:13:53 +08:00
hahwu
1b259919f7 订单优化 2025-09-23 17:56:18 +08:00
hahwu
66a40023e6 订单优化 2025-09-23 17:31:32 +08:00
hahwu
1da1ddf48a 自动订单优化 2025-09-23 14:57:51 +08:00
hahwu
ddc051eebc 订单修改 2025-09-23 12:09:02 +08:00
hahwu
3b9eba9a48 当装饰品订单完成之后,将自动发射器产物重新纳入自动订单 2025-09-23 10:50:08 +08:00
hahwu
153493bd40 订单bug修复 2025-09-22 16:35:09 +08:00
hahwu
0c4104de41 订单bug修复 2025-09-22 15:54:12 +08:00
hahwu
6c78877c6a 订单bug修复 2025-09-22 14:12:48 +08:00
hahwu
dddf7d3953 订单价格修改 2025-09-22 10:56:35 +08:00
hahwu
e11c24cc34 订单优化 2025-09-19 18:21:34 +08:00
hahwu
1dcc9e2154 棋子商店bug修复 2025-09-19 17:32:44 +08:00
hahwu
f14a49e072 棋子商店bug修复 2025-09-19 17:32:16 +08:00
hahwu
849a75709c 订单bug修复 2025-09-19 16:23:18 +08:00
hahwu
237a4c8d61 bug修复 2025-09-19 15:11:35 +08:00
hahwu
2b427c7f23 bug修复 2025-09-19 15:11:12 +08:00
hahwu
dec81f8877 打点优化 2025-09-19 12:26:15 +08:00
hahwu
95a4c869ff 日志打点 2025-09-19 12:09:39 +08:00
hahwu
c1137f6a72 打点优化 2025-09-19 10:53:58 +08:00
hahwu
687437dcb4 订单优化 2025-09-19 09:46:37 +08:00
hahwu
30a9d6c6eb 订单优化 2025-09-18 19:19:32 +08:00
hahwu
f064cda204 订单优化 2025-09-18 19:09:14 +08:00
hahwu
d4a7447ae9 订单优化 2025-09-18 19:00:21 +08:00
hahwu
fef69f6edb 订单bug修复 2025-09-18 18:23:01 +08:00
hahwu
e5cd774cc8 订单优化 2025-09-18 17:06:33 +08:00
hahwu
c87b55d74b bug修复 2025-09-18 16:26:49 +08:00
hahwu
103b70d5e8 playroom修复 2025-09-18 16:23:44 +08:00
hahwu
af7afadf29 订单优化 2025-09-18 16:10:04 +08:00
hahwu
7c43add304 订单优化 2025-09-18 16:07:10 +08:00
hahwu
e65861c49f 订单优化 2025-09-18 15:29:08 +08:00
hahwu
a245a5e33d 零件订单bug修复 2025-09-17 16:55:11 +08:00
hahwu
ac9472d22c playroom装饰修复 2025-09-17 16:38:06 +08:00
hahwu
21cbfd6d82 playroom装饰修复 2025-09-17 16:37:20 +08:00
hahwu
b189bd633c 时间线 2025-09-17 15:02:00 +08:00
hahwu
0108996dd4 气泡 2025-09-17 14:27:38 +08:00
hahwu
149dfb9562 订单优化 2025-09-17 11:58:00 +08:00
hahwu
2960a87c58 日志打点 2025-09-17 11:58:00 +08:00
hahwu
3aa1c3543e 订单优化 2025-09-17 11:53:18 +08:00
hahwu
119b26b3d9 气泡 2025-09-17 11:02:14 +08:00
hahwu
d80567c7b7 气泡 2025-09-17 10:58:16 +08:00
hahwu
ae5a2e5732 日志打点 2025-09-17 10:50:43 +08:00
hahwu
ed98fa7082 订单优化 2025-09-16 18:57:42 +08:00
hahwu
5b2813d875 订单优化 2025-09-16 18:55:42 +08:00
hahwu
1d0a1f17b4 订单优化 2025-09-16 18:51:37 +08:00
hahwu
48ebc10c8e 订单优化 2025-09-16 18:51:13 +08:00
hahwu
00f6d544d8 订单优化 2025-09-16 18:46:32 +08:00
hahwu
9fddde1ec3 订单优化 2025-09-16 18:46:17 +08:00
hahwu
17bd48f9fd 时间线气泡 2025-09-16 17:09:33 +08:00
hahwu
51bed78e8b 订单逻辑优化 2025-09-16 17:07:25 +08:00
hahwu
e0f5d67ee6 订单逻辑优化 2025-09-16 17:07:06 +08:00
hahwu
a1518fd186 订单异常报警 2025-09-16 14:20:10 +08:00
hahwu
169078a545 增加时间线:参与好友寻宝 2025-09-16 14:17:24 +08:00
hahwu
9ca619c75c 订单异常报警 2025-09-16 12:13:04 +08:00
hahwu
c869bfbec2 订单异常报警 2025-09-16 11:34:03 +08:00
hahwu
81b48f0776 订单异常报警 2025-09-16 11:33:45 +08:00
hahwu
c8b48541e1 订单优化 2025-09-15 10:37:47 +08:00
hahwu
f17090d7f8 Merge branch 'develop' into sdk 2025-09-15 10:19:55 +08:00
hahwu
e43b18b34a 订单优化 2025-09-15 10:19:08 +08:00
hahwu
ec9ba10b6e 优化 2025-09-12 14:21:57 +08:00
hahwu
72ad6c5893 优化 2025-09-12 14:21:44 +08:00
hahwu
8be09db21c 优化 2025-09-12 11:04:11 +08:00
hahwu
c38df53518 bug修复 2025-09-11 23:21:48 +08:00
hahwu
c4bf1e6a8b bug修复 2025-09-11 23:16:10 +08:00
hahwu
c74c2465ea 补偿邮件 2025-09-11 23:04:14 +08:00
hahwu
35b6f72d30 补偿邮件 2025-09-11 23:03:33 +08:00
hahwu
c8d88cc5f4 订单优化 2025-09-11 22:50:58 +08:00
hahwu
641a0a330b 生成自动发射器满级订单时,不在生成对应产物的订单 2025-09-11 22:33:08 +08:00
hahwu
35d3fb5e81 生成自动发射器满级订单时,不在生成对应产物的订单 2025-09-11 22:19:41 +08:00
hahwu
8fde7e5e45 棋子商店优化 2025-09-11 20:39:55 +08:00
hahwu
1697536614 棋子商店优化 2025-09-11 20:39:55 +08:00
hahwu
05dd4e3ab7 棋子商店优化 2025-09-11 20:39:27 +08:00
hahwu
a3665587e4 棋子商店优化 2025-09-11 20:37:16 +08:00
hahwu
3f28a4852f 优化 2025-09-11 18:11:33 +08:00
hahwu
c3da735fe3 优化 2025-09-11 18:08:53 +08:00
hahwu
eebc7bcdce gm优化 2025-09-11 17:58:57 +08:00
hahwu
dfa3142b19 gm优化 2025-09-11 17:58:38 +08:00
hahwu
3077a1e0d4 无尽礼包优化 2025-09-11 17:54:43 +08:00
hahwu
4512648ae7 无尽礼包优化 2025-09-11 17:54:23 +08:00
hahwu
d80d7adce5 发射器退隐优化 2025-09-11 16:02:48 +08:00
hahwu
25037b86d2 发射器退隐优化 2025-09-11 15:59:05 +08:00
hahwu
8ba6a9c5bc 装饰bug修复 2025-09-11 15:01:56 +08:00
hahwu
94ea5b5751 装饰bug修复 2025-09-11 15:01:44 +08:00
hahwu
5ecdfd81bd 订单优化 2025-09-11 14:13:24 +08:00
hahwu
1dcaa848a1 订单优化 2025-09-11 14:10:51 +08:00
hahwu
57e3ecdcc5 订单优化 2025-09-11 11:39:10 +08:00
hahwu
1344325fcc 订单优化 2025-09-11 11:38:42 +08:00
hahwu
6f5068802a 优化 2025-09-11 11:04:19 +08:00
hahwu
938d586ebf 优化 2025-09-11 11:04:03 +08:00
hahwu
daff3f15a1 订单优化 2025-09-11 10:59:39 +08:00
hahwu
0b3484a7d8 订单优化 2025-09-11 10:59:20 +08:00
hahwu
2a3d38f0f5 订单优化 2025-09-11 10:42:31 +08:00
hahwu
55b80c9135 订单优化 2025-09-11 10:42:18 +08:00
hahwu
d432d5ffb7 订单修复 2025-09-10 19:08:50 +08:00
hahwu
9317aea2e3 订单修复 2025-09-10 19:08:29 +08:00
hahwu
558e443427 增加道具类型114 2025-09-10 18:37:56 +08:00
hahwu
439bebd6ec 增加道具类型114 2025-09-10 18:31:10 +08:00
hahwu
f45425b019 订单优化 2025-09-10 17:01:57 +08:00
hahwu
acfcd08d38 订单优化 2025-09-10 16:57:45 +08:00
hahwu
b6d5cbd5e2 新手任务bug修复 2025-09-10 14:21:52 +08:00
hahwu
96e4b63f63 新手任务bug修复 2025-09-10 14:18:35 +08:00
hahwu
fc987795a8 一键建造修改 2025-09-10 12:14:15 +08:00
hahwu
f91ae551a0 一键建造修改 2025-09-10 12:13:28 +08:00
hahwu
e6a9792b4a 订单难度调整 2025-09-10 11:31:12 +08:00
hahwu
871d2c564a 订单难度调整 2025-09-10 11:30:58 +08:00
hahwu
e6b1bbd127 20250910补偿 2025-09-10 11:19:23 +08:00
hahwu
494089834e 20250910补偿 2025-09-10 11:19:09 +08:00
hahwu
44eca04927 20250910补偿 2025-09-10 11:11:54 +08:00
hahwu
f28a9e7454 20250910补偿 2025-09-10 11:11:38 +08:00
hahwu
cf114eceac 订单bug修复 2025-09-10 10:41:30 +08:00
hahwu
786c5d941d 订单bug修复 2025-09-10 10:36:29 +08:00
hahwu
3d8542fae5 Merge branch 'develop' into sdk 2025-09-10 10:23:24 +08:00
hahwu
829a26dce0 测试 2025-09-10 10:20:14 +08:00
hahwu
41fe5c1c2c 拜访优化 2025-09-09 18:09:40 +08:00
hahwu
40becaa198 装饰修复 2025-09-09 18:00:51 +08:00
hahwu
416a1067da 修复 2025-09-09 16:55:04 +08:00
hahwu
30e11d1b50 优化 2025-09-09 15:23:57 +08:00
hahwu
302420b724 优化 2025-09-09 15:20:21 +08:00
hahwu
35499f2177 优化 2025-09-09 11:44:33 +08:00
hahwu
cca8a27f9a 订单优化 2025-09-09 11:23:33 +08:00
hahwu
dda545ad0d 优化 2025-09-08 17:16:53 +08:00
hahwu
cf4b4370e0 调试 2025-09-08 16:30:51 +08:00
hahwu
273d32d658 优化 2025-09-08 15:53:41 +08:00
hahwu
51246def30 新手任务优化 2025-09-08 15:31:24 +08:00
hahwu
53a5f0693f 优化 2025-09-08 15:08:58 +08:00
hahwu
66ca58f075 初始装饰零件写死 2025-09-08 14:38:01 +08:00
hahwu
3d9e697c04 增加GM 2025-09-08 12:14:10 +08:00
hahwu
475be1e5ae 增加GM 2025-09-08 11:52:19 +08:00
hahwu
e53a023276 增加满级产物订单 2025-09-08 11:46:49 +08:00
hahwu
56fbd7ad3f 发射器条件修改 2025-09-08 11:40:50 +08:00
hahwu
91e9ebaea0 取消免费体力 2025-09-05 18:06:59 +08:00
hahwu
6f26efa8a5 优化 2025-09-05 10:04:47 +08:00
hahwu
d4104881c0 订单优化 2025-09-04 15:18:37 +08:00
hahwu
08a6960c9d 订单优化 2025-09-04 15:04:19 +08:00
hahwu
0d649cc9b5 订单优化 2025-09-04 14:35:58 +08:00
hahwu
ecc3aec6af 新订单类型 2025-09-04 12:25:26 +08:00
hahwu
1602e3ded8 订单修复 2025-09-04 12:23:27 +08:00
hahwu
6ee551123f 修复 2025-09-03 18:20:36 +08:00
hahwu
2b601c6d52 零件消耗优化 2025-09-03 17:51:36 +08:00
hahwu
2d3c42bcb5 bug修复 2025-09-03 16:22:16 +08:00
hahwu
40f6fe40cd 优化 2025-09-03 16:07:48 +08:00
hahwu
445cee80f6 棋子商店修改 2025-09-03 14:49:53 +08:00
hahwu
cb4235baf7 订单bug修复 2025-09-03 09:54:05 +08:00
hahwu
14fd275cd9 装饰零件 2025-09-02 16:51:47 +08:00
hahwu
72edb784fd 订单优化 2025-09-02 15:41:54 +08:00
hahwu
d64fe56c22 优化 2025-09-02 12:10:31 +08:00
hahwu
9fec847b36 订单逻辑修改 2025-09-01 16:11:19 +08:00
hahwu
3759505847 修复 2025-09-01 15:30:23 +08:00
hahwu
2bd18208d4 优化 2025-08-29 16:35:21 +08:00
hahwu
de8777777b 优化 2025-08-29 16:04:08 +08:00
hahwu
f54d558db5 优化 2025-08-29 15:59:57 +08:00
hahwu
31ed67dbed 任务优化 2025-08-29 15:32:41 +08:00
hahwu
c3feafc38c 钻石消耗任务 2025-08-29 15:05:45 +08:00
hahwu
d5470cd69e 优化 2025-08-29 12:03:45 +08:00
hahwu
58be1191c5 playroom拜访解锁等级 2025-08-29 10:24:54 +08:00
hahwu
4c9ebf13bb 棋子商店 2025-08-28 15:56:15 +08:00
hahwu
9e64882298 优化 2025-08-28 11:43:56 +08:00
hahwu
4d4611fb04 发射器退役 2025-08-28 11:39:17 +08:00
hahwu
c1c30dc936 固定订单优化 2025-08-28 11:09:51 +08:00
hahwu
c869a2a3d5 装饰优化 2025-08-27 14:26:33 +08:00
hahwu
754f52febf 订单bug修复 2025-08-27 14:21:00 +08:00
hahwu
3d19b8ba7f 订单优化 2025-08-27 12:08:00 +08:00
hahwu
c7f6ab790b 新手任务中的登录任务特殊处理 2025-08-27 11:32:11 +08:00
hahwu
0721c28dfc 订单优化 2025-08-27 11:22:16 +08:00
hahwu
9bd9ca7fd8 修复 2025-08-26 17:56:27 +08:00
hahwu
bc23153c40 bug修复 2025-08-26 17:26:26 +08:00
hahwu
25a9032e6d 装饰优化 2025-08-26 17:21:39 +08:00
hahwu
e00a2292ae 固定订单,新手任务 2025-08-26 11:59:12 +08:00
hahwu
2446ba6a23 固定订单 2025-08-26 11:36:36 +08:00
hahwu
bedc0fdbdc 新手任务 2025-08-26 10:27:15 +08:00
hahwu
787041b6ec 新手任务 2025-08-26 09:58:06 +08:00
hahwu
9dde6ce4a7 每日任务 2025-08-25 17:49:10 +08:00
hahwu
4bd9ecdec2 新手任务 2025-08-25 16:13:43 +08:00
hahwu
50036af6a1 新手任务 2025-08-25 16:10:39 +08:00
hahwu
810067c37a 新手任务 2025-08-25 11:42:09 +08:00
hahwu
d8e361ccc2 零件协议 2025-08-22 16:47:13 +08:00
hahwu
38241235a3 Merge branch 'develop' into sdk 2025-08-21 15:00:49 +08:00
hahwu
9d44143c90 bug优化 2025-08-21 14:59:24 +08:00
hahwu
daccee5ba4 订单bug优化 2025-08-20 18:21:01 +08:00
hahwu
dfe2931ab2 优化 2025-08-20 16:27:20 +08:00
hahwu
03d584c12c 猫咪怪盗优化 2025-08-20 15:55:18 +08:00
hahwu
d84266817f Gm优化 2025-08-20 15:27:01 +08:00
hahwu
4f24b30ba8 宠物怪盗优化 2025-08-19 15:43:37 +08:00
hahwu
c65ba4d019 回收日bug修复 2025-08-18 17:11:15 +08:00
hahwu
595265d389 日志优化 2025-08-18 16:41:17 +08:00
hahwu
0b6b730ae5 日志优化 2025-08-18 16:33:53 +08:00
hahwu
0e526cadfd playroom优化 2025-08-15 17:58:04 +08:00
hahwu
3cfef77824 playroom优化 2025-08-15 17:42:24 +08:00
hahwu
58729f6500 playroom优化 2025-08-15 17:21:13 +08:00
hahwu
5d61834398 优化 2025-08-15 15:51:40 +08:00
hahwu
0f535a2aea playroom优化 2025-08-15 11:31:08 +08:00
hahwu
e79d7b6729 playroom优化 2025-08-15 11:24:00 +08:00
hahwu
bf34ec773a 优化 2025-08-15 10:11:56 +08:00
hahwu
743445b64a Merge branch 'develop' into sdk 2025-08-13 10:24:07 +08:00
hahwu
6679f1638b 日志优化 2025-08-11 15:55:07 +08:00
hahwu
f8d4ee070c 日志优化 2025-08-11 15:52:43 +08:00
hahwu
b4871dba2e 开包bug修复 2025-08-04 10:48:16 +08:00
hahwu
8c002f7092 开包bug修复 2025-08-04 10:45:53 +08:00
hahwu
8e0947b054 优化 2025-07-30 20:32:12 +08:00
hahwu
11dcb3fb7e 优化 2025-07-30 20:32:03 +08:00
hahwu
bf35c7ec87 Revert "禁用时间线新功能"
This reverts commit e9b22196a3.
2025-07-30 17:18:47 +08:00
hahwu
014f155980 Merge branch 'develop' into sdk 2025-07-30 16:11:34 +08:00
hahwu
3853663616 封号功能 2025-07-30 15:40:45 +08:00
hahwu
0b3961adc4 封号功能 2025-07-30 15:33:55 +08:00
hahwu
9faaaf86e2 封号功能 2025-07-30 15:14:59 +08:00
hahwu
e9b22196a3 禁用时间线新功能 2025-07-30 15:08:55 +08:00
hahwu
ce2a139da2 Merge branch 'develop' into sdk 2025-07-30 15:07:20 +08:00
hahwu
1afbf01a1a 封号功能 2025-07-30 15:05:22 +08:00
hahwu
da8f73020f 禁用GM 2025-07-30 15:04:48 +08:00
hahwu
618fc63604 封号优化 2025-07-30 15:02:37 +08:00
hahwu
c9b6c86139 封号功能 2025-07-30 12:03:51 +08:00
hahwu
35a6ae35ee 禁用GM 2025-07-30 11:48:47 +08:00
hahwu
18d6797e56 时间线气泡优化 2025-07-30 11:42:04 +08:00
hahwu
04d3fc4952 优化 2025-07-30 11:16:31 +08:00
hahwu
de6aa4db37 优化 2025-07-30 11:16:20 +08:00
hahwu
c797acb1d4 修复 2025-07-30 11:12:45 +08:00
hahwu
4b5788d8a3 修复 2025-07-30 11:12:23 +08:00
hahwu
47d4112999 优化时间线奖励 2025-07-30 11:01:23 +08:00
hahwu
30a5e9e86a Merge branch 'develop' into sdk 2025-07-30 10:45:10 +08:00
hahwu
659e9dc135 优化初始资源 2025-07-30 10:40:45 +08:00
hahwu
cd687ee1e2 猫咪戏法修改 2025-07-29 18:46:23 +08:00
hahwu
6118aba235 猫咪戏法修改 2025-07-29 16:32:56 +08:00
hahwu
fd246374da 卡牌优化 2025-07-29 15:55:03 +08:00
hahwu
fcca991555 卡牌优化 2025-07-29 15:30:12 +08:00
hahwu
d704fa62cf gm优化 2025-07-29 10:24:29 +08:00
hahwu
dce5b214ba gm优化 2025-07-29 10:24:04 +08:00
hahwu
5f214e2cb1 playroom优化 2025-07-28 12:24:40 +08:00
hahwu
265038b8cb playroom优化 2025-07-28 10:38:24 +08:00
hahwu
292bb2de65 禁用GM指令 2025-07-28 10:00:03 +08:00
hahwu
34ab97fb30 禁用GM指令 2025-07-28 09:59:35 +08:00
hahwu
0a4ad94306 增加playroom广告观看次数限制 2025-07-25 18:22:34 +08:00
hahwu
6ca18ac8f4 增加黑名单 2025-07-25 15:43:11 +08:00
hahwu
d836d785b4 猫草大作战 2025-07-24 12:08:49 +08:00
hahwu
de62790ff4 playroombug修复 2025-07-23 17:37:27 +08:00
hahwu
d42963777e playroombug修复 2025-07-23 17:37:09 +08:00
hahwu
56874ec22c 修复playroom转盘bug 2025-07-23 17:00:11 +08:00
hahwu
9cf0b15ec3 修复playroom转盘bug 2025-07-23 16:53:37 +08:00
hahwu
7c23a004c0 修复playroom普通奖励 2025-07-23 16:44:39 +08:00
hahwu
18b0eb88b1 修复playroom普通奖励 2025-07-23 16:44:29 +08:00
hahwu
94d31c2cab playroom装扮任务优化 2025-07-23 16:17:26 +08:00
hahwu
bab980b508 playroom装扮任务优化 2025-07-23 16:16:28 +08:00
hahwu
037142bb0a 修复playroom转盘bug 2025-07-23 15:46:32 +08:00
hahwu
87c715cb84 修复playroom转盘bug 2025-07-23 15:46:18 +08:00
hahwu
abe849d68c Merge branch 'develop' into sdk 2025-07-23 11:52:58 +08:00
hahwu
04a9ca69c9 修改转盘奖励 2025-07-22 15:40:29 +08:00
hahwu
489a1ed7bc 限时事件修复 2025-07-22 15:31:19 +08:00
hahwu
22ce441bcc 信息修复 2025-07-22 10:09:42 +08:00
hahwu
54e283ed06 邮件修复 2025-07-18 18:50:15 +08:00
hahwu
3fbf00c41c debug 2025-07-18 18:46:36 +08:00
hahwu
af28ca49a5 debug 2025-07-18 18:43:13 +08:00
hahwu
c490034238 后台优化 2025-07-18 18:21:24 +08:00
hahwu
4ef8aa86dc 猫草大作战 2025-07-18 18:07:36 +08:00
hahwu
45f3e27de2 头像优化 2025-07-18 11:18:11 +08:00
hahwu
a18ed72132 小猪存钱修复 2025-07-17 16:43:53 +08:00
hahwu
6239e6a0ff 修复触发邮件 2025-07-17 16:43:53 +08:00
hahwu
5155c77997 小猪存钱修复 2025-07-17 16:43:35 +08:00
hahwu
c513cb0255 修复触发邮件 2025-07-17 16:36:48 +08:00
hahwu
414b785007 修复触发邮件 2025-07-17 16:36:37 +08:00
hahwu
cdce97d0cf Revert "增加定时同步"
This reverts commit 0ea9f60a3c.
2025-07-17 16:04:57 +08:00
hahwu
cbeac8fd2a Revert "增加定时同步"
This reverts commit 0bab2987ed.
2025-07-17 16:04:45 +08:00
hahwu
0bab2987ed 增加定时同步 2025-07-17 16:03:05 +08:00
hahwu
0ea9f60a3c 增加定时同步 2025-07-17 16:02:23 +08:00
hahwu
55f44293a2 playroom优化 2025-07-17 15:37:06 +08:00
hahwu
e4dd46c0f7 playroom优化 2025-07-17 15:36:56 +08:00
hahwu
099d7793f0 Merge branch 'develop' into sdk 2025-07-17 15:08:28 +08:00
hahwu
6852df7484 后台优化 2025-07-17 14:58:06 +08:00
hahwu
e9ed6f0228 后台优化 2025-07-17 14:55:21 +08:00
hahwu
667f81f99d 后台优化 2025-07-17 14:52:45 +08:00
hahwu
da8eefca45 后台优化 2025-07-17 14:30:02 +08:00
hahwu
e05fa5fbcf 后台优化 2025-07-17 14:19:09 +08:00
hahwu
8d0b43cb11 Merge branch 'develop' into sdk 2025-07-17 10:10:35 +08:00
hahwu
d39d1d206c playroom优化 2025-07-17 10:05:59 +08:00
hahwu
0e327d9373 playroom优化 2025-07-17 09:40:48 +08:00
hahwu
a3e70e6bf1 补偿优化 2025-07-16 19:29:53 +08:00
hahwu
42e908a36a playroom优化 2025-07-16 18:57:29 +08:00
hahwu
170a30be23 优化 2025-07-16 18:55:40 +08:00
hahwu
7a2f4de850 playroom优化 2025-07-16 18:37:12 +08:00
hahwu
f125c77a0d 优化 2025-07-16 17:25:52 +08:00
hahwu
22efb95048 优化 2025-07-16 17:23:00 +08:00
hahwu
a3d1d25bd1 Gm修复 2025-07-16 17:21:42 +08:00
hahwu
e402917a85 优化 2025-07-16 17:13:32 +08:00
hahwu
8a1461d4af 邮件补偿20250717 2025-07-16 16:40:42 +08:00
hahwu
cd05ef1935 邮件补偿20250717 2025-07-16 16:19:49 +08:00
hahwu
e4abe4ead6 playroom优化 2025-07-16 11:55:28 +08:00
hahwu
59ec90f4ba playroom表情 2025-07-15 18:39:53 +08:00
hahwu
c58c35c9ed 优化日志 2025-07-15 16:42:57 +08:00
hahwu
f3d7b5c5a5 playroom拜访增加限制 2025-07-15 09:43:14 +08:00
hahwu
b142411fc5 好友邀请码 2025-07-14 17:01:29 +08:00
hahwu
0765eaf13c playroom优化 2025-07-14 16:19:36 +08:00
hahwu
6f7280e8b5 好友邀请码 2025-07-14 16:13:22 +08:00
hahwu
993bf4c40f 优化 2025-07-11 20:55:10 +08:00
hahwu
f7d7278f33 优化 2025-07-11 20:55:10 +08:00
hahwu
7fedf2e87d 优化 2025-07-11 20:55:10 +08:00
hahwu
1d22f2052e 优化 2025-07-11 17:58:44 +08:00
hahwu
12a8e16d0a 幸运猫优化 2025-07-11 17:06:16 +08:00
hahwu
83309a3c83 幸运猫优化 2025-07-11 16:50:07 +08:00
hahwu
2b73b7c6de Merge branch 'develop' into sdk 2025-07-11 11:56:03 +08:00
hahwu
642703bc6a 不能申请自己为好友 2025-07-10 18:30:49 +08:00
hahwu
da2ae614f3 限时事件优化 2025-07-10 11:31:15 +08:00
hahwu
e8b91b8dd9 优化 2025-07-09 17:56:12 +08:00
hahwu
6ba72333d2 Merge branch 'develop' into sdk 2025-07-08 18:38:33 +08:00
hahwu
a77015015e playroom优化 2025-07-08 18:21:40 +08:00
hahwu
054cbdf1e8 playroom过滤今天已拜访过的好友 2025-07-08 17:57:32 +08:00
hahwu
1714e9d27e playroom优化 2025-07-08 17:33:19 +08:00
hahwu
d77a9f7e43 playroom推荐优化 2025-07-08 17:09:31 +08:00
hahwu
2677c1c7bc 类型优化 2025-07-07 14:55:49 +08:00
hahwu
5df6d959d8 playroom优化 2025-07-07 14:40:23 +08:00
hahwu
853e7775f9 装饰套装 2025-07-07 11:07:30 +08:00
hahwu
82ec411046 订单优化 2025-07-07 10:58:29 +08:00
hahwu
0059a97122 限时事件优化 2025-07-04 19:15:43 +08:00
hahwu
ca0581553c 限时事件优化 2025-07-04 19:15:31 +08:00
hahwu
90ee76bbce Merge branch 'develop' into sdk 2025-07-04 18:47:07 +08:00
hahwu
eaa8a32b00 playroom优化 2025-07-04 10:36:12 +08:00
hahwu
97c2c06c9a 好友搜索优化 2025-07-04 09:46:04 +08:00
hahwu
12390f9abb Merge branch 'develop' into sdk 2025-07-03 18:59:58 +08:00
hahwu
4972439ad6 时间优化 2025-07-03 18:32:09 +08:00
hahwu
a6493487c4 日志记录 2025-07-03 17:18:19 +08:00
hahwu
e8bce4c167 卡牌优化 2025-07-03 17:04:08 +08:00
hahwu
01c8479ad8 装饰优化 2025-07-03 14:34:49 +08:00
hahwu
c5f974a39d 时间线优化 2025-07-02 15:24:29 +08:00
hahwu
989f68174a 时间线优化 2025-07-02 11:40:52 +08:00
hahwu
9d3f3bf6d7 特惠礼包优化 2025-07-02 10:54:30 +08:00
hahwu
b59093dfbb 优化 2025-07-01 16:50:40 +08:00
hahwu
c2f8b7d9e4 增加调试GM 2025-07-01 16:47:23 +08:00
hahwu
673026b2ba 增加调试GM 2025-07-01 16:45:23 +08:00
hahwu
b6d7913727 优化 2025-07-01 16:42:10 +08:00
hahwu
1cc633b5ac 邀请好友界面,Lily已经是玩家好友了,视为获得了已邀请了一位好友(后续添加其他NPC好友不计数) 2025-07-01 14:47:15 +08:00
hahwu
0c56cbd224 宠物宝藏优化 2025-07-01 12:17:29 +08:00
hahwu
02b7884357 订单优化 2025-07-01 11:27:25 +08:00
hahwu
d79476b0d2 排行榜优化 2025-07-01 10:21:42 +08:00
hahwu
505f9a0dd6 优化排行榜 2025-06-30 18:50:41 +08:00
hahwu
12764c1070 特惠礼包修复 2025-06-30 17:08:02 +08:00
hahwu
53b065dd38 卡牌交换优化 2025-06-30 15:49:51 +08:00
hahwu
9f69ebb15d 好友搜索优化 2025-06-30 15:27:42 +08:00
hahwu
c69e6dfced 校验修复 2025-06-30 12:08:36 +08:00
hahwu
c19a82f119 校验修复 2025-06-30 12:08:18 +08:00
hahwu
2ef0238730 卡牌优化 2025-06-30 11:07:19 +08:00
hahwu
8916d8489d 卡牌优化 2025-06-30 11:06:51 +08:00
hahwu
a34ad81edb Merge branch 'develop' into sdk 2025-06-27 19:26:22 +08:00
hahwu
d96eaad462 日志优化,增加30条上限 2025-06-27 19:25:56 +08:00
hahwu
bb826fdf47 日志优化,增加30条上限 2025-06-27 19:23:08 +08:00
hahwu
4fee81001b 时间线刷新 2025-06-27 18:12:24 +08:00
hahwu
2af6165372 时间线刷新 2025-06-27 17:56:41 +08:00
hahwu
ee22dd9770 时间线优化 2025-06-27 17:52:50 +08:00
hahwu
8817b293ea Merge branch 'develop' into sdk 2025-06-27 17:20:43 +08:00
hahwu
a51e37490b 验证码登录优化 2025-06-27 17:19:53 +08:00
hahwu
0fb9c56de9 playroom优化 2025-06-27 17:10:37 +08:00
hahwu
39df9df173 每日任务优化 2025-06-27 15:39:40 +08:00
hahwu
2fe1c75c4e 每日任务优化 2025-06-27 15:39:12 +08:00
hahwu
7b086e99d2 退役订单优化 2025-06-27 12:10:44 +08:00
hahwu
c2218c245b 退役订单优化 2025-06-27 12:10:29 +08:00
hahwu
9db4aaa806 优化 2025-06-25 18:29:05 +08:00
hahwu
87d2f34f58 Merge branch 'develop' into sdk 2025-06-25 09:49:04 +08:00
hahwu
cc3c693dc8 playroom优化 2025-06-24 18:39:33 +08:00
hahwu
acf3a58515 优化 2025-06-24 17:20:46 +08:00
hahwu
e0ea39d02c 订单修复 2025-06-24 15:36:57 +08:00
hahwu
23a60155c0 验证码优化 2025-06-24 11:25:43 +08:00
hahwu
f3d088fa4d 验证码测试 2025-06-24 09:40:44 +08:00
hahwu
1870c4979d 验证码测试 2025-06-23 11:19:11 +08:00
hahwu
b6f28d7266 充值记录优化 2025-06-23 10:12:19 +08:00
hahwu
a574dabfe9 日常任务在首日特殊任务结束之后才开启计数 2025-06-23 09:58:41 +08:00
hahwu
3f6ea08963 宠物宝藏优化 2025-06-23 09:48:19 +08:00
hahwu
c1bc68bc43 优化 2025-06-19 17:38:39 +08:00
hahwu
a1a5d735d4 登录优化 2025-06-19 12:29:29 +08:00
hahwu
52e1427b08 修改宠物怪盗优化 2025-06-19 10:47:38 +08:00
hahwu
c6b40c145c 验证码登录 2025-06-18 15:25:50 +08:00
hahwu
64531f94f4 增加身份证校验 2025-06-18 14:12:25 +08:00
hahwu
9ffd430ae3 增加场景奖励 2025-06-17 16:36:41 +08:00
hahwu
f6838d7bb9 卡牌优化 2025-06-17 11:35:11 +08:00
hahwu
6f36ee8920 bug修复 2025-06-17 11:08:05 +08:00
hahwu
031d22d195 kafka修复 2025-06-17 11:00:53 +08:00
hahwu
72b4d08d93 增加了使用设别号登录 2025-06-17 10:24:10 +08:00
hahwu
97350e8bde 补充登录和身份证校验逻辑 2025-06-16 10:37:41 +08:00
hahwu
9326a2a75c playroom优化 2025-06-13 17:42:15 +08:00
hahwu
230b53e990 playroom优化 2025-06-11 17:23:27 +08:00
hahwu
90bf326cf1 订单优化 2025-06-11 16:56:18 +08:00
hahwu
3fb89c3947 playroom优化 2025-06-11 16:28:56 +08:00
hahwu
4212d46663 kafka优化 2025-06-11 15:59:01 +08:00
hahwu
1976d60e35 优化 2025-06-11 15:58:44 +08:00
hahwu
6722304746 bug修复 2025-06-11 15:25:47 +08:00
hahwu
7bd781f032 bug修复 2025-06-11 14:54:20 +08:00
hahwu
43a27e9eba 日志优化 2025-06-11 14:29:14 +08:00
hahwu
db9fb4ad85 增加GM命令 2025-06-11 11:29:43 +08:00
hahwu
61b4fe2c4e bug修复 2025-06-10 09:58:09 +08:00
hahwu
5ebb0daa01 订单优化 2025-06-06 19:24:49 +08:00
hahwu
6b1da9b2fb 订单优化 2025-06-06 18:43:37 +08:00
hahwu
2754c5b787 限时事件优化 2025-06-06 18:02:20 +08:00
hahwu
41531e52fb 限时事件优化 2025-06-06 15:55:22 +08:00
hahwu
bc1e788bd9 限时事件优化 2025-06-06 11:24:38 +08:00
hahwu
1145fd63ba playroom优化 2025-06-05 17:43:42 +08:00
hahwu
5bc2d37de3 playroom优化 2025-06-05 17:34:02 +08:00
hahwu
7f034659e3 playroom优化 2025-06-05 17:26:46 +08:00
hahwu
61d865d6c5 优化 2025-06-05 17:17:13 +08:00
hahwu
dd5e04ed9f 拜访点数 2025-06-05 11:07:49 +08:00
hahwu
ca7becb3d4 拜访点数 2025-06-05 11:06:59 +08:00
hahwu
f2517b95bd playroom优化 2025-06-05 10:34:05 +08:00
hahwu
d881f0e679 playroom优化 2025-06-05 10:19:31 +08:00
hahwu
bda7e96e30 bug修复 2025-06-04 19:00:12 +08:00
hahwu
3ac2336e76 登录天数bug修复 2025-06-04 18:57:25 +08:00
hahwu
5b2f8552e8 增加登录天数 2025-06-03 14:58:15 +08:00
hahwu
0be7aa3422 装饰bug修复 2025-06-03 11:50:07 +08:00
hahwu
0896004185 装饰增加宠物经验 2025-06-03 11:23:53 +08:00
hahwu
af29896e76 装饰增加宠物经验 2025-06-03 10:21:53 +08:00
hahwu
4e50670358 订单优化 2025-05-30 19:10:38 +08:00
hahwu
7b9bf73b1a 邮件优化 2025-05-30 18:58:13 +08:00
hahwu
bde44a8a6d 宠物订单优化 2025-05-30 18:41:54 +08:00
hahwu
e7971ae3f6 宠物订单优化 2025-05-30 18:41:32 +08:00
hahwu
dd9b66b368 充值优化 2025-05-30 18:26:15 +08:00
hahwu
b95f655b66 宠物订单优化 2025-05-30 18:15:51 +08:00
hahwu
2ff05589c5 增加服务端版本 2025-05-30 15:19:40 +08:00
hahwu
6c95148a63 宠物订单 2025-05-30 15:03:29 +08:00
hahwu
43da244384 playroom修复 2025-05-30 14:37:30 +08:00
hahwu
6437df189b 宠物币礼包 2025-05-30 11:31:10 +08:00
hahwu
f7089c55a7 取消触发式订单 2025-05-30 10:40:27 +08:00
hahwu
17343a432e 增加额外的经验条 2025-05-29 18:15:26 +08:00
hahwu
42e9b73764 增加额外的经验条 2025-05-29 18:09:02 +08:00
hahwu
5341353003 playroom优化 2025-05-29 17:52:00 +08:00
hahwu
9c9c19c9a6 订单撤销功能的逻辑调整(这块逻辑注释掉) 2025-05-29 16:10:54 +08:00
hahwu
2ef0c6fabe playroom优化 2025-05-28 18:15:15 +08:00
hahwu
461312dc87 宠物互动的数值衰减,要从玩家签完领养证明之后才开始 2025-05-28 18:11:49 +08:00
hahwu
87983240aa playroom优化 2025-05-28 17:53:07 +08:00
hahwu
78b36ce4d4 playroom优化 2025-05-28 15:52:05 +08:00
hahwu
1cb7532d8b playroom优化 2025-05-28 15:39:12 +08:00
hahwu
ea66010137 登录协议修复 2025-05-28 11:53:25 +08:00
hahwu
38bf34520a 优化playroom点赞 2025-05-27 18:51:45 +08:00
hahwu
7a03d82fcd 增加房间解锁流水 2025-05-27 18:29:35 +08:00
hahwu
812402526a 拜访机制调整 2025-05-27 17:44:46 +08:00
hahwu
322b0e1926 NPC好友优化 2025-05-27 11:34:20 +08:00
hahwu
075ba32e99 修改密码 2025-05-27 10:44:21 +08:00
hahwu
0786f52259 登录返回优化 2025-05-26 18:55:34 +08:00
hahwu
d04f482da1 登录返回优化 2025-05-26 18:51:28 +08:00
hahwu
6df9903bc9 优化登录返回 2025-05-26 18:30:33 +08:00
hahwu
8ea3efb33a 优化订单接口 2025-05-22 10:14:00 +08:00
hahwu
4255f95a12 playroom优化 2025-05-21 16:07:23 +08:00
hahwu
b4dfeb2a09 playroom优化 2025-05-16 16:26:10 +08:00
hahwu
a7fef8e6f1 小猫戏法优化 2025-05-16 14:18:21 +08:00
hahwu
735adeeac3 小猫戏法优化 2025-05-16 12:10:23 +08:00
hahwu
09879e9a4a 小猫戏法优化 2025-05-16 11:08:44 +08:00
hahwu
d066b8b979 高级订单优化 2025-05-15 11:48:03 +08:00
hahwu
220d2bfc10 邮件优化 2025-05-14 16:35:53 +08:00
hahwu
f0416cbb1f 招财猫优化 2025-05-14 16:35:42 +08:00
hahwu
0fab14a138 增加playroom引导奖励 2025-05-12 17:27:37 +08:00
hahwu
c5d7f55543 playroom优化 2025-05-09 17:23:33 +08:00
hahwu
a910950e45 playroom优化 2025-05-09 16:46:50 +08:00
hahwu
37275d64fb 修复升级棋子图鉴刷新 2025-05-09 15:34:19 +08:00
hahwu
a5866339b4 增加GM 2025-05-08 14:18:10 +08:00
hahwu
64d166fa7e 优化 2025-05-07 11:25:55 +08:00
hahwu
4b37daa27a playroom点赞加道具 2025-04-29 18:29:32 +08:00
hahwu
ed1364c21e 去好友家小猫玩游戏,成功之后如果选产物,都额外加5猫币奖励 2025-04-29 18:05:23 +08:00
hahwu
0935f089e4 增加玩家全服数据 2025-04-29 14:55:12 +08:00
hahwu
b20f111cbb 增加玩家全服数据 2025-04-29 14:38:50 +08:00
hahwu
38d6f14b95 增加玩家全服数据 2025-04-29 14:20:01 +08:00
hahwu
c50e176e6c playroom优化 2025-04-29 10:14:18 +08:00
hahwu
924cd98b5c playroom优化 2025-04-28 18:29:39 +08:00
hahwu
a341917c7e 调试优化 2025-04-28 18:29:28 +08:00
hahwu
3a86736040 增加playroominfo 点赞次数 2025-04-28 17:32:52 +08:00
hahwu
5ac7ba79aa 猫咪戏法优化 2025-04-28 10:38:33 +08:00
hahwu
985f5ad0c9 猜颜色活动优化 2025-04-27 15:54:28 +08:00
hahwu
8522a99dd6 playroom优化 2025-04-25 16:37:55 +08:00
hahwu
3dedb5f21f 优化facebook解绑 2025-04-25 16:22:32 +08:00
hahwu
eadb885126 playroom优化 2025-04-25 14:27:27 +08:00
hahwu
e4cb8bfd5e 增加GM 2025-04-25 14:11:53 +08:00
hahwu
43bbdc6f6c 招财猫优化 2025-04-24 18:53:03 +08:00
hahwu
d022641ff2 playroom每日任务 2025-04-24 16:16:24 +08:00
hahwu
787bec91cc playroom每日任务 2025-04-24 16:08:38 +08:00
hahwu
7e6ebb205b playroom每日任务 2025-04-24 15:03:43 +08:00
hahwu
23df24e0dd playroom每日任务 2025-04-24 14:42:52 +08:00
hahwu
35c8ca8f82 playroom每日任务 2025-04-24 10:06:35 +08:00
hahwu
75a6048ca4 playroom每日任务 2025-04-23 18:37:49 +08:00
hahwu
69c8fabc68 playroom每日任务 2025-04-23 15:45:39 +08:00
hahwu
7e59299731 playroom每日任务 2025-04-23 15:07:39 +08:00
hahwu
44ca22a924 招财猫优化 2025-04-23 11:47:26 +08:00
hahwu
7a1a12d530 招财猫优化 2025-04-23 10:46:31 +08:00
hahwu
76b60c892a 任务修复 2025-04-22 17:29:41 +08:00
hahwu
e37366c71a 任务修复 2025-04-22 16:56:04 +08:00
hahwu
d3f859ab8e 任务修复 2025-04-22 16:51:47 +08:00
hahwu
eac8c6b4d2 playroom每日任务 2025-04-22 11:33:07 +08:00
hahwu
a3dc973d91 引导订单修复 2025-04-22 10:16:24 +08:00
hahwu
6b8c141e51 playroom每日任务 2025-04-21 16:33:47 +08:00
hahwu
6b93915fb5 时间线优化 2025-04-21 16:23:43 +08:00
hahwu
7d61511c32 playroom每日任务 2025-04-21 15:56:10 +08:00
hahwu
7e6039f16a 时间线优化 2025-04-21 15:47:36 +08:00
hahwu
839aea3cfa 时间线优化 2025-04-21 15:35:34 +08:00
hahwu
f95a049c90 GM命令 2025-04-21 15:19:26 +08:00
hahwu
f659a1880f 时间线优化 2025-04-21 12:26:49 +08:00
hahwu
ed0890f9d2 时间线优化 2025-04-21 12:13:15 +08:00
hahwu
6139208d0e playroom房间点数 2025-04-21 10:58:38 +08:00
hahwu
cd3edb76fb 猫咪小学徒 2025-04-21 10:08:12 +08:00
hahwu
6e4a804079 回收日优化 2025-04-18 17:22:23 +08:00
hahwu
07781cfa63 playroom优化 2025-04-18 16:52:56 +08:00
hahwu
fefac439c8 猫咪戏法优化 2025-04-18 11:23:36 +08:00
hahwu
8ff8c7f34a 猫咪戏法优化 2025-04-18 11:15:56 +08:00
hahwu
cd38d8cb43 优化 2025-04-18 09:41:27 +08:00
hahwu
0801743ee0 playroom优化 2025-04-17 19:11:32 +08:00
hahwu
20e6023c5e playroom优化 2025-04-17 19:09:30 +08:00
hahwu
331ec61294 系统优化 2025-04-17 10:51:29 +08:00
hahwu
cf7401791e 邮件优化 2025-04-17 10:45:01 +08:00
hahwu
9227881d57 限时事件优化 2025-04-17 10:43:53 +08:00
hahwu
2b771fa30c 邮件优化 2025-04-17 10:30:05 +08:00
hahwu
51b612aed8 配置优化 2025-04-17 10:04:27 +08:00
hahwu
e7fa1bf25d 优化 2025-04-16 19:11:11 +08:00
hahwu
53e305e9d3 日志优化 2025-04-16 19:08:36 +08:00
hahwu
564c650a28 猫咪戏法修复bug 2025-04-16 18:59:23 +08:00
hahwu
4b74c69df4 捉迷藏修复 2025-04-16 17:14:17 +08:00
hahwu
61f50a8e61 增加GM 2025-04-11 18:27:07 +08:00
hahwu
befdbd75a4 增加GM 2025-04-11 18:20:51 +08:00
hahwu
10b45a9992 优化 2025-04-11 11:53:11 +08:00
hahwu
3bcd2d0b70 优化 2025-04-11 10:02:34 +08:00
hahwu
bfd29f02c4 优化 2025-04-11 10:00:29 +08:00
hahwu
01e7a90efd 引导订单优化 2025-04-10 19:08:29 +08:00
hahwu
e3628c77b7 好友优化 2025-04-10 18:22:51 +08:00
hahwu
6dca79233c 好友优化 2025-04-10 17:55:45 +08:00
hahwu
9d08ae6d33 优化 2025-04-10 16:49:57 +08:00
hahwu
19ebb2d6b6 好友优化 2025-04-10 16:21:19 +08:00
hahwu
46b93a0a5d 优化 2025-04-10 16:09:07 +08:00
hahwu
4a7a02238b playroom点赞 2025-04-10 11:58:36 +08:00
hahwu
ed67514159 日志优化 2025-04-10 11:56:26 +08:00
hahwu
674af65789 playroom点赞 2025-04-10 11:54:40 +08:00
hahwu
454e428697 头像框优化 2025-04-10 11:52:43 +08:00
hahwu
8ffd149d12 多语言邮件 2025-04-10 11:38:53 +08:00
hahwu
6c33c83033 npc好友优化 2025-04-10 10:04:46 +08:00
hahwu
8a521a5d43 三段竞赛优化 2025-04-10 09:49:55 +08:00
hahwu
ed4cc858e9 飞书通知优化 2025-04-07 16:25:00 +08:00
hahwu
c100fe828c 飞书通知优化 2025-04-07 16:24:40 +08:00
hahwu
6d7168caa4 超值加购礼包 2025-04-03 16:07:38 +08:00
hahwu
993b1b20b7 赛跑活动 2025-04-02 17:20:45 +08:00
hahwu
56d3b6d53a 赛跑活动 2025-04-02 16:22:23 +08:00
hahwu
4ddde1b2e9 赛跑优化 2025-04-02 14:43:19 +08:00
hahwu
c430dd8cae 邮件优化 2025-04-02 14:41:25 +08:00
hahwu
e8264aa348 赛跑优化 2025-04-02 12:31:02 +08:00
hahwu
4b009814f9 邮件优化 2025-04-02 12:11:43 +08:00
hahwu
4ff91df8ff 邮件优化 2025-04-02 12:08:56 +08:00
hahwu
63f19a4414 Merge branch 'develop' into sdk 2025-04-02 11:58:39 +08:00
hahwu
e5e75f9436 心愿单活动 2025-04-02 09:56:58 +08:00
hahwu
dd7840ffbd 赛车活动优化 2025-04-01 19:02:12 +08:00
hahwu
e93bd6b8aa 订单优化 2025-04-01 18:48:39 +08:00
hahwu
3adf049c92 赛车活动优化 2025-04-01 18:37:56 +08:00
hahwu
ece4e28731 订单优化 2025-04-01 18:37:16 +08:00
hahwu
21e1c63d0d 优化 2025-04-01 17:53:48 +08:00
hahwu
a311e6ca03 限时事件 回收日 2025-04-01 17:50:47 +08:00
hahwu
bd5176db26 心愿单 2025-04-01 17:19:40 +08:00
hahwu
d606e0029a 接口优化 2025-04-01 16:24:42 +08:00
hahwu
f5995941af 接口优化 2025-04-01 16:24:30 +08:00
hahwu
80d0aa0996 心愿单 2025-04-01 16:21:43 +08:00
hahwu
0084b32f0f 赠送礼物 2025-04-01 15:11:56 +08:00
hahwu
3af23789a1 日志监控 2025-04-01 14:26:05 +08:00
hahwu
5f1889c753 订单优化 2025-04-01 12:24:30 +08:00
hahwu
e37c130cc0 赠送礼物 2025-04-01 12:13:13 +08:00
hahwu
29fa052ae9 赠送礼物 2025-04-01 11:18:22 +08:00
hahwu
df19eaaf9f 限时事件+NPC好友 2025-04-01 10:17:07 +08:00
hahwu
ee5781df40 日志监控 2025-04-01 10:15:20 +08:00
hahwu
5ec5b42430 小猫戏法 2025-03-31 17:46:56 +08:00
hahwu
69dc7d638a 赛跑活动 2025-03-31 15:53:25 +08:00
hahwu
71eafdecb0 赛跑活动 2025-03-31 15:47:16 +08:00
hahwu
534d00a975 夏令时优化 2025-03-31 10:10:03 +08:00
hahwu
c7a5363a75 优化 2025-03-28 15:38:19 +08:00
hahwu
e32a09bcd3 优化 2025-03-28 14:57:08 +08:00
hahwu
0f5bb01aec 好友优化 2025-03-28 14:37:06 +08:00
hahwu
0c1ec3ce50 订单优化 2025-03-28 14:34:21 +08:00
hahwu
4e0023ec85 优化 2025-03-28 12:30:27 +08:00
hahwu
cb66894d3f 优化 2025-03-28 12:28:54 +08:00
hahwu
328ebf9399 优化 2025-03-28 12:10:26 +08:00
hahwu
278ee8c44a 消息优化 2025-03-28 11:01:00 +08:00
hahwu
815d896283 好友消息优化 2025-03-28 10:58:57 +08:00
hahwu
c86a66252d 好友消息 2025-03-28 10:53:19 +08:00
hahwu
4518a455a8 日志优化 2025-03-28 09:43:37 +08:00
hahwu
9e3d8e5f39 好友搜索优化 2025-03-27 19:09:37 +08:00
hahwu
4de86e2d61 图鉴优化 2025-03-27 16:56:12 +08:00
hahwu
b496fe07c2 幸运猫优化 2025-03-27 10:47:55 +08:00
hahwu
c94fbff299 优化 2025-03-27 10:44:18 +08:00
hahwu
46e75a50ca 日志 2025-03-27 10:23:44 +08:00
hahwu
5a91d9cbf0 debug 2025-03-27 10:20:56 +08:00
hahwu
32dbdf452c 补发邮件 2025-03-26 18:46:47 +08:00
hahwu
c019b57fde 活动补发邮件 2025-03-26 18:21:45 +08:00
hahwu
7797127879 好运猫优化 2025-03-26 17:52:25 +08:00
hahwu
5e4012965d 猜颜色优化 2025-03-26 16:59:48 +08:00
hahwu
3d697cadfc 卡牌优化 2025-03-26 16:45:50 +08:00
hahwu
6402dedefa 卡牌优化 2025-03-26 16:43:27 +08:00
hahwu
0b64edf79a 幸运猫优化 2025-03-26 16:32:34 +08:00
hahwu
d6bdbf08f9 优化 2025-03-26 16:31:54 +08:00
hahwu
31f935f1b9 猜颜色优化 2025-03-26 14:52:07 +08:00
hahwu
4763b05981 优化 2025-03-26 11:32:34 +08:00
hahwu
bd767da106 优化 2025-03-26 09:51:35 +08:00
hahwu
a803e6c284 playroom优化 2025-03-26 09:48:02 +08:00
hahwu
5e6e29cca9 playroom优化 2025-03-25 19:01:57 +08:00
hahwu
5d70b05a67 playroom优化 2025-03-25 17:37:17 +08:00
hahwu
c2a3f3c0ed playroom优化 2025-03-25 17:35:16 +08:00
hahwu
ca398c7df8 邮件触发 2025-03-25 17:32:46 +08:00
hahwu
9d76a84624 设置语言 2025-03-25 17:20:18 +08:00
hahwu
496940caa3 邮件触发 2025-03-25 17:15:37 +08:00
hahwu
f527918672 gm 2025-03-25 15:38:11 +08:00
hahwu
92070b9c75 猜颜色 2025-03-25 14:55:24 +08:00
hahwu
b650706f2f 猜颜色优化 2025-03-25 14:51:50 +08:00
hahwu
de271d25ef 猜颜色 2025-03-25 11:39:18 +08:00
hahwu
a9e52dc1e6 用户点赞 2025-03-25 10:51:34 +08:00
hahwu
cb62a1f662 活动bug修复 2025-03-24 17:41:54 +08:00
hahwu
0c335a1a47 猜颜色优化 2025-03-24 16:17:02 +08:00
hahwu
7ae38d5cf9 活动礼包 2025-03-24 15:22:45 +08:00
hahwu
774a44d561 活动优化 2025-03-24 15:20:44 +08:00
hahwu
4d376505fd 活动优化 2025-03-24 15:09:39 +08:00
hahwu
e1810b7188 招财猫优化 2025-03-24 15:09:23 +08:00
hahwu
348cf17068 买一赠一礼包 2025-03-24 14:53:40 +08:00
hahwu
defff755a3 招财猫优化 2025-03-24 12:20:39 +08:00
hahwu
33e4ff1c55 活动优化 2025-03-21 16:21:56 +08:00
hahwu
bd9cce0536 日志优化 2025-03-20 17:53:45 +08:00
hahwu
6846d45765 系统优化 2025-03-20 15:47:57 +08:00
hahwu
283cba2255 挖矿优化 2025-03-20 15:32:37 +08:00
hahwu
ff7954f500 挖矿优化 2025-03-20 15:06:26 +08:00
hahwu
d4f32d23a6 修复seed订单异常 2025-03-20 09:47:03 +08:00
hahwu
7bb36e079e 修复seed订单异常 2025-03-20 09:46:07 +08:00
hahwu
febad55f7f Merge branch 'develop' into sdk 2025-03-19 18:39:24 +08:00
hahwu
fd96ad4eeb 限时事件优化 2025-03-19 18:37:07 +08:00
hahwu
54611a0a96 系统优化 2025-03-19 18:33:52 +08:00
hahwu
daf7152619 系统优化 2025-03-19 18:20:16 +08:00
hahwu
903559ca2a 系统优化 2025-03-19 18:00:30 +08:00
hahwu
4190880b15 系统优化 2025-03-19 17:52:31 +08:00
hahwu
13eb99f410 系统优化 2025-03-19 17:42:49 +08:00
hahwu
c3798e7a29 系统优化 2025-03-19 17:28:46 +08:00
hahwu
fcb838fa9a 系统优化 2025-03-19 17:24:52 +08:00
hahwu
29e3061fc7 宠物宝藏优化 2025-03-19 17:20:37 +08:00
hahwu
cd9afa8c7e 宠物宝藏 2025-03-19 16:42:14 +08:00
hahwu
3588490801 系统优化 2025-03-19 16:28:55 +08:00
hahwu
09ac4e46e8 1 2025-03-19 16:28:55 +08:00
hahwu
aaa60b04db 系统优化 2025-03-19 16:28:54 +08:00
hahwu
6f1ab4c1e0 系统优化 2025-03-19 16:20:31 +08:00
hahwu
76fd55d1d5 1 2025-03-19 15:52:14 +08:00
hahwu
8325ce66f2 系统优化 2025-03-19 15:44:50 +08:00
hahwu
32e37f5bd4 订单优化 2025-03-19 14:16:58 +08:00
hahwu
a28e21ffe8 订单优化 2025-03-19 14:16:31 +08:00
hahwu
bcfbe808aa 系统优化 2025-03-19 12:27:28 +08:00
hahwu
a59a667a46 系统优化 2025-03-19 12:27:12 +08:00
hahwu
3ef9e724c4 宠物宝藏 2025-03-19 12:11:24 +08:00
hahwu
bfb9649462 宠物宝藏 2025-03-19 12:04:48 +08:00
hahwu
e7e8f3aad8 宠物宝藏优化 2025-03-19 10:50:59 +08:00
hahwu
7dcdfbb06a 捉迷藏小游戏 2025-03-18 14:37:24 +08:00
hahwu
df9cf9d44d 幸运猫 2025-03-18 12:22:27 +08:00
hahwu
a91952b796 playroom优化 2025-03-18 11:44:51 +08:00
hahwu
5d00d87e18 优化 2025-03-18 11:31:35 +08:00
hahwu
6e0d3a2709 优化图鉴收集功能 2025-03-18 11:14:32 +08:00
hahwu
5d160f71ef 招财猫 2025-03-18 10:56:48 +08:00
hahwu
3d295a8291 收藏室优化 2025-03-17 20:10:52 +08:00
hahwu
d693b6d314 收藏室优化 2025-03-17 20:10:36 +08:00
hahwu
9e7be7ce01 增加GM 2025-03-17 19:46:03 +08:00
hahwu
67b7f09a6a 高级订单修复 2025-03-17 17:47:47 +08:00
hahwu
d83adcf2d9 高级订单修复 2025-03-17 17:47:34 +08:00
hahwu
b47c689089 增加头像,表情奖励 2025-03-17 17:34:44 +08:00
hahwu
5bf481e7b8 全球排行榜优化 2025-03-17 15:55:17 +08:00
hahwu
c485317d8f 全球排行榜优化 2025-03-17 15:52:10 +08:00
hahwu
c73add3bba 增加头像,表情奖励 2025-03-17 15:37:56 +08:00
hahwu
4499df0b43 修复每日任务奖励bug 2025-03-17 15:00:45 +08:00
hahwu
5416127c6a 修复每日任务奖励bug 2025-03-17 15:00:12 +08:00
hahwu
9e32a07bda 发射器退役bug 2025-03-17 12:20:02 +08:00
hahwu
6cdb186f6d 发射器退役bug 2025-03-17 12:19:04 +08:00
hahwu
83305772e4 playroom优化 2025-03-17 11:02:45 +08:00
hahwu
43eb59558f 升级器解锁图鉴 2025-03-17 10:08:43 +08:00
hahwu
7b61cb479a 升级器解锁图鉴 2025-03-17 09:50:36 +08:00
hahwu
03f153c45b 升级器解锁图鉴 2025-03-17 09:50:14 +08:00
hahwu
054be4f843 棋盘优化 2025-03-14 18:17:41 +08:00
hahwu
b8146dcd8c 棋盘优化 2025-03-14 18:17:27 +08:00
hahwu
c51e7d2ac6 棋盘背包优化 2025-03-14 17:48:29 +08:00
hahwu
6f2b7613a4 棋盘背包优化 2025-03-14 17:48:16 +08:00
hahwu
752d21e4e2 头像头像框初始化 2025-03-14 16:45:46 +08:00
hahwu
7f8d04b725 头像框优化 2025-03-14 15:43:49 +08:00
hahwu
982a42a14a 订单调整 2025-03-14 12:24:13 +08:00
hahwu
0ae51244ec 订单调整 2025-03-14 11:51:18 +08:00
hahwu
d9200928d4 订单调整 2025-03-14 11:45:49 +08:00
hahwu
c38f192aa9 订单调整 2025-03-14 11:30:15 +08:00
hahwu
33515bcfa0 动态订单调整 2025-03-14 11:04:50 +08:00
hahwu
36095c1ee2 动态订单调整 2025-03-14 11:04:50 +08:00
hahwu
f96355a239 动态订单调整 2025-03-14 11:01:04 +08:00
hahwu
5b1b870358 动态订单调整 2025-03-14 10:59:38 +08:00
hahwu
69bc3aa629 Merge branch 'develop' into sdk 2025-03-14 09:48:40 +08:00
hahwu
7fc47d72ee 单元测试 2025-03-14 09:48:25 +08:00
hahwu
5496f1df45 Seed订单机制 2025-03-13 12:25:04 +08:00
hahwu
ad529697e9 收藏室更新 2025-03-12 17:18:13 +08:00
hahwu
30d86ce06b 增加收藏室GM 2025-03-12 17:01:05 +08:00
hahwu
7d0a5b1c89 收藏室增加日志 2025-03-12 16:46:23 +08:00
hahwu
56f9db57ef 每日任务更新 2025-03-12 16:37:26 +08:00
hahwu
4f5adaab9b 日志优化 2025-03-12 12:14:42 +08:00
hahwu
20622df1da 升级器 2025-03-11 18:16:05 +08:00
hahwu
49229e74d3 系统优化 2025-03-11 16:27:17 +08:00
hahwu
4679941755 新增每日任务 2025-03-11 16:15:29 +08:00
hahwu
498e8c24f5 套餐优惠礼包 2025-03-11 15:47:16 +08:00
hahwu
2e04fe3164 收藏室优化 2025-03-11 12:04:08 +08:00
hahwu
002aabaeba 功能优化 2025-03-11 10:59:13 +08:00
hahwu
fd0939df7f 高级订单优化 2025-03-10 17:42:05 +08:00
hahwu
9f968554cb 收藏室优化 2025-03-10 17:40:04 +08:00
hahwu
040481cdde 高级产物订单 2025-03-10 15:58:09 +08:00
hahwu
76ab00b1c6 开放高级产物订单 2025-03-10 15:40:57 +08:00
hahwu
43b1b8e955 收藏室 2025-03-10 15:21:17 +08:00
hahwu
a2526c947a 数据优化 2025-03-10 14:47:48 +08:00
hahwu
5435ffbed5 收藏室 2025-03-10 14:21:24 +08:00
hahwu
665f818f22 收藏室 2025-03-10 14:15:20 +08:00
hahwu
2ae7bb8d5a 增加GM 2025-03-10 11:22:55 +08:00
hahwu
fe15155400 系统优化 2025-03-07 15:43:44 +08:00
hahwu
780f501941 表情优化 2025-03-06 17:25:35 +08:00
hahwu
d8c9347fea 表情优化 2025-03-06 17:13:22 +08:00
hahwu
61a083658a 功能优化 2025-03-06 16:48:49 +08:00
hahwu
668703d3f4 图鉴附属点赞功能 2025-03-06 11:13:42 +08:00
hahwu
65eb41e01a 升级器 2025-03-06 10:15:10 +08:00
hahwu
163cdf68ff 表情优化 2025-03-06 09:37:38 +08:00
hahwu
dc7634e385 表情优化 2025-03-05 17:40:49 +08:00
hahwu
59d2456ad2 Merge branch 'develop' into sdk 2025-03-05 10:25:32 +08:00
hahwu
ab01c7248d 日志优化 2025-03-05 10:10:52 +08:00
hahwu
eac1db699c 系统优化 2025-03-03 18:33:32 +08:00
hahwu
1f1381bd8b 系统优化 2025-02-28 11:18:41 +08:00
hahwu
bca1e62368 优化 2025-02-27 18:57:24 +08:00
hahwu
a7305f66dc 系统优化 2025-02-27 17:35:52 +08:00
hahwu
3bdf9850b9 连击快手优化 2025-02-27 16:09:39 +08:00
hahwu
b8525b762e 连击快手优化 2025-02-27 12:17:43 +08:00
hahwu
05d9872bd7 连击快手优化 2025-02-27 11:39:04 +08:00
hahwu
5b995c9694 连击快手优化 2025-02-27 11:10:08 +08:00
hahwu
70ab147a3d 宠物宝藏优化 2025-02-26 17:06:31 +08:00
hahwu
822477f49a 后台功能优化 2025-02-25 14:15:34 +08:00
hahwu
ede1b50b2c 后台功能优化 2025-02-25 12:28:49 +08:00
hahwu
732d732027 后台功能优化 2025-02-25 12:23:33 +08:00
hahwu
f65abd6ff6 后台功能优化 2025-02-25 10:52:51 +08:00
hahwu
c626fcd33c 数数打点修复 2025-02-24 16:18:06 +08:00
hahwu
7b0ce97ca8 数数打点修复 2025-02-24 16:18:06 +08:00
hahwu
0aead0b14d 数数打点修复 2025-02-24 16:15:28 +08:00
hahwu
1b3a08da0b 数数打点修复 2025-02-24 16:09:34 +08:00
hahwu
d69d466651 表情功能开发 2025-02-24 15:49:15 +08:00
hahwu
a2e79d0962 表情功能开发 2025-02-24 15:07:18 +08:00
hahwu
caa096e79c 日志优化 2025-02-24 11:58:53 +08:00
hahwu
3580537335 修改断线重连 2025-02-21 18:06:42 +08:00
hahwu
fc3619a16d 修改断线重连 2025-02-21 18:06:32 +08:00
hahwu
6b1c59d50e playroom优化 2025-02-21 16:49:20 +08:00
hahwu
4304401960 playroom优化 2025-02-21 14:53:23 +08:00
hahwu
e5a3d04ea2 bug修复 2025-02-21 14:34:26 +08:00
hahwu
98206a6f44 bug修复 2025-02-21 14:31:56 +08:00
hahwu
b43aa1c699 Merge branch 'develop' into sdk 2025-02-21 12:11:13 +08:00
hahwu
0340e3dd83 支付校验优化 2025-02-21 10:11:39 +08:00
hahwu
aaa4f5e805 优化sql语句 2025-02-21 10:10:43 +08:00
hahwu
357d1b468f 框架结构优化 2025-02-20 12:19:26 +08:00
hahwu
f8ce894ab6 playroom优化 2025-02-18 12:25:16 +08:00
hahwu
1975227ef1 修复订单bug 2025-02-17 19:09:48 +08:00
hahwu
61b5b091b9 修复订单bug 2025-02-17 19:09:21 +08:00
hahwu
7b0d9be8c1 playroom商店 2025-02-17 15:31:07 +08:00
hahwu
9f1187962e 公共模块优化 2025-02-17 15:25:23 +08:00
hahwu
cd5203b8f6 playroom bug 2025-02-17 12:05:52 +08:00
hahwu
eb312084f1 playroom bug 2025-02-17 11:45:54 +08:00
hahwu
6426cf266d playroom bug 2025-02-17 11:37:29 +08:00
hahwu
774a7bd2b7 playroom bug 2025-02-17 11:31:44 +08:00
hahwu
d7a39e9635 好友消息优化 2025-02-17 11:16:45 +08:00
hahwu
10a058967d 优化日志 2025-02-17 10:20:53 +08:00
hahwu
7072f8ee92 优化日志 2025-02-17 10:20:42 +08:00
hahwu
a9763d5aab 1 2025-02-17 10:14:52 +08:00
hahwu
e21196906a 增加账号拷贝GM 2025-02-14 17:50:21 +08:00
hahwu
aabda95938 后台GM命令 2025-02-14 14:53:11 +08:00
hahwu
65f07a23a5 后台GM命令 2025-02-14 14:53:10 +08:00
hahwu
21f2f604d6 后台GM命令 2025-02-14 14:53:09 +08:00
hahwu
1ae9cd4e15 后台GM命令 2025-02-14 14:53:08 +08:00
hahwu
6100bfb1e3 数数打点 2025-02-14 14:53:07 +08:00
hahwu
8c2a1d0e68 日志优化 2025-02-14 14:53:07 +08:00
hahwu
e27b59283e 后台GM命令 2025-02-14 14:38:15 +08:00
hahwu
4b0a9e50c3 后台GM命令 2025-02-14 14:30:42 +08:00
hahwu
140a7dc72b 后台GM命令 2025-02-14 14:27:53 +08:00
hahwu
4b6920be1d 后台GM命令 2025-02-14 11:32:25 +08:00
hahwu
f62e7b3dc9 数数打点 2025-02-14 11:10:13 +08:00
hahwu
9b8e26892f 日志优化 2025-02-14 10:28:18 +08:00
hahwu
db9a09df35 好友bug优化 2025-02-13 17:13:19 +08:00
hahwu
2d51971ff1 好友bug优化 2025-02-13 17:12:21 +08:00
hahwu
b73960848a 修复能量倍数bug 2025-02-13 14:59:20 +08:00
hahwu
774c2a6a08 卡牌优化 2025-02-13 14:59:20 +08:00
hahwu
bf38f7d5c6 修复能量倍数bug 2025-02-13 14:59:00 +08:00
hahwu
4dfe224ac1 卡牌优化 2025-02-13 14:49:54 +08:00
hahwu
908eea82f7 日志优化 2025-02-13 14:15:08 +08:00
hahwu
b0d74c0e60 日志优化 2025-02-13 14:14:30 +08:00
hahwu
b9199c19dd 逻辑错误发送到飞书 2025-02-12 14:57:24 +08:00
hahwu
2c68486e08 逻辑错误发送到飞书 2025-02-12 14:56:48 +08:00
hahwu
c5de7434f5 Merge branch 'develop' into sdk 2025-02-12 12:26:50 +08:00
hahwu
862d885d21 逻辑错误发送到飞书 2025-02-12 12:21:54 +08:00
hahwu
a1056a5556 逻辑错误发送到飞书 2025-02-12 11:41:49 +08:00
hahwu
e7ccaf65d2 playroom bug修复 2025-02-12 10:11:20 +08:00
hahwu
0c12dd7bc7 优化离线时间 2025-02-11 18:12:13 +08:00
hahwu
8d6834ac1f 优化离线时间 2025-02-11 18:09:10 +08:00
hahwu
c04d31d778 优化离线时间 2025-02-11 17:37:28 +08:00
hahwu
fbcef69695 禁用高级产物订单 2025-02-11 15:46:08 +08:00
hahwu
db64339ea6 增加配置重载 2025-02-11 15:27:53 +08:00
hahwu
d9cf7c8237 增加配置重载 2025-02-11 15:12:13 +08:00
hahwu
7c63dff032 修复高级产物bug 2025-02-11 11:16:06 +08:00
hahwu
6e1c9d42d3 订单优化 2025-02-11 10:55:47 +08:00
hahwu
b60261c1c1 功能优化 2025-02-11 10:35:20 +08:00
hahwu
d464813ed7 playroom优化 2025-02-11 10:34:21 +08:00
hahwu
55c925e0cb 数数打点 2025-02-10 15:54:36 +08:00
hahwu
0a4b895e14 数数打点 2025-02-10 15:54:07 +08:00
hahwu
96de9238d7 游戏功能优化 2025-02-10 15:15:45 +08:00
hahwu
8a3c76231d 游戏功能优化 2025-02-10 15:15:32 +08:00
hahwu
4dc0d503b3 邮件功能优化 2025-02-10 11:32:50 +08:00
hahwu
2511e26486 邮件功能优化 2025-02-10 11:25:30 +08:00
hahwu
14628ba7e6 邮件功能优化 2025-02-10 11:09:07 +08:00
hahwu
1c9d024b1e 邮件功能优化 2025-02-10 10:56:08 +08:00
hahwu
a1e0c487fe playroom修复bug 2025-02-10 10:09:44 +08:00
hahwu
2260bd6d71 Merge branch 'sdk' into online 2025-02-08 19:02:51 +08:00
hahwu
a773f58e45 邮件补发 2025-02-08 18:10:26 +08:00
hahwu
25bbf06444 playroom优化 2025-02-08 17:09:14 +08:00
hahwu
87cb5dadb9 配置更新 2025-02-08 15:54:03 +08:00
hahwu
09ef04565b 配置更新 2025-02-08 15:52:20 +08:00
hahwu
18e40fa78e 邮件功能优化 2025-02-08 15:52:01 +08:00
hahwu
c464275763 高级产物处理机制 2025-02-08 10:45:18 +08:00
hahwu
fe6cffa6e4 playroom bug修复 2025-02-08 10:30:47 +08:00
hahwu
78c8ba45bf bug修复 2025-02-08 10:10:29 +08:00
hahwu
e52432ec1a bug修复 2025-02-08 10:10:17 +08:00
hahwu
40954c9fa8 bug修复 2025-02-08 10:09:56 +08:00
hahwu
fdced55115 Revert "debug"
This reverts commit 9b7df3d3da.
2025-02-08 10:09:38 +08:00
hahwu
9b7df3d3da debug 2025-02-08 10:05:20 +08:00
hahwu
3a3d79624a 修改GM 2025-02-07 15:09:29 +08:00
hahwu
ff818441f7 playroom优化 2025-02-07 14:58:10 +08:00
hahwu
6893138df0 修改GM 2025-02-07 14:34:20 +08:00
hahwu
7c64006080 翻倍掉档配置 2025-02-07 11:59:50 +08:00
hahwu
15e1f476ed 收集所有卡牌,补发图鉴奖励 2025-02-07 11:05:11 +08:00
hahwu
328b101d05 playroom优化 2025-02-07 10:30:11 +08:00
hahwu
ca0efb53dd playroom优化 2025-02-06 19:18:44 +08:00
hahwu
8f8f83483a 数数埋点 2025-02-06 19:05:23 +08:00
hahwu
bbdd83d751 数数埋点 2025-02-06 19:05:12 +08:00
hahwu
0db815ba45 日志 2025-02-06 19:04:58 +08:00
hahwu
4b5b936a18 playroom优化 2025-02-06 19:04:15 +08:00
hahwu
47c3ccda89 数数埋点 2025-02-06 19:03:59 +08:00
hahwu
a060722c1f 数数埋点 2025-02-06 17:39:11 +08:00
hahwu
2b2c091ac8 数数埋点 2025-02-06 17:38:59 +08:00
hahwu
935e890db2 数数埋点 2025-02-06 17:38:31 +08:00
hahwu
26be4af8a7 playroom优化 2025-02-06 17:26:17 +08:00
hahwu
51ffa2449b playroom优化 2025-02-06 16:54:18 +08:00
hahwu
50ab44d3f5 邮件补偿 2025-02-06 11:44:44 +08:00
hahwu
8ce71367c8 邮件补偿 2025-02-06 11:44:30 +08:00
hahwu
8ee28c28e2 邮件补偿 2025-02-06 11:44:04 +08:00
hahwu
3c31586879 playroom优化 2025-02-06 11:43:55 +08:00
hahwu
0746cd7a6d 数数埋点 2025-02-05 16:37:32 +08:00
hahwu
8d0988490d 数数埋点 2025-02-05 16:37:22 +08:00
hahwu
331a46e5ce 数数埋点 2025-02-05 16:36:52 +08:00
hahwu
5b57c0a565 优化日志 2025-02-05 11:10:30 +08:00
hahwu
7e96008168 优化日志 2025-02-05 11:06:20 +08:00
hahwu
2c75672e4b 锦标赛优化 2025-01-24 19:30:46 +08:00
hahwu
59d3dbbe92 锦标赛优化 2025-01-24 19:30:37 +08:00
hahwu
91ab7b1861 锦标赛优化 2025-01-24 19:30:21 +08:00
hahwu
e405207ea8 订单优化 2025-01-24 18:28:35 +08:00
hahwu
f23133e394 一键高级号 2025-01-24 17:40:42 +08:00
hahwu
abc26c29a5 日志 2025-01-24 15:23:26 +08:00
hahwu
afb158420b 日志 2025-01-24 15:23:18 +08:00
hahwu
ce2eaa49fd 日志 2025-01-24 15:22:52 +08:00
hahwu
5d1824142a 锦标赛日志 2025-01-24 14:06:03 +08:00
hahwu
a7e1607113 锦标赛日志 2025-01-24 14:05:55 +08:00
hahwu
dbf06fa9e5 锦标赛日志 2025-01-24 14:05:34 +08:00
hahwu
65e898fda3 Merge branch 'sdk' into online 2025-01-24 12:30:34 +08:00
hahwu
5a3e42628a Merge branch 'develop' into sdk 2025-01-24 12:30:11 +08:00
hahwu
a0417ad241 优化订单逻辑 2025-01-24 12:29:05 +08:00
hahwu
966e996ae0 优化数据收取 2025-01-24 12:07:05 +08:00
hahwu
0e3997eb0b 优化数据收取 2025-01-24 12:00:12 +08:00
hahwu
96afc1958f 修复棋子出售bug 2025-01-24 11:30:51 +08:00
hahwu
f365123c8b 小猪存钱罐优化 2025-01-24 11:30:51 +08:00
hahwu
77f2b31d62 修复棋子出售bug 2025-01-24 11:30:38 +08:00
hahwu
2c3b420743 修复棋子出售bug 2025-01-24 11:30:21 +08:00
hahwu
6d858c55dc 小猪存钱罐优化 2025-01-24 11:08:16 +08:00
hahwu
14486f7a39 小猪存钱罐优化 2025-01-24 11:07:59 +08:00
hahwu
19ac59a26f 小猪存钱罐优化 2025-01-24 11:07:45 +08:00
hahwu
c0c2760805 支付校验 2025-01-24 10:51:16 +08:00
hahwu
56e50e6a90 支付校验 2025-01-24 10:51:06 +08:00
hahwu
4feea55788 支付校验 2025-01-24 10:50:45 +08:00
hahwu
33e05e8e8d 日志优化 2025-01-24 10:24:47 +08:00
hahwu
ce058edb7f 日志优化 2025-01-24 10:24:34 +08:00
hahwu
8e386659e2 vendor 2025-01-24 10:24:07 +08:00
hahwu
c8cdd1f2cf 日志优化 2025-01-24 10:18:57 +08:00
hahwu
83ec142878 Merge branch 'sdk' into online 2025-01-23 17:34:23 +08:00
hahwu
feb8677fa3 死循环优化 2025-01-23 17:34:09 +08:00
hahwu
629855c2b7 死循环优化 2025-01-23 17:33:32 +08:00
hahwu
7a5bb87b9e Revert "系统优化"
This reverts commit f7a9ab3be1.
2025-01-23 17:13:54 +08:00
hahwu
f7a9ab3be1 系统优化 2025-01-23 17:05:48 +08:00
hahwu
33ce4eba43 debug日志 2025-01-23 16:38:01 +08:00
hahwu
aeff99968b debug日志 2025-01-23 16:37:47 +08:00
hahwu
bdd66bdbbd debug日志 2025-01-23 16:21:59 +08:00
hahwu
512a7e977f debug日志 2025-01-23 16:21:45 +08:00
hahwu
3fca083518 修复宝箱雨奖励 2025-01-23 15:34:19 +08:00
hahwu
bc61b4520e 修复宝箱雨奖励 2025-01-23 15:34:04 +08:00
hahwu
0f41aa3da8 能量倍数bug修复 2025-01-23 15:13:25 +08:00
hahwu
e4b8eb9d2c 日常任务优化 2025-01-23 15:13:25 +08:00
hahwu
f675b35cf6 能量倍数bug修复 2025-01-23 15:13:12 +08:00
hahwu
ecca89df16 日常任务优化 2025-01-23 15:05:46 +08:00
hahwu
d4dc459192 同一个卡包内不能开出重复卡牌 2025-01-23 11:56:18 +08:00
hahwu
64e006c603 同一个卡包内不能开出重复卡牌 2025-01-23 11:55:51 +08:00
hahwu
0f732904be 棋子商店优化 2025-01-23 11:43:12 +08:00
hahwu
9bf4933ec6 棋子商店优化 2025-01-23 11:41:57 +08:00
hahwu
a637572ca9 debug日志 2025-01-23 10:23:07 +08:00
hahwu
55200f6bbe debug日志 2025-01-23 10:22:30 +08:00
hahwu
e4d063a1fb Merge branch 'develop' into sdk 2025-01-23 10:19:15 +08:00
hahwu
5331ceea99 锦标赛bug修复 2025-01-23 10:17:37 +08:00
hahwu
f0c6b07c66 好友优化 2025-01-22 18:54:10 +08:00
hahwu
15508345ca 好友优化 2025-01-22 18:47:48 +08:00
hahwu
a3b4937469 好友优化 2025-01-22 18:21:17 +08:00
hahwu
3860669bfe 好友优化 2025-01-22 17:51:20 +08:00
hahwu
71df4c7ce6 卡牌赛季初奖励 2025-01-22 17:41:40 +08:00
hahwu
8c3ca0e3a5 卡牌赛季初奖励 2025-01-22 17:41:08 +08:00
hahwu
a5be274ed9 第一个限时事件bonus写死为“猫咪旋风” 2025-01-22 17:08:50 +08:00
hahwu
ca4ddc283f 第一个限时事件bonus写死为“猫咪旋风” 2025-01-22 17:06:08 +08:00
hahwu
75bbec9cfc 宝箱雨bug修复 2025-01-22 15:51:48 +08:00
hahwu
316da899e7 宝箱雨bug修复 2025-01-22 15:51:35 +08:00
hahwu
e09178e42e 安慰订单链 2025-01-22 15:03:54 +08:00
hahwu
b6dd03ce6a 安慰订单链 2025-01-22 15:03:31 +08:00
hahwu
e7178b532a 超级订单修复 2025-01-22 11:52:55 +08:00
hahwu
30af5939ba 超级订单修复 2025-01-22 11:39:46 +08:00
hahwu
5a3bf40424 facebook修复 2025-01-22 11:32:52 +08:00
hahwu
04e7cae275 facebook修复 2025-01-22 11:32:42 +08:00
hahwu
ce145f8215 facebook修复 2025-01-22 11:25:07 +08:00
hahwu
11e922799e Merge branch 'sdk' into online 2025-01-22 11:11:00 +08:00
hahwu
cd548bbf20 更新协议 2025-01-22 11:07:44 +08:00
hahwu
459a9e7709 facebook优化 2025-01-22 11:07:44 +08:00
hahwu
32162baa7a 修改GM命令 2025-01-22 11:07:44 +08:00
hahwu
c4aab7ef76 日志修复 2025-01-22 11:07:43 +08:00
hahwu
b680feb000 能量倍数修复 2025-01-22 11:07:27 +08:00
hahwu
c48166d663 更新协议 2025-01-22 11:07:27 +08:00
hahwu
884f84fe42 facebook优化 2025-01-22 11:07:27 +08:00
hahwu
08db502523 修改GM命令 2025-01-22 11:07:27 +08:00
hahwu
2b6954983c 日志修复 2025-01-22 11:07:27 +08:00
hahwu
9e97c0c32b 能量倍数修复 2025-01-22 11:06:43 +08:00
hahwu
8c5a8d78cf 更新协议 2025-01-22 11:03:51 +08:00
hahwu
b2b9a8c2f9 facebook优化 2025-01-22 11:01:17 +08:00
hahwu
77e686f97d 修改GM命令 2025-01-22 10:40:16 +08:00
hahwu
4e3ab132bf 日志修复 2025-01-22 09:50:20 +08:00
hahwu
dff9671f74 日志修复 2025-01-22 09:49:57 +08:00
hahwu
71cac1cb38 日志 2025-01-21 19:31:16 +08:00
hahwu
06dd7b4aa6 facebook优化 2025-01-21 19:31:15 +08:00
hahwu
5808380440 facebook优化 2025-01-21 19:31:15 +08:00
hahwu
54c0363ae0 日志 2025-01-21 19:31:02 +08:00
hahwu
9737d7d17c facebook优化 2025-01-21 19:30:33 +08:00
hahwu
a9f9f1d1dd facebook优化 2025-01-21 19:30:33 +08:00
hahwu
002dfefc98 facebook优化 2025-01-21 19:30:15 +08:00
hahwu
ad7944cdce facebook优化 2025-01-21 19:29:44 +08:00
hahwu
fef96ecc7d 日志 2025-01-21 19:22:51 +08:00
hahwu
056dd07c15 debug日志 2025-01-21 19:16:05 +08:00
hahwu
038b89db28 facebook优化 2025-01-21 19:07:19 +08:00
hahwu
a562733d9f facebook优化 2025-01-21 19:07:10 +08:00
hahwu
f2c2b9c5bc facebook优化 2025-01-21 19:06:55 +08:00
hahwu
64747fa33e 锦标赛系数调整 2025-01-21 19:06:55 +08:00
hahwu
8e377313d4 Gm 2025-01-21 19:06:23 +08:00
hahwu
3d13c78896 Gm 2025-01-21 19:05:45 +08:00
hahwu
c950a73d82 facebook优化 2025-01-21 19:03:57 +08:00
hahwu
e57b7d0055 facebook优化 2025-01-21 19:00:05 +08:00
hahwu
c230556d9b facebook优化 2025-01-21 18:59:53 +08:00
hahwu
4b1af9f589 锦标赛系数调整 2025-01-21 18:57:55 +08:00
hahwu
58cadc4795 facebook优化 2025-01-21 18:44:05 +08:00
hahwu
a644b4a222 facebook优化 2025-01-21 18:43:55 +08:00
hahwu
f2aca21e9a facebook优化 2025-01-21 18:43:28 +08:00
hahwu
cd96c45a8b facebook优化 2025-01-21 18:27:09 +08:00
hahwu
e112a9528e facebook优化 2025-01-21 18:26:58 +08:00
hahwu
146db7a84f facebook优化 2025-01-21 18:26:33 +08:00
hahwu
cf18a5db20 facebook处理 2025-01-21 17:54:34 +08:00
hahwu
f687b4f19a facebook处理 2025-01-21 17:54:24 +08:00
hahwu
cf0047bd99 facebook处理 2025-01-21 17:54:13 +08:00
hahwu
a17c06f6ab debug日志 2025-01-21 17:32:33 +08:00
hahwu
155a9a06c3 debug日志 2025-01-21 17:32:15 +08:00
hahwu
e351036f51 bug修复 2025-01-21 17:03:13 +08:00
hahwu
27588b2b74 bug修复 2025-01-21 17:03:03 +08:00
hahwu
46c6e67f14 bug修复 2025-01-21 17:02:51 +08:00
hahwu
608e42c8cf 修改倍数highRoller到期下调倍数 2025-01-21 16:53:42 +08:00
hahwu
07d6ec9ded 修改倍数highRoller到期下调倍数 2025-01-21 16:53:42 +08:00
hahwu
f6243d164c 修改倍数highRoller到期下调倍数 2025-01-21 16:47:17 +08:00
hahwu
5f647da1ac 修改倍数highRoller到期下调倍数 2025-01-21 16:45:13 +08:00
hahwu
95d889cf58 Merge branch 'sdk' into online 2025-01-21 16:15:36 +08:00
hahwu
3fa4c98ff8 Merge branch 'develop' into sdk 2025-01-21 16:15:17 +08:00
hahwu
06f177031b 订单支付优化 2025-01-21 16:14:59 +08:00
hahwu
fad68b78dc 订单优化 2025-01-21 16:08:19 +08:00
hahwu
82f641e33f Merge branch 'sdk' into online 2025-01-21 15:31:39 +08:00
hahwu
3839690358 facebook优化 2025-01-21 15:31:11 +08:00
hahwu
02c916c11b facebook优化 2025-01-21 15:30:57 +08:00
hahwu
eea0ed6cdb 优化 2025-01-21 15:10:32 +08:00
hahwu
2caa12d4af 支付优化 2025-01-21 15:06:50 +08:00
hahwu
6657cc6efc 宝箱雨优化 2025-01-21 14:36:43 +08:00
hahwu
8ddaa2c95d 宝箱雨优化 2025-01-21 14:36:32 +08:00
hahwu
9f6bbc56ad Merge branch 'develop' into sdk 2025-01-21 14:08:38 +08:00
hahwu
1c6c515e03 支付优化 2025-01-21 12:26:24 +08:00
hahwu
6f91c8892f 小猪存钱优化 2025-01-21 11:29:22 +08:00
hahwu
b8de7d7da7 宝箱雨优化 2025-01-21 09:56:05 +08:00
hahwu
64c3285b5d playroom优化 2025-01-20 18:47:23 +08:00
hahwu
3571c8947c 支付优化 2025-01-20 17:45:23 +08:00
hahwu
1324f775d0 支付优化 2025-01-20 17:42:35 +08:00
hahwu
5617ed1e82 优化GM 2025-01-20 16:53:22 +08:00
hahwu
ff5c8bc777 只保存15天以内的日志文件 2025-01-20 14:58:05 +08:00
hahwu
326fc96371 修改后台接口 2025-01-20 11:59:11 +08:00
hahwu
c6d4a48145 Merge branch 'develop' into sdk 2025-01-20 11:13:45 +08:00
hahwu
2a3a7ffa81 修改棋子商店钻石公式 2025-01-20 11:13:01 +08:00
hahwu
795cd6ee96 playroom增加0点更新 2025-01-20 11:01:12 +08:00
hahwu
b1f9e44746 先禁用高级产物处理 2025-01-20 10:06:57 +08:00
hahwu
b42609e0a0 playroom优化 2025-01-20 10:05:31 +08:00
hahwu
4b4bbdc080 订单逻辑修改 2025-01-18 19:13:59 +08:00
hahwu
1bebf0ddc2 修改记录 2025-01-18 19:12:04 +08:00
hahwu
f9490379cc 修改日志 2025-01-18 19:10:55 +08:00
hahwu
9dc3aa6298 Merge branch 'sdk' into online 2025-01-18 18:22:47 +08:00
hahwu
90734285f2 修复订单调整bug 2025-01-18 17:23:22 +08:00
hahwu
ca8b74710e 修复订单调整bug 2025-01-18 17:23:01 +08:00
hahwu
79ce8206af 修复订单调整bug 2025-01-18 17:14:06 +08:00
hahwu
01ca01eecf 修复订单调整bug 2025-01-18 17:13:14 +08:00
hahwu
e88166b64d 修复订单调整bug 2025-01-18 16:59:29 +08:00
hahwu
d5e06d8462 修复订单调整bug 2025-01-18 16:59:15 +08:00
hahwu
a679420e87 打包文件 2025-01-18 16:11:51 +08:00
hahwu
62cd9b8ef4 打包文件 2025-01-18 16:11:33 +08:00
hahwu
436c2049bc 修复订单调整bug 2025-01-18 16:01:47 +08:00
hahwu
5e6b3b4f87 修复订单调整bug 2025-01-18 16:01:33 +08:00
hahwu
1e091de9b3 高级产物处理机制 2025-01-17 17:41:59 +08:00
hahwu
27893e8752 Merge branch 'sdk' into online 2025-01-17 16:52:28 +08:00
hahwu
550330da90 Merge branch 'develop' into sdk 2025-01-17 16:50:52 +08:00
hahwu
156b9da2d3 基准测试 2025-01-17 16:50:38 +08:00
hahwu
c04d345970 支付日志 2025-01-17 11:30:03 +08:00
hahwu
a61eb69a0f 修改订单逻辑 2025-01-17 10:45:37 +08:00
hahwu
d26720a9c2 修改订单逻辑 2025-01-17 09:55:59 +08:00
hahwu
03003f7297 Merge branch 'sdk' into online 2025-01-16 15:52:25 +08:00
hahwu
9b631d6106 Merge branch 'develop' into sdk 2025-01-16 15:52:07 +08:00
hahwu
d4c01061aa 更新日志 2025-01-16 15:49:50 +08:00
hahwu
ea134948f6 优化日志 2025-01-16 15:14:37 +08:00
hahwu
e3870ec10a 修改订单逻辑 2025-01-16 14:40:55 +08:00
hahwu
555d46e623 修改订单逻辑 2025-01-16 12:34:16 +08:00
hahwu
f778132175 增加日志 2025-01-17 00:01:09 +08:00
hahwu
4dbc0f3b54 修改订单逻辑 2025-01-15 18:51:14 +08:00
hahwu
80b6bddd65 修改bonus bug 2025-01-15 18:31:14 +08:00
hahwu
bcd7743073 修改bonus bug 2025-01-15 18:17:46 +08:00
hahwu
553fdef352 修改订单逻辑 2025-01-15 16:55:10 +08:00
hahwu
74948cd917 订单发货修复 2025-01-15 16:24:35 +08:00
hahwu
c6c884b232 修改订单逻辑 2025-01-15 16:07:27 +08:00
hahwu
e8fd0d9953 修改订单逻辑 2025-01-15 14:29:09 +08:00
hahwu
83bfb64d49 修改订单逻辑 2025-01-15 10:23:07 +08:00
hahwu
e0e6731d4f 修改订单逻辑 2025-01-15 09:51:15 +08:00
hahwu
741a389ebd 修改订单逻辑 2025-01-14 19:03:14 +08:00
hahwu
169378ff01 修改bug 2025-01-14 18:46:15 +08:00
hahwu
d96b05a2aa 修改订单逻辑 2025-01-14 18:39:34 +08:00
hahwu
affc20bf9f 修改订单逻辑 2025-01-14 17:59:42 +08:00
hahwu
e3fe8db5f4 增加分离器 2025-01-14 10:56:08 +08:00
hahwu
282fd732f1 增加分离器 2025-01-14 10:53:08 +08:00
hahwu
0d3b0733d2 当棋盘中有订单棋子时不对其进行调整 2025-01-14 10:38:09 +08:00
hahwu
29fa621444 日志 2025-01-13 18:01:43 +08:00
hahwu
398e7208dc 日志 2025-01-13 18:01:23 +08:00
hahwu
599e4b40d1 日志 2025-01-13 17:47:02 +08:00
hahwu
eb6a8777a0 预热订单优化 2025-01-13 16:57:17 +08:00
hahwu
48f6ab1e1e Merge branch 'sdk' into online 2025-01-13 16:51:58 +08:00
hahwu
1b913664a5 Merge branch 'develop' into sdk 2025-01-13 16:50:55 +08:00
hahwu
11845fd598 修改订单发货接口 2025-01-13 16:47:32 +08:00
hahwu
4a8a953247 预热订单优化 2025-01-13 16:29:44 +08:00
hahwu
c480c4d66a 增加请求玩家资产接口 2025-01-13 16:20:57 +08:00
hahwu
30c1b4414b 竞标赛优化 2025-01-13 15:54:55 +08:00
hahwu
1f96a6e4ad 竞标赛优化 2025-01-13 15:54:44 +08:00
hahwu
1345d7d5e2 竞标赛优化 2025-01-13 15:54:33 +08:00
hahwu
c8fef28bc7 增加定时增加能量流水 2025-01-13 15:49:06 +08:00
hahwu
a116c2a475 优化日志 2025-01-13 15:49:06 +08:00
hahwu
5bbd80c19f 增加注册日志 2025-01-13 15:49:05 +08:00
hahwu
bf0fa0ec81 预热订单优化 2025-01-13 15:49:05 +08:00
hahwu
34830bb795 预热订单优化 2025-01-13 15:49:05 +08:00
hahwu
4643066b69 增加定时增加能量流水 2025-01-13 15:48:10 +08:00
hahwu
54e1325b02 优化日志 2025-01-13 15:48:10 +08:00
hahwu
bb5ab05f23 增加注册日志 2025-01-13 15:48:10 +08:00
hahwu
886e026d17 增加定时增加能量流水 2025-01-13 15:26:14 +08:00
hahwu
6c60f90518 优化日志 2025-01-13 15:14:21 +08:00
hahwu
2b6b25bf80 增加注册日志 2025-01-13 14:52:31 +08:00
hahwu
3d70bfac4c 预热订单优化 2025-01-13 14:12:57 +08:00
hahwu
261e60125b 预热订单优化 2025-01-13 14:12:40 +08:00
hahwu
cf5ec556f7 预热订单优化 2025-01-13 12:15:01 +08:00
hahwu
18e22b4f1e 预热订单优化 2025-01-13 12:14:49 +08:00
hahwu
356358f011 优化kafka日志 2025-01-13 11:11:51 +08:00
hahwu
e4658a828c 优化kafka日志 2025-01-13 11:11:40 +08:00
hahwu
12dc1bc0ab 优化kafka日志 2025-01-13 11:11:26 +08:00
hahwu
00fe38dadc 优化kafka日志 2025-01-13 10:59:43 +08:00
hahwu
4b0fe58de6 优化kafka日志 2025-01-13 10:58:25 +08:00
hahwu
e93bcc02b1 优化kafka日志 2025-01-13 10:58:04 +08:00
hahwu
fd96d805af 增加playroom道具购买接口 2025-01-13 10:41:53 +08:00
hahwu
4b629461c1 修改支付发货接口 2025-01-11 14:24:06 +08:00
hahwu
c67856e7f5 增加后台接口 2025-01-11 14:24:06 +08:00
hahwu
a69ea709a3 修改支付发货接口 2025-01-11 12:02:17 +08:00
hahwu
163336bc0f 增加后台接口 2025-01-10 18:09:48 +08:00
hahwu
4aac987725 优化kafka日志 2025-01-10 17:18:26 +08:00
hahwu
827a016102 优化kafka日志 2025-01-10 17:18:18 +08:00
hahwu
fa00657ead 优化kafka日志 2025-01-10 17:18:05 +08:00
hahwu
821ae9d112 创建订单号 2025-01-10 15:32:54 +08:00
hahwu
4c5f48ffd2 创建订单号 2025-01-10 15:32:54 +08:00
hahwu
0ec68a5e96 创建订单号 2025-01-10 15:32:53 +08:00
hahwu
81db17842b 增加开启资源宝箱接口 2025-01-10 15:32:53 +08:00
hahwu
bc5cd1b769 创建订单号 2025-01-10 15:32:41 +08:00
hahwu
0896af547d 创建订单号 2025-01-10 15:32:41 +08:00
hahwu
0d00c77bbc 创建订单号 2025-01-10 15:32:41 +08:00
hahwu
8d8750fd9b 增加开启资源宝箱接口 2025-01-10 15:32:40 +08:00
hahwu
11a93fa7e3 创建订单号 2025-01-10 15:16:26 +08:00
hahwu
e4690f1235 创建订单号 2025-01-10 15:07:52 +08:00
hahwu
328477628d 创建订单号 2025-01-10 14:58:34 +08:00
hahwu
a520e17de1 增加开启资源宝箱接口 2025-01-10 11:05:47 +08:00
hahwu
eca7e36e47 看广告获取体力 2025-01-09 17:22:58 +08:00
hahwu
35b0bf57a7 看广告获取体力 2025-01-09 17:22:46 +08:00
hahwu
b614c1dd14 看广告获取体力 2025-01-09 16:37:53 +08:00
hahwu
365cdf0fb4 增加玩家nickname记录 2025-01-08 15:53:58 +08:00
hahwu
cb8bba3537 kafka增加断线重连功能 2025-01-08 15:53:58 +08:00
hahwu
d91d60ead0 增加玩家nickname记录 2025-01-08 15:53:47 +08:00
hahwu
9e433518a0 kafka增加断线重连功能 2025-01-08 15:53:47 +08:00
hahwu
bf3ba5c6d8 增加玩家nickname记录 2025-01-08 15:53:11 +08:00
hahwu
a6b5e94b24 kafka增加断线重连功能 2025-01-08 15:23:05 +08:00
hahwu
abd5039f98 修复冠军赛0点更新bug 2025-01-08 14:56:26 +08:00
hahwu
e4325aaf9d 修复冠军赛0点更新bug 2025-01-08 14:56:18 +08:00
hahwu
0b60ff67ce 修复冠军赛0点更新bug 2025-01-08 14:56:08 +08:00
hahwu
1401edf26e 修改GM 2025-01-08 11:27:55 +08:00
hahwu
edca58e661 修复好友搜索bug 2025-01-08 11:27:54 +08:00
hahwu
b5ec7a1a24 修复玩家基础信息 2025-01-08 11:27:54 +08:00
hahwu
80e1a2b9ca 修改GM 2025-01-08 11:19:46 +08:00
hahwu
a1e93e7344 修改GM 2025-01-08 11:19:31 +08:00
hahwu
ddddc06c9f 修复好友搜索bug 2025-01-08 11:13:36 +08:00
hahwu
b0a98d1680 修复玩家基础信息 2025-01-08 11:13:36 +08:00
hahwu
a235b772b4 修复好友搜索bug 2025-01-08 11:10:54 +08:00
hahwu
c9b64db2bf 修复玩家基础信息 2025-01-08 11:03:21 +08:00
hahwu
1cf3bef001 Merge branch 'sdk' into online 2025-01-07 16:58:19 +08:00
hahwu
274d730c0b Merge branch 'develop' into sdk 2025-01-07 14:09:38 +08:00
hahwu
61ad4fdab2 宝箱雨改版 2025-01-06 18:10:11 +08:00
hahwu
bb4f3716ce 修复锦标赛bug 2025-01-06 17:58:43 +08:00
hahwu
ef325d3b49 修复锦标赛bug 2025-01-06 17:56:47 +08:00
hahwu
b328f9b6ea Merge branch 'develop' into sdk 2025-01-06 16:41:32 +08:00
hahwu
4e132222eb 修复锦标赛bug 2025-01-06 16:31:16 +08:00
hahwu
6039251df3 修复锦标赛bug 2025-01-06 16:30:57 +08:00
hahwu
51f790cd62 宝箱雨改版 2025-01-06 16:22:55 +08:00
hahwu
662f61b437 宝箱雨改版 2025-01-06 15:44:11 +08:00
hahwu
901187b32c 卡牌图鉴 2025-01-06 14:37:41 +08:00
hahwu
ba3043e99b 卡牌图鉴 2025-01-06 14:37:30 +08:00
hahwu
0bc2dd257d 增加订单日志 2025-01-03 17:51:20 +08:00
hahwu
ace4a53641 增加订单日志 2025-01-03 17:24:47 +08:00
hahwu
16e4a7dd5a 禁用接口 2025-01-03 16:24:15 +08:00
hahwu
b2b9d8c818 禁用接口 2025-01-03 16:24:05 +08:00
hahwu
6350a86518 修改日志队列的长度 2025-01-03 15:19:06 +08:00
hahwu
d79ff003ca 修改日志队列的长度 2025-01-03 15:17:36 +08:00
hahwu
0e30ce57e7 kafka重连 2025-01-03 15:12:47 +08:00
hahwu
9e46a815cf kafka重连 2025-01-03 15:12:07 +08:00
hahwu
4b6e5839ba 取消明文密码 2025-01-02 16:32:08 +08:00
hahwu
e6a87a6c04 配置重载优化 2025-01-02 16:08:26 +08:00
hahwu
db575f5bc0 修改DB配置 2025-01-02 10:16:15 +08:00
hahwu
541c4b6a9b 增加功能解锁配置管理 2025-01-02 10:01:27 +08:00
hahwu
e51aecbba8 增加功能解锁配置管理 2025-01-02 10:01:16 +08:00
hahwu
094d646c8b 增加功能解锁配置管理 2025-01-02 10:00:51 +08:00
hahwu
a2f9587bc6 订单bug修改 2024-12-31 19:07:15 +08:00
hahwu
18cd5d056e 修复订单bug 2024-12-31 19:07:14 +08:00
hahwu
3d55a709f0 订单bug修改 2024-12-31 19:06:33 +08:00
hahwu
9702ad11b0 增加周更新GM 2024-12-31 17:32:39 +08:00
hahwu
6f19443383 增加周更新GM 2024-12-31 17:32:29 +08:00
hahwu
9a748cd008 增加周更新GM 2024-12-31 17:29:53 +08:00
hahwu
1c63e7b700 日常任务周进度清理 2024-12-31 17:28:35 +08:00
hahwu
19bd0ac003 日常任务周进度清理 2024-12-31 17:28:21 +08:00
hahwu
8312df445d 后台开发 2024-12-31 17:28:02 +08:00
hahwu
db07de59f9 日常任务周进度清理 2024-12-31 17:27:37 +08:00
hahwu
c310b60eca 代码优化 2024-12-31 14:38:22 +08:00
hahwu
8d8f6bf38b 修改变量名错误 2024-12-27 11:21:09 +08:00
hahwu
ab20b20aa3 修改变量名错误 2024-12-27 11:20:59 +08:00
hahwu
e27526c635 修改变量名错误 2024-12-27 11:20:14 +08:00
hahwu
b0c687d0ad 增加猫咪旋风类型 2024-12-27 10:59:34 +08:00
hahwu
504a92cb43 优化卡牌图鉴初始化 2024-12-27 09:50:43 +08:00
hahwu
52f9e87875 优化卡牌图鉴初始化 2024-12-27 09:50:32 +08:00
hahwu
ccb5cc44ef 优化卡牌图鉴初始化 2024-12-27 09:49:11 +08:00
hahwu
c807bdcdc1 屏蔽bouns 2024-12-26 19:11:34 +08:00
hahwu
65790cbb02 卡牌图鉴 2024-12-26 19:05:59 +08:00
hahwu
43a93ba49e 屏蔽触发式订单 2024-12-26 18:50:00 +08:00
hahwu
281b8d0565 修复订单bug 2024-12-26 18:42:25 +08:00
hahwu
55f2eff714 Merge branch 'develop' into online 2024-12-26 17:34:33 +08:00
hahwu
e4e02667ce Merge branch 'develop' into sdk 2024-12-26 17:34:11 +08:00
hahwu
01931bb1a2 增加客户端日志接口 2024-12-26 17:29:46 +08:00
hahwu
64f799102f 卡牌图鉴 2024-12-26 15:16:35 +08:00
hahwu
f35c05cc12 宠物宝藏修改 2024-12-26 14:20:13 +08:00
hahwu
d9022408fc 玩家数据bug优化 2024-12-26 12:10:16 +08:00
hahwu
aeccd787f0 bouns修复 2024-12-26 12:08:58 +08:00
hahwu
2260462017 卡牌图鉴 2024-12-26 11:59:01 +08:00
hahwu
b0630b1164 玩家数据bug优化 2024-12-26 11:49:36 +08:00
hahwu
01f1009473 卡牌图鉴 2024-12-26 11:49:15 +08:00
hahwu
7146014b0f 玩家数据bug优化 2024-12-26 11:49:01 +08:00
hahwu
9b83ca5946 玩家数据bug优化 2024-12-26 11:18:03 +08:00
hahwu
e3752d3f08 优化playroom翻牌逻辑 2024-12-26 11:13:40 +08:00
hahwu
41d8b18551 玩家数据bug优化 2024-12-26 10:49:32 +08:00
hahwu
1b729145fe 玩家数据bug优化 2024-12-26 10:47:05 +08:00
hahwu
5a5b3a287a 增加客户端日志接口 2024-12-26 10:46:04 +08:00
hahwu
621236ea5e 玩家数据bug优化 2024-12-26 10:12:55 +08:00
hahwu
84de8d99f1 玩家数据bug优化 2024-12-26 10:12:08 +08:00
hahwu
2dc1515d48 限时事件优化 2024-12-26 10:11:48 +08:00
hahwu
c5f7ff602d 限时事件bounes优化 2024-12-25 17:34:52 +08:00
hahwu
7e03391514 限时事件,playroom优化 2024-12-25 17:07:08 +08:00
hahwu
8d71d97035 Merge branch 'develop' into online 2024-12-25 15:28:56 +08:00
hahwu
7dff488a44 bug修复 2024-12-25 15:23:53 +08:00
hahwu
0d7eea6356 优化系统bug 2024-12-25 11:11:06 +08:00
hahwu
d5325f59e5 优化系统bug 2024-12-24 18:53:37 +08:00
hahwu
3d63153d66 修复bug 2024-12-24 12:04:59 +08:00
hahwu
a2139b3ad4 修复bug 2024-12-23 17:12:15 +08:00
hahwu
fcf3be65be 宠物宝藏 2024-12-23 11:35:13 +08:00
hahwu
2ac0f00d00 playroom 优化 2024-12-19 12:15:02 +08:00
hahwu
5fdbc830b9 playroom 优化 2024-12-19 12:11:51 +08:00
hahwu
b96b200c47 playroom 优化 2024-12-19 12:09:35 +08:00
hahwu
4b4f101369 优化日志写入 2024-12-18 15:25:39 +08:00
hahwu
2a4b54f6c6 playroom修改 2024-12-18 14:47:32 +08:00
hahwu
971bd4d272 修复playroom bug 2024-12-18 09:51:13 +08:00
hahwu
34999b3ec3 playroom bug修复 2024-12-18 09:51:13 +08:00
hahwu
0634aaabcb playroom接口 2024-12-18 09:51:12 +08:00
hahwu
df99febd5f kafka 2024-12-18 09:50:37 +08:00
hahwu
6104d52803 修复playroom bug 2024-12-17 16:26:54 +08:00
hahwu
fc67916525 playroom bug修复 2024-12-17 15:03:09 +08:00
hahwu
4ea86a221e playroom接口 2024-12-17 09:52:54 +08:00
hahwu
d30a2a109b 玩家回归奖励 2024-12-16 17:44:14 +08:00
hahwu
fe97b5c31f 玩家回归奖励 2024-12-16 17:43:40 +08:00
hahwu
6900b807ba 屏蔽限时事件转盘 2024-12-16 15:16:44 +08:00
hahwu
e63ca32171 优化 2024-12-16 15:04:03 +08:00
hahwu
b5eb216860 bug修复 2024-12-13 16:16:50 +08:00
hahwu
7e794dc2dc bug修复 2024-12-12 19:03:45 +08:00
hahwu
7c51add1a1 修复playroom, 商店bug 2024-12-12 18:29:57 +08:00
hahwu
3e809a4e7d 修复棋子商店bug, 发射器退役优化 2024-12-12 15:44:47 +08:00
hahwu
417024ce88 修复12点更新bug 2024-12-12 14:24:54 +08:00
hahwu
a88818c0d8 修复发射器退役bug, 锦标赛bug,优化0点更新逻辑 2024-12-12 14:22:15 +08:00
hahwu
a020d44b5c 修改playroom bug 2024-12-12 10:36:12 +08:00
hahwu
f4268d781a playroom, 发射器退役 2024-12-11 17:33:20 +08:00
hahwu
e5a7ae7182 更新playroom 2024-12-10 17:56:01 +08:00
hahwu
edfb3acea7 三段竞赛 2024-12-04 16:36:49 +08:00
hahwu
d3d7e44f2e 修复数据存储和订单逻辑 2024-12-04 15:03:08 +08:00
hahwu
5134ceb766 猜颜色活动 2024-12-04 10:23:56 +08:00
hahwu
0e65949997 更新挖矿功能 2024-12-03 15:30:16 +08:00
hahwu
a58675074a 更新项目 2024-12-02 16:42:56 +08:00
hahwu
859433eee7 代码整理 2024-11-29 17:10:47 +08:00
hahwu
83f33f24ec 项目更新 2024-11-29 15:05:48 +08:00
hahwu
f3d2f96181 项目更新 2024-11-29 10:51:04 +08:00
hahwu
3df1b09ac6 优化订单生成 2024-11-28 10:26:29 +08:00
hahwu
fbf8a5c257 更新代码 2024-11-27 19:17:32 +08:00
hahwu
0ab62a2a65 更新代码 2024-11-27 14:25:52 +08:00
hahwu
7655b2c96f 锦标赛优化 2024-11-26 17:17:14 +08:00
hahwu
1e345f4ca1 增加一键装饰 2024-11-25 18:22:29 +08:00
hahwu
6c05584404 万能卡 2024-11-25 17:29:59 +08:00
hahwu
10c0d7bea9 卡牌交换 2024-11-25 16:35:38 +08:00
hahwu
891d9f40c1 项目更新 2024-11-25 15:01:25 +08:00
hahwu
a516884a65 更新文件 2024-11-22 15:43:28 +08:00
2208 changed files with 40044 additions and 1015215 deletions

21
.gitignore vendored
View File

@ -10,3 +10,24 @@ src/server/log/
src/server/*.exe
src/server/gamedata/config/*.json
src/server/unit_test.go
src/server/teLog/*
src/server/teLog/log.2024-11-28
src/server/logs/ga_log/*.log
src/server/goroutine.prof
src/server/heap_after.pb.gz
src/server/test/logs/*
src/server/test/teLog/*
src/server/*.exe*
src/server/msg/Gameapi.pb.go
src/server/gen-go
src/server/GeoLite2-Country
src/server/test/GeoLite2-Country
src/server/msg/Gameapi_grpc.pb.go
src/server/unit_test/*.exe*
src/server/unit_test/log*
src/server/msg/meowmentnet
src/server/.github/agents/merge2-server-lead.agent.md
src/server/runtime/*
src/server/unit_test/runtime/*
src/server/game/logs/*
D/Github/rag_kb/logs/reindex.log

View File

@ -0,0 +1,174 @@
---
name: acquire-codebase-knowledge
description: 'Use this skill when the user explicitly asks to map, document, or onboard into an existing codebase. Trigger for prompts like "map this codebase", "document this architecture", "onboard me to this repo", or "create codebase docs". Do not trigger for routine feature implementation, bug fixes, or narrow code edits unless the user asks for repository-level discovery.'
license: MIT
compatibility: 'Cross-platform. Requires Python 3.8+ and git. Run scripts/scan.py from the target project root.'
metadata:
version: "1.3"
enhancements:
- Multi-language manifest detection (25+ languages supported)
- CI/CD pipeline detection (10+ platforms)
- Container & orchestration detection
- Code metrics by language
- Security & compliance config detection
- Performance testing markers
argument-hint: 'Optional: specific area to focus on, e.g. "architecture only", "testing and concerns"'
---
# Acquire Codebase Knowledge
Produces seven populated documents in `docs/codebase/` covering everything needed to work effectively on the project. Only document what is verifiable from files or terminal output — never infer or assume.
## Output Contract (Required)
Before finishing, all of the following must be true:
1. Exactly these files exist in `docs/codebase/`: `STACK.md`, `STRUCTURE.md`, `ARCHITECTURE.md`, `CONVENTIONS.md`, `INTEGRATIONS.md`, `TESTING.md`, `CONCERNS.md`.
2. Every claim is traceable to source files, config, or terminal output.
3. Unknowns are marked as `[TODO]`; intent-dependent decisions are marked `[ASK USER]`.
4. Every document includes a short "evidence" list with concrete file paths.
5. Final response includes numbered `[ASK USER]` questions and intent-vs-reality divergences.
## Workflow
Copy and track this checklist:
```
- [ ] Phase 1: Run scan, read intent documents
- [ ] Phase 2: Investigate each documentation area
- [ ] Phase 3: Populate all seven docs in docs/codebase/
- [ ] Phase 4: Validate docs, present findings, resolve all [ASK USER] items
```
## Focus Area Mode
If the user supplies a focus area (for example: "architecture only" or "testing and concerns"):
1. Always run Phase 1 in full.
2. Fully complete focus-area documents first.
3. For non-focus documents not yet analyzed, keep required sections present and mark unknowns as `[TODO]`.
4. Still run the Phase 4 validation loop on all seven documents before final output.
### Phase 1: Scan and Read Intent
1. Run the scan script from the target project root:
```bash
python3 "$SKILL_ROOT/scripts/scan.py" --output docs/codebase/.codebase-scan.txt
```
Where `$SKILL_ROOT` is the absolute path to the skill folder. Works on Windows, macOS, and Linux.
**Quick start:** If you have the path inline:
```bash
python3 /absolute/path/to/skills/acquire-codebase-knowledge/scripts/scan.py --output docs/codebase/.codebase-scan.txt
```
2. Search for `PRD`, `TRD`, `README`, `ROADMAP`, `SPEC`, `DESIGN` files and read them.
3. Summarise the stated project intent before reading any source code.
### Phase 2: Investigate
Use the scan output to answer questions for each of the seven templates. Load [`references/inquiry-checkpoints.md`](references/inquiry-checkpoints.md) for the full per-template question list.
If the stack is ambiguous (multiple manifest files, unfamiliar file types, no `package.json`), load [`references/stack-detection.md`](references/stack-detection.md).
### Phase 3: Populate Templates
Copy each template from `assets/templates/` into `docs/codebase/`. Fill in this order:
1. [STACK.md](assets/templates/STACK.md) — language, runtime, frameworks, all dependencies
2. [STRUCTURE.md](assets/templates/STRUCTURE.md) — directory layout, entry points, key files
3. [ARCHITECTURE.md](assets/templates/ARCHITECTURE.md) — layers, patterns, data flow
4. [CONVENTIONS.md](assets/templates/CONVENTIONS.md) — naming, formatting, error handling, imports
5. [INTEGRATIONS.md](assets/templates/INTEGRATIONS.md) — external APIs, databases, auth, monitoring
6. [TESTING.md](assets/templates/TESTING.md) — frameworks, file organization, mocking strategy
7. [CONCERNS.md](assets/templates/CONCERNS.md) — tech debt, bugs, security risks, perf bottlenecks
Use `[TODO]` for anything that cannot be determined from code. Use `[ASK USER]` where the right answer requires team intent.
### Phase 4: Validate, Repair, Verify
Run this mandatory validation loop before finalizing:
1. Validate each doc against `references/inquiry-checkpoints.md`.
2. For each non-trivial claim, confirm at least one evidence reference exists.
3. If any required section is missing or unsupported:
- Fix the document.
- Re-run validation.
4. Repeat until all seven docs pass.
Then present a summary of all seven documents, list every `[ASK USER]` item as a numbered question, and highlight any Intent vs. Reality divergences from Phase 1.
Validation pass criteria:
- No unsupported claims.
- No empty required sections.
- Unknowns use `[TODO]` rather than assumptions.
- Team-intent gaps are explicitly marked `[ASK USER]`.
---
## Gotchas
**Monorepos:** Root `package.json` may have no source — check for `workspaces`, `packages/`, or `apps/` directories. Each workspace may have independent dependencies and conventions. Map each sub-package separately.
**Outdated README:** README often describes intended architecture, not the current one. Cross-reference with actual file structure before treating any README claim as fact.
**TypeScript path aliases:** `tsconfig.json` `paths` config means imports like `@/foo` don't map directly to the filesystem. Map aliases to real paths before documenting structure.
**Generated/compiled output:** Never document patterns from `dist/`, `build/`, `generated/`, `.next/`, `out/`, or `__pycache__/`. These are artefacts — document source conventions only.
**`.env.example` reveals required config:** Secrets are never committed. Read `.env.example`, `.env.template`, or `.env.sample` to discover required environment variables.
**`devDependencies` ≠ production stack:** Only `dependencies` (or equivalent, e.g. `[tool.poetry.dependencies]`) runs in production. Document linters, formatters, and test frameworks separately as dev tooling.
**Test TODOs ≠ production debt:** TODOs inside `test/`, `tests/`, `__tests__/`, or `spec/` are coverage gaps, not production technical debt. Separate them in `CONCERNS.md`.
**High-churn files = fragile areas:** Files appearing most in recent git history have the highest modification rate and likely hidden complexity. Always note them in `CONCERNS.md`.
---
## Anti-Patterns
| ❌ Don't | ✅ Do instead |
|---------|--------------|
| "Uses Clean Architecture with Domain/Data layers." (when no such directories exist) | State only what directory structure actually shows. |
| "This is a Next.js project." (without checking `package.json`) | Check `dependencies` first. State what's actually there. |
| Guess the database from a variable name like `dbUrl` | Check manifest for `pg`, `mysql2`, `mongoose`, `prisma`, etc. |
| Document `dist/` or `build/` naming patterns as conventions | Source files only. |
---
## Enhanced Scan Output Sections
The `scan.py` script now produce the following sections in addition to the original output:
- **CODE METRICS** — Total files, lines of code by language, largest files (complexity signals)
- **CI/CD PIPELINES** — Detected GitHub Actions, GitLab CI, Jenkins, CircleCI, etc.
- **CONTAINERS & ORCHESTRATION** — Docker, Docker Compose, Kubernetes, Vagrant configs
- **SECURITY & COMPLIANCE** — Snyk, Dependabot, SECURITY.md, SBOM, security policies
- **PERFORMANCE & TESTING** — Benchmark configs, profiling markers, load testing tools
Use these sections during Phase 2 to inform investigation questions and identify tool-specific patterns.
---
## Bundled Assets
| Asset | When to load |
|-------|-------------|
| [`scripts/scan.py`](scripts/scan.py) | Phase 1 — run first, before reading any code (Python 3.8+ required) |
| [`references/inquiry-checkpoints.md`](references/inquiry-checkpoints.md) | Phase 2 — load for per-template investigation questions |
| [`references/stack-detection.md`](references/stack-detection.md) | Phase 2 — only if stack is ambiguous |
| [`assets/templates/STACK.md`](assets/templates/STACK.md) | Phase 3 step 1 |
| [`assets/templates/STRUCTURE.md`](assets/templates/STRUCTURE.md) | Phase 3 step 2 |
| [`assets/templates/ARCHITECTURE.md`](assets/templates/ARCHITECTURE.md) | Phase 3 step 3 |
| [`assets/templates/CONVENTIONS.md`](assets/templates/CONVENTIONS.md) | Phase 3 step 4 |
| [`assets/templates/INTEGRATIONS.md`](assets/templates/INTEGRATIONS.md) | Phase 3 step 5 |
| [`assets/templates/TESTING.md`](assets/templates/TESTING.md) | Phase 3 step 6 |
| [`assets/templates/CONCERNS.md`](assets/templates/CONCERNS.md) | Phase 3 step 7 |
Template usage mode:
- Default mode: complete only the "Core Sections (Required)" in each template.
- Extended mode: add optional sections only when the repo complexity justifies them.

View File

@ -0,0 +1,49 @@
# Architecture
## Core Sections (Required)
### 1) Architectural Style
- Primary style: [layered/feature/event-driven/other]
- Why this classification: [short evidence-backed rationale]
- Primary constraints: [2-3 constraints that shape design]
### 2) System Flow
```text
[entry] -> [processing] -> [domain logic] -> [data/integration] -> [response/output]
```
Describe the flow in 4-6 steps using file-backed evidence.
### 3) Layer/Module Responsibilities
| Layer or module | Owns | Must not own | Evidence |
|-----------------|------|--------------|----------|
| [name] | [responsibility] | [non-responsibility] | [file] |
### 4) Reused Patterns
| Pattern | Where found | Why it exists |
|---------|-------------|---------------|
| [singleton/repository/adapter/etc] | [path] | [reason] |
### 5) Known Architectural Risks
- [Risk 1 + impact]
- [Risk 2 + impact]
### 6) Evidence
- [path/to/entrypoint]
- [path/to/main-layer-files]
- [path/to/data-or-integration-layer]
## Extended Sections (Optional)
Add only when needed:
- Startup or initialization order details
- Async/event topology diagrams
- Anti-pattern catalog with refactoring paths
- Failure-mode analysis and resilience posture

View File

@ -0,0 +1,56 @@
# Codebase Concerns
## Core Sections (Required)
### 1) Top Risks (Prioritized)
| Severity | Concern | Evidence | Impact | Suggested action |
|----------|---------|----------|--------|------------------|
| [high/med/low] | [issue] | [file or scan output] | [impact] | [next action] |
### 2) Technical Debt
List the most important debt items only.
| Debt item | Why it exists | Where | Risk if ignored | Suggested fix |
|-----------|---------------|-------|-----------------|---------------|
| [item] | [reason] | [path] | [risk] | [fix] |
### 3) Security Concerns
| Risk | OWASP category (if applicable) | Evidence | Current mitigation | Gap |
|------|--------------------------------|----------|--------------------|-----|
| [risk] | [A01/A03/etc or N/A] | [path] | [what exists] | [what is missing] |
### 4) Performance and Scaling Concerns
| Concern | Evidence | Current symptom | Scaling risk | Suggested improvement |
|---------|----------|-----------------|-------------|-----------------------|
| [issue] | [path/metric] | [symptom] | [risk] | [action] |
### 5) Fragile/High-Churn Areas
| Area | Why fragile | Churn signal | Safe change strategy |
|------|-------------|-------------|----------------------|
| [path] | [reason] | [recent churn evidence] | [approach] |
### 6) `[ASK USER]` Questions
Add unresolved intent-dependent questions as a numbered list.
1. [ASK USER] [question]
### 7) Evidence
- [scan output section reference]
- [path/to/code-file]
- [path/to/config-or-history-evidence]
## Extended Sections (Optional)
Add only when needed:
- Full bug inventory
- Component-level remediation roadmap
- Cost/effort estimates by concern
- Dependency-risk and ownership mapping

View File

@ -0,0 +1,52 @@
# Coding Conventions
## Core Sections (Required)
### 1) Naming Rules
| Item | Rule | Example | Evidence |
|------|------|---------|----------|
| Files | [RULE] | [EXAMPLE] | [FILE] |
| Functions/methods | [RULE] | [EXAMPLE] | [FILE] |
| Types/interfaces | [RULE] | [EXAMPLE] | [FILE] |
| Constants/env vars | [RULE] | [EXAMPLE] | [FILE] |
### 2) Formatting and Linting
- Formatter: [TOOL + CONFIG FILE]
- Linter: [TOOL + CONFIG FILE]
- Most relevant enforced rules: [RULE_1], [RULE_2], [RULE_3]
- Run commands: [COMMANDS]
### 3) Import and Module Conventions
- Import grouping/order: [RULE]
- Alias vs relative import policy: [RULE]
- Public exports/barrel policy: [RULE]
### 4) Error and Logging Conventions
- Error strategy by layer: [SHORT SUMMARY]
- Logging style and required context fields: [SUMMARY]
- Sensitive-data redaction rules: [SUMMARY]
### 5) Testing Conventions
- Test file naming/location rule: [RULE]
- Mocking strategy norm: [RULE]
- Coverage expectation: [RULE or TODO]
### 6) Evidence
- [path/to/lint-config]
- [path/to/format-config]
- [path/to/representative-source-file]
## Extended Sections (Optional)
Add only for large or inconsistent codebases:
- Layer-specific error handling matrix
- Language-specific strictness options
- Repo-specific commit/branching conventions
- Known convention violations to clean up

View File

@ -0,0 +1,48 @@
# External Integrations
## Core Sections (Required)
### 1) Integration Inventory
| System | Type (API/DB/Queue/etc) | Purpose | Auth model | Criticality | Evidence |
|--------|---------------------------|---------|------------|-------------|----------|
| [name] | [type] | [purpose] | [auth] | [high/med/low] | [file] |
### 2) Data Stores
| Store | Role | Access layer | Key risk | Evidence |
|-------|------|--------------|----------|----------|
| [db/cache/etc] | [role] | [module] | [risk] | [file] |
### 3) Secrets and Credentials Handling
- Credential sources: [env/secrets manager/config]
- Hardcoding checks: [result]
- Rotation or lifecycle notes: [known/unknown]
### 4) Reliability and Failure Behavior
- Retry/backoff behavior: [implemented/none/partial]
- Timeout policy: [where configured]
- Circuit-breaker or fallback behavior: [if any]
### 5) Observability for Integrations
- Logging around external calls: [yes/no + where]
- Metrics/tracing coverage: [yes/no + where]
- Missing visibility gaps: [list]
### 6) Evidence
- [path/to/integration-wrapper]
- [path/to/config-or-env-template]
- [path/to/monitoring-or-logging-config]
## Extended Sections (Optional)
Add only when needed:
- Endpoint-by-endpoint catalog
- Auth flow sequence diagrams
- SLA/SLO per integration
- Region/failover topology notes

View File

@ -0,0 +1,56 @@
# Technology Stack
## Core Sections (Required)
### 1) Runtime Summary
| Area | Value | Evidence |
|------|-------|----------|
| Primary language | [VALUE] | [FILE_PATH] |
| Runtime + version | [VALUE] | [FILE_PATH] |
| Package manager | [VALUE] | [FILE_PATH] |
| Module/build system | [VALUE] | [FILE_PATH] |
### 2) Production Frameworks and Dependencies
List only high-impact production dependencies (frameworks, data, transport, auth).
| Dependency | Version | Role in system | Evidence |
|------------|---------|----------------|----------|
| [NAME] | [VERSION] | [ROLE] | [FILE_PATH] |
### 3) Development Toolchain
| Tool | Purpose | Evidence |
|------|---------|----------|
| [TOOL] | [LINT/FORMAT/TEST/BUILD] | [FILE_PATH] |
### 4) Key Commands
```bash
[install command]
[build command]
[test command]
[lint command]
```
### 5) Environment and Config
- Config sources: [LIST FILES]
- Required env vars: [VAR_1], [VAR_2], [TODO]
- Deployment/runtime constraints: [SHORT NOTE]
### 6) Evidence
- [path/to/manifest]
- [path/to/runtime-config]
- [path/to/build-or-ci-config]
## Extended Sections (Optional)
Add only when needed for complex repos:
- Full dependency taxonomy by category
- Detailed compiler/runtime flags
- Environment matrix (dev/stage/prod)
- Process manager and container runtime details

View File

@ -0,0 +1,44 @@
# Codebase Structure
## Core Sections (Required)
### 1) Top-Level Map
List only meaningful top-level directories and files.
| Path | Purpose | Evidence |
|------|---------|----------|
| [path/] | [purpose] | [source] |
### 2) Entry Points
- Main runtime entry: [FILE]
- Secondary entry points (worker/cli/jobs): [FILES or NONE]
- How entry is selected (script/config): [NOTE]
### 3) Module Boundaries
| Boundary | What belongs here | What must not be here |
|----------|-------------------|------------------------|
| [module/layer] | [responsibility] | [forbidden logic] |
### 4) Naming and Organization Rules
- File naming pattern: [kebab/camel/Pascal + examples]
- Directory organization pattern: [feature/layer/domain]
- Import aliasing or path conventions: [RULE]
### 5) Evidence
- [path/to/root-tree-source]
- [path/to/entry-config]
- [path/to/key-module]
## Extended Sections (Optional)
Add only when repository complexity requires it:
- Subdirectory deep maps by feature/layer
- Middleware/boot order details
- Generated-vs-source layout boundaries
- Monorepo workspace-level structure maps

View File

@ -0,0 +1,57 @@
# Testing Patterns
## Core Sections (Required)
### 1) Test Stack and Commands
- Primary test framework: [NAME + VERSION]
- Assertion/mocking tools: [TOOLS]
- Commands:
```bash
[run all tests]
[run unit tests]
[run integration/e2e tests]
[run coverage]
```
### 2) Test Layout
- Test file placement pattern: [co-located/tests folder/etc]
- Naming convention: [pattern]
- Setup files and where they run: [paths]
### 3) Test Scope Matrix
| Scope | Covered? | Typical target | Notes |
|-------|----------|----------------|-------|
| Unit | [yes/no] | [modules/services] | [notes] |
| Integration | [yes/no] | [API/data boundaries] | [notes] |
| E2E | [yes/no] | [user flows] | [notes] |
### 4) Mocking and Isolation Strategy
- Main mocking approach: [module/class/network]
- Isolation guarantees: [what is reset and when]
- Common failure mode in tests: [short note]
### 5) Coverage and Quality Signals
- Coverage tool + threshold: [value or TODO]
- Current reported coverage: [value or TODO]
- Known gaps/flaky areas: [list]
### 6) Evidence
- [path/to/test-config]
- [path/to/representative-test-file]
- [path/to/ci-or-coverage-config]
## Extended Sections (Optional)
Add only when needed:
- Framework-specific suite patterns
- Detailed mock recipes per dependency type
- Historical flaky test catalog
- Test performance bottlenecks and optimization ideas

View File

@ -0,0 +1,70 @@
# Inquiry Checkpoints
Per-template investigation questions for Phase 2 of the acquire-codebase-knowledge workflow. For each template area, look for answers in the scan output first, then read source files to fill gaps.
---
## 1. STACK.md — Tech Stack
- What is the primary language and exact version? (check `.nvmrc`, `go.mod`, `pyproject.toml`, Docker `FROM` line)
- What package manager is used? (`npm`, `yarn`, `pnpm`, `go mod`, `pip`, `uv`)
- What are the core runtime frameworks? (web server, ORM, DI container)
- What do `dependencies` (production) vs `devDependencies` (dev tooling) contain?
- Is there a Docker image and what base image does it use?
- What are the key scripts in `package.json` / `Makefile` / `pyproject.toml`?
## 2. STRUCTURE.md — Directory Layout
- Where does source code live? (usually `src/`, `lib/`, or project root for Go)
- What are the entry points? (check `main` in `package.json`, `scripts.start`, `cmd/main.go`, `app.py`)
- What is the stated purpose of each top-level directory?
- Are there non-obvious directories (e.g., `eng/`, `platform/`, `infra/`)?
- Are there hidden config directories (`.github/`, `.vscode/`, `.husky/`)?
- What naming conventions do directories follow? (camelCase, kebab-case, domain-based vs layer-based)
## 3. ARCHITECTURE.md — Patterns
- Is the code organized by layer (controllers → services → repos) or by feature?
- What is the primary data flow? Trace one request or command from entry to data store.
- Are there singletons, dependency injection patterns, or explicit initialization order requirements?
- Are there background workers, queues, or event-driven components?
- What design patterns appear repeatedly? (Factory, Repository, Decorator, Strategy)
## 4. CONVENTIONS.md — Coding Standards
- What is the file naming convention? (check 10+ files — camelCase, kebab-case, PascalCase)
- What is the function and variable naming convention?
- Are private methods/fields prefixed (e.g., `_methodName`, `#field`)?
- What linter and formatter are configured? (check `.eslintrc`, `.prettierrc`, `golangci.yml`)
- What are the TypeScript strictness settings? (`strict`, `noImplicitAny`, etc.)
- How are errors handled at each layer? (throw vs. return structured error)
- What logging library is used and what is the log message format?
- How are imports organized? (barrel exports, path aliases, grouping rules)
## 5. INTEGRATIONS.md — External Services
- What external APIs are called? (search for `axios.`, `fetch(`, `http.Get(`, base URLs in constants)
- How are credentials stored and accessed? (`.env`, secrets manager, env vars)
- What databases are connected? (check manifest for `pg`, `mongoose`, `prisma`, `typeorm`, `sqlalchemy`)
- Is there an API gateway, service mesh, or proxy between the app and external services?
- What monitoring or observability tools are used? (APM, Prometheus, logging pipeline)
- Are there message queues or event buses? (Kafka, RabbitMQ, SQS, Pub/Sub)
## 6. TESTING.md — Test Setup
- What test runner is configured? (check `scripts.test` in `package.json`, `pytest.ini`, `go test`)
- Where are test files located? (alongside source, in `tests/`, in `__tests__/`)
- What assertion library is used? (Jest expect, Chai, pytest assert)
- How are external dependencies mocked? (jest.mock, dependency injection, fixtures)
- Are there integration tests that hit real services vs. unit tests with mocks?
- Is there a coverage threshold enforced? (check `jest.config.js`, `.nycrc`, `pyproject.toml`)
## 7. CONCERNS.md — Known Issues
- How many TODOs/FIXMEs/HACKs are in production code? (see scan output)
- Which files have the highest git churn in the last 90 days? (see scan output)
- Are there any files over 500 lines that mix multiple responsibilities?
- Do any services make sequential calls that could be parallelized?
- Are there hardcoded values (URLs, IDs, magic numbers) that should be config?
- What security risks exist? (missing input validation, raw error messages exposed to clients, missing auth checks)
- Are there performance patterns that don't scale? (N+1 queries, in-memory caches in multi-instance setups)

View File

@ -0,0 +1,131 @@
# Stack Detection Reference
Load this file when the tech stack is ambiguous — e.g., multiple manifest files present, unfamiliar file extensions, or no obvious `package.json` / `go.mod`.
---
## Manifest File → Ecosystem
| File | Ecosystem | Key fields to read |
|------|-----------|--------------------|
| `package.json` | Node.js / JavaScript / TypeScript | `dependencies`, `devDependencies`, `scripts`, `main`, `type`, `engines` |
| `go.mod` | Go | Module path, Go version, `require` block |
| `requirements.txt` | Python (pip) | Package list with pinned versions |
| `Pipfile` | Python (pipenv) | `[packages]`, `[dev-packages]`, `[requires]` python version |
| `pyproject.toml` | Python (poetry / uv / hatch) | `[tool.poetry.dependencies]`, `[project]`, `[build-system]` |
| `setup.py` / `setup.cfg` | Python (setuptools, legacy) | `install_requires`, `python_requires` |
| `Cargo.toml` | Rust | `[dependencies]`, `[[bin]]`, `[lib]` |
| `pom.xml` | Java / Kotlin (Maven) | `<dependencies>`, `<artifactId>`, `<groupId>`, `<java.version>` |
| `build.gradle` / `build.gradle.kts` | Java / Kotlin (Gradle) | `dependencies {}`, `sourceCompatibility` |
| `composer.json` | PHP | `require`, `require-dev` |
| `Gemfile` | Ruby | `gem` declarations, `ruby` version constraint |
| `mix.exs` | Elixir | `deps/0`, `elixir: "~> X.Y"` |
| `pubspec.yaml` | Dart / Flutter | `dependencies`, `dev_dependencies`, `environment.sdk` |
| `*.csproj` | .NET / C# | `<PackageReference>`, `<TargetFramework>` |
| `*.sln` | .NET solution | References multiple `.csproj` projects |
| `deno.json` / `deno.jsonc` | Deno (TypeScript runtime) | `imports`, `tasks` |
| `bun.lockb` | Bun (JavaScript runtime) | Binary lockfile — check `package.json` for deps |
---
## Language Runtime Version Detection
| Language | Where to find the version |
|----------|--------------------------|
| Node.js | `.nvmrc`, `.node-version`, `engines.node` in `package.json`, Docker `FROM node:X` |
| Python | `.python-version`, `pyproject.toml [requires-python]`, Docker `FROM python:X` |
| Go | First line of `go.mod` (`go 1.21`) |
| Java | `<java.version>` in `pom.xml`, `sourceCompatibility` in `build.gradle`, Docker `FROM eclipse-temurin:X` |
| Ruby | `.ruby-version`, `Gemfile` `ruby 'X.Y.Z'` |
| Rust | `rust-toolchain.toml`, `rust-toolchain` file |
| .NET | `<TargetFramework>` in `.csproj` (e.g., `net8.0`) |
---
## Framework Detection (Node.js / TypeScript)
| Dependency in `package.json` | Framework |
|-----------------------------|-----------|
| `express` | Express.js (minimal HTTP server) |
| `fastify` | Fastify (high-performance HTTP server) |
| `next` | Next.js (SSR/SSG React — check for `pages/` or `app/` directory) |
| `nuxt` | Nuxt.js (SSR/SSG Vue) |
| `@nestjs/core` | NestJS (opinionated Node.js framework with DI) |
| `koa` | Koa (middleware-focused, no built-in router) |
| `@hapi/hapi` | Hapi |
| `@trpc/server` | tRPC (type-safe API without REST/GraphQL schemas) |
| `routing-controllers` | routing-controllers (decorator-based Express wrapper) |
| `typeorm` | TypeORM (SQL ORM with decorators) |
| `prisma` | Prisma (type-safe ORM, check `prisma/schema.prisma`) |
| `mongoose` | Mongoose (MongoDB ODM) |
| `sequelize` | Sequelize (SQL ORM) |
| `drizzle-orm` | Drizzle (lightweight SQL ORM) |
| `react` without `next` | Vanilla React SPA (check for `react-router-dom`) |
| `vue` without `nuxt` | Vanilla Vue SPA |
---
## Framework Detection (Python)
| Package | Framework |
|---------|-----------|
| `fastapi` | FastAPI (async REST, auto OpenAPI docs) |
| `flask` | Flask (minimal WSGI web framework) |
| `django` | Django (batteries-included, check `settings.py`) |
| `starlette` | Starlette (ASGI, often used as FastAPI base) |
| `aiohttp` | aiohttp (async HTTP client and server) |
| `sqlalchemy` | SQLAlchemy (SQL ORM; check for `alembic` migrations) |
| `alembic` | Alembic (SQLAlchemy migration tool) |
| `pydantic` | Pydantic (data validation; core to FastAPI) |
| `celery` | Celery (distributed task queue) |
---
## Monorepo Detection
Check these signals in order:
1. `pnpm-workspace.yaml` — pnpm workspaces
2. `lerna.json` — Lerna monorepo
3. `nx.json` — Nx monorepo (also check `workspace.json`)
4. `turbo.json` — Turborepo
5. `rush.json` — Rush (Microsoft monorepo manager)
6. `moon.yml` — Moon
7. `package.json` with `"workspaces": [...]` — npm/yarn workspaces
8. Presence of `packages/`, `apps/`, `libs/`, or `services/` directories with their own `package.json`
If monorepo is detected: each workspace may have **independent** dependencies and conventions. Map each sub-package separately in `STACK.md` and note the monorepo structure in `STRUCTURE.md`.
---
## TypeScript Path Alias Detection
If `tsconfig.json` has a `paths` key, imports with non-relative prefixes are aliases. Map them before documenting structure.
```json
// tsconfig.json example
"paths": {
"@/*": ["./src/*"],
"@components/*": ["./src/components/*"],
"@utils/*": ["./src/utils/*"]
}
```
Imports like `import { foo } from '@/utils/bar'` resolve to `src/utils/bar`. Document as `src/utils/bar`, not `@/utils/bar`.
---
## Docker Base Image → Runtime
If no manifest file is present but a `Dockerfile` exists, the `FROM` line reveals the runtime:
| FROM line pattern | Runtime |
|------------------|---------|
| `FROM node:X` | Node.js X |
| `FROM python:X` | Python X |
| `FROM golang:X` | Go X |
| `FROM eclipse-temurin:X` | Java X (Eclipse Temurin JDK) |
| `FROM mcr.microsoft.com/dotnet/aspnet:X` | .NET X |
| `FROM ruby:X` | Ruby X |
| `FROM rust:X` | Rust X |
| `FROM alpine` (alone) | Check what's installed via `RUN apk add` |

View File

@ -0,0 +1,712 @@
#!/usr/bin/env python3
"""
scan.py Collect project discovery information for the acquire-codebase-knowledge skill.
Run from the project root directory.
Usage: python3 scan.py [OPTIONS]
Options:
--output FILE Write output to FILE instead of stdout
--help Show this message and exit
Exit codes:
0 Success
1 Usage error
"""
import os
import sys
import argparse
import subprocess
import json
from pathlib import Path
from typing import List, Set
import re
TREE_LIMIT = 200
TREE_MAX_DEPTH = 3
TODO_LIMIT = 60
MANIFEST_PREVIEW_LINES = 80
RECENT_COMMITS_LIMIT = 20
CHURN_LIMIT = 20
EXCLUDE_DIRS = {
"node_modules", ".git", "dist", "build", "out", ".next", ".nuxt",
"__pycache__", ".venv", "venv", ".tox", "target", "vendor",
"coverage", ".nyc_output", "generated", ".cache", ".turbo",
".yarn", ".pnp", "bin", "obj"
}
MANIFESTS = [
# JavaScript/Node.js
"package.json", "package-lock.json", "yarn.lock", "pnpm-lock.yaml", "bun.lockb",
"deno.json", "deno.jsonc",
# Python
"requirements.txt", "Pipfile", "Pipfile.lock", "pyproject.toml", "setup.py", "setup.cfg",
"poetry.lock", "pdm.lock", "uv.lock",
# Go
"go.mod", "go.sum",
# Rust
"Cargo.toml", "Cargo.lock",
# Java/Kotlin
"pom.xml", "build.gradle", "build.gradle.kts", "settings.gradle", "settings.gradle.kts",
"gradle.properties",
# PHP/Composer
"composer.json", "composer.lock",
# Ruby
"Gemfile", "Gemfile.lock", "*.gemspec",
# Elixir
"mix.exs", "mix.lock",
# Dart/Flutter
"pubspec.yaml", "pubspec.lock",
# .NET/C#
"*.csproj", "*.sln", "*.slnx", "global.json", "packages.config",
# Swift
"Package.swift", "Package.resolved",
# Scala
"build.sbt", "scala-cli.yml",
# Haskell
"*.cabal", "stack.yaml", "cabal.project", "cabal.project.local",
# OCaml
"dune-project", "opam", "opam.lock",
# Nim
"*.nimble", "nim.cfg",
# Crystal
"shard.yml", "shard.lock",
# R
"DESCRIPTION", "renv.lock",
# Julia
"Project.toml", "Manifest.toml",
# Build systems
"CMakeLists.txt", "Makefile", "GNUmakefile",
"SConstruct", "build.xml",
"BUILD", "BUILD.bazel", "WORKSPACE", "bazel.lock",
"justfile", ".justfile", "Taskfile.yml",
"tox.ini", "Vagrantfile"
]
ENTRY_CANDIDATES = [
# JavaScript/Node.js/TypeScript
"src/index.ts", "src/index.js", "src/index.mjs",
"src/main.ts", "src/main.js", "src/main.py",
"src/app.ts", "src/app.js",
"src/server.ts", "src/server.js",
"index.ts", "index.js", "app.ts", "app.js",
"lib/index.ts", "lib/index.js",
# Go
"main.go", "cmd/main.go", "cmd/*/main.go",
# Python
"main.py", "app.py", "server.py", "run.py", "cli.py",
"src/main.py", "src/__main__.py",
# .NET/C#
"Program.cs", "src/Program.cs", "Main.cs",
# Java
"Main.java", "Application.java", "App.java",
"src/main/java/Main.java",
# Kotlin
"Main.kt", "Application.kt", "App.kt",
# Rust
"src/main.rs", "src/lib.rs",
# Swift
"main.swift", "Package.swift", "Sources/main.swift",
# Ruby
"app.rb", "main.rb", "lib/app.rb",
# PHP
"index.php", "app.php", "public/index.php",
# Go
"cmd/*/main.go",
# Scala
"src/main/scala/Main.scala",
# Haskell
"Main.hs", "app/Main.hs",
# Clojure
"src/core.clj", "-main.clj",
# Elixir
"lib/application.ex", "mix.exs",
]
LINT_FILES = [
".eslintrc", ".eslintrc.json", ".eslintrc.js", ".eslintrc.cjs", ".eslintrc.yml", ".eslintrc.yaml",
"eslint.config.js", "eslint.config.mjs", "eslint.config.cjs",
".prettierrc", ".prettierrc.json", ".prettierrc.js", ".prettierrc.yml",
"prettier.config.js", "prettier.config.mjs",
".editorconfig",
"tsconfig.json", "tsconfig.base.json", "tsconfig.build.json",
".golangci.yml", ".golangci.yaml",
"setup.cfg", ".flake8", ".pylintrc", "mypy.ini",
".rubocop.yml", "phpcs.xml", "phpstan.neon",
"biome.json", "biome.jsonc"
]
ENV_TEMPLATES = [".env.example", ".env.template", ".env.sample", ".env.defaults", ".env.local.example"]
SOURCE_EXTS = [
"ts", "tsx", "js", "jsx", "mjs", "cjs",
"py", "go", "java", "kt", "rb", "php",
"rs", "cs", "cpp", "c", "h", "ex", "exs",
"swift", "scala", "clj", "cljs", "lua",
"vim", "vim", "hs", "ml", "ml", "nim", "cr",
"r", "jl", "groovy", "gradle", "xml", "json"
]
MONOREPO_FILES = ["pnpm-workspace.yaml", "lerna.json", "nx.json", "rush.json", "turbo.json", "moon.yml"]
MONOREPO_DIRS = ["packages", "apps", "libs", "services", "modules"]
CI_CD_CONFIGS = {
".github/workflows": "GitHub Actions",
".gitlab-ci.yml": "GitLab CI",
"Jenkinsfile": "Jenkins",
".circleci/config.yml": "CircleCI",
".travis.yml": "Travis CI",
"azure-pipelines.yml": "Azure Pipelines",
"appveyor.yml": "AppVeyor",
".drone.yml": "Drone CI",
".woodpecker.yml": "Woodpecker CI",
"bitbucket-pipelines.yml": "Bitbucket Pipelines"
}
CONTAINER_FILES = [
"Dockerfile", "docker-compose.yml", "docker-compose.yaml",
".dockerignore", "Dockerfile.*",
"k8s", "kustomization.yaml", "Chart.yaml",
"Vagrantfile", "podman-compose.yml"
]
SECURITY_CONFIGS = [
".snyk", "security.txt", "SECURITY.md",
".dependabot.yml", ".whitesource",
"sbom.json", "sbom.spdx", ".bandit.yaml"
]
PERFORMANCE_MARKERS = [
"benchmark", "bench", "perf.data", ".prof",
"k6.js", "locustfile.py", "jmeter.jmx"
]
def parse_args():
"""Parse command-line arguments."""
parser = argparse.ArgumentParser(
description="Scan the current directory (project root) and output discovery information "
"for the acquire-codebase-knowledge skill.",
add_help=True
)
parser.add_argument(
"--output",
type=str,
help="Write output to FILE instead of stdout"
)
return parser.parse_args()
def should_exclude(path: Path) -> bool:
"""Check if a path should be excluded from scanning."""
return any(part in EXCLUDE_DIRS for part in path.parts)
def get_directory_tree(max_depth: int = TREE_MAX_DEPTH) -> List[str]:
"""Get directory tree up to max_depth."""
files = []
def walk(path: Path, depth: int):
if depth > max_depth or should_exclude(path):
return
try:
for item in sorted(path.iterdir()):
if should_exclude(item):
continue
rel_path = item.relative_to(Path.cwd())
files.append(str(rel_path))
if item.is_dir():
walk(item, depth + 1)
except (PermissionError, OSError):
pass
walk(Path.cwd(), 0)
return files[:TREE_LIMIT]
def find_manifest_files() -> List[str]:
"""Find manifest files matching patterns."""
found = []
for pattern in MANIFESTS:
if "*" in pattern:
# Handle glob patterns
for path in Path.cwd().glob(pattern):
if path.is_file() and not should_exclude(path):
found.append(path.name)
else:
path = Path.cwd() / pattern
if path.is_file():
found.append(pattern)
return sorted(set(found))
def read_file_preview(filepath: Path, max_lines: int = MANIFEST_PREVIEW_LINES) -> str:
"""Read file with line limit."""
try:
with open(filepath, 'r', encoding='utf-8', errors='replace') as f:
lines = f.readlines()
if not lines:
return "None found."
preview = ''.join(lines[:max_lines])
if len(lines) > max_lines:
preview += f"\n[TRUNCATED] Showing first {max_lines} of {len(lines)} lines."
return preview
except Exception as e:
return f"[Error reading file: {e}]"
def find_entry_points() -> List[str]:
"""Find entry point candidates."""
found = []
for candidate in ENTRY_CANDIDATES:
if Path(candidate).exists():
found.append(candidate)
return found
def find_lint_config() -> List[str]:
"""Find linting and formatting config files."""
found = []
for filename in LINT_FILES:
if Path(filename).exists():
found.append(filename)
return found
def find_env_templates() -> List[tuple]:
"""Find environment variable templates."""
found = []
for filename in ENV_TEMPLATES:
path = Path(filename)
if path.exists():
found.append((filename, path))
return found
def search_todos() -> List[str]:
"""Search for TODO/FIXME/HACK comments."""
todos = []
patterns = ["TODO", "FIXME", "HACK"]
exclude_dirs_str = "|".join(EXCLUDE_DIRS | {"test", "tests", "__tests__", "spec", "__mocks__", "fixtures"})
try:
for root, dirs, files in os.walk(Path.cwd()):
# Remove excluded directories from dirs to prevent os.walk from descending
dirs[:] = [d for d in dirs if d not in EXCLUDE_DIRS and d not in {"test", "tests", "__tests__", "spec", "__mocks__", "fixtures"}]
for file in files:
# Check file extension
ext = Path(file).suffix.lstrip('.')
if ext not in SOURCE_EXTS:
continue
filepath = Path(root) / file
try:
with open(filepath, 'r', encoding='utf-8', errors='replace') as f:
for line_num, line in enumerate(f, 1):
for pattern in patterns:
if pattern in line:
rel_path = filepath.relative_to(Path.cwd())
todos.append(f"{rel_path}:{line_num}: {line.strip()}")
except Exception:
pass
except Exception:
pass
return todos[:TODO_LIMIT]
def get_git_commits() -> List[str]:
"""Get recent git commits."""
try:
result = subprocess.run(
["git", "log", "--oneline", "-n", str(RECENT_COMMITS_LIMIT)],
capture_output=True,
text=True,
cwd=Path.cwd()
)
if result.returncode == 0:
return result.stdout.strip().split('\n') if result.stdout.strip() else []
return []
except Exception:
return []
def get_git_churn() -> List[str]:
"""Get high-churn files from last 90 days."""
try:
result = subprocess.run(
["git", "log", "--since=90 days ago", "--name-only", "--pretty=format:"],
capture_output=True,
text=True,
cwd=Path.cwd()
)
if result.returncode == 0:
files = [f.strip() for f in result.stdout.split('\n') if f.strip()]
# Count occurrences
from collections import Counter
counts = Counter(files)
churn = sorted(counts.items(), key=lambda x: x[1], reverse=True)
return [f"{count:4d} {filename}" for filename, count in churn[:CHURN_LIMIT]]
return []
except Exception:
return []
def is_git_repo() -> bool:
"""Check if current directory is a git repository."""
try:
subprocess.run(
["git", "rev-parse", "--git-dir"],
capture_output=True,
cwd=Path.cwd(),
timeout=2
)
return True
except Exception:
return False
def detect_monorepo() -> List[str]:
"""Detect monorepo signals."""
signals = []
for filename in MONOREPO_FILES:
if Path(filename).exists():
signals.append(f"Monorepo tool detected: {filename}")
for dirname in MONOREPO_DIRS:
if Path(dirname).is_dir():
signals.append(f"Sub-package directory found: {dirname}/")
# Check package.json workspaces
if Path("package.json").exists():
try:
with open("package.json", 'r') as f:
content = f.read()
if '"workspaces"' in content:
signals.append("package.json has 'workspaces' field (npm/yarn workspaces monorepo)")
except Exception:
pass
return signals
def detect_ci_cd_pipelines() -> List[str]:
"""Detect CI/CD pipeline configurations."""
pipelines = []
for config_path, pipeline_name in CI_CD_CONFIGS.items():
path = Path(config_path)
if path.is_file():
pipelines.append(f"CI/CD: {pipeline_name}")
elif path.is_dir():
# Check for workflow files in directory
try:
if list(path.glob("*.yml")) or list(path.glob("*.yaml")):
pipelines.append(f"CI/CD: {pipeline_name}")
except Exception:
pass
return pipelines
def detect_containers() -> List[str]:
"""Detect containerization and orchestration configs."""
containers = []
for config in CONTAINER_FILES:
path = Path(config)
if path.is_file():
if "Dockerfile" in config:
containers.append("Container: Docker found")
elif "docker-compose" in config:
containers.append("Orchestration: Docker Compose found")
elif config.endswith(".yaml") or config.endswith(".yml"):
containers.append(f"Container/Orchestration: {config}")
elif path.is_dir():
if config in ["k8s", "kubernetes"]:
containers.append("Orchestration: Kubernetes configs found")
try:
if list(path.glob("*.yml")) or list(path.glob("*.yaml")):
containers.append(f"Container/Orchestration: {config}/ directory found")
except Exception:
pass
return containers
def detect_security_configs() -> List[str]:
"""Detect security and compliance configurations."""
security = []
for config in SECURITY_CONFIGS:
if Path(config).exists():
config_name = config.replace(".yml", "").replace(".yaml", "").lstrip(".")
security.append(f"Security: {config_name}")
return security
def detect_performance_markers() -> List[str]:
"""Detect performance testing and profiling markers."""
performance = []
for marker in PERFORMANCE_MARKERS:
if Path(marker).exists():
performance.append(f"Performance: {marker} found")
else:
# Check for directories
try:
if Path(marker).is_dir():
performance.append(f"Performance: {marker}/ directory found")
except Exception:
pass
return performance
def collect_code_metrics() -> dict:
"""Collect code metrics: file counts by extension, total LOC."""
metrics = {
"total_files": 0,
"by_extension": {},
"by_language": {},
"total_lines": 0,
"largest_files": []
}
# Language mapping
lang_map = {
"ts": "TypeScript", "tsx": "TypeScript/React", "js": "JavaScript",
"jsx": "JavaScript/React", "py": "Python", "go": "Go",
"java": "Java", "kt": "Kotlin", "rs": "Rust",
"cs": "C#", "rb": "Ruby", "php": "PHP",
"swift": "Swift", "scala": "Scala", "ex": "Elixir",
"cpp": "C++", "c": "C", "h": "C Header",
"clj": "Clojure", "lua": "Lua", "hs": "Haskell"
}
file_sizes = []
try:
for root, dirs, files in os.walk(Path.cwd()):
dirs[:] = [d for d in dirs if d not in EXCLUDE_DIRS]
for file in files:
filepath = Path(root) / file
ext = filepath.suffix.lstrip('.')
if not ext or ext in {"pyc", "o", "a", "so"}:
continue
try:
size = filepath.stat().st_size
file_sizes.append((filepath.relative_to(Path.cwd()), size))
metrics["total_files"] += 1
metrics["by_extension"][ext] = metrics["by_extension"].get(ext, 0) + 1
lang = lang_map.get(ext, "Other")
metrics["by_language"][lang] = metrics["by_language"].get(lang, 0) + 1
# Count lines for text files
if ext in SOURCE_EXTS and size < 1_000_000: # Skip huge files
try:
with open(filepath, 'r', encoding='utf-8', errors='ignore') as f:
metrics["total_lines"] += len(f.readlines())
except Exception:
pass
except Exception:
pass
# Top 10 largest files
file_sizes.sort(key=lambda x: x[1], reverse=True)
metrics["largest_files"] = [
f"{str(f)}: {s/1024:.1f}KB" for f, s in file_sizes[:10]
]
except Exception:
pass
return metrics
def print_section(title: str, content: List[str], output_file=None) -> None:
"""Print a section with title and content."""
lines = [f"\n=== {title} ==="]
if isinstance(content, list):
lines.extend(content if content else ["None found."])
elif isinstance(content, str):
lines.append(content)
text = '\n'.join(lines) + '\n'
if output_file:
output_file.write(text)
else:
print(text, end='')
def main():
"""Main entry point."""
args = parse_args()
output_file = None
if args.output:
output_dir = Path(args.output).parent
output_dir.mkdir(parents=True, exist_ok=True)
output_file = open(args.output, 'w', encoding='utf-8')
print(f"Writing output to: {args.output}", file=sys.stderr)
try:
# Directory tree
print_section(
f"DIRECTORY TREE (max depth {TREE_MAX_DEPTH}, source files only)",
get_directory_tree(),
output_file
)
# Stack detection
manifests = find_manifest_files()
if manifests:
manifest_content = [""]
for manifest in manifests:
manifest_path = Path(manifest)
manifest_content.append(f"--- {manifest} ---")
if manifest == "bun.lockb":
manifest_content.append("[Binary lockfile — see package.json for dependency details.]")
else:
manifest_content.append(read_file_preview(manifest_path))
print_section("STACK DETECTION (manifest files)", manifest_content, output_file)
else:
print_section("STACK DETECTION (manifest files)", ["No recognized manifest files found in project root."], output_file)
# Entry points
entries = find_entry_points()
if entries:
entry_content = [f"Found: {e}" for e in entries]
print_section("ENTRY POINTS", entry_content, output_file)
else:
print_section("ENTRY POINTS", ["No common entry points found. Check 'main' or 'scripts.start' in manifest files above."], output_file)
# Linting config
lint = find_lint_config()
if lint:
lint_content = [f"Found: {l}" for l in lint]
print_section("LINTING AND FORMATTING CONFIG", lint_content, output_file)
else:
print_section("LINTING AND FORMATTING CONFIG", ["No linting or formatting config files found in project root."], output_file)
# Environment templates
envs = find_env_templates()
if envs:
env_content = []
for filename, filepath in envs:
env_content.append(f"--- {filename} ---")
env_content.append(read_file_preview(filepath))
print_section("ENVIRONMENT VARIABLE TEMPLATES", env_content, output_file)
else:
print_section("ENVIRONMENT VARIABLE TEMPLATES", ["No .env.example or .env.template found. Identify required environment variables by searching the code and config for environment variable reads."], output_file)
# TODOs
todos = search_todos()
if todos:
print_section("TODO / FIXME / HACK (production code only, test dirs excluded)", todos, output_file)
else:
print_section("TODO / FIXME / HACK (production code only, test dirs excluded)", ["None found."], output_file)
# Git info
if is_git_repo():
commits = get_git_commits()
if commits:
print_section("GIT RECENT COMMITS (last 20)", commits, output_file)
else:
print_section("GIT RECENT COMMITS (last 20)", ["No commits found."], output_file)
churn = get_git_churn()
if churn:
print_section("HIGH-CHURN FILES (last 90 days, top 20)", churn, output_file)
else:
print_section("HIGH-CHURN FILES (last 90 days, top 20)", ["None found."], output_file)
else:
print_section("GIT RECENT COMMITS (last 20)", ["Not a git repository or no commits yet."], output_file)
print_section("HIGH-CHURN FILES (last 90 days, top 20)", ["Not a git repository."], output_file)
# Monorepo detection
monorepo = detect_monorepo()
if monorepo:
print_section("MONOREPO SIGNALS", monorepo, output_file)
else:
print_section("MONOREPO SIGNALS", ["No monorepo signals detected."], output_file)
# Code metrics
metrics = collect_code_metrics()
metrics_output = [
f"Total files scanned: {metrics['total_files']}",
f"Total lines of code: {metrics['total_lines']}",
""
]
if metrics["by_language"]:
metrics_output.append("Files by language:")
for lang, count in sorted(metrics["by_language"].items(), key=lambda x: x[1], reverse=True):
metrics_output.append(f" {lang}: {count}")
if metrics["largest_files"]:
metrics_output.append("")
metrics_output.append("Top 10 largest files:")
metrics_output.extend(metrics["largest_files"])
print_section("CODE METRICS", metrics_output, output_file)
# CI/CD Detection
ci_cd = detect_ci_cd_pipelines()
if ci_cd:
print_section("CI/CD PIPELINES", ci_cd, output_file)
else:
print_section("CI/CD PIPELINES", ["No CI/CD pipelines detected."], output_file)
# Container Detection
containers = detect_containers()
if containers:
print_section("CONTAINERS & ORCHESTRATION", containers, output_file)
else:
print_section("CONTAINERS & ORCHESTRATION", ["No containerization configs detected."], output_file)
# Security Configs
security = detect_security_configs()
if security:
print_section("SECURITY & COMPLIANCE", security, output_file)
else:
print_section("SECURITY & COMPLIANCE", ["No security configs detected."], output_file)
# Performance Markers
performance = detect_performance_markers()
if performance:
print_section("PERFORMANCE & TESTING", performance, output_file)
else:
print_section("PERFORMANCE & TESTING", ["No performance testing configs detected."], output_file)
# Final message
final_msg = "\n=== SCAN COMPLETE ===\n"
if output_file:
output_file.write(final_msg)
else:
print(final_msg, end='')
return 0
except Exception as e:
print(f"Error: {e}", file=sys.stderr)
return 1
finally:
if output_file:
output_file.close()
if __name__ == "__main__":
sys.exit(main())

View File

@ -0,0 +1,190 @@
---
name: ai-code-review
description: "Use when reviewing the workspace for high-risk infinite loops, array out-of-bounds, nil pointer dereferences, or when the user asks for AI code review across the project. Runs a findings-first technical debt review with concrete evidence and prioritized fixes."
---
# AI Code Review
## Purpose
对工作区执行聚焦型代码审查,默认覆盖以下三类高风险问题:
1. 高危死循环
2. 数组超界
3. 空指针
适用场景:
- 用户要求“审查工作区代码”
- 用户要求检查“高危死循环”
- 用户要求检查“数组超界”
- 用户要求检查“空指针”
- 用户要求一次性执行多项静态风险 review
## Review Contract
结论必须基于代码证据或可执行检查,不能靠猜测。
最终输出必须满足:
1. 以 Findings 为主,而不是先写概览。
2. 每条结论都带证据锚点:文件,必要时带函数名或调用路径。
3. 每条结论说明:
- 问题类型
- 影响范围
- 风险等级
- 建议改法
4. 明确区分:
- 现有技术债
- 可能的行为回归风险
- 测试或验证缺口
5. 如果未发现高置信度问题,也要明确写出“未发现高置信度问题”,并说明剩余风险和未验证点。
6. 如果用户要求“存档”“归档”“生成 markdown 文档”或有明显文档沉淀意图,结束前必须生成一份 Markdown 归档文档,放入工作区可追踪目录,例如 `docs/`
## Workflow
### Phase 1: Define Scope
如果用户没有进一步缩小范围,默认按下面三个主题全部审查:
1. 高危死循环
2. 数组超界
3. 空指针
如果用户只提到其中一项,则只审查该项,不要扩散到其他主题。
### Phase 2: Collect Evidence Locally
从最具体的锚点开始读代码,不先做全仓导览。
建议优先搜索:
- 高危死循环:`for {}`、递归调度、关闭 channel 后继续接收、无界重试、无退出条件的重排/生成循环
- 数组超界:`slice[i]`、`arr[i]`、`list[0]`、`list[len(...)-1]`、切片截取、随机索引、双层循环中的索引联动
- 空指针map/value 取出后直接访问字段、可能为 nil 的 struct pointer、接口断言结果、外部依赖返回值、通道/消息/计时器回调对象
只补齐最小必要上下文:
- 谁调用它
- 退出条件或边界条件是什么
- 是否依赖配置、外部返回值、channel 生命周期、全局状态
- 有没有同类安全实现可对照
### Phase 3: Classify Findings
对每条发现的问题归类:
- Correctness Debt
- Runtime Debt
- Maintainability Debt
- Delivery Debt
并至少评估:
- 严重度:高 / 中 / 低
- 发生概率:高 / 中 / 低
- 影响面:单点 / 模块 / 跨模块 / 全局
- 修复成本:小 / 中 / 大
- 是否适合立即修复:是 / 否
### Phase 4: Propose Fix Paths
每个重要问题都提供三档建议,至少给出其中前两档:
1. 最小修复:最小改动,先消除直接风险
2. 渐进重构:分阶段治理,控制回归面
3. 理想状态:长期更合理的结构,仅在必要时说明
修改建议必须说明:
- 建议改哪些文件或哪一层
- 为什么这样切分
- 影响哪些调用链或数据流
- 推荐如何验证
### Phase 5: Archive Review Result
如果用户要求归档、沉淀文档、方便后续查询,或者上下文明确表明本次 review 需要形成记录,则必须补一份 Markdown 归档文档。
归档文档要求:
- 默认放在 `docs/` 目录
- 文件名包含主题和日期,便于查询,例如 `AI_CODE_REVIEW_YYYYMMDD.md`
- 内容与最终回答保持一致,但适当整理成便于追踪的问题清单
- 至少包含审查范围、Findings、Open Questions / Assumptions、测试与验证缺口、Recommended Next Steps
- 如果本轮包含“已修复”或“已验证”项,也要单独记录,避免后续重复排查
## Topic-Specific Guidance
### A. 高危死循环
重点检查:
- `for {}` 是否依赖阻塞 channel / context 正常退出
- channel 接收是否处理 `ok`
- 定时器/回调是否会在 `remain <= 0` 时立刻重排自身
- worker pool / retry / polling 是否存在无界等待
- 业务生成循环是否只靠“期望随机成功”退出
只有以下情况才可判为高危:
- 没有稳定退出条件
- 关闭/异常状态下会退化成空转
- 可能导致请求永久挂住或 goroutine 永久滞留
### B. 数组超界
重点检查:
- 访问 `list[0]` 前是否保证 `len(list) > 0`
- 访问 `list[len(list)-1]` 前是否保证非空
- 切片 `a[x:y]` 是否保证 `0 <= x <= y <= len(a)`
- 随机索引或配置索引是否保证合法
- 双层循环中索引是否随着集合长度变化失效
优先找“长度来自外部数据/配置/随机结果”的路径,而不是只找语法层面的索引表达式。
### C. 空指针
重点检查:
- map 取值后返回 pointer是否判空再解引用
- 函数返回 `(*T, error)``interface{}` 时是否检查 nil / 断言结果
- channel 接收到的对象、timer callback、消息对象是否可能为 nil
- 全局单例、模块对象、配置对象在初始化失败或空配置下是否仍被访问
- 外部依赖结果是否在错误路径后继续被使用
## Output Shape
默认按下面结构输出:
1. Findings
- 按严重度排序
- 每条包含:位置、问题、影响、风险等级、建议方案
2. Open Questions / Assumptions
- 只列会影响结论的问题
3. Recommended Next Steps
- 给出最值得先做的 1-3 个动作
如果是多主题审查,可按主题分组,但仍然必须以 Findings 为主。
如果用户要求归档文档,最终还应补充:
4. Archive Output
- 说明已生成的 Markdown 文档路径
- 确认文档可用于后续存档查询
## Decision Rules
- 不把纯风格问题当成技术债。
- 不把“正常常驻事件循环”误判为死循环,除非它缺失退出协议或关闭后会空转。
- 不把“理论上可能为空”直接写成空指针问题,除非能从调用链证明该值确实可能为 nil。
- 不把“有边界检查的索引访问”误判为数组超界。
- 如果风险只能算疑点,明确写成“需验证项”,不要伪装成确定性结论。
## Example Invocations
- `/ai-code-review 审查项目中是否有高危死循环的问题`
- `/ai-code-review 审查项目中是否有数组超界的问题`
- `/ai-code-review 审查项目中是否有空指针的问题`
- `/ai-code-review 审查工作区代码中的高危死循环、数组超界和空指针`

View File

@ -0,0 +1,157 @@
---
name: codebase-tech-debt-review
description: 'Use this skill when the user asks to review a codebase, assess architecture or maintainability, identify technical debt, find risky design choices, or propose refactoring plans. Trigger for prompts like "review this codebase", "代码评审", "发现技术债", "找架构问题", "提出修改方案", "refactor plan", and "design debt review". Do not use for routine bug fixes, single-file edits, or broad documentation unless the main task is debt discovery and remediation planning.'
argument-hint: 'Optional: review scope or focus, e.g. "game package only", "routing layer", "performance and concurrency", "output top 10 debts"'
---
# Codebase Tech Debt Review
对现有代码库进行评审,发现技术债、风险点和结构性问题,并给出分层次的修改方案。结论必须基于代码或可执行检查,不能靠猜测。
## Output Contract
结束前必须满足以下条件:
1. 输出以“发现的问题”为主,而不是先讲概览。
2. 每个问题都要带证据锚点:具体文件,必要时带函数名或调用路径。
3. 每个问题都要说明影响范围、风险等级和建议改法。
4. 明确区分:
- 现有技术债
- 可能的行为回归风险
- 测试或验证缺口
5. 如果建议重构,必须给出至少一个可落地的最小实施路径。
6. 不把“风格偏好”当成技术债,除非它已经影响维护性、正确性、性能或协作效率。
## Review Modes
如果用户提供了明确范围,按范围优先:
- 包级:例如 `game/`、`db/`、`cluster/`
- 主题级:例如 路由层、配置加载、并发安全、Redis 生命周期
- 风险级:例如 性能、可维护性、协议兼容、测试覆盖
如果用户没有给范围:
1. 先识别最可能承载复杂度或风险的入口面。
2. 优先审查以下区域:
- 大文件或大函数
- 全局状态和单例调用路径
- 配置初始化与启动流程
- 并发、缓存、数据库、跨节点消息
- 近期高频改动或明显重复代码
## Workflow
按下面的流程执行,并在过程中持续压缩范围,避免泛泛而谈。
### Phase 1: Define Scope and Review Lens
1. 识别用户要的评审范围。
2. 如果范围不明确,先选择一个最可能承载问题的入口锚点:文件、函数、模块、失败行为或关键调用链。
3. 设定本次评审重点:
- 正确性与回归风险
- 可维护性与重复实现
- 架构耦合与边界混乱
- 性能或并发问题
- 缺失测试与验证困难
### Phase 2: Collect Local Evidence
1. 从最具体的锚点开始读代码,不先做全仓漫游。
2. 找到直接控制行为的位置,而不是只停留在注册、转发、包装层。
3. 对每个候选问题,补齐最小必要上下文:
- 它由谁调用
- 它修改了什么状态
- 是否存在重复逻辑
- 是否依赖全局变量、配置、缓存、RPC 或数据库
4. 只在必要时扩大一跳上下文,避免把评审做成代码库导览。
### Phase 3: Classify Findings
对每个发现的问题归入一个主类,必要时可带次类:
- Correctness Debt隐藏 bug、错误处理缺陷、边界条件遗漏
- Maintainability Debt超大文件、重复代码、命名混乱、职责混杂
- Architecture Debt层级耦合、跨模块泄漏、全局状态失控、抽象失真
- Runtime Debt性能瓶颈、锁竞争、缓存膨胀、初始化脆弱性、并发安全问题
- Delivery Debt测试困难、缺失断言、无法局部验证、变更半径过大
### Phase 4: Score and Decide
对每个问题至少评估以下维度:
- 严重度:高 / 中 / 低
- 发生概率:高 / 中 / 低
- 影响面:单点 / 模块 / 跨模块 / 全局
- 修复成本:小 / 中 / 大
- 是否适合立即修复:是 / 否
然后给出推荐动作:
- Immediate Fix低风险、收益明确、应优先落地
- Incremental Refactor应拆小步完成先做控制面或边界面
- Strategic Follow-up需要专项治理不适合当前顺手改完
### Phase 5: Propose Modification Plans
每个重要问题都要给出改造方案,优先提供三档建议:
1. 最小修复:最小改动,先消除明显风险
2. 渐进重构:分阶段拆分,控制回归面
3. 理想状态:更合理的长期结构,仅在确有必要时说明
修改方案必须包含:
- 建议改哪些文件或哪一层
- 为什么这样切分或重构
- 可能影响的调用链或数据流
- 推荐验证方式
### Phase 6: Validate the Review
在结束前检查:
1. 每条问题是否都有真实证据。
2. 是否把“个人偏好”误写成了技术债。
3. 是否遗漏了最关键的回归风险或测试缺口。
4. 方案是否可以从当前代码状态直接启动,而不是停留在抽象口号。
5. 若没有发现明显问题,也要明确说明“未发现高置信度问题”,并列出剩余风险和未验证点。
## Required Response Shape
最终回答默认按下面结构输出:
1. Findings
- 按严重度排序列出问题
- 每条包含:位置、问题、影响、建议方案
2. Open Questions / Assumptions
- 只列会影响结论的问题
3. Recommended Next Steps
- 给出最值得先做的 1-3 个动作
如果用户明确要求“review”始终以 findings 为主,不要先写长篇摘要。
## Decision Rules
- 如果问题只存在于生成代码、日志、构建产物,不作为主结论,除非它已影响主工程维护。
- 如果某个问题需要大面积重写才能解决,优先先给“渐进重构路径”,不要直接建议全盘重构。
- 如果一个大文件只是大,但边界清晰、变更安全、验证方便,不自动认定为高优先级技术债。
- 如果一个设计让局部验证困难、改动半径不可控,即便当前没 bug也应视为高价值技术债候选。
- 如果无法确认风险是否真实,明确写成“疑点”或“需验证项”,不要伪装成确定性结论。
## Quality Bar
高质量评审应满足:
- 结论具体,可定位,可复核
- 建议可执行,不空泛
- 能区分立即问题与长期债务
- 不扩大范围到无关模块
- 能说明为什么某个债务值得优先处理
## Example Prompts
- `/codebase-tech-debt-review game 路由层和实现层的技术债`
- `/codebase-tech-debt-review 评审这个仓库的初始化和配置加载风险`
- `/codebase-tech-debt-review focus on Redis lifecycle and cache growth`
- `/codebase-tech-debt-review 输出 top 10 技术债并给分阶段整改方案`

View File

@ -1,179 +0,0 @@
package GoUtil
import (
"bytes"
"encoding/gob"
"math/rand"
"reflect"
"strconv"
"time"
)
// 实例化一个通过字符串映射函数切片的map
var eventByName = make(map[string][]*EventObj)
type EventObj struct {
Callback func([]interface{})
Obj interface{}
}
func (o *EventObj) isEqual(tar *EventObj) bool {
if reflect.ValueOf(o.Callback).Pointer() == reflect.ValueOf(tar.Callback).Pointer() && o.Obj == tar.Obj {
return true
}
return false
}
// 注册事件,提供事件名和回调函数
func RegisterEvent(name string, callback func([]interface{}), Obj interface{}) {
eo := new(EventObj)
eo.Callback = callback
eo.Obj = Obj
// 通过名字查找事件列表
list := eventByName[name]
// 在列表切片中添加函数
list = append(list, eo)
// 将修改的事件列表切片保存回去
eventByName[name] = list
}
// 调用事件
func CallEvent(name string, param []interface{}) {
// 通过名字找到事件列表
list := eventByName[name]
// 遍历这个事件的所有回调
for _, eo := range list {
// 传入参数调用回调
eo.Callback(param)
}
}
func RemoveEvent(name string, callback func([]interface{}), Obj interface{}) {
// 通过名字找到事件列表
list := eventByName[name]
j := 0
eo := new(EventObj)
eo.Callback = callback
eo.Obj = Obj
// 遍历这个事件的所有回调
for _, v := range list {
if !v.isEqual(eo) {
list[j] = v
j++
}
}
eventByName[name] = list[:j]
// fmt.Println("len eventByName[name]:", len(eventByName[name]))
//fmt.Println("len eventByName[name]:", len(eventByName))
// fmt.Println("len list:", len(list))
}
func DeleteEleByValue(list []int, ele int) []int {
j := 0
for _, v := range list {
if v != ele {
list[j] = v
j++
}
}
ret := list[:j]
return ret
}
func BoolToInt32(b bool) int32 {
if b {
return 1
}
return 0
}
func IfTrue(a bool, b, c interface{}) interface{} {
if a {
return b
}
return c
}
func Int64(a interface{}) int64 {
if a == nil {
return 0
}
switch v := a.(type) {
case int:
return int64(v)
case int32:
return int64(v)
case int64:
return v
case float64:
return int64(v)
}
return 0
}
func Int(a interface{}) int {
if a == nil {
return 0
}
switch v := a.(type) {
case int:
return v
case int32:
return int(v)
case int64:
return int(v)
case float64:
return int(v)
case string:
r, err := strconv.Atoi(v)
if err != nil {
return 0
}
return r
}
return 0
}
func GobMarshal(data interface{}) ([]byte, error) {
var buf bytes.Buffer
encode := gob.NewEncoder(&buf)
err := encode.Encode(data)
if err != nil {
return nil, err
}
return buf.Bytes(), nil
}
func GobUnmarshal(data []byte, obj interface{}) error {
decode := gob.NewDecoder(bytes.NewReader(data))
err := decode.Decode(obj)
if err != nil {
return err
}
return nil
}
func GetServerIdByUid(uid int) int {
return int(uid / 100000)
}
func CreateOrderSn(uid int) string {
Now := time.Now()
return "order_" + strconv.Itoa(uid) + "_" + Now.Format("20060102150405") + RandString(6)
}
// 生成指定长度的随机字符串
func RandString(n int) string {
const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
r := rand.New(rand.NewSource(time.Now().UnixNano()))
b := make([]byte, n)
for i := range b {
b[i] = letters[r.Intn(len(letters))]
}
return string(b)
}

View File

@ -1,8 +0,0 @@
package GoUtil
func RandNum(start int, end int) int {
if start > end {
return 0
}
return start + GetRand().Intn(end-start+1)
}

View File

@ -1,172 +0,0 @@
package GoUtil
import (
"sort"
"strconv"
)
func IntToInt32(d []int) []int32 {
r := make([]int32, 0, len(d))
for _, d := range d {
r = append(r, int32(d))
}
return r
}
func RandPopSlice(slice []int) ([]int, int) {
slen := len(slice)
if slen == 0 {
return slice, -1
}
Id := GetRand().Intn(slen)
return append(slice[0:Id], slice[Id+1:]...), slice[Id]
}
func RandSlice(slice []int) int {
slen := len(slice)
if slen == 0 {
return 0
}
Id := GetRand().Intn(slen)
return slice[Id]
}
func RandSliceNum(slice []int, num int) []int {
r := make([]int, 0, num)
if num <= 0 || len(slice) < num {
return slice
}
for i := 0; i < num; i++ {
Id := GetRand().Intn(len(slice))
r = append(r, slice[Id])
slice = append(slice[0:Id], slice[Id+1:]...)
}
return r
}
func RandSliceNumNonAdjacent(slice []int, num int) []int {
if len(slice) < 2*num {
return nil
}
sort.Ints(slice)
result := make([]int, 0, num)
for len(result) < num {
Id := GetRand().Intn(len(slice))
result = append(result, slice[Id])
slice = SubAdjacentElemSlice(slice, Id)
}
return result
}
func SubAdjacentElemSlice(s []int, Id int) []int {
r := make([]int, 0, len(s))
for k, v := range s {
if k < Id-1 || k > Id+1 {
r = append(r, v)
}
}
return r
}
func RandStringSlice(slice []string) string {
slen := len(slice)
if slen == 0 {
return ""
}
Id := GetRand().Intn(slen)
return slice[Id]
}
func SliceStringToInt(s []string) []int {
if len(s) == 0 {
return []int{}
}
r := make([]int, 0, len(s))
for _, v := range s {
v1, _ := strconv.Atoi(v)
r = append(r, v1)
}
return r
}
func SliceIntToInt32(s []int) []int32 {
r := make([]int32, 0, len(s))
for _, v := range s {
r = append(r, int32(v))
}
return r
}
func PopElemSlice(s []int, Id int) ([]int, bool) {
for k, v := range s {
if v == Id {
return append(s[0:k], s[k+1:]...), true
}
}
return s, false
}
func ElemInSlice(s []int, Id int) bool {
for _, v := range s {
if v == Id {
return true
}
}
return false
}
func GetElemNum(s []int, Id int) int {
n := 0
for _, v := range s {
if v == Id {
n++
}
}
return n
}
func InArray(Id int, s []int) bool {
for _, v := range s {
if v == Id {
return true
}
}
return false
}
func InStringArray(Id string, s []string) bool {
for _, v := range s {
if v == Id {
return true
}
}
return false
}
// toInterfaceSlice converts a slice of strings to a slice of empty interfaces
func ToInterfaceSlice(slice []string) []interface{} {
result := make([]interface{}, len(slice))
for i, v := range slice {
result[i] = v
}
return result
}
// a - b
func SubSlices(a, b []int) []int {
result := make([]int, 0, len(a))
for _, num := range a {
if !InArray(num, b) {
result = append(result, num)
}
}
return result
}
func PopSlice(s []int) (int, []int) {
if len(s) == 0 {
return -1, s
}
return s[0], s[1:]
}

View File

@ -3,13 +3,19 @@ package MergeConst
type ProtocolType int32
const (
Protocol_Error_Account_Exist int32 = 100
Protocol_Error_Account_OR_PWD_ERROR int32 = 101
Protocol_Error_Account_OR_PWD_Short int32 = 102
Protocol_Error_Account_Fail int32 = 103
Protocol_Error_Account_NoExsit int32 = 104
Protocol_Res_Buy_Cnt_Limit int32 = 110
Protocol_Res_Buy_CD int32 = 111
Protocol_Error_Account_Exist int32 = 100
Protocol_Error_Account_OR_PWD_ERROR int32 = 101
Protocol_Error_Account_OR_PWD_Short int32 = 102
Protocol_Error_Account_Fail int32 = 103
Protocol_Error_Account_NoExsit int32 = 104
Protocol_Error_Account_Code_Error int32 = 105
Protocol_Error_Account_Device_Error int32 = 106
Protocol_Error_Id_Not_Verify int32 = 107
Protocol_Error_Id_Verify_Error int32 = 108
Protocol_Error_Account_Ban int32 = 109
Protocol_Res_Buy_Cnt_Limit int32 = 110
Protocol_Res_Buy_CD int32 = 111
Protocol_Email_Find_Fail int32 = 120
Protocol_Active_No_Exsit int32 = 130
Protocol_Active_7Day_Rewarded int32 = 131
@ -94,10 +100,12 @@ const G_Champion_settlement_Duration = 3600 * 23
const G_Champion_Unlock_Score = 2
const G_Champion_Max_Score = 44650
const G_Champion_People_Cnt = 50
const SUCCESS = 1
const (
ORDER_STATUS_IDLE = 0 // 订单未完成
ORDER_STATUS_PAY = 1 // 订单已支付
ORDER_STATUS_CANCLE = 2 // 订单取消
ORDER_STATUS_CANCEL = 2 // 订单取消
ORDER_STATUS_SHIP = 3 // 订单已发货
)
const GLOBAL_SERVER_ID = 0 // 全局服ID

View File

@ -0,0 +1,8 @@
package MergeConst
const (
Go_gc_percent = 200
Go_gc_memory_limit = 1024 << 20
Go_game_version = "1.0.6" // 游戏版本号,格式为 "主版本号.次版本号.修订号",每次发布新版本时需要更新
Go_log_delete_days = 3 // 日志删除天数,超过这个天数的日志文件将被删除
)

16
src/server/README.MD Normal file
View File

@ -0,0 +1,16 @@
## 命名规范
- **清晰优先**:名称应准确表达含义,避免歧义。
- **导出规则**:导出标识符首字母大写,未导出首字母小写。
- **包名**:小写单词,简短且描述职责(如 `store`、`auth`)。
- **文件名**:小写(可下划线分隔),测试文件以 `_test.go` 结尾。
- **函数/变量/类型**:驼峰命名;导出使用 PascalCase例如 `UserService`、`CreateUser`)。
- **接口**:以行为命名,常用 `-er` 结尾(如 `Reader`、`Store`),避免 `I` 前缀。
- **缩写**:统一大小写(例如导出使用 `ID`、`URL`;未导出使用 `id`、`url`)。
- **错误**:包级错误使用 `Err` 前缀(如 `ErrNotFound`)。
- **Context 与并发**:统一使用 `ctx` 作 `context.Context`,通道命名要有语义(如 `done`, `jobs`)。
- **构造函数/选项**:使用 `NewType` 与 `WithXxx` 约定,避免多个布尔参数。

View File

@ -0,0 +1,161 @@
package backend
import (
"context"
"fmt"
"net"
"server/conf"
"server/game"
"server/msg"
"gitea.bywaystudios.com/pet_home/leaf/log"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
type backendServer struct {
msg.UnimplementedBackendServer
}
func unaryRequestLogInterceptor(
ctx context.Context,
req interface{},
info *grpc.UnaryServerInfo,
handler grpc.UnaryHandler,
) (resp interface{}, err error) {
//log.Debug("gRPC request start: method=%s req=%v", info.FullMethod, req)
resp, err = handler(ctx, req)
if err != nil {
log.Error("gRPC request failed: method=%s err=%v", info.FullMethod, err)
return nil, err
}
//log.Debug("gRPC request finished: method=%s", info.FullMethod)
return resp, nil
}
func unaryPanicRecoveryInterceptor(
ctx context.Context,
req interface{},
info *grpc.UnaryServerInfo,
handler grpc.UnaryHandler,
) (_ interface{}, err error) {
defer func() {
if recovered := recover(); recovered != nil {
log.Error("gRPC request panic: method=%s panic=%v", info.FullMethod, recovered)
err = status.Error(codes.Internal, fmt.Sprintf("rpc panic: %v", recovered))
}
}()
return handler(ctx, req)
}
func (s *backendServer) ReloadActivity(ctx context.Context, req *msg.ReqActivityCfgReload) (*msg.ResActivityCfgReload, error) {
log.Debug("Received ReloadActivity request: %v", req)
game.AcitivityCfgReload()
return nil, nil
}
func (s *backendServer) OrderShipping(ctx context.Context, req *msg.ReqOrderShipping) (*msg.ResOrderShipping, error) {
log.Debug("Received OrderShipping request: %v", req)
res, err := game.AdminShipping(req)
if err != nil {
log.Error("OrderShipping error: %v", err)
return &msg.ResOrderShipping{
Code: 1,
Msg: err.Error(),
}, err
}
return res, nil
}
func (s *backendServer) UserDetail(ctx context.Context, req *msg.UserDetailParam) (*msg.ResUserDetail, error) {
log.Debug("Received UserDetail request: %v", req)
res, err := game.AdminPlayerDetailInfo(req)
if err != nil {
log.Error("UserDetail error: %v", err)
return &msg.ResUserDetail{
Code: 1,
Msg: err.Error(),
}, err
}
return res, nil
}
func (s *backendServer) ServerInfo(ctx context.Context, req *msg.ReqServerInfo) (*msg.ResServerInfo, error) {
//log.Debug("Received ServerInfo request: %v", req)
res, err := game.AdminServerInfo()
if err != nil {
log.Error("ServerInfo error: %v", err)
return &msg.ResServerInfo{}, err
}
return res, nil
}
func (s *backendServer) PlayerGm(ctx context.Context, req *msg.ReqAdminGm) (*msg.ResAdminGm, error) {
log.Debug("Received PlayerGm request: %v", req)
res, err := game.AdminPlayerGm(req)
if err != nil {
log.Error("PlayerGm error: %v", err)
return &msg.ResAdminGm{
Code: 1,
Msg: err.Error(),
}, err
}
return res, nil
}
func (s *backendServer) BanPlayer(ctx context.Context, req *msg.ReqAdminBan) (*msg.ResBackend, error) {
log.Debug("Received BanPlayer request: %v", req)
err := game.AdminBanPlayer(req)
if err != nil {
log.Error("BanPlayer error: %v", err)
return &msg.ResBackend{
Code: 1,
Msg: err.Error(),
}, err
}
return &msg.ResBackend{}, nil
}
func (s *backendServer) ReloadMail(ctx context.Context, req *msg.ReqReloadServerMail) (*msg.ResBackend, error) {
log.Debug("Received ReloadMail request: %v", req)
err := game.AdminReloadMail()
if err != nil {
log.Error("ReloadMail error: %v", err)
return &msg.ResBackend{
Code: 1,
Msg: err.Error(),
}, err
}
return &msg.ResBackend{}, nil
}
func Start() {
defer func() {
if r := recover(); r != nil {
log.Debug("Backend server panic: %v", r)
log.Debug("Backend server closing down\n")
}
}()
if conf.Server.RPCAddr == "" {
log.Debug("RPC server address not configured, skipping gRPC server startup")
return
}
lis, err := net.Listen("tcp", conf.Server.RPCAddr)
if err != nil {
log.Error("failed to listen: %v", err)
return
}
s := grpc.NewServer(grpc.ChainUnaryInterceptor(
unaryPanicRecoveryInterceptor,
unaryRequestLogInterceptor,
))
msg.RegisterBackendServer(s, &backendServer{})
log.Debug("gRPC server listening on %s", conf.Server.RPCAddr)
if err := s.Serve(lis); err != nil {
log.Error("server exited with error: %v", err)
}
}

View File

@ -2,8 +2,9 @@ package base
import (
"server/conf"
"server/pkg/github.com/name5566/leaf/chanrpc"
"server/pkg/github.com/name5566/leaf/module"
"gitea.bywaystudios.com/pet_home/leaf/chanrpc"
"gitea.bywaystudios.com/pet_home/leaf/module"
)
func NewSkeleton() *module.Skeleton {

View File

@ -0,0 +1,60 @@
package main
import (
"server/game"
"server/game/mod/msg"
"testing"
)
func BenchmarkChampionshipGroup(b *testing.B) {
for i := 0; i < b.N; i++ {
game.G_GameLogicPtr.CreateChampshipMgr()
game.G_GameLogicPtr.ChampshipMgr.Debug()
}
}
func BenchmarkPlayerInit(b *testing.B) {
for i := 0; i < b.N; i++ {
p := new(game.Player)
p.InitPlayer("aaa002")
p.LoginBackData()
}
}
func BenchmarkPlayerBackup(b *testing.B) {
p := new(game.Player)
p.InitPlayer("aaa002")
for i := 0; i < b.N; i++ {
p.BackUp()
}
}
func BenchmarkPlayerRecover(b *testing.B) {
p := new(game.Player)
p.InitPlayer("aaa002")
backup := p.BackUp()
for i := 0; i < b.N; i++ {
p.Recover(backup)
}
}
func BenchmarkPlayerHandleMsg(b *testing.B) {
p := new(game.Player)
p.InitPlayer("aaa002")
list := make([]*msg.Msg, 0, 1000)
for i := 0; i < 1000; i++ {
m := &msg.Msg{
HandleType: msg.HANDLE_MOD_PLAYER_LOGIN,
Type: msg.HANDLE_TYPE_APPLY,
From: 100001,
To: i + 100002,
Extra: 0,
}
list = append(list, m)
}
for i := 0; i < b.N; i++ {
for _, m := range list {
p.HandleMsg(m)
}
}
}

View File

@ -3,12 +3,14 @@ package mergeCluster
import (
"fmt"
"math"
"server/GoUtil"
"server/conf"
"server/game/mod/msg"
"server/pkg/github.com/name5566/leaf/log"
"server/pkg/github.com/name5566/leaf/network"
GoUtil "server/game_util"
"sync"
"gitea.bywaystudios.com/pet_home/leaf/network"
"gitea.bywaystudios.com/pet_home/leaf/log"
)
const (
@ -16,7 +18,9 @@ const (
)
var FuncMap = map[int]func(*Agent, *msg.Msg) error{} // 消息处理函数
var funcMapMu sync.RWMutex // FuncMap 的读写锁
var CallbackChan = make(map[string]chan *msg.Msg) // 回调消息队列
var callbackChanMu sync.RWMutex // CallbackChan 的读写锁
var (
server *network.TCPServer // 服务器
Center *network.TCPClient // 集群中心
@ -30,14 +34,14 @@ func Init() {
server = new(network.TCPServer)
server.Addr = conf.Server.ListenAddr
server.MaxConnNum = int(math.MaxInt32)
server.PendingWriteNum = conf.PendingWriteNum
server.PendingWriteNum = 1 << 14
server.LenMsgLen = 4
server.MaxMsgLen = 4096
server.MaxMsgLen = 1 << 16
server.NewAgent = newServerAgent
server.Start()
}
if conf.Server.CenterAddr != "" {
if conf.Server.CenterAddr != "" && conf.Server.ServerType != "center" {
connectRemote(conf.Server.CenterAddr, ClusterCenterId, "center")
}
@ -47,6 +51,11 @@ func Init() {
registerFunc(msg.CLUSTER_EXIT, ClusterExit)
}
// GetCallbackChanMu 返回 CallbackChan 的读写锁,供外部包安全访问 CallbackChan
func GetCallbackChanMu() *sync.RWMutex {
return &callbackChanMu
}
func Destroy() {
if server != nil {
log.Release("closing cluster server")
@ -82,18 +91,18 @@ func (a *Agent) Run() {
for {
data, err := a.conn.ReadMsg()
if err != nil {
log.Debug("read message error: %v", err)
log.Error("read message error: %v", err)
break
}
m := msg.Msg{}
err = GoUtil.GobUnmarshal(data, &m)
if err != nil {
log.Debug("decode err: %v", err)
log.Error("decode err: %v, data: %s", err, string(data))
return
}
err = processMsg(a, &m)
if err != nil {
log.Debug("ProcessMsg err: %v", err)
log.Error("ProcessMsg err: %v", err)
}
}
}

View File

@ -2,12 +2,14 @@ package mergeCluster
import (
"fmt"
"server/GoUtil"
"server/conf"
"server/game/mod/msg"
"server/pkg/github.com/name5566/leaf/log"
"server/pkg/github.com/name5566/leaf/network"
GoUtil "server/game_util"
"time"
"gitea.bywaystudios.com/pet_home/leaf/network"
"gitea.bywaystudios.com/pet_home/leaf/log"
)
func RevcServerMsg(m *msg.Msg) error {
@ -24,7 +26,7 @@ func HandShake(a *Agent) {
}
data, err := GoUtil.GobMarshal(m)
if err != nil {
log.Debug("HandShake GobMarshal err %v", err)
log.Error("HandShake GobMarshal err %v", err)
return
}
log.Debug("握手 server id :%d", conf.Server.ServerID)
@ -48,19 +50,14 @@ func HandShakeRecv(a *Agent, m *msg.Msg) error {
RemoteAddr: m.Extra.(string),
},
})
} else {
syncMsg := &msg.Msg{
Type: msg.CLUSTER_FRIEND_SYNC,
To: ServerId,
HandleType: msg.HANDLE_MOD_CLUSTER_SYNC,
}
sendGameMsg(syncMsg)
}
syncMsg := &msg.Msg{
Type: msg.CLUSTER_FRIEND_SYNC,
To: ServerId,
}
sendGameMsg(syncMsg)
fmt.Print("现有区服连接:")
serverAgent.Range(func(key, value interface{}) bool {
fmt.Print(key)
fmt.Print(",")
return true
})
fmt.Println()
return nil
}
@ -79,7 +76,7 @@ func ClusterJoin(a *Agent, m *msg.Msg) error {
return nil
}
log.Debug("ClusterJoin ServerId %v", clusterJoin.ServerId)
connectRemote(clusterJoin.RemoteAddr, clusterJoin.ServerId, "server")
//connectRemote(clusterJoin.RemoteAddr, clusterJoin.ServerId, "server")
return nil
}
@ -96,38 +93,82 @@ func connectRemote(RemoteAddr string, ConnType int, ConnLabel string) error {
client := new(network.TCPClient)
client.Addr = RemoteAddr
client.ConnNum = 1
client.PendingWriteNum = conf.PendingWriteNum
client.PendingWriteNum = 1 << 14
client.LenMsgLen = 4
client.MaxMsgLen = 4096
client.MaxMsgLen = 1 << 16
client.NewAgent = newAgent
client.ConnType = ConnType
client.ConnLabel = ConnLabel
client.ConnectInterval = time.Duration(time.Minute * 5)
client.ConnectInterval = time.Duration(time.Minute * 1)
if ConnType == ClusterCenterId { // 中心服断开重连
client.AutoReconnect = true
}
client.Start()
Center = client
clients = append(clients, client)
log.Debug("connet remote to addr:%s", RemoteAddr)
return nil
}
func SendServerMsg(m *msg.Msg, serverId int) error {
if m.UniKey == "" {
m.UniKey = GoUtil.UniKey(fmt.Sprintf("%v,Cluster Msg", m))
}
if m.SendT == 0 {
m.SendT = GoUtil.Now()
}
if v, ok := serverAgent.Load(serverId); ok {
data, err := GoUtil.GobMarshal(m)
if err != nil {
log.Debug("SendServerMsg GobMarshal err %v", err)
log.Error("SendServerMsg GobMarshal err %v", err)
return err
}
v.(network.Agent).WriteMsg(data)
return nil
}
return fmt.Errorf("server %d not online", serverId)
}
func CallServerMsg(m *msg.Msg, serverId int) (*msg.Msg, error) {
if m.UniKey == "" {
m.UniKey = GoUtil.UniKey(fmt.Sprintf("%v,Cluster Msg", m))
}
if m.SendT == 0 {
m.SendT = GoUtil.Now()
}
v, ok := serverAgent.Load(serverId)
// 之后再发送消息
if !ok {
return nil, fmt.Errorf("server %d not online", serverId)
}
// 先注册回调通道,避免发送出去后对方快速返回导致丢失
newChan := make(chan *msg.Msg, 1)
registerChanel(m.UniKey, newChan)
defer unregisterChanel(m.UniKey)
data, err := GoUtil.GobMarshal(m)
if err != nil {
log.Error("CallServerMsg GobMarshal err %v", err)
return nil, err
}
v.(network.Agent).WriteMsg(data)
// 等待返回(直接接收一次)
timeout := time.After(5 * time.Second)
select {
case backm := <-newChan:
if backm == nil {
return nil, fmt.Errorf("server %d not response", serverId)
}
// log.Debug("CallServerMsg reply %v", backm)
return backm, nil
case <-timeout:
return nil, fmt.Errorf("timeout waiting for server %d response", serverId)
}
}
func SendMsgAll(m *msg.Msg) {
data, err := GoUtil.GobMarshal(m)
if err != nil {
log.Debug("SendMsgAll GobMarshal err %v", err)
log.Error("SendMsgAll GobMarshal err %v", err)
return
}
serverAgent.Range(func(key, value interface{}) bool {
@ -138,7 +179,12 @@ func SendMsgAll(m *msg.Msg) {
func processMsg(a *Agent, m *msg.Msg) error {
var err error
if fun, ok := FuncMap[m.Type]; ok {
funcMapMu.RLock()
fun, ok := FuncMap[m.Type]
funcMapMu.RUnlock()
if ok {
err = fun(a, m)
} else {
MsgChan <- m //交由game Module消息处理
@ -147,7 +193,21 @@ func processMsg(a *Agent, m *msg.Msg) error {
}
func registerFunc(key int, fun func(*Agent, *msg.Msg) error) {
funcMapMu.Lock()
FuncMap[key] = fun
funcMapMu.Unlock()
}
func registerChanel(key string, chanel chan *msg.Msg) {
callbackChanMu.Lock()
CallbackChan[key] = chanel
callbackChanMu.Unlock()
}
func unregisterChanel(key string) {
callbackChanMu.Lock()
delete(CallbackChan, key)
callbackChanMu.Unlock()
}
func sendGameMsg(m *msg.Msg) {

View File

@ -1,17 +1,8 @@
package mergeCluster
import (
"encoding/gob"
"server/game/mod/msg"
)
func init() {
gob.Register(&msg.Msg{})
gob.Register(&ClusterJoinData{})
}
type ClusterJoinData struct {
ServerId int
Country int
RemoteAddr string
}

View File

@ -1,41 +0,0 @@
package startOrderCfg
import (
"server/gamedata"
"strconv"
)
type StartOrderCfg struct {
}
var CFG_NAME = "StartOrder"
func init() {
gamedata.InitCfg(CFG_NAME)
}
// 根据step 获取初始订单
func GetStartOrderByStep(id int) []*gamedata.StartOrderData {
ret := make([]*gamedata.StartOrderData, 0)
data, err := gamedata.GetData(CFG_NAME)
if err != nil {
return ret
}
for k, v := range data {
record := v.(map[string]interface{})
stepVal := gamedata.ParseInt(record["step"])
var MergeList []int
for _, vv := range record["merge_id_list"].([]interface{}) {
MergeList = append(MergeList, gamedata.ParseInt(vv))
}
if int(stepVal) == id {
OrderId, _ := strconv.Atoi(k)
ret = append(ret, &gamedata.StartOrderData{
Id: OrderId,
Step: int(stepVal),
MergeList: MergeList,
})
}
}
return ret
}

View File

@ -1,46 +0,0 @@
package activityCfg
import (
"server/GoUtil"
"server/gamedata"
"server/pkg/github.com/name5566/leaf/log"
"strconv"
)
const (
CFG_ACTIVITY = "Activity"
)
func init() {
gamedata.InitCfg(CFG_ACTIVITY)
}
func GetActivityList() []*gamedata.ActivityData {
data, err := gamedata.GetData(CFG_ACTIVITY)
if err != nil {
log.Debug("GetActivityList err:%v", err)
}
Now := GoUtil.Now()
var List []*gamedata.ActivityData
for k, v := range data {
StartTimeStr := gamedata.GetStringValue(v, "StartTime")
EndTimeStr := gamedata.GetStringValue(v, "EndTime")
StartTime := GoUtil.ParseTime(StartTimeStr)
EndTime := GoUtil.ParseTime(EndTimeStr)
if Now >= StartTime && Now <= EndTime {
Id, _ := strconv.Atoi(k)
Type := gamedata.GetIntValue(v, "Type")
Name := gamedata.GetStringValue(v, "Name")
Title := gamedata.GetStringValue(v, "Title")
ActivityData := &gamedata.ActivityData{
Id: Id,
Type: Type,
Name: Name,
Title: Title,
}
List = append(List, ActivityData)
}
}
return List
}

View File

@ -0,0 +1,145 @@
package activityCfg
import (
"server/game/mod/item"
GoUtil "server/game_util"
"server/gamedata"
"strconv"
"gitea.bywaystudios.com/pet_home/leaf/log"
)
const (
CFG_ACTIVITY = "Activity"
CFG_ACTIVITY_GIFT = "ActivityGift"
)
func init() {
gamedata.InitCfg(CFG_ACTIVITY)
gamedata.InitCfg(CFG_ACTIVITY_GIFT)
}
func GetActivityGiftId(chargeId int) int {
data, err := gamedata.GetData(CFG_ACTIVITY_GIFT)
if err != nil {
log.Debug("GetActivityGiftId err:%v", err)
return 0
}
for _, v := range data {
ChargeId := gamedata.GetIntValue(v, "ChargeId")
if ChargeId == chargeId {
return gamedata.GetIntValue(v, "AId")
}
}
return 0
}
func GetActivityGiftItems(chargeId int) []*item.Item {
data, err := gamedata.GetData(CFG_ACTIVITY_GIFT)
if err != nil {
log.Debug("GetActivityGiftItems err:%v", err)
return nil
}
for _, v := range data {
ChargeId := gamedata.GetIntValue(v, "ChargeId")
if ChargeId == chargeId {
Items := gamedata.GetItemList(v, "Items")
return Items
}
}
return nil
}
func GetActivityList() []*gamedata.ActivityData {
data, err := gamedata.GetData(CFG_ACTIVITY)
if err != nil {
log.Debug("GetActivityList err:%v", err)
return nil
}
List := make([]*gamedata.ActivityData, 0, len(data))
for k, v := range data {
StartTimeStr := gamedata.GetStringValue(v, "StartTime")
EndTimeStr := gamedata.GetStringValue(v, "EndTime")
StartTime := GoUtil.ParseTime(StartTimeStr)
EndTime := GoUtil.ParseTime(EndTimeStr)
Type := gamedata.GetIntValue(v, "Type")
Name := gamedata.GetStringValue(v, "Name")
Title := gamedata.GetStringValue(v, "Title")
Level := gamedata.GetIntValue(v, "Level")
ActivityData := &gamedata.ActivityData{
Id: GoUtil.Int(k),
Type: Type,
AType: gamedata.GetIntValue(v, "AType"),
Name: Name,
Title: Title,
Level: Level,
StartTime: StartTime,
EndTime: EndTime,
}
List = append(List, ActivityData)
}
return List
}
func GetActivityListOrigin() []*gamedata.ActivityData {
data, err := gamedata.GetData(CFG_ACTIVITY)
if err != nil {
log.Debug("GetActivityList err:%v", err)
return nil
}
List := make([]*gamedata.ActivityData, 0, len(data))
for k, v := range data {
StartTimeStr := gamedata.GetStringValue(v, "StartTime")
EndTimeStr := gamedata.GetStringValue(v, "EndTime")
StartTime := GoUtil.ParseTime(StartTimeStr)
EndTime := GoUtil.ParseTime(EndTimeStr)
Id, err := strconv.Atoi(k)
if err != nil {
log.Debug("GetActivityList parse id err:%v, key:%s", err, k)
continue
}
Type := gamedata.GetIntValue(v, "Type")
Name := gamedata.GetStringValue(v, "Name")
Title := gamedata.GetStringValue(v, "Title")
Level := gamedata.GetIntValue(v, "Level")
ActivityData := &gamedata.ActivityData{
Id: Id,
Type: Type,
AType: gamedata.GetIntValue(v, "AType"),
Name: Name,
Title: Title,
Level: Level,
StartTime: StartTime,
EndTime: EndTime,
}
List = append(List, ActivityData)
}
return List
}
func GetActivityRewardItems(ActId int) []*item.Item {
data, err := gamedata.GetData(CFG_ACTIVITY_GIFT)
if err != nil {
log.Debug("GetActivityRewardItems err:%v", err)
return nil
}
for _, v := range data {
ChargeId := gamedata.GetIntValue(v, "ChargeId")
AId := gamedata.GetIntValue(v, "AId")
if ChargeId == 0 && AId == ActId {
Items := gamedata.GetItemList(v, "Items")
return Items
}
}
return nil
}
func GetActivityRecycleMail(ActId int) (string, string, string) {
data, err := gamedata.GetDataByIntKey(CFG_ACTIVITY, ActId)
if err != nil {
log.Debug("GetActivityRecycleMail err:%v", err)
return "", "", ""
}
return gamedata.GetStringValue(data, "Title"), gamedata.GetStringValue(data, "Mail_Title"), gamedata.GetStringValue(data, "Mail_Content")
}

View File

@ -1,9 +1,11 @@
package avatarCfg
import (
"math/rand"
"server/gamedata"
"server/pkg/github.com/name5566/leaf/log"
"strconv"
"gitea.bywaystudios.com/pet_home/leaf/log"
)
const (
@ -20,7 +22,7 @@ func GetInitList() []int {
log.Debug("GetInitList err: %v", err)
return nil
}
r := make([]int, 0)
r := make([]int, 0, len(data))
for k, v := range data {
IsInit := gamedata.GetIntValue(v, "Init")
if IsInit == 1 {
@ -31,6 +33,15 @@ func GetInitList() []int {
return r
}
func GetRandInitId() int {
list := GetInitList()
if len(list) == 0 {
return 0
}
index := rand.Intn(len(list))
return list[index]
}
func CheckExist(Id int) bool {
data, err := gamedata.GetData(CFG_AVATAR)
if err != nil {
@ -40,3 +51,17 @@ func CheckExist(Id int) bool {
_, ok := data[strconv.Itoa(Id)]
return ok
}
func GetAllId() []int {
data, err := gamedata.GetData(CFG_AVATAR)
if err != nil {
log.Debug("GetAllId err: %v", err)
return nil
}
r := make([]int, 0, len(data))
for k := range data {
Id, _ := strconv.Atoi(k)
r = append(r, Id)
}
return r
}

View File

@ -1,39 +0,0 @@
package baseCfg
import (
"server/gamedata"
"server/pkg/github.com/name5566/leaf/log"
"strconv"
)
const (
CFG_BASE = "Base"
)
const (
ENERGY_SHOP_MUL = "energy_shop_mul"
)
func init() {
gamedata.InitCfg(CFG_BASE)
}
func GetEnergyByMul(T int) float64 {
data, err := gamedata.GetDataByKey(CFG_BASE, ENERGY_SHOP_MUL)
if err != nil {
log.Debug("GetEnergyByMul err:%v", err)
return 0
}
d := data["Value"].(map[string]interface{})
if len(d) == 0 {
return 0
}
T = min(T, len(d))
T1 := strconv.Itoa(T)
if v, ok := d[T1]; ok {
return v.(float64)
} else {
return 0
}
}

View File

@ -0,0 +1,98 @@
package baseCfg
import (
"server/game/mod/item"
"server/gamedata"
"strconv"
"gitea.bywaystudios.com/pet_home/leaf/log"
)
const (
CFG_BASE = "Base"
)
const (
ENERGY_SHOP_MUL = "energy_shop_mul"
ENERGY_CFG = "BaseEnergy"
MAX_ENERGY_LEVEL = 999 // 最大能量等级上限
)
func init() {
gamedata.InitCfg(CFG_BASE)
gamedata.InitCfg(ENERGY_CFG)
}
func GetEnergyByMul(T int) float64 {
data, err := gamedata.GetDataByKey(CFG_BASE, ENERGY_SHOP_MUL)
if err != nil {
log.Debug("GetEnergyByMul err:%v", err)
return 0
}
valueData, ok := data["Value"]
if !ok {
log.Debug("GetEnergyByMul: Value field not found")
return 0
}
d, ok := valueData.(map[string]interface{})
if !ok || len(d) == 0 {
log.Debug("GetEnergyByMul: Value is not a valid map")
return 0
}
T = min(T, len(d))
T1 := strconv.Itoa(T)
if v, ok := d[T1]; ok {
if floatVal, ok := v.(float64); ok {
return floatVal
}
log.Debug("GetEnergyByMul: value is not float64, key=%s", T1)
}
return 0
}
func GetFriendReplyReward() []*item.Item {
data, err := gamedata.GetDataByKey(CFG_BASE, "friend_reply")
if err != nil {
log.Debug("GetFriendReplyReward err:%v", err)
return nil
}
return gamedata.GetItemList(data, "Value")
}
func GetEnergyByADNum() []*item.Item {
data, err := gamedata.GetDataByKey(CFG_BASE, "ad_energy_gift")
if err != nil {
log.Debug("GetEnergyByADNum err:%v", err)
return nil
}
return gamedata.GetItemList(data, "Value")
}
func GetMaxEnergyMul(Lv int, Energy int) int {
Mul := 0
for i := 1; i <= MAX_ENERGY_LEVEL; i++ {
data, err := gamedata.GetDataByIntKey(ENERGY_CFG, i)
if err != nil {
break
}
UnlockLv := gamedata.GetIntValue(data, "UnlockLv")
needEnergy := gamedata.GetIntValue(data, "NeedEnergy")
if Lv < UnlockLv || Energy < needEnergy {
break
}
Mul = i - 1
}
return Mul
}
func GetLimitEnergyMul(Lv int) int {
data, err := gamedata.GetDataByIntKey(ENERGY_CFG, Lv+1)
if err != nil {
log.Debug("GetLimitEnergyMul err:%v, Lv=%d", err, Lv)
return 0
}
return gamedata.GetIntValue(data, "Limit")
}

View File

@ -1,12 +1,13 @@
package cardCfg
import (
"server/GoUtil"
"server/game/mod/item"
GoUtil "server/game_util"
"server/gamedata"
"server/pkg/github.com/name5566/leaf/log"
"strconv"
"strings"
"gitea.bywaystudios.com/pet_home/leaf/log"
)
const (
@ -28,51 +29,73 @@ func init() {
func GetAllCollectReward() []*item.Item {
data, err := gamedata.GetDataByKey(CARD_CONST_CFG_NAME, "all_collect_reward")
if err != nil {
log.Debug("GetAllCollectReward data not found")
log.Debug("GetAllCollectReward err:%v", err)
return nil
}
return item.ParseItem(data["Value"])
if value, ok := data["Value"]; ok {
return item.ParseItem(value)
}
return nil
}
func GetStarById(Id int) int {
data, err := gamedata.GetDataByIntKey(CARD_DETAIL_CFG_NAME, Id)
if err != nil {
log.Debug("GetStarById data not found")
log.Debug("GetStarById err:%v, Id=%d", err, Id)
return 0
}
return gamedata.GetIntValue(data, "Star")
}
func GetRoundById(Id int) int {
data, err := gamedata.GetDataByIntKey(CARD_DETAIL_CFG_NAME, Id)
if err != nil {
log.Debug("GetRoundById err:%v, Id=%d", err, Id)
return 0
}
return gamedata.GetIntValue(data, "Round")
}
// 获取开启卡包获得的卡牌数量
func GetPackRewardCnt(Star int) int {
data, err := gamedata.GetDataByIntKey(CARD_PACK_CFG_NAME, Star)
if err != nil {
log.Debug("GetPackRewardCnt data not found")
log.Debug("GetPackRewardCnt err:%v, Star=%d", err, Star)
return 0
}
return gamedata.GetIntValue(data, "RewardCnt")
}
// 获取开启开包得x星卡
// 获取开启开包得x星卡
func GetPackMustHave(Star int) int {
data, err := gamedata.GetDataByIntKey(CARD_PACK_CFG_NAME, Star)
if err != nil {
log.Debug("GetPackMustHave data not found")
log.Debug("GetPackMustHave err:%v, Star=%d", err, Star)
return 0
}
return gamedata.GetIntValue(data, "MustHave")
}
// 根据星级获取卡牌列表
func GetCardListByStar(Star, IsGold int) []int {
var CardList []int
func GetCardListByStar(Round, Star, IsGold int) []int {
data, err := gamedata.GetData(CARD_DETAIL_CFG_NAME)
if err != nil {
log.Debug("GetCardListByStar data not found")
log.Debug("GetCardListByStar err:%v", err)
return nil
}
CardList := make([]int, 0, len(data)/10)
for k, v := range data {
Extra := gamedata.GetIntValue(v, "Round")
if Round < Extra {
continue
}
vStar := gamedata.GetIntValue(v, "Star")
isGold := gamedata.GetIntValue(v, "IsGold")
if vStar == Star && IsGold == isGold {
id, _ := strconv.Atoi(k)
id, err := strconv.Atoi(k)
if err != nil {
log.Debug("GetCardListByStar parse id err:%v, key=%s", err, k)
continue
}
CardList = append(CardList, id)
}
}
@ -80,16 +103,20 @@ func GetCardListByStar(Star, IsGold int) []int {
}
// 获取随机卡牌
func GetRandListByStar(Star int) []int {
var randList []int
func GetRandListByStar(Star int) map[int]int {
data, err := gamedata.GetDataByIntKey(CARD_PACK_CFG_NAME, Star)
if err != nil {
log.Debug("GetRandListByStar data not found")
log.Debug("GetRandListByStar err:%v, Star=%d", err, Star)
return nil
}
r1 := gamedata.GetStringValue(data, "RandRate")
r2 := strings.Split(r1, ",")
randList = GoUtil.SliceStringToInt(r2)
return randList
randList := GoUtil.SliceStringToInt(r2)
r := make(map[int]int, len(randList))
for k, v := range randList {
r[k+1] = v
}
return r
}
// 获取卡牌收集奖励
@ -106,33 +133,33 @@ func GetCollectReward(Color int) ([]*item.Item, int) {
}
// 获取兑换奖励
func GetExchangeCfg(Id int) (int, []*item.Item, []int) {
func GetExchangeCfg(Id int) (int, []*item.Item) {
data, err := gamedata.GetDataByIntKey(CARD_EXCHANGE_CFG, Id)
if err != nil {
log.Debug("GetExchangeCfg data not found")
return 0, nil, nil
log.Debug("GetExchangeCfg err:%v, Id=%d", err, Id)
return 0, nil
}
itemNum := gamedata.GetIntValue(data, "Item")
itemList := []*item.Item{item.NewItem(item.ITEM_ENERGY_ID, itemNum)}
CostStar := gamedata.GetIntValue(data, "CostStar")
CardPackStr := gamedata.GetStringValue(data, "CardPack")
s1 := CardPackStr[1 : len(CardPackStr)-1]
CardPack := GoUtil.SliceStringToInt(strings.Split(s1, ","))
return CostStar, itemList, CardPack
itemList := gamedata.GetItemList(data, "Item")
return CostStar, itemList
}
// 根据系列获取卡牌列表
func GetCardListByColor(Id int) []int {
data, err := gamedata.GetData(CARD_DETAIL_CFG_NAME)
if err != nil {
log.Debug("GetCardListByColor data not found")
return []int{}
log.Debug("GetCardListByColor err:%v, Id=%d", err, Id)
return nil
}
var r []int
r := make([]int, 0, len(data)/5)
for k, v := range data {
color := gamedata.GetIntValue(v, "PictureAlbum")
if color == Id {
k1, _ := strconv.Atoi(k)
k1, err := strconv.Atoi(k)
if err != nil {
log.Debug("GetCardListByColor parse id err:%v, key=%s", err, k)
continue
}
r = append(r, k1)
}
}
@ -140,24 +167,42 @@ func GetCardListByColor(Id int) []int {
}
// 获取所有卡牌id
func GetAllCardId() []int {
func GetAllCardId(Round int) []int {
data, err := gamedata.GetData(CARD_DETAIL_CFG_NAME)
if err != nil {
log.Debug("GetCardListByColor data not found")
return []int{}
log.Debug("GetAllCardId err:%v, Round=%d", err, Round)
return nil
}
var r []int
for k := range data {
k1, _ := strconv.Atoi(k)
r := make([]int, 0, len(data))
for k, v := range data {
Extra := gamedata.GetIntValue(v, "Round")
if Round < Extra {
continue
}
k1, err := strconv.Atoi(k)
if err != nil {
log.Debug("GetAllCardId parse id err:%v, key=%s", err, k)
continue
}
r = append(r, k1)
}
return r
}
func CheckCardIsGold(Id int) bool {
data, err := gamedata.GetDataByIntKey(CARD_DETAIL_CFG_NAME, Id)
if err != nil {
log.Debug("CheckCardIsGold err:%v, Id=%d", err, Id)
return false
}
return gamedata.GetIntValue(data, "IsGold") == 1
}
func GetCardDuration() int64 {
data, err := gamedata.GetDataByKey(CARD_CONST_CFG_NAME, "duration")
if err != nil {
log.Debug("GetCardDuration data not found")
log.Debug("GetCardDuration err:%v", err)
return 0
}
return int64(gamedata.GetIntValue(data, "Value"))
}
@ -165,7 +210,8 @@ func GetCardDuration() int64 {
func GetExTimes() int {
data, err := gamedata.GetDataByKey(CARD_CONST_CFG_NAME, "exchange_times")
if err != nil {
log.Debug("GetExTimes data not found")
log.Debug("GetExTimes err:%v", err)
return 0
}
return gamedata.GetIntValue(data, "Value")
}
@ -173,7 +219,29 @@ func GetExTimes() int {
func GetReqTimes() int {
data, err := gamedata.GetDataByKey(CARD_CONST_CFG_NAME, "request_times")
if err != nil {
log.Debug("GetReqTimes data not found")
log.Debug("GetReqTimes err:%v", err)
return 0
}
return gamedata.GetIntValue(data, "Value")
}
func GetItemIdByCardId(CardId int) int {
data, err := gamedata.GetDataByIntKey(CARD_DETAIL_CFG_NAME, CardId)
if err != nil {
log.Debug("GetItemIdByCardId err:%v, CardId=%d", err, CardId)
return 0
}
return gamedata.GetIntValue(data, "ItemId")
}
func GetSeasonFirstReward() []*item.Item {
data, err := gamedata.GetDataByKey(CARD_CONST_CFG_NAME, "season_first")
if err != nil {
log.Debug("GetSeasonFirstReward err:%v", err)
return nil
}
if value, ok := data["Value"]; ok {
return item.ParseItem(value)
}
return nil
}

View File

@ -0,0 +1,156 @@
package catnipCfg
import (
"server/game/mod/item"
GoUtil "server/game_util"
"server/gamedata"
"gitea.bywaystudios.com/pet_home/leaf/log"
)
const (
CATNIP_TEMPLATE_CFG_NAME = "CatnipTemplate"
CATNIP_JACKPOT_CFG_NAME = "CatnipJackpot"
CATNIP_MULTIPLIER_CFG_NAME = "CatnipMultiplier"
CATNIP_GAME_CFG_NAME = "CatnipGame"
)
const (
CATNIP_REWARD_TYPE_LOW = 1
CATNIP_REWARD_TYPE_MID = 2
CATNIP_REWARD_TYPE_HIGH = 3
)
func init() {
gamedata.InitCfg(CATNIP_TEMPLATE_CFG_NAME)
gamedata.InitCfg(CATNIP_JACKPOT_CFG_NAME)
gamedata.InitCfg(CATNIP_MULTIPLIER_CFG_NAME)
gamedata.InitCfg(CATNIP_GAME_CFG_NAME)
}
func GetGameNum(Id int) int {
data, err := gamedata.GetDataByIntKey(CATNIP_TEMPLATE_CFG_NAME, Id)
if err != nil {
log.Debug("GetGameNum err:%v, Id=%d", err, Id)
return 0
}
return gamedata.GetIntValue(data, "PassNum")
}
func GetGameMaxProgress(Id int) int {
TemplateId := GetTemplateId(Id)
data, err := gamedata.GetData(CATNIP_GAME_CFG_NAME)
if err != nil {
log.Debug("GetGameMaxProgress err:%v, Id=%d", err, Id)
return 0
}
MaxProgress := 0
for _, v := range data {
if gamedata.GetIntValue(v, "Template") != TemplateId {
continue
}
MaxProgress = max(MaxProgress, gamedata.GetIntValue(v, "Need"))
}
return MaxProgress
}
func GetJackpotItem(Mul int) (int, []*item.Item, int, int) {
data, err := gamedata.GetData(CATNIP_JACKPOT_CFG_NAME)
if err != nil {
log.Debug("GetJackpotItem err:%v, Mul=%d", err, Mul)
return 0, nil, 0, 0
}
JackpotType := GetJackpotType(Mul)
r := make(map[int]int, len(data))
for k, v := range data {
if gamedata.GetIntValue(v, "Type") == JackpotType {
r[GoUtil.Int(k)] = 1
}
}
Id := GoUtil.RandMap(r)
if Id == 0 {
return 0, nil, 0, 0
}
itemData, err := gamedata.GetDataByIntKey(CATNIP_JACKPOT_CFG_NAME, Id)
if err != nil {
log.Debug("GetJackpotItem get itemData err:%v, Id=%d", err, Id)
return 0, nil, 0, 0
}
return Id, gamedata.GetItemList(itemData, "Items"), gamedata.GetIntValue(itemData, "Growth"), gamedata.GetIntValue(itemData, "FriendItems")
}
func GetJackpotType(Mul int) int {
data, err := gamedata.GetDataByIntKey(CATNIP_MULTIPLIER_CFG_NAME, Mul)
if err != nil {
log.Debug("GetJackpotType err:%v, Mul=%d", err, Mul)
return 0
}
R := map[int]int{
CATNIP_REWARD_TYPE_LOW: gamedata.GetIntValue(data, "Low"),
CATNIP_REWARD_TYPE_MID: gamedata.GetIntValue(data, "Mid"),
CATNIP_REWARD_TYPE_HIGH: gamedata.GetIntValue(data, "High"),
}
return GoUtil.RandMap(R)
}
func GetTemplateId(Id int) int {
data, err := gamedata.GetDataByIntKey(CATNIP_TEMPLATE_CFG_NAME, Id)
if err != nil {
log.Debug("GetTemplateId err:%v, Id=%d", err, Id)
return 0
}
return gamedata.GetIntValue(data, "Template")
}
func GetProgressReward(Id int, Rewards []int, Progress int) ([]*item.Item, []int) {
TemplateId := GetTemplateId(Id)
data, err := gamedata.GetData(CATNIP_GAME_CFG_NAME)
if err != nil {
log.Debug("GetProgressReward err:%v, Id=%d", err, Id)
return nil, nil
}
Items := make([]*item.Item, 0, len(data))
Ids := make([]int, 0, len(data))
for k, v := range data {
rewardId := GoUtil.Int(k)
if !GoUtil.InArray(rewardId, Rewards) && gamedata.GetIntValue(v, "Need") < Progress && gamedata.GetIntValue(v, "Template") == TemplateId {
Items = append(Items, gamedata.GetItemList(v, "Items")...)
Ids = append(Ids, rewardId)
}
}
return Items, Ids
}
func GetProgressNum(Id int) int {
TemplateId := GetTemplateId(Id)
data, err := gamedata.GetData(CATNIP_GAME_CFG_NAME)
if err != nil {
log.Debug("GetProgressNum err:%v, Id=%d", err, Id)
return 0
}
Num := 0
for _, v := range data {
if gamedata.GetIntValue(v, "Template") == TemplateId {
Num++
}
}
return Num
}
func GetItemCost(Id, Mul int) []*item.Item {
data, err := gamedata.GetDataByIntKey(CATNIP_TEMPLATE_CFG_NAME, Id)
if err != nil {
log.Debug("GetItemCost err:%v, Id=%d", err, Id)
return nil
}
return item.MutilItem(gamedata.GetItemList(data, "ItemCost"), Mul)
}
func GetGrandReward(Id int) []*item.Item {
data, err := gamedata.GetDataByIntKey(CATNIP_TEMPLATE_CFG_NAME, Id)
if err != nil {
log.Debug("GetGrandReward err:%v, Id=%d", err, Id)
return nil
}
return gamedata.GetItemList(data, "Reward")
}

View File

@ -1,47 +0,0 @@
package champshipCfg
import (
"server/GoUtil"
"server/game/mod/item"
"server/gamedata"
)
const (
CFG_CHAMPSHIP_SCORE = "ChampshipScore"
CFG_CHAMPSHIP_RANK = "ChampshipRank"
CFG_CHAMPSHIP_JACKPOT = "ChampshipJackpot"
)
func init() {
gamedata.InitCfg(CFG_CHAMPSHIP_SCORE)
gamedata.InitCfg(CFG_CHAMPSHIP_RANK)
gamedata.InitCfg(CFG_CHAMPSHIP_JACKPOT)
}
func GetChessScore(ChessLv int) int {
data, err := gamedata.GetDataByIntKey(CFG_CHAMPSHIP_SCORE, ChessLv)
if err != nil {
return 0
}
return gamedata.GetIntValue(data, "Score")
}
func GetReward(Reward, Score int) (int, []*item.Item) {
data, err := gamedata.GetData(CFG_CHAMPSHIP_JACKPOT)
if err != nil {
return Reward, nil
}
r := make([]*item.Item, 0)
for k, v := range data {
Id := GoUtil.Int(k)
if Reward >= Id {
continue
}
Reward = max(Reward, Id)
if Score >= gamedata.GetIntValue(v, "Score") {
Items := item.ParseItem(gamedata.GetStringValue(v, "Items"))
r = item.Merge(r, Items)
}
}
return Reward, r
}

View File

@ -0,0 +1,148 @@
package champshipCfg
import (
"server/game/mod/item"
GoUtil "server/game_util"
"server/gamedata"
"strings"
"gitea.bywaystudios.com/pet_home/leaf/log"
)
const (
CFG_CHAMPSHIP_SCORE = "ChampshipScore"
CFG_CHAMPSHIP_RANK = "ChampshipRank"
CFG_CHAMPSHIP_JACKPOT = "ChampshipJackpot"
CFG_CHAMPSHIP_GROUP = "ChampshipGroup"
CFG_CHAMPSHIP_CONST = "ChampshipConst"
)
func init() {
gamedata.InitCfg(CFG_CHAMPSHIP_SCORE)
gamedata.InitCfg(CFG_CHAMPSHIP_RANK)
gamedata.InitCfg(CFG_CHAMPSHIP_JACKPOT)
gamedata.InitCfg(CFG_CHAMPSHIP_GROUP)
gamedata.InitCfg(CFG_CHAMPSHIP_CONST)
}
func GetGroupId(N int) int {
data, err := gamedata.GetData(CFG_CHAMPSHIP_GROUP)
if err != nil {
log.Debug("GetGroupId err:%v, N=%d", err, N)
return 1
}
for k, v := range data {
if N >= gamedata.GetIntValue(v, "Min") && N <= gamedata.GetIntValue(v, "Max") {
return GoUtil.Int(k)
}
}
return 11
}
func GetChessScore(ChessLv int) int {
data, err := gamedata.GetDataByIntKey(CFG_CHAMPSHIP_SCORE, ChessLv)
if err != nil {
log.Debug("GetChessScore err:%v, ChessLv=%d", err, ChessLv)
return 0
}
return gamedata.GetIntValue(data, "Score")
}
func GetReward(Reward, Score, activityId, orderFactor int) (int, []*item.Item) {
data, err := gamedata.GetData(CFG_CHAMPSHIP_JACKPOT)
if err != nil {
log.Debug("GetReward err:%v, Reward=%d, Score=%d", err, Reward, Score)
return Reward, nil
}
r := make([]*item.Item, 0, len(data))
NewReward := Reward
for _, v := range data {
rewardGroupType := gamedata.GetIntValue(v, "RewardGroupType")
if rewardGroupType != activityId {
continue
}
Id := gamedata.GetIntValue(v, "Step")
if Reward >= Id {
continue
}
if Score < gamedata.GetIntValue(v, "Total") {
continue
}
Items := item.ParseItem(gamedata.GetValue(v, "Items"))
r = item.Merge(r, Items)
NewReward = max(NewReward, Id)
starReward := gamedata.GetIntValue(v, "StarReward")
if starReward > 0 {
starnum := int((float64(starReward) * float64(orderFactor) / 500)) * 5
if starnum > 0 {
r = append(r, item.NewItem(item.ITEM_STAR_ID, starnum))
}
}
}
return NewReward, r
}
func GetRewardItems(Reward int) ([]*item.Item, int) {
data, err := gamedata.GetDataByIntKey(CFG_CHAMPSHIP_JACKPOT, Reward)
if err != nil {
log.Debug("GetRewardItems err:%v, Reward=%d", err, Reward)
return nil, 0
}
return gamedata.GetItemList(data, "Items"), gamedata.GetIntValue(data, "Score")
}
func GetRewardTotalScore(Reward int) int {
data, err := gamedata.GetDataByIntKey(CFG_CHAMPSHIP_JACKPOT, Reward)
if err != nil {
log.Debug("GetRewardTotalScore err:%v, Reward=%d", err, Reward)
return 0
}
return gamedata.GetIntValue(data, "Total")
}
func GetRankReward(Rank, yesterdayActivityId int) []*item.Item {
data, err := gamedata.GetData(CFG_CHAMPSHIP_RANK)
if err != nil {
log.Debug("GetRankReward err:%v, Rank=%d, yesterdayActivityId=%d", err, Rank, yesterdayActivityId)
return nil
}
for _, v := range data {
if Rank >= gamedata.GetIntValue(v, "Min") && Rank <= gamedata.GetIntValue(v, "Max") && yesterdayActivityId == gamedata.GetIntValue(v, "RewardGroupType") {
return gamedata.GetItemList(v, "Items")
}
}
return nil
}
func GetMaxRewardId() int {
data, err := gamedata.GetData(CFG_CHAMPSHIP_JACKPOT)
if err != nil {
log.Debug("GetMaxRewardId err:%v", err)
return 0
}
MaxId := 0
for k := range data {
Id := GoUtil.Int(k)
if Id > MaxId {
MaxId = Id
}
}
return MaxId
}
func GetChampshipActivityId() []int {
data, err := gamedata.GetDataByKey(CFG_CHAMPSHIP_CONST, "RewardGroupType_const")
if err != nil {
log.Debug("GetChampshipActivityId err:%v", err)
return nil
}
value := gamedata.GetStringValue(data, "Value")
var res []int
for _, v := range strings.Split(value, "|") {
id := GoUtil.Int(v)
if id > 0 {
res = append(res, id)
}
}
return res
}

View File

@ -1,253 +0,0 @@
package chargeCfg
import (
"server/GoUtil"
"server/game/mod/item"
"server/gamedata"
"sort"
"strings"
)
const (
CFG_CHARGE = "Charge"
CFG_DIAMOND_SHOP = "DiamondShop"
CFG_ENERGY_SHOP = "EnergyShop"
CFG_SEPCIAL_SHOP = "SpecialShop"
CFG_FREE_SHOP = "FreeShop"
CFG_CHARGE_CONST = "ChargeConst"
CFG_CHARGE_GIFT = "ChargeGift"
)
func init() {
gamedata.InitCfg(CFG_CHARGE)
gamedata.InitCfg(CFG_DIAMOND_SHOP)
gamedata.InitCfg(CFG_ENERGY_SHOP)
gamedata.InitCfg(CFG_SEPCIAL_SHOP)
gamedata.InitCfg(CFG_FREE_SHOP)
gamedata.InitCfg(CFG_CHARGE_CONST)
gamedata.InitCfg(CFG_CHARGE_GIFT)
}
func GetMoneyCharge(ChargeId int) float64 {
data, err := gamedata.GetDataByIntKey(CFG_CHARGE, ChargeId)
if err != nil {
return 0
}
return gamedata.GetFloatValue(data, "Money")
}
func GetDiamondShopReward(ChargeId int) []*item.Item {
data, err := gamedata.GetData(CFG_DIAMOND_SHOP)
if err != nil {
return nil
}
for _, v := range data {
if gamedata.GetIntValue(v, "ChargeId") == ChargeId {
return []*item.Item{item.NewItem(item.ITEM_DIAMOND_ID, gamedata.GetIntValue(v, "Diamond"))}
}
}
return nil
}
func GetEnergyShopId(ChargeId int) int {
data, err := gamedata.GetData(CFG_ENERGY_SHOP)
if err != nil {
return 0
}
for k, v := range data {
if gamedata.GetIntValue(v, "ChargeId") == ChargeId {
return GoUtil.Int(k)
}
}
return 0
}
func GetEnergyShopReward(ChargeId int, First bool) []*item.Item {
data, err := gamedata.GetData(CFG_ENERGY_SHOP)
if err != nil {
return nil
}
for _, v := range data {
if gamedata.GetIntValue(v, "ChargeId") == ChargeId {
Energy := gamedata.GetIntValue(v, "Energy")
if !First {
Energy += gamedata.GetIntValue(v, "FirstCharge")
}
return []*item.Item{item.NewItem(item.ITEM_ENERGY_ID, Energy)}
}
}
return nil
}
func GetSpecialShopReward(ChargeId int) []*item.Item {
data, err := gamedata.GetData(CFG_SEPCIAL_SHOP)
if err != nil {
return nil
}
for _, v := range data {
if gamedata.GetIntValue(v, "ChargeId") == ChargeId {
return item.ParseItem(gamedata.GetValue(v, "Items"))
}
}
return nil
}
func GetSpecialShopType(ChargeId int) int {
data, err := gamedata.GetData(CFG_SEPCIAL_SHOP)
if err != nil {
return 0
}
for _, v := range data {
if gamedata.GetIntValue(v, "ChargeId") == ChargeId {
return gamedata.GetIntValue(v, "Type")
}
}
return 0
}
func GetFreeShopReward(ChargeId int) []*item.Item {
data, err := gamedata.GetDataByIntKey(CFG_FREE_SHOP, ChargeId)
if err != nil {
return nil
}
Item := gamedata.GetValue(data, "Items")
return item.ParseItem(Item)
}
func GetSpecialShopGrade(Money float64, Type int) int {
type grade struct {
id int
money float64
}
data, err := gamedata.GetData(CFG_SEPCIAL_SHOP)
if err != nil {
return 0
}
l := make([]grade, 0)
for _, v := range data {
if gamedata.GetIntValue(v, "Type") != Type {
continue
}
l = append(l, grade{
id: gamedata.GetIntValue(v, "Grade"),
money: GetMoneyCharge(gamedata.GetIntValue(v, "ChargeId")),
})
}
sort.Slice(l, func(i, j int) bool {
return l[i].money < l[j].money
})
for _, v := range l {
if Money < v.money {
return v.id
}
}
return 0
}
func GetUnlockShopLv() int {
data, err := gamedata.GetDataByKey(CFG_CHARGE_CONST, "shop_unlock_lv")
if err != nil {
return 0
}
return gamedata.GetIntValue(data, "Value")
}
func GetSpecialShopCount() int {
data, err := gamedata.GetDataByKey(CFG_CHARGE_CONST, "special_shop_count")
if err != nil {
return 0
}
return gamedata.GetIntValue(data, "Value")
}
func GetAdChargeId() int {
data, err := gamedata.GetDataByKey(CFG_CHARGE_CONST, "ad_charge_id")
if err != nil {
return 0
}
return gamedata.GetIntValue(data, "Value")
}
func GetPiggyDuration() int {
data, err := gamedata.GetDataByKey(CFG_CHARGE_CONST, "pig_duration")
if err != nil {
return 0
}
return gamedata.GetIntValue(data, "Value")
}
func GetPiggyMaxDia() int {
data, err := gamedata.GetDataByKey(CFG_CHARGE_CONST, "pig_max_diamond")
if err != nil {
return 0
}
return gamedata.GetIntValue(data, "Value")
}
func GetPiggyRandDia() (int, int) {
data, err := gamedata.GetDataByKey(CFG_CHARGE_CONST, "pig_diamond_rand")
if err != nil {
return 0, 0
}
str := gamedata.GetStringValue(data, "Value")
strArr := strings.Split(str, ",")
if len(strArr) != 2 {
return 0, 0
}
return GoUtil.Int(strArr[0]), GoUtil.Int(strArr[1])
}
func GetPiggyBankChargeId() int {
data, err := gamedata.GetDataByKey(CFG_CHARGE_CONST, "pig_charge_id")
if err != nil {
return 0
}
return gamedata.GetIntValue(data, "Value")
}
func GetGiftReward(ChargeId int) []*item.Item {
data, err := gamedata.GetData(CFG_CHARGE_GIFT)
if err != nil {
return nil
}
for _, v := range data {
if gamedata.GetIntValue(v, "ChargeId") == ChargeId {
return item.ParseItem(gamedata.GetValue(v, "Items"))
}
}
return nil
}
func GetGiftLimit(ChargeId int) int {
data, err := gamedata.GetData(CFG_CHARGE_GIFT)
if err != nil {
return 0
}
for _, v := range data {
if gamedata.GetIntValue(v, "ChargeId") == ChargeId {
return gamedata.GetIntValue(v, "Count")
}
}
return 0
}
func GetGiftId(ChargeId int) int {
data, err := gamedata.GetData(CFG_CHARGE_GIFT)
if err != nil {
return 0
}
for k, v := range data {
if gamedata.GetIntValue(v, "ChargeId") == ChargeId {
return GoUtil.Int(k)
}
}
return 0
}
func GetChargeInfo(ChargeId int) (float64, string) {
data, err := gamedata.GetDataByIntKey(CFG_CHARGE, ChargeId)
if err != nil {
return 0, ""
}
return gamedata.GetFloatValue(data, "Money"), gamedata.GetStringValue(data, "Unit")
}

View File

@ -0,0 +1,419 @@
package chargeCfg
import (
"server/game/mod/item"
GoUtil "server/game_util"
"server/gamedata"
"sort"
"strings"
"gitea.bywaystudios.com/pet_home/leaf/log"
)
const (
CFG_CHARGE = "Charge"
CFG_DIAMOND_SHOP = "DiamondShop" // 钻石商店
CFG_ENERGY_SHOP = "EnergyShop" // 体力商店
CFG_SEPCIAL_SHOP = "SpecialShop" // 特惠商店
CFG_FREE_SHOP = "FreeShop" // 免费商店
CFG_CHARGE_CONST = "ChargeConst" // 充值常量配置
CFG_CHARGE_GIFT = "ChargeGift" // 充值礼包
CFG_CHARGE_WISH = "ChargeWish" // 许愿配置
CFG_CHARGE_PETCOIN = "ChargePetCoin" // 宠物币充值配置
CFG_CHARGE_AD = "ChargeAD" // 广告充值配置
)
func init() {
gamedata.InitCfg(CFG_CHARGE)
gamedata.InitCfg(CFG_DIAMOND_SHOP)
gamedata.InitCfg(CFG_ENERGY_SHOP)
gamedata.InitCfg(CFG_SEPCIAL_SHOP)
gamedata.InitCfg(CFG_FREE_SHOP)
gamedata.InitCfg(CFG_CHARGE_CONST)
gamedata.InitCfg(CFG_CHARGE_GIFT)
gamedata.InitCfg(CFG_CHARGE_WISH)
gamedata.InitCfg(CFG_CHARGE_PETCOIN)
gamedata.InitCfg(CFG_CHARGE_AD)
}
func GetADReward(ChargeId int) ([]*item.Item, int) {
data, err := gamedata.GetData(CFG_CHARGE_AD)
if err != nil {
log.Debug("GetADReward err:%v, ChargeId=%d", err, ChargeId)
return nil, 0
}
for _, v := range data {
if gamedata.GetIntValue(v, "ChargeId") == ChargeId {
return item.ParseItem(gamedata.GetValue(v, "Items")), gamedata.GetIntValue(v, "PetWork")
}
}
return nil, 0
}
func GetWeeklyDiscountDay() int {
data, err := gamedata.GetDataByKey(CFG_CHARGE_CONST, "weekly_discount_day")
if err != nil {
log.Debug("GetWeeklyDiscountDay err:%v", err)
return -1
}
return gamedata.GetIntValue(data, "Value")
}
func GetMoneyCharge(ChargeId int) float64 {
data, err := gamedata.GetDataByIntKey(CFG_CHARGE, ChargeId)
if err != nil {
log.Debug("GetMoneyCharge err:%v, ChargeId=%d", err, ChargeId)
return 0
}
return gamedata.GetFloatValue(data, "Money")
}
func GetDiamondShopReward(ChargeId int) []*item.Item {
data, err := gamedata.GetData(CFG_DIAMOND_SHOP)
if err != nil {
log.Debug("GetDiamondShopReward err:%v, ChargeId=%d", err, ChargeId)
return nil
}
for _, v := range data {
if gamedata.GetIntValue(v, "ChargeId") == ChargeId {
return []*item.Item{item.NewItem(item.ITEM_DIAMOND_ID, gamedata.GetIntValue(v, "Diamond"))}
}
}
return nil
}
func GetEnergyShopId(ChargeId int) int {
data, err := gamedata.GetData(CFG_ENERGY_SHOP)
if err != nil {
log.Debug("GetEnergyShopId err:%v, ChargeId=%d", err, ChargeId)
return 0
}
for k, v := range data {
if gamedata.GetIntValue(v, "ChargeId") == ChargeId {
return GoUtil.Int(k)
}
}
return 0
}
func GetWeeklyInfo(Pos int) (int, int) {
data, err := gamedata.GetDataByKey(CFG_CHARGE_CONST, "weekly_chess_shop")
if err != nil {
log.Debug("GetWeeklyInfo err:%v, Pos=%d", err, Pos)
return 0, 0
}
valueData, ok := data["Value"]
if !ok {
log.Debug("GetWeeklyInfo: Value field not found")
return 0, 0
}
r, ok := valueData.([]interface{})
if !ok {
log.Debug("GetWeeklyInfo: Value is not []interface{}")
return 0, 0
}
for _, v := range r {
v1, ok := v.(map[string]interface{})
if !ok {
continue
}
Id := GoUtil.Int(v1["Id"])
if Id == Pos {
return GoUtil.Int(v1["Discount"]), GoUtil.Int(v1["Limit"])
}
}
return 0, 0
}
func GetWeeklyInfoAll() map[int]gamedata.WeeklyDiscountInfo {
data, err := gamedata.GetDataByKey(CFG_CHARGE_CONST, "weekly_chess_shop")
if err != nil {
log.Debug("GetWeeklyInfoAll err:%v", err)
return nil
}
valueData, ok := data["Value"]
if !ok {
log.Debug("GetWeeklyInfoAll: Value field not found")
return nil
}
r, ok := valueData.([]interface{})
if !ok {
log.Debug("GetWeeklyInfoAll: Value is not []interface{}")
return nil
}
res := make(map[int]gamedata.WeeklyDiscountInfo, len(r))
for _, v := range r {
v1, ok := v.(map[string]interface{})
if !ok {
continue
}
Id := GoUtil.Int(v1["Id"])
res[Id] = gamedata.WeeklyDiscountInfo{
Id: GoUtil.Int(v1["Id"]),
Discount: GoUtil.Int(v1["Discount"]),
WeeklyLimit: GoUtil.Int(v1["Limit"]),
}
}
return res
}
func GetEnergyShopReward(ChargeId int, First bool) []*item.Item {
data, err := gamedata.GetData(CFG_ENERGY_SHOP)
if err != nil {
log.Debug("GetEnergyShopReward err:%v, ChargeId=%d", err, ChargeId)
return nil
}
for _, v := range data {
if gamedata.GetIntValue(v, "ChargeId") == ChargeId {
Energy := gamedata.GetIntValue(v, "Energy")
if !First {
Energy += gamedata.GetIntValue(v, "FirstCharge")
}
return []*item.Item{item.NewItem(item.ITEM_ENERGY_ID, Energy)}
}
}
return nil
}
func GetSpecialShopReward(ChargeId int) []*item.Item {
data, err := gamedata.GetData(CFG_SEPCIAL_SHOP)
if err != nil {
log.Debug("GetSpecialShopReward err:%v, ChargeId=%d", err, ChargeId)
return nil
}
for _, v := range data {
if gamedata.GetIntValue(v, "ChargeId") == ChargeId {
return item.ParseItem(gamedata.GetValue(v, "Items"))
}
}
return nil
}
func GetSpecialShopType(ChargeId int) int {
data, err := gamedata.GetData(CFG_SEPCIAL_SHOP)
if err != nil {
log.Debug("GetSpecialShopType err:%v, ChargeId=%d", err, ChargeId)
return 0
}
for _, v := range data {
if gamedata.GetIntValue(v, "ChargeId") == ChargeId {
return gamedata.GetIntValue(v, "Type")
}
}
return 0
}
func GetFreeShopReward(ChargeId int) []*item.Item {
data, err := gamedata.GetDataByIntKey(CFG_FREE_SHOP, ChargeId)
if err != nil {
log.Debug("GetFreeShopReward err:%v, ChargeId=%d", err, ChargeId)
return nil
}
Item := gamedata.GetValue(data, "Items")
return item.ParseItem(Item)
}
func GetSpecialShopGrade(Money float64, Type int) int {
type grade struct {
id int
money float64
}
data, err := gamedata.GetData(CFG_SEPCIAL_SHOP)
if err != nil {
log.Debug("GetSpecialShopGrade err:%v, Money=%.2f, Type=%d", err, Money, Type)
return 0
}
l := make([]grade, 0, len(data))
for _, v := range data {
if gamedata.GetIntValue(v, "Type") != Type {
continue
}
l = append(l, grade{
id: gamedata.GetIntValue(v, "Grade"),
money: GetMoneyCharge(gamedata.GetIntValue(v, "ChargeId")),
})
}
sort.Slice(l, func(i, j int) bool {
return l[i].money > l[j].money
})
if len(l) == 0 {
return 0
}
maxMoney := 0.0
for _, v := range l {
if Money >= v.money {
maxMoney = max(maxMoney, v.money)
}
}
for _, v := range l {
if v.money == maxMoney {
return v.id
}
}
return l[len(l)-1].id
}
func GetUnlockShopLv() int {
data, err := gamedata.GetDataByKey(CFG_CHARGE_CONST, "shop_unlock_lv")
if err != nil {
log.Debug("GetUnlockShopLv err:%v", err)
return 0
}
return gamedata.GetIntValue(data, "Value")
}
func GetSpecialShopCount() int {
data, err := gamedata.GetDataByKey(CFG_CHARGE_CONST, "special_shop_count")
if err != nil {
log.Debug("GetSpecialShopCount err:%v", err)
return 0
}
return gamedata.GetIntValue(data, "Value")
}
func GetAdChargeId() int {
data, err := gamedata.GetDataByKey(CFG_CHARGE_CONST, "ad_charge_id")
if err != nil {
log.Debug("GetAdChargeId err:%v", err)
return 0
}
return gamedata.GetIntValue(data, "Value")
}
func GetPiggyDuration() int {
data, err := gamedata.GetDataByKey(CFG_CHARGE_CONST, "pig_duration")
if err != nil {
log.Debug("GetPiggyDuration err:%v", err)
return 0
}
return gamedata.GetIntValue(data, "Value")
}
func GetPiggyMaxDia() int {
data, err := gamedata.GetDataByKey(CFG_CHARGE_CONST, "pig_max_diamond")
if err != nil {
log.Debug("GetPiggyMaxDia err:%v", err)
return 0
}
return gamedata.GetIntValue(data, "Value")
}
func GetPiggyRandDia() (int, int) {
data, err := gamedata.GetDataByKey(CFG_CHARGE_CONST, "pig_diamond_rand")
if err != nil {
log.Debug("GetPiggyRandDia err:%v", err)
return 0, 0
}
str := gamedata.GetStringValue(data, "Value")
strArr := strings.Split(str, ",")
if len(strArr) != 2 {
return 0, 0
}
return GoUtil.Int(strArr[0]), GoUtil.Int(strArr[1])
}
func GetPiggyBankChargeId() int {
data, err := gamedata.GetDataByKey(CFG_CHARGE_CONST, "pig_charge_id")
if err != nil {
log.Debug("GetPiggyBankChargeId err:%v", err)
return 0
}
return gamedata.GetIntValue(data, "Value")
}
func GetGiftReward(ChargeId int) []*item.Item {
data, err := gamedata.GetData(CFG_CHARGE_GIFT)
if err != nil {
log.Debug("GetGiftReward err:%v, ChargeId=%d", err, ChargeId)
return nil
}
for _, v := range data {
if gamedata.GetIntValue(v, "ChargeId") == ChargeId {
return item.ParseItem(gamedata.GetValue(v, "Items"))
}
}
return nil
}
func GetGiftLimit(ChargeId int) int {
data, err := gamedata.GetData(CFG_CHARGE_GIFT)
if err != nil {
log.Debug("GetGiftLimit err:%v, ChargeId=%d", err, ChargeId)
return 0
}
for _, v := range data {
if gamedata.GetIntValue(v, "ChargeId") == ChargeId {
return gamedata.GetIntValue(v, "Count")
}
}
return 0
}
func GetGiftId(ChargeId int) int {
data, err := gamedata.GetData(CFG_CHARGE_GIFT)
if err != nil {
log.Debug("GetGiftId err:%v, ChargeId=%d", err, ChargeId)
return 0
}
for k, v := range data {
if gamedata.GetIntValue(v, "ChargeId") == ChargeId {
return GoUtil.Int(k)
}
}
return 0
}
func GetChargeInfo(ChargeId int) (float64, string) {
data, err := gamedata.GetDataByIntKey(CFG_CHARGE, ChargeId)
if err != nil {
log.Debug("GetChargeInfo err:%v, ChargeId=%d", err, ChargeId)
return 0, ""
}
return gamedata.GetFloatValue(data, "Money"), gamedata.GetStringValue(data, "Unit")
}
func GetProduceName(ChargeId int) string {
data, err := gamedata.GetDataByIntKey(CFG_CHARGE, ChargeId)
if err != nil {
log.Debug("GetProduceName err:%v, ChargeId=%d", err, ChargeId)
return ""
}
return gamedata.GetStringValue(data, "Remark")
}
func GetWishCount(ItemId int) int {
data, err := gamedata.GetDataByIntKey(CFG_CHARGE_WISH, ItemId)
if err != nil {
log.Debug("GetWishCount err:%v, ItemId=%d", err, ItemId)
return 0
}
return gamedata.GetIntValue(data, "Wish")
}
func GetPetCoinShopReward(ChargeId int) []*item.Item {
data, err := gamedata.GetData(CFG_CHARGE_PETCOIN)
if err != nil {
log.Debug("GetPetCoinShopReward err:%v, ChargeId=%d", err, ChargeId)
return nil
}
for _, v := range data {
if gamedata.GetIntValue(v, "ChargeId") == ChargeId {
return gamedata.GetItemList(v, "Items")
}
}
return nil
}
func GetWeeklyDiscountStartEnd() (int64, int64) {
data, err := gamedata.GetDataByKey(CFG_CHARGE_CONST, "weekly_discount_time")
if err != nil {
log.Debug("GetWeeklyDiscountStartEnd err:%v", err)
return 0, 0
}
str := gamedata.GetStringValue(data, "Value")
strArr := strings.Split(str, "|")
if len(strArr) != 2 {
return 0, 0
}
return GoUtil.ParseTime(strArr[0]), GoUtil.ParseTime(strArr[1])
}

View File

@ -0,0 +1,68 @@
package collectCfg
import (
"server/game/mod/item"
GoUtil "server/game_util"
"server/gamedata"
"gitea.bywaystudios.com/pet_home/leaf/log"
)
const (
CFG_COLLECT = "Collect"
CFG_COLLECT_JACKPOT = "CollectJackpot"
)
func init() {
gamedata.InitCfg(CFG_COLLECT)
gamedata.InitCfg(CFG_COLLECT_JACKPOT)
}
func GetJackpotId() []int {
data, err := gamedata.GetData(CFG_COLLECT_JACKPOT)
if err != nil {
log.Debug("GetJackpotId err:%v", err)
return nil
}
ret := make([]int, 0, len(data))
for k := range data {
ret = append(ret, GoUtil.Int(k))
}
return ret
}
func GetJackpotItems(Id int) []*item.Item {
data, err := gamedata.GetDataByIntKey(CFG_COLLECT_JACKPOT, Id)
if err != nil {
log.Debug("GetJackpotItems err:%v, Id=%d", err, Id)
return nil
}
return gamedata.GetItemList(data, "Items")
}
func GetRewardNeed(Id int) int {
data, err := gamedata.GetDataByIntKey(CFG_COLLECT, Id)
if err != nil {
log.Debug("GetRewardNeed err:%v, Id=%d", err, Id)
return 0
}
return gamedata.GetIntValue(data, "Need")
}
func GetRewardType(Id int) int {
data, err := gamedata.GetDataByIntKey(CFG_COLLECT, Id)
if err != nil {
log.Debug("GetRewardType err:%v, Id=%d", err, Id)
return 0
}
return gamedata.GetIntValue(data, "Type")
}
func GetRewardLen() int {
data, err := gamedata.GetData(CFG_COLLECT)
if err != nil {
log.Debug("GetRewardLen err:%v", err)
return 0
}
return len(data)
}

View File

@ -7,18 +7,19 @@ import (
var (
// log conf
LogFlag = log.LstdFlags
LogFlag = log.LstdFlags | log.Lmicroseconds
// gate conf
PendingWriteNum = 2000
MaxMsgLen uint32 = 16384 // 16KB
PendingWriteNum = 2000 // 客户端链接的写入消息队列长度
MaxMsgLen uint32 = 65536 // 16KB
HTTPTimeout = 10 * time.Second
LenMsgLen = 2
LittleEndian = false
// skeleton conf
GoLen = 10000
TimerDispatcherLen = 10000
AsynCallLen = 10000
ChanRPCLen = 10000
// 增加 goroutine 相关配置,避免 "Too many goroutines" 错误
GoLen = 50000 // 从 10000 增加到 50000控制并发 goroutine 数量
TimerDispatcherLen = 50000 // 从 10000 增加到 50000定时器队列长度
AsynCallLen = 50000 // 从 10000 增加到 50000异步调用队列长度
ChanRPCLen = 50000 // 从 10000 增加到 50000RPC 通道长度
)

View File

@ -1,89 +0,0 @@
package dailyTaskCfg
import (
"server/game/mod/item"
"server/gamedata"
"server/pkg/github.com/name5566/leaf/log"
"strconv"
)
const (
DAILY_TASK_CONST = "DailyTaskConst"
DAILY_TASK = "DailyTask"
DAILY_TASK_ACTIVE = "DailyTaskActive"
DAILY_TASK_JACKPOT = "DailyTaskJackpot"
)
func init() {
gamedata.InitCfg(DAILY_TASK_CONST)
gamedata.InitCfg(DAILY_TASK)
gamedata.InitCfg(DAILY_TASK_ACTIVE)
gamedata.InitCfg(DAILY_TASK_JACKPOT)
}
// 获取任务列表
func GetTaskList(Lv int) map[int]string {
r := make(map[int]string)
data, err := gamedata.GetData(DAILY_TASK)
if err != nil {
log.Debug("GetTaskList get data nil")
return r
}
for k, v := range data {
lv := gamedata.GetIntValue(v, "Lv")
if Lv >= lv {
k1, _ := strconv.Atoi(k)
r[k1] = gamedata.GetStringValue(v, "Task")
}
}
return r
}
func GetTaskActiveId() []int {
data, err := gamedata.GetData(DAILY_TASK_ACTIVE)
if err != nil {
log.Debug("GetTaskActiveId get data nil")
return []int{}
}
r := make([]int, 0, len(data))
for k := range data {
Id, _ := strconv.Atoi(k)
r = append(r, Id)
}
return r
}
func GetTaskActiveById(Id int) int {
data, err := gamedata.GetDataByIntKey(DAILY_TASK_ACTIVE, Id)
if err != nil {
log.Debug("GetTaskActiveById get data nil")
return 0
}
return gamedata.GetIntValue(data, "Num")
}
type Jackpot struct {
Items []*item.Item
Is_card int
}
func GetTaskWeekJackpot() map[int]Jackpot {
r := make(map[int]Jackpot)
data, err := gamedata.GetData(DAILY_TASK_JACKPOT)
if err != nil {
log.Debug("GetTaskWeekJackpot get data nil")
return r
}
for k, v := range data {
Id, _ := strconv.Atoi(k)
isCard := gamedata.GetIntValue(v, "CardPack")
Item := item.ParseItem(gamedata.GetValue(v, "Items"))
r[Id] = Jackpot{
Items: Item,
Is_card: isCard,
}
}
return r
}

View File

@ -0,0 +1,162 @@
package dailyTaskCfg
import (
"server/game/mod/item"
"server/gamedata"
"strconv"
"strings"
"gitea.bywaystudios.com/pet_home/leaf/log"
)
const (
DAILY_TASK_CONST = "DailyTaskConst"
DAILY_TASK = "DailyTask"
DAILY_TASK_ACTIVE = "DailyTaskActive"
DAILY_TASK_JACKPOT = "DailyTaskJackpot"
DAILY_TASK_REWARD = "DailyTaskReward"
)
func init() {
gamedata.InitCfg(DAILY_TASK_CONST)
gamedata.InitCfg(DAILY_TASK)
gamedata.InitCfg(DAILY_TASK_ACTIVE)
gamedata.InitCfg(DAILY_TASK_JACKPOT)
gamedata.InitCfg(DAILY_TASK_REWARD)
}
func GetDailyTaskReward(Id, activityId int) []*item.Item {
data, err := gamedata.GetData(DAILY_TASK_REWARD)
if err != nil {
log.Debug("GetDailyTaskReward err:%v", err)
return nil
}
for _, v := range data {
sortId := gamedata.GetIntValue(v, "SortId")
rewardGroupType := gamedata.GetIntValue(v, "RewardGroupType")
if sortId == Id && rewardGroupType == activityId {
items := gamedata.GetItemList(v, "ItemID")
petCoinBase := gamedata.GetIntValue(v, "PetCoinBase")
if petCoinBase > 0 {
items = append(items, &item.Item{
Id: item.ITEM_STAR_ID,
Num: petCoinBase,
})
}
return items
}
}
return nil
}
func GetDailyTaskScore(Id, activityId int) int {
data, err := gamedata.GetData(DAILY_TASK_REWARD)
if err != nil {
log.Debug("GetDailyTaskScore err:%v", err)
return 0
}
for _, v := range data {
sortId := gamedata.GetIntValue(v, "SortId")
rewardGroupType := gamedata.GetIntValue(v, "RewardGroupType")
if sortId == Id && rewardGroupType == activityId {
return gamedata.GetIntValue(v, "ScoreReward")
}
}
return 0
}
func GetActivityIds() []int {
data, err := gamedata.GetDataByKey(DAILY_TASK_CONST, "RewardGroupType_const")
if err != nil {
log.Debug("GetActivityIds err:%v", err)
return nil
}
str := gamedata.GetStringValue(data, "Value")
strArr := strings.Split(str, "|")
result := make([]int, 0, len(strArr))
for _, v := range strArr {
a, _ := strconv.Atoi(v)
if a != 0 {
result = append(result, a)
}
}
return result
}
// 获取任务列表
func GetTaskList(Lv int) map[int]string {
data, err := gamedata.GetData(DAILY_TASK)
if err != nil {
log.Debug("GetTaskList err:%v, Lv=%d", err, Lv)
return nil
}
r := make(map[int]string, len(data))
for k, v := range data {
lv := gamedata.GetIntValue(v, "Lv")
if Lv >= lv {
k1, err := strconv.Atoi(k)
if err != nil {
log.Debug("GetTaskList parse id err:%v, key=%s", err, k)
continue
}
r[k1] = gamedata.GetStringValue(v, "Task")
}
}
return r
}
func GetTaskActiveId() []int {
data, err := gamedata.GetData(DAILY_TASK_ACTIVE)
if err != nil {
log.Debug("GetTaskActiveId err:%v", err)
return nil
}
r := make([]int, 0, len(data))
for k := range data {
Id, err := strconv.Atoi(k)
if err != nil {
log.Debug("GetTaskActiveId parse id err:%v, key=%s", err, k)
continue
}
r = append(r, Id)
}
return r
}
func GetTaskActiveById(Id int) int {
data, err := gamedata.GetDataByIntKey(DAILY_TASK_ACTIVE, Id)
if err != nil {
log.Debug("GetTaskActiveById err:%v, Id=%d", err, Id)
return 0
}
return gamedata.GetIntValue(data, "Num")
}
type Jackpot struct {
Items []*item.Item
Is_card int
}
func GetTaskWeekJackpot() map[int]Jackpot {
data, err := gamedata.GetData(DAILY_TASK_JACKPOT)
if err != nil {
log.Debug("GetTaskWeekJackpot err:%v", err)
return nil
}
r := make(map[int]Jackpot, len(data))
for k, v := range data {
Id, err := strconv.Atoi(k)
if err != nil {
log.Debug("GetTaskWeekJackpot parse id err:%v, key=%s", err, k)
continue
}
isCard := gamedata.GetIntValue(v, "CardPack")
Item := item.ParseItem(gamedata.GetValue(v, "Items"))
r[Id] = Jackpot{
Items: Item,
Is_card: isCard,
}
}
return r
}

View File

@ -1,95 +0,0 @@
package decorateCfg
import (
"server/game/mod/item"
"server/gamedata"
"server/pkg/github.com/name5566/leaf/log"
"strconv"
)
const (
DECORATE_COST = "DecorateCost"
INDOOR_PROGRESS = "IndoorProgress"
)
func init() {
gamedata.InitCfg("DecorateCost")
gamedata.InitCfg("IndoorProgress")
}
func GetStarCost(AreaId, SortId int) int {
data, err := gamedata.GetData(DECORATE_COST)
if err != nil {
log.Debug("DecorateCost GetStarCost not found")
return 0
}
for _, v := range data {
v1 := v.(map[string]interface{})
if gamedata.ParseInt(v1["AreaId"]) == AreaId && gamedata.ParseInt(v1["SortId"]) == SortId {
return gamedata.ParseInt(v1["CostCount"])
}
}
return 0
}
func GetAreaId(id int) int {
value, err := gamedata.GetDataByIntKey("DecorateCost", id)
if err != nil {
log.Debug("DecorateCost GetAreaId id:%v not found", id)
return 0
}
return gamedata.ParseInt(value["AreaId"])
}
func GetNextAreaId(AreaId, SortId int) int {
data, err := gamedata.GetData(DECORATE_COST)
if err != nil {
log.Debug("DecorateCost GetStarCost not found")
return 0
}
for k, v := range data {
v1 := v.(map[string]interface{})
if gamedata.ParseInt(v1["AreaId"]) == AreaId && gamedata.ParseInt(v1["SortId"]) == SortId {
UnionKey, _ := strconv.Atoi(k)
nextId := UnionKey + 1
AreaId = GetAreaId(nextId)
return AreaId
}
}
return 0
}
func GetAllSortIdByAreaId(AreaId int) []int {
data, err := gamedata.GetData(DECORATE_COST)
if err != nil {
log.Debug("DecorateCost GetAllSortIdByAreaId not found")
return nil
}
var SortIdList []int
for _, v := range data {
v1 := v.(map[string]interface{})
if gamedata.ParseInt(v1["AreaId"]) == AreaId {
Sort := gamedata.ParseInt(v1["SortId"])
if Sort == 0 {
continue
}
SortIdList = append(SortIdList, Sort)
}
}
return SortIdList
}
func GetProgressReward(Scene, Lv int) []*item.Item {
value, err := gamedata.GetData("IndoorProgress")
if err != nil {
log.Debug("IndoorProgress GetProgressReward not found")
return nil
}
for _, v := range value {
v1 := v.(map[string]interface{})
if gamedata.ParseInt(v1["Scene"]) == Scene && gamedata.ParseInt(v1["Lv"]) == Lv {
return item.ParseItem(v1["Item"])
}
}
return nil
}

View File

@ -0,0 +1,256 @@
package decorateCfg
import (
"server/game/mod/item"
"server/gamedata"
"strconv"
"strings"
"gitea.bywaystudios.com/pet_home/leaf/log"
)
const (
DECORATE_COST = "DecorateCost"
INDOOR_PROGRESS = "IndoorProgress"
DECORATE_CONST = "DecorateConst"
DECORATE_AREA = "DecorateArea" // 装饰区域
)
func init() {
gamedata.InitCfg(DECORATE_COST)
gamedata.InitCfg(INDOOR_PROGRESS)
gamedata.InitCfg(DECORATE_CONST)
gamedata.InitCfg(DECORATE_AREA)
}
func GetStarCost(AreaId, SortId int) int {
data, err := gamedata.GetData(DECORATE_COST)
if err != nil {
log.Debug("GetStarCost err:%v, AreaId=%d, SortId=%d", err, AreaId, SortId)
return 0
}
for _, v := range data {
if gamedata.GetIntValue(v, "AreaId") == AreaId && gamedata.GetIntValue(v, "SortId") == SortId {
return gamedata.GetIntValue(v, "CostCount")
}
}
return 0
}
func GetStarCostByAreaId(AreaId int) int {
data, err := gamedata.GetData(DECORATE_COST)
if err != nil {
log.Debug("GetStarCost err:%v, AreaId=%d", err, AreaId)
return 0
}
var total int
for _, v := range data {
if gamedata.GetIntValue(v, "AreaId") == AreaId {
total += gamedata.GetIntValue(v, "CostCount")
}
}
return total
}
func GetAreaId(id int) int {
value, err := gamedata.GetDataByIntKey("DecorateCost", id)
if err != nil {
log.Debug("GetAreaId err:%v, id=%d", err, id)
return 0
}
return gamedata.GetIntValue(value, "AreaId")
}
func GetNextAreaId(AreaId, SortId int) int {
data, err := gamedata.GetData(DECORATE_COST)
if err != nil {
log.Debug("GetNextAreaId err:%v, AreaId=%d, SortId=%d", err, AreaId, SortId)
return 0
}
for k, v := range data {
if gamedata.GetIntValue(v, "AreaId") == AreaId && gamedata.GetIntValue(v, "SortId") == SortId {
UnionKey, err := strconv.Atoi(k)
if err != nil {
log.Debug("GetNextAreaId parse key err:%v, key=%s", err, k)
return 0
}
nextId := UnionKey + 1
AreaId = GetAreaId(nextId)
return AreaId
}
}
return 0
}
func GetAllSortIdByAreaId(AreaId int) []int {
data, err := gamedata.GetData(DECORATE_COST)
if err != nil {
log.Debug("GetAllSortIdByAreaId err:%v, AreaId=%d", err, AreaId)
return nil
}
SortIdList := make([]int, 0, len(data)/5)
for _, v := range data {
if gamedata.GetIntValue(v, "AreaId") == AreaId {
Sort := gamedata.GetIntValue(v, "SortId")
if Sort == 0 {
continue
}
SortIdList = append(SortIdList, Sort)
}
}
return SortIdList
}
func GetProgressReward(Scene, Lv int) []*item.Item {
value, err := gamedata.GetData(INDOOR_PROGRESS)
if err != nil {
log.Debug("GetProgressReward err:%v, Scene=%d, Lv=%d", err, Scene, Lv)
return nil
}
for _, v := range value {
if gamedata.GetIntValue(v, "Scene") == Scene && gamedata.GetIntValue(v, "Lv") == Lv {
return gamedata.GetItemList(v, "AreaReward")
}
}
return nil
}
func GetDecoarteReward(Scene int, Lv int) []*item.Item {
value, err := gamedata.GetData(INDOOR_PROGRESS)
if err != nil {
log.Debug("GetDecoarteReward err:%v, Scene=%d, Lv=%d", err, Scene, Lv)
return nil
}
for _, v := range value {
if gamedata.GetIntValue(v, "Scene") == Scene && gamedata.GetIntValue(v, "Lv") == Lv {
return gamedata.GetItemList(v, "Item")
}
}
return nil
}
func GetAreaReward(AreaId int) []*item.Item {
value, err := gamedata.GetDataByIntKey(DECORATE_AREA, AreaId)
if err != nil {
log.Debug("GetAreaReward err:%v, AreaId=%d", err, AreaId)
return nil
}
return gamedata.GetItemList(value, "Items")
}
// 获取一键装饰的解锁等级
func GetOneKeyDecorateUnlockLv() int {
data, err := gamedata.GetDataByKey(DECORATE_CONST, "OneKey")
if err != nil {
log.Debug("GetOneKeyDecorateUnlockLv err:%v", err)
return 0
}
return gamedata.GetIntValue(data, "Value")
}
// 获取每次装饰增加的体力值
func GetDecorateAddEnergy() int {
data, err := gamedata.GetDataByKey(DECORATE_CONST, "AddEnergy")
if err != nil {
log.Debug("GetDecorateAddEnergy err:%v", err)
return 0
}
return gamedata.GetIntValue(data, "Value")
}
func GetAreaPerPExp(AreaId int) int {
data, err := gamedata.GetDataByIntKey(DECORATE_AREA, AreaId)
if err != nil {
log.Debug("GetAreaPerPExp err:%v, AreaId=%d", err, AreaId)
return 0
}
return gamedata.GetIntValue(data, "PerPExp")
}
func GetDecoratePExp(AreaId, SortId int) int {
data, err := gamedata.GetData(DECORATE_COST)
if err != nil {
log.Debug("GetDecoratePExp err:%v, AreaId=%d, SortId=%d", err, AreaId, SortId)
return 0
}
for _, v := range data {
if gamedata.GetIntValue(v, "AreaId") == AreaId && gamedata.GetIntValue(v, "SortId") == SortId {
return gamedata.GetIntValue(v, "PetExp")
}
}
return 0
}
func GetAllPartId() []int {
data, err := gamedata.GetDataByKey(DECORATE_CONST, "AllPartId")
if err != nil {
log.Debug("GetAllPartId err:%v", err)
return nil
}
str := gamedata.GetStringValue(data, "Value")
r1 := strings.Split(str, "|")
PartIdList := make([]int, 0, len(r1))
for _, v := range r1 {
id, err := strconv.Atoi(v)
if err != nil {
log.Debug("GetAllPartId parse id err:%v, v=%s", err, v)
continue
}
PartIdList = append(PartIdList, id)
}
return PartIdList
}
func GetIdBySenceAndLv(Sence, Lv int) int {
data, err := gamedata.GetData(INDOOR_PROGRESS)
if err != nil {
log.Debug("GetIdBySenceAndLv err:%v, Sence=%d, Lv=%d", err, Sence, Lv)
return 0
}
for k, v := range data {
if gamedata.GetIntValue(v, "Scene") == Sence && gamedata.GetIntValue(v, "Lv") == Lv {
id, err := strconv.Atoi(k)
if err != nil {
log.Debug("GetIdBySenceAndLv parse id err:%v, key=%s", err, k)
return 0
}
return id
}
}
return 0
}
func GetPartNumByAreaId(AreaId int) map[int]int {
data, err := gamedata.GetData(INDOOR_PROGRESS)
if err != nil {
log.Debug("GetPartNumByAreaId err:%v, AreaId=%d", err, AreaId)
return nil
}
res := make(map[int]int, len(data))
for k, v := range data {
Sence := gamedata.GetIntValue(v, "Scene")
if Sence != AreaId {
continue
}
id, err := strconv.Atoi(k)
if err != nil {
log.Debug("GetPartNumByAreaId parse id err:%v, key=%s", err, k)
continue
}
PartNum := gamedata.GetIntValue(v, "Part")
if PartNum == 0 {
continue
}
res[id] = PartNum
}
return res
}
func GetAreaIdByIndoorId(IndoorId int) int {
data, err := gamedata.GetDataByIntKey(INDOOR_PROGRESS, IndoorId)
if err != nil {
log.Debug("GetAreaIdByIndoorId err:%v, IndoorId=%d", err, IndoorId)
return 0
}
return gamedata.GetIntValue(data, "Scene")
}

View File

@ -0,0 +1,54 @@
package emojiCfg
import (
GoUtil "server/game_util"
"server/gamedata"
"gitea.bywaystudios.com/pet_home/leaf/log"
)
const (
CFG_EMOJI = "Emoji"
)
func init() {
gamedata.InitCfg(CFG_EMOJI)
}
func GetInitList() []int {
data, err := gamedata.GetData(CFG_EMOJI)
if err != nil {
log.Debug("GetInitList err:%v", err)
return nil
}
ret := make([]int, 0, len(data))
for k, v := range data {
Init := gamedata.GetIntValue(v, "Init")
if Init == 1 {
ret = append(ret, GoUtil.Int(k))
}
}
return ret
}
func GetAllList() []int {
data, err := gamedata.GetData(CFG_EMOJI)
if err != nil {
log.Debug("GetAllList err:%v", err)
return nil
}
ret := make([]int, 0, len(data))
for k := range data {
ret = append(ret, GoUtil.Int(k))
}
return ret
}
func GetEmojiName(Id int) string {
data, err := gamedata.GetDataByIntKey(CFG_EMOJI, Id)
if err != nil {
log.Debug("GetEmojiName err:%v, Id=%d", err, Id)
return ""
}
return gamedata.GetStringValue(data, "Name")
}

View File

@ -3,6 +3,8 @@ package endlessCfg
import (
"server/game/mod/item"
"server/gamedata"
"gitea.bywaystudios.com/pet_home/leaf/log"
)
const (
@ -15,14 +17,15 @@ func init() {
gamedata.InitCfg(CFG_ENDLESS_JACKPOT)
}
func GetDiamond(Money float64) float64 {
func GetEnergy(Money float64) float64 {
data, err := gamedata.GetData(CFG_ENDLESS)
if err != nil {
log.Debug("GetEnergy err:%v, Money=%.2f", err, Money)
return 0
}
for _, v := range data {
if Money == gamedata.GetFloatValue(v, "Money") {
return gamedata.GetFloatValue(v, "Diamond")
return gamedata.GetFloatValue(v, "Energy")
}
}
return 0
@ -31,6 +34,7 @@ func GetDiamond(Money float64) float64 {
func NextMoney(Money float64) float64 {
data, err := gamedata.GetData(CFG_ENDLESS)
if err != nil {
log.Debug("NextMoney err:%v, Money=%.2f", err, Money)
return 0
}
for _, v := range data {
@ -44,6 +48,7 @@ func NextMoney(Money float64) float64 {
func GetChargeId(Money float64) int {
data, err := gamedata.GetData(CFG_ENDLESS)
if err != nil {
log.Debug("GetChargeId err:%v, Money=%.2f", err, Money)
return 0
}
for _, v := range data {
@ -54,27 +59,29 @@ func GetChargeId(Money float64) int {
return 0
}
func GetPerDiamond(Id int) float64 {
func GetPerEnergy(Id int) float64 {
data, err := gamedata.GetData(CFG_ENDLESS_JACKPOT)
if err != nil {
log.Debug("GetPerEnergy err:%v, Id=%d", err, Id)
return 0
}
for _, v := range data {
if Id == gamedata.GetIntValue(v, "ItemId") {
return gamedata.GetFloatValue(v, "Diamond")
return gamedata.GetFloatValue(v, "Energy")
}
}
return 0
}
func GetItemId(Diamond float64, Type int) []int {
func GetItemId(Energy float64, Type, Lv int) []int {
data, err := gamedata.GetData(CFG_ENDLESS_JACKPOT)
if err != nil {
log.Debug("GetItemId err:%v, Energy=%.2f, Type=%d, Lv=%d", err, Energy, Type, Lv)
return nil
}
var ItemId []int
ItemId := make([]int, 0, len(data))
for _, v := range data {
if Diamond >= gamedata.GetFloatValue(v, "Diamond") && Type != gamedata.GetIntValue(v, "Type") && gamedata.GetIntValue(v, "ItemId") != item.ITEM_ENERGY_ID {
if Lv >= gamedata.GetIntValue(v, "Lv") && Energy >= gamedata.GetFloatValue(v, "Energy") && Type != gamedata.GetIntValue(v, "Type") && gamedata.GetIntValue(v, "ItemId") != item.ITEM_ENERGY_ID {
ItemId = append(ItemId, gamedata.GetIntValue(v, "ItemId"))
}
}
@ -84,6 +91,7 @@ func GetItemId(Diamond float64, Type int) []int {
func GetType(ItemId int) int {
data, err := gamedata.GetData(CFG_ENDLESS_JACKPOT)
if err != nil {
log.Debug("GetType err:%v, ItemId=%d", err, ItemId)
return 0
}
for _, v := range data {
@ -94,14 +102,15 @@ func GetType(ItemId int) int {
return 0
}
func GeDiamondByItemId(ItemId int) float64 {
func GetEnergyByItemId(ItemId int) float64 {
data, err := gamedata.GetData(CFG_ENDLESS_JACKPOT)
if err != nil {
log.Debug("GetEnergyByItemId err:%v, ItemId=%d", err, ItemId)
return 0
}
for _, v := range data {
if ItemId == gamedata.GetIntValue(v, "ItemId") {
return gamedata.GetFloatValue(v, "Diamond")
return gamedata.GetFloatValue(v, "Energy")
}
}
return 0

View File

@ -1,42 +0,0 @@
package faceCfg
import (
"server/gamedata"
"server/pkg/github.com/name5566/leaf/log"
"strconv"
)
const (
CFG_FACE = "Face"
)
func init() {
gamedata.InitCfg(CFG_FACE)
}
func GetInitList() []int {
data, err := gamedata.GetData(CFG_FACE)
if err != nil {
log.Debug("GetInitList err: %v", err)
return nil
}
r := make([]int, 0)
for k, v := range data {
IsInit := gamedata.GetIntValue(v, "Init")
if IsInit == 1 {
k, _ := strconv.Atoi(k)
r = append(r, k)
}
}
return r
}
func CheckExist(Id int) bool {
data, err := gamedata.GetData(CFG_FACE)
if err != nil {
log.Debug("CheckExist err: %v", err)
return false
}
_, ok := data[strconv.Itoa(Id)]
return ok
}

View File

@ -0,0 +1,75 @@
package faceCfg
import (
"math/rand"
"server/gamedata"
"strconv"
"gitea.bywaystudios.com/pet_home/leaf/log"
)
const (
CFG_FACE = "Face"
)
func init() {
gamedata.InitCfg(CFG_FACE)
}
func GetInitList() []int {
data, err := gamedata.GetData(CFG_FACE)
if err != nil {
log.Debug("GetInitList err:%v", err)
return nil
}
r := make([]int, 0, len(data))
for k, v := range data {
IsInit := gamedata.GetIntValue(v, "Init")
if IsInit == 1 {
id, err := strconv.Atoi(k)
if err != nil {
log.Debug("GetInitList parse id err:%v, key=%s", err, k)
continue
}
r = append(r, id)
}
}
return r
}
func GetRandInitId() int {
list := GetInitList()
if len(list) == 0 {
return 0
}
index := rand.Intn(len(list))
return list[index]
}
func CheckExist(Id int) bool {
data, err := gamedata.GetData(CFG_FACE)
if err != nil {
log.Debug("CheckExist err:%v, Id=%d", err, Id)
return false
}
_, ok := data[strconv.Itoa(Id)]
return ok
}
func GetAllId() []int {
data, err := gamedata.GetData(CFG_FACE)
if err != nil {
log.Debug("GetAllId err:%v", err)
return nil
}
r := make([]int, 0, len(data))
for k := range data {
Id, err := strconv.Atoi(k)
if err != nil {
log.Debug("GetAllId parse id err:%v, key=%s", err, k)
continue
}
r = append(r, Id)
}
return r
}

View File

@ -0,0 +1,71 @@
package friendCfg
import (
"server/gamedata"
"gitea.bywaystudios.com/pet_home/leaf/log"
)
const (
CFG_NPC_FRIENDS = "NPCFriends"
CFG_CONST = "FriendConst"
)
func init() {
gamedata.InitCfg(CFG_NPC_FRIENDS)
gamedata.InitCfg(CFG_CONST)
}
func IsNpcFriend(Id int) bool {
_, err := gamedata.GetDataByIntKey(CFG_NPC_FRIENDS, Id)
if err != nil {
log.Debug("IsNpcFriend err:%v, Id=%d", err, Id)
return false
}
return true
}
func GetFriendSponsorNum() int {
data, err := gamedata.GetDataByKey(CFG_CONST, "friend_energy_bonus")
if err != nil {
log.Debug("GetFriendSponsorNum err:%v", err)
return 0
}
return gamedata.GetIntValue(data, "Value")
}
func GetFriendLimitNum() int {
data, err := gamedata.GetDataByKey(CFG_CONST, "friend_limit")
if err != nil {
log.Debug("GetFriendLimitNum err:%v", err)
return 0
}
return gamedata.GetIntValue(data, "Value")
}
func GetDailyRecommendLimit() int {
data, err := gamedata.GetDataByKey(CFG_CONST, "friend_daily_recommend_limit")
if err != nil {
log.Debug("GetDailyRecommendLimit err:%v", err)
return 0
}
return gamedata.GetIntValue(data, "Value")
}
func GetApplyListLimit() int {
data, err := gamedata.GetDataByKey(CFG_CONST, "friend_apply_list_limit")
if err != nil {
log.Debug("GetApplyListLimit err:%v", err)
return 0
}
return gamedata.GetIntValue(data, "Value")
}
func GetDailyGetApplyLimit() int {
data, err := gamedata.GetDataByKey(CFG_CONST, "friend_daily_get_apply_limit")
if err != nil {
log.Debug("GetDailyGetApplyLimit err:%v", err)
return 0
}
return gamedata.GetIntValue(data, "Value")
}

View File

@ -0,0 +1,87 @@
package friendTreasureCfg
import (
"server/game/mod/item"
GoUtil "server/game_util"
"server/gamedata"
)
const (
CFG_FRIEND_TREASURE_PROB = "FriendTreasureProb"
CFG_FRIEND_TREASURE_CHEST = "FriendTreasureChest"
CFG_FRIEND_TREASURE_CONST = "FriendTreasureConst"
)
func init() {
gamedata.InitCfg(CFG_FRIEND_TREASURE_PROB)
gamedata.InitCfg(CFG_FRIEND_TREASURE_CHEST)
gamedata.InitCfg(CFG_FRIEND_TREASURE_CONST)
}
func GetBigReward() []*item.Item {
data, err := gamedata.GetDataByKey(CFG_FRIEND_TREASURE_CONST, "big_reward")
if err != nil {
return nil
}
return gamedata.GetItemList(data, "Value")
}
func GetFriendTreasureProb(Num int) map[int]int {
data, err := gamedata.GetData(CFG_FRIEND_TREASURE_PROB)
if err != nil {
return nil
}
ProbMap := make(map[int]int)
for k, v := range data {
Id := GoUtil.Int(k)
Prob := gamedata.GetIntValue(v, "Prob")
ProbMap[Id] = Prob
}
AddProb := 0
n := 0
for k, v := range ProbMap {
if k > Num {
AddProb += v
n++
ProbMap[k] = 0
}
}
PerProb := AddProb / n
for k, v := range ProbMap {
if k <= Num && k != 1 {
ProbMap[k] = v + PerProb
}
}
return ProbMap
}
func GetProbAdd(Id int) int {
data, err := gamedata.GetDataByIntKey(CFG_FRIEND_TREASURE_PROB, Id)
if err != nil {
return 0
}
return gamedata.GetIntValue(data, "Add")
}
func GetChestProb() map[int]int {
data, err := gamedata.GetData(CFG_FRIEND_TREASURE_CHEST)
if err != nil {
return nil
}
ProbMap := make(map[int]int)
for k, v := range data {
Id := GoUtil.Int(k)
Prob := gamedata.GetIntValue(v, "Prob")
ProbMap[Id] = Prob
}
return ProbMap
}
func GetChestItems(Id int) []*item.Item {
data, err := gamedata.GetDataByIntKey(CFG_FRIEND_TREASURE_CHEST, Id)
if err != nil {
return nil
}
return gamedata.GetItemList(data, "Items")
}

View File

@ -0,0 +1,73 @@
package fur_cfg
import (
"server/game/mod/item"
"server/gamedata"
)
const (
CFG_FUR_CONST = "FurShopConst"
CFG_FUR_SHOP = "FurShop"
)
func init() {
gamedata.InitCfg(CFG_FUR_CONST)
gamedata.InitCfg(CFG_FUR_SHOP)
}
func GetFurShopCost(id int) []*item.Item {
data, err := gamedata.GetDataByIntKey(CFG_FUR_SHOP, id)
if err != nil {
return nil
}
return gamedata.GetItemList(data, "Cost")
}
func GetFurShopFreeTimes() int {
data, err := gamedata.GetDataByKey(CFG_FUR_CONST, "free_unlock")
if err != nil {
return 0
}
return gamedata.GetIntValue(data, "Value")
}
func GetFurShopTag(id int) int {
data, err := gamedata.GetDataByIntKey(CFG_FUR_SHOP, id)
if err != nil {
return 0
}
return gamedata.GetIntValue(data, "TAG")
}
func GetFurShopName(id int) string {
data, err := gamedata.GetDataByIntKey(CFG_FUR_SHOP, id)
if err != nil {
return ""
}
return gamedata.GetStringValue(data, "Name")
}
func GetFurShopItem(id int) []*item.Item {
data, err := gamedata.GetDataByIntKey(CFG_FUR_SHOP, id)
if err != nil {
return nil
}
itemId := gamedata.GetIntValue(data, "ItemID")
if itemId == 0 {
return nil
}
return []*item.Item{
{
Id: itemId,
Num: 1,
},
}
}
func GetFurShopCostNum(id int) int {
item := GetFurShopCost(id)
if len(item) == 0 {
return 0
}
return item[0].Num
}

View File

@ -0,0 +1,119 @@
package guesscolorCfg
import (
"server/game/mod/item"
GoUtil "server/game_util"
"server/gamedata"
"strings"
"gitea.bywaystudios.com/pet_home/leaf/log"
)
const (
CFG_GUESS_COLOR_TEMPLATE = "GuessColorTemplate"
CFG_GUESS_COLOR_REWARD = "GuessColorReward"
)
func init() {
gamedata.InitCfg(CFG_GUESS_COLOR_TEMPLATE)
gamedata.InitCfg(CFG_GUESS_COLOR_REWARD)
}
func GetFirstPass(Id int) map[int]int {
data, err := gamedata.GetDataByIntKey(CFG_GUESS_COLOR_TEMPLATE, Id)
if err != nil {
log.Debug("GetFirstPass err:%v, Id=%d", err, Id)
return nil
}
str := gamedata.GetStringValue(data, "FirstPass")
arr := strings.Split(str, "|")
ret := make(map[int]int, len(arr))
for k, v := range arr {
ret[k+1] = GoUtil.Int(v)
}
return ret
}
func GetLoseItem(Id int) []*item.Item {
data, err := gamedata.GetDataByIntKey(CFG_GUESS_COLOR_TEMPLATE, Id)
if err != nil {
log.Debug("GetLoseItem err:%v, Id=%d", err, Id)
return nil
}
return gamedata.GetItemList(data, "ItemCost")
}
func GetFirstItem(Id int) []*item.Item {
data, err := gamedata.GetDataByIntKey(CFG_GUESS_COLOR_TEMPLATE, Id)
if err != nil {
log.Debug("GetFirstItem err:%v, Id=%d", err, Id)
return nil
}
return gamedata.GetItemList(data, "FirstItem")
}
func GetActivityItemId(Id int) int {
data, err := gamedata.GetDataByIntKey(CFG_GUESS_COLOR_TEMPLATE, Id)
if err != nil {
log.Debug("GetActivityItemId err:%v, Id=%d", err, Id)
return 0
}
return gamedata.GetIntValue(data, "ItemId")
}
func GetRewardItem(Id int, Type int) []*item.Item {
data, err := gamedata.GetData(CFG_GUESS_COLOR_REWARD)
if err != nil {
log.Debug("GetRewardItem err:%v, Id=%d, Type=%d", err, Id, Type)
return nil
}
for k, v := range data {
min := GoUtil.Int(k)
max := gamedata.GetIntValue(v, "Max")
if Id >= min && Id <= max {
if Type == 1 {
return gamedata.GetItemList(v, "Items1")
}
return gamedata.GetItemList(v, "Items2")
}
}
return nil
}
func GetPassNum(Id int) int {
data, err := gamedata.GetData(CFG_GUESS_COLOR_REWARD)
if err != nil {
log.Debug("GetPassNum err:%v, Id=%d", err, Id)
return 0
}
for k, v := range data {
min := GoUtil.Int(k)
max := gamedata.GetIntValue(v, "Max")
if Id >= min && Id <= max {
return gamedata.GetIntValue(v, "Num")
}
}
return 0
}
func GetWinTime(Id int) int {
data, err := gamedata.GetData(CFG_GUESS_COLOR_REWARD)
if err != nil {
log.Debug("GetWinTime err:%v, Id=%d", err, Id)
return 0
}
for k, v := range data {
min := GoUtil.Int(k)
max := gamedata.GetIntValue(v, "Max")
if Id >= min && Id <= max {
Type := gamedata.GetIntValue(v, "Type")
if Type == 1 {
return gamedata.GetIntValue(v, "Win")
}
str := gamedata.GetStringValue(v, "Win")
arr := strings.Split(str, "|")
return GoUtil.RandNum(GoUtil.Int(arr[0]), GoUtil.Int(arr[1]))
}
}
return 0
}

View File

@ -0,0 +1,25 @@
package guidecfg
import (
"server/game/mod/item"
"server/gamedata"
"gitea.bywaystudios.com/pet_home/leaf/log"
)
const (
GUIDE_REWARD = "GuideReward"
)
func init() {
gamedata.InitCfg(GUIDE_REWARD)
}
func GetGuideReward(Id int) ([]*item.Item, int) {
data, err := gamedata.GetDataByIntKey(GUIDE_REWARD, Id)
if err != nil {
log.Debug("GetGuideReward err:%v, Id=%d", err, Id)
return nil, 0
}
return item.ParseItem(gamedata.GetValue(data, "Items")), gamedata.GetIntValue(data, "Num")
}

View File

@ -0,0 +1,119 @@
package GuideTaskCfg
import (
"server/game/mod/item"
GoUtil "server/game_util"
"server/gamedata"
"strconv"
"gitea.bywaystudios.com/pet_home/leaf/log"
)
const (
GUIDE_TASK_CONST = "GuideTaskConst"
GUIDE_TASK_TASK = "GuideTaskTask"
GUIDE_TASK_ACTIVE = "GuideTaskActive"
)
func init() {
gamedata.InitCfg(GUIDE_TASK_CONST)
gamedata.InitCfg(GUIDE_TASK_TASK)
gamedata.InitCfg(GUIDE_TASK_ACTIVE)
}
func GetTaskRewardById(Id int) []*item.Item {
data, err := gamedata.GetDataByIntKey(GUIDE_TASK_TASK, Id)
if err != nil {
log.Debug("GetTaskRewardById err:%v, Id=%d", err, Id)
return nil
}
return gamedata.GetItemList(data, "Items")
}
func GetTaskById(Id int) string {
data, err := gamedata.GetDataByIntKey(GUIDE_TASK_TASK, Id)
if err != nil {
return ""
}
return gamedata.GetStringValue(data, "Task")
}
func GetTaskActive(Id int) int {
data, err := gamedata.GetDataByIntKey(GUIDE_TASK_TASK, Id)
if err != nil {
log.Debug("GetTaskActive err:%v, Id=%d", err, Id)
return 0
}
return gamedata.GetIntValue(data, "Active")
}
func GetUnlock(Id int) int {
data, err := gamedata.GetDataByIntKey(GUIDE_TASK_TASK, Id)
if err != nil {
log.Debug("GetUnlock err:%v, Id=%d", err, Id)
return 0
}
return gamedata.GetIntValue(data, "Unlock")
}
func GetActiveReward(Id int) ([]*item.Item, int) {
data, err := gamedata.GetDataByIntKey(GUIDE_TASK_ACTIVE, Id)
if err != nil {
log.Debug("GetActiveReward err:%v, Id=%d", err, Id)
return nil, 0
}
return gamedata.GetItemList(data, "Items"), gamedata.GetIntValue(data, "Active")
}
func GetActiveReward2(reward []int, Active int) []*item.Item {
data, err := gamedata.GetData(GUIDE_TASK_ACTIVE)
if err != nil {
log.Debug("GetActiveReward2 err:%v, Active=%d", err, Active)
return nil
}
for k, v := range data {
active := gamedata.GetIntValue(v, "Active")
if active < Active {
continue
}
if GoUtil.InArray(GoUtil.Int(k), reward) {
continue
}
return gamedata.GetItemList(v, "Items")
}
return nil
}
func GetUnlockLv() int {
data, err := gamedata.GetDataByKey(GUIDE_TASK_CONST, "UnlockLv")
if err != nil {
log.Debug("GetUnlockLv err:%v", err)
return 0
}
return gamedata.GetIntValue(data, "Value")
}
func GetDays() int {
data, err := gamedata.GetDataByKey(GUIDE_TASK_CONST, "Days")
if err != nil {
log.Debug("GetDays err:%v", err)
return 0
}
return gamedata.GetIntValue(data, "Value")
}
func GetTaskList(Lv int) map[int]string {
data, err := gamedata.GetData(GUIDE_TASK_TASK)
if err != nil {
log.Debug("GetTaskList err:%v, Lv=%d", err, Lv)
return nil
}
r := make(map[int]string, len(data))
for k, v := range data {
k1, err := strconv.Atoi(k)
if err != nil {
log.Debug("GetTaskList parse id err:%v, key=%s", err, k)
continue
}
r[k1] = gamedata.GetStringValue(v, "Task")
}
return r
}

View File

@ -1,24 +0,0 @@
package guildCfg
import (
"server/game/mod/item"
"server/gamedata"
"server/pkg/github.com/name5566/leaf/log"
)
const (
GUILD_REWARD = "GuildReward"
)
func init() {
gamedata.InitCfg(GUILD_REWARD)
}
func GetGuildReward(Id int) ([]*item.Item, int) {
data, err := gamedata.GetDataByIntKey(GUILD_REWARD, Id)
if err != nil {
log.Debug("GuildCfg GetGuildReward Id:%v not found", Id)
return nil, 0
}
return item.ParseItem(gamedata.GetValue(data, "Items")), gamedata.GetIntValue(data, "Num")
}

View File

@ -0,0 +1,36 @@
package handbookCfg
import (
"server/game/mod/item"
"server/gamedata"
"gitea.bywaystudios.com/pet_home/leaf/log"
)
const (
CFG_HANDBOOK = "Handbook"
CFG_HANDBOOK_CONST = "HandbookConst"
)
func init() {
gamedata.InitCfg(CFG_HANDBOOK)
gamedata.InitCfg(CFG_HANDBOOK_CONST)
}
func GetHandbookReward(Type string) []*item.Item {
data, err := gamedata.GetDataByKey(CFG_HANDBOOK, Type)
if err != nil {
log.Debug("GetHandbookReward err:%v, Type=%s", err, Type)
return nil
}
return gamedata.GetItemList(data, "Items")
}
func GetHandbookCollectReward() []*item.Item {
data, err := gamedata.GetDataByKey(CFG_HANDBOOK_CONST, "collect_item")
if err != nil {
log.Debug("GetHandbookCollectReward err:%v", err)
return nil
}
return gamedata.GetItemList(data, "Value")
}

View File

@ -0,0 +1,25 @@
package inviteCfg
import (
"server/game/mod/item"
"server/gamedata"
"gitea.bywaystudios.com/pet_home/leaf/log"
)
const (
CFG_INVITE = "Invite"
)
func init() {
gamedata.InitCfg(CFG_INVITE)
}
func GetInviteReward(index int) (int, []*item.Item) {
data, err := gamedata.GetDataByIntKey(CFG_INVITE, index)
if err != nil {
log.Debug("GetInviteReward err:%v, index=%d", err, index)
return 0, nil
}
return gamedata.GetIntValue(data, "Need"), gamedata.GetItemList(data, "Items")
}

View File

@ -1,48 +0,0 @@
package itemCfg
import (
"server/gamedata"
"server/pkg/github.com/name5566/leaf/log"
"strconv"
"strings"
)
var CFG_NAME = "Item"
func init() {
gamedata.InitCfg(CFG_NAME)
}
func GetItemType(Id int) int {
data, err := gamedata.GetDataByIntKey(CFG_NAME, Id)
if err != nil {
log.Debug("ItemCfg GetItemType Id:%v not found", Id)
return 0
}
return gamedata.ParseInt(data["IType"])
}
func GetItemEffect(Id int) int {
data, err := gamedata.GetDataByIntKey(CFG_NAME, Id)
if err != nil {
log.Debug("ItemCfg GetItemEffect Id:%v not found", Id)
return 0
}
return gamedata.ParseInt(data["Effect"])
}
func GetItemEffectList(Id int) []int {
data, err := gamedata.GetDataByIntKey(CFG_NAME, Id)
if err != nil {
log.Debug("ItemCfg GetItemEffect Id:%v not found", Id)
return []int{}
}
var res []int
str := gamedata.GetStringValue(data, "Effect")
a := strings.Split(str, ",")
for _, v := range a {
b, _ := strconv.Atoi(v)
res = append(res, b)
}
return res
}

View File

@ -0,0 +1,65 @@
package itemCfg
import (
languageCfg "server/conf/language"
"server/gamedata"
"server/msg/meowmentnet"
"strconv"
"strings"
"gitea.bywaystudios.com/pet_home/leaf/log"
)
var CFG_NAME = "Item"
func init() {
gamedata.InitCfg(CFG_NAME)
}
func GetItemType(Id int) int {
data, err := gamedata.GetDataByIntKey(CFG_NAME, Id)
if err != nil {
log.Debug("GetItemType err:%v, Id=%d", err, Id)
return 0
}
return gamedata.GetIntValue(data, "IType")
}
func GetItemEffect(Id int) int {
data, err := gamedata.GetDataByIntKey(CFG_NAME, Id)
if err != nil {
log.Debug("GetItemEffect err:%v, Id=%d", err, Id)
return 0
}
return gamedata.GetIntValue(data, "Effect")
}
func GetItemEffectList(Id int) []int {
data, err := gamedata.GetDataByIntKey(CFG_NAME, Id)
if err != nil {
log.Debug("GetItemEffectList err:%v, Id=%d", err, Id)
return nil
}
str := gamedata.GetStringValue(data, "Effect")
a := strings.Split(str, ",")
res := make([]int, 0, len(a))
for _, v := range a {
b, err := strconv.Atoi(v)
if err != nil {
log.Debug("GetItemEffectList parse err:%v, v=%s", err, v)
continue
}
res = append(res, b)
}
return res
}
func GetItemName(Id int) (string, string) {
data, err := gamedata.GetDataByIntKey(CFG_NAME, Id)
if err != nil {
log.Debug("GetItemName err:%v, Id=%d", err, Id)
return "", ""
}
name := gamedata.GetStringValue(data, "Name")
return languageCfg.GetLanguage(meowmentnet.LANG_TYPE_LangCn, name), languageCfg.GetLanguage(meowmentnet.LANG_TYPE_LangEn, name)
}

View File

@ -5,13 +5,16 @@ import (
"io"
"os"
"server/pkg/github.com/name5566/leaf/log"
"gitea.bywaystudios.com/pet_home/leaf/log"
)
var Server struct {
AppID int
AppPath string
LogLevel string
LogPath string
WSAddr string
RPCAddr string
CertFile string
KeyFile string
TCPAddr string
@ -28,6 +31,12 @@ var Server struct {
RedisAddr string
RedisPort string
RedisPwd string
RedisDb int
RedisWriteAddr string // 主写地址host:port 或 单独 host, 仍兼容旧 RedisAddr/RedisPort
RedisReadAddrs string // 只读地址逗号分隔host:port,...
RedisMasterName string // 哨兵模式下的 master 名称
RedisConnType string // "Direct" 或 "Sentinel"
GameName string
ServerType string
@ -39,13 +48,28 @@ var Server struct {
ListenAddr string
CenterAddr string
CenterNode int
RemoteAddr string
RemoteAddr string
GameConfPath string
TELOGDIR string
GoogleVerify bool
Partition int
KafkaHost string
KafkaPort string
Version string
CountryCode string
IdVerify bool
}
func init() {
// data, err := ioutil.ReadFile("conf/server.json")
file, err := os.Open("conf/server.json")
func InitConfig() {
filePath := "conf/server.json"
if len(os.Args) == 2 {
if os.Args[1] != "" && os.Args[1][0] != '-' {
filePath = os.Args[1]
}
}
file, err := os.Open(filePath)
if err != nil {
panic(err)
}

View File

@ -0,0 +1,64 @@
package languageCfg
import (
"server/gamedata"
msg "server/msg/meowmentnet"
)
const (
CFG_LANGUAGE = "LanguageData"
CFG_ALLLANGUAGE = "AllLanguage"
CFG_ALLLANGUAGE_CLIENT = "AllLanguageClient"
)
func init() {
gamedata.InitCfg(CFG_LANGUAGE)
gamedata.InitCfg(CFG_ALLLANGUAGE)
gamedata.InitCfg(CFG_ALLLANGUAGE_CLIENT)
}
func GetLanguage(lang msg.LANG_TYPE, key string) string {
res := _GetClientLanguage(lang, key)
if res != key {
return res
}
return _GetBackendLanguage(lang, key)
}
func _GetClientLanguage(lang msg.LANG_TYPE, key string) string {
data, err := gamedata.GetDataByKey(CFG_ALLLANGUAGE_CLIENT, key)
if err != nil {
return key
}
switch lang {
case msg.LANG_TYPE_LangEn:
return gamedata.GetStringValue(data, "en_US")
case msg.LANG_TYPE_LangPtbr:
return gamedata.GetStringValue(data, "pt_BR")
case msg.LANG_TYPE_LangCn:
return gamedata.GetStringValue(data, "zh_CN")
case msg.LANG_TYPE_LangEsLatam:
return gamedata.GetStringValue(data, "es_LATAM")
default:
return key
}
}
func _GetBackendLanguage(lang msg.LANG_TYPE, key string) string {
data, err := gamedata.GetDataByKey(CFG_ALLLANGUAGE, key)
if err != nil {
return key
}
switch lang {
case msg.LANG_TYPE_LangEn:
return gamedata.GetStringValue(data, "en_US")
case msg.LANG_TYPE_LangPtbr:
return gamedata.GetStringValue(data, "pt_BR")
case msg.LANG_TYPE_LangCn:
return gamedata.GetStringValue(data, "zh_CN")
case msg.LANG_TYPE_LangEsLatam:
return gamedata.GetStringValue(data, "es_LATAM")
default:
return key
}
}

View File

@ -1,219 +0,0 @@
package limitedTimeEventCfg
import (
"server/GoUtil"
"server/game/mod/item"
"server/gamedata"
"server/pkg/github.com/name5566/leaf/log"
"strconv"
"strings"
)
const (
CFG_LIMITED_TIME_EVENT = "LimitedTimeEvent"
CFG_LIMITED_TIME_EVENT_METEOR = "LimitedTimeEventMeteor"
CFG_LIMITED_TIME_EVENT_CHEST = "LimitedTimeEventChest"
CFG_LIMITED_TIME_EVENT_ORDER = "LimitedTimeEventOrder"
CFG_LIMITED_TIME_EVENT_SENCE = "LimitedTimeEventSence"
CFG_LIMITED_TIME_EVENT_SENCE_JACKPOT = "LimitedTimeEventSenceJackpot"
CFG_LIMITED_TIME_EVENT_FAST = "LimitedTimeEventFast"
CFG_LIMITED_TIME_EVENT_JACKPOT = "LimitedTimeEventJackpot"
CFG_LIMITED_TIME_EVENT_CONST = "LimitedTimeEventConst"
)
func init() {
gamedata.InitCfg(CFG_LIMITED_TIME_EVENT)
gamedata.InitCfg(CFG_LIMITED_TIME_EVENT_METEOR)
gamedata.InitCfg(CFG_LIMITED_TIME_EVENT_CHEST)
gamedata.InitCfg(CFG_LIMITED_TIME_EVENT_ORDER)
gamedata.InitCfg(CFG_LIMITED_TIME_EVENT_SENCE)
gamedata.InitCfg(CFG_LIMITED_TIME_EVENT_SENCE_JACKPOT)
gamedata.InitCfg(CFG_LIMITED_TIME_EVENT_FAST)
gamedata.InitCfg(CFG_LIMITED_TIME_EVENT_JACKPOT)
gamedata.InitCfg(CFG_LIMITED_TIME_EVENT_CONST)
}
// 获取限时事件触发列表
func GetLimitedTimeEventCfg() []*gamedata.LimitedTimeEventData {
data, err := gamedata.GetData(CFG_LIMITED_TIME_EVENT)
if err != nil {
log.Debug("GetLimitedTimeEventCfg err:%v", err)
return nil
}
var res []*gamedata.LimitedTimeEventData
Day, Hour := GoUtil.GetWeekdayAndHour()
for k, v := range data {
weekDay := gamedata.GetIntValue(v, "WeekDay")
startTime := gamedata.GetIntValue(v, "StartTime")
endTime := gamedata.GetIntValue(v, "EndTime")
if weekDay == Day && Hour >= startTime && Hour < endTime {
Id, _ := strconv.Atoi(k)
res = append(res, &gamedata.LimitedTimeEventData{
Id: Id,
EventId: gamedata.GetIntValue(v, "EventId"),
Duration: int64(gamedata.GetIntValue(v, "Duration")),
})
}
}
return res
}
// 获取流星雨加成
func GetMeteorAdd(ChessLv int) int {
data, err := gamedata.GetDataByIntKey(CFG_LIMITED_TIME_EVENT_METEOR, ChessLv)
if err != nil {
log.Debug("GetMeteorAdd err:%v", err)
return 0
}
return gamedata.GetIntValue(data, "Add")
}
// 获取宝箱雨奖励
func GetChestReward(Star int) []*item.Item {
data, err := gamedata.GetData(CFG_LIMITED_TIME_EVENT_CHEST)
if err != nil {
log.Debug("GetChestReward err:%v", err)
return nil
}
for _, v := range data {
Min := gamedata.GetIntValue(v, "Min")
Max := gamedata.GetIntValue(v, "Max")
if Star >= Min && Star <= Max {
return gamedata.GetItemList(v, "Items")
}
}
return nil
}
// 获取超级订单奖励
func GetSuperOrderReward(Star int) []*item.Item {
data, err := gamedata.GetData(CFG_LIMITED_TIME_EVENT_ORDER)
if err != nil {
log.Debug("GetSuperOrderReward err:%v", err)
return nil
}
for _, v := range data {
Min := gamedata.GetIntValue(v, "Min")
Max := gamedata.GetIntValue(v, "Max")
if Star >= Min && Star <= Max {
return gamedata.GetItemList(v, "Items")
}
}
return nil
}
// 获取场景冲刺奖励
func GetSceneDashReward(Sence, Progress int) (int, []*item.Item) {
data, err := gamedata.GetData(CFG_LIMITED_TIME_EVENT_SENCE)
if err != nil {
log.Debug("GetSceneDashReward err:%v", err)
return 0, nil
}
for k, v := range data {
if Sence == gamedata.GetIntValue(v, "Sence") && Progress == gamedata.GetIntValue(v, "Progress") {
return GoUtil.Int(k), gamedata.GetItemList(v, "Items")
}
}
return 0, nil
}
func GetSenceJackpotProb() map[int]int {
r := make(map[int]int)
data, err := gamedata.GetData(CFG_LIMITED_TIME_EVENT_SENCE_JACKPOT)
if err != nil {
log.Debug("GetSenceJackpotReward err:%v", err)
return nil
}
for k, v := range data {
Id := GoUtil.Int(k)
r[Id] = gamedata.GetIntValue(v, "Prob")
}
return r
}
func GetSenceJackpotReward(Id int) []*item.Item {
data, err := gamedata.GetDataByIntKey(CFG_LIMITED_TIME_EVENT_SENCE_JACKPOT, Id)
if err != nil {
log.Debug("GetSenceJackpotReward err:%v", err)
return nil
}
return gamedata.GetItemList(data, "Items")
}
// 获取连击快手奖励
func GetFastProduceReward(Times, Energy int) []*item.Item {
data, err := gamedata.GetData(CFG_LIMITED_TIME_EVENT_FAST)
if err != nil {
log.Debug("GetSceneDashReward err:%v", err)
return nil
}
for _, v := range data {
if Times == gamedata.GetIntValue(v, "Times") && Energy <= gamedata.GetIntValue(v, "Max") && Energy >= gamedata.GetIntValue(v, "Min") {
return gamedata.GetItemList(v, "Items")
}
}
return nil
}
func GetProgressMax() int {
data, err := gamedata.GetDataByKey(CFG_LIMITED_TIME_EVENT_CONST, "Progress_max")
if err != nil {
log.Debug("GetProgressMax err:%v", err)
return 0
}
return gamedata.GetIntValue(data, "value")
}
func GetProgressRewardRand(Lv int) map[int]int {
data, err := gamedata.GetData(CFG_LIMITED_TIME_EVENT_JACKPOT)
if err != nil {
log.Debug("GetProgressRewardRand err:%v", err)
return nil
}
r := make(map[int]int)
for k, v := range data {
if Lv >= gamedata.GetIntValue(v, "Min") && Lv <= gamedata.GetIntValue(v, "Max") {
Id := GoUtil.Int(k)
r[Id] = gamedata.GetIntValue(v, "Prob")
}
}
return r
}
func GetProgressSelectNum(Lv int) int {
data, err := gamedata.GetDataByKey(CFG_LIMITED_TIME_EVENT_CONST, "Progress_lv_num")
if err != nil {
log.Debug("GetProgressSelectNum err:%v", err)
return 0
}
Str := gamedata.GetStringValue(data, "value")
Strarr := strings.Split(Str, ";")
for _, v := range Strarr {
a := strings.Split(v, ",")
if len(a) != 3 {
continue
}
Min, _ := strconv.Atoi(a[0])
Max, _ := strconv.Atoi(a[1])
if Lv >= Min && Lv <= Max {
Num, _ := strconv.Atoi(a[2])
return Num
}
}
return 0
}
func GetProgressReward(RewardId int) []*item.Item {
data, err := gamedata.GetData(CFG_LIMITED_TIME_EVENT_JACKPOT)
if err != nil {
log.Debug("GetProgressReward err:%v", err)
return nil
}
for k, v := range data {
Id := GoUtil.Int(k)
if Id == RewardId {
return gamedata.GetItemList(v, "Items")
}
}
return nil
}

View File

@ -0,0 +1,466 @@
package limitedTimeEventCfg
import (
"math"
"server/game/mod/item"
GoUtil "server/game_util"
"server/gamedata"
"sort"
"strings"
"gitea.bywaystudios.com/pet_home/leaf/log"
)
const (
CFG_LIMITED_TIME_EVENT = "LimitedTimeEvent"
CFG_LIMITED_TIME_EVENT_METEOR = "LimitedTimeEventMeteor"
CFG_LIMITED_TIME_EVENT_CHEST = "LimitedTimeEventChest"
CFG_LIMITED_TIME_EVENT_ORDER = "LimitedTimeEventOrder"
CFG_LIMITED_TIME_EVENT_SENCE = "LimitedTimeEventSence"
CFG_LIMITED_TIME_EVENT_SENCE_JACKPOT = "LimitedTimeEventSenceJackpot"
CFG_LIMITED_TIME_EVENT_FAST = "LimitedTimeEventFast"
CFG_LIMITED_TIME_EVENT_JACKPOT = "LimitedTimeEventJackpot"
CFG_LIMITED_TIME_EVENT_CHEST_JACKPOT = "LimitedTimeEventChestJackpot"
CFG_LIMITED_TIME_EVENT_CONST = "LimitedTimeEventConst"
CFG_LIMITED_TIME_EVENT_BONUS = "LimitedTimeEventBonus"
CFG_LIMITED_TIME_EVENT_MONEY = "LimitedTimeEventMoney"
CFG_LIMITED_TIME_EVENT_LUCKY = "LimitedTimeEventLucky"
CFG_LIMTTED_TIME_EVENT_DECORATE_OFF = "LimitedTimeEventDecorateOff"
)
func init() {
gamedata.InitCfg(CFG_LIMITED_TIME_EVENT)
gamedata.InitCfg(CFG_LIMITED_TIME_EVENT_METEOR)
gamedata.InitCfg(CFG_LIMITED_TIME_EVENT_CHEST)
gamedata.InitCfg(CFG_LIMITED_TIME_EVENT_ORDER)
gamedata.InitCfg(CFG_LIMITED_TIME_EVENT_SENCE)
gamedata.InitCfg(CFG_LIMITED_TIME_EVENT_SENCE_JACKPOT)
gamedata.InitCfg(CFG_LIMITED_TIME_EVENT_FAST)
gamedata.InitCfg(CFG_LIMITED_TIME_EVENT_JACKPOT)
gamedata.InitCfg(CFG_LIMITED_TIME_EVENT_CONST)
gamedata.InitCfg(CFG_LIMITED_TIME_EVENT_BONUS)
gamedata.InitCfg(CFG_LIMITED_TIME_EVENT_CHEST_JACKPOT)
gamedata.InitCfg(CFG_LIMITED_TIME_EVENT_MONEY)
gamedata.InitCfg(CFG_LIMITED_TIME_EVENT_LUCKY)
gamedata.InitCfg(CFG_LIMTTED_TIME_EVENT_DECORATE_OFF)
}
// 获取限时事件触发列表
func GetLimitedTimeEventCfg() []*gamedata.LimitedTimeEventData {
return nil
}
func GetEventName(Id int) string {
data, err := gamedata.GetDataByIntKey(CFG_LIMITED_TIME_EVENT, Id)
if err != nil {
log.Debug("GetEventName err:%v, Id=%d", err, Id)
return ""
}
return gamedata.GetStringValue(data, "Name")
}
// 获取流星雨加成
func GetMeteorAdd(ChessLv int) int {
data, err := gamedata.GetDataByIntKey(CFG_LIMITED_TIME_EVENT_METEOR, ChessLv)
if err != nil {
log.Debug("GetMeteorAdd err:%v, ChessLv=%d", err, ChessLv)
return 0
}
return gamedata.GetIntValue(data, "Add")
}
// 获取宝箱雨奖励
func GetChestReward(Star int) []*item.Item {
data, err := gamedata.GetData(CFG_LIMITED_TIME_EVENT_CHEST)
if err != nil {
log.Debug("GetChestReward err:%v, Star=%d", err, Star)
return nil
}
for _, v := range data {
Min := gamedata.GetIntValue(v, "Min")
Max := gamedata.GetIntValue(v, "Max")
if Star >= Min && Star <= Max {
return gamedata.GetItemList(v, "Items")
}
}
return nil
}
// 获取超级订单奖励
func GetSuperOrderReward(energy int) []*item.Item {
data, err := gamedata.GetData(CFG_LIMITED_TIME_EVENT_ORDER)
if err != nil {
log.Debug("GetSuperOrderReward err:%v, energy=%d", err, energy)
return nil
}
star := energy
for _, v := range data {
Min := gamedata.GetIntValue(v, "Min")
Max := gamedata.GetIntValue(v, "Max")
if star >= Min && star <= Max {
return gamedata.GetItemList(v, "Items")
}
}
return nil
}
// 获取场景冲刺奖励
func GetSceneDashReward(Sence, Progress int) (int, []*item.Item) {
data, err := gamedata.GetData(CFG_LIMITED_TIME_EVENT_SENCE)
if err != nil {
log.Debug("GetSceneDashReward err:%v, Sence=%d, Progress=%d", err, Sence, Progress)
return 0, nil
}
for k, v := range data {
if Sence == gamedata.GetIntValue(v, "Sence") && Progress == gamedata.GetIntValue(v, "Progress") {
return GoUtil.Int(k), gamedata.GetItemList(v, "Items")
}
}
return 0, nil
}
// 获取场景冲刺奖池
func GetSenceJackpotProb() map[int]int {
data, err := gamedata.GetData(CFG_LIMITED_TIME_EVENT_SENCE_JACKPOT)
if err != nil {
log.Debug("GetSenceJackpotProb err:%v", err)
return nil
}
r := make(map[int]int, len(data))
for k, v := range data {
Id := GoUtil.Int(k)
r[Id] = gamedata.GetIntValue(v, "Prob")
}
return r
}
// 获取场景冲刺奖励
func GetSenceJackpotReward(Id int) []*item.Item {
data, err := gamedata.GetDataByIntKey(CFG_LIMITED_TIME_EVENT_SENCE_JACKPOT, Id)
if err != nil {
log.Debug("GetSenceJackpotReward err:%v, Id=%d", err, Id)
return nil
}
return gamedata.GetItemList(data, "Items")
}
// 获取连击快手奖励
func GetFastProduceReward(Energy int) []*item.Item {
data, err := gamedata.GetData(CFG_LIMITED_TIME_EVENT_FAST)
if err != nil {
log.Debug("GetFastProduceReward err:%v, Energy=%d", err, Energy)
return nil
}
if len(data) == 0 {
return nil
}
// Convert map to slice for sorting
type sortData struct {
Id string
Energy float64
}
sortedList := make([]sortData, 0, len(data))
energy := float64(Energy) / 10.0
for k, v := range data {
dataEnergy := gamedata.GetFloatValue(v, "EnergyValue")
sortedList = append(sortedList, sortData{k, math.Abs(energy - dataEnergy)})
}
// Sort by Energy in ascending order
sort.Slice(sortedList, func(i, j int) bool {
return sortedList[i].Energy < sortedList[j].Energy
})
return gamedata.GetItemList(data[sortedList[0].Id], "Items")
}
// 获取连击快手最大次数
func GetFastProduceMaxTimes() int {
Max := 0
data, err := gamedata.GetData(CFG_LIMITED_TIME_EVENT_FAST)
if err != nil {
log.Debug("GetFastProduceMaxTimes err:%v", err)
return 0
}
for _, v := range data {
Max = max(gamedata.GetIntValue(v, "Times"), Max)
}
return Max
}
// 获取进度最大值
func GetProgressMax(Lv, Num int) int {
data, err := gamedata.GetData(CFG_LIMITED_TIME_EVENT_BONUS)
if err != nil {
log.Debug("GetProgressMax err:%v, Lv=%d, Num=%d", err, Lv, Num)
return 0
}
for _, v := range data {
Min := gamedata.GetIntValue(v, "Min")
Max := gamedata.GetIntValue(v, "Max")
if Lv >= Min && Lv <= Max {
OrderNumStr := gamedata.GetStringValue(v, "OrderNum")
OrderNum := GoUtil.SplitInt(OrderNumStr, ",")
if Num >= len(OrderNum) {
return OrderNum[len(OrderNum)-1]
} else {
return OrderNum[Num]
}
}
}
return 0
}
func GetBonusLv(Lv int) int {
data, err := gamedata.GetData(CFG_LIMITED_TIME_EVENT_BONUS)
if err != nil {
log.Debug("GetBonusLv err:%v, Lv=%d", err, Lv)
return 0
}
for k, v := range data {
Min := gamedata.GetIntValue(v, "Min")
Max := gamedata.GetIntValue(v, "Max")
if Lv >= Min && Lv <= Max {
return GoUtil.Int(k)
}
}
return 0
}
// 获取进度奖励随机
func GetProgressRewardRand(Lv int) map[int]int {
data, err := gamedata.GetData(CFG_LIMITED_TIME_EVENT_JACKPOT)
if err != nil {
log.Debug("GetProgressRewardRand err:%v, Lv=%d", err, Lv)
return nil
}
r := make(map[int]int, len(data))
for k, v := range data {
if Lv >= gamedata.GetIntValue(v, "BonusLv") {
Id := GoUtil.Int(k)
r[Id] = gamedata.GetIntValue(v, "Prob")
}
}
return r
}
func GetJackpotIdByType(Type int) int {
data, err := gamedata.GetData(CFG_LIMITED_TIME_EVENT_JACKPOT)
if err != nil {
log.Debug("GetJackpotIdByType err:%v, Type=%d", err, Type)
return 0
}
for k, v := range data {
Id := GoUtil.Int(k)
if Type == gamedata.GetIntValue(v, "Type") {
return Id
}
}
return 0
}
// 获取进度选择数量
func GetProgressSelectNum(Lv int) int {
data, err := gamedata.GetData(CFG_LIMITED_TIME_EVENT_BONUS)
if err != nil {
log.Debug("GetProgressSelectNum err:%v, Lv=%d", err, Lv)
return 0
}
for _, v := range data {
Min := gamedata.GetIntValue(v, "Min")
Max := gamedata.GetIntValue(v, "Max")
if Lv >= Min && Lv <= Max {
return gamedata.GetIntValue(v, "Option")
}
}
return 0
}
// 获取进度奖励
func GetProgressReward(RewardId int) []*item.Item {
data, err := gamedata.GetData(CFG_LIMITED_TIME_EVENT_JACKPOT)
if err != nil {
log.Debug("GetProgressReward err:%v, RewardId=%d", err, RewardId)
return nil
}
for k, v := range data {
Id := GoUtil.Int(k)
if Id == RewardId {
return gamedata.GetItemList(v, "Items")
}
}
return nil
}
// 获取进度奖励类型
func GetProgressRewardType(RewardId int) int {
data, err := gamedata.GetDataByIntKey(CFG_LIMITED_TIME_EVENT_JACKPOT, RewardId)
if err != nil {
log.Debug("GetProgressRewardType err:%v, RewardId=%d", err, RewardId)
return 0
}
return gamedata.GetIntValue(data, "Type")
}
func GetUnlockLv() int {
data, err := gamedata.GetDataByKey(CFG_LIMITED_TIME_EVENT_CONST, "unlock_lv")
if err != nil {
log.Debug("GetUnlockLv err:%v", err)
return 0
}
return gamedata.GetIntValue(data, "Value")
}
func GetCatReturnGiftItems() []*item.Item {
data, err := gamedata.GetDataByKey(CFG_LIMITED_TIME_EVENT_CONST, "Cat_Return_Gift_Items")
if err != nil {
log.Debug("GetCatReturnGiftItems err:%v", err)
return nil
}
return gamedata.GetItemList(data, "Value")
}
func GetCatSaleCD() int64 {
data, err := gamedata.GetDataByKey(CFG_LIMITED_TIME_EVENT_CONST, "Event_Cooldown_7days")
if err != nil {
log.Debug("GetCatSaleCD err:%v", err)
return 0
}
return int64(gamedata.GetIntValue(data, "Value"))
}
func GetFirstEvent() int {
data, err := gamedata.GetDataByKey(CFG_LIMITED_TIME_EVENT_CONST, "First_Event")
if err != nil {
log.Debug("GetFirstEvent err:%v", err)
return 0
}
return gamedata.GetIntValue(data, "Value")
}
func GetFastCD() int {
data, err := gamedata.GetDataByKey(CFG_LIMITED_TIME_EVENT_CONST, "Fast_Cd")
if err != nil {
log.Debug("GetFastCD err:%v", err)
return 0
}
return gamedata.GetIntValue(data, "Value")
}
func GetPaybackDay() int {
data, err := gamedata.GetDataByKey(CFG_LIMITED_TIME_EVENT_CONST, "payback_day")
if err != nil {
log.Debug("GetPaybackDay err:%v", err)
return 0
}
return gamedata.GetIntValue(data, "Value")
}
func GetHighRollerNeedEnergy() int {
data, err := gamedata.GetDataByKey(CFG_LIMITED_TIME_EVENT_CONST, "High_Roller_Need_Energy")
if err != nil {
log.Debug("GetHighRollerNeedEnergy err:%v", err)
return 0
}
return gamedata.GetIntValue(data, "Value")
}
func GetThiefProb() int {
data, err := gamedata.GetDataByKey(CFG_LIMITED_TIME_EVENT_CONST, "Thief_Prob")
if err != nil {
log.Debug("GetThiefProb err:%v", err)
return 0
}
value := gamedata.GetStringValue(data, "Value")
a1 := strings.Split(value, "|")
prob := make(map[int]int, len(a1))
for _, v := range a1 {
a2 := strings.Split(v, ":")
prob[GoUtil.Int(a2[0])] = GoUtil.Int(a2[1])
}
return GoUtil.RandMap(prob)
}
// 招财猫
func GetMoneyCat(Id int) (float64, int) {
data, err := gamedata.GetDataByIntKey(CFG_LIMITED_TIME_EVENT_MONEY, Id)
if err != nil {
log.Debug("GetMoneyCat err:%v, Id=%d", err, Id)
return 0.0, 0
}
return gamedata.GetFloatValue(data, "Mul"), gamedata.GetIntValue(data, "Cd")
}
func GetMoneyCatMax() int {
data, err := gamedata.GetData(CFG_LIMITED_TIME_EVENT_MONEY)
if err != nil {
log.Debug("GetMoneyCatMax err:%v", err)
return 0
}
return len(data)
}
func GetLuckyCatMaxEarning(Remain int) int {
data, err := gamedata.GetData(CFG_LIMITED_TIME_EVENT_LUCKY)
if err != nil {
log.Debug("GetLuckyCatMaxEarning err:%v, Remain=%d", err, Remain)
return 0
}
type d struct {
t int
e int
}
l := make([]d, 0, len(data))
for k, v := range data {
t := GoUtil.Int(k)
e := gamedata.GetIntValue(v, "Earn")
l = append(l, d{t, e})
}
sort.Slice(l, func(i, j int) bool {
return l[i].t < l[j].t
})
for _, v := range l {
if Remain <= v.t {
return v.e
}
}
return 0
}
func GetCatTrickEnergy() int {
data, err := gamedata.GetDataByKey(CFG_LIMITED_TIME_EVENT_CONST, "CatTrick_ConsumeEnergy")
if err != nil {
log.Debug("GetCatTrickEnergy err:%v", err)
return 0
}
return gamedata.GetIntValue(data, "Value")
}
func GetCatTrickDiamond() int {
data, err := gamedata.GetDataByKey(CFG_LIMITED_TIME_EVENT_CONST, "CatTrick_RewardDiamond")
if err != nil {
log.Debug("GetCatTrickDiamond err:%v", err)
return 0
}
return gamedata.GetIntValue(data, "Value")
}
func GetDecorateOffDiscount(AreaId, StepId int) int {
data, err := gamedata.GetData(CFG_LIMTTED_TIME_EVENT_DECORATE_OFF)
if err != nil {
log.Debug("GetDecorateOffDiscount err:%v, AreaId=%d, StepId=%d", err, AreaId, StepId)
return 100
}
for _, v := range data {
StartAreaId := gamedata.GetIntValue(v, "StartArea")
EndAreaId := gamedata.GetIntValue(v, "EndArea")
StartStepId := gamedata.GetIntValue(v, "StartStep")
EndStepId := gamedata.GetIntValue(v, "EndStep")
if AreaId >= StartAreaId && AreaId <= EndAreaId && StepId >= StartStepId && StepId <= EndStepId {
return gamedata.GetIntValue(v, "Off")
}
}
return 100
}

View File

@ -0,0 +1,112 @@
package mailCfg
import (
"fmt"
languageCfg "server/conf/language"
"server/game/mod/item"
GoUtil "server/game_util"
"server/gamedata"
"server/msg/meowmentnet"
"strings"
)
const (
CFG_LOGIN_BACK = "LoginBack"
CFG_MAIL = "Mail"
MAIL_RECALL_ID = 12
MAIL_CHARGE_SEND_ID = 13
MAIL_CHARGE_RECEIVE_ID = 14
)
type TriggerMail struct {
Id int
Title string
SubTitle string
Content string
EnglistTitle string
SubTitleEn string
EnglistContent string
Items []*item.Item
Type int
Trigger []string
}
func init() {
gamedata.InitCfg(CFG_LOGIN_BACK)
gamedata.InitCfg(CFG_MAIL)
}
func GetLoginBack(Id string) (int, int) {
data, err := gamedata.GetDataByKey(CFG_LOGIN_BACK, Id)
if err != nil {
return 0, 0
}
return gamedata.GetIntValue(data, "Num1"), gamedata.GetIntValue(data, "Num2")
}
func GetTriggerMail() []*TriggerMail {
data, err := gamedata.GetData(CFG_MAIL)
if err != nil {
return nil
}
ret := make([]*TriggerMail, 0)
for k, v := range data {
T := gamedata.GetStringValue(v, "Trigger")
if T == "" {
continue
}
T1 := strings.Split(T, "|")
if len(T1) < 4 {
continue
}
data := &TriggerMail{
Id: GoUtil.Int(k),
Title: gamedata.GetStringValue(v, "Title"),
Content: gamedata.GetStringValue(v, "Content"),
EnglistTitle: gamedata.GetStringValue(v, "EnglistTitle"),
EnglistContent: gamedata.GetStringValue(v, "EnglistContent"),
Items: gamedata.GetItemList(v, "Items"),
Type: gamedata.GetIntValue(v, "Type"),
Trigger: strings.Split(T, "|"),
}
ret = append(ret, data)
}
return ret
}
func GetRecallMail(Title, ItemName, TitleEn, ItemNameEn string) (string, string, string, string) {
data, err := gamedata.GetDataByIntKey(CFG_MAIL, 12)
if err != nil {
return "", "", "", ""
}
mt := languageCfg.GetLanguage(meowmentnet.LANG_TYPE_LangCn, gamedata.GetStringValue(data, "Title"))
mc := languageCfg.GetLanguage(meowmentnet.LANG_TYPE_LangCn, gamedata.GetStringValue(data, "Content"))
mt_en := languageCfg.GetLanguage(meowmentnet.LANG_TYPE_LangEn, gamedata.GetStringValue(data, "Title"))
mc_en := languageCfg.GetLanguage(meowmentnet.LANG_TYPE_LangEn, gamedata.GetStringValue(data, "Content"))
return fmt.Sprintf(mt, Title), fmt.Sprintf(mc, Title, ItemName), fmt.Sprintf(mt_en, TitleEn), fmt.Sprintf(mc_en, Title, ItemNameEn)
}
func GetChargeSendMail(PlayerName string) (string, string, string, string) {
data, err := gamedata.GetDataByIntKey(CFG_MAIL, MAIL_CHARGE_SEND_ID)
if err != nil {
return "", "", "", ""
}
mt := languageCfg.GetLanguage(meowmentnet.LANG_TYPE_LangCn, gamedata.GetStringValue(data, "Title"))
mc := languageCfg.GetLanguage(meowmentnet.LANG_TYPE_LangCn, gamedata.GetStringValue(data, "Content"))
mt_en := languageCfg.GetLanguage(meowmentnet.LANG_TYPE_LangEn, gamedata.GetStringValue(data, "Title"))
mc_en := languageCfg.GetLanguage(meowmentnet.LANG_TYPE_LangEn, gamedata.GetStringValue(data, "Content"))
return mt, fmt.Sprintf(mc, PlayerName), mt_en, fmt.Sprintf(mc_en, PlayerName)
}
func GetChargeReceiveMail(PlayerName string, Content string) (string, string, string, string) {
data, err := gamedata.GetDataByIntKey(CFG_MAIL, MAIL_CHARGE_RECEIVE_ID)
if err != nil {
return "", "", "", ""
}
mt := languageCfg.GetLanguage(meowmentnet.LANG_TYPE_LangCn, gamedata.GetStringValue(data, "Title"))
mc := languageCfg.GetLanguage(meowmentnet.LANG_TYPE_LangCn, gamedata.GetStringValue(data, "Content"))
mt_en := languageCfg.GetLanguage(meowmentnet.LANG_TYPE_LangEn, gamedata.GetStringValue(data, "Title"))
mc_en := languageCfg.GetLanguage(meowmentnet.LANG_TYPE_LangEn, gamedata.GetStringValue(data, "Content"))
return fmt.Sprintf(mt, PlayerName), fmt.Sprintf(mc, Content), fmt.Sprintf(mt_en, PlayerName), fmt.Sprintf(mc_en, Content)
}

View File

@ -1,188 +0,0 @@
package mergeDataCfg
import (
"errors"
"server/gamedata"
"server/pkg/github.com/name5566/leaf/log"
"strconv"
"strings"
)
const (
CFG_NAME = "MergeData"
CONST_NAME = "MergeDataConst"
// 棋子类型
CHESS_PRODUCT_MAIN_TYPE = 1 // 主产物
CHESS_PRODUCT_SUB_TYPE = 2 // 次产物
CHESS_PRODUCT_SUB_EMIT_TYPE = 3 // 次发射器产物
)
func init() {
gamedata.InitCfg(CFG_NAME)
gamedata.InitCfg(CONST_NAME)
}
// 获取单个数据
func GetOne(Id int) (*gamedata.MergeDataRecord, error) {
data, err := gamedata.GetDataByIntKey(CFG_NAME, Id)
if err != nil {
log.Debug("MergeDataCfg GetOne Id:%v not found", Id)
return &gamedata.MergeDataRecord{}, errors.New("not found")
}
return &gamedata.MergeDataRecord{
Id: gamedata.ParseInt(data["Id"]),
Lv: gamedata.ParseInt(data["Lv"]),
MaxLv: gamedata.ParseInt(data["MaxLv"]),
SellType: gamedata.ParseString(data["SellType"]),
SellNum: gamedata.ParseInt(data["SellNum"]),
SellDiamond: gamedata.ParseInt(data["SellDiamond"]),
Color: gamedata.ParseString(data["Color"]),
Star: gamedata.ParseInt(data["Star"]),
Type: gamedata.ParseString(data["Type"]),
Emit_Product: gamedata.ParseString(data["Emit_Product"]),
CoolTime: gamedata.ParseInt(data["CoolTime"]),
}, nil
}
// 根据id获取棋子获得的星星
func GetStarById(Id int) int {
data, err := gamedata.GetDataByIntKey(CFG_NAME, Id)
if err != nil {
log.Debug("MergeDataCfg GetOne Id:%v not found", Id)
return 0
}
return gamedata.ParseInt(data["Star"])
}
// 根据等级和颜色获取棋子id
func GetChessIdByLvAndColor(Lv int, Color string) int {
if Lv == 0 || Color == "" {
return 0
}
data, err := gamedata.GetData(CFG_NAME)
if err != nil {
log.Debug("MergeDataCfg GetChessIdByLvAndColor lv:%v Color:%v not found", Lv, Color)
return 0
}
for k, v := range data {
lv := gamedata.GetIntValue(v, "Lv")
color := gamedata.GetStringValue(v, "Color")
if Lv == lv && color == Color {
Id, _ := strconv.Atoi(k)
return Id
}
}
return 0
}
// 根据Id获取棋子等级
func GetLvById(Id int) int {
data, err := gamedata.GetDataByIntKey(CFG_NAME, Id)
if err != nil {
log.Debug("GetLvById GetOne Id:%v not found", Id)
return 0
}
return gamedata.ParseInt(data["Lv"])
}
// 根据Id获取棋子最大等级
func GetMaxLvById(Id int) int {
data, err := gamedata.GetDataByIntKey(CFG_NAME, Id)
if err != nil {
log.Debug("GetMaxLvById GetOne Id:%v not found", Id)
return 0
}
return gamedata.ParseInt(data["MaxLv"])
}
// 根据Id获取棋子类型
func GetTypeById(Id int) string {
data, err := gamedata.GetDataByIntKey(CFG_NAME, Id)
if err != nil {
log.Debug("GetTypeById GetOne Id:%v not found", Id)
return ""
}
return gamedata.ParseString(data["Type"])
}
// 根据Id获取棋子类型
func GetColorById(Id int) string {
data, err := gamedata.GetDataByIntKey(CFG_NAME, Id)
if err != nil {
log.Debug("GetColorById GetOne Id:%v not found", Id)
return ""
}
return gamedata.ParseString(data["Color"])
}
// 根据Id获取发射器产出类型
func GetEmitProduceType(Id int) []string {
data, err := gamedata.GetDataByIntKey(CFG_NAME, Id)
if err != nil {
log.Debug("GetTypeById GetOne Id:%v not found", Id)
return []string{}
}
return strings.Split(gamedata.ParseString(data["Emit_Product"]), ",")
}
// 根据Id获取发射器Id
func GetEmitId(Id int) string {
data, err := gamedata.GetDataByIntKey(CFG_NAME, Id)
if err != nil {
log.Debug("GetTypeById GetOne Id:%v not found", Id)
return ""
}
return gamedata.ParseString(data["Emit_ID"])
}
// 获取常量
func GetConst(Key string) string {
data, err := gamedata.GetDataByKey(CONST_NAME, Key)
if err != nil {
log.Debug("GetConst GetOne Id:%s not found", Key)
return ""
}
return gamedata.ParseString(data["Value"])
}
// 获取常量
func GetConstInt(Key string) int {
data, err := gamedata.GetDataByKey(CONST_NAME, Key)
if err != nil {
log.Debug("GetConst GetOne Id:%s not found", Key)
return 0
}
return gamedata.ParseInt(data["Value"])
}
func GetExtraEmitId() map[string]struct{} {
Value := GetConst("EmitId_Extra_Order")
arr := strings.Split(Value, ",")
var r = make(map[string]struct{})
for _, v := range arr {
r[v] = struct{}{}
}
return r
}
func GetProductType(Chess int) int {
data, err := gamedata.GetDataByIntKey(CFG_NAME, Chess)
if err != nil {
log.Debug("GetProductType GetOne Id:%v not found", Chess)
return 0
}
return gamedata.ParseInt(data["PType"])
}
func GetChessBagMaxGrid() int {
return GetConstInt("chess_bag_max")
}
func GetChessBagBugNum() int {
return GetConstInt("chess_bag_buy")
}
func GetChessBagInitNum() int {
return GetConstInt("chess_bag_init")
}

View File

@ -0,0 +1,620 @@
package mergeDataCfg
import (
"errors"
"server/game/mod/item"
GoUtil "server/game_util"
"server/gamedata"
"strconv"
"strings"
"gitea.bywaystudios.com/pet_home/leaf/log"
)
const (
CFG_NAME = "MergeData"
CONST_NAME = "MergeDataConst"
CFG_MERGE_EMIT = "MergeDataEmit"
// 棋子类型
CHESS_PRODUCT_MAIN_TYPE = 1 // 主产物
CHESS_PRODUCT_SECONDARY_TYPE = 2 // 次产物
CHESS_PRODUCT_SUB_TYPE = 3 // 子发射器产物
)
func init() {
gamedata.InitCfg(CFG_NAME)
gamedata.InitCfg(CONST_NAME)
gamedata.InitCfg(CFG_MERGE_EMIT)
}
func GetEmitTypeByColor(Color string) string {
data, err := gamedata.GetData(CFG_MERGE_EMIT)
if err != nil {
return ""
}
for k, v := range data {
ColorList := strings.Split(gamedata.GetStringValue(v, "Order_Type"), ",")
if GoUtil.InStringArray(Color, ColorList) {
return k
}
}
return ""
}
func GetEmitProduce(EmitType string) []string {
data, err := gamedata.GetDataByKey(CFG_MERGE_EMIT, EmitType)
if err != nil {
log.Debug("GetEmitProduce GetOne EmitType:%s not found", EmitType)
return []string{}
}
return strings.Split(gamedata.GetStringValue(data, "Product_Type"), ",")
}
func GetEmitOrderProduce(EmitType string) []string {
data, err := gamedata.GetDataByKey(CFG_MERGE_EMIT, EmitType)
if err != nil {
log.Debug("GetEmitProduce GetOne EmitType:%s not found", EmitType)
return []string{}
}
return strings.Split(gamedata.GetStringValue(data, "Order_Type"), ",")
}
// 获取单个数据
func GetOne(Id int) (*gamedata.MergeDataRecord, error) {
data, err := gamedata.GetDataByIntKey(CFG_NAME, Id)
if err != nil {
log.Debug("MergeDataCfg GetOne Id:%v not found", Id)
return &gamedata.MergeDataRecord{}, errors.New("not found")
}
return &gamedata.MergeDataRecord{
Id: gamedata.GetIntValue(data, "Id"),
Lv: gamedata.GetIntValue(data, "Lv"),
MaxLv: gamedata.GetIntValue(data, "MaxLv"),
SellType: gamedata.GetStringValue(data, "SellType"),
SellNum: gamedata.GetIntValue(data, "SellNum"),
SellDiamond: gamedata.GetIntValue(data, "SellDiamond"),
Color: gamedata.GetStringValue(data, "Color"),
Star: gamedata.GetIntValue(data, "Star"),
Type: gamedata.GetStringValue(data, "Type"),
Emit_Product: gamedata.GetStringValue(data, "Emit_Product"),
CoolTime: gamedata.GetIntValue(data, "CoolTime"),
Emit_Type: gamedata.GetStringValue(data, "Emit_Type"),
}, nil
}
// 根据id获取棋子获得的星星
func GetStarById(Id int) int {
data, err := gamedata.GetDataByIntKey(CFG_NAME, Id)
if err != nil {
log.Debug("MergeDataCfg GetOne Id:%v not found", Id)
return 0
}
return gamedata.GetIntValue(data, "Star")
}
func GetNameById(Id int) string {
data, err := gamedata.GetDataByIntKey(CFG_NAME, Id)
if err != nil {
log.Debug("MergeDataCfg GetOne Id:%v not found", Id)
return ""
}
return gamedata.GetStringValue(data, "Title")
}
// 根据等级和颜色获取棋子id
func GetChessIdByLvAndColor(Lv int, Color string) int {
if Lv == 0 || Color == "" {
return 0
}
data, err := gamedata.GetData(CFG_NAME)
if err != nil {
// log.Debug("MergeDataCfg GetChessIdByLvAndColor lv:%v Color:%v not found", Lv, Color)
return 0
}
for k, v := range data {
lv := gamedata.GetIntValue(v, "Lv")
color := gamedata.GetStringValue(v, "Color")
if Lv == lv && color == Color {
Id, _ := strconv.Atoi(k)
return Id
}
}
// log.Debug("MergeDataCfg GetChessIdByLvAndColor lv:%v Color:%v not found", Lv, Color)
return 0
}
// 根据Id获取棋子等级
func GetLvById(Id int) int {
data, err := gamedata.GetDataByIntKey(CFG_NAME, Id)
if err != nil {
//log.Debug("GetLvById GetOne Id:%v not found", Id)
return 0
}
return gamedata.GetIntValue(data, "Lv")
}
// 根据Id获取棋子最大等级
func GetMaxLvById(Id int) int {
data, err := gamedata.GetDataByIntKey(CFG_NAME, Id)
if err != nil {
// log.Debug("GetMaxLvById GetOne Id:%v not found", Id)
return 0
}
return gamedata.GetIntValue(data, "MaxLv")
}
// 根据Id获取棋子最大等级
func GetMaxLvByColor(Color string) int {
ChessId := GetChessIdByLvAndColor(1, Color)
data, err := gamedata.GetDataByIntKey(CFG_NAME, ChessId)
if err != nil {
return 0
}
return gamedata.GetIntValue(data, "MaxLv")
}
// 根据Id获取发射器最小等级
func GetEmitMinLvById(Id string) int {
data, err := gamedata.GetDataByKey(CFG_MERGE_EMIT, Id)
if err != nil {
// log.Debug("GetEmitMinLvById GetOne Id:%v not found", Id)
return 0
}
return gamedata.GetIntValue(data, "Emit_Min_Lv")
}
// 根据Id获取发射器修正系数
func GetEmitRatio(Id string) float64 {
data, err := gamedata.GetDataByKey(CFG_MERGE_EMIT, Id)
if err != nil {
// log.Debug("GetEmitRatio GetOne Id:%v not found", Id)
return 0
}
return gamedata.GetFloatValue(data, "Ratio")
}
func GetEmitRetire(Id string) int {
data, err := gamedata.GetDataByKey(CFG_MERGE_EMIT, Id)
if err != nil {
log.Debug("GetEmitRetire GetOne Id:%v not found", Id)
return 0
}
return gamedata.GetIntValue(data, "Retire")
}
func GetEmitProductNumByColor(Id string) int {
data, err := gamedata.GetData(CFG_MERGE_EMIT)
if err != nil {
log.Debug("GetEmitProductNumByColor GetOne Id:%v not found", Id)
return 0
}
for _, v := range data {
ColorList := strings.Split(gamedata.GetStringValue(v, "Product_Type"), ",")
if GoUtil.InStringArray(Id, ColorList) {
return len(strings.Split(gamedata.GetStringValue(v, "Product_Type"), ","))
}
}
return 0
}
// 根据Id获取棋子类型
func GetTypeById(Id int) string {
data, err := gamedata.GetDataByIntKey(CFG_NAME, Id)
if err != nil {
log.Debug("GetTypeById GetOne Id:%v not found", Id)
return ""
}
return gamedata.GetStringValue(data, "Type")
}
// 根据Id获取棋子类型
func GetColorById(Id int) string {
data, err := gamedata.GetDataByIntKey(CFG_NAME, Id)
if err != nil {
log.Debug("GetColorById GetOne Id:%v not found", Id)
return ""
}
return gamedata.GetStringValue(data, "Color")
}
// 根据Id获取棋子类型
func GetSellNumById(Id int) int {
data, err := gamedata.GetDataByIntKey(CFG_NAME, Id)
if err != nil {
log.Debug("GetSellNumById GetOne Id:%v not found", Id)
return 0
}
return gamedata.GetIntValue(data, "SellNum")
}
// 根据Id获取发射器产出类型
func GetEmitProduceType(Id int) []string {
data, err := gamedata.GetDataByIntKey(CFG_NAME, Id)
if err != nil {
//log.Debug("GetEmitProduceType GetOne Id:%v not found", Id)
return []string{}
}
return strings.Split(gamedata.GetStringValue(data, "Emit_Product"), ",")
}
// 根据Id获取发射器产出类型
func GetEmitProduceChessType(Id int) []string {
data, err := gamedata.GetDataByIntKey(CFG_NAME, Id)
if err != nil {
//log.Debug("GetEmitProduceChessType GetOne Id:%v not found", Id)
return []string{}
}
value := gamedata.GetStringValue(data, "Product_Type")
if value == "" {
return []string{}
}
return strings.Split(value, ",")
}
// 根据Id获取发射器Id
func GetEmitId(Id int) string {
data, err := gamedata.GetDataByIntKey(CFG_NAME, Id)
if err != nil {
// log.Debug("GetEmitId GetOne Id:%v not found", Id)
return ""
}
return gamedata.GetStringValue(data, "Emit_ID")
}
func GetAllId() []int {
data, err := gamedata.GetData(CFG_NAME)
if err != nil {
return []int{}
}
key := make([]int, 0, len(data))
for k := range data {
key = append(key, GoUtil.Int(k))
}
return key
}
func GetEmitType(Id int) string {
data, err := gamedata.GetDataByIntKey(CFG_NAME, Id)
if err != nil {
log.Debug("GetEmitType GetOne Id:%v not found", Id)
return ""
}
return gamedata.GetStringValue(data, "Emit_Type")
}
func GetEmitN(EmitSeries string) int {
data, err := gamedata.GetDataByKey(CFG_MERGE_EMIT, EmitSeries)
if err != nil {
log.Debug("GetEmitN GetOne EmitSeries:%v not found", EmitSeries)
return 0
}
return gamedata.GetIntValue(data, "N")
}
// 获取常量
func GetConst(Key string) string {
data, err := gamedata.GetDataByKey(CONST_NAME, Key)
if err != nil {
log.Debug("GetConst GetOne Id:%s not found", Key)
return ""
}
return gamedata.GetStringValue(data, "Value")
}
// 获取常量
func GetConstInt(Key string) int {
data, err := gamedata.GetDataByKey(CONST_NAME, Key)
if err != nil {
log.Debug("GetConst GetOne Id:%s not found", Key)
return 0
}
return gamedata.GetIntValue(data, "Value")
}
func GetExtraEmitId() map[string]struct{} {
Value := GetConst("EmitId_Extra_Order")
arr := strings.Split(Value, ",")
var r = make(map[string]struct{})
for _, v := range arr {
r[v] = struct{}{}
}
return r
}
func GetChessBagMaxGrid() int {
return GetConstInt("chess_bag_max")
}
func GetChessBagBugNum() int {
return GetConstInt("chess_bag_buy")
}
func GetChessBagInitNum() int {
return GetConstInt("chess_bag_init")
}
func GetSourceChestItem() []*item.Item {
data, err := gamedata.GetDataByKey(CONST_NAME, "source_chest_reward")
if err != nil {
log.Debug("GetSourceChestItem GetOne not found")
return nil
}
Str := gamedata.GetStringValue(data, "Value")
A1 := strings.Split(Str, "|")
var r []*item.Item
if len(A1) < 2 {
return r
}
A2 := strings.Split(A1[0], "/")
A3 := GoUtil.StringToInt(A2)
A4 := GoUtil.RandSlice(A3)
r = append(r, item.NewItem(item.ITEM_DIAMOND_ID, A4))
B2 := strings.Split(A1[1], "/")
B3 := GoUtil.StringToInt(B2)
B4 := GoUtil.RandSlice(B3)
r = append(r, item.NewItem(item.ITEM_ENERGY_ID, B4))
return r
}
func GetHighSourceChestItem() []*item.Item {
data, err := gamedata.GetDataByKey(CONST_NAME, "high_source_chest_reward")
if err != nil {
log.Debug("GetHighSourceChestItem GetOne not found")
return nil
}
Str := gamedata.GetStringValue(data, "Value")
A1 := strings.Split(Str, "|")
var r []*item.Item
if len(A1) < 2 {
return r
}
A2 := strings.Split(A1[0], "/")
A3 := GoUtil.StringToInt(A2)
A4 := GoUtil.RandSlice(A3)
r = append(r, item.NewItem(item.ITEM_DIAMOND_ID, A4))
B2 := strings.Split(A1[1], "/")
B3 := GoUtil.StringToInt(B2)
B4 := GoUtil.RandSlice(B3)
r = append(r, item.NewItem(item.ITEM_ENERGY_ID, B4))
return r
}
func GetRetireReward() []*item.Item {
data, err := gamedata.GetDataByKey(CONST_NAME, "retire_reward")
if err != nil {
log.Debug("GetRetireReward GetOne not found")
return nil
}
return gamedata.GetItemList(data, "Value")
}
func DynamicLevRev(Lv int, EmitId int, Color string) int {
data, err := gamedata.GetDataByIntKey(CFG_NAME, EmitId)
if err != nil {
// log.Debug("DynamicLev GetOne EmitId:%v not found", EmitId)
return Lv
}
DynamicLv := gamedata.GetStringValue(data, "Dynamic")
if DynamicLv == "" {
return Lv
}
Arr := strings.Split(DynamicLv, ",")
for _, v := range Arr {
Arr2 := strings.Split(v, "=")
if len(Arr2) != 2 {
continue
}
if Color == Arr2[0] {
NewLv, _ := strconv.Atoi(Arr2[1])
return Lv + NewLv
}
}
return Lv
}
func DynamicLev(Lv int, EmitId int, Color string) int {
data, err := gamedata.GetDataByIntKey(CFG_NAME, EmitId)
if err != nil {
//log.Debug("DynamicLev GetOne EmitId:%v not found", EmitId)
return Lv
}
DynamicLv := gamedata.GetStringValue(data, "Dynamic")
if DynamicLv == "" {
return Lv
}
Arr := strings.Split(DynamicLv, ",")
for _, v := range Arr {
Arr2 := strings.Split(v, "=")
if len(Arr2) != 2 {
continue
}
if Color == Arr2[0] {
NewLv, _ := strconv.Atoi(Arr2[1])
return Lv - NewLv
}
}
return Lv
}
func DynamicLev2(Lv int, EmitId int, Color1 string, Color2 string) int {
data, err := gamedata.GetDataByIntKey(CFG_NAME, EmitId)
if err != nil {
//log.Debug("DynamicLev GetOne EmitId:%v not found", EmitId)
return Lv
}
DynamicLv := gamedata.GetStringValue(data, "Dynamic")
if DynamicLv == "" {
return Lv
}
Arr := strings.Split(DynamicLv, ",")
BaseLv := Lv
for _, v := range Arr {
Arr2 := strings.Split(v, "=")
if len(Arr2) != 2 {
continue
}
if Color1 == Arr2[0] {
NewLv, _ := strconv.Atoi(Arr2[1])
BaseLv = Lv + NewLv
}
}
for _, v := range Arr {
Arr2 := strings.Split(v, "=")
if len(Arr2) != 2 {
continue
}
if Color2 == Arr2[0] {
NewLv, _ := strconv.Atoi(Arr2[1])
return BaseLv - NewLv
}
}
return Lv
}
func DynamicAdjust(EmitId int, Color string, EnergyMul int) int {
data, err := gamedata.GetDataByIntKey(CFG_NAME, EmitId)
if err != nil {
//log.Debug("DynamicLev GetOne EmitId:%v not found", EmitId)
return 0
}
DynamicLv := ""
switch EnergyMul {
case 0:
DynamicLv = gamedata.GetStringValue(data, "Dynamic")
case 1, 2:
DynamicLv = gamedata.GetStringValue(data, "Dynamic1")
case 3, 4:
DynamicLv = gamedata.GetStringValue(data, "Dynamic2")
default:
DynamicLv = gamedata.GetStringValue(data, "Dynamic3")
}
if DynamicLv == "" {
return 0
}
Arr := strings.Split(DynamicLv, ",")
for _, v := range Arr {
Arr2 := strings.Split(v, "=")
if len(Arr2) != 2 {
continue
}
if Color == Arr2[0] {
NewLv, _ := strconv.Atoi(Arr2[1])
return NewLv
}
}
return 0
}
func GetAdjust(EmitId int, Color string, EnergyMul int) int {
data, err := gamedata.GetDataByIntKey(CFG_NAME, EmitId)
if err != nil {
//log.Debug("DynamicLev GetOne EmitId:%v not found", EmitId)
return 0
}
AdjustLv := ""
switch EnergyMul {
case 0:
AdjustLv = gamedata.GetStringValue(data, "Dynamic")
case 1, 2:
AdjustLv = gamedata.GetStringValue(data, "Dynamic1")
case 3, 4:
AdjustLv = gamedata.GetStringValue(data, "Dynamic2")
default:
AdjustLv = gamedata.GetStringValue(data, "Dynamic3")
}
if AdjustLv == "" {
return 0
}
Arr := strings.Split(AdjustLv, ",")
for _, v := range Arr {
Arr2 := strings.Split(v, "=")
if len(Arr2) != 2 {
continue
}
if Color == Arr2[0] {
NewLv, _ := strconv.Atoi(Arr2[1])
return NewLv
}
}
return 0
}
func GetSellDiamondMul(EmitId int, Color string) int {
data, err := gamedata.GetDataByIntKey(CFG_NAME, EmitId)
if err != nil {
log.Debug("GetSellDiamondMul GetOne EmitId:%v not found", EmitId)
return 0
}
ProductEmit := gamedata.GetStringValue(data, "Emit_Product")
if ProductEmit == "" {
return 1
}
Arr := strings.Split(ProductEmit, ",")
if Color == Arr[0] {
return 1
}
if !GoUtil.InStringArray(Color, Arr) {
return 1
}
Emit_List := gamedata.GetStringValue(data, "Emit_List")
MainProb := getColorProb(Emit_List, Arr[0])
SubProb := getColorProb(Emit_List, Color)
return int(MainProb / SubProb)
}
func getColorProb(Emit_List string, Color string) float64 {
Arr := strings.Split(Emit_List, ",")
Prob := 0.0
for _, v := range Arr {
Arr2 := strings.Split(v, "=")
A1 := GoUtil.Int(Arr2[0])
A2, _ := strconv.ParseFloat(Arr2[1], 64)
A1Color := GetColorById(A1)
if A1Color != Color {
continue
}
Prob += A2 * float64(A1%10)
}
return Prob
}
func GetMergeStar(MergeList []int) int {
Star := 0
for _, v := range MergeList {
Star += GetStarById(v)
}
return Star
}
func GetAllChessBySeries(Series string) []int {
data, err := gamedata.GetData(CFG_NAME)
ProductList := GetEmitProduce(Series)
if err != nil {
return []int{}
}
var r []int
for k, v := range data {
Type := gamedata.GetStringValue(v, "Type")
if Type == "Emitter" {
EmitId := gamedata.GetStringValue(v, "Emit_ID")
if EmitId == Series {
r = append(r, GoUtil.Int(k))
}
}
if Type == "Product" {
Color := gamedata.GetStringValue(v, "Color")
if GoUtil.InStringArray(Color, ProductList) {
r = append(r, GoUtil.Int(k))
}
}
}
return r
}

View File

@ -0,0 +1,130 @@
package miningCfg
import (
"server/game/mod/item"
GoUtil "server/game_util"
"server/gamedata"
"strconv"
"strings"
)
const (
CFG_MINING_TEMPLATE = "MiningTemplate"
CFG_MINING_JACKPOT = "MiningJackpot"
CFG_MINING_GEM = "MiningGem"
CFG_MINING_PASS = "MiningPass"
)
func init() {
gamedata.InitCfg(CFG_MINING_TEMPLATE)
gamedata.InitCfg(CFG_MINING_JACKPOT)
gamedata.InitCfg(CFG_MINING_GEM)
gamedata.InitCfg(CFG_MINING_PASS)
}
func GetTemplate(Id int) int {
data, err := gamedata.GetDataByIntKey(CFG_MINING_TEMPLATE, Id)
if err != nil {
return 0
}
return gamedata.GetIntValue(data, "Template")
}
func GetActivityItemId(Id int) int {
data, err := gamedata.GetDataByIntKey(CFG_MINING_TEMPLATE, Id)
if err != nil {
return 0
}
return gamedata.GetIntValue(data, "ItemId")
}
func GetLoseItem(Id int) []*item.Item {
data, err := gamedata.GetDataByIntKey(CFG_MINING_TEMPLATE, Id)
if err != nil {
return nil
}
return gamedata.GetItemList(data, "ItemCost")
}
func GetStartItemNum(Id int) int {
data, err := gamedata.GetDataByIntKey(CFG_MINING_TEMPLATE, Id)
if err != nil {
return 0
}
return gamedata.GetIntValue(data, "StartItemNum")
}
func GetPassArea(Id int) int {
data, err := gamedata.GetDataByIntKey(CFG_MINING_PASS, Id)
if err != nil {
return 0
}
Area := gamedata.GetStringValue(data, "Area")
strArr := strings.Split(Area, "*")
a, _ := strconv.Atoi(strArr[0])
b, _ := strconv.Atoi(strArr[1])
return a * b
}
func GetPassGem(Id int) []int {
data, err := gamedata.GetDataByIntKey(CFG_MINING_PASS, Id)
if err != nil {
return nil
}
Gem := gamedata.GetStringValue(data, "Gem")
strArr := strings.Split(Gem, "|")
result := make([]int, 0, len(strArr))
for _, v := range strArr {
a, _ := strconv.Atoi(v)
result = append(result, a)
}
return result
}
func GetRandItem() []*item.Item {
data, err := gamedata.GetData(CFG_MINING_JACKPOT)
if err != nil {
return nil
}
ProbMap := make(map[int]int)
for k, v := range data {
Id := GoUtil.Int(k)
ProbMap[Id] = gamedata.GetIntValue(v, "Prob")
}
RandId := GoUtil.RandMap(ProbMap)
Info, _ := gamedata.GetDataByIntKey(CFG_MINING_JACKPOT, RandId)
return gamedata.GetItemList(Info, "Items")
}
func GetPassItem(Id int, orderFactor int) []*item.Item {
data, err := gamedata.GetDataByIntKey(CFG_MINING_PASS, Id)
if err != nil {
return nil
}
items := gamedata.GetItemList(data, "Items")
starReward := gamedata.GetIntValue(data, "StarReward")
var starnum int
if starReward > 0 {
starnum = int((float64(starReward) * float64(orderFactor) / 500)) * 5
if starnum > 0 {
items = append(items, &item.Item{
Id: item.ITEM_STAR_ID,
Num: starnum,
})
}
}
return items
}
func GetPassItemList(orderFactor int) map[int][]*item.Item {
data, err := gamedata.GetData(CFG_MINING_PASS)
if err != nil {
return nil
}
result := make(map[int][]*item.Item)
for k := range data {
Id := GoUtil.Int(k)
result[Id] = GetPassItem(Id, orderFactor)
}
return result
}

View File

@ -0,0 +1,43 @@
package notification_cfg
import "server/gamedata"
const (
CFG_NOTIFICATION = "Notification"
)
func init() {
gamedata.InitCfg(CFG_NOTIFICATION)
}
func GetFriendApplyNotificationCooldown() int {
data, err := gamedata.GetDataByIntKey(CFG_NOTIFICATION, 2)
if err != nil {
return 0
}
return gamedata.GetIntValue(data, "Cooldown")
}
func GetPetroomGameNotificationCooldown() (int, int) {
data, err := gamedata.GetDataByIntKey(CFG_NOTIFICATION, 1)
if err != nil {
return 0, 0
}
return gamedata.GetIntValue(data, "Cooldown"), gamedata.GetIntValue(data, "DailyLimit")
}
func GetPetroomGameNotificationMsg() (string, string) {
data, err := gamedata.GetDataByIntKey(CFG_NOTIFICATION, 1)
if err != nil {
return "", ""
}
return gamedata.GetStringValue(data, "TitleKey"), gamedata.GetStringValue(data, "InfoKey")
}
func GetFriendApplyNotificationMsg() (string, string) {
data, err := gamedata.GetDataByIntKey(CFG_NOTIFICATION, 2)
if err != nil {
return "", ""
}
return gamedata.GetStringValue(data, "TitleKey"), gamedata.GetStringValue(data, "InfoKey")
}

View File

@ -1,38 +0,0 @@
package orderCfg
import (
"server/gamedata"
)
const (
CFG_ORDER_DATA = "OrderData"
CFG_ORDER_CHESS_DATA = "OrderChessData"
)
func init() {
gamedata.InitCfg(CFG_ORDER_DATA)
gamedata.InitCfg(CFG_ORDER_CHESS_DATA)
}
func GetLvMin(EnergyMul int) int {
data, err := gamedata.GetDataByIntKey(CFG_ORDER_DATA, EnergyMul)
if err != nil {
return 0
}
return gamedata.GetIntValue(data, "LvMin")
}
func GetLvMax(EnergyMul, N int) int {
if v, ok := gamedata.G_AllConfigsJsonData["OrderChessData"]; ok {
data := v.GetData()
for _, v := range data {
dEnergy := gamedata.GetIntValue(v, "EnergyMul")
Min := gamedata.GetIntValue(v, "MinN")
Max := gamedata.GetIntValue(v, "MaxN")
if dEnergy == EnergyMul && N >= Min && N <= Max {
return gamedata.GetIntValue(v, "MaxLv")
}
}
}
return 0
}

View File

@ -0,0 +1,190 @@
package orderCfg
import (
"math"
"server/gamedata"
"sort"
"strconv"
)
const (
CFG_ORDER_DATA = "OrderData"
CFG_ORDER_CHESS_DATA = "OrderChessData"
CFG_ORDER_NUM_DATA = "OrderNumData"
CFG_START_ORDER = "StartOrder"
CFG_CONST = "OrderConst"
CFG_ORDER_SCENE = "OrderScene"
CFG_ORDER_K = "OrderK"
)
func init() {
gamedata.InitCfg(CFG_ORDER_DATA)
gamedata.InitCfg(CFG_ORDER_CHESS_DATA)
gamedata.InitCfg(CFG_ORDER_NUM_DATA)
gamedata.InitCfg(CFG_START_ORDER)
gamedata.InitCfg(CFG_CONST)
gamedata.InitCfg(CFG_ORDER_SCENE)
gamedata.InitCfg(CFG_ORDER_K)
}
func GetOrderK(Lv int) int {
data, err := gamedata.GetDataByIntKey(CFG_ORDER_K, Lv)
if err != nil {
return 0
}
Energy := gamedata.GetIntValue(data, "Energy")
K := gamedata.GetFloatValue(data, "K")
return int(math.Round(float64(Energy)*K/10) * 10)
}
func GetOrderFactor(Scene int) int {
data, err := gamedata.GetDataByIntKey(CFG_ORDER_SCENE, Scene)
if err != nil {
return 0
}
return gamedata.GetIntValue(data, "Factor")
}
func GetEmitExtraOrder() string {
data, err := gamedata.GetDataByKey(CFG_CONST, "EmitId_Extra_Order")
if err != nil {
return ""
}
return gamedata.GetStringValue(data, "Value")
}
func GetLvMin(EnergyMul int) int {
data, err := gamedata.GetDataByIntKey(CFG_ORDER_DATA, EnergyMul)
if err != nil {
return 0
}
return gamedata.GetIntValue(data, "LvMin")
}
func GetLvMax(EnergyMul, N int) int {
data, err := gamedata.GetData(CFG_ORDER_CHESS_DATA)
if err != nil {
return 0
}
for _, v := range data {
dEnergy := gamedata.GetIntValue(v, "EnergyMul")
Min := gamedata.GetIntValue(v, "MinN")
Max := gamedata.GetIntValue(v, "MaxN")
if dEnergy == EnergyMul {
if N >= Min && N <= Max {
return gamedata.GetIntValue(v, "MaxLv")
}
}
}
return 0
}
func GetOrderNum(Level int) int {
data, err := gamedata.GetData(CFG_ORDER_NUM_DATA)
if err != nil {
return 0
}
for _, v := range data {
Min := gamedata.GetIntValue(v, "Min")
Max := gamedata.GetIntValue(v, "Max")
if Level >= Min && Level <= Max {
return gamedata.GetIntValue(v, "Num")
}
}
return 0
}
func GetStartOrderInfo(Id int) (int, int) {
data, err := gamedata.GetDataByIntKey(CFG_START_ORDER, Id)
if err != nil {
return -1, -1
}
return gamedata.GetIntValue(data, "group"), gamedata.GetIntValue(data, "step")
}
func GetStartOrderList() []*gamedata.StartOrderData {
data, err := gamedata.GetData(CFG_START_ORDER)
if err != nil {
return []*gamedata.StartOrderData{}
}
ret := make([]*gamedata.StartOrderData, 0, len(data))
for k, v := range data {
OrderId, _ := strconv.Atoi(k)
ret = append(ret, &gamedata.StartOrderData{
Id: OrderId,
MergeList: gamedata.GetIntSliceValue(v, "merge_id_list"),
Appear: gamedata.GetStringValue(v, "appear"),
Preview: gamedata.GetStringValue(v, "preview"),
Items: gamedata.GetItemList(v, "reward"),
Group: gamedata.GetIntValue(v, "group"),
Step: gamedata.GetIntValue(v, "step"),
})
}
sort.Slice(ret, func(i, j int) bool {
return ret[i].Id < ret[j].Id
})
return ret
}
func GetStartOrderByStep(id int) []*gamedata.StartOrderData {
data, err := gamedata.GetData(CFG_START_ORDER)
if err != nil {
return []*gamedata.StartOrderData{}
}
ret := make([]*gamedata.StartOrderData, len(data))
for k, v := range data {
stepVal := gamedata.GetIntValue(v, "step")
if int(stepVal) == id {
OrderId, _ := strconv.Atoi(k)
ret = append(ret, &gamedata.StartOrderData{
Id: OrderId,
Step: int(stepVal),
MergeList: gamedata.GetIntSliceValue(v, "merge_id_list"),
})
}
}
sort.Slice(ret, func(i, j int) bool {
return ret[i].Id < ret[j].Id
})
return ret
}
func GetStartOrderById(id int) *gamedata.StartOrderData {
data, err := gamedata.GetData(CFG_START_ORDER)
if err != nil {
return nil
}
v, ok := data[strconv.Itoa(id)]
if !ok {
return nil
}
stepVal := gamedata.GetIntValue(v, "step")
return &gamedata.StartOrderData{
Id: id,
Step: int(stepVal),
MergeList: gamedata.GetIntSliceValue(v, "merge_id_list"),
}
}
func GetMaxStep() int {
data, err := gamedata.GetData(CFG_START_ORDER)
if err != nil {
return 0
}
max := 0
for _, v := range data {
stepVal := gamedata.GetIntValue(v, "step")
if int(stepVal) > max {
max = int(stepVal)
}
}
return max
}
func GetOrderType(Id int) int {
data, err := gamedata.GetDataByIntKey(CFG_START_ORDER, Id)
if err != nil {
return 0
}
return gamedata.GetIntValue(data, "type")
}

View File

@ -0,0 +1,122 @@
package passCfg
import (
"server/game/mod/item"
"server/gamedata"
"slices"
)
const (
CFG_PASS_TEMPLATE = "PassTemplate"
CFG_PASS = "Pass"
)
func init() {
gamedata.InitCfg(CFG_PASS_TEMPLATE)
gamedata.InitCfg(CFG_PASS)
}
func GetTemplate(Id int) int {
data, err := gamedata.GetDataByIntKey(CFG_PASS_TEMPLATE, Id)
if err != nil {
return 0
}
return gamedata.GetIntValue(data, "Template")
}
func GetActivityItemId(Id int) int {
data, err := gamedata.GetDataByIntKey(CFG_PASS_TEMPLATE, Id)
if err != nil {
return 0
}
return gamedata.GetIntValue(data, "ItemId")
}
func GetLowChargeId(Id int) int {
data, err := gamedata.GetDataByIntKey(CFG_PASS_TEMPLATE, Id)
if err != nil {
return 0
}
return gamedata.GetIntValue(data, "LowChargeId")
}
func GetHighChargeId(Id int) int {
data, err := gamedata.GetDataByIntKey(CFG_PASS_TEMPLATE, Id)
if err != nil {
return 0
}
return gamedata.GetIntValue(data, "HighChargeId")
}
func GetNewLevel(Template int, Score int, Reward []int) []int {
data, err := gamedata.GetData(CFG_PASS)
if err != nil {
return nil
}
NewReward := make([]int, 0, len(data))
for _, v := range data {
if gamedata.GetIntValue(v, "Template") != Template {
continue
}
LevelScore := gamedata.GetIntValue(v, "TotalScore")
Index := gamedata.GetIntValue(v, "Level")
if Score >= LevelScore && !slices.Contains(Reward, Index) {
NewReward = append(NewReward, Index)
}
}
return NewReward
}
func GetFreeChargeItems(Template int, Reward []int) []*item.Item {
data, err := gamedata.GetData(CFG_PASS)
if err != nil {
return nil
}
Items := make([]*item.Item, 0, len(data))
for _, v := range data {
if gamedata.GetIntValue(v, "Template") != Template {
continue
}
Index := gamedata.GetIntValue(v, "Level")
if slices.Contains(Reward, Index) {
Items = append(Items, gamedata.GetItemList(data, "FreeReward")...)
}
}
return Items
}
func GetLowChargeItems(Template int, Reward []int) []*item.Item {
data, err := gamedata.GetData(CFG_PASS)
if err != nil {
return nil
}
Items := make([]*item.Item, 0, len(data))
for _, v := range data {
if gamedata.GetIntValue(v, "Template") != Template {
continue
}
Index := gamedata.GetIntValue(v, "Level")
if slices.Contains(Reward, Index) {
Items = append(Items, gamedata.GetItemList(data, "LowReward")...)
}
}
return Items
}
func GetHighChargeItems(Template int, Reward []int) []*item.Item {
data, err := gamedata.GetData(CFG_PASS)
if err != nil {
return nil
}
Items := make([]*item.Item, 0, len(data))
for _, v := range data {
if gamedata.GetIntValue(v, "Template") != Template {
continue
}
Index := gamedata.GetIntValue(v, "Level")
if slices.Contains(Reward, Index) {
Items = append(Items, gamedata.GetItemList(data, "HighReward")...)
}
}
return Items
}

View File

@ -0,0 +1,731 @@
package playroomCfg
import (
"server/game/mod/item"
GoUtil "server/game_util"
"server/gamedata"
"strings"
"gitea.bywaystudios.com/pet_home/leaf/log"
)
const (
CFG_PLAYROOM_CONST = "PlayroomConst"
CFG_PLAYROOM_DECORATE = "PlayroomDecorate"
CFG_PLAYROOM_MOOD = "PlayroomMood"
CFG_PLAYROOM_PHYSIOLOGY = "PlayroomPhysiology"
CFG_PLAYROOM_PHYSIOLOGY_TYPE = "PlayroomPhysiologyType"
CFG_PLAYROOM_SHOP = "PlayroomShop"
CFG_PLAYROOM_DRESS = "PlayroomDress"
CFG_PLAYROOM_AIR = "PlayroomAir"
CFG_PLAYROOM_LOCK = "PlayroomLock"
CFG_PLAYROOM_DAILYTASK = "PlayroomDailyTask"
CFG_PLAYROOM_DAILYTASKREWARD = "PlayroomDailyTaskReward"
CFG_PLAYROOM_TASKJACKPOT = "PlayroomTaskJackpot"
CFG_PLAYROOM_ORDERITEM = "PlayroomOrderItem" // 订单物品
)
func init() {
gamedata.InitCfg(CFG_PLAYROOM_CONST)
gamedata.InitCfg(CFG_PLAYROOM_DECORATE)
gamedata.InitCfg(CFG_PLAYROOM_MOOD)
gamedata.InitCfg(CFG_PLAYROOM_PHYSIOLOGY)
gamedata.InitCfg(CFG_PLAYROOM_PHYSIOLOGY_TYPE)
gamedata.InitCfg(CFG_PLAYROOM_SHOP)
gamedata.InitCfg(CFG_PLAYROOM_DRESS)
gamedata.InitCfg(CFG_PLAYROOM_AIR)
gamedata.InitCfg(CFG_PLAYROOM_LOCK)
gamedata.InitCfg(CFG_PLAYROOM_DAILYTASK)
gamedata.InitCfg(CFG_PLAYROOM_DAILYTASKREWARD)
gamedata.InitCfg(CFG_PLAYROOM_TASKJACKPOT)
gamedata.InitCfg(CFG_PLAYROOM_ORDERITEM)
}
func GetShopItem(Id int) (int, []*item.Item, int, int) {
data, err := gamedata.GetDataByIntKey(CFG_PLAYROOM_SHOP, Id)
if err != nil {
return 0, nil, 0, 0
}
return gamedata.GetIntValue(data, "ItemId"), gamedata.GetItemList(data, "Cost"), gamedata.GetIntValue(data, "Discount"), gamedata.GetIntValue(data, "Limit")
}
func GetShopWeeklyLimit() map[int]gamedata.WeeklyDiscountInfo {
r := make(map[int]gamedata.WeeklyDiscountInfo)
data, err := gamedata.GetData(CFG_PLAYROOM_SHOP)
if err != nil {
return r
}
for k, v := range data {
Id := GoUtil.Int(k)
Limit := gamedata.GetIntValue(v, "Limit")
if Limit == 0 {
continue
}
r[Id] = gamedata.WeeklyDiscountInfo{
Id: Id,
Discount: gamedata.GetIntValue(v, "Discount"),
WeeklyLimit: Limit,
}
}
return r
}
func GetShopWish(Id int) int {
data, err := gamedata.GetDataByIntKey(CFG_PLAYROOM_SHOP, Id)
if err != nil {
return 0
}
return gamedata.GetIntValue(data, "Wish")
}
func GetUnLockLv() int {
data, err := gamedata.GetDataByKey(CFG_PLAYROOM_CONST, "Lv")
if err != nil {
return 999
}
return gamedata.GetIntValue(data, "Value")
}
func GetGameOutline() int {
data, err := gamedata.GetDataByKey(CFG_PLAYROOM_CONST, "GameOutline")
if err != nil {
return 999
}
return gamedata.GetIntValue(data, "Value")
}
func GetChipNum() int {
data, err := gamedata.GetDataByKey(CFG_PLAYROOM_CONST, "ChipNum")
if err != nil {
return 12
}
return gamedata.GetIntValue(data, "Value")
}
func GetOrderStar() int {
data, err := gamedata.GetDataByKey(CFG_PLAYROOM_CONST, "Star")
if err != nil {
return 1000
}
return gamedata.GetIntValue(data, "Value")
}
func GetRewardStar() int {
data, err := gamedata.GetDataByKey(CFG_PLAYROOM_CONST, "RewardStar")
if err != nil {
return 1000
}
return gamedata.GetIntValue(data, "Value")
}
func GetTriggerCd() int {
data, err := gamedata.GetDataByKey(CFG_PLAYROOM_CONST, "CD")
if err != nil {
return 1000
}
return gamedata.GetIntValue(data, "Value")
}
func GetFoodItem() []int {
data, err := gamedata.GetDataByKey(CFG_PLAYROOM_CONST, "Food")
if err != nil {
return []int{}
}
r := make([]int, 0, len(data))
for _, v := range strings.Split(gamedata.GetStringValue(data, "Value"), ",") {
if v == "" {
continue
}
Id := GoUtil.Int(v)
r = append(r, Id)
}
return r
}
func GetCleanItem() []int {
data, err := gamedata.GetDataByKey(CFG_PLAYROOM_CONST, "Clean")
if err != nil {
return []int{}
}
r := make([]int, 0, len(data))
for _, v := range strings.Split(gamedata.GetStringValue(data, "Value"), ",") {
if v == "" {
continue
}
Id := GoUtil.Int(v)
r = append(r, Id)
}
return r
}
func GetToyItem() []int {
data, err := gamedata.GetDataByKey(CFG_PLAYROOM_CONST, "Toy")
if err != nil {
return []int{}
}
r := make([]int, 0, len(data))
for _, v := range strings.Split(gamedata.GetStringValue(data, "Value"), ",") {
if v == "" {
continue
}
Id := GoUtil.Int(v)
r = append(r, Id)
}
return r
}
func GetDailyItem() []int {
data, err := gamedata.GetDataByKey(CFG_PLAYROOM_CONST, "Daily")
if err != nil {
return []int{}
}
r := make([]int, 0, len(data))
for _, v := range strings.Split(gamedata.GetStringValue(data, "Value"), ",") {
if v == "" {
continue
}
Id := GoUtil.Int(v)
r = append(r, Id)
}
return r
}
func GetPremiumItem() []int {
data, err := gamedata.GetDataByKey(CFG_PLAYROOM_CONST, "PremiumItem")
if err != nil {
return []int{}
}
r := make([]int, 0, len(data))
for _, v := range strings.Split(gamedata.GetStringValue(data, "Value"), ",") {
if v == "" {
continue
}
Id := GoUtil.Int(v)
r = append(r, Id)
}
return r
}
func GetInteractNum() int {
data, err := gamedata.GetDataByKey(CFG_PLAYROOM_CONST, "InteractNum")
if err != nil {
return 0
}
return gamedata.GetIntValue(data, "Value")
}
func GetInteractUnlock() int {
data, err := gamedata.GetDataByKey(CFG_PLAYROOM_CONST, "InteractUnlock")
if err != nil {
return 0
}
return gamedata.GetIntValue(data, "Value")
}
func GetRoomDailyTaskUnlock() int {
data, err := gamedata.GetDataByKey(CFG_PLAYROOM_CONST, "RoomDailyTaskUnlock")
if err != nil {
return 0
}
return gamedata.GetIntValue(data, "Value")
}
func GetVisitorItem() int {
data, err := gamedata.GetDataByKey(CFG_PLAYROOM_CONST, "VisitorItem")
if err != nil {
return 0
}
return gamedata.GetIntValue(data, "Value")
}
func GetWorkItem() int {
data, err := gamedata.GetDataByKey(CFG_PLAYROOM_CONST, "WorkItem")
if err != nil {
return 0
}
return gamedata.GetIntValue(data, "Value")
}
func GetWorkChargeId() int {
data, err := gamedata.GetDataByKey(CFG_PLAYROOM_CONST, "WorkChargeId")
if err != nil {
return 0
}
return gamedata.GetIntValue(data, "Value")
}
func GetJackpotNum() int {
data, err := gamedata.GetDataByKey(CFG_PLAYROOM_CONST, "Jackpot")
if err != nil {
return 0
}
return gamedata.GetIntValue(data, "Value")
}
func GetInteract(Id, Type int) (int, []*item.Item, int) {
data, err := gamedata.GetDataByIntKey(CFG_PLAYROOM_MOOD, Id)
if err != nil {
return 0, nil, 0
}
if Type == 1 {
return gamedata.GetIntValue(data, "Type"), gamedata.GetItemList(data, "Cost"), gamedata.GetIntValue(data, "Effect")
}
return gamedata.GetIntValue(data, "Type"), gamedata.GetItemList(data, "Cost2"), gamedata.GetIntValue(data, "Effect")
}
func GetInteractPhysiology(Id int) (int, int) {
data, err := gamedata.GetDataByIntKey(CFG_PLAYROOM_MOOD, Id)
if err != nil {
return 0, 0
}
return gamedata.GetIntValue(data, "PType"), gamedata.GetIntValue(data, "PEffect")
}
func GetInteractPExp(Id int) int {
data, err := gamedata.GetDataByIntKey(CFG_PLAYROOM_MOOD, Id)
if err != nil {
return 0
}
return gamedata.GetIntValue(data, "PExp")
}
func GetInitDecorate() []int {
data, err := gamedata.GetData(CFG_PLAYROOM_DECORATE)
if err != nil {
return []int{}
}
r := make([]int, 0, len(data))
for k, v := range data {
if gamedata.GetIntValue(v, "Init") == 1 {
r = append(r, GoUtil.Int(k))
}
}
return r
}
func GetDecorateList() []int {
data, err := gamedata.GetData(CFG_PLAYROOM_DECORATE)
if err != nil {
return []int{}
}
r := make([]int, 0, len(data))
for k := range data {
r = append(r, GoUtil.Int(k))
}
return r
}
func GetBuyItem(Id int) ([]*item.Item, []*item.Item) {
data, err := gamedata.GetDataByIntKey(CFG_PLAYROOM_MOOD, Id)
if err != nil {
return nil, nil
}
return gamedata.GetItemList(data, "Cost2"), gamedata.GetItemList(data, "Buy")
}
func GetPhysiologyMax(Id int) int {
data, err := gamedata.GetDataByIntKey(CFG_PLAYROOM_PHYSIOLOGY_TYPE, Id)
if err != nil {
return 0
}
return gamedata.GetIntValue(data, "Max")
}
func GetPhysiologyDuration(Id int, Num int) int {
data, err := gamedata.GetData(CFG_PLAYROOM_PHYSIOLOGY)
if err != nil {
return 0
}
for _, v := range data {
Type := gamedata.GetIntValue(v, "Type")
if Type != Id {
continue
}
Min := gamedata.GetIntValue(v, "Min")
Max := gamedata.GetIntValue(v, "Max")
if Num >= Min && Num <= Max {
return gamedata.GetIntValue(v, "Per") * 60
}
}
return 0
}
func GetPhysiologyTypeList() []int {
data, err := gamedata.GetData(CFG_PLAYROOM_PHYSIOLOGY_TYPE)
if err != nil {
return []int{}
}
r := make([]int, 0, len(data))
for k := range data {
r = append(r, GoUtil.Int(k))
}
return r
}
func GetMoodEffect(Id int) (int, int) {
data, err := gamedata.GetDataByIntKey(CFG_PLAYROOM_PHYSIOLOGY_TYPE, Id)
if err != nil {
return 0, 0
}
return gamedata.GetIntValue(data, "MType"), gamedata.GetIntValue(data, "MEffect")
}
func IsStokeCat(Id int) bool {
data, err := gamedata.GetDataByKey(CFG_PLAYROOM_CONST, "StokeCatId")
if err != nil {
return false
}
return gamedata.GetIntValue(data, "Value") == Id
}
func IsTakeCat(Id int) bool {
data, err := gamedata.GetDataByKey(CFG_PLAYROOM_CONST, "TakeCatId")
if err != nil {
return false
}
Ids := gamedata.GetIntSliceValue(data, "Value")
return GoUtil.InArray(Id, Ids)
}
func GetRoomPointInvite() int {
data, err := gamedata.GetDataByKey(CFG_PLAYROOM_CONST, "RoomPointInvite")
if err != nil {
return 0
}
return gamedata.GetIntValue(data, "Value")
}
func GetRoomPointAdd() int {
data, err := gamedata.GetDataByKey(CFG_PLAYROOM_CONST, "RoomPointAdd")
if err != nil {
return 0
}
return gamedata.GetIntValue(data, "Value")
}
func IsPlayCat(Id int) bool {
data, err := gamedata.GetDataByKey(CFG_PLAYROOM_CONST, "PlayCatId")
if err != nil {
return false
}
Ids := gamedata.GetIntSliceValue(data, "Value")
return GoUtil.InArray(Id, Ids)
}
func GetInitAirList() []int {
data, err := gamedata.GetData(CFG_PLAYROOM_AIR)
if err != nil {
return []int{}
}
r := make([]int, 0, len(data))
for k, v := range data {
if gamedata.GetIntValue(v, "Init") == 1 {
r = append(r, GoUtil.Int(k))
}
}
return r
}
func GetAirList() []int {
data, err := gamedata.GetData(CFG_PLAYROOM_AIR)
if err != nil {
return []int{}
}
r := make([]int, 0, len(data))
for k := range data {
r = append(r, GoUtil.Int(k))
}
return r
}
func GetInitDressList() []int {
data, err := gamedata.GetData(CFG_PLAYROOM_DRESS)
if err != nil {
return []int{}
}
r := make([]int, 0, len(data))
for k, v := range data {
if gamedata.GetIntValue(v, "Init") == 1 {
r = append(r, GoUtil.Int(k))
}
}
return r
}
func GetDressList() []int {
data, err := gamedata.GetData(CFG_PLAYROOM_DRESS)
if err != nil {
return []int{}
}
r := make([]int, 0, len(data))
for k := range data {
r = append(r, GoUtil.Int(k))
}
return r
}
func GetDressPart(Id int) int {
data, err := gamedata.GetDataByIntKey(CFG_PLAYROOM_DRESS, Id)
if err != nil {
return 0
}
return gamedata.GetIntValue(data, "IPart")
}
func GetDressName(Id int) string {
data, err := gamedata.GetDataByIntKey(CFG_PLAYROOM_DRESS, Id)
if err != nil {
return ""
}
return gamedata.GetStringValue(data, "Name")
}
func GetUnlockNeed(Type int) int {
data, err := gamedata.GetDataByIntKey(CFG_PLAYROOM_LOCK, Type)
if err != nil {
return 0
}
return gamedata.GetIntValue(data, "Need")
}
func GetUnlockName(Type int) string {
data, err := gamedata.GetDataByIntKey(CFG_PLAYROOM_LOCK, Type)
if err != nil {
return ""
}
return gamedata.GetStringValue(data, "Name")
}
func GetDailyTask(Type int) map[int]string {
data, err := gamedata.GetData(CFG_PLAYROOM_DAILYTASK)
if err != nil {
return nil
}
r := make(map[int]string)
r1 := make([]int, 0, len(data))
r2 := make(map[int]string)
for k, v := range data {
if gamedata.GetIntValue(v, "Type") == Type {
k1 := GoUtil.Int(k)
r[k1] = gamedata.GetStringValue(v, "Task")
r1 = append(r1, k1)
}
}
daily_group_num := 0
switch Type {
case 1:
daily_group_num = GetPlayroomTaskDailyNum1()
case 2:
daily_group_num = GetPlayroomTaskDailyNum2()
}
r3 := GoUtil.RandSliceNum(r1, daily_group_num)
for _, v := range r3 {
r2[v] = r[v]
}
return r2
}
func GetDailyTaskType(Id int) int {
data, err := gamedata.GetDataByIntKey(CFG_PLAYROOM_DAILYTASK, Id)
if err != nil {
return 0
}
return gamedata.GetIntValue(data, "Type")
}
func GetDailyTaskReward(Id int) []*item.Item {
data, err := gamedata.GetDataByIntKey(CFG_PLAYROOM_DAILYTASKREWARD, Id)
if err != nil {
return nil
}
return gamedata.GetItemList(data, "Items")
}
// 获取场景冲刺奖池
func GetTaskJackpotProb() map[int]int {
r := make(map[int]int)
data, err := gamedata.GetData(CFG_PLAYROOM_TASKJACKPOT)
if err != nil {
log.Debug("GetTaskJackpotProb err:%v", err)
return nil
}
for k, v := range data {
Id := GoUtil.Int(k)
r[Id] = gamedata.GetIntValue(v, "Prob")
}
return r
}
// 获取场景冲刺奖励
func GetTaskJackpotReward(Id int) []*item.Item {
data, err := gamedata.GetDataByIntKey(CFG_PLAYROOM_TASKJACKPOT, Id)
if err != nil {
log.Debug("GetTaskJackpotReward err:%v", err)
return nil
}
return gamedata.GetItemList(data, "Items")
}
func GetPhysiologyList(MoodType int) []int {
data, err := gamedata.GetData(CFG_PLAYROOM_PHYSIOLOGY_TYPE)
if err != nil {
return []int{}
}
r := make([]int, 0, len(data))
for k, v := range data {
if gamedata.GetIntValue(v, "MType") == MoodType {
r = append(r, GoUtil.Int(k))
}
}
return r
}
func GetInteractIdBath() []int {
return []int{11, 12, 13}
}
func GetOrderItemByGrade(Grade int) map[int][]int {
data, err := gamedata.GetData(CFG_PLAYROOM_ORDERITEM)
if err != nil {
log.Debug("GetOrderItemByGrade err:%v", err)
return nil
}
r := make(map[int][]int)
for k, v := range data {
GradeStr := gamedata.GetStringValue(v, "Grade")
GradeList := make([]int, 0)
for _, v1 := range strings.Split(GradeStr, ",") {
v2 := GoUtil.Int(v1)
if v2 > 0 {
GradeList = append(GradeList, v2)
}
}
if !GoUtil.InArray(Grade, GradeList) {
continue
}
Id := GoUtil.Int(k)
Type := gamedata.GetIntValue(v, "Type")
if Type == 0 {
continue
}
if _, ok := r[Type]; !ok {
r[Type] = make([]int, 0)
}
if Id == 0 {
log.Debug("GetOrderItemByGrade Id is 0, Type:%v", Type)
continue
}
r[Type] = append(r[Type], Id)
}
return r
}
func GetOrderItemList() []int {
data, err := gamedata.GetData(CFG_PLAYROOM_ORDERITEM)
if err != nil {
log.Debug("GetOrderItemList err:%v", err)
return nil
}
r := make([]int, 0, len(data))
for k := range data {
Id := GoUtil.Int(k)
if Id == 0 {
log.Debug("GetOrderItemList Id is 0")
continue
}
r = append(r, Id)
}
return r
}
func GetPetOrderItemExpByList(ItemList []*item.Item) int {
r := 0
for _, v := range ItemList {
data, err := gamedata.GetDataByIntKey(CFG_PLAYROOM_ORDERITEM, v.Id)
if err != nil {
// log.Debug("GetPetOrderItemExpByList err:%v", err)
continue
}
Star := gamedata.GetIntValue(data, "Star")
r += Star * v.Num
}
return r
}
func GetShopItemAdNum(Id int) int {
data, err := gamedata.GetData(CFG_PLAYROOM_SHOP)
if err != nil {
log.Debug("GetShopItemAdNum err:%v", err)
return 0
}
for _, v := range data {
if gamedata.GetIntValue(v, "ItemId") == Id {
return gamedata.GetIntValue(v, "Dailystorage")
}
}
return 0
}
func GetDecoInfo(Id int) (int, string) {
data, err := gamedata.GetDataByIntKey(CFG_PLAYROOM_DECORATE, Id)
if err != nil {
log.Debug("GetDecoInfo err:%v", err)
return 0, ""
}
Type := gamedata.GetIntValue(data, "TypeEnum")
Name := gamedata.GetStringValue(data, "Chinese")
return Type, Name
}
func GetDailyTaskListById(Id int) []int {
data, err := gamedata.GetData(CFG_PLAYROOM_DAILYTASK)
if err != nil {
log.Debug("GetDailyTaskListById err:%v", err)
return nil
}
Type := GetDailyTaskType(Id)
r := make([]int, 0, len(data))
for k, v := range data {
if gamedata.GetIntValue(v, "Type") == Type {
r = append(r, GoUtil.Int(k))
}
}
return r
}
func GetItemAddPhysiology(Item map[int]int) int {
r := 0
data, err := gamedata.GetData(CFG_PLAYROOM_MOOD)
if err != nil {
log.Debug("GetItemAddPhysiology err")
return 0
}
for _, v := range data {
costItem := gamedata.GetItemList(v, "Cost2")
if len(costItem) < 1 {
continue
}
if costItem[0].Id == 0 {
continue
}
if val, ok := Item[costItem[0].Id]; ok {
r += gamedata.GetIntValue(v, "PEffect") * val
}
}
return r
}
func GetPlayroomTaskDailyNum1() int {
data, err := gamedata.GetDataByKey(CFG_PLAYROOM_CONST, "dailytask_groupnum1")
if err != nil {
log.Debug("GetPlayroomTaskDailyNum1 err:%v", err)
return 0
}
return gamedata.GetIntValue(data, "Value")
}
func GetPlayroomTaskDailyNum2() int {
data, err := gamedata.GetDataByKey(CFG_PLAYROOM_CONST, "dailytask_groupnum2")
if err != nil {
log.Debug("GetPlayroomTaskDailyNum2 err:%v", err)
return 0
}
return gamedata.GetIntValue(data, "Value")
}

View File

@ -0,0 +1,78 @@
package raceCfg
import (
"server/game/mod/item"
"server/gamedata"
)
const (
CFG_RACE_TEMPLATE = "RaceTemplate"
CFG_RACE_PASS = "RacePass"
)
func init() {
gamedata.InitCfg(CFG_RACE_TEMPLATE)
gamedata.InitCfg(CFG_RACE_PASS)
}
func GetRaceNum(Pass int) int {
data, err := gamedata.GetDataByIntKey(CFG_RACE_PASS, Pass)
if err != nil {
return 0
}
return gamedata.GetIntValue(data, "Num")
}
func GetRaceNeed(Pass int) int {
data, err := gamedata.GetDataByIntKey(CFG_RACE_PASS, Pass)
if err != nil {
return 0
}
return gamedata.GetIntValue(data, "Need")
}
func GetMaxPass(Id int) int {
data, err := gamedata.GetDataByIntKey(CFG_RACE_TEMPLATE, Id)
if err != nil {
return 0
}
return gamedata.GetIntValue(data, "PassNum")
}
func GetActivityItemId(Id int) int {
data, err := gamedata.GetDataByIntKey(CFG_RACE_TEMPLATE, Id)
if err != nil {
return 0
}
return gamedata.GetIntValue(data, "ItemId")
}
func GetCD(Id int) int {
data, err := gamedata.GetDataByIntKey(CFG_RACE_PASS, Id)
if err != nil {
return 0
}
return gamedata.GetIntValue(data, "Cd")
}
func GetCoin(Id int) int {
data, err := gamedata.GetDataByIntKey(CFG_RACE_TEMPLATE, Id)
if err != nil {
return 0
}
return gamedata.GetIntValue(data, "ItemId")
}
func GetExtraReward(Id int) []*item.Item {
data, err := gamedata.GetDataByIntKey(CFG_RACE_PASS, Id)
if err != nil {
return nil
}
return gamedata.GetItemList(data, "ExtraReward")
}
func GetReward(Id int) []*item.Item {
data, err := gamedata.GetDataByIntKey(CFG_RACE_PASS, Id)
if err != nil {
return nil
}
return gamedata.GetItemList(data, "Reward")
}

View File

@ -0,0 +1,52 @@
package randnameCfg
import (
GoUtil "server/game_util"
"server/gamedata"
"strconv"
)
const (
CFG_RAND_NAME = "RandName"
)
func init() {
gamedata.InitCfg(CFG_RAND_NAME)
}
func GetRandName() string {
data, _ := gamedata.GetData(CFG_RAND_NAME)
Id := GoUtil.RandNum(1, len(data))
v, err := gamedata.GetDataByIntKey(CFG_RAND_NAME, Id)
if err != nil {
return "Lily"
}
return gamedata.GetStringValue(v, "EnName")
}
func GetRandNames(n int) []string {
rs := make([]string, 0)
data, err := gamedata.GetData(CFG_RAND_NAME)
if err != nil {
for i := 0; i < n; i++ {
id := strconv.Itoa(i + 1)
rs[i] = "Lily" + id
}
}
all := make([]int, len(data))
index := 0
for k := range data {
ik := GoUtil.Int(k)
all[index] = ik
index++
}
ids := GoUtil.RandSliceNumNonAdjacent(all, n)
for _, v := range ids {
v, err := gamedata.GetDataByIntKey(CFG_RAND_NAME, v)
if err != nil {
rs = append(rs, "Lucy")
}
rs = append(rs, gamedata.GetStringValue(v, "EnName"))
}
return rs
}

View File

@ -1,32 +1,49 @@
{
"AppID": 0,
"LogLevel": "debug",
"LogPath": "",
"TCPAddr": ":3565",
"WSAddr": ":3566",
"MySqlAddr": "127.0.0.1",
"LogPath": "./log",
"TCPAddr": ":3601",
"WSAddr": ":3567",
"RPCAddr": ":50051",
"MySqlAddr": "mysql",
"MySqlPort": "3306",
"MySqlUsr": "root",
"MySqlPwd": "root",
"MySqlPwd": "IOagNEq3C84c-20CmHEin5iODVc=",
"MaxConnNum": 20000,
"DbName": "Merge_Pet",
"DbName": "merge_pet_1",
"HttpPort": ":8081",
"RemoteAddr":"host.docker.internal:9001",
"AppPath": "./app",
"TELOGDIR" : "./log/teLog/",
"GameName": "pet_home_local",
"GameName": "Merge_Pet",
"GameID": 1,
"ServerType":"node",
"ServerID": 1,
"ServerOpenTime": "2018-01-01 00:00:00",
"ServerName": "Merge_Pet",
"ServerStatus" : 1,
"ServerCenter" : 1,
"ServerCenter" : 0,
"GameConfPath": "/Github/pet_home_server/src/server/gamedata/config/",
"ListenAddr":":9002",
"CenterAddr": "127.0.0.1:7000",
"RemoteAddr":"127.0.0.1:9002",
"RedisAddr":"127.0.0.1",
"RedisPort" :"6379",
"RedisAddr":"127.0.0.1",
"RedisPort" :"6379",
"RedisPwd" :"",
"ListenAddr": ":9001",
"CenterAddr": ":3560"
"RedisWriteAddr":"redis:6379",
"RedisReadAddrs":"redis:6379",
"RedisMasterName":"mymaster",
"RedisConnType":"Direct",
"GoogleVerify":false,
"Partition":3,
"KafkaHost":"kafka-server",
"CountryCode":"004",
"KafkaPort":"9092",
"Version":"1.0.0",
"IdVerify":false
}

View File

@ -4,8 +4,9 @@ import (
"math"
"server/game/mod/item"
"server/gamedata"
"server/pkg/github.com/name5566/leaf/log"
"strconv"
"gitea.bywaystudios.com/pet_home/leaf/log"
)
const (
@ -29,18 +30,18 @@ func GetSevenLoginReward() []*gamedata.SevenLoginRewardData {
var result []*gamedata.SevenLoginRewardData
for k, v := range data {
Id, _ := strconv.Atoi(k)
Diamond := gamedata.GetIntValue(v, "Diamond")
Energy := gamedata.GetFloatValue(v, "Energy")
RewardNum := gamedata.GetIntValue(v, "RewardNum")
result = append(result, &gamedata.SevenLoginRewardData{
Id: Id,
Diamond: Diamond,
Energy: Energy,
RewardNum: RewardNum,
})
}
return result
}
func GetSevenLoginMonthReward() []*gamedata.SevenLoginRewardData {
func GetSevenLoginMonthReward(nowMonth int) []*gamedata.SevenLoginRewardData {
data, err := gamedata.GetData(CFG_SEVEN_LOGIN_MONTH)
if err != nil {
log.Debug("GetSevenLoginReward err:%v", err)
@ -48,19 +49,23 @@ func GetSevenLoginMonthReward() []*gamedata.SevenLoginRewardData {
}
var result []*gamedata.SevenLoginRewardData
for k, v := range data {
month := gamedata.GetIntValue(v, "Month")
if month != nowMonth {
continue
}
Id, _ := strconv.Atoi(k)
Diamond := gamedata.GetIntValue(v, "Diamond")
Energy := gamedata.GetFloatValue(v, "Energy")
RewardNum := gamedata.GetIntValue(v, "RewardNum")
result = append(result, &gamedata.SevenLoginRewardData{
Id: Id,
Diamond: Diamond,
Energy: Energy,
RewardNum: RewardNum,
})
}
return result
}
func GetSevenLoginJackpot(IsMonth int) []*gamedata.SevenLoginJackpotData {
func GetSevenLoginJackpot(isMonth, level int) []*gamedata.SevenLoginJackpotData {
data, err := gamedata.GetData(CFG_SEVEN_LOGIN_JACKPOT)
if err != nil {
log.Debug("GetSevenLoginJackpot err:%v", err)
@ -69,20 +74,24 @@ func GetSevenLoginJackpot(IsMonth int) []*gamedata.SevenLoginJackpotData {
var result []*gamedata.SevenLoginJackpotData
for k, v := range data {
Id, _ := strconv.Atoi(k)
Diamond := gamedata.GetIntValue(v, "Diamond")
Energy := gamedata.GetFloatValue(v, "Energy")
Type := gamedata.GetIntValue(v, "Type")
Month := gamedata.GetIntValue(v, "Month")
if IsMonth != Month && IsMonth != 0 {
Level := gamedata.GetIntValue(v, "Level")
if isMonth != Month && isMonth != 0 {
continue
}
if level < Level && level != 0 {
continue
}
ItemMap := gamedata.GetValue(v, "Item")
Items := item.ParseItem(ItemMap)
result = append(result, &gamedata.SevenLoginJackpotData{
Id: Id,
Diamond: Diamond,
Items: Items,
Type: Type,
Month: Month,
Id: Id,
Energy: Energy,
Items: Items,
Type: Type,
Month: Month,
})
}
return result

View File

@ -18,7 +18,7 @@ func GetStartChessList() []int {
var ChessList []int
for _, v := range data {
v1 := v.(map[string]interface{})
ChessList = append(ChessList, gamedata.ParseInt(v1["MergeId"]))
ChessList = append(ChessList, gamedata.GetIntValue(v1, "MergeId"))
}
return ChessList
}

View File

@ -1,85 +0,0 @@
package userCfg
import (
"server/game/mod/item"
"server/gamedata"
"server/pkg/github.com/name5566/leaf/log"
)
var CFG_NAME = "UserData"
func init() {
gamedata.InitCfg(CFG_NAME)
}
// 获取用户能量倍数
func GetEnergyMulByLv(lv int) int {
data, err := gamedata.GetDataByIntKey(CFG_NAME, lv)
if err != nil {
log.Debug("UserDataCfg GetEnergyMulByLv lv:%v not found", lv)
return 0
}
return gamedata.GetIntValue(data, "EnergyMul")
}
// 获取七天登录加成
func GetSevenloginAdd(Lv int) int {
data, err := gamedata.GetDataByIntKey(CFG_NAME, Lv)
if err != nil {
log.Debug("UserDataCfg GetSevenloginAdd lv:%v not found", Lv)
return 0
}
return gamedata.GetIntValue(data, "SevenLogin")
}
// 获取订单系数
func GetOrderNByLv(lv int) (int, error) {
data, err := gamedata.GetDataByIntKey(CFG_NAME, lv)
if err != nil {
log.Debug("UserDataCfg GetOrderNByLv lv:%v not found", lv)
return 0, err
}
return gamedata.GetIntValue(data, "OrderN"), nil
}
// 获取升级经验
func GetLevUpExp(lv int) int {
data, err := gamedata.GetDataByIntKey(CFG_NAME, lv)
if err != nil {
log.Debug("UserDataCfg GetLevUpExp lv:%v not found", lv)
return 0
}
return gamedata.GetIntValue(data, "Exp")
}
// 获取能量回复时间
func GetRecover(lv int) int {
data, err := gamedata.GetDataByIntKey(CFG_NAME, lv)
if err != nil {
log.Debug("UserDataCfg GetRecover lv:%v not found", lv)
return 0
}
return gamedata.GetIntValue(data, "Recover")
}
// 获取解锁背包数量
func GetUnlockPack(lv int) int {
data, err := gamedata.GetDataByIntKey(CFG_NAME, lv)
if err != nil {
log.Debug("UserDataCfg GetUnlockPack lv:%v not found", lv)
return 0
}
return gamedata.GetIntValue(data, "UnlockPack")
}
// 获取升级奖励
func GetLevUpReward(lv int) []*item.Item {
data, err := gamedata.GetDataByIntKey(CFG_NAME, lv)
if err != nil {
log.Debug("UserDataCfg GetLevUpReward lv:%v not found", lv)
return nil
}
itemMap := gamedata.GetValue(data, "Item")
itemList := item.ParseItem(itemMap)
return itemList
}

View File

@ -0,0 +1,169 @@
package userCfg
import (
"server/game/mod/item"
"server/gamedata"
"gitea.bywaystudios.com/pet_home/leaf/log"
)
const (
CFG_NAME = "UserData"
CFG_NANE_CONST = "UserDataConst"
)
func init() {
gamedata.InitCfg(CFG_NAME)
gamedata.InitCfg(CFG_NANE_CONST)
}
// 获取用户能量倍数
func GetEnergyMulByLv(lv int) int {
data, err := gamedata.GetDataByIntKey(CFG_NAME, lv)
if err != nil {
log.Debug("UserDataCfg GetEnergyMulByLv lv:%v not found", lv)
return 0
}
return gamedata.GetIntValue(data, "EnergyMul")
}
func GetEnergyMax(Lv int) int {
data, err := gamedata.GetDataByIntKey(CFG_NAME, Lv)
if err != nil {
log.Debug("UserDataCfg GetEnergyMax lv:%v not found", Lv)
return 0
}
return gamedata.GetIntValue(data, "MaxEnergy")
}
// 获取七天登录加成
func GetSevenloginAdd(Lv int) int {
data, err := gamedata.GetDataByIntKey(CFG_NAME, Lv)
if err != nil {
log.Debug("UserDataCfg GetSevenloginAdd lv:%v not found", Lv)
return 0
}
return gamedata.GetIntValue(data, "SevenLogin")
}
// 获取订单系数
func GetOrderNByLv(lv int) (int, error) {
data, err := gamedata.GetDataByIntKey(CFG_NAME, lv)
if err != nil {
log.Debug("UserDataCfg GetOrderNByLv lv:%v not found", lv)
return 0, err
}
return gamedata.GetIntValue(data, "OrderN"), nil
}
// 获取升级经验
func GetLevUpExp(lv int) (int, int) {
data, err := gamedata.GetDataByIntKey(CFG_NAME, lv)
if err != nil {
log.Debug("UserDataCfg GetLevUpExp lv:%v not found", lv)
return 0, 0
}
return gamedata.GetIntValue(data, "Exp"), gamedata.GetIntValue(data, "PExp")
}
func GetNewLevUpExp(lv int, Exp int, PetExt int) (int, int) {
ExpLv := lv
PetLv := lv
for {
data1, err := gamedata.GetDataByIntKey(CFG_NAME, ExpLv)
if err != nil {
break
}
ExpNeed := gamedata.GetIntValue(data1, "Exp")
if Exp >= ExpNeed {
Exp -= ExpNeed
ExpLv++
} else {
break
}
}
for {
data2, err := gamedata.GetDataByIntKey(CFG_NAME, PetLv)
if err != nil {
break
}
PetNeed := gamedata.GetIntValue(data2, "PExp")
if PetExt >= PetNeed {
PetExt -= PetNeed
PetLv++
} else {
break
}
}
return ExpLv, PetLv
}
// 获取能量回复时间
func GetRecover(lv int) int {
data, err := gamedata.GetDataByIntKey(CFG_NAME, lv)
if err != nil {
log.Debug("UserDataCfg GetRecover lv:%v not found", lv)
return 0
}
return gamedata.GetIntValue(data, "Recover")
}
// 获取解锁背包数量
func GetUnlockPack(lv int) int {
data, err := gamedata.GetDataByIntKey(CFG_NAME, lv)
if err != nil {
log.Debug("UserDataCfg GetUnlockPack lv:%v not found", lv)
return 0
}
return gamedata.GetIntValue(data, "UnlockPack")
}
// 获取升级奖励
func GetLevUpReward(lv int) []*item.Item {
data, err := gamedata.GetDataByIntKey(CFG_NAME, lv)
if err != nil {
log.Debug("UserDataCfg GetLevUpReward lv:%v not found", lv)
return nil
}
itemMap := gamedata.GetValue(data, "Item")
itemList := item.ParseItem(itemMap)
return itemList
}
func GetUnlock(Lv int) string {
data, err := gamedata.GetDataByIntKey(CFG_NAME, Lv)
if err != nil {
log.Debug("UserDataCfg GetUnlock lv:%v not found", Lv)
return ""
}
s1 := gamedata.GetStringValue(data, "Unlock_1")
s2 := gamedata.GetStringValue(data, "Unlock_2")
return s1 + "," + s2
}
func GetInitEnergy() int {
data, err := gamedata.GetDataByKey(CFG_NANE_CONST, "Energy")
if err != nil {
log.Debug("UserDataCfg GetInitEnergy not found")
return 0
}
return gamedata.GetIntValue(data, "Value")
}
func GetInitDiamond() int {
data, err := gamedata.GetDataByKey(CFG_NANE_CONST, "Diamond")
if err != nil {
log.Debug("UserDataCfg GetInitDiamond not found")
return 0
}
return gamedata.GetIntValue(data, "Value")
}
func GetInitStar() int {
data, err := gamedata.GetDataByKey(CFG_NANE_CONST, "Star")
if err != nil {
log.Debug("UserDataCfg GetInitStar not found")
return 0
}
return gamedata.GetIntValue(data, "Value")
}

View File

@ -1,21 +0,0 @@
Copyright (C) 2012 Rob Figueiredo
All Rights Reserved.
MIT LICENSE
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.

View File

@ -1,125 +0,0 @@
[![GoDoc](http://godoc.org/github.com/robfig/cron?status.png)](http://godoc.org/github.com/robfig/cron)
[![Build Status](https://travis-ci.org/robfig/cron.svg?branch=master)](https://travis-ci.org/robfig/cron)
# cron
Cron V3 has been released!
To download the specific tagged release, run:
go get github.com/robfig/cron/v3@v3.0.0
Import it in your program as:
import "github.com/robfig/cron/v3"
It requires Go 1.11 or later due to usage of Go Modules.
Refer to the documentation here:
http://godoc.org/github.com/robfig/cron
The rest of this document describes the the advances in v3 and a list of
breaking changes for users that wish to upgrade from an earlier version.
## Upgrading to v3 (June 2019)
cron v3 is a major upgrade to the library that addresses all outstanding bugs,
feature requests, and rough edges. It is based on a merge of master which
contains various fixes to issues found over the years and the v2 branch which
contains some backwards-incompatible features like the ability to remove cron
jobs. In addition, v3 adds support for Go Modules, cleans up rough edges like
the timezone support, and fixes a number of bugs.
New features:
- Support for Go modules. Callers must now import this library as
`github.com/robfig/cron/v3`, instead of `gopkg.in/...`
- Fixed bugs:
- 0f01e6b parser: fix combining of Dow and Dom (#70)
- dbf3220 adjust times when rolling the clock forward to handle non-existent midnight (#157)
- eeecf15 spec_test.go: ensure an error is returned on 0 increment (#144)
- 70971dc cron.Entries(): update request for snapshot to include a reply channel (#97)
- 1cba5e6 cron: fix: removing a job causes the next scheduled job to run too late (#206)
- Standard cron spec parsing by default (first field is "minute"), with an easy
way to opt into the seconds field (quartz-compatible). Although, note that the
year field (optional in Quartz) is not supported.
- Extensible, key/value logging via an interface that complies with
the https://github.com/go-logr/logr project.
- The new Chain & JobWrapper types allow you to install "interceptors" to add
cross-cutting behavior like the following:
- Recover any panics from jobs
- Delay a job's execution if the previous run hasn't completed yet
- Skip a job's execution if the previous run hasn't completed yet
- Log each job's invocations
- Notification when jobs are completed
It is backwards incompatible with both v1 and v2. These updates are required:
- The v1 branch accepted an optional seconds field at the beginning of the cron
spec. This is non-standard and has led to a lot of confusion. The new default
parser conforms to the standard as described by [the Cron wikipedia page].
UPDATING: To retain the old behavior, construct your Cron with a custom
parser:
// Seconds field, required
cron.New(cron.WithSeconds())
// Seconds field, optional
cron.New(
cron.WithParser(
cron.SecondOptional | cron.Hour | cron.Dom | cron.Month | cron.Dow | cron.Descriptor))
- The Cron type now accepts functional options on construction rather than the
previous ad-hoc behavior modification mechanisms (setting a field, calling a setter).
UPDATING: Code that sets Cron.ErrorLogger or calls Cron.SetLocation must be
updated to provide those values on construction.
- CRON_TZ is now the recommended way to specify the timezone of a single
schedule, which is sanctioned by the specification. The legacy "TZ=" prefix
will continue to be supported since it is unambiguous and easy to do so.
UPDATING: No update is required.
- By default, cron will no longer recover panics in jobs that it runs.
Recovering can be surprising (see issue #192) and seems to be at odds with
typical behavior of libraries. Relatedly, the `cron.WithPanicLogger` option
has been removed to accommodate the more general JobWrapper type.
UPDATING: To opt into panic recovery and configure the panic logger:
cron.New(cron.WithChain(
cron.Recover(logger), // or use cron.DefaultLogger
))
- In adding support for https://github.com/go-logr/logr, `cron.WithVerboseLogger` was
removed, since it is duplicative with the leveled logging.
UPDATING: Callers should use `WithLogger` and specify a logger that does not
discard `Info` logs. For convenience, one is provided that wraps `*log.Logger`:
cron.New(
cron.WithLogger(cron.VerbosePrintfLogger(logger)))
### Background - Cron spec format
There are two cron spec formats in common usage:
- The "standard" cron format, described on [the Cron wikipedia page] and used by
the cron Linux system utility.
- The cron format used by [the Quartz Scheduler], commonly used for scheduled
jobs in Java software
[the Cron wikipedia page]: https://en.wikipedia.org/wiki/Cron
[the Quartz Scheduler]: http://www.quartz-scheduler.org/documentation/quartz-2.x/tutorials/crontrigger.html
The original version of this package included an optional "seconds" field, which
made it incompatible with both of these formats. Now, the "standard" format is
the default format accepted, and the Quartz format is opt-in.

View File

@ -1,92 +0,0 @@
package cron
import (
"fmt"
"runtime"
"sync"
"time"
)
// JobWrapper decorates the given Job with some behavior.
type JobWrapper func(Job) Job
// Chain is a sequence of JobWrappers that decorates submitted jobs with
// cross-cutting behaviors like logging or synchronization.
type Chain struct {
wrappers []JobWrapper
}
// NewChain returns a Chain consisting of the given JobWrappers.
func NewChain(c ...JobWrapper) Chain {
return Chain{c}
}
// Then decorates the given job with all JobWrappers in the chain.
//
// This:
// NewChain(m1, m2, m3).Then(job)
// is equivalent to:
// m1(m2(m3(job)))
func (c Chain) Then(j Job) Job {
for i := range c.wrappers {
j = c.wrappers[len(c.wrappers)-i-1](j)
}
return j
}
// Recover panics in wrapped jobs and log them with the provided logger.
func Recover(logger Logger) JobWrapper {
return func(j Job) Job {
return FuncJob(func() {
defer func() {
if r := recover(); r != nil {
const size = 64 << 10
buf := make([]byte, size)
buf = buf[:runtime.Stack(buf, false)]
err, ok := r.(error)
if !ok {
err = fmt.Errorf("%v", r)
}
logger.Error(err, "panic", "stack", "...\n"+string(buf))
}
}()
j.Run()
})
}
}
// DelayIfStillRunning serializes jobs, delaying subsequent runs until the
// previous one is complete. Jobs running after a delay of more than a minute
// have the delay logged at Info.
func DelayIfStillRunning(logger Logger) JobWrapper {
return func(j Job) Job {
var mu sync.Mutex
return FuncJob(func() {
start := time.Now()
mu.Lock()
defer mu.Unlock()
if dur := time.Since(start); dur > time.Minute {
logger.Info("delay", "duration", dur)
}
j.Run()
})
}
}
// SkipIfStillRunning skips an invocation of the Job if a previous invocation is
// still running. It logs skips to the given logger at Info level.
func SkipIfStillRunning(logger Logger) JobWrapper {
var ch = make(chan struct{}, 1)
ch <- struct{}{}
return func(j Job) Job {
return FuncJob(func() {
select {
case v := <-ch:
j.Run()
ch <- v
default:
logger.Info("skip")
}
})
}
}

View File

@ -1,221 +0,0 @@
package cron
import (
"io/ioutil"
"log"
"reflect"
"sync"
"testing"
"time"
)
func appendingJob(slice *[]int, value int) Job {
var m sync.Mutex
return FuncJob(func() {
m.Lock()
*slice = append(*slice, value)
m.Unlock()
})
}
func appendingWrapper(slice *[]int, value int) JobWrapper {
return func(j Job) Job {
return FuncJob(func() {
appendingJob(slice, value).Run()
j.Run()
})
}
}
func TestChain(t *testing.T) {
var nums []int
var (
append1 = appendingWrapper(&nums, 1)
append2 = appendingWrapper(&nums, 2)
append3 = appendingWrapper(&nums, 3)
append4 = appendingJob(&nums, 4)
)
NewChain(append1, append2, append3).Then(append4).Run()
if !reflect.DeepEqual(nums, []int{1, 2, 3, 4}) {
t.Error("unexpected order of calls:", nums)
}
}
func TestChainRecover(t *testing.T) {
panickingJob := FuncJob(func() {
panic("panickingJob panics")
})
t.Run("panic exits job by default", func(t *testing.T) {
defer func() {
if err := recover(); err == nil {
t.Errorf("panic expected, but none received")
}
}()
NewChain().Then(panickingJob).
Run()
})
t.Run("Recovering JobWrapper recovers", func(t *testing.T) {
NewChain(Recover(PrintfLogger(log.New(ioutil.Discard, "", 0)))).
Then(panickingJob).
Run()
})
t.Run("composed with the *IfStillRunning wrappers", func(t *testing.T) {
NewChain(Recover(PrintfLogger(log.New(ioutil.Discard, "", 0)))).
Then(panickingJob).
Run()
})
}
type countJob struct {
m sync.Mutex
started int
done int
delay time.Duration
}
func (j *countJob) Run() {
j.m.Lock()
j.started++
j.m.Unlock()
time.Sleep(j.delay)
j.m.Lock()
j.done++
j.m.Unlock()
}
func (j *countJob) Started() int {
defer j.m.Unlock()
j.m.Lock()
return j.started
}
func (j *countJob) Done() int {
defer j.m.Unlock()
j.m.Lock()
return j.done
}
func TestChainDelayIfStillRunning(t *testing.T) {
t.Run("runs immediately", func(t *testing.T) {
var j countJob
wrappedJob := NewChain(DelayIfStillRunning(DiscardLogger)).Then(&j)
go wrappedJob.Run()
time.Sleep(2 * time.Millisecond) // Give the job 2ms to complete.
if c := j.Done(); c != 1 {
t.Errorf("expected job run once, immediately, got %d", c)
}
})
t.Run("second run immediate if first done", func(t *testing.T) {
var j countJob
wrappedJob := NewChain(DelayIfStillRunning(DiscardLogger)).Then(&j)
go func() {
go wrappedJob.Run()
time.Sleep(time.Millisecond)
go wrappedJob.Run()
}()
time.Sleep(3 * time.Millisecond) // Give both jobs 3ms to complete.
if c := j.Done(); c != 2 {
t.Errorf("expected job run twice, immediately, got %d", c)
}
})
t.Run("second run delayed if first not done", func(t *testing.T) {
var j countJob
j.delay = 10 * time.Millisecond
wrappedJob := NewChain(DelayIfStillRunning(DiscardLogger)).Then(&j)
go func() {
go wrappedJob.Run()
time.Sleep(time.Millisecond)
go wrappedJob.Run()
}()
// After 5ms, the first job is still in progress, and the second job was
// run but should be waiting for it to finish.
time.Sleep(5 * time.Millisecond)
started, done := j.Started(), j.Done()
if started != 1 || done != 0 {
t.Error("expected first job started, but not finished, got", started, done)
}
// Verify that the second job completes.
time.Sleep(25 * time.Millisecond)
started, done = j.Started(), j.Done()
if started != 2 || done != 2 {
t.Error("expected both jobs done, got", started, done)
}
})
}
func TestChainSkipIfStillRunning(t *testing.T) {
t.Run("runs immediately", func(t *testing.T) {
var j countJob
wrappedJob := NewChain(SkipIfStillRunning(DiscardLogger)).Then(&j)
go wrappedJob.Run()
time.Sleep(2 * time.Millisecond) // Give the job 2ms to complete.
if c := j.Done(); c != 1 {
t.Errorf("expected job run once, immediately, got %d", c)
}
})
t.Run("second run immediate if first done", func(t *testing.T) {
var j countJob
wrappedJob := NewChain(SkipIfStillRunning(DiscardLogger)).Then(&j)
go func() {
go wrappedJob.Run()
time.Sleep(time.Millisecond)
go wrappedJob.Run()
}()
time.Sleep(3 * time.Millisecond) // Give both jobs 3ms to complete.
if c := j.Done(); c != 2 {
t.Errorf("expected job run twice, immediately, got %d", c)
}
})
t.Run("second run skipped if first not done", func(t *testing.T) {
var j countJob
j.delay = 10 * time.Millisecond
wrappedJob := NewChain(SkipIfStillRunning(DiscardLogger)).Then(&j)
go func() {
go wrappedJob.Run()
time.Sleep(time.Millisecond)
go wrappedJob.Run()
}()
// After 5ms, the first job is still in progress, and the second job was
// aleady skipped.
time.Sleep(5 * time.Millisecond)
started, done := j.Started(), j.Done()
if started != 1 || done != 0 {
t.Error("expected first job started, but not finished, got", started, done)
}
// Verify that the first job completes and second does not run.
time.Sleep(25 * time.Millisecond)
started, done = j.Started(), j.Done()
if started != 1 || done != 1 {
t.Error("expected second job skipped, got", started, done)
}
})
t.Run("skip 10 jobs on rapid fire", func(t *testing.T) {
var j countJob
j.delay = 10 * time.Millisecond
wrappedJob := NewChain(SkipIfStillRunning(DiscardLogger)).Then(&j)
for i := 0; i < 11; i++ {
go wrappedJob.Run()
}
time.Sleep(200 * time.Millisecond)
done := j.Done()
if done != 1 {
t.Error("expected 1 jobs executed, 10 jobs dropped, got", done)
}
})
}

View File

@ -1,27 +0,0 @@
package cron
import "time"
// ConstantDelaySchedule represents a simple recurring duty cycle, e.g. "Every 5 minutes".
// It does not support jobs more frequent than once a second.
type ConstantDelaySchedule struct {
Delay time.Duration
}
// Every returns a crontab Schedule that activates once every duration.
// Delays of less than a second are not supported (will round up to 1 second).
// Any fields less than a Second are truncated.
func Every(duration time.Duration) ConstantDelaySchedule {
if duration < time.Second {
duration = time.Second
}
return ConstantDelaySchedule{
Delay: duration - time.Duration(duration.Nanoseconds())%time.Second,
}
}
// Next returns the next time this should be run.
// This rounds so that the next activation time will be on the second.
func (schedule ConstantDelaySchedule) Next(t time.Time) time.Time {
return t.Add(schedule.Delay - time.Duration(t.Nanosecond())*time.Nanosecond)
}

View File

@ -1,54 +0,0 @@
package cron
import (
"testing"
"time"
)
func TestConstantDelayNext(t *testing.T) {
tests := []struct {
time string
delay time.Duration
expected string
}{
// Simple cases
{"Mon Jul 9 14:45 2012", 15*time.Minute + 50*time.Nanosecond, "Mon Jul 9 15:00 2012"},
{"Mon Jul 9 14:59 2012", 15 * time.Minute, "Mon Jul 9 15:14 2012"},
{"Mon Jul 9 14:59:59 2012", 15 * time.Minute, "Mon Jul 9 15:14:59 2012"},
// Wrap around hours
{"Mon Jul 9 15:45 2012", 35 * time.Minute, "Mon Jul 9 16:20 2012"},
// Wrap around days
{"Mon Jul 9 23:46 2012", 14 * time.Minute, "Tue Jul 10 00:00 2012"},
{"Mon Jul 9 23:45 2012", 35 * time.Minute, "Tue Jul 10 00:20 2012"},
{"Mon Jul 9 23:35:51 2012", 44*time.Minute + 24*time.Second, "Tue Jul 10 00:20:15 2012"},
{"Mon Jul 9 23:35:51 2012", 25*time.Hour + 44*time.Minute + 24*time.Second, "Thu Jul 11 01:20:15 2012"},
// Wrap around months
{"Mon Jul 9 23:35 2012", 91*24*time.Hour + 25*time.Minute, "Thu Oct 9 00:00 2012"},
// Wrap around minute, hour, day, month, and year
{"Mon Dec 31 23:59:45 2012", 15 * time.Second, "Tue Jan 1 00:00:00 2013"},
// Round to nearest second on the delay
{"Mon Jul 9 14:45 2012", 15*time.Minute + 50*time.Nanosecond, "Mon Jul 9 15:00 2012"},
// Round up to 1 second if the duration is less.
{"Mon Jul 9 14:45:00 2012", 15 * time.Millisecond, "Mon Jul 9 14:45:01 2012"},
// Round to nearest second when calculating the next time.
{"Mon Jul 9 14:45:00.005 2012", 15 * time.Minute, "Mon Jul 9 15:00 2012"},
// Round to nearest second for both.
{"Mon Jul 9 14:45:00.005 2012", 15*time.Minute + 50*time.Nanosecond, "Mon Jul 9 15:00 2012"},
}
for _, c := range tests {
actual := Every(c.delay).Next(getTime(c.time))
expected := getTime(c.expected)
if actual != expected {
t.Errorf("%s, \"%s\": (expected) %v != %v (actual)", c.time, c.delay, expected, actual)
}
}
}

View File

@ -1,350 +0,0 @@
package cron
import (
"context"
"sort"
"sync"
"time"
)
// Cron keeps track of any number of entries, invoking the associated func as
// specified by the schedule. It may be started, stopped, and the entries may
// be inspected while running.
type Cron struct {
entries []*Entry
chain Chain
stop chan struct{}
add chan *Entry
remove chan EntryID
snapshot chan chan []Entry
running bool
logger Logger
runningMu sync.Mutex
location *time.Location
parser Parser
nextID EntryID
jobWaiter sync.WaitGroup
}
// Job is an interface for submitted cron jobs.
type Job interface {
Run()
}
// Schedule describes a job's duty cycle.
type Schedule interface {
// Next returns the next activation time, later than the given time.
// Next is invoked initially, and then each time the job is run.
Next(time.Time) time.Time
}
// EntryID identifies an entry within a Cron instance
type EntryID int
// Entry consists of a schedule and the func to execute on that schedule.
type Entry struct {
// ID is the cron-assigned ID of this entry, which may be used to look up a
// snapshot or remove it.
ID EntryID
// Schedule on which this job should be run.
Schedule Schedule
// Next time the job will run, or the zero time if Cron has not been
// started or this entry's schedule is unsatisfiable
Next time.Time
// Prev is the last time this job was run, or the zero time if never.
Prev time.Time
// WrappedJob is the thing to run when the Schedule is activated.
WrappedJob Job
// Job is the thing that was submitted to cron.
// It is kept around so that user code that needs to get at the job later,
// e.g. via Entries() can do so.
Job Job
}
// Valid returns true if this is not the zero entry.
func (e Entry) Valid() bool { return e.ID != 0 }
// byTime is a wrapper for sorting the entry array by time
// (with zero time at the end).
type byTime []*Entry
func (s byTime) Len() int { return len(s) }
func (s byTime) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s byTime) Less(i, j int) bool {
// Two zero times should return false.
// Otherwise, zero is "greater" than any other time.
// (To sort it at the end of the list.)
if s[i].Next.IsZero() {
return false
}
if s[j].Next.IsZero() {
return true
}
return s[i].Next.Before(s[j].Next)
}
// New returns a new Cron job runner, modified by the given options.
//
// Available Settings
//
// Time Zone
// Description: The time zone in which schedules are interpreted
// Default: time.Local
//
// Parser
// Description: Parser converts cron spec strings into cron.Schedules.
// Default: Accepts this spec: https://en.wikipedia.org/wiki/Cron
//
// Chain
// Description: Wrap submitted jobs to customize behavior.
// Default: A chain that recovers panics and logs them to stderr.
//
// See "cron.With*" to modify the default behavior.
func New(opts ...Option) *Cron {
c := &Cron{
entries: nil,
chain: NewChain(),
add: make(chan *Entry),
stop: make(chan struct{}),
snapshot: make(chan chan []Entry),
remove: make(chan EntryID),
running: false,
runningMu: sync.Mutex{},
logger: DefaultLogger,
location: time.Local,
parser: standardParser,
}
for _, opt := range opts {
opt(c)
}
return c
}
// FuncJob is a wrapper that turns a func() into a cron.Job
type FuncJob func()
func (f FuncJob) Run() { f() }
// AddFunc adds a func to the Cron to be run on the given schedule.
// The spec is parsed using the time zone of this Cron instance as the default.
// An opaque ID is returned that can be used to later remove it.
func (c *Cron) AddFunc(spec string, cmd func()) (EntryID, error) {
return c.AddJob(spec, FuncJob(cmd))
}
// AddJob adds a Job to the Cron to be run on the given schedule.
// The spec is parsed using the time zone of this Cron instance as the default.
// An opaque ID is returned that can be used to later remove it.
func (c *Cron) AddJob(spec string, cmd Job) (EntryID, error) {
schedule, err := c.parser.Parse(spec)
if err != nil {
return 0, err
}
return c.Schedule(schedule, cmd), nil
}
// Schedule adds a Job to the Cron to be run on the given schedule.
// The job is wrapped with the configured Chain.
func (c *Cron) Schedule(schedule Schedule, cmd Job) EntryID {
c.runningMu.Lock()
defer c.runningMu.Unlock()
c.nextID++
entry := &Entry{
ID: c.nextID,
Schedule: schedule,
WrappedJob: c.chain.Then(cmd),
Job: cmd,
}
if !c.running {
c.entries = append(c.entries, entry)
} else {
c.add <- entry
}
return entry.ID
}
// Entries returns a snapshot of the cron entries.
func (c *Cron) Entries() []Entry {
c.runningMu.Lock()
defer c.runningMu.Unlock()
if c.running {
replyChan := make(chan []Entry, 1)
c.snapshot <- replyChan
return <-replyChan
}
return c.entrySnapshot()
}
// Location gets the time zone location
func (c *Cron) Location() *time.Location {
return c.location
}
// Entry returns a snapshot of the given entry, or nil if it couldn't be found.
func (c *Cron) Entry(id EntryID) Entry {
for _, entry := range c.Entries() {
if id == entry.ID {
return entry
}
}
return Entry{}
}
// Remove an entry from being run in the future.
func (c *Cron) Remove(id EntryID) {
c.runningMu.Lock()
defer c.runningMu.Unlock()
if c.running {
c.remove <- id
} else {
c.removeEntry(id)
}
}
// Start the cron scheduler in its own goroutine, or no-op if already started.
func (c *Cron) Start() {
c.runningMu.Lock()
defer c.runningMu.Unlock()
if c.running {
return
}
c.running = true
go c.run()
}
// Run the cron scheduler, or no-op if already running.
func (c *Cron) Run() {
c.runningMu.Lock()
if c.running {
c.runningMu.Unlock()
return
}
c.running = true
c.runningMu.Unlock()
c.run()
}
// run the scheduler.. this is private just due to the need to synchronize
// access to the 'running' state variable.
func (c *Cron) run() {
c.logger.Info("start")
// Figure out the next activation times for each entry.
now := c.now()
for _, entry := range c.entries {
entry.Next = entry.Schedule.Next(now)
c.logger.Info("schedule", "now", now, "entry", entry.ID, "next", entry.Next)
}
for {
// Determine the next entry to run.
sort.Sort(byTime(c.entries))
var timer *time.Timer
if len(c.entries) == 0 || c.entries[0].Next.IsZero() {
// If there are no entries yet, just sleep - it still handles new entries
// and stop requests.
timer = time.NewTimer(100000 * time.Hour)
} else {
timer = time.NewTimer(c.entries[0].Next.Sub(now))
}
for {
select {
case now = <-timer.C:
now = now.In(c.location)
c.logger.Info("wake", "now", now)
// Run every entry whose next time was less than now
for _, e := range c.entries {
if e.Next.After(now) || e.Next.IsZero() {
break
}
c.startJob(e.WrappedJob)
e.Prev = e.Next
e.Next = e.Schedule.Next(now)
c.logger.Info("run", "now", now, "entry", e.ID, "next", e.Next)
}
case newEntry := <-c.add:
timer.Stop()
now = c.now()
newEntry.Next = newEntry.Schedule.Next(now)
c.entries = append(c.entries, newEntry)
c.logger.Info("added", "now", now, "entry", newEntry.ID, "next", newEntry.Next)
case replyChan := <-c.snapshot:
replyChan <- c.entrySnapshot()
continue
case <-c.stop:
timer.Stop()
c.logger.Info("stop")
return
case id := <-c.remove:
timer.Stop()
now = c.now()
c.removeEntry(id)
c.logger.Info("removed", "entry", id)
}
break
}
}
}
// startJob runs the given job in a new goroutine.
func (c *Cron) startJob(j Job) {
c.jobWaiter.Add(1)
go func() {
defer c.jobWaiter.Done()
j.Run()
}()
}
// now returns current time in c location
func (c *Cron) now() time.Time {
return time.Now().In(c.location)
}
// Stop stops the cron scheduler if it is running; otherwise it does nothing.
// A context is returned so the caller can wait for running jobs to complete.
func (c *Cron) Stop() context.Context {
c.runningMu.Lock()
defer c.runningMu.Unlock()
if c.running {
c.stop <- struct{}{}
c.running = false
}
ctx, cancel := context.WithCancel(context.Background())
go func() {
c.jobWaiter.Wait()
cancel()
}()
return ctx
}
// entrySnapshot returns a copy of the current cron entry list.
func (c *Cron) entrySnapshot() []Entry {
var entries = make([]Entry, len(c.entries))
for i, e := range c.entries {
entries[i] = *e
}
return entries
}
func (c *Cron) removeEntry(id EntryID) {
var entries []*Entry
for _, e := range c.entries {
if e.ID != id {
entries = append(entries, e)
}
}
c.entries = entries
}

View File

@ -1,702 +0,0 @@
package cron
import (
"bytes"
"fmt"
"log"
"strings"
"sync"
"sync/atomic"
"testing"
"time"
)
// Many tests schedule a job for every second, and then wait at most a second
// for it to run. This amount is just slightly larger than 1 second to
// compensate for a few milliseconds of runtime.
const OneSecond = 1*time.Second + 50*time.Millisecond
type syncWriter struct {
wr bytes.Buffer
m sync.Mutex
}
func (sw *syncWriter) Write(data []byte) (n int, err error) {
sw.m.Lock()
n, err = sw.wr.Write(data)
sw.m.Unlock()
return
}
func (sw *syncWriter) String() string {
sw.m.Lock()
defer sw.m.Unlock()
return sw.wr.String()
}
func newBufLogger(sw *syncWriter) Logger {
return PrintfLogger(log.New(sw, "", log.LstdFlags))
}
func TestFuncPanicRecovery(t *testing.T) {
var buf syncWriter
cron := New(WithParser(secondParser),
WithChain(Recover(newBufLogger(&buf))))
cron.Start()
defer cron.Stop()
cron.AddFunc("* * * * * ?", func() {
panic("YOLO")
})
select {
case <-time.After(OneSecond):
if !strings.Contains(buf.String(), "YOLO") {
t.Error("expected a panic to be logged, got none")
}
return
}
}
type DummyJob struct{}
func (d DummyJob) Run() {
panic("YOLO")
}
func TestJobPanicRecovery(t *testing.T) {
var job DummyJob
var buf syncWriter
cron := New(WithParser(secondParser),
WithChain(Recover(newBufLogger(&buf))))
cron.Start()
defer cron.Stop()
cron.AddJob("* * * * * ?", job)
select {
case <-time.After(OneSecond):
if !strings.Contains(buf.String(), "YOLO") {
t.Error("expected a panic to be logged, got none")
}
return
}
}
// Start and stop cron with no entries.
func TestNoEntries(t *testing.T) {
cron := newWithSeconds()
cron.Start()
select {
case <-time.After(OneSecond):
t.Fatal("expected cron will be stopped immediately")
case <-stop(cron):
}
}
// Start, stop, then add an entry. Verify entry doesn't run.
func TestStopCausesJobsToNotRun(t *testing.T) {
wg := &sync.WaitGroup{}
wg.Add(1)
cron := newWithSeconds()
cron.Start()
cron.Stop()
cron.AddFunc("* * * * * ?", func() { wg.Done() })
select {
case <-time.After(OneSecond):
// No job ran!
case <-wait(wg):
t.Fatal("expected stopped cron does not run any job")
}
}
// Add a job, start cron, expect it runs.
func TestAddBeforeRunning(t *testing.T) {
wg := &sync.WaitGroup{}
wg.Add(1)
cron := newWithSeconds()
cron.AddFunc("* * * * * ?", func() { wg.Done() })
cron.Start()
defer cron.Stop()
// Give cron 2 seconds to run our job (which is always activated).
select {
case <-time.After(OneSecond):
t.Fatal("expected job runs")
case <-wait(wg):
}
}
// Start cron, add a job, expect it runs.
func TestAddWhileRunning(t *testing.T) {
wg := &sync.WaitGroup{}
wg.Add(1)
cron := newWithSeconds()
cron.Start()
defer cron.Stop()
cron.AddFunc("* * * * * ?", func() { wg.Done() })
select {
case <-time.After(OneSecond):
t.Fatal("expected job runs")
case <-wait(wg):
}
}
// Test for #34. Adding a job after calling start results in multiple job invocations
func TestAddWhileRunningWithDelay(t *testing.T) {
cron := newWithSeconds()
cron.Start()
defer cron.Stop()
time.Sleep(5 * time.Second)
var calls int64
cron.AddFunc("* * * * * *", func() { atomic.AddInt64(&calls, 1) })
<-time.After(OneSecond)
if atomic.LoadInt64(&calls) != 1 {
t.Errorf("called %d times, expected 1\n", calls)
}
}
// Add a job, remove a job, start cron, expect nothing runs.
func TestRemoveBeforeRunning(t *testing.T) {
wg := &sync.WaitGroup{}
wg.Add(1)
cron := newWithSeconds()
id, _ := cron.AddFunc("* * * * * ?", func() { wg.Done() })
cron.Remove(id)
cron.Start()
defer cron.Stop()
select {
case <-time.After(OneSecond):
// Success, shouldn't run
case <-wait(wg):
t.FailNow()
}
}
// Start cron, add a job, remove it, expect it doesn't run.
func TestRemoveWhileRunning(t *testing.T) {
wg := &sync.WaitGroup{}
wg.Add(1)
cron := newWithSeconds()
cron.Start()
defer cron.Stop()
id, _ := cron.AddFunc("* * * * * ?", func() { wg.Done() })
cron.Remove(id)
select {
case <-time.After(OneSecond):
case <-wait(wg):
t.FailNow()
}
}
// Test timing with Entries.
func TestSnapshotEntries(t *testing.T) {
wg := &sync.WaitGroup{}
wg.Add(1)
cron := New()
cron.AddFunc("@every 2s", func() { wg.Done() })
cron.Start()
defer cron.Stop()
// Cron should fire in 2 seconds. After 1 second, call Entries.
select {
case <-time.After(OneSecond):
cron.Entries()
}
// Even though Entries was called, the cron should fire at the 2 second mark.
select {
case <-time.After(OneSecond):
t.Error("expected job runs at 2 second mark")
case <-wait(wg):
}
}
// Test that the entries are correctly sorted.
// Add a bunch of long-in-the-future entries, and an immediate entry, and ensure
// that the immediate entry runs immediately.
// Also: Test that multiple jobs run in the same instant.
func TestMultipleEntries(t *testing.T) {
wg := &sync.WaitGroup{}
wg.Add(2)
cron := newWithSeconds()
cron.AddFunc("0 0 0 1 1 ?", func() {})
cron.AddFunc("* * * * * ?", func() { wg.Done() })
id1, _ := cron.AddFunc("* * * * * ?", func() { t.Fatal() })
id2, _ := cron.AddFunc("* * * * * ?", func() { t.Fatal() })
cron.AddFunc("0 0 0 31 12 ?", func() {})
cron.AddFunc("* * * * * ?", func() { wg.Done() })
cron.Remove(id1)
cron.Start()
cron.Remove(id2)
defer cron.Stop()
select {
case <-time.After(OneSecond):
t.Error("expected job run in proper order")
case <-wait(wg):
}
}
// Test running the same job twice.
func TestRunningJobTwice(t *testing.T) {
wg := &sync.WaitGroup{}
wg.Add(2)
cron := newWithSeconds()
cron.AddFunc("0 0 0 1 1 ?", func() {})
cron.AddFunc("0 0 0 31 12 ?", func() {})
cron.AddFunc("* * * * * ?", func() { wg.Done() })
cron.Start()
defer cron.Stop()
select {
case <-time.After(2 * OneSecond):
t.Error("expected job fires 2 times")
case <-wait(wg):
}
}
func TestRunningMultipleSchedules(t *testing.T) {
wg := &sync.WaitGroup{}
wg.Add(2)
cron := newWithSeconds()
cron.AddFunc("0 0 0 1 1 ?", func() {})
cron.AddFunc("0 0 0 31 12 ?", func() {})
cron.AddFunc("* * * * * ?", func() { wg.Done() })
cron.Schedule(Every(time.Minute), FuncJob(func() {}))
cron.Schedule(Every(time.Second), FuncJob(func() { wg.Done() }))
cron.Schedule(Every(time.Hour), FuncJob(func() {}))
cron.Start()
defer cron.Stop()
select {
case <-time.After(2 * OneSecond):
t.Error("expected job fires 2 times")
case <-wait(wg):
}
}
// Test that the cron is run in the local time zone (as opposed to UTC).
func TestLocalTimezone(t *testing.T) {
wg := &sync.WaitGroup{}
wg.Add(2)
now := time.Now()
// FIX: Issue #205
// This calculation doesn't work in seconds 58 or 59.
// Take the easy way out and sleep.
if now.Second() >= 58 {
time.Sleep(2 * time.Second)
now = time.Now()
}
spec := fmt.Sprintf("%d,%d %d %d %d %d ?",
now.Second()+1, now.Second()+2, now.Minute(), now.Hour(), now.Day(), now.Month())
cron := newWithSeconds()
cron.AddFunc(spec, func() { wg.Done() })
cron.Start()
defer cron.Stop()
select {
case <-time.After(OneSecond * 2):
t.Error("expected job fires 2 times")
case <-wait(wg):
}
}
// Test that the cron is run in the given time zone (as opposed to local).
func TestNonLocalTimezone(t *testing.T) {
wg := &sync.WaitGroup{}
wg.Add(2)
loc, err := time.LoadLocation("Atlantic/Cape_Verde")
if err != nil {
fmt.Printf("Failed to load time zone Atlantic/Cape_Verde: %+v", err)
t.Fail()
}
now := time.Now().In(loc)
// FIX: Issue #205
// This calculation doesn't work in seconds 58 or 59.
// Take the easy way out and sleep.
if now.Second() >= 58 {
time.Sleep(2 * time.Second)
now = time.Now().In(loc)
}
spec := fmt.Sprintf("%d,%d %d %d %d %d ?",
now.Second()+1, now.Second()+2, now.Minute(), now.Hour(), now.Day(), now.Month())
cron := New(WithLocation(loc), WithParser(secondParser))
cron.AddFunc(spec, func() { wg.Done() })
cron.Start()
defer cron.Stop()
select {
case <-time.After(OneSecond * 2):
t.Error("expected job fires 2 times")
case <-wait(wg):
}
}
// Test that calling stop before start silently returns without
// blocking the stop channel.
func TestStopWithoutStart(t *testing.T) {
cron := New()
cron.Stop()
}
type testJob struct {
wg *sync.WaitGroup
name string
}
func (t testJob) Run() {
t.wg.Done()
}
// Test that adding an invalid job spec returns an error
func TestInvalidJobSpec(t *testing.T) {
cron := New()
_, err := cron.AddJob("this will not parse", nil)
if err == nil {
t.Errorf("expected an error with invalid spec, got nil")
}
}
// Test blocking run method behaves as Start()
func TestBlockingRun(t *testing.T) {
wg := &sync.WaitGroup{}
wg.Add(1)
cron := newWithSeconds()
cron.AddFunc("* * * * * ?", func() { wg.Done() })
var unblockChan = make(chan struct{})
go func() {
cron.Run()
close(unblockChan)
}()
defer cron.Stop()
select {
case <-time.After(OneSecond):
t.Error("expected job fires")
case <-unblockChan:
t.Error("expected that Run() blocks")
case <-wait(wg):
}
}
// Test that double-running is a no-op
func TestStartNoop(t *testing.T) {
var tickChan = make(chan struct{}, 2)
cron := newWithSeconds()
cron.AddFunc("* * * * * ?", func() {
tickChan <- struct{}{}
})
cron.Start()
defer cron.Stop()
// Wait for the first firing to ensure the runner is going
<-tickChan
cron.Start()
<-tickChan
// Fail if this job fires again in a short period, indicating a double-run
select {
case <-time.After(time.Millisecond):
case <-tickChan:
t.Error("expected job fires exactly twice")
}
}
// Simple test using Runnables.
func TestJob(t *testing.T) {
wg := &sync.WaitGroup{}
wg.Add(1)
cron := newWithSeconds()
cron.AddJob("0 0 0 30 Feb ?", testJob{wg, "job0"})
cron.AddJob("0 0 0 1 1 ?", testJob{wg, "job1"})
job2, _ := cron.AddJob("* * * * * ?", testJob{wg, "job2"})
cron.AddJob("1 0 0 1 1 ?", testJob{wg, "job3"})
cron.Schedule(Every(5*time.Second+5*time.Nanosecond), testJob{wg, "job4"})
job5 := cron.Schedule(Every(5*time.Minute), testJob{wg, "job5"})
// Test getting an Entry pre-Start.
if actualName := cron.Entry(job2).Job.(testJob).name; actualName != "job2" {
t.Error("wrong job retrieved:", actualName)
}
if actualName := cron.Entry(job5).Job.(testJob).name; actualName != "job5" {
t.Error("wrong job retrieved:", actualName)
}
cron.Start()
defer cron.Stop()
select {
case <-time.After(OneSecond):
t.FailNow()
case <-wait(wg):
}
// Ensure the entries are in the right order.
expecteds := []string{"job2", "job4", "job5", "job1", "job3", "job0"}
var actuals []string
for _, entry := range cron.Entries() {
actuals = append(actuals, entry.Job.(testJob).name)
}
for i, expected := range expecteds {
if actuals[i] != expected {
t.Fatalf("Jobs not in the right order. (expected) %s != %s (actual)", expecteds, actuals)
}
}
// Test getting Entries.
if actualName := cron.Entry(job2).Job.(testJob).name; actualName != "job2" {
t.Error("wrong job retrieved:", actualName)
}
if actualName := cron.Entry(job5).Job.(testJob).name; actualName != "job5" {
t.Error("wrong job retrieved:", actualName)
}
}
// Issue #206
// Ensure that the next run of a job after removing an entry is accurate.
func TestScheduleAfterRemoval(t *testing.T) {
var wg1 sync.WaitGroup
var wg2 sync.WaitGroup
wg1.Add(1)
wg2.Add(1)
// The first time this job is run, set a timer and remove the other job
// 750ms later. Correct behavior would be to still run the job again in
// 250ms, but the bug would cause it to run instead 1s later.
var calls int
var mu sync.Mutex
cron := newWithSeconds()
hourJob := cron.Schedule(Every(time.Hour), FuncJob(func() {}))
cron.Schedule(Every(time.Second), FuncJob(func() {
mu.Lock()
defer mu.Unlock()
switch calls {
case 0:
wg1.Done()
calls++
case 1:
time.Sleep(750 * time.Millisecond)
cron.Remove(hourJob)
calls++
case 2:
calls++
wg2.Done()
case 3:
panic("unexpected 3rd call")
}
}))
cron.Start()
defer cron.Stop()
// the first run might be any length of time 0 - 1s, since the schedule
// rounds to the second. wait for the first run to true up.
wg1.Wait()
select {
case <-time.After(2 * OneSecond):
t.Error("expected job fires 2 times")
case <-wait(&wg2):
}
}
type ZeroSchedule struct{}
func (*ZeroSchedule) Next(time.Time) time.Time {
return time.Time{}
}
// Tests that job without time does not run
func TestJobWithZeroTimeDoesNotRun(t *testing.T) {
cron := newWithSeconds()
var calls int64
cron.AddFunc("* * * * * *", func() { atomic.AddInt64(&calls, 1) })
cron.Schedule(new(ZeroSchedule), FuncJob(func() { t.Error("expected zero task will not run") }))
cron.Start()
defer cron.Stop()
<-time.After(OneSecond)
if atomic.LoadInt64(&calls) != 1 {
t.Errorf("called %d times, expected 1\n", calls)
}
}
func TestStopAndWait(t *testing.T) {
t.Run("nothing running, returns immediately", func(t *testing.T) {
cron := newWithSeconds()
cron.Start()
ctx := cron.Stop()
select {
case <-ctx.Done():
case <-time.After(time.Millisecond):
t.Error("context was not done immediately")
}
})
t.Run("repeated calls to Stop", func(t *testing.T) {
cron := newWithSeconds()
cron.Start()
_ = cron.Stop()
time.Sleep(time.Millisecond)
ctx := cron.Stop()
select {
case <-ctx.Done():
case <-time.After(time.Millisecond):
t.Error("context was not done immediately")
}
})
t.Run("a couple fast jobs added, still returns immediately", func(t *testing.T) {
cron := newWithSeconds()
cron.AddFunc("* * * * * *", func() {})
cron.Start()
cron.AddFunc("* * * * * *", func() {})
cron.AddFunc("* * * * * *", func() {})
cron.AddFunc("* * * * * *", func() {})
time.Sleep(time.Second)
ctx := cron.Stop()
select {
case <-ctx.Done():
case <-time.After(time.Millisecond):
t.Error("context was not done immediately")
}
})
t.Run("a couple fast jobs and a slow job added, waits for slow job", func(t *testing.T) {
cron := newWithSeconds()
cron.AddFunc("* * * * * *", func() {})
cron.Start()
cron.AddFunc("* * * * * *", func() { time.Sleep(2 * time.Second) })
cron.AddFunc("* * * * * *", func() {})
time.Sleep(time.Second)
ctx := cron.Stop()
// Verify that it is not done for at least 750ms
select {
case <-ctx.Done():
t.Error("context was done too quickly immediately")
case <-time.After(750 * time.Millisecond):
// expected, because the job sleeping for 1 second is still running
}
// Verify that it IS done in the next 500ms (giving 250ms buffer)
select {
case <-ctx.Done():
// expected
case <-time.After(1500 * time.Millisecond):
t.Error("context not done after job should have completed")
}
})
t.Run("repeated calls to stop, waiting for completion and after", func(t *testing.T) {
cron := newWithSeconds()
cron.AddFunc("* * * * * *", func() {})
cron.AddFunc("* * * * * *", func() { time.Sleep(2 * time.Second) })
cron.Start()
cron.AddFunc("* * * * * *", func() {})
time.Sleep(time.Second)
ctx := cron.Stop()
ctx2 := cron.Stop()
// Verify that it is not done for at least 1500ms
select {
case <-ctx.Done():
t.Error("context was done too quickly immediately")
case <-ctx2.Done():
t.Error("context2 was done too quickly immediately")
case <-time.After(1500 * time.Millisecond):
// expected, because the job sleeping for 2 seconds is still running
}
// Verify that it IS done in the next 1s (giving 500ms buffer)
select {
case <-ctx.Done():
// expected
case <-time.After(time.Second):
t.Error("context not done after job should have completed")
}
// Verify that ctx2 is also done.
select {
case <-ctx2.Done():
// expected
case <-time.After(time.Millisecond):
t.Error("context2 not done even though context1 is")
}
// Verify that a new context retrieved from stop is immediately done.
ctx3 := cron.Stop()
select {
case <-ctx3.Done():
// expected
case <-time.After(time.Millisecond):
t.Error("context not done even when cron Stop is completed")
}
})
}
func TestMultiThreadedStartAndStop(t *testing.T) {
cron := New()
go cron.Run()
time.Sleep(2 * time.Millisecond)
cron.Stop()
}
func wait(wg *sync.WaitGroup) chan bool {
ch := make(chan bool)
go func() {
wg.Wait()
ch <- true
}()
return ch
}
func stop(cron *Cron) chan bool {
ch := make(chan bool)
go func() {
cron.Stop()
ch <- true
}()
return ch
}
// newWithSeconds returns a Cron with the seconds field enabled.
func newWithSeconds() *Cron {
return New(WithParser(secondParser), WithChain())
}

View File

@ -1,212 +0,0 @@
/*
Package cron implements a cron spec parser and job runner.
Usage
Callers may register Funcs to be invoked on a given schedule. Cron will run
them in their own goroutines.
c := cron.New()
c.AddFunc("30 * * * *", func() { fmt.Println("Every hour on the half hour") })
c.AddFunc("30 3-6,20-23 * * *", func() { fmt.Println(".. in the range 3-6am, 8-11pm") })
c.AddFunc("CRON_TZ=Asia/Tokyo 30 04 * * * *", func() { fmt.Println("Runs at 04:30 Tokyo time every day") })
c.AddFunc("@hourly", func() { fmt.Println("Every hour, starting an hour from now") })
c.AddFunc("@every 1h30m", func() { fmt.Println("Every hour thirty, starting an hour thirty from now") })
c.Start()
..
// Funcs are invoked in their own goroutine, asynchronously.
...
// Funcs may also be added to a running Cron
c.AddFunc("@daily", func() { fmt.Println("Every day") })
..
// Inspect the cron job entries' next and previous run times.
inspect(c.Entries())
..
c.Stop() // Stop the scheduler (does not stop any jobs already running).
CRON Expression Format
A cron expression represents a set of times, using 5 space-separated fields.
Field name | Mandatory? | Allowed values | Allowed special characters
---------- | ---------- | -------------- | --------------------------
Minutes | Yes | 0-59 | * / , -
Hours | Yes | 0-23 | * / , -
Day of month | Yes | 1-31 | * / , - ?
Month | Yes | 1-12 or JAN-DEC | * / , -
Day of week | Yes | 0-6 or SUN-SAT | * / , - ?
Month and Day-of-week field values are case insensitive. "SUN", "Sun", and
"sun" are equally accepted.
The specific interpretation of the format is based on the Cron Wikipedia page:
https://en.wikipedia.org/wiki/Cron
Alternative Formats
Alternative Cron expression formats support other fields like seconds. You can
implement that by creating a custom Parser as follows.
cron.New(
cron.WithParser(
cron.SecondOptional | cron.Hour | cron.Dom | cron.Month | cron.Dow | cron.Descriptor))
The most popular alternative Cron expression format is Quartz:
http://www.quartz-scheduler.org/documentation/quartz-2.x/tutorials/crontrigger.html
Special Characters
Asterisk ( * )
The asterisk indicates that the cron expression will match for all values of the
field; e.g., using an asterisk in the 5th field (month) would indicate every
month.
Slash ( / )
Slashes are used to describe increments of ranges. For example 3-59/15 in the
1st field (minutes) would indicate the 3rd minute of the hour and every 15
minutes thereafter. The form "*\/..." is equivalent to the form "first-last/...",
that is, an increment over the largest possible range of the field. The form
"N/..." is accepted as meaning "N-MAX/...", that is, starting at N, use the
increment until the end of that specific range. It does not wrap around.
Comma ( , )
Commas are used to separate items of a list. For example, using "MON,WED,FRI" in
the 5th field (day of week) would mean Mondays, Wednesdays and Fridays.
Hyphen ( - )
Hyphens are used to define ranges. For example, 9-17 would indicate every
hour between 9am and 5pm inclusive.
Question mark ( ? )
Question mark may be used instead of '*' for leaving either day-of-month or
day-of-week blank.
Predefined schedules
You may use one of several pre-defined schedules in place of a cron expression.
Entry | Description | Equivalent To
----- | ----------- | -------------
@yearly (or @annually) | Run once a year, midnight, Jan. 1st | 0 0 1 1 *
@monthly | Run once a month, midnight, first of month | 0 0 1 * *
@weekly | Run once a week, midnight between Sat/Sun | 0 0 * * 0
@daily (or @midnight) | Run once a day, midnight | 0 0 * * *
@hourly | Run once an hour, beginning of hour | 0 * * * *
Intervals
You may also schedule a job to execute at fixed intervals, starting at the time it's added
or cron is run. This is supported by formatting the cron spec like this:
@every <duration>
where "duration" is a string accepted by time.ParseDuration
(http://golang.org/pkg/time/#ParseDuration).
For example, "@every 1h30m10s" would indicate a schedule that activates after
1 hour, 30 minutes, 10 seconds, and then every interval after that.
Note: The interval does not take the job runtime into account. For example,
if a job takes 3 minutes to run, and it is scheduled to run every 5 minutes,
it will have only 2 minutes of idle time between each run.
Time zones
By default, all interpretation and scheduling is done in the machine's local
time zone (time.Local). You can specify a different time zone on construction:
cron.New(
cron.WithLocation(time.UTC))
Individual cron schedules may also override the time zone they are to be
interpreted in by providing an additional space-separated field at the beginning
of the cron spec, of the form "CRON_TZ=Asia/Tokyo".
For example:
# Runs at 6am in time.Local
cron.New().AddFunc("0 6 * * ?", ...)
# Runs at 6am in America/New_York
nyc, _ := time.LoadLocation("America/New_York")
c := cron.New(cron.WithLocation(nyc))
c.AddFunc("0 6 * * ?", ...)
# Runs at 6am in Asia/Tokyo
cron.New().AddFunc("CRON_TZ=Asia/Tokyo 0 6 * * ?", ...)
# Runs at 6am in Asia/Tokyo
c := cron.New(cron.WithLocation(nyc))
c.SetLocation("America/New_York")
c.AddFunc("CRON_TZ=Asia/Tokyo 0 6 * * ?", ...)
The prefix "TZ=(TIME ZONE)" is also supported for legacy compatibility.
Be aware that jobs scheduled during daylight-savings leap-ahead transitions will
not be run!
Job Wrappers / Chain
A Cron runner may be configured with a chain of job wrappers to add
cross-cutting functionality to all submitted jobs. For example, they may be used
to achieve the following effects:
- Recover any panics from jobs (activated by default)
- Delay a job's execution if the previous run hasn't completed yet
- Skip a job's execution if the previous run hasn't completed yet
- Log each job's invocations
Install wrappers for all jobs added to a cron using the `cron.WithChain` option:
cron.New(cron.WithChain(
cron.SkipIfStillRunning(logger),
))
Install wrappers for individual jobs by explicitly wrapping them:
job = cron.NewChain(
cron.SkipIfStillRunning(logger),
).Then(job)
Thread safety
Since the Cron service runs concurrently with the calling code, some amount of
care must be taken to ensure proper synchronization.
All cron methods are designed to be correctly synchronized as long as the caller
ensures that invocations have a clear happens-before ordering between them.
Logging
Cron defines a Logger interface that is a subset of the one defined in
github.com/go-logr/logr. It has two logging levels (Info and Error), and
parameters are key/value pairs. This makes it possible for cron logging to plug
into structured logging systems. An adapter, [Verbose]PrintfLogger, is provided
to wrap the standard library *log.Logger.
For additional insight into Cron operations, verbose logging may be activated
which will record job runs, scheduling decisions, and added or removed jobs.
Activate it with a one-off logger as follows:
cron.New(
cron.WithLogger(
cron.VerbosePrintfLogger(log.New(os.Stdout, "cron: ", log.LstdFlags))))
Implementation
Cron entries are stored in an array, sorted by their next activation time. Cron
sleeps until the next job is due to be run.
Upon waking:
- it runs each entry that is active on that second
- it calculates the next run times for the jobs that were run
- it re-sorts the array of entries by next activation time.
- it goes to sleep until the soonest job.
*/
package cron

View File

@ -1,86 +0,0 @@
package cron
import (
"io/ioutil"
"log"
"os"
"strings"
"time"
)
// DefaultLogger is used by Cron if none is specified.
var DefaultLogger Logger = PrintfLogger(log.New(os.Stdout, "cron: ", log.LstdFlags))
// DiscardLogger can be used by callers to discard all log messages.
var DiscardLogger Logger = PrintfLogger(log.New(ioutil.Discard, "", 0))
// Logger is the interface used in this package for logging, so that any backend
// can be plugged in. It is a subset of the github.com/go-logr/logr interface.
type Logger interface {
// Info logs routine messages about cron's operation.
Info(msg string, keysAndValues ...interface{})
// Error logs an error condition.
Error(err error, msg string, keysAndValues ...interface{})
}
// PrintfLogger wraps a Printf-based logger (such as the standard library "log")
// into an implementation of the Logger interface which logs errors only.
func PrintfLogger(l interface{ Printf(string, ...interface{}) }) Logger {
return printfLogger{l, false}
}
// VerbosePrintfLogger wraps a Printf-based logger (such as the standard library
// "log") into an implementation of the Logger interface which logs everything.
func VerbosePrintfLogger(l interface{ Printf(string, ...interface{}) }) Logger {
return printfLogger{l, true}
}
type printfLogger struct {
logger interface{ Printf(string, ...interface{}) }
logInfo bool
}
func (pl printfLogger) Info(msg string, keysAndValues ...interface{}) {
if pl.logInfo {
keysAndValues = formatTimes(keysAndValues)
pl.logger.Printf(
formatString(len(keysAndValues)),
append([]interface{}{msg}, keysAndValues...)...)
}
}
func (pl printfLogger) Error(err error, msg string, keysAndValues ...interface{}) {
keysAndValues = formatTimes(keysAndValues)
pl.logger.Printf(
formatString(len(keysAndValues)+2),
append([]interface{}{msg, "error", err}, keysAndValues...)...)
}
// formatString returns a logfmt-like format string for the number of
// key/values.
func formatString(numKeysAndValues int) string {
var sb strings.Builder
sb.WriteString("%s")
if numKeysAndValues > 0 {
sb.WriteString(", ")
}
for i := 0; i < numKeysAndValues/2; i++ {
if i > 0 {
sb.WriteString(", ")
}
sb.WriteString("%v=%v")
}
return sb.String()
}
// formatTimes formats any time.Time values as RFC3339.
func formatTimes(keysAndValues []interface{}) []interface{} {
var formattedArgs []interface{}
for _, arg := range keysAndValues {
if t, ok := arg.(time.Time); ok {
arg = t.Format(time.RFC3339)
}
formattedArgs = append(formattedArgs, arg)
}
return formattedArgs
}

View File

@ -1,45 +0,0 @@
package cron
import (
"time"
)
// Option represents a modification to the default behavior of a Cron.
type Option func(*Cron)
// WithLocation overrides the timezone of the cron instance.
func WithLocation(loc *time.Location) Option {
return func(c *Cron) {
c.location = loc
}
}
// WithSeconds overrides the parser used for interpreting job schedules to
// include a seconds field as the first one.
func WithSeconds() Option {
return WithParser(NewParser(
Second | Minute | Hour | Dom | Month | Dow | Descriptor,
))
}
// WithParser overrides the parser used for interpreting job schedules.
func WithParser(p Parser) Option {
return func(c *Cron) {
c.parser = p
}
}
// WithChain specifies Job wrappers to apply to all jobs added to this cron.
// Refer to the Chain* functions in this package for provided wrappers.
func WithChain(wrappers ...JobWrapper) Option {
return func(c *Cron) {
c.chain = NewChain(wrappers...)
}
}
// WithLogger uses the provided logger.
func WithLogger(logger Logger) Option {
return func(c *Cron) {
c.logger = logger
}
}

View File

@ -1,42 +0,0 @@
package cron
import (
"log"
"strings"
"testing"
"time"
)
func TestWithLocation(t *testing.T) {
c := New(WithLocation(time.UTC))
if c.location != time.UTC {
t.Errorf("expected UTC, got %v", c.location)
}
}
func TestWithParser(t *testing.T) {
var parser = NewParser(Dow)
c := New(WithParser(parser))
if c.parser != parser {
t.Error("expected provided parser")
}
}
func TestWithVerboseLogger(t *testing.T) {
var buf syncWriter
var logger = log.New(&buf, "", log.LstdFlags)
c := New(WithLogger(VerbosePrintfLogger(logger)))
if c.logger.(printfLogger).logger != logger {
t.Error("expected provided logger")
}
c.AddFunc("@every 1s", func() {})
c.Start()
time.Sleep(OneSecond)
c.Stop()
out := buf.String()
if !strings.Contains(out, "schedule,") ||
!strings.Contains(out, "run,") {
t.Error("expected to see some actions, got:", out)
}
}

View File

@ -1,434 +0,0 @@
package cron
import (
"fmt"
"math"
"strconv"
"strings"
"time"
)
// Configuration options for creating a parser. Most options specify which
// fields should be included, while others enable features. If a field is not
// included the parser will assume a default value. These options do not change
// the order fields are parse in.
type ParseOption int
const (
Second ParseOption = 1 << iota // Seconds field, default 0
SecondOptional // Optional seconds field, default 0
Minute // Minutes field, default 0
Hour // Hours field, default 0
Dom // Day of month field, default *
Month // Month field, default *
Dow // Day of week field, default *
DowOptional // Optional day of week field, default *
Descriptor // Allow descriptors such as @monthly, @weekly, etc.
)
var places = []ParseOption{
Second,
Minute,
Hour,
Dom,
Month,
Dow,
}
var defaults = []string{
"0",
"0",
"0",
"*",
"*",
"*",
}
// A custom Parser that can be configured.
type Parser struct {
options ParseOption
}
// NewParser creates a Parser with custom options.
//
// It panics if more than one Optional is given, since it would be impossible to
// correctly infer which optional is provided or missing in general.
//
// Examples
//
// // Standard parser without descriptors
// specParser := NewParser(Minute | Hour | Dom | Month | Dow)
// sched, err := specParser.Parse("0 0 15 */3 *")
//
// // Same as above, just excludes time fields
// subsParser := NewParser(Dom | Month | Dow)
// sched, err := specParser.Parse("15 */3 *")
//
// // Same as above, just makes Dow optional
// subsParser := NewParser(Dom | Month | DowOptional)
// sched, err := specParser.Parse("15 */3")
//
func NewParser(options ParseOption) Parser {
optionals := 0
if options&DowOptional > 0 {
optionals++
}
if options&SecondOptional > 0 {
optionals++
}
if optionals > 1 {
panic("multiple optionals may not be configured")
}
return Parser{options}
}
// Parse returns a new crontab schedule representing the given spec.
// It returns a descriptive error if the spec is not valid.
// It accepts crontab specs and features configured by NewParser.
func (p Parser) Parse(spec string) (Schedule, error) {
if len(spec) == 0 {
return nil, fmt.Errorf("empty spec string")
}
// Extract timezone if present
var loc = time.Local
if strings.HasPrefix(spec, "TZ=") || strings.HasPrefix(spec, "CRON_TZ=") {
var err error
i := strings.Index(spec, " ")
eq := strings.Index(spec, "=")
if loc, err = time.LoadLocation(spec[eq+1 : i]); err != nil {
return nil, fmt.Errorf("provided bad location %s: %v", spec[eq+1:i], err)
}
spec = strings.TrimSpace(spec[i:])
}
// Handle named schedules (descriptors), if configured
if strings.HasPrefix(spec, "@") {
if p.options&Descriptor == 0 {
return nil, fmt.Errorf("parser does not accept descriptors: %v", spec)
}
return parseDescriptor(spec, loc)
}
// Split on whitespace.
fields := strings.Fields(spec)
// Validate & fill in any omitted or optional fields
var err error
fields, err = normalizeFields(fields, p.options)
if err != nil {
return nil, err
}
field := func(field string, r bounds) uint64 {
if err != nil {
return 0
}
var bits uint64
bits, err = getField(field, r)
return bits
}
var (
second = field(fields[0], seconds)
minute = field(fields[1], minutes)
hour = field(fields[2], hours)
dayofmonth = field(fields[3], dom)
month = field(fields[4], months)
dayofweek = field(fields[5], dow)
)
if err != nil {
return nil, err
}
return &SpecSchedule{
Second: second,
Minute: minute,
Hour: hour,
Dom: dayofmonth,
Month: month,
Dow: dayofweek,
Location: loc,
}, nil
}
// normalizeFields takes a subset set of the time fields and returns the full set
// with defaults (zeroes) populated for unset fields.
//
// As part of performing this function, it also validates that the provided
// fields are compatible with the configured options.
func normalizeFields(fields []string, options ParseOption) ([]string, error) {
// Validate optionals & add their field to options
optionals := 0
if options&SecondOptional > 0 {
options |= Second
optionals++
}
if options&DowOptional > 0 {
options |= Dow
optionals++
}
if optionals > 1 {
return nil, fmt.Errorf("multiple optionals may not be configured")
}
// Figure out how many fields we need
max := 0
for _, place := range places {
if options&place > 0 {
max++
}
}
min := max - optionals
// Validate number of fields
if count := len(fields); count < min || count > max {
if min == max {
return nil, fmt.Errorf("expected exactly %d fields, found %d: %s", min, count, fields)
}
return nil, fmt.Errorf("expected %d to %d fields, found %d: %s", min, max, count, fields)
}
// Populate the optional field if not provided
if min < max && len(fields) == min {
switch {
case options&DowOptional > 0:
fields = append(fields, defaults[5]) // TODO: improve access to default
case options&SecondOptional > 0:
fields = append([]string{defaults[0]}, fields...)
default:
return nil, fmt.Errorf("unknown optional field")
}
}
// Populate all fields not part of options with their defaults
n := 0
expandedFields := make([]string, len(places))
copy(expandedFields, defaults)
for i, place := range places {
if options&place > 0 {
expandedFields[i] = fields[n]
n++
}
}
return expandedFields, nil
}
var standardParser = NewParser(
Minute | Hour | Dom | Month | Dow | Descriptor,
)
// ParseStandard returns a new crontab schedule representing the given
// standardSpec (https://en.wikipedia.org/wiki/Cron). It requires 5 entries
// representing: minute, hour, day of month, month and day of week, in that
// order. It returns a descriptive error if the spec is not valid.
//
// It accepts
// - Standard crontab specs, e.g. "* * * * ?"
// - Descriptors, e.g. "@midnight", "@every 1h30m"
func ParseStandard(standardSpec string) (Schedule, error) {
return standardParser.Parse(standardSpec)
}
// getField returns an Int with the bits set representing all of the times that
// the field represents or error parsing field value. A "field" is a comma-separated
// list of "ranges".
func getField(field string, r bounds) (uint64, error) {
var bits uint64
ranges := strings.FieldsFunc(field, func(r rune) bool { return r == ',' })
for _, expr := range ranges {
bit, err := getRange(expr, r)
if err != nil {
return bits, err
}
bits |= bit
}
return bits, nil
}
// getRange returns the bits indicated by the given expression:
// number | number "-" number [ "/" number ]
// or error parsing range.
func getRange(expr string, r bounds) (uint64, error) {
var (
start, end, step uint
rangeAndStep = strings.Split(expr, "/")
lowAndHigh = strings.Split(rangeAndStep[0], "-")
singleDigit = len(lowAndHigh) == 1
err error
)
var extra uint64
if lowAndHigh[0] == "*" || lowAndHigh[0] == "?" {
start = r.min
end = r.max
extra = starBit
} else {
start, err = parseIntOrName(lowAndHigh[0], r.names)
if err != nil {
return 0, err
}
switch len(lowAndHigh) {
case 1:
end = start
case 2:
end, err = parseIntOrName(lowAndHigh[1], r.names)
if err != nil {
return 0, err
}
default:
return 0, fmt.Errorf("too many hyphens: %s", expr)
}
}
switch len(rangeAndStep) {
case 1:
step = 1
case 2:
step, err = mustParseInt(rangeAndStep[1])
if err != nil {
return 0, err
}
// Special handling: "N/step" means "N-max/step".
if singleDigit {
end = r.max
}
if step > 1 {
extra = 0
}
default:
return 0, fmt.Errorf("too many slashes: %s", expr)
}
if start < r.min {
return 0, fmt.Errorf("beginning of range (%d) below minimum (%d): %s", start, r.min, expr)
}
if end > r.max {
return 0, fmt.Errorf("end of range (%d) above maximum (%d): %s", end, r.max, expr)
}
if start > end {
return 0, fmt.Errorf("beginning of range (%d) beyond end of range (%d): %s", start, end, expr)
}
if step == 0 {
return 0, fmt.Errorf("step of range should be a positive number: %s", expr)
}
return getBits(start, end, step) | extra, nil
}
// parseIntOrName returns the (possibly-named) integer contained in expr.
func parseIntOrName(expr string, names map[string]uint) (uint, error) {
if names != nil {
if namedInt, ok := names[strings.ToLower(expr)]; ok {
return namedInt, nil
}
}
return mustParseInt(expr)
}
// mustParseInt parses the given expression as an int or returns an error.
func mustParseInt(expr string) (uint, error) {
num, err := strconv.Atoi(expr)
if err != nil {
return 0, fmt.Errorf("failed to parse int from %s: %s", expr, err)
}
if num < 0 {
return 0, fmt.Errorf("negative number (%d) not allowed: %s", num, expr)
}
return uint(num), nil
}
// getBits sets all bits in the range [min, max], modulo the given step size.
func getBits(min, max, step uint) uint64 {
var bits uint64
// If step is 1, use shifts.
if step == 1 {
return ^(math.MaxUint64 << (max + 1)) & (math.MaxUint64 << min)
}
// Else, use a simple loop.
for i := min; i <= max; i += step {
bits |= 1 << i
}
return bits
}
// all returns all bits within the given bounds. (plus the star bit)
func all(r bounds) uint64 {
return getBits(r.min, r.max, 1) | starBit
}
// parseDescriptor returns a predefined schedule for the expression, or error if none matches.
func parseDescriptor(descriptor string, loc *time.Location) (Schedule, error) {
switch descriptor {
case "@yearly", "@annually":
return &SpecSchedule{
Second: 1 << seconds.min,
Minute: 1 << minutes.min,
Hour: 1 << hours.min,
Dom: 1 << dom.min,
Month: 1 << months.min,
Dow: all(dow),
Location: loc,
}, nil
case "@monthly":
return &SpecSchedule{
Second: 1 << seconds.min,
Minute: 1 << minutes.min,
Hour: 1 << hours.min,
Dom: 1 << dom.min,
Month: all(months),
Dow: all(dow),
Location: loc,
}, nil
case "@weekly":
return &SpecSchedule{
Second: 1 << seconds.min,
Minute: 1 << minutes.min,
Hour: 1 << hours.min,
Dom: all(dom),
Month: all(months),
Dow: 1 << dow.min,
Location: loc,
}, nil
case "@daily", "@midnight":
return &SpecSchedule{
Second: 1 << seconds.min,
Minute: 1 << minutes.min,
Hour: 1 << hours.min,
Dom: all(dom),
Month: all(months),
Dow: all(dow),
Location: loc,
}, nil
case "@hourly":
return &SpecSchedule{
Second: 1 << seconds.min,
Minute: 1 << minutes.min,
Hour: all(hours),
Dom: all(dom),
Month: all(months),
Dow: all(dow),
Location: loc,
}, nil
}
const every = "@every "
if strings.HasPrefix(descriptor, every) {
duration, err := time.ParseDuration(descriptor[len(every):])
if err != nil {
return nil, fmt.Errorf("failed to parse duration %s: %s", descriptor, err)
}
return Every(duration), nil
}
return nil, fmt.Errorf("unrecognized descriptor: %s", descriptor)
}

View File

@ -1,383 +0,0 @@
package cron
import (
"reflect"
"strings"
"testing"
"time"
)
var secondParser = NewParser(Second | Minute | Hour | Dom | Month | DowOptional | Descriptor)
func TestRange(t *testing.T) {
zero := uint64(0)
ranges := []struct {
expr string
min, max uint
expected uint64
err string
}{
{"5", 0, 7, 1 << 5, ""},
{"0", 0, 7, 1 << 0, ""},
{"7", 0, 7, 1 << 7, ""},
{"5-5", 0, 7, 1 << 5, ""},
{"5-6", 0, 7, 1<<5 | 1<<6, ""},
{"5-7", 0, 7, 1<<5 | 1<<6 | 1<<7, ""},
{"5-6/2", 0, 7, 1 << 5, ""},
{"5-7/2", 0, 7, 1<<5 | 1<<7, ""},
{"5-7/1", 0, 7, 1<<5 | 1<<6 | 1<<7, ""},
{"*", 1, 3, 1<<1 | 1<<2 | 1<<3 | starBit, ""},
{"*/2", 1, 3, 1<<1 | 1<<3, ""},
{"5--5", 0, 0, zero, "too many hyphens"},
{"jan-x", 0, 0, zero, "failed to parse int from"},
{"2-x", 1, 5, zero, "failed to parse int from"},
{"*/-12", 0, 0, zero, "negative number"},
{"*//2", 0, 0, zero, "too many slashes"},
{"1", 3, 5, zero, "below minimum"},
{"6", 3, 5, zero, "above maximum"},
{"5-3", 3, 5, zero, "beyond end of range"},
{"*/0", 0, 0, zero, "should be a positive number"},
}
for _, c := range ranges {
actual, err := getRange(c.expr, bounds{c.min, c.max, nil})
if len(c.err) != 0 && (err == nil || !strings.Contains(err.Error(), c.err)) {
t.Errorf("%s => expected %v, got %v", c.expr, c.err, err)
}
if len(c.err) == 0 && err != nil {
t.Errorf("%s => unexpected error %v", c.expr, err)
}
if actual != c.expected {
t.Errorf("%s => expected %d, got %d", c.expr, c.expected, actual)
}
}
}
func TestField(t *testing.T) {
fields := []struct {
expr string
min, max uint
expected uint64
}{
{"5", 1, 7, 1 << 5},
{"5,6", 1, 7, 1<<5 | 1<<6},
{"5,6,7", 1, 7, 1<<5 | 1<<6 | 1<<7},
{"1,5-7/2,3", 1, 7, 1<<1 | 1<<5 | 1<<7 | 1<<3},
}
for _, c := range fields {
actual, _ := getField(c.expr, bounds{c.min, c.max, nil})
if actual != c.expected {
t.Errorf("%s => expected %d, got %d", c.expr, c.expected, actual)
}
}
}
func TestAll(t *testing.T) {
allBits := []struct {
r bounds
expected uint64
}{
{minutes, 0xfffffffffffffff}, // 0-59: 60 ones
{hours, 0xffffff}, // 0-23: 24 ones
{dom, 0xfffffffe}, // 1-31: 31 ones, 1 zero
{months, 0x1ffe}, // 1-12: 12 ones, 1 zero
{dow, 0x7f}, // 0-6: 7 ones
}
for _, c := range allBits {
actual := all(c.r) // all() adds the starBit, so compensate for that..
if c.expected|starBit != actual {
t.Errorf("%d-%d/%d => expected %b, got %b",
c.r.min, c.r.max, 1, c.expected|starBit, actual)
}
}
}
func TestBits(t *testing.T) {
bits := []struct {
min, max, step uint
expected uint64
}{
{0, 0, 1, 0x1},
{1, 1, 1, 0x2},
{1, 5, 2, 0x2a}, // 101010
{1, 4, 2, 0xa}, // 1010
}
for _, c := range bits {
actual := getBits(c.min, c.max, c.step)
if c.expected != actual {
t.Errorf("%d-%d/%d => expected %b, got %b",
c.min, c.max, c.step, c.expected, actual)
}
}
}
func TestParseScheduleErrors(t *testing.T) {
var tests = []struct{ expr, err string }{
{"* 5 j * * *", "failed to parse int from"},
{"@every Xm", "failed to parse duration"},
{"@unrecognized", "unrecognized descriptor"},
{"* * * *", "expected 5 to 6 fields"},
{"", "empty spec string"},
}
for _, c := range tests {
actual, err := secondParser.Parse(c.expr)
if err == nil || !strings.Contains(err.Error(), c.err) {
t.Errorf("%s => expected %v, got %v", c.expr, c.err, err)
}
if actual != nil {
t.Errorf("expected nil schedule on error, got %v", actual)
}
}
}
func TestParseSchedule(t *testing.T) {
tokyo, _ := time.LoadLocation("Asia/Tokyo")
entries := []struct {
parser Parser
expr string
expected Schedule
}{
{secondParser, "0 5 * * * *", every5min(time.Local)},
{standardParser, "5 * * * *", every5min(time.Local)},
{secondParser, "CRON_TZ=UTC 0 5 * * * *", every5min(time.UTC)},
{standardParser, "CRON_TZ=UTC 5 * * * *", every5min(time.UTC)},
{secondParser, "CRON_TZ=Asia/Tokyo 0 5 * * * *", every5min(tokyo)},
{secondParser, "@every 5m", ConstantDelaySchedule{5 * time.Minute}},
{secondParser, "@midnight", midnight(time.Local)},
{secondParser, "TZ=UTC @midnight", midnight(time.UTC)},
{secondParser, "TZ=Asia/Tokyo @midnight", midnight(tokyo)},
{secondParser, "@yearly", annual(time.Local)},
{secondParser, "@annually", annual(time.Local)},
{
parser: secondParser,
expr: "* 5 * * * *",
expected: &SpecSchedule{
Second: all(seconds),
Minute: 1 << 5,
Hour: all(hours),
Dom: all(dom),
Month: all(months),
Dow: all(dow),
Location: time.Local,
},
},
}
for _, c := range entries {
actual, err := c.parser.Parse(c.expr)
if err != nil {
t.Errorf("%s => unexpected error %v", c.expr, err)
}
if !reflect.DeepEqual(actual, c.expected) {
t.Errorf("%s => expected %b, got %b", c.expr, c.expected, actual)
}
}
}
func TestOptionalSecondSchedule(t *testing.T) {
parser := NewParser(SecondOptional | Minute | Hour | Dom | Month | Dow | Descriptor)
entries := []struct {
expr string
expected Schedule
}{
{"0 5 * * * *", every5min(time.Local)},
{"5 5 * * * *", every5min5s(time.Local)},
{"5 * * * *", every5min(time.Local)},
}
for _, c := range entries {
actual, err := parser.Parse(c.expr)
if err != nil {
t.Errorf("%s => unexpected error %v", c.expr, err)
}
if !reflect.DeepEqual(actual, c.expected) {
t.Errorf("%s => expected %b, got %b", c.expr, c.expected, actual)
}
}
}
func TestNormalizeFields(t *testing.T) {
tests := []struct {
name string
input []string
options ParseOption
expected []string
}{
{
"AllFields_NoOptional",
[]string{"0", "5", "*", "*", "*", "*"},
Second | Minute | Hour | Dom | Month | Dow | Descriptor,
[]string{"0", "5", "*", "*", "*", "*"},
},
{
"AllFields_SecondOptional_Provided",
[]string{"0", "5", "*", "*", "*", "*"},
SecondOptional | Minute | Hour | Dom | Month | Dow | Descriptor,
[]string{"0", "5", "*", "*", "*", "*"},
},
{
"AllFields_SecondOptional_NotProvided",
[]string{"5", "*", "*", "*", "*"},
SecondOptional | Minute | Hour | Dom | Month | Dow | Descriptor,
[]string{"0", "5", "*", "*", "*", "*"},
},
{
"SubsetFields_NoOptional",
[]string{"5", "15", "*"},
Hour | Dom | Month,
[]string{"0", "0", "5", "15", "*", "*"},
},
{
"SubsetFields_DowOptional_Provided",
[]string{"5", "15", "*", "4"},
Hour | Dom | Month | DowOptional,
[]string{"0", "0", "5", "15", "*", "4"},
},
{
"SubsetFields_DowOptional_NotProvided",
[]string{"5", "15", "*"},
Hour | Dom | Month | DowOptional,
[]string{"0", "0", "5", "15", "*", "*"},
},
{
"SubsetFields_SecondOptional_NotProvided",
[]string{"5", "15", "*"},
SecondOptional | Hour | Dom | Month,
[]string{"0", "0", "5", "15", "*", "*"},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
actual, err := normalizeFields(test.input, test.options)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if !reflect.DeepEqual(actual, test.expected) {
t.Errorf("expected %v, got %v", test.expected, actual)
}
})
}
}
func TestNormalizeFields_Errors(t *testing.T) {
tests := []struct {
name string
input []string
options ParseOption
err string
}{
{
"TwoOptionals",
[]string{"0", "5", "*", "*", "*", "*"},
SecondOptional | Minute | Hour | Dom | Month | DowOptional,
"",
},
{
"TooManyFields",
[]string{"0", "5", "*", "*"},
SecondOptional | Minute | Hour,
"",
},
{
"NoFields",
[]string{},
SecondOptional | Minute | Hour,
"",
},
{
"TooFewFields",
[]string{"*"},
SecondOptional | Minute | Hour,
"",
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
actual, err := normalizeFields(test.input, test.options)
if err == nil {
t.Errorf("expected an error, got none. results: %v", actual)
}
if !strings.Contains(err.Error(), test.err) {
t.Errorf("expected error %q, got %q", test.err, err.Error())
}
})
}
}
func TestStandardSpecSchedule(t *testing.T) {
entries := []struct {
expr string
expected Schedule
err string
}{
{
expr: "5 * * * *",
expected: &SpecSchedule{1 << seconds.min, 1 << 5, all(hours), all(dom), all(months), all(dow), time.Local},
},
{
expr: "@every 5m",
expected: ConstantDelaySchedule{time.Duration(5) * time.Minute},
},
{
expr: "5 j * * *",
err: "failed to parse int from",
},
{
expr: "* * * *",
err: "expected exactly 5 fields",
},
}
for _, c := range entries {
actual, err := ParseStandard(c.expr)
if len(c.err) != 0 && (err == nil || !strings.Contains(err.Error(), c.err)) {
t.Errorf("%s => expected %v, got %v", c.expr, c.err, err)
}
if len(c.err) == 0 && err != nil {
t.Errorf("%s => unexpected error %v", c.expr, err)
}
if !reflect.DeepEqual(actual, c.expected) {
t.Errorf("%s => expected %b, got %b", c.expr, c.expected, actual)
}
}
}
func TestNoDescriptorParser(t *testing.T) {
parser := NewParser(Minute | Hour)
_, err := parser.Parse("@every 1m")
if err == nil {
t.Error("expected an error, got none")
}
}
func every5min(loc *time.Location) *SpecSchedule {
return &SpecSchedule{1 << 0, 1 << 5, all(hours), all(dom), all(months), all(dow), loc}
}
func every5min5s(loc *time.Location) *SpecSchedule {
return &SpecSchedule{1 << 5, 1 << 5, all(hours), all(dom), all(months), all(dow), loc}
}
func midnight(loc *time.Location) *SpecSchedule {
return &SpecSchedule{1, 1, 1, all(dom), all(months), all(dow), loc}
}
func annual(loc *time.Location) *SpecSchedule {
return &SpecSchedule{
Second: 1 << seconds.min,
Minute: 1 << minutes.min,
Hour: 1 << hours.min,
Dom: 1 << dom.min,
Month: 1 << months.min,
Dow: all(dow),
Location: loc,
}
}

View File

@ -1,188 +0,0 @@
package cron
import "time"
// SpecSchedule specifies a duty cycle (to the second granularity), based on a
// traditional crontab specification. It is computed initially and stored as bit sets.
type SpecSchedule struct {
Second, Minute, Hour, Dom, Month, Dow uint64
// Override location for this schedule.
Location *time.Location
}
// bounds provides a range of acceptable values (plus a map of name to value).
type bounds struct {
min, max uint
names map[string]uint
}
// The bounds for each field.
var (
seconds = bounds{0, 59, nil}
minutes = bounds{0, 59, nil}
hours = bounds{0, 23, nil}
dom = bounds{1, 31, nil}
months = bounds{1, 12, map[string]uint{
"jan": 1,
"feb": 2,
"mar": 3,
"apr": 4,
"may": 5,
"jun": 6,
"jul": 7,
"aug": 8,
"sep": 9,
"oct": 10,
"nov": 11,
"dec": 12,
}}
dow = bounds{0, 6, map[string]uint{
"sun": 0,
"mon": 1,
"tue": 2,
"wed": 3,
"thu": 4,
"fri": 5,
"sat": 6,
}}
)
const (
// Set the top bit if a star was included in the expression.
starBit = 1 << 63
)
// Next returns the next time this schedule is activated, greater than the given
// time. If no time can be found to satisfy the schedule, return the zero time.
func (s *SpecSchedule) Next(t time.Time) time.Time {
// General approach
//
// For Month, Day, Hour, Minute, Second:
// Check if the time value matches. If yes, continue to the next field.
// If the field doesn't match the schedule, then increment the field until it matches.
// While incrementing the field, a wrap-around brings it back to the beginning
// of the field list (since it is necessary to re-verify previous field
// values)
// Convert the given time into the schedule's timezone, if one is specified.
// Save the original timezone so we can convert back after we find a time.
// Note that schedules without a time zone specified (time.Local) are treated
// as local to the time provided.
origLocation := t.Location()
loc := s.Location
if loc == time.Local {
loc = t.Location()
}
if s.Location != time.Local {
t = t.In(s.Location)
}
// Start at the earliest possible time (the upcoming second).
t = t.Add(1*time.Second - time.Duration(t.Nanosecond())*time.Nanosecond)
// This flag indicates whether a field has been incremented.
added := false
// If no time is found within five years, return zero.
yearLimit := t.Year() + 5
WRAP:
if t.Year() > yearLimit {
return time.Time{}
}
// Find the first applicable month.
// If it's this month, then do nothing.
for 1<<uint(t.Month())&s.Month == 0 {
// If we have to add a month, reset the other parts to 0.
if !added {
added = true
// Otherwise, set the date at the beginning (since the current time is irrelevant).
t = time.Date(t.Year(), t.Month(), 1, 0, 0, 0, 0, loc)
}
t = t.AddDate(0, 1, 0)
// Wrapped around.
if t.Month() == time.January {
goto WRAP
}
}
// Now get a day in that month.
//
// NOTE: This causes issues for daylight savings regimes where midnight does
// not exist. For example: Sao Paulo has DST that transforms midnight on
// 11/3 into 1am. Handle that by noticing when the Hour ends up != 0.
for !dayMatches(s, t) {
if !added {
added = true
t = time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, loc)
}
t = t.AddDate(0, 0, 1)
// Notice if the hour is no longer midnight due to DST.
// Add an hour if it's 23, subtract an hour if it's 1.
if t.Hour() != 0 {
if t.Hour() > 12 {
t = t.Add(time.Duration(24-t.Hour()) * time.Hour)
} else {
t = t.Add(time.Duration(-t.Hour()) * time.Hour)
}
}
if t.Day() == 1 {
goto WRAP
}
}
for 1<<uint(t.Hour())&s.Hour == 0 {
if !added {
added = true
t = time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), 0, 0, 0, loc)
}
t = t.Add(1 * time.Hour)
if t.Hour() == 0 {
goto WRAP
}
}
for 1<<uint(t.Minute())&s.Minute == 0 {
if !added {
added = true
t = t.Truncate(time.Minute)
}
t = t.Add(1 * time.Minute)
if t.Minute() == 0 {
goto WRAP
}
}
for 1<<uint(t.Second())&s.Second == 0 {
if !added {
added = true
t = t.Truncate(time.Second)
}
t = t.Add(1 * time.Second)
if t.Second() == 0 {
goto WRAP
}
}
return t.In(origLocation)
}
// dayMatches returns true if the schedule's day-of-week and day-of-month
// restrictions are satisfied by the given time.
func dayMatches(s *SpecSchedule, t time.Time) bool {
var (
domMatch bool = 1<<uint(t.Day())&s.Dom > 0
dowMatch bool = 1<<uint(t.Weekday())&s.Dow > 0
)
if s.Dom&starBit > 0 || s.Dow&starBit > 0 {
return domMatch && dowMatch
}
return domMatch || dowMatch
}

View File

@ -1,300 +0,0 @@
package cron
import (
"strings"
"testing"
"time"
)
func TestActivation(t *testing.T) {
tests := []struct {
time, spec string
expected bool
}{
// Every fifteen minutes.
{"Mon Jul 9 15:00 2012", "0/15 * * * *", true},
{"Mon Jul 9 15:45 2012", "0/15 * * * *", true},
{"Mon Jul 9 15:40 2012", "0/15 * * * *", false},
// Every fifteen minutes, starting at 5 minutes.
{"Mon Jul 9 15:05 2012", "5/15 * * * *", true},
{"Mon Jul 9 15:20 2012", "5/15 * * * *", true},
{"Mon Jul 9 15:50 2012", "5/15 * * * *", true},
// Named months
{"Sun Jul 15 15:00 2012", "0/15 * * Jul *", true},
{"Sun Jul 15 15:00 2012", "0/15 * * Jun *", false},
// Everything set.
{"Sun Jul 15 08:30 2012", "30 08 ? Jul Sun", true},
{"Sun Jul 15 08:30 2012", "30 08 15 Jul ?", true},
{"Mon Jul 16 08:30 2012", "30 08 ? Jul Sun", false},
{"Mon Jul 16 08:30 2012", "30 08 15 Jul ?", false},
// Predefined schedules
{"Mon Jul 9 15:00 2012", "@hourly", true},
{"Mon Jul 9 15:04 2012", "@hourly", false},
{"Mon Jul 9 15:00 2012", "@daily", false},
{"Mon Jul 9 00:00 2012", "@daily", true},
{"Mon Jul 9 00:00 2012", "@weekly", false},
{"Sun Jul 8 00:00 2012", "@weekly", true},
{"Sun Jul 8 01:00 2012", "@weekly", false},
{"Sun Jul 8 00:00 2012", "@monthly", false},
{"Sun Jul 1 00:00 2012", "@monthly", true},
// Test interaction of DOW and DOM.
// If both are restricted, then only one needs to match.
{"Sun Jul 15 00:00 2012", "* * 1,15 * Sun", true},
{"Fri Jun 15 00:00 2012", "* * 1,15 * Sun", true},
{"Wed Aug 1 00:00 2012", "* * 1,15 * Sun", true},
{"Sun Jul 15 00:00 2012", "* * */10 * Sun", true}, // verifies #70
// However, if one has a star, then both need to match.
{"Sun Jul 15 00:00 2012", "* * * * Mon", false},
{"Mon Jul 9 00:00 2012", "* * 1,15 * *", false},
{"Sun Jul 15 00:00 2012", "* * 1,15 * *", true},
{"Sun Jul 15 00:00 2012", "* * */2 * Sun", true},
}
for _, test := range tests {
sched, err := ParseStandard(test.spec)
if err != nil {
t.Error(err)
continue
}
actual := sched.Next(getTime(test.time).Add(-1 * time.Second))
expected := getTime(test.time)
if test.expected && expected != actual || !test.expected && expected == actual {
t.Errorf("Fail evaluating %s on %s: (expected) %s != %s (actual)",
test.spec, test.time, expected, actual)
}
}
}
func TestNext(t *testing.T) {
runs := []struct {
time, spec string
expected string
}{
// Simple cases
{"Mon Jul 9 14:45 2012", "0 0/15 * * * *", "Mon Jul 9 15:00 2012"},
{"Mon Jul 9 14:59 2012", "0 0/15 * * * *", "Mon Jul 9 15:00 2012"},
{"Mon Jul 9 14:59:59 2012", "0 0/15 * * * *", "Mon Jul 9 15:00 2012"},
// Wrap around hours
{"Mon Jul 9 15:45 2012", "0 20-35/15 * * * *", "Mon Jul 9 16:20 2012"},
// Wrap around days
{"Mon Jul 9 23:46 2012", "0 */15 * * * *", "Tue Jul 10 00:00 2012"},
{"Mon Jul 9 23:45 2012", "0 20-35/15 * * * *", "Tue Jul 10 00:20 2012"},
{"Mon Jul 9 23:35:51 2012", "15/35 20-35/15 * * * *", "Tue Jul 10 00:20:15 2012"},
{"Mon Jul 9 23:35:51 2012", "15/35 20-35/15 1/2 * * *", "Tue Jul 10 01:20:15 2012"},
{"Mon Jul 9 23:35:51 2012", "15/35 20-35/15 10-12 * * *", "Tue Jul 10 10:20:15 2012"},
{"Mon Jul 9 23:35:51 2012", "15/35 20-35/15 1/2 */2 * *", "Thu Jul 11 01:20:15 2012"},
{"Mon Jul 9 23:35:51 2012", "15/35 20-35/15 * 9-20 * *", "Wed Jul 10 00:20:15 2012"},
{"Mon Jul 9 23:35:51 2012", "15/35 20-35/15 * 9-20 Jul *", "Wed Jul 10 00:20:15 2012"},
// Wrap around months
{"Mon Jul 9 23:35 2012", "0 0 0 9 Apr-Oct ?", "Thu Aug 9 00:00 2012"},
{"Mon Jul 9 23:35 2012", "0 0 0 */5 Apr,Aug,Oct Mon", "Tue Aug 1 00:00 2012"},
{"Mon Jul 9 23:35 2012", "0 0 0 */5 Oct Mon", "Mon Oct 1 00:00 2012"},
// Wrap around years
{"Mon Jul 9 23:35 2012", "0 0 0 * Feb Mon", "Mon Feb 4 00:00 2013"},
{"Mon Jul 9 23:35 2012", "0 0 0 * Feb Mon/2", "Fri Feb 1 00:00 2013"},
// Wrap around minute, hour, day, month, and year
{"Mon Dec 31 23:59:45 2012", "0 * * * * *", "Tue Jan 1 00:00:00 2013"},
// Leap year
{"Mon Jul 9 23:35 2012", "0 0 0 29 Feb ?", "Mon Feb 29 00:00 2016"},
// Daylight savings time 2am EST (-5) -> 3am EDT (-4)
{"2012-03-11T00:00:00-0500", "TZ=America/New_York 0 30 2 11 Mar ?", "2013-03-11T02:30:00-0400"},
// hourly job
{"2012-03-11T00:00:00-0500", "TZ=America/New_York 0 0 * * * ?", "2012-03-11T01:00:00-0500"},
{"2012-03-11T01:00:00-0500", "TZ=America/New_York 0 0 * * * ?", "2012-03-11T03:00:00-0400"},
{"2012-03-11T03:00:00-0400", "TZ=America/New_York 0 0 * * * ?", "2012-03-11T04:00:00-0400"},
{"2012-03-11T04:00:00-0400", "TZ=America/New_York 0 0 * * * ?", "2012-03-11T05:00:00-0400"},
// hourly job using CRON_TZ
{"2012-03-11T00:00:00-0500", "CRON_TZ=America/New_York 0 0 * * * ?", "2012-03-11T01:00:00-0500"},
{"2012-03-11T01:00:00-0500", "CRON_TZ=America/New_York 0 0 * * * ?", "2012-03-11T03:00:00-0400"},
{"2012-03-11T03:00:00-0400", "CRON_TZ=America/New_York 0 0 * * * ?", "2012-03-11T04:00:00-0400"},
{"2012-03-11T04:00:00-0400", "CRON_TZ=America/New_York 0 0 * * * ?", "2012-03-11T05:00:00-0400"},
// 1am nightly job
{"2012-03-11T00:00:00-0500", "TZ=America/New_York 0 0 1 * * ?", "2012-03-11T01:00:00-0500"},
{"2012-03-11T01:00:00-0500", "TZ=America/New_York 0 0 1 * * ?", "2012-03-12T01:00:00-0400"},
// 2am nightly job (skipped)
{"2012-03-11T00:00:00-0500", "TZ=America/New_York 0 0 2 * * ?", "2012-03-12T02:00:00-0400"},
// Daylight savings time 2am EDT (-4) => 1am EST (-5)
{"2012-11-04T00:00:00-0400", "TZ=America/New_York 0 30 2 04 Nov ?", "2012-11-04T02:30:00-0500"},
{"2012-11-04T01:45:00-0400", "TZ=America/New_York 0 30 1 04 Nov ?", "2012-11-04T01:30:00-0500"},
// hourly job
{"2012-11-04T00:00:00-0400", "TZ=America/New_York 0 0 * * * ?", "2012-11-04T01:00:00-0400"},
{"2012-11-04T01:00:00-0400", "TZ=America/New_York 0 0 * * * ?", "2012-11-04T01:00:00-0500"},
{"2012-11-04T01:00:00-0500", "TZ=America/New_York 0 0 * * * ?", "2012-11-04T02:00:00-0500"},
// 1am nightly job (runs twice)
{"2012-11-04T00:00:00-0400", "TZ=America/New_York 0 0 1 * * ?", "2012-11-04T01:00:00-0400"},
{"2012-11-04T01:00:00-0400", "TZ=America/New_York 0 0 1 * * ?", "2012-11-04T01:00:00-0500"},
{"2012-11-04T01:00:00-0500", "TZ=America/New_York 0 0 1 * * ?", "2012-11-05T01:00:00-0500"},
// 2am nightly job
{"2012-11-04T00:00:00-0400", "TZ=America/New_York 0 0 2 * * ?", "2012-11-04T02:00:00-0500"},
{"2012-11-04T02:00:00-0500", "TZ=America/New_York 0 0 2 * * ?", "2012-11-05T02:00:00-0500"},
// 3am nightly job
{"2012-11-04T00:00:00-0400", "TZ=America/New_York 0 0 3 * * ?", "2012-11-04T03:00:00-0500"},
{"2012-11-04T03:00:00-0500", "TZ=America/New_York 0 0 3 * * ?", "2012-11-05T03:00:00-0500"},
// hourly job
{"TZ=America/New_York 2012-11-04T00:00:00-0400", "0 0 * * * ?", "2012-11-04T01:00:00-0400"},
{"TZ=America/New_York 2012-11-04T01:00:00-0400", "0 0 * * * ?", "2012-11-04T01:00:00-0500"},
{"TZ=America/New_York 2012-11-04T01:00:00-0500", "0 0 * * * ?", "2012-11-04T02:00:00-0500"},
// 1am nightly job (runs twice)
{"TZ=America/New_York 2012-11-04T00:00:00-0400", "0 0 1 * * ?", "2012-11-04T01:00:00-0400"},
{"TZ=America/New_York 2012-11-04T01:00:00-0400", "0 0 1 * * ?", "2012-11-04T01:00:00-0500"},
{"TZ=America/New_York 2012-11-04T01:00:00-0500", "0 0 1 * * ?", "2012-11-05T01:00:00-0500"},
// 2am nightly job
{"TZ=America/New_York 2012-11-04T00:00:00-0400", "0 0 2 * * ?", "2012-11-04T02:00:00-0500"},
{"TZ=America/New_York 2012-11-04T02:00:00-0500", "0 0 2 * * ?", "2012-11-05T02:00:00-0500"},
// 3am nightly job
{"TZ=America/New_York 2012-11-04T00:00:00-0400", "0 0 3 * * ?", "2012-11-04T03:00:00-0500"},
{"TZ=America/New_York 2012-11-04T03:00:00-0500", "0 0 3 * * ?", "2012-11-05T03:00:00-0500"},
// Unsatisfiable
{"Mon Jul 9 23:35 2012", "0 0 0 30 Feb ?", ""},
{"Mon Jul 9 23:35 2012", "0 0 0 31 Apr ?", ""},
// Monthly job
{"TZ=America/New_York 2012-11-04T00:00:00-0400", "0 0 3 3 * ?", "2012-12-03T03:00:00-0500"},
// Test the scenario of DST resulting in midnight not being a valid time.
// https://github.com/robfig/cron/issues/157
{"2018-10-17T05:00:00-0400", "TZ=America/Sao_Paulo 0 0 9 10 * ?", "2018-11-10T06:00:00-0500"},
{"2018-02-14T05:00:00-0500", "TZ=America/Sao_Paulo 0 0 9 22 * ?", "2018-02-22T07:00:00-0500"},
}
for _, c := range runs {
sched, err := secondParser.Parse(c.spec)
if err != nil {
t.Error(err)
continue
}
actual := sched.Next(getTime(c.time))
expected := getTime(c.expected)
if !actual.Equal(expected) {
t.Errorf("%s, \"%s\": (expected) %v != %v (actual)", c.time, c.spec, expected, actual)
}
}
}
func TestErrors(t *testing.T) {
invalidSpecs := []string{
"xyz",
"60 0 * * *",
"0 60 * * *",
"0 0 * * XYZ",
}
for _, spec := range invalidSpecs {
_, err := ParseStandard(spec)
if err == nil {
t.Error("expected an error parsing: ", spec)
}
}
}
func getTime(value string) time.Time {
if value == "" {
return time.Time{}
}
var location = time.Local
if strings.HasPrefix(value, "TZ=") {
parts := strings.Fields(value)
loc, err := time.LoadLocation(parts[0][len("TZ="):])
if err != nil {
panic("could not parse location:" + err.Error())
}
location = loc
value = parts[1]
}
var layouts = []string{
"Mon Jan 2 15:04 2006",
"Mon Jan 2 15:04:05 2006",
}
for _, layout := range layouts {
if t, err := time.ParseInLocation(layout, value, location); err == nil {
return t
}
}
if t, err := time.ParseInLocation("2006-01-02T15:04:05-0700", value, location); err == nil {
return t
}
panic("could not parse time value " + value)
}
func TestNextWithTz(t *testing.T) {
runs := []struct {
time, spec string
expected string
}{
// Failing tests
{"2016-01-03T13:09:03+0530", "14 14 * * *", "2016-01-03T14:14:00+0530"},
{"2016-01-03T04:09:03+0530", "14 14 * * ?", "2016-01-03T14:14:00+0530"},
// Passing tests
{"2016-01-03T14:09:03+0530", "14 14 * * *", "2016-01-03T14:14:00+0530"},
{"2016-01-03T14:00:00+0530", "14 14 * * ?", "2016-01-03T14:14:00+0530"},
}
for _, c := range runs {
sched, err := ParseStandard(c.spec)
if err != nil {
t.Error(err)
continue
}
actual := sched.Next(getTimeTZ(c.time))
expected := getTimeTZ(c.expected)
if !actual.Equal(expected) {
t.Errorf("%s, \"%s\": (expected) %v != %v (actual)", c.time, c.spec, expected, actual)
}
}
}
func getTimeTZ(value string) time.Time {
if value == "" {
return time.Time{}
}
t, err := time.Parse("Mon Jan 2 15:04 2006", value)
if err != nil {
t, err = time.Parse("Mon Jan 2 15:04:05 2006", value)
if err != nil {
t, err = time.Parse("2006-01-02T15:04:05-0700", value)
if err != nil {
panic(err)
}
}
}
return t
}
// https://github.com/robfig/cron/issues/144
func TestSlash0NoHang(t *testing.T) {
schedule := "TZ=America/New_York 15/0 * * * *"
_, err := ParseStandard(schedule)
if err == nil {
t.Error("expected an error on 0 increment")
}
}

View File

@ -1,38 +1,120 @@
package db
import (
//"database/sql"
"context"
"database/sql"
"fmt"
"reflect"
"server/GoUtil"
"server/MergeConst"
"server/conf"
"server/pkg/github.com/name5566/leaf/log"
GoUtil "server/game_util"
"strings"
"sync"
"time"
"gitea.bywaystudios.com/pet_home/leaf/log"
// "server/game"
_ "github.com/go-sql-driver/mysql"
"github.com/jmoiron/sqlx"
)
type user struct {
Id int `db:"user_id"`
Sex int `db:"sex"`
UserName string `db:"username"`
Email string `db:"email"`
var SqlDb *sqlx.DB
var sqlDbMu sync.RWMutex
// GetDB 线程安全地获取数据库连接
func GetDB() *sqlx.DB {
sqlDbMu.RLock()
defer sqlDbMu.RUnlock()
return SqlDb
}
var SqlDb *sqlx.DB
// GetDBOrPanic 获取数据库连接,如果为 nil 则记录错误
func GetDBOrPanic() *sqlx.DB {
db := GetDB()
if db == nil {
log.Error("Database connection is nil, please check database initialization")
}
return db
}
func InitDB() {
//"用户名:密码@[连接方式](主机名:端口号)/数据库名"
connect := fmt.Sprintf("%s:%s@(%s:%s)/%s", conf.Server.MySqlUsr, conf.Server.MySqlPwd, conf.Server.MySqlAddr, conf.Server.MySqlPort, conf.Server.DbName)
SqlDb = sqlx.MustConnect("mysql", connect) // 设置连接数据库的参数
SqlDb.SetMaxOpenConns(20) // 设置最大打开的连接数
log.Debug("connect mysql success")
// EnsureDB 确保数据库连接可用,如果不可用则返回错误
func EnsureDB() (*sqlx.DB, error) {
db := GetDB()
if db == nil {
return nil, fmt.Errorf("database connection is nil")
}
if err := db.Ping(); err != nil {
return nil, fmt.Errorf("database ping failed: %w", err)
}
return db, nil
}
// 封装创建连接
func connectMySQL() (*sqlx.DB, error) {
MysqlPwd, _ := GoUtil.Decrypt(conf.Server.MySqlPwd)
// 减少超时时间,避免长时间阻塞
connect := fmt.Sprintf("%s:%s@(%s:%s)/%s?timeout=10s&readTimeout=15s&writeTimeout=15s&parseTime=true", conf.Server.MySqlUsr, MysqlPwd, conf.Server.MySqlAddr, conf.Server.MySqlPort, conf.Server.DbName)
db, err := sqlx.Connect("mysql", connect)
if err != nil {
return nil, err
}
// 增加最大连接数,减少连接等待时间
db.SetMaxOpenConns(100)
db.SetMaxIdleConns(20)
db.SetConnMaxLifetime(30 * time.Minute) // 减少连接生命周期
db.SetConnMaxIdleTime(5 * time.Minute) // 减少空闲时间
return db, nil
}
func InitDB(ctx context.Context) {
if err := ReconnectDB(); err != nil {
log.Debug("connect mysql failed: %v", err)
}
// 定时检测与重连
go func(ctx context.Context) {
ticker := time.NewTicker(5 * time.Second) // 改为5秒检测一次降低频率
defer ticker.Stop()
for {
select {
case <-ctx.Done():
return
case <-ticker.C:
if _, err := EnsureDB(); err != nil {
log.Debug("mysql health check failed: %v, start reconnect", err)
if err := ReconnectDB(); err != nil {
log.Debug("mysql reconnect failed: %v", err)
}
}
}
}
}(ctx)
}
// 自动重连
func ReconnectDB() error {
newDb, err := connectMySQL()
if err != nil {
return err
}
sqlDbMu.Lock()
oldDb := SqlDb
SqlDb = newDb
sqlDbMu.Unlock()
if oldDb != nil {
_ = oldDb.Close()
}
log.Debug("mysql reconnect success")
return nil
}
func SeriesTransaction(sqlstrs []string, params [][]any) (err error) {
tx, err := SqlDb.Begin()
sqlDb := GetDB()
if sqlDb == nil {
return fmt.Errorf("database connection is nil")
}
tx, err := sqlDb.Begin()
if err != nil {
log.Debug("Transaction failed, err:%v\n", err)
return err
@ -60,61 +142,6 @@ func SeriesTransaction(sqlstrs []string, params [][]any) (err error) {
return
}
// 更新数据
func FormatUpdateOneAttrRow(u interface{}, tableName string, UpdateAttr string, Exclude string) (err error) {
t := reflect.TypeOf(u)
len1 := t.Elem().NumField()
Fields := make([]string, len1)
Values := make([]interface{}, len1)
pp := reflect.ValueOf(u)
origin := "UPDATE " + tableName + " SET "
index := 0
var keyValue interface{}
for i := 0; i < len1; i++ {
field := t.Elem().Field(i)
if field.Tag.Get("db") != Exclude {
if field.Tag.Get("db") == UpdateAttr {
Fields[index] = field.Tag.Get("db") + " = ?"
ufield := pp.Elem().FieldByName(field.Name)
k := ufield.Kind()
if k == reflect.String {
Values[index] = ufield.String()
}
if k == reflect.Int32 {
Values[index] = ufield.Int()
}
index++
}
} else {
ufield := pp.Elem().FieldByName(field.Name)
k := ufield.Kind()
if k == reflect.String {
keyValue = ufield.String()
}
if k == reflect.Int32 {
keyValue = ufield.Int()
}
}
}
Values[index] = keyValue
// Values = append(Values, keyValue)
origin += strings.Join(Fields, ",")
strLen := len(origin)
origin = origin[:strLen-1]
origin = origin + " WHERE " + Exclude + " = ?"
sqlStr := origin
_, err = SqlDb.Exec(sqlStr, Values...)
if err != nil {
log.Debug("table: %s, sql :%s, exec failed, err:%v\n", tableName, sqlStr, err)
return
}
return
}
// "UPDATE user SET age = ?, degree = ? WHERE id = ?"
func FormatAllMemUpdateDb(u interface{}, tableName string, Exclude string) (err error) {
t := reflect.TypeOf(u)
@ -145,7 +172,7 @@ func FormatAllMemUpdateDb(u interface{}, tableName string, Exclude string) (err
if k == reflect.String {
keyValue = ufield.String()
}
if k == reflect.Int32 || k == reflect.Int {
if k == reflect.Int32 || k == reflect.Int || k == reflect.Int64 {
keyValue = ufield.Int()
}
}
@ -187,6 +214,9 @@ func FormatAllMemInsertDb(u interface{}, tableName string) (insertID int64, err
if k == reflect.Int32 {
Values[i] = ufield.Int()
}
if k == reflect.Int64 {
Values[i] = ufield.Int()
}
}
for i := range Fields {
Fields[i] = "`" + Fields[i] + "`"
@ -219,6 +249,28 @@ func GetPlayerBaseInfoFromDbByName(name string) *ResPlayerBaseInfo {
return &res
}
func GetPlayerBan(name string) int64 {
sqlStr := "SELECT ban FROM t_player_baseinfo WHERE user_name = ?"
var ban int64
if err := SqlDb.Get(&ban, sqlStr, name); err != nil {
log.Debug("table: %s, sql :%s, exec failed, err:%v\n", "PlayerBaseInfo", sqlStr, err)
return 0
}
return ban
}
func UpdatePlayerBan(uid int64, ban int64) error {
sqlStr := "UPDATE t_player_baseinfo SET ban = ? WHERE dwUin = ?"
_, err := SqlDb.Exec(sqlStr, ban, uid)
return err
}
func UpdatePlayerBaseInfoName(oldName, newName string) error {
sqlStr := "UPDATE t_player_baseinfo SET user_name = ? WHERE user_name = ?"
_, err := SqlDb.Exec(sqlStr, newName, oldName)
return err
}
func GetPlayerBaseInfoFromDbById(id int32) *ResPlayerBaseInfo {
sqlStr := "SELECT * FROM t_player_baseinfo WHERE dwUin = ?"
var res ResPlayerBaseInfo
@ -233,207 +285,125 @@ func GetAccountInfoFromDb(name string) *Db_Account {
sqlStr := "SELECT * FROM t_account WHERE user_name = ?"
var res Db_Account
if err := SqlDb.Get(&res, sqlStr, name); err != nil {
log.Debug("table: %s, sql :%s, exec failed, err:%v\n", "account", sqlStr, err)
log.Debug("登录的账号不存在:%s", name)
return nil
}
return &res
}
func FormatAllMemLoadDb(u interface{}, tableName string, Exclude string) (err error) {
t := reflect.TypeOf(u)
len := t.Elem().NumField()
Fields := make([]string, len)
Values := make([]interface{}, len)
pp := reflect.ValueOf(u)
origin := "SELECT * FROM " + tableName + " WHERE "
index := 0
var keyValue interface{}
for i := 0; i < len; i++ {
field := t.Elem().Field(i)
if field.Tag.Get("db") != Exclude {
Fields[index] = field.Tag.Get("db") + " = ?"
ufield := pp.Elem().FieldByName(field.Name)
k := ufield.Kind()
if k == reflect.String {
Values[index] = ufield.String()
}
if k == reflect.Int32 || k == reflect.Int || k == reflect.Int64 {
Values[index] = ufield.Int()
}
index++
} else {
ufield := pp.Elem().FieldByName(field.Name)
k := ufield.Kind()
if k == reflect.String {
keyValue = ufield.String()
}
if k == reflect.Int32 {
keyValue = ufield.Int()
}
}
}
Values[index] = keyValue
// Values = append(Values, keyValue)
origin += strings.Join(Fields, " AND ")
sqlStr := origin
if err := SqlDb.Get(u, sqlStr, Values...); err != nil {
log.Debug("table: %s, sql :%s, exec failed, err:%v\n", tableName, sqlStr, err)
func ResetAccountData(oldName, newName string) error {
sqlStr := "UPDATE t_account SET user_name = ? WHERE user_name = ?"
_, err := SqlDb.Exec(sqlStr, newName, oldName)
if err != nil {
return err
}
sqlStr = "UPDATE t_player_baseinfo SET user_name = ? WHERE user_name = ?"
_, err = SqlDb.Exec(sqlStr, newName, oldName)
return err
}
func MappingAccountData(oldName, newName string) error {
sqlStr := "UPDATE t_account SET user_name = ? WHERE user_name = ?"
_, err := SqlDb.Exec(sqlStr, "", oldName)
if err != nil {
return err
}
sqlStr = "UPDATE t_player_baseinfo SET user_name = ? WHERE user_name = ?"
_, err = SqlDb.Exec(sqlStr, "", oldName)
if err != nil {
return err
}
sqlStr = "UPDATE t_account SET user_name = ? WHERE user_name = ?"
_, err = SqlDb.Exec(sqlStr, oldName, newName)
if err != nil {
return err
}
sqlStr = "UPDATE t_player_baseinfo SET user_name = ? WHERE user_name = ?"
_, err = SqlDb.Exec(sqlStr, oldName, newName)
return err
}
func UpdateAccountInfoToDb(account *Db_Account) (err error) {
_, err = SqlDb.Exec("UPDATE t_account SET user_password = ? WHERE user_name = ?", account.UserPassword, account.UserName)
return
}
func UpdateAccountInfoName(account *Db_Account, newName string) (err error) {
_, err = SqlDb.Exec("UPDATE t_account SET user_name = ? WHERE user_name = ?", newName, account.UserName)
return
}
func UpdateAccountInfoDeviceToDb(account *Db_Account) (err error) {
_, err = SqlDb.Exec("UPDATE t_account SET device_id = ? WHERE user_name = ?", account.DeviceId, account.UserName)
return
}
func GetServerData(d interface{}, Key string) (err error) {
sqlDb := GetDB()
if sqlDb == nil {
return fmt.Errorf("database connection is nil")
}
sql := "select * from t_server_mod where `key` = ?"
err = SqlDb.Get(d, sql, Key)
err = sqlDb.Get(d, sql, Key)
return
}
func SaveServerData(data *SqlServerModStruct) error {
sqlDb := GetDB()
if sqlDb == nil {
return fmt.Errorf("database connection is nil")
}
sql := "update t_server_mod set `mData` = ? , `updateTime` = ? where `key` = ?"
_, err := SqlDb.Exec(sql, data.ModData, data.UpdataTime, data.Key)
_, err := sqlDb.Exec(sql, data.ModData, data.UpdataTime, data.Key)
return err
}
func SaveServerDataWithTx(tx *sql.Tx, data *SqlServerModStruct) error {
sql := "update t_server_mod set `mData` = ? , `updateTime` = ? where `key` = ?"
_, err := tx.Exec(sql, data.ModData, data.UpdataTime, data.Key)
return err
}
func InsertServerData(data *SqlServerModStruct) error {
sqlDb := GetDB()
if sqlDb == nil {
return fmt.Errorf("database connection is nil")
}
sql := "insert into t_server_mod (`mData` , `updateTime` ,`key`) Values (?,?,?)"
_, err := SqlDb.Exec(sql, data.ModData, data.UpdataTime, data.Key)
_, err := sqlDb.Exec(sql, data.ModData, data.UpdataTime, data.Key)
return err
}
func SavePlayerModData(data *SqlModStruct) error {
sql := "INSERT INTO `t_player_mod` (`mData` , `updateTime` ,`dwUin`) Values (?,?,?) ON DUPLICATE KEY UPDATE `mData` = ? , `updateTime` = ? "
tableName := "t_player_mod_" + fmt.Sprintf("%02d", data.DwUin%100)
sql := "INSERT INTO `" + tableName + "` (`mData` , `updateTime` ,`dwUin`) Values (?,?,?) ON DUPLICATE KEY UPDATE `mData` = ? , `updateTime` = ? "
_, err := SqlDb.Exec(sql, data.ModData, data.UpdataTime, data.DwUin, data.ModData, data.UpdataTime)
return err
}
func InsertPlayerModData(data *SqlModStruct) error {
sql := "insert into t_player_mod (`mData` , `updateTime` ,`dwUin`) Values (?,?,?)"
tableName := "t_player_mod_" + fmt.Sprintf("%02d", data.DwUin%100)
sql := "insert into `" + tableName + "` (`mData` , `updateTime` ,`dwUin`) Values (?,?,?)"
_, err := SqlDb.Exec(sql, data.ModData, data.UpdataTime, data.DwUin)
return err
}
func SavePlayerClientData(data *SqlModStruct) error {
sql := "INSERT INTO `t_player_client_data` (`mData` , `updateTime` ,`dwUin`) Values (?,?,?) ON DUPLICATE KEY UPDATE `mData` = ? , `updateTime` = ? "
_, err := SqlDb.Exec(sql, data.ModData, data.UpdataTime, data.DwUin, data.ModData, data.UpdataTime)
return err
}
func GetPlayerClientData(d interface{}, Key string) (err error) {
sql := "select * from t_server_mod where `dwUin` = ?"
err = SqlDb.Get(d, sql, Key)
return
}
func InsertData(u interface{}, tableName string) (insertID int64, err error) {
t := reflect.TypeOf(u)
len := t.Elem().NumField()
Fields := make([]string, 0)
Fields1 := make([]string, 0)
Values := make([]interface{}, 0)
pp := reflect.ValueOf(u)
origin := "INSERT INTO " + tableName + "("
for i := 0; i < len; i++ {
field := t.Elem().Field(i)
if field.Tag.Get("db") == "id" {
continue
}
ufield := pp.Elem().FieldByName(field.Name)
k := ufield.Kind()
if k == reflect.String && ufield.String() == "" {
continue
}
if (k == reflect.Int32 || k == reflect.Int || k == reflect.Int64) && ufield.Int() == 0 {
continue
}
if k == reflect.String {
Values = append(Values, ufield.String())
}
if k == reflect.Int32 || k == reflect.Int || k == reflect.Int64 {
Values = append(Values, ufield.Int())
}
Fields = append(Fields, field.Tag.Get("db"))
Fields1 = append(Fields1, "?")
}
for i := range Fields {
Fields[i] = "`" + Fields[i] + "`"
}
origin += strings.Join(Fields, ",")
origin += ") VALUES("
origin += strings.Join(Fields1, ",")
origin += ")"
sqlStr := origin
result, err := SqlDb.Exec(sqlStr, Values...)
if err != nil {
log.Debug("InsertData exec failed, sql : %s ;err:%v\n", sqlStr, err)
return
}
insertID, err = result.LastInsertId()
if err != nil {
log.Debug("InsertData exec failed, err:%v\n", err)
return
}
return
}
func UpdateData(u interface{}, tableName string, Exclude string) (err error) {
t := reflect.TypeOf(u)
len1 := t.Elem().NumField()
Fields := make([]string, 0)
Values := make([]interface{}, 0)
pp := reflect.ValueOf(u)
origin := "UPDATE " + tableName + " SET "
var ExcludeValue interface{}
for i := 0; i < len1; i++ {
field := t.Elem().Field(i)
ufield := pp.Elem().FieldByName(field.Name)
k := ufield.Kind()
if field.Tag.Get("db") != Exclude && field.Tag.Get("db") != "id" {
if k == reflect.String {
Values = append(Values, ufield.String())
}
if k == reflect.Int32 || k == reflect.Int || k == reflect.Int64 {
Values = append(Values, ufield.Int())
}
Fields = append(Fields, "`"+field.Tag.Get("db")+"` = ?")
}
if field.Tag.Get("db") == Exclude {
if k == reflect.String {
ExcludeValue = ufield.String()
}
if k == reflect.Int32 || k == reflect.Int || k == reflect.Int64 {
ExcludeValue = append(Values, ufield.Int())
}
}
}
Values = append(Values, ExcludeValue)
origin += strings.Join(Fields, ",")
origin = origin + " WHERE `" + Exclude + "` = ?"
sqlStr := origin
_, err = SqlDb.Exec(sqlStr, Values...)
if err != nil {
log.Debug("update failed, sql %s err:%v\n", sqlStr, err)
return
}
return
}
func GetServerMailData(data *[]*SqlServerMailStruct) error {
sql := "select * from system_mail_info"
sql := "select `mail_id`, `title`, `content`, `subTitle`, `subTitle_en`, `title_en`, `content_en`, `title_ptbr`, `subTitle_ptbr`, `content_ptbr`, `title_es_latam`, `subTitle_es_latam`, `content_es_latam`, `items`, `start_time`, `register_time`, `end_time`, `mail_type`, `send_type`, `to_uids`, `create_time`, `level` from system_mail_info"
err := SqlDb.Select(data, sql)
return err
}
func CreateOrderSn(Uid, ChargeId int, OrderSn, Platform, Channel string, Price float64, Currency string) error {
sql := "insert into t_player_charge (`Uid`,`OrderId`, `ProductId`, `Price`,`Currency`, `CreateTime`, `PayPlatform`, `PayChannel`) Values (?,?,?,?,?,?,?,?)"
func GetActivityData(data *[]*SqlActivityCfgStruct) error {
sql := "select `id`, `type`, `title`, `mail_title`, `mail_content`, `level_limit`, `start_time`, `end_time`, `cfg`, `extra`, `interval` from t_activity_mod"
err := SqlDb.Select(data, sql)
return err
}
func CreateOrderSn(Uid, ChargeId int, OrderSn, Platform, Channel string, Price float64, Currency, Extra string) error {
sql := "insert into t_player_charge (`Uid`,`OrderId`, `ProductId`, `Price`,`Currency`, `CreateTime`, `PayPlatform`, `PayChannel`, `PayChannelExtra`) Values (?,?,?,?,?,?,?,?,?)"
Now := GoUtil.Now()
_, err := SqlDb.Exec(sql, Uid, OrderSn, ChargeId, Price, Currency, Now, Platform, Channel)
_, err := SqlDb.Exec(sql, Uid, OrderSn, ChargeId, Price, Currency, Now, Platform, Channel, Extra)
return err
}
@ -444,8 +414,49 @@ func GetPlayerChargeData(OrderSn string) (*SqlChargeOrderStruct, error) {
return data, err
}
func GetPlayerChargeDataList(Uid int) ([]*SqlChargeOrderStruct, error) {
sql := "select * from t_player_charge where Uid = ? and PayStatus = ?"
data := &[]*SqlChargeOrderStruct{}
err := SqlDb.Select(data, sql, Uid, MergeConst.ORDER_STATUS_PAY)
return *data, err
}
func GetPlayerPayChannelOrderId(OrderSn string) (*SqlChargeOrderStruct, error) {
sql := "select * from t_player_charge where PayChannelOrderId = ?"
data := &SqlChargeOrderStruct{}
err := SqlDb.Get(data, sql, OrderSn)
return data, err
}
func UpdatePlayerChargeData(data *SqlChargeOrderStruct) error {
sql := "update t_player_charge set PayTime = ?, PayStatus = ?, PayChannelOrderId = ? where OrderId = ?"
_, err := SqlDb.Exec(sql, data.PayTime, data.PayStatus, data.PayChannelOrderId, data.OrderId)
return err
}
func SearchPlayer(key string) ([]*ResPlayerBaseInfo, error) {
sql := "select * from t_player_baseinfo where nick_name like ? limit 10"
data := &[]*ResPlayerBaseInfo{}
err := SqlDb.Select(data, sql, "%"+key+"%")
return *data, err
}
func GetCommendPlayerFromDb(uid, login int64, level int) ([]int, error) {
sqlStr := "SELECT dwUin FROM t_player_baseinfo WHERE dwUin != ? AND logout_time > ? AND level >= ? ORDER BY logout_time DESC LIMIT 1000"
var res []int
if err := SqlDb.Select(&res, sqlStr, uid, login, level); err != nil {
log.Debug("table: %s, sql :%s, exec failed, err:%v\n", "PlayerBaseInfo", sqlStr, err)
return nil, err
}
return res, nil
}
func GetDebugPlayer(uid int) ([]int, error) {
sqlStr := "SELECT dwUin FROM t_player_baseinfo WHERE dwUin != ? ORDER BY login_time DESC LIMIT 1000"
var res []int
if err := SqlDb.Select(&res, sqlStr, uid); err != nil {
log.Debug("table: %s, sql :%s, exec failed, err:%v\n", "PlayerBaseInfo", sqlStr, err)
return nil, err
}
return res, nil
}

View File

@ -2,43 +2,130 @@ package db
import (
"context"
"fmt"
"server/conf"
"server/pkg/github.com/name5566/leaf/log"
"strings"
"time"
"gitea.bywaystudios.com/pet_home/leaf/log"
"github.com/redis/go-redis/v9"
)
var ctx = context.Background()
var Rdb *redis.Client
var RdbWrite *redis.Client
var RdbRead *redis.Client
func InitRedis() {
rdb := redis.NewClient(&redis.Options{
Addr: conf.Server.RedisAddr + ":" + conf.Server.RedisPort,
Password: conf.Server.RedisPwd, // no password set
DB: 0,
})
_, err := rdb.Ping(ctx).Result()
if err != nil {
log.Debug("连接redis出错错误信息%v", err)
return
// helper: 创建单个客户端addr 可以为 host:port 或 host
func connectClient(addr string) (*redis.Client, error) {
if addr == "" {
return nil, nil
}
log.Debug("成功连接redis")
Rdb = rdb
// 如果没有端口且配置了旧的 RedisPort则尝试补端口
if !strings.Contains(addr, ":") && conf.Server.RedisPort != "" {
addr = addr + ":" + conf.Server.RedisPort
}
rdb := redis.NewClient(&redis.Options{
Addr: addr,
Password: conf.Server.RedisPwd,
DB: conf.Server.RedisDb,
})
if _, err := rdb.Ping(ctx).Result(); err != nil {
return nil, err
}
return rdb, nil
}
// InitRedis: 初始化读写分离客户端(向后兼容旧配置)
func InitRedis() {
// 决定写地址:优先使用 RedisWriteAddr其次使用旧的 RedisAddr:RedisPort
writeAddr := conf.Server.RedisWriteAddr
if writeAddr == "" {
if conf.Server.RedisAddr != "" {
writeAddr = conf.Server.RedisAddr
if conf.Server.RedisPort != "" && !strings.Contains(writeAddr, ":") {
writeAddr = writeAddr + ":" + conf.Server.RedisPort
}
}
}
// 决定读地址:优先使用 RedisReadAddrs逗号分隔若为空则回退到写地址
readAddrs := conf.Server.RedisReadAddrs
if strings.TrimSpace(readAddrs) == "" {
readAddrs = writeAddr
}
// 取第一个可用的只读地址(简单实现)
var readClient *redis.Client
for _, a := range strings.Split(readAddrs, ",") {
a = strings.TrimSpace(a)
if a == "" {
continue
}
c, err := connectClient(a)
if err == nil {
readClient = c
break
}
log.Debug("connect read addr %s failed: %v", a, err)
}
// 如果所有只读都不可用,尝试连接写地址作为回退
writeClient, err := connectClient(writeAddr)
if err != nil {
log.Debug("连接redis写节点出错错误信息%v", err)
// 若读已连上则也作为写回退,否则返回
if readClient != nil {
RdbWrite = readClient
RdbRead = readClient
log.Debug("只有只读节点可用,读写共用该节点")
return
}
return
}
// 如果读未连接成功,读回退到写
if readClient == nil {
readClient = writeClient
}
RdbWrite = writeClient
RdbRead = readClient
log.Debug("成功初始化 redis读写分离写: %v, 读: %v", writeAddr, readAddrs)
}
// 写操作使用 RdbWrite
func RedisSetKey(key string, value string, expiration time.Duration) {
err := Rdb.Set(ctx, key, value, expiration).Err()
if RdbWrite == nil {
log.Debug("redis write client is nil")
return
}
err := RdbWrite.Set(ctx, key, value, expiration).Err()
if err != nil {
log.Debug("redis set failed, err:%v\n", err)
}
}
// 获取锁
// 新增:写入字节数据,避免 string 转换拷贝
func RedisSetKeyBytes(key string, value []byte, expiration time.Duration) {
if RdbWrite == nil {
log.Debug("redis write client is nil")
return
}
err := RdbWrite.Set(ctx, key, value, expiration).Err()
if err != nil {
log.Debug("redis set failed, err:%v\n", err)
}
}
// 获取锁(写)
func RedisLock(key string, value string, expiration time.Duration) bool {
ok, err := Rdb.SetNX(ctx, key, value, expiration).Result()
if RdbWrite == nil {
log.Debug("redis write client is nil")
return false
}
ok, err := RdbWrite.SetNX(ctx, key, value, expiration).Result()
if err != nil {
log.Debug("redis lock failed, err:%v\n", err)
return false
@ -46,8 +133,12 @@ func RedisLock(key string, value string, expiration time.Duration) bool {
return ok
}
// 释放锁
// 释放锁(写)
func RedisUnlock(key string, value string) bool {
if RdbWrite == nil {
log.Debug("redis write client is nil")
return false
}
script := `
if redis.call("GET", KEYS[1]) == ARGV[1] then
return redis.call("DEL", KEYS[1])
@ -55,7 +146,7 @@ func RedisUnlock(key string, value string) bool {
return 0
end
`
result, err := Rdb.Eval(ctx, script, []string{key}, value).Result()
result, err := RdbWrite.Eval(ctx, script, []string{key}, value).Result()
if err != nil {
log.Debug("redis unlock failed, err:%v\n", err)
return false
@ -63,25 +154,219 @@ func RedisUnlock(key string, value string) bool {
return result.(int64) == 1
}
// 读操作使用 RdbRead
func RedisGetKey(key string) (string, error) {
val, err := Rdb.Get(ctx, key).Result()
if RdbRead == nil {
return "", nil
}
val, err := RdbRead.Get(ctx, key).Result()
if err != nil {
log.Debug("redis get failed, err:%v\n", err)
return "", err
}
return val, nil
}
func RedisGetKeyBytes(key string) ([]byte, error) {
if RdbRead == nil {
return nil, nil
}
val, err := RdbRead.Get(ctx, key).Bytes()
if err != nil {
return nil, err
}
return val, nil
}
func RedisDelKey(key string) {
err := Rdb.Del(ctx, key).Err()
if RdbWrite == nil {
log.Debug("redis write client is nil")
return
}
err := RdbWrite.Del(ctx, key).Err()
if err != nil {
log.Debug("redis del failed, err:%v\n", err)
}
}
func RedisZAdd(key string, member string, score float64) {
err := Rdb.ZAdd(ctx, key, redis.Z{Score: score, Member: member}).Err()
if RdbWrite == nil {
log.Debug("redis write client is nil")
return
}
err := RdbWrite.ZAdd(ctx, key, redis.Z{Score: score, Member: member}).Err()
if err != nil {
log.Debug("redis zadd failed, err:%v\n", err)
return
}
const maxRankSize int64 = 10000
count, err := RdbWrite.ZCard(ctx, key).Result()
if err != nil {
log.Debug("redis zcard failed, err:%v\n", err)
return
}
if count > maxRankSize {
removeCount := count - maxRankSize
err = RdbWrite.ZRemRangeByRank(ctx, key, 0, removeCount-1).Err()
if err != nil {
log.Debug("redis zremrangebyrank failed, err:%v\n", err)
}
}
}
func RedisZRangeWithScores(key string, start, stop int64) ([]redis.Z, error) {
if RdbRead == nil {
return nil, nil
}
val, err := RdbRead.ZRangeWithScores(ctx, key, start, stop).Result()
if err != nil {
return nil, err
}
return val, nil
}
func RedisZRevRangeWithScores(key string, start, stop int64) ([]redis.Z, error) {
if RdbRead == nil {
return nil, nil
}
val, err := RdbRead.ZRevRangeWithScores(ctx, key, start, stop).Result()
if err != nil {
return nil, err
}
return val, nil
}
func RedisZGetByIndexWithScore(key string, index int64) (*redis.Z, error) {
if RdbRead == nil {
return nil, nil
}
val, err := RdbRead.ZRangeWithScores(ctx, key, index, index).Result()
if err != nil {
return nil, err
}
if len(val) == 0 {
return nil, nil
}
return &val[0], nil
}
func RedisZRankWithScores(key, member string) (int64, float64, error) {
if RdbRead == nil {
return 0, 0, nil
}
val, err := RdbRead.ZRank(ctx, key, member).Result()
if err != nil {
return 0, 0, err
}
score, err := RdbRead.ZScore(ctx, key, member).Result()
if err != nil {
return 0, 0, err
}
return val, score, nil
}
func RedisZCard(key string) (int64, error) {
if RdbRead == nil {
return 0, nil
}
val, err := RdbRead.ZCard(ctx, key).Result()
if err != nil {
return 0, err
}
return val, nil
}
func RedisDel(key string) {
if RdbWrite == nil {
log.Debug("redis write client is nil")
return
}
err := RdbWrite.Del(ctx, key).Err()
if err != nil {
log.Debug("redis del failed, err:%v\n", err)
}
}
func RedisSetNx(key string, value string, expiration time.Duration) bool {
if RdbWrite == nil {
log.Debug("redis write client is nil")
return false
}
ok, err := RdbWrite.SetNX(ctx, key, value, expiration).Result()
if err != nil {
log.Debug("redis setnx failed, err:%v\n", err)
return false
}
return ok
}
func RedisSetKeyInt(key string, value int, expiration time.Duration) {
if RdbWrite == nil {
log.Debug("redis write client is nil")
return
}
err := RdbWrite.Set(ctx, key, value, expiration).Err()
if err != nil {
log.Debug("redis set failed, err:%v\n", err)
}
}
func RedisGetKeyInt(key string) (int, error) {
if RdbRead == nil {
return 0, nil
}
val, err := RdbRead.Get(ctx, key).Int()
if err != nil {
return 0, err
}
return val, nil
}
func RedisIncrBy(key string, increment int64) (int64, error) {
if RdbWrite == nil {
log.Debug("redis write client is nil")
return 0, nil
}
val, err := RdbWrite.IncrBy(ctx, key, increment).Result()
if err != nil {
return 0, err
}
return val, nil
}
func RedisLPush(key string, values ...interface{}) error {
if RdbWrite == nil {
return fmt.Errorf("redis write client is nil")
}
const maxListSize int64 = 10000
pipe := RdbWrite.TxPipeline()
pipe.LPush(ctx, key, values...)
pipe.LTrim(ctx, key, 0, maxListSize-1)
_, err := pipe.Exec(ctx)
if err != nil {
log.Debug("redis lpush failed, err:%v\n", err)
return err
}
return nil
}
func RedisLRange(key string, start, stop int64) ([]string, error) {
if RdbRead == nil {
return nil, nil
}
val, err := RdbRead.LRange(ctx, key, start, stop).Result()
if err != nil {
return nil, err
}
return val, nil
}
func RedisLLen(key string) (int64, error) {
if RdbRead == nil {
return 0, nil
}
val, err := RdbRead.LLen(ctx, key).Result()
if err != nil {
return 0, err
}
return val, nil
}

View File

@ -20,10 +20,12 @@ type Db_Account struct {
Channel string `db:"channel"`
DeviceId string `db:"device_id"`
AutoId int `db:"auto_id"`
IdName string `db:"id_name"`
IdNum string `db:"id_num"`
}
type ResPlayerBaseInfo struct {
DwUin int32 `db:"dwUin"`
DwUin int64 `db:"dwUin"`
Energy int32 `db:"energy"`
Star int32 `db:"star"`
RecoverTime int32 `db:"recover_time"`
@ -35,11 +37,12 @@ type ResPlayerBaseInfo struct {
Guild int32 `db:"guild"`
PackUnlockCount int32 `db:"pack_unlock_count"`
LastPlayTime int32 `db:"last_play_time"`
EnergyBuyCount int32 `db:"EnergyBuyCount"`
Ban int64 `db:"ban"`
UserName string `db:"user_name"`
NickName string `db:"nick_name"`
LoginTime int32 `db:"login_time"`
LogoutTime int32 `db:"logout_time"`
Todayolinetime int32 `db:"todayolinetime"`
Node int32 `db:"node"`
Rolecreatetime int32 `db:"rolecreatetime"`
EmitOrderCnt int32 `db:"EmitOrderCnt"`
DailyRenewTime int32 `db:"DailyRenewTime"`
@ -458,7 +461,7 @@ type SqlAddFriendStruct struct {
}
type SqlModStruct struct {
DwUin int32 `db:"dwUin"`
DwUin int64 `db:"dwUin"`
ModData []byte `db:"mData"`
UpdataTime int32 `db:"updateTime"`
}
@ -471,15 +474,42 @@ type SqlServerModStruct struct {
}
type SqlServerMailStruct struct {
Id int `db:"mail_id"`
Title string `db:"title"`
Content string `db:"content"`
Items string `db:"items"`
Start_time int64 `db:"start_time"`
Register_time int64 `db:"register_time"`
End_time int64 `db:"end_time"`
Mail_type int `db:"mail_type"`
To_uids string `db:"to_uids"`
Id int `db:"mail_id"`
Title string `db:"title"`
Content string `db:"content"`
SubTitle string `db:"subTitle"`
SubTitleEn string `db:"subTitle_en"`
TitleEn string `db:"title_en"`
ContentEn string `db:"content_en"`
TitlePtBr string `db:"title_ptbr"`
SubTitlePtBr string `db:"subTitle_ptbr"`
ContentPtBr string `db:"content_ptbr"`
TitleEsLatam string `db:"title_es_latam"`
SubTitleEsLatam string `db:"subTitle_es_latam"`
ContentEsLatam string `db:"content_es_latam"`
Items string `db:"items"`
Start_time int64 `db:"start_time"`
Register_time int64 `db:"register_time"`
End_time int64 `db:"end_time"`
Mail_type int `db:"mail_type"`
Send_type int `db:"send_type"`
To_uids string `db:"to_uids"`
CreateTime int64 `db:"create_time"`
Level int `db:"level"`
}
type SqlActivityCfgStruct struct {
Id int `db:"id"`
Type int `db:"type"`
Title string `db:"title"`
MailTitle string `db:"mail_title"`
MailContent string `db:"mail_content"`
Level int `db:"level_limit"`
Start_time int64 `db:"start_time"`
End_time int64 `db:"end_time"`
Cfg string `db:"cfg"`
Interval int64 `db:"interval"`
Extra string `db:"extra"`
}
type SqlChargeOrderStruct struct {

41
src/server/ga/log.go Normal file
View File

@ -0,0 +1,41 @@
package ga
import (
galog "gitea.bywaystudios.com/pet_home/galog"
)
const (
PROJECT_ID = "20659"
CLIENT_ID = "Android_5.00_tyGuest,facebook.googleplay.0-hall20659.googleplay.Meowment"
)
var glogger *galog.GALogger
func GaInit() {
glog, err := galog.NewGALogger("logs", PROJECT_ID, CLIENT_ID, galog.LogTypeTrack)
if err != nil {
panic(err)
}
glogger = glog
}
func GAlogEvent(event string, userID string, deviceID string, properties map[string]interface{}) {
newProperties := make(map[string]interface{})
for k, v := range properties {
// // JSON encode the value
// jsonBytes, err := json.Marshal(v)
// if err != nil {
// newProperties["proj_"+k] = fmt.Sprintf("%v", v) // 无法解析直接转为字符串
// continue
// }
// newProperties["proj_"+k] = string(jsonBytes)
newProperties["proj_"+k] = v
}
properties = newProperties
glogger.
GetEntry(event).
SetDeviceID(deviceID).
SetUserID(userID).
SetProperties(properties).
Flush()
}

29
src/server/ga/te.go Normal file
View File

@ -0,0 +1,29 @@
package ga
import (
"server/conf"
"gitea.bywaystudios.com/pet_home/leaf/log"
thinkingdata "gitea.bywaystudios.com/pet_home/thinkdata"
)
var Te thinkingdata.TDAnalytics
func TeInit() {
// 初始化
// 创建 LogConfig 配置文件
config := thinkingdata.TDLogConsumerConfig{
Directory: conf.Server.TELOGDIR, // 事件采集的文件路径
}
// 初始化 logConsumer
consumer, _ := thinkingdata.NewLogConsumerWithConfig(config)
// 创建 te 对象
Te = thinkingdata.New(consumer)
thinkingdata.SetCustomLogger(teLog{})
}
type teLog struct{}
func (t teLog) Print(msg string) {
log.Debug("te log: %s", msg)
}

Some files were not shown because too many files have changed in this diff Show More