mirror of
https://github.com/libretro/RetroArch.git
synced 2026-11-09 03:29:43 -10:00
video_driver: consolidate three frame-time reset toggles into one
Replaces the per-event reset toggles
frame_time_counter_reset_after_fastforwarding
frame_time_counter_reset_after_load_state
frame_time_counter_reset_after_save_state
with a single combined bool
frame_time_counter_auto_reset
Rationale
=========
The three toggles all do the same thing -- drain the
'Estimated Screen Refresh Rate' sample buffer when an event
known to introduce contaminated samples completes -- on three
separate events. Splitting them was historical (each added at
the time a particular contaminator was identified) rather than
structurally motivated. As a UI, three identically-described
toggles next to each other is harder to reason about than a
single 'do best-effort cleanup' switch.
This becomes more relevant after the previous gated-sampling
commit: with 'Sample Frame Time Only In Stable State' on, the
buffer never accepts contaminated samples in the first place,
making the reset toggles entirely redundant. Folding them into
one bool and hiding it under that condition keeps the submenu
focused on the user's actual mental model -- either prevent
contamination or clean it up afterwards.
Behaviour
=========
* Default: false (matches the prior aggregate -- all three old
toggles defaulted off).
* When true: drains video_st->frame_time_count = 0 after
fast-forward toggle-off, save state, or load state. Same
three events the previous toggles handled; combined under
one switch.
* When 'video_frame_time_sample_gated' is true, this toggle is
hidden from the menu (still readable from config if the user
set it manually, but irrelevant -- no samples to clean up).
Migration
=========
Old config keys
frame_time_counter_reset_after_fastforwarding
frame_time_counter_reset_after_load_state
frame_time_counter_reset_after_save_state
are silently ignored on load (config parser doesn't warn on
unknown bool keys). Users who had any of them enabled will see
the new combined setting at its default (false) and need to
re-enable once if they want the cleanup behaviour. Acceptable
regression given the prior defaults were all off anyway.
Wiring
======
config.def.h DEFAULT_FRAME_TIME_COUNTER_AUTO_RESET
configuration.{c,h} struct field + SETTING_BOOL
command.c load/save state reset sites
runloop.c fastmotion reset sites
(incl. runloop_apply_fastmotion_override
parameter rename)
msg_hash.h enum
msg_hash_lbl_str.h config key string
intl/msg_hash_us.h 'Auto-Reset After Disruptive Events' +
sublabel describing the relationship to
the gated sampling setting
menu/menu_setting.c CONFIG_BOOL registration (single, replacing
three)
menu/menu_displaylist.c build_list keeps the always-shown items
(REFRESH_RATE_AUTO + GATED toggle); the
AUTO_RESET entry is added via an explicit
conditional PARSE_SETTINGS_ENUM call gated
on !video_frame_time_sample_gated
menu/cbs/menu_cbs_sublabel.c sublabel binding (single, replacing three)
Compile-checked: command.o, runloop.o, configuration.o,
intl/msg_hash_us.o, menu/menu_displaylist.o,
menu/cbs/menu_cbs_sublabel.o all clean. menu/menu_setting.o has
the same pre-existing DEFAULT_PLAYLIST_SHOW_INLINE_CORE_NAME
sandbox build failure as before, unrelated.
This commit is contained in:
1 parent
d22e84cd1f
commit
fb7dcadb01
11 files changed
+52
-92
No files matched your search
@@ -88,9 +88,9 @@ static void command_post_state_loaded(void)
|
||||
{
|
||||
video_driver_state_t *video_st =
|
||||
video_state_get_ptr();
|
||||
bool frame_time_counter_reset_after_load_state =
|
||||
config_get_ptr()->bools.frame_time_counter_reset_after_load_state;
|
||||
if (frame_time_counter_reset_after_load_state)
|
||||
bool frame_time_counter_auto_reset =
|
||||
config_get_ptr()->bools.frame_time_counter_auto_reset;
|
||||
if (frame_time_counter_auto_reset)
|
||||
video_st->frame_time_count = 0;
|
||||
}
|
||||
#if defined(HAVE_GFX_WIDGETS) && defined(HAVE_SCREENSHOTS)
|
||||
@@ -2413,8 +2413,8 @@ bool command_event_main_state(unsigned cmd)
|
||||
settings->bools.savestate_auto_index;
|
||||
unsigned savestate_max_keep =
|
||||
settings->uints.savestate_max_keep;
|
||||
bool frame_time_counter_reset_after_save_state =
|
||||
settings->bools.frame_time_counter_reset_after_save_state;
|
||||
bool frame_time_counter_auto_reset =
|
||||
settings->bools.frame_time_counter_auto_reset;
|
||||
|
||||
if (cmd == CMD_EVENT_SAVE_STATE)
|
||||
content_save_state(state_path, true);
|
||||
@@ -2425,7 +2425,7 @@ bool command_event_main_state(unsigned cmd)
|
||||
if (savestate_auto_index && (savestate_max_keep > 0))
|
||||
command_event_set_savestate_garbage_collect(settings);
|
||||
|
||||
if (frame_time_counter_reset_after_save_state)
|
||||
if (frame_time_counter_auto_reset)
|
||||
video_st->frame_time_count = 0;
|
||||
|
||||
ret = true;
|
||||
|
||||
@@ -431,6 +431,19 @@
|
||||
* of slower convergence after content load. */
|
||||
#define DEFAULT_FRAME_TIME_SAMPLE_GATED false
|
||||
|
||||
/* When true, drains the 'Estimated Screen Refresh Rate' sample
|
||||
* buffer after fast-forward, save state, or load state -- events
|
||||
* whose timing doesn't reflect normal frame cadence and would
|
||||
* skew the deviation measurement. Best-effort cleanup for users
|
||||
* who haven't enabled DEFAULT_FRAME_TIME_SAMPLE_GATED (which
|
||||
* prevents the contamination at the source).
|
||||
*
|
||||
* Replaces three separate per-event reset toggles
|
||||
* (frame_time_counter_reset_after_{fastforwarding,load_state,
|
||||
* save_state}). Defaults to false to match the prior aggregate
|
||||
* behaviour (all three off). */
|
||||
#define DEFAULT_FRAME_TIME_COUNTER_AUTO_RESET false
|
||||
|
||||
/* Duplicates frames for the purposes of running Shaders at a higher framerate
|
||||
* than content framerate. Requires running screen at multiple of 60hz, and
|
||||
* don't combine with Swap_interval > 1, or BFI. (Though BFI can be done in a shader
|
||||
|
||||
+1
-3
@@ -2266,9 +2266,7 @@ static struct config_bool_setting *populate_settings_bool(
|
||||
SETTING_BOOL("playlist_use_filename", &settings->bools.playlist_use_filename, true, DEFAULT_PLAYLIST_USE_FILENAME, false);
|
||||
SETTING_BOOL("playlist_allow_non_png", &settings->bools.playlist_allow_non_png, true, DEFAULT_PLAYLIST_ALLOW_NON_PNG, false);
|
||||
|
||||
SETTING_BOOL("frame_time_counter_reset_after_fastforwarding", &settings->bools.frame_time_counter_reset_after_fastforwarding, true, false, false);
|
||||
SETTING_BOOL("frame_time_counter_reset_after_load_state", &settings->bools.frame_time_counter_reset_after_load_state, true, false, false);
|
||||
SETTING_BOOL("frame_time_counter_reset_after_save_state", &settings->bools.frame_time_counter_reset_after_save_state, true, false, false);
|
||||
SETTING_BOOL("frame_time_counter_auto_reset", &settings->bools.frame_time_counter_auto_reset, true, DEFAULT_FRAME_TIME_COUNTER_AUTO_RESET, false);
|
||||
|
||||
#ifdef HAVE_COMMAND
|
||||
SETTING_BOOL("network_cmd_enable", &settings->bools.network_cmd_enable, true, DEFAULT_NETWORK_CMD_ENABLE, false);
|
||||
|
||||
+1
-3
@@ -794,9 +794,7 @@ typedef struct settings
|
||||
#endif
|
||||
|
||||
/* Frame time counter */
|
||||
bool frame_time_counter_reset_after_fastforwarding;
|
||||
bool frame_time_counter_reset_after_load_state;
|
||||
bool frame_time_counter_reset_after_save_state;
|
||||
bool frame_time_counter_auto_reset;
|
||||
|
||||
/* Menu */
|
||||
bool menu_enable_widgets;
|
||||
|
||||
+4
-20
@@ -5187,28 +5187,12 @@ MSG_HASH(
|
||||
/* Settings > Frame Throttle > Frame Time Counter */
|
||||
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_FRAME_TIME_COUNTER_RESET_AFTER_FASTFORWARDING,
|
||||
"Reset After Fast-Forward"
|
||||
MENU_ENUM_LABEL_VALUE_FRAME_TIME_COUNTER_AUTO_RESET,
|
||||
"Auto-Reset After Disruptive Events"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_SUBLABEL_FRAME_TIME_COUNTER_RESET_AFTER_FASTFORWARDING,
|
||||
"Reset the frame time counter after fast-forwarding."
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_FRAME_TIME_COUNTER_RESET_AFTER_LOAD_STATE,
|
||||
"Reset After Load State"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_SUBLABEL_FRAME_TIME_COUNTER_RESET_AFTER_LOAD_STATE,
|
||||
"Reset the frame time counter after loading a state."
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_FRAME_TIME_COUNTER_RESET_AFTER_SAVE_STATE,
|
||||
"Reset After Save State"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_SUBLABEL_FRAME_TIME_COUNTER_RESET_AFTER_SAVE_STATE,
|
||||
"Reset the frame time counter after saving a state."
|
||||
MENU_ENUM_SUBLABEL_FRAME_TIME_COUNTER_AUTO_RESET,
|
||||
"Clear the 'Estimated Screen Refresh Rate' sample buffer after fast-forwarding, save state, or load state. These operations introduce timing samples that don't reflect normal frame cadence and would skew the deviation measurement. Best-effort cleanup; has no effect when 'Sample Frame Time Only In Stable State' is enabled (which prevents the contamination at the source)."
|
||||
)
|
||||
|
||||
/* Settings > Recording */
|
||||
|
||||
@@ -303,9 +303,7 @@ DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_user_settings_list, MENU_
|
||||
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_recording_settings_list, MENU_ENUM_SUBLABEL_RECORDING_SETTINGS)
|
||||
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_frame_throttle_settings_list, MENU_ENUM_SUBLABEL_FRAME_THROTTLE_SETTINGS)
|
||||
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_frame_time_counter_settings_list, MENU_ENUM_SUBLABEL_FRAME_TIME_COUNTER_SETTINGS)
|
||||
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_frame_time_counter_reset_after_fastforwarding, MENU_ENUM_SUBLABEL_FRAME_TIME_COUNTER_RESET_AFTER_FASTFORWARDING)
|
||||
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_frame_time_counter_reset_after_load_state, MENU_ENUM_SUBLABEL_FRAME_TIME_COUNTER_RESET_AFTER_LOAD_STATE)
|
||||
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_frame_time_counter_reset_after_save_state, MENU_ENUM_SUBLABEL_FRAME_TIME_COUNTER_RESET_AFTER_SAVE_STATE)
|
||||
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_frame_time_counter_auto_reset, MENU_ENUM_SUBLABEL_FRAME_TIME_COUNTER_AUTO_RESET)
|
||||
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_onscreen_display_settings_list,MENU_ENUM_SUBLABEL_ONSCREEN_DISPLAY_SETTINGS)
|
||||
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_core_settings_list, MENU_ENUM_SUBLABEL_CORE_SETTINGS)
|
||||
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_information_list_list, MENU_ENUM_SUBLABEL_INFORMATION_LIST_LIST)
|
||||
@@ -2921,14 +2919,8 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs,
|
||||
case MENU_ENUM_LABEL_SETTINGS_SHOW_FRAME_THROTTLE:
|
||||
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_settings_show_frame_throttle);
|
||||
break;
|
||||
case MENU_ENUM_LABEL_FRAME_TIME_COUNTER_RESET_AFTER_FASTFORWARDING:
|
||||
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_frame_time_counter_reset_after_fastforwarding);
|
||||
break;
|
||||
case MENU_ENUM_LABEL_FRAME_TIME_COUNTER_RESET_AFTER_LOAD_STATE:
|
||||
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_frame_time_counter_reset_after_load_state);
|
||||
break;
|
||||
case MENU_ENUM_LABEL_FRAME_TIME_COUNTER_RESET_AFTER_SAVE_STATE:
|
||||
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_frame_time_counter_reset_after_save_state);
|
||||
case MENU_ENUM_LABEL_FRAME_TIME_COUNTER_AUTO_RESET:
|
||||
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_frame_time_counter_auto_reset);
|
||||
break;
|
||||
case MENU_ENUM_LABEL_SETTINGS_SHOW_RECORDING:
|
||||
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_settings_show_recording);
|
||||
|
||||
+12
-3
@@ -11935,9 +11935,6 @@ unsigned menu_displaylist_build_list(
|
||||
static menu_displaylist_build_info_selective_t build_list[] = {
|
||||
{MENU_ENUM_LABEL_VIDEO_REFRESH_RATE_AUTO, PARSE_ONLY_FLOAT, true},
|
||||
{MENU_ENUM_LABEL_VIDEO_FRAME_TIME_SAMPLE_GATED, PARSE_ONLY_BOOL, true},
|
||||
{MENU_ENUM_LABEL_FRAME_TIME_COUNTER_RESET_AFTER_FASTFORWARDING, PARSE_ONLY_BOOL, true},
|
||||
{MENU_ENUM_LABEL_FRAME_TIME_COUNTER_RESET_AFTER_LOAD_STATE, PARSE_ONLY_BOOL, true},
|
||||
{MENU_ENUM_LABEL_FRAME_TIME_COUNTER_RESET_AFTER_SAVE_STATE, PARSE_ONLY_BOOL, true},
|
||||
};
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(build_list); i++)
|
||||
@@ -11947,6 +11944,18 @@ unsigned menu_displaylist_build_list(
|
||||
false) == 0)
|
||||
count++;
|
||||
}
|
||||
|
||||
/* Auto-reset toggle is redundant when gated sampling
|
||||
* is active (no contamination enters the buffer to
|
||||
* begin with), so hide it in that case to keep the
|
||||
* menu focused. */
|
||||
if (!settings->bools.video_frame_time_sample_gated)
|
||||
{
|
||||
if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list,
|
||||
MENU_ENUM_LABEL_FRAME_TIME_COUNTER_AUTO_RESET,
|
||||
PARSE_ONLY_BOOL, false) == 0)
|
||||
count++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DISPLAYLIST_REWIND_SETTINGS_LIST:
|
||||
|
||||
+4
-34
@@ -12478,40 +12478,10 @@ static bool setting_append_list(
|
||||
|
||||
CONFIG_BOOL(
|
||||
list, list_info,
|
||||
&settings->bools.frame_time_counter_reset_after_fastforwarding,
|
||||
MENU_ENUM_LABEL_FRAME_TIME_COUNTER_RESET_AFTER_FASTFORWARDING,
|
||||
MENU_ENUM_LABEL_VALUE_FRAME_TIME_COUNTER_RESET_AFTER_FASTFORWARDING,
|
||||
false,
|
||||
MENU_ENUM_LABEL_VALUE_OFF,
|
||||
MENU_ENUM_LABEL_VALUE_ON,
|
||||
&group_info,
|
||||
&subgroup_info,
|
||||
parent_group,
|
||||
general_write_handler,
|
||||
general_read_handler,
|
||||
SD_FLAG_NONE);
|
||||
|
||||
CONFIG_BOOL(
|
||||
list, list_info,
|
||||
&settings->bools.frame_time_counter_reset_after_load_state,
|
||||
MENU_ENUM_LABEL_FRAME_TIME_COUNTER_RESET_AFTER_LOAD_STATE,
|
||||
MENU_ENUM_LABEL_VALUE_FRAME_TIME_COUNTER_RESET_AFTER_LOAD_STATE,
|
||||
false,
|
||||
MENU_ENUM_LABEL_VALUE_OFF,
|
||||
MENU_ENUM_LABEL_VALUE_ON,
|
||||
&group_info,
|
||||
&subgroup_info,
|
||||
parent_group,
|
||||
general_write_handler,
|
||||
general_read_handler,
|
||||
SD_FLAG_NONE);
|
||||
|
||||
CONFIG_BOOL(
|
||||
list, list_info,
|
||||
&settings->bools.frame_time_counter_reset_after_save_state,
|
||||
MENU_ENUM_LABEL_FRAME_TIME_COUNTER_RESET_AFTER_SAVE_STATE,
|
||||
MENU_ENUM_LABEL_VALUE_FRAME_TIME_COUNTER_RESET_AFTER_SAVE_STATE,
|
||||
false,
|
||||
&settings->bools.frame_time_counter_auto_reset,
|
||||
MENU_ENUM_LABEL_FRAME_TIME_COUNTER_AUTO_RESET,
|
||||
MENU_ENUM_LABEL_VALUE_FRAME_TIME_COUNTER_AUTO_RESET,
|
||||
DEFAULT_FRAME_TIME_COUNTER_AUTO_RESET,
|
||||
MENU_ENUM_LABEL_VALUE_OFF,
|
||||
MENU_ENUM_LABEL_VALUE_ON,
|
||||
&group_info,
|
||||
|
||||
+1
-3
@@ -3233,9 +3233,7 @@ enum msg_hash_enums
|
||||
MENU_LABEL(OVERLAY_SETTINGS),
|
||||
MENU_LABEL(REWIND_SETTINGS),
|
||||
MENU_LABEL(FRAME_TIME_COUNTER_SETTINGS),
|
||||
MENU_LABEL(FRAME_TIME_COUNTER_RESET_AFTER_FASTFORWARDING),
|
||||
MENU_LABEL(FRAME_TIME_COUNTER_RESET_AFTER_LOAD_STATE),
|
||||
MENU_LABEL(FRAME_TIME_COUNTER_RESET_AFTER_SAVE_STATE),
|
||||
MENU_LABEL(FRAME_TIME_COUNTER_AUTO_RESET),
|
||||
MENU_LABEL(CHEAT_SETTINGS),
|
||||
MENU_LABEL(CHEAT_DETAILS_SETTINGS),
|
||||
MENU_LABEL(CHEAT_SEARCH_SETTINGS),
|
||||
|
||||
+1
-3
@@ -1560,9 +1560,7 @@
|
||||
#define MENU_ENUM_LABEL_SETTINGS_SHOW_USER_STR "settings_show_user"
|
||||
#define MENU_ENUM_LABEL_SETTINGS_SHOW_DIRECTORY_STR "settings_show_directory"
|
||||
#define MENU_ENUM_LABEL_SETTINGS_SHOW_STEAM_STR "settings_show_steam"
|
||||
#define MENU_ENUM_LABEL_FRAME_TIME_COUNTER_RESET_AFTER_FASTFORWARDING_STR "frame_time_counter_reset_after_fastforwarding"
|
||||
#define MENU_ENUM_LABEL_FRAME_TIME_COUNTER_RESET_AFTER_LOAD_STATE_STR "frame_time_counter_reset_after_load_state"
|
||||
#define MENU_ENUM_LABEL_FRAME_TIME_COUNTER_RESET_AFTER_SAVE_STATE_STR "frame_time_counter_reset_after_save_state"
|
||||
#define MENU_ENUM_LABEL_FRAME_TIME_COUNTER_AUTO_RESET_STR "frame_time_counter_auto_reset"
|
||||
#define MENU_ENUM_LABEL_DELETE_PLAYLIST_STR "delete_playlist"
|
||||
#define MENU_ENUM_LABEL_LOCALAP_ENABLE_STR "localap_enable"
|
||||
#define MENU_ENUM_LABEL_DRIVER_SWITCH_ENABLE_STR "driver_switch_enable"
|
||||
|
||||
@@ -4008,7 +4008,7 @@ static bool core_unload_game(void)
|
||||
}
|
||||
|
||||
static void runloop_apply_fastmotion_override(runloop_state_t *runloop_st,
|
||||
bool frame_time_counter_reset_after_fastforwarding,
|
||||
bool frame_time_counter_auto_reset,
|
||||
float fastforward_ratio_default,
|
||||
bool audio_fastforward_mute)
|
||||
{
|
||||
@@ -4059,7 +4059,7 @@ static void runloop_apply_fastmotion_override(runloop_state_t *runloop_st,
|
||||
/* Reset frame time counter when toggling
|
||||
* fast-forward off, if required */
|
||||
if ( !(runloop_st->flags & RUNLOOP_FLAG_FASTMOTION)
|
||||
&& frame_time_counter_reset_after_fastforwarding)
|
||||
&& frame_time_counter_auto_reset)
|
||||
video_st->frame_time_count = 0;
|
||||
|
||||
/* Ensure fast forward widget is disabled when
|
||||
@@ -4150,7 +4150,7 @@ void runloop_event_deinit_core(void)
|
||||
if (runloop_st->fastmotion_override.pending)
|
||||
{
|
||||
runloop_apply_fastmotion_override(runloop_st,
|
||||
settings->bools.frame_time_counter_reset_after_fastforwarding,
|
||||
settings->bools.frame_time_counter_auto_reset,
|
||||
settings->floats.fastforward_ratio,
|
||||
settings->bools.audio_fastforward_mute
|
||||
);
|
||||
@@ -6923,7 +6923,7 @@ static enum runloop_state_enum runloop_check_state(
|
||||
if (runloop_st->fastmotion_override.pending)
|
||||
{
|
||||
runloop_apply_fastmotion_override(runloop_st,
|
||||
settings->bools.frame_time_counter_reset_after_fastforwarding,
|
||||
settings->bools.frame_time_counter_auto_reset,
|
||||
settings->floats.fastforward_ratio,
|
||||
settings->bools.audio_fastforward_mute);
|
||||
runloop_st->fastmotion_override.pending = false;
|
||||
@@ -6963,7 +6963,7 @@ static enum runloop_state_enum runloop_check_state(
|
||||
if (check2)
|
||||
{
|
||||
bool audio_fastforward_mute = settings->bools.audio_fastforward_mute;
|
||||
bool frame_time_counter_reset_after_ffwd = settings->bools.frame_time_counter_reset_after_fastforwarding;
|
||||
bool frame_time_counter_auto_reset = settings->bools.frame_time_counter_auto_reset;
|
||||
if (input_st->flags & INP_FLAG_NONBLOCKING)
|
||||
{
|
||||
input_st->flags &= ~INP_FLAG_NONBLOCKING;
|
||||
@@ -6987,7 +6987,7 @@ static enum runloop_state_enum runloop_check_state(
|
||||
/* Reset frame time counter when toggling
|
||||
* fast-forward off, if required */
|
||||
if ( !(runloop_st->flags & RUNLOOP_FLAG_FASTMOTION)
|
||||
&& frame_time_counter_reset_after_ffwd)
|
||||
&& frame_time_counter_auto_reset)
|
||||
video_st->frame_time_count = 0;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user