WebSocket adoption to the game.

Introduction

As I wrote in the previous article, I began some experiments of the network use on the web browser game.

First of all, I evaluated WebSocket performance which could be the easiest to use, to know what I can do with it.

WebSocket is on TCP, so it's little slower for the online coop play though I wanted to do that. So, I used it for the other easy purpose instead.

I'll write down here what I did.

WebSocket evaluation

I first looked for a hosting service on which WebSocket can run, and I found Heroku.

https://www.heroku.com/

I followed this document and built a WebSocket server. (I was very surprised cuz it was really easy to build.)

https://devcenter.heroku.com/articles/node-websockets

This is the server side code which I used. I modified the sample code a little in the above page. It just responds the data which it receives.

I referred to something easy text chat codes when I implemented it, but I'm afraid I've lost the URL...

// server.js
var WebSocketServer = require('ws').Server;
var http = require('http');
var express = require('express');
var app = express();
var port = process.env.PORT || 5000;

app.use(express.static(__dirname + '/'));

var server = http.createServer(app);
server.listen(port);

var wss = new WebSocketServer({server: server});
wss.on('connection', function(ws) {
  ws.on('message', function(data) {

    wss.clients.forEach(function(client) {
      client.send(data);
    });

  });
});


And the following it the client code. It just sends the data to the server and then receives it. It periodically repeats the transfer and calculates fps.

<html>
<head>
<script>
var url = 'http://something.herokuapp.com/'; // dummy URL
var host = url.replace(/^http/, 'ws')
var ws = new WebSocket(host);

var a = 0;
var oldTime = Date.now();

ws.onmessage = function(event) {
  var message = JSON.parse(event.data);
  if(a % 60 == 0) {
    var newTime = Date.now();
    var fps = (1000*60/(newTime - oldTime));
    oldTime = newTime;
    document.getElementById('fps').textContent = parseInt(fps);
  }
  a++;
  requestAnimationFrame(runStep);
};

ws.onopen = function(event) {
  runStep();
};

function runStep() {
  ws.send('{"a": 1}'); // no sense.
};
</script>
</head>
<body>
<span id="fps"></span> fps
</body>
</html>

It resulted in 10fps. This number is just the result from my home to Heroku server. I mean, it depends on the environment.

I wanted it to run at 60fps cuz I want to implement the online coop play into the game.

This slowness could come from the network protocol TCP which WebSocket uses. Prolly I need to give up to get faster performance with WebSocket.

WebSocket adoption to the game

Though I gave up adopting WebSocket for the online coop play, I tried to somehow use it for something on the game.

See the right top white circle on the following snapshop. This is what I implemented.


You can see the number of people who are playing the game and can know the other players status. This information is transferred via WebSocket. It doesn't need so fast performance.

You can play the game on your chrome and see the code.

http://takahirox.github.io/toho-like-js/index.html
https://github.com/takahirox/toho-like-js

Fast network

I can't implement the online coop play, so I've been studying WebRTC which can run on UDP. I'm reading this book to learn, cuz not so many WebRTC information on the web.

Real-Time Communication with WebRTC: Peer-to-Peer in the Browser (English Edition)

Real-Time Communication with WebRTC: Peer-to-Peer in the Browser (English Edition)


This is the link for the US people. http://www.amazon.com/dp/1449371876

Conclusion

I evaluated WebSocket performance with Heroku. Unfortunately it didn't satisfy my expectation to implement the online coop play. But somehow I tried to use WebSocket on the game so far.

What made me surprised is the easy WebSocket server and client building on Heroku.

Though WebSocket performance doesn't reach my requirement, its easy to use is very interesting. I'm gonna use WebSocket for another (personal) project which doesn't need fast network performance, like online card game or something.

The next step of the game project is prolly improvement of CPU-GPU data transfer. Lately the fact the game sometimes doesn't reach 60fps on my environment makes me disappointed... I speculate it's because of CPU-GPU transfer.

Porting the 2D shooter game to WebGL.

Introduction

In the previous article http://d.hatena.ne.jp/takahirox/20140518/1400388214 I found that WebGL can much improve the drawing performance.

