Skip to content

Conversation

w-czerski
Copy link
Contributor

@w-czerski w-czerski commented Jan 28, 2025

About:

This PR introduces a notification bus for the CSV spawner.
It provides the ability to trigger actions when entities are spawned and to gather information when the spawning process begins.
This is particularly useful when there is a need to react to the moment "when entities are spawned".

Signed-off-by: Wojciech Czerski <[email protected]>
Signed-off-by: Wojciech Czerski <[email protected]>
Signed-off-by: Wojciech Czerski <[email protected]>
Signed-off-by: Wojciech Czerski <[email protected]>
Signed-off-by: Wojciech Czerski <[email protected]>
Copy link
Contributor

@arturkamieniecki arturkamieniecki left a comment

Choose a reason for hiding this comment

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

There is a lot of info passed in the bus calls. Verify if everything is needed. Consider creating a struct for this data.

Overall looks good!

@w-czerski
Copy link
Contributor Author

Sure, it’s just a quick fix that I needed. A lot of the information is indeed unnecessary. I’ll refactor it soon.

On further thought, it might be better to introduce some global robotec-o3de-tools notification buses to reduce dependencies? This way, we would only need to include the notification bus in CMake once, instead of creating dependencies for individual tools like spline tools, geo json, csv spawner, etc.

Copy link
Contributor

@patrykantosz patrykantosz left a comment

Choose a reason for hiding this comment

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

Code looks okay. I'm just curious if anything (beyond this PR, as it is obvious the implementation is not here) implements handlers for these two newly added functions? Or has the bus been created for some further work planned for the future?

@w-czerski
Copy link
Contributor Author

