SVX日記
2004-12-28(Tue) コロがしたい、マキこみたい
先日かきかけのまま放置してある塊魂のレビューだが、なにしろこのゲームは「人間の爽快感を刺激してくるゲーム」であるとまとめたい。少なからずゲームにはそういう面がある。シューティングの破壊衝動、インベーダやパックマンの掃除感覚、テトリスの整理整頓。しかし、このゲームの面白いトコロは敵を「制限時間」のみに絞って徹底的にゲーム性を排除し、他のゲームにはない爽快感を持たせている点であろう。つまり「人間の爽快感を刺激してくる『だけの』ゲーム」ということだ。
結果、この類のゲームの最大の勲章であると私が考える「普段の生活へのオーバーラップ」現象を勝ち得ている。街を歩いているだけで「転がしたく」なって「巻き込みたく」なってくるのである。今まで私からこの「普段の生活へのオーバーラップ」現象を勝ち得たゲームは「テトリス」ぐらいか。ビルの窓など格子状のモノを見たりすると頭の中にブロックが降り始める病気には、かなり長い期間に及んで悩まされたものである。
そして今は「塊魂」だ。そして私的にかなり転がしたい場所、それはズバリ「秋葉」である。あれだけゴチャゴチャした場所を転がしたらさぞ爽快であろう。駅前から路上の吸殻等を巻き込みつつラジオセンターへ。細かいチップ等をワシワシと巻き込みつつ抜け、信号を渡ってラジオデパートの中へ。この頃には大きめのトランスまで巻き込める大きさに成長しているハズだ。向かいのオモチャ屋もひと転がししておくか。あとは順当にノートPC、デスクトップPC、ディスプレイの順で巻き込む大きさを増していったら、次のターゲットはヲタク達。浜田電気から、ヒロセテクニカル、秋月、鈴商と抜けながらオタクを一網打尽にすれば、路肩のバイクを巻き込めるサイズに成長しているはず。あとは道路上のクルマを巻き込みつつ、右回りで山手線方面に向かい、小さな店舗を巻き込みつつ、トライアミューズメントタワーを巻き込めないまでもひと揺らし。目抜き通りに戻って小さな店舗を一掃してから、ツクモの黒いビル、秋葉原駅を巻き込んで、タイムアップである。やっふー。
と、ここで思った。このゲームをトラックボール対応にしてはどうだろうか? 転がすスピードを変化できれば、それを活かしたゲーム性を追加することもできる(このゲームの場合ゲーム性を低く保つことで一般受けを狙っている面があることもわかっているが、テクニカルな要素を追加することでゲーム好きを満足させる方向性に進むのも間違いではないだろう)。後楽園遊園地を転がして勢いをつけてジェットコースターのコースからマーブルマッドネスのようにジャンプ!! ところが、落ちたところはK1を興行中の埼玉スーパーアリーナ!! それじゃマーブルマッチョネスやがなッ!? 国技館に落ちればマーブルファットネスかいッ!! ……お後がよろしいようで。
2021-12-28(Tue) キミはそこにいるべきなんだ!
先日、SK11の21mmのソケットを購入した。ロードスターのホイールナットを締める用である。SK11のソケットレンチセットは19mmまでなので、追加購入という形だ。
2024-12-28(Sat) WebSocketクライアントを実装する
しばらく前にMezatalkというチャットツールを作り、職場で活用している。チャットツールといえばWebSocketだ。発言の送信や受信には必須の機能である。通常、発言はブラウザのJavaScriptから行われる。が、Ruby版のコマンド「wsclient」も用意してある。ボットに発言させたい場合などに使える。こんな感じだ。
res = system('./wsclient',
'ws://127.0.0.1:33109/',
"{:REQUEST=>'login', :TYPE=>'talk', :USER=>'user1', :ROOM=>'_t~room1'}@@login",
"{:REQUEST=>'sentence'}@@Hello.",
)
実際、先日に記事にしたXalebotは、その名の通りボットであり、問い合わせに対する回答案などをMezatalkに発言するという連携機能も備わっている。
で、今回「特定の発言が行われたら、別のシステムでその発言を処理する」という機能を実装する必要が生じた。まずは、別のシステムから発言を取得できるようなAPIを実装し、WebSocket経由で発言を取得できるようにした。さらに、発言が行われた場合に、設定ファイル中に記述した関数を呼び出す機能を実装し、そこから「wsclient」を実行しようとした、のだが……それが、どうやっても動かない。普通にコマンドとして実行すれば動くのだが、設定ファイル中からだと動かない。にっちもさっちもよっちもごっちも動かない。ドハマリ。
「wsclient」は「em-websocket-client」というRubyのライブラリを使っているのだが、見よう見まねで書いたコードなので、それ以上に追求のしようがない。うーむ、こうなったら、独自に実装するか。
というわけで、RFCの6455「The WebSocket Protocol」を眺めつつ、チマチマと実装していく。HTTPで接続後、プロトコルを切り替えたり、クライアントからの送信内容にはマスクを施したり、なんかいろいろと珍しい処理がある。面倒クサいが面白い。面白いが面倒クサい。
#!/usr/bin/env ruby
require './wshelper'
require 'timeout'
require 'socket'
wsh = WebSocketHelper.new(uri = ARGV.shift)
Timeout.timeout(3) {
sock = TCPSocket.open(wsh.uri.host, wsh.uri.port)
sock.syswrite(wsh.handshake)
sock.sysread(9999)
while(request = ARGV.shift)
sock.syswrite(wsh.encode(request))
puts('[%s]' % wsh.decode(sock.sysread(9999)))
end
puts('Closed.')
}
require 'uri'
class WebSocketHelper
attr_reader :uri
def initialize(uri)
@uri = URI.parse(uri)
end
def handshake
(<<REQ % [@uri.path, @uri.host, @uri.port, make_websocket_key]).gsub(/\n/, "\r\n")
GET %s HTTP/1.1
Host: %s:%s
Upgrade: websocket
Connection: upgrade
Sec-WebSocket-Key: %s
Sec-WebSocket-Version: 13
REQ
end
def make_websocket_key
nonce = []; 4.times { nonce << rand(0xFFFFFFFF) }
@websocket_key = [nonce.pack('N*')].pack('m0')
end
def encode(req)
make_masking_key
head(req) + payload(req)
end
def make_masking_key
@masking_key = rand(0xFFFFFFFF)
end
def head(req)
head = ''
fopc = 0
fopc += (fin = 1) << 7
fopc += (opcode = 1)
head << [fopc].pack('C')
mplen = 0
mplen += (mask = 1) << 7
if((it = req.length) < 126)
mplen += it
head << [mplen].pack('C')
elsif(it < 65536)
mplen += 126
head << [mplen, it].pack('Cn')
else
mplen += 127
head << [mplen, 0, it].pack('CNN')
end
if(mask == 1)
head << [@masking_key].pack('N')
end
head
end
def payload(req0)
len0 = req0.length; req = req0.dup
req << "\x00" while(req.length % 4 != 0)
res = []; req.unpack('N*').each {|u32|
res << (u32 ^ @masking_key)
}
res.pack('N*')[0, len0]
end
def decode(res)
fopc = res.slice!(0, 1)
mplen = res.slice!(0, 1).unpack('C')[0]
if(mplen < 126 and mplen == res.length)
elsif(mplen == 126 and (res.slice!(0, 2).unpack('n')[0]) == res.length)
elsif(mplen == 127 and (res.slice!(0, 8).unpack('NN')[1]) == res.length)
else
raise('Unexpected.')
end
res
end
end
まぁ、ヤルことちゃんとヤッてない。んが、仕事はキッチリこなします。まるで、オレみたいなヤツだな。んが、やっぱり、設定ファイル中から呼ぶと動かない。にっちもさっちもよっちもごっちも動かない。な、なんでぇ?
終いにはtcpdumpでパケットまで確認してしまう。要求は出ている。んが、応答が返らない。設定ファイル中から呼んだ場合だけ。なんだこれ。いや、正確にはタイムアウトした瞬間に応答が返る。なんだこれ。なんだこれ。なんだこれ。サーバ側の問題?
サーバ側は「em-websocket」というRubyのライブラリを使っているのだが、見よう見まねで書いたコードなので、それ以上に追求のしようがない。うーむ、こうなったら、独自に実装するか……って、イヤ、それはオオゴトすぎるべ。さすがに、これまで4年近くも動いているコアの部分はイジるべきではないだろう。
it = @configs[:post_paragraph_hook] and it.call(room)
これ、connection.onmessageの延長、つまり、コールバック関数の中で動いている。そんなトコで、さらに「wsclient」で要求を出して応答を待ったって、サーバ側もそこで待っとるっちゅーねん。つまり、要求を処理するヤツが、要求を出して応答を待ってたって、応答するヤツはテメエ自身なんだから、応答が返るわけがない。ぷふゎぁ〜……。
it = @configs[:post_paragraph_hook] and Thread.new {
it.call(room)
}