So I ported the 2D shooter game which I've been developing to WebGL, not only background but also almost all main shooter game entities.

Screenshot

Play

http://takahirox.github.io/toho-like-js/index.html

You can play it on your chrome. Don't forget to turn your hardware acceleration on.

Source code

https://github.com/takahirox/toho-like-js

You can see the code on GitHub.

Design change

You know, GPU does fast drawing if you transfer the much drawn data at one drawing call.

In the previous design with 2D canvas, I drew entities one by one. So I did the design change to draw much entities at a time.

Entity (called Element) had the drawing method display() which draws it self on 2D canvas. I stopped to use it and made a new class Drawer. It transfers the multiple view data via EntityManager(called ElementMaanger) which manages related Entities.

This design change affect many functions, so it took 2-3 weeks to port... tired.

two layers

I adopted two layers, one is WebGL layer and the other one is 2D canvas layer.

WebGL is fast, but not so straightforward. 2D canvas has the advantage to draw not performance critical element because of its easy to use.

So, I draw the main shooter game related entities onto WebGL layer because they appears on a screen a lot while I draw the others, like simple strings and only one image, on 2D canvas layer.

This idea makes the performance better and doesn't make design and implementation complicated.

Performance bottle neck

The current design and implementation are not so optimized yet. CPU-GPU transfer is one of performance bottle neck.

I have some ideas to improve it, for example partial update and reduce the number of vertices. I'll use them when I meet any performance problems.

Conclusion

I ported the 2D shooter game to WebGL. Adopting two layers reduces the complexity (a little).

Lately I feel I wanna try to use Network feature. It's cool if you play the game with your friend online, isn't it?

WebGL benchmark based on 2D traditional shooting game

Introduction

I made a WebGL benchmark based on 2D traditional shooting game to know the WebGL potential.

I'll introduce it and I got a good result much better than Canvas.

Screenshot



As you see, WebGL displays thousands bullets including various type bullets at 60fps on my old laptop which I bought 6-7 years ago as $650. This result is much better than Canvas one. (I don't have precise number of the Canvas one though. It's annoying to make the Canvas version benchmark, hahaha.)

BTW

I did similar examination before but I got bad performance then because I didn't tune the code for WebGL.

After that I bought this book, read through, and tune the code for WebGL. Then I got the good result.

Professional WebGL Programming: Developing 3D Graphics for the Web

Professional WebGL Programming: Developing 3D Graphics for the Web

This link is for US people -> http://www.amazon.com/dp/1119968860

I'll recommend this book to a novice WebGL engineer who wanna know the pure WebGL because it explains the WebGL in the detail without any WebGL library like THREE.js except for matrix calculation utilities.

WebGL is indeed fast, but it requires tuning.

Conclusion

I confirmed that I can break a performance wall of the 2D browser shooting game with WebGL. I'm gonna adopt WebGL to the game main layer. But it requires not few code changes... Sigh.

Adopted WebGL to draw the 3D background of 2D shooter game.

Introduction

Finally I adopted WebGL to draw the 3D background of the traditional 2D shooter game which I made.

The combination of 2D main game layer and 3D background layer looks good, isn't it?

Screenshot

D

Attention

Turn your hardware acceleration on to fully enjoy the game. The game must be very heavy unless doing it because software emulates WebGL.

See "chrome://gpu" and "chrome://flags" on your chrome to know if the hardware acceleration is enabled.

How

See the last article http://d.hatena.ne.jp/takahirox/20140420/1398019062 to know the idea which uses two layers, they're 2D canvas game layer and 3D background WebGL layer.

Note that I don't use any WebGL utility libraries now except for glMatrix which calculates matrix.

Conclusion

I haven't decided yet to adopt WebGL to draw ALL entities. When drawing speed become the issue again, I'll consider it.

Currently I don't think the drawing speed is a serious problem because the game works at 60fps on my old laptop which I bought 6-7 years ago.

However, if it's very heavy on your environment, please let me know.

WebGL experiment onto JavaScript STG

Introduction