In current state, we need some explicit logic to handle is terrain created (please look at #102).
This is just a helpful functionality to prevent users to stacks tick frames, or queue functions. User can directly do something on spawn ready, and skip in most cases terrain handling (since this is not useful or needed for user).

Exactly this functionality is needed in one project I'm working on. I would reconsider what information should be passed down in the buses. Right now notification is only needed.

@w-czerski
Copy link
Contributor Author

w-czerski commented Jan 29, 2025

I've added:

  • more context with descriptions
  • default implementation of events (do nothing - we don't want to force override of each not implemented function)
  • introduced struct with info
  • added a enum result status code for the spawn finished process
    Let me know what you think. I've made it more generic for the future.

Comment on lines 29 to 32
Success = 0, ///< Operation succeeded.
Fail = 1 << 0, ///< Generic failure.
SpawnStopped = 1 << 1, ///< Spawning was stopped prematurely but not necessarily a failure.
ErrorOccurred = 1 << 2, ///< An error occurred during spawning (potentially recoverable).
Copy link
Collaborator

Choose a reason for hiding this comment

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

Are the bitshifts needed here? Typically, the bitshifts are used when multiple flags can happen at once.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Overall, I thought - spawn can still be successful even when some errors / issues occur. Like partialy some entites are spawned, some are spawned but not in desired place etc.

@jhanca-robotecai
Copy link
Collaborator

Rethining the problem... Could we include a behavioral context for LUA/ScriptCanvas in this PR? I can imagine a script reacting to spawned objects.

@w-czerski
Copy link
Contributor Author

Sure. I'll add also similar notify bus for Geo Json Spawner.

Signed-off-by: Wojciech Czerski <[email protected]>
Signed-off-by: Wojciech Czerski <[email protected]>
Signed-off-by: Wojciech Czerski <[email protected]>
@w-czerski w-czerski self-assigned this Mar 18, 2025
w-czerski added 14 commits July 2, 2025 14:40
Signed-off-by: Wojciech Czerski <[email protected]>
Signed-off-by: Wojciech Czerski <[email protected]>
Signed-off-by: Wojciech Czerski <[email protected]>
Signed-off-by: Wojciech Czerski <[email protected]>
Signed-off-by: Wojciech Czerski <[email protected]>
Signed-off-by: Wojciech Czerski <[email protected]>
Signed-off-by: Wojciech Czerski <[email protected]>
Signed-off-by: Wojciech Czerski <[email protected]>
Signed-off-by: Wojciech Czerski <[email protected]>
Signed-off-by: Wojciech Czerski <[email protected]>
@w-czerski w-czerski force-pushed the wc/csvspawner_notifications branch from 9ce6d49 to b30dda3 Compare July 2, 2025 12:40
Copy link
Contributor

@norbertprokopiuk norbertprokopiuk left a comment

Choose a reason for hiding this comment

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

I checked the code and I found some key issues that need your attention:

  1. Unused Variable: The presence of an unused lambda capture relating to tickets is causing compilation issues. This needs to be either utilized or removed to get the code to compile.

  2. Dangling References: There are dangling references, which can lead to unexpected behavior.

  3. Logical Issues: The conditional logic involving totalTicket and completedTickets isn't set up correctly. This results in conditions that can never be met due to how the variables are managed.

  4. Missing Notifications: It's important to include notification calls to alert users about any failures.

I will be happy to check refined version

};
optionalArgs.m_completionCallback =
[parentId, &spawnStatusCode](
[parentId, &spawnStatusCode, &broadcastSpawnInfo, &tickets, totalTickets, &completedTickets](
Copy link
Contributor

Choose a reason for hiding this comment

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

It appears that the four references in question are currently dangling. This arises because local variables are being passed to the completion callback, which is executed after the SpawnEntities function has ended. Once the function finishes, the references to the callback become invalid since the variables they point to no longer exist. It is important to address this issue to ensure the code functions as intended.

};
optionalArgs.m_completionCallback =
[parentId, &spawnStatusCode](
[parentId, &spawnStatusCode, &broadcastSpawnInfo, &tickets, totalTickets, &completedTickets](
Copy link
Contributor

Choose a reason for hiding this comment

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

The reference to tickets is not only dangling but also remains unused, which consequently interferes with building O3DE in the profile configuration.

Comment on lines 337 to 342
completedTickets++;
if (completedTickets == totalTickets)
{
// Call CsvSpawner EBus notification - Finished
CsvSpawnerNotificationBus::Broadcast(&CsvSpawnerInterface::OnEntitiesSpawnFinished, broadcastSpawnInfo, spawnStatusCode);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Even if we assume that completedTickets is not a dangling reference, the current condition will not function as intended. The issue arises because totalTicket is passed as a copy and incremented after the callback is defined. Consequently, completedTickets will always be one greater than totalTickets, resulting in the condition never being met. If you move the totalTickets++ before defining the callback, it would trigger with every callback. Therefore, it is advisable to explore a different approach to address this condition.

[parentId, &spawnStatusCode, &broadcastSpawnInfo, &tickets, totalTickets, &completedTickets](
[[maybe_unused]] AzFramework::EntitySpawnTicket::Id ticketId, AzFramework::SpawnableConstEntityContainerView view)
{
if (view.empty())
Copy link
Contributor

Choose a reason for hiding this comment

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

The code within this if statement exhibits undefined behavior because spawnStatusCode is a dangling reference. This may be the reason why you have not encountered any issues, as the code possibly has not executed as expected. Additionally, the code is missing a notification call to indicate a failure. A similar situation is present in the preinsertionCallback.

w-czerski added 3 commits July 2, 2025 15:36
Signed-off-by: Wojciech Czerski <[email protected]>
Signed-off-by: Wojciech Czerski <[email protected]>
Copy link
Contributor

@norbertprokopiuk norbertprokopiuk left a comment

Choose a reason for hiding this comment

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

I checked your fix and undefined behaviour are removed, which is nice. Good Job. Although there are some issues still present.

  • there is invalid logic with decrementing of pendingSpawns
  • I think that notifcation bus definition is a bit wrong. We can add multiple CSVComponents on scene, so we should either send one notification when all components finished their jobs or each of them should send individual notification. Currently all notifications are broadcasted what is not optimal solution

Could you also explain why you decide to solve sending notification in the current way? I was thinking if wouldn't it be cleaner to define internal event, only in this Gem, which would be trigger everytime single completion callback is executed. It would take status, and cmopleted ticketId and would trigger the event on entityId of the componentOwner. Each component already has map of spawnedTickets. you would need to add unordered_set, which will gather all completedTickets inside mentioned event callback. If all tickets are completed, then you can send main notification that spawning is finished. I think it could potentially limit amount of data passed between threads and it might be a bit cleaner. But I don't want to force you to change the implementation right now. I would like to know your point of view on this problem.


#include "CsvSpawnerUtils.h"

#include "AzCore/std/smart_ptr/make_shared.h"
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
#include "AzCore/std/smart_ptr/make_shared.h"
#include <AzCore/std/smart_ptr/make_shared.h>

SpawnInfo broadcastSpawnInfo =
SpawnInfo{ entitiesToSpawn, physicsSceneName, parentId }; // Spawn Info used in CsvSpawner EBus notify.
SpawnStatus spawnStatusCode = SpawnStatus::Success; // Spawn Status Code used for CsvSpawner EBus notify - OnEntitiesSpawnFinished.
// SpawnStatus spawnStatusCode = SpawnStatus::Success; // Spawn Status Code used for CsvSpawner EBus notify -
Copy link
Contributor

Choose a reason for hiding this comment

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

I recommend to remove this code if it is commented

Comment on lines 37 to 44
virtual void OnEntitiesSpawnBegin(CsvSpawnerUtils::SpawnInfo m_spawnInfo) = 0;

/**
* @brief Called when entity spawning finishes.
* @param m_spawnInfo Struct holding information about entities to be spawned.
* @param m_statusCode Status code indicating success, failure and warnings of the spawn.
*/
virtual void OnEntitiesSpawnFinished(CsvSpawnerUtils::SpawnInfo& m_spawnInfo, CsvSpawnerUtils::SpawnStatus m_statusCode) = 0;
virtual void OnEntitiesSpawnFinished(CsvSpawnerUtils::SpawnInfo m_spawnInfo, CsvSpawnerUtils::SpawnStatus m_statusCode) = 0;
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there a reason for removing those references?

Copy link
Contributor

Choose a reason for hiding this comment

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

I think that created notification methods are correct, but I've noticed that you set AddressPolicy as Single. We can have multiple CSVSpawner components on the scene. It means that we should be send notification either when all components finishes their jobs (you would need system component for that), or you can change this notification bus AddressPolicy to ById to allow each component to send their own notification. Then it would be client call to decide on which entity/component notification they want to connect with.

auto spawner = AZ::Interface<AzFramework::SpawnableEntitiesDefinition>::Get();
AZ_Assert(spawner, "Unable to get spawnable entities definition");

auto pendingSpawns = AZStd::make_shared<AZStd::atomic_int>(static_cast<int>(entitiesToSpawn.size()));
Copy link
Contributor

Choose a reason for hiding this comment

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

Inside for loop through entitiesToSpawn you have some conditions which cause iteration skip without calling spawning interface. This causes lack of decremention of pendingSpawns what leads to situation where condition in line 349 is never met if as least one entity failed to spawn.

Comment on lines +343 to +344
const AZ::Entity* root = *view.begin();
AZ::TransformBus::Event(root->GetId(), &AZ::TransformBus::Events::SetParent, parentId);
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
const AZ::Entity* root = *view.begin();
AZ::TransformBus::Event(root->GetId(), &AZ::TransformBus::Events::SetParent, parentId);
const AZ::Entity* root = *view.begin();
AZ_Assert(root, "Invalid root entity");
AZ::TransformBus::Event(root->GetId(), &AZ::TransformBus::Events::SetParent, parentId);

@jhanca-robotecai jhanca-robotecai deleted the branch o3de-2409 July 25, 2025 07:55
@jhanca-robotecai jhanca-robotecai changed the base branch from main to 2409 July 25, 2025 08:50
@jhanca-robotecai jhanca-robotecai changed the title Csv Spawner Notification Bus [2409] Csv Spawner Notification Bus Jul 25, 2025
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.

7 participants