並ゲームプログラマの書き捨て場

ゲームプログラマやってる私の日記、雑記、備忘録、公開場所

ゲーム開発者よ、Coinhiveで稼げ

金はすべてに勝る。djannです。



Coinhiveライクなシステムをゲームに仕込み、その上で無料で提供する収益化手法を世のゲーム開発者に提案したい。
あなたのゲームを好きになり、長時間遊んでくれればくれるほど、あなたには収益が入る。



さて、近頃IT業界が賑やかだ。
技術的に程度の低いサイバー警察へ強制捜査を受けたというような話があったり話(勘違いで簡単に人権侵害が行われるのは危険だ)

it-giron.com

そしてあのうっとうしい広告に代わる収益化手法として産まれたCoinhive(コインハイブ)を用いたWebページの提供者が、合同捜査本部まで立てられ一斉検挙されたという。

doocts.com

サイバー警察の能力向上を願う、またCoinhiveのような新たな技術や発想をどうか潰して萎縮させてくれるなという陳情書や裁判は、是非とも光明を見出だしてほしいし、応援したい。

it-giron.com

しかして、結果が出るまで現状のままうっとうしい広告を出し続ける、はたまた収益化を諦めるというのは発展への機会損失となり非常に痛い。

では、今でもそういった、演算資源を間借りしたマイニングを行うのに適した環境は無いものだろうか?



思うに、Webページだけでなくゲームも多くの無料の作品が世に提供されており、広告による収益化を望むといった、似た土壌が形成されている。

Webページとの違いとして、ゲームの場合はそのコンテンツを享受する前には企業ロゴや各種警告などが表示されることは一般的だ。そこに「マイニングを行う」と一文を入れることが出来る。これならばCoinhiveの争点となっている「ユーザの意図しない動作」にはならないだろう。

警告だけで不十分というのならば、インストール時に許諾要項を出し、合意を取ることも出来る。


また、ゲームは多くの演算資源を使うことが事前に予測されており、しかしVsyncや各オブジェクトの同期などで完全に100%の演算資源を使わず待ちを行う場面も多い。

一番は、ゲームはある程度の時間継続して起動し続けてくれるだろう。ちょっとした内容を読んだら他へ移動するWebページより、より収益が見込めるはずだ(誤差レベルだろうが)。

そして、少なくともWebページよりは悪意の改竄による影響範囲が狭まると思われるので、ターゲットにもされにくく少なくとも意図しない悪意へ収益を与えてしまう嫌悪感も軽減できるのではないだろうか?


以上の理由が、Coinhiveライクなシステムをゲームに仕込み、その上で無料で提供する収益化手法を世のゲーム開発者に提案する根拠だ。
繰り返しになるが、あなたのゲームを好きになり、長時間遊んでくれればくれるほど、あなたには収益が入る。

ユーザにとっても、ユーザが遊べば遊ぶほど収益になるという考えが一般的になれば、即時的にガチャでお金にするのではなく、より面白いものを作ることに開発者が力を注いでくれるようになるだろう。

これはとてもよいことだ。開発者はより面白くすればするほど稼ぐことができ、ユーザは札束で叩くゲームから解放される。ちょっとした演算資源の間借りさえ許せば、そんなゲームのユートピアが我々を待っている。

そして、ゲームによる演算資源を間借りしたマイニングが広く行われるようになれば、Webページでもマイニングが行われることが「社会的なコンセンサスが取られていない」ということにはならなくなるかもしれない。

汝、自らを救うべからず

私は誰からも救われない・・・djannです。


さて、突然だが自力救済(じりょくきゅうさい)という言葉をご存知だろうか?
言葉の通り、自らの力で救うという意味であり、例を挙げるとするならば

「あいつに貸したゲームが返ってこない。何度も何度も持って来いと言ったにも関わらずあいつは持ってこないんだ。なので、あいつの家に遊びに行った時に持って帰った。」

というような行為を指す。自らの不利益を自らの力、行動で解消する行為だ。
さて、この場合例えば、相手がゲームを盗られたと訴えたとしよう。そうすると一般的な感情的には何も悪くない、もう何も怖くないはずだが、実はこれは違法な行いとなってしまう。

