From 4023d7068784549bdfb02e9d080feaadfea66ee7 Mon Sep 17 00:00:00 2001 From: Kolyah35 Date: Sat, 4 Apr 2026 01:13:56 +0300 Subject: [PATCH] FEAT: server-side commands --- src/client/gui/screens/ConsoleScreen.cpp | 8 +++++-- src/commands/Command.hpp | 4 ++-- src/commands/CommandHelp.cpp | 28 +++++++++++++++--------- src/commands/CommandHelp.hpp | 4 ++-- src/commands/CommandKick.cpp | 14 ++++++------ src/commands/CommandKick.hpp | 4 ++-- src/commands/CommandManager.cpp | 8 ++++--- src/commands/CommandManager.hpp | 2 +- src/commands/CommandOp.cpp | 14 ++++++------ src/commands/CommandOp.hpp | 4 ++-- src/network/ClientSideNetworkHandler.cpp | 6 ++++- src/network/ServerSideNetworkHandler.cpp | 21 +++++++++++++++++- src/network/ServerSideNetworkHandler.h | 6 +++++ 13 files changed, 83 insertions(+), 40 deletions(-) diff --git a/src/client/gui/screens/ConsoleScreen.cpp b/src/client/gui/screens/ConsoleScreen.cpp index 9821f99..42f47ee 100644 --- a/src/client/gui/screens/ConsoleScreen.cpp +++ b/src/client/gui/screens/ConsoleScreen.cpp @@ -66,7 +66,11 @@ void ConsoleScreen::execute() if (_input[0] == '/') { // Command _input = Util::stringTrim(_input.substr(1)); - minecraft->commandManager().execute(*minecraft, *minecraft->player, _input); + + std::istringstream iss(minecraft->commandManager().execute(*minecraft, *minecraft->player, _input)); + for (std::string line; std::getline(iss, line); ) { + minecraft->gui.addMessage(line); + } } else { // @ai @rewrite if (minecraft->netCallback && minecraft->raknetInstance->isServer()) { @@ -75,7 +79,7 @@ void ConsoleScreen::execute() ChatPacket chatPkt(_input); minecraft->raknetInstance->send(chatPkt); } else { - minecraft->gui.addMessage(_input); + minecraft->gui.addMessage("<" + minecraft->player->name + "> " + _input); } } diff --git a/src/commands/Command.hpp b/src/commands/Command.hpp index f44c3df..fb3fdd0 100644 --- a/src/commands/Command.hpp +++ b/src/commands/Command.hpp @@ -17,8 +17,8 @@ public: bool isPlayerOp(Minecraft& mc, Player& player); - virtual void execute(Minecraft& mc, Player& player, const std::vector& args) = 0; - virtual void printHelp(Minecraft& mc) = 0; + virtual std::string execute(Minecraft& mc, Player& player, const std::vector& args) = 0; + virtual std::string help(Minecraft& mc) = 0; protected: Command(const std::string& name, CommandFlags flags = (CommandFlags)0) : m_name(name), m_flags(flags) {} diff --git a/src/commands/CommandHelp.cpp b/src/commands/CommandHelp.cpp index 18f7741..9f09da4 100644 --- a/src/commands/CommandHelp.cpp +++ b/src/commands/CommandHelp.cpp @@ -2,26 +2,34 @@ #include "commands/Command.hpp" #include "CommandManager.hpp" #include +#include CommandHelp::CommandHelp() : Command("help") {} -void CommandHelp::execute(Minecraft& mc, Player& player, const std::vector& args) { +std::string CommandHelp::execute(Minecraft& mc, Player& player, const std::vector& args) { if (args.empty()) { auto cmds = mc.commandManager().getListAllCommands(); - mc.addMessage("Usage: /help "); - mc.addMessage("List of all commands:"); + std::ostringstream output; + + output << "List of all commands:" << std::endl; for (auto& cmd : cmds) { - mc.addMessage(" - " + cmd); + output << " - " + cmd << std::endl;; } - } else { - Command* cmd = mc.commandManager().getCommand(args[0]); - if (cmd != nullptr) { - cmd->printHelp(mc); - } + return output.str(); } + + Command* cmd = mc.commandManager().getCommand(args[0]); + + if (cmd != nullptr) { + return cmd->help(mc); + } + + return "help: command " + args[0] + " not found"; } -void CommandHelp::printHelp(Minecraft& mc) {} \ No newline at end of file +std::string CommandHelp::help(Minecraft& mc) { + return "Usage: /help "; +} \ No newline at end of file diff --git a/src/commands/CommandHelp.hpp b/src/commands/CommandHelp.hpp index 48e1f1f..4c2349a 100644 --- a/src/commands/CommandHelp.hpp +++ b/src/commands/CommandHelp.hpp @@ -4,6 +4,6 @@ class CommandHelp : public Command { public: CommandHelp(); - void execute(Minecraft& mc, Player& player, const std::vector& args); - void printHelp(Minecraft& mc); + std::string execute(Minecraft& mc, Player& player, const std::vector& args); + std::string help(Minecraft& mc); }; \ No newline at end of file diff --git a/src/commands/CommandKick.cpp b/src/commands/CommandKick.cpp index b08ba86..2dad690 100644 --- a/src/commands/CommandKick.cpp +++ b/src/commands/CommandKick.cpp @@ -9,13 +9,13 @@ CommandKick::CommandKick() : Command("kick") {} -void CommandKick::execute(Minecraft& mc, Player& player, const std::vector& args) { +std::string CommandKick::execute(Minecraft& mc, Player& player, const std::vector& args) { if (!isPlayerOp(mc, player)) { - return mc.addMessage("You aren't enough priveleged to run this command"); + return "You aren't enough priveleged to run this command"; } if (args.empty()) { - return printHelp(mc); + return help(mc); } auto it = std::find_if(mc.level->players.begin(), mc.level->players.end(), [args] (auto& it) -> bool { @@ -23,11 +23,11 @@ void CommandKick::execute(Minecraft& mc, Player& player, const std::vectorplayers.end()) { - return mc.addMessage("kick: can't find player with name " + args[0]); + return "kick: can't find player with name " + args[0]; } if (*it == (Player*)mc.player) { - return mc.addMessage("kick: you can't kick urself lol"); + return "kick: you can't kick urself lol"; } mc.level->removePlayer(*it); @@ -36,6 +36,6 @@ void CommandKick::execute(Minecraft& mc, Player& player, const std::vector"); +std::string CommandKick::help(Minecraft& mc) { + return "Usage: /kick "; } \ No newline at end of file diff --git a/src/commands/CommandKick.hpp b/src/commands/CommandKick.hpp index 80225a8..5b94819 100644 --- a/src/commands/CommandKick.hpp +++ b/src/commands/CommandKick.hpp @@ -4,6 +4,6 @@ class CommandKick : public Command { public: CommandKick(); - void execute(Minecraft& mc, Player& player, const std::vector& args); - void printHelp(Minecraft& mc); + std::string execute(Minecraft& mc, Player& player, const std::vector& args); + std::string help(Minecraft& mc); }; \ No newline at end of file diff --git a/src/commands/CommandManager.cpp b/src/commands/CommandManager.cpp index 2b7f7f0..f92f380 100644 --- a/src/commands/CommandManager.cpp +++ b/src/commands/CommandManager.cpp @@ -33,7 +33,7 @@ std::vector CommandManager::getListAllCommands() { return ret; } -void CommandManager::execute(Minecraft& mc, Player& player, const std::string& input) { +std::string CommandManager::execute(Minecraft& mc, Player& player, const std::string& input) { std::istringstream ss(input); std::string cmd; @@ -44,7 +44,7 @@ void CommandManager::execute(Minecraft& mc, Player& player, const std::string& i }); if (it == m_commands.end()) { - return mc.addMessage("Command /" + cmd + " not found"); + return "Command /" + cmd + " not found"; } std::vector args; @@ -53,11 +53,13 @@ void CommandManager::execute(Minecraft& mc, Player& player, const std::string& i while (ss >> tok) args.push_back(tok); if (!mc.level->isClientSide || (*it)->getFlags() & CommandFlags::COMMAND_FLAG_SINGLEPLAYER_ONLY) { - (*it)->execute(mc, player, args); + return (*it)->execute(mc, player, args); } else { ChatPacket packet("/" + input); mc.raknetInstance->send(packet); } + + return ""; } Command* CommandManager::getCommand(const std::string& name) { diff --git a/src/commands/CommandManager.hpp b/src/commands/CommandManager.hpp index 28817ea..c33b363 100644 --- a/src/commands/CommandManager.hpp +++ b/src/commands/CommandManager.hpp @@ -10,7 +10,7 @@ public: std::vector getListAllCommands(); - void execute(Minecraft& mc, Player& player, const std::string& input); + std::string execute(Minecraft& mc, Player& player, const std::string& input); Command* getCommand(const std::string& name); diff --git a/src/commands/CommandOp.cpp b/src/commands/CommandOp.cpp index b53646e..df28db5 100644 --- a/src/commands/CommandOp.cpp +++ b/src/commands/CommandOp.cpp @@ -9,13 +9,13 @@ CommandOp::CommandOp() : Command("op") {} -void CommandOp::execute(Minecraft& mc, Player& player, const std::vector& args) { +std::string CommandOp::execute(Minecraft& mc, Player& player, const std::vector& args) { if (!isPlayerOp(mc, player)) { - return mc.addMessage("You aren't enough priveleged to run this command"); + return "You aren't enough priveleged to run this command"; } if (args.empty()) { - return printHelp(mc); + return help(mc); } auto it = std::find_if(mc.level->players.begin(), mc.level->players.end(), [args] (auto& it) -> bool { @@ -23,13 +23,13 @@ void CommandOp::execute(Minecraft& mc, Player& player, const std::vectorops.find(args[0]) != mc.level->ops.end()) { - return mc.addMessage("op: player " + args[0] + " already opped"); + return "op: player " + args[0] + " already opped"; } mc.level->ops.emplace((*it)->name); - mc.addMessage("op: successfully opped player " + args[0]); + return "op: successfully opped player " + args[0]; } -void CommandOp::printHelp(Minecraft& mc) { - mc.addMessage("Usage: /op "); +std::string CommandOp::help(Minecraft& mc) { + return "Usage: /op "; } \ No newline at end of file diff --git a/src/commands/CommandOp.hpp b/src/commands/CommandOp.hpp index ef5b30a..e91eea9 100644 --- a/src/commands/CommandOp.hpp +++ b/src/commands/CommandOp.hpp @@ -4,6 +4,6 @@ class CommandOp : public Command { public: CommandOp(); - void execute(Minecraft& mc, Player& player, const std::vector& args); - void printHelp(Minecraft& mc); + std::string execute(Minecraft& mc, Player& player, const std::vector& args); + std::string help(Minecraft& mc); }; \ No newline at end of file diff --git a/src/network/ClientSideNetworkHandler.cpp b/src/network/ClientSideNetworkHandler.cpp index 2ae545a..ecfc12d 100755 --- a/src/network/ClientSideNetworkHandler.cpp +++ b/src/network/ClientSideNetworkHandler.cpp @@ -949,7 +949,11 @@ void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, Containe void ClientSideNetworkHandler::handle( const RakNet::RakNetGUID& source, ChatPacket* packet ) { #ifndef STANDALONE_SERVER - minecraft->gui.displayClientMessage(packet->message); + // minecraft->gui.displayClientMessage(packet->message); + std::istringstream iss(packet->message); + for (std::string line; std::getline(iss, line); ) { + minecraft->gui.addMessage(line); + } #endif } diff --git a/src/network/ServerSideNetworkHandler.cpp b/src/network/ServerSideNetworkHandler.cpp index a069b0f..69793b4 100755 --- a/src/network/ServerSideNetworkHandler.cpp +++ b/src/network/ServerSideNetworkHandler.cpp @@ -5,6 +5,7 @@ #include "../world/entity/player/Inventory.h" #include "../world/Container.h" #include "../world/inventory/BaseContainerMenu.h" +#include "network/packet/ChatPacket.h" #include "network/packet/ContainerSetSlotPacket.h" #include "network/packet/LoginStatusPacket.h" #include "network/packet/MovePlayerPacket.h" @@ -24,6 +25,7 @@ #include "../raknet/PacketPriority.h" #include "platform/log.h" #include "util/Mth.h" +#include "util/StringUtils.h" #include "world/item/ItemInstance.h" #include "world/level/storage/LevelStorage.h" #include "world/phys/Vec3.h" @@ -154,7 +156,18 @@ void ServerSideNetworkHandler::displayGameMessage(const std::string& message) void ServerSideNetworkHandler::handle(const RakNet::RakNetGUID& source, ChatPacket* packet) { - displayGameMessage(packet->message); + auto player = getPlayer(source); + + if (player == nullptr) return; // TODO maybe kick? + + if (packet->message[0] == '/') { + // This is a command + + ChatPacket resp(minecraft->commandManager().execute(*minecraft, *player, Util::stringTrim(packet->message.substr(1)))); + return sendPrivate(resp, source); + } + + displayGameMessage("<" + player->name + "> " + packet->message); } void ServerSideNetworkHandler::onNewClient(const RakNet::RakNetGUID& clientGuid) @@ -1020,3 +1033,9 @@ Player* ServerSideNetworkHandler::getPlayer( const RakNet::RakNetGUID& source ) if (source == level->players[i]->owner) return level->players[i]; return NULL; } + +void ServerSideNetworkHandler::sendPrivate(Packet& packet, const RakNet::RakNetGUID& source) { + RakNet::BitStream bitStream; + packet.write(&bitStream); + rakPeer->Send(&bitStream, HIGH_PRIORITY, RELIABLE_ORDERED, 0, source, false); +} \ No newline at end of file diff --git a/src/network/ServerSideNetworkHandler.h b/src/network/ServerSideNetworkHandler.h index a65585a..1e9585d 100755 --- a/src/network/ServerSideNetworkHandler.h +++ b/src/network/ServerSideNetworkHandler.h @@ -71,6 +71,12 @@ private: * @brief Send packet to all players */ void redistributePacket(Packet* packet, const RakNet::RakNetGUID& fromPlayer); + + /** + * @brief Send packer private with GUID + */ + void sendPrivate(Packet& packet, const RakNet::RakNetGUID& source); + Player* getPlayer(const RakNet::RakNetGUID& source); Minecraft* minecraft;