feat(lua): Allow lua scripts to flag modders with a custom reason if needed. (#2248)

This commit is contained in:
Quentin 2023-10-13 00:11:37 +02:00 committed by GitHub
parent 3323e1c43f
commit 5515c841a1
10 changed files with 301 additions and 68 deletions

View File

@ -18,6 +18,7 @@ class DocKind(Enum):
Constructor = "constructor" Constructor = "constructor"
Function = "function" Function = "function"
Tabs = "tabs" Tabs = "tabs"
Infraction = "infraction"
class Table: class Table:
@ -324,7 +325,10 @@ def parse_lua_api_doc(folder_path):
.lower() .lower()
) )
if doc_kind is not DocKind.Tabs: if (
doc_kind is not DocKind.Tabs
and doc_kind is not DocKind.Infraction
):
continue continue
if doc_kind is not None and "//" in line: if doc_kind is not None and "//" in line:
@ -367,7 +371,10 @@ def parse_lua_api_doc(folder_path):
line, line,
line_lower, line_lower,
) )
case DocKind.Tabs: parse_tabs_doc(file) case DocKind.Tabs:
parse_tabs_doc(file)
case DocKind.Infraction:
parse_infraction_doc(file)
case _: case _:
# print("unsupported doc kind: " + str(doc_kind)) # print("unsupported doc kind: " + str(doc_kind))
pass pass
@ -386,6 +393,7 @@ def parse_table_doc(cur_table, line, line_lower):
return cur_table return cur_table
def parse_class_doc(cur_class, line, line_lower): def parse_class_doc(cur_class, line, line_lower):
if is_lua_doc_comment_startswith(line_lower, "name"): if is_lua_doc_comment_startswith(line_lower, "name"):
class_name = line.split(lua_api_comment_separator, 1)[1].strip() class_name = line.split(lua_api_comment_separator, 1)[1].strip()
@ -402,28 +410,43 @@ def parse_class_doc(cur_class, line, line_lower):
def parse_function_doc(cur_function, cur_table, cur_class, line, line_lower): def parse_function_doc(cur_function, cur_table, cur_class, line, line_lower):
if is_lua_doc_comment_startswith(line_lower, "table") and lua_api_comment_separator in line_lower: if (
is_lua_doc_comment_startswith(line_lower, "table")
and lua_api_comment_separator in line_lower
):
table_name = line.split(lua_api_comment_separator, 1)[1].strip() table_name = line.split(lua_api_comment_separator, 1)[1].strip()
cur_table = make_table(table_name) cur_table = make_table(table_name)
cur_function = Function("Didnt get name yet", cur_table, [], None, "", "") cur_function = Function("Didnt get name yet", cur_table, [], None, "", "")
cur_table.functions.append(cur_function) cur_table.functions.append(cur_function)
elif is_lua_doc_comment_startswith(line_lower, "class") and lua_api_comment_separator in line_lower: elif (
is_lua_doc_comment_startswith(line_lower, "class")
and lua_api_comment_separator in line_lower
):
class_name = line.split(lua_api_comment_separator, 1)[1].strip() class_name = line.split(lua_api_comment_separator, 1)[1].strip()
cur_class = make_class(class_name) cur_class = make_class(class_name)
cur_function = Function("Didnt get name yet", cur_class, [], None, "", "") cur_function = Function("Didnt get name yet", cur_class, [], None, "", "")
cur_class.functions.append(cur_function) cur_class.functions.append(cur_function)
elif is_lua_doc_comment_startswith(line_lower, "name") and lua_api_comment_separator in line_lower: elif (
is_lua_doc_comment_startswith(line_lower, "name")
and lua_api_comment_separator in line_lower
):
function_name = line.split(lua_api_comment_separator, 1)[1].strip() function_name = line.split(lua_api_comment_separator, 1)[1].strip()
cur_function.name = function_name cur_function.name = function_name
if function_name not in functions: if function_name not in functions:
functions[function_name] = cur_function functions[function_name] = cur_function
elif is_lua_doc_comment_startswith(line_lower, "param") and lua_api_comment_separator in line_lower: elif (
is_lua_doc_comment_startswith(line_lower, "param")
and lua_api_comment_separator in line_lower
):
parameter = make_parameter_from_doc_line(line) parameter = make_parameter_from_doc_line(line)
cur_function.parameters.append(parameter) cur_function.parameters.append(parameter)
elif is_lua_doc_comment_startswith(line_lower, "return") and lua_api_comment_separator in line_lower: elif (
is_lua_doc_comment_startswith(line_lower, "return")
and lua_api_comment_separator in line_lower
):
return_info = line.split(lua_api_comment_separator, 2) return_info = line.split(lua_api_comment_separator, 2)
try: try:
cur_function.return_type = return_info[1].strip() cur_function.return_type = return_info[1].strip()
@ -439,19 +462,28 @@ def parse_function_doc(cur_function, cur_table, cur_class, line, line_lower):
def parse_field_doc(cur_field, cur_table, cur_class, line, line_lower): def parse_field_doc(cur_field, cur_table, cur_class, line, line_lower):
if is_lua_doc_comment_startswith(line_lower, "table") and lua_api_comment_separator in line_lower: if (
is_lua_doc_comment_startswith(line_lower, "table")
and lua_api_comment_separator in line_lower
):
table_name = line.split(lua_api_comment_separator, 1)[1].strip() table_name = line.split(lua_api_comment_separator, 1)[1].strip()
cur_table = make_table(table_name) cur_table = make_table(table_name)
cur_field = Field("Didnt get name yet", "", "") cur_field = Field("Didnt get name yet", "", "")
cur_table.fields.append(cur_field) cur_table.fields.append(cur_field)
elif is_lua_doc_comment_startswith(line_lower, "class") and lua_api_comment_separator in line_lower: elif (
is_lua_doc_comment_startswith(line_lower, "class")
and lua_api_comment_separator in line_lower
):
class_name = line.split(lua_api_comment_separator, 1)[1].strip() class_name = line.split(lua_api_comment_separator, 1)[1].strip()
cur_class = make_class(class_name) cur_class = make_class(class_name)
cur_field = Field("Didnt get name yet", "", "") cur_field = Field("Didnt get name yet", "", "")
cur_class.fields.append(cur_field) cur_class.fields.append(cur_field)
elif is_lua_doc_comment_startswith(line_lower, "field") and lua_api_comment_separator in line_lower: elif (
is_lua_doc_comment_startswith(line_lower, "field")
and lua_api_comment_separator in line_lower
):
field_info = line.split(lua_api_comment_separator, 2) field_info = line.split(lua_api_comment_separator, 2)
cur_field.name = field_info[1].strip() cur_field.name = field_info[1].strip()
cur_field.type_ = field_info[2].strip() cur_field.type_ = field_info[2].strip()
@ -467,13 +499,19 @@ def parse_field_doc(cur_field, cur_table, cur_class, line, line_lower):
def parse_constructor_doc(cur_constructor, cur_class, line, line_lower): def parse_constructor_doc(cur_constructor, cur_class, line, line_lower):
if is_lua_doc_comment_startswith(line_lower, "class") and lua_api_comment_separator in line_lower: if (
is_lua_doc_comment_startswith(line_lower, "class")
and lua_api_comment_separator in line_lower
):
class_name = line.split(lua_api_comment_separator, 1)[1].strip() class_name = line.split(lua_api_comment_separator, 1)[1].strip()
cur_class = make_class(class_name) cur_class = make_class(class_name)
cur_constructor = Constructor(cur_class, [], "") cur_constructor = Constructor(cur_class, [], "")
cur_class.constructors.append(cur_constructor) cur_class.constructors.append(cur_constructor)
elif is_lua_doc_comment_startswith(line_lower, "param") and lua_api_comment_separator in line_lower: elif (
is_lua_doc_comment_startswith(line_lower, "param")
and lua_api_comment_separator in line_lower
):
parameter = make_parameter_from_doc_line(line) parameter = make_parameter_from_doc_line(line)
cur_constructor.parameters.append(parameter) cur_constructor.parameters.append(parameter)
else: else:
@ -485,6 +523,8 @@ def parse_constructor_doc(cur_constructor, cur_class, line, line_lower):
tabs_enum = [] tabs_enum = []
def parse_tabs_doc(file): def parse_tabs_doc(file):
start_parsing = False start_parsing = False
for line in file: for line in file:
@ -503,7 +543,29 @@ def parse_tabs_doc(file):
continue continue
else: else:
tabs_enum.append(line.replace(",", "").strip()) tabs_enum.append(line.replace(",", "").strip())
infraction_enum = []
def parse_infraction_doc(file):
start_parsing = False
for line in file:
if "enum class" in line.lower():
start_parsing = True
continue
if start_parsing:
if "};" in line.lower():
return
if "{" == line.lower().strip():
continue
if "//" in line.lower():
continue
if "" == line.lower().strip():
continue
else:
infraction_enum.append(line.replace(",", "").strip())
def make_parameter_from_doc_line(line): def make_parameter_from_doc_line(line):
@ -519,13 +581,15 @@ def make_parameter_from_doc_line(line):
return Parameter(param_name, param_type, param_desc) return Parameter(param_name, param_type, param_desc)
def sanitize_description(line): def sanitize_description(line):
if line.startswith("// ") and line[3] != ' ': if line.startswith("// ") and line[3] != " ":
line = line[3:] line = line[3:]
if line.startswith("//"): if line.startswith("//"):
line = line[2:] line = line[2:]
return line.rstrip() return line.rstrip()
def is_lua_doc_comment_startswith(line_lower, starts_with_text): def is_lua_doc_comment_startswith(line_lower, starts_with_text):
return line_lower.replace("//", "").strip().startswith(starts_with_text) return line_lower.replace("//", "").strip().startswith(starts_with_text)
@ -550,7 +614,8 @@ if os.path.exists(tabs_file_name):
os.remove(tabs_file_name) os.remove(tabs_file_name)
f = open(tabs_file_name, "a") f = open(tabs_file_name, "a")
f.write("""# Tabs f.write(
"""# Tabs
All the tabs from the menu are listed below, used as parameter for adding gui elements to them. All the tabs from the menu are listed below, used as parameter for adding gui elements to them.
@ -565,16 +630,42 @@ end)
For a complete list of available gui functions, please refer to the tab class documentation and the gui table documentation. For a complete list of available gui functions, please refer to the tab class documentation and the gui table documentation.
""") """
)
f.write(f"## Tab Count: {len(tabs_enum)}\n\n") f.write(f"## Tab Count: {len(tabs_enum)}\n\n")
# Minus the first, because it's the `NONE` tab, minus the last one because it's for runtime defined tabs. # Minus the first, because it's the `NONE` tab, minus the last one because it's for runtime defined tabs.
for i in range(1, len(tabs_enum) - 1 ): for i in range(1, len(tabs_enum) - 1):
f.write("### `GUI_TAB_" + tabs_enum[i] + "`\n") f.write("### `GUI_TAB_" + tabs_enum[i] + "`\n")
f.close() f.close()
infraction_file_name = f"../lua/infraction.md"
if os.path.exists(infraction_file_name):
os.remove(infraction_file_name)
f = open(infraction_file_name, "a")
f.write(
"""# Infraction
All the infraction from the menu are listed below, used as parameter for adding an infraction to a given player, for flagging them as modder.
**Example Usage:**
```lua
network.flag_player_as_modder(player_index, infraction.CUSTOM_REASON, "My custom reason on why the player is flagged as a modder")
```
"""
)
f.write(f"## Infraction Count: {len(infraction_enum)}\n\n")
for i in range(0, len(infraction_enum)):
f.write("### `" + infraction_enum[i] + "`\n")
f.close()
try: try:
os.makedirs("./classes/") os.makedirs("./classes/")
except: except:
@ -589,13 +680,13 @@ for class_name, class_ in classes.items():
f.close() f.close()
commands_file_name = f"./commands.md" commands_file_name = f"./commands.md"
if os.path.exists(commands_file_name): if os.path.exists(commands_file_name):
os.remove(commands_file_name) os.remove(commands_file_name)
f = open(commands_file_name, "a") f = open(commands_file_name, "a")
f.write("""# Commands f.write(
"""# Commands
All the current commands from the menu are listed below. All the current commands from the menu are listed below.
@ -608,7 +699,8 @@ command.call_player(somePlayerIndex, "spawn", {joaat("adder")})
For a complete list of available command functions, please refer to the command table documentation. For a complete list of available command functions, please refer to the command table documentation.
""") """
)
commands = [] commands = []
@ -629,4 +721,4 @@ for cmd in commands:
f.write(f"Arg Count: {arg_count}\n") f.write(f"Arg Count: {arg_count}\n")
f.write("\n") f.write("\n")
f.close() f.close()

