MegaAntiCheat / client-backend

GNU General Public License v3.0
118 stars 25 forks source link

Refactor to use an event loop with message propagation #114

Closed Bash-09 closed 7 months ago

Bash-09 commented 7 months ago

This is a huge refactor that pretty much reworks the entire core of the application so that it uses an EventLoop object which can be composed with message sources and handlers, which emit messages that can be handled by other handlers and update the internal state.

This allows different operations that need to happen on the data to be properly compartmentalized and keep their logic entirely separate to the operation of everything else. For example, managing friend list lookups was previously very tightly intertwined with the main loop, adding a large amount of complexity (and indentation). Now this operation can be done by defining some messages and a handler struct to manage the logic, specifying them in the define_messages and define_handlers macros, and appending it to the event loop:

// Message
#[derive(Debug)]
pub struct FriendLookupResult {
    steamid: SteamID,
    result: Result<Vec<Friend>, SteamAPIError>,
}
impl StateUpdater<MACState> for FriendLookupResult {
    fn update_state(self, state: &mut MACState) {
        // Update state in here
    }
}

// Handler
pub struct LookupProfiles {
    batch_buffer: VecDeque<SteamID>,
}

impl<IM, OM> HandlerStruct<MACState, IM, OM> for LookupProfiles
where
    IM: Is<NewPlayers> + Is<ProfileLookupBatchTick>,
    OM: Is<ProfileLookupResult>,
{
    fn handle_message(&mut self, state: &MACState, message: &IM) -> Option<Handled<OM>> {
        // Actual logic in here
    }
}

define_messages!(Message<MACState>:
    Refresh,
    Command,
    RawConsoleOutput,
    ConsoleOutput,
    NewPlayers,
    ProfileLookupBatchTick,
    ProfileLookupResult,
    FriendLookupResult,
    Preferences,
    UserUpdates,
    WebRequest
);

define_handlers!(Handler<MACState, Message>:
    CommandManager,
    ConsoleParser,
    ExtractNewPlayers,
    LookupProfiles,
    LookupFriends,
    WebAPIHandler
);

fn main() {
    // ...
    let mut event_loop: EventLoop<MACState, Message, Handler> = EventLoop::new()
        .add_source(console_log)
        .add_source(refresh_timer)
        .add_source(lookup_batch_timer)
        .add_source(Box::new(web_requests))
        .add_handler(CommandManager::new())
        .add_handler(ConsoleParser::default())
        .add_handler(ExtractNewPlayers)
        .add_handler(LookupProfiles::new())
        .add_handler(LookupFriends)
        .add_handler(WebAPIHandler);
    // ...
}

This has, of course, touch a huge amount of the code and involved rewriting several of the core functionalities, so extensive testing will be necessary before I merge this in. I plan to make a diagram to show the current flow of all the different messages and handlers in the future, and if anybody plans to add new functionality in the future I will be happy to write up a more details description of how the event loop works and what's necessary to add new messages and handlers.

megascatterbomb commented 7 months ago

Functionality wise everything seems to be working correctly. Confirmed that friend logic works for both the user's friends and for friends of cheaters.

As discussed in the discord, we may wish to recheck the conditions for the FriendsAPIUsage::CheatersOnly conditions whenever the user changes a verdict (currently a verdict change to cheater doesn't trigger a friends list query), but all the existing functionality appears to be working well.

Bash-09 commented 7 months ago

Awesome! After fixing the couple things that did pop up, I'm feeling confident to merge this in now. Anything else that pops up can be dealt with.