Skip to content

[Cursor Review] feat(world/view_layer.go): implement block view layer#29

Closed
HashimTheArab wants to merge 50 commits into
HashimTheArab:masterfrom
RestartFU:dan/block-view-layer
Closed

[Cursor Review] feat(world/view_layer.go): implement block view layer#29
HashimTheArab wants to merge 50 commits into
HashimTheArab:masterfrom
RestartFU:dan/block-view-layer

Conversation

@HashimTheArab

@HashimTheArab HashimTheArab commented May 11, 2026

Copy link
Copy Markdown
Owner

Cursor review mirror of df-mc#1249

Original: df-mc#1249


Note

Medium Risk
Touches core player block interaction, chunk encoding, and client sync paths; incorrect override handling could desync clients or leak private edits to the world.

Overview
Adds per-player block view layers alongside the existing entity view-layer: a player can see fake blocks at positions without changing the real world, and revert with ViewPublicBlock.

ViewLayer now stores block overrides (ViewBlock, Block, Blocks) and notifies sessions when they change. Chunk / SubChunk / Palette gain Clone() so chunk data can be copied before applying overrides. Session merges overrides into chunks sent to the client (applyViewLayerToChunk), applies them on ViewBlockUpdate, and refreshes overrides via ViewLayerBlockChanged (including sub-chunk height adverts when needed).

Player interaction uses the visible block (viewedBlock / breakingBlock): use/place on private blocks is blocked; breaking private blocks clears the override, shows break effects only to that player (no world drops/XP), and crack/particles/sounds route through ViewPrivateBlockAction while skipping viewers who have their own override at that position.

Reviewed by Cursor Bugbot for commit 223aae4. Bugbot is set up for automated code reviews on this repo. Configure here.

Summary by CodeRabbit

  • New Features

    • Per-player view-layer: clients can see customized block appearances or revert to the public view.
    • Sessions can publish private or public block views; private breaks show effects only to the viewer while public breaks are broadcast.
    • Block-breaking uses the visible block for crack timing, particles, drops and XP.
  • Refactor

    • View-layer overrides are consistently applied when serving chunks and sending block updates so per-player views are honored.

Review Change Stack

@HashimTheArab

Copy link
Copy Markdown
Owner Author

@cursor review

@coderabbitai

coderabbitai Bot commented May 11, 2026

Copy link
Copy Markdown

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds per-position view-layer block overrides and integrates them into session APIs, chunk serialization, and player block-breaking; introduces cloneable chunk components and updates handler signatures to indicate private (view) vs public (world) breaks.

Changes

View-Layer Block Override Feature

Layer / File(s) Summary
View-Layer Block Infrastructure
server/world/view_layer.go
ViewLayer stores per-position block overrides with a block registry, exposes ViewBlock, ViewPublicBlock, Block, Blocks, and adds ViewLayerUpdater.ViewLayerBlockChanged.
Chunk Cloning Support
server/world/chunk/chunk.go, server/world/chunk/sub_chunk.go, server/world/chunk/paletted_storage.go, server/world/chunk/palette.go
Added Clone() methods for Chunk, SubChunk, PalettedStorage, and Palette to enable non-destructive override application.
Session-Level View-Layer API
server/session/view_layer.go
Session methods ViewBlock, ViewPublicBlock, and ViewLayerBlockChanged added; publicBlock helper resolves visible blocks from loaded chunks or registry.
Chunk View Construction with Overrides
server/session/chunk.go
ViewChunk and ViewSubChunks apply session view-layer overrides via applyViewLayerToChunk, cloning chunks and block-entity maps only when needed.
Block Update Distribution
server/session/world.go
ViewBlockUpdate applies view-layer overrides for layer 0 then delegates actual packet sending to viewBlockUpdate.
Handler Interface Contract
server/player/handler.go
Handler methods HandleStartBreak and HandleBlockBreak gain a private bool parameter; NopHandler adjusted to new signatures.
Player Block-Breaking Integration
server/player/player.go
Breaking logic selects effective breakingBlock(pos), passes private to handler hooks, recomputes crack timings from the effective block, and routes BreakBlock to private/public paths with corresponding particles; new Player.ViewBlock/ViewPublicBlock forward overrides to session.
Session initialization
server/session/session.go
Config.New selects block registry earlier and initializes Session.viewLayer with NewViewLayerWithBlockRegistry.

Sequence Diagram

