This commit is contained in:
TheBrokenRail 2021-11-13 23:29:48 -05:00
parent a79a601c59
commit 472f5d67a5
28 changed files with 617 additions and 240 deletions

View File

@ -1 +1 @@
2.2.7
2.2.8

View File

@ -1,5 +1,15 @@
# Changelog
**2.2.8**
* Add "Hide Chat Messages" Optional Feature Flag
* Add "Remove Creative Restrictions" Optional Feature Flag
* Improve GLFW->SDL Mouse Motion Event Conversion
* Performance Optimizations
* Make Majority Of Server-Specific Logging Code Also Apply To The Client
* Simple Benchmark Mode
* Fix Typo When Audio Source File Doesn't Exist
* Improve Build System
**2.2.7**
* Fix Crash When OpenAL Is Unavailable
* Fix Command Input In Server

View File

@ -9,3 +9,8 @@ FALSE This Flag Is Off By Default
## ``--only-generate`` (Server Mode Only)
If you run MCPI-Reborn with ``--only-generate``, it will immediately exit once world generation has completed. This is mainly used for automatically testing MCPI-Reborn.
## ``--benchmark`` (Client Mode Only)
If you run MCPI-Reborn with ``--benchmark``, it will enter a simple benchmark mode. This means automatically loading a newly generated world, then rotating the camera for a period of time. When it has finished, it will then exit and print the average FPS while the world was loaded. In this mode, all user input is blocked. However you can still modify rendering settings by changing feature flags.
The world used will always be re-created on start and uses a hard-coded seed.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 31 KiB

View File

@ -8,6 +8,7 @@ TRUE Display Nametags By Default
TRUE Fix Sign Placement
TRUE Show Block Outlines
FALSE Expand Creative Inventory
FALSE Remove Creative Restrictions
FALSE Peaceful Mode
TRUE Animated Water
TRUE Remove Invalid Item Background
@ -16,6 +17,7 @@ TRUE Smooth Lighting
FALSE 3D Anaglyph
TRUE Fix Camera Rendering
TRUE Implement Chat
FALSE Hide Chat Messages
TRUE Implement Death Messages
TRUE Implement Game-Mode Switching
TRUE Allow Joining Survival Servers

View File

@ -5,6 +5,7 @@
#include <SDL/SDL.h>
#include <media-layer/internal.h>
#include <media-layer/core.h>
// SDL Is Replaced With GLFW
@ -41,3 +42,7 @@ int SDL_PushEvent(SDL_Event *event) {
pthread_mutex_unlock(&queue_mutex);
return 1;
}
void media_ensure_loaded() {
// NOP
}

View File

