#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <time.h>
#include <winsock2.h>
#include <windows.h>
#include <ws2tcpip.h>
#include <stdlib.h>
#include <vector>
#include <functional>

#pragma comment(lib, "ws2_32.lib")

// Atomic helpers
static inline LONG AtomicRead(LONG* val) { return InterlockedCompareExchange(val, 0, 0); }
static inline void AtomicWrite(LONG* val, LONG newVal) { InterlockedExchange(val, newVal); }

// Async callback queue
static std::vector<std::function<void()>> g_callbackQueue;
static CRITICAL_SECTION g_callbackQueueCs;

static void InitCallbackQueue() {
    InitializeCriticalSection(&g_callbackQueueCs);
}

static void ShutdownCallbackQueue() {
    DeleteCriticalSection(&g_callbackQueueCs);
}

static void QueueCallback(std::function<void()> callback) {
    EnterCriticalSection(&g_callbackQueueCs);
    g_callbackQueue.push_back(std::move(callback));
    LeaveCriticalSection(&g_callbackQueueCs);
}

static void DrainCallbackQueue() {
    EnterCriticalSection(&g_callbackQueueCs);
    std::vector<std::function<void()>> localQueue;
    localQueue.swap(g_callbackQueue);
    LeaveCriticalSection(&g_callbackQueueCs);

    for (auto& cb : localQueue) {
        if (cb) cb();
    }
}

// logging system
FILE *logFile = NULL;
FILE *packetFile = NULL;
FILE *telemetryFile = NULL;
static CRITICAL_SECTION g_packetCs;

void InitLogFiles();
void CloseLogFiles();

void Log(const char *format, ...) {
  if (!logFile) return;
  va_list args;
  va_start(args, format);
  SYSTEMTIME st;
  GetLocalTime(&st);
  fprintf(logFile, "[%02u:%02u:%02u.%03u] ", st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);
  vfprintf(logFile, format, args);
  fprintf(logFile, "\n");
  va_end(args);
}

void LogTelemetry(const char *format, ...) {
  va_list args;
  va_start(args, format);
  SYSTEMTIME st;
  GetLocalTime(&st);
  if (logFile) {
    fprintf(logFile, "[%02u:%02u:%02u.%03u] ", st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);
    vfprintf(logFile, format, args);
    fprintf(logFile, "\n");
  }
  va_end(args);
  va_start(args, format);
  if (telemetryFile) {
    fprintf(telemetryFile, "[%02u:%02u:%02u.%03u] ", st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);
    vfprintf(telemetryFile, format, args);
    fprintf(telemetryFile, "\n");
    fflush(telemetryFile);
  }
  va_end(args);
}

static void HexDump(const void *data, size_t len, char *out) {
  const unsigned char *p = (const unsigned char *)data;
  for (size_t i = 0; i < len; ++i) sprintf(out + i * 3, "%02X ", p[i]);
  if (len > 0) out[len * 3 - 1] = '\0';
}

typedef USHORT(WINAPI *RtlCaptureStackBackTraceFn)(ULONG, ULONG, PVOID *, PULONG);
static RtlCaptureStackBackTraceFn pRtlCaptureStackBackTrace = nullptr;

static void LogCallStack(const char *context) {
  if (!pRtlCaptureStackBackTrace) {
    HMODULE ntdll = GetModuleHandleA("ntdll.dll");
    if (ntdll) {
      pRtlCaptureStackBackTrace =
          (RtlCaptureStackBackTraceFn)GetProcAddress(ntdll, "RtlCaptureStackBackTrace");
    }
    if (!pRtlCaptureStackBackTrace) {
      Log("[Stack/%s] !! RtlCaptureStackBackTrace unavailable", context);
      return;
    }
  }

  void *frames[16] = {0};
  USHORT count = pRtlCaptureStackBackTrace(1, 16, frames, nullptr);

  char buf[2048] = {0};
  int pos = 0;
  for (USHORT i = 0; i < count && pos < (int)sizeof(buf) - 128; i++) {
    HMODULE mod = nullptr;
    if (GetModuleHandleExA(0x4 /*FROM_ADDRESS*/ | 0x2 /*UNCHANGED_REFCOUNT*/,
                            (LPCSTR)frames[i], &mod) && mod) {
      char modPath[MAX_PATH] = {0};
      GetModuleFileNameA(mod, modPath, MAX_PATH);
      const char *modName = strrchr(modPath, '\\');
      modName = modName ? modName + 1 : modPath;
      pos += sprintf_s(buf + pos, sizeof(buf) - pos, "%s+0x%llx  ", modName,
                        (unsigned long long)((uintptr_t)frames[i] - (uintptr_t)mod));
    } else {
      pos += sprintf_s(buf + pos, sizeof(buf) - pos, "0x%p  ", frames[i]);
    }
  }
  Log("[Stack/%s] %s", context, buf);
}

static bool StackContainsModule(const char *targetModule) {
  if (!pRtlCaptureStackBackTrace) {
    HMODULE ntdll = GetModuleHandleA("ntdll.dll");
    if (ntdll) {
      pRtlCaptureStackBackTrace =
          (RtlCaptureStackBackTraceFn)GetProcAddress(ntdll, "RtlCaptureStackBackTrace");
    }
    if (!pRtlCaptureStackBackTrace) return true;
  }
  void *frames[16] = {0};
  USHORT count = pRtlCaptureStackBackTrace(1, 16, frames, nullptr);
  for (USHORT i = 0; i < count; i++) {
    HMODULE mod = nullptr;
    if (GetModuleHandleExA(0x4 /*FROM_ADDRESS*/ | 0x2 /*UNCHANGED_REFCOUNT*/,
                            (LPCSTR)frames[i], &mod) && mod) {
      char modPath[MAX_PATH] = {0};
      GetModuleFileNameA(mod, modPath, MAX_PATH);
      const char *modName = strrchr(modPath, '\\');
      modName = modName ? modName + 1 : modPath;
      if (_stricmp(modName, targetModule) == 0) return true;
    }
  }
  return false;
}

static void AsciiDump(const void *data, size_t len, char *out) {
  const unsigned char *p = (const unsigned char *)data;
  for (size_t i = 0; i < len; ++i) out[i] = (p[i] >= 32 && p[i] < 127) ? (char)p[i] : '.';
  out[len] = '\0';
}

