server.h

main 157 lines · 4.4 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#ifndef FS_SERVER_H_984DA68ABF744127850F90CC710F281B
21#define FS_SERVER_H_984DA68ABF744127850F90CC710F281B
22
23#include "connection.h"
24#include "signals.h"
25#include <memory>
26
27class Protocol;
28
29class 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
40template <typename ProtocolType>
41class 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
62class 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
95class 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
126template <typename ProtocolType>
127bool 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
157
Top