BF4の各月のプレイ時間取得するスクリプト書いた2015年02月02日すた

このエントリーをはてなブックマークに追加
はてなブックマーク - BF4の各月のプレイ時間取得するスクリプト書いた


いよいよ来月がBattlefield Hardline(BFH)の発売日。
今月くらいはBF4休んでうんぬんかんぬん


EMAIL・PASSWORD・USER_IDに自分の値入れておいて実行するだけ。CSVで出力されるからあとは適当に。
USER_IDはバトルログの自分のステータスページの9桁くらい(適当)の数値。

感覚的に先月やり過ぎたかなって思ってて、
実際に1月が一番プレイ時間長くて草。かなり忙しかったはずなのに。



外資系やらIT系やらの経団連に入っていない企業の本採用がすでに始まってて、1月は面接やらで大分メンタル削られてた。
そのストレスのはけ口としてBF4やってしまった感はある


とりあえずこの時期の就活は一段落で開放感あるけど
2月,3月と論文の締め切りあるゆえん2月は研究に全力出す。

さすがに全くやらないとストレスのはけ口がなくなっちゃうから
2月は28時間以下目標で。

今月はがんばるぞい!なんとしても在学中にもう一度海外行くぞい!



バトルフィールド ハードライン [オンラインコード] [ダウンロード]
エレクトロニック・アーツ (2015-03-19)
売り上げランキング: 231

このエントリーをはてなブックマークに追加
はてなブックマーク - BF4の各月のプレイ時間取得するスクリプト書いた

Arduinoとリードスイッチで部屋の扉の開閉監視2015年01月27日すた

このエントリーをはてなブックマークに追加
はてなブックマーク - Arduinoとリードスイッチで部屋の扉の開閉監視

概要

Arduinoとリードスイッチで部屋の扉の開閉状況のモニタリング始めた。
部屋の扉が開いたらPushbulletでスマホなりMacのChromeなりに通知が来る。

外出中に親が勝手に部屋の掃除するのが嫌で、
何度もやらなくていいとお願いしたけれど
一向にやめてくれないし、明らかにやった形跡あるのにやってないやってないと言うので
それなら開閉モニタリングしておこう、という中学生みたいな動機。


予算:6000円くらい
作業時間:半日


用意するもの

Arudino本体リードスイッチと適当な長い導線さえあれば出来る。
ソースコードはgist


Arduinoをはじめようキット
スイッチサイエンス
売り上げランキング: 644
ProjectBox for Arduino (ブルーエッジ)
スイッチサイエンス
売り上げランキング: 20,842


Arduino上で回路を組む

Arduinoにリードスイッチをつけて、オンオフの値を読み取れるようにする。
作る回路は以下。リードスイッチの両端を2番ポートとGNDにつなげるだけ。




Arduino IDEでコーディング

リードスイッチは磁石が近づくと断線状態となる。プルアップ抵抗がうんたらかんたら。
とりあえず以下のコードで動くので()細かいことは気にしないことにする。


RubyでArduinoとシリアル通信して値を読み込む&通知を送信

RubyでArduinoとシリアル通信して値を読み込む。

通知にはPushbulletを使う。AndroidやPC/MacのChromeに手軽に通知を送れて便利。
自分で開けたのに通知送られても困るので平日の昼間に限定。別途物理的なスイッチをArduino側に追加してもいいけどとりあえず。
環境変数PUSHBULLET_API_KEYに自身のAccessTokenをセットしておく。
#どちらか
$ echo 'PUSHBULLET_API_KEY="MY_ACCESS_TOKEN"'>>~/.zshenv
$ echo 'PUSHBULLET_API_KEY="MY_ACCESS_TOKEN"'>>~/.bashrc




設置

扉が空いたらリードスイッチと磁石が離れるようにうまく設置する。



完成

誰かが扉を開けるとスマホに通知が来る。





終わり

PCが警察に押収されたら(PC周辺の状況が変化したら)任意のコマンドを実行しようとかいうSWATdなるプログラムもあるらしい。

なお扉の開閉監視してまで護りたい物はなかった模様

このエントリーをはてなブックマークに追加
はてなブックマーク - Arduinoとリードスイッチで部屋の扉の開閉監視

socket.io-clientを用いてExpress+Socket.IOで作ったサービスへ同時接続テストを行う2012年10月29日すた

このエントリーをはてなブックマークに追加
はてなブックマーク - socket.io-clientを用いてExpress+Socket.IOで作ったサービスへ同時接続テストを行う

Express3.x(connect2.3+)とSocket.IOでのセッション管理mochaとsuperagentでexpressを使ったサービスのログイン周りのテストの続き。

今回はsocket.ioの同時接続のテストについて。今回は

  • 1000人同時接続が出来るか
  • 正しくメッセージのやりとりが出来るか(チャットなので)
  • ログイン済みユーザーのみが接続できるか
のテストを行なってみました。

socket.io-clientを用いれば、コンソールから同時接続のテストが簡単に出来ます。
ただし、環境によってはOS自体のファイルディスクリプタの最大数を引き上げないと1000人同時接続は難しいです。


ソースコード

今回は今までのを全て含めたものをgithubに上げています。
pxsta / express-socket.io-chat-test


今回の環境

  • Mac OSX 10.7.4 Lion
  • socket.io@0.9.10
  • socket.io-client@0.9.10
  • express@3.0.0rc3


やったこと

ファイルディスクリプタの最大数の変更(環境によっては不要)

1000人同時接続のテストを行う際にはOS自体のファイルディスクリプタの最大数を引き上げる必要があります。
少なくともMacでは256などというとても低い値に設定されているのでそのままでは無理です。

Mac OSX 10.7.4 Lionの環境で1000人接続のテストを行った所、117人あたりで接続を受け付けなくなるか、以下のようなエラーで止まってしまいまいした。
[ERROR] console - Error: EBADF, Bad file descriptor
/node_modules/express/node_modules/connect/lib/middleware/errorHandler.js:68
                .replace('{style}', style)
                 ^
TypeError: Cannot call method 'replace' of undefined
    at /node_modules/express/node_modules/connect/lib/middleware/errorHandler.js:68:18
    at [object Object]. (fs.js:80:5)
    at [object Object].emit (events.js:64:17)
    at fs.js:820:12