void DumpPacket(const char *direction, const void *data, uint32_t len,
                const struct sockaddr_in *addr, uint8_t channel) {
  if (!packetFile || !data || len == 0) return;
  EnterCriticalSection(&g_packetCs);
  SYSTEMTIME st;
  GetLocalTime(&st);
  fprintf(packetFile, "\n========== PACKET %s [%02u:%02u:%02u.%03u] ==========\n",
          direction, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);
  if (addr) fprintf(packetFile, "Peer: %s:%d  ", inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
  fprintf(packetFile, "Size: %u bytes  Channel: %u\n", len, channel);
  const unsigned char *p = (const unsigned char *)data;
  for (uint32_t offset = 0; offset < len; offset += 16) {
    uint32_t lineLen = (len - offset) > 16 ? 16 : (len - offset);
    char hex[16 * 3 + 1] = {0};
    char ascii[16 + 1] = {0};
    HexDump(p + offset, lineLen, hex);
    AsciiDump(p + offset, lineLen, ascii);
    fprintf(packetFile, "%04X  %-48s  %s\n", offset, hex, ascii);
  }
  fprintf(packetFile, "==================================================\n");
  fflush(packetFile);
  LeaveCriticalSection(&g_packetCs);
}

// Forward declarations
void InitNetwork();
void LogStateSnapshot(const char *context);
void ResetConnectionState();
void FirePeerConnectionEstablished(void* remotePeerId);
void FirePeerConnectionRequest(void* remotePeerId);
void FireLobbyMemberJoined(void* memberId);
void HostOnFirstPacketReceived(const struct sockaddr_in* from);
void SendHelloAck(const struct sockaddr_in* to);
void TryFireHostEvents(void* clientId);
void TickRetryPendingEvents();

static void SendHelloAckLocked(const struct sockaddr_in* to);
static void HostOnFirstPacketReceivedNoLock(const struct sockaddr_in* from);
static bool DiscardMitmHelloIfPresentLocked();

// Network State
SOCKET udpSocket = INVALID_SOCKET;
struct sockaddr_in targetAddr;
static LONG g_isHost = 0;
static LONG g_initialized = 0;
char g_dllDir[MAX_PATH] = {0};
char g_searchAppVersion[64] = "1.2.0";
char g_searchOnlineRule[64] = "Koikoi";
char g_lobbyAppVersion[64] = "1.2.0";
char g_lobbyOnlineRule[64] = "Koikoi";
char g_lobbyPlatform[32] = "Steam";
char g_lobbyEosProductId[64] = "DIRECTIP_HOST_PUID";
int64_t g_lobbyWinRate = 10;
bool g_lobbyIsCrossPlatform = true;
static LONG g_clientSawSearchResult = 0;
static uint32_t g_maxMembers = 4;
const char *kMitmHello = "EOSMITM_HELLO";
static const char *kMitmAck   = "EOSMITM_ACK";

// Per-session log throttle counters (reset in ResetConnectionState)
static LONG g_sendLogCount = 0;
static LONG g_receiveLogCount = 0;
static LONG g_sizeLogCount = 0;
static LONG g_copyInfoLogCount = 0;
static LONG g_ownerLogCount = 0;
static LONG g_memberByIndexLogCount = 0;
static LONG g_memberCountLogCount = 0;
static LONG g_memberInfoLogCount = 0;

static LONG g_peerConnected = 0;
static LONG g_lobbyHasRemoteMember = 0;
static LONG g_hostFiredEvents = 0;
static LONG g_helloAckReceived = 0;
static LONG g_hostHasClientAddr = 0;
static LONG g_establishedFired = 0;
static LONG g_lobbyMemberJoinedFired = 0;
static LONG g_connectionRequestFired = 0;
static LONG g_initLock = 0;

typedef void (*EOS_Connect_OnLoginStatusChangedCallback)(const void* Data);
static EOS_Connect_OnLoginStatusChangedCallback g_loginStatusChangedCb = nullptr;
static void* g_loginStatusChangedCbData = nullptr;

static CRITICAL_SECTION g_hostEventCs;
static CRITICAL_SECTION g_socketCs;

static HANDLE g_stopEvent = NULL;
static HANDLE g_peerWatchThread = NULL;
static uint64_t g_notifIdCounter = 10;
static LONG g_watchThreadGeneration = 0;

// Rate limiting for ResetConnectionState
static DWORD g_lastResetTime = 0;
static volatile DWORD g_lastGoodPacketTime = 0; // GetTickCount() of last successful send/recv, for CloseConnections health-gating bodge
static LONG g_resetCount = 0;

static LONG g_resetInProgress = 0;

static const DWORD kAutoTeardownSuppressWindowMs = 100;
static LONG g_autoTeardownSuppressed = 0;
static volatile DWORD g_autoTeardownSuppressTime = 0;

// Telemetry blocking config
static bool g_telemetryBlock = false;
static bool g_dumpTraffic = false;
static char g_blockedDomains[1024] = "api.epicgames.dev";
static char g_blockedIPs[1024] = "104.18.125.108";

void LogStateSnapshot(const char *context) {
  Log("[State] %s | isHost=%d peerConnected=%d lobbyHasRemoteMember=%d initialized=%d clientSawSearchResult=%d",
      context ? context : "???", (int)AtomicRead(&g_isHost), (int)AtomicRead(&g_peerConnected),
      (int)AtomicRead(&g_lobbyHasRemoteMember), (int)AtomicRead(&g_initialized),
      (int)AtomicRead(&g_clientSawSearchResult));
}

// EOS Types Mock
typedef int32_t EOS_EResult;
#define EOS_Success 0
#define EOS_NotFound 8
#define EOS_InvalidParameters 4

typedef void *EOS_HP2P;
typedef void *EOS_ProductUserId;

struct EOS_P2P_SocketId {
    int32_t ApiVersion;
    char SocketName[33];
};
static EOS_P2P_SocketId g_socketId = { 2, "DirectIP" };

static EOS_ProductUserId HostProductUserId() { return (EOS_ProductUserId)1; }
static EOS_ProductUserId ClientProductUserId() { return (EOS_ProductUserId)2; }
static EOS_ProductUserId LocalProductUserId() {
  InitNetwork();
  return AtomicRead(&g_isHost) ? HostProductUserId() : ClientProductUserId();
}
static EOS_ProductUserId RemoteProductUserId() {
  InitNetwork();
  return AtomicRead(&g_isHost) ? ClientProductUserId() : HostProductUserId();
}

// P2P structs
struct EOS_P2P_SendPacketOptions {
  int32_t ApiVersion;
  EOS_ProductUserId LocalUserId;
  EOS_ProductUserId RemoteUserId;
  const void *SocketId;
  uint8_t Channel;
  uint32_t DataLengthBytes;
  const void *Data;
  bool bAllowDelayedDelivery;
  uint8_t Reliability;
  bool bDisableAutoAcceptConnection;
};

struct EOS_P2P_GetNextReceivedPacketSizeOptions {
  int32_t ApiVersion;
  EOS_ProductUserId LocalUserId;
  const uint8_t *RequestedChannel;
};

struct EOS_P2P_ReceivePacketOptions {
  int32_t ApiVersion;
  EOS_ProductUserId LocalUserId;
  uint32_t MaxDataSizeBytes;
  const uint8_t *RequestedChannel;
};

// Callback types
struct EOS_P2P_OnIncomingConnectionRequestInfo {
    void* ClientData;
    EOS_ProductUserId LocalUserId;
    EOS_ProductUserId RemoteUserId;
    const EOS_P2P_SocketId* SocketId;
};
typedef void (*EOS_P2P_OnIncomingConnectionRequestCallback)(const EOS_P2P_OnIncomingConnectionRequestInfo* Data);

struct EOS_P2P_OnPeerConnectionEstablishedInfo {
    void* ClientData;
    EOS_ProductUserId LocalUserId;
    EOS_ProductUserId RemoteUserId;
    const EOS_P2P_SocketId* SocketId;
    int32_t ConnectionType;
};
typedef void (*EOS_P2P_OnPeerConnectionEstablishedCallback)(const EOS_P2P_OnPeerConnectionEstablishedInfo* Data);

static EOS_P2P_OnIncomingConnectionRequestCallback g_connectionRequestCb = nullptr;
static void* g_connectionRequestCbData = nullptr;
static EOS_P2P_OnPeerConnectionEstablishedCallback g_connectionEstablishedCb = nullptr;
static void* g_connectionEstablishedCbData = nullptr;

struct EOS_Lobby_LobbyMemberStatusReceivedCallbackInfo {
    void* ClientData;
    const char* LobbyId;
    EOS_ProductUserId TargetUserId;
    int32_t CurrentStatus;
};
typedef void (*EOS_Lobby_OnLobbyMemberStatusReceivedCallback)(const EOS_Lobby_LobbyMemberStatusReceivedCallbackInfo* Data);

static EOS_Lobby_OnLobbyMemberStatusReceivedCallback g_memberStatusCb = nullptr;
static void* g_memberStatusCbData = nullptr;

struct EOS_Lobby_LobbyUpdateReceivedCallbackInfo {
    void* ClientData;
    const char* LobbyId;
};
typedef void (*EOS_Lobby_OnLobbyUpdateReceivedCallback)(const EOS_Lobby_LobbyUpdateReceivedCallbackInfo* Data);

struct EOS_Lobby_LobbyMemberUpdateReceivedCallbackInfo {
    void* ClientData;
    const char* LobbyId;
    EOS_ProductUserId TargetUserId;
};
typedef void (*EOS_Lobby_OnLobbyMemberUpdateReceivedCallback)(const EOS_Lobby_LobbyMemberUpdateReceivedCallbackInfo* Data);

static EOS_Lobby_OnLobbyUpdateReceivedCallback g_lobbyUpdateCb = nullptr;
static void* g_lobbyUpdateCbData = nullptr;
static EOS_Lobby_OnLobbyMemberUpdateReceivedCallback g_memberUpdateCb = nullptr;
static void* g_memberUpdateCbData = nullptr;

void ResetConnectionState() {
  DWORD now = GetTickCount();
  bool nothingToReset = (udpSocket == INVALID_SOCKET) &&
                         !g_peerWatchThread &&
                         AtomicRead(&g_initialized) == 0 &&
                         AtomicRead(&g_peerConnected) == 0 &&
                         AtomicRead(&g_lobbyHasRemoteMember) == 0;
  if (nothingToReset && (now - g_lastResetTime) < 50) {
      Log("[Net] ResetConnectionState: already clean, skipping redundant call");
      return;
  }
  g_lastResetTime = now;
  LogCallStack("ResetConnectionState");
  // Clear auto-teardown suppression: any real reset cancels the window
  AtomicWrite(&g_autoTeardownSuppressed, 0);
  AtomicWrite(&g_resetInProgress, 1);

  Log("[Net] Resetting connection state for new session");
  if (g_peerWatchThread) {
    InterlockedIncrement(&g_watchThreadGeneration);
    if (g_stopEvent) SetEvent(g_stopEvent);
    WaitForSingleObject(g_peerWatchThread, 50); // best-effort; not relied upon below
    EnterCriticalSection(&g_socketCs);
    CloseHandle(g_peerWatchThread);
    g_peerWatchThread = NULL;
    LeaveCriticalSection(&g_socketCs);
  }

  // Reset per-session log throttle counters so sessions 4+ remain visible.
  AtomicWrite(&g_sendLogCount, 0);
  AtomicWrite(&g_receiveLogCount, 0);
  AtomicWrite(&g_sizeLogCount, 0);
  AtomicWrite(&g_copyInfoLogCount, 0);
  AtomicWrite(&g_ownerLogCount, 0);
  AtomicWrite(&g_memberByIndexLogCount, 0);
  AtomicWrite(&g_memberCountLogCount, 0);
  AtomicWrite(&g_memberInfoLogCount, 0);

  EnterCriticalSection(&g_socketCs);
  if (udpSocket != INVALID_SOCKET) {
    closesocket(udpSocket);
    udpSocket = INVALID_SOCKET;
  }
  LeaveCriticalSection(&g_socketCs);

  AtomicWrite(&g_peerConnected, 0);
  AtomicWrite(&g_lobbyHasRemoteMember, 0);
  AtomicWrite(&g_hostFiredEvents, 0);
  AtomicWrite(&g_clientSawSearchResult, 0);
  AtomicWrite(&g_helloAckReceived, 0);
  AtomicWrite(&g_hostHasClientAddr, 0);
  AtomicWrite(&g_establishedFired, 0);
  AtomicWrite(&g_lobbyMemberJoinedFired, 0);
  AtomicWrite(&g_connectionRequestFired, 0);
  AtomicWrite(&g_initialized, 0);
  AtomicWrite(&g_isHost, 0);

  g_connectionRequestCb = nullptr;
  g_connectionRequestCbData = nullptr;
  g_connectionEstablishedCb = nullptr;
  g_connectionEstablishedCbData = nullptr;
  g_memberStatusCb = nullptr;
  g_memberStatusCbData = nullptr;
  g_lobbyUpdateCb = nullptr;
  g_lobbyUpdateCbData = nullptr;
  g_memberUpdateCb = nullptr;
  g_memberUpdateCbData = nullptr;

  AtomicWrite(&g_resetInProgress, 0);
}

void InitNetwork() {
  if (AtomicRead(&g_initialized)) return;
  if (InterlockedCompareExchange(&g_initLock, 1, 0) != 0) {
    while (!AtomicRead(&g_initialized)) Sleep(1);
    return;
  }
  if (AtomicRead(&g_initialized)) { InterlockedExchange(&g_initLock, 0); return; }

  WSADATA wsaData;
  int wsaErr = WSAStartup(MAKEWORD(2, 2), &wsaData);
  Log("[Net] WSAStartup result: %d", wsaErr);

  char iniPath[MAX_PATH];
  snprintf(iniPath, sizeof(iniPath), "%sdirectip.ini", g_dllDir);

  char role[32] = {0};
  char ip[64] = {0};
  char portStr[16] = {0};

  GetPrivateProfileStringA("Network", "Role", "Client", role, sizeof(role), iniPath);
  GetPrivateProfileStringA("Network", "IP", "127.0.0.1", ip, sizeof(ip), iniPath);
  GetPrivateProfileStringA("Network", "Port", "7777", portStr, sizeof(portStr), iniPath);

  int port = atoi(portStr);
  AtomicWrite(&g_isHost, (_stricmp(role, "Host") == 0) ? 1 : 0);

  EnterCriticalSection(&g_socketCs);
  udpSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
  if (udpSocket == INVALID_SOCKET) {
    Log("[Net] socket() failed: %d", WSAGetLastError());
    LeaveCriticalSection(&g_socketCs);
    InterlockedExchange(&g_initLock, 0);
    return;
  }

  // Allow rapid rebind to avoid WSAEADDRINUSE
  int reuse = 1;
  if (setsockopt(udpSocket, SOL_SOCKET, SO_REUSEADDR, (char*)&reuse, sizeof(reuse)) == SOCKET_ERROR) {
    Log("[Net] setsockopt(SO_REUSEADDR) failed: %d", WSAGetLastError());
  }

  u_long mode = 1;
  ioctlsocket(udpSocket, FIONBIO, &mode);

  struct sockaddr_in bindAddr = {0};
  bindAddr.sin_family = AF_INET;
  bindAddr.sin_addr.s_addr = INADDR_ANY;
  bindAddr.sin_port = htons(AtomicRead(&g_isHost) ? port : 0);

  int bindRes = bind(udpSocket, (struct sockaddr *)&bindAddr, sizeof(bindAddr));
  LeaveCriticalSection(&g_socketCs);

  Log("[Net] bind() result: %d (port %d)", bindRes, AtomicRead(&g_isHost) ? port : 0);

  if (bindRes != 0) {
    Log("[Net] bind() failed, aborting network init");
    EnterCriticalSection(&g_socketCs);
    if (udpSocket != INVALID_SOCKET) { closesocket(udpSocket); udpSocket = INVALID_SOCKET; }
    LeaveCriticalSection(&g_socketCs);
    InterlockedExchange(&g_initLock, 0);
    return;
  }

  targetAddr.sin_family = AF_INET;
  targetAddr.sin_port = htons(port);
  targetAddr.sin_addr.s_addr = inet_addr(ip);

  // Read optional lobby attributes from ini
  char maxMembersStr[16] = "4";
  GetPrivateProfileStringA("Network", "MaxMembers", "4", maxMembersStr, sizeof(maxMembersStr), iniPath);
  g_maxMembers = (uint32_t)atoi(maxMembersStr);

  char platformStr[32] = "Steam";
  GetPrivateProfileStringA("Network", "Platform", "Steam", platformStr, sizeof(platformStr), iniPath);
  strncpy(g_lobbyPlatform, platformStr, sizeof(g_lobbyPlatform) - 1);
  g_lobbyPlatform[sizeof(g_lobbyPlatform) - 1] = '\0';

  char winRateStr[32] = "10";
  GetPrivateProfileStringA("Network", "WinRate", "10", winRateStr, sizeof(winRateStr), iniPath);
  g_lobbyWinRate = _atoi64(winRateStr);

  char crossStr[16] = "true";
  GetPrivateProfileStringA("Network", "IsCrossPlatform", "true", crossStr, sizeof(crossStr), iniPath);
  g_lobbyIsCrossPlatform = (_stricmp(crossStr, "true") == 0 || strcmp(crossStr, "1") == 0);

  char appVerStr[64] = "1.2.0";
  GetPrivateProfileStringA("Network", "AppVersion", "1.2.0", appVerStr, sizeof(appVerStr), iniPath);
  strncpy(g_lobbyAppVersion, appVerStr, sizeof(g_lobbyAppVersion) - 1);
  g_lobbyAppVersion[sizeof(g_lobbyAppVersion) - 1] = '\0';

  char ruleStr[64] = "Koikoi";
  GetPrivateProfileStringA("Network", "OnlineRule", "Koikoi", ruleStr, sizeof(ruleStr), iniPath);
  strncpy(g_lobbyOnlineRule, ruleStr, sizeof(g_lobbyOnlineRule) - 1);
  g_lobbyOnlineRule[sizeof(g_lobbyOnlineRule) - 1] = '\0';

  Log("[Net] Initialized. Role=%s Target=%s:%d BindPort=%d ini=%s",
      role, ip, port, AtomicRead(&g_isHost) ? port : 0, iniPath);
  LogStateSnapshot("InitNetwork");

  AtomicWrite(&g_initialized, 1);
  InterlockedExchange(&g_initLock, 0);
}

void FirePeerConnectionEstablished(EOS_ProductUserId remotePeerId) {
    Log("[Net] >> FirePeerConnectionEstablished remote=%p", remotePeerId);
    if (!g_connectionEstablishedCb) {
        Log("[Net] g_connectionEstablishedCb is NULL, cannot fire yet");
        return;
    }
    if (InterlockedCompareExchange(&g_establishedFired, 1, 0) != 0) {
        Log("[Net] FirePeerConnectionEstablished already delivered, skipping");
        return;
    }
    EOS_P2P_OnPeerConnectionEstablishedInfo info = {0};
    info.ClientData = g_connectionEstablishedCbData;
    info.LocalUserId = LocalProductUserId();
    info.RemoteUserId = remotePeerId;
    info.SocketId = &g_socketId;
    info.ConnectionType = 0;
    Log("[Net] Invoking g_connectionEstablishedCb");
    g_connectionEstablishedCb(&info);
    Log("[Net] << FirePeerConnectionEstablished");
}

void FirePeerConnectionRequest(EOS_ProductUserId remotePeerId) {
    Log("[Net] >> FirePeerConnectionRequest remote=%p", remotePeerId);
    AtomicWrite(&g_peerConnected, 1);
    AtomicWrite(&g_lobbyHasRemoteMember, 1);
    if (g_connectionRequestCb) {
        if (InterlockedCompareExchange(&g_connectionRequestFired, 1, 0) != 0) {
            Log("[Net] FirePeerConnectionRequest already delivered, skipping");
            return;
        }
        LogStateSnapshot("FirePeerConnectionRequest");
        EOS_P2P_OnIncomingConnectionRequestInfo info = {0};
        info.ClientData = g_connectionRequestCbData;
        info.LocalUserId = LocalProductUserId();
        info.RemoteUserId = remotePeerId;
        info.SocketId = &g_socketId;
        Log("[Net] Invoking g_connectionRequestCb");
        g_connectionRequestCb(&info);
        // Do not auto-fire established here. The game must call AcceptConnection.
    } else {
        Log("[Net] g_connectionRequestCb is NULL, will retry when registered");
    }
    Log("[Net] << FirePeerConnectionRequest");
}

void FireLobbyMemberJoined(EOS_ProductUserId memberId) {
    Log("[Net] >> FireLobbyMemberJoined member=%p", memberId);
    AtomicWrite(&g_lobbyHasRemoteMember, 1);
    if (!g_memberStatusCb) {
        Log("[Net] g_memberStatusCb is NULL, cannot fire yet");
        return;
    }
    if (InterlockedCompareExchange(&g_lobbyMemberJoinedFired, 1, 0) != 0) {
        Log("[Net] FireLobbyMemberJoined already delivered, skipping");
        return;
    }
    EOS_Lobby_LobbyMemberStatusReceivedCallbackInfo info = {0};
    info.ClientData = g_memberStatusCbData;
    info.LobbyId = "DirectIP_Lobby";
    info.TargetUserId = memberId;
    info.CurrentStatus = 0;
    Log("[Net] Invoking g_memberStatusCb for JOINED");
    g_memberStatusCb(&info);
    Log("[Net] << FireLobbyMemberJoined");
}

static void SendHelloAckLocked(const struct sockaddr_in* to) {
    if (udpSocket == INVALID_SOCKET || !to) return;
    sendto(udpSocket, kMitmAck, (int)strlen(kMitmAck), 0,
           (struct sockaddr*)to, sizeof(*to));
    Log("[Net] Sent ACK to %s:%d", inet_ntoa(to->sin_addr), ntohs(to->sin_port));
}

void SendHelloAck(const struct sockaddr_in* to) {
    if (udpSocket == INVALID_SOCKET || !to) return;
    EnterCriticalSection(&g_socketCs);
    SendHelloAckLocked(to);
    LeaveCriticalSection(&g_socketCs);
}

static void HostOnFirstPacketReceivedNoLock(const struct sockaddr_in* from) {
    if (!from) return;
    if (!AtomicRead(&g_hostHasClientAddr)) {
        targetAddr = *from;
        AtomicWrite(&g_hostHasClientAddr, 1);
    }
    Log("[Net] Host detected client at %s:%d — firing member+connection events",
        inet_ntoa(from->sin_addr), ntohs(from->sin_port));
    LogStateSnapshot("HostFirstPacket");
    TryFireHostEvents(ClientProductUserId());
}

void HostOnFirstPacketReceived(const struct sockaddr_in* from) {
    if (!from) return;
    EnterCriticalSection(&g_socketCs);
    HostOnFirstPacketReceivedNoLock(from);
    LeaveCriticalSection(&g_socketCs);
}

void TryFireHostEvents(void* clientId) {
  EOS_ProductUserId id = (EOS_ProductUserId)clientId;
  if (!AtomicRead(&g_hostFiredEvents)) {
    EnterCriticalSection(&g_hostEventCs);
    if (!AtomicRead(&g_hostFiredEvents)) {
      AtomicWrite(&g_hostFiredEvents, 1);
      LeaveCriticalSection(&g_hostEventCs);
      AtomicWrite(&g_lobbyHasRemoteMember, 1);
      AtomicWrite(&g_peerConnected, 1);
      FireLobbyMemberJoined(id);
      FirePeerConnectionRequest(id);
      // Established is intentionally omitted; game must call AcceptConnection
    } else {
      LeaveCriticalSection(&g_hostEventCs);
    }
  } else {
    // Fallbacks for late-registered callbacks only
    if (AtomicRead(&g_lobbyHasRemoteMember) && g_memberStatusCb &&
        InterlockedCompareExchange(&g_lobbyMemberJoinedFired, 1, 0) == 0) {
      FireLobbyMemberJoined(id);
    }
    if (AtomicRead(&g_peerConnected) && g_connectionRequestCb &&
        InterlockedCompareExchange(&g_connectionRequestFired, 1, 0) == 0) {
      FirePeerConnectionRequest(id);
    }
    // Removed Established fallback to enforce AcceptConnection contract
  }
}

void TickRetryPendingEvents() {
  bool memberPending = AtomicRead(&g_lobbyHasRemoteMember) &&
                       AtomicRead(&g_lobbyMemberJoinedFired) == 0 &&
                       g_memberStatusCb;
  bool requestPending = AtomicRead(&g_peerConnected) &&
                        AtomicRead(&g_connectionRequestFired) == 0 &&
                        g_connectionRequestCb;
  if (!memberPending && !requestPending) return;

  EOS_ProductUserId id = RemoteProductUserId();
  if (memberPending) FireLobbyMemberJoined(id);
  if (requestPending) FirePeerConnectionRequest(id);
}

static bool DiscardMitmHelloIfPresentLocked() {
    // Caller must hold g_socketCs
    if (udpSocket == INVALID_SOCKET) return false;
    char peekBuf[64];
    struct sockaddr_in from;
    int fromLen = sizeof(from);
    int peeked = recvfrom(udpSocket, peekBuf, sizeof(peekBuf), MSG_PEEK,
                          (struct sockaddr *)&from, &fromLen);
    bool result = false;
    size_t helloLen = strlen(kMitmHello);
    size_t ackLen   = strlen(kMitmAck);

    if (peeked == (int)helloLen && memcmp(peekBuf, kMitmHello, helloLen) == 0) {
        recvfrom(udpSocket, peekBuf, sizeof(peekBuf), 0,
                 (struct sockaddr *)&from, &fromLen);
        if (AtomicRead(&g_isHost)) {
            if (!AtomicRead(&g_hostHasClientAddr)) {
                targetAddr = from;
                AtomicWrite(&g_hostHasClientAddr, 1);
            }
            SendHelloAckLocked(&from);
            TryFireHostEvents(ClientProductUserId());
        }
        Log("[Net] Filtered DirectIP hello from %s:%d",
            inet_ntoa(from.sin_addr), ntohs(from.sin_port));
        result = true;
    } else if (!AtomicRead(&g_isHost) && peeked == (int)ackLen &&
               memcmp(peekBuf, kMitmAck, ackLen) == 0) {
        recvfrom(udpSocket, peekBuf, sizeof(peekBuf), 0,
                 (struct sockaddr *)&from, &fromLen);
        AtomicWrite(&g_helloAckReceived, 1);
        AtomicWrite(&g_peerConnected, 1);
        AtomicWrite(&g_lobbyHasRemoteMember, 1);
        Log("[Net] Filtered DirectIP ACK from %s:%d",
            inet_ntoa(from.sin_addr), ntohs(from.sin_port));
        result = true;
    }
    return result;
}

DWORD WINAPI PeerWatchThread(LPVOID param) {
  LONG myGeneration = (LONG)(intptr_t)param;
  InitNetwork();
  Log("[Thread] PeerWatchThread started. isHost=%d generation=%ld",
      (int)AtomicRead(&g_isHost), (long)myGeneration);
  if (AtomicRead(&g_isHost)) {
    int loopCount = 0;
    while (loopCount < 400) {
      if (WaitForSingleObject(g_stopEvent, 0) == WAIT_OBJECT_0) break;
      if (AtomicRead(&g_watchThreadGeneration) != myGeneration) {
        Log("[Thread] Host: stale generation, exiting");
        break;
      }

      char peekBuf[64];
      struct sockaddr_in from;
      int fromLen = sizeof(from);
      bool consumedHello = false;
      bool gotNonHello = false;

      EnterCriticalSection(&g_socketCs);
      u_long bytesAvail = 0;
      if (ioctlsocket(udpSocket, FIONREAD, &bytesAvail) == 0 && bytesAvail > 0) {
        int peeked = recvfrom(udpSocket, peekBuf, sizeof(peekBuf), MSG_PEEK,
                              (struct sockaddr*)&from, &fromLen);
        if (peeked > 0) {
          size_t helloLen = strlen(kMitmHello);
          if (peeked == (int)helloLen &&
              memcmp(peekBuf, kMitmHello, helloLen) == 0) {
            recvfrom(udpSocket, peekBuf, sizeof(peekBuf), 0,
                     (struct sockaddr*)&from, &fromLen);
            consumedHello = true;
          } else {
            gotNonHello = true;
          }
        }
      }
      LeaveCriticalSection(&g_socketCs);

      if (consumedHello) {
        Log("[Net] Consumed DirectIP hello from client %s:%d",
            inet_ntoa(from.sin_addr), ntohs(from.sin_port));
        EnterCriticalSection(&g_socketCs);
        if (!AtomicRead(&g_hostHasClientAddr)) {
            targetAddr = from;
            AtomicWrite(&g_hostHasClientAddr, 1);
        }
        LeaveCriticalSection(&g_socketCs);
        SendHelloAck(&from);
        TryFireHostEvents(ClientProductUserId());
        continue;
      }

      if (gotNonHello) {
        if (!AtomicRead(&g_hostHasClientAddr)) {
            EnterCriticalSection(&g_socketCs);
            if (!AtomicRead(&g_hostHasClientAddr)) {
                targetAddr = from;
                AtomicWrite(&g_hostHasClientAddr, 1);
            }
            LeaveCriticalSection(&g_socketCs);
        }
        TryFireHostEvents(ClientProductUserId());
        if (AtomicRead(&g_peerConnected) && AtomicRead(&g_lobbyHasRemoteMember)) {
            Log("[Thread] Host bootstrap complete, exiting watch thread");
            break;
        }
      } else if (AtomicRead(&g_lobbyHasRemoteMember) || AtomicRead(&g_peerConnected)) {
          TryFireHostEvents(ClientProductUserId());
          if (AtomicRead(&g_peerConnected) && AtomicRead(&g_lobbyHasRemoteMember)) {
              Log("[Thread] Host bootstrap complete, exiting watch thread");
              break;
          }
      }

      Sleep(50);
      if (++loopCount % 20 == 0) {
          Log("[Thread] Host waiting for client hello... (%d loops)", loopCount);
      }
    }
  } else {
    int helloRetry = 0;
    while (AtomicRead(&g_helloAckReceived) == 0 && AtomicRead(&g_peerConnected) == 0) {
      if (WaitForSingleObject(g_stopEvent, 0) == WAIT_OBJECT_0) break;
      if (AtomicRead(&g_watchThreadGeneration) != myGeneration) {
        Log("[Thread] Client hello: stale generation, exiting");
        goto thread_exit;
      }

      EnterCriticalSection(&g_socketCs);
      sendto(udpSocket, kMitmHello, (int)strlen(kMitmHello), 0,
             (struct sockaddr *)&targetAddr, sizeof(targetAddr));
      LeaveCriticalSection(&g_socketCs);
      Log("[Net] Client sent DirectIP hello to host (attempt %d)", helloRetry + 1);

      DWORD start = GetTickCount();
      while (GetTickCount() - start < 250) {
        if (WaitForSingleObject(g_stopEvent, 0) == WAIT_OBJECT_0) break;

        char buf[64];
        struct sockaddr_in from;
        int fromLen = sizeof(from);
        int r = -1;

        EnterCriticalSection(&g_socketCs);
        u_long bytesAvail = 0;
        if (ioctlsocket(udpSocket, FIONREAD, &bytesAvail) == 0 && bytesAvail > 0) {
          r = recvfrom(udpSocket, buf, sizeof(buf), MSG_PEEK,
                       (struct sockaddr*)&from, &fromLen);
        }
        LeaveCriticalSection(&g_socketCs);

        if (r > 0) {
          size_t ackLen = strlen(kMitmAck);
          if (r == (int)ackLen && memcmp(buf, kMitmAck, ackLen) == 0) {
            EnterCriticalSection(&g_socketCs);
            recvfrom(udpSocket, buf, sizeof(buf), 0,
                     (struct sockaddr*)&from, &fromLen);
            LeaveCriticalSection(&g_socketCs);
            AtomicWrite(&g_helloAckReceived, 1);
            AtomicWrite(&g_peerConnected, 1);
            AtomicWrite(&g_lobbyHasRemoteMember, 1);
            Log("[Net] Client received ACK from host %s:%d",
                inet_ntoa(from.sin_addr), ntohs(from.sin_port));
            break;
          } else {
            AtomicWrite(&g_peerConnected, 1);
            AtomicWrite(&g_lobbyHasRemoteMember, 1);
            Log("[Net] Client received data from host, assuming connected");
            break;
          }
        }
        Sleep(10);
      }

      if (AtomicRead(&g_helloAckReceived) || AtomicRead(&g_peerConnected)) break;

      helloRetry++;
      if (helloRetry > 40) {
        Log("[Net] Client gave up waiting for host after %d retries", helloRetry);
        break;
      }
    }

    int eventRetry = 0;
    while (eventRetry < 200) {
      if (WaitForSingleObject(g_stopEvent, 0) == WAIT_OBJECT_0) break;
      if (AtomicRead(&g_watchThreadGeneration) != myGeneration) {
        Log("[Thread] Client events: stale generation, exiting");
        goto thread_exit;
      }

      bool needRequest  = AtomicRead(&g_connectionRequestFired) == 0;
      bool needEstablished = AtomicRead(&g_establishedFired) == 0;
      bool needMember     = AtomicRead(&g_lobbyMemberJoinedFired) == 0;

      if (!needRequest && !needEstablished && !needMember) {
        Log("[Thread] Client all events fired successfully");
        break;
      }

      if (needMember && g_memberStatusCb) {
        FireLobbyMemberJoined(HostProductUserId());
      }
      if (needRequest && g_connectionRequestCb) {
        FirePeerConnectionRequest(HostProductUserId());
      }
      if (needEstablished && g_connectionEstablishedCb) {
        FirePeerConnectionEstablished(HostProductUserId());
      }

      Sleep(100);
      eventRetry++;
    }
  }
thread_exit:
  Log("[Thread] PeerWatchThread exiting (generation=%ld)", (long)myGeneration);
  EnterCriticalSection(&g_socketCs);
  if (AtomicRead(&g_watchThreadGeneration) == myGeneration) {
    if (g_peerWatchThread) CloseHandle(g_peerWatchThread);
    g_peerWatchThread = NULL;
  }
  LeaveCriticalSection(&g_socketCs);
  return 0;
}

static bool ShouldSuppressAutoTeardown() {
    if (!AtomicRead(&g_autoTeardownSuppressed))
        return false;
    DWORD now = GetTickCount();
    DWORD elapsed = now - g_autoTeardownSuppressTime;
    if (elapsed < kAutoTeardownSuppressWindowMs) {
        Log("[Proxy] ShouldSuppressAutoTeardown: active (%lu ms elapsed), "
            "deferring reset", (unsigned long)elapsed);
        return true;
    }
    Log("[Proxy] ShouldSuppressAutoTeardown: window expired (%lu ms elapsed), "
        "clearing flag", (unsigned long)elapsed);
    AtomicWrite(&g_autoTeardownSuppressed, 0);
    return false;
}

extern "C" {

__declspec(dllexport) EOS_EResult
EOS_P2P_SendPacket(EOS_HP2P Handle, const EOS_P2P_SendPacketOptions *Options) {
  Log("[Proxy] >> EOS_P2P_SendPacket entry");
  if (!Options) {
    Log("[Proxy] !! EOS_P2P_SendPacket rejected: Options is NULL");
    return EOS_InvalidParameters;
  }
  InitNetwork();

  struct sockaddr_in sendAddr;
  EnterCriticalSection(&g_socketCs);
  sendAddr = targetAddr;
  LeaveCriticalSection(&g_socketCs);

  EnterCriticalSection(&g_socketCs);
  int sent = sendto(udpSocket, (const char *)Options->Data, Options->DataLengthBytes,
                    0, (struct sockaddr *)&sendAddr, sizeof(sendAddr));
  LeaveCriticalSection(&g_socketCs);


  if (InterlockedIncrement(&g_sendLogCount) <= 80 || sent == SOCKET_ERROR) {
    LogCallStack("EOS_P2P_SendPacket");
    Log("[Proxy] SendPacket: requested=%u sent=%d channel=%u remote=%p",
        Options->DataLengthBytes, sent, Options->Channel, Options->RemoteUserId);
  }
  if (sent == SOCKET_ERROR) {
    Log("[Proxy] !! sendto failed: WSA %d", WSAGetLastError());
  } else {
    g_lastGoodPacketTime = GetTickCount();
    DumpPacket("SEND", Options->Data, (uint32_t)sent, &sendAddr, Options->Channel);
  }
  Log("[Proxy] << EOS_P2P_SendPacket exit (result=Success)");
  return EOS_Success;
}

__declspec(dllexport) EOS_EResult EOS_P2P_GetNextReceivedPacketSize(
    EOS_HP2P Handle, const EOS_P2P_GetNextReceivedPacketSizeOptions *Options,
    uint32_t *OutPacketSizeBytes) {


  if (AtomicRead(&g_resetInProgress)) return EOS_NotFound;
  InitNetwork();
  if (!AtomicRead(&g_initialized)) return EOS_NotFound;
  if (!OutPacketSizeBytes) {
    Log("[Proxy] !! GetNextReceivedPacketSize rejected: OutPacketSizeBytes is NULL");
    return EOS_InvalidParameters;
  }
  EnterCriticalSection(&g_socketCs);
  while (DiscardMitmHelloIfPresentLocked()) {}

  char tempBuf[65536];
  struct sockaddr_in from;
  int fromLen = sizeof(from);
  int peeked = recvfrom(udpSocket, tempBuf, sizeof(tempBuf), MSG_PEEK,
                        (struct sockaddr*)&from, &fromLen);
  LeaveCriticalSection(&g_socketCs);

  if (peeked > 0) {
    *OutPacketSizeBytes = (uint32_t)peeked;

    if (InterlockedIncrement(&g_sizeLogCount) <= 80) {
      int requestedChannel = (Options && Options->RequestedChannel) ? (int)*Options->RequestedChannel : -1;
      Log("[Proxy] GetNextReceivedPacketSize: %d bytes requestedChannel=%d",
          peeked, requestedChannel);
    }
    return EOS_Success;
  }
  return EOS_NotFound;
}

__declspec(dllexport) EOS_EResult EOS_P2P_ReceivePacket(
    EOS_HP2P Handle, const EOS_P2P_ReceivePacketOptions *Options,
    void *OutPeerId, void *OutSocketId, uint8_t *OutChannel, void *OutData,
    uint32_t *OutBytesWritten) {
  Log("[Proxy] >> EOS_P2P_ReceivePacket entry");

  if (AtomicRead(&g_resetInProgress)) {
    Log("[Proxy] << EOS_P2P_ReceivePacket exit (reset in progress, NotFound)");
    return EOS_NotFound;
  }
  InitNetwork();
  if (!AtomicRead(&g_initialized)) {
    Log("[Proxy] << EOS_P2P_ReceivePacket exit (not initialized, NotFound)");
    return EOS_NotFound;
  }
  if (!Options || !OutData || !OutBytesWritten) {
    Log("[Proxy] !! ReceivePacket rejected: bad args (Options=%p OutData=%p OutBytesWritten=%p)",
        (void*)Options, OutData, (void*)OutBytesWritten);
    return EOS_InvalidParameters;
  }
  struct sockaddr_in from;
  int fromLen = sizeof(from);

  EnterCriticalSection(&g_socketCs);
  while (DiscardMitmHelloIfPresentLocked()) {}
  int ret = recvfrom(udpSocket, (char *)OutData, Options->MaxDataSizeBytes, 0,
                     (struct sockaddr *)&from, &fromLen);
  LeaveCriticalSection(&g_socketCs);

  if (ret > 0) {
    g_lastGoodPacketTime = GetTickCount();
    // Only update host client address on first packet, without recursive locking
    if (AtomicRead(&g_isHost) && AtomicRead(&g_hostHasClientAddr) == 0) {
        HostOnFirstPacketReceivedNoLock(&from);
    }
    *OutBytesWritten = ret;
    uint8_t channel = Options->RequestedChannel ? *Options->RequestedChannel : 0;
    if (OutChannel) *OutChannel = channel;
    if (OutPeerId) *(EOS_ProductUserId *)OutPeerId = RemoteProductUserId();
    if (OutSocketId) memcpy(OutSocketId, &g_socketId, sizeof(g_socketId));

    DumpPacket("RECV", OutData, (uint32_t)ret, &from, channel);

    if (InterlockedIncrement(&g_receiveLogCount) <= 80) {
      LogCallStack("EOS_P2P_ReceivePacket");
      Log("[Proxy] ReceivePacket: %d bytes from %s:%d peer=%p channel=%u",
          ret, inet_ntoa(from.sin_addr), ntohs(from.sin_port),
          RemoteProductUserId(), channel);
    }
    Log("[Proxy] << EOS_P2P_ReceivePacket exit (success, %d bytes)", ret);
    return EOS_Success;
  }
  Log("[Proxy] << EOS_P2P_ReceivePacket exit (NotFound)");
  return EOS_NotFound;
}

__declspec(dllexport) EOS_EResult
EOS_P2P_AcceptConnection(EOS_HP2P Handle, const void *Options) {
  Log("[Proxy] >> EOS_P2P_AcceptConnection called");
  AtomicWrite(&g_peerConnected, 1);
  LogStateSnapshot("AcceptConnection");
  FirePeerConnectionEstablished(RemoteProductUserId());
  Log("[Proxy] << EOS_P2P_AcceptConnection exit");
  return EOS_Success;
}

__declspec(dllexport) EOS_EResult EOS_P2P_CloseConnection(EOS_HP2P Handle, const void *Options) {
  Log("[Proxy] EOS_P2P_CloseConnection called");
  LogCallStack("EOS_P2P_CloseConnection");
  return EOS_Success;
}

__declspec(dllexport) EOS_EResult
EOS_P2P_CloseConnections(EOS_HP2P Handle, const void *Options) {
  Log("[Proxy] EOS_P2P_CloseConnections called");
  LogCallStack("EOS_P2P_CloseConnections");

  // This is a bodge, this isnt a best practice at all, but it works
  bool userInitiated = StackContainsModule("USER32.dll");
  bool socketHealthy = (udpSocket != INVALID_SOCKET) &&
                        (GetTickCount() - g_lastGoodPacketTime) < 4000;

  static LONG s_suppressedCount = 0;
  if (!userInitiated && socketHealthy && s_suppressedCount < 20) {
    LONG n = InterlockedIncrement(&s_suppressedCount);
    // Set the shared suppression flag so LeaveLobby and DestroyLobby
    // (which fire in the same tick) also skip their reset.
    AtomicWrite(&g_autoTeardownSuppressed, 1);
    g_autoTeardownSuppressTime = GetTickCount();
    Log("[Proxy] EOS_P2P_CloseConnections SUPPRESSED (auto-teardown, socket healthy, "
        "suppressed=%ld, msSinceLastGoodPacket=%lu, suppression window started)",
        n, (unsigned long)(GetTickCount() - g_lastGoodPacketTime));
    return EOS_Success; // tell the game it happened; don't actually reset anything
  }

  s_suppressedCount = 0;
  AtomicWrite(&g_autoTeardownSuppressed, 0);
  Log("[Proxy] EOS_P2P_CloseConnections executing real reset "
      "(userInitiated=%d socketHealthy=%d msSinceLastGoodPacket=%lu)",
      (int)userInitiated, (int)socketHealthy,
      (unsigned long)(GetTickCount() - g_lastGoodPacketTime));
  ResetConnectionState();
  return EOS_Success;
}

__declspec(dllexport) EOS_EResult
EOS_P2P_QueryNATType(EOS_HP2P Handle, const void *Options, void *ClientData, void *CompletionDelegate) {
  Log("[Proxy] EOS_P2P_QueryNATType called");
  return EOS_Success;
}

__declspec(dllexport) EOS_EResult EOS_P2P_GetNATType(EOS_HP2P Handle, const void *Options, int32_t *OutNATType) {
  Log("[Proxy] EOS_P2P_GetNATType called");
  if (OutNATType) *OutNATType = 0;
  return EOS_Success;
}

__declspec(dllexport) EOS_EResult EOS_P2P_SetRelayControl(EOS_HP2P Handle, const void *Options) {
  Log("[Proxy] EOS_P2P_SetRelayControl called");
  return EOS_Success;
}
__declspec(dllexport) EOS_EResult EOS_P2P_GetRelayControl(EOS_HP2P Handle, const void *Options, int32_t *OutRelayControl) {
  Log("[Proxy] EOS_P2P_GetRelayControl called");
  return EOS_Success;
}
__declspec(dllexport) EOS_EResult EOS_P2P_SetPortRange(EOS_HP2P Handle, const void *Options) {
  Log("[Proxy] EOS_P2P_SetPortRange called");
  return EOS_Success;
}
__declspec(dllexport) EOS_EResult
EOS_P2P_GetPortRange(EOS_HP2P Handle, const void *Options, uint16_t *OutPort, uint16_t *OutNumAdditionalPortsToTry) {
  Log("[Proxy] EOS_P2P_GetPortRange called");
  if (OutPort) *OutPort = 7777;
  if (OutNumAdditionalPortsToTry) *OutNumAdditionalPortsToTry = 0;
  return EOS_Success;
}

__declspec(dllexport) uint64_t EOS_P2P_AddNotifyPeerConnectionRequest(
    EOS_HP2P Handle, const void *Options, void *ClientData,
    EOS_P2P_OnIncomingConnectionRequestCallback NotificationFn) {
  static LONG g_addCount = 0;
  Log("[Proxy] EOS_P2P_AddNotifyPeerConnectionRequest called (count=%d)", (int)InterlockedIncrement(&g_addCount));
  g_connectionRequestCb = NotificationFn;
  g_connectionRequestCbData = ClientData;
  Log("[Proxy] Stored connection request callback=%p data=%p", (void*)NotificationFn, ClientData);
  if (AtomicRead(&g_peerConnected) && NotificationFn) {
    Log("[Proxy] Peer already connected, immediately firing request");
    FirePeerConnectionRequest(RemoteProductUserId());
  }
  return g_notifIdCounter++;
}

__declspec(dllexport) void
EOS_P2P_RemoveNotifyPeerConnectionRequest(EOS_HP2P Handle, uint64_t NotificationId) {
  Log("[Proxy] EOS_P2P_RemoveNotifyPeerConnectionRequest id=%llu", (unsigned long long)NotificationId);
}

__declspec(dllexport) uint64_t EOS_P2P_AddNotifyPeerConnectionEstablished(
    EOS_HP2P Handle, const void *Options, void *ClientData,
    EOS_P2P_OnPeerConnectionEstablishedCallback NotificationFn) {
  Log("[Proxy] EOS_P2P_AddNotifyPeerConnectionEstablished called cb=%p", (void*)NotificationFn);
  g_connectionEstablishedCb = NotificationFn;
  g_connectionEstablishedCbData = ClientData;
  if (AtomicRead(&g_peerConnected) && NotificationFn) {
    Log("[Proxy] Peer already connected, immediately firing established");
    FirePeerConnectionEstablished(RemoteProductUserId());
  }
  return g_notifIdCounter++;
}

__declspec(dllexport) void
EOS_P2P_RemoveNotifyPeerConnectionEstablished(EOS_HP2P Handle, uint64_t NotificationId) {
  Log("[Proxy] EOS_P2P_RemoveNotifyPeerConnectionEstablished id=%llu", (unsigned long long)NotificationId);
}

__declspec(dllexport) uint64_t EOS_P2P_AddNotifyPeerConnectionInterrupted(
    EOS_HP2P Handle, const void *Options, void *ClientData, void *NotificationFn) {
  Log("[Proxy] EOS_P2P_AddNotifyPeerConnectionInterrupted called");
  return g_notifIdCounter++;
}

__declspec(dllexport) void
EOS_P2P_RemoveNotifyPeerConnectionInterrupted(EOS_HP2P Handle, uint64_t NotificationId) {
  Log("[Proxy] EOS_P2P_RemoveNotifyPeerConnectionInterrupted id=%llu", (unsigned long long)NotificationId);
}

__declspec(dllexport) uint64_t EOS_P2P_AddNotifyIncomingPacketQueueFull(
    EOS_HP2P Handle, const void *Options, void *ClientData, void *NotificationFn) {
  Log("[Proxy] EOS_P2P_AddNotifyIncomingPacketQueueFull called");
  return g_notifIdCounter++;
}

__declspec(dllexport) void
EOS_P2P_RemoveNotifyIncomingPacketQueueFull(EOS_HP2P Handle, uint64_t NotificationId) {
  Log("[Proxy] EOS_P2P_RemoveNotifyIncomingPacketQueueFull id=%llu", (unsigned long long)NotificationId);
}

__declspec(dllexport) EOS_EResult EOS_P2P_ClearPacketQueue(EOS_HP2P Handle, const void *Options) {
  Log("[Proxy] EOS_P2P_ClearPacketQueue called (ignored)");
  LogCallStack("EOS_P2P_ClearPacketQueue");
  return EOS_Success;
}

__declspec(dllexport) EOS_EResult EOS_P2P_SetPacketQueueSize(EOS_HP2P Handle, const void *Options) {
  Log("[Proxy] EOS_P2P_SetPacketQueueSize called");
  return EOS_Success;
}

__declspec(dllexport) EOS_EResult EOS_P2P_GetPacketQueueInfo(EOS_HP2P Handle, const void *Options, void *OutPacketQueueInfo) {
  Log("[Proxy] EOS_P2P_GetPacketQueueInfo called");
  if (OutPacketQueueInfo) memset(OutPacketQueueInfo, 0, 32);
  return EOS_Success;
}

__declspec(dllexport) uint64_t
EOS_P2P_AddNotifyPeerConnectionClosed(EOS_HP2P Handle, const void *Options, void *ClientData, void *NotificationFn) {
  Log("[Proxy] EOS_P2P_AddNotifyPeerConnectionClosed called");
  return g_notifIdCounter++;
}
__declspec(dllexport) void
EOS_P2P_RemoveNotifyPeerConnectionClosed(EOS_HP2P Handle, uint64_t NotificationId) {
  Log("[Proxy] EOS_P2P_RemoveNotifyPeerConnectionClosed id=%llu", (unsigned long long)NotificationId);
}

// Auth and Connect Mocking
typedef void *EOS_HAuth;
struct EOS_Auth_LoginOptions {
  int32_t ApiVersion;
  const void *Credentials;
  int32_t ScopeFlags;
};

struct EOS_Auth_LoginCallbackInfo {
  int32_t ResultCode;
  void *ClientData;
  EOS_ProductUserId LocalUserId;
  void *PinGrantInfo;
  void *ContinuanceToken;
  void *AccountFeatureRestrictedInfo;
  EOS_ProductUserId SelectedAccountId;
};
typedef void (*EOS_Auth_OnLoginCallback)(const EOS_Auth_LoginCallbackInfo *Data);

typedef void *EOS_HConnect;
struct EOS_Connect_LoginOptions {
  int32_t ApiVersion;
  const void *Credentials;
  const void *UserLoginInfo;
};

struct EOS_Connect_LoginCallbackInfo {
  int32_t ResultCode;
  void *ClientData;
  EOS_ProductUserId LocalUserId;
  void *ContinuanceToken;
};
typedef void (*EOS_Connect_OnLoginCallback)(const EOS_Connect_LoginCallbackInfo *Data);

struct EOS_Connect_CreateUserOptions {
  int32_t ApiVersion;
  void *ContinuanceToken;
};
struct EOS_Connect_CreateUserCallbackInfo {
  int32_t ResultCode;
  void *ClientData;
  EOS_ProductUserId LocalUserId;
};
typedef void (*EOS_Connect_OnCreateUserCallback)(const EOS_Connect_CreateUserCallbackInfo *Data);

__declspec(dllexport) void
EOS_Auth_Login(EOS_HAuth Handle, const EOS_Auth_LoginOptions *Options,
               void *ClientData, EOS_Auth_OnLoginCallback CompletionDelegate) {
  Log("[Proxy] >> EOS_Auth_Login called");
  if (CompletionDelegate) {
    EOS_Auth_LoginCallbackInfo info = {0};
    info.ResultCode = EOS_Success;
    info.ClientData = ClientData;
    info.LocalUserId = (EOS_ProductUserId)1;
    info.SelectedAccountId = (EOS_ProductUserId)1;
    Log("[Proxy] Invoking EOS_Auth_Login callback with Success");
    CompletionDelegate(&info);
  }
  Log("[Proxy] << EOS_Auth_Login exit");
}

struct EOS_Connect_LoginStatusChangedCallbackInfo {
  void* ClientData;
  EOS_ProductUserId LocalUserId;
  int32_t PreviousStatus; // EOS_ELoginStatus
  int32_t CurrentStatus;  // EOS_ELoginStatus
};

__declspec(dllexport) void
EOS_Connect_Login(EOS_HConnect Handle, const EOS_Connect_LoginOptions *Options,
                  void *ClientData, EOS_Connect_OnLoginCallback CompletionDelegate) {
  static LONG g_connectLoginCount = 0;
  Log("[Proxy] EOS_Connect_Login called (call #%d)", (int)InterlockedIncrement(&g_connectLoginCount));
  EOS_ProductUserId localUser = LocalProductUserId();
  if (CompletionDelegate) {
    EOS_Connect_LoginCallbackInfo info = {0};
    info.ResultCode = EOS_Success;
    info.ClientData = ClientData;
    info.LocalUserId = localUser;
    Log("[Proxy] Invoking EOS_Connect_Login callback localUser=%p", info.LocalUserId);
    CompletionDelegate(&info);
  }

  if (g_loginStatusChangedCb) {
    EOS_Connect_LoginStatusChangedCallbackInfo statusInfo = {0};
    statusInfo.ClientData = g_loginStatusChangedCbData;
    statusInfo.LocalUserId = localUser;
    statusInfo.PreviousStatus = 0; // EOS_LS_NotLoggedIn
    statusInfo.CurrentStatus = 2;  // EOS_LS_LoggedIn
    Log("[Proxy] Invoking LoginStatusChanged callback -> LoggedIn");
    g_loginStatusChangedCb(&statusInfo);
  }
}

__declspec(dllexport) void EOS_Connect_CreateUser(
    EOS_HConnect Handle, const EOS_Connect_CreateUserOptions *Options,
    void *ClientData, EOS_Connect_OnCreateUserCallback CompletionDelegate) {
  Log("[Proxy] EOS_Connect_CreateUser called");
  if (CompletionDelegate) {
    EOS_Connect_CreateUserCallbackInfo info = {0};
    info.ResultCode = EOS_Success;
    info.ClientData = ClientData;
    info.LocalUserId = LocalProductUserId();
    CompletionDelegate(&info);
  }
}

__declspec(dllexport) uint64_t
EOS_Connect_AddNotifyAuthExpiration(EOS_HConnect Handle, const void *Options,
                                    void *ClientData, void *NotificationFn) {
  Log("[Proxy] EOS_Connect_AddNotifyAuthExpiration called");
  return g_notifIdCounter++;
}

// EOS_ELoginStatus: 0=NotLoggedIn, 1=UsingLocalProfile, 2=LoggedIn
__declspec(dllexport) int32_t
EOS_Connect_GetLoginStatus(EOS_HConnect Handle, EOS_ProductUserId LocalUserId) {

  Log("[Proxy] EOS_Connect_GetLoginStatus called -> LoggedIn (2)");
  return 2; // EOS_LS_LoggedIn
}

__declspec(dllexport) uint64_t
EOS_Connect_AddNotifyLoginStatusChanged(EOS_HConnect Handle, const void *Options,
                                        void *ClientData,
                                        EOS_Connect_OnLoginStatusChangedCallback NotificationFn) {
  Log("[Proxy] EOS_Connect_AddNotifyLoginStatusChanged called cb=%p", (void*)NotificationFn);
  g_loginStatusChangedCb = NotificationFn;
  g_loginStatusChangedCbData = ClientData;
  return g_notifIdCounter++;
}

__declspec(dllexport) void
EOS_Connect_RemoveNotifyLoginStatusChanged(EOS_HConnect Handle, uint64_t InId) {
  Log("[Proxy] EOS_Connect_RemoveNotifyLoginStatusChanged id=%llu", (unsigned long long)InId);
  g_loginStatusChangedCb = nullptr;
  g_loginStatusChangedCbData = nullptr;
}

__declspec(dllexport) int32_t
EOS_Auth_GetLoginStatus(EOS_HAuth Handle, EOS_ProductUserId LocalUserId) {
  Log("[Proxy] EOS_Auth_GetLoginStatus called -> LoggedIn (2)");
  return 2;
}

__declspec(dllexport) EOS_ProductUserId
EOS_EpicAccountId_FromString(const char *AccountIdString) {
  Log("[Proxy] EOS_EpicAccountId_FromString(%s)", AccountIdString ? AccountIdString : "NULL");
  return (EOS_ProductUserId)1;
}
__declspec(dllexport) EOS_ProductUserId
EOS_ProductUserId_FromString(const char *AccountIdString) {
  Log("[Proxy] EOS_ProductUserId_FromString(%s)", AccountIdString ? AccountIdString : "NULL");
  if (AccountIdString && strstr(AccountIdString, "HOST")) return HostProductUserId();
  if (AccountIdString && strstr(AccountIdString, "CLIENT")) return ClientProductUserId();
  return LocalProductUserId();
}
__declspec(dllexport) int32_t
EOS_EpicAccountId_IsValid(EOS_ProductUserId AccountId) {
  return 1;
}
__declspec(dllexport) int32_t
EOS_ProductUserId_IsValid(EOS_ProductUserId AccountId) {
  int valid = (AccountId == HostProductUserId() || AccountId == ClientProductUserId());
  Log("[Proxy] EOS_ProductUserId_IsValid(%p) -> %d", AccountId, valid);
  return valid;
}

__declspec(dllexport) EOS_EResult
EOS_ProductUserId_ToString(EOS_ProductUserId AccountId, char *OutBuffer,
                           int32_t *InOutBufferLength) {
  const char *value = nullptr;
  if (AccountId == HostProductUserId()) value = "DIRECTIP_HOST_PUID";
  else if (AccountId == ClientProductUserId()) value = "DIRECTIP_CLIENT_PUID";
  else {
    Log("[Proxy] !! EOS_ProductUserId_ToString unknown AccountId=%p", AccountId);
    return EOS_InvalidParameters;
  }
  int32_t needed = (int32_t)strlen(value) + 1;
  if (!InOutBufferLength) return EOS_InvalidParameters;
  if (!OutBuffer || *InOutBufferLength < needed) {
    *InOutBufferLength = needed;
    return EOS_InvalidParameters;
  }
  memcpy(OutBuffer, value, needed);
  *InOutBufferLength = needed;
  Log("[Proxy] EOS_ProductUserId_ToString(%p) -> %s", AccountId, value);
  return EOS_Success;
}

struct EOS_Connect_CopyIdTokenOptions {
    int32_t ApiVersion;
    EOS_ProductUserId LocalUserId;
};

struct EOS_Connect_IdToken {
    int32_t ApiVersion;
    EOS_ProductUserId ProductUserId;
    const char* JsonWebToken;
};

__declspec(dllexport) EOS_EResult EOS_Connect_CopyIdToken(
    EOS_HConnect Handle, const EOS_Connect_CopyIdTokenOptions* Options,
    EOS_Connect_IdToken** OutIdToken) {
    Log("[Proxy] EOS_Connect_CopyIdToken called");
    if (!Options || !OutIdToken) return EOS_InvalidParameters;

    EOS_Connect_IdToken* token = (EOS_Connect_IdToken*)calloc(1, sizeof(EOS_Connect_IdToken));
    if (!token) return EOS_InvalidParameters;

    token->ApiVersion = 1;
    token->ProductUserId = Options->LocalUserId ? Options->LocalUserId : LocalProductUserId();
    token->JsonWebToken = "eyJhbGciOiJub25lIn0.eyJzdWIiOiIxMjM0NTY3ODkwIiwicHJvZHVjdFVzZXJJZCI6IkRJUkVDVElQX0hPU1RfUFVJRCJ9.";

    *OutIdToken = token;
    Log("[Proxy] EOS_Connect_CopyIdToken -> success, token=%p", (void*)token);
    return EOS_Success;
}

__declspec(dllexport) void EOS_Connect_IdToken_Release(EOS_Connect_IdToken* IdToken) {
    Log("[Proxy] EOS_Connect_IdToken_Release called");
    if (IdToken) {
        free(IdToken);
    }
}

struct EOS_Connect_VerifyIdTokenOptions {
    int32_t ApiVersion;
    const EOS_Connect_IdToken* IdToken;
};

struct EOS_Connect_VerifyIdTokenCallbackInfo {
    int32_t ResultCode;
    void* ClientData;
    EOS_ProductUserId ProductUserId;
};
typedef void (*EOS_Connect_OnVerifyIdTokenCallback)(const EOS_Connect_VerifyIdTokenCallbackInfo* Data);

__declspec(dllexport) void EOS_Connect_VerifyIdToken(
    EOS_HConnect Handle, const EOS_Connect_VerifyIdTokenOptions* Options,
    void* ClientData, EOS_Connect_OnVerifyIdTokenCallback CompletionDelegate) {
    Log("[Proxy] EOS_Connect_VerifyIdToken called");
    if (CompletionDelegate) {
        EOS_Connect_VerifyIdTokenCallbackInfo info = {0};
        info.ResultCode = EOS_Success;
        info.ClientData = ClientData;
        info.ProductUserId = (Options && Options->IdToken) ? Options->IdToken->ProductUserId : LocalProductUserId();
        Log("[Proxy] Invoking EOS_Connect_VerifyIdToken callback -> Success");
        CompletionDelegate(&info);
    }
}

// Lobby Mocking
typedef void *EOS_HLobby;
typedef void *EOS_HLobbySearch;
typedef void *EOS_HLobbyDetails;

struct EOS_Lobby_CreateLobbyCallbackInfo {
  int32_t ResultCode;
  void *ClientData;
  const char *LobbyId;
};
typedef void (*EOS_Lobby_OnCreateLobbyCallback)(const EOS_Lobby_CreateLobbyCallbackInfo *Data);

struct EOS_Lobby_JoinLobbyCallbackInfo {
  int32_t ResultCode;
  void *ClientData;
  const char *LobbyId;
};
typedef void (*EOS_Lobby_OnJoinLobbyCallback)(const EOS_Lobby_JoinLobbyCallbackInfo *Data);

struct EOS_Lobby_JoinLobbyByIdOptions {
    int32_t ApiVersion;
    const char* LobbyId;
    EOS_ProductUserId LocalUserId;
};

struct EOS_LobbySearch_FindCallbackInfo {
  int32_t ResultCode;
  void *ClientData;
};
typedef void (*EOS_LobbySearch_OnFindCallback)(const EOS_LobbySearch_FindCallbackInfo *Data);

struct EOS_LobbySearch_CopySearchResultByIndexOptions {
  int32_t ApiVersion;
  uint32_t LobbyIndex;
};

struct EOS_LobbyDetails_Info {
  int32_t ApiVersion;
  const char *LobbyId;
  EOS_ProductUserId LobbyOwnerUserId;
  uint32_t PermissionLevel;
  uint32_t AvailableSlots;
  uint32_t MaxMembers;
  int32_t bAllowInvites;
  const char *BucketId;
  int32_t bAllowHostMigration;
  int32_t bRTCRoomEnabled;
  int32_t bAllowJoinById;
  int32_t bRejoinAfterKickRequiresInvite;
};

__declspec(dllexport) void
EOS_Lobby_CreateLobby(EOS_HLobby Handle, const void *Options, void *ClientData,
                      EOS_Lobby_OnCreateLobbyCallback CompletionDelegate) {
  Log("[Proxy] >> EOS_Lobby_CreateLobby called");
  LogCallStack("EOS_Lobby_CreateLobby");
  InitNetwork();
  if (!AtomicRead(&g_isHost) && AtomicRead(&g_clientSawSearchResult)) {
    Log("[Proxy] Client called CreateLobby after seeing search result; forcing DirectIP join behavior");
    AtomicWrite(&g_lobbyHasRemoteMember, 1);
  }
  EnterCriticalSection(&g_socketCs);
  if (!g_peerWatchThread) {
    ResetEvent(g_stopEvent);
    LONG gen = InterlockedIncrement(&g_watchThreadGeneration);
    g_peerWatchThread = CreateThread(NULL, 0, PeerWatchThread, (LPVOID)(intptr_t)gen, 0, NULL);
    Log("[Proxy] Started PeerWatchThread from CreateLobby (generation=%ld)", (long)gen);
  } else {
    Log("[Proxy] CreateLobby: PeerWatchThread already running, not spawning a second one");
  }
  LeaveCriticalSection(&g_socketCs);
  if (CompletionDelegate) {
    EOS_Lobby_CreateLobbyCallbackInfo info = {0};
    info.ResultCode = EOS_Success;
    info.ClientData = ClientData;
    info.LobbyId = "DirectIP_Lobby";
    Log("[Proxy] Invoking CreateLobby callback");
    CompletionDelegate(&info);
  }
  LogStateSnapshot("CreateLobby");
  Log("[Proxy] << EOS_Lobby_CreateLobby exit");
}

__declspec(dllexport) void
EOS_Lobby_JoinLobby(EOS_HLobby Handle, const void *Options, void *ClientData,
                    EOS_Lobby_OnJoinLobbyCallback CompletionDelegate) {
  Log("[Proxy] >> EOS_Lobby_JoinLobby called");
  LogCallStack("EOS_Lobby_JoinLobby");
  InitNetwork();
  if (!AtomicRead(&g_isHost)) AtomicWrite(&g_lobbyHasRemoteMember, 1);
  EnterCriticalSection(&g_socketCs);
  if (!g_peerWatchThread) {
    ResetEvent(g_stopEvent);
    LONG gen = InterlockedIncrement(&g_watchThreadGeneration);
    g_peerWatchThread = CreateThread(NULL, 0, PeerWatchThread, (LPVOID)(intptr_t)gen, 0, NULL);
    Log("[Proxy] Started PeerWatchThread from JoinLobby (generation=%ld)", (long)gen);
  } else {
    Log("[Proxy] JoinLobby: PeerWatchThread already running, not spawning a second one");
  }
  LeaveCriticalSection(&g_socketCs);
  if (CompletionDelegate) {
    EOS_Lobby_JoinLobbyCallbackInfo info = {0};
    info.ResultCode = EOS_Success;
    info.ClientData = ClientData;
    info.LobbyId = "DirectIP_Lobby";
    Log("[Proxy] Invoking JoinLobby callback");
    CompletionDelegate(&info);
  }
  LogStateSnapshot("JoinLobby");
  Log("[Proxy] << EOS_Lobby_JoinLobby exit");
}

__declspec(dllexport) void
EOS_Lobby_JoinLobbyById(EOS_HLobby Handle, const EOS_Lobby_JoinLobbyByIdOptions* Options,
                        void* ClientData, EOS_Lobby_OnJoinLobbyCallback CompletionDelegate) {
    Log("[Proxy] >> EOS_Lobby_JoinLobbyById called id=%s", Options ? Options->LobbyId : "NULL");
    LogCallStack("EOS_Lobby_JoinLobbyById");
    InitNetwork();
    if (!AtomicRead(&g_isHost)) AtomicWrite(&g_lobbyHasRemoteMember, 1);
    EnterCriticalSection(&g_socketCs);
    if (!g_peerWatchThread) {
        ResetEvent(g_stopEvent);
        LONG gen = InterlockedIncrement(&g_watchThreadGeneration);
        g_peerWatchThread = CreateThread(NULL, 0, PeerWatchThread, (LPVOID)(intptr_t)gen, 0, NULL);
        Log("[Proxy] Started PeerWatchThread from JoinLobbyById (generation=%ld)", (long)gen);
    } else {
        Log("[Proxy] JoinLobbyById: PeerWatchThread already running, not spawning a second one");
    }
    LeaveCriticalSection(&g_socketCs);
    if (CompletionDelegate) {
        EOS_Lobby_JoinLobbyCallbackInfo info = {0};
        info.ResultCode = EOS_Success;
        info.ClientData = ClientData;
        info.LobbyId = "DirectIP_Lobby";
        Log("[Proxy] Invoking JoinLobbyById callback");
        CompletionDelegate(&info);
    }
    LogStateSnapshot("JoinLobbyById");
    Log("[Proxy] << EOS_Lobby_JoinLobbyById exit");
}

__declspec(dllexport) EOS_EResult
EOS_Lobby_CreateLobbySearch(EOS_HLobby Handle, const void *Options,
                            EOS_HLobbySearch *OutLobbySearchHandle) {
  Log("[Proxy] EOS_Lobby_CreateLobbySearch called");
  if (OutLobbySearchHandle) *OutLobbySearchHandle = (EOS_HLobbySearch)3;
  return EOS_Success;
}

__declspec(dllexport) void
EOS_LobbySearch_Find(EOS_HLobbySearch Handle, const void *Options,
                     void *ClientData,
                     EOS_LobbySearch_OnFindCallback CompletionDelegate) {
  Log("[Proxy] >> EOS_LobbySearch_Find called");
  if (CompletionDelegate) {
    EOS_LobbySearch_FindCallbackInfo info = {0};
    info.ResultCode = EOS_Success;
    info.ClientData = ClientData;
    Log("[Proxy] Invoking LobbySearch_Find callback");
    CompletionDelegate(&info);
  }
  Log("[Proxy] << EOS_LobbySearch_Find exit");
}

__declspec(dllexport) uint32_t EOS_LobbySearch_GetSearchResultCount(
    EOS_HLobbySearch Handle, const void *Options) {
  InitNetwork();
  uint32_t count = AtomicRead(&g_isHost) ? 0 : 1;
  Log("[Proxy] EOS_LobbySearch_GetSearchResultCount -> %u", count);
  return count;
}

__declspec(dllexport) EOS_EResult EOS_LobbySearch_CopySearchResultByIndex(
    EOS_HLobbySearch Handle,
    const EOS_LobbySearch_CopySearchResultByIndexOptions *Options,
    EOS_HLobbyDetails *OutLobbyDetailsHandle) {
  Log("[Proxy] EOS_LobbySearch_CopySearchResultByIndex called");
  AtomicWrite(&g_clientSawSearchResult, 1);
  LogStateSnapshot("CopySearchResultByIndex");
  if (OutLobbyDetailsHandle) *OutLobbyDetailsHandle = (EOS_HLobbyDetails)4;
  return EOS_Success;
}

static EOS_LobbyDetails_Info dummyLobbyInfo = {
    2, "DirectIP_Lobby", (EOS_ProductUserId)1,
    0, 1, 4, 1, "DirectIP_Bucket",
    0, 0, 1, 0
};

__declspec(dllexport) EOS_EResult
EOS_LobbyDetails_CopyInfo(EOS_HLobbyDetails Handle, const void *Options,
                          EOS_LobbyDetails_Info **OutLobbyDetailsInfo) {

  uint32_t currentMembers = (AtomicRead(&g_lobbyHasRemoteMember) || AtomicRead(&g_peerConnected)) ? 2 : 1;
  dummyLobbyInfo.AvailableSlots = g_maxMembers - currentMembers;
  dummyLobbyInfo.MaxMembers = g_maxMembers;
  if (InterlockedIncrement(&g_copyInfoLogCount) <= 20) {
    Log("[Proxy] EOS_LobbyDetails_CopyInfo owner=%p slots=%u/%u bucket=%s",
        dummyLobbyInfo.LobbyOwnerUserId, dummyLobbyInfo.AvailableSlots,
        dummyLobbyInfo.MaxMembers, dummyLobbyInfo.BucketId);
  }
  if (OutLobbyDetailsInfo) *OutLobbyDetailsInfo = &dummyLobbyInfo;
  return EOS_Success;
}

__declspec(dllexport) EOS_ProductUserId
EOS_LobbyDetails_GetLobbyOwner(EOS_HLobbyDetails Handle, const void *Options) {

  if (InterlockedIncrement(&g_ownerLogCount) <= 80) Log("[Proxy] EOS_LobbyDetails_GetLobbyOwner called");
  return HostProductUserId();
}

__declspec(dllexport) void
EOS_LobbyDetails_Release(void* LobbyDetailsHandle) {
    (void)LobbyDetailsHandle;
}

__declspec(dllexport) void
EOS_LobbySearch_Release(EOS_HLobbySearch LobbySearchHandle) {
  Log("[Proxy] EOS_LobbySearch_Release called");
}

__declspec(dllexport) EOS_EResult
EOS_LobbySearch_RemoveParameter(EOS_HLobbySearch Handle, const void *Options) {
  Log("[Proxy] EOS_LobbySearch_RemoveParameter called");
  return EOS_Success;
}
__declspec(dllexport) EOS_EResult
EOS_LobbySearch_SetLobbyId(EOS_HLobbySearch Handle, const void *Options) {
  Log("[Proxy] EOS_LobbySearch_SetLobbyId called");
  return EOS_Success;
}
__declspec(dllexport) EOS_EResult
EOS_LobbySearch_SetMaxResults(EOS_HLobbySearch Handle, const void *Options) {
  Log("[Proxy] EOS_LobbySearch_SetMaxResults called");
  return EOS_Success;
}

struct EOS_Lobby_AttributeData {
    int32_t ApiVersion;
    const char* Key;
    union { int64_t AsInt64; double AsDouble; int32_t AsBool; const char* AsUtf8; } Value;
    int32_t ValueType;
};

struct EOS_LobbySearch_SetParameterOptions {
    int32_t ApiVersion;
    const EOS_Lobby_AttributeData* Parameter;
    int32_t ComparisonOp;
};

__declspec(dllexport) EOS_EResult
EOS_LobbySearch_SetParameter(EOS_HLobbySearch Handle, const EOS_LobbySearch_SetParameterOptions *Options) {
  if (Options && Options->Parameter && Options->Parameter->Key) {
      if (Options->Parameter->ValueType == 3) {
          Log("[Proxy] EOS_LobbySearch_SetParameter: %s = %s", Options->Parameter->Key, Options->Parameter->Value.AsUtf8);
          if (Options->Parameter->Value.AsUtf8) {
              if (strcmp(Options->Parameter->Key, "AppVersion") == 0) {
                  strncpy(g_searchAppVersion, Options->Parameter->Value.AsUtf8, sizeof(g_searchAppVersion) - 1);
                  g_searchAppVersion[sizeof(g_searchAppVersion) - 1] = '\0';
              } else if (strcmp(Options->Parameter->Key, "OnlineRule") == 0) {
                  strncpy(g_searchOnlineRule, Options->Parameter->Value.AsUtf8, sizeof(g_searchOnlineRule) - 1);
                  g_searchOnlineRule[sizeof(g_searchOnlineRule) - 1] = '\0';
              }
          }
      } else {
          Log("[Proxy] EOS_LobbySearch_SetParameter: %s (Type: %d)", Options->Parameter->Key, Options->Parameter->ValueType);
      }
  } else {
      Log("[Proxy] EOS_LobbySearch_SetParameter called (empty)");
  }
  return EOS_Success;
}
__declspec(dllexport) EOS_EResult
EOS_LobbySearch_SetTargetUserId(EOS_HLobbySearch Handle, const void *Options) {
  Log("[Proxy] EOS_LobbySearch_SetTargetUserId called");
  return EOS_Success;
}

typedef void *EOS_HLobbyModification;

struct EOS_LobbyModification_AddAttributeOptions {
    int32_t ApiVersion;
    const EOS_Lobby_AttributeData* Attribute;
    int32_t Visibility;
};

__declspec(dllexport) EOS_EResult EOS_LobbyModification_AddAttribute(
    EOS_HLobbyModification Handle, const EOS_LobbyModification_AddAttributeOptions *Options) {
  if (Options && Options->Attribute && Options->Attribute->Key) {
      if (Options->Attribute->ValueType == 3) {
          Log("[Proxy] EOS_LobbyModification_AddAttribute: %s = %s", Options->Attribute->Key, Options->Attribute->Value.AsUtf8);
          if (Options->Attribute->Value.AsUtf8) {
              if (strcmp(Options->Attribute->Key, "AppVersion") == 0) {
                  strncpy(g_lobbyAppVersion, Options->Attribute->Value.AsUtf8, sizeof(g_lobbyAppVersion) - 1);
                  g_lobbyAppVersion[sizeof(g_lobbyAppVersion) - 1] = '\0';
              } else if (strcmp(Options->Attribute->Key, "OnlineRule") == 0) {
                  strncpy(g_lobbyOnlineRule, Options->Attribute->Value.AsUtf8, sizeof(g_lobbyOnlineRule) - 1);
                  g_lobbyOnlineRule[sizeof(g_lobbyOnlineRule) - 1] = '\0';
              } else if (strcmp(Options->Attribute->Key, "Platform") == 0) {
                  strncpy(g_lobbyPlatform, Options->Attribute->Value.AsUtf8, sizeof(g_lobbyPlatform) - 1);
                  g_lobbyPlatform[sizeof(g_lobbyPlatform) - 1] = '\0';
              } else if (strcmp(Options->Attribute->Key, "EosProductID") == 0) {
                  strncpy(g_lobbyEosProductId, Options->Attribute->Value.AsUtf8, sizeof(g_lobbyEosProductId) - 1);
                  g_lobbyEosProductId[sizeof(g_lobbyEosProductId) -  1] = '\0';
              } else if (strcmp(Options->Attribute->Key, "IsCrossPlatform") == 0) {
                  g_lobbyIsCrossPlatform = _stricmp(Options->Attribute->Value.AsUtf8, "true") == 0 ||
                                           strcmp(Options->Attribute->Value.AsUtf8, "1") == 0;
              }
          }
      } else {
          if (Options->Attribute->ValueType == 1) {
              Log("[Proxy] EOS_LobbyModification_AddAttribute: %s = %lld (int64)",
                  Options->Attribute->Key, (long long)Options->Attribute->Value.AsInt64);
              if (strcmp(Options->Attribute->Key, "WinRate") == 0) g_lobbyWinRate = Options->Attribute->Value.AsInt64;
          } else if (Options->Attribute->ValueType == 0) {
              Log("[Proxy] EOS_LobbyModification_AddAttribute: %s = %d (bool)",
                  Options->Attribute->Key, Options->Attribute->Value.AsBool);
          } else {
              Log("[Proxy] EOS_LobbyModification_AddAttribute: %s (Type: %d)", Options->Attribute->Key, Options->Attribute->ValueType);
          }
      }
  } else {
      Log("[Proxy] EOS_LobbyModification_AddAttribute called (empty)");
  }
  return EOS_Success;
}
__declspec(dllexport) EOS_EResult EOS_LobbyModification_AddMemberAttribute(
    EOS_HLobbyModification Handle, const void *Options) {
  Log("[Proxy] EOS_LobbyModification_AddMemberAttribute called");
  return EOS_Success;
}
__declspec(dllexport) void
EOS_LobbyModification_Release(EOS_HLobbyModification Handle) {
  Log("[Proxy] EOS_LobbyModification_Release called");
}
__declspec(dllexport) EOS_EResult EOS_LobbyModification_RemoveAttribute(
    EOS_HLobbyModification Handle, const void *Options) {
  Log("[Proxy] EOS_LobbyModification_RemoveAttribute called");
  return EOS_Success;
}
__declspec(dllexport) EOS_EResult EOS_LobbyModification_RemoveMemberAttribute(
    EOS_HLobbyModification Handle, const void *Options) {
  Log("[Proxy] EOS_LobbyModification_RemoveMemberAttribute called");
  return EOS_Success;
}
__declspec(dllexport) EOS_EResult EOS_LobbyModification_SetAllowedPlatformIds(
    EOS_HLobbyModification Handle, const void *Options) {
  Log("[Proxy] EOS_LobbyModification_SetAllowedPlatformIds called");
  return EOS_Success;
}
__declspec(dllexport) EOS_EResult EOS_LobbyModification_SetBucketId(
    EOS_HLobbyModification Handle, const void *Options) {
  Log("[Proxy] EOS_LobbyModification_SetBucketId called");
  return EOS_Success;
}
__declspec(dllexport) EOS_EResult EOS_LobbyModification_SetInvitesAllowed(
    EOS_HLobbyModification Handle, const void *Options) {
  Log("[Proxy] EOS_LobbyModification_SetInvitesAllowed called");
  return EOS_Success;
}
__declspec(dllexport) EOS_EResult EOS_LobbyModification_SetMaxMembers(
    EOS_HLobbyModification Handle, const void *Options) {
  Log("[Proxy] EOS_LobbyModification_SetMaxMembers called");
  return EOS_Success;
}
__declspec(dllexport) EOS_EResult EOS_LobbyModification_SetPermissionLevel(
    EOS_HLobbyModification Handle, const void *Options) {
  Log("[Proxy] EOS_LobbyModification_SetPermissionLevel called");
  return EOS_Success;
}

__declspec(dllexport) EOS_EResult EOS_Lobby_UpdateLobbyModification(
    EOS_HLobby Handle, const void *Options,
    EOS_HLobbyModification *OutLobbyModificationHandle) {
  Log("[Proxy] EOS_Lobby_UpdateLobbyModification called");
  if (OutLobbyModificationHandle) *OutLobbyModificationHandle = (EOS_HLobbyModification)5;
  return EOS_Success;
}

__declspec(dllexport) uint64_t
EOS_Lobby_AddNotifyLobbyUpdateReceived(EOS_HLobby Handle, const void *Options,
                                       void *ClientData, void *NotificationFn) {
  Log("[Proxy] EOS_Lobby_AddNotifyLobbyUpdateReceived stored cb=%p", NotificationFn);
  g_lobbyUpdateCb = (EOS_Lobby_OnLobbyUpdateReceivedCallback)NotificationFn;
  g_lobbyUpdateCbData = ClientData;
  return g_notifIdCounter++;
}
__declspec(dllexport) void
EOS_Lobby_RemoveNotifyLobbyUpdateReceived(EOS_HLobby Handle, uint64_t InId) {
  Log("[Proxy] EOS_Lobby_RemoveNotifyLobbyUpdateReceived id=%llu", (unsigned long long)InId);
}

struct EOS_Lobby_Attribute {
    int32_t ApiVersion;
    EOS_Lobby_AttributeData* Data;
    int32_t Visibility;
};

static EOS_Lobby_AttributeData attrData1 = { 1, "AppVersion", {.AsUtf8 = "1.2.0"}, 3 };
static EOS_Lobby_Attribute attr1 = { 1, &attrData1, 0 };
static EOS_Lobby_AttributeData attrData2 = { 1, "OnlineRule", {.AsUtf8 = "Koikoi"}, 3 };
static EOS_Lobby_Attribute attr2 = { 1, &attrData2, 0 };
static EOS_Lobby_AttributeData attrData3 = { 1, "IsCrossPlatform", {.AsUtf8 = "true"}, 3 };
static EOS_Lobby_Attribute attr3 = { 1, &attrData3, 0 };
static EOS_Lobby_AttributeData attrData4 = { 1, "Platform", {.AsUtf8 = "Steam"}, 3 };
static EOS_Lobby_Attribute attr4 = { 1, &attrData4, 0 };
static EOS_Lobby_AttributeData attrData5 = { 1, "WinRate", {.AsInt64 = 0}, 1 };
static EOS_Lobby_Attribute attr5 = { 1, &attrData5, 0 };
static EOS_Lobby_AttributeData attrData6 = { 1, "EosProductID", {.AsUtf8 = ""}, 3 };
static EOS_Lobby_Attribute attr6 = { 1, &attrData6, 0 };

static EOS_Lobby_Attribute* LobbyAttributeByIndex(uint32_t index) {
    attrData1.Value.AsUtf8 = g_searchAppVersion[0] ? g_searchAppVersion : g_lobbyAppVersion;
    attrData2.Value.AsUtf8 = g_searchOnlineRule[0] ? g_searchOnlineRule : g_lobbyOnlineRule;
    attrData3.Value.AsUtf8 = g_lobbyIsCrossPlatform ? "true" : "false";
    attrData4.Value.AsUtf8 = g_lobbyPlatform;
    attrData5.Value.AsInt64 = g_lobbyWinRate;
    attrData6.Value.AsUtf8 = g_lobbyEosProductId[0] ? g_lobbyEosProductId : "DIRECTIP_HOST_PUID";
    switch (index) {
    case 0: return &attr1;
    case 1: return &attr2;
    case 2: return &attr3;
    case 3: return &attr4;
    case 4: return &attr5;
    case 5: return &attr6;
    default: return nullptr;
    }
}

static EOS_Lobby_Attribute* LobbyAttributeByKey(const char* key) {
    if (!key) return nullptr;
    for (uint32_t i = 0; i < 6; ++i) {
        EOS_Lobby_Attribute* attr = LobbyAttributeByIndex(i);
        if (attr && attr->Data && strcmp(attr->Data->Key, key) == 0) return attr;
    }
    return nullptr;
}

__declspec(dllexport) uint32_t EOS_LobbyDetails_GetAttributeCount(
    EOS_HLobbyDetails Handle, const void *Options) {
  Log("[Proxy] EOS_LobbyDetails_GetAttributeCount called -> 6");
  return 6;
}

__declspec(dllexport) EOS_EResult EOS_LobbyDetails_CopyAttributeByIndex(EOS_HLobbyDetails Handle, const void* Options, EOS_Lobby_Attribute** OutAttribute) { 
    if (!Options || !OutAttribute) return EOS_InvalidParameters;
    uint32_t index = ((const uint32_t*)Options)[1];
    EOS_Lobby_Attribute* attr = LobbyAttributeByIndex(index);
    if (!attr) return EOS_NotFound;
    if (attr->Data->ValueType == 3) {
        Log("[Proxy] CopyAttributeByIndex %u -> %s = %s", index, attr->Data->Key, attr->Data->Value.AsUtf8);
    } else if (attr->Data->ValueType == 1) {
        Log("[Proxy] CopyAttributeByIndex %u -> %s = %lld", index, attr->Data->Key, (long long)attr->Data->Value.AsInt64);
    } else {
        Log("[Proxy] CopyAttributeByIndex %u -> %s type=%d", index, attr->Data->Key, attr->Data->ValueType);
    }
    *OutAttribute = attr;
    return EOS_Success;
}

__declspec(dllexport) EOS_EResult EOS_LobbyDetails_CopyAttributeByKey(EOS_HLobbyDetails Handle, const void* Options, EOS_Lobby_Attribute** OutAttribute) { 
    if (!Options || !OutAttribute) return EOS_InvalidParameters;
    const char* key = ((const char* const*)Options)[1];
    EOS_Lobby_Attribute* attr = LobbyAttributeByKey(key);
    if (!attr) {
        Log("[Proxy] CopyAttributeByKey(%s) -> NotFound", key ? key : "NULL");
        return EOS_NotFound;
    }
    if (attr->Data->ValueType == 3) {
        Log("[Proxy] CopyAttributeByKey %s = %s", key, attr->Data->Value.AsUtf8);
    } else if (attr->Data->ValueType == 1) {
        Log("[Proxy] CopyAttributeByKey %s = %lld", key, (long long)attr->Data->Value.AsInt64);
    } else {
        Log("[Proxy] CopyAttributeByKey %s type=%d", key, attr->Data->ValueType);
    }
    *OutAttribute = attr;
    return EOS_Success;
}

__declspec(dllexport) void EOS_Lobby_Attribute_Release(EOS_Lobby_Attribute* Attribute) {
    (void)Attribute;
}

struct EOS_LobbyDetails_CopyMemberInfoOptions {
  int32_t ApiVersion;
  EOS_ProductUserId TargetUserId;
};

struct EOS_LobbyDetails_MemberInfo {
  int32_t ApiVersion;
  EOS_ProductUserId ProductUserId;
};

struct EOS_LobbyDetails_GetMemberByIndexOptions {
  int32_t ApiVersion;
  uint32_t MemberIndex;
};

__declspec(dllexport) EOS_EResult EOS_LobbyDetails_CopyMemberAttributeByIndex(EOS_HLobbyDetails Handle, const void* Options, void** OutAttribute) {
  Log("[Proxy] EOS_LobbyDetails_CopyMemberAttributeByIndex called");
  if(OutAttribute) *OutAttribute = nullptr;
  return EOS_NotFound;
}
__declspec(dllexport) EOS_EResult EOS_LobbyDetails_CopyMemberAttributeByKey(EOS_HLobbyDetails Handle, const void* Options, void** OutAttribute) {
  Log("[Proxy] EOS_LobbyDetails_CopyMemberAttributeByKey called");
  if(OutAttribute) *OutAttribute = nullptr;
  return EOS_NotFound;
}
__declspec(dllexport) EOS_EResult EOS_LobbyDetails_CopyMemberInfo(
    EOS_HLobbyDetails Handle, const void* Options, void** OutMemberInfo) {
  static EOS_LobbyDetails_MemberInfo hostInfo = {1, (EOS_ProductUserId)1};
  static EOS_LobbyDetails_MemberInfo clientInfo = {1, (EOS_ProductUserId)2};
  EOS_ProductUserId targetUserId = LocalProductUserId();
  if (Options) {
    const EOS_LobbyDetails_CopyMemberInfoOptions* opts = (const EOS_LobbyDetails_CopyMemberInfoOptions*)Options;
    if (opts->TargetUserId) targetUserId = opts->TargetUserId;
  }
  EOS_LobbyDetails_MemberInfo* info = nullptr;
  if (targetUserId == HostProductUserId()) info = &hostInfo;
  else if (targetUserId == ClientProductUserId() && (AtomicRead(&g_lobbyHasRemoteMember) || AtomicRead(&g_peerConnected))) info = &clientInfo;

  if (InterlockedIncrement(&g_memberInfoLogCount) <= 60)
    Log("[Proxy] CopyMemberInfo target=%p result=%s", targetUserId, info ? "success" : "notfound");
  if (!info) { if (OutMemberInfo) *OutMemberInfo = nullptr; return EOS_NotFound; }
  if (OutMemberInfo) *OutMemberInfo = info;
  return EOS_Success;
}
__declspec(dllexport) uint32_t EOS_LobbyDetails_GetMemberAttributeCount(
    EOS_HLobbyDetails Handle, const void *Options) {
  return 0;
}
__declspec(dllexport) EOS_ProductUserId EOS_LobbyDetails_GetMemberByIndex(
    EOS_HLobbyDetails Handle, const void *Options) {
  uint32_t memberIndex = 0;
  if (Options) {
    const EOS_LobbyDetails_GetMemberByIndexOptions* opts = (const EOS_LobbyDetails_GetMemberByIndexOptions*)Options;
    memberIndex = opts->MemberIndex;
  }
  EOS_ProductUserId member = nullptr;
  if (memberIndex == 0) member = HostProductUserId();
  else if (memberIndex == 1 && (AtomicRead(&g_lobbyHasRemoteMember) || AtomicRead(&g_peerConnected))) member = ClientProductUserId();

  if (InterlockedIncrement(&g_memberByIndexLogCount) <= 80)
    Log("[Proxy] GetMemberByIndex index=%u -> %p", memberIndex, member);
  return member;
}
__declspec(dllexport) uint32_t
EOS_LobbyDetails_GetMemberCount(EOS_HLobbyDetails Handle, const void *Options) {
  uint32_t count = (AtomicRead(&g_lobbyHasRemoteMember) || AtomicRead(&g_peerConnected)) ? 2 : 1;

  if (InterlockedIncrement(&g_memberCountLogCount) <= 80) Log("[Proxy] GetMemberCount -> %u", count);
  return count;
}
__declspec(dllexport) void
EOS_LobbyDetails_Info_Release(void *LobbyDetailsInfo) {
    (void)LobbyDetailsInfo;
}
__declspec(dllexport) void
EOS_LobbyDetails_MemberInfo_Release(void *LobbyDetailsMemberInfo) {
    (void)LobbyDetailsMemberInfo;
}

__declspec(dllexport) EOS_EResult
EOS_Lobby_CopyLobbyDetailsHandle(EOS_HLobby Handle, const void *Options,
                                 EOS_HLobbyDetails *OutLobbyDetailsHandle) {
  Log("[Proxy] EOS_Lobby_CopyLobbyDetailsHandle called");
  if (OutLobbyDetailsHandle) *OutLobbyDetailsHandle = (EOS_HLobbyDetails)4;
  return EOS_Success;
}
__declspec(dllexport) EOS_EResult EOS_Lobby_CopyLobbyDetailsHandleByInviteId(
    EOS_HLobby Handle, const void *Options,
    EOS_HLobbyDetails *OutLobbyDetailsHandle) {
  Log("[Proxy] EOS_Lobby_CopyLobbyDetailsHandleByInviteId called");
  if (OutLobbyDetailsHandle) *OutLobbyDetailsHandle = (EOS_HLobbyDetails)4;
  return EOS_Success;
}
__declspec(dllexport) EOS_EResult EOS_Lobby_CopyLobbyDetailsHandleByUiEventId(
    EOS_HLobby Handle, const void *Options,
    EOS_HLobbyDetails *OutLobbyDetailsHandle) {
  Log("[Proxy] EOS_Lobby_CopyLobbyDetailsHandleByUiEventId called");
  if (OutLobbyDetailsHandle) *OutLobbyDetailsHandle = (EOS_HLobbyDetails)4;
  return EOS_Success;
}

struct EOS_Lobby_UpdateLobbyCallbackInfo {
  int32_t ResultCode;
  void *ClientData;
  const char *LobbyId;
};
typedef void (*EOS_Lobby_OnUpdateLobbyCallback)(const EOS_Lobby_UpdateLobbyCallbackInfo *Data);
__declspec(dllexport) void
EOS_Lobby_UpdateLobby(EOS_HLobby Handle, const void *Options, void *ClientData,
                      EOS_Lobby_OnUpdateLobbyCallback CompletionDelegate) {
  Log("[Proxy] >> EOS_Lobby_UpdateLobby called");
  if (CompletionDelegate) {
    EOS_Lobby_UpdateLobbyCallbackInfo info = {EOS_Success, ClientData, "DirectIP_Lobby"};
    CompletionDelegate(&info);
  }
  if (g_lobbyUpdateCb) {
    EOS_Lobby_LobbyUpdateReceivedCallbackInfo notif = {0};
    notif.ClientData = g_lobbyUpdateCbData;
    notif.LobbyId = "DirectIP_Lobby";
    g_lobbyUpdateCb(&notif);
  }
  if (g_memberUpdateCb) {
    EOS_Lobby_LobbyMemberUpdateReceivedCallbackInfo notif = {0};
    notif.ClientData = g_memberUpdateCbData;
    notif.LobbyId = "DirectIP_Lobby";
    notif.TargetUserId = RemoteProductUserId();
    g_memberUpdateCb(&notif);
  }
  Log("[Proxy] << EOS_Lobby_UpdateLobby exit");
}

struct EOS_Lobby_DestroyLobbyCallbackInfo {
  int32_t ResultCode;
  void *ClientData;
  const char *LobbyId;
};
typedef void (*EOS_Lobby_OnDestroyLobbyCallback)(const EOS_Lobby_DestroyLobbyCallbackInfo *Data);
__declspec(dllexport) void
EOS_Lobby_DestroyLobby(EOS_HLobby Handle, const void *Options, void *ClientData,
                       EOS_Lobby_OnDestroyLobbyCallback CompletionDelegate) {
  Log("[Proxy] EOS_Lobby_DestroyLobby called");
  LogCallStack("EOS_Lobby_DestroyLobby");
  if (!ShouldSuppressAutoTeardown()) {
    ResetConnectionState();
  }
  if (CompletionDelegate) {
    EOS_Lobby_DestroyLobbyCallbackInfo info = {EOS_Success, ClientData, "DirectIP_Lobby"};
    CompletionDelegate(&info);
  }
}
struct EOS_Lobby_LeaveLobbyCallbackInfo {
  int32_t ResultCode;
  void *ClientData;
  const char *LobbyId;
};
typedef void (*EOS_Lobby_OnLeaveLobbyCallback)(const EOS_Lobby_LeaveLobbyCallbackInfo *Data);
__declspec(dllexport) void
EOS_Lobby_LeaveLobby(EOS_HLobby Handle, const void *Options, void *ClientData,
                     EOS_Lobby_OnLeaveLobbyCallback CompletionDelegate) {
  Log("[Proxy] EOS_Lobby_LeaveLobby called");
  LogCallStack("EOS_Lobby_LeaveLobby");
  if (!ShouldSuppressAutoTeardown()) {
    ResetConnectionState();
  }
  if (CompletionDelegate) {
    EOS_Lobby_LeaveLobbyCallbackInfo info = {EOS_Success, ClientData, "DirectIP_Lobby"};
    CompletionDelegate(&info);
  }
}

__declspec(dllexport) uint64_t EOS_Lobby_AddNotifyLobbyMemberStatusReceived(
    EOS_HLobby Handle, const void* Options, void* ClientData,
    EOS_Lobby_OnLobbyMemberStatusReceivedCallback NotificationFn) {
    Log("[Proxy] EOS_Lobby_AddNotifyLobbyMemberStatusReceived called cb=%p", (void*)NotificationFn);
    g_memberStatusCb = NotificationFn;
    g_memberStatusCbData = ClientData;
    if (AtomicRead(&g_lobbyHasRemoteMember) && NotificationFn) {
        Log("[Proxy] Remote member already present, immediately firing status");
        FireLobbyMemberJoined(RemoteProductUserId());
    }
    return g_notifIdCounter++;
}
__declspec(dllexport) void EOS_Lobby_RemoveNotifyLobbyMemberStatusReceived(EOS_HLobby Handle, uint64_t InId) {
  Log("[Proxy] EOS_Lobby_RemoveNotifyLobbyMemberStatusReceived id=%llu", (unsigned long long)InId);
}
__declspec(dllexport) uint64_t EOS_Lobby_AddNotifyLobbyMemberUpdateReceived(
    EOS_HLobby Handle, const void *Options, void *ClientData,
    void *NotificationFn) {
  Log("[Proxy] EOS_Lobby_AddNotifyLobbyMemberUpdateReceived stored cb=%p", NotificationFn);
  g_memberUpdateCb = (EOS_Lobby_OnLobbyMemberUpdateReceivedCallback)NotificationFn;
  g_memberUpdateCbData = ClientData;
  return g_notifIdCounter++;
}
__declspec(dllexport) void
EOS_Lobby_RemoveNotifyLobbyMemberUpdateReceived(EOS_HLobby Handle, uint64_t InId) {
  Log("[Proxy] EOS_Lobby_RemoveNotifyLobbyMemberUpdateReceived id=%llu", (unsigned long long)InId);
}
__declspec(dllexport) int32_t EOS_Lobby_IsRTCRoomConnected(
    EOS_HLobby Handle, const void *Options, bool *bIsRTCRoomConnected) {
  if (bIsRTCRoomConnected) *bIsRTCRoomConnected = false;
  return EOS_Success;
}
__declspec(dllexport) EOS_EResult
EOS_Lobby_GetRTCRoomName(EOS_HLobby Handle, const void *Options,
                         char *OutBuffer, uint32_t *InOutBufferLength) {
  Log("[Proxy] EOS_Lobby_GetRTCRoomName called");
  if (!OutBuffer || !InOutBufferLength) return EOS_InvalidParameters;
  uint32_t needed = 1;
  if (*InOutBufferLength < needed) {
      *InOutBufferLength = needed;
      return EOS_InvalidParameters;
  }
  OutBuffer[0] = '\0';
  *InOutBufferLength = needed;
  return EOS_Success;
}

// some lobby functions

__declspec(dllexport) EOS_EResult EOS_Lobby_IsLobbyOwner(EOS_HLobby Handle, const void* Options, int32_t* bIsLobbyOwner) {
    Log("[Proxy] EOS_Lobby_IsLobbyOwner called");
    if (bIsLobbyOwner) *bIsLobbyOwner = AtomicRead(&g_isHost) ? 1 : 0;
    return EOS_Success;
}

__declspec(dllexport) uint32_t EOS_Lobby_GetInviteCount(EOS_HLobby Handle, const void* Options) {
    Log("[Proxy] EOS_Lobby_GetInviteCount called -> 0");
    return 0;
}

__declspec(dllexport) EOS_EResult EOS_Lobby_CopyInviteIdByIndex(EOS_HLobby Handle, const void* Options, char* OutBuffer, int32_t* InOutBufferLength) {
    Log("[Proxy] EOS_Lobby_CopyInviteIdByIndex called");
    return EOS_NotFound;
}

__declspec(dllexport) EOS_EResult EOS_Lobby_GetConnectString(EOS_HLobby Handle, const void* Options, char* OutBuffer, uint32_t* InOutBufferLength) {
    Log("[Proxy] EOS_Lobby_GetConnectString called");
    if (!OutBuffer || !InOutBufferLength) return EOS_InvalidParameters;
    const char* connectStr = "DirectIP_Lobby_Connect";
    uint32_t needed = (uint32_t)strlen(connectStr) + 1;
    if (*InOutBufferLength < needed) {
        *InOutBufferLength = needed;
        return EOS_InvalidParameters;
    }
    memcpy(OutBuffer, connectStr, needed);
    *InOutBufferLength = needed;
    return EOS_Success;
}

__declspec(dllexport) EOS_EResult EOS_Lobby_ParseConnectString(EOS_HLobby Handle, const void* Options, char* OutLobbyId, uint32_t* InOutBufferLength) {
    Log("[Proxy] EOS_Lobby_ParseConnectString called");
    if (!OutLobbyId || !InOutBufferLength) return EOS_InvalidParameters;
    const char* lobbyId = "DirectIP_Lobby";
    uint32_t needed = (uint32_t)strlen(lobbyId) + 1;
    if (*InOutBufferLength < needed) {
        *InOutBufferLength = needed;
        return EOS_InvalidParameters;
    }
    memcpy(OutLobbyId, lobbyId, needed);
    *InOutBufferLength = needed;
    return EOS_Success;
}

// Platform mocks

typedef void* EOS_HPlatform;

__declspec(dllexport) EOS_EResult EOS_Platform_GetActiveCountryCode(EOS_HPlatform Handle, const void* Options, char* OutBuffer, int32_t* InOutBufferLength) {
    if (!OutBuffer || !InOutBufferLength) return EOS_InvalidParameters;
    const char* country = "US";
    int32_t needed = (int32_t)strlen(country) + 1;
    if (*InOutBufferLength < needed) {
        *InOutBufferLength = needed;
        return EOS_InvalidParameters;
    }
    memcpy(OutBuffer, country, needed);
    *InOutBufferLength = needed;
    return EOS_Success;
}

__declspec(dllexport) EOS_EResult EOS_Platform_GetActiveLocaleCode(EOS_HPlatform Handle, const void* Options, char* OutBuffer, int32_t* InOutBufferLength) {
    if (!OutBuffer || !InOutBufferLength) return EOS_InvalidParameters;
    const char* locale = "en";
    int32_t needed = (int32_t)strlen(locale) + 1;
    if (*InOutBufferLength < needed) {
        *InOutBufferLength = needed;
        return EOS_InvalidParameters;
    }
    memcpy(OutBuffer, locale, needed);
    *InOutBufferLength = needed;
    return EOS_Success;
}


typedef void* EOS_HPlayerDataStorage;

struct EOS_PlayerDataStorage_QueryFileOptions {
  int32_t ApiVersion;
  EOS_ProductUserId LocalUserId;
  const char* Filename;
};

struct EOS_PlayerDataStorage_QueryFileCallbackInfo {
  EOS_EResult ResultCode;
  void* ClientData;
  EOS_ProductUserId LocalUserId;
  const char* Filename;
};
typedef void (*EOS_PlayerDataStorage_OnQueryFileCompleteCallback)(const EOS_PlayerDataStorage_QueryFileCallbackInfo* Data);

__declspec(dllexport) void EOS_PlayerDataStorage_QueryFile(
    EOS_HPlayerDataStorage Handle, const EOS_PlayerDataStorage_QueryFileOptions* Options,
    void* ClientData, const EOS_PlayerDataStorage_OnQueryFileCompleteCallback CompletionCallback) {
  const char* fname = (Options && Options->Filename) ? Options->Filename : "(null)";
  Log("[Proxy] EOS_PlayerDataStorage_QueryFile called file=%s", fname);
  if (CompletionCallback) {
    EOS_PlayerDataStorage_QueryFileCallbackInfo info = {0};
    info.ResultCode = EOS_NotFound; // no real cloud backend -- behave like an empty save slot
    info.ClientData = ClientData;
    info.LocalUserId = Options ? Options->LocalUserId : LocalProductUserId();
    info.Filename = fname;
    CompletionCallback(&info);
  }
}


typedef void* EOS_HSanctions;

struct EOS_Sanctions_QueryActivePlayerSanctionsOptions {
  int32_t ApiVersion;
  EOS_ProductUserId LocalUserId;
  EOS_ProductUserId TargetUserId;
};

struct EOS_Sanctions_QueryActivePlayerSanctionsCallbackInfo {
  EOS_EResult ResultCode;
  void* ClientData;
  EOS_ProductUserId LocalUserId;
  EOS_ProductUserId TargetUserId;
};
typedef void (*EOS_Sanctions_OnQueryActivePlayerSanctionsCallback)(const EOS_Sanctions_QueryActivePlayerSanctionsCallbackInfo* Data);

__declspec(dllexport) void EOS_Sanctions_QueryActivePlayerSanctions(
    EOS_HSanctions Handle, const EOS_Sanctions_QueryActivePlayerSanctionsOptions* Options,
    void* ClientData, const EOS_Sanctions_OnQueryActivePlayerSanctionsCallback CompletionDelegate) {
  Log("[Proxy] EOS_Sanctions_QueryActivePlayerSanctions called (answered locally, not forwarded to real SDK)");
  if (CompletionDelegate) {
    EOS_Sanctions_QueryActivePlayerSanctionsCallbackInfo info = {0};
    info.ResultCode = EOS_Success; // no sanctions to report
    info.ClientData = ClientData;
    info.LocalUserId = Options ? Options->LocalUserId : LocalProductUserId();
    info.TargetUserId = Options ? Options->TargetUserId : LocalProductUserId();
    CompletionDelegate(&info);
  }
}

struct EOS_Sanctions_GetPlayerSanctionCountOptions {
  int32_t ApiVersion;
  EOS_ProductUserId TargetUserId;
};

__declspec(dllexport) uint32_t EOS_Sanctions_GetPlayerSanctionCount(
    EOS_HSanctions Handle, const EOS_Sanctions_GetPlayerSanctionCountOptions* Options) {
  Log("[Proxy] EOS_Sanctions_GetPlayerSanctionCount called -> 0");
  return 0;
}

struct EOS_Sanctions_CopyPlayerSanctionByIndexOptions {
  int32_t ApiVersion;
  EOS_ProductUserId TargetUserId;
  uint32_t SanctionIndex;
};

__declspec(dllexport) EOS_EResult EOS_Sanctions_CopyPlayerSanctionByIndex(
    EOS_HSanctions Handle, const EOS_Sanctions_CopyPlayerSanctionByIndexOptions* Options, void** OutSanction) {
  Log("[Proxy] EOS_Sanctions_CopyPlayerSanctionByIndex called -> NotFound");
  if (OutSanction) *OutSanction = nullptr;
  return EOS_NotFound;
}


struct EOS_LogMessage {
  const char* Category;
  const char* Message;
  int32_t Level; // EOS_ELogLevel
};
typedef void (*EOS_LogMessageFunc)(const EOS_LogMessage* Message);

// EOS_ELogLevel
#define EOS_LOG_Off          0
#define EOS_LOG_Fatal        1
#define EOS_LOG_Error        2
#define EOS_LOG_Warning      3
#define EOS_LOG_Info         4
#define EOS_LOG_Verbose      5
#define EOS_LOG_VeryVerbose  6

// EOS_ELogCategory
#define EOS_LC_ALL_CATEGORIES 0x7FFFFFFF

typedef EOS_EResult (*EOS_Logging_SetCallback_Fn)(EOS_LogMessageFunc Callback);
typedef EOS_EResult (*EOS_Logging_SetLogLevel_Fn)(int32_t LogCategory, int32_t LogLevel);

static EOS_Logging_SetCallback_Fn Original_EOS_Logging_SetCallback = nullptr;
static EOS_Logging_SetLogLevel_Fn Original_EOS_Logging_SetLogLevel = nullptr;

static EOS_LogMessageFunc g_gameLogCallback = nullptr;
static LONG g_sdkLogCount = 0;

static const char* LogLevelToStr(int32_t level) {
  switch (level) {
    case EOS_LOG_Fatal:   return "FATAL";
    case EOS_LOG_Error:   return "ERROR";
    case EOS_LOG_Warning: return "WARN";
    case EOS_LOG_Info:    return "INFO";
    case EOS_LOG_Verbose: return "VERBOSE";
    case EOS_LOG_VeryVerbose: return "VVERBOSE";
    default:              return "?";
  }
}

void ProxySdkLogCallback(const EOS_LogMessage* Message) {
  if (Message) {
    InterlockedIncrement(&g_sdkLogCount);

    bool isTelemetry = Message->Message &&
        (strstr(Message->Message, "api.epicgames.dev") ||
         strstr(Message->Message, "0.0.0.0") ||
         strstr(Message->Message, "Couldn't connect to server") ||
         strstr(Message->Message, "Connection refused") ||
         strstr(Message->Message, "Failed to connect to the backend") ||
         strstr(Message->Message, "EOS_NoConnection"));

    if (isTelemetry) {
      if (telemetryFile) {
        SYSTEMTIME st;
        GetLocalTime(&st);
        fprintf(telemetryFile, "[%02u:%02u:%02u.%03u] [SDK/%s] %s: %s\n",
                st.wHour, st.wMinute, st.wSecond, st.wMilliseconds,
                Message->Category ? Message->Category : "?",
                LogLevelToStr(Message->Level),
                Message->Message ? Message->Message : "");
        fflush(telemetryFile);
      }
    } else {
      Log("[SDK/%s] %s: %s",
          Message->Category ? Message->Category : "?",
          LogLevelToStr(Message->Level),
          Message->Message ? Message->Message : "");
    }
  }
  if (g_gameLogCallback) g_gameLogCallback(Message);
}

__declspec(dllexport) EOS_EResult EOS_Logging_SetCallback(EOS_LogMessageFunc Callback) {
  Log("[Proxy] EOS_Logging_SetCallback called by game, cb=%p (chaining through proxy sink)", (void*)Callback);
  g_gameLogCallback = Callback;
  if (Original_EOS_Logging_SetCallback) {
    return Original_EOS_Logging_SetCallback(ProxySdkLogCallback);
  }
  return EOS_Success;
}

__declspec(dllexport) EOS_EResult EOS_Logging_SetLogLevel(int32_t LogCategory, int32_t LogLevel) {
  Log("[Proxy] EOS_Logging_SetLogLevel called by game: category=%d level=%d (forwarding as-is)",
      LogCategory, LogLevel);
  if (Original_EOS_Logging_SetLogLevel) {
    return Original_EOS_Logging_SetLogLevel(LogCategory, LogLevel);
  }
  return EOS_Success;
}

// EOS_Platform_Tick
typedef void (*EOS_Platform_Tick_Fn)(void* Handle);
static EOS_Platform_Tick_Fn Original_EOS_Platform_Tick = nullptr;
static LONG g_tickCount = 0;

__declspec(dllexport) void EOS_Platform_Tick(void* Handle) {
    if (Original_EOS_Platform_Tick) {
        Original_EOS_Platform_Tick(Handle);
    }

    LONG tick = InterlockedIncrement(&g_tickCount);
    size_t pending = 0;
    {
        EnterCriticalSection(&g_callbackQueueCs);
        pending = g_callbackQueue.size();
        LeaveCriticalSection(&g_callbackQueueCs);
    }
    if (pending > 0) {
        Log("[Tick] #%ld: %zu callback(s) pending, draining now", (long)tick, pending);
    }
    if (tick % 300 == 1) {
        DWORD now = GetTickCount();
        DWORD msSinceGoodPkt = (g_lastGoodPacketTime && g_lastGoodPacketTime != -1)
            ? (now - g_lastGoodPacketTime) : (DWORD)-1;
        Log("[Tick] heartbeat #%ld (isHost=%d initialized=%d peerConnected=%d "
            "lobbyHasRemoteMember=%d supp=%lu msSinceGoodPkt=%lu socket=%s)",
            (long)tick, (int)AtomicRead(&g_isHost), (int)AtomicRead(&g_initialized),
            (int)AtomicRead(&g_peerConnected), (int)AtomicRead(&g_lobbyHasRemoteMember),
            (unsigned long)AtomicRead(&g_autoTeardownSuppressed),
            (unsigned long)msSinceGoodPkt,
            (udpSocket != INVALID_SOCKET) ? "open" : "closed");
    }

    DrainCallbackQueue();
    TickRetryPendingEvents();
}

} // extern "C"

#include "proxy_pointers.h"

HMODULE hOriginalDll = NULL;

void InitLogFiles() {
  InitializeCriticalSection(&g_packetCs);
  char path[MAX_PATH];
  GetModuleFileNameA(NULL, path, MAX_PATH);
  char *lastSlash = strrchr(path, '\\');
  if (!lastSlash) lastSlash = strrchr(path, '/');
  if (lastSlash) *(lastSlash + 1) = '\0';
  else path[0] = '\0';
  char logPath[MAX_PATH];
  char pktPath[MAX_PATH];
  char telPath[MAX_PATH];
  strcpy(logPath, path); strcat(logPath, "proxy_p2p.log");
  strcpy(pktPath, path); strcat(pktPath, "proxy_packets.log");
  strcpy(telPath, path); strcat(telPath, "telemetry_logs.log");
  logFile = fopen(logPath, "a");
  packetFile = fopen(pktPath, "a");
  telemetryFile = fopen(telPath, "a");
  if (logFile) {

    setvbuf(logFile, NULL, _IOFBF, 1 << 16);
    SYSTEMTIME st;
    GetLocalTime(&st);
    fprintf(logFile, "\n=========================================\n");
    fprintf(logFile, "Proxy DLL attached at %02u:%02u:%02u\n", st.wHour, st.wMinute, st.wSecond);
    fflush(logFile); // flush this one banner line immediately so attach is visible even if it crashes right after
  }
  if (packetFile) {
    SYSTEMTIME st;
    GetLocalTime(&st);
    fprintf(packetFile, "\n=========================================\n");
    fprintf(packetFile, "Packet dump started at %02u:%02u:%02u\n", st.wHour, st.wMinute, st.wSecond);
    fflush(packetFile);
  }
  if (telemetryFile) {
    SYSTEMTIME st;
    GetLocalTime(&st);
    fprintf(telemetryFile, "\n========== Telemetry Log %02u:%02u:%02u ==========\n",
            st.wHour, st.wMinute, st.wSecond);
    fflush(telemetryFile);
  }
}

void CloseLogFiles() {
  if (logFile) { fclose(logFile); logFile = NULL; }
  if (packetFile) { fclose(packetFile); packetFile = NULL; }
  if (telemetryFile) { fclose(telemetryFile); telemetryFile = NULL; }
  DeleteCriticalSection(&g_packetCs);
}

// === WinSock Telemetry Blocking Hooks ===

typedef int (WSAAPI *GetAddrInfoFn)(PCSTR, PCSTR, const ADDRINFOA *, PADDRINFOA *);
typedef int (WSAAPI *ConnectFn)(SOCKET, const struct sockaddr *, int);

static GetAddrInfoFn Real_GetAddrInfo = NULL;
static ConnectFn Real_Connect = NULL;

static bool PatchIat(HMODULE module, const char *sourceDll, const char *funcName,
                     WORD ordinal, void *hook, void **orig) {
  if (!module) return false;
  PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)module;
  if (dos->e_magic != 0x5A4D) return false;
  PIMAGE_NT_HEADERS nt = (PIMAGE_NT_HEADERS)((char*)module + dos->e_lfanew);
  if (nt->Signature != 0x00004550) return false;
  PIMAGE_DATA_DIRECTORY importDir = &nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
  if (!importDir->VirtualAddress || !importDir->Size) return false;
  static const uintptr_t kOrdFlag = 0x8000000000000000ULL;
  PIMAGE_IMPORT_DESCRIPTOR imports = (PIMAGE_IMPORT_DESCRIPTOR)((char*)module + importDir->VirtualAddress);
  for (; imports->Name; imports++) {
    char *dllName = (char*)module + imports->Name;
    if (_stricmp(dllName, sourceDll) != 0) continue;
    PIMAGE_THUNK_DATA thunk = (PIMAGE_THUNK_DATA)((char*)module + imports->FirstThunk);
    PIMAGE_THUNK_DATA origThunk = (PIMAGE_THUNK_DATA)((char*)module + imports->OriginalFirstThunk);
    for (; thunk->u1.Function; thunk++, origThunk++) {
      bool match = false;
      if (funcName) {
        if (origThunk->u1.Ordinal & kOrdFlag) continue;
        PIMAGE_IMPORT_BY_NAME imp = (PIMAGE_IMPORT_BY_NAME)((char*)module + origThunk->u1.AddressOfData);
        if (strcmp(imp->Name, funcName) == 0) match = true;
      } else {
        if (!(origThunk->u1.Ordinal & kOrdFlag)) continue;
        if ((origThunk->u1.Ordinal & 0xFFFF) == ordinal) match = true;
      }
      if (!match) continue;
      if (orig) *orig = (void*)(uintptr_t)thunk->u1.Function;
      DWORD old;
      if (VirtualProtect(&thunk->u1.Function, sizeof(void*), PAGE_READWRITE, &old)) {
        thunk->u1.Function = (uintptr_t)(LONG_PTR)hook;
        VirtualProtect(&thunk->u1.Function, sizeof(void*), old, &old);
        return true;
      }
      return false;
    }
  }
  return false;
}

