From b036acd3f4ea62a6b31ed24d2566890594788fc6 Mon Sep 17 00:00:00 2001
From: Snorre Ettrup Altschul <snorre@altschul.dk>
Date: Thu, 6 Feb 2025 17:03:01 +0100
Subject: [PATCH] toc wip

---
 public/css/css.css                            |  4 +
 public/index.html                             | 29 +++++--
 public/js/md2html.js                          | 85 ++++++++++++++++---
 ...tting hungry minecraft servers to sleep.md | 67 +++++++++++----
 public/posts/index.html                       | 18 ++++
 public/posts/test.md                          | 11 +++
 public/posts/toc.md                           |  5 ++
 7 files changed, 188 insertions(+), 31 deletions(-)
 create mode 100644 public/posts/toc.md

diff --git a/public/css/css.css b/public/css/css.css
index 7aee9e4..a05d6af 100644
--- a/public/css/css.css
+++ b/public/css/css.css
@@ -8,6 +8,10 @@ code {
   border-radius: var(--radius);
 }
 
+ul.posts>li {
+  margin-top: var(--margin);
+}
+
 pre.codeblock {
   background: var(--background-text);
   color: var(--text);
diff --git a/public/index.html b/public/index.html
index 2deb6b8..3ec0d87 100644
--- a/public/index.html
+++ b/public/index.html
@@ -20,11 +20,12 @@
         <li>Vim motions</li>
       </ul>
     </div>
-    <div class="content" style="grid-area: content2">
-      <a href="/posts"><h2>Projects I've finished</h2></a>
+    <div class="content posts" style="grid-area: content2">
+      <h2><a href="posts">Projects I've finished</a></h2>
       <ul>
-        <li><a href="/posts?post=openbirch">Openbirch</a><br>(made it to an alpha release)</li>
-        <li><a href="/posts?post=minecraft-proxy">Minecraft server
+        <li><a href="/posts?post=How I made a programming language 700x slower than python">Openbirch</a><br>(made it to
+          an alpha release)</li>
+        <li><a href="/posts?post=Putting hungry minecraft servers to sleep">Minecraft server
             hibernator</a></li>
         <li><a href="https://gitlab.com/SpoodyTheOne/the-wheel">Wheel of names drinking game</a><br>(scraping the bottom
           of the barrel for finished projects)</li>
@@ -32,7 +33,9 @@
     </div>
     <!-- <div class="content" style="grid-area: content3">content</div> -->
 
-    <div class="footer" style="grid-area: footer1"><marquee>Footer</marquee></div>
+    <div class="footer" style="grid-area: footer1">
+      <marquee>Footer</marquee>
+    </div>
     <div class="footer" style="grid-area: footer2; text-align: center">
       <!-- <a href="https://nixwebr.ing/next/<name>">&leftarrow; prev</a> <a href="https://nixwebr.ing">nixwebr.ing</a> -->
       <!-- <a href="https://nixwebr.ing/prev/<name>">next &rightarrow;</a> -->
@@ -49,6 +52,22 @@
   <script src="/js/main.js"></script>
   <script>
     generate_background();
+
+    (async () => {
+      let response = await fetch(`/posts/index`);
+      if (response.status != 200)
+        return;
+
+      let posts = (await response.text()).split("\n");
+
+      let p = document.querySelector("#content>.content.posts>h2");
+      p.innerText = "Recent posts";
+
+      let elem = document.querySelector("#content>.content.posts>ul");
+      elem.innerHTML = posts.slice(0,3).map(x => `<li><a href="/posts?post=${x}">${x}</a></li>`).join("\n");
+      elem.style.maxWidth = "40ch";
+      elem.classList.add("posts");
+    })()
   </script>
 </body>
 
diff --git a/public/js/md2html.js b/public/js/md2html.js
index dad335e..aa98e10 100644
--- a/public/js/md2html.js
+++ b/public/js/md2html.js
@@ -61,14 +61,23 @@ const md2tokens = (markdown, newline) => {
     switch (markdown[i]) {
       case '\\':
         i++;
+
+        if (peek(0) == 't' && peek(1) == 'o' && peek(2) == 'c') {
+          advance();
+          advance();
+          advance();
+          tokens.push({ type: "toc" });
+          break;
+        }
+
         current_string += markdown[i];
         break;
 
       case '*':
         finish();
         advance();
-        newline=false;
-        newline_count=0;
+        newline = false;
+        newline_count = 0;
         let depth_em = 1;
         let type = "em";
         if (match("*")) {
@@ -82,8 +91,8 @@ const md2tokens = (markdown, newline) => {
         }
 
         current_string = capture_until_predicate((i) => { let found = true; for (let j = 0; j < depth_em; j++) { if (markdown[i + j] != "*") { found = false; break; } } return found; })
-  
-        for (let j=0;j<depth_em-1;j++) {
+
+        for (let j = 0; j < depth_em - 1; j++) {
           // console.log(markdown[i] == "\n");
           advance();
         }
@@ -103,6 +112,8 @@ const md2tokens = (markdown, newline) => {
           depth_he++;
         } while (match("#"));
 
+        advance();
+
         while (markdown[i] != "\n" && markdown.length > i) {
           current_string += markdown[i];
           i++
@@ -110,6 +121,7 @@ const md2tokens = (markdown, newline) => {
 
         tokens.push({ type: "header", level: depth_he, content: current_string });
         current_string = "";
+        newline_count = 2;
         break;
 
       case '!':
@@ -144,23 +156,37 @@ const md2tokens = (markdown, newline) => {
         tokens.push({ type: "image", alt: alt_text, url: url, title: title })
         break;
 
+      case '>':
       case '-':
         if (!newline) {
           def();
           break;
         }
 
+        if (peek(0) == "-" && peek(1) == "-" && peek(2) == "-" && peek(3) == "\n") {
+          advance();
+          advance();
+          advance();
+
+          tokens.push({ type: "hline" });
+          break;
+        }
+
+        let type_l = peek(0) == '-' ? "ul" : "quote";
+        let ch = peek(0);
+
         let items = [];
 
         advance();
 
         do {
+          match(" ");
           let text = capture_until("\n");
           advance();
           items.push(md2tokens(text, false));
-        } while (match("-"));
+        } while (match(ch));
 
-        tokens.push({ type: "ul", items: items });
+        tokens.push({ type: type_l, items: items });
 
         break;
 
@@ -177,8 +203,8 @@ const md2tokens = (markdown, newline) => {
       case '[':
         finish();
         advance();
-        newline=false;
-        newline_count=0;
+        newline = false;
+        newline_count = 0;
         let text = capture_until("]");
         advance();
 
@@ -196,8 +222,8 @@ const md2tokens = (markdown, newline) => {
       case '`':
         finish();
         advance();
-        newline=false;
-        newline_count=0;
+        newline = false;
+        newline_count = 0;
         let stop = (i) => markdown[i] == "`";
         let type_c = "inlinecode";
         let language = "";
@@ -211,7 +237,7 @@ const md2tokens = (markdown, newline) => {
         current_string = capture_until_predicate(stop);
         tokens.push({ type: type_c, content: current_string, language: language });
         current_string = "";
-        if (type_c == "codeblock") { advance(); advance(); newline_count=2; } // remove trailing `
+        if (type_c == "codeblock") { advance(); advance(); newline_count = 2; } // remove trailing `
         break;
 
       default:
@@ -240,6 +266,32 @@ const tokens2html = (tokens) => {
 
   for (let token of tokens) {
     switch (token.type) {
+      case "toc":
+        let htok = tokens.filter(x => x.type == "header");
+        console.log(htok);
+        const createList = (tokens, depth = 2) => {
+          let list = '';
+          let listItems = '';
+
+
+          while (tokens.length > 0 && tokens[0].level <= depth) {
+            const token = tokens.shift();
+            listItems += `<li><a href="#${token.content}">${token.content}</a></li>`;
+
+            if (tokens.length > 0 && tokens[0].level > token.depth) {
+              listItems += createList(tokens, token.depth + 1);
+            }
+          }
+
+          if (listItems) {
+            list = `<ol>${listItems}</ol>`;
+          }
+
+          return list;
+        };
+
+        output += createList(htok);
+        break;
       case "span":
         output += `<span>${token.content}</span>`;
         break
@@ -254,6 +306,10 @@ const tokens2html = (tokens) => {
         break;
       case "header":
         output += `<h${token.level}>${token.content}</h${token.level}>`
+        if (token.level == 2) output += "<hr>";
+        break;
+      case "hline":
+        output += "<hr>";
         break;
       case "newline":
         output += "<br>"
@@ -269,6 +325,13 @@ const tokens2html = (tokens) => {
         }
         output += "</ul>";
         break;
+      case "quote":
+        output += "<pre class=\"quote\">\n";
+        for (let item of token.items) {
+          output += `${tokens2html(item)}\n`;
+        }
+        output += "</pre>";
+        break;
       case "inlinecode":
         output += `<code>${token.content}</code>`;
         break;
diff --git a/public/posts/Putting hungry minecraft servers to sleep.md b/public/posts/Putting hungry minecraft servers to sleep.md
index 1e7bc74..894bd7b 100644
--- a/public/posts/Putting hungry minecraft servers to sleep.md	
+++ b/public/posts/Putting hungry minecraft servers to sleep.md	
@@ -1,34 +1,71 @@
 2
 Minecraft Rust Async Networking Proxy CRIU
 # Minecraft servers are HUNGRY
+
 They hunger for your ram and your cpu. This makes it either expensive or laggy to try and host multiple servers at once.
 This was something I encountered when my friend built a homeserver out of spare computer parts that was barely powerful enough to run a minecraft server.
-The problem was that soon multiple people wanted a minecraft server hosted by him, including ourselves with modded experiences.
+The problem was that soon multiple people wanted a minecraft server hosted by him (which included us wanting to play modded minecraft).
 
 It was a hassle to ssh into the server and start and stop the various servers depending on who wanted to play,
 especially since a lot of people only played very rarily.
 
 I remembered that I'd seen [a project](https://github.com/gekware/minecraft-server-hibernation) that claimed to be able to hibernate a minecraft server if nobody was playing on it.
-This worked by starting and stopping the server, leading to noticeable join delays when joining modded servers.
+The only issue was that it worked for a single server, and did so by starting and stopping the server process.
 
-Another nice-to-have was being able to join through a subdomain. After having messed around with nginx to help my friend set up various services like nextcloud
-on subdomains we wanted to do the same for the minecraft servers.
+This meant that if we wanted to join a modded server the hundreds of mods could make us wait for several minutes before we could play.
 
-Being able to join the vanilla server through `vanilla.domain.com` and the modded through `modded.domain.com` would simply be really cool.
+Another issue was the fact that it could only host a single server. This meant that we would have to run multiple intances of the watcher,
+and that each server would be assigned to an arbitrary port that would be needed when connecting.
 
-# Minecraft Server Hibernation Proxy Manager Rs (Name WIP)
+# Building a reverse proxy for minecraft
 
-I ended up writing [a Rust program](https://gitlab.com/SpoodyTheOne/minecraft-server-hibernation-proxy-rust) that could do this and more. It listens on the default minecraft server port of `25565` and parses any incoming data as minecraft packets.
-If the data contains a url that matches `*.domain.com` then it uses the subdomain to look up a configured server, and if found redirects all future traffic to that port.
+Since my friends server was accessible through a domain we thought it would be cool if instead of supplying a port
+you could connect to a subdomain and be sent to a specific server.
 
-![Image of a server displaying the startup message](/posts/minecraft-proxy/starting.png "A server that is waiting to be started")
+The simplest way to do this would be to create a dhcp record for each subdomain to point to a server,
+but that would be slow and tedious to set up for every server.
 
-If the server is stopped or hibernating then the command to start it is automatically run. Likewise if all players leave a configurable countdown is started.
-Once it reaches 0 the server is hibenated using CRIU, which writes the entire memory and all open file descriptors of the server program to disk.
+We then tried nginx, as it seemingly could magically redirect traffic to an internal port based on the subdomain.
+I quickly found out that this did *not* work for minecraft servers (who would have guessed), but after doing some research
+I decided on creating my own reverse proxy that spoke the minecraft protocol instead of HTTP.
 
-![Hibernating server](/posts/minecraft-proxy/hibernating.png "Hibernating server")
+# The Minecraft protocol
 
-This means that joining a hibernated server only takes a few seconds, and any server currently hibernating will only take up disk space.
-It can also be restored even after system restarts, so it would be possible to halt systemd shutdowns until all servers are hibernated.
+Minecraft implements its own protocol consistent of packets. My first idea was to see if anybody had created a
+rust library for dealing with minecraft packets. 
 
-<marquee style="font-size: 20em">Marq**WEE**</marquee>
+While some did exist, most of them where unfinished or couldn't do what I wanted. [One crate](https://www.youtube.com/watch?v=E4WlUXrJgy4)
+was useful for seeing how an implementation of parsing packets was done, but ultimately I had to write my own parser.
+
+*images of minecraft protocol*
+
+# Detecting the subdomain
+
+Luckily for me, the Minecraft procotol sends the full address it is trying to connect to during the handshake. This meant that I simply
+had to parse the handshake and extract the subdomain from the address, see if it matches any configured server, and then redirect all
+traffic to the internal port that server runs on.
+
+*code explanation*
+
+# Why aren't my chunks loading?
+
+Just as I thought I was finally done I encountered a problem. The chunks where loading *really* slowly. It felt like we we're back to
+hosting on [aternos](aternos.org) (nothing against aternos, the **free** servers are just a little slow).
+
+*image of chunks not loading*
+
+I couldn't figure out why it was so slow. I thought it was because all the traffic had to flow through my program,
+but the sockets are connected on the kernel level.
+
+After hours of debugging I found the solution. `stream.set_nodelay(true)`
+
+## ...
+
+**All this time the problem was as simple as saying ** `if (slow) { dont(); }`!
+
+To say I was pissed was an understatement, but at the same time I was glad it was finally working.
+
+The reason this fixed it was because it disabled [Nagle's algorithm](https://en.wikipedia.org/wiki/Nagle%27s_algorithm), which bunches packets together
+in order to (hopefully) speed up the sending of lots of smaller packets as one big packet.
+
+For some reason this absolutely destroyed the chunk loading speed, and disabling it led to finally having the perfect Minecraft reverse proxy hibernation system thing.
diff --git a/public/posts/index.html b/public/posts/index.html
index 31fe201..808844c 100644
--- a/public/posts/index.html
+++ b/public/posts/index.html
@@ -21,6 +21,24 @@
 
     ul,ol {
       margin-top:0;
+      padding-left: calc(4*var(--margin));
+    }
+
+    pre.quote {
+      font: inherit;
+
+      background: var(--background-text);
+      border-left: var(--margin) solid var(--border);
+      border-radius: 0 var(--radius) var(--radius) 0;
+      /* padding-bottom: var(--margin); */
+      padding: var(--padding);
+    }
+
+    hr {
+      border: none;
+      background-color: var(--border);
+      border-radius: var(--radius);
+      height: 2px;
     }
 
     img {
diff --git a/public/posts/test.md b/public/posts/test.md
index 02f5bd7..a6af185 100644
--- a/public/posts/test.md
+++ b/public/posts/test.md
@@ -1,5 +1,6 @@
 0
 Test
+\toc
 # H1
 ## H2
 ### H3
@@ -16,6 +17,8 @@ This is regular text
 
 ***bold italic***
 
+---
+
 `this is an inline code block`
 
 ```
@@ -60,6 +63,14 @@ in
 - Test
 - here
 
+> this
+> is
+> a
+>
+> QUOTE
+
+> **QUOTE**
+
 ![test broken image](broken_image.png)
 ![test image](/test.png)
 ![test image](/test.png "Image with caption")
diff --git a/public/posts/toc.md b/public/posts/toc.md
new file mode 100644
index 0000000..b1a302a
--- /dev/null
+++ b/public/posts/toc.md
@@ -0,0 +1,5 @@
+0
+Tick Tock Cock
+# Table of Contents test
+
+\toc