CORDEA blog

Android applications engineer

アプリ向け Error monitoring, Crash reporting tools

結構ある

Name Android iOS Flutter Pricing for individuals Pricing for teams *1
Crashlytics $0 $0
Sentry $0 $26
Bugsnag $0 $18
Rollbar $0 $0
Smartlook $0 $0
Shake ⚠️ *2 $0 $160
Raygun $40 $40
Instabug $249 $249
Bugsee $0 Custom
New Relic $0 $0~ *3
Datadog $0.31 *4 $0.93~
Dynatrace - *5 -

記事公開時点、✅ は公式 SDK があるかどうか

  1. Team の定義
    • 5 developers.
    • 1 app.
    • About 100 events/day, 3,000 events/month.
  2. Crash reporting はまだサポートしてなさそう
  3. Free plan. 1 Full platform user, 4 basic users.
  4. Error Tracking ($0.31~).
  5. むずかし

firebase.google.com sentry.io newrelic.com www.dynatrace.com www.datadoghq.com www.bugsnag.com raygun.com rollbar.com www.shakebugs.com www.instabug.com www.smartlook.com bugsee.com

GitHub Flavored Markdown で Store badges を揃える

App Store, Google Play, Microsoft Apps などの install badge を README などに配置したい時、
Google Play の badge は image 自体に margin があるので、それぞれを揃えて表示するのがとっても面倒。
margin などは使えないので img の width と height で地道に揃える。

まぁあまり置く機会も無いのだけど。

README.md · GitHub

Age of Empires IV で Game Mode の Mod 作るときの tips

小ネタ

Age of Empires IV Mod Workshop

勝敗を決める

Core_SetPlayerVictorious/Core_SetPlayerDefeated を使う
これは新しく Mod 作った時の example code にある

注意点としては、Player が負けて AI が生き残っているような場合を作ると概要が開けず待ちぼうけをくらうことになる
なので、Player が残っていない時点で AI の勝敗も決めておくとよさそう
なお、勝者なしの場合は Core_WinnerlessGameOver を使用する

あと Defeated が呼び出されると Core_IsPlayerEliminated が変わり勝敗が判定できる
勝利条件にもよると思うので変わらなければ Player_IsAlive とかも見るとよさそう

Unit を作成・配置する

UnitEntry_DeploySquads を使う
これも example の通り

もしまだ読み込まれていない Unit (時代が今よりも先, 他国の固有, etc) の場合は、豆腐になるので読み込む必要がある
これには Squad_Precache を使う

第 1 引数は Deploy に渡している Blueprint, 第 3 引数は PlayerID
ここでいう PlayerID は integer ではない、Player_GetID で取得できる値は integer なので注意
他の引数は空でも良い (0 あるいは "")
読み込みが完了したことを検知するには GE_EntityPrecached を使用する、Global Event については下で

Unit の一覧は Content Editor 下部のオブジェクトブラウザから templates > ebps > races 以下を見ればだいたいわかる

Unit の suffix (人種コード)

Player_GetRaceName で得られる人種名は full name なので変換が必要

Race name Race code
abbasid abb
chinese chi
english eng
french fre
hre hre
mongol mon
rus rus
sultanate sul

そのうち Util_ とかで提供されるんじゃないですかね

Unit の殺害 Event

Global Event の中でもよく使いそうな
Unit (Entity) が殺されたことを Event で取得できる
Rule_AddGlobalEvent の第 2 引数に GE_EntityKilled を渡す
function に返る context の中身は

  • victimOwner
    • 被害者側の player
  • victim
    • 対象 Unit
  • victimSquad
  • killer
    • victim を殺した Unit

など。killerOwner とかもあったかも
killer と victim が一緒だと自殺 (Player による delete)

Unit が何人残っているか

Player_GetEntityCountByUnitType を使う

