' ============================================================
'  ★ 提示: 本目录是"想自己改界面/品牌"的商家才需要的源码。
'          不想折腾的话, 直接用加盾程序包里的【加盾工具.exe】(已编译成品,
'          双击即用, 免易语言免编译), 功能与下面这套源码完全一致。
' ============================================================
'
' ============================================================
'  安生云盾 - 易语言加盾程序 (全新版, 参考实现)
' ============================================================
'  作用: 客户(游戏服主)在自己电脑上使用:
'        登录 → 激活卡密 → 刷新规则 → 两种方式给游戏加盾
'        【无源码封装式】选择游戏EXE → 生成加盾包 → 发给玩家(双击启动器即玩)
'        【无源码DLL式】 生成 DLL接入包 → 嵌入你自己的登录器/加载器注入
'
'  依赖: 精易模块(网页_访问_对象 / 类_json / 编码_URL编码)
'  配套(与本程序同目录): launcher.exe / agent.exe / shield_hook64.dll
'
'  ────────────────────────────────────────────────────────────
'  窗口与组件清单(名称必须一致):
'
'  [启动窗口] 名称=窗口_启动窗口
'    编辑框_面板地址   内容=http://你的域名/api.php
'    编辑框_账号 / 编辑框_密码(密码输入)
'    按钮_登录 / 按钮_注册 / 标签_提示
'
'  [注册窗口] 名称=窗口_注册
'    编辑框_账号 / 编辑框_密码(密码输入) / 编辑框_确认密码(密码输入)
'    编辑框_邮箱 / 按钮_确定注册 / 标签_提示
'
'  [主窗口] 名称=窗口_主窗口
'    标签_等级 / 标签_到期 / 标签_流量 / 标签_接入IP
'    组合框_规则      类型=不可编辑列表(内容由程序填充)
'    按钮_刷新
'    编辑框_卡密 / 按钮_激活卡密
'    按钮_生成加盾包  标题=生成加盾包(封装式)
'    按钮_生成DLL包   标题=生成DLL接入包
'    标签_状态
'    时钟_状态        时钟周期=60000
'    通用对话框_选文件 / 通用对话框_选目录
'
'  搭建: 新建Windows窗口程序 → 建上述3窗口与组件 → 粘贴本文件对应程序集
'        → 改常量"面板地址" → 静态编译(与 launcher.exe/agent.exe/shield_hook64.dll 同目录)
'  ────────────────────────────────────────────────────────────

.版本 2
.程序集 窗口程序集_启动窗口
.程序集变量 集_token, 文本型

.常量 面板地址, "“http://你的域名/api.php”"        ' ← 改成你的面板API地址
.常量 文件名_启动器, "“launcher.exe”"
.常量 文件名_代理, "“agent.exe”"
.常量 文件名_钩子, "“shield_hook64.dll”"

.子程序 __启动窗口_创建完毕
    编辑框_面板地址.内容 ＝ 读配置项 (取运行目录 () ＋ “\ansheng.ini”, “账号”, “面板地址”, 面板地址)
    编辑框_账号.内容 ＝ 读配置项 (取运行目录 () ＋ “\ansheng.ini”, “账号”, “账号”, “”)

' ── 面板API统一请求: 返回JSON文本 ──
.子程序 API请求, 文本型
.参数 动作, 文本型
.参数 提交数据, 文本型, 可空
.参数 带令牌, 逻辑型, 可空
.局部变量 地址, 文本型
.局部变量 附加头, 文本型
    地址 ＝ 编辑框_面板地址.内容 ＋ “?action=” ＋ 动作
    附加头 ＝ “Content-Type: application/x-www-form-urlencoded”
    .如果真 (带令牌 且 集_token ≠ “”)
        附加头 ＝ 附加头 ＋ #换行符 ＋ “Authorization: Bearer ” ＋ 集_token
    .如果真结束
    返回 (到文本 (网页_访问_对象 (地址, “POST”, 提交数据, , , 附加头, , , , 15)))

.子程序 _按钮_登录_被单击
.局部变量 json, 类_json
.局部变量 数据, 文本型
    .如果 (编辑框_账号.内容 ＝ “” 或 编辑框_密码.内容 ＝ “”)
        标签_提示.标题 ＝ “请输入账号和密码”
        返回 ()
    .如果结束
    数据 ＝ “username=” ＋ 编码_URL编码 (编辑框_账号.内容, 真, 真)
    数据 ＝ 数据 ＋ “&password=” ＋ 编码_URL编码 (编辑框_密码.内容, 真, 真)
    json.解析 (API请求 (“login”, 数据, 假))
    .如果 (json.取通用属性 (“code”) ＝ “0”)
        集_token ＝ json.取通用属性 (“data.token”)
        写配置项 (取运行目录 () ＋ “\ansheng.ini”, “账号”, “面板地址”, 编辑框_面板地址.内容)
        写配置项 (取运行目录 () ＋ “\ansheng.ini”, “账号”, “账号”, 编辑框_账号.内容)
        载入 (窗口_主窗口, , 真)
    .否则
        标签_提示.标题 ＝ json.取通用属性 (“msg”)
    .如果结束