Macの場合は
$ ulimit -n
256
$ sysctl kern.maxfilesperproc
kern.maxfilesperproc: 10240
$ ulimit -n  10240
$ ulimit -n  
10240
のようにして変更可能です。ulimitにkern.maxfilesperprocの値を設定すれば最大値まで引き上げられます。


socket.io-clientによる同時接続のテスト(1000人くらい)

socket.io-clientを用いれば同時接続のテストをコンソールから行うことが出来ます。
以下のように、接続自体は今までクライアント側で書いていたものをサーバ側でそのまま使うことが出来ます。
var io = require('socket.io-client');
var options = {
    'force new connection' : true,  //別々のコネクションとして認識させるために必要
    port : 3000
};

//接続
var socket = io.connect("",options);
socket.on('connect', function(data) {
    done("wrong behaviour");
});

//メッセージの送信
socket.emit("chat","send message test");

そのため、複数人接続のテストはmochaと組み合わせて以下のようにして簡単に出来ます。
var should = require('should')
  , expect = require('expect.js');
var options = {
    'force new connection' : true,  //別々のコネクションとして認識させるために必要
    port : 3000,
};

//socket.ioの接続テスト
describe('socket.io test', function() {
    it('1000件接続出来るかどうか。ファイルディスクリプタの最大値を大きくしないと一度に接続できない', function(done) {
        this.timeout(600000);
        var maxCount = 100;
        var clientCount = 0;
        var clients = [];
        
        //全てが接続し終わったら全て切断してから終了
        var end = function() {
            for (var k=0;k<clients.length;k++) {
                clients[k].disconnect();
            }
            done();
        }; 
        
        //maxCount個のクライアントを接続する
        for (var i = 0; i < maxCount; i++) {
            var client = helper.login({
                auth : {
                    userID : 'test' + i,
                    password : 'test'
                },
                server : {
                    host : "",
                    details : options
                }
            }, function(err, client) {
                clients.push(client);
                
                //接続出来たクライアントがmaxCount個に達するまで待つ
                client.on('connect', function(data) {
                    if ((++clientCount) == maxCount) {
                        end();
                    }
                });
            });
        }
    });
});
接続する際のパラメータを
'force new connection':true
と設定しておかないと全てが1つのコネクションとして扱われてしまいます。
パラメータさえ指定しておけば後は繋ぎたいだけforで回して接続すれば出来てしまいます。簡単!


socket.ioの認証まわりのテスト

socket.io-clientではcookieをセットすることができません。

socket.ioの接続認証はexpressのセッションIDをcookieより取得、さらにそのセッションIDを元にsessionStoreからセッションデータを取得、正しいセッションデータがあれば接続を認める、という流れになっています。

そのため、cookieが無いとsocket.ioの認証をそもそも受けられないのですが、
expressやsocket.ioのテストはこんな感じで書いてます、というお話 – アルパカDiary
という素晴らしいエントリーが合ったので参考にさせていただきました。

socket.io-client/lib/socket.js内のio.Socket.prototype.handshakeをsupport/helper.js内でオーバーライドしてしまい、
cookieをx-set-cookieというキーでヘッダに格納しているようです。

support/helper.jsに補助的なメソッドを追加&テスト用にcookieをセットする、という手法を使わせて頂き、
socket.ioの認証テストは以下のように。

var should = require('should')
  , expect = require('expect.js')
  , helper = require('./support/helper')
  , app = require('../app.js')

var options = {
    transports : ['websocket'],
    'force new connection' : true,  //別々のコネクションとして認識させるために必要
    port : app.get('port'),
};


//socket.ioの接続テスト
describe('socket.io test', function() {
    before(function(done) {
        helper.initDataStore(done);
    });
    
    it('ログイン前はsocket.ioで接続できないはず', function(done) {
        this.timeout(10000);
        helper.login({
            auth : {
                userID : 'test',
                password : 'wrong password'
            },
            server : {
                host : "",
                details : options
            }
        }, function(err, client) {
            //接続できてはいけない
            client.on('connect', function(data) {
                done("wrong behaviour");
            });
            
            //エラーが起こるはず
            client.on('error', function(data) {
                //ハンドシェイクのエラーのはず
                should.equal(data.toString(),"handshake error");
                done();
            });
        });
    });
    it('ログインした後はsocket.ioで接続できるはず', function(done) {
        this.timeout(10000);
        helper.login({
            auth : {
                userID : 'test',
                password : 'test'
            },
            server : {
                host : "",
                details : options
            }
        }, function(err, client) {
            client.on('connect', function(data) {
                //接続完了するはず
                done();
            });
        });
    });
});
helper.loginではログインしてexpressのセッションの発行を受けたのち、コールバック関数でsocket.io-clientのsocketインスタンスを返しています。

socket.ioの認証でエラーが起きるとerrorイベントが発火するためclient.on(‘error’, function(data) {})の用にしてそのエラーをキャッチするようにしたいます。
testUserはログイン済みのため接続できてconnectionイベントが発火しますが、invalidUserはログインしていないため接続できずにerrorイベントが発火します。


メッセージ交換が出来るかどうかのテストも以下のように簡単に行うことが出来ます。
    it('メッセージの送受信がclient0とclient1でできるかどうか', function(done) {
        this.timeout(10000);
        var maxCount = 2;
        var clientCount = 0;
        var clients = [];
        
        var connectCompleat = function() {
            clients[0].on("message",function(message){
                //client0が受信したメッセージはclient1が送信したメッセージなはず
                should.equal(message, clients[1].userID + ' ' + "message");
                
                //テスト終了前に切断する
                clients[0].disconnect();
                clients[1].disconnect();
                done();
            });
            clients[1].emit("chat","message");
        }; 
        
        for (var i = 0; i < maxCount; i++) {
            (function(userID) {
                var client = helper.login({
                    auth : {
                        userID : userID,
                        password : 'test'
                    },
                    server : {
                        host : "",
                        details : options
                    }
                }, function(err, client) {
                    client.userID = userID;
                    clients.push(client);
                    
                    //client0とclient1が接続するまで待つ
                    client.on('connect', function(data) {
                        if ((++clientCount) == maxCount) {
                            connectCompleat();
                        }
                    });
                });
            })('test' + i);
        }
    });

