<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                # 習題 52: 創建你的 web 游戲 這本書馬上就要結束了。本章的練習對你是一個真正的挑戰。當你完成以后,你就可以算是一個能力不錯的 Python 初學者了。為了進一步學習,你還需要多讀一些書,多寫一些程序,不過你已經具備進一步學習的技能了。接下來的學習就只是時間、動力、以及資源的問題了。 在本章習題中,我們不會去創建一個完整的游戲,取而代之的是我們會為《習題 42》中的游戲創建一個“引擎(engine)”,讓這個游戲能夠在瀏覽器中運行起來。這會涉及到將《習題 42》中的游戲“重構(refactor)”,將《習題 47》中的架構混合進來,添加自動測試代碼,最后創建一個可以運行游戲的 web 引擎。 這是一節很龐大的習題。我預測你要花一周到一個月才能完成它。最好的方法是一點一點來,每晚上完成一點,在進行下一步之前確認上一步有正確完成。 ### 重構《習題 42》的游戲 你已經在兩個練習中修改了 gothonweb 項目,這節習題中你會再修改一次。這種修改的技術叫做“重構(refactoring)”,或者用我喜歡的講法來說,叫“修修補補(fixing stuff)”。重構是一個編程術語,它指的是清理舊代碼或者為舊代碼添加新功能的過程。你其實已經做過這樣的事情了,只不過不知道這個術語而已。這是寫軟件過程的第二個自然屬性。 你在本節中要做的,是將《習題 47》中的可以測試的房間地圖,以及《習題 42》中的游戲這兩樣東西歸并到一起,創建一個新的游戲架構。游戲的內容不會發生變化,只不過我們會通過“重構”讓它有一個更好的架構而已。 第一步是將 ex47/game.py 的內容復制到 gothonweb/map.py中,然后將 tests/ex47_tests.py 的內容復制到 tests/map_tests.py 中,然后再次運行 nosetests,確認他們還能正常工作。 Note 從現在開始我不會再向你展示運行測試的輸出了,我就假設你回去運行這些測試,而且知道怎樣的輸出是正確的。 將《習題 47》的代碼拷貝完畢后,你就該開始重構它,讓它包含《習題 42》中的地圖。我一開始會把基本架構為你準備好,然后你需要去完成 map.py 和 map_tests.py 里邊的內容。 首先要做的是使用 Room 類來構建基本的地圖架構: <table class="highlighttable"><tbody><tr><td class="linenos"> <div class="linenodiv"> <pre> 1&#13; 2&#13; 3&#13; 4&#13; 5&#13; 6&#13; 7&#13; 8&#13; 9&#13; 10&#13; 11&#13; 12&#13; 13&#13; 14&#13; 15&#13; 16&#13; 17&#13; 18&#13; 19&#13; 20&#13; 21&#13; 22&#13; 23&#13; 24&#13; 25&#13; 26&#13; 27&#13; 28&#13; 29&#13; 30&#13; 31&#13; 32&#13; 33&#13; 34&#13; 35&#13; 36&#13; 37&#13; 38&#13; 39&#13; 40&#13; 41&#13; 42&#13; 43&#13; 44&#13; 45&#13; 46&#13; 47&#13; 48&#13; 49&#13; 50&#13; 51&#13; 52&#13; 53&#13; 54&#13; 55&#13; 56&#13; 57&#13; 58&#13; 59&#13; 60&#13; 61&#13; 62&#13; 63&#13; 64&#13; 65&#13; 66&#13; 67&#13; 68&#13; 69&#13; 70&#13; 71&#13; 72&#13; 73&#13; 74&#13; 75&#13; 76&#13; 77&#13; 78&#13; 79&#13; 80&#13; 81&#13; 82&#13; 83&#13; 84&#13; 85&#13; 86&#13; 87&#13; 88&#13; 89&#13; 90&#13; 91&#13; 92&#13; 93&#13; 94&#13; 95&#13; 96&#13; 97&#13; 98&#13; 99&#13; 100&#13; 101&#13; 102&#13; 103&#13; 104&#13; 105&#13; 106&#13; 107&#13; 108&#13; 109&#13; 110&#13; 111&#13; 112&#13; 113&#13; 114&#13; 115&#13; 116&#13; 117&#13; 118&#13; 119&#13; 120&#13; 121&#13; 122&#13; 123&#13; 124&#13; 125&#13; 126&#13; 127&#13; 128</pre> </div> </td> <td class="code"> <div class="highlight"> <pre>class Room(object):&#13; &#13; def __init__(self, name, description):&#13; self.name = name&#13; self.description = description&#13; self.paths = {}&#13; &#13; def go(self, direction):&#13; return self.paths.get(direction, None)&#13; &#13; def add_paths(self, paths):&#13; self.paths.update(paths)&#13; &#13; &#13; central_corridor = Room("Central Corridor",&#13; """&#13; The Gothons of Planet Percal #25 have invaded your ship and destroyed&#13; your entire crew. You are the last surviving member and your last&#13; mission is to get the neutron destruct bomb from the Weapons Armory,&#13; put it in the bridge, and blow the ship up after getting into an &#13; escape pod.&#13; &#13; You're running down the central corridor to the Weapons Armory when&#13; a Gothon jumps out, red scaly skin, dark grimy teeth, and evil clown costume&#13; flowing around his hate filled body. He's blocking the door to the&#13; Armory and about to pull a weapon to blast you.&#13; """)&#13; &#13; &#13; laser_weapon_armory = Room("Laser Weapon Armory",&#13; """&#13; Lucky for you they made you learn Gothon insults in the academy.&#13; You tell the one Gothon joke you know:&#13; Lbhe zbgure vf fb sng, jura fur fvgf nebhaq gur ubhfr, fur fvgf nebhaq gur ubhfr.&#13; The Gothon stops, tries not to laugh, then busts out laughing and can't move.&#13; While he's laughing you run up and shoot him square in the head&#13; putting him down, then jump through the Weapon Armory door.&#13; &#13; You do a dive roll into the Weapon Armory, crouch and scan the room&#13; for more Gothons that might be hiding. It's dead quiet, too quiet.&#13; You stand up and run to the far side of the room and find the&#13; neutron bomb in its container. There's a keypad lock on the box&#13; and you need the code to get the bomb out. If you get the code&#13; wrong 10 times then the lock closes forever and you can't&#13; get the bomb. The code is 3 digits.&#13; """)&#13; &#13; &#13; the_bridge = Room("The Bridge",&#13; """&#13; The container clicks open and the seal breaks, letting gas out.&#13; You grab the neutron bomb and run as fast as you can to the&#13; bridge where you must place it in the right spot.&#13; &#13; You burst onto the Bridge with the netron destruct bomb&#13; under your arm and surprise 5 Gothons who are trying to&#13; take control of the ship. Each of them has an even uglier&#13; clown costume than the last. They haven't pulled their&#13; weapons out yet, as they see the active bomb under your&#13; arm and don't want to set it off.&#13; """)&#13; &#13; &#13; escape_pod = Room("Escape Pod",&#13; """&#13; You point your blaster at the bomb under your arm&#13; and the Gothons put their hands up and start to sweat.&#13; You inch backward to the door, open it, and then carefully&#13; place the bomb on the floor, pointing your blaster at it.&#13; You then jump back through the door, punch the close button&#13; and blast the lock so the Gothons can't get out.&#13; Now that the bomb is placed you run to the escape pod to&#13; get off this tin can.&#13; &#13; You rush through the ship desperately trying to make it to&#13; the escape pod before the whole ship explodes. It seems like&#13; hardly any Gothons are on the ship, so your run is clear of&#13; interference. You get to the chamber with the escape pods, and&#13; now need to pick one to take. Some of them could be damaged&#13; but you don't have time to look. There's 5 pods, which one&#13; do you take?&#13; """)&#13; &#13; &#13; the_end_winner = Room("The End",&#13; """&#13; You jump into pod 2 and hit the eject button.&#13; The pod easily slides out into space heading to&#13; the planet below. As it flies to the planet, you look&#13; back and see your ship implode then explode like a&#13; bright star, taking out the Gothon ship at the same&#13; time. You won!&#13; """)&#13; &#13; &#13; the_end_loser = Room("The End",&#13; """&#13; You jump into a random pod and hit the eject button.&#13; The pod escapes out into the void of space, then&#13; implodes as the hull ruptures, crushing your body&#13; into jam jelly.&#13; """&#13; )&#13; &#13; escape_pod.add_paths({&#13; '2': the_end_winner,&#13; '*': the_end_loser&#13; })&#13; &#13; generic_death = Room("death", "You died.")&#13; &#13; the_bridge.add_paths({&#13; 'throw the bomb': generic_death,&#13; 'slowly place the bomb': escape_pod&#13; })&#13; &#13; laser_weapon_armory.add_paths({&#13; '0132': the_bridge,&#13; '*': generic_death&#13; })&#13; &#13; central_corridor.add_paths({&#13; 'shoot!': generic_death,&#13; 'dodge!': generic_death,&#13; 'tell a joke': laser_weapon_armory&#13; })&#13; &#13; START = central_corridor&#13; </pre> </div> </td> </tr></tbody></table> 你會發現我們的 Room 類和地圖有一些問題: 1. 在進入一個房間以前會打印出一段文字作為房間的描述,我們需要將這些描述和每個房間關聯起來,這樣房間的次序就不會被打亂了,這對我們的游戲是一件好事。這些描述本來是在 if-else 結構中的,這是我們后面要修改的東西。 1. 原版游戲中我們使用了專門的代碼來生成一些內容,例如炸彈的激活鍵碼,艦艙的選擇等,這次我們做游戲時就先使用默認值好了,不過后面的加分習題里,我會要求你把這些功能再加到游戲中。 1. 我為所有的游戲中的失敗結尾寫了一個 generic_death,你需要去補全這個函數。你需要把原版游戲中所有的失敗結尾都加進去,并確保代碼能正確運行。 1. 我添加了一種新的轉換模式,以 "*" 為標記,用來在游戲引擎中實現“catch-all”動作。 等你把上面的代碼基本寫好以后,接下來就是引導你繼續寫下去的自動測試的內容 tests/map_test.py 了: <table class="highlighttable"><tbody><tr><td class="linenos"> <div class="linenodiv"> <pre> 1&#13; 2&#13; 3&#13; 4&#13; 5&#13; 6&#13; 7&#13; 8&#13; 9&#13; 10&#13; 11&#13; 12&#13; 13&#13; 14&#13; 15&#13; 16&#13; 17&#13; 18&#13; 19&#13; 20&#13; 21&#13; 22&#13; 23&#13; 24&#13; 25&#13; 26&#13; 27&#13; 28&#13; 29&#13; 30&#13; 31&#13; 32&#13; 33&#13; 34&#13; 35&#13; 36&#13; 37&#13; 38</pre> </div> </td> <td class="code"> <div class="highlight"> <pre>from nose.tools import *&#13; from gothonweb.map import *&#13; &#13; def test_room():&#13; gold = Room("GoldRoom", &#13; """This room has gold in it you can grab. There's a&#13; door to the north.""")&#13; assert_equal(gold.name, "GoldRoom")&#13; assert_equal(gold.paths, {})&#13; &#13; def test_room_paths():&#13; center = Room("Center", "Test room in the center.")&#13; north = Room("North", "Test room in the north.")&#13; south = Room("South", "Test room in the south.")&#13; &#13; center.add_paths({'north': north, 'south': south})&#13; assert_equal(center.go('north'), north)&#13; assert_equal(center.go('south'), south)&#13; &#13; def test_map():&#13; start = Room("Start", "You can go west and down a hole.")&#13; west = Room("Trees", "There are trees here, you can go east.")&#13; down = Room("Dungeon", "It's dark down here, you can go up.")&#13; &#13; start.add_paths({'west': west, 'down': down})&#13; west.add_paths({'east': start})&#13; down.add_paths({'up': start})&#13; &#13; assert_equal(start.go('west'), west)&#13; assert_equal(start.go('west').go('east'), start)&#13; assert_equal(start.go('down').go('up'), start)&#13; &#13; def test_gothon_game_map():&#13; assert_equal(START.go('shoot!'), generic_death)&#13; assert_equal(START.go('dodge!'), generic_death)&#13; &#13; room = START.go('tell a joke')&#13; assert_equal(room, laser_weapon_armory)&#13; </pre> </div> </td> </tr></tbody></table> 你在這部分練習中的任務是完成地圖,并且讓自動測試可以完整地檢查過整個地圖。這包括將所有的 generic_death 對象修正為游戲中實際的失敗結尾。讓你的代碼成功運行起來,并讓你的測試越全面越好。后面我們會對地圖做一些修改,到時候這些測試將保證修改后的代碼還可以正常工作。 ### 會話(session)和用戶跟蹤 在你的 web 程序運行的某個位置,你需要追蹤一些信息,并將這些信息和用戶的瀏覽器關聯起來。在 HTTP 協議的框架中,web 環境是“無狀態(stateless)”的,這意味著你的每一次請求和你其它的請求都是相互獨立的。如果你請求了頁面 A,輸入了一些數據,然后點了一個頁面 B 的鏈接,那你在頁面 A 輸入的數據就全部消失了。 解決這個問題的方法是為 web 程序建立一個很小的數據存儲功能,給每個瀏覽器進程賦予一個獨一無二的數字,用來跟蹤瀏覽器所作的事情。這個存儲通常用數據庫或者存儲在磁盤上的文件來實現。在 lpthw.web 這個小框架中實現這樣的功能是很容易的,以下就是一個這樣的例子: <table class="highlighttable"><tbody><tr><td class="linenos"> <div class="linenodiv"> <pre> 1&#13; 2&#13; 3&#13; 4&#13; 5&#13; 6&#13; 7&#13; 8&#13; 9&#13; 10&#13; 11&#13; 12&#13; 13&#13; 14&#13; 15&#13; 16&#13; 17&#13; 18&#13; 19&#13; 20&#13; 21&#13; 22&#13; 23&#13; 24</pre> </div> </td> <td class="code"> <div class="highlight"> <pre>import web&#13; &#13; web.config.debug = False&#13; &#13; urls = (&#13; "/count", "count",&#13; "/reset", "reset"&#13; )&#13; app = web.application(urls, locals())&#13; store = web.session.DiskStore('sessions')&#13; session = web.session.Session(app, store, initializer={'count': 0})&#13; &#13; class count:&#13; def GET(self):&#13; session.count += 1&#13; return str(session.count)&#13; &#13; class reset:&#13; def GET(self):&#13; session.kill()&#13; return ""&#13; &#13; if __name__ == "__main__":&#13; app.run()&#13; </pre> </div> </td> </tr></tbody></table> 為了實現這個功能,你需要創建一個 sessions/ 文件夾作為程序的會話存儲位置,創建好以后運行這個程序,然后檢查 /count 頁面,刷新一下這個頁面,看計數會不會累加上去。關掉瀏覽器后,程序就會“忘掉”之前的位置,這也是我們的游戲所需的功能。有一種方法可以讓瀏覽器永遠記住一些信息,不過這會讓測試和開發變得更難。如果你回到 /reset/ 頁面,然后再訪問 /count 頁面,你可以看到你的計數器被重置了,因為你已經把會話殺掉了。 你需要花點時間弄懂這段代碼,注意會話開始時 count 的值是如何設為 0 的。另外再看看 sessions/ 下面的文件,看你能不能把它們打開。下面是我把一個 Python 會話打開并且解碼的過程: ~~~ >>> import pickle >>> import base64 >>> base64.b64decode(open("sessions/XXXXX").read()) "(dp1\nS'count'\np2\nI1\nsS'ip'\np3\nV127.0.0.1\np4\nsS'session_id'\np5\nS'XXXX'\np6\ns." >>> >>> x = base64.b64decode(open("sessions/XXXXX").read()) >>> >>> pickle.loads(x) {'count': 1, 'ip': u'127.0.0.1', 'session_id': 'XXXXX'} ~~~ 所以會話其實就是使用 pickle 和 base64 這些庫寫到磁盤上的字典。存儲和管理會話的方法很多,大概和 Python 的 web 框架那么多,所以了解它們的工作原理并不重要。當然如果你需要調試或者清空會話時,知道點原理還是有用的。 ### 創建引擎 你應該已經寫好了游戲地圖和它的單元測試代碼。現在我要求你制作一個簡單的游戲引擎,用來讓游戲中的各個房間運轉起來,從玩家收集輸入,并且記住玩家到了那一幕。我們將用到你剛學過的會話來制作一個簡單的引擎,讓它可以: 1. 為新用戶啟動新的游戲。 1. 將房間展示給用戶。 1. 接受用戶的輸入。 1. 在游戲中處理用戶的輸入。 1. 顯示游戲的結果,繼續游戲的下一幕,知道玩家角色死亡為止。 為了創建這個引擎,你需要將我們久經考驗的 bin/app.py 搬過來,創建一個功能完備的、基于會話的游戲引擎。這里的難點是我會先使用基本的 HTML 文件創建一個非常簡單的版本,接下來將由你完成它,基本的引擎是這個樣子的: <table class="highlighttable"><tbody><tr><td class="linenos"> <div class="linenodiv"> <pre> 1&#13; 2&#13; 3&#13; 4&#13; 5&#13; 6&#13; 7&#13; 8&#13; 9&#13; 10&#13; 11&#13; 12&#13; 13&#13; 14&#13; 15&#13; 16&#13; 17&#13; 18&#13; 19&#13; 20&#13; 21&#13; 22&#13; 23&#13; 24&#13; 25&#13; 26&#13; 27&#13; 28&#13; 29&#13; 30&#13; 31&#13; 32&#13; 33&#13; 34&#13; 35&#13; 36&#13; 37&#13; 38&#13; 39&#13; 40&#13; 41&#13; 42&#13; 43&#13; 44&#13; 45&#13; 46&#13; 47&#13; 48&#13; 49</pre> </div> </td> <td class="code"> <div class="highlight"> <pre>import web&#13; from gothonweb import map&#13; &#13; urls = (&#13; '/game', 'GameEngine',&#13; '/', 'Index',&#13; )&#13; &#13; app = web.application(urls, globals())&#13; &#13; # little hack so that debug mode works with sessions&#13; if web.config.get('_session') is None:&#13; store = web.session.DiskStore('sessions')&#13; session = web.session.Session(app, store,&#13; initializer={'room': None})&#13; web.config._session = session&#13; else:&#13; session = web.config._session&#13; &#13; render = web.template.render('templates/', base="layout")&#13; &#13; &#13; class Index(object):&#13; def GET(self):&#13; # this is used to "setup" the session with starting values&#13; session.room = map.START&#13; web.seeother("/game")&#13; &#13; &#13; class GameEngine(object):&#13; &#13; def GET(self):&#13; if session.room:&#13; return render.show_room(room=session.room)&#13; else:&#13; # why is there here? do you need it?&#13; return render.you_died()&#13; &#13; def POST(self):&#13; form = web.input(action=None)&#13; &#13; # there is a bug here, can you fix it?&#13; if session.room and form.action:&#13; session.room = session.room.go(form.action)&#13; &#13; web.seeother("/game")&#13; &#13; if __name__ == "__main__":&#13; app.run()&#13; </pre> </div> </td> </tr></tbody></table> 這個腳本里你可以看到更多的新東西,不過了不起的事情是,整個基于網頁的游戲引擎只要一個小文件就可以做到了。這段腳本里最有技術含量的事情就是將會話帶回來的那幾行,這對于調試模式下的代碼重載是必須的,否則每次你刷新網頁,會話就會消失,游戲也不會再繼續了。 在你運行 bin/app.py 之前,你需要修改 PYTHONPATH 環境變量。不知道什么是環境變量?為了運行一個最基本的 Python 程序,你就得學會環境變量,Python 的這一點確實有點挫。不過沒辦法,用 Python 的人就喜歡這樣: 在你的命令行終端,輸入下面的內容: ~~~ export PYTHONPATH=$PYTHONPATH:. ~~~ 如果你用的是 Windows,那就在 PowerShell 中輸入以下內容: ~~~ $env:PYTHONPATH = "$env:PYTHONPATH;." ~~~ 你只要針對每一個命令行會話界面輸入一次就可以了,不過如果你運行 Python 代碼時看到了 import 錯誤,那你就需要去執行一下上面的命令,或者也許是因為你上次執行的 有錯才導致 import 錯誤的。 接下來你需要刪掉 templates/hello_form.html 和 templates/index.html,然后重新創建上面代碼中提到的兩個模板。這里是一個非常簡單的 templates/show_room.html 供你參考: ~~~ $def with (room) <h1> $room.name </h1> <pre> $room.description </pre> $if room.name == "death": <p><a href="/">Play Again?</a></p> $else: <p> <form action="/game" method="POST"> - <input type="text" name="action"> <input type="SUBMIT"> </form> </p> ~~~ 這就用來顯示游戲中的房間的模板。接下來,你需要在用戶跑到地圖的邊界時,用一個 模板告訴用戶他的角色的死亡信息,也就是 templates/you_died.html 這個模板: ~~~ <h1>You Died!</h1> <p>Looks like you bit the dust.</p> <p><a href="/">Play Again</a></p> ~~~ 準備好了這些文件,你現在可以做下面的事情了: 1. 讓測試代碼 tests/app_tests.py 再次運行起來,這樣你就可以去測試這個游戲。由于會話的存在,你可能頂多只能實現幾次點擊,不過你應該可以做出一些基本的測試來。 1. 刪除 sessions/* 下的文件,再重新運行一遍游戲,確認游戲是從一開始運行起來的。 1. 執行 pythonbin/app.py 腳本,試玩一下你的游戲。 你需要和往常一樣刷新和修正你的游戲,慢慢修改游戲的 HTML 文件和引擎,直到你實現游戲需要的所有功能為止。 ### 你的期末考試 你有沒有覺著我一下子給了你超多的信息呢?那就對了,我想要你在學習技能的同時可以有一些可以用來鼓搗的東西。為了完成這節習題,我將給你最后一套需要你自己完成的練習。你將注意到,到目前為止你寫的游戲并不是很好,這只是你的第一版代碼而已。你現在的任務是讓游戲更加完善,實現下面的這些功能: 1. 修正代碼中所有我提到和沒提到的 bug,如果你發現了新的 bug,你可以告訴我。 1. 改進所有的自動測試,讓你可以測試更多的內容,直到你可以不用瀏覽器就能測到所有的內容為止。 1. 讓 HTML 頁面看上去更美觀一些。 1. 研究一下網頁登錄系統,為這個程序創建一個登錄界面,這樣人們就可以登錄這個游戲,并且可以保存游戲高分。 1. 完成游戲地圖,盡可能地把游戲做大,功能做全。 1. 給用戶一個“幫助系統”,讓他們可以查詢每個房間里可以執行哪些命令。 1. 為你的游戲添加新功能,想到什么功能就添加什么功能。 1. 創建多個地圖,讓用戶可以選擇他們想要玩的一張來進行游戲。你的 bin/app.py 應該可以運行提供給它的任意的地圖,這樣你的引擎就可以支持多個不同的游戲。 1. 最后,使用你在習題 48 和 49 中學到的東西來創建一個更好的輸入處理器。你手頭已經有了大部分必要的代碼,你只需要改進語法,讓它和你的輸入表單以及游戲引擎掛鉤即可。 祝你好運!
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看