OSDN Git Service

プロトコルバージョンを3に変更
[mmo/main.git] / server / main.cpp
1 //\r
2 // MMO Server\r
3 //\r
4 \r
5 #include <iostream>\r
6 #include <sstream>\r
7 #include <ctime>\r
8 #include <boost/format.hpp>\r
9 #include <boost/date_time/posix_time/posix_time.hpp>\r
10 #include <boost/property_tree/json_parser.hpp>\r
11 #include <boost/foreach.hpp>\r
12 #include "version.hpp"\r
13 #include "Server.hpp"\r
14 #include "../common/network/Encrypter.hpp"\r
15 #include "../common/network/Signature.hpp"\r
16 #include "../common/database/AccountProperty.hpp"\r
17 #include "../common/Logger.hpp"\r
18 #include "Config.hpp"\r
19 #include "version.hpp"\r
20 \r
21 #ifdef __linux__\r
22 #include "ServerSigHandler.hpp"\r
23 #include <csignal>\r
24 #endif\r
25 \r
26 #ifdef _WIN32\r
27 #include <boost/interprocess/windows_shared_memory.hpp>\r
28 #include <boost/interprocess/mapped_region.hpp>\r
29 #endif\r
30 \r
31 using namespace boost::posix_time;\r
32 \r
33 void client_sync(network::Server& server);\r
34 void public_ping(network::Server& server);\r
35 void server();\r
36 \r
37 int main(int argc, char* argv[])\r
38 {\r
39         Logger::Info(_T("%s"), unicode::ToTString(MMO_VERSION_TEXT));\r
40 \r
41 #ifndef NDEBUG\r
42  try {\r
43 #endif\r
44 \r
45          server();\r
46 \r
47 #ifndef NDEBUG\r
48   } catch (std::exception& e) {\r
49       Logger::Error(e.what());\r
50       Logger::Info("Stop Server");\r
51   }\r
52 #endif\r
53 \r
54   return 0;\r
55 \r
56 }\r
57 \r
58 void server()\r
59 {\r
60 \r
61     // 署名\r
62     network::Signature sign("server_key");\r
63 \r
64     // アカウント\r
65     network::Server server;\r
66 \r
67     auto callback = std::make_shared<std::function<void(network::Command)>>(\r
68             [&server, &sign](network::Command c){\r
69 \r
70         // ログを出力\r
71         auto msg = (boost::format("Receive: 0x%08x %dbyte") % c.header() % c.body().size()).str();\r
72         if (auto session = c.session().lock()) {\r
73             msg += " from " + session->global_ip();\r
74         }\r
75 \r
76         // if (auto session = c.session().lock()) {\r
77         //     std::cout << "Write Average: " << session->GetReadByteAverage() << "bytes" << std::endl;\r
78         // }\r
79                 auto header = c.header();\r
80         switch (c.header()) {\r
81 \r
82                 case network::header::ServerRequestedFullServerInfo:\r
83                 {\r
84                         if (auto session = c.session().lock()) {\r
85                                 session->Send(network::ClientReceiveFullServerInfo(server.GetFullStatus()));\r
86                         }\r
87                 }\r
88                 break;\r
89 \r
90                 case network::header::ServerRequestedPlainFullServerInfo:\r
91                 {\r
92                         //if (auto session = c.session().lock()) {\r
93                         //      session->Send(network::ClientReceivePlainFullServerInfo(server.GetFullStatus()));\r
94                         //}\r
95                 }\r
96                 break;\r
97 \r
98                 // ステータス要求\r
99                 case network::header::ServerRequstedStatus:\r
100                 {\r
101                         // ステータスを送り返す\r
102                         server.SendUDP(server.GetStatusJSON(), c.udp_endpoint());\r
103                 }\r
104                 break;\r
105 \r
106         // JSONメッセージ受信\r
107         case network::header::ServerReceiveJSON:\r
108         {\r
109             if (auto session = c.session().lock()) {\r
110                 uint32_t id = static_cast<unsigned int>(session->id());\r
111                                 if (id == 0) {\r
112                                         Logger::Error(_T("Invalid session id"));\r
113                                         break;\r
114                                 }\r
115                                 \r
116                                 std::stringstream message_json(network::Utils::Deserialize<std::string>(c.body()));\r
117 \r
118                                 using namespace boost::property_tree;\r
119                                 ptree message_tree;\r
120                                 json_parser::read_json(message_json, message_tree);\r
121 \r
122                                 // プライベートメッセージの処理\r
123                                 std::list<uint32_t> destination_list;\r
124                                 auto private_list_tree =  message_tree.get_child("private", ptree());\r
125                                 BOOST_FOREACH(const auto& user_id, private_list_tree) {\r
126                                         destination_list.push_back(user_id.second.get_value<uint32_t>());\r
127                                 }\r
128 \r
129                 ptime now = second_clock::universal_time();\r
130                 auto time_string = to_iso_extended_string(now);\r
131 \r
132                 std::string info_json;\r
133                 info_json += "{";\r
134                 info_json += (boost::format("\"id\":\"%d\",") % id).str();\r
135                 info_json += (boost::format("\"time\":\"%s\"") % time_string).str();\r
136                 info_json += "}";\r
137 \r
138                                 auto send_command = network::ClientReceiveJSON(info_json, message_json.str());\r
139 \r
140                                 if (destination_list.size() > 0) {\r
141                                         BOOST_FOREACH(uint32_t user_id, destination_list) {\r
142                                                 server.SendTo(send_command, user_id);\r
143                                         }\r
144                                 } else {\r
145                                         auto name = server.account().GetUserName(id);\r
146                                         auto body = message_tree.get<std::string>("body", std::string());\r
147                                         server.AddChatLog((boost::format("[%s] %s") % name % body).str());\r
148 \r
149                                         server.SendAll(send_command, session->channel());\r
150                                 }\r
151 \r
152                                 Logger::Info("Receive JSON: %s", message_json.str());\r
153             }\r
154         }\r
155             break;\r
156 \r
157 \r
158         // 位置情報受信\r
159         case network::header::ServerUpdatePlayerPosition:\r
160         {\r
161             if (auto session = c.session().lock()) {\r
162                 PlayerPosition pos;\r
163                 network::Utils::Deserialize(c.body(), &pos.x, &pos.y, &pos.z, &pos.theta, &pos.vy);\r
164                 server.account().SetUserPosition(session->id(), pos);\r
165                 server.SendOthers(network::ClientUpdatePlayerPosition(session->id(),\r
166                                         pos.x,pos.y,pos.z,pos.theta, pos.vy), session->id(), session->channel(), true);\r
167             }\r
168         }\r
169             break;\r
170 \r
171         // 公開鍵フィンガープリント受信\r
172         case network::header::ServerReceiveClientInfo:\r
173         {\r
174             if (auto session = c.session().lock()) {\r
175 \r
176                                 // 最大接続数を超えていないか判定\r
177                                 if (server.GetUserCount() >= server.config().capacity()) {\r
178                                         Logger::Info("Refused Session");\r
179                                         session->SyncSend(network::ClientReceiveServerCrowdedError());\r
180                                         session->Close();\r
181 \r
182                                 }\r
183 \r
184                                 session->ResetReadByteAverage();\r
185 \r
186                 std::string finger_print;\r
187                 uint16_t version;\r
188                 uint16_t udp_port;\r
189 \r
190                 network::Utils::Deserialize(c.body(), &finger_print, &version, &udp_port);\r
191 \r
192                 // クライアントのプロトコルバージョンをチェック\r
193                 if (version != MMO_PROTOCOL_VERSION) {\r
194                     Logger::Info("Unsupported Client Version : v%d", version);\r
195                     session->Send(network::ClientReceiveUnsupportVersionError(1));\r
196                     return;\r
197                 }\r
198 \r
199                 // UDPパケットの宛先を設定\r
200                 session->set_udp_port(udp_port);\r
201 \r
202                 Logger::Info("UDP destination is %s:%d", session->global_ip(), session->udp_port());\r
203 \r
204                 // テスト送信\r
205                 server.SendUDPTestPacket(session->global_ip(), session->udp_port());\r
206 \r
207                 uint32_t id = server.account().GetUserIdFromFingerPrint(finger_print);\r
208                 if (id == 0) {\r
209                     // 未登録の場合、公開鍵を要求\r
210                     session->Send(network::ClientRequestedPublicKey());\r
211                 } else {\r
212                     uint32_t user_id = static_cast<uint32_t>(id);\r
213                     // ログイン\r
214                     session->set_id(user_id);\r
215                     server.account().LogIn(user_id);\r
216                     session->encrypter().SetPublicKey(server.account().GetPublicKey(user_id));\r
217 \r
218                     server.account().SetUserIPAddress(session->id(), session->global_ip());\r
219                     server.account().SetUserUDPPort(session->id(), session->udp_port());\r
220 \r
221                     // 共通鍵を送り返す\r
222                     auto key = session->encrypter().GetCryptedCommonKey();\r
223                     session->Send(network::ClientReceiveCommonKey(key, sign.Sign(key), user_id));\r
224 \r
225                 }\r
226                 Logger::Info(msg);\r
227             }\r
228         }\r
229             break;\r
230 \r
231         // 公開鍵受信\r
232         case network::header::ServerReceivePublicKey:\r
233         {\r
234             if (auto session = c.session().lock()) {\r
235                                 auto public_key = network::Utils::Deserialize<std::string>(c.body());\r
236                 uint32_t user_id = server.account().RegisterPublicKey(public_key);\r
237 \r
238                                 assert(user_id > 0);\r
239 \r
240                                 session->ResetReadByteAverage();\r
241 \r
242                 // ログイン\r
243                 session->set_id(user_id);\r
244                 server.account().LogIn(user_id);\r
245                 session->encrypter().SetPublicKey(server.account().GetPublicKey(user_id));\r
246 \r
247                 server.account().SetUserIPAddress(session->id(), session->global_ip());\r
248                 server.account().SetUserUDPPort(session->id(), session->udp_port());\r
249 \r
250                 // 共通鍵を送り返す\r
251                 auto key = session->encrypter().GetCryptedCommonKey();\r
252                 session->Send(network::ClientReceiveCommonKey(key, sign.Sign(key), user_id));\r
253 \r
254             }\r
255             Logger::Info(msg);\r
256         }\r
257             break;\r
258 \r
259         // 暗号化通信開始\r
260         case network::header::ServerStartEncryptedSession:\r
261         {\r
262             if (auto session = c.session().lock()) {\r
263                                 \r
264                                 session->Send(network::ClientReceiveServerInfo(server.config().stage()));\r
265 \r
266                 session->Send(network::ClientStartEncryptedSession());\r
267                 session->EnableEncryption();\r
268 \r
269                 Logger::Info(msg);\r
270             }\r
271         }\r
272             break;\r
273 \r
274         // アカウント初期化情報の受信\r
275         case network::header::ServerReceiveAccountInitializeData:\r
276         {\r
277             if (auto session = c.session().lock()) {\r
278                                 auto data = network::Utils::Deserialize<std::string>(c.body());\r
279                 server.account().LoadInitializeData(session->id(), data);\r
280 \r
281                 const auto& list = server.account().GetIDList();\r
282                 BOOST_FOREACH(UserID user_id, list) {\r
283                     session->Send(network::ClientReceiveAccountRevisionUpdateNotify(user_id,\r
284                             server.account().GetUserRevision(user_id)));\r
285                 }\r
286 \r
287                 server.SendOthers(\r
288                         network::ClientReceiveAccountRevisionUpdateNotify(session->id(),\r
289                                 server.account().GetUserRevision(session->id())), session->id());\r
290 \r
291                 Logger::Info(msg);\r
292             }\r
293         }\r
294         break;\r
295 \r
296         // アカウント更新情報の要求\r
297         case network::header::ServerRequestedAccountRevisionPatch:\r
298         {\r
299             if (auto session = c.session().lock()) {\r
300                 uint32_t user_id;\r
301                 uint32_t client_revision;\r
302                 network::Utils::Deserialize(c.body(), &user_id, &client_revision);\r
303 \r
304                 if (client_revision < server.account().GetUserRevision(user_id)) {\r
305                     session->Send(network::ClientReceiveAccountRevisionPatch(\r
306                             server.account().GetUserRevisionPatch(user_id, client_revision)));\r
307                 }\r
308                 Logger::Info(msg);\r
309             }\r
310         }\r
311         break;\r
312 \r
313         case network::header::ServerUpdateAccountProperty:\r
314         {\r
315             if (auto session = c.session().lock()) {\r
316                 AccountProperty property;\r
317                                 std::string buffer = c.body().substr(sizeof(AccountProperty));\r
318                 network::Utils::Deserialize(c.body(), &property);\r
319 \r
320                 auto old_revision = server.account().GetUserRevision(session->id());\r
321 \r
322                 switch (property) {\r
323 \r
324                 case NAME:\r
325                     {\r
326                                                 std::string value;\r
327                                                 network::Utils::Deserialize(buffer, &value);\r
328                         server.account().SetUserName(session->id(), value);\r
329                     }\r
330                     break;\r
331                 case TRIP:\r
332                     {\r
333                                                 std::string value;\r
334                                                 network::Utils::Deserialize(buffer, &value);\r
335                         server.account().SetUserTrip(session->id(), value);\r
336                     }\r
337                     break;\r
338                 case MODEL_NAME:\r
339                     {\r
340                                                 std::string value;\r
341                                                 network::Utils::Deserialize(buffer, &value);\r
342                         server.account().SetUserModelName(session->id(), value);\r
343                     }\r
344                     break;\r
345                 case CHANNEL:\r
346                     {\r
347                                                 std::string value;\r
348                                                 network::Utils::Deserialize(buffer, &value);\r
349                                                 auto channel = *reinterpret_cast<const unsigned int*>(value.data());\r
350                         server.account().SetUserChannel(session->id(), channel);\r
351                                                 session->set_channel(channel);\r
352                     }\r
353                     break;\r
354                 default:\r
355                     ;\r
356                 }\r
357 \r
358                 auto new_revison = server.account().GetUserRevision(session->id());\r
359                 if (new_revison > old_revision) {\r
360                     server.SendAll(\r
361                             network::ClientReceiveAccountRevisionUpdateNotify(\r
362                             session->id(),new_revison));\r
363                 }\r
364 \r
365                 Logger::Info(msg);\r
366             }\r
367         }\r
368         break;\r
369 \r
370         // エラー\r
371         case network::header::UserFatalConnectionError:\r
372         {\r
373             if (c.body().size() > 0) {\r
374                 uint32_t user_id = network::Utils::Deserialize<uint32_t>(c.body());\r
375                 server.account().LogOut(user_id);\r
376 \r
377                 server.SendAll(\r
378                         network::ClientReceiveAccountRevisionUpdateNotify(user_id,\r
379                                 server.account().GetUserRevision(user_id)));\r
380 \r
381                 Logger::Info("Logout User: %d", user_id);\r
382                                 server.account().Remove(user_id);\r
383             }\r
384         }\r
385         Logger::Info(msg);\r
386         break;\r
387 \r
388         default:\r
389             break;\r
390         }\r
391 \r
392     });\r
393 \r
394         client_sync(server);\r
395 \r
396         if (server.config().is_public()) {\r
397                 public_ping(server);\r
398         }\r
399 \r
400     server.Start(callback);\r
401 }\r
402 \r
403 void public_ping(network::Server& server)\r
404 {\r
405     boost::thread([&server](){\r
406         while (1) {\r
407             boost::this_thread::sleep(boost::posix_time::seconds(10));\r
408                         server.SendPublicPing();\r
409         }\r
410     });\r
411 }\r
412 \r
413 void client_sync(network::Server& server)\r
414 {\r
415     bool execute_with_client;\r
416     try {\r
417                 #ifdef _WIN32\r
418                 using namespace boost::interprocess;\r
419         windows_shared_memory shm(open_only, "MMO_SERVER_WITH_CLIENT", read_only);\r
420                 #endif\r
421         execute_with_client = true;\r
422     } catch(std::exception& e) {\r
423         Logger::Info("Stand-alone Mode");\r
424         execute_with_client = false;\r
425     }\r
426 \r
427     // クライアントから起動している場合、クライアントの状態を監視\r
428         #ifdef _WIN32\r
429     if (execute_with_client) {\r
430         boost::thread([&server](){\r
431             while (1) {\r
432                 boost::this_thread::sleep(boost::posix_time::seconds(4));\r
433                 try {\r
434                                         using namespace boost::interprocess;\r
435                                         windows_shared_memory shm(open_only, "MMO_SERVER_WITH_CLIENT", read_only);\r
436                 } catch(std::exception& e) {\r
437                     server.Stop();\r
438                     break;\r
439                 }\r
440             }\r
441         });\r
442     }\r
443         #endif\r
444     #ifdef __linux__\r
445     network::ServerSigHandler handler(SIGINT,&server);\r
446     #endif\r
447 }\r