Merge remote-tracking branch 'refs/remotes/origin/main'

This commit is contained in:
Kolyah35
2026-03-21 00:15:24 +03:00
21 changed files with 269 additions and 127 deletions

View File

@@ -113,7 +113,7 @@ cmake --build .
$HOME/Android/Sdk/cmdline-tools/bin/sdkmanager $HOME/Android/Sdk/cmdline-tools/bin/sdkmanager
``` ```
> [!NOTE] > [!Note]
> `sdkmanager` expects the SDK to include a `cmdline-tools/latest/` folder. > `sdkmanager` expects the SDK to include a `cmdline-tools/latest/` folder.
> If you only have `cmdline-tools/bin`, create the required layout: > If you only have `cmdline-tools/bin`, create the required layout:
> >
@@ -134,7 +134,7 @@ cmake --build .
sdkmanager --install "platform-tools" "platforms;android-35" "build-tools;35.0.0" sdkmanager --install "platform-tools" "platforms;android-35" "build-tools;35.0.0"
``` ```
> [!NOTE] > [!Note]
> if you want build.sh to always find the SDK, > if you want build.sh to always find the SDK,
> Set ANDROID_SDK_ROOT in your shell config (~/.bashrc / ~/.profile / ~/.config/fish/config.fish), for example: > Set ANDROID_SDK_ROOT in your shell config (~/.bashrc / ~/.profile / ~/.config/fish/config.fish), for example:
> >
@@ -162,14 +162,14 @@ cmake --build .
7. Extract the archive to `/home/username/`, so that the final directory path is `/home/username/android-ndk-r14b/` 7. Extract the archive to `/home/username/`, so that the final directory path is `/home/username/android-ndk-r14b/`
> [!WARNING] > [!Warning]
> Make sure you dont end up with a nested folder like `/home/username/android-ndk-r14b/android-ndk-r14b/`. > Make sure you dont end up with a nested folder like `/home/username/android-ndk-r14b/android-ndk-r14b/`.
8. Re run `build.sh` 8. Re run `build.sh`
## Web ## Web
1. Download and install **emsdk**: https://emscripten.org/docs/getting_started/downloads.html 1. Download and install **emsdk**: https://emscripten.org/docs/getting_started/downloads.html
> [!NOTE] > [!Note]
> On arch linux you can use AUR: > On arch linux you can use AUR:
> `yay -Sy emsdk` > `yay -Sy emsdk`
@@ -179,7 +179,7 @@ cmake --build .
cmake .. -B . -G Ninja "-DCMAKE_TOOLCHAIN_FILE=$EMSDK/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake" cmake .. -B . -G Ninja "-DCMAKE_TOOLCHAIN_FILE=$EMSDK/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake"
cmake --build . --target MinecraftPE cmake --build . --target MinecraftPE
``` ```
> [!NOTE] > [!Note]
> If you are using VSCode with CMake plugin, you can add Emscripten kit > If you are using VSCode with CMake plugin, you can add Emscripten kit
> 1. Press Ctrl + Shift + P > 1. Press Ctrl + Shift + P
> 2. Type `CMake: Edit User-Local CMake Kits` and hit Enter > 2. Type `CMake: Edit User-Local CMake Kits` and hit Enter

View File

@@ -152,6 +152,7 @@ options.group.graphics=Graphics
options.group.tweaks=Tweaks options.group.tweaks=Tweaks
options.allowSprint=Allow sprint options.allowSprint=Allow sprint
options.barOnTop=HUD above inventory options.barOnTop=HUD above inventory
options.rpiCursor=Show Raspberry PI cursor
options.autojump=Auto Jump options.autojump=Auto Jump
options.thirdperson=Third Person options.thirdperson=Third Person
options.servervisible=Server Visible options.servervisible=Server Visible

View File