1000人同時接続のテストの際にファイルディスクリプタ最大数が原因だと気づかずにはまってしまいましたが、
それさえ忘れなければ普段クライアントで利用しているコードがサーバ側でも使えるのは便利ですね。


前々回・前回・今回のサンプルコードを全て含めたものはpxsta / express-socket.io-chat-testです。

このエントリーをはてなブックマークに追加
はてなブックマーク - socket.io-clientを用いてExpress+Socket.IOで作ったサービスへ同時接続テストを行う

mochaとsuperagentでexpressを使ったサービスのログイン周りのテスト2012年10月29日すた

このエントリーをはてなブックマークに追加
はてなブックマーク - mochaとsuperagentでexpressを使ったサービスのログイン周りのテスト

前回のexpress+soket.ioでのセッション管理に続いて、今回はexpressのセッション管理のテストについて。

mochasuperagentを使えば

  • ページの表示確認(ステータスコード・ページbody本文)
  • ログインページヘのpostで正しくログイン出来るか
  • ログイン状態管理(セッションでの振り分けが出来ているか・リダイレクトが正しく行われているか)
などのログイン周りのテストが簡単に出来るようなので試してみました。

ソースコードとサンプル

コード
mochaとsuperagentを用いてexpressのアプリのログイン周りのテスト — Gist
.
├── app.js
├── chat.html
├── index.html
├── node_modules
│   ├── connect-redis
│   ├── cookie
│   ├── ejs
│   ├── express
│   ├── should
│   ├── socket.io
│   └── superagent
├── package.json
└── test
     ├── app.test.js
     └── mocha.opts


テスト対象の動作サンプル
Socket.io-Express3.0.0rc3 session sample
簡単なチャット。passwordがtestなら何でもログイン可能。やけに時間がかかるのはherokuだから?


やったこと

インストール

npm install mocha
npm install superagent
npm install should
shouldもとりあえず。
mochaについてはテストフレームワーク mocha – hokaccha.hamalog v2がとても参考になりました。

app.js側の準備

基本的に前回とほとんど同じですが、expressの設定とsessionStoreをテストコードから参照するために2行ほど書き換えます。
var app = module.exports = express();
app.configure(function() {
    //テスト用
    app.set("sessionStore",sessionStore);
});


ログインが必要なページのテスト

まず、/chatにアクセスするためにはログインが必要。
ログイン前に直接アクセスすると/にリダイレクトされるはずです。そのテストを行うコードは次のようになります。
var url = require('url')
  , should = require('should')
  , assert = require('assert')
  , superagent = require('superagent');

describe('HTTP Server Test', function() {
    //テストに使用するユーザーを作成
    var testUser = superagent.agent();
    
    it('ログイン前にチャットページにアクセスしたらログインページにリダイレクトされるはず', function(done) {
        testUser.get('http://localhost:3000/chat').end(function(err, res) {
            should.not.exist(err);
            res.redirects.should.not.be.empty;
            should.equal(url.parse(res.redirects[0], false, true).pathname, "/");
            done();
        });
    });
});
superagent.agent()でユーザーを作成して、
getでHTTP GETの要求を/chatにした後リダイレクト先が/であることを確かめています。

testディレクトリにapp.test.jsなどという名前で保存してmochaを実行すれば
$ mocha
   info  - socket.io started

  ․

  ✔ 1 test complete (17ms)
などと表示され無事にテストが通ったことが分かります。

ログイン処理のテスト

次に、/ページではログインを行います。
ログインフォームは
<form action="/user/login" method="post">
    ID: <input type="text"  name="userID" value="test"/> <br/>
    PASS: <input type="text"  name="password" value ="test"/>
    <input type="submit" value="login" />
</form>
postを受け付ける部分は
//ログイン処理
app.post('/user/login', function(req, res) {
    var postData = {
        userID : req.body.userID,
        password : req.body.password
    };
    //passがtestならログイン成功させる
    if ( typeof postData.userID !== 'undefined' && typeof postData.password !== 'undefined' && postData.password.toString() === 'test') {
        //sessionにユーザーID保存
        req.session.userID = postData.userID.toString();
        res.redirect('/chat');
    }
    else {
        console.log('login failed');
        res.redirect('/');
    }
});
のようになっていて、/user/loginにHTTP POSTでuserID, passwordを送るようになっています。

superagentではユーザーごとのcookieの管理が出来るため、セッション管理のテストもできます。
このログイン処理のテストを行うコードをmocha+superagentで書くと以下のように。
describe('HTTP Server Test', function() {
    //テストに使用するユーザーを作成
    var testUser = superagent.agent();  //正規にログインするユーザー
    var invalidUser = superagent.agent(); //ログインしないユーザー

    it('ログインに成功したら/chatにリダイレクトされるはず', function(done) {
        //ログインページに対してのPOST送信でログイン処理を行う
        testUser.post('http://localhost:3000/user/login').send({
            userID : 'test',
            password : 'test'
        }).end(function(err, res) {
            should.not.exist(err);
            res.redirects.should.not.be.empty;
            should.equal(url.parse(res.redirects[0], false, true).pathname, "/chat"); 
            done();
        });
    });

    it('ログイン済みのtestUserは/chatに直接アクセスすると/chatにアクセスできるはず', function(done) {
        testUser.get('http://localhost:3000/chat').end(function(err, res) {
            should.not.exist(err);

            //ページが表示され、それは/chatなはず
            should.equal(res.statusCode, 200);
            should.equal(res.req.path, "/chat");
            done();
        });
    });

    it('ログインしてないinvalidUserは/chatに直接にしても/にリダイレクトされるはず', function(done) {
        invalidUser.get('http://localhost:3000/chat').end(function(err, res) {
            should.not.exist(err);

            //リダイレクト先が存在し、/にリダイレクトされるはず
            res.redirects.should.not.be.empty;
            should.equal(url.parse(res.redirects[0], false, true).pathname, "/");
            done();
        });
    });
});

testUserとinvalidUserの2ユーザーを作り、testUserは.post(‘user/login’).send({userID : ‘test’,password : ‘test’})でログイン処理を。
testUserはログイン済みのため/chatにアクセスできますが、invalidUserは未ログインなため/にリダイレクトされます。

セッションストアにデータが収められているかのテスト

