Chat: keep more lines of history, and add timestamps

The log was trimmed to 50 lines, which isn't enough to scroll back
through a conversation - now 250. Chat entries also carry the time they
arrived, shown as a dimmed HH:MM in front of the name, behind a new
"Show timestamps in chat" setting.

The timestamp is kept next to the text rather than baked into it, since
the chat view finds the sender by splitting the line at the first colon.

Fixes #15444

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01JvJR8oJNSCimCM9KXVLjfq
This commit is contained in:
Henrik RydgårdandClaude Opus 5 committed 2026-09-04 13:10:54 -06:00
1 parent eb1bc34123
commit fbdf611fc4
6 files changed
+47 -14

No files matched your search

+1
View File
@@ -1057,6 +1057,7 @@ static const ConfigSetting networkSettings[] = {
ConfigSetting("EnableNetworkChat", SETTING(g_Config, bEnableNetworkChat), false, CfgFlag::PER_GAME),
ConfigSetting("ChatButtonPosition", SETTING(g_Config, iChatButtonPosition), (int)ScreenEdgePosition::BOTTOM_LEFT, CfgFlag::PER_GAME),
ConfigSetting("ChatScreenPosition", SETTING(g_Config, iChatScreenPosition), (int)ScreenEdgePosition::BOTTOM_LEFT, CfgFlag::PER_GAME),
ConfigSetting("ChatTimestamps", SETTING(g_Config, bChatTimestamps), true, CfgFlag::PER_GAME),
ConfigSetting("EnableQuickChat", SETTING(g_Config, bEnableQuickChat), true, CfgFlag::PER_GAME),
ConfigSetting("QuickChat1", SETTING(g_Config, sQuickChat[0]), "Quick Chat 1", CfgFlag::PER_GAME),
ConfigSetting("QuickChat2", SETTING(g_Config, sQuickChat[1]), "Quick Chat 2", CfgFlag::PER_GAME),
+1
View File
@@ -611,6 +611,7 @@ public:
bool bDontDownloadInfraJson;
int iChatButtonPosition;
int iChatScreenPosition;
bool bChatTimestamps;
bool bEnableQuickChat;
std::string sQuickChat[5];
+16 -10
View File
@@ -120,7 +120,17 @@ sockaddr LocalIP;
int defaultWlanChannel = PSP_SYSTEMPARAM_ADHOC_CHANNEL_11; // Don't put 0(Auto) here, it needed to be a valid/actual channel number
static std::mutex chatLogLock;
static std::vector<std::string> chatLog;
static std::vector<ChatLogEntry> chatLog;
// Enough to scroll back through a decent conversation without growing unbounded.
static const size_t MAX_CHAT_LOG_LINES = 250;
// chatLogLock must be held.
static void AddChatLogEntry(std::string text) {
chatLog.push_back(ChatLogEntry{std::move(text), time(nullptr)});
if (chatLog.size() > MAX_CHAT_LOG_LINES) {
chatLog.erase(chatLog.begin(), chatLog.begin() + (chatLog.size() - MAX_CHAT_LOG_LINES));
}
}
static int chatMessageGeneration = 0;
static int chatMessageCount = 0;
@@ -1322,25 +1332,21 @@ void sendChat(std::string_view chatString) {
std::string name = g_Config.sNickName;
std::lock_guard<std::mutex> guard(chatLogLock);
chatLog.emplace_back(name.substr(0, 8) + ": " + chat.message);
AddChatLogEntry(name.substr(0, 8) + ": " + chat.message);
chatMessageGeneration++;
}
}
} else {
std::lock_guard<std::mutex> guard(chatLogLock);
auto n = GetI18NCategory(I18NCat::NETWORKING);
chatLog.push_back(std::string(n->T("You're in Offline Mode, go to lobby or online hall")));
AddChatLogEntry(std::string(n->T("You're in Offline Mode, go to lobby or online hall")));
INFO_LOG(Log::sceNet, "Offline. Would have sent: %.*s", STR_VIEW(chatString));
chatMessageGeneration++;
}
}
std::vector<std::string> getChatLog() {
std::vector<ChatLogEntry> getChatLog() {
std::lock_guard<std::mutex> guard(chatLogLock);
// If the log gets large, trim it down.
if (chatLog.size() > 50) {
chatLog.erase(chatLog.begin(), chatLog.begin() + (chatLog.size() - 50));
}
return chatLog;
}
@@ -1555,7 +1561,7 @@ int friendFinder() {
incoming.append((char*)packet->base.message);
std::lock_guard<std::mutex> guard(chatLogLock);
chatLog.push_back(incoming);
AddChatLogEntry(incoming);
chatMessageGeneration++;
chatMessageCount++;
@@ -1626,7 +1632,7 @@ int friendFinder() {
//joined.append((char *)packet->ip);
std::lock_guard<std::mutex> guard(chatLogLock);
chatLog.push_back(incoming);
AddChatLogEntry(incoming);
chatMessageGeneration++;
#ifdef LOCALHOST_AS_PEER
+7 -1
View File
@@ -29,6 +29,7 @@
#include <mutex>
#include <thread>
#include <climits>
#include <ctime>
#include "Common/Net/Resolve.h"
#include "Common/Serialize/Serializer.h"
@@ -884,7 +885,12 @@ void addFriend(SceNetAdhocctlConnectPacketS2C * packet);
* @param std::string ChatString
*/
void sendChat(std::string_view chatString);
std::vector<std::string> getChatLog();
struct ChatLogEntry {
std::string text; // "name: message", or an info line with no colon.
time_t timestamp;
};
std::vector<ChatLogEntry> getChatLog();
int GetChatChangeID();
int GetChatMessageCount();
+21 -3
View File
@@ -10,6 +10,7 @@
#include "Common/UI/UI.h"
#include "Common/Data/Text/I18n.h"
#include "Common/File/FileUtil.h" // For the localtime_r shim on Windows.
#include "Common/Data/Encoding/Utf8.h"
#include "Common/System/Request.h"
#include "Core/Config.h"
@@ -137,11 +138,13 @@ void ChatMenu::UpdateChat() {
using namespace UI;
if (chatVert_ != nullptr) {
chatVert_->Clear(); //read Access violation is proadhoc.cpp use NULL_->Clear() pointer?
std::vector<std::string> chatLog = getChatLog();
for (auto i : chatLog) {
std::vector<ChatLogEntry> chatLog = getChatLog();
for (const auto &entry : chatLog) {
const std::string &i = entry.text;
uint32_t namecolor = 0x29B6F6;
uint32_t textcolor = 0xFFFFFF;
uint32_t infocolor = 0xFDD835;
uint32_t timecolor = 0x9E9E9E;
std::string name = g_Config.sNickName;
std::string displayname = i.substr(0, i.find(':'));
@@ -150,12 +153,27 @@ void ChatMenu::UpdateChat() {
namecolor = 0xE53935;
}
std::string timestamp;
if (g_Config.bChatTimestamps) {
char buf[16];
tm localTime;
localtime_r(&entry.timestamp, &localTime);
if (strftime(buf, sizeof(buf), "%H:%M ", &localTime)) {
timestamp = buf;
}
}
if (i.length() <= displayname.length() || i[displayname.length()] != ':') {
TextView *v = chatVert_->Add(new TextView(i, ALIGN_LEFT | FLAG_WRAP_TEXT, true, new LayoutParams(FILL_PARENT, WRAP_CONTENT)));
// Info line - no name to color separately, so the timestamp can just go in the text.
TextView *v = chatVert_->Add(new TextView(timestamp + i, ALIGN_LEFT | FLAG_WRAP_TEXT, true, new LayoutParams(FILL_PARENT, WRAP_CONTENT)));
v->SetTextColor(0xFF000000 | infocolor);
} else {
LinearLayout *line = chatVert_->Add(new LinearLayout(ORIENT_HORIZONTAL, new LayoutParams(FILL_PARENT, FILL_PARENT)));
line->SetSpacing(0.0f);
if (!timestamp.empty()) {
TextView *timeView = line->Add(new TextView(timestamp, ALIGN_LEFT, true, new LinearLayoutParams(WRAP_CONTENT, WRAP_CONTENT, 0.0f)));
timeView->SetTextColor(0xFF000000 | timecolor);
}
TextView *nameView = line->Add(new TextView(displayname, ALIGN_LEFT, true, new LinearLayoutParams(WRAP_CONTENT, WRAP_CONTENT, 0.0f)));
nameView->SetTextColor(0xFF000000 | namecolor);
+1
View File
@@ -1070,6 +1070,7 @@ void GameSettingsScreen::CreateNetworkingSettings(UI::ViewGroup *networkingSetti
networkingSettings->Add(new PopupMultiChoice(&g_Config.iChatButtonPosition, n->T("Chat Button Position"), chatButtonPositions, 0, ARRAY_SIZE(chatButtonPositions), I18NCat::DIALOG, screenManager()))->SetEnabledPtr(&g_Config.bEnableNetworkChat);
static const char *chatScreenPositions[] = { "Bottom Left", "Bottom Center", "Bottom Right", "Top Left", "Top Center", "Top Right" };
networkingSettings->Add(new PopupMultiChoice(&g_Config.iChatScreenPosition, n->T("Chat Screen Position"), chatScreenPositions, 0, ARRAY_SIZE(chatScreenPositions), I18NCat::DIALOG, screenManager()))->SetEnabledPtr(&g_Config.bEnableNetworkChat);
networkingSettings->Add(new CheckBox(&g_Config.bChatTimestamps, n->T("Show timestamps in chat")))->SetEnabledPtr(&g_Config.bEnableNetworkChat);
networkingSettings->Add(new ItemHeader(n->T("Quick chat")));
CheckBox *qc = networkingSettings->Add(new CheckBox(&g_Config.bEnableQuickChat, n->T("Enable quick chat")));