Latest Posts (14 found)
NULL on error 2 weeks ago

As gambiarras mais impiedosas que já fiz: parte 2

Dando continuidade à parte 1 . Antes da gambiarra da parte 1, eu trabalhava, em meu segundo emprego, com mídia indoor. Para quem não sabe, mídia indoor são aquelas televisões informativas instaladas em elevadores, supermercados e outros locais com média ou grande circulação de pessoas, que também exibem propagandas de vez em quando. Quando cheguei lá, eles já tinham um sistema que funcionava muito bem. Era um player de vídeo que usava o MPlayer ou, às vezes, uma distribuição Linux chamada MoviX . O único problema era o acesso remoto e o gerenciamento das playlists, que às vezes era feito por SSH ou exigia que alguém fosse até o local. Porém, além do gerenciamento remoto, que seria relativamente fácil de resolver, havia outro problema: A banda de internet. Naquela época, não existiam CDNs ou, caso existissem, custavam uma nota preta e eram de difícil acesso. A Amazon Web Services até existia, pois surgiu em 2006, e estamos falando de aproximadamente 2008, mas ainda era desconhecida, cara e não tinha infraestrutura no Brasil. Essa ideia de cadastrar um cartão em um serviço cujo valor oscilava estava completamente fora da realidade da minha bolha naquela época. Talvez uma VPS servisse, mas, de qualquer forma, esse não é o ponto. Nós tínhamos alguns “servidores”, também conhecidos como computadores pessoais antigos, lentos e que já não estavam sendo utilizados, além de uma conexão de 1 megabit compartilhada com todo o escritório. Naquela época, eu já era um grande fã da Valve, da Blizzard e de outras empresas que, por baixo dos panos, usavam BitTorrent para distribuir seus jogos. No instalador da Blizzard, inclusive, era possível ver detalhes como a quantidade de seeders e leechers durante o download, além da velocidade da transferência. Então, munido desse conhecimento, decidi criar um player de vídeo em C++ com uma biblioteca de torrent embutida. O player fazia polling constantemente, pois WebSocket ainda não existia. Ao receber uma playlist, ele calculava a diferença para descobrir quais arquivos precisavam ser baixados e quais deveriam ser excluídos do disco. Depois de identificar as mídias necessárias, iniciava os downloads usando torrent. Para deixar as coisas ainda mais interessantes, eu usava um HTTP seed , por meio do qual alguns dos primeiros pedaços eram baixados por HTTP, de forma aleatória. Na época, tínhamos algumas dezenas de máquinas. Como todas compartilhavam os arquivos entre si, a carga sobre a conexão do escritório diminuía bastante, enquanto os downloads se tornavam muito mais rápidos. Seria possível até saturar a conexão dos locais onde as máquinas estavam instaladas, mas, por precaução, o próprio cliente torrent limitava o uso da banda. Bem, talvez isso não seja exatamente uma gambiarra , mas foi um feito memorável da minha carreira. Espero que gostem e compartilhem as gambiarras de vocês nos comentários.

0 views
NULL on error 2 weeks ago

As gambiarras mais impiedosas que já fiz: parte 1

