/** * Give the async task time to do its work * Can only be called on the async task manager thread */ void FOnlineAsyncTaskSteamLogoffServer::Tick() { if (!bInit) { // @TODO ONLINE Listen Servers need to unset rich presence //SteamFriends()->SetRichPresence("connect", ""); for master server sessions SteamGameServer()->EnableHeartbeats(false); SteamGameServer()->LogOff(); bInit = true; } // Wait for the disconnect FOnlineSessionSteamPtr SessionInt = StaticCastSharedPtr<FOnlineSessionSteam>(Subsystem->GetSessionInterface()); if (!SessionInt->bSteamworksGameServerConnected && !SessionInt->GameServerSteamId.IsValid()) { bIsComplete = true; bWasSuccessful = true; } else { // Fallback timeout in case we don't hear from Steam if (GetElapsedTime() >= ASYNC_TASK_TIMEOUT) { SessionInt->bSteamworksGameServerConnected = false; SessionInt->GameServerSteamId = NULL; bIsComplete = true; bWasSuccessful = false; } } }
//----------------------------------------------------------------------------- // Purpose: Destructor //----------------------------------------------------------------------------- CSpaceWarServer::~CSpaceWarServer() { #ifdef USE_GS_AUTH_API // Notify Steam master server we are going offline SteamGameServer()->EnableHeartbeats( false ); #endif delete m_pSun; for( uint32 i=0; i < MAX_PLAYERS_PER_SERVER; ++i ) { if ( m_rgpShips[i] ) { // Tell this client we are exiting MsgServerExiting_t msg; BSendDataToClient( i, (char*)&msg, sizeof(msg) ); delete m_rgpShips[i]; m_rgpShips[i] = NULL; } } // Disconnect from the steam servers SteamGameServer()->LogOff(); // release our reference to the steam client library SteamGameServer_Shutdown(); }
void FOnlineSubsystemSteam::ShutdownSteamworks() { if (bSteamworksGameServerInitialized) { if (SteamGameServer() != nullptr) { // Since SteamSDK 1.17, LogOff is required to stop the game server advertising after exit; ensure we don't miss this at shutdown if (SteamGameServer()->BLoggedOn()) { SteamGameServer()->LogOff(); } SteamGameServer_Shutdown(); if (SessionInterface.IsValid()) { SessionInterface->GameServerSteamId = nullptr; SessionInterface->bSteamworksGameServerConnected = false; } } } if (bSteamworksClientInitialized) { SteamAPI_Shutdown(); bSteamworksClientInitialized = false; } }
/* <ee28a> ../engine/sv_steam3.cpp:506 */ void CSteam3Server::Shutdown(void) /* linkage=_ZN13CSteam3Server8ShutdownEv */ { if (this->m_bLoggedOn) { SteamGameServer()->EnableHeartbeats(0); SteamGameServer()->LogOff(); SteamGameServer_Shutdown(); this->m_bLoggedOn = false; } }
void CSteam3Server::Shutdown() { if (m_bLoggedOn) { SteamGameServer()->EnableHeartbeats(0); SteamGameServer()->LogOff(); SteamGameServer_Shutdown(); m_bLoggedOn = false; } }
//----------------------------------------------------------------------------- // Purpose: Removes a player at the given position //----------------------------------------------------------------------------- void CSpaceWarServer::RemovePlayerFromServer( uint32 uShipPosition ) { if ( uShipPosition >= MAX_PLAYERS_PER_SERVER ) { OutputDebugString( "Trying to remove ship at invalid position\n" ); return; } if ( !m_rgpShips[uShipPosition] ) { OutputDebugString( "Trying to remove a ship that does not exist\n" ); return; } OutputDebugString( "Removing a ship\n" ); delete m_rgpShips[uShipPosition]; m_rgpShips[uShipPosition] = NULL; m_rguPlayerScores[uShipPosition] = 0; #ifdef USE_GS_AUTH_API // Tell the GS the user is leaving the server SteamGameServer()->EndAuthSession( m_rgClientData[uShipPosition].m_SteamIDUser ); #endif memset( &m_rgClientData[uShipPosition], 0, sizeof( ClientConnectionData_t ) ); }
//----------------------------------------------------------------------------- // Purpose: Handle a new client connecting //----------------------------------------------------------------------------- void CSpaceWarServer::OnClientBeginAuthentication( CSteamID steamIDClient, void *pToken, uint32 uTokenLen ) { // First, check this isn't a duplicate and we already have a user logged on from the same steamid for( uint32 i = 0; i < MAX_PLAYERS_PER_SERVER; ++i ) { if ( m_rgClientData[i].m_SteamIDUser == steamIDClient ) { // We already logged them on... (should maybe tell them again incase they don't know?) return; } } // Second, do we have room? uint32 nPendingOrActivePlayerCount = 0; for ( uint32 i = 0; i < MAX_PLAYERS_PER_SERVER; ++i ) { if ( m_rgPendingClientData[i].m_bActive ) ++nPendingOrActivePlayerCount; if ( m_rgClientData[i].m_bActive ) ++nPendingOrActivePlayerCount; } // We are full (or will be if the pending players auth), deny new login if ( nPendingOrActivePlayerCount >= MAX_PLAYERS_PER_SERVER ) { MsgServerFailAuthentication_t msg; SteamGameServerNetworking()->SendP2PPacket( steamIDClient, &msg, sizeof( msg ), k_EP2PSendReliable ); } // If we get here there is room, add the player as pending for( uint32 i = 0; i < MAX_PLAYERS_PER_SERVER; ++i ) { if ( !m_rgPendingClientData[i].m_bActive ) { m_rgPendingClientData[i].m_ulTickCountLastData = m_pGameEngine->GetGameTickCount(); #ifdef USE_GS_AUTH_API // authenticate the user with the Steam back-end servers if ( k_EBeginAuthSessionResultOK != SteamGameServer()->BeginAuthSession( pToken, uTokenLen, steamIDClient ) ) { MsgServerFailAuthentication_t msg; SteamGameServerNetworking()->SendP2PPacket( steamIDClient, &msg, sizeof( msg ), k_EP2PSendReliable ); break; } m_rgPendingClientData[i].m_SteamIDUser = steamIDClient; m_rgPendingClientData[i].m_bActive = true; break; #else m_rgPendingClientData[i].m_bActive = true; // we need to tell the server our Steam id in the non-auth case, so we stashed it in the login message, pull it back out m_rgPendingClientData[i].m_SteamIDUser = *(CSteamID *)pToken; // You would typically do your own authentication method here and later call OnAuthCompleted // In this sample we just automatically auth anyone who connects OnAuthCompleted( true, i ); break; #endif } } }
//----------------------------------------------------------------------------- // Purpose: Callback from Steam when logon is fully completed and VAC secure policy is set //----------------------------------------------------------------------------- void CSpaceWarServer::OnPolicyResponse( GSPolicyResponse_t *pPolicyResponse ) { #ifdef USE_GS_AUTH_API // Check if we were able to go VAC secure or not if ( SteamGameServer()->BSecure() ) { OutputDebugString( "SpaceWarServer is VAC Secure!\n" ); } else { OutputDebugString( "SpaceWarServer is not VAC Secure!\n" ); } char rgch[128]; sprintf_safe( rgch, "Game server SteamID: %llu\n", SteamGameServer()->GetSteamID().ConvertToUint64() ); rgch[ sizeof(rgch) - 1 ] = 0; OutputDebugString( rgch ); #endif }
//----------------------------------------------------------------------------- // Purpose: Returns the SteamID of the game server //----------------------------------------------------------------------------- CSteamID CSpaceWarServer::GetSteamID() { #ifdef USE_GS_AUTH_API return SteamGameServer()->GetSteamID(); #else // this is a placeholder steam id to use when not making use of Steam auth or matchmaking return k_steamIDNonSteamGS; #endif }
/** * Give the async task time to do its work * Can only be called on the async task manager thread */ void FOnlineAsyncTaskSteamCreateServer::Tick() { if (!bInit) { ISteamGameServer* SteamGameServerPtr = SteamGameServer(); check(SteamGameServerPtr); UE_LOG_ONLINE(Verbose, TEXT("Initializing Steam game server")); SteamGameServerPtr->SetModDir(STEAMGAMEDIR); SteamGameServerPtr->SetProduct(STEAMPRODUCTNAME); SteamGameServerPtr->SetGameDescription(STEAMGAMEDESC); if (!SteamGameServerPtr->BLoggedOn()) { // Login the server with Steam SteamGameServerPtr->LogOnAnonymous(); } // Setup advertisement and force the initial update SteamGameServerPtr->SetHeartbeatInterval(-1); SteamGameServerPtr->EnableHeartbeats(true); SteamGameServerPtr->ForceHeartbeat(); bInit = true; } // Wait for the connection and policy response callbacks FOnlineSessionSteamPtr SessionInt = StaticCastSharedPtr<FOnlineSessionSteam>(Subsystem->GetSessionInterface()); if (SessionInt->bSteamworksGameServerConnected && SessionInt->GameServerSteamId->IsValid() && SessionInt->bPolicyResponseReceived) { bIsComplete = true; bWasSuccessful = true; } else { // Fallback timeout in case we don't hear from Steam if (GetElapsedTime() >= ASYNC_TASK_TIMEOUT) { bIsComplete = true; bWasSuccessful = false; } } }
//----------------------------------------------------------------------------- // Purpose: Called once we are connected to Steam to tell it about our details //----------------------------------------------------------------------------- void CSpaceWarServer::SendUpdatedServerDetailsToSteam() { // Tell the Steam authentication servers about our game char rgchServerName[128]; if ( SpaceWarClient() ) { // If a client is running (should always be since we don't support a dedicated server) // then we'll form the name based off of it sprintf_safe( rgchServerName, "%s's game", SpaceWarClient()->GetLocalPlayerName() ); } else { sprintf_safe( rgchServerName, "%s", "Spacewar!" ); } m_sServerName = rgchServerName; // // Set state variables, relevant to any master server updates or client pings // // These server state variables may be changed at any time. Note that there is no lnoger a mechanism // to send the player count. The player count is maintained by steam and you should use the player // creation/authentication functions to maintain your player count. SteamGameServer()->SetMaxPlayerCount( 4 ); SteamGameServer()->SetPasswordProtected( false ); SteamGameServer()->SetServerName( m_sServerName.c_str() ); SteamGameServer()->SetBotPlayerCount( 0 ); // optional, defaults to zero SteamGameServer()->SetMapName( "MilkyWay" ); #ifdef USE_GS_AUTH_API // Update all the players names/scores for( uint32 i=0; i < MAX_PLAYERS_PER_SERVER; ++i ) { if ( m_rgClientData[i].m_bActive && m_rgpShips[i] ) { SteamGameServer()->BUpdateUserData( m_rgClientData[i].m_SteamIDUser, m_rgpShips[i]->GetPlayerName(), m_rguPlayerScores[i] ); } } #endif // game type is a special string you can use for your game to differentiate different game play types occurring on the same maps // When users search for this parameter they do a sub-string search of this string // (i.e if you report "abc" and a client requests "ab" they return your server) //SteamGameServer()->SetGameType( "dm" ); // update any rule values we publish //SteamMasterServerUpdater()->SetKeyValue( "rule1_setting", "value" ); //SteamMasterServerUpdater()->SetKeyValue( "rule2_setting", "value2" ); }
/** * Give the async task a chance to marshal its data back to the game thread * Can only be called on the game thread by the async task manager */ void FOnlineAsyncTaskSteamCreateServer::Finalize() { FOnlineSessionSteamPtr SessionInt = StaticCastSharedPtr<FOnlineSessionSteam>(Subsystem->GetSessionInterface()); if (bWasSuccessful) { FNamedOnlineSession* Session = SessionInt->GetNamedSession(SessionName); if (Session) { // Setup the host session info FOnlineSessionInfoSteam* NewSessionInfo = new FOnlineSessionInfoSteam(ESteamSession::AdvertisedSessionHost, *SessionInt->GameServerSteamId); NewSessionInfo->Init(); ISteamGameServer* SteamGameServerPtr = SteamGameServer(); check(SteamGameServerPtr); // Create the proper Steam P2P address for this machine NewSessionInfo->SteamP2PAddr = ISocketSubsystem::Get()->GetLocalBindAddr(*GLog); NewSessionInfo->SteamP2PAddr->SetPort(Subsystem->GetGameServerGamePort()); UE_LOG_ONLINE(Verbose, TEXT("Server SteamP2P IP: %s"), *NewSessionInfo->SteamP2PAddr->ToString(true)); // Create the proper ip address for this server NewSessionInfo->HostAddr = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateInternetAddr(SteamGameServerPtr->GetPublicIP(), Subsystem->GetGameServerGamePort()); UE_LOG_ONLINE(Verbose, TEXT("Server IP: %s"), *NewSessionInfo->HostAddr->ToString(true)); if (!Session->OwningUserId.IsValid()) { check(Session->SessionSettings.bIsDedicated); // Associate the dedicated server anonymous login as the owning user Session->OwningUserId = SessionInt->GameServerSteamId; Session->OwningUserName = Session->OwningUserId->ToString(); } Session->SessionInfo = MakeShareable(NewSessionInfo); Session->SessionSettings.bAntiCheatProtected = SteamGameServerPtr->BSecure() != 0 ? true : false; Session->SessionState = EOnlineSessionState::Pending; UWorld* World = GetWorldForOnline(Subsystem->GetInstanceName()); UpdatePublishedSettings(World, Session); SessionInt->RegisterLocalPlayers(Session); if (SteamUser()) { SteamUser()->AdvertiseGame(NewSessionInfo->SessionId, SteamGameServerPtr->GetPublicIP(), Subsystem->GetGameServerGamePort()); } // Set host rich presence string so we can find via FindFriendSession() // FString ConnectionString = Sessions->GetSteamConnectionString(Session->SessionName); // if (!SteamFriends()->SetRichPresence("connect", TCHAR_TO_UTF8(*ConnectionString))) // { // UE_LOG_ONLINE(Verbose, TEXT("Failed to set rich presence for session %s"), *Session->SessionName.ToString()); // } } else { UE_LOG_ONLINE(Warning, TEXT("No session %s found to update with Steam backend"), *SessionName.ToString()); } } else { SessionInt->RemoveNamedSession(SessionName); } }
/** * Update the backend with the currently defined settings * * @param World current running world instance * @param SessionName name of session to update published settings */ void UpdatePublishedSettings(UWorld* World, FNamedOnlineSession* Session) { ISteamGameServer* SteamGameServerPtr = SteamGameServer(); check(SteamGameServerPtr); // Copy the current settings so we can remove the ones used for well defined search parameters FOnlineSessionSettings TempSessionSettings = Session->SessionSettings; // Server name FString ServerName = Session->OwningUserName; SteamGameServerPtr->SetServerName(TCHAR_TO_UTF8(*ServerName)); // Max user slots reported int32 NumTotalSlots = Session->SessionSettings.NumPublicConnections + Session->SessionSettings.NumPrivateConnections; SteamGameServerPtr->SetMaxPlayerCount(NumTotalSlots); // Region setting FString Region(TEXT("")); SteamGameServerPtr->SetRegion(TCHAR_TO_UTF8(*Region)); // @TODO ONLINE Password protected or not SteamGameServerPtr->SetPasswordProtected(false); // Dedicated server or not SteamGameServerPtr->SetDedicatedServer(Session->SessionSettings.bIsDedicated ? true : false); // Map name FString MapName; if (TempSessionSettings.Get(SETTING_MAPNAME, MapName) && !MapName.IsEmpty()) { SteamGameServerPtr->SetMapName(TCHAR_TO_UTF8(*MapName)); } TempSessionSettings.Remove(SETTING_MAPNAME); // Bot Count int32 BotCount = 0; if (TempSessionSettings.Get(SETTING_NUMBOTS, BotCount)) { SteamGameServerPtr->SetBotPlayerCount(BotCount); } TempSessionSettings.Remove(SETTING_NUMBOTS); // Update all the players names/scores if (World) { AGameState const* const GameState = World->GameState; if (GameState) { for (int32 PlayerIdx=0; PlayerIdx < GameState->PlayerArray.Num(); PlayerIdx++) { APlayerState const* const PlayerState = GameState->PlayerArray[PlayerIdx]; if (PlayerState && PlayerState->UniqueId.IsValid()) { CSteamID SteamId(*(uint64*)PlayerState->UniqueId->GetBytes()); SteamGameServerPtr->BUpdateUserData(SteamId, TCHAR_TO_UTF8(*PlayerState->PlayerName), PlayerState->Score); } } } } // Get the advertised session settings out as Steam key/value pairs FSteamSessionKeyValuePairs AdvertisedKeyValuePairs; GetServerKeyValuePairsFromSession(Session, AdvertisedKeyValuePairs); if (Session->SessionInfo.IsValid()) { FOnlineSessionInfoSteam* SessionInfo = (FOnlineSessionInfoSteam*)(Session->SessionInfo.Get()); GetServerKeyValuePairsFromSessionInfo(SessionInfo, AdvertisedKeyValuePairs); } FString SessionFlags = GetSessionFlagsAsString(Session->SessionSettings); AdvertisedKeyValuePairs.Add(STEAMKEY_SESSIONFLAGS, SessionFlags); FString SessionBuildUniqueId = GetBuildIdAsSteamKey(Session->SessionSettings); GetServerKeyValuePairsFromSessionSettings(TempSessionSettings, AdvertisedKeyValuePairs, EOnlineDataAdvertisementType::ViaOnlineService); FSteamSessionKeyValuePairs AuxKeyValuePairs; GetServerKeyValuePairsFromSessionSettings(TempSessionSettings, AuxKeyValuePairs, EOnlineDataAdvertisementType::ViaPingOnly); FSteamSessionKeyValuePairs TempKeyValuePairs; GetServerKeyValuePairsFromSessionSettings(TempSessionSettings, TempKeyValuePairs, EOnlineDataAdvertisementType::ViaOnlineServiceAndPing); AdvertisedKeyValuePairs.Append(TempKeyValuePairs); AuxKeyValuePairs.Append(TempKeyValuePairs); FString GameTagsString, GameDataString; // Start the game tags with the build id so search results can early out GameTagsString = SessionBuildUniqueId; // Create the properly formatted Steam string (ie key:value,key:value,key) for GameTags/GameData FSteamSessionKeyValuePairs::TConstIterator It(AdvertisedKeyValuePairs); if (It) { UE_LOG_ONLINE(Verbose, TEXT("Master Server Data (%s, %s)"), *It.Key(), *It.Value()); FString NewKey = FString::Printf(TEXT("%s:%s"), *It.Key(), *It.Value()); if (GameTagsString.Len() + NewKey.Len() < k_cbMaxGameServerTags) { GameTagsString = GameTagsString + "," + NewKey; } else { UE_LOG_ONLINE(Warning, TEXT("Server setting %s overflows Steam SetGameTags call"), *NewKey); } if (NewKey.Len() < k_cbMaxGameServerGameData) { GameDataString = NewKey; } else { UE_LOG_ONLINE(Warning, TEXT("Server setting %s overflows Steam SetGameData call"), *NewKey); } ++It; } for (; It; ++It) { UE_LOG_ONLINE(Verbose, TEXT("Master Server Data (%s, %s)"), *It.Key(), *It.Value()); FString NewKey = FString::Printf(TEXT(",%s:%s"), *It.Key(), *It.Value()); if (GameTagsString.Len() + NewKey.Len() < k_cbMaxGameServerTags) { GameTagsString += NewKey; } else { UE_LOG_ONLINE(Warning, TEXT("Server setting %s overflows Steam SetGameTags call"), *NewKey); } if (GameDataString.Len() + NewKey.Len() < k_cbMaxGameServerGameData) { GameDataString += NewKey; } else { UE_LOG_ONLINE(Warning, TEXT("Server setting %s overflows Steam SetGameData call"), *NewKey); } } // Small and searchable game tags (returned in initial server query structure) if (GameTagsString.Len() > 0 && GameTagsString.Len() < k_cbMaxGameServerTags) { UE_LOG_ONLINE(Verbose, TEXT("SetGameTags(%s)"), *GameTagsString); SteamGameServerPtr->SetGameTags(TCHAR_TO_UTF8(*GameTagsString)); } // Large and searchable game data (never returned) if (GameDataString.Len() > 0 && GameDataString.Len() < k_cbMaxGameServerGameData) { UE_LOG_ONLINE(Verbose, TEXT("SetGameData(%s)"), *GameDataString); SteamGameServerPtr->SetGameData(TCHAR_TO_UTF8(*GameDataString)); } // @TODO ONLINE - distinguish between server side keys (SetGameData()) and client side keys (SetKeyValue()) // Set the advertised filter keys (these can not be filtered at master-server level, only client side) SteamGameServerPtr->ClearAllKeyValues(); // Key value pairs sent as rules (requires secondary RulesRequest call) for (FSteamSessionKeyValuePairs::TConstIterator AdvKeyIt(AdvertisedKeyValuePairs); AdvKeyIt; ++AdvKeyIt) { UE_LOG_ONLINE(Verbose, TEXT("Aux Server Data (%s, %s)"), *AdvKeyIt.Key(), *AdvKeyIt.Value()); SteamGameServerPtr->SetKeyValue(TCHAR_TO_UTF8(*AdvKeyIt.Key()), TCHAR_TO_UTF8(*AdvKeyIt.Value())); } // Key value pairs sent as rules (requires secondary RulesRequest call) for (FSteamSessionKeyValuePairs::TConstIterator AuxKeyIt(AuxKeyValuePairs); AuxKeyIt; ++AuxKeyIt) { UE_LOG_ONLINE(Verbose, TEXT("Aux Server Data (%s, %s)"), *AuxKeyIt.Key(), *AuxKeyIt.Value()); SteamGameServerPtr->SetKeyValue(TCHAR_TO_UTF8(*AuxKeyIt.Key()), TCHAR_TO_UTF8(*AuxKeyIt.Value())); } }
//----------------------------------------------------------------------------- // Purpose: Receives incoming network data //----------------------------------------------------------------------------- void CSpaceWarServer::ReceiveNetworkData() { char *pchRecvBuf = NULL; uint32 cubMsgSize; CSteamID steamIDRemote; while ( SteamGameServerNetworking()->IsP2PPacketAvailable( &cubMsgSize ) ) { // free any previous receive buffer if ( pchRecvBuf ) free( pchRecvBuf ); // alloc a new receive buffer of the right size pchRecvBuf = (char *)malloc( cubMsgSize ); // see if there is any data waiting on the socket if ( !SteamGameServerNetworking()->ReadP2PPacket( pchRecvBuf, cubMsgSize, &cubMsgSize, &steamIDRemote ) ) break; if ( cubMsgSize < sizeof( DWORD ) ) { OutputDebugString( "Got garbage on server socket, too short\n" ); continue; } EMessage eMsg = (EMessage)LittleDWord( *(DWORD*)pchRecvBuf ); switch ( eMsg ) { case k_EMsgClientInitiateConnection: { // We always let clients do this without even checking for room on the server since we reserve that for // the authentication phase of the connection which comes next MsgServerSendInfo_t msg; msg.SetSteamIDServer( SteamGameServer()->GetSteamID().ConvertToUint64() ); #ifdef USE_GS_AUTH_API // You can only make use of VAC when using the Steam authentication system msg.SetSecure( SteamGameServer()->BSecure() ); #endif msg.SetServerName( m_sServerName.c_str() ); SteamGameServerNetworking()->SendP2PPacket( steamIDRemote, &msg, sizeof( MsgServerSendInfo_t ), k_EP2PSendReliable ); } break; case k_EMsgClientBeginAuthentication: { if ( cubMsgSize != sizeof( MsgClientBeginAuthentication_t ) ) { OutputDebugString( "Bad connection attempt msg\n" ); continue; } MsgClientBeginAuthentication_t *pMsg = (MsgClientBeginAuthentication_t*)pchRecvBuf; #ifdef USE_GS_AUTH_API OnClientBeginAuthentication( steamIDRemote, (void*)pMsg->GetTokenPtr(), pMsg->GetTokenLen() ); #else OnClientBeginAuthentication( steamIDRemote, 0 ); #endif } break; case k_EMsgClientSendLocalUpdate: { if ( cubMsgSize != sizeof( MsgClientSendLocalUpdate_t ) ) { OutputDebugString( "Bad client update msg\n" ); continue; } // Find the connection that should exist for this users address bool bFound = false; for( uint32 i=0; i<MAX_PLAYERS_PER_SERVER; ++i ) { if ( m_rgClientData[i].m_SteamIDUser == steamIDRemote ) { bFound = true; MsgClientSendLocalUpdate_t *pMsg = (MsgClientSendLocalUpdate_t*)pchRecvBuf; OnReceiveClientUpdateData( i, pMsg->AccessUpdateData() ); break; } } if ( !bFound ) OutputDebugString( "Got a client data update, but couldn't find a matching client\n" ); } break; case k_EMsgClientPing: { // send back a response MsgServerPingResponse_t msg; SteamGameServerNetworking()->SendP2PPacket( steamIDRemote, &msg, sizeof( msg ), k_EP2PSendUnreliable ); } break; case k_EMsgClientLeavingServer: { if ( cubMsgSize != sizeof( MsgClientLeavingServer_t ) ) { OutputDebugString( "Bad leaving server msg\n" ); continue; } // Find the connection that should exist for this users address bool bFound = false; for( uint32 i=0; i<MAX_PLAYERS_PER_SERVER; ++i ) { if ( m_rgClientData[i].m_SteamIDUser == steamIDRemote ) { bFound = true; RemovePlayerFromServer( i ); break; } // Also check for pending connections that may match if ( m_rgPendingClientData[i].m_SteamIDUser == steamIDRemote ) { #ifdef USE_GS_AUTH_API // Tell the GS the user is leaving the server SteamGameServer()->SendUserDisconnect( m_rgPendingClientData[i].m_SteamIDUser ); #endif // Clear our data on the user memset( &m_rgPendingClientData[i], 0 , sizeof( ClientConnectionData_t ) ); break; } } if ( !bFound ) OutputDebugString( "Got a client leaving server msg, but couldn't find a matching client\n" ); } default: char rgch[128]; sprintf_safe( rgch, "Invalid message %x\n", eMsg ); rgch[ sizeof(rgch) - 1 ] = 0; OutputDebugString( rgch ); } } if ( pchRecvBuf ) free( pchRecvBuf ); }
//----------------------------------------------------------------------------- // Purpose: A new client that connected has had their authentication processed //----------------------------------------------------------------------------- void CSpaceWarServer::OnAuthCompleted( bool bAuthSuccessful, uint32 iPendingAuthIndex ) { if ( !m_rgPendingClientData[iPendingAuthIndex].m_bActive ) { OutputDebugString( "Got auth completed callback for client who is not pending\n" ); return; } if ( !bAuthSuccessful ) { #ifdef USE_GS_AUTH_API // Tell the GS the user is leaving the server SteamGameServer()->EndAuthSession( m_rgPendingClientData[iPendingAuthIndex].m_SteamIDUser ); #endif // Send a deny for the client, and zero out the pending data MsgServerFailAuthentication_t msg; SteamGameServerNetworking()->SendP2PPacket( m_rgPendingClientData[iPendingAuthIndex].m_SteamIDUser, &msg, sizeof( msg ), k_EP2PSendReliable ); memset( &m_rgPendingClientData[iPendingAuthIndex], 0, sizeof( ClientConnectionData_t ) ); return; } bool bAddedOk = false; for( uint32 i = 0; i < MAX_PLAYERS_PER_SERVER; ++i ) { if ( !m_rgClientData[i].m_bActive ) { // copy over the data from the pending array memcpy( &m_rgClientData[i], &m_rgPendingClientData[iPendingAuthIndex], sizeof( ClientConnectionData_t ) ); memset( &m_rgPendingClientData[iPendingAuthIndex], 0, sizeof( ClientConnectionData_t ) ); m_rgClientData[i].m_ulTickCountLastData = m_pGameEngine->GetGameTickCount(); // Add a new ship, make it dead immediately AddPlayerShip( i ); m_rgpShips[i]->SetDisabled( true ); MsgServerPassAuthentication_t msg; msg.SetPlayerPosition( i ); BSendDataToClient( i, (char*)&msg, sizeof( msg ) ); bAddedOk = true; break; } } // If we just successfully added the player, check if they are #2 so we can restart the round if ( bAddedOk ) { uint32 uPlayers = 0; for( uint32 i = 0; i < MAX_PLAYERS_PER_SERVER; ++i ) { if ( m_rgClientData[i].m_bActive ) ++uPlayers; } // If we just got the second player, immediately reset round as a draw. This will prevent // the existing player getting a win, and it will cause a new round to start right off // so that the one player can't just float around not letting the new one get into the game. if ( uPlayers == 2 ) { if ( m_eGameState != k_EServerWaitingForPlayers ) SetGameState( k_EServerDraw ); } } }
//----------------------------------------------------------------------------- // Purpose: Constructor -- note the syntax for setting up Steam API callback handlers //----------------------------------------------------------------------------- CSpaceWarServer::CSpaceWarServer( IGameEngine *pGameEngine ) { m_bConnectedToSteam = false; const char *pchGameDir = "spacewar"; uint32 unIP = INADDR_ANY; uint16 usMasterServerUpdaterPort = SPACEWAR_MASTER_SERVER_UPDATER_PORT; #ifdef USE_GS_AUTH_API EServerMode eMode = eServerModeAuthenticationAndSecure; #else // Don't let Steam do authentication EServerMode eMode = eServerModeNoAuthentication; #endif // Initialize the SteamGameServer interface, we tell it some info about us, and we request support // for both Authentication (making sure users own games) and secure mode, VAC running in our game // and kicking users who are VAC banned // !FIXME! We need a way to pass the dedicated server flag here! #ifdef _PS3 if ( !SteamGameServer_Init( &g_SteamPS3Params, unIP, SPACEWAR_AUTHENTICATION_PORT, SPACEWAR_SERVER_PORT, usMasterServerUpdaterPort, eMode, SPACEWAR_SERVER_VERSION ) ) #else if ( !SteamGameServer_Init( unIP, SPACEWAR_AUTHENTICATION_PORT, SPACEWAR_SERVER_PORT, usMasterServerUpdaterPort, eMode, SPACEWAR_SERVER_VERSION ) ) #endif { OutputDebugString( "SteamGameServer_Init call failed\n" ); } if ( SteamGameServer() ) { // Set the "game dir". // This is currently required for all games. However, soon we will be // using the AppID for most purposes, and this string will only be needed // for mods. it may not be changed after the server has logged on SteamGameServer()->SetModDir( pchGameDir ); // These fields are currently required, but will go away soon. // See their documentation for more info SteamGameServer()->SetProduct( "SteamworksExample" ); SteamGameServer()->SetGameDescription( "Steamworks Example" ); // We don't support specators in our game. // .... but if we did: //SteamGameServer()->SetSpectatorPort( ... ); //SteamGameServer()->SetSpectatorServerName( ... ); // Initiate Anonymous logon. // Coming soon: Logging into authenticated, persistent game server account SteamGameServer()->LogOnAnonymous(); // We want to actively update the master server with our presence so players can // find us via the steam matchmaking/server browser interfaces #ifdef USE_GS_AUTH_API SteamGameServer()->EnableHeartbeats( true ); #endif } else { OutputDebugString( "SteamGameServer() interface is invalid\n" ); } m_uPlayerCount = 0; m_pGameEngine = pGameEngine; m_eGameState = k_EServerWaitingForPlayers; for( uint32 i = 0; i < MAX_PLAYERS_PER_SERVER; ++i ) { m_rguPlayerScores[i] = 0; m_rgpShips[i] = NULL; } // No one has won m_uPlayerWhoWonGame = 0; m_ulStateTransitionTime = m_pGameEngine->GetGameTickCount(); m_ulLastServerUpdateTick = 0; // zero the client connection data memset( &m_rgClientData, 0, sizeof( m_rgClientData ) ); memset( &m_rgPendingClientData, 0, sizeof( m_rgPendingClientData ) ); // Seed random num generator srand( (uint32)time( NULL ) ); // Initialize sun m_pSun = new CSun( pGameEngine ); // Initialize ships ResetPlayerShips(); }