Infrastructure username handling: Add separate username, pick Nickname as default if valid.

This commit is contained in:
Henrik Rydgård 2025-01-07 17:43:02 +01:00
parent 41e156a326
commit 01b06b76c9
11 changed files with 103 additions and 35 deletions

View file

@ -105,6 +105,38 @@ int countChar(std::string_view haystack, char needle) {
return count; return count;
} }
std::string SanitizeString(std::string_view input, StringRestriction restriction, int maxLength) {
if (restriction == StringRestriction::None) {
return std::string(input);
}
// First, remove any chars not in A-Za-z0-9_-. This will effectively get rid of any Unicode char, emojis etc too.
std::string sanitized;
for (auto c : input) {
switch (restriction) {
case StringRestriction::None:
sanitized.push_back(c);
break;
case StringRestriction::AlphaNumDashUnderscore:
if ((c >= 'A' && c <= 'Z') ||
(c >= 'a' && c <= 'z') ||
(c >= '0' && c <= '9') || c == '-' || c == '_') {
// Allowed chars.
sanitized.push_back(c);
}
break;
}
}
if (maxLength >= 0) {
// TODO: Cut at whole UTF-8 chars!
if (sanitized.size() > maxLength) {
sanitized.resize(maxLength);
}
}
return sanitized;
}
bool CharArrayFromFormatV(char* out, int outsize, const char* format, va_list args) bool CharArrayFromFormatV(char* out, int outsize, const char* format, va_list args)
{ {
int writtenCount = vsnprintf(out, outsize, format, args); int writtenCount = vsnprintf(out, outsize, format, args);

View file

@ -71,6 +71,13 @@ inline bool equalsNoCase(std::string_view str, std::string_view key) {
bool containsNoCase(std::string_view haystack, std::string_view needle); bool containsNoCase(std::string_view haystack, std::string_view needle);
enum class StringRestriction {
None,
AlphaNumDashUnderscore, // Used for infrastructure usernames
};
std::string SanitizeString(std::string_view username, StringRestriction restriction, int maxLength = -1);
void DataToHexString(const uint8_t *data, size_t size, std::string *output); void DataToHexString(const uint8_t *data, size_t size, std::string *output);
void DataToHexString(int indent, uint32_t startAddr, const uint8_t* data, size_t size, std::string* output); void DataToHexString(int indent, uint32_t startAddr, const uint8_t* data, size_t size, std::string* output);

View file

@ -519,7 +519,7 @@ void SliderFloatPopupScreen::OnCompleted(DialogResult result) {
} }
PopupTextInputChoice::PopupTextInputChoice(RequesterToken token, std::string *value, std::string_view title, std::string_view placeholder, int maxLen, ScreenManager *screenManager, LayoutParams *layoutParams) PopupTextInputChoice::PopupTextInputChoice(RequesterToken token, std::string *value, std::string_view title, std::string_view placeholder, int maxLen, ScreenManager *screenManager, LayoutParams *layoutParams)
: AbstractChoiceWithValueDisplay(title, layoutParams), screenManager_(screenManager), value_(value), placeHolder_(placeholder), maxLen_(maxLen), token_(token) { : AbstractChoiceWithValueDisplay(title, layoutParams), screenManager_(screenManager), value_(value), placeHolder_(placeholder), maxLen_(maxLen), token_(token), restriction_(StringRestriction::None) {
OnClick.Handle(this, &PopupTextInputChoice::HandleClick); OnClick.Handle(this, &PopupTextInputChoice::HandleClick);
} }
@ -529,7 +529,7 @@ EventReturn PopupTextInputChoice::HandleClick(EventParams &e) {
// Choose method depending on platform capabilities. // Choose method depending on platform capabilities.
if (System_GetPropertyBool(SYSPROP_HAS_TEXT_INPUT_DIALOG)) { if (System_GetPropertyBool(SYSPROP_HAS_TEXT_INPUT_DIALOG)) {
System_InputBoxGetString(token_, text_, *value_, passwordMasking_, [=](const std::string &enteredValue, int) { System_InputBoxGetString(token_, text_, *value_, passwordMasking_, [=](const std::string &enteredValue, int) {
*value_ = StripSpaces(enteredValue); *value_ = SanitizeString(StripSpaces(enteredValue), restriction_, maxLen_);
EventParams params{}; EventParams params{};
OnChange.Trigger(params); OnChange.Trigger(params);
}); });
@ -553,6 +553,7 @@ std::string PopupTextInputChoice::ValueText() const {
} }
EventReturn PopupTextInputChoice::HandleChange(EventParams &e) { EventReturn PopupTextInputChoice::HandleChange(EventParams &e) {
*value_ = StripSpaces(SanitizeString(*value_, restriction_, maxLen_));
e.v = this; e.v = this;
OnChange.Trigger(e); OnChange.Trigger(e);

View file

@ -8,6 +8,9 @@
#include "Common/UI/View.h" #include "Common/UI/View.h"
#include "Common/UI/ScrollView.h" #include "Common/UI/ScrollView.h"
// from StringUtils
enum class StringRestriction;
namespace UI { namespace UI {
static const float NO_DEFAULT_FLOAT = -1000000.0f; static const float NO_DEFAULT_FLOAT = -1000000.0f;
@ -371,6 +374,10 @@ public:
Event OnChange; Event OnChange;
void SetRestriction(StringRestriction restriction) {
restriction_ = restriction;
}
protected: protected:
std::string ValueText() const override; std::string ValueText() const override;
@ -384,6 +391,7 @@ private:
std::string defaultText_; std::string defaultText_;
int maxLen_; int maxLen_;
bool restoreFocus_ = false; bool restoreFocus_ = false;
StringRestriction restriction_;
}; };
class ChoiceWithValueDisplay : public AbstractChoiceWithValueDisplay { class ChoiceWithValueDisplay : public AbstractChoiceWithValueDisplay {

View file

@ -111,7 +111,7 @@ GPUBackend GPUBackendFromString(std::string_view backend) {
return GPUBackend::OPENGL; return GPUBackend::OPENGL;
} }
const char *DefaultLangRegion() { std::string DefaultLangRegion() {
// Unfortunate default. There's no need to use bFirstRun, since this is only a default. // Unfortunate default. There's no need to use bFirstRun, since this is only a default.
static std::string defaultLangRegion = "en_US"; static std::string defaultLangRegion = "en_US";
std::string langRegion = System_GetProperty(SYSPROP_LANGREGION); std::string langRegion = System_GetProperty(SYSPROP_LANGREGION);
@ -136,7 +136,7 @@ const char *DefaultLangRegion() {
} }
} }
return defaultLangRegion.c_str(); return defaultLangRegion;
} }
static int DefaultDepthRaster() { static int DefaultDepthRaster() {
@ -602,6 +602,20 @@ static std::string FastForwardModeToString(int v) {
return "CONTINUOUS"; return "CONTINUOUS";
} }
static std::string DefaultInfrastructureUsername() {
// If the user has already picked a Nickname that satisfies the rules and is not "PPSSPP",
// let's use that.
// NOTE: This type of dependency means that network settings must be AFTER system settings in sections[].
if (g_Config.sNickName != "PPSSPP" &&
!g_Config.sNickName.empty() &&
g_Config.sNickName == SanitizeString(g_Config.sNickName, StringRestriction::AlphaNumDashUnderscore, 16)) {
return g_Config.sNickName;
}
// Otherwise let's leave it empty, which will result in login failure and a warning.
return std::string();
}
static const ConfigSetting graphicsSettings[] = { static const ConfigSetting graphicsSettings[] = {
ConfigSetting("EnableCardboardVR", &g_Config.bEnableCardboardVR, false, CfgFlag::PER_GAME), ConfigSetting("EnableCardboardVR", &g_Config.bEnableCardboardVR, false, CfgFlag::PER_GAME),
ConfigSetting("CardboardScreenSize", &g_Config.iCardboardScreenSize, 50, CfgFlag::PER_GAME), ConfigSetting("CardboardScreenSize", &g_Config.iCardboardScreenSize, 50, CfgFlag::PER_GAME),
@ -893,6 +907,7 @@ static const ConfigSetting networkSettings[] = {
ConfigSetting("ForcedFirstConnect", &g_Config.bForcedFirstConnect, false, CfgFlag::PER_GAME), ConfigSetting("ForcedFirstConnect", &g_Config.bForcedFirstConnect, false, CfgFlag::PER_GAME),
ConfigSetting("EnableUPnP", &g_Config.bEnableUPnP, false, CfgFlag::PER_GAME), ConfigSetting("EnableUPnP", &g_Config.bEnableUPnP, false, CfgFlag::PER_GAME),
ConfigSetting("UPnPUseOriginalPort", &g_Config.bUPnPUseOriginalPort, false, CfgFlag::PER_GAME), ConfigSetting("UPnPUseOriginalPort", &g_Config.bUPnPUseOriginalPort, false, CfgFlag::PER_GAME),
ConfigSetting("InfrastructureUsername", &g_Config.sInfrastructureUsername, &DefaultInfrastructureUsername, CfgFlag::PER_GAME),
ConfigSetting("EnableNetworkChat", &g_Config.bEnableNetworkChat, false, CfgFlag::PER_GAME), ConfigSetting("EnableNetworkChat", &g_Config.bEnableNetworkChat, false, CfgFlag::PER_GAME),
ConfigSetting("ChatButtonPosition", &g_Config.iChatButtonPosition, (int)ScreenEdgePosition::BOTTOM_LEFT, CfgFlag::PER_GAME), ConfigSetting("ChatButtonPosition", &g_Config.iChatButtonPosition, (int)ScreenEdgePosition::BOTTOM_LEFT, CfgFlag::PER_GAME),
@ -993,8 +1008,8 @@ static const ConfigSectionSettings sections[] = {
{"Graphics", graphicsSettings, ARRAY_SIZE(graphicsSettings)}, {"Graphics", graphicsSettings, ARRAY_SIZE(graphicsSettings)},
{"Sound", soundSettings, ARRAY_SIZE(soundSettings)}, {"Sound", soundSettings, ARRAY_SIZE(soundSettings)},
{"Control", controlSettings, ARRAY_SIZE(controlSettings)}, {"Control", controlSettings, ARRAY_SIZE(controlSettings)},
{"Network", networkSettings, ARRAY_SIZE(networkSettings)},
{"SystemParam", systemParamSettings, ARRAY_SIZE(systemParamSettings)}, {"SystemParam", systemParamSettings, ARRAY_SIZE(systemParamSettings)},
{"Network", networkSettings, ARRAY_SIZE(networkSettings)},
{"Debugger", debuggerSettings, ARRAY_SIZE(debuggerSettings)}, {"Debugger", debuggerSettings, ARRAY_SIZE(debuggerSettings)},
{"JIT", jitSettings, ARRAY_SIZE(jitSettings)}, {"JIT", jitSettings, ARRAY_SIZE(jitSettings)},
{"Upgrade", upgradeSettings, ARRAY_SIZE(upgradeSettings)}, {"Upgrade", upgradeSettings, ARRAY_SIZE(upgradeSettings)},

View file

@ -443,8 +443,9 @@ public:
bool bDiscardRegsOnJRRA; bool bDiscardRegsOnJRRA;
// SystemParam // SystemParam
std::string sNickName; std::string sNickName; // AdHoc and system nickname
std::string sMACAddress; std::string sMACAddress;
int iLanguage; int iLanguage;
int iTimeFormat; int iTimeFormat;
int iDateFormat; int iDateFormat;
@ -459,6 +460,7 @@ public:
std::string proAdhocServer; std::string proAdhocServer;
std::string primaryDNSServer; std::string primaryDNSServer;
std::string secondaryDNSServer; std::string secondaryDNSServer;
std::string sInfrastructureUsername; // Username used for Infrastructure play. Different restrictions.
bool bEnableWlan; bool bEnableWlan;
std::map<std::string, std::string> mHostToAlias; // TODO: mPostShaderSetting std::map<std::string, std::string> mHostToAlias; // TODO: mPostShaderSetting
bool bEnableAdhocServer; bool bEnableAdhocServer;

View file

@ -36,7 +36,7 @@ bool ConfigSetting::Get(const Section *section) const {
case TYPE_FLOAT: case TYPE_FLOAT:
return section->Get(iniKey_, ptr_.f, cb_.f ? cb_.f() : default_.f); return section->Get(iniKey_, ptr_.f, cb_.f ? cb_.f() : default_.f);
case TYPE_STRING: case TYPE_STRING:
return section->Get(iniKey_, ptr_.s, cb_.s ? cb_.s() : default_.s); return section->Get(iniKey_, ptr_.s, cb_.s ? cb_.s().c_str() : default_.s);
case TYPE_TOUCH_POS: case TYPE_TOUCH_POS:
{ {
ConfigTouchPos defaultTouchPos = cb_.touchPos ? cb_.touchPos() : default_.touchPos; ConfigTouchPos defaultTouchPos = cb_.touchPos ? cb_.touchPos() : default_.touchPos;

View file

@ -57,7 +57,7 @@ struct ConfigSetting {
typedef uint32_t(*Uint32DefaultCallback)(); typedef uint32_t(*Uint32DefaultCallback)();
typedef uint64_t(*Uint64DefaultCallback)(); typedef uint64_t(*Uint64DefaultCallback)();
typedef float (*FloatDefaultCallback)(); typedef float (*FloatDefaultCallback)();
typedef const char *(*StringDefaultCallback)(); typedef std::string (*StringDefaultCallback)();
typedef ConfigTouchPos(*TouchPosDefaultCallback)(); typedef ConfigTouchPos(*TouchPosDefaultCallback)();
typedef const char *(*PathDefaultCallback)(); typedef const char *(*PathDefaultCallback)();
typedef ConfigCustomButton(*CustomButtonDefaultCallback)(); typedef ConfigCustomButton(*CustomButtonDefaultCallback)();

View file

@ -20,7 +20,10 @@
#include <mutex> #include <mutex>
#include <deque> #include <deque>
#include <StringUtils.h>
#include "Common/System/OSD.h"
#include "Common/Data/Text/I18n.h"
#include "Common/StringUtils.h"
#include "Core/MemMapHelpers.h" #include "Core/MemMapHelpers.h"
#include "Core/CoreTiming.h" #include "Core/CoreTiming.h"
#include "Core/Config.h" #include "Core/Config.h"
@ -52,7 +55,6 @@ std::recursive_mutex npAuthEvtMtx;
std::deque<NpAuthArgs> npAuthEvents; std::deque<NpAuthArgs> npAuthEvents;
std::map<int, NpAuthHandler> npAuthHandlers; std::map<int, NpAuthHandler> npAuthHandlers;
// Tickets data are in big-endian based on captured packets // Tickets data are in big-endian based on captured packets
static int writeTicketParam(u8* buffer, const u16_be type, const char* data = nullptr, const u16_be size = 0) { static int writeTicketParam(u8* buffer, const u16_be type, const char* data = nullptr, const u16_be size = 0) {
if (buffer == nullptr) return 0; if (buffer == nullptr) return 0;
@ -112,12 +114,16 @@ static void notifyNpAuthHandlers(u32 id, u32 result, u32 argAddr) {
static int sceNpInit() static int sceNpInit()
{ {
ERROR_LOG(Log::sceNet, "UNIMPL %s()", __FUNCTION__); ERROR_LOG(Log::sceNet, "UNIMPL %s()", __FUNCTION__);
npOnlineId = g_Config.sNickName;
// Truncate the nickname to 16 chars exactly - longer names are not support. // We'll sanitize an extra time here, just to be safe from ini modifications.
if (npOnlineId.size() > 16) { if (g_Config.sInfrastructureUsername == SanitizeString(g_Config.sInfrastructureUsername, StringRestriction::AlphaNumDashUnderscore, 16)) {
npOnlineId.resize(16); npOnlineId = g_Config.sInfrastructureUsername;
} else {
npOnlineId.clear();
} }
// NOTE: Checking validity and returning -1 here doesn't seem to work. Instead, we will fail to generate a ticket.
return 0; return 0;
} }
@ -402,6 +408,14 @@ int sceNpAuthGetTicket(u32 requestId, u32 bufferAddr, u32 length)
if (!Memory::IsValidAddress(bufferAddr)) if (!Memory::IsValidAddress(bufferAddr))
return hleLogError(Log::sceNet, SCE_NP_AUTH_ERROR_INVALID_ARGUMENT, "invalid arg"); return hleLogError(Log::sceNet, SCE_NP_AUTH_ERROR_INVALID_ARGUMENT, "invalid arg");
// We have validated, and this will be empty if the ID is bad.
if (npOnlineId.empty()) {
auto n = GetI18NCategory(I18NCat::NETWORKING);
// Temporary message.
g_OSD.Show(OSDType::MESSAGE_ERROR, n->T("To play in Infrastructure Mode, you must enter a username"), 5.0f);
return hleLogError(Log::sceNet, SCE_NP_AUTH_ERROR_UNKNOWN, "Missing npOnlineId");
}
int result = length; int result = length;
Memory::Memset(bufferAddr, 0, length, "NpAuthGetTicket"); Memory::Memset(bufferAddr, 0, length, "NpAuthGetTicket");
SceNpTicket ticket = {}; SceNpTicket ticket = {};

View file

@ -941,6 +941,16 @@ void GameSettingsScreen::CreateNetworkingSettings(UI::ViewGroup *networkingSetti
useOriPort->SetEnabledPtr(&g_Config.bEnableUPnP); useOriPort->SetEnabledPtr(&g_Config.bEnableUPnP);
networkingSettings->Add(new ItemHeader(n->T("Infrastructure"))); networkingSettings->Add(new ItemHeader(n->T("Infrastructure")));
if (g_Config.sInfrastructureUsername.empty()) {
networkingSettings->Add(new NoticeView(NoticeLevel::WARN, n->T("To play in Infrastructure Mode, you must enter a username"), ""));
}
PopupTextInputChoice *usernameChoice = networkingSettings->Add(new PopupTextInputChoice(GetRequesterToken(), &g_Config.sInfrastructureUsername, n->T("Username"), "", 16, screenManager()));
usernameChoice->SetRestriction(StringRestriction::AlphaNumDashUnderscore);
usernameChoice->OnChange.Add([this](UI::EventParams &e) {
RecreateViews();
return UI::EVENT_DONE;
});
networkingSettings->Add(new PopupTextInputChoice(GetRequesterToken(), &g_Config.primaryDNSServer, n->T("Primary DNS server"), "", 32, screenManager())); networkingSettings->Add(new PopupTextInputChoice(GetRequesterToken(), &g_Config.primaryDNSServer, n->T("Primary DNS server"), "", 32, screenManager()));
networkingSettings->Add(new PopupTextInputChoice(GetRequesterToken(), &g_Config.secondaryDNSServer, n->T("Secondary DNS server"), "", 32, screenManager())); networkingSettings->Add(new PopupTextInputChoice(GetRequesterToken(), &g_Config.secondaryDNSServer, n->T("Secondary DNS server"), "", 32, screenManager()));
@ -1682,62 +1692,42 @@ UI::EventReturn GameSettingsScreen::OnAudioDevice(UI::EventParams &e) {
} }
UI::EventReturn GameSettingsScreen::OnChangeQuickChat0(UI::EventParams &e) { UI::EventReturn GameSettingsScreen::OnChangeQuickChat0(UI::EventParams &e) {
#if PPSSPP_PLATFORM(WINDOWS) || defined(USING_QT_UI) || PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(SWITCH)
auto n = GetI18NCategory(I18NCat::NETWORKING); auto n = GetI18NCategory(I18NCat::NETWORKING);
System_InputBoxGetString(GetRequesterToken(), n->T("Enter Quick Chat 1"), g_Config.sQuickChat0, false, [](const std::string &value, int) { System_InputBoxGetString(GetRequesterToken(), n->T("Enter Quick Chat 1"), g_Config.sQuickChat0, false, [](const std::string &value, int) {
g_Config.sQuickChat0 = value; g_Config.sQuickChat0 = value;
}); });
#endif
return UI::EVENT_DONE; return UI::EVENT_DONE;
} }
UI::EventReturn GameSettingsScreen::OnChangeQuickChat1(UI::EventParams &e) { UI::EventReturn GameSettingsScreen::OnChangeQuickChat1(UI::EventParams &e) {
#if PPSSPP_PLATFORM(WINDOWS) || defined(USING_QT_UI) || PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(SWITCH)
auto n = GetI18NCategory(I18NCat::NETWORKING); auto n = GetI18NCategory(I18NCat::NETWORKING);
System_InputBoxGetString(GetRequesterToken(), n->T("Enter Quick Chat 2"), g_Config.sQuickChat1, false, [](const std::string &value, int) { System_InputBoxGetString(GetRequesterToken(), n->T("Enter Quick Chat 2"), g_Config.sQuickChat1, false, [](const std::string &value, int) {
g_Config.sQuickChat1 = value; g_Config.sQuickChat1 = value;
}); });
#endif
return UI::EVENT_DONE; return UI::EVENT_DONE;
} }
UI::EventReturn GameSettingsScreen::OnChangeQuickChat2(UI::EventParams &e) { UI::EventReturn GameSettingsScreen::OnChangeQuickChat2(UI::EventParams &e) {
#if PPSSPP_PLATFORM(WINDOWS) || defined(USING_QT_UI) || PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(SWITCH)
auto n = GetI18NCategory(I18NCat::NETWORKING); auto n = GetI18NCategory(I18NCat::NETWORKING);
System_InputBoxGetString(GetRequesterToken(), n->T("Enter Quick Chat 3"), g_Config.sQuickChat2, false, [](const std::string &value, int) { System_InputBoxGetString(GetRequesterToken(), n->T("Enter Quick Chat 3"), g_Config.sQuickChat2, false, [](const std::string &value, int) {
g_Config.sQuickChat2 = value; g_Config.sQuickChat2 = value;
}); });
#endif
return UI::EVENT_DONE; return UI::EVENT_DONE;
} }
UI::EventReturn GameSettingsScreen::OnChangeQuickChat3(UI::EventParams &e) { UI::EventReturn GameSettingsScreen::OnChangeQuickChat3(UI::EventParams &e) {
#if PPSSPP_PLATFORM(WINDOWS) || defined(USING_QT_UI) || PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(SWITCH)
auto n = GetI18NCategory(I18NCat::NETWORKING); auto n = GetI18NCategory(I18NCat::NETWORKING);
System_InputBoxGetString(GetRequesterToken(), n->T("Enter Quick Chat 4"), g_Config.sQuickChat3, false, [](const std::string &value, int) { System_InputBoxGetString(GetRequesterToken(), n->T("Enter Quick Chat 4"), g_Config.sQuickChat3, false, [](const std::string &value, int) {
g_Config.sQuickChat3 = value; g_Config.sQuickChat3 = value;
}); });
#endif
return UI::EVENT_DONE; return UI::EVENT_DONE;
} }
UI::EventReturn GameSettingsScreen::OnChangeQuickChat4(UI::EventParams &e) { UI::EventReturn GameSettingsScreen::OnChangeQuickChat4(UI::EventParams &e) {
#if PPSSPP_PLATFORM(WINDOWS) || defined(USING_QT_UI) || PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(SWITCH)
auto n = GetI18NCategory(I18NCat::NETWORKING); auto n = GetI18NCategory(I18NCat::NETWORKING);
System_InputBoxGetString(GetRequesterToken(), n->T("Enter Quick Chat 5"), g_Config.sQuickChat4, false, [](const std::string &value, int) { System_InputBoxGetString(GetRequesterToken(), n->T("Enter Quick Chat 5"), g_Config.sQuickChat4, false, [](const std::string &value, int) {
g_Config.sQuickChat4 = value; g_Config.sQuickChat4 = value;
}); });
#endif
return UI::EVENT_DONE;
}
UI::EventReturn GameSettingsScreen::OnChangeNickname(UI::EventParams &e) {
#if PPSSPP_PLATFORM(WINDOWS) || defined(USING_QT_UI) || PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(SWITCH)
auto n = GetI18NCategory(I18NCat::NETWORKING);
System_InputBoxGetString(GetRequesterToken(), n->T("Enter a new PSP nickname"), g_Config.sNickName, false, [](const std::string &value, int) {
g_Config.sNickName = StripSpaces(value);
});
#endif
return UI::EVENT_DONE; return UI::EVENT_DONE;
} }

View file

@ -89,7 +89,6 @@ private:
UI::EventReturn OnChangeQuickChat2(UI::EventParams &e); UI::EventReturn OnChangeQuickChat2(UI::EventParams &e);
UI::EventReturn OnChangeQuickChat3(UI::EventParams &e); UI::EventReturn OnChangeQuickChat3(UI::EventParams &e);
UI::EventReturn OnChangeQuickChat4(UI::EventParams &e); UI::EventReturn OnChangeQuickChat4(UI::EventParams &e);
UI::EventReturn OnChangeNickname(UI::EventParams &e);
UI::EventReturn OnChangeproAdhocServerAddress(UI::EventParams &e); UI::EventReturn OnChangeproAdhocServerAddress(UI::EventParams &e);
UI::EventReturn OnChangeBackground(UI::EventParams &e); UI::EventReturn OnChangeBackground(UI::EventParams &e);
UI::EventReturn OnFullscreenChange(UI::EventParams &e); UI::EventReturn OnFullscreenChange(UI::EventParams &e);