mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-10-02 02:43:44 +00:00
feat(chat): keep activity lobbies of every world and start their instances through master
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,8 +1,10 @@
|
||||
set(DCHATSERVER_SOURCES
|
||||
"ChatIgnoreList.cpp"
|
||||
"ChatMatchmaking.cpp"
|
||||
"ChatPacketHandler.cpp"
|
||||
"ChatGuilds.cpp"
|
||||
"GuildManager.cpp"
|
||||
"Matchmaking.cpp"
|
||||
"PlayerContainer.cpp"
|
||||
"TeamContainer.cpp"
|
||||
)
|
||||
|
||||
126
dChatServer/ChatMatchmaking.cpp
Normal file
126
dChatServer/ChatMatchmaking.cpp
Normal file
@@ -0,0 +1,126 @@
|
||||
#include "ChatMatchmaking.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "ChatPacketHandler.h"
|
||||
#include "ChatPackets.h"
|
||||
#include "ClientPackets.h"
|
||||
#include "Game.h"
|
||||
#include "GeneralUtils.h"
|
||||
#include "Logger.h"
|
||||
#include "PlayerContainer.h"
|
||||
#include "TeamContainer.h"
|
||||
#include "ZoneInstanceManager.h"
|
||||
#include "dServer.h"
|
||||
|
||||
namespace {
|
||||
Matchmaking::Lobbies g_Lobbies;
|
||||
|
||||
void SendUpdates(const std::vector<Matchmaking::Update>& updates) {
|
||||
for (const auto& update : updates) {
|
||||
const auto& player = Game::playerContainer.GetPlayerData(update.to);
|
||||
if (!player) continue;
|
||||
ClientPackets::MatchUpdate msg;
|
||||
msg.target = update.to;
|
||||
msg.data = update.data;
|
||||
msg.type = update.type;
|
||||
ChatPacketHandler::SendRouted(update.to, player.worldServerSysAddr, msg);
|
||||
}
|
||||
}
|
||||
|
||||
// The players' worlds send them to the instance master started
|
||||
void SendToInstance(const Matchmaking::Match& match, const bool mythranShift, const uint32_t zoneID, const uint32_t zoneInstance, const uint32_t zoneClone, const std::string& serverIP, const uint16_t serverPort) {
|
||||
std::map<SystemAddress, ChatPackets::MatchTransfer> perWorld;
|
||||
std::vector<LWOOBJID> online;
|
||||
for (const auto playerID : match.players) {
|
||||
const auto& player = Game::playerContainer.GetPlayerData(playerID);
|
||||
if (!player || Game::playerContainer.PlayerBeingRemoved(playerID)) continue;
|
||||
online.push_back(playerID);
|
||||
|
||||
auto& transfer = perWorld[player.worldServerSysAddr];
|
||||
transfer.activityID = match.settings.activityID;
|
||||
transfer.zoneID = LWOZONEID(zoneID, zoneInstance, zoneClone);
|
||||
transfer.serverIP = serverIP;
|
||||
transfer.serverPort = serverPort;
|
||||
transfer.mythranShift = mythranShift;
|
||||
transfer.players.push_back(playerID);
|
||||
}
|
||||
|
||||
for (const auto& [world, transfer] : perWorld) {
|
||||
RakNet::BitStream bitStream;
|
||||
transfer.WritePacket(bitStream);
|
||||
Game::server->Send(bitStream, world, false);
|
||||
}
|
||||
|
||||
// Players who start an activity together are a team, as when a world made the lobby (teams of up to
|
||||
// ChatPackets::CreateTeam::MAX_MEMBERS players; bigger groups never were one)
|
||||
if (online.size() > 1 && online.size() <= ChatPackets::CreateTeam::MAX_MEMBERS) {
|
||||
ChatPackets::CreateTeam team;
|
||||
team.leaderID = online.front();
|
||||
team.members = online;
|
||||
team.zoneID = LWOZONEID(zoneID, zoneInstance, zoneClone);
|
||||
TeamContainer::CreateTeamServer(team, UNASSIGNED_SYSTEM_ADDRESS);
|
||||
}
|
||||
|
||||
LOG("Matchmaking: activity %i starts in zone %u instance %u clone %u (%zu players, %zu worlds)", match.settings.activityID, zoneID, zoneInstance, zoneClone, online.size(), perWorld.size());
|
||||
}
|
||||
}
|
||||
|
||||
namespace ChatMatchmaking {
|
||||
Matchmaking::Lobbies& GetLobbies() {
|
||||
return g_Lobbies;
|
||||
}
|
||||
|
||||
void HandleMatchRequest(const ChatPackets::MatchRequest& request, const SystemAddress& sysAddr) {
|
||||
const auto& player = Game::playerContainer.GetPlayerData(request.playerID);
|
||||
// Only players this chat server knows are in the world that sent it
|
||||
if (!player || player.worldServerSysAddr != sysAddr) return;
|
||||
|
||||
std::vector<Matchmaking::Update> updates;
|
||||
switch (request.type) {
|
||||
case ChatPackets::eMatchRequestType::JOIN: {
|
||||
Matchmaking::ActivitySettings settings;
|
||||
settings.activityID = request.activityID;
|
||||
settings.instanceMapID = request.instanceMapID;
|
||||
settings.minTeams = request.minTeams;
|
||||
settings.maxTeams = request.maxTeams;
|
||||
settings.minTeamSize = request.minTeamSize;
|
||||
settings.maxTeamSize = request.maxTeamSize;
|
||||
settings.waitTime = static_cast<float>(request.waitTime) / 1000.0f;
|
||||
settings.startDelay = static_cast<float>(request.startDelay) / 1000.0f;
|
||||
const auto& name = request.playerName.empty() ? player.playerName : request.playerName;
|
||||
g_Lobbies.Join(request.playerID, name, request.playerChoices, settings, updates);
|
||||
break;
|
||||
}
|
||||
case ChatPackets::eMatchRequestType::READY:
|
||||
g_Lobbies.SetReady(request.playerID, request.value != 0, updates);
|
||||
break;
|
||||
case ChatPackets::eMatchRequestType::LEAVE:
|
||||
g_Lobbies.Leave(request.playerID, updates);
|
||||
break;
|
||||
}
|
||||
SendUpdates(updates);
|
||||
}
|
||||
|
||||
void PlayerLeftWorld(const LWOOBJID playerID) {
|
||||
std::vector<Matchmaking::Update> updates;
|
||||
if (g_Lobbies.Leave(playerID, updates)) SendUpdates(updates);
|
||||
}
|
||||
|
||||
void Update(const float deltaTime) {
|
||||
if (g_Lobbies.GetLobbies().empty()) return;
|
||||
std::vector<Matchmaking::Update> updates;
|
||||
const auto matches = g_Lobbies.Tick(deltaTime, updates);
|
||||
SendUpdates(updates);
|
||||
for (const auto& match : matches) StartMatch(match);
|
||||
}
|
||||
|
||||
void StartMatch(const Matchmaking::Match& match) {
|
||||
// A clone no other instance has: master starts a new instance of the zone for this match alone
|
||||
const auto cloneID = GeneralUtils::GenerateRandomNumber<uint32_t>(1, UINT32_MAX);
|
||||
ZoneInstanceManager::Instance()->RequestZoneTransfer(Game::server, match.settings.instanceMapID, cloneID, false,
|
||||
[match](bool mythranShift, uint32_t zoneID, uint32_t zoneInstance, uint32_t zoneClone, std::string serverIP, uint16_t serverPort) {
|
||||
SendToInstance(match, mythranShift, zoneID, zoneInstance, zoneClone, serverIP, serverPort);
|
||||
});
|
||||
}
|
||||
}
|
||||
34
dChatServer/ChatMatchmaking.h
Normal file
34
dChatServer/ChatMatchmaking.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#ifndef CHATMATCHMAKING_H
|
||||
#define CHATMATCHMAKING_H
|
||||
|
||||
#include "dCommonVars.h"
|
||||
#include "Matchmaking.h"
|
||||
|
||||
struct SystemAddress;
|
||||
|
||||
namespace ChatPackets {
|
||||
struct MatchRequest;
|
||||
}
|
||||
|
||||
/**
|
||||
* The chat server's activity matchmaking (docs/Matchmaking.md): lobbies across every world. Worlds send the players'
|
||||
* lobby joins, readies and leaves; lobby updates go to the clients through their worlds; when a countdown runs out
|
||||
* master starts one instance of the activity's zone and each player's world is told to send them there.
|
||||
* Runs on the chat server's main thread only.
|
||||
*/
|
||||
namespace ChatMatchmaking {
|
||||
void HandleMatchRequest(const ChatPackets::MatchRequest& request, const SystemAddress& sysAddr);
|
||||
|
||||
// The player left their world (logged out, or loaded into another one): they leave their lobby
|
||||
void PlayerLeftWorld(LWOOBJID playerID);
|
||||
|
||||
// Once a frame: the lobby countdowns
|
||||
void Update(float deltaTime);
|
||||
|
||||
// Starts the instance of a formed match and tells the players' worlds (also used by tests)
|
||||
void StartMatch(const Matchmaking::Match& match);
|
||||
|
||||
Matchmaking::Lobbies& GetLobbies();
|
||||
}
|
||||
|
||||
#endif // CHATMATCHMAKING_H
|
||||
@@ -33,6 +33,7 @@
|
||||
#include "ChatPackets.h"
|
||||
#include "master/LiveUpdate.h"
|
||||
#include "MasterPackets.h"
|
||||
#include "ChatMatchmaking.h"
|
||||
|
||||
#include "Game.h"
|
||||
#include "Server.h"
|
||||
@@ -169,6 +170,10 @@ int main(int argc, char** argv) {
|
||||
Profiler::Scope scope("Player container update", Profiler::Phase::ENTITIES);
|
||||
Game::playerContainer.Update(deltaTime);
|
||||
}
|
||||
{
|
||||
Profiler::Scope scope("Matchmaking", Profiler::Phase::ENTITIES);
|
||||
ChatMatchmaking::Update(deltaTime);
|
||||
}
|
||||
|
||||
//Check for packets here:
|
||||
//ReceiveFromMaster also handles the master packets if needed; it hands back the ones for us.
|
||||
@@ -283,7 +288,15 @@ namespace {
|
||||
handlers.On<GuildKick>(Chat::GUILD_KICK, [](const GuildKick& kick, const SystemAddress&) { ChatGuilds::Get().Kick(kick.playerID, kick.kickedPlayer.GetAsString()); });
|
||||
handlers.On<GuildSetRank>(Chat::GUILD_SET_RANK, [](const GuildSetRank& rank, const SystemAddress&) { ChatGuilds::Get().SetRank(rank.playerID, rank.targetPlayer.GetAsString(), static_cast<eGuildRank>(rank.rank)); });
|
||||
handlers.On<GuildDisband>(Chat::GUILD_DISBAND, [](const GuildDisband& disband, const SystemAddress&) { ChatGuilds::Get().Disband(disband.playerID); });
|
||||
handlers.On<UnexpectedDisconnect>(Chat::UNEXPECTED_DISCONNECT, [](const UnexpectedDisconnect& notify, const SystemAddress& sysAddr) { Game::playerContainer.ScheduleRemovePlayer(notify, sysAddr); });
|
||||
handlers.On<UnexpectedDisconnect>(Chat::UNEXPECTED_DISCONNECT, [](const UnexpectedDisconnect& notify, const SystemAddress& sysAddr) {
|
||||
// The world they are in lost them: they can't be sent from it to an activity. (A world they already left
|
||||
// can say so after they joined a lobby in their new one.)
|
||||
const auto& player = Game::playerContainer.GetPlayerData(notify.playerID);
|
||||
if (player && player.worldServerSysAddr == sysAddr) ChatMatchmaking::PlayerLeftWorld(notify.playerID);
|
||||
Game::playerContainer.ScheduleRemovePlayer(notify, sysAddr);
|
||||
});
|
||||
// Activity lobbies across every world (docs/Matchmaking.md)
|
||||
handlers.On<MatchRequest>(Chat::MATCH_REQUEST, ChatMatchmaking::HandleMatchRequest);
|
||||
handlers.On<FindPlayerRequest>(Chat::WHO, ChatPacketHandler::HandleWho);
|
||||
handlers.On<ShowAllRequest>(Chat::SHOW_ALL, ChatPacketHandler::HandleShowAll);
|
||||
handlers.On<AchievementNotify>(Chat::ACHIEVEMENT_NOTIFY, ChatPacketHandler::OnAchievementNotify);
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "dConfig.h"
|
||||
#include "MessageType/Chat.h"
|
||||
#include "TeamContainer.h"
|
||||
#include "ChatMatchmaking.h"
|
||||
#include "ChatGuilds.h"
|
||||
#include "GuildManager.h"
|
||||
|
||||
@@ -58,6 +59,9 @@ void PlayerContainer::InsertPlayer(const ChatPackets::LoginSessionNotify& notify
|
||||
return;
|
||||
}
|
||||
|
||||
// Loaded into a world (a new one when changing zones): a lobby they waited in on their last world is left
|
||||
ChatMatchmaking::PlayerLeftWorld(playerId);
|
||||
|
||||
auto isLogin = !m_Players.contains(playerId);
|
||||
auto& data = m_Players[playerId];
|
||||
data = PlayerData();
|
||||
|
||||
Reference in New Issue
Block a user