sequenceDiagram
  participant Player
  participant Session
  participant Handler
  participant World
  participant Particles
  Player->>Session: check privateBlock(pos)
  Session-->>Player: effective block (private? true/false)
  Player->>Handler: HandleStartBreak(ctx,pos,private)
  Player->>World: StartCrackAction(using effective block)
  Player->>World: Finish breaking at pos
  World->>Handler: HandleBlockBreak(ctx,pos,private,&drops,&xp)
  alt private == true
    World->>Particles: spawn private break particles (player-only)
    World->>Session: ViewPublicBlock(pos)
  else
    World->>Particles: spawn public/world break particles
    World->>World: apply world block removal and drops
  end
Loading

🎯 3 (Moderate) | ⏱️ ~25 minutes

🐰 I nibble code and mend a view,
Each player sees a block that's new,
World stays still while feelings change,
Cloned chunks hide the visual range,
Hopping through overrides — cue the brew!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main feature: implementing block view layer functionality in world/view_layer.go.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
server/player/player.go (1)

1917-1928: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Guard against stale breakingPos in the !p.breaking path.

p.breakingPos is only authoritative while a break is actually in progress (or just after a creative-mode StartBreaking early-returns). When FinishBreaking is invoked with !p.breaking and there was no prior StartBreaking this tick, p.breakingPos is either the zero value cube.Pos{} or whatever was last set. If a private block happens to exist at that stale position, this new branch will call BreakBlock against it.

Consider only entering the private fast-path when you can confirm the position is current, e.g. by also requiring that StartBreaking set it during this interaction (or by passing the pos into FinishBreaking).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/player/player.go` around lines 1917 - 1928, FinishBreaking currently
uses p.breakingPos even when p.breaking is false, which can be stale; update the
logic so the private fast-path only runs for a current, authoritative
position—either change FinishBreaking to accept the target pos (preferred) or
add/consult a validity flag set by StartBreaking (e.g. p.breakingPosValid true
in StartBreaking and cleared in AbortBreaking/after FinishBreaking) and require
p.breakingPosValid && p.breakingPos != zero before calling privateBlock(...) and
BreakBlock; reference Player.FinishBreaking, p.breaking, p.breakingPos,
StartBreaking, AbortBreaking, privateBlock and BreakBlock when making the
change.
🧹 Nitpick comments (2)
server/session/view_layer.go (1)

86-95: 💤 Low value

Inconsistent nil-guard on s.viewLayer.

Every other helper in this file early-returns when s.viewLayer == nil, but ViewLayerBlockChanged directly dereferences s.viewLayer.Block(pos) at Line 88. In current usage this is the layer's own callback so it should be safe, but a future caller (or a session that never had a view layer wired up) would NPE. Consider matching the pattern used by ViewBlock/ViewPublicBlock for defensive consistency.

🛡️ Proposed nil-guard
 func (s *Session) ViewLayerBlockChanged(pos cube.Pos) {
+	if s.viewLayer == nil {
+		return
+	}
 	if b, ok := s.viewLayer.Block(pos); ok {
 		s.viewBlockUpdate(pos, b, 0)
 		return
 	}
 	if b, ok := s.publicBlock(pos); ok {
 		s.viewBlockUpdate(pos, b, 0)
 	}
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/session/view_layer.go` around lines 86 - 95, ViewLayerBlockChanged
currently dereferences s.viewLayer without a nil-guard which can cause an NPE;
add the same defensive early-return used by ViewBlock/ViewPublicBlock by
checking if s.viewLayer == nil at the start of ViewLayerBlockChanged and return
immediately if so, then proceed to call s.viewLayer.Block(pos) and fall back to
s.publicBlock(pos) as before, ensuring you still call s.viewBlockUpdate(pos, b,
0) for whichever block is found.
server/player/player.go (1)

1841-1842: 💤 Low value

Redundant view-layer lookup.

breakingBlock(pos) already calls privateBlock(pos) internally, then Line 1842 calls privateBlock(pos) again just to read the boolean. Consider returning both values from a single helper to avoid the double lookup (and any chance the two reads disagree if the override is mutated concurrently).

♻️ Proposed helper
-	b := p.breakingBlock(pos)
-	_, private := p.privateBlock(pos)
+	b, private := p.breakingBlock(pos)

…and update breakingBlock:

