September 13, Contour Day was celebrated in Contour. In the largest development office, they played Pac-Man and tried to eat 280 pizza boxes . At the same time, fifteen hundred people were drawing pixels online. In this post, four developers tell how the holiday was done.
The day of the programmer is celebrated by the entire company, not just the developers. Therefore, an idea was needed for an online game in which everyone can participate. I remembered that in April, Reddit Place was a social experiment on collective drawing on a canvas of 1000 Ă— 1000 pixels, in which a million people participated.
I decided that I had to make my Place, with a timelapse and an API.
On Reddit, a million people painted on a canvas the size of one megapixel. Everyone could paint no more than one pixel every 5–20 minutes. If you make a festive canvas of 256 × 256 pixels (15 times less) and consider that we have not a million employees (but 200 times less), then the delay between the pixels should also be about 10 times less.
Therefore, for our field of 256 Ă— 256 pixels, I chose a delay from 2:56 to 0:32. And after that he talked about the idea to colleagues who agreed to help.
I immediately realized that the front would need a canvas, a palette and a zoom. But the designers (Vladimir dzekh and Yuliya krasilnikovayu ) turned out to be more cunning and came up with another rewind, statistics, leaderboard and screenshots.
By the way, at first there were fewer colors in the palette, but then the guys added brown in order not to limit anyone's creative impulses.
In the meantime, as a modern fender, I began to reflexively think about setting up a Webpack, Babel and Autoprefixer. And when I woke up, I learned that the backend developer had already done everything. And it even worked. Crooked, askew, but it worked: dots on the canvas were set, zoom was zooming. I sawed off from the beautiful design all unnecessary and beautifully laid out.
There are two problems: Edge and Safari.
In Safari, everything really slowed down with terrible force. First I discovered that canvas is not rendered into a separate composite layer. Therefore, the browser redraws the entire document every time the canvas is updated. Added transition: translateZ(0)
canvas, and everything began to slow down faster. Then refactored the rest of the Bakder code, got rid of a dozen redraws. The interface flew on the first spacecraft.
I didn’t care about IE right away, because I knew that players would use normal browsers. The trouble came from an older brother. If you ask Edge to draw a square, he flatly refuses. Says: "But smooth transitions are better!" - and blurs the entire picture.
The same problem was the guys from Reddit. At first I solved it using the image-rendering
CSS property and the CanvasRenderingContext2D.imageSmoothingEnabled
flag. But before the launch, it turned out that Edge was messing up with the server through web sockets. Therefore, I declared him an abnormal browser.
I am proud that I tried to bring React, Webpack, Babel, LESS and Autoprefixer to code three times, but I was able to defeat myself. As a result, everything is written on pure ES6 + and CSS, but with trendy grids, web sockets and fetch.
I did not want to write everything from scratch, so I looked for something ready. The original Place is on Github , but there is too much code. I took a simple clone for NodeJS and walked a file through it. That is why, when Veronica took over, the interface was already working somehow. In general, there are lots of clones , choose for yourself any.
In the code of the selected clone, we had to fix vulnerabilities and add the missing ones: a timer, an expanded palette, rewind online, collecting statistics, drawing via web sockets instead of REST requests, logging through the Passport (our internal authenticator).
The architecture was as follows: the user put a pixel in the browser, the browser sent a message through a web socket to the server, the server sent a message about changing the canvas to the queue (Apache Kafka). Then the servers took data from the queue and sent it to all clients. Above is the original scheme from the author of the clone, in which clients still communicate with the server using REST requests.
We were planning to rewind, so I decided to cache the canvas snapshots with versions. At startup, the server should have taken the last snapshot from the cache, rather than building it from scratch. The same snapshot was received by the client when connected to the server, which caused the loading of the canvas to be almost instantaneous. It was possible to send a client a snapshot of any version and thus organize rewind. The new snapshot was saved after changing every hundred pixels.
About a day after the start of the game an incident happened. I fixed the bug and restarted the server. And users saw that some of the drawn points were gone.
The fact was that when connecting to the server, the client requested a specific version of the canvas. Therefore, after the start, the server took from the cache not the last snapshot, but some old version that was requested by one of the clients. Users did not see the fresh pixels and began to draw in a new way, and the server continued to add data to the queue.
When I realized what was happening, I cleared the cache. The server reloaded the data from the queue and generated the current version of the canvas. It turned out to be a funny effect: new user patterns were superimposed on previous ones, since everything lost was returned and new changes were added. The fix was fast, so nothing messed up:
In general, it is not for nothing that they say that invalidation of caches is one of the two most difficult tasks in computer science.
It's funny that I, while repairing the cache, accidentally turned off authentication. There was also a kolhaker colleague who filled out a few thousand pixels with a script in a couple of minutes. I rolled out a fix, but the green bar remained:
I also wanted after the end of the game to make a good time-lapse and count the statistics. For this, I decided to start a database and save the coordinates, color, date and time, as well as the user ID for each point drawn in it.
The server was under NodeJS, so I chose LokiJS . This database was praised for its simplicity and speed of work, because all data is stored in memory and automatically written to disk at specified intervals. For my task fit.
I set up saving once every 1 minute. Tested locally, including under load - everything worked like a clock. And on the battlefield, something paranormal was happening. The data was not saved to the disk according to a schedule, but of its own accord. For example, for several hours they were not saved even once. For three days I did not find the reasons for this behavior. As a result, a lot of statistics were lost during server restarts.
However, I was ready for this from the very beginning, because timelaps were very necessary. Every minute I saved the canvas as an image to a file. It turned out a few gigabytes of pictures, but a video with a time-lapse was recorded and voiced by a couple of commands:
$ ffmpeg -pattern_type glob \ -i "*.png" \ -c:v libx264 \ -vf format=yuv420p \ timelapse.mp4 $ ffmpeg -i timelapse.mp4 \ -i sci-fi.mp3 \ 256.mp4
After the start of the game, everyone quickly realized that one was not a warrior in the field. Self-organization began in Staff, our internal social network:
I also participated in this:
But even with the team it was not interesting to draw a large-scale image. I figured it out after the first seven pixels of the rainbow. Not even pleased that colleagues have done a bunch of useful tools:
I expected more from the Programmer’s Day. And I waited - on the second day, Igor published the following code snippet in Staffa and began distributing API keys to those who wanted to:
It was already something!
To warm up, I wrote a bot who drew a given picture. It was not very fun. Then he came up with the idea to create a picture algorithmically. The result was a bot that bent a perfectly round rainbow. But it was also boring.
I realized that a non-boring bot should not just draw pixels, but interact with the outside world and other people's creativity. But it was necessary to avoid vandalism, because the bot is a force, and responsibility must come with force.
You could draw a clock with the current time. Or a moving picture that crawls across a canvas and overwrites other people's drawings ... in order to restore them later. And then I came up with a plot that combined these two ideas.
The clock became a countdown timer, the moving picture - a rocket taking off. In addition, a rocket is very convenient to draw - first you lengthen the upper part by a pixel, then you shorten the lower part by a pixel. This not only looks good, but also saves pixels, because the delay in drawing through the API has not been canceled.
This was supposed to be the slowest flight of a rocket in the history of mankind. With the current delay for a couple of hours, I could move the rocket just a few pixels. It was necessary either to reduce the rocket, or to move it in jumps, or to accept the fact that it would fly for a day. He shared the agony of choice with Igor, and he with the words “Do good!” Suddenly dumped nearly 50 keys for the API. With so many keys, a rocket could reach a speed of one pixel per second!
It remains a little: choose the design of the rocket and write all the code. I dropped the cartoon rockets and chose the Vostok carrier rocket. It immediately became clear that the rocket’s flight should end with the launch of the Vostok-1 spacecraft into orbit.
Why "East"? Because right now a lot of engineers from Kontur are working on a secret project with the code name Vostok . I wanted the guys to be nice.
I set up the bot, started the countdown timer, called the viewers through Staff. The rocket took off. And then I realized how ridiculous the rocket looks in space with unseparated accelerating blocks and the first stage. Miraculously I found 10 free minutes to add a branch to the stage and restart the bot. So it was not only the slowest flight of a rocket in the history of mankind, but also the first flight of a rocket, in the middle of which its structure was changed.
It was nice to see how colleagues erased a copy of the rocket, because of the bug left on the launch pad. They draw a one-pixel man in the window of the rocket. Rework the word "go" in the "ponafeh". In general, it was pleasing that everyone behaved culturally, despite the absence of rules. Even when the place on the canvas is over:
By the way, without NSFW-content has not done. Someone from the word TRON painted by my first boring bot stubbornly made the word PRON.
Vanya later said that on September 13, 1,630 people and a dozen bots, that is, about a third of all company employees, were simultaneously drawing on canvas. On average, 440 clients were connected to the servers, and during the daytime hours - 840.
As a result, we got this picture:
And such a time-lapse. My rocket takes off at 27 seconds:
Do you program on holidays and for holidays? Tell us in the comments.
PS If you are interested in what we do not tell on Habré, subscribe to our channel in Telegram .
Source: https://habr.com/ru/post/338716/
All Articles