Gambiarra, de modo geral, é conhecida como uma espécie de quebra-galho ou atalho para conseguir algo usando a criatividade, muitas vezes de forma pejorativa. Ressalto que, neste texto, a ideia é justamente enfatizar a criatividade diante de grandes desafios. Certa vez, enquanto trabalhava com aplicativos mobile e livros digitais, fui encarregado de renderizar um livro em um aplicativo Android nativo. React Native ainda não existia. Talvez o Sencha Touch já existisse, mas, de qualquer forma, os tablets-alvo eram muito lentos e tinham pouquíssimos recursos. Pois bem, o que você faria nessa situação? Fácil, não é? Renderizaria as páginas do PDF como imagens JPEG e criaria um aplicativo para paginá-las, a.k.a. um livro digital. Não, claro que não! Seu gerente gosta de desafiar você e não gostou nem da qualidade das imagens nem do espaço ocupado em disco. Então, ele propôs usar Adobe Flash. Sim, naquela época ele ainda existia, embora já estivesse no fim da vida. Mas como? Ninguém sabia. Foi então que tive a ideia de criar uma WebView de um pixel por um pixel e embutir o Adobe Flash nela para renderizar as páginas sob demanda e com a máxima qualidade. As páginas viriam de um arquivo , pois o software usado para criar os livros exportava a arte vetorial para SWF. Nos PDFs, por algum motivo, tudo era convertido em bitmap. Mas surgiu outro problema: como transferir dados entre o aplicativo Android, escrito em Java, a WebView, em JavaScript, e o Adobe Flash? Criei uma WebView de um pixel por um pixel com o Flash Player rodando dentro dela. Na época, essa era a única maneira de executar Flash no Android, pois o Adobe AIR ainda não existia. Fazendo o Java chamar , eu conseguia executar uma função JavaScript e passar o bitmap renderizado da página solicitada. Sim, o bitmap inteiro era transmitido como uma string. Por sorte, a WebView não tinha uma limitação de 4.096 caracteres, ou algo semelhante às limitações comuns em servidores HTTP. Portanto, era possível transmitir páginas muito grandes e em alta resolução. Feito isso, bastava repassar os dados para o Flash. Mas como? Exemplo simplificado: No lado do Flash, em ActionScript: Então, no lado da WebView, em JavaScript: Feio? É, eu sei. Mas funcionou perfeitamente. Passamos a ter páginas com alto DPI, zoom, rotação e tudo mais. Como bônus, ficou bem rápido, pois o aparelho precisava renderizar cada página apenas uma vez. Isso acontecia em menos de meio segundo. Depois, bastava desenhar a imagem na tela. :-)

0 views
NULL on error 3 weeks ago

Nostalgia

“A nostalgia é um sentimento permanente. Em outras palavras, sentimos nostalgia mesmo vivendo em nosso próprio país, ao lado das pessoas que mais amamos. Apesar de um lar feliz e de uma família feliz, um homem pode sofrer de nostalgia simplesmente porque sente que sua alma está sendo limitada, que ela não se desenvolve da maneira como deseja. A nostalgia é o sentimento de impotência diante do mundo, a dor de não conseguir transmitir sua espiritualidade aos outros.” Andrei Tarkóvski Se você chegou aqui pelo feed RSS, deixe um comentário. Quero ver se ainda há assinantes. :-) Esse vai ser um post diferente. Antigamente eu gostava de compartilhar snippets de código. Hoje, com as LLMs, acho que faz mais sentido perguntar para elas do que correr o risco de dar um copy-paste de um Zé Ninguém da internet. Não sei se é a meia-idade batendo, mas sinto muita saudade da internet dos anos 2000. Tínhamos salas de IRC sempre cheias, zines, e era praticamente obrigatório todo site ter um feed RSS. Até o Google fazer o favor de matar o Google Reader, que, na minha opinião, era a melhor rede social que eles já criaram. Sim, eu considero o Reader uma rede social. Lembro de chegar ao trabalho e a primeira coisa que ia fazer era abrir o Reader para ver os comentários dos meus amigos em notícias, tirinhas, blogs e artigos. As tirinhas também viviam uma época de ouro. Irmãos Brain, Nerdson, Nebulosa’s Nerd Bar e tantas outras faziam parte da rotina de muita gente. O YouTube também era diferente. Quase não existia clickbait. Quem dedicava tempo para gravar e publicar um vídeo normalmente fazia isso porque gostava do assunto, não porque queria transformar aquilo em sua principal fonte de renda ou em uma renda extra. Talvez seja só nostalgia. Ou talvez a internet realmente tenha mudado mais do que a gente percebe. Fóruns de internet também eram muito populares. Eu frequentava bastante o unidev.com.br e o pdj.com.br. Fiz muitos amigos por lá, amizades que mantenho até hoje, mesmo depois de tantas décadas. Vou citar apenas os nicks da época: tiozorak , tiotoddy , vertexportus , crocidb , vinigodoy , n2liquid , killguns , alext , lord eternal , zizaco , billguedes entre outros. Se esqueci de alguém, me perdoe! E eu, o skhaz ! Acho que, por ser millennial e a maioria dos meus amigos também ser, éramos muito engajados. Gostávamos de dar pitaco em tudo, trocar ideias e fofocar. Sem contar o ócio. Não havia séries para maratonar, nem muito conteúdo no YouTube, etc. :-) Além de participar de fóruns, eu também tentei manter um blog. Fiz alguns amigos na blogosfera e, recentemente, “restaurei” o skhaz.com em skhaz.github.io/skhaz.com . E por falar em restauração e velharias, também restaurei algumas zines publicadas entre ~2000 e ~2010. Você pode conferi-las em: skhaz.github.io/zines . E os e-mails? Ah, os e-mails… Que saudade de receber um e-mail escrito por um humano. Eu os usava para tudo: enviar arquivos, organizar eventos, trocar ideias ou simplesmente participar de grupos, como o Google Groups. Foi lá que também fiz vários amigos, participei de eventos e aprendi muito. E não sou apenas eu que estou falando isso. Coincidentemente, o Caloni, que, além de ser um amigo das baixarias, foi uma das pessoas que me influenciaram a pôr a cara a tapa na internet, publicou algo semelhante. Blogue do Caloni: Breve nota sobre o futuro do blogue Os próximos que eu arriscar escrever caminharão menos em construção e passagem de conhecimento e mais em relatos de experiência. É isso. De besta para besta.