現代社会(日本のみとは限らない)では、司法の介さぬところで自らの手で不利益を解消する行為は「自力救済」として、明確に認められない行為となっているのだ。
原理的には、それがまかり通るのであれば、この世の中は「物理的な力」が全てとなってしまいまた、自力救済を手助けするような団体が世の支配権を得てしまうからだ。
もちろんそんなことになっては、逆に生きづらい世の中になってしまうのは目に見えている、故に自力救済は禁じられている。

が、小さな諍いに逐一司法を介入させることが難しいのもまた現実、はてさて結局厚顔無恥な居直りがこの世の中、一番強いと言う事だ。

Clang for Windows 3.7を入れた

サナ・・・実は俺、お前のことを・・・djannです。


毎回恒例とも言えるようになったClang for Windowsの更新レビューである。といってもClang 3.7が出たのは2015年9月となるので、ずいぶん間が空いてのこととなる。

さて、インストール手順はいつもの通り*1だ。前回と同じく、コード中のリテラルを変更ビルドを行っても、実行ファイル中での定数は変わっていないバグ(?)はそのままのようである。

個人的にReleaseNotes中でのトピックスだと思ったのは、例外に対して一部対応が行われた事だろう。相変わらず自身でtry - catch等を書くことは出来ないが、従来のように

#define _HAS_EXCEPTIONS (0)

を定義してからC++ヘッダをインクルードしなければならないといった制限は無くなった。またVisual Studio 2015上でchar16_t/char32_tの取り扱いが変わったことによる自作型の定義も不必要となっている。

故に、今回のバージョンから「例外機構は例外として、それ以外はインストールが終われば特別気にすることなくC++のコードが書ける」ようになったと言えるだろう。もちろん標準ライブラリはそのVisual Studio実装済みのものに限定されるが、その他言語機能はClangで最新の実装済みのものが使用可能である。


例外に関しては、ReleaseNotes中で明示的に「しばらくはサポートされない」と書かれているので、そういうことなのだろう。では例外以前にリテラルの書き換え等の軽微なコード変更が反映されないバグの修正を願っておこう。また、修正されないことも考えてOSS界隈への参入方法もこっそりと調べておこう。

*1:インストーラを起動。旧バージョンのアンインストールを行わせ、はいはい言ってインストール完了。その後C:\Program Files (x68)\LLVM\tools\msbuild\install.batを管理者権限で起動。

Clang for Windows 3.6.2を入れた

そう・・・あなたの為のClangよ・・・。djannです。


今更になるが、このブログ中で一番役に立っているともっぱらの噂のClang for Windows系の記事の最新版を記述する。なお3.6.2自体は7月16日に既にリリースされていたらしい。

と言っても、今回入れたことによって何が変わったかは、ほぼ分かっていない。リテラルを書き換えてビルドしても、変更された結果が反映されていないバグもそのままだし#define _HAS_EXCEPTIONS (0)を先に定義しなければC++の標準ライブラリをインクルード出来ない*1のもそのまま、変数の値をウォッチ出来ないのもそのままだ。

強いて大きな変化を挙げるとするならば、Visual Studio 2015が公開されているので、そちらに対応が行われたのだろうということくらいだ(前から対応されてた?)


という訳で、早速いつもの手順*2でインストールを行い、その使用感を試している。Visual Studio 2012、Visual Studio 2013に関しては、パッと見は*3本当にアップデートをしたのか?というほど何も変わっていない。今までのプロジェクトもそのまま全く同じように(バグまで)ビルド出来るし、新しく作ってからの手順も変わっていない。ひとまず以下のようにハローワールドだ。

#define _HAS_EXCEPTIONS (0)
#include <iostream>

int main()
{
	std::cout << "Hello, New LLVM world!!" << std::endl;
}

さて、では肝心のVisual Studio 2015ではどうなのかというと、現状のところどうしようもない。まずかわいいバグとして、以下のようにPlatform Toolsetでの選択肢がLLVM-vs2014となっている。おかしいな、これはVisual Studio 2015だったと思ったのだが。

f:id:djann:20150727015335p:plain


さて、早速ビルドしたところ、以下のエラーが大量に出る羽目になってしまった。

use of undeclared identifier 'char16_t'
use of undeclared identifier 'char32_t'

単純にVisual Studio上のコードハイライトを見てみるだけでも、従来char16_tやchar32_tはtypedefされていたものが、どうやら組み込みの型になったらしい。

int main()
{
	char16_t a;	// error : unknown type name 'char16_t'
}

