waitlist.cpp

main 150 lines · 4.1 KB Raw
Alex Alex Commit Server upload 18/10/2026 23:38
1/**
2 * The Forgotten Server - a free and open-source MMORPG server emulator
3 * Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20#include "otpch.h"
21
22#include "configmanager.h"
23#include "game.h"
24#include "waitlist.h"
25
26#include <tuple>
27
28extern ConfigManager g_config;
29extern Game g_game;
30
31namespace {
32
33struct Wait
34{
35 constexpr Wait(int64_t timeout, uint32_t playerGUID) :
36 timeout(timeout), playerGUID(playerGUID) {}
37
38 int64_t timeout;
39 uint32_t playerGUID;
40};
41
42using WaitList = std::list<Wait>;
43
44void cleanupList(WaitList& list)
45{
46 int64_t time = OTSYS_TIME();
47
48 auto it = list.begin(), end = list.end();
49 while (it != end) {
50 if ((it->timeout - time) <= 0) {
51 it = list.erase(it);
52 } else {
53 ++it;
54 }
55 }
56}
57
58int64_t getTimeout(std::size_t slot)
59{
60 //timeout is set to 15 seconds longer than expected retry attempt
61 return WaitingList::getTime(slot) + 15;
62}
63
64} // namespace
65
66struct WaitListInfo
67{
68 WaitList priorityWaitList;
69 WaitList waitList;
70
71 std::tuple<WaitList&, WaitList::iterator, WaitList::size_type> findClient(const Player *player) {
72 std::size_t slot = 1;
73 for (auto it = priorityWaitList.begin(), end = priorityWaitList.end(); it != end; ++it, ++slot) {
74 if (it->playerGUID == player->getGUID()) {
75 return std::make_tuple(std::ref(priorityWaitList), it, slot);
76 }
77 }
78
79 for (auto it = waitList.begin(), end = waitList.end(); it != end; ++it, ++slot) {
80 if (it->playerGUID == player->getGUID()) {
81 return std::make_tuple(std::ref(waitList), it, slot);
82 }
83 }
84 return std::make_tuple(std::ref(waitList), waitList.end(), slot);
85 }
86};
87
88WaitingList& WaitingList::getInstance()
89{
90 static WaitingList waitingList;
91 return waitingList;
92}
93
94uint8_t WaitingList::getTime(std::size_t slot)
95{
96 if (slot < 5) {
97 return 5;
98 } else if (slot < 10) {
99 return 10;
100 } else if (slot < 20) {
101 return 20;
102 } else if (slot < 50) {
103 return 60;
104 } else {
105 return 120;
106 }
107}
108
109std::size_t WaitingList::clientLogin(const Player* player)
110{
111 // Currentslot = position in wait list, 0 for direct access
112 std::size_t currentSlot = 0;
113 if (player->hasFlag(PlayerFlag_CanAlwaysLogin) || player->getAccountType() >= ACCOUNT_TYPE_GAMEMASTER) {
114 return currentSlot;
115 }
116
117 cleanupList(info->priorityWaitList);
118 cleanupList(info->waitList);
119
120 uint32_t maxPlayers = static_cast<uint32_t>(g_config.getNumber(ConfigManager::MAX_PLAYERS));
121 if (maxPlayers == 0 || (info->priorityWaitList.empty() && info->waitList.empty() && g_game.getPlayersOnline() < maxPlayers)) {
122 return currentSlot;
123 }
124
125 auto result = info->findClient(player);
126 if (std::get<1>(result) != std::get<0>(result).end()) {
127 currentSlot = std::get<2>(result);
128 // If server has capacity for this client, let him in even though his current slot might be higher than 0.
129 if ((g_game.getPlayersOnline() + currentSlot) <= maxPlayers) {
130 std::get<0>(result).erase(std::get<1>(result));
131 return 0;
132 }
133
134 //let them wait a bit longer
135 std::get<1>(result)->timeout = OTSYS_TIME() + (getTimeout(currentSlot) * 1000);
136 return currentSlot;
137 }
138
139 currentSlot = info->priorityWaitList.size();
140 if (player->isPremium()) {
141 info->priorityWaitList.emplace_back(OTSYS_TIME() + (getTimeout(++currentSlot) * 1000), player->getGUID());
142 } else {
143 currentSlot += info->waitList.size();
144 info->waitList.emplace_back(OTSYS_TIME() + (getTimeout(++currentSlot) * 1000), player->getGUID());
145 }
146 return currentSlot;
147}
148
149WaitingList::WaitingList() : info(new WaitListInfo) {}
150
Top