Browse Source

Port wiki pages, update code highlight CSS

master
Noah Petherbridge 2 years ago
parent
commit
b475859a1e
32 changed files with 1408 additions and 52 deletions
  1. +4
    -6
      www/index.gohtml
  2. +46
    -46
      www/solar/monokai.css
  3. +17
    -0
      www/wiki/Breath-of-the-Wild-Challenge-Run.md
  4. +21
    -0
      www/wiki/Built-with-RiveScript.md
  5. +57
    -0
      www/wiki/Common-ffmpeg-commands.md
  6. +89
    -0
      www/wiki/Debian-on-Macbook.md
  7. +15
    -0
      www/wiki/Fedora-Dependencies-for-Ubuntu-Projects.md
  8. +70
    -0
      www/wiki/Fedora-NetworkManager-OpenVPN.md
  9. +93
    -0
      www/wiki/Fedora-on-Macbook.md
  10. +7
    -0
      www/wiki/FollowingSync-Privacy-Policy.md
  11. +13
    -0
      www/wiki/Go-QT5-Installation.md
  12. +49
    -0
      www/wiki/IPv6-on-Linux.md
  13. +26
    -0
      www/wiki/Main-Page.md
  14. +57
    -0
      www/wiki/Minecraft-Clone.md
  15. +82
    -0
      www/wiki/Minecraft-vs-Terraria-Account-Models.md
  16. +105
    -0
      www/wiki/Optimize-RiveScript.md
  17. +58
    -0
      www/wiki/PowerTOP-and-USB-Autosuspend.md
  18. +58
    -0
      www/wiki/Protocol-Buffers-Protocol.md
  19. +74
    -0
      www/wiki/Publishing-RiveScript-Modules.md
  20. +22
    -0
      www/wiki/Python-cocos2d-Installation.md
  21. +74
    -0
      www/wiki/Python-for-RiveScript-Go.md
  22. +12
    -0
      www/wiki/Python-on-Mac-OS-X-Tips.md
  23. +7
    -0
      www/wiki/Resetting-Modal-Bluetooth-Headphones.md
  24. +32
    -0
      www/wiki/RiveScript-Discussions.md
  25. +5
    -0
      www/wiki/September-Links.md
  26. +3
    -0
      www/wiki/TMP.md
  27. +34
    -0
      www/wiki/Tasker-OpenVPN-Connect.md
  28. +7
    -0
      www/wiki/Test-Page.md
  29. +74
    -0
      www/wiki/Things-Learned-with-jQuery-Mobile.md
  30. +35
    -0
      www/wiki/Time-Travel.md
  31. +57
    -0
      www/wiki/Unicode-Problems.md
  32. +105
    -0
      www/wiki/nginx-configs.md

www/index.html → www/index.gohtml View File

@@ -1,13 +1,11 @@
{% extends "layout.html" %}
{% block title %}Welcome!{% endblock %}
{% block content %}

{{ define "title" }}Welcome!{{ end }}
{{ define "content" }}
<h1>Welcome!</h1>

Welcome to <a href="http://www.kirsle.net/">Kirsle.net</a>! This is the personal
website of Noah Petherbridge, and it's where my various software projects and
web blog lives.<p>

{{ include_page("blog.partial_index") | safe }}
{{ RenderIndex .Request "" "" }}

{% endblock %}
{{ end }}

+ 46
- 46
www/solar/monokai.css View File