そして上記コードの通り、Clang for Windows 3.6.2ではchar16_tは組み込みの型として存在せず、結果エラーとなるようだ。そして性質の悪いことに(当たり前だが)、これらcharXX_t型は標準ライブラリ各所で特殊化に指定されており、C++標準ライブラリを用いようとすれば、すぐさま大量のエラーを吐いてくれる。

では型定義をしたらどうか?

using char16_t = short;
using char32_t = long;
#include <iostream>

int main()	// 以下略.

shortやlongに対する特殊化を再定義したことになり、これもエラーだ。では新たな型を作ってはどうか?
これは整数値への変換と受け取りが可能な型を作成すればいける。ただし標準ライブラリの挙動が正しいままかどうか、その他もろもろの検証が必要になるだろう。とりあえずビルドを通すだけの適当な定義は以下の通りとなる。LLVM-vs2015で(いや2014だったか)最小限のHello, Worldプログラムは以下の通りだ。

struct char16_t {
	short elem;
	constexpr char16_t() : elem(0) {}
	constexpr char16_t(int val) : elem(val) {
	}
	operator int() const { return elem; }
	operator unsigned int() const { return elem; }
	operator short() const { return elem; }
	operator unsigned short() const { return elem; }
	operator long() const { return elem; }
	operator unsigned long() const { return elem; }
};
struct char32_t{
	int elem;
	constexpr char32_t() : elem(0) {}
	constexpr char32_t(int val) : elem(val) {}
	operator int() const { return elem; }
	operator unsigned int() const { return elem; }
	operator short() const { return elem; }
	operator unsigned short() const { return elem; }
	operator long() const { return elem; }
	operator unsigned long() const { return elem; }
};
#define _HAS_EXCEPTIONS (0)
#include <iostream>

int main()
{
	std::cout << "Hello, New LLVM World!!" << std::endl;

	std::cin.get();
}

というわけで、長々と書いたが現状Visual Studio 2015でLLVM-vs2014を使ってビルドするのは使い物にならない。流石にここまでして使う意味はないだろう。大丈夫だ、Visual Studio 2013があれば戦える。ドラゴンボールがあればry


回を重ねるごとに雑になってきている気がするが、今回の記事は以上となる。

*1:C言語の標準ライブラリならインクルード可ではある。全てかどうかは定かではないが

*2:インストーラを起動。旧バージョンのアンインストールを行わせ、はいはい言ってインストール完了。その後C:\Program Files (x68)\LLVM\tools\msbuild\install.batを管理者権限で起動するところまで。

*3:細かいところは知らない

相変わらず彼は規格に詳しくない

こういう決まりですので。djannです。


相変わらずの規格上の疑問である。ポインタ型からboolへの変換は、規格上認められた動作で、暗黙的に行えるという認識だったのだが、これは正しくはないのだろうか?

Visual Studio 2015で以下のコードをコンパイルすると警告が出る。

class foo {
	int *m_ptr;
	explicit operator bool() { return m_ptr; }
};
'int *': forcing value to bool 'true' or 'false'

int*をtrueまたはfalseのbool型に強制する。それのどこが問題なのだろうか?nullptrであればfalseに、そうでなければtrueに変換されることを明確に期待しているのだが、規格上正しくないのだろうか?


こちらが該当しそうな気がしていたのだが

4.12 Boolean conversions
1 A prvalue of arithmetic, unscoped enumeration, pointer, or pointer to member type can be converted to a prvalue of type bool. A zero value, null pointer value, or null member pointer value is converted to false; any other value is converted to true.

Visual Studio 2015 Community入れた

まだまだ関係は続いていく。djannです。


先日正式公開されたVisual Studio 2015を入れた。前回の記事でもこっそりと2015を使っていると書いているが、公開翌日にインストールしている。

さて、このVisual Studio 2015だが、私自身は特別何の問題もなく入れられ、使用出来た。だが会社の者や知人はインストールしてもC++が使えなかったりと言った問題があったらしいので注意すべし。
ちなみに私はインストール時にカスタムインストールで、常にすべての項目を選択してインストールする事にしているので、もしかしたらそれが要因なのかもしれない。



Visual Studio 2013からだが、Productivity Power Toolsを入れなくても標準でスクロールバーのマップモードが入った。だが選択したキーワード、検索キーワードの位置がソース中のどこにあるか表示してくれる機能(下記画像参照)の、色変更設定がどこで行えるのか、そもそも行えないのかが不明となっている。知っている人がいれば教えてほしい。