@ -16,6 +16,14 @@
#include "audio/engine.h"
#endif // #ifndef MCPI_HEADLESS_MODE
// Allow Disabling Interaction
static void update_cursor();
static int is_interactable = 1;
void media_set_interactable(int toggle) {
is_interactable = toggle;
update_cursor();
}
// GLFW Code Not Needed In Headless Mode
#ifndef MCPI_HEADLESS_MODE
@ -139,63 +147,75 @@ static SDLMod glfw_modifier_to_sdl_modifier(int mods) {
// Pass Key Presses To SDL
static void glfw_key(__attribute__((unused)) GLFWwindow *window, int key, int scancode, int action, __attribute__((unused)) int mods) {
SDL_Event event;
int up = action == GLFW_RELEASE;
event.type = up ? SDL_KEYUP : SDL_KEYDOWN;
event.key.state = up ? SDL_RELEASED : SDL_PRESSED;
event.key.keysym.scancode = scancode;
event.key.keysym.mod = glfw_modifier_to_sdl_modifier(mods);
event.key.keysym.sym = glfw_key_to_sdl_key(key);
SDL_PushEvent(&event);
if (key == GLFW_KEY_BACKSPACE && !up) {
character_event((char) '\b');
if (is_interactable) {
SDL_Event event;
int up = action == GLFW_RELEASE;
event.type = up ? SDL_KEYUP : SDL_KEYDOWN;
event.key.state = up ? SDL_RELEASED : SDL_PRESSED;
event.key.keysym.scancode = scancode;
event.key.keysym.mod = glfw_modifier_to_sdl_modifier(mods);
event.key.keysym.sym = glfw_key_to_sdl_key(key);
SDL_PushEvent(&event);
if (key == GLFW_KEY_BACKSPACE && !up) {
character_event((char) '\b');
}
}
}
// Pass Text To Minecraft
static void glfw_char(__attribute__((unused)) GLFWwindow *window, unsigned int codepoint) {
character_event((char) codepoint);
if (is_interactable) {
character_event((char) codepoint);
}
}
// Last Mouse Location
static double last_mouse_x = 0;
static double last_mouse_y = 0;
static int ignore_relative_mouse = 1;
// Ignore Relative Cursor Motion
static int ignore_relative_motion = 0;
// Pass Mouse Movement To SDL
static void glfw_motion(__attribute__((unused)) GLFWwindow *window, double xpos, double ypos) {
SDL_Event event;
event.type = SDL_MOUSEMOTION;
event.motion.x = xpos;
event.motion.y = ypos;
event.motion.xrel = !ignore_relative_mouse ? (xpos - last_mouse_x) : 0;
event.motion.yrel = !ignore_relative_mouse ? (ypos - last_mouse_y) : 0;
ignore_relative_mouse = 0;
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 = 0;
last_mouse_x = xpos;
last_mouse_y = ypos;
SDL_PushEvent(&event);
}
// Create And Push SDL Mouse Click Event
static void click_event(int button, int 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);
if (is_interactable) {
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(__attribute__((unused)) GLFWwindow *window, int button, int action, __attribute__((unused)) int mods) {
int up = action == GLFW_RELEASE;
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);
if (is_interactable) {
int up = action == GLFW_RELEASE;
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);
}
}
// Pass Mouse Scroll To SDL
static void glfw_scroll(__attribute__((unused)) GLFWwindow *window, __attribute__((unused)) double xoffset, double yoffset) {
if (yoffset != 0) {
if (is_interactable && yoffset != 0) {
int sdl_button = yoffset > 0 ? SDL_BUTTON_WHEELUP : SDL_BUTTON_WHEELDOWN;
click_event(sdl_button, 0);
click_event(sdl_button, 1);
@ -207,6 +227,17 @@ static void glfw_scroll(__attribute__((unused)) GLFWwindow *window, __attribute_
// Track Media Layer State
static int is_running = 0;
// Disable V-Sync
static int disable_vsync = 0;
void media_disable_vsync() {
disable_vsync = 1;
#ifndef MCPI_HEADLESS_MODE
if (is_running) {
glfwSwapInterval(0);
}
#endif // #ifndef MCPI_HEADLESS_MODE
}
// Init Media Layer
void SDL_WM_SetCaption(const char *title, __attribute__((unused)) const char *icon) {
// Don't Enable GLFW In Headless Mode
@ -250,6 +281,12 @@ void SDL_WM_SetCaption(const char *title, __attribute__((unused)) const char *ic
// Set State
is_running = 1;
// Update State
update_cursor();
if (disable_vsync) {
media_disable_vsync();
}
}
void media_swap_buffers() {
@ -341,37 +378,53 @@ static int cursor_grabbed = 0;
static int cursor_visible = 1;
// Update GLFW Cursor State (Client Only)
static void update_cursor() {
#ifndef MCPI_HEADLESS_MODE
static void update_glfw_cursor() {
// Store Old Mode
int old_mode = glfwGetInputMode(glfw_window, GLFW_CURSOR);
if (is_running) {
// Get New State
int new_cursor_visible = is_interactable ? cursor_visible : 1;
int new_cursor_grabbed = is_interactable ? cursor_grabbed : 0;
// Handle Cursor Visibility
int new_mode;
if (!cursor_visible) {
if (cursor_grabbed) {
new_mode = GLFW_CURSOR_DISABLED;
// Store Old Mode
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_HIDDEN;
new_mode = GLFW_CURSOR_NORMAL;
}
} else {
new_mode = GLFW_CURSOR_NORMAL;
}
if (new_mode != old_mode) {
// Set New Mode
glfwSetInputMode(glfw_window, GLFW_CURSOR, new_mode);
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 = 1;
}
// 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
glfwSetInputMode(glfw_window, GLFW_RAW_MOUSE_MOTION, new_mode == GLFW_CURSOR_DISABLED ? GLFW_TRUE : GLFW_FALSE);
// Set New Mode
glfwSetInputMode(glfw_window, GLFW_CURSOR, new_mode);
// Reset Last Mouse Position
ignore_relative_mouse = 1;
// 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
glfwSetInputMode(glfw_window, GLFW_RAW_MOUSE_MOTION, new_mode == GLFW_CURSOR_DISABLED ? GLFW_TRUE : GLFW_FALSE);
}
// 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);
}
}
}
#endif // #ifndef MCPI_HEADLESS_MODE
}
#endif
// Fix SDL Cursor Visibility/Grabbing
SDL_GrabMode SDL_WM_GrabInput(SDL_GrabMode mode) {
@ -386,9 +439,7 @@ SDL_GrabMode SDL_WM_GrabInput(SDL_GrabMode mode) {
cursor_grabbed = 0;
}
// Update Cursor GLFW State (Client Only)
#ifndef MCPI_HEADLESS_MODE
update_glfw_cursor();
#endif
update_cursor();
// Return
return mode;
}
@ -406,9 +457,7 @@ int SDL_ShowCursor(int toggle) {
cursor_visible = 0;
}
// Update Cursor GLFW State (Client Only)
#ifndef MCPI_HEADLESS_MODE
update_glfw_cursor();
#endif
update_cursor();
// Return
return toggle;
}

View File

@ -17,6 +17,8 @@ extern "C" {
#define GL_DEPTH_TEST 0xb71
#define GL_PACK_ALIGNMENT 0xd05
#define GL_UNPACK_ALIGNMENT 0xcf5
#define GL_SRC_ALPHA 0x302
#define GL_ONE_MINUS_SRC_ALPHA 0x303
#include <stdio.h>
#include <stdint.h>

View File

@ -8,11 +8,15 @@ extern "C" {
#define DEFAULT_WIDTH 840
#define DEFAULT_HEIGHT 480
void media_ensure_loaded();
void media_take_screenshot(char *home);
void media_toggle_fullscreen();
void media_swap_buffers();
void media_cleanup();
void media_get_framebuffer_size(int *width, int *height);
void media_set_interactable(int is_interactable);
void media_disable_vsync();
#ifdef __cplusplus
}

View File

@ -307,3 +307,32 @@ CALL(60, media_audio_play, void, (const char *source, const char *name, float x,
free(name);
#endif
}
CALL(62, media_set_interactable, void, (int is_interactable)) {
#if defined(MEDIA_LAYER_PROXY_SERVER)
// Lock Proxy
start_proxy_call();
// Arguments
write_int(is_interactable);
// Release Proxy
end_proxy_call();
#else
int is_interactable = read_int();
// Run
media_set_interactable(is_interactable);
#endif
}
CALL(63, media_disable_vsync, void, ()) {
#if defined(MEDIA_LAYER_PROXY_SERVER)
// Lock Proxy
start_proxy_call();
// Release Proxy
end_proxy_call();
#else
// Run
media_disable_vsync();
#endif
}

View File

@ -10,6 +10,8 @@
#include <unordered_map>
#include <fstream>
#include <media-layer/core.h>
#include "../common/common.h"
// Track Client State
@ -85,58 +87,36 @@ static void start_media_layer_proxy_client(int read, int write) {
update_client_state(1, 0);
}
// Maximize Pipe Buffer Size
static void maximize_pipe_fd_size(int fd) {
// Read Maximum Pipe Size
std::ifstream max_size_file("/proc/sys/fs/pipe-max-size");
if (!max_size_file.good()) {
PROXY_ERR("%s", "Unable To Open Maximum Pipe Size File");
}
// Read One Line
int max_size;
std::string line;
if (std::getline(max_size_file, line) && line.size() > 0) {
max_size = std::stoi(line);
} else {
PROXY_ERR("%s", "Unable To Read Maximum Pipe Size File");
}
// Set Maximum Pipe Size
errno = 0;
if (fcntl(fd, F_SETPIPE_SZ, max_size) < max_size) {
PROXY_ERR("Unable To Set Maximum Pipe Size: %s", errno != 0 ? strerror(errno) : "Unknown Error");
}
}
static void maximize_pipe_size(int pipe[2]) {
maximize_pipe_fd_size(pipe[0]);
maximize_pipe_fd_size(pipe[1]);
}
// Start Server
__attribute__((constructor)) static void init_media_layer_proxy_server() {
PROXY_INFO("%s", "Starting...");
static int loaded = 0;
__attribute__((constructor)) void media_ensure_loaded() {
if (!loaded) {
loaded = 1;
// Create Connection
int server_to_client_pipe[2];
safe_pipe2(server_to_client_pipe, 0);
maximize_pipe_size(server_to_client_pipe);
int client_to_server_pipe[2];
safe_pipe2(client_to_server_pipe, 0);
maximize_pipe_size(client_to_server_pipe);
// Set Connection
set_connection(client_to_server_pipe[0], server_to_client_pipe[1]);
// Log
PROXY_INFO("%s", "Starting...");
// Start Client
start_media_layer_proxy_client(server_to_client_pipe[0], client_to_server_pipe[1]);
// Create Connection
int server_to_client_pipe[2];
safe_pipe2(server_to_client_pipe, 0);
int client_to_server_pipe[2];
safe_pipe2(client_to_server_pipe, 0);
// Set Connection
set_connection(client_to_server_pipe[0], server_to_client_pipe[1]);
// Wait For Connection Message
char *str = read_string();
if (strcmp(str, CONNECTED_MSG) == 0) {
PROXY_INFO("%s", "Connected");
} else {
PROXY_ERR("%s", "Unable To Connect");
// Start Client
start_media_layer_proxy_client(server_to_client_pipe[0], client_to_server_pipe[1]);
// Wait For Connection Message
char *str = read_string();
if (strcmp(str, CONNECTED_MSG) == 0) {
PROXY_INFO("%s", "Connected");
} else {
PROXY_ERR("%s", "Unable To Connect");
}
// Free
free(str);
}
// Free
free(str);
}
// Assign Unique ID To Function

View File

@ -23,7 +23,7 @@ target_link_libraries(chat reborn symbols feature pthread)
if(MCPI_SERVER_MODE)
add_library(server SHARED src/server/server.cpp src/server/server_properties.cpp)
target_link_libraries(server reborn symbols feature home compat version dl media-layer-core pthread)
target_link_libraries(server reborn symbols feature home misc compat dl media-layer-core pthread)
else()
target_link_libraries(compat input sign chat home dl)
@ -39,7 +39,7 @@ else()
target_link_libraries(camera reborn symbols media-layer-core feature home)
add_library(input SHARED src/input/input.cpp src/input/bow.c src/input/attack.c src/input/toggle.c src/input/misc.c src/input/drop.cpp)
target_link_libraries(input reborn symbols feature media-layer-core)
target_link_libraries(input reborn symbols creative feature media-layer-core)
add_library(sign SHARED src/sign/sign.cpp)
target_link_libraries(sign reborn symbols feature input)
@ -58,6 +58,11 @@ else()
add_library(atlas SHARED src/atlas/atlas.cpp)
target_link_libraries(atlas reborn symbols feature GLESv1_CM)
if(NOT MCPI_HEADLESS_MODE)
add_library(benchmark SHARED src/benchmark/benchmark.cpp)
target_link_libraries(benchmark reborn symbols compat misc media-layer-core)
endif()
endif()
add_library(game-mode SHARED src/game-mode/game-mode.c src/game-mode/game-mode.cpp)
@ -66,8 +71,8 @@ target_link_libraries(game-mode reborn symbols feature)
add_library(death SHARED src/death/death.cpp)
target_link_libraries(death reborn symbols feature)
add_library(misc SHARED src/misc/misc.c src/misc/misc.cpp)
target_link_libraries(misc reborn symbols feature)
add_library(misc SHARED src/misc/misc.c src/misc/misc.cpp src/misc/logging.cpp)
target_link_libraries(misc reborn symbols feature GLESv1_CM)
add_library(options SHARED src/options/options.c)
target_link_libraries(options reborn symbols feature)
@ -79,11 +84,14 @@ add_library(test SHARED src/test/test.c)
target_link_libraries(test reborn home)
add_library(init SHARED src/init/init.c)
target_link_libraries(init compat game-mode misc death options chat home version test)
target_link_libraries(init compat game-mode misc death options chat home version test media-layer-core)
if(MCPI_SERVER_MODE)
target_link_libraries(init server)
else()
target_link_libraries(init multiplayer sound camera input sign creative touch textures atlas)
if(NOT MCPI_HEADLESS_MODE)
target_link_libraries(init benchmark)
endif()
endif()
## Install Mods
@ -92,4 +100,7 @@ if(MCPI_SERVER_MODE)
install(TARGETS server DESTINATION "${MCPI_INSTALL_DIR}/mods")
else()
install(TARGETS multiplayer sound override camera input sign creative touch textures atlas DESTINATION "${MCPI_INSTALL_DIR}/mods")
if(NOT MCPI_HEADLESS_MODE)
install(TARGETS benchmark DESTINATION "${MCPI_INSTALL_DIR}/mods")
endif()
endif()

View File

@ -0,0 +1,2 @@
# ``benchmark`` Mod
This mod contain a simple game benchmark.

View File

@ -0,0 +1,147 @@
#include <ctime>
#include <libreborn/libreborn.h>
#include <symbols/minecraft.h>
#include <media-layer/core.h>
#include <SDL/SDL.h>
#include "../init/init.h"
#include "../compat/compat.h"
#include "../misc/misc.h"
// --benchmark: Activate Benchmark
static bool active = false;
__attribute__((constructor)) static void _init_active(int argc, char *argv[]) {
// Iterate Arguments
for (int i = 1; i < argc; i++) {
// Check Argument
if (strcmp(argv[i], "--benchmark") == 0) {
// Enabled
active = true;
break;
}
}
}
// Constants
#define NANOSECONDS_IN_SECOND 1000000000ll
// Config
#define BENCHMARK_GAME_MODE 1 // Creative Mode
#define BENCHMARK_SEED 2048 // Random Number
#define BENCHMARK_WORLD_NAME "_Benchmark" // Random Number
#define BENCHMARK_LENGTH (180ll * NANOSECONDS_IN_SECOND) // 3 Minutes
#define BENCHMARK_ROTATION_INTERVAL ((long long int) (0.02f * NANOSECONDS_IN_SECOND))
#define BENCHMARK_ROTATION_AMOUNT 10
// Create/Start World
static void start_world(unsigned char *minecraft) {
// Log
INFO("%s", "Loading Benchmark");
// Specify Level Settings
LevelSettings settings;
settings.game_type = BENCHMARK_GAME_MODE;
settings.seed = BENCHMARK_SEED;
// Delete World If It Already Exists
unsigned char *level_source = (*Minecraft_getLevelSource)(minecraft);
unsigned char *level_source_vtable = *(unsigned char **) level_source;
ExternalFileLevelStorageSource_deleteLevel_t ExternalFileLevelStorageSource_deleteLevel = *(ExternalFileLevelStorageSource_deleteLevel_t *) (level_source_vtable + ExternalFileLevelStorageSource_deleteLevel_vtable_offset);
(*ExternalFileLevelStorageSource_deleteLevel)(level_source, BENCHMARK_WORLD_NAME);
// Select Level
(*Minecraft_selectLevel)(minecraft, BENCHMARK_WORLD_NAME, BENCHMARK_WORLD_NAME, settings);
// Open ProgressScreen
void *screen = ::operator new(PROGRESS_SCREEN_SIZE);
ALLOC_CHECK(screen);
screen = (*ProgressScreen)((unsigned char *) screen);
(*Minecraft_setScreen)(minecraft, (unsigned char *) screen);
}
// Track Frames
static unsigned long long int frames = 0;
HOOK(media_swap_buffers, void, ()) {
ensure_media_swap_buffers();
(*real_media_swap_buffers)();
frames++;
}
// Get Time
static long long int get_time() {
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
long long int a = (long long int) ts.tv_nsec;
long long int b = ((long long int) ts.tv_sec) * NANOSECONDS_IN_SECOND;
return a + b;
}
// Store Time When World Loaded
static int world_loaded = 0;
static long long int world_loaded_time;
static unsigned long long int world_loaded_frames;
// Runs Every Tick
static bool loaded = false;
static bool exit_requested = false;
static void Minecraft_update_injection(unsigned char *minecraft) {
// Create/Start World
if (!loaded) {
start_world(minecraft);
loaded = true;
}
// Detect World Loaded
if (!world_loaded && (*Minecraft_isLevelGenerated)(minecraft)) {
world_loaded = 1;
world_loaded_time = get_time();
world_loaded_frames = frames;
}
// Run Benchmark
if (!exit_requested && world_loaded) {
// Get Time
long long int current_time = get_time() - world_loaded_time;
unsigned long long int current_frames = frames - world_loaded_frames;
// Rotate Player
static long long int rotate_point = BENCHMARK_ROTATION_INTERVAL;
if (current_time >= rotate_point) {
SDL_Event event;
event.type = SDL_MOUSEMOTION;
event.motion.x = 0;
event.motion.y = 0;
event.motion.xrel = BENCHMARK_ROTATION_AMOUNT;
event.motion.yrel = 0;
SDL_PushEvent(&event);
// Reset Rotation Timer
rotate_point += BENCHMARK_ROTATION_INTERVAL;
}
// Check If Benchmark Is Over
if (current_time >= BENCHMARK_LENGTH) {
// Request Exit
compat_request_exit();
// Disable Special Behavior After Requesting Exit
exit_requested = true;
// Calculate FPS
static double frames_per_nanosecond = ((double) current_frames) / ((double) current_time);
static double frames_per_second = frames_per_nanosecond * NANOSECONDS_IN_SECOND;
INFO("Benchmark Completed After %llu Frames In %lld Nanoseconds, Average FPS: %f", current_frames, current_time, frames_per_second);
}
}
}
// Init Benchmark
void init_benchmark() {
if (active) {
misc_run_on_update(Minecraft_update_injection);
// Disable Interaction
media_set_interactable(0);
// Disable V-Sync
media_disable_vsync();
}
}

View File

@ -3,6 +3,7 @@
#include "../init/init.h"
#include "../feature/feature.h"
#include "creative.h"
// Add Item To Inventory
static void inventory_add_item(unsigned char *inventory, unsigned char *item, bool is_tile) {
@ -51,10 +52,43 @@ static int32_t Inventory_setupDefault_FillingContainer_addItem_call_injection(un
return ret;
}
// Check Restriction Status
static int is_restricted = 1;
int creative_is_restricted() {
return is_restricted;
}
// Init
void init_creative() {
// Add Extra Items To Creative Inventory (Only Replace Specific Function Call)
if (feature_has("Expand Creative Inventory", 0)) {
overwrite_call((void *) 0x8e0fc, (void *) Inventory_setupDefault_FillingContainer_addItem_call_injection);
}
// Remove Creative Restrictions (Opening Chests, Crafting, Etc)
if (feature_has("Remove Creative Restrictions", 0)) {
unsigned char nop_patch[4] = {0x00, 0xf0, 0x20, 0xe3}; // "nop"
patch((void *) 0x1e3f4, nop_patch);
unsigned char slot_count_patch[4] = {0x18, 0x00, 0x00, 0xea}; // "b 0x27110"
patch((void *) 0x270a8, slot_count_patch);
patch((void *) 0x341c0, nop_patch);
patch((void *) 0x3adb4, nop_patch);
patch((void *) 0x3b374, nop_patch);
patch((void *) 0x43ee8, nop_patch);
patch((void *) 0x43f3c, nop_patch);
patch((void *) 0x43fd0, nop_patch);
patch((void *) 0x43fd8, nop_patch);
patch((void *) 0x8d080, nop_patch);
patch((void *) 0x8d090, nop_patch);
patch((void *) 0x91d48, nop_patch);
patch((void *) 0x92098, nop_patch);
unsigned char FillingContainer_removeResource_creative_check_patch[4] = {0x03, 0x00, 0x53, 0xe1}; // "cmp r3, r3"
patch((void *) 0x923c0, FillingContainer_removeResource_creative_check_patch);
patch((void *) 0x92828, nop_patch);
// Maximize Creative Inventory Stack Size
unsigned char maximize_stack_patch[4] = {0xff, 0xc0, 0xa0, 0xe3}; // "mov r12, 0xff"
patch((void *) 0x8e104, maximize_stack_patch);
// Disable Other Restrictions
is_restricted = 0;
}
}

View File

@ -0,0 +1,11 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
int creative_is_restricted();
#ifdef __cplusplus
}
#endif

View File

@ -1,7 +1,11 @@
#include "init.h"
#include <media-layer/core.h>
__attribute__((constructor)) static void init() {
media_ensure_loaded();
run_tests();
init_version();
init_compat();
#ifdef MCPI_SERVER_MODE
init_server();
@ -22,5 +26,7 @@ __attribute__((constructor)) static void init() {
init_options();
init_chat();
init_home();
init_version();
#if !defined(MCPI_SERVER_MODE) && !defined(MCPI_HEADLESS_MODE)
init_benchmark();
#endif
}

View File

@ -5,6 +5,7 @@ extern "C" {
#endif
void run_tests();
void init_version();
void init_compat();
#ifdef MCPI_SERVER_MODE
void init_server();
@ -25,7 +26,9 @@ void init_death();
void init_options();
void init_chat();
void init_home();
void init_version();
#if !defined(MCPI_SERVER_MODE) && !defined(MCPI_HEADLESS_MODE)
void init_benchmark();
#endif
#ifdef __cplusplus
}

View File

@ -3,6 +3,7 @@
#include "input.h"
#include "../feature/feature.h"
#include "../creative/creative.h"
// Enable Item Dropping
static int enable_drop = 0;
@ -22,7 +23,7 @@ void input_drop(int drop_slot) {
// Handle Drop Item Presses
static void _handle_drop(unsigned char *minecraft) {
if (!(*Minecraft_isCreativeMode)(minecraft) && (drop_item_presses > 0 || drop_slot_pressed)) {
if ((!creative_is_restricted() || !(*Minecraft_isCreativeMode)(minecraft)) && (drop_item_presses > 0 || drop_slot_pressed)) {
// Get Player
unsigned char *player = *(unsigned char **) (minecraft + Minecraft_player_property_offset);
if (player != NULL) {

View File

@ -3,6 +3,7 @@
#include "input.h"
#include "../feature/feature.h"
#include "../creative/creative.h"
// Enable Miscellaneous Input Fixes
static int enable_misc = 0;
@ -59,9 +60,9 @@ static void _handle_mouse_grab(unsigned char *minecraft) {
// Block UI Interaction When Mouse Is Locked
static bool Gui_tickItemDrop_Minecraft_isCreativeMode_call_injection(unsigned char *minecraft) {
if (SDL_WM_GrabInput(SDL_GRAB_QUERY) == SDL_GRAB_OFF) {
if (!enable_misc || SDL_WM_GrabInput(SDL_GRAB_QUERY) == SDL_GRAB_OFF) {
// Call Original Method
return (*Minecraft_isCreativeMode)(minecraft);
return creative_is_restricted() && (*Minecraft_isCreativeMode)(minecraft);
} else {
// Disable Item Drop Ticking
return 1;
@ -82,12 +83,12 @@ void _init_misc() {
if (enable_misc) {
// Fix OptionsScreen Ignoring The Back Button
patch_address(OptionsScreen_handleBackEvent_vtable_addr, (void *) OptionsScreen_handleBackEvent_injection);
// Disable Item Dropping Using The Cursor When Cursor Is Hidden
overwrite_call((void *) 0x27800, (void *) Gui_tickItemDrop_Minecraft_isCreativeMode_call_injection);
// Disable Opening Inventory Using The Cursor When Cursor Is Hidden
overwrite_calls((void *) Gui_handleClick, (void *) Gui_handleClick_injection);
}
// Disable Item Dropping Using The Cursor When Cursor Is Hidden
overwrite_call((void *) 0x27800, (void *) Gui_tickItemDrop_Minecraft_isCreativeMode_call_injection);
input_run_on_tick(_handle_back);
input_run_on_tick(_handle_mouse_grab);
}

108
mods/src/misc/logging.cpp Normal file
View File

@ -0,0 +1,108 @@
#include <string>
#include <libreborn/libreborn.h>
#include <symbols/minecraft.h>
#include "misc.h"
// Print Chat To Log
static bool Gui_addMessage_recursing = false;
static void Gui_addMessage_injection(unsigned char *gui, std::string const& text) {
// Sanitize Message
char *new_message = strdup(text.c_str());
ALLOC_CHECK(new_message);
sanitize_string(&new_message, -1, 1);
// Process Message
if (!Gui_addMessage_recursing) {
// Start Recursing
Gui_addMessage_recursing = true;
// Print Log Message
fprintf(stderr, "[CHAT]: %s\n", new_message);
// Call Original Method
(*Gui_addMessage)(gui, std::string(new_message));
// End Recursing
Gui_addMessage_recursing = false;
} else {
// Call Original Method
(*Gui_addMessage)(gui, std::string(new_message));
}
// Free
free(new_message);
}
// Check If Two Percentages Are Different Enough To Be Logged
#define SIGNIFICANT_PROGRESS 5
static bool is_progress_difference_significant(int32_t new_val, int32_t old_val) {
if (new_val != old_val) {
if (new_val == -1 || old_val == -1) {
return true;
} else if (new_val == 0 || new_val == 100) {
return true;
} else {
return new_val - old_val >= SIGNIFICANT_PROGRESS;
}
} else {
return false;
}
}
// Print Progress Reports
static int last_progress = -1;
static const char *last_message = NULL;
static void print_progress(unsigned char *minecraft) {
const char *message = (*Minecraft_getProgressMessage)(minecraft);
int32_t progress = *(int32_t *) (minecraft + Minecraft_progress_property_offset);
if ((*Minecraft_isLevelGenerated)(minecraft)) {
message = "Ready";
progress = -1;
}
if (message != NULL) {
bool message_different = message != last_message;
bool progress_significant = is_progress_difference_significant(progress, last_progress);
if (message_different || progress_significant) {
if (progress != -1) {
INFO("Status: %s: %i%%", message, progress);
} else {
INFO("Status: %s", message);
}
if (message_different) {
last_message = message;
}
if (progress_significant) {
last_progress = progress;
}
}
}
}
// Print Progress Reports Regularly
static void Minecraft_update_injection(unsigned char *minecraft) {
// Print Progress Reports
print_progress(minecraft);
}
// Log When Game Is Saved
void Level_saveLevelData_injection(unsigned char *level) {
// Print Log Message
INFO("%s", "Saving Game");
// Call Original Method
(*Level_saveLevelData)(level);
}
// Init
void _init_misc_logging() {
// Print Chat To Log
overwrite_calls((void *) Gui_addMessage, (void *) Gui_addMessage_injection);
// Print Progress Reports
misc_run_on_update(Minecraft_update_injection);
// Print Log On Game Save
overwrite_calls((void *) Level_saveLevelData, (void *) Level_saveLevelData_injection);
}

View File

@ -2,6 +2,8 @@
#include <string.h>
#include <unistd.h>
#include <GLES/gl.h>
#include <libreborn/libreborn.h>
#include <symbols/minecraft.h>
@ -12,36 +14,52 @@
// Maximum Username Length
#define MAX_USERNAME_LENGTH 16
// Render Selected Item Text
static void Gui_renderChatMessages_injection(unsigned char *gui, int32_t param_1, uint32_t param_2, uint32_t param_3, unsigned char *font) {
// Additional GUI Rendering
static int hide_chat_messages = 0;
static int render_selected_item_text = 0;
static void Gui_renderChatMessages_injection(unsigned char *gui, int32_t y_offset, uint32_t max_messages, bool disable_fading, unsigned char *font) {
// Call Original Method
(*Gui_renderChatMessages)(gui, param_1, param_2, param_3, font);
// Calculate Selected Item Text Scale
unsigned char *minecraft = *(unsigned char **) (gui + Gui_minecraft_property_offset);
int32_t screen_width = *(int32_t *) (minecraft + Minecraft_screen_width_property_offset);
float scale = ((float) screen_width) * *InvGuiScale;
if (!hide_chat_messages) {
(*Gui_renderChatMessages)(gui, y_offset, max_messages, disable_fading, font);
}
// Render Selected Item Text
(*Gui_renderOnSelectItemNameText)(gui, (int32_t) scale, font, param_1 - 0x13);
if (render_selected_item_text) {
// Fix GL Mode
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// Calculate Selected Item Text Scale
unsigned char *minecraft = *(unsigned char **) (gui + Gui_minecraft_property_offset);
int32_t screen_width = *(int32_t *) (minecraft + Minecraft_screen_width_property_offset);
float scale = ((float) screen_width) * *InvGuiScale;
// Render Selected Item Text
(*Gui_renderOnSelectItemNameText)(gui, (int32_t) scale, font, y_offset - 0x13);
}
}
// Reset Selected Item Text Timer On Slot Select
static uint32_t reset_selected_item_text_timer = 0;
static void Gui_tick_injection(unsigned char *gui) {
// Call Original Method
(*Gui_tick)(gui);
// Handle Reset
float *selected_item_text_timer = (float *) (gui + Gui_selected_item_text_timer_property_offset);
if (reset_selected_item_text_timer) {
// Reset
*selected_item_text_timer = 0;
reset_selected_item_text_timer = 0;
if (render_selected_item_text) {
float *selected_item_text_timer = (float *) (gui + Gui_selected_item_text_timer_property_offset);
if (reset_selected_item_text_timer) {
// Reset
*selected_item_text_timer = 0;
reset_selected_item_text_timer = 0;
}
}
}
// Trigger Reset Selected Item Text Timer On Slot Select
static void Inventory_selectSlot_injection(unsigned char *inventory, int32_t slot) {
// Call Original Method
(*Inventory_selectSlot)(inventory, slot);
// Trigger Reset Selected Item Text Timer
reset_selected_item_text_timer = 1;
if (render_selected_item_text) {
reset_selected_item_text_timer = 1;
}
}
// Sanitize Username
@ -125,12 +143,12 @@ void init_misc() {
patch((void *) 0x63c98, invalid_item_background_patch);
}
// Fix Selected Item Text
if (feature_has("Render Selected Item Text", 0)) {
overwrite_calls((void *) Gui_renderChatMessages, (void *) Gui_renderChatMessages_injection);
overwrite_calls((void *) Gui_tick, (void *) Gui_tick_injection);
overwrite_calls((void *) Inventory_selectSlot, (void *) Inventory_selectSlot_injection);
}
// Render Selected Item Text + Hide Chat Messages
hide_chat_messages = feature_has("Hide Chat Messages", 0);
render_selected_item_text = feature_has("Render Selected Item Text", 0);
overwrite_calls((void *) Gui_renderChatMessages, (void *) Gui_renderChatMessages_injection);
overwrite_calls((void *) Gui_tick, (void *) Gui_tick_injection);
overwrite_calls((void *) Inventory_selectSlot, (void *) Inventory_selectSlot_injection);
// Sanitize Username
patch_address(LoginPacket_read_vtable_addr, (void *) LoginPacket_read_injection);
@ -144,6 +162,7 @@ void init_misc() {
// Fix Bug Where RakNetInstance Starts Pinging Potential Servers Before The "Join Game" Screen Is Opened
overwrite_calls((void *) RakNetInstance, (void *) RakNetInstance_injection);
// Init C++
// Init C++ And Logging
_init_misc_cpp();
_init_misc_logging();
}

View File

@ -1,6 +1,7 @@
#include <string>
#include <fstream>
#include <streambuf>
#include <vector>
#include <cstring>
@ -24,34 +25,24 @@ static AppPlatform_readAssetFile_return_value AppPlatform_readAssetFile_injectio
return ret;
}
// Print Chat To Log
static bool Gui_addMessage_recursing = false;
static void Gui_addMessage_injection(unsigned char *gui, std::string const& text) {
// Sanitize Message
char *new_message = strdup(text.c_str());
ALLOC_CHECK(new_message);
sanitize_string(&new_message, -1, 1);
// Run Functions On Input Tick
static std::vector<misc_update_function_t> &get_misc_update_functions() {
static std::vector<misc_update_function_t> functions;
return functions;
}
void misc_run_on_update(misc_update_function_t function) {
get_misc_update_functions().push_back(function);
}
// Process Message
if (!Gui_addMessage_recursing) {
// Start Recursing
Gui_addMessage_recursing = true;
// Handle Custom Update Behavior
static void Minecraft_update_injection(unsigned char *minecraft) {
// Call Original Method
(*Minecraft_update)(minecraft);
// Print Log Message
fprintf(stderr, "[CHAT]: %s\n", new_message);
// Call Original Method
(*Gui_addMessage)(gui, std::string(new_message));
// End Recursing
Gui_addMessage_recursing = false;
} else {
// Call Original Method
(*Gui_addMessage)(gui, std::string(new_message));
// Run Input Tick Functions
for (misc_update_function_t function : get_misc_update_functions()) {
(*function)(minecraft);
}
// Free
free(new_message);
}
// Init
@ -61,6 +52,6 @@ void _init_misc_cpp() {
overwrite((void *) AppPlatform_readAssetFile, (void *) AppPlatform_readAssetFile_injection);
}
// Print Chat To Log
overwrite_calls((void *) Gui_addMessage, (void *) Gui_addMessage_injection);
// Handle Custom Update Behavior
overwrite_calls((void *) Minecraft_update, (void *) Minecraft_update_injection);
}

View File

@ -4,7 +4,13 @@
extern "C" {
#endif
typedef void (*misc_update_function_t)(unsigned char *minecraft);
void misc_run_on_update(misc_update_function_t function);
void Level_saveLevelData_injection(unsigned char *level);
__attribute__((visibility("internal"))) void _init_misc_cpp();
__attribute__((visibility("internal"))) void _init_misc_logging();
#ifdef __cplusplus
}

View File

@ -16,6 +16,7 @@
#include <SDL/SDL.h>
#include <libreborn/libreborn.h>
#include <symbols/minecraft.h>
#include "server_properties.h"
@ -23,9 +24,7 @@
#include "../init/init.h"
#include "../home/home.h"
#include "../compat/compat.h"
#include "../version/version.h"
#include <symbols/minecraft.h>
#include "../misc/misc.h"
// --only-generate: Ony Generate World And Then Exit
static bool only_generate = false;
@ -66,17 +65,20 @@ static std::string get_world_name() {
// Create/Start World
static void start_world(unsigned char *minecraft) {
INFO("Starting Minecraft: Pi Edition: Dedicated Server (%s)", version_get());
// Get World Name
std::string world_name = get_world_name();
// Log
INFO("Loading World: %s", world_name.c_str());
// Specify Level Settings
LevelSettings settings;
settings.game_type = get_server_properties().get_int("game-mode", DEFAULT_GAME_MODE);;
settings.game_type = get_server_properties().get_int("game-mode", DEFAULT_GAME_MODE);
std::string seed_str = get_server_properties().get_string("seed", DEFAULT_SEED);
int32_t seed = seed_str.length() > 0 ? std::stoi(seed_str) : time(NULL);
settings.seed = seed;
// Select Level
std::string world_name = get_world_name();
(*Minecraft_selectLevel)(minecraft, world_name, world_name, settings);
// Don't Open Port When Using --only-generate
@ -94,51 +96,6 @@ static void start_world(unsigned char *minecraft) {
(*Minecraft_setScreen)(minecraft, (unsigned char *) screen);
}
// Check If Two Percentages Are Different Enough To Be Logged
#define SIGNIFICANT_PROGRESS 5
static bool is_progress_difference_significant(int32_t new_val, int32_t old_val) {
if (new_val != old_val) {
if (new_val == -1 || old_val == -1) {
return true;
} else if (new_val == 0 || new_val == 100) {
return true;
} else {
return new_val - old_val >= SIGNIFICANT_PROGRESS;
}
} else {
return false;
}
}
// Print Progress Reports
static int last_progress = -1;
static const char *last_message = NULL;
static void print_progress(unsigned char *minecraft) {
const char *message = (*Minecraft_getProgressMessage)(minecraft);
int32_t progress = *(int32_t *) (minecraft + Minecraft_progress_property_offset);
if ((*Minecraft_isLevelGenerated)(minecraft)) {
message = "Ready";
progress = -1;
}
if (message != NULL) {
bool message_different = message != last_message;
bool progress_significant = is_progress_difference_significant(progress, last_progress);
if (message_different || progress_significant) {
if (progress != -1) {
INFO("Status: %s: %i%%", message, progress);
} else {
INFO("Status: %s", message);
}
if (message_different) {
last_message = message;
}
if (progress_significant) {
last_progress = progress;
}
}
}
}
// Check If Running In Whitelist Mode
static bool is_whitelist() {
return get_server_properties().get_bool("whitelist", DEFAULT_WHITELIST);
@ -244,15 +201,6 @@ static void list_callback(unsigned char *minecraft, std::string username, unsign
INFO(" - %s (%s)", username.c_str(), get_player_ip(minecraft, player));
}
// Log When Game Is Saved
static void Level_saveLevelData_injection(unsigned char *level) {
// Print Log Message
INFO("%s", "Saving Game");
// Call Original Method
(*Level_saveLevelData)(level);
}
// Handle Server Stop
static void handle_server_stop(unsigned char *minecraft) {
if (compat_check_exit_requested()) {
@ -384,12 +332,6 @@ static void Minecraft_update_injection(unsigned char *minecraft) {
only_generate = false;
}
// Print Progress Reports
print_progress(minecraft);
// Call Original Method
(*Minecraft_update)(minecraft);
// Handle Commands
handle_commands(minecraft);
@ -494,9 +436,9 @@ static void server_init() {
// Open Properties File
std::string file(home_get());
file.append("/server.properties");
std::ifstream properties_file(file);
// Check Properties File
if (!properties_file.good()) {
// Write Defaults
std::ofstream properties_file_output(file);
@ -551,9 +493,7 @@ static void server_init() {
unsigned char player_patch[4] = {0x00, 0x20, 0xa0, 0xe3}; // "mov r2, #0x0"
patch((void *) 0x1685c, player_patch);
// Start World On Launch
overwrite_calls((void *) Minecraft_update, (void *) Minecraft_update_injection);
// Print Log On Game Save
overwrite_calls((void *) Level_saveLevelData, (void *) Level_saveLevelData_injection);
misc_run_on_update(Minecraft_update_injection);
// Set Max Players
unsigned char max_players_patch[4] = {get_max_players(), 0x30, 0xa0, 0xe3}; // "mov r3, #MAX_PLAYERS"
patch((void *) 0x166d0, max_players_patch);

View File

@ -42,7 +42,7 @@ std::string _sound_get_source_file() {
// Check If Sound Exists
if (access(full_path, F_OK) == -1) {
// Fail
WARN("%s", "Audio Source File Doesn't Exists: " SOURCE_FILE_BASE);
WARN("%s", "Audio Source File Doesn't Exist: " SOURCE_FILE_BASE);
source.assign("");
} else {
// Set

View File

@ -31,4 +31,7 @@ void init_version() {
overwrite((void *) Common_getGameVersionString, (void *) Common_getGameVersionString_injection);
// Normal GUI
patch_address((void *) minecraft_pi_version, version_get());
// Log
INFO("Starting Minecraft: Pi Edition (%s)", version_get());
}

View File

@ -160,6 +160,9 @@ static uint32_t Minecraft_handleBack_vtable_offset = 0x34;
typedef unsigned char *(*Minecraft_getCreator_t)(unsigned char *minecraft);
static Minecraft_getCreator_t Minecraft_getCreator = (Minecraft_getCreator_t) 0x17538;
typedef unsigned char *(*Minecraft_getLevelSource_t)(unsigned char *minecraft);
static Minecraft_getLevelSource_t Minecraft_getLevelSource = (Minecraft_getLevelSource_t) 0x16e84;
static uint32_t Minecraft_screen_width_property_offset = 0x20; // int32_t
static uint32_t Minecraft_network_handler_property_offset = 0x174; // NetEventCallback *
static uint32_t Minecraft_rak_net_instance_property_offset = 0x170; // RakNetInstance *
@ -327,7 +330,7 @@ static Gui_handleClick_t Gui_handleClick = (Gui_handleClick_t) 0x2599c;
typedef void (*Gui_renderOnSelectItemNameText_t)(unsigned char *gui, int32_t param_1, unsigned char *font, int32_t param_2);
static Gui_renderOnSelectItemNameText_t Gui_renderOnSelectItemNameText = (Gui_renderOnSelectItemNameText_t) 0x26aec;
typedef void (*Gui_renderChatMessages_t)(unsigned char *gui, int32_t param_1, uint32_t param_2, bool param_3, unsigned char *font);
typedef void (*Gui_renderChatMessages_t)(unsigned char *gui, int32_t y_offset, uint32_t max_messages, bool disable_fading, unsigned char *font);
static Gui_renderChatMessages_t Gui_renderChatMessages = (Gui_renderChatMessages_t) 0x273d8;
static uint32_t Gui_minecraft_property_offset = 0x9f4; // Minecraft *
@ -631,6 +634,11 @@ static AppPlatform_readAssetFile_t AppPlatform_readAssetFile = (AppPlatform_read
typedef void (*Minecraft_selectLevel_t)(unsigned char *minecraft, std::string const& level_dir, std::string const& level_name, LevelSettings const& vsettings);
static Minecraft_selectLevel_t Minecraft_selectLevel = (Minecraft_selectLevel_t) 0x16f38;
// ExternalFileLevelStorageSource
typedef void (*ExternalFileLevelStorageSource_deleteLevel_t)(unsigned char *external_file_level_storage_source, std::string const& level_name);
static uint32_t ExternalFileLevelStorageSource_deleteLevel_vtable_offset = 0x20;
// CommandServer
typedef std::string (*CommandServer_parse_t)(unsigned char *command_server, struct ConnectedClient &client, std::string const& command);