Volver al Blog

Marathon Input Issues Fix: Arquitectura de juegos de PC frente a conflictos de Overlay

Publicado el 28 de febrero de 2026
Marathon Input Issues Fix: Arquitectura de juegos de PC frente a conflictos de Overlay

Todo desarrollador de multiplayer competitivo conoce esa sensación de ver cómo un netcode perfectamente diseñado se rompe por culpa de un overlay de Discord. Pasas meses optimizando la infraestructura de servidores para lograr una latencia inferior a 30ms, solo para descubrir que los jugadores fallan sus disparos porque una aplicación de streaming en segundo plano está asfixiando el hilo de input del juego.

Este escenario exacto se está dando en el Server Slam de Marathon, el próximo extraction shooter de Bungie. Los jugadores de PC reportan un grave mouse lag, pérdida de inputs y movimientos de cámara lentos. Bungie ha reconocido oficialmente el problema, señalando directamente a utilidades de terceros, overlays de streamers y métodos específicos de captura de ventana como los principales culpables.

Si monitorizas los foros de la comunidad, verás a miles de jugadores buscando desesperadamente un marathon input issues fix. La solución temporal para el usuario es simple: transmitir el monitor completo en lugar de capturar la ventana del juego y desactivar los overlays.

Pero para los desarrolladores, esto plantea una pregunta arquitectónica crítica: ¿Por qué las apps de streaming y overlays secuestran los inputs del juego y cómo podemos diseñar nuestros clientes de PC para evitarlo?

En este análisis técnico, analizaremos la gestión de input en Windows, por qué OBS Game Capture causa problemas de frame pacing y cómo desacoplar los hilos de input para sobrevivir en entornos de PC hostiles.

La causa raíz: Cómo los Overlays secuestran el Render Pipeline

Para entender por qué una app de streaming arruina el mouse input, debemos ver cómo llegan los juegos de PC a la pantalla.

Cuando se usa OBS o Discord, estas aplicaciones inyectan una DLL directamente en el proceso del juego. Esta DLL intercepta las llamadas a la API gráfica, haciendo un hook en la función Present de DirectX o vkQueuePresentKHR en Vulkan.

El cuello de botella del Swap Chain

Al hacer hook en el swap chain, el overlay obliga al motor a esperar mientras copia el frame buffer a su propia memoria antes de permitir que el frame se presente en el monitor.

Si la CPU o GPU están cargadas por el encoder de streaming, esta operación se detiene. Un frame que debería tardar 16.6ms (a 60 FPS) tarda de repente 24ms. Como la mayoría de motores consultan el input estándar de Windows (WM_MOUSEMOVE) en el hilo principal justo antes del render tick, retrasar el render tick retrasa inherentemente el input polling.

Tu ratón gaming de 1000Hz empieza a ser consultado en intervalos erráticos, lo que el jugador percibe como un movimiento flotante o pesado.

Fullscreen vs. Windowed Capture

Bungie notó que los problemas ocurren principalmente al capturar la ventana directamente. Esto se debe a cómo el Windows Desktop Window Manager (DWM) gestiona la composición.

En Exclusive Fullscreen o usando el moderno DXGI Flip Model, se omite la composición del DWM. Sin embargo, cuando una app de streaming fuerza un hook de captura de ventana, a menudo devuelve al juego a un estado composited, añadiendo de 1 a 3 frames de latencia (16ms a 48ms) de input delay.

Arquitectura de Input imbatible: Raw Input API

Para inmunizar tu juego contra el lag de los overlays, debes abandonar los message pumps estándar de Windows para inputs críticos. Confiar en WM_MOUSEMOVE es una sentencia de muerte para los shooters competitivos.

En su lugar, implementa la Raw Input API (WM_INPUT) y mueve el input polling a un hilo dedicado de alta prioridad desacoplado del render loop.

Implementando Raw Input en C++ (Win32)

Así es como se omiten las colas estándar para registrar datos raw del hardware directamente:

