Skip to content

Commit

Permalink
Add --setrole CLI for server configuration
Browse files Browse the repository at this point in the history
Allows to elevate or degrade the specified player.
  • Loading branch information
SmallJoker committed Oct 21, 2023
1 parent 925ebc3 commit bc5b9d7
Show file tree
Hide file tree
Showing 10 changed files with 152 additions and 66 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,9 @@ Permission/player flag examples:
* `--version` outputs the current game version
* `--unittest` runs the included tests to sanity check
* `--server` starts a server-only instance without GUI

* `--setrole USERNAME ROLE`
* `ROLE` can be one of: `normal`, `moderator`, `admin`.
* Can be executed while a server is already running.

**World import/export**

Expand Down
1 change: 0 additions & 1 deletion src/core/playerflags.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
#include <cstdint>
#include <string>

class Packet;
typedef uint32_t playerflags_t;

struct PlayerFlags {
Expand Down
2 changes: 1 addition & 1 deletion src/gui/gui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ void Gui::connect(SceneConnect *sc)
ClientStartData init;

if (sc->start_localhost) {
m_server = new Server();
m_server = new Server(&m_pending_disconnect);
init.address = "127.0.0.1";
}

Expand Down
161 changes: 113 additions & 48 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "gui/gui.h"
static Gui *my_gui = nullptr;
#endif
#include "server/database_auth.h" // AuthAccount
#include "server/server.h"
#include <string.h> // strcmp
#include "version.h"
Expand All @@ -21,6 +22,8 @@ void sleep_ms(long delay)

void unittest();

extern BlockManager *g_blockmanager;

static bool shutdown_requested = false;
static void exit_cleanup()
{
Expand All @@ -38,38 +41,120 @@ static void sigint_handler(int signal)
exit_cleanup();
}

int main(int argc, char *argv[])
static int run_client()
{
atexit(exit_cleanup);
srand(time(nullptr));
if (!BUILD_CLIENT) {
puts("-!- Client is not available on this build.");
return EXIT_FAILURE;
}

#if BUILD_CLIENT
// This is the instance used by the client and GUI
g_blockmanager = new BlockManager();
g_blockmanager->doPackRegistration();
Gui gui;
my_gui = &gui;
gui.run();
my_gui = nullptr;
#endif

bool run_server = (BUILD_CLIENT == 0);
if (argc >= 2) {
if (strcmp(argv[1], "--version") == 0) {
puts(VERSION_STRING);
return EXIT_SUCCESS;
}
if (strcmp(argv[1], "--unittest") == 0) {
// Depends on BlockManager and ENet
unittest();
return EXIT_SUCCESS;
}
if (strcmp(argv[1], "--server") == 0) {
// Dedicated server
run_server = true;
goto run;
return EXIT_SUCCESS;
}

static int run_server()
{
Server server(&shutdown_requested);
auto t_last = std::chrono::steady_clock::now();
while (!shutdown_requested) {
float dtime;
{
// Measure precise timings
auto t_now = std::chrono::steady_clock::now();
dtime = std::chrono::duration<float>(t_now - t_last).count();
t_last = t_now;
}
puts("-!- Unknown command line option.");

server.step(dtime);
sleep_ms(100);
}

return EXIT_SUCCESS;
}

static int server_setrole(char *username, char *role)
{
AuthAccount::AccountLevel newlevel = AuthAccount::AL_INVALID;

if (strcmp(role, "normal") == 0) {
newlevel = AuthAccount::AL_REGISTERED;
} else if (strcmp(role, "moderator") == 0) {
newlevel = AuthAccount::AL_MODERATOR;
} else if (strcmp(role, "admin") == 0) {
newlevel = AuthAccount::AL_SERVER_ADMIN;
}

if (newlevel == AuthAccount::AL_INVALID) {
puts("-!- Unknown role. Available: normal, moderator, admin");
return EXIT_FAILURE;
}

run:
char *ptr = username;
while (*ptr) {
*ptr = toupper(*ptr);
ptr++;
}

DatabaseAuth auth_db;
if (!auth_db.tryOpen("server_auth.sqlite"))
return EXIT_FAILURE; // logged by Database::ok

AuthAccount acc;
if (!auth_db.load(username, &acc)) {
puts("-!- This account is yet not registered.");
return EXIT_FAILURE;
}

auth_db.enableWAL();
acc.level = newlevel;
if (auth_db.save(acc)) {
puts("--- Role changed successfully!");
return EXIT_SUCCESS;
}

return EXIT_FAILURE; // logged by Database::ok
}

static int parse_args(int argc, char *argv[])
{
if (argc < 2)
return BUILD_CLIENT ? run_client() : run_server();

if (strcmp(argv[1], "--version") == 0) {
puts(VERSION_STRING);
return EXIT_SUCCESS;
}
if (strcmp(argv[1], "--unittest") == 0) {
// Depends on BlockManager and ENet
unittest();
return EXIT_SUCCESS;
}
if (strcmp(argv[1], "--server") == 0) {
// Dedicated server
return run_server();
}
if (strcmp(argv[1], "--setrole") == 0) {
if (argc != 4) {
puts("-!- Missing arguments: --setrole USERNAME ROLE");
return EXIT_FAILURE;
}
return server_setrole(argv[2], argv[3]);
}

puts("-!- Unknown command line option.");
return EXIT_FAILURE;
}

int main(int argc, char *argv[])
{
atexit(exit_cleanup);
srand(time(nullptr));

#ifdef __unix__
struct sigaction act;
Expand All @@ -79,33 +164,13 @@ int main(int argc, char *argv[])
sigaction(SIGTERM, &act, NULL);
#endif

if (run_server) {
Server server;
auto t_last = std::chrono::steady_clock::now();
while (!shutdown_requested) {
float dtime;
{
// Measure precise timings
auto t_now = std::chrono::steady_clock::now();
dtime = std::chrono::duration<float>(t_now - t_last).count();
t_last = t_now;
}

server.step(dtime);
sleep_ms(100);
}

return EXIT_SUCCESS;
}
// Used by Gui, Client and Unittests but not Server.
g_blockmanager = new BlockManager();
g_blockmanager->doPackRegistration();

#if BUILD_CLIENT
Gui gui;
my_gui = &gui;
gui.run();
my_gui = nullptr;
int status = parse_args(argc, argv);

delete g_blockmanager;
#endif

return EXIT_SUCCESS;
return status;
}
7 changes: 6 additions & 1 deletion src/server/database.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,16 @@ bool Database::runCustomQuery(const char *query)
char *errmsg = nullptr;
bool good = ok(query, sqlite3_exec(m_database, query, nullptr, nullptr, &errmsg));
if (!good && errmsg)
printf("\t Message: %s\n", errmsg);
fprintf(stderr, "\t Message: %s\n", errmsg);

return good;
}

bool Database::enableWAL()
{
return runCustomQuery("PRAGMA journal_mode=WAL;");
}

bool Database::ok(const char *where, int status) const
{
if (status == SQLITE_OK || status == SQLITE_DONE)
Expand Down
1 change: 1 addition & 0 deletions src/server/database.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class Database {
virtual void close();

bool runCustomQuery(const char *query);
bool enableWAL();

protected:
Database() {}
Expand Down
2 changes: 1 addition & 1 deletion src/server/database_auth.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ struct AuthLogEntry {
std::string text;
};

class DatabaseAuth : Database {
class DatabaseAuth : public Database {
public:
DatabaseAuth() : Database() {}
~DatabaseAuth();
Expand Down
23 changes: 20 additions & 3 deletions src/server/server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

static uint16_t PACKET_ACTIONS_MAX; // initialized in ctor

Server::Server() :
Server::Server(bool *shutdown_requested) :
Environment(new BlockManager()),
m_chatcmd(this)
{
Expand All @@ -26,23 +26,35 @@ Server::Server() :
m_bmgr->doPackRegistration();

m_con = new Connection(Connection::TYPE_SERVER, "Server");
m_con->listenAsync(*this);
if (!m_con->listenAsync(*this)) {
if (shutdown_requested)
*shutdown_requested = true;
return;
}

{
// Initialize persistent world storage
m_world_db = new DatabaseWorld();
if (!m_world_db->tryOpen("server_worlddata.sqlite")) {
fprintf(stderr, "Failed to open world database!\n");
delete m_world_db;
m_world_db = nullptr;
if (shutdown_requested)
*shutdown_requested = true;
}
}

{
// Initialize auth
m_auth_db = new DatabaseAuth();
if (!m_auth_db->tryOpen("server_auth.sqlite")) {
if (m_auth_db->tryOpen("server_auth.sqlite")) {
m_auth_db->enableWAL();
} else {
fprintf(stderr, "Failed to open auth database!\n");
delete m_auth_db;
m_auth_db = nullptr;
if (shutdown_requested)
*shutdown_requested = true;
}
}

Expand Down Expand Up @@ -83,6 +95,11 @@ Server::~Server()

void Server::step(float dtime)
{
if (m_is_first_step) {
puts("Server: Up and runnning.");
m_is_first_step = false;
}

// maybe run player physics?

// always player lock first, world lock after.
Expand Down
3 changes: 2 additions & 1 deletion src/server/server.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ struct LobbyWorld;

class Server : public Environment, public ChatCommandHandler {
public:
Server();
Server(bool *shutdown_requested = nullptr);
~Server();

void step(float dtime) override;
Expand Down Expand Up @@ -69,6 +69,7 @@ class Server : public Environment, public ChatCommandHandler {
// ----------- Other members -----------
DatabaseAuth *m_auth_db = nullptr;
DatabaseWorld *m_world_db = nullptr;
bool m_is_first_step = true;

std::map<peer_t, Timer> m_deaths;

Expand Down
14 changes: 5 additions & 9 deletions src/server/server_commands.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -433,18 +433,15 @@ playerflags_t Server::mayManipulatePlayer(Player *actor, Player *target)
PlayerFlags flags_a = actor->getFlags();
PlayerFlags flags_t = target->getFlags();

if (actor == target)
flags_t = PlayerFlags();

return flags_a.mayManipulate(flags_t, PlayerFlags::PF_MASK_WORLD | PlayerFlags::PF_MASK_TMP);
}


bool Server::changePlayerFlags(Player *player, std::string msg, bool do_add)
{
const PlayerFlags myflags = player->getFlags();
if (!myflags.check(PlayerFlags::PF_COOWNER)) {
systemChatSend(player, "Insufficient permissions");
return false;
}

auto world = player->getWorld();
auto &meta = world->getMeta();

Expand All @@ -457,7 +454,6 @@ bool Server::changePlayerFlags(Player *player, std::string msg, bool do_add)
PlayerFlags targetflags = meta.getPlayerFlags(playername);
const playerflags_t old_flags = targetflags.flags;


if (!target_player && targetflags.flags == 0) {
systemChatSend(player, "Cannot find player " + playername);
return false;
Expand Down Expand Up @@ -521,10 +517,10 @@ void Server::handlePlayerFlagsChange(Player *player, playerflags_t flags_mask)
}

// Notify the other players
if (flags.flags & flags_mask & PlayerFlags::PF_MASK_SEND_OTHERS) {
if (flags_mask & PlayerFlags::PF_MASK_SEND_OTHERS) {
Packet out;
out.write(Packet2Client::PlayerFlags);
player->writeFlags(out, PlayerFlags::PF_MASK_SEND_OTHERS);
player->writeFlags(out, flags_mask & PlayerFlags::PF_MASK_SEND_OTHERS);
broadcastInWorld(player, 1, out);
}

Expand Down

0 comments on commit bc5b9d7

Please sign in to comment.