概览与界面
SGS Framework 由下载器在 Laya 初始化前注入。Core 统一管理生命周期、运行时事件、可逆 Hook、插件安装与配置、LocalStorage 前缀、版本检测和 Laya 底层访问。
屏幕边缘的 SGS 悬浮窗自动吸附左侧或右侧。自动隐藏开启时,只保留边缘感应区,鼠标进入当前侧边缘才显示;拖动后在边缘松开也会按该设置隐藏。点击悬浮窗可选择“选项”、切换自动隐藏,并查看不可点击的 Core 版本号。
选项窗口默认占视口宽高各 50%,可拖动和从右下角缩放。最小宽高为视口的 20%,同时保留 260×180 像素的可用下限;移动、缩放、视口变化和浏览器缩放后,窗口都会被约束在屏幕内。
窗口包含“插件管理”“插件配置”“Core”三个 Tab。Core Tab 的“Core 版本”区域同时显示版本检测与“开发者模式”开关。开关成功保存后,公共字段会立即更新;已经运行的插件可以监听对应事件,后续启动的插件可以直接读取当前值。
window.SgsFramework/script/plugin/index.json5000 ms / 请求安装与入口
- 在油猴脚本管理器中打开 download.mjs 并安装。
- 下载器在
document-start安装 Laya 初始化屏障,优先请求本站core.mjs,失败后请求 GitHub;两个来源各自最多等待 5 秒。 - 远程请求都失败时,下载器先尝试页面 LocalStorage 缓存;仍不可用时显示“继续游戏 / 刷新重试”。选择继续时使用可用缓存,没有缓存则不加载 Framework。
- Core 注入完成后释放 Laya 屏障,恢复已启用插件并执行静默版本检测。
用户手册:https://sgs.senrax.com/
安装脚本:https://sgs.senrax.com/script/download.mjs
公开目录:https://sgs.senrax.com/script/
在 https://web.sanguosha.com/login/index.html 登录页,Core 会显示只读版本标记,便于确认注入是否成功。
Core API
window.SgsFramework 与 window.__SgsFramework 指向同一个实例。插件通过 context.framework 获得该实例;页面控制台也可以直接读取全局入口。
| 入口 | 暴露方法与定义 |
|---|---|
| Core | developerMode 是由 Core UI 控制的只读布尔字段,对应能力名 core.developer-mode;status() 返回版本、开发者模式、插件、Laya、事件监视、更新和 UI 状态;addDisposer(fn,label) 注册清理;dispose(reason) 停止插件并释放全部资源。 |
events | on(type,handler)、once、off、emit、clear、listenerCount。on/once 返回取消函数。 |
logger | scope(name) 创建作用域 Logger;info、warn、error 同时写控制台和 log 事件。 |
storage | scope(name)、get/set/remove、getJson/setJson、keys。Core 负责真实 LocalStorage 前缀与 JSON 序列化。 |
timers | setTimeout(fn,ms,label)、setInterval(fn,ms,label),返回取消函数并参与 Core 热重载清理。 |
hooks | wrapMethod(owner,name,handler,{label})。Handler 接收 {original,args,thisArg,callOriginal};返回的取消函数恢复包装前方法。 |
dom | createOverlay、createPanel、createButton、toast。创建结果包含元素与移除函数。 |
browser | openUrl、request、requestText、requestJson、requestHeaders。请求默认不带凭据、禁用缓存并在 5 秒后中止。 |
laya | getLaya()、getStage()、status()、whenReady()、walk()、find()、snapshot()。节点引用保持为原始 Laya 对象。 |
runtime | start()、stop()、poll()、status()、getBattle(),封装战斗、窗口和公开日志生命周期。 |
plugins | inspect/addFromUrl/install、enable/disable/remove、checkOne/checkUpdates/update、settings/config/invokeAction/list、插件源与孤儿配置管理方法。 |
update | check()、reload()、status();reload 执行当前页面 Core 热替换。 |
ui | mount()、openWindow(tab)、closeWindow()、selectTab()、refresh()、confirmAction()、toast()、status()。 |
install(context) {
function applyDeveloperMode() {
if (context.framework.developerMode) {
// 创建或启用仅用于开发的行为。
} else {
// 关闭并清理仅用于开发的行为。
}
}
applyDeveloperMode();
context.addDisposer(
context.events.on("core:developer-mode-changed", applyDeveloperMode),
"developer-mode"
);
}
插件安装时先读取 context.framework.developerMode,可以获得 Core 启动时恢复的值。运行期间切换开关时,字段先更新,再触发事件;只在动作执行时判断的代码也可以直接读取字段,不需要缓存副本。
插件定义
插件是可直接下载、无需构建的经典 JavaScript 文件。Core 在执行代码前解析 Header,用于安装确认和版本检测;正文必须调用 SgsFramework.plugins.define()。
| Header | 含义 |
|---|---|
@id | 稳定且唯一的插件 ID;更新包必须保持一致。 |
@name / @version | 显示名称与可比较版本号。 |
@description | 安装确认框和插件列表中的简介。 |
@permissions | 逗号分隔的能力声明,仅披露,不构成沙箱。 |
@updateMode | default、header 或 api,不区分大小写。 |
@versionHeader | Header 模式读取的响应头,默认 x-sgs-plugin-version。 |
@versionApi | API 模式 JSON 地址;相对地址按插件 URL 解析。 |
// ==SgsPlugin==
// @id example.plugin
// @name Example Plugin
// @version 1.0.0
// @description Example description.
// @permissions core.events,browser.dom-overlay
// @updateMode default
// ==/SgsPlugin==
(() => {
SgsFramework.plugins.define({
id: "example.plugin",
manifest: {
name: "Example Plugin",
version: "1.0.0",
description: "Example description.",
permissions: ["core.events", "browser.dom-overlay"]
},
defaults: {},
settings: [],
install(context) {
const remove = context.events.on("battle:end", () => {});
context.addDisposer(remove, "battle-end");
return () => {};
}
});
})();
context 提供 framework、id、manifest、sourceUrl、events、logger、storage、config、timers、hooks、dom、browser、laya、runtime 和 addDisposer。install 返回的函数也会在禁用、更新、删除或 Core 热替换时调用。
插件配置
插件声明配置分组、默认值和可选动作,Core 负责控件排版、保存、校验、错误提示与重置。每个插件可独立收起;没有配置项的插件显示“无可配置项”,也不显示重置按钮。
type | 值与触发行为 |
|---|---|
toggle | 保存布尔值;声明 onChange 时,在保存成功后调用对应动作。 |
button | action / onClick 指向插件动作;插件未启动时不可用。 |
text | 保存字符串;支持 inputType、数值范围、长度范围和多个正则 patterns。 |
select | 按 options: [{value,label}] 显示下拉列表并保存选中值;可声明 onChange。 |
custom | render / action 接收 Core 创建的 host;插件只拥有该 host 内部内容。 |
defaults: { enabled: true, mode: "safe" },
settings: [{
id: "general",
name: "General",
items: [
{ key: "enabled", name: "Enabled", type: "toggle", onChange: "apply" },
{ key: "mode", name: "Mode", type: "select", options: [
{ value: "safe", label: "Safe" },
{ value: "full", label: "Full" }
], onChange: "apply" }
]
}],
actions: { apply(context, value, key) {} }
context.config.get/set/reset/defaults 与配置页共享同一状态。Core 先校验并序列化新值,再触发配置动作,因此动作读取 context.config 时已经是新值。首次安装没有历史配置或用户重置时,使用插件声明的 defaults。
AutoRewards 显示“领取周卡月卡、自动免费将池、自动免费祈福、自动官阶俸禄、自动签到、自动砍树”六个 Toggle。六项默认开启;免费常山战鼓没有独立开关,会在每次自动检查中处理。
AutoRewards 的“签到策略”下拉列表提供“不自动签到、自动签到-跳过试用、自动签到-跳过使用-月底领”。“自动签到”Toggle 是签到子系统总开关;下拉选择“不自动签到”也会停用整个签到子系统。“月底领”只在 +8 服务器日历的当月最后一天允许领取试用武将奖励,其他日期仍跳过。
Core Tab 的“开发者模式”是 Core 自身设置,不属于插件配置。切换它不修改任何插件的 defaults/settings;插件通过公共字段和 Core 事件决定开发专用行为。
更新检测
| 模式 | 请求与版本来源 |
|---|---|
default | GET 插件 JavaScript,读取响应体并解析 Header 的 @version。 |
header | 通过 Fetch 发送 HEAD,不读取响应体;读取 @versionHeader 指定的响应头。 |
api | GET @versionApi JSON;必须提供 version,可提供本次下载使用的 url。 |
Core 恢复插件后自动检测插件版本,同时检测自身版本。插件管理页提供手动检测按钮;远程版本更高时,插件行显示更新按钮。Core 页显示当前/远程版本,并在有更新时显示热重载按钮。
插件更新会保存新源码、停止旧实例、执行新定义并恢复原启用状态。Core 热重载会释放旧 Core 的插件、Hook、定时器、监听器和 DOM,再由新 Core 从缓存源码恢复已启用插件。
跨域来源必须允许当前游戏页面读取响应。Header 模式若使用自定义响应头,还要通过 Access-Control-Expose-Headers 暴露该名称。
运行时事件
| 事件 | 触发证据与 Payload |
|---|---|
framework:ready | Core 服务和 UI 已发布;Payload 为 status() 快照。 |
core:developer-mode-changed | 用户成功切换开发者模式后触发;Payload 为 { enabled, previous }。事件处理器执行时,framework.developerMode 已经是 enabled。 |
battle:start | 有效可见的 TableGameScene/RogueLikeGameScene 且 manager.seats 存在;提供 scene、manager、sceneName、startedAt。 |
battle:end | Scene 离开、失去有效可见性、seats 消失或被替换;提供原 Scene/Manager、原因和结束时间。 |
battle:log | 来自公开 Laya HTML 战斗日志容器;提供 html、纯文本、节点和 Scene。 |
window:open | Stage 中有效可见的 Window/Dialog/Popup 节点出现;提供稳定对象 ID、原始节点和节点信息。 |
window:close | 此前可见窗口离开集合;提供同一对象 ID、原始节点和关闭时间。 |
| 插件事件 | plugin:installed/removed/started/stopped/updated、plugin:config-changed、plugin:config-reset。 |
Core 启动时从存储恢复开发者模式,并在恢复插件前公开该字段;启动恢复本身不触发变更事件。插件应在安装时读取一次字段,并在需要持续响应时注册 core:developer-mode-changed,同时把取消函数交给插件清理流程。
有效可见性会检查节点及父链的 visible、alpha、scaleX/scaleY 和 destroyed。Core 不通过 console.log 判断战斗,也不把可能残留的窗口注册表当作事实。
高层事件不足时,可从 framework.laya.getLaya()、getStage() 或事件 Payload 取得原始 Laya 对象并调用底层公开接口。
存储
sgs.framework.core.developer-mode
sgs.framework.ui.dock
sgs.framework.ui.window
sgs.framework.plugins.registry
sgs.framework.plugins.sources
sgs.framework.plugins.catalog-cache
sgs.framework.plugins.<pluginId>.code
sgs.framework.plugins.<pluginId>.config
sgs.framework.plugins.<pluginId>.data.*
sgs.framework.core.developer-mode 保存 Core 开发者模式,缺失时按 false 处理。Core 启动时先读取该键,再创建插件上下文;切换时先写入该键,保存成功后才更新公共字段与发送变更事件。写入失败时字段保持原值,Toggle 恢复原状态并显示错误提示。
context.config 保存插件声明的配置对象;context.storage 保存插件自己的内部数据。插件只传相对键,Core 自动添加 plugins.<pluginId> 前缀并负责字符串或 JSON 序列化。Core 的停靠位置与窗口几何信息由 sgs.framework.ui.* 独立保存。
Core 启动时把历史双分隔符键(例如 ui..dock、data..runtime-source)迁移为单点键;如果单点键已经存在,以单点键为准。
删除插件会删除注册记录和源码缓存,但保留 .config 与 .data.*。Core 页的“检查已删除插件配置”按插件 ID 汇总这些孤儿键、数量和字符数;用户确认删除时只移除该插件的配置/数据,不影响已安装或被禁用插件。
插件源
插件管理页按来源分组,每个官方、第三方或手动来源都可收起。固定官方源 /script/plugin/index.json 发布 SGS Card Tracker、SGS Blocker、SGS DebugTools 与 SGS AutoRewards。Catalog 中 DebugTools 继续使用稳定 ID sgs.automation,AutoRewards 使用独立 ID sgs.auto-rewards。
{
"name": "Source name",
"plugins": [
{ "id": "example.plugin", "url": "https://example/plugin.js" }
]
}
第三方源可添加名称与 Catalog URL,并支持禁用、重命名和删除;删除来源不删除已经安装的插件。从 URL 安装时,Core 先下载并解析 Header,显示名称、版本、简介、来源 URL 和权限列表,用户确认后才执行代码。Catalog 的 ID 与插件 Header/定义不一致时,安装校验失败。
官方插件
官方源提供 SGS Card Tracker、SGS Blocker、SGS DebugTools 与 SGS AutoRewards。它们使用普通插件相同的安装、启用、禁用、更新和删除流程;停止时释放所属 Hook、DOM、定时器、离屏对象、等待任务和可调用服务。
SGS Card Tracker
无配置项。插件加载与 Scripts/dist/sgs-runtime.js 相同的运行时构建,只在有效可见的 TableGameScene 或 RogueLikeGameScene 中显示 UI。离开战斗后停止描画并清理临时状态。已知牌只来自自己的手牌、公开日志或窗口、公开区和明确揭示牌的协议记录;隐藏对手 handCards 不作为已知事实。
SGS Blocker
提供“屏蔽广告 / 屏蔽特效 / 屏蔽道具弹窗”三个独立开关。每项只操作已核对的当前 Laya 路径,不按宽泛类名隐藏节点,也不全局关闭 Tween、Timer、Animation 或渲染。
| 开关 | 作用范围 |
|---|---|
| 屏蔽广告 | 阻止 ModeScene.showAdView、隐藏当前广告视图、过滤类型 0x27cb 的军典入口,并通过公开关闭方法处理 AdPushWindow。 |
| 屏蔽特效 | 在 XMLHttpRequest.open 中只替换已确认的特效资源模式;保留铁索/酒白名单和两个已确认的结束回调路径。 |
| 屏蔽道具弹窗 | 处理 GetPropSpecialWindow、GeneralOpenResultWindow、SkinOpenResultWindowNew、OldbackOneClickDrawAwdWin 和 TianShuWindow type=6。 |
禁用、更新、删除 Blocker 或热替换 Core 时,每个 Hook 都恢复包装前的方法。
SGS DebugTools
无配置项。插件显示名为 DebugTools,但为使已安装的旧插件原位更新,ID 仍是 sgs.automation。它通过 plugins.invokeAction() 暴露四个调试函数;相同函数的并发调用复用同一 Promise。
| 函数 | 目标与结果 |
|---|---|
openRogueMap | 查找 modeId=151 并打开山河图;目标 Scene 已显示时返回 already-open。 |
openGeneralTrial | 查找 modeId=147 并打开武将试炼;试炼 Scene 或挑战窗口已显示时返回 already-open。 |
getCharacterInfo | 返回自己角色的 ID、昵称、等级/经验、元宝、绑定元宝、银两、在线时长、VIP、官阶、公会等白名单字段,不打开资料窗口。 |
claimDailyTaskRewards | 离屏循环领取 CanAward 的每日任务,再处理运行时提供的累计次数奖励;不假定任务数量,也不打开任务栏。 |
const invokeDebug = (action) => SgsFramework.plugins.invokeAction(
"sgs.automation", action
);
const rogue = await invokeDebug("openRogueMap");
const trial = await invokeDebug("openGeneralTrial");
const character = await invokeDebug("getCharacterInfo");
const dailyTasks = await invokeDebug("claimDailyTaskRewards");
函数返回 ok、action、status 与对应的前后状态。插件未启用时返回 inactive。模式入口不选择武将、不开始挑战、不确认弹窗,也不继续战斗。
SGS AutoRewards
插件启用后每秒读取一次运行时管理器公开的 ServerTime/ServerDate。首次取得 +8 服务器时钟时执行一次启动检查;服务器日期变化时执行一次跨日检查。检查按“卡类、免费将池、免费祈福、免费常山战鼓、官阶俸禄、签到、发财树”顺序串行,正在执行时不会并发重复,期间检测到的跨日会排队补跑。
| 项目 | 检查与领取规则 |
|---|---|
| 月卡 / 周卡 | 先同步卡数据。启动检查只领取服务器状态标记可领的每日奖励和银两;跨日检查对仍有效的类型 3 月卡和类型 2 周卡各发送一次每日奖励及银两请求,以绕过跨日按钮状态未刷新的客户端问题。月卡随后独立领取全部满足条件的累计天数奖励;周卡没有累计路径。 |
| 免费将池 | 免费掉落 ID、免费 UI 标记、管理器免费状态与空闲请求池同时成立时,只用 Free_MianFei_Draw 抽取一次。 |
| 免费祈福 | 先同步全部轮次;只对 freeOpen、活动时间、抽取时间与 CanFreeBless 同时成立的轮次发送 count=1。 |
| 免费擂鼓 | 已加入公会且 CanDrum 时,重复常山战鼓的免费请求,直到 LeftFreeDrumTimes 为零或一次请求未确认。该项没有独立开关。 |
| 官阶俸禄 | 分别判断、领取并确认每日俸禄与每周俸禄;其中一项不存在不影响另一项。 |
| 签到 | 只处理正常免费签到状态;补签状态不点击。每日签到后或已签到时都检查累计签到奖励。试用武将按签到策略跳过;“月底领”只在 +8 服务器日历当月最后一天放行。 |
| 发财树 | 可砍时发送一次砍树请求;免费奖励物品存在且没有必须处理的树事件时再领奖。跨日时即使客户端仍缓存“已领”状态也会尝试一次砍树;启动时相信重登后状态。 |
插件提供六个 Toggle 与一个签到策略下拉列表,默认开启全部 Toggle,并默认“自动签到-跳过试用”;第三项按界面显示为“自动签到-跳过使用-月底领”。AutoRewards 不暴露手动领奖 Action;配置变更影响下一次启动/跨日自动检查。禁用插件会停止服务器时钟监听并取消后续等待。
权限与边界
插件权限是安装前披露,不是能力沙箱,也不会按权限名阻止执行。插件与页面脚本处于同一环境,可以读取运行时对象、调用公开方法、发出真实游戏请求或创建 DOM。只安装可信来源,并审查声明网络、Hook、Laya、游戏请求或 DOM 能力的代码。
开发者模式只是插件可读取的布尔信号,不授予权限、不隔离 Debug 代码,也不改变生命周期。使用该信号的插件必须自己在关闭时清理开发专用 Hook、监听器、定时器和 DOM,并把最终清理交给 Disposer。
Card Tracker 不把隐藏对手手牌当作已知事实。Blocker 只使用具体、可恢复且经过运行时核对的路径。DebugTools 的模式入口会改变当前界面,但只调用活动场分页和模式入口;不选择武将、不开始挑战、不确认弹窗,也不调用购买路径。入口条件不成立时返回结构化失败。
DebugTools 的 getCharacterInfo 只读取当前 Stage 中 IsSelf=true 的角色对象,并构造白名单结果。它不返回原始对象、Session、重连 Token、二级密码、账号散列或登录 IP;对象不可用时不打开资料窗口或发出资料查询。claimDailyTaskRewards 会发出真实领奖请求,但只处理每日且 CanAward 的任务和运行时累计奖励数组,不以固定任务数量判断正确性,也不把任务 View 加入 Stage。
AutoRewards 启用后会在首次取得服务器时间和每次 +8 服务器日期变化时发出真实请求。跨日卡类流程有意忽略客户端每日/银两按钮的本地可领标记:只要卡仍有效,就对月卡和周卡各发送一次对应请求;服务器仍负责最终资格校验。启动检查会先同步状态并避免对已领取卡奖励重复请求。月卡累计奖励始终按运行时的可领/已领数组逐项判断。
AutoRewards 不调用月卡/周卡购买或续费方法。武将抽取必须同时满足免费掉落 ID、免费 UI、管理器免费状态和请求空闲,并只使用 Free_MianFei_Draw;祈福必须通过免费配置与时间检查,只发送 count=1。两者均不调用付费抽取、灯购买、道具购买或元宝补足路径。
免费擂鼓只调用常山战鼓的 SendGuildDonate(),不调用亮银槌、鎏金锤、背包道具、SendGuildDrumUse() 或元宝确认。官阶只调用资格为真的每日/每周俸禄接口,不升级官阶,也不领取其他官阶活动奖励。
签到只在普通免费状态执行,补签状态不会点击,因此不会发起补签消耗。试用武将由 IsTryGeneralSkin 判定;跳过策略不会污染将池,“月底领”会在服务器当月最后一天放行该奖励。累计签到奖励仍会独立处理。
发财树只在免费奖励物品就绪时领奖,不购买物品。存在 HasTriggerEvent 时返回 event-required,不会自动打开、跳过或伪造事件窗口;该次可能需要用户手动处理后等待下一次自动检查。跨日的强制砍树请求只用于绕过客户端缓存的昨日状态,服务器仍负责资格校验。
AutoRewards 使用串行队列避免并发重复;禁用、更新、删除插件或热替换 Core 时停止时钟监听、取消等待并销毁离屏对象。已送达服务器的请求无法撤回,但清理后不会继续下一轮请求。DebugTools 停止后 Action 返回 inactive。
浏览器 Fetch 受 CORS 约束。第三方插件与 Catalog 来源需要允许游戏页面跨域访问;Header 更新模式还要显式暴露版本响应头。
没有匹配的章节。