最後に、sessionStoreに正しくセッション情報が保存されているかどうかのテスト。
ヘッダのcookieからセッションIDを取り出して、それを元にセッションストアからセッションデータを取り出してユーザー名を確かめます。
これはsocket.ioの認証時にも同じ事をしています。

var url = require('url')
  , connect = require("express/node_modules/connect")
  , should = require('should')
  , assert = require('assert')
  , superagent = require('superagent')
  , app = require('../app.js');


describe('HTTP Server Test', function() {
    //テストに使用するユーザーを作成
    var testUser = superagent.agent();

    it('ログインに成功したらセッションストレージにユーザー名が保存されているはず', function(done) {
        testUser.post('http://localhost:3000/user/login').send({
            userID : 'test',
            password : 'test'
        }).end(function(err, res) {
            should.not.exist(err);
            
            //sessionStoreにsessionデータが保存されているはず
            var header_cookie = res.req._headers.cookie;
            var cookie = require('cookie').parse(decodeURIComponent(header_cookie));
            cookie = connect.utils.parseSignedCookies(cookie, app.get('secretKey'));
            var sessionID = cookie[app.get('cookieSessionKey')];
            
            app.get("sessionStore").get(sessionID,function(err,session){
                 should.not.exist(err);
                 
                 //userIDがログイン時のものと一致するはず
                 should.equal(session.userID,"test");
                 done();
            });
        });
    });
});
sessionStoreにセッション情報が保存されていることを確認。

と、いうことで、mocha+superagentを使えばログイン処理まわりのテストも簡単に出来そうです!
superagentを使えば複数人の接続のテストも出来るかと!

今回のサンプルコードはここにまとめて置いておきました。
https://gist.github.com/3968061

このエントリーをはてなブックマークに追加
はてなブックマーク - mochaとsuperagentでexpressを使ったサービスのログイン周りのテスト

Express3.x(connect2.3+)とSocket.ioでのセッション管理2012年10月29日すた

このエントリーをはてなブックマークに追加
はてなブックマーク - Express3.x(connect2.3+)とSocket.ioでのセッション管理

久しぶりにnode.jsを触ったらexpressがいつの間にか2.xから3.0.3rcになっていて微妙に挙動が違う!
そしてconnect@2.3.2からはparseCookieが無くなってて昔のままだとセッションの共有が出来ない!


そして一番ハマったのはセッションIDが署名されているにも関わらずcookieからparseCookieで取り出した値をそのままセッションIDとして使っていたために、
req.sessionIDとcookieから取り出したsessionIDが別のものになっていて
sessionStoreから正しくセッションデータを取り出せなかった、ってこと。
署名したセッションID入りのcookieはconnect.utils.parseSignedCookies(cookies:object, secret:string)で元に戻しましょう。

と、いうことでexpress3.xとsocket.ioを使った簡単なチャットを新たに作ってみました。

今回の環境

  • node.js v0.8.7
  • express@3.0.0rc3
  • connect@2.4.3
  • socket.io@0.9.10

ソースコードとサンプル

コード
Socket.io-Express3.x-session — Gist

サンプル
Socket.io-Express3.0.0rc3 session sample
簡単なチャット。passwordがtestなら何でもログイン可能。やけに時間がかかるのはherokuだから?
(herokuってこんなに遅いんですか?)

やったこと

connect2.3以降はparseCookieがconnect内に無いため、変わりにcookieモジュールを使う。npmから普通に入る。
npm install cookie

express3.xからはexpress()がhttp.Server型のオブジェクトを返さなくなったため、http.createServerでhttp.Server型のインスタンスを生成してからsocket.ioに渡す。
var express = require('express')
  , app = express()
  , http = require('http')
  , io = require('socket.io').listen(http.createServer(app).listen(3000));

また、express.sessionミドルウェアがsecretパラメータを使用しなくなり、変わりにexpress.cookieParserがsecretパラメータを受け付けるようになった。
secretパラメータの値を元に、expressのセッションが署名される。
//メモリストアかRedisのどちらかでセッションを保存
//var sessionStore = new express.session.MemoryStore()
var RedisStore = require('connect-redis')(express)
  , sessionStore = new RedisStore();

app.configure(function() {
    app.set('secretKey', 'mySecret');
    app.set('cookieSessionKey', 'sid');

    //expressでセッション管理を行う
    app.use(express.cookieParser(app.get('secretKey')));     //セッションの署名に使われるキーを設定
    app.use(express.session({
        key : app.get('cookieSessionKey'),     //cookieにexpressのsessionIDを保存する際のキーを設定
        store : sessionStore
    }));
});
express.cookieParserに渡した値でセッションが署名される。
また、express.sessionのkeyパラメータはブラウザにセッションIDが保存される際のキーとなる。
storeに指定したストレージにセッション情報が保存される。今回はRedis。


そして肝心のsocket.ioの認証部分。ここでsocket.ioとexpressのセッション情報を紐つける。


今までのようにconnectのparseCookieが使えないため、cookieモジュールのparseCookieを用いる。
また、cookie中のセッションIDは署名されているため、単にparseCookieに掛けただけではreq.sessionIDと異なるセッションIDしか取れない。sessionStoreにはreq.sessionIDと同じ値がキーとなって保存されているためそれは不都合。

res.cookie(name, val, options)にてcookieをセットする際にutils.sign(val, secret)にて署名が行われ、セッションIDは
s:' + セッションID + '.' + secretのハッシュ値
という形に変換されている。
そのためpaeseCookieだけでは「s:本来のセッションID.ハッシュ値」という値しか取れない。

さらに、handshakeData.headers.cookieはURLエンコードされているので、
s%3A:' + URLエンコードされたセッションID + '.' + secretのハッシュ値
というように頭の「s:」 が「s%3A」となって格納されている。

そこで、decodeURIComponentでURLデコードしてたのち同じくutils.js内のutil.parseSignedCookiesを用いて本来のセッションIDを取得する。


