読者です 読者をやめる 読者になる 読者になる

JuniperのNetwork Connect利用時に外部にアクセスする

プログラミング

はじめに

私の大学では学内に専用のネットワークがあり、学生が学外から安全な経路でアクセスできるようにするために、Juniper Networks社の製品を導入してVPN接続サービスを提供しています。VPNに接続する際はWeb上のログイン画面でユーザーIDとパスワードを入力、接続モードを全ての通信でVPNを経由するNetworkConnect、ログイン後のWebシステム上でのみ学内にアクセスできるWebモード(リバースプロキシ)から選択してログインする必要があります。

しかし、この2つのモードにはそれぞれ問題があり、NetworkConnectでは外部のサイトにアクセスできず、Webモードではリバースプロキシを通すため、アドレスバーのURLを書き換える必要があり、任意のアドレスに簡単に飛べません(例えばブックマークから特定の科目のページにアクセスできません)。

そこで、OpenConnectというVPN クライアントとSplit Tunnelingという技術で学内はVPNを経由、それ意外はVPNを経由しないようにし、より簡単に接続するためにOpenConnectをラップしたスクリプトを作成しました。

なお、自作したスクリプトは現時点ではMacのみをサポートしています。

使ったもの

OpenConnect

OpenConnectは初め、CiscoのAnyConnect SSL VPNをサポートするために作成されたもので、その後、Pulse Connect Secureとして知られるJuniper SSL VPNをサポートしました。ということが下記のページに書かれています。

OpenConnect VPN client.

Split Tunneling

VPN接続

f:id:lightnet328:20161230190518p:plain

VPN接続 + Split Tunneling

f:id:lightnet328:20161230190521p:plain 鍵のマークがVPN ゲート、ビルのマークが学内、Webの画面が外部のページです。 Split Tunnelingを利用するとIPを指定することで、指定したIPとそれ以外でVPN ゲートを通すか通さないかを分けることが出来ます。

これを実現するために、OpenConnectではvpncというスクリプト(以下、vpnc-script)を利用しています。 また、vpncではrouteコマンドでルーティングテーブルを書き換えることによって特定のIPのみをVPNゲートを通すようにしているようです。

実際にルーティングテーブルがどのように変わるかを確認したい場合は、

$ netstat -rn > a
$ netstat -rn > b
$ diff a b

とすると良いと思います。

手順

セットアップ

TUN/TAP

仮想ネットワークデバイスです。

$ brew cask install tuntap

OpenConnect

VPN クライアントです。

$ brew install openconnect

HomebrewでOpenConnectを入れるとvpnc-scriptも/usr/local/etc/vpnc-scriptにインストールされます。

Juniper Network Connect Client

OpenConnectの簡素なラッパーです。

$ brew install tuntap
$ brew install openconnect
$ git clone https://github.com/lightnet328/jncc ~/.jncc
$ cp ~/.jncc/jncc.config.example ~/.jncc/jncc.config
$ vim ~/.jncc/jncc.config
$ echo 'export PATH="$PATH:$HOME/jncc"' >> ~/.zshrc
$ exec $SHELL -l

bashを使ってる方は~/.zshrc~/.bashrcと読み替えて実行してください。

接続

OpenConnect

利用するVPNサーバーがJuniper VPNで、認証方式がIDとパスワードの場合、以下のコマンドでネットワーク接続することができます。

$ sudo openconnect --user myid0123 vpn.tekitou.ac.jp --juniper

ただ、これでは前述した通り、全ての通信がVPNを経由してしまいます。 VPNを経由したいIPアドレスがわかっている場合は、以下のようなスクリプトを用意し、openconnectコマンドにそのパスを渡してあげることでVPNを通るかどうか振り分けることができます。

export CISCO_SPLIT_INC=2
export CISCO_SPLIT_INC_1_ADDR=xxx.xxx.xxx.xxx
export CISCO_SPLIT_INC_1_MASK=255.255.255.255
export CISCO_SPLIT_INC_1_MASKLEN=32
export CISCO_SPLIT_INC_2_ADDR=xxx.xxx.xxx.xxx
export CISCO_SPLIT_INC_2_MASK=255.255.255.255
export CISCO_SPLIT_INC_2_MASKLEN=32
exec /usr/local/etc/vpnc-script
$ sudo openconnect --user myid0123 vpn.tekitou.ac.jp --juniper --script=(SCRIPT PATH)

マニュアルはman openconnectOpenConnect VPN client.でどうぞ。

Jniper Network Connect Client

VPNを経由したいIPアドレスが予めわかっている場合は前述の手段で困りませんが、多くの場合はわからないと思います。そこで、設定ファイルにVPNを経由したいドメインを書いておくことで自動でIPアドレスを求めるクライアントを作りました。

安直なネーミングですが、Juniper Network Connect ClientとしてGitHubに公開しています。

仕組みとしては単純で、まずOpenConnectでVPN接続し、その状態でnslookupして設定しておいたドメイン名をIPアドレスに変換し、そのIPアドレスのみをVPNに通すようにしています。

他の部分も結構簡素なので何かあればIssueを建てるなりPull Requestを送るなりしてください。

設定ファイルはjncc.configで中身は以下のようになっています(コメントは除きました)。

ID=myid0123
SERVER=vpn.tekitou.ac.jp
SPLIT_HOSTS[0]=portal.tekitou.ac.jp
SPLIT_HOSTS[1]=xxx.tekitou.ac.jp
SPLIT_HOSTS[2]=oxo.tekitou.ac.jp
VPNC_SCRIPT_FILE=/usr/local/etc/vpnc-script

接続する時は、

$ sudo jncc connect

切断する時は、

$ sudo jncc disconnect

を実行するだけです。

簡単ですね。

おわりに

ネットワークには詳しくないのですが、なんとかVPN接続の手間を減らすことが出来ました。もし、間違っている点があれば、コメントやTwitterのリプライにてお知らせいただければ幸いです。

気がつけば2016年ももうすぐ終わりですね。良いお年を。

KIT-LT #1 を開催した

イベント

お久しぶりです。最近はPHPカンファレンス2016CODE HAMMER 2に参加したりしていました。こういう機会にブログを書こうと思うのですが家に着いた瞬間に頭の中が空になるのでダメです。

f:id:lightnet328:20161224231846j:plain

イベント概要

kitlt.connpass.com

きっかけ

同期の@yuu_7975 くんにやろうと言われたので。

今年の4月に「学内の情報系の人間の活動の単位がプロジェクトで固まっていて、もうちょっと広い単位で情報の共有をする場があっても良いのに」と思ってプロジェクトと部活で集まって活動を紹介し合おう!みたいなイベントを開いたけど発表者とその周辺を除いて、3人しか集まらなかったので「もう二度とやるか」と思った記憶があるので、今振り返るとなんでまたイベント開こうと思ったんだろうという気持ち。

運営側の云々

まず、募集はconnpassがめっちゃ便利だった。仮だとしても大体何人くらい参加してくれるということが事前にわかるのは心に優しい。4月にやったイベントは発表者を運営側で集める形(といっても運営は僕しかいなかった)でプロジェクト回って時間の調整、場所の確保、学内ポータルでの告知の依頼、ポスターの掲示とかをやってたのでとてもつらかった(ポスターは部活の先輩に作ってもらった)。発表者はこっちで集めても参加者はconnpassに任せればよかったのか?

告知についてはTwitterで呟く以外特に行わなかったので今考えるともう少ししてもよかった。『広げたい 参加者の層』

イベント後のアンケートで満足度を満足、やや満足、どちらともいえない、やや不満、不満の中から選んでもらったところ満足・やや満足以外の回答が無かったので心底「やって良かったなあ。」と思った。次いつやるかはまだ決めかねているけどまたやりたいですね。意見・要望も集まってるのでそれに添えるように頑張ります。個人的にも改善したい点がいくつかあったので@yuu_7975 くんと相談しながらやっていきたい所存。

発表した

『Illustration2Vecとその応用』という題で発表した。応用・・・?って感じだったので『Illustration2Vecを使ってみた』とかでも良かったかもしれない。 イベント開いておきながらなんだけど発表苦手だからなんとかして欲しい。

石川県内のイベントとコミュニティ

石川県、東京や大阪と比べたらIT系の人が多く集まるイベントは少ないけどそれでもKanazawa.rb、JAWS-UG 金沢、アルゴリズム勉強会、ITざっくばらん会みたいに定期的に開催してるイベント(+コミュニティ)が幾つかある。折角あるので参加したら楽しい人は楽しいと思うんだけどそもそも学生の参加者が少ないらしい。

KIT-LT #1はイベント名がイベント名だし会場も大学だったので外部からの参加者は3名だった。丁度他のイベントと被ってしまったのでキャンセルされた方もいたが、それでももう少し多く来て欲しいという気持ち。他のイベントが「学生が少ない・・・」と思うのと同じだろう。こういうイベントは所属や年代じゃなくて興味や目的で集まるのが理想だと思うので良くなるといいですね(?)まあ『会場が遠い』みたいなのはあると思う。

Kanazawa.rbとITざっくばらん会は行こう行こうと思って一度も参加したことがないので今度行きます。

参加者の発表資料

他の参加者の資料もTwitterを漁って掘り出してきたので載せておきます。

おわりに

KIT-LT #2で僕と握手!

pixiv 2016 SUMMER BOOT CAMPに参加してきた

インターンシップ

お久しぶりです。8/29~9/2の5日間、ピクシブ株式会社の「pixiv 2016 SUMMER BOOT CAMP」に技術職(エンジニア)として参加してきました。

f:id:lightnet328:20160910000003j:plain

インターンシップ概要

ssl.pixiv.net

応募

応募理由

過去の参加者のまっぴーさんからTwitter経由で聞いたのがこのインターンを知ったきっかけだったと思います。Web アプリを作るのが好きなので、実際に多くのユーザーに使われているサービスを開発している会社で働いてみたかったということが一番の理由です。一番まともに書ける言語がPHPだったこと(pixiv.netはPHP製)、お金がもらえるということも大きな理由でした。

応募した選考コース

技術職の場合、選考フローが通常選考とGitHub選考の2種類あるのですが、GitHubに公開してるリポジトリが1つしか無かったため通常選考で応募しました。

エントリーシートでは自己PRを400字、開発成果を300字くらい書きました。

業務内容

1日目

初日は10時半にオフィスに集合しました。前泊をしていたのですが、腹痛に見舞われ電車を逃してしまい、既に皆さんがプロジェクター前の机に集まっていたので大変申し訳なくなりました。

f:id:lightnet328:20160910000159j:plain

プロジェクター前の机は4台あって、前の2台にインターン生、後ろの2台にメンターさん、社員さんが座っていたのですが、社員さんが若かったので「あれ、インターン生15人のはずだけど30人くらいいる・・・?」みたいなことを思いました。

まず最初にインターン生が自己紹介をしたのですが、年齢は高校生から院生まで様々で出身は関東・関西が半々みたいな感じでした。

その後、業務に関する説明と配属先の説明を受け、インターン生とメンターさんとでお昼ごはんを頂きました。

f:id:lightnet328:20160910000312j:plain

f:id:lightnet328:20160910000318j:plain

お昼ごはんを食べた部屋がおしゃれだったので帰り際に写真を撮ってきました。壁に掛かっているモニターには屏風に描かれていそうな絵が映しだされていて、波が動いていました。他のメンターさんに「壁のモニターってあの絵を映す以外に使ったりしてるんですか」と聞いてみたら「いや、あれ以外を映しているのをみたことがない」と言われました。趣がある。

午後からエンジニアはpixivの開発に携わるにあたって必要な環境を整え、それぞれの配属先の席に移動しました。配属先でメンターさんからチームの業務内容と僕に割り振っていただいたタスクの説明を受けました。

  • デザインの崩れの修正
  • UX向上のための修正
  • コミュニティ性向上のための機能の開発
  • ユーザーをより見つけやすくする機能の開発

割り振っていただいたタスクは上記のようになっていて、プロジェクトのファイル構造や開発からデプロイまでの手順に慣れるために簡単なタスクから取り組みました。

初日は19時から懇親会があったため、デザインの崩れの修正をしてプルリクの作成、レビュー、デプロイをしたところで業務を切り上げました。自分で一から人に使われるようなサービスを考え、作ることも楽しいのですが、既に多くのユーザーを抱えているサービスに自分の書いたコードが取り込まれるのにもやりがいを感じました。

懇親会ではメンターさんやインターン生と今日やったことやインターンに来るまでの流れみたいなことを話し合ってました。特に今回のインターンインターン生がそれぞれのチームに配属され、インターン生同士でコミュニケーションを取る機会が少なかったため初日に懇親会があったのは良かったです。

f:id:lightnet328:20160910000330j:plain

🍣👏

また、今回のインターンでは1日の初めに"本日の予定"メール、終わりに"日報"メールを送る必要があったため懇親会後に日報を作成しました。

初日はこれで終わり。ピクシブ株式会社様に用意していただいたホテルでぽやしみました(感謝)。

2日目

2日目。ピクシブ株式会社の始業時間は10時からなのでよく眠ることが出来ました。大学の講義も毎日10時からが良いです。とはいえ、"本日の予定"メールを始業前には送る必要があるので9時半頃にはオフィスに着いてましたが。

会社にはウォーターサーバー、お茶やミニッツメイドのグレープフルーツジュースなどが入った冷蔵庫、みそしるサーバー(!!)などが用意されています。

f:id:lightnet328:20160910000339j:plain

"本日の予定"メールを送信した後、隣の席のインターン生とみそしるを頂きました💪

f:id:lightnet328:20160910000342j:plain

毎日10時からは"朝会"があります。朝会ではチームメンバーが集まり、挨拶を交わしながらそれぞれのタスクを確認します。

その後、エンジニアとして働くインターン生はピクシブのエンジニアの方々から、幾つかのプロジェクトの存在目的、開発体制や取り組んでいるマネジメントやISUCONについての講義を受けました。その中で、特に印象に残っているのが"割れ窓理論"で、これは"建物の窓が壊れているのを放置すると、誰も注意を払っていないという象徴になり、やがて他の窓もまもなく全て壊される"という環境犯罪学上の理論です。

建物の窓が壊れているのを放置すると、誰も注意を払っていないという象徴になり、やがて他の窓もまもなく全て壊される

割れ窓理論 - Wikipedia

このプロジェクトでは今すぐに修正する必要のない小さなバグや修正が必要な内部の構造などを割れ窓と考え、週の内の半日をチーム全体で割れ窓を修正する日を設けているそうです。僕自身、小さなタスクを後でやろうと言って積み上げることがよくあるので、良い取り組みだと思いました。

ISUCONについての講義では、ISUCONの概要や実際にどこをどのようにチューニングをすればよいのかなどを教わりました。丁度、ISUCON予選日が旅行と被ってしまっているので参加できないのが非常に残念なのですが、Web サービスのチューニングに関しては全く知識が無いため(去年、自作サービスを公開したらApacheのチューニング不足で連日サーバーが落ちて泣いた経験有り)、どこをどういう風に直せばよいのかというのはとても参考になって良かったです。また、@catastuyさんによる『ISUCONとはなにかと ISUCONに対するアプローチ』はQiitaで公開されています。

インターン生向けのISUCON CM - Qiita

講義後のお昼は配属先のチームの皆さんとうどんを食べました。1週間前くらいからなんか肌寒さを感じていたので温かいうどんを食べれて良かったです。

f:id:lightnet328:20160910000347j:plain

午後からは、UX向上のための修正、コミュニティ性向上のための機能の開発に取り組みました。コミュニティ性向上の機能は既存のコードを修正するだけでなく、新しく追加する部分もあり「おお、開発してるぞ!」と感じました。

3日目

3日目には全体会議やランチ会がありました。

全体会議ではその名の通り社員全体で行う会議で、毎週どこかのチームがプロジェクトの現状を他のチームに報告したり、その他全体で共有する事項を伝えます。この会議で具体的な数字や今後の戦略などを共有し、改めてインターン生は自己紹介をしたのですが、会議の内容が内容なだけに、「あれ、僕は社員だったのでは・・・?」と錯覚しました。

その後、他のチームの方々と混ざりながらピクシブ・ランチを頂きました。これは"ランチ会"と呼ばれていて、社内全体でランチを食べます。大きな会社で働いた経験は無いので比較はできないのですが、この"ランチ会"があるからなのか、チーム毎に物理的な仕切りが無かったりするからなのか、ピクシブはチーム間の隔たりが無く誰もが誰とでも話していると感じました。

f:id:lightnet328:20160910000355j:plain

その後は昨日に引き続き、コミュニティ性向上のための機能の開発をしました。開発中にチームの方々と仕様の細部について話し合ったりしたのですが、常にユーザーのことを考え最善を尽くそうという姿勢を感じました。開発に取り組む姿勢として非常に魅力的で、見習いたい、僕も趣味の開発はこういうようにやりたいと思いました。

4日目

4日目はユーザーをより見つけやすくする機能の開発をしました。この機能は"コミュニティ性向上のための機能"と共通部分があり、それと同じ要領でやれば割りと早めに出来ると考えていたのですが、開発中に表示部分で仕様に沿っていない挙動があり思ったより時間がかかってしまいました。具体的に言うとMedia Queriesを使っている部分なんですが、max-width、 min-widthの幅を計算が大変でした。(ここの幅がこうで、デバイスの幅が云々だからなんたら〜みたいなのって大変じゃないですか?僕だけ?)

初日に説明された予定では5日目はスライド作成と書かれていたので、3日目に、"明日中に完成させたい"とつぶやいてたのですが、機能が完成したのは23時半頃で内心「大丈夫かな・・・💦」という感じでした。また、コーディングからデプロイまでにレビューを挟むのでチームの方に遅くまで残ってもらってしまい申し訳なく思いました。ただ、4日目になると、一日の過ごし方が何となくわかってきて集中して開発に取り組めました。

あ、お昼は和食ビュッフェでした。白身魚のフライが美味しかったです。余談ですが、チームの方のお気に入りのお店らしく、Slackのチームのチャンネルには毎日このお店の今日のメニューが流れていて面白かったです。

f:id:lightnet328:20160910000400j:plain

5日目

朝はコンビニでおにぎりを買ってオフィスの机でインターン生何人かと朝食を食べました。技術職の方々と今何してるのかみたいな話をしていた時に総合職の方が「呪文みたいで何言ってるかわからん・・・」と言っていたのが面白かったです。

朝会後からはずっとスライド製作をしていました。大勢の前で発表する機会はこれまでにもそれなりにあったのですが、話すことの取捨選択が苦手でどれも盛り込んでしまったり、スライドの文字が多かったりすることがありました。今回は作成途中で何度かメンターさんにアドバイスを貰うことでわかりやすいスライドを作ることが出来たと思います。

15時半からはインターン生全員で順番に成果発表をしました。技術職の方の発表はメンテナンスだったり、新規機能開発だったりと何をしたのか伝わって来やすかったです(リファクタリングとかをしていた人はViewが無いため発表が大変そうでした)。総合職の方は記事の編集や会員数増加のための取り組みなどをした上で、その成果を数値で表したりしていて自分のやったことが数値で評価されるのはシビアだと感じました。また、質の良い記事を一つ書くにも相当な時間と労力が費やされていることがわかりました。

成果発表後にそれぞれのチームのメンターさんがインターン生にコメントをしていました。僕のメンターさんからは「自分で作った機能を触っている時に「良いですねこれ!」と言っていて、満足していたのでそういうところが良かったと思う。」ということや「今後、作ってもらった機能は長く生きていくと思う。」「結構遅くまで残って働いてることがあって、責任感があってよかった。」というコメントを頂きました。遅くまで残って作業をしていたのは自分がした仕事の出来に満足できていなくて「これで大丈夫かな・・・」と思っていたからなので、そのような講評をいただけて素直に嬉しかったです。

また、BOOTH チームのメンターさんは「課題としてはこれくらいかと思っていても、実際にはそれを作るのに既存のコードを多く呼んだりするので触る量は多い」ということを仰っていて、チームは違うのですが本当にその通りだと思いました。機能を追加するのに既存の関数の依存関係やコーディング規約、というか"これはこういう書き方で統一されているよね"というようなものを把握するのが意外と大変でした。

成果発表が終わった後、19時からはインターン生とメンターさんで打ち上げに行きました。初日の懇親会より打ち解けることができて良かったです。インターン生が順番に感想を言っていく時に「採用してください!」という声が聞こえていたりしました。詳しくは聞いていないのですが本気らしいです(すごい)。僕も僕でメンターさんに「次回も応募したいです。通った時はまたよろしくお願いします。」みたいなこと言ってました。

まとめ

全日程をだらだらと書いてしまいました。すみません。ただ、実際の5日間はこれよりももっと濃くて充実していたものだったと思います。

働いていて良いなと思ったこと、感じたことは、

  • 会社のオフィスと人間関係が明るい・賑やか
  • "サービスを作っている"、"ユーザーが第一"という共通認識を持っている
  • 多くの人に使われていて、自分も使っているサービスを自分で作れる

この3つです。特に多くの人に使われていて自分も使っているサービスの開発に携わるということは恐らくそれほど多くないはずで、その開発に愛と責任を持って取り組めていることがとても良いと思いました。

最後に『pixiv 2016 SUMMER BOOT CAMP』参加前と参加後のツイートを載せておきます。

参加前

参加後

pixiv インターンに行ってみたくなった人へ

もし「pixivのインターンに行ってみたいな、別の視点から詳しく知りたいな」という方がこの記事を読んでくれた時のために、参考になる今年のインターン生の記事などを並べておきたいと思います。

ここからも余談です。これは他チームのメンターさんから聞いた話なのですが、このインターンシップはどうやら新卒採用プロセスのエントリー選考、一次選考、二次選考、三次選考後のインターンシップと似たようなものらしいです。一応インターン中に死ぬことはなかったので自尊心の高まりを感じました。

新卒採用プロセス - ピクシブ株式会社 採用サイト

pixiv 2017 SPRING BOOT CAMPで僕と握手!(僕が受かったら)

プロ生ちゃんからプロ生ちゃんグッズを貰った!!

その他

プロ生ちゃんからプロ生ちゃんグッズを貰いました。

事の経緯

プロ生ちゃんのラバストは金沢Unity勉強会という勉強会でジャンケンに勝って貰って以来、ずっとリュックに付けて持ち歩いてたのですが、いつの間にか落としていました。検討がつく場所は一通り探したのですが、どうしても見当たらず・・・。泣く泣くTwitterしてたらなんとプロ生ちゃんがプロ生ちゃんのラバストを送ってくれることに!!

プロ生ちゃんラバストを落としたことに気づいた時の僕 f:id:lightnet328:20150620181911j:plain

プロ生ちゃん優しい!

貰ったもの

ラバストが送られてくるのを楽しみに待ってた時の僕 f:id:lightnet328:20150620181915j:plain

届いた。 f:id:lightnet328:20150620181919j:plain

ラバストだけだと思ったらめちゃ色々入っててびっくりした時の僕 f:id:lightnet328:20150620181925j:plain

ラバスト、シール、ステッカー、名刺、Webコミックの印刷版まであります。

感動した。プロ生ちゃんありがとう!

後、Webコミックの印刷版ですが3冊も送られてきていますね。観賞用・保存用・布教用でしょうか・・・? 僕は布教活動に熱心なオタクなので2冊布教しようかなと思っているのですが欲しい人は@lightnet328までリプライください。石川県に住んでて会えそうな人、先着2名様まで受け付けます。

元はWebコミックなのでWebで見ることもできますヨ! コミックのタイトルはスパゲッティコードにちなんで『すぱこー』らしいです。

Web Comic | プログラミング生放送

色々なグッズを貰っちゃいましたが、他にも缶バッジやマグカップ、なんとジャムソースもあるみたいです! ジャムがあるしスパゲッティも売れそうですよね。 プロ生ちゃんグッズについては以下のオンラインストアからお買い求めいただけるみたいです。

プロ生ちゃんの良さ

f:id:lightnet328:20150620181945j:plain

f:id:lightnet328:20150620181954j:plain

素晴らしい。

ラバスト付けました

やったぜ!早速リュックに付けました。

これは勉強会で貰ったプロ生ちゃんとプロ生ちゃんに貰ったプロ生ちゃんとのツーショットの様子です。 f:id:lightnet328:20150620182011j:plain

プロ生ちゃんについて

さて、プロ生ちゃんの太ももの良さがわかったところでプロ生ちゃんについて紹介します。

プロ生ちゃんこと暮井 慧(くれい けい)ちゃんは、都内の公立高校に通う女子高生で情報処理研究会という部活に所属しているらしいです。 また、プログラミング生放送というIT・開発系の勉強会をオンラインストリーミングするITコミュニティのキャラクターとしても活動しているみたいです。

プロ生ちゃんすごい!

最近開催されたプログラミング生放送勉強会としては、以下の3つがあるようです。

現段階は次回の開催は決まっていないみたいですがプロ生ちゃんはTwitterもやっていてイベントの告知などもしているので興味のある人はフォローするといいと思います!

プロ生ちゃん(暮井 慧)- @pronama

プロ生ちゃんですが、グッズ販売やWebコミックの連載だけでなくプロ生ちゃんのクリエイター向け素材も公開しているようです。 イラスト、Web コミックのPhotoshopデータや、ドット絵、MMDやUnity、CLIP STUDIOなどで使える 3D モデルなど、多くの素材が公開されています。

SDキャラクター

f:id:lightnet328:20150620182029p:plain

f:id:lightnet328:20150620182032p:plain

f:id:lightnet328:20150620182034p:plain

f:id:lightnet328:20150620182037p:plain

SDキャラクター素材には以上のような13種類の画像が含まれています。

Atomにはpronama-chan packageというエディタ背景にプロ生ちゃん(SDキャラ)を表示するパッケージがあるので、Atomユーザーの方は入れるといいと思います。

hurumeki/atom-pronama-chan f:id:lightnet328:20150620182424p:plain

ドット絵

f:id:lightnet328:20150620182047p:plain

これは配布されているドット絵データに入っているスプライト画像で、 パッケージにはこのスプライト画像と切り分けられた個別の画像が含まれています。

種類が豊富でこのドット絵だけでも様々なゲームが作れそうですよね。

3Dモデル

クリエイター向け素材 ダウンロード | プログラミング生放送

気になる利用規約についてですが、公式ページには

キャラクター「プロ生ちゃん(暮井 慧)」、「戸増千由莉」および「フィネス・ヒルヴィレッジ」を利用した作品を、使用料不要の比較的自由な条件の下で、公開できます。

とあります。 素材を利用した作品を公開する際は下記の利用ガイドラインに目を通しておいてください。

利用ガイドライン | プログラミング生放送

僕はUnity触ってみたいマンとして2ヶ月間生活してしまっているのでプロ生ちゃん素材をきっかけに時間のあるときにUnityを触ってみようと思います。それではみなさんもよいプロ生ちゃんライフを!

便利アイテム

ライフハック

YouTuberなので今日は便利アイテムの紹介をしようと思います。

ちなみに、YouTuberではないです。

便利アイテム

HAKUBA シリコン ブロアー CP オレンジ KMC-42CP-OR

HAKUBA シリコン ブロアー CP オレンジ KMC-42CP-OR

エアブローです。

耳掃除をした後に母親から耳をフーッっと吹いてもらったことは多くの人があると思いますが(無かったらごめん)、このエアブローを使えばセルフフーッをすることができます。

便利!

また、積もった埃を吹き飛ばすこともできます。

便利ですね。

感想

深夜テンション

ListViewに含まれるTextViewのリンクをクリックできるようにする。

Java Android

最近は自由登校期間で学校が休みで殆ど家に引きこもっています。

折角の休みなのでAndroid開発をするためにJavaの勉強をしていたのですが、自分の調べ方が悪いのか思う通りの結果が出てこなかった部分があり、先ほど解決したので備忘録として残しておきます。

起きた問題

ListViewにOnItemClickListenerをセット、ListViewに含まれるTextViewのテキストの一部にリンクを貼るためにURLSpan(ClickableSpanでもいい)をセット。

この状態で、

・TextViewをクリックするとリンクをクリックしたかどうがにかかわらず、ListViewにセットしたOnItemClickListenerが反応しない。

・行末がリンクの場合、リンクがTextViewの端まで及ぶ。

などの問題が起こる。

原因

タッチイベントは親Viewから子Viewに伝搬され、子Viewで処理した場合そこでタッチイベントが終了します。

また、子Viewで処理しなかった場合、子Viewがあれば子Viewにイベントを伝え、無ければ親Viewにイベントを伝えます。

要するに、親からイベントが伝わり、子から処理される仕組みらしいです。

自分の曖昧な理解を書いたため、これらについて間違いがあるかもしれません。

詳しくは、 Android のタッチイベントを理解する(その1) - Unmotivated が参考になります。

今回はTextViewにセットしたLinkMovementMethodがそこでイベントを遮っていたようです。

解決策

TextViewのonTouch時の動作を書き換えてあげればいいようです。

TextViewのリンク以外をクリックした時はListViewのクリックに行く方法 - oigamishunta’s blog

android - ListView: TextView with LinkMovementMethod makes list item unclickable? - Stack Overflow

これについてはいくつか解決策がありましたが、今回は「android - ListView: TextView with LinkMovementMethod makes list item unclickable? - Stack Overflow」の2番目の回答を参考にしました。

textView.setOnTouchListener(new OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        CharSequence text = ((TextView) v).getText();
        Spannable stext = Spannable.Factory.getInstance().newSpannable(text);
        TextView widget = (TextView) v;
        int action = event.getAction();

        if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_DOWN) {
            int x = (int) event.getX();
            int y = (int) event.getY();

            x -= widget.getTotalPaddingLeft();
            y -= widget.getTotalPaddingTop();

            x += widget.getScrollX();
            y += widget.getScrollY();

            Layout layout = widget.getLayout();
            int line = layout.getLineForVertical(y);
            int off = layout.getOffsetForHorizontal(line, x);

            ClickableSpan[] link = stext.getSpans(off, off, ClickableSpan.class);

            if (link.length != 0) {
                if (action == MotionEvent.ACTION_UP) {
                    link[0].onClick(widget);
                }
                return true;
            }
        }
        return false;
    }
});

「参考にしました」、というかほぼそのままです。

これで、「TextViewをクリックするとリンクをクリックしたかどうがにかかわらず、ListViewにセットしたOnItemClickListenerが反応しない。」という問題が解決しました。

しかし、このままだとリンクの範囲が少しおかしなことになり、行末がリンクの場合、リンクがTextViewの端まで及んでしまうようです。

というか、

SpannableString ss = new SpannableString(text);
ss.setSpan(new URLSpan("URLな文字列"), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
textView.setText(ss);
textView.setMovementMethod(LinkMovementMethod.getInstance());

みたいにした場合、「行末がリンクの場合、リンクがTextViewの端まで及ぶ。」ことになると思う。

解決策のコードでgetSpansしてる辺り(=リンクを取得してる辺り)を見てみます。

SpannableStringBuilder | Android Developers 英語よくわからないけどgetSpansの部分を見ると、queryStartとqueryEndに重なるspansを返すみたいですね。

getSpans(off, off, ClickableSpan.class);の部分は問題なさそうなのでoffとlineについて見てみます。

Layout | Android Developers getLineForVerticalは引数で指定した垂直位置に存在する行番号を、getOffsetForHorizontalは引数で指定した行番号の行に存在する文字の中で、引数で指定した水平位置に最も近い文字の文字位置を返すみたいです。

getOffsetForHorizontalの最も近い文字の位置を返す部分に問題がありそうです。

Layout | Android Developersについて詳しく見てみたら、getLineMaxというメソッドがありました。

引数で行番号を指定するとその行の長さを返すみたいです。

getLineMaxで行の長さを取得し、これがクリックしたTextViewの位置より小さく、尚且つクリックした位置がリンクであればリンククリック時の処理をさせれば上手く行きそうです。

textView.setOnTouchListener(new OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        CharSequence text = ((TextView) v).getText();
        Spannable stext = Spannable.Factory.getInstance().newSpannable(text);
        TextView widget = (TextView) v;
        int action = event.getAction();

        if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_DOWN) {
            int x = (int) event.getX();
            int y = (int) event.getY();

            x -= widget.getTotalPaddingLeft();
            y -= widget.getTotalPaddingTop();

            x += widget.getScrollX();
            y += widget.getScrollY();

            Layout layout = widget.getLayout();
            int line = layout.getLineForVertical(y);
            int off = layout.getOffsetForHorizontal(line, x);

            if(x < layout.getLineMax(line)) {
                ClickableSpan[] link = stext.getSpans(off, off, ClickableSpan.class);
                if (link.length != 0) {
                    if (action == MotionEvent.ACTION_UP) {
                        link[0].onClick(widget);
                    }
                    return true;
                }
            }
        }
        return false;
    }
});
if(x < layout.getLineMax(line)) {}

これを加えただけですが、これで「行末がリンクの場合、リンクがTextViewの端まで及ぶ。」という問題も解決することが出来ました。

感想

久々に文章を書いたのでつらかった。

英語が読めないのでつらい。

語彙力の低下を感じたのでつらい。

JavaAndroid開発はたのしいと思う。

2進数を64進数に変換する

PHP プログラミング

2進数を64進数に変換しようと思ったんだけど検索してもいい感じに出てこなかったから自分で書いた。

コード

<?php
$chars = array_merge(range(0,9), range('a','z'), range('A','Z'), array('+','/'));
$b64map = [];
foreach($chars as $k => $c) {
    $b64map[bn(decbin($k), 6)] = $c;
}

function binToB64($binnum) {
    $chars = array_merge(range(0,9), range('a','z'), range('A','Z'), array('+','/'));
    $b64map = [];
    foreach($chars as $k => $c) {
        $b64map[bn(decbin($k), 6)] = $c;
    }

    $length = strlen($binnum);
    for($m = 0; $m < $length; $m = $m + 6) {}
    $bin = bn($binnum, $m);
    $binArray = str_split($bin, 6);
    $b64 = "";
    foreach($binArray as $b)
        $b64 .= $b64map[$b];
    return $b64;
}

function bn($binnum, $n) {
    $length = strlen($binnum);
    $l = $n - $length;
    $zero = "";
    for($i = 0; $i < $l; $i++)
        $zero .= "0";
    return $zero .$binnum;
}
?>

仕組み

汚いコードなので忘れた時に備えて仕組みも書いておく。

まず2進数と64進数の変換表を作る。

2進数の桁が6n桁でない場合桁が6n桁になるまで上位桁を0で埋める。

次に文字列の2進数を6桁毎に区切る。

区切られた2進数をそれぞれ64進数に変換して結合する。

終わり。

感想

2も64も基数が2で揃っているので2進16進変換と同じ要領で変換できた。

デコード部分は今のところ使う予定が無いので作ってません。

より良い方法があれば教えてください。