2008年7月31日木曜日

インフィニットループ 古城が見せた夢

このブログ記事をはてなブックマークに追加

日本一ソフトウェアの「インフィニットループ 古城が見せた夢」は、古城を舞台としたアドベンチャーゲームということもあって、Infocomの古典的テキストアドベンチャー、「Moonmist」(日本ではシステムソフトが「ムーンミスト 白き貴婦人の謎」というタイトルで販売)を髣髴とさせる。幽霊(インフィニットループでは死神)に捕まり死んでしまうという点も似ている。もっとも、Moonmistの主人公は幽霊というわけではないが。Moonmistの発売は1986年と古く、あまり知られていないと思われるが、あの古城に漂うミステリアスで独特の雰囲気が好きだった。

個人的にはインフィニットループの絵柄はあまり好みではないのだが、ゲームのテンポが良く、ついついプレイしてしまう。死神に殺された主人公が幽霊となり人々に憑依することで事件の真相を徐々に知ることになる。主人公は死神に生命力を奪われたり、破滅的な事件を食い止められなかったりすると時間が死亡当時に戻されて最初からプレイすることになるが、それまでの記憶や物語の変化はそのままなので、憑依する人物を変えたり、変化したストーリーを追うことで、また違った展開が楽しめる。また、主人公が現実世界に干渉する唯一の手段として、「夢」がある。これは、眠っている人物に対してキーワードを与えることで、それに関連する夢を見せることができるのだ。それにより、前回とは違ったストーリーが展開していく。

エンディングまでプレイしたが、ストーリーの薄さが気になった。もう少し練れなかったものだろうか。登場人物がそれほど多いわけではないので物語が進むとある程度察しがついてしまうのだ。他人に憑依して、さらに時間が戻るというシステムがユニークなだけにその点が残念だった。個人的にはジョージ・R・R・マーティンの「氷と炎の歌」シリーズのようなストーリーだったら最高なのだが…。今後に期待したい。

今回、絵柄が好みでなかったので最初はスルーするつもりだったのだが、PS3のPLAYSTATION Networkから大容量体験版をダウンロードしてプレイしたら思いのほか面白く、製品版も購入してしまった。因みに体験版のセーブデータはそのまま製品版に引き継ぐことができる。また、公式サイトではFlash体験版があるので、こちらで試すのも良いと思う。

続きを読む...

2008年7月29日火曜日

「Googleを支える技術」を読んで

このブログ記事をはてなブックマークに追加

Googleを支える技術を読んでみた。Googleが出している論文を分かりやすく噛み砕いて説明している。基本的には論文を読めば分かることなのだが、それほど分量もなく平易に書かれているので、ざっとGoogleの技術を理解するのには良いと思う。

内容としては、Googleの歴史、Googleファイルシステム(GFS)、分散ストレージ(Bigtable)、分散データ処理(MapReduce)、運用コスト、開発体制について書かれている。GFS, Bigtable, MapReduceあたりは技術的な話で、まあ面白いのだが、大体予想していた通りであった。それに対して、運用コストの話は自分の視点からではほとんど考えない部分であり、Googleが如何に電力を少なくするかに注力しているかが良く分かり興味深い。また、開発体制については自分の考えている理想に近いものであった。

ここのところ、思ったよりも自分がGoogleに傾倒していると感じる。理想としては多種多様な技術をはまんべんなく吸収し使用していくべきだろうが、Googleをベータ版の頃から使っている自分としてはついついGoogleの技術に目が行ってしまう。自分のメイン言語がGoogleと一致していることも大きいのかもしれない。それでも、様々な技術を勉強する姿勢だけは忘れないようにしたい。

続きを読む...

2008年7月27日日曜日

ゲームを作りたい人のために

このブログ記事をはてなブックマークに追加

ここのところ、ゲームクリエイターの人材発掘に力を入れているようだ。SCEJからはクリエイター発掘支援プログラム「PlayStation@C.A.M.P!」、MSからはXNA Game Studioによる「マイクロソフト XNA ゲーム クリエーター コンテスト 2008」が開催される。

これらの内容をみるととても魅力的だ。自分が学生だったら迷わず参加していただろう。しかし、こうやって魅力的なコンテストなどを行っていかないと(もしくは行っても)なかなか良いクリエイターが見つからないのが今のゲーム業界なのかもしれない。

自分が一番最初にゲームのアイデアやシナリオを考えたのは小学生のころだった。そのころはインベーダーゲームが流行っており、それに対しとても惹きつけられたのを思い出す(もちろんゲームセンターあらしはお気に入りだった)。もっとも、小学生で小遣いもあまりなかったので、実際にはアーケードゲームはほとんどプレイしなかったのだが。

その当時に作ったシューティングゲームのアイデアは以下の通り。まず、雑魚敵は多種多様に存在し、画面の半分以上を占めるような非常に大きなボス敵がいる。そのボス敵を倒すのは非常に困難で、短い間だけ開閉される出入り口にミサイルを何発も命中させなければならない。その間に弾幕攻撃と執拗な雑魚敵の襲来があるというものだった。もちろん、方眼紙を使って、それぞれの敵をデザインし、何枚も使ってどのように敵が動くのかも描いたものだ。今考えるととても幼稚に思えるが、後年、実際にそんなコンセプトを持つシューティングゲームがたくさん出てきた。

今では多種多様なゲームが作られており、自分が小学生の頃に思い描いたような理想のゲームを自分自身で考えるということはなくなってしまっているのかもしれない。ゲームを考えるのはプロであって、自分たちではないと思っているのだろう。確かに、ゲームはより複雑になり、たくさんの人たちの連携で作成されるようになった。一人が、シナリオを書いて、イラストを作り、音楽を考え、プログラムを組むという時代ではなくなったかもしれない。それでも、本当に面白いアイデアというのは個人の発想力がモノを言うと思う。

最後にプログラミングの観点から。個人的にはゲームを作るためのプログラム言語はActionScript 3.0が良いように思う。基本としてECMAScriptであるし、オブジェクト指向言語としてもしっかりしている。そして何よりも画像やサウンドを扱うのに長けているからだ。自分でも良いアイデアが浮かぶようなら何か作りたいものだ。

続きを読む...

2008年7月26日土曜日

入門Luaプログラミング

このブログ記事をはてなブックマークに追加

入門Luaプログラミングを30分で読み終えて、一通り使える気分になった。確かに簡単に覚えられると思う。まだ、開発環境さえ入れてないので、実際は動かしていないけど。PlayStation Homeの開発言語にもなっているようで、今後が楽しみだ。

以下、メモ。

・基本的に添字は1から。Fortranと同じ。
・endで終わる。
・""と[[]]
・関数はfunction
・配列は{}
・while~do, for~do
・偽はfalseとnilのみ。
・+と..
・io., os., string., table., math.など。

続きを読む...

2008年7月24日木曜日

Google App Engine: OpenID

このブログ記事をはてなブックマークに追加

GoogleでOpenIDを取得するというエントリでも書いたが、Google App EngineでOpenIDを取得できる。さらに、OpenIDを利用したコンシューマ向けのサンプルサイトも立ち上がっている。どちらもソースコードが公開されているのが素晴らしい。

最近はOpenIDが流行っているみたいだし、Google App EngineでOpenID認証を使ってみるかな。

続きを読む...

GoogleでOpenIDを取得する

このブログ記事をはてなブックマークに追加

GoogleのブログサービスBloggerでブログサイトを作れば、そのURLがそのままOpenIDとなる。ブログサイトをわざわざ作りたくない場合は、OpenID for Google AccountというGoogle App Engineのサービスが稼動しているのでこれを利用しよう。これならば、Googleアカウントさえ持っていればOpenIDとしてすぐに使える。

実は、BloggerのURLがOpenIDになっているのを知らずに、日本で最初のOpenIDプロバイダであるOpenID.ne.jpでOpenIDを取得したのだが、どうもここのサイトが心もとなく感じたので、少し調べてみると、様々な不安の声が上がっていることを知った。サーバが落ちる登録確認メールに不具合があるSSLを利用していない、などなど。そんな声もあってか、ついには究極の個人情報漏洩対策が取られたようで、ここでOpenIDを取得する際、必要な情報はニックネームとパスワードだけになってしまった。メールアドレスさえ必要ないのだ(因みにOpenID作成後に登録・更新可能)。確かに個人情報を登録しなければ漏洩は防げるだろうけど、捨てID作り放題で、OpenIDの信憑性を著しく下げないだろうか。もっとも、OpenID自体は個人の特定には不向きな方法なので、こういうのもありなのか。

と云うわけで、Bloggerを利用している・利用する予定の人や、GmailなどでGoogleアカウントを持っている人は、第三者サイトでOpenIDを作成しなくてもすぐに利用できる。今年はOpenIDが流行るのかなぁ。

追記:

独自ドメインなどでbloggerのOpenIDを利用したい場合、<head></head>内に以下の2行を記述する。

<link rel="openid.server" href="http://www.blogger.com/openid-server.g" /> <link rel="openid.delegate" href="http://ブログ名.blogspot.com/" />

追記2:

OpenID.ne.jpでパスワードの変更ができなかったので問い合わせたところ、一度アカウントを削除してから再度登録することになるそうだ。アカウントの削除はニックネームやメールアドレスをOpenID.ne.jpにメールで知らせれば良いらしい。メールでの対応は丁寧な印象を持ったが、それにしてもパスワード変更やアカウント削除ぐらいウェブ上で行えるようにしてもらいたい。

追記3:

上記のOpenID for Google Accountだけど、Claimed IDをちゃんと返さないようで正常に処理されないみたい。

続きを読む...

2008年7月23日水曜日

Google: Livelyの仮想世界でオブジェクトを自作する

このブログ記事をはてなブックマークに追加

GoogleグループのLively HelpBuilding Stuff for Livelyという掲示があった。以下に引用する。

Glad to see so much interest in contributing content! We're gearing up to enable user-created content, but we're not quite there yet so we appreciate you patience.

If you're interested in becoming a content devleoper, please email: livelydevelopersignup at google.com (replacing the spaces and "at" w/ @)

We'll get in touch with you when we're getting ready to give the green light.

ということで、Livelyのコンテンツ開発者になることに興味ある人は上記のメールアドレスにメールを送ると、オブジェクトやアイテム、コンテンツの作成が可能になった時点で連絡をくれるらしい。

詳しいことはまだ分からないけど面白そうだ。

追記:

すぐに返事が来たけど、MaxやMayaを使った経験があるかとか、3Dゲームのコンテンツを作ったことがあるかとか訊かれた。んー、OpenGLとC++で分子モデリングソフトウェアとかは作ったことがあるけど、専門的な3Dモデリングツールなどは使ったことないなぁ。この辺が必須だと自分にはちょっと難しいのかな。

追記2:

「高機能な3Dモデリングツールは使ったことがなくて、3D関係では分子モデリングソフトウェアぐらいしか作ったことがない。MaxやMayaが必須だと自分が開発者になるのは難しいと思う」ってメールをしたら、その返事が来た。やはり現在のところ、コンテンツを作成するのにMaxかMayaを使う必要があるらしい。うーん、この辺のツールは数十万円ほどすると思うし、個人では購入できないなぁ。また、現在、GoogleデスクトップガジェットをLively内で使えるようにしているらしくて、それらのいくつかを開発しないかって言ってきた。面白そうだけど、Googleデスクトップガジェットは眼中になかったから、すぐにできるかどうか分からないなぁ。ちょっと調べてみるか。

続きを読む...

Python: 日付や時刻を操作する

このブログ記事をはてなブックマークに追加

Pythonで日付や時刻の操作を行うには、datetimeモジュールを利用すればよい。以下に示すコード程度を理解していれば、後はリンク先で示すようなリファレンスでどうとでもなると思う。

#!/usr/bin/env python # -*- coding: utf-8 -*- from datetime import * # 協定世界時(UTC)から9時間進める…日本標準時. # ロケールが日本の場合、datetime.now() と同じ. DATETIME = datetime.utcnow() + timedelta(hours=9) # 日付を指定の文字列にする. DATE_STR = DATETIME.strftime("%Y年%m月%d日") # 時刻を指定の文字列にする. TIME_STR = DATETIME.strftime("%H時%M分%S秒") # weekday(): 0…月曜日~6…日曜日 WEEKDAYS = ("月", "火", "水", "木", "金", "土", "日") # 現在の日時を表示. print u"現在の日時は、%s(%s)%sです。" % (DATE_STR, WEEKDAYS[DATETIME.weekday()], TIME_STR) # 2000年1月1日から. datetime()でも可. WHEN = date(2000, 1, 1) # 現在までの時間. PAST = date.today() - WHEN # 指定の日付から現在までの日数を表示. print u"%sから%d日が過ぎました。" % (WHEN.strftime("%Y年%m月%d日"), PAST.days)


出力結果:

現在の日時は、2008年07月23日(水)01時33分44秒です。 2000年01月01日から3126日が過ぎました。

続きを読む...

2008年7月19日土曜日

Python: Google Analyticsのトラッキングコードを複数のHTMLファイルに一括で埋め込む

このブログ記事をはてなブックマークに追加

Google Analyticsは便利だが自前のHTMLファイルが多数あるとトラッキングコードを埋め込むのが面倒だ(ブログだったらテンプレートを変更するだけなんだけどね)。そこで、以下に示すPythonスクリプトで一括で埋め込むことができるようにした。もちろん、階層を持つディレクトリでも問題ない。念のため、バックアップは必ず取っておこう。本スクリプトの使用は自己責任で。

使い方について。まずは、以下に示すソースコードを適当な名前(例えば insert_google_analytics.py)で保存する。次に、トラッキングIDを"UA-xxxxxx-x"から正しいIDに変更する。そして、そのスクリプトをHTMLファイルが保存してあるディレクトリで実行するだけ。もちろん、事前にPythonをインストールしておくこと。

#!/usr/bin/env python import sys, os, re ANALYSTIC_HTML = """<script type="text/javascript"> var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www."); document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E")); </script> <script type="text/javascript"> var pageTracker = _gat._getTracker("UA-xxxxxxx-x"); pageTracker._initData(); pageTracker._trackPageview(); </script> """ def search_web(search_path): html_list = [] for root, dirs, files in os.walk(search_path): for f in files: if os.path.splitext(f)[1] == ".html": html_list.append(os.path.join(root, f)) return html_list def insert_analystics(f): has_analystics = False infile = file(f) in_data = infile.readlines() infile.close() outfile = file(f, "w") out_data = [] for l in in_data: if re.search(r"google-analytics.com/ga.js", l): has_analystics = True search_last_body = re.search(r"(.*)</body>.*", l.lower()) if not has_analystics and search_last_body: s1 = l[:len(search_last_body.group(1))].strip() s2 = l[len(search_last_body.group(1)):].strip() if len(s1) > 0: out_data.append(s1 + "\n") out_data.append(ANALYSTIC_HTML) out_data.append(s2 + "\n") else: out_data.append(l) outfile.writelines(out_data) def main(): html_files = search_web(".") for f in html_files: insert_analystics(f) if __name__ == "__main__": main()

続きを読む...

2008年7月17日木曜日

PS3: エレファンクをプレイしてみた

このブログ記事をはてなブックマークに追加



PLAYSTATION Storeからエレファンク(Elefunk)がダウンロード販売された。800円と低価格ながら、まったりプレイする分にはなかなか面白い。こういった良質のカジュアルゲームが出てくるのは良いことだ。

内容を簡単に説明すると、自分で橋を組み立て、その橋を壊さずに無事にゾウを渡すというパズルゲームである。ゾウの重量による橋のたわみや歪みはPS3でちゃんと演算処理されており、その点はリアルだ。適当に作るとすぐに壊れてしまうだろう。頭を使おう。思ったよりも手応えがあるのがまた良い。

最初、勝手が分からず少し苦労したが、橋の構造や耐久性が理解できてくると面白くなる。コツとしては、ある程度弱そうなところを補強しつつまずは組み立てる。そして、ゾウを渡らせて崩れるようだったら、崩れ始めたところをよく見ておき、そこを更に補強する。これで、ある程度は行けると思う。…とは書いたものの自分もまだ序盤なのでこの先どうなるかはわからない。一日、一ステージが個人的には気持ちの良いペースだ。

建物のパーツを交互に取り除いていき、先にゾウを落とした方が負けとなるオンライン対戦も面白そうだし、多くの人に遊んで欲しいな。

続きを読む...

2008年7月10日木曜日

Google: Livelyの簡単な使い方と遊んでみた感想

このブログ記事をはてなブックマークに追加



Googleがセカンドライフに類似したサービス、Lively(ライブリー)を公開した。クライアントをダウンロードしてインストールするのは同じだが、それ以降は全てウェブ上で操作ができる。実際に使ってみたところセカンドライフよりもかなり簡単だと感じた。

まず、Googleアカウントでサインアップし、もし、自分のルーム(room)を作っていないのであればMy roomsからCreate a new roomボタンでルームを作る。ルームを作成したあと、まずは一番上のアイコン、My Avatarで自分のアバター(分身)を作る。体のパーツなどが複数あるので組み合わせて自分好みのアバターを作ろう。上から2番目のアイコンはMy Wardrobeで衣装棚だ。好みの服装などをここで揃える。3番目のアイコンはPeopleで現在入っている人々の情報を確認できる。4番目のアイコンはInvite Peopleで電子メールなどで知り合いを自分のルームに誘うことができる。5番目のアイコンは調度品・付属品の管理で、ここでルームに置くための様々な品物を選ぶことができる。6番目は調度品などを動かして自由に配置できるようになる。7番目のアイコンは、新しいルームを作ったり、現在のルームの名前や外観を変更することができる。最後のアイコンはほかのルームのリストを表示させる。

以上、簡単な操作方法を説明したが、カタログからはデフォルトで支給されている調度品やルーム外観以外のデータを手に入れることができるのでそちらも確認して欲しい。また、調度品に含まれるTVモニタや写真立てには、それぞれYouTubeの動画を指定したり、Picasaから写真を取り込んだりもできる。なかなか面白い試みだ。

アバターとルームを作ったので、実際にどんなものか確認するためにGoogle Roomに入ってみたが、最初の読み込みに時間がかかった。セカンドライフの時もそうであったがうまく処理できないものだろうか。しかし、それを読み込んでしまえば比較的軽快であった。3年以上前のノートPCでも何とかなるくらいだ。

入ってすぐに周りを見回してみたが、そこには20~30人ほどのアバターがいた。どうやら人数制限で入れない人もいるようだ。チャットは吹き出し形式で、画面に映っていないアバターからの発言でも、吹き出しが見えるようになっている。因みに日本語は直接入力はできないようだが、メモ帳などに書かれた日本語をコピー&ペーストで表示させることができるようだ。アバターのアニメーションはいろいろあって、怒ったり、笑ったり、踊ったり、お辞儀やウインクなど様々だ。二人での動作もあって、たとえば握手したり、手を繋いだり、喧嘩したり、投げ飛ばしたりできるし、キスもできる。周りを見ているだけでも結構面白かった。調度品などにも仕掛けがあるものがあって、巨大なティラノサウルスの化石には大きく吠えるアニメーションが仕掛けてあったり、電話ボックスの扉が開いたり、飛行機のエンジンがかかって火を噴いたりと様々である。現在のところ、ユーザがそれらを作ることはできないようだが、Googleのことだから、そのうち作れるようになる気がする。

また、調度品などのカタログにはPrice(値段)の項目があり、現在では全部無料になっているが、ユーザがこれらを作れるようになった暁には、自分たちで値段を設定できるようになるのではないかと予想している。

セカンドライフのときは自分の好みでないモデリングでかなり嫌気がさしてしまったので、その辺はよりセンスの良いものを期待したい。現在のところはまあまあだと思う。あとは速度と使いやすさが重要だ。特に、テンポよく操作できるだけの軽快さは必須だと思う。個人的にはGoogleらしく調度品などにプログラムを組み込めるようにしてほしい。Pythonでできれば最高なのだが。

自分のルームを公開しておくので気軽に入ってみてほしい。

続きを読む...

2008年7月2日水曜日

Google App Engine: Flashコンテンツをデータベースに入れてみた

このブログ記事をはてなブックマークに追加

Flash置場が欲しかったので、Google App Engineのデータベース(datastore)を使ったFlashコンテンツのアップローダを作ってみた。BlobProperty()を使うことでデータベースには一応入ったが、サイズが大きいFlashでは例の時間制限に引っ掛かってアップロード途中でエラーを吐いてしまった。この制限、どうにかならないかなぁ。

取り敢えず、ソースを以下に示しておく。

flash_uploader.py

#!/usr/bin/env python # -*- coding: utf-8 -*- import cgi, re, urllib, datetime import wsgiref.handlers from google.appengine.api import users from google.appengine.ext import webapp from google.appengine.ext import db # Flash SWFデータ class FlashData(db.Model): author = db.UserProperty() username = db.StringProperty(multiline=False) date = db.DateTimeProperty(auto_now_add=True) title = db.StringProperty(multiline=False) summary = db.TextProperty() width = db.IntegerProperty() height = db.IntegerProperty() content = db.BlobProperty() class Flash(webapp.RequestHandler): def get(self): user = users.get_current_user() self.response.out.write("""<title>Flashコンテンツ - アップローダ</title> <h1>Flashコンテンツ - アップローダ</h1> """) if not user: url = users.create_login_url(self.request.uri) url_linktext = "Login" self.response.out.write("""<p>アップロードを行う場合は、<a href="https://www.google.com/accounts/">Googleアカウント</a>でログインしてください。[<a href="%s">%s</a>]</p> """ % (url, url_linktext)) else: H = (datetime.datetime.utcnow() + datetime.timedelta(hours=9)).hour if H >= 3 and H < 9: greeting = u"おはようございます" elif H >= 9 and H < 18: greeting = u"こんにちは" elif H >= 18 or H < 3: greeting = u"こんばんは" else: greeting = u"こんにちは" url = users.create_logout_url(self.request.uri) url_linktext = "Logout" self.response.out.write("""<p>%s。[<a href="%s">%s</a>]</p> """ % (greeting.encode("utf-8"), url, url_linktext)) self.response.out.write("""<form action="/flash/upload" enctype="multipart/form-data" method="post"> <div><label>タイトル:</label> <input type="text" name="title" size="31" maxlength="255"></input> <label>名前:</label> <input type="text" name="username" size="12" maxlength="50"></input></div> <div><label>概要:</label></div> <div><textarea name="summary" rows="5" cols="60"></textarea></div> <div><label>ファイル:</label> <input type="file" name="flash" /> <label>幅:</label> <input type=text" name="width" size="4" value="300"></input> <label>高さ:</label> <input type=text" name="height" size="4" value="300"></input></div> <div><input type="submit" value="アップロード"></div> </form> """) items = FlashData.all().order("-date").fetch(limit=20) for item in items: self.response.out.write("<div style=\"line-height:140%;\"><ul>") if len(item.title.strip()) == 0: item.title = u"(無題)" self.response.out.write("<dt><strong><a href=\"/flash/play?title=%s&w=%d&h=%d&id=%s\">%s</a></strong> <span style=\"color:gray;font-size:x-small;\">by %s, %s年%s月%s日%s時%s分%s秒</span></dt>" % (urllib.quote_plus(item.title.encode("utf-8")), item.width, item.height, item.key(), cgi.escape(item.title.encode("utf-8")), cgi.escape(item.username.encode("utf-8")), item.date.year, item.date.month, item.date.day, item.date.hour, item.date.minute, item.date.second)) if len(item.summary.strip()) == 0: item.summary = "&nbsp;" self.response.out.write("<dd>%s</dd>" % item.summary.encode("utf-8")) self.response.out.write("</ul></div>") class FlashPlay(webapp.RequestHandler): def get(self): title = urllib.unquote_plus(self.request.get("title")) w = self.request.get("w") h = self.request.get("h") id = self.request.get("id") self.response.out.write("<title>%s</title>" % title) self.response.out.write("<h1>%s</h1>" % title) self.response.out.write("""<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,0,0" width=%s height=%s> <param name="movie" value="/flash/data/%s" /> <param name="quality" value="high" /> <!-- <param name="bgcolor" value="#ffffff" /> --> <embed src="/flash/data/%s" width=%s height=%s quality="high" bgcolor="#ffffff" align="middle" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" /> </object> """ % (w, h, id, w, h, id)) self.response.out.write("<div><a href=\"/flash\">%s</a></div>" % u"戻る") class FlashUpload(webapp.RequestHandler): def post(self): data = FlashData() data.author = users.get_current_user() data.username = self.request.get("username").strip() if not data.username: data.username = data.author.nickname() data.title = self.request.get("title") data.summary = cgi.escape(self.request.get("summary").strip()) width = self.request.get("width") height = self.request.get("height") data.width = int(width) if width.isdigit() else 300 data.height = int(height) if height.isdigit() else 300 flash = self.request.get("flash") data.content = db.Blob(flash) data.date = data.date + datetime.timedelta(hours=9) data.put() self.redirect("/flash") class FlashSWF(webapp.RequestHandler): def get(self, flash_id): content = FlashData.get(flash_id).content self.response.headers["Content-Type"] = "application/x-shockwave-flash" self.response.out.write(content) def main(): application = webapp.WSGIApplication([("/flash", Flash), ("/flash/play", FlashPlay), ("/flash/upload", FlashUpload), ("/flash/data/([^/]+)", FlashSWF)], debug=False) wsgiref.handlers.CGIHandler().run(application) if __name__ == "__main__": main()

追記(2008/7/4): 説明文の表示がずれるバグを修正。

続きを読む...