From 7dbd759ce1546acedafbad23f668267615f2ee59 Mon Sep 17 00:00:00 2001 From: David Luevano Alvarado Date: Sun, 20 Aug 2023 04:30:55 -0600 Subject: add yourls usage --- .../blog/a/jellyfin_server_with_sonarr_radarr.html | 10 +- live/blog/a/mail_server_with_postfix.html | 2 +- live/blog/a/pastebin_alt_with_privatebin.html | 22 ++-- live/blog/a/torrenting_with_qbittorrent.html | 2 +- live/blog/a/website_with_nginx.html | 4 +- live/blog/g/flappybird_godot_devlog_1.html | 56 +++++------ live/blog/g/gogodot_jam3_devlog_1.html | 18 ++-- live/blog/rss.xml | 112 +++++++++++---------- 8 files changed, 123 insertions(+), 103 deletions(-) (limited to 'live/blog') diff --git a/live/blog/a/jellyfin_server_with_sonarr_radarr.html b/live/blog/a/jellyfin_server_with_sonarr_radarr.html index 3bdcb53..44f0d0a 100644 --- a/live/blog/a/jellyfin_server_with_sonarr_radarr.html +++ b/live/blog/a/jellyfin_server_with_sonarr_radarr.html @@ -334,7 +334,7 @@ systemctl start sonarr.service
Profiles

This is a bit different than with Radarr, the way it is configured is by setting “Release profiles”. I took the profiles from TRaSH: WEB-DL Release profile regex. The only possible change I’ll do is disable the Low Quality Groups and/or the “Golden rule” filter (for x265 encoded video).

For me it ended up looking like this:

-
+
Sonarr: Release profiles
Sonarr: Release profiles
@@ -379,12 +379,12 @@ systemctl start sonarr.service

Once you click on “Add X” it will add it to the Series section and will start as monitored. So far I haven’t noticed that it immediately starts downloading (because of the “Start search for missing episodes” setting) but I always click on unmonitor the series, so I can manually check (again, due to the low quality of my indexers).

When it automatically starts to download an episode/season it will send it to qBitTorrent and you can monitor it over there. Else you can also monitor at Activity -> Queue. Same thing goes if you download manually each episode/season via the interactive search.

To interactively search episodes/seasons go to Series and then click on any series, then click either on the interactive search button for the episode or the season, it is an icon of a person as shown below:

-
+
Sonarr: Interactive search button
Sonarr: Interactive search button

Then it will bring a window with the search results, where it shows the indexer it got the result from, the size of the torrent, peers, language, quality, the score it received from the configured release profiles an alert in case that the torrent is “bad” and the download button to manually download the torrent you want. An example shown below:

-
+
Sonarr: Interactive search results
Sonarr: Interactive search results
@@ -479,12 +479,12 @@ systemctl start jellyfin.service

The initial setup wizard makes you create an user (will be the admin for now) and at least one library, though these can be done later. For more check Jellyfin: Quick start.

Remember to use the configured directory as mentioned in Directory structure. Any other configuration (like adding users or libraries) can be done at the dashboard: click on the 3 horizontal lines on the top left of the Web UI then navigate to Administration -> Dashboard. I didn’t configure much other than adding a couple of users for me and friends, I wouldn’t recommend using the admin account to watch (personal preference).

Once there is at least one library it will show at Home along with the latest movies (if any) similar to the following (don’t judge, these are just the latest I added due to friend’s requests):

-
+
Jellyfin: Home libraries
Jellyfin: Home libraries

And inside the “Movies” library you can see the whole catalog where you can filter or just scroll as well as seeing Suggestions (I think this starts getting populated after a while) and Genres:

