Merge and add requested changes

This commit is contained in:
Bigjango13 2024-10-22 16:42:45 -07:00
commit 375c4be7ae
84 changed files with 1842 additions and 1022 deletions

@ -1 +1 @@
Subproject commit 16962f2f36a51b2acefad9cec3622f6de5730aa3
Subproject commit bae887e095d87e756d1bf4aa4f95a97693a97b62

View File

@ -48,6 +48,9 @@
* `Click Buttons On Mouse Down` (Enabled By Default)
* `3D Dropped Items` (Enabled By Default)
* `Render Entity Shadows` (Enabled By Default)
* `Render Vignette` (Enabled By Default)
* `Increase Render Chunk Size` (Enabled By Default)
* `Proper Entity Shading` (Enabled By Default)
* Existing Functionality (All Enabled By Default)
* `Fix Screen Rendering When Hiding HUD`
* `Sanitize Usernames`

View File

@ -101,22 +101,57 @@ void bootstrap(const options_t &options) {
}
}
// Fix MCPI Dependencies
char new_mcpi_exe_path[] = MCPI_PATCHED_DIR "/XXXXXX";
std::string linker;
// Configure Preloaded Objects
std::vector<std::string> mcpi_ld_preload;
{
// Log
DEBUG("Patching ELF Dependencies...");
DEBUG("Locating Mods...");
// ARM Components
mcpi_ld_preload = bootstrap_mods(binary_directory);
}
// Configure Library Search Path
std::vector<std::string> mcpi_ld_path;
{
// Log
DEBUG("Setting Linker Search Paths...");
// Library Search Path For ARM Components
{
// Add ARM Library Directory
mcpi_ld_path.push_back("lib/arm");
// Add ARM Sysroot Libraries (Ensure Priority) (Ignore On Actual ARM System)
#ifdef MCPI_USE_PREBUILT_ARMHF_TOOLCHAIN
mcpi_ld_path.push_back("sysroot/lib");
mcpi_ld_path.push_back("sysroot/lib/arm-linux-gnueabihf");
mcpi_ld_path.push_back("sysroot/usr/lib");
mcpi_ld_path.push_back("sysroot/usr/lib/arm-linux-gnueabihf");
#endif
// Fix Paths
for (std::string &path : mcpi_ld_path) {
path = binary_directory + '/' + path;
}
}
}
// Fix MCPI Dependencies
char new_mcpi_exe_path[] = MCPI_PATCHED_DIR "/XXXXXX";
{
// Log
DEBUG("Patching ELF...");
// Find Linker
linker = "/lib/ld-linux-armhf.so.3";
std::string linker = "/lib/ld-linux-armhf.so.3";
#ifdef MCPI_USE_PREBUILT_ARMHF_TOOLCHAIN
// Use ARM Sysroot Linker
linker = binary_directory + "/sysroot" + linker;
#endif
// Patch
patch_mcpi_elf_dependencies(game_binary.c_str(), new_mcpi_exe_path);
patch_mcpi_elf_dependencies(game_binary, new_mcpi_exe_path, linker, mcpi_ld_path, mcpi_ld_preload);
// Verify
if (!starts_with(new_mcpi_exe_path, MCPI_PATCHED_DIR)) {
@ -132,37 +167,6 @@ void bootstrap(const options_t &options) {
set_and_print_env(_MCPI_VANILLA_ASSETS_PATH_ENV, assets_path.c_str());
}
// Configure Library Search Path
std::string mcpi_ld_path = "";
{
// Log
DEBUG("Setting Linker Search Paths...");
// Library Search Path For ARM Components
{
// Add ARM Library Directory
mcpi_ld_path += binary_directory + "/lib/arm:";
// Add ARM Sysroot Libraries (Ensure Priority) (Ignore On Actual ARM System)
#ifdef MCPI_USE_PREBUILT_ARMHF_TOOLCHAIN
mcpi_ld_path += binary_directory + "/sysroot/lib:";
mcpi_ld_path += binary_directory + "/sysroot/lib/arm-linux-gnueabihf:";
mcpi_ld_path += binary_directory + "/sysroot/usr/lib:";
mcpi_ld_path += binary_directory + "/sysroot/usr/lib/arm-linux-gnueabihf:";
#endif
}
}
// Configure Preloaded Objects
std::string mcpi_ld_preload;
{
// Log
DEBUG("Locating Mods...");
// ARM Components
mcpi_ld_preload = bootstrap_mods(binary_directory);
}
// Start Game
INFO("Starting Game...");
@ -178,13 +182,6 @@ void bootstrap(const options_t &options) {
args.push_back("0x40000"); // Arbitrary Value (Aligns To 4k And 16k Page Sizes)
#endif
// Setup Linker
args.push_back(linker);
args.push_back("--library-path");
args.push_back(mcpi_ld_path);
args.push_back("--preload");
args.push_back(mcpi_ld_preload);
// Specify MCPI Binary
args.push_back(new_mcpi_exe_path);

View File

@ -1,9 +1,10 @@
#pragma once
#include <string>
#include <vector>
#include "options/parser.h"
void bootstrap(const options_t &options);
void copy_sdk(const std::string &binary_directory, bool log_with_debug);
std::string bootstrap_mods(const std::string &binary_directory);
std::vector<std::string> bootstrap_mods(const std::string &binary_directory);

View File

@ -113,3 +113,5 @@ TRUE 3D Dropped Items
TRUE Render Entity Shadows
TRUE Render Vignette
TRUE Implement RaspberryJuice API
TRUE Increase Render Chunk Size
TRUE Proper Entity Shading

View File

@ -8,14 +8,14 @@
#include "bootstrap.h"
// Get All Mods In Folder
static void load(std::string &ld_preload, const std::string &folder, int recursion_limit = 128);
static void handle_file(std::string &ld_preload, const std::string &file, const int recursion_limit) {
static void load(std::vector<std::string> &ld_preload, const std::string &folder, int recursion_limit = 128);
static void handle_file(std::vector<std::string> &ld_preload, const std::string &file, const int recursion_limit) {
// Check Type
struct stat file_stat = {};
lstat(file.c_str(), &file_stat);
if (S_ISDIR(file_stat.st_mode)) {
// Recurse Into Directory
load(ld_preload, std::string(file) + "/", recursion_limit - 1);
load(ld_preload, std::string(file) + '/', recursion_limit - 1);
} else if (S_ISLNK(file_stat.st_mode)) {
// Resolve Symlink
char *resolved_file = realpath(file.c_str(), nullptr);
@ -27,7 +27,8 @@ static void handle_file(std::string &ld_preload, const std::string &file, const
const int result = access(file.c_str(), R_OK);
if (result == 0) {
// Add To LD_PRELOAD
ld_preload += file + ":";
DEBUG("Found Mod: %s", file.c_str());
ld_preload.push_back(file);
} else if (result == -1 && errno != 0) {
// Fail
WARN("Unable To Access: %s: %s", file.c_str(), strerror(errno));
@ -35,7 +36,7 @@ static void handle_file(std::string &ld_preload, const std::string &file, const
}
}
}
static void load(std::string &ld_preload, const std::string &folder, const int recursion_limit) {
static void load(std::vector<std::string> &ld_preload, const std::string &folder, const int recursion_limit) {
// Check Recursion
if (recursion_limit <= 0) {
ERR("Reached Recursion Limit While Loading Mods");
@ -74,9 +75,9 @@ static void load(std::string &ld_preload, const std::string &folder, const int r
// Bootstrap Mods
#define SUBDIRECTORY_FOR_MODS "/mods/"
std::string bootstrap_mods(const std::string &binary_directory) {
std::vector<std::string> bootstrap_mods(const std::string &binary_directory) {
// Prepare
std::string preload = "";
std::vector<std::string> preload;
// ~/.minecraft-pi/mods
{

View File

@ -1,5 +1,6 @@
#include <cstdlib>
#include <sys/stat.h>
#include <ranges>
#include <LIEF/ELF.hpp>
@ -16,7 +17,7 @@ static void duplicate_mcpi_executable(char *new_path) {
ensure_directory(MCPI_PATCHED_DIR);
// Generate New File
int new_file_fd = mkstemp(new_path);
const int new_file_fd = mkstemp(new_path);
if (new_file_fd == -1) {
ERR("Unable To Create Temporary File: %s", strerror(errno));
}
@ -24,34 +25,72 @@ static void duplicate_mcpi_executable(char *new_path) {
}
// Fix MCPI Dependencies
static const char *libraries_to_remove[] = {
"libbcm_host.so",
"libX11.so.6",
"libEGL.so",
"libGLESv2.so",
"libSDL-1.2.so.0"
static std::vector<std::string> needed_libraries = {
"libmedia-layer-core.so",
"libpng12.so.0",
"libstdc++.so.6",
"libm.so.6",
"libgcc_s.so.1",
"libc.so.6",
"libpthread.so.0"
};
static const char *libraries_to_add[] = {
"libmedia-layer-core.so"
static std::vector<std::string> function_prefixes_to_patch = {
"SDL_",
"gl"
};
void patch_mcpi_elf_dependencies(const char *original_path, char *new_path) {
void patch_mcpi_elf_dependencies(const std::string &original_path, char *new_path, const std::string &interpreter, const std::vector<std::string> &rpath, const std::vector<std::string> &mods) {
// Duplicate MCPI executable into /tmp so it can be modified.
duplicate_mcpi_executable(new_path);
// Patch File
{
std::unique_ptr<LIEF::ELF::Binary> binary = LIEF::ELF::Parser::parse(original_path);
for (size_t i = 0; i < (sizeof (libraries_to_remove) / sizeof (const char *)); i++) {
binary->remove_library(libraries_to_remove[i]);
}
for (size_t i = 0; i < (sizeof (libraries_to_add) / sizeof (const char *)); i++) {
binary->add_library(libraries_to_add[i]);
}
LIEF::ELF::Builder builder{*binary};
builder.build();
builder.write(new_path);
// Load Binary
std::unique_ptr<LIEF::ELF::Binary> binary = LIEF::ELF::Parser::parse(original_path);
// Set Interpreter
if (!interpreter.empty()) {
binary->interpreter(interpreter);
}
// Remove Existing Needed Libraries
std::vector<std::string> to_remove;
for (const LIEF::ELF::DynamicEntry &entry : binary->dynamic_entries()) {
const LIEF::ELF::DynamicEntryLibrary *library = dynamic_cast<const LIEF::ELF::DynamicEntryLibrary *>(&entry);
if (library) {
to_remove.push_back(library->name());
}
}
for (const std::string &library : to_remove) {
binary->remove_library(library);
}
// Setup RPath
binary->add(LIEF::ELF::DynamicEntryRpath(rpath));
// Add Libraries
std::vector<std::string> all_libraries;
for (const std::vector<std::string> &list : {mods, needed_libraries}) {
all_libraries.insert(all_libraries.end(), list.begin(), list.end());
}
for (const std::string &library : all_libraries | std::views::reverse) {
binary->add_library(library);
}
// Fix Symbol Names
for (LIEF::ELF::Symbol &symbol : binary->dynamic_symbols()) {
if (symbol.is_function()) {
for (const std::string &prefix : function_prefixes_to_patch) {
if (symbol.name().rfind(prefix, 0) == 0) {
symbol.name("media_" + symbol.name());
break;
}
}
}
}
// Write Binary
LIEF::ELF::Builder builder{*binary};
builder.build();
builder.write(new_path);
// Fix Permissions
if (chmod(new_path, S_IRUSR | S_IXUSR) != 0) {
ERR("Unable To Set File Permissions: %s: %s", new_path, strerror(errno));

View File

@ -1,13 +1,8 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <string>
#include <vector>
#define MCPI_PATCHED_DIR "/tmp/.minecraft-pi-patched"
void patch_mcpi_elf_dependencies(const char *original_path, char *new_path);
#ifdef __cplusplus
}
#endif
void patch_mcpi_elf_dependencies(const std::string &original_path, char *new_path, const std::string &interpreter, const std::vector<std::string> &rpath, const std::vector<std::string> &mods);

View File

@ -10,7 +10,7 @@
void reborn_init_patch();
// Replace Call Located At start With A Call To target
void overwrite_call(void *start, void *target);
void overwrite_call(void *start, void *target, bool force_b_instruction = false);
// Replace All Calls To Method start With target
void *overwrite_calls_manual(void *start, void *target, bool allow_no_callsites = false);

View File

@ -21,8 +21,8 @@ static void _overwrite_call_internal(void *start, void *target, const bool use_b
// Increment Code Block Position
increment_code_block();
}
void overwrite_call(void *start, void *target) {
const bool use_b_instruction = ((unsigned char *) start)[3] == B_INSTRUCTION;
void overwrite_call(void *start, void *target, const bool force_b_instruction) {
const bool use_b_instruction = force_b_instruction || ((unsigned char *) start)[3] == B_INSTRUCTION;
_overwrite_call_internal(start, target, use_b_instruction);
}

View File

@ -19,9 +19,6 @@ if(BUILD_ARM_COMPONENTS)
)
endif()
# Add Extras
add_subdirectory(extras)
# Add Core
if(BUILD_MEDIA_LAYER_CORE)
add_subdirectory(gles)

View File

@ -4,10 +4,12 @@ project(media-layer-core)
set(CORE_SRC
src/base.cpp
src/media.cpp
src/cursor.cpp
src/util.cpp
src/events.cpp
src/audio/api.cpp
src/audio/engine.c
src/audio/file.cpp
$<TARGET_OBJECTS:media-layer-extras>
)
# Build

View File

@ -2,13 +2,14 @@
#include <SDL/SDL.h>
#include <media-layer/internal.h>
#include <media-layer/core.h>
#include <libreborn/libreborn.h>
#include "media.h"
// SDL Is Replaced With GLFW
int SDL_Init(__attribute__((unused)) uint32_t flags) {
int media_SDL_Init(__attribute__((unused)) uint32_t flags) {
return 0;
}
@ -16,9 +17,9 @@ int SDL_Init(__attribute__((unused)) uint32_t flags) {
static std::vector<SDL_Event> queue;
int SDL_PollEvent(SDL_Event *event) {
int media_SDL_PollEvent(SDL_Event *event) {
// Handle External Events
_media_handle_SDL_PollEvent();
_media_handle_media_SDL_PollEvent();
// Poll Event
int ret;
@ -32,7 +33,7 @@ int SDL_PollEvent(SDL_Event *event) {
return ret;
}
int SDL_PushEvent(SDL_Event *event) {
int media_SDL_PushEvent(SDL_Event *event) {
queue.push_back(*event);
return 1;
}

View File

@ -0,0 +1,108 @@
#include "media.h"
// Enable/Disable Raw Mouse Motion
bool raw_mouse_motion_enabled = true;
void media_set_raw_mouse_motion_enabled(const int enabled) {
raw_mouse_motion_enabled = enabled;
if (glfw_window) {
glfwSetInputMode(glfw_window, GLFW_RAW_MOUSE_MOTION, GLFW_FALSE);
}
if (!raw_mouse_motion_enabled) {
WARN("Raw mouse motion has been DISABLED, this IS NOT recommended, and should only ever be used on systems that don't support or have broken raw mouse motion.");
}
}
// Store Cursor State
static bool cursor_grabbed = false;
static bool cursor_visible = true;
// Ignore Relative Cursor Motion
bool ignore_relative_motion = false;
// Update GLFW Cursor State (Client Only)
void _media_update_cursor() {
if (glfw_window) {
// Get New State
const bool new_cursor_visible = is_interactable ? cursor_visible : true;
const bool new_cursor_grabbed = is_interactable ? cursor_grabbed : false;
// Store Old Mode
const int old_mode = glfwGetInputMode(glfw_window, GLFW_CURSOR);
// Handle Cursor Visibility
int new_mode;
if (!new_cursor_visible) {
if (new_cursor_grabbed) {
new_mode = GLFW_CURSOR_DISABLED;
} else {
new_mode = GLFW_CURSOR_HIDDEN;
}
} else {
new_mode = GLFW_CURSOR_NORMAL;
}
if (new_mode != old_mode) {
// Ignore Relative Cursor Motion When Locking
if (new_mode == GLFW_CURSOR_DISABLED && old_mode != GLFW_CURSOR_DISABLED) {
ignore_relative_motion = true;
}
// Set New Mode
glfwSetInputMode(glfw_window, GLFW_CURSOR, new_mode);
// Handle Cursor Lock/Unlock
if ((new_mode == GLFW_CURSOR_DISABLED && old_mode != GLFW_CURSOR_DISABLED) || (new_mode != GLFW_CURSOR_DISABLED && old_mode == GLFW_CURSOR_DISABLED)) {
// Use Raw Mouse Motion
if (raw_mouse_motion_enabled) {
glfwSetInputMode(glfw_window, GLFW_RAW_MOUSE_MOTION, new_mode == GLFW_CURSOR_DISABLED ? GLFW_TRUE : GLFW_FALSE);
}
// Request Focus
glfwRequestWindowAttention(glfw_window);
}
// Reset Mouse Position When Unlocking
if (new_mode != GLFW_CURSOR_DISABLED && old_mode == GLFW_CURSOR_DISABLED) {
double cursor_x;
double cursor_y;
glfwGetCursorPos(glfw_window, &cursor_x, &cursor_y);
_media_glfw_motion(glfw_window, cursor_x, cursor_y);
}
}
}
}
// Fix SDL Cursor Visibility/Grabbing
SDL_GrabMode media_SDL_WM_GrabInput(const SDL_GrabMode mode) {
if (mode == SDL_GRAB_QUERY) {
// Query
return cursor_grabbed ? SDL_GRAB_ON : SDL_GRAB_OFF;
} else if (mode == SDL_GRAB_ON) {
// Store State
cursor_grabbed = true;
} else if (mode == SDL_GRAB_OFF) {
// Store State
cursor_grabbed = false;
}
// Update Cursor GLFW State (Client Only)
_media_update_cursor();
// Return
return mode;
}
// Stub SDL Cursor Visibility
int media_SDL_ShowCursor(const int toggle) {
if (toggle == SDL_QUERY) {
// Query
return cursor_visible ? SDL_ENABLE : SDL_DISABLE;
} else if (toggle == SDL_ENABLE) {
// Store State
cursor_visible = true;
} else if (toggle == SDL_DISABLE) {
// Store State
cursor_visible = false;
}
// Update Cursor GLFW State (Client Only)
_media_update_cursor();
// Return
return toggle;
}

View File

@ -0,0 +1,275 @@
#include "media.h"
// Convert GLFW Key To SDL Key
static SDLKey glfw_key_to_sdl_key(const int key) {
switch (key) {
// Movement
case GLFW_KEY_W:
return SDLK_w;
case GLFW_KEY_A:
return SDLK_a;
case GLFW_KEY_S:
return SDLK_s;
case GLFW_KEY_D:
return SDLK_d;
case GLFW_KEY_SPACE:
return SDLK_SPACE;
case GLFW_KEY_LEFT_SHIFT:
return SDLK_LSHIFT;
case GLFW_KEY_RIGHT_SHIFT:
return SDLK_RSHIFT;
// Inventory
case GLFW_KEY_E:
return SDLK_e;
// Drop Item
case GLFW_KEY_Q:
return SDLK_q;
// Toolbar
case GLFW_KEY_1:
return SDLK_1;
case GLFW_KEY_2:
return SDLK_2;
case GLFW_KEY_3:
return SDLK_3;
case GLFW_KEY_4:
return SDLK_4;
case GLFW_KEY_5:
return SDLK_5;
case GLFW_KEY_6:
return SDLK_6;
case GLFW_KEY_7:
return SDLK_7;
case GLFW_KEY_8:
return SDLK_8;
case GLFW_KEY_9:
return SDLK_9;
case GLFW_KEY_0:
return SDLK_0;
// UI Control
case GLFW_KEY_ESCAPE:
return SDLK_ESCAPE;
case GLFW_KEY_UP:
return SDLK_UP;
case GLFW_KEY_DOWN:
return SDLK_DOWN;
case GLFW_KEY_LEFT:
return SDLK_LEFT;
case GLFW_KEY_RIGHT:
return SDLK_RIGHT;
case GLFW_KEY_TAB:
return SDLK_TAB;
case GLFW_KEY_ENTER:
return SDLK_RETURN;
case GLFW_KEY_BACKSPACE:
return SDLK_BACKSPACE;
case GLFW_KEY_DELETE:
return SDLK_DELETE;
// Fullscreen
case GLFW_KEY_F11:
return SDLK_F11;
// Screenshot
case GLFW_KEY_F2:
return SDLK_F2;
// Debug
case GLFW_KEY_F3:
return SDLK_F3;
// Hide GUI
case GLFW_KEY_F1:
return SDLK_F1;
// Third Person
case GLFW_KEY_F5:
return SDLK_F5;
// Chat
case GLFW_KEY_T:
return SDLK_t;
// Unknown
default:
return SDLK_UNKNOWN;
}
}
// Convert GLFW Key Modifier To SDL Key Modifier
static SDLMod glfw_modifier_to_sdl_modifier(const int mods) {
int ret = KMOD_NONE;
// Control
if ((mods & GLFW_MOD_CONTROL) != 0) {
ret |= KMOD_CTRL;
}
// Shift
if ((mods & GLFW_MOD_SHIFT) != 0) {
ret |= KMOD_SHIFT;
}
// Alt
if ((mods & GLFW_MOD_ALT) != 0) {
ret |= KMOD_ALT;
}
// Return
return SDLMod(ret);
}
// Pass Key Presses To SDL
static void glfw_key_raw(int key, int scancode, int action, int mods) {
SDL_Event event1;
bool up = action == GLFW_RELEASE;
event1.type = up ? SDL_KEYUP : SDL_KEYDOWN;
event1.key.state = up ? SDL_RELEASED : SDL_PRESSED;
event1.key.keysym.scancode = scancode;
event1.key.keysym.mod = glfw_modifier_to_sdl_modifier(mods);
event1.key.keysym.sym = glfw_key_to_sdl_key(key);
media_SDL_PushEvent(&event1);
// Allow MCPI To Access Original GLFW Keycode
SDL_Event event2;
event2.type = SDL_USEREVENT;
event2.user.code = USER_EVENT_REAL_KEY;
event2.user.data1 = event1.key.state;
event2.user.data2 = key;
media_SDL_PushEvent(&event2);
}
static void glfw_key(__attribute__((unused)) GLFWwindow *window, const int key, const int scancode, const int action, const int mods) {
if (is_interactable) {
glfw_key_raw(key, scancode, action, mods);
}
}
// Pass Text To Minecraft
static void character_event(char c) {
if (!is_interactable) {
return;
}
// SDL_UserEvent Is Never Used In MCPI, So It Is Repurposed For Character Events
SDL_Event event;
event.type = SDL_USEREVENT;
event.user.code = USER_EVENT_CHARACTER;
event.user.data1 = (int) c;
media_SDL_PushEvent(&event);
}
static void codepoint_to_utf8(unsigned char *const buffer, const unsigned int code) {
// https://stackoverflow.com/a/42013433/16198887
if (code <= 0x7f) {
buffer[0] = code;
} else if (code <= 0x7ff) {
buffer[0] = 0xc0 | (code >> 6); // 110xxxxx
buffer[1] = 0x80 | (code & 0x3f); // 10xxxxxx
} else if (code <= 0xffff) {
buffer[0] = 0xe0 | (code >> 12); // 1110xxxx
buffer[1] = 0x80 | ((code >> 6) & 0x3f); // 10xxxxxx
buffer[2] = 0x80 | (code & 0x3f); // 10xxxxxx
} else if (code <= 0x10ffff) {
buffer[0] = 0xf0 | (code >> 18); // 11110xxx
buffer[1] = 0x80 | ((code >> 12) & 0x3f); // 10xxxxxx
buffer[2] = 0x80 | ((code >> 6) & 0x3f); // 10xxxxxx
buffer[3] = 0x80 | (code & 0x3f); // 10xxxxxx
}
}
static void glfw_char(__attribute__((unused)) GLFWwindow *window, const unsigned int codepoint) {
// Convert
size_t str_size = 4 /* Maximum UTF-8 character size */ + 1 /* NULL-terminator */;
char str[str_size] = {};
codepoint_to_utf8((unsigned char *) str, codepoint);
char *cp437_str = to_cp437(str);
// Send Event
for (int i = 0; cp437_str[i] != '\0'; i++) {
character_event(cp437_str[i]);
}
// Free
free(cp437_str);
}
// Convert Screen Coordinates To Pixels
static void convert_to_pixels(GLFWwindow *window, double *xpos, double *ypos) {
// Skip If Cursor Is Grabbed
if (media_SDL_WM_GrabInput(SDL_GRAB_QUERY) == SDL_GRAB_ON && raw_mouse_motion_enabled) {
return;
}
// Get Window Size
int window_width;
int window_height;
glfwGetWindowSize(window, &window_width, &window_height);
// Get Framebuffer Size
int framebuffer_width;
int framebuffer_height;
glfwGetFramebufferSize(window, &framebuffer_width, &framebuffer_height);
// Calculate Ratios
const double width_ratio = ((double) framebuffer_width) / ((double) window_width);
const double height_ratio = ((double) framebuffer_height) / ((double) window_height);
// Multiply
*xpos *= width_ratio;
*ypos *= height_ratio;
}
// Last Mouse Location
static double last_mouse_x = 0;
static double last_mouse_y = 0;
// Pass Mouse Movement To SDL
void _media_glfw_motion(__attribute__((unused)) GLFWwindow *window, double xpos, double ypos) {
convert_to_pixels(window, &xpos, &ypos);
if (is_interactable) {
SDL_Event event;
event.type = SDL_MOUSEMOTION;
event.motion.x = uint16_t(xpos);
event.motion.y = uint16_t(ypos);
event.motion.xrel = !ignore_relative_motion ? (xpos - last_mouse_x) : 0;
event.motion.yrel = !ignore_relative_motion ? (ypos - last_mouse_y) : 0;
media_SDL_PushEvent(&event);
}
ignore_relative_motion = false;
last_mouse_x = xpos;
last_mouse_y = ypos;
}
// Create And Push SDL Mouse Click Event
static void click_event(int button, bool up) {
SDL_Event event;
event.type = up ? SDL_MOUSEBUTTONUP : SDL_MOUSEBUTTONDOWN;
event.button.x = uint16_t(last_mouse_x);
event.button.y = uint16_t(last_mouse_y);
event.button.state = up ? SDL_RELEASED : SDL_PRESSED;
event.button.button = button;
media_SDL_PushEvent(&event);
}
// Pass Mouse Click To SDL
static void glfw_click_raw(const int button, const int action) {
const bool up = action == GLFW_RELEASE;
const int sdl_button = button == GLFW_MOUSE_BUTTON_RIGHT ? SDL_BUTTON_RIGHT : (button == GLFW_MOUSE_BUTTON_LEFT ? SDL_BUTTON_LEFT : SDL_BUTTON_MIDDLE);
click_event(sdl_button, up);
}
static void glfw_click(__attribute__((unused)) GLFWwindow *window, const int button, const int action, __attribute__((unused)) int mods) {
if (is_interactable) {
glfw_click_raw(button, action);
}
}
// Pass Mouse Scroll To SDL
static void glfw_scroll(__attribute__((unused)) GLFWwindow *window, __attribute__((unused)) double xoffset, double yoffset) {
if (is_interactable && yoffset != 0) {
const int sdl_button = yoffset > 0 ? SDL_BUTTON_WHEELUP : SDL_BUTTON_WHEELDOWN;
click_event(sdl_button, false);
click_event(sdl_button, true);
}
}
// Intercept SDL Events
void _media_handle_media_SDL_PollEvent() {
if (glfw_window) {
// Process GLFW Events
glfwPollEvents();
// Close Window
if (glfwWindowShouldClose(glfw_window)) {
SDL_Event event;
event.type = SDL_QUIT;
media_SDL_PushEvent(&event);
glfwSetWindowShouldClose(glfw_window, GLFW_FALSE);
}
}
}
// Register Event Listeners
void _media_register_event_listeners() {
glfwSetKeyCallback(glfw_window, glfw_key);
glfwSetCharCallback(glfw_window, glfw_char);
glfwSetCursorPosCallback(glfw_window, _media_glfw_motion);
glfwSetMouseButtonCallback(glfw_window, glfw_click);
glfwSetScrollCallback(glfw_window, glfw_scroll);
}

View File

@ -1,309 +1,21 @@
#include <ctime>
#include <unistd.h>
#include <SDL/SDL.h>
#include <libreborn/libreborn.h>
#define GLFW_INCLUDE_NONE
#include <GLFW/glfw3.h>
#include <media-layer/core.h>
#include <media-layer/internal.h>
#include "media.h"
#include "audio/engine.h"
// Allow Disabling Interaction
static void update_cursor();
static int is_interactable = 1;
void media_set_interactable(const int toggle) {
if (bool(toggle) != is_interactable) {
is_interactable = toggle;
update_cursor();
}
}
// Store Cursor State
static bool cursor_grabbed = false;
static bool cursor_visible = true;
// Track If Raw Mouse Motion Is Enabled
static bool raw_mouse_motion_enabled = true;
// Window
static GLFWwindow *glfw_window = nullptr;
GLFWwindow *glfw_window = nullptr;
// Handle GLFW Error
static void glfw_error(__attribute__((unused)) int error, const char *description) {
WARN("GLFW Error: %s", description);
}
// Convert GLFW Key To SDL Key
static SDLKey glfw_key_to_sdl_key(const int key) {
switch (key) {
// Movement
case GLFW_KEY_W:
return SDLK_w;
case GLFW_KEY_A:
return SDLK_a;
case GLFW_KEY_S:
return SDLK_s;
case GLFW_KEY_D:
return SDLK_d;
case GLFW_KEY_SPACE:
return SDLK_SPACE;
case GLFW_KEY_LEFT_SHIFT:
return SDLK_LSHIFT;
case GLFW_KEY_RIGHT_SHIFT:
return SDLK_RSHIFT;
// Inventory
case GLFW_KEY_E:
return SDLK_e;
// Drop Item
case GLFW_KEY_Q:
return SDLK_q;
// Toolbar
case GLFW_KEY_1:
return SDLK_1;
case GLFW_KEY_2:
return SDLK_2;
case GLFW_KEY_3:
return SDLK_3;
case GLFW_KEY_4:
return SDLK_4;
case GLFW_KEY_5:
return SDLK_5;
case GLFW_KEY_6:
return SDLK_6;
case GLFW_KEY_7:
return SDLK_7;
case GLFW_KEY_8:
return SDLK_8;
case GLFW_KEY_9:
return SDLK_9;
case GLFW_KEY_0:
return SDLK_0;
// UI Control
case GLFW_KEY_ESCAPE:
return SDLK_ESCAPE;
case GLFW_KEY_UP:
return SDLK_UP;
case GLFW_KEY_DOWN:
return SDLK_DOWN;
case GLFW_KEY_LEFT:
return SDLK_LEFT;
case GLFW_KEY_RIGHT:
return SDLK_RIGHT;
case GLFW_KEY_TAB:
return SDLK_TAB;
case GLFW_KEY_ENTER:
return SDLK_RETURN;
case GLFW_KEY_BACKSPACE:
return SDLK_BACKSPACE;
case GLFW_KEY_DELETE:
return SDLK_DELETE;
// Fullscreen
case GLFW_KEY_F11:
return SDLK_F11;
// Screenshot
case GLFW_KEY_F2:
return SDLK_F2;
// Debug
case GLFW_KEY_F3:
return SDLK_F3;
// Hide GUI
case GLFW_KEY_F1:
return SDLK_F1;
// Third Person
case GLFW_KEY_F5:
return SDLK_F5;
// Chat
case GLFW_KEY_T:
return SDLK_t;
// Unknown
default:
return SDLK_UNKNOWN;
}
}
// Convert GLFW Key Modifier To SDL Key Modifier
static SDLMod glfw_modifier_to_sdl_modifier(const int mods) {
int ret = KMOD_NONE;
// Control
if ((mods & GLFW_MOD_CONTROL) != 0) {
ret |= KMOD_CTRL;
}
// Shift
if ((mods & GLFW_MOD_SHIFT) != 0) {
ret |= KMOD_SHIFT;
}
// Alt
if ((mods & GLFW_MOD_ALT) != 0) {
ret |= KMOD_ALT;
}
// Return
return SDLMod(ret);
}
// Pass Key Presses To SDL
static void glfw_key_raw(int key, int scancode, int action, int mods) {
SDL_Event event1;
bool up = action == GLFW_RELEASE;
event1.type = up ? SDL_KEYUP : SDL_KEYDOWN;
event1.key.state = up ? SDL_RELEASED : SDL_PRESSED;
event1.key.keysym.scancode = scancode;
event1.key.keysym.mod = glfw_modifier_to_sdl_modifier(mods);
event1.key.keysym.sym = glfw_key_to_sdl_key(key);
SDL_PushEvent(&event1);
// Allow MCPI To Access Original GLFW Keycode
SDL_Event event2;
event2.type = SDL_USEREVENT;
event2.user.code = USER_EVENT_REAL_KEY;
event2.user.data1 = event1.key.state;
event2.user.data2 = key;
SDL_PushEvent(&event2);
}
static void glfw_key(__attribute__((unused)) GLFWwindow *window, const int key, const int scancode, const int action, const int mods) {
if (is_interactable) {
glfw_key_raw(key, scancode, action, mods);
}
}
// Pass Text To Minecraft
static void character_event(char c) {
if (!is_interactable) {
return;
}
// SDL_UserEvent Is Never Used In MCPI, So It Is Repurposed For Character Events
SDL_Event event;
event.type = SDL_USEREVENT;
event.user.code = USER_EVENT_CHARACTER;
event.user.data1 = (int) c;
SDL_PushEvent(&event);
}
static void codepoint_to_utf8(unsigned char *const buffer, const unsigned int code) {
// https://stackoverflow.com/a/42013433/16198887
if (code <= 0x7f) {
buffer[0] = code;
} else if (code <= 0x7ff) {
buffer[0] = 0xc0 | (code >> 6); // 110xxxxx
buffer[1] = 0x80 | (code & 0x3f); // 10xxxxxx
} else if (code <= 0xffff) {
buffer[0] = 0xe0 | (code >> 12); // 1110xxxx
buffer[1] = 0x80 | ((code >> 6) & 0x3f); // 10xxxxxx
buffer[2] = 0x80 | (code & 0x3f); // 10xxxxxx
} else if (code <= 0x10ffff) {
buffer[0] = 0xf0 | (code >> 18); // 11110xxx
buffer[1] = 0x80 | ((code >> 12) & 0x3f); // 10xxxxxx
buffer[2] = 0x80 | ((code >> 6) & 0x3f); // 10xxxxxx
buffer[3] = 0x80 | (code & 0x3f); // 10xxxxxx
}
}
static void glfw_char(__attribute__((unused)) GLFWwindow *window, const unsigned int codepoint) {
// Convert
size_t str_size = 4 /* Maximum UTF-8 character size */ + 1 /* NULL-terminator */;
char str[str_size] = {};
codepoint_to_utf8((unsigned char *) str, codepoint);
char *cp437_str = to_cp437(str);
// Send Event
for (int i = 0; cp437_str[i] != '\0'; i++) {
character_event(cp437_str[i]);
}
// Free
free(cp437_str);
}
// Last Mouse Location
static double last_mouse_x = 0;
static double last_mouse_y = 0;
// Ignore Relative Cursor Motion
static bool ignore_relative_motion = false;
// Convert Screen Coordinates To Pixels
static void convert_to_pixels(GLFWwindow *window, double *xpos, double *ypos) {
// Skip If Cursor Is Grabbed
if (cursor_grabbed && raw_mouse_motion_enabled) {
return;
}
// Get Window Size
int window_width;
int window_height;
glfwGetWindowSize(window, &window_width, &window_height);
// Get Framebuffer Size
int framebuffer_width;
int framebuffer_height;
glfwGetFramebufferSize(window, &framebuffer_width, &framebuffer_height);
// Calculate Ratios
const double width_ratio = ((double) framebuffer_width) / ((double) window_width);
const double height_ratio = ((double) framebuffer_height) / ((double) window_height);
// Multiply
*xpos *= width_ratio;
*ypos *= height_ratio;
}
// Pass Mouse Movement To SDL
static void glfw_motion(__attribute__((unused)) GLFWwindow *window, double xpos, double ypos) {
convert_to_pixels(window, &xpos, &ypos);
if (is_interactable) {
SDL_Event event;
event.type = SDL_MOUSEMOTION;
event.motion.x = xpos;
event.motion.y = ypos;
event.motion.xrel = !ignore_relative_motion ? (xpos - last_mouse_x) : 0;
event.motion.yrel = !ignore_relative_motion ? (ypos - last_mouse_y) : 0;
SDL_PushEvent(&event);
}
ignore_relative_motion = false;
last_mouse_x = xpos;
last_mouse_y = ypos;
}
// Create And Push SDL Mouse Click Event
static void click_event(int button, bool up) {
SDL_Event event;
event.type = up ? SDL_MOUSEBUTTONUP : SDL_MOUSEBUTTONDOWN;
event.button.x = last_mouse_x;
event.button.y = last_mouse_y;
event.button.state = up ? SDL_RELEASED : SDL_PRESSED;
event.button.button = button;
SDL_PushEvent(&event);
}
// Pass Mouse Click To SDL
static void glfw_click_raw(const int button, const int action) {
const bool up = action == GLFW_RELEASE;
const int sdl_button = button == GLFW_MOUSE_BUTTON_RIGHT ? SDL_BUTTON_RIGHT : (button == GLFW_MOUSE_BUTTON_LEFT ? SDL_BUTTON_LEFT : SDL_BUTTON_MIDDLE);
click_event(sdl_button, up);
}
static void glfw_click(__attribute__((unused)) GLFWwindow *window, const int button, const int action, __attribute__((unused)) int mods) {
if (is_interactable) {
glfw_click_raw(button, action);
}
}
// Pass Mouse Scroll To SDL
static void glfw_scroll(__attribute__((unused)) GLFWwindow *window, __attribute__((unused)) double xoffset, double yoffset) {
if (is_interactable && yoffset != 0) {
const int sdl_button = yoffset > 0 ? SDL_BUTTON_WHEELUP : SDL_BUTTON_WHEELDOWN;
click_event(sdl_button, false);
click_event(sdl_button, true);
}
}
// Enable/Disable Raw Mouse Motion
void media_set_raw_mouse_motion_enabled(const int enabled) {
raw_mouse_motion_enabled = enabled;
if (glfw_window) {
glfwSetInputMode(glfw_window, GLFW_RAW_MOUSE_MOTION, GLFW_FALSE);
}
if (!raw_mouse_motion_enabled) {
WARN("Raw mouse motion has been DISABLED, this IS NOT recommended, and should only ever be used on systems that don't support or have broken raw mouse motion.");
}
}
// Disable V-Sync
static int disable_vsync = 0;
static bool disable_vsync = false;
void media_disable_vsync() {
disable_vsync = 1;
disable_vsync = true;
if (glfw_window) {
glfwSwapInterval(0);
}
@ -321,7 +33,7 @@ void media_force_egl() {
// Init Media Layer
#define GL_VERSION 0x1f02
typedef const char *(*glGetString_t)(unsigned int name);
void SDL_WM_SetCaption(const char *title, __attribute__((unused)) const char *icon) {
void media_SDL_WM_SetCaption(const char *title, __attribute__((unused)) const char *icon) {
// Disable In Headless Mode
if (reborn_is_headless()) {
return;
@ -356,11 +68,7 @@ void SDL_WM_SetCaption(const char *title, __attribute__((unused)) const char *ic
}
// Event Handlers
glfwSetKeyCallback(glfw_window, glfw_key);
glfwSetCharCallback(glfw_window, glfw_char);
glfwSetCursorPosCallback(glfw_window, glfw_motion);
glfwSetMouseButtonCallback(glfw_window, glfw_click);
glfwSetScrollCallback(glfw_window, glfw_scroll);
_media_register_event_listeners();
// Make Window Context Current
glfwMakeContextCurrent(glfw_window);
@ -373,7 +81,7 @@ void SDL_WM_SetCaption(const char *title, __attribute__((unused)) const char *ic
_media_audio_init();
// Update State
update_cursor();
_media_update_cursor();
if (disable_vsync) {
media_disable_vsync();
}
@ -382,58 +90,6 @@ void SDL_WM_SetCaption(const char *title, __attribute__((unused)) const char *ic
atexit(media_cleanup);
}
void media_swap_buffers() {
if (glfw_window) {
glfwSwapBuffers(glfw_window);
}
}
// Track Fullscreen
static bool is_fullscreen = false;
// Old Size And Position To Use When Exiting Fullscreen
static int old_width = -1;
static int old_height = -1;
static int old_x = -1;
static int old_y = -1;
// Toggle Fullscreen
void media_toggle_fullscreen() {
if (glfw_window) {
if (is_fullscreen) {
glfwSetWindowMonitor(glfw_window, nullptr, old_x, old_y, old_width, old_height, GLFW_DONT_CARE);
old_width = -1;
old_height = -1;
old_x = -1;
old_y = -1;
} else {
glfwGetWindowSize(glfw_window, &old_width, &old_height);
glfwGetWindowPos(glfw_window, &old_x, &old_y);
GLFWmonitor *monitor = glfwGetPrimaryMonitor();
const GLFWvidmode *mode = glfwGetVideoMode(monitor);
glfwSetWindowMonitor(glfw_window, monitor, 0, 0, mode->width, mode->height, GLFW_DONT_CARE);
}
is_fullscreen = !is_fullscreen;
}
}
// Intercept SDL Events
void _media_handle_SDL_PollEvent() {
if (glfw_window) {
// Process GLFW Events
glfwPollEvents();
// Close Window
if (glfwWindowShouldClose(glfw_window)) {
SDL_Event event;
event.type = SDL_QUIT;
SDL_PushEvent(&event);
glfwSetWindowShouldClose(glfw_window, GLFW_FALSE);
}
}
}
// Cleanup Media Layer
void media_cleanup() {
if (glfw_window) {
@ -450,113 +106,4 @@ void media_cleanup() {
// Mark As Stopped
glfw_window = nullptr;
}
}
// Update GLFW Cursor State (Client Only)
static void update_cursor() {
if (glfw_window) {
// Get New State
const bool new_cursor_visible = is_interactable ? cursor_visible : true;
const bool new_cursor_grabbed = is_interactable ? cursor_grabbed : false;
// Store Old Mode
const int old_mode = glfwGetInputMode(glfw_window, GLFW_CURSOR);
// Handle Cursor Visibility
int new_mode;
if (!new_cursor_visible) {
if (new_cursor_grabbed) {
new_mode = GLFW_CURSOR_DISABLED;
} else {
new_mode = GLFW_CURSOR_HIDDEN;
}
} else {
new_mode = GLFW_CURSOR_NORMAL;
}
if (new_mode != old_mode) {
// Ignore Relative Cursor Motion When Locking
if (new_mode == GLFW_CURSOR_DISABLED && old_mode != GLFW_CURSOR_DISABLED) {
ignore_relative_motion = true;
}
// Set New Mode
glfwSetInputMode(glfw_window, GLFW_CURSOR, new_mode);
// Handle Cursor Lock/Unlock
if ((new_mode == GLFW_CURSOR_DISABLED && old_mode != GLFW_CURSOR_DISABLED) || (new_mode != GLFW_CURSOR_DISABLED && old_mode == GLFW_CURSOR_DISABLED)) {
// Use Raw Mouse Motion
if (raw_mouse_motion_enabled) {
glfwSetInputMode(glfw_window, GLFW_RAW_MOUSE_MOTION, new_mode == GLFW_CURSOR_DISABLED ? GLFW_TRUE : GLFW_FALSE);
}
// Request Focus
if (!glfwGetWindowAttrib(glfw_window, GLFW_FOCUSED)) {
glfwRequestWindowAttention(glfw_window);
}
}
// Reset Mouse Position When Unlocking
if (new_mode != GLFW_CURSOR_DISABLED && old_mode == GLFW_CURSOR_DISABLED) {
double cursor_x;
double cursor_y;
glfwGetCursorPos(glfw_window, &cursor_x, &cursor_y);
glfw_motion(glfw_window, cursor_x, cursor_y);
}
}
}
}
// Fix SDL Cursor Visibility/Grabbing
SDL_GrabMode SDL_WM_GrabInput(const SDL_GrabMode mode) {
if (mode == SDL_GRAB_QUERY) {
// Query
return cursor_grabbed ? SDL_GRAB_ON : SDL_GRAB_OFF;
} else if (mode == SDL_GRAB_ON) {
// Store State
cursor_grabbed = true;
} else if (mode == SDL_GRAB_OFF) {
// Store State
cursor_grabbed = false;
}
// Update Cursor GLFW State (Client Only)
update_cursor();
// Return
return mode;
}
// Stub SDL Cursor Visibility
int SDL_ShowCursor(const int toggle) {
if (toggle == SDL_QUERY) {
// Query
return cursor_visible ? SDL_ENABLE : SDL_DISABLE;
} else if (toggle == SDL_ENABLE) {
// Store State
cursor_visible = true;
} else if (toggle == SDL_DISABLE) {
// Store State
cursor_visible = false;
}
// Update Cursor GLFW State (Client Only)
update_cursor();
// Return
return toggle;
}
// Get Framebuffer Size
void media_get_framebuffer_size(int *width, int *height) {
if (glfw_window) {
glfwGetFramebufferSize(glfw_window, width, height);
} else {
*width = DEFAULT_WIDTH;
*height = DEFAULT_HEIGHT;
}
}
// Check OpenGL Extension
int media_has_extension(const char *name) {
if (glfw_window) {
return glfwExtensionSupported(name);
} else {
return 0;
}
}

View File

@ -0,0 +1,25 @@
#pragma once
#include <SDL/SDL.h>
#include <libreborn/libreborn.h>
#define GLFW_INCLUDE_NONE
#include <GLFW/glfw3.h>
#include <media-layer/core.h>
// Interactivity
__attribute__((visibility("internal"))) extern bool is_interactable;
// Window
__attribute__((visibility("internal"))) extern GLFWwindow *glfw_window;
// Cursor
__attribute__((visibility("internal"))) void _media_update_cursor();
__attribute__((visibility("internal"))) extern bool ignore_relative_motion;
__attribute__((visibility("internal"))) extern bool raw_mouse_motion_enabled;
// Events
__attribute__((visibility("internal"))) void _media_register_event_listeners();
__attribute__((visibility("internal"))) void _media_handle_media_SDL_PollEvent();
__attribute__((visibility("internal"))) void _media_glfw_motion(GLFWwindow *window, double xpos, double ypos);

View File

@ -0,0 +1,67 @@
#include "media.h"
// Allow Disabling Interaction
bool is_interactable = true;
void media_set_interactable(const int toggle) {
if (bool(toggle) != is_interactable) {
is_interactable = toggle;
_media_update_cursor();
}
}
// Get Framebuffer Size
void media_get_framebuffer_size(int *width, int *height) {
if (glfw_window) {
glfwGetFramebufferSize(glfw_window, width, height);
} else {