@@ -59,6 +59,7 @@ public class MainActivity extends Activity {
private static final int PERMISSION_REQUEST_CODE = 123; private static final int PERMISSION_REQUEST_CODE = 123;
private GLView _glView; private GLView _glView;
private boolean _nativeInitialized = false;
public float invScale = 1.0f;// / 1.5f; public float invScale = 1.0f;// / 1.5f;
private int _screenWidth = 0; private int _screenWidth = 0;
private int _screenHeight = 0; private int _screenHeight = 0;
@@ -77,63 +78,48 @@ public class MainActivity extends Activity {
_screenWidth = Math.max(_dm.widthPixels, _dm.heightPixels); _screenWidth = Math.max(_dm.widthPixels, _dm.heightPixels);
_screenHeight = Math.min(_dm.widthPixels, _dm.heightPixels); _screenHeight = Math.min(_dm.widthPixels, _dm.heightPixels);
nativeOnCreate(_screenWidth, _screenHeight);
_glView = new GLView(getApplication(), this); _glView = new GLView(getApplication(), this);
//_glView.setEGLConfigChooser(8, 8, 8, 8, 16, 0); //_glView.setEGLConfigChooser(8, 8, 8, 8, 16, 0);
_glView.setEGLConfigChooser(true); _glView.setEGLConfigChooser(true);
//_glView //_glView
// _glView.setEGLConfigChooser(
// new GLSurfaceView.EGLConfigChooser() {
//
// @Override
// public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
// // TODO Auto-generated method stub
//
// // Specify a configuration for our opengl session
// // and grab the first configuration that matches is
// int[] configSpec = {
// EGL10.EGL_DEPTH_SIZE, 24,
// EGL10.EGL_NONE
// };
// EGLConfig[] configs = new EGLConfig[1];
// int[] num_config = new int[1];
// egl.eglChooseConfig(display, configSpec, configs, 1, num_config);
// EGLConfig config = configs[0];
// return config;
//
// //return null;
// }
// } );
_glView.commit(); _glView.commit();
setContentView(_glView); setContentView(_glView);
_soundPlayer = new SoundPlayer(this, AudioManager.STREAM_MUSIC); _soundPlayer = new SoundPlayer(this, AudioManager.STREAM_MUSIC);
// request dangerous permissions at runtime if necessary
checkAndRequestPermissions(); checkAndRequestPermissions();
initNative();
} }
private void checkAndRequestPermissions() { private void initNative() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (_nativeInitialized) {
boolean needRequest = false; return;
if (checkSelfPermission(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
needRequest = true;
}
if (checkSelfPermission(android.Manifest.permission.READ_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
needRequest = true;
}
if (needRequest) {
requestPermissions(new String[] {
android.Manifest.permission.WRITE_EXTERNAL_STORAGE,
android.Manifest.permission.READ_EXTERNAL_STORAGE
}, PERMISSION_REQUEST_CODE);
}
} }
_nativeInitialized = true;
nativeOnCreate(_screenWidth, _screenHeight);
}
// request dangerous permissions at runtime if necessary
private boolean checkAndRequestPermissions() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
return true;
}
boolean writeGranted = checkSelfPermission(android.Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
boolean readGranted = checkSelfPermission(android.Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
if (writeGranted && readGranted) {
return true;
}
requestPermissions(new String[] {
android.Manifest.permission.WRITE_EXTERNAL_STORAGE,
android.Manifest.permission.READ_EXTERNAL_STORAGE
}, PERMISSION_REQUEST_CODE);
return false;
} }
@Override @Override
@@ -146,8 +132,18 @@ public class MainActivity extends Activity {
break; break;
} }
} }
if (!granted) { if (granted) {
// user denied; you might want to warn or close the app initNative();
} else {
// We can still run using app-specific external files in scoped-storage,
// so allow startup while warning the user.
initNative();
new AlertDialog.Builder(this)
.setTitle("Storage permission recommended")
.setMessage("MinecraftPE can still run with app-private storage, but public external save/load may require permission.")
.setPositiveButton("OK", null)
.setCancelable(true)
.show();
} }
} }
super.onRequestPermissionsResult(requestCode, permissions, grantResults); super.onRequestPermissionsResult(requestCode, permissions, grantResults);

View File

@@ -75,6 +75,8 @@ public:
virtual void playSound(const std::string& fn, float volume, float pitch) {} virtual void playSound(const std::string& fn, float volume, float pitch) {}
virtual void hideCursor(bool hide) {}
virtual void showDialog(int dialogId) {} virtual void showDialog(int dialogId) {}
virtual void createUserInput() {} virtual void createUserInput() {}

View File