0 views
NULL on error 6 months ago

Hiding information inside images

As a child, there was a game called secret message which consisted of writing something on a sheet of paper with lemon juice and handing it to a friend who knew that by holding a candle underneath, a message would be revealed — without any of the intermediaries ever knowing. We can say that this game was a form of steganography “Steganography (from the Greek ‘hidden writing’) is the study and use of techniques to conceal the existence of a message within another, a form of security through obscurity. In other words, steganography is the particular branch of cryptology that consists of disguising one piece of writing within another in order to mask its true meaning.” - Wikipedia Today I’m going to introduce another type of steganography — one that uses images to hide text inside pixels! An image is composed of pixels, and each pixel represents the smallest possible color point using N bits. The more bits per color, the more colors a pixel can represent. In my example I will focus only on 32-bit images ( RGBA or ARGB ) where 8 bits are used per color component. Obviously the technique is easily adaptable to other formats. What happens if I change a single bit of the red component of every pixel? Even with the original image side by side, we would be unable to notice the difference — only a hashing algorithm like SHA1 could tell the files apart. Therefore, I can use this same trick of altering pixels to hide a message inside the image, and only someone who knows the implementation will know how to extract the message. But to do this we need to separate each bit of the message and change only one or two bits of each color component. It sounds like very little space, but if we do the math, an 800×600 image — which is considered low resolution today — can hold 800 width _ 600 height = 480000 pixels 480000 pixels _ 4 components = 1920000 bits 1920000 bits / 8 = 240000 bytes In other words, we can fit a message of up to ~30 KB in an 800×600 image :) Remember, we are talking about pixels, not file size — that depends on the chosen format. And speaking of format, this only works with lossless formats. To achieve our goal we need to master the ancient art of bit twiddling . There is an excellent resource on the subject called: Bit Twiddling Hacks Writing a hidden message: Reading a hidden message from an image: The file size changed slightly due to the format used — in this case, PNG. This technique can be used to hide not only text, but also other images, sounds, or any kind of data. The source code can be found in this gist

0 views
NULL on error 11 months ago

Carimbo now have a better stack trace and Sentry integration

