In the previous two parts (
Making web sites for PHP from scratch and
Interprocess communication ), I used chats as a demonstration, but in this article, using the example of an online game, I will show that the scope of using web websites can be much broader.
As usual, at the end of the article links to the demo game and the source code on github.
Content:
- Browser support for web sockets
- Online game development
- Thanks
- Demo and source code
Browser support for web sockets
Some people think that it’s too early to use web sockets, because they are not yet supported by all browsers. Therefore, if they are used, then only in conjunction with alternative transports: Adobe® Flash® Socket, AJAX long polling, AJAX multipart streaming, Forever Iframe, JSONP Polling.
')
Wikipedia tells us which browsers support
web sites :
Google Chrome (since version 4.0.249.0);
Apple Safari (starting with version 5.0.7533.16);
Mozilla Firefox (since version 4);
Opera (since version 10.70 9067);
Internet Explorer (since version 10);
As we can see, the weakest link is Internet Explorer with versions less than a tenth. According to
liveinternet statistics, for Russia - Internet Explorer with versions 9, 8, 7 and 6 has shares of 1.4, 1.7, 0.5 and 0.1 percent, respectively. Totally it turns out 3.7%. If you add to this figure more users with outdated versions of other browsers, then the final score may increase slightly, but I do not think that it will become more than 4%.
Based on this, everyone must decide for himself whether to support the zoo of alternative transports or forget about these users and live on.
For the sake of fairness, I want to say that the share of Internet Explorer is greater abroad, and the situation with the support of websockets is appropriate there. According to statistics from the Internet Explorer
w3schools site with versions 9, 8, 7 and 6, the shares are 2.3, 3.1, 0.4 and 0.1 percent, respectively, which totals 5.9%
Online game development
So, now to the point. To demonstrate the work of the web socket server on php, I wanted to write a simple game. For a start, I needed to decide which one. Perhaps the only requirement for it was this:
all players must be on the same card and be able to interact with any other player
I’ve been googling for a long time on this topic until I came across
this page in the toaster , where
TravisBickle , the developer of phpdaemon, asks the community to suggest the idea of ​​a simple game that would demonstrate the work of web sockets. Despite the fact that some of the answers were quite interesting, this question is almost 3 years old ...
Of all the proposals, I chose “tanchiki”, but decided to make a simplified version of what was offered, and not a full-fledged game, so that the development process did not delay and the demo still saw the light, and did not remain in the halls of my mind.
Taking the chat code from the previous article, I added a little client part using:
canvas
and the context
: drawImage
object method for drawing the tank image, fillRect
for painting rectangles and fillText
for labels (I’ll say that I never worked with them before)addEventListener
for handling up, down, left, right, and space keys (as well as w, s, a, d)
On the server side, I slightly expanded the message handler from the client:
- each tank is an array consisting of coordinates, name and number of "lives"
- when I receive commands from the client “up”, “down” and so on, I recalculate the values ​​corresponding to the coordinates and send to the client all the arrays of tanks
- data exchange with the client using json
An example of a response from a server with three tanks[{"Name": "adsa", "x": 25, "y": 38, "dir": "right", "health": 0}, {"name": "qwe", "x": 20, “y”: 18, “dir”: “right”, “health”: 0}, {“name”: “sgfd4”, “x”: 5, “y”: 7, “dir”: “right "," Health ": 0}]
Thus, I realized the tanks moving on the screen.
Since I was counting somewhere for 50 - 500 simultaneous players, it became clear that all the tanks on one screen do not fit, so I limited the scope of the tank to the size of the usual Battle City field, and also added a minimap. Due to the fact that the original black background is not clear, whether the tank is moving, or everything else, I had to use the texture. If you can suggest the best texture option, please leave a link to it in the comments.
The next step was shooting. To do this, it is necessary not only to process messages from customers, but also to trigger on a timer to calculate the movement of the projectile (I decided that 10 times per second would be enough or every 100,000 microseconds, respectively). Let me remind you that I used the
stream_select(array &$read, array &$write, array &$except, int $tv_sec [, int $tv_usec = 0])
function
stream_select(array &$read, array &$write, array &$except, int $tv_sec [, int $tv_usec = 0])
, which accepts arrays of sockets necessary for processing and it works either with changes in them, or on a timeout. It was decided to use the timeout feature of this function to implement timers, but, unfortunately, what happened was written in the documentation.
Using the stream_select function with a timeout is a bad idea. If you do decide, we recommend using timeouts of at least 200.000 microseconds.
With my timeout of 100,000 microseconds, the CPU usage was 100%.
In this regard, I decided that even if my tanks would not shoot, they still need to interact. So I began to handle their collisions :)
It was not clear which tank of the two points points for a head-on collision, as well as difficulties with a blow to the side. For this reason, I refused these two ambiguous types of contact and left only one remaining option - one tank rams the other behind.
On this, it seems, it was possible to stop - the goal of “interaction” was achieved, but we wanted more.
After spending some more time, I implemented timers using libevent (now my library works with both sream_select and libevent): $this->base = event_base_new(); $this->event = event_new(); event_set($this->event, $this->_server, EV_READ | EV_PERSIST, array($this, 'accept'), $this->base); event_base_set($this->event, $this->base); event_add($this->event); $timer = event_timer_new(); event_timer_set($timer, array($this, '_onTimer'), $timer); event_base_set($timer, $this->base); event_timer_add($timer, 100000); event_base_loop($this->base);
Smart people write that event_timer is essentially a buffer that has a timeout, and I decided to look for whether it is possible to do something similar to stream_select, but, alas, to
no avail . If you know how to do this, please write in the comments.
Now I managed to get around this problem like this: $pair = stream_socket_pair(STREAM_PF_UNIX, STREAM_SOCK_STREAM, STREAM_IPPROTO_IP);
As a result, the processor load is about 0%.
Now nothing prevented me from adding the possibility of shooting, but, at the request of my friends, I decided to leave the possibility of "clashes."
Thanks
I would like to thank all those who paid my attention to the shortcomings in my code in the first two publications:
Skpd ,
pavlick ,
mayorovp ,
truezemez ,
Fesor ,
sovok_kpss ,
spein ,
seriyPSThank you very much and, of course, +1 in karma.
Thanks to you, you managed to achieve stability for the resulting library and a deeper understanding for me.
Demo and source code
Technical details:
- at the same time, the tank can have only one launched projectile, so try to hit the enemy and not shoot through the entire map
- while your projectile is flying, you can ram other tanks in the back
- your tank is yellow, opponents are green
- all tanks are on the same large map, navigate by the mini-map, which is located in the upper right corner
- the card automatically increases depending on the number of players, but does not decrease back
- obstacles, eagle, forest, pomegranate, etc. not implemented
Demo game (using stream_select)Demo game (using libevent)The source code of the library and examples is on github and is available under the MIT license.