Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor chat and add configurable chat messages #369

Merged
merged 29 commits into from
Jun 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
37c30c4
update asm and c codeset
rapito Aug 26, 2022
d0a07e1
chore: update asm codeset
rapito Aug 26, 2022
c4bc77b
update codeset and fix default debug command
rapito Aug 26, 2022
1e19df1
chore: update codeset chat refactor
JLaferri Aug 26, 2022
5a66927
update codeset
rapito Aug 27, 2022
a6bec4b
Update codeset
rapito Aug 29, 2022
378b9b3
Update codeset
rapito Aug 29, 2022
51ee7b1
chore: update asm and c codesets
JLaferri Aug 29, 2022
18ef2e2
Merge remote-tracking branch 'upstream/slippi' into refactor/quickchat
rapito Sep 21, 2022
cd78b29
Use iconv fallbacks on macos so special and SJIS characters are prope…
rapito Nov 9, 2022
f4f90de
build codesets
rapito Nov 9, 2022
1e24adf
Merge remote-tracking branch 'origin/refactor/quickchat' into refacto…
rapito Nov 9, 2022
5e927ce
Merge branch 'slippi' into pr/369
JLaferri Mar 10, 2023
01ae8c5
Merge branch 'slippi' into pr/369
JLaferri Apr 6, 2023
508cd45
start support for configured chat messages
JLaferri May 14, 2023
408c6a7
Merge branch 'slippi' into pr/369
JLaferri May 30, 2023
4743b82
use test default messages
JLaferri May 31, 2023
c69a93d
receive chat messages from mm service
JLaferri Jun 6, 2023
6017c63
feature: show configured messages in preview
JLaferri Jun 6, 2023
c8bf3ca
chore: clean comments and loggers
JLaferri Jun 6, 2023
fc25a5c
fix: ifdef iconvctl for mac only
JLaferri Jun 6, 2023
4da8023
chore: try to get linux building
JLaferri Jun 6, 2023
3d1cb13
chore: use nullptr instead of NULL
JLaferri Jun 6, 2023
1212c9b
chore: ifdef attempt 3 for linux
JLaferri Jun 7, 2023
c95d5a2
chore: dont call functions that dont exist
JLaferri Jun 7, 2023
0280568
swap elif for else
NikhilNarayana Jun 7, 2023
08b7730
undo template name move
NikhilNarayana Jun 7, 2023
54abd78
chore: update code lists
JLaferri Jun 7, 2023
ef4dff4
Merge branch 'slippi' into pr/369
JLaferri Jun 7, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified Data/Sys/GameFiles/GALE01/GameSetup.dat
Binary file not shown.
Binary file modified Data/Sys/GameFiles/GALE01/SlippiCSS.dat
Binary file not shown.
Binary file modified Data/Sys/GameFiles/GALE01/slpCSS.dat
Binary file not shown.
882 changes: 255 additions & 627 deletions Data/Sys/GameSettings/GALE01r2.ini

Large diffs are not rendered by default.

882 changes: 255 additions & 627 deletions Data/Sys/GameSettings/GALJ01r2.ini

Large diffs are not rendered by default.

