Skip to content

Commit

Permalink
Debugconsole and input history improvements (#5944)
Browse files Browse the repository at this point in the history
  • Loading branch information
tothxa committed Aug 26, 2023
1 parent b947960 commit 9bb7f4f
Show file tree
Hide file tree
Showing 31 changed files with 391 additions and 125 deletions.
1 change: 1 addition & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ wl_library(widelands_ball_of_mud
ui_fsmenu
widelands_options
wui
wui_chat_ui
)

wl_library(widelands_options
Expand Down
10 changes: 5 additions & 5 deletions src/game_io/game_interactive_player_packet.cc
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,11 @@ void GameInteractivePlayerPacket::read(FileSystem& fs, Game& game, MapObjectLoad
ibase->map_view()->scroll_to_map_pixel(center_map_pixel, MapView::Transition::Jump);
}
if (ipl != nullptr) { // Not in replays
#ifndef NDEBUG
display_flags |= InteractiveBase::dfDebug;
#else
display_flags &= ~InteractiveBase::dfDebug;
#endif
if (g_allow_script_console) {
display_flags |= InteractiveBase::dfDebug;
} else {
display_flags &= ~InteractiveBase::dfDebug;
}
ipl->set_display_flags(display_flags);
ipl->set_player_number(player_number);
}
Expand Down
20 changes: 17 additions & 3 deletions src/network/gameclient.cc
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,10 @@ void GameClient::run() {
NEVER_HERE();
}

const std::string& GameClient::get_local_playername() const {
return d->localplayername;
}

void GameClient::set_write_replay(bool replay) {
d->should_write_replay = replay;
}
Expand Down Expand Up @@ -502,12 +506,12 @@ void GameClient::set_map(const std::string& /*mapname*/,
// client is not allowed to do this
}

void GameClient::send_cheating_info() {
void GameClient::send_cheating_info(const std::string& code, const std::string& arg2) {
SendPacket packet;
packet.unsigned_8(NETCMD_SYSTEM_MESSAGE_CODE);
packet.string("CHEAT");
packet.string(code);
packet.string(d->localplayername);
packet.string("");
packet.string(arg2);
packet.string("");
d->net->send(packet);
}
Expand Down Expand Up @@ -1145,6 +1149,16 @@ void GameClient::handle_system_message(RecvPacket& packet) {
// c.sender remains empty to indicate a system message
d->chatmessages.push_back(c);
Notifications::publish(c);

if (g_allow_script_console && code == "CLIENT_HAS_JOINED_GAME") {
// Warn others
// TODO(tothxa): It would be better to only broadcast if we are the new user, otherwise send
// it to the new user only, but
// 1. We can only send commands to the host
// 2. System messages are assembled and translated on each client, so
// individual players can't be @-addressed
send_cheating_info("CAN_CHEAT");
}
}

/**
Expand Down
4 changes: 3 additions & 1 deletion src/network/gameclient.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ struct GameClient : public GameController, public GameSettingsProvider, public C
~GameClient() override;

void run();
[[nodiscard]] const std::string& get_local_playername() const;

// GameController interface
void think() override;
Expand Down Expand Up @@ -118,7 +119,8 @@ struct GameClient : public GameController, public GameSettingsProvider, public C
return true;
}

void send_cheating_info();
// arg1 is always the real user's name
void send_cheating_info(const std::string& code = "CHEAT", const std::string& arg2 = "");

std::shared_ptr<GameController>& get_pointer() {
return pointer_;
Expand Down
15 changes: 14 additions & 1 deletion src/network/gamehost.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1834,6 +1834,16 @@ void GameHost::welcome_client(uint32_t const number, std::string& playername) {
}

send_system_message_code("CLIENT_HAS_JOINED_GAME", effective_name);

if (g_allow_script_console) {
// TODO(tothxa): The host could warn only the new client, but other clients can only
// broadcast:
// 1. They can only send commands to the host
// 2. System messages are assembled and translated on each client, so
// individual players can't be @-addressed
// Until this is solved, it's better if the host broadcasts too.
send_system_message_code("CAN_CHEAT", d->localplayername);
}
}

void GameHost::committed_network_time(const Time& time) {
Expand Down Expand Up @@ -2463,12 +2473,15 @@ void GameHost::handle_packet(uint32_t const client_num, RecvPacket& r) {
}
}

static const std::set<std::string> cheating_message_codes = {
"CHEAT", "CAN_CHEAT", "SWITCHED_PLAYER", "CHEAT_OTHER"};

void GameHost::handle_system_message(RecvPacket& packet) {
const std::string code = packet.string();
const std::string arg1 = packet.string();
const std::string arg2 = packet.string();
const std::string arg3 = packet.string();
if (code != "CHEAT") {
if (cheating_message_codes.count(code) == 0) {
log_err("[Host]: Received system command %s(%s,%s,%s) from client", code.c_str(),
arg1.c_str(), arg2.c_str(), arg3.c_str());
throw DisconnectException("MALFORMED_COMMANDS");
Expand Down
7 changes: 6 additions & 1 deletion src/network/network_gaming_messages.cc
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,12 @@ static const std::map<std::string, std::string> ngmessages = {
{"COMPLETED_FILE_TRANSFER", gettext_noop("Completed transfer of file %1$s to %2$s")},
{"PLAYER_DEFEATED", gettext_noop("The player ‘%s’ was defeated and became a spectator.")},
{"CLIENT_HUNG", gettext_noop("Client %1$s did not answer for more than %2$s.")},
{"CHEAT", gettext_noop("Client %s is cheating!")}};
{"CAN_CHEAT", gettext_noop("Client %s has cheating features enabled!")},
{"CHEAT", gettext_noop("Client %s is cheating!")},
/** TRANSLATORS: %1 and %2 are user and player names */
{"SWITCHED_PLAYER", gettext_noop("Cheating: Client %1$s now controls %2$s")},
/** TRANSLATORS: %1 and %2 are user and player names */
{"CHEAT_OTHER", gettext_noop("Client %1$s is cheating, acting as %2$s!")}};

/// Returns a translated message fitting to the message code \arg code
const std::string NetworkGamingMessages::get_message(const std::string& code) {
Expand Down
1 change: 1 addition & 0 deletions src/ui_basic/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ wl_library(ui_basic
graphic_text
graphic_text_layout
graphic_wordwrap
io_fileread
io_filesystem
notifications
scripting_lua_interface
Expand Down
92 changes: 77 additions & 15 deletions src/ui_basic/textinput.cc
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,16 @@

#include <algorithm>

#include "base/log.h"
#include "base/utf8.h"
#include "graphic/font_handler.h"
#include "graphic/graphic.h"
#include "graphic/rendertarget.h"
#include "graphic/style_manager.h"
#include "graphic/text_layout.h"
#include "graphic/wordwrap.h"
#include "io/fileread.h"
#include "io/filewrite.h"
#include "ui_basic/mouse_constants.h"
#include "ui_basic/scrollbar.h"
#include "wlapplication_options.h"
Expand Down Expand Up @@ -816,25 +819,29 @@ bool EditBox::handle_key(bool const down, SDL_Keysym const code) {
}

// Save history if active and text is not empty
if (history_active_ && !d_->text.empty()) {
for (unsigned i = kHistorySize - 1; i > 0; --i) {
history_[i] = history_[i - 1];
}
history_[0] = d_->text;
if (history_ != nullptr && !d_->text.empty()) {
history_->add_entry(d_->text);
history_position_ = -1;
history_->clear_tmp();
}

ok();
return true;

case SDLK_UP:
// Load entry from history if active and text is not empty
if (history_active_) {
if (history_position_ > static_cast<int>(kHistorySize) - 2) {
history_position_ = kHistorySize - 2;
if (history_ != nullptr) {
if (history_position_ < 0) {
history_->set_tmp(d_->text);
}
++history_position_;
if (history_position_ >= history_->current_size()) {
history_position_ = history_->current_size() - 1;
return true;
}
if (!history_[++history_position_].empty()) {
d_->text = history_[history_position_];
const std::string& hist_prev = history_->get_entry(history_position_);
if (!hist_prev.empty()) {
d_->text = hist_prev;
set_caret_pos(d_->text.size());
d_->reset_selection();
changed();
Expand All @@ -845,12 +852,14 @@ bool EditBox::handle_key(bool const down, SDL_Keysym const code) {

case SDLK_DOWN:
// Load entry from history if active and text is not equivalent to the current one
if (history_active_) {
if (history_position_ < 1) {
history_position_ = 1;
if (history_ != nullptr) {
--history_position_;
if (history_position_ < 0) {
history_position_ = -1;
}
if (history_[--history_position_] != d_->text) {
d_->text = history_[history_position_];
const std::string& hist_next = history_->get_entry(history_position_);
if (hist_next != d_->text) {
d_->text = hist_next;
set_caret_pos(d_->text.size());
d_->reset_selection();
changed();
Expand Down Expand Up @@ -1111,4 +1120,57 @@ void AbstractTextInputPanel::Data::refresh_ww() {
scrollbar.set_steps(textheight - owner.get_h() + 2 * get_style().background().margin());
}

void EditBoxHistory::add_entry(const std::string& new_entry) {
// Avoid duplicates next to each other
if (!entries_.empty() && new_entry == entries_.at(0)) {
return;
}
entries_.emplace(entries_.begin(), new_entry);
changed_ = true;
if (entries_.size() > max_size_) {
entries_.pop_back();
}
}

const std::string& EditBoxHistory::get_entry(int16_t position) const {
if (position < 0 || position >= static_cast<int>(entries_.size())) {
return tmp_;
}
return entries_.at(position);
}

void EditBoxHistory::load(const std::string& filename) {
FileRead fr;
if (fr.try_open(*g_fs, filename)) {
entries_.clear();
try {
char* line;
while ((line = fr.read_line()) != nullptr) {
add_entry(line);
}
// Only set it on success to allow next save() to try to fix problem
changed_ = false;
} catch (const std::exception& e) {
log_err(
"Loading %s, line %" PRIuS ": %s", filename.c_str(), entries_.size() + 1, e.what());
}
}
}

void EditBoxHistory::save(const std::string& filename) {
if (!changed_) {
return;
}
try {
FileWrite fw;
for (auto it = entries_.rbegin(); it != entries_.rend(); ++it) {
fw.print_f("%s\n", it->c_str());
}
fw.write(*g_fs, filename);
changed_ = false;
} catch (const std::exception& e) {
log_err("Saving %s: %s", filename.c_str(), e.what());
}
}

} // namespace UI
58 changes: 45 additions & 13 deletions src/ui_basic/textinput.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ class AbstractTextInputPanel : public Panel {
return warning_;
}

bool handle_mousepress(uint8_t btn, int32_t x, int32_t y) override;
bool handle_mousemove(uint8_t state, int32_t x, int32_t, int32_t, int32_t) override;
bool handle_mousewheel(int32_t x, int32_t y, uint16_t modstate) override;
bool handle_key(bool down, SDL_Keysym) override;
bool handle_textinput(const std::string& text) override;

protected:
AbstractTextInputPanel(UI::Panel*,
const std::string& name,
Expand All @@ -72,12 +78,6 @@ class AbstractTextInputPanel : public Panel {

void draw(RenderTarget&) override;

bool handle_mousepress(uint8_t btn, int32_t x, int32_t y) override;
bool handle_mousemove(uint8_t state, int32_t x, int32_t, int32_t, int32_t) override;
bool handle_mousewheel(int32_t x, int32_t y, uint16_t modstate) override;
bool handle_key(bool down, SDL_Keysym) override;
bool handle_textinput(const std::string& text) override;

void scrollpos_changed(int32_t);
void delete_selected_text() const;
void copy_selected_text() const;
Expand All @@ -104,11 +104,42 @@ class AbstractTextInputPanel : public Panel {
uint32_t multiclick_counter_{0U};
};

class EditBoxHistory {
public:
explicit EditBoxHistory(uint16_t max_size) : max_size_(max_size) {
}

// Newer entries have lower positions
void add_entry(const std::string& new_entry);

// Returns tmp_ when position is out of range
[[nodiscard]] const std::string& get_entry(int16_t position) const;

[[nodiscard]] int16_t current_size() const {
return entries_.size();
}

void clear_tmp() {
tmp_.clear();
}
void set_tmp(const std::string& s) {
tmp_ = s;
}
// No getter, use get_entry(-1) to get tmp_

void load(const std::string& filename);
void save(const std::string& filename);

private:
uint16_t max_size_{0};
std::vector<std::string> entries_;
std::string tmp_;
bool changed_{false};
};

/** Subclass for single-line text input. */
class EditBox : public AbstractTextInputPanel {
public:
static constexpr unsigned kHistorySize = 16;

EditBox(UI::Panel* parent,
const std::string& name,
int32_t x,
Expand All @@ -118,22 +149,23 @@ class EditBox : public AbstractTextInputPanel {

Notifications::Signal<> ok;

void activate_history(bool activate) {
history_active_ = activate;
void activate_history(EditBoxHistory* history) {
history_ = history;
}

protected:
bool handle_key(bool down, SDL_Keysym) override;

protected:
uint32_t max_text_width_for_wrap() const override;
void scroll_cursor_into_view() override;
void escape_illegal_characters() const override;
[[nodiscard]] bool should_expand_selection() const override {
return true;
}

bool history_active_{false};
private:
int16_t history_position_{-1};
std::string history_[kHistorySize];
EditBoxHistory* history_{nullptr};
};

/** Subclass for multi-line text input. */
Expand Down
1 change: 1 addition & 0 deletions src/ui_fsmenu/internet_lobby.cc
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ InternetLobby::InternetLobby(MenuCapsule& fsmm,
0,
0,
InternetGaming::ref(),
&g_chat_sent_history,
UI::PanelStyle::kFsMenu),

// Right column content
Expand Down
4 changes: 1 addition & 3 deletions src/ui_fsmenu/keyboard_options.cc
Original file line number Diff line number Diff line change
Expand Up @@ -285,11 +285,9 @@ KeyboardOptions::KeyboardOptions(Panel& parent)
b->set_force_scrolling(true);
for (KeyboardShortcut k = shortcut_start; k <= shortcut_end; ++k) {
if (is_real(k)) {
#ifdef NDEBUG
if (is_debug_only(k)) {
if (!g_allow_script_console && is_developer_tool(k)) {
continue;
}
#endif
add_key(*b, k);
}
}
Expand Down

0 comments on commit 9bb7f4f

Please sign in to comment.