OSDN Git Service

サーバーのチャンネル設定用default.config.jsonを追加
[mmo/main.git] / server / Server.cpp
1 //\r
2 // Server.cpp\r
3 //\r
4 \r
5 #include "Server.hpp"\r
6 #include "version.hpp"\r
7 #include <algorithm>\r
8 #include <boost/make_shared.hpp>\r
9 #include <boost/foreach.hpp>\r
10 #include <boost/archive/text_oarchive.hpp>\r
11 #include <boost/property_tree/ptree_serialization.hpp>\r
12 #include "../common/Logger.hpp"\r
13 #include "../common/network/Command.hpp"\r
14 #include "../common/network/Utils.hpp"\r
15 \r
16 namespace network {\r
17 \r
18     Server::Server() :\r
19             endpoint_(tcp::v4(), config_.port()),\r
20             acceptor_(io_service_, endpoint_),\r
21             socket_udp_(io_service_, udp::endpoint(udp::v4(), config_.port())),\r
22             udp_packet_count_(0),\r
23                         recent_chat_log_(10)\r
24     {\r
25     }\r
26 \r
27     void Server::Start(CallbackFuncPtr callback)\r
28     {\r
29         callback_ = std::make_shared<CallbackFunc>(\r
30                 [&](network::Command c){\r
31 \r
32             // ログアウト\r
33             if (c.header() == network::header::FatalConnectionError || \r
34                                 c.header() == network::header::UserFatalConnectionError) {\r
35                 if (callback) {\r
36                                         (*callback)(c);\r
37                                 }\r
38             } else if (auto session = c.session().lock()) {\r
39                                 auto read_average = session->GetReadByteAverage();\r
40                                 if (read_average > config_.receive_limit_2()) {\r
41                                         Logger::Info(_T("Banished a session: %d %dbyte/s"), session->id(), read_average);\r
42                                         session->Close();\r
43                                 } else if(read_average > config_.receive_limit_1()) {\r
44                                         Logger::Info(_T("Receive limit exceeded: %d: %d byte/s"), session->id(), read_average);\r
45                                 } else {\r
46                                         if (callback) {\r
47                                                 (*callback)(c);\r
48                                         }\r
49                 }\r
50             }\r
51 \r
52         });\r
53 \r
54                 BOOST_FOREACH(const auto& host, config().lobby_servers()) {\r
55                         udp::resolver resolver(io_service_);\r
56                         udp::resolver::query query(udp::v4(), host.c_str(), "39380");\r
57                         lobby_hosts_.push_back(resolver.resolve(query));\r
58                 }\r
59 \r
60         {\r
61         auto new_session = boost::make_shared<ServerSession>(io_service_);\r
62         acceptor_.async_accept(new_session->tcp_socket(),\r
63                               boost::bind(&Server::ReceiveSession, this, new_session, boost::asio::placeholders::error));\r
64         }\r
65 \r
66         {\r
67             socket_udp_.async_receive_from(\r
68                 boost::asio::buffer(receive_buf_udp_, UDP_MAX_RECEIVE_LENGTH), sender_endpoint_,\r
69                 boost::bind(&Server::ReceiveUDP, this,\r
70                   boost::asio::placeholders::error,\r
71                   boost::asio::placeholders::bytes_transferred));\r
72         }\r
73 \r
74         boost::asio::io_service::work work(io_service_);\r
75         io_service_.run();\r
76     }\r
77 \r
78     void Server::Stop()\r
79     {\r
80         io_service_.stop();\r
81                 Logger::Info("stop server");\r
82     }\r
83     void Server::Stop(int innterrupt_type)\r
84     {\r
85         io_service_.stop();\r
86         Logger::Info(_T("stop server innterrupt_type=%d"),innterrupt_type);\r
87     }\r
88 \r
89         int Server::GetUserCount() const\r
90         {\r
91                 auto count = std::count_if(sessions_.begin(), sessions_.end(),\r
92                         [](const SessionWeakPtr& s){ \r
93                                 return !s.expired() && s.lock()->online() && s.lock()->id() > 0; \r
94                         });\r
95 \r
96                 return count;\r
97         }\r
98 \r
99         std::string Server::GetStatusJSON() const\r
100         {\r
101                 auto msg = (\r
102                                         boost::format("{\"nam\":\"%s\",\"ver\":\"%d.%d.%d\",\"cnt\":%d,\"cap\":%d,\"stg\":\"%s\"}")\r
103                                                 % config_.server_name()\r
104                                                 % MMO_VERSION_MAJOR % MMO_VERSION_MINOR % MMO_VERSION_REVISION\r
105                                                 % GetUserCount()\r
106                                                 % config_.capacity()\r
107                                                 % channel_.GetDefaultStage()\r
108                                         ).str();\r
109 \r
110                 return msg;\r
111         }\r
112 \r
113         std::string Server::GetFullStatus() const\r
114         {\r
115                 using namespace boost::property_tree;\r
116                 ptree xml_ptree;\r
117 \r
118                 xml_ptree.put_child("config", config_.pt());\r
119                 xml_ptree.put("version", (boost::format("%d.%d.%d") \r
120                         % MMO_VERSION_MAJOR % MMO_VERSION_MINOR % MMO_VERSION_REVISION).str());\r
121                 xml_ptree.put("protocol_version", MMO_PROTOCOL_VERSION);\r
122 \r
123                 {\r
124                         ptree player_array;\r
125                         auto id_list = account_.GetIDList();\r
126                         BOOST_FOREACH(const auto& s, sessions_) {\r
127                                 if (!s.expired() && s.lock()->online() && s.lock()->id() > 0) {\r
128                                         auto id = s.lock()->id();\r
129                                         ptree player;\r
130                                         player.put("name", account_.GetUserName(id));\r
131                                         player.put("model_name", account_.GetUserModelName(id));\r
132                                         player_array.push_back(std::make_pair("", player));\r
133                                 }\r
134                         }\r
135                         xml_ptree.put_child("players", player_array);\r
136                 }\r
137 \r
138                 //{\r
139                 //      ptree log_array;\r
140                 //      BOOST_FOREACH(const std::string& msg, recent_chat_log_) {\r
141                 //              log_array.push_back(std::make_pair("", msg));\r
142                 //      }\r
143                 //      xml_ptree.put_child("recent_chat_log", log_array);\r
144                 //}\r
145 \r
146                 xml_ptree.put_child("channels", channel_.pt());\r
147                 std::stringstream stream;\r
148                 boost::archive::text_oarchive oa(stream);\r
149                 oa << xml_ptree;\r
150 \r
151                 return stream.str();\r
152         }\r
153 \r
154         const Config& Server::config() const\r
155         {\r
156                 return config_;\r
157         }\r
158 \r
159         Account& Server::account()\r
160         {\r
161                 return account_;\r
162         }\r
163         \r
164         void Server::AddChatLog(const std::string& msg)\r
165         {\r
166                 recent_chat_log_.push_back(msg);\r
167         }\r
168 \r
169     bool Server::Empty() const\r
170     {\r
171         return GetUserCount() == 0;\r
172     }\r
173 \r
174         bool Server::IsBlockedAddress(const boost::asio::ip::address& address)\r
175         {\r
176                 BOOST_FOREACH(const auto& pattern, config_.blocking_address_patterns()) {\r
177                         if (network::Utils::MatchWithWildcard(pattern, address.to_string())) {\r
178                                 return true;\r
179                         }\r
180                 }\r
181                 return false;\r
182         }\r
183 \r
184     void Server::ReceiveSession(const SessionPtr& session, const boost::system::error_code& error)\r
185     {\r
186                 \r
187                 config_.Reload();\r
188 \r
189                 const auto address = session->tcp_socket().remote_endpoint().address();\r
190 \r
191                 // 拒否IPでないか判定\r
192                 if(IsBlockedAddress(address)) {\r
193                         Logger::Info("Blocked IP Address: %s", address);\r
194             session->Close();\r
195 \r
196                 } else {\r
197             session->set_on_receive(callback_);\r
198             session->Start();\r
199             sessions_.push_back(SessionWeakPtr(session));\r
200 \r
201             // クライアント情報を要求\r
202             session->Send(ClientRequestedClientInfo());\r
203         }\r
204 \r
205         auto new_session = boost::make_shared<ServerSession>(io_service_);\r
206         acceptor_.async_accept(new_session->tcp_socket(),\r
207                 boost::bind(&Server::ReceiveSession, this, new_session, boost::asio::placeholders::error));\r
208 \r
209                 RefreshSession();\r
210     }\r
211 \r
212         void Server::RefreshSession()\r
213         {\r
214                 // 使用済のセッションのポインタを破棄\r
215         auto it = std::remove_if(sessions_.begin(), sessions_.end(),\r
216                 [](const SessionWeakPtr& ptr){\r
217             return ptr.expired();\r
218         });\r
219         sessions_.erase(it, sessions_.end());\r
220                 Logger::Info("Active connection: %d", GetUserCount());\r
221         }\r
222 \r
223     void Server::SendAll(const Command& command, int channel, bool limited)\r
224     {\r
225         BOOST_FOREACH(SessionWeakPtr& ptr, sessions_) {\r
226             if (auto session = ptr.lock()) {\r
227                                 if (channel < 0 || (channel >= 0 && session->channel() == channel)) {\r
228                                         if (!limited || session->write_average_limit() > session->GetWriteByteAverage()) {\r
229                                                 if (session->id() > 0) {\r
230                                                         session->Send(command);\r
231                                                 }\r
232                                         }\r
233                                 }\r
234             }\r
235         }\r
236     }\r
237 \r
238     void Server::SendOthers(const Command& command, uint32_t self_id, int channel, bool limited)\r
239     {\r
240         BOOST_FOREACH(SessionWeakPtr& ptr, sessions_) {\r
241             if (auto session = ptr.lock()) {\r
242                                 if (channel < 0 || (channel >= 0 && session->channel() == channel)) {\r
243                                         if (!limited || session->write_average_limit() > session->GetWriteByteAverage()) {\r
244                                                 if (session->id() > 0 && session->id() != self_id) {\r
245                                                         session->Send(command);\r
246                                                 }\r
247                                         }\r
248                                 }\r
249             }\r
250         }\r
251     }\r
252         \r
253     void Server::SendTo(const Command& command, uint32_t user_id)\r
254         {\r
255                 auto it = std::find_if(sessions_.begin(), sessions_.end(),\r
256                         [user_id](SessionWeakPtr& ptr){\r
257                                 return ptr.lock()->id() == user_id;\r
258                         });\r
259                 \r
260                 if (it != sessions_.end()) {\r
261                         it->lock()->Send(command);\r
262                 }\r
263         }\r
264 \r
265     void Server::SendUDPTestPacket(const std::string& ip_address, uint16_t port)\r
266     {\r
267         using boost::asio::ip::udp;\r
268 \r
269         std::stringstream port_str;\r
270         port_str << (int)port;\r
271 \r
272         udp::resolver resolver(io_service_);\r
273         udp::resolver::query query(udp::v4(), ip_address.c_str(), port_str.str().c_str());\r
274         udp::resolver::iterator iterator = resolver.resolve(query);\r
275 \r
276         static char request[] = "MMO UDP Test Packet";\r
277         for (int i = 0; i < UDP_TEST_PACKET_TIME; i++) {\r
278 \r
279             io_service_.post(boost::bind(&Server::DoWriteUDP, this, request, *iterator));\r
280         }\r
281     }\r
282 \r
283         void Server::SendPublicPing()\r
284         {\r
285                 static char request[] = "P";\r
286                 BOOST_FOREACH(const auto& iterator, lobby_hosts_) {\r
287                         io_service_.post(boost::bind(&Server::DoWriteUDP, this, request, *iterator));\r
288                 }\r
289         }\r
290 \r
291     void Server::SendUDP(const std::string& message, const boost::asio::ip::udp::endpoint endpoint)\r
292     {\r
293                 io_service_.post(boost::bind(&Server::DoWriteUDP, this, message, endpoint));\r
294     }\r
295 \r
296     void Server::ReceiveUDP(const boost::system::error_code& error, size_t bytes_recvd)\r
297     {\r
298         if (bytes_recvd > 0) {\r
299             std::string buffer(receive_buf_udp_, bytes_recvd);\r
300             FetchUDP(buffer, sender_endpoint_);\r
301         }\r
302         if (!error) {\r
303           socket_udp_.async_receive_from(\r
304               boost::asio::buffer(receive_buf_udp_, UDP_MAX_RECEIVE_LENGTH), sender_endpoint_,\r
305               boost::bind(&Server::ReceiveUDP, this,\r
306                 boost::asio::placeholders::error,\r
307                 boost::asio::placeholders::bytes_transferred));\r
308         } else {\r
309             Logger::Error("%s", error.message());\r
310         }\r
311     }\r
312 \r
313     void Server::DoWriteUDP(const std::string& msg, const udp::endpoint& endpoint)\r
314     {\r
315         boost::shared_ptr<std::string> s = \r
316               boost::make_shared<std::string>(msg.data(), msg.size());\r
317 \r
318         socket_udp_.async_send_to(\r
319             boost::asio::buffer(s->data(), s->size()), endpoint,\r
320             boost::bind(&Server::WriteUDP, this,\r
321               boost::asio::placeholders::error, s));\r
322     }\r
323 \r
324     void Server::WriteUDP(const boost::system::error_code& error, boost::shared_ptr<std::string> holder)\r
325     {\r
326 //        if (!error) {\r
327 //            if (!send_queue_.empty()) {\r
328 //                  send_queue_.pop();\r
329 //                  if (!send_queue_.empty())\r
330 //                  {\r
331 //                    boost::asio::async_write(socket_tcp_,\r
332 //                        boost::asio::buffer(send_queue_.front().data(),\r
333 //                          send_queue_.front().size()),\r
334 //                        boost::bind(&Session::WriteTCP, this,\r
335 //                          boost::asio::placeholders::error));\r
336 //                  }\r
337 //            }\r
338 //        } else {\r
339 //            FatalError();\r
340 //        }\r
341     }\r
342 \r
343     void Server::FetchUDP(const std::string& buffer, const boost::asio::ip::udp::endpoint endpoint)\r
344     {\r
345         uint8_t header;\r
346         std::string body;\r
347         SessionWeakPtr session;\r
348 \r
349                 // IPアドレスとポートからセッションを特定\r
350                 auto it = std::find_if(sessions_.begin(), sessions_.end(),\r
351                         [&endpoint](const SessionWeakPtr& session) -> bool {\r
352                                 if (auto session_ptr = session.lock()) {\r
353 \r
354                                         const auto session_endpoint = session_ptr->tcp_socket().remote_endpoint();\r
355                                         const auto session_port = session_ptr->udp_port();\r
356 \r
357                                         return (session_endpoint.address() == endpoint.address() &&\r
358                                                 session_port == endpoint.port());\r
359 \r
360                                 } else {\r
361                                         return false;\r
362                                 }\r
363                         });\r
364 \r
365                 if (it != sessions_.end()) {\r
366                         session = *it;\r
367                         Logger::Debug("Receive UDP Command: %d", session.lock()->id());\r
368                 } else {\r
369                         Logger::Debug("Receive anonymous UDP Command");\r
370                 }\r
371 \r
372         if (buffer.size() > network::Utils::Deserialize(buffer, &header)) {\r
373                         body = buffer.substr(sizeof(header));\r
374                 }\r
375 \r
376         // 復号\r
377         if (session.lock() && header == header::ENCRYPT_HEADER) {\r
378             body.erase(0, sizeof(header));\r
379                         body = session.lock()->encrypter().Decrypt(body);\r
380             Utils::Deserialize(body, &header);\r
381                         body = buffer.substr(sizeof(header));\r
382         }\r
383 \r
384                 if (header == network::header::ServerRequstedStatus) {\r
385                         SendUDP(GetStatusJSON(), endpoint);\r
386                 } else {\r
387                         if (callback_) {\r
388                                 (*callback_)(Command(static_cast<network::header::CommandHeader>(header), body, session));\r
389                         }\r
390                 }\r
391 \r
392     }\r
393 \r
394     void Server::ServerSession::Start()\r
395     {\r
396         online_ = true;\r
397 \r
398         // Nagleアルゴリズムを無効化\r
399         socket_tcp_.set_option(boost::asio::ip::tcp::no_delay(true));\r
400 \r
401                 // バッファサイズを変更 1MiB\r
402                 boost::asio::socket_base::receive_buffer_size option(1048576);\r
403                 socket_tcp_.set_option(option);\r
404 \r
405         // IPアドレスを取得\r
406         global_ip_ = socket_tcp_.remote_endpoint().address().to_string();\r
407 \r
408         boost::asio::async_read_until(socket_tcp_,\r
409             receive_buf_, NETWORK_UTILS_DELIMITOR,\r
410             boost::bind(\r
411               &ServerSession::ReceiveTCP, shared_from_this(),\r
412               boost::asio::placeholders::error));\r
413     }\r
414 }\r