Skip to content

Add Player.currentAvatar FK and publish avatar update events#1146

Merged
Brutus5000 merged 8 commits into
developfrom
feat/player-avatar-id-and-rabbit-event
Jun 25, 2026
Merged

Add Player.currentAvatar FK and publish avatar update events#1146
Brutus5000 merged 8 commits into
developfrom
feat/player-avatar-id-and-rabbit-event

Conversation

@Brutus5000

@Brutus5000 Brutus5000 commented Jun 21, 2026

Copy link
Copy Markdown
Member

What

Exposes the login.avatar_id FK column (added in faf/db v143) on the Player entity as a currentAvatar relationship, making the API a writer for a player's currently selected avatar.

When a player's currentAvatar is updated, a success.player_avatar.update message is published to the faf-lobby topic exchange so the lobby server can refresh its in-memory state and broadcast a player_info to connected clients.

Changes

  • Player.java — new currentAvatar @ManyToOne mapped to the avatar_id column, exposed via Elide with IsEntityOwner update permission and a POSTCOMMIT LifeCycleHook.
  • PlayerAvatarUpdateHook.java (new) — publishes {"player_id": <int>, "avatar_id": <int|null>} to exchange faf-lobby with routing key success.player_avatar.update.

Wire contract

  • Exchange: faf-lobby (topic)
  • Routing key: success.player_avatar.update
  • Payload: {"player_id": <int>, "avatar_id": <int|null>}

