Skip to content

Commit

Permalink
feat: add CommandSenderWrapper
Browse files Browse the repository at this point in the history
  • Loading branch information
wu-vincent committed Oct 5, 2024
1 parent 23a6373 commit 3ff6d42
Show file tree
Hide file tree
Showing 5 changed files with 144 additions and 5 deletions.
118 changes: 118 additions & 0 deletions include/endstone/command/command_sender_wrapper.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
// Copyright (c) 2024, The Endstone Project. (https://endstone.dev) All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#pragma once

#include "endstone/command/command_sender.h"

namespace endstone {

/**
* @brief Represents a wrapper that forwards commands to the wrapped CommandSender and captures its output
*/
class CommandSenderWrapper final : public CommandSender {
public:
using Callback = std::function<void(const Message &)>;
explicit CommandSenderWrapper(CommandSender &sender, Callback on_message = {}, Callback on_error = {})
: sender_(sender), on_message_(std::move(on_message)), on_error_(std::move(on_error)){};

void sendMessage(const Message &message) const override
{
if (on_message_) {
on_message_(message);
}
}
void sendErrorMessage(const Message &message) const override
{
if (on_error_) {
on_error_(message);
}
}

[[nodiscard]] bool isOp() const override
{
return sender_.isOp();
}
void setOp(bool value) override
{
sender_.setOp(value);
}
[[nodiscard]] bool isPermissionSet(std::string name) const override
{
return sender_.isPermissionSet(name);
}
[[nodiscard]] bool isPermissionSet(const Permission &perm) const override
{
return sender_.isPermissionSet(perm);
}
[[nodiscard]] bool hasPermission(std::string name) const override
{
return sender_.hasPermission(name);
}
[[nodiscard]] bool hasPermission(const Permission &perm) const override
{
return sender_.hasPermission(perm);
}
Result<PermissionAttachment *> addAttachment(Plugin &plugin, const std::string &name, bool value) override
{
return sender_.addAttachment(plugin, name, value);
}
Result<PermissionAttachment *> addAttachment(Plugin &plugin) override
{
return sender_.addAttachment(plugin);
}
Result<void> removeAttachment(PermissionAttachment &attachment) override
{
return sender_.removeAttachment(attachment);
}
void recalculatePermissions() override
{
sender_.recalculatePermissions();
}
[[nodiscard]] std::unordered_set<PermissionAttachmentInfo *> getEffectivePermissions() const override
{
return sender_.getEffectivePermissions();
}
[[nodiscard]] CommandSender *asCommandSender() const override
{
return sender_.asCommandSender();
}
[[nodiscard]] ConsoleCommandSender *asConsole() const override
{
return sender_.asConsole();
}
[[nodiscard]] Actor *asActor() const override
{
return sender_.asActor();
}
[[nodiscard]] Player *asPlayer() const override
{
return sender_.asPlayer();
}
[[nodiscard]] Server &getServer() const override
{
return sender_.getServer();
}
[[nodiscard]] std::string getName() const override
{
return sender_.getName();
}

private:
CommandSender &sender_;
Callback on_message_;
Callback on_error_;
};

} // namespace endstone
2 changes: 1 addition & 1 deletion include/endstone/detail/signal_handler.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ inline void print_frame(std::ostream &stream, bool color, unsigned frame_number_
const auto *green = color ? "\033[32m" : "";
const auto *yellow = color ? "\033[33m" : "";
const auto *blue = color ? "\033[34m" : "";
std::string line = fmt::format("#{:<{}} ", counter, frame_number_width);
std::string line = fmt::format("[{:<{}}] ", counter, frame_number_width);
if (frame.is_inline) {
line += fmt::format("{:<{}}", "(inlined)", 2 * sizeof(cpptrace::frame_ptr) + 2);
}
Expand Down
10 changes: 8 additions & 2 deletions python/src/endstone/_internal/endstone_python.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import numpy
import os
import typing
import uuid
__all__ = ['ActionForm', 'Actor', 'ActorDeathEvent', 'ActorEvent', 'ActorKnockbackEvent', 'ActorRemoveEvent', 'ActorSpawnEvent', 'ActorTeleportEvent', 'BarColor', 'BarFlag', 'BarStyle', 'Block', 'BlockBreakEvent', 'BlockData', 'BlockEvent', 'BlockFace', 'BlockPlaceEvent', 'BlockState', 'BossBar', 'BroadcastMessageEvent', 'ColorFormat', 'Command', 'CommandExecutor', 'CommandSender', 'ConsoleCommandSender', 'Criteria', 'Dimension', 'DisplaySlot', 'Dropdown', 'Event', 'EventPriority', 'GameMode', 'Inventory', 'ItemStack', 'Label', 'Level', 'Location', 'Logger', 'MessageForm', 'Mob', 'ModalForm', 'Objective', 'ObjectiveSortOrder', 'Packet', 'PacketType', 'Permissible', 'Permission', 'PermissionAttachment', 'PermissionAttachmentInfo', 'PermissionDefault', 'Player', 'PlayerChatEvent', 'PlayerCommandEvent', 'PlayerDeathEvent', 'PlayerEvent', 'PlayerInteractActorEvent', 'PlayerInteractEvent', 'PlayerInventory', 'PlayerJoinEvent', 'PlayerKickEvent', 'PlayerLoginEvent', 'PlayerQuitEvent', 'PlayerTeleportEvent', 'Plugin', 'PluginCommand', 'PluginDescription', 'PluginDisableEvent', 'PluginEnableEvent', 'PluginLoadOrder', 'PluginLoader', 'PluginManager', 'Position', 'RenderType', 'Scheduler', 'Score', 'Scoreboard', 'Server', 'ServerCommandEvent', 'ServerListPingEvent', 'ServerLoadEvent', 'Skin', 'Slider', 'SocketAddress', 'SpawnParticleEffectPacket', 'StepSlider', 'Task', 'TextInput', 'ThunderChangeEvent', 'Toggle', 'Translatable', 'Vector', 'WeatherChangeEvent']
__all__ = ['ActionForm', 'Actor', 'ActorDeathEvent', 'ActorEvent', 'ActorKnockbackEvent', 'ActorRemoveEvent', 'ActorSpawnEvent', 'ActorTeleportEvent', 'BarColor', 'BarFlag', 'BarStyle', 'Block', 'BlockBreakEvent', 'BlockData', 'BlockEvent', 'BlockFace', 'BlockPlaceEvent', 'BlockState', 'BossBar', 'BroadcastMessageEvent', 'ColorFormat', 'Command', 'CommandExecutor', 'CommandSender', 'CommandSenderWrapper', 'ConsoleCommandSender', 'Criteria', 'Dimension', 'DisplaySlot', 'Dropdown', 'Event', 'EventPriority', 'GameMode', 'Inventory', 'ItemStack', 'Label', 'Level', 'Location', 'Logger', 'MessageForm', 'Mob', 'ModalForm', 'Objective', 'ObjectiveSortOrder', 'Packet', 'PacketType', 'Permissible', 'Permission', 'PermissionAttachment', 'PermissionAttachmentInfo', 'PermissionDefault', 'Player', 'PlayerChatEvent', 'PlayerCommandEvent', 'PlayerDeathEvent', 'PlayerEvent', 'PlayerInteractActorEvent', 'PlayerInteractEvent', 'PlayerInventory', 'PlayerJoinEvent', 'PlayerKickEvent', 'PlayerLoginEvent', 'PlayerQuitEvent', 'PlayerTeleportEvent', 'Plugin', 'PluginCommand', 'PluginDescription', 'PluginDisableEvent', 'PluginEnableEvent', 'PluginLoadOrder', 'PluginLoader', 'PluginManager', 'Position', 'RenderType', 'Scheduler', 'Score', 'Scoreboard', 'Server', 'ServerCommandEvent', 'ServerListPingEvent', 'ServerLoadEvent', 'Skin', 'Slider', 'SocketAddress', 'SpawnParticleEffectPacket', 'StepSlider', 'Task', 'TextInput', 'ThunderChangeEvent', 'Toggle', 'Translatable', 'Vector', 'WeatherChangeEvent']
class ActionForm:
"""
Represents a form with buttons that let the player take action.
Expand Down Expand Up @@ -751,6 +751,12 @@ class CommandSender(Permissible):
"""
Returns the server instance that this command is running on
"""
class CommandSenderWrapper(CommandSender):
"""
Represents a wrapper class that forwards commands to a CommandSender and captures its output
"""
def __init__(self, sender: CommandSender, on_message: typing.Callable[[str | Translatable], None] = None, on_error: typing.Callable[[str | Translatable], None] = None) -> None:
...
class ConsoleCommandSender(CommandSender):
"""
Represents a console command sender.
Expand Down Expand Up @@ -2585,7 +2591,7 @@ class Server:
"""
Creates a new Scoreboard to be tracked by the server.
"""
def dispatch_command(self, sender: CommandSender, command: str) -> bool:
def dispatch_command(self, sender: CommandSender, command_line: str) -> bool:
"""
Dispatches a command on this server, and executes it if found.
"""
Expand Down
10 changes: 8 additions & 2 deletions python/src/endstone/command.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
from endstone._internal.endstone_python import Command, CommandExecutor, CommandSender, ConsoleCommandSender
from endstone._internal.endstone_python import (
Command,
CommandExecutor,
CommandSender,
CommandSenderWrapper,
ConsoleCommandSender,
)

__all__ = ["Command", "CommandExecutor", "CommandSender", "ConsoleCommandSender"]
__all__ = ["Command", "CommandExecutor", "CommandSender", "CommandSenderWrapper", "ConsoleCommandSender"]
9 changes: 9 additions & 0 deletions src/endstone_python/command.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,13 @@

#include <utility>

#include <pybind11/functional.h>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>

#include "endstone/command/command_executor.h"
#include "endstone/command/command_sender.h"
#include "endstone/command/command_sender_wrapper.h"
#include "endstone/command/console_command_sender.h"
#include "endstone/logger.h"
#include "endstone/plugin/plugin.h"
Expand Down Expand Up @@ -67,6 +69,13 @@ void init_command(py::module &m, py::class_<CommandSender, Permissible> &command
"Returns the server instance that this command is running on")
.def_property_readonly("name", &CommandSender::getName, "Gets the name of this command sender");

py::class_<CommandSenderWrapper, CommandSender>(
m, "CommandSenderWrapper",
"Represents a wrapper that forwards commands to the wrapped CommandSender and captures its output")
.def(py::init<CommandSender &, CommandSenderWrapper::Callback, CommandSenderWrapper::Callback>(),
py::arg("sender"), py::arg("on_message") = CommandSenderWrapper::Callback{},
py::arg("on_error") = CommandSenderWrapper::Callback{});

py::class_<ConsoleCommandSender, CommandSender>(m, "ConsoleCommandSender", "Represents a console command sender.");

py::class_<Command, std::shared_ptr<Command>>(m, "Command",
Expand Down

0 comments on commit 3ff6d42

Please sign in to comment.