Adventure Land

Adventure LandというMORPG風のゲームがある。数か月前くらいにできて、まだプロトタイプだそうだが、cursors.io, agar.io, diep.ioのようにHTML5ベースのゲームだ。

HTML5ということはJavaScriptで動いているわけで、クライアントサイドでごにょごにょやる層も出てくるわけだが、他と違うのはその仕組みがもともとゲームに備わっていることだ。

試しにスクリプト機能をいろいろといじってみた。ただしプロトタイプ版のため、現時点とは仕様が変わる可能性がある。

続きを読む

diff -Wの関数名長制限をなくす

diffには差分文脈の関数を表示する機能があり、-W (--show-function-line)で関数とするパターンをカスタマイズできる。ただ関数名の長さに40字の制限があって長いと途中で途切れてしまう。
unix diff --show-function-line truncates outputfunction is truncated to 40 charactersによると文字数はハードコードされているらしい。入れているMSYSですんなりコンパイルできるか不明なのでバイナリをいじってみた。
ソースに当たるまでもなく、上記情報によると、print_context_functionでputc(' ', out);した後のfor (j = i; j < i + 40 && function[j] != '\n'; j++)が問題の箇所。関数冒頭でputc(' '...しているところを探す。

PUSH EBP
PUSH EDI
MOV EBP,EDX
PUSH ESI
PUSH EBX
MOV EDI,EAX
XOR ESI,ESI
SUB ESP,1C
MOV DWORD PTR SS:[LOCAL.9],EAX
MOV DWORD PTR SS:[LOCAL.10],20
CALL <JMP.&msys-2_0.putc_unlocked>       ; Jump to msys-2_0._putc_unlocked
MOV EDX,EBP
 MOVZX ECX,BYTE PTR DS:[EDX]
 MOV EAX,EDX
 LEA EBX,[ECX-9]
 CMP BL,4
 JBE SHORT _00402B00
 CMP CL,20
 JE SHORT _00402B00
 LEA EDX,[ESI+27]              ; 8D56 27
 MOV ECX,ESI                   ; 89F1
  ADD ECX,1                    ; 83C1 01
  CMP ECX,EDX                  ; 39D1
  JG SHORT _00402AB4           ; 7F 07
  CMP BYTE PTR SS:[ECX+EBP],0A ; 807C0D 00 0A
  JNE SHORT _00402AA6          ; 75 F2
 CMP ESI,ECX

あった。条件分岐JG SHORT _00402AB4NOPにする(1EABの7F 07を90 90とする)と、長さ制限が消えた。

WindowsのStrawberry Perlで最新のSQLiteを使う

SQLiteで新しいバージョンを使いたくなった。CPANで入れてみても9か月前のバージョンしか入らない。

cpanm -n DBD::SQLite
perl -MDBD::SQLite -e "print $DBD::SQLite::sqlite_version"
# 3.10.2

インターフェイスをどうこうする必要はないのでSQLiteのソースだけ入れ替えればいいはずと思ってやってみた。
先ほどインストールしたDBD::SQLiteのソースをローカルから拾ってきて展開。
またSQLiteのダウンロードページでamalgamationソースを持ってきて、sqlite3.c, sqlite3.h, sqlite3ext.hを置き換える。
あとはcpanmのローカルディレクトリインストール機能を使う。

bash
tar xf /C/path/to/strawberry/data/.cpanm/work/*/DBD-SQLite-1.50.tar.gz
cd DBD-SQLite-1.50
wget https://www.sqlite.org/2016/sqlite-amalgamation-3150000.zip
unzip sqlite-amalgamation-3150000.zip
mv sqlite-amalgamation-3150000/sqlite3* .
exit
cpanm -n .

バージョンアップできた。

perl -MDBD::SQLite -e "print $DBD::SQLite::sqlite_version"
# 3.15.0

32bit版Windows 7をWindows 10に更新

あるPCがPCIボードの事情でWin 7 32bitにしがみついていたが最近それが不要になった。すでに10月で無料更新期限後だが、まだ普通に更新できるという話なので更新してみた。
まず公式ダウンロードページから作成ツールをダウンロードする。後々のことも考えてインストールメディアの作成を選んだ。
最初はUSBメモリに保存しようとしたのだが、接続しているUSBメモリドライブが表示されない。仕方がないのでISOファイルを作成することにした。

メディアが作成できない

OSのダウンロードは開始するのだが、完了後検証しようとするとエラー 0x80070008 0x9002で中断してしまう。やり直して先に進んでも、検証途中に0x80070008 0xA0003で中断してしまうこともあった。
0x80070008はいわゆるシステムリソース不足のエラー。メモリがいくら空いていてもシステム領域に限界のある32bit Windowsカーネルではある意味どうしようもないのだが、インストーラなので使い勝手を考えなければ空ける余地はある*1
常駐ソフトをMSSEを含め可能な限り切って、画面解像度を落として再起動、再起動後にも可能な限りプロセスを終了して行ったところ、何とかISOファイルを作成できた。
ちなみに3回失敗した。ISOの保存場所に関係なくC:\ESD\Downloadにinstallx**.esdがダウンロードされるが、プログラムの作りこみが甘くダウンロード自体が完了しても検証時やISO作成時に上記エラーで中断してしまうとダウンロードからやり直しになる。x86とx64で計5GBあるので都合15GBも帯域を無駄にしてしまった。
このダウンロードしたESDからISOを作るツールもあるようだが、isolatedな形式ということでISOの作成ができないばかりか、RSA鍵も不明でデコードもできないようだったので上記のように頑張るしかなかった。
ともあれ最終的にはISOが用意できたのでアップデートにやっと移れる。

後で知ったのだが、Windows以外(のUser-Agent)ではISOファイルを直接ダウンロードできたらしい。なぜかWindowsではオプションを表示してくれない。*2

インストール

ISOをWinCDEmuでマウントして、内容をNTFSフォーマットのUSBメモリのルートにすべてコピーした。DVDに焼いてもいいが、アクセス速度的にも高速なUSBメモリの方が便利だろう。仮想イメージからSetupを起動してもエラーになる。
USBメモリ上のSetupを起動して、アップグレードを開始する。
途中2回くらい再起動した。Setupを7から起動したからかUEFI BIOSのブート順序を入れ替えないでも特に問題なかった。

巷の期限後の移行記事を見ると、ライセンスはインストール時に7のプロダクトキーを入れるとOKという話だった。しかし今回インストールと初期設定を済ませてもキーを尋ねられることはなく、Win-Breakを押してみるとすでにライセンス認証済みとなっていて、何も入力作業は必要なかった。たまたまなのか、何かしら最近のWindows 7側の更新で状況が変わったのかは分からない。

環境設定

Windows Vistaや7にしたとき同様、キーの置き換えがリセットされているのでremapkeyを管理者権限で起動し再設定する。
AppDataのTEMPのジャンクションも再設定した。
おせっかいにもアプリの関連付け設定もブラウザやメディアファイルでMS謹製のものに勝手に変更されていたので修正した。*3

7と比べて

高速スタートアップなしで起動は10秒弱遅くなった。
メモリ使用量は起動時点では130MBくらい増えている。初期状態で余計なものを削っていないせいもあるかもしれない。
Windowsの上書きインストールならばこんなものだろう。後日64bit化したい。

*1:それにしても巨大ファイルのverifyができないというのはお粗末な作りだ。全体をmmapでもしようとしているのだろうか。

*2:去年にそんな記事を読んだ記憶もあるのだが、すっかり忘れていた。

*3:せっかく設定してもたまに勝手にMSのアプリにリセットしてくれる。opt-outできない余計な機能を増やす伝統はいつまで経っても変わらないようだ。

startコマンドがファイルデスクリプタを引き継いでしまう

3つのスクリプトがあるとする。

  • a.bat は b.bat を実行し、そのエラー出力をログに保存する
  • b.bat はいろいろな処理をする過程で c.pl というデーモンを起動する
  • c.pl はデーモンなので終了しない
# a.bat
call b.bat 2>>error.log

# b.bat
start wperl c.pl

# c.pl
use AnyEvent;
my $cv = AE::cv;
...
$cv->recv; # 基本的に終了しない

この時、startで起動した c.pl の出力はリダイレクトされていないのにも関わらず、ログファイルが c.pl によって使用中になっていまう。a.bat としてはログを処理して処分したいが、Windowsにおいて開いているファイルは処分できないので困る。
startでも&2をリダイレクトしてみたが、古いデスクリプタを閉じてはくれないようだ。
a.bat であらかじめ c.pl を起動しておく以外に解決する方法はないのだろうか? まあ、トラブルメーカーなbatを止めるのがいいのだろう。