ということでsocket.ioの認証部分。parseSignedCookiesにcookieのオブジェクトと署名に用いた値を渡すだけ。
//socket.ioのコネクション認証時にexpressのセッションIDを元にログイン済みか確認する
io.set('authorization', function(handshakeData, callback) {
    if (handshakeData.headers.cookie) {
        //cookieを取得
        var cookie = require('cookie').parse(decodeURIComponent(handshakeData.headers.cookie));
        //cookie中の署名済みの値を元に戻す
        cookie = connect.utils.parseSignedCookies(cookie, app.get('secretKey'));
        //cookieからexpressのセッションIDを取得する
        var sessionID = cookie[app.get('cookieSessionKey')];

        // セッションデータをストレージから取得
        sessionStore.get(sessionID, function(err, session) {
            if (err) {
                //セッションが取得できなかったら
                console.dir(err);
                callback(err.message, false);
            }
            else if (!session) {
                console.log('session not found');
                callback('session not found', false);
            }
            else {
                console.log("authorization success");

                // socket.ioからもセッションを参照できるようにする
                handshakeData.cookie = cookie;
                handshakeData.sessionID = sessionID;
                handshakeData.sessionStore = sessionStore;
                handshakeData.session = new Session(handshakeData, session);

                callback(null, true);
            }
        });
    }
    else {
        //cookieが見つからなかった時
        return callback('cookie not found', false);
    }
});


だらだらと書いてきましたが、結局のところ
app.config(function(){
    app.use(express.cookieParser("署名に使うキー"));
    app.use(express.session({
        key : "expressのセッションIDがcookieに保存される際のキー(デフォルトはconnect.sid)",
        store : sessionStore
    }));
});
のように署名に使うキー、cookieに保存される際のキー、ストレージを設定して、
socket.ioの認証部分で
//ヘッダーからcookie取得
var cookie = require('cookie').parse(decodeURIComponent(handshakeData.headers.cookie));
//cookie中の署名済みの値を上で設定した'署名に使うキー'を元にして戻して
cookie = connect.utils.parseSignedCookies('署名に使うキー');
var sessionID = cookie[app.get('cookieSessionKey')];
署名されたcookieからconnect.utils.parseSignedCookiesで元のsessionIDを取得しましょう、ただそれだけです。
cookieからparseCookieして取得したsessionIDとreq.sessionIDが違うせいでsessionStoreからsessionデータを取得できない、エラーはないけどundefinedになる、というのに中々気づけず結構ハマったので。

このエントリーをはてなブックマークに追加
はてなブックマーク - Express3.x(connect2.3+)とSocket.ioでのセッション管理

オーストラリア行って来ました2012年10月07日すた

このエントリーをはてなブックマークに追加
はてなブックマーク - オーストラリア行って来ました

ということで夏休み最終週に5泊7日でオーストラリア行って来ました。

夏休み中、バイトやらインターンやらでプログラミングしかしてなかったし、長い休み取れるの最後っぽかったから勢いで!

人数はもちろん1人&ツアーとかは無しで!

心細い場面もあったけど、やっぱり誰かに気を使うことなく自由気ままに行動できるのはかなり気持ちいいですね〜


とりあえず日曜日の夜の飛行機で台湾経由でオーストラリアに。

成田〜台湾までが3時間、台湾からシドニーまでが10時間くらいで、だいたい13時間くらいですかね。


飛行機はちょっと心配なチャイナエアラインでwでも機内食普通に美味しいというね!

で、翌日の11時ごろにシドニー到着!

到着した時(やべーついに来ちまったww)みたいなワクテカ感はやばかったw

 
 

で、まあ、シドニー、普通に都会って感じでした

人多すぎて写真とってないけど、シドニー中心部のcity周辺なんかは渋谷って感じ。

歩道にはこれでもかってほど人がいて、しかもみんな早歩きだから落ち着かないw

信号が全部押しボタン式だったけど、何故かみんな連打してた。どんだけせっかちなんだよw

あと、タンクトップの人からコート着てる人、中国人から黒人まで、いろんな人が居ました。そして寒かったw

——————————————————————————————————————————————–

で、さっそく1日目の行動開始。

スーツケース邪魔だったから早めにホテルにチェックインしたかったんだけど・・・

紙の地図使うのが久しぶり過ぎて2時間近く歩きさまようというねww


ネット繋がらないからGoogleMap使えないし、1人だから誰かに相談することもできないし、正直焦ったww

空港の2つ隣の駅のセントラル駅近くのホテルにしたけど、
人多いし、皆せっかちに歩いてるわで、あんまり人に聞けるような雰囲気でもなかった。

で、まあ結局、清掃活動?してる人に地図見せて、「ここ行きたいんですけど〜」的なこと言ってなんとかホテル到着!

チェックインの時の英語はplease speak more slowly 連呼しつつもなんとか。


見知らぬ土地での落ち着ける場所を確保出来た時の安心感と言ったらもうねw


—————————–

その後はネット環境を手に入れるべくTelstraの店舗を探しに。



これまた店舗探すのに1時間くらいかかって、
さらに店の中に実物がおいてなくて、しょうがないから店員にpocket wifi的なものがあるはずだからそれ下さいって頼んで、
奥から持ってきてくれたと思ったら今度はレジの店員がくっそ早口でわけわかめw

購入〜アクティベートまでさらに1時間くらいかかったかなw

で、telstraのPocket wifi的なもの+プリペイド5GBの3G回線げっと!これがあれば何の不自由なしにスマホやらMBAからネットにアクセスできます。

3G回線を通信量5GBまで使えて、端末+プリペイド3G回線で70$前後だったと思います。

 

MBAもiPadもGoogleMapもOK。
多分、これがオーストラリア旅行の中で1番役に立ったものかなwGoogleMap使えるのは大きいw


店舗探しやら店員との格闘で疲れて、向かいにあったマクドナルドで休憩。

日本のチーズバーガーセット的なものが$4ちょいだったかな?サイズ・値段はあんまり変わらない。
店員さんかわいすぎわろりんぬって感じだったw

で、結局、ホテル探し&telstra探しで1日目終了w先が思いやられる1日目でした。
———————————————————————————————————————

2日目は朝からシドニーの市内観光。


市内は電車で移動。7日間乗り放題のパスが$50くらい。
シドニー市内には電車の駅がたくさんあるし、乗り放題パスがあるから適当に歩いててもどこかしらの駅に付いてホテルまで戻れる。


シドニータワーからとか。普通に高層ビル多い。



オペラハウスとハーバーブリッジ見て、ああシドニー来たんだなってさらに実感w


夜景もなかなか!


動物園。定番のコアラ


