diff --git a/_site/2020-04-27-quiz-your-friends-xss.html b/_site/2020-04-27-quiz-your-friends-xss.html index d5adf79..d3c84ef 100644 --- a/_site/2020-04-27-quiz-your-friends-xss.html +++ b/_site/2020-04-27-quiz-your-friends-xss.html @@ -1 +1 @@ - The "Quiz Your Friends" XSS Exploit | tait.tech

The "Quiz Your Friends" XSS Exploit


TODO write intro

How I Found This Exploit

While filling in one of my friend’s surveys I thought it would be funny for them to know it is me without anyone else knowing. We were young and had Inspect Elemented a few things together, so it was a safe bet that an HTML joke would let them know.

I decided to write my name like so: <b>Steve</b>. Steve is in reference to the main character in the video game Minecraft.

Me typing in my name as <b>Steve</b>.

Now in theory this should have shown in in the leaderboard as: “<b>Steve</b>” However, to my horror and excitement, I saw this in the leaderboard:

<b>Steve</b> displaying in the leaderboard as bold text: Steve

The text “Steve” showed up in bold on the leaderboard. This told me all I needed to know. How did this happen? You might wonder.

Server-Side Validation

Here is a great demonstration why you should do most of your validation on the server side. As a user, I can edit any of the HTML, CSS, or Javascript your server serves to me.

Quiz your friends uses the maxlength=20 HTML attribute on the name input field. Imagine trying to fit in a script tag doing anything useful with 20 characters! Don’t forget that includes the <script> tag. That would leave 13 characters for Javascript. Although I’m sure a genius would be able to code golf that, I know I couldn’t.

Now obviously I can edit any HTML that a server has sent to me. If I open up my inspect element window, I can go ahead and change that maxlength attribute to anything I want. Let’s change it to 100!

An image of the Quiz Your Friends name input field with inspect element. THe code reads: <font class="style6"><input class="inputbutton" name="takername" type="text" id="takername" maxlength="20" width="425" placeholder="Your First Name" style="text-align: center; text-decoration:inherit; font-size:38px;" tabindex="-1"></font>
Manually changing the maxlength attribute.

In theory, there is a way that a site can stop people from just putting in their name of any length: server-side validation. The server could check to see if the input is too long and reject it if it is. The Quiz My Friends server has no such checks in place. Therefore, I can send an almost arbitrary load to them. Being able to send something potentially very large (more than a few megabytes) is a vulnerability of its own. Imagine being able to send entire executable programs as your “name” in one of these quizzes?

Hacking:

So I went on my merry way thinking about ways to use malicious javascript. Then, I thought that might be mean, so I decided to warn users instead. I filled in the name with a script tag and a call to alert() to warn the user about this site.

JAVASCRIPT_NAME.JPG

I ran out of room before I could finish it. Hmmm. What if I do “Inspect Element” and manually override the max-length attribute?

The unfortunate truth is: this worked as well!

Not only could I manually set the max-length by changing it in the HTML, but there were no client-side or server-side checks to make sure the name I was sending was less than or equal to 20 characters. If Javascript checked it, it wold have stopped me (although maybe not a professional). If the server checked it, it could have stopped almost anyone.

This means that I (or you) is able to write something of arbitrary length to the <script> tag.


\ No newline at end of file + The "Quiz Your Friends" XSS Exploit | tait.tech

The "Quiz Your Friends" XSS Exploit


TODO write intro

How I Found This Exploit

While filling in one of my friend’s surveys I thought it would be funny for them to know it is me without anyone else knowing. We were young and had Inspect Elemented a few things together, so it was a safe bet that an HTML joke would let them know.

I decided to write my name like so: <b>Steve</b>. Steve is in reference to the main character in the video game Minecraft.

Me typing in my name as <b>Steve</b>.

Now in theory this should have shown in in the leaderboard as: “<b>Steve</b>” However, to my horror and excitement, I saw this in the leaderboard:

<b>Steve</b> displaying in the leaderboard as bold text: Steve

The text “Steve” showed up in bold on the leaderboard. This told me all I needed to know. How did this happen? You might wonder.

Server-Side Validation

Here is a great demonstration why you should do most of your validation on the server side. As a user, I can edit any of the HTML, CSS, or Javascript your server serves to me.

Quiz your friends uses the maxlength=20 HTML attribute on the name input field. Imagine trying to fit in a script tag doing anything useful with 20 characters! Don’t forget that includes the <script> tag. That would leave 13 characters for Javascript. Although I’m sure a genius would be able to code golf that, I know I couldn’t.

Now obviously I can edit any HTML that a server has sent to me. If I open up my inspect element window, I can go ahead and change that maxlength attribute to anything I want. Let’s change it to 100!

An image of the Quiz Your Friends name input field with inspect element. THe code reads: <font class="style6"><input class="inputbutton" name="takername" type="text" id="takername" maxlength="20" width="425" placeholder="Your First Name" style="text-align: center; text-decoration:inherit; font-size:38px;" tabindex="-1"></font>
Manually changing the maxlength attribute.

In theory, there is a way that a site can stop people from just putting in their name of any length: server-side validation. The server could check to see if the input is too long and reject it if it is. The Quiz My Friends server has no such checks in place. Therefore, I can send an almost arbitrary load to them. Being able to send something potentially very large (more than a few megabytes) is a vulnerability of its own. Imagine being able to send entire executable programs as your “name” in one of these quizzes?

Hacking:

So I went on my merry way thinking about ways to use malicious javascript. Then, I thought that might be mean, so I decided to warn users instead. I filled in the name with a script tag and a call to alert() to warn the user about this site.

JAVASCRIPT_NAME.JPG

I ran out of room before I could finish it. Hmmm. What if I do “Inspect Element” and manually override the max-length attribute?

The unfortunate truth is: this worked as well!

Not only could I manually set the max-length by changing it in the HTML, but there were no client-side or server-side checks to make sure the name I was sending was less than or equal to 20 characters. If Javascript checked it, it wold have stopped me (although maybe not a professional). If the server checked it, it could have stopped almost anyone.

This means that I (or you) is able to write something of arbitrary length to the <script> tag.


\ No newline at end of file diff --git a/_site/2020/01/22/padding-and-margin.html b/_site/2020/01/22/padding-and-margin.html index cfe706f..fa1597f 100644 --- a/_site/2020/01/22/padding-and-margin.html +++ b/_site/2020/01/22/padding-and-margin.html @@ -1 +1 @@ - Padding And Margin | tait.tech

Padding And Margin


Many people have expressed confusion over how padding and margins work in HTML/CSS. I have been one of those people. In this short article I will explain what the differences are between the two, and how it may affect the functionality of your site.

Here is an image from the World Wide Web Consortium (W3C) who sets the standards for the web.

The W3C standard for padding, margin, borders and width. Width encompases the inner element + padding; the border then encompases it. The margin is the space beyond the border and in between other elements.

Now although this image shows all the different types of spacing as equal, the majority of the time these will mostly be padding (inner) and margin (outer). Padding is the inner space between the element and its border; margin is the outer space between two different elements.

Within the margin the user is unable to press any links or execute any javascript code. It is empty space. If each <link> on your navigation bar has 10 pixels of margin, then there would be 20 pixels in between each <link> that would not be clickable by the user.

If you have <link>s on your navigation bar with padding set to 20 pixels, however, then there will be 20 pixels on each side of the <link> text where the user is able to click.

If that part is confusing, try thinking about it in terms of whether background-color would apply.

Attribute Padding Margin
Spacing within element between elements
background-color applies Yes No

In summary:

  • Padding: the space within a tag which is still part of the same tag. background-color applies.
  • Margin: the space in between two seperate tags. background-color does not apply; it is empty space.
  • Border: the space in between the two; it surrounds the padding, but is not the margin. It looks nice somtimes, but it has no non-visual function. background-color does not apply.

I hope this covers the basics of margin and padding! Happy coding!


\ No newline at end of file + Padding And Margin | tait.tech

Padding And Margin


Many people have expressed confusion over how padding and margins work in HTML/CSS. I have been one of those people. In this short article I will explain what the differences are between the two, and how it may affect the functionality of your site.

Here is an image from the World Wide Web Consortium (W3C) who sets the standards for the web.

The W3C standard for padding, margin, borders and width. Width encompases the inner element + padding; the border then encompases it. The margin is the space beyond the border and in between other elements.

Now although this image shows all the different types of spacing as equal, the majority of the time these will mostly be padding (inner) and margin (outer). Padding is the inner space between the element and its border; margin is the outer space between two different elements.

Within the margin the user is unable to press any links or execute any javascript code. It is empty space. If each <link> on your navigation bar has 10 pixels of margin, then there would be 20 pixels in between each <link> that would not be clickable by the user.

If you have <link>s on your navigation bar with padding set to 20 pixels, however, then there will be 20 pixels on each side of the <link> text where the user is able to click.

If that part is confusing, try thinking about it in terms of whether background-color would apply.

Attribute Padding Margin
Spacing within element between elements
background-color applies Yes No

In summary:

  • Padding: the space within a tag which is still part of the same tag. background-color applies.
  • Margin: the space in between two seperate tags. background-color does not apply; it is empty space.
  • Border: the space in between the two; it surrounds the padding, but is not the margin. It looks nice somtimes, but it has no non-visual function. background-color does not apply.

I hope this covers the basics of margin and padding! Happy coding!


\ No newline at end of file diff --git a/_site/2020/02/19/rsa2.html b/_site/2020/02/19/rsa2.html index 76c5e78..13f0951 100644 --- a/_site/2020/02/19/rsa2.html +++ b/_site/2020/02/19/rsa2.html @@ -1 +1 @@ - How Does Encryption Work, in Theory? | tait.tech

How Does Encryption Work, in Theory?


There are many kinds of encryption used in our everyday communication. Online and offline, over the internet and in person. In this article, I will explain the basics of how encryption should work in theory. I explain in this article why encryption is important, and why you should care about it.

We will start by looking at in-person, offline encryption.

Cryptography We Do Everyday

We encrypt things all the time without even thinking about it. If you spend a significant amount of time with the same group of friends, you will tend to develop common codes that may not make sense to others outside the group. For example: for years, my family called sombody falling from a sitting position “doing a Don”. There is a story of course—We knew a guy named Don who fell from his plastic beach chair in a rather hilarious way; “doing a Don” was born.

These types of minor dialects in speech are cryptographic in their own way. The truth is though, that we use cryptography much more than that!

“Is cryptography any different than talking? We say something other than what we mean, and then expect everyone is able to decipher the true meaning behind the words. Only, I never do…” — Adapted from a scene in The Imitation Game (p. 39-40)

How many times have you hinted, flirted, and innuendoed to try to say “I find you very physically attractive”? Have you told your friend that always stinks to wear more deodorant? Have you ever had someone say the words “I’m fine” when you know for certain that they are indeed not okay?

Words Said Meaning
What can you do? I don’t want to talk about this anymore.
I don’t want to overstay my welcome. I want to go home now.
I don’t like them and don’t know why. They threaten my ego.
Creepy Unattractive and friendly

All of these scenarios are perfect examples of lies encryption! If we have the key to these codes, we can start to understand what people really mean. Hopefully I have convinced you that you use deceit cryptography on a regular basis in your life, so let us consider what a basic encryption method might be:

Grade-School Encryption

Back when I was in middle school I used to pass notes like these:

A message I would have sent in middle school. ROT5: Xfwfm hx hzy
The kind of message I would have sent in middle school. A ROT5 Ceasar cipher.

This is a message encrypted using the Caesar cipher. This encryption technique was used by Julius Caesar during the reign of the Roman Empire to “encrypt messages of military significance.”[1] This is one of the oldest and simplest methods of encryption known to us today.

A diagram of a Ceasar Shift algorithm. A <-> N, B <-> O, et cetera.
A diagram of a ROT13 Ceasar shift algorithm. A <-> N, B <-> O, et cetera.

You can try this out yourself by moving some letters forward in the alphabet. An ‘A’ turns into a ‘B’, ‘B’ into ‘C’, ‘C’ into ‘D’, et cetera. In this case, “Hello!” would become “Ifmmp!” That is just using a shift of one. You can use a shift of seven, for example, and then you would shift letters like so:

  • A -> +7 -> H
  • Q -> +7 -> X
  • T -> +7 -> A

When you reach the end of the alphabet, wrap around to the beginning to find the encrypted letter.

Example of a Caesar Cipher

Let’s setup a little story to illustrate the problems of encryption. We will have three characters:

  • Alice, young lady with feelings for Bob
  • Bob, a young lad with an addiction to pancakes
  • Eve, a wee jealous girl scout who sits between Bob and Alice

Alice really likes Bob and wants to tell Bob her feelings, so she writes “I love you, Bob! Please eat healthier!” on a sticky note. She passes it to Eve, so Eve can pass it to Alice’s love interest. However, in an unfortunate turn of events Eve reads the note herself, and decides not to give it to Bob.

Oh the horror! Alice is without young love! How could she remedy this so that Bob can read her message, but evil Eve can not? Let’s use the Caesar cipher to fix this problem.

Let us assume that Alice and Bob already have a shared key, 7 for example. To encrypt this message, she should shift her letters seven letters forward in the alphabet—just like the example above.

A longer Ceasar cipher encrypted message: ROT2: Wpeng Vgf ku dqqogt ogog]
A longer Ceasar cipher encrypted message using ROT2.

Now Alice’s message reads “P svcl fvb, Ivi! Wslhzl lha olhsaoply!”

Now, when Alice sends her Romeo a little note, all he has to do is decrypt the text by shifting the letters down by 7. Here is a site which can do longer pieces of text for you instead of doing it manually.

Problems

Before the two love-birds start smooching on the branch of a big pine tree in the schoolyard, perhaps we should consider some problems with the Ceasar cipher.

It is Very Easy to Break

Even Eve with her measly grade 4 math skills could easily start going through this message with pen and paper and figure out any combination in a couple hours at maximum. Imagine how easy this is for a computer? This could be broken in a few microseconds even on an older processor like the Intel Core 2 Duo.

No Secure Way of Sharing Keys

We assumed in our previous example that Bob and Alice already have a shared key (seven) to encrypt and decrypt all of their messages. If Bob and Alice did not have a previous friendship and time to share secrets of this sort, there is no way to share their key with eachother without Eve also knowing. This would defeat the entire purpose of obscuring the message in the first place.

Universal Vulnerability of Messages

Every message sent between the two parties uses the same code to encrypt and decrypt. If someone finds out the code once, all previous communications are comprimised.

Better Encryption Methods

To combat the issues with easily breakable, shared-key cryptography, we can turn to the beautiful beast that is Asymetric Cryptography. I will discuss this more in another article, but for the technically inclined:

  1. RSA/EC provides very large cryptographic keys. It would be impossible for a human to encrypt or decrypt a message manually.
  2. Asymetric cryptography provides four keys, instead of just one; stopping evesdroppers from listening in on your secret conversations—even if you do not have the chance to exchange keys in advance.

\ No newline at end of file + How Does Encryption Work, in Theory? | tait.tech

How Does Encryption Work, in Theory?


There are many kinds of encryption used in our everyday communication. Online and offline, over the internet and in person. In this article, I will explain the basics of how encryption should work in theory. I explain in this article why encryption is important, and why you should care about it.

We will start by looking at in-person, offline encryption.

Cryptography We Do Everyday

We encrypt things all the time without even thinking about it. If you spend a significant amount of time with the same group of friends, you will tend to develop common codes that may not make sense to others outside the group. For example: for years, my family called sombody falling from a sitting position “doing a Don”. There is a story of course—We knew a guy named Don who fell from his plastic beach chair in a rather hilarious way; “doing a Don” was born.

These types of minor dialects in speech are cryptographic in their own way. The truth is though, that we use cryptography much more than that!

“Is cryptography any different than talking? We say something other than what we mean, and then expect everyone is able to decipher the true meaning behind the words. Only, I never do…” — Adapted from a scene in The Imitation Game (p. 39-40)

How many times have you hinted, flirted, and innuendoed to try to say “I find you very physically attractive”? Have you told your friend that always stinks to wear more deodorant? Have you ever had someone say the words “I’m fine” when you know for certain that they are indeed not okay?

Words Said Meaning
What can you do? I don’t want to talk about this anymore.
I don’t want to overstay my welcome. I want to go home now.
I don’t like them and don’t know why. They threaten my ego.
Creepy Unattractive and friendly

All of these scenarios are perfect examples of lies encryption! If we have the key to these codes, we can start to understand what people really mean. Hopefully I have convinced you that you use deceit cryptography on a regular basis in your life, so let us consider what a basic encryption method might be:

Grade-School Encryption

Back when I was in middle school I used to pass notes like these:

A message I would have sent in middle school. ROT5: Xfwfm hx hzy
The kind of message I would have sent in middle school. A ROT5 Ceasar cipher.

This is a message encrypted using the Caesar cipher. This encryption technique was used by Julius Caesar during the reign of the Roman Empire to “encrypt messages of military significance.”[1] This is one of the oldest and simplest methods of encryption known to us today.

A diagram of a Ceasar Shift algorithm. A <-> N, B <-> O, et cetera.
A diagram of a ROT13 Ceasar shift algorithm. A <-> N, B <-> O, et cetera.

You can try this out yourself by moving some letters forward in the alphabet. An ‘A’ turns into a ‘B’, ‘B’ into ‘C’, ‘C’ into ‘D’, et cetera. In this case, “Hello!” would become “Ifmmp!” That is just using a shift of one. You can use a shift of seven, for example, and then you would shift letters like so:

  • A -> +7 -> H
  • Q -> +7 -> X
  • T -> +7 -> A

When you reach the end of the alphabet, wrap around to the beginning to find the encrypted letter.

Example of a Caesar Cipher

Let’s setup a little story to illustrate the problems of encryption. We will have three characters:

  • Alice, young lady with feelings for Bob
  • Bob, a young lad with an addiction to pancakes
  • Eve, a wee jealous girl scout who sits between Bob and Alice

Alice really likes Bob and wants to tell Bob her feelings, so she writes “I love you, Bob! Please eat healthier!” on a sticky note. She passes it to Eve, so Eve can pass it to Alice’s love interest. However, in an unfortunate turn of events Eve reads the note herself, and decides not to give it to Bob.

Oh the horror! Alice is without young love! How could she remedy this so that Bob can read her message, but evil Eve can not? Let’s use the Caesar cipher to fix this problem.

Let us assume that Alice and Bob already have a shared key, 7 for example. To encrypt this message, she should shift her letters seven letters forward in the alphabet—just like the example above.

A longer Ceasar cipher encrypted message: ROT2: Wpeng Vgf ku dqqogt ogog]
A longer Ceasar cipher encrypted message using ROT2.

Now Alice’s message reads “P svcl fvb, Ivi! Wslhzl lha olhsaoply!”

Now, when Alice sends her Romeo a little note, all he has to do is decrypt the text by shifting the letters down by 7. Here is a site which can do longer pieces of text for you instead of doing it manually.

Problems

Before the two love-birds start smooching on the branch of a big pine tree in the schoolyard, perhaps we should consider some problems with the Ceasar cipher.

It is Very Easy to Break

