Skip to content

Conversation

@hwh008
Copy link
Contributor

@hwh008 hwh008 commented Oct 29, 2025

remove specific one relationship that you get from an entity. firstly, do it by checking if the relationship references are the identical, not if the objects are the same.

if the relationship isn't the inner object within entity, then use condition-match to take off similar relationships.

Summary by CodeRabbit

  • Bug Fixes

    • Relationship removal now prioritizes exact matches, avoiding unintended bulk removals and enabling precise single-relationship deletions.
  • Tests

    • Added test coverage to verify removal of specific relationship instances (note: a duplicate test was added accidentally).
…t by checking if the relationship references are the same, not if the objects are the same.
@coderabbitai
Copy link

coderabbitai bot commented Oct 29, 2025

Walkthrough

Modified entity relationship removal to prefer an exact relationship instance removal first, suppressing subsequent pattern-based removals in that call. If no exact match is found, pattern-based removals proceed (respecting any limit). Added a test for removing a specific relationship instance; the test was accidentally duplicated.

Changes

Cohort / File(s) Summary
Core logic
addons/gecs/ecs/entity.gd
remove_relationship now does a first pass to detect and queue an exact rel == relationship removal and sets pattern_remove false for that call; if no exact match, it proceeds to pattern-based removals honoring limit.
Tests
addons/gecs/tests/core/test_relationships.gd
Added func test_remove_specific_relationship() which creates multiple C_Likes relationships, removes one specific instance, and asserts remaining instances; the test function appears twice (duplicate declaration) in the file.

Sequence Diagram(s)

sequenceDiagram
    participant Caller
    participant Entity
    rect rgb(245,250,255)
    note right of Entity: remove_relationship(relationship, limit?)
    end
    Caller->>Entity: remove_relationship(relationship, limit)
    Entity->>Entity: First pass: scan for exact match (rel == relationship)
    alt Exact match found
        Entity->>Entity: queue exact removal\nset pattern_remove = false
    else No exact match
        Entity->>Entity: keep pattern_remove = true
    end
    Entity->>Entity: Second pass: iterate relationships
    alt pattern_remove == true
        Entity->>Entity: remove rels where rel.matches(relationship)\n(honors limit)
    else pattern_remove == false
        Entity->>Entity: skip pattern-based removals
    end
    Entity->>Caller: emit relationship_removed for each removed\nreturn removal result
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20–25 minutes

  • Attention areas:
    • Ensure exact-match uses object identity as intended (not structural equality).
    • Verify pattern-based removals are skipped when an exact match is queued and that limit is respected when applicable.
    • Remove/consolidate the duplicate test_remove_specific_relationship in the tests file.

Poem

🐇 I dug through links both near and wide,
Spotted one true bond I could not hide.
I plucked that thread, left patterns be,
Now two remain where three used to be,
A hop, a nibble—relations tidy! 🎉

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title "Remove specific one relationship when its reference pointers are the identical" is directly related to the main change in the changeset. The PR modifies the remove_relationship function in entity.gd to compare relationship references (pointers) for identity rather than equality, and the title accurately captures this core functionality. The title is specific and clear—it conveys the primary change (removal of a specific relationship by pointer identity) without being vague or generic, and a teammate scanning history would understand the intent. While the phrasing has minor grammatical awkwardness, the meaning is unambiguous and the title is sufficiently descriptive for the changeset.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e506264 and 189aafe.

📒 Files selected for processing (1)
  • addons/gecs/ecs/entity.gd (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • addons/gecs/ecs/entity.gd

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.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (3)
addons/gecs/ecs/entity.gd (2)

273-297: Consider performance optimization and improve documentation.

The two-pass approach correctly implements exact-match-first with pattern-match fallback, but iterating through relationships twice may impact performance for entities with many relationships.

Additionally, the docstring (lines 255-272) should document this new exact-match behavior to clarify that passing an actual relationship instance from get_relationship() will remove only that specific instance, while other relationship objects trigger pattern-based removal.

Consider optimizing to a single-pass implementation:

 func remove_relationship(relationship: Relationship, limit: int = -1) -> void:
 	if limit == 0:
 		return
 
 	var to_remove = []
 	var removed_count = 0
-	
-	var pattern_remove = true
-	for rel in relationships:
-		if rel == relationship:
-			to_remove.append(rel)
-			pattern_remove = false
-			break
 
+	# Check for exact match first
+	var exact_match_index = relationships.find(relationship)
+	if exact_match_index != -1:
+		to_remove.append(relationships[exact_match_index])
+	else:
+		# No exact match, use pattern matching
 	for rel in relationships:
-		if pattern_remove and rel.matches(relationship):
+			if rel.matches(relationship):
 			to_remove.append(rel)
 			removed_count += 1
-			# If limit is positive and we've reached it, stop collecting
 			if limit > 0 and removed_count >= limit:
 				break
+	}
 
 	for rel in to_remove:
 		relationships.erase(rel)
 		relationship_removed.emit(self, rel)

