taki

C#でリモートのMySQLやSSH接続をやってみた


こんにちは、大阪スタジオ ソフトウェアエンジニアの滝です。
今回のお話は、C#でMySQLに接続してデータを取得したり、SSH接続でコマンドの送信をやってみたお話になります。

きっかけは、業務で集計ツールを作成する機会があり、その要件は、Windows環境からターミナルを使わずに、データベース及びログサーバーからデータ集計を行い、その結果をExcelファイルに出力するというものです。
実現には様々な方法が考えられますが、今回はWindows環境から行いたいということだったため、デスクトップクライアントを作成し、アプリケーションを実行することで集計処理を自動で行ない、結果をExcelファイルに出力するようなツールを作成することにしました。
要件を満たすために、C#でMySQLに接続してデータを取得したり、SSH接続でコマンドの送信を行う必要があり、どのように実現したかを簡単なサンプルコードと共に紹介させていただきます。

プロジェクトはコンソールアプリでもフォームアプリでもどちらでも動作します。
対応プラットフォームなど、詳しい情報は以下のリンクから確認できます。

 

今回使うライブラリ

・SSH.NET (github)
https://github.com/sshnet/SSH.NET

・MySQL(github)
https://dev.mysql.com/doc/connector-net/en/

 

SSH接続のやりかた

  • 必要なもの
    • SSH接続が可能なリモート環境(物理マシンでもVMでもOK)
      • 記事内で使用している環境はLinux CentOS 6.9(VirtualBox使用)
    • VisualStudio(どのエディションでも可)
      • 記事内で使用している開発環境はVisualStudio2017
  1. 新しいプロジェクトを作成または既存のプロジェクトを読み込む
    このサンプルではConnectSSHというプロジェクトをコンソールプロジェクトで作成しました。
    既存のプロジェクトでテストしていただいても問題ありませんが、できれば新規作成をおすすめします。

  2. NuGetからSSH.NETの追加
    図のように、ソリューションエクスプローラー内で1で作成したプロジェクト名の項目を右クリックし、NuGetパッケージの管理をクリックします。参照タブをクリックし、検索フォームに「SSH.NET」と入力し検索します。Renci製のSSH.NETをプロジェクトにインストールします

  3. とりあえず接続してみる
    以下にサンプルコードを記載します。コードの説明はコメントに書いております。

    using System;
    using Renci.SshNet;
    
    namespace RemoteServerConnect
    {
        class Program
        {
            static void Main(string[] args)
            {
                try
                {
                    // 接続先のホスト名またはIPアドレス
                    var hostNameOrIpAddr = "192.168.xxx.xxx";
    
                    // 接続先のポート番号
                    var portNo = 22;
    
                    // ログインユーザー名
                    var userName = "taki";
    
                    // ログインパスワード
                    var passWord = "xxxxxxxxxx";
    
                    // コネクション情報
                    ConnectionInfo info = new ConnectionInfo(hostNameOrIpAddr, portNo, userName,
                        new AuthenticationMethod[] {
                            new PasswordAuthenticationMethod(userName, passWord)
                            /* PrivateKeyAuthenticationMethod("キーの場所")を指定することでssh-key認証にも対応しています */
                        }
                    );
    
                    // クライアント作成
                    SshClient ssh = new SshClient(info);
    
                    // 接続開始
                    ssh.Connect();
    
                    if(ssh.IsConnected)
                    {
                        // 接続に成功した(接続状態である)
                        Console.WriteLine("[OK] SSH Connection succeeded!!");
                    }
                    else
                    {
                        // 接続に失敗した(未接続状態である)
                        Console.WriteLine("[NG] SSH Connection failed!!");
                        return;
                    }
    
                    // 接続終了
                    ssh.Disconnect();
                }
                catch(Exception ex)
                {
                    // エラー発生時
                    Console.WriteLine(ex);
                    throw ex;
                }
                
            }
        }
    }
    

    パスワード認証でSSH接続をしてみた例になります。
    接続できない場合は、ファイアウォールやポートフォワーディング設定など、ネットワーク接続自体に問題がないかを確認してみてください。

  4. コマンドを送信してみる(リモートマシンの時間を取得)
    using System;
    using Renci.SshNet;
    
    namespace RemoteServerConnect
    {
        class Program
        {
            static void Main(string[] args)
            {
                try
                {
                    // 接続先のホスト名またはIPアドレス
                    var hostNameOrIpAddr = "192.168.xxx.xxx";
    
                    // 接続先のポート番号
                    var portNo = 22;
    
                    // ログインユーザー名
                    var userName = "taki";
    
                    // ログインパスワード
                    var passWord = "xxxxxxxxxxxxxx";
    
                    // コネクション情報
                    ConnectionInfo info = new ConnectionInfo(hostNameOrIpAddr, portNo, userName,
                        new AuthenticationMethod[] {
                            new PasswordAuthenticationMethod(userName, passWord)
                            /* PrivateKeyAuthenticationMethod("キーの場所")を指定することでssh-key認証にも対応しています */
                        }
                    );
    
                    // クライアント作成
                    SshClient ssh = new SshClient(info);
    
                    // 接続開始
                    ssh.Connect();
    
                    if(ssh.IsConnected)
                    {
                        // 接続に成功した(接続状態である)
                        Console.WriteLine("[OK] SSH Connection succeeded!!");
                    }
                    else
                    {
                        // 接続に失敗した(未接続状態である)
                        Console.WriteLine("[NG] SSH Connection failed!!");
                        return;
                    }
    
                    // 送信したいコマンドを変数に入れる
                    var commandString = "date";
    
                    // コマンドを作成する
                    SshCommand cmd = ssh.CreateCommand(commandString);
    
                    // コマンドを実行する
                    Console.WriteLine("[CMD] {0}", commandString);
                    cmd.Execute();
    
                    // 結果を変数に入れる
                    var stdOut = cmd.Result;
                    var stdErr = cmd.Error;
    
                    // 終了コードを表示する
                    Console.WriteLine("終了コード:{0}", cmd.ExitStatus);
    
                    // 標準出力を表示する
                    if (stdOut != string.Empty)
                    {
                        Console.WriteLine("標準出力:");
                        Console.WriteLine(stdOut);
                        Console.WriteLine("---------");
                    }
    
                    // エラー出力を表示する
                    if (cmd.ExitStatus != 0 && stdErr != string.Empty)
                    {
                        Console.WriteLine("標準エラー出力:");
                        Console.WriteLine(stdErr);
                        Console.WriteLine("----------------");
                    }
    
                    // 接続終了
                    ssh.Disconnect();
                }
                catch(Exception ex)
                {
                    // エラー発生時
                    Console.WriteLine(ex);
                    throw ex;
                }           
            }
        }
    }
    

    今回はサンプルとして、リモート側の時間を取得してコンソールに表示するサンプルを作ってみました。

  5. こんなことに使えます
    SSHをターミナル以外のWindowsクライアントから送信するという事自体が滅多にないことだとは思いますが
    Webクライアントが使えない環境や、サーバーへのSSHアクセスはさせたいがコマンドをテンプレート化して使用させるなどの制限をしたい場合などに使えるかもしれません。
    もちろん、このライブラリを使ってターミナルクライアントを作ることも可能だと思います。