Even Eve with her measly grade 4 math skills could easily start going through this message with pen and paper and figure out any combination in a couple hours at maximum. Imagine how easy this is for a computer? This could be broken in a few microseconds even on an older processor like the Intel Core 2 Duo.

No Secure Way of Sharing Keys

We assumed in our previous example that Bob and Alice already have a shared key (seven) to encrypt and decrypt all of their messages. If Bob and Alice did not have a previous friendship and time to share secrets of this sort, there is no way to share their key with eachother without Eve also knowing. This would defeat the entire purpose of obscuring the message in the first place.

Universal Vulnerability of Messages

Every message sent between the two parties uses the same code to encrypt and decrypt. If someone finds out the code once, all previous communications are comprimised.

Better Encryption Methods

To combat the issues with easily breakable, shared-key cryptography, we can turn to the beautiful beast that is Asymetric Cryptography. I will discuss this more in another article, but for the technically inclined:

  1. RSA/EC provides very large cryptographic keys. It would be impossible for a human to encrypt or decrypt a message manually.
  2. Asymetric cryptography provides four keys, instead of just one; stopping evesdroppers from listening in on your secret conversations—even if you do not have the chance to exchange keys in advance.

\ No newline at end of file diff --git a/_site/2020/04/06/rsa4.html b/_site/2020/04/06/rsa4.html index 0a394e1..47bfb6c 100644 --- a/_site/2020/04/06/rsa4.html +++ b/_site/2020/04/06/rsa4.html @@ -1,4 +1,4 @@ - How To Encrypt Your Own Documents Using gpg | tait.tech

How To Encrypt Your Own Documents Using gpg


If you have ever wanted to garuntee the utmost security of your emails and documents, then this is the guide for you! It should be noted that in some circles the tools used are more common than in others. These are the everyday tools of many privacy advocates and computer nerds.

If you have never used Linux however, then the method of doing this will be rather unfamiliar. This tutorial will be done on an Arch Linux machine, but it should be the same on Ubuntu, Fedora, CentOS, Debian, OpenBSD, FreeBSD, MacOSX, etc. The only operating system that does not include these tools by default (or easily accessible) is Windows.

This tutorial makes heavy use of the terminal. You have been warned.

Let us…begin!

Glossary

  • ASCII armour — A way to encode OpenPGP documents so they are readable by humans. These files end in .asc
  • (Open)PGP — An open standard for encoding pulbic keys and encrypted documents.
  • GPG — GNUPrivacyGaurd is an implementation of OpenPGP. It is installed by default on most Linux distrobutions.

Step 0: Setup

We will be using the utility gpg for this tutorial.

The other thing to note: The character ‘$’ (dollar sign) is usually not typed when shown in a command. It simply indicates that you do not need administrative privilages to run these commands.

Test to see if you get this output in your terminal.

+    How To Encrypt Your Own Documents Using gpg | tait.tech       

How To Encrypt Your Own Documents Using gpg


If you have ever wanted to garuntee the utmost security of your emails and documents, then this is the guide for you! It should be noted that in some circles the tools used are more common than in others. These are the everyday tools of many privacy advocates and computer nerds.

If you have never used Linux however, then the method of doing this will be rather unfamiliar. This tutorial will be done on an Arch Linux machine, but it should be the same on Ubuntu, Fedora, CentOS, Debian, OpenBSD, FreeBSD, MacOSX, etc. The only operating system that does not include these tools by default (or easily accessible) is Windows.

This tutorial makes heavy use of the terminal. You have been warned.

Let us…begin!

Glossary

  • ASCII armour — A way to encode OpenPGP documents so they are readable by humans. These files end in .asc
  • (Open)PGP — An open standard for encoding pulbic keys and encrypted documents.
  • GPG — GNUPrivacyGaurd is an implementation of OpenPGP. It is installed by default on most Linux distrobutions.

Step 0: Setup

We will be using the utility gpg for this tutorial.

The other thing to note: The character ‘$’ (dollar sign) is usually not typed when shown in a command. It simply indicates that you do not need administrative privilages to run these commands.

Test to see if you get this output in your terminal.

 $ gpg --version
 
 gpg (GnuPG) 2.2.20
@@ -7,7 +7,7 @@ Copyright (C) 2020 Free Software Foundation, Inc.
 License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>
 
 ...
-

If this is not successful look into how to install these tools on your system.

Step 1: Get/Create A Public Key!

Get Somebody Else’s

Step one is having somebody to send your encrypted message to. Maybe this is a friend, a journalist, or a whistleblower.

To encrypt a document with somebody’s public key, you need to first obtain it. My public key is available at this link, and you can use it to send me encrypted stuff.

If you are on a linux terminal, you can use the curl or wget command to download it.

wget:

+

If this is not successful look into how to install these tools on your system.

Step 1: Get/Create A Public Key!

Get Somebody Else’s

Step one is having somebody to send your encrypted message to. Maybe this is a friend, a journalist, or a whistleblower.

To encrypt a document with somebody’s public key, you need to first obtain it. My public key is available at this link, and you can use it to send me encrypted stuff.

If you are on a linux terminal, you can use the curl or wget command to download it.

wget:

 $ wget https://tait.tech/public-key.asc
 

Curl:

 $ curl https://tait.tech/public-key.asc -o public-key.asc
@@ -25,7 +25,7 @@ Please select what kind of key you want:
   (4) RSA (sign only)
   (14) Existing key from card
 Your selection? 
-

Select the option 1. You want two keys, both RSA.

Next we will select the key size:

+

Select the option 1. You want two keys, both RSA.

Next we will select the key size:

 RSA keys may be between 1024 and 4096 bits long.
 What keysize do you want? (2048) 
 

Type the number 2048.

Next it will ask you how long you want the key to be valid.

@@ -40,7 +40,7 @@ Key is valid for? (0)
 

Type the number 1. This will enable you time to test it, but it will make the key expire within 24 hours so that if you accidentally share your private key, or delete your VM and no longer have access to it, you will be fine.

It will ask your if you are sure about the expiry date.

 Key expires at Tue Apr  7 02:24:23 2020 UTC
 Is this correct? (y/N) 
-

Type y to confirm your choice.

Now gpg is going to ask you to create a user id to indetify this key. Use some test data for now. User input is in bold, feel free to follow along or to put your own test data in.

Once you are more comfortable with the tools, then you can create a public/private keypair that you will keep for some time.

+

Type y to confirm your choice.

Now gpg is going to ask you to create a user id to indetify this key. Use some test data for now. User input is in bold, feel free to follow along or to put your own test data in.

Once you are more comfortable with the tools, then you can create a public/private keypair that you will keep for some time.

 GnuPG needs to construct a user ID to identify your key.
 
 Real name: Mr. Tester
@@ -50,7 +50,7 @@ You selected this USER-ID:
     "Mr. Tester (for testing only) <test@test.org>"
 
 Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O
-

It will then ask you for a password. If you are simply using this for test purposes, then you can feel free to set it to something like “test”. When create a long-term use pulbic key make sure to make the password very secure.

During the process of creating your key, gpg may warn you with this message:

+

It will then ask you for a password. If you are simply using this for test purposes, then you can feel free to set it to something like “test”. When create a long-term use pulbic key make sure to make the password very secure.

During the process of creating your key, gpg may warn you with this message:

 We need to generate a lot of random bytes. It is a good idea to perform
 some other action (type on the keyboard, move the mouse, utilize the
 disks) during the prime generation; this gives the random number
@@ -68,19 +68,19 @@ pub   rsa2048 2020-04-06 [SC] [expires: 2020-04-07]
 $ gpg --export --armour "Mr. Tester" > public-key.asc
 

To use the email as the identifier:

 $ gpg --export --armour "<test@test.org>" > public-key.asc
-

Step 2: Import Public Key

This list of keys that gpg keeps on tap so to speak, is called our “keyring”. Your will need to import a new public key to encrypt files with gpg.

If you already created your own public key, then this step is not necessary unless you want to also encrypt something for me :)

A keyring holding eight allen keys.
A keyring holding eight allen keys.

To import a public key to use for encrypting files, use the --import option of gpg. Like so:

+

Step 2: Import Public Key

This list of keys that gpg keeps on tap so to speak, is called our “keyring”. Your will need to import a new public key to encrypt files with gpg.

If you already created your own public key, then this step is not necessary unless you want to also encrypt something for me :)

A keyring holding eight allen keys.
A keyring holding eight allen keys.

To import a public key to use for encrypting files, use the --import option of gpg. Like so:

 $ gpg --import public-key.asc
 gpg: key 64FB4E386953BEAD: public key "Tait Hoyem <tait.hoyem@protonmail.com>" imported
 gpg: Total number processed: 1
 gpg:               imported: 1
-

Now that we have imported a public key, we can make a message to send!

Step 3: Have A Message To Encrypt

You can make a new file which holds some important, secret data. Feel free to use a graphical editor if you have one, if not, nano works alright too.

+

Now that we have imported a public key, we can make a message to send!

Step 3: Have A Message To Encrypt

You can make a new file which holds some important, secret data. Feel free to use a graphical editor if you have one, if not, nano works alright too.

   Rules Of A Good Life:
 
   1. Wash your hands!
   2. Work hard!
   3. Be firm.
   5. Have good friends!
-

Save this file as something like test-pgp.txt, and we’ll use that name later.

Step 4: Encrypt A Message

Now that we have a message to send and person to send to, all we have to do is encrypt this message and it’ll be on its merry way! To do so, we must specify two new options to gpg.

The first is --recipient. This tells gpg to encrypt using a certin public key that we have in our keyring. You can use the person’s name, email address, or the key’s uid.

The second is --encrypt.

You will also specify the --armour option to use ASCII armoured files. Put this option after --encrypt, and put the file name after --armour. See below.

You can either use your own public key name to encrypt a document (allowng only you to decrypt it), or you can use my public key that we imported earlier (allowing only me to decrypt it). Either way works fine.

This is the big one!

+

Save this file as something like test-pgp.txt, and we’ll use that name later.

Step 4: Encrypt A Message

Now that we have a message to send and person to send to, all we have to do is encrypt this message and it’ll be on its merry way! To do so, we must specify two new options to gpg.

The first is --recipient. This tells gpg to encrypt using a certin public key that we have in our keyring. You can use the person’s name, email address, or the key’s uid.

The second is --encrypt.

You will also specify the --armour option to use ASCII armoured files. Put this option after --encrypt, and put the file name after --armour. See below.

You can either use your own public key name to encrypt a document (allowng only you to decrypt it), or you can use my public key that we imported earlier (allowing only me to decrypt it). Either way works fine.

This is the big one!

 $ gpg --recipient "Tait Hoyem" --encrypt --armour test-gpg.txt
 

“But there is no output!” you might say! Yes, that is because our new (encrypted) file has already been saved. Let’s look at it with cat.

 $ cat test-gpg.txt.asc
@@ -103,7 +103,7 @@ jaDMzZnIKoax1GFz/coUAHFQub2rLzaQ5DDbvrkX++UrAjuUtRcSFH0TKhahZmCF
 nv117moLfK22Mst/
 =bw8T
 -----END PGP MESSAGE-----
-

Step 5: Decryption (optional)

If you created your own public/private keypair in step 1, and you encryped using --recipient "Your Test Name", then you can decrypt your document as well!

You will need to specify --decrypt, and that’s all folks!

+

Step 5: Decryption (optional)

If you created your own public/private keypair in step 1, and you encryped using --recipient "Your Test Name", then you can decrypt your document as well!

You will need to specify --decrypt, and that’s all folks!

 $ gpg --decrypt test-gpg.txt.asc
 

A password dialog will then come up asking for your previously created password. As long as you remember your password from before and enter it correctly: voila!

 gpg: encrypted with 4096-bit RSA key, ID 6989B986FCBE4225, created 2020-01-02
diff --git a/_site/2020/04/12/nas1.html b/_site/2020/04/12/nas1.html
index 663037d..38c95db 100644
--- a/_site/2020/04/12/nas1.html
+++ b/_site/2020/04/12/nas1.html
@@ -1 +1 @@
-    NAS Part 1: Theorize | tait.tech       

NAS Part 1: Theorize


New Project, phase one: Theorize.

I want to build a NAS server to store a bunch of data on. Current problem is lack of a computer to accept multiple SATA connections.

Problem 1: SATA connectors

This can be solved by an HBE card. Although they tend to be quite expensive (250+). One decent model that isn’t that much is the LSI 9211-8I. This is ideal for future expansion.

A cheaper option is a PCIe multi-SATA connector like this.

Either work, but one is cheaper and the other is more expandable. The 9211-8I uses two SAS ports, which can be expanded indefinetely. SAS supports splitting. SATA can be connected in a 4:1 ratio to SAS connectors with some cheap cables.

Problem 2: Drives

I do not have enough drives to make this work right now. For the setup I want it would require 5 or 6 drives. I will get 4-5 drives worth of space as one drive worth of space is dedicated to “parity”, making you able to:

  1. Verify data integrity. If anything goes wrong with a write, it will be fixed automatically.
  2. If one drive dies, the system can stay online with no problem. Two drives and I’m eff-you-see-kay-ed-dee.

My other option is to use two drives worth of space for partiy. This would only have me 3-4 drives of space, but this system can withstand the failure of two drives.

Problem 3: Computer System

I currently have 5 computers.

  1. Celery Stick. An old grey HP laptop with a Braille stickered keyboard. Does not work right now; bad thermal paste job.
  2. A Dell laptop lent to me by my school during my studies.
  3. Houston. A 21-inch 2011 iMac for which the screen does not work under Linux (excep with the nomodeset kernel option enabled).
  4. An Old Toshiba laptop (circa 2010) that I got for $50 to test with OpenBSD (works….sometimes).
  5. Main Rig. My main laptop is an ASUS-705 TUF gaming laptop.

None of these have PCIe expansion slots with a case that can handle the new drives.

I think it’s reasonable to say that for hard-drives and low-end tower PCs, I will likely have luck on a place like Kijiji (Canadian Craigslist).

The search continues :)

I’m in for a fun ride…. and a few monnies.


\ No newline at end of file + NAS Part 1: Theorize | tait.tech

NAS Part 1: Theorize


New Project, phase one: Theorize.

I want to build a NAS server to store a bunch of data on. Current problem is lack of a computer to accept multiple SATA connections.

Problem 1: SATA connectors

This can be solved by an HBE card. Although they tend to be quite expensive (250+). One decent model that isn’t that much is the LSI 9211-8I. This is ideal for future expansion.

A cheaper option is a PCIe multi-SATA connector like this.

Either work, but one is cheaper and the other is more expandable. The 9211-8I uses two SAS ports, which can be expanded indefinetely. SAS supports splitting. SATA can be connected in a 4:1 ratio to SAS connectors with some cheap cables.

Problem 2: Drives

I do not have enough drives to make this work right now. For the setup I want it would require 5 or 6 drives. I will get 4-5 drives worth of space as one drive worth of space is dedicated to “parity”, making you able to:

  1. Verify data integrity. If anything goes wrong with a write, it will be fixed automatically.
  2. If one drive dies, the system can stay online with no problem. Two drives and I’m eff-you-see-kay-ed-dee.

My other option is to use two drives worth of space for partiy. This would only have me 3-4 drives of space, but this system can withstand the failure of two drives.

Problem 3: Computer System

I currently have 5 computers.

  1. Celery Stick. An old grey HP laptop with a Braille stickered keyboard. Does not work right now; bad thermal paste job.
  2. A Dell laptop lent to me by my school during my studies.
  3. Houston. A 21-inch 2011 iMac for which the screen does not work under Linux (excep with the nomodeset kernel option enabled).
  4. An Old Toshiba laptop (circa 2010) that I got for $50 to test with OpenBSD (works….sometimes).
  5. Main Rig. My main laptop is an ASUS-705 TUF gaming laptop.

None of these have PCIe expansion slots with a case that can handle the new drives.

I think it’s reasonable to say that for hard-drives and low-end tower PCs, I will likely have luck on a place like Kijiji (Canadian Craigslist).

The search continues :)

I’m in for a fun ride…. and a few monnies.


\ No newline at end of file diff --git a/_site/2020/04/25/xss.html b/_site/2020/04/25/xss.html index b490c41..b1dba37 100644 --- a/_site/2020/04/25/xss.html +++ b/_site/2020/04/25/xss.html @@ -1,6 +1,6 @@ - What is XSS? | tait.tech

What is XSS?


I found a cross-site scripting (XSS) attack in a well-known quiz hosting website. I disclosed the vulnerability to them years ago, so I thought now might be a good time to write about it.

In this first article I will explain what XSS is.

In the next article I will explain how I found this attack.

What is cross-site scripting (XSS)

Cross-site scripting, XSS for short, is a technique to execute arbitrary Javascript code on a user visiting a website by linking to Javascript code stored on another server.

So for example:

I have a file on my website called hacked.js. If I was able to run this javascript file on anybody visiting a certain website that is not mine, this would be called cross-site scripting.

Click the above hacked.js link to view the code I use to “hack” this website. It’s safe, I promise ;)

Now, how can we get this code to execute when a user visits this site? To explain, I will start with some of the underlying technologies.

Escape Characters!

No, this is not a Sherlock Holmes novel!

If we suppose that a website is built with sequences like these (called “tags”): <body>, <p> (for paragraph), <link> and <b> for bold, then why can you see the left and right angle bracket characters? Don’t they mean something? Shouldn’t they be telling the browser: “Hey! Make me bold!”? Why doesn’t everything after me typing <b> turn bold?

The answer is:

There are special characters in HTML to type a visible left (<) and visible right angle bracket (>) in a website. If I use the left and right brackets on my keyboard however, things will indeed show up bold.

This is the code for the sentence I wrote above:

+    What is XSS? | tait.tech       

What is XSS?


I found a cross-site scripting (XSS) attack in a well-known quiz hosting website. I disclosed the vulnerability to them years ago, so I thought now might be a good time to write about it.

In this first article I will explain what XSS is.

In the next article I will explain how I found this attack.

What is cross-site scripting (XSS)

Cross-site scripting, XSS for short, is a technique to execute arbitrary Javascript code on a user visiting a website by linking to Javascript code stored on another server.

So for example:

I have a file on my website called hacked.js. If I was able to run this javascript file on anybody visiting a certain website that is not mine, this would be called cross-site scripting.

Click the above hacked.js link to view the code I use to “hack” this website. It’s safe, I promise ;)

Now, how can we get this code to execute when a user visits this site? To explain, I will start with some of the underlying technologies.

Escape Characters!

No, this is not a Sherlock Holmes novel!

If we suppose that a website is built with sequences like these (called “tags”): <body>, <p> (for paragraph), <link> and <b> for bold, then why can you see the left and right angle bracket characters? Don’t they mean something? Shouldn’t they be telling the browser: “Hey! Make me bold!”? Why doesn’t everything after me typing <b> turn bold?

The answer is:

There are special characters in HTML to type a visible left (<) and visible right angle bracket (>) in a website. If I use the left and right brackets on my keyboard however, things will indeed show up bold.

This is the code for the sentence I wrote above:

 There are special characters in HTML to type a visible left (&lt;)
 and visible right angle bracket (&gt;) in a website.
 If I use the left and right brackets on my keyboard however,
 things will indeed <b>show up bold</b>.
-

Notice how all visible left angle brackets use an &lt; to show them?

