mirror of
https://github.com/smartcmd/MinecraftConsoles.git
synced 2026-03-23 00:18:13 +05:00
* add: Dedicated Server implementation - Introduced `ServerMain.cpp` for the dedicated server logic, handling command-line arguments, server initialization, and network management. - Created `postbuild_server.ps1` script for post-build tasks, including copying necessary resources and DLLs for the dedicated server. - Added `CopyServerAssets.cmake` to manage the copying of server assets during the build process, ensuring required files are available for the dedicated server. - Defined project filters in `Minecraft.Server.vcxproj.filters` for better organization of server-related files. * add: refactor world loader & add server properties - Introduced ServerLogger for logging startup steps and world I/O operations. - Implemented ServerProperties for loading and saving server configuration from `server.properties`. - Added WorldManager to handle world loading and creation based on server properties. - Updated ServerMain to integrate server properties loading and world management. - Enhanced project files to include new source and header files for the server components. * update: implement enhanced logging functionality with configurable log levels * update: update keyboard and mouse input initialization1dc8a005ed* fix: change virtual screen resolution to 1920x1080(HD) Since 31881af56936aeef38ff322b975fd0 , `skinHud.swf` for 720 is not included in `MediaWindows64.arc`, the app crashes unless the virtual screen is set to HD. * fix: dedicated server build settings for miniaudio migration and missing sources - remove stale Windows64 Miles (mss64) link/copy references from server build - add Common/Filesystem/Filesystem.cpp to Minecraft.Server.vcxproj - add Windows64/PostProcesser.cpp to Minecraft.Server.vcxproj - fix unresolved externals (PostProcesser::*, FileExists) in dedicated server build * update: changed the virtual screen to 720p Since the crash caused by the 720p `skinHud.swf` not being included in `MediaWindows64.arc` has been resolved, switching back to 720p to reduce resource usage. * add: add Docker support for Dedicated Server add with entrypoint and build scripts * fix: add initial save for newly created worlds in dedicated server on the server side, I fixed the behavior introduced after commitaadb511, where newly created worlds are intentionally not saved to disk immediately. * update: add basically all configuration options that are implemented in the classes to `server.properties` * update: add LAN advertising configuration for server.properties LAN-Discovery, which isn’t needed in server mode and could potentially be a security risk, has also been disabled(only server mode). * add: add implementing interactive command line using linenoise - Integrated linenoise library for line editing and completion in the server console. - Updated ServerLogger to handle external writes safely during logging. - Modified ServerMain to initialize and manage the ServerCli for command input. - The implementation is separate from everything else, so it doesn't affect anything else. - The command input section and execution section are separated into threads. * update: enhance command line completion with predictive hints Like most command line tools, it highlights predictions in gray. * add: implement `StringUtils` for string manipulation and refactor usages Unified the scattered utility functions. * fix: send DisconnectPacket on shutdown and fix Win64 recv-thread teardown race Before this change, server/host shutdown closed sockets directly in ServerConnection::stop(), which bypassed the normal disconnect flow. As a result, clients could be dropped without receiving a proper DisconnectPacket during stop/kill/world-close paths. Also, WinsockNetLayer::Shutdown() could destroy synchronization objects while host-side recv threads were still exiting, causing a crash in RecvThreadProc (access violation on world close in host mode). * fix: return client to menus when Win64 host connection drops - Add client-side host disconnect handling in CPlatformNetworkManagerStub::DoWork() for _WINDOWS64. - When in QNET_STATE_GAME_PLAY as a non-host and WinsockNetLayer::IsConnected() becomes false, trigger g_NetworkManager.HandleDisconnect(false) to enter the normal disconnect/UI flow. - Use m_bLeaveGameOnTick as a one-shot guard to prevent repeated disconnect handling while the link remains down. - Reset m_bLeaveGameOnTick on LeaveGame(), HostGame(), and JoinGame() to avoid stale state across sessions. * update: converted Japanese comments to English * add: create `Minecraft.Server` developer guide in English and Japanese * update: add note about issue * add: add `nlohmann/json` json lib * add: add FileUtils Moved file operations to `utils`. * add: Dedicated Server BAN access manager with persistent player and IP bans - add Access frontend that publishes thread-safe ban manager snapshots for dedicated server use - add BanManager storage for banned-players.json and banned-ips.json with load/save/update flows - add persistent player and IP ban checks during dedicated server connection handling - add UTF-8 BOM-safe JSON parsing and shared file helpers backed by nlohmann/json - add Unicode-safe ban file read/write and safer atomic replacement behavior on Windows - add active-ban snapshot APIs and expiry-aware filtering for expires metadata - add RAII-based dedicated access shutdown handling during server startup and teardown * update: changed file read/write operations to use `FileUtils`. - As a side effect, saving has become faster! * fix: Re-added the source that had somehow disappeared. * add: significantly improved the dedicated server logging system - add ServerLogManager to Minecraft.Server as the single entry point for dedicated-server log output - forward CMinecraftApp logger output to the server logger when running with g_Win64DedicatedServer - add named network logs for incoming, accepted, rejected, and disconnected connections - cache connection metadata by smallId so player name and remote IP remain available for disconnect logs - keep Minecraft.Client changes minimal by using lightweight hook points and handling log orchestration on the server side * fix: added the updated library source * add: add `ban` and `pardon` commands for Player and IP * fix: fix stop command shutdown process add dedicated server shutdown request handling * fix: fixed the save logic during server shutdown Removed redundant repeated saves and eliminated the risks of async writes. * update: added new sever files to Docker entrypoint * fix: replace shutdown flag with atomic variable for thread safety * update: update Dedicated Server developer guide English is machine translated. Please forgive me. * update: check for the existence of `GameHDD` and create * add: add Whitelist to Dedicated Server * refactor: clean up and refactor the code - unify duplicated implementations that were copied repeatedly - update outdated patterns to more modern ones * fix: include UI header (new update fix) * fix: fix the detection range for excessive logging `getHighestNonEmptyY()` returning `-1` occurs normally when the chunk is entirely air. The caller (`Minecraft.World/LevelChunk.cpp:2400`) normalizes `-1` to `0`. * update: add world size config to dedicated server properties * update: update README add explanation of `server.properties` & launch arguments * update: add nightly release workflow for dedicated server and client builds to Actions * fix: update name for workflow * add random seed generation * add: add Docker nightly workflow for Dedicated Server publish to GitHub Container Registry * fix: ghost player when clients disconnect out of order #4 * fix: fix 7zip option * fix: fix Docker workflow for Dedicated Server artifact handling * add: add no build Dedicated Server startup scripts and Docker Compose * update: add README for Docker Dedicated Server setup with no local build * refactor: refactor command path structure As the number of commands has increased and become harder to navigate, each command has been organized into separate folders. * update: support stream(file stdin) input mode for server CLI Support for the stream (file stdin) required when attaching a tty to a Docker container on Linux. * add: add new CLI Console Commands for Dedicated Server Most of these commands are executed using the command dispatcher implemented on the `Minecraft.World` side. When registering them with the dispatcher, the sender uses a permission-enabled configuration that treats the CLI as a player. - default game. - enchant - experience. - give - kill(currently, getting a permission error for some reason) - time - weather. - update tp & gamemode command * fix: change player map icon to random select * update: increase the player limit * add: restore the basic anti-cheat implementation and add spawn protection Added the following anti-cheat measures and add spawn protection to `server.properties`. - instant break - speed - reach * fix: fix Docker image tag --------- Co-authored-by: sylvessa <225480449+sylvessa@users.noreply.github.com>
2155 lines
70 KiB
C++
2155 lines
70 KiB
C++
#include "stdafx.h"
|
|
#include "..\..\..\Minecraft.World\StringHelpers.h"
|
|
#include "..\..\..\Minecraft.World\AABB.h"
|
|
#include "..\..\..\Minecraft.World\Vec3.h"
|
|
#include "..\..\..\Minecraft.World\Socket.h"
|
|
#include "..\..\..\Minecraft.World\ThreadName.h"
|
|
#include "..\..\..\Minecraft.World\Entity.h"
|
|
#include "..\..\..\Minecraft.World\net.minecraft.world.level.tile.h"
|
|
#include "..\..\..\Minecraft.World\FireworksRecipe.h"
|
|
#include "..\..\ClientConnection.h"
|
|
#include "..\..\Minecraft.h"
|
|
#include "..\..\User.h"
|
|
#include "..\..\MinecraftServer.h"
|
|
#include "..\..\PlayerList.h"
|
|
#include "..\..\ServerPlayer.h"
|
|
#include "..\..\PlayerConnection.h"
|
|
#include "..\..\MultiPlayerLevel.h"
|
|
#include "..\..\ProgressRenderer.h"
|
|
#include "..\..\MultiPlayerLocalPlayer.h"
|
|
#include "..\..\..\Minecraft.World\DisconnectPacket.h"
|
|
#include "..\..\..\Minecraft.World\compression.h"
|
|
#include "..\..\..\Minecraft.World\OldChunkStorage.h"
|
|
#include "..\..\TexturePackRepository.h"
|
|
#include "..\..\TexturePack.h"
|
|
|
|
#include "..\..\Gui.h"
|
|
#include "..\..\LevelRenderer.h"
|
|
#include "..\..\..\Minecraft.World\IntCache.h"
|
|
#include "..\GameRules\ConsoleGameRules.h"
|
|
#include "GameNetworkManager.h"
|
|
|
|
#ifdef _XBOX
|
|
#include "Common\XUI\XUI_PauseMenu.h"
|
|
#else
|
|
#include "Common\UI\UI.h"
|
|
#include "Common\UI\UIScene_PauseMenu.h"
|
|
#include "..\..\Xbox\Network\NetworkPlayerXbox.h"
|
|
#endif
|
|
|
|
#ifdef _DURANGO
|
|
#include "..\Minecraft.World\DurangoStats.h"
|
|
#endif
|
|
|
|
#ifdef _WINDOWS64
|
|
#include "..\..\Windows64\Network\WinsockNetLayer.h"
|
|
#include "..\..\Windows64\Windows64_Xuid.h"
|
|
#endif
|
|
|
|
// Global instance
|
|
CGameNetworkManager g_NetworkManager;
|
|
CPlatformNetworkManager *CGameNetworkManager::s_pPlatformNetworkManager;
|
|
|
|
int64_t CGameNetworkManager::messageQueue[512];
|
|
int64_t CGameNetworkManager::byteQueue[512];
|
|
int CGameNetworkManager::messageQueuePos = 0;
|
|
|
|
CGameNetworkManager::CGameNetworkManager()
|
|
{
|
|
m_bInitialised = false;
|
|
m_bLastDisconnectWasLostRoomOnly = false;
|
|
m_bFullSessionMessageOnNextSessionChange = false;
|
|
|
|
#ifdef __ORBIS__
|
|
m_pUpsell = nullptr;
|
|
m_pInviteInfo = nullptr;
|
|
#endif
|
|
}
|
|
|
|
void CGameNetworkManager::Initialise()
|
|
{
|
|
ServerStoppedCreate( false );
|
|
ServerReadyCreate( false );
|
|
int flagIndexSize = LevelRenderer::getGlobalChunkCount() / (Level::maxBuildHeight / 16); // dividing here by number of renderer chunks in one column
|
|
#ifdef _XBOX
|
|
s_pPlatformNetworkManager = new CPlatformNetworkManagerXbox();
|
|
#elif defined __PS3__ || defined __ORBIS__ || defined __PSVITA__
|
|
s_pPlatformNetworkManager = new CPlatformNetworkManagerSony();
|
|
#elif defined _DURANGO
|
|
s_pPlatformNetworkManager = new CPlatformNetworkManagerDurango();
|
|
#else
|
|
s_pPlatformNetworkManager = new CPlatformNetworkManagerStub();
|
|
#endif
|
|
s_pPlatformNetworkManager->Initialise( this, flagIndexSize );
|
|
m_bNetworkThreadRunning = false;
|
|
m_bInitialised = true;
|
|
}
|
|
|
|
void CGameNetworkManager::Terminate()
|
|
{
|
|
if( m_bInitialised )
|
|
{
|
|
s_pPlatformNetworkManager->Terminate();
|
|
}
|
|
}
|
|
|
|
void CGameNetworkManager::DoWork()
|
|
{
|
|
#ifdef _XBOX
|
|
// did we get any notifications from the game listener?
|
|
if(app.GetNotifications()->size()!=0)
|
|
{
|
|
PNOTIFICATION pNotification=app.GetNotifications()->back();
|
|
|
|
switch(pNotification->dwNotification)
|
|
{
|
|
case XN_LIVE_LINK_STATE_CHANGED:
|
|
{
|
|
int iPrimaryPlayer = g_NetworkManager.GetPrimaryPad();
|
|
bool bConnected = (pNotification->uiParam!=0)?true:false;
|
|
if((g_NetworkManager.GetLockedProfile()!=-1) && iPrimaryPlayer!=-1 && bConnected == false && g_NetworkManager.IsInSession() )
|
|
{
|
|
app.SetAction(iPrimaryPlayer,eAppAction_EthernetDisconnected);
|
|
}
|
|
}
|
|
break;
|
|
case XN_LIVE_INVITE_ACCEPTED:
|
|
s_pPlatformNetworkManager->Notify(pNotification->dwNotification,pNotification->uiParam);
|
|
break;
|
|
}
|
|
|
|
app.GetNotifications()->pop_back();
|
|
delete pNotification;
|
|
}
|
|
#endif
|
|
s_pPlatformNetworkManager->DoWork();
|
|
|
|
#ifdef __ORBIS__
|
|
if (m_pUpsell != nullptr && m_pUpsell->hasResponse())
|
|
{
|
|
int iPad_invited = m_iPlayerInvited, iPad_checking = m_pUpsell->m_userIndex;
|
|
|
|
m_iPlayerInvited = -1;
|
|
|
|
delete m_pUpsell;
|
|
m_pUpsell = nullptr;
|
|
|
|
if (ProfileManager.HasPlayStationPlus(iPad_checking))
|
|
{
|
|
this->GameInviteReceived(iPad_invited, m_pInviteInfo);
|
|
|
|
// m_pInviteInfo deleted by GameInviteReceived.
|
|
m_pInviteInfo = nullptr;
|
|
}
|
|
else
|
|
{
|
|
delete m_pInviteInfo;
|
|
m_pInviteInfo = nullptr;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
bool CGameNetworkManager::_RunNetworkGame(LPVOID lpParameter)
|
|
{
|
|
bool success = true;
|
|
|
|
bool isHost = g_NetworkManager.IsHost();
|
|
// Start the network game
|
|
Minecraft *pMinecraft=Minecraft::GetInstance();
|
|
success = StartNetworkGame(pMinecraft,lpParameter);
|
|
|
|
if(!success) return false;
|
|
|
|
if( isHost )
|
|
{
|
|
// We do not have a lobby, so the only players in the game at this point are local ones.
|
|
|
|
success = s_pPlatformNetworkManager->_RunNetworkGame();
|
|
if(!success)
|
|
{
|
|
app.SetAction(ProfileManager.GetPrimaryPad(),eAppAction_ExitWorld,(void *)TRUE);
|
|
return true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Client needs QNET_STATE_GAME_PLAY so that IsInGameplay() returns true
|
|
s_pPlatformNetworkManager->SetGamePlayState();
|
|
}
|
|
|
|
if( g_NetworkManager.IsLeavingGame() ) return false;
|
|
|
|
app.SetGameStarted(true);
|
|
|
|
// 4J-PB - if this is the trial game, start the trial timer
|
|
if(!ProfileManager.IsFullVersion())
|
|
{
|
|
ui.SetTrialTimerLimitSecs(MinecraftDynamicConfigurations::GetTrialTime());
|
|
app.SetTrialTimerStart();
|
|
}
|
|
//app.CloseXuiScenes(ProfileManager.GetPrimaryPad());
|
|
|
|
return success;
|
|
}
|
|
|
|
bool CGameNetworkManager::StartNetworkGame(Minecraft *minecraft, LPVOID lpParameter)
|
|
{
|
|
#ifdef _DURANGO
|
|
ProfileManager.SetDeferredSignoutEnabled(true);
|
|
#endif
|
|
|
|
int64_t seed = 0;
|
|
bool dedicatedNoLocalHostPlayer = false;
|
|
if (lpParameter != nullptr)
|
|
{
|
|
NetworkGameInitData *param = static_cast<NetworkGameInitData *>(lpParameter);
|
|
seed = param->seed;
|
|
dedicatedNoLocalHostPlayer = param->dedicatedNoLocalHostPlayer;
|
|
|
|
app.setLevelGenerationOptions(param->levelGen);
|
|
if(param->levelGen != nullptr)
|
|
{
|
|
if(app.getLevelGenerationOptions() == nullptr)
|
|
{
|
|
app.DebugPrintf("Game rule was not loaded, and seed is required. Exiting.\n");
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
param->seed = seed = app.getLevelGenerationOptions()->getLevelSeed();
|
|
|
|
if(param->levelGen->isTutorial())
|
|
{
|
|
// Load the tutorial save data here
|
|
if(param->levelGen->requiresBaseSave() && !param->levelGen->getBaseSavePath().empty() )
|
|
{
|
|
#ifdef _XBOX
|
|
#ifdef _TU_BUILD
|
|
wstring fileRoot = L"UPDATE:\\res\\GameRules\\" + param->levelGen->getBaseSavePath();
|
|
#else
|
|
wstring fileRoot = L"GAME:\\res\\TitleUpdate\\GameRules\\" + param->levelGen->getBaseSavePath();
|
|
#endif
|
|
#else
|
|
#ifdef _WINDOWS64
|
|
wstring fileRoot = L"Windows64Media\\Tutorial\\" + param->levelGen->getBaseSavePath();
|
|
File root(fileRoot);
|
|
if(!root.exists()) fileRoot = L"Windows64\\Tutorial\\" + param->levelGen->getBaseSavePath();
|
|
#elif defined(__ORBIS__)
|
|
wstring fileRoot = L"/app0/orbis/Tutorial/" + param->levelGen->getBaseSavePath();
|
|
#elif defined(__PSVITA__)
|
|
wstring fileRoot = L"PSVita/Tutorial/" + param->levelGen->getBaseSavePath();
|
|
#elif defined(__PS3__)
|
|
wstring fileRoot = L"PS3/Tutorial/" + param->levelGen->getBaseSavePath();
|
|
#else
|
|
wstring fileRoot = L"Tutorial\\" + param->levelGen->getBaseSavePath();
|
|
#endif
|
|
#endif
|
|
File grf(fileRoot);
|
|
if (grf.exists())
|
|
{
|
|
#ifdef _UNICODE
|
|
wstring path = grf.getPath();
|
|
const WCHAR *pchFilename=path.c_str();
|
|
HANDLE fileHandle = CreateFile(
|
|
pchFilename, // file name
|
|
GENERIC_READ, // access mode
|
|
0, // share mode // TODO 4J Stu - Will we need to share file? Probably not but...
|
|
nullptr, // Unused
|
|
OPEN_EXISTING , // how to create // TODO 4J Stu - Assuming that the file already exists if we are opening to read from it
|
|
FILE_FLAG_SEQUENTIAL_SCAN, // file attributes
|
|
nullptr // Unsupported
|
|
);
|
|
#else
|
|
const char *pchFilename=wstringtofilename(grf.getPath());
|
|
HANDLE fileHandle = CreateFile(
|
|
pchFilename, // file name
|
|
GENERIC_READ, // access mode
|
|
0, // share mode // TODO 4J Stu - Will we need to share file? Probably not but...
|
|
nullptr, // Unused
|
|
OPEN_EXISTING , // how to create // TODO 4J Stu - Assuming that the file already exists if we are opening to read from it
|
|
FILE_FLAG_SEQUENTIAL_SCAN, // file attributes
|
|
nullptr // Unsupported
|
|
);
|
|
#endif
|
|
|
|
if( fileHandle != INVALID_HANDLE_VALUE )
|
|
{
|
|
DWORD bytesRead,dwFileSize = GetFileSize(fileHandle,nullptr);
|
|
PBYTE pbData = (PBYTE) new BYTE[dwFileSize];
|
|
BOOL bSuccess = ReadFile(fileHandle,pbData,dwFileSize,&bytesRead,nullptr);
|
|
if(bSuccess==FALSE)
|
|
{
|
|
app.FatalLoadError();
|
|
}
|
|
CloseHandle(fileHandle);
|
|
|
|
// 4J-PB - is it possible that we can get here after a read fail and it's not an error?
|
|
param->levelGen->setBaseSaveData(pbData, dwFileSize);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static int64_t sseed = seed; // Create static version so this will be valid until next call to this function & whilst thread is running
|
|
ServerStoppedCreate(false);
|
|
if( g_NetworkManager.IsHost() )
|
|
{
|
|
ServerStoppedCreate(true);
|
|
ServerReadyCreate(true);
|
|
// Ready to go - create actual networking thread & start hosting
|
|
C4JThread* thread = new C4JThread(&CGameNetworkManager::ServerThreadProc, lpParameter, "Server", 256 * 1024);
|
|
#if defined __PS3__ || defined __PSVITA__
|
|
thread->SetPriority(THREAD_PRIORITY_BELOW_NORMAL);
|
|
#endif //__PS3__
|
|
|
|
thread->SetProcessor(CPU_CORE_SERVER);
|
|
thread->Run();
|
|
|
|
ServerReadyWait();
|
|
ServerReadyDestroy();
|
|
|
|
if( MinecraftServer::serverHalted() )
|
|
return false;
|
|
|
|
// printf("Server ready to go!\n");
|
|
}
|
|
else
|
|
{
|
|
Socket::Initialise(nullptr);
|
|
}
|
|
|
|
#ifndef _XBOX
|
|
Minecraft *pMinecraft = Minecraft::GetInstance();
|
|
// Make sure that we have transitioned through any joining/creating stages and are actually playing the game, so that we know the players should be valid
|
|
bool changedMessage = false;
|
|
while(!IsReadyToPlayOrIdle())
|
|
{
|
|
changedMessage = true;
|
|
pMinecraft->progressRenderer->progressStage( g_NetworkManager.CorrectErrorIDS(IDS_PROGRESS_SAVING_TO_DISC) ); // "Finalizing..." vaguest message I could find
|
|
pMinecraft->progressRenderer->progressStagePercentage( g_NetworkManager.GetJoiningReadyPercentage() );
|
|
Sleep(10);
|
|
}
|
|
if( changedMessage )
|
|
{
|
|
pMinecraft->progressRenderer->progressStagePercentage( 100 );
|
|
}
|
|
#endif
|
|
|
|
// If we aren't in session, then something bad must have happened - we aren't joining, creating or ready play
|
|
if(!IsInSession() )
|
|
{
|
|
MinecraftServer::HaltServer();
|
|
return false;
|
|
}
|
|
|
|
// 4J Stu - Wait a while to make sure that DLC is loaded. This is the last point before the network communication starts
|
|
// so the latest we can check this
|
|
while( !app.DLCInstallProcessCompleted() && app.DLCInstallPending() && !g_NetworkManager.IsLeavingGame() )
|
|
{
|
|
Sleep( 10 );
|
|
}
|
|
if( g_NetworkManager.IsLeavingGame() )
|
|
{
|
|
MinecraftServer::HaltServer();
|
|
return false;
|
|
}
|
|
|
|
// PRIMARY PLAYER
|
|
|
|
vector<ClientConnection *> createdConnections;
|
|
ClientConnection *connection = nullptr;
|
|
|
|
if( g_NetworkManager.IsHost() && dedicatedNoLocalHostPlayer )
|
|
{
|
|
app.DebugPrintf("Dedicated server mode: skipping local host client connection\n");
|
|
|
|
// Keep telemetry behavior consistent with the host path.
|
|
INT multiplayerInstanceId = TelemetryManager->GenerateMultiplayerInstanceId();
|
|
TelemetryManager->SetMultiplayerInstanceId(multiplayerInstanceId);
|
|
|
|
app.SetGameMode( eMode_Multiplayer );
|
|
}
|
|
else if( g_NetworkManager.IsHost() )
|
|
{
|
|
connection = new ClientConnection(minecraft, nullptr);
|
|
}
|
|
else
|
|
{
|
|
INetworkPlayer *pNetworkPlayer = g_NetworkManager.GetLocalPlayerByUserIndex(ProfileManager.GetLockedProfile());
|
|
if(pNetworkPlayer == nullptr)
|
|
{
|
|
MinecraftServer::HaltServer();
|
|
app.DebugPrintf("%d\n",ProfileManager.GetLockedProfile());
|
|
// If the player is nullptr here then something went wrong in the session setup, and continuing will end up in a crash
|
|
return false;
|
|
}
|
|
|
|
Socket *socket = pNetworkPlayer->GetSocket();
|
|
|
|
// Fix for #13259 - CRASH: Gameplay: loading process is halted when player loads saved data
|
|
if(socket == nullptr)
|
|
{
|
|
assert(false);
|
|
MinecraftServer::HaltServer();
|
|
// If the socket is nullptr here then something went wrong in the session setup, and continuing will end up in a crash
|
|
return false;
|
|
}
|
|
|
|
connection = new ClientConnection(minecraft, socket);
|
|
}
|
|
|
|
if (connection != nullptr)
|
|
{
|
|
if( !connection->createdOk )
|
|
{
|
|
assert(false);
|
|
delete connection;
|
|
connection = nullptr;
|
|
MinecraftServer::HaltServer();
|
|
return false;
|
|
}
|
|
|
|
connection->send(std::make_shared<PreLoginPacket>(minecraft->user->name));
|
|
|
|
// Tick connection until we're ready to go. The stages involved in this are:
|
|
// (1) Creating the ClientConnection sends a prelogin packet to the server
|
|
// (2) the server sends a prelogin back, which is handled by the clientConnection, and returns a login packet
|
|
// (3) the server sends a login back, which is handled by the client connection to start the game
|
|
if( !g_NetworkManager.IsHost() )
|
|
{
|
|
Minecraft::GetInstance()->progressRenderer->progressStart(IDS_PROGRESS_CONNECTING);
|
|
}
|
|
else
|
|
{
|
|
// 4J Stu - Host needs to generate a unique multiplayer id for sentient telemetry reporting
|
|
INT multiplayerInstanceId = TelemetryManager->GenerateMultiplayerInstanceId();
|
|
TelemetryManager->SetMultiplayerInstanceId(multiplayerInstanceId);
|
|
}
|
|
TexturePack *tPack = Minecraft::GetInstance()->skins->getSelected();
|
|
do
|
|
{
|
|
app.DebugPrintf("ticking connection A\n");
|
|
connection->tick();
|
|
|
|
// 4J Stu - We were ticking this way too fast which could cause the connection to time out
|
|
// The connections should tick at 20 per second
|
|
Sleep(50);
|
|
} while ( (IsInSession() && !connection->isStarted() && !connection->isClosed() && !g_NetworkManager.IsLeavingGame()) || tPack->isLoadingData() || (Minecraft::GetInstance()->skins->needsUIUpdate() || ui.IsReloadingSkin()) );
|
|
ui.CleanUpSkinReload();
|
|
|
|
// 4J Stu - Fix for #11279 - CRASH: TCR 001: BAS Game Stability: Signing out of game will cause title to crash
|
|
// We need to break out of the above loop if m_bLeavingGame is set, and close the connection
|
|
if( g_NetworkManager.IsLeavingGame() || !IsInSession() )
|
|
{
|
|
connection->close();
|
|
}
|
|
|
|
if( connection->isStarted() && !connection->isClosed() )
|
|
{
|
|
createdConnections.push_back( connection );
|
|
|
|
int primaryPad = ProfileManager.GetPrimaryPad();
|
|
app.SetRichPresenceContext(primaryPad,CONTEXT_GAME_STATE_BLANK);
|
|
if (GetPlayerCount() > 1) // Are we offline or online, and how many players are there
|
|
{
|
|
if (IsLocalGame()) ProfileManager.SetCurrentGameActivity(primaryPad,CONTEXT_PRESENCE_MULTIPLAYEROFFLINE,false);
|
|
else ProfileManager.SetCurrentGameActivity(primaryPad,CONTEXT_PRESENCE_MULTIPLAYER,false);
|
|
}
|
|
else
|
|
{
|
|
if(IsLocalGame()) ProfileManager.SetCurrentGameActivity(primaryPad,CONTEXT_PRESENCE_MULTIPLAYER_1POFFLINE,false);
|
|
else ProfileManager.SetCurrentGameActivity(primaryPad,CONTEXT_PRESENCE_MULTIPLAYER_1P,false);
|
|
}
|
|
|
|
|
|
// ALL OTHER LOCAL PLAYERS
|
|
for(int idx = 0; idx < XUSER_MAX_COUNT; ++idx)
|
|
{
|
|
// Already have setup the primary pad
|
|
if(idx == ProfileManager.GetPrimaryPad() ) continue;
|
|
|
|
if( GetLocalPlayerByUserIndex(idx) != nullptr && !ProfileManager.IsSignedIn(idx) )
|
|
{
|
|
INetworkPlayer *pNetworkPlayer = g_NetworkManager.GetLocalPlayerByUserIndex(idx);
|
|
Socket *socket = pNetworkPlayer->GetSocket();
|
|
app.DebugPrintf("Closing socket due to player %d not being signed in any more\n");
|
|
if( !socket->close(false) ) socket->close(true);
|
|
|
|
continue;
|
|
}
|
|
|
|
// By default when we host we only have the local player, but currently allow multiple local players to join
|
|
// when joining any other way, so just because they are signed in doesn't mean they are in the session
|
|
// 4J Stu - If they are in the session, then we should add them to the game. Otherwise we won't be able to add them later
|
|
INetworkPlayer *pNetworkPlayer = g_NetworkManager.GetLocalPlayerByUserIndex(idx);
|
|
if( pNetworkPlayer == nullptr )
|
|
continue;
|
|
|
|
ClientConnection *connection;
|
|
|
|
Socket *socket = pNetworkPlayer->GetSocket();
|
|
connection = new ClientConnection(minecraft, socket, idx);
|
|
|
|
minecraft->addPendingLocalConnection(idx, connection);
|
|
//minecraft->createExtraLocalPlayer(idx, (convStringToWstring( ProfileManager.GetGamertag(idx) )).c_str(), idx, connection);
|
|
|
|
// Open the socket on the server end to accept incoming data
|
|
Socket::addIncomingSocket(socket);
|
|
|
|
connection->send(std::make_shared<PreLoginPacket>(convStringToWstring(ProfileManager.GetGamertag(idx))));
|
|
|
|
createdConnections.push_back( connection );
|
|
|
|
// Tick connection until we're ready to go. The stages involved in this are:
|
|
// (1) Creating the ClientConnection sends a prelogin packet to the server
|
|
// (2) the server sends a prelogin back, which is handled by the clientConnection, and returns a login packet
|
|
// (3) the server sends a login back, which is handled by the client connection to start the game
|
|
do
|
|
{
|
|
// We need to keep ticking the connections for players that already logged in
|
|
for (auto& it : createdConnections )
|
|
{
|
|
if ( it )
|
|
it->tick();
|
|
}
|
|
|
|
// 4J Stu - We were ticking this way too fast which could cause the connection to time out
|
|
// The connections should tick at 20 per second
|
|
Sleep(50);
|
|
app.DebugPrintf("<***> %d %d %d %d %d\n",IsInSession(), !connection->isStarted(),!connection->isClosed(),ProfileManager.IsSignedIn(idx),!g_NetworkManager.IsLeavingGame());
|
|
#if defined _XBOX || __PS3__
|
|
} while (IsInSession() && !connection->isStarted() && !connection->isClosed() && ProfileManager.IsSignedIn(idx) && !g_NetworkManager.IsLeavingGame() );
|
|
#else
|
|
// TODO - This SHOULD be something just like the code above but temporarily changing here so that we don't have to depend on the profilemanager behaviour
|
|
} while (IsInSession() && !connection->isStarted() && !connection->isClosed() && !g_NetworkManager.IsLeavingGame() );
|
|
#endif
|
|
|
|
// 4J Stu - Fix for #11279 - CRASH: TCR 001: BAS Game Stability: Signing out of game will cause title to crash
|
|
// We need to break out of the above loop if m_bLeavingGame is set, and stop creating new connections
|
|
// The connections in the createdConnections vector get closed at the end of the thread
|
|
if( g_NetworkManager.IsLeavingGame() || !IsInSession() ) break;
|
|
|
|
if( ProfileManager.IsSignedIn(idx) && !connection->isClosed() )
|
|
{
|
|
app.SetRichPresenceContext(idx,CONTEXT_GAME_STATE_BLANK);
|
|
if (IsLocalGame()) ProfileManager.SetCurrentGameActivity(idx,CONTEXT_PRESENCE_MULTIPLAYEROFFLINE,false);
|
|
else ProfileManager.SetCurrentGameActivity(idx,CONTEXT_PRESENCE_MULTIPLAYER,false);
|
|
}
|
|
else
|
|
{
|
|
connection->close();
|
|
auto it = find(createdConnections.begin(), createdConnections.end(), connection);
|
|
if(it != createdConnections.end() ) createdConnections.erase( it );
|
|
}
|
|
}
|
|
|
|
app.SetGameMode( eMode_Multiplayer );
|
|
}
|
|
else if ( connection->isClosed() || !IsInSession())
|
|
{
|
|
// assert(false);
|
|
MinecraftServer::HaltServer();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
if(g_NetworkManager.IsLeavingGame() || !IsInSession() )
|
|
{
|
|
for (auto& it : createdConnections)
|
|
{
|
|
it->close();
|
|
}
|
|
// assert(false);
|
|
MinecraftServer::HaltServer();
|
|
return false;
|
|
}
|
|
|
|
// Catch in-case server has been halted (by a player signout).
|
|
if ( MinecraftServer::serverHalted() )
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
int CGameNetworkManager::CorrectErrorIDS(int IDS)
|
|
{
|
|
return s_pPlatformNetworkManager->CorrectErrorIDS(IDS);
|
|
}
|
|
|
|
int CGameNetworkManager::GetLocalPlayerMask(int playerIndex)
|
|
{
|
|
return s_pPlatformNetworkManager->GetLocalPlayerMask( playerIndex );
|
|
}
|
|
|
|
int CGameNetworkManager::GetPlayerCount()
|
|
{
|
|
return s_pPlatformNetworkManager->GetPlayerCount();
|
|
}
|
|
|
|
int CGameNetworkManager::GetOnlinePlayerCount()
|
|
{
|
|
return s_pPlatformNetworkManager->GetOnlinePlayerCount();
|
|
}
|
|
|
|
bool CGameNetworkManager::AddLocalPlayerByUserIndex( int userIndex )
|
|
{
|
|
return s_pPlatformNetworkManager->AddLocalPlayerByUserIndex( userIndex );
|
|
}
|
|
|
|
bool CGameNetworkManager::RemoveLocalPlayerByUserIndex( int userIndex )
|
|
{
|
|
return s_pPlatformNetworkManager->RemoveLocalPlayerByUserIndex( userIndex );
|
|
}
|
|
|
|
INetworkPlayer *CGameNetworkManager::GetLocalPlayerByUserIndex(int userIndex )
|
|
{
|
|
return s_pPlatformNetworkManager->GetLocalPlayerByUserIndex( userIndex );
|
|
}
|
|
|
|
INetworkPlayer *CGameNetworkManager::GetPlayerByIndex(int playerIndex)
|
|
{
|
|
return s_pPlatformNetworkManager->GetPlayerByIndex( playerIndex );
|
|
}
|
|
|
|
INetworkPlayer *CGameNetworkManager::GetPlayerByXuid(PlayerUID xuid)
|
|
{
|
|
return s_pPlatformNetworkManager->GetPlayerByXuid( xuid );
|
|
}
|
|
|
|
INetworkPlayer *CGameNetworkManager::GetPlayerBySmallId(unsigned char smallId)
|
|
{
|
|
return s_pPlatformNetworkManager->GetPlayerBySmallId( smallId );
|
|
}
|
|
|
|
#ifdef _DURANGO
|
|
wstring CGameNetworkManager::GetDisplayNameByGamertag(wstring gamertag)
|
|
{
|
|
return s_pPlatformNetworkManager->GetDisplayNameByGamertag(gamertag);
|
|
}
|
|
#endif
|
|
|
|
INetworkPlayer *CGameNetworkManager::GetHostPlayer()
|
|
{
|
|
return s_pPlatformNetworkManager->GetHostPlayer();
|
|
}
|
|
|
|
void CGameNetworkManager::RegisterPlayerChangedCallback(int iPad, void (*callback)(void *callbackParam, INetworkPlayer *pPlayer, bool leaving), void *callbackParam)
|
|
{
|
|
s_pPlatformNetworkManager->RegisterPlayerChangedCallback( iPad, callback, callbackParam );
|
|
}
|
|
|
|
void CGameNetworkManager::UnRegisterPlayerChangedCallback(int iPad, void (*callback)(void *callbackParam, INetworkPlayer *pPlayer, bool leaving), void *callbackParam)
|
|
{
|
|
s_pPlatformNetworkManager->UnRegisterPlayerChangedCallback( iPad, callback, callbackParam );
|
|
}
|
|
|
|
void CGameNetworkManager::HandleSignInChange()
|
|
{
|
|
s_pPlatformNetworkManager->HandleSignInChange();
|
|
}
|
|
|
|
bool CGameNetworkManager::ShouldMessageForFullSession()
|
|
{
|
|
return s_pPlatformNetworkManager->ShouldMessageForFullSession();
|
|
}
|
|
|
|
bool CGameNetworkManager::IsInSession()
|
|
{
|
|
return s_pPlatformNetworkManager->IsInSession();
|
|
}
|
|
|
|
bool CGameNetworkManager::IsInGameplay()
|
|
{
|
|
return s_pPlatformNetworkManager->IsInGameplay();
|
|
}
|
|
|
|
bool CGameNetworkManager::IsReadyToPlayOrIdle()
|
|
{
|
|
return s_pPlatformNetworkManager->IsReadyToPlayOrIdle();
|
|
}
|
|
|
|
bool CGameNetworkManager::IsLeavingGame()
|
|
{
|
|
return s_pPlatformNetworkManager->IsLeavingGame();
|
|
}
|
|
|
|
bool CGameNetworkManager::SetLocalGame(bool isLocal)
|
|
{
|
|
return s_pPlatformNetworkManager->SetLocalGame( isLocal );
|
|
}
|
|
|
|
bool CGameNetworkManager::IsLocalGame()
|
|
{
|
|
return s_pPlatformNetworkManager->IsLocalGame();
|
|
}
|
|
|
|
void CGameNetworkManager::SetPrivateGame(bool isPrivate)
|
|
{
|
|
s_pPlatformNetworkManager->SetPrivateGame( isPrivate );
|
|
}
|
|
|
|
bool CGameNetworkManager::IsPrivateGame()
|
|
{
|
|
return s_pPlatformNetworkManager->IsPrivateGame();
|
|
}
|
|
|
|
void CGameNetworkManager::HostGame(int localUsersMask, bool bOnlineGame, bool bIsPrivate, unsigned char publicSlots, unsigned char privateSlots)
|
|
{
|
|
// 4J Stu - clear any previous connection errors
|
|
Minecraft::GetInstance()->clearConnectionFailed();
|
|
|
|
s_pPlatformNetworkManager->HostGame( localUsersMask, bOnlineGame, bIsPrivate, publicSlots, privateSlots );
|
|
}
|
|
|
|
bool CGameNetworkManager::IsHost()
|
|
{
|
|
return (s_pPlatformNetworkManager->IsHost() == TRUE);
|
|
}
|
|
|
|
bool CGameNetworkManager::IsInStatsEnabledSession()
|
|
{
|
|
return s_pPlatformNetworkManager->IsInStatsEnabledSession();
|
|
}
|
|
|
|
bool CGameNetworkManager::SessionHasSpace(unsigned int spaceRequired)
|
|
{
|
|
return s_pPlatformNetworkManager->SessionHasSpace( spaceRequired );
|
|
}
|
|
|
|
vector<FriendSessionInfo *> *CGameNetworkManager::GetSessionList(int iPad, int localPlayers, bool partyOnly)
|
|
{
|
|
return s_pPlatformNetworkManager->GetSessionList( iPad, localPlayers, partyOnly );
|
|
}
|
|
|
|
bool CGameNetworkManager::GetGameSessionInfo(int iPad, SessionID sessionId,FriendSessionInfo *foundSession)
|
|
{
|
|
return s_pPlatformNetworkManager->GetGameSessionInfo( iPad, sessionId, foundSession );
|
|
}
|
|
|
|
void CGameNetworkManager::SetSessionsUpdatedCallback( void (*SessionsUpdatedCallback)(LPVOID pParam), LPVOID pSearchParam )
|
|
{
|
|
s_pPlatformNetworkManager->SetSessionsUpdatedCallback( SessionsUpdatedCallback, pSearchParam );
|
|
}
|
|
|
|
void CGameNetworkManager::GetFullFriendSessionInfo( FriendSessionInfo *foundSession, void (* FriendSessionUpdatedFn)(bool success, void *pParam), void *pParam )
|
|
{
|
|
s_pPlatformNetworkManager->GetFullFriendSessionInfo(foundSession, FriendSessionUpdatedFn, pParam);
|
|
}
|
|
|
|
void CGameNetworkManager::ForceFriendsSessionRefresh()
|
|
{
|
|
s_pPlatformNetworkManager->ForceFriendsSessionRefresh();
|
|
}
|
|
|
|
bool CGameNetworkManager::JoinGameFromInviteInfo( int userIndex, int userMask, const INVITE_INFO *pInviteInfo)
|
|
{
|
|
return s_pPlatformNetworkManager->JoinGameFromInviteInfo( userIndex, userMask, pInviteInfo );
|
|
}
|
|
|
|
CGameNetworkManager::eJoinGameResult CGameNetworkManager::JoinGame(FriendSessionInfo *searchResult, int localUsersMask)
|
|
{
|
|
app.SetTutorialMode( false );
|
|
g_NetworkManager.SetLocalGame(false);
|
|
|
|
int primaryUserIndex = ProfileManager.GetLockedProfile();
|
|
|
|
// 4J-PB - clear any previous connection errors
|
|
Minecraft::GetInstance()->clearConnectionFailed();
|
|
|
|
// Make sure that the Primary Pad is in by default
|
|
localUsersMask |= GetLocalPlayerMask( ProfileManager.GetPrimaryPad() );
|
|
|
|
return static_cast<eJoinGameResult>(s_pPlatformNetworkManager->JoinGame(searchResult, localUsersMask, primaryUserIndex));
|
|
}
|
|
|
|
void CGameNetworkManager::CancelJoinGame(LPVOID lpParam)
|
|
{
|
|
#ifdef _XBOX_ONE
|
|
s_pPlatformNetworkManager->CancelJoinGame();
|
|
#endif
|
|
}
|
|
|
|
bool CGameNetworkManager::LeaveGame(bool bMigrateHost)
|
|
{
|
|
Minecraft::GetInstance()->gui->clearMessages();
|
|
return s_pPlatformNetworkManager->LeaveGame( bMigrateHost );
|
|
}
|
|
|
|
int CGameNetworkManager::JoinFromInvite_SignInReturned(void *pParam,bool bContinue, int iPad)
|
|
{
|
|
INVITE_INFO * pInviteInfo = static_cast<INVITE_INFO *>(pParam);
|
|
|
|
if(bContinue==true)
|
|
{
|
|
#ifdef __ORBIS__
|
|
// Check if PSN is unavailable because of age restriction
|
|
int npAvailability = ProfileManager.getNPAvailability(iPad);
|
|
if (npAvailability == SCE_NP_ERROR_AGE_RESTRICTION)
|
|
{
|
|
UINT uiIDA[1];
|
|
uiIDA[0] = IDS_OK;
|
|
ui.RequestErrorMessage(IDS_ONLINE_SERVICE_TITLE, IDS_CONTENT_RESTRICTION, uiIDA, 1, iPad);
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
app.DebugPrintf("JoinFromInvite_SignInReturned, iPad %d\n",iPad);
|
|
// It's possible that the player has not signed in - they can back out
|
|
if(ProfileManager.IsSignedIn(iPad) && ProfileManager.IsSignedInLive(iPad) )
|
|
{
|
|
app.DebugPrintf("JoinFromInvite_SignInReturned, passed sign-in tests\n");
|
|
int localUsersMask = 0;
|
|
int joiningUsers = 0;
|
|
|
|
bool noPrivileges = false;
|
|
for(unsigned int index = 0; index < XUSER_MAX_COUNT; ++index)
|
|
{
|
|
if(ProfileManager.IsSignedIn(index) )
|
|
{
|
|
++joiningUsers;
|
|
if( !ProfileManager.AllowedToPlayMultiplayer(index) ) noPrivileges = true;
|
|
localUsersMask |= GetLocalPlayerMask( index );
|
|
}
|
|
}
|
|
|
|
// Check if user-created content is allowed, as we cannot play multiplayer if it's not
|
|
bool noUGC = false;
|
|
#if defined(__PS3__) || defined(__PSVITA__)
|
|
ProfileManager.GetChatAndContentRestrictions(iPad,false,&noUGC,nullptr,nullptr);
|
|
#elif defined(__ORBIS__)
|
|
ProfileManager.GetChatAndContentRestrictions(iPad,false,nullptr,&noUGC,nullptr);
|
|
#endif
|
|
|
|
if(noUGC)
|
|
{
|
|
int messageText = IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_SINGLE_LOCAL;
|
|
if(joiningUsers > 1) messageText = IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_ALL_LOCAL;
|
|
|
|
ui.RequestUGCMessageBox(IDS_CONNECTION_FAILED, messageText);
|
|
}
|
|
else if(noPrivileges)
|
|
{
|
|
UINT uiIDA[1];
|
|
uiIDA[0]=IDS_CONFIRM_OK;
|
|
ui.RequestErrorMessage( IDS_NO_MULTIPLAYER_PRIVILEGE_TITLE, IDS_NO_MULTIPLAYER_PRIVILEGE_JOIN_TEXT, uiIDA,1,ProfileManager.GetPrimaryPad());
|
|
}
|
|
else
|
|
{
|
|
#if defined(__ORBIS__) || defined(__PSVITA__)
|
|
bool chatRestricted = false;
|
|
ProfileManager.GetChatAndContentRestrictions(iPad,false,&chatRestricted,nullptr,nullptr);
|
|
if(chatRestricted)
|
|
{
|
|
ProfileManager.DisplaySystemMessage( 0, ProfileManager.GetPrimaryPad() );
|
|
}
|
|
#endif
|
|
ProfileManager.SetLockedProfile(iPad);
|
|
ProfileManager.SetPrimaryPad(iPad);
|
|
|
|
g_NetworkManager.SetLocalGame(false);
|
|
|
|
// If the player was signed in before selecting play, we'll not have read the profile yet, so query the sign-in status to get this to happen
|
|
ProfileManager.QuerySigninStatus();
|
|
|
|
// 4J-PB - clear any previous connection errors
|
|
Minecraft::GetInstance()->clearConnectionFailed();
|
|
|
|
// change the minecraft player name
|
|
Minecraft::GetInstance()->user->name = convStringToWstring( ProfileManager.GetGamertag(ProfileManager.GetPrimaryPad()));
|
|
|
|
bool success = g_NetworkManager.JoinGameFromInviteInfo(
|
|
iPad, // dwUserIndex
|
|
localUsersMask, // dwUserMask
|
|
pInviteInfo ); // pInviteInfo
|
|
if( !success )
|
|
{
|
|
app.DebugPrintf( "Failed joining game from invite\n" );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
app.DebugPrintf("JoinFromInvite_SignInReturned, failed sign-in tests :%d %d\n",ProfileManager.IsSignedIn(iPad),ProfileManager.IsSignedInLive(iPad));
|
|
}
|
|
}
|
|
return 0;
|
|
|
|
}
|
|
|
|
void CGameNetworkManager::UpdateAndSetGameSessionData(INetworkPlayer *pNetworkPlayerLeaving)
|
|
{
|
|
Minecraft *pMinecraft = Minecraft::GetInstance();
|
|
TexturePack *tPack = pMinecraft->skins->getSelected();
|
|
s_pPlatformNetworkManager->SetSessionTexturePackParentId( tPack->getDLCParentPackId() );
|
|
s_pPlatformNetworkManager->SetSessionSubTexturePackId( tPack->getDLCSubPackId() );
|
|
|
|
s_pPlatformNetworkManager->UpdateAndSetGameSessionData( pNetworkPlayerLeaving );
|
|
}
|
|
|
|
void CGameNetworkManager::SendInviteGUI(int quadrant)
|
|
{
|
|
s_pPlatformNetworkManager->SendInviteGUI(quadrant);
|
|
}
|
|
|
|
void CGameNetworkManager::ResetLeavingGame()
|
|
{
|
|
s_pPlatformNetworkManager->ResetLeavingGame();
|
|
}
|
|
|
|
bool CGameNetworkManager::IsNetworkThreadRunning()
|
|
{
|
|
return m_bNetworkThreadRunning;;
|
|
}
|
|
|
|
int CGameNetworkManager::RunNetworkGameThreadProc( void* lpParameter )
|
|
{
|
|
// Share AABB & Vec3 pools with default (main thread) - should be ok as long as we don't tick the main thread whilst this thread is running
|
|
AABB::UseDefaultThreadStorage();
|
|
Vec3::UseDefaultThreadStorage();
|
|
Compression::UseDefaultThreadStorage();
|
|
Tile::CreateNewThreadStorage();
|
|
IntCache::CreateNewThreadStorage();
|
|
|
|
g_NetworkManager.m_bNetworkThreadRunning = true;
|
|
bool success = g_NetworkManager._RunNetworkGame(lpParameter);
|
|
g_NetworkManager.m_bNetworkThreadRunning = false;
|
|
if( !success)
|
|
{
|
|
TexturePack *tPack = Minecraft::GetInstance()->skins->getSelected();
|
|
while ( tPack->isLoadingData() || (Minecraft::GetInstance()->skins->needsUIUpdate() || ui.IsReloadingSkin()) )
|
|
{
|
|
Sleep(1);
|
|
}
|
|
ui.CleanUpSkinReload();
|
|
if(app.GetDisconnectReason() == DisconnectPacket::eDisconnect_None)
|
|
{
|
|
app.SetDisconnectReason( DisconnectPacket::eDisconnect_ConnectionCreationFailed );
|
|
}
|
|
// If we failed before the server started, clear the game rules. Otherwise the server will clear it up.
|
|
if(MinecraftServer::getInstance() == nullptr) app.m_gameRules.unloadCurrentGameRules();
|
|
Tile::ReleaseThreadStorage();
|
|
return -1;
|
|
}
|
|
|
|
#ifdef __PSVITA__
|
|
// 4J-JEV: Wait for the loading/saving to finish.
|
|
while (StorageManager.GetSaveState() != C4JStorage::ESaveGame_Idle) Sleep(10);
|
|
#endif
|
|
|
|
Tile::ReleaseThreadStorage();
|
|
IntCache::ReleaseThreadStorage();
|
|
return 0;
|
|
}
|
|
|
|
int CGameNetworkManager::ServerThreadProc( void* lpParameter )
|
|
{
|
|
int64_t seed = 0;
|
|
if (lpParameter != nullptr)
|
|
{
|
|
NetworkGameInitData *param = static_cast<NetworkGameInitData *>(lpParameter);
|
|
seed = param->seed;
|
|
app.SetGameHostOption(eGameHostOption_All,param->settings);
|
|
|
|
// 4J Stu - If we are loading a DLC save that's separate from the texture pack, load
|
|
if (param != nullptr && param->levelGen != nullptr && param->levelGen->isFromDLC())
|
|
{
|
|
while((Minecraft::GetInstance()->skins->needsUIUpdate() || ui.IsReloadingSkin()))
|
|
{
|
|
Sleep(1);
|
|
}
|
|
param->levelGen->loadBaseSaveData();
|
|
|
|
while (!param->levelGen->hasLoadedData())
|
|
{
|
|
Sleep(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
SetThreadName(-1, "Minecraft Server thread");
|
|
AABB::CreateNewThreadStorage();
|
|
Vec3::CreateNewThreadStorage();
|
|
IntCache::CreateNewThreadStorage();
|
|
Compression::UseDefaultThreadStorage();
|
|
OldChunkStorage::UseDefaultThreadStorage();
|
|
Entity::useSmallIds();
|
|
Level::enableLightingCache();
|
|
Tile::CreateNewThreadStorage();
|
|
FireworksRecipe::CreateNewThreadStorage();
|
|
|
|
MinecraftServer::main(seed, lpParameter); //saveData, app.GetGameHostOption(eGameHostOption_All));
|
|
|
|
Tile::ReleaseThreadStorage();
|
|
AABB::ReleaseThreadStorage();
|
|
Vec3::ReleaseThreadStorage();
|
|
IntCache::ReleaseThreadStorage();
|
|
Level::destroyLightingCache();
|
|
|
|
if(lpParameter != nullptr) delete lpParameter;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
int CGameNetworkManager::ExitAndJoinFromInviteThreadProc( void* lpParam )
|
|
{
|
|
// Share AABB & Vec3 pools with default (main thread) - should be ok as long as we don't tick the main thread whilst this thread is running
|
|
AABB::UseDefaultThreadStorage();
|
|
Vec3::UseDefaultThreadStorage();
|
|
Compression::UseDefaultThreadStorage();
|
|
|
|
//app.SetGameStarted(false);
|
|
UIScene_PauseMenu::_ExitWorld(nullptr);
|
|
|
|
while( g_NetworkManager.IsInSession() )
|
|
{
|
|
Sleep(1);
|
|
}
|
|
|
|
// Xbox should always be online when receiving invites - on PS3 we need to check & ask the user to sign in
|
|
#if !defined(__PS3__) && !defined(__PSVITA__)
|
|
JoinFromInviteData *inviteData = static_cast<JoinFromInviteData *>(lpParam);
|
|
app.SetAction(inviteData->dwUserIndex, eAppAction_JoinFromInvite, lpParam);
|
|
#else
|
|
if(ProfileManager.IsSignedInLive(ProfileManager.GetPrimaryPad()))
|
|
{
|
|
JoinFromInviteData *inviteData = (JoinFromInviteData *)lpParam;
|
|
app.SetAction(inviteData->dwUserIndex, eAppAction_JoinFromInvite, lpParam);
|
|
}
|
|
else
|
|
{
|
|
UINT uiIDA[2];
|
|
uiIDA[0]=IDS_PRO_NOTONLINE_ACCEPT;
|
|
uiIDA[1]=IDS_PRO_NOTONLINE_DECLINE;
|
|
ui.RequestErrorMessage(IDS_PRO_NOTONLINE_TITLE, IDS_PRO_NOTONLINE_TEXT, uiIDA, 2, ProfileManager.GetPrimaryPad(),&CGameNetworkManager::MustSignInReturned_0,lpParam);
|
|
}
|
|
#endif
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
#if defined __PS3__ || defined __PSVITA__ || defined __ORBIS__
|
|
// This case happens when we have been returned from the game to the main menu after receiving an invite and are now trying to go back in to join the new game
|
|
// The pair of methods MustSignInReturned_0 & PSNSignInReturned_0 handle this
|
|
int CGameNetworkManager::MustSignInReturned_0(void *pParam,int iPad,C4JStorage::EMessageResult result)
|
|
{
|
|
if(result==C4JStorage::EMessage_ResultAccept)
|
|
{
|
|
#ifdef __PS3__
|
|
SQRNetworkManager_PS3::AttemptPSNSignIn(&CGameNetworkManager::PSNSignInReturned_0, pParam,true);
|
|
#elif defined __PSVITA__
|
|
SQRNetworkManager_Vita::AttemptPSNSignIn(&CGameNetworkManager::PSNSignInReturned_0, pParam,true);
|
|
#elif defined __ORBIS__
|
|
SQRNetworkManager_Orbis::AttemptPSNSignIn(&CGameNetworkManager::PSNSignInReturned_0, pParam,true);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
app.SetAction(0,eAppAction_Idle);
|
|
ui.NavigateToHomeMenu();
|
|
ui.UpdatePlayerBasePositions();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int CGameNetworkManager::PSNSignInReturned_0(void* pParam, bool bContinue, int iPad)
|
|
{
|
|
JoinFromInviteData *inviteData = (JoinFromInviteData *)pParam;
|
|
|
|
// If the invite data isn't set up yet (indicated by it being all zeroes, easiest detected via the net version), then try and get it again... this can happen if we got
|
|
// the invite whilst signed out
|
|
|
|
if( bContinue )
|
|
{
|
|
if(inviteData->pInviteInfo->netVersion == 0)
|
|
{
|
|
#if defined __PS3__ || defined __VITA__
|
|
if(!SQRNetworkManager_PS3::UpdateInviteData((SQRNetworkManager::PresenceSyncInfo *)inviteData->pInviteInfo))
|
|
{
|
|
bContinue = false;
|
|
}
|
|
#elif defined __ORBIS__
|
|
// TODO: No Orbis equivalent (should there be?)
|
|
#endif
|
|
}
|
|
}
|
|
|
|
if( bContinue )
|
|
{
|
|
app.SetAction(inviteData->dwUserIndex, eAppAction_JoinFromInvite, pParam);
|
|
}
|
|
else
|
|
{
|
|
app.SetAction(inviteData->dwUserIndex,eAppAction_Idle);
|
|
ui.NavigateToHomeMenu();
|
|
ui.UpdatePlayerBasePositions();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// This case happens when we were in the main menus when we got an invite, and weren't signed in... now can proceed with the normal flow of code for this situation
|
|
// The pair of methods MustSignInReturned_1 & PSNSignInReturned_1 handle this
|
|
int CGameNetworkManager::MustSignInReturned_1(void *pParam,int iPad,C4JStorage::EMessageResult result)
|
|
{
|
|
if(result==C4JStorage::EMessage_ResultAccept)
|
|
{
|
|
#ifdef __PS3__
|
|
SQRNetworkManager_PS3::AttemptPSNSignIn(&CGameNetworkManager::PSNSignInReturned_1, pParam,true);
|
|
#elif defined __PSVITA__
|
|
SQRNetworkManager_Vita::AttemptPSNSignIn(&CGameNetworkManager::PSNSignInReturned_1, pParam,true);
|
|
#elif defined __ORBIS__
|
|
SQRNetworkManager_Orbis::AttemptPSNSignIn(&CGameNetworkManager::PSNSignInReturned_1, pParam,true);
|
|
#endif
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int CGameNetworkManager::PSNSignInReturned_1(void* pParam, bool bContinue, int iPad)
|
|
{
|
|
INVITE_INFO *inviteInfo = (INVITE_INFO *)pParam;
|
|
|
|
// If the invite data isn't set up yet (indicated by it being all zeroes, easiest detected via the net version), then try and get it again... this can happen if we got
|
|
// the invite whilst signed out
|
|
|
|
if( bContinue )
|
|
{
|
|
if(inviteInfo->netVersion == 0)
|
|
{
|
|
#if defined __PS3__ || defined __VITA__
|
|
if(!SQRNetworkManager_PS3::UpdateInviteData((SQRNetworkManager::PresenceSyncInfo *)inviteInfo))
|
|
{
|
|
bContinue = false;
|
|
}
|
|
#elif defined __ORBIS__
|
|
// TODO: No Orbis equivalent (should there be?)
|
|
#endif
|
|
|
|
}
|
|
}
|
|
|
|
if( bContinue )
|
|
{
|
|
g_NetworkManager.HandleInviteWhenInMenus(0, inviteInfo);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
void CGameNetworkManager::_LeaveGame()
|
|
{
|
|
s_pPlatformNetworkManager->_LeaveGame(false, true);
|
|
}
|
|
|
|
int CGameNetworkManager::ChangeSessionTypeThreadProc( void* lpParam )
|
|
{
|
|
// Share AABB & Vec3 pools with default (main thread) - should be ok as long as we don't tick the main thread whilst this thread is running
|
|
AABB::UseDefaultThreadStorage();
|
|
Vec3::UseDefaultThreadStorage();
|
|
Compression::UseDefaultThreadStorage();
|
|
|
|
Minecraft *pMinecraft = Minecraft::GetInstance();
|
|
MinecraftServer *pServer = MinecraftServer::getInstance();
|
|
|
|
#if defined(__PS3__) || defined(__ORBIS__) || defined __PSVITA__
|
|
UINT uiIDA[1];
|
|
uiIDA[0]=IDS_CONFIRM_OK;
|
|
if( g_NetworkManager.m_bLastDisconnectWasLostRoomOnly )
|
|
{
|
|
if(g_NetworkManager.m_bSignedOutofPSN)
|
|
{
|
|
C4JStorage::EMessageResult result = ui.RequestErrorMessage( IDS_PROGRESS_CONVERTING_TO_OFFLINE_GAME, IDS_ERROR_PSN_SIGN_OUT, uiIDA,1,ProfileManager.GetPrimaryPad());
|
|
}
|
|
else
|
|
{
|
|
C4JStorage::EMessageResult result = ui.RequestErrorMessage( IDS_ERROR_NETWORK_TITLE, IDS_PROGRESS_CONVERTING_TO_OFFLINE_GAME, uiIDA,1,ProfileManager.GetPrimaryPad());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
C4JStorage::EMessageResult result = ui.RequestErrorMessage( IDS_CONNECTION_LOST, g_NetworkManager.CorrectErrorIDS(IDS_CONNECTION_LOST_LIVE_NO_EXIT), uiIDA,1,ProfileManager.GetPrimaryPad());
|
|
}
|
|
|
|
// Swap these two messages around as one is too long to display at 480
|
|
pMinecraft->progressRenderer->progressStartNoAbort( IDS_PROGRESS_CONVERTING_TO_OFFLINE_GAME );
|
|
pMinecraft->progressRenderer->progressStage( -1 ); //g_NetworkManager.CorrectErrorIDS(IDS_CONNECTION_LOST_LIVE_NO_EXIT) );
|
|
#elif defined(_XBOX_ONE)
|
|
if( g_NetworkManager.m_bFullSessionMessageOnNextSessionChange )
|
|
{
|
|
UINT uiIDA[1];
|
|
uiIDA[0]=IDS_CONFIRM_OK;
|
|
C4JStorage::EMessageResult result = ui.RequestErrorMessage( IDS_PROGRESS_CONVERTING_TO_OFFLINE_GAME, IDS_IN_PARTY_SESSION_FULL, uiIDA,1,ProfileManager.GetPrimaryPad());
|
|
pMinecraft->progressRenderer->progressStartNoAbort( IDS_PROGRESS_CONVERTING_TO_OFFLINE_GAME );
|
|
pMinecraft->progressRenderer->progressStage( -1 );
|
|
}
|
|
else
|
|
{
|
|
pMinecraft->progressRenderer->progressStartNoAbort( g_NetworkManager.CorrectErrorIDS(IDS_CONNECTION_LOST_LIVE_NO_EXIT) );
|
|
pMinecraft->progressRenderer->progressStage( IDS_PROGRESS_CONVERTING_TO_OFFLINE_GAME );
|
|
}
|
|
|
|
#else
|
|
pMinecraft->progressRenderer->progressStartNoAbort( g_NetworkManager.CorrectErrorIDS(IDS_CONNECTION_LOST_LIVE_NO_EXIT) );
|
|
pMinecraft->progressRenderer->progressStage( IDS_PROGRESS_CONVERTING_TO_OFFLINE_GAME );
|
|
#endif
|
|
|
|
while( app.GetXuiServerAction(ProfileManager.GetPrimaryPad() ) != eXuiServerAction_Idle && !MinecraftServer::serverHalted() )
|
|
{
|
|
Sleep(10);
|
|
}
|
|
app.SetXuiServerAction(ProfileManager.GetPrimaryPad(),eXuiServerAction_PauseServer,(void *)TRUE);
|
|
|
|
// wait for the server to be in a non-ticking state
|
|
pServer->m_serverPausedEvent->WaitForSignal(INFINITE);
|
|
|
|
#if defined(__PS3__) || defined(__ORBIS__) || defined __PSVITA__
|
|
// Swap these two messages around as one is too long to display at 480
|
|
pMinecraft->progressRenderer->progressStartNoAbort( IDS_PROGRESS_CONVERTING_TO_OFFLINE_GAME );
|
|
pMinecraft->progressRenderer->progressStage( -1 ); //g_NetworkManager.CorrectErrorIDS(IDS_CONNECTION_LOST_LIVE_NO_EXIT) );
|
|
#elif defined(_XBOX_ONE)
|
|
if( g_NetworkManager.m_bFullSessionMessageOnNextSessionChange )
|
|
{
|
|
pMinecraft->progressRenderer->progressStartNoAbort( IDS_PROGRESS_CONVERTING_TO_OFFLINE_GAME );
|
|
pMinecraft->progressRenderer->progressStage( -1 );
|
|
}
|
|
else
|
|
{
|
|
pMinecraft->progressRenderer->progressStartNoAbort( g_NetworkManager.CorrectErrorIDS(IDS_CONNECTION_LOST_LIVE_NO_EXIT) );
|
|
pMinecraft->progressRenderer->progressStage( IDS_PROGRESS_CONVERTING_TO_OFFLINE_GAME );
|
|
}
|
|
#else
|
|
pMinecraft->progressRenderer->progressStartNoAbort( g_NetworkManager.CorrectErrorIDS(IDS_CONNECTION_LOST_LIVE_NO_EXIT) );
|
|
pMinecraft->progressRenderer->progressStage( IDS_PROGRESS_CONVERTING_TO_OFFLINE_GAME );
|
|
#endif
|
|
|
|
pMinecraft->progressRenderer->progressStagePercentage(25);
|
|
|
|
#ifdef _XBOX_ONE
|
|
// wait for any players that were being added, to finish doing this. On XB1, if we don't do this then there's an async thread running doing this,
|
|
// which could then finish at any inappropriate time later
|
|
while( s_pPlatformNetworkManager->IsAddingPlayer() )
|
|
{
|
|
Sleep(1);
|
|
}
|
|
#endif
|
|
|
|
// Null the network player of all the server players that are local, to stop them being removed from the server when removed from the session
|
|
if( pServer != nullptr )
|
|
{
|
|
PlayerList *players = pServer->getPlayers();
|
|
for(auto& servPlayer : players->players)
|
|
{
|
|
if( servPlayer->connection->isLocal() && !servPlayer->connection->isGuest() )
|
|
{
|
|
servPlayer->connection->connection->getSocket()->setPlayer(nullptr);
|
|
}
|
|
}
|
|
}
|
|
|
|
// delete the current session - if we weren't actually disconnected fully from the network but have just lost our room, then pass a bLeaveRoom flag of false
|
|
// here as by definition we don't need to leave the room (again). This is currently only an issue for sony platforms.
|
|
if( g_NetworkManager.m_bLastDisconnectWasLostRoomOnly )
|
|
{
|
|
s_pPlatformNetworkManager->_LeaveGame(false, false);
|
|
}
|
|
else
|
|
{
|
|
s_pPlatformNetworkManager->_LeaveGame(false, true);
|
|
}
|
|
|
|
// wait for the current session to end
|
|
while( g_NetworkManager.IsInSession() )
|
|
{
|
|
Sleep(1);
|
|
}
|
|
|
|
// Reset this flag as the we don't need to know that we only lost the room only from this point onwards, the behaviour is exactly the same
|
|
g_NetworkManager.m_bLastDisconnectWasLostRoomOnly = false;
|
|
g_NetworkManager.m_bFullSessionMessageOnNextSessionChange = false;
|
|
|
|
pMinecraft->progressRenderer->progressStagePercentage(50);
|
|
|
|
// Defaulting to making this a local game
|
|
g_NetworkManager.SetLocalGame(true);
|
|
|
|
// Create a new session with all the players that were in the old one
|
|
int localUsersMask = 0;
|
|
char numLocalPlayers = 0;
|
|
for(unsigned int index = 0; index < XUSER_MAX_COUNT; ++index)
|
|
{
|
|
if(ProfileManager.IsSignedIn(index) && pMinecraft->localplayers[index] != nullptr )
|
|
{
|
|
numLocalPlayers++;
|
|
localUsersMask |= GetLocalPlayerMask(index);
|
|
}
|
|
}
|
|
|
|
s_pPlatformNetworkManager->_HostGame( localUsersMask );
|
|
|
|
pMinecraft->progressRenderer->progressStagePercentage(75);
|
|
|
|
// Wait for all the local players to rejoin the session
|
|
while( g_NetworkManager.GetPlayerCount() < numLocalPlayers )
|
|
{
|
|
Sleep(1);
|
|
}
|
|
|
|
// Restore the network player of all the server players that are local
|
|
if( pServer != nullptr )
|
|
{
|
|
for(unsigned int index = 0; index < XUSER_MAX_COUNT; ++index)
|
|
{
|
|
if(ProfileManager.IsSignedIn(index) && pMinecraft->localplayers[index] != nullptr )
|
|
{
|
|
PlayerUID localPlayerXuid = pMinecraft->localplayers[index]->getXuid();
|
|
|
|
PlayerList *players = pServer->getPlayers();
|
|
for(auto& servPlayer : players->players)
|
|
{
|
|
if( servPlayer->getXuid() == localPlayerXuid )
|
|
{
|
|
servPlayer->connection->connection->getSocket()->setPlayer( g_NetworkManager.GetLocalPlayerByUserIndex(index) );
|
|
}
|
|
}
|
|
|
|
// Player might have a pending connection
|
|
if (pMinecraft->m_pendingLocalConnections[index] != nullptr)
|
|
{
|
|
// Update the network player
|
|
pMinecraft->m_pendingLocalConnections[index]->getConnection()->getSocket()->setPlayer(g_NetworkManager.GetLocalPlayerByUserIndex(index));
|
|
}
|
|
else if ( pMinecraft->m_connectionFailed[index] && (pMinecraft->m_connectionFailedReason[index] == DisconnectPacket::eDisconnect_ConnectionCreationFailed) )
|
|
{
|
|
pMinecraft->removeLocalPlayerIdx(index);
|
|
#ifdef _XBOX_ONE
|
|
ProfileManager.RemoveGamepadFromGame(index);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pMinecraft->progressRenderer->progressStagePercentage(100);
|
|
|
|
#ifndef _XBOX
|
|
// Make sure that we have transitioned through any joining/creating stages so we're actually ready to set to play
|
|
while(!s_pPlatformNetworkManager->IsReadyToPlayOrIdle())
|
|
{
|
|
Sleep(10);
|
|
}
|
|
#endif
|
|
|
|
s_pPlatformNetworkManager->_StartGame();
|
|
|
|
#ifndef _XBOX
|
|
// Wait until the message box has been closed
|
|
while(ui.IsSceneInStack(XUSER_INDEX_ANY, eUIScene_MessageBox))
|
|
{
|
|
Sleep(10);
|
|
}
|
|
#endif
|
|
|
|
// Start the game again
|
|
app.SetGameStarted(true);
|
|
app.SetXuiServerAction(ProfileManager.GetPrimaryPad(),eXuiServerAction_PauseServer,(void *)FALSE);
|
|
app.SetChangingSessionType(false);
|
|
app.SetReallyChangingSessionType(false);
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
void CGameNetworkManager::SystemFlagSet(INetworkPlayer *pNetworkPlayer, int index)
|
|
{
|
|
s_pPlatformNetworkManager->SystemFlagSet( pNetworkPlayer, index );
|
|
}
|
|
|
|
bool CGameNetworkManager::SystemFlagGet(INetworkPlayer *pNetworkPlayer, int index)
|
|
{
|
|
return s_pPlatformNetworkManager->SystemFlagGet( pNetworkPlayer, index );
|
|
}
|
|
|
|
wstring CGameNetworkManager::GatherStats()
|
|
{
|
|
return s_pPlatformNetworkManager->GatherStats();
|
|
}
|
|
|
|
void CGameNetworkManager::renderQueueMeter()
|
|
{
|
|
#ifdef _XBOX
|
|
int height = 720;
|
|
|
|
CGameNetworkManager::byteQueue[(CGameNetworkManager::messageQueuePos) & (CGameNetworkManager::messageQueue_length - 1)] = GetHostPlayer()->GetSendQueueSizeBytes(nullptr, false);
|
|
CGameNetworkManager::messageQueue[(CGameNetworkManager::messageQueuePos++) & (CGameNetworkManager::messageQueue_length - 1)] = GetHostPlayer()->GetSendQueueSizeMessages(nullptr, false);
|
|
|
|
Minecraft *pMinecraft = Minecraft::GetInstance();
|
|
pMinecraft->gui->renderGraph(CGameNetworkManager::messageQueue_length, CGameNetworkManager::messageQueuePos, CGameNetworkManager::messageQueue, 10, 1000, CGameNetworkManager::byteQueue, 100, 25000);
|
|
#endif
|
|
}
|
|
|
|
wstring CGameNetworkManager::GatherRTTStats()
|
|
{
|
|
return s_pPlatformNetworkManager->GatherRTTStats();
|
|
}
|
|
|
|
void CGameNetworkManager::StateChange_AnyToHosting()
|
|
{
|
|
app.DebugPrintf("Disabling Guest Signin\n");
|
|
XEnableGuestSignin(FALSE);
|
|
Minecraft::GetInstance()->clearPendingClientTextureRequests();
|
|
}
|
|
|
|
void CGameNetworkManager::StateChange_AnyToJoining()
|
|
{
|
|
app.DebugPrintf("Disabling Guest Signin\n");
|
|
XEnableGuestSignin(FALSE);
|
|
Minecraft::GetInstance()->clearPendingClientTextureRequests();
|
|
|
|
ConnectionProgressParams *param = new ConnectionProgressParams();
|
|
param->iPad = ProfileManager.GetPrimaryPad();
|
|
param->stringId = -1;
|
|
param->showTooltips = false;
|
|
param->setFailTimer = true;
|
|
param->timerTime = CONNECTING_PROGRESS_CHECK_TIME;
|
|
|
|
ui.NavigateToScene(ProfileManager.GetPrimaryPad(), eUIScene_ConnectingProgress, param);
|
|
}
|
|
|
|
void CGameNetworkManager::StateChange_JoiningToIdle(CPlatformNetworkManager::eJoinFailedReason reason)
|
|
{
|
|
DisconnectPacket::eDisconnectReason disconnectReason;
|
|
switch(reason)
|
|
{
|
|
case CPlatformNetworkManager::JOIN_FAILED_SERVER_FULL:
|
|
disconnectReason = DisconnectPacket::eDisconnect_ServerFull;
|
|
break;
|
|
case CPlatformNetworkManager::JOIN_FAILED_INSUFFICIENT_PRIVILEGES:
|
|
disconnectReason = DisconnectPacket::eDisconnect_NoMultiplayerPrivilegesJoin;
|
|
app.SetAction(ProfileManager.GetPrimaryPad(),eAppAction_FailedToJoinNoPrivileges);
|
|
break;
|
|
default:
|
|
disconnectReason = DisconnectPacket::eDisconnect_ConnectionCreationFailed;
|
|
break;
|
|
};
|
|
Minecraft::GetInstance()->connectionDisconnected(ProfileManager.GetPrimaryPad(), disconnectReason);
|
|
}
|
|
|
|
void CGameNetworkManager::StateChange_AnyToStarting()
|
|
{
|
|
#if defined __PS3__ || defined __ORBIS__ || defined __PSVITA__
|
|
app.getRemoteStorage()->shutdown(); // shut the remote storage lib down and hopefully get our 7mb back
|
|
#endif
|
|
|
|
if(!g_NetworkManager.IsHost())
|
|
{
|
|
LoadingInputParams *loadingParams = new LoadingInputParams();
|
|
loadingParams->func = &CGameNetworkManager::RunNetworkGameThreadProc;
|
|
loadingParams->lpParam = nullptr;
|
|
|
|
UIFullscreenProgressCompletionData *completionData = new UIFullscreenProgressCompletionData();
|
|
completionData->bShowBackground=TRUE;
|
|
completionData->bShowLogo=TRUE;
|
|
completionData->type = e_ProgressCompletion_CloseAllPlayersUIScenes;
|
|
completionData->iPad = ProfileManager.GetPrimaryPad();
|
|
loadingParams->completionData = completionData;
|
|
|
|
ui.NavigateToScene(ProfileManager.GetPrimaryPad(),eUIScene_FullscreenProgress, loadingParams);
|
|
}
|
|
}
|
|
|
|
void CGameNetworkManager::StateChange_AnyToEnding(bool bStateWasPlaying)
|
|
{
|
|
// Kick off a stats write for players that are signed into LIVE, if this is a local game
|
|
if( bStateWasPlaying && g_NetworkManager.IsLocalGame() )
|
|
{
|
|
for(unsigned int i = 0; i < XUSER_MAX_COUNT; ++i)
|
|
{
|
|
INetworkPlayer *pNetworkPlayer = g_NetworkManager.GetLocalPlayerByUserIndex(i);
|
|
if(pNetworkPlayer != nullptr && ProfileManager.IsSignedIn( i ) )
|
|
{
|
|
app.DebugPrintf("Stats save for an offline game for the player at index %d\n", i );
|
|
Minecraft::GetInstance()->forceStatsSave(pNetworkPlayer->GetUserIndex());
|
|
}
|
|
}
|
|
}
|
|
|
|
Minecraft::GetInstance()->gui->clearMessages();
|
|
|
|
if(!g_NetworkManager.IsHost() && !g_NetworkManager.IsLeavingGame() )
|
|
{
|
|
// 4J Stu - If the host is saving then it might take a while to quite the session, so do it ourself
|
|
//m_bLeavingGame = true;
|
|
|
|
// The host has notified that the game is about to end
|
|
if(app.GetDisconnectReason() == DisconnectPacket::eDisconnect_None) app.SetDisconnectReason( DisconnectPacket::eDisconnect_Quitting );
|
|
app.SetAction(ProfileManager.GetPrimaryPad(),eAppAction_ExitWorld,(void *)TRUE);
|
|
}
|
|
}
|
|
|
|
void CGameNetworkManager::StateChange_AnyToIdle()
|
|
{
|
|
app.DebugPrintf("Enabling Guest Signin\n");
|
|
XEnableGuestSignin(TRUE);
|
|
// Reset this here so that we can search for games again
|
|
// 4J Stu - If we are changing session type there is a race between that thread setting the game to local, and this setting it to not local
|
|
if(!app.GetChangingSessionType()) g_NetworkManager.SetLocalGame( false );
|
|
|
|
}
|
|
|
|
void CGameNetworkManager::CreateSocket( INetworkPlayer *pNetworkPlayer, bool localPlayer )
|
|
{
|
|
Minecraft *pMinecraft = Minecraft::GetInstance();
|
|
|
|
Socket *socket = nullptr;
|
|
shared_ptr<MultiplayerLocalPlayer> mpPlayer = nullptr;
|
|
int userIdx = pNetworkPlayer->GetUserIndex();
|
|
if (userIdx >= 0 && userIdx < XUSER_MAX_COUNT)
|
|
mpPlayer = pMinecraft->localplayers[userIdx];
|
|
if( localPlayer && mpPlayer != nullptr && mpPlayer->connection != nullptr)
|
|
{
|
|
// If we already have a MultiplayerLocalPlayer here then we are doing a session type change
|
|
socket = mpPlayer->connection->getSocket();
|
|
|
|
// Pair this socket and network player
|
|
pNetworkPlayer->SetSocket( socket);
|
|
if( socket )
|
|
{
|
|
socket->setPlayer( pNetworkPlayer );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#ifdef _WINDOWS64
|
|
// Non-host split-screen: open a dedicated TCP connection for this pad
|
|
if (localPlayer && !g_NetworkManager.IsHost() && g_NetworkManager.IsInGameplay())
|
|
{
|
|
int padIdx = pNetworkPlayer->GetUserIndex();
|
|
BYTE assignedSmallId = 0;
|
|
|
|
if (!WinsockNetLayer::JoinSplitScreen(padIdx, &assignedSmallId))
|
|
{
|
|
app.DebugPrintf("Split-screen pad %d: failed to open TCP to host\n", padIdx);
|
|
pMinecraft->connectionDisconnected(padIdx, DisconnectPacket::eDisconnect_ConnectionCreationFailed);
|
|
return;
|
|
}
|
|
|
|
// Update the local IQNetPlayer (at pad index) with the host-assigned smallId.
|
|
// The NetworkPlayerXbox created by NotifyPlayerJoined already points to
|
|
// m_player[padIdx], so we just set the smallId for network routing.
|
|
IQNet::m_player[padIdx].m_smallId = assignedSmallId;
|
|
IQNet::m_player[padIdx].m_resolvedXuid = Win64Xuid::DeriveXuidForPad(Win64Xuid::ResolvePersistentXuid(), padIdx);
|
|
|
|
// Network socket (not hostLocal) — data goes through TCP via GetLocalSocket
|
|
socket = new Socket(pNetworkPlayer, false, false);
|
|
pNetworkPlayer->SetSocket(socket);
|
|
|
|
ClientConnection* connection = new ClientConnection(pMinecraft, socket, padIdx);
|
|
if (connection->createdOk)
|
|
{
|
|
connection->send(shared_ptr<PreLoginPacket>(new PreLoginPacket(pNetworkPlayer->GetOnlineName())));
|
|
pMinecraft->addPendingLocalConnection(padIdx, connection);
|
|
}
|
|
else
|
|
{
|
|
pMinecraft->connectionDisconnected(padIdx, DisconnectPacket::eDisconnect_ConnectionCreationFailed);
|
|
delete connection;
|
|
}
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
socket = new Socket( pNetworkPlayer, g_NetworkManager.IsHost(), g_NetworkManager.IsHost() && localPlayer );
|
|
pNetworkPlayer->SetSocket( socket );
|
|
|
|
// 4J Stu - May be other states we want to accept aswell
|
|
// Add this user to the game server if the game is started already
|
|
if( g_NetworkManager.IsHost() && g_NetworkManager.IsInGameplay() )
|
|
{
|
|
Socket::addIncomingSocket(socket);
|
|
}
|
|
|
|
// If this is a local player and we are already in the game, we need to setup a local connection and log
|
|
// the player in to the game server
|
|
if( localPlayer && g_NetworkManager.IsInGameplay() )
|
|
{
|
|
int idx = pNetworkPlayer->GetUserIndex();
|
|
app.DebugPrintf("Creating new client connection for idx: %d\n", idx);
|
|
|
|
ClientConnection *connection;
|
|
connection = new ClientConnection(pMinecraft, socket, idx);
|
|
|
|
if( connection->createdOk )
|
|
{
|
|
connection->send(std::make_shared<PreLoginPacket>(pNetworkPlayer->GetOnlineName()));
|
|
pMinecraft->addPendingLocalConnection(idx, connection);
|
|
}
|
|
else
|
|
{
|
|
pMinecraft->connectionDisconnected( idx , DisconnectPacket::eDisconnect_ConnectionCreationFailed );
|
|
delete connection;
|
|
connection = nullptr;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void CGameNetworkManager::CloseConnection( INetworkPlayer *pNetworkPlayer )
|
|
{
|
|
MinecraftServer *server = MinecraftServer::getInstance();
|
|
if( server != nullptr )
|
|
{
|
|
PlayerList *players = server->getPlayers();
|
|
if( players != nullptr )
|
|
{
|
|
players->closePlayerConnectionBySmallId(pNetworkPlayer->GetSmallId());
|
|
}
|
|
}
|
|
}
|
|
|
|
void CGameNetworkManager::PlayerJoining( INetworkPlayer *pNetworkPlayer )
|
|
{
|
|
if (g_NetworkManager.IsInGameplay()) // 4J-JEV: Wait to do this at StartNetworkGame if not in-game yet.
|
|
{
|
|
// 4J-JEV: Update RichPresence when a player joins the game.
|
|
bool multiplayer = g_NetworkManager.GetPlayerCount() > 1, localgame = g_NetworkManager.IsLocalGame();
|
|
for (int iPad=0; iPad<XUSER_MAX_COUNT; ++iPad)
|
|
{
|
|
INetworkPlayer *pNetworkPlayer = g_NetworkManager.GetLocalPlayerByUserIndex(iPad);
|
|
if (pNetworkPlayer == nullptr) continue;
|
|
|
|
app.SetRichPresenceContext(iPad,CONTEXT_GAME_STATE_BLANK);
|
|
if (multiplayer)
|
|
{
|
|
if (localgame) ProfileManager.SetCurrentGameActivity(iPad, CONTEXT_PRESENCE_MULTIPLAYEROFFLINE, false);
|
|
else ProfileManager.SetCurrentGameActivity(iPad, CONTEXT_PRESENCE_MULTIPLAYER, false);
|
|
}
|
|
else
|
|
{
|
|
if (localgame) ProfileManager.SetCurrentGameActivity(iPad, CONTEXT_PRESENCE_MULTIPLAYER_1POFFLINE, false);
|
|
else ProfileManager.SetCurrentGameActivity(iPad, CONTEXT_PRESENCE_MULTIPLAYER_1P, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
if( pNetworkPlayer->IsLocal() )
|
|
{
|
|
TelemetryManager->RecordPlayerSessionStart(pNetworkPlayer->GetUserIndex());
|
|
}
|
|
#ifdef _XBOX
|
|
else
|
|
{
|
|
if( !pNetworkPlayer->IsHost() )
|
|
{
|
|
for(int idx = 0; idx < XUSER_MAX_COUNT; ++idx)
|
|
{
|
|
if(Minecraft::GetInstance()->localplayers[idx] != nullptr)
|
|
{
|
|
TelemetryManager->RecordLevelStart(idx, eSen_FriendOrMatch_Playing_With_Invited_Friends, eSen_CompeteOrCoop_Coop_and_Competitive, Minecraft::GetInstance()->level->difficulty, app.GetLocalPlayerCount(), g_NetworkManager.GetOnlinePlayerCount());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void CGameNetworkManager::PlayerLeaving( INetworkPlayer *pNetworkPlayer )
|
|
{
|
|
if( pNetworkPlayer->IsLocal() )
|
|
{
|
|
ProfileManager.SetCurrentGameActivity(pNetworkPlayer->GetUserIndex(),CONTEXT_PRESENCE_IDLE,false);
|
|
|
|
TelemetryManager->RecordPlayerSessionExit(pNetworkPlayer->GetUserIndex(), app.GetDisconnectReason());
|
|
}
|
|
#ifdef _XBOX
|
|
else
|
|
{
|
|
for(int idx = 0; idx < XUSER_MAX_COUNT; ++idx)
|
|
{
|
|
if(Minecraft::GetInstance()->localplayers[idx] != nullptr)
|
|
{
|
|
TelemetryManager->RecordLevelStart(idx, eSen_FriendOrMatch_Playing_With_Invited_Friends, eSen_CompeteOrCoop_Coop_and_Competitive, Minecraft::GetInstance()->level->difficulty, app.GetLocalPlayerCount(), g_NetworkManager.GetOnlinePlayerCount());
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void CGameNetworkManager::HostChanged()
|
|
{
|
|
// Disable host migration
|
|
app.SetAction(ProfileManager.GetPrimaryPad(),eAppAction_ExitWorld,(void *)TRUE);
|
|
}
|
|
|
|
void CGameNetworkManager::WriteStats( INetworkPlayer *pNetworkPlayer )
|
|
{
|
|
Minecraft::GetInstance()->forceStatsSave( pNetworkPlayer->GetUserIndex() );
|
|
}
|
|
|
|
void CGameNetworkManager::GameInviteReceived( int userIndex, const INVITE_INFO *pInviteInfo)
|
|
{
|
|
#ifdef __ORBIS__
|
|
if (m_pUpsell != nullptr)
|
|
{
|
|
delete pInviteInfo;
|
|
return;
|
|
}
|
|
|
|
// Need to check we're signed in to PSN
|
|
bool isSignedInLive = true;
|
|
bool isLocalMultiplayerAvailable = app.IsLocalMultiplayerAvailable();
|
|
int iPadNotSignedInLive = -1;
|
|
for(unsigned int i = 0; i < XUSER_MAX_COUNT; i++)
|
|
{
|
|
if (ProfileManager.IsSignedIn(i) && (i == ProfileManager.GetPrimaryPad() || isLocalMultiplayerAvailable))
|
|
{
|
|
if (isSignedInLive && !ProfileManager.IsSignedInLive(i))
|
|
{
|
|
// Record the first non signed in live pad
|
|
iPadNotSignedInLive = i;
|
|
}
|
|
|
|
isSignedInLive = isSignedInLive && ProfileManager.IsSignedInLive(i);
|
|
}
|
|
}
|
|
|
|
if (!isSignedInLive)
|
|
{
|
|
// Determine why they're not "signed in live"
|
|
|
|
// Check if PSN is unavailable because of age restriction
|
|
int npAvailability = ProfileManager.getNPAvailability(iPadNotSignedInLive);
|
|
if (npAvailability == SCE_NP_ERROR_AGE_RESTRICTION)
|
|
{
|
|
// 4J Stu - This is a bit messy and is due to the library incorrectly returning false for IsSignedInLive if the npAvailability isn't SCE_OK
|
|
UINT uiIDA[1];
|
|
uiIDA[0]=IDS_OK;
|
|
ui.RequestErrorMessage(IDS_ONLINE_SERVICE_TITLE, IDS_CONTENT_RESTRICTION, uiIDA, 1, iPadNotSignedInLive);
|
|
}
|
|
else if (ProfileManager.isSignedInPSN(iPadNotSignedInLive))
|
|
{
|
|
// Signed in to PSN but not connected (no internet access)
|
|
assert(!ProfileManager.isConnectedToPSN(iPadNotSignedInLive));
|
|
|
|
UINT uiIDA[1];
|
|
uiIDA[0] = IDS_OK;
|
|
ui.RequestErrorMessage( IDS_ERROR_NETWORK_TITLE, IDS_ERROR_NETWORK, uiIDA, 1, iPadNotSignedInLive);
|
|
}
|
|
else
|
|
{
|
|
// Not signed in to PSN
|
|
UINT uiIDA[1];
|
|
uiIDA[0] = IDS_PRO_NOTONLINE_ACCEPT;
|
|
ui.RequestAlertMessage( IDS_PRO_NOTONLINE_TITLE, IDS_PRO_NOTONLINE_TEXT, uiIDA, 1, iPadNotSignedInLive, &CGameNetworkManager::MustSignInReturned_1, (void *)pInviteInfo);
|
|
}
|
|
return;
|
|
}
|
|
|
|
// if this is the trial game, we'll check and send the user to unlock the game later, in HandleInviteWhenInMenus
|
|
if(ProfileManager.IsFullVersion())
|
|
{
|
|
// 4J-JEV: Check that all players are authorised for PsPlus, present upsell to players that aren't and try again.
|
|
for (unsigned int index = 0; index < XUSER_MAX_COUNT; index++)
|
|
{
|
|
if ( ProfileManager.IsSignedIn(index)
|
|
&& !ProfileManager.HasPlayStationPlus(userIndex) )
|
|
{
|
|
m_pInviteInfo = (INVITE_INFO *) pInviteInfo;
|
|
m_iPlayerInvited = userIndex;
|
|
|
|
m_pUpsell = new PsPlusUpsellWrapper(index);
|
|
m_pUpsell->displayUpsell();
|
|
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
int localUsersMask = 0;
|
|
Minecraft *pMinecraft = Minecraft::GetInstance();
|
|
int joiningUsers = 0;
|
|
|
|
bool noPrivileges = false;
|
|
for(unsigned int index = 0; index < XUSER_MAX_COUNT; ++index)
|
|
{
|
|
if(ProfileManager.IsSignedIn(index) )
|
|
{
|
|
// 4J-PB we shouldn't bring any inactive players into the game, except for the invited player (who may be an inactive player)
|
|
// 4J Stu - If we are not in a game, then bring in all players signed in
|
|
if(index==userIndex || pMinecraft->localplayers[index]!=nullptr )
|
|
{
|
|
++joiningUsers;
|
|
if( !ProfileManager.AllowedToPlayMultiplayer(index) ) noPrivileges = true;
|
|
localUsersMask |= GetLocalPlayerMask( index );
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check if user-created content is allowed, as we cannot play multiplayer if it's not
|
|
bool noUGC = false;
|
|
bool bContentRestricted=false;
|
|
BOOL pccAllowed = TRUE;
|
|
BOOL pccFriendsAllowed = TRUE;
|
|
#if defined(__PS3__) || defined(__PSVITA__)
|
|
ProfileManager.GetChatAndContentRestrictions(userIndex,false,&noUGC,&bContentRestricted,nullptr);
|
|
#else
|
|
ProfileManager.AllowedPlayerCreatedContent(ProfileManager.GetPrimaryPad(),false,&pccAllowed,&pccFriendsAllowed);
|
|
if(!pccAllowed && !pccFriendsAllowed) noUGC = true;
|
|
#endif
|
|
|
|
#if defined(_XBOX) || defined(__PS3__)
|
|
if(joiningUsers > 1 && !RenderManager.IsHiDef() && userIndex != ProfileManager.GetPrimaryPad())
|
|
{
|
|
UINT uiIDA[1];
|
|
uiIDA[0]=IDS_CONFIRM_OK;
|
|
|
|
// 4J-PB - it's possible there is no primary pad here, when accepting an invite from the dashboard
|
|
ui.RequestErrorMessage( IDS_CONNECTION_FAILED, IDS_CONNECTION_FAILED_NO_SD_SPLITSCREEN, uiIDA,1,XUSER_INDEX_ANY);
|
|
}
|
|
else
|
|
#endif
|
|
|
|
if( noUGC )
|
|
{
|
|
#ifdef __PSVITA__
|
|
// showing the system message for chat restriction here instead now, to fix FQA bug report
|
|
ProfileManager.DisplaySystemMessage( SCE_MSG_DIALOG_SYSMSG_TYPE_TRC_PSN_CHAT_RESTRICTION, ProfileManager.GetPrimaryPad() );
|
|
#else
|
|
int messageText = IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_SINGLE_LOCAL;
|
|
if(joiningUsers > 1) messageText = IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_ALL_LOCAL;
|
|
|
|
ui.RequestUGCMessageBox(IDS_CONNECTION_FAILED, messageText, XUSER_INDEX_ANY);
|
|
#endif
|
|
}
|
|
#if defined(__PS3__) || defined __PSVITA__
|
|
else if(bContentRestricted)
|
|
{
|
|
int messageText = IDS_CONTENT_RESTRICTION;
|
|
if(joiningUsers > 1) messageText = IDS_CONTENT_RESTRICTION_MULTIPLAYER;
|
|
|
|
ui.RequestContentRestrictedMessageBox(IDS_CONNECTION_FAILED, messageText, XUSER_INDEX_ANY);
|
|
}
|
|
#endif
|
|
else if(noPrivileges)
|
|
{
|
|
UINT uiIDA[1];
|
|
uiIDA[0]=IDS_CONFIRM_OK;
|
|
|
|
// 4J-PB - it's possible there is no primary pad here, when accepting an invite from the dashboard
|
|
//StorageManager.RequestMessageBox( IDS_NO_MULTIPLAYER_PRIVILEGE_TITLE, IDS_NO_MULTIPLAYER_PRIVILEGE_JOIN_TEXT, uiIDA,1,ProfileManager.GetPrimaryPad(),nullptr,nullptr, app.GetStringTable());
|
|
ui.RequestErrorMessage( IDS_NO_MULTIPLAYER_PRIVILEGE_TITLE, IDS_NO_MULTIPLAYER_PRIVILEGE_JOIN_TEXT, uiIDA,1,XUSER_INDEX_ANY);
|
|
}
|
|
else
|
|
{
|
|
#if defined(__ORBIS__) || defined(__PSVITA__)
|
|
bool chatRestricted = false;
|
|
ProfileManager.GetChatAndContentRestrictions(ProfileManager.GetPrimaryPad(),false,&chatRestricted,nullptr,nullptr);
|
|
if(chatRestricted)
|
|
{
|
|
ProfileManager.DisplaySystemMessage( SCE_MSG_DIALOG_SYSMSG_TYPE_TRC_PSN_CHAT_RESTRICTION, ProfileManager.GetPrimaryPad() );
|
|
}
|
|
#endif
|
|
if( !g_NetworkManager.IsInSession() )
|
|
{
|
|
#if defined (__PS3__) || defined (__PSVITA__)
|
|
// PS3 is more complicated here - we need to make sure that the player is online. If they are then we can do the same as the xbox, if not we need to try and get them online and then, if they do sign in, go down the same path
|
|
|
|
// Determine why they're not "signed in live"
|
|
// MGH - On Vita we need to add a new message at some point for connecting when already signed in
|
|
if(ProfileManager.IsSignedInLive(ProfileManager.GetPrimaryPad()))
|
|
{
|
|
HandleInviteWhenInMenus(userIndex, pInviteInfo);
|
|
}
|
|
else
|
|
{
|
|
UINT uiIDA[2];
|
|
uiIDA[0]=IDS_PRO_NOTONLINE_ACCEPT;
|
|
uiIDA[1]=IDS_PRO_NOTONLINE_DECLINE;
|
|
ui.RequestErrorMessage(IDS_PRO_NOTONLINE_TITLE, IDS_PRO_NOTONLINE_TEXT, uiIDA, 2, ProfileManager.GetPrimaryPad(),&CGameNetworkManager::MustSignInReturned_1,(void *)pInviteInfo);
|
|
}
|
|
|
|
|
|
#else
|
|
HandleInviteWhenInMenus(userIndex, pInviteInfo);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
app.DebugPrintf("We are already in a multiplayer game...need to leave it\n");
|
|
|
|
// JoinFromInviteData *joinData = new JoinFromInviteData();
|
|
// joinData->dwUserIndex = dwUserIndex;
|
|
// joinData->dwLocalUsersMask = dwLocalUsersMask;
|
|
// joinData->pInviteInfo = pInviteInfo;
|
|
|
|
// tell the app to process this
|
|
#ifdef __PSVITA__
|
|
if(((CPlatformNetworkManagerSony*)s_pPlatformNetworkManager)->checkValidInviteData(pInviteInfo))
|
|
#endif
|
|
{
|
|
app.ProcessInvite(userIndex,localUsersMask,pInviteInfo);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
volatile bool waitHere = true;
|
|
|
|
void CGameNetworkManager::HandleInviteWhenInMenus( int userIndex, const INVITE_INFO *pInviteInfo)
|
|
{
|
|
// We are in the root menus somewhere
|
|
|
|
#if 0
|
|
while( waitHere )
|
|
{
|
|
Sleep(1);
|
|
}
|
|
#endif
|
|
|
|
// if this is the trial game, then we need the user to unlock the full game
|
|
if(!ProfileManager.IsFullVersion())
|
|
{
|
|
// The marketplace will fail with the primary player set to -1
|
|
ProfileManager.SetPrimaryPad(userIndex);
|
|
|
|
app.SetAction(userIndex,eAppAction_DashboardTrialJoinFromInvite);
|
|
}
|
|
else
|
|
{
|
|
#ifndef _XBOX_ONE
|
|
ProfileManager.SetPrimaryPad(userIndex);
|
|
#endif
|
|
|
|
// 4J Stu - If we accept an invite from the main menu before going to play game we need to load the DLC
|
|
// These checks are done within the StartInstallDLCProcess - (!app.DLCInstallProcessCompleted() && !app.DLCInstallPending()) app.StartInstallDLCProcess(dwUserIndex);
|
|
app.StartInstallDLCProcess(userIndex);
|
|
|
|
// 4J Stu - Fix for #10936 - MP Lab: TCR 001: Matchmaking: Player is stuck in a soft-locked state after selecting the guest account when prompted
|
|
// The locked profile should not be changed if we are in menus as the main player might sign out in the sign-in ui
|
|
//ProfileManager.SetLockedProfile(-1);
|
|
|
|
#ifdef _XBOX_ONE
|
|
if((!app.IsLocalMultiplayerAvailable())&&InputManager.IsPadLocked(userIndex))
|
|
#else
|
|
if(!app.IsLocalMultiplayerAvailable())
|
|
#endif
|
|
{
|
|
bool noPrivileges=!ProfileManager.AllowedToPlayMultiplayer(userIndex);
|
|
|
|
if(noPrivileges)
|
|
{
|
|
UINT uiIDA[1];
|
|
uiIDA[0]=IDS_CONFIRM_OK;
|
|
ui.RequestErrorMessage( IDS_NO_MULTIPLAYER_PRIVILEGE_TITLE, IDS_NO_MULTIPLAYER_PRIVILEGE_JOIN_TEXT, uiIDA,1,ProfileManager.GetPrimaryPad());
|
|
}
|
|
else
|
|
{
|
|
ProfileManager.SetLockedProfile(userIndex);
|
|
ProfileManager.SetPrimaryPad(userIndex);
|
|
|
|
int localUsersMask=0;
|
|
localUsersMask |= GetLocalPlayerMask( userIndex );
|
|
|
|
// If the player was signed in before selecting play, we'll not have read the profile yet, so query the sign-in status to get this to happen
|
|
ProfileManager.QuerySigninStatus();
|
|
|
|
// 4J-PB - clear any previous connection errors
|
|
Minecraft::GetInstance()->clearConnectionFailed();
|
|
|
|
g_NetworkManager.SetLocalGame(false);
|
|
|
|
// change the minecraft player name
|
|
Minecraft::GetInstance()->user->name = convStringToWstring( ProfileManager.GetGamertag(ProfileManager.GetPrimaryPad()));
|
|
|
|
bool success = g_NetworkManager.JoinGameFromInviteInfo( userIndex, localUsersMask, pInviteInfo );
|
|
if( !success )
|
|
{
|
|
app.DebugPrintf( "Failed joining game from invite\n" );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// the FromInvite will make the lib decide how many panes to display based on connected pads/signed in players
|
|
#ifdef _XBOX
|
|
ProfileManager.RequestSignInUI(true, false, false, false, false,&CGameNetworkManager::JoinFromInvite_SignInReturned, (LPVOID)pInviteInfo,userIndex);
|
|
#else
|
|
SignInInfo info;
|
|
info.Func = &CGameNetworkManager::JoinFromInvite_SignInReturned;
|
|
info.lpParam = (LPVOID)pInviteInfo;
|
|
info.requireOnline = true;
|
|
app.DebugPrintf("Using fullscreen layer\n");
|
|
ui.NavigateToScene(ProfileManager.GetPrimaryPad(),eUIScene_QuadrantSignin,&info,eUILayer_Alert,eUIGroup_Fullscreen);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
void CGameNetworkManager::AddLocalPlayerFailed(int idx, bool serverFull/* = false*/)
|
|
{
|
|
Minecraft::GetInstance()->connectionDisconnected(idx, serverFull ? DisconnectPacket::eDisconnect_ServerFull : DisconnectPacket::eDisconnect_ConnectionCreationFailed);
|
|
}
|
|
|
|
#if defined __PS3__ || defined __PSVITA__ || defined __ORBIS__
|
|
void CGameNetworkManager::HandleDisconnect(bool bLostRoomOnly,bool bPSNSignout)
|
|
#else
|
|
void CGameNetworkManager::HandleDisconnect(bool bLostRoomOnly)
|
|
#endif
|
|
{
|
|
int iPrimaryPlayer = g_NetworkManager.GetPrimaryPad();
|
|
|
|
if((g_NetworkManager.GetLockedProfile()!=-1) && iPrimaryPlayer!=-1 && g_NetworkManager.IsInSession() )
|
|
{
|
|
m_bLastDisconnectWasLostRoomOnly = bLostRoomOnly;
|
|
#if defined __PS3__ || defined __PSVITA__ || defined __ORBIS__
|
|
m_bSignedOutofPSN=bPSNSignout;
|
|
#endif
|
|
app.SetAction(iPrimaryPlayer,eAppAction_EthernetDisconnected);
|
|
}
|
|
else
|
|
{
|
|
m_bLastDisconnectWasLostRoomOnly = false;
|
|
}
|
|
}
|
|
|
|
int CGameNetworkManager::GetPrimaryPad()
|
|
{
|
|
return ProfileManager.GetPrimaryPad();
|
|
}
|
|
|
|
int CGameNetworkManager::GetLockedProfile()
|
|
{
|
|
return ProfileManager.GetLockedProfile();
|
|
}
|
|
|
|
bool CGameNetworkManager::IsSignedInLive(int playerIdx)
|
|
{
|
|
return ProfileManager.IsSignedInLive(playerIdx);
|
|
}
|
|
|
|
bool CGameNetworkManager::AllowedToPlayMultiplayer(int playerIdx)
|
|
{
|
|
return ProfileManager.AllowedToPlayMultiplayer(playerIdx);
|
|
}
|
|
|
|
const char *CGameNetworkManager::GetOnlineName(int playerIdx)
|
|
{
|
|
return ProfileManager.GetGamertag(playerIdx);
|
|
}
|
|
|
|
void CGameNetworkManager::ServerReadyCreate(bool create)
|
|
{
|
|
m_hServerReadyEvent = ( create ? ( new C4JThread::Event ) : nullptr );
|
|
}
|
|
|
|
void CGameNetworkManager::ServerReady()
|
|
{
|
|
m_hServerReadyEvent->Set();
|
|
}
|
|
|
|
void CGameNetworkManager::ServerReadyWait()
|
|
{
|
|
m_hServerReadyEvent->WaitForSignal(INFINITE);
|
|
}
|
|
|
|
void CGameNetworkManager::ServerReadyDestroy()
|
|
{
|
|
delete m_hServerReadyEvent;
|
|
m_hServerReadyEvent = nullptr;
|
|
}
|
|
|
|
bool CGameNetworkManager::ServerReadyValid()
|
|
{
|
|
return ( m_hServerReadyEvent != nullptr );
|
|
}
|
|
|
|
void CGameNetworkManager::ServerStoppedCreate(bool create)
|
|
{
|
|
m_hServerStoppedEvent = ( create ? ( new C4JThread::Event ) : nullptr );
|
|
}
|
|
|
|
void CGameNetworkManager::ServerStopped()
|
|
{
|
|
m_hServerStoppedEvent->Set();
|
|
}
|
|
|
|
void CGameNetworkManager::ServerStoppedWait()
|
|
{
|
|
// If this is called from the main thread, then this won't be ticking anything which can mean that the storage manager state can't progress.
|
|
// This means that the server thread we are waiting on won't ever finish, as it might be locked waiting for this to complete itself.
|
|
// Do some ticking here then if this is the case.
|
|
if( C4JThread::isMainThread() )
|
|
{
|
|
int result = WAIT_TIMEOUT;
|
|
do
|
|
{
|
|
#ifndef _XBOX
|
|
RenderManager.StartFrame();
|
|
#endif
|
|
result = m_hServerStoppedEvent->WaitForSignal(20);
|
|
// Tick some simple things
|
|
ProfileManager.Tick();
|
|
StorageManager.Tick();
|
|
InputManager.Tick();
|
|
RenderManager.Tick();
|
|
ui.tick();
|
|
ui.render();
|
|
RenderManager.Present();
|
|
} while( result == WAIT_TIMEOUT );
|
|
}
|
|
else
|
|
{
|
|
m_hServerStoppedEvent->WaitForSignal(INFINITE);
|
|
}
|
|
}
|
|
|
|
void CGameNetworkManager::ServerStoppedDestroy()
|
|
{
|
|
delete m_hServerStoppedEvent;
|
|
m_hServerStoppedEvent = nullptr;
|
|
}
|
|
|
|
bool CGameNetworkManager::ServerStoppedValid()
|
|
{
|
|
return ( m_hServerStoppedEvent != nullptr );
|
|
}
|
|
|
|
int CGameNetworkManager::GetJoiningReadyPercentage()
|
|
{
|
|
return s_pPlatformNetworkManager->GetJoiningReadyPercentage();
|
|
}
|
|
|
|
#ifndef _XBOX
|
|
void CGameNetworkManager::FakeLocalPlayerJoined()
|
|
{
|
|
s_pPlatformNetworkManager->FakeLocalPlayerJoined();
|
|
}
|
|
#endif
|
|
|
|
#ifdef __PSVITA__
|
|
bool CGameNetworkManager::usingAdhocMode()
|
|
{
|
|
return ((CPlatformNetworkManagerSony*)s_pPlatformNetworkManager)->usingAdhocMode();
|
|
}
|
|
|
|
void CGameNetworkManager::setAdhocMode(bool bAdhoc)
|
|
{
|
|
((CPlatformNetworkManagerSony*)s_pPlatformNetworkManager)->setAdhocMode(bAdhoc);
|
|
}
|
|
|
|
void CGameNetworkManager::startAdhocMatching()
|
|
{
|
|
((CPlatformNetworkManagerSony*)s_pPlatformNetworkManager)->startAdhocMatching();
|
|
}
|
|
|
|
#endif
|