diff --git a/README.md b/README.md index ee8fa18..21584f2 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ To use, install the ``minecraft-pi-server`` package and run ``minecraft-pi-serve This is also compatible with MCPE 0.6.1. ### Limitations -- Player inventories are not saved because of limitations with MCPE LAN worlds +- Player data is not saved because of limitations with MCPE LAN worlds - An easy workaround is to place your inventory in a chest before logging off - Survival mode servers are only compatible with ``minecraft-pi-docker`` clients diff --git a/mods/CMakeLists.txt b/mods/CMakeLists.txt index 2c04373..0782457 100644 --- a/mods/CMakeLists.txt +++ b/mods/CMakeLists.txt @@ -28,7 +28,7 @@ target_link_libraries(core dl) add_library(feature SHARED src/feature/feature.c) -add_library(server SHARED src/server/server.cpp src/server/server_properties.cpp src/server/playerdata.cpp) +add_library(server SHARED src/server/server.cpp src/server/server_properties.cpp) target_link_libraries(server core feature dl SDL pthread) add_library(screenshot SHARED src/screenshot/screenshot.c) diff --git a/mods/src/minecraft.h b/mods/src/minecraft.h index 15460c0..2989bec 100644 --- a/mods/src/minecraft.h +++ b/mods/src/minecraft.h @@ -35,9 +35,6 @@ static unsigned char **Tile_grass_carried = (unsigned char **) 0x181dd4; static float *InvGuiScale = (float *) 0x135d98; -typedef long int (*getRemainingFileSize_t)(FILE *file); -static getRemainingFileSize_t getRemainingFileSize = (getRemainingFileSize_t) 0xba520; - // Structures struct LevelSettings { @@ -48,23 +45,10 @@ struct LevelSettings { struct RakNet_RakNetGUID { unsigned char data[10]; }; - struct RakNet_SystemAddress { unsigned char data[20]; }; -struct RakNet_BitStream { - unsigned char data[273]; -}; - -struct RakDataOutput { - unsigned char data[8]; -}; - -struct RakDataInput { - unsigned char data[8]; -}; - // GameMode typedef void (*GameMode_releaseUsingItem_t)(unsigned char *game_mode, unsigned char *player); @@ -115,9 +99,6 @@ static void *MouseBuildInput_tickBuild_vtable_addr = (void *) 0x102564; typedef int (*Player_isUsingItem_t)(unsigned char *player); static Player_isUsingItem_t Player_isUsingItem = (Player_isUsingItem_t) 0x8f15c; -typedef void (*Player_setArmor_t)(unsigned char *player, int32_t slot, unsigned char *item); -static Player_setArmor_t Player_setArmor = (Player_setArmor_t) 0x8fde0; - // Player static void *LocalPlayer_openTextEdit_vtable_addr = (void *) 0x106460; @@ -201,22 +182,6 @@ static FillingContainer_addItem_t FillingContainer_addItem = (FillingContainer_a typedef struct RakNet_SystemAddress (*RakNet_RakPeer_GetSystemAddressFromGuid_t)(unsigned char *rak_peer, struct RakNet_RakNetGUID guid); -// RakNet::BitStream - -typedef unsigned char *(*RakNet_BitStream_constructor_t)(struct RakNet_BitStream *stream); -static RakNet_BitStream_constructor_t RakNet_BitStream_constructor = (RakNet_BitStream_constructor_t) 0xd3b84; - -typedef void (*RakNet_BitStream_destructor_t)(struct RakNet_BitStream *stream); -static RakNet_BitStream_destructor_t RakNet_BitStream_destructor = (RakNet_BitStream_destructor_t) 0xd3ce8; - -// RakDataOutput - -static unsigned char *RakDataOutput_vtable = (unsigned char *) 0x109628; - -// RakDataInput - -static unsigned char *RakDataInput_vtable = (unsigned char *) 0x1095c8; - // ServerSideNetworkHandler typedef void (*ServerSideNetworkHandler_onDisconnect_t)(unsigned char *server_side_network_handler, unsigned char *guid); @@ -226,44 +191,12 @@ static void *ServerSideNetworkHandler_onDisconnect_vtable_addr = (void *) 0x109b typedef unsigned char *(*ServerSideNetworkHandler_getPlayer_t)(unsigned char *server_side_network_handler, unsigned char *guid); static ServerSideNetworkHandler_getPlayer_t ServerSideNetworkHandler_getPlayer = (ServerSideNetworkHandler_getPlayer_t) 0x75464; -// CompoundTag - -typedef unsigned char *(*CompoundTag_t)(unsigned char *tag); -static CompoundTag_t CompoundTag = (CompoundTag_t) 0xb9920; - -// Tag - -typedef void (*Tag_writeNamedTag_t)(unsigned char *tag, struct RakDataOutput *output); -static Tag_writeNamedTag_t Tag_writeNamedTag = (Tag_writeNamedTag_t) 0x6850c; - -typedef void (*Tag_deleteChildren_t)(unsigned char *tag); -typedef void (*Tag_destructor_t)(unsigned char *tag); - // Entity -typedef void (*Entity_saveWithoutId_t)(unsigned char *entity, unsigned char *tag); - -typedef void (*Entity_load_t)(unsigned char *entity, unsigned char *tag); - -typedef void (*Entity_moveTo_t)(unsigned char *entity, float param_1, float param_2, float param_3, float param_4, float param_5); -static Entity_moveTo_t Entity_moveTo = (Entity_moveTo_t) 0x7a834; - typedef void (*Entity_die_t)(unsigned char *entity, unsigned char *cause); -// ServerPlayer - -static void *ServerPlayer_moveTo_vtable_addr = (void *) 0x109e54; - -// NbtIo - -typedef unsigned char *(*NbtIo_read_t)(struct RakDataInput *input); -static NbtIo_read_t NbtIo_read = (NbtIo_read_t) 0xb98cc; - // Inventory -typedef void (*Inventory_clearInventoryWithDefault_t)(unsigned char *inventory); -static Inventory_clearInventoryWithDefault_t Inventory_clearInventoryWithDefault = (Inventory_clearInventoryWithDefault_t) 0x8e7c8; - typedef void (*Inventory_selectSlot_t)(unsigned char *inventory, int32_t slot); static Inventory_selectSlot_t Inventory_selectSlot = (Inventory_selectSlot_t) 0x8d13c; @@ -333,11 +266,6 @@ static Textures_tick_t Textures_tick = (Textures_tick_t) 0x531c4; typedef bool (*RakNet_RakPeer_IsBanned_t)(unsigned char *rakpeer, const char *ip); static RakNet_RakPeer_IsBanned_t RakNet_RakPeer_IsBanned = (RakNet_RakPeer_IsBanned_t) 0xda3b4; -// RakNet::BitStream - -typedef struct RakNet_BitStream *(*RakNet_BitStream_constructor_with_data_t)(struct RakNet_BitStream *stream, unsigned char *data, uint32_t size, bool copyData); -static RakNet_BitStream_constructor_with_data_t RakNet_BitStream_constructor_with_data = (RakNet_BitStream_constructor_with_data_t) 0xd3c30; - // RakNet::SystemAddress typedef char *(*RakNet_SystemAddress_ToString_t)(struct RakNet_SystemAddress *system_address, bool print_delimiter, char delimiter); diff --git a/mods/src/server/playerdata.cpp b/mods/src/server/playerdata.cpp deleted file mode 100644 index b4150d5..0000000 --- a/mods/src/server/playerdata.cpp +++ /dev/null @@ -1,217 +0,0 @@ -#include - -#include "playerdata.h" -#include "server_internal.h" - -#include "../minecraft.h" - -// Execute Command Without LD_PRELOAD -static void exec_without_preload(std::string str) { - std::string preload = getenv("LD_PRELOAD"); - unsetenv("LD_PRELOAD"); - system(str.c_str()); - setenv("LD_PRELOAD", preload.c_str(), 1); -} - -// Get Player Data NBT Path -static std::string get_player_data_path(std::string username) { - std::string path = std::string(getenv("HOME")) + "/.minecraft/games/com.mojang/minecraftWorlds/" + server_internal_get_world_name() + "/playerdata"; - exec_without_preload("mkdir -p " + path); - return path + '/' + username + ".dat"; -} - -// Destruct Tag -static void destruct_tag(unsigned char *tag) { - unsigned char *tag_vtable = *(unsigned char **) tag; - Tag_deleteChildren_t Tag_deleteChildren = *(Tag_deleteChildren_t *) (tag_vtable + 0x8); - (*Tag_deleteChildren)(tag); - Tag_destructor_t Tag_destructor = *(Tag_destructor_t *) (tag_vtable + 0x4); - (*Tag_destructor)(tag); -} - -// Save Player Callback -static void save_player_callback(std::string username, unsigned char *player) { - std::string nbt_file = get_player_data_path(username); - - // Open File - FILE *file = fopen(nbt_file.c_str(), "wb"); - - // Write Storage Version - uint32_t storage_version = 3; - fwrite(&storage_version, 4, 1, file); - - // Create Tag - unsigned char *tag = (unsigned char *) ::operator new(0x38); - (*CompoundTag)(tag); - - // Allocate - RakNet_BitStream stream; - RakDataOutput output; - - // Set VTable - *(unsigned char **) &output.data[0] = RakDataOutput_vtable; - - // Construct BitStream - (*RakNet_BitStream_constructor)(&stream); - - // Set BitStream - *(RakNet_BitStream **) &output.data[4] = &stream; - - // Save NBT - unsigned char *player_vtable = *(unsigned char **) player; - Entity_saveWithoutId_t Entity_saveWithoutId = *(Entity_saveWithoutId_t *) (player_vtable + 0xcc); - (*Entity_saveWithoutId)(player, tag); - - // Write NBT - (*Tag_writeNamedTag)(tag, &output); - - // Destruct Tag - destruct_tag(tag); - - // Write To File - uint32_t numberOfBitsUsed = *(uint32_t *) &stream.data[0]; - uint32_t size = (numberOfBitsUsed + 7) >> 3; - fwrite(&size, 4, 1, file); - unsigned char *data = *(unsigned char **) &stream.data[12]; - fwrite(data, 1, size, file); - - // Destruct BitStream - (*RakNet_BitStream_destructor)(&stream); - - // Close File - fclose(file); -} - -// Clear Player Inventory And Armor -static void clear_inventory(unsigned char *player) { - unsigned char *inventory = *(unsigned char **) (player + 0xbe0); - (*Inventory_clearInventoryWithDefault)(inventory); - for (int i = 0; i < 4; i++) { - (*Player_setArmor)(player, i, NULL); - } -} - -// Load Player Callback -static void load_player_callback(std::string username, unsigned char *player) { - std::string nbt_file = get_player_data_path(username); - - // Open File - FILE *file = fopen(nbt_file.c_str(), "rb"); - - if (file) { - uint32_t storage_version; - size_t data_read = fread(&storage_version, 4, 1, file); - - // Check File - if (data_read == 1 && storage_version == 3) { - // Read Expected File Size - uint32_t expected_size; - data_read = fread(&expected_size, 4, 1, file); - - // Get Actual Size - long int remaining = (*getRemainingFileSize)(file); - - if (data_read == 1 && expected_size > 0 && ((uint32_t) remaining) == expected_size) { - // Read File - unsigned char *data = (unsigned char *) malloc(expected_size); - data_read = fread(data, 1, expected_size, file); - - if (data_read == expected_size) { - // Allocate - RakNet_BitStream stream; - RakDataInput input; - - // Set VTable - *(unsigned char **) &input.data[0] = RakDataInput_vtable; - - // Construct BitStream - (*RakNet_BitStream_constructor_with_data)(&stream, data, expected_size, false); - - // Set BitStream - *(RakNet_BitStream **) &input.data[4] = &stream; - - // Create Tag - unsigned char *tag = (*NbtIo_read)(&input); - - if (tag != NULL) { - // Load Data - unsigned char *player_vtable = *(unsigned char **) player; - Entity_load_t Entity_load = *(Entity_load_t *) (player_vtable + 0xd0); - (*Entity_load)(player, tag); - - // Clear Inventory Because The Client Will Also Have An Empty Inventory - clear_inventory(player); - - // Destruct tag - destruct_tag(tag); - } - - // Destruct BitStream - (*RakNet_BitStream_destructor)(&stream); - } - - // Free Data - free(data); - } - } - - // Close File - fclose(file); - } -} - -static uint32_t get_entity_id(unsigned char *entity) { - return *(uint32_t *) (entity + 0x1c); -} -static void ServerPlayer_moveTo_injection(unsigned char *player, float param_1, float param_2, float param_3, float param_4, float param_5) { - // Call Original Method - (*Entity_moveTo)(player, param_1, param_2, param_3, param_4, param_5); - - // Check If Player Is Spawned - unsigned char *minecraft = server_internal_get_minecraft(player); - unsigned char *level = server_internal_get_level(minecraft); - std::vector players = server_internal_get_players(level); - bool spawned = false; - uint32_t player_id = get_entity_id(player); - for (std::size_t i = 0; i < players.size(); i++) { - if (player_id == get_entity_id(players[i])) { - spawned = true; - break; - } - } - - // Load Data - if (!spawned) { - load_player_callback(server_internal_get_server_player_username(player), player); - } -} - -void playerdata_save(unsigned char *level) { - // Save Players - std::vector players = server_internal_get_players(level); - for (std::size_t i = 0; i < players.size(); i++) { - // Iterate Players - unsigned char *player = players[i]; - std::string username = server_internal_get_server_player_username(player); - save_player_callback(username, player); - } -} - -static void ServerSideNetworkHandler_onDisconnect_injection(unsigned char *server_side_network_handler, unsigned char *guid) { - // Save Player Data - unsigned char *player = (*ServerSideNetworkHandler_getPlayer)(server_side_network_handler, guid); - if (player != NULL) { - std::string username = server_internal_get_server_player_username(player); - save_player_callback(username, player); - } - - // Call Original Method - (*ServerSideNetworkHandler_onDisconnect)(server_side_network_handler, guid); -} - -void playerdata_init() { - // Load Player NBT - patch_address(ServerPlayer_moveTo_vtable_addr, (void *) ServerPlayer_moveTo_injection); - // Save On Logout - patch_address(ServerSideNetworkHandler_onDisconnect_vtable_addr, (void *) ServerSideNetworkHandler_onDisconnect_injection); -} \ No newline at end of file diff --git a/mods/src/server/playerdata.h b/mods/src/server/playerdata.h deleted file mode 100644 index 06e7a11..0000000 --- a/mods/src/server/playerdata.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef PLAYERDATA_H - -#define PLAYERDATA_H - -void playerdata_save(unsigned char *level); -void playerdata_init(); - -#endif \ No newline at end of file diff --git a/mods/src/server/server.cpp b/mods/src/server/server.cpp index 18a79f2..683417a 100644 --- a/mods/src/server/server.cpp +++ b/mods/src/server/server.cpp @@ -17,7 +17,6 @@ #include "server.h" #include "server_internal.h" #include "server_properties.h" -#include "playerdata.h" #include "../feature/feature.h" #include "../init/init.h" @@ -244,9 +243,6 @@ static void Level_saveLevelData_injection(unsigned char *level) { // Call Original Method (*Level_saveLevelData)(level); - - // Save Player Data - playerdata_save(level); } // Stop Server @@ -495,9 +491,6 @@ static void server_init() { // Custom Banned IP List overwrite((void *) RakNet_RakPeer_IsBanned, (void *) RakNet_RakPeer_IsBanned_injection); - // Load Player Data - playerdata_init(); - if (get_server_properties().get_bool("show-minecon-badge", DEFAULT_SHOW_MINECON_BADGE)) { // Show The MineCon Icon Next To MOTD In Server List unsigned char minecon_badge_patch[4] = {0x04, 0x1a, 0x9f, 0xe5};