バイナリログ転送ツール bingo

前回のエントリから相当間が空いてしまいました。
ネタがあればちゃんと書いていきたい所です。

で、タイトルの通り bingo というアプリケーションをリリースしました。

何をするアプリケーションかと言いますと、
MySQLのバイナリログ(更新差分)を簡単にリアルタイム転送するツール、です。

MySQL to MySQLであればMySQLのレプリケーションでいいじゃん、となるのですが
例えばBigQuery等にリアルタイムでデータ転送しようと思うと結構面倒くさいと思います。
(トリガーを使ったり、短時間毎に差分を取得したり。。)

bingo はMySQLのレプリケーション技術で更新データを読み取りますので、
非同期ではありますが、ほぼリアルタイムに更新データを取得できます。
現状はfluentdにhttp postする事しか出来ませんが、色々と応用できるとは思います。

使い方(詳しくはREADMEへ)

bingoは起動するとすぐにMySQLに接続します。

$ bingo
2016/09/02 01:19:10 connected to mysql(root@127.0.0.1:3306)
2016/09/02 01:19:10 start reading binlog
2016/09/02 01:19:10     Binlog Version:  4
2016/09/02 01:19:10     Server Version:  5.7.14-log

MySQLに何かしらデータを書き込みます。

$ mysql -u root -e 'create database testdb default character set utf8mb4'
$ mysql -u root -e 'create table testdb.testtable (id bigint, name varchar(32))'
$ mysql -u root -e 'insert into testdb.testtable (id, name) values (1, "hello world")'
$ mysql -u root -e 'insert into testdb.testtable (id, name) values (2, "はろーわーるど")'

localhost:8888 で待ち受けるfluentdにはこんな感じでデータがpostされます。

2016-09-02 01:23:28 -0400 bingo.data: {"database":"testdb","table":"testtable","columns":["1","hello world"]}
2016-09-02 01:23:36 -0400 bingo.data: {"database":"testdb","table":"testtable","columns":["2","はろーわーるど"]}

余力があればカラム名つきでpostするような実装を入れたいところですが、ひとまずはここまで。
テストも途中からほとんど書けていないのでこれもIssueですね。。

タッチイベント内でのRenderTextureの不具合

TiledMapを使わずにSpriteを敷き詰める場合、
描画高速化と継ぎ目を目立たなくする対策としてRenderTextureを使用しているのですが、
以下の条件を満たしているとオフスクリーンの一部の描画がされないケースがありました。

  • 一定サイズより大きいRenderTextureを作成する
  • タッチイベント内で描画を行う

理屈はよくわかりませんが、再現したのでコードをメモしておきます。

  • プロジェクト作成
cocos new cocos-test -p com.test -l cpp
  • HelloWorld に onEnter を追加します。
class HelloWorld : public cocos2d::Layer
{
public:
...
+    void onEnter();
...
}
  • HelloWorld.cpp に下記のコードを追記します。
#define SIZE 64

void HelloWorld::onEnter() {
	Layer::onEnter();
	
	testRenderTextureBug();
	
	auto listener = EventListenerTouchOneByOne::create();
	listener->onTouchBegan = [=](Touch*, Event*){
		testRenderTextureBug();
		auto delay = DelayTime::create(2);
		auto call = CallFunc::create([=]{
			this->testRenderTextureBug();
		});
		runAction(Sequence::create(delay, call, NULL));
		return true;
	};
	getEventDispatcher()->addEventListenerWithSceneGraphPriority(listener, this);
}

void HelloWorld::testRenderTextureBug() {
	
	auto tex = RenderTexture::create(SIZE*10, SIZE*10);
	tex->setPosition(SIZE*2, SIZE*5);
	tex->beginWithClear(0.3, 0.3, 0.3, 1);
	for (int i = 0; i < 10; i++) {
		for (int j = 0; j < 10; j++) {
			auto color = Color3B(0, i*16, j*16);
			auto s = Sprite::create();
			s->setTextureRect(Rect(0, 0, SIZE, SIZE));
			s->setColor(color);
			s->getTexture()->setAliasTexParameters();
			s->setPosition(j*SIZE, i*SIZE);
			s->visit();
		}
	}
	tex->end();
	addChild(tex);
}

testRenderTextureBug() を呼ぶ度にRenderTextureを生成して重ねます。(下のRenderTextureは見えなくなる)
実行し、画面タッチするとバグの再現を確認できます。

  • onEnterで生成すると、正常なRenderTextureが表示される

rt_test1

  • 画面タッチイベント内で生成すると、一部欠けたRenderTextureが表示される

