《MMO 服务器开发》附录 A aegis 卷一 API 与模块速查

《MMO 服务器开发》卷一附录 A:把 aegis 第 1–17 章的 internal 包收成一张速查表。给出模块地图、每包导出类型/函数/方法签名与出处节号,钉死 11 字节帧头、length = 7 + N 与全卷 14 个命令号,并附一段用 go/ast 解析源码、断言 API 清单与包内导出符号逐字一致的 L1 自检。

附录 A aegis 卷一 API 与模块速查

卷一从第 1 章到第 17 章把 aegis 拆成十来个 internal 包,每章各写几段可编译、可 go test 的代码。本附录把全卷的导出面收成三类速查:模块地图回答「东西在哪」,按包 API 表回答「怎么调」,帧格式与命令号回答「线上长什么样」。表里每个签名都逐字取自对应节的 // file: 代码块,出处节号指向第一次给出它的那一节。

模块地图

aegis 每章一个独立 module,包路径相对该章 module 根。下表按依赖顺序排列。

包路径职责出处节关键文件
internal/overview分层骨架与分工清单1.1–1.3layers.go
internal/netioReactor 循环与承载测量2.1–2.3reactor_loop.go
internal/proto帧头、分帧、版本协商3.1–3.3header.go / frame.go
internal/codec二进制读写、varint、零拷贝4.1–4.3binary.go / zerocopy.go
internal/kcp可靠 UDP 与弱网模拟5.1–5.3kcp.go / hol.go
internal/ws握手、分片、心跳、票据6.1–6.3frame.go / server.go
internal/session会话表、顶号迁移、网关7.1–7.3table.go / gateway.go
internal/router路由表、序号窗口、广播8.1–8.3router.go / fanout.go
internal/timing时间轮、冷却、世界时钟9.1–9.3wheel.go / worldclock.go
internal/actor邮箱、实体运行时、背压10.1–10.3mailbox.go / entity.go
internal/idem幂等窗口、指纹、结算11.1–11.3window.go / reward.go
internal/sync状态/帧同步、快照、AOI12.1–12.3world.go / snapshot.go
internal/predict本地预测、和解、插值13.1–13.3predict.go / interpolate.go
internal/clock时间线、回滚、公平性14.1–14.3clock.go / rewind/ / fairness/
internal/authority信任契约、移动校验、限速15.1–15.3boundary.go / move.go
internal/guard连接配额、封包加密16.1–16.3limit.go / crypto.go
internal/gateway + cmd/aegis进程装配与入口17.1–17.3config.go / main.go
internal/handoff + internal/review移交表与回顾台账18.1 / 18.3handoff.go / ledger.go

帧格式与命令号速查

全卷共用同一套 wire 契约,第 3 章定死、第 4 章起一律复用,不得自创。固定头 11 字节,length 统计它自身之后的字节数:

固定头(HeaderSize = 11)
  length  uint32  大端   bytes 0..3    length = 7 + N
  cmd     uint16  大端   bytes 4..5    命令号
  seq     uint32  大端   bytes 6..9    连接内自增序号
  flags   uint8          byte 10       标志位
载荷 N 字节             bytes 11..11+N
帧总长 = 11 + N     MaxPayload = 1 << 20

命令号 type Cmd uint16,高字节是模块号(ModuleSystem 0x00 / ModulePlayer 0x01 / ModuleScene 0x02 / ModuleChat 0x03 / ModuleGuild 0x04 / ModuleMail 0x05),低字节是模块内序号。全卷实际用到的命令号如下(均出自 3.1 消息头与命令号 ):