// Inicialización de Raw Input en C++ Win32
void InitializeRawInput(HWND hwnd) {
    RAWINPUTDEVICE Rid[1];
    Rid[0].usUsagePage = 0x01; 
    Rid[0].usUsage = 0x02; 
    Rid[0].dwFlags = 0;   
    Rid[0].hwndTarget = hwnd;

    if (RegisterRawInputDevices(Rid, 1, sizeof(Rid[0])) == FALSE) {
        DWORD error = GetLastError();
        LogWarning("Error al registrar raw input device: %d", error);
    }
}

// Dentro de tu WndProc
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    switch (uMsg) {
        case WM_INPUT: {
            UINT dwSize = 0;
            GetRawInputData((HRAWINPUT)lParam, RID_INPUT, NULL, &dwSize, sizeof(RAWINPUTHEADER));
            if (dwSize == 0) break;
            LPBYTE lpb = new BYTE[dwSize];
            GetRawInputData((HRAWINPUT)lParam, RID_INPUT, lpb, &dwSize, sizeof(RAWINPUTHEADER));
            RAWINPUT* raw = (RAWINPUT*)lpb;
            if (raw->header.dwType == RIM_TYPEMOUSE) {
                int deltaX = raw->data.mouse.lLastX;
                int deltaY = raw->data.mouse.lLastY;
                InputManager::GetInstance().AccumulateMouseDelta(deltaX, deltaY);
            }
            delete[] lpb;
            return DefWindowProc(hwnd, uMsg, wParam, lParam);
        }
    }
    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

Desarrolladores de Unreal Engine: Desactivar Smoothing

Unreal gestiona Raw Input internamente, pero suele aplicar curvas de suavizado que empeoran el input lag. Desactívalo en el PlayerController:

void ACompetitivePlayerController::SetupInputComponent() {
    Super::SetupInputComponent();
    bEnableMouseSmoothing = false;
    LocalPlayer->AspectRatioAxisConstraint = AspectRatio_MaintainYFOV;
}

Efecto Cascada: Client Lag vs. Server Rollback

El input lag afecta a la arquitectura de servidor autoritativo. Cuando un overlay detiene el loop del cliente, los paquetes de input llegan tarde al servidor. El servidor debe entonces adelantar la simulación para ponerse al día. Si el retraso es grave, la lógica de reconciliation fuerza una corrección brusca (rubberbanding), similar a lo descrito en How To Fix Player Location Desync In Uefn And Unreal Engine Multiplayer.

Gestión en el Servidor

El servidor necesita un buffer de input para absorber el jitter del cliente.

void ServerProcessClientInputs(ClientConnection* client) {
    const int TARGET_BUFFER_SIZE = 2; 
    int currentBufferSize = client->InputQueue.Size();
    if (currentBufferSize > TARGET_BUFFER_SIZE + 3) {
        client->InputQueue.DropOldest(currentBufferSize - TARGET_BUFFER_SIZE);
    }
    if (!client->InputQueue.IsEmpty()) {
        InputCommand cmd = client->InputQueue.Pop();
        SimulatePlayerMovement(client->PlayerEntity, cmd);
    }
}

Usando horizOn, puedes desplegar servidores optimizados con protección contra desync, permitiendo que tu equipo se centre en el loop del cliente.

5 Buenas Prácticas para Input en PC

  1. Desacoplar Input de Rendering: Usa un hilo de alta prioridad a 1000Hz.
  2. Forzar DXGI Flip Model: Usa DXGI_SWAP_EFFECT_FLIP_DISCARD para omitir el DWM.
  3. Implementar Hardware Cursor: Usa SetCursor del SO en lugar de quads texturizados.
  4. Advertir sobre Hook Injections: Detecta DLLs de overlays y avisa al jugador.
  5. Usar Fixed Timestep Simulation: Vincula física e input a un paso de tiempo fijo.

Conclusión

El Server Slam de Marathon recuerda lo hostil que es el ecosistema de PC. Mientras los jugadores buscan un marathon input issues fix, los desarrolladores deben ser proactivos implementando Raw Input y desacoplando hilos. Para el backend, confía en horizOn para gestionar la orquestación de servidores y el desync.