Functionality Improvements

+ Added 'keycatchers' as an standalone component
+ Improvement to logger with output division
+ Improvements to game console
This commit is contained in:
project-bo4 2024-07-28 12:26:23 +03:30 committed by Rim
parent b66a191aff
commit 84facee97e
9 changed files with 183 additions and 55 deletions

View File

@ -159,7 +159,7 @@ namespace command
add(command, [f = std::move(function)](const params&)
{
f();
});
}, desc);
}
void add(const std::string& command, command_param_function function, const char* desc)

View File

@ -6,6 +6,7 @@
#include "component/dvars.hpp"
#include "component/scheduler.hpp"
#include "component/keycatchers.hpp"
#include <utilities/hook.hpp>
#include <utilities/string.hpp>
@ -437,8 +438,7 @@ namespace game_console
}
}
bool console_char_event(const int local_client_num, const int key)
bool console_char_event(const int local_client_num, const int key, const bool is_repeated)
{
if (key == game::keyNum_t::K_GRAVE ||
key == game::keyNum_t::K_TILDE ||
@ -490,7 +490,7 @@ namespace game_console
for (size_t i = 0; i < clipboard.length(); i++)
{
console_char_event(local_client_num, clipboard[i]);
console_char_event(local_client_num, clipboard[i], false);
}
return false;
@ -546,7 +546,7 @@ namespace game_console
return true;
}
bool console_key_event(const int local_client_num, const int key, const int down)
bool console_key_event(const int local_client_num, const int key, const bool down, const unsigned int time)
{
if (key == game::keyNum_t::K_GRAVE || key == game::keyNum_t::K_TILDE)
{
@ -650,10 +650,8 @@ namespace game_console
});
}
if (key == game::keyNum_t::K_ENTER)
if (key == game::keyNum_t::K_ENTER && !utilities::string::is_truely_empty(con.buffer))
{
//game::Cbuf_AddText(0, utilities::string::va("%s \n", fixed_input.data()));
if (history_index != -1)
{
const auto itr = history.begin() + history_index;
@ -684,29 +682,6 @@ namespace game_console
return true;
}
utilities::hook::detour cl_key_event_hook;
void cl_key_event_stub(int localClientNum, int key, bool down, unsigned int time)
{
if (!game_console::console_key_event(localClientNum, key, down))
{
return;
}
cl_key_event_hook.invoke<void>(localClientNum, key, down, time);
}
utilities::hook::detour cl_char_event_hook;
void cl_char_event_stub(const int localClientNum, const int key, bool isRepeated)
{
if (!game_console::console_char_event(localClientNum, key))
{
return;
}
cl_char_event_hook.invoke<void>(localClientNum, key, isRepeated);
}
class component final : public component_interface
{
public:
@ -714,9 +689,8 @@ namespace game_console
{
scheduler::loop(draw_console, scheduler::renderer);
cl_key_event_hook.create(0x142839250_g, cl_key_event_stub);
cl_char_event_hook.create(0x142836F80_g, cl_char_event_stub);
keycatchers::add_key_event(console_key_event);
keycatchers::add_char_event(console_char_event);
// initialize our structs
con.cursor = 0;

View File

@ -0,0 +1,71 @@
#include <std_include.hpp>
#include "keycatchers.hpp"
#include "loader/component_loader.hpp"
#include <utilities/hook.hpp>
#include <utilities/concurrency.hpp>
namespace keycatchers
{
namespace
{
utilities::concurrency::container<std::vector<ke_typedef>, std::recursive_mutex> key_event_callbacks;
utilities::concurrency::container<std::vector<ch_typedef>, std::recursive_mutex> char_event_callbacks;
utilities::hook::detour cl_key_event_hook;
void cl_key_event_stub(const int localClientNum, const int key, bool down, const unsigned int time)
{
key_event_callbacks.access([&](std::vector<ke_typedef>& callbacks)
{
for (auto& func : callbacks) {
if (!func(localClientNum, key, down, time))
return;
}
});
cl_key_event_hook.invoke<void>(localClientNum, key, down, time);
}
utilities::hook::detour cl_char_event_hook;
void cl_char_event_stub(const int localClientNum, const int key, bool isRepeated)
{
char_event_callbacks.access([&](std::vector<ch_typedef>& callbacks)
{
for (auto& func : callbacks) {
if (!func(localClientNum, key, isRepeated))
return;
}
});
cl_char_event_hook.invoke<void>(localClientNum, key, isRepeated);
}
}
void add_key_event(ke_typedef&& func)
{
key_event_callbacks.access([&func](std::vector<ke_typedef>& callbacks)
{
callbacks.push_back(std::move(func));
});
}
void add_char_event(ch_typedef&& func)
{
char_event_callbacks.access([&func](std::vector<ch_typedef>& callbacks)
{
callbacks.push_back(std::move(func));
});
}
class component final : public component_interface
{
public:
void post_unpack() override
{
cl_key_event_hook.create(0x142839250_g, cl_key_event_stub);
cl_char_event_hook.create(0x142836F80_g, cl_char_event_stub);
}
};
}
REGISTER_COMPONENT(keycatchers::component)

View File

@ -0,0 +1,10 @@
#pragma once
namespace keycatchers
{
using ke_typedef = std::function<bool(int, int, bool, unsigned int)>;
using ch_typedef = std::function<bool(int, int, bool)>;
void add_key_event(ke_typedef&& func);
void add_char_event(ch_typedef&& func);
}

View File

@ -5,9 +5,6 @@
#include <utilities/nt.hpp>
#define OUTPUT_DEBUG_API
#define OUTPUT_GAME_CONSOLE
namespace logger
{
const char* LogTypeNames[] =
@ -15,7 +12,8 @@ namespace logger
"DEBUG",
"INFO",
"WARN",
"ERROR"
"ERROR",
""
};
void write(const int type, std::string str)
@ -24,15 +22,14 @@ namespace logger
if (type == LOG_TYPE_DEBUG) return;
#endif // _DEBUG
game_console::print(str);
if (type == type::LOG_TYPE_CONSOLE) return;
std::stringstream ss;
ss << "[ " << LogTypeNames[type] << " ] " << str << std::endl;
std::string text = ss.str();
#ifdef OUTPUT_GAME_CONSOLE
game_console::print(text);
#endif // OUTPUT_GAME_CONSOLE
#ifdef OUTPUT_DEBUG_API
OutputDebugStringA(text.c_str());
#endif // OUTPUT_DEBUG_API

View File

@ -7,7 +7,8 @@ namespace logger
LOG_TYPE_DEBUG = 0,
LOG_TYPE_INFO = 1,
LOG_TYPE_WARN = 2,
LOG_TYPE_ERROR = 3
LOG_TYPE_ERROR = 3,
LOG_TYPE_CONSOLE = 4
};
void write(const int type, std::string str);

View File

@ -4,7 +4,6 @@
#include "dvars.hpp"
#include "hashes.hpp"
#include "command.hpp"
#include "game_console.hpp"
#include "loader/component_loader.hpp"
#include "definitions/xassets.hpp"
@ -1099,17 +1098,17 @@ namespace mods {
if (!game::Com_IsRunningUILevel())
{
// avoid gsc issues, but if a script is loaded in the frontend, it will still crash
game_console::print("can't load mods while in-game!");
logger::write(logger::LOG_TYPE_CONSOLE, "can't load mods while in-game!");
return;
}
if (!storage.load_mods())
{
game_console::print("mods reloaded.");
logger::write(logger::LOG_TYPE_CONSOLE, "mods reloaded.");
}
else
{
game_console::print("mods reloaded with errors, see logs.");
logger::write(logger::LOG_TYPE_CONSOLE, "mods reloaded with errors, see logs.");
}
}
}

View File

@ -181,6 +181,30 @@ namespace utilities::string
}
#pragma warning(pop)
void copy(char* dest, const size_t max_size, const char* src)
{
if (!max_size)
{
return;
}
for (size_t i = 0;; ++i)
{
if (i + 1 == max_size)
{
dest[i] = 0;
break;
}
dest[i] = src[i];
if (!src[i])
{
break;
}
}
}
std::string replace(std::string str, const std::string& from, const std::string& to)
{
if (from.empty())
@ -198,6 +222,46 @@ namespace utilities::string
return str;
}
std::string& ltrim(std::string& str)
{
str.erase(str.begin(), std::find_if(str.begin(), str.end(), [](const unsigned char input)
{
return !std::isspace(input);
}));
return str;
}
std::string& rtrim(std::string& str)
{
str.erase(std::find_if(str.rbegin(), str.rend(), [](const unsigned char input)
{
return !std::isspace(input);
}).base(), str.end());
return str;
}
std::string& trim(std::string& str)
{
return ltrim(rtrim(str));
}
bool is_truely_empty(std::string str)
{
return trim(str).size() == 0;
}
StringMatch compare(const std::string& s1, const std::string& s2)
{
if (s1 == s2)
return StringMatch::Identical;
else if (to_lower(s1) == to_lower(s2))
return StringMatch::CaseVariant;
else
return StringMatch::Mismatch;
}
double match(const std::string& input, const std::string& text)
{
if (text == input) return 1.00; // identical
@ -211,13 +275,6 @@ namespace utilities::string
return ((double)match_percent / 100);
}
bool compare(const std::string& s1, const std::string& s2, bool sensetive)
{
if (sensetive && (s1 == s2)) return true;
if (!sensetive && (to_lower(s1) == to_lower(s2))) return true;
return false;
}
bool contains(std::string text, std::string substr, bool sensetive)
{
if (!sensetive) {

View File

@ -100,9 +100,28 @@ namespace utilities::string
std::string convert(const std::wstring& wstr);
std::wstring convert(const std::string& str);
void copy(char* dest, size_t max_size, const char* src);
template <size_t Size>
void copy(char(&dest)[Size], const char* src)
{
copy(dest, Size, src);
}
std::string replace(std::string str, const std::string& from, const std::string& to);
std::string& trim(std::string& str);
bool is_truely_empty(std::string str);
enum StringMatch
{
Mismatch = 0,
Identical = 1,
CaseVariant = 2
};
StringMatch compare(const std::string& s1, const std::string& s2);
double match(const std::string& input, const std::string& text);
bool compare(const std::string& s1, const std::string& s2, bool sensetive = false);
bool contains(std::string text, std::string substr, bool sensetive = false);
}