.子程序 _按钮_注册_被单击
    载入 (窗口_注册, , 假)


.版本 2
.程序集 窗口程序集_注册

.子程序 _按钮_确定注册_被单击
.局部变量 json, 类_json
.局部变量 数据, 文本型
    .如果 (编辑框_密码.内容 ≠ 编辑框_确认密码.内容)
        标签_提示.标题 ＝ “两次输入的密码不一致”
        返回 ()
    .如果结束
    .如果真 (取文本长度 (编辑框_密码.内容) ＜ 6)
        标签_提示.标题 ＝ “密码至少6位”
        返回 ()
    .如果真结束
    数据 ＝ “username=” ＋ 编码_URL编码 (编辑框_账号.内容, 真, 真)
    数据 ＝ 数据 ＋ “&password=” ＋ 编码_URL编码 (编辑框_密码.内容, 真, 真)
    数据 ＝ 数据 ＋ “&email=” ＋ 编码_URL编码 (编辑框_邮箱.内容, 真, 真)
    json.解析 (窗口_启动窗口.API请求 (“register”, 数据, 假))
    .如果 (json.取通用属性 (“code”) ＝ “0”)
        信息框 (“注册成功, 请登录(新号含试用期, 续费用卡密)”, 0, , )
        窗口_启动窗口.编辑框_账号.内容 ＝ 编辑框_账号.内容
        本窗口.销毁 ()
    .否则
        标签_提示.标题 ＝ json.取通用属性 (“msg”)
    .如果结束


.版本 2
.程序集 窗口程序集_主窗口
.程序集变量 集_接入IP, 文本型
.程序集变量 集_节点IP, 文本型
.程序集变量 集_代理账号, 文本型
.程序集变量 集_代理密码, 文本型
.程序集变量 集_规则端口, 文本型, , "0"       ' 与 组合框_规则 一一对应(接入端口)
.程序集变量 集_规则目标, 文本型, , "0"       ' 对应 游戏服务器 ip:port

.子程序 __主窗口_创建完毕
    时钟_状态.时钟周期 ＝ 60000
    子程序_刷新 ()