rt_test2

  • 画面タッチ後のCallFunc内で生成すると、正常なRenderTextureが表示される

rt_test3

この通り、対策としてはCallFuncで生成する事でしょうか。
根が深そうなのでソースコードまでは追っていません。

Asepriteのマウスポインタを固定する

個人的にドット絵を描くならAsepriteが一番使いやすいと思っているのですが、
マウスポインタがよく消えたり見えにくかったりするのが難点だと感じていました。
つい最近ソースが公開されている事を知ったので、ソースに手をいれて強制的に固定するようにしてみました。

まずは普通にcloneして最新のv1.1.1のタグに切り替えます

git clone --recursive https://github.com/aseprite/aseprite.git
cd aseprite
git checkout v1.1.1

そして、下記の2ファイルを変更します

  • src/ui/system.cpp 109行目付近
       case ui::kSizeWCursor: nativeCursor = she::kSizeWCursor; break;
       case ui::kSizeNWCursor: nativeCursor = she::kSizeNWCursor; break;
     }
+    nativeCursor = she::kArrowCursor;
   }
  • src/ui/window.cpp 411行目付近
           case HitTestBorderS: cursor = kSizeSCursor; break;
         }

+        cursor = kArrowCursor;
         set_mouse_cursor(cursor);
         return true;
       }

これでasepriteをビルドします。(INSTALL.mdをご参考ください)
Visual Studioの開発者用コマンドプロンプトから、下記の通り実行します。

mkdir build
cd build
cmake .. -G "NMake Makefiles"
nmake aseprite

Visual Studio 2015 Community (Update 1) をインストールした Windows 10 でビルド成功を確認しました。
成果物として build/bin/aseprite.exe が出力されるので、これを起動して動作確認します。
メニューバーに “WARNING” と表示される場合は、設定ファイルとバイナリのバージョン差異があるため、src/config.h の25行目付近を書き換えます。

 // General information
 #define PACKAGE "Aseprite"
-#define VERSION "1.1.1-dev"
+#define VERSION "1.1.1"

 #ifdef CUSTOM_WEBSITE_URL
 #define WEBSITE                 CUSTOM_WEBSITE_URL // To test web server

マウスポインタの切り替えがなくなり、見えなくなる事もなくなってより使いやすくなった気がします。
ただ、見ての通り無理矢理固定するので、ドット絵なマウスポインタと切り替えたりはできなくなります。
また、拡大縮小ツールを使った時や、アプリ内ウィンドウの端っこをつかむときもカーソルが変化しなくなる為、部分的には使いにくくなります。
それでも個人的にはこの方が作業ストレスが減るので、しばらくこのまま使ってみようと思います。

そして cocos2d-x に帰ってくる

あれから結局 CocosSharp はまだ早いと断念し、本家 cocos2d-x に乗り換えました。
さすがに本家では基本的な機能で問題が出ることはなかったのですが、
いくつかの問題があったのでまとめて起きます。

ClippingNode::setStencil で SIGABRT になる

また ClippingNode かい、という感じです。
ちゃんと原因がわかると納得なんですが、同様のタイミングでエラーになった方を
検索してもあまり見かけなかったので、参考までに。

再現させるコードを簡潔に書くと、こんな感じ。

auto clip = ClippingNode::create();
addChild(clip);

auto mask = DrawNode::create();
clip->setStencil(mask);

auto move = MoveBy::create(...);
auto callfunc = CallFunc::create([clip]{
	auto newMask = DrawNode::create();
	clip->setStencil(newMask);
});
clip->runAction(Sequence::create(move, callfunc, NULL));

まず、マスク用のイメージは DrawNode を使い、動的に生成します。
その後、stencil を書き換える CallFunc を実行します。
すると以下のエラーメッセージを出力する CCASSERT に引っかかります。

Node still marked as running on node destruction! Was base class onExit() called in derived class onExit() implementations?

setStencil の中で、古い stencil に対して CC_SAFE_RELEASE が実行され、
参照カウンタが 0 になった場合はそのままデストラクタが呼ばれるという流れになるのですが、
stencil ノードが表示中の為、ASSERT に引っかかってしまうという事の用です。

最終的に DrawNode のインスタンスを保持しておいて、マスク用のイメージを書き換える事で回避しました。

auto clip = ClippingNode::create();
addChild(clip);

auto mask = DrawNode::create();
clip->setStencil(mask);

auto move = MoveBy::create(...);
auto callfunc = CallFunc::create([clip, mask]{
	// 既存のDrawNodeを書き換える
	mask->clear();
	mask->drawPolygon(...)
});
clip->runAction(Sequence::create(move, callfunc, NULL));