11 changes: 11 additions & 0 deletions Data/Sys/Slippi/InjectionLists/list_netplay-old2.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"Details": [
{
"InjectionAddress": "800056AC",
"Name": "Required: Slippi Online",
"Codetype": "Auto",
"Annotation": "Online/Static/ChatMessages.asm",
"Tags": ""
}
]
}
7 changes: 0 additions & 7 deletions Data/Sys/Slippi/InjectionLists/list_netplay.json
Original file line number Diff line number Diff line change
Expand Up @@ -1232,13 +1232,6 @@
"Annotation": "Online/Static/CSSUpdateCSP.asm",
"Tags": ""
},
{
"InjectionAddress": "800056AC",
"Name": "Required: Slippi Online",
"Codetype": "Auto",
"Annotation": "Online/Static/ChatMessages.asm",
"Tags": ""
},
{
"InjectionAddress": "80005690",
"Name": "Required: Slippi Online",
Expand Down
68 changes: 64 additions & 4 deletions Source/Core/Common/StringUtil.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -544,11 +544,25 @@ std::string UTF32toUTF8(const std::u32string &input)
}
#else
template <typename T>
std::string CodeTo(const char *tocode, const char *fromcode, const std::basic_string<T>& input)
#ifdef __APPLE__
std::string CodeToWithFallbacks(const char *tocode, const char *fromcode, const std::basic_string<T> &input,
iconv_fallbacks *fallbacks)
#else
std::string CodeTo(const char *tocode, const char *fromcode, const std::basic_string<T> &input)
#endif
{
std::string result;

iconv_t const conv_desc = iconv_open(tocode, fromcode);

// Only on OS X can we call iconvctl, it isn't found on Linux
#ifdef __APPLE__
if (fallbacks)
{
iconvctl(conv_desc, ICONV_SET_FALLBACKS, fallbacks);
}
#endif

if ((iconv_t)-1 == conv_desc)
{
ERROR_LOG(COMMON, "Iconv initialization failure [%s]: %s", fromcode, strerror(errno));
Expand Down Expand Up @@ -599,6 +613,13 @@ std::string CodeTo(const char *tocode, const char *fromcode, const std::basic_st
return result;
}

#ifdef __APPLE__
template <typename T> std::string CodeTo(const char *tocode, const char *fromcode, const std::basic_string<T> &input)
{
return CodeToWithFallbacks(tocode, fromcode, input, nullptr);
}
#endif

template <typename T>
std::string CodeToUTF8(const char* fromcode, const std::basic_string<T>& input)
{
Expand All @@ -618,9 +639,48 @@ std::string SHIFTJISToUTF8(const std::string& input)
return CodeToUTF8("SJIS", input);
}

std::string UTF8ToSHIFTJIS(const std::string& input)
#ifdef __APPLE__
void uc_to_mb_fb(unsigned int code, void (*write_replacement)(const char *buf, size_t buflen, void *callback_arg),
void *callback_arg, void *data)
{
static std::unordered_map<unsigned int, const char*> specialCharConvert = {
{'!', (const char*)"\x81\x49"}, {'"', (const char*)"\x81\x68"}, {'#', (const char*)"\x81\x94"}, {'$', (const char*)"\x81\x90"},
{'%', (const char*)"\x81\x93"}, {'&', (const char*)"\x81\x95"}, {'\'', (const char*)"\x81\x66"}, {'(', (const char*)"\x81\x69"},
{')', (const char*)"\x81\x6a"}, {'*', (const char*)"\x81\x96"}, {'+', (const char*)"\x81\x7b"}, {',', (const char*)"\x81\x43"},
{'-', (const char*)"\x81\x7c"}, {'.', (const char*)"\x81\x44"}, {'/', (const char*)"\x81\x5e"}, {':', (const char*)"\x81\x46"},
{';', (const char*)"\x81\x47"}, {'<', (const char*)"\x81\x83"}, {'=', (const char*)"\x81\x81"}, {'>', (const char*)"\x81\x84"},
{'?', (const char*)"\x81\x48"}, {'@', (const char*)"\x81\x97"}, {'[', (const char*)"\x81\x6d"}, {'\\', (const char*)"\x81\x5f"},
{']', (const char*)"\x81\x6e"}, {'^', (const char*)"\x81\x4f"}, {'_', (const char*)"\x81\x51"}, {'`', (const char*)"\x81\x4d"},
{'{', (const char*)"\x81\x6f"}, {'|', (const char*)"\x81\x62"}, {'}', (const char*)"\x81\x70"}, {'~', (const char*)"\x81\x60"},
{U'¥', "\x81\x8f"}, {U'•', "\x81\x45"}, {U'—', "\x81\x7C"}
};

bool hasConversion = specialCharConvert.count(code);
if (!hasConversion)
return;

auto newChar = specialCharConvert[code];
// Add new chars to pos to replace
write_replacement(newChar, 2, callback_arg);
}
#endif

std::string UTF8ToSHIFTJIS(const std::string &input)
{
return CodeTo("SJIS", "UTF-8", input);
#ifdef __APPLE__
// Set SHIFTJIS callbacks only if converting to shift jis
auto fallbacks = new iconv_fallbacks();
fallbacks->uc_to_mb_fallback = uc_to_mb_fb;
fallbacks->mb_to_uc_fallback = nullptr;
fallbacks->mb_to_wc_fallback = nullptr;
fallbacks->wc_to_mb_fallback = nullptr;
fallbacks->data = nullptr;
auto str = CodeToWithFallbacks("SJIS", "UTF-8", input, fallbacks);
free(fallbacks);
#else
auto str = CodeTo("SJIS", "UTF-8", input);
#endif
return str;
}

std::string UTF16ToUTF8(const std::wstring& input)
Expand Down Expand Up @@ -671,4 +731,4 @@ std::string ValueToString(int value)
std::string ValueToString(bool value)
{
return value ? "True" : "False";
}
}
9 changes: 9 additions & 0 deletions Source/Core/Common/StringUtil.h
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,15 @@ std::string SHIFTJISToUTF8(const std::string& str);
std::string UTF8ToSHIFTJIS(const std::string& str);
std::string UTF16ToUTF8(const std::wstring &str);


#ifdef __APPLE__
/**
* Callback Implementation used for iconv when a unicode character could not be automatically converted.
* This callback is used specifically for SHIFTJIS failures when converting some special wide characters for melee.
*/
void uc_to_mb_fb(unsigned int code,void (*write_replacement)(const char *buf, size_t buflen, void* callback_arg), void* callback_arg, void* data);
#endif

#ifdef _WIN32

std::wstring UTF8ToUTF16(const std::string& str);
Expand Down
2 changes: 1 addition & 1 deletion Source/Core/Common/Version.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
//" " BUILD_TYPE_STR " " SCM_DESC_STR;
//#endif
#ifndef IS_PLAYBACK
#define SLIPPI_REV_STR "3.0.4" // netplay version
#define SLIPPI_REV_STR "3.0.5-dev.1" // netplay version
#else
#define SLIPPI_REV_STR "3.0.1" // playback version
#endif
Expand Down
41 changes: 40 additions & 1 deletion Source/Core/Core/HW/EXI_DeviceSlippi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2070,7 +2070,7 @@ void CEXISlippi::prepareOnlineMatchState()
chatMessagePlayerIdx = 0;
localChatMessageId = 0;
// in CSS p1 is always current player and p2 is opponent
localPlayerName = p1Name = "Player 1";
localPlayerName = p1Name = userInfo.displayName;
oppName = p2Name = "Player 2";
#endif

Expand Down Expand Up @@ -3020,6 +3020,42 @@ void CEXISlippi::handleCompleteSet(const SlippiExiTypes::ReportSetCompletionQuer
}
}

