SDL/WASM Port

This commit is contained in:
TheBrokenRail
2023-08-05 01:58:24 -04:00
committed by iProgramInCpp
parent 0b16b2843a
commit 0fbe90752d
37 changed files with 1704 additions and 40 deletions

1
platforms/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/build*

View File

@@ -15,6 +15,12 @@
// Add sound system overrides here
#elif defined(USE_SDL)
// -- OpenAL based sound system for SDL
#include "sdl/SoundSystemAL.hpp"
#define SOUND_SYSTEM_TYPE SoundSystemAL
#elif defined(_WIN32)
// -- DirectSound based sound system for Windows

2
platforms/sdl/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
/build
/assets

View File

@@ -0,0 +1,308 @@
#include "AppPlatform_sdl.hpp"
#include <sstream>
#include <fstream>
#include <sys/stat.h>
#ifndef __EMSCRIPTEN__
#include <png.h>
#include "compat/GL.hpp"
#else
#include <emscripten.h>
#endif
#include "client/common/Utils.hpp"
AppPlatform_sdl::AppPlatform_sdl(std::string storageDir, SDL_Window *window) {
_storageDir = storageDir;
_window = window;
}
int AppPlatform_sdl::checkLicense() {
// we own the game!!
return 1;
}
// Ensure Screenshots Folder Exists
void ensure_screenshots_folder(const char *screenshots) {
// Check Screenshots Folder
struct stat obj;
if (stat(screenshots, &obj) != 0 || !S_ISDIR(obj.st_mode)) {
// Create Screenshots Folder
#ifdef _WIN32
int ret = mkdir(screenshots);
#else
int ret = mkdir(screenshots, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
#endif
if (ret != 0) {
// Unable To Create Folder
LogMsg("Error Creating Directory: %s: %s", screenshots, strerror(errno));
exit(EXIT_FAILURE);
}
}
}
#ifndef __EMSCRIPTEN__
// 4 (Year) + 1 (Hyphen) + 2 (Month) + 1 (Hyphen) + 2 (Day) + 1 (Underscore) + 2 (Hour) + 1 (Period) + 2 (Minute) + 1 (Period) + 2 (Second) + 1 (Null Terminator)
#define TIME_SIZE 20
// Take Screenshot
static int save_png(const char *filename, unsigned char *pixels, int line_size, int width, int height) {
// Return value
int ret = 0;
// Variables
png_structp png = NULL;
png_infop info = NULL;
FILE *file = NULL;
png_colorp palette = NULL;
png_bytep rows[height];
for (int i = 0; i < height; ++i) {
rows[height - i - 1] = (png_bytep)(&pixels[i * line_size]);
}
// Init
png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!png) {
ret = 1;
goto ret;
}
info = png_create_info_struct(png);
if (!info) {
ret = 1;
goto ret;
}
// Open File
file = fopen(filename, "wb");
if (!file) {
ret = 1;
goto ret;
}
// Prepare To Write
png_init_io(png, file);
png_set_IHDR(png, info, width, height, 8 /* Depth */, PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
palette = (png_colorp) png_malloc(png, PNG_MAX_PALETTE_LENGTH * sizeof(png_color));
if (!palette) {
ret = 1;
goto ret;
}
png_set_PLTE(png, info, palette, PNG_MAX_PALETTE_LENGTH);
png_write_info(png, info);
png_set_packing(png);
// Write
png_write_image(png, rows);
png_write_end(png, info);
ret:
// Free
if (palette != NULL) {
png_free(png, palette);
}
if (file != NULL) {
fclose(file);
}
if (png != NULL) {
png_destroy_write_struct(&png, &info);
}
// Return
return ret;
}
void AppPlatform_sdl::saveScreenshot(const std::string &filename, int glWidth, int glHeight) {
// Get Directory
std::string screenshots = _storageDir + "/screenshots";
// Get Timestamp
time_t rawtime;
struct tm *timeinfo;
time(&rawtime);
timeinfo = localtime(&rawtime);
char time[TIME_SIZE];
strftime(time, TIME_SIZE, "%Y-%m-%d_%H.%M.%S", timeinfo);
// Ensure Screenshots Folder Exists
ensure_screenshots_folder(screenshots.c_str());
// Prevent Overwriting Screenshots
int num = 1;
std::string file = screenshots + '/' + time + ".png";
while (access(file.c_str(), F_OK) != -1) {
file = screenshots + '/' + time + '-' + std::to_string(num) + ".png";
num++;
}
// Get Image Size
GLint viewport[4];
glGetIntegerv(GL_VIEWPORT, viewport);
int x = viewport[0];
int y = viewport[1];
int width = viewport[2];
int height = viewport[3];
// Get Line Size
int line_size = width * 4;
{
// Handle Alignment
int alignment;
glGetIntegerv(GL_PACK_ALIGNMENT, &alignment);
// Round
int diff = line_size % alignment;
if (diff > 0) {
line_size = line_size + (alignment - diff);
}
}
int size = height * line_size;
// Read Pixels
bool fail = false;
unsigned char *pixels = (unsigned char *) malloc(size);
if (pixels == NULL) {
fail = true;
} else {
glReadPixels(x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
}
// Save Image
if (fail || save_png(file.c_str(), pixels, line_size, width, height)) {
LogMsg("Screenshot Failed: %s", file.c_str());
} else {
LogMsg("Screenshot Saved: %s", file.c_str());
}
// Free
if (pixels != NULL) {
free(pixels);
}
}
#endif
int AppPlatform_sdl::getScreenWidth() const {
int width;
SDL_GL_GetDrawableSize(_window, &width, nullptr);
return width;
}
int AppPlatform_sdl::getScreenHeight() const {
int height;
SDL_GL_GetDrawableSize(_window, nullptr, &height);
return height;
}
#ifndef __EMSCRIPTEN__
static void png_read_sdl(png_structp png_ptr, png_bytep data, png_size_t length) {
SDL_RWread((SDL_RWops *) png_get_io_ptr(png_ptr), (char *) data, length, 1);
}
static void nop_png_warning(png_structp png_ptr, png_const_charp warning_message) {
// Do Nothing
}
#endif
Texture AppPlatform_sdl::loadTexture(const std::string& str, bool b) {
Texture out;
out.field_C = 0;
out.field_D = 0;
std::string realPath = str;
if (realPath.size() && realPath[0] == '/') {
// trim it off
realPath = realPath.substr(1);
}
realPath = "assets/" + realPath;
#ifndef __EMSCRIPTEN__
SDL_RWops *io = SDL_RWFromFile(realPath.c_str(), "rb");
if (io != NULL) {
png_structp pngPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, nop_png_warning);
if (!pngPtr) {
return out;
}
png_infop infoPtr = png_create_info_struct(pngPtr);
if (!infoPtr) {
png_destroy_read_struct(&pngPtr, NULL, NULL);
return out;
}
png_set_read_fn(pngPtr, (png_voidp) io, png_read_sdl);
png_read_info(pngPtr, infoPtr);
png_set_expand(pngPtr);
png_set_strip_16(pngPtr);
png_set_gray_to_rgb(pngPtr);
png_read_update_info(pngPtr, infoPtr);
out.m_width = png_get_image_width(pngPtr, infoPtr);
out.m_height = png_get_image_height(pngPtr, infoPtr);
bool opaque = png_get_color_type(pngPtr, infoPtr) != PNG_COLOR_TYPE_RGBA;
if (!opaque) {
out.field_C = 1;
}
int pixelSize = opaque ? 3 : 4;
png_bytep *rowPtrs = new png_bytep[out.m_height];
unsigned char *pixels = new unsigned char[pixelSize * out.m_width * out.m_height];
int rowStrideBytes = pixelSize * out.m_width;
for (int i = 0; i < out.m_height; i++) {
rowPtrs[i] = (png_bytep) &pixels[i * rowStrideBytes];
}
png_read_image(pngPtr, rowPtrs);
out.m_pixels = (uint32_t *) pixels;
png_destroy_read_struct(&pngPtr, &infoPtr, (png_infopp) 0);
delete[](png_bytep) rowPtrs;
SDL_RWclose(io);
}
#else
char *data = emscripten_get_preloaded_image_data(("/" + realPath).c_str(), &out.m_width, &out.m_height);
if (data != NULL) {
size_t data_size = out.m_width * out.m_height * 4;
out.m_pixels = (uint32_t *) new unsigned char[data_size];
out.field_C = 1;
memcpy(out.m_pixels, data, data_size);
free(data);
return out;
}
#endif
LogMsg("Couldn't find file: %s", str.c_str());
return out;
}
void AppPlatform_sdl::setMouseGrabbed(bool b) {
SDL_SetWindowGrab(_window, b ? SDL_TRUE : SDL_FALSE);
SDL_SetRelativeMouseMode(b ? SDL_TRUE : SDL_FALSE);
}
void AppPlatform_sdl::setMouseDiff(int x, int y) {
xrel = x;
yrel = y;
}
void AppPlatform_sdl::getMouseDiff(int& x, int& y) {
x = xrel;
y = yrel;
}
void AppPlatform_sdl::clearDiff() {
xrel = 0;
yrel = 0;
}
bool AppPlatform_sdl::shiftPressed() {
return m_bShiftPressed[0] || m_bShiftPressed[1];
}
void AppPlatform_sdl::setShiftPressed(bool b, bool isLeft) {
m_bShiftPressed[isLeft ? 0 : 1] = b;
}
int AppPlatform_sdl::getUserInputStatus() {
return -1;
}

View File

@@ -0,0 +1,45 @@
#pragma once
#include <string>
#include <SDL2/SDL.h>
#include "AppPlatform.hpp"
#ifdef ORIGINAL_CODE
#error "This isn't original code. You probably shouldn't try to compile this"
#endif
void ensure_screenshots_folder(const char *screenshots);
class AppPlatform_sdl : public AppPlatform {
public:
AppPlatform_sdl(std::string storageDir, SDL_Window *window);
#ifndef __EMSCRIPTEN__
void saveScreenshot(const std::string& fileName, int width, int height) override;
#endif
int checkLicense() override;
int getScreenWidth() const override;
int getScreenHeight() const override;
Texture loadTexture(const std::string& str, bool b) override;
int getUserInputStatus() override;
// Also add these to allow proper turning within the game.
void setMouseGrabbed(bool b) override;
void setMouseDiff(int x, int y);
void getMouseDiff(int& x, int& y) override;
void clearDiff() override;
// Also add these to allow proper text input within the game.
bool shiftPressed() override;
void setShiftPressed(bool b, bool isLeft);
private:
std::string _storageDir;
SDL_Window *_window;
bool m_bShiftPressed[2] = {false, false};
int xrel;
int yrel;
};

View File

@@ -0,0 +1,64 @@
cmake_minimum_required(VERSION 3.16.0)
project(reminecraftpe)
# SDL Build
add_compile_definitions(USE_SDL HANDLE_CHARS_SEPARATELY)
# WASM
if(EMSCRIPTEN)
function(add_compile_and_link_options)
add_compile_options(${ARGV})
add_link_options(${ARGV})
endfunction()
set(CMAKE_EXECUTABLE_SUFFIX ".js")
endif()
# Clang
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
add_compile_options(-Wno-inconsistent-missing-override -Wno-enum-compare-switch -Wno-register)
endif()
# Threads
if(EMSCRIPTEN)
add_compile_and_link_options(-pthread)
else()
find_package(Threads)
link_libraries(Threads::Threads)
endif()
# Build
add_executable(reminecraftpe
main.cpp
AppPlatform_sdl.cpp
SoundSystemAL.cpp
)
# Core
add_subdirectory(../../source source)
target_link_libraries(reminecraftpe reminecraftpe-core)
# LibPNG
if(NOT EMSCRIPTEN)
find_package(PNG REQUIRED)
target_link_libraries(reminecraftpe PNG::PNG)
endif()
# SDL
if(TARGET SDL2::SDL2main)
target_link_libraries(reminecraftpe SDL2::SDL2main)
endif()
# WASM
if(EMSCRIPTEN)
target_link_options(reminecraftpe PRIVATE -Wno-pthreads-mem-growth)
target_link_options(reminecraftpe PRIVATE -sALLOW_MEMORY_GROWTH=1)
# Export Resize Function
target_link_options(reminecraftpe PRIVATE -sEXPORTED_FUNCTIONS=_main,_resize_from_js -sEXPORTED_RUNTIME_METHODS=ccall)
endif()
# Assets
if(EMSCRIPTEN)
target_link_options(reminecraftpe PRIVATE --use-preload-plugins --preload-file "${CMAKE_CURRENT_SOURCE_DIR}/assets@/assets")
elseif(NOT EXISTS "${CMAKE_CURRENT_BINARY_DIR}/assets")
file(CREATE_LINK "${CMAKE_CURRENT_SOURCE_DIR}/assets" "${CMAKE_CURRENT_BINARY_DIR}/assets" SYMBOLIC)
endif()

View File

@@ -0,0 +1,245 @@
#include "SoundSystemAL.hpp"
#include "client/common/Utils.hpp"
SoundSystemAL::SoundSystemAL() {
device = alcOpenDevice(NULL);
if (!device) {
LogMsg("Unable To Load Audio Engine");
return;
}
// Create Context
context = alcCreateContext(device, NULL);
ALCenum err = alcGetError(device);
if (err != ALC_NO_ERROR) {
LogMsg("Unable To Open Audio Context: %s", alcGetString(device, err));
return;
}
// Select Context
alcMakeContextCurrent(context);
err = alcGetError(device);
if (err != ALC_NO_ERROR) {
LogMsg("Unable To Select Audio Context: %s", alcGetString(device, err));
return;
}
// Set Distance Model
alDistanceModel(AL_LINEAR_DISTANCE_CLAMPED);
// Mark As Loaded
loaded = true;
}
SoundSystemAL::~SoundSystemAL() {
if (loaded) {
// Delete Audio Sources
delete_sources();
// Delete Audio Buffers
delete_buffers();
// Deselect Context
alcMakeContextCurrent(NULL);
ALCenum err = alcGetError(device);
if (err != ALC_NO_ERROR) {
LogMsg("Unable To Deselect Audio Context: %s", alcGetString(device, err));
}
// Destroy Context
alcDestroyContext(context);
err = alcGetError(device);
if (err != ALC_NO_ERROR) {
LogMsg("Unable To Destroy Audio Context: %s", alcGetString(device, err));
}
// Close Device
alcCloseDevice(device);
err = alcGetError(device);
if (err != ALC_NO_ERROR) {
LogMsg("Unable To Close Audio Device: %s", alcGetString(device, err));
}
}
}
// Error Checking
#define AL_ERROR_CHECK() AL_ERROR_CHECK_MANUAL(alGetError())
#define AL_ERROR_CHECK_MANUAL(val) \
{ \
ALenum __err = val; \
if (__err != AL_NO_ERROR) { \
LogMsg("(%s:%i) OpenAL Error: %s", __FILE__, __LINE__, alGetString(__err)); \
exit(EXIT_FAILURE); \
} \
}
// Delete Sources
void SoundSystemAL::delete_sources() {
if (loaded) {
for (ALuint source : idle_sources) {
alDeleteSources(1, &source);
AL_ERROR_CHECK();
}
for (ALuint source : sources) {
alDeleteSources(1, &source);
AL_ERROR_CHECK();
}
}
idle_sources.clear();
sources.clear();
}
// Delete Buffers
void SoundSystemAL::delete_buffers() {
if (loaded) {
for (auto &it : buffers) {
if (it.second && alIsBuffer(it.second)) {
alDeleteBuffers(1, &it.second);
AL_ERROR_CHECK();
}
}
}
buffers.clear();
}
// Get Buffer
ALuint SoundSystemAL::get_buffer(const SoundDesc &sound) {
if (buffers.count(sound.m_pData) > 0) {
return buffers[sound.m_pData];
} else {
// Sound Format
ALenum format = AL_NONE;
if (sound.m_header.m_channels == 1) {
format = sound.m_header.m_bytes_per_sample == 2 ? AL_FORMAT_MONO16 : AL_FORMAT_MONO8;
} else if (sound.m_header.m_channels == 2) {
format = sound.m_header.m_bytes_per_sample == 2 ? AL_FORMAT_STEREO16 : AL_FORMAT_STEREO8;
}
// Sound Data Size
int size = sound.m_header.m_length * sound.m_header.m_bytes_per_sample;
// Create Buffer
ALuint buffer;
alGenBuffers(1, &buffer);
AL_ERROR_CHECK();
alBufferData(buffer, format, sound.m_pData, size, sound.m_header.m_sample_rate);
AL_ERROR_CHECK();
// Store
buffers[sound.m_pData] = buffer;
return buffer;
}
}
void SoundSystemAL::update(float x, float y, float z, float yaw) {
// Check
if (loaded) {
// Update Listener Volume
float volume = 1;
alListenerf(AL_GAIN, volume);
AL_ERROR_CHECK();
// Update Listener Position
alListener3f(AL_POSITION, x, y, z);
AL_ERROR_CHECK();
// Update Listener Orientation
float radian_yaw = yaw * (M_PI / 180);
ALfloat orientation[] = {-sinf(radian_yaw), 0.0f, cosf(radian_yaw), 0.0f, 1.0f, 0.0f};
alListenerfv(AL_ORIENTATION, orientation);
AL_ERROR_CHECK();
// Clear Finished Sources
std::vector<ALuint>::iterator it = sources.begin();
while (it != sources.end()) {
ALuint source = *it;
bool remove = false;
// Check
if (source && alIsSource(source)) {
// Is Valid Source
ALint source_state;
alGetSourcei(source, AL_SOURCE_STATE, &source_state);
AL_ERROR_CHECK();
if (source_state != AL_PLAYING) {
// Finished Playing
remove = true;
if (idle_sources.size() < MAX_IDLE_SOURCES) {
idle_sources.push_back(source);
} else {
alDeleteSources(1, &source);
AL_ERROR_CHECK();
}
}
} else {
// Not A Source
remove = true;
}
// Remove If Needed
if (remove) {
it = sources.erase(it);
} else {
++it;
}
}
}
}
void SoundSystemAL::play(const SoundDesc &sound, float x, float y, float z, float volume, float pitch, bool is_ui) {
if (loaded) {
// Load Sound
ALuint buffer = get_buffer(sound);
if (volume > 0.0f && buffer) {
// Get Source
ALuint al_source;
if (idle_sources.size() > 0) {
// Use Idle Source
al_source = idle_sources.back();
idle_sources.pop_back();
} else {
// Create Source
alGenSources(1, &al_source);
// Special Out-Of-Memory Handling
{
ALenum err = alGetError();
if (err == AL_OUT_OF_MEMORY) {
return;
} else {
AL_ERROR_CHECK_MANUAL(err);
}
}
}
// Set Properties
alSourcef(al_source, AL_PITCH, pitch);
AL_ERROR_CHECK();
alSourcef(al_source, AL_GAIN, volume);
AL_ERROR_CHECK();
alSource3f(al_source, AL_POSITION, x, y, z);
AL_ERROR_CHECK();
alSource3f(al_source, AL_VELOCITY, 0, 0, 0);
AL_ERROR_CHECK();
alSourcei(al_source, AL_LOOPING, AL_FALSE);
AL_ERROR_CHECK();
alSourcei(al_source, AL_SOURCE_RELATIVE, is_ui ? AL_TRUE : AL_FALSE);
AL_ERROR_CHECK();
// Set Attenuation
alSourcef(al_source, AL_MAX_DISTANCE, 16.0f);
AL_ERROR_CHECK();
alSourcef(al_source, AL_ROLLOFF_FACTOR, 6.0f);
AL_ERROR_CHECK();
alSourcef(al_source, AL_REFERENCE_DISTANCE, 5.0f);
AL_ERROR_CHECK();
// Set Buffer
alSourcei(al_source, AL_BUFFER, buffer);
AL_ERROR_CHECK();
// Play
alSourcePlay(al_source);
AL_ERROR_CHECK();
sources.push_back(al_source);
}
}
}

View File

@@ -0,0 +1,31 @@
#pragma once
#include <AL/al.h>
#include <AL/alc.h>
#include <string>
#include <vector>
#include <unordered_map>
#include "client/sound/SoundData.hpp"
#define MAX_IDLE_SOURCES 50
class SoundSystemAL {
public:
SoundSystemAL();
~SoundSystemAL();
void update(float x, float y, float z, float yaw);
void play(const SoundDesc &sound, float x, float y, float z, float volume, float pitch, bool is_ui);
private:
void delete_sources();
void delete_buffers();
ALuint get_buffer(const SoundDesc &sound);
ALCdevice *device = NULL;
ALCcontext *context = NULL;
bool loaded = false;
std::vector<ALuint> sources;
std::vector<ALuint> idle_sources;
std::unordered_map<void *, ALuint> buffers;
};

258
platforms/sdl/main.cpp Normal file
View File

@@ -0,0 +1,258 @@
#include <cstdarg>
#include <SDL2/SDL.h>
#include "compat/GL.hpp"
#include "compat/AKeyCodes.hpp"
#include "App.hpp"
#include "AppPlatform_sdl.hpp"
#include "NinecraftApp.hpp"
#ifdef __EMSCRIPTEN__
#include <emscripten.h>
#include <emscripten/html5.h>
#endif
void LogMsg(const char* fmt, ...) {
va_list lst;
va_start(lst, fmt);
vprintf(fmt, lst);
printf("\n");
va_end(lst);
}
// I hate duplicating code, but yeah
void LogMsgNoCR(const char* fmt, ...) {
va_list lst;
va_start(lst, fmt);
vprintf(fmt, lst);
va_end(lst);
}
AppPlatform_sdl *g_AppPlatform;
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;
}
}
// 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: {
Keyboard::feed(event.key.state == SDL_PRESSED ? 1 : 0, translate_sdl_key_to_mcpe(event.key.keysym.sym));
if (event.key.keysym.sym == SDLK_LSHIFT || event.key.keysym.sym == SDLK_RSHIFT) {
g_AppPlatform->setShiftPressed(event.key.state == SDL_PRESSED, event.key.keysym.sym == SDLK_LSHIFT);
}
break;
}
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP: {
Mouse::feed(event.button.button == SDL_BUTTON_LEFT ? 1 : 2, event.button.state == SDL_PRESSED ? 1 : 0, event.button.x, event.button.y);
break;
}
case SDL_MOUSEMOTION: {
Mouse::_x = event.motion.x;
Mouse::_y = event.motion.y;
Mouse::feed(0, 0, event.motion.x, event.motion.y);
g_AppPlatform->setMouseDiff(event.motion.xrel, event.motion.yrel);
break;
}
case SDL_MOUSEWHEEL: {
Mouse::feed(3, event.wheel.y, Mouse::_x, Mouse::_y);
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;
}
}
}
}
// GUI Scale
static void calulate_gui_scale() {
int width = Minecraft::width;
// Modified Version Of https://github.com/MCPI-Revival/Ninecraft/blob/3f71638a10b581f6a50669edb24bc1ef1a92fbea/ninecraft/src/main.c#L243-L255
if (width < 1000) {
if (width < 400) {
Gui::InvGuiScale = 1.0;
} else {
Gui::InvGuiScale = 0.5;
}
} else {
Gui::InvGuiScale = 0.25;
}
}
// Resizing
static void resize() {
SDL_GL_GetDrawableSize(window, &Minecraft::width, &Minecraft::height);
calulate_gui_scale();
if (g_pApp != nullptr && g_pApp->m_pScreen != nullptr) {
g_pApp->m_pScreen->setSize(Minecraft::width * Gui::InvGuiScale, Minecraft::height * Gui::InvGuiScale);
}
}
// Main Loop
#ifndef __EMSCRIPTEN__
#define EM_BOOL bool
#define EM_TRUE true
#define EM_FALSE false
#endif
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()) {
delete g_pApp;
delete g_AppPlatform;
teardown();
// Stop Looping
return EM_FALSE;
} else {
// Keep Looping
return EM_TRUE;
}
}
// Main
int main(int argc, char *argv[]) {
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
LOGE("Unable To Initialize SDL: %s\n", 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
window = SDL_CreateWindow("ReMinecraftPE", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, Minecraft::width, Minecraft::height, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE);
if (!window) {
LOGE("Unable To Create SDL Window\n");
exit(EXIT_FAILURE);
}
// Enable Text Input
SDL_StartTextInput();
// Create OpenGL ES Context
context = SDL_GL_CreateContext(window);
if (!context) {
LOGE("Unable To Create OpenGL Context\n");
exit(EXIT_FAILURE);
}
// Setup Compatibility Layer If Needed
#ifdef USE_GLES1_COMPATIBILITY_LAYER
init_gles_compatibility_layer();
#endif
// Setup Teardown
#ifndef __EMSCRIPTEN__
atexit(teardown);
#endif
// Set Size
resize();
// Storage Directory
#ifdef _WIN32
std::string storagePath = getenv("APPDATA");
#elif defined(__EMSCRIPTEN__)
std::string storagePath = "";
#else
std::string storagePath = getenv("HOME");
#endif
storagePath += "/.reminecraftpe";
ensure_screenshots_folder(storagePath.c_str());
// Start MCPE
g_pApp = new NinecraftApp;
g_pApp->m_externalStorageDir = storagePath;
g_AppPlatform = new AppPlatform_sdl(g_pApp->m_externalStorageDir, window);
g_pApp->m_pPlatform = g_AppPlatform;
g_pApp->init();
// 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;
}

View File

@@ -0,0 +1,54 @@
<!DOCTYPE html>
<html lang="en-us">
<head>
<meta charset="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>ReMinecraftPE</title>
<style>
html, body {
background-color: black;
width: 100%;
height: 100%;
margin: 0;
}
canvas {
display: block;
border: 0px none;
}
</style>
</head>
<body>
<canvas id="canvas" oncontextmenu="event.preventDefault()" tabindex=-1></canvas>
<script type="text/javascript" src="coi-serviceworker.min.js"></script>
<script type='text/javascript'>
var Module = {
print: function (text) {
if (arguments.length > 1) text = Array.prototype.slice.call(arguments).join(' ');
console.log(text);
},
printErr: function (text) {
if (arguments.length > 1) text = Array.prototype.slice.call(arguments).join(' ');
console.error(text);
},
canvas: (function () {
var canvas = document.getElementById('canvas');
// As a default initial behavior, pop up an alert when webgl context is lost. To make your
// application robust, you may want to override this behavior before shipping!
// See http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.15.2
canvas.addEventListener("webglcontextlost", function(e) { alert('WebGL context lost. You will need to reload the page.'); e.preventDefault(); }, false);
// Handle Resize
function setCanvasSize() {
Module.ccall('resize_from_js', null, ['number', 'number'], [window.innerWidth, window.innerHeight]);
}
window.addEventListener("resize", setCanvasSize);
return canvas;
})(),
arguments: [String(window.innerWidth), String(window.innerHeight)]
};
</script>
<script async type="text/javascript" src="reminecraftpe.js"></script>
</body>
</html>