.子程序 子程序_刷新
.局部变量 json, 类_json
.局部变量 i, 整数型
.局部变量 到期, 长整数型
.局部变量 已用, 双精度小数型
.局部变量 上限, 双精度小数型
.局部变量 端口, 文本型
.局部变量 目标, 文本型
    json.解析 (窗口_启动窗口.API请求 (“config”, “”, 真))
    .如果 (json.取通用属性 (“code”) ≠ “0”)
        标签_状态.标题 ＝ “获取失败: ” ＋ json.取通用属性 (“msg”)
        返回 ()
    .如果结束
    ' 账号信息
    标签_等级.标题 ＝ “等级: ” ＋ json.取通用属性 (“data.tier.name”) ＋ “　玩家 ” ＋ json.取通用属性 (“data.tier.player_limit”) ＋ “　端口 ” ＋ json.取通用属性 (“data.tier.port_limit”) ＋ “　源机 ” ＋ json.取通用属性 (“data.tier.src_limit”)
    到期 ＝ 到长整数 (json.取通用属性 (“data.expire_at”))
    .如果 (到期 ＞ 0)
        标签_到期.标题 ＝ “到期: ” ＋ 时间_到文本 (到时间 (增减时间 (到时间 (“1970-01-01 08:00:00”), #秒, 到期)), 1)
    .否则
        标签_到期.标题 ＝ “到期: 永久”
    .如果结束
    已用 ＝ 到数值 (json.取通用属性 (“data.traffic_used”))
    上限 ＝ 到数值 (json.取通用属性 (“data.traffic_limit”))
    标签_流量.标题 ＝ “流量: ” ＋ 子程序_GB (已用) ＋ “ / ” ＋ 子程序_GB (上限) ＋ “ GB”
    ' 接入信息
    集_接入IP ＝ json.取通用属性 (“data.entry_ip”)
    集_节点IP ＝ json.取通用属性 (“data.node.ip”)
    集_代理账号 ＝ json.取通用属性 (“data.proxy.user”)
    集_代理密码 ＝ json.取通用属性 (“data.proxy.pass”)
    标签_接入IP.标题 ＝ “接入IP: ” ＋ 集_接入IP
    ' 规则下拉
    组合框_规则.清空 ()
    重定义数组 (集_规则端口, 假, 0)
    重定义数组 (集_规则目标, 假, 0)
    .计次循环首 (json.成员数 (“data.rules”), i)
        端口 ＝ json.取通用属性 (“data.rules[” ＋ 到文本 (i － 1) ＋ “].listen_port”)
        目标 ＝ json.取通用属性 (“data.rules[” ＋ 到文本 (i － 1) ＋ “].src_ip”) ＋ “:” ＋ json.取通用属性 (“data.rules[” ＋ 到文本 (i － 1) ＋ “].src_port”)
        加入成员 (集_规则端口, 端口)
        加入成员 (集_规则目标, 目标)
        组合框_规则.加入项目 (“接入端口 ” ＋ 端口 ＋ “ → ” ＋ 目标, )
    .计次循环尾
    .如果真 (组合框_规则.取项目数 () ＞ 0)
        组合框_规则.现行选中项 ＝ 0
    .如果真结束
    标签_状态.标题 ＝ “就绪: 共 ” ＋ 到文本 (json.成员数 (“data.rules”)) ＋ “ 条规则(网页配置)”

.子程序 子程序_GB, 文本型
.参数 字节数, 双精度小数型
    返回 (到文本 (四舍五入 (字节数 ÷ 1073741824, 3)))

.子程序 子程序_取所选规则, 逻辑型
' 从下拉框取出所选规则, 写入 端口/目标 参数
.参数 端口, 文本型, 参考
.参数 目标, 文本型, 参考
.局部变量 序号, 整数型
    序号 ＝ 组合框_规则.现行选中项
    .如果真 (序号 ＜ 0 或 序号 ≥ 取数组成员数 (集_规则端口))
        信息框 (“请先在网页配置规则, 再在下方下拉框选择规则”, 0, , )
        返回 (假)
    .如果真结束
    端口 ＝ 集_规则端口 [序号 ＋ 1]
    目标 ＝ 集_规则目标 [序号 ＋ 1]
    返回 (真)

.子程序 子程序_写配置
' 生成 shield.ini(Hook映射) 与 agent.ini(本地代理配置)
.参数 目录, 文本型
.参数 端口, 文本型
.参数 目标, 文本型
.局部变量 内容, 文本型
    内容 ＝ “entry=” ＋ 集_接入IP ＋ #换行符
    内容 ＝ 内容 ＋ “map=” ＋ 目标 ＋ “=” ＋ 端口 ＋ #换行符
    写到文件 (目录 ＋ “shield.ini”, 到字节集 (内容))
    内容 ＝ “node=” ＋ 集_节点IP ＋ “:1080” ＋ #换行符
    内容 ＝ 内容 ＋ “user=” ＋ 集_代理账号 ＋ #换行符
    内容 ＝ 内容 ＋ “pass=” ＋ 集_代理密码 ＋ #换行符
    内容 ＝ 内容 ＋ “entry=” ＋ 集_接入IP ＋ #换行符
    内容 ＝ 内容 ＋ “ports=” ＋ 端口 ＋ #换行符
    内容 ＝ 内容 ＋ “targets=” ＋ 目标 ＋ #换行符
    写到文件 (目录 ＋ “agent.ini”, 到字节集 (内容))

.子程序 _按钮_刷新_被单击
    子程序_刷新 ()

.子程序 _按钮_激活卡密_被单击
.局部变量 json, 类_json
.局部变量 数据, 文本型
    .如果真 (删首尾空 (编辑框_卡密.内容) ＝ “”)
        信息框 (“请输入卡密”, 0, , )
        返回 ()
    .如果真结束
    数据 ＝ “card_key=” ＋ 编码_URL编码 (删首尾空 (编辑框_卡密.内容), 真, 真)
    json.解析 (窗口_启动窗口.API请求 (“activate”, 数据, 真))
    .如果 (json.取通用属性 (“code”) ＝ “0”)
        信息框 (json.取通用属性 (“msg”), 0, , )
        编辑框_卡密.内容 ＝ “”
        子程序_刷新 ()
    .否则
        信息框 (json.取通用属性 (“msg”), 0, , )
    .如果结束

.子程序 _按钮_生成加盾包_被单击
' 无源码封装式接入: 生成"游戏+启动器+代理+Hook+配置"完整加盾包
.局部变量 端口, 文本型
.局部变量 目标, 文本型
.局部变量 游戏路径, 文本型
.局部变量 目录, 文本型
.局部变量 游戏名, 文本型
.局部变量 位置, 整数型
    .如果真 (子程序_取所选规则 (端口, 目标) ＝ 假)
        返回 ()
    .如果真结束
    .如果真 (通用对话框_选文件.打开 () ＝ 假)
        返回 ()
    .如果真结束
    游戏路径 ＝ 通用对话框_选文件.文件名
    .如果真 (通用对话框_选目录.打开 () ＝ 假)
        返回 ()
    .如果真结束
    目录 ＝ 通用对话框_选目录.文件夹 ＋ “\”
    位置 ＝ 倒找文本 (游戏路径, “\”, , 假)
    .如果 (位置 ＝ -1)
        游戏名 ＝ 游戏路径
    .否则
        游戏名 ＝ 取文本右边 (游戏路径, 取文本长度 (游戏路径) － 位置)
    .如果结束
    复制文件 (游戏路径, 目录 ＋ 游戏名)
    复制文件 (取运行目录 () ＋ “\” ＋ 窗口_启动窗口.文件名_启动器, 目录 ＋ 窗口_启动窗口.文件名_启动器)
    复制文件 (取运行目录 () ＋ “\” ＋ 窗口_启动窗口.文件名_代理, 目录 ＋ 窗口_启动窗口.文件名_代理)
    复制文件 (取运行目录 () ＋ “\” ＋ 窗口_启动窗口.文件名_钩子, 目录 ＋ 窗口_启动窗口.文件名_钩子)
    子程序_写配置 (目录, 端口, 目标)
    信息框 (“【加盾包】已生成到: ” ＋ 目录 ＋ #换行符 ＋ #换行符 ＋ “把整个目录压缩发给玩家; 玩家解压后双击 ” ＋ 窗口_启动窗口.文件名_启动器 ＋ “ 即可进游戏(自动走盾)”, 0, , )

.子程序 _按钮_生成DLL包_被单击
' 无源码DLL式接入: 只输出 Hook DLL + 本地代理 + 配置, 供客户自己的登录器注入
.局部变量 端口, 文本型
.局部变量 目标, 文本型
.局部变量 目录, 文本型
.局部变量 说明, 文本型
    .如果真 (子程序_取所选规则 (端口, 目标) ＝ 假)
        返回 ()
    .如果真结束
    .如果真 (通用对话框_选目录.打开 () ＝ 假)
        返回 ()
    .如果真结束
    目录 ＝ 通用对话框_选目录.文件夹 ＋ “\”
    复制文件 (取运行目录 () ＋ “\” ＋ 窗口_启动窗口.文件名_代理, 目录 ＋ 窗口_启动窗口.文件名_代理)
    复制文件 (取运行目录 () ＋ “\” ＋ 窗口_启动窗口.文件名_钩子, 目录 ＋ 窗口_启动窗口.文件名_钩子)
    子程序_写配置 (目录, 端口, 目标)
    说明 ＝ “安生云盾 - 无源码DLL式接入包” ＋ #换行符 ＋ #换行符
    说明 ＝ 说明 ＋ “文件说明:” ＋ #换行符
    说明 ＝ 说明 ＋ “  shield_hook64.dll  加盾Hook DLL(注入游戏后接管其网络连接)” ＋ #换行符
    说明 ＝ 说明 ＋ “  agent.exe          本地代理(与游戏EXE同目录, 先于游戏启动)” ＋ #换行符
    说明 ＝ 说明 ＋ “  shield.ini         Hook映射(接入IP + 游戏原目标 -> 接入端口)” ＋ #换行符
    说明 ＝ 说明 ＋ “  agent.ini          本地代理配置(节点/账号/端口映射)” ＋ #换行符 ＋ #换行符
    说明 ＝ 说明 ＋ “接入步骤:” ＋ #换行符
    说明 ＝ 说明 ＋ “  1. 把 agent.exe / shield_hook64.dll / shield.ini / agent.ini 放到游戏目录;” ＋ #换行符
    说明 ＝ 说明 ＋ “  2. 启动游戏前先启动 agent.exe(隐藏窗口);” ＋ #换行符
    说明 ＝ 说明 ＋ “  3. 以挂起方式启动游戏(CREATE_SUSPENDED)并注入 shield_hook64.dll,” ＋ #换行符
    说明 ＝ 说明 ＋ “     再恢复运行(CreateRemoteThread + LoadLibrary 即可);” ＋ #换行符
    说明 ＝ 说明 ＋ “  4. 游戏连接即按 shield.ini 映射走盾, 无需改游戏代码。” ＋ #换行符
    写到文件 (目录 ＋ “接入说明.txt”, 到字节集 (说明))
    信息框 (“【DLL接入包】已生成到: ” ＋ 目录 ＋ #换行符 ＋ #换行符 ＋ “包内文件放入你的游戏目录, 由你的登录器注入启动即可(详见 接入说明.txt)”, 0, , )

.子程序 _时钟_状态_周期事件
    窗口_启动窗口.API请求 (“heartbeat”, “”, 真)

.子程序 _主窗口_将被销毁
    时钟_状态.时钟周期 ＝ 0