@@ -73,50 +73,20 @@ public:
: filename_; : filename_;
std::ifstream source(filename.c_str(), std::ios::binary); std::ifstream source(filename.c_str(), std::ios::binary);
if (source) { if (!source) {
png_structp pngPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!pngPtr)
return out;
png_infop infoPtr = png_create_info_struct(pngPtr);
if (!infoPtr) {
png_destroy_read_struct(&pngPtr, NULL, NULL);
return out;
}
// Hack to get around the broken libpng for windows
png_set_read_fn(pngPtr,(void*)&source, png_funcReadFile);
png_read_info(pngPtr, infoPtr);
// Set up the texdata properties
out.w = png_get_image_width(pngPtr, infoPtr);
out.h = png_get_image_height(pngPtr, infoPtr);
png_bytep* rowPtrs = new png_bytep[out.h];
out.data = new unsigned char[4 * out.w * out.h];
out.memoryHandledExternally = false;
int rowStrideBytes = 4 * out.w;
for (int i = 0; i < out.h; i++) {
rowPtrs[i] = (png_bytep)&out.data[i*rowStrideBytes];
}
png_read_image(pngPtr, rowPtrs);
// Teardown and return
png_destroy_read_struct(&pngPtr, &infoPtr,(png_infopp)0);
delete[] (png_bytep)rowPtrs;
source.close();
return out;
}
else
{
LOGI("Couldn't find file: %s\n", filename.c_str()); LOGI("Couldn't find file: %s\n", filename.c_str());
return out; return out;
} }
std::vector<unsigned char> fileData((std::istreambuf_iterator<char>(source)), std::istreambuf_iterator<char>());
source.close();
if (fileData.empty()) {
LOGI("Couldn't read file: %s\n", filename.c_str());
return out;
}
return loadTextureFromMemory(fileData.data(), fileData.size());
} }
TextureData loadTextureFromMemory(const unsigned char* data, size_t size) override { TextureData loadTextureFromMemory(const unsigned char* data, size_t size) override {
@@ -139,6 +109,11 @@ public:
virtual bool supportsTouchscreen() override { return true; } virtual bool supportsTouchscreen() override { return true; }
virtual void hideCursor(bool hide) {
int isHide = hide ? GLFW_CURSOR_NORMAL : GLFW_CURSOR_HIDDEN;
glfwSetInputMode(window, GLFW_CURSOR, isHide);
}
virtual void openURL(const std::string& url) override { virtual void openURL(const std::string& url) override {
#ifdef _WIN32 #ifdef _WIN32
ShellExecuteA(NULL, "open", url.c_str(), NULL, NULL, SW_SHOWNORMAL); ShellExecuteA(NULL, "open", url.c_str(), NULL, NULL, SW_SHOWNORMAL);

View File

@@ -99,6 +99,9 @@ void NinecraftApp::init()
I18n::loadLanguage(platform(), "en_US"); I18n::loadLanguage(platform(), "en_US");
#endif #endif
if (!externalStoragePath.empty()) {
options.setOptionsFilePath(externalStoragePath);
}
Minecraft::init(); Minecraft::init();
#if !defined(DEMO_MODE) && !defined(APPLE_DEMO_PROMOTION) && !defined(NO_STORAGE) #if !defined(DEMO_MODE) && !defined(APPLE_DEMO_PROMOTION) && !defined(NO_STORAGE)

View File

@@ -1143,6 +1143,8 @@ void Minecraft::init()
checkGlError("Init complete"); checkGlError("Init complete");
#endif #endif
options.load();
setIsCreativeMode(false); // false means it's Survival Mode setIsCreativeMode(false); // false means it's Survival Mode
reloadOptions(); reloadOptions();
} }

View File

@@ -20,6 +20,7 @@ OptionBool fixedCamera("fixedCamera", false);
OptionBool isFlying("isflying", false); OptionBool isFlying("isflying", false);
OptionBool barOnTop("barOnTop", false); OptionBool barOnTop("barOnTop", false);
OptionBool allowSprint("allowSprint", true); OptionBool allowSprint("allowSprint", true);
OptionBool rpiCursor("rpiCursor", false);
OptionBool autoJump("autoJump", true); OptionBool autoJump("autoJump", true);
@@ -158,6 +159,7 @@ void Options::initTable() {
m_options[OPTIONS_BAR_ON_TOP] = &barOnTop; m_options[OPTIONS_BAR_ON_TOP] = &barOnTop;
m_options[OPTIONS_ALLOW_SPRINT] = &allowSprint; m_options[OPTIONS_ALLOW_SPRINT] = &allowSprint;
m_options[OPTIONS_RPI_CURSOR] = &rpiCursor;
m_options[OPTIONS_AUTOJUMP] = &autoJump; m_options[OPTIONS_AUTOJUMP] = &autoJump;
m_options[OPTIONS_LAST_IP] = &lastIp; m_options[OPTIONS_LAST_IP] = &lastIp;
@@ -283,6 +285,10 @@ void Options::save() {
optionsFile.save(stringVec); optionsFile.save(stringVec);
} }
void Options::setOptionsFilePath(const std::string& path) {
optionsFile.setOptionsPath(path + "/options.txt");
}
void Options::notifyOptionUpdate(OptionId key, bool value) { void Options::notifyOptionUpdate(OptionId key, bool value) {
minecraft->optionUpdated(key, value); minecraft->optionUpdated(key, value);
} }

View File

@@ -83,6 +83,7 @@ enum OptionId {
OPTIONS_FIRST_LAUNCH, OPTIONS_FIRST_LAUNCH,
OPTIONS_LAST_IP, OPTIONS_LAST_IP,
OPTIONS_RPI_CURSOR,
// Should be last! // Should be last!
OPTIONS_COUNT OPTIONS_COUNT
}; };
@@ -100,15 +101,10 @@ public:
// elements werent initialized so i was getting a garbage pointer and a crash // elements werent initialized so i was getting a garbage pointer and a crash
m_options.fill(nullptr); m_options.fill(nullptr);
initTable(); initTable();
load(); // load() is deferred to init() where path is configured correctly
} }
void initTable(); void initTable();
void set(OptionId key, int value);
void set(OptionId key, float value);
void set(OptionId key, const std::string& value);
void toggle(OptionId key);
int getIntValue(OptionId key) { int getIntValue(OptionId key) {
auto option = opt<OptionInt>(key); auto option = opt<OptionInt>(key);
@@ -144,6 +140,11 @@ public:
void load(); void load();
void save(); void save();
void set(OptionId key, int value);
void set(OptionId key, float value);
void set(OptionId key, const std::string& value);
void setOptionsFilePath(const std::string& path);
void toggle(OptionId key);
void notifyOptionUpdate(OptionId key, bool value); void notifyOptionUpdate(OptionId key, bool value);
void notifyOptionUpdate(OptionId key, float value); void notifyOptionUpdate(OptionId key, float value);

View File

@@ -4,6 +4,12 @@
#include <errno.h> #include <errno.h>
#include <platform/log.h> #include <platform/log.h>
#if !defined(_WIN32)
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#endif
OptionsFile::OptionsFile() { OptionsFile::OptionsFile() {
#ifdef __APPLE__ #ifdef __APPLE__
settingsPath = "./Documents/options.txt"; settingsPath = "./Documents/options.txt";
@@ -14,6 +20,14 @@ OptionsFile::OptionsFile() {
#endif #endif
} }
void OptionsFile::setOptionsPath(const std::string& path) {
settingsPath = path;
}
std::string OptionsFile::getOptionsPath() const {
return settingsPath;
}
void OptionsFile::save(const StringVector& settings) { void OptionsFile::save(const StringVector& settings) {
FILE* pFile = fopen(settingsPath.c_str(), "w"); FILE* pFile = fopen(settingsPath.c_str(), "w");
if(pFile != NULL) { if(pFile != NULL) {
@@ -22,10 +36,33 @@ void OptionsFile::save(const StringVector& settings) {
} }
fclose(pFile); fclose(pFile);
} else { } else {
LOGI("OptionsFile::save failed to open '%s' for writing: %s", settingsPath.c_str(), strerror(errno)); if (errno != ENOENT)
LOGI("OptionsFile::save failed to open '%s' for writing: %s", settingsPath.c_str(), strerror(errno));
// Ensure parent directory exists for safekeeping if path contains directories
std::string dir = settingsPath;
size_t fpos = dir.find_last_of("/\\");
if (fpos != std::string::npos) {
dir.resize(fpos);
struct stat st;
if (stat(dir.c_str(), &st) != 0) {
// attempt recursive mkdir
std::string toCreate;
for (size_t i = 0; i <= dir.size(); ++i) {
if (i == dir.size() || dir[i] == '/' || dir[i] == '\\') {
if (!toCreate.empty()) {
mkdir(toCreate.c_str(), 0755);
}
}
if (i < dir.size())
toCreate.push_back(dir[i]);
}
}
}
} }
} }
StringVector OptionsFile::getOptionStrings() { StringVector OptionsFile::getOptionStrings() {
StringVector returnVector; StringVector returnVector;
FILE* pFile = fopen(settingsPath.c_str(), "r"); FILE* pFile = fopen(settingsPath.c_str(), "r");
@@ -46,7 +83,8 @@ StringVector OptionsFile::getOptionStrings() {
} }
fclose(pFile); fclose(pFile);
} else { } else {
LOGI("OptionsFile::getOptionStrings failed to open '%s' for reading: %s", settingsPath.c_str(), strerror(errno)); if (errno != ENOENT)
LOGI("OptionsFile::getOptionStrings failed to open '%s' for reading: %s", settingsPath.c_str(), strerror(errno));
} }
return returnVector; return returnVector;
} }