写真じゃ大きさ伝わらないけど、かなり大きくて$3くらい。安くてお腹いっぱいになれる。

2日目はこんな感じですかね。
正直、市内は人多くて結構疲れましたw

そして更に、シドニー地味に寒いw
最高気温が22度前後で、あまり気温が上がらない。普通に長袖着てたけど風が冷たかったかな。


で、向こうにもスーパーマーケット的なものが。


野菜から生活食品、そして日本のお菓子やら味噌汁の元なども売ってました。
そして日本人の方も普通に店内に居ましたw


持ち運び・保存に便利なクッキーとパンを購入。
シドニー、いろんな所に広めの公園があって、ベンチに座って休憩するときにパンなどがあると重宝しました。

———————————————————————————————————————

3日目はちょっと遠出。ブルーマウンテン。

シドニーから電車で2時間くらいですかね。



3時間近く山道を歩いたわけですが、久しぶりに自然の中歩いたって感じで気持ちよかった!

人多すぎの市内とは大違い!




そして眺めよすぎ!


ふもとの町は、さすがにシドニーから2時間の距離ってだけあって、落ち着いた雰囲気でした。

それとお土産用にユーカリキャンディ購入。

3日目はこんな感じですかね。

夜にまたスーパーに繰り出してパンやら飲み物やらクッキーを購入。

コーラの500mlペットボトルが$4前後だったのは日本と比べるとかなり高いですねw

———————————————————————————————————————
4,5日目は待ちに待ったエアーズロックへ!


シドニーからエアーズロックまでは飛行機で3.5時間くらいかかったかな。
地味に遠いwというかオーストラリア広すぎw


で、本当はエアーズロック登りたかったのに雨降りそうだからってことで登山道閉まってました・・・;;
正直これはかなり残念でした・・・。


登山道。登れる時はこの先の頂上に人が居るんだとか・・・。
写真だと大したことなさそうだけど実際は45度の細い急斜面をチェーン頼りに登って行くんだとか。


ということで麓歩き!



見渡す限り砂地というか岩地というか。そして日差しが強くてさすがにサングラスかけましたw



暑いし砂地だけど、岩の麓には水場も。




近くで見るとかなり大きいし、ゴツゴツしてました。


普通にトカゲとかもいました。何食べるんだろう


結局エアーズロックでは1泊のみ。シドニー&日本に帰らないと行けないので;;
登れなかったのが残念だったけど、あたり一面砂地なのは新鮮だったり、間近でエアーズロック見れたときは嬉しかったですね。

ちなみに街頭なんてものは勿論無いので日没後は真っ暗でした。星とか見えまくりんぐ


で、5日目の14時くらいの飛行機で再びシドニーに戻って・・・1,2,3日目とは違うホテルにチェックイン


$199/1泊のところ(実際は$60/1泊で予約出来たw)だけあって、朝食付きだったりテレビ付いてたりしてなかなか快適でしたw

———————————————————————————————————————
6日目は最終日!22時の便に乗る予定だったから遠出はせずに市内をブラブラと。


なんとかマーケット。セントラル駅から歩いて行けた。
お土産用のガラクタとかたくさん売ってました。

そのあとはオペラ・ハウス近くの駅で降りてまたお土産探したり、公園入って休憩したり、空港内でも買い物したり。



バイト先用にTimTamたくさん買ったり、自分用にコアラの小さいぬいぐるみ買ったりストラップ買ったりペンケースやらステッカーやら!

—————————————–
で、22時(GMT+10)の飛行機に乗って、台湾に着いたのが6時(GMT+8)くらい。だいたい10時間くらい。

台湾桃園国際空港綺麗だった!んだけど特に買うもの無しw
どこかのブースにでかい液晶テレビ置いてあって、しかもなぜかNHK流れてたから1時間くらい時間潰したw

で、台湾からまた飛行機3時間乗って無事帰国!
いやあ、シドニー寒かっただけに日本暑いですね!!!w



そしてオーストラリア遠すぎ!w
出発が夏休み最終週だからってのもあったけど5泊7日とかあっという間すぎ!!もう少し向こうに居たかったかな。

——————————————–
なんだかんだで海外行ったの初めてだったし、ツアーなしの1人旅だったから心細くなる場面はあったけど、
誰にも気を使うことなく行動出来たしバイトのことやら大学のことから開放されてかなりリフレッシュできました。

1人旅って言っても、オーストラリアならネットガンガン繋がったから特に不便は感じなかったかな。ホテルの予約もネットでできるし、オーストラリアならもちろん英語通じるから全く会話ができないってわけじゃないですし。

ちなみに最終日のホテルは現地でオンライン予約しましたw
MBAたん大活躍でした


英語の聞き取りが全然出来なかったのは残念だったかな。
勉強してないから当然っちゃ当然だったけど、次行くときはもっと勉強してからがいいですね。


次は・・・タイとかベトナムみたいな日本から近くてもう少し自然豊かなところに行ってみたいですね!
いずれにしても英語の勉強が必要なんですけどね><


いずれにしても夏休み最終週にリフレッシュ出来たのはすごくよかったです!楽しかったまた行きたい!今度こそエアーズロック登りたい!今度の拠点はケアンズで!

このエントリーをはてなブックマークに追加
はてなブックマーク - オーストラリア行って来ました

オーストラリア行ってきます!2012年09月23日すた

このエントリーをはてなブックマークに追加
はてなブックマーク - オーストラリア行ってきます!

夏休みもいつの間にか最終週・・・なんだけど、


今日からオーストラリア行ってきます!
5泊7日!


もう少ししたら搭乗かな。
日本は雨だけど向こうは晴れてるはず!

10/1に実験のガイダンスさえなければもっと滞在出来たのにね!www





このエントリーをはてなブックマークに追加
はてなブックマーク - オーストラリア行ってきます!

Canvas+node.js+socket.ioで簡易オンラインゲーム作ってみた2011年12月19日すた

このエントリーをはてなブックマークに追加
はてなブックマーク - Canvas+node.js+socket.ioで簡易オンラインゲーム作ってみた

Canvas+node.js+socket.ioで簡易オンラインゲーム作ってみました!
ほんとに簡易!


もともとはサークルの交流会のLTデモのためにちょちょっと作ったものなのでゲーム性はほとんどないです。
作成時間は半日くらい。その後の細かな手直しに4時間くらい。

