Added Hand Sway (not an option yet) from b1,8 Fire works properly in multiplayer now and also renders on undead mobs in sunlight now hopefully fixed bow stuck bug on touchscreen devices Fire on entites uses updated rendering from b1.6.6 Shadow bug fix attempt the 15th Added looking at block in debug screen Zombie Pigman now renders his hat layer Temporarily disabled most randomlevelsource cave, canyon and tall grass code just for this commit to test performance
1689 lines
43 KiB
C++
Executable File
1689 lines
43 KiB
C++
Executable File
#include "Minecraft.h"
|
|
#include "Options.h"
|
|
#include "client/Options.h"
|
|
#include "client/player/input/IBuildInput.h"
|
|
#include "platform/input/Keyboard.h"
|
|
#include "world/item/Item.h"
|
|
#include "world/item/ItemInstance.h"
|
|
#include <string>
|
|
#include <cstdlib>
|
|
|
|
|
|
#if defined(APPLE_DEMO_PROMOTION)
|
|
#define NO_NETWORK
|
|
#endif
|
|
|
|
#if defined(RPI)
|
|
#define CREATORMODE
|
|
#endif
|
|
#include "../network/RakNetInstance.h"
|
|
#include "../network/ClientSideNetworkHandler.h"
|
|
#include "../network/ServerSideNetworkHandler.h"
|
|
//#include "../network/Packet.h"
|
|
#include "../world/entity/player/Inventory.h"
|
|
#include "../world/level/tile/Tile.h"
|
|
#include "../world/level/storage/LevelStorageSource.h"
|
|
#include "../world/level/storage/LevelStorage.h"
|
|
#include "player/input/KeyboardInput.h"
|
|
#include "player/input/ControllerTurnInput.h"
|
|
#include "player/input/XperiaPlayInput.h"
|
|
#include "world/level/chunk/ChunkSource.h"
|
|
|
|
#ifndef STANDALONE_SERVER
|
|
#include "player/input/touchscreen/TouchInputHolder.h"
|
|
#include "particle/ParticleEngine.h"
|
|
#include "gui/Screen.h"
|
|
#include "gui/Font.h"
|
|
#include "gui/screens/RenameMPLevelScreen.h"
|
|
#include "gui/screens/ConsoleScreen.h"
|
|
#include "gui/screens/ChatScreen.h"
|
|
#include "sound/SoundEngine.h"
|
|
#include "player/input/touchscreen/TouchscreenInput.h"
|
|
#include "renderer/Chunk.h"
|
|
#include "gui/screens/PrerenderTilesScreen.h"
|
|
#include "renderer/Textures.h"
|
|
#include "gui/screens/DeathScreen.h"
|
|
#include "gui/screens/FurnaceScreen.h"
|
|
#include "gui/screens/ArmorScreen.h"
|
|
#include "renderer/tileentity/TileEntityRenderDispatcher.h"
|
|
#include "renderer/ptexture/DynamicTexture.h"
|
|
#include "renderer/GameRenderer.h"
|
|
#include "renderer/ItemInHandRenderer.h"
|
|
#include "renderer/LevelRenderer.h"
|
|
#include "renderer/entity/EntityRenderDispatcher.h"
|
|
#include "gui/Screen.h"
|
|
#include "gui/Font.h"
|
|
#include "gui/screens/RenameMPLevelScreen.h"
|
|
#include "sound/SoundEngine.h"
|
|
#endif // STANDALONE_SERVER
|
|
|
|
#include "player/LocalPlayer.h"
|
|
#include "gamemode/CreativeMode.h"
|
|
#include "gamemode/SurvivalMode.h"
|
|
#include "player/LocalPlayer.h"
|
|
#include "../platform/CThread.h"
|
|
#include "../platform/input/Mouse.h"
|
|
#include "../AppPlatform.h"
|
|
#include "../LicenseCodes.h"
|
|
#include "../util/PerfTimer.h"
|
|
#include "../util/PerfRenderer.h"
|
|
#include "player/input/MouseBuildInput.h"
|
|
|
|
#include "player/input/IInputHolder.h"
|
|
|
|
#include "player/input/MouseTurnInput.h"
|
|
#include "../world/entity/MobFactory.h"
|
|
#include "../world/level/MobSpawner.h"
|
|
#include "../util/Mth.h"
|
|
#include "../network/packet/InteractPacket.h"
|
|
#include "../network/packet/RespawnPacket.h"
|
|
#include "../network/packet/AdventureSettingsPacket.h"
|
|
#include "../network/packet/SetSpawnPositionPacket.h"
|
|
#include "IConfigListener.h"
|
|
#include "../world/entity/MobCategory.h"
|
|
#include "../world/Difficulty.h"
|
|
#include "../server/ServerLevel.h"
|
|
|
|
#ifdef CREATORMODE
|
|
#include "../server/CreatorLevel.h"
|
|
#endif
|
|
|
|
#include "../network/command/CommandServer.h"
|
|
#include "gamemode/CreatorMode.h"
|
|
|
|
#include "../world/level/GrassColor.h"
|
|
#include "renderer/LevelRenderer.h"
|
|
#include "particle/ParticleEngine.h"
|
|
#include "../world/level/Level.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*/
|
|
}
|
|
|
|
/*static*/
|
|
const char* Minecraft::progressMessages[] = {
|
|
"Locating server",
|
|
"Building terrain",
|
|
"Preparing",
|
|
"Saving chunks"
|
|
};
|
|
|
|
int Minecraft::customDebugId = Minecraft::CDI_NONE;
|
|
|
|
#if defined(_MSC_VER)
|
|
#pragma warning( disable : 4355 ) // 'this' pointer in initialization list which is perfectly legal
|
|
#endif
|
|
|
|
bool Minecraft::useAmbientOcclusion = false;
|
|
|
|
Minecraft::Minecraft() :
|
|
level(NULL),
|
|
player(NULL),
|
|
cameraTargetPlayer(NULL),
|
|
levelRenderer(NULL),
|
|
gameRenderer(NULL),
|
|
#ifndef STANDALONE_SERVER
|
|
particleEngine(NULL),
|
|
_perfRenderer(NULL),
|
|
#endif
|
|
_commandServer(NULL),
|
|
#ifndef STANDALONE_SERVER
|
|
textures(NULL),
|
|
#endif
|
|
lastTickTime(-1),
|
|
lastTime(0),
|
|
ticksSinceLastUpdate(0),
|
|
gameMode(NULL),
|
|
mouseGrabbed(true),
|
|
missTime(0),
|
|
pause(false),
|
|
_running(false),
|
|
timer(20),
|
|
#ifndef STANDALONE_SERVER
|
|
gui(this),
|
|
#endif
|
|
netCallback(NULL),
|
|
#ifndef STANDALONE_SERVER
|
|
screen(NULL),
|
|
font(NULL),
|
|
#endif
|
|
screenMutex(false),
|
|
#ifndef STANDALONE_SERVER
|
|
scheduledScreen(NULL),
|
|
hasScheduledScreen(false),
|
|
soundEngine(NULL),
|
|
#endif
|
|
ticks(0),
|
|
isGeneratingLevel(false),
|
|
_hasSignaledGeneratingLevelFinished(true),
|
|
generateLevelThread(NULL),
|
|
progressStagePercentage(0),
|
|
progressStageStatusId(0),
|
|
isLookingForMultiplayer(false),
|
|
_licenseId(LicenseCodes::WAIT_PLATFORM_NOT_READY),
|
|
inputHolder(0),
|
|
_supportsNonTouchscreen(false),
|
|
#ifndef STANDALONE_SERVER
|
|
screenChooser(this),
|
|
#endif
|
|
width(1), height(1),
|
|
//_respawnPlayerTicks(-1),
|
|
#ifdef __APPLE__
|
|
_isSuperFast(false),
|
|
#endif
|
|
_powerVr(false),
|
|
commandPort(4711),
|
|
reserved_d1(0),reserved_d2(0),
|
|
reserved_f1(0),reserved_f2(0), options(this)
|
|
{
|
|
//#ifdef ANDROID
|
|
|
|
#if defined(NO_NETWORK)
|
|
raknetInstance = new IRakNetInstance();
|
|
#else
|
|
raknetInstance = new RakNetInstance();
|
|
#endif
|
|
#ifndef STANDALONE_SERVER
|
|
soundEngine = new SoundEngine(20.0f);
|
|
soundEngine->init(this, &options);
|
|
#endif
|
|
//setupPieces();
|
|
}
|
|
|
|
Minecraft::~Minecraft()
|
|
{
|
|
delete netCallback;
|
|
delete raknetInstance;
|
|
#ifndef STANDALONE_SERVER
|
|
delete levelRenderer;
|
|
delete gameRenderer;
|
|
delete particleEngine;
|
|
|
|
delete soundEngine;
|
|
#endif
|
|
delete gameMode;
|
|
#ifndef STANDALONE_SERVER
|
|
delete font;
|
|
delete textures;
|
|
|
|
if (screen != NULL) {
|
|
delete screen;
|
|
screen = NULL;
|
|
}
|
|
#endif
|
|
if (level != NULL) {
|
|
level->saveGame();
|
|
if (level->getChunkSource())
|
|
level->getChunkSource()->saveAll(true);
|
|
delete level->getLevelStorage();
|
|
delete level;
|
|
level = NULL;
|
|
}
|
|
|
|
//delete player;
|
|
delete inputHolder;
|
|
|
|
delete storageSource;
|
|
delete _perfRenderer;
|
|
delete _commandServer;
|
|
|
|
MobFactory::clearStaticTestMobs();
|
|
#ifndef STANDALONE_SERVER
|
|
EntityRenderDispatcher::destroy();
|
|
#endif
|
|
}
|
|
|
|
// Only called by server
|
|
void Minecraft::selectLevel( const std::string& levelId, const std::string& levelName, const LevelSettings& settings )
|
|
{
|
|
#if defined(CREATORMODE)
|
|
level = new CreatorLevel(
|
|
#else
|
|
level = new ServerLevel(
|
|
#endif
|
|
storageSource->selectLevel(levelId, false),
|
|
levelName,
|
|
settings,
|
|
SharedConstants::GeneratorVersion);
|
|
|
|
// note: settings is useless beyond this point, since it's
|
|
// either copied to LevelData (or LevelData read from file)
|
|
setLevel(level, "Generating level");
|
|
setIsCreativeMode(level->getLevelData()->getGameType() == GameType::Creative);
|
|
_running = true;
|
|
}
|
|
|
|
void Minecraft::setLevel(Level* level, const std::string& message /* ="" */, LocalPlayer* forceInsertPlayer /* = NULL */) {
|
|
cameraTargetPlayer = NULL;
|
|
LOGI("Seed is %ld\n", level->getSeed());
|
|
|
|
if (level != NULL) {
|
|
level->raknetInstance = raknetInstance;
|
|
gameMode->initLevel(level);
|
|
|
|
if (!player && forceInsertPlayer)
|
|
{
|
|
player = forceInsertPlayer;
|
|
player->resetPos(false);
|
|
//level->addEntity(forceInsertPlayer);
|
|
}
|
|
else if (player != NULL) {
|
|
player->resetPos(false);
|
|
if (level != NULL) {
|
|
level->addEntity(player);
|
|
}
|
|
}
|
|
this->level = level;
|
|
_hasSignaledGeneratingLevelFinished = false;
|
|
#ifdef STANDALONE_SERVER
|
|
const bool threadedLevelCreation = false;
|
|
#else
|
|
const bool threadedLevelCreation = true;
|
|
#endif
|
|
|
|
if (threadedLevelCreation) {
|
|
// Threaded
|
|
// "Lock"
|
|
isGeneratingLevel = true;
|
|
generateLevelThread = new CThread(Minecraft::prepareLevel_tspawn, this);
|
|
} else {
|
|
// Non-threaded
|
|
generateLevel("Currently not used", level);
|
|
}
|
|
} else {
|
|
player = NULL;
|
|
}
|
|
|
|
this->lastTickTime = 0;
|
|
this->_running = true;
|
|
}
|
|
|
|
void Minecraft::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;
|
|
#ifndef STANDALONE_SERVER
|
|
levelRenderer->setLevel(NULL);
|
|
particleEngine->setLevel(NULL);
|
|
#endif
|
|
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;
|
|
#ifndef STANDALONE_SERVER
|
|
gui.clearMessages();
|
|
if (renameLevel) {
|
|
setScreen(new RenameMPLevelScreen(LevelStorageSource::TempLevelId));
|
|
}
|
|
else
|
|
screenChooser.setScreen(SCREEN_STARTMENU);
|
|
#endif
|
|
}
|
|
|
|
void Minecraft::prepareLevel(const std::string& title) {
|
|
LOGI("status: 1\n");
|
|
progressStageStatusId = 1;
|
|
|
|
Stopwatch A, B, C, D;
|
|
A.start();
|
|
|
|
Stopwatch L;
|
|
|
|
// Dont update lights if we load the level (ok, actually just with leveldata version=1.+(?))
|
|
if (!level->isNew())
|
|
level->setUpdateLights(false);
|
|
|
|
int Max = CHUNK_CACHE_WIDTH * CHUNK_CACHE_WIDTH;
|
|
int pp = 0;
|
|
for (int x = 8; x < (CHUNK_CACHE_WIDTH * CHUNK_WIDTH); x += CHUNK_WIDTH) {
|
|
for (int z = 8; z < (CHUNK_CACHE_WIDTH * CHUNK_WIDTH); z += CHUNK_WIDTH) {
|
|
progressStagePercentage = 100 * pp++ / Max;
|
|
//printf("level generation progress %d\n", progressStagePercentage);
|
|
B.start();
|
|
level->getTile(x, 64, z);
|
|
B.stop();
|
|
L.start();
|
|
if (level->isNew())
|
|
while (level->updateLights())
|
|
;
|
|
L.stop();
|
|
}
|
|
}
|
|
A.stop();
|
|
level->setUpdateLights(true);
|
|
|
|
C.start();
|
|
for (int x = 0; x < CHUNK_CACHE_WIDTH; x++)
|
|
{
|
|
for (int z = 0; z < CHUNK_CACHE_WIDTH; z++)
|
|
{
|
|
LevelChunk* chunk = level->getChunk(x, z);
|
|
if (chunk && !chunk->createdFromSave)
|
|
{
|
|
chunk->unsaved = false;
|
|
chunk->clearUpdateMap();
|
|
}
|
|
}
|
|
}
|
|
C.stop();
|
|
|
|
LOGI("status: 3\n");
|
|
progressStageStatusId = 3;
|
|
if (level->isNew()) {
|
|
level->setInitialSpawn(); // @note: should obviously be called from Level itself
|
|
level->saveLevelData();
|
|
level->getChunkSource()->saveAll(false);
|
|
level->saveGame();
|
|
} else {
|
|
level->saveLevelData();
|
|
level->loadEntities();
|
|
}
|
|
|
|
progressStagePercentage = -1;
|
|
progressStageStatusId = 2;
|
|
LOGI("status: 2\n");
|
|
|
|
D.start();
|
|
level->prepare();
|
|
D.stop();
|
|
|
|
A.print("Generate level: ");
|
|
L.print(" - light: ");
|
|
B.print(" - getTl: ");
|
|
C.print(" - clear: ");
|
|
D.print(" - prepr: ");
|
|
progressStageStatusId = 0;
|
|
}
|
|
|
|
void Minecraft::update() {
|
|
//LOGI("Enter Update\n");
|
|
|
|
if (Options::debugGl)
|
|
LOGI(">>>>>>>>>>\n");
|
|
|
|
TIMER_PUSH("root");
|
|
|
|
//if (level) {
|
|
// LOGI("numplayers: %d\n", level->players.size());
|
|
// for (int i = 0; i < level->players.size(); ++i) {
|
|
// Player* p = level->players[i];
|
|
// bool inEnt = std::find(level->entities.begin(), level->entities.end(), p) != level->entities.end();
|
|
// LOGI(" %p, %d, %d - in? %d\n", p, p->entityId, p->owner.ToUint32(p->owner), inEnt);
|
|
// }
|
|
//}
|
|
|
|
// If we're paused (local world / invisible server), freeze gameplay and
|
|
// networking and only keep UI responsive.
|
|
bool freezeGame = pause;
|
|
|
|
if (!freezeGame) {
|
|
timer.advanceTime();
|
|
}
|
|
|
|
if (raknetInstance && !freezeGame) {
|
|
raknetInstance->runEvents(netCallback);
|
|
}
|
|
|
|
TIMER_PUSH("tick");
|
|
int toTick = freezeGame ? 1 : timer.ticks;
|
|
if (!freezeGame) timer.ticks = 0;
|
|
for (int i = 0; i < toTick; ++i, ++ticks)
|
|
tick(i, toTick-1);
|
|
|
|
TIMER_POP_PUSH("updatelights");
|
|
if (level && !isGeneratingLevel) {
|
|
level->updateLights();
|
|
}
|
|
TIMER_POP();
|
|
|
|
#ifndef STANDALONE_SERVER
|
|
if (gameMode != NULL) gameMode->render(timer.a);
|
|
TIMER_PUSH("sound");
|
|
soundEngine->update(player, timer.a);
|
|
TIMER_POP_PUSH("render");
|
|
gameRenderer->render(timer.a);
|
|
TIMER_POP();
|
|
#else
|
|
CThread::sleep(1);
|
|
#endif
|
|
#ifndef STANDALONE_SERVER
|
|
Multitouch::resetThisUpdate();
|
|
#endif
|
|
|
|
#ifndef STANDALONE_SERVER
|
|
TIMER_POP();
|
|
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;
|
|
}
|
|
#endif
|
|
//LOGI("Exit Update\n");
|
|
}
|
|
|
|
void Minecraft::tick(int nTick, int maxTick) {
|
|
if (missTime > 0) missTime--;
|
|
#ifndef STANDALONE_SERVER
|
|
if (!screen && player) {
|
|
if (player->health <= 0) {
|
|
setScreen(new DeathScreen());
|
|
}
|
|
}
|
|
#endif
|
|
TIMER_PUSH("gameMode");
|
|
if (level && !pause) {
|
|
gameMode->tick();
|
|
}
|
|
|
|
TIMER_POP_PUSH("commandServer");
|
|
if (level && _commandServer) {
|
|
_commandServer->tick();
|
|
}
|
|
|
|
TIMER_POP_PUSH("input");
|
|
tickInput();
|
|
#ifndef STANDALONE_SERVER
|
|
TIMER_POP_PUSH("gui");
|
|
gui.tick();
|
|
#endif
|
|
//
|
|
// 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)
|
|
{
|
|
if (!pause) {
|
|
#ifndef STANDALONE_SERVER
|
|
TIMER_POP_PUSH("gameRenderer");
|
|
gameRenderer->tick(nTick, maxTick);
|
|
|
|
TIMER_POP_PUSH("levelRenderer");
|
|
levelRenderer->tick();
|
|
#endif
|
|
level->difficulty = options.getIntValue(OPTIONS_DIFFICULTY);
|
|
if (level->isClientSide) level->difficulty = Difficulty::EASY;
|
|
|
|
TIMER_POP_PUSH("level");
|
|
level->tickEntities();
|
|
level->tick();
|
|
#ifndef STANDALONE_SERVER
|
|
TIMER_POP_PUSH("animateTick");
|
|
if (player) {
|
|
level->animateTick(Mth::floor(player->x), Mth::floor(player->y), Mth::floor(player->z));
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
#ifndef STANDALONE_SERVER
|
|
textures->loadAndBindTexture("terrain.png");
|
|
if (!pause && !(screen && !screen->renderGameBehind())) {
|
|
#if !defined(RPI)
|
|
#ifdef __APPLE__
|
|
if (isSuperFast())
|
|
#endif
|
|
{
|
|
if (nTick == maxTick) {
|
|
TIMER_POP_PUSH("textures");
|
|
textures->tick(true);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
TIMER_POP_PUSH("particles");
|
|
if (!pause) {
|
|
particleEngine->tick();
|
|
}
|
|
if (screen) {
|
|
screenMutex = true;
|
|
screen->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();
|
|
#endif
|
|
TIMER_POP();
|
|
}
|
|
|
|
class InputRAII {
|
|
public:
|
|
~InputRAII() {
|
|
#ifndef STANDALONE_SERVER
|
|
Mouse::reset();
|
|
Keyboard::reset();
|
|
#endif
|
|
}
|
|
};
|
|
|
|
void Minecraft::tickInput() {
|
|
#ifndef STANDALONE_SERVER
|
|
InputRAII raiiInput;
|
|
|
|
if (screen && !screen->passEvents) {
|
|
screenMutex = true;
|
|
screen->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 (!screen && 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 (screen && (dynamic_cast<ChatScreen*>(screen) || dynamic_cast<ConsoleScreen*>(screen))) {
|
|
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 (!screen && 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 (!screen && 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 (options.getBooleanValue(OPTIONS_RENDER_DEBUG)) {
|
|
if (key >= '0' && key <= '9') {
|
|
_perfRenderer->debugFpsMeterKeyPress(key - '0');
|
|
}
|
|
}
|
|
|
|
if (key == Keyboard::KEY_ESCAPE)
|
|
pauseGame(false);
|
|
|
|
#ifdef PLATFORM_DESKTOP
|
|
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 (!useTouchscreen()) {
|
|
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();
|
|
if (player && player->isUsingItem()) {
|
|
gameMode->releaseUsingItem(player);
|
|
}
|
|
}
|
|
} 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);
|
|
}
|
|
}
|
|
|
|
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();
|
|
#endif
|
|
}
|
|
|
|
void Minecraft::handleBuildAction(BuildActionIntention* action) {
|
|
#ifndef STANDALONE_SERVER
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
bool Minecraft::isOnlineClient()
|
|
{
|
|
return (level != NULL && level->isClientSide);
|
|
}
|
|
|
|
bool Minecraft::isOnline()
|
|
{
|
|
return netCallback != NULL;
|
|
}
|
|
|
|
void Minecraft::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;
|
|
|
|
#ifndef STANDALONE_SERVER
|
|
if (screen != NULL) return;
|
|
screenChooser.setScreen(isBackPaused? SCREEN_PAUSEPREV : SCREEN_PAUSE);
|
|
#endif
|
|
}
|
|
void Minecraft::gameLostFocus() {
|
|
#ifndef STANDALONE_SERVER
|
|
if(screen != NULL) {
|
|
screen->lostFocus();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
void Minecraft::setScreen( Screen* screen )
|
|
{
|
|
#ifndef STANDALONE_SERVER
|
|
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->screen != NULL) {
|
|
this->screen->removed();
|
|
delete this->screen;
|
|
}
|
|
|
|
this->screen = 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();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void Minecraft::grabMouse()
|
|
{
|
|
#ifndef STANDALONE_SERVER
|
|
if (mouseGrabbed) return;
|
|
mouseGrabbed = true;
|
|
mouseHandler.grab();
|
|
//setScreen(NULL);
|
|
#endif
|
|
}
|
|
|
|
void Minecraft::releaseMouse()
|
|
{
|
|
#ifndef STANDALONE_SERVER
|
|
if (!mouseGrabbed) {
|
|
return;
|
|
}
|
|
if (player) {
|
|
player->releaseAllKeys();
|
|
}
|
|
mouseGrabbed = false;
|
|
mouseHandler.release();
|
|
#endif
|
|
}
|
|
|
|
bool Minecraft::useTouchscreen() {
|
|
#if defined(TARGET_OS_IPHONE)
|
|
return true;
|
|
#elif defined(RPI)
|
|
return false;
|
|
#endif
|
|
return options.getBooleanValue(OPTIONS_USE_TOUCHSCREEN) && !_supportsNonTouchscreen;
|
|
}
|
|
bool Minecraft::supportNonTouchScreen() {
|
|
return _supportsNonTouchscreen;
|
|
}
|
|
void Minecraft::init()
|
|
{
|
|
#ifndef STANDALONE_SERVER
|
|
checkGlError("Init enter");
|
|
|
|
_supportsNonTouchscreen = !platform()->supportsTouchscreen();
|
|
|
|
LOGI("IS TOUCHSCREEN? %d\n", options.getBooleanValue(OPTIONS_USE_TOUCHSCREEN));
|
|
|
|
textures = new Textures(&options, platform());
|
|
textures->addDynamicTexture(new WaterTexture());
|
|
textures->addDynamicTexture(new WaterSideTexture());
|
|
textures->addDynamicTexture(new LavaTexture());
|
|
textures->addDynamicTexture(new LavaSideTexture());
|
|
textures->addDynamicTexture(new FireTexture(0));
|
|
textures->addDynamicTexture(new FireTexture(1));
|
|
gui.texturesLoaded(textures);
|
|
|
|
levelRenderer = new LevelRenderer(this);
|
|
gameRenderer = new GameRenderer(this);
|
|
particleEngine = new ParticleEngine(level, textures);
|
|
|
|
// 4j's code for reference
|
|
// FoliageColor::init(textures->loadTexturePixels(L"misc/foliagecolor.png"));
|
|
|
|
|
|
// my code
|
|
TextureId foliageId = (textures->loadTexture("misc/foliagecolor.png")); // loading the uh png for foliage color
|
|
int* foliagePixels = textures->loadTexturePixels(foliageId, "misc/foliagecolor.png");
|
|
// now i can finally initialize foliage color, probably not the best way to handle this but i cant be arsed rn
|
|
FoliageColor::init(foliagePixels);
|
|
|
|
TextureId grassId = (textures->loadTexture("misc/grasscolor.png")); // loading the uh png for foliage color
|
|
int* grassPixels = textures->loadTexturePixels(grassId, "misc/grasscolor.png");
|
|
GrassColor::init(grassPixels);
|
|
|
|
bool tint = options.getBooleanValue(OPTIONS_FOLIAGE_TINT); // finally, toggleable foliage color
|
|
FoliageColor::setUseTint(tint);
|
|
GrassColor::setUseTint(tint);
|
|
|
|
bool sideTint = options.getBooleanValue(OPTIONS_TINTED_SIDE);
|
|
TileRenderer::setUseTint(sideTint);
|
|
|
|
|
|
// Platform specific initialization here
|
|
font = new Font(&options, "font/default8.png", textures);
|
|
|
|
_perfRenderer = new PerfRenderer(this, font);
|
|
|
|
checkGlError("Init complete");
|
|
#endif
|
|
|
|
options.load();
|
|
|
|
setIsCreativeMode(false); // false means it's Survival Mode
|
|
reloadOptions();
|
|
}
|
|
|
|
void Minecraft::setSize(int w, int h) {
|
|
#ifndef STANDALONE_SERVER
|
|
transformResolution(&w, &h);
|
|
|
|
width = w;
|
|
height = h;
|
|
int screenWidth;
|
|
int screenHeight;
|
|
//#ifdef PLATFORM_DESKTOP
|
|
if (options.getBooleanValue(OPTIONS_WINDOW_SCALE)){ // scales with resolution using a formula instead of having hardcoded if checks
|
|
int guiScale = options.getIntValue(OPTIONS_GUI_SCALE);
|
|
if (guiScale == 0) {
|
|
guiScale = 1000;
|
|
}
|
|
|
|
// determine gui scale, optionally overriding auto
|
|
|
|
|
|
Gui::GuiScale = (float)Mth::Min(guiScale, Mth::Max(1, Mth::Min(width / 320, height / 240)));
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
|
|
// if (platform()) {
|
|
// float pixelsPerMillimeter = options.getProgressValue(&Option::PIXELS_PER_MILLIMETER);
|
|
// pixelCalc.setPixelsPerMillimeter(pixelsPerMillimeter);
|
|
// pixelCalcUi.setPixelsPerMillimeter(pixelsPerMillimeter * Gui::InvGuiScale);
|
|
// }
|
|
|
|
}
|
|
|
|
|
|
Gui::InvGuiScale = 1.0f / Gui::GuiScale;
|
|
screenWidth = (int)(width * Gui::InvGuiScale);
|
|
screenHeight = (int)(height * Gui::InvGuiScale);
|
|
|
|
Config config = createConfig(this);
|
|
gui.onConfigChanged(config);
|
|
|
|
if (screen)
|
|
screen->setSize(screenWidth, screenHeight);
|
|
|
|
if (inputHolder)
|
|
inputHolder->onConfigChanged(config);
|
|
//LOGI("Setting size: %d, %d: %f\n", width, height, Gui::InvGuiScale);
|
|
|
|
#ifdef WIN32
|
|
char resbuf[128];
|
|
sprintf(resbuf, " %d x %d @ scale %.2f", width, height, Gui::GuiScale);
|
|
//gui.addMessage(resbuf);
|
|
#endif
|
|
#endif /* STANDALONE_SERVER */
|
|
}
|
|
|
|
void Minecraft::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 Minecraft::_reloadInput() {
|
|
#ifndef STANDALONE_SERVER
|
|
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();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
//
|
|
// Multiplayer
|
|
//
|
|
void Minecraft::locateMultiplayer() {
|
|
isLookingForMultiplayer = true;
|
|
|
|
raknetInstance->pingForHosts(19132);
|
|
netCallback = new ClientSideNetworkHandler(this, raknetInstance);
|
|
}
|
|
|
|
void Minecraft::cancelLocateMultiplayer() {
|
|
isLookingForMultiplayer = false;
|
|
|
|
raknetInstance->stopPingForHosts();
|
|
|
|
delete netCallback;
|
|
netCallback = NULL;
|
|
}
|
|
|
|
bool Minecraft::joinMultiplayer( const PingedCompatibleServer& server )
|
|
{
|
|
if (isLookingForMultiplayer && netCallback) {
|
|
isLookingForMultiplayer = false;
|
|
return raknetInstance->connect(server.address.ToString(false), server.address.GetPort());
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Minecraft::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;
|
|
}
|
|
|
|
void Minecraft::hostMultiplayer(int port) {
|
|
// Tear down last instance
|
|
raknetInstance->disconnect();
|
|
delete netCallback;
|
|
netCallback = NULL;
|
|
|
|
#if !defined(NO_NETWORK)
|
|
netCallback = new ServerSideNetworkHandler(this, raknetInstance);
|
|
#ifdef STANDALONE_SERVER
|
|
raknetInstance->host("Server", port, 16);
|
|
#else
|
|
raknetInstance->host(options.getStringValue(OPTIONS_USERNAME), port);
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
//
|
|
// Level generation
|
|
//
|
|
/*static*/
|
|
|
|
void* Minecraft::prepareLevel_tspawn(void *p_param)
|
|
{
|
|
Minecraft* mc = (Minecraft*) p_param;
|
|
mc->generateLevel("Currently not used", mc->level);
|
|
return 0;
|
|
}
|
|
|
|
void Minecraft::generateLevel( const std::string& message, Level* level )
|
|
{
|
|
Stopwatch s;
|
|
s.start();
|
|
prepareLevel(message);
|
|
s.stop();
|
|
s.print("Level generated: ");
|
|
|
|
// "Unlock"
|
|
isGeneratingLevel = false;
|
|
}
|
|
|
|
void Minecraft::_levelGenerated()
|
|
{
|
|
#ifndef STANDALONE_SERVER
|
|
if (player == NULL) {
|
|
player = (LocalPlayer*) gameMode->createPlayer(level);
|
|
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();
|
|
#endif
|
|
|
|
level->validateSpawn();
|
|
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);
|
|
}
|
|
|
|
if (level && level->dimension) {
|
|
|
|
level->dimension->FogType = options.getIntValue(OPTIONS_FOG_TYPE);
|
|
}
|
|
|
|
|
|
this->cameraTargetPlayer = player;
|
|
|
|
std::string serverName = options.getStringValue(OPTIONS_USERNAME) + " - " + level->getLevelData()->levelName;
|
|
|
|
if (raknetInstance->isServer())
|
|
raknetInstance->announceServer(serverName);
|
|
|
|
if (netCallback) {
|
|
netCallback->levelGenerated(level);
|
|
}
|
|
|
|
#if defined(WIN32) || defined(RPI)
|
|
if (_commandServer) {
|
|
delete _commandServer;
|
|
}
|
|
_commandServer = new CommandServer(this);
|
|
_commandServer->init(commandPort);
|
|
#endif
|
|
|
|
// Hack to (hopefully) get the players to show (note: in LevelListener
|
|
// instead, since adding yourself always generates a entityAdded)
|
|
//EntityRenderDispatcher::getInstance()->onGraphicsReset();
|
|
_hasSignaledGeneratingLevelFinished = true;
|
|
}
|
|
|
|
std::string Minecraft::gatherStats1() {
|
|
#ifndef STANDALONE_SERVER
|
|
return levelRenderer->gatherStats1();
|
|
#endif
|
|
return "Blank";
|
|
}
|
|
|
|
std::string Minecraft::gatherStats2() {
|
|
#ifndef STANDALONE_SERVER
|
|
return levelRenderer->gatherStats2();
|
|
#endif
|
|
return "Blank";
|
|
}
|
|
|
|
std::string Minecraft::gatherStats3() {
|
|
#ifndef STANDALONE_SERVER
|
|
return ("P: " + particleEngine->countParticles() + ". T: " + (level->gatherStats()));
|
|
#endif
|
|
return "Blank";
|
|
}
|
|
|
|
std::string Minecraft::gatherStats4() {
|
|
return level->gatherChunkSourceStats();
|
|
}
|
|
|
|
|
|
|
|
|
|
Player* Minecraft::respawnPlayer(int playerId) {
|
|
for (unsigned int i = 0; i < level->players.size(); ++i) {
|
|
if (level->players[i]->entityId == playerId) {
|
|
resetPlayer(level->players[i]);
|
|
return level->players[i];
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void Minecraft::resetPlayer(Player* player) {
|
|
level->validateSpawn();
|
|
player->reset();
|
|
|
|
Pos p;
|
|
if(player->hasRespawnPosition()) {
|
|
p = player->getRespawnPosition();
|
|
}
|
|
else {
|
|
p = level->getSharedSpawnPos();
|
|
}
|
|
player->setPos((float)p.x + 0.5f, (float)p.y + 1.0f, (float)p.z + 0.5f);
|
|
player->resetPos(true);
|
|
|
|
if (isCreativeMode())
|
|
player->inventory->clearInventoryWithDefault();
|
|
}
|
|
|
|
void Minecraft::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 Minecraft::onGraphicsReset()
|
|
{
|
|
#ifndef STANDALONE_SERVER
|
|
textures->clear();
|
|
|
|
font->onGraphicsReset();
|
|
gui.onGraphicsReset();
|
|
|
|
if (levelRenderer) levelRenderer->onGraphicsReset();
|
|
if (gameRenderer) gameRenderer->onGraphicsReset();
|
|
|
|
EntityRenderDispatcher::getInstance()->onGraphicsReset();
|
|
TileEntityRenderDispatcher::getInstance()->onGraphicsReset();
|
|
#endif
|
|
}
|
|
|
|
int Minecraft::getProgressStatusId() {
|
|
return progressStageStatusId;
|
|
}
|
|
|
|
const char* Minecraft::getProgressMessage()
|
|
{
|
|
return progressMessages[progressStageStatusId];
|
|
}
|
|
|
|
bool Minecraft::isLevelGenerated()
|
|
{
|
|
return level != NULL && !isGeneratingLevel;
|
|
}
|
|
|
|
LevelStorageSource* Minecraft::getLevelSource()
|
|
{
|
|
return storageSource;
|
|
}
|
|
|
|
// int Minecraft::getLicenseId() {
|
|
// if (!LicenseCodes::isReady(_licenseId))
|
|
// _licenseId = platform()->checkLicense();
|
|
// return _licenseId;
|
|
// }
|
|
|
|
void Minecraft::audioEngineOn() {
|
|
#ifndef STANDALONE_SERVER
|
|
soundEngine->enable(true);
|
|
#endif
|
|
}
|
|
void Minecraft::audioEngineOff() {
|
|
#ifndef STANDALONE_SERVER
|
|
soundEngine->enable(false);
|
|
#endif
|
|
}
|
|
|
|
void Minecraft::setIsCreativeMode(bool isCreative)
|
|
{
|
|
#ifdef CREATORMODE
|
|
delete gameMode;
|
|
gameMode = new CreatorMode(this);
|
|
_isCreativeMode = true;
|
|
#else
|
|
if (!gameMode || isCreative != _isCreativeMode)
|
|
{
|
|
delete gameMode;
|
|
if (isCreative) gameMode = new CreativeMode(this);
|
|
else gameMode = new SurvivalMode(this);
|
|
_isCreativeMode = isCreative;
|
|
}
|
|
#endif
|
|
if (player)
|
|
gameMode->initAbilities(player->abilities);
|
|
}
|
|
|
|
bool Minecraft::isCreativeMode() {
|
|
return _isCreativeMode;
|
|
}
|
|
|
|
bool Minecraft::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);
|
|
}
|
|
|
|
bool Minecraft::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;
|
|
}
|
|
|
|
ICreator* Minecraft::getCreator()
|
|
{
|
|
#ifdef CREATORMODE
|
|
return ((CreatorMode*)gameMode)->getCreator();
|
|
#else
|
|
return NULL;
|
|
#endif
|
|
}
|
|
|
|
void Minecraft::optionUpdated(OptionId option, bool value ) {
|
|
if(netCallback != NULL && option == OPTIONS_SERVER_VISIBLE) {
|
|
ServerSideNetworkHandler* ss = (ServerSideNetworkHandler*) netCallback;
|
|
ss->allowIncomingConnections(value);
|
|
} else if (option == OPTIONS_USE_TOUCHSCREEN) {
|
|
_reloadInput();
|
|
}
|
|
}
|
|
|
|
void Minecraft::optionUpdated(OptionId option, float value ) {
|
|
// #ifndef STANDALONE_SERVER
|
|
// if(option == OPTIONS_PIXELS_PER_MILLIMETER) {
|
|
// pixelCalcUi.setPixelsPerMillimeter(value * Gui::InvGuiScale);
|
|
// pixelCalc.setPixelsPerMillimeter(value);
|
|
// }
|
|
// #endif
|
|
}
|
|
|
|
void Minecraft::optionUpdated(OptionId option, int value ) {
|
|
if(option == OPTIONS_GUI_SCALE) {
|
|
// reapply screen scaling using current window size
|
|
setSize(width, height);
|
|
}
|
|
}
|