View File

@@ -11,6 +11,8 @@ public:
OptionsFile(); OptionsFile();
void save(const StringVector& settings); void save(const StringVector& settings);
StringVector getOptionStrings(); StringVector getOptionStrings();
void setOptionsPath(const std::string& path);
std::string getOptionsPath() const;
private: private:
std::string settingsPath; std::string settingsPath;

View File

@@ -225,7 +225,8 @@ void OptionsScreen::generateOptionScreens() {
.addOptionItem(OPTIONS_AMBIENT_OCCLUSION, minecraft); .addOptionItem(OPTIONS_AMBIENT_OCCLUSION, minecraft);
optionPanes[4]->addOptionItem(OPTIONS_ALLOW_SPRINT, minecraft) optionPanes[4]->addOptionItem(OPTIONS_ALLOW_SPRINT, minecraft)
.addOptionItem(OPTIONS_BAR_ON_TOP, minecraft); .addOptionItem(OPTIONS_BAR_ON_TOP, minecraft)
.addOptionItem(OPTIONS_RPI_CURSOR, minecraft);
} }
void OptionsScreen::mouseClicked(int x, int y, int buttonNum) { void OptionsScreen::mouseClicked(int x, int y, int buttonNum) {

View File

@@ -187,6 +187,33 @@ static bool ensureDirectoryExists(const std::string& path) {
return _mkdir(path.c_str()) == 0 || errno == EEXIST; return _mkdir(path.c_str()) == 0 || errno == EEXIST;
#else #else
struct stat st; struct stat st;
if (stat(path.c_str(), &st) == 0 && S_ISDIR(st.st_mode))
return true;
std::string subPath;
size_t i = 0;
while (i < path.length()) {
i = path.find_first_of("/\\", i);
if (i == std::string::npos) {
subPath = path;
} else {
subPath = path.substr(0, i);
}
if (!subPath.empty()) {
if (stat(subPath.c_str(), &st) != 0) {
if (mkdir(subPath.c_str(), 0755) != 0 && errno != EEXIST)
return false;
} else if (!S_ISDIR(st.st_mode)) {
return false;
}
}
if (i == std::string::npos)
break;
i += 1;
}
if (stat(path.c_str(), &st) == 0 && S_ISDIR(st.st_mode)) if (stat(path.c_str(), &st) == 0 && S_ISDIR(st.st_mode))
return true; return true;
return mkdir(path.c_str(), 0755) == 0 || errno == EEXIST; return mkdir(path.c_str(), 0755) == 0 || errno == EEXIST;
@@ -236,7 +263,8 @@ static void* fetchSkinForPlayer(void* param) {
std::vector<unsigned char> skinData; std::vector<unsigned char> skinData;
if (!HttpClient::download(skinUrl, skinData) || skinData.empty()) { if (!HttpClient::download(skinUrl, skinData) || skinData.empty()) {
LOGW("[Skin] download failed for %s\n", skinUrl.c_str()); LOGW("[Skin] download failed for %s\n", skinUrl.c_str());
return NULL; player->setTextureName("mob/char.png");
return NULL;
} }
// Save to cache // Save to cache

View File

@@ -1,4 +1,5 @@
#include "GameRenderer.h" #include "GameRenderer.h"
#include "client/Options.h"
#include "gles.h" #include "gles.h"
#include "../../util/PerfTimer.h" #include "../../util/PerfTimer.h"
@@ -214,12 +215,14 @@ void GameRenderer::render(float a) {
glDisable2(GL_SCISSOR_TEST); glDisable2(GL_SCISSOR_TEST);
mc->screen->render(xMouse, yMouse, a); mc->screen->render(xMouse, yMouse, a);
#ifdef RPI
mc->platform()->hideCursor(!mc->options.getBooleanValue(OPTIONS_RPI_CURSOR));
if (mc->options.getBooleanValue(OPTIONS_RPI_CURSOR))
renderCursor(xMouse, yMouse, mc); renderCursor(xMouse, yMouse, mc);
#endif
// Screen might have been removed, so check it again // Screen might have been removed, so check it again
if (mc->screen && !mc->screen->isInGameScreen()) if (mc->screen && !mc->screen->isInGameScreen())
sleepMs(15); sleepMs(15);
} }
} }

View File

@@ -19,12 +19,13 @@ public:
protected: protected:
void additionalRendering(Mob* mob, float a); void additionalRendering(Mob* mob, float a);
private:
HumanoidModel* humanoidModel; HumanoidModel* humanoidModel;
// Last rotation values for cape smoothing (reduces jitter) // Last rotation values for cape smoothing (reduces jitter)
float lastCapeXRot; float lastCapeXRot;
float lastCapeZRot; float lastCapeZRot;
private:
// i guess ill keep this just in case seomthing breaks
}; };
#endif /*NET_MINECRAFT_CLIENT_RENDERER_ENTITY__HumanoidMobRenderer_H__*/ #endif /*NET_MINECRAFT_CLIENT_RENDERER_ENTITY__HumanoidMobRenderer_H__*/

