OSDN Git Service

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