As I’ve already said countless times, I’m working on my first game for Steam, which you can check out online at reprobate.site . Since it’s a paid game, I need to provide proper bug support. With that in mind, and based on both professional and personal experience, I decided to use Sentry . At first, integrating C++ and Lua should have been straightforward, but I ran into some issues when using the Conan package manager. Initially, the package wasn’t being included in the compiler’s include flags, which led me to open an issue both on Conan Center and on Sentry Native . After spending a whole day on this, I eventually found out that the fix was actually pretty simple. Now Carimbo has native support for Sentry, both on the web (WebAssembly) and natively (Android, iOS, Linux, Windows, and macOS). Here’s how I managed to get it working. This was certainly my biggest problem. For some reason, even when following Conan’s documentation, I couldn’t get it to include the header path for Sentry Native. In the end, my solution looked like this: The native part was pretty easy, but for the web part I had an insight while walking, because I realized I could inject JavaScript using the Emscripten API. Since the game assets are stored in a compressed file, PhysicsFS provides an API to handle them transparently. It’s great for distributing the game — you only need the cartridge.zip and the executable — and it works even better on the web. The engine must provide a searcher so that Lua can find the game’s other Lua scripts. For this, I use a custom searcher: it first looks for the scripts inside the game package, and if they’re not found, it falls back to the interpreter’s default search to load them from the standard library. To improve stack traces, the secret lies in the second parameter of lua.load. You can pass a string starting with followed by the file name. This alone gives you a much richer stack trace. In Carimbo, I have a terminate hook that catches any exception and atexit hooks to always handle cleanup. This way, I can provide Sentry support in a practically universal and abstract manner for the engine’s user. You can find more details about the engine and its implementation in the official repository: github.com/willtobyte/carimbo .

0 views
NULL on error 1 years ago

Replacing Lua’s math.random module with the Xorshift algorithm

I recently discovered that V8 , the engine behind Node.js and Chrome , has been using since 2014 . Out of curiosity, I checked what the Lua VM uses, and to my surprise, it relies on the standard C library, which is extremely slow. You can find more details here: v8/src/numbers/math-random.cc at main · v8/v8 I’m currently working on a game that uses random in the main loop, which is quite slow. So to work around that, I implemented a pseudo-random function. This is a Linear Congruential Generator (LCG) — one of the oldest and simplest methods for generating pseudorandom numbers. For the noise effect, I end up calling random up to 6 times per loop, which is extremely costly, and not even the C++ implementation of below could save me. Anyway, I ended up replacing Lua’s implementation with the one below, because optimizations are always welcome — especially when the gain in randomness is significantly higher.

0 views
NULL on error 1 years ago

Poor’s Man Shaders

Spoiler: it’s not shaders I’m waiting for a universal solution that the SDL developers are working on — cross-platform, multi-API shaders, the SDL_shadercross . The idea is that you write shaders in a single language, and at runtime, they get compiled for the target GPU. Unfortunately, it’s a large and complex project, and it will take time before it becomes stable. In the meantime, in my Carimbo engine, I was wondering if I could implement something similar to shaders — something that would allow Lua code to write arbitrary pixels into a buffer and stream that buffer into a texture. So I created what I call a canvas, which is basically a texture the same size as the screen, rendered after certain elements. The set_pixel function receives a pointer to a uint32_t buffer that exactly matches the texture size. This pointer is actually a Lua string, which I found to be the most performant way to transfer data between Lua and C++ without relying on preallocated buffers. On Lua side: Some effects I’ve created so far: https://youtu.be/GUWTWRQuzxw https://youtu.be/usJ9QM7V8BI https://youtu.be/DUhQmL91cNA

0 views
NULL on error 1 years ago

AI will replace programmers—just not yet, because it still generates very extremely inefficient code.

I was working on my engine, which includes a sort of canvas where Lua code can generate chunks of pixels and send them in batches for the C++ engine to render. This worked very well and smoothly at 60 frames per second with no frame drops at low resolutions (240p, which is the screen size of my games). However, when I happened to try 1080p, the frame rate dropped. Since I was in a rush and a bit lazy—because I can’t afford to spend too much time on personal projects—I decided to use AI to optimize it, and this was the best solution I could squeeze out. It went from 40 FPS down to 17, much worse than the initial implementation! Naturally, the code was not just complex, but also way slower. That’s when I decided to take my brain off the shelf and came up with this solution: Kabum! Smooth 60 frames per second, even at 8K resolution or higher.

0 views
NULL on error 1 years ago

How to avoid dynamic linking of Steam’s client library using a very old trick