View File

@@ -43,8 +43,8 @@ public:
protected: protected:
void setArmor(Model* armor); void setArmor(Model* armor);
Model* getArmor(); Model* getArmor();
Model* model; // allows derived renderers to swap models dynamically for skin formats
private: private:
Model* model;
Model* armor; Model* armor;
}; };

View File

@@ -1,5 +1,6 @@
#include "PlayerRenderer.h" #include "PlayerRenderer.h"
#include "EntityRenderDispatcher.h" #include "EntityRenderDispatcher.h"
#include "../Textures.h"
#include "../../../world/entity/player/Player.h" #include "../../../world/entity/player/Player.h"
#include "../../../world/level/Level.h" #include "../../../world/level/Level.h"
#include "../../../world/item/ArmorItem.h" #include "../../../world/item/ArmorItem.h"
@@ -14,12 +15,22 @@ static const std::string armorFilenames[10] = {
PlayerRenderer::PlayerRenderer( HumanoidModel* humanoidModel, float shadow ) PlayerRenderer::PlayerRenderer( HumanoidModel* humanoidModel, float shadow )
: super(humanoidModel, shadow), : super(humanoidModel, shadow),
playerModel64(humanoidModel),
playerModel32(new HumanoidModel(0, 0, 64, 32)),
armorParts1(new HumanoidModel(1.0f, 0, 64, 64)), armorParts1(new HumanoidModel(1.0f, 0, 64, 64)),
armorParts2(new HumanoidModel(0.5f, 0, 64, 64)) armorParts2(new HumanoidModel(0.5f, 0, 64, 64))
{ {
// default to legacy skin path until we know the exact texture size
model = playerModel32;
humanoidModel = playerModel32;
} }
PlayerRenderer::~PlayerRenderer() { PlayerRenderer::~PlayerRenderer() {
// prevent MobRenderer destructor from deleting model pointers we manage manually
model = nullptr;
delete playerModel32;
delete playerModel64;
delete armorParts1; delete armorParts1;
delete armorParts2; delete armorParts2;
} }
@@ -43,6 +54,15 @@ void PlayerRenderer::setupRotations( Entity* mob, float bob, float bodyRot, floa
super::setupRotations(mob, bob, bodyRot, a); super::setupRotations(mob, bob, bodyRot, a);
} }
bool PlayerRenderer::isModernPlayerSkin(Mob* mob) {
const std::string texName = mob->getTexture();
TextureId texId = entityRenderDispatcher->textures->loadTexture(texName);
if (!Textures::isTextureIdValid(texId))
return false;
const TextureData* texData = entityRenderDispatcher->textures->getTemporaryTextureData(texId);
return texData && texData->w == 64 && texData->h == 64;
}
void PlayerRenderer::renderName( Mob* mob, float x, float y, float z ){ void PlayerRenderer::renderName( Mob* mob, float x, float y, float z ){
//@todo: figure out how to handle HideGUI //@todo: figure out how to handle HideGUI
if (mob != entityRenderDispatcher->cameraEntity && mob->level->adventureSettings.showNameTags) { if (mob != entityRenderDispatcher->cameraEntity && mob->level->adventureSettings.showNameTags) {
@@ -50,6 +70,20 @@ void PlayerRenderer::renderName( Mob* mob, float x, float y, float z ){
} }
} }
void PlayerRenderer::render(Entity* mob_, float x, float y, float z, float rot, float a) {
Mob* mob = (Mob*) mob_;
HumanoidModel* desired = isModernPlayerSkin(mob) ? playerModel64 : playerModel32;
if (model != desired || humanoidModel != desired) {
model = desired;
humanoidModel = desired;
}
// LOGI("[PlayerRenderer] %s: skin=%s, modelTex=%dx%d, desired=%s\n",
// ((Player*)mob)->name.c_str(), mob->getTexture().c_str(),
// humanoidModel->texWidth, humanoidModel->texHeight,
// (desired == playerModel64 ? "64" : "32"));
HumanoidMobRenderer::render(mob_, x, y, z, rot, a);
}
int PlayerRenderer::prepareArmor(Mob* mob, int layer, float a) { int PlayerRenderer::prepareArmor(Mob* mob, int layer, float a) {
Player* player = (Player*) mob; Player* player = (Player*) mob;
@@ -80,8 +114,11 @@ int PlayerRenderer::prepareArmor(Mob* mob, int layer, float a) {
} }
void PlayerRenderer::onGraphicsReset() { void PlayerRenderer::onGraphicsReset() {
super::onGraphicsReset(); if (playerModel32) playerModel32->onGraphicsReset();
if (playerModel64) playerModel64->onGraphicsReset();
if (armorParts1) armorParts1->onGraphicsReset(); if (armorParts1) armorParts1->onGraphicsReset();
if (armorParts2) armorParts2->onGraphicsReset(); if (armorParts2) armorParts2->onGraphicsReset();
super::onGraphicsReset();
} }

View File

@@ -11,6 +11,8 @@ public:
~PlayerRenderer(); ~PlayerRenderer();
virtual int prepareArmor(Mob* mob, int layer, float a); virtual int prepareArmor(Mob* mob, int layer, float a);
bool isModernPlayerSkin(Mob* mob);
virtual void render(Entity* mob, float x, float y, float z, float rot, float a);
virtual void setupPosition(Entity* mob, float x, float y, float z); virtual void setupPosition(Entity* mob, float x, float y, float z);
virtual void setupRotations(Entity* mob, float bob, float bodyRot, float a); virtual void setupRotations(Entity* mob, float bob, float bodyRot, float a);
@@ -18,6 +20,8 @@ public:
virtual void renderName(Mob* mob, float x, float y, float z); virtual void renderName(Mob* mob, float x, float y, float z);
virtual void onGraphicsReset(); virtual void onGraphicsReset();
private: private:
HumanoidModel* playerModel32;
HumanoidModel* playerModel64;
HumanoidModel* armorParts1; HumanoidModel* armorParts1;
HumanoidModel* armorParts2; HumanoidModel* armorParts2;
}; };

