update this to use the new globalballpred and other things i ask void RLBotBot::RenderGlobalBallPred(const RLGSC::GameState& gs) {
    if (!renderBallPredictionsEnabled) return;
    rlbot::ScopedRenderer renderer("global_ballpred_renderer");

    BallPredEnv* ballPredEnv = BallPred::GetOrMake(predictionArena);
    if (!ballPredEnv) return;

    BallPred::Update(predictionArena);

    constexpr float MAX_PRED_TIME = 2.0f;  // 6 seconds prediction
    constexpr float TIME_STEP = 1.0f / 60.0f;  // 60 fps
    constexpr int NUM_POINTS = static_cast<int>(MAX_PRED_TIME / TIME_STEP) + 1;

    std::vector<rlbot::flat::Vector3> points(NUM_POINTS);

    for (int i = 0; i < NUM_POINTS; i++) {
        float t = i * TIME_STEP;
        PhysObj ballState = ballPredEnv->GetAtTime(t, gs.ball);
        points[i] = rlbot::flat::Vector3{
            static_cast<float>(ballState.pos.x),
            static_cast<float>(ballState.pos.y),
            static_cast<float>(ballState.pos.z)
        };
    }

    // Draw lines between consecutive points
    for (int i = 0; i < NUM_POINTS - 1; i++) {
        renderer.DrawLine3D(rlbot::Color::yellow, points[i], points[i + 1]);
    }

    // Draw a green dot at the end of the prediction line
    if (!points.empty()) {
        renderer.DrawRect3D(
            rlbot::Color::green,
            points.back(),
            10,
            10,
            true,
            true
        );
    }
} #include <functional>
#include <vector>
#include <memory>
#include <mutex>

// Include necessary project-specific headers
#include "C:\Epsilon\RLGymPPO_CPP-main\RLGymPPO_CPP\RLGymSim_CPP\RocketSim\src\Sim\Arena\Arena.h"
#include "C:\Epsilon\RLGymPPO_CPP-main\RLGymPPO_CPP\RLGymSim_CPP\src\RLGymSim_CPP\Utils\GlobalBallPred\BallPredTrackerManager.h"
#include "C:\Epsilon\RLGymPPO_CPP-main\RLGymPPO_CPP\RLGymSim_CPP\src\RLGymSim_CPP\Utils\Gamestates\PhysObj.h"

#include <mutex>
#include <unordered_map>


RS_NS_START

class BallPred {
private:
    std::unordered_map<const Arena*, std::unique_ptr<BallPredTracker>> predTrackers;
    mutable std::mutex mtx;
    const Arena* currentArena = nullptr;

    BallPred() = default;
    BallPred(const BallPred&) = delete;
    BallPred& operator=(const BallPred&) = delete;

public:
    static BallPred& getInstance() {
        static BallPred instance;
        return instance;
    }

    void Update(const Arena* arena);
    PhysObj GetAtTime(float time) const;
    void SetCurrentArena(const Arena* arena);
};

RS_NS_END // GlobalBallPred.cpp
#include "GlobalBallPred.h"

RS_NS_START

void BallPred::Update(const Arena* arena) {
    std::lock_guard<std::mutex> lock(mtx);
    auto it = predTrackers.find(arena);
    if (it == predTrackers.end()) {
        predTrackers[arena] = std::make_unique<BallPredTracker>(const_cast<Arena*>(arena), 240);
    }
    predTrackers[arena]->UpdatePredFromArena(const_cast<Arena*>(arena));
    SetCurrentArena(arena);
}

PhysObj BallPred::GetAtTime(float time) const {
    std::lock_guard<std::mutex> lock(mtx);
    if (currentArena && predTrackers.count(currentArena) > 0) {
        return PhysObj(predTrackers.at(currentArena)->GetBallStateForTime(time));
    }
    // Return a default PhysObj if no prediction is available
    return PhysObj();
}

void BallPred::SetCurrentArena(const Arena* arena) {
    currentArena = arena;
}

RS_NS_ENDyou should specify the intervalyou should find the closest reach margin with like 0.5s interval
then check neighboring times at 0.25s
then at 0.125s
etc
until you find the best tick
like a trinary search