architecture - mclucy/lucy GitHub Wiki
架构总览
本页面描述 Lucy 的整体系统架构,适用于希望了解各模块职责、调用关系与数据流向的开发者。阅读前建议先熟悉 核心概念。Lucy 是一个面向 Minecraft 服务器的包管理器,其设计遵循"模块分层、平台感知、多源聚合"的原则,将 CLI 入口、环境探测、上游路由、依赖解析、安装执行与缓存管理各自封装为独立包。
关键要点
关键概念与术语
核心对象
- Platform(平台):mod loader 或服务端软件(如 Fabric、Forge、MCDR、Vanilla),用于定义运行时环境。
- Project(项目):具体 mod 或插件(如
fabric-api、carpet)。 - Package(包):绑定到特定平台与版本的 Project 编译产物,是安装操作的原子单位。
- Package ID:格式为
[platform]/[project]@[version],例如fabric/fabric-api@latest。
数据流样例
以下链路描述 lucy add 从输入到缓存落盘的完整执行路径:
用户输入
│
▼
main.go → cmd.Cli.Run() main.go:15
│
▼
cmd/cmd_add.go → actionAdd() cmd/cmd_add.go:49
│ syntax.Parse() 解析 PackageId
│ 版本默认值收敛为 VersionCompatible cmd/cmd_add.go:54
│
▼
install/install.go → Install() install/install.go:25
│ probe.ServerInfo() 获取环境快照 install/install.go:39
│ ensureServerPlatformMatch() 校验平台兼容性 install/install.go:35
│
▼
upstream/routing/routing.go → ResolveProviders() upstream/routing/routing.go:68
│ 依据 platform + source 选出有序 Provider 列表
│ SourceAuto 时走 topology 感知路由 upstream/routing/routing.go:92
│
▼
upstream/routing/routing_execution.go → FetchMany() upstream/routing/routing_execution.go:94
│ 对所有 Provider 并行执行 Fetch
│ 返回全部成功结果(非优先返回)
│
▼
install/install.go → installer(p) install/install.go:98
│ 按 Platform 分发至对应 platformInstaller
│ registerInstaller() 注册模式 install/install.go:18
│
▼
cache/cache.go → Network() cache/cache.go:25
content-addressed 缓存层(artifact / metadata)
// ../lucy/main.go:13-18
func main() {
defer logger.DumpHistory()
if err := cmd.Cli.Run(context.Background(), os.Args); err != nil {
logger.ReportError(err)
}
}
[!WARNING] 以下内容基于非完整代码推断,可能与实际预期行为存在偏差。 请以
install/install.go:43中的实际实现为准。Install()在解析 Provider 时会优先使用ResolveProvidersByTopology(),并在检测到 MCDR 环境时追加 MCDR Provider,从而在混合服务器(如 Fabric + MCDR)中实现跨源并行拉取。
并行执行策略
SearchMany 与 FetchMany 均采用 sync.WaitGroup + goroutine slot 模式:
- 每个 Provider 对应一个预分配 slot
- 并发完成后统一收集结果
- 结果顺序由 slot 下标保证,与 goroutine 调度顺序无关
参考:upstream/routing/routing_execution.go:55、upstream/routing/routing_execution.go:109。
[!WARNING] 此功能尚未完整/正确实现
FirstFetch并行优先返回功能尚未实现,当前调用将直接 panic。详见upstream/routing/routing_execution.go:155。
probe.ServerInfo() 使用 sync.RWMutex 实现 memoization:
- 首次调用:持写锁构建缓存
- 后续调用:持读锁直接返回缓存副本(值拷贝,非指针)
参考:probe/probe.go:39。
// ../lucy/probe/probe.go:27-58
var (
serverInfoMu sync.RWMutex
serverInfoCache types.ServerInfo
serverInfoReady bool
resetProbeExecCache = func() {}
resetProbeFileLockCache = func() {}
)
// ServerInfo is the exposed function for external packages to get serverInfo.
// The value is cached after the first build, and read access is blocked while
// Rebuild refreshes the cache.
func ServerInfo() types.ServerInfo {
serverInfoMu.RLock()
if serverInfoReady {
cached := serverInfoCache
serverInfoMu.RUnlock()
return cached
}
serverInfoMu.RUnlock()
serverInfoMu.Lock()
defer serverInfoMu.Unlock()
if !serverInfoReady {
resetProbeMemoizedStateLocked()
serverInfoCache = buildServerInfo()
serverInfoReady = true
}
return serverInfoCache
}
buildServerInfo() 内部会并发启动 7 个 goroutine,分别采集:
- 环境信息
- 工作路径
- 可执行文件信息
- mod 路径
- 已安装包列表
- 存档路径
- 服务器活跃状态(文件锁检测)
参考:probe/probe.go:86。
[!WARNING] 以下内容基于非完整代码推断,可能与实际预期行为存在偏差。
请以 probe/probe.go:166 中的实际实现为准:EnrichTopologyFromPackages() 会在全部 goroutine 完成后执行,先将已安装包元数据回填到 RuntimeTopology,再注入 executable 的 RuntimeIdentity 包。该顺序使 topology enrichment 优先利用包名证据完成能力推断。
模块职责矩阵
| 模块 | 职责 | 关键文件 |
|---|---|---|
cmd |
CLI 入口、flag 解析、用户交互、shell 补全 | cmd/cmd_add.go:17 |
probe |
服务器环境探测、topology 构建、memoization | probe/probe.go:39 |
upstream/routing |
Provider 选择策略、平台/source 到 Provider 的映射 | upstream/routing/routing.go:68 |
upstream/routing(执行层) |
并行 Fetch/Search/Info 执行、错误聚合 | upstream/routing/routing_execution.go:94 |
upstream/{modrinth,curseforge,mcdr,github} |
各数据源 Provider 实现(search / fetch / info / dependencies) | upstream/routing/routing.go:46 |
dependency |
版本解析:semver、Maven range、Minecraft 版本格式 | dependency/version_dependency.go:1 |
install |
platformInstaller 注册与分发、平台兼容性校验、安装执行 | install/install.go:18 |
cache |
content-addressed 缓存(artifact / metadata)、TTL 与容量策略 | cache/cache.go:25 |
types / exttype |
全局领域模型(PackageId、ServerInfo、RuntimeTopology 等) |
(综合多处源码) |
probe/internal/detector |
平台感知文件探测(jar 元数据、MCDR 插件、可执行文件识别) | probe/probe.go:221 |
[!WARNING] 以下内容基于非完整代码推断,可能与实际预期行为存在偏差。
请以 dependency/ 目录中的实际实现为准:dependency 包当前主体文件可能为空(version_dependency.go 仅含 package 声明);版本解析逻辑可能直接在调用方通过 github.com/Masterminds/semver/v3 内联,或相关实现仍在开发中。
相关页面
参考文件
本页内容综合引用以下文件,基线 commit:86d3480cffa821b9b9c0747263a637b2973a7366。
main.go:15— CLI 入口,cmd.Cli.Run()调用点cmd/cmd_add.go:49—actionAdd实现,syntax.Parse()与install.Install()调用cmd/cmd_add.go:54— 版本默认值收敛逻辑(VersionLatest→VersionCompatible)upstream/routing/routing.go:35—autoProviders默认 Provider 列表定义upstream/routing/routing.go:68—ResolveProviders()平台/source 路由逻辑upstream/routing/routing.go:92—ResolveProvidersFromTopology()topology 感知路由upstream/routing/routing_execution.go:38—SearchMany()并行搜索实现upstream/routing/routing_execution.go:94—FetchMany()并行拉取实现upstream/routing/routing_execution.go:149—FirstFetch()未实现(panic)upstream/routing/routing_execution.go:160—FirstInfo()channel-based 并发实现probe/probe.go:39—ServerInfo()memoized 读写锁实现probe/probe.go:86—buildServerInfo()7 goroutine 并发构建install/install.go:18—registerInstaller()注册模式install/install.go:25—Install()主流程:路由 → 拉取 → 安装cache/cache.go:25—Network()singleton handler,content-addressed 缓存入口