mirror of
https://github.com/celisej567/mcpe.git
synced 2025-12-31 17:49:17 +03:00
423 lines
10 KiB
C++
423 lines
10 KiB
C++
#include <cstdarg>
|
|
#include <libgen.h>
|
|
|
|
#include "thirdparty/SDL2/SDL2.h"
|
|
|
|
#include "thirdparty/GL/GL.hpp"
|
|
#include "client/app/App.hpp"
|
|
|
|
#ifdef __EMSCRIPTEN__
|
|
#include "emscripten/AppPlatform_sdl.hpp"
|
|
#else
|
|
#include "desktop/AppPlatform_sdl.hpp"
|
|
#endif
|
|
typedef AppPlatform_sdl UsedAppPlatform;
|
|
|
|
#include "client/app/NinecraftApp.hpp"
|
|
#include "client/player/input/Multitouch.hpp"
|
|
|
|
static float g_fPointToPixelScale = 1.0f;
|
|
|
|
UsedAppPlatform *g_pAppPlatform;
|
|
NinecraftApp *g_pApp;
|
|
|
|
SDL_Window *window = NULL;
|
|
SDL_GLContext context = NULL;
|
|
static void teardown()
|
|
{
|
|
if (window != NULL)
|
|
{
|
|
SDL_GL_DeleteContext(context);
|
|
SDL_DestroyWindow(window);
|
|
SDL_Quit();
|
|
window = NULL;
|
|
}
|
|
}
|
|
|
|
static int TranslateSDLKeyCodeToVirtual(SDL_Scancode sdlCode)
|
|
{
|
|
switch (sdlCode) {
|
|
#define CODE(x) case SDL_SCANCODE_ ## x: return SDLVK_ ## x;
|
|
#include "compat/SDLKeyCodes.h"
|
|
#undef CODE
|
|
}
|
|
return SDLVK_UNKNOWN;
|
|
}
|
|
|
|
// Touch
|
|
#define TOUCH_IDS_SIZE (MAX_TOUCHES - 1) // ID 0 Is Reserved For The Mouse
|
|
struct touch_id_data {
|
|
bool active = false;
|
|
int id;
|
|
};
|
|
static touch_id_data touch_ids[TOUCH_IDS_SIZE];
|
|
static char get_touch_id(int device, int finger) {
|
|
int real_id = (device * 100) + finger;
|
|
for (int i = 0; i < TOUCH_IDS_SIZE; i++) {
|
|
touch_id_data &data = touch_ids[i];
|
|
if (data.active && data.id == real_id) {
|
|
return i + 1;
|
|
}
|
|
}
|
|
// Not Found
|
|
for (int i = 0; i < TOUCH_IDS_SIZE; i++) {
|
|
// Find First Inactive ID, And Activate It
|
|
touch_id_data &data = touch_ids[i];
|
|
if (!data.active) {
|
|
data.active = true;
|
|
data.id = real_id;
|
|
return i + 1;
|
|
}
|
|
}
|
|
// Fail
|
|
return 0;
|
|
}
|
|
static void drop_touch_id(int id) {
|
|
touch_ids[id - 1].active = false;
|
|
}
|
|
static void handle_touch(int x, int y, int type, char id) {
|
|
if (id == 0) {
|
|
return;
|
|
}
|
|
switch (type) {
|
|
case SDL_FINGERDOWN:
|
|
case SDL_FINGERUP: {
|
|
bool data = type == SDL_FINGERUP ? 0 : 1;
|
|
Mouse::feed(BUTTON_LEFT, data, x, y);
|
|
Multitouch::feed(BUTTON_LEFT, data, x, y, id);
|
|
if (type == SDL_FINGERUP) {
|
|
drop_touch_id(id);
|
|
}
|
|
break;
|
|
}
|
|
case SDL_FINGERMOTION: {
|
|
Mouse::feed(BUTTON_NONE, 0, x, y);
|
|
Multitouch::feed(BUTTON_NONE, 0, x, y, id);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Resize From JS
|
|
#ifdef __EMSCRIPTEN__
|
|
extern "C" void resize_from_js(int new_width, int new_height)
|
|
{
|
|
SDL_SetWindowSize(window, new_width, new_height);
|
|
}
|
|
#endif
|
|
|
|
// Handle Events
|
|
static bool window_resized = false;
|
|
static void handle_events()
|
|
{
|
|
SDL_Event event;
|
|
while (SDL_PollEvent(&event))
|
|
{
|
|
switch (event.type)
|
|
{
|
|
case SDL_KEYDOWN:
|
|
case SDL_KEYUP:
|
|
{
|
|
// TODO: Shouldn't we be handling this in Keyboard?
|
|
|
|
// We really should. We didn't add the f2 key anywhere -iProgramInCpp
|
|
/*
|
|
if (event.key.keysym.sym == SDLK_F2)
|
|
{
|
|
if (event.key.state == SDL_PRESSED && g_pAppPlatform != nullptr)
|
|
{
|
|
g_pAppPlatform->saveScreenshot("", -1, -1);
|
|
}
|
|
break;
|
|
}
|
|
*/
|
|
|
|
Keyboard::feed(AppPlatform_sdl_base::GetKeyState(event), TranslateSDLKeyCodeToVirtual(event.key.keysym.scancode));
|
|
if (event.key.keysym.sym == SDLK_LSHIFT || event.key.keysym.sym == SDLK_RSHIFT)
|
|
{
|
|
g_pAppPlatform->setShiftPressed(event.key.state == SDL_PRESSED, event.key.keysym.sym == SDLK_LSHIFT);
|
|
}
|
|
break;
|
|
}
|
|
case SDL_MOUSEBUTTONDOWN:
|
|
case SDL_MOUSEBUTTONUP:
|
|
{
|
|
if (event.button.which != SDL_TOUCH_MOUSEID) {
|
|
const float scale = g_fPointToPixelScale;
|
|
MouseButtonType type = AppPlatform_sdl_base::GetMouseButtonType(event);
|
|
bool state = AppPlatform_sdl_base::GetMouseButtonState(event);
|
|
float x = event.button.x * scale;
|
|
float y = event.button.y * scale;
|
|
Mouse::feed(type, state, x, y);
|
|
Multitouch::feed(type, state, x, y, 0);
|
|
}
|
|
break;
|
|
}
|
|
case SDL_MOUSEMOTION:
|
|
{
|
|
if (event.button.which != SDL_TOUCH_MOUSEID) {
|
|
float scale = g_fPointToPixelScale;
|
|
float x = event.motion.x * scale;
|
|
float y = event.motion.y * scale;
|
|
Multitouch::feed(BUTTON_NONE, 0, x, y, 0);
|
|
Mouse::feed(BUTTON_NONE, false, x, y);
|
|
g_pAppPlatform->setMouseDiff(event.motion.xrel * scale, event.motion.yrel * scale);
|
|
}
|
|
break;
|
|
}
|
|
case SDL_MOUSEWHEEL:
|
|
{
|
|
if (event.button.which != SDL_TOUCH_MOUSEID) {
|
|
Mouse::feed(BUTTON_SCROLLWHEEL, AppPlatform_sdl_base::GetMouseButtonState(event), Mouse::getX(), Mouse::getY());
|
|
}
|
|
break;
|
|
}
|
|
case SDL_FINGERDOWN:
|
|
case SDL_FINGERUP:
|
|
case SDL_FINGERMOTION: {
|
|
float x = event.tfinger.x * Minecraft::width;
|
|
float y = event.tfinger.y * Minecraft::height;
|
|
handle_touch(x, y, event.type, get_touch_id(event.tfinger.touchId, event.tfinger.fingerId));
|
|
break;
|
|
}
|
|
case SDL_TEXTINPUT:
|
|
{
|
|
if (g_pApp != nullptr)
|
|
{
|
|
char x = event.text.text[0];
|
|
if (x >= ' ' && x <= '~')
|
|
{
|
|
g_pApp->handleCharInput(x);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case SDL_WINDOWEVENT:
|
|
{
|
|
if (event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED)
|
|
{
|
|
window_resized = true;
|
|
}
|
|
break;
|
|
}
|
|
case SDL_QUIT:
|
|
{
|
|
g_pApp->quit();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Resizing
|
|
static void resize()
|
|
{
|
|
int drawWidth, drawHeight;
|
|
SDL_GL_GetDrawableSize(window,
|
|
&drawWidth, &drawHeight);
|
|
|
|
int windowWidth, windowHeight;
|
|
SDL_GetWindowSize(window,
|
|
&windowWidth, &windowHeight);
|
|
|
|
Minecraft::width = drawWidth;
|
|
Minecraft::height = drawHeight;
|
|
|
|
// recalculate the point to pixel scale.
|
|
// This currently assumes that the aspect ratio is the same.
|
|
g_fPointToPixelScale = float(drawWidth) / float(windowWidth);
|
|
|
|
// Update the scale multiplier. We use the same value, because we pass to `sizeUpdate`, the window width/height.
|
|
// They will be multiplied by the GUI scale multiplier, becoming the drawwidth and drawheight, times the decided on GUI scale.
|
|
Minecraft::setGuiScaleMultiplier(g_fPointToPixelScale);
|
|
|
|
// give it an update.
|
|
// As said before, internally, this multiplies by the GUI scale multiplier
|
|
if (g_pApp)
|
|
g_pApp->sizeUpdate(windowWidth, windowHeight);
|
|
}
|
|
|
|
// Main Loop
|
|
static bool is_first_window_resize = true;
|
|
static EM_BOOL main_loop(double time, void *user_data)
|
|
{
|
|
// Handle Events
|
|
handle_events();
|
|
|
|
// Screen Size
|
|
if (window_resized)
|
|
{
|
|
window_resized = false;
|
|
resize();
|
|
}
|
|
|
|
// Update MCPE
|
|
g_pApp->update();
|
|
|
|
// Swap Buffers
|
|
SDL_GL_SwapWindow(window);
|
|
|
|
if (g_pApp->wantToQuit())
|
|
{
|
|
g_pApp->saveOptions();
|
|
delete g_pApp;
|
|
delete g_pAppPlatform;
|
|
teardown();
|
|
// Stop Looping
|
|
return EM_FALSE;
|
|
}
|
|
else
|
|
{
|
|
// Keep Looping
|
|
return EM_TRUE;
|
|
}
|
|
}
|
|
|
|
extern bool g_bIsMenuBackgroundAvailable; // client/gui/Screen.cpp
|
|
extern bool g_bAreCloudsAvailable; // client/renderer/LevelRenderer.cpp
|
|
extern bool g_bIsGrassColorAvailable; // world/level/GrassColor.cpp
|
|
extern bool g_bIsFoliageColorAvailable; // world/level/FoliageColor.cpp
|
|
|
|
#ifdef __EMSCRIPTEN__
|
|
bool DoesAssetExist(const std::string & fileName)
|
|
{
|
|
std::string realPath = g_pAppPlatform->getAssetPath(fileName);
|
|
int width = 0, height = 0;
|
|
char *data = emscripten_get_preloaded_image_data(("/" + realPath).c_str(), &width, &height);
|
|
if (data == NULL)
|
|
return false;
|
|
|
|
free(data);
|
|
return true;
|
|
}
|
|
#else
|
|
// access works just fine on linux and friends
|
|
#define DoesAssetExist(fileName) (XPL_ACCESS("assets/" fileName, 0) == 0)
|
|
#endif
|
|
|
|
void CheckOptionalTextureAvailability()
|
|
{
|
|
g_bIsMenuBackgroundAvailable = DoesAssetExist("gui/background/panorama_0.png");
|
|
g_bAreCloudsAvailable = DoesAssetExist("environment/clouds.png");
|
|
g_bIsGrassColorAvailable = DoesAssetExist("misc/grasscolor.png");
|
|
g_bIsFoliageColorAvailable = DoesAssetExist("misc/foliagecolor.png");
|
|
}
|
|
|
|
// Main
|
|
int main(int argc, char *argv[])
|
|
{
|
|
// cd build; ./reminecraftpe otherwise
|
|
chdir(dirname(argv[0]));
|
|
|
|
if (SDL_Init(SDL_INIT_VIDEO) < 0)
|
|
{
|
|
LOG_E("Unable To Initialize SDL: %s", SDL_GetError());
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
// Configure OpenGL ES Context
|
|
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
|
|
#ifdef USE_GLES1_COMPATIBILITY_LAYER
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
|
|
#else
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
|
|
#endif
|
|
// Double-Buffering
|
|
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
|
|
|
|
// Window Size
|
|
#ifdef __EMSCRIPTEN__
|
|
Minecraft::width = std::stoi(argv[1]);
|
|
Minecraft::height = std::stoi(argv[2]);
|
|
#endif
|
|
|
|
// Create Window
|
|
int flags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI;
|
|
window = SDL_CreateWindow("ReMinecraftPE", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, Minecraft::width, Minecraft::height, flags);
|
|
if (!window)
|
|
{
|
|
LOG_E("Unable to create SDL window");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
// Enable Text Input
|
|
SDL_StartTextInput();
|
|
|
|
// Create OpenGL ES Context
|
|
context = SDL_GL_CreateContext(window);
|
|
if (!context)
|
|
{
|
|
const char* const GL_ERROR_MSG = "Unable to create OpenGL context";
|
|
LOG_E(GL_ERROR_MSG);
|
|
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "OpenGL Error", GL_ERROR_MSG, window);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
xglInit();
|
|
|
|
if (!xglInitted())
|
|
{
|
|
const char* const GL_ERROR_MSG = "Error initializing GL extensions. OpenGL 2.0 or later is required. Update your graphics drivers!";
|
|
LOG_E(GL_ERROR_MSG);
|
|
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "OpenGL Error", GL_ERROR_MSG, window);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
#endif
|
|
|
|
// Setup Compatibility Layer If Needed
|
|
#ifdef USE_GLES1_COMPATIBILITY_LAYER
|
|
init_gles_compatibility_layer();
|
|
#endif
|
|
|
|
// Setup Teardown
|
|
#ifndef __EMSCRIPTEN__
|
|
atexit(teardown);
|
|
#endif
|
|
|
|
// Storage Directory
|
|
std::string storagePath;
|
|
#ifdef _WIN32
|
|
storagePath = getenv("APPDATA");
|
|
#elif defined(__EMSCRIPTEN__)
|
|
storagePath = "";
|
|
#else
|
|
storagePath = getenv("HOME");
|
|
#endif
|
|
storagePath += "/.reminecraftpe";
|
|
|
|
if (!storagePath.empty())
|
|
createFolderIfNotExists(storagePath.c_str());
|
|
|
|
// Start MCPE
|
|
g_pApp = new NinecraftApp;
|
|
g_pApp->m_externalStorageDir = storagePath;
|
|
g_pAppPlatform = new UsedAppPlatform(g_pApp->m_externalStorageDir, window);
|
|
g_pApp->m_pPlatform = g_pAppPlatform;
|
|
g_pApp->init();
|
|
|
|
CheckOptionalTextureAvailability();
|
|
|
|
// Set Size
|
|
resize();
|
|
|
|
// Loop
|
|
#ifndef __EMSCRIPTEN__
|
|
while (true)
|
|
{
|
|
EM_BOOL result = main_loop(0, nullptr);
|
|
if (result == EM_FALSE)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
#else
|
|
emscripten_request_animation_frame_loop(main_loop, nullptr);
|
|
#endif
|
|
|
|
return 0;
|
|
}
|