GUI Script console
This commit is contained in:
parent
dc3af7412c
commit
49e4891b31
@ -120,6 +120,7 @@ namespace gui
|
|||||||
menu_checkbox("Asset list", "asset_list");
|
menu_checkbox("Asset list", "asset_list");
|
||||||
menu_checkbox("Entity list", "entity_list");
|
menu_checkbox("Entity list", "entity_list");
|
||||||
menu_checkbox("Console", "console");
|
menu_checkbox("Console", "console");
|
||||||
|
menu_checkbox("Script console", "script_console");
|
||||||
|
|
||||||
ImGui::EndMenu();
|
ImGui::EndMenu();
|
||||||
}
|
}
|
||||||
|
@ -182,10 +182,10 @@ namespace gui_console
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
game_console::add(input.data());
|
|
||||||
|
|
||||||
input.clear();
|
|
||||||
ImGui::SetKeyboardFocusHere(-1);
|
ImGui::SetKeyboardFocusHere(-1);
|
||||||
|
game_console::add(input.data());
|
||||||
|
history_index = -1;
|
||||||
|
input.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
|
231
src/client/component/gui_script_console.cpp
Normal file
231
src/client/component/gui_script_console.cpp
Normal file
@ -0,0 +1,231 @@
|
|||||||
|
#include <std_include.hpp>
|
||||||
|
#include "loader/component_loader.hpp"
|
||||||
|
|
||||||
|
#include "game/game.hpp"
|
||||||
|
#include "game/dvars.hpp"
|
||||||
|
|
||||||
|
#include "scheduler.hpp"
|
||||||
|
#include "command.hpp"
|
||||||
|
#include "gui.hpp"
|
||||||
|
|
||||||
|
#include "game/scripting/lua/context.hpp"
|
||||||
|
#include "game/scripting/lua/engine.hpp"
|
||||||
|
|
||||||
|
#include <utils/string.hpp>
|
||||||
|
#include <utils/hook.hpp>
|
||||||
|
#include <utils/concurrency.hpp>
|
||||||
|
|
||||||
|
namespace gui_script_console
|
||||||
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
bool auto_scroll = true;
|
||||||
|
bool multi_line_input = false;
|
||||||
|
int history_index = -1;
|
||||||
|
std::string input{};
|
||||||
|
std::string filter{};
|
||||||
|
std::deque<std::string> history;
|
||||||
|
|
||||||
|
struct menu_data_t
|
||||||
|
{
|
||||||
|
std::deque<std::string> command_queue;
|
||||||
|
std::deque<std::string> output;
|
||||||
|
};
|
||||||
|
|
||||||
|
utils::concurrency::container<menu_data_t> menu_data;
|
||||||
|
|
||||||
|
std::string run_command(const std::string& code)
|
||||||
|
{
|
||||||
|
const auto result = scripting::lua::engine::load(code);
|
||||||
|
if (result.has_value())
|
||||||
|
{
|
||||||
|
return result.value();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return "Script not running";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void run_commands()
|
||||||
|
{
|
||||||
|
menu_data.access([](menu_data_t& menu_data_)
|
||||||
|
{
|
||||||
|
for (const auto& command : menu_data_.command_queue)
|
||||||
|
{
|
||||||
|
menu_data_.output.push_back(run_command(command));
|
||||||
|
}
|
||||||
|
|
||||||
|
menu_data_.command_queue.clear();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
int multi_line_input_text_edit(ImGuiInputTextCallbackData* data)
|
||||||
|
{
|
||||||
|
if (data->EventKey == ImGuiKey_Tab)
|
||||||
|
{
|
||||||
|
data->InsertChars(data->CursorPos, "\t");
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int input_text_edit(ImGuiInputTextCallbackData* data)
|
||||||
|
{
|
||||||
|
switch (data->EventFlag)
|
||||||
|
{
|
||||||
|
case ImGuiInputTextFlags_CallbackHistory:
|
||||||
|
{
|
||||||
|
if (data->EventKey == ImGuiKey_UpArrow)
|
||||||
|
{
|
||||||
|
if (++history_index >= history.size())
|
||||||
|
{
|
||||||
|
history_index = static_cast<int>(history.size()) - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
data->DeleteChars(0, data->BufTextLen);
|
||||||
|
|
||||||
|
if (history_index != -1)
|
||||||
|
{
|
||||||
|
const auto text = history.at(history_index).data();
|
||||||
|
data->InsertChars(0, text, text + strlen(text));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (data->EventKey == ImGuiKey_DownArrow)
|
||||||
|
{
|
||||||
|
if (--history_index < -1)
|
||||||
|
{
|
||||||
|
history_index = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
data->DeleteChars(0, data->BufTextLen);
|
||||||
|
|
||||||
|
if (history_index != -1)
|
||||||
|
{
|
||||||
|
const auto text = history.at(history_index).data();
|
||||||
|
data->InsertChars(0, text, text + strlen(text));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_frame()
|
||||||
|
{
|
||||||
|
if (!gui::enabled_menus["script_console"])
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
menu_data.access([](menu_data_t& menu_data_)
|
||||||
|
{
|
||||||
|
if (!game::CL_IsCgameInitialized())
|
||||||
|
{
|
||||||
|
menu_data_.command_queue.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
static const float footer_height = ImGui::GetStyle().ItemSpacing.y + ImGui::GetFrameHeightWithSpacing();
|
||||||
|
static const auto input_text_flags = ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_CallbackCompletion |
|
||||||
|
ImGuiInputTextFlags_CallbackHistory;
|
||||||
|
|
||||||
|
ImGui::Begin("Script console", &gui::enabled_menus["script_console"]);
|
||||||
|
|
||||||
|
if (ImGui::BeginPopup("Options"))
|
||||||
|
{
|
||||||
|
ImGui::Checkbox("Auto-scroll", &auto_scroll);
|
||||||
|
ImGui::Checkbox("Multi-line input", &multi_line_input);
|
||||||
|
ImGui::EndPopup();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::Button("Clear"))
|
||||||
|
{
|
||||||
|
menu_data_.output.clear();
|
||||||
|
input.clear();
|
||||||
|
history.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
|
if (ImGui::Button("Options"))
|
||||||
|
{
|
||||||
|
ImGui::OpenPopup("Options");
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::InputText("Filter", &filter);
|
||||||
|
|
||||||
|
ImGui::BeginChild("script_console_scroll", ImVec2(0, -1 * footer_height - 80.f * multi_line_input), false);
|
||||||
|
|
||||||
|
for (const auto& line : menu_data_.output)
|
||||||
|
{
|
||||||
|
ImGui::Text(line.data());
|
||||||
|
ImGui::Separator();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auto_scroll && ImGui::GetScrollY() >= ImGui::GetScrollMaxY())
|
||||||
|
{
|
||||||
|
ImGui::SetScrollHereY(1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::EndChild();
|
||||||
|
|
||||||
|
bool execute = false;
|
||||||
|
if (multi_line_input)
|
||||||
|
{
|
||||||
|
ImGui::InputTextMultiline("", &input, ImVec2(0, 100),
|
||||||
|
ImGuiInputTextFlags_CallbackCompletion, multi_line_input_text_edit);
|
||||||
|
ImGui::SameLine();
|
||||||
|
execute = ImGui::Button("Execute", ImVec2(100, 100));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
execute = ImGui::InputText("Input", &input, input_text_flags, input_text_edit);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (execute)
|
||||||
|
{
|
||||||
|
menu_data_.output.push_back(input);
|
||||||
|
menu_data_.command_queue.push_back(input);
|
||||||
|
|
||||||
|
if (history_index != -1)
|
||||||
|
{
|
||||||
|
const auto itr = history.begin() + history_index;
|
||||||
|
|
||||||
|
if (*itr == input)
|
||||||
|
{
|
||||||
|
history.erase(history.begin() + history_index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
history.push_front(input);
|
||||||
|
if (history.size() > 10)
|
||||||
|
{
|
||||||
|
history.erase(history.begin() + 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::SetKeyboardFocusHere(-1);
|
||||||
|
history_index = -1;
|
||||||
|
input.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::End();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class component final : public component_interface
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void post_unpack() override
|
||||||
|
{
|
||||||
|
gui::on_frame(on_frame);
|
||||||
|
scheduler::loop(run_commands, scheduler::pipeline::server);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
REGISTER_COMPONENT(gui_script_console::component)
|
@ -539,6 +539,53 @@ namespace scripting::lua
|
|||||||
this->load_script("__init__");
|
this->load_script("__init__");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
context::context()
|
||||||
|
: folder_({})
|
||||||
|
, scheduler_(state_)
|
||||||
|
, event_handler_(state_)
|
||||||
|
|
||||||
|
{
|
||||||
|
this->state_.open_libraries(sol::lib::base,
|
||||||
|
sol::lib::package,
|
||||||
|
sol::lib::io,
|
||||||
|
sol::lib::string,
|
||||||
|
sol::lib::os,
|
||||||
|
sol::lib::math,
|
||||||
|
sol::lib::table);
|
||||||
|
|
||||||
|
this->state_["include"] = []()
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
this->state_["require"] = []()
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
setup_entity_type(this->state_, this->event_handler_, this->scheduler_);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string context::load(const std::string& code)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
const auto result = this->state_.safe_script(code, &sol::script_pass_on_error);
|
||||||
|
if (result.valid())
|
||||||
|
{
|
||||||
|
const auto object = result.get<sol::object>();
|
||||||
|
return this->state_["tostring"](object).get<std::string>();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const sol::error error = result;
|
||||||
|
return error.what();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const std::exception& e)
|
||||||
|
{
|
||||||
|
return e.what();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
context::~context()
|
context::~context()
|
||||||
{
|
{
|
||||||
this->state_.collect_garbage();
|
this->state_.collect_garbage();
|
||||||
|
@ -17,6 +17,7 @@ namespace scripting::lua
|
|||||||
class context
|
class context
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
context();
|
||||||
context(std::string folder);
|
context(std::string folder);
|
||||||
~context();
|
~context();
|
||||||
|
|
||||||
@ -29,6 +30,8 @@ namespace scripting::lua
|
|||||||
void run_frame();
|
void run_frame();
|
||||||
void notify(const event& e);
|
void notify(const event& e);
|
||||||
|
|
||||||
|
std::string load(const std::string& code);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
sol::state state_{};
|
sol::state state_{};
|
||||||
std::string folder_;
|
std::string folder_;
|
||||||
|
@ -19,6 +19,8 @@ namespace scripting::lua::engine
|
|||||||
|
|
||||||
void load_scripts()
|
void load_scripts()
|
||||||
{
|
{
|
||||||
|
get_scripts().push_back(std::make_unique<context>());
|
||||||
|
|
||||||
const auto script_dir = "scripts/"s;
|
const auto script_dir = "scripts/"s;
|
||||||
|
|
||||||
if (!utils::io::directory_exists(script_dir))
|
if (!utils::io::directory_exists(script_dir))
|
||||||
@ -66,4 +68,15 @@ namespace scripting::lua::engine
|
|||||||
script->run_frame();
|
script->run_frame();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<std::string> load(const std::string& code)
|
||||||
|
{
|
||||||
|
if (get_scripts().size() == 0)
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& script = get_scripts()[0];
|
||||||
|
return {script->load(code)};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,4 +8,6 @@ namespace scripting::lua::engine
|
|||||||
void stop();
|
void stop();
|
||||||
void notify(const event& e);
|
void notify(const event& e);
|
||||||
void run_frame();
|
void run_frame();
|
||||||
|
|
||||||
|
std::optional<std::string> load(const std::string& code);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user