static int my_tolower(int c) {
  return (c >= 'A' && c <= 'Z') ? c + 32 : c;
}

static bool Listed(const char *value, const char *list) {
  if (!value || !list || !list[0]) return false;
  const char *p = list;
  size_t vlen = strlen(value);
  while (*p) {
    while (*p == ' ' || *p == ',' || *p == ';') p++;
    if (!*p) break;
    const char *start = p;
    while (*p && *p != ' ' && *p != ',' && *p != ';') p++;
    size_t len = p - start;
    if (len != vlen) continue;
    bool match = true;
    for (size_t i = 0; i < len; i++) {
      if (my_tolower((unsigned char)value[i]) != my_tolower((unsigned char)start[i])) {
        match = false;
        break;
      }
    }
    if (match) return true;
  }
  return false;
}

static int WSAAPI Hook_GetAddrInfo(PCSTR name, PCSTR svc, const ADDRINFOA *hints, PADDRINFOA *res) {
  int ret = Real_GetAddrInfo(name, svc, hints, res);
  if (ret != 0) return ret;
  if (name && Listed(name, g_blockedDomains)) {
    if (g_telemetryBlock) {
      LogTelemetry("[Telemetry] Redirected DNS: %s -> 0.0.0.0 (block via connect)", name);
      ADDRINFOA *cur = *res;
      while (cur) {
        if (cur->ai_family == AF_INET && cur->ai_addr) {
          ((struct sockaddr_in *)cur->ai_addr)->sin_addr.s_addr = htonl(INADDR_ANY);
        }
        cur = cur->ai_next;
      }
    } else if (g_dumpTraffic) {
      LogTelemetry("[Telemetry] Traffic: DNS lookup %s (ALLOWED, dump mode)", name);
    }
  }
  return ret;
}