23
docs/lua/infraction.md Normal file
View File

@ -0,0 +1,23 @@
# Infraction
All the infraction from the menu are listed below, used as parameter for adding an infraction to a given player, for flagging them as modder.
**Example Usage:**
```lua
network.flag_player_as_modder(player_index, infraction.CUSTOM_REASON, "My custom reason on why the player is flagged as a modder")
```
## Infraction Count: 12
### `TRIGGERED_ANTICHEAT`
### `TRIED_CRASH_PLAYER`
### `TRIED_KICK_PLAYER`
### `ATTACKING_WITH_GODMODE`
### `ATTACKING_WITH_INVISIBILITY`
### `ATTACKING_WHEN_HIDDEN_FROM_PLAYER_LIST`
### `SPOOFED_DATA`
### `SPOOFED_HOST_TOKEN`
### `INVALID_PLAYER_MODEL`
### `SUPER_JUMP`
### `UNDEAD_OTR`
### `CUSTOM_REASON`

View File

@ -2,7 +2,7 @@
Table containing helper functions for network related features. Table containing helper functions for network related features.
## Functions (10) ## Functions (11)
### `trigger_script_event(bitset, _args)` ### `trigger_script_event(bitset, _args)`
@ -79,16 +79,18 @@ integer = network.get_selected_player()
integer = network.get_selected_database_player_rockstar_id() integer = network.get_selected_database_player_rockstar_id()
``` ```
### `flag_player_as_modder(player_idx)` ### `flag_player_as_modder(player_idx, reason, custom_reason)`
Flags the given player as a modder in our local database. Flags the given player as a modder in our local database.
- **Parameters:** - **Parameters:**
- `player_idx` (integer): Index of the player. - `player_idx` (integer): Index of the player.
- `reason` (Infraction): Reason why the player is flagged as a modder, if the infraction is CUSTOM_REASON, then the custom_reason string is added in the local database. For a full list of the possible infraction reasons to use, please check the infraction page.
- `custom_reason` (string): Optional, required only when the infraction is CUSTOM_REASON. The custom reason why the player is flagged as a modder.
**Example Usage:** **Example Usage:**
```lua ```lua
network.flag_player_as_modder(player_idx) network.flag_player_as_modder(player_idx, reason, custom_reason)
``` ```
### `is_player_flagged_as_modder(player_idx)` ### `is_player_flagged_as_modder(player_idx)`
@ -104,6 +106,19 @@ network.flag_player_as_modder(player_idx)
boolean = network.is_player_flagged_as_modder(player_idx) boolean = network.is_player_flagged_as_modder(player_idx)
``` ```
### `get_flagged_modder_reason(player_idx)`
- **Parameters:**
- `player_idx` (integer): Index of the player.
- **Returns:**
- `string`: Returns a string which contains the reason(s) why the player is flagged as a modder.
**Example Usage:**
```lua
string = network.get_flagged_modder_reason(player_idx)
```
### `force_script_host(script_name)` ### `force_script_host(script_name)`
Try to force ourself to be host for the given GTA Script. Try to force ourself to be host for the given GTA Script.