@@ -2,51 +2,51 @@
* http://tmtheme-editor.herokuapp.com/#/theme/Monokai%20Bright
* Using: https://github.com/davinche/pygments-from-tmtheme
*/
.codehilite { color: #F8F8F2; }
.codehilite .ge { font-style: italic; }
.codehilite .gs { font-weight: bold; }
.codehilite .c { color: #75715E; }
.codehilite .cp { color: #75715E; }
.codehilite .c1 { color: #75715E; }
.codehilite .cs { color: #75715E; }
.codehilite .cm { color: #75715E; }
.codehilite .m { color: #AE81FF; }
.codehilite .mf { color: #AE81FF; }
.codehilite .mi { color: #AE81FF; }
.codehilite .mo { color: #AE81FF; }
.codehilite .se { color: #AE81FF; }
.codehilite .kc { color: #AE81FF; }
.codehilite .k { color: #66D9EF; font-style: italic; }
.codehilite .kd { color: #66D9EF; font-style: italic; }
.codehilite .kn { color: #F92672; }
.codehilite .kt { color: #66D9EF; font-style: italic; }
.codehilite .s { color: #E6DB74; }
.codehilite .sb { color: #E6DB74; }
.codehilite .sc { color: #E6DB74; }
.codehilite .sd { color: #E6DB74; }
.codehilite .s2 { color: #E6DB74; }
.codehilite .sh { color: #E6DB74; }
.codehilite .si { color: #E6DB74; }
.codehilite .sx { color: #E6DB74; }
.codehilite .sr { color: #E6DB74; }
.codehilite .s1 { color: #E6DB74; }
.codehilite .ss { color: #E6DB74; }
.codehilite .na { color: #A6E22E; }
.codehilite .nc { color: #A6E22E; font-style: italic; text-decoration: underline; }
.codehilite .no { color: #AE81FF; }
.codehilite .nd { color: #A6E22E; text-decoration: underline; }
.codehilite .ne { color: #A6E22E; text-decoration: underline; }
.codehilite .nf { color: #A6E22E; }
.codehilite .nt { color: #F92672; }
.codehilite .nv { color: #F8F8F2; }
.codehilite .ow { color: #F92672; }
.codehilite .o { color: #F92672; }
.codehilite .n { color: #F8F8F2; }
.codehilite .nl { color: #F8F8F2; }
.codehilite .nn { color: #F8F8F2; }
.codehilite .nx { color: #F8F8F2; }
.codehilite .bp { color: #F8F8F2; }
.codehilite .p { color: #F8F8F2; }
.highlight { color: #F8F8F2; }
.highlight .ge { font-style: italic; }
.highlight .gs { font-weight: bold; }
.highlight .c { color: #75715E; }
.highlight .cp { color: #75715E; }
.highlight .c1 { color: #75715E; }
.highlight .cs { color: #75715E; }
.highlight .cm { color: #75715E; }
.highlight .m { color: #AE81FF; }
.highlight .mf { color: #AE81FF; }
.highlight .mi { color: #AE81FF; }
.highlight .mo { color: #AE81FF; }
.highlight .se { color: #AE81FF; }
.highlight .kc { color: #AE81FF; }
.highlight .k { color: #66D9EF; font-style: italic; }
.highlight .kd { color: #66D9EF; font-style: italic; }
.highlight .kn { color: #F92672; }
.highlight .kt { color: #66D9EF; font-style: italic; }
.highlight .s { color: #E6DB74; }
.highlight .sb { color: #E6DB74; }
.highlight .sc { color: #E6DB74; }
.highlight .sd { color: #E6DB74; }
.highlight .s2 { color: #E6DB74; }
.highlight .sh { color: #E6DB74; }
.highlight .si { color: #E6DB74; }
.highlight .sx { color: #E6DB74; }
.highlight .sr { color: #E6DB74; }
.highlight .s1 { color: #E6DB74; }
.highlight .ss { color: #E6DB74; }
.highlight .na { color: #A6E22E; }
.highlight .nc { color: #A6E22E; font-style: italic; text-decoration: underline; }
.highlight .no { color: #AE81FF; }
.highlight .nd { color: #A6E22E; text-decoration: underline; }
.highlight .ne { color: #A6E22E; text-decoration: underline; }
.highlight .nf { color: #A6E22E; }
.highlight .nt { color: #F92672; }
.highlight .nv { color: #F8F8F2; }
.highlight .ow { color: #F92672; }
.highlight .o { color: #F92672; }
.highlight .n { color: #F8F8F2; }
.highlight .nl { color: #F8F8F2; }
.highlight .nn { color: #F8F8F2; }
.highlight .nx { color: #F8F8F2; }
.highlight .bp { color: #F8F8F2; }
.highlight .p { color: #F8F8F2; }

/* Kirsle.net overrides */
.codehilite { background-color: transparent }
.highlight { background-color: transparent }

+ 17
- 0
www/wiki/Breath-of-the-Wild-Challenge-Run.md View File

@@ -0,0 +1,17 @@
# Breath of the Wild Challenge Run

After sinking 290+ hours into *Zelda: Breath of the Wild*, completing play-throughs on Normal Mode and Master Mode to 100% completion (minus Korok seeds & armor upgrades)... I decided to play a new Normal Mode game but with added restrictions:

## Rules

All of the above rules are enforced **until you get all 120 shrines.** After that you can go wild: get all the heart containers, activate all the towers, etc.

* **No activating any towers**
* I'm finding that it makes me learn the landscape better. I know generally where things are, but sometimes can't find them, and discover new things. For example, this is my third play-through and only this time did I discover the Ancient Tree while I was searching for the Sage Temple ruins.
* **Only 3 hearts and 1 stamina wheel**
* This will give a *lot* more value to Hearty meals in-game. When I have a lot of base hearts, I found myself making a ton of simple "full heal" meals by just cooking a single Hearty Truffle.
* You *can* temporarily get up to 13 hearts to get the Master Sword, but then immediately return the 10 hearts to the evil statue in Hateno Village.
* **No fast travel except to Travel Medallion or Shrine of Resurrection**
* **Catch your own horse.**
* Don't use Epona. You can spawn her and immediately board her.
* **Don't recolor any clothing until you fully upgrade it.**

+ 21
- 0
www/wiki/Built-with-RiveScript.md View File

@@ -0,0 +1,21 @@
# Built with RiveScript

This is a collection of known projects that are built on top of RiveScript.

* [I.R.I.S. (IRIS Really Is Smart)](https://olympus-gianksp.rhcloud.com/portfolio/i-r-i-s-2012/) - a Java bot built on RiveScript-Java.
* JARVIS - Python home automation bot
* GitHub: <https://github.com/truestealth/JARVIS>
* G+ Group "JARVIS in the making": <https://plus.google.com/communities/103848511044640132327>
* [Tinman](https://github.com/massyn/tinman-rive) - an open source RiveScript brain
* [Domogik](https://github.com/domogik/domogik) - Home automation software that includes RiveScript in its [butler plugin](https://github.com/domogik/domogik/tree/master/src/domogik/butler)
* Website: <http://www.domogik.org/> (French language)
* [Vixia](http://www.vixia.fr/createbot/index.php) - a French language chatbot hosting site that uses RiveScript.
* Uses a custom PHP implementation of RiveScript ([source](http://www.vixia.fr/createbot/sources.php)) - appears to be pretty simplistic at time of writing, and doesn't appear to be based off any existing source code of other RiveScript implementations.
* [Sidekick](https://getyoursidekick.org/) - "Get your very own Sidekick!" - A multi-network bot powered by Botkit and RiveScript, with support for Twitter and Slack.

Other RiveScript-related links that I need to find a home for on the official website somewhere:

* Syntax highlight plugins:
* [Atom-RiveScript](https://github.com/aichaos/atom-rivescript) -- by me.
* [SublimeText](https://packagecontrol.io/packages/RiveScript)
* [vim syntax](https://static.rivescript.com/files/etc/vim-rivescript-0.01.tar.gz) -- by me. Really basic highlighting.

+ 57
- 0
www/wiki/Common-ffmpeg-commands.md View File

@@ -0,0 +1,57 @@
# Common ffmpeg commands

For personal reference, some common things I've had to look up for ffmpeg.

# Chromecast-ability

## Convert to h264+aac

To make a video file Chromecast-able, the video codec needs to be h.264 and the audio codec needs to be AAC.

If a video is Chromecastable, you're able to drag and drop the video into a Google Chrome browser window and it should play the video inside the browser. You can then cast your tab to the Chromecast and watch it on your TV. Videos that *aren't* encoded correctly will instead attempt to "download" when dragged into the window and they need to be reencoded so they work.

The base command that should be able to convert any video is this:

```bash
ffmpeg -i $input -c:v libx264 -preset slow -crf 22 -c:a aac $output
```

## Extract Subtitles from MKV Videos

Some `.mkv` videos have subtitles embedded inside the video file itself, where players like VLC can get them. When you convert these to h.264 you lose the subtitle information. If you want to keep subtitles for VLC, you have to extract them from the original `.mkv` file into a stand-alone `.srt` subtitle file.

You can `dnf install mkvtoolnix` on Fedora to get the tools to manipulate mkv files.

First, you need to find the track number that has the subtitles in it. You can do this within VLC by going to Tools->Media Information, Codec tab, and look for the stream number for the subtitles. An example video had Stream 0 (video), Stream 1 (audio) and Stream 2 (subtitle). You can also install `mkvtoolnix-gui` and use the `mkvinfo` command.

Then to extract the subtitles:

```bash
mkvextract tracks $input $tracknum:$output

# example
mkvextract tracks Video.mkv 2:Video.srt
```

As long as VLC sees a matching SRT file name that corresponds to the video you're watching, it can use the subtitles.

# Rotation and Metadata

Videos recorded on a smartphone are oftentimes encoded "sideways" and have an Orientation metadata tag that tells smart video players how to rotate the video for playback. Video players like VLC know how to deal with the Orientation tag so the video typically looks right in VLC, but other tools (and sometimes Facebook etc.) don't and the video ends up being rotated 90 degrees in either direction.

There's probably a way to strip the metadata at the same time as rotating the video, but I found I had to use two separate commands:

```bash
# reset the rotation metadata
ffmpeg -i $input -metadata:s:v:0 rotate=0 $output

# rotate the video 90 degrees clockwise
ffmpeg -i $input -vf "transpose=1" $output
```

The transpose option takes the following values:

* `0`: 90CounterCLockwise and Vertical Flip (default)
* `1`: 90Clockwise
* `2`: 90CounterClockwise
* `3`: 90Clockwise and Vertical Flip

+ 89
- 0
www/wiki/Debian-on-Macbook.md View File

@@ -0,0 +1,89 @@
# Debian on Macbook

Debian is a bit trickier to install and set up on a Macbook than Fedora is ([Fedora on Macbook](/wiki/Fedora-on-Macbook)). Here's some information I found in comparison to Fedora.

All of this was done using Debian's graphical installer. YMMV if you use the text installer.

# Installation Notes

If you've never installed Debian on anything before, play around in VirtualBox for practice.

## EFI System Partition

In Fedora when you let it create its own partition layout, it would create its own EFI system partition for `/boot/efi` and install its boot code there. My disk looked like this:

1. `/dev/sda1`: Apple EFI System Partition
2. `/dev/sda2`: Mac OS X
3. `/dev/sda3`: Mac OS Recovery
4. `/dev/sda4`: Fedora EFI System Partition (`/boot/efi`)
5. `/dev/sda5`: Fedora boot partition (`/boot`)
6. `/dev/sda6`: Encrypted LUKS + LVM container
* rootfs: `/`
* home: `/home`
* swap

With Debian on the other hand, if you're not careful it will just install its bootloader onto the same ESP partition as Apple's. If you don't want that and you'd prefer Debian to use your own ESP partition, these are the steps:

1. Create your ESP partition first. I made mine 200MB because that's what Fedora does, but it could be much smaller. 10MB, probably.
* "Use as": "EFI System Partition"
2. Select the *existing* Apple EFI partition (/dev/sda1 in my case), and pick "Use as: do not use"
3. On the main screen of the (graphical) partition manager, there should be a letter "K" (for "keep") next to your *second* ESP partition and NOT next to the first one.

Keep your eye on it as you go and add partitions, because for example setting up an encrypted space or LVM will require writing the partition table and it will reset which partitions it wants to "keep" or not, and it will probably put the "K" next to the Apple ESP again!

## Aside: Encrypted LUKS + LVM

This isn't Mac specific, but Fedora makes it *so* easy to set up full disk encryption with LUKS (dm-crypt) and LVM and setting this up on Debian is a much more manual process.

The main thing to do is **make sure you create an encrypted volume first, THEN put the LVM on top of it.** If you do it the other way around you'll end up entering your encryption password once each for all the partitions inside the LVM!

So I set up my ESP and /boot partitions as normal (this part's easy), and then I picked the encrypted disk setup and chose the free space at the end of the disk for where to put the encrypted volume.

Once the encrypted volume was there I went into the Logical Volume Management and picked the `/dev/mapper/` crypto partition as the target. In the LVM you can add multiple logical volumes and this is done similarly to normal partitions, so I made one for rootfs (50GB), swap (4GB) and home (the remaining space).

# Apple Startup Manager

When I had Fedora on the Macbook: if I booted the computer while holding the Option key, the Apple Startup Manager would appear and list Mac OS X and Fedora as bootable targets, and any other bootable USB devices attached at the time. Also, under Mac OS X in the Startup Disk preferences, Fedora would have an entry and could be picked as the default operating system.

In Debian none of this got set up for me automatically.

I tried following directions [from here](http://glandium.org/blog/?p=2830) but just ended up with an unbootable system (or rather, it would simply boot into OS X; Debian was still missing from the Apple Startup Manager). YMMV.

I decided not to mess with it. What I found from researching is that:

* The Intel EFI spec says that EFI System Partitions (ESP) must be formatted as FAT filesystems.
* However, Apple has customized their EFI implementation to include a read-only HFS+ driver as well.
* For the Apple Startup Manager, the ESP is ignored entirely and it only cares about HFS+ partitions that were "blessed" (Fedora made and blessed an HFS+ partition, but Debian made the ESP as a FAT filesystem).
* When the system boots up, it:
* First checks the Intel-compatible EFI for a bootable device, this is where Debian gets found.
* If nothing is found, it goes to the Apple custom firmware system which finds the blessed HFS+ disks and ends up booting OS X.

Macbooks have an Intel FAT ESP partition but they don't use it to boot. It occasionally gets used as a staging area for firmware updates though.

In the event that the EFI boot gets messed up and prefers Mac OS instead (e.g. this happens when upgrading to a newer version of OS X), I'll just have to use a Linux LiveUSB and re-assign the preferred boot order via the `efibootmgr` command.

# Hardware Support

## Display Brightness

Unlike in Fedora, Debian fully supports all of the keyboard functions on the Macbook Air (mid 2015), including the display backlight keys. I now suspect this may have been an SELinux issue in Fedora, since Debian doesn't have SELinux and Fedora would try to adjust the brightness (showing a progress bar on-screen) but simply failing to.

## Broadcom Wireless

In Fedora you'd have needed to get RPMFusion and install `kmod-wl` or `akmod-wl` to get the wifi to work.

In Debian you can follow the [directions on the Debian wiki](https://wiki.debian.org/wl) to get the `wl` driver loaded. Briefly:

Enable the "non-free" repo in `/etc/apt/sources.list` and run these commands:

```bash
# Update and install linux headers and broadcom-sta-dkms
% apt-get update
% apt-get install linux-headers-$(uname -r|sed 's,[^-]*-[^-]*-,,') broadcom-sta-dkms

# Unload conflicting modules
% modprobe -r b44 b43 b43legacy ssb brcmsmac

# Load the wl driver
% modprobe wl
```

+ 15
- 0
www/wiki/Fedora-Dependencies-for-Ubuntu-Projects.md View File

@@ -0,0 +1,15 @@
# Fedora Dependencies for Ubuntu Projects

A lot of projects list Debian/Ubuntu dependencies and not Fedora/RHEL ones, so these are where I keep my list of Fedora dependencies for such projects.

# Game Engines

## Engo (Go)

Engo is a 2D game engine written in Go.

```bash
sudo dnf install openal-soft-devel mesa-libGLU-devel freeglut-devel \
libX11-devel xrandr-devel libXcursor-devel libXinerama-devel libXi-devel
go get -u engo.io/engo
```

+ 70
- 0
www/wiki/Fedora-NetworkManager-OpenVPN.md View File

@@ -0,0 +1,70 @@
# Fedora NetworkManager OpenVPN

I set up an OpenVPN server on my Raspberry Pi using the instructions [here](http://readwrite.com/2014/04/10/raspberry-pi-vpn-tutorial-server-secure-web-browsing).

Setting it up in Fedora via the NetworkManager Applet wasn't extremely straightforward.

## Install OpenVPN Support

```bash
sudo dnf -y install NetworkManager-openvpn NetworkManager-openvpn-gnome \
openvpn
```

## NetworkManager Logs

The log is the most useful place to look when the OpenVPN connection fails, as the only thing the applet tells you is that it failed but it doesn't elaborate.

Check the logs with `journalctl`:

```bash
sudo journalctl -u NetworkManager
# -or-
sudo journalctl -t nm-openvpn
```

## SELinux: Where to place your certificates

SELinux will prevent access for nm-applet to read your certificates unless you put them in the right location.

Put your OpenVPN cert/key files in `~/.cert/`

## Make the .ovpn file importable

The Raspberry Pi OpenVPN tutorial above ends with creating a `.ovpn` file for the client certificate. This file is importable and works just fine in OpenVPN Connect for Android (and presumably other apps), but the NetworkManager Applet doesn't import it correctly: you'd still have to browse and pick all your cert/key files/etc.

If you open the ovpn file in a text editor you see it has all the certificates and keys included in-line in the file, like:

```
<ca>
-----BEGIN CERTIFICATE-----
...base64 encoded junk...
-----END CERTIFICATE-----
</ca>
```

To make it compatible with the NetworkManager Applet, keep all these certs in separate files (example, `ca.crt`) and edit the config file to refer to them by name.

Full file example:

```bash
client
dev tun
proto udp
remote ${YOUR_VPN_SERVER} 1194
resolv-retry infinite
nobind
persist-key
persist-tun
mute-replay-warnings
ns-cert-type server
key-direction 1
cipher AES-128-CBC
comp-lzo
verb 1
mute 20
ca ca.crt
cert ${Your_User.crt}
key ${Your_User.key}
tls-auth ta.key
```

+ 93
- 0
www/wiki/Fedora-on-Macbook.md View File

@@ -0,0 +1,93 @@
# Fedora on Macbook

Notes on running Fedora Linux on a Macbook (Air 2015).

# What Doesn't Work

Most functionality works out of the box: media keys on the keyboard (keyboard backlight brightness, playback keys, volume control).

Things that don't work: monitor backlight brightness keys (the keyboard brightness DOES work, though) and the Facetime HD camera, which identifies itself as a PCI device rather than USB and there are no generic webcam drivers for Linux that can work with a PCI webcam.

# Installation/Boot

Make a Fedora USB stick like usual. Hold Option when booting the Macbook and choose the Fedora Media from USB to boot.

I found that rEFIt/rEFInd are no longer necessary when installing Fedora. Just free up some partition space and allow Fedora to automatically create a partition layout, and it sets up a /boot/efi HFS+ partition automatically to make the OS bootable. Manual instructions for this are available for Debian and Arch, etc.

GRUB installs itself and things work just like any other PC. In Fedora (as of v22), the GRUB menu lists a couple entries for Mac OS X (32-bit and 64-bit) but neither one works. Booting Fedora works though.

To boot OS X, hold down the Option key during boot and pick OS X from the firmware bootloader. If you want OS X to be the default OS you can pick it as the System Disk from within OS X's settings. In this case, to boot Linux you'd hold Option on boot and choose Fedora, which takes you to GRUB and then you boot Fedora from there.

# Backlight Brightness

See <http://sh.kirsle.net/mb-brightness> for this. The keyboard brightness keys (in Xfce at least) don't work; they show a brightness graph but the actual brightness doesn't change. This script uses root to write brightness values to files in /sys.

# Battery Saving

Use `powertop` to tune the battery. The easiest thing is to just make it run `--auto-tune` automatically on boot via systemd.

The `powertop` package comes with a systemd service you can enable. If you don't have one for some reason, create `/etc/systemd/system/powertop.service` with these contents:

```ini
[Unit]
Description=Powertop tunings

[Service]
Type=oneshot
ExecStart=/sbin/powertop --auto-tune

[Install]
WantedBy=multi-user.target
```

And enable the service with `sudo systemctl enable powertop.service`

Other things to mess with are `powertop --calibrate` and `powertop --html`

With this you can typically squeeze 7+ hours of battery life out of the 13" Macbook Air 2015 (which gets about 12 hours battery life under OS X, but this is possibly the best you can do with non-Apple software on a Macbook).

# Keyboard Tweaks

This section provides some tips on changing keyboard settings. All of these are temporary (they don't persist across reboots), and making them permanent varies from distro to distro. [Arch Linux](https://wiki.archlinux.org/index.php/Apple_Keyboard) and [Ubuntu](https://help.ubuntu.com/community/AppleKeyboard) make them permanent by adding configs to `/etc/modprobe.d/hid_apple.conf` and maybe rebuilding the initramfs (Fedora uses `dracut` and this whole method *does not work*).

The way to make these changes permanent in Fedora is to use systemd's `rc.local` compatibility service. This works as of Fedora 22.

Create a shell script at `/etc/rc.d/rc.local` with the commands below for the features you want to enable. For example:

```bash
#!/bin/bash
echo 0 | sudo tee /sys/module/hid_apple/parameters/iso_layout
echo 1 | sudo tee /sys/module/hid_apple/parameters/swap_opt_cmd
```

Make the script executable (`sudo chmod 0755 /etc/rc.d/rc.local`) and reboot.

## Tilde/Backtick Key

The keyboard layout on the Macbook maps the tilde/backtick key to `<` and `>` under Linux. The fix that works for me is:

```
$ echo 0 | sudo tee /sys/module/hid_apple/parameters/iso_layout
```

## Swap the Command and Option Keys

By default the Option/Alt key is mapped as Alt in Linux, and the Command key is mapped to Super (equivalent to the Windows key on a PC). To swap these the other way around, so e.g. Command+Tab brings up the window switcher instead of Alt+Tab (as it does under Mac OS X), do this:

```
$ echo 1 | sudo tee /sys/module/hid_apple/parameters/swap_opt_cmd
```

## Function Keys

By default if you want to input a function key (F1-F12) you have to hold Fn and press the key you want. Otherwise those keys map to the media keys when pressed. You can swap this behavior backwards (sort of like a `Fn Lock` feature of some PCs), so that the keys will input their function key when pressed and you need to hold Fn down to input the media key.

```bash
# FnLock ON
$ echo 2 | sudo tee /sys/module/hid_apple/parameters/fnmode

# FnLock OFF
$ echo 1 | sudo tee /sys/module/hid_apple/parameters/fnmode
```

If you echo `0` into it, it effectively *disables* the `Fn` key. So pressing `Fn+F11` is the same as pressing `F11` and this renders the media functions inaccessible.

+ 7
- 0
www/wiki/FollowingSync-Privacy-Policy.md View File

@@ -0,0 +1,7 @@
# FollowingSync Privacy Policy

Following-Sync is an Instagram app that helps you re-synchronize your following list, to unfollow profiles who do not follow you back.

The API server stores **no** data from Instagram. The Instagram access token is stored in a client-side cookie, and all API actions are done on-demand.

The app only requests the relationship permission, to be able to query and modify your following list.

+ 13
- 0
www/wiki/Go-QT5-Installation.md View File

@@ -0,0 +1,13 @@
# Go QT5 Installation

How to install everything needed for `go-qml` (QML for QT5).

<https://github.com/go-qml/qml>

# Fedora Dependencies

* qt5-qtbase-devel (Base QT5 development libraries)
* qt5-qtdeclarative-devel (provides `Qt5Quick.pc`)
* qt5-qtquickcontrols (provides `QtQuick.Controls` and `QtQuick.Layouts`)

And you should be able to `go run` the various examples in the git repo.

+ 49
- 0
www/wiki/IPv6-on-Linux.md View File

@@ -0,0 +1,49 @@
# IPv6 on Linux

Notes on setting up IPv6 support on Linux -- particularly for setting a static IP address (DHCP is usually pretty easy as long as your router is handing out v6 addresses!)

If you're a newbie to IPv6 I suggest you read up on it first. Some quick highlights:

* An IPv6 address is a 128-bit address split into 8 octets of 4 hexadecimal characters each. They look like e.g. `2606:1234:5678:dc00:c646:19ff:fe20:c4e1` but they can be truncated shorter if a sequence of octets evaluates to 0.
* Example: `2606:1234:5678:dc00::ABCD` is equivalent to `2606:1234:5678:dc00:0000:0000:0000:ABCD`
* Your ISP assigns you a 64-bit prefix. This means the first 4 octets of your address space are out of your control. The latter 4 octets you can assign as you wish.
* To keep your addresses as short as possible you may want to only assign the final octet to your devices, example `2606:1234:5678:dc00::ABCD`

## Finding out your information on DHCP

It's easiest to get a DHCP lease for a v6 address first, gather information from it, and then set a static one.

First, to find your IPv6 address you can use either `ifconfig` or `ip -6 address show`, example:

```bash
$ ip -6 address show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
3: wlp3s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qlen 1000
inet6 2606:1234:5678:dc00:c646:19ff:fe20:c4e1/64 scope global mngtmpaddr dynamic
valid_lft 3600sec preferred_lft 3600sec
inet6 fe80::c646:19ff:fe20:c4e1/64 scope link
valid_lft forever preferred_lft forever
```

In this example `2606:1234:5678:dc00:c646:19ff:fe20:c4e1` is our DHCP IPv6 address. If you only have an address beginning with `fe80` or another link-local prefix, then **you do not have a public IPv6 address** and shouldn't bother with the rest of this page.

Then, you need to find your router's IPv6 address (the gateway address):

```bash
$ ip -6 route show default
default via fe80::16cf:e2ff:fea6:2047 dev wlp3s0 proto ra metric 1024 expires 1798sec hoplimit 64 pref medium
```

Here the router's address is the link-local `fe80::16cf:e2ff:fea6:2047` address. This one confused me for a second, because my router got *its own* public IPv6 address from the ISP, but that address is not the gateway address; the link-local one is!

## Setting a Static IPv6 Address

If you're using a graphical Linux desktop you can configure the static IP address in the Network Manager applet.

![Screenshot](/creativity/articles/ipv6-nm-applet.png)

Just pick the "Manual" method, Add an IP address, enter in the static IPv6 address you want, the prefix is usually going to be 64 (you control 64 bits of the 128-bit address), and paste the router's link-local address in the Gateway.

The DNS servers I use are Google's Public DNS, which have the IPv6 addresses of `2001:4860:4860::8888, 2001:4860:4860::8844`

+ 26
- 0
www/wiki/Main-Page.md View File

@@ -0,0 +1,26 @@
# Main Page

Welcome to the Kirsle.net Wiki!

This mostly just serves as a place for me to quickly write down random ideas and make quick pages that I can edit from my website's front-end. It's like the GitHub Wikis, except it's hosted on my own server and I wrote the code for it myself. ;)

See [the index](/wiki/_pages) for the list of all available pages in this wiki.

# Technical How-To's

* [Python cocos2d Installation](/wiki/Python-cocos2d-Installation)
* [Fedora on Macbook](/wiki/Fedora-on-Macbook)
* [nginx configs](/wiki/nginx-configs)

# Ideas and Notes

* [Minecraft Clone](/wiki/Minecraft-Clone)
* [Minecraft vs Terraria Account Models](/wiki/Minecraft-vs-Terraria-Account-Models)
* [Things Learned with jQuery Mobile](/wiki/Things-Learned-with-jQuery-Mobile)
* [Built with RiveScript](/wiki/Built-with-RiveScript) - Collection of known RiveScript projects
* [Protocol Buffers Protocol](/wiki/Protocol-Buffers-Protocol)
* [Optimize RiveScript](/wiki/Optimize-RiveScript) - So it can support Alice-sized brains

# Personal Reference

* [Publishing RiveScript Modules](/wiki/Publishing-RiveScript-Modules)

+ 57
- 0
www/wiki/Minecraft-Clone.md View File

@@ -0,0 +1,57 @@
# Minecraft Clone

Just a brain dump on my ideas for a Minecraft-style game.

# Minimum Requirements

The game should fix all of the shortcomings of Minecraft and do everything better. See [blog post](https://www.kirsle.net/blog/entry/minecraft-clones). Briefly:

* Cubic chunks (16x16x16 instead of 16x16x256) allowing for infinitely tall and deep worlds in addition to infinitely wide worlds.
* Better chunk saving: only save modified blocks to disk. The game should always re-generate chunks and apply changes on top. The chunk header would include the terrain algorithm version used, so on drastic algorithm changes, old chunks can still use the old algorithm.

# Terrain Generation Milestones

Terrain generation will probably be the most difficult part. Briefly, these would be the major milestones in terrain:

## Superflat (infinite)

A superflat world would probably be the first world type, as it would be the easiest to generate. It would work exactly like in Minecraft. Since getting the cubic chunk system and other fundamental design goals sorted out will be the top priority in the beginning, an infinitely generating superflat world would be ideal early on. It could also be used to test the chunk saving format (e.g. initially, nothing should be saved to disk until you start placing and destroying blocks) and the world could easily scale infinitely in all directions, since no special terrain algorithms are needed.

## Limited World (MVP)

This is the minimum viable product/the goal for terrain generation for a first "official" release of the game.

The world would be similar in principal to how Terraria does it. The world would be generated *in advance* of starting the game, and the terrain generation algorithm would fully populate *and save* all chunks up front. This will take some time but that's acceptable as the world will only be generated once and all blocks saved to disk.

The world would still be large (like Terraria), and all essential terrain features would be included. The algorithm would be deterministic (one seed value would always generate the same world), but it would eliminate a TON of complexity surrounding using a procedurally generated infinitely large world algorithm. If the game has e.g. a stronghold like in Minecraft or a dungeon like in Terraria, it would be sure to be generated inside of the limited world size.

Mechanic ideas for world generation:

* Do it in multiple phases. During world set-up, we have all the time in the world (to be fair, Terraria takes its time generating a Large world type, too).
* Phase 1 could be simple topography using a Perlin noise height map
* Phase 2 could be biome assignments (find a noise + contrast algorithm that creates a "bubbly" texture, and assign each enclosed bubble a random but deterministic biome style). It would be a priority that every biome exists in at least one place, since the world is limited, so some manual overriding (deterministically) can be added to ensure this.
* The cubic chunk system would allow for altitude-dependent biomes, but it could be kept simple initially and have Minecraft style biomes, where a biome applies to the full vertical height.
* Some height specific biomes would be generated on a second pass, for some special ones i.e. Sky Islands and Mushroom Caves.
* Phase 3 could be generating cave systems and ravines and such, possibly in multiple passes.
* Phase 4 would be structures of varying kinds (stronghold/dungeon, loot structures, etc.)

The point is that the *finite* world size means terrain generation can be made simpler, as the game never has to query "which blocks should be at this random chunk?" and worry about getting a consistent response to that question.

## Infinite World

If I find a way to procedurally generate an infinitely large world (particularly one that plays nice with the cubic chunk system; easier said than done), it would act as an extension of the Limited World type. For example, all *essential* structures (stronghold/dungeon) would still probably generate within a certain radius of the spawn point. This is consistent with how Minecraft does it, anyway.

Some challenges to this:

* Like in Minecraft, the game should be able to ask for any random chunk in the world, and get a consistent response every time as to what blocks should be in that chunk.
* For basic terrain generation based on noise algorithms, this may be easy if the mathematical functions could accept X/Y/Z coordinates and return a fast answer.
* For more complicated things, i.e. cave systems it may be more complicated.

# Random Feature Ideas

This is getting way ahead of myself, but a list of nice-to-have's in a Minecraft inspired game:

* Features to borrow from Terraria:
* Tree felling: break the trunk and the whole tree "explodes" as dropped log items, instead of you needing to mine every. individual. block.
* Their crafting system is better than Minecraft's. In Terraria, your "crafting menu" just lists all possible things you can craft, *based on your current inventory and what types of crafting stations you are standing adjacent to.* This helps with in-game discovery of available recipes while still keeping an air of mystery by not showing the player up front that they can build a crafting table and an ender chest.
* Once momentum picks up, add content, content, and more content! Terraria has a million billion types of weapons and armor and all kinds of other things, to the point that in Terraria, your player character is separate from your world and you can keep your inventory between worlds.

+ 82
- 0
www/wiki/Minecraft-vs-Terraria-Account-Models.md View File

@@ -0,0 +1,82 @@
# Minecraft vs Terraria Account Models

A comparison of the account/player models between Minecraft and Terraria and some ideas for things that could be done in my own game of that nature.

## Minecraft: Central Account System

Minecraft uses a centralized account system. When the game launcher starts up, it pings the Minecraft Central Account Server (CAS) to authenticate your player username and password and the client gets a session token from CAS.

When logging into a multiplayer server, your client sends your username and the session token to the server. The server can then send that information to CAS and verify that you are indeed who you say you are.

If the CAS goes down for good, players who already have the game can still play it forever in local "offline mode" (single player), including opening their game for LAN play. Server owners can continue running servers by switching them into offline mode, which removes any user authentication (better remove your list of operators because impersonation becomes possible!)

**Advantages:**

* Servers can have whitelists and operator lists based on name, and nobody can impersonate an admin user.

**Disadvantages:**

* If the CAS goes down, nobody can log into any servers (unless those servers switch to offline mode).

## Terraria: No CAS

With Terraria, once you've bought and downloaded the game, the company could disappear completely and not impact the players at all. There is no CAS which means no authentication with online multiplayer.

**Advantages:**

* Obviously, no phoning home and no central servers to depend on.
* Company can go under without affecting players at all.

**Disadvantages:**

* No central authority to vouch for player identities, which means servers can't keep admin lists based on names. Authentication has to be done in-game, after joining the server, by using custom server commands like `/login` and a server-specific password, etc.

## Player Models

In Minecraft, players are merely avatars with a name and a skin. Every world you join (including multiplayer worlds) start you with a clean slate: no inventory, no armor, no weapons, etc., and you only get one avatar per (paid) Mojang account. This is good for Minecraft because there's not a whole lot of *content* to the game (for example, armor options: Leather, Iron, Gold, Diamond.. and that's it). If you could keep your enchanted diamond armor between worlds the game would be pretty boring.

In Terraria, player avatars are their own separate entity from worlds, and an avatar has its own inventory and stats that are stored with the avatar. When switching from world to world, your avatar's inventory is kept intact. This even applies to joining multiplayer servers (although a lot of servers in the wild run on a custom server program that enforces server-side inventory).

In single player this lets you have a "New Game Plus" type experience, where you could play on one world until the end-game, and then start a brand new one and keep all your high level armor and weapons. There is a metric ton of content in Terraria and some of it is very difficult to get, so being able to keep it forever once you've obtained it is nice.

The down side is obvious: griefing on multiplayer. You could beef up your avatar on single player (or simply use an offline inventory edit hack) and then join multiplayer with a bunch of noobs and wreck up the place with your high level gear.

## Network Protocols

As if Terraria letting you join multiplayer using your local player avatars wasn't ripe enough for abuse, the network protocol trusts the client way too much. For example, the multiplayer server has no authority to tell a client *what the client's HP is*, among pretty much everything else. A hacked client can simply deny that it took damage. The server is a dumb proxy layer to enable clients to communicate but it does nothing to prevent cheating.

The advantage is that this is easy to program. The server doesn't need to run any game logic, just provide channels of communication for the clients to exchange game state information. The disadvantage obviously are the cheaters. I think on the whole, though, this makes it so Terraria is designed to be played with smaller groups of trusted friends rather than running public open-to-the-world servers with hundreds or thousands of connected players.

Minecraft on the other hand does most of the rule enforcements, physics calculations, etc. on the server side. The client mostly just communicates what actions they're taking and where they're walking to. The client does do some predictive processing, for example if you break a block the client will send to the server that the block was broken, but instead of waiting for the acknowledgement, it will render the block broken on the client. In case of lag or the server disagreeing that the block was broken, the client puts it back where it belongs.

This model is more tricky to develop and prone to bugs and lag-related glitching, but goes a long way toward thwarting hackers.

## My Ideas

If I were to make a game in the style of Minecraft/Terraria, I would go with some kind of hybrid approach:

### Central Account System

I would have one of these, like Minecraft. But to alleviate potential downtime of the CAS server, I would implement long-lived session tokens.

Briefly, a client should only *have* to communicate with the CAS server one time per computer/install of the game. It would be given a long-lived session token, with a public and private component.

When authenticating with a multiplayer server it would send the player's name and the public session key, which the server could verify with the CAS. This part would work just like in Minecraft. But additionally, the server can remember the result of this transaction so that the same player can join later even if the CAS is down at that time.

Now the issue would be the possibility of a hacker intercepting the session token and then impersonating the user. To solve that, the multiplayer server can verify the client by asking it to sign a random challenge by using its private session key (which only the client would have).

The player could de-authenticate old sessions through the CAS's website, in case their computer crashed completely and they couldn't delete the old session keys from it first. This would be akin to revoking a GPG key.

To handle the case of keys being compromised (read: stolen), multiplayer servers could periodically check with the CAS server to find any information about keys that had been revoked by the user. If the CAS was unreachable, the server would try again some other time.

And to handle the case that the CAS servers go down permanently, never to return: I would plan to release a patch that removes the authentication requirement from the game.

### Player Avatars

I like Terraria's approach of inventories coming with the avatars to multiplayer servers. I would design the game for the use case of having small groups of trusted friends to play online with.

However, I would also build features into the vanilla multiplayer server to enforce server-side inventory. It would be off by default.

### Network Protocol

I would probably go with a "simple proxy" model for the server, at least to start out with, because it's simpler. Again, the target use case would be for servers to be small and trusted. Additional monitoring code could always be added to the server to passively watch what's happening between players and apply heuristics to detect cheating (for example, making sure players can't travel too fast to detect teleport hacks, or see if a client is denying that their HP is being dropped, etc.)

+ 105
- 0
www/wiki/Optimize-RiveScript.md View File

@@ -0,0 +1,105 @@
# Optimize RiveScript

# Problem

None of the RiveScript modules can effectively handle a brain the size of Alice's. The Golang version is able to *load* Alice the fastest (< 1 second) whereas the others take closer to 20+ seconds. However, when actually fetching a reply they all take about 15 seconds.

The root problem is probably in the sorted reply structure, which looks generally like this:

```javascript
sorted = {
"random": [ // topic name
["how are you", pointer ], // triggers ordered by priority
["hello bot", pointer ],
["*", pointer]
]
};
```

Under a topic, all triggers are sorted in their optimal sort order, which is generally: atomic triggers with the most number of words are first, less specific triggers later, least specific last. But triggers with custom priorities (`{weight}` tags, or from a topic that inherits other topics, etc.) always come before lower priority sets of triggers.

In the Alice reply set this means there's about 68,000 triggers in one giant array under the "random" topic, so the code has to scan through several tens of thousands of triggers when finding a match.

# Alicebot Program V

Alicebot Program V is an AIML bot and it stores patterns in a more efficient way: it separates the first word of the pattern away from the rest. When looking up a response for the user, it can then use the first word as a dictionary key (there's a relatively small set of distinct first words), and then have a much simpler array of triggers to look at. Example:

```perl
# The following patterns are represented here:
# ITS *
# ITS BORING
# ITS FUN
# ITS GOOD *

$data = {
aiml => {
matches => {
'ITS' => [
'* <that> * <topic> * <pos> 17818',
'BORING <that> * <topic> * <pos> 17819',
'FUN <that> * <topic> * <pos> 17820',
'GOOD * <that> * <topic> * <pos> 17821',
],
},
},
};
```

My [blog entry](/blog/entry/alicebot-program-v) has more details. The `<pos>` refers to an array index where the pattern's details are; in the more recent RiveScript implementations (CoffeeScript and Go) we keep pointers with the triggers in the sorted structure so we don't need to worry about that.

# Complex Triggers

At first glance a Program V style way of sorting triggers looks good, but in RiveScript triggers are much more complicated and "regexp-y", for example:

`(what is|what was) your name`

These things would still need to be taken into account. Also the relative priority of each trigger via `{weight}` and topic inheritance.

# Possible Solution

Change the sort structure to look more like this:

```javascript
sorted = {
"random": [ // topic name
[ // these arrays are for priority level, higher on top
[
"hello", // first word
[ // list of triggers under that word
["hello bot", pointer]
]
],
["how", [ ["how are you", pointer] ],
["*", [ ["*", pointer] ]
]
]
}
```

So the logic for matching a trigger would be along these lines:

```python
user_first_word = re.split(r'\s+', message)[0]

for priority in self._sorted.topics[topic]:
for first_word in priority:
# this next line would actually be a regexp for * triggers, etc.
if user_first_word == first_word[0]:
# Their first word matches! Look through all the triggers for this word.
for trigger in first_word[1]:
# Again this would be a regexp in reality
if message == trigger[0]:
# Have a match!
matched = trigger[1]

# now `matched` points to the trigger's details for the
# replies, conditions, etc.
```

For finding the first words, a function like `getFirstWords(trigger)` could be added that returns one or multiple first words.

* If the trigger begins with `[` or `(`, return the first words of all the regexp-y parts.
* Example: `(what time|when) is it` would return `["what", "when"]`
* Example: `how are you` would return `["how"]`
* The first words would be sorted by length, with words like `*` at the bottom.
* All triggers that share a first word get placed in an array under that word, sorted in the normal order (most optimal matching first).

+ 58
- 0
www/wiki/PowerTOP-and-USB-Autosuspend.md View File

@@ -0,0 +1,58 @@
# PowerTOP and USB Autosuspend

Some of this was touched on at [Fedora on Macbook](/wiki/Fedora-on-Macbook).

PowerTOP is a useful tool to get more battery life out of a laptop. It makes the difference of several hours of battery life, so it's a useful thing to install.

## PowerTOP

Install and enable it:

```bash
sudo dnf install powertop
sudo systemctl enable powertop.service
sudo systemctl start powertop.service
```

The service runs `powertop --auto-tune` which automatically sets most tunables to their "Good" state, for maximum battery life.

But sometimes it doesn't work well, especially with USB mice.

## USB Autosuspend

It perplexed me for a while why my USB mouse on a laptop would deactivate itself randomly (its LED turns off, and the mouse doesn't work until you click one of the buttons, and then it will power itself off again after a couple minutes...). Unplugging the USB mouse and plugging it back in would fix the problem and it would no longer auto-suspend itself. I was checking `/var/log/messages` and all the usual suspects until I realized it was PowerTOP that was the problem.

When `powertop --auto-tune` gets run, every USB device attached at the time gets the `auto` power management profile set, meaning they deactivate themselves to save power. On a keyboard or most other devices that's probably fine, but a mouse isn't. My workarounds have been to either *not* connect my USB devices until after my laptop boots into my desktop environment, or to unplug and replug the USB mouse after the fact.

I can't find a good "official" way to work around the problem. Google suggests editing things in `/etc/laptop-mode` but that isn't a thing in Fedora, and others have suggested setting up udev rules for your USB device, but that doesn't work either. I even found this [Power Management Guide][1] for Fedora 19 which pointed me to `powertop2tuned` to override powertop rules, but that didn't help me either.

My hacky work-around: make a shell script that starts on user log-in, that undoes PowerTOP's change to the USB device.

1. Run `sudo powertop` and go to the Tunables tab.
2. Find the entry like "Autosuspend for USB device Microsoft 3-Button Mouse with IntelliEye(TM) [Microsoft]", and toggle it into a "Bad" state (meaning it doesn't auto-suspend now).
3. When toggling it, PowerTOP shows a message of what it did to toggle it, which looks like:

`echo 'on' > '/sys/bus/usb/devices/2-3.2.3.1/power/control'`

4. Create a shell script that does that job. I made mine at `~/bin/powertop-fix`:

```bash
#!/bin/sh

# Disable USB auto-suspend for my mouse on startup
sleep 5;
MOUSE="/sys/bus/usb/devices/2-3.2.3.1/power/control";
if [ -f "$MOUSE" ]; then
echo 'on' > $MOUSE;
fi
```

5. Edit your sudoers file to allow running that script with no password:

```bash
kirsle ALL=(ALL) NOPASSWD: /home/kirsle/bin/powertop-fix
```

6. Make `sudo /home/kirsle/bin/powertop-fix` start automatically using your desktop's session management settings (e.g. Xfce Sessions and Startup -> Application Autostart).

[1]: https://docs.fedoraproject.org/en-US/Fedora/19/pdf/Power_Management_Guide/Fedora-19-Power_Management_Guide-en-US.pdf

+ 58
- 0
www/wiki/Protocol-Buffers-Protocol.md View File

@@ -0,0 +1,58 @@
# Protocol Buffers Protocol

Notes on how one could use Google's [Protocol Buffers](https://developers.google.com/protocol-buffers/) to create an arbitrary TCP stream-based network protocol. For sake of example, use a simple chat-based messaging protocol.

Like many protocols your messages would have a "type" (or action) and associated data.

## Protobuf Messages

```protobuf
// Login packet, to authenticate with the server
message Login {
required string username = 1;
required string password = 2;
}

// Register packet, to sign up
message Register {
required string username = 1;
required string password = 2;
required string email = 3;
}

// Packet for holding a chat message, this is symmetrical
// meaning the client sends this to the server for an outbound
// message and the server sends it to the client for inbound
message Chat {
required string from = 1;
required string to = 2;
required string body = 3;
}
```

## Handshake Flow

It's good practice to version your network protocols, so the first thing the client should do is send a relatively boring message saying the version number. This part of the protocol would **never** change. Maybe something like (`>>>` is the client to the server and `<<<` is the server's response):

```
>>> VER 1
<<< VER 1
```

The server responds with the same version number to acknowledge its support. You should also plan other possible responses (such as communicating to the client that its version number is obsolete and that it should update its software, in case you need to make a massive overhaul of the protocol in the future).

## Packet Format

After the initial version handshake the client and server switch to protocol buffers for all messages. Each type of message has its own protobuf message schema, so it needs some way to know which type of message it's sending and how long it is (so it knows when one message ends and the next packet begins).

So each packet would consist of a format like:

1. [At least*] one byte to indicate the Message Type
2. A fixed size packed integer to indicate the length of the protobuf message (i.e. a 32-bit integer, or 4 binary bytes... or you could probably get away with 16-bit and have a max size of 65K for each protobuf message)
3. The binary-encoded protobuf message.

\* The Message Type byte should reserve one bit for extensibility: for example, reserve the highest bit. This means that initially you can only have up to 128 types of messages in your protocol (using Message Type IDs from 0 to 127 (`01111111`), and if you run out of space you can set the highest bit to `1` (so, `128` for `100000000`) and that could instruct your protocol to read the *next* byte to continue finding the Message Type. [UTF-8 encoding](https://www.youtube.com/watch?v=MijmeoH9LT4) works on a similar principal. The highest bit of every byte would be reserved for extending the length, so you don't run out of space as the protocol grows.

If you found you need to extend the length limit (part #2 of the packet format), you could handle this by incrementing the Protocol Version Number from the handshake. Client and server could both switch to larger integers when using the new protocol.

The mapping between Message Type values and your protobuf messages would be done on your own. For example you could arbitrarily say that Message Type 1 is Login, 2 is Register, 3 is Logout, etc.

+ 74
- 0
www/wiki/Publishing-RiveScript-Modules.md View File

@@ -0,0 +1,74 @@
# Publishing RiveScript Modules

My personal notes on how to distribute releases of RiveScript to various language module repositories.

# Python

If this is the first time publishing from a new computer, create `~/.pypirc` with your PyPI credentials:

```
[distutils]
index-servers=pypi

[pypi]
repository = https://pypi.python.org/pypi
username = $USERNAME
password = $PASSWORD
```

Make sure everything's ready to go (version numbers incremented, documentation rebuilt, etc.) and run this command to create all the distributable items:

```bash
# Install the requirements to get the bdist_wheel command
$ pip install -r requirements.txt
$ python setup.py sdist bdist_wheel
```

This generates files in the `dist/` folder:

* `sdist` creates the source tarball
* `bdist_wheel` creates a portable pre-built wheel file (the new "egg")
* `bdist_rpm` creates an RPM (not necessary to upload this to PyPI)
* `bdist_wininst` creates a Win32 installer (not necessary to upload this to PyPI)

And to upload to PyPI:

```bash
$ pip install twine
$ twine upload dist/*
```

See also: <https://python-packaging-user-guide.readthedocs.org/en/latest/distributing/#packaging-your-project>

## Build RPM

See also: <https://fedoraproject.org/wiki/Packaging:Python>

To build the RPM for Fedora:

```bash
$ rpmbuild -ba python-rivescript.spec
```

# JavaScript

* Prepare it for NPM distribution:
1. Build distributable (compiled JS) files: `grunt dist`
2. Test an installation from a different directory: `npm install ../rivescript-js` and make sure it works.
3. From the rivescript-js folder, `npm login` if it's the first time on a new PC and `npm publish` to publish.
* Create release tarballs for GitHub binary distribution:
1. Build distributable files: `grunt dist`
2. Remove cruft: `rm -rf node_modules`
3. Go up a directory to create zip/tar files
* zip -r rivescript-js-1.1.2.zip rivescript-js -x '*.git*'
* tar -czvf rivescript-js-1.1.2.tar.gz rivescript-js --exclude .git

See also: [npm developers](https://docs.npmjs.com/misc/developers)

# Perl

1. `perl Makefile.PL`
2. `make`
3. `make dist`

Upload resulting tarball to [PAUSE](http://pause.perl.org/) for indexing on CPAN.

+ 22
- 0
www/wiki/Python-cocos2d-Installation.md View File

@@ -0,0 +1,22 @@
# Python cocos2d Installation

Notes for installing the cocos2d game framework in Python, from the ground up.

# Mac OS X

This is a very low level, ground-up list of steps. Skip some if you've already gotten some of these out of the way before.

1. Install homebrew
2. Install Python: `brew install python`
3. Install cocos2d: `pip install cocos2d`

Install was surprisingly simple (no need to get OpenGL dependencies? I did have Xcode installed, though).

# Python 3

Works in Python 3! OS X instructions:

1. `brew install python3`
2. `mkdir ~/.venv`
3. `pyvenv ~/.venv/sandbox`
4. `pip install cocos2d`

+ 74
- 0
www/wiki/Python-for-RiveScript-Go.md View File

@@ -0,0 +1,74 @@
# Python for RiveScript Go

It'd be nice if rivescript-go were able to parse and run Python object macros for RiveScript bots, by using the [Python C API](https://docs.python.org/3/c-api/index.html).

There are two projects I found so far: [sbinet/go-python](https://github.com/sbinet/go-python) and [qur/gopy](https://github.com/qur/gopy). They both only support Python 2 so far, but that will work for now.

I did some experimenting and came up with the following Go code that demonstrates the key pieces of functionality needed: dynamically parse a Python function, call the function giving it an array of string arguments, and retrieve the result of the function as a Go string.

```go
package main

import (
"fmt"
"github.com/sbinet/go-python"
)

func init() {
err := python.Initialize()
if err != nil {
panic(err.Error())
}
}

func main() {
// The source code of the python function we wanna be able to call.
pycode := `
def test(rs, args):
print "Test works"
return "Forwards: {}\nBackwards: {}".format(
" ".join(args),
" ".join(args[::-1]),
)
`

// The []string to use as the 'args' param to `def test()`
args := StringList_ToPython("Hello", "world")
defer args.DecRef() // Always do this so Python can count references well.

// To load the function you can simply eval the code in the global scope:
python.PyRun_SimpleString(pycode)

// Get the main module's dictionary so we can get a reference to our
// function back out of it.
main_module := python.PyImport_AddModule("__main__")
main_dict := python.PyModule_GetDict(main_module)
test_function := python.PyDict_GetItemString(main_dict, "test")

// The tuple of (rs, args) arguments to pass to the function.
// This tuple is the *args in Python lingo.
test_args := python.PyTuple_New(2)
python.PyTuple_SetItem(test_args, 0, python.Py_None)
python.PyTuple_SetItem(test_args, 1, StringList_ToPython("Hello", "world"))

// Call the actual Python function now. Functions return a *PyObject, and
// we can cast it back to a string.
returned := test_function.CallObject(test_args)
result := python.PyString_AsString(returned)

// Print the result of the function.
fmt.Println("Result:", result)
}

// StringList_ToPython is a helper function that simply converts a Go []string
// into a Python List of the same length with the same contents.
func StringList_ToPython(items... string) *python.PyObject {
list := python.PyList_New(len(items))

for i, item := range items {
python.PyList_SetItem(list, i, python.PyString_FromString(item))
}

return list
}
```

+ 12
- 0
www/wiki/Python-on-Mac-OS-X-Tips.md View File

@@ -0,0 +1,12 @@
# Python on Mac OS X Tips

# Installing Modules with pip

## cryptography

Depends on libffi and openssl: `brew install libffi openssl`

### Error: can't find openssl/aes.h

* Solution: `env LDFLAGS="-L$(brew --prefix openssl)/lib" CFLAGS="-I$(brew --prefix openssl)/include" pip install cryptography`
* Found at: <https://github.com/pyca/cryptography/issues/2350>

+ 7
- 0
www/wiki/Resetting-Modal-Bluetooth-Headphones.md View File

@@ -0,0 +1,7 @@
# Resetting Modal Bluetooth Headphones

* [Modal Bluetooth Headphones Documentation](http://storage.bestbuy.com/pacsales/resources/exclusive_brands/modal/PDF/MD-HPBT01_Bluetooth-Stereo-Headset-User-Guide_English.pdf)

In case the headphones freeze up and don't react to being turned on/off, the reset sequence is:

Press the **Talk**, **Play**, and **+** (volume up) simultaneously.

+ 32
- 0
www/wiki/RiveScript-Discussions.md View File

@@ -0,0 +1,32 @@
# RiveScript Discussions

Links to particularly interesting RiveScript discussions.

These are mostly GitHub issues that I wanted to keep track of easily after they're closed. I'll slowly add to this list as I dig up more interesting topics.

## Common Topics

These topics are so common that I use GitHub Issue labels to categorize them.

### Unicode (UTF-8)

* [JavaScript](https://github.com/aichaos/rivescript-js/issues?utf8=%E2%9C%93&q=label%3Aunicode)
* [Python](https://github.com/aichaos/rivescript-python/issues?utf8=%E2%9C%93&q=label%3Aunicode)
* [Perl](https://github.com/aichaos/rivescript-perl/issues?utf8=%E2%9C%93&q=label%3Aunicode)

See also [Unicode Problems](/wiki/Unicode-Problems) on my wiki.

### Async (JS)

Of the programming languages I've ported RiveScript to, JavaScript is the most heavily centered around asynchronous programming. As such it has a lot of discussions on GitHub and even its own wiki page there.

* [Promises for user variable functions](https://github.com/aichaos/rivescript-js/issues/146) - lots of backstory on async in RiveScript.
* [All Async Issues](https://github.com/aichaos/rivescript-js/issues?utf8=%E2%9C%93&q=label%3Aasync%20)
* [Asynchronous Support](https://github.com/aichaos/rivescript-js/wiki/Asynchronous-Support) (wiki)

## Performance and Scale

* [RiveScript Performance](https://github.com/aichaos/rivescript-js/issues/153) (JS)
* @atladmin had success with 20K replies and no performance dips.
* Discussion of getting databases involved (RiveScript won't do that).
* Compliments on RiveScript being high performance with no moving parts, unlike SuperScript (it depends on MongoDB and other deployment headaches).

+ 5
- 0
www/wiki/September-Links.md View File

@@ -0,0 +1,5 @@
# September Links

### YouTube

* [Why Women Are Stripey](https://www.youtube.com/watch?v=BD6h-wDj7bw)

+ 3
- 0
www/wiki/TMP.md View File

@@ -0,0 +1,3 @@
# TMP

<https://mediatemple.net/?hello=world>

+ 34
- 0
www/wiki/Tasker-OpenVPN-Connect.md View File

@@ -0,0 +1,34 @@
# Tasker OpenVPN Connect

Source: <https://forums.openvpn.net/topic13122.html#p33893>

VPN profile name, as shown in OpenVPN Connect: **example.com/autologin**
In the steps below, replace this with your own profile name.
For fields with no listed value here, leave the Tasker field blank (shows as "Optional").
In the first Extra field in the "To Connect" procedure below, the colon and everything in bold is written as intended.

To Connect:

* Misc > **Send Intent**
* Action: **android.intent.action.VIEW**
* Cat: **None**
* Mime Type:
* Data:
* Extra: **net.openvpn.openvpn.AUTOSTART_PROFILE_NAME: example.com/autologin**
* Extra:
* Package: **net.openvpn.openvpn**
* Class: **net.openvpn.openvpn.OpenVPNClient**
* Target: **Activity**

To Disconnect:

* Misc > Send Intent
* Action: **android.intent.action.VIEW**
* Cat: **None**
* Mime Type:
* Data:
* Extra:
* Extra:
* Package: **net.openvpn.openvpn**
* Class: **net.openvpn.openvpn.OpenVPNDisconnect**
* Target: **Activity**

+ 7
- 0
www/wiki/Test-Page.md View File

@@ -0,0 +1,7 @@
# Test Page

This is a page to test the wiki itself.

It has links to some pages such as the [Main Page](/wiki/Main-Page) and [Minecraft Clone](/wiki/Minecraft-Clone), and some that don't exist, like [Not A Page](/wiki/Not-A-Page) and [Another Missing Page](/wiki/Another-Missing-Page).

Some links can have aliases, like [the default page](/wiki/Main-Page) and the same for [a broken page](/wiki/Not-A-Page).

+ 74
- 0
www/wiki/Things-Learned-with-jQuery-Mobile.md View File

@@ -0,0 +1,74 @@
# Things Learned with jQuery Mobile

## No global top/bottom headers

Each individual page needs the markup for their own header/footer bars. I ended up using Perl Template::Toolkit to build the index.html from a template so I could reuse these components without copying/pasting them all over the place!

## Can't mix it with FontAwesome

Just doesn't work.

## Difficult to mix it with Knockout

This sort of thing doesn't work:

```html
<div data-role="navbar">
<ul>
<!-- ko if: logged_in() && my_username() == this_username() -->
<li>Edit Profile</li>
<!-- /ko -->
<li>Other stuff</li>
</ul>
</div>
```

jQuery Mobile does all the styling and rendering in JS, and if Knockout is hiding DOM elements at this phase, then jQuery Mobile can't style them and they'll look default and stupid.

## Transition performance sucks

Even when you set transitions to `none`, you get a white screen flicker between loading pages on Android 4.3

Most reliable fix as of late is to do this in two parts:

First, disable all transitions in the JS for Android only.

```javascript
$(document).bind("mobileinit", function() {
if (navigator.userAgent.match(/Android/i)) {
$.extend($.mobile, {
defaultPageTransition: "none"
});
}
});
```

Secondly, disable hardware acceleration, because the screen will still flash white between pages even with `none` set as the transition. Unfortunately you can't just use `phonegap run android` because it will always set `hardwareAccelerated=true` in the manifest file (even if you manually change it *and* mark the file immutable in Linux). Instead navigate to the Android platform and build it manually.

```bash
cd platforms/android
ant debug
# copy bin/Siikir-debug.apk to your phone or whatever
```

The PhoneGap Build service probably won't be able to do this for you, so for Android you'll always need to build locally.

Allegedly iOS has better hardware acceleration and shouldn't need you to disable transitions or acceleration, and PhoneGap Build should be able to be used. To be investigated.

**Better Solution**

http://www.fishycode.com/post/40863390300/fixing-jquery-mobiles-none-transition-flicker-in

Change from this:

```html
<meta name="viewport" content="width=device-width, initial-scale=1">
```

To:

```html
<meta name="viewport" content="width=device-width, user-scalable=no" />
```

Then HW accel can be used in Android and the flickering is gone too!

+ 35
- 0
www/wiki/Time-Travel.md View File

@@ -0,0 +1,35 @@
# Time Travel

This is my personal pet theory about time travel which would solve all the various time travel paradoxes that come up in popular fiction.

At some point I'll turn this into a blog post, with diagrams and/or animations.

-----

### Tape Analogy for Time

Visualize time as being frames on a strip of film, where each frame is a snapshot of the entire universe "frozen" in time, and time progresses when a "reader" is scanning through the frames, as though playing a movie in a VCR. In this analogy, you can imagine the "present" as the VCR reader head, and when the movie is playing, the film strip of time passes by the reader head "at the speed of time."

So if one could theoretically rewind the tape and hit play again, the "present" will have jumped back into the past and it plays through the frames of time again, at the normal speed of time. But for the sake of this theory, pretend that this never happens -- **THE** present never gets rewound or fast forwarded, and in the beginning, there is only one reader head: **THE** present.

### Build a Time Machine

And now you've built yourself a time machine and use it to jump back 50 years into the past. In our VCR analogy, when you jumped into the past, you've added a *second* reader head to the tape. So the tape is still playing at the normal speed of time, but now there are two readers: the present, which hasn't changed, and a new "past" reader that sees the tape 50 years after the present has seen it (the tape moves right-to-left, future-to-past, and a specific frame on the tape would scan past the "present" reader before it reaches the "past" reader further to the left).

So far so good: two readers, reading the same static history of the universe, but at different spots. Time is passing for each reader at the normal speed of time, they're just seeing two different slices of it from 50 years apart.

### Change History

Now suppose you, in the 50-years-ago "past", have done something to change history, such as killing your own grandma and trying to cause a time travel paradox -- you'd no longer be born, so you don't exist, but how did you time travel into the past then to make sure you won't exist?

So when history is changed, the "Past" reader head now begins to *write* to the tape, as though it's recording over top of the existing film.

However: the Present and Past heads are 50 years apart, and the tape moves at a constant speed for them both. The changes made by the Past head *will never catch up* to the Present. Anybody who was alive just one second into the future when you made a change, they would *never* see your change: it would *always* be one second in their past. Or put another way: you could time travel to the past, kill your grandma, and time travel back to your original present, and nothing at all would've changed. Everyone still lived and died like they always had and you apparently didn't cause any universe-shattering paradox after all.

The altered past would overtake history at the speed of time, one frame at a time. But it will never catch up to the original present.

### Change the Future?

If you had jumped 50 years into the future, now you've have a new "Future" read head that gets to peek at the film before the Present gets to see.

If you changed something here, your original Present *would* eventually see it after it catches up in 50 years. This is fine though; most time travel paradoxes come from rewriting the past, not the future.

+ 57
- 0
www/wiki/Unicode-Problems.md View File

@@ -0,0 +1,57 @@
# Unicode Problems

A collection of problems I've discovered with [Unicode](https://en.wikipedia.org/wiki/Unicode). Many of these came up in the context of RiveScript (my chatbot scripting language), because people wanna be able to support foreign language chatbots and RiveScript wasn't originally designed with that in mind and UTF-8 support was added later and these are some of the problems encountered.

tl;dr. of some problems:

* [You can't just do `toUppercase()` or `toLowercase()` without risking losing information](#case-folding)
* [Regular expressions are more complicated](#regular-expressions)
* [String equality comparisons are more complicated](#unicode-normalization)

Bug tickets concerning Unicode problems in RiveScript:

* [JavaScript](https://github.com/aichaos/rivescript-js/issues?utf8=%E2%9C%93&q=label%3Aunicode)
* [Python](https://github.com/aichaos/rivescript-python/issues?utf8=%E2%9C%93&q=label%3Aunicode)
* [Perl](https://github.com/aichaos/rivescript-perl/issues?utf8=%E2%9C%93&q=label%3Aunicode)

## Case folding

Some Unicode characters, when made upper- or lowercase, actually transform entirely into separate characters (sometimes multiple separate characters). When reversing the operation, you don't get the original text back as what you started with.

Example: the German symbol `ß`, when made uppercase, becomes two ASCII letters `SS`, which then become two ASCII letters `ss` when made lowercased again:

```javascript
"ß".toUpperCase().toLowerCase() !== "ß";
```

Example: in Turkish, the lowercase letter `i` when uppercased becomes a capital dotted letter `İ`, and when lowercased again becomes a dotless lowercase letter `ı`. This is only the case of your system locale is set to Turkish; if your system locale is English for example `i` becomes `I` becomes `i` again.

```javascript
// Works in English but not in Turkish, exact same code
"i".toUpperCase().toLowerCase() === "i";
```

For this reason I've never updated RiveScript to force-lowercase your triggers. People hated writing a lowercase word `i` for English triggers, but case folding can be a real problem.

[Case folding on W3.org](https://www.w3.org/International/wiki/Case_folding)

## Regular Expressions

Unicode makes dealing with regular expressions more difficult. If you ever found yourself writing a character class like `[A-Za-z0-9]`, this will not work if you expect Unicode. Neither will `\w` or `\W` depending on your programming language of choice.

If you want to make a regexp that matches, say, "letters but not numbers", you can't take a whitelist approach of only matching letters because there's too damn many of them. Instead you have to take a blacklist approach, and use a regexp like `[^0-9]` which will probably work in a lot of cases until somebody starts getting fancy foreign numeric symbols involved, and then the `[0-9]` character class won't work either.

In JavaScript land, regular expressions have trouble in Unicode for many reasons until ES2015 when they added the `/u` flag. Before that, the `.` metacharacter only matches characters in the BMP plane, but not astral characters, and `/u` makes it match astral characters. But, there's still a problem with using the `\b` word-boundary metacharacter, which still deals only with the boundaries between `[A-Za-z0-9]` and spaces and will break with Unicode symbols, even those in the BMP plane. [Unicode-aware regular expressions in ES6](https://mathiasbynens.be/notes/es6-unicode-regex).

Examples (GitHub Issues):

* [UTF-8 and Optionals](https://github.com/aichaos/rivescript-python/issues/37) (Python) - The `\b` word boundary doesn't work with some Unicode symbols.
* [regex doesnt work for UTF8](https://github.com/aichaos/rivescript-js/issues/147) (JavaScript) - The `\b` doesn't work with non-ASCII, and even using Unicode-aware regexps from ES2015+ doesn't change this behavior.

## Unicode Normalization

You can have multiple binary representations of exactly the same string of characters in Unicode.

For example, the accented letter `é` can be represented as either U00E9 Unicode accented letter é, or a combination of two characters, ASCII letter `e` and combining accent mark. Visually the two characters are exactly the same, but a string equality comparison would fail.

Fortunately there are Unicode normalization libraries available for most popular programming languages.

+ 105
- 0
www/wiki/nginx-configs.md View File

@@ -0,0 +1,105 @@
# nginx configs

I started migrating all my sites from Apache (mod_wsgi, mod_fcgid, mod_rewrite, etc.) over to nginx. For the interim phase I forwarded domains from nginx to Apache while slowly converting domains over.

The eventual goal is to get all sites running on Python/Flask/gunicorn and use supervisor ([details on that here](https://github.com/kirsle/rophako/wiki/Server-Configuration#nginx-configurations)) but in the mean time I had to set up PHP/CGI support in nginx.

These are some of those configs:

## mod_rewrite site with an index.cgi

Apache config using mod_rewrite:

```apache
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.cgi [L]
</IfModule>
```

Ported over to nginx:

```nginx
server {
server_name noah.is;
listen 80;

root /home/www/sites/noah.is;
index index.cgi index.html index.htm;

location / {
try_files $uri $uri/ /index.cgi;
}

# legacy CGI scripts
# <https://wiki.debian.org/nginx/FastCGI>
location ~ \.cgi$ {
try_files $uri $uri/ /index.cgi;
gzip off;
fastcgi_pass unix:/var/run/fcgiwrap.socket;
include fastcgi_params;
fastcgi_param SERVER_NAME $host;
}
}
```

## Legacy PHP and CGI Script Support

Kirsle.net used some PHP scripts ([Piwik Analytics](http://piwik.org/)) in the `/piwik` folder and some legacy CGI scripts (ttf2eot converter, etc.) in `/wizards` and these had to continue working while the rest of the site moved to gunicorn/supervisor/nginx.

Irrelevant aliases were removed from this snippet along with SSL settings (not very interesting).

```nginx
server {
server_name www.kirsle.net kirsle.net;
listen 443 ssl;

# (ssl configs removed)

index index.cgi index.html index.htm;

root /home/www/git/rophako;

location /static {
alias /home/www/public_html/static;
}
location /piwik {
alias /home/www/public_html/piwik;
index index.php;
}
location /wizards {
alias /home/www/public_html/wizards;
}

# uwsgi
location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_pass http://127.0.0.1:9000;
}

# run php scripts
location ~ \.php$ {
include fastcgi_params;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass unix:/var/run/php5-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /home/www/public_html$fastcgi_script_name;
}

# legacy CGI scripts
# https://wiki.debian.org/nginx/FastCGI
location ~ \.cgi$ {
try_files $uri $uri/ /index.cgi;
gzip off;
root /home/www/public_html;
fastcgi_pass unix:/var/run/fcgiwrap.socket;
include fastcgi_params;
fastcgi_param SERVER_NAME $host;
}
}
```

Loading…
Cancel
Save