These are called escape characters. They tell a system, in this case your web browser: “Hello! Please show me off! I don’t want to be hidden.”

Sanitization

Most of the time XSS attacks are done using poorly sanitized HTML <input> elements.

Sanitization is when a program (usually on the server side), will remove characters like < and replace them with the aforementioned “escape characters”. Internally this would be something like &lt;, but they would show up to a user as <.

When inputs are not properly sanitized and the input is shown to the user in another part of the website, then a malicous user can type in HTML that will run whenever anybody tries to look at what they typed. For example: a name for a quiz website (input) and the leaderboard for said quiz (display).

HTML, by itself is not very dangerous. The worst thing you could do is probably put a link on your name, and then point it to a porn site. Make your name bold, italic. Maybe make the background a funny color. Although this may annoy your victim it is not dangerous security wise.

There is one tag however, that is scary…

<script>

The <script> tag allows you to write code that can:

  1. Change the page contents.
  2. Redirect the user to a new page automatically.
  3. Get a user’s location.
  4. Open a user’s microphone/webcam.
  5. With the src attribute you can also load a script from another site. (This is XSS)

Those last two will ask for permission from the user (if their browser isn’t insanely insecure).

In my next article I’ll talk about a website I found which is vulnerable to this attack. And, show you how you can run your own XSS attack.


\ No newline at end of file +

Notice how all visible left angle brackets use an &lt; to show them?

These are called escape characters. They tell a system, in this case your web browser: “Hello! Please show me off! I don’t want to be hidden.”

Sanitization

Most of the time XSS attacks are done using poorly sanitized HTML <input> elements.

Sanitization is when a program (usually on the server side), will remove characters like < and replace them with the aforementioned “escape characters”. Internally this would be something like &lt;, but they would show up to a user as <.

When inputs are not properly sanitized and the input is shown to the user in another part of the website, then a malicous user can type in HTML that will run whenever anybody tries to look at what they typed. For example: a name for a quiz website (input) and the leaderboard for said quiz (display).

HTML, by itself is not very dangerous. The worst thing you could do is probably put a link on your name, and then point it to a porn site. Make your name bold, italic. Maybe make the background a funny color. Although this may annoy your victim it is not dangerous security wise.

There is one tag however, that is scary…

<script>

The <script> tag allows you to write code that can:

  1. Change the page contents.
  2. Redirect the user to a new page automatically.
  3. Get a user’s location.
  4. Open a user’s microphone/webcam.
  5. With the src attribute you can also load a script from another site. (This is XSS)

Those last two will ask for permission from the user (if their browser isn’t insanely insecure).

In my next article I’ll talk about a website I found which is vulnerable to this attack. And, show you how you can run your own XSS attack.


\ No newline at end of file diff --git a/_site/2020/05/01/nginx-socket-io-projects.html b/_site/2020/05/01/nginx-socket-io-projects.html index 207e648..fe30fc7 100644 --- a/_site/2020/05/01/nginx-socket-io-projects.html +++ b/_site/2020/05/01/nginx-socket-io-projects.html @@ -1,4 +1,4 @@ - How to use NGINX as a reverse-proxy server for a Node.js application using socket.io | tait.tech

How to use NGINX as a reverse-proxy server for a Node.js application using socket.io


Despite the long name of the article, I have a feeling this may apply to more people than I might think. If you have a Node.js application which needs socket.io connections that you want to pass throgh nginx’s reverse_proxy directive then this is the article for you!

You must seperate the socket.io sockets and the static resources.

  • The socket connections can be routed through the default $host/socket.io if you want to ease modifications to the source code.
  • The connections to your main npm Node.js application can be routed through the relevant directory.

Here is the relevant part of my projects.tait.tech.conf file:

+    How to use NGINX as a reverse-proxy server for a Node.js application using socket.io | tait.tech       

How to use NGINX as a reverse-proxy server for a Node.js application using socket.io


Despite the long name of the article, I have a feeling this may apply to more people than I might think. If you have a Node.js application which needs socket.io connections that you want to pass throgh nginx’s reverse_proxy directive then this is the article for you!

You must seperate the socket.io sockets and the static resources.

  • The socket connections can be routed through the default $host/socket.io if you want to ease modifications to the source code.
  • The connections to your main npm Node.js application can be routed through the relevant directory.