View File

@ -1,11 +1,12 @@
#pragma once #pragma once
#include "gta/joaat.hpp"
namespace big namespace big
{ {
// Add new values to the bottom // Lua API: Infraction
enum class Infraction enum class Infraction
{ {
// Add new values to the bottom (for serialization)
DESYNC_PROTECTION, // do not use DESYNC_PROTECTION, // do not use
BREAKUP_KICK_DETECTED, // do not use BREAKUP_KICK_DETECTED, // do not use
LOST_CONNECTION_KICK_DETECTED, // do not use LOST_CONNECTION_KICK_DETECTED, // do not use
@ -22,24 +23,7 @@ namespace big
INVALID_PLAYER_MODEL, INVALID_PLAYER_MODEL,
SUPER_JUMP, SUPER_JUMP,
UNDEAD_OTR, UNDEAD_OTR,
}; // So that lua scripts can add a custom runtime reason.
CUSTOM_REASON,
inline std::unordered_map<Infraction, const char*> infraction_desc = {
{Infraction::DESYNC_PROTECTION, "Used desync protections"},
{Infraction::BREAKUP_KICK_DETECTED, "Kicked someone using breakup kick"},
{Infraction::LOST_CONNECTION_KICK_DETECTED, "Tried to kick someone using lost connection kick"},
{Infraction::SPOOFED_ROCKSTAR_ID, "Had spoofed RID"},
{Infraction::TRIGGERED_ANTICHEAT, "Triggered Rockstar's anticheat"},
{Infraction::TRIED_CRASH_PLAYER, "Tried to crash you"},
{Infraction::TRIED_KICK_PLAYER, "Tried to kick you"},
{Infraction::BLAME_EXPLOSION_DETECTED, "Tried to blame someone for their explosion"},
{Infraction::ATTACKING_WITH_GODMODE, "Attacked someone while using godmode"},
{Infraction::ATTACKING_WITH_INVISIBILITY, "Attacked someone while being invisible"},
{Infraction::ATTACKING_WHEN_HIDDEN_FROM_PLAYER_LIST, "Attacked someone while being hidden from the player list"},
{Infraction::SPOOFED_DATA, "Had spoofed data"},
{Infraction::SPOOFED_HOST_TOKEN, "Had spoofed their host token"},
{Infraction::INVALID_PLAYER_MODEL, "Had used an invalid player model"},
{Infraction::SUPER_JUMP, "Had used super jump"},
{Infraction::UNDEAD_OTR, "Had used undead OTR"},
}; };
} }