Also update the docstring to document the exact-match-first behavior:

 ## Removes a relationship from the entity.[br]
 ## [param relationship] The [Relationship] to remove.[br]
 ## [param limit] Maximum number of relationships to remove. -1 = all (default), 0 = none, >0 = up to that many.[br]
 ## [br]
+## If the relationship parameter is an exact instance (same object reference) from the entity's relationships,[br]
+## only that specific relationship is removed. Otherwise, pattern matching is used to find matching relationships.[br]
+## [br]
 ## [b]Examples:[/b]

280-280: Minor: Unnecessary blank line added.

This empty line doesn't add to code readability in this context.

addons/gecs/tests/core/test_relationships.gd (1)

981-999: Good test coverage for the new exact-match functionality.

The test correctly validates that removing a specific relationship instance only removes that one relationship, not all pattern-matching relationships. The test setup is clear and assertions are comprehensive.

Consider adding edge case tests to strengthen coverage:

func test_remove_specific_relationship_not_in_entity():
	# Test removing a relationship instance that's not in the entity
	var external_rel = Relationship.new(C_Likes.new(2), e_alice)
	e_bob.add_relationship(Relationship.new(C_Likes.new(1), e_alice))
	
	# Should not remove anything since it's not the same instance
	e_bob.remove_relationship(external_rel)
	assert_int(e_bob.get_relationships(Relationship.new({C_Likes:{}}, null)).size()).is_equal(1)

func test_remove_specific_relationship_with_limit():
	# Test that limit parameter is ignored for exact matches
	var rel = Relationship.new(C_Likes.new(1), e_alice)
	e_bob.add_relationship(rel)
	e_bob.remove_relationship(rel, 0)  # limit=0 should still work for exact match?
	
	# Document expected behavior
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5bc63a9 and c04350f.