MySQL接続のやりかた

  • 必要なもの
    • MySQLサーバーが起動していて接続が可能なリモート環境(物理マシンでもVMでもOK)
      • 記事内で使用している環境はLinux CentOS 6.9 / MySQL5.5.50(VirtualBox使用)
    • VisualStudio(どのエディションでも可)
      • 記事内で使用している開発環境はVisualStudio2017
  1. 新しいプロジェクトを作成または既存のプロジェクトを読み込む
    このサンプルではConnectMysqlというプロジェクトをコンソールプロジェクトで作成しました。
    既存のプロジェクトでテストしていただいても問題ありませんが、できれば新規作成をおすすめします。

  2. NuGetからMysql.Dataの追加
    図のように、ソリューションエクスプローラー内で1で作成したプロジェクト名の項目を右クリックし、NuGetパッケージの管理をクリックします。参照タブをクリックし、検索フォームに「Mysql」と入力し検索します。
    Oracle製のMysql.Dataをプロジェクトにインストールします。

  3. とりあえず接続してみる
    以下にサンプルコードを記載します。コードの説明はコメントに書いております。

    using System;
    using MySql.Data.MySqlClient;
    
    namespace ConnectMysql
    {
        class Program
        {
            static void Main(string[] args)
            {
                // 接続に必要なパラメータ文字列を生成する
                var connectionParams = string.Format(
                    "host={0}; userid={1}; password={2}; database={3}; charset={4}",
                    "192.168.xxx.xxx",
                    "taki",
                    "xxxxxxxxxxxx",
                    "sample_db",
                    "utf8"
                );
    
                // コネクターを作成
                var mysql = new MySqlConnection(connectionParams);
    
                try
                {
                    // 接続開始
                    mysql.Open();
                }
                catch (MySqlException ex)
                {
                    // 何らかの理由で接続に失敗した
                    Console.WriteLine(ex);
                    throw ex;
                }
            }
        }
    }
    

    リモートにインストールされたMySQLサーバーにアクセスする例になります。
    接続できない場合は、MySQLユーザーのアクセス権、ファイアウォールやポートフォワーディング設定など、設定に問題がないかを確認してみてください。

  4. insertとselectをしてみる
    using System;
    using MySql.Data.MySqlClient;
    
    namespace ConnectMysql
    {
        class Program
        {
            static void Main(string[] args)
            {
                // 接続に必要なパラメータ文字列を生成する
                var connectionParams = string.Format(
                    "host={0}; userid={1}; password={2}; database={3}; charset={4}",
                    "192.168.xxx.xxx",
                    "taki",
                    "xxxxxxxxxxx",
                    "sample_db",
                    "utf8"
                );
    
                // コネクターを作成
                var mysql = new MySqlConnection(connectionParams);
    
                try
                {
                    // 接続開始
                    mysql.Open();
                }
                catch (MySqlException ex)
                {
                    // 何らかの理由で接続に失敗した
                    Console.WriteLine(ex);
                    throw ex;
                }
    
                // サンプルではこういうテーブルにアクセスしている
                // CREATE TABLE shoplineup(ID INT AUTO_INCREMENT NOT NULL PRIMARY KEY, Name VARCHAR(16) NOT NULL, Price INT NOT NULL);
    
                // コマンド文字列用変数
                string cmd = "";
    
                // insertしてみる
                cmd = @"INSERT INTO shoplineup SET Name='Sword', Price=100;";
    
                // コマンドコンポーネント作成
                var insertCmd = new MySqlCommand(cmd, mysql);
    
                // 実行
                MySqlDataReader insertResult = insertCmd.ExecuteReader();
    
                // 変更を与えた件数を表示してみる
                Console.WriteLine("{0}row affected", insertResult.RecordsAffected);
    
                // 結果を閉じる
                insertResult.Close();
    
                // selectしてみる
                cmd = @"SELECT * FROM shoplineup;";
    
                
    
                // コマンドコンポーネント作成
                var selectCmd = new MySqlCommand(cmd, mysql);
    
                // 実行
                MySqlDataReader selectResult = selectCmd.ExecuteReader();
    
                // MySqlDataReaderというクラスに結果が入っている
                // Read()を呼ぶことで次の行にアクセスする
    
                while(selectResult.Read())
                {
                    var id = selectResult.GetInt32("ID");  // フィールド名でのアクセス
                    var name = selectResult.GetString(1);  // カラムインデックスでのアクセス
                    var price = selectResult.GetUInt32(2); // 同上
    
                    Console.WriteLine("ID:{0} Name:{1} Price:{2}", id, name, price);
                }
    
                // 結果を閉じる
                selectResult.Close();
    
                // Mysql接続終了
                mysql.Close();
            }
        }
    }
    

    今回はサンプルとして、sample_dbというデータベースにあるshoplineupテーブルに対してinsertおよびselectをする例を作ってみました。

  5. DataTableで欲しい場合

    var datatable = new DataTable();
    var adapter = new MySqlDataAdapter(query, mysql);
    adapter.Fill(datatable);
    return datatable;

    このようにして、クエリを実行する部分にMySqlDataAdapterを使用することで結果をDataTableで得ることも出来ます。

  6. こんなことに使えます
    MySQLターミナルクライアントを作ることも出来ますし、機能をテンプレート化してデータの集計用クライアントとしても使うことが出来ます。

