diff --git a/mods/src/extra.cpp b/mods/src/extra.cpp index 009f7f8..7297804 100644 --- a/mods/src/extra.cpp +++ b/mods/src/extra.cpp @@ -83,6 +83,8 @@ extern "C" { typedef unsigned char *(*ItemInstance_t)(unsigned char *item_instance, unsigned char *item); static ItemInstance_t ItemInstance_item = (ItemInstance_t) 0x9992c; static ItemInstance_t ItemInstance_tile = (ItemInstance_t) 0x998e4; + typedef unsigned char *(*ItemInstance_damage_t)(unsigned char *item_instance, unsigned char *item, int32_t count, int32_t damage); + static ItemInstance_damage_t ItemInstance_damage = (ItemInstance_damage_t) 0x99960; typedef int32_t (*FillingContainer_addItem_t)(unsigned char *filling_container, unsigned char *item_instance); static FillingContainer_addItem_t FillingContainer_addItem = (FillingContainer_addItem_t) 0x92aa0; @@ -95,16 +97,20 @@ extern "C" { } // Items + static unsigned char **item_sign = (unsigned char **) 0x17bba4; static unsigned char **item_flintAndSteel = (unsigned char **) 0x17ba70; static unsigned char **item_snowball = (unsigned char **) 0x17bbb0; static unsigned char **item_shears = (unsigned char **) 0x17bbf0; - static unsigned char **item_sign = (unsigned char **) 0x17bba4; + static unsigned char **item_egg = (unsigned char **) 0x17bbd0; + static unsigned char **item_dye_powder = (unsigned char **) 0x17bbe0; // Tiles static unsigned char **tile_water = (unsigned char **) 0x181b3c; static unsigned char **tile_lava = (unsigned char **) 0x181cc8; static unsigned char **tile_calmWater = (unsigned char **) 0x181b40; static unsigned char **tile_calmLava = (unsigned char **) 0x181ccc; static unsigned char **tile_glowingObsidian = (unsigned char **) 0x181dcc; + static unsigned char **tile_topSnow = (unsigned char **) 0x181b30; + static unsigned char **tile_ice = (unsigned char **) 0x181d80; static unsigned char **tile_invisible_bedrock = (unsigned char **) 0x181d94; static int32_t FillingContainer_addItem_injection(unsigned char *filling_container, unsigned char *item_instance) { @@ -118,13 +124,21 @@ extern "C" { // Add Items inventory_add_item(filling_container, *item_flintAndSteel, false); inventory_add_item(filling_container, *item_snowball, false); + inventory_add_item(filling_container, *item_egg, false); inventory_add_item(filling_container, *item_shears, false); + for (int i = 0; i < 15; i++) { + unsigned char *item_instance = (unsigned char *) ::operator new(0xc); + item_instance = (*ItemInstance_damage)(item_instance, *item_dye_powder, 1, i); + (*FillingContainer_addItem)(filling_container, item_instance); + } // Add Tiles inventory_add_item(filling_container, *tile_water, true); inventory_add_item(filling_container, *tile_lava, true); inventory_add_item(filling_container, *tile_calmWater, true); inventory_add_item(filling_container, *tile_calmLava, true); inventory_add_item(filling_container, *tile_glowingObsidian, true); + inventory_add_item(filling_container, *tile_topSnow, true); + inventory_add_item(filling_container, *tile_ice, true); inventory_add_item(filling_container, *tile_invisible_bedrock, true); } diff --git a/mods/src/server/server.cpp b/mods/src/server/server.cpp index 271e69f..9a706ba 100644 --- a/mods/src/server/server.cpp +++ b/mods/src/server/server.cpp @@ -4,9 +4,12 @@ #include #include #include +#include + #include #include +#include #include @@ -102,41 +105,34 @@ static void *read_stdin_thread(__attribute__((unused)) void *data) { } } -typedef void (*ServerSideNetworkHandler_displayGameMessage_t)(unsigned char *server_side_network_handler, std::string const& message); -static ServerSideNetworkHandler_displayGameMessage_t ServerSideNetworkHandler_displayGameMessage = (ServerSideNetworkHandler_displayGameMessage_t) 0x750c4; +// Create/Start World +static void start_world(unsigned char *minecraft) { + INFO("%s", "Starting Minecraft: Pi Edition Dedicated Server"); -// Runs Every Tick + LevelSettings settings; + 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; + + std::string world_name = get_server_properties().get_string("world-name", DEFAULT_WORLD_NAME); + (*Minecraft_selectLevel)(minecraft, world_name, world_name, settings); + + int port = get_server_properties().get_int("port", DEFAULT_PORT); + (*Minecraft_hostMultiplayer)(minecraft, port); + INFO("Listening On: %i", port); + + void *screen = ::operator new(0x4c); + screen = (*ProgressScreen)((unsigned char *) screen); + (*Minecraft_setScreen)(minecraft, (unsigned char *) screen); + + stored_minecraft = minecraft; +} + +// Print Progress Reports static int last_progress = -1; static const char *last_message = NULL; -static bool loaded = false; -static void Minecraft_update_injection(unsigned char *minecraft) { - // Create/Start World - if (!loaded) { - INFO("%s", "Starting Minecraft: Pi Edition Dedicated Server"); - - LevelSettings settings; - 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; - - std::string world_name = get_server_properties().get_string("world-name", DEFAULT_WORLD_NAME); - (*Minecraft_selectLevel)(minecraft, world_name, world_name, settings); - - int port = get_server_properties().get_int("port", DEFAULT_PORT); - (*Minecraft_hostMultiplayer)(minecraft, port); - INFO("Listening On: %i", port); - - void *screen = ::operator new(0x4c); - screen = (*ProgressScreen)((unsigned char *) screen); - (*Minecraft_setScreen)(minecraft, (unsigned char *) screen); - - stored_minecraft = minecraft; - - loaded = true; - } - - // Print Progress Message +static void print_progress(unsigned char *minecraft) { const char *message = (*Minecraft_getProgressMessage)(minecraft); int32_t progress = *(int32_t *) (minecraft + 0xc60); if ((*Minecraft_isLevelGenerated)(minecraft)) { @@ -160,30 +156,100 @@ static void Minecraft_update_injection(unsigned char *minecraft) { } } } +} - // Call Original Method - revert_overwrite((void *) Minecraft_update, Minecraft_update_original); - (*Minecraft_update)(minecraft); - revert_overwrite((void *) Minecraft_update, Minecraft_update_original); +struct RakNet_RakNetGUID { + uint64_t g; + unsigned short SystemIndex; +}; +struct RakNet_SystemAddress { + union { + sockaddr_in addr4; + } address; + unsigned short debugPort; +}; +typedef RakNet_SystemAddress (*RakNet_RakPeer_GetSystemAddressFromGuid_t)(unsigned char *rak_peer, RakNet_RakNetGUID guid); - // Use STDIN - if (stdin_buffer_complete) { - if (stdin_buffer != NULL) { - unsigned char *server_side_network_handler = *(unsigned char **) (minecraft + 0x174); - if (server_side_network_handler != NULL) { - char *message = NULL; - // Format Message - asprintf(&message, "[Server] %s", stdin_buffer); - // Post Message To Chat - (*ServerSideNetworkHandler_displayGameMessage)(server_side_network_handler, message); - free(message); - } +typedef void (*ServerSideNetworkHandler_displayGameMessage_t)(unsigned char *server_side_network_handler, std::string const& message); +static ServerSideNetworkHandler_displayGameMessage_t ServerSideNetworkHandler_displayGameMessage = (ServerSideNetworkHandler_displayGameMessage_t) 0x750c4; - free((void *) stdin_buffer); - stdin_buffer = NULL; +typedef char *(*RakNet_SystemAddress_ToString_t)(RakNet_SystemAddress *system_address, bool print_delimiter, char delimiter); +static RakNet_SystemAddress_ToString_t RakNet_SystemAddress_ToString = (RakNet_SystemAddress_ToString_t) 0xd6198; + +typedef void (*ServerSideNetworkHandler_onDisconnect_t)(unsigned char *server_side_network_handler, RakNet_RakNetGUID const& guid); + +static std::string get_banned_ips_file() { + std::string file(getenv("HOME")); + file.append("/.minecraft/banned-ips.txt"); + return file; +} + +typedef void (*player_callback_t)(unsigned char *minecraft, std::string username, unsigned char *player); + +// Find Players With Username And Run Callback +static void find_players(unsigned char *minecraft, std::string target_username, player_callback_t callback) { + unsigned char *level = *(unsigned char **) (minecraft + 0x188); + std::vector players = *(std::vector *) (level + 0x60); + bool found_player = false; + for (std::size_t i = 0; i < players.size(); i++) { + // Iterate Players + unsigned char *player = players[i]; + std::string username = *(std::string *) (player + 0xbf4); + if (target_username == "" || username == target_username) { + // Run Callback + (*callback)(minecraft, username, player); + found_player = true; } - stdin_buffer_complete = false; } + if (!found_player && target_username != "") { + INFO("Invalid Player: %s", target_username.c_str()); + } +} + +// Get IP From Player +static char *get_player_ip(unsigned char *minecraft, unsigned char *player) { + RakNet_RakNetGUID guid = *(RakNet_RakNetGUID *) (player + 0xc08); + unsigned char *rak_net_instance = *(unsigned char **) (minecraft + 0x170); + unsigned char *rak_peer = *(unsigned char **) (rak_net_instance + 0x4); + unsigned char *rak_peer_vtable = *(unsigned char **) rak_peer; + RakNet_RakPeer_GetSystemAddressFromGuid_t RakNet_RakPeer_GetSystemAddressFromGuid = *(RakNet_RakPeer_GetSystemAddressFromGuid_t *) (rak_peer_vtable + 0xd0); + // Get SystemAddress + RakNet_SystemAddress address = (*RakNet_RakPeer_GetSystemAddressFromGuid)(rak_peer, guid); + // Get IP + return (*RakNet_SystemAddress_ToString)(&address, false, '|'); +} + +// Ban Player +static void ban_callback(unsigned char *minecraft, std::string username, unsigned char *player) { + // Get IP + char *ip = get_player_ip(minecraft, player); + + // Ban Player + INFO("Banned: %s (%s)", username.c_str(), ip); + // Write To File + std::ofstream banned_ips_output(get_banned_ips_file(), std::ios_base::app); + if (banned_ips_output) { + if (banned_ips_output.good()) { + banned_ips_output << ip <<'\n'; + } + if (banned_ips_output.is_open()) { + banned_ips_output.close(); + } + } +} + +// Kill Player +typedef void (*Entity_die_t)(unsigned char *entity, unsigned char *cause); +static void kill_callback(__attribute__((unused)) unsigned char *minecraft, __attribute__((unused)) std::string username, unsigned char *player) { + unsigned char *player_vtable = *(unsigned char **) player; + Entity_die_t Entity_die = *(Entity_die_t *) (player_vtable + 0x130); + (*Entity_die)(player, NULL); + INFO("Killed: %s", username.c_str()); +} + +// List Player +static void list_callback(unsigned char *minecraft, std::string username, unsigned char *player) { + INFO(" - %s (%s)", username.c_str(), get_player_ip(minecraft, player)); } typedef void (*Level_saveLevelData_t)(unsigned char *level); @@ -200,20 +266,7 @@ static void Level_saveLevelData_injection(unsigned char *level) { revert_overwrite((void *) Level_saveLevelData, Level_saveLevelData_original); } -typedef void (*Gui_addMessage_t)(unsigned char *gui, std::string const& text); -static Gui_addMessage_t Gui_addMessage = (Gui_addMessage_t) 0x27820; -static void *Gui_addMessage_original = NULL; - -static void Gui_addMessage_injection(unsigned char *gui, std::string const& text) { - // Print Log Message - fprintf(stderr, "[CHAT]: %s\n", text.c_str()); - - // Call Original Method - revert_overwrite((void *) Gui_addMessage, Gui_addMessage_original); - (*Gui_addMessage)(gui, text); - revert_overwrite((void *) Gui_addMessage, Gui_addMessage_original); -} - +// Stop Server static void exit_handler(__attribute__((unused)) int data) { INFO("%s", "Stopping Server"); if (stored_minecraft != NULL) { @@ -229,6 +282,137 @@ static void exit_handler(__attribute__((unused)) int data) { SDL_PushEvent(&event); } +// Handle Commands +static void handle_commands(unsigned char *minecraft) { + if (stdin_buffer_complete) { + if (stdin_buffer != NULL) { + unsigned char *server_side_network_handler = *(unsigned char **) (minecraft + 0x174); + if (server_side_network_handler != NULL) { + std::string data((char *) stdin_buffer); + + static std::string ban_command("ban "); + static std::string say_command("say "); + static std::string kill_command("kill "); + static std::string list_command("list"); + static std::string stop_command("stop"); + static std::string help_command("help"); + if (data.rfind(ban_command, 0) == 0) { + // IP-Ban Target Username + std::string ban_username = data.substr(ban_command.length()); + find_players(minecraft, ban_username, ban_callback); + } else if (data.rfind(kill_command, 0) == 0) { + // Kill Target Username + std::string kill_username = data.substr(kill_command.length()); + find_players(minecraft, kill_username, kill_callback); + } else if (data.rfind(say_command, 0) == 0) { + // Format Message + std::string message = "[Server] " + data.substr(say_command.length()); + // Post Message To Chat + (*ServerSideNetworkHandler_displayGameMessage)(server_side_network_handler, message); + } else if (data == list_command) { + // List Players + INFO("%s", "All Players:"); + find_players(minecraft, "", list_callback); + } else if (data == stop_command) { + // Stop Server + exit_handler(-1); + } else if (data == help_command) { + INFO("%s", "All Commands:"); + INFO("%s", " ban - IP-Ban All Players With Specifed Username"); + INFO("%s", " kill - Kill All Players With Specifed Username"); + INFO("%s", " say - Print Specified Message To Chat"); + INFO("%s", " list - List All Players"); + INFO("%s", " stop - Stop Server"); + INFO("%s", " help - Print This Message"); + } else { + INFO("Invalid Command: %s", data.c_str()); + } + } + + free((void *) stdin_buffer); + stdin_buffer = NULL; + } + stdin_buffer_complete = false; + } +} + +// Runs Every Tick +static bool loaded = false; +static void Minecraft_update_injection(unsigned char *minecraft) { + // Create/Start World + if (!loaded) { + start_world(minecraft); + loaded = true; + } + + // Print Progress Reports + print_progress(minecraft); + + // Call Original Method + revert_overwrite((void *) Minecraft_update, Minecraft_update_original); + (*Minecraft_update)(minecraft); + revert_overwrite((void *) Minecraft_update, Minecraft_update_original); + + // Handle Commands + handle_commands(minecraft); +} + +typedef void (*Gui_addMessage_t)(unsigned char *gui, std::string const& text); +static Gui_addMessage_t Gui_addMessage = (Gui_addMessage_t) 0x27820; +static void *Gui_addMessage_original = NULL; + +static void Gui_addMessage_injection(unsigned char *gui, std::string const& text) { + // Print Log Message + fprintf(stderr, "[CHAT]: %s\n", text.c_str()); + + // Call Original Method + revert_overwrite((void *) Gui_addMessage, Gui_addMessage_original); + (*Gui_addMessage)(gui, text); + revert_overwrite((void *) Gui_addMessage, Gui_addMessage_original); +} + +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; +static void *RakNet_RakPeer_IsBanned_original = NULL; + +static bool RakNet_RakPeer_IsBanned_injection(unsigned char *rakpeer, const char *ip) { + // Call Original + revert_overwrite((void *) RakNet_RakPeer_IsBanned, RakNet_RakPeer_IsBanned_original); + bool ret = (*RakNet_RakPeer_IsBanned)(rakpeer, ip); + revert_overwrite((void *) RakNet_RakPeer_IsBanned, RakNet_RakPeer_IsBanned_original); + + if (ret) { + return true; + } else { + // Check banned-ips.txt + std::string banned_ips_file_path = get_banned_ips_file(); + std::ifstream banned_ips_file(banned_ips_file_path); + if (banned_ips_file) { + bool ret = false; + if (banned_ips_file.good()) { + std::string line; + while (std::getline(banned_ips_file, line)) { + if (line.length() > 0) { + if (line[0] == '#') { + continue; + } + if (strcmp(line.c_str(), ip) == 0) { + ret = true; + break; + } + } + } + } + if (banned_ips_file.is_open()) { + banned_ips_file.close(); + } + return ret; + } else { + ERR("%s", "Unable To Read banned-ips.txt"); + } + } +} + const char *server_get_motd() { return get_server_properties().get_string("motd", DEFAULT_MOTD).c_str(); } @@ -253,7 +437,7 @@ void server_init() { std::ifstream properties_file(file); - if (!properties_file || !properties_file.is_open()) { + if (!properties_file || !properties_file.good()) { // Write Defaults std::ofstream properties_file_output(file); properties_file_output << "# Message Of The Day\n"; @@ -284,6 +468,19 @@ void server_init() { properties_file.close(); + // Create Empty Banned IPs File + std::string banned_ips_file_path = get_banned_ips_file(); + std::ifstream banned_ips_file(banned_ips_file_path); + if (!banned_ips_file || !banned_ips_file.good()) { + // Write Default + std::ofstream banned_ips_output(banned_ips_file_path); + banned_ips_output << "# List Of Banned IPs; Each Line Is One IP Address\n"; + banned_ips_output.close(); + } + if (banned_ips_file.is_open()) { + banned_ips_file.close(); + } + // Prevent Main Player From Loading unsigned char player_patch[4] = {0x00, 0x20, 0xa0, 0xe3}; patch((void *) 0x1685c, player_patch); @@ -301,6 +498,8 @@ void server_init() { // Set Max Players unsigned char max_players_patch[4] = {server_get_max_players(), 0x30, 0xa0, 0xe3}; patch((void *) 0x166d0, max_players_patch); + // Custom Banned IP List + RakNet_RakPeer_IsBanned_original = overwrite((void *) RakNet_RakPeer_IsBanned, (void *) RakNet_RakPeer_IsBanned_injection); // Start Reading STDIN pthread_t read_stdin_thread_obj;