void CEXISlippi::handleGetPlayerSettings()
{
m_read_queue.clear();

SlippiExiTypes::GetPlayerSettingsResponse resp = {};

std::vector<std::vector<std::string>> messagesByPlayer = {
SlippiUser::defaultChatMessages, SlippiUser::defaultChatMessages, SlippiUser::defaultChatMessages,
SlippiUser::defaultChatMessages};

// These chat messages will be used when previewing messages
auto userChatMessages = user->GetUserInfo().chatMessages;
if (userChatMessages.size() == 16) {
messagesByPlayer[0] = userChatMessages;
}

// These chat messages will be set when we have an opponent. We load their and our messages
auto playerInfo = matchmaking->GetPlayerInfo();
for (auto &player : playerInfo)
{
messagesByPlayer[player.port - 1] = player.chatMessages;
}

for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 16; j++)
{
auto str = ConvertStringForGame(messagesByPlayer[i][j], MAX_MESSAGE_LENGTH);
sprintf(resp.settings[i].chatMessages[j], "%s", str.c_str());
}
}

auto data_ptr = (u8 *)&resp;
m_read_queue.insert(m_read_queue.end(), data_ptr, data_ptr + sizeof(SlippiExiTypes::GetPlayerSettingsResponse));
}

void CEXISlippi::DMAWrite(u32 _uAddr, u32 _uSize)
{
u8 *memPtr = Memory::GetPointer(_uAddr);
Expand Down Expand Up @@ -3187,6 +3223,9 @@ void CEXISlippi::DMAWrite(u32 _uAddr, u32 _uSize)
case CMD_REPORT_SET_COMPLETE:
handleCompleteSet(SlippiExiTypes::Convert<SlippiExiTypes::ReportSetCompletionQuery>(&memPtr[bufLoc]));
break;
case CMD_GET_PLAYER_SETTINGS:
handleGetPlayerSettings();
break;
default:
writeToFileAsync(&memPtr[bufLoc], payloadLen + 1, "");
m_slippiserver->write(&memPtr[bufLoc], payloadLen + 1);
Expand Down
4 changes: 4 additions & 0 deletions Source/Core/Core/HW/EXI_DeviceSlippi.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

#define ROLLBACK_MAX_FRAMES 7
#define MAX_NAME_LENGTH 15
#define MAX_MESSAGE_LENGTH 25
#define CONNECT_CODE_LENGTH 8

extern bool g_needInputForFrame;
Expand Down Expand Up @@ -79,6 +80,7 @@ class CEXISlippi : public IEXIDevice
CMD_GP_COMPLETE_STEP = 0xC0,
CMD_GP_FETCH_STEP = 0xC1,
CMD_REPORT_SET_COMPLETE = 0xC2,
CMD_GET_PLAYER_SETTINGS = 0xC3,

// Misc
CMD_LOG_MESSAGE = 0xD0,
Expand Down Expand Up @@ -134,6 +136,7 @@ class CEXISlippi : public IEXIDevice
{CMD_GP_COMPLETE_STEP, static_cast<u32>(sizeof(SlippiExiTypes::GpCompleteStepQuery) - 1)},
{CMD_GP_FETCH_STEP, static_cast<u32>(sizeof(SlippiExiTypes::GpFetchStepQuery) - 1)},
{CMD_REPORT_SET_COMPLETE, static_cast<u32>(sizeof(SlippiExiTypes::ReportSetCompletionQuery) - 1)},
{CMD_GET_PLAYER_SETTINGS, 0},

// Misc
{CMD_LOG_MESSAGE, 0xFFFF}, // Variable size... will only work if by itself
Expand Down Expand Up @@ -205,6 +208,7 @@ class CEXISlippi : public IEXIDevice
void handleGamePrepStepComplete(const SlippiExiTypes::GpCompleteStepQuery &query);
void prepareGamePrepOppStep(const SlippiExiTypes::GpFetchStepQuery &query);
void handleCompleteSet(const SlippiExiTypes::ReportSetCompletionQuery &query);
void handleGetPlayerSettings();

// replay playback stuff
void prepareGameInfo(u8 *payload);
Expand Down
9 changes: 9 additions & 0 deletions Source/Core/Core/Slippi/SlippiExiTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,15 @@ struct OverwriteSelectionsQuery
OverwriteCharSelections chars[4];
};

