mirror of
https://github.com/celisej567/mcpe.git
synced 2026-09-04 19:36:43 +03:00
* WIP Android Port Android port. Still needs touch controls and mouse turning (if that's even possible on android) and file saving and SoundSystemSL You control the camera and movement with your controller for now. You can navigate the gui using touch. Options.cpp,LocalPlayer.cpp,Minecraft.cpp is configured to use controller. Blocked out some code in ControllerTurnInput.cpp,Controller.cpp that didn't make sense. * Fix glClear glClear is supossed to use GL_DEPTH_BUFFER_BIT (thx TheBrokenRail) * * Fix build. * * Ignore assets. * * More stuff * * Fix more build errors. * * It finally built What I needed to do is rebuild the debug keystore because apparently android studio created it with sha1 digest alg which isn't supported by ant * * Clean up filters. * * Add cramped mode to the pause screen. * * Fix a bug with the hotbar * * In NinecraftApp::handleBack, pause the game if there is no screen. * * AppPlatform_android: Add placeholder SoundSystem instance till we get SoundSystemSL working * * Add properly working touch code. * * Oh, remove some testing things * * Fix state resetting when going in background and back in foreground * Fix bug where the sky isn't being regenerated on graphics reset * Fix bug where the m_currBoundTex isn't reset in Textures::clear potentially leaving a texture with that ID unassigned and corrupted * Fix bug in CThread where the thread is detached and then also joined. * Don't log anything if the program isn't in debug mode. * * Add virtual keyboard support. The screen instance slides so that the focused text box is kept visible. * Rename from com.minecraftcpp to com.reminecraftpe --------- Co-authored-by: iProgramInCpp <iprogramincpp@gmail.com>
1211 lines
26 KiB
C++
1211 lines
26 KiB
C++
/********************************************************************
|
|
Minecraft: Pocket Edition - Decompilation Project
|
|
Copyright (C) 2023 iProgramInCpp
|
|
|
|
The following code is licensed under the BSD 1 clause license.
|
|
SPDX-License-Identifier: BSD-1-Clause
|
|
********************************************************************/
|
|
|
|
#include "client/app/Minecraft.hpp"
|
|
#include "client/gui/screens/PauseScreen.hpp"
|
|
#include "client/gui/screens/StartMenuScreen.hpp"
|
|
#include "client/gui/screens/RenameMPLevelScreen.hpp"
|
|
#include "client/gui/screens/SavingWorldScreen.hpp"
|
|
#include "client/gui/screens/DeathScreen.hpp"
|
|
#include "network/ServerSideNetworkHandler.hpp"
|
|
#include "client/network/ClientSideNetworkHandler.hpp"
|
|
|
|
#include "world/gamemode/SurvivalMode.hpp"
|
|
#include "world/gamemode/CreativeMode.hpp"
|
|
|
|
#include "client/player/input/ControllerTurnInput.hpp"
|
|
#include "client/player/input/MouseTurnInput.hpp"
|
|
#include "client/player/input/KeyboardInput.hpp"
|
|
#include "client/player/input/IBuildInput.hpp"
|
|
#include "client/player/input/CustomInputHolder.hpp"
|
|
#include "client/player/input/TouchInputHolder.hpp"
|
|
#include "client/player/input/Multitouch.hpp"
|
|
|
|
#include "world/tile/SandTile.hpp"
|
|
|
|
// custom:
|
|
#include "client/renderer/PatchManager.hpp"
|
|
|
|
int Minecraft::width = C_DEFAULT_SCREEN_WIDTH;
|
|
int Minecraft::height = C_DEFAULT_SCREEN_HEIGHT;
|
|
float Minecraft::guiScaleMultiplier = 1.0f;
|
|
bool Minecraft::useAmbientOcclusion = false;
|
|
int Minecraft::customDebugId = 0;
|
|
|
|
//@HUH: For the demo, this is defined as TRUE.
|
|
//@HUH: deadmau5 had camera cheats? That's interesting.
|
|
const bool Minecraft::DEADMAU5_CAMERA_CHEATS = true;
|
|
|
|
const char* Minecraft::progressMessages[] =
|
|
{
|
|
"Locating server",
|
|
"Building terrain",
|
|
"Preparing",
|
|
"Saving chunks",
|
|
};
|
|
|
|
Minecraft::Minecraft() :
|
|
m_gui(this)
|
|
{
|
|
m_options = nullptr;
|
|
field_18 = false;
|
|
field_288 = false;
|
|
m_pLevelRenderer = nullptr;
|
|
m_pGameRenderer = nullptr;
|
|
m_pParticleEngine = nullptr;
|
|
m_pSoundEngine = nullptr;
|
|
m_pGameMode = nullptr;
|
|
m_pTextures = nullptr;
|
|
m_pFont = nullptr;
|
|
m_pRakNetInstance = nullptr;
|
|
m_pNetEventCallback = nullptr;
|
|
field_2B0 = 0;
|
|
m_pUser = nullptr;
|
|
m_pLevel = nullptr;
|
|
m_pLocalPlayer = nullptr;
|
|
m_pMobPersp = nullptr; // why is there a duplicate?
|
|
field_D0C = 0;
|
|
m_pPrepThread = nullptr;
|
|
m_pScreen = nullptr;
|
|
field_D18 = 10;
|
|
m_pInputHolder = nullptr;
|
|
m_bGrabbedMouse = true;
|
|
m_progressPercent = 0;
|
|
m_bPreparingLevel = false;
|
|
m_pLevelStorageSource = nullptr; // TODO
|
|
field_D9C = 0;
|
|
field_DA0 = 0;
|
|
field_DA4 = 0;
|
|
field_DA8 = 0;
|
|
field_DAC = 0;
|
|
m_bUsingScreen = false;
|
|
m_bHasQueuedScreen = false;
|
|
m_pQueuedScreen = nullptr;
|
|
m_licenseID = -2;
|
|
m_fLastUpdated = 0;
|
|
m_fDeltaTime = 0;
|
|
|
|
m_Logger = new Logger();
|
|
}
|
|
|
|
int Minecraft::getLicenseId()
|
|
{
|
|
if (m_licenseID < 0)
|
|
m_licenseID = m_pPlatform->checkLicense();
|
|
|
|
return m_licenseID;
|
|
}
|
|
|
|
void Minecraft::releaseMouse()
|
|
{
|
|
if (!m_bGrabbedMouse)
|
|
return;
|
|
|
|
if (m_pLocalPlayer)
|
|
m_pLocalPlayer->m_pMoveInput->releaseAllKeys();
|
|
|
|
m_bGrabbedMouse = false;
|
|
m_mouseHandler.release();
|
|
|
|
// Note, normally the platform stuff would be located within
|
|
// the mouse handler, but we don't have access to the platform
|
|
// from there!
|
|
platform()->setMouseGrabbed(false);
|
|
}
|
|
|
|
void Minecraft::grabMouse()
|
|
{
|
|
if (m_bGrabbedMouse)
|
|
return;
|
|
|
|
m_bGrabbedMouse = true;
|
|
m_mouseHandler.grab();
|
|
|
|
setScreen(nullptr);
|
|
|
|
platform()->setMouseGrabbed(!isTouchscreen());
|
|
}
|
|
|
|
void Minecraft::setScreen(Screen* pScreen)
|
|
{
|
|
#ifndef ORIGINAL_CODE
|
|
if (pScreen == nullptr && !isLevelGenerated())
|
|
{
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
if (m_bUsingScreen)
|
|
{
|
|
m_bHasQueuedScreen = true;
|
|
m_pQueuedScreen = pScreen;
|
|
return;
|
|
}
|
|
|
|
if (pScreen && pScreen->isErrorScreen())
|
|
{
|
|
// not in original
|
|
delete pScreen;
|
|
return;
|
|
}
|
|
|
|
if (m_pScreen)
|
|
{
|
|
m_pScreen->removed();
|
|
delete m_pScreen;
|
|
}
|
|
|
|
m_pScreen = pScreen;
|
|
if (pScreen)
|
|
{
|
|
releaseMouse();
|
|
pScreen->init(this, int(width * Gui::InvGuiScale), int(height * Gui::InvGuiScale));
|
|
}
|
|
else
|
|
{
|
|
grabMouse();
|
|
}
|
|
}
|
|
|
|
void Minecraft::onGraphicsReset()
|
|
{
|
|
m_pTextures->clear();
|
|
m_pFont->onGraphicsReset();
|
|
|
|
if (m_pLevelRenderer)
|
|
m_pLevelRenderer->onGraphicsReset();
|
|
|
|
if (m_pGameRenderer)
|
|
m_pGameRenderer->onGraphicsReset();
|
|
|
|
EntityRenderDispatcher::getInstance()->onGraphicsReset();
|
|
}
|
|
|
|
void Minecraft::saveOptions()
|
|
{
|
|
if (platform()->hasFileSystemAccess())
|
|
getOptions()->save();
|
|
}
|
|
|
|
bool Minecraft::isLevelGenerated()
|
|
{
|
|
if (m_pLevel)
|
|
return !m_bPreparingLevel;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool Minecraft::isOnline()
|
|
{
|
|
return m_pNetEventCallback != nullptr;
|
|
}
|
|
|
|
bool Minecraft::isOnlineClient()
|
|
{
|
|
if (!m_pLevel)
|
|
return false;
|
|
|
|
return m_pLevel->m_bIsMultiplayer;
|
|
}
|
|
|
|
bool Minecraft::isTouchscreen()
|
|
{
|
|
return m_bIsTouchscreen;
|
|
}
|
|
|
|
void Minecraft::setGuiScaleMultiplier(float f)
|
|
{
|
|
guiScaleMultiplier = f;
|
|
}
|
|
|
|
void Minecraft::handleMouseDown(int type, bool b)
|
|
{
|
|
if (!m_pGameMode->field_8 && (type != 1 || this->field_DA4 <= 0))
|
|
{
|
|
if (b && type == 1 && m_hitResult.m_hitType == HitResult::AABB && !m_hitResult.m_bUnk24)
|
|
{
|
|
m_pGameMode->continueDestroyBlock(m_hitResult.m_tileX, m_hitResult.m_tileY, m_hitResult.m_tileZ, m_hitResult.m_hitSide);
|
|
m_pParticleEngine->crack(m_hitResult.m_tileX, m_hitResult.m_tileY, m_hitResult.m_tileZ, m_hitResult.m_hitSide);
|
|
}
|
|
else
|
|
{
|
|
m_pGameMode->stopDestroyBlock();
|
|
}
|
|
}
|
|
}
|
|
|
|
void Minecraft::handleBuildAction(BuildActionIntention* pAction)
|
|
{
|
|
if (pAction->isRemove())
|
|
{
|
|
if (field_DA4 > 0)
|
|
return;
|
|
|
|
m_pLocalPlayer->swing();
|
|
}
|
|
|
|
bool bInteract = true;
|
|
if (!m_hitResult.isHit())
|
|
{
|
|
if (pAction->isRemove() && !m_pGameMode->isCreativeType())
|
|
field_DA4 = 10;
|
|
}
|
|
else if (m_hitResult.m_hitType == HitResult::ENTITY)
|
|
{
|
|
if (pAction->isAttack())
|
|
{
|
|
m_pGameMode->attack(m_pLocalPlayer, m_hitResult.m_pEnt);
|
|
}
|
|
else if (pAction->isInteract())
|
|
{
|
|
if (m_hitResult.m_pEnt->interactPreventDefault())
|
|
bInteract = false;
|
|
|
|
m_pGameMode->interact(m_pLocalPlayer, m_hitResult.m_pEnt);
|
|
}
|
|
}
|
|
else if (m_hitResult.m_hitType == HitResult::AABB)
|
|
{
|
|
Tile* pTile = Tile::tiles[m_pLevel->getTile(m_hitResult.m_tileX, m_hitResult.m_tileY, m_hitResult.m_tileZ)];
|
|
|
|
if (pAction->isRemove())
|
|
{
|
|
if (!pTile)
|
|
return;
|
|
|
|
// @BUG: This is only done on the client side.
|
|
m_pLevel->extinguishFire(m_hitResult.m_tileX, m_hitResult.m_tileY, m_hitResult.m_tileZ, m_hitResult.m_hitSide);
|
|
|
|
if (pTile != Tile::unbreakable || (m_pLocalPlayer->field_B94 > 99 && m_hitResult.m_bUnk24 != 1))
|
|
{
|
|
m_pGameMode->startDestroyBlock(m_hitResult.m_tileX, m_hitResult.m_tileY, m_hitResult.m_tileZ, m_hitResult.m_hitSide);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ItemInstance* pItem = getSelectedItem();
|
|
if (m_pGameMode->useItemOn(
|
|
m_pLocalPlayer,
|
|
m_pLevel,
|
|
pItem->m_itemID <= 0 ? nullptr : pItem,
|
|
m_hitResult.m_tileX,
|
|
m_hitResult.m_tileY,
|
|
m_hitResult.m_tileZ,
|
|
m_hitResult.m_hitSide))
|
|
{
|
|
bInteract = false;
|
|
|
|
m_pLocalPlayer->swing();
|
|
|
|
if (isOnline())
|
|
{
|
|
if (pItem->m_itemID > C_MAX_TILES || pItem->m_itemID < 0)
|
|
return;
|
|
|
|
int dx = m_hitResult.m_tileX, dz = m_hitResult.m_tileZ;
|
|
uint8_t dy = uint8_t(m_hitResult.m_tileY);
|
|
|
|
uint8_t hitSide = m_hitResult.m_hitSide;
|
|
|
|
if (m_pLevel->getTile(m_hitResult.m_tileX, m_hitResult.m_tileY, m_hitResult.m_tileZ) != Tile::topSnow->m_ID)
|
|
{
|
|
switch (m_hitResult.m_hitSide)
|
|
{
|
|
case HitResult::NOHIT: break;
|
|
case HitResult::MINY: dy--; break;
|
|
case HitResult::MAXY: dy++; break;
|
|
case HitResult::MINZ: dz--; break;
|
|
case HitResult::MAXZ: dz++; break;
|
|
case HitResult::MINX: dx--; break;
|
|
case HitResult::MAXX: dx++; break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hitSide = HitResult::MINY;
|
|
}
|
|
|
|
m_pRakNetInstance->send(new PlaceBlockPacket(m_pLocalPlayer->m_EntityID, dx, dy, dz, uint8_t(pItem->m_itemID), hitSide));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bInteract && pAction->isInteract())
|
|
{
|
|
ItemInstance* pItem = getSelectedItem();
|
|
if (pItem)
|
|
{
|
|
if (m_pGameMode->useItem(m_pLocalPlayer, m_pLevel, pItem))
|
|
m_pGameRenderer->m_pItemInHandRenderer->itemUsed();
|
|
}
|
|
}
|
|
}
|
|
|
|
void Minecraft::handleMouseClick(int type)
|
|
{
|
|
if (!isTouchscreen())
|
|
{
|
|
if (type == 1)
|
|
{
|
|
BuildActionIntention bai(INTENT_HELD);
|
|
handleBuildAction(&bai);
|
|
}
|
|
if (type == 2)
|
|
{
|
|
BuildActionIntention bai(INTENT_CLICKED);
|
|
handleBuildAction(&bai);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Minecraft::tickInput()
|
|
{
|
|
if (m_pScreen)
|
|
{
|
|
if (!m_pScreen->field_10)
|
|
{
|
|
m_bUsingScreen = true;
|
|
m_pScreen->updateEvents();
|
|
m_bUsingScreen = false;
|
|
|
|
if (m_bHasQueuedScreen)
|
|
{
|
|
setScreen(m_pQueuedScreen);
|
|
m_pQueuedScreen = nullptr;
|
|
m_bHasQueuedScreen = false;
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (!m_pLocalPlayer)
|
|
return;
|
|
|
|
bool bIsInGUI = m_gui.isInside(Mouse::getX(), Mouse::getY());
|
|
|
|
while (Mouse::next())
|
|
{
|
|
if (getTimeMs() - field_2B4 > 200)
|
|
continue;
|
|
|
|
if (Mouse::isButtonDown(BUTTON_LEFT))
|
|
m_gui.handleClick(1, Mouse::getX(), Mouse::getY());
|
|
|
|
if (!bIsInGUI && getOptions()->field_19)
|
|
{
|
|
if (Mouse::getEventButton() == BUTTON_LEFT && Mouse::getEventButtonState())
|
|
{
|
|
handleMouseClick(1);
|
|
field_DAC = field_DA8;
|
|
}
|
|
if (Mouse::getEventButton() == BUTTON_RIGHT && Mouse::getEventButtonState())
|
|
{
|
|
handleMouseClick(2);
|
|
field_DAC = field_DA8;
|
|
}
|
|
#ifdef ENH_ALLOW_SCROLL_WHEEL
|
|
if (Mouse::getEventButton() == BUTTON_SCROLLWHEEL)
|
|
{
|
|
int slot = m_pLocalPlayer->m_pInventory->m_SelectedHotbarSlot;
|
|
|
|
int maxItems = m_gui.getNumSlots() - 1;
|
|
if (isTouchscreen())
|
|
maxItems--;
|
|
|
|
if (Mouse::getEventButtonState() == 0) // @NOTE: Scroll up
|
|
{
|
|
if (slot-- == 0)
|
|
{
|
|
slot = maxItems;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (slot++ == maxItems) // @NOTE: Scroll down
|
|
{
|
|
slot = 0;
|
|
}
|
|
}
|
|
|
|
m_pLocalPlayer->m_pInventory->selectSlot(slot);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
while (Keyboard::next())
|
|
{
|
|
int keyCode = Keyboard::getEventKey();
|
|
bool bPressed = Keyboard::getEventKeyState() == 1;
|
|
|
|
m_pLocalPlayer->m_pMoveInput->setKey(keyCode, bPressed);
|
|
|
|
if (bPressed)
|
|
{
|
|
m_gui.handleKeyPressed(keyCode);
|
|
|
|
for (int i = 0; i < m_gui.getNumSlots(); i++)
|
|
{
|
|
if (getOptions()->isKey(eKeyMappingIndex(KM_SLOT_1 + i), keyCode))
|
|
m_pLocalPlayer->m_pInventory->selectSlot(i);
|
|
}
|
|
|
|
if (getOptions()->isKey(KM_TOGGLE3RD, keyCode))
|
|
{
|
|
getOptions()->m_bThirdPerson = !getOptions()->m_bThirdPerson;
|
|
}
|
|
else if (getOptions()->isKey(KM_MENU_CANCEL, keyCode))
|
|
{
|
|
pauseGame();
|
|
}
|
|
else if (getOptions()->isKey(KM_DROP, keyCode))
|
|
{
|
|
int itemID = m_pLocalPlayer->m_pInventory->getSelectedItemId();
|
|
if (itemID > 0)
|
|
{
|
|
ItemInstance inst(itemID, 1, 0);
|
|
m_pLocalPlayer->drop(&inst);
|
|
}
|
|
}
|
|
else if (getOptions()->isKey(KM_TOGGLEGUI, keyCode))
|
|
{
|
|
getOptions()->m_bDontRenderGui = !getOptions()->m_bDontRenderGui;
|
|
}
|
|
else if (getOptions()->isKey(KM_TOGGLEDEBUG, keyCode))
|
|
{
|
|
getOptions()->m_bDebugText = !getOptions()->m_bDebugText;
|
|
}
|
|
#ifdef ENH_ALLOW_AO
|
|
else if (getOptions()->isKey(KM_TOGGLEAO, keyCode))
|
|
{
|
|
// Toggle ambient occlusion.
|
|
getOptions()->m_bAmbientOcclusion = !getOptions()->m_bAmbientOcclusion;
|
|
Minecraft::useAmbientOcclusion = getOptions()->m_bAmbientOcclusion;
|
|
m_pLevelRenderer->allChanged();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if (getOptions()->field_19)
|
|
continue;
|
|
|
|
if (getTimeMs() - field_2B4 <= 200)
|
|
{
|
|
if (getOptions()->getKey(KM_DESTROY) == keyCode && bPressed)
|
|
{
|
|
BuildActionIntention intention(INTENT_HELD);
|
|
handleBuildAction(&intention);
|
|
}
|
|
|
|
if (getOptions()->getKey(KM_PLACE) == keyCode && bPressed)
|
|
{
|
|
BuildActionIntention intention(INTENT_CLICKED);
|
|
handleBuildAction(&intention);
|
|
}
|
|
}
|
|
}
|
|
|
|
BuildActionIntention bai;
|
|
bool b = m_pInputHolder->getBuildInput()->tickBuild(m_pLocalPlayer, &bai);
|
|
|
|
if (b && !bai.isRemoveContinue())
|
|
handleBuildAction(&bai);
|
|
|
|
bool flag =
|
|
// If we are mouse operated, the LMB is held down and it's not in the GUI
|
|
((m_options->field_19 && Mouse::isButtonDown(BUTTON_LEFT) && !bIsInGUI) ||
|
|
// We are instead keyboard operated, so check for the KM_DESTROY key being held down
|
|
(!m_options->field_19 && Keyboard::isKeyDown(m_options->m_keyMappings[KM_DESTROY].value)) ||
|
|
// The build action intention is a remove one
|
|
(b && bai.isRemove()));
|
|
|
|
if (flag && !m_pScreen && (field_DA8 - field_DAC) >= (m_timer.m_ticksPerSecond * 0.25f))
|
|
{
|
|
bai = BuildActionIntention(INTENT_HELD);
|
|
handleBuildAction(&bai); // handleMouseClick(BUTTON_LEFT)
|
|
field_DAC = field_DA8;
|
|
}
|
|
|
|
handleMouseDown(BUTTON_LEFT, flag);
|
|
|
|
field_2B4 = getTimeMs();
|
|
|
|
Keyboard::reset();
|
|
Mouse::reset();
|
|
}
|
|
|
|
void Minecraft::tickMouse()
|
|
{
|
|
if (!m_bGrabbedMouse)
|
|
return;
|
|
|
|
platform()->recenterMouse();
|
|
}
|
|
|
|
void Minecraft::handleCharInput(char chr)
|
|
{
|
|
if (m_pScreen)
|
|
m_pScreen->charInput(chr);
|
|
}
|
|
|
|
void Minecraft::sendMessage(const std::string& message)
|
|
{
|
|
if (isOnlineClient())
|
|
{
|
|
// send the server a message packet
|
|
if (m_pRakNetInstance)
|
|
m_pRakNetInstance->send(new MessagePacket(message));
|
|
else
|
|
m_gui.addMessage("You aren't actually playing multiplayer!");
|
|
}
|
|
else
|
|
{
|
|
// fake the server having received a packet
|
|
MessagePacket mp(message);
|
|
if (m_pNetEventCallback && m_pRakNetInstance)
|
|
m_pNetEventCallback->handle(m_pRakNetInstance->m_pRakPeerInterface->GetMyGUID(), &mp);
|
|
else
|
|
m_gui.addMessage("You aren't hosting a multiplayer server!");
|
|
}
|
|
}
|
|
|
|
void Minecraft::resetPlayer(Player* player)
|
|
{
|
|
m_pLevel->validateSpawn();
|
|
player->reset();
|
|
|
|
Pos pos = m_pLevel->getSharedSpawnPos();
|
|
player->setPos(float(pos.x), float(pos.y), float(pos.z));
|
|
player->resetPos();
|
|
|
|
// Of course we have to add him back into the game, if he isn't already.
|
|
EntityVector& vec = m_pLevel->m_entities;
|
|
for (int i = 0; i < int(vec.size()); i++)
|
|
{
|
|
if (vec[i] == player)
|
|
return;
|
|
}
|
|
|
|
std::vector<Player*>& vec2 = m_pLevel->m_players;
|
|
for (int i = 0; i < int(vec2.size()); i++)
|
|
{
|
|
// remove the player if he is already in the player list
|
|
if (vec2[i] == player)
|
|
{
|
|
vec2.erase(vec2.begin() + i);
|
|
i--;
|
|
}
|
|
}
|
|
|
|
// add him in!!
|
|
m_pLevel->addEntity(player);
|
|
}
|
|
|
|
void Minecraft::respawnPlayer(Player* player)
|
|
{
|
|
resetPlayer(player);
|
|
|
|
// TODO: send a RespawnPacket
|
|
}
|
|
|
|
std::string Minecraft::getVersionString()
|
|
{
|
|
return "v0.1.0 alpha";
|
|
}
|
|
|
|
void Minecraft::_reloadInput()
|
|
{
|
|
if (m_pInputHolder)
|
|
delete m_pInputHolder;
|
|
|
|
if (isTouchscreen())
|
|
{
|
|
m_pInputHolder = new TouchInputHolder(this, m_options);
|
|
}
|
|
else
|
|
{
|
|
m_pInputHolder = new CustomInputHolder(
|
|
new KeyboardInput(m_options),
|
|
#ifdef ORIGINAL_CODE
|
|
new ControllerTurnInput,
|
|
#else
|
|
new MouseTurnInput(this),
|
|
#endif
|
|
new IBuildInput
|
|
);
|
|
}
|
|
|
|
m_mouseHandler.setTurnInput(m_pInputHolder->getTurnInput());
|
|
|
|
if (m_pLevel && m_pLocalPlayer)
|
|
{
|
|
m_pLocalPlayer->m_pMoveInput = m_pInputHolder->getMoveInput();
|
|
}
|
|
|
|
m_options->field_19 = !isTouchscreen();
|
|
}
|
|
|
|
void Minecraft::_levelGenerated()
|
|
{
|
|
if (m_pNetEventCallback)
|
|
m_pNetEventCallback->levelGenerated(m_pLevel);
|
|
}
|
|
|
|
void Minecraft::tick()
|
|
{
|
|
if (field_DA4 > 0)
|
|
field_DA4--;
|
|
|
|
if (!m_pScreen)
|
|
{
|
|
if (m_pLocalPlayer && m_pLocalPlayer->m_health <= 0)
|
|
{
|
|
setScreen(new DeathScreen);
|
|
}
|
|
}
|
|
|
|
tickInput();
|
|
|
|
m_gui.tick();
|
|
|
|
// if the level has been prepared, delete the prep thread
|
|
if (!m_bPreparingLevel)
|
|
{
|
|
if (m_pPrepThread)
|
|
{
|
|
delete m_pPrepThread;
|
|
m_pPrepThread = nullptr;
|
|
_levelGenerated();
|
|
}
|
|
|
|
SandTile::instaFall = false;
|
|
|
|
if (m_pLevel && !field_288)
|
|
{
|
|
m_pGameMode->tick();
|
|
m_pGameRenderer->tick();
|
|
m_pLevelRenderer->tick();
|
|
m_pLevel->tickEntities();
|
|
m_pLevel->tick();
|
|
|
|
if (m_pLocalPlayer)
|
|
{
|
|
m_pLevel->animateTick(
|
|
Mth::floor(m_pLocalPlayer->m_pos.x),
|
|
Mth::floor(m_pLocalPlayer->m_pos.y),
|
|
Mth::floor(m_pLocalPlayer->m_pos.z));
|
|
}
|
|
}
|
|
|
|
m_pTextures->loadAndBindTexture(C_TERRAIN_NAME);
|
|
|
|
if (!field_288)
|
|
{
|
|
m_pTextures->tick();
|
|
m_pParticleEngine->tick();
|
|
|
|
#ifndef ORIGINAL_CODE
|
|
if (m_pMobPersp)
|
|
{
|
|
m_pSoundEngine->m_pSoundSystem->setListenerPos(m_pMobPersp->m_pos.x, m_pMobPersp->m_pos.y, m_pMobPersp->m_pos.z);
|
|
m_pSoundEngine->m_pSoundSystem->setListenerAngle(m_pMobPersp->m_yaw, m_pMobPersp->m_pitch);
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
if (m_pScreen)
|
|
m_pScreen->tick();
|
|
|
|
Multitouch::reset();
|
|
}
|
|
}
|
|
|
|
void Minecraft::update()
|
|
{
|
|
if (field_288 && m_pLevel)
|
|
{
|
|
float x = m_timer.m_renderTicks;
|
|
m_timer.advanceTime();
|
|
m_timer.m_renderTicks = x;
|
|
}
|
|
else
|
|
{
|
|
m_timer.advanceTime();
|
|
}
|
|
|
|
if (m_pRakNetInstance)
|
|
{
|
|
m_pRakNetInstance->runEvents(m_pNetEventCallback);
|
|
}
|
|
|
|
for (int i = 0; i < m_timer.m_ticks; i++)
|
|
{
|
|
tick();
|
|
field_DA8++;
|
|
}
|
|
|
|
if (m_pLevel && !m_bPreparingLevel)
|
|
{
|
|
m_pLevel->updateLights();
|
|
}
|
|
|
|
#ifndef ORIGINAL_CODE
|
|
tickMouse();
|
|
#endif
|
|
|
|
m_pGameRenderer->render(m_timer.m_renderTicks);
|
|
|
|
double time = double(getTimeS());
|
|
m_fDeltaTime = time - m_fLastUpdated;
|
|
m_fLastUpdated = time;
|
|
|
|
// Added by iProgramInCpp
|
|
m_pGameMode->render(m_timer.m_renderTicks);
|
|
}
|
|
|
|
void Minecraft::init()
|
|
{
|
|
if (platform()->hasFileSystemAccess())
|
|
m_options = new Options(m_externalStorageDir);
|
|
else
|
|
m_options = new Options();
|
|
|
|
m_bIsTouchscreen = platform()->isTouchscreen();
|
|
|
|
_reloadInput();
|
|
|
|
m_pRakNetInstance = new RakNetInstance;
|
|
|
|
m_pSoundEngine = new SoundEngine(platform()->getSoundSystem());
|
|
m_pSoundEngine->init(m_options);
|
|
|
|
m_pTextures = new Textures(m_options, platform());
|
|
m_pTextures->addDynamicTexture(new WaterTexture);
|
|
m_pTextures->addDynamicTexture(new WaterSideTexture);
|
|
m_pTextures->addDynamicTexture(new LavaTexture);
|
|
m_pTextures->addDynamicTexture(new LavaSideTexture);
|
|
m_pTextures->addDynamicTexture(new FireTexture(0));
|
|
m_pLevelRenderer = new LevelRenderer(this, m_pTextures);
|
|
m_pGameRenderer = new GameRenderer(this);
|
|
m_pParticleEngine = new ParticleEngine(m_pLevel, m_pTextures);
|
|
m_pUser = new User(getOptions()->m_playerName, "");
|
|
|
|
#ifdef TEST_SURVIVAL_MODE
|
|
m_pGameMode = new SurvivalMode(this);
|
|
#else
|
|
m_pGameMode = new CreativeMode(this);
|
|
#endif
|
|
|
|
m_pFont = new Font(m_options, "font/default.png", m_pTextures);
|
|
|
|
// Patch Manager
|
|
GetPatchManager()->LoadPatchData(platform()->getPatchData());
|
|
|
|
m_pTextures->loadAndBindTexture(C_TERRAIN_NAME);
|
|
GetPatchManager()->PatchTextures(platform(), TYPE_TERRAIN);
|
|
m_pTextures->loadAndBindTexture(C_ITEMS_NAME);
|
|
GetPatchManager()->PatchTextures(platform(), TYPE_ITEMS);
|
|
|
|
GetPatchManager()->PatchTiles();
|
|
}
|
|
|
|
Minecraft::~Minecraft()
|
|
{
|
|
SAFE_DELETE(m_options);
|
|
SAFE_DELETE(m_pNetEventCallback);
|
|
SAFE_DELETE(m_pRakNetInstance);
|
|
SAFE_DELETE(m_pLevelRenderer);
|
|
SAFE_DELETE(m_pGameRenderer);
|
|
SAFE_DELETE(m_pParticleEngine);
|
|
SAFE_DELETE(m_pSoundEngine);
|
|
SAFE_DELETE(m_pGameMode);
|
|
SAFE_DELETE(m_pFont);
|
|
SAFE_DELETE(m_pTextures);
|
|
|
|
if (m_pLevel)
|
|
{
|
|
LevelStorage* pStor = m_pLevel->getLevelStorage();
|
|
if (pStor)
|
|
delete pStor;
|
|
if (m_pLevel)
|
|
delete m_pLevel;
|
|
}
|
|
|
|
SAFE_DELETE(m_pUser);
|
|
SAFE_DELETE(m_pLevelStorageSource);
|
|
SAFE_DELETE(m_pInputHolder);
|
|
SAFE_DELETE(m_Logger);
|
|
|
|
//@BUG: potentially leaking a CThread instance if this is destroyed early?
|
|
}
|
|
|
|
void Minecraft::prepareLevel(const std::string& unused)
|
|
{
|
|
field_DA0 = 1;
|
|
|
|
float startTime = getTimeS();
|
|
Level* pLevel = m_pLevel;
|
|
|
|
if (!pLevel->field_B0C)
|
|
{
|
|
pLevel->setUpdateLights(0);
|
|
}
|
|
|
|
for (int i = 8, i2 = 0; i != 8 + C_MAX_CHUNKS_X * 16; i += 16)
|
|
{
|
|
for (int j = 8; j != 8 + C_MAX_CHUNKS_Z * 16; j += 16, i2 += 100)
|
|
{
|
|
// this looks like some kind of progress tracking
|
|
m_progressPercent = i2 / (C_MAX_CHUNKS_X * C_MAX_CHUNKS_Z);
|
|
|
|
float time1 = getTimeS();
|
|
|
|
// generating all the chunks at once
|
|
(void) m_pLevel->getTile(i, (C_MAX_Y + C_MIN_Y) / 2, j);
|
|
|
|
if (time1 != -1.0f)
|
|
getTimeS();
|
|
|
|
float time2 = getTimeS();
|
|
if (m_pLevel->field_B0C)
|
|
{
|
|
while (m_pLevel->updateLights());
|
|
}
|
|
|
|
if (time2 != -1.0f)
|
|
getTimeS();
|
|
}
|
|
}
|
|
|
|
if (startTime != -1.0f)
|
|
getTimeS();
|
|
|
|
m_pLevel->setUpdateLights(1);
|
|
|
|
startTime = getTimeS();
|
|
|
|
for (int x = 0; x < C_MAX_CHUNKS_X; x++)
|
|
{
|
|
for (int z = 0; z < C_MAX_CHUNKS_Z; z++)
|
|
{
|
|
LevelChunk* pChunk = m_pLevel->getChunk(x, z);
|
|
if (!pChunk)
|
|
continue;
|
|
|
|
if (pChunk->field_237)
|
|
continue;
|
|
|
|
pChunk->m_bUnsaved = false;
|
|
pChunk->clearUpdateMap();
|
|
}
|
|
}
|
|
|
|
if (startTime != -1.0f)
|
|
getTimeS();
|
|
|
|
field_DA0 = 3;
|
|
|
|
if (m_pLevel->field_B0C)
|
|
{
|
|
m_pLevel->setInitialSpawn();
|
|
m_pLevel->saveLevelData();
|
|
m_pLevel->getChunkSource()->saveAll();
|
|
}
|
|
else
|
|
{
|
|
m_pLevel->saveLevelData();
|
|
}
|
|
|
|
m_progressPercent = -1;
|
|
field_DA0 = 2;
|
|
|
|
startTime = getTimeS();
|
|
|
|
m_pLevel->prepare();
|
|
|
|
if (startTime != -1.0f)
|
|
getTimeS();
|
|
|
|
// These strings are initialized and then removed quickly afterwards. Probably some debug leftover
|
|
// "Generate level:";
|
|
// " - light: ";
|
|
// " - getTl: ";
|
|
// " - clear: ";
|
|
// " - prepr: ";
|
|
}
|
|
|
|
void Minecraft::sizeUpdate(int newWidth, int newHeight)
|
|
{
|
|
// re-calculate the GUI scale.
|
|
Gui::InvGuiScale = getBestScaleForThisScreenSize(newWidth, newHeight) / guiScaleMultiplier;
|
|
|
|
if (m_pScreen)
|
|
m_pScreen->setSize(int(Minecraft::width * Gui::InvGuiScale), int(Minecraft::height * Gui::InvGuiScale));
|
|
|
|
if (m_pInputHolder)
|
|
m_pInputHolder->setScreenSize(newWidth * guiScaleMultiplier, newHeight * guiScaleMultiplier);
|
|
}
|
|
|
|
float Minecraft::getBestScaleForThisScreenSize(int width, int height)
|
|
{
|
|
if (height > 1800)
|
|
return 1.0f / 4.0f;
|
|
|
|
if (isTouchscreen())
|
|
{
|
|
if (height > 600)
|
|
return 1.0f / 4.0f;
|
|
|
|
if (height > 400)
|
|
return 1.0f / 3.0f;
|
|
|
|
if (height > 300)
|
|
return 1.0f / 2.0f;
|
|
}
|
|
else
|
|
{
|
|
if (height > 1600)
|
|
return 1.0f / 4.0f;
|
|
|
|
if (height > 800)
|
|
return 1.0f / 3.0f;
|
|
|
|
if (height > 400)
|
|
return 1.0f / 2.0f;
|
|
}
|
|
|
|
return 1.0f;
|
|
}
|
|
|
|
void Minecraft::generateLevel(const std::string& unused, Level* pLevel)
|
|
{
|
|
float time = getTimeS(); //@UNUSED
|
|
|
|
prepareLevel(unused);
|
|
|
|
if (time != -1.0f)
|
|
getTimeS(); //@QUIRK: unused return value
|
|
|
|
// std::string("Level generated: "); //@QUIRK: unused string instance
|
|
|
|
LocalPlayer* pLocalPlayer = m_pLocalPlayer;
|
|
if (!pLocalPlayer)
|
|
{
|
|
pLocalPlayer = m_pGameMode->createPlayer(pLevel);
|
|
m_pLocalPlayer = pLocalPlayer;
|
|
m_pGameMode->initPlayer(pLocalPlayer);
|
|
}
|
|
|
|
if (pLocalPlayer)
|
|
pLocalPlayer->m_pMoveInput = m_pInputHolder->getMoveInput();
|
|
|
|
if (m_pLevelRenderer)
|
|
m_pLevelRenderer->setLevel(pLevel);
|
|
|
|
if (m_pParticleEngine)
|
|
m_pParticleEngine->setLevel(pLevel);
|
|
|
|
m_pGameMode->adjustPlayer(m_pLocalPlayer);
|
|
|
|
pLevel->validateSpawn();
|
|
pLevel->loadPlayer(m_pLocalPlayer);
|
|
|
|
if (m_pLocalPlayer)
|
|
{
|
|
m_pLocalPlayer->resetPos();
|
|
}
|
|
|
|
m_pMobPersp = m_pLocalPlayer;
|
|
m_pLevel = pLevel;
|
|
|
|
m_bPreparingLevel = false;
|
|
|
|
if (m_pRakNetInstance->m_bIsHost)
|
|
m_pRakNetInstance->announceServer(m_pUser->field_0);
|
|
}
|
|
|
|
void* Minecraft::prepareLevel_tspawn(void* ptr)
|
|
{
|
|
Minecraft* pMinecraft = (Minecraft*)ptr;
|
|
|
|
pMinecraft->generateLevel("Currently not used", pMinecraft->m_pLevel);
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void Minecraft::pauseGame()
|
|
{
|
|
if (m_pScreen) return;
|
|
m_pLevel->savePlayerData();
|
|
setScreen(new PauseScreen);
|
|
}
|
|
|
|
void Minecraft::setLevel(Level* pLevel, const std::string& text, LocalPlayer* pLocalPlayer)
|
|
{
|
|
m_pMobPersp = nullptr;
|
|
|
|
if (pLevel)
|
|
{
|
|
m_pGameMode->initLevel(pLevel);
|
|
|
|
if (pLocalPlayer && m_pLocalPlayer == nullptr)
|
|
{
|
|
m_pLocalPlayer = pLocalPlayer;
|
|
pLocalPlayer->resetPos();
|
|
}
|
|
else if (m_pLocalPlayer)
|
|
{
|
|
m_pLocalPlayer->resetPos();
|
|
pLevel->addEntity(m_pLocalPlayer);
|
|
}
|
|
|
|
m_pLevel = pLevel;
|
|
m_bPreparingLevel = true;
|
|
m_pPrepThread = new CThread(&Minecraft::prepareLevel_tspawn, this);
|
|
}
|
|
else
|
|
{
|
|
m_pLocalPlayer = nullptr;
|
|
}
|
|
}
|
|
|
|
void Minecraft::selectLevel(const std::string& a, const std::string& b, int c)
|
|
{
|
|
LevelStorage* pStor = m_pLevelStorageSource->selectLevel(a, false);
|
|
Dimension* pDim = Dimension::getNew(0);
|
|
|
|
m_pLevel = new Level(pStor, b, c, 1, pDim);
|
|
setLevel(m_pLevel, "Generating level", nullptr);
|
|
|
|
field_D9C = 1;
|
|
}
|
|
|
|
const char* Minecraft::getProgressMessage()
|
|
{
|
|
return progressMessages[field_DA0];
|
|
}
|
|
|
|
LevelStorageSource* Minecraft::getLevelSource()
|
|
{
|
|
return m_pLevelStorageSource;
|
|
}
|
|
|
|
ItemInstance* Minecraft::getSelectedItem()
|
|
{
|
|
ItemInstance* pInst = m_pLocalPlayer->m_pInventory->getSelectedItem();
|
|
|
|
if (!pInst)
|
|
{
|
|
m_CurrItemInstance.m_itemID = -1;
|
|
m_CurrItemInstance.m_amount = 999;
|
|
m_CurrItemInstance.m_auxValue = 0;
|
|
return &m_CurrItemInstance;
|
|
}
|
|
|
|
if (m_pGameMode->isSurvivalType())
|
|
return pInst;
|
|
|
|
m_CurrItemInstance.m_itemID = pInst->m_itemID;
|
|
m_CurrItemInstance.m_amount = 999;
|
|
m_CurrItemInstance.m_auxValue = pInst->m_auxValue;
|
|
|
|
return &m_CurrItemInstance;
|
|
}
|
|
|
|
int Minecraft::getFpsIntlCounter()
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
void Minecraft::leaveGame(bool bCopyMap)
|
|
{
|
|
m_bPreparingLevel = false;
|
|
m_pRakNetInstance->disconnect();
|
|
m_pMobPersp = nullptr;
|
|
m_pLevelRenderer->setLevel(nullptr);
|
|
m_pParticleEngine->setLevel(nullptr);
|
|
|
|
#ifndef ORIGINAL_CODE
|
|
// @BUG: Deleting ServerSideNetworkHandler too late! This causes
|
|
// access to invalid memory in the destructor seeing as we already deleted the level.
|
|
delete m_pNetEventCallback;
|
|
#endif
|
|
|
|
#ifdef ENH_IMPROVED_SAVING
|
|
field_288 = true;
|
|
setScreen(new SavingWorldScreen(bCopyMap, m_pLocalPlayer));
|
|
#else
|
|
if (m_pLevel)
|
|
{
|
|
LevelStorage* pStorage = m_pLevel->getLevelStorage();
|
|
SAFE_DELETE(pStorage);
|
|
SAFE_DELETE(m_pLevel);
|
|
|
|
m_pLevel = nullptr;
|
|
}
|
|
#endif
|
|
|
|
#ifdef ORIGINAL_CODE
|
|
delete m_pNetEventCallback;
|
|
#endif
|
|
|
|
m_pLocalPlayer = nullptr;
|
|
m_pNetEventCallback = nullptr;
|
|
field_D9C = 0;
|
|
|
|
#ifndef ENH_IMPROVED_SAVING
|
|
// this is safe to do, since on destruction, we don't actually delete it.
|
|
SAFE_DELETE(m_pLocalPlayer);
|
|
|
|
if (bCopyMap)
|
|
setScreen(new RenameMPLevelScreen("_LastJoinedServer"));
|
|
else
|
|
setScreen(new StartMenuScreen);
|
|
#endif
|
|
}
|
|
|
|
void Minecraft::hostMultiplayer()
|
|
{
|
|
#ifndef __EMSCRIPTEN__
|
|
m_pRakNetInstance->host(m_pUser->field_0, C_DEFAULT_PORT, C_MAX_CONNECTIONS);
|
|
m_pNetEventCallback = new ServerSideNetworkHandler(this, m_pRakNetInstance);
|
|
#endif
|
|
}
|
|
|
|
void Minecraft::joinMultiplayer(const PingedCompatibleServer& serverInfo)
|
|
{
|
|
#ifndef __EMSCRIPTEN__
|
|
if (field_18 && m_pNetEventCallback)
|
|
{
|
|
field_18 = false;
|
|
m_pRakNetInstance->connect(serverInfo.m_address.ToString(), serverInfo.m_address.GetPort());
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void Minecraft::cancelLocateMultiplayer()
|
|
{
|
|
#ifndef __EMSCRIPTEN__
|
|
field_18 = false;
|
|
m_pRakNetInstance->stopPingForHosts();
|
|
delete m_pNetEventCallback;
|
|
m_pNetEventCallback = nullptr;
|
|
#endif
|
|
}
|
|
|
|
void Minecraft::locateMultiplayer()
|
|
{
|
|
#ifndef __EMSCRIPTEN__
|
|
field_18 = true;
|
|
m_pRakNetInstance->pingForHosts(C_DEFAULT_PORT);
|
|
m_pNetEventCallback = new ClientSideNetworkHandler(this, m_pRakNetInstance);
|
|
#endif
|
|
}
|