Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pygame.event.get() returns incorrect order of events when using eventtype kwarg #3305

Open
bigwhoopgames opened this issue Jan 25, 2025 · 5 comments
Labels
bug Not working as intended

Comments

@bigwhoopgames
Copy link

When calling pygame.event.get() with a sequence of event types using the eventtype keyword the return values are sorted by event type rather than preserving their order in the queue. This works fine when only one event type is pass but it problematic when asking for multiple types. Events returned by this function should not be reordered. The docs do not mention reordering and it does not seem like wanted behavior when dealing with events as order inherently matters when dealing with a queue.

On a side not, the eventtype kwarg does not accept sets of events and only takes sequences. It would be nice to allow for sets to be used as well as lists.

I did not test if using the exclude functionality would result in events being sorted as well.

Environment:

pygame-ce 2.5.2 (SDL 2.30.8, Python 3.12.0)

Current behavior:

Events are returned sorted by event type.

Expected behavior:

Events to return in the same order as they exist in the queue regardless of type.

Test code


pygame.init()

# define custom events
USEREVENT_1 = pygame.event.custom_type()
USEREVENT_2 = pygame.event.custom_type()

# post custom events
pygame.event.post(pygame.event.Event(USEREVENT_1, {'message': 'First Event 1'}))
pygame.event.post(pygame.event.Event(USEREVENT_2, {'message': 'First Event 2'}))
pygame.event.post(pygame.event.Event(USEREVENT_1, {'message': 'Second Event 1'}))

# user events
userevent_types = [USEREVENT_1, USEREVENT_2]

# print the events in the order returned
for event in pygame.event.get(eventtype = userevent_types):
    print(f'Message: {event.message}')

pygame.quit()
@bigwhoopgames bigwhoopgames added the bug Not working as intended label Jan 25, 2025
@bigwhoopgames
Copy link
Author

I ultimately moved away from using the eventtype kwarg. I had planned to first pull any pygame.QUIT events, followed by custom user events, followed by a regular event.get() for any leftovers. The performance impact of calling .get multiple times and using it to effectively order events the way I wanted was too great. Profiling reported about a half millisecond per call and doing three calls added a whole millisecond+ to each frame time.

It might be prudent to give the .get function some love and attempt to improve it's performance / functionality in general. It is a critical function for pygame and should be well tuned.

Anyways, back to one big loop of if else logic for me.

@ankith26
Copy link
Member

The underlying SDL function (SDL_PeepEvents) does not support arbitrary event type sequences, so we internally make multiple single event peek calls, and you are right, event ordering is lost in this process.
I imagine that fixing this will make the code more complex and therefore probably impact performance as well.

As far as performance is concerned, the best thing to do is to simply call the regular pygame.event.get(), as this is the most common case the implementation for this is more optimized than all the other usecases in the current implementation.

@Osaidgit
Copy link

Osaidgit commented Feb 9, 2025

The issue you're describing with pygame.event.get() reordering events when using the eventtype keyword argument is indeed problematic, especially since the order of events is often crucial in game development. The fact that the events are being sorted by their type rather than being returned in the order they were queued is not the expected behavior, and it contradicts the typical use case of an event queue.

Current Behavior

When you call pygame.event.get(eventtype=[USEREVENT_1, USEREVENT_2]), the events are being returned sorted by their type, which is not documented and not the intended behavior. This can lead to confusion and bugs in your game logic, especially if the order of events matters.

Expected Behavior

The events should be returned in the order they were added to the queue, regardless of their type. This is the standard behavior for event queues and is what most developers would expect.

Workaround

As you mentioned, one workaround is to avoid using the eventtype keyword argument and instead filter the events manually in your event loop. This way, you can ensure that the events are processed in the correct order. Here's an example of how you might do this:

import pygame

pygame.init()

# Define custom events
USEREVENT_1 = pygame.event.custom_type()
USEREVENT_2 = pygame.event.custom_type()

# Post custom events
pygame.event.post(pygame.event.Event(USEREVENT_1, {'message': 'First Event 1'}))
pygame.event.post(pygame.event.Event(USEREVENT_2, {'message': 'First Event 2'}))
pygame.event.post(pygame.event.Event(USEREVENT_1, {'message': 'Second Event 1'}))

# Main event loop
running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        elif event.type in (USEREVENT_1, USEREVENT_2):
            print(f'Message: {event.message}')
        # Handle other event types here

pygame.quit()

Performance Considerations

You mentioned that calling pygame.event.get() multiple times had a performance impact. While this is true, manually filtering events in a single loop is generally more efficient than making multiple calls to pygame.event.get(). If performance is a concern, you might want to profile your code to see if there are any other bottlenecks that can be optimized.

Future Improvements

It would be beneficial for the Pygame community to address this issue in future releases. The eventtype keyword argument should be fixed to preserve the order of events, and it would also be helpful if it accepted sets in addition to lists. If you're comfortable with contributing to open-source projects, you might consider opening an issue or submitting a pull request to the Pygame-CE GitHub repository.

Conclusion

For now, the best approach is to manually filter events in your event loop to ensure they are processed in the correct order. This avoids the reordering issue and gives you more control over how events are handled in your game.

@oddbookworm
Copy link
Member

multiple calls to pygame.event.get()

Don't make multiple calls to pygame.event.get() in one frame, unless you absolutely know what you're doing. Period. Best practice is simply just to grab all of your events a single time at once in the frame and then divvy out copies to whatever objects need them

@Starbuck5
Copy link
Member

Don't make multiple calls to pygame.event.get() in one frame, unless you absolutely know what you're doing. Period. Best practice is simply just to grab all of your events a single time at once in the frame and then divvy out copies to whatever objects need them

I believe the comment you're replying to is AI generated. It's actually a good summary IMO, but still.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Not working as intended
Projects
None yet
Development

No branches or pull requests

5 participants