基本的な事なのかもしれませんが、
addChildやremoveChildでは SIGABRT になる事はなかったので少し面食らいました。
ちなみにアクション中に限らず、onTouchBegan 中に同様に setStencil すると SIGABRT が起きます。
ClippingNode のステンシルノードは入れ替えたりしない想定で使いましょう、という事で。

RemoveSelfの後のアクションが実行されない事がある

これも基本的な事っぽいのですが、
以下のコードだと “action completed.” はログ出力されません。

auto move = MoveBy::create(...);
auto remove = RemoveSelf::create();
auto delay = DelayTime::create(1);
auto cb = CallFunc::create([=]{
	log("action completed.");
});
node->runAction(Sequence::create(move, remove, delay, cb, NULL));

これは、RemoveSelf の実行後、nodeがどこからも参照されていなければ一定のタイミングで
メモリ上から解放され、その後のアクションは実行されないからです。
間に DelayTime を挟んでいなければ実行されるとは思いますが、あまり信用しない方がいい気がします。

画面上から Node を削除した後 CallFunc を実行した場合は、下記の様に記述するのが正解のようです。

auto move = MoveBy::create(...);
auto hide = Hide::create();
auto delay = DelayTime::create(1);
auto cb = CallFunc::create([=]{
	log("action completed.");
});
auto remove = RemoveSelf::create();
node->runAction(Sequence::create(move, hide, delay, cb, remove, NULL));

cocos2d-xでの本格的な開発は経験がないので、
オープンソースのコードを参考にさせてもらいたいのですが、
ゲームはあまりオープンソースでコードを公開されるケースが少ないですね。
特許だったりチートされる可能性だったり色々あるので仕方ないのですが・・・

またネタが溜まったら書きます。

CocosSharpでClippingNodeでクリッピングされない問題

またまた問題にぶつかりましたよっと・・・
クリッピングしたいのに一切クリッピングされずに表示されてしまう。
むしろStencil(マスク用のSprite)がそのまま表示されているという謎の挙動。

昔からある既知の不具合の一つなんですかね。

Issueには上がっているものの修正されてはいないようです。
GameDrawPrimitives のアルファ処理がiOSやMacでは動作しないといった原因のようなのですが、Androidの実機でも同様の症状が出ました。

CocosSharpはまだちょっと早いかもしれませんね・・・

CocosSharpで独自フォントが表示されない問題

以前取得した Xamarin のライセンスを有効活用しようと、
Xamarin Studio と CocosSharp を使ってアプリを作ってみています。
1.7.0 はまだpre1バージョンだったので、あえて 1.6.2.0 にダウングレードして使い始めました。
(Official NuGet Gallery から取得できる CocosSharp PCL)

とりあえず各グラフィックス機能の使い方を確認しようと、
SpriteだったりLabelだったりをテストしていたところ、いきなり独自フォントが表示されない問題に躓きました。

var label = new CCLabel("hello world", "myfont.ttf", 30, CCLabelFormat.SystemFont);
AddChild(label);

たったこれだけのコード。。
参考にした CoinTime というサンプルプロジェクトを参考に、色々試して見るもうまくいかない。
https://developer.xamarin.com/samples/mobile/CoinTime/

何でだろうとさらに調査したところ、なんと普通に不具合だそうです。

1.7.0.0 pre2 でようやく修正されるって・・・どれだけ使われてないの・・・

自力でパッチを当てようと試みた結果

幸い CocosSharp はオープンソースプロダクトなので、自力でパッチを当てることが可能です。
(ただしうまくいくとは言ってない)

README を読みながらソースを git clone し、 git submodule update を実行。
Xamarin Studioでソリューションファイルを開く前に、下記のおまじないを実行します。

mono MonoGame/Protobuild.exe default.build

そして、CocosSharp.iOS.sln を開いてバグを修正し、プロジェクトをビルド。
できあがった CocosSharp.dll を元々あった CocosSharp.dll と差し替えて実行すると、先のコードのままで独自フォントが利用できるようになりました。

いきなりパッチ当てる必要が出てくるとは、幸先悪いなーという感じですが、とりあえずこれで先に進めそう。
Android版のdllも同じようにビルドして配置したところエラーが出たけど無視。
Android版なんてなかった。

KATANA01にUWPアプリをインストールしてみた

KATANA01買いました に続いて、
とりあえず自作アプリをインストールしてみるところまでやってみました。

USB接続でデバッグ実行する

KATANA01をUSB接続した状態で Visual Studio 2015 を起動して、Universal アプリのプロジェクトを作成、
MainPageに適当にテキストを配置してビルド。ビルドターゲットは ARM + Device にします。

