5 #include "Server.hpp"
\r
6 #include "version.hpp"
\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
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
25 void Server::Start(CallbackFuncPtr callback)
\r
27 callback_ = std::make_shared<CallbackFunc>(
\r
28 [&](network::Command c){
\r
31 if (c.header() == network::header::FatalConnectionError) {
\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
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
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
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
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
71 boost::asio::io_service::work work(io_service_);
\r
78 Logger::Info("stop server");
\r
80 void Server::Stop(int innterrupt_type)
\r
83 Logger::Info(_T("stop server innterrupt_type=%d"),innterrupt_type);
\r
86 int Server::GetUserCount() const
\r
88 auto count = std::count_if(sessions_.begin(), sessions_.end(),
\r
89 [](const SessionWeakPtr& s){ return !s.expired() && s.lock()->online(); });
\r
94 std::string Server::GetStatusJSON() const
\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
101 % config_.capacity()
\r
108 std::string Server::GetFullStatus() const
\r
110 using namespace boost::property_tree;
\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
119 ptree player_array;
\r
120 auto id_list = account_.GetIDList();
\r
121 BOOST_FOREACH(unsigned int id, id_list) {
\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
127 xml_ptree.put_child("players", player_array);
\r
132 BOOST_FOREACH(const std::string& msg, recent_chat_log_) {
\r
133 log_array.push_back(std::make_pair("", msg));
\r
135 xml_ptree.put_child("recent_chat_log", log_array);
\r
138 xml_ptree.put_child("channels", channel_.pt());
\r
140 std::stringstream stream;
\r
141 write_xml(stream, xml_ptree);
\r
142 return stream.str();
\r
145 const Config& Server::config() const
\r
150 Account& Server::account()
\r
155 void Server::AddChatLog(const std::string& msg)
\r
157 recent_chat_log_.push_back(msg);
\r
160 bool Server::Empty() const
\r
162 return GetUserCount() == 0;
\r
165 bool Server::IsBlockedAddress(const boost::asio::ip::address& address)
\r
167 BOOST_FOREACH(const auto& pattern, config_.blocking_address_patterns()) {
\r
168 if (network::Utils::MatchWithWildcard(pattern, address.to_string())) {
\r
175 void Server::ReceiveSession(const SessionPtr& session, const boost::system::error_code& error)
\r
180 const auto address = session->tcp_socket().remote_endpoint().address();
\r
183 if(IsBlockedAddress(address)) {
\r
184 Logger::Info("Blocked IP Address: %s", address);
\r
188 session->set_on_receive(callback_);
\r
190 sessions_.push_back(SessionWeakPtr(session));
\r
193 session->Send(ClientRequestedClientInfo());
\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
203 void Server::RefreshSession()
\r
205 // 使用済のセッションのポインタを破棄
\r
206 auto it = std::remove_if(sessions_.begin(), sessions_.end(),
\r
207 [](const SessionWeakPtr& ptr){
\r
208 return ptr.expired();
\r
210 sessions_.erase(it, sessions_.end());
\r
211 Logger::Info("Active connection: %d", GetUserCount());
\r
214 void Server::SendAll(const Command& command, int channel, bool limited)
\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
229 void Server::SendOthers(const Command& command, uint32_t self_id, int channel, bool limited)
\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
244 void Server::SendTo(const Command& command, uint32_t user_id)
\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
251 if (it != sessions_.end()) {
\r
252 it->lock()->Send(command);
\r
256 void Server::SendUDPTestPacket(const std::string& ip_address, uint16_t port)
\r
258 using boost::asio::ip::udp;
\r
260 std::stringstream port_str;
\r
261 port_str << (int)port;
\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
267 static char request[] = "MMO UDP Test Packet";
\r
268 for (int i = 0; i < UDP_TEST_PACKET_TIME; i++) {
\r
270 io_service_.post(boost::bind(&Server::DoWriteUDP, this, request, *iterator));
\r
274 void Server::SendPublicPing()
\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
282 void Server::SendUDP(const std::string& message, const boost::asio::ip::udp::endpoint endpoint)
\r
284 io_service_.post(boost::bind(&Server::DoWriteUDP, this, message, endpoint));
\r
287 void Server::ReceiveUDP(const boost::system::error_code& error, size_t bytes_recvd)
\r
289 if (bytes_recvd > 0) {
\r
290 std::string buffer(receive_buf_udp_, bytes_recvd);
\r
291 FetchUDP(buffer, sender_endpoint_);
\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
300 Logger::Error("%s", error.message());
\r
304 void Server::DoWriteUDP(const std::string& msg, const udp::endpoint& endpoint)
\r
306 boost::shared_ptr<std::string> s =
\r
307 boost::make_shared<std::string>(msg.data(), msg.size());
\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
315 void Server::WriteUDP(const boost::system::error_code& error, boost::shared_ptr<std::string> holder)
\r
318 // if (!send_queue_.empty()) {
\r
319 // send_queue_.pop();
\r
320 // if (!send_queue_.empty())
\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
334 void Server::FetchUDP(const std::string& buffer, const boost::asio::ip::udp::endpoint endpoint)
\r
338 SessionWeakPtr session;
\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
345 const auto session_endpoint = session_ptr->tcp_socket().remote_endpoint();
\r
346 const auto session_port = session_ptr->udp_port();
\r
348 return (session_endpoint.address() == endpoint.address() &&
\r
349 session_port == endpoint.port());
\r
356 if (it != sessions_.end()) {
\r
358 Logger::Debug("Receive UDP Command: %d", session.lock()->id());
\r
360 Logger::Debug("Receive anonymous UDP Command");
\r
363 if (buffer.size() > network::Utils::Deserialize(buffer, &header)) {
\r
364 body = buffer.substr(sizeof(header));
\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
375 if (header == network::header::ServerRequstedStatus) {
\r
376 SendUDP(GetStatusJSON(), endpoint);
\r
379 (*callback_)(Command(static_cast<network::header::CommandHeader>(header), body, session));
\r
385 void Server::ServerSession::Start()
\r
390 socket_tcp_.set_option(boost::asio::ip::tcp::no_delay(true));
\r
393 boost::asio::socket_base::receive_buffer_size option(1048576);
\r
394 socket_tcp_.set_option(option);
\r
397 global_ip_ = socket_tcp_.remote_endpoint().address().to_string();
\r
399 boost::asio::async_read_until(socket_tcp_,
\r
400 receive_buf_, NETWORK_UTILS_DELIMITOR,
\r
402 &ServerSession::ReceiveTCP, shared_from_this(),
\r
403 boost::asio::placeholders::error));
\r