Here is the relevant part of my projects.tait.tech.conf file:

 location /socket.io {
   proxy_pass http://localhost:8080/socket.io/;
   proxy_set_header Upgrade $http_upgrade;
@@ -14,7 +14,7 @@ location /ttrpg {
   proxy_set_header X-Real-IP $remote_addr;
   proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
 }
-

Explaination:

For this application, I needed the /ttrpg directory to connect to my main Node.js instance. This was going to be the root of a ttrpg project. It was to have static files served form my Node.js application.

I also needed /socket.io to conenct to my running npm instance. When I tried to route all the traffic through the /trrpg location directive I had no luck whatsoever; $host/ttrpg/socket.io/* calls always failed with a 404.

Having two seperate blocks forwarding in different ways seems to fix this. I am not knowledgable enough to understand how.

For now, the project is alive!!!

Happy hacking!

P.S. I forgot to mention I also symbolically linked the socket.io.js file (that node is supposed to serve automatically) to the static client dir. For some reson the node instance would not serve this file without that.

+

Explaination:

For this application, I needed the /ttrpg directory to connect to my main Node.js instance. This was going to be the root of a ttrpg project. It was to have static files served form my Node.js application.

I also needed /socket.io to conenct to my running npm instance. When I tried to route all the traffic through the /trrpg location directive I had no luck whatsoever; $host/ttrpg/socket.io/* calls always failed with a 404.

Having two seperate blocks forwarding in different ways seems to fix this. I am not knowledgable enough to understand how.

For now, the project is alive!!!

Happy hacking!

P.S. I forgot to mention I also symbolically linked the socket.io.js file (that node is supposed to serve automatically) to the static client dir. For some reson the node instance would not serve this file without that.

 $ pwd
 /home/user/ttrpg.co/client
 $ ln -s ../server/node_modules/socket.io-client/dist/socket.io.js .
diff --git a/_site/2020/06/04/site-update.html b/_site/2020/06/04/site-update.html
index 6a9cd41..70003ca 100644
--- a/_site/2020/06/04/site-update.html
+++ b/_site/2020/06/04/site-update.html
@@ -1 +1 @@
-    Site Update | tait.tech       

Site Update


I updated the site with some easier to identify information about me and my projects :)

Also, Clue has been delayed due to my partner in crime on the project wokring too many hours.

I also posted a new project called Caesar Cipher in C. It will be an intermediate example of how to use build systems like make.


\ No newline at end of file + Site Update | tait.tech

Site Update


I updated the site with some easier to identify information about me and my projects :)

Also, Clue has been delayed due to my partner in crime on the project wokring too many hours.

I also posted a new project called Caesar Cipher in C. It will be an intermediate example of how to use build systems like make.


\ No newline at end of file diff --git a/_site/2020/06/25/tmux-minecraft.html b/_site/2020/06/25/tmux-minecraft.html index 555472e..887ff9f 100644 --- a/_site/2020/06/25/tmux-minecraft.html +++ b/_site/2020/06/25/tmux-minecraft.html @@ -1,21 +1,21 @@ - How to use tmux to send and receive things from your Minecraft server | tait.tech

How to use tmux to send and receive things from your Minecraft server


So recently I had problem. I run a Minecraft server on a big Linux computer I have running in my room. Now, as a system administrator it is very helpful to be able to run some simple commands without needing to login with my key, password, TFA, etc. It is, frankly, a lot of work. Especially when I really just want to be playing games but I just need to check something quickly.

So for simple things like finding out of the network, CPU, memory or disk usage is my bottleneck, I wrote this really nifty script to connect the world of Minecraft and the Linux shell.

My completed solution for what I needed can be found at https://github.com/TTWNO/termcraft.

If you want some of the implementation details, stick around.

Solution

So to solve this interesting problem, I decided to use tmux. tmux is a tterminal multiplexer. This allows you to run a terminal session, then detach fromc it while it still runs in the background.

This is very valuable when running command line applications that need to have an active console connection, like a Minecraft server.

So first I looked at the tmux command send-keys.

send-keys

send-keys allows you to send text, and key presses to a tmux session. Now assuming this tmux session is attached to a Minecraft server, there is no reason you could not run a command like this:

+    How to use tmux to send and receive things from your Minecraft server | tait.tech       

How to use tmux to send and receive things from your Minecraft server


So recently I had problem. I run a Minecraft server on a big Linux computer I have running in my room. Now, as a system administrator it is very helpful to be able to run some simple commands without needing to login with my key, password, TFA, etc. It is, frankly, a lot of work. Especially when I really just want to be playing games but I just need to check something quickly.

So for simple things like finding out of the network, CPU, memory or disk usage is my bottleneck, I wrote this really nifty script to connect the world of Minecraft and the Linux shell.

My completed solution for what I needed can be found at https://github.com/TTWNO/termcraft.

If you want some of the implementation details, stick around.

Solution

So to solve this interesting problem, I decided to use tmux. tmux is a tterminal multiplexer. This allows you to run a terminal session, then detach fromc it while it still runs in the background.

This is very valuable when running command line applications that need to have an active console connection, like a Minecraft server.

So first I looked at the tmux command send-keys.

send-keys

send-keys allows you to send text, and key presses to a tmux session. Now assuming this tmux session is attached to a Minecraft server, there is no reason you could not run a command like this:

 $ tmux send-keys "tell @a This is a Test" Enter
-

This will send the text “tell @a This is a Test” to the Minecraft server. Then, it will hit the newline character, this will execute the command.

So now we can send information to the server and have it tell the users something.

But how do we get information about who is typing what in the Minecraft chat?

tmux’s capture-pane is painful

So in the manual page for tmux I can see a section recorded below for options I can give to the capture-pane subcommand.

+

This will send the text “tell @a This is a Test” to the Minecraft server. Then, it will hit the newline character, this will execute the command.

So now we can send information to the server and have it tell the users something.

But how do we get information about who is typing what in the Minecraft chat?

tmux’s capture-pane is painful

So in the manual page for tmux I can see a section recorded below for options I can give to the capture-pane subcommand.

   -S and -E specify the starting and ending line numbers,
   zero is the first line of the visible pane and negative
   numbers are lines in the history.  ‘-’ to -S is the start
   of the history and to -E the end of the visible pane.  The
   default is to capture only the visible contents of the pane.
-

What it seems to be saying is I can start at line -S n and end at line -E n. Negative numbers start from the bottom, so in theory I can do the following: tmux capture-pane -S -1 should capture only the last line, because I’m starting from the last line. Right?

No. It just doesn’t work. Negative numbers do not work with the tmux capture-pane subcommand.

So I did some simple UNIX piping, like so, to get just the last thing in the chat.

+

What it seems to be saying is I can start at line -S n and end at line -E n. Negative numbers start from the bottom, so in theory I can do the following: tmux capture-pane -S -1 should capture only the last line, because I’m starting from the last line. Right?

No. It just doesn’t work. Negative numbers do not work with the tmux capture-pane subcommand.

So I did some simple UNIX piping, like so, to get just the last thing in the chat.

 $ tmux capture-pane -p -t steve | tail -n1
 [SERVER] [ExtraDebuggingInfoHere]: <TaterTheTot> MY_MESSAGE
-

TaterTheTot is my Minecraft username :)

-p prints the result to the terminal/stdout.

steve is the name of the tmux session I’m trying to pull form.

So that’s done! Beauty!

Now that we have that, how can we extract the username and the message from the latest line?

grep

grep is a command to find patterns of text. grep has an option to only show a matching pattern of text. This option is -o.

Let’s see how we can use this in conjunction with our latest line of server output to get our results.

+

TaterTheTot is my Minecraft username :)

-p prints the result to the terminal/stdout.

steve is the name of the tmux session I’m trying to pull form.

So that’s done! Beauty!

Now that we have that, how can we extract the username and the message from the latest line?

grep

grep is a command to find patterns of text. grep has an option to only show a matching pattern of text. This option is -o.

Let’s see how we can use this in conjunction with our latest line of server output to get our results.

 $ echo "[DEBUG] [SERVER] blah blah: <TaterTheTot> MY_MESAGE" | grep -o "<.*>"
 <TaterTheTot>
-

Now, that’s my name with the < and > attached. Not bad! We can use the sed command to clean it up a bit.

The syntax is like so: select/somepattern/replacewith/global

So the following command is: s/[<>]//g

Select any characters that are either < or >. Replace with nothing. Do so globally (as in, don’t stop after you replace only one character).

Take two!

+

Now, that’s my name with the < and > attached. Not bad! We can use the sed command to clean it up a bit.

The syntax is like so: select/somepattern/replacewith/global

So the following command is: s/[<>]//g

Select any characters that are either < or >. Replace with nothing. Do so globally (as in, don’t stop after you replace only one character).

Take two!

 $ echo "[DEBUG] [SERVER] blah blah: <TaterTheTot> MY_MESAGE" | grep -o "<.*>" | sed 's/[<>]//g'
 TaterTheTot
-

Beautiful!

Now what about that pesky message?

more grep; more sed

Simple: capture everything after the >. Leaving the user’s message entirely in tact.

+

Beautiful!

Now what about that pesky message?

more grep; more sed

Simple: capture everything after the >. Leaving the user’s message entirely in tact.

 $ echo "[DEBUG] [SERVER] blah blah: <TaterTheTot> MY_MESAGE" | grep -o ">.*$" | sed 's/> //'
 MY_MESSAGE
 

So now we have a way to get the username of someone typing in the Minecraft server chat. We have a way to find out what they said. And, we have a way to respond.

You can imagine how these might go together for your own use case.

Conclusion

This shows some pretty fun stuff you can do with a few simple Linux commands and a Minecraft server.

I hope you learned something and found my explanations not horrific haha!

Remember to checkout the git repository to see what I did with it: https://github.com/TTWNO/termcraft.

Happy hacking!


\ No newline at end of file diff --git a/_site/2020/07/19/multicraft-php-gentoo.html b/_site/2020/07/19/multicraft-php-gentoo.html index cf4948d..b53176d 100644 --- a/_site/2020/07/19/multicraft-php-gentoo.html +++ b/_site/2020/07/19/multicraft-php-gentoo.html @@ -1,6 +1,6 @@ - Installing MultiCraft on Gentoo Linux | tait.tech

Installing MultiCraft on Gentoo Linux


In a very odd combination of requirements, I needed to install MultiCraft on a Gentoo Linux system. The PHP USE flags are important so you don’t have to recompile it three times like I did.

Here are some useful tips I came across:

PHP USE flags

In /etc/portage/package.use/php I placed the following line:

+    Installing MultiCraft on Gentoo Linux | tait.tech       

Installing MultiCraft on Gentoo Linux


In a very odd combination of requirements, I needed to install MultiCraft on a Gentoo Linux system. The PHP USE flags are important so you don’t have to recompile it three times like I did.

Here are some useful tips I came across:

PHP USE flags

In /etc/portage/package.use/php I placed the following line:

 dev-lang/php cgi mysql mysqli fpm pdo gd truetype
-

This should give you enough for a mysql backended MultiCraft installation. The cgi option may not be required as fpm stands for FastCGI Process Managment. I don’t know for sure though.

Paper

This will grab the latest version of the Paper jar file using YivesMirror. I’m not sure how reputable it is, but my buddy who works with this stuff more often than me seemed to recognize it.

+

This should give you enough for a mysql backended MultiCraft installation. The cgi option may not be required as fpm stands for FastCGI Process Managment. I don’t know for sure though.

Paper

This will grab the latest version of the Paper jar file using YivesMirror. I’m not sure how reputable it is, but my buddy who works with this stuff more often than me seemed to recognize it.

 ## See the default craftbukkit.jar.conf for a detailed documentation of the
 ## format of this file.
 [config]
@@ -15,7 +15,7 @@ category = Mods
 
 [start]
 command = "{JAVA}" -Xmx{MAX_MEMORY}M -Xms{START_MEMORY}M -XX:MaxPermSize=128M -Djline.terminal=jline.UnsupportedTerminal -jar "{JAR}" nogui
-

Other Tips

Do not use the option to setup a separate user for each server. This completely stalled any work getting done with a ton of ‘permission denied’ errors.

Security

If the panel is in the root directory of your NGINX web server, use the following in your server block to deny access to the /protected directory.

+

Other Tips

Do not use the option to setup a separate user for each server. This completely stalled any work getting done with a ton of ‘permission denied’ errors.

Security

If the panel is in the root directory of your NGINX web server, use the following in your server block to deny access to the /protected directory.

 location /protected {
   deny all;
   return 404;
diff --git a/_site/2020/08/18/django-deployment.html b/_site/2020/08/18/django-deployment.html
index f08d53c..6c15280 100644
--- a/_site/2020/08/18/django-deployment.html
+++ b/_site/2020/08/18/django-deployment.html
@@ -1,6 +1,6 @@
     How to Solve The Django Deployment Puzzle | tait.tech       

How to Solve The Django Deployment Puzzle


A few days ago I had a Django project I wanted to put on a real server. This project is still in its infancy, but I thought it would be nice to put it on my resume and show my friends. Little did I know the headache coming my way. Here are some tips to help you not make the same mistakes as me.

ASGI Servers

Because my project used the ASGI (Asynchronous webServer Gateway Interface), I needed to find a good production ASGI server to handle all the incoming requests. The best thing I found was uvicorn. It focuses on speed, which is a priority, especially when using the ASGI protocol.

To run uvicorn on the command line for testing purposes, use something like the following:

 $ uvicorn --reload myapp.asgi:application
-

The --reload option says to reload the server if any of the files get updated. This is not recommended in production. Sadly, I thought this meant I would need to do a hard shutdown of the server process every time I wanted to update. This turned out to not be the case.

Workload Managers

There is another equine-named program called gunicorn which can hold a number of processes under its control. An interesting feature of gunicorn is that it will gracefully switch from an old to a new deployment, replacing the subprocesses one-by-one and eventually having only the new deployment active on all subprocesses. The greatest part? Zero down time. The server keeps any old processes open if there is communication with them, then shift and new connections to the new deployment. This was a very cool feature I wanted to take advantage of.

“Now hold on!” you might protest. “gunicorn is a WSGI server!” … oh you got me there! Yes, that’s right, gunicorn is paired with uvicorn to serve my files.

systemd

Love it or hate it, the majority of Linux distributions use the systemd init system. I decided it would be very convenient to have a .service file for my Django application to run automatically at boot. Systemd allows me to do this with a file like the following one I stored in /lib/systemd/system/lamegames.service.

+

The --reload option says to reload the server if any of the files get updated. This is not recommended in production. Sadly, I thought this meant I would need to do a hard shutdown of the server process every time I wanted to update. This turned out to not be the case.

Workload Managers

There is another equine-named program called gunicorn which can hold a number of processes under its control. An interesting feature of gunicorn is that it will gracefully switch from an old to a new deployment, replacing the subprocesses one-by-one and eventually having only the new deployment active on all subprocesses. The greatest part? Zero down time. The server keeps any old processes open if there is communication with them, then shift and new connections to the new deployment. This was a very cool feature I wanted to take advantage of.

“Now hold on!” you might protest. “gunicorn is a WSGI server!” … oh you got me there! Yes, that’s right, gunicorn is paired with uvicorn to serve my files.

systemd

Love it or hate it, the majority of Linux distributions use the systemd init system. I decided it would be very convenient to have a .service file for my Django application to run automatically at boot. Systemd allows me to do this with a file like the following one I stored in /lib/systemd/system/lamegames.service.

 [Unit]
 Description=Gunicorn/Uvicorn (lamegames.io)
 
@@ -14,7 +14,7 @@ Restart=always
 
 [Install]
 WantedBy=multi-user.target
-

nginx

NGINX (pronounced engine-X) is a performance web server designed for speed and simplicity. For the front facing side of the site, I do need a production web server like nginx. Gunicorn simply doesn’t need all the features that nginx provides, but I do. To configure my nginx installation, I used the following few directives to:

  1. Redirect most traffic towards the gunicorn server.
  2. Redirect statically served files (CSS, JS, images) to the directory specified in the STATIC_ROOT variable of my settings.py file.
  3. Use TLS to enable https://

Serving the static files from nginx as opposed to the gunicorn server is necessary. Gunicorn and other production A/WSGI web server will not set the proper MIME type over TLS. This will cause your browser to not load the Javascript/CSS.

This is the important part of my nginx config.

+

nginx

NGINX (pronounced engine-X) is a performance web server designed for speed and simplicity. For the front facing side of the site, I do need a production web server like nginx. Gunicorn simply doesn’t need all the features that nginx provides, but I do. To configure my nginx installation, I used the following few directives to:

  1. Redirect most traffic towards the gunicorn server.
  2. Redirect statically served files (CSS, JS, images) to the directory specified in the STATIC_ROOT variable of my settings.py file.
  3. Use TLS to enable https://

Serving the static files from nginx as opposed to the gunicorn server is necessary. Gunicorn and other production A/WSGI web server will not set the proper MIME type over TLS. This will cause your browser to not load the Javascript/CSS.

This is the important part of my nginx config.

 server {
     location / {
         proxy_set_header Host $http_host;
@@ -37,4 +37,4 @@ server {
 }
 

Setup

After all that, I was able to do the following:

 # systemctl enable lamegames
-

This enabled my gunicorn server to run once the server started. NGINX is that way be default.

And tada! You now have a working Django project on a production server!

Notes

  • If using ws:// websockets, change them to wss:// for secure web sockets.
  • Make sure to use channels.routing.get_default_application() instead of django.get_asgi_application() if your’re wanting to use channels/redis WebSockets.

\ No newline at end of file +

This enabled my gunicorn server to run once the server started. NGINX is that way be default.

And tada! You now have a working Django project on a production server!

Notes

  • If using ws:// websockets, change them to wss:// for secure web sockets.
  • Make sure to use channels.routing.get_default_application() instead of django.get_asgi_application() if your’re wanting to use channels/redis WebSockets.

\ No newline at end of file diff --git a/_site/assets/css/style.css b/_site/assets/css/style.css index 823cdd1..af1906e 100644 --- a/_site/assets/css/style.css +++ b/_site/assets/css/style.css @@ -1 +1 @@ -body{background-color:#fefefe;padding:16px;font-family:-apple-system,helvetica,arial,sans-serif;font-size:16px;color:#444}#wrapper{max-width:800px;margin:auto}.clear-list{list-style-type:none;margin:0;padding:0}.clear-list li{margin:0;padding:0}.projects a{font-weight:bold}h1{font-size:2.5em}h2{font-size:2em}h3{font-size:1.6em}h4{font-size:1.3em}h5{font-size:1.1em}header{margin-bottom:32px}hr{border:0;border-bottom:1px solid #999}a{text-decoration:underline;color:#47a}a:visited{color:#941452}a.nav-link,a.post-title-link{color:#333;text-decoration:none}.post-title{color:#333;line-height:1.3em}a.citation-link{text-decoration:none}#menu,label[for="menu"]{display:none}nav{text-align:center;padding:1em 0}nav a{margin:1em;color:#333;font-weight:bold;font-style:none}nav a:hover{text-decoration:underline}li{margin:.5em}p{line-height:1.5em;padding:.1em 0}table,table tr,table td,table th{border:1px solid #aaa;border-collapse:collapse;padding:5px;font-weight:normal}table th{font-weight:bold}table{width:75%;margin:auto}img{display:block;width:55%;margin-left:auto;margin-right:auto}blockquote{font-style:italic}address{font-style:normal}@media screen and (max-width:600px){#menu,label[for="menu"]{text-align:left;display:inline-block;font-size:20px}body{width:90%}#info{margin:0 7px}.menu-content{max-height:0;overflow:hidden}nav{text-align:left;width:100%}nav a{display:block;text-align:left;padding-left:0;margin-left:0}#menu{display:none}input:checked ~ .menu-content{max-height:100%}}.mono{font-family:monospace}.bold{font-weight:bold}sup{margin:0;padding:0}figcaption{margin-top:10px;font-style:italic}footer{padding-top:16px;margin-bottom:100px}.terminal{line-height:1em;overflow:scroll;padding:10px;color:#00ff41;margin:0;background-color:#151515}.file{overflow:scroll;padding:10px;margin:0;line-height:1.2em;background-color:#dfdfdf;color:#000}.small-image{width:100%}.post-date{font-size:18px;text-transform:uppercase;font-weight:bold;color:#555}.post-excerpt{margin:16px;margin-bottom:0}.highlight .hll{background-color:#ffc}.highlight{background:#f0f0f0}.highlight .c{color:#60a0b0;font-style:italic}.highlight .err{border:1px solid #f00}.highlight .k{color:#007020;font-weight:bold}.highlight .o{color:#666}.highlight .ch{color:#60a0b0;font-style:italic}.highlight .cm{color:#60a0b0;font-style:italic}.highlight .cp{color:#007020}.highlight .cpf{color:#60a0b0;font-style:italic}.highlight .c1{color:#60a0b0;font-style:italic}.highlight .cs{color:#60a0b0;background-color:#fff0f0}.highlight .gd{color:#a00000}.highlight .ge{font-style:italic}.highlight .gr{color:#f00}.highlight .gh{color:#000080;font-weight:bold}.highlight .gi{color:#00a000}.highlight .go{color:#888}.highlight .gp{color:#c65d09;font-weight:bold}.highlight .gs{font-weight:bold}.highlight .gu{color:#800080;font-weight:bold}.highlight .gt{color:#04d}.highlight .kc{color:#007020;font-weight:bold}.highlight .kd{color:#007020;font-weight:bold}.highlight .kn{color:#007020;font-weight:bold}.highlight .kp{color:#007020}.highlight .kr{color:#007020;font-weight:bold}.highlight .kt{color:#902000}.highlight .m{color:#40a070}.highlight .s{color:#4070a0}.highlight .na{color:#4070a0}.highlight .nb{color:#007020}.highlight .nc{color:#0e84b5;font-weight:bold}.highlight .no{color:#60add5}.highlight .nd{color:#555;font-weight:bold}.highlight .ni{color:#d55537;font-weight:bold}.highlight .ne{color:#007020}.highlight .nf{color:#06287e}.highlight .nl{color:#002070;font-weight:bold}.highlight .nn{color:#0e84b5;font-weight:bold}.highlight .nt{color:#062873;font-weight:bold}.highlight .nv{color:#bb60d5}.highlight .ow{color:#007020;font-weight:bold}.highlight .w{color:#bbb}.highlight .mb{color:#40a070}.highlight .mf{color:#40a070}.highlight .mh{color:#40a070}.highlight .mi{color:#40a070}.highlight .mo{color:#40a070}.highlight .sa{color:#4070a0}.highlight .sb{color:#4070a0}.highlight .sc{color:#4070a0}.highlight .dl{color:#4070a0}.highlight .sd{color:#4070a0;font-style:italic}.highlight .s2{color:#4070a0}.highlight .se{color:#4070a0;font-weight:bold}.highlight .sh{color:#4070a0}.highlight .si{color:#70a0d0;font-style:italic}.highlight .sx{color:#c65d09}.highlight .sr{color:#235388}.highlight .s1{color:#4070a0}.highlight .ss{color:#517918}.highlight .bp{color:#007020}.highlight .fm{color:#06287e}.highlight .vc{color:#bb60d5}.highlight .vg{color:#bb60d5}.highlight .vi{color:#bb60d5}.highlight .vm{color:#bb60d5}.highlight .il{color:#40a070}.highlight .n{color:#000}div.highlight{padding:0 5px;width:85%;margin:auto;overflow-x:scroll} \ No newline at end of file +body{background-color:#fefefe;padding:16px;font-family:-apple-system,helvetica,arial,sans-serif;font-size:16px;color:#444;line-height:1.5em}#wrapper{max-width:800px;margin:auto}.clear-list{list-style-type:none;margin:0;padding:0}.clear-list li{margin:0;padding:0}.projects a{font-weight:bold}h1{font-size:2.5em}h2{font-size:2em}h3{font-size:1.6em}h4{font-size:1.3em}h5{font-size:1.1em}header{margin-bottom:32px}hr{border:0;border-bottom:1px solid #999}a{text-decoration:underline;color:#47a}a:visited{color:#941452}a.nav-link,a.post-title-link{color:#333;text-decoration:none}.post-title{line-height:1.5em;color:#333}a.citation-link{text-decoration:none}#menu,label[for="menu"]{display:none}nav{text-align:center;padding:1em 0}nav a{margin:1em;color:#333;font-weight:bold;font-style:none}nav a:hover{text-decoration:underline}li{margin:.5em}p{padding:.1em 0}table,table tr,table td,table th{border:1px solid #aaa;border-collapse:collapse;padding:5px;font-weight:normal}table th{font-weight:bold}table{width:75%;margin:auto}img{display:block;width:55%;margin-left:auto;margin-right:auto}blockquote{font-style:italic}address{font-style:normal}@media screen and (max-width:600px){#menu,label[for="menu"]{text-align:left;display:inline-block;font-size:20px}body{width:90%}#info{margin:0 7px}.menu-content{max-height:0;overflow:hidden}nav{text-align:left;width:100%}nav a{display:block;text-align:left;padding-left:0;margin-left:0}#menu{display:none}input:checked ~ .menu-content{max-height:100%}}.mono{font-family:monospace}.bold{font-weight:bold}sup{margin:0;padding:0}figcaption{margin-top:10px;font-style:italic}footer{padding-top:16px;margin-bottom:100px}.terminal{line-height:1em;overflow:scroll;padding:10px;color:#00ff41;margin:0;background-color:#151515}.file{overflow:scroll;padding:10px;margin:0;line-height:1.2em;background-color:#dfdfdf;color:#000}.small-image{width:100%}.post-date{font-size:18px;text-transform:uppercase;font-weight:bold;color:#555}.post-excerpt{margin:16px;margin-bottom:0}.highlight .hll{background-color:#ffc}.highlight{background:#f0f0f0}.highlight .c{color:#60a0b0;font-style:italic}.highlight .err{border:1px solid #f00}.highlight .k{color:#007020;font-weight:bold}.highlight .o{color:#666}.highlight .ch{color:#60a0b0;font-style:italic}.highlight .cm{color:#60a0b0;font-style:italic}.highlight .cp{color:#007020}.highlight .cpf{color:#60a0b0;font-style:italic}.highlight .c1{color:#60a0b0;font-style:italic}.highlight .cs{color:#60a0b0;background-color:#fff0f0}.highlight .gd{color:#a00000}.highlight .ge{font-style:italic}.highlight .gr{color:#f00}.highlight .gh{color:#000080;font-weight:bold}.highlight .gi{color:#00a000}.highlight .go{color:#888}.highlight .gp{color:#c65d09;font-weight:bold}.highlight .gs{font-weight:bold}.highlight .gu{color:#800080;font-weight:bold}.highlight .gt{color:#04d}.highlight .kc{color:#007020;font-weight:bold}.highlight .kd{color:#007020;font-weight:bold}.highlight .kn{color:#007020;font-weight:bold}.highlight .kp{color:#007020}.highlight .kr{color:#007020;font-weight:bold}.highlight .kt{color:#902000}.highlight .m{color:#40a070}.highlight .s{color:#4070a0}.highlight .na{color:#4070a0}.highlight .nb{color:#007020}.highlight .nc{color:#0e84b5;font-weight:bold}.highlight .no{color:#60add5}.highlight .nd{color:#555;font-weight:bold}.highlight .ni{color:#d55537;font-weight:bold}.highlight .ne{color:#007020}.highlight .nf{color:#06287e}.highlight .nl{color:#002070;font-weight:bold}.highlight .nn{color:#0e84b5;font-weight:bold}.highlight .nt{color:#062873;font-weight:bold}.highlight .nv{color:#bb60d5}.highlight .ow{color:#007020;font-weight:bold}.highlight .w{color:#bbb}.highlight .mb{color:#40a070}.highlight .mf{color:#40a070}.highlight .mh{color:#40a070}.highlight .mi{color:#40a070}.highlight .mo{color:#40a070}.highlight .sa{color:#4070a0}.highlight .sb{color:#4070a0}.highlight .sc{color:#4070a0}.highlight .dl{color:#4070a0}.highlight .sd{color:#4070a0;font-style:italic}.highlight .s2{color:#4070a0}.highlight .se{color:#4070a0;font-weight:bold}.highlight .sh{color:#4070a0}.highlight .si{color:#70a0d0;font-style:italic}.highlight .sx{color:#c65d09}.highlight .sr{color:#235388}.highlight .s1{color:#4070a0}.highlight .ss{color:#517918}.highlight .bp{color:#007020}.highlight .fm{color:#06287e}.highlight .vc{color:#bb60d5}.highlight .vg{color:#bb60d5}.highlight .vi{color:#bb60d5}.highlight .vm{color:#bb60d5}.highlight .il{color:#40a070}.highlight .n{color:#000}div.highlight{padding:0 5px;width:85%;margin:auto;overflow-x:scroll} \ No newline at end of file diff --git a/_site/assets/css/style.css.map b/_site/assets/css/style.css.map new file mode 100644 index 0000000..c76d315 --- /dev/null +++ b/_site/assets/css/style.css.map @@ -0,0 +1 @@ +{ "version": 3, "file": "style.css", "sources": [ "style.scss", "_sass/main.scss" ], "sourcesContent": [ "@import \"main\";\n", "$normal-text-color: #444444;\n$nav-link-color: #333;\n$nav-link-hover-color: black;\n$link-color: #47a;\n$visited-link-color: #941452;\n\n$last-p-padd: 1.5em;\n$nav-padd: 1em;\n$line-under: 1px solid #aaa; \n\n$line-height: 1.5em;\n$term-line-height: 1em;\n$file-line-height: 1.2em;\n\nbody {\n background-color: #fefefe;\n padding: 16px;\n font-family: -apple-system, helvetica, arial, sans-serif;\n font-size: 16px;\n color: $normal-text-color;\n line-height: $line-height;\n}\n#wrapper {\n max-width: 800px;\n margin: auto;\n}\n\n.clear-list {\n list-style-type: none;\n margin: 0;\n padding: 0;\n}\n.clear-list li {\n margin: 0;\n padding: 0;\n}\n.projects a {\n font-weight: bold;\n}\nh1 {\n font-size: 2.5em;\n}\nh2 {\n font-size: 2.0em;\n}\nh3 {\n font-size: 1.6em;\n}\nh4 {\n font-size: 1.3em;\n}\nh5 {\n font-size: 1.1em;\n}\nheader {\n margin-bottom: 32px;\n}\nhr {\n border: none;\n border-bottom: 1px solid #999;\n}\na {\n text-decoration: underline;\n color: $link-color;\n} \na:visited {\n color: $visited-link-color;\n}\na.nav-link,\na.post-title-link {\n color: $nav-link-color;\n text-decoration: none;\n}\n.post-title {\n line-height: $line-height;\n color: $nav-link-color;\n}\na.citation-link {\n text-decoration: none;\n}\n\n#menu,\nlabel[for=\"menu\"]{\n display: none;\n}\n\nnav {\n text-align: center;\n padding: $nav-padd 0px;\n}\nnav a {\n margin: 1em;\n color: $nav-link-color;\n font-weight: bold;\n font-style: none;\n}\nnav a:hover {\n text-decoration: underline; \n}\n\nli {\n margin: .5em;\n}\n\np {\n padding: .1em 0;\n}\n\ntable,\ntable tr,\ntable td,\ntable th {\n border: 1px solid #aaa;\n border-collapse: collapse;\n padding: 5px;\n font-weight: normal;\n}\ntable th {\n font-weight: bold;\n}\ntable {\n width: 75%;\n margin: auto;\n}\n\nimg {\n display: block;\n width: 55%;\n margin-left: auto;\n margin-right: auto;\n}\n\nblockquote {\n font-style: italic;\n}\naddress { \n font-style: normal;\n}\n\n@media screen and (max-width: 600px){\n #menu,\n label[for=\"menu\"]{\n text-align: left;\n display: inline-block;\n font-size: 20px;\n }\n body {\n width: 90%;\n }\n #info {\n margin: 0 7px;\n }\n .menu-content {\n max-height: 0;\n overflow: hidden;\n }\n nav {\n text-align: left;\n width: 100%;\n }\n nav a {\n display: block;\n text-align: left;\n padding-left: 0;\n margin-left: 0;\n }\n #menu {\n display: none;\n }\n input:checked ~ .menu-content {\n max-height: 100%;\n }\n}\n\n.mono {\n font-family: monospace;\n}\n\n.bold {\n font-weight: bold;\n}\n\nsup {\n margin: 0;\n padding: 0;\n}\n\nfigcaption {\n margin-top: 10px;\n font-style: italic;\n}\n\nfooter {\n padding-top: 16px;\n margin-bottom: 100px;\n}\n\n.terminal {\n line-height: $term-line-height;\n overflow: scroll;\n padding: 10px; \n color: #00FF41;\n margin: 0px;\n background-color: #151515;\n}\n\n.file {\n overflow: scroll;\n padding: 10px;\n margin: 0px;\n line-height: $file-line-height;\n background-color: #dfdfdf;\n color: #000;\n}\n\n.small-image {\n width: 100%;\n}\n\n.post-date {\n font-size: 18px;\n text-transform: uppercase;\n font-weight: bold;\n color: #555;\n}\n\n.post-excerpt {\n margin: 16px;\n margin-bottom: 0;\n}\n\n\n/* code highlghting */\n.highlight .hll { background-color: #ffffcc }\n.highlight { background: #f0f0f0; }\n.highlight .c { color: #60a0b0; font-style: italic } /* Comment */\n.highlight .err { border: 1px solid #FF0000 } /* Error */\n.highlight .k { color: #007020; font-weight: bold } /* Keyword */\n.highlight .o { color: #666666 } /* Operator */\n.highlight .ch { color: #60a0b0; font-style: italic } /* Comment.Hashbang */\n.highlight .cm { color: #60a0b0; font-style: italic } /* Comment.Multiline */\n.highlight .cp { color: #007020 } /* Comment.Preproc */\n.highlight .cpf { color: #60a0b0; font-style: italic } /* Comment.PreprocFile */\n.highlight .c1 { color: #60a0b0; font-style: italic } /* Comment.Single */\n.highlight .cs { color: #60a0b0; background-color: #fff0f0 } /* Comment.Special */\n.highlight .gd { color: #A00000 } /* Generic.Deleted */\n.highlight .ge { font-style: italic } /* Generic.Emph */\n.highlight .gr { color: #FF0000 } /* Generic.Error */\n.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */\n.highlight .gi { color: #00A000 } /* Generic.Inserted */\n.highlight .go { color: #888888 } /* Generic.Output */\n.highlight .gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */\n.highlight .gs { font-weight: bold } /* Generic.Strong */\n.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */\n.highlight .gt { color: #0044DD } /* Generic.Traceback */\n.highlight .kc { color: #007020; font-weight: bold } /* Keyword.Constant */\n.highlight .kd { color: #007020; font-weight: bold } /* Keyword.Declaration */\n.highlight .kn { color: #007020; font-weight: bold } /* Keyword.Namespace */\n.highlight .kp { color: #007020 } /* Keyword.Pseudo */\n.highlight .kr { color: #007020; font-weight: bold } /* Keyword.Reserved */\n.highlight .kt { color: #902000 } /* Keyword.Type */\n.highlight .m { color: #40a070 } /* Literal.Number */\n.highlight .s { color: #4070a0 } /* Literal.String */\n.highlight .na { color: #4070a0 } /* Name.Attribute */\n.highlight .nb { color: #007020 } /* Name.Builtin */\n.highlight .nc { color: #0e84b5; font-weight: bold } /* Name.Class */\n.highlight .no { color: #60add5 } /* Name.Constant */\n.highlight .nd { color: #555555; font-weight: bold } /* Name.Decorator */\n.highlight .ni { color: #d55537; font-weight: bold } /* Name.Entity */\n.highlight .ne { color: #007020 } /* Name.Exception */\n.highlight .nf { color: #06287e } /* Name.Function */\n.highlight .nl { color: #002070; font-weight: bold } /* Name.Label */\n.highlight .nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */\n.highlight .nt { color: #062873; font-weight: bold } /* Name.Tag */\n.highlight .nv { color: #bb60d5 } /* Name.Variable */\n.highlight .ow { color: #007020; font-weight: bold } /* Operator.Word */\n.highlight .w { color: #bbbbbb } /* Text.Whitespace */\n.highlight .mb { color: #40a070 } /* Literal.Number.Bin */\n.highlight .mf { color: #40a070 } /* Literal.Number.Float */\n.highlight .mh { color: #40a070 } /* Literal.Number.Hex */\n.highlight .mi { color: #40a070 } /* Literal.Number.Integer */\n.highlight .mo { color: #40a070 } /* Literal.Number.Oct */\n.highlight .sa { color: #4070a0 } /* Literal.String.Affix */\n.highlight .sb { color: #4070a0 } /* Literal.String.Backtick */\n.highlight .sc { color: #4070a0 } /* Literal.String.Char */\n.highlight .dl { color: #4070a0 } /* Literal.String.Delimiter */\n.highlight .sd { color: #4070a0; font-style: italic } /* Literal.String.Doc */\n.highlight .s2 { color: #4070a0 } /* Literal.String.Double */\n.highlight .se { color: #4070a0; font-weight: bold } /* Literal.String.Escape */\n.highlight .sh { color: #4070a0 } /* Literal.String.Heredoc */\n.highlight .si { color: #70a0d0; font-style: italic } /* Literal.String.Interpol */\n.highlight .sx { color: #c65d09 } /* Literal.String.Other */\n.highlight .sr { color: #235388 } /* Literal.String.Regex */\n.highlight .s1 { color: #4070a0 } /* Literal.String.Single */\n.highlight .ss { color: #517918 } /* Literal.String.Symbol */\n.highlight .bp { color: #007020 } /* Name.Builtin.Pseudo */\n.highlight .fm { color: #06287e } /* Name.Function.Magic */\n.highlight .vc { color: #bb60d5 } /* Name.Variable.Class */\n.highlight .vg { color: #bb60d5 } /* Name.Variable.Global */\n.highlight .vi { color: #bb60d5 } /* Name.Variable.Instance */\n.highlight .vm { color: #bb60d5 } /* Name.Variable.Magic */\n.highlight .il { color: #40a070 } /* Literal.Number.Integer.Long */\n\n/* ADDED CUSTOM */\n.highlight .n { color: #000; } /* General name */\n\ndiv.highlight {\n padding: 0px 5px;\n width: 85%;\n margin: auto;\n overflow-x: scroll;\n}\n" ], "names": [], "mappings": "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" } \ No newline at end of file diff --git a/_site/blog/index.html b/_site/blog/index.html index 9f2259f..27bf1ea 100644 --- a/_site/blog/index.html +++ b/_site/blog/index.html @@ -1 +1 @@ - Blog | tait.tech

Blog

Minesweeper Bomb Generation And Tile Revealing

When I was creating a little Minesweeper game, I got confused at some points. My bomb generation didn’t look quite right, and I for sure didn’t quite get the whole cascading tile reveal thing. With a bit of internet research, I found what I was looking for. I’ll explain it all in one place for my own research purposes.

How to Solve The Django Deployment Puzzle

A few days ago I had a Django project I wanted to put on a real server. This project is still in its infancy, but I thought it would be nice to put it on my resume and show my friends. Little did I know the headache coming my way. Here are some tips to help you not make the same mistakes as me.

BSD Journey, Part 1

As Linux becomes controlled by corporate sponsors and becomes more full of proprietary blobs, drivers, and even closed-source software like Steam, One may wonder if there are other options out there. For me, somebody that is intensely interested in security, there is one option: OpenBSD.

Know How Your Representative Votes In Parliament

As an advocate for openness, I had an idea to make a project out of the government of Canada’s Open Data initiative to take a look at how my local MP voted on various pieces of legislation. It turns out though that this was not necessary due to how easy it was to find this information on the government’s own website. In this article, I will explain how you can do the same.

Installing MultiCraft on Gentoo Linux

In a very odd combination of requirements, I needed to install MultiCraft on a Gentoo Linux system. The PHP USE flags are important so you don’t have to recompile it three times like I did.

Independence

“When given a choice between independence and dependence, always choose independence; you will never regret that choice!”—Luke Smith

How to use tmux to send and receive things from your Minecraft server

So recently I had problem. I run a Minecraft server on a big Linux computer I have running in my room. Now, as a system administrator it is very helpful to be able to run some simple commands without needing to login with my key, password, TFA, etc. It is, frankly, a lot of work. Especially when I really just want to be playing games but I just need to check something quickly.

Site Update

I updated the site with some easier to identify information about me and my projects :)

New Game: Clue (coming soon)

Ooo! Exciting! Today I want to announce a new project I’ll be working on which should be live within the month of May: Clue.

What is XSS?

I found a cross-site scripting (XSS) attack in a well-known quiz hosting website. I disclosed the vulnerability to them years ago, so I thought now might be a good time to write about it.

How To Encrypt Your Own Documents Using gpg

If you have ever wanted to garuntee the utmost security of your emails and documents, then this is the guide for you! It should be noted that in some circles the tools used are more common than in others. These are the everyday tools of many privacy advocates and computer nerds.

How Does Encryption Work, in Theory?

There are many kinds of encryption used in our everyday communication. Online and offline, over the internet and in person. In this article, I will explain the basics of how encryption should work in theory. I explain in this article why encryption is important, and why you should care about it.

Is Encryption Worth It?

What is the most embarassing thing you have typed into Google search? What is the most personal secret you told a friend in confidence? What is your bank password? What is your business’s secret to stay ahead of the competition?

Padding And Margin

Many people have expressed confusion over how padding and margins work in HTML/CSS. I have been one of those people. In this short article I will explain what the differences are between the two, and how it may affect the functionality of your site.


\ No newline at end of file + Blog | tait.tech

Blog

Minesweeper Bomb Generation And Tile Revealing

When I was creating a little Minesweeper game, I got confused at some points. My bomb generation didn’t look quite right, and I for sure didn’t quite get the whole cascading tile reveal thing. With a bit of internet research, I found what I was looking for. I’ll explain it all in one place for my own research purposes.

How to Solve The Django Deployment Puzzle

A few days ago I had a Django project I wanted to put on a real server. This project is still in its infancy, but I thought it would be nice to put it on my resume and show my friends. Little did I know the headache coming my way. Here are some tips to help you not make the same mistakes as me.

BSD Journey, Part 1

As Linux becomes controlled by corporate sponsors and becomes more full of proprietary blobs, drivers, and even closed-source software like Steam, One may wonder if there are other options out there. For me, somebody that is intensely interested in security, there is one option: OpenBSD.

Know How Your Representative Votes In Parliament

As an advocate for openness, I had an idea to make a project out of the government of Canada’s Open Data initiative to take a look at how my local MP voted on various pieces of legislation. It turns out though that this was not necessary due to how easy it was to find this information on the government’s own website. In this article, I will explain how you can do the same.

Installing MultiCraft on Gentoo Linux

In a very odd combination of requirements, I needed to install MultiCraft on a Gentoo Linux system. The PHP USE flags are important so you don’t have to recompile it three times like I did.

Independence

“When given a choice between independence and dependence, always choose independence; you will never regret that choice!”—Luke Smith

How to use tmux to send and receive things from your Minecraft server

So recently I had problem. I run a Minecraft server on a big Linux computer I have running in my room. Now, as a system administrator it is very helpful to be able to run some simple commands without needing to login with my key, password, TFA, etc. It is, frankly, a lot of work. Especially when I really just want to be playing games but I just need to check something quickly.

Site Update

I updated the site with some easier to identify information about me and my projects :)