View File

@@ -25,12 +25,33 @@ static void setupExternalPath(struct android_app* state, MAIN_CLASS* app)
{ {
LOGI("Environment exists"); LOGI("Environment exists");
} }
jclass clazz = env->FindClass("android/os/Environment"); // try appspecific external directory first
jmethodID method = env->GetStaticMethodID(clazz, "getExternalStorageDirectory", "()Ljava/io/File;"); jobject activity = state->activity->clazz;
if (env->ExceptionOccurred()) { jclass activityClass = env->GetObjectClass(activity);
env->ExceptionDescribe(); jmethodID getExternalFilesDir = env->GetMethodID(activityClass, "getExternalFilesDir", "(Ljava/lang/String;)Ljava/io/File;");
jobject file = NULL;
if (getExternalFilesDir != NULL) {
file = env->CallObjectMethod(activity, getExternalFilesDir, NULL);
}
if (file == NULL) {
// Fallback to the legacy shared storage directory
jclass clazz = env->FindClass("android/os/Environment");
jmethodID method = env->GetStaticMethodID(clazz, "getExternalStorageDirectory", "()Ljava/io/File;");
if (env->ExceptionOccurred()) {
env->ExceptionDescribe();
env->ExceptionClear();
}
file = env->CallStaticObjectMethod(clazz, method);
}
if (!file) {
LOGI("Failed to get external storage file object, using current working dir");
app->externalStoragePath = ".";
app->externalCacheStoragePath = ".";
return;
} }
jobject file = env->CallStaticObjectMethod(clazz, method);
jclass fileClass = env->GetObjectClass(file); jclass fileClass = env->GetObjectClass(file);
jmethodID fileMethod = env->GetMethodID(fileClass, "getAbsolutePath", "()Ljava/lang/String;"); jmethodID fileMethod = env->GetMethodID(fileClass, "getAbsolutePath", "()Ljava/lang/String;");
@@ -38,7 +59,7 @@ static void setupExternalPath(struct android_app* state, MAIN_CLASS* app)
const char* str = env->GetStringUTFChars((jstring) pathString, NULL); const char* str = env->GetStringUTFChars((jstring) pathString, NULL);
app->externalStoragePath = str; app->externalStoragePath = str;
app->externalCacheStoragePath = str; app->externalCacheStoragePath = str;
LOGI("%s", str); LOGI("%s", str);
// ensure the process working directory is set to a writable location // ensure the process working directory is set to a writable location

