architecture - mclucy/lucy GitHub Wiki

架构总览

本页面描述 Lucy 的整体系统架构,适用于希望了解各模块职责、调用关系与数据流向的开发者。阅读前建议先熟悉 核心概念。Lucy 是一个面向 Minecraft 服务器的包管理器,其设计遵循"模块分层、平台感知、多源聚合"的原则,将 CLI 入口、环境探测、上游路由、依赖解析、安装执行与缓存管理各自封装为独立包。

关键要点

关键概念与术语

核心对象

  • Platform(平台):mod loader 或服务端软件(如 Fabric、Forge、MCDR、Vanilla),用于定义运行时环境。
  • Project(项目):具体 mod 或插件(如 fabric-apicarpet)。
  • 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)中实现跨源并行拉取。

并行执行策略

SearchManyFetchMany 均采用 sync.WaitGroup + goroutine slot 模式:

  • 每个 Provider 对应一个预分配 slot
  • 并发完成后统一收集结果
  • 结果顺序由 slot 下标保证,与 goroutine 调度顺序无关

参考:upstream/routing/routing_execution.go:55upstream/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,分别采集:

  1. 环境信息
  2. 工作路径
  3. 可执行文件信息
  4. mod 路径
  5. 已安装包列表
  6. 存档路径
  7. 服务器活跃状态(文件锁检测)

参考: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 全局领域模型(PackageIdServerInfoRuntimeTopology 等) (综合多处源码)
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:49actionAdd 实现,syntax.Parse()install.Install() 调用
  • cmd/cmd_add.go:54 — 版本默认值收敛逻辑(VersionLatestVersionCompatible
  • upstream/routing/routing.go:35autoProviders 默认 Provider 列表定义
  • upstream/routing/routing.go:68ResolveProviders() 平台/source 路由逻辑
  • upstream/routing/routing.go:92ResolveProvidersFromTopology() topology 感知路由
  • upstream/routing/routing_execution.go:38SearchMany() 并行搜索实现
  • upstream/routing/routing_execution.go:94FetchMany() 并行拉取实现
  • upstream/routing/routing_execution.go:149FirstFetch() 未实现(panic)
  • upstream/routing/routing_execution.go:160FirstInfo() channel-based 并发实现
  • probe/probe.go:39ServerInfo() memoized 读写锁实现
  • probe/probe.go:86buildServerInfo() 7 goroutine 并发构建
  • install/install.go:18registerInstaller() 注册模式
  • install/install.go:25Install() 主流程:路由 → 拉取 → 安装
  • cache/cache.go:25Network() singleton handler,content-addressed 缓存入口