Split Up server.cpp
This commit is contained in:
parent
6f027ba397
commit
14331ce871
@ -86,7 +86,8 @@ add_library(mods SHARED
|
|||||||
src/fps/fps.cpp
|
src/fps/fps.cpp
|
||||||
# server
|
# server
|
||||||
src/server/server.cpp
|
src/server/server.cpp
|
||||||
src/server/server_properties.cpp
|
src/server/commands.cpp
|
||||||
|
src/server/properties.cpp
|
||||||
# multiplayer
|
# multiplayer
|
||||||
src/multiplayer/multiplayer.cpp
|
src/multiplayer/multiplayer.cpp
|
||||||
# benchmark
|
# benchmark
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#include <symbols/minecraft.h>
|
#include <symbols/minecraft.h>
|
||||||
|
|
||||||
#include "server_properties.h"
|
#include "properties.h"
|
||||||
|
|
||||||
struct ServerCommand {
|
struct ServerCommand {
|
||||||
const std::string name;
|
const std::string name;
|
||||||
|
246
mods/src/server/commands.cpp
Normal file
246
mods/src/server/commands.cpp
Normal file
@ -0,0 +1,246 @@
|
|||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
#include <libreborn/log.h>
|
||||||
|
#include <libreborn/util/string.h>
|
||||||
|
|
||||||
|
#include <symbols/minecraft.h>
|
||||||
|
|
||||||
|
#include <mods/misc/misc.h>
|
||||||
|
#include <mods/compat/compat.h>
|
||||||
|
#include <mods/fps/fps.h>
|
||||||
|
#include <mods/server/server.h>
|
||||||
|
|
||||||
|
#include "internal.h"
|
||||||
|
|
||||||
|
// Get Vector Of Players In Level
|
||||||
|
static std::vector<Player *> get_players_in_level(Level *level) {
|
||||||
|
return level->players;
|
||||||
|
}
|
||||||
|
// Get Level From Minecraft
|
||||||
|
static Level *get_level(const Minecraft *minecraft) {
|
||||||
|
return minecraft->level;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find Players With Username And Run Callback
|
||||||
|
typedef void (*player_callback_t)(Minecraft *minecraft, const std::string &username, Player *player);
|
||||||
|
static void find_players(Minecraft *minecraft, const std::string &target_username, const player_callback_t callback, const bool all_players) {
|
||||||
|
Level *level = get_level(minecraft);
|
||||||
|
const std::vector<Player *> players = get_players_in_level(level);
|
||||||
|
bool found_player = false;
|
||||||
|
for (Player *player : players) {
|
||||||
|
// Iterate Players
|
||||||
|
std::string username = misc_get_player_username_utf(player);
|
||||||
|
if (all_players || username == target_username) {
|
||||||
|
// Run Callback
|
||||||
|
callback(minecraft, username, player);
|
||||||
|
found_player = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!all_players && !found_player) {
|
||||||
|
INFO("Invalid Player: %s", target_username.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get IP From Player
|
||||||
|
static char *get_player_ip(const Minecraft *minecraft, Player *player) {
|
||||||
|
RakNet_RakPeer *rak_peer = minecraft->rak_net_instance->peer;
|
||||||
|
const RakNet_RakNetGUID guid = ((ServerPlayer *) player)->guid;
|
||||||
|
// Return
|
||||||
|
return get_rak_net_guid_ip(rak_peer, guid);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ban Player
|
||||||
|
static void ban_callback(Minecraft *minecraft, const std::string &username, Player *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 blacklist_output(get_blacklist_file(), std::ios_base::app);
|
||||||
|
if (blacklist_output) {
|
||||||
|
if (blacklist_output.good()) {
|
||||||
|
blacklist_output << "# " << username << '\n' << ip << '\n';
|
||||||
|
}
|
||||||
|
if (blacklist_output.is_open()) {
|
||||||
|
blacklist_output.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Reload
|
||||||
|
is_ip_in_blacklist(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kill Player
|
||||||
|
static void kill_callback(__attribute__((unused)) Minecraft *minecraft, __attribute__((unused)) const std::string &username, Player *player) {
|
||||||
|
player->hurt(nullptr, INT32_MAX);
|
||||||
|
INFO("Killed: %s", username.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
// List Player
|
||||||
|
static void list_callback(Minecraft *minecraft, const std::string &username, Player *player) {
|
||||||
|
INFO(" - %s (%s)", username.c_str(), get_player_ip(minecraft, player));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read STDIN Thread
|
||||||
|
static pthread_t read_stdin_thread_obj;
|
||||||
|
static volatile bool stdin_line_ready = false;
|
||||||
|
static std::string stdin_line;
|
||||||
|
static void *read_stdin_thread(__attribute__((unused)) void *data) {
|
||||||
|
// Loop
|
||||||
|
char *line = nullptr;
|
||||||
|
size_t len = 0;
|
||||||
|
while (getline(&line, &len, stdin) != -1) {
|
||||||
|
stdin_line = line;
|
||||||
|
stdin_line_ready = true;
|
||||||
|
// Wait For Line To Be Read
|
||||||
|
while (stdin_line_ready) {}
|
||||||
|
}
|
||||||
|
free(line);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
void start_reading_commands() {
|
||||||
|
pthread_create(&read_stdin_thread_obj, nullptr, read_stdin_thread, nullptr);
|
||||||
|
}
|
||||||
|
void stop_reading_commands() {
|
||||||
|
pthread_cancel(read_stdin_thread_obj);
|
||||||
|
pthread_join(read_stdin_thread_obj, nullptr);
|
||||||
|
stdin_line_ready = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle Commands
|
||||||
|
bool ServerCommand::has_args() const {
|
||||||
|
return name[name.length() - 1] == ' ';
|
||||||
|
}
|
||||||
|
std::string ServerCommand::get_lhs_help() const {
|
||||||
|
std::string out;
|
||||||
|
out.append(4, ' ');
|
||||||
|
out += name;
|
||||||
|
if (has_args()) {
|
||||||
|
out += "<Arguments>";
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
std::string ServerCommand::get_full_help(const int max_lhs_length) const {
|
||||||
|
std::string out = get_lhs_help();
|
||||||
|
out.append(max_lhs_length - out.length(), ' ');
|
||||||
|
out += " - ";
|
||||||
|
out += comment;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
std::vector<ServerCommand> *server_get_commands(Minecraft *minecraft, ServerSideNetworkHandler *server_side_network_handler) {
|
||||||
|
std::vector<ServerCommand> *commands = new std::vector<ServerCommand>;
|
||||||
|
// Ban Player
|
||||||
|
if (!is_whitelist()) {
|
||||||
|
commands->push_back({
|
||||||
|
.name = "ban ",
|
||||||
|
.comment = "IP-Ban All Players With Specified Username",
|
||||||
|
.callback = [minecraft](const std::string &cmd) {
|
||||||
|
find_players(minecraft, cmd, ban_callback, false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Reload White/Blacklist
|
||||||
|
commands->push_back({
|
||||||
|
.name = "reload",
|
||||||
|
.comment = std::string("Reload The ") + (is_whitelist() ? "Whitelist" : "Blacklist"),
|
||||||
|
.callback = [](__attribute__((unused)) const std::string &cmd) {
|
||||||
|
INFO("Reloading %s", is_whitelist() ? "Whitelist" : "Blacklist");
|
||||||
|
is_ip_in_blacklist(nullptr);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Kill Player
|
||||||
|
commands->push_back({
|
||||||
|
.name = "kill ",
|
||||||
|
.comment = "Kill All Players With Specified Username",
|
||||||
|
.callback = [minecraft](const std::string &cmd) {
|
||||||
|
find_players(minecraft, cmd, kill_callback, false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Post Message
|
||||||
|
commands->push_back({
|
||||||
|
.name = "say ",
|
||||||
|
.comment = "Print Specified Message To Chat",
|
||||||
|
.callback = [server_side_network_handler](const std::string &cmd) {
|
||||||
|
// Format Message
|
||||||
|
const std::string message = "[Server] " + cmd;
|
||||||
|
std::string cpp_string = to_cp437(message);
|
||||||
|
// Post Message To Chat
|
||||||
|
server_side_network_handler->displayGameMessage(cpp_string);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// List Players
|
||||||
|
commands->push_back({
|
||||||
|
.name = "list",
|
||||||
|
.comment = "List All Players",
|
||||||
|
.callback = [minecraft](__attribute__((unused)) const std::string &cmd) {
|
||||||
|
INFO("All Players:");
|
||||||
|
find_players(minecraft, "", list_callback, true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Ticks-Per-Second
|
||||||
|
commands->push_back({
|
||||||
|
.name = "tps",
|
||||||
|
.comment = "Print TPS",
|
||||||
|
.callback = [](__attribute__((unused)) const std::string &cmd) {
|
||||||
|
INFO("TPS: %f", tps);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Stop
|
||||||
|
commands->push_back({
|
||||||
|
.name = "stop",
|
||||||
|
.comment = "Stop Server",
|
||||||
|
.callback = [](__attribute__((unused)) const std::string &cmd) {
|
||||||
|
compat_request_exit();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Help Page
|
||||||
|
commands->push_back({
|
||||||
|
.name = "help",
|
||||||
|
.comment = "Print This Message",
|
||||||
|
.callback = [commands](__attribute__((unused)) const std::string &cmd) {
|
||||||
|
INFO("All Commands:");
|
||||||
|
std::string::size_type max_lhs_length = 0;
|
||||||
|
for (const ServerCommand &command : *commands) {
|
||||||
|
const std::string::size_type lhs_length = command.get_lhs_help().length();
|
||||||
|
if (lhs_length > max_lhs_length) {
|
||||||
|
max_lhs_length = lhs_length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const ServerCommand &command : *commands) {
|
||||||
|
INFO("%s", command.get_full_help(max_lhs_length).c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Return
|
||||||
|
return commands;
|
||||||
|
}
|
||||||
|
void handle_commands(Minecraft *minecraft) {
|
||||||
|
// Check If Level Is Generated
|
||||||
|
if (minecraft->isLevelGenerated() && stdin_line_ready) {
|
||||||
|
// Read Line
|
||||||
|
std::string data = std::move(stdin_line);
|
||||||
|
data.pop_back(); // Remove Newline
|
||||||
|
stdin_line_ready = false;
|
||||||
|
// Command Ready; Run It
|
||||||
|
ServerSideNetworkHandler *server_side_network_handler = (ServerSideNetworkHandler *) minecraft->network_handler;
|
||||||
|
if (server_side_network_handler != nullptr) {
|
||||||
|
// Generate Command List
|
||||||
|
const std::vector<ServerCommand> *commands = server_get_commands(minecraft, server_side_network_handler);
|
||||||
|
// Run
|
||||||
|
bool success = false;
|
||||||
|
for (const ServerCommand &command : *commands) {
|
||||||
|
const bool valid = command.has_args() ? data.rfind(command.name, 0) == 0 : data == command.name;
|
||||||
|
if (valid) {
|
||||||
|
command.callback(data.substr(command.name.length()));
|
||||||
|
success = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!success) {
|
||||||
|
INFO("Invalid Command: %s", data.c_str());
|
||||||
|
}
|
||||||
|
// Free
|
||||||
|
delete commands;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
mods/src/server/internal.h
Normal file
11
mods/src/server/internal.h
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
__attribute__((visibility("internal"))) bool is_whitelist();
|
||||||
|
__attribute__((visibility("internal"))) std::string get_blacklist_file();
|
||||||
|
__attribute__((visibility("internal"))) bool is_ip_in_blacklist(const char *ip);
|
||||||
|
|
||||||
|
__attribute__((visibility("internal"))) char *get_rak_net_guid_ip(RakNet_RakPeer *rak_peer, const RakNet_RakNetGUID &guid);
|
||||||
|
|
||||||
|
__attribute__((visibility("internal"))) void handle_commands(Minecraft *minecraft);
|
||||||
|
__attribute__((visibility("internal"))) void start_reading_commands();
|
||||||
|
__attribute__((visibility("internal"))) void stop_reading_commands();
|
@ -1,12 +1,14 @@
|
|||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
|
||||||
#include <mods/server/server_properties.h>
|
#include <mods/server/properties.h>
|
||||||
|
|
||||||
|
// Get All Possible Properties
|
||||||
std::vector<const ServerProperty *> &ServerProperty::get_all() {
|
std::vector<const ServerProperty *> &ServerProperty::get_all() {
|
||||||
static std::vector<const ServerProperty *> out;
|
static std::vector<const ServerProperty *> out;
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Load File
|
||||||
void ServerProperties::load(std::istream &stream) {
|
void ServerProperties::load(std::istream &stream) {
|
||||||
std::string line;
|
std::string line;
|
||||||
while (std::getline(stream, line)) {
|
while (std::getline(stream, line)) {
|
||||||
@ -23,10 +25,10 @@ void ServerProperties::load(std::istream &stream) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get Value
|
||||||
std::string ServerProperties::get_string(const ServerProperty &property) const {
|
std::string ServerProperties::get_string(const ServerProperty &property) const {
|
||||||
return properties.contains(property.key) ? properties.at(property.key) : property.def;
|
return properties.contains(property.key) ? properties.at(property.key) : property.def;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ServerProperties::get_int(const ServerProperty &property) const {
|
int ServerProperties::get_int(const ServerProperty &property) const {
|
||||||
try {
|
try {
|
||||||
return std::stoi(get_string(property));
|
return std::stoi(get_string(property));
|
||||||
@ -34,7 +36,6 @@ int ServerProperties::get_int(const ServerProperty &property) const {
|
|||||||
return std::stoi(property.def);
|
return std::stoi(property.def);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool is_true(const std::string &val) {
|
static bool is_true(const std::string &val) {
|
||||||
return val == "true" || val == "yes" || val == "1";
|
return val == "true" || val == "yes" || val == "1";
|
||||||
}
|
}
|
@ -23,7 +23,8 @@
|
|||||||
#include <mods/compat/compat.h>
|
#include <mods/compat/compat.h>
|
||||||
#include <mods/misc/misc.h>
|
#include <mods/misc/misc.h>
|
||||||
#include <mods/game-mode/game-mode.h>
|
#include <mods/game-mode/game-mode.h>
|
||||||
#include <mods/fps/fps.h>
|
|
||||||
|
#include "internal.h"
|
||||||
|
|
||||||
// --only-generate: Ony Generate World And Then Exit
|
// --only-generate: Ony Generate World And Then Exit
|
||||||
static bool only_generate = false;
|
static bool only_generate = false;
|
||||||
@ -56,20 +57,12 @@ static auto &get_property_types() {
|
|||||||
return types;
|
return types;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get World Name
|
|
||||||
static std::string get_world_name() {
|
|
||||||
const std::string name = get_server_properties().get_string(get_property_types().world_name);
|
|
||||||
std::string safe_name = to_cp437(name);
|
|
||||||
return safe_name;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create/Start World
|
// Create/Start World
|
||||||
static void start_world(Minecraft *minecraft) {
|
static void start_world(Minecraft *minecraft) {
|
||||||
// Get World Name
|
// Get World Name
|
||||||
const std::string world_name = get_world_name();
|
std::string world_name = get_server_properties().get_string(get_property_types().world_name);
|
||||||
|
|
||||||
// Log
|
|
||||||
INFO("Loading World: %s", world_name.c_str());
|
INFO("Loading World: %s", world_name.c_str());
|
||||||
|
world_name = to_cp437(world_name);
|
||||||
|
|
||||||
// Peaceful Mode
|
// Peaceful Mode
|
||||||
Options *options = &minecraft->options;
|
Options *options = &minecraft->options;
|
||||||
@ -100,11 +93,11 @@ static void start_world(Minecraft *minecraft) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check If Running In Whitelist Mode
|
// Check If Running In Whitelist Mode
|
||||||
static bool is_whitelist() {
|
bool is_whitelist() {
|
||||||
return get_server_properties().get_bool(get_property_types().enable_whitelist);
|
return get_server_properties().get_bool(get_property_types().enable_whitelist);
|
||||||
}
|
}
|
||||||
// Get Path Of Blacklist (Or Whitelist) File
|
// Get Path Of Blacklist (Or Whitelist) File
|
||||||
static std::string get_blacklist_file() {
|
std::string get_blacklist_file() {
|
||||||
std::string file = home_get();
|
std::string file = home_get();
|
||||||
file += '/';
|
file += '/';
|
||||||
file += is_whitelist() ? "whitelist" : "blacklist";
|
file += is_whitelist() ? "whitelist" : "blacklist";
|
||||||
@ -112,130 +105,18 @@ static std::string get_blacklist_file() {
|
|||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get Vector Of Players In Level
|
|
||||||
static std::vector<Player *> get_players_in_level(Level *level) {
|
|
||||||
return level->players;
|
|
||||||
}
|
|
||||||
// Get Level From Minecraft
|
|
||||||
static Level *get_level(const Minecraft *minecraft) {
|
|
||||||
return minecraft->level;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find Players With Username And Run Callback
|
|
||||||
typedef void (*player_callback_t)(Minecraft *minecraft, const std::string &username, Player *player);
|
|
||||||
static void find_players(Minecraft *minecraft, const std::string &target_username, const player_callback_t callback, const bool all_players) {
|
|
||||||
Level *level = get_level(minecraft);
|
|
||||||
const std::vector<Player *> players = get_players_in_level(level);
|
|
||||||
bool found_player = false;
|
|
||||||
for (Player *player : players) {
|
|
||||||
// Iterate Players
|
|
||||||
std::string username = misc_get_player_username_utf(player);
|
|
||||||
if (all_players || username == target_username) {
|
|
||||||
// Run Callback
|
|
||||||
callback(minecraft, username, player);
|
|
||||||
found_player = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!all_players && !found_player) {
|
|
||||||
INFO("Invalid Player: %s", target_username.c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get RakNet Objects
|
|
||||||
static RakNet_RakNetGUID get_rak_net_guid(Player *player) {
|
|
||||||
return ((ServerPlayer *) player)->guid;
|
|
||||||
}
|
|
||||||
static RakNet_SystemAddress get_system_address(RakNet_RakPeer *rak_peer, RakNet_RakNetGUID guid) {
|
|
||||||
// Get SystemAddress
|
|
||||||
return rak_peer->GetSystemAddressFromGuid(guid);
|
|
||||||
}
|
|
||||||
static RakNet_RakPeer *get_rak_peer(const Minecraft *minecraft) {
|
|
||||||
return minecraft->rak_net_instance->peer;
|
|
||||||
}
|
|
||||||
static char *get_rak_net_guid_ip(RakNet_RakPeer *rak_peer, const RakNet_RakNetGUID &guid) {
|
|
||||||
RakNet_SystemAddress address = get_system_address(rak_peer, guid);
|
|
||||||
// Get IP
|
|
||||||
return address.ToString(false, '|');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get IP From Player
|
|
||||||
static char *get_player_ip(const Minecraft *minecraft, Player *player) {
|
|
||||||
RakNet_RakPeer *rak_peer = get_rak_peer(minecraft);
|
|
||||||
const RakNet_RakNetGUID guid = get_rak_net_guid(player);
|
|
||||||
// Return
|
|
||||||
return get_rak_net_guid_ip(rak_peer, guid);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ban Player
|
|
||||||
static bool is_ip_in_blacklist(const char *ip);
|
|
||||||
static void ban_callback(Minecraft *minecraft, const std::string &username, Player *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 blacklist_output(get_blacklist_file(), std::ios_base::app);
|
|
||||||
if (blacklist_output) {
|
|
||||||
if (blacklist_output.good()) {
|
|
||||||
blacklist_output << "# " << username << '\n' << ip << '\n';
|
|
||||||
}
|
|
||||||
if (blacklist_output.is_open()) {
|
|
||||||
blacklist_output.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Reload
|
|
||||||
is_ip_in_blacklist(nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Kill Player
|
|
||||||
static void kill_callback(__attribute__((unused)) Minecraft *minecraft, __attribute__((unused)) const std::string &username, Player *player) {
|
|
||||||
player->hurt(nullptr, INT32_MAX);
|
|
||||||
INFO("Killed: %s", username.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
// List Player
|
|
||||||
static void list_callback(Minecraft *minecraft, const std::string &username, Player *player) {
|
|
||||||
INFO(" - %s (%s)", username.c_str(), get_player_ip(minecraft, player));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get ServerSideNetworkHandler From Minecraft
|
|
||||||
static ServerSideNetworkHandler *get_server_side_network_handler(const Minecraft *minecraft) {
|
|
||||||
return (ServerSideNetworkHandler *) minecraft->network_handler;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read STDIN Thread
|
|
||||||
static pthread_t read_stdin_thread_obj;
|
|
||||||
static volatile bool stdin_line_ready = false;
|
|
||||||
static std::string stdin_line;
|
|
||||||
static void *read_stdin_thread(__attribute__((unused)) void *data) {
|
|
||||||
// Loop
|
|
||||||
char *line = nullptr;
|
|
||||||
size_t len = 0;
|
|
||||||
while (getline(&line, &len, stdin) != -1) {
|
|
||||||
stdin_line = line;
|
|
||||||
stdin_line_ready = true;
|
|
||||||
// Wait For Line To Be Read
|
|
||||||
while (stdin_line_ready) {}
|
|
||||||
}
|
|
||||||
free(line);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle Server Stop
|
// Handle Server Stop
|
||||||
static void handle_server_stop(Minecraft *minecraft) {
|
static void handle_server_stop(Minecraft *minecraft) {
|
||||||
if (compat_check_exit_requested()) {
|
if (compat_check_exit_requested()) {
|
||||||
INFO("Stopping Server");
|
INFO("Stopping Server");
|
||||||
// Save And Exit
|
// Save And Exit
|
||||||
Level *level = get_level(minecraft);
|
Level *level = minecraft->level;
|
||||||
if (level != nullptr) {
|
if (level != nullptr) {
|
||||||
level->saveLevelData();
|
level->saveLevelData();
|
||||||
}
|
}
|
||||||
minecraft->leaveGame(false);
|
minecraft->leaveGame(false);
|
||||||
// Kill Reader Thread
|
// Kill Reader Thread
|
||||||
pthread_cancel(read_stdin_thread_obj);
|
stop_reading_commands();
|
||||||
pthread_join(read_stdin_thread_obj, nullptr);
|
|
||||||
stdin_line_ready = false;
|
|
||||||
// Stop Game
|
// Stop Game
|
||||||
SDL_Event event;
|
SDL_Event event;
|
||||||
event.type = SDL_QUIT;
|
event.type = SDL_QUIT;
|
||||||
@ -243,144 +124,6 @@ static void handle_server_stop(Minecraft *minecraft) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle Commands
|
|
||||||
bool ServerCommand::has_args() const {
|
|
||||||
return name[name.length() - 1] == ' ';
|
|
||||||
}
|
|
||||||
std::string ServerCommand::get_lhs_help() const {
|
|
||||||
std::string out;
|
|
||||||
out.append(4, ' ');
|
|
||||||
out += name;
|
|
||||||
if (has_args()) {
|
|
||||||
out += "<Arguments>";
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
std::string ServerCommand::get_full_help(const int max_lhs_length) const {
|
|
||||||
std::string out = get_lhs_help();
|
|
||||||
out.append(max_lhs_length - out.length(), ' ');
|
|
||||||
out += " - ";
|
|
||||||
out += comment;
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
std::vector<ServerCommand> *server_get_commands(Minecraft *minecraft, ServerSideNetworkHandler *server_side_network_handler) {
|
|
||||||
std::vector<ServerCommand> *commands = new std::vector<ServerCommand>;
|
|
||||||
// Ban Player
|
|
||||||
if (!is_whitelist()) {
|
|
||||||
commands->push_back({
|
|
||||||
.name = "ban ",
|
|
||||||
.comment = "IP-Ban All Players With Specified Username",
|
|
||||||
.callback = [minecraft](const std::string &cmd) {
|
|
||||||
find_players(minecraft, cmd, ban_callback, false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// Reload White/Blacklist
|
|
||||||
commands->push_back({
|
|
||||||
.name = "reload",
|
|
||||||
.comment = std::string("Reload The ") + (is_whitelist() ? "Whitelist" : "Blacklist"),
|
|
||||||
.callback = [](__attribute__((unused)) const std::string &cmd) {
|
|
||||||
INFO("Reloading %s", is_whitelist() ? "Whitelist" : "Blacklist");
|
|
||||||
is_ip_in_blacklist(nullptr);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// Kill Player
|
|
||||||
commands->push_back({
|
|
||||||
.name = "kill ",
|
|
||||||
.comment = "Kill All Players With Specified Username",
|
|
||||||
.callback = [minecraft](const std::string &cmd) {
|
|
||||||
find_players(minecraft, cmd, kill_callback, false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// Post Message
|
|
||||||
commands->push_back({
|
|
||||||
.name = "say ",
|
|
||||||
.comment = "Print Specified Message To Chat",
|
|
||||||
.callback = [server_side_network_handler](const std::string &cmd) {
|
|
||||||
// Format Message
|
|
||||||
const std::string message = "[Server] " + cmd;
|
|
||||||
std::string cpp_string = to_cp437(message);
|
|
||||||
// Post Message To Chat
|
|
||||||
server_side_network_handler->displayGameMessage(cpp_string);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// List Players
|
|
||||||
commands->push_back({
|
|
||||||
.name = "list",
|
|
||||||
.comment = "List All Players",
|
|
||||||
.callback = [minecraft](__attribute__((unused)) const std::string &cmd) {
|
|
||||||
INFO("All Players:");
|
|
||||||
find_players(minecraft, "", list_callback, true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// Ticks-Per-Second
|
|
||||||
commands->push_back({
|
|
||||||
.name = "tps",
|
|
||||||
.comment = "Print TPS",
|
|
||||||
.callback = [](__attribute__((unused)) const std::string &cmd) {
|
|
||||||
INFO("TPS: %f", tps);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// Stop
|
|
||||||
commands->push_back({
|
|
||||||
.name = "stop",
|
|
||||||
.comment = "Stop Server",
|
|
||||||
.callback = [](__attribute__((unused)) const std::string &cmd) {
|
|
||||||
compat_request_exit();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// Help Page
|
|
||||||
commands->push_back({
|
|
||||||
.name = "help",
|
|
||||||
.comment = "Print This Message",
|
|
||||||
.callback = [commands](__attribute__((unused)) const std::string &cmd) {
|
|
||||||
INFO("All Commands:");
|
|
||||||
std::string::size_type max_lhs_length = 0;
|
|
||||||
for (const ServerCommand &command : *commands) {
|
|
||||||
const std::string::size_type lhs_length = command.get_lhs_help().length();
|
|
||||||
if (lhs_length > max_lhs_length) {
|
|
||||||
max_lhs_length = lhs_length;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (const ServerCommand &command : *commands) {
|
|
||||||
INFO("%s", command.get_full_help(max_lhs_length).c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// Return
|
|
||||||
return commands;
|
|
||||||
}
|
|
||||||
static void handle_commands(Minecraft *minecraft) {
|
|
||||||
// Check If Level Is Generated
|
|
||||||
if (minecraft->isLevelGenerated() && stdin_line_ready) {
|
|
||||||
// Read Line
|
|
||||||
std::string data = std::move(stdin_line);
|
|
||||||
data.pop_back(); // Remove Newline
|
|
||||||
stdin_line_ready = false;
|
|
||||||
// Command Ready; Run It
|
|
||||||
ServerSideNetworkHandler *server_side_network_handler = get_server_side_network_handler(minecraft);
|
|
||||||
if (server_side_network_handler != nullptr) {
|
|
||||||
// Generate Command List
|
|
||||||
const std::vector<ServerCommand> *commands = server_get_commands(minecraft, server_side_network_handler);
|
|
||||||
// Run
|
|
||||||
bool success = false;
|
|
||||||
for (const ServerCommand &command : *commands) {
|
|
||||||
const bool valid = command.has_args() ? data.rfind(command.name, 0) == 0 : data == command.name;
|
|
||||||
if (valid) {
|
|
||||||
command.callback(data.substr(command.name.length()));
|
|
||||||
success = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!success) {
|
|
||||||
INFO("Invalid Command: %s", data.c_str());
|
|
||||||
}
|
|
||||||
// Free
|
|
||||||
delete commands;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Runs Every Tick
|
// Runs Every Tick
|
||||||
static bool loaded = false;
|
static bool loaded = false;
|
||||||
static void Minecraft_update_injection(Minecraft *minecraft) {
|
static void Minecraft_update_injection(Minecraft *minecraft) {
|
||||||
@ -406,7 +149,7 @@ static void Minecraft_update_injection(Minecraft *minecraft) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check Blacklist/Whitelist
|
// Check Blacklist/Whitelist
|
||||||
static bool is_ip_in_blacklist(const char *ip) {
|
bool is_ip_in_blacklist(const char *ip) {
|
||||||
static std::vector<std::string> ips;
|
static std::vector<std::string> ips;
|
||||||
if (ip == nullptr) {
|
if (ip == nullptr) {
|
||||||
// Reload
|
// Reload
|
||||||
@ -449,6 +192,13 @@ static bool RakNet_RakPeer_IsBanned_injection(__attribute__((unused)) RakNet_Rak
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get IP Address
|
||||||
|
char *get_rak_net_guid_ip(RakNet_RakPeer *rak_peer, const RakNet_RakNetGUID &guid) {
|
||||||
|
RakNet_SystemAddress address = rak_peer->GetSystemAddressFromGuid(guid);
|
||||||
|
// Get IP
|
||||||
|
return address.ToString(false, '|');
|
||||||
|
}
|
||||||
|
|
||||||
// Log IPs
|
// Log IPs
|
||||||
static Player *ServerSideNetworkHandler_onReady_ClientGeneration_ServerSideNetworkHandler_popPendingPlayer_injection(ServerSideNetworkHandler *server_side_network_handler, const RakNet_RakNetGUID &guid) {
|
static Player *ServerSideNetworkHandler_onReady_ClientGeneration_ServerSideNetworkHandler_popPendingPlayer_injection(ServerSideNetworkHandler *server_side_network_handler, const RakNet_RakNetGUID &guid) {
|
||||||
// Call Original Method
|
// Call Original Method
|
||||||
@ -459,7 +209,7 @@ static Player *ServerSideNetworkHandler_onReady_ClientGeneration_ServerSideNetwo
|
|||||||
// Get Data
|
// Get Data
|
||||||
const std::string *username = &player->username;
|
const std::string *username = &player->username;
|
||||||
const Minecraft *minecraft = server_side_network_handler->minecraft;
|
const Minecraft *minecraft = server_side_network_handler->minecraft;
|
||||||
RakNet_RakPeer *rak_peer = get_rak_peer(minecraft);
|
RakNet_RakPeer *rak_peer = minecraft->rak_net_instance->peer;
|
||||||
char *ip = get_rak_net_guid_ip(rak_peer, guid);
|
char *ip = get_rak_net_guid_ip(rak_peer, guid);
|
||||||
|
|
||||||
// Log
|
// Log
|
||||||
@ -571,7 +321,7 @@ static void server_init() {
|
|||||||
overwrite_call((void *) 0x75e54, ServerSideNetworkHandler_popPendingPlayer, ServerSideNetworkHandler_onReady_ClientGeneration_ServerSideNetworkHandler_popPendingPlayer_injection);
|
overwrite_call((void *) 0x75e54, ServerSideNetworkHandler_popPendingPlayer, ServerSideNetworkHandler_onReady_ClientGeneration_ServerSideNetworkHandler_popPendingPlayer_injection);
|
||||||
|
|
||||||
// Start Reading STDIN
|
// Start Reading STDIN
|
||||||
pthread_create(&read_stdin_thread_obj, nullptr, read_stdin_thread, nullptr);
|
start_reading_commands();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init Server
|
// Init Server
|
||||||
|
@ -2,6 +2,9 @@
|
|||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
|
# Change Directory
|
||||||
|
cd "$(dirname "$0")/../"
|
||||||
|
|
||||||
# Setup
|
# Setup
|
||||||
export XDG_SESSION_TYPE=x11
|
export XDG_SESSION_TYPE=x11
|
||||||
unset MCPI_GUI_SCALE
|
unset MCPI_GUI_SCALE
|
||||||
|
@ -17,26 +17,23 @@ if [ ! -f "${APPIMAGE}" ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Make Test Directory
|
# Make Test Directory
|
||||||
TEST_WORKING_DIR="$(pwd)/.testing-tmp"
|
export MCPI_PROFILE_DIRECTORY="$(pwd)/.testing-tmp"
|
||||||
rm -rf "${TEST_WORKING_DIR}"
|
rm -rf "${MCPI_PROFILE_DIRECTORY}"
|
||||||
mkdir "${TEST_WORKING_DIR}"
|
mkdir "${MCPI_PROFILE_DIRECTORY}"
|
||||||
ROOT="$(pwd)"
|
|
||||||
cd "${TEST_WORKING_DIR}"
|
|
||||||
|
|
||||||
# Prepare AppImage For Docker
|
# Prepare AppImage For Docker
|
||||||
cp "${APPIMAGE}" tmp.AppImage
|
EXE="$(mktemp)"
|
||||||
"${ROOT}/scripts/fix-appimage-for-docker.sh" tmp.AppImage
|
cp "${APPIMAGE}" "${EXE}"
|
||||||
chmod +x tmp.AppImage
|
./scripts/fix-appimage-for-docker.sh "${EXE}"
|
||||||
|
|
||||||
# Run
|
# Run
|
||||||
if [ "${MODE}" = "server" ]; then
|
if [ "${MODE}" = "server" ]; then
|
||||||
# Server Test
|
# Server Test
|
||||||
./tmp.AppImage --appimage-extract-and-run --server --only-generate
|
"${EXE}" --appimage-extract-and-run --server --only-generate
|
||||||
else
|
else
|
||||||
# Client Test
|
# Client Test
|
||||||
export MCPI_PROFILE_DIRECTORY="${TEST_WORKING_DIR}"
|
"${EXE}" --appimage-extract-and-run --default --no-cache --benchmark --force-headless
|
||||||
./tmp.AppImage --appimage-extract-and-run --default --no-cache --benchmark --force-headless
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Clean Up
|
# Clean Up
|
||||||
rm -rf "${TEST_WORKING_DIR}"
|
rm -f "${EXE}"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user