vs_ss_hello

そしてデバッグ実行。

wp_ss_app1

動いたー。
これは簡単に動作しました。

色々調べていると、どうやらWiFi経由でデバッグ実行できるらしい。
ということを知ってしまったのが運の尽き。
ここからすごい勢いでドハマりするのであった。。
本当の地獄はここからだ!

WiFi経由でリモートデバッグする

USB接続を解除して、ビルドターゲットを ARM + リモートコンピューター (Remote Machine) にします。

vs_ss_target_remote

接続先を設定するダイアログが表示されます。
すると、自分のKATANA01が表示されているので選択し、デバッグ実行します。

vs_ss_remote_dialog

PINを要求されるので、デバイスポータルでログインしたときと同じように端末でPINを発行して入力。
が・・・ダメ・・・!

vs_ss_wp_pinerror

何度やってもWindowsやVisual Studio、端末を再起動してもダメ。
ペアリングをすべて解除してもダメ。
Windows 10 も最新の build 10586 にアップデートしてもダメ。
Visual Studio は Update 1。

ネットを検索してみると、同じような症状でハマっている人が多数。

レジストリをいじってみましたが、DEP6100, DEP6200が消えただけでデプロイに失敗するのは変わらず。
今のところWiFi経由でリモートデバッグは出来ていません。
もう少しこなれてからなんですかねー。

AWS Lambda Scheduled Eventを試してみる

個人的に心待ちにしていた機能、Scheduled Eventが発表されました。
すでにリリースされているそうなので早速試してみました。

とりあえずサンプルとなるLambda functionを作成。
このサイトへのHTTPリクエストを発行するコードとなっています。

サンプルLambda

そして、Event sources に Scheduled Eventを追加。
とりあえずcronフォーマットで5分毎に実行する形で登録してみます。

lambda_schedule_1

このサイトのアクセスログを関してみたところ、ちゃんと5分ごとにアクセスが来ていることが確認できました。
(こちらのIPアドレスをwhoisすると、ちゃんとamazonからのものでした)

lambda_access

しかし Lambda はすばらしいですね。

WinObjC雑感

公開から少し間が空いてしまいましたが、
WinObjCについて触れてみたので、感想を書いておこうと思います。

WinObjCとは

Microsoftが公開している、Windows上でObjective-C(iOS)のソースコードをコンパイルできるようにする、ブリッジモジュールの事です。

https://github.com/Microsoft/WinObjC/

今のところプレビュー版で、ARM向けのバイナリが生成できないなど、色々と制約はあります。

サンプルの動かし型

前述のgithubからSDK一式zipをダウンロードして展開し、
VisualStudio2015でソリューションファイルを開き、ビルドして実行するだけです。動かすだけなら超簡単だった。

WinObjC_Sample

ちゃんと.mファイルを修正すると、その通りバイナリにも反映されています。

WinObjC_Sample2

そして、Objective-Cのコードにバグを仕込むと、コンパイル時にちゃんと検出してくれています。

WinObjC_Errors

イケてるところ

  • Windows上でiOSアプリの動作確認ができる。(これが最重要ですねw)
  • VisualStudioで設定したブレークポイントでちゃんとブレークできる。
  • C++なんかと同様、ブレーク時点でのイミディエイトウィンドウで変数の値を確認できる。

微妙なところ

  • UIコンポーネントのサンプルもあったけれど、iOSとはデザインが微妙に異なるため、見た目の確認には厳しいかも。
  • Objective-Cのコードに合わせてエディタがハイライトされない。
  • IntelliSenseやフォーマッター、関数・クラス定義へのジャンプもできない。
  • 動的なシンタックスエラーチェックなどもできない。

まだプレビュー版なので、正式版に期待といったところでしょうか。
VisualStudioで開発できるようにするからには、IDEのサポートも期待したいところです。
ちなみに、サンプルの範囲で確認しただけなので、ライブラリの利用は試していません。

amazon-ecsで当月発売の商品を取得する

で、Powerサーチの使い方がいまいち分かりにくかったのでメモメモ。

opts = {
  browse_node: "nodeid",
  response_group: "Large",
  country: "jp",
  power: "pubdate:during 09-2015"
}
response = Amazon::Ecs.item_search(keywords, opts)

pubdate:duringの後に当月を指定します。
pubdate:beforeにすると、前月以前になり、pubdate:afterにすると、翌月以降発売の商品が取得できます。

Powerサーチを使いこなせれば色々できそう。

前回の記事から二週間も空いてしまった、、時間見つけて書かないとなぁ。