Files
minecraft-pe-0.6.1/src/MinecraftClient.cpp
2026-03-26 03:55:28 +03:00

1258 lines
33 KiB
C++

#include "Minecraft.h"
#include "client/Options.h"
#include "client/gamemode/GameMode.h"
#include "client/gui/screens/ChatScreen.h"
#include "client/gui/screens/ConsoleScreen.h"
#include "client/gui/screens/DeathScreen.h"
#include "client/gui/screens/UsernameScreen.h"
#include "client/player/LocalPlayer.h"
#include "client/player/input/IBuildInput.h"
#include "client/player/input/IInputHolder.h"
#include "client/player/input/KeyboardInput.h"
#include "client/player/input/MouseBuildInput.h"
#include "client/player/input/MouseTurnInput.h"
#include "client/player/input/touchscreen/TouchscreenInput.h"
#include "client/player/input/touchscreen/TouchInputHolder.h"
#include "client/renderer/Chunk.h"
#include "client/renderer/EntityTileRenderer.h"
#include "client/renderer/ItemInHandRenderer.h"
#include "client/renderer/Tesselator.h"
#include "client/renderer/entity/EntityRenderDispatcher.h"
#include "client/renderer/entity/ItemRenderer.h"
#include "client/renderer/ptexture/DynamicTexture.h"
#include "network/RakNetInstance.h"
#include "network/ServerSideNetworkHandler.h"
#include "network/packet/InteractPacket.h"
#include "network/packet/RespawnPacket.h"
#include "platform/CThread.h"
#include "platform/input/Multitouch.h"
#include "util/PerfRenderer.h"
#include "util/PerfTimer.h"
#include "world/level/Level.h"
#include "world/level/chunk/ChunkSource.h"
#include <MinecraftClient.h>
static void checkGlError(const char* tag) {
#ifdef GLDEBUG
while (1) {
const int errCode = glGetError();
if (errCode == GL_NO_ERROR) break;
LOGE("################\nOpenGL-error @ %s : #%d\n", tag, errCode);
}
#endif /*GLDEBUG*/
}
MinecraftClient::~MinecraftClient() {
delete levelRenderer;
delete gameRenderer;
delete particleEngine;
delete soundEngine;
delete inputHolder;
delete _perfRenderer;
delete font;
delete textures;
if (getScreen != NULL) {
delete getScreen;
getScreen = NULL;
}
EntityRenderDispatcher::destroy();
#ifdef PLATFORM_DESKTOP
ItemRenderer::teardown_static();
if (EntityTileRenderer::instance != NULL) {
delete EntityTileRenderer::instance;
EntityTileRenderer::instance = NULL;
}
TileEntityRenderDispatcher::destroy();
#endif
}
void MinecraftClient::init() {
Minecraft::init();
setSize(m_platform->getScreenWidth(), m_platform->getScreenHeight());
initGLStates();
Tesselator::instance.init();
I18n::loadLanguage(m_platform, "en_US");
checkGlError("Init enter");
_supportsNonTouchscreen = !m_platform->supportsTouchscreen();
LOGI("IS TOUCHSCREEN? %d\n", options.getBooleanValue(OPTIONS_USE_TOUCHSCREEN));
textures = new Textures(&options, m_platform);
textures->addDynamicTexture(new WaterTexture());
textures->addDynamicTexture(new WaterSideTexture());
gui.texturesLoaded(textures);
levelRenderer = new LevelRenderer(this);
gameRenderer = new GameRenderer(this);
particleEngine = new ParticleEngine(level, textures);
// Platform specific initialization here
font = new Font(&options, "font/default8.png", textures);
_perfRenderer = new PerfRenderer(this, font);
checkGlError("Init complete");
screenChooser.setScreen(SCREEN_STARTMENU);
if (options.getBooleanValue(OPTIONS_FIRST_LAUNCH)) {
options.toggle(OPTIONS_FIRST_LAUNCH);
setScreen(new UsernameScreen());
}
reloadOptions();
}
void MinecraftClient::leaveGame(bool renameLevel /*=false*/) {
if (isGeneratingLevel || !_hasSignaledGeneratingLevelFinished)
return;
isGeneratingLevel = false;
bool saveLevel = level && (!level->isClientSide || renameLevel);
raknetInstance->disconnect();
if (saveLevel) {
// If server or wanting to save level as client, save all unsaved chunks!
level->getChunkSource()->saveAll(true);
}
LOGI("Clearing levels\n");
cameraTargetPlayer = NULL;
LOGI("Erasing callback\n");
delete netCallback;
netCallback = NULL;
LOGI("Erasing level\n");
if (level != NULL) {
delete level->getLevelStorage();
delete level;
level = NULL;
}
//delete player;
player = NULL;
cameraTargetPlayer = NULL;
_running = false;
}
void MinecraftClient::update() {
m_frames++;
// Generate Multitouch active pointer list
Multitouch::commit();
if (Options::debugGl)
LOGI(">>>>>>>>>>\n");
TIMER_PUSH("root");
// If we're paused (local world / invisible server), freeze gameplay and
// networking and only keep UI responsive.
bool freezeGame = pause;
if (!freezeGame) {
Minecraft::update();
}
if (gameMode != NULL) {
if (gamemode.destroyProgress <= 0) {
m_gui.progress = 0;
levelRenderer->destroyProgress = 0;
} else {
float dp = oDestroyProgress + (destroyProgress - oDestroyProgress) * a;
m_gui.progress = dp;
levelRenderer->destroyProgress = dp;
}
}
TIMER_PUSH("sound");
soundEngine->update(player, timer.a);
TIMER_POP_PUSH("render");
gameRenderer->render(timer.a);
TIMER_POP();
Multitouch::resetThisUpdate();
checkGlError("Update finished");
if (options.getBooleanValue(OPTIONS_RENDER_DEBUG)) {
#ifndef PLATFORM_DESKTOP
if (!PerfTimer::enabled) {
PerfTimer::reset();
PerfTimer::enabled = true;
}
_perfRenderer->renderFpsMeter(1);
checkGlError("render debug");
#endif
} else {
PerfTimer::enabled = false;
}
TIMER_POP();
swapBuffers();
Mouse::reset2();
#ifndef WIN32
updateStats();
#endif
}
void MinecraftClient::updateStats() {
if (Options::debugGl)
LOGI("--------------------------------------------\n");
//*
int now = getTimeMs();
//int since = now - _lastTickMs;
if (now >= lastTime + 1000)
{
if (player) {
LOGI("%d fps \t%3d chunk updates. (%.2f, %.2f, %.2f)\n",
m_frames, Chunk::updates, player->x, player->y, player->z);
Chunk::resetUpdates();
//static int _n = 0;
//if (++_n % 5 == -1) { // @note: -1
// static char filename[256];
// sprintf(filename, "%s/games/com.mojang/img_%.4d.jpg", externalStoragePath.c_str(), _n/5);
// _context.platform->saveScreenshot(filename, width, height);
//}
LOGI("%s", levelRenderer->gatherStats1().c_str());
//printf("Texture swaps (this frame): %d\n", Textures::textureChanges);
} else {
LOGI("%d fps\n", m_frames);
}
//const int* pointerIds;
//int pointerCount = Multitouch::getActivePointerIds(&pointerIds);
//if (pointerCount) {
// std::string s = "Pointers (";
// s += (char)(48 + pointerCount);
// s += ": ";
// for (int i = 0; i < pointerCount; ++i) {
// s += (char)(48 + pointerIds[i]);
// s += ", ";
// }
// LOGI("%s\n", s.c_str());
//}
lastTime = now;
m_frames = 0;
#ifdef GLDEBUG
while (1) {
int error = glGetError();
if (error == GL_NO_ERROR) break;
LOGI("#################### GL-ERROR: %d\t#####################\n", error);
LOGI("#################### GL-ERROR: %d\t#####################\n", error);
LOGI("#################### GL-ERROR: %d\t#####################\n", error);
}
#endif
}
Textures::textureChanges = 0;
}
void MinecraftClient::restartServer() {
Minecraft::restartServer();
gui.addMessage("This server has restarted!");
// @fix maybe code below should run also on server
for (int i = level->players.size()-1; i >= 0; --i) {
Player* p = level->players[i];
if (p != player)
level->removeEntity(p);
}
}
bool MinecraftClient::handleBack(bool isDown) {
if (isGeneratingLevel)
{
return true;
}
if (level)
{
if (!isDown)
{
if (getScreen)
{
if (!getScreen->handleBackEvent(isDown))
{
if (player->containerMenu) player->closeContainer();
setScreen(NULL);
}
return true;
} else {
pauseGame(true);
}
//leaveGame();
return false;
}
return true;
}
else if (getScreen)
{
return getScreen->handleBackEvent(isDown);
}
return false;
}
void MinecraftClient::onGraphicsReset() {
initGLStates();
Tesselator::instance.init();
m_textures.clear();
font->onGraphicsReset();
gui.onGraphicsReset();
if (levelRenderer) levelRenderer->onGraphicsReset();
if (gameRenderer) gameRenderer->onGraphicsReset();
EntityRenderDispatcher::getInstance()->onGraphicsReset();
TileEntityRenderDispatcher::getInstance()->onGraphicsReset();
}
void MinecraftClient::tick(int nTick, int maxTick) {
if (!pause) Minecraft::tick(nTick, maxTick);
if (!getScreen && player) {
if (player->health <= 0) {
setScreen(new DeathScreen());
}
}
TIMER_PUSH("input");
tickInput();
TIMER_POP_PUSH("gui");
gui.tick();
//
// Ongoing level generation in a (perhaps) different thread. When it's
// ready, _levelGenerated() is called once and any threads are deleted.
//
if (isGeneratingLevel) {
return;
} else if (!_hasSignaledGeneratingLevelFinished) {
if (generateLevelThread) {
delete generateLevelThread;
generateLevelThread = NULL;
}
_levelGenerated();
}
//
// Normal game loop, run before or efter level generation
//
if (level != NULL && !pause) {
TIMER_POP_PUSH("gameRenderer");
gameRenderer->tick(nTick, maxTick);
TIMER_POP_PUSH("levelRenderer");
levelRenderer->tick();
TIMER_POP_PUSH("animateTick");
if (player) {
level->animateTick(Mth::floor(player->x), Mth::floor(player->y), Mth::floor(player->z));
}
}
m_textures.loadAndBindTexture("terrain.png");
if (!pause && !(getScreen && !getScreen->renderGameBehind())) {
#if !defined(RPI)
#ifdef __APPLE__
if (isSuperFast())
#endif
{
if (nTick == maxTick) {
TIMER_POP_PUSH("textures");
m_textures.tick(true);
}
}
#endif
}
TIMER_POP_PUSH("particles");
if (!pause) {
particleEngine->tick();
}
if (getScreen) {
screenMutex = true;
getScreen->tick();
screenMutex = false;
}
// @note: fix to keep "isPressed" and "isReleased" as long as necessary.
// Most likely keyboard and mouse could/should be reset here as well.
Multitouch::reset();
TIMER_POP();
}
class InputRAII {
public:
~InputRAII() {
Mouse::reset();
Keyboard::reset();
}
};
void MinecraftClient::tickInput() {
InputRAII raiiInput;
// input handling code here...
if (getScreen && !getScreen->passEvents) {
screenMutex = true;
getScreen->updateEvents();
//screen->updateSetScreen();
screenMutex = false;
if (hasScheduledScreen) {
setScreen(scheduledScreen);
scheduledScreen = NULL;
hasScheduledScreen = false;
}
return;
}
if (!player) {
return;
}
#ifdef RPI
bool mouseDiggable = true;
bool allowGuiClicks = !mouseGrabbed;
#else
bool mouseDiggable = !gui.isInside(Mouse::getX(), Mouse::getY());
bool allowGuiClicks = true;
#endif
TIMER_PUSH("mouse");
while (Mouse::next()) {
//if (Mouse::getButtonState(MouseAction::ACTION_LEFT))
// LOGI("mouse-down-at: %d, %d\n", Mouse::getX(), Mouse::getY());
int passedTime = getTimeMs() - lastTickTime;
if (passedTime > 200) continue; // @note: As long Mouse::clear CLEARS the whole buffer, it's safe to break here
// But since it might be rewritten anyway (and hopefully there aren't a lot of messages, we just continue.
const MouseAction& e = Mouse::getEvent();
if (!useTouchscreen() && !mouseGrabbed) {
if (!getScreen && e.data == MouseAction::DATA_DOWN) {
grabMouse();
}
}
if (allowGuiClicks && e.action == MouseAction::ACTION_LEFT && e.data == MouseAction::DATA_DOWN) {
gui.handleClick(MouseAction::ACTION_LEFT, Mouse::getX(), Mouse::getY());
}
if (e.action == MouseAction::ACTION_WHEEL) {
// If chat/console is open, use the wheel to scroll through chat history.
if (getScreen && (dynamic_cast<ChatScreen*>(getScreen) || dynamic_cast<ConsoleScreen*>(getScreen))) {
gui.scrollChat(e.dy);
} else {
Inventory* v = player->inventory;
int numSlots = gui.getNumSlots();
if (!useTouchscreen()) numSlots--;
int slot = (v->selected - e.dy + numSlots) % numSlots;
v->selectSlot(slot);
}
}
/*
if (mouseDiggable && options.useMouseForDigging) {
if (Mouse::getEventButton() == MouseAction::ACTION_LEFT && Mouse::getEventButtonState()) {
handleMouseClick(MouseAction::ACTION_LEFT);
lastClickTick = ticks;
}
if (Mouse::getEventButton() == MouseAction::ACTION_RIGHT && Mouse::getEventButtonState()) {
handleMouseClick(MouseAction::ACTION_RIGHT);
lastClickTick = ticks;
}
}
*/
}
TIMER_POP_PUSH("keyboard");
while (Keyboard::next()) {
int key = Keyboard::getEventKey();
bool isPressed = (Keyboard::getEventKeyState() == KeyboardAction::KEYDOWN);
player->setKey(key, isPressed);
if (isPressed) {
gui.handleKeyPressed(key);
if (key >= '0' && key <= '9') {
int digit = key - '0';
int slot = digit - 1;
if (slot >= 0 && slot < gui.getNumSlots())
player->inventory->selectSlot(slot);
#if defined(WIN32)
if (digit >= 1 && GetAsyncKeyState(VK_CONTROL) < 0) {
// Set adventure settings here!
AdventureSettingsPacket p(level->adventureSettings);
p.toggle((AdventureSettingsPacket::Flags)(1 << slot));
p.fillIn(level->adventureSettings);
raknetInstance->send(p);
}
if (digit == 0) {
Pos pos((int)player->x, (int)player->y-1, (int)player->z);
SetSpawnPositionPacket p(pos);
raknetInstance->send(p);
}
#endif
}
if (key == Keyboard::KEY_LEFT_CTRL) {
player->setSprinting(true);
}
if (key == Keyboard::KEY_E) {
screenChooser.setScreen(SCREEN_BLOCKSELECTION);
}
if (!getScreen && key == Keyboard::KEY_T && level) {
setScreen(new ConsoleScreen());
}
if (key == Keyboard::KEY_F3) {
options.toggle(OPTIONS_RENDER_DEBUG);
}
if (key == Keyboard::KEY_F5) {
options.toggle(OPTIONS_THIRD_PERSON_VIEW);
/*
ImprovedNoise noise;
for (int i = 0; i < 16; ++i)
printf("%d\t%f\n", i, noise.grad2(i, 3, 8));
*/
}
if (!getScreen && key == Keyboard::KEY_O || key == 250) {
releaseMouse();
}
if (key == Keyboard::KEY_F) {
int dst = options.getIntValue(OPTIONS_VIEW_DISTANCE);
options.set(OPTIONS_VIEW_DISTANCE, (dst + 1) % 4);
}
#ifdef CHEATS
if (key == Keyboard::KEY_U) {
onGraphicsReset();
player->heal(100);
}
if (key == Keyboard::KEY_B || key == 108) // Toggle the game mode
setIsCreativeMode(!isCreativeMode());
if (key == Keyboard::KEY_P) // Step forward in time
level->setTime( level->getTime() + 1000);
if (key == Keyboard::KEY_G) {
setScreen(new ArmorScreen());
/*
std::vector<AABB>& boxs = level->getCubes(NULL, AABB(128.1f, 73, 128.1f, 128.9f, 74.9f, 128.9f));
LOGI("boxes: %d\n", (int)boxs.size());
*/
}
if (key == Keyboard::KEY_Y) {
textures->reloadAll();
player->hurtTo(2);
}
if (key == Keyboard::KEY_Z || key == 108) {
for (int i = 0; i < 1; ++i) {
Mob* mob = NULL;
int forceId = 0;//MobTypes::Sheep;
int types[] = {
MobTypes::Sheep,
MobTypes::Pig,
MobTypes::Chicken,
MobTypes::Cow,
};
int mobType = (forceId > 0)? forceId : types[Mth::random(sizeof(types) / sizeof(int))];
mob = MobFactory::CreateMob(mobType, level);
//((Animal*)mob)->setAge(-1000);
float dx = 4 - 8 * Mth::random() + 4 * Mth::sin(Mth::DEGRAD * player->yRot);
float dz = 4 - 8 * Mth::random() + 4 * Mth::cos(Mth::DEGRAD * player->yRot);
if (mob && !MobSpawner::addMob(level, mob, player->x + dx, player->y, player->z + dz, Mth::random()*360, 0, true))
delete mob;
}
}
if (key == Keyboard::KEY_X) {
const EntityList& entities = level->getAllEntities();
for (int i = entities.size()-1; i >= 0; --i) {
Entity* e = entities[i];
if (!e->isPlayer())
level->removeEntity(e);
}
}
if (key == Keyboard::KEY_C /*|| key == 4*/) {
player->inventory->clearInventoryWithDefault();
// @todo: Add saving here for benchmarking
}
if (key == Keyboard::KEY_H) {
setScreen( new PrerenderTilesScreen() );
}
if (key == Keyboard::KEY_O) {
for (int i = Inventory::MAX_SELECTION_SIZE; i < player->inventory->getContainerSize(); ++i)
if (player->inventory->getItem(i))
player->inventory->dropSlot(i, false);
}
if (key == Keyboard::KEY_M) {
Difficulty difficulty = (Difficulty)options.getIntValue(OPTIONS_DIFFICULTY);
options.set(OPTIONS_DIFFICULTY, (difficulty == Difficulty::PEACEFUL)?
Difficulty::NORMAL : Difficulty::PEACEFUL);
//setIsCreativeMode( !isCreativeMode() );
}
if (options.getBooleanValue(OPTIONS_RENDER_DEBUG)) {
if (key >= '0' && key <= '9') {
_perfRenderer->debugFpsMeterKeyPress(key - '0');
}
}
#endif
if (key == Keyboard::KEY_ESCAPE)
pauseGame(false);
#ifndef OPENGL_ES
if (key == Keyboard::KEY_P) {
static bool isWireFrame = false;
isWireFrame = !isWireFrame;
glPolygonMode(GL_FRONT, isWireFrame? GL_LINE : GL_FILL);
//glPolygonMode(GL_BACK, isWireFrame? GL_LINE : GL_FILL);
}
#endif
}
#ifdef WIN32
if (key == Keyboard::KEY_M) {
for (int i = 0; i < 5 * SharedConstants::TicksPerSecond; ++i)
level->tick();
}
#endif
}
TIMER_POP_PUSH("handlemouse");
static bool prevMouseDownLeft = false;
if (Mouse::getButtonState(MouseAction::ACTION_LEFT) == 0) {
gameMode->stopDestroyBlock();
}
if (!Mouse::isButtonDown(MouseAction::ACTION_RIGHT)) {
gameMode->releaseUsingItem(player);
}
if (useTouchscreen()) {
// Touch: gesture recognizer classifies the action type (turn/destroy/build)
BuildActionIntention bai;
if (inputHolder && inputHolder->getBuildInput()->tickBuild(player, &bai)) {
handleBuildAction(&bai);
} else {
gameMode->stopDestroyBlock();
}
} else {
// Desktop: left mouse = destroy/attack
if (Mouse::isButtonDown(MouseAction::ACTION_LEFT)) {
auto baiFlags = BuildActionIntention::BAI_REMOVE | BuildActionIntention::BAI_ATTACK;
if (!prevMouseDownLeft) baiFlags |= BuildActionIntention::BAI_FIRSTREMOVE;
BuildActionIntention bai(baiFlags);
handleBuildAction(&bai);
}
prevMouseDownLeft = Mouse::isButtonDown(MouseAction::ACTION_LEFT);
// Build and use/interact is on same button
// USPESHNO spizheno
static int buildHoldTicks = 0;
if (Mouse::isButtonDown(MouseAction::ACTION_RIGHT)) {
if (buildHoldTicks >= 5) buildHoldTicks = 0;
if (++buildHoldTicks == 1) {
BuildActionIntention bai(BuildActionIntention::BAI_BUILD | BuildActionIntention::BAI_INTERACT);
handleBuildAction(&bai);
}
} else {
buildHoldTicks = 0;
gameMode->releaseUsingItem(player);
}
}
// after input handling
lastTickTime = getTimeMs();
// we have (hopefully) handled the keyboard & mouse queue and it
// can now be emptied. If wanted, the reset could be changed to:
// index -= numRead; // then this code doesn't have to be placed here
// + it prepares for tick not handling all or any events.
// update: RAII'ing instead, see above
//Keyboard::reset();
//Mouse::reset();
TIMER_POP();
}
void MinecraftClient::handleBuildAction(BuildActionIntention* action) {
if (action->isRemove()) {
if (missTime > 0) return;
player->swing();
}
if(player->isUsingItem())
return;
bool mayUse = true;
if (!hitResult.isHit()) {
if (action->isRemove() && !gameMode->isCreativeType()) {
missTime = 10;
}
} else if (hitResult.type == ENTITY) {
if (action->isAttack()) {
player->swing();
//LOGI("attacking!\n");
InteractPacket packet(InteractPacket::Attack, player->entityId, hitResult.entity->entityId);
raknetInstance->send(packet);
gameMode->attack(player, hitResult.entity);
} else if (action->isInteract()) {
if (hitResult.entity->interactPreventDefault())
mayUse = false;
//LOGI("interacting!\n");
InteractPacket packet(InteractPacket::Interact, player->entityId, hitResult.entity->entityId);
raknetInstance->send(packet);
gameMode->interact(player, hitResult.entity);
}
} else if (hitResult.type == TILE) {
int x = hitResult.x;
int y = hitResult.y;
int z = hitResult.z;
int face = hitResult.f;
int oldTileId = level->getTile(x, y, z);
Tile* oldTile = Tile::tiles[oldTileId];
//bool tryDestroyBlock = false;
if (action->isRemove()) {
if (!oldTile)
return;
//LOGI("tile: %s - %d, %d, %d. b: %f - %f\n", oldTile->getDescriptionId().c_str(), x, y, z, oldTile->getBrightness(level, x, y, z), oldTile->getBrightness(level, x, y+1, z));
level->extinguishFire(x, y, z, hitResult.f);
if (action->isFirstRemove()) {
gameMode->startDestroyBlock(x, y, z, hitResult.f);
} else {
gameMode->continueDestroyBlock(x, y, z, hitResult.f);
}
particleEngine->crack(x, y, z, hitResult.f);
player->swing();
}
else {
ItemInstance* item = player->inventory->getSelected();
if (gameMode->useItemOn(player, level, item, x, y, z, face, hitResult.pos)) {
mayUse = false;
player->swing();
#ifdef RPI
} else if (item && item->id == ((Item*)Item::sword_iron)->id) {
player->swing();
#endif
}
if (item && item->count <= 0) {
player->inventory->clearSlot(player->inventory->selected);
}
//} else if (item && item->count != oldCount) {
// gameRenderer->itemInHandRenderer->itemPlaced();
//}
}
}
if (mayUse && action->isInteract()) {
ItemInstance* item = player->inventory->getSelected();
if (item && !player->isUsingItem()) {
if (gameMode->useItem(player, level, item)) {
gameRenderer->itemInHandRenderer->itemUsed();
}
if (item && item->count <= 0) {
player->inventory->clearSlot(player->inventory->selected);
}
}
}
}
bool MinecraftClient::isOnlineClient() {
return (level && level->isClientSide);
}
void MinecraftClient::pauseGame(bool isBackPaused) {
// Only freeze gameplay when running a local server and it is not accepting
// incoming connections (invisible server), which includes typical single-
// player/lobby mode. If the server is visible, the game should keep ticking.
bool canFreeze = false;
if (raknetInstance && raknetInstance->isServer() && netCallback) {
ServerSideNetworkHandler* ss = (ServerSideNetworkHandler*) netCallback;
if (!ss->allowsIncomingConnections())
canFreeze = true;
}
pause = canFreeze;
if (getScreen != NULL) return;
screenChooser.setScreen(isBackPaused? SCREEN_PAUSEPREV : SCREEN_PAUSE);
}
void MinecraftClient::gameLostFocus() {
if(getScreen != NULL) {
getScreen->lostFocus();
}
}
void MinecraftClient::setScreen( Screen* screen ) {
Mouse::reset();
Multitouch::reset();
Multitouch::resetThisUpdate();
if (screenMutex) {
hasScheduledScreen = true;
scheduledScreen = screen;
return;
}
if (screen != NULL && screen->isErrorScreen())
return;
if (screen == NULL && level == NULL)
screen = screenChooser.createScreen(SCREEN_STARTMENU);
if (this->getScreen != NULL) {
this->getScreen->removed();
delete this->getScreen;
}
this->getScreen = screen;
if (screen != NULL) {
releaseMouse();
//ScreenSizeCalculator ssc = new ScreenSizeCalculator(options, width, height);
int screenWidth = (int)(width * Gui::InvGuiScale); //ssc.getWidth();
int screenHeight = (int)(height * Gui::InvGuiScale); //ssc.getHeight();
screen->init(this, screenWidth, screenHeight);
if (screen->isInGameScreen() && level) {
level->saveLevelData();
level->saveGame();
}
//noRender = false;
} else {
// Closing a screen and returning to the game should unpause.
pause = false;
grabMouse();
}
}
void MinecraftClient::grabMouse() {
if (mouseGrabbed) return;
mouseGrabbed = true;
mouseHandler.grab();
//setScreen(NULL);
}
void MinecraftClient::releaseMouse() {
if (!mouseGrabbed) {
return;
}
if (player) {
player->releaseAllKeys();
}
mouseGrabbed = false;
mouseHandler.release();
}
bool MinecraftClient::useTouchscreen() {
#ifdef RPI
return false;
#endif
return options.getBooleanValue(OPTIONS_USE_TOUCHSCREEN) && !_supportsNonTouchscreen;
}
bool MinecraftClient::supportNonTouchScreen() {
return _supportsNonTouchscreen;
}
void MinecraftClient::setSize(int w, int h) {
transformResolution(&w, &h);
width = w;
height = h;
int guiScale = options.getIntValue(OPTIONS_GUI_SCALE);
// determine gui scale, optionally overriding auto
if (guiScale != 0) {
// manual selection: 1->small, 2->medium, 3->large, 4->larger, 5->largest
switch (guiScale) {
case 1: Gui::GuiScale = 2.0f; break;
case 2: Gui::GuiScale = 3.0f; break;
case 3: Gui::GuiScale = 4.0f; break;
case 4: Gui::GuiScale = 5.0f; break;
case 5: Gui::GuiScale = 6.0f; break;
default: Gui::GuiScale = 1.0f; break; // auto
}
} else {
// auto compute from resolution
if (width >= 1000) {
#ifdef __APPLE__
Gui::GuiScale = (width > 2000)? 8.0f : 4.0f;
#else
Gui::GuiScale = 4.0f;
#endif
}
else if (width >= 800) {
#ifdef __APPLE__
Gui::GuiScale = 4.0f;
#else
Gui::GuiScale = 3.0f;
#endif
}
else if (width >= 400)
Gui::GuiScale = 2.0f;
else
Gui::GuiScale = 1.0f;
}
Gui::InvGuiScale = 1.0f / Gui::GuiScale;
int screenWidth = (int)(width * Gui::InvGuiScale);
int screenHeight = (int)(height * Gui::InvGuiScale);
// if (platform()) {
// float pixelsPerMillimeter = options.getProgressValue(&Options::Option::PIXELS_PER_MILLIMETER);
// pixelCalc.setPixelsPerMillimeter(pixelsPerMillimeter);
// pixelCalcUi.setPixelsPerMillimeter(pixelsPerMillimeter * Gui::InvGuiScale);
// }
Config config = createConfig(this);
gui.onConfigChanged(config);
if (getScreen)
getScreen->setSize(screenWidth, screenHeight);
if (inputHolder)
inputHolder->onConfigChanged(config);
//LOGI("Setting size: %d, %d: %f\n", width, height, Gui::InvGuiScale);
}
void MinecraftClient::reloadOptions() {
options.save();
bool wasTouchscreen = options.getBooleanValue(OPTIONS_USE_TOUCHSCREEN);
options.set(OPTIONS_USE_TOUCHSCREEN, useTouchscreen());
options.save();
if ((wasTouchscreen != useTouchscreen()) || (inputHolder == 0))
_reloadInput();
// user->name = options.username;
LOGI("Reloading-options\n");
// @todo @fix Android and iOS behaves a bit differently when leaving
// an options screen (Android recreates OpenGL surface)
setSize(width, height);
}
void MinecraftClient::_reloadInput() {
delete inputHolder;
const bool useTouchHolder = useTouchscreen();
if (useTouchHolder) {
inputHolder = new TouchInputHolder(this, &options);
} else {
#if defined(ANDROID) || defined(__APPLE__)
inputHolder = new CustomInputHolder(
new XperiaPlayInput(&options),
new ControllerTurnInput(2, ControllerTurnInput::MODE_DELTA),
new IBuildInput());
#else
inputHolder = new CustomInputHolder(
new KeyboardInput(&options),
new MouseTurnInput(MouseTurnInput::MODE_DELTA, width/2, height/2),
new MouseBuildInput());
#endif
}
mouseHandler.setTurnInput(inputHolder->getTurnInput());
if (level && player) {
player->input = inputHolder->getMoveInput();
}
}
void MinecraftClient::initGLStates() {
//glShadeModel2(GL_SMOOTH);
//glClearDepthf(1.0f);
glEnable2(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glDepthRangef(0, 1);
glEnable2(GL_ALPHA_TEST);
glAlphaFunc(GL_GREATER, 0.1f);
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
glEnable2(GL_TEXTURE_2D);
#ifndef _EMSCRIPTEN_
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
#endif
// Both updates isPowerVR flag in java and returns if the graphics chip is PowerVR SGX or not
_powerVr = platform()->isPowerVR();
#ifdef __APPLE__
_isSuperFast = platform()->isSuperFast();
#endif
}
void MinecraftClient::_levelGenerated() {
Minecraft::_levelGenerated();
level->loadPlayer(player, true);
// if we are client side, we trust the server to have given us a correct position
if (player && !level->isClientSide) {
player->resetPos(false);
}
this->cameraTargetPlayer = player;
if (player == NULL) {
player = new LocalPlayer(minecraft, level, minecraft.options.getStringValue(OPTIONS_USERNAME), level->dimension->id, isCreativeType());
gameMode->initPlayer(player);
}
if (player) {
player->input = inputHolder->getMoveInput();
}
if (levelRenderer != NULL) levelRenderer->setLevel(level);
if (particleEngine != NULL) particleEngine->setLevel(level);
gameMode->adjustPlayer(player);
gui.onLevelGenerated();
#if defined(WIN32) || defined(RPI)
if (_commandServer) {
delete _commandServer;
}
_commandServer = new CommandServer(this);
_commandServer->init(commandPort);
#endif
}
void MinecraftClient::respawnPlayer() {
// RESET THE FRACKING PLAYER HERE
//bool slowCheck = false;
//for (int i = 0; i < level->entities.size(); ++i)
// if (level->entities[i] == player) slowCheck = true;
//
//LOGI("Has entity? %d, %d\n", level->getEntity(player->entityId), slowCheck);
resetPlayer(player);
// tell server (or other client) that we re-spawned
RespawnPacket packet(player);
raknetInstance->send(packet);
}
void MinecraftClient::audioEngineOn() {
soundEngine->enable(true);
}
void MinecraftClient::audioEngineOff() {
soundEngine->enable(false);
}
void MinecraftClient::setIsCreativeMode(bool isCreative) {
Minecraft::setIsCreativeMode(isCreative);
if (player)
gameMode->initAbilities(player->abilities);
}
void MinecraftClient::optionUpdated(OptionId option, bool value ) {
Minecraft::optionUpdated(option, value);
if (option == OPTIONS_USE_TOUCHSCREEN) {
_reloadInput();
} else if (option == OPTIONS_VSYNC) {
m_platform->setVSync(value);
}
}
void MinecraftClient::optionUpdated(OptionId option, float value ) {
Minecraft::optionUpdated(option, value);
// #ifndef STANDALONE_SERVER
// if(option == OPTIONS_PIXELS_PER_MILLIMETER) {
// pixelCalcUi.setPixelsPerMillimeter(value * Gui::InvGuiScale);
// pixelCalc.setPixelsPerMillimeter(value);
// }
// #endif
}
void MinecraftClient::optionUpdated(OptionId option, int value ) {
Minecraft::optionUpdated(option, value);
if(option == OPTIONS_GUI_SCALE) {
// reapply screen scaling using current window size
setSize(width, height);
}
if (option == OPTIONS_DIFFICULTY && level) {
level->difficulty = options.getIntValue(OPTIONS_DIFFICULTY);
}
}
// @attn shittttt
void MinecraftClient::setLevel(Level* level, const std::string& message /* ="" */, Player* forceInsertPlayer /* = NULL */) {
Minecraft::setLevel(level, message, forceInsertPlayer);
cameraTargetPlayer = NULL;
if (level != NULL) {
if (!player && forceInsertPlayer)
{
player = forceInsertPlayer;
player->resetPos(false);
//level->addEntity(forceInsertPlayer);
}
else if (player != NULL) {
player->resetPos(false);
if (level != NULL) {
level->addEntity(player);
}
}
} else {
player = NULL;
}
}
void MinecraftClient::hostMultiplayer(int port) {
Minecraft::hostMultiplayer(port);
raknetInstance->host(options.getStringValue(OPTIONS_USERNAME), port);
}
std::string MinecraftClient::getServerName() {
return options.getStringValue(OPTIONS_USERNAME);
}
void MinecraftClient::locateMultiplayer() {
isLookingForMultiplayer = true;
raknetInstance->pingForHosts(19132);
netCallback = new ClientSideNetworkHandler(this, raknetInstance);
}
void MinecraftClient::cancelLocateMultiplayer() {
isLookingForMultiplayer = false;
raknetInstance->stopPingForHosts();
delete netCallback;
netCallback = NULL;
}
bool MinecraftClient::joinMultiplayer( const PingedCompatibleServer& server ) {
if (isLookingForMultiplayer && netCallback) {
isLookingForMultiplayer = false;
return raknetInstance->connect(server.address.ToString(false), server.address.GetPort());
}
return false;
}
bool MinecraftClient::joinMultiplayerFromString( const std::string& server ) {
std::string ip = "";
std::string port = "19132";
size_t pos = server.find(":");
if (pos != std::string::npos) {
ip = server.substr(0, pos);
port = server.substr(pos + 1);
} else {
ip = server;
}
printf("%s \n", port.c_str());
if (isLookingForMultiplayer && netCallback) {
isLookingForMultiplayer = false;
printf("test");
int portNum = atoi(port.c_str());
return raknetInstance->connect(ip.c_str(), portNum);
}
return false;
}
bool MinecraftClient::isKindleFire(int kindleVersion) {
// if (kindleVersion != 1)
// return false;
// std::string model = platform()->getPlatformStringVar(PlatformStringVars::DEVICE_BUILD_MODEL);
// std::string modelLower(model);
// std::transform(modelLower.begin(), modelLower.end(), modelLower.begin(), tolower);
// return (modelLower.find("kindle") != std::string::npos) && (modelLower.find("fire") != std::string::npos);
return false;
}
bool MinecraftClient::transformResolution(int* w, int* h)
{
bool changed = false;
// Kindle Fire 1: reporting wrong height in
// certain cases (e.g. after screen lock)
if (isKindleFire(1) && *h >= 560 && *h <= 620) {
*h = 580;
changed = true;
}
return changed;
}
void MinecraftClient::onBlockDestroyed(Player* player, int x, int y, int z, int face) {
particleEngine->destroy(x, y, z);
soundEngine->play(oldTile->soundType->getBreakSound(), x + 0.5f, y + 0.5f, z + 0.5f, (oldTile->soundType->getVolume() + 1) / 2, oldTile->soundType->getPitch() * 0.8f);
if (player == this->player) {
if (options.getBooleanValue(OPTIONS_DESTROY_VIBRATION)) minecraft.platform()->vibrate(24);
RemoveBlockPacket packet(player, x, y, z);
minecraft.raknetInstance->send(packet);
}
}