模块符号值说明
系统 0x00CmdHeartbeatReq0x0001客户端心跳
系统 0x00CmdHeartbeatResp0x0002心跳应答
系统 0x00CmdErrorNotify0x0003错误通知
系统 0x00CmdLoginReq0x0010登录请求
系统 0x00CmdLoginResp0x0011登录响应
世界 0x01CmdEnterWorldReq0x0101进入世界
世界 0x01CmdEnterWorldResp0x0102进入世界应答
世界 0x01CmdAttrSync0x0103属性同步
场景 0x02CmdMoveReq0x0201移动请求
场景 0x02CmdMoveResp0x0202移动应答
场景 0x02CmdAoiAdd0x0203AOI 进入视野
场景 0x02CmdAoiRemove0x0204AOI 离开视野
聊天 0x03CmdChatSend0x0301发送聊天
聊天 0x03CmdChatRecv0x0302收到聊天

标志位 type Flags uint8:FlagRequest 1<<0 / FlagResponse 1<<1 / FlagPush 1<<2 / FlagCompressed 1<<3 / FlagEncrypted 1<<4 / FlagFragment 1<<5。

按包 API 速查

下表按包给出导出类型与关键方法,签名逐字取自对应节代码块;省略号表示字段较多,完整定义见出处节。

internal/proto(3.1–3.3)

符号签名说明出处
Cmd / MakeCmdtype Cmd uint16 / func MakeCmd(module, sub uint8) Cmd命令号3.1
HeaderSize / MaxPayloadconst HeaderSize = 11 / const MaxPayload = 1 << 20头长与上限3.1
Header / PayloadLentype Header struct { Length uint32; Cmd Cmd; Seq uint32; Flags Flags }固定头3.1
EncodeHeader / DecodeHeaderfunc EncodeHeader(dst []byte, h Header) / func DecodeHeader(src []byte) (Header, error)头编解码3.1
Flags / Has 等type Flags uint8;Has / HasAll / With / Clear / String标志位3.1
Frametype Frame struct { Cmd Cmd; Seq uint32; Flags Flags; Payload []byte }完整帧3.2
Encode / EncodeIntofunc Encode(f Frame) []byte / func EncodeInto(dst []byte, f Frame) []byte帧编码3.2
DecodeFromfunc DecodeFrom(buf []byte) (Frame, int, error)从缓冲解一帧3.2
ReadFrame / NewDecoderfunc ReadFrame(r io.Reader) (Frame, error) / func NewDecoder(r io.Reader, bufSize int) *Decoder流式读帧3.2
ProtocolVersion / Negotiateconst ProtocolVersion uint16 = 3 / func Negotiate(clientVersion uint16) LoginResp版本协商3.3
DecodePlayerProfileV2func DecodePlayerProfileV2(payload []byte) (PlayerProfile, error)多版本解码3.3

internal/codec(4.1–4.3)

符号签名说明出处
Writer / NewWritertype Writer struct{ buf []byte } / func NewWriter(capacity int) *Writer写缓冲4.1
Writer.PutUint8 等func (w *Writer) PutUint8(v uint8) … PutUint64 / PutFloat32定长写入4.1
Writer.PutVarint / PutStringfunc (w *Writer) PutVarint(v uint64) / func (w *Writer) PutString(s string) error变长 / 前缀写入4.1
Reader / NewReadertype Reader struct { buf []byte; pos int } / func NewReader(b []byte) *Reader读缓冲4.1
Reader.GetUint8 等func (r *Reader) GetUint8() (uint8, error) … GetUint64 / GetVarint定长 / 变长读取4.1
Reader.SliceBytes / SharesBackingArrayfunc (r *Reader) SliceBytes() ([]byte, error) / func SharesBackingArray(a, b []byte) bool零拷贝视图4.2
SplitFramefunc SplitFrame(raw []byte) (payload []byte, err error)复用帧头切载荷4.2
MoveNotify / DecodeFlattype MoveNotify struct { PlayerID uint64; X, Y, Z float32; Speed float32; Timestamp uint64 }扁平化协议4.3
ErrShortBuffer 等var ErrShortBuffer = errors.New("codec: 读取越界,剩余字节不足")越界错误4.1

internal/kcp(5.1–5.3)