まとめ

SSH接続及びMySQL接続をC#でやってみるというチャレンジでしたが、いかがでしたでしょうか?
パッケージのおかげで誰でも簡単に実装出来るので、アイディア次第ではとても便利なアプリを作ることが出来ると思います。
C#でSSH接続やMySQL接続をやってみたいという方のお役に立てれば幸いです。


syoshino

AssetBundleGraphTool を触ってみました


エンジニアの吉野です。

先日アップデートされたばかりの AssetBundleGraphTool v1.3 をこれまた先日リリースされた Unity 2017.1 に入れてみました。

AssetBundleGraphTool の bitbucket ページ
日本語マニュアル

今回はゲームで使用する Asset がどの AssetBundle に入っているかのテーブルデータをカスタムノードで作成したのでかいつまんで紹介しようと思います。

概要

ゲームで Asset を読むときに、開発中はローカルから、実機では AssetBundle を使いたいということが良くあります。
(Unite 2017 で紹介されていた Addressable Assets が入ればいらなくなるはずなので、それまでのつなぎで…)
これを満たすための仕様としては、おおむね以下の図のようになると思います。

asset load flow

この図の AssetBundleTable で使う AssetPath → AssetName 変換用のデータをカスタムノードで作ります。

完成したGraph図はこのようになります。