The lobby-side consumer is implemented separately (FAForever/server PR #1095).

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Players can now have a current avatar set and saved as part of their profile.
    • Avatar changes are now tracked and published for downstream updates.
  • Bug Fixes

    • Avatar selection is now validated to ensure only assigned avatars can be selected.
    • Clearing an avatar is supported without triggering assignment checks.
  • Deprecations

    • The older “selected avatar” flag is now deprecated in favor of the new current avatar setting.

Expose login.avatar_id on the Player entity as `currentAvatar` so the
API becomes the writer for a player's selected avatar. On update, a
`success.player_avatar.update` message is published on the `faf-lobby`
exchange so the lobby server can refresh its in-memory state.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 21, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0276b6f0-2eab-4268-b732-583ed5da99db

📥 Commits

Reviewing files that changed from the base of the PR and between f8f579b and 8c8ca0e.

📒 Files selected for processing (1)
  • src/test/java/com/faforever/api/data/hook/PlayerAvatarValidationHookTest.java
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/test/java/com/faforever/api/data/hook/PlayerAvatarValidationHookTest.java

📝 Walkthrough

Walkthrough

The PR adds player avatar validation and post-commit update publishing, deprecates AvatarAssignment.isSelected(), and adds a shell script that builds and pushes a Docker image from the current branch.

Changes

Player avatar selection flow

Layer / File(s) Summary
Validation hook and error code
src/main/java/com/faforever/api/error/ErrorCode.java, src/main/java/com/faforever/api/data/hook/PlayerAvatarValidationHook.java, src/test/java/com/faforever/api/data/hook/PlayerAvatarValidationHookTest.java
AVATAR_NOT_ASSIGNED is added, PlayerAvatarValidationHook checks assigned avatars against AvatarAssignmentRepository, and tests cover assigned, unassigned, and null-avatar cases.
Avatar update publishing
src/main/java/com/faforever/api/data/hook/PlayerAvatarUpdateHook.java
PlayerAvatarUpdateHook reads Player#getCurrentAvatar(), builds a player_id/avatar_id payload, logs it, and publishes it to RabbitMQ with success.player_avatar.update.
Player avatar wiring and deprecation
src/main/java/com/faforever/api/data/domain/Player.java, src/main/java/com/faforever/api/data/domain/AvatarAssignment.java
Player adds the lazy currentAvatar mapping through avatar_id, binds the precommit validation and postcommit update hooks, and AvatarAssignment.isSelected() is marked deprecated.

Branch image push script

Layer / File(s) Summary
Branch and tag validation
push-branch-image.sh
The script rejects develop, main, master, and detached HEAD refs, then derives a Docker tag from the current branch name.
Optional jar build
push-branch-image.sh
The script runs ./gradlew bootJar -x test unless SKIP_BUILD=1 is set.
Image build and push
push-branch-image.sh
The script uses docker buildx build --push with the selected platform and --provenance=false, then prints the pushed image tag.

Sequence Diagram(s)

sequenceDiagram
  participant ElideRuntime
  participant PlayerAvatarValidationHook
  participant AvatarAssignmentRepository

  ElideRuntime->>PlayerAvatarValidationHook: execute(UPDATE, PRECOMMIT, player)
  alt player.getCurrentAvatar() is null
    PlayerAvatarValidationHook->>ElideRuntime: allow update
  else avatar is present
    PlayerAvatarValidationHook->>AvatarAssignmentRepository: findOneByAvatarIdAndPlayerId(avatarId, playerId)
    alt assignment exists
      AvatarAssignmentRepository-->>PlayerAvatarValidationHook: Optional<AvatarAssignment>
      PlayerAvatarValidationHook->>ElideRuntime: allow update
    else assignment missing
      AvatarAssignmentRepository-->>PlayerAvatarValidationHook: Optional.empty()
      PlayerAvatarValidationHook-->>ElideRuntime: ApiException(AVATAR_NOT_ASSIGNED)
    end
  end
Loading
sequenceDiagram
  participant ElideRuntime
  participant PlayerAvatarUpdateHook
  participant RabbitTemplate

  ElideRuntime->>PlayerAvatarUpdateHook: execute(UPDATE, POSTCOMMIT, player)
  PlayerAvatarUpdateHook->>PlayerAvatarUpdateHook: read player.getCurrentAvatar()
  PlayerAvatarUpdateHook->>PlayerAvatarUpdateHook: build payload {player_id, avatar_id}
  PlayerAvatarUpdateHook->>RabbitTemplate: convertAndSend(EXCHANGE_FAF_LOBBY, success.player_avatar.update, payload)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • FAForever/faf-java-api#1150: Updates the same avatar-selection path by deprecating AvatarAssignment.isSelected() and introducing Player#getCurrentAvatar() plus avatar update publishing behavior.

Poem

I wiggle my whiskers and hop through the code,
New avatars now take the rabbit-approved road.
The lobby gets whispers when the change is complete,
And branch-built images launch with a Docker beat.
Hoppity ho! 🐇

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 11.11% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: adding Player.currentAvatar and emitting avatar update events.
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.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/player-avatar-id-and-rabbit-event

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.

@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

🤖 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 `@src/main/java/com/faforever/api/data/domain/Player.java`:
- Around line 41-51: The getCurrentAvatar() method in the Player class has
permission checks but lacks validation that the avatar being assigned actually
belongs to the player through AvatarAssignment. Add a PRECOMMIT phase hook
(similar to the existing PlayerAvatarUpdateHook which is in POSTCOMMIT) or use a
custom validator that leverages
AvatarAssignmentRepository.findOneByAvatarIdAndPlayerId to verify the avatar is
assigned to the player before allowing the update to proceed. This validation
should throw an appropriate exception if the avatar is not assigned to the
player.
🪄 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: 08ae3f29-710e-4b2f-95d9-8e92b5cb6298

📥 Commits

Reviewing files that changed from the base of the PR and between 39eea66 and 51e66fc.

📒 Files selected for processing (2)
  • src/main/java/com/faforever/api/data/domain/Player.java
  • src/main/java/com/faforever/api/data/hook/PlayerAvatarUpdateHook.java

Comment thread src/main/java/com/faforever/api/data/domain/Player.java
Brutus5000 and others added 5 commits June 21, 2026 23:58
The Player.currentAvatar mapping reads login.avatar_id, added in
faf-db v143. Bump the integration-test and compose migration image
so the column exists.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The selected avatar is now tracked via login.avatar_id
(Player.currentAvatar). The legacy `selected` flag remains for
backwards compatibility with older clients and is slated for removal.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Mirror the audit log that existed on the deprecated
AvatarAssignment.selected flag so avatar selection changes via the new
login.avatar_id path are recorded too.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Builds the current branch's jar and pushes a linux/amd64 Docker image
tagged after the branch name, for deploying test builds. Refuses to run
on develop/main/master. Mirrors the script in the lobby server repo.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
IsEntityOwner only ensures the caller edits their own Player row, not
that the avatar is granted to them. Add a PRECOMMIT LifeCycleHook that
verifies an AvatarAssignment exists for the player before allowing the
currentAvatar update, rejecting unowned avatars with AVATAR_NOT_ASSIGNED.
Clearing the avatar (null) remains allowed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@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

🤖 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 `@push-branch-image.sh`:
- Around line 29-32: The tag sanitization for the Docker image does not fully
validate Docker tag requirements. The sed substitution replaces invalid
characters but can still produce invalid tags if the first character becomes a
hyphen or dot (Docker requires first character to be alphanumeric or
underscore), or if the resulting tag exceeds 128 characters. After the sed
substitution that creates the tag variable, add logic to remove any leading
hyphens or dots from the tag and truncate the tag to a maximum of 128 characters
to ensure complete Docker compliance before passing it to the docker buildx
command.

In
`@src/test/java/com/faforever/api/data/hook/PlayerAvatarValidationHookTest.java`:
- Line 21: The import statement for assertThrows is using the JUnit 4 version
from org.junit.Assert, but the test class PlayerAvatarValidationHookTest is
using JUnit Jupiter annotations (`@Test`, `@BeforeEach`, `@ExtendWith`). Change the
static import to use org.junit.jupiter.api.Assertions.assertThrows instead of
org.junit.Assert.assertThrows to align with the Jupiter test framework being
used throughout the test class.
🪄 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: a36f9019-8128-42af-b983-8127394cec2b

📥 Commits

Reviewing files that changed from the base of the PR and between 48e6d4b and f8f579b.

📒 Files selected for processing (5)
  • push-branch-image.sh
  • src/main/java/com/faforever/api/data/domain/Player.java
  • src/main/java/com/faforever/api/data/hook/PlayerAvatarValidationHook.java
  • src/main/java/com/faforever/api/error/ErrorCode.java
  • src/test/java/com/faforever/api/data/hook/PlayerAvatarValidationHookTest.java
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/main/java/com/faforever/api/data/domain/Player.java

Comment thread push-branch-image.sh
Comment on lines +29 to +32
# Docker tags must match [A-Za-z0-9_][A-Za-z0-9_.-]{0,127}.
# Replace anything else (most commonly '/' from branch prefixes) with '-'.
tag=$(printf '%s' "$branch" | sed 's/[^A-Za-z0-9_.-]/-/g')

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Ensure sanitized tag is always Docker-valid before invoking buildx.

Current sanitization replaces invalid chars, but it can still yield an invalid first character (./-) or exceed 128 chars, causing avoidable late failures in docker buildx.

Suggested patch
-# Docker tags must match [A-Za-z0-9_][A-Za-z0-9_.-]{0,127}.
-# Replace anything else (most commonly '/' from branch prefixes) with '-'.
-tag=$(printf '%s' "$branch" | sed 's/[^A-Za-z0-9_.-]/-/g')
+# Docker tags must match [A-Za-z0-9_][A-Za-z0-9_.-]{0,127}.
+# Replace invalid chars, normalize leading '.'/'-', and cap length.
+tag=$(
+  printf '%s' "$branch" \
+    | sed 's/[^A-Za-z0-9_.-]/-/g' \
+    | sed 's/^[.-][.-]*/_/' \
+    | cut -c1-128
+)
+
+if [ -z "$tag" ]; then
+  echo "Refusing to push: sanitized tag is empty for branch '$branch'." >&2
+  exit 1
+fi
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# Docker tags must match [A-Za-z0-9_][A-Za-z0-9_.-]{0,127}.
# Replace anything else (most commonly '/' from branch prefixes) with '-'.
tag=$(printf '%s' "$branch" | sed 's/[^A-Za-z0-9_.-]/-/g')
# Docker tags must match [A-Za-z0-9_][A-Za-z0-9_.-]{0,127}.
# Replace invalid chars, normalize leading '.'/'-', and cap length.
tag=$(
printf '%s' "$branch" \
| sed 's/[^A-Za-z0-9_.-]/-/g' \
| sed 's/^[.-][.-]*/_/' \
| cut -c1-128
)
if [ -z "$tag" ]; then
echo "Refusing to push: sanitized tag is empty for branch '$branch'." >&2
exit 1
fi
🤖 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 `@push-branch-image.sh` around lines 29 - 32, The tag sanitization for the
Docker image does not fully validate Docker tag requirements. The sed
substitution replaces invalid characters but can still produce invalid tags if
the first character becomes a hyphen or dot (Docker requires first character to
be alphanumeric or underscore), or if the resulting tag exceeds 128 characters.
After the sed substitution that creates the tag variable, add logic to remove
any leading hyphens or dots from the tag and truncate the tag to a maximum of
128 characters to ensure complete Docker compliance before passing it to the
docker buildx command.

Comment thread src/test/java/com/faforever/api/data/hook/PlayerAvatarValidationHookTest.java Outdated
Brutus5000 and others added 2 commits June 22, 2026 13:03
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…-id-and-rabbit-event

# Conflicts:
#	compose.yaml
#	src/inttest/java/com/faforever/api/config/MainDbTestContainers.java
@Brutus5000 Brutus5000 merged commit 43234e2 into develop Jun 25, 2026
3 of 4 checks passed
@Brutus5000 Brutus5000 deleted the feat/player-avatar-id-and-rabbit-event branch June 25, 2026 22:35
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.

1 participant