Open Source Gaming Low Code, or No Code - Open Source Game Development GDevelop and Superpowers I'm always trying to encourage my nieces and nephews to get into programming.  I think Apple really hit the nail on the head with their Swift Playground app that teaches kids how the basics of development works, and gives them some really great logic pussles. There are also things like Karel (it's been out there for a long time).  One of our nieces is always asking to play Roblox or TocaBoca, and I asked one day casually if she was winning.  She gave me a puzzled look and meakly answered, "No." I asked why not?  She told me you don't rally win.  So my next question, of course, was, "...then why play?"   So these "games" are jut about running around and doing stuff.... This is what what we used to call "going out to play".  My friends and I would leave our homes in the morning, and be gone essentially all day, never giving our parents the slightest hint of where we were going, or what we were doing (and now when we tell them, they are glad of it).  We'd play till dark, just roaming, exploring, getting into trouble here and there, and come home for a meal if we just had to. I guess that's what these "games" are supposed to emulate...and sure, these days I don't think it's as safe for kids to go play like we did, but I feel like games should have a point. I tell you all this, to tell you there are some terrific game development engines out there, but while i know the basics of programming, I'm not a game developer by any stretch of the imagination.  I'm not an artist, and i'm sure not an animator. This is where GDevelop and Superpowers come in.  These are just two of many low-code / no-code game development engines available out there today.  Many of you may have heard of, or even used Game Maker . There was another one, also open source, that I found, Platinum Arts Sandbox but it was very outdated, sadly, adn I couldn't get it to run, but it really had a lot of potential for just making some really fun games on levels. Anyway, I've now found GDevelop and Superpowers which both have some really great promise. Both of these will run on Windows, MacOS, or Linux (so excellent!).   Installing GDevelop You can download the AppImage of GDevelop for linux, and then just give the downloaded AppImage file execute permissions. You can do this in the User Interface of Linux by right-clicking the app, then selecting Properties, move to the Permisssions tab, and make sure the 'Allow execting as a program' box is checked.  Alternatively, you can open a command line, move to the folder where you downloaded the appimage file and use a command like: chmod +x gdevelop.appimage Now, you can simply double click the icon to open the development IDE for GDevelop.   Installing Superpowers For Superpowers, you'll download the .zip file for Linux.  You can extract the zip file by right-clicking it, and selecting "Extract Here" from the context menu you see. This will give you a folder with all of the Superpowers files inside it. You should see a file named "Superpowers" with no extension. Again, right-click it, select "Properties" from the context menu, then navigate to the "Permissions" tab and make sure the "Allow executing as a program" is checked. Now, just double click the icon and it will open up the Superpowers IDE. What's the Differense? it's important to realize what you are getting into with these applications.  If you are a complete noob (newbie, new person) to programming and development, adn you really don't know any or most of the basics of programming, then you'll likely do better with GDevelop.  I've taken it for a spin and built a simple Platformer type game (similar to Super Mario Brothers).  it took me about 2 hours, simply following their online tutorial, and it is really quite amazing!  I had to write 0 (zero) code in order to make the entire game! If however, you're a person with a bit more coding experience, and you'd like to use some of that knowledge, then I think Superpowers might do just fine for you.  You'll want to know some programming if you're going to take on the Superpowers IDE, as it's not as heavy on the click, drag, drop type development of GDevelop. Tutorials Both GDevelop and Superpowers have some great beginner through intermediate tutorials. Both have them accessible right through the IDE (basically leading you to YouTube channels).   GDevelop also has a host of community provided tutorials you can watch and learn from. The GDevelop Youtube Channel Superpowers Youtube Channel Conclusion Regardless of how you get into it, making games can often be much more fun that just playing the games.  I hope you'll be inspired to get out there and give these really great, open source, development options a try. If you do create a fun game, please let me know over at https://discuss.opensourceisawesome.com, because I would love to test my skills! Support my Content and Channel Thank you to all of my patrons over at Patreon. I truly appreciate your support of my content more than I can express!   If you'd like to support my Channel and ongoing efforts through Patreon: https://www.patreon.com/bePatron?u=234177 Get Puzzles An open source, self hosted puzzle game built from your photos. Install Get Puzzles Setup a Server Install Updates Update and upgrade your server's packages using the following commands: - For Ubuntu/Debian: sudo apt update && sudo apt upgrade -y - For RedHat/CentOS/Fedora/Alma/Rocky: sudo dnf update -y Create a Non-Root User Create a non-root user with superuser (sudo) privileges: 1. Add a new user using adduser 2. Set the password for this user. 3. Enter the relevant information (optional) 4. Enter 'Y', then press Enter. 5. Add the user to the "sudo" group: - For Ubuntu/Debian: usermod -aG sudo - For RedHat/CentOS/Fedora/Alma/Rocky: usermod -aG wheel Ubuntu / Debian usermod -aG sudo RedHat / CentOS / Fedora / Alma / Rocky usermod -aG wheel Now, you can log out of the system, and log back in as your new non-root super user. Install Docker and Docker Compose Install Docker and Docker Compose on your server: 1. Install the curl utility: - For Ubuntu/Debian: sudo apt install curl -y - For RedHat/CentOS/Fedora/Alma/Rocky: sudo dnf install curl -y 2. Run the command to install Docker and Docker Compose: curl https://get.docker.com | sh Add Your User to the Docker Group Add your non-root user to the docker group so you can use Docker commands without sudo: sudo usermod -aG docker Now log out and back into your system, and you are ready to setup your application. Install Get Puzzles First, let’s setup our folder structure. If you’ve already done this, continue on, but if not, this can help you run multiple applications in Docker (Podman) on a single device. mkdir -p docker/get_puzzles Now we’ll move into the directory we just made: cd docker/get_puzzles We need to create two files. A compose file for our application configuration, and an environment file for our applications environment variables. First, let’s create our compose file, which we’ll call “compose.yaml” nano compose.yaml In the editor that opens, copy and paste the content below: services: # ── PostgreSQL ────────────────────────────────────────────────────────────── postgres: image: postgres:16-alpine container_name: puzzle-postgres environment: POSTGRES_USER: ${DB_USER:-puzzle_user} POSTGRES_PASSWORD: ${DB_PASSWORD:-puzzle_password} POSTGRES_DB: ${DB_NAME:-puzzle_db} volumes: - ./postgres_data:/var/lib/postgresql/data - ./init.sql:/docker-entrypoint-initdb.d/init.sql:ro healthcheck: test: ["CMD-SHELL", "pg_isready -U ${DB_USER:-puzzle_user} -d ${DB_NAME:-puzzle_db}"] interval: 5s timeout: 5s retries: 5 restart: unless-stopped networks: - puzzle-net # ── MinIO (S3-compatible object storage) ──────────────────────────────────── minio: image: minio/minio:latest container_name: puzzle-minio environment: MINIO_ROOT_USER: ${MINIO_ACCESS_KEY:-minio_admin} MINIO_ROOT_PASSWORD: ${MINIO_SECRET_KEY:-minio_password} volumes: - ./minio_data:/data command: server /data --console-address ":9001" healthcheck: test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"] interval: 5s timeout: 5s retries: 5 restart: unless-stopped networks: - puzzle-net # ── ClamAV (virus scanner) ───────────────────────────────────────────────── clamav: image: clamav/clamav:latest container_name: puzzle-clamav volumes: - ./clamav_data:/var/lib/clamav healthcheck: test: ["CMD-SHELL", "/usr/local/bin/clamdcheck.sh || exit 1"] interval: 10s timeout: 10s retries: 10 start_period: 120s restart: unless-stopped networks: - puzzle-net # ── Application (combined frontend + backend) ─────────────────────────────── app: image: bmcgonag/get-puzzles:latest container_name: puzzle-app environment: NODE_ENV: production # ── Database ── DB_HOST: postgres DB_PORT: 5432 DB_USER: ${DB_USER:-puzzle_user} DB_PASSWORD: ${DB_PASSWORD:-puzzle_password} DB_NAME: ${DB_NAME:-puzzle_db} # ── MinIO ── MINIO_ENDPOINT: minio MINIO_PORT: 9000 MINIO_USE_SSL: "false" MINIO_ACCESS_KEY: ${MINIO_ACCESS_KEY:-minio_admin} MINIO_SECRET_KEY: ${MINIO_SECRET_KEY:-minio_password} MINIO_BUCKET: ${MINIO_BUCKET:-puzzle-uploads} # ── ClamAV ── CLAMAV_HOST: clamav CLAMAV_PORT: 3310 # ── Frontend / CORS ── FRONTEND_URL: ${FRONTEND_URL:-http://localhost} NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL:-http://localhost/api} PORT: "3000" BACKEND_PORT: "3001" ports: - "${APP_PORT:-8080}:80" depends_on: postgres: condition: service_healthy minio: condition: service_healthy clamav: condition: service_healthy restart: unless-stopped networks: - puzzle-net networks: puzzle-net: driver: bridge Looking through the configuration, you’ll see multiple services being setup. We have Postgres, the database where we store all the relevant puzzle information, users, etc. Next, we have Minio, an object storage system made for storing the images your puzzles are based on. Then we have ClamAV. This is to scan the images being uploaded to make sure there is nothing malicious in them in an attempt to protect your server. Finally, we have App, this is the actual Get Puzzles application, which utilizes the other services to give your the working app. Save the file with CTRL + O, then press Enter to confirm, and exit the nano editor with CTRL + X. Next, we need to create the environment variable file. All those values in the configuration above that have “${SOME_CAPITAL_LETTERS}” format, are variables that allow you to setup your system in a way which is secure, and allows you to run the system with your own domain or sub-domain. Let’s create that file, called “.env”. The dot (.) in front of the filename means it’s hidden, and you need to use special flags to see it when listing the files in the directory. nano .env Now, copy and paste the following into that file: # POSTGRES DATA DB_USER: puzzle DB_PASSWORD:puzzle_password DB_NAME:-puzzle_db # MINIO DATA MINIO_ACCESS_KEY: minio_admin MINIO_SECRET_KEY: minio_password MINIO_BUCKET: puzzle-uploads # APP FRONTEND_URL: https://localhost NEXT_PUBLIC_API_URL: http://localhost/api APP_PORT: 8080 In this file, you’ll want to change the values (particularly for passwords and secrets) to long strong passwords and secrets in order to keep your system safe. You’ll want to replace localhost with an IP address for your server, or the domain / subdomain you’ll be using to access your server (e.g. puzzle.yourdomain.net). Save this file with CTRL + O, then press Enter to confirm, and exit the editor with CTRL + X. Setup the initial SQL script needed. In the same directory we will add a file called ‘init.sql’. This file will have the script needed to get the system setup initially. You only need it for the first time the app runs. You can do one of the following: ##### Directly download the file using: wget https://forge.routemehome.org/brian/get_puzzles/raw/branch/main/init.sql Then skip down to the Pull Image section. Or, create the file with: nano init.sql then copy and paste the following script code into that file: -- Users table CREATE TABLE IF NOT EXISTS users ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), email VARCHAR(255) UNIQUE NOT NULL, username VARCHAR(100) UNIQUE NOT NULL, password_hash VARCHAR(255), role VARCHAR(20) NOT NULL DEFAULT 'player' CHECK (role IN ('player', 'admin')), email_verified BOOLEAN NOT NULL DEFAULT FALSE, totp_secret VARCHAR(255), encryption_salt VARCHAR(255), ip_address VARCHAR(45), last_active_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), status VARCHAR(20) NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'banned')), created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); -- Sessions table for Lucia Auth CREATE TABLE IF NOT EXISTS sessions ( id VARCHAR(255) PRIMARY KEY, user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, expires_at TIMESTAMP WITH TIME ZONE NOT NULL ); -- Uploaded photos table CREATE TABLE IF NOT EXISTS uploaded_photos ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, file_path VARCHAR(500), encrypted_file_path VARCHAR(500), encrypted_dek TEXT, iv VARCHAR(255), salt VARCHAR(255), original_filename VARCHAR(255), mime_type VARCHAR(100), file_size INTEGER, width INTEGER, height INTEGER, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), deleted_at TIMESTAMP WITH TIME ZONE ); -- Puzzles table CREATE TABLE IF NOT EXISTS puzzles ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), uploaded_photo_id UUID REFERENCES uploaded_photos(id) ON DELETE SET NULL, piece_count INTEGER NOT NULL, cut_style VARCHAR(50) NOT NULL DEFAULT 'classic' CHECK (cut_style IN ('classic', 'geometric', 'spiral')), has_rotation BOOLEAN NOT NULL DEFAULT FALSE, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); -- Puzzle progress table CREATE TABLE IF NOT EXISTS puzzle_progress ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, puzzle_id UUID REFERENCES puzzles(id) ON DELETE SET NULL, status VARCHAR(50) NOT NULL DEFAULT 'in_progress' CHECK (status IN ('in_progress', 'solved')), state_json JSONB, hints_used INTEGER NOT NULL DEFAULT 0, started_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), completed_at TIMESTAMP WITH TIME ZONE ); -- Wall items table for gallery CREATE TABLE IF NOT EXISTS wall_items ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, uploaded_photo_id UUID REFERENCES uploaded_photos(id) ON DELETE SET NULL, puzzle_id UUID REFERENCES puzzles(id) ON DELETE SET NULL, x_pos FLOAT NOT NULL DEFAULT 0, y_pos FLOAT NOT NULL DEFAULT 0, target_width FLOAT NOT NULL DEFAULT 200, target_height FLOAT NOT NULL DEFAULT 200, z_index INTEGER NOT NULL DEFAULT 0, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); -- Audit logs table CREATE TABLE IF NOT EXISTS audit_logs ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID REFERENCES users(id) ON DELETE SET NULL, action VARCHAR(100) NOT NULL, entity_type VARCHAR(50), entity_id VARCHAR(255), details JSONB, ip_address VARCHAR(45), created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); -- Admin settings table CREATE TABLE IF NOT EXISTS admin_settings ( key VARCHAR(100) PRIMARY KEY, value TEXT NOT NULL, updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); -- SMTP settings table CREATE TABLE IF NOT EXISTS smtp_settings ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), host VARCHAR(255) NOT NULL, port INTEGER NOT NULL, username VARCHAR(255) NOT NULL, password_encrypted TEXT NOT NULL, encryption_type VARCHAR(10) NOT NULL DEFAULT 'tls' CHECK (encryption_type IN ('tls', 'ssl', 'none')), from_email VARCHAR(255) NOT NULL, from_name VARCHAR(255), is_active BOOLEAN NOT NULL DEFAULT FALSE, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); -- IP blocks table CREATE TABLE IF NOT EXISTS ip_blocks ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), ip_address VARCHAR(45) NOT NULL UNIQUE, reason TEXT, blocked_by UUID REFERENCES users(id) ON DELETE SET NULL, expires_at TIMESTAMP WITH TIME ZONE, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); -- Password reset tokens table CREATE TABLE IF NOT EXISTS password_reset_tokens ( token VARCHAR(255) PRIMARY KEY, user_id UUID NOT NULL UNIQUE REFERENCES users(id) ON DELETE CASCADE, expires_at TIMESTAMP WITH TIME ZONE NOT NULL, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); -- Social sharing tables CREATE TABLE IF NOT EXISTS shared_puzzles ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, puzzle_id UUID NOT NULL REFERENCES puzzles(id) ON DELETE CASCADE, token VARCHAR(64) UNIQUE NOT NULL, allow_gallery_view BOOLEAN DEFAULT FALSE, view_count INTEGER DEFAULT 0, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), revoked_at TIMESTAMP WITH TIME ZONE ); CREATE TABLE IF NOT EXISTS shared_galleries ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, token VARCHAR(64) UNIQUE NOT NULL, view_count INTEGER DEFAULT 0, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), revoked_at TIMESTAMP WITH TIME ZONE ); -- Indexes for performance CREATE INDEX IF NOT EXISTS idx_users_email ON users(email); CREATE INDEX IF NOT EXISTS idx_users_username ON users(username); CREATE INDEX IF NOT EXISTS idx_uploaded_photos_user_id ON uploaded_photos(user_id); CREATE INDEX IF NOT EXISTS idx_puzzles_photo_id ON puzzles(uploaded_photo_id); CREATE INDEX IF NOT EXISTS idx_puzzle_progress_user_id ON puzzle_progress(user_id); CREATE INDEX IF NOT EXISTS idx_puzzle_progress_puzzle_id ON puzzle_progress(puzzle_id); CREATE INDEX IF NOT EXISTS idx_wall_items_user_id ON wall_items(user_id); CREATE INDEX IF NOT EXISTS idx_audit_logs_user_id ON audit_logs(user_id); CREATE INDEX IF NOT EXISTS idx_audit_logs_created_at ON audit_logs(created_at); CREATE INDEX IF NOT EXISTS idx_shared_puzzles_token ON shared_puzzles(token); CREATE INDEX IF NOT EXISTS idx_shared_puzzles_user_id ON shared_puzzles(user_id); CREATE INDEX IF NOT EXISTS idx_shared_puzzles_puzzle_id ON shared_puzzles(puzzle_id); CREATE INDEX IF NOT EXISTS idx_shared_galleries_token ON shared_galleries(token); CREATE INDEX IF NOT EXISTS idx_shared_galleries_user_id ON shared_galleries(user_id); Save the file with CTRL + O, then press Enter to confirm. Now exit the nano editor with CTRL + X. Now we are ready to start pulling images, and getting the application started. Pull the Images Now we’ll pull the images needed to run our application. docker compose pull Start the Application Containers Once those images have pulled, we’ll start our application containers with: docker compose up -d You can view the logs with: docker compose logs -f If you prefer, you can concatenate the commands to just start the containers and follow the logs as the system starts: docker compose up -d && docker compose logs -f Once the logs settle, you’ll want to navigate to the IP address of your application server with the port you set in the .env file. If you set port 8020, and your application is running on a machine with IP 192.168.1.21, then you’d go to http://192.168.1.21:8020 in your browser. Additionally, if you’re setting up a domain / sub-domain, then you’ll want to navigate to that name, and ensure your web server (NGinX or Apache) config is working. Finally, if you are setting up a Reverse Proxy (e.g. Pangolin, Netbird, Tailscale, NGinX Proxy Manager, etc), thne you’ll want to make sure you use the proper IP and port to access your system.