mirror of
https://github.com/Detanup01/gbe_fork.git
synced 2024-11-27 13:14:01 +08:00
(RIN forum) added new release 4 by ce20fdf2 from https://cs.rin.ru/forum/viewtopic.php?p=2933673#p2933673
This commit is contained in:
parent
b01a7ee116
commit
62a92e5bf6
@ -100,6 +100,7 @@ struct Group_Clans {
|
|||||||
|
|
||||||
struct Overlay_Appearance {
|
struct Overlay_Appearance {
|
||||||
float font_size = 16.0;
|
float font_size = 16.0;
|
||||||
|
float icon_size = 64.0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Settings {
|
class Settings {
|
||||||
@ -217,6 +218,9 @@ public:
|
|||||||
|
|
||||||
//steamhttp external download support
|
//steamhttp external download support
|
||||||
bool http_online = false;
|
bool http_online = false;
|
||||||
|
|
||||||
|
//steam deck flag
|
||||||
|
bool steam_deck = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -105,6 +105,10 @@ static void load_overlay_appearance(std::string appearance_filepath, Settings *s
|
|||||||
float nfont_size = std::stoull(value, NULL, 0);
|
float nfont_size = std::stoull(value, NULL, 0);
|
||||||
settings_client->overlay_appearance.font_size = nfont_size;
|
settings_client->overlay_appearance.font_size = nfont_size;
|
||||||
settings_server->overlay_appearance.font_size = nfont_size;
|
settings_server->overlay_appearance.font_size = nfont_size;
|
||||||
|
} else if (name.compare("Icon_Size") == 0) {
|
||||||
|
float nicon_size = std::stoull(value, NULL, 0);
|
||||||
|
settings_client->overlay_appearance.icon_size = nicon_size;
|
||||||
|
settings_server->overlay_appearance.icon_size = nicon_size;
|
||||||
}
|
}
|
||||||
PRINT_DEBUG("Overlay appearance %s %s\n", name.c_str(), value.c_str());
|
PRINT_DEBUG("Overlay appearance %s %s\n", name.c_str(), value.c_str());
|
||||||
} catch (...) {}
|
} catch (...) {}
|
||||||
@ -253,7 +257,7 @@ uint32 create_localstorage_settings(Settings **settings_client_out, Settings **s
|
|||||||
bool local_save = false;
|
bool local_save = false;
|
||||||
|
|
||||||
{
|
{
|
||||||
char array[33] = {};
|
char array[256] = {};
|
||||||
if (Local_Storage::get_file_data(program_path + "local_save.txt", array, sizeof(array) - 1) != -1) {
|
if (Local_Storage::get_file_data(program_path + "local_save.txt", array, sizeof(array) - 1) != -1) {
|
||||||
save_path = program_path + Settings::sanitize(array);
|
save_path = program_path + Settings::sanitize(array);
|
||||||
local_save = true;
|
local_save = true;
|
||||||
@ -366,6 +370,7 @@ uint32 create_localstorage_settings(Settings **settings_client_out, Settings **s
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool steam_offline_mode = false;
|
bool steam_offline_mode = false;
|
||||||
|
bool steam_deck_mode = false;
|
||||||
bool steamhttp_online_mode = false;
|
bool steamhttp_online_mode = false;
|
||||||
bool disable_networking = false;
|
bool disable_networking = false;
|
||||||
bool disable_overlay = false;
|
bool disable_overlay = false;
|
||||||
@ -386,6 +391,8 @@ uint32 create_localstorage_settings(Settings **settings_client_out, Settings **s
|
|||||||
PRINT_DEBUG("steam settings path %s\n", p.c_str());
|
PRINT_DEBUG("steam settings path %s\n", p.c_str());
|
||||||
if (p == "offline.txt") {
|
if (p == "offline.txt") {
|
||||||
steam_offline_mode = true;
|
steam_offline_mode = true;
|
||||||
|
} else if (p == "steam_deck.txt") {
|
||||||
|
steam_deck_mode = true;
|
||||||
} else if (p == "http_online.txt") {
|
} else if (p == "http_online.txt") {
|
||||||
steamhttp_online_mode = true;
|
steamhttp_online_mode = true;
|
||||||
} else if (p == "disable_networking.txt") {
|
} else if (p == "disable_networking.txt") {
|
||||||
@ -466,6 +473,8 @@ uint32 create_localstorage_settings(Settings **settings_client_out, Settings **s
|
|||||||
settings_server->warn_local_save = local_save;
|
settings_server->warn_local_save = local_save;
|
||||||
settings_client->supported_languages = supported_languages;
|
settings_client->supported_languages = supported_languages;
|
||||||
settings_server->supported_languages = supported_languages;
|
settings_server->supported_languages = supported_languages;
|
||||||
|
settings_client->steam_deck = steam_deck_mode;
|
||||||
|
settings_server->steam_deck = steam_deck_mode;
|
||||||
settings_client->http_online = steamhttp_online_mode;
|
settings_client->http_online = steamhttp_online_mode;
|
||||||
settings_server->http_online = steamhttp_online_mode;
|
settings_server->http_online = steamhttp_online_mode;
|
||||||
|
|
||||||
|
@ -597,6 +597,22 @@ int GetAchievementIcon( const char *pchName )
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string get_achievement_icon_name( const char *pchName, bool pbAchieved )
|
||||||
|
{
|
||||||
|
if (pchName == nullptr) return "";
|
||||||
|
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||||
|
|
||||||
|
try {
|
||||||
|
auto it = defined_achievements_find(pchName);
|
||||||
|
if (it != defined_achievements.end()) {
|
||||||
|
if (pbAchieved) return it.value()["icon"].get<std::string>();
|
||||||
|
else return it.value()["icon_gray"].get<std::string>();
|
||||||
|
}
|
||||||
|
} catch (...) {}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Get general attributes for an achievement. Accepts the following keys:
|
// Get general attributes for an achievement. Accepts the following keys:
|
||||||
// - "name" and "desc" for retrieving the localized achievement name and description (returned in UTF8)
|
// - "name" and "desc" for retrieving the localized achievement name and description (returned in UTF8)
|
||||||
|
@ -417,8 +417,8 @@ ESteamIPv6ConnectivityState GetIPv6ConnectivityState( ESteamIPv6ConnectivityProt
|
|||||||
// returns true if currently running on the Steam Deck device
|
// returns true if currently running on the Steam Deck device
|
||||||
bool IsSteamRunningOnSteamDeck()
|
bool IsSteamRunningOnSteamDeck()
|
||||||
{
|
{
|
||||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
PRINT_DEBUG("%s %i\n", __FUNCTION__, (int)settings->steam_deck);
|
||||||
return false;
|
return settings->steam_deck;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Opens a floating keyboard over the game content and sends OS keyboard keys directly to the game.
|
// Opens a floating keyboard over the game content and sends OS keyboard keys directly to the game.
|
||||||
|
@ -1 +1,2 @@
|
|||||||
Font_Size 16.0
|
Font_Size 16.0
|
||||||
|
Icon_Size 64.0
|
@ -0,0 +1 @@
|
|||||||
|
Rename this to: steam_deck.txt to pretend steam is running on a steam deck.
|
@ -101,6 +101,10 @@ int find_free_notification_id(std::vector<Notification> const& notifications)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "notification.h"
|
#include "notification.h"
|
||||||
|
char *notif_achievement_wav_custom;
|
||||||
|
char *notif_invite_wav_custom;
|
||||||
|
bool notif_achievement_wav_custom_inuse = false;
|
||||||
|
bool notif_invite_wav_custom_inuse = false;
|
||||||
|
|
||||||
void Steam_Overlay::steam_overlay_run_every_runcb(void* object)
|
void Steam_Overlay::steam_overlay_run_every_runcb(void* object)
|
||||||
{
|
{
|
||||||
@ -330,7 +334,24 @@ void Steam_Overlay::NotifyUser(friend_window_state& friend_state)
|
|||||||
{
|
{
|
||||||
friend_state.window_state |= window_state_need_attention;
|
friend_state.window_state |= window_state_need_attention;
|
||||||
#ifdef __WINDOWS__
|
#ifdef __WINDOWS__
|
||||||
PlaySound((LPCSTR)notif_invite_wav, NULL, SND_ASYNC | SND_MEMORY);
|
if (notif_invite_wav_custom_inuse) {
|
||||||
|
PlaySoundA((LPCSTR)notif_invite_wav_custom, NULL, SND_ASYNC | SND_MEMORY);
|
||||||
|
} else {
|
||||||
|
PlaySoundA((LPCSTR)notif_invite_wav, NULL, SND_ASYNC | SND_MEMORY);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Steam_Overlay::NotifyUserAchievement()
|
||||||
|
{
|
||||||
|
if (settings->disable_overlay_achievement_notification) return;
|
||||||
|
if (!show_overlay)
|
||||||
|
{
|
||||||
|
#ifdef __WINDOWS__
|
||||||
|
if (notif_achievement_wav_custom_inuse) {
|
||||||
|
PlaySoundA((LPCSTR)notif_achievement_wav_custom, NULL, SND_ASYNC | SND_MEMORY);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -427,10 +448,21 @@ void Steam_Overlay::AddAchievementNotification(nlohmann::json const& ach)
|
|||||||
Notification notif;
|
Notification notif;
|
||||||
notif.id = id;
|
notif.id = id;
|
||||||
notif.type = notification_type_achievement;
|
notif.type = notification_type_achievement;
|
||||||
|
|
||||||
// Load achievement image
|
// Load achievement image
|
||||||
|
std::string file_path = Local_Storage::get_game_settings_path() + ach["icon"].get<std::string>();
|
||||||
|
unsigned long long file_size = file_size_(file_path);
|
||||||
|
if (file_size) {
|
||||||
|
std::string img = Local_Storage::load_image_resized(file_path, "", settings->overlay_appearance.icon_size);
|
||||||
|
if (img.length() > 0) {
|
||||||
|
if (_renderer) notif.icon = _renderer->CreateImageResource((void*)img.c_str(), settings->overlay_appearance.icon_size, settings->overlay_appearance.icon_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
notif.message = ach["displayName"].get<std::string>() + "\n" + ach["description"].get<std::string>();
|
notif.message = ach["displayName"].get<std::string>() + "\n" + ach["description"].get<std::string>();
|
||||||
notif.start_time = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch());
|
notif.start_time = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch());
|
||||||
notifications.emplace_back(notif);
|
notifications.emplace_back(notif);
|
||||||
|
NotifyUserAchievement();
|
||||||
have_notifications = true;
|
have_notifications = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -690,7 +722,24 @@ void Steam_Overlay::BuildNotifications(int width, int height)
|
|||||||
switch (it->type)
|
switch (it->type)
|
||||||
{
|
{
|
||||||
case notification_type_achievement:
|
case notification_type_achievement:
|
||||||
|
{
|
||||||
|
if (!it->icon.expired()) {
|
||||||
|
ImGui::BeginTable("imgui_table", 2);
|
||||||
|
ImGui::TableSetupColumn("imgui_table_image", ImGuiTableColumnFlags_WidthFixed, settings->overlay_appearance.icon_size);
|
||||||
|
ImGui::TableSetupColumn("imgui_table_text");
|
||||||
|
ImGui::TableNextRow(ImGuiTableRowFlags_None, settings->overlay_appearance.icon_size);
|
||||||
|
|
||||||
|
ImGui::TableSetColumnIndex(0);
|
||||||
|
ImGui::Image((ImU64)*it->icon.lock().get(), ImVec2(settings->overlay_appearance.icon_size, settings->overlay_appearance.icon_size));
|
||||||
|
|
||||||
|
ImGui::TableSetColumnIndex(1);
|
||||||
ImGui::TextWrapped("%s", it->message.c_str());
|
ImGui::TextWrapped("%s", it->message.c_str());
|
||||||
|
|
||||||
|
ImGui::EndTable();
|
||||||
|
} else {
|
||||||
|
ImGui::TextWrapped("%s", it->message.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case notification_type_invite:
|
case notification_type_invite:
|
||||||
{
|
{
|
||||||
@ -826,6 +875,51 @@ void Steam_Overlay::CreateFonts()
|
|||||||
reset_LastError();
|
reset_LastError();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Steam_Overlay::LoadAudio()
|
||||||
|
{
|
||||||
|
std::string file_path;
|
||||||
|
std::string file_name;
|
||||||
|
unsigned long long file_size;
|
||||||
|
|
||||||
|
for (int i = 0; i < 2; i++) {
|
||||||
|
if (i == 0) file_name = "overlay_achievement_notification.wav";
|
||||||
|
if (i == 1) file_name = "overlay_friend_notification.wav";
|
||||||
|
|
||||||
|
file_path = Local_Storage::get_game_settings_path() + file_name;
|
||||||
|
file_size = file_size_(file_path);
|
||||||
|
if (!file_size) {
|
||||||
|
if (settings->local_save.length() > 0) {
|
||||||
|
file_path = settings->local_save + "/settings/" + file_name;
|
||||||
|
} else {
|
||||||
|
file_path = Local_Storage::get_user_appdata_path() + "/settings/" + file_name;
|
||||||
|
}
|
||||||
|
file_size = file_size_(file_path);
|
||||||
|
}
|
||||||
|
if (file_size) {
|
||||||
|
std::ifstream myfile;
|
||||||
|
myfile.open(utf8_decode(file_path), std::ios::binary | std::ios::in);
|
||||||
|
if (myfile.is_open()) {
|
||||||
|
myfile.seekg (0, myfile.end);
|
||||||
|
int length = myfile.tellg();
|
||||||
|
myfile.seekg (0, myfile.beg);
|
||||||
|
|
||||||
|
if (i == 0) {
|
||||||
|
notif_achievement_wav_custom = new char [length];
|
||||||
|
myfile.read (notif_achievement_wav_custom, length);
|
||||||
|
notif_achievement_wav_custom_inuse = true;
|
||||||
|
}
|
||||||
|
if (i == 1) {
|
||||||
|
notif_invite_wav_custom = new char [length];
|
||||||
|
myfile.read (notif_invite_wav_custom, length);
|
||||||
|
notif_invite_wav_custom_inuse = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
myfile.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Try to make this function as short as possible or it might affect game's fps.
|
// Try to make this function as short as possible or it might affect game's fps.
|
||||||
void Steam_Overlay::OverlayProc()
|
void Steam_Overlay::OverlayProc()
|
||||||
{
|
{
|
||||||
@ -854,7 +948,15 @@ void Steam_Overlay::OverlayProc()
|
|||||||
|
|
||||||
bool show = true;
|
bool show = true;
|
||||||
|
|
||||||
if (ImGui::Begin(translationSteamOverlay[current_language], &show, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoBringToFrontOnFocus))
|
char tmp[TRANSLATION_BUFFER_SIZE];
|
||||||
|
snprintf(tmp, sizeof(tmp), translationRenderer[current_language], (_renderer == nullptr ? "Unknown" : _renderer->GetLibraryName().c_str()));
|
||||||
|
std::string windowTitle;
|
||||||
|
windowTitle.append(translationSteamOverlay[current_language]);
|
||||||
|
windowTitle.append(" (");
|
||||||
|
windowTitle.append(tmp);
|
||||||
|
windowTitle.append(")");
|
||||||
|
|
||||||
|
if (ImGui::Begin(windowTitle.c_str(), &show, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoBringToFrontOnFocus))
|
||||||
{
|
{
|
||||||
ImGui::LabelText("##label", translationUserPlaying[current_language],
|
ImGui::LabelText("##label", translationUserPlaying[current_language],
|
||||||
settings->get_local_name(),
|
settings->get_local_name(),
|
||||||
@ -862,8 +964,6 @@ void Steam_Overlay::OverlayProc()
|
|||||||
settings->get_local_game_id().AppID());
|
settings->get_local_game_id().AppID());
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
|
|
||||||
ImGui::LabelText("##label", translationRenderer[current_language], (_renderer == nullptr ? "Unknown" : _renderer->GetLibraryName().c_str()));
|
|
||||||
|
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
if (ImGui::Button(translationShowAchievements[current_language])) {
|
if (ImGui::Button(translationShowAchievements[current_language])) {
|
||||||
show_achievements = true;
|
show_achievements = true;
|
||||||
@ -913,8 +1013,34 @@ void Steam_Overlay::OverlayProc()
|
|||||||
bool achieved = x.achieved;
|
bool achieved = x.achieved;
|
||||||
bool hidden = x.hidden && !achieved;
|
bool hidden = x.hidden && !achieved;
|
||||||
|
|
||||||
|
if (x.icon.expired()) {
|
||||||
|
std::string file_path = Local_Storage::get_game_settings_path() + x.icon_name;
|
||||||
|
unsigned long long file_size = file_size_(file_path);
|
||||||
|
if (file_size) {
|
||||||
|
std::string img = Local_Storage::load_image_resized(file_path, "", settings->overlay_appearance.icon_size);
|
||||||
|
if (img.length() > 0) {
|
||||||
|
if (_renderer) x.icon = _renderer->CreateImageResource((void*)img.c_str(), settings->overlay_appearance.icon_size, settings->overlay_appearance.icon_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
|
|
||||||
|
if (!x.icon.expired()) {
|
||||||
|
ImGui::BeginTable(x.title.c_str(), 2);
|
||||||
|
ImGui::TableSetupColumn("imgui_table_image", ImGuiTableColumnFlags_WidthFixed, settings->overlay_appearance.icon_size);
|
||||||
|
ImGui::TableSetupColumn("imgui_table_text");
|
||||||
|
ImGui::TableNextRow(ImGuiTableRowFlags_None, settings->overlay_appearance.icon_size);
|
||||||
|
|
||||||
|
ImGui::TableSetColumnIndex(0);
|
||||||
|
ImGui::Image((ImU64)*x.icon.lock().get(), ImVec2(settings->overlay_appearance.icon_size, settings->overlay_appearance.icon_size));
|
||||||
|
|
||||||
|
ImGui::TableSetColumnIndex(1);
|
||||||
ImGui::Text("%s", x.title.c_str());
|
ImGui::Text("%s", x.title.c_str());
|
||||||
|
} else {
|
||||||
|
ImGui::Text("%s", x.title.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
if (hidden) {
|
if (hidden) {
|
||||||
ImGui::Text(translationHiddenAchievement[current_language]);
|
ImGui::Text(translationHiddenAchievement[current_language]);
|
||||||
} else {
|
} else {
|
||||||
@ -930,6 +1056,9 @@ void Steam_Overlay::OverlayProc()
|
|||||||
} else {
|
} else {
|
||||||
ImGui::TextColored(ImVec4(255, 0, 0, 255), translationNotAchieved[current_language]);
|
ImGui::TextColored(ImVec4(255, 0, 0, 255), translationNotAchieved[current_language]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!x.icon.expired()) ImGui::EndTable();
|
||||||
|
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
}
|
}
|
||||||
ImGui::EndChild();
|
ImGui::EndChild();
|
||||||
@ -1084,6 +1213,9 @@ void Steam_Overlay::RunCallbacks()
|
|||||||
ach.unlock_time = 0;
|
ach.unlock_time = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (achieved) ach.icon_name = steamUserStats->get_achievement_icon_name(ach.name.c_str(), true);
|
||||||
|
else ach.icon_name = steamUserStats->get_achievement_icon_name(ach.name.c_str(), false);
|
||||||
|
|
||||||
achievements.push_back(ach);
|
achievements.push_back(ach);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1096,6 +1228,7 @@ void Steam_Overlay::RunCallbacks()
|
|||||||
_renderer = future_renderer.get();
|
_renderer = future_renderer.get();
|
||||||
PRINT_DEBUG("got renderer %p\n", _renderer);
|
PRINT_DEBUG("got renderer %p\n", _renderer);
|
||||||
CreateFonts();
|
CreateFonts();
|
||||||
|
LoadAudio();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,6 +68,7 @@ struct Notification
|
|||||||
std::chrono::seconds start_time;
|
std::chrono::seconds start_time;
|
||||||
std::string message;
|
std::string message;
|
||||||
std::pair<const Friend, friend_window_state>* frd;
|
std::pair<const Friend, friend_window_state>* frd;
|
||||||
|
std::weak_ptr<uint64_t> icon;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Overlay_Achievement
|
struct Overlay_Achievement
|
||||||
@ -75,9 +76,11 @@ struct Overlay_Achievement
|
|||||||
std::string name;
|
std::string name;
|
||||||
std::string title;
|
std::string title;
|
||||||
std::string description;
|
std::string description;
|
||||||
|
std::string icon_name;
|
||||||
bool hidden;
|
bool hidden;
|
||||||
bool achieved;
|
bool achieved;
|
||||||
uint32 unlock_time;
|
uint32 unlock_time;
|
||||||
|
std::weak_ptr<uint64_t> icon;
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef EMU_OVERLAY
|
#ifdef EMU_OVERLAY
|
||||||
@ -142,6 +145,7 @@ class Steam_Overlay
|
|||||||
bool IHaveLobby();
|
bool IHaveLobby();
|
||||||
|
|
||||||
void NotifyUser(friend_window_state& friend_state);
|
void NotifyUser(friend_window_state& friend_state);
|
||||||
|
void NotifyUserAchievement();
|
||||||
|
|
||||||
// Right click on friend
|
// Right click on friend
|
||||||
void BuildContextMenu(Friend const& frd, friend_window_state &state);
|
void BuildContextMenu(Friend const& frd, friend_window_state &state);
|
||||||
@ -167,6 +171,7 @@ public:
|
|||||||
void HookReady(bool ready);
|
void HookReady(bool ready);
|
||||||
|
|
||||||
void CreateFonts();
|
void CreateFonts();
|
||||||
|
void LoadAudio();
|
||||||
void OverlayProc();
|
void OverlayProc();
|
||||||
|
|
||||||
void OpenOverlayInvite(CSteamID lobbyId);
|
void OpenOverlayInvite(CSteamID lobbyId);
|
||||||
|
Loading…
Reference in New Issue
Block a user