graph sample

カスタムノードの作り方は日本語のマニュアルの「カスタムノードを追加する」を見てください。
以下、作成したノードの要点を解説していこうと思います。

入力/出力するノードのタイプを指定する

今回は、「AssetBundle 名と AssetPath が設定されたノード」(Grouping)と 「AssetBundle をビルドするノード」(BundleBuilder)の間に入れたいので、以下のようにします。


    public override Model.NodeOutputSemantics NodeInputType
    {
        get
        {
            return Model.NodeOutputSemantics.AssetBundleConfigurations;
        }
    }

    public override Model.NodeOutputSemantics NodeOutputType
    {
        get
        {
            return Model.NodeOutputSemantics.AssetBundleConfigurations;
        }
    }

インスペクター上から AssetBundleTable を指定できるようにする

Inputされた情報を書き込む AssetBundleTable を渡せるようにします。
指定はエディタ拡張でおなじみの OnInspectorGUI(…) 部分に追加します。


public override void OnInspectorGUI(
    NodeGUI node,
    AssetReferenceStreamManager streamManager,
    NodeGUIEditor editor,
    Action onValueChanged)
{
...
    tableAsset = EditorGUILayout.ObjectField("AssetBundleTable Object", tableAsset, typeof(AssetBundleTable), false) as AssetBundleTable;
...
}

ビルド時に AssetBundleTable を更新する

Graph をビルドすると、カスタムノードの Build(…) が呼ばれます。この時に受け取った情報をTableに設定します。


public override void Build(
    BuildTarget target,
    Model.NodeData node,
    IEnumerable<PerformGraph.AssetGroups> incoming,
    IEnumerable<Model.ConnectionData> connectionsToOutput,
    PerformGraph.Output Output,
    Action<Model.NodeData, string, float> progressFunc)
{
...
    tableAsset.Clear();
    foreach (var ag in incoming)
     {
         foreach (var assetGroup in ag.assetGroups)
         {
             foreach (var inputAsset in assetGroup.Value)
             {
                 tableAsset.Add(new AssetBundleLoadPath(assetGroup.Key, inputAsset));
             }
         }
     }
     EditorUtility.SetDirty(tableAsset);
     AssetDatabase.SaveAssets();
...
}