f:id:djann:20150726175255p:plain

それでもdecltypeの挙動は謎である

貴方はこういう人ですね?djannです。


depotには現在with_state_valueというテンプレートが存在する。boostに存在し、C++17で標準入りされるといわれているoptionalの表面的な概念を取り入れたもので、指定した型のオブジェクトと、その有効状態を保持するものだ。

depot::with_state_value<int> val;

if ( val ){
	// unreachable.
	std::cout << "available : " << *val << std::endl;
} else {
	std::cout << "unavailable." << std::endl;
}

val = 10;

if ( val ){
	std::cout << "available : " << *val << std::endl;
} else {
	// unreachable.
	std::cout << "unavailable." << std::endl;
}

// explicit destroy.
val = depot::null_value();

if ( val ){
	// unreachable.
	std::cout << "available : " << *val << std::endl;
} else {
	std::cout << "unavailable." << std::endl;
}

// emplace object.
val.emplace( 25 );

if ( val ){
	std::cout << "available : " << *val << std::endl;
} else {
	// unreachable.
	std::cout << "unavailable." << std::endl;
}

実行結果は以下のようになる。

unavailable.
available : 10
unavailable.
available : 25


このwith_state_valueを用いる際に、例えばunavailable状態のフローで間違えてvalを用いてしまうこともあるかもしれない。もちろん「そんなコードを書く方が悪い」と言ってしまえばそれまでであるし、間違えてunavailableなwith_state_valueを用いた際には即停止するようになっているのですぐに気付くだろう。

だがそれ以前に、間違いを起こせない書き方をしたいという場合もある。そんな場合に用いるフリーテンプレート関数を用意しようとしたところ、「Visual Studio上で」decltypeの挙動が想定通りに動かずに現在調査中であり、その解決法を読者に教授いただきたくこの記事を書き始めた次第である。


想定仕様

想定している使用法としては、以下の通りである。

  • 第一引数にはwith_state_value&を受け取る
  • 第二引数には関数(関数オブジェクト、ラムダ)を受け取る
    • 第二引数がwith_state_valueのテンプレート引数を1つ受け取るのならばそれはavailable状態に行う処理
    • 第二引数の関数が引数を受け取らないのならばそれはunavaiable状態に行う処理
  • 戻り値は渡された関数準拠とし、if式のような形で記述できることとする
  • 三引数版があり、こちらはavaiable状態、unavaiable状態両方の関数を受け取る
    • 第二引数はavailable用、第三引数はunavaiable用とし、それぞれは前述の仕様に相当する
    • この場合、両関数の戻り値の型が異なる場合はエラーとする

コードとしては以下の通りとなる。

depot::with_state_value<int> val;

// no running.
depot::match( val, []( int &n ){
	std::cout << "available : " << n << std::endl;
});
// running.
depot::match( val, [](){
	std::cout << "unavaiable." << std::endl;
});
val.emplace( 10 );
// three arguments.
depot::match( val, []( int &n ){
	std::cout << "available : " << n << std::endl;
}, [](){
	std::cout << "unavaiable." << std::endl;
});

これの実現の為、以下のようなコードを書いたところ、Clangでは正常に働き、Visual Studioでは(2012, 2013, 2015でテスト)、使用しようとすらしていない段階で引数無し版を受け取る関数で「定義済みの関数が再定義された」とのエラーが出てしまう。

template <class vType, class FuncType>
auto match( depot::with_state_value<vType> &val, FuncType fun ) -> decltype(fun(*val)) {
	if ( val ){
		return fun( *val );
	}
	return decltype(fun(*val))();	// default value.
}
template <class vType, class FuncType>
auto match( depot::with_state_value<vType> &val, FuncType fun ) -> decltype(fun()) {
	if ( !val ){
		return fun();
	}
	return decltype(fun())();	// default value.
}
template <class vType, class ThenFunc, class ElseFunc>
auto match( depot::with_state_value<vType> &val, ThenFunc fun1, ElseFunc fun2 )
	-> typename enable_if<is_same<decltype(fun1(*val)), decltype(fun2())>::value, decltype(fun1(*val))>::type
{
	if ( val ){
		return fun1( *val );
	} else {
		return fun2();
	}
}