As you know, this blog is more focused on sharing code snippets than on teaching, so today I’m going to show you something I recently discovered. If you’ve been following me, you know I’ve been working in my free time on a 2D game engine where creators can build games using only Lua — and I’d say, even fairly complex ones. Right now, I’m working on a point-and-click game that you can play here: https://bereprobate.com/. It’s built using this same engine, and I’m publishing builds in parallel to Steam and the Web using GitHub Actions. The thing is, Steam — which is the main target platform for this game — supports achievements, and I want to include them. But to use achievements, you have to link the Steam library to your engine. The problem is, doing that creates a dependency on that library in the binaries, which I don’t want. I also don’t want to maintain a separate build just for that. Then I thought: “Why not load the Steam library dynamically? Use LoadLibraryA on Windows and dlopen on macOS. (Sorry Linux — it’s Proton-only for now.)” I tried the experiment below, and it worked. If the DLL/dylib is present, the Steam features work just fine. If not, everything runs normally. Achivement class

0 views
NULL on error 1 years ago

I’ve built an IoT device to let my family know when I’m in a meeting

Do you work from home, and do people in your household always show up at the worst possible moments? Let me introduce the “I’m in Meeting” IoT device: it lights up at your office door whenever you turn on your webcam. It consists of an ESP32 with connected to Wi-Fi, using the Arduino framework for simplicity. The ESP32 exposes an HTTP server that handles a PATCH request on the /camera endpoint. This endpoint receives a JSON payload with a status of “on” or “off”, and turns the LED panel red or blue accordingly. For those who don’t know, mDNS (or Bonjour on Apple platforms) is a way to assign an IP address to a .local hostname for the device, so I don’t need to figure out its IP manually—just use the local domain. Super convenient, right? On the other side, I have a Python daemon that periodically queries Apple’s API to check if any cameras are in use, and then sends a PATCH request with “on” or “off” to http://esp32.local/camera. Very simple, but quite useful. See in action here https://youtu.be/c-cD_JLuCuQ Source code https://github.com/skhaz/onair

0 views
NULL on error 1 years ago

Building and Publishing Games to Steam Directly from GitHub Actions

I have been using GitHub Actions extensively both at work and in personal projects, as shown in this post What I’ve been automating with GitHub Actions, an automated life . In my free time, I’m working on a 2D hide-and-seek game, and as you might expect, I’ve automated the entire release pipeline for publishing on Steam. After a few attempts, when it finally worked, it felt like magic: all I had to do was create a new tag, and within minutes, the Steam client was downloading the update. As I mentioned earlier, I have a 2D engine that, while simple, is quite comprehensive. With each new tag, I compile it in parallel for Windows, macOS, Linux, and WebAssembly. Once compilation is complete, I create a release and publish it on GitHub. Releases · willtobyte/carimbo Publishing on Steam is quite simple. First, you need a developer account with the correct documentation and fees paid. After that, you’ll need to generate some secret keys as follows: If you don’t have the steamcmd application installed, you’ll need to install it using: Copy the contents of the authentication file: Note: You must have MFA enabled. After logging in, run the command below and copy the output into a GitHub Action variable named . Also, create the variables with your username and with your game’s ID. Additionally, the Action downloads the latest Carimbo release for Windows only (sorry Linux and macOS users, my time is limited). Ideally, I should pin the runtime version (the Carimbo version) using something like a runtime.txt file. Maybe I’ll implement this in the future, but for now, everything runs on the bleeding edge. :-) Boom! If everything is correct, your game should appear in your Steam client under the list of owned games.

0 views
NULL on error 1 years ago

My First Game with Carimbo, My Homemade Engine, For my Son