View File

@@ -30,12 +30,33 @@ static void setupExternalPath(JNIEnv* env, MAIN_CLASS* app)
{ {
LOGI("Environment exists"); LOGI("Environment exists");
} }
jclass clazz = env->FindClass("android/os/Environment"); // try appspecific external directory first
jmethodID method = env->GetStaticMethodID(clazz, "getExternalStorageDirectory", "()Ljava/io/File;"); jobject activity = g_pActivity;
if (env->ExceptionOccurred()) { jclass activityClass = env->GetObjectClass(activity);
env->ExceptionDescribe(); jmethodID getExternalFilesDir = env->GetMethodID(activityClass, "getExternalFilesDir", "(Ljava/lang/String;)Ljava/io/File;");
jobject file = NULL;
if (getExternalFilesDir != NULL) {
file = env->CallObjectMethod(activity, getExternalFilesDir, NULL);
}
if (file == NULL) {
// Fallback to the legacy shared storage directory
jclass clazz = env->FindClass("android/os/Environment");
jmethodID method = env->GetStaticMethodID(clazz, "getExternalStorageDirectory", "()Ljava/io/File;");
if (env->ExceptionOccurred()) {
env->ExceptionDescribe();
env->ExceptionClear();
}
file = env->CallStaticObjectMethod(clazz, method);
}
if (!file) {
LOGI("Failed to get external storage file object, using current working dir");
app->externalStoragePath = ".";
app->externalCacheStoragePath = ".";
return;
} }
jobject file = env->CallStaticObjectMethod(clazz, method);
jclass fileClass = env->GetObjectClass(file); jclass fileClass = env->GetObjectClass(file);
jmethodID fileMethod = env->GetMethodID(fileClass, "getAbsolutePath", "()Ljava/lang/String;"); jmethodID fileMethod = env->GetMethodID(fileClass, "getAbsolutePath", "()Ljava/lang/String;");
@@ -43,7 +64,7 @@ static void setupExternalPath(JNIEnv* env, MAIN_CLASS* app)
const char* str = env->GetStringUTFChars((jstring) pathString, NULL); const char* str = env->GetStringUTFChars((jstring) pathString, NULL);
app->externalStoragePath = str; app->externalStoragePath = str;
app->externalCacheStoragePath = str; app->externalCacheStoragePath = str;
LOGI("%s", str); LOGI("%s", str);
// same fix as the native entry point: make sure cwd is writable // same fix as the native entry point: make sure cwd is writable

View File

@@ -166,7 +166,7 @@ int main(void) {
AppPlatform_glfw* platform = (AppPlatform_glfw*)appContext.platform; AppPlatform_glfw* platform = (AppPlatform_glfw*)appContext.platform;
platform->window = glfwCreateWindow(appContext.platform->getScreenWidth(), appContext.platform->getScreenHeight(), "main", NULL, NULL); platform->window = glfwCreateWindow(appContext.platform->getScreenWidth(), appContext.platform->getScreenHeight(), "Minecraft PE 0.6.1", NULL, NULL);
if (platform->window == NULL) { if (platform->window == NULL) {
return 1; return 1;