Lately, I've studied WebGL to get used to GPU. I think I understand the basic knowledge, so I began to try to adopt it into the STG which I've been developing.

I've had the problem in the STGthat the canvas drawing leads the slowness, and it limits the the potential(e.g. the number of shots). I hope WebGL solves this problem.

Screenshot

I adopted WebGL to draw the background so far, since it's the heaviest in the STG.


Not sure well why, but the desktop capture soft I use makes the game very slow. So I put the pic here instead of a video. *SOB*

You see it reaches 60 fps even the background draws the heavy 3D graphics. (You can't see in the pic, but the background rotates and scales up/down.)

Of course, the current background is just temporal to know how WebGL works well. I'm going to replace it to the beautiful one.

Idea

I have two layers, one is main layer and the other one is background layer. The trick is to overlay the two layers.

The main layer consists of the all game related entities, like fighter, shots, enemies, and so on except for the background, while the background draws only background.

What I did is, I put main layer in HTML.

<div id="canvasdiv" style="position:relative; width:640px; height:480px">
<canvas id="mainCanvas" width="640" height="480"
 style="z-index: 2; position:absolute;left0px;top0px;"/>
</div>


Then, create the background layer in the game code and put it under the main layer.

  // Currently I use THREE as WebGL library.
  var renderer = new THREE.WebGLRenderer( ) ;
  var div = document.getElementById( 'canvasdiv' ) ;
  var element = renderer.domElement ;
  element.style.zIndex = 1 ;
  element.style.position = 'absolute' ;
  element.style.top = '0px' ;
  element.style.left = '0px' ;
  div.appendChild( element ) ;

Secret talk

To tell you the truth, I tried this idea months ago. But at that time I forgot to turn hardware acceleration on and I wrongly understood WebGL was slow! (When it turned off, software draws.)

Conclusion

I got the good performance to draw the heavy background. Since It broke the limit of drawing potential, finally I can increase the shots in a screen. (You like much many shots in a screen, right?)

I wonder if I should use WebGL to draw all entities. I wanna do it, but it requires not small changes, Hm....

MySQL source code reading 2 - basic flow

Introduction

This article follows http://d.hatena.ne.jp/takahirox/20140331/1396222792.

I'll confirm the basic query processing flow in this article.

Basic flow

The basic processing is written in the internal manual.


And, the internal manual shows us the server sample code with insert.


The server processes a query as followings after it receives the query from a client.

  1. parse a SQL
  2. optimizes(optimizes a query and decide how to handle it)
    1. this is what I wanna secondly see
  3. handling a query
    1. this is what I wanna first see
  4. return results

Select

What I first wanna see is select. This is the backtrace at the point of my_read() when I executed select. my_read() calls libc read() to read table data from disk, it's the lowest function.

#0  my_read (Filedes=40, Buffer=0xa00117f8 "", Count=90, MyFlags=32)
    at /home/takahiro/mysql-server/mysql-5.6/mysys/my_read.c:50
#1  0x08674b65 in inline_mysql_file_read (
    src_file=0x8bbf580 "/home/takahiro/mysql-server/mysql-5.6/mysys/mf_iocache.c
    at /home/takahiro/mysql-server/mysql-5.6/include/mysql/psi/mysql_file.h:1100
#2  0x08675e46 in _my_b_read (info=0xa000fad0, Buffer=0xa000f250 "\377", 
    Count=9) at /home/takahiro/mysql-server/mysql-5.6/mysys/mf_iocache.c:562
#3  0x08996191 in _mi_read_rnd_static_record (info=0xa000f968, 
    buf=0xa000f250 "\377", filepos=0, skip_deleted_blocks=1 '\001')
    at /home/takahiro/mysql-server/mysql-5.6/storage/myisam/mi_statrec.c:279
#4  0x0898f90a in mi_scan (info=0xa000f968, buf=0xa000f250 "\377")
    at /home/takahiro/mysql-server/mysql-5.6/storage/myisam/mi_scan.c:45
#5  0x089560a3 in ha_myisam::rnd_next (this=0xa000f0c8, buf=0xa000f250 "\377")
    at /home/takahiro/mysql-server/mysql-5.6/storage/myisam/ha_myisam.cc:1758
