Files
InstanceBrick--nightcap/include/runtime/roblox_fresh_game_launch_controller.h
2026-08-16 04:19:32 +03:00

118 lines
4.6 KiB
C++

#ifndef MOCKTAIL_RUNTIME_ROBLOX_FRESH_GAME_LAUNCH_CONTROLLER_H_
#define MOCKTAIL_RUNTIME_ROBLOX_FRESH_GAME_LAUNCH_CONTROLLER_H_
#include <jni.h>
#include <cstdint>
#include <memory>
#include <mutex>
#include <string>
#include "mocktail/status.h"
#include "runtime/roblox_experience_launch_bridge.h"
#include "runtime/roblox_game_session_native_adapter.h"
namespace mocktail {
namespace runtime {
// Borrowed LuaApp JNI objects plus owned identity/surface values needed
// to create one fresh UGCGame session. All borrowed JNI references need only
// remain valid for the synchronous Launch call; RobloxGameSessionRuntime
// promotes the complete binding to global references before native start.
struct RobloxFreshGameLaunchContext {
uint64_t request_id = 0;
jclass native_gl_class = nullptr;
jobject surface = nullptr;
jobject platform_params = nullptr;
jobject activity = nullptr;
jobject device_params = nullptr;
jobject vr_context = nullptr;
GameSessionPrincipal principal;
GameSurface game_surface;
std::string username;
bool is_under_13 = false;
int32_t join_request_type = -1;
bool complete() const;
};
using FreshLaunchPresentObserver = void (*)(void* context,
uint64_t frame_serial);
using RegisterFreshLaunchPresentObserverFn = bool (*)(
void* context, FreshLaunchPresentObserver observer, void* observer_context);
using ClearFreshLaunchPresentObserverFn = void (*)(void* context);
// Production composition adapts this to window::ScopedPresentObserver.
// clear_observer must wait for an in-flight callback before returning.
// Boundary callbacks must not re-enter this controller.
struct RobloxFreshLaunchPresentBoundary {
void* context = nullptr;
RegisterFreshLaunchPresentObserverFn register_observer = nullptr;
ClearFreshLaunchPresentObserverFn clear_observer = nullptr;
bool complete() const {
return register_observer != nullptr && clear_observer != nullptr;
}
};
// Builds a new local StartGameParams JNI object from the request. No field is
// read from environment variables or from a previous game session. Caller
// owns the returned local reference.
Status BuildFreshStartGameParams(JNIEnv* env,
const RobloxExperienceLaunchRequest& request,
const RobloxFreshGameLaunchContext& context,
jobject* result);
// Owns one LuaApp -> UGCGame transition. Observer registration precedes native
// start so a host present emitted during start is retained. Leave returns to
// LuaApp and releases only per-session JNI references, allowing another fresh
// launch. Shutdown is terminal and destroys an active app-level native runtime.
class RobloxFreshGameLaunchController final {
public:
RobloxFreshGameLaunchController(
JniEnvironmentProvider environment, RobloxGameSessionSymbols symbols,
RobloxFreshLaunchPresentBoundary present_boundary,
RobloxGameSurfaceJniConfig surface_config = {},
RobloxGamePresentedObserver presented_observer = {});
~RobloxFreshGameLaunchController();
RobloxFreshGameLaunchController(const RobloxFreshGameLaunchController&) =
delete;
RobloxFreshGameLaunchController& operator=(
const RobloxFreshGameLaunchController&) = delete;
Status Launch(const RobloxExperienceLaunchRequest& request,
const RobloxFreshGameLaunchContext& context);
Status Leave();
// Records an already-completed native UGCGame -> LuaApp transition. Unlike
// Leave(), this must not invoke native lifecycle callbacks because Roblox
// has performed them before publishing the LuaApp stage observation.
Status ObserveLuaAppReturn();
Status Shutdown();
GameSessionUpdateResult SurfaceCreated(uint64_t surface_generation);
GameSessionUpdateResult SurfaceChanged(GameSurface surface);
GameSessionUpdateResult RebindSurface(GameSurface surface);
GameSessionUpdateResult SurfaceDestroyed(uint64_t surface_generation);
bool active() const;
GameSessionSnapshot Snapshot() const;
private:
const JniEnvironmentProvider environment_;
const RobloxGameSessionSymbols symbols_;
const RobloxFreshLaunchPresentBoundary present_boundary_;
const RobloxGameSurfaceJniConfig surface_config_;
const RobloxGamePresentedObserver presented_observer_;
mutable std::mutex mutex_;
std::unique_ptr<RobloxGameSessionRuntime> runtime_;
GameSessionSnapshot last_snapshot_;
bool observer_registered_ = false;
bool active_ = false;
bool app_shutdown_ = false;
};
} // namespace runtime
} // namespace mocktail
#endif // MOCKTAIL_RUNTIME_ROBLOX_FRESH_GAME_LAUNCH_CONTROLLER_H_