New Game: Clue (coming soon)

Ooo! Exciting! Today I want to announce a new project I’ll be working on which should be live within the month of May: Clue.

What is XSS?

I found a cross-site scripting (XSS) attack in a well-known quiz hosting website. I disclosed the vulnerability to them years ago, so I thought now might be a good time to write about it.

How To Encrypt Your Own Documents Using gpg

If you have ever wanted to garuntee the utmost security of your emails and documents, then this is the guide for you! It should be noted that in some circles the tools used are more common than in others. These are the everyday tools of many privacy advocates and computer nerds.

How Does Encryption Work, in Theory?

There are many kinds of encryption used in our everyday communication. Online and offline, over the internet and in person. In this article, I will explain the basics of how encryption should work in theory. I explain in this article why encryption is important, and why you should care about it.

Is Encryption Worth It?

What is the most embarassing thing you have typed into Google search? What is the most personal secret you told a friend in confidence? What is your bank password? What is your business’s secret to stay ahead of the competition?

Padding And Margin

Many people have expressed confusion over how padding and margins work in HTML/CSS. I have been one of those people. In this short article I will explain what the differences are between the two, and how it may affect the functionality of your site.


\ No newline at end of file diff --git a/_site/feed.xml b/_site/feed.xml index f2e669b..0e69f3e 100644 --- a/_site/feed.xml +++ b/_site/feed.xml @@ -1 +1 @@ -Jekyll2020-09-25T20:48:25+00:00/feed.xmlMinesweeper Bomb Generation And Tile Revealing2020-09-12T00:00:00+00:002020-09-12T00:00:00+00:00/2020/09/12/minesweeper<p>When I was creating a little Minesweeper game, I got confused at some points. My bomb generation didn’t look quite right, and I for sure didn’t quite get the whole cascading tile reveal thing. With a bit of internet research, I found what I was looking for. I’ll explain it all in one place for my own research purposes.</p> <h2 id="bomb-generation">Bomb Generation</h2> <p>When I started this project I attempted to use a random bomb generator. By this I mean on each square, before it gets generated, give it a one in 15 change of being a bomb. Personally, I’m not sure why this never looked right. Something about the layout of the bombs did not mimic the classic Minesweeper game.</p> <p>After looking at some open source Minesweeper examples, I started to get the idea. I wrote some mathematical statements describing the generation of bombs and how to get their x,y position from an appropriate number. For those non-mathy people, don’t leave just yet; there will be code equivalents to the math.</p> <p>W and H are the width and height of the board respectively.</p> <p><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn mathvariant="italic">0</mn><mo>≤</mo><mi>r</mi><mo>≤</mo><mtext>W</mtext><mo>×</mo><mtext>H</mtext></mrow><annotation encoding="application/x-tex"> \it 0 \leq r \leq \text W \times \text H </annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8193em;vertical-align:-0.13597em;"></span><span class="mord"><span class="mord mathit">0</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">≤</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mord mathit">r</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">≤</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mord text"><span class="mord">W</span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">×</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mord text"><span class="mord">H</span></span></span></span></span></span></span> <span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>x</mi><mo>=</mo><mi>r</mi><mtext> </mtext><mo lspace="0.22em" rspace="0.22em"><mrow><mi mathvariant="normal">m</mi><mi mathvariant="normal">o</mi><mi mathvariant="normal">d</mi></mrow></mo><mtext> </mtext><mtext>W</mtext></mrow><annotation encoding="application/x-tex"> \it x = r \bmod \text W </annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.69444em;vertical-align:0em;"></span><span class="mord"><span class="mord mathit">x</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mord mathit">r</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mspace" style="margin-right:0.05555555555555555em;"></span><span class="mbin"><span class="mord"><span class="mord mathrm">m</span><span class="mord mathrm">o</span><span class="mord mathrm">d</span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mspace" style="margin-right:0.05555555555555555em;"></span><span class="mord text"><span class="mord">W</span></span></span></span></span></span></span> <span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>y</mi><mo>=</mo><mrow><mo fence="true">⌊</mo><mfrac><mi>r</mi><mtext>H</mtext></mfrac><mo fence="true">⌋</mo></mrow></mrow><annotation encoding="application/x-tex"> \it y = \left\lfloor\frac{r}{\text H}\right\rfloor </annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.8359999999999999em;vertical-align:-0.686em;"></span><span class="mord"><span class="mord mathit">y</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="minner"><span class="mopen delimcenter" style="top:0em;"><span class="delimsizing size2">⌊</span></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.10756em;"><span style="top:-2.314em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord text"><span class="mord">H</span></span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.677em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord mathit">r</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.686em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mclose delimcenter" style="top:0em;"><span class="delimsizing size2">⌋</span></span></span></span></span></span></span></span></p> <p>The code equivalent to this in Python is below:</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">random</span> <span class="c1"># r &lt;= 0 &lt;= W*H </span><span class="n">r</span> <span class="o">=</span> <span class="n">random</span><span class="p">.</span><span class="n">randint</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">W</span><span class="o">*</span><span class="n">H</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span> <span class="c1"># x = r mod W </span><span class="n">x</span> <span class="o">=</span> <span class="n">r</span> <span class="o">%</span> <span class="n">W</span> <span class="c1"># y = floor(r/H); note the special syntax python has for this operation </span><span class="n">y</span> <span class="o">=</span> <span class="n">r</span> <span class="o">//</span> <span class="n">H</span> </code></pre></div></div> <p>So that’s that, we can put this in a big ‘ol for loop and generate an arbitrary <em>n</em> number of bombs given a width and height of a Minesweeper board.</p> <h2 id="cascading-tile-revealing">Cascading Tile Revealing</h2> <p>This one is hard to describe; I am adapting this from <a href="https://leetcode.com/problems/minesweeper/">leetcode.com</a>. Whenever a player clicks a tile, the following logic should be used:</p> <ol> <li>If a mine is revealed, the game is over. (obviously)</li> <li>If a tile with <em>no</em> adjacent mines is revealed, recursively reveal all eight adjacent tiles.</li> <li>If a tile with one or more adjacent mines is revealed, display the number of mines next to it.</li> </ol> <p>Here is the code in Python for this algorithm.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">reveal_square</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">board</span><span class="p">,</span> <span class="n">alread_revealed</span><span class="p">):</span> <span class="c1"># if already checked </span> <span class="k">if</span> <span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span> <span class="ow">in</span> <span class="n">already_revealed</span><span class="p">:</span> <span class="k">return</span> <span class="c1"># if it's a bomb </span> <span class="k">if</span> <span class="n">board</span><span class="p">[</span><span class="n">x</span><span class="p">][</span><span class="n">y</span><span class="p">]</span> <span class="o">==</span> <span class="s">'B'</span><span class="p">:</span> <span class="n">you_lose</span><span class="p">()</span> <span class="k">return</span> <span class="c1"># if the bomb number is more than 0 </span> <span class="n">already_revealed</span><span class="p">.</span><span class="n">append</span><span class="p">((</span><span class="n">nx</span><span class="p">,</span> <span class="n">ny</span><span class="p">))</span> <span class="c1"># from -1 to 1 </span> <span class="k">for</span> <span class="n">xd</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">):</span> <span class="k">for</span> <span class="n">yd</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">):</span> <span class="c1"># skip if it is this the center tile </span> <span class="k">if</span> <span class="n">x</span><span class="o">+</span><span class="n">xd</span> <span class="o">==</span> <span class="n">x</span> <span class="ow">and</span> <span class="n">y</span><span class="o">+</span><span class="n">yd</span> <span class="o">==</span> <span class="n">y</span><span class="p">:</span> <span class="k">continue</span> <span class="c1"># recursively check the adjacent square </span> <span class="n">reveal</span><span class="p">(</span><span class="n">x</span><span class="o">+</span><span class="n">xd</span><span class="p">,</span> <span class="n">y</span><span class="o">+</span><span class="n">yd</span><span class="p">,</span> <span class="n">board</span><span class="p">,</span> <span class="n">already_revealed</span><span class="p">)</span> <span class="k">return</span> <span class="n">already_revealed</span> </code></pre></div></div> <p>This has no checks for valid squares, but it’s the general idea. This function returns an array of tile coordinates which should be revealed.</p> <h2 id="conclusion">Conclusion</h2> <p>I wrote this because in the first place because I was writing my own Minesweeper game. I hope that this helps you with getting the general idea of a Minesweeper game. The completed version of this game is available on my <a href="https://lamegames.tait.tech/">lamegames</a> site. Let me know what you think!</p> <p>Happy hacking!</p>When I was creating a little Minesweeper game, I got confused at some points. My bomb generation didn’t look quite right, and I for sure didn’t quite get the whole cascading tile reveal thing. With a bit of internet research, I found what I was looking for. I’ll explain it all in one place for my own research purposes.lamegames.tait.tech2020-09-09T00:00:00+00:002020-09-09T00:00:00+00:00/2020/09/09/lamegames<p>This is an announcement for a new project of mine: <a href="https://lamegames.tait.tech">lamegames.tait.tech</a>.</p> <p>This is something I’m really excited to work on!</p> <p>Right now, I’ve just got a rock-paper-scissors game. A chat function, and a few simple card games to come.</p> <p>Check out the repository on my <a href="https://github.com/TTWNO/lamegames.io">Github</a>.</p>This is an announcement for a new project of mine: lamegames.tait.tech.How to Solve The Django Deployment Puzzle2020-08-18T00:00:00+00:002020-08-18T00:00:00+00:00/2020/08/18/django-deployment<p>A few days ago I had a Django project I wanted to put on a real server. This project is still in its infancy, but I thought it would be nice to put it on my resume and show my friends. Little did I know the headache coming my way. Here are some tips to help you not make the same mistakes as me.</p> <h3 id="asgi-servers">ASGI Servers</h3> <p>Because my project used the ASGI (Asynchronous webServer Gateway Interface), I needed to find a good production ASGI server to handle all the incoming requests. The best thing I found was <a href="http://www.uvicorn.org/">uvicorn</a>. It focuses on speed, which is a priority, especially when using the ASGI protocol.</p> <p>To run uvicorn on the command line for testing purposes, use something like the following:</p> <pre class="terminal"> $ uvicorn --reload myapp.asgi:application </pre> <p>The <code class="highlighter-rouge">--reload</code> option says to reload the server if any of the files get updated. This is not recommended in production. Sadly, I thought this meant I would need to do a hard shutdown of the server process every time I wanted to update. This turned out to not be the case.</p> <h3 id="workload-managers">Workload Managers</h3> <p>There is another equine-named program called <a href="https://gunicorn.org/">gunicorn</a> which can hold a number of processes under its control. An interesting feature of <code class="highlighter-rouge">gunicorn</code> is that it will gracefully switch from an old to a new deployment, replacing the subprocesses one-by-one and eventually having only the new deployment active on all subprocesses. The greatest part? Zero down time. The server keeps any old processes open if there is communication with them, then shift and new connections to the new deployment. This was a very cool feature I wanted to take advantage of.</p> <p>“Now hold on!” you might protest. “gunicorn is a WSGI server!” … oh you got me there! Yes, that’s right, <code class="highlighter-rouge">gunicorn</code> is paired with <code class="highlighter-rouge">uvicorn</code> to serve my files.</p> <h3 id="systemd">systemd</h3> <p>Love it or hate it, the majority of Linux distributions use the <code class="highlighter-rouge">systemd</code> init system. I decided it would be very convenient to have a .service file for my Django application to run automatically at boot. <code class="highlighter-rouge">Systemd</code> allows me to do this with a file like the following one I stored in <code class="highlighter-rouge">/lib/systemd/system/lamegames.service</code>.</p> <pre class="file"> [Unit] Description=Gunicorn/Uvicorn (lamegames.io) [Service] WorkingDirectory=/home/lame/lamegames.io Type=simple RemainAfterExit=yes ExecStart=/home/lame/lamegames.io/env/bin/gunicorn lamegames.asgi:application -w 2 -k uvicorn.workers.UvicornWorker ExecStop=/bin/kill -HUP $MAINPID Restart=always [Install] WantedBy=multi-user.target </pre> <h3 id="nginx">nginx</h3> <p>NGINX (pronounced engine-X) is a performance web server designed for speed and simplicity. For the front facing side of the site, I do need a production web server like nginx. Gunicorn simply doesn’t need all the features that nginx provides, but I do. To configure my nginx installation, I used the following few directives to:</p> <ol> <li>Redirect most traffic towards the gunicorn server.</li> <li>Redirect statically served files (CSS, JS, images) to the directory specified in the STATIC_ROOT variable of my <code class="highlighter-rouge">settings.py</code> file.</li> <li>Use TLS to enable https://</li> </ol> <p>Serving the static files from nginx as opposed to the <code class="highlighter-rouge">gunicorn</code> server is necessary. Gunicorn and other production A/WSGI web server will not set the proper MIME type over TLS. This will cause your browser to not load the Javascript/CSS.</p> <p>This is the important part of my nginx config.</p> <pre class="file"> server { location / { proxy_set_header Host $http_host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # these two lines ensure that WebSocket, and HTTP2 connection are forwarded correctly proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "Upgrade"; proxy_redirect off; proxy_buffering off; # this forwards all traffic to the local server on port 8000 proxy_pass http://localhost:8000; } # This forwards all static requests to Django's STATIC_ROOT set in settings.py; it is generated using the collectstatic command. location /static { autoindex on; alias /home/lame/lamegames.io/static_generated; } } </pre> <h3 id="setup">Setup</h3> <p>After all that, I was able to do the following:</p> <pre class="terminal"> # systemctl enable lamegames </pre> <p>This enabled my <code class="highlighter-rouge">gunicorn</code> server to run once the server started. NGINX is that way be default.</p> <p>And tada! You now have a working Django project on a production server!</p> <h4 id="notes">Notes</h4> <ul> <li>If using ws:// websockets, change them to wss:// for secure web sockets.</li> <li>Make sure to use channels.routing.get_default_application() instead of django.get_asgi_application() if your’re wanting to use channels/redis WebSockets.</li> </ul>A few days ago I had a Django project I wanted to put on a real server. This project is still in its infancy, but I thought it would be nice to put it on my resume and show my friends. Little did I know the headache coming my way. Here are some tips to help you not make the same mistakes as me.BSD Journey, Part 12020-08-15T00:00:00+00:002020-08-15T00:00:00+00:00/2020/08/15/openbsd1<p>As Linux becomes controlled by corporate sponsors and becomes more full of proprietary blobs, drivers, and even closed-source software like Steam, One may wonder if there are other options out there. For me, somebody that is intensely interested in security, there is one option: OpenBSD.</p> <p>Now, my interest in OpenBSD has been going on for a long time. I started poking around for Linux alternatives way back a few years ago when Linus Torvalds decided to leave after he got in trouble for some <a href="https://arstechnica.com/information-technology/2013/07/linus-torvalds-defends-his-right-to-shame-linux-kernel-developers/">unprofessional behaviour</a>. That said, Linus did come back to Linux development, but I knew that his abrasive style is what brought good code to the Linux kernel. I also knew that his ability to be critical would be hurt by the new <a href="https://itsfoss.com/linux-code-of-conduct/">code of conduct</a>. It would become a tool for the SJW types to hammer on Linus for being a “white male, et al.”; It would become a tool for the easily offended to use to get their dumb code into Linux; It would become a tool for the corporatization, the HR-ification of Linux. Frankly, this does not interest me.</p> <p>Now I’m sure that OpenBSD has its own internal policies that I disagree with. That said, Theo De Raadt is still at least known for calling Firefox an “amorphous peace of garbage” due to its lack of privilege separation. And, in their <a href="https://openbsd.org/goals.html">project goals</a> page, they specifically mention:</p> <blockquote> <p>Be as politics-free as possible; solutions should be decided on the basis of technical merit.</p> </blockquote> <p>Now that’s something I can get behind! Bet you that’s not in the Linux COC?</p> <p>He also went to university in my hometown, so that’s pretty cool! I can support a local madman who thinks he can make a better operating system than all those corporations. Maybe he was right, maybe not. What I know is I am excited to find out!</p> <p>Wish my luck on my OpenBSD journey. I will post updates here along the way.</p> <p>Happy hacking!</p>As Linux becomes controlled by corporate sponsors and becomes more full of proprietary blobs, drivers, and even closed-source software like Steam, One may wonder if there are other options out there. For me, somebody that is intensely interested in security, there is one option: OpenBSD.Know How Your Representative Votes In Parliament2020-07-30T00:00:00+00:002020-07-30T00:00:00+00:00/2020/07/30/canadian-parliament<p>As an advocate for openness, I had an idea to make a project out of the government of Canada’s <a href="https://open.canada.ca/en/open-data">Open Data</a> initiative to take a look at how my local MP voted on various pieces of legislation. It turns out though that this was not necessary due to how easy it was to find this information on the government’s own website. In this article, I will explain how you can do the same.</p> <h3 id="1-find-your-representative">1. Find Your Representative</h3> <p>The first step in this process is to find who your representative is. To do so, go to the government’s own website <a href="https://www.ourcommons.ca/Members/en">ourcommons.ca’s search tool</a>.</p> <p>Simply type in your postal code in the search box to find out who your MP is.</p> <h3 id="2-their-voting-record">2. Their Voting Record</h3> <p>Every MP’s voting record is public knowledge, and it is available nice and simple in a table on that MP’s page. For example, this is a link to <a href="https://www.ourcommons.ca/Members/en/pierre-poilievre(25524)/votes">Pierre Poilievre’s voting record</a>.</p> <p>To find your MP’s voting record, do step one, then: After the <strong>Overview</strong>, and <strong>Seat in The House</strong> sections, there are three tabs, <strong>Roles</strong>, <strong>Work</strong>, and <strong>Contact</strong>. Click on work. At the bottom of that tab is a link which says <strong>Chamber Votes</strong>. This will open a small window with some recent votes by this politician. If you want to see all their votes, there is a button at the bottom named <strong>All Votes by This Member</strong>.</p> <p>Tada! You can now keep your local MP accountable for anything you do or do not support.</p> <h3 id="3-bill-details">3. Bill Details</h3> <p>If you want to get into the nitty gritty, once you open a specific bill, you can actually find out the status of said bill, or read the actual text by clicking the <strong>View this Bill on LEGISinfo</strong> button.</p> <p>Both the status of the bill, and a link to a PDF document containing the bilingual text of the bill are visible in the main body of the page.</p> <h4 id="conclusion">Conclusion</h4> <p>I thought this was pretty cool! It was <em>way</em> simpler than I thought it would be.</p> <p>Thanks, Canada!</p>As an advocate for openness, I had an idea to make a project out of the government of Canada’s Open Data initiative to take a look at how my local MP voted on various pieces of legislation. It turns out though that this was not necessary due to how easy it was to find this information on the government’s own website. In this article, I will explain how you can do the same.Installing MultiCraft on Gentoo Linux2020-07-19T00:00:00+00:002020-07-19T00:00:00+00:00/2020/07/19/multicraft-php-gentoo<p>In a very odd combination of requirements, I needed to install <a href="https://multicraft.org">MultiCraft</a> on a Gentoo Linux system. The PHP <code class="highlighter-rouge">USE</code> flags are important so you don’t have to recompile it three times like I did.</p> <p>Here are some useful tips I came across:</p> <h3 id="php-use-flags">PHP <code class="highlighter-rouge">USE</code> flags</h3> <p>In <code class="highlighter-rouge">/etc/portage/package.use/php</code> I placed the following line:</p> <pre class="terminal"> dev-lang/php cgi mysql mysqli fpm pdo gd truetype </pre> <p>This should give you enough for a mysql backended MultiCraft installation. The <code class="highlighter-rouge">cgi</code> option may not be required as <code class="highlighter-rouge">fpm</code> stands for <em>FastCGI Process Managment</em>. I don’t know for sure though.</p> <h3 id="paper">Paper</h3> <p>This will grab the latest version of the Paper jar file using <a href="https://yivesmirror.com">YivesMirror</a>. I’m not sure how reputable it is, but my buddy who works with this stuff more often than me seemed to recognize it.</p> <pre class="terminal"> ## See the default craftbukkit.jar.conf for a detailed documentation of the ## format of this file. [config] name = Paper 1.16.1 Latest source = https://yivesmirror.com/files/paper/Paper-1.16.1-latest.jar category = Mods [encoding] #encode = system #decode = system #fileEncoding = latin-1 [start] command = "{JAVA}" -Xmx{MAX_MEMORY}M -Xms{START_MEMORY}M -XX:MaxPermSize=128M -Djline.terminal=jline.UnsupportedTerminal -jar "{JAR}" nogui </pre> <h3 id="other-tips">Other Tips</h3> <p>Do not use the option to setup a separate user for each server. This completely stalled any work getting done with a ton of ‘permission denied’ errors.</p> <h4 id="security">Security</h4> <p>If the panel is in the root directory of your NGINX web server, use the following in your server block to deny access to the <code class="highlighter-rouge">/protected</code> directory.</p> <pre class="terminal"> location /protected { deny all; return 404; } </pre> <h5 id="mysql">MySQL</h5> <p>It is always good practice to separate privileges. The MultiCraft daemon should have one SQL login, with one database allocated to it. The MultiCraft panel should have a separate SQL login, with a separate database allocated to it.</p> <p>You can do this with the following commands in your MySQL prompt:</p> <pre class="terminal"> sql&gt; CREATE DATABASE multicraft_daemon_database; Query OK, 0 rows affected (0.01 sec) sql&gt; CREATE DATABASE multicraft_panel_database; Query OK, 0 rows affected (0.01 sec) sql&gt; CREATE USER 'muilticraft_daemon'@'localhost' IDENTIFIED BY 'strong password here'; Query OK, 0 rows affected (0.01 sec) sql&gt; CREATE USER 'multicraft_panel'@'localhost' IDENTIFIED BY 'different strong password here'; Query OK, 0 rows affected (0.01 sec) sql&gt; GRANT ALL PRIVILEGES ON multicraft_daemon_database . * TO 'multicraft_daemon'@'localhost'; Query OK, 0 rows affected (0.01 sec) sql&gt; GRANT ALL PRIVILEGES ON multicraft_panel_database . * TO 'mutlicraft_panel'@'localhost'; Query OK, 0 rows affected (0.01 sec) </pre> <p>During setup, make sure the proper credentials are used for each step. Database 1 is the panel database. Database 2 is the daemon database.</p> <p>Happy hacking :)</p>In a very odd combination of requirements, I needed to install MultiCraft on a Gentoo Linux system. The PHP USE flags are important so you don’t have to recompile it three times like I did.Independence2020-07-12T00:00:00+00:002020-07-12T00:00:00+00:00/2020/07/12/independence<blockquote> <p>“When given a choice between independence and dependence, always choose independence; you will never regret that choice!”—Luke Smith</p> </blockquote> <p>Whatever you may believe about the YouTube personality Luke Smith, the quote above summarizes a core principle of mine. Much like many people have religious principles, I have <em>Independence</em>.</p> <p>My choice to use Linux as my primary operating system, host my own website, own my own domain name—all of these are directly related to this core principle of independence.</p> <p>I never want a man, or a company to have too much power over my life. Just like I would not trust just any person to be able to read my emails, know where I live, where I am going, who are my friends, what do I believe; in the same way, I do not trust a company with that same information.</p> <blockquote> <p>“If you want to find out what a man is to the bottom, give him power. Any man can stand adversity — only a great man can stand prosperity.”—Robert Ingersoll</p> </blockquote> <p>Take control of your own digital life:</p> <ol> <li>Own your own domain.</li> <li>Hookup an email and a website to that.</li> </ol> <p>That’s it!</p> <p>Without this, any of your internet privileges can be revoked at any time by Google, Facebook, YouTube, Twitter, or even an angry Twitter Mob. Maybe because they hate your skin colour, maybe they hate your religious/political views, or maybe you got caught on a technicality.</p> <p>If you own your own domain, however:</p> <p>Your email provider goes down/bans you: change your provider; keep the email.</p> <p>Your website is pulled for controversial views: switch hosts.</p> <p>Protect yourself; give yourself choices. Why give others that power when you could have it for yourself?</p>“When given a choice between independence and dependence, always choose independence; you will never regret that choice!”—Luke SmithHow to use tmux to send and receive things from your Minecraft server2020-06-25T00:00:00+00:002020-06-25T00:00:00+00:00/2020/06/25/tmux-minecraft<p>So recently I had problem. I run a Minecraft server on a big Linux computer I have running in my room. Now, as a system administrator it is very helpful to be able to run some simple commands without needing to login with my key, password, TFA, etc. It is, frankly, a lot of work. Especially when I really just want to be playing games but I just need to check something quickly.</p> <p>So for simple things like finding out of the network, CPU, memory or disk usage is my bottleneck, I wrote this really nifty script to connect the world of Minecraft and the Linux shell.</p> <p>My completed solution for what I needed can be found at <a href="https://github.com/TTWNO/termcraft/">https://github.com/TTWNO/termcraft</a>.</p> <p>If you want some of the implementation details, stick around.</p> <h2 id="solution">Solution</h2> <p>So to solve this interesting problem, I decided to use <code class="highlighter-rouge">tmux</code>. <code class="highlighter-rouge">tmux</code> is a <strong>t</strong>terminal <strong>mu</strong>ltiple<strong>x</strong>er. This allows you to run a terminal session, then detach fromc it while it still runs in the background.</p> <p>This is very valuable when running command line applications that need to have an active console connection, like a Minecraft server.</p> <p>So first I looked at the <code class="highlighter-rouge">tmux</code> command <code class="highlighter-rouge">send-keys</code>.</p> <h4 id="send-keys"><code class="highlighter-rouge">send-keys</code></h4> <p><code class="highlighter-rouge">send-keys</code> allows you to send text, and key presses to a <code class="highlighter-rouge">tmux</code> session. Now assuming this <code class="highlighter-rouge">tmux</code> session is attached to a Minecraft server, there is no reason you could not run a command like this:</p> <pre class="terminal"> $ tmux send-keys "tell @a This is a Test" Enter </pre> <p>This will send the text “tell @a This is a Test” to the Minecraft server. Then, it will hit the newline character, this will execute the command.</p> <p>So now we can send information to the server and have it tell the users something.</p> <p>But how do we get information about who is typing what in the Minecraft chat?</p> <h3 id="tmuxs-capture-pane-is-painful"><code class="highlighter-rouge">tmux</code>’s <code class="highlighter-rouge">capture-pane</code> is painful</h3> <p>So in the manual page for <code class="highlighter-rouge">tmux</code> I can see a section recorded below for options I can give to the <code class="highlighter-rouge">capture-pane</code> subcommand.</p> <pre class="terminal"> -S and -E specify the starting and ending line numbers, zero is the first line of the visible pane and negative numbers are lines in the history. ‘-’ to -S is the start of the history and to -E the end of the visible pane. The default is to capture only the visible contents of the pane. </pre> <p>What it seems to be saying is I can start at line <code class="highlighter-rouge">-S n</code> and end at line <code class="highlighter-rouge">-E n</code>. Negative numbers start from the bottom, so <em>in theory</em> I can do the following: <code class="highlighter-rouge">tmux capture-pane -S -1</code> should capture only the last line, because I’m starting from the last line. Right?</p> <p>No. It just doesn’t work. Negative numbers do <em>not</em> work with the <code class="highlighter-rouge">tmux capture-pane</code> subcommand.</p> <p>So I did some simple UNIX piping, like so, to get just the last thing in the chat.</p> <pre class="terminal"> $ tmux capture-pane -p -t steve | tail -n1 [SERVER] [ExtraDebuggingInfoHere]: &lt;TaterTheTot&gt; MY_MESSAGE </pre> <p>TaterTheTot is my Minecraft username :)</p> <p><code class="highlighter-rouge">-p</code> prints the result to the terminal/stdout.</p> <p><code class="highlighter-rouge">steve</code> is the name of the tmux session I’m trying to pull form.</p> <p>So that’s done! Beauty!</p> <p>Now that we have that, how can we extract the username and the message from the latest line?</p> <h3 id="grep"><code class="highlighter-rouge">grep</code></h3> <p><code class="highlighter-rouge">grep</code> is a command to find patterns of text. <code class="highlighter-rouge">grep</code> has an option to only show a matching pattern of text. This option is <code class="highlighter-rouge">-o</code>.</p> <p>Let’s see how we can use this in conjunction with our latest line of server output to get our results.</p> <pre class="terminal"> $ echo "[DEBUG] [SERVER] blah blah: &lt;TaterTheTot&gt; MY_MESAGE" | grep -o "&lt;.&ast;&gt;" &lt;TaterTheTot&gt; </pre> <p>Now, that’s my name with the &lt; and &gt; attached. Not bad! We can use the <code class="highlighter-rouge">sed</code> command to clean it up a bit.</p> <p>The syntax is like so: <code class="highlighter-rouge">select/somepattern/replacewith/global</code></p> <p>So the following command is: <code class="highlighter-rouge">s/[&lt;&gt;]//g</code></p> <p>Select any characters that are either &lt; or &gt;. Replace with nothing. Do so globally (as in, don’t stop after you replace only one character).</p> <p>Take two!</p> <pre class="terminal"> $ echo "[DEBUG] [SERVER] blah blah: &lt;TaterTheTot&gt; MY_MESAGE" | grep -o "&lt;.&ast;&gt;" | sed 's/[&lt;&gt;]//g' TaterTheTot </pre> <p>Beautiful!</p> <p>Now what about that pesky message?</p> <h3 id="more-grep-more-sed">more <code class="highlighter-rouge">grep</code>; more <code class="highlighter-rouge">sed</code></h3> <p>Simple: capture everything after the &gt;. Leaving the user’s message entirely in tact.</p> <pre class="terminal"> $ echo "[DEBUG] [SERVER] blah blah: &lt;TaterTheTot&gt; MY_MESAGE" | grep -o "&gt;.&ast;$" | sed 's/&gt; //' MY_MESSAGE </pre> <p>So now we have a way to get the username of someone typing in the Minecraft server chat. We have a way to find out what they said. And, we have a way to respond.</p> <p>You can imagine how these might go together for your own use case.</p> <h3 id="conclusion">Conclusion</h3> <p>This shows some pretty fun stuff you can do with a few simple Linux commands and a Minecraft server.</p> <p>I hope you learned something and found my explanations not horrific haha!</p> <p>Remember to checkout the git repository to see what I did with it: <a href="https://github.com/TTWNO/termcraft">https://github.com/TTWNO/termcraft</a>.</p> <p>Happy hacking!</p>So recently I had problem. I run a Minecraft server on a big Linux computer I have running in my room. Now, as a system administrator it is very helpful to be able to run some simple commands without needing to login with my key, password, TFA, etc. It is, frankly, a lot of work. Especially when I really just want to be playing games but I just need to check something quickly.Site Update2020-06-04T00:00:00+00:002020-06-04T00:00:00+00:00/2020/06/04/site-update<p>I updated the site with some easier to identify information about me and my projects :)</p> <p>Also, Clue has been delayed due to my partner in crime on the project wokring too many hours.</p> <p>I also posted a new project called <em><a href="https://github.com/TTWNO/caesar-cipher">Caesar Cipher</a></em> in C. It will be an intermediate example of how to use build systems like <code class="highlighter-rouge">make</code>.</p>I updated the site with some easier to identify information about me and my projects :)New Game: Clue (coming soon)2020-05-19T00:00:00+00:002020-05-19T00:00:00+00:00/2020/05/19/clue-announcement<p>Ooo! Exciting! Today I want to announce a new project I’ll be working on which should be live within the month of May: Clue.</p> <p>The original board game, implemented in an accessible format via the web.</p> <p>It uses a Node.js backend and standard Javascript/HTML frontend. Nothing fancy.</p> <p>All the code will be hosted here: <a href="https://github.com/TTWNO/clue">https://github.com/TTWNO/clue</a></p> <p>It will be licensed under the BSD-3 license, meaning it can be used for any reason—even commercially and without source-code disclosure—without prior authorization, but it <em>must</em> acknowledge that I helped build the end product.</p> <p>Once the project is live, it will be located at: <a href="">Lame Games</a> (currently a dead link).</p>Ooo! Exciting! Today I want to announce a new project I’ll be working on which should be live within the month of May: Clue. \ No newline at end of file +Jekyll2020-09-27T16:19:31+00:00/feed.xmlMinesweeper Bomb Generation And Tile Revealing2020-09-12T00:00:00+00:002020-09-12T00:00:00+00:00/2020/09/12/minesweeper<p>When I was creating a little Minesweeper game, I got confused at some points. My bomb generation didn’t look quite right, and I for sure didn’t quite get the whole cascading tile reveal thing. With a bit of internet research, I found what I was looking for. I’ll explain it all in one place for my own research purposes.</p> <h2 id="bomb-generation">Bomb Generation</h2> <p>When I started this project I attempted to use a random bomb generator. By this I mean on each square, before it gets generated, give it a one in 15 change of being a bomb. Personally, I’m not sure why this never looked right. Something about the layout of the bombs did not mimic the classic Minesweeper game.</p> <p>After looking at some open source Minesweeper examples, I started to get the idea. I wrote some mathematical statements describing the generation of bombs and how to get their x,y position from an appropriate number. For those non-mathy people, don’t leave just yet; there will be code equivalents to the math.</p> <p>W and H are the width and height of the board respectively.</p> <p><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn mathvariant="italic">0</mn><mo>≤</mo><mi>r</mi><mo>≤</mo><mtext>W</mtext><mo>×</mo><mtext>H</mtext></mrow><annotation encoding="application/x-tex"> \it 0 \leq r \leq \text W \times \text H </annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8193em;vertical-align:-0.13597em;"></span><span class="mord"><span class="mord mathit">0</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">≤</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mord mathit">r</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">≤</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mord text"><span class="mord">W</span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">×</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mord text"><span class="mord">H</span></span></span></span></span></span></span> <span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>x</mi><mo>=</mo><mi>r</mi><mtext> </mtext><mo lspace="0.22em" rspace="0.22em"><mrow><mi mathvariant="normal">m</mi><mi mathvariant="normal">o</mi><mi mathvariant="normal">d</mi></mrow></mo><mtext> </mtext><mtext>W</mtext></mrow><annotation encoding="application/x-tex"> \it x = r \bmod \text W </annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.69444em;vertical-align:0em;"></span><span class="mord"><span class="mord mathit">x</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mord mathit">r</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mspace" style="margin-right:0.05555555555555555em;"></span><span class="mbin"><span class="mord"><span class="mord mathrm">m</span><span class="mord mathrm">o</span><span class="mord mathrm">d</span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mspace" style="margin-right:0.05555555555555555em;"></span><span class="mord text"><span class="mord">W</span></span></span></span></span></span></span> <span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>y</mi><mo>=</mo><mrow><mo fence="true">⌊</mo><mfrac><mi>r</mi><mtext>H</mtext></mfrac><mo fence="true">⌋</mo></mrow></mrow><annotation encoding="application/x-tex"> \it y = \left\lfloor\frac{r}{\text H}\right\rfloor </annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.8359999999999999em;vertical-align:-0.686em;"></span><span class="mord"><span class="mord mathit">y</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="minner"><span class="mopen delimcenter" style="top:0em;"><span class="delimsizing size2">⌊</span></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.10756em;"><span style="top:-2.314em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord text"><span class="mord">H</span></span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.677em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord mathit">r</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.686em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mclose delimcenter" style="top:0em;"><span class="delimsizing size2">⌋</span></span></span></span></span></span></span></span></p> <p>The code equivalent to this in Python is below:</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">random</span> <span class="c1"># r &lt;= 0 &lt;= W*H </span><span class="n">r</span> <span class="o">=</span> <span class="n">random</span><span class="p">.</span><span class="n">randint</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">W</span><span class="o">*</span><span class="n">H</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span> <span class="c1"># x = r mod W </span><span class="n">x</span> <span class="o">=</span> <span class="n">r</span> <span class="o">%</span> <span class="n">W</span> <span class="c1"># y = floor(r/H); note the special syntax python has for this operation </span><span class="n">y</span> <span class="o">=</span> <span class="n">r</span> <span class="o">//</span> <span class="n">H</span> </code></pre></div></div> <p>So that’s that, we can put this in a big ‘ol for loop and generate an arbitrary <em>n</em> number of bombs given a width and height of a Minesweeper board.</p> <h2 id="cascading-tile-revealing">Cascading Tile Revealing</h2> <p>This one is hard to describe; I am adapting this from <a href="https://leetcode.com/problems/minesweeper/">leetcode.com</a>. Whenever a player clicks a tile, the following logic should be used:</p> <ol> <li>If a mine is revealed, the game is over. (obviously)</li> <li>If a tile with <em>no</em> adjacent mines is revealed, recursively reveal all eight adjacent tiles.</li> <li>If a tile with one or more adjacent mines is revealed, display the number of mines next to it.</li> </ol> <p>Here is the code in Python for this algorithm.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">reveal_square</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">board</span><span class="p">,</span> <span class="n">alread_revealed</span><span class="p">):</span> <span class="c1"># if already checked </span> <span class="k">if</span> <span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span> <span class="ow">in</span> <span class="n">already_revealed</span><span class="p">:</span> <span class="k">return</span> <span class="c1"># if it's a bomb </span> <span class="k">if</span> <span class="n">board</span><span class="p">[</span><span class="n">x</span><span class="p">][</span><span class="n">y</span><span class="p">]</span> <span class="o">==</span> <span class="s">'B'</span><span class="p">:</span> <span class="n">you_lose</span><span class="p">()</span> <span class="k">return</span> <span class="c1"># if the bomb number is more than 0 </span> <span class="n">already_revealed</span><span class="p">.</span><span class="n">append</span><span class="p">((</span><span class="n">nx</span><span class="p">,</span> <span class="n">ny</span><span class="p">))</span> <span class="c1"># from -1 to 1 </span> <span class="k">for</span> <span class="n">xd</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">):</span> <span class="k">for</span> <span class="n">yd</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">):</span> <span class="c1"># skip if it is this the center tile </span> <span class="k">if</span> <span class="n">x</span><span class="o">+</span><span class="n">xd</span> <span class="o">==</span> <span class="n">x</span> <span class="ow">and</span> <span class="n">y</span><span class="o">+</span><span class="n">yd</span> <span class="o">==</span> <span class="n">y</span><span class="p">:</span> <span class="k">continue</span> <span class="c1"># recursively check the adjacent square </span> <span class="n">reveal</span><span class="p">(</span><span class="n">x</span><span class="o">+</span><span class="n">xd</span><span class="p">,</span> <span class="n">y</span><span class="o">+</span><span class="n">yd</span><span class="p">,</span> <span class="n">board</span><span class="p">,</span> <span class="n">already_revealed</span><span class="p">)</span> <span class="k">return</span> <span class="n">already_revealed</span> </code></pre></div></div> <p>This has no checks for valid squares, but it’s the general idea. This function returns an array of tile coordinates which should be revealed.</p> <h2 id="conclusion">Conclusion</h2> <p>I wrote this because in the first place because I was writing my own Minesweeper game. I hope that this helps you with getting the general idea of a Minesweeper game. The completed version of this game is available on my <a href="https://lamegames.tait.tech/">lamegames</a> site. Let me know what you think!</p> <p>Happy hacking!</p>When I was creating a little Minesweeper game, I got confused at some points. My bomb generation didn’t look quite right, and I for sure didn’t quite get the whole cascading tile reveal thing. With a bit of internet research, I found what I was looking for. I’ll explain it all in one place for my own research purposes.lamegames.tait.tech2020-09-09T00:00:00+00:002020-09-09T00:00:00+00:00/2020/09/09/lamegames<p>This is an announcement for a new project of mine: <a href="https://lamegames.tait.tech">lamegames.tait.tech</a>.</p> <p>This is something I’m really excited to work on!</p> <p>Right now, I’ve just got a rock-paper-scissors game. A chat function, and a few simple card games to come.</p> <p>Check out the repository on my <a href="https://github.com/TTWNO/lamegames.io">Github</a>.</p>This is an announcement for a new project of mine: lamegames.tait.tech.How to Solve The Django Deployment Puzzle2020-08-18T00:00:00+00:002020-08-18T00:00:00+00:00/2020/08/18/django-deployment<p>A few days ago I had a Django project I wanted to put on a real server. This project is still in its infancy, but I thought it would be nice to put it on my resume and show my friends. Little did I know the headache coming my way. Here are some tips to help you not make the same mistakes as me.</p> <h3 id="asgi-servers">ASGI Servers</h3> <p>Because my project used the ASGI (Asynchronous webServer Gateway Interface), I needed to find a good production ASGI server to handle all the incoming requests. The best thing I found was <a href="http://www.uvicorn.org/">uvicorn</a>. It focuses on speed, which is a priority, especially when using the ASGI protocol.</p> <p>To run uvicorn on the command line for testing purposes, use something like the following:</p> <pre class="terminal"> $ uvicorn --reload myapp.asgi:application </pre> <p>The <code class="language-plaintext highlighter-rouge">--reload</code> option says to reload the server if any of the files get updated. This is not recommended in production. Sadly, I thought this meant I would need to do a hard shutdown of the server process every time I wanted to update. This turned out to not be the case.</p> <h3 id="workload-managers">Workload Managers</h3> <p>There is another equine-named program called <a href="https://gunicorn.org/">gunicorn</a> which can hold a number of processes under its control. An interesting feature of <code class="language-plaintext highlighter-rouge">gunicorn</code> is that it will gracefully switch from an old to a new deployment, replacing the subprocesses one-by-one and eventually having only the new deployment active on all subprocesses. The greatest part? Zero down time. The server keeps any old processes open if there is communication with them, then shift and new connections to the new deployment. This was a very cool feature I wanted to take advantage of.</p> <p>“Now hold on!” you might protest. “gunicorn is a WSGI server!” … oh you got me there! Yes, that’s right, <code class="language-plaintext highlighter-rouge">gunicorn</code> is paired with <code class="language-plaintext highlighter-rouge">uvicorn</code> to serve my files.</p> <h3 id="systemd">systemd</h3> <p>Love it or hate it, the majority of Linux distributions use the <code class="language-plaintext highlighter-rouge">systemd</code> init system. I decided it would be very convenient to have a .service file for my Django application to run automatically at boot. <code class="language-plaintext highlighter-rouge">Systemd</code> allows me to do this with a file like the following one I stored in <code class="language-plaintext highlighter-rouge">/lib/systemd/system/lamegames.service</code>.</p> <pre class="file"> [Unit] Description=Gunicorn/Uvicorn (lamegames.io) [Service] WorkingDirectory=/home/lame/lamegames.io Type=simple RemainAfterExit=yes ExecStart=/home/lame/lamegames.io/env/bin/gunicorn lamegames.asgi:application -w 2 -k uvicorn.workers.UvicornWorker ExecStop=/bin/kill -HUP $MAINPID Restart=always [Install] WantedBy=multi-user.target </pre> <h3 id="nginx">nginx</h3> <p>NGINX (pronounced engine-X) is a performance web server designed for speed and simplicity. For the front facing side of the site, I do need a production web server like nginx. Gunicorn simply doesn’t need all the features that nginx provides, but I do. To configure my nginx installation, I used the following few directives to:</p> <ol> <li>Redirect most traffic towards the gunicorn server.</li> <li>Redirect statically served files (CSS, JS, images) to the directory specified in the STATIC_ROOT variable of my <code class="language-plaintext highlighter-rouge">settings.py</code> file.</li> <li>Use TLS to enable https://</li> </ol> <p>Serving the static files from nginx as opposed to the <code class="language-plaintext highlighter-rouge">gunicorn</code> server is necessary. Gunicorn and other production A/WSGI web server will not set the proper MIME type over TLS. This will cause your browser to not load the Javascript/CSS.</p> <p>This is the important part of my nginx config.</p> <pre class="file"> server { location / { proxy_set_header Host $http_host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # these two lines ensure that WebSocket, and HTTP2 connection are forwarded correctly proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "Upgrade"; proxy_redirect off; proxy_buffering off; # this forwards all traffic to the local server on port 8000 proxy_pass http://localhost:8000; } # This forwards all static requests to Django's STATIC_ROOT set in settings.py; it is generated using the collectstatic command. location /static { autoindex on; alias /home/lame/lamegames.io/static_generated; } } </pre> <h3 id="setup">Setup</h3> <p>After all that, I was able to do the following:</p> <pre class="terminal"> # systemctl enable lamegames </pre> <p>This enabled my <code class="language-plaintext highlighter-rouge">gunicorn</code> server to run once the server started. NGINX is that way be default.</p> <p>And tada! You now have a working Django project on a production server!</p> <h4 id="notes">Notes</h4> <ul> <li>If using ws:// websockets, change them to wss:// for secure web sockets.</li> <li>Make sure to use channels.routing.get_default_application() instead of django.get_asgi_application() if your’re wanting to use channels/redis WebSockets.</li> </ul>A few days ago I had a Django project I wanted to put on a real server. This project is still in its infancy, but I thought it would be nice to put it on my resume and show my friends. Little did I know the headache coming my way. Here are some tips to help you not make the same mistakes as me.BSD Journey, Part 12020-08-15T00:00:00+00:002020-08-15T00:00:00+00:00/2020/08/15/openbsd1<p>As Linux becomes controlled by corporate sponsors and becomes more full of proprietary blobs, drivers, and even closed-source software like Steam, One may wonder if there are other options out there. For me, somebody that is intensely interested in security, there is one option: OpenBSD.</p> <p>Now, my interest in OpenBSD has been going on for a long time. I started poking around for Linux alternatives way back a few years ago when Linus Torvalds decided to leave after he got in trouble for some <a href="https://arstechnica.com/information-technology/2013/07/linus-torvalds-defends-his-right-to-shame-linux-kernel-developers/">unprofessional behaviour</a>. That said, Linus did come back to Linux development, but I knew that his abrasive style is what brought good code to the Linux kernel. I also knew that his ability to be critical would be hurt by the new <a href="https://itsfoss.com/linux-code-of-conduct/">code of conduct</a>. It would become a tool for the SJW types to hammer on Linus for being a “white male, et al.”; It would become a tool for the easily offended to use to get their dumb code into Linux; It would become a tool for the corporatization, the HR-ification of Linux. Frankly, this does not interest me.</p> <p>Now I’m sure that OpenBSD has its own internal policies that I disagree with. That said, Theo De Raadt is still at least known for calling Firefox an “amorphous peace of garbage” due to its lack of privilege separation. And, in their <a href="https://openbsd.org/goals.html">project goals</a> page, they specifically mention:</p> <blockquote> <p>Be as politics-free as possible; solutions should be decided on the basis of technical merit.</p> </blockquote> <p>Now that’s something I can get behind! Bet you that’s not in the Linux COC?</p> <p>He also went to university in my hometown, so that’s pretty cool! I can support a local madman who thinks he can make a better operating system than all those corporations. Maybe he was right, maybe not. What I know is I am excited to find out!</p> <p>Wish my luck on my OpenBSD journey. I will post updates here along the way.</p> <p>Happy hacking!</p>As Linux becomes controlled by corporate sponsors and becomes more full of proprietary blobs, drivers, and even closed-source software like Steam, One may wonder if there are other options out there. For me, somebody that is intensely interested in security, there is one option: OpenBSD.Know How Your Representative Votes In Parliament2020-07-30T00:00:00+00:002020-07-30T00:00:00+00:00/2020/07/30/canadian-parliament<p>As an advocate for openness, I had an idea to make a project out of the government of Canada’s <a href="https://open.canada.ca/en/open-data">Open Data</a> initiative to take a look at how my local MP voted on various pieces of legislation. It turns out though that this was not necessary due to how easy it was to find this information on the government’s own website. In this article, I will explain how you can do the same.</p> <h3 id="1-find-your-representative">1. Find Your Representative</h3> <p>The first step in this process is to find who your representative is. To do so, go to the government’s own website <a href="https://www.ourcommons.ca/Members/en">ourcommons.ca’s search tool</a>.</p> <p>Simply type in your postal code in the search box to find out who your MP is.</p> <h3 id="2-their-voting-record">2. Their Voting Record</h3> <p>Every MP’s voting record is public knowledge, and it is available nice and simple in a table on that MP’s page. For example, this is a link to <a href="https://www.ourcommons.ca/Members/en/pierre-poilievre(25524)/votes">Pierre Poilievre’s voting record</a>.</p> <p>To find your MP’s voting record, do step one, then: After the <strong>Overview</strong>, and <strong>Seat in The House</strong> sections, there are three tabs, <strong>Roles</strong>, <strong>Work</strong>, and <strong>Contact</strong>. Click on work. At the bottom of that tab is a link which says <strong>Chamber Votes</strong>. This will open a small window with some recent votes by this politician. If you want to see all their votes, there is a button at the bottom named <strong>All Votes by This Member</strong>.</p> <p>Tada! You can now keep your local MP accountable for anything you do or do not support.</p> <h3 id="3-bill-details">3. Bill Details</h3> <p>If you want to get into the nitty gritty, once you open a specific bill, you can actually find out the status of said bill, or read the actual text by clicking the <strong>View this Bill on LEGISinfo</strong> button.</p> <p>Both the status of the bill, and a link to a PDF document containing the bilingual text of the bill are visible in the main body of the page.</p> <h4 id="conclusion">Conclusion</h4> <p>I thought this was pretty cool! It was <em>way</em> simpler than I thought it would be.</p> <p>Thanks, Canada!</p>As an advocate for openness, I had an idea to make a project out of the government of Canada’s Open Data initiative to take a look at how my local MP voted on various pieces of legislation. It turns out though that this was not necessary due to how easy it was to find this information on the government’s own website. In this article, I will explain how you can do the same.Installing MultiCraft on Gentoo Linux2020-07-19T00:00:00+00:002020-07-19T00:00:00+00:00/2020/07/19/multicraft-php-gentoo<p>In a very odd combination of requirements, I needed to install <a href="https://multicraft.org">MultiCraft</a> on a Gentoo Linux system. The PHP <code class="language-plaintext highlighter-rouge">USE</code> flags are important so you don’t have to recompile it three times like I did.</p> <p>Here are some useful tips I came across:</p> <h3 id="php-use-flags">PHP <code class="language-plaintext highlighter-rouge">USE</code> flags</h3> <p>In <code class="language-plaintext highlighter-rouge">/etc/portage/package.use/php</code> I placed the following line:</p> <pre class="terminal"> dev-lang/php cgi mysql mysqli fpm pdo gd truetype </pre> <p>This should give you enough for a mysql backended MultiCraft installation. The <code class="language-plaintext highlighter-rouge">cgi</code> option may not be required as <code class="language-plaintext highlighter-rouge">fpm</code> stands for <em>FastCGI Process Managment</em>. I don’t know for sure though.</p> <h3 id="paper">Paper</h3> <p>This will grab the latest version of the Paper jar file using <a href="https://yivesmirror.com">YivesMirror</a>. I’m not sure how reputable it is, but my buddy who works with this stuff more often than me seemed to recognize it.</p> <pre class="terminal"> ## See the default craftbukkit.jar.conf for a detailed documentation of the ## format of this file. [config] name = Paper 1.16.1 Latest source = https://yivesmirror.com/files/paper/Paper-1.16.1-latest.jar category = Mods [encoding] #encode = system #decode = system #fileEncoding = latin-1 [start] command = "{JAVA}" -Xmx{MAX_MEMORY}M -Xms{START_MEMORY}M -XX:MaxPermSize=128M -Djline.terminal=jline.UnsupportedTerminal -jar "{JAR}" nogui </pre> <h3 id="other-tips">Other Tips</h3> <p>Do not use the option to setup a separate user for each server. This completely stalled any work getting done with a ton of ‘permission denied’ errors.</p> <h4 id="security">Security</h4> <p>If the panel is in the root directory of your NGINX web server, use the following in your server block to deny access to the <code class="language-plaintext highlighter-rouge">/protected</code> directory.</p> <pre class="terminal"> location /protected { deny all; return 404; } </pre> <h5 id="mysql">MySQL</h5> <p>It is always good practice to separate privileges. The MultiCraft daemon should have one SQL login, with one database allocated to it. The MultiCraft panel should have a separate SQL login, with a separate database allocated to it.</p> <p>You can do this with the following commands in your MySQL prompt:</p> <pre class="terminal"> sql&gt; CREATE DATABASE multicraft_daemon_database; Query OK, 0 rows affected (0.01 sec) sql&gt; CREATE DATABASE multicraft_panel_database; Query OK, 0 rows affected (0.01 sec) sql&gt; CREATE USER 'muilticraft_daemon'@'localhost' IDENTIFIED BY 'strong password here'; Query OK, 0 rows affected (0.01 sec) sql&gt; CREATE USER 'multicraft_panel'@'localhost' IDENTIFIED BY 'different strong password here'; Query OK, 0 rows affected (0.01 sec) sql&gt; GRANT ALL PRIVILEGES ON multicraft_daemon_database . * TO 'multicraft_daemon'@'localhost'; Query OK, 0 rows affected (0.01 sec) sql&gt; GRANT ALL PRIVILEGES ON multicraft_panel_database . * TO 'mutlicraft_panel'@'localhost'; Query OK, 0 rows affected (0.01 sec) </pre> <p>During setup, make sure the proper credentials are used for each step. Database 1 is the panel database. Database 2 is the daemon database.</p> <p>Happy hacking :)</p>In a very odd combination of requirements, I needed to install MultiCraft on a Gentoo Linux system. The PHP USE flags are important so you don’t have to recompile it three times like I did.Independence2020-07-12T00:00:00+00:002020-07-12T00:00:00+00:00/2020/07/12/independence<blockquote> <p>“When given a choice between independence and dependence, always choose independence; you will never regret that choice!”—Luke Smith</p> </blockquote> <p>Whatever you may believe about the YouTube personality Luke Smith, the quote above summarizes a core principle of mine. Much like many people have religious principles, I have <em>Independence</em>.</p> <p>My choice to use Linux as my primary operating system, host my own website, own my own domain name—all of these are directly related to this core principle of independence.</p> <p>I never want a man, or a company to have too much power over my life. Just like I would not trust just any person to be able to read my emails, know where I live, where I am going, who are my friends, what do I believe; in the same way, I do not trust a company with that same information.</p> <blockquote> <p>“If you want to find out what a man is to the bottom, give him power. Any man can stand adversity — only a great man can stand prosperity.”—Robert Ingersoll</p> </blockquote> <p>Take control of your own digital life:</p> <ol> <li>Own your own domain.</li> <li>Hookup an email and a website to that.</li> </ol> <p>That’s it!</p> <p>Without this, any of your internet privileges can be revoked at any time by Google, Facebook, YouTube, Twitter, or even an angry Twitter Mob. Maybe because they hate your skin colour, maybe they hate your religious/political views, or maybe you got caught on a technicality.</p> <p>If you own your own domain, however:</p> <p>Your email provider goes down/bans you: change your provider; keep the email.</p> <p>Your website is pulled for controversial views: switch hosts.</p> <p>Protect yourself; give yourself choices. Why give others that power when you could have it for yourself?</p>“When given a choice between independence and dependence, always choose independence; you will never regret that choice!”—Luke SmithHow to use tmux to send and receive things from your Minecraft server2020-06-25T00:00:00+00:002020-06-25T00:00:00+00:00/2020/06/25/tmux-minecraft<p>So recently I had problem. I run a Minecraft server on a big Linux computer I have running in my room. Now, as a system administrator it is very helpful to be able to run some simple commands without needing to login with my key, password, TFA, etc. It is, frankly, a lot of work. Especially when I really just want to be playing games but I just need to check something quickly.</p> <p>So for simple things like finding out of the network, CPU, memory or disk usage is my bottleneck, I wrote this really nifty script to connect the world of Minecraft and the Linux shell.</p> <p>My completed solution for what I needed can be found at <a href="https://github.com/TTWNO/termcraft/">https://github.com/TTWNO/termcraft</a>.</p> <p>If you want some of the implementation details, stick around.</p> <h2 id="solution">Solution</h2> <p>So to solve this interesting problem, I decided to use <code class="language-plaintext highlighter-rouge">tmux</code>. <code class="language-plaintext highlighter-rouge">tmux</code> is a <strong>t</strong>terminal <strong>mu</strong>ltiple<strong>x</strong>er. This allows you to run a terminal session, then detach fromc it while it still runs in the background.</p> <p>This is very valuable when running command line applications that need to have an active console connection, like a Minecraft server.</p> <p>So first I looked at the <code class="language-plaintext highlighter-rouge">tmux</code> command <code class="language-plaintext highlighter-rouge">send-keys</code>.</p> <h4 id="send-keys"><code class="language-plaintext highlighter-rouge">send-keys</code></h4> <p><code class="language-plaintext highlighter-rouge">send-keys</code> allows you to send text, and key presses to a <code class="language-plaintext highlighter-rouge">tmux</code> session. Now assuming this <code class="language-plaintext highlighter-rouge">tmux</code> session is attached to a Minecraft server, there is no reason you could not run a command like this:</p> <pre class="terminal"> $ tmux send-keys "tell @a This is a Test" Enter </pre> <p>This will send the text “tell @a This is a Test” to the Minecraft server. Then, it will hit the newline character, this will execute the command.</p> <p>So now we can send information to the server and have it tell the users something.</p> <p>But how do we get information about who is typing what in the Minecraft chat?</p> <h3 id="tmuxs-capture-pane-is-painful"><code class="language-plaintext highlighter-rouge">tmux</code>’s <code class="language-plaintext highlighter-rouge">capture-pane</code> is painful</h3> <p>So in the manual page for <code class="language-plaintext highlighter-rouge">tmux</code> I can see a section recorded below for options I can give to the <code class="language-plaintext highlighter-rouge">capture-pane</code> subcommand.</p> <pre class="terminal"> -S and -E specify the starting and ending line numbers, zero is the first line of the visible pane and negative numbers are lines in the history. ‘-’ to -S is the start of the history and to -E the end of the visible pane. The default is to capture only the visible contents of the pane. </pre> <p>What it seems to be saying is I can start at line <code class="language-plaintext highlighter-rouge">-S n</code> and end at line <code class="language-plaintext highlighter-rouge">-E n</code>. Negative numbers start from the bottom, so <em>in theory</em> I can do the following: <code class="language-plaintext highlighter-rouge">tmux capture-pane -S -1</code> should capture only the last line, because I’m starting from the last line. Right?</p> <p>No. It just doesn’t work. Negative numbers do <em>not</em> work with the <code class="language-plaintext highlighter-rouge">tmux capture-pane</code> subcommand.</p> <p>So I did some simple UNIX piping, like so, to get just the last thing in the chat.</p> <pre class="terminal"> $ tmux capture-pane -p -t steve | tail -n1 [SERVER] [ExtraDebuggingInfoHere]: &lt;TaterTheTot&gt; MY_MESSAGE </pre> <p>TaterTheTot is my Minecraft username :)</p> <p><code class="language-plaintext highlighter-rouge">-p</code> prints the result to the terminal/stdout.</p> <p><code class="language-plaintext highlighter-rouge">steve</code> is the name of the tmux session I’m trying to pull form.</p> <p>So that’s done! Beauty!</p> <p>Now that we have that, how can we extract the username and the message from the latest line?</p> <h3 id="grep"><code class="language-plaintext highlighter-rouge">grep</code></h3> <p><code class="language-plaintext highlighter-rouge">grep</code> is a command to find patterns of text. <code class="language-plaintext highlighter-rouge">grep</code> has an option to only show a matching pattern of text. This option is <code class="language-plaintext highlighter-rouge">-o</code>.</p> <p>Let’s see how we can use this in conjunction with our latest line of server output to get our results.</p> <pre class="terminal"> $ echo "[DEBUG] [SERVER] blah blah: &lt;TaterTheTot&gt; MY_MESAGE" | grep -o "&lt;.&ast;&gt;" &lt;TaterTheTot&gt; </pre> <p>Now, that’s my name with the &lt; and &gt; attached. Not bad! We can use the <code class="language-plaintext highlighter-rouge">sed</code> command to clean it up a bit.</p> <p>The syntax is like so: <code class="language-plaintext highlighter-rouge">select/somepattern/replacewith/global</code></p> <p>So the following command is: <code class="language-plaintext highlighter-rouge">s/[&lt;&gt;]//g</code></p> <p>Select any characters that are either &lt; or &gt;. Replace with nothing. Do so globally (as in, don’t stop after you replace only one character).</p> <p>Take two!</p> <pre class="terminal"> $ echo "[DEBUG] [SERVER] blah blah: &lt;TaterTheTot&gt; MY_MESAGE" | grep -o "&lt;.&ast;&gt;" | sed 's/[&lt;&gt;]//g' TaterTheTot </pre> <p>Beautiful!</p> <p>Now what about that pesky message?</p> <h3 id="more-grep-more-sed">more <code class="language-plaintext highlighter-rouge">grep</code>; more <code class="language-plaintext highlighter-rouge">sed</code></h3> <p>Simple: capture everything after the &gt;. Leaving the user’s message entirely in tact.</p> <pre class="terminal"> $ echo "[DEBUG] [SERVER] blah blah: &lt;TaterTheTot&gt; MY_MESAGE" | grep -o "&gt;.&ast;$" | sed 's/&gt; //' MY_MESSAGE </pre> <p>So now we have a way to get the username of someone typing in the Minecraft server chat. We have a way to find out what they said. And, we have a way to respond.</p> <p>You can imagine how these might go together for your own use case.</p> <h3 id="conclusion">Conclusion</h3> <p>This shows some pretty fun stuff you can do with a few simple Linux commands and a Minecraft server.</p> <p>I hope you learned something and found my explanations not horrific haha!</p> <p>Remember to checkout the git repository to see what I did with it: <a href="https://github.com/TTWNO/termcraft">https://github.com/TTWNO/termcraft</a>.</p> <p>Happy hacking!</p>So recently I had problem. I run a Minecraft server on a big Linux computer I have running in my room. Now, as a system administrator it is very helpful to be able to run some simple commands without needing to login with my key, password, TFA, etc. It is, frankly, a lot of work. Especially when I really just want to be playing games but I just need to check something quickly.Site Update2020-06-04T00:00:00+00:002020-06-04T00:00:00+00:00/2020/06/04/site-update<p>I updated the site with some easier to identify information about me and my projects :)</p> <p>Also, Clue has been delayed due to my partner in crime on the project wokring too many hours.</p> <p>I also posted a new project called <em><a href="https://github.com/TTWNO/caesar-cipher">Caesar Cipher</a></em> in C. It will be an intermediate example of how to use build systems like <code class="language-plaintext highlighter-rouge">make</code>.</p>I updated the site with some easier to identify information about me and my projects :)New Game: Clue (coming soon)2020-05-19T00:00:00+00:002020-05-19T00:00:00+00:00/2020/05/19/clue-announcement<p>Ooo! Exciting! Today I want to announce a new project I’ll be working on which should be live within the month of May: Clue.</p> <p>The original board game, implemented in an accessible format via the web.</p> <p>It uses a Node.js backend and standard Javascript/HTML frontend. Nothing fancy.</p> <p>All the code will be hosted here: <a href="https://github.com/TTWNO/clue">https://github.com/TTWNO/clue</a></p> <p>It will be licensed under the BSD-3 license, meaning it can be used for any reason—even commercially and without source-code disclosure—without prior authorization, but it <em>must</em> acknowledge that I helped build the end product.</p> <p>Once the project is live, it will be located at: <a href="">Lame Games</a> (currently a dead link).</p>Ooo! Exciting! Today I want to announce a new project I’ll be working on which should be live within the month of May: Clue. \ No newline at end of file diff --git a/_site/resume/index.html b/_site/resume/index.html index f86252e..bb09c3d 100644 --- a/_site/resume/index.html +++ b/_site/resume/index.html @@ -1 +1 @@ - Resume | tait.tech