符号签名说明出处
KCP / NewKCPtype KCP struct { ... } / func NewKCP(conv uint32, output outputFunc) *KCP可靠 UDP 会话5.2
KCP.Send / Recvfunc (k *KCP) Send(data []byte) int / func (k *KCP) Recv(buf []byte) int收发数据5.2
KCP.Input / Updatefunc (k *KCP) Input(data []byte) int / func (k *KCP) Update(now uint32)喂包与推进5.2
SimConfig / Simulatetype SimConfig struct { ... } / func Simulate(cfg SimConfig, streams int) []float64弱网模拟5.1 / 5.3

internal/ws(6.1–6.3)

符号签名说明出处
Frametype Frame struct { Fin bool; Opcode byte; Masked bool; Payload []byte }单帧6.1
OpText 等const OpText byte = 0x1 … OpBinary 0x2 / OpClose 0x8 / OpPing 0x9 / OpPong 0xA操作码6.1
ReadFrame / WriteFramefunc ReadFrame(r io.Reader, maxPayload uint64) (*Frame, error) / func WriteFrame(w io.Writer, fin bool, opcode byte, payload []byte, masked bool) error单帧读写6.1
ReadMessage / ReadMessageMaskedfunc ReadMessage(r io.Reader, maxPayload uint64) (byte, []byte, error)读完整消息6.1
Server / NewServertype Server struct { ReadTimeout time.Duration; PingInterval time.Duration; MaxPayload uint64; ... }服务端6.2
TicketManager / Issue / Verifyfunc (m *TicketManager) Issue(account string, connSeq uint64, ttl time.Duration, now time.Time) (string, error)重连票据6.3
CloseGoingAway 等const CloseGoingAway uint16 = 1001 / CloseProtocolErr 1002 / ClosePolicyViolat 1003关闭码6.2

internal/session(7.1–7.3)

符号签名说明出处
ConnID / Conn / NewConntype ConnID uint64 / func NewConn(send func(cmd uint16, payload []byte) error) *Conn连接对象7.1
Conn.Send / Close / Closedfunc (c *Conn) Send(cmd uint16, payload []byte) error / func (c *Conn) Close() error发送与关闭7.1
Table / NewTabletype Table struct { ... } / func NewTable() *Table会话表7.1
Table.Bindfunc (t *Table) Bind(account string, c *Conn) (evicted *Conn)绑定账号7.1 / 7.2
Table.Lookup / Get / Removefunc (t *Table) Lookup(account string) (*Conn, bool) / func (t *Table) Get(connID ConnID) (*Conn, bool)查询与摘除7.1
Registrar / Login / Kick / Migratefunc (r *Registrar) Migrate(account string, c *Conn) (old *Conn) / func (r *Registrar) Kick(account string) error顶号与迁移7.2
Gateway / NewGatewayfunc NewGateway(table *Table, auth func(account, token string) bool, route func(cmd uint16) string) *Gateway网关边界7.3
Gateway.Admit / OnCommand / Routedfunc (g *Gateway) OnCommand(account string, cmd uint16) (string, error)准入与路由7.3
ErrNoRoute 等var ErrNoRoute = errors.New("session: no route")错误哨兵7.3

internal/router(8.1–8.3)

