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

Ideas related to figuring out who the local player is for multiplayer Main or Battle Stages #15

Open
LocalH opened this issue Oct 15, 2024 · 3 comments

Comments

@LocalH
Copy link
Contributor

LocalH commented Oct 15, 2024

I see the "LogPilgrimCorePlayerState: CLIENT: player LocalAitch set readiness to 1" lines that seem to display for each Battle Stage. The readme states you have not been able to reliably determine who the local player is. If the script is running before the player logs in to Fortnite, there is a line present when the player logs in "[2024.10.14-16.06.47:019][626]LogOnlineAccount: Display: [UOnlineAccountCommon::ProcessUserLogin] Successfully logged in user. UserId=[xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx] DisplayName=[LocalAitch] EpicAccountId=[MCP:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx] AuthTicket=[]" where the strings of x's are 32-byte hex strings that for me were identical. You could parse this line and extract the display name. Not sure what contingency you might want to have if someone runs the script after logging in to Fortnite (or in the slim chance they encounter an issue and have to restart it).

@LeviSnoot
Copy link
Owner

The problem isn't acquiring the player's username, it's that when you're in a Main Stage game with multiple players, the line LogPilgrimGemBreakListener: UPilgrimGemBreakListener::Init: is printed in the log once for every player in that game, and this line does not contain a unique player identifier. It does contain a string that is probably used as an identifier internally, but this string changes often and isn't tied to the local player in any other log lines that would confirm which of these lines belong to the local player.

Ironically this actually is not an issue in Battle Stage at all, it's only in Main Stage when there is more than one player. Feel free to take a look yourself though, maybe I'm missing something.

@LocalH
Copy link
Contributor Author

LocalH commented Oct 15, 2024

I see what you're saying, I've been looking at logs a lot this morning. It's wild how piecemeal the logging seems to be in certain places, like with the game evaluator and how it only logs forgiven swings

@LeviSnoot
Copy link
Owner

LeviSnoot commented Oct 27, 2024

I've had a small breakthrough with this. The log writes two sets of lines that could be cross-referenced to help us determine the line of the local player.

To demonstrate, here are two logs. The first is me in a single player Main Stage session.

[2024.10.27-09.37.42:001][967]LogPilgrimGemBreakListener: UPilgrimGemBreakListener::Init: Setup for FortPlayerStateAthena_2147482644 using track EPilgrimTrackType::TrackDrum and Difficulty EPilgrimSongDifficulty::DifficultyExpert
[...]
[2024.10.27-09.37.44:024][ 77]LogPilgrimGemBreakListener: Broadcasting GemBreakStart for FortPlayerStateAthena_2147482644.  Local(1), Id(160), IsStart(1), IsEnd(0), StartAt(0.000000), Duration(15.000000), EndAt(15.000000). CurrentSong(MS:0.000000, Bar:0.000000)

Clearly, I am FortPlayerStateAthena_2147482644 in this scenario, we don't need to account for single player sessions, but this helps set the stage for where I'm going with this. Now, let's take a look at a fill lobby in Main Stage with four players total:

[2024.10.27-10.12.07:815][354]LogPilgrimGemBreakListener: UPilgrimGemBreakListener::Init: Setup for FortPlayerStateAthena_2147482641 using track EPilgrimTrackType::TrackBass and Difficulty EPilgrimSongDifficulty::DifficultyEasy
[2024.10.27-10.12.07:815][354]LogPilgrimGemBreakListener: UPilgrimGemBreakListener::Init: Setup for FortPlayerStateAthena_2147482644 using track EPilgrimTrackType::TrackBass and Difficulty EPilgrimSongDifficulty::DifficultyEasy
[2024.10.27-10.12.07:815][354]LogPilgrimGemBreakListener: UPilgrimGemBreakListener::Init: Setup for FortPlayerStateAthena_2147482642 using track EPilgrimTrackType::TrackGuitar and Difficulty EPilgrimSongDifficulty::DifficultyExpert
[2024.10.27-10.12.07:815][354]LogPilgrimGemBreakListener: UPilgrimGemBreakListener::Init: Setup for FortPlayerStateAthena_2147482640 using track EPilgrimTrackType::TrackBass and Difficulty EPilgrimSongDifficulty::DifficultyExpert
[...]
[2024.10.27-10.12.09:812][456]LogPilgrimGemBreakListener: Broadcasting GemBreakStart for FortPlayerStateAthena_2147482641.  Local(0), Id(497), IsStart(1), IsEnd(0), StartAt(0.000000), Duration(15.000000), EndAt(15.000000). CurrentSong(MS:0.000000, Bar:0.000000)
[2024.10.27-10.12.09:812][456]LogPilgrimGemBreakListener: Broadcasting GemBreakStart for FortPlayerStateAthena_2147482644.  Local(0), Id(497), IsStart(1), IsEnd(0), StartAt(0.000000), Duration(15.000000), EndAt(15.000000). CurrentSong(MS:0.000000, Bar:0.000000)
[2024.10.27-10.12.09:812][456]LogPilgrimGemBreakListener: Broadcasting GemBreakStart for FortPlayerStateAthena_2147482640.  Local(0), Id(542), IsStart(1), IsEnd(0), StartAt(0.000000), Duration(15.000000), EndAt(15.000000). CurrentSong(MS:0.000000, Bar:0.000000)

