Refactor, GLFW->SDL
[vg.git] / vg_steam.h
diff --git a/vg_steam.h b/vg_steam.h
new file mode 100644 (file)
index 0000000..b39da94
--- /dev/null
@@ -0,0 +1,651 @@
+#ifndef VG_STEAM_H
+#define VG_STEAM_H
+
+#include "vg.h"
+#include "vg_log.h"
+
+/* 
+ * TODO: Combine interfaces and stuff here instead of having them in client code
+ */
+
+#if defined(__linux__) || defined(__APPLE__) 
+/* 
+ * The 32-bit version of gcc has the alignment requirement for u64 and double 
+ * set to 4 meaning that even with #pragma pack(8) these types will only be 
+ * four-byte aligned. The 64-bit version of gcc has the alignment requirement 
+ * for these types set to 8 meaning that unless we use #pragma pack(4) our 
+ * structures will get bigger. The 64-bit structure packing has to match the 
+ * 32-bit structure packing for each platform.
+ */
+ #define VALVE_CALLBACK_PACK_SMALL
+;
+ #pragma pack( push, 4 )
+#else
+ #define VALVE_CALLBACK_PACK_LARGE
+ #pragma pack( push, 8 )
+#endif
+
+typedef i32    HSteamPipe;
+typedef i32    HSteamUser;
+
+typedef int E_iCallBack_t;
+
+typedef u64    u64_steamid;
+typedef u64 SteamAPICall_t;
+
+typedef u32 AppId_t;
+const AppId_t k_uAppIdInvalid = 0x0;
+
+typedef u32 DepotId_t;
+const DepotId_t k_uDepotIdInvalid = 0x0;
+
+typedef u32 RTime32;
+
+enum { k_iSteamUserCallbacks = 100 };
+enum { k_iSteamGameServerCallbacks = 200 };
+enum { k_iSteamFriendsCallbacks = 300 };
+enum { k_iSteamBillingCallbacks = 400 };
+enum { k_iSteamMatchmakingCallbacks = 500 };
+enum { k_iSteamContentServerCallbacks = 600 };
+enum { k_iSteamUtilsCallbacks = 700 };
+enum { k_iClientFriendsCallbacks = 800 };
+enum { k_iClientUserCallbacks = 900 };
+enum { k_iSteamAppsCallbacks = 1000 };
+enum { k_iSteamUserStatsCallbacks = 1100 };
+enum { k_iSteamNetworkingCallbacks = 1200 };
+enum { k_iSteamNetworkingSocketsCallbacks = 1220 };
+enum { k_iSteamNetworkingMessagesCallbacks = 1250 };
+enum { k_iSteamNetworkingUtilsCallbacks = 1280 };
+enum { k_iClientRemoteStorageCallbacks = 1300 };
+enum { k_iClientDepotBuilderCallbacks = 1400 };
+enum { k_iSteamGameServerItemsCallbacks = 1500 };
+enum { k_iClientUtilsCallbacks = 1600 };
+enum { k_iSteamGameCoordinatorCallbacks = 1700 };
+enum { k_iSteamGameServerStatsCallbacks = 1800 };
+enum { k_iSteam2AsyncCallbacks = 1900 };
+enum { k_iSteamGameStatsCallbacks = 2000 };
+enum { k_iClientHTTPCallbacks = 2100 };
+enum { k_iClientScreenshotsCallbacks = 2200 };
+enum { k_iSteamScreenshotsCallbacks = 2300 };
+enum { k_iClientAudioCallbacks = 2400 };
+enum { k_iClientUnifiedMessagesCallbacks = 2500 };
+enum { k_iSteamStreamLauncherCallbacks = 2600 };
+enum { k_iClientControllerCallbacks = 2700 };
+enum { k_iSteamControllerCallbacks = 2800 };
+enum { k_iClientParentalSettingsCallbacks = 2900 };
+enum { k_iClientDeviceAuthCallbacks = 3000 };
+enum { k_iClientNetworkDeviceManagerCallbacks = 3100 };
+enum { k_iClientMusicCallbacks = 3200 };
+enum { k_iClientRemoteClientManagerCallbacks = 3300 };
+enum { k_iClientUGCCallbacks = 3400 };
+enum { k_iSteamStreamClientCallbacks = 3500 };
+enum { k_IClientProductBuilderCallbacks = 3600 };
+enum { k_iClientShortcutsCallbacks = 3700 };
+enum { k_iClientRemoteControlManagerCallbacks = 3800 };
+enum { k_iSteamAppListCallbacks = 3900 };
+enum { k_iSteamMusicCallbacks = 4000 };
+enum { k_iSteamMusicRemoteCallbacks = 4100 };
+enum { k_iClientVRCallbacks = 4200 };
+enum { k_iClientGameNotificationCallbacks = 4300 }; 
+enum { k_iSteamGameNotificationCallbacks = 4400 }; 
+enum { k_iSteamHTMLSurfaceCallbacks = 4500 };
+enum { k_iClientVideoCallbacks = 4600 };
+enum { k_iClientInventoryCallbacks = 4700 };
+enum { k_iClientBluetoothManagerCallbacks = 4800 };
+enum { k_iClientSharedConnectionCallbacks = 4900 };
+enum { k_ISteamParentalSettingsCallbacks = 5000 };
+enum { k_iClientShaderCallbacks = 5100 };
+enum { k_iSteamGameSearchCallbacks = 5200 };
+enum { k_iSteamPartiesCallbacks = 5300 };
+enum { k_iClientPartiesCallbacks = 5400 };
+enum { k_iSteamSTARCallbacks = 5500 };
+enum { k_iClientSTARCallbacks = 5600 };
+enum { k_iSteamRemotePlayCallbacks = 5700 };
+enum { k_iClientCompatCallbacks = 5800 };
+enum { k_iSteamChatCallbacks = 5900 };
+
+// General result codes
+typedef enum EResult
+{
+       k_EResultNone = 0,                                      // no result
+       k_EResultOK     = 1,                                            // success
+       k_EResultFail = 2,                                      // generic failure 
+       k_EResultNoConnection = 3,                      // no/failed network connection
+//     k_EResultNoConnectionRetry = 4, // OBSOLETE - removed
+       k_EResultInvalidPassword = 5,           // password/ticket is invalid
+       k_EResultLoggedInElsewhere = 6, // same user logged in elsewhere
+       k_EResultInvalidProtocolVer = 7,        // protocol version is incorrect
+       k_EResultInvalidParam = 8,                      // a parameter is incorrect
+       k_EResultFileNotFound = 9,                      // file was not found
+       k_EResultBusy = 10,                                     // called method busy - action not taken
+       k_EResultInvalidState = 11,             // called object was in an invalid state
+       k_EResultInvalidName = 12,                 // name is invalid
+       k_EResultInvalidEmail = 13,             // email is invalid
+       k_EResultDuplicateName = 14,            // name is not unique
+       k_EResultAccessDenied = 15,             // access is denied
+       k_EResultTimeout = 16,                     // operation timed out
+       k_EResultBanned = 17,                           // VAC2 banned
+       k_EResultAccountNotFound = 18,  // account not found
+       k_EResultInvalidSteamID = 19,           // steamID is invalid
+       k_EResultServiceUnavailable = 20,// The requested service is currently 
+                                    // unavailable
+       k_EResultNotLoggedOn = 21,       // The user is not logged on
+       k_EResultPending = 22,                          // Request is pending (may be in process, or
+                                    // waiting on third party)
+       k_EResultEncryptionFailure = 23,                // Encryption or Decryption failed
+       k_EResultInsufficientPrivilege = 24,// Insufficient privilege
+       k_EResultLimitExceeded = 25,                    // Too much of a good thing
+       k_EResultRevoked = 26,                          // Access has been revoked (used for revoked
+                                    // guest passes)
+       k_EResultExpired = 27,                          // License/Guest pass the user is trying to 
+                                    // access is expired
+       k_EResultAlreadyRedeemed = 28,  // Guest pass has already been redeemed by 
+                                    // account, cannot be acked again
+       k_EResultDuplicateRequest = 29, // The request is a duplicate and the action
+                                    // has already occurred in the past, ignored
+                                    // this time
+       k_EResultAlreadyOwned = 30,             // All the games in this guest pass 
+                                    // redemption request are already owned by 
+                                    // the user
+       k_EResultIPNotFound = 31,                       // IP address not found
+       k_EResultPersistFailed = 32,            // failed to write change to the data store
+       k_EResultLockingFailed = 33,            // failed to acquire access lock for this 
+                                    // operation
+       k_EResultLogonSessionReplaced = 34,
+       k_EResultConnectFailed = 35,
+       k_EResultHandshakeFailed = 36,
+       k_EResultIOFailure = 37,
+       k_EResultRemoteDisconnect = 38,
+       k_EResultShoppingCartNotFound = 39,     // failed to find the shopping cart 
+                                       // requested
+       k_EResultBlocked = 40,                                  // a user didn't allow it
+       k_EResultIgnored = 41,                                  // target is ignoring sender
+       k_EResultNoMatch = 42,                                  // nothing matching the request found
+       k_EResultAccountDisabled = 43,
+       k_EResultServiceReadOnly = 44,          // this service is not accepting content 
+                                       // changes right now
+       k_EResultAccountNotFeatured = 45,       // account doesn't have value, so this 
+                                       // feature isn't available
+       k_EResultAdministratorOK = 46,          // allowed to take this action, but only 
+                                       // because requester is admin
+       k_EResultContentVersion = 47,                   // A Version mismatch in content 
+                                       // transmitted within the Steam protocol.
+       k_EResultTryAnotherCM = 48,                     // The current CM can't service the user 
+                                       // making a request, user should try 
+                                       // another.
+       k_EResultPasswordRequiredToKickSession = 49, // You are already logged in 
+                                       // elsewhere, this cached credential 
+                                       // login has failed.
+       k_EResultAlreadyLoggedInElsewhere = 50, // You are already logged in 
+                                           // elsewhere, you must wait
+       k_EResultSuspended = 51,   // Long running operation (content download) 
+                              // suspended/paused
+       k_EResultCancelled = 52,   // Operation canceled (typically by user: 
+                              // content download)
+       k_EResultDataCorruption = 53,   // Operation canceled because data is ill 
+                                 // formed or unrecoverable
+       k_EResultDiskFull = 54,                 // Operation canceled - not enough disk space.
+       k_EResultRemoteCallFailed = 55, // an remote call or IPC call failed
+       k_EResultPasswordUnset = 56,            // Password could not be verified as it's 
+                                    // unset server side
+       k_EResultExternalAccountUnlinked = 57,  // External account (PSN, Facebook...)
+                                          // is not linked to a Steam account
+       k_EResultPSNTicketInvalid = 58,                 // PSN ticket was invalid
+       k_EResultExternalAccountAlreadyLinked = 59,     // External account (PSN, 
+         // Facebook...) is already linked to some other account, 
+         // must explicitly request to replace/delete the link first
+       k_EResultRemoteFileConflict = 60, // The sync cannot resume due to a conflict
+                                     // between the local and remote files
+       k_EResultIllegalPassword = 61,   // The requested new password is not legal
+       k_EResultSameAsPreviousValue = 62,// new value is the same as the old one ( 
+                                     // secret question and answer )
+       k_EResultAccountLogonDenied = 63, // account login denied due to 2nd factor 
+                                     // authentication failure
+       k_EResultCannotUseOldPassword = 64,     // The requested new password is not 
+                                       // legal
+       k_EResultInvalidLoginAuthCode = 65,     // account login denied due to auth code 
+                                       // invalid
+       k_EResultAccountLogonDeniedNoMail = 66, // account login denied due to 2nd 
+                                           // factor auth failure - and no mail 
+                                           // has been sent
+       k_EResultHardwareNotCapableOfIPT = 67,
+       k_EResultIPTInitError = 68,
+       k_EResultParentalControlRestricted = 69,// operation failed due to parental 
+                                           // control restrictions for current 
+                                           // user
+       k_EResultFacebookQueryError = 70,                // Facebook query returned an error
+       k_EResultExpiredLoginAuthCode = 71,              // account login denied due to auth 
+                                           // code expired
+       k_EResultIPLoginRestrictionFailed = 72,
+       k_EResultAccountLockedDown = 73,
+       k_EResultAccountLogonDeniedVerifiedEmailRequired = 74,
+       k_EResultNoMatchingURL = 75,
+       k_EResultBadResponse = 76,              // parse failure, missing field, etc.
+       k_EResultRequirePasswordReEntry = 77, // The user cannot complete the action 
+                                         // until they re-enter their password
+       k_EResultValueOutOfRange = 78,  // the value entered is outside the 
+                                    // acceptable range
+       k_EResultUnexpectedError = 79,  // something happened that we didn't expect
+                                    // to ever happen
+       k_EResultDisabled = 80,                         // The requested service has been configured
+                                    // to be unavailable
+       k_EResultInvalidCEGSubmission = 81,     // The set of files submitted to the CEG 
+                                       // server are not valid !
+       k_EResultRestrictedDevice = 82,         // The device being used is not allowed 
+                                       // to perform this action
+       k_EResultRegionLocked = 83,                     // The action could not be complete 
+                                       // because it is region restricted
+       k_EResultRateLimitExceeded = 84,        // Temporary rate limit exceeded, try 
+                                    // again later, different from 
+                                    // k_EResultLimitExceeded which may be 
+                                    // permanent
+       k_EResultAccountLoginDeniedNeedTwoFactor = 85, // Need two-factor code to 
+                                                  // login
+       k_EResultItemDeleted = 86,      // The thing we're trying to access has been 
+                              // deleted
+       k_EResultAccountLoginDeniedThrottle = 87,       // login attempt failed, try to 
+                                             // throttle response to possible 
+                                             // attacker
+       k_EResultTwoFactorCodeMismatch = 88,            // two factor code mismatch
+       k_EResultTwoFactorActivationCodeMismatch = 89,  // activation code for 
+                                                   // two-factor didn't match
+       k_EResultAccountAssociatedToMultiplePartners = 90,      // account has been 
+                                          // associated with multiple partners
+       k_EResultNotModified = 91,                                      // data not modified
+       k_EResultNoMobileDevice = 92,   // the account does not have a mobile 
+                                 // device associated with it
+       k_EResultTimeNotSynced = 93,    // the time presented is out of range or 
+                                 // tolerance
+       k_EResultSmsCodeFailed = 94,    // SMS code failure (no match, none pending, 
+                                 // etc.)
+       k_EResultAccountLimitExceeded = 95,     // Too many accounts access this resource
+       k_EResultAccountActivityLimitExceeded = 96,// Too many changes to 
+                                              // this account
+       k_EResultPhoneActivityLimitExceeded = 97,       // Too many changes to this phone
+       k_EResultRefundToWallet = 98,   // Cannot refund to payment method, must use 
+                                 // wallet
+       k_EResultEmailSendFailure = 99, // Cannot send an email
+       k_EResultNotSettled = 100,      // Can't perform operation till payment 
+                              // has settled
+       k_EResultNeedCaptcha = 101,// Needs to provide a valid captcha
+       k_EResultGSLTDenied = 102,      // a game server login token owned by this token's
+                              // owner has been banned
+       k_EResultGSOwnerDenied = 103,   // game server owner is denied for other reason
+                        // (account lock, community ban, vac ban, missing phone)
+       k_EResultInvalidItemType = 104,// the type of thing we were requested to act 
+                                  // on is invalid
+       k_EResultIPBanned = 105,// the ip address has been banned from taking this 
+                           // action
+       k_EResultGSLTExpired = 106,// this token has expired from disuse; can be 
+                              // reset for use
+       k_EResultInsufficientFunds = 107,// user doesn't have enough wallet funds to 
+                                    // complete the action
+       k_EResultTooManyPending = 108,  // There are too many of this thing pending 
+                                    // already
+       k_EResultNoSiteLicensesFound = 109,     // No site licenses found
+       k_EResultWGNetworkSendExceeded = 110,// the WG couldn't send a response
+                              // because we exceeded max network send size
+       k_EResultAccountNotFriends = 111, // the user is not mutually friends
+       k_EResultLimitedUserAccount = 112,// the user is limited
+       k_EResultCantRemoveItem = 113,   // item can't be removed
+       k_EResultAccountDeleted = 114,   // account has been deleted
+       k_EResultExistingUserCancelledLicense = 115,    
+   // A license for this already exists, but cancelled
+       k_EResultCommunityCooldown = 116,       // access is denied because of a 
+            // community cooldown (probably from support profile data resets)
+       k_EResultNoLauncherSpecified = 117,     // No launcher was specified, but a 
+   // launcher was needed to choose correct realm for operation.
+       k_EResultMustAgreeToSSA = 118,// User must agree to china SSA or global SSA 
+                                 // before login
+       k_EResultLauncherMigrated = 119,        // The specified launcher type is no longer 
+                           // supported; the user should be directed elsewhere
+       k_EResultSteamRealmMismatch = 120,      // The user's realm does not match the 
+                                       // realm of the requested resource
+       k_EResultInvalidSignature = 121,                        // signature check did not match
+       k_EResultParseFailure = 122,            // Failed to parse input
+       k_EResultNoVerifiedPhone = 123, // account does not have a verified phone 
+                                    // number
+} EResult;
+
+typedef struct {
+
+       HSteamUser m_hSteamUser;        // Specific user to whom this callback applies.
+       int m_iCallback; 
+       u8 *m_pubParam;                 // Points to the callback structure
+       int m_cubParam;                 // Size of the data pointed to by m_pubParam
+       
+} CallbackMsg_t;
+
+typedef struct {
+
+       SteamAPICall_t m_hAsyncCall;
+       int m_iCallback;
+       u32 m_cubParam;
+       
+} SteamAPICallCompleted_t;
+
+enum { k_iSteamAPICallCompleted = k_iSteamUtilsCallbacks + 3 };
+
+// Steam universes.  Each universe is a self-contained Steam instance.
+typedef enum {
+       k_EUniverseInvalid = 0,
+       k_EUniversePublic = 1,
+       k_EUniverseBeta = 2,
+       k_EUniverseInternal = 3,
+       k_EUniverseDev = 4,
+       // k_EUniverseRC = 5,                           // no such universe anymore
+       k_EUniverseMax
+} EUniverse_t;
+
+struct SteamIDComponent_t
+{
+#ifdef VALVE_BIG_ENDIAN
+   EUniverse_t                 m_EUniverse : 8
+   unsigned int                m_EAccountType : 4;
+   unsigned int                m_unAccountInstance : 20;
+   u32                                 m_unAccountID : 32;
+#else
+   u32                                 m_unAccountID : 32;
+   unsigned int                m_unAccountInstance : 20;
+   unsigned int                m_EAccountType : 4;
+   EUniverse_t                 m_EUniverse : 8;
+#endif
+};
+
+typedef struct 
+{
+       // 64 bits total
+       union 
+   {
+      struct SteamIDComponent_t m_comp;
+               u64 m_unAll64Bits;
+       };
+} 
+CSteamID;
+
+typedef struct GameID_t
+{
+#ifdef VALVE_BIG_ENDIAN
+       unsigned int m_nModID : 32;
+       unsigned int m_nType : 8;
+       unsigned int m_nAppID : 24;
+#else
+       unsigned int m_nAppID : 24;
+       unsigned int m_nType : 8;
+       unsigned int m_nModID : 32;
+#endif
+} CGameID;
+
+#pragma pack( pop )
+
+/*
+ * Standard login
+ * =============================================================================
+ */
+
+int   SteamAPI_RestartAppIfNecessary( u32 unOwnAppID );
+int   SteamAPI_Init(void);
+void  SteamAPI_Shutdown(void);
+
+/*
+ * Server mode login
+ * =============================================================================
+ */
+
+typedef enum EServerMode EServerMode;
+enum EServerMode
+{
+       eServerModeInvalid = 0,
+       eServerModeNoAuthentication = 1,
+       eServerModeAuthentication = 2,
+       eServerModeAuthenticationAndSecure = 3,
+};
+
+int SteamInternal_GameServer_Init( u32 unIP, u16 usLegacySteamPort, 
+                                   u16 usGamePort, u16 usQueryPort, 
+                                   EServerMode eServerMode, 
+                                   const char *pchVersionString );
+
+/* Initialize SteamGameServer client and interface objects, and set server 
+ * properties which may not be changed.
+ * After calling this function, you should set any additional server parameters, 
+ * and then call ISteamGameServer::LogOnAnonymous() or ISteamGameServer::LogOn()
+ *
+ * - unIP will usually be zero.  If you are on a machine with multiple IP 
+ *   addresses, you can pass a non-zero value here and the relevant sockets will
+ *   be bound to that IP.  This can be used to ensure that the IP you desire is 
+ *   the one used in the server browser.
+ * - usGamePort is the port that clients will connect to for gameplay. You will
+ *   usually open up your own socket bound to this port.
+ * - usQueryPort is the port that will manage server browser related duties and 
+ *   info pings from clients.  If you pass STEAMGAMESERVER_QUERY_PORT_SHARED for
+ *   usQueryPort, then it will use "GameSocketShare" mode, which means that the
+ *   game is responsible for sending and receiving UDP packets for the master
+ *   server updater.  (See ISteamGameServer::HandleIncomingPacket and 
+ *   ISteamGameServer::GetNextOutgoingPacket.)
+ * - The version string should be in the form x.x.x.x, and is used by the master
+ *   server to detect when the server is out of date. (Only servers with the 
+ *   latest version will be listed.)
+ */
+int   SteamGameServer_Init( u32 unIP, u16 usGamePort, u16 usQueryPort, 
+                            EServerMode eServerMode, 
+                            const char *pchVersionString )
+{
+   return SteamInternal_GameServer_Init( unIP, 0, usGamePort, usQueryPort,
+                                         eServerMode, pchVersionString );
+}
+
+
+void *SteamAPI_SteamGameServer_v014(void);
+void *SteamAPI_SteamGameServer(void) 
+{ 
+   return SteamAPI_SteamGameServer_v014(); 
+}
+
+void SteamAPI_ISteamGameServer_LogOnAnonymous( void* self );
+
+void SteamGameServer_Shutdown(void);
+
+int  SteamGameServer_BSecure(void);
+u64  SteamGameServer_GetSteamID(void);
+
+/*
+ * Async callbacks
+ * =============================================================================
+ */
+typedef struct steam_async steam_async;
+struct steam_async
+{
+   SteamAPICall_t id;
+   void *data;
+
+   void (*p_handler)( void *result, void *userdata );
+}
+static steam_async_trackers[32];
+static u32 steam_async_track_count;
+
+steam_async *steam_new_async(void)
+{
+   if( steam_async_track_count == vg_list_size(steam_async_trackers) )
+   {
+      vg_error( "Maximum concurrent API calls exceeded (%u)\n",
+                steam_async_track_count );
+      return NULL;
+   }
+
+   return &steam_async_trackers[ steam_async_track_count ++ ];
+}
+
+/*
+ * Regular callbacks
+ * =============================================================================
+ */
+
+typedef struct steam_callback_handler steam_callback_handler;
+struct steam_callback_handler
+{
+   u32 callback_id;
+   void (*p_handler)( CallbackMsg_t *msg );
+}
+static steam_callback_handlers[64];
+static u32 steam_callback_handler_count;
+
+static int steam_register_callback( u32 id, 
+                                    void (*p_handler)( CallbackMsg_t *msg ) )
+{
+   if( steam_callback_handler_count == vg_list_size(steam_callback_handlers) )
+   {
+      vg_error( "Too many steam callback handlers registered (%u)\n", 
+                  steam_callback_handler_count );
+
+      return 0;
+   }
+   
+   steam_callback_handler *handler = &steam_callback_handlers[
+      steam_callback_handler_count ++ ];
+
+   handler->p_handler = p_handler;
+   handler->callback_id = id;
+   
+   return 1;
+}
+
+/*
+ * Event loop
+ * =============================================================================
+ */
+HSteamPipe SteamAPI_GetHSteamPipe(void);
+HSteamPipe SteamGameServer_GetHSteamPipe(void);
+HSteamUser SteamAPI_GetHSteamUser(void);
+void   SteamAPI_ManualDispatch_Init(void);
+void   SteamAPI_ManualDispatch_RunFrame( HSteamPipe hSteamPipe );
+int    SteamAPI_ManualDispatch_GetNextCallback( HSteamPipe hSteamPipe, 
+                                               CallbackMsg_t *pCallbackMsg );
+void   SteamAPI_ManualDispatch_FreeLastCallback( HSteamPipe hSteamPipe );
+int    SteamAPI_ManualDispatch_GetAPICallResult( HSteamPipe hSteamPipe, 
+         SteamAPICall_t hSteamAPICall, void *pCallback, int cubCallback, 
+         int iCallbackExpected, int *pbFailed );
+
+void   SteamAPI_ReleaseCurrentThreadMemory(void);
+
+static void steamworks_process_api_call( HSteamPipe pipe, 
+                                         CallbackMsg_t *callback )
+{
+   SteamAPICallCompleted_t *pCallCompleted = 
+      (SteamAPICallCompleted_t *)callback->m_pubParam;
+
+   int bFailed;
+
+   void *temp = alloca( pCallCompleted->m_cubParam );
+   
+   if( SteamAPI_ManualDispatch_GetAPICallResult( 
+      pipe,
+      pCallCompleted->m_hAsyncCall, 
+      temp,
+      pCallCompleted->m_cubParam, 
+      pCallCompleted->m_iCallback, 
+      &bFailed ) 
+   )
+   {
+      /* 
+       * Dispatch the call result to the registered handler(s) for the
+       * call identified by pCallCompleted->m_hAsyncCall 
+       */
+      
+      vg_info( "steamworks_event::api_call_completed( %lu )\n", 
+            pCallCompleted->m_hAsyncCall );
+      
+      int j=0;
+      for( int i=0; i<steam_async_track_count; i++ )
+      {
+         if( steam_async_trackers[j].id != pCallCompleted->m_hAsyncCall )
+         {
+            steam_async_trackers[j ++] = steam_async_trackers[i];
+         }
+         else
+         {
+            steam_async *pasync = &steam_async_trackers[j];
+            pasync->p_handler( temp, pasync->data );
+         }
+      }
+
+      if( steam_async_track_count == j )
+      {
+         vg_error( "No tracker was register for API call\n" );
+      }
+
+      steam_async_track_count = j;
+   } 
+   else 
+   { 
+#if 0
+      typedef enum ESteamAPICallFailure
+      {
+         k_ESteamAPICallFailureNone = -1,
+         k_ESteamAPICallFailureSteamGone = 0, 
+         k_ESteamAPICallFailureNetworkFailure = 1,     
+         k_ESteamAPICallFailureInvalidHandle = 2,
+         k_ESteamAPICallFailureMismatchedCallback = 3,
+      } 
+
+      ESteamAPICallFailure;
+      ESteamAPICallFailure fail_why = 
+      SteamAPI_ISteamUtils_GetAPICallFailureReason( 
+            steam_api_classes.utils, pCallCompleted->m_hAsyncCall );
+   
+      vg_error( "steamworks_event: error getting call result on"
+                "%lu (code %d)\n", 
+                pCallCompleted->m_hAsyncCall, fail_why );
+#endif
+   }
+}
+
+static void steamworks_event_loop( HSteamPipe pipe )
+{
+       SteamAPI_ManualDispatch_RunFrame( pipe );
+       CallbackMsg_t callback;
+       
+       while( SteamAPI_ManualDispatch_GetNextCallback( pipe, &callback ) )
+       {
+               vg_low( "steamworks_event::callback( %i )\n", callback.m_iCallback );
+       
+               /* Check for dispatching API call results */
+               if( callback.m_iCallback == k_iSteamAPICallCompleted )
+      {
+         steamworks_process_api_call( pipe, &callback );
+               } 
+               else 
+               {
+                       /* 
+          * Look at callback.m_iCallback to see what kind of callback it is,
+                        * and dispatch to appropriate handler(s)
+                        * void *data = callback.m_pubParam;
+          */
+
+         for( int i=0; i<steam_callback_handler_count; i++ )
+         {
+            steam_callback_handler *handler = &steam_callback_handlers[i];
+            if( handler->callback_id == callback.m_iCallback )
+            {
+               handler->p_handler( &callback );
+               break;
+            }
+         }
+               }
+               
+               SteamAPI_ManualDispatch_FreeLastCallback( pipe );
+       }
+}
+
+/*
+ * This is required to run the server outside of steamcmd environment.
+ * It can be any appid but idealy the one that is actually your game
+ */
+static void steamworks_ensure_txt( const char *appid_str )
+{
+   FILE *txt = fopen("steam_appid.txt", "w");
+   fputs( appid_str, txt );
+   fclose( txt );
+}
+
+#endif /* VG_STEAM_H */