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 <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
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
27 void Server::Start(CallbackFuncPtr callback)
\r
29 callback_ = std::make_shared<CallbackFunc>(
\r
30 [&](network::Command c){
\r
33 if (c.header() == network::header::FatalConnectionError ||
\r
34 c.header() == network::header::UserFatalConnectionError) {
\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
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
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
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
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
74 boost::asio::io_service::work work(io_service_);
\r
81 Logger::Info("stop server");
\r
83 void Server::Stop(int innterrupt_type)
\r
86 Logger::Info(_T("stop server innterrupt_type=%d"),innterrupt_type);
\r
89 int Server::GetUserCount() const
\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
99 std::string Server::GetStatusJSON() const
\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
106 % config_.capacity()
\r
107 % channel_.GetDefaultStage()
\r
113 std::string Server::GetFullStatus() const
\r
115 using namespace boost::property_tree;
\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
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
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
135 xml_ptree.put_child("players", player_array);
\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
143 // xml_ptree.put_child("recent_chat_log", log_array);
\r
146 xml_ptree.put_child("channels", channel_.pt());
\r
147 std::stringstream stream;
\r
148 boost::archive::text_oarchive oa(stream);
\r
151 return stream.str();
\r
154 const Config& Server::config() const
\r
159 Account& Server::account()
\r
164 void Server::AddChatLog(const std::string& msg)
\r
166 recent_chat_log_.push_back(msg);
\r
169 bool Server::Empty() const
\r
171 return GetUserCount() == 0;
\r
174 bool Server::IsBlockedAddress(const boost::asio::ip::address& address)
\r
176 BOOST_FOREACH(const auto& pattern, config_.blocking_address_patterns()) {
\r
177 if (network::Utils::MatchWithWildcard(pattern, address.to_string())) {
\r
184 void Server::ReceiveSession(const SessionPtr& session, const boost::system::error_code& error)
\r
189 const auto address = session->tcp_socket().remote_endpoint().address();
\r
192 if(IsBlockedAddress(address)) {
\r
193 Logger::Info("Blocked IP Address: %s", address);
\r
197 session->set_on_receive(callback_);
\r
199 sessions_.push_back(SessionWeakPtr(session));
\r
202 session->Send(ClientRequestedClientInfo());
\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
212 void Server::RefreshSession()
\r
214 // 使用済のセッションのポインタを破棄
\r
215 auto it = std::remove_if(sessions_.begin(), sessions_.end(),
\r
216 [](const SessionWeakPtr& ptr){
\r
217 return ptr.expired();
\r
219 sessions_.erase(it, sessions_.end());
\r
220 Logger::Info("Active connection: %d", GetUserCount());
\r
223 void Server::SendAll(const Command& command, int channel, bool limited)
\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
238 void Server::SendOthers(const Command& command, uint32_t self_id, int channel, bool limited)
\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
253 void Server::SendTo(const Command& command, uint32_t user_id)
\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
260 if (it != sessions_.end()) {
\r
261 it->lock()->Send(command);
\r
265 void Server::SendUDPTestPacket(const std::string& ip_address, uint16_t port)
\r
267 using boost::asio::ip::udp;
\r
269 std::stringstream port_str;
\r
270 port_str << (int)port;
\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
276 static char request[] = "MMO UDP Test Packet";
\r
277 for (int i = 0; i < UDP_TEST_PACKET_TIME; i++) {
\r
279 io_service_.post(boost::bind(&Server::DoWriteUDP, this, request, *iterator));
\r
283 void Server::SendPublicPing()
\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
291 void Server::SendUDP(const std::string& message, const boost::asio::ip::udp::endpoint endpoint)
\r
293 io_service_.post(boost::bind(&Server::DoWriteUDP, this, message, endpoint));
\r
296 void Server::ReceiveUDP(const boost::system::error_code& error, size_t bytes_recvd)
\r
298 if (bytes_recvd > 0) {
\r
299 std::string buffer(receive_buf_udp_, bytes_recvd);
\r
300 FetchUDP(buffer, sender_endpoint_);
\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
309 Logger::Error("%s", error.message());
\r
313 void Server::DoWriteUDP(const std::string& msg, const udp::endpoint& endpoint)
\r
315 boost::shared_ptr<std::string> s =
\r
316 boost::make_shared<std::string>(msg.data(), msg.size());
\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
324 void Server::WriteUDP(const boost::system::error_code& error, boost::shared_ptr<std::string> holder)
\r
327 // if (!send_queue_.empty()) {
\r
328 // send_queue_.pop();
\r
329 // if (!send_queue_.empty())
\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
343 void Server::FetchUDP(const std::string& buffer, const boost::asio::ip::udp::endpoint endpoint)
\r
347 SessionWeakPtr session;
\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
354 const auto session_endpoint = session_ptr->tcp_socket().remote_endpoint();
\r
355 const auto session_port = session_ptr->udp_port();
\r
357 return (session_endpoint.address() == endpoint.address() &&
\r
358 session_port == endpoint.port());
\r
365 if (it != sessions_.end()) {
\r
367 Logger::Debug("Receive UDP Command: %d", session.lock()->id());
\r
369 Logger::Debug("Receive anonymous UDP Command");
\r
372 if (buffer.size() > network::Utils::Deserialize(buffer, &header)) {
\r
373 body = buffer.substr(sizeof(header));
\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
384 if (header == network::header::ServerRequstedStatus) {
\r
385 SendUDP(GetStatusJSON(), endpoint);
\r
388 (*callback_)(Command(static_cast<network::header::CommandHeader>(header), body, session));
\r
394 void Server::ServerSession::Start()
\r
399 socket_tcp_.set_option(boost::asio::ip::tcp::no_delay(true));
\r
402 boost::asio::socket_base::receive_buffer_size option(1048576);
\r
403 socket_tcp_.set_option(option);
\r
406 global_ip_ = socket_tcp_.remote_endpoint().address().to_string();
\r
408 boost::asio::async_read_until(socket_tcp_,
\r
409 receive_buf_, NETWORK_UTILS_DELIMITOR,
\r
411 &ServerSession::ReceiveTCP, shared_from_this(),
\r
412 boost::asio::placeholders::error));
\r