struct PlayerSettings
{
char chatMessages[16][51];
};
struct GetPlayerSettingsResponse
{
PlayerSettings settings[4];
};

// Not sure if resetting is strictly needed, might be contained to the file
#pragma pack()

Expand Down
10 changes: 10 additions & 0 deletions Source/Core/Core/Slippi/SlippiMatchmaking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,16 @@ void SlippiMatchmaking::handleMatchmaking()
playerInfo.displayName = el.value("displayName", "");
playerInfo.connectCode = el.value("connectCode", "");
playerInfo.port = el.value("port", 0);
playerInfo.chatMessages = SlippiUser::defaultChatMessages;
if (el["chatMessages"].is_array())
{
playerInfo.chatMessages = el.value("chatMessages", SlippiUser::defaultChatMessages);
if (playerInfo.chatMessages.size() != 16)
{
playerInfo.chatMessages = SlippiUser::defaultChatMessages;
}
}

m_playerInfo.push_back(playerInfo);

if (isLocal)
Expand Down
43 changes: 42 additions & 1 deletion Source/Core/Core/Slippi/SlippiUser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,28 @@
#include <json.hpp>
using json = nlohmann::json;

const std::vector<std::string> SlippiUser::defaultChatMessages = {
"ggs",
"one more",
"brb",
"good luck",

"well played",
"that was fun",
"thanks",
"too good",

"sorry",
"my b",
"lol",
"wow",

"gotta go",
"one sec",
"let's play again later",
"bad connection",
};

#ifdef _WIN32
#define MAX_SYSTEM_PROGRAM (4096)
static void system_hidden(const char *cmd)
Expand Down Expand Up @@ -275,6 +297,16 @@ SlippiUser::UserInfo SlippiUser::parseFile(std::string fileContents)
info.connectCode = readString(res, "connectCode");
info.latestVersion = readString(res, "latestVersion");

info.chatMessages = SlippiUser::defaultChatMessages;
if (res["chatMessages"].is_array())
{
info.chatMessages = res.value("chatMessages", SlippiUser::defaultChatMessages);
if (info.chatMessages.size() != 16)
{
info.chatMessages = SlippiUser::defaultChatMessages;
}
}

return info;
}

Expand All @@ -300,7 +332,7 @@ void SlippiUser::overwriteFromServer()

// Perform curl request
std::string resp;
curl_easy_setopt(m_curl, CURLOPT_URL, (url + "/" + userInfo.uid).c_str());
curl_easy_setopt(m_curl, CURLOPT_URL, (url + "/" + userInfo.uid + "?additionalFields=chatMessages").c_str());
curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &resp);
CURLcode res = curl_easy_perform(m_curl);

Expand All @@ -323,4 +355,13 @@ void SlippiUser::overwriteFromServer()
userInfo.connectCode = r.value("connectCode", userInfo.connectCode);
userInfo.latestVersion = r.value("latestVersion", userInfo.latestVersion);
userInfo.displayName = r.value("displayName", userInfo.displayName);

if (r["chatMessages"].is_array())
{
userInfo.chatMessages = r.value("chatMessages", SlippiUser::defaultChatMessages);
if (userInfo.chatMessages.size() != 16)
{
userInfo.chatMessages = SlippiUser::defaultChatMessages;
}
}
}
4 changes: 4 additions & 0 deletions Source/Core/Core/Slippi/SlippiUser.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ class SlippiUser
std::string fileContents = "";

int port;

std::vector<std::string> chatMessages;
};

SlippiUser();
Expand All @@ -36,6 +38,8 @@ class SlippiUser
bool IsLoggedIn();
void FileListenThread();

const static std::vector<std::string> defaultChatMessages;

protected:
UserInfo parseFile(std::string fileContents);
void deleteFile();
Expand Down