-func (p *Player) breakingBlock(pos cube.Pos) world.Block {
-	if b, ok := p.privateBlock(pos); ok {
-		return b
-	}
-	return p.tx.Block(pos)
-}
+func (p *Player) breakingBlock(pos cube.Pos) (world.Block, bool) {
+	if b, ok := p.privateBlock(pos); ok {
+		return b, true
+	}
+	return p.tx.Block(pos), false
+}

Then adjust the ContinueBreaking call site to discard the boolean.

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@server/player/player.go`:
- Around line 2054-2058: The private-branch call to p.Handler().HandleBlockBreak
passes drops and xp pointers but never applies them; after the handler returns
(and if ctx.Cancelled() is false) iterate the drops slice returned via drops and
add each item.Stack to the player (e.g. via p.Inventory() add/transfer logic or
the same spawn-vs-inventory behavior used in the world-block branch) and call
p.AddExperience(xp) to grant the earned XP; alternatively, if you prefer not to
mutate API, remove the drops and xp pointer parameters from HandleBlockBreak and
its callers (update the handler signature) so the private branch cannot
misleadingly accept them.
- Around line 2052-2066: The private-break branch leaks visuals to other players
and incorrectly restores the public block on cancel: make the break particle
local to the player by replacing the broadcast
p.tx.AddParticle(pos.Vec3Centre(), particle.BlockBreak{Block: privateBlock})
with a player-local particle send (use whatever per-player particle API or
method on Player/interface — e.g., a p.SendParticle or scoped send) so only this
client sees the privateBlock particle; on cancel, stop calling
p.resendNearbyBlocks(pos) (which re-sends p.tx.Block(pos)) and instead re-assert
the player's private override using the layer-specific update routine (e.g.,
call ViewLayerBlockChanged or the equivalent per-player block override resend
for pos with privateBlock) so the client keeps its private view; remove the dead
variable b := p.tx.Block(pos) from this branch (or confirm and document if its
omission of Exhaust(0.005) and tool durability handling is intentional), and if
those survival side-effects were expected include p.Exhaust and durability logic
here as in the public-break path.

---

Outside diff comments:
In `@server/player/player.go`:
- Around line 1917-1928: FinishBreaking currently uses p.breakingPos even when
p.breaking is false, which can be stale; update the logic so the private
fast-path only runs for a current, authoritative position—either change
FinishBreaking to accept the target pos (preferred) or add/consult a validity
flag set by StartBreaking (e.g. p.breakingPosValid true in StartBreaking and
cleared in AbortBreaking/after FinishBreaking) and require p.breakingPosValid &&
p.breakingPos != zero before calling privateBlock(...) and BreakBlock; reference
Player.FinishBreaking, p.breaking, p.breakingPos, StartBreaking, AbortBreaking,
privateBlock and BreakBlock when making the change.

---

Nitpick comments:
In `@server/session/view_layer.go`:
- Around line 86-95: ViewLayerBlockChanged currently dereferences s.viewLayer
without a nil-guard which can cause an NPE; add the same defensive early-return
used by ViewBlock/ViewPublicBlock by checking if s.viewLayer == nil at the start
of ViewLayerBlockChanged and return immediately if so, then proceed to call
s.viewLayer.Block(pos) and fall back to s.publicBlock(pos) as before, ensuring
you still call s.viewBlockUpdate(pos, b, 0) for whichever block is found.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 18a6965e-ffc1-48f2-acb8-f09799d84d06

📥 Commits

Reviewing files that changed from the base of the PR and between 7869803 and fa7715d.

📒 Files selected for processing (10)
  • server/player/handler.go
  • server/player/player.go
  • server/session/chunk.go
  • server/session/view_layer.go
  • server/session/world.go
  • server/world/chunk/chunk.go
  • server/world/chunk/palette.go
  • server/world/chunk/paletted_storage.go
  • server/world/chunk/sub_chunk.go
  • server/world/view_layer.go

Comment thread server/player/player.go Outdated
Comment thread server/player/player.go Outdated
Comment thread server/player/player.go
Comment thread server/player/player.go Outdated
@HashimTheArab

Copy link
Copy Markdown
Owner Author

@cursor review

Comment thread server/player/player.go Outdated
@HashimTheArab

Copy link
Copy Markdown
Owner Author

@cursor review

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
server/player/player.go (2)

1883-1886: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Keep private break feedback session-local.

b can now come from a per-player override, but these crack actions, particles, and block-breaking sounds still go through p.viewers() / p.tx.*. Other nearby players will see break progress for a block they never had at that position. The private path should send feedback only to p.session()/p.ShowParticle/p.PlaySound, while shared broadcasts should keep using the public world block.

Also applies to: 1947-1961, 2097-2099

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/player/player.go` around lines 1883 - 1886, The crack-action and other
break-feedback currently use p.viewers() and public broadcasts but must respect
per-player overrides: replace sending private break feedback (the result of
p.breakTime(b) stored in p.lastBreakDuration and the subsequent
viewer.ViewBlockAction calls) with session-local methods so only the acting
player receives it—use p.session(), p.ShowParticle and p.PlaySound (or other
p.session-scoped senders) for the private path; keep the shared/world broadcasts
(the public world block updates and p.tx.* broadcasts) unchanged. Update the
occurrences around p.lastBreakDuration/p.breakTime(b) and the similar blocks at
the other noted places (lines ~1947-1961 and ~2097-2099) so private feedback is
sent via session-local APIs and only shared broadcasts remain on
p.viewers()/p.tx.*.