符号签名说明出处
Kind + 三常量type Kind uint8;KindRequest / KindResponse / KindPush消息类型8.1
Handlertype Handler func(*Context) error处理器8.1
Conntype Conn interface { ID() uint64; Send(cmd Cmd, seq uint32, flags uint8, payload []byte) error }连接抽象8.1
Context / Reply / Pushtype Context struct { Conn Conn; Frame Frame; ReplyCmd Cmd } / func (c *Context) Reply(payload []byte) error分发上下文8.1
Router / New / Dispatchfunc New() *Router / func (r *Router) Dispatch(c Conn, f Frame) error路由表与分发8.1
Router.HandleRequest 等func (r *Router) HandleRequest(cmd, reply Cmd, h Handler) / HandleNotify / HandlePush注册处理器8.1
SeqWindow / Acceptfunc NewSeqWindow(size uint32) *SeqWindow / func (w *SeqWindow) Accept(seq uint32) bool位图去重窗口8.2
ReorderBuffer / Pushfunc NewReorderBuffer(cap int) *ReorderBuffer / func (b *ReorderBuffer) Push(f Frame) (ready []Frame, buffered int, overflow bool)乱序重排8.2
Broadcaster / BroadcastEncodedfunc NewBroadcaster() *Broadcaster / func (b *Broadcaster) BroadcastEncoded(cmd Cmd, seq uint32, flags uint8, payload []byte) (sent int)编一次发 N 次8.3
WireConntype WireConn interface { ID() uint64; WriteFrame(buf []byte) error }广播连接8.3

internal/timing(9.1–9.3)

符号签名说明出处
Clock / Cooldown / NewCooldowntype Clock func() time.Time / func NewCooldown(now Clock) *Cooldown技能冷却9.1
Cooldown.Use / Ready / Remainingfunc (c *Cooldown) Use(entity uint64, skill uint32, cd time.Duration) bool冷却判定9.1
Watchdog / NewWatchdogfunc NewWatchdog(now Clock, onExpire func(id uint64)) *Watchdog心跳看门狗9.1
Wheel / New / Afterfunc New(interval time.Duration, slots int) *Wheel / func (w *Wheel) After(d time.Duration, fn func()) uint64时间轮与挂任务9.2
WorldClock / NewWorldClock / Advancefunc NewWorldClock(step time.Duration, policy Policy) *WorldClock / func (c *WorldClock) Advance(wall time.Duration) int墙钟折算逻辑步9.3
WorldClock.SetMaxSteps / SetScalefunc (c *WorldClock) SetMaxSteps(n int) / func (c *WorldClock) SetScale(s float64)Tick 预算与倍速9.3
Policy + 三常量type Policy int;CatchUp / Drop / SlowDown落后策略9.3

internal/actor(10.1–10.3)

符号签名说明出处
EntityID / Msgtype EntityID uint64 / type Msg struct { Cmd uint16; Seq uint32; From EntityID; Payload []byte }实体 ID 与消息10.1
Entity / OutOfOrdertype Entity struct { ID EntityID; X int32; Y int32; HP int; ... } / func (e *Entity) OutOfOrder() int实体与乱序计数10.1 / 10.2
Handle / Send / Snapshottype Handle struct { ... } / func (h *Handle) Send(msg Msg) error / func (h *Handle) Snapshot() Entity实体句柄10.1
Registry / NewRegistryfunc NewRegistry(capacity int, policy RejectPolicy) *Registry实体注册表10.1
Registry.Spawn / Route / Lookupfunc (r *Registry) Spawn(id EntityID) *Handle / func (r *Registry) Route(id EntityID, msg Msg) error增删查与投递10.1
Mailbox / NewMailbox / Send / Recvfunc NewMailbox(capacity int, policy RejectPolicy) *Mailbox / func (m *Mailbox) Send(msg Msg) error有界邮箱10.2
RejectPolicy + 四常量type RejectPolicy int;RejectBlock / RejectDrop / RejectDegrade / RejectDisconnect满时策略10.2

internal/idem(11.1–11.3)

符号签名说明出处
CommandIDtype CommandID struct { Actor uint64; Cmd uint16; Seq uint32 }命令标识11.2
Fingerprint / FingerprintOftype Fingerprint uint64 / func FingerprintOf(cmd uint16, actor, token uint64, args ...uint64) Fingerprint业务指纹11.2
Deduper / NewDeduper / Observefunc NewDeduper(ttl time.Duration, n int) *Deduper / func (d *Deduper) Observe(cmd uint16, actor, token uint64, now time.Time, args ...uint64) (Fingerprint, bool)去重器11.2
Window / NewWindowfunc NewWindow(ttl time.Duration, n int) *Window幂等窗口11.1
Settler / NewSettler / Settlefunc NewSettler(win *Window, store Store) *Settler / func (s *Settler) Settle(fp Fingerprint, now time.Time) (st State, fresh bool)结算器11.3
Store / MemStoretype Store interface { Begin(key uint64) (State, bool); Commit(key uint64, st State) } / func NewMemStore() *MemStore持久约束11.3
State + 三常量type State uint8;StateNew / StateGranted / StateDelivered结算态11.3

