2022-06-11 01:59:57 +00:00
|
|
|
// Config Needs To Load First
|
|
|
|
#include <libreborn/libreborn.h>
|
|
|
|
|
2021-06-17 21:32:24 +00:00
|
|
|
#ifndef MCPI_SERVER_MODE
|
|
|
|
#error "Server Code Requires Server Mode"
|
|
|
|
#endif
|
|
|
|
|
2020-10-10 23:02:13 +00:00
|
|
|
#include <string>
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <ctime>
|
|
|
|
#include <cstdio>
|
|
|
|
#include <fstream>
|
2020-11-03 22:39:55 +00:00
|
|
|
#include <vector>
|
|
|
|
|
2021-11-21 04:09:25 +00:00
|
|
|
#include <sys/ioctl.h>
|
2020-10-31 01:31:55 +00:00
|
|
|
#include <pthread.h>
|
2020-10-10 23:02:13 +00:00
|
|
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
2021-06-17 21:32:24 +00:00
|
|
|
#include <SDL/SDL.h>
|
2020-10-10 23:02:13 +00:00
|
|
|
|
2021-11-14 04:29:48 +00:00
|
|
|
#include <symbols/minecraft.h>
|
2020-10-10 23:02:13 +00:00
|
|
|
|
2024-01-24 01:51:36 +00:00
|
|
|
#include <mods/server/server.h>
|
2020-10-10 23:02:13 +00:00
|
|
|
|
2022-06-25 21:30:08 +00:00
|
|
|
#include <mods/feature/feature.h>
|
|
|
|
#include <mods/init/init.h>
|
|
|
|
#include <mods/home/home.h>
|
|
|
|
#include <mods/compat/compat.h>
|
|
|
|
#include <mods/misc/misc.h>
|
2020-11-21 21:52:27 +00:00
|
|
|
|
2021-06-17 21:32:24 +00:00
|
|
|
// --only-generate: Ony Generate World And Then Exit
|
|
|
|
static bool only_generate = false;
|
2024-05-12 07:19:01 +00:00
|
|
|
__attribute__((constructor)) static void _init_only_generate() {
|
|
|
|
only_generate = getenv("_MCPI_ONLY_GENERATE") != nullptr;
|
2021-06-17 21:32:24 +00:00
|
|
|
}
|
|
|
|
|
2020-10-10 23:02:13 +00:00
|
|
|
// Server Properties
|
2024-01-24 01:51:36 +00:00
|
|
|
ServerProperties &get_server_properties() {
|
2020-10-10 23:02:13 +00:00
|
|
|
static ServerProperties properties;
|
|
|
|
return properties;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Default Server Properties
|
2022-04-13 00:38:44 +00:00
|
|
|
#define DEFAULT_MOTD "Minecraft Server"
|
2020-11-10 20:16:42 +00:00
|
|
|
#define DEFAULT_SHOW_MINECON_BADGE "false"
|
2020-10-10 23:02:13 +00:00
|
|
|
#define DEFAULT_GAME_MODE "0"
|
|
|
|
#define DEFAULT_PORT "19132"
|
|
|
|
#define DEFAULT_SEED ""
|
2021-07-04 23:02:45 +00:00
|
|
|
#define DEFAULT_FORCE_MOB_SPAWNING "false"
|
2022-05-11 22:24:03 +00:00
|
|
|
#define DEFAULT_PEACEFUL_MODE "false"
|
2020-10-10 23:02:13 +00:00
|
|
|
#define DEFAULT_WORLD_NAME "world"
|
2020-10-11 19:38:48 +00:00
|
|
|
#define DEFAULT_MAX_PLAYERS "4"
|
2021-03-06 20:56:05 +00:00
|
|
|
#define DEFAULT_WHITELIST "false"
|
2023-02-25 05:26:45 +00:00
|
|
|
#define DEFAULT_DEATH_MESSAGES "true"
|
|
|
|
#define DEFAULT_GENERATE_CAVES "true"
|
2020-10-10 23:02:13 +00:00
|
|
|
|
2020-11-06 04:05:37 +00:00
|
|
|
// Get World Name
|
2021-06-17 21:32:24 +00:00
|
|
|
static std::string get_world_name() {
|
2022-07-20 06:58:14 +00:00
|
|
|
std::string name = get_server_properties().get_string("world-name", DEFAULT_WORLD_NAME);
|
|
|
|
char *safe_name_c = to_cp437(name.c_str());
|
|
|
|
std::string safe_name = safe_name_c;
|
|
|
|
free(safe_name_c);
|
|
|
|
return safe_name;
|
2020-11-06 04:05:37 +00:00
|
|
|
}
|
|
|
|
|
2020-11-03 22:39:55 +00:00
|
|
|
// Create/Start World
|
2024-01-06 23:03:48 +00:00
|
|
|
static void start_world(Minecraft *minecraft) {
|
2021-11-14 04:29:48 +00:00
|
|
|
// Get World Name
|
|
|
|
std::string world_name = get_world_name();
|
|
|
|
|
|
|
|
// Log
|
|
|
|
INFO("Loading World: %s", world_name.c_str());
|
2020-10-31 01:31:55 +00:00
|
|
|
|
2022-05-11 22:24:03 +00:00
|
|
|
// Peaceful Mode
|
2024-01-06 23:03:48 +00:00
|
|
|
Options *options = &minecraft->options;
|
|
|
|
options->game_difficulty = get_server_properties().get_bool("peaceful-mode", DEFAULT_PEACEFUL_MODE) ? 0 : 2;
|
2022-05-11 22:24:03 +00:00
|
|
|
|
2021-06-17 21:32:24 +00:00
|
|
|
// Specify Level Settings
|
2020-11-03 22:39:55 +00:00
|
|
|
LevelSettings settings;
|
2021-11-14 04:29:48 +00:00
|
|
|
settings.game_type = get_server_properties().get_int("game-mode", DEFAULT_GAME_MODE);
|
2020-11-03 22:39:55 +00:00
|
|
|
std::string seed_str = get_server_properties().get_string("seed", DEFAULT_SEED);
|
2024-04-02 23:22:01 +00:00
|
|
|
int32_t seed = seed_str.length() > 0 ? std::stoi(seed_str) : time(nullptr);
|
2020-11-03 22:39:55 +00:00
|
|
|
settings.seed = seed;
|
2020-10-10 23:02:13 +00:00
|
|
|
|
2021-06-17 21:32:24 +00:00
|
|
|
// Select Level
|
2024-05-15 09:02:19 +00:00
|
|
|
minecraft->selectLevel(&world_name, &world_name, &settings);
|
2020-10-10 23:02:13 +00:00
|
|
|
|
2021-06-17 21:32:24 +00:00
|
|
|
// Don't Open Port When Using --only-generate
|
|
|
|
if (!only_generate) {
|
|
|
|
// Open Port
|
|
|
|
int port = get_server_properties().get_int("port", DEFAULT_PORT);
|
|
|
|
INFO("Listening On: %i", port);
|
2024-05-15 09:02:19 +00:00
|
|
|
minecraft->hostMultiplayer(port);
|
2021-06-17 21:32:24 +00:00
|
|
|
}
|
2020-10-10 23:02:13 +00:00
|
|
|
|
2021-06-17 21:32:24 +00:00
|
|
|
// Open ProgressScreen
|
2024-05-17 04:36:28 +00:00
|
|
|
ProgressScreen *screen = new ProgressScreen;
|
2021-02-16 17:26:40 +00:00
|
|
|
ALLOC_CHECK(screen);
|
2024-05-15 09:02:19 +00:00
|
|
|
screen = screen->constructor();
|
|
|
|
minecraft->setScreen((Screen *) screen);
|
2020-11-03 22:39:55 +00:00
|
|
|
}
|
2020-10-10 23:02:13 +00:00
|
|
|
|
2021-03-06 20:56:05 +00:00
|
|
|
// Check If Running In Whitelist Mode
|
|
|
|
static bool is_whitelist() {
|
|
|
|
return get_server_properties().get_bool("whitelist", DEFAULT_WHITELIST);
|
|
|
|
}
|
|
|
|
// Get Path Of Blacklist (Or Whitelist) File
|
|
|
|
static std::string get_blacklist_file() {
|
2021-06-28 02:16:37 +00:00
|
|
|
std::string file(home_get());
|
2021-06-17 21:32:24 +00:00
|
|
|
file.append(is_whitelist() ? "/whitelist.txt" : "/blacklist.txt");
|
2020-11-03 22:39:55 +00:00
|
|
|
return file;
|
|
|
|
}
|
|
|
|
|
2020-11-06 04:05:37 +00:00
|
|
|
// Get Vector Of Players In Level
|
2024-01-06 23:03:48 +00:00
|
|
|
static std::vector<Player *> get_players_in_level(Level *level) {
|
|
|
|
return level->players;
|
2020-11-06 04:05:37 +00:00
|
|
|
}
|
|
|
|
// Get Player's Username
|
2024-01-06 23:03:48 +00:00
|
|
|
static std::string get_player_username(Player *player) {
|
|
|
|
std::string *username = &player->username;
|
2022-07-20 06:58:14 +00:00
|
|
|
char *safe_username_c = from_cp437(username->c_str());
|
|
|
|
std::string safe_username = safe_username_c;
|
|
|
|
free(safe_username_c);
|
|
|
|
return safe_username;
|
2020-11-06 04:05:37 +00:00
|
|
|
}
|
|
|
|
// Get Level From Minecraft
|
2024-01-06 23:03:48 +00:00
|
|
|
static Level *get_level(Minecraft *minecraft) {
|
|
|
|
return minecraft->level;
|
2020-11-06 04:05:37 +00:00
|
|
|
}
|
|
|
|
|
2020-11-03 22:39:55 +00:00
|
|
|
// Find Players With Username And Run Callback
|
2024-01-06 23:03:48 +00:00
|
|
|
typedef void (*player_callback_t)(Minecraft *minecraft, std::string username, Player *player);
|
|
|
|
static void find_players(Minecraft *minecraft, std::string target_username, player_callback_t callback, bool all_players) {
|
|
|
|
Level *level = get_level(minecraft);
|
|
|
|
std::vector<Player *> players = get_players_in_level(level);
|
2020-11-03 22:39:55 +00:00
|
|
|
bool found_player = false;
|
|
|
|
for (std::size_t i = 0; i < players.size(); i++) {
|
|
|
|
// Iterate Players
|
2024-01-06 23:03:48 +00:00
|
|
|
Player *player = players[i];
|
2022-07-20 06:58:14 +00:00
|
|
|
std::string username = get_player_username(player);
|
2020-11-04 00:31:27 +00:00
|
|
|
if (all_players || username == target_username) {
|
2020-11-03 22:39:55 +00:00
|
|
|
// Run Callback
|
2024-01-07 08:23:43 +00:00
|
|
|
callback(minecraft, username, player);
|
2020-11-03 22:39:55 +00:00
|
|
|
found_player = true;
|
|
|
|
}
|
|
|
|
}
|
2020-11-04 00:31:27 +00:00
|
|
|
if (!all_players && !found_player) {
|
2020-11-03 22:39:55 +00:00
|
|
|
INFO("Invalid Player: %s", target_username.c_str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-17 21:32:24 +00:00
|
|
|
// Get RakNet Objects
|
2024-01-06 23:03:48 +00:00
|
|
|
static RakNet_RakNetGUID get_rak_net_guid(Player *player) {
|
|
|
|
return ((ServerPlayer *) player)->guid;
|
2020-11-04 21:05:31 +00:00
|
|
|
}
|
2024-01-06 23:03:48 +00:00
|
|
|
static RakNet_SystemAddress get_system_address(RakNet_RakPeer *rak_peer, RakNet_RakNetGUID guid) {
|
2020-11-03 22:39:55 +00:00
|
|
|
// Get SystemAddress
|
2024-05-15 09:02:19 +00:00
|
|
|
return rak_peer->GetSystemAddressFromGuid(guid);
|
2020-11-04 21:05:31 +00:00
|
|
|
}
|
2024-01-06 23:03:48 +00:00
|
|
|
static RakNet_RakPeer *get_rak_peer(Minecraft *minecraft) {
|
|
|
|
return minecraft->rak_net_instance->peer;
|
2020-11-04 21:05:31 +00:00
|
|
|
}
|
2024-01-06 23:03:48 +00:00
|
|
|
static char *get_rak_net_guid_ip(RakNet_RakPeer *rak_peer, RakNet_RakNetGUID guid) {
|
2021-09-28 18:04:05 +00:00
|
|
|
RakNet_SystemAddress address = get_system_address(rak_peer, guid);
|
|
|
|
// Get IP
|
2024-05-15 09:02:19 +00:00
|
|
|
return address.ToString(false, '|');
|
2021-09-28 18:04:05 +00:00
|
|
|
}
|
2020-11-04 21:05:31 +00:00
|
|
|
|
|
|
|
// Get IP From Player
|
2024-01-06 23:03:48 +00:00
|
|
|
static char *get_player_ip(Minecraft *minecraft, Player *player) {
|
|
|
|
RakNet_RakPeer *rak_peer = get_rak_peer(minecraft);
|
2021-09-28 18:04:05 +00:00
|
|
|
RakNet_RakNetGUID guid = get_rak_net_guid(player);
|
|
|
|
// Return
|
2024-01-06 23:03:48 +00:00
|
|
|
return get_rak_net_guid_ip(rak_peer, guid);
|
2020-11-03 22:39:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Ban Player
|
2022-06-26 03:32:31 +00:00
|
|
|
static bool is_ip_in_blacklist(const char *ip);
|
2024-01-06 23:03:48 +00:00
|
|
|
static void ban_callback(Minecraft *minecraft, std::string username, Player *player) {
|
2020-11-03 22:39:55 +00:00
|
|
|
// Get IP
|
|
|
|
char *ip = get_player_ip(minecraft, player);
|
|
|
|
|
|
|
|
// Ban Player
|
|
|
|
INFO("Banned: %s (%s)", username.c_str(), ip);
|
|
|
|
// Write To File
|
2021-03-06 20:56:05 +00:00
|
|
|
std::ofstream blacklist_output(get_blacklist_file(), std::ios_base::app);
|
|
|
|
if (blacklist_output) {
|
|
|
|
if (blacklist_output.good()) {
|
|
|
|
blacklist_output << "# " << username << '\n' << ip << '\n';
|
2020-11-03 22:39:55 +00:00
|
|
|
}
|
2021-03-06 20:56:05 +00:00
|
|
|
if (blacklist_output.is_open()) {
|
|
|
|
blacklist_output.close();
|
2020-10-31 01:31:55 +00:00
|
|
|
}
|
|
|
|
}
|
2022-06-26 03:32:31 +00:00
|
|
|
// Reload
|
2024-04-02 23:22:01 +00:00
|
|
|
is_ip_in_blacklist(nullptr);
|
2020-10-10 23:02:13 +00:00
|
|
|
}
|
|
|
|
|
2020-11-03 22:39:55 +00:00
|
|
|
// Kill Player
|
2024-01-06 23:03:48 +00:00
|
|
|
static void kill_callback(__attribute__((unused)) Minecraft *minecraft, __attribute__((unused)) std::string username, Player *player) {
|
2024-05-15 09:02:19 +00:00
|
|
|
player->hurt(nullptr, INT32_MAX);
|
2020-11-03 22:39:55 +00:00
|
|
|
INFO("Killed: %s", username.c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
// List Player
|
2024-01-06 23:03:48 +00:00
|
|
|
static void list_callback(Minecraft *minecraft, std::string username, Player *player) {
|
2020-11-03 22:39:55 +00:00
|
|
|
INFO(" - %s (%s)", username.c_str(), get_player_ip(minecraft, player));
|
|
|
|
}
|
|
|
|
|
2021-06-17 21:32:24 +00:00
|
|
|
// Handle Server Stop
|
2024-01-06 23:03:48 +00:00
|
|
|
static void handle_server_stop(Minecraft *minecraft) {
|
2021-06-17 21:32:24 +00:00
|
|
|
if (compat_check_exit_requested()) {
|
2022-04-15 01:12:42 +00:00
|
|
|
INFO("Stopping Server");
|
2020-11-04 21:05:31 +00:00
|
|
|
// Save And Exit
|
2024-01-06 23:03:48 +00:00
|
|
|
Level *level = get_level(minecraft);
|
2024-04-02 23:22:01 +00:00
|
|
|
if (level != nullptr) {
|
2024-05-15 09:02:19 +00:00
|
|
|
level->saveLevelData();
|
2020-11-10 02:31:02 +00:00
|
|
|
}
|
2024-05-15 09:02:19 +00:00
|
|
|
minecraft->leaveGame(false);
|
2020-11-04 21:05:31 +00:00
|
|
|
// Stop Game
|
|
|
|
SDL_Event event;
|
|
|
|
event.type = SDL_QUIT;
|
|
|
|
SDL_PushEvent(&event);
|
2020-11-03 22:39:55 +00:00
|
|
|
}
|
2020-11-04 21:05:31 +00:00
|
|
|
}
|
|
|
|
|
2022-05-29 22:44:27 +00:00
|
|
|
// Track TPS
|
|
|
|
#define NANOSECONDS_IN_SECOND 1000000000ll
|
|
|
|
static long long int get_time() {
|
2024-05-05 00:46:15 +00:00
|
|
|
timespec ts = {};
|
2022-05-29 22:44:27 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
static bool is_last_tick_time_set = false;
|
|
|
|
static long long int last_tick_time;
|
|
|
|
static double tps = 0;
|
2024-01-06 23:03:48 +00:00
|
|
|
static void Minecraft_tick_injection(__attribute__((unused)) Minecraft *minecraft) {
|
2022-05-29 22:44:27 +00:00
|
|
|
long long int time = get_time();
|
|
|
|
if (is_last_tick_time_set) {
|
|
|
|
long long int tick_time = time - last_tick_time;
|
|
|
|
tps = ((double) NANOSECONDS_IN_SECOND) / ((double) tick_time);
|
|
|
|
} else {
|
|
|
|
is_last_tick_time_set = true;
|
|
|
|
}
|
|
|
|
last_tick_time = time;
|
|
|
|
}
|
|
|
|
|
2020-11-04 21:05:31 +00:00
|
|
|
// Get ServerSideNetworkHandler From Minecraft
|
2024-01-06 23:03:48 +00:00
|
|
|
static ServerSideNetworkHandler *get_server_side_network_handler(Minecraft *minecraft) {
|
|
|
|
return (ServerSideNetworkHandler *) minecraft->network_handler;
|
2020-11-03 22:39:55 +00:00
|
|
|
}
|
|
|
|
|
2021-06-17 21:32:24 +00:00
|
|
|
// Read STDIN Thread
|
|
|
|
static volatile bool stdin_buffer_complete = false;
|
2024-04-02 23:22:01 +00:00
|
|
|
static volatile char *stdin_buffer = nullptr;
|
2021-06-17 21:32:24 +00:00
|
|
|
static void *read_stdin_thread(__attribute__((unused)) void *data) {
|
2022-05-29 22:44:27 +00:00
|
|
|
// Loop
|
2024-05-05 00:46:15 +00:00
|
|
|
while (true) {
|
2022-05-29 22:44:27 +00:00
|
|
|
int bytes_available;
|
|
|
|
if (ioctl(fileno(stdin), FIONREAD, &bytes_available) == -1) {
|
|
|
|
bytes_available = 0;
|
|
|
|
}
|
|
|
|
char buffer[bytes_available];
|
|
|
|
bytes_available = read(fileno(stdin), (void *) buffer, bytes_available);
|
|
|
|
for (int i = 0; i < bytes_available; i++) {
|
|
|
|
if (!stdin_buffer_complete) {
|
|
|
|
// Read Data
|
|
|
|
char x = buffer[i];
|
|
|
|
if (x == '\n') {
|
2024-04-02 23:22:01 +00:00
|
|
|
if (stdin_buffer == nullptr) {
|
2022-05-29 22:44:27 +00:00
|
|
|
stdin_buffer = (volatile char *) malloc(1);
|
|
|
|
stdin_buffer[0] = '\0';
|
2021-06-17 21:32:24 +00:00
|
|
|
}
|
2022-05-29 22:44:27 +00:00
|
|
|
stdin_buffer_complete = true;
|
|
|
|
} else {
|
|
|
|
string_append((char **) &stdin_buffer, "%c", (char) x);
|
2021-06-17 21:32:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-04-02 23:22:01 +00:00
|
|
|
return nullptr;
|
2021-06-17 21:32:24 +00:00
|
|
|
}
|
|
|
|
__attribute__((destructor)) static void _free_stdin_buffer() {
|
2024-04-02 23:22:01 +00:00
|
|
|
if (stdin_buffer != nullptr) {
|
2021-09-28 18:04:05 +00:00
|
|
|
free((void *) stdin_buffer);
|
2024-04-02 23:22:01 +00:00
|
|
|
stdin_buffer = nullptr;
|
2021-09-28 18:04:05 +00:00
|
|
|
}
|
2021-06-17 21:32:24 +00:00
|
|
|
}
|
|
|
|
|
2020-11-03 22:39:55 +00:00
|
|
|
// Handle Commands
|
2024-01-06 23:03:48 +00:00
|
|
|
static void handle_commands(Minecraft *minecraft) {
|
2021-06-17 21:32:24 +00:00
|
|
|
// Check If Level Is Generated
|
2024-05-15 09:08:39 +00:00
|
|
|
if (minecraft->isLevelGenerated() && stdin_buffer_complete) {
|
2021-06-17 21:32:24 +00:00
|
|
|
// Command Ready; Run It
|
2024-04-02 23:22:01 +00:00
|
|
|
if (stdin_buffer != nullptr) {
|
2024-01-06 23:03:48 +00:00
|
|
|
ServerSideNetworkHandler *server_side_network_handler = get_server_side_network_handler(minecraft);
|
2024-04-02 23:22:01 +00:00
|
|
|
if (server_side_network_handler != nullptr) {
|
2020-11-03 22:39:55 +00:00
|
|
|
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");
|
2022-06-26 03:32:31 +00:00
|
|
|
static std::string reload_command("reload");
|
2022-05-29 22:44:27 +00:00
|
|
|
static std::string tps_command("tps");
|
2020-11-03 22:39:55 +00:00
|
|
|
static std::string stop_command("stop");
|
|
|
|
static std::string help_command("help");
|
2021-03-06 20:56:05 +00:00
|
|
|
if (!is_whitelist() && data.rfind(ban_command, 0) == 0) {
|
2020-11-03 22:39:55 +00:00
|
|
|
// IP-Ban Target Username
|
|
|
|
std::string ban_username = data.substr(ban_command.length());
|
2020-11-04 00:31:27 +00:00
|
|
|
find_players(minecraft, ban_username, ban_callback, false);
|
2022-06-26 03:32:31 +00:00
|
|
|
} else if (data == reload_command) {
|
|
|
|
INFO("Reloading %s", is_whitelist() ? "Whitelist" : "Blacklist");
|
2024-04-02 23:22:01 +00:00
|
|
|
is_ip_in_blacklist(nullptr);
|
2020-11-03 22:39:55 +00:00
|
|
|
} else if (data.rfind(kill_command, 0) == 0) {
|
|
|
|
// Kill Target Username
|
|
|
|
std::string kill_username = data.substr(kill_command.length());
|
2020-11-04 00:31:27 +00:00
|
|
|
find_players(minecraft, kill_username, kill_callback, false);
|
2020-11-03 22:39:55 +00:00
|
|
|
} else if (data.rfind(say_command, 0) == 0) {
|
|
|
|
// Format Message
|
|
|
|
std::string message = "[Server] " + data.substr(say_command.length());
|
2022-07-20 06:58:14 +00:00
|
|
|
char *safe_message = to_cp437(message.c_str());
|
2024-01-06 23:03:48 +00:00
|
|
|
std::string cpp_string = safe_message;
|
2020-11-03 22:39:55 +00:00
|
|
|
// Post Message To Chat
|
2024-05-15 09:02:19 +00:00
|
|
|
server_side_network_handler->displayGameMessage(&cpp_string);
|
2022-07-20 06:58:14 +00:00
|
|
|
// Free
|
|
|
|
free(safe_message);
|
2020-11-03 22:39:55 +00:00
|
|
|
} else if (data == list_command) {
|
|
|
|
// List Players
|
2022-04-15 01:12:42 +00:00
|
|
|
INFO("All Players:");
|
2020-11-04 00:31:27 +00:00
|
|
|
find_players(minecraft, "", list_callback, true);
|
2022-05-29 22:44:27 +00:00
|
|
|
} else if (data == tps_command) {
|
|
|
|
// Print TPS
|
|
|
|
INFO("TPS: %f", tps);
|
2020-11-03 22:39:55 +00:00
|
|
|
} else if (data == stop_command) {
|
|
|
|
// Stop Server
|
2021-06-17 21:32:24 +00:00
|
|
|
compat_request_exit();
|
2020-11-03 22:39:55 +00:00
|
|
|
} else if (data == help_command) {
|
2022-04-15 01:12:42 +00:00
|
|
|
INFO("All Commands:");
|
2021-03-06 20:56:05 +00:00
|
|
|
if (!is_whitelist()) {
|
2022-04-15 01:12:42 +00:00
|
|
|
INFO(" ban <Username> - IP-Ban All Players With Specifed Username");
|
2021-03-06 20:56:05 +00:00
|
|
|
}
|
2022-06-26 03:32:31 +00:00
|
|
|
INFO(" reload - Reload The %s", is_whitelist() ? "Whitelist" : "Blacklist");
|
2022-04-15 01:12:42 +00:00
|
|
|
INFO(" kill <Username> - Kill All Players With Specifed Username");
|
|
|
|
INFO(" say <Message> - Print Specified Message To Chat");
|
|
|
|
INFO(" list - List All Players");
|
2022-05-29 22:44:27 +00:00
|
|
|
INFO(" tps - Print TPS");
|
2022-04-15 01:12:42 +00:00
|
|
|
INFO(" stop - Stop Server");
|
|
|
|
INFO(" help - Print This Message");
|
2020-11-03 22:39:55 +00:00
|
|
|
} else {
|
|
|
|
INFO("Invalid Command: %s", data.c_str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-17 21:32:24 +00:00
|
|
|
// Free
|
2020-11-03 22:39:55 +00:00
|
|
|
free((void *) stdin_buffer);
|
2024-04-02 23:22:01 +00:00
|
|
|
stdin_buffer = nullptr;
|
2020-11-03 22:39:55 +00:00
|
|
|
}
|
|
|
|
stdin_buffer_complete = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Runs Every Tick
|
|
|
|
static bool loaded = false;
|
2024-01-06 23:03:48 +00:00
|
|
|
static void Minecraft_update_injection(Minecraft *minecraft) {
|
2020-11-03 22:39:55 +00:00
|
|
|
// Create/Start World
|
|
|
|
if (!loaded) {
|
|
|
|
start_world(minecraft);
|
|
|
|
loaded = true;
|
|
|
|
}
|
|
|
|
|
2021-06-17 21:32:24 +00:00
|
|
|
// Handle --only-generate
|
2024-05-15 09:02:19 +00:00
|
|
|
if (only_generate && minecraft->isLevelGenerated()) {
|
2021-06-17 21:32:24 +00:00
|
|
|
// Request Exit
|
|
|
|
compat_request_exit();
|
|
|
|
// Disable Special Behavior After Requesting Exit
|
|
|
|
only_generate = false;
|
|
|
|
}
|
|
|
|
|
2020-11-03 22:39:55 +00:00
|
|
|
// Handle Commands
|
|
|
|
handle_commands(minecraft);
|
2020-11-04 21:05:31 +00:00
|
|
|
|
|
|
|
// Server Stop
|
|
|
|
handle_server_stop(minecraft);
|
2020-11-03 22:39:55 +00:00
|
|
|
}
|
|
|
|
|
2022-06-26 03:32:31 +00:00
|
|
|
// Check Blacklist/Whitelist
|
|
|
|
static bool is_ip_in_blacklist(const char *ip) {
|
|
|
|
static std::vector<std::string> ips;
|
2024-04-02 23:22:01 +00:00
|
|
|
if (ip == nullptr) {
|
2022-06-26 03:32:31 +00:00
|
|
|
// Reload
|
|
|
|
ips.clear();
|
|
|
|
// Check banned-ips.txt
|
|
|
|
std::string blacklist_file_path = get_blacklist_file();
|
|
|
|
std::ifstream blacklist_file(blacklist_file_path);
|
|
|
|
if (blacklist_file) {
|
|
|
|
if (blacklist_file.good()) {
|
|
|
|
std::string line;
|
|
|
|
while (std::getline(blacklist_file, line)) {
|
|
|
|
// Check Line
|
|
|
|
if (line.length() > 0 && line[0] != '#') {
|
|
|
|
ips.push_back(line);
|
2020-11-03 22:39:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-06-26 03:32:31 +00:00
|
|
|
if (blacklist_file.is_open()) {
|
|
|
|
blacklist_file.close();
|
|
|
|
}
|
2021-03-06 20:56:05 +00:00
|
|
|
} else {
|
2022-06-26 03:32:31 +00:00
|
|
|
ERR("Unable To Read Blacklist/Whitelist");
|
2020-11-10 00:49:41 +00:00
|
|
|
}
|
2022-06-26 03:32:31 +00:00
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
// Check List
|
2022-07-14 03:35:05 +00:00
|
|
|
for (std::string &x : ips) {
|
2022-06-26 03:32:31 +00:00
|
|
|
if (x.compare(ip) == 0) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ban Players
|
2024-05-05 00:46:15 +00:00
|
|
|
static bool RakNet_RakPeer_IsBanned_injection(__attribute__((unused)) RakNet_RakPeer_IsBanned_t original, __attribute__((unused)) RakNet_RakPeer *rakpeer, const char *ip) {
|
2022-06-26 03:32:31 +00:00
|
|
|
// Check List
|
|
|
|
bool ret = is_ip_in_blacklist(ip);
|
|
|
|
if (is_whitelist()) {
|
|
|
|
return !ret;
|
2020-11-10 00:49:41 +00:00
|
|
|
} else {
|
2022-06-26 03:32:31 +00:00
|
|
|
return ret;
|
2020-10-10 23:02:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-28 18:04:05 +00:00
|
|
|
// Log IPs
|
2024-01-06 23:03:48 +00:00
|
|
|
static Player *ServerSideNetworkHandler_onReady_ClientGeneration_ServerSideNetworkHandler_popPendingPlayer_injection(ServerSideNetworkHandler *server_side_network_handler, RakNet_RakNetGUID *guid) {
|
2021-09-28 18:04:05 +00:00
|
|
|
// Call Original Method
|
2024-05-15 09:02:19 +00:00
|
|
|
Player *player = server_side_network_handler->popPendingPlayer(guid);
|
2021-09-28 18:04:05 +00:00
|
|
|
|
|
|
|
// Check If Player Is Null
|
2024-04-02 23:22:01 +00:00
|
|
|
if (player != nullptr) {
|
2021-09-28 18:04:05 +00:00
|
|
|
// Get Data
|
2024-01-06 23:03:48 +00:00
|
|
|
std::string *username = &player->username;
|
|
|
|
Minecraft *minecraft = server_side_network_handler->minecraft;
|
|
|
|
RakNet_RakPeer *rak_peer = get_rak_peer(minecraft);
|
2021-09-28 18:04:05 +00:00
|
|
|
char *ip = get_rak_net_guid_ip(rak_peer, *guid);
|
|
|
|
|
|
|
|
// Log
|
2021-12-01 02:54:43 +00:00
|
|
|
INFO("%s Has Joined (IP: %s)", username->c_str(), ip);
|
2021-09-28 18:04:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Return
|
|
|
|
return player;
|
|
|
|
}
|
|
|
|
|
2022-04-13 00:38:44 +00:00
|
|
|
// Get MOTD
|
|
|
|
static std::string get_motd() {
|
|
|
|
std::string motd(get_server_properties().get_string("motd", DEFAULT_MOTD));
|
|
|
|
return motd;
|
|
|
|
}
|
|
|
|
|
2021-03-06 20:56:05 +00:00
|
|
|
// Get Feature Flags
|
2020-12-01 17:02:48 +00:00
|
|
|
static bool loaded_features = false;
|
2020-12-02 23:18:49 +00:00
|
|
|
static const char *get_features() {
|
2020-12-01 17:02:48 +00:00
|
|
|
static std::string features;
|
|
|
|
if (!loaded_features) {
|
|
|
|
loaded_features = true;
|
|
|
|
|
2021-07-04 23:02:45 +00:00
|
|
|
features.clear();
|
2021-07-05 01:23:12 +00:00
|
|
|
if (get_server_properties().get_bool("force-mob-spawning", DEFAULT_FORCE_MOB_SPAWNING)) {
|
|
|
|
features += "Force Mob Spawning|";
|
|
|
|
}
|
2022-04-10 00:06:44 +00:00
|
|
|
if (get_server_properties().get_bool("death-messages", DEFAULT_DEATH_MESSAGES)) {
|
|
|
|
features += "Implement Death Messages|";
|
|
|
|
}
|
2023-02-25 05:26:45 +00:00
|
|
|
if (get_server_properties().get_bool("generate-caves", DEFAULT_GENERATE_CAVES)) {
|
|
|
|
features += "Generate Caves|";
|
|
|
|
}
|
2020-12-01 17:02:48 +00:00
|
|
|
}
|
|
|
|
return features.c_str();
|
2020-10-10 23:02:13 +00:00
|
|
|
}
|
2020-12-01 17:02:48 +00:00
|
|
|
|
2021-03-06 20:56:05 +00:00
|
|
|
// Get Max Players
|
2020-12-01 17:02:48 +00:00
|
|
|
static unsigned char get_max_players() {
|
2020-10-11 19:38:48 +00:00
|
|
|
int val = get_server_properties().get_int("max-players", DEFAULT_MAX_PLAYERS);
|
|
|
|
if (val < 0) {
|
|
|
|
val = 0;
|
|
|
|
}
|
|
|
|
if (val > 255) {
|
|
|
|
val = 255;
|
|
|
|
}
|
|
|
|
return (unsigned char) val;
|
|
|
|
}
|
2020-10-10 23:02:13 +00:00
|
|
|
|
2022-09-21 21:34:19 +00:00
|
|
|
// Real Init Server
|
2020-12-02 23:18:49 +00:00
|
|
|
static void server_init() {
|
2020-10-10 23:02:13 +00:00
|
|
|
// Open Properties File
|
2021-06-28 02:16:37 +00:00
|
|
|
std::string file(home_get());
|
2021-06-17 21:32:24 +00:00
|
|
|
file.append("/server.properties");
|
2020-10-10 23:02:13 +00:00
|
|
|
std::ifstream properties_file(file);
|
|
|
|
|
2021-11-14 04:29:48 +00:00
|
|
|
// Check Properties File
|
2021-06-28 02:16:37 +00:00
|
|
|
if (!properties_file.good()) {
|
2020-10-10 23:02:13 +00:00
|
|
|
// Write Defaults
|
|
|
|
std::ofstream properties_file_output(file);
|
2022-04-13 00:38:44 +00:00
|
|
|
properties_file_output << "# Message Of The Day\n";
|
|
|
|
properties_file_output << "motd=" DEFAULT_MOTD "\n";
|
2020-11-10 20:16:42 +00:00
|
|
|
properties_file_output << "# Show The MineCon Badge Next To MOTD In Server List\n";
|
|
|
|
properties_file_output << "show-minecon-badge=" DEFAULT_SHOW_MINECON_BADGE "\n";
|
2020-10-14 17:40:32 +00:00
|
|
|
properties_file_output << "# Game Mode (0 = Survival, 1 = Creative)\n";
|
2020-10-10 23:02:13 +00:00
|
|
|
properties_file_output << "game-mode=" DEFAULT_GAME_MODE "\n";
|
2020-10-14 17:40:32 +00:00
|
|
|
properties_file_output << "# Port\n";
|
2020-10-10 23:02:13 +00:00
|
|
|
properties_file_output << "port=" DEFAULT_PORT "\n";
|
2020-10-14 17:40:32 +00:00
|
|
|
properties_file_output << "# World Seed (Blank = Random Seed)\n";
|
2020-10-10 23:02:13 +00:00
|
|
|
properties_file_output << "seed=" DEFAULT_SEED "\n";
|
2021-07-04 23:02:45 +00:00
|
|
|
properties_file_output << "# Force Mob Spawning (false = Disabled, true = Enabled)\n";
|
|
|
|
properties_file_output << "force-mob-spawning=" DEFAULT_FORCE_MOB_SPAWNING "\n";
|
2022-05-11 22:24:03 +00:00
|
|
|
properties_file_output << "# Peaceful Mode (false = Disabled, true = Enabled)\n";
|
|
|
|
properties_file_output << "peaceful-mode=" DEFAULT_PEACEFUL_MODE "\n";
|
2020-10-14 17:40:32 +00:00
|
|
|
properties_file_output << "# World To Select\n";
|
2020-10-10 23:02:13 +00:00
|
|
|
properties_file_output << "world-name=" DEFAULT_WORLD_NAME "\n";
|
2020-10-15 03:23:31 +00:00
|
|
|
properties_file_output << "# Maximum Player Count\n";
|
2020-10-11 19:38:48 +00:00
|
|
|
properties_file_output << "max-players=" DEFAULT_MAX_PLAYERS "\n";
|
2021-03-06 20:56:05 +00:00
|
|
|
properties_file_output << "# Enable Whitelist\n";
|
|
|
|
properties_file_output << "whitelist=" DEFAULT_WHITELIST "\n";
|
2022-04-10 00:06:44 +00:00
|
|
|
properties_file_output << "# Enable Death Messages\n";
|
|
|
|
properties_file_output << "death-messages=" DEFAULT_DEATH_MESSAGES "\n";
|
2023-02-25 05:26:45 +00:00
|
|
|
properties_file_output << "# Generate Caves\n";
|
|
|
|
properties_file_output << "generate-caves=" DEFAULT_GENERATE_CAVES "\n";
|
2020-10-10 23:02:13 +00:00
|
|
|
properties_file_output.close();
|
|
|
|
// Re-Open File
|
|
|
|
properties_file = std::ifstream(file);
|
|
|
|
}
|
|
|
|
|
2021-06-28 02:16:37 +00:00
|
|
|
// Check Properties File
|
2020-10-10 23:02:13 +00:00
|
|
|
if (!properties_file.is_open()) {
|
2021-06-28 02:16:37 +00:00
|
|
|
ERR("Unable To Open %s", file.c_str());
|
2020-10-10 23:02:13 +00:00
|
|
|
}
|
|
|
|
// Load Properties
|
|
|
|
get_server_properties().load(properties_file);
|
2021-03-06 20:56:05 +00:00
|
|
|
// Close Properties File
|
2020-10-10 23:02:13 +00:00
|
|
|
properties_file.close();
|
|
|
|
|
2021-03-06 20:56:05 +00:00
|
|
|
// Create Empty Blacklist/Whitelist File
|
|
|
|
std::string blacklist_file_path = get_blacklist_file();
|
|
|
|
std::ifstream blacklist_file(blacklist_file_path);
|
2021-06-28 02:16:37 +00:00
|
|
|
if (!blacklist_file.good()) {
|
2020-11-03 22:39:55 +00:00
|
|
|
// Write Default
|
2021-03-06 20:56:05 +00:00
|
|
|
std::ofstream blacklist_output(blacklist_file_path);
|
|
|
|
blacklist_output << "# Blacklist/Whitelist; Each Line Is One IP Address\n";
|
|
|
|
blacklist_output.close();
|
2020-11-03 22:39:55 +00:00
|
|
|
}
|
2021-03-06 20:56:05 +00:00
|
|
|
if (blacklist_file.is_open()) {
|
|
|
|
blacklist_file.close();
|
2020-11-03 22:39:55 +00:00
|
|
|
}
|
2022-06-26 03:32:31 +00:00
|
|
|
// Load Blacklist/Whitelist
|
2024-04-02 23:22:01 +00:00
|
|
|
is_ip_in_blacklist(nullptr);
|
2020-11-03 22:39:55 +00:00
|
|
|
|
2020-10-10 23:02:13 +00:00
|
|
|
// Prevent Main Player From Loading
|
2021-03-05 00:27:24 +00:00
|
|
|
unsigned char player_patch[4] = {0x00, 0x20, 0xa0, 0xe3}; // "mov r2, #0x0"
|
2020-10-10 23:02:13 +00:00
|
|
|
patch((void *) 0x1685c, player_patch);
|
|
|
|
// Start World On Launch
|
2021-11-14 04:29:48 +00:00
|
|
|
misc_run_on_update(Minecraft_update_injection);
|
2020-10-11 19:38:48 +00:00
|
|
|
// Set Max Players
|
2021-03-05 00:27:24 +00:00
|
|
|
unsigned char max_players_patch[4] = {get_max_players(), 0x30, 0xa0, 0xe3}; // "mov r3, #MAX_PLAYERS"
|
2020-10-11 19:38:48 +00:00
|
|
|
patch((void *) 0x166d0, max_players_patch);
|
2020-11-03 22:39:55 +00:00
|
|
|
// Custom Banned IP List
|
2024-05-24 08:44:53 +00:00
|
|
|
overwrite_calls(RakNet_RakPeer_IsBanned, RakNet_RakPeer_IsBanned_injection);
|
2020-10-31 01:31:55 +00:00
|
|
|
|
2021-06-17 21:32:24 +00:00
|
|
|
// Show The MineCon Icon Next To MOTD In Server List
|
2020-11-10 20:16:42 +00:00
|
|
|
if (get_server_properties().get_bool("show-minecon-badge", DEFAULT_SHOW_MINECON_BADGE)) {
|
2021-03-05 00:27:24 +00:00
|
|
|
unsigned char minecon_badge_patch[4] = {0x04, 0x1a, 0x9f, 0xe5}; // "ldr r1, [0x741f0]"
|
2020-11-10 20:16:42 +00:00
|
|
|
patch((void *) 0x737e4, minecon_badge_patch);
|
2020-11-05 01:12:48 +00:00
|
|
|
}
|
|
|
|
|
2021-09-28 18:04:05 +00:00
|
|
|
// Log IPs
|
|
|
|
overwrite_call((void *) 0x75e54, (void *) ServerSideNetworkHandler_onReady_ClientGeneration_ServerSideNetworkHandler_popPendingPlayer_injection);
|
|
|
|
|
2022-05-29 22:44:27 +00:00
|
|
|
// Track TPS
|
|
|
|
misc_run_on_tick(Minecraft_tick_injection);
|
|
|
|
|
2020-10-31 01:31:55 +00:00
|
|
|
// Start Reading STDIN
|
|
|
|
pthread_t read_stdin_thread_obj;
|
2024-04-02 23:22:01 +00:00
|
|
|
pthread_create(&read_stdin_thread_obj, nullptr, read_stdin_thread, nullptr);
|
2020-10-10 23:02:13 +00:00
|
|
|
}
|
2020-12-02 23:18:49 +00:00
|
|
|
|
2021-06-17 21:32:24 +00:00
|
|
|
// Init Server
|
2020-12-02 23:18:49 +00:00
|
|
|
void init_server() {
|
2021-06-17 21:32:24 +00:00
|
|
|
server_init();
|
2022-07-20 06:58:14 +00:00
|
|
|
set_and_print_env("MCPI_FEATURE_FLAGS", get_features());
|
|
|
|
set_and_print_env("MCPI_RENDER_DISTANCE", "Tiny");
|
|
|
|
set_and_print_env("MCPI_USERNAME", get_motd().c_str());
|
2021-06-17 21:32:24 +00:00
|
|
|
}
|