この Unit type が何かというと、自分もオブジェクトブラウザから調べる方法を知らない
チューニングパックを作成すると作られる attrib/*.xml で unit を表示して
extensions > type_ext > unit_type_list の中を見ると書いてあるのが Unit type
なので、村人の場合は "villager"、軍事 Unit 全体の場合は "military"

なお、特定の Unit がどの type かを知るためには Entity_IsOfType が使用できる

Console 出力系

Debug に

  • print
  • Util_PrintIf
  • Util_PrintTable

とりあえずまだ Beta なので Document も完全ではなく割と苦労します。頑張りましょう
いくつかデモで作ったので参考までに

github.com
github.com
github.com

Windows PC で BSoD 頻発してたのを直した

Windows PC が BSoD 起こすようになったけど直ったメモ

大まかな構成

問題としては

  • BSoD (VIDEO_TDR_FAILURE)
  • BSoD (DPC_WATCHDOG_VIOLATION)
  • Driver 読み込みエラー (code 43)

このあたりが起動後 10 分以内にほぼ 100% 起きる

  • 復元
  • Driver 更新
  • Clean install

を何回か実行したけど効果なし

Solution

最終的に UEFI (BIOS) から PCIe x16 Bus Interface を Gen3 にすることで平和になった
私の環境では Advanced -> AMD PBS -> PCIe x16 Bus Interface にあった

Flutter で 2 つの Path を組み合わせる Path#combine

Android における Path#op あるいは Canvas#clipPath のようなことをする Path#combine


api.flutter.dev


以下のような形で Path を組み合わせる

final path = Path.combine(
  _pathOperation,
  Path()
    ..addOval(Rect.fromCircle(
      center: Offset(centerX - 50, centerY),
      radius: 100,
    ))
    ..close(),
  Path()
    ..addOval(Rect.fromCircle(
      center: Offset(centerX + 50, centerY),
      radius: 100,
    ))
    ..close(),
);

canvas.drawPath(path, _paint);

PathOperation

Android と一緒です

api.flutter.dev

difference


f:id:CORDEA:20210821153045p:plain

intersect

f:id:CORDEA:20210821153118p:plain

union

f:id:CORDEA:20210821153140p:plain

xor

f:id:CORDEA:20210821153154p:plain

reverseDifference


f:id:CORDEA:20210821153128p:plain



github.com

Lua で SQLCipher (lsqlcipher) を使う

lsqlcipher の Install から使うまで

Install

LuaRocks で install します。

luarocks.org

$ luarocks install lsqlcipher

私の今の環境 (macOS) だと LDFLAGS で指定されている path に lcrypto がないので sqlcipher の install に失敗します。

$ luarocks install lsqlcipher 
Installing https://luarocks.org/lsqlcipher-0.9.5-3.src.rock
Missing dependencies for lsqlcipher 0.9.5-3:
   sqlcipher (not installed)

lsqlcipher 0.9.5-3 depends on lua >= 5.1, < 5.5 (5.4-1 provided by VM)
lsqlcipher 0.9.5-3 depends on sqlcipher (not installed)
Installing https://luarocks.org/sqlcipher-4.4.2-2.rockspec

...

configure: error: C compiler cannot create executables
See `config.log' for more details

Error: Failed installing dependency: https://luarocks.org/sqlcipher-4.4.2-2.rockspec - Build error: Failed building.


やり方は色々だと思いますが、symbolic link を貼りました。

$ ln -s /usr/local/Cellar/openssl@1.1/1.1.1k/lib/libcrypto.dylib /usr/local/lib/
$ ln -s /usr/local/Cellar/openssl@1.1/1.1.1k/include/openssl /usr/local/include/

必ずしも原因が同じとは限らないので、error が出た場合は config.log を見るのが良さそうです。
といっても config.log 消えてると思うので、その場合は Source になっている sqlcipher を自分で build して試してみると良いと思います。

https://luarocks.org/sqlcipher-4.4.2-2.rockspec

Usage

local sqlite3 = require("lsqlcipher")
local db = sqlite3.open("./foo.sqlite")

db:key("bar")
db:exec [[
    CREATE TABLE IF NOT EXISTS baz (
        id INTEGER PRIMARY KEY
    );
]]

あとは lsqlite3 と一緒です

luarocks.org

Ktor Client で Twitter の Filtered stream を取得する

小ネタ

developer.twitter.com

HttpClient

このへんはあまり関係ないのでよしなに

val client = HttpClient(CIO) {
    defaultRequest {
        url {
            protocol = URLProtocol.HTTPS
            host = "api.twitter.com"
        }
        header("Authorization", "Bearer $token")
    }

    Json {
        serializer = KotlinxSerializer(json)
    }
}

Add rules

取得する Tweets の rules を POST する

返り値は String でも HttpResponse でも任意に定義した Response でも何でもいいですが、
kotlinx.serialization でそのまま decode する場合は、errors など key がない場合があるので ignoreUnknownKeys = true を指定しておくと良いです

val response = client.post<String> {
    url {
        encodedPath = "2/tweets/search/stream/rules"
    }
    body = StreamRulesRequest(rules)
}

Get tweets

Tweets を real-time に取得する
Ktor Client の doc に書いてある通り、HttpStatement を使用します。

Streaming—Ktor

client.get<HttpStatement> {
    url { encodedPath = "2/tweets/search/stream" }
}.execute { response ->
    val channel = response.receive<ByteReadChannel>()
    do {
        val tweet = channel.readUTF8Line() ?: break
    } while (tweet.isNotBlank())
}

Flow とかで emit すれば扱いやすくなると思います。

もう少し詳しく見たければこちらを

info-provision-bot/TwitterClient.kt at main · CORDEA/info-provision-bot · GitHub