internal/sync(12.1–12.3)

符号签名说明出处
Vec2 / Entity / Inputtype Vec2 struct{ X, Y float32 } / type Input struct { PlayerID uint32; MoveX int8; MoveY int8; Fire bool }世界基础类型12.1
World / NewWorldfunc NewWorld() *World权威世界12.1
World.Step / Replicate / Digestfunc (w *World) Step(inputs []Input) / func (w *World) Digest() uint64推进与摘要12.1
StateSyncServer / NewStateSyncServerfunc NewStateSyncServer(w *World, tickRate int) *StateSyncServer状态同步服务端12.1
LockstepServer / NewLockstepServerfunc NewLockstepServer(players int) *LockstepServer帧同步服务端12.1
LockstepServer.Submit / Advancefunc (s *LockstepServer) Advance() (LockstepFrame, bool)收帧与推进12.1
Snapshot / Delta / DeltaEntrytype Snapshot struct { Tick uint32; Entities []EntityState } / type Delta struct { Tick uint32; Baseline uint32; Entries []DeltaEntry }快照与增量12.2
Diff / EncodeFull / EncodeDeltafunc Diff(base, cur Snapshot) Delta / func EncodeFull(s Snapshot, seq uint32) []byte差分与编码12.2
FieldMask + 四常量type FieldMask uint8;FieldPos FieldMask = 1 << iota / FieldVel / FieldHP / FieldKind字段掩码12.2
Grid / NewGrid / Interestfunc NewGrid(n int, cols int32, cell float32) *Grid / func (g *Grid) Interest(id uint32, radius float32, dst []uint32) []uint32AOI 兴趣过滤12.3

internal/predict(13.1–13.3)

符号签名说明出处
Vec2 / Input / Speedtype Vec2 struct { X, Y float64 } / type Input struct { Seq uint32; MoveX int8; MoveY int8 } / const Speed = 5.0预测基础类型13.1
Predictortype Predictor struct { Pos Vec2; Pending []Input; lastSeq uint32 }本地预测器13.1
Predictor.ApplyLocalInput / NextSeqfunc (p *Predictor) ApplyLocalInput(in Input) Vec2 / func (p *Predictor) NextSeq() uint32本地先走一步13.1
Predictor.Reconcilefunc (p *Predictor) Reconcile(serverPos Vec2, ackedSeq uint32) (pos Vec2, replayed int, err float64)回滚与和解13.2
Interpolate / Extrapolatefunc Interpolate(buf []Snapshot, renderTick float64) Vec2 / func Extrapolate(s Snapshot, dt float64) Vec2插值与外推13.3

internal/clock(14.1–14.3,含 rewind / fairness)