static int WSAAPI Hook_Connect(SOCKET s, const struct sockaddr *addr, int addrlen) {
  if (addr && addr->sa_family == AF_INET) {
    struct sockaddr_in *sin = (struct sockaddr_in *)addr;
    char ip[16];
    strcpy(ip, inet_ntoa(sin->sin_addr));
    bool isBlocked = g_telemetryBlock && Listed(ip, g_blockedIPs);
    if (isBlocked) {
      LogTelemetry("[Telemetry] Blocked connect to %s:%d", ip, ntohs(sin->sin_port));
      WSASetLastError(WSAECONNREFUSED);
      return SOCKET_ERROR;
    }
    if (g_dumpTraffic && Listed(ip, g_blockedIPs)) {
      LogTelemetry("[Telemetry] Traffic: connect to %s:%d (ALLOWED, dump mode)", ip, ntohs(sin->sin_port));
    }
  }
  return Real_Connect(s, addr, addrlen);
}

BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
  switch (ul_reason_for_call) {
  case DLL_PROCESS_ATTACH: {
    DisableThreadLibraryCalls(hModule);
    InitializeCriticalSection(&g_hostEventCs);
    InitializeCriticalSection(&g_socketCs);
    InitCallbackQueue();
    g_stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    char path[MAX_PATH];
    GetModuleFileNameA(hModule, path, MAX_PATH);
    strncpy(g_dllDir, path, MAX_PATH);
    char *dirSlash = strrchr(g_dllDir, '\\');
    if (!dirSlash) dirSlash = strrchr(g_dllDir, '/');
    if (dirSlash) *(dirSlash + 1) = '\0';
    else g_dllDir[0] = '\0';
    InitLogFiles();

    // Load telemetry config
    {
      char iniPath2[MAX_PATH];
      snprintf(iniPath2, sizeof(iniPath2), "%sdirectip.ini", g_dllDir);
      char telemetryStr[8] = "true";
      GetPrivateProfileStringA("Network", "Telemetry", "true", telemetryStr, sizeof(telemetryStr), iniPath2);
      g_telemetryBlock = (_stricmp(telemetryStr, "false") == 0 || strcmp(telemetryStr, "0") == 0);
      char dumpStr[8] = "false";
      GetPrivateProfileStringA("Telemetry", "DumpTraffic", "false", dumpStr, sizeof(dumpStr), iniPath2);
      g_dumpTraffic = (_stricmp(dumpStr, "true") == 0 || strcmp(dumpStr, "1") == 0);
      GetPrivateProfileStringA("Telemetry", "BlockDomains", "api.epicgames.dev", g_blockedDomains, sizeof(g_blockedDomains), iniPath2);
      GetPrivateProfileStringA("Telemetry", "BlockIPs", "104.18.125.108", g_blockedIPs, sizeof(g_blockedIPs), iniPath2);
      // Do NOT add 0.0.0.0 to the block list — let connect fail naturally
      // so the EOS SDK's HttpManagerThread doesn't crash on synthetic errors.
      LogTelemetry("[Telemetry] Config: block=%d dump=%d domains=%s ips=%s",
          (int)g_telemetryBlock, (int)g_dumpTraffic, g_blockedDomains, g_blockedIPs);
    }

    char *lastSlash = strrchr(path, '\\');
    if (!lastSlash) lastSlash = strrchr(path, '/');
    if (lastSlash) {
      *(lastSlash + 1) = '\0';
      strcat(path, "EOSSDK-Win64-Shipping_orig.dll");
    } else {
      strcpy(path, "EOSSDK-Win64-Shipping_orig.dll");
    }
    Log("[Proxy] DLL Loaded. Redirecting P2P to UDP.");
    Log("[Proxy] DLL directory: %s", g_dllDir);
    Log("[Proxy] Trying to load original: %s", path);
    hOriginalDll = LoadLibraryA(path);
    if (hOriginalDll) {
      LoadOriginalFunctions(hOriginalDll);
      Original_EOS_Platform_Tick = (EOS_Platform_Tick_Fn)GetProcAddress(hOriginalDll, "EOS_Platform_Tick");
      Original_EOS_Logging_SetCallback = (EOS_Logging_SetCallback_Fn)GetProcAddress(hOriginalDll, "EOS_Logging_SetCallback");
      Original_EOS_Logging_SetLogLevel = (EOS_Logging_SetLogLevel_Fn)GetProcAddress(hOriginalDll, "EOS_Logging_SetLogLevel");
      if (Original_EOS_Logging_SetCallback) {
        EOS_EResult r = Original_EOS_Logging_SetCallback(ProxySdkLogCallback);
        Log("[Proxy] Registered proxy SDK log sink, result=%d", r);
      } else {
        Log("[Proxy] !! EOS_Logging_SetCallback not found in original DLL -- no internal SDK logs available");
      }
      if (Original_EOS_Logging_SetLogLevel) {
        EOS_EResult r = Original_EOS_Logging_SetLogLevel(EOS_LC_ALL_CATEGORIES, EOS_LOG_VeryVerbose);
        Log("[Proxy] Forced SDK log level to VeryVerbose (all categories), result=%d", r);
      } else {
        Log("[Proxy] !! EOS_Logging_SetLogLevel not found in original DLL -- log verbosity left at default");
      }
      Log("[Proxy] Original DLL loaded successfully.");
    } else {
      Log("[Proxy] Failed to load original DLL! Error: %d", GetLastError());
    }

    // Patch EOS SDK IAT for ws2_32 telemetry hooks
    bool okGa = PatchIat(hOriginalDll, "ws2_32.dll", "getaddrinfo", 0,
                         (void*)Hook_GetAddrInfo, (void**)&Real_GetAddrInfo);
    bool okCo = PatchIat(hOriginalDll, "ws2_32.dll", NULL, 4,
                         (void*)Hook_Connect, (void**)&Real_Connect);
    LogTelemetry("[Telemetry] IAT hooks: getaddrinfo=%s(%p) connect=%s(%p)",
        okGa ? "OK" : "FAIL", (void*)Real_GetAddrInfo,
        okCo ? "OK" : "FAIL", (void*)Real_Connect);

    break;
  }
  case DLL_PROCESS_DETACH:
    if (AtomicRead(&g_initialized)) WSACleanup();
    Log("[Proxy] DLL detaching. Cleaning up.");
    if (g_stopEvent) {
        SetEvent(g_stopEvent);
        CloseHandle(g_stopEvent);
        g_stopEvent = NULL;
    }
    ShutdownCallbackQueue();
    DeleteCriticalSection(&g_hostEventCs);
    DeleteCriticalSection(&g_socketCs);
    CloseLogFiles();
    if (hOriginalDll) FreeLibrary(hOriginalDll);
    break;
  }
  return TRUE;
}