📒 Files selected for processing (2)
  • addons/gecs/ecs/entity.gd (1 hunks)
  • addons/gecs/tests/core/test_relationships.gd (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.gd

📄 CodeRabbit inference engine (CLAUDE.md)

Always use the res:// prefix for file paths in Godot scripts and tests

Files:

  • addons/gecs/ecs/entity.gd
  • addons/gecs/tests/core/test_relationships.gd
🔇 Additional comments (2)
addons/gecs/tests/core/test_relationships.gd (2)

980-980: Clarification: Empty line addition.

Line 980 shows an empty line was added (marked with ~). This is fine for spacing between test functions.


981-981: No duplicate function declaration found—original review concern is unfounded.

The verification confirms there is only one declaration of test_remove_specific_relationship at line 981. The AI summary's claim about a duplicate is incorrect. The code requires no changes.

Likely an incorrect or invalid review comment.

@hwh008 hwh008 force-pushed the fix/remove_relationship_by_pointer branch from c04350f to e506264 Compare October 29, 2025 13:16
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
addons/gecs/tests/core/test_relationships.gd (1)

981-999: Consider consistent entity setup for better test isolation.

The test creates a fresh e_bob entity but relies on e_alice from the before_test() setup. This mixing of fresh and setup entities could cause issues if the test runs in isolation or if the setup changes.

Consider one of these approaches:

Option 1: Use setup entities consistently

 func test_remove_specific_relationship():
-	e_bob = Person.new()
-	world.add_entity(e_bob)
+	# Use e_bob from before_test() setup
 	
 	e_bob.add_relationship(Relationship.new(C_Likes.new(1), e_alice))

Option 2: Create all entities fresh for complete isolation

 func test_remove_specific_relationship():
 	e_bob = Person.new()
+	e_alice = Person.new()
+	e_alice.name = "e_alice"
 	world.add_entity(e_bob)
+	world.add_entity(e_alice)
 	
 	e_bob.add_relationship(Relationship.new(C_Likes.new(1), e_alice))

Otherwise, the test correctly validates the PR's objective of removing a specific relationship by reference identity. The assertions appropriately verify that only the targeted relationship is removed while others remain.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c04350f and e506264.

📒 Files selected for processing (2)
  • addons/gecs/ecs/entity.gd (1 hunks)
  • addons/gecs/tests/core/test_relationships.gd (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • addons/gecs/ecs/entity.gd
🧰 Additional context used
📓 Path-based instructions (1)
**/*.gd

📄 CodeRabbit inference engine (CLAUDE.md)

Always use the res:// prefix for file paths in Godot scripts and tests

Files:

  • addons/gecs/tests/core/test_relationships.gd
@csprance
Copy link
Owner

Actionable comments posted: 0

🧹 Nitpick comments (3)

addons/gecs/ecs/entity.gd (2)> 273-297: Consider performance optimization and improve documentation.

The two-pass approach correctly implements exact-match-first with pattern-match fallback, but iterating through relationships twice may impact performance for entities with many relationships.
Additionally, the docstring (lines 255-272) should document this new exact-match behavior to clarify that passing an actual relationship instance from get_relationship() will remove only that specific instance, while other relationship objects trigger pattern-based removal.
Consider optimizing to a single-pass implementation:

 func remove_relationship(relationship: Relationship, limit: int = -1) -> void:
 	if limit == 0:
 		return
 
 	var to_remove = []
 	var removed_count = 0
-	
-	var pattern_remove = true
-	for rel in relationships:
-		if rel == relationship:
-			to_remove.append(rel)
-			pattern_remove = false
-			break
 
+	# Check for exact match first
+	var exact_match_index = relationships.find(relationship)
+	if exact_match_index != -1:
+		to_remove.append(relationships[exact_match_index])
+	else:
+		# No exact match, use pattern matching
 	for rel in relationships:
-		if pattern_remove and rel.matches(relationship):
+			if rel.matches(relationship):
 			to_remove.append(rel)
 			removed_count += 1
-			# If limit is positive and we've reached it, stop collecting
 			if limit > 0 and removed_count >= limit:
 				break
+	}
 
 	for rel in to_remove:
 		relationships.erase(rel)
 		relationship_removed.emit(self, rel)

Also update the docstring to document the exact-match-first behavior:

 ## Removes a relationship from the entity.[br]
 ## [param relationship] The [Relationship] to remove.[br]
 ## [param limit] Maximum number of relationships to remove. -1 = all (default), 0 = none, >0 = up to that many.[br]
 ## [br]
+## If the relationship parameter is an exact instance (same object reference) from the entity's relationships,[br]
+## only that specific relationship is removed. Otherwise, pattern matching is used to find matching relationships.[br]
+## [br]
 ## [b]Examples:[/b]

280-280: Minor: Unnecessary blank line added.
This empty line doesn't add to code readability in this context.

addons/gecs/tests/core/test_relationships.gd (1)> 981-999: Good test coverage for the new exact-match functionality.

The test correctly validates that removing a specific relationship instance only removes that one relationship, not all pattern-matching relationships. The test setup is clear and assertions are comprehensive.
Consider adding edge case tests to strengthen coverage:

func test_remove_specific_relationship_not_in_entity():
	# Test removing a relationship instance that's not in the entity
	var external_rel = Relationship.new(C_Likes.new(2), e_alice)
	e_bob.add_relationship(Relationship.new(C_Likes.new(1), e_alice))
	
	# Should not remove anything since it's not the same instance
	e_bob.remove_relationship(external_rel)
	assert_int(e_bob.get_relationships(Relationship.new({C_Likes:{}}, null)).size()).is_equal(1)

func test_remove_specific_relationship_with_limit():
	# Test that limit parameter is ignored for exact matches
	var rel = Relationship.new(C_Likes.new(1), e_alice)
	e_bob.add_relationship(rel)
	e_bob.remove_relationship(rel, 0)  # limit=0 should still work for exact match?
	
	# Document expected behavior

📜 Review details

Can we implement this suggestion I like this here if it works this way we only iterate once

@hwh008
Copy link
Contributor Author

hwh008 commented Oct 30, 2025

yes. i had made more commits. it's similar to that suggestion.
check this: 189aafe

@csprance csprance merged commit 1b1a9e8 into csprance:main Oct 30, 2025
1 check passed
@csprance
Copy link
Owner

Thanks! Looks good!

github-actions bot added a commit that referenced this pull request Oct 30, 2025
Based on commit 1b1a9e8 by Chris Sprance:

Merge pull request #70 from hwh008/fix/remove_relationship_by_pointer
github-actions bot added a commit that referenced this pull request Oct 30, 2025
Based on commit 1b1a9e8 by Chris Sprance:

Merge pull request #70 from hwh008/fix/remove_relationship_by_pointer
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

2 participants