符号签名说明出处
Source / Timeline / NewTimelinetype Source func() time.Duration / func NewTimeline(source Source) *Timeline时间线14.1
Timeline.Elapsed / ForwardJumps / BackwardJumpsfunc (t *Timeline) ForwardJumps() int / func (t *Timeline) BackwardJumps() int跳变统计14.1
Offset / Measuretype Offset struct { RTT time.Duration; Offset time.Duration } / func Measure(t1, t2, t3, t4 time.Duration) Offset偏移测量14.1
Pos / History / NewHistorytype Pos struct { T time.Duration; X, Y float64 } / func NewHistory(capacity int) *History回滚历史(rewind)14.2
History.Push / Atfunc (h *History) At(t time.Duration) (Pos, bool) / func (h *History) Push(p Pos)回看历史点14.2
RewindTo / HitTestfunc RewindTo(serverNow, rtt, interp time.Duration) time.Duration / func HitTest(h *History, t time.Duration, ax, ay, r float64) (bool, Pos)回退时刻与命中14.2
Policy / ClampRewindtype Policy struct { MaxRewind time.Duration; MaxRTT time.Duration } / func (p Policy) ClampRewind(claimed time.Duration) (time.Duration, bool)补偿上限(fairness)14.3
LagTracker / NewLagTrackerfunc NewLagTracker(baseline, spike time.Duration, window int) *LagTracker延迟尖峰跟踪14.3
LagTracker.Observe / SpikeRate / Suspiciousfunc (t *LagTracker) SpikeRate() float64 / func (t *LagTracker) Suspicious() bool可疑高延迟识别14.3

internal/authority(15.1–15.3)

符号签名说明出处
Trust + 三常量type Trust uint8;Untrusted Trust = iota / Derived / Advisory三级信任15.1
FieldSpec / CommandSpectype FieldSpec struct { Name string; Trust Trust } / type CommandSpec struct { Kind string; Fields []FieldSpec }字段级信任契约15.1
Registry / Register / Checkfunc NewRegistry() *Registry / func (r *Registry) Check(kind string, sent []string) []Violation契约注册与校验15.1
Violationtype Violation struct { Command string; Field string; Reason string }违规描述15.1
Vec3 / MoveProposal / MoveConfigtype Vec3 struct{ X, Y, Z float64 } / type MoveConfig struct { Tolerance float64; TeleportThreshold float64 }移动校验输入15.2
ValidateMove / PathBlockedfunc ValidateMove(p MoveProposal, cfg MoveConfig) MoveVerdict / func PathBlocked(from, to Vec3, step float64, solid SolidFunc) bool移动裁决与穿墙检测15.2
TokenBucket / NewTokenBucket / Allowfunc NewTokenBucket(capacity, refill float64, now time.Time) *TokenBucket / func (b *TokenBucket) Allow(now time.Time) bool令牌桶限速15.3
Scorer / Dispositiontype Scorer struct { ... } / type Disposition uint8;Allow / Throttle / Flag / Disconnect / Ban处置分级15.3

internal/guard(16.1–16.3)

符号签名说明出处
Limitstype Limits struct { MaxConnsPerIP int; MaxPendingPerIP int; MaxConnsGlobal int; ... }连接配额16.2
Limiter / New / Acquirefunc New(lim Limits) *Limiter / func (l *Limiter) Acquire(ip string, now time.Time) (*Ticket, error)限流与准入16.2
Limiter.AllowHandshakefunc (l *Limiter) AllowHandshake(ip string, now time.Time) bool握手令牌桶16.2
Sealer / NewSealer / Seal / Openfunc NewSealer(key []byte) (*Sealer, error) / func (s *Sealer) Seal(seq uint32, plaintext []byte) ([]byte, error)AEAD 封包16.3
ReplayWindow / Acceptfunc (w *ReplayWindow) Accept(seq uint32) bool重放窗口16.3
DeriveKey / NonceLen / SeqLenfunc DeriveKey(secret []byte, label string) []byte / const NonceLen = 12 / const SeqLen = 4密钥派生与布局16.3

internal/gateway + cmd/aegis(17.1–17.3)

符号签名说明出处
Configtype Config struct { Role string; Listen string; Upstream string; ReadTimeout time.Duration; Tick time.Duration; AOIRadius float64; MaxSpeed float64 }进程配置17.1
DefaultConfigfunc DefaultConfig() Config默认配置17.1

L1 自检:API 清单与源码逐字对齐

附录最容易腐化的地方,是签名抄错了一个参数名而没人发现。下面这段是 L1,把 API 清单编码成 Go 表,折叠源码空白后断言每个签名都能在对应包里逐字找到——它同时替本附录所有表格兜底。

