6 Commits

Author SHA1 Message Date
052196d1cd Treat bcast areas 1 and 2 as global 2025-01-02 18:26:00 -05:00
e84f6505b8 Name request monitor events 2025-01-02 18:06:06 -05:00
b483bf7190 Respect name check flag in getPlayerName 2025-01-02 17:44:37 -05:00
6ff51685a8 Move event buffers to Monitor 2025-01-02 17:39:16 -05:00
b4ed31d4fb Dedicated bcast monitor event 2025-01-02 17:18:53 -05:00
36e0667ed2 Add Email and LastPasswordReset columns to Account table (#299)
* Add Email column to Account table

* Add LastPasswordReset timestamp column, missing DB version bump
2024-11-28 23:01:29 -05:00
11 changed files with 92 additions and 37 deletions

8
sql/migration5.sql Normal file
View File

@@ -0,0 +1,8 @@
BEGIN TRANSACTION;
-- New Columns
ALTER TABLE Accounts ADD Email TEXT DEFAULT '' NOT NULL;
ALTER TABLE Accounts ADD LastPasswordReset INTEGER DEFAULT 0 NOT NULL;
-- Update DB Version
UPDATE Meta SET Value = 6 WHERE Key = 'DatabaseVersion';
UPDATE Meta SET Value = strftime('%s', 'now') WHERE Key = 'LastMigration';
COMMIT;

View File

@@ -1,14 +1,16 @@
CREATE TABLE IF NOT EXISTS Accounts ( CREATE TABLE IF NOT EXISTS Accounts (
AccountID INTEGER NOT NULL, AccountID INTEGER NOT NULL,
Login TEXT NOT NULL UNIQUE COLLATE NOCASE, Login TEXT NOT NULL UNIQUE COLLATE NOCASE,
Password TEXT NOT NULL, Password TEXT NOT NULL,
Selected INTEGER DEFAULT 1 NOT NULL, Selected INTEGER DEFAULT 1 NOT NULL,
AccountLevel INTEGER NOT NULL, AccountLevel INTEGER NOT NULL,
Created INTEGER DEFAULT (strftime('%s', 'now')) NOT NULL, Created INTEGER DEFAULT (strftime('%s', 'now')) NOT NULL,
LastLogin INTEGER DEFAULT (strftime('%s', 'now')) NOT NULL, LastLogin INTEGER DEFAULT (strftime('%s', 'now')) NOT NULL,
BannedUntil INTEGER DEFAULT 0 NOT NULL, BannedUntil INTEGER DEFAULT 0 NOT NULL,
BannedSince INTEGER DEFAULT 0 NOT NULL, BannedSince INTEGER DEFAULT 0 NOT NULL,
BanReason TEXT DEFAULT '' NOT NULL, BanReason TEXT DEFAULT '' NOT NULL,
Email TEXT DEFAULT '' NOT NULL,
LastPasswordReset INTEGER DEFAULT 0 NOT NULL,
PRIMARY KEY(AccountID AUTOINCREMENT) PRIMARY KEY(AccountID AUTOINCREMENT)
); );

View File

@@ -1,6 +1,7 @@
#include "Chat.hpp" #include "Chat.hpp"
#include "servers/CNShardServer.hpp" #include "servers/CNShardServer.hpp"
#include "servers/Monitor.hpp"
#include "Player.hpp" #include "Player.hpp"
#include "PlayerManager.hpp" #include "PlayerManager.hpp"
@@ -8,8 +9,6 @@
#include <assert.h> #include <assert.h>
std::vector<std::string> Chat::dump;
using namespace Chat; using namespace Chat;
static void chatHandler(CNSocket* sock, CNPacketData* data) { static void chatHandler(CNSocket* sock, CNPacketData* data) {
@@ -28,7 +27,7 @@ static void chatHandler(CNSocket* sock, CNPacketData* data) {
std::string logLine = "[FreeChat] " + PlayerManager::getPlayerName(plr, true) + ": " + fullChat; std::string logLine = "[FreeChat] " + PlayerManager::getPlayerName(plr, true) + ": " + fullChat;
std::cout << logLine << std::endl; std::cout << logLine << std::endl;
dump.push_back(logLine); Monitor::chats.push_back(logLine);
// send to client // send to client
INITSTRUCT(sP_FE2CL_REP_SEND_FREECHAT_MESSAGE_SUCC, resp); INITSTRUCT(sP_FE2CL_REP_SEND_FREECHAT_MESSAGE_SUCC, resp);
@@ -51,7 +50,7 @@ static void menuChatHandler(CNSocket* sock, CNPacketData* data) {
std::string logLine = "[MenuChat] " + PlayerManager::getPlayerName(plr, true) + ": " + fullChat; std::string logLine = "[MenuChat] " + PlayerManager::getPlayerName(plr, true) + ": " + fullChat;
std::cout << logLine << std::endl; std::cout << logLine << std::endl;
dump.push_back(logLine); Monitor::chats.push_back(logLine);
// send to client // send to client
INITSTRUCT(sP_FE2CL_REP_SEND_MENUCHAT_MESSAGE_SUCC, resp); INITSTRUCT(sP_FE2CL_REP_SEND_MENUCHAT_MESSAGE_SUCC, resp);
@@ -103,14 +102,14 @@ static void announcementHandler(CNSocket* sock, CNPacketData* data) {
memcpy(msg.szAnnounceMsg, announcement->szAnnounceMsg, sizeof(msg.szAnnounceMsg)); memcpy(msg.szAnnounceMsg, announcement->szAnnounceMsg, sizeof(msg.szAnnounceMsg));
std::map<CNSocket*, Player*>::iterator it; std::map<CNSocket*, Player*>::iterator it;
// This value is completely arbitrary, but these make the most sense when you consider the architecture of the game
switch (announcement->iAreaType) { switch (announcement->iAreaType) {
case 0: // area (all players in viewable chunks) case 0: // area (all players in viewable chunks)
sock->sendPacket(msg, P_FE2CL_GM_REP_PC_ANNOUNCE); sock->sendPacket(msg, P_FE2CL_GM_REP_PC_ANNOUNCE);
PlayerManager::sendToViewable(sock, msg, P_FE2CL_GM_REP_PC_ANNOUNCE); PlayerManager::sendToViewable(sock, msg, P_FE2CL_GM_REP_PC_ANNOUNCE);
break; break;
case 1: // shard case 1: // channel
case 2: // world case 2: // shard
break; // not applicable to OpenFusion
case 3: // global (all players) case 3: // global (all players)
for (it = PlayerManager::players.begin(); it != PlayerManager::players.end(); it++) { for (it = PlayerManager::players.begin(); it != PlayerManager::players.end(); it++) {
CNSocket* allSock = it->first; CNSocket* allSock = it->first;
@@ -120,9 +119,12 @@ static void announcementHandler(CNSocket* sock, CNPacketData* data) {
break; break;
} }
std::string logLine = "[Bcast " + std::to_string(announcement->iAreaType) + "] " + PlayerManager::getPlayerName(plr, false) + ": " + AUTOU16TOU8(msg.szAnnounceMsg); std::string logLine = std::to_string(announcement->iAreaType) + " "
std::cout << logLine << std::endl; + std::to_string(announcement->iAnnounceType) + " "
dump.push_back("**" + logLine + "**"); + std::to_string(announcement->iDuringTime) + " "
+ PlayerManager::getPlayerName(plr, false) + ": " + AUTOU16TOU8(msg.szAnnounceMsg);
std::cout << "Broadcast " << logLine << std::endl;
Monitor::bcasts.push_back(logLine);
} }
// Buddy freechatting // Buddy freechatting
@@ -155,7 +157,7 @@ static void buddyChatHandler(CNSocket* sock, CNPacketData* data) {
std::string logLine = "[BuddyChat] " + PlayerManager::getPlayerName(plr) + " (to " + PlayerManager::getPlayerName(otherPlr) + "): " + fullChat; std::string logLine = "[BuddyChat] " + PlayerManager::getPlayerName(plr) + " (to " + PlayerManager::getPlayerName(otherPlr) + "): " + fullChat;
std::cout << logLine << std::endl; std::cout << logLine << std::endl;
dump.push_back(logLine); Monitor::chats.push_back(logLine);
U8toU16(fullChat, (char16_t*)&resp.szFreeChat, sizeof(resp.szFreeChat)); U8toU16(fullChat, (char16_t*)&resp.szFreeChat, sizeof(resp.szFreeChat));
@@ -185,7 +187,7 @@ static void buddyMenuChatHandler(CNSocket* sock, CNPacketData* data) {
std::string logLine = "[BuddyMenuChat] " + PlayerManager::getPlayerName(plr) + " (to " + PlayerManager::getPlayerName(otherPlr) + "): " + fullChat; std::string logLine = "[BuddyMenuChat] " + PlayerManager::getPlayerName(plr) + " (to " + PlayerManager::getPlayerName(otherPlr) + "): " + fullChat;
std::cout << logLine << std::endl; std::cout << logLine << std::endl;
dump.push_back(logLine); Monitor::chats.push_back(logLine);
U8toU16(fullChat, (char16_t*)&resp.szFreeChat, sizeof(resp.szFreeChat)); U8toU16(fullChat, (char16_t*)&resp.szFreeChat, sizeof(resp.szFreeChat));
@@ -218,7 +220,7 @@ static void tradeChatHandler(CNSocket* sock, CNPacketData* data) {
std::string logLine = "[TradeChat] " + PlayerManager::getPlayerName(plr) + " (to " + PlayerManager::getPlayerName(otherPlr) + "): " + fullChat; std::string logLine = "[TradeChat] " + PlayerManager::getPlayerName(plr) + " (to " + PlayerManager::getPlayerName(otherPlr) + "): " + fullChat;
std::cout << logLine << std::endl; std::cout << logLine << std::endl;
dump.push_back(logLine); Monitor::chats.push_back(logLine);
resp.iEmoteCode = pacdat->iEmoteCode; resp.iEmoteCode = pacdat->iEmoteCode;
sock->sendPacket(resp, P_FE2CL_REP_PC_TRADE_EMOTES_CHAT); sock->sendPacket(resp, P_FE2CL_REP_PC_TRADE_EMOTES_CHAT);
@@ -241,7 +243,7 @@ static void groupChatHandler(CNSocket* sock, CNPacketData* data) {
std::string logLine = "[GroupChat] " + PlayerManager::getPlayerName(plr, true) + ": " + fullChat; std::string logLine = "[GroupChat] " + PlayerManager::getPlayerName(plr, true) + ": " + fullChat;
std::cout << logLine << std::endl; std::cout << logLine << std::endl;
dump.push_back(logLine); Monitor::chats.push_back(logLine);
// send to client // send to client
INITSTRUCT(sP_FE2CL_REP_SEND_ALL_GROUP_FREECHAT_MESSAGE_SUCC, resp); INITSTRUCT(sP_FE2CL_REP_SEND_ALL_GROUP_FREECHAT_MESSAGE_SUCC, resp);
@@ -264,7 +266,7 @@ static void groupMenuChatHandler(CNSocket* sock, CNPacketData* data) {
std::string logLine = "[GroupMenuChat] " + PlayerManager::getPlayerName(plr, true) + ": " + fullChat; std::string logLine = "[GroupMenuChat] " + PlayerManager::getPlayerName(plr, true) + ": " + fullChat;
std::cout << logLine << std::endl; std::cout << logLine << std::endl;
dump.push_back(logLine); Monitor::chats.push_back(logLine);
// send to client // send to client
INITSTRUCT(sP_FE2CL_REP_SEND_ALL_GROUP_MENUCHAT_MESSAGE_SUCC, resp); INITSTRUCT(sP_FE2CL_REP_SEND_ALL_GROUP_MENUCHAT_MESSAGE_SUCC, resp);

View File

@@ -8,7 +8,6 @@
#include <vector> #include <vector>
namespace Chat { namespace Chat {
extern std::vector<std::string> dump;
void init(); void init();
void sendServerMessage(CNSocket* sock, std::string msg); // uses MOTD void sendServerMessage(CNSocket* sock, std::string msg); // uses MOTD

View File

@@ -3,6 +3,7 @@
#include "core/Core.hpp" #include "core/Core.hpp"
#include "db/Database.hpp" #include "db/Database.hpp"
#include "servers/CNShardServer.hpp" #include "servers/CNShardServer.hpp"
#include "servers/Monitor.hpp"
#include "PlayerManager.hpp" #include "PlayerManager.hpp"
#include "Items.hpp" #include "Items.hpp"
@@ -10,8 +11,6 @@
using namespace Email; using namespace Email;
std::vector<std::string> Email::dump;
// New email notification // New email notification
static void emailUpdateCheck(CNSocket* sock, CNPacketData* data) { static void emailUpdateCheck(CNSocket* sock, CNPacketData* data) {
INITSTRUCT(sP_FE2CL_REP_PC_NEW_EMAIL, resp); INITSTRUCT(sP_FE2CL_REP_PC_NEW_EMAIL, resp);
@@ -324,7 +323,7 @@ static void emailSend(CNSocket* sock, CNPacketData* data) {
std::string logEmail = "[Email] " + PlayerManager::getPlayerName(plr, true) + " (to " + PlayerManager::getPlayerName(&otherPlr, true) + "): <" + email.SubjectLine + ">\n" + email.MsgBody; std::string logEmail = "[Email] " + PlayerManager::getPlayerName(plr, true) + " (to " + PlayerManager::getPlayerName(&otherPlr, true) + "): <" + email.SubjectLine + ">\n" + email.MsgBody;
std::cout << logEmail << std::endl; std::cout << logEmail << std::endl;
dump.push_back(logEmail); Monitor::emails.push_back(logEmail);
// notification to recipient if online // notification to recipient if online
CNSocket* recipient = PlayerManager::getSockFromID(pkt->iTo_PCUID); CNSocket* recipient = PlayerManager::getSockFromID(pkt->iTo_PCUID);

View File

@@ -4,7 +4,5 @@
#include <string> #include <string>
namespace Email { namespace Email {
extern std::vector<std::string> dump;
void init(); void init();
} }

View File

@@ -620,6 +620,10 @@ std::string PlayerManager::getPlayerName(Player *plr, bool id) {
if (plr == nullptr) if (plr == nullptr)
return "NOT IN GAME"; return "NOT IN GAME";
if (plr->PCStyle.iNameCheck == 0) {
return "Player " + std::to_string(plr->iID);
}
std::string ret = ""; std::string ret = "";
if (id && plr->accountLevel <= 30) if (id && plr->accountLevel <= 30)
ret += "(GM) "; ret += "(GM) ";

View File

@@ -5,7 +5,7 @@
#include <string> #include <string>
#include <vector> #include <vector>
#define DATABASE_VERSION 5 #define DATABASE_VERSION 6
namespace Database { namespace Database {

View File

@@ -1,4 +1,5 @@
#include "servers/CNLoginServer.hpp" #include "servers/CNLoginServer.hpp"
#include "servers/Monitor.hpp"
#include "core/CNShared.hpp" #include "core/CNShared.hpp"
#include "db/Database.hpp" #include "db/Database.hpp"
@@ -312,6 +313,16 @@ void CNLoginServer::nameSave(CNSocket* sock, CNPacketData* data) {
std::cout << "[WARN] Login Server: Database failed to create new character!" << std::endl; std::cout << "[WARN] Login Server: Database failed to create new character!" << std::endl;
return invalidCharacter(sock); return invalidCharacter(sock);
} }
Player plr;
Database::getPlayer(&plr, (int)resp.iPC_UID);
// fire name check event if needed
if (plr.PCStyle.iNameCheck == 0) {
std::string namereq = std::to_string(resp.iPC_UID) + " " + AUTOU16TOU8(save->szFirstName) + " " + AUTOU16TOU8(save->szLastName);
Monitor::namereqs.push_back(namereq);
}
resp.iSlotNum = save->iSlotNum; resp.iSlotNum = save->iSlotNum;
resp.iGender = save->iGender; resp.iGender = save->iGender;
@@ -327,7 +338,9 @@ void CNLoginServer::nameSave(CNSocket* sock, CNPacketData* data) {
DEBUGLOG( DEBUGLOG(
std::cout << "Login Server: new character created" << std::endl; std::cout << "Login Server: new character created" << std::endl;
std::cout << "\tSlot: " << (int)save->iSlotNum << std::endl; std::cout << "\tSlot: " << (int)save->iSlotNum << std::endl;
std::cout << "\tName: " << AUTOU16TOU8(save->szFirstName) << " " << AUTOU16TOU8(save->szLastName) << std::endl; std::cout << "\tName: " << AUTOU16TOU8(save->szFirstName) << " " << AUTOU16TOU8(save->szLastName);
if (plr.PCStyle.iNameCheck == 0) std::cout << " (pending approval)";
std::cout << std::endl;
) )
} }

View File

@@ -14,6 +14,13 @@ static std::mutex sockLock; // guards socket list
static std::list<SOCKET> sockets; static std::list<SOCKET> sockets;
static sockaddr_in address; static sockaddr_in address;
std::vector<std::string> Monitor::chats;
std::vector<std::string> Monitor::bcasts;
std::vector<std::string> Monitor::emails;
std::vector<std::string> Monitor::namereqs;
using namespace Monitor;
static bool transmit(std::list<SOCKET>::iterator& it, char *buff, int len) { static bool transmit(std::list<SOCKET>::iterator& it, char *buff, int len) {
int n = 0; int n = 0;
int sock = *it; int sock = *it;
@@ -99,15 +106,23 @@ outer:
} }
// chat // chat
for (auto& str : Chat::dump) { for (auto& str : chats) {
n = std::snprintf(buff, sizeof(buff), "chat %s\n", str.c_str()); n = std::snprintf(buff, sizeof(buff), "chat %s\n", str.c_str());
if (!transmit(it, buff, n)) if (!transmit(it, buff, n))
goto outer; goto outer;
} }
// announcements
for (auto& str : bcasts) {
n = std::snprintf(buff, sizeof(buff), "bcast %s\n", str.c_str());
if (!transmit(it, buff, n))
goto outer;
}
// emails // emails
for (auto& str : Email::dump) { for (auto& str : emails) {
n = process_email(buff, str); n = process_email(buff, str);
if (!transmit(it, buff, n)) if (!transmit(it, buff, n))
@@ -117,14 +132,24 @@ outer:
goto outer; goto outer;
} }
// name requests
for (auto& str : namereqs) {
n = std::snprintf(buff, sizeof(buff), "namereq %s\n", str.c_str());
if (!transmit(it, buff, n))
goto outer;
}
if (!transmit(it, (char*)"end\n", 4)) if (!transmit(it, (char*)"end\n", 4))
continue; continue;
it++; it++;
} }
Chat::dump.clear(); chats.clear();
Email::dump.clear(); bcasts.clear();
emails.clear();
namereqs.clear();
} }
bool Monitor::acceptConnection(SOCKET fd, uint16_t revents) { bool Monitor::acceptConnection(SOCKET fd, uint16_t revents) {

View File

@@ -3,6 +3,11 @@
#include "core/Core.hpp" #include "core/Core.hpp"
namespace Monitor { namespace Monitor {
extern std::vector<std::string> chats;
extern std::vector<std::string> bcasts;
extern std::vector<std::string> emails;
extern std::vector<std::string> namereqs;
SOCKET init(); SOCKET init();
bool acceptConnection(SOCKET, uint16_t); bool acceptConnection(SOCKET, uint16_t);
}; };