Finding nearby players via logs

23/09/2023

Rust

I’ve been playing FiveM for quite a while now but recently I noticed something in the game’s console. It looked something like this:


MainThrd/ Creating physical player 126 (boris)


and


MainThrd/ Processing removal for player 126 (boris)
MainThrd/ reassigning ped: 0000025fa238b850 2450
MainThrd/ deleted object id
MainThrd/ success! reassigned the ped!


Now this was interesting to me as I recognised that these logs might be caused by players entering and exiting my vicinity. After doing some more research I found I was correct. Additionally, I found that these logs in the console of the game were written to a log file.


Log watching

I had been looking for an opportunity to learn Rust, and this seemed like the perfect challenge.


After setting up a new Rust project, I started coding. It was definitely more challenging than I initially anticipated.

I used the hotwatch crate to watch the log file for any updates, however, I encountered a significant performance issue as I was reading the entire file every time hotwatch detected a change, leading to substantial lag. Eventually, I discovered the File.seek method.


Here’s what the code looks like (please bear in mind that this is my first Rust project):


let mut watcher = Hotwatch::new()?;

// Watch the log_file for changes
watcher.watch(log_file, move |event: Event| {
    // Read the file metadata
    if let Ok(metadata) = fs::metadata(&event.paths[0]) {
        let new_file_size = metadata.len();

        if new_file_size > last_file_size {
            let file = fs::File::open(&event.paths[0]);
            if let Ok(mut file) = file {
                // Read the file contents from last_file_size and onwards
                if file.seek(SeekFrom::Start(last_file_size)).is_ok() {
                    let mut new_contents = String::new();

                    if file.read_to_string(&mut new_contents).is_ok() {
                        last_file_size = new_file_size;

                        // Handle the new lines in the file
                    }
                }
            }
        }
    }

    Flow::Continue
})?;

watcher.run();

The UI

Creating the user interface turned out to be more challenging than expected because I wanted a transparent window with passthrough that I could overlay on my game. I found egui and egui_overlay for this.


Then in the case that there are more than 25 players nearby, I made use of multiple columns to display them. I do realize that this isn’t the best solution as if the window is too small for 25 players to be listed below each other, some players get cropped off before switching to another column, but this wasn’t a big enough issue to try to fix.


I also used the hotkey crate to make the overlay’s windowed mode toggleable so that users could reposition the window over their game.


Automatic builds with GitHub Actions

I wanted to try to setup automatic building using GitHub Actions and eventually landed on this, which seems to work:


name: Release Rust Binary

on:
  push:
    tags:
      - 'v*.*.*'

jobs:
  build-win:
    runs-on: windows-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v1

      - name: Install latest rust toolchain
        uses: actions-rs/toolchain@v1
        with:
          toolchain: stable
          default: true
          override: true

      - name: Build
        run: cargo build --all --release

      - name: Release
        uses: softprops/action-gh-release@v1
        if: startsWith(github.ref, 'refs/tags/')
        with:
          files: target/release/fivem-player-proximity.exe
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Final notes

I don’t recommend actually using this program as most servers see it as cheating because you’re not supposed to know who’s nearby, and this project was just a fun experiment to try and learn Rust.

That being said if you do want to try it out or want to read all the code, check out the repository

Copyright © 2023-2024 boris.foo, All rights reserved.