インスタンス化のタイミングでdecltype(fun())が評価できず、その結果オーバーロード候補から外され1引数版と区別され呼び出されると思っていたのだが、その認識は間違っていたのだろうか?
ちなみにdecltypeを使わずenable_if等でオーバーロード候補から外している他の関数テンプレートの場合は、Visual Studio上でもその想定通りの挙動を行ってくれている。

知りたいのは、この挙動が規格上はどうなのかという部分と、Visual Studio上でどう回避すればよいのかという部分である。

  • 戻り値の型をFuncTypeより取得する
  • 引数無し、1引数で振り分ける

というのが必要となる。

追記

少なくともVisual Studio 2012でdecltype(&lambda::operator())によってRetType(Args...)のシグネチャが得られるようだ(Clangでは駄目。規格上も駄目な感じがする)。なのでVisual Studio上では窓口の関数を一つだけ定義し、そこのみでdecltypeを用いて内部で呼び出す関数で従来のメタ関数を用いて判別を行う。という形で実装出来そうだ。

追記その2

前述の手法を用いて、Visual Studio 2012上で動作するmatch()関数の作成に成功した(現状lambdaでしか試していないし、関数ポインタを渡した際にはおそらくコンパイルエラーになる、というかコンパイラの内部エラーが発生した)。なお、このコードはClangではそもそもエラーとなる。おそらくdecltype(&FuncType::operator())と取得しているところが原因と思われる。そもそもClangの場合プリプロセッサで分岐させ、最初に思いついたコードを働かせれば問題ない。

// fetch a signature.
namespace match_use_functions {
	// result type for lambda in visual studio 2012.
	template <class vType>
	struct result_of;
	template <class RetType, class classType>
	struct result_of<RetType (classType::*)()const>{
		typedef RetType type;
	};
	template <class RetType, class classType, class ArgType>
	struct result_of<RetType (classType::*)(ArgType)const>{
		typedef RetType type;
	};

	// zero or one argument condition.
	template <class vType>
	struct has_argument : public depot::false_type {};
	template <class RetType, class classType, class ArgType>
	struct has_argument<RetType (classType::*)(ArgType)const> : public depot::true_type {
		typedef ArgType type;	// not used.
	};
}	// namespace match_use_functions.

template <class vType, class FuncType>
struct match_t {
	template <class SigType>
	static auto invoke( depot::with_state_value<vType> &value, FuncType &func )
		-> typename depot::enable_if<depot::match_use_functions::has_argument<SigType>, typename match_use_functions::result_of<SigType>::type>::type
	{
		if ( value ){
			return func( *value );
		}
		return typename match_use_functions::result_of<SigType>::type();
	}
	template <class SigType>
	static auto invoke( depot::with_state_value<vType> &value, FuncType &func )
		-> typename depot::disable_if<depot::match_use_functions::has_argument<SigType>, typename match_use_functions::result_of<SigType>::type>::type
	{
		if ( !value ){
			return func();
		}
		return typename match_use_functions::result_of<SigType>::type();
	}
};

//
template <class vType, class FuncType>
auto match( depot::with_state_value<vType> &value, FuncType func )
	-> decltype(depot::match_t<vType,FuncType>::template invoke<decltype(&FuncType::operator())>( value, func ))
{
	return depot::match_t<vType,FuncType>::template invoke<decltype(&FuncType::operator())>( value, func );
}
template <class vType, class ThenFunc, class ElseFunc>
auto match( depot::with_state_value<vType> &val, ThenFunc fun1, ElseFunc fun2 )
	-> typename depot::enable_if<depot::is_same<decltype(fun1(*val)), decltype(fun2())>, decltype(fun1(*val))>::type
{
	if ( val ){
		return fun1( *val );
	} else {
		return fun2();
	}
}

※depot::enable_ifはstd::enable_ifと違いtrue_type, false_typeを受け取り、第二引数がvoid型でもtypeを持つもの。

追記その3

どうやらこれはSFINAE式?と呼ばれるものに当たるようで、Visual Studio上では明確にそちらの対応が行われていないとアナウンスされているようだ。sizeofやdecltypeを用いた「式」によるSFINAEはVisual Studio上では動かない。と事前認識を持って、以降は取り掛かる必要があるようだ。

なお、decltypeを用いてlambda::operator()のシグネチャを取得する事も、もちろん規格上認められていないので、完全に「現存するバージョンの」Visual Studio専用workaroundとなる。このようなコードは出来るだけ書くべきではない。