View File

@ -6,6 +6,7 @@
#include "services/player_database/player_database_service.hpp" #include "services/player_database/player_database_service.hpp"
#include "util/notify.hpp" #include "util/notify.hpp"
#include "util/scripts.hpp" #include "util/scripts.hpp"
#include "util/session.hpp"
#include "util/system.hpp" #include "util/system.hpp"
#include "util/teleport.hpp" #include "util/teleport.hpp"
@ -100,19 +101,26 @@ namespace lua::network
return -1; return -1;
} }
static void flag_player_as_modder(int player_idx, big::Infraction reason)
{
if (auto player = big::g_player_service->get_by_id(player_idx))
{
big::session::add_infraction(player, reason);
}
}
// Lua API: Function // Lua API: Function
// Table: network // Table: network
// Name: flag_player_as_modder // Name: flag_player_as_modder
// Param: player_idx: integer: Index of the player. // Param: player_idx: integer: Index of the player.
// Param: reason: Infraction: Reason why the player is flagged as a modder, if the infraction is CUSTOM_REASON, then the custom_reason string is added in the local database. For a full list of the possible infraction reasons to use, please check the infraction page.
// Param: custom_reason: string: Optional, required only when the infraction is CUSTOM_REASON. The custom reason why the player is flagged as a modder.
// Flags the given player as a modder in our local database. // Flags the given player as a modder in our local database.
static void flag_player_as_modder(int player_idx) static void flag_player_as_modder_custom_reason(int player_idx, big::Infraction reason, const std::string& custom_reason)
{ {
if (auto player = big::g_player_service->get_by_id(player_idx)) if (auto player = big::g_player_service->get_by_id(player_idx))
{ {
auto pers = big::g_player_database_service->get_or_create_player(player); big::session::add_infraction(player, reason, custom_reason);
pers->is_modder = true;
big::g_player_database_service->save();
player->is_modder = true;
} }
} }
@ -129,6 +137,21 @@ namespace lua::network
return false; return false;
} }
// Lua API: Function
// Table: network
// Name: get_flagged_modder_reason
// Param: player_idx: integer: Index of the player.
// Returns: string: Returns a string which contains the reason(s) why the player is flagged as a modder.
static std::string get_flagged_modder_reason(int player_idx)
{
if (auto player = big::g_player_service->get_by_id(player_idx))
{
return big::g_player_database_service->get_or_create_player(player)->get_all_infraction_descriptions();
}
return "";
}
// Lua API: Function // Lua API: Function
// Table: network // Table: network
// Name: force_script_host // Name: force_script_host
@ -158,16 +181,39 @@ namespace lua::network
void bind(sol::state& state) void bind(sol::state& state)
{ {
auto ns = state["network"].get_or_create<sol::table>(); state.new_enum<big::Infraction>("infraction",
{
{"ATTACKING_WHEN_HIDDEN_FROM_PLAYER_LIST", big::Infraction::ATTACKING_WHEN_HIDDEN_FROM_PLAYER_LIST},
{"ATTACKING_WITH_GODMODE", big::Infraction::ATTACKING_WITH_GODMODE},
{"ATTACKING_WITH_INVISIBILITY", big::Infraction::ATTACKING_WITH_INVISIBILITY},
{"BLAME_EXPLOSION_DETECTED", big::Infraction::BLAME_EXPLOSION_DETECTED},
{"BREAKUP_KICK_DETECTED", big::Infraction::BREAKUP_KICK_DETECTED},
{"CUSTOM_REASON", big::Infraction::CUSTOM_REASON},
{"DESYNC_PROTECTION", big::Infraction::DESYNC_PROTECTION},
{"INVALID_PLAYER_MODEL", big::Infraction::INVALID_PLAYER_MODEL},
{"LOST_CONNECTION_KICK_DETECTED", big::Infraction::LOST_CONNECTION_KICK_DETECTED},
{"SPOOFED_DATA", big::Infraction::SPOOFED_DATA},
{"SPOOFED_HOST_TOKEN", big::Infraction::SPOOFED_HOST_TOKEN},
{"SPOOFED_ROCKSTAR_ID", big::Infraction::SPOOFED_ROCKSTAR_ID},
{"SUPER_JUMP", big::Infraction::SUPER_JUMP},
{"TRIED_CRASH_PLAYER", big::Infraction::TRIED_CRASH_PLAYER},
{"TRIED_KICK_PLAYER", big::Infraction::TRIED_KICK_PLAYER},
{"TRIGGERED_ANTICHEAT", big::Infraction::TRIGGERED_ANTICHEAT},
{"UNDEAD_OTR", big::Infraction::UNDEAD_OTR},
});
auto ns = state["network"].get_or_create<sol::table>();
ns["trigger_script_event"] = trigger_script_event; ns["trigger_script_event"] = trigger_script_event;
ns["give_pickup_rewards"] = give_pickup_rewards; ns["give_pickup_rewards"] = give_pickup_rewards;
ns["set_player_coords"] = set_player_coords; ns["set_player_coords"] = set_player_coords;
ns["set_all_player_coords"] = set_all_player_coords; ns["set_all_player_coords"] = set_all_player_coords;
ns["get_selected_player"] = get_selected_player; ns["get_selected_player"] = get_selected_player;
ns["get_selected_database_player_rockstar_id"] = get_selected_database_player_rockstar_id; ns["get_selected_database_player_rockstar_id"] = get_selected_database_player_rockstar_id;
ns["flag_player_as_modder"] = flag_player_as_modder; ns["flag_player_as_modder"] = sol::overload(flag_player_as_modder, flag_player_as_modder_custom_reason);
ns["is_player_flagged_as_modder"] = is_player_flagged_as_modder; ns["is_player_flagged_as_modder"] = is_player_flagged_as_modder;
ns["force_script_host"] = force_script_host; ns["get_flagged_modder_reason"] = get_flagged_modder_reason;
ns["send_chat_message"] = send_chat_message; ns["force_script_host"] = force_script_host;
ns["send_chat_message"] = send_chat_message;
} }
} }