AssetBundleTable 自身も AssetBundle にする

実機で使うので、AssetBundleTable も忘れずに AssetBundle にします。
幸いこの後に AssetBundle をビルドするノードがあるので、Output に追加して流します。


public override void Build(
    BuildTarget target,
    Model.NodeData node,
    IEnumerable<PerformGraph.AssetGroups> incoming,
    IEnumerable<Model.ConnectionData> connectionsToOutput,
    PerformGraph.Output Output,
    Action<Model.NodeData, string, float> progressFunc)
{
...
    EditorUtility.SetDirty(tableAsset);
    AssetDatabase.SaveAssets();

    if (Output != null)
    {
        var dst = (connectionsToOutput == null || !connectionsToOutput.Any()) ?
            null : connectionsToOutput.First();

        // 上流のノードから流れてきたものをそのまま渡します。
        foreach (var ag in incoming)
        {
            Output(dst, ag.assetGroups);
        }

        // AssetBundleTableを追加して渡します。
        var additionalOutput = new Dictionary<string, List<AssetReference>>();
        additionalOutput.Add(settings.tableAssetBundleName,
            new List<AssetReference> { 
                AssetReference.CreateReference(AssetDatabase.GetAssetPath(tableAsset), typeof(AssetBundleTableListData))
            }
        );
        Output(dst, additionalOutput);
    }
}

これで、作成した Graph がビルドされるたびに AssetBundleTable が更新されて、AssetBundle にまでなってくれます。
日本語のわかりやすいマニュアルがあるおかげで、すんなりと実装できてしまいました。

使ってみた感想

ノードの拡張機能があるおかげで Asset を読んで加工してなにかする、という機能をグラフィカルかつ低コストで作成できる素晴らしいツールだと思います。
カスタムノード以外にもいろいろな機能があるのでぜひマニュアルを見てみてください。
いろいろ応用できそうなので今後も活用していこうと思います!


rastam

英語圏のオススメ Ruby・Rails 書籍


初めまして!エンジニアのラスタムです。

英語圏ウェブ開発業界で独自の文化を作り出した Ruby と Rails。
そのコミュニティーに大きな影響を与えた書籍が沢山あります。
日本でも話題になり、和訳されているものもあれば、訳されていないものも未だに山ほどあります。
代表的な作品を紹介させていただきます。

初心者向け

Programming Ruby

Ruby 言語を初めて英語圏に普及させた超大作。
表紙のつるはしにちなんだ、「The Pickaxe」という愛称がつくほどの名声を博しています。
Ruby の言語的仕組みに興味がある方にオススメ。

★ 和訳あり!

The Ruby on Rails Tutorial

Ruby・Rails コア機能だけでなく、git や bundler など、現代ウェブ開発でよく使うツールを総合的に紹介する入門書。
他言語のウェブ開発を既に経験しているエンジニアには特にオススメ!

★ 和訳あり!

Why’s Poignant Guide to Ruby

不思議なプログラマー兼イラストレーター why the lucky stiff 氏が執筆した青年向け入門書。
3 コマ漫画を添えた、独特でユーモア溢れる文章が、Ruby エンジニアの間で人気を集め、流行語さえ生み出しました。
2009 年に why the lucky stiff 氏は、なぜか Ruby コミュニティーから脱退しましたが、今も愛され続けているこの作品は、ボランティアにより維持され続けています。

★ 和訳あり!

中上級者向け

Rails Antipatterns

Thoughtbot 社初期刊行物のひとつで、個人的に最もお気に入りの一冊。
Model、View、Controller の各レイヤーでよく悪用されるアンチパターンとその解決案を読みやすく紹介しています。
Rails 3 以来は改訂されていないため、時代遅れになってしまいましたが、スパゲッティーが美しくてシンプルなコードへと変わっていくリファクター談を楽しむだけでも十分読む価値があると思います。

Practical Object-Oriented Design in Ruby