// file: api_reference_test.go
// tier: L1
// verify: go test ./...
package appendix

import (
	"os"
	"path/filepath"
	"strings"
	"testing"
)

// apiTable 钉死每个 internal 包的一个代表导出符号,签名逐字取自正文代码块。
var apiTable = []struct{ pkg, sig string }{
	{"proto", "type Frame struct { Cmd Cmd Seq uint32 Flags Flags Payload []byte }"},
	{"proto", "const HeaderSize = 11"},
	{"proto", "const MaxPayload = 1 << 20"},
	{"proto", "func DecodeFrom(buf []byte) (Frame, int, error)"},
	{"codec", "func NewWriter(capacity int) *Writer"},
	{"codec", "func SplitFrame(raw []byte) (payload []byte, err error)"},
	{"kcp", "func (k *KCP) Input(data []byte) int"},
	{"ws", "func ReadFrame(r io.Reader, maxPayload uint64) (*Frame, error)"},
	{"session", "func (t *Table) Bind(account string, c *Conn) (evicted *Conn)"},
	{"router", "type Handler func(*Context) error"},
	{"timing", "func (w *Wheel) After(d time.Duration, fn func()) uint64"},
	{"actor", "type Msg struct { Cmd uint16 Seq uint32 From EntityID Payload []byte }"},
	{"idem", "func FingerprintOf(cmd uint16, actor, token uint64, args ...uint64) Fingerprint"},
	{"sync", "type Vec2 struct{ X, Y float32 }"},
	{"predict", "const Speed = 5.0"},
	{"clock", "func Measure(t1, t2, t3, t4 time.Duration) Offset"},
	{"rewind", "func RewindTo(serverNow, rtt, interp time.Duration) time.Duration"},
	{"fairness", "func NewLagTracker(baseline, spike time.Duration, window int) *LagTracker"},
	{"authority", "func ValidateMove(p MoveProposal, cfg MoveConfig) MoveVerdict"},
	{"guard", "func NewSealer(key []byte) (*Sealer, error)"},
	{"gateway", "func DefaultConfig() Config"},
}

// fold 折叠空白,使「逐字一致」在 token 层面成立而不苛求排版。
func fold(s string) string { return strings.Join(strings.Fields(s), " ") }

// inPackage 在任意 ../chNN/internal/<pkg> 下折叠源码,查找签名是否逐字出现。
func inPackage(root, pkg, sig string) bool {
	found := false
	_ = filepath.Walk(root, func(p string, info os.FileInfo, err error) error {
		if err != nil || info.IsDir() || !strings.HasSuffix(p, ".go") || strings.HasSuffix(p, "_test.go") {
			return nil
		}
		if filepath.Base(filepath.Dir(p)) != pkg {
			return nil
		}
		b, _ := os.ReadFile(p)
		if strings.Contains(fold(string(b)), sig) {
			found = true
		}
		return nil
	})
	return found
}

func TestAPIListMatchesSource(t *testing.T) {
	root, _ := filepath.Abs("..")
	if _, err := os.Stat(filepath.Join(root, "ch03", "internal", "proto", "header.go")); err != nil {
		t.Skip("卷一源码树不在 ../chNN,跳过符号核对")
	}
	for _, e := range apiTable {
		if !inPackage(root, e.pkg, fold(e.sig)) {
			t.Errorf("%s: 源码里找不到签名 %s", e.pkg, e.sig)
		}
	}
}

fold 把任意空白折成单空格,所以「逐字一致」在 token 层面成立、不苛求排版;inPackage 跳过 _test.go,只认非测试源码。源码树不在 ../chNN 时它会 t.Skip,故只有本附录的模块里 go test ./... 依然全绿。

阅读下一节:附录 B 环境与复现速查

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「mmo」更多文章

  1. 《MMO 服务器开发》工程化与运营 · 目录
  2. 《MMO 服务器开发》9.3 上线检查清单
  3. 《MMO 服务器开发》9.2 性能预算达标