View File

@ -0,0 +1,63 @@
#pragma once
#include "persistent_player.hpp"
#include "core/data/infractions.hpp"
namespace big
{
static std::unordered_map<Infraction, const char*> infraction_desc = {
{Infraction::DESYNC_PROTECTION, "Used desync protections"},
{Infraction::BREAKUP_KICK_DETECTED, "Kicked someone using breakup kick"},
{Infraction::LOST_CONNECTION_KICK_DETECTED, "Tried to kick someone using lost connection kick"},
{Infraction::SPOOFED_ROCKSTAR_ID, "Had spoofed RID"},
{Infraction::TRIGGERED_ANTICHEAT, "Triggered Rockstar's anticheat"},
{Infraction::TRIED_CRASH_PLAYER, "Tried to crash you"},
{Infraction::TRIED_KICK_PLAYER, "Tried to kick you"},
{Infraction::BLAME_EXPLOSION_DETECTED, "Tried to blame someone for their explosion"},
{Infraction::ATTACKING_WITH_GODMODE, "Attacked someone while using godmode"},
{Infraction::ATTACKING_WITH_INVISIBILITY, "Attacked someone while being invisible"},
{Infraction::ATTACKING_WHEN_HIDDEN_FROM_PLAYER_LIST, "Attacked someone while being hidden from the player list"},
{Infraction::SPOOFED_DATA, "Had spoofed data"},
{Infraction::SPOOFED_HOST_TOKEN, "Had spoofed their host token"},
{Infraction::INVALID_PLAYER_MODEL, "Had used an invalid player model"},
{Infraction::SUPER_JUMP, "Had used super jump"},
{Infraction::UNDEAD_OTR, "Had used undead OTR"},
};
const char* persistent_player::get_infraction_description(int infraction)
{
if (static_cast<Infraction>(infraction) == Infraction::CUSTOM_REASON)
{
return custom_infraction_reason.c_str();
}
return infraction_desc[static_cast<Infraction>(infraction)];
}
std::string persistent_player::get_all_infraction_descriptions()
{
if (!infractions.size())
{
return "";
}
constexpr auto separator = ", ";
constexpr size_t separator_length = (std::string(separator)).size();
std::string s;
for (const auto infr : infractions)
{
s += get_infraction_description(infr);
s += separator;
}
// Remove last useless separator
for (size_t i = 0; i < separator_length; i++)
{
s.pop_back();
}
return s;
}
}