Tait Hoyem

Experience

Total Impact SignsTechnical Advisor (2014-2016)

Implemented a semi-automatic payroll system replacing the manual sign in book used before.

IndependentTutoring (2019-present)

Working with computer science students explaining introductory to advanced concepts.

ImaginusSecurity Gaurd (2019-2020)

Night-time security to gaurd inventory.

Education

The Linux Foundation—Certification / Certified Linux System Administrator (2016)

Certified in Linux system administration tasks. Running web servers, maintaining mass storage systems, running secure remote control tools. Certificate no longer valid.

Southern Alberta Institute of Technology—Diploma / Information Technology-Software Development Major (2019-2021)

Full-stack two year software development diploma. Focusing on databases, interface design and enterprise solutions.

Professional Development

Dartmouth College (Open Corseware)—Professional Certificate / C Programming with Linux (2020)

A certification in C programming, the gcc compiler, and the make compilation system.

Projects

epub-with-pinyin

A program to add Pinyin above Chinese characters in .epub files to assist those learning Mandarin Chinese.

chess

A command-line chess game in C++. It is compileable on almost any system.

tait.tech

All the code for my website is open source.

lamegames

A little games website I made for some demonstrations of Django functionality. Very, very lame. Would not recommend.

subnetting

One-night write of a subnet calculation tool.