In this case, we can determine that I am FortPlayerStateAthena_2147482642 because I simply don't appear in the Broadcasting GemBreakStart line.

So I tried making a little test script to identify the player by the omission in Broadcasting GemBreakStart. Unfortunately, that didn't end up working because I later had a session where it was just me and one other player. Now, the log instead looks like this:

[2024.10.27-12.11.04:354][524]LogPilgrimGemBreakListener: UPilgrimGemBreakListener::Init: Setup for FortPlayerStateAthena_2147482644 using track EPilgrimTrackType::TrackGuitar and Difficulty EPilgrimSongDifficulty::DifficultyExpert
[2024.10.27-12.11.04:354][524]LogPilgrimGemBreakListener: UPilgrimGemBreakListener::Init: Setup for FortPlayerStateAthena_2147482645 using track EPilgrimTrackType::TrackBass and Difficulty EPilgrimSongDifficulty::DifficultyHard
[...]
[2024.10.27-12.11.06:367][640]LogPilgrimGemBreakListener: Broadcasting GemBreakStart for FortPlayerStateAthena_2147482644.  Local(1), Id(535), IsStart(1), IsEnd(0), StartAt(0.000000), Duration(22.000000), EndAt(22.000000). CurrentSong(MS:0.000000, Bar:0.000000)
[2024.10.27-12.11.06:367][640]LogPilgrimGemBreakListener: Broadcasting GemBreakStart for FortPlayerStateAthena_2147482645.  Local(0), Id(525), IsStart(1), IsEnd(0), StartAt(0.000000), Duration(6.000000), EndAt(6.000000). CurrentSong(MS:0.000000, Bar:0.000000)

This time it DOES print me in Broadcasting GemBreakStart. So the behaviour is inconsistent, but we should be able to extrapolate what we want, but we need to handle a few different cases.

I ended up writing a little diagnostic script but it doesn't work as I had intended just yet, but may serve as a good starting point for anyone wanting to help me investigate. I'll include that below:

identify_local_player.py

import re
import os
import time

def identify_local_player(log_file_path):
    init_pattern = re.compile(r'LogPilgrimGemBreakListener: UPilgrimGemBreakListener::Init: Setup for (FortPlayerStateAthena_\d+) using track (EPilgrimTrackType::Track\w+) and Difficulty (EPilgrimSongDifficulty::Difficulty\w+)')
    gem_break_pattern = re.compile(r'LogPilgrimGemBreakListener: Broadcasting GemBreakStart for (FortPlayerStateAthena_\d+)\.  Local\((\d)\)')

    player_info = {}
    gem_break_ids = set()
    local_player_id = None
    local_player_identified = False

    with open(log_file_path, 'r', encoding='utf-8-sig') as log_file:
        log_file.seek(0, os.SEEK_END)

        while True:
            line = log_file.readline()
            if not line:
                time.sleep(0.1)
                continue

            init_match = init_pattern.search(line)
            if init_match:
                player_id, track, difficulty = init_match.groups()
                player_info[player_id] = {'track': track, 'difficulty': difficulty}
                local_player_identified = False

            gem_break_match = gem_break_pattern.search(line)
            if gem_break_match:
                player_id, is_local = gem_break_match.groups()
                gem_break_ids.add(player_id)
                if is_local == '1':
                    local_player_id = player_id

            if not local_player_identified:
                if local_player_id is None:
                    # Determine the local player by finding the missing identifier
                    all_player_ids = set(player_info.keys())
                    missing_ids = all_player_ids - gem_break_ids
                    if len(missing_ids) == 1:
                        local_player_id = missing_ids.pop()

                if local_player_id and local_player_id in player_info:
                    local_player_info = player_info[local_player_id]
                    session_type = "Single Player" if len(player_info) == 1 else "Multiplayer"
                    print(f"Local player ID: {local_player_id}")
                    print(f"Instrument: {local_player_info['track']}")
                    print(f"Difficulty: {local_player_info['difficulty']}")
                    print(f"Session Type: {session_type}")
                    print("-" * 40)  # Separator line
                    local_player_identified = True

            if local_player_identified and len(player_info) > len(gem_break_ids):
                player_info.clear()
                gem_break_ids.clear()
                local_player_id = None
                local_player_identified = False

if __name__ == "__main__":
    log_file_path = os.path.expandvars(r'%localappdata%\FortniteGame\Saved\Logs\FortniteGame.log')
    identify_local_player(log_file_path)

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

No branches or pull requests

2 participants