{"record":{"id":"a34b9118b0e14323","repo":"egametang/ET","slug":"frame-out-frame-maxframe-this-maxframe","errorCode":null,"errorMessage":"frame out: {frame}, maxframe: {this.MaxFrame}","messagePattern":"frame out: (.+?), maxframe: (.+?)","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"critical","filePath":"Packages/cn.etetet.lockstep/Scripts/Model/Share/FrameBuffer.cs","lineNumber":63,"sourceCode":"        {\n            if (frame < 0)\n            {\n                return false;\n            }\n\n            if (frame > this.MaxFrame)\n            {\n                return false;\n            }\n\n            return true;\n        }\n\n        private void EnsureFrame(int frame)\n        {\n            if (!CheckFrame(frame))\n            {\n                throw new Exception($\"frame out: {frame}, maxframe: {this.MaxFrame}\");\n            }\n        }\n        \n        public OneFrameInputs FrameInputs(int frame)\n        {\n            EnsureFrame(frame);\n            OneFrameInputs oneFrameInputs = this.frameInputs[frame % this.frameInputs.Capacity];\n            return oneFrameInputs;\n        }\n\n        public void MoveForward(int frame)\n        {\n            if (this.MaxFrame - frame > LSConstValue.FrameCountPerSecond) // 至少留出1秒的空间\n            {\n                return;\n            }\n            \n            ++this.MaxFrame;","sourceCodeStart":45,"sourceCodeEnd":81,"githubUrl":"https://github.com/egametang/ET/blob/5cab01f7a8bee5f49f4781eebe9e2b1c6d7ebe0f/Packages/cn.etetet.lockstep/Scripts/Model/Share/FrameBuffer.cs#L45-L81","documentation":"Thrown by FrameBuffer.EnsureFrame when a frame index is negative or exceeds MaxFrame. FrameBuffer is a ring buffer for lockstep simulation; MaxFrame starts at (initialFrame + 30s of frames) and advances via MoveForward one frame at a time. Accessing a frame beyond this ceiling means the buffer has not been extended to cover that simulation step yet.","triggerScenarios":"Calling FrameInputs, GetHash, SetHash, or Snapshot with a frame number greater than MaxFrame or less than 0. This happens when the lockstep simulation tries to read/write a future frame before MoveForward has been called enough times to extend the buffer, or when a desync/replay seeks to a frame the buffer doesn't cover.","commonSituations":"Lockstep simulation advances faster than MoveForward is called (server hasn't sent enough frame data). A replay system seeks to a frame beyond the buffer's MaxFrame. The buffer capacity (60s * frameRate) is too small for long matches and MaxFrame advancement logic has a bug. Negative frame from an underflow in frame arithmetic.","solutions":["Ensure MoveForward is called the correct number of times before accessing frames at the simulation frontier — the server must deliver frame data that triggers MaxFrame advancement.","Verify the caller is not requesting a frame the simulation hasn't reached yet (add a CheckFrame guard before access).","For replays, ensure MaxFrame is set high enough or the replay logic calls MoveForward incrementally."],"exampleFix":"// before\npublic OneFrameInputs FrameInputs(int frame)\n{\n    EnsureFrame(frame);\n    return this.frameInputs[frame % this.frameInputs.Capacity];\n}\n// after: caller checks first\nif (!buffer.CheckFrame(frame))\n{\n    Log.Error($\"frame {frame} out of range, max {buffer.MaxFrame}\");\n    return null;\n}","handlingStrategy":"validation","validationCode":"// Guard before accessing a frame in the buffer\nif (!buffer.CheckFrame(frame))\n{\n    Log.Error($\"Frame {frame} out of range, max {buffer.MaxFrame}\");\n    return; // or handle appropriately\n}\nvar inputs = buffer.FrameInputs(frame);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always call CheckFrame(frame) before FrameInputs/GetHash/SetHash/Snapshot.","Ensure MoveForward is called in lockstep with the simulation advance so MaxFrame tracks correctly.","In replay mode, verify the replay data does not seek beyond the buffer's capacity window."],"tags":["lockstep","frame-buffer","simulation","out-of-bounds"],"backgroundTag":null,"analyzedSha":"5cab01f7a8bee5f49f4781eebe9e2b1c6d7ebe0f","analyzedAt":"2026-08-13T21:10:40.377Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}