通称 POODR(púdər、プードル)で知られている名作で、執筆者 Sandi Metz 氏が 2013 年 Ruby Hero 賞を受賞するほどの盛名を馳せました。
カプセル化やポリモーフィズムといったオブジェクト指向を具体的な例を添えて紹介している、経験者向けの技術書。

★ 和訳あり!

99 Bottles of OOP

Sandi Metz 待望の最新作。
そして共同執筆者として参画しているのはなんと、感動作カンファレンストーク Therapeutic Refactoring と、レビュー駆動多言語学習ツール exercism.io 生みの親 Katrina Owen 氏。
99 Bottles of Beer 民謡生成アルゴリズムを題材に、実装・リファクター案をいくつか取り上げ、それぞれのメリット・デメリットを丁寧に説明しています。


PadrinoからRailsに移行しました


こんにちは、ソフトウェアエンジニアの藤井です。

今回は弊社で開発・運営を行っている 幻塔戦記グリフォン のWebAPIサーバーで使用していたPadrinoアプリケーションをRailsに移行した話を紹介します。

以下はチームのメンバーが発表したスライドです。

 

続きを読む


GAME CREATORS CONFERENCE’16 登壇させて頂きました

リンク


エンジニアの山藤です。

2016年3月5日に開催されたGAME CREATORS CONFERENCE’16で登壇させて頂きました。当日は朝一番のセッションでしたが聴講して下さった皆様、懇親会でお声掛け下さった方々もありがとうございました、運営スタッフの皆様もお疲れ様でした!

当日使用した資料です。

既にCEDECの資料でも公開してますが、ゲームサーバは複数のプロセスから構成されていて、このプロセス数もゲーム内のコンテンツ追加に伴って増えたりします。開発中から将来の拡張をある程度見越した設計・実装を行い、後は運用に合わせて柔軟な対応が必要になったりします。

オンラインゲームでは日々ゲームを取り巻く色々な状況も変わってくるので、継続的なソフトウェアのチューニングもですが、システム、ゲームバランスの改善、それらに合わせたハードウェア構成の変更・運用が必要になってきます。

リリースした後も、こうやって一つのタイトルをお客様と一緒に育てていくのもオンラインゲームの醍醐味だと思って日々頑張らせて頂いております。これからもよろしくお願いします!


ttsuchie

C++14プロジェクトでのType Erasureの活用について発表しました


こんにちは、エンジニアの土江です。

今回の開発者ブログでは、プロジェクト内の勉強会で発表した、C++のType Erasureという手法について紹介します。

併せて、Boost.TypeErasureのコンセプトを元にGoogle Mockのモックを自動生成するライブラリを開発中です。今回はそのライブラリの開発中のコードを掲載します。ライブラリのイメージとしては、C#のNSubstituteのようなものです。

 

所属しているプロジェクトについて

本題の前に、現在所属しているプロジェクトの文化と開発体制の紹介をします。

今所属しているプロジェクトでは隔週で勉強会をする文化があり、技術をチーム内で共有する場としています。

プロジェクトではC++14とBoostライブラリを活用したサーバーの開発をしています。その中でType Erasureという手法を使ってコードの見通しを良くしています。

この記事では、Type Erasureという手法について、チーム内勉強会でチームに共有した内容を紹介します。

 

Type Erasureとは

C++におけるType Erasureとは、テンプレートや継承では難しい、動的で柔軟な多態性を実現するための手法です。

標準ライブラリ内でも使用されている手法であり、少々複雑ですが非常に便利なテクニックです。

今回の発表の内容は、Type Erasureとは何か、Boost.TypeErasureライブラリの使い方、更に便利にType Erasureを利用するために、といったものとなっています。

 

Boost.Type Erasureの活用

スライド中に出てきた、Boost.TypeErasureのコンセプトからgmockのモックを自動生成するライブラリを現在開発中です。

基本となるコードを以下のgistに掲載します。

https://gist.github.com/ttsuchie/efd5dc4f200efd0b3231

まだまだ開発中ですが、基本機能はすでに整っています。

終わりに