js暦2ヶ月、node.js暦1ヶ月という短さですが、node.jsとsocket.ioの手軽さには本当に感服です!

サンプル

とりあえずブラウザでアクセス
Chrome16、Firefox7で動作確認。表示だけならAndroid2.3でも可。
※Avastなどのウイルス対策ソフトが動いてるとサーバーと接続できないみたいです

2chのプログラミングスレで晒してもなんとか動いてたので、たぶん、普通に動くはずです。

操作方法

矢印キーで移動、スペースキーで加速
物理をすっかり忘れてしまって未知なる力で動かしてるので、加速回りは怪しいかも

CPUもいますが勝手に自殺しちゃうので、
Chromeのタブ+シークレットウィンドウ使うか、Firefox+Chromeでアクセスすれば自分対自分ができます。

開発環境

node.js 0.6.0
socket.io 0.8.7
express 2.5.1

ソース

最近git使い始めたので、なんとなくgithubにもあげてみました。
https://github.com/pxsta/OnlineBallAtack-socketio

仕様

サーバ側でゲームのメインループまわして移動量計算・あたり判定、
クライアント側は定期的にボールの位置情報を受信してCanvasに描写してるだけです。

ユーザーの識別にはSocket.ioのセッションIDを使用。
再接続/リロード時のためにExpressのセッションIDとSocket.ioのセッションIDを紐つけています。
ExpressのセッションIDはサーバのみが保持して、クライアント側には他クライアントのセッションIDは知らせていません。

サーバ(メインループ部のみ)

src/server.js
//メインループ
var run = function()
{
    setInterval(function(){
           update();
           sync();
        },1000.0/MyApp.config.FPS);
};

//ゲームデータの更新
var update = function()
{
    var ballArray = MyApp.ballList.toArray();
    for(var i=0;i<ballArray.length;i++){
        //ここであたり判定とかボールが落ちてないかの確認とか
    }
    
    //各ボールを現在のスピード等に基づいて移動させる
    MyApp.ballList.update(param);
};

//クライアントに同期させる
var sync = function(){
    var ballArray = MyApp.ballList.toArray();
    var sendMessage = {ballList:MyApp.ballList.toJSON()};
    
    for(var i=0;i<ballArray.length;i++){
        //各クライアントにすべてのボールの位置情報を送る
        var socketID = ballArray[i].getSocketID();
        io.sockets.socket(socketID).volatile.emit("updateBallInfo",JSON.stringify(sendMessage));
    }
}

サーバ側で、updateとdrawを定期的に実行というありがちなメインループをまわす。
今回はdrawをsyncに変えて、描写の代わりにクライアントにゲームの状態を送信。

クライアント

src/client/client.js
//サーバからボールの情報を受信したときのイベントハンドラ
connection.on('updateBallInfo', function (msg) {
    var messageJson = JSON.parse(msg);
    
    //ボールの状態を更新する
    MyApp.ballList.get(messageJson.ballList[i].socketID).updateValue(messageJson.ballList[i]);    
});

//メインループ
var run = function()
{    
    MyApp.mainLoopID=setInterval(function()
    {
        update();
        draw();
    },1000.0/MyApp.config.FPS);
};

var update = function(){
    //落ちたボールをリストから削除したり
    //自分に落ちてるフラグがセットされてたら画面メインループ停止させたり
    //サーバから受信した各ボールの情報をもとにゲームオーバー判定とかするだけ。
};

//canvasに描写する
var draw = function()
{
    var ctx = MyApp.context;
    var offset = MyApp.getOffset();
    
    //Canvasをゼロクリアする
    ctx.clearRect(0,0,MyApp.canvasSize.width,MyApp.canvasSize.height);


    //マップを描写
    MyApp.map.draw(ctx,MyApp.getOffset());
    
    // ボールを描写
    MyApp.ballList.draw(ctx,MyApp.getOffset());
     
};
“updateBallInfo”としてサーバから定期的に送られてくるボール情報を受信して、ローカルに各ボールの状態を更新・保存、
その状態を元にボールを描写。

ボールの移動

