OSDN Git Service

Merge pull request #3532 from sikabane-works/release/3.0.0.87-alpha
[hengbandforosx/hengbandosx.git] / src / io / report.cpp
1 /*!
2  * @file report.c
3  * @brief スコアサーバ転送機能の実装
4  * @date 2014/07/14
5  * @author Hengband Team
6  */
7
8 #include "io/report.h"
9 #include "core/asking-player.h"
10 #include "core/stuff-handler.h"
11 #include "core/turn-compensator.h"
12 #include "core/visuals-reseter.h"
13 #include "game-option/special-options.h"
14 #include "io-dump/character-dump.h"
15 #include "io/input-key-acceptor.h"
16 #include "mind/mind-elementalist.h"
17 #include "net/http-client.h"
18 #include "player-base/player-class.h"
19 #include "player-info/class-info.h"
20 #include "player-info/race-info.h"
21 #include "player/player-personality.h"
22 #include "player/player-status.h"
23 #include "realm/realm-names-table.h"
24 #include "system/angband-version.h"
25 #include "system/dungeon-info.h"
26 #include "system/floor-type-definition.h"
27 #include "system/player-type-definition.h"
28 #include "system/redrawing-flags-updater.h"
29 #include "system/system-variables.h"
30 #include "term/gameterm.h"
31 #include "term/screen-processor.h"
32 #include "term/z-form.h"
33 #include "util/angband-files.h"
34 #include "view/display-messages.h"
35 #include "world/world.h"
36 #include <algorithm>
37 #include <fstream>
38 #include <span>
39 #include <sstream>
40 #include <string>
41 #include <string_view>
42 #include <vector>
43
44 #ifdef WORLD_SCORE
45
46 concptr screen_dump = nullptr;
47
48 #ifdef JP
49 constexpr auto SCORE_POST_URL = "http://mars.kmc.gr.jp/~dis/heng_score/register_score.php"; /*!< スコア開示URL */
50 #endif
51
52 static constexpr auto get_score_content_type()
53 {
54 #ifdef JP
55 #ifdef SJIS
56     return "text/plain; charset=SHIFT_JIS";
57 #endif
58 #ifdef EUC
59     return "text/plain; charset=EUC-JP";
60 #endif
61 #else
62     return "text/plain; charset=ASCII";
63 #endif
64 }
65
66 /*!
67  * @brief スコアサーバにスコアを送信する
68  * @param score_data 送信するスコアデータ
69  * @return 送信に成功した場合true、失敗した場合false
70  */
71 static bool post_score_to_score_server(PlayerType *player_ptr, const std::string &score_data)
72 {
73     http::Client client;
74     client.user_agent = format("Hengband %d.%d.%d", H_VER_MAJOR, H_VER_MINOR, H_VER_PATCH);
75
76     term_clear();
77
78     while (true) {
79         term_fresh();
80         prt(_("スコア送信中...", "Sending the score..."), 0, 0);
81         term_fresh();
82
83         const auto res = client.post(SCORE_POST_URL, score_data, get_score_content_type());
84         if (res.has_value() && (res->status == 200)) {
85             return true;
86         }
87
88         prt(_("スコア・サーバへの送信に失敗しました。", "Failed to send to the score server."), 0, 0);
89         (void)inkey();
90         if (!input_check_strict(player_ptr, _("もう一度接続を試みますか? ", "Try again? "), UserCheck::NO_HISTORY)) {
91             return false;
92         }
93     }
94 }
95
96 /*!
97  * @brief キャラクタダンプを引数で指定した出力ストリームに書き込む
98  * @param player_ptr プレイヤーへの参照ポインタ
99  * @param stream 書き込む出力ストリーム
100  * @return エラーコード
101  */
102 static errr make_dump(PlayerType *player_ptr, std::ostream &stream)
103 {
104     FILE *fff;
105     GAME_TEXT file_name[1024];
106
107     /* Open a new file */
108     fff = angband_fopen_temp(file_name, 1024);
109     if (!fff) {
110 #ifdef JP
111         msg_format("一時ファイル %s を作成できませんでした。", file_name);
112 #else
113         msg_format("Failed to create temporary file %s.", file_name);
114 #endif
115         msg_print(nullptr);
116         return 1;
117     }
118
119     /* 一旦一時ファイルを作る。通常のダンプ出力と共通化するため。 */
120     make_character_dump(player_ptr, fff);
121     angband_fclose(fff);
122
123     // 一時ファイルを削除する前に閉じるためブロックにする
124     {
125         std::ifstream ifs(file_name);
126         stream << ifs.rdbuf();
127     }
128
129     fd_kill(file_name);
130
131     /* Success */
132     return 0;
133 }
134
135 /*!
136  * @brief スクリーンダンプを作成する/ Make screen dump to buffer
137  * @return 作成したスクリーンダンプの参照ポインタ
138  */
139 concptr make_screen_dump(PlayerType *player_ptr)
140 {
141     constexpr auto html_head =
142         "<html>\n<body text=\"#ffffff\" bgcolor=\"#000000\">\n"
143         "<pre>\n";
144     constexpr auto html_foot =
145         "</pre>\n"
146         "</body>\n</html>\n";
147
148     int wid, hgt;
149     term_get_size(&wid, &hgt);
150
151     std::stringstream screen_ss;
152
153     auto &rfu = RedrawingFlagsUpdater::get_instance();
154     bool old_use_graphics = use_graphics;
155     if (old_use_graphics) {
156         /* Clear -more- prompt first */
157         msg_print(nullptr);
158
159         use_graphics = false;
160         reset_visuals(player_ptr);
161
162         static constexpr auto flags = {
163             MainWindowRedrawingFlag::WIPE,
164             MainWindowRedrawingFlag::BASIC,
165             MainWindowRedrawingFlag::EXTRA,
166             MainWindowRedrawingFlag::MAP,
167             MainWindowRedrawingFlag::EQUIPPY,
168         };
169         rfu.set_flags(flags);
170         handle_stuff(player_ptr);
171     }
172
173     screen_ss << html_head;
174
175     /* Dump the screen */
176     for (int y = 0; y < hgt; y++) {
177         /* Start the row */
178         if (y != 0) {
179             screen_ss << '\n';
180         }
181
182         /* Dump each row */
183         TERM_COLOR a = 0, old_a = 0;
184         auto c = ' ';
185         for (int x = 0; x < wid - 1; x++) {
186             int rv, gv, bv;
187             concptr cc = nullptr;
188             /* Get the attr/char */
189             (void)(term_what(x, y, &a, &c));
190
191             switch (c) {
192             case '&':
193                 cc = "&amp;";
194                 break;
195             case '<':
196                 cc = "&lt;";
197                 break;
198             case '>':
199                 cc = "&gt;";
200                 break;
201             case '"':
202                 cc = "&quot;";
203                 break;
204             case '\'':
205                 cc = "&#39;";
206                 break;
207 #ifdef WINDOWS
208             case 0x1f:
209                 c = '.';
210                 break;
211             case 0x7f:
212                 c = (a == 0x09) ? '%' : '#';
213                 break;
214 #endif
215             }
216
217             a = a & 0x0F;
218             if ((y == 0 && x == 0) || a != old_a) {
219                 rv = angband_color_table[a][1];
220                 gv = angband_color_table[a][2];
221                 bv = angband_color_table[a][3];
222                 screen_ss << format("%s<font color=\"#%02x%02x%02x\">", ((y == 0 && x == 0) ? "" : "</font>"), rv, gv, bv);
223                 old_a = a;
224             }
225
226             if (cc) {
227                 screen_ss << cc;
228             } else {
229                 screen_ss << c;
230             }
231         }
232     }
233
234     screen_ss << "</font>\n";
235
236     screen_ss << html_foot;
237
238     concptr ret;
239     if (const auto screen_dump_size = screen_ss.tellp();
240         (0 <= screen_dump_size) && (screen_dump_size < SCREEN_BUF_MAX_SIZE)) {
241         ret = string_make(screen_ss.str().data());
242     } else {
243         ret = nullptr;
244     }
245
246     if (!old_use_graphics) {
247         return ret;
248     }
249
250     use_graphics = true;
251     reset_visuals(player_ptr);
252     static constexpr auto flags = {
253         MainWindowRedrawingFlag::WIPE,
254         MainWindowRedrawingFlag::BASIC,
255         MainWindowRedrawingFlag::EXTRA,
256         MainWindowRedrawingFlag::MAP,
257         MainWindowRedrawingFlag::EQUIPPY,
258     };
259     rfu.set_flags(flags);
260     handle_stuff(player_ptr);
261     return ret;
262 }
263
264 /*!
265  * @brief スコア転送処理のメインルーチン
266  * @param player_ptr プレイヤーへの参照ポインタ
267  * @return 正常にスコアを送信できたらtrue、失敗時に送信を中止したらfalse
268  */
269 bool report_score(PlayerType *player_ptr)
270 {
271     std::stringstream score_ss;
272     std::string personality_desc = ap_ptr->title;
273     personality_desc.append(_(ap_ptr->no ? "の" : "", " "));
274
275     auto realm1_name = PlayerClass(player_ptr).equals(PlayerClassType::ELEMENTALIST) ? get_element_title(player_ptr->element) : realm_names[player_ptr->realm1];
276     score_ss << format("name: %s\n", player_ptr->name)
277              << format("version: %s\n", get_version().data())
278              << format("score: %ld\n", calc_score(player_ptr))
279              << format("level: %d\n", player_ptr->lev)
280              << format("depth: %d\n", player_ptr->current_floor_ptr->dun_level)
281              << format("maxlv: %d\n", player_ptr->max_plv)
282              << format("maxdp: %d\n", max_dlv[DUNGEON_ANGBAND])
283              << format("au: %d\n", player_ptr->au)
284              << format("turns: %d\n", turn_real(player_ptr, w_ptr->game_turn))
285              << format("sex: %d\n", player_ptr->psex)
286              << format("race: %s\n", rp_ptr->title)
287              << format("class: %s\n", cp_ptr->title)
288              << format("seikaku: %s\n", personality_desc.data())
289              << format("realm1: %s\n", realm1_name)
290              << format("realm2: %s\n", realm_names[player_ptr->realm2])
291              << format("killer: %s\n", player_ptr->died_from.data())
292              << "-----charcter dump-----\n";
293
294     make_dump(player_ptr, score_ss);
295     if (screen_dump) {
296         score_ss << "-----screen shot-----\n"
297                  << screen_dump;
298     }
299
300     return post_score_to_score_server(player_ptr, score_ss.str());
301 }
302 #else
303 concptr screen_dump = nullptr;
304 #endif /* WORLD_SCORE */