今回の開発者ブログでは、C++のテクニックを紹介しました。

AimingにはC++を採用しているプロジェクトがいくつかあり、BoostやC++11/14の新機能を取り入れた開発を行っています。


『剣と魔法のログレス(PCブラウザ版)』の運用について


はじめまして。Aimingエンジニアの角谷です。
「剣と魔法のログレス(PCブラウザ版)」(以下ログレス)でサーバー・クライアントの
開発に携わっております。

今回は社内勉強会で話した内容を紹介したいと思います。
テーマは社内からのプロジェクトに対する質問のいくつかを取り上げたものになりました。

勉強会テーマ

  • ゲーム紹介
  • 過去に起きた問題で一番大変だった問題とどのような対応をしたか
  • これまでで一番大変だった事とどのように乗り越えたか
  • 産まれてしまったと思う技術的負債について。それについての失敗談や内容について
  • プロジェクトでエンジニアからみて他のプロジェクトに自慢したいこと

勉強会について

弊社では、運用(開発)をしているプロジェクトを対象に、定期的にテーマに沿った
勉強会を行っています。この時は新人向け、他プロジェクトに興味のある人向けに
行ったものになりました。

開発1.5年、運営4年+

Aimingで開発している中では相当なレガシーな運用と開発を行っていると思います。
(データ管理が Git でなくsvn など)
しかし、だからこその開発のシンプルさと親和性があると思いたいですね。
成果物の一部はスマホ版にも引き継がれています。

Flash開発

レガシーな開発として筆頭なのが Flash を利用している事があるでしょうか。
ブラウザとの相性も悪くなく、Adobe製品との組み合わせもやり易いものでした。
ブラウザによる動作違いも当時は少ないものでした。(PPAPI導入後はその範疇ではない)
MXML(Slide参照)によるUI作成機能は、これまでの開発の中ではやり易いものでした。

今後

プロジェクト運用中で得た経験や事実は、本だけでは得られないものです。
長期運営しているからこそ出来ること・起きたモノの解消をログレスは目指します。
まだまだ国内開発で頑張っておりますので、これからもよろしくお願いします。


「関西ゲーム勉強会 2015冬」に参加させて頂きました!


初めまして、Aimingエンジニアの山藤です。

先日、11月28日に大阪で行われた「関西ゲーム勉強会 2015冬」で発表させて頂きました、当日はかなりの盛況でした。勉強会運営の皆様、参加者の皆様、ありがとうございました。

内容は「ログレスの戦闘からみるデータ同期」という事で、「剣と魔法のログレス〜いにしえの女神〜」の戦闘コンテンツでのゲームサーバ・クライアントのやりとりや、通信遅延をフォローする実装例についてです。

 

ログレスでは、クライアントがサーバからの返答を待ってから動かす、クライアント間の厳密な同期が必要になる戦闘コンテンツや、返答を待たずに動作を開始するマップ移動等、ユーザー様が不快感の少ない操作ができ、ゲームが破綻しない様に、コンテンツ毎に同期の取り方を微妙に変えていたりします。

また機会があれば他のコンテンツの実装例も紹介させて頂きます。

丁度この記事を書かせて頂いてる間に、12月4日台湾のAppleStoreで台湾版のログレスがセールストップ取りました。これからも応援よろしくお願いします。


hirooka

C# + MySQL + Dapper で軽量 O/R Mapper


エンジニアの廣岡です。

最近は仕事で C# を用いたサーバを書いていますが、C# で O/R Mapper (ORM) を使ったことがなくて「とりあえず動く軽量なものが欲しい」と思って見つけ出した Dapper という Micro-ORM を紹介していきます。

Dapper: https://github.com/StackExchange/dapper-dot-net

ところで Micro-ORM とは、ORM の機能のうちいくつかの機能が無いものを指すようですが、「SQL 文を作るクエリビルダの機能」がないものを指すことが多いようです。

C# + MySQL + Dapper

