Files
DrPerkyLegit-LCEServerTest/Minecraft.Client/Minecraft.cpp
2026-05-16 11:30:28 -04:00

2629 lines
76 KiB
C++

#include "stdafx.h"
#include "Minecraft.h"
#include "GameMode.h"
#include "Timer.h"
#include "LevelRenderer.h"
#include "ParticleEngine.h"
#include "MultiPlayerLocalPlayer.h"
#include "User.h"
#include "Textures.h"
#include "GameRenderer.h"
#include "Options.h"
#include "TexturePackRepository.h"
#include "StatsCounter.h"
#include "SurvivalMode.h"
#include "Chunk.h"
#include "CreativeMode.h"
#include "DemoLevel.h"
#include "MultiPlayerLevel.h"
#include "MultiPlayerLocalPlayer.h"
#include "DemoUser.h"
#include "Input.h"
#include "FrustumCuller.h"
#include "Camera.h"
#include "..\Minecraft.World\MobEffect.h"
#include "..\Minecraft.World\Difficulty.h"
#include "..\Minecraft.World\net.minecraft.world.level.h"
#include "..\Minecraft.World\net.minecraft.world.entity.h"
#include "..\Minecraft.World\net.minecraft.world.entity.player.h"
#include "..\Minecraft.World\net.minecraft.world.entity.item.h"
#include "..\Minecraft.World\net.minecraft.world.phys.h"
#include "..\Minecraft.World\File.h"
#include "..\Minecraft.World\net.minecraft.world.level.storage.h"
#include "..\Minecraft.World\net.minecraft.h"
#include "..\Minecraft.World\net.minecraft.stats.h"
#include "..\Minecraft.World\System.h"
#include "..\Minecraft.World\ByteBuffer.h"
#include "..\Minecraft.World\net.minecraft.world.level.tile.h"
#include "..\Minecraft.World\net.minecraft.world.level.chunk.h"
#include "..\Minecraft.World\net.minecraft.world.level.dimension.h"
#include "..\Minecraft.World\net.minecraft.world.item.h"
#include "..\Minecraft.World\Minecraft.World.h"
#include "Windows64\Windows64_Xuid.h"
#include "ClientConnection.h"
#include "..\Minecraft.World\HellRandomLevelSource.h"
#include "..\Minecraft.World\net.minecraft.world.entity.animal.h"
#include "..\Minecraft.World\net.minecraft.world.entity.monster.h"
#include "..\Minecraft.World\StrongholdFeature.h"
#include "..\Minecraft.World\IntCache.h"
#include "..\Minecraft.World\Villager.h"
#include "..\Minecraft.World\SparseLightStorage.h"
#include "..\Minecraft.World\SparseDataStorage.h"
#include "..\Minecraft.World\ChestTileEntity.h"
#include "TextureManager.h"
#ifdef _XBOX
#include "Xbox\Network\NetworkPlayerXbox.h"
#endif
#include "DLCTexturePack.h"
#ifdef __ORBIS__
#include "Orbis\Network\PsPlusUpsellWrapper_Orbis.h"
#endif
#include <Windows64/Windows64_Minecraft.h>
// #define DISABLE_SPU_CODE
// 4J Turning this on will change the graph at the bottom of the debug overlay to show the number of packets of each type added per fram
//#define DEBUG_RENDER_SHOWS_PACKETS 1
//#define SPLITSCREEN_TEST
// If not disabled, this creates an event queue on a seperate thread so that the Level::tick calls can be offloaded
// from the main thread, and have longer to run, since it's called at 20Hz instead of 60
#define DISABLE_LEVELTICK_THREAD
Minecraft *Minecraft::m_instance = nullptr;
int64_t Minecraft::frameTimes[512];
int64_t Minecraft::tickTimes[512];
int Minecraft::frameTimePos = 0;
int64_t Minecraft::warezTime = 0;
File Minecraft::workDir = File(L"");
#ifdef __PSVITA__
TOUCHSCREENRECT QuickSelectRect[3]=
{
{ 560, 890, 1360, 980 },
{ 450, 840, 1449, 960 },
{ 320, 840, 1600, 970 },
};
int QuickSelectBoxWidth[3]=
{
89,
111,
142
};
// 4J - TomK ToDo: these really shouldn't be magic numbers, it should read the hud position from flash.
int iToolTipOffset = 85;
#endif
ResourceLocation Minecraft::DEFAULT_FONT_LOCATION = ResourceLocation(TN_DEFAULT_FONT);
ResourceLocation Minecraft::ALT_FONT_LOCATION = ResourceLocation(TN_ALT_FONT);
Minecraft::Minecraft(Component *mouseComponent, Canvas *parent, MinecraftApplet *minecraftApplet, int width, int height, bool fullscreen)
{
// 4J - added this block of initialisers
gameMode = nullptr;
hasCrashed = false;
timer = new Timer(SharedConstants::TICKS_PER_SECOND);
oldLevel = nullptr; //4J Stu added
level = nullptr;
levels = MultiPlayerLevelArray(3); // 4J Added
levelRenderer = nullptr;
player = nullptr;
cameraTargetPlayer = nullptr;
particleEngine = nullptr;
user = nullptr;
parent = nullptr;
pause = false;
textures = nullptr;
font = nullptr;
localPlayerIdx = 0;
rightClickDelay = 0;
// 4J Stu Added
InitializeCriticalSection(&m_setLevelCS);
//m_hPlayerRespawned = CreateEvent(nullptr, FALSE, FALSE, nullptr);
progressRenderer = nullptr;
gameRenderer = nullptr;
bgLoader = nullptr;
ticks = 0;
// 4J-PB - moved into the local player
//missTime = 0;
//lastClickTick = 0;
//isRaining = false;
// 4J-PB - end
orgWidth = orgHeight = 0;
noRender = true;
//humanoidModel = new HumanoidModel(0);
hitResult = nullptr;
options = nullptr;
//soundEngine = new SoundEngine();
mouseHandler = nullptr;
skins = nullptr;
workingDirectory = File(L"");
levelSource = nullptr;
stats[0] = nullptr;
stats[1] = nullptr;
stats[2] = nullptr;
stats[3] = nullptr;
connectToPort = 0;
workDir = File(L"");
// 4J removed
//wasDown = false;
lastTimer = -1;
// 4J removed
//lastTickTime = System::currentTimeMillis();
recheckPlayerIn = 0;
running = true;
unoccupiedQuadrant = -1;
Stats::init();
orgHeight = height;
this->fullscreen = fullscreen;
this->minecraftApplet = nullptr;
this->parent = parent;
// 4J - Our actual physical frame buffer is always 1280x720 ie in a 16:9 ratio. If we want to do a 4:3 mode, we are telling the original minecraft code
// that the width is 3/4 what it actually is, to correctly present a 4:3 image. Have added width_phys and height_phys for any code we add that requires
// to know the real physical dimensions of the frame buffer.
if( RenderManager.IsWidescreen() )
{
this->width = width;
}
else
{
this->width = (width * 3 ) / 4;
}
this->height = height;
this->width_phys = width;
this->height_phys = height;
this->fullscreen = fullscreen;
appletMode = false;
Minecraft::m_instance = this;
TextureManager::createInstance();
for(int i=0;i<XUSER_MAX_COUNT;i++)
{
m_pendingLocalConnections[i] = nullptr;
m_connectionFailed[i] = false;
localgameModes[i]= nullptr;
}
animateTickLevel = nullptr; // 4J added
m_inFullTutorialBits = 0; // 4J Added
reloadTextures = false;
// initialise the audio before any textures are loaded - to avoid the problem in win64 of the Miles audio causing the codec for textures to be unloaded
// 4J-PB - Removed it from here on Orbis due to it causing a crash with the network init.
// We should work out why...
#ifndef __ORBIS__
//this->soundEngine->init(nullptr);
#endif
#ifndef DISABLE_LEVELTICK_THREAD
levelTickEventQueue = new C4JThread::EventQueue(levelTickUpdateFunc, levelTickThreadInitFunc, "LevelTick_EventQueuePoll");
levelTickEventQueue->setProcessor(3);
levelTickEventQueue->setPriority(THREAD_PRIORITY_NORMAL);
#endif // DISABLE_LEVELTICK_THREAD
}
void Minecraft::clearConnectionFailed()
{
for(int i=0;i<XUSER_MAX_COUNT;i++)
{
m_connectionFailed[i] = false;
m_connectionFailedReason[i] = DisconnectPacket::eDisconnect_None;
}
app.SetDisconnectReason(DisconnectPacket::eDisconnect_None);
}
void Minecraft::connectTo(const wstring& server, int port)
{
connectToIp = server;
connectToPort = port;
}
void Minecraft::init()
{
workingDirectory = File(L"");//getWorkingDirectory();
levelSource = new McRegionLevelStorageSource(File(workingDirectory, L"saves"));
// levelSource = new MemoryLevelStorageSource();
options = new Options(this, workingDirectory);
textures = new Textures(skins, options);
gameRenderer = new GameRenderer(this);
for( int i=0 ; i<4 ; ++i ) stats[i] = new StatsCounter();
MemSect(31);
checkGlError(L"Pre startup");
MemSect(0);
MemSect(31);
checkGlError(L"Startup");
MemSect(0);
levelRenderer = new LevelRenderer(this, textures);
MemSect(31);
checkGlError(L"Post startup");
MemSect(0);
RenderManager.CBuffLockStaticCreations();
}
void Minecraft::renderLoadingScreen()
{
}
void Minecraft::blit(int x, int y, int sx, int sy, int w, int h)
{
}
LevelStorageSource *Minecraft::getLevelSource()
{
return levelSource;
}
void Minecraft::setScreen(Screen *screen)
{
}
void Minecraft::checkGlError(const wstring& string)
{
// 4J - TODO
}
void Minecraft::destroy()
{
//4J Gordon: Do not force a stats save here
/*stats->forceSend();
stats->forceSave();*/
// try {
setLevel(nullptr);
// } catch (Throwable e) {
// }
// try {
MemoryTracker::release();
// } catch (Throwable e) {
// }
soundEngine->destroy();
//} finally {
Display::destroy();
// if (!hasCrashed) System.exit(0); //4J - removed
//}
//System.gc(); // 4J - removed
}
// 4J-PB - splitting this function into 3 parts, so we can call the middle part from our xbox game loop
#if 0
void Minecraft::run()
{
running = true;
// try { // 4J - removed try/catch
init();
// } catch (Exception e) {
// e.printStackTrace();
// crash(new CrashReport("Failed to start game", e));
// return;
// }
// try { // 4J - removed try/catch
if (Minecraft::FLYBY_MODE)
{
generateFlyby();
return;
}
int64_t lastTime = System::currentTimeMillis();
int frames = 0;
while (running)
{
// try { // 4J - removed try/catch
// if (minecraftApplet != null && !minecraftApplet.isActive()) break; // 4J - removed
AABB::resetPool();
Vec3::resetPool();
// if (parent == nullptr && Display.isCloseRequested()) { // 4J - removed
// stop();
// }
if (pause && level != nullptr)
{
float lastA = timer->a;
timer->advanceTime();
timer->a = lastA;
}
else
{
timer->advanceTime();
}
int64_t beforeTickTime = System::nanoTime();
for (int i = 0; i < timer->ticks; i++)
{
ticks++;
// try { // 4J - try/catch removed
tick();
// } catch (LevelConflictException e) {
// this.level = null;
// setLevel(null);
// setScreen(new LevelConflictScreen());
// }
}
int64_t tickDuraction = System::nanoTime() - beforeTickTime;
checkGlError(L"Pre render");
TileRenderer::fancy = options->fancyGraphics;
// if (pause) timer.a = 1;
soundEngine->update(player, timer->a);
glEnable(GL_TEXTURE_2D);
if (level != nullptr) level->updateLights();
// if (!Keyboard::isKeyDown(Keyboard.KEY_F7)) Display.update(); // 4J - removed
if (player != nullptr && player->isInWall()) options->thirdPersonView = false;
if (!noRender)
{
if (gameMode != nullptr) gameMode->render(timer->a);
gameRenderer->render(timer->a);
}
/* 4J - removed
if (!Display::isActive())
{
if (fullscreen)
{
this->toggleFullScreen();
}
Sleep(10);
}
*/
if (options->renderDebug)
{
renderFpsMeter(tickDuraction);
}
else
{
lastTimer = System::nanoTime();
}
achievementPopup->render();
Sleep(0); // 4J - was Thread.yield()
// if (Keyboard::isKeyDown(Keyboard::KEY_F7)) Display.update(); // 4J - removed condition
Display::update();
// checkScreenshot(); // 4J - removed
/* 4J - removed
if (parent != nullptr && !fullscreen)
{
if (parent.getWidth() != width || parent.getHeight() != height)
{
width = parent.getWidth();
height = parent.getHeight();
if (width <= 0) width = 1;
if (height <= 0) height = 1;
resize(width, height);
}
}
*/
checkGlError(L"Post render");
frames++;
pause = !isClientSide() && screen != nullptr && screen->isPauseScreen();
while (System::currentTimeMillis() >= lastTime + 1000)
{
fpsString = std::to_wstring(frames) + L" fps (" + std::to_wstring(Chunk::updates) + L" chunk updates)";
Chunk::updates = 0;
lastTime += 1000;
frames = 0;
}
/*
} catch (LevelConflictException e) {
this.level = null;
setLevel(null);
setScreen(new LevelConflictScreen());
} catch (OutOfMemoryError e) {
emergencySave();
setScreen(new OutOfMemoryScreen());
System.gc();
}
*/
}
/*
} catch (StopGameException e) {
} catch (Throwable e) {
emergencySave();
e.printStackTrace();
crash(new CrashReport("Unexpected error", e));
} finally {
destroy();
}
*/
destroy();
}
#endif
void Minecraft::run()
{
running = true;
// try { // 4J - removed try/catch
init();
// } catch (Exception e) {
// e.printStackTrace();
// crash(new CrashReport("Failed to start game", e));
// return;
// }
// try { // 4J - removed try/catch
}
// 4J added - Selects which local player is currently active for processing by the existing minecraft code
bool Minecraft::setLocalPlayerIdx(int idx)
{
return true;
}
int Minecraft::getLocalPlayerIdx()
{
return localPlayerIdx;
}
void Minecraft::updatePlayerViewportAssignments()
{
unoccupiedQuadrant = -1;
// Find out how many viewports we'll be needing
int viewportsRequired = 0;
for( int i = 0; i < XUSER_MAX_COUNT; i++ )
{
if( localplayers[i] != nullptr ) viewportsRequired++;
}
if( viewportsRequired == 3 ) viewportsRequired = 4;
// Allocate away...
if( viewportsRequired == 1 )
{
// Single viewport
for( int i = 0; i < XUSER_MAX_COUNT; i++ )
{
if( localplayers[i] != nullptr ) localplayers[i]->m_iScreenSection = C4JRender::VIEWPORT_TYPE_FULLSCREEN;
}
}
else if( viewportsRequired == 2 )
{
// Split screen - TODO - option for vertical/horizontal split
int found = 0;
for( int i = 0; i < XUSER_MAX_COUNT; i++ )
{
if( localplayers[i] != nullptr )
{
// Primary player settings decide what the mode is
if(app.GetGameSettings(ProfileManager.GetPrimaryPad(),eGameSetting_SplitScreenVertical))
{
localplayers[i]->m_iScreenSection = C4JRender::VIEWPORT_TYPE_SPLIT_LEFT + found;
}
else
{
localplayers[i]->m_iScreenSection = C4JRender::VIEWPORT_TYPE_SPLIT_TOP + found;
}
found++;
}
}
}
else if( viewportsRequired >= 3 )
{
// Quadrants - this is slightly more complicated. We don't want to move viewports around if we are going from 3 to 4, or 4 to 3 players,
// so persist any allocations for quadrants that already exist.
bool quadrantsAllocated[4] = {false,false,false,false};
for( int i = 0; i < XUSER_MAX_COUNT; i++ )
{
if( localplayers[i] != nullptr )
{
// 4J Stu - If the game hasn't started, ignore current allocations (as the players won't have seen them)
// This fixes an issue with the primary player being the 4th controller quadrant, but ending up in the 3rd viewport.
if(app.GetGameStarted())
{
if( ( localplayers[i]->m_iScreenSection >= C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_LEFT ) &&
( localplayers[i]->m_iScreenSection <= C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_RIGHT ) )
{
quadrantsAllocated[localplayers[i]->m_iScreenSection - C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_LEFT] = true;
}
}
else
{
// Reset the viewport so that it can be assigned in the next loop
localplayers[i]->m_iScreenSection = C4JRender::VIEWPORT_TYPE_FULLSCREEN;
}
}
}
// Found which quadrants are currently in use, now allocate out any spares that are required
for( int i = 0; i < XUSER_MAX_COUNT; i++ )
{
if( localplayers[i] != nullptr )
{
if( ( localplayers[i]->m_iScreenSection < C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_LEFT ) ||
( localplayers[i]->m_iScreenSection > C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_RIGHT ) )
{
for( int j = 0; j < 4; j++ )
{
if( !quadrantsAllocated[j] )
{
localplayers[i]->m_iScreenSection = C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_LEFT + j;
quadrantsAllocated[j] = true;
break;
}
}
}
}
}
// If there's an unoccupied quadrant, record which one so we can clear it to black when rendering
for( int i = 0; i < XUSER_MAX_COUNT; i++ )
{
if( quadrantsAllocated[i] == false )
{
unoccupiedQuadrant = i;
}
}
}
}
// Add a temporary player so that the viewports get re-arranged, and add the player to the game session
bool Minecraft::addLocalPlayer(int idx)
{
//int iLocalPlayerC=app.GetLocalPlayerCount();
if( m_pendingLocalConnections[idx] != nullptr )
{
// 4J Stu - Should we ever be in a state where this happens?
assert(false);
m_pendingLocalConnections[idx]->close();
}
m_connectionFailed[idx] = false;
m_pendingLocalConnections[idx] = nullptr;
bool success=g_NetworkManager.AddLocalPlayerByUserIndex(idx);
if(success)
{
app.DebugPrintf("Adding temp local player on pad %d\n", idx);
localplayers[idx] = shared_ptr<MultiplayerLocalPlayer>(new MultiplayerLocalPlayer(this, level, user, nullptr));
localgameModes[idx] = nullptr;
updatePlayerViewportAssignments();
#ifdef _XBOX
// tell the xui scenes a splitscreen player joined
XUIMessage xuiMsg;
CustomMessage_Splitscreenplayer_Struct myMsgData;
CustomMessage_Splitscreenplayer( &xuiMsg, &myMsgData, true);
// send the message
for(int i=0;i<XUSER_MAX_COUNT;i++)
{
//if((i!=idx) && (localplayers[i]!=nullptr))
{
XuiBroadcastMessage( CXuiSceneBase::GetPlayerBaseScene(i), &xuiMsg );
}
}
#endif
}
else
{
app.DebugPrintf("g_NetworkManager.AddLocalPlayerByUserIndex failed\n");
#ifdef _DURANGO
ProfileManager.RemoveGamepadFromGame(idx);
#endif
}
return success;
}
void Minecraft::addPendingLocalConnection(int idx, ClientConnection *connection)
{
m_pendingLocalConnections[idx] = connection;
}
shared_ptr<MultiplayerLocalPlayer> Minecraft::createExtraLocalPlayer(int idx, const wstring& name, int iPad, int iDimension, ClientConnection *clientConnection /*= nullptr*/,MultiPlayerLevel *levelpassedin)
{
if( clientConnection == nullptr) return nullptr;
if( clientConnection == m_pendingLocalConnections[idx] )
{
int tempScreenSection = C4JRender::VIEWPORT_TYPE_FULLSCREEN;
if( localplayers[idx] != nullptr && localgameModes[idx] == nullptr )
{
// A temp player displaying a connecting screen
tempScreenSection = localplayers[idx]->m_iScreenSection;
}
wstring prevname = user->name;
user->name = name;
// Don't need this any more
m_pendingLocalConnections[idx] = nullptr;
// Add the connection to the level which will now take responsibility for ticking it
// 4J-PB - can't use the dimension from localplayers[idx], since there may be no localplayers at this point
//MultiPlayerLevel *mpLevel = (MultiPlayerLevel *)getLevel( localplayers[idx]->dimension );
MultiPlayerLevel *mpLevel;
if(levelpassedin)
{
level=levelpassedin;
mpLevel=levelpassedin;
}
else
{
level=getLevel( iDimension );
mpLevel = getLevel( iDimension );
mpLevel->addClientConnection( clientConnection );
}
if( app.GetTutorialMode() )
{
localgameModes[idx] = new FullTutorialMode(idx, this, clientConnection);
}
// check if we're in the trial version
else if(ProfileManager.IsFullVersion()==false)
{
localgameModes[idx] = new TrialMode(idx, this, clientConnection);
}
else
{
localgameModes[idx] = new ConsoleGameMode(idx, this, clientConnection);
}
// 4J-PB - can't do this here because they use a render context, but this is running from a thread.
// Moved the creation of these into the main thread, before level launch
//localitemInHandRenderers[idx] = new ItemInHandRenderer(this);
localplayers[idx] = localgameModes[idx]->createPlayer(level);
PlayerUID playerXUIDOffline = INVALID_XUID;
PlayerUID playerXUIDOnline = INVALID_XUID;
ProfileManager.GetXUID(idx,&playerXUIDOffline,false);
ProfileManager.GetXUID(idx,&playerXUIDOnline,true);
#ifdef _WINDOWS64
// Compatibility rule for Win64 id migration
// host keeps legacy host XUID, non-host uses persistent uid.dat XUID.
INetworkPlayer *localNetworkPlayer = g_NetworkManager.GetLocalPlayerByUserIndex(idx);
if(localNetworkPlayer != nullptr && localNetworkPlayer->IsHost())
{
playerXUIDOffline = Win64Xuid::GetLegacyEmbeddedHostXuid();
}
else
{
playerXUIDOffline = Win64Xuid::ResolvePersistentXuid();
}
#endif
localplayers[idx]->setXuid(playerXUIDOffline);
localplayers[idx]->setOnlineXuid(playerXUIDOnline);
localplayers[idx]->setIsGuest(ProfileManager.IsGuest(idx));
localplayers[idx]->m_displayName = ProfileManager.GetDisplayName(idx);
localplayers[idx]->m_iScreenSection = tempScreenSection;
if( levelpassedin == nullptr) level->addEntity(localplayers[idx]); // Don't add if we're passing the level in, we only do this from the client connection & we'll be handling adding it ourselves
localplayers[idx]->SetXboxPad(iPad);
if( localplayers[idx]->input != nullptr ) delete localplayers[idx]->input;
localplayers[idx]->input = new Input();
localplayers[idx]->resetPos();
levelRenderer->setLevel(idx, level);
localplayers[idx]->level = level;
user->name = prevname;
updatePlayerViewportAssignments();
// Fix for #105852 - TU12: Content: Gameplay: Local splitscreen Players are spawned at incorrect places after re-joining previously saved and loaded "Mass Effect World".
// Move this check to ClientConnection::handleMovePlayer
// // 4J-PB - can't call this when this function is called from the qnet thread (GetGameStarted will be false)
// if(app.GetGameStarted())
// {
// ui.CloseUIScenes(idx);
// }
}
return localplayers[idx];
}
// on a respawn of the local player, just store them
void Minecraft::storeExtraLocalPlayer(int idx)
{
localplayers[idx] = player;
if( localplayers[idx]->input != nullptr ) delete localplayers[idx]->input;
localplayers[idx]->input = new Input();
if(ProfileManager.IsSignedIn(idx))
{
localplayers[idx]->name = convStringToWstring( ProfileManager.GetGamertag(idx) );
}
}
void Minecraft::removeLocalPlayerIdx(int idx)
{
bool updateXui = true;
if(localgameModes[idx] != nullptr)
{
if( getLevel( localplayers[idx]->dimension )->isClientSide )
{
shared_ptr<MultiplayerLocalPlayer> mplp = localplayers[idx];
( (MultiPlayerLevel *)getLevel( localplayers[idx]->dimension ) )->removeClientConnection(mplp->connection, true);
delete mplp->connection;
mplp->connection = nullptr;
g_NetworkManager.RemoveLocalPlayerByUserIndex(idx);
}
getLevel( localplayers[idx]->dimension )->removeEntity(localplayers[idx]);
#ifdef _XBOX
// 4J Stu - Fix for #12368 - Crash: Game crashes when saving then exiting and selecting to save
app.TutorialSceneNavigateBack(idx);
#endif
// 4J Stu - Fix for #13257 - CRASH: Gameplay: Title crashed after exiting the tutorial
// It doesn't matter if they were in the tutorial already
playerLeftTutorial( idx );
delete localgameModes[idx];
localgameModes[idx] = nullptr;
}
else if( m_pendingLocalConnections[idx] != nullptr )
{
m_pendingLocalConnections[idx]->sendAndDisconnect(std::make_shared<DisconnectPacket>(DisconnectPacket::eDisconnect_Quitting));;
delete m_pendingLocalConnections[idx];
m_pendingLocalConnections[idx] = nullptr;
g_NetworkManager.RemoveLocalPlayerByUserIndex(idx);
}
else
{
// Not sure how this works on qnet, but for other platforms, calling RemoveLocalPlayerByUserIndex won't do anything if there isn't a local user to remove
// Now just updating the UI directly in this case
#ifdef _XBOX
// 4J Stu - A signout early in the game creation before this player has connected to the game server
updateXui = false;
#endif
// 4J Stu - Adding this back in for exactly the reason my comment above suggests it was added in the first place
#if defined(_XBOX_ONE) || defined(__ORBIS__)
g_NetworkManager.RemoveLocalPlayerByUserIndex(idx);
#endif
}
localplayers[idx] = nullptr;
if( idx == ProfileManager.GetPrimaryPad() )
{
// We should never try to remove the Primary player in this way
assert(false);
/*
// If we are removing the primary player then there can't be a valid gamemode left anymore, this
// pointer will be referring to the one we've just deleted
gameMode = nullptr;
// Remove references to player
player = nullptr;
cameraTargetPlayer = nullptr;
EntityRenderDispatcher::instance->cameraEntity = nullptr;
TileEntityRenderDispatcher::instance->cameraEntity = nullptr;
*/
}
else if( updateXui )
{
gameRenderer->DisableUpdateThread();
levelRenderer->setLevel(idx, nullptr);
gameRenderer->EnableUpdateThread();
updatePlayerViewportAssignments();
}
// We only create these once ever so don't delete it here
//delete localitemInHandRenderers[idx];
}
void Minecraft::createPrimaryLocalPlayer(int iPad)
{
localgameModes[iPad] = gameMode;
localplayers[iPad] = player;
//gameRenderer->itemInHandRenderer = localitemInHandRenderers[iPad];
// Give them the gamertag if they're signed in
if(ProfileManager.IsSignedIn(ProfileManager.GetPrimaryPad()))
{
user->name = convStringToWstring( ProfileManager.GetGamertag(ProfileManager.GetPrimaryPad()) );
}
}
#ifdef _WINDOWS64
void Minecraft::applyFrameMouseLook()
{
// Per-frame mouse look: consume mouse deltas every frame instead of waiting
// for the 20Hz game tick. Apply the same delta to both xRot/yRot AND xRotO/yRotO
// so the render interpolation instantly reflects the change without waiting for a tick.
if (level == nullptr) return;
for (int i = 0; i < XUSER_MAX_COUNT; i++)
{
if (localplayers[i] == nullptr) continue;
int iPad = localplayers[i]->GetXboxPad();
if (iPad != 0) continue; // Mouse only applies to pad 0
if (!g_KBMInput.IsMouseGrabbed()) continue;
if (localgameModes[iPad] == nullptr) continue;
float rawDx, rawDy;
g_KBMInput.ConsumeMouseDelta(rawDx, rawDy);
if (rawDx == 0.0f && rawDy == 0.0f) continue;
float mouseSensitivity = static_cast<float>(app.GetGameSettings(iPad, eGameSetting_Sensitivity_InGame)) / 100.0f;
float mdx = rawDx * mouseSensitivity;
float mdy = -rawDy * mouseSensitivity;
if (app.GetGameSettings(iPad, eGameSetting_ControlInvertLook))
mdy = -mdy;
// Apply 0.15f scaling (same as Entity::interpolateTurn / Entity::turn)
float dyaw = mdx * 0.15f;
float dpitch = -mdy * 0.15f;
// Apply to both current and old rotation so render interpolation
// reflects the change immediately (no 50ms tick delay)
localplayers[i]->yRot += dyaw;
localplayers[i]->yRotO += dyaw;
localplayers[i]->xRot += dpitch;
localplayers[i]->xRotO += dpitch;
// Clamp pitch
if (localplayers[i]->xRot < -90.0f) localplayers[i]->xRot = -90.0f;
if (localplayers[i]->xRot > 90.0f) localplayers[i]->xRot = 90.0f;
if (localplayers[i]->xRotO < -90.0f) localplayers[i]->xRotO = -90.0f;
if (localplayers[i]->xRotO > 90.0f) localplayers[i]->xRotO = 90.0f;
}
}
#endif
void Minecraft::run_middle()
{
static int64_t lastTime = 0;
static bool bFirstTimeIntoGame = true;
static bool bAutosaveTimerSet=false;
static unsigned int uiAutosaveTimer=0;
static int iFirstTimeCountdown=60;
if( lastTime == 0 ) lastTime = System::nanoTime();
static int frames = 0;
EnterCriticalSection(&m_setLevelCS);
if(running)
{
if (reloadTextures)
{
reloadTextures = false;
textures->reloadAll();
}
//while (running)
{
// try { // 4J - removed try/catch
// if (minecraftApplet != null && !minecraftApplet.isActive()) break; // 4J - removed
AABB::resetPool();
Vec3::resetPool();
// if (parent == nullptr && Display.isCloseRequested()) { // 4J - removed
// stop();
// }
// 4J-PB - Once we're in the level, check if the players have the level in their banned list and ask if they want to play it
for( int i = 0; i < XUSER_MAX_COUNT; i++ )
{
if( localplayers[i] && (app.GetBanListCheck(i)==false) && !Minecraft::GetInstance()->isTutorial() && ProfileManager.IsSignedInLive(i) && !ProfileManager.IsGuest(i) )
{
// If there is a sys ui displayed, we can't display the message box here, so ignore until we can
if(!ProfileManager.IsSystemUIDisplayed())
{
app.SetBanListCheck(i,true);
// 4J-PB - check if the level is in the banned level list
// get the unique save name and xuid from whoever is the host
#if defined _XBOX || defined _XBOX_ONE
INetworkPlayer *pHostPlayer = g_NetworkManager.GetHostPlayer();
#ifdef _XBOX
PlayerUID xuid=((NetworkPlayerXbox *)pHostPlayer)->GetUID();
#else
PlayerUID xuid=pHostPlayer->GetUID();
#endif
if(app.IsInBannedLevelList(i,xuid,app.GetUniqueMapName()))
{
// put up a message box asking if the player would like to unban this level
app.DebugPrintf("This level is banned\n");
// set the app action to bring up the message box to give them the option to remove from the ban list or exit the level
app.SetAction(i,eAppAction_LevelInBanLevelList,(void *)TRUE);
}
#endif
}
}
}
if(!ProfileManager.IsSystemUIDisplayed() && app.DLCInstallProcessCompleted() && !app.DLCInstallPending() && app.m_dlcManager.NeedsCorruptCheck() )
{
app.m_dlcManager.checkForCorruptDLCAndAlert();
}
for( int i = 0; i < XUSER_MAX_COUNT; i++ )
{
#ifdef __ORBIS__
if ( m_pPsPlusUpsell != nullptr && m_pPsPlusUpsell->hasResponse() && m_pPsPlusUpsell->m_userIndex == i )
{
delete m_pPsPlusUpsell;
m_pPsPlusUpsell = nullptr;
if ( ProfileManager.HasPlayStationPlus(i) )
{
app.DebugPrintf("<Minecraft.cpp> Player_%i is now authorised for PsPlus.\n", i);
if (!ui.PressStartPlaying(i)) ui.ShowPressStart(i);
}
else
{
UINT uiIDA[1] = { IDS_OK };
ui.RequestErrorMessage( IDS_CANTJOIN_TITLE, IDS_NO_PLAYSTATIONPLUS, uiIDA, 1, i);
}
}
else
#endif
if(localplayers[i])
{
// 4J-PB - add these to check for coming out of idle
if(InputManager.ButtonPressed(i, MINECRAFT_ACTION_JUMP)) localplayers[i]->ullButtonsPressed|=1LL<<MINECRAFT_ACTION_JUMP;
if(InputManager.ButtonPressed(i, MINECRAFT_ACTION_USE)) localplayers[i]->ullButtonsPressed|=1LL<<MINECRAFT_ACTION_USE;
if(InputManager.ButtonPressed(i, MINECRAFT_ACTION_INVENTORY)) localplayers[i]->ullButtonsPressed|=1LL<<MINECRAFT_ACTION_INVENTORY;
if(InputManager.ButtonPressed(i, MINECRAFT_ACTION_ACTION)) localplayers[i]->ullButtonsPressed|=1LL<<MINECRAFT_ACTION_ACTION;
if(InputManager.ButtonPressed(i, MINECRAFT_ACTION_CRAFTING)) localplayers[i]->ullButtonsPressed|=1LL<<MINECRAFT_ACTION_CRAFTING;
if(InputManager.ButtonPressed(i, MINECRAFT_ACTION_PAUSEMENU))
{
localplayers[i]->ullButtonsPressed|=1LL<<MINECRAFT_ACTION_PAUSEMENU;
app.DebugPrintf("PAUSE PRESSED - ipad = %d, Storing press\n",i);
}
#ifdef _DURANGO
if(InputManager.ButtonPressed(i, ACTION_MENU_GTC_PAUSE)) localplayers[i]->ullButtonsPressed|=1LL<<ACTION_MENU_GTC_PAUSE;
#endif
if(InputManager.ButtonPressed(i, MINECRAFT_ACTION_DROP)) localplayers[i]->ullButtonsPressed|=1LL<<MINECRAFT_ACTION_DROP;
// 4J-PB - If we're flying, the sneak needs to be held on to go down
if(localplayers[i]->abilities.flying)
{
if(InputManager.ButtonDown(i, MINECRAFT_ACTION_SNEAK_TOGGLE)) localplayers[i]->ullButtonsPressed|=1LL<<MINECRAFT_ACTION_SNEAK_TOGGLE;
}
else
{
if(InputManager.ButtonPressed(i, MINECRAFT_ACTION_SNEAK_TOGGLE)) localplayers[i]->ullButtonsPressed|=1LL<<MINECRAFT_ACTION_SNEAK_TOGGLE;
}
if(InputManager.ButtonPressed(i, MINECRAFT_ACTION_RENDER_THIRD_PERSON)) localplayers[i]->ullButtonsPressed|=1LL<<MINECRAFT_ACTION_RENDER_THIRD_PERSON;
if(InputManager.ButtonPressed(i, MINECRAFT_ACTION_GAME_INFO)) localplayers[i]->ullButtonsPressed|=1LL<<MINECRAFT_ACTION_GAME_INFO;
#ifdef _WINDOWS64
// Keyboard/mouse button presses for player 0
if (i == 0)
{
if (g_KBMInput.IsKBMActive())
{
if(g_KBMInput.IsMouseButtonPressed(KeyboardMouseInput::MOUSE_LEFT))
localplayers[i]->ullButtonsPressed|=1LL<<MINECRAFT_ACTION_ACTION;
if(g_KBMInput.IsMouseButtonPressed(KeyboardMouseInput::MOUSE_RIGHT))
localplayers[i]->ullButtonsPressed|=1LL<<MINECRAFT_ACTION_USE;
if(g_KBMInput.IsKeyPressed(KeyboardMouseInput::KEY_DROP))
localplayers[i]->ullButtonsPressed|=1LL<<MINECRAFT_ACTION_DROP;
}
if(g_KBMInput.IsKeyPressed(KeyboardMouseInput::KEY_THIRD_PERSON))
localplayers[i]->ullButtonsPressed|=1LL<<MINECRAFT_ACTION_RENDER_THIRD_PERSON;
if(g_KBMInput.IsKeyPressed(KeyboardMouseInput::KEY_DEBUG_MENU))
{
localplayers[i]->ullButtonsPressed|=1LL<<MINECRAFT_ACTION_RENDER_DEBUG;
}
}
#endif
#if _DEBUG // ndef _FINAL_BUILD // Disable conflicting debug functionality in release builds
if( app.DebugSettingsOn() && app.GetUseDPadForDebug() )
{
localplayers[i]->ullDpad_last = 0;
localplayers[i]->ullDpad_this = 0;
localplayers[i]->ullDpad_filtered = 0;
if(InputManager.ButtonPressed(i, MINECRAFT_ACTION_DPAD_RIGHT)) localplayers[i]->ullButtonsPressed|=1LL<<MINECRAFT_ACTION_CHANGE_SKIN;
if(InputManager.ButtonPressed(i, MINECRAFT_ACTION_DPAD_UP)) localplayers[i]->ullButtonsPressed|=1LL<<MINECRAFT_ACTION_FLY_TOGGLE;
if(InputManager.ButtonPressed(i, MINECRAFT_ACTION_DPAD_DOWN)) localplayers[i]->ullButtonsPressed|=1LL<<MINECRAFT_ACTION_RENDER_DEBUG;
if(InputManager.ButtonPressed(i, MINECRAFT_ACTION_DPAD_LEFT)) localplayers[i]->ullButtonsPressed|=1LL<<MINECRAFT_ACTION_SPAWN_CREEPER;
}
else
#endif
{
// Movement on DPAD is stored ulimately into ullDpad_filtered - this ignores any diagonals pressed, instead reporting the last single direction - otherwise
// we get loads of accidental diagonal movements
localplayers[i]->ullDpad_this = 0;
int dirCount = 0;
#ifndef __PSVITA__
if(InputManager.ButtonDown(i, MINECRAFT_ACTION_DPAD_LEFT)) { localplayers[i]->ullDpad_this|=1LL<<MINECRAFT_ACTION_DPAD_LEFT; dirCount++; }
if(InputManager.ButtonDown(i, MINECRAFT_ACTION_DPAD_RIGHT)) { localplayers[i]->ullDpad_this|=1LL<<MINECRAFT_ACTION_DPAD_RIGHT; dirCount++; }
if(InputManager.ButtonDown(i, MINECRAFT_ACTION_DPAD_UP)) { localplayers[i]->ullDpad_this|=1LL<<MINECRAFT_ACTION_DPAD_UP; dirCount++; }
if(InputManager.ButtonDown(i, MINECRAFT_ACTION_DPAD_DOWN)) { localplayers[i]->ullDpad_this|=1LL<<MINECRAFT_ACTION_DPAD_DOWN; dirCount++; }
#endif
if( dirCount <= 1 )
{
localplayers[i]->ullDpad_last = localplayers[i]->ullDpad_this;
localplayers[i]->ullDpad_filtered = localplayers[i]->ullDpad_this;
}
else
{
localplayers[i]->ullDpad_filtered = localplayers[i]->ullDpad_last;
}
}
// for the opacity timer
if(InputManager.ButtonPressed(i, MINECRAFT_ACTION_LEFT_SCROLL) || InputManager.ButtonPressed(i, MINECRAFT_ACTION_RIGHT_SCROLL))
//InputManager.ButtonPressed(i, MINECRAFT_ACTION_USE) || InputManager.ButtonPressed(i, MINECRAFT_ACTION_ACTION))
{
app.SetOpacityTimer(i);
}
}
else
{
// 4J Stu - This doesn't make any sense with the way we handle XboxOne users
#ifndef _DURANGO
// did we just get input from a player who doesn't exist? They'll be wanting to join the game then
#ifdef _WINDOWS64
// The 4J toggle system is unreliable here: UIController::handleInput() calls
// ButtonPressed for every ACTION_MENU_* mapped button (which covers all physical
// buttons) before run_middle() runs. Bypass it with raw XInput and own edge detection.
// A latch counter keeps startJustPressed active for ~120 frames after the rising edge
// so the detection window is large enough to be caught reliably.
static WORD s_prevXButtons[XUSER_MAX_COUNT] = {};
static int s_startPressLatch[XUSER_MAX_COUNT] = {};
XINPUT_STATE xstate_join;
memset(&xstate_join, 0, sizeof(xstate_join));
WORD xCurButtons = 0;
if (XInputGetState(i, &xstate_join) == ERROR_SUCCESS)
{
xCurButtons = xstate_join.Gamepad.wButtons;
if ((xCurButtons & XINPUT_GAMEPAD_START) != 0 && (s_prevXButtons[i] & XINPUT_GAMEPAD_START) == 0)
s_startPressLatch[i] = 120; // rising edge: latch for ~120 frames (~2s at 60fps)
else if (s_startPressLatch[i] > 0)
s_startPressLatch[i]--;
s_prevXButtons[i] = xCurButtons;
}
bool startJustPressed = s_startPressLatch[i] > 0;
#else
bool tryJoin = !pause && !ui.IsIgnorePlayerJoinMenuDisplayed(ProfileManager.GetPrimaryPad()) && g_NetworkManager.SessionHasSpace() && RenderManager.IsHiDef() && InputManager.ButtonPressed(i);
#endif
#ifdef __ORBIS__
// Check for remote play
tryJoin = tryJoin && InputManager.IsLocalMultiplayerAvailable();
// 4J Stu - Check that content restriction information has been received
if( !g_NetworkManager.IsLocalGame() )
{
tryJoin = tryJoin && ProfileManager.GetChatAndContentRestrictions(i,true,nullptr,nullptr,nullptr);
}
#endif
#endif // _DURANGO
}
}
#ifdef _DURANGO
// did we just get input from a player who doesn't exist? They'll be wanting to join the game then
if(!pause && !ui.IsIgnorePlayerJoinMenuDisplayed(ProfileManager.GetPrimaryPad()) && g_NetworkManager.SessionHasSpace() && RenderManager.IsHiDef() )
{
int firstEmptyUser = 0;
for( int i = 0; i < XUSER_MAX_COUNT; i++ )
{
if(localplayers[i] == nullptr)
{
firstEmptyUser = i;
break;
}
}
// For durango, check for unmapped controllers
for(unsigned int iPad = XUSER_MAX_COUNT; iPad < (XUSER_MAX_COUNT + InputManager.MAX_GAMEPADS); ++iPad)
{
bool isPadLocked = InputManager.IsPadLocked(iPad), isPadConnected = InputManager.IsPadConnected(iPad), buttonPressed = InputManager.ButtonPressed(iPad);
if (isPadLocked || !isPadConnected || !buttonPressed) continue;
if(!ui.PressStartPlaying(firstEmptyUser))
{
ui.ShowPressStart(firstEmptyUser);
}
else
{
// did we just get input from a player who doesn't exist? They'll be wanting to join the game then
if(InputManager.ButtonPressed(iPad, MINECRAFT_ACTION_PAUSEMENU))
{
// bring up the sign in dialog
app.DebugPrintf("Bringing up the sign in ui\n");
ProfileManager.RequestSignInUI(false, g_NetworkManager.IsLocalGame(), true, false,true,&Minecraft::InGame_SignInReturned, this,iPad);
// 4J Stu - If we are joining a pad here, then we don't want to try and join any others
break;
}
}
}
}
#endif
if (pause && level != nullptr)
{
float lastA = timer->a;
timer->advanceTime();
timer->a = lastA;
}
else
{
timer->advanceTime();
}
//int64_t beforeTickTime = System::nanoTime();
for (int i = 0; i < timer->ticks; i++)
{
bool bLastTimerTick = ( i == ( timer->ticks - 1 ) );
// 4J-PB - the tick here can run more than once, and this is a problem for our input, which would see the a key press twice with the same time - let's tick the inputmanager again
if(i!=0)
{
InputManager.Tick();
app.HandleButtonPresses();
}
ticks++;
// try { // 4J - try/catch removed
bool bFirst = true;
for( int idx = 0; idx < XUSER_MAX_COUNT; idx++ )
{
// 4J - If we are waiting for this connection to do something, then tick it here.
// This replaces many of the original Java scenes which would tick the connection while showing that scene
if( m_pendingLocalConnections[idx] != nullptr )
{
m_pendingLocalConnections[idx]->tick();
}
// reset the player inactive tick
if(localplayers[idx]!=nullptr)
{
// any input received?
if((localplayers[idx]->ullButtonsPressed!=0) || InputManager.GetJoypadStick_LX(idx,false)!=0.0f ||
InputManager.GetJoypadStick_LY(idx,false)!=0.0f || InputManager.GetJoypadStick_RX(idx,false)!=0.0f ||
InputManager.GetJoypadStick_RY(idx,false)!=0.0f )
{
localplayers[idx]->ResetInactiveTicks();
}
else
{
localplayers[idx]->IncrementInactiveTicks();
}
if(localplayers[idx]->GetInactiveTicks()>200)
{
if(!localplayers[idx]->isIdle() && localplayers[idx]->onGround)
{
localplayers[idx]->setIsIdle(true);
}
}
else
{
if(localplayers[idx]->isIdle())
{
localplayers[idx]->setIsIdle(false);
}
}
}
if( setLocalPlayerIdx(idx) )
{
tick(bFirst, bLastTimerTick);
bFirst = false;
// clear the stored button downs since the tick for this player will now have actioned them
player->ullButtonsPressed=0LL;
}
}
setLocalPlayerIdx(ProfileManager.GetPrimaryPad());
// 4J - added - now do the equivalent of level::animateTick, but taking into account the positions of all our players
for( int l = 0; l < levels.length; l++ )
{
if( levels[l] )
{
levels[l]->animateTickDoWork();
}
}
// } catch (LevelConflictException e) {
// this.level = null;
// setLevel(null);
// setScreen(new LevelConflictScreen());
// }
// SparseLightStorage::tick(); // 4J added
// CompressedTileStorage::tick(); // 4J added
// SparseDataStorage::tick(); // 4J added
}
//int64_t tickDuraction = System::nanoTime() - beforeTickTime;
MemSect(31);
checkGlError(L"Pre render");
MemSect(0);
// if (pause) timer.a = 1;
PIXBeginNamedEvent(0,"Sound engine update");
soundEngine->tick((shared_ptr<Mob> *)localplayers, timer->a);
PIXEndNamedEvent();
PIXBeginNamedEvent(0,"Light update");
glEnable(GL_TEXTURE_2D);
PIXEndNamedEvent();
// if (!Keyboard::isKeyDown(Keyboard.KEY_F7)) Display.update(); // 4J - removed
// 4J-PB - changing this to be per player
//if (player != nullptr && player->isInWall()) options->thirdPersonView = false;
if (player != nullptr && player->isInWall()) player->SetThirdPersonView(0);
if (!noRender)
{
bool bFirst = true;
int iPrimaryPad=ProfileManager.GetPrimaryPad();
for( int i = 0; i < XUSER_MAX_COUNT; i++ )
{
if( setLocalPlayerIdx(i) )
{
PIXBeginNamedEvent(0,"Game render player idx %d",i);
RenderManager.StateSetViewport(static_cast<C4JRender::eViewportType>(player->m_iScreenSection));
//gameRenderer->render(timer->a, bFirst);
bFirst = false;
PIXEndNamedEvent();
if(i==iPrimaryPad)
{
#ifdef __ORBIS__
// PS4 does much of the screen-capturing for every frame, to simplify the synchronisation when we actually want a capture. This call tells it the point in the frame to do it.
RenderManager.InternalScreenCapture();
#endif
// check to see if we need to capture a screenshot for the save game thumbnail
switch(app.GetXuiAction(i))
{
case eAppAction_ExitWorldCapturedThumbnail:
case eAppAction_SaveGameCapturedThumbnail:
case eAppAction_AutosaveSaveGameCapturedThumbnail:
// capture the save thumbnail
app.CaptureSaveThumbnail();
break;
}
}
}
}
setLocalPlayerIdx(iPrimaryPad);
RenderManager.StateSetViewport(C4JRender::VIEWPORT_TYPE_FULLSCREEN);
#ifdef _XBOX
// Do we need to capture a screenshot for a social post?
for( int i = 0; i < XUSER_MAX_COUNT; i++ )
{
if(app.GetXuiAction(i)==eAppAction_SocialPostScreenshot)
{
app.CaptureScreenshot(i);
}
}
#endif
}
glFlush();
/* 4J - removed
if (!Display::isActive())
{
if (fullscreen)
{
this->toggleFullScreen();
}
Sleep(10);
}
*/
#if PACKET_ENABLE_STAT_TRACKING
Packet::updatePacketStatsPIX();
#endif
if (options->renderDebug)
{
//renderFpsMeter(tickDuraction);
#if DEBUG_RENDER_SHOWS_PACKETS
// To show data for only one packet type
//Packet::renderPacketStats(31);
// To show data for all packet types selected as being renderable in the Packet:static_ctor call to Packet::map
Packet::renderAllPacketStats();
#else
// To show the size of the QNet queue in bytes and messages
g_NetworkManager.renderQueueMeter();
#endif
}
else
{
lastTimer = System::nanoTime();
}
PIXBeginNamedEvent(0,"Sleeping");
Sleep(0); // 4J - was Thread.yield()
PIXEndNamedEvent();
// if (Keyboard::isKeyDown(Keyboard::KEY_F7)) Display.update(); // 4J - removed condition
PIXBeginNamedEvent(0,"Display update");
Display::update();
PIXEndNamedEvent();
// checkScreenshot(); // 4J - removed
/* 4J - removed
if (parent != nullptr && !fullscreen)
{
if (parent.getWidth() != width || parent.getHeight() != height)
{
width = parent.getWidth();
height = parent.getHeight();
if (width <= 0) width = 1;
if (height <= 0) height = 1;
resize(width, height);
}
}
*/
MemSect(31);
checkGlError(L"Post render");
MemSect(0);
frames++;
//pause = !isClientSide() && screen != nullptr && screen->isPauseScreen();
//pause = g_NetworkManager.IsLocalGame() && g_NetworkManager.GetPlayerCount() == 1 && app.IsPauseMenuDisplayed(ProfileManager.GetPrimaryPad());
pause = app.IsAppPaused();
#ifndef _CONTENT_PACKAGE
while (System::nanoTime() >= lastTime + 1000000000)
{
MemSect(31);
fpsString = std::to_wstring(frames) + L" fps (" + std::to_wstring(Chunk::updates) + L" chunk updates)";
MemSect(0);
Chunk::updates = 0;
lastTime += 1000000000;
frames = 0;
}
#endif
/*
} catch (LevelConflictException e) {
this.level = null;
setLevel(null);
setScreen(new LevelConflictScreen());
} catch (OutOfMemoryError e) {
emergencySave();
setScreen(new OutOfMemoryScreen());
System.gc();
}
*/
}
/*
} catch (StopGameException e) {
} catch (Throwable e) {
emergencySave();
e.printStackTrace();
crash(new CrashReport("Unexpected error", e));
} finally {
destroy();
}
*/
}
LeaveCriticalSection(&m_setLevelCS);
}
void Minecraft::run_end()
{
destroy();
}
void Minecraft::emergencySave()
{
// 4J - lots of try/catches removed here, and garbage collector things
levelRenderer->clear();
AABB::clearPool();
Vec3::clearPool();
setLevel(nullptr);
}
void Minecraft::renderFpsMeter(int64_t tickTime)
{
int nsPer60Fps = 1000000000l / 60;
if (lastTimer == -1)
{
lastTimer = System::nanoTime();
}
int64_t now = System::nanoTime();
Minecraft::tickTimes[(Minecraft::frameTimePos) & (Minecraft::frameTimes_length - 1)] = tickTime;
Minecraft::frameTimes[(Minecraft::frameTimePos++) & (Minecraft::frameTimes_length - 1)] = now - lastTimer;
lastTimer = now;
glClear(GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_PROJECTION);
glEnable(GL_COLOR_MATERIAL);
glLoadIdentity();
glOrtho(0, static_cast<float>(width), static_cast<float>(height), 0, 1000, 3000);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0, 0, -2000);
glLineWidth(1);
glDisable(GL_TEXTURE_2D);
Tesselator *t = Tesselator::getInstance();
t->begin(GL_QUADS);
int hh1 = (int) (nsPer60Fps / 200000);
t->color(0x20000000);
t->vertex(static_cast<float>(0), static_cast<float>(height - hh1), static_cast<float>(0));
t->vertex(static_cast<float>(0), static_cast<float>(height), static_cast<float>(0));
t->vertex(static_cast<float>(Minecraft::frameTimes_length), static_cast<float>(height), static_cast<float>(0));
t->vertex(static_cast<float>(Minecraft::frameTimes_length), static_cast<float>(height - hh1), static_cast<float>(0));
t->color(0x20200000);
t->vertex(static_cast<float>(0), static_cast<float>(height - hh1 * 2), static_cast<float>(0));
t->vertex(static_cast<float>(0), static_cast<float>(height - hh1), static_cast<float>(0));
t->vertex(static_cast<float>(Minecraft::frameTimes_length), static_cast<float>(height - hh1), static_cast<float>(0));
t->vertex(static_cast<float>(Minecraft::frameTimes_length), static_cast<float>(height - hh1 * 2), static_cast<float>(0));
t->end();
int64_t totalTime = 0;
for (int i = 0; i < Minecraft::frameTimes_length; i++)
{
totalTime += Minecraft::frameTimes[i];
}
int hh = static_cast<int>(totalTime / 200000 / Minecraft::frameTimes_length);
t->begin(GL_QUADS);
t->color(0x20400000);
t->vertex(static_cast<float>(0), static_cast<float>(height - hh), static_cast<float>(0));
t->vertex(static_cast<float>(0), static_cast<float>(height), static_cast<float>(0));
t->vertex(static_cast<float>(Minecraft::frameTimes_length), static_cast<float>(height), static_cast<float>(0));
t->vertex(static_cast<float>(Minecraft::frameTimes_length), static_cast<float>(height - hh), static_cast<float>(0));
t->end();
t->begin(GL_LINES);
for (int i = 0; i < Minecraft::frameTimes_length; i++)
{
int col = ((i - Minecraft::frameTimePos) & (Minecraft::frameTimes_length - 1)) * 255 / Minecraft::frameTimes_length;
int cc = col * col / 255;
cc = cc * cc / 255;
int cc2 = cc * cc / 255;
cc2 = cc2 * cc2 / 255;
if (Minecraft::frameTimes[i] > nsPer60Fps)
{
t->color(0xff000000 + cc * 65536);
}
else
{
t->color(0xff000000 + cc * 256);
}
int64_t time = Minecraft::frameTimes[i] / 200000;
int64_t time2 = Minecraft::tickTimes[i] / 200000;
t->vertex((float)(i + 0.5f), (float)( height - time + 0.5f), static_cast<float>(0));
t->vertex((float)(i + 0.5f), (float)( height + 0.5f), static_cast<float>(0));
// if (Minecraft.frameTimes[i]>nsPer60Fps) {
t->color(0xff000000 + cc * 65536 + cc * 256 + cc * 1);
// } else {
// t.color(0xff808080 + cc/2 * 256);
// }
t->vertex((float)(i + 0.5f), (float)( height - time + 0.5f), static_cast<float>(0));
t->vertex((float)(i + 0.5f), (float)( height - (time - time2) + 0.5f), static_cast<float>(0));
}
t->end();
glEnable(GL_TEXTURE_2D);
}
void Minecraft::stop()
{
running = false;
// keepPolling = false;
}
void Minecraft::pauseGame()
{
}
void Minecraft::resize(int width, int height)
{
}
void Minecraft::verify()
{
/* 4J - TODO
new Thread() {
public void run() {
try {
HttpURLConnection huc = (HttpURLConnection) new URL("https://login.minecraft.net/session?name=" + user.name + "&session=" + user.sessionId).openConnection();
huc.connect();
if (huc.getResponseCode() == 400) {
warezTime = System.currentTimeMillis();
}
huc.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();
*/
}
void Minecraft::levelTickUpdateFunc(void* pParam)
{
Level* pLevel = static_cast<Level *>(pParam);
pLevel->tick();
}
void Minecraft::levelTickThreadInitFunc()
{
AABB::CreateNewThreadStorage();
Vec3::CreateNewThreadStorage();
IntCache::CreateNewThreadStorage();
Compression::UseDefaultThreadStorage();
}
// 4J - added bFirst parameter, which is true for the first active viewport in splitscreen
// 4J - added bUpdateTextures, which is true if the actual renderer textures are to be updated - this will be true for the last time this tick runs with bFirst true
void Minecraft::tick(bool bFirst, bool bUpdateTextures)
{
int iPad=player->GetXboxPad();
//OutputDebugString("Minecraft::tick\n");
//4J-PB - only tick this player's stats
stats[iPad]->tick(iPad);
// Tick the opacity timer (to display the interface at default opacity for a certain time if the user has been navigating it)
app.TickOpacityTimer(iPad);
// 4J added
if( bFirst ) levelRenderer->destroyedTileManager->tick();
//gui->tick();
//gameRenderer->pick(1);
#if 0
// 4J - removed - we don't use ChunkCache anymore
if (player != nullptr)
{
ChunkSource *cs = level->getChunkSource();
if (dynamic_cast<ChunkCache *>(cs) != nullptr)
{
ChunkCache *spcc = (ChunkCache *)cs;
// 4J - there was also Mth::floors on these ints but that seems superfluous
int xt = ((int) player->x) >> 4;
int zt = ((int) player->z) >> 4;
spcc->centerOn(xt, zt);
}
}
#endif
// soundEngine.playMusicTick();
if (!pause && level != nullptr) gameMode->tick();
MemSect(31);
glBindTexture(GL_TEXTURE_2D, textures->loadTexture(TN_TERRAIN)); //L"/terrain.png"));
MemSect(0);
if( bFirst )
{
PIXBeginNamedEvent(0,"Texture tick");
if (!pause) textures->tick(bUpdateTextures);
PIXEndNamedEvent();
}
/*
* if (serverConnection != null && !(screen instanceof ErrorScreen)) {
* if (!serverConnection.isConnected()) {
* progressRenderer.progressStart("Connecting..");
* progressRenderer.progressStagePercentage(0); } else {
* serverConnection.tick(); serverConnection.sendPosition(player); } }
*/
}
void Minecraft::reloadSound()
{
// System.out.println("FORCING RELOAD!"); // 4J - removed
soundEngine = new SoundEngine();
soundEngine->init(options);
bgLoader->forceReload();
}
bool Minecraft::isClientSide()
{
return level != nullptr && level->isClientSide;
}
void Minecraft::selectLevel(ConsoleSaveFile *saveFile, const wstring& levelId, const wstring& levelName, LevelSettings *levelSettings)
{
}
bool Minecraft::saveSlot(int slot, const wstring& name)
{
return false;
}
bool Minecraft::loadSlot(const wstring& userName, int slot)
{
return false;
}
void Minecraft::releaseLevel(int message)
{
//this->level = nullptr;
setLevel(nullptr, message);
}
// 4J Stu - This code was within setLevel, but I moved it out so that I can call it at a better
// time when exiting from an online game
void Minecraft::forceStatsSave(int idx)
{
//4J Gordon: Force a stats save
stats[idx]->save(idx, true);
//4J Gordon: If the player is signed in, save the leaderboards
if( ProfileManager.IsSignedInLive(idx) )
{
int tempLockedProfile = ProfileManager.GetLockedProfile();
ProfileManager.SetLockedProfile(idx);
stats[idx]->saveLeaderboards();
ProfileManager.SetLockedProfile(tempLockedProfile);
}
}
// 4J Added
MultiPlayerLevel *Minecraft::getLevel(int dimension)
{
if (dimension == -1) return levels[1];
else if(dimension == 1) return levels[2];
else return levels[0];
}
// 4J Stu - Removed as redundant with default values in params.
//void Minecraft::setLevel(Level *level, bool doForceStatsSave /*= true*/)
//{
// setLevel(level, -1, nullptr, doForceStatsSave);
//}
// Also causing ambiguous call for some reason
// as it is matching shared_ptr<Player> from the func below with bool from this one
//void Minecraft::setLevel(Level *level, const wstring& message, bool doForceStatsSave /*= true*/)
//{
// setLevel(level, message, nullptr, doForceStatsSave);
//}
void Minecraft::forceaddLevel(MultiPlayerLevel *level)
{
int dimId = level->dimension->id;
if (dimId == -1) levels[1] = level;
else if(dimId == 1) levels[2] = level;
else levels[0] = level;
}
void Minecraft::setLevel(MultiPlayerLevel *level, int message /*=-1*/, shared_ptr<Player> forceInsertPlayer /*=nullptr*/, bool doForceStatsSave /*=true*/, bool bPrimaryPlayerSignedOut /*=false*/)
{
EnterCriticalSection(&m_setLevelCS);
bool playerAdded = false;
this->cameraTargetPlayer = nullptr;
// 4J-PB - since we now play music in the menu, just let it keep playing
//soundEngine->playStreaming(L"", 0, 0, 0, 0, 0);
// 4J - stop update thread from processing this level, which blocks until it is safe to move on - will be re-enabled if we set the level to be non-nullptr
gameRenderer->DisableUpdateThread();
for(unsigned int i = 0; i < levels.length; ++i)
{
// 4J We only need to save out in multiplayer is we are setting the level to nullptr
// If we ever go back to making single player only then this will not work properly!
if (levels[i] != nullptr && level == nullptr)
{
// 4J Stu - This is really only relevant for single player (ie not what we do at the moment)
if((doForceStatsSave==true) && player!=nullptr)
forceStatsSave(player->GetXboxPad() );
// 4J Stu - Added these for the case when we exit a level so we are setting the level to nullptr
// The level renderer needs to have it's stored level set to nullptr so that it doesn't break next time we set one
if (levelRenderer != nullptr)
{
for(DWORD p = 0; p < XUSER_MAX_COUNT; ++p)
{
levelRenderer->setLevel(p, nullptr);
}
}
if (particleEngine != nullptr) particleEngine->setLevel(nullptr);
}
}
// 4J If we are setting the level to nullptr then we are exiting, so delete the levels
if( level == nullptr )
{
if(levels[0]!=nullptr)
{
delete levels[0];
levels[0] = nullptr;
// Both level share the same savedDataStorage
if(levels[1]!=nullptr) levels[1]->savedDataStorage = nullptr;
}
if(levels[1]!=nullptr)
{
delete levels[1];
levels[1] = nullptr;
}
if(levels[2]!=nullptr)
{
delete levels[2];
levels[2] = nullptr;
}
// Delete all the player objects
for(unsigned int idx = 0; idx < XUSER_MAX_COUNT; ++idx)
{
shared_ptr<MultiplayerLocalPlayer> mplp = localplayers[idx];
if(mplp != nullptr && mplp->connection != nullptr )
{
delete mplp->connection;
mplp->connection = nullptr;
}
if( localgameModes[idx] != nullptr )
{
delete localgameModes[idx];
localgameModes[idx] = nullptr;
}
if( m_pendingLocalConnections[idx] != nullptr )
{
delete m_pendingLocalConnections[idx];
m_pendingLocalConnections[idx] = nullptr;
}
localplayers[idx] = nullptr;
}
// If we are removing the primary player then there can't be a valid gamemode left anymore, this
// pointer will be referring to the one we've just deleted
gameMode = nullptr;
// Remove references to player
player = nullptr;
cameraTargetPlayer = nullptr;
}
this->level = level;
if (level != nullptr)
{
int dimId = level->dimension->id;
if (dimId == -1) levels[1] = level;
else if(dimId == 1) levels[2] = level;
else levels[0] = level;
// If no player has been set, then this is the first level to be set this game, so set up
// a primary player & initialise some other things
if (player == nullptr)
{
int iPrimaryPlayer = ProfileManager.GetPrimaryPad();
player = gameMode->createPlayer(level);
PlayerUID playerXUIDOffline = INVALID_XUID;
PlayerUID playerXUIDOnline = INVALID_XUID;
ProfileManager.GetXUID(iPrimaryPlayer,&playerXUIDOffline,false);
ProfileManager.GetXUID(iPrimaryPlayer,&playerXUIDOnline,true);
#ifdef __PSVITA__
if(CGameNetworkManager::usingAdhocMode() && playerXUIDOnline.getOnlineID()[0] == 0)
{
// player doesn't have an online UID, set it from the player name
playerXUIDOnline.setForAdhoc();
}
#endif
#ifdef _WINDOWS64
// On Windows, the implementation has been changed to use a per-client pseudo XUID based on `uid.dat`.
// To maintain player data compatibility with existing worlds, the world host (the first player) will use the previous embedded pseudo XUID.
INetworkPlayer *localNetworkPlayer = g_NetworkManager.GetLocalPlayerByUserIndex(iPrimaryPlayer);
if(localNetworkPlayer != nullptr && localNetworkPlayer->IsHost())
{
playerXUIDOffline = Win64Xuid::GetLegacyEmbeddedHostXuid();
}
else
{
playerXUIDOffline = Win64Xuid::ResolvePersistentXuid();
}
#endif
player->setXuid(playerXUIDOffline);
player->setOnlineXuid(playerXUIDOnline);
player->m_displayName = ProfileManager.GetDisplayName(iPrimaryPlayer);
player->resetPos();
gameMode->initPlayer(player);
player->SetXboxPad(iPrimaryPlayer);
for(int i=0;i<XUSER_MAX_COUNT;i++)
{
m_pendingLocalConnections[i] = nullptr;
if( i != iPrimaryPlayer ) localgameModes[i] = nullptr;
}
}
if (player != nullptr)
{
player->resetPos();
// gameMode.initPlayer(player);
if (level != nullptr)
{
level->addEntity(player);
playerAdded = true;
}
}
if(player->input != nullptr) delete player->input;
player->input = new Input();
if (levelRenderer != nullptr) levelRenderer->setLevel(player->GetXboxPad(), level);
if (particleEngine != nullptr) particleEngine->setLevel(level);
#if 0
// 4J - removed - we don't use ChunkCache anymore
ChunkSource *cs = level->getChunkSource();
if (dynamic_cast<ChunkCache *>(cs) != nullptr)
{
ChunkCache *spcc = (ChunkCache *)cs;
// 4J - these had a Mth::floor which seems unrequired
int xt = ((int) player->x) >> 4;
int zt = ((int) player->z) >> 4;
spcc->centerOn(xt, zt);
}
#endif
gameMode->adjustPlayer(player);
for(int i=0;i<XUSER_MAX_COUNT;i++)
{
m_pendingLocalConnections[i] = nullptr;
}
updatePlayerViewportAssignments();
this->cameraTargetPlayer = player;
// 4J - allow update thread to start processing the level now both it & the player should be ok
gameRenderer->EnableUpdateThread();
}
else
{
levelSource->clearAll();
player = nullptr;
// Clear all players if the new level is nullptr
for(int i=0;i<XUSER_MAX_COUNT;i++)
{
if( m_pendingLocalConnections[i] != nullptr ) m_pendingLocalConnections[i]->close();
m_pendingLocalConnections[i] = nullptr;
localplayers[i] = nullptr;
localgameModes[i] = nullptr;
}
}
// System.gc(); // 4J - removed
// 4J removed
//this->lastTickTime = 0;
LeaveCriticalSection(&m_setLevelCS);
}
void Minecraft::prepareLevel(int title)
{
int r = 128;
if (gameMode->isCutScene()) r = 64;
int pp = 0;
int max = r * 2 / 16 + 1;
max = max * max;
ChunkSource *cs = level->getChunkSource();
Pos *spawnPos = level->getSharedSpawnPos();
if (player != nullptr)
{
spawnPos->x = static_cast<int>(player->x);
spawnPos->z = static_cast<int>(player->z);
}
#if 0
// 4J - removed - we don't use ChunkCache anymore
if (dynamic_cast<ChunkCache *>(cs)!=nullptr)
{
ChunkCache *spcc = (ChunkCache *) cs;
spcc->centerOn(spawnPos->x >> 4, spawnPos->z >> 4);
}
#endif
delete spawnPos;
}
wstring Minecraft::gatherStats1()
{
//return levelRenderer->gatherStats1();
return L"Time to autosave: " + std::to_wstring( app.SecondsToAutosave() ) + L"s";
}
wstring Minecraft::gatherStats2()
{
return g_NetworkManager.GatherStats();
//return levelRenderer->gatherStats2();
}
wstring Minecraft::gatherStats3()
{
return g_NetworkManager.GatherRTTStats();
//return L"P: " + particleEngine->countParticles() + L". T: " + level->gatherStats();
}
wstring Minecraft::gatherStats4()
{
return level->gatherChunkSourceStats();
}
void Minecraft::start(const wstring& name, const wstring& sid)
{
startAndConnectTo(name, sid, L"");
}
void Minecraft::startAndConnectTo(const wstring& name, const wstring& sid, const wstring& url)
{
const bool fullScreen = false;
const wstring userName = name;
/* 4J - removed window handling things here
final Frame frame = new Frame("Minecraft");
Canvas canvas = new Canvas();
frame.setLayout(new BorderLayout());
frame.add(canvas, BorderLayout.CENTER);
// OverlayLayout oll = new OverlayLayout(frame);
// oll.addLayoutComponent(canvas, BorderLayout.CENTER);
// oll.addLayoutComponent(new JLabel("TEST"), BorderLayout.EAST);
canvas.setPreferredSize(new Dimension(854, 480));
frame.pack();
frame.setLocationRelativeTo(null);
*/
Minecraft *minecraft;
// 4J - was new Minecraft(frame, canvas, NULL, 854, 480, fullScreen);
// Logical width is proportional to the real screen aspect ratio so that
// the ortho projection and HUD layout match the viewport without stretching.
constexpr int logicalH = 0;
const int logicalW = 0;
minecraft = new Minecraft(nullptr, nullptr, nullptr, logicalW, logicalH, fullScreen);
/* - 4J - removed
{
@Override
public void onCrash(CrashReport crashReport) {
frame.removeAll();
frame.add(new CrashInfoPanel(crashReport), BorderLayout.CENTER);
frame.validate();
}
}; */
/* 4J - removed
final Thread thread = new Thread(minecraft, "Minecraft main thread");
thread.setPriority(Thread.MAX_PRIORITY);
*/
minecraft->serverDomain = L"www.minecraft.net";
// 4J Stu - We never want the player to be DemoUser, we always want them to have their gamertag displayed
//if (ProfileManager.IsFullVersion())
{
if (userName != L"" && sid != L"") // 4J - username & side were compared with nullptr rather than empty strings
{
minecraft->user = new User(userName, sid);
}
else
{
minecraft->user = new User(L"Player" + std::to_wstring(System::currentTimeMillis() % 1000), L"");
}
}
//else
//{
// minecraft->user = new DemoUser();
//}
/* 4J - TODO
if (url != nullptr)
{
String[] tokens = url.split(":");
minecraft.connectTo(tokens[0], Integer.parseInt(tokens[1]));
}
*/
/* 4J - removed
frame.setVisible(true);
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent arg0) {
minecraft.stop();
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.exit(0);
}
});
*/
// 4J - TODO - consider whether we need to actually create a thread here
minecraft->run();
}
ClientConnection *Minecraft::getConnection(int iPad)
{
return localplayers[iPad]->connection;
}
// 4J-PB - so we can access this from within our xbox game loop
Minecraft *Minecraft::GetInstance()
{
return m_instance;
}
bool useLomp = false;
int g_iMainThreadId;
void Minecraft::main()
{
wstring name;
wstring sessionId;
//g_iMainThreadId = GetCurrentThreadId();
useLomp = true;
MinecraftWorld_RunStaticCtors();
//EntityRenderDispatcher::staticCtor();
//TileEntityRenderDispatcher::staticCtor();
User::staticCtor();
Tutorial::staticCtor();
ColourTable::staticCtor();
app.loadDefaultGameRules();
#ifdef _LARGE_WORLDS
//LevelRenderer::staticCtor();
#endif
// 4J Stu - This block generates XML for the game rules schema
#if 0
for(unsigned int i = 0; i < Item::items.length; ++i)
{
if(Item::items[i] != nullptr)
{
app.DebugPrintf("<xs:enumeration value=\"%d\"><xs:annotation><xs:documentation>%ls</xs:documentation></xs:annotation></xs:enumeration>\n", i, app.GetString( Item::items[i]->getDescriptionId() ));
}
}
app.DebugPrintf("\n\n\n\n\n");
for(unsigned int i = 0; i < 256; ++i)
{
if(Tile::tiles[i] != nullptr)
{
app.DebugPrintf("<xs:enumeration value=\"%d\"><xs:annotation><xs:documentation>%ls</xs:documentation></xs:annotation></xs:enumeration>\n", i, app.GetString( Tile::tiles[i]->getDescriptionId() ));
}
}
__debugbreak();
#endif
// 4J-PB - Can't call this for the first 5 seconds of a game - MS rule
//if (ProfileManager.IsFullVersion())
{
name = L"Player" + std::to_wstring(System::currentTimeMillis() % 1000);
sessionId = L"-";
/* 4J - TODO - get a session ID from somewhere?
if (args.length > 0) name = args[0];
sessionId = "-";
if (args.length > 1) sessionId = args[1];
*/
}
// On PS4, we call Minecraft::Start from another thread, as this has been timed taking ~2.5 seconds and we need to do some basic
// rendering stuff so that we don't break the TRCs on SubmitDone calls
#ifndef __ORBIS__
Minecraft::start(name, sessionId);
#endif
}
bool Minecraft::renderNames()
{
if (m_instance == nullptr || !m_instance->options->hideGui)
{
return true;
}
return false;
}
bool Minecraft::useFancyGraphics()
{
return (m_instance != nullptr && m_instance->options->fancyGraphics);
}
bool Minecraft::useAmbientOcclusion()
{
return (m_instance != nullptr && m_instance->options->ambientOcclusion != Options::AO_OFF);
}
bool Minecraft::renderDebug()
{
return (m_instance != nullptr && m_instance->options->renderDebug);
}
bool Minecraft::handleClientSideCommand(const wstring& chatMessage)
{
return false;
}
int Minecraft::maxSupportedTextureSize()
{
// 4J Force value
return 1024;
//for (int texSize = 16384; texSize > 0; texSize >>= 1) {
// GL11.glTexImage2D(GL11.GL_PROXY_TEXTURE_2D, 0, GL11.GL_RGBA, texSize, texSize, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, (ByteBuffer) null);
// final int width = GL11.glGetTexLevelParameteri(GL11.GL_PROXY_TEXTURE_2D, 0, GL11.GL_TEXTURE_WIDTH);
// if (width != 0) {
// return texSize;
// }
//}
//return -1;
}
void Minecraft::delayTextureReload()
{
reloadTextures = true;
}
int64_t Minecraft::currentTimeMillis()
{
return System::currentTimeMillis();//(Sys.getTime() * 1000) / Sys.getTimerResolution();
}
/*void Minecraft::handleMouseDown(int button, bool down)
{
if (gameMode->instaBuild) return;
if (!down) missTime = 0;
if (button == 0 && missTime > 0) return;
if (down && hitResult != nullptr && hitResult->type == HitResult::TILE && button == 0)
{
int x = hitResult->x;
int y = hitResult->y;
int z = hitResult->z;
gameMode->continueDestroyBlock(x, y, z, hitResult->f);
particleEngine->crack(x, y, z, hitResult->f);
}
else
{
gameMode->stopDestroyBlock();
}
}
void Minecraft::handleMouseClick(int button)
{
if (button == 0 && missTime > 0) return;
if (button == 0)
{
app.DebugPrintf("handleMouseClick - Player %d is swinging\n",player->GetXboxPad());
player->swing();
}
bool mayUse = true;
// * if (button == 1) { ItemInstance item =
// * player.inventory.getSelected(); if (item != null) { if
// * (gameMode.useItem(player, item)) {
// * gameRenderer.itemInHandRenderer.itemUsed(); return; } } }
// 4J-PB - Adding a special case in here for sleeping in a bed in a multiplayer game - we need to wake up, and we don't have the inbedchatscreen with a button
if(button==1 && (player->isSleeping() && level != nullptr && level->isClientSide))
{
shared_ptr<MultiplayerLocalPlayer> mplp = dynamic_pointer_cast<MultiplayerLocalPlayer>( player );
if(mplp) mplp->StopSleeping();
// 4J - TODO
//if (minecraft.player instanceof MultiplayerLocalPlayer)
//{
// ClientConnection connection = ((MultiplayerLocalPlayer) minecraft.player).connection;
// connection.send(new PlayerCommandPacket(minecraft.player, PlayerCommandPacket.STOP_SLEEPING));
//}
}
if (hitResult == nullptr)
{
if (button == 0 && !(dynamic_cast<CreativeMode *>(gameMode) != nullptr)) missTime = 10;
}
else if (hitResult->type == HitResult::ENTITY)
{
if (button == 0)
{
gameMode->attack(player, hitResult->entity);
}
if (button == 1)
{
gameMode->interact(player, hitResult->entity);
}
}
else if (hitResult->type == HitResult::TILE)
{
int x = hitResult->x;
int y = hitResult->y;
int z = hitResult->z;
int face = hitResult->f;
// * if (button != 0) { if (hitResult.f == 0) y--; if (hitResult.f ==
// * 1) y++; if (hitResult.f == 2) z--; if (hitResult.f == 3) z++; if
// * (hitResult.f == 4) x--; if (hitResult.f == 5) x++; }
// if (isClientSide())
// {
// return;
// }
if (button == 0)
{
gameMode->startDestroyBlock(x, y, z, hitResult->f);
}
else
{
shared_ptr<ItemInstance> item = player->inventory->getSelected();
int oldCount = item != nullptr ? item->count : 0;
if (gameMode->useItemOn(player, level, item, x, y, z, face))
{
mayUse = false;
app.DebugPrintf("Player %d is swinging\n",player->GetXboxPad());
player->swing();
}
if (item == nullptr)
{
return;
}
if (item->count == 0)
{
player->inventory->items[player->inventory->selected] = nullptr;
}
else if (item->count != oldCount)
{
gameRenderer->itemInHandRenderer->itemPlaced();
}
}
}
if (mayUse && button == 1)
{
shared_ptr<ItemInstance> item = player->inventory->getSelected();
if (item != nullptr)
{
if (gameMode->useItem(player, level, item))
{
gameRenderer->itemInHandRenderer->itemUsed();
}
}
}
}
*/
bool Minecraft::isTutorial()
{
return m_inFullTutorialBits > 0;
/*if( gameMode != nullptr && gameMode->isTutorial() )
{
return true;
}
else
{
return false;
}*/
}
void Minecraft::playerStartedTutorial(int iPad)
{
// If the app doesn't think we are in a tutorial mode then just ignore this add
if( app.GetTutorialMode() ) m_inFullTutorialBits = m_inFullTutorialBits | ( 1 << iPad );
}
void Minecraft::playerLeftTutorial(int iPad)
{
// 4J Stu - Fix for bug that was flooding Sentient with LevelStart events
// If the tutorial bits are already 0 then don't need to update anything
if(m_inFullTutorialBits == 0)
{
app.SetTutorialMode( false );
return;
}
m_inFullTutorialBits = m_inFullTutorialBits & ~( 1 << iPad );
if(m_inFullTutorialBits == 0)
{
app.SetTutorialMode( false );
// 4J Stu -This telemetry event means something different on XboxOne, so we don't call it for simple state changes like this
#ifndef _XBOX_ONE
for(DWORD idx = 0; idx < XUSER_MAX_COUNT; ++idx)
{
if(localplayers[idx] != nullptr)
{
TelemetryManager->RecordLevelStart(idx, eSen_FriendOrMatch_Playing_With_Invited_Friends, eSen_CompeteOrCoop_Coop_and_Competitive, level->difficulty, app.GetLocalPlayerCount(), g_NetworkManager.GetOnlinePlayerCount());
}
}
#endif
}
}
#ifdef _DURANGO
void Minecraft::inGameSignInCheckAllPrivilegesCallback(LPVOID lpParam, bool hasPrivileges, int iPad)
{
Minecraft* pClass = (Minecraft*)lpParam;
if(!hasPrivileges)
{
ProfileManager.RemoveGamepadFromGame(iPad);
}
else
{
if( !g_NetworkManager.SessionHasSpace() )
{
UINT uiIDA[1];
uiIDA[0]=IDS_OK;
ui.RequestErrorMessage(IDS_MULTIPLAYER_FULL_TITLE, IDS_MULTIPLAYER_FULL_TEXT, uiIDA, 1);
ProfileManager.RemoveGamepadFromGame(iPad);
}
else if( ProfileManager.IsSignedInLive(iPad) && ProfileManager.AllowedToPlayMultiplayer(iPad) )
{
// create the local player for the iPad
shared_ptr<Player> player = pClass->localplayers[iPad];
if( player == nullptr)
{
if( pClass->level->isClientSide )
{
pClass->addLocalPlayer(iPad);
}
else
{
// create the local player for the iPad
shared_ptr<Player> player = pClass->localplayers[iPad];
if( player == nullptr)
{
player = pClass->createExtraLocalPlayer(iPad, (convStringToWstring( ProfileManager.GetGamertag(iPad) )).c_str(), iPad, pClass->level->dimension->id);
}
}
}
}
}
}
#endif
#ifdef _XBOX_ONE
int Minecraft::InGame_SignInReturned(void *pParam,bool bContinue, int iPad, int iController)
#else
int Minecraft::InGame_SignInReturned(void *pParam,bool bContinue, int iPad)
#endif
{
Minecraft* pMinecraftClass = static_cast<Minecraft *>(pParam);
if(g_NetworkManager.IsInSession())
{
// 4J Stu - There seems to be a bug in the signin ui call that enables guest sign in. We never allow this within game, so make sure that it's disabled
// Fix for #66516 - TCR #124: MPS Guest Support ; #001: BAS Game Stability: TU8: The game crashes when second Guest signs-in on console which takes part in Xbox LIVE multiplayer session.
app.DebugPrintf("Disabling Guest Signin\n");
XEnableGuestSignin(FALSE);
}
// If sign in succeded, we're in game and this player isn't already playing, continue
if(bContinue==true && g_NetworkManager.IsInSession() && pMinecraftClass->localplayers[iPad] == nullptr)
{
// It's possible that the player has not signed in - they can back out or choose no for the converttoguest
if(ProfileManager.IsSignedIn(iPad))
{
#ifdef _DURANGO
if(!g_NetworkManager.IsLocalGame() && ProfileManager.IsSignedInLive(iPad) && ProfileManager.AllowedToPlayMultiplayer(iPad))
{
ProfileManager.CheckMultiplayerPrivileges(iPad, true, &inGameSignInCheckAllPrivilegesCallback, pMinecraftClass);
}
else
#endif
if( !g_NetworkManager.SessionHasSpace() )
{
UINT uiIDA[1];
uiIDA[0]=IDS_OK;
#ifdef _DURANGO
ProfileManager.RemoveGamepadFromGame(iPad);
#endif
}
// if this is a local game then profiles just need to be signed in
else if( g_NetworkManager.IsLocalGame() || (ProfileManager.IsSignedInLive(iPad) && ProfileManager.AllowedToPlayMultiplayer(iPad)) )
{
#ifdef __ORBIS__
bool contentRestricted = false;
ProfileManager.GetChatAndContentRestrictions(iPad,false,nullptr,&contentRestricted,nullptr); // TODO!
if (!g_NetworkManager.IsLocalGame() && contentRestricted)
{
ui.RequestContentRestrictedMessageBox(IDS_NO_MULTIPLAYER_PRIVILEGE_TITLE, IDS_CONTENT_RESTRICTION, iPad);
}
else if(!g_NetworkManager.IsLocalGame() && !ProfileManager.HasPlayStationPlus(iPad))
{
pMinecraftClass->m_pPsPlusUpsell = new PsPlusUpsellWrapper(iPad);
pMinecraftClass->m_pPsPlusUpsell->displayUpsell();
}
else
#endif
if( pMinecraftClass->level->isClientSide )
{
pMinecraftClass->addLocalPlayer(iPad);
}
else
{
// create the local player for the iPad
shared_ptr<Player> player = pMinecraftClass->localplayers[iPad];
if( player == nullptr)
{
player = pMinecraftClass->createExtraLocalPlayer(iPad, (convStringToWstring( ProfileManager.GetGamertag(iPad) )).c_str(), iPad, pMinecraftClass->level->dimension->id);
}
}
}
else if( ProfileManager.IsSignedInLive(ProfileManager.GetPrimaryPad()) && !ProfileManager.AllowedToPlayMultiplayer(iPad) )
{
// 4J Stu - Don't allow converting to guests as we don't allow any guest sign-in while in the game
// Fix for #66516 - TCR #124: MPS Guest Support ; #001: BAS Game Stability: TU8: The game crashes when second Guest signs-in on console which takes part in Xbox LIVE multiplayer session.
//ProfileManager.RequestConvertOfflineToGuestUI( &Minecraft::InGame_SignInReturned, pMinecraftClass,iPad);
UINT uiIDA[1];
uiIDA[0]=IDS_CONFIRM_OK;
#ifdef _DURANGO
ProfileManager.RemoveGamepadFromGame(iPad);
#endif
}
}
}
return 0;
}
void Minecraft::tickAllConnections()
{
int oldIdx = getLocalPlayerIdx();
for(unsigned int i = 0; i < XUSER_MAX_COUNT; i++ )
{
shared_ptr<MultiplayerLocalPlayer> mplp = localplayers[i];
if( mplp && mplp->connection)
{
setLocalPlayerIdx(i);
mplp->connection->tick();
}
}
setLocalPlayerIdx(oldIdx);
}
bool Minecraft::addPendingClientTextureRequest(const wstring &textureName)
{
auto it = find(m_pendingTextureRequests.begin(), m_pendingTextureRequests.end(), textureName);
if( it == m_pendingTextureRequests.end() )
{
m_pendingTextureRequests.push_back(textureName);
return true;
}
return false;
}
void Minecraft::handleClientTextureReceived(const wstring &textureName)
{
auto it = find(m_pendingTextureRequests.begin(), m_pendingTextureRequests.end(), textureName);
if( it != m_pendingTextureRequests.end() )
{
m_pendingTextureRequests.erase(it);
}
}
unsigned int Minecraft::getCurrentTexturePackId()
{
return skins->getSelected()->getId();
}
ColourTable *Minecraft::getColourTable()
{
TexturePack *selected = skins->getSelected();
ColourTable *colours = selected->getColourTable();
if(colours == nullptr)
{
colours = skins->getDefault()->getColourTable();
}
return colours;
}
#if defined __ORBIS__
int Minecraft::MustSignInReturnedPSN(void *pParam, int iPad, C4JStorage::EMessageResult result)
{
Minecraft* pMinecraft = (Minecraft *)pParam;
if(result == C4JStorage::EMessage_ResultAccept)
{
SQRNetworkManager_Orbis::AttemptPSNSignIn(&Minecraft::InGame_SignInReturned, pMinecraft, false, iPad);
}
return 0;
}
#endif