https://youtu.be/nVRzCstyspQ After a while, I decided to resume work on my game engine . I made a game for my son. I could have used an existing engine, but I chose to write everything from scratch because code is like pasta—it’s much better and more enjoyable when homemade. This actually reminds me of when my father used to build my toys, from kites to wooden slides. Good memories. I have decided to do the same using what I know: programming. You can watch it here or play it here , (runs in the browser thanks to WebAssembly), use and for moving around and to shoot. The engine was written in C++17, and the games are in Lua. The engine exposes some primitives to the Lua VM, which in turn coordinates the entire game. Game source code and engine source code . Artwork by Aline Cardoso @yuugenpixie . The entire game is scripted in Lua . First, we create the engine itself. Then we point to some resources for asset prefetching; they are loaded lazily to avoid impacting the game loop . We created the postal service, which is something I borrowed from languages like Erlang , where an entity can send messages to any other. As we’ll see below, the bullet sends a hit message when it collides with the octopus, and the octopus, upon receiving the hit, decrements its own health. We also obtain the SoundManager to play sounds and spawn the main entities. It’s not a triple-A title, but I’m very careful with resource management. A widely known technique is to create an object pool that you can reuse. In the code below, we limit it to a maximum of 3 bullets present on the screen. The method is a callback called each loop in the engine. In the case of the bullet, I check if it has approached the green octopus. If so, I send a message to it indicating that it received a hit. On the octopus’s side, when it receives a “hit” message, the entity triggers an explosion animation, switches to attack mode, and decreases its health by 1. If it reaches 0, it changes the animation to “dead”. And finally, the player’s , where I apply the velocity if any movement keys are pressed, or set the animation to idle if none are pressed. We also have the projectile firing, where the fire function is called, taking an instance of a bullet from the pool, placing it in a semi-random position, and applying velocity. In the end, he requested some minor tweaks, such as the projectile being the princess sprite, the target being the player, and the entity that shoots being the green octopus. 🤷‍♂️

0 views
NULL on error 2 years ago

How I saved a few dozen dollars by migrating my personal projects from the cloud to a Raspberry Pi

Since the first URL shorteners emerged, that’s always drawn me in for some reason, perhaps because I figured out on my own that they encoded the primary key in base36 (maybe). So around ~2010, I decided to make my own. It was called “encurta.ae” (make it short there in Portuguese), and it was based on AppEngine. Even back then, I was quite fond of serverless and such. It worked great locally, but when I deployed it, the datastore IDs were too large, causing the URLs to be too long. Fast-forward to nowadays, I’ve decided to bring this project to life again. This time with a twist: when shortening the URL, the shortener would take a screenshot of the website and embed Open Graph tags in the redirect URL. This way, when shared on a social network, the link would display a title, description, and thumbnail that accurately represent what the user is about to open. Typically, I use serverless for my personal projects because they scale to zero, thus eliminating costs when not in use. However, after working with serverless for many years, I’ve been wanting to experiment with a more grounded and down-to-earth approach to development and deployment. Deployment is done using Docker Compose, via SSH using the DOCKER_HOST environment variable pointing directly to a Raspberry Pi that I had bought and never used before. Now it saves me $5 per month, and I can keep a limited number of projects running on it. And then you might ask: How do you expose it to the internet? I use Cloudflare Tunnel ; the setup is simple and creates a direct connection between the Raspberry Pi and the nearest Point of Presence . This type of hosting is extremely advantageous because the server’s IP and/or ports are never revealed; it stays behind my firewall. Everything goes through Cloudflare. I have more than one Docker Compose file, and that’s what’s coolest. Locally, I run one, for deployment I instruct the Compose to read two others for logging and tunneling. Then for deploying Example of the “worker queue” It’s noticeable that it took a bit more effort than if I had used a task queue. However, it turned out to be quite robust and easy to debug, and for that reason alone, it was worth it. In sharing the shortened link ( https://takealook.pro/4MP ) for my company Ultratech Software on social networks, you can have a really nice preview, as seen below. Take A Look I opted to use Go , with several goroutines performing tasks in parallel, and a purely written work queue mechanism. Playwright & Chromium for automation and screenshot. SQLite for the database (it’s simple) Backblaze for storage BunnyCDN for cotent delivery Papertrail for logging

0 views
NULL on error 2 years ago

My 2024 Setup

As can be seen, I am using Kagi, which delivers excellent results without any garbage or ads, instead of Google. I also use email with my own domain through Apple’s iCloud email system, which allows for custom domains, as well as calendar and other features, all outside Google’s domain. Unfortunately, I have to maintain my Google account to use YouTube and occasionally for sites that do not allow creating accounts with a username and password, but only through Single Sign-On (SSO). brew as package manager asdf as language & tools management Zed as code editor Bear as notes keeping Firefox as browser Kagi as search engine ChatGPT as AI assistant Bruno as API client OrbStack as Docker desktop replacement, plus virtual machines Apple ecosystem for mail, calendar, reminders, music, messaging, and more.

0 views