OSDN Git Service

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