src/client/client.js
$(window).keydown(function(e){
    //サーバにキー押下情報を送る
    connection.emit("keydown",code);
}
ボールの移動は、
クライアント側はキーイベントを監視して押されたボタンの情報をサーバに送るのみ。

src/server.js

//クライアントがキーを押した時
connection.on('keydown',function(code){
    var moveVector={x:0,y:0,mode:"normal"};
    if(37<=code&&code<=40||code==32){
        if(code==37){
            moveVector.x+=(-1);
        }
        else if(code==39){
            moveVector.x+=1;
        }
        else if(code==38){
            moveVector.y+=(-1);
        }
        else if(code==40){
            moveVector.y+=1;
        }
        else if(code==32){
            //ターボ
            moveVector.mode="tarbo";
        }
    }
    //ボールの行動バッファに加える
    MyApp.ballList.get(connection.id).setActionBuffer(function(){
        MyApp.ballList.get(connection.id).addSpeed(moveVector);
        MyApp.ballList.get(connection.id).setMode(moveVector.mode);
    });
});
サーバ側は受信したキー押下情報を元にスピードの上げ下げ。
バッファに1つだけ保存して、次のupdate内で実行。

クライアントからkeydownメッセージ受信した時点でスピード更新してもいい気がしたけど、
あるクライアントはメッセージ送信が1ms/回、あるクライアントは500ms/回とかだとまずい気がしたのでバッファに1つだけ保存して、updateで定期的に実行するようにしました。

とりあえず

updateとdrawの定期実行というよくあるメインループをサーバ側でやらせて、
drawをsyncに変えて描写の変わりにクライアントにゲーム状態を送るようにして、
クライアントではupdateの代わりにイベントハンドラでゲームの状態を受信して、それをdrawで定期的に描写してるだけです。

チート的なことをされないために、あたり判定などのメインループのコードはサーバ側だけに書いてます。
ボール移動の際も、クライアントからキーコードを送ってもらうだけで、具体的な処理はサーバ側で行っています。

ballAtack.jsでボールの基本的なクラスを定義して、serverBallAtack.jsでballAtack.jsの各クラスを継承して、ExpressのセッションID・ボール移動処理などのサーバのみに必要な値・メソッドを追加しています。

問題点

  • サーバは60FPSでゲームの状態を送信してますが、それに通信速度が追いつかないと問答無用でカクカクします。
  • 通信途切れたら動けません(再接続はできます)
  • クライアントからsocket.emit(“hoge”,”あばばばば・・・(1MB分続く・・・)”)みたいなの実行されて巨大なデータ送られ続けたらどうなるんだろう?
これでも読めば何か変わるんでしょうかね。


敷居の低さ

そもそもこれを作ったのは大学のサークル同士の交流会のLTのためなのですが、

前日になって、
スライドできてない><→node.js+socket.ioのことでも話すか!→デモでも作って穴埋めしよう→これならサクッと作れそう!
という単純な動機で作りました。

js暦2ヶ月、node.js暦1ヶ月ちょいの自分ですが、それでも半日程度で作ることができました。
そもそも、通信部分はサーバ/クライアント合わせてわずか100行ちょっとで、その他は普通のゲームと同じです。

ゲームに限らず、webサービスでリアルタイムなコンテンツをサクッと追加するのにも使えそうです!
何かおもしろいものないかなあ。

このエントリーをはてなブックマークに追加
はてなブックマーク - Canvas+node.js+socket.ioで簡易オンラインゲーム作ってみた

MacBook Air11インチ届きました2011年11月28日すた

このエントリーをはてなブックマークに追加
はてなブックマーク - MacBook Air11インチ届きました

ということでMBA11インチ特盛り届きました!先週の月曜に。


前々からほしいと思っていたのですが、まあうだうだ悩んでるなら欲しいと思ったうちに買ってしまおうということで!
DHDのときもそうでした。

Pro/Mini持ってないゆえん、スペックは特盛り(i7/4GB/256GB)にしました。
この1週間ちょびちょびと使ってみましたが、やはり買ってよかったです!

当然のことながら軽い、軽すぎる。そして小さい!(そして地味に今までの白MB13インチよりも解像度高いのね)

かばんにスッと入ってスッと出せてそもそも背負ってるのか分からないくらいの重さに感動
「あーどうして動かなかったんだろう」、ってモヤモヤしてる時とかも帰りのバスでスッとだせて便利です。

メモリ4GBなのは気になったけどSSDのおかげか、スワップも気にならず。
MB13インチ、メモリ2GBのときは死ぬ思いしてました。まじで。

バッテリーは公称5時間で正直、短め。
EclipseでやらMonoDevelopで作業して4時間前後でしょうか。

まあそこらへんはhyperjuiceで補えそう。
モバイルブースター3つ持ち歩くのと同じくらいの重さですかね。普段2つゆえんまあそこは気にならないかな。

そして家ではコスパ抜群のDELLの27インチIPS液晶に繋ぐことでかなり捗ります。2560×1440。

気になるのは、重たい作業させたときに熱くなってファンもうるさくなったり、(クリアカバーで覆ってるせいもある?)
そして本体が薄い分、もちろんキーボードも浅い。

でもその他動作面ではかなり満足!

そして実は11インチと13インチで1週間くらい迷っていました・・・w

が、ProならまだしもAirで13インチは大きくもなく小さくもなく中途半端な気がしたのと、
近くの電気屋で何度か実機触ってみて、11インチの小ささに改めて憧れてしまって最終的に11にしました。

そしてやはり11インチにしてよかったです!かばんにスッと入ってスッと出せるし重さを感じさせないのは本当に驚きでした。
まだ買ってから1週間ですが、動作に不満もなし。買ってよかったです!

これでnode.jsとruby捗るで!!乞うご期待

このエントリーをはてなブックマークに追加
はてなブックマーク - MacBook Air11インチ届きました

MAMI KAWADA m.a.l.l LIVE 2nd stage 1日目行ってきました2011年11月28日すた

このエントリーをはてなブックマークに追加
はてなブックマーク - MAMI KAWADA m.a.l.l LIVE 2nd stage 1日目行ってきました

ということで半年ぶりの川田まみのライブに行ってまいりました。

今回はバラードも多かったし新曲もあって、やっぱりやっぱり川田まみすごくよかった!!
そして聞きたかったIMMORALも聞けて文句なしでした!!

そして今回もまさかの3列目というね!!

となりは身長低めのおにゃのこだったけどマスクしてて顔が見えなかったwそして低いながらジャンプしまくっててちょっと驚いたw

序盤はバラード多めな感じ。
風と君を抱いてとか明日への涙とか雨とか。

最初は8曲くらい連続で来た気がする。長いww

で、MC。
OUT FLOWリリース後の打ち上げで、トイレで寝てしまったんだとか。なんだかツンとしたイメージとあわなくてふひひww

そしてこの11月で「川田まみ」としてデビューしてからちょうど10年、
これからも新しいことにどんどん挑戦していくつもりなんだとか。

ということで、自らギター持ちつつ弾き語り!w

緋色の空やらSee visionSやらNo buts!来てそれはもう激アツww
緋色の空は何度聞いてもやっぱりいいですね!!

とか思ってたらあっという間に
「その次これで最後になります。これからもよろしくお願いしますー。」

まじですかー!もうそんな時間ですかい
といいつつももちろんアンコール

そしてアンコールでまさかのIMMMORAL!!!

これが一番聞きたかったゆえん
なんか思わず鳥肌立ってしまったww

しかも服装がなんか露出度高くなってたw
その格好で「インモラル・・・インモラル・・・」とかボソボソ言うのはなんかえろかったw

そしてまあなんと今回のライブ初出の新曲もありまして!!
しっとり系の流れる感じでですごくよかったですね。

バラードの分量多目でしたがやはり川田まみのしっとり系はいいですね。
寒い中行ってよかった!来年もぜひぜひワンマンライブを!

そしてななときのOPフル、EDに期待!

——————————————————–
ちなみにこんな感じでした。

風と君を抱いて
明日への涙
intron tone
Beehive
TRILL

空の森で
a frame

-ギター弾き語り-
dilemma
緋色の空
sence

See visionS
masterpiece
No buts!
portamento

アンコール
IMMORAL
Half way(新曲)
CARPE DIEM

このエントリーをはてなブックマークに追加
はてなブックマーク - MAMI KAWADA m.a.l.l LIVE 2nd stage 1日目行ってきました

Dansette