Server upload

RD Core / Commit #2

Commit Server upload

Alex Alex committed 18/10/2026 23:38 main Patch
1,106 files +223,404 -0
A src/script.h +40-0 View file
@@ -0,0 +1,40 @@
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+#ifndef FS_SCRIPTS_H
21+#define FS_SCRIPTS_H
22+
23+#include "luascript.h"
24+#include "enums.h"
25+
26+class Scripts
27+{
28+ public:
29+ Scripts();
30+ ~Scripts();
31+
32+ bool loadScripts(std::string folderName, bool isLib, bool reload);
33+ LuaScriptInterface& getScriptInterface() {
34+ return scriptInterface;
35+ }
36+ private:
37+ LuaScriptInterface scriptInterface;
38+};
39+
40+#endif
A src/scriptmanager.cpp +127-0 View file
@@ -0,0 +1,127 @@
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 "scriptmanager.h"
23+
24+#include "actions.h"
25+#include "chat.h"
26+#include "talkaction.h"
27+#include "spells.h"
28+#include "movement.h"
29+#include "weapons.h"
30+#include "globalevent.h"
31+#include "events.h"
32+#include "script.h"
33+
34+Actions* g_actions = nullptr;
35+CreatureEvents* g_creatureEvents = nullptr;
36+Chat* g_chat = nullptr;
37+Events* g_events = nullptr;
38+GlobalEvents* g_globalEvents = nullptr;
39+Spells* g_spells = nullptr;
40+TalkActions* g_talkActions = nullptr;
41+MoveEvents* g_moveEvents = nullptr;
42+Weapons* g_weapons = nullptr;
43+Scripts* g_scripts = nullptr;
44+
45+extern LuaEnvironment g_luaEnvironment;
46+
47+ScriptingManager::~ScriptingManager()
48+{
49+ delete g_events;
50+ delete g_weapons;
51+ delete g_spells;
52+ delete g_actions;
53+ delete g_talkActions;
54+ delete g_moveEvents;
55+ delete g_chat;
56+ delete g_creatureEvents;
57+ delete g_globalEvents;
58+ delete g_scripts;
59+}
60+
61+bool ScriptingManager::loadScriptSystems()
62+{
63+ if (g_luaEnvironment.loadFile("data/global.lua") == -1) {
64+ std::cout << "[Warning - ScriptingManager::loadScriptSystems] Can not load data/global.lua" << std::endl;
65+ }
66+
67+ g_scripts = new Scripts();
68+ std::cout << ">> Loading lua libs" << std::endl;
69+ if (!g_scripts->loadScripts("scripts/lib", true, false)) {
70+ std::cout << "> ERROR: Unable to load lua libs!" << std::endl;
71+ return false;
72+ }
73+
74+ g_chat = new Chat();
75+
76+ g_weapons = new Weapons();
77+ if (!g_weapons->loadFromXml()) {
78+ std::cout << "> ERROR: Unable to load weapons!" << std::endl;
79+ return false;
80+ }
81+
82+ g_weapons->loadDefaults();
83+
84+ g_spells = new Spells();
85+ if (!g_spells->loadFromXml()) {
86+ std::cout << "> ERROR: Unable to load spells!" << std::endl;
87+ return false;
88+ }
89+
90+ g_actions = new Actions();
91+ if (!g_actions->loadFromXml()) {
92+ std::cout << "> ERROR: Unable to load actions!" << std::endl;
93+ return false;
94+ }
95+
96+ g_talkActions = new TalkActions();
97+ if (!g_talkActions->loadFromXml()) {
98+ std::cout << "> ERROR: Unable to load talk actions!" << std::endl;
99+ return false;
100+ }
101+
102+ g_moveEvents = new MoveEvents();
103+ if (!g_moveEvents->loadFromXml()) {
104+ std::cout << "> ERROR: Unable to load move events!" << std::endl;
105+ return false;
106+ }
107+
108+ g_creatureEvents = new CreatureEvents();
109+ if (!g_creatureEvents->loadFromXml()) {
110+ std::cout << "> ERROR: Unable to load creature events!" << std::endl;
111+ return false;
112+ }
113+
114+ g_globalEvents = new GlobalEvents();
115+ if (!g_globalEvents->loadFromXml()) {
116+ std::cout << "> ERROR: Unable to load global events!" << std::endl;
117+ return false;
118+ }
119+
120+ g_events = new Events();
121+ if (!g_events->load()) {
122+ std::cout << "> ERROR: Unable to load events!" << std::endl;
123+ return false;
124+ }
125+
126+ return true;
127+}
A src/scriptmanager.h +41-0 View file
@@ -0,0 +1,41 @@
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+#ifndef FS_SCRIPTMANAGER_H_F9428B7803A44FB88EB1A915CFD37F8B
21+#define FS_SCRIPTMANAGER_H_F9428B7803A44FB88EB1A915CFD37F8B
22+
23+class ScriptingManager
24+{
25+ public:
26+ ScriptingManager() = default;
27+ ~ScriptingManager();
28+
29+ // non-copyable
30+ ScriptingManager(const ScriptingManager&) = delete;
31+ ScriptingManager& operator=(const ScriptingManager&) = delete;
32+
33+ static ScriptingManager& getInstance() {
34+ static ScriptingManager instance;
35+ return instance;
36+ }
37+
38+ bool loadScriptSystems();
39+};
40+
41+#endif
A src/server.cpp +206-0 View file
@@ -0,0 +1,206 @@
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 "outputmessage.h"
23+#include "server.h"
24+#include "scheduler.h"
25+#include "configmanager.h"
26+#include "ban.h"
27+
28+extern ConfigManager g_config;
29+Ban g_bans;
30+
31+ServiceManager::~ServiceManager()
32+{
33+ stop();
34+}
35+
36+void ServiceManager::die()
37+{
38+ io_service.stop();
39+}
40+
41+void ServiceManager::run()
42+{
43+ assert(!running);
44+ running = true;
45+ io_service.run();
46+}
47+
48+void ServiceManager::stop()
49+{
50+ if (!running) {
51+ return;
52+ }
53+
54+ running = false;
55+
56+ for (auto& servicePortIt : acceptors) {
57+ try {
58+ io_service.post(std::bind(&ServicePort::onStopServer, servicePortIt.second));
59+ } catch (boost::system::system_error& e) {
60+ std::cout << "[ServiceManager::stop] Network Error: " << e.what() << std::endl;
61+ }
62+ }
63+
64+ acceptors.clear();
65+
66+ death_timer.expires_from_now(boost::posix_time::seconds(3));
67+ death_timer.async_wait(std::bind(&ServiceManager::die, this));
68+}
69+
70+ServicePort::~ServicePort()
71+{
72+ close();
73+}
74+
75+bool ServicePort::is_single_socket() const
76+{
77+ return !services.empty() && services.front()->is_single_socket();
78+}
79+
80+std::string ServicePort::get_protocol_names() const
81+{
82+ if (services.empty()) {
83+ return std::string();
84+ }
85+
86+ std::string str = services.front()->get_protocol_name();
87+ for (size_t i = 1; i < services.size(); ++i) {
88+ str.push_back(',');
89+ str.push_back(' ');
90+ str.append(services[i]->get_protocol_name());
91+ }
92+ return str;
93+}
94+
95+void ServicePort::accept()
96+{
97+ if (!acceptor) {
98+ return;
99+ }
100+
101+ auto connection = ConnectionManager::getInstance().createConnection(io_service, shared_from_this());
102+ acceptor->async_accept(connection->getSocket(), std::bind(&ServicePort::onAccept, shared_from_this(), connection, std::placeholders::_1));
103+}
104+
105+void ServicePort::onAccept(Connection_ptr connection, const boost::system::error_code& error)
106+{
107+ if (!error) {
108+ if (services.empty()) {
109+ return;
110+ }
111+
112+ auto remote_ip = connection->getIP();
113+ if (remote_ip != 0 && g_bans.acceptConnection(remote_ip)) {
114+ Service_ptr service = services.front();
115+ if (service->is_single_socket()) {
116+ connection->accept(service->make_protocol(connection));
117+ } else {
118+ connection->accept();
119+ }
120+ } else {
121+ connection->close(Connection::FORCE_CLOSE);
122+ }
123+
124+ accept();
125+ } else if (error != boost::asio::error::operation_aborted) {
126+ if (!pendingStart) {
127+ close();
128+ pendingStart = true;
129+ g_scheduler.addEvent(createSchedulerTask(15000,
130+ std::bind(&ServicePort::openAcceptor, std::weak_ptr<ServicePort>(shared_from_this()), serverPort)));
131+ }
132+ }
133+}
134+
135+Protocol_ptr ServicePort::make_protocol(bool checksummed, NetworkMessage& msg, const Connection_ptr& connection) const
136+{
137+ uint8_t protocolID = msg.getByte();
138+ for (auto& service : services) {
139+ if (protocolID != service->get_protocol_identifier()) {
140+ continue;
141+ }
142+
143+ if ((checksummed && service->is_checksummed()) || !service->is_checksummed()) {
144+ return service->make_protocol(connection);
145+ }
146+ }
147+ return nullptr;
148+}
149+
150+void ServicePort::onStopServer()
151+{
152+ close();
153+}
154+
155+void ServicePort::openAcceptor(std::weak_ptr<ServicePort> weak_service, uint16_t port)
156+{
157+ if (auto service = weak_service.lock()) {
158+ service->open(port);
159+ }
160+}
161+
162+void ServicePort::open(uint16_t port)
163+{
164+ close();
165+
166+ serverPort = port;
167+ pendingStart = false;
168+
169+ try {
170+ if (g_config.getBoolean(ConfigManager::BIND_ONLY_GLOBAL_ADDRESS)) {
171+ acceptor.reset(new boost::asio::ip::tcp::acceptor(io_service, boost::asio::ip::tcp::endpoint(
172+ boost::asio::ip::address(boost::asio::ip::address_v4::from_string(g_config.getString(ConfigManager::IP))), serverPort)));
173+ } else {
174+ acceptor.reset(new boost::asio::ip::tcp::acceptor(io_service, boost::asio::ip::tcp::endpoint(
175+ boost::asio::ip::address(boost::asio::ip::address_v4(INADDR_ANY)), serverPort)));
176+ }
177+
178+ acceptor->set_option(boost::asio::ip::tcp::no_delay(true));
179+
180+ accept();
181+ } catch (boost::system::system_error& e) {
182+ std::cout << "[ServicePort::open] Error: " << e.what() << std::endl;
183+
184+ pendingStart = true;
185+ g_scheduler.addEvent(createSchedulerTask(15000,
186+ std::bind(&ServicePort::openAcceptor, std::weak_ptr<ServicePort>(shared_from_this()), port)));
187+ }
188+}
189+
190+void ServicePort::close()
191+{
192+ if (acceptor && acceptor->is_open()) {
193+ boost::system::error_code error;
194+ acceptor->close(error);
195+ }
196+}
197+
198+bool ServicePort::add_service(const Service_ptr& new_svc)
199+{
200+ if (std::any_of(services.begin(), services.end(), [](const Service_ptr& svc) {return svc->is_single_socket();})) {
201+ return false;
202+ }
203+
204+ services.push_back(new_svc);
205+ return true;
206+}
A src/server.h +156-0 View file
@@ -0,0 +1,156 @@
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+#ifndef FS_SERVER_H_984DA68ABF744127850F90CC710F281B
21+#define FS_SERVER_H_984DA68ABF744127850F90CC710F281B
22+
23+#include "connection.h"
24+#include "signals.h"
25+#include <memory>
26+
27+class Protocol;
28+
29+class ServiceBase
30+{
31+ public:
32+ virtual bool is_single_socket() const = 0;
33+ virtual bool is_checksummed() const = 0;
34+ virtual uint8_t get_protocol_identifier() const = 0;
35+ virtual const char* get_protocol_name() const = 0;
36+
37+ virtual Protocol_ptr make_protocol(const Connection_ptr& c) const = 0;
38+};
39+
40+template <typename ProtocolType>
41+class Service final : public ServiceBase
42+{
43+ public:
44+ bool is_single_socket() const override {
45+ return ProtocolType::server_sends_first;
46+ }
47+ bool is_checksummed() const override {
48+ return ProtocolType::use_checksum;
49+ }
50+ uint8_t get_protocol_identifier() const override {
51+ return ProtocolType::protocol_identifier;
52+ }
53+ const char* get_protocol_name() const override {
54+ return ProtocolType::protocol_name();
55+ }
56+
57+ Protocol_ptr make_protocol(const Connection_ptr& c) const override {
58+ return std::make_shared<ProtocolType>(c);
59+ }
60+};
61+
62+class ServicePort : public std::enable_shared_from_this<ServicePort>
63+{
64+ public:
65+ explicit ServicePort(boost::asio::io_service& io_service) : io_service(io_service) {}
66+ ~ServicePort();
67+
68+ // non-copyable
69+ ServicePort(const ServicePort&) = delete;
70+ ServicePort& operator=(const ServicePort&) = delete;
71+
72+ static void openAcceptor(std::weak_ptr<ServicePort> weak_service, uint16_t port);
73+ void open(uint16_t port);
74+ void close();
75+ bool is_single_socket() const;
76+ std::string get_protocol_names() const;
77+
78+ bool add_service(const Service_ptr& new_svc);
79+ Protocol_ptr make_protocol(bool checksummed, NetworkMessage& msg, const Connection_ptr& connection) const;
80+
81+ void onStopServer();
82+ void onAccept(Connection_ptr connection, const boost::system::error_code& error);
83+
84+ private:
85+ void accept();
86+
87+ boost::asio::io_service& io_service;
88+ std::unique_ptr<boost::asio::ip::tcp::acceptor> acceptor;
89+ std::vector<Service_ptr> services;
90+
91+ uint16_t serverPort = 0;
92+ bool pendingStart = false;
93+};
94+
95+class ServiceManager
96+{
97+ public:
98+ ServiceManager() = default;
99+ ~ServiceManager();
100+
101+ // non-copyable
102+ ServiceManager(const ServiceManager&) = delete;
103+ ServiceManager& operator=(const ServiceManager&) = delete;
104+
105+ void run();
106+ void stop();
107+
108+ template <typename ProtocolType>
109+ bool add(uint16_t port);
110+
111+ bool is_running() const {
112+ return acceptors.empty() == false;
113+ }
114+
115+ private:
116+ void die();
117+
118+ std::unordered_map<uint16_t, ServicePort_ptr> acceptors;
119+
120+ boost::asio::io_service io_service;
121+ Signals signals{io_service};
122+ boost::asio::deadline_timer death_timer { io_service };
123+ bool running = false;
124+};
125+
126+template <typename ProtocolType>
127+bool ServiceManager::add(uint16_t port)
128+{
129+ if (port == 0) {
130+ std::cout << "ERROR: No port provided for service " << ProtocolType::protocol_name() << ". Service disabled." << std::endl;
131+ return false;
132+ }
133+
134+ ServicePort_ptr service_port;
135+
136+ auto foundServicePort = acceptors.find(port);
137+
138+ if (foundServicePort == acceptors.end()) {
139+ service_port = std::make_shared<ServicePort>(io_service);
140+ service_port->open(port);
141+ acceptors[port] = service_port;
142+ } else {
143+ service_port = foundServicePort->second;
144+
145+ if (service_port->is_single_socket() || ProtocolType::server_sends_first) {
146+ std::cout << "ERROR: " << ProtocolType::protocol_name() <<
147+ " and " << service_port->get_protocol_names() <<
148+ " cannot use the same port " << port << '.' << std::endl;
149+ return false;
150+ }
151+ }
152+
153+ return service_port->add_service(std::make_shared<Service<ProtocolType>>());
154+}
155+
156+#endif
A src/shaders.cpp +57-0 View file
@@ -0,0 +1,57 @@
1+#include "otpch.h"
2+
3+#include "shaders.h"
4+
5+#include "pugicast.h"
6+#include "tools.h"
7+
8+bool Shaders::reload()
9+{
10+ shaders.clear();
11+ return loadFromXml();
12+}
13+
14+bool Shaders::loadFromXml()
15+{
16+ pugi::xml_document doc;
17+ pugi::xml_parse_result result = doc.load_file("data/XML/shaders.xml");
18+ if (!result) {
19+ printXMLError("Error - Shaders::loadFromXml", "data/XML/shaders.xml", result);
20+ return false;
21+ }
22+
23+ for (auto shaderNode : doc.child("shaders").children()) {
24+ shaders.emplace_back(
25+ static_cast<uint8_t>(pugi::cast<uint16_t>(shaderNode.attribute("id").value())),
26+ shaderNode.attribute("name").as_string(),
27+ shaderNode.attribute("premium").as_bool()
28+ );
29+ }
30+ shaders.shrink_to_fit();
31+ return true;
32+}
33+
34+Shader* Shaders::getShaderByID(uint8_t id)
35+{
36+ auto it = std::find_if(shaders.begin(), shaders.end(), [id](const Shader& shader) {
37+ return shader.id == id;
38+ });
39+
40+ return it != shaders.end() ? &*it : nullptr;
41+}
42+
43+Shader* Shaders::getShaderByName(const std::string& name) {
44+ auto shaderName = name.c_str();
45+ for (auto& it : shaders) {
46+ if (strcasecmp(shaderName, it.name.c_str()) == 0) {
47+ return &it;
48+ }
49+ }
50+
51+ return nullptr;
52+}
53+
54+std::string Shaders::getShaderNameByID(uint8_t id) {
55+ auto shader = getShaderByID(id);
56+ return shader ? shader->name : "";
57+}
A src/shaders.h +31-0 View file
@@ -0,0 +1,31 @@
1+#ifndef FS_SHADERS_H
2+#define FS_SHADERS_H
3+
4+struct Shader
5+{
6+ Shader(uint8_t id, std::string name, bool premium) :
7+ name(std::move(name)), id(id), premium(premium) {}
8+
9+ uint8_t id;
10+ std::string name;
11+ bool premium;
12+};
13+
14+class Shaders
15+{
16+ public:
17+ bool reload();
18+ bool loadFromXml();
19+ Shader* getShaderByID(uint8_t id);
20+ Shader* getShaderByName(const std::string& name);
21+ std::string getShaderNameByID(uint8_t id);
22+
23+ const std::vector<Shader>& getShaders() const {
24+ return shaders;
25+ }
26+
27+ private:
28+ std::vector<Shader> shaders;
29+};
30+
31+#endif
A src/signals.cpp +220-0 View file
@@ -0,0 +1,220 @@
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+#include <csignal>
22+
23+#include "signals.h"
24+#include "tasks.h"
25+#include "game.h"
26+#include "actions.h"
27+#include "configmanager.h"
28+#include "spells.h"
29+#include "talkaction.h"
30+#include "movement.h"
31+#include "weapons.h"
32+#include "raids.h"
33+#include "quests.h"
34+#include "mounts.h"
35+#include "auras.h"
36+#include "wings.h"
37+#include "globalevent.h"
38+#include "monster.h"
39+#include "events.h"
40+#include "scheduler.h"
41+#include "databasetasks.h"
42+
43+
44+extern Scheduler g_scheduler;
45+extern DatabaseTasks g_databaseTasks;
46+extern Dispatcher g_dispatcher;
47+
48+extern ConfigManager g_config;
49+extern Actions* g_actions;
50+extern Monsters g_monsters;
51+extern TalkActions* g_talkActions;
52+extern MoveEvents* g_moveEvents;
53+extern Spells* g_spells;
54+extern Weapons* g_weapons;
55+extern Game g_game;
56+extern CreatureEvents* g_creatureEvents;
57+extern GlobalEvents* g_globalEvents;
58+extern Events* g_events;
59+extern Chat* g_chat;
60+extern LuaEnvironment g_luaEnvironment;
61+
62+using ErrorCode = boost::system::error_code;
63+
64+Signals::Signals(boost::asio::io_service& service) :
65+ set(service)
66+{
67+ set.add(SIGINT);
68+ set.add(SIGTERM);
69+#ifndef _WIN32
70+ set.add(SIGUSR1);
71+ set.add(SIGHUP);
72+#else
73+ // This must be a blocking call as Windows calls it in a new thread and terminates
74+ // the process when the handler returns (or after 5 seconds, whichever is earlier).
75+ // On Windows it is called in a new thread.
76+ signal(SIGBREAK, dispatchSignalHandler);
77+#endif
78+
79+ asyncWait();
80+}
81+
82+void Signals::asyncWait()
83+{
84+ set.async_wait([this] (ErrorCode err, int signal) {
85+ if (err) {
86+ std::cerr << "Signal handling error: " << err.message() << std::endl;
87+ return;
88+ }
89+ dispatchSignalHandler(signal);
90+ asyncWait();
91+ });
92+}
93+
94+// On Windows this function does not need to be signal-safe,
95+// as it is called in a new thread.
96+// https://github.com/otland/forgottenserver/pull/2473
97+void Signals::dispatchSignalHandler(int signal)
98+{
99+ switch(signal) {
100+ case SIGINT: //Shuts the server down
101+ g_dispatcher.addTask(createTask(sigintHandler));
102+ break;
103+ case SIGTERM: //Shuts the server down
104+ g_dispatcher.addTask(createTask(sigtermHandler));
105+ break;
106+#ifndef _WIN32
107+ case SIGHUP: //Reload config/data
108+ g_dispatcher.addTask(createTask(sighupHandler));
109+ break;
110+ case SIGUSR1: //Saves game state
111+ g_dispatcher.addTask(createTask(sigusr1Handler));
112+ break;
113+#else
114+ case SIGBREAK: //Shuts the server down
115+ g_dispatcher.addTask(createTask(sigbreakHandler));
116+ // hold the thread until other threads end
117+ g_scheduler.join();
118+ g_databaseTasks.join();
119+ g_dispatcher.join();
120+ break;
121+#endif
122+ default:
123+ break;
124+ }
125+}
126+
127+void Signals::sigbreakHandler()
128+{
129+ //Dispatcher thread
130+ std::cout << "SIGBREAK received, shutting game server down..." << std::endl;
131+ g_game.setGameState(GAME_STATE_SHUTDOWN);
132+}
133+
134+void Signals::sigtermHandler()
135+{
136+ //Dispatcher thread
137+ std::cout << "SIGTERM received, shutting game server down..." << std::endl;
138+ g_game.setGameState(GAME_STATE_SHUTDOWN);
139+}
140+
141+void Signals::sigusr1Handler()
142+{
143+ //Dispatcher thread
144+ std::cout << "SIGUSR1 received, saving the game state..." << std::endl;
145+ g_game.saveGameState();
146+}
147+
148+void Signals::sighupHandler()
149+{
150+ //Dispatcher thread
151+ std::cout << "SIGHUP received, reloading config files..." << std::endl;
152+
153+ g_actions->reload();
154+ std::cout << "Reloaded actions." << std::endl;
155+
156+ g_config.reload();
157+ std::cout << "Reloaded config." << std::endl;
158+
159+ g_creatureEvents->reload();
160+ std::cout << "Reloaded creature scripts." << std::endl;
161+
162+ g_moveEvents->reload();
163+ std::cout << "Reloaded movements." << std::endl;
164+
165+ Npcs::reload();
166+ std::cout << "Reloaded npcs." << std::endl;
167+
168+ g_game.raids.reload();
169+ g_game.raids.startup();
170+ std::cout << "Reloaded raids." << std::endl;
171+
172+ g_spells->reload();
173+ std::cout << "Reloaded monsters." << std::endl;
174+
175+ g_monsters.reload();
176+ std::cout << "Reloaded spells." << std::endl;
177+
178+ g_talkActions->reload();
179+ std::cout << "Reloaded talk actions." << std::endl;
180+
181+ Item::items.reload();
182+ std::cout << "Reloaded items." << std::endl;
183+
184+ g_weapons->reload();
185+ g_weapons->loadDefaults();
186+ std::cout << "Reloaded weapons." << std::endl;
187+
188+ g_game.quests.reload();
189+ std::cout << "Reloaded quests." << std::endl;
190+
191+ g_game.mounts.reload();
192+ std::cout << "Reloaded mounts." << std::endl;
193+
194+ g_game.auras.reload();
195+ std::cout << "Reloaded auras." << std::endl;
196+
197+ g_game.wings.reload();
198+ std::cout << "Reloaded wings." << std::endl;
199+
200+ g_globalEvents->reload();
201+ std::cout << "Reloaded globalevents." << std::endl;
202+
203+ g_events->load();
204+ std::cout << "Reloaded events." << std::endl;
205+
206+ g_chat->load();
207+ std::cout << "Reloaded chatchannels." << std::endl;
208+
209+ g_luaEnvironment.loadFile("data/global.lua");
210+ std::cout << "Reloaded global.lua." << std::endl;
211+
212+ lua_gc(g_luaEnvironment.getLuaState(), LUA_GCCOLLECT, 0);
213+}
214+
215+void Signals::sigintHandler()
216+{
217+ //Dispatcher thread
218+ std::cout << "SIGINT received, shutting game server down..." << std::endl;
219+ g_game.setGameState(GAME_STATE_SHUTDOWN);
220+}
A src/signals.h +42-0 View file
@@ -0,0 +1,42 @@
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+#ifndef FS_SIGNALHANDLINGTHREAD_H_01C6BF08B0EFE9E200175D108CF0B35F
21+#define FS_SIGNALHANDLINGTHREAD_H_01C6BF08B0EFE9E200175D108CF0B35F
22+
23+#include <boost/asio.hpp>
24+
25+class Signals
26+{
27+ boost::asio::signal_set set;
28+ public:
29+ explicit Signals(boost::asio::io_service& service);
30+
31+ private:
32+ void asyncWait();
33+ static void dispatchSignalHandler(int signal);
34+
35+ static void sigbreakHandler();
36+ static void sigintHandler();
37+ static void sighupHandler();
38+ static void sigtermHandler();
39+ static void sigusr1Handler();
40+};
41+
42+#endif
A src/spawn.cpp +332-0 View file
@@ -0,0 +1,332 @@
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 "spawn.h"
23+#include "game.h"
24+#include "monster.h"
25+#include "configmanager.h"
26+#include "scheduler.h"
27+
28+#include "pugicast.h"
29+#include "events.h"
30+
31+extern ConfigManager g_config;
32+extern Monsters g_monsters;
33+extern Game g_game;
34+extern Events* g_events;
35+
36+static constexpr int32_t MINSPAWN_INTERVAL = 1000;
37+
38+bool Spawns::loadFromXml(const std::string& filename)
39+{
40+ if (loaded) {
41+ return true;
42+ }
43+
44+ pugi::xml_document doc;
45+ pugi::xml_parse_result result = doc.load_file(filename.c_str());
46+ if (!result) {
47+ printXMLError("Error - Spawns::loadFromXml", filename, result);
48+ return false;
49+ }
50+
51+ this->filename = filename;
52+ loaded = true;
53+
54+ for (auto spawnNode : doc.child("spawns").children()) {
55+ Position centerPos(
56+ pugi::cast<uint16_t>(spawnNode.attribute("centerx").value()),
57+ pugi::cast<uint16_t>(spawnNode.attribute("centery").value()),
58+ pugi::cast<uint16_t>(spawnNode.attribute("centerz").value())
59+ );
60+
61+ int32_t radius;
62+ pugi::xml_attribute radiusAttribute = spawnNode.attribute("radius");
63+ if (radiusAttribute) {
64+ radius = pugi::cast<int32_t>(radiusAttribute.value());
65+ } else {
66+ radius = -1;
67+ }
68+
69+ spawnList.emplace_front(centerPos, radius);
70+ Spawn& spawn = spawnList.front();
71+
72+ for (auto childNode : spawnNode.children()) {
73+ if (strcasecmp(childNode.name(), "monster") == 0) {
74+ pugi::xml_attribute nameAttribute = childNode.attribute("name");
75+ if (!nameAttribute) {
76+ continue;
77+ }
78+
79+ Direction dir;
80+
81+ pugi::xml_attribute directionAttribute = childNode.attribute("direction");
82+ if (directionAttribute) {
83+ dir = static_cast<Direction>(pugi::cast<uint16_t>(directionAttribute.value()));
84+ } else {
85+ dir = DIRECTION_NORTH;
86+ }
87+
88+ Position pos(
89+ centerPos.x + pugi::cast<uint16_t>(childNode.attribute("x").value()),
90+ centerPos.y + pugi::cast<uint16_t>(childNode.attribute("y").value()),
91+ centerPos.z
92+ );
93+ uint32_t interval = pugi::cast<uint32_t>(childNode.attribute("spawntime").value()) * 1000;
94+ if (interval > MINSPAWN_INTERVAL) {
95+ spawn.addMonster(nameAttribute.as_string(), pos, dir, interval);
96+ } else {
97+ std::cout << "[Warning - Spawns::loadFromXml] " << nameAttribute.as_string() << ' ' << pos << " spawntime can not be less than " << MINSPAWN_INTERVAL / 1000 << " seconds." << std::endl;
98+ }
99+ } else if (strcasecmp(childNode.name(), "npc") == 0) {
100+ pugi::xml_attribute nameAttribute = childNode.attribute("name");
101+ if (!nameAttribute) {
102+ continue;
103+ }
104+
105+ Npc* npc = Npc::createNpc(nameAttribute.as_string());
106+ if (!npc) {
107+ continue;
108+ }
109+
110+ pugi::xml_attribute directionAttribute = childNode.attribute("direction");
111+ if (directionAttribute) {
112+ npc->setDirection(static_cast<Direction>(pugi::cast<uint16_t>(directionAttribute.value())));
113+ }
114+
115+ npc->setMasterPos(Position(
116+ centerPos.x + pugi::cast<uint16_t>(childNode.attribute("x").value()),
117+ centerPos.y + pugi::cast<uint16_t>(childNode.attribute("y").value()),
118+ centerPos.z
119+ ), radius);
120+ npcList.push_front(npc);
121+ }
122+ }
123+ }
124+ return true;
125+}
126+
127+void Spawns::startup()
128+{
129+ if (!loaded || isStarted()) {
130+ return;
131+ }
132+
133+ for (Npc* npc : npcList) {
134+ g_game.placeCreature(npc, npc->getMasterPos(), false, true);
135+ }
136+ npcList.clear();
137+
138+ for (Spawn& spawn : spawnList) {
139+ spawn.startup();
140+ }
141+
142+ started = true;
143+}
144+
145+void Spawns::clear()
146+{
147+ for (Spawn& spawn : spawnList) {
148+ spawn.stopEvent();
149+ }
150+ spawnList.clear();
151+
152+ loaded = false;
153+ started = false;
154+ filename.clear();
155+}
156+
157+bool Spawns::isInZone(const Position& centerPos, int32_t radius, const Position& pos)
158+{
159+ if (radius == -1) {
160+ return true;
161+ }
162+
163+ return ((pos.getX() >= centerPos.getX() - radius) && (pos.getX() <= centerPos.getX() + radius) &&
164+ (pos.getY() >= centerPos.getY() - radius) && (pos.getY() <= centerPos.getY() + radius));
165+}
166+
167+void Spawn::startSpawnCheck()
168+{
169+ if (checkSpawnEvent == 0) {
170+ checkSpawnEvent = g_scheduler.addEvent(createSchedulerTask(getInterval(), std::bind(&Spawn::checkSpawn, this)));
171+ }
172+}
173+
174+Spawn::~Spawn()
175+{
176+ for (const auto& it : spawnedMap) {
177+ Monster* monster = it.second;
178+ monster->setSpawn(nullptr);
179+ monster->decrementReferenceCounter();
180+ }
181+}
182+
183+bool Spawn::findPlayer(const Position& pos)
184+{
185+ SpectatorVec spectators;
186+ g_game.map.getSpectators(spectators, pos, false, true);
187+ for (Creature* spectator : spectators) {
188+ if (!spectator->getPlayer()->hasFlag(PlayerFlag_IgnoredByMonsters)) {
189+ return true;
190+ }
191+ }
192+ return false;
193+}
194+
195+bool Spawn::isInSpawnZone(const Position& pos)
196+{
197+ return Spawns::isInZone(centerPos, radius, pos);
198+}
199+
200+bool Spawn::spawnMonster(uint32_t spawnId, MonsterType* mType, const Position& pos, Direction dir, bool startup /*= false*/)
201+{
202+ std::unique_ptr<Monster> monster_ptr(new Monster(mType));
203+ if (!g_events->eventMonsterOnSpawn(monster_ptr.get(), pos, startup, false)) {
204+ return false;
205+ }
206+
207+ if (startup) {
208+ //No need to send out events to the surrounding since there is no one out there to listen!
209+ if (!g_game.internalPlaceCreature(monster_ptr.get(), pos, true)) {
210+ return false;
211+ }
212+ } else {
213+ if (!g_game.placeCreature(monster_ptr.get(), pos, false, true)) {
214+ return false;
215+ }
216+ }
217+
218+ Monster* monster = monster_ptr.release();
219+ monster->setDirection(dir);
220+ monster->setSpawn(this);
221+ monster->setMasterPos(pos);
222+ monster->incrementReferenceCounter();
223+
224+ spawnedMap.insert(spawned_pair(spawnId, monster));
225+ spawnMap[spawnId].lastSpawn = OTSYS_TIME();
226+ return true;
227+}
228+
229+void Spawn::startup()
230+{
231+ for (const auto& it : spawnMap) {
232+ uint32_t spawnId = it.first;
233+ const spawnBlock_t& sb = it.second;
234+ spawnMonster(spawnId, sb.mType, sb.pos, sb.direction, true);
235+ }
236+}
237+
238+void Spawn::checkSpawn()
239+{
240+ checkSpawnEvent = 0;
241+
242+ cleanup();
243+
244+ uint32_t spawnCount = 0;
245+
246+ for (auto& it : spawnMap) {
247+ uint32_t spawnId = it.first;
248+ if (spawnedMap.find(spawnId) != spawnedMap.end()) {
249+ continue;
250+ }
251+
252+ spawnBlock_t& sb = it.second;
253+ if (OTSYS_TIME() >= sb.lastSpawn + sb.interval) {
254+ if (findPlayer(sb.pos)) {
255+ sb.lastSpawn = OTSYS_TIME();
256+ continue;
257+ }
258+
259+ spawnMonster(spawnId, sb.mType, sb.pos, sb.direction);
260+ if (++spawnCount >= static_cast<uint32_t>(g_config.getNumber(ConfigManager::RATE_SPAWN))) {
261+ break;
262+ }
263+ }
264+ }
265+
266+ if (spawnedMap.size() < spawnMap.size()) {
267+ checkSpawnEvent = g_scheduler.addEvent(createSchedulerTask(getInterval(), std::bind(&Spawn::checkSpawn, this)));
268+ }
269+}
270+
271+void Spawn::cleanup()
272+{
273+ auto it = spawnedMap.begin();
274+ while (it != spawnedMap.end()) {
275+ uint32_t spawnId = it->first;
276+ Monster* monster = it->second;
277+ if (monster->isRemoved()) {
278+ if (spawnId != 0) {
279+ spawnMap[spawnId].lastSpawn = OTSYS_TIME();
280+ }
281+
282+ monster->decrementReferenceCounter();
283+ it = spawnedMap.erase(it);
284+ } else if (!isInSpawnZone(monster->getPosition()) && spawnId != 0) {
285+ spawnedMap.insert(spawned_pair(0, monster));
286+ it = spawnedMap.erase(it);
287+ } else {
288+ ++it;
289+ }
290+ }
291+}
292+
293+bool Spawn::addMonster(const std::string& name, const Position& pos, Direction dir, uint32_t interval)
294+{
295+ MonsterType* mType = g_monsters.getMonsterType(name);
296+ if (!mType) {
297+ std::cout << "[Spawn::addMonster] Can not find " << name << std::endl;
298+ return false;
299+ }
300+
301+ this->interval = std::min(this->interval, interval);
302+
303+ spawnBlock_t sb;
304+ sb.mType = mType;
305+ sb.pos = pos;
306+ sb.direction = dir;
307+ sb.interval = interval;
308+ sb.lastSpawn = 0;
309+
310+ uint32_t spawnId = spawnMap.size() + 1;
311+ spawnMap[spawnId] = sb;
312+ return true;
313+}
314+
315+void Spawn::removeMonster(Monster* monster)
316+{
317+ for (auto it = spawnedMap.begin(), end = spawnedMap.end(); it != end; ++it) {
318+ if (it->second == monster) {
319+ monster->decrementReferenceCounter();
320+ spawnedMap.erase(it);
321+ break;
322+ }
323+ }
324+}
325+
326+void Spawn::stopEvent()
327+{
328+ if (checkSpawnEvent != 0) {
329+ g_scheduler.stopEvent(checkSpawnEvent);
330+ checkSpawnEvent = 0;
331+ }
332+}
A src/spawn.h +103-0 View file
@@ -0,0 +1,103 @@
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+#ifndef FS_SPAWN_H_1A86089E080846A9AE53ED12E7AE863B
21+#define FS_SPAWN_H_1A86089E080846A9AE53ED12E7AE863B
22+
23+#include "tile.h"
24+#include "position.h"
25+
26+class Monster;
27+class MonsterType;
28+class Npc;
29+
30+struct spawnBlock_t {
31+ Position pos;
32+ MonsterType* mType;
33+ int64_t lastSpawn;
34+ uint32_t interval;
35+ Direction direction;
36+};
37+
38+class Spawn
39+{
40+ public:
41+ Spawn(Position pos, int32_t radius) : centerPos(std::move(pos)), radius(radius) {}
42+ ~Spawn();
43+
44+ // non-copyable
45+ Spawn(const Spawn&) = delete;
46+ Spawn& operator=(const Spawn&) = delete;
47+
48+ bool addMonster(const std::string& name, const Position& pos, Direction dir, uint32_t interval);
49+ void removeMonster(Monster* monster);
50+
51+ uint32_t getInterval() const {
52+ return interval;
53+ }
54+ void startup();
55+
56+ void startSpawnCheck();
57+ void stopEvent();
58+
59+ bool isInSpawnZone(const Position& pos);
60+ void cleanup();
61+
62+ private:
63+ //map of the spawned creatures
64+ using SpawnedMap = std::multimap<uint32_t, Monster*>;
65+ using spawned_pair = SpawnedMap::value_type;
66+ SpawnedMap spawnedMap;
67+
68+ //map of creatures in the spawn
69+ std::map<uint32_t, spawnBlock_t> spawnMap;
70+
71+ Position centerPos;
72+ int32_t radius;
73+
74+ uint32_t interval = 60000;
75+ uint32_t checkSpawnEvent = 0;
76+
77+ static bool findPlayer(const Position& pos);
78+ bool spawnMonster(uint32_t spawnId, MonsterType* mType, const Position& pos, Direction dir, bool startup = false);
79+ void checkSpawn();
80+};
81+
82+class Spawns
83+{
84+ public:
85+ static bool isInZone(const Position& centerPos, int32_t radius, const Position& pos);
86+
87+ bool loadFromXml(const std::string& filename);
88+ void startup();
89+ void clear();
90+
91+ bool isStarted() const {
92+ return started;
93+ }
94+
95+ private:
96+ std::forward_list<Npc*> npcList;
97+ std::forward_list<Spawn> spawnList;
98+ std::string filename;
99+ bool loaded = false;
100+ bool started = false;
101+};
102+
103+#endif
A src/spectators.h +83-0 View file
@@ -0,0 +1,83 @@
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+#ifndef FS_SPECTATORS_H_D78A7CCB7080406E8CAA6B1D31D3DA71
21+#define FS_SPECTATORS_H_D78A7CCB7080406E8CAA6B1D31D3DA71
22+
23+#include <vector>
24+
25+class Creature;
26+
27+class SpectatorVec
28+{
29+ using Vec = std::vector<Creature*>;
30+ using Iterator = Vec::iterator;
31+ using ConstIterator = Vec::const_iterator;
32+public:
33+ SpectatorVec() {
34+ vec.reserve(32);
35+ }
36+
37+ void addSpectators(const SpectatorVec& spectators) {
38+ const size_t size = vec.size();
39+ for (Creature* spectator : spectators.vec) {
40+ bool duplicate = false;
41+ for (size_t i = 0; i < size; ++i) {
42+ if (vec[i] == spectator) {
43+ duplicate = true;
44+ break;
45+ }
46+ }
47+
48+ if (!duplicate) {
49+ vec.emplace_back(spectator);
50+ }
51+ }
52+ }
53+
54+ void erase(Creature* spectator) {
55+ for (size_t i = 0, len = vec.size(); i < len; i++) {
56+ if (vec[i] == spectator) {
57+ Creature* tmp = vec[len - 1];
58+ vec[len - 1] = vec[i];
59+ vec[i] = tmp;
60+ vec.pop_back();
61+ break;
62+ }
63+ }
64+ }
65+
66+ inline size_t size() const { return vec.size(); }
67+ inline bool empty() const { return vec.empty(); }
68+ inline Iterator begin() { return vec.begin(); }
69+ inline ConstIterator begin() const { return vec.begin(); }
70+ inline ConstIterator cbegin() const { return vec.cbegin(); }
71+ inline Iterator end() { return vec.end(); }
72+ inline ConstIterator end() const { return vec.end(); }
73+ inline ConstIterator cend() const { return vec.cend(); }
74+ inline void emplace_back(Creature* c) { return vec.emplace_back(c); }
75+
76+ template<class InputIterator>
77+ inline void insert(Iterator pos, InputIterator first, InputIterator last) { vec.insert(pos, first, last); }
78+
79+private:
80+ Vec vec;
81+};
82+
83+#endif
Top