2050-2064: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don't resend the public block after a private-break failure.

When private is true, resendNearbyBlocks(pos) re-sends p.tx.Block(pos), which overwrites the client's override with the public block on cancel / range failure / unbreakable failure. That leaves the player desynced until some later view-layer refresh happens. Re-assert the view-layer state instead of using the world resend path for private breaks.

Suggested shape
+	resendBrokenPos := func() {
+		if private {
+			p.session().ViewLayerBlockChanged(pos)
+			return
+		}
+		p.resendNearbyBlocks(pos)
+	}
+
 	if !p.canReach(pos.Vec3Centre()) || !p.GameMode().AllowsEditing() {
-		p.resendNearbyBlocks(pos)
+		resendBrokenPos()
 		return
 	}
 	if _, breakable := b.(block.Breakable); !breakable && !p.GameMode().CreativeInventory() {
-		p.resendNearbyBlocks(pos)
+		resendBrokenPos()
 		return
 	}
 	...
 	if p.Handler().HandleBlockBreak(ctx, pos, private, &drops, &xp); ctx.Cancelled() {
-		p.resendNearbyBlocks(pos)
+		resendBrokenPos()
 		return
 	}

Also applies to: 2077-2079

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/player/player.go` around lines 2050 - 2064, When handling a private
break (privateBlock, private := p.privateBlock(pos)), do not call
p.resendNearbyBlocks(pos) because that uses p.tx.Block(pos) and will overwrite
the client's private override with the public world block; instead re-assert the
view-layer private state by sending the privateBlock to the client using the
same channel used for overrides (e.g. the method that previously sends
privateBlock/overrides to the client rather than p.tx.Block), and apply the same
change for the other early-return paths (range/cancel/unbreakable failures
around lines 2077-2079) so failures send the private override back to the player
rather than resending the public world block.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@server/player/player.go`:
- Around line 2084-2095: The BreakHandler is being invoked even for private
breaks, which lets handlers inspect/mutate the public world; change the logic so
handlers run only when the break is not private. Concretely, guard the
BreakHandler call with the same private check (e.g., move the
info.BreakHandler(pos, p.tx, p) invocation into the else branch where
p.tx.SetBlock is used, or add if !private before calling it) so that when
private is true (the code path that calls p.ViewPublicBlock) the
BreakInfo.BreakHandler is skipped.

---

Duplicate comments:
In `@server/player/player.go`:
- Around line 1883-1886: The crack-action and other break-feedback currently use
p.viewers() and public broadcasts but must respect per-player overrides: replace
sending private break feedback (the result of p.breakTime(b) stored in
p.lastBreakDuration and the subsequent viewer.ViewBlockAction calls) with
session-local methods so only the acting player receives it—use p.session(),
p.ShowParticle and p.PlaySound (or other p.session-scoped senders) for the
private path; keep the shared/world broadcasts (the public world block updates
and p.tx.* broadcasts) unchanged. Update the occurrences around
p.lastBreakDuration/p.breakTime(b) and the similar blocks at the other noted
places (lines ~1947-1961 and ~2097-2099) so private feedback is sent via
session-local APIs and only shared broadcasts remain on p.viewers()/p.tx.*.
- Around line 2050-2064: When handling a private break (privateBlock, private :=
p.privateBlock(pos)), do not call p.resendNearbyBlocks(pos) because that uses
p.tx.Block(pos) and will overwrite the client's private override with the public
world block; instead re-assert the view-layer private state by sending the
privateBlock to the client using the same channel used for overrides (e.g. the
method that previously sends privateBlock/overrides to the client rather than
p.tx.Block), and apply the same change for the other early-return paths
(range/cancel/unbreakable failures around lines 2077-2079) so failures send the
private override back to the player rather than resending the public world
block.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 18755c7f-b05c-4481-a487-2cd988194c7e