Languages

Human
  • English (Native)

  • Mandarin Chinese (Novice)

Computer
  • C++ (4/5)

  • C (3/5)

  • Java (2/5)

  • Python (2/5)

\ No newline at end of file + Resume | tait.tech

Tait Hoyem

Experience

Total Impact SignsTechnical Advisor (2014-2016)

Implemented a semi-automatic payroll system replacing the manual sign in book used before.

IndependentTutoring (2019-present)

Working with computer science students explaining introductory to advanced concepts.

ImaginusSecurity Gaurd (2019-2020)

Night-time security to gaurd inventory.

Education

The Linux Foundation—Certification / Certified Linux System Administrator (2016)

Certified in Linux system administration tasks. Running web servers, maintaining mass storage systems, running secure remote control tools. Certificate no longer valid.

Southern Alberta Institute of Technology—Diploma / Information Technology-Software Development Major (2019-2021)

Full-stack two year software development diploma. Focusing on databases, interface design and enterprise solutions.

Professional Development

Dartmouth College (Open Corseware)—Professional Certificate / C Programming with Linux (2020)

A certification in C programming, the gcc compiler, and the make compilation system.

Projects

epub-with-pinyin

A program to add Pinyin above Chinese characters in .epub files to assist those learning Mandarin Chinese.

chess

A command-line chess game in C++. It is compileable on almost any system.

tait.tech

All the code for my website is open source.

lamegames

A little games website I made for some demonstrations of Django functionality. Very, very lame. Would not recommend.

subnetting

One-night write of a subnet calculation tool.

Languages

Human
  • English (Native)

  • Mandarin Chinese (Novice)

Computer
  • C++ (4/5)

  • C (3/5)

  • Java (2/5)

  • Python (2/5)

\ No newline at end of file