取得に失敗しました

2019年度 入部

GitHub

いけちぃ

部長

自己紹介

右脳と左脳の2コアで生活してます
なお同時処理は苦手

自己紹介画像

取得に失敗しました

この人が書いた記事

プログラミング Polygon
プログラミング

アプリ版 Minecraft サーバ制御ツールの制作

前回の「情クラ!」サイトの Android アプリ制作記です。(※この記事はブログからの転用です。) やっぱアプリ化、したいよね!!Webベースのツールを作ると、そのアプリ版を作りたくなる、ここまでがテンプレですよね。 今回は、ストアにリリースが手軽な Android アプリを制作したいと思います。 Android Studio 使いやすい...この時、プログラミングというものに触れてまだ半年もたっていませんでした。 なので初心者でも使いやすい Visual Studio Code を当時愛用していました。(今でも時々使っています) しかし今回アプリ開発というのもあり、デバッグのしやすいエディタを使うことにしました。 まぁもちろん Android Studio 一択になるわけですが。 JetBrains 社が開発したソフトウェアを初めて触ったのですが、 これがまた使いやすいソフトウェアで感動したのを覚えています。 (私が JetBrains 教徒になる話はまたいつか) さぁ開発だ!の前に、デザインを作っていきます。 スマホによってサイズが異なるので、対応できるデザインを意識して作成しました。 コーディング すべしぬべし今回の技術的な部分です。サーバの様子を取得するのとリクエストを送る機能をつけます!(大したことはしてませんが) まず、オンラインプレイヤーの取得部分です。 ホーム画面の上半分には、オンラインのメンバーが一覧でわかるようにしています。 今回、Minecraftの画像を取得するAPIを自作しました。 そこからAndroidに画像を取得するようにしています。 APIからの画像取得には、Picasso というライブラリを使用しました。 Picasso(公式サイト) https://square.github.io/picasso/ 具体的には、以下のような書き方でさくっとインターネットから画像取得ができちゃうものです!Picasso.get()   .load("https://jokura.net/api/getSkin?id=minecraft_id") // 今回作成したAPI   .into(face1);face1というのは ImageView の id で、APIから取得した画像を流し込んでくれます! また、プレイヤーがオンラインかどうかは、用意したAPIから返ってきた情報を元に表示を切り替えます。 APIからのGET・POSTメソッドには、便利な OkHttp3 などの便利なライブラリがありますが、この時(約2年前)は初心者だったこともあり、Java通信(HttpUrlConnection)で実装しました(笑) var connection: HttpURLConnection? = null var reader: BufferedReader? = null val buffer: StringBuffer try {   val url = URL( /* 通信するAPI */ )   connection = url.openConnection() as HttpURLConnection   connection.connect() // ここで指定したAPIを叩いています。   // 取得したデータを処理していきます。とりあえず取得した文字をbufferに。   val stream = connection.inputStream   reader = BufferedReader(InputStreamReader(stream))   buffer = StringBuffer()   var line: String?   while (true) {     line = reader.readLine()     if (line == null) break     buffer.append(line)   }   // ここからJSONに変換していきます。   val jsonText = buffer.toString()   val parentJsonObj = JSONObject(jsonText)       // オンラインメンバーの情報は、JSON内の top というキーの中に格納してあります。   val parentJsonArray = parentJsonObj.getJSONArray("top")   val detailJsonObj = parentJsonArray.getJSONObject(0)   // player1さんのオンライン状況が取得できました!   val player1: Int = detailJsonObj.getInt("player1")   return player1 } catch (e: MalformedURLException) {   e.printStackTrace() } catch (e: IOException) {   e.printStackTrace() } catch (e: JSONException) {   e.printStackTrace() } // 接続を切断してあげましょう。おつかれ! finally {   connection?.disconnect()   try {     reader?.close()   } catch (e: IOException) {     e.printStackTrace()   } } // 失敗した時はnullやエラーコードなどを返しましょう。 return null }一部抜粋・改変していますが、こんな感じでリクエストをかいています。 ちょっと脱線しちゃいましたね他にどんな機能を実装したのかみてみましょう!たぶんこちらの方が興味ありますよね (笑) トップ画面には、「バックアップ」と「再起動」の2つのボタンが用意してあります。 この2つのボタンについては後述します。 また、「稼働状況」を押すとサーバの現在の状況をみることができます。 今思うと、ここのデザインは Web 版と同じなので、 WebView で表示させるとよかったですね (笑) とりあえず、今回は xml ファイルで view を丁寧に記述しました。 今回のメインディッシュ本アプリのメイン機能は、なんといってもこの2つです! トップに設置されている「バックアップ」「再起動」の機能について説明します。 この Activity の開始と同時に、バックエンドと通信して 再起動 または バックアップ が実行できるか確認して、表示を切り分けます。 実行できない例としては、 - サーバが停止している - 他のユーザが現在処理を行っている - 処理実行後のクールタイムにある のいずれかですね。右上の更新ボタンで最新情報を再取得できます。 今回、神経を使ったのは処理部分でしたなんといってもサーバ関連で怖いのが、リクエストが同時に送られることですよね。 今回、サーバを制御できるプラットフォームが複数あるため、他のアプリやWebサイトから同時にリクエストが送られた場合、最初のリクエストのみ通す必要があります。また、再起動やバックアップなどが実行された後は、クールタイムを設ける必要もあります。 そういった、さまざまなリクエストを処理できるように、情クラ!ではバックエンドのAPIを用意し、そこからサーバ処理を行っています。 リクエストが失敗した場合には、その理由をエラーコードで返しユーザに通知しています。 実行可能の状態でボタンを押した場合でも、バックエンドでキャンセルされた場合その旨のトーストが表示されます。 2年の月日を経て...前回の記事 でも述べた通り、情クラ!はサービス終了しました。 今回、"プログラムの整合性" というものを勉強できた、サービス開発だったと思います。 他にも Minecraft 関係で得た知見はかなり大きいものだったので、今後何か一般向けのサービスに繋げていきたいと思います。

> 内容を見る

プログラミング Polygon
プログラミング

Minecraftサーバ リモート制御サイトの制作

プログラミングを始めた頃に構築した、Minecraftサーバのリモート制御ツールの開発体験記です。(※この記事はブログからの転用です。)Minecraft っていいよねMinecraft って、複数人でプレイすると沼ですよね。 今や大陸内には高速道路が広がり、複数の大陸間には水上橋が建築されています。(やばすぎ) ある日、Minecraft のサーバ管理者になった最初はローカルでサーバを建ててプレイしていましたが、 プレイヤーがどんどん増えてきたため、Google Cloud Platform(GCP)でサーバを立てることにしました。 これが、「情クラ!」という Minecraft サーバの誕生のきっかけです。 いつどこでも Minecraft サーバで遊べる環境が完成しましたが、しかし1つ大きな問題が発生しました。 Google Cloud Platform(GCP)が、まだ365日無料トライアルだった頃の話です。 当時は n1-standard-1 プラン(仮想 CPU 数: 1, メモリ: 3.75 GB)を使用していたため、大人数が長時間プレイしていると動作がもっさりしてくるのです。 サーバ管理者だった私は、毎度 SSH 接続してサーバを再起動していました。 ですがここは、ぜひ技術の力で課題解決をしよう!ということで、Minecraftサーバを誰でも簡単に制御できる、Webアプリケーションを作成することになりました。まずは完成品をどうぞ 最初に実装したのは、「ホーム」のお知らせ機能と「サーバ状況」の機能です。 モバイルファーストのデザインですが、レスポンシブ対応させています。 ホーム: プレイヤーが自由に建築報告を投稿することができますサーバ状況: サーバの起動・停止状況やオンラインメンバーを確認できますここで、少し技術的な話をしましょう一応そういうブログ( ?)なので、今回 Minecraft サーバと連携したノウハウについて記述しておきます。 Minecraft サーバでは、ある特定のパケットを受け取ると、現在のサーバのステータスを返す機能が搭載されています。 これは、Minecraft のマルチサーバ 一覧画面などで使用されています。 まず、クライアント側が Handshake パケットを送信します。(以下 wiki の情報) さらに続けてリクエストパケットを送信します。その応答パケットとして、サーバが以下のようなJSONを返します。{ "version": { "name": "1.16.1", "protocol": 47 }, "players": { "max": 12, "online": 5, "sample": [ { "name": "minecraft_name", "id": "4566e69f-c907-48ee-8d71-d7ba5aa00d20" } ] }, "description": { "text": "Hello world" }, "favicon": "data:image/png;base64,<data>" }また、このパケットを送信するためのライブラリとして、今回は以下のPHPライブラリを使用しました。 PHP-Minecraft-Query https://github.com/xPaw/PHP-Minecraft-Query実装した機能あまり技術的な話をすると長くなっちゃいそうなので、次に行きます。 他に実装した機能も紹介します。 ピクチャ: プレイヤーが自由に画像を投稿することができますオンラインユーザ: ホワイトリストに登録されたプレイヤーが表示され、サーバに入っているかどうかがわかります起動・停止: サーバをしばらく利用しない場合は、GCP課金対策のためにサーバを切ることができます再起動: Minecraft サーバを再起動させることができますバックアップ実行: Minecraft サーバのバックアップを作成します(毎日5:00に定期実行されます)バックアップ履歴: バックアップ履歴の一覧を表示します情クラマップ: 情クラワールドの建築物紹介が載っています情クラ!公式アプリ: 公式アプリへのリンクですみんなログインしよう今回サーバのコントロール機能が Web に公開されるので、ログイン機能を実装する必要があります。全員 Google アカウントを持っていたので、Firebase Authentication を使って Google ログインを実装します。 以下の公式ドキュメントを読むとよくわかりますよ(雑) Firebase Authentication(Google) https://firebase.google.com/docs/auth/web/start?hl=jaマイクラサーバのバックエンドはどうなってるの?Minecraft サーバでは、Node.js + TypeScript でAPIを構築しています。 以下は Minecraft を起動するルーティングの一部抜粋です。// --- Start Server ------------------------------------------------------------ router.post('/api/run/start', (req: express.Request, res: express.Response) => { const schema = Joi.object({ user: Joi.string().required(), }) const validation = schema.validate(req.body) if (validation.error) { post("不正なリクエストを拒否しました", "ユーザ固有IDが設定されていないリクエストが送信されました", 3) res.status(400).send('Bad request') return } statusAsync(req.body.user) .then(() => { startAsync(req.body.user) .then(() => { res.send('Success') }) .catch((err) => { if (err == 'failed due to run interval') post("起動コマンドを拒否しました", "前回の処理の実行から" + process.env.WAIT_SECONDS_FROM_LAST_PROCESS + "秒経過していないため、コマンドを拒否しました。", 2) else post("起動コマンドを拒否しました", "サーバが既に起動しているため、起動コマンドを拒否しました。サーバとの同期ができていない恐れがあります。[Err: startAsync()]", 2) res.status(400).send('Bad request') }) }) .catch(() => { post("起動コマンドを拒否しました", "既に起動しているため、起動コマンドを拒否しました。サーバとの同期ができていない恐れがあります。[Err: statusAsync()]", 2) res.status(400).send('Bad request') }) }) // -----------------------------------------------------------------------------軽く説明していきます。 まず、Joi という npm パッケージを利用して、送信されたクエリパラメータなどのバリデーションを行います。 そして、post( ) という関数がありますが、これはサーバのエラーなどを Discord のサーバに通知するための関数として用意してあります。 server.jar は screen 上で走らせているのですが、screen の二重起動にならないように初めに起動してもよいかのチェックも行っています。 また、実行系はシェルにまとめてあります。#!/bin/bash JARFILE=/home/jokura_server/minecraft/server.jar MEM=15000M cd `dirname $0` screen -UAmdS minecraft java -server -Xms${MEM} -Xmx${MEM} -jar ${JARFILE} nogui上記のファイルは、起動用のシェルです。 他にも再起動用やバックアップ用などのシェルも用意してあります。(下記は 再起動用)#!/bin/bash WAIT=30 STARTSCRIPT=/home/jokura_server/minecraft/start.sh SCREEN_NAME='minecraft' screen -p 0 -S ${SCREEN_NAME} -X eval 'stuff "say '${WAIT}'秒後にサーバを再起動します\015"' screen -p 0 -S ${SCREEN_NAME} -X eval 'stuff "say すぐに再接続可能になるので、しばらくお待ち下さい\015"' sleep $WAIT screen -p 0 -S ${SCREEN_NAME} -X eval 'stuff "stop\015"' while [ -n "$(screen -list | grep -o "${SCREEN_NAME}")" ] do sleep 1 done $STARTSCRIPT今現在の 情クラ!今は残念ながら、Webサービスを終了しています。 (Minecraft の活動自体はしています!) 2021年4月から、Minecraft サーバを別のメンバーが自宅サーバで建ててくれることになりました。 やはり GCP などの従量課金制のサーバで運用するには、かなり気を使ってしまうので正直解放されました。 かといって、情クラ!での活動はまだまだ続けていく予定なので、ぜひこのブログでも活動を共有していけたらなと思います!

> 内容を見る