📥 Commits

Reviewing files that changed from the base of the PR and between 33d3320 and ba6cea3.

📒 Files selected for processing (1)
  • server/player/player.go

Comment thread server/player/player.go
Comment thread server/player/player.go
@HashimTheArab

Copy link
Copy Markdown
Owner Author

@cursor review

Comment thread server/player/player.go
@RestartFU

Copy link
Copy Markdown
Collaborator

@cursor reivew, maybe?

@cursor

cursor Bot commented May 13, 2026

Copy link
Copy Markdown

Unable to authenticate your request. Please make sure to connect your GitHub account to Cursor. Go to Cursor

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
server/player/player.go (1)

2076-2080: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Cancel path still overwrites the private override with the world block.

When HandleBlockBreak cancels a private break, p.resendNearbyBlocks(pos) ultimately calls Session.ViewBlockUpdate(pos, p.tx.Block(pos), 0), which is the public block. For a private view-layer block the client was seeing, this resends the underlying world block and wipes the override — the opposite of the intended "cancel" behavior. The override should be re-asserted instead (e.g. via ViewLayerBlockChanged).

🛠️ Suggested fix
-	if p.Handler().HandleBlockBreak(ctx, pos, private, &drops, &xp); ctx.Cancelled() {
-		p.resendNearbyBlocks(pos)
-		return
-	}
+	if p.Handler().HandleBlockBreak(ctx, pos, private, &drops, &xp); ctx.Cancelled() {
+		if private {
+			p.session().ViewLayerBlockChanged(pos)
+		} else {
+			p.resendNearbyBlocks(pos)
+		}
+		return
+	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/player/player.go` around lines 2076 - 2080, The cancel path after
calling p.Handler().HandleBlockBreak currently calls p.resendNearbyBlocks(pos)
which ends up invoking Session.ViewBlockUpdate(pos, p.tx.Block(pos), 0) and thus
overwrites any private view-layer override; instead, detect when the cancelled
break was on a private/view-layer block and re-assert that override rather than
resending the world block — e.g. replace the p.resendNearbyBlocks(pos) call in
the ctx.Cancelled() branch with logic that uses the session's view-layer update
method (use ViewLayerBlockChanged or the equivalent Session method) to send the
private view-layer block back to the client (using the same override value you
passed into HandleBlockBreak) so the override is preserved.
🧹 Nitpick comments (1)
server/player/player.go (1)

2084-2086: 💤 Low value

Use the safe session accessor / existing helper for the per-player particle.

p.s.ViewParticle(...) bypasses the session.Nop guard used everywhere else in this file. A nil p.s (or a Nop session ever reaching this path) would panic, and the direct field access is inconsistent with the rest of BreakBlock (which goes through p.session()). ShowParticle already does exactly this dispatch.

♻️ Suggested refactor
 	if private {
 		p.ViewPublicBlock(pos)
-		p.s.ViewParticle(pos.Vec3Centre(), particle.BlockBreak{Block: b})
+		p.ShowParticle(pos.Vec3Centre(), particle.BlockBreak{Block: b})
 	} else {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/player/player.go` around lines 2084 - 2086, Replace the direct session
field access p.s.ViewParticle(...) with the safe accessor used elsewhere: call
the existing helper that dispatches to either the real session or session.Nop
(e.g., use p.session() or the ShowParticle wrapper) so nil or Nop sessions won't
panic; specifically, inside BreakBlock replace
p.s.ViewParticle(pos.Vec3Centre(), particle.BlockBreak{Block: b}) with the
helper call (ShowParticle or p.session().ViewParticle via p.session()) that
mirrors other uses in BreakBlock.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@server/player/player.go`:
- Line 2090: The if-statement `if breakable, ok := b.(block.Breakable); ok  {`
has extra spacing before the opening brace; update the `player.go` occurrence so
there is a single space before `{` (i.e., change the condition to `if breakable,
ok := b.(block.Breakable); ok {`) to satisfy gofmt/golangci-lint while keeping
the same variables `breakable`, `ok`, and the type assertion to
`block.Breakable`.

---

Duplicate comments:
In `@server/player/player.go`:
- Around line 2076-2080: The cancel path after calling
p.Handler().HandleBlockBreak currently calls p.resendNearbyBlocks(pos) which
ends up invoking Session.ViewBlockUpdate(pos, p.tx.Block(pos), 0) and thus
overwrites any private view-layer override; instead, detect when the cancelled
break was on a private/view-layer block and re-assert that override rather than
resending the world block — e.g. replace the p.resendNearbyBlocks(pos) call in
the ctx.Cancelled() branch with logic that uses the session's view-layer update
method (use ViewLayerBlockChanged or the equivalent Session method) to send the
private view-layer block back to the client (using the same override value you
passed into HandleBlockBreak) so the override is preserved.

---

Nitpick comments:
In `@server/player/player.go`:
- Around line 2084-2086: Replace the direct session field access
p.s.ViewParticle(...) with the safe accessor used elsewhere: call the existing
helper that dispatches to either the real session or session.Nop (e.g., use
p.session() or the ShowParticle wrapper) so nil or Nop sessions won't panic;
specifically, inside BreakBlock replace p.s.ViewParticle(pos.Vec3Centre(),
particle.BlockBreak{Block: b}) with the helper call (ShowParticle or
p.session().ViewParticle via p.session()) that mirrors other uses in BreakBlock.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: bb097eef-2457-408e-bd46-6e21298bdbb1

📥 Commits

Reviewing files that changed from the base of the PR and between 6de1b81 and b659386.

📒 Files selected for processing (1)
  • server/player/player.go

Comment thread server/player/player.go Outdated
@HashimTheArab

Copy link
Copy Markdown
Owner Author

@cursor review

Comment thread server/player/player.go
Comment thread server/player/player.go Outdated
@HashimTheArab

Copy link
Copy Markdown
Owner Author

@cursor review

Comment thread server/player/player.go
@HashimTheArab

Copy link
Copy Markdown
Owner Author

@cursor review

Restore the existing session constructor pattern for selecting the block registry after initialising the session. NewViewLayer no longer needs the registry, so the temporary local was unnecessary.\n\nConstraint: Review feedback identified the precomputed block registry local as unnecessary.\nRejected: Keeping br in the struct literal | It creates avoidable churn from the existing constructor flow.\nConfidence: high\nScope-risk: narrow\nDirective: Keep registry fallback assignment near other post-construction session setup unless a constructor dependency requires it earlier.\nTested: make lint; go test ./server/session; go test ./server/...\nNot-tested: Custom block registry integration smoke test.
Comment thread server/player/player.go Outdated
RestartFU and others added 2 commits May 28, 2026 12:42
When a private block override disappears while the player is still finishing a private break, resend the public block instead of silently returning. This corrects client prediction without breaking the public block.\n\nConstraint: Private break completion must not mutate the public block if the private override vanished mid-break.\nRejected: Falling through to BreakBlock | It can break the public block for a stale private interaction.\nConfidence: high\nScope-risk: narrow\nDirective: Stale private interactions should resynchronise the client rather than mutate public world state.\nTested: make lint; go test ./server/player ./server/session; go test ./server/...\nNot-tested: Manual client finish packet after plugin removes private override mid-break.
@HashimTheArab

Copy link
Copy Markdown
Owner Author

@cursor review

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 223aae4. Configure here.

RestartFU added 2 commits May 28, 2026 13:14
Merge upstream master into the view-layer branch and keep upstream chunk clone/cache-light handling while preserving the branch's view-layer changes.\n\nConstraint: GitHub reported conflicts in chunk clone/storage files after upstream chunk optimisations.\nRejected: Keeping older append-based clones | Upstream slices.Clone and light sentinel preservation are the newer base behavior.\nConfidence: high\nScope-risk: moderate\nDirective: Preserve upstream chunk storage optimisations when resolving future conflicts in this area.\nTested: make lint; go test ./server/player ./server/session ./server/world/chunk ./server/block/cube/trace; go test ./server/...\nNot-tested: Manual chunk light/cache regression scenarios.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 04ba75cbf5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread server/player/player.go Outdated
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 72a16a83ba

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread server/item/crossbow.go
Comment thread server/item/crossbow.go
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants