From c317149ea5182a8f70a8230bf142d61f17e48548 Mon Sep 17 00:00:00 2001 From: CreatCodeBuild Date: Wed, 24 May 2017 21:04:03 -0400 Subject: [PATCH 01/11] - print --- hyper2web/app.py | 1 - hyper2web/http.py | 2 -- hyper2web/router.py | 10 +--------- 3 files changed, 1 insertion(+), 12 deletions(-) diff --git a/hyper2web/app.py b/hyper2web/app.py index f82ea1b..2dd1c7a 100644 --- a/hyper2web/app.py +++ b/hyper2web/app.py @@ -29,7 +29,6 @@ def __init__(self, port=5000, root='./public', if auto_serve_static_file: async def default_get(http, stream, parameters): - print('default_get') route = stream.headers[':path'].lstrip('/') full_path = os.path.join(self.root, route) if os.path.exists(full_path): diff --git a/hyper2web/http.py b/hyper2web/http.py index 9e55c04..0d75e32 100644 --- a/hyper2web/http.py +++ b/hyper2web/http.py @@ -123,7 +123,6 @@ async def data_received(self, event: events.DataReceived): """ if event.stream_id not in self.streams: # But I think this situation is impossible since header should always arrive before data - print('data before header') raise Exception('data before header') # update this handler @@ -161,7 +160,6 @@ async def send_and_end(self, stream: Stream, data: bytes): """Send data associate with this stream to client and end the stream""" # Headers content_type, content_encoding = mimetypes.guess_type(str(data, encoding='utf8')) - print(content_type, content_encoding) response_headers = [ (':status', '200'), ('content-length', str(len(data))), diff --git a/hyper2web/router.py b/hyper2web/router.py index 2c465d1..3f50eaa 100644 --- a/hyper2web/router.py +++ b/hyper2web/router.py @@ -1,5 +1,3 @@ -import os - from .abstract import AbstractRouter from .http import HTTP, Stream @@ -43,7 +41,7 @@ def _match(cls, route, path): # '/something/xxx/' to 'something/xxx'. Get rid of '/' at the left and the right end of a string route = route.lstrip('/').rstrip('/').split('/') path = path.lstrip('/').rstrip('/').split('/') - print(route, path) + if len(route) != len(path): return False, None else: @@ -58,13 +56,10 @@ def _match(cls, route, path): parameters[r[1:-1]] = p elif r != p: return False, None - print('out of for loop') return True, parameters # async async def handle_route(self, http: HTTP, stream: Stream): - print('app.App.handle_route') - path = stream.headers[':path'] method = stream.headers[':method'] @@ -73,7 +68,6 @@ async def handle_route(self, http: HTTP, stream: Stream): # 如果没有任何匹配,就默认为静态文件读取 if route is None: if method == 'GET': - print('GET') handler = self.default_get else: handler = None @@ -81,8 +75,6 @@ async def handle_route(self, http: HTTP, stream: Stream): handler = self._routes[method].get(route, None) if handler is not None: - print('handle') - print(handler) await handler(http, stream, parameters) else: # maybe raise an error? From f92854cfc5ac7ed0434ecc497d635edf0b00fd0a Mon Sep 17 00:00:00 2001 From: CreatCodeBuild Date: Sun, 4 Jun 2017 01:19:26 -0400 Subject: [PATCH 02/11] + game --- .gitignore | 3 ++- hyper2web/app.py | 3 ++- hyper2web/sslsocket.py | 32 +++++++++++++++++++------------- 3 files changed, 23 insertions(+), 15 deletions(-) diff --git a/.gitignore b/.gitignore index b53a6de..18bb811 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,7 @@ # common .idea/ *.pyc -example/ +example/* .cache/ __pycache__/ @@ -9,3 +9,4 @@ __pycache__/ build/ dist/ *.egg-info +!example/game/* \ No newline at end of file diff --git a/hyper2web/app.py b/hyper2web/app.py index 2dd1c7a..bb8775e 100644 --- a/hyper2web/app.py +++ b/hyper2web/app.py @@ -51,7 +51,8 @@ def up(self): kernel.run(h2_server(address=("localhost", self.port), certfile="{}.crt.pem".format("localhost"), keyfile="{}.key".format("localhost"), - app=self)) + app=self), + shutdown=True) def get(self, route: str, handler): self._router.register('GET', route, handler) diff --git a/hyper2web/sslsocket.py b/hyper2web/sslsocket.py index c53a03d..7059b08 100644 --- a/hyper2web/sslsocket.py +++ b/hyper2web/sslsocket.py @@ -1,3 +1,5 @@ +import os + from curio import socket, ssl @@ -5,18 +7,22 @@ def create_listening_ssl_socket(address, certfile, keyfile): """ Create and return a listening TLS socket on a given address. """ - ssl_context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) - ssl_context.options |= ( - ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1 | ssl.OP_NO_COMPRESSION - ) - ssl_context.set_ciphers("ECDHE+AESGCM") - ssl_context.load_cert_chain(certfile=certfile, keyfile=keyfile) - ssl_context.set_alpn_protocols(["h2"]) + # check if 2 files exist. If not, raise exceptions + if os.path.isfile(certfile) and os.path.isfile(keyfile): + ssl_context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) + ssl_context.options |= ( + ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1 | ssl.OP_NO_COMPRESSION + ) + ssl_context.set_ciphers("ECDHE+AESGCM") + ssl_context.load_cert_chain(certfile=certfile, keyfile=keyfile) + ssl_context.set_alpn_protocols(["h2"]) - sock = socket.socket() - sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - sock = ssl_context.wrap_socket(sock) - sock.bind(address) - sock.listen() + sock = socket.socket() + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + sock = ssl_context.wrap_socket(sock) + sock.bind(address) + sock.listen() - return sock + return sock + else: + raise FileNotFoundError(certfile + " and/or " + keyfile + " don't exist. HTTP/2 needs certificate files.") From fa982d1a4a7dce904c2834f5425be322c4358619 Mon Sep 17 00:00:00 2001 From: CreatCodeBuild Date: Sun, 4 Jun 2017 01:22:55 -0400 Subject: [PATCH 03/11] change ignore --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 18bb811..b49c04c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,7 @@ # common .idea/ *.pyc -example/* +/example/* .cache/ __pycache__/ From e127a3c9dcb4af67cbb4e7c06a2ef6c2f329f9e8 Mon Sep 17 00:00:00 2001 From: CreatCodeBuild Date: Sun, 4 Jun 2017 01:23:08 -0400 Subject: [PATCH 04/11] change ignore --- .gitignore | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index b49c04c..8459e58 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,7 @@ # common .idea/ *.pyc -/example/* +example/* .cache/ __pycache__/ @@ -9,4 +9,4 @@ __pycache__/ build/ dist/ *.egg-info -!example/game/* \ No newline at end of file +!/example/game/* \ No newline at end of file From 90399dbdaee61f76662b3444d711db6e1c29f697 Mon Sep 17 00:00:00 2001 From: CreatCodeBuild Date: Sun, 4 Jun 2017 01:23:16 -0400 Subject: [PATCH 05/11] change ignore --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 8459e58..54bd8aa 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,7 @@ # common .idea/ *.pyc -example/* +/example/* .cache/ __pycache__/ From 6251ce62d582abcb30fa9f990fd9a9941b44e463 Mon Sep 17 00:00:00 2001 From: CreatCodeBuild Date: Sun, 4 Jun 2017 01:23:44 -0400 Subject: [PATCH 06/11] change ignore --- .gitignore | 2 +- example/game/app.py | 32 +++ example/game/game_record.json | 1 + example/game/localhost.crt.pem | 21 ++ example/game/localhost.key | 27 +++ example/game/public/game.js | 343 +++++++++++++++++++++++++++++++++ example/game/public/index.css | 6 + example/game/public/index.html | 36 ++++ example/game/public/index.js | 17 ++ 9 files changed, 484 insertions(+), 1 deletion(-) create mode 100644 example/game/app.py create mode 100644 example/game/game_record.json create mode 100644 example/game/localhost.crt.pem create mode 100644 example/game/localhost.key create mode 100644 example/game/public/game.js create mode 100644 example/game/public/index.css create mode 100644 example/game/public/index.html create mode 100644 example/game/public/index.js diff --git a/.gitignore b/.gitignore index 54bd8aa..ae904ba 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,4 @@ __pycache__/ build/ dist/ *.egg-info -!/example/game/* \ No newline at end of file +!/example/game \ No newline at end of file diff --git a/example/game/app.py b/example/game/app.py new file mode 100644 index 0000000..aa59e27 --- /dev/null +++ b/example/game/app.py @@ -0,0 +1,32 @@ +import json + + +from hyper2web import app + + +game_record_path = 'game_record.json' + +try: + with open(game_record_path, encoding='utf8') as f: + game_record = json.load(f, encoding='utf8') +except json.decoder.JSONDecodeError and FileNotFoundError: + print('init a new record') + game_record = {} + + +app = app.App() +# should raise an error if no response method is called +async def post_record(http, stream, para): + record = json.load(stream.data) + http.send_error(stream, 200) + +app.post('/post_record', post_record) + + +try: + app.up() +except BaseException as e: + pass +finally: + with open(game_record_path, mode='w', encoding='utf8') as f: + json.dump(game_record, f) diff --git a/example/game/game_record.json b/example/game/game_record.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/example/game/game_record.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/example/game/localhost.crt.pem b/example/game/localhost.crt.pem new file mode 100644 index 0000000..ebc67ef --- /dev/null +++ b/example/game/localhost.crt.pem @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDhTCCAm2gAwIBAgIJAOrxh0dOYJLdMA0GCSqGSIb3DQEBCwUAMFkxCzAJBgNV +BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX +aWRnaXRzIFB0eSBMdGQxEjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0xNTA5MTkxNDE2 +NDRaFw0xNTEwMTkxNDE2NDRaMFkxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21l +LVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxEjAQBgNV +BAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMqt +A1iu8EN00FU0eBcBGlLVmNEgV7Jkbukra+kwS8j/U2y50QPGJc/FiIVDfuBqk5dL +ACTNc6A/FQcXvWmOc5ixmC3QKKasMpuofqKz0V9C6irZdYXZ9rcsW0gHQIr989yd +R+N1VbIlEVW/T9FJL3B2UD9GVIkUELzm47CSOWZvAxQUlsx8CUNuUCWqyZJoqTFN +j0LeJDOWGCsug1Pkj0Q1x+jMVL6l6Zf6vMkLNOMsOsWsxUk+0L3tl/OzcTgUOCsw +UzY59RIi6Rudrp0oaU8NuHr91yiSqPbKFlX10M9KwEEdnIpcxhND3dacrDycj3ux +eWlqKync2vOFUkhwiaMCAwEAAaNQME4wHQYDVR0OBBYEFA0PN+PGoofZ+QIys2Jy +1Zz94vBOMB8GA1UdIwQYMBaAFA0PN+PGoofZ+QIys2Jy1Zz94vBOMAwGA1UdEwQF +MAMBAf8wDQYJKoZIhvcNAQELBQADggEBAEplethBoPpcP3EbR5Rz6snDDIcbtAJu +Ngd0YZppGT+P0DYnPJva4vRG3bb84ZMSuppz5j67qD6DdWte8UXhK8BzWiHzwmQE +QmbKyzzTMKQgTNFntpx5cgsSvTtrHpNYoMHzHOmyAOboNeM0DWiRXsYLkWTitLTN +qbOpstwPubExbT9lPjLclntShT/lCupt+zsbnrR9YiqlYFY/fDzfAybZhrD5GMBY +XdMPItwAc/sWvH31yztarjkLmld76AGCcO5r8cSR/cX98SicyfjOBbSco8GkjYNY +582gTPkKGYpStuN7GNT5tZmxvMq935HRa2XZvlAIe8ufp8EHVoYiF3c= +-----END CERTIFICATE----- \ No newline at end of file diff --git a/example/game/localhost.key b/example/game/localhost.key new file mode 100644 index 0000000..a114ad6 --- /dev/null +++ b/example/game/localhost.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAyq0DWK7wQ3TQVTR4FwEaUtWY0SBXsmRu6Str6TBLyP9TbLnR +A8Ylz8WIhUN+4GqTl0sAJM1zoD8VBxe9aY5zmLGYLdAopqwym6h+orPRX0LqKtl1 +hdn2tyxbSAdAiv3z3J1H43VVsiURVb9P0UkvcHZQP0ZUiRQQvObjsJI5Zm8DFBSW +zHwJQ25QJarJkmipMU2PQt4kM5YYKy6DU+SPRDXH6MxUvqXpl/q8yQs04yw6xazF +ST7Qve2X87NxOBQ4KzBTNjn1EiLpG52unShpTw24ev3XKJKo9soWVfXQz0rAQR2c +ilzGE0Pd1pysPJyPe7F5aWorKdza84VSSHCJowIDAQABAoIBACp+nh4BB/VMz8Wd +q7Q/EfLeQB1Q57JKpoqTBRwueSVai3ZXe4CMEi9/HkG6xiZtkiZ9njkZLq4hq9oB +2z//kzMnwV2RsIRJxI6ohGy+wR51HD4BvEdlTPpY/Yabpqe92VyfSYxidKZWaU0O +QMED1EODOw4ZQ+4928iPrJu//PMB4e7TFao0b9Fk/XLWtu5/tQZz9jsrlTi1zthh +7n+oaGNhfTeIJJL4jrhTrKW1CLHXATtr9SJlfZ3wbMxQVeyj2wUlP1V0M6kBuhNj +tbGbMpixD5iCNJ49Cm2PHg+wBOfS3ADGIpi3PcGw5mb8nB3N9eGBRPhLShAlq5Hi +Lv4tyykCgYEA8u3b3xJ04pxWYN25ou/Sc8xzgDCK4XvDNdHVTuZDjLVA+VTVPzql +lw7VvJArsx47MSPvsaX/+4hQXYtfnR7yJpx6QagvQ+z4ludnIZYrQwdUmb9pFL1s +8UNj+3j9QFRPenIiIQ8qxxNIQ9w2HsVQ8scvc9CjYop/YYAPaQyHaL8CgYEA1ZSz +CR4NcpfgRSILdhb1dLcyw5Qus1VOSAx3DYkhDkMiB8XZwgMdJjwehJo9yaqRCLE8 +Sw5znMnkfoZpu7+skrjK0FqmMpXMH9gIszHvFG8wSw/6+2HIWS19/wOu8dh95LuC +0zurMk8rFqxgWMWF20afhgYrUz42cvUTo10FVB0CgYEAt7mW6W3PArfUSCxIwmb4 +VmXREKkl0ATHDYQl/Cb//YHzot467TgQll883QB4XF5HzBFurX9rSzO7/BN1e6I0 +52i+ubtWC9xD4fUetXMaQvZfUGxIL8xXgVxDWKQXfLiG54c8Mp6C7s6xf8kjEUCP +yR1F0SSA/Pzb+8RbY0p7eocCgYA+1rs+SXtHZev0KyoYGnUpW+Uxqd17ofOgOxqj +/t6c5Z+TjeCdtnDTGQkZlo/rT6XQWuUUaDIXxUbW+xEMzj4mBPyXBLS1WWFvVQ5q +OpzO9E/PJeqAH6rkof/aEelc+oc/zvOU1o9uA+D3kMvgEm1psIOq2RHSMhGvDPA0 +NmAk+QKBgQCwd1681GagdIYSZUCBecnLtevXmIsJyDW2yR1NNcIe/ukcVQREMDvy +5DDkhnGDgnV1D5gYcXb34g9vYvbfTnBMl/JXmMAAG1kIS+3pvHyN6f1poVe3yJV1 +yHVuvymnJxKnyaV0L3ntepVvV0vVNIkA3oauoUTLto6txBI+b/ImDA== +-----END RSA PRIVATE KEY----- \ No newline at end of file diff --git a/example/game/public/game.js b/example/game/public/game.js new file mode 100644 index 0000000..b21ac2d --- /dev/null +++ b/example/game/public/game.js @@ -0,0 +1,343 @@ +function Game() { + + //used as a static factory + let MagicalPoint = { + construct: function(x, y, radius) { + let shape = new createjs.Shape(); + shape.graphics.beginFill(createjs.Graphics.getRGB(0,0,0)); + shape.graphics.drawCircle(0, 0, radius); + shape.x = x; + shape.y = y; + return shape; + }, + + constructElectron: function(x, y, radius) { + let shape = new createjs.Shape(); + shape.graphics.beginFill(createjs.Graphics.getRGB(200,0,200)); + shape.graphics.drawCircle(0, 0, radius); + shape.x = x; + shape.y = y; + return shape; + }, + + constructNucleus: function(x, y, radius) { + let shape = new createjs.Shape(); + shape.graphics.beginFill(createjs.Graphics.getRGB(0,255,255)); + shape.graphics.drawCircle(0, 0, radius); + shape.x = x; + shape.y = y; + return shape; + }, + + onKeyBoard: function(event) { + let KEYCODE_LEFT = 37, + KEYCODE_RIGHT = 39, + KEYCODE_UP = 38, + KEYCODE_DOWN = 40; + + console.log(Animator.direction); + switch(event.keyCode) { + case KEYCODE_LEFT: + //gotten.to({x: shape.x-distance}, time); + Animator.direction.x -= 1; + break; + case KEYCODE_RIGHT: + //gotten.to({x: shape.x+distance}, time); + Animator.direction.x += 1; + break; + case KEYCODE_UP: + // gotten.to({y: shape.y-distance}, time); + Animator.direction.y -= 1; + break; + case KEYCODE_DOWN: + // gotten.to({y: shape.y+distance}, time); + Animator.direction.y += 1; + break; + } + } + }; + + //used as a static singleton + let Animator = { + init: function(stage) { + console.log('Animator.init()'); + this.stage = stage; + this.userPoint = undefined; + this.electrons = undefined; + this.nucleus = undefined; + Animator.direction = {x: 0, y:0}; + }, + + findPosition: function(center, radius, xth, totalNumber) { + let radian = Math.PI*2/totalNumber * xth; + let cos = Math.cos(radian); + let sin = Math.sin(radian); + console.log(cos, sin); + let x = radius * cos + center.x; + let y = radius * sin + center.y; + console.log(x, y); + return { + x: x, + y: y + }; + }, + + moveUserPoint: function() { + + if(this.userPoint.x > 800) { + this.userPoint.x = 0; + } + if(this.userPoint.x < 0) { + this.userPoint.x = 800; + } + if(this.userPoint.y > 600) { + this.userPoint.y = 0; + } + if(this.userPoint.y < 0) { + this.userPoint.y = 600; + } + this.userPoint.x += Animator.direction.x; + this.userPoint.y += Animator.direction.y; + }, + + /* + electron: Shape + nucleus: Shape + distance: number, angular distance in radians + time: number + */ + orbiting: function(electron, nucleus, distance) { + console.log('Animator.orbiting()'); + let radius = Math.sqrt( + (nucleus.x-electron.x)*(nucleus.x-electron.x) + + (nucleus.y-electron.y)*(nucleus.y-electron.y)); + + let x1 = electron.x - nucleus.x; + let y1 = electron.y - nucleus.y; + + let cos1 = x1/radius; + let sin1 = y1/radius; + let radian = Math.acos(cos1); + if(y1 < 0) { + radian += Math.PI; + } + + createjs.Ticker.addEventListener("tick", function() { + //bring the whole coordinates down to 0 centered + radian += distance; + + let cos2 = Math.cos(radian); + let sin2 = Math.sin(radian); + + let x2 = radius*cos2; + let y2 = radius*sin2; + + electron.x = x2 + nucleus.x; + electron.y = y2 + nucleus.y; + + Animator.stage.update(); + }); + }, + + collisionDetection: function(shape, otherShapes) { + otherShapes.forEach(function(item, index, array) { + if(Animator.isCollided(shape, item)) { + Game.handleCollision(); + } + }); + }, + + //both shape1 and shape2 have to be circles + isCollided: function(shape1, shape2) { + let r1 = shape1.graphics.command.radius; + let r2 = shape2.graphics.command.radius; + + let x1 = shape1.x; + let y1 = shape1.y; + let x2 = shape2.x; + let y2 = shape2.y; + + let distance = Math.sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2)); + + if(distance < (r1+r2)) { + console.log(x1, y1); + console.log(x2, y2); + return true; + } else { + return false; + } + } + }; + + //used as static singleton + let Game = { + init: function() { + this.currentLevel = 0; + this.levels = [ + { + name: 'Hydrogen', + electron: [1] + }, + { + name: 'Helium', + electron: [2] + }, + { + name: 'Lithium', + electron: [2,1] + }, + { + name: 'Beryllium', + electron: [2,2] + }, + { + name: 'Baron', + electron: [2,3] + }, + { + name: 'Carbon', + electron: [2,4] + }, + { + name: 'Nitrogen', + electron: [2,5] + }, + { + name: 'Carbon', + electron: [2,6] + }, + { + name: 'Nitrogen', + electron: [2,7] + }, + { + name: 'Nitrogen', + electron: [2,8] + } + ]; + this.startPos = { + x: 400, + y: 550, + size: 10 + }; + this.nucleusPos = { + x: 400, + y: 250, + size: 25 + }; + }, + + run: function() { + let stage = new createjs.Stage("game_canvas"); + Animator.init(stage); + Game.currentLevel = 4; + this.initLevel(4); + }, + + initLevel: function(levelIndex) { + console.log('Game.initLevel('+this.currentLevel+')'); + + Animator.userPoint = undefined; + Animator.electrons = undefined; + Animator.nucleus = undefined; + Animator.direction = {x: 0, y:0}; + + // createjs.Ticker.removeAllEventListeners(); + createjs.Tween.removeAllTweens(); + Animator.stage.removeAllChildren(); + + //get current level + let level = this.levels[levelIndex]; + + //draw target name + let targetText = level.name; + let text = new createjs.Text(targetText, "20px Arial", "#ff7700"); + text.x = 363; + text.y = 50; + text.textBaseline = "alphabetic"; + Animator.stage.addChild(text); + Animator.targetText = text; + + //init nucleus + Animator.nucleus = MagicalPoint.constructNucleus(this.nucleusPos.x, this.nucleusPos.y, this.nucleusPos.size); + Animator.stage.addChild(Animator.nucleus); + + let numberOfOrbits = level.electron.length; + Animator.electrons = []; + //init electrons + for(let i = 0; i < numberOfOrbits; i++) { + let electronsPerOrbit = level.electron[i]; + let nthOrbit = i+1; + let radius = nthOrbit*50; + for(let j = 0; j < electronsPerOrbit; j++) { + let pos = Animator.findPosition(Animator.nucleus, radius, j, electronsPerOrbit); + let electron = MagicalPoint.constructElectron(pos.x, pos.y, 5); + //console.log(pos.x, pos.y); + Animator.orbiting(electron, Animator.nucleus, nthOrbit*0.06/(nthOrbit*2-1)); + Animator.stage.addChild(electron); + Animator.electrons.push(electron); + } + } + + //the point gamer controls + Animator.userPoint = MagicalPoint.construct(this.startPos.x, this.startPos.y, this.startPos.size); + Animator.stage.addChild(Animator.userPoint); + document.onkeydown = MagicalPoint.onKeyBoard; + + Animator.stage.update(); + //register animation events + createjs.Ticker.setFPS(30); + createjs.Ticker.addEventListener("tick", function() { + Animator.moveUserPoint(); + Animator.collisionDetection(Animator.userPoint, Animator.electrons); + Game.checkGameState(); + }); + createjs.Ticker.addEventListener("tick", Animator.stage); + }, + + goToNextLevel: function() { + console.log('Game.goToNextLevel()'); + if(this.currentLevel < this.levels.length-1) { + this.currentLevel++; + this.initLevel(this.currentLevel); + } else { + //finished the game + createjs.Ticker.removeAllEventListeners(); + createjs.Tween.removeAllTweens(); + let text = new createjs.Text("Congradulations! Last Level 达成!", "40px Arial", "000000"); + text.x = 140; + text.y = 200; + text.textBaseline = "alphabetic"; + Animator.stage.addChild(text); + } + }, + + isLevelCompleted: function() { + //console.log('Game.isLevelCompleted()'); + return Animator.isCollided(Animator.nucleus, Animator.userPoint); + }, + + checkGameState: function() { + //console.log('Game.checkGameState()'); + if(Game.isLevelCompleted()) { + Game.goToNextLevel(); + } + }, + + handleCollision: function() { + console.log('Game.handleCollision()'); + createjs.Ticker.removeAllEventListeners(); + createjs.Tween.removeAllTweens(); + let text = new createjs.Text("You Died", "40px Arial", "000000"); + text.x = 200; + text.y = 200; + text.textBaseline = "alphabetic"; + Animator.stage.addChild(text); + } + }; + + return { //public API + Game: Game + } +} + diff --git a/example/game/public/index.css b/example/game/public/index.css new file mode 100644 index 0000000..e96a939 --- /dev/null +++ b/example/game/public/index.css @@ -0,0 +1,6 @@ +#about_this_app { + position: fixed; + bottom: 0; + width: 100%; + text-align: center; +} \ No newline at end of file diff --git a/example/game/public/index.html b/example/game/public/index.html new file mode 100644 index 0000000..47481c6 --- /dev/null +++ b/example/game/public/index.html @@ -0,0 +1,36 @@ + + + + + Atom Master 元素大师 + + + + + + + + + + + + +
+

User Arrow Key to control the point

+

Your Goal is to reach the center, avoiding blue electrons

+
+ +
+ This application is served by + Hyper2Web + , an enjoyable Python web framework written by me. +
+ + + \ No newline at end of file diff --git a/example/game/public/index.js b/example/game/public/index.js new file mode 100644 index 0000000..8875074 --- /dev/null +++ b/example/game/public/index.js @@ -0,0 +1,17 @@ +let game_canvas = new Vue({ + el: '#game_canvas', + data: { + + } +}); + +let game_ui = new Vue({ + el: '#game_ui', + data: { + + } +}); + +let game = Game().Game +game.init(); +game.run(); From 76a2eea865a7939fc795429f55695327c3c02e03 Mon Sep 17 00:00:00 2001 From: CreatCodeBuild Date: Sun, 4 Jun 2017 01:25:12 -0400 Subject: [PATCH 07/11] change ignore --- .gitignore | 1 + example/game/game_record.json | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 example/game/game_record.json diff --git a/.gitignore b/.gitignore index ae904ba..2c57d08 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ .idea/ *.pyc /example/* +/example/game/game_record.json .cache/ __pycache__/ diff --git a/example/game/game_record.json b/example/game/game_record.json deleted file mode 100644 index 9e26dfe..0000000 --- a/example/game/game_record.json +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file From 35589a12a2c32aa4ea8c41368df94056a2462f2c Mon Sep 17 00:00:00 2001 From: CreatCodeBuild Date: Sun, 4 Jun 2017 02:26:06 -0400 Subject: [PATCH 08/11] added a readme to game --- example/game/readme.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 example/game/readme.md diff --git a/example/game/readme.md b/example/game/readme.md new file mode 100644 index 0000000..944ad4f --- /dev/null +++ b/example/game/readme.md @@ -0,0 +1,14 @@ +# Element Master Game Example +This example is a HTML5 game I created. The game is written in VueJS and is served by Hyper2Web. + +# Backend functionality demonstrated by this game +0. GET + Needless to say. GET has to work in order to load the page. +1. POST + After the user beats a level, the front end sends(post) a time record to the backend. The backend keeps track of the best plays. +2. Server Push + This HTTP/2 new feature is not supported by the framework yet. + + When it is supported, the user will get live update of top10 plays of the current level. + + In the old days of HTTP/1. One can only implements this update with WebSocket or constantly GET requests. From c181c0a0f30f46a2334de3fcd4101908cfd70889 Mon Sep 17 00:00:00 2001 From: CreatCodeBuild Date: Sun, 4 Jun 2017 02:27:58 -0400 Subject: [PATCH 09/11] added a readme to game --- example/game/readme.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/example/game/readme.md b/example/game/readme.md index 944ad4f..b45b99e 100644 --- a/example/game/readme.md +++ b/example/game/readme.md @@ -3,10 +3,13 @@ This example is a HTML5 game I created. The game is written in VueJS and is serv # Backend functionality demonstrated by this game 0. GET + Needless to say. GET has to work in order to load the page. 1. POST + After the user beats a level, the front end sends(post) a time record to the backend. The backend keeps track of the best plays. 2. Server Push + This HTTP/2 new feature is not supported by the framework yet. When it is supported, the user will get live update of top10 plays of the current level. From 775190a981c94f8ce79e1ccf354d32fa9457a524 Mon Sep 17 00:00:00 2001 From: CreatCodeBuild Date: Mon, 5 Jun 2017 01:49:08 -0400 Subject: [PATCH 10/11] + top10 rank --- example/game/app.py | 37 +++++++++++++++++----------------- example/game/game.py | 31 ++++++++++++++++++++++++++++ example/game/public/game.js | 23 +++++++++++++-------- example/game/public/index.html | 13 ++++++++++-- example/game/public/index.js | 15 +++++++++++--- example/game/public/service.js | 20 ++++++++++++++++++ 6 files changed, 107 insertions(+), 32 deletions(-) create mode 100644 example/game/game.py create mode 100644 example/game/public/service.js diff --git a/example/game/app.py b/example/game/app.py index aa59e27..8995e10 100644 --- a/example/game/app.py +++ b/example/game/app.py @@ -1,32 +1,31 @@ import json +from curio import aopen from hyper2web import app - - -game_record_path = 'game_record.json' - -try: - with open(game_record_path, encoding='utf8') as f: - game_record = json.load(f, encoding='utf8') -except json.decoder.JSONDecodeError and FileNotFoundError: - print('init a new record') - game_record = {} +from game import update_record, game_record, game_record_path app = app.App() # should raise an error if no response method is called +# should raise an error if response method is not called with await async def post_record(http, stream, para): - record = json.load(stream.data) - http.send_error(stream, 200) + record = json.loads(str(stream.data, encoding='utf8')) + update_record(record, game_record) + await http.send_error(stream, 200) + + # write records to disk + async with aopen(game_record_path, mode='w') as f: + game_record_string = json.dumps(game_record, indent='\t') + await f.write(game_record_string) app.post('/post_record', post_record) -try: - app.up() -except BaseException as e: - pass -finally: - with open(game_record_path, mode='w', encoding='utf8') as f: - json.dump(game_record, f) +async def get_top10(http, stream, para): + level_top10 = game_record.get(para['levelIndex'], []) + string = json.dumps(level_top10) + await http.send_and_end(stream, bytes(string, encoding='utf8')) +app.get('/get_top10/{levelIndex}', get_top10) + +app.up() diff --git a/example/game/game.py b/example/game/game.py new file mode 100644 index 0000000..7e04256 --- /dev/null +++ b/example/game/game.py @@ -0,0 +1,31 @@ +import json + + +def update_record(this_record, old_records): + timeUsed = this_record['timeUsed'] + + levelIndex = str(this_record['level']) + level_top10 = old_records.get(levelIndex, []) + + i = 0 + while i < 10 and i < len(level_top10): + if timeUsed < level_top10[i]['timeUsed']: + level_top10.insert(i, this_record) + break + i += 1 + else: + level_top10.insert(i, this_record) + if len(level_top10) > 10: + level_top10.pop() + + old_records[levelIndex] = level_top10 + + +game_record_path = 'game_record.json' + +try: + with open(game_record_path, encoding='utf8') as f: + game_record = json.load(f, encoding='utf8') +except json.decoder.JSONDecodeError and FileNotFoundError: + print('init a new record') + game_record = {} diff --git a/example/game/public/game.js b/example/game/public/game.js index b21ac2d..2c6b6c0 100644 --- a/example/game/public/game.js +++ b/example/game/public/game.js @@ -1,4 +1,4 @@ -function Game() { +const Game = (function() { //used as a static factory let MagicalPoint = { @@ -230,8 +230,8 @@ function Game() { run: function() { let stage = new createjs.Stage("game_canvas"); Animator.init(stage); - Game.currentLevel = 4; - this.initLevel(4); + Game.currentLevel = 0; + this.initLevel(Game.currentLevel); }, initLevel: function(levelIndex) { @@ -293,6 +293,9 @@ function Game() { Game.checkGameState(); }); createjs.Ticker.addEventListener("tick", Animator.stage); + + // set up timer + this.levels[this.currentLevel].startTime = Date.now() }, goToNextLevel: function() { @@ -320,6 +323,13 @@ function Game() { checkGameState: function() { //console.log('Game.checkGameState()'); if(Game.isLevelCompleted()) { + // todo: post time record to server + let timeUsed = Date.now() - this.levels[this.currentLevel].startTime; + Service.post_record({ + level: this.currentLevel, + timeUsed: timeUsed, + user: "Master" + }); Game.goToNextLevel(); } }, @@ -336,8 +346,5 @@ function Game() { } }; - return { //public API - Game: Game - } -} - + return Game; +})(); diff --git a/example/game/public/index.html b/example/game/public/index.html index 47481c6..a57ce99 100644 --- a/example/game/public/index.html +++ b/example/game/public/index.html @@ -2,7 +2,7 @@ - Atom Master 元素大师 + 元素大师 @@ -10,6 +10,7 @@ + @@ -21,8 +22,16 @@ border:solid 1px black; margin-top: 60px;'> +
+

天地英雄榜

+
    +
  • + {{index+1}}: {{ item.user }}, {{item.timeUsed}} 毫秒 +
  • +
+
-

User Arrow Key to control the point

+

Use Arrow Key to control the point

Your Goal is to reach the center, avoiding blue electrons

diff --git a/example/game/public/index.js b/example/game/public/index.js index 8875074..f0b94ed 100644 --- a/example/game/public/index.js +++ b/example/game/public/index.js @@ -12,6 +12,15 @@ let game_ui = new Vue({ } }); -let game = Game().Game -game.init(); -game.run(); +let top10_list = new Vue({ + el: "#天地英雄榜", + data: { + top10: undefined + } +}); + +// get top10 +Service.get_top10(top10_list, 0); + +Game.init(); +Game.run(); diff --git a/example/game/public/service.js b/example/game/public/service.js new file mode 100644 index 0000000..43810c3 --- /dev/null +++ b/example/game/public/service.js @@ -0,0 +1,20 @@ +const Service = (function() { + return { + post_record: function post_record(record) { + record = JSON.stringify(record); + return fetch('/post_record', + { + method: 'POST', + body: record + }); + }, + + get_top10: function get_top10(model, levelIndex) { + fetch('/get_top10/'+levelIndex, {method: 'GET'}).then(function(response) { + response.json().then(function(data) { + model.top10 = data; + }) + }); + } + }; +})(); From 60d4ef6b4a1c3a0c644e48e9d4ac37fe598550c2 Mon Sep 17 00:00:00 2001 From: CreatCodeBuild Date: Mon, 5 Jun 2017 03:40:03 -0400 Subject: [PATCH 11/11] change read me and refined game --- README.md | 11 ++++++----- example/game/public/game.js | 12 +++++++----- example/game/public/index.css | 16 ++++++++++++---- example/game/public/index.html | 13 ++++--------- example/game/public/index.js | 23 ++++++----------------- example/game/public/service.js | 8 ++++++-- 6 files changed, 41 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index 306144d..d80a6c4 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,9 @@ # Super Fast HTTP2 Framework for Progressive Web Application # Installation -Clone this project to your local directory. In this directory, -``` -python setup.py -``` -This will automatically install `hyper2web` to the associated Python as a site-package. +At this point, the best way to install it is probably download the zip and extract it to your projects' directory. + +I have not figured out how to make it installable with `pip` yet. I will make it available on PyPi once I have the first release. @@ -56,6 +54,9 @@ from hyper2web import app app.App(port=5000).up() ``` +# Example +See the example folders for examples. + # Test Python modules/packages and imports are confusing. You have to do ```python diff --git a/example/game/public/game.js b/example/game/public/game.js index 2c6b6c0..334f070 100644 --- a/example/game/public/game.js +++ b/example/game/public/game.js @@ -203,15 +203,15 @@ const Game = (function() { electron: [2,5] }, { - name: 'Carbon', + name: 'Oxygen', electron: [2,6] }, { - name: 'Nitrogen', + name: 'Fluorine', electron: [2,7] }, { - name: 'Nitrogen', + name: 'Neon', electron: [2,8] } ]; @@ -295,7 +295,9 @@ const Game = (function() { createjs.Ticker.addEventListener("tick", Animator.stage); // set up timer - this.levels[this.currentLevel].startTime = Date.now() + this.levels[this.currentLevel].startTime = Date.now(); + // get top10 of this level from server + Service.get_top10(this.currentLevel) }, goToNextLevel: function() { @@ -307,7 +309,7 @@ const Game = (function() { //finished the game createjs.Ticker.removeAllEventListeners(); createjs.Tween.removeAllTweens(); - let text = new createjs.Text("Congradulations! Last Level 达成!", "40px Arial", "000000"); + let text = new createjs.Text("Congratulations! Last Level 达成!", "40px Arial", "000000"); text.x = 140; text.y = 200; text.textBaseline = "alphabetic"; diff --git a/example/game/public/index.css b/example/game/public/index.css index e96a939..9f8b9eb 100644 --- a/example/game/public/index.css +++ b/example/game/public/index.css @@ -1,6 +1,14 @@ #about_this_app { - position: fixed; - bottom: 0; - width: 100%; - text-align: center; + position: fixed; + bottom: 0; + width: 100%; + text-align: center; +} + +#game_canvas { + border:solid 1px black; +} + +h3 { + margin: 10px; } \ No newline at end of file diff --git a/example/game/public/index.html b/example/game/public/index.html index a57ce99..71fd45a 100644 --- a/example/game/public/index.html +++ b/example/game/public/index.html @@ -14,14 +14,8 @@ - - + +

天地英雄榜

    @@ -30,7 +24,8 @@

    天地英雄榜

-
+ +

Use Arrow Key to control the point

Your Goal is to reach the center, avoiding blue electrons

diff --git a/example/game/public/index.js b/example/game/public/index.js index f0b94ed..8ef4acf 100644 --- a/example/game/public/index.js +++ b/example/game/public/index.js @@ -1,26 +1,15 @@ -let game_canvas = new Vue({ - el: '#game_canvas', - data: { - - } -}); - let game_ui = new Vue({ - el: '#game_ui', - data: { - - } + el: '#game_ui', + data: {} }); let top10_list = new Vue({ - el: "#天地英雄榜", - data: { - top10: undefined - } + el: "#天地英雄榜", + data: {top10: undefined} }); -// get top10 -Service.get_top10(top10_list, 0); +// get top10. This is an inferior solution. +Service.top10_list_vue = top10_list; Game.init(); Game.run(); diff --git a/example/game/public/service.js b/example/game/public/service.js index 43810c3..bc9721f 100644 --- a/example/game/public/service.js +++ b/example/game/public/service.js @@ -9,10 +9,14 @@ const Service = (function() { }); }, - get_top10: function get_top10(model, levelIndex) { + //model: a Vue object + //GET top 10 of a level and assign it to model.top10 + get_top10: function get_top10(levelIndex) { + let self = this; fetch('/get_top10/'+levelIndex, {method: 'GET'}).then(function(response) { response.json().then(function(data) { - model.top10 = data; + console.log(self); + self.top10_list_vue.top10 = data; }) }); }