#6  0x082399ed in handler::ha_rnd_next (this=0xa000f0c8, buf=0xa000f250 "\377")
    at /home/takahiro/mysql-server/mysql-5.6/sql/handler.cc:2638
#7  0x085708cb in rr_sequential (info=0xa0005a70)
    at /home/takahiro/mysql-server/mysql-5.6/sql/records.cc:478
#8  0x0839e09a in join_init_read_record (tab=0xa0005a24)
    at /home/takahiro/mysql-server/mysql-5.6/sql/sql_executor.cc:2392
#9  0x0839b8fc in sub_select (join=0xa0005028, join_tab=0xa0005a24, 
    end_of_records=false)
    at /home/takahiro/mysql-server/mysql-5.6/sql/sql_executor.cc:1253
#10 0x0839b324 in do_select (join=0xa0005028)
    at /home/takahiro/mysql-server/mysql-5.6/sql/sql_executor.cc:930
#11 0x0839934e in JOIN::exec (this=0xa0005028)
    at /home/takahiro/mysql-server/mysql-5.6/sql/sql_executor.cc:191
#12 0x083f8959 in mysql_execute_select (thd=0x95a7368, select_lex=0x95a9040, 
    free_join=true)
    at /home/takahiro/mysql-server/mysql-5.6/sql/sql_select.cc:1100
#13 0x083f8c88 in mysql_select (thd=0x95a7368, tables=0xa0004bc0, wild_num=1, 
    fields=..., conds=0x0, order=0x95a9134, group=0x95a90d0, having=0x0, 
    select_options=2147748608, result=0xa0005010, unit=0x95a8bdc, 
    select_lex=0x95a9040)
    at /home/takahiro/mysql-server/mysql-5.6/sql/sql_select.cc:1221
#14 0x083f6d86 in handle_select (thd=0x95a7368, result=0xa0005010, 
    setup_tables_done_option=0)
    at /home/takahiro/mysql-server/mysql-5.6/sql/sql_select.cc:110
#15 0x083d1fda in execute_sqlcom_select (thd=0x95a7368, all_tables=0xa0004bc0)
    at /home/takahiro/mysql-server/mysql-5.6/sql/sql_parse.cc:5094
#16 0x083ca2d0 in mysql_execute_command (thd=0x95a7368)
    at /home/takahiro/mysql-server/mysql-5.6/sql/sql_parse.cc:2642
#17 0x083d4595 in mysql_parse (thd=0x95a7368, 
    rawbuf=0xa0004a78 "select * from a", length=15, parser_state=0xa4dcda18)
    at /home/takahiro/mysql-server/mysql-5.6/sql/sql_parse.cc:6235
#18 0x083c72f0 in dispatch_command (command=COM_QUERY, thd=0x95a7368, 
    packet=0x967e781 "", packet_length=15)
    at /home/takahiro/mysql-server/mysql-5.6/sql/sql_parse.cc:1334
#19 0x083c64b7 in do_command (thd=0x95a7368)
    at /home/takahiro/mysql-server/mysql-5.6/sql/sql_parse.cc:1036
#20 0x0838e99b in do_handle_one_connection (thd_arg=0x95a7368)
    at /home/takahiro/mysql-server/mysql-5.6/sql/sql_connect.cc:982
#21 0x0838e4cf in handle_one_connection (arg=0x95a7368)
    at /home/takahiro/mysql-server/mysql-5.6/sql/sql_connect.cc:898
#22 0x089ea076 in pfs_spawn_thread (arg=0x95bd540)
    at /home/takahiro/mysql-server/mysql-5.6/storage/perfschema/pfs.cc:1858
#23 0xb7f74d4c in start_thread () from /lib/i386-linux-gnu/libpthread.so.0
#24 0xb7d83bae in clone () from /lib/i386-linux-gnu/libc.so.6

From this back trace and the internal manual, mysql_select() or handle_select() could be the basement function for handling select.

Conclusion

I'll read the code for select next article.