本文最后更新于 148 天前,其中的信息可能已经有所发展或是发生改变。
1.前言
服务器核心:leaves-1.21.4
选用插件:LushRewards
插件文档为全生肉,可能需要借助翻译器
2. 插件基本信息
数据存储支持 Json,SQLite,MySQL,PostgreSQL。
目前支持从其它俩款插件导入数据,分别为 DailyRewards+ 和 NDailyRewards 。
时间计算的还算基准
3. 配置文件示例
3.1奖励配置文件示例
## daily-playtime-xeiu.yml (这里的文件名即为模板名,后文会讲到)
# 每日游戏时长奖励: 每日重置累计游戏时长目标
type: playtime-rewards
enabled: true
# 重置周期(单位:天,0表示禁用重置)
reset-playtime-at: 1 #设为1则服务器时间每天0:00重置
# 检查在线玩家是否可领取奖励的时间间隔(单位:分钟,-1表示禁用)
refresh-time: 3
# 当奖励可领取时是否发送通知
enable-notifications: true
goals:
- play-minutes: 30 # 30 分钟 (分钟为单位)
display-item:
amount: 1
lore: #这里填写的是能看见的信息(如图1)
- "&#bdbebf奖励内容:(随机一项发放)"
- "&#bdbebf- 1 个钻石"
- "&#bdbebf- 1 个下界合金碎片"
- "&#bdbebf- 8 个牛排"
- "&#bdbebf- 16 个泥土"
- "&#bdbebf- 16 个圆石"
- "&#bdbebf- 16 个原木"
- "&#bdbebf- 16 个金锭"
- "&#E10000&l注意:"
- "&#c4f8ff领取前请保证背包有足够的空间"
- "&#c4f8ff每逢服务器时间0:00(UTC+8)重置进度"
- "&#c4f8ff逾期未领取的奖品将无法领取,不能补发"
rewards:
- type: random #类别(item为物品,command为控制台执行命令,player-command为玩家执行命令,random为随机发放,permission给予玩家权限节点,template为文件自定奖励。)
rewards:
- weight: 100 #概率加权
type: item
material: diamond
amount: 1
broadcast:
- "&f[�FF00今日在线时长奖励&f] 恭喜玩家 &b%player% &f达到了 &r今日在线时长 30 分钟, &f获得了 &e 钻石*1 &f的奖励 !" #broadcast为广播消息(同服在线玩家都能看见),message为向领取奖励的玩家发消息(只有领取奖励的玩家能看见)
- weight: 100 #概率加权
type: item
material: netherite_scrap
amount: 1
broadcast:
- "&f[�FF00今日在线时长奖励&f] 恭喜玩家 &b%player% &f达到了 &r今日在线时长 30 分钟, &f获得了 &e 下界合金碎片*1 &f的奖励 !"
- weight: 100 #概率加权
type: item
material: cooked_beef
amount: 8
broadcast:
- "&f[�FF00今日在线时长奖励&f] 恭喜玩家 &b%player% &f达到了 &r今日在线时长 30 分钟, &f获得了 &e 牛排*8 &f的奖励 !"
- weight: 100 #概率加权
type: item
material: dirt
amount: 16
broadcast:
- "&f[�FF00今日在线时长奖励&f] 恭喜玩家 &b%player% &f达到了 &r今日在线时长 30 分钟, &f获得了 &e 泥土*16 &f的奖励 !"
- weight: 100 #概率加权
type: item
material: oak_log
amount: 16
broadcast:
- "&f[�FF00今日在线时长奖励&f] 恭喜玩家 &b%player% &f达到了 &r今日在线时长 30 分钟, &f获得了 &e 原木*16 &f的奖励 !"
- weight: 100 #概览加权
type: item
material: cobblestone
amount: 16
broadcast:
- "&f[�FF00今日在线时长奖励&f] 恭喜玩家 &b%player% &f达到了 &r今日在线时长 30 分钟, &f获得了 &e 圆石*16 &f的奖励 !"
- weight: 100 #概率加权
type: item
material: gold_ingot
amount: 16
broadcast:
- "&f[�FF00今日在线时长奖励&f] 恭喜玩家 &b%player% &f达到了 &r今日在线时长 30 分钟, &f获得了 &e 金锭*16 &f的奖励 !"
- play-minutes: 60 # 1 小时 (分钟为单位)
display-item:
amount: 2
lore:
- "&#bdbebf奖励内容:(随机一项发放)"
- "&#bdbebf- 1 个潮涌核心"
- "&#bdbebf- 2 个下界合金碎片"
- "&#bdbebf- 2 个钻石"
- "&#bdbebf- 16 个绿宝石"
- "&#bdbebf- 16 个火药"
- "&#bdbebf- 16 个铁锭"
- "&#bdbebf- 16 个石英"
- "&#E10000&l注意:"
- "&#c4f8ff领取前请保证背包有足够的空间"
- "&#c4f8ff每逢服务器时间0:00(UTC+8)重置进度"
- "&#c4f8ff逾期未领取的奖品将无法领取,不能补发"
rewards:
- type: random
rewards:
- weight: 100 #概率加权
type: item
material: conduit
amount: 1
broadcast:
- "&f[�FF00今日在线时长奖励&f] 恭喜玩家 &b%player% &f达到了 &r今日在线时长 1 小时, &f获得了 &e 潮涌核心*1 &f的奖励 !"
- weight: 100 #概率加权
type: item
material: netherite_scrap
amount: 2
broadcast:
- "&f[�FF00今日在线时长奖励&f] 恭喜玩家 &b%player% &f达到了 &r今日在线时长 1 小时, &f获得了 &e 下界合金碎片*2 &f的奖励 !"
- weight: 100 #概率加权
type: item
material: diamond
amount: 2
broadcast:
- "&f[�FF00今日在线时长奖励&f] 恭喜玩家 &b%player% &f达到了 &r今日在线时长 1 小时, &f获得了 &e 钻石*2 &f的奖励 !"
- weight: 100 #概率加权
type: item
material: emerald
amount: 16
broadcast:
- "&f[�FF00今日在线时长奖励&f] 恭喜玩家 &b%player% &f达到了 &r今日在线时长 1 小时, &f获得了 &e 绿宝石*16 &f的奖励 !"
- weight: 100 #概览加权
type: item
material: gunpowder
amount: 16
broadcast:
- "&f[�FF00今日在线时长奖励&f] 恭喜玩家 &b%player% &f达到了 &r今日在线时长 1 小时, &f获得了 &e 火药*16 &f的奖励 !"
- weight: 100 #概览加权
type: item
material: iron_ingot
amount: 16
broadcast:
- "&f[�FF00今日在线时长奖励&f] 恭喜玩家 &b%player% &f达到了 &r今日在线时长 1 小时, &f获得了 &e 铁锭*16 &f的奖励 !"
- weight: 100 #概览加权
type: item
material: quartz
amount: 16
broadcast:
- "&f[�FF00今日在线时长奖励&f] 恭喜玩家 &b%player% &f达到了 &r今日在线时长 1 小时, &f获得了 &e 石英*16 &f的奖励 !"
- play-minutes: 120 # 2 小时
display-item:
amount: 3
lore:
- "&#bdbebf奖励内容:(随机一项发放)"
- "&#bdbebf- 1 个下界合金升级模板"
- "&#bdbebf- 1 个附魔书-迅捷潜行III"
- "&#bdbebf- 3 个下界合金碎片"
- "&#bdbebf- 4 个钻石"
- "&#bdbebf- 8 个金胡萝卜"
- "&#bdbebf- 8 个附魔之瓶"
- "&#bdbebf- 64 个原木"
- "&#E10000&l注意:"
- "&#c4f8ff领取前请保证背包有足够的空间"
- "&#c4f8ff每逢服务器时间0:00(UTC+8)重置进度"
- "&#c4f8ff逾期未领取的奖品将无法领取,不能补发"
rewards:
- type: random
rewards:
- weight: 100 #概率加权
type: item
material: netherite_upgrade_smithing_template
amount: 1
broadcast:
- "&f[�FF00今日在线时长奖励&f] 恭喜玩家 &b%player% &f达到了 &r今日在线时长 2 小时, &f获得了 &e 下界合金升级模板*1 &f的奖励 !"
- weight: 100 #概率加权
type: command
commands:
- "give %player% minecraft:enchanted_book[minecraft:enchantments={swift_sneak:3}] 1"
broadcast:
- "&f[�FF00今日在线时长奖励&f] 恭喜玩家 &b%player% &f达到了 &r今日在线时长 2 小时, &f获得了 &e 附魔书-迅捷潜行III *1 &f的奖励 !"
- weight: 100 #概率加权
type: item
material: oak_log
amount: 64
broadcast:
- "&f[�FF00今日在线时长奖励&f] 恭喜玩家 &b%player% &f达到了 &r今日在线时长 2 小时, &f获得了 &e 原木*64 &f的奖励 !"
- weight: 100 #概率加权
type: item
material: netherite_scrap
amount: 3
broadcast:
- "&f[�FF00今日在线时长奖励&f] 恭喜玩家 &b%player% &f达到了 &r今日在线时长 2 小时, &f获得了 &e 下界合金碎片*3 &f的奖励 !"
- weight: 100 #概率加权
type: item
material: diamond
amount: 4
broadcast:
- "&f[�FF00今日在线时长奖励&f] 恭喜玩家 &b%player% &f达到了 &r今日在线时长 2 小时, &f获得了 &e 钻石*4 &f的奖励 !"
- weight: 100 #概率加权
type: item
material: golden_carrot
amount: 8
broadcast:
- "&f[�FF00今日在线时长奖励&f] 恭喜玩家 &b%player% &f达到了 &r今日在线时长 2 小时, &f获得了 &e 金胡萝卜*8 &f的奖励 !"
- weight: 100 #概率加权
type: item
material: experience_bottle
amount: 8
broadcast:
- "&f[�FF00今日在线时长奖励&f] 恭喜玩家 &b%player% &f达到了 &r今日在线时长 2 小时, &f获得了 &e 附魔之瓶*8 &f的奖励 !"
- play-minutes: 240 # 4 小时
display-item:
amount: 4
lore:
- "&#bdbebf奖励内容:(随机一项发放)"
- "&#bdbebf- 1 个信标"
- "&#bdbebf- 1 个龙首"
- "&#bdbebf- 1 个磁石"
- "&#bdbebf- 1 个三叉戟"
- "&#bdbebf- 1 个沉重核心"
- "&#bdbebf- 4 个下界合金碎片"
- "&#bdbebf- 8 个钻石"
- "&#E10000&l注意:"
- "&#c4f8ff领取前请保证背包有足够的空间"
- "&#c4f8ff每逢服务器时间0:00(UTC+8)重置进度"
- "&#c4f8ff逾期未领取的奖品将无法领取,不能补发"
rewards:
- type: random
rewards:
- weight: 100 #概率加权
type: item
material: beacon
amount: 1
broadcast:
- "&f[�FF00今日在线时长奖励&f] 恭喜玩家 &b%player% &f达到了 &r今日在线时长 4 小时, &f获得了 &e 信标*1 &f的奖励 !"
- weight: 100 #概率加权
type: item
material: dragon_head
amount: 1
broadcast:
- "&f[�FF00今日在线时长奖励&f] 恭喜玩家 &b%player% &f达到了 &r今日在线时长 4 小时, &f获得了 &e 龙首*1 &f的奖励 !"
- weight: 100 #概率加权
type: item
material: lodestone
amount: 1
broadcast:
- "&f[�FF00今日在线时长奖励&f] 恭喜玩家 &b%player% &f达到了 &r今日在线时长 4 小时, &f获得了 &e 磁石*1 &f的奖励 !"
- weight: 100 #概率加权
type: item
material: trident
amount: 1
broadcast:
- "&f[�FF00今日在线时长奖励&f] 恭喜玩家 &b%player% &f达到了 &r今日在线时长 4 小时, &f获得了 &e 三叉戟*1 &f的奖励 !"
- weight: 100 #概率加权
type: item
material: heavy_core
amount: 1
broadcast:
- "&f[�FF00今日在线时长奖励&f] 恭喜玩家 &b%player% &f达到了 &r今日在线时长 4 小时, &f获得了 &e 沉重核心 &f的奖励 !"
- weight: 100 #概率加权
type: item
material: netherite_scrap
amount: 4
broadcast:
- "&f[�FF00今日在线时长奖励&f] 恭喜玩家 &b%player% &f达到了 &r今日在线时长 4 小时, &f获得了 &e 下界核心碎片*4 &f的奖励 !"
- weight: 100 #概率加权
type: item
material: diamond
amount: 8
broadcast:
- "&f[�FF00今日在线时长奖励&f] 恭喜玩家 &b%player% &f达到了 &r今日在线时长 4 小时, &f获得了 &e 钻石*8 &f的奖励 !"
gui:
title: "ȑbf2&l今日在线时长奖励"
scroll-type: FIXED
# 可用模板: DEFAULT(默认), COMPACT(紧凑)
# 查看 https://docs.lushplugins.org/lush-rewards/configuring/gui-templates#custom-templates 了解如何创建和使用自定义模板
template: "CUSTOM"
format:
- "#########"
- "#RRRR##P#"
- "#########"
item-templates:
default-reward:
display-name: "&f[�FF00今日在线时长奖励&f] &#f5e389%minutes% 分钟"
redeemable-reward:
material: player_head
skull-texture: e3RleHR1cmVzOntTS0lOOnt1cmw6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmI1N2Y0ODVlOWVjY2E1MzIzNDQ4NTBjNTFhYjQ0ZGQ2YWE1ZDQ1MTRiMzFjNmRmOWZjMjBlMzZiM2E4OWQ4YyJ9fX0=
display-name: "&f[�FF00今日在线时长奖励&f] &#f5e389%minutes% 分钟 - 可领取"
enchanted: true
collected-reward:
material: player_head
skull-texture: e3RleHR1cmVzOntTS0lOOnt1cmw6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZWZkNTEyY2NlNGU3OTUyOGI2MGIzNWRmZTZmN2JmYzg0M2IyOTY4NDIxMDViMWNkMjY4MjdiNTkwOGIxMzU0ZCJ9fX0=
display-name: "&f[�FF00今日在线时长奖励&f] &#f5e389%minutes% 分钟 - 已领取"
enchanted: false
'P':
material: player_head
display-name: "&#A5B8FE&l玩家档案"
skull-texture: mirror
lore:
- "&#c4f8ff今日在线时间: &f%lushrewards_daily-playtime-xeiu_playtime_minutes% &#c4f8ff分钟"
- "&#c4f8ff下一奖励剩余在线时间: &f%lushrewards_daily-playtime-xeiu_time_until_next_reward% &#c4f8ff分钟"
- "&#E10000&l注意事项:"
- "&#c4f8ff领取前请保证背包有足够的空间"
- "&#c4f8ff每逢服务器时间0:00(UTC+8)重置进度"
- "&#c4f8ff逾期未领取的奖品将无法领取,不能补发"
以上为每日在线时长奖励的示例模板
3.2插件主配置文件示例
## config.yml
# 更多配置详情请参阅 LushRewards 文档:https://docs.lushplugins.org/lush-rewards
# 当设置为 false 时,游戏时间将包含挂机/闲置时间
playtime-ignore-afk: false
# 提醒间隔时间(单位:秒,设置为 -1 可禁用)
reminder-period: 180
# 玩家收到提醒时播放的音效
reminder-sound: block_note_block_pling
# 不同奖励分类的展示物品
categories:
small:
material: player_head
skull-texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYTAxYzYzYzIwM2ExODQxZGUyN2EwZTA5YTI5NWY1Yjg3MzM2MjcwMjRmMTY2OGEwMWQyMDE4NmEzZjg2MzA0MCJ9fX0=
medium:
material: player_head
skull-texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZjEwMzhjZGM4NDIzNGI1YWZiM2FlYTg1M2JmM2QwNTE3NWQxOWFlN2I0ODc1YTkxYjUyZjc2NmMwN2IzZGIwYiJ9fX0=
large:
material: player_head
skull-texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNjUyODdhZjk3ZjkzZDM3MTg5NzNmNGUxM2VkMGZhM2E2ZjQzOWU3MmMxNDVmZTRjZDkwMmIwNWMxYzEwYWYxOCJ9fX0=
# 物品展示模板(这些模板可用于任何奖励界面)
item-templates:
# 即将到来奖励的模板
upcoming-reward:
lore:
- "&7&o- 即将到来的奖励"
# 边框物品模板 - 可用类似方式创建更多用于界面模板的物品
'#':
material: light_blue_stained_glass_pane
display-name: "&7"
messages:
prefix: "&#A5B8FE&lImpart Lab 奖励系统 &8» "
reload: "&#b7faa2LushRewards 已重载 Bb04f🔃"
reminder: "%prefix% &#ffe27a您似乎还有未领取的奖励,使用 &#ffc940/rewards claim &#ffe27a领取目前能领取的所有奖励"
daily-reward-given: "&#b7faa2你已领取目前能领取的所有奖励"
playtime-reward-given: "&#b7faa2你已领取累计游戏 Bb04f%total_minutes% &#b7faa2分钟的奖励"
no-rewards-available: "&#ff6969你没有可领取的奖励"
reset: "&#ffe27a确定要重置 &#e0c01b%target% &#ffe27a的天数吗?输入 &#e0c01b'/rewards edit-user <模块ID> reset %target% confirm' &#ffe27a确认操作"
set-days-confirm: "Bb04f%target% &#b7faa2在 Bb04f%module% &#b7faa2模块的天数已设置为 Bb04f%day%"
set-streak-confirm: "Bb04f%target% &#b7faa2在 Bb04f%module% &#b7faa2模块的连续天数已设置为 Bb04f%streak%"
set-playtime-confirm: "Bb04f%target% &#b7faa2的游戏时长已设置为 Bb04f%playtime%"
unknown-player: "&#ff6969找不到玩家 &#d13636%player%"
no-permissions: "&#ff6969权限不足"
incorrect-usage: "&#ff6969指令使用错误,正确用法:&#d13636%command-usage%"
confirm-command: "&#ffe27a确定要执行此操作吗?输入 &#e0c01b'%command%' &#ffe27a确认"
# 性能模式仅存储当前玩家可见的奖励
# 启用后配置会在每日开始时自动重载
enable-performance-mode: false
# 调试模式:none(无), daily(每日), playtime(游戏时长), all(全部)
debug-mode: none
# 更新器:禁用后将关闭更新通知和更新指令
enable-updater: false
上面是主配置文件示例
## storage.yml
# 选择你需要的数据存储类型 (Options: json, sqlite, mysql, postgres)
type: mysql
# 数据库基本信息,如果选了Json和SQLite就不用继续填了。
storage:
host: localhost #ip
# Mysql 默认端口: 3306
# Postgres 默认端口: 5432
port: 3310 #端口
database: timereward #库名
schema: schemaName # 这个配置项只有PostgreSQL才用的上
user: timereward #用户名
password: password #密码
上面是数据库信息配置文件示例
## reward-templates.yml
rewards:
iron_ingots:
type: item
material: iron_ingot
amount: 10
上面是自定义奖励列表的配件文件示例。
4. 其它插件(数据包)数据迁入 — MySQL
4.1 将数据处理成有 玩家ID 和 在线时长(单位为分钟) 的符合Json数据格式规范的 .json文件。
这我也没法写,因为各种插件的数据格式是不一样的,只能仁者见仁智者见智了。
4.2 动用你的聪明的小脑袋瓜,把json数据转换成.sql文件,然后导入到mysql数据库里就行了。
//.sql文件格式示例
//此为示例!!!不能用的,只供模板参考!
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for lushrewards_users
-- ----------------------------
DROP TABLE IF EXISTS `lushrewards_users`;
CREATE TABLE `lushrewards_users` (
`uuid` char(36) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL,
`data` json NULL,
PRIMARY KEY (`uuid`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of lushrewards_users
-- ----------------------------
INSERT INTO `lushrewards_users` (`uuid`, `data`) VALUES ('abcdefgh-jklm-nopq-rstu-vwxyz4c608f7', '{\"username\": \"playername\", \"minutesPlayed\": 6446}');
INSERT INTO `lushrewards_users` (`uuid`, `data`) VALUES ('abcdefgh-jklm-nopq-rstu-vwxyz4c608f7', '{\"username\": \"playername\", \"minutesPlayed\": 681}');
INSERT INTO `lushrewards_users` (`uuid`, `data`) VALUES ('abcdefgh-jklm-nopq-rstu-vwxyz4c608f7', '{\"username\": \"playername\", \"minutesPlayed\": 114}');
INSERT INTO `lushrewards_users` (`uuid`, `data`) VALUES ('abcdefgh-jklm-nopq-rstu-vwxyz4c608f7', '{\"username\": \"playername\", \"minutesPlayed\": 190}');
INSERT INTO `lushrewards_users` (`uuid`, `data`) VALUES ('abcdefgh-jklm-nopq-rstu-vwxyz4c608f7', '{\"username\": \"playername\", \"minutesPlayed\": 7}');
INSERT INTO `lushrewards_users` (`uuid`, `data`) VALUES ('abcdefgh-jklm-nopq-rstu-vwxyz4c608f7', '{\"username\": \"playername\", \"minutesPlayed\": 1050}');
INSERT INTO `lushrewards_users` (`uuid`, `data`) VALUES ('abcdefgh-jklm-nopq-rstu-vwxyz4c608f7', '{\"username\": \"playername\", \"minutesPlayed\": 223}');
INSERT INTO `lushrewards_users` (`uuid`, `data`) VALUES ('abcdefgh-jklm-nopq-rstu-vwxyz4c608f7', '{\"username\": \"playername\", \"minutesPlayed\": 7}');
SET FOREIGN_KEY_CHECKS = 1;
4.3 把json数据转换成.sql文件,代码示例 (java)
package org.example;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.JsonNode;
import java.net.http.*;
import java.net.URI;
import java.time.Duration;
import java.util.*;
import java.io.*;
import java.nio.file.*;
import java.util.concurrent.*;
public class lushrewards_users_SQL {
private static final String INPUT_JSON = "onlinetime_output.json"; //json文件输入
private static final String OUTPUT_SQL = "update_script.sql"; //sql文件输出
private static final String API_URL = "https://api.mojang.com/users/profiles/minecraft/"; //mojang api
private static final int MAX_RETRIES = 3; // 重试最大次数
private static final int RETRY_DELAY = 5; // 重试等待时长,seconds
private static final HttpClient httpClient = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_1_1)
.connectTimeout(Duration.ofSeconds(30))
.build();
static class PlayerData {
public String Name; //玩家名字
public int Score; //游戏时长数据(单位:分钟)
}
public static void main(String[] args) throws Exception {
ObjectMapper mapper = new ObjectMapper();
List<PlayerData> players = mapper.readValue(
new File(INPUT_JSON),
mapper.getTypeFactory().constructCollectionType(List.class, PlayerData.class));
List<String> sqlStatements = new ArrayList<>();
sqlStatements.add("SET FOREIGN_KEY_CHECKS = 0;");
ExecutorService executor = Executors.newFixedThreadPool(2);
PrintWriter logger = new PrintWriter("update_log.txt"); //日志文件输出
for (PlayerData player : players) {
executor.submit(() -> processPlayer(player, sqlStatements, logger));
}
executor.shutdown();
executor.awaitTermination(1, TimeUnit.HOURS);
sqlStatements.add("SET FOREIGN_KEY_CHECKS = 1;");
Files.write(Paths.get(OUTPUT_SQL), sqlStatements);
logger.close();
}
private static void processPlayer(PlayerData player, List<String> sql, PrintWriter logger) {
try {
String uuid = getUUIDWithRetry(player.Name, logger);
if (uuid != null) {
generateUpdateStatements(player, uuid, sql, logger);
}
} catch (Exception e) {
logger.println("ERROR processing " + player.Name + ": " + e.getMessage()); //日志
}
}
private static String getUUIDWithRetry(String username, PrintWriter logger) {
for (int i = 0; i < MAX_RETRIES; i++) {
try {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(API_URL + username))
.GET()
.build();
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == 200) {
JsonNode node = new ObjectMapper().readTree(response.body());
String uuid = node.get("id").asText();
logger.println("[SUCCESS]: 成功获取到uuid: " + username + ": " + formatUUID(uuid)); //成功获取uuid的日志输出
return formatUUID(uuid);
} else if (response.statusCode() == 429) {
logger.println("WARN: Rate limited for " + username + ", retrying in " + RETRY_DELAY + "s"); //警告日志
TimeUnit.SECONDS.sleep(RETRY_DELAY);
}
} catch (Exception e) {
logger.println("WARN: Attempt " + (i+1) + " failed for " + username + ": " + e.getMessage()); //警告日志
}
}
logger.println("[ERROR]: Failed to get UUID for " + username + " after " + MAX_RETRIES + " attempts"); //获取uuid失败日志输出
return null;
}
private static void generateUpdateStatements(PlayerData player, String uuid, List<String> sql, PrintWriter logger) {
//lushrewards_users sql语句
String userUpdate = String.format(
"INSERT INTO `lushrewards_users` (`uuid`, `data`) VALUES ('%s', '{\\\"username\\\": \\\"%s\\\", \\\"minutesPlayed\\\": %s}');",
uuid, player.Name, player.Score);
sql.add(userUpdate);
logger.println("SUCCESS: Generated updates for " + player.Name + " (" + uuid + ")"); //sql语句输出成功日志输出
}
private static String formatUUID(String uuid) { //uuid格式化
return uuid.replaceAll(
"(\\p{XDigit}{8})(\\p{XDigit}{4})(\\p{XDigit}{4})(\\p{XDigit}{4})(\\p{XDigit}+)",
"$1-$2-$3-$4-$5");
}
}