2019年度 入部
いけちぃ
きのこの声がでるひと
旧部長、右脳と左脳の2コアで生活してますなお並列処理は苦手ブログやってます↓https://coalabo.net
自己紹介画像
この人が書いた記事
ノーツタッチ時の遅延がなかなか改善できませんでした改善前は、効果音はUnity標準のサウンドアセットを使用していました。この再生方式は、安定した音声を再生するために十分な量のデータをバッファに溜め込んでから出力しています。 これまでBufferingTimeを短くしたり、音声データの先頭余白をギリギリまでカットしたりすることで、体感上遅延が少なくなるように工夫してきましたが、ノーツが多く同時に効果音が再生される箇所では遅延が若干感じられました。 ▼ ノーツの少ない箇所(遅延が小さい) ▼ ノーツの多い箇所(遅延が大きい) プロセカで使われている SonicSYNC 技術の解説動画を発見しましたたまたまチャンネル登録していた CA.Developer チャンネルで、「プロジェクトセカイ」の音声再生の仕組みとSonicSYNCで変わったこと | CA.Unity#3 という動画が、5日ほど前に投稿されました。 「プロジェクトセカイ」の音声再生の仕組みとSonicSYNCで変わったこと | CA.Unity#3 https://www.youtube.com/watch?v=7ql4CNBehes SonicSYNC とは、「スマートフォンでの音声再生遅延を限りなくゼロにする機能」で、ハードウェアと同期して即座に音声信号を生成することにより、ソフトウェア処理による再生遅延を限りなくゼロにできる技術です。音声処理が短時間で終わるように細かく分割し、音が必要になった時にすぐに準備して鳴らすという工程を繰り返すことで、これまでのようにバッファーに溜めておく必要がないため遅延が改善されます。 個人が無償で使えるADX2 LEというミドルウェア本来であれば企業向けである ADX2 が有償で提供されているのですが、学生や個人が無償で使うことができる ADX2 LE というものがあります。(コンテンツの売り上げが1,000万円以内であることなど、一定の条件はあります。)今回 ADX2 LE を導入して、ノーツタッチ時の効果音の遅延を改善していきたいと思います。 必要な音声ファイルを生成する今回の ADX2 では、音声ファイルを以下のものに変換する必要があります。 .acf: 環境設定やACBファイルから参照されるキューシート共通の情報を格納する .acb: キュー情報、再生パラメータ、メモリ再生用波形データをキューシート単位でパッキング .awb: ストリーム再生用波形データをキューシート単位でパッキング(ストリームにする場合のみ、今回は使用しない) これらのファイルを生成するために、「CRI Atom Craft」というソフトウェアを使用します。 CRI Atom Craft https://game.criware.jp/learn/tutorial/atomcraft/ 変換元の素材ファイルは以下のものが使用できます。 ファイル種類: WAVファイル(.wav)または AIFFファイル(.aif) チャンネル数: 最大6チャンネル 量子化ビット数: 16ビット 今回は1つのキューシートに、Perfect Great Good の3つのサウンドを登録します。 最後の書き出し時には、Unity Assets出力 にチェックを忘れないようにしましょう。 書き出したファイルは、Unity の CRIWARE からインポートすることで、StreamingAssets フォルダに保存されます。 ハマったところUnity 側で CRI Atom のスクリプトがありますので、読み込んだACFファイル ACBファイル を指定します。 ここで、Don't Destroy On Load のチェックをつけておきます。読み込み後に destroy してしまうと、Sceneを2回目に読み込んだ際(2曲目をプレイする際)にサウンド再生ができなくなります。 そして、Don't Initialize On Awake のチェックを外しておきます。 また、ビルドする際に 強制的にUnity 標準オーディオが無効化 されます。 Unity標準のオーディオが無効化されることで、アプリケーションのCPU負荷およびメモリ使用量を軽減できるそうです。 しかし今回は、プレイ音源は Assetbundle を使用しているため、Unity標準オーディオと共存させます。 CriWareBuildPreprocessorPrefs.asset という設定ファイルを書き出し、Mute Other Audio を解除します。 これでビルド時に、強制的にUnity標準オーディオが無効化されないようになります。 導入後の遅延の測定計測した結果、ノーツをタッチしてから効果音が鳴るまで、約60~70ミリ秒まで遅延を減らすことができました。 CRIWAREの公式サイトでは、iOS・Android共に「画面をタップしてから音が出るまで」53ミリ秒程度と記載されていたので、vivace(製作中の音ゲー)のタッチ入力のアルゴリズムに改善方法があるということになります。 しかし、ノーツが多い時の効果音遅延が劇的に改善されているので、DEMON譜面をプレイされるガチプレイヤーさんたちには大きなアップデートになるのではないでしょうか。もう少しアルゴリズムを改修してからストアにリリースしようと思いますので、お楽しみに〜 ダウンロードはこちらさっきDBをみてみると、登録ユーザー数が299人になっていました! これを見たあなた、ぜひ300番目のプレイヤーになってください AppStore: https://bit.ly/vivace-app-ios GooglePlayStore: https://bit.ly/vivace-app-android
内容を見る
はじめに 私がよく参考にさせて頂いているデザインサイト・フリーアイコンサイトをまとめてみました! デザイン編 Dribbble 全て閲覧するにはアカウントが必要アニメーションやプロダクトデザインなども掲載されているのが面白いところAdobe Xd や Figma などのツール向けにダウンロードできるのが素晴らしい UI Garage iOS・Web・Androidとジャンル分けされており、すぐに目的のデザインにたどり着けるこのサイトは細かいジャンル分けがされており、「404ページ」・「ダッシュボード」・「設定画面」など90種類以上の項目があり、参考にしたいパーツのデザインにどっぷりと浸かれる記事ページでは、いろいろなサービスのデザイン考察や設計に役立つTipsを載せていて,暇な時間に軽く読める Material Design Googleさんが2014年にデザインガイドラインとして定めた、例の "Material Design"視覚的に良いデザイン・悪いデザインを学習できるのは有名だが、動きや起動画面・テキストの表示方法などの細かいUI・UXも学習できる点に価値ありアイコンやテーマもダウンロードできる 動くWebデザインアイディア帳 ティザーサイトを制作する際にかなり重宝!!かなりかっこいい1つの機能(例: ローディング)にもさまざまな配置パターンが用意されており、コードを書く作業がかなり減るjQueryを使用するものが多いので、脱jQueryを目指している方には大変かも... アイコン編お気に入りのアイコンを一言で紹介 Feather 角が取れた優しいストロークアイコンアイコン数は若干少なめだが、アイコンが統一しているのでナビゲーションバーなどに使いやすい icons8 1つの画像に様々な画風のアイコンがたくさんあるため、このWebページを訪れれば欲しいタイプの画像が見つかる私は 「iOS」 風が好き JAM こちらも丸みを帯びたストロークアイコン先程の Feather と比べ、アイコン数が結構多いのでよく使っている Uicons flaticon の会社がやっている、Font Awesome のようなものアイコンがかなり可愛いのでよく使っているストロークアイコン以外にも、塗りつぶしアイコンにできたり線の角を角ばらせることもでき、拡張性は高い SVG Repo "Collection"という単位があり、その Collection 内にあるアイコン(画像)はテーマが統一されており、同じ画面に複数のアイコンを使う際に見栄えが良いこの "Collecton" がたくさん用意されており(現時点で450+セットある)、フラットな天気アイコンや丸みを帯びた日本食アイコンなど、眺めるだけで楽しい Font Awesome 認知度は一番高いと思うが、一応入れておくCDNやnpmなどから入れることができ、導入がかなり楽アイコン数もずば抜けて多い unDraw 海外のデザイナーたちがよく使ってるフラットアイコン stories 同じくフラットアイコン
これは1年前のお話...学祭に向けて、昨年 しげ と ふぉ と3人で、VIVACEという音ゲーを制作 しました。 当日の朝4時まで Discord をつないでコーディングをし、学祭ギリギリでビルドして、エラーがないことも確認できました。 Discord の通話を切った後、悲劇は起こります 私: 「じゃぁもう4時半だし、PlayStore に APK ファイルをアップロードして、寝るか〜〜」 カチッ カチッ (ドラッグの音) Google Play Store: 「 エラー: APK ファイルのサイズを 100 MB まで小さくするか、APK 拡張ファイルを使用する必要があります。」 私: 「 !!!!!!!!!」 そうです、この音ゲーアプリ、楽曲ファイルが原因で軽く100MBを超えちゃってるんです。 さすがに眠くて、Google Play Store でのリリースは諦めました。(当日は APK 配布) 今回は追加ダウンロード機能を実装して、アプリストアへのリリースまでやります!大体の音ゲーって、アプリサイズ縮小のために、起動後にコンテンツのダウンロードを行いますよね。 今回はそれを、AssetBundle と用意している API サーバを用いて実装していきます。 AssetBundle とは、アプリのビルド時に行っている処理を事前に行っておく事により、実行時に外部からアセットがロードできるようにしたものです。 これを Web サーバなどに置いておくことで、ダウンロードしてリソースを使用できるようになります。 AssetBundle の作成今回は AssetBundleBrowser を使用します。これはプレリリース版ですので、使用は自己責任でお願いします。 AssetBundleBrowser (GitHub) https://github.com/Unity-Technologies/AssetBundles-Browser 使い方は簡単、このツールに AssetBundle にまとめたいものをドラッグします。 今回だと、音源とプレビュー用の音源、各難易度の譜面とアートワークをセットし、 Build タブに移動します。 AssetBundle は各プラットフォームごとに分かれています。 今回のリリースは、iOS と Android に絞ってますので、この2つを作成します。 配信するためのサーバを用意します本来は用意してあるバックエンドサーバから、AssetBundle を DL できるようにするといいのですが、Google Cloud Platform を使用しているため、あまりディスク容量を増やしたくありません。 ということで、Google Drive のいつもの作戦を使います! 本来 Google Drive で共有リンクを生成すると、 https://drive.google.com/file/d/ hogehoge / view?usp=sharing というリンクになるのですが、以下のようにリンクを変更すると、ファイルをダウンロードさせるヘッダーに変更できます。 https://drive.google.com/uc?id= hogehoge &usp=sharing 毎回リンクを手動で変更させるのは面倒なので、データベースには共有リンクで登録しておき、アプリ内でリンクを変換させるようにしておきます。 music.asset_bundle_ios .Replace("https://drive.google.com/file/d/", "https://drive.google.com/uc?id=") .Replace("/view?usp=sharing", "&usp=sharing"); また、曲選択画面に表示される曲一覧は、DB と同期できるようにしてあります。 急遽任意の曲を非表示にするときや、楽曲を追加した時でもリアルタイム更新できるようになりました。 (セキュリティのため、通信には JWT での認証が必要です) アプリ内画面では、APIのデータを取得しこのように表示されます。 運用・メンテナンスしていく上では、こういうリモートでの更新機能も大切ですね。 Unity 内部のコード調べてみると、UnityWebRequest には AssetBundle をダウンロードする専用の UnityWebRequestAssetBundle が用意されているようです。 また、一度DLした楽曲はキャッシュしておきたいので、第2引数に CachedAssetBundle を入れて呼び出すことで、2回目以降の起動でダウンロードを省略できます。 キャッシュされる条件ですが、CachedAssetBundle 以外にも version(uint) や hash(Hash128) を第2引数に入れてロードすることで、同じくキャッシュが行われるそうです。(非推奨になりつつありますが、WWW.LoadFromCacheOrDownload を使ってキャッシュする場合は version の型は int に変わるそうなので注意) ちなみに非推奨という点では、SendWebRequest の後のリクエスト処理も、request.isNetworkError ではなく、request.result の値を、UnityWebRequest.Result.Success・UnityWebRequest.Result.ConnectionError・UnityWebRequest.Result.ProtocolError... といった用意されたクラスの enum の値で識別するのを推奨するようになりましたね。見やすくてとても気に入っています。(コード詳細↓) private IEnumerator DownloadAssetBundle(MusicList music, string downloadUrl) {   var c = new CachedAssetBundle   {     name = music.name,     hash = Hash128.Parse(music.name + music.version)   };   using var request = UnityWebRequestAssetBundle.GetAssetBundle(downloadUrl, c);   yield return request.SendWebRequest();   switch (request.result)   {     case UnityWebRequest.Result.Success:       /* 略 */       break;     case UnityWebRequest.Result.ConnectionError:    case UnityWebRequest.Result.ProtocolError:     case UnityWebRequest.Result.DataProcessingError:       /* 略 */       break;     case UnityWebRequest.Result.InProgress:       break;     default: throw new ArgumentOutOfRangeException();   } } CachedAssetBundle を使用することで、URL から抽出されるファイル名と指定したバージョン番号を意識しなくて良くなり(そもそも Google Drive のリンクなのでできないですが...)、保存場所の衝突など AssetBundle でよく発生する問題をハッシュ値で回避することができます。 あとは対応するプラットフォーム毎にダウンロードURLを切り替え、リソース使用部分を全て AssetBundle から読み込むように設定し直します。 今回のプロジェクトの場合、バックエンドのスコア登録機能も、データベースの楽曲テーブルと同期させる必要がありました。 無事ストアリリースできました!! ストアリリース後、1日で60人の方が遊んでくれて、正直GCPの料金が上がらないか心配 とても嬉しい です。 ということで、是非こちらから遊んでみてくださいね ♪ 今秋、楽曲追加も予定しています。 AppStore: https://bit.ly/vivace-app-ios GooglePlayStore: https://bit.ly/vivace-app-android
だってお風呂で音楽聴きたいもん...こちらの iPhoneXS、毎日お風呂で使ってたんですが水没しちゃいました。 iPhoneXS は 2018 年に発売され、IEC規格60529に基づくIP68等級に適合しているそうです。 どのくらいかというと、最大水深2メートルで最大30分間使用できるらしい。。 毎日お風呂で壁に立てかけて音楽を聴いてたんですが(風呂に携帯持ち込むなと、友人にめちゃめちゃ怒られました)、突然ディスプレイがちらつき始めたのです。 ん、まだ息はある...?とりあえず iPhone を完全に放置し、内部が乾燥したであろう2日後に、おそるおそる Mac に接続。 すると、なんと反応があるではないですか!もしかして壊れていたのはディスプレイだけ...? とりあえず急いでバックアップをとり、密林に行って交換用ディスプレイを購入することにしました。 購入したもの https://www.amazon.co.jp/gp/product/B08RYRPJGK/ref=ppx_yo_dt_b_asin_title_o06_s01 届きました!修理開始です ディスプレイが届きました!めちゃめちゃ綺麗な箱に入って届いて、若干テンションが上がりました(笑) 今回のキットにはドライバー3本とピンセット、ヘラ、吸盤、開口ツールなどの工具に加え、防水フレームステッカー(大切)とおまけのガラスフィルムもついてきました。 写真はは左から順に、星型ドライバー、十字型ドライバー、Y字型ドライバーです。 まずはおしりのネジ2つを外していきます。YouTube で分解動画などがよくありますが、皆さん簡単そうに回していますが一番ここが難しかったです。 少しでも力を抜くと、すぐにネジがなめてしまうので注意しましょう。 あとは大体想像つくかと思いますが、ドライヤーでシールの粘着力を弱めていきます。 そして吸盤で引っ張り、隙間を作って開いていきます。 お風呂で使っていたため、湿気などで防水フレームのシールが朽ちているかなと思っていましたが、予想通りでした。 ゴムが水分で、画像のように粉々になっていました。(あとで綺麗に除去しておきました。) 交換用の防水フレームシールが入っていてよかったです! 本体正面から見て右側にケーブルがあるので、あまり深くヘラを差し込まないようにしましょう。 ようやくご対面です(一苦労) ここから、まずバッテリーに接続されているケーブルを外し、その後ディスプレイにつながるケーブル3本を外します。 ケーブルが接続されているところにはカバーがついているのですが、ここをネジで外します。 驚いたのはネジの高さ(深さ?)で、1~2mm くらいしかありませんでした。 さすが Apple 製品、コンパクトな作りです。(紛失注意) こちらがカバーを外した画像です。 まずはバッテリーの接続をはずし、通電するといけないので端子をセロハンテープ等で巻いておきます。 そして、てこの原理をつかってディスプレイのケーブルコネクタを解除します。 これで iPhone の 2枚おろしの完成です! 新しいディスプレイに FaceID 部分を移植しますここが一番緊張しました。 上部のカメラ部分を新しいディスプレイに移すのですが、あまりネジで強く締めすぎるとディスプレイが割れてしまうので、慎重に作業を行います。 あとは逆の手順でもとに戻していきます組み立ては端子を挿すだけでいいので、簡単でした。 蓋を閉じる前に、動作確認をしておきます! ついた無事電源が入りました!!! もう一度電源を切って、蓋を閉めておきます。 耐水性がなくなってしまうので、付属したフレームステッカーを貼るのを忘れないようにしましょう。 閉めるときに、おしりのネジを片方なめてしまったことは内緒です。 交換ディスプレイのレビュー今回購入したディスプレイのレビュー欄には、3D Touch の感度が悪いとありました。 私が試した感じだと、やはり 3D Touch は押し込んでも反応しないことが8割くらいあります。 現在2ヶ月くらい利用していますが、ディスプレイ自体はきちんと使用できています。ただ、気づいた点もいくつかあるのでまとめておきます。 ・正規品じゃないので、True Tone 機能が使えなかった。 ・3D Touch は 1秒くらい長押ししてから押し込むと反応する。 ・ディスプレイの下部のカーブが正規品と異なる。(角の表示範囲が若干異なる) ただ、この iPhone XS のゴールドの色や形は気に入っているので、これからも修理しながら大切に使っていこうと思います! 追伸:お風呂用の防水 Bluetooth スピーカーを購入しました。
前回の「情クラ!」サイトの 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 関係で得た知見はかなり大きいものだったので、今後何か一般向けのサービスに繋げていきたいと思います。
プログラミングを始めた頃に構築した、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 などの従量課金制のサーバで運用するには、かなり気を使ってしまうので正直解放されました。 かといって、情クラ!での活動はまだまだ続けていく予定なので、ぜひこのブログでも活動を共有していけたらなと思います!