C# から MySQL に接続するときは、ADO.NET インターフェイスを持った MySQL 公式の Connector/Net を使うのが無難でしょう。
http://dev.mysql.com/downloads/connector/net/
これを使えば基本的な MySQL への接続・SQL 実行などはできるようになるのですが、IDataReader や DataSet といったややローレベルなインターフェイスなのでちょっと使いにくいです。
そこで ORM が登場するわけですが、Entity Framework のようなリッチな ORM の導入がちょっとめんどくさかった(というか勉強不足だった)ので、超軽量な ORM は無いのかな〜と思って探していたら Dapper という Micro-ORM を見つけました。
これがどう軽量なのかというと

  • ライブラリは SqlMapper.cs ファイルだけ(数千行ありますが)
  • 基本的には ADO.NET の IDBConnection に拡張メソッドを追加しているだけ

というもので、基本は ADO.NET として使えばよくて、SQL クエリの結果をクラスや構造体にマッピングしたい時に Dapper による拡張メソッドを呼び出すというものです。
クエリビルダは無くデータマッピングしかしないのですが、今すぐ欲しい物としては十分な機能だったので採用しました。

サンプルコード

Dapper の IDbConnection.Query を使って DB 上の Users テーブルから User クラスの一覧を取得するコード

class User
{
    public int Age { get; set; }
    public string Name { get; set; }
}

using (var connection = new MySQLConnection("設定"))
{
    connection.Open();
    var users = connection.Query<User>("SELECT * FROM Users"); // この行だけ Dapper
    foreach (var user in users)
    {
        Console.WriteLine(user.Name);
    }
}

Dapper の IDbConnection.Execute を使って DB 上の Users テーブルにレコードを挿入するコード
(あまり ORM の恩恵がないケース)

using (var connection = new MySQLConnection("設定"))
{
    connection.Open();
    var numOfAffected = connection.Execute("INSERT INTO Users (Age, Name) VALUES (23, Alice)");
}

Dapper で満足?

Dapper は急ぎで欲しかった機能を提供してくれました。ただその後、クエリビルダがないことによって恥ずかしながらテーブル名をタイポしてしまい、気づきにくいバグを作ってしまいました。
この種のバグを二度と作らないためにも Dapper 用にクエリビルダを導入するか、Linq to Entities のようなフル ORM に乗り換えるか、検討をはじめています。

Dapper は ADO.NET をもう少し便利に使いたいなーというシーンには使いどころもありますが、ゲームのサーバ側の MySQL 用に使うには機能不足感があると思います。

Unity 上で SQLite を扱うときなんかにはこの軽量さが発揮されそうですね。


kanbe

CEDEC2014で「剣と魔法のログレス いにしえの女神」について講演しました


こんにちは。クライアントエンジニアの神部です。 9月2日(火)にCEDEC2014にて「剣と魔法のログレス いにしえの女神 〜スマホ時代の MMORPG を支える技術」というセッションタイトルで講演を行いました。

OLYMPUS DIGITAL CAMERA

セッションで使用したスライドを公開しましたのでお知らせいたします。


内容としてはMMORPGスマホアプリである「剣と魔法のログレス いにしえの女神」の開発について、開発環境の説明、クライアントサイドはCocos2d-xの使用について、クライアント/サーバ間のリアルタイム通信についての話、またサーバサイドはサーバ構成の説明からチャットシステムのデータフローなどとなっております。

セッション自体の満足度いかんは別として、立ち見が出るほどの盛況なセッションとなり、足を運んでいただいた皆様には感謝を申し上げます。
セッション後に質問があった以下については、また別の機会の勉強会などで掘り下げて話をさせていただくかもしれません。

  • クライアントのパッチシステムについて
  • Cocos2d-x上で動作するFlashアニメーションプレイヤについて
  • 回線切断時などからのゲームへの素早い復帰について(インゲームの表示で”リロードします”の処理)

今回のセッションを聴いていただいた皆様に、スマホアプリでMMORPGが動作していることとそのMMORPGがどのように作られているかが少しでも伝わったなら幸いです。

最後に、講演の機会をくださいましたCEDEC2014 運営事務局の皆様、ありがとうございました!