View File

@ -1,5 +1,4 @@
#pragma once #pragma once
#include "core/data/infractions.hpp"
#include "json_util.hpp" #include "json_util.hpp"
#include <unordered_set> #include <unordered_set>
@ -43,12 +42,13 @@ namespace big
struct persistent_player struct persistent_player
{ {
std::string name; std::string name;
uint64_t rockstar_id = 0; uint64_t rockstar_id = 0;
bool block_join = false; bool block_join = false;
int block_join_reason = 1; int block_join_reason = 1;
bool is_modder = false; bool is_modder = false;
bool notify_online = false; bool notify_online = false;
std::unordered_set<int> infractions; std::unordered_set<int> infractions;
std::string custom_infraction_reason = "";
std::string notes = ""; std::string notes = "";
std::optional<CommandAccessLevel> command_access_level = std::nullopt; std::optional<CommandAccessLevel> command_access_level = std::nullopt;
bool join_redirect = false; bool join_redirect = false;
@ -66,7 +66,9 @@ namespace big
std::string game_mode_id = ""; std::string game_mode_id = "";
rage::rlSessionInfo redirect_info{}; rage::rlSessionInfo redirect_info{};
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(persistent_player, name, rockstar_id, block_join, block_join_reason, is_modder, notify_online, infractions, notes, command_access_level, join_redirect, join_redirect_preference) NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(persistent_player, name, rockstar_id, block_join, block_join_reason, is_modder, notify_online, infractions, custom_infraction_reason, notes, command_access_level, join_redirect, join_redirect_preference)
};
const char* get_infraction_description(int infraction);
std::string get_all_infraction_descriptions();
};
}; };

View File

@ -1,4 +1,5 @@
#pragma once #pragma once
#include "core/data/infractions.hpp"
#include "core/data/session_types.hpp" #include "core/data/session_types.hpp"
#include "fiber_pool.hpp" #include "fiber_pool.hpp"
#include "gta/joaat.hpp" #include "gta/joaat.hpp"
@ -48,7 +49,7 @@ namespace big::session
else else
{ {
*scr_globals::session.at(2).as<int*>() = 0; *scr_globals::session.at(2).as<int*>() = 0;
*scr_globals::session2.as<int*>() = (int)session; *scr_globals::session2.as<int*>() = (int)session;
} }
*scr_globals::session.as<int*>() = 1; *scr_globals::session.as<int*>() = 1;
@ -63,7 +64,7 @@ namespace big::session
} }
scr_functions::reset_session_data({true, true}); scr_functions::reset_session_data({true, true});
*scr_globals::session3.as<int*>() = 0; *scr_globals::session3.as<int*>() = 0;
*scr_globals::session4.as<int*>() = 1; *scr_globals::session4.as<int*>() = 1;
*scr_globals::session5.as<int*>() = 32; *scr_globals::session5.as<int*>() = 32;
@ -158,7 +159,7 @@ namespace big::session
}); });
} }
inline void add_infraction(player_ptr player, Infraction infraction) inline void add_infraction(player_ptr player, Infraction infraction, const std::string& custom_reason = "")
{ {
if (g.debug.fuzzer.enabled) if (g.debug.fuzzer.enabled)
return; return;
@ -168,8 +169,15 @@ namespace big::session
{ {
plyr->is_modder = true; plyr->is_modder = true;
player->is_modder = true; player->is_modder = true;
plyr->infractions.insert((int)infraction); plyr->infractions.insert((int)infraction);
if (infraction == Infraction::CUSTOM_REASON)
{
plyr->custom_infraction_reason += plyr->custom_infraction_reason.size() ? (std::string(", ") + custom_reason) : custom_reason;
}
g_player_database_service->save(); g_player_database_service->save();
g.reactions.modder_detection.process(player); g.reactions.modder_detection.process(player);
} }
} }

View File

@ -182,7 +182,7 @@ namespace big
for (auto& infraction : current_player->infractions) for (auto& infraction : current_player->infractions)
{ {
ImGui::BulletText(infraction_desc[(Infraction)infraction]); ImGui::BulletText(current_player->get_infraction_description(infraction));
} }
} }

View File

@ -71,7 +71,7 @@ namespace big
{ {
ImGui::BeginTooltip(); ImGui::BeginTooltip();
for (auto infraction : sorted_player->infractions) for (auto infraction : sorted_player->infractions)
ImGui::BulletText(infraction_desc[(Infraction)infraction]); ImGui::BulletText(sorted_player->get_infraction_description(infraction));
ImGui::EndTooltip(); ImGui::EndTooltip();
} }
} }