-
+
Jellyfin: Library catalog options
Jellyfin: Library catalog options
diff --git a/live/blog/a/mail_server_with_postfix.html b/live/blog/a/mail_server_with_postfix.html index 0540673..defe607 100644 --- a/live/blog/a/mail_server_with_postfix.html +++ b/live/blog/a/mail_server_with_postfix.html @@ -453,7 +453,7 @@ systemctl enable spamassassin.service
  • Password: your user password (as in the password you use to login to the server with that user)
  • All that’s left to do is test your mail server for spoofing, and to see if everything is setup correctly. Go to DKIM Test and follow the instructions (basically click next, and send an email with whatever content to the email that they provide). After you send the email, you should see something like:

    -
    +
    DKIM Test successful
    DKIM Test successful
    diff --git a/live/blog/a/pastebin_alt_with_privatebin.html b/live/blog/a/pastebin_alt_with_privatebin.html index a511a41..85f05cf 100644 --- a/live/blog/a/pastebin_alt_with_privatebin.html +++ b/live/blog/a/pastebin_alt_with_privatebin.html @@ -110,6 +110,7 @@
  • SSL certificate
  • +
  • Usage
  • PrivateBin
      @@ -218,20 +219,20 @@ extension=mysqli

      Create a new user and database as described in MariaDB: Create users/databases.

      Configuration

      -

      The default configuration file is self explanatory, it is located at /etc/webapps/yourls/config.php.

      -

      Set the user/database YOURLS will use and either create a cookie or get one from URL provided. It is important to change the $yours_user_passwords variable, YOURLS will hash the passwords on login so it is not stored in plaintext. Password hashing can be disabled with:

      +

      The default configuration file is self explanatory, it is located at /etc/webapps/yourls/config.php. Make sure to correctly set the user/database YOURLS will use and either create a cookie or get one from URL provided.

      +

      It is important to change the $yours_user_passwords variable, YOURLS will hash the passwords on login so it is not stored in plaintext. Password hashing can be disabled with:

      define( 'YOURLS_NO_HASH_PASSWORD', true );
       

      I also changed the “shortening method” to 62 to include more characters:

      define( 'YOURLS_URL_CONVERT', 62 );
       
      -

      Lastly, the $yourls_reserved_URL variable will need more blacklisted words depending on the use-case. YOURLS_SITE needs to match whatever is set in nginx.

      +

      The $yourls_reserved_URL variable will need more blacklisted words depending on the use-case. Make sure the YOURLS_PRIVATE variable is set to true (default) if the service will be exposed to the public.

      Nginx

      Create a yourls.conf at the usual sites-<available/enabled> path for nginx:

      server {
           listen 80;
           root /usr/share/webapps/yourls/;
      -    server_name short.yourdomain.com;
      +    server_name short.example.com;
           index index.php;
       
           location / {
      @@ -251,6 +252,15 @@ extension=mysqli
       

      Restart the nginx service for changes to take effect:

      systemctl restart nginx.service
       
      +

      Usage

      +

      The admin area is located at https://short.example.com/admin/, login with any of the configured users set with the $yours_user_passwords in the config. Activate plugins by going to the “Manage Plugins” page (located at the top left) and clicking in the respective “Activate” button by hovering the “Actin” column, as shown below:

      +
      +YOURLS: Activate plugin +
      YOURLS: Activate plugin
      +
      +

      I personally activated the “Random ShortURLs” and “Allow Hyphens in Short URLs”. Once the “Random ShortURLs” plugin is activated it can be configured by going to the “Random ShortURLs Settings” page (located at the top left, right below “Manage Plugins”), only config available is “Random Keyword Length”.

      +

      The main admin area can be used to manually shorten any link provided, by using the automatic shortening or by providing a custom short URL.

      +

      Finally, the “Tools” page (located at the top left) conains the signature token, used for YOURLS: Passwordless API as well as useful bookmarklets for URL shortening while browsing.

      PrivateBin

      PrivateBin is a minimalist self-hosted alternative to pastebin.

      Install from the AUR with yay:

      @@ -300,7 +310,7 @@ opt[12] = true ; PDO::ATTR_PERSISTENT
      server {
           listen 80;
           root //usr/share/webapps/privatebin/;
      -    server_name bin.yourdomain.com;
      +    server_name bin.example.com;
           index index.php;
       
           if ($pastebin_badagent) {
      @@ -344,7 +354,7 @@ opt[12] = true    ; PDO::ATTR_PERSISTENT
         

      And that’s it, at this point you can already look at the default initial page of Nginx if you enter the IP of your server in a web browser. You should see something like this:

      -
      +
      Nginx welcome page
      Nginx welcome page
      @@ -187,7 +187,7 @@ cd sites-available systemctl restart nginx

      If everything goes correctly, you can now go to your website by typing domain.name on a web browser. But you will see a “404 Not Found” page like the following (maybe with different Nginx version):

      -
      +
      Nginx 404 Not Found page
      Nginx 404 Not Found page
      diff --git a/live/blog/g/flappybird_godot_devlog_1.html b/live/blog/g/flappybird_godot_devlog_1.html index bcfdf6e..616d75c 100644 --- a/live/blog/g/flappybird_godot_devlog_1.html +++ b/live/blog/g/flappybird_godot_devlog_1.html @@ -163,35 +163,35 @@

      Config

      Default import settings

      Since this is just pixel art, the importing settings for textures needs to be adjusted so the sprites don’t look blurry. Go to Project -> Project settings… -> Import defaults and on the drop down select Texture, untick everything and make sure Compress/Mode is set to Lossless.

      -
      +
      Project settings - Import defaults - Texture settings
      Project settings - Import defaults - Texture settings

      General settings

      It’s also a good idea to setup some config variables project-wide. To do so, go to Project -> Project settings… -> General, select Application/config and add a new property (there is a text box at the top of the project settings window) for game scale: application/config/game_scale for the type use float and then click on add; configure the new property to 3.0; On the same window, also add application/config/version as a string, and make it 1.0.0 (or whatever number you want).

      -
      +
      Project settings - General - Game scale and version properties
      Project settings - General - Game scale and version properties

      For my personal preferences, also disable some of the GDScript debug warnings that are annoying, this is done at Project -> Project settings… -> General, select Debug/GDScript and toggle off Unused arguments, Unused signal and Return value discarded, and any other that might come up too often and don’t want to see.

      -
      +
      Project settings - General - GDScript debug warnings
      Project settings - General - GDScript debug warnings

      Finally, set the initial window size in Project -> Project settings… -> General, select Display/Window and set Size/Width and Size/Height to 600 and 800, respectively. As well as the Stretch/Mode to viewport , and Stretch/Aspect to keep:

      -
      +
      Project settings - General - Initial window size
      Project settings - General - Initial window size

      Keybindings

      I only used 3 actions (keybindings): jump, restart and toggle_debug (optional). To add custom keybindings (so that the Input.something() API can be used), go to Project -> Project settings… -> Input Map and on the text box write jump and click add, then it will be added to the list and it’s just a matter of clicking the + sign to add a Physical key, press any key you want to be used to jump and click ok. Do the same for the rest of the actions.

      -
      +
      Project settings - Input Map - Adding necessary keybindings
      Project settings - Input Map - Adding necessary keybindings

      Layers

      Finally, rename the physics layers so we don’t lose track of which layer is which. Go to Project -> Layer Names -> 2d Physics and change the first 5 layer names to (in order): player, ground, pipe, ceiling and score.

      -
      +
      Project settings - Layer Names - 2D Physics
      Project settings - Layer Names - 2D Physics
      @@ -200,12 +200,12 @@

      Importing

      Create the necessary directories to hold the respective assets and it’s just a matter of dragging and dropping, I used directories: res://entities/actors/player/sprites/, res://fonts/, res://levels/world/background/sprites/, res://levels/world/ground/sprites/, res://levels/world/pipe/sprites/, res://sfx/. For the player sprites, the FileSystem window looks like this (entities/actor directories are really not necessary):

      -
      +
      FileSystem - Player sprite imports
      FileSystem - Player sprite imports

      It should look similar for other directories, except maybe for the file extensions. For example, for the sfx:

      -
      +
      FileSystem - SFX imports
      FileSystem - SFX imports
      @@ -213,72 +213,72 @@

      Now it’s time to actually create the game, by creating the basic scenes that will make up the game. The hardest part and the most confusing is going to be the TileMaps, so that goes first.

      TileMaps

      I’m using a scene called WorldTiles with a Node2D node as root called the same. With 2 different TileMap nodes as children named GroundTileMap and PipeTileMap (these are their own scene); yes 2 different TileMaps because we need 2 different physics colliders (in Godot 4.0 you can have a single TileMap with different physics colliders in it). Each node has its own script. It should look something like this:

      -
      +
      Scene - WorldTiles (TileMaps)
      Scene - WorldTiles (TileMaps)

      I used the following directory structure:

      -
      +
      Scene - WorldTiles - Directory structure
      Scene - WorldTiles - Directory structure

      To configure the GroundTileMap, select the node and click on (empty) on the TileMap/Tile set property and then click on New TileSet, then click where the (empty) used to be, a new window should open on the bottom:

      -
      +
      TileSet - Configuration window
      TileSet - Configuration window

      Click on the plus on the bottom left and you can now select the specific tile set to use. Now click on the yellow + New Single Tile, activate the grid and select any of the tiles. Should look like this:

      -
      +
      TileSet - New single tile
      TileSet - New single tile

      We need to do this because for some reason we can’t change the snap options before selecting a tile. After selecting a random tile, set up the Snap Options/Step (in the Inspector) and set it to 16x16 (or if using a different tile set, to it’s tile size):

      -
      +
      TileSet - Tile - Step snap options
      TileSet - Tile - Step snap options

      Now you can select the actual single tile. Once selected click on Collision, use the rectangle tool and draw the rectangle corresponding to that tile’s collision:

      -
      +
      TileSet - Tile - Selection and collision
      TileSet - Tile - Selection and collision

      Do the same for the other 3 tiles. If you select the TileMap itself again, it should look like this on the right (on default layout it’s on the left of the Inspector):

      -
      +
      TileSet - Available tiles
      TileSet - Available tiles

      The ordering is important only for the “underground tile”, which is the filler ground, it should be at the end (index 3); if this is not the case, repeat the process (it’s possible to rearrange them but it’s hard to explain as it’s pretty weird).

      At this point the tilemap doesn’t have any physics and the cell size is wrong. Select the GroundTileMap, set the TileMap/Cell/Size to 16x16, the TileMap/Collision/Layer set to bit 2 only (ground layer) and disable any TileMap/Collision/Mask bits. Should look something like this:

      -
      +
      TileMap - Cell size and collision configuration
      TileMap - Cell size and collision configuration

      Now it’s just a matter of repeating the same for the pipes (PipeTileMap), only difference is that when selecting the tiles you need to select 2 tiles, as the pipe is 2 tiles wide, or just set the Snap Options/Step to 32x16, for example, just keep the cell size to 16x16.

      Default ground tiles

      I added few default ground tiles to the scene, just for testing purposes but I left them there. These could be place programatically, but I was too lazy to change things. On the WorldTiles scene, while selecting the GroundTileMap, you can select the tiles you want to paint with, and left click in the grid to paint with the selected tile. Need to place tiles from (-8, 7) to (10, 7) as well as the tile below with the filler ground (the tile position/coordinates show at the bottom left, refer to the image below):

      -
      +
      Scene - WorldTiles - Default ground tiles
      Scene - WorldTiles - Default ground tiles

      Player

      On a new scene called Player with a KinematicBody2D node named Player as the root of the scene, then for the children: AnimatedSprite as Sprite, CollisionShape2D as Collision (with a circle shape) and 3 AudioStreamPlayers for JumpSound, DeadSound and HitSound. Not sure if it’s a good practice to have the audio here, since I did that at the end, pretty lazy. Then, attach a script to the Player node and then it should look like this:

      -
      +
      Scene - Player - Node setup
      Scene - Player - Node setup

      Select the Player node and set the CollisionShape2D/Collision/Layer to 1 and the CollisionObject2D/Collision/Mask to 2 and 3 (ground and pipe).

      For the Sprite node, when selecting it click on the (empty) for the AnimatedSprite/Frames property and click New SpriteFrames, click again where the (empty) used to be and ane window should open on the bottom:

      -
      +
      Scene - Player - SpriteFrames window
      Scene - Player - SpriteFrames window

      Right off the bat, set the Speed to 10 FPS (bottom left) and rename default to bird_1. With the bird_1 selected, click on the Add frames from a Sprite Sheet, which is the second button under Animation Frames: which looks has an icon of a small grid (next to the folder icon), a new window will popup where you need to select the respective sprite sheet to use and configure it for importing. On the Select Frames window, change the Vertical to 1, and then select all 4 frames (Ctrl + Scroll wheel to zoom in):

      -
      +
      Scene - Player - Sprite sheet importer
      Scene - Player - Sprite sheet importer

      After that, the SpriteFrames window should look like this:

      -
      +
      Scene - Player - SpriteFrames window with sprite sheet configured
      Scene - Player - SpriteFrames window with sprite sheet configured
      @@ -297,12 +297,12 @@

    Game

    This is the actual Game scene that holds all the playable stuff, here we will drop in all the previous scenes; the root node is a Node2D and also has an attached script. Also need to add 2 additional AudioStreamPlayers for the “start” and “score” sounds, as well as a Sprite for the background (Sprite/Offset/Offset set to (0, 10)) and a Camera2D (Camera2D/Current set to true (checked)). It should look something like this:

    -
    +
    Scene - Game - Node setup
    Scene - Game - Node setup

    The scene viewport should look something like the following:

    -
    +
    Scene - Game - Viewport
    Scene - Game - Viewport
    @@ -310,12 +310,12 @@

    Fonts

    We need some font Resources to style the Label fonts. Under the FileSystem window, right click on the fonts directory (create one if needed) and click on New Resource... and select DynamicFontData, save it in the “fonts” directory as SilverDynamicFontData.tres (Silver as it is the font I’m using) then double click the just created resource and set the DynamicFontData/Font Path to the actual Silver.ttf font (or whatever you want).

    Then create a new resource and this time select DynamicFont, name it SilverDynamicFont.tres, then double click to edit and add the SilverDynamicFontData.tres to the DynamicFont/Font/Font Data property (and I personally toggled off the DynamicFont/Font/Antialiased property), now just set the DynamicFont/Settings/(Size, Outline Size, Outline Color) to 32, 1 and black, respectively (or any other values you want). It should look something like this:

    -
    +
    Resource - DynamicFont - Default font
    Resource - DynamicFont - Default font

    Do the same for another DynamicFont which will be used for the score label, named SilverScoreDynamicFont.tres. Only changes are Dynamic/Settings/(Size, Outline Size) which are set to 128 and 2, respectively. The final files for the fonts should look something like this:

    -
    +
    Resource - Dynamicfont - Directory structure
    Resource - Dynamicfont - Directory structure
    @@ -344,7 +344,7 @@
  • The scene ends up looking like this:

    -
    +
    Scene - UI - Node setup
    Scene - UI - Node setup
    @@ -478,7 +478,7 @@ func _remove_first_ground() -> void:

    Where you might notice that the _initial_new_tile_x is 11, instead of 10, refer to Default ground tiles where we placed tiles from -8 to 10, so the next empty one is 11. These _place_new_ground and _remove_first_ground functions are called upon receiving the signal.

    PipeTileMap

    This is really similar to the GroundTileMap code, instead of defining an enum for the ground tiles, we define it for the pipe patterns (because each pipe is composed of multiple pipe tiles). If your pipe tile set looks like this (notice the index):

    -
    +
    PipeTileMap - Tile set indexes
    PipeTileMap - Tile set indexes
    @@ -580,7 +580,7 @@ func get_high_score() -> int: save_data()

    Now, this script in particular will need to be a Singleton (AutoLoad), which means that there will be only one instance and will be available across all scripts. To do so, go to Project -> Project settings… -> AutoLoad and select this script in the Path: and add a Node Name: (I used SavedData, if you use something else, be careful while following this devlog) which will be the name we’ll use to access the singleton. Toggle on Enable if needed, it should look like this:

    -
    +
    Project settings - AutoLoad - SavedData singleton
    Project settings - AutoLoad - SavedData singleton
    diff --git a/live/blog/g/gogodot_jam3_devlog_1.html b/live/blog/g/gogodot_jam3_devlog_1.html index baf14d0..f73bb27 100644 --- a/live/blog/g/gogodot_jam3_devlog_1.html +++ b/live/blog/g/gogodot_jam3_devlog_1.html @@ -141,7 +141,7 @@

    Other than that I used few key sprites from vryell: Controller & Keyboard Icons and a font from datagoblin: Monogram.

    The snake

    This is the most challenging part in my opinion as making all the body parts follow the head in a user defined path it’s kinda hard. I tried with like 4-5 options and the one I’m detailing here is the only one that worked as I wanted for me. This time the directory structure I’m using is the following:

    -
    +
    FileSystem - Snake dir structure
    FileSystem - Snake dir structure
    @@ -175,7 +175,7 @@ func _rotate_to(direction: int) -> void: _direction = _direction.rotated(deg2rad(direction * Global.SNAKE_ROT_SPEED * get_physics_process_delta_time()))

    After tunning all the necessary parameters you should get something like this:

    -
    +
    Snake - Basic movement (left and right controls)
    Snake - Basic movement (left and right controls)
    @@ -212,7 +212,7 @@ func _on_Head_snake_path_new_point(coordinates: Vector2) -> void: update()

    With this, we’re now populating the Path2D curve points with the position of the snake head. You should be able to see it because of the _draw call. If you run it you should see something like this:

    -
    +
    Snake - Basic movement with path
    Snake - Basic movement with path
    @@ -256,7 +256,7 @@ func _on_Head_snake_path_new_point(coordinates: Vector2) -> void: _add_initial_segment(TAIL_SEGMENT_NP)

    Select the Snake node and add the Body and Tail scene to the parameters, respectively. Then when running you should see something like this:

    -
    +
    Snake - Basic movement with all body parts
    Snake - Basic movement with all body parts
    @@ -299,7 +299,7 @@ func _add_segment_to_queue() -> void: body_segment_queue.append(body_segment_queue.back() + Global.SNAKE_SEGMENT_SIZE)

    With everything implemented and connected accordingly then we can add segments on demand (for testing I’m adding with a key press), it should look like this:

    -
    +
    Snake - Basic movement with dynamic addition of new segments
    Snake - Basic movement with dynamic addition of new segments
    @@ -362,21 +362,21 @@ func _on_body_entered(body: Node) -> void: add_child(food)

    And this is used in _process to place new food whenever needed. For now I added a condition to add food until 10 pieces are in place, and keep adding whenever the food is is lower than 10. After setting everything up, this is the result:

    -
    +
    Snake - Food basic interaction
    Snake - Food basic interaction

    Za warudo! (The world)

    It just happend that I saw a video to create random maps by using a method called random walks, this video was made by NAD LABS: Nuclear Throne Like Map Generation In Godot. It’s a pretty simple but powerful script, he provided the source code from which I based my random walker, just tweaked a few things and added others. Some of the maps than can be generated with this method (already aded some random sprites):

    -
    +
    World map generator - Random map 1
    World map generator - Random map 1
    -
    +
    World map generator - Random map 2
    World map generator - Random map 2
    -
    +
    World map generator - Random map 3
    World map generator - Random map 3
    diff --git a/live/blog/rss.xml b/live/blog/rss.xml index 675de5f..e068ac4 100644 --- a/live/blog/rss.xml +++ b/live/blog/rss.xml @@ -56,6 +56,7 @@
  • SSL certificate
  • +
  • Usage
  • PrivateBin
      @@ -164,20 +165,20 @@ extension=mysqli

      Create a new user and database as described in MariaDB: Create users/databases.

      Configuration

      -

      The default configuration file is self explanatory, it is located at /etc/webapps/yourls/config.php.

      -

      Set the user/database YOURLS will use and either create a cookie or get one from URL provided. It is important to change the $yours_user_passwords variable, YOURLS will hash the passwords on login so it is not stored in plaintext. Password hashing can be disabled with:

      +

      The default configuration file is self explanatory, it is located at /etc/webapps/yourls/config.php. Make sure to correctly set the user/database YOURLS will use and either create a cookie or get one from URL provided.

      +

      It is important to change the $yours_user_passwords variable, YOURLS will hash the passwords on login so it is not stored in plaintext. Password hashing can be disabled with:

      define( 'YOURLS_NO_HASH_PASSWORD', true );
       

      I also changed the “shortening method” to 62 to include more characters:

      define( 'YOURLS_URL_CONVERT', 62 );
       
      -

      Lastly, the $yourls_reserved_URL variable will need more blacklisted words depending on the use-case. YOURLS_SITE needs to match whatever is set in nginx.

      +

      The $yourls_reserved_URL variable will need more blacklisted words depending on the use-case. Make sure the YOURLS_PRIVATE variable is set to true (default) if the service will be exposed to the public.

      Nginx

      Create a yourls.conf at the usual sites-<available/enabled> path for nginx:

      server {
           listen 80;
           root /usr/share/webapps/yourls/;
      -    server_name short.yourdomain.com;
      +    server_name short.example.com;
           index index.php;
       
           location / {
      @@ -197,6 +198,15 @@ extension=mysqli
       

      Restart the nginx service for changes to take effect:

      systemctl restart nginx.service
       
      +

      Usage

      +

      The admin area is located at https://short.example.com/admin/, login with any of the configured users set with the $yours_user_passwords in the config. Activate plugins by going to the “Manage Plugins” page (located at the top left) and clicking in the respective “Activate” button by hovering the “Actin” column, as shown below:

      +
      +YOURLS: Activate plugin +
      YOURLS: Activate plugin
      +
      +

      I personally activated the “Random ShortURLs” and “Allow Hyphens in Short URLs”. Once the “Random ShortURLs” plugin is activated it can be configured by going to the “Random ShortURLs Settings” page (located at the top left, right below “Manage Plugins”), only config available is “Random Keyword Length”.

      +

      The main admin area can be used to manually shorten any link provided, by using the automatic shortening or by providing a custom short URL.

      +

      Finally, the “Tools” page (located at the top left) conains the signature token, used for YOURLS: Passwordless API as well as useful bookmarklets for URL shortening while browsing.

      PrivateBin

      PrivateBin is a minimalist self-hosted alternative to pastebin.

      Install from the AUR with yay:

      @@ -246,7 +256,7 @@ opt[12] = true ; PDO::ATTR_PERSISTENT
      server {
           listen 80;
           root //usr/share/webapps/privatebin/;
      -    server_name bin.yourdomain.com;
      +    server_name bin.example.com;
           index index.php;
       
           if ($pastebin_badagent) {
      @@ -526,7 +536,7 @@ systemctl start sonarr.service
       
      Profiles

      This is a bit different than with Radarr, the way it is configured is by setting “Release profiles”. I took the profiles from TRaSH: WEB-DL Release profile regex. The only possible change I’ll do is disable the Low Quality Groups and/or the “Golden rule” filter (for x265 encoded video).

      For me it ended up looking like this:

      -
      +
      Sonarr: Release profiles
      Sonarr: Release profiles
      @@ -571,12 +581,12 @@ systemctl start sonarr.service

      Once you click on “Add X” it will add it to the Series section and will start as monitored. So far I haven’t noticed that it immediately starts downloading (because of the “Start search for missing episodes” setting) but I always click on unmonitor the series, so I can manually check (again, due to the low quality of my indexers).

      When it automatically starts to download an episode/season it will send it to qBitTorrent and you can monitor it over there. Else you can also monitor at Activity -> Queue. Same thing goes if you download manually each episode/season via the interactive search.

      To interactively search episodes/seasons go to Series and then click on any series, then click either on the interactive search button for the episode or the season, it is an icon of a person as shown below:

      -
      +
      Sonarr: Interactive search button
      Sonarr: Interactive search button

      Then it will bring a window with the search results, where it shows the indexer it got the result from, the size of the torrent, peers, language, quality, the score it received from the configured release profiles an alert in case that the torrent is “bad” and the download button to manually download the torrent you want. An example shown below:

      -
      +
      Sonarr: Interactive search results
      Sonarr: Interactive search results
      @@ -671,12 +681,12 @@ systemctl start jellyfin.service

      The initial setup wizard makes you create an user (will be the admin for now) and at least one library, though these can be done later. For more check Jellyfin: Quick start.

      Remember to use the configured directory as mentioned in Directory structure. Any other configuration (like adding users or libraries) can be done at the dashboard: click on the 3 horizontal lines on the top left of the Web UI then navigate to Administration -> Dashboard. I didn’t configure much other than adding a couple of users for me and friends, I wouldn’t recommend using the admin account to watch (personal preference).

      Once there is at least one library it will show at Home along with the latest movies (if any) similar to the following (don’t judge, these are just the latest I added due to friend’s requests):

      -
      +
      Jellyfin: Home libraries
      Jellyfin: Home libraries

      And inside the “Movies” library you can see the whole catalog where you can filter or just scroll as well as seeing Suggestions (I think this starts getting populated after a while) and Genres:

      -
      +
      Jellyfin: Library catalog options
      Jellyfin: Library catalog options
      @@ -982,7 +992,7 @@ systemctl start jackett.service

      Indexers

      For Jackett, an indexer is just a configured tracker for some of the commonly known torrent sites. Jackett comes with a lot of pre-configured public and private indexers which usually have multiple URLs (mirrors) per indexer, useful when the main torrent site is down. Some indexers come with extra features/configuration depending on what the site specializes on.

      To add an indexer click on the “+ Add Indexer” at the top of the Web UI and look for indexers you want, then click on the “+” icon on the far-most right for each indexer or select the ones you want (clicking on the checkbox on the far-most left of the indexer) and scroll all the way to the bottom to click on “Add Selected”. They then will show as a list with some available actions such as “Copy RSS Feed”, “Copy Torznab Feed”, “Copy Potato Feed”, a button to search, configure, delete and test the indexer, as shown below:

      -
      +
      Jacket: configured indexers
      Jackett: configured indexers
      @@ -1654,7 +1664,7 @@ default:other::r-x

      Other than that I used few key sprites from vryell: Controller & Keyboard Icons and a font from datagoblin: Monogram.

      The snake

      This is the most challenging part in my opinion as making all the body parts follow the head in a user defined path it’s kinda hard. I tried with like 4-5 options and the one I’m detailing here is the only one that worked as I wanted for me. This time the directory structure I’m using is the following:

      -
      +
      FileSystem - Snake dir structure
      FileSystem - Snake dir structure
      @@ -1688,7 +1698,7 @@ func _rotate_to(direction: int) -> void: _direction = _direction.rotated(deg2rad(direction * Global.SNAKE_ROT_SPEED * get_physics_process_delta_time()))

      After tunning all the necessary parameters you should get something like this:

      -
      +
      Snake - Basic movement (left and right controls)
      Snake - Basic movement (left and right controls)
      @@ -1725,7 +1735,7 @@ func _on_Head_snake_path_new_point(coordinates: Vector2) -> void: update()

      With this, we’re now populating the Path2D curve points with the position of the snake head. You should be able to see it because of the _draw call. If you run it you should see something like this:

      -
      +
      Snake - Basic movement with path
      Snake - Basic movement with path
      @@ -1769,7 +1779,7 @@ func _on_Head_snake_path_new_point(coordinates: Vector2) -> void: _add_initial_segment(TAIL_SEGMENT_NP)

      Select the Snake node and add the Body and Tail scene to the parameters, respectively. Then when running you should see something like this:

      -
      +
      Snake - Basic movement with all body parts
      Snake - Basic movement with all body parts
      @@ -1812,7 +1822,7 @@ func _add_segment_to_queue() -> void: body_segment_queue.append(body_segment_queue.back() + Global.SNAKE_SEGMENT_SIZE)

      With everything implemented and connected accordingly then we can add segments on demand (for testing I’m adding with a key press), it should look like this:

      -
      +
      Snake - Basic movement with dynamic addition of new segments
      Snake - Basic movement with dynamic addition of new segments
      @@ -1875,21 +1885,21 @@ func _on_body_entered(body: Node) -> void: add_child(food)

      And this is used in _process to place new food whenever needed. For now I added a condition to add food until 10 pieces are in place, and keep adding whenever the food is is lower than 10. After setting everything up, this is the result:

      -
      +
      Snake - Food basic interaction
      Snake - Food basic interaction

      Za warudo! (The world)

      It just happend that I saw a video to create random maps by using a method called random walks, this video was made by NAD LABS: Nuclear Throne Like Map Generation In Godot. It’s a pretty simple but powerful script, he provided the source code from which I based my random walker, just tweaked a few things and added others. Some of the maps than can be generated with this method (already aded some random sprites):

      -
      +
      World map generator - Random map 1
      World map generator - Random map 1
      -
      +
      World map generator - Random map 2
      World map generator - Random map 2
      -
      +
      World map generator - Random map 3
      World map generator - Random map 3
      @@ -2312,35 +2322,35 @@ func physics_process(delta: float) -> void:

      Config

      Default import settings

      Since this is just pixel art, the importing settings for textures needs to be adjusted so the sprites don’t look blurry. Go to Project -> Project settings… -> Import defaults and on the drop down select Texture, untick everything and make sure Compress/Mode is set to Lossless.

      -
      +
      Project settings - Import defaults - Texture settings
      Project settings - Import defaults - Texture settings

      General settings

      It’s also a good idea to setup some config variables project-wide. To do so, go to Project -> Project settings… -> General, select Application/config and add a new property (there is a text box at the top of the project settings window) for game scale: application/config/game_scale for the type use float and then click on add; configure the new property to 3.0; On the same window, also add application/config/version as a string, and make it 1.0.0 (or whatever number you want).

      -
      +
      Project settings - General - Game scale and version properties
      Project settings - General - Game scale and version properties

      For my personal preferences, also disable some of the GDScript debug warnings that are annoying, this is done at Project -> Project settings… -> General, select Debug/GDScript and toggle off Unused arguments, Unused signal and Return value discarded, and any other that might come up too often and don’t want to see.

      -
      +
      Project settings - General - GDScript debug warnings
      Project settings - General - GDScript debug warnings

      Finally, set the initial window size in Project -> Project settings… -> General, select Display/Window and set Size/Width and Size/Height to 600 and 800, respectively. As well as the Stretch/Mode to viewport , and Stretch/Aspect to keep:

      -
      +
      Project settings - General - Initial window size
      Project settings - General - Initial window size

      Keybindings

      I only used 3 actions (keybindings): jump, restart and toggle_debug (optional). To add custom keybindings (so that the Input.something() API can be used), go to Project -> Project settings… -> Input Map and on the text box write jump and click add, then it will be added to the list and it’s just a matter of clicking the + sign to add a Physical key, press any key you want to be used to jump and click ok. Do the same for the rest of the actions.

      -
      +
      Project settings - Input Map - Adding necessary keybindings
      Project settings - Input Map - Adding necessary keybindings

      Layers

      Finally, rename the physics layers so we don’t lose track of which layer is which. Go to Project -> Layer Names -> 2d Physics and change the first 5 layer names to (in order): player, ground, pipe, ceiling and score.

      -
      +
      Project settings - Layer Names - 2D Physics
      Project settings - Layer Names - 2D Physics
      @@ -2349,12 +2359,12 @@ func physics_process(delta: float) -> void:

      Importing

      Create the necessary directories to hold the respective assets and it’s just a matter of dragging and dropping, I used directories: res://entities/actors/player/sprites/, res://fonts/, res://levels/world/background/sprites/, res://levels/world/ground/sprites/, res://levels/world/pipe/sprites/, res://sfx/. For the player sprites, the FileSystem window looks like this (entities/actor directories are really not necessary):

      -
      +
      FileSystem - Player sprite imports
      FileSystem - Player sprite imports

      It should look similar for other directories, except maybe for the file extensions. For example, for the sfx:

      -
      +
      FileSystem - SFX imports
      FileSystem - SFX imports
      @@ -2362,72 +2372,72 @@ func physics_process(delta: float) -> void:

      Now it’s time to actually create the game, by creating the basic scenes that will make up the game. The hardest part and the most confusing is going to be the TileMaps, so that goes first.

      TileMaps

      I’m using a scene called WorldTiles with a Node2D node as root called the same. With 2 different TileMap nodes as children named GroundTileMap and PipeTileMap (these are their own scene); yes 2 different TileMaps because we need 2 different physics colliders (in Godot 4.0 you can have a single TileMap with different physics colliders in it). Each node has its own script. It should look something like this:

      -
      +
      Scene - WorldTiles (TileMaps)
      Scene - WorldTiles (TileMaps)

      I used the following directory structure:

      -
      +
      Scene - WorldTiles - Directory structure
      Scene - WorldTiles - Directory structure

      To configure the GroundTileMap, select the node and click on (empty) on the TileMap/Tile set property and then click on New TileSet, then click where the (empty) used to be, a new window should open on the bottom:

      -
      +
      TileSet - Configuration window
      TileSet - Configuration window

      Click on the plus on the bottom left and you can now select the specific tile set to use. Now click on the yellow + New Single Tile, activate the grid and select any of the tiles. Should look like this:

      -
      +
      TileSet - New single tile
      TileSet - New single tile

      We need to do this because for some reason we can’t change the snap options before selecting a tile. After selecting a random tile, set up the Snap Options/Step (in the Inspector) and set it to 16x16 (or if using a different tile set, to it’s tile size):

      -
      +
      TileSet - Tile - Step snap options
      TileSet - Tile - Step snap options

      Now you can select the actual single tile. Once selected click on Collision, use the rectangle tool and draw the rectangle corresponding to that tile’s collision:

      -
      +
      TileSet - Tile - Selection and collision
      TileSet - Tile - Selection and collision

      Do the same for the other 3 tiles. If you select the TileMap itself again, it should look like this on the right (on default layout it’s on the left of the Inspector):

      -
      +
      TileSet - Available tiles
      TileSet - Available tiles

      The ordering is important only for the “underground tile”, which is the filler ground, it should be at the end (index 3); if this is not the case, repeat the process (it’s possible to rearrange them but it’s hard to explain as it’s pretty weird).

      At this point the tilemap doesn’t have any physics and the cell size is wrong. Select the GroundTileMap, set the TileMap/Cell/Size to 16x16, the TileMap/Collision/Layer set to bit 2 only (ground layer) and disable any TileMap/Collision/Mask bits. Should look something like this:

      -
      +
      TileMap - Cell size and collision configuration
      TileMap - Cell size and collision configuration

      Now it’s just a matter of repeating the same for the pipes (PipeTileMap), only difference is that when selecting the tiles you need to select 2 tiles, as the pipe is 2 tiles wide, or just set the Snap Options/Step to 32x16, for example, just keep the cell size to 16x16.

      Default ground tiles

      I added few default ground tiles to the scene, just for testing purposes but I left them there. These could be place programatically, but I was too lazy to change things. On the WorldTiles scene, while selecting the GroundTileMap, you can select the tiles you want to paint with, and left click in the grid to paint with the selected tile. Need to place tiles from (-8, 7) to (10, 7) as well as the tile below with the filler ground (the tile position/coordinates show at the bottom left, refer to the image below):

      -
      +
      Scene - WorldTiles - Default ground tiles
      Scene - WorldTiles - Default ground tiles

      Player

      On a new scene called Player with a KinematicBody2D node named Player as the root of the scene, then for the children: AnimatedSprite as Sprite, CollisionShape2D as Collision (with a circle shape) and 3 AudioStreamPlayers for JumpSound, DeadSound and HitSound. Not sure if it’s a good practice to have the audio here, since I did that at the end, pretty lazy. Then, attach a script to the Player node and then it should look like this:

      -
      +
      Scene - Player - Node setup
      Scene - Player - Node setup

      Select the Player node and set the CollisionShape2D/Collision/Layer to 1 and the CollisionObject2D/Collision/Mask to 2 and 3 (ground and pipe).

      For the Sprite node, when selecting it click on the (empty) for the AnimatedSprite/Frames property and click New SpriteFrames, click again where the (empty) used to be and ane window should open on the bottom:

      -
      +
      Scene - Player - SpriteFrames window
      Scene - Player - SpriteFrames window

      Right off the bat, set the Speed to 10 FPS (bottom left) and rename default to bird_1. With the bird_1 selected, click on the Add frames from a Sprite Sheet, which is the second button under Animation Frames: which looks has an icon of a small grid (next to the folder icon), a new window will popup where you need to select the respective sprite sheet to use and configure it for importing. On the Select Frames window, change the Vertical to 1, and then select all 4 frames (Ctrl + Scroll wheel to zoom in):

      -
      +
      Scene - Player - Sprite sheet importer
      Scene - Player - Sprite sheet importer

      After that, the SpriteFrames window should look like this:

      -
      +
      Scene - Player - SpriteFrames window with sprite sheet configured
      Scene - Player - SpriteFrames window with sprite sheet configured
      @@ -2446,12 +2456,12 @@ func physics_process(delta: float) -> void:

    Game

    This is the actual Game scene that holds all the playable stuff, here we will drop in all the previous scenes; the root node is a Node2D and also has an attached script. Also need to add 2 additional AudioStreamPlayers for the “start” and “score” sounds, as well as a Sprite for the background (Sprite/Offset/Offset set to (0, 10)) and a Camera2D (Camera2D/Current set to true (checked)). It should look something like this:

    -
    +
    Scene - Game - Node setup
    Scene - Game - Node setup

    The scene viewport should look something like the following:

    -
    +
    Scene - Game - Viewport
    Scene - Game - Viewport
    @@ -2459,12 +2469,12 @@ func physics_process(delta: float) -> void:

    Fonts

    We need some font Resources to style the Label fonts. Under the FileSystem window, right click on the fonts directory (create one if needed) and click on New Resource... and select DynamicFontData, save it in the “fonts” directory as SilverDynamicFontData.tres (Silver as it is the font I’m using) then double click the just created resource and set the DynamicFontData/Font Path to the actual Silver.ttf font (or whatever you want).

    Then create a new resource and this time select DynamicFont, name it SilverDynamicFont.tres, then double click to edit and add the SilverDynamicFontData.tres to the DynamicFont/Font/Font Data property (and I personally toggled off the DynamicFont/Font/Antialiased property), now just set the DynamicFont/Settings/(Size, Outline Size, Outline Color) to 32, 1 and black, respectively (or any other values you want). It should look something like this:

    -
    +
    Resource - DynamicFont - Default font
    Resource - DynamicFont - Default font

    Do the same for another DynamicFont which will be used for the score label, named SilverScoreDynamicFont.tres. Only changes are Dynamic/Settings/(Size, Outline Size) which are set to 128 and 2, respectively. The final files for the fonts should look something like this:

    -
    +
    Resource - Dynamicfont - Directory structure
    Resource - Dynamicfont - Directory structure
    @@ -2493,7 +2503,7 @@ func physics_process(delta: float) -> void:
  • The scene ends up looking like this:

    -
    +
    Scene - UI - Node setup
    Scene - UI - Node setup
    @@ -2627,7 +2637,7 @@ func _remove_first_ground() -> void:

    Where you might notice that the _initial_new_tile_x is 11, instead of 10, refer to Default ground tiles where we placed tiles from -8 to 10, so the next empty one is 11. These _place_new_ground and _remove_first_ground functions are called upon receiving the signal.

    PipeTileMap

    This is really similar to the GroundTileMap code, instead of defining an enum for the ground tiles, we define it for the pipe patterns (because each pipe is composed of multiple pipe tiles). If your pipe tile set looks like this (notice the index):

    -
    +
    PipeTileMap - Tile set indexes
    PipeTileMap - Tile set indexes
    @@ -2729,7 +2739,7 @@ func get_high_score() -> int: save_data()

    Now, this script in particular will need to be a Singleton (AutoLoad), which means that there will be only one instance and will be available across all scripts. To do so, go to Project -> Project settings… -> AutoLoad and select this script in the Path: and add a Node Name: (I used SavedData, if you use something else, be careful while following this devlog) which will be the name we’ll use to access the singleton. Toggle on Enable if needed, it should look like this:

    -
    +
    Project settings - AutoLoad - SavedData singleton
    Project settings - AutoLoad - SavedData singleton
    @@ -4505,7 +4515,7 @@ systemctl enable spamassassin.service
  • Password: your user password (as in the password you use to login to the server with that user)
  • All that’s left to do is test your mail server for spoofing, and to see if everything is setup correctly. Go to DKIM Test and follow the instructions (basically click next, and send an email with whatever content to the email that they provide). After you send the email, you should see something like:

    -
    +
    DKIM Test successful
    DKIM Test successful
    ]]> @@ -4554,7 +4564,7 @@ systemctl enable nginx.service systemctl start nginx.service

    And that’s it, at this point you can already look at the default initial page of Nginx if you enter the IP of your server in a web browser. You should see something like this:

    -
    +
    Nginx welcome page
    Nginx welcome page
    @@ -4621,7 +4631,7 @@ cd sites-available systemctl restart nginx

    If everything goes correctly, you can now go to your website by typing domain.name on a web browser. But you will see a “404 Not Found” page like the following (maybe with different Nginx version):

    -
    +
    Nginx 404 Not Found page
    Nginx 404 Not Found page
    -- cgit v1.2.3-70-g09d2