Switching It Up – Personal Gaming History

Editor’s note: I know the author of this blog (he is not me), and I can assure
you that he has confided in me (I am not him), that this is indeed real
(we both know what is real).

Seriously though this is a truthful account as best as I can remember. Also it
helps if you do the Law & Order “DUN DUN” thing in your head before each section.

YEAR: 1982
AGE: 10
LOCATION: Best friends living room
RESOURCES: Atari 2600; Yars’ Revenge cartridge; milk refills
ATTIRE: Horizontal striped turtleneck, matching light blue Toughskins cords
MISSION: Beat Yars’ Revenge
RESULT: Mission failed; friendship maintained; blisters
NOTES: Yar can suck it
YEAR: 1983
AGE: 11
LOCATION: Strip mall arcade
RESOURCES: 5 dollar bill; half pack Big League Chew
ATTIRE: Horizontal striped turtleneck; matching light blue Toughskins cords
MISSION: Spend wad; win games; look cool; avoid inevitable dork move
RESULT: Mission failed; wad spent; games lost; coolness unachievable; dork move successful
NOTES: Got on the leader board of Food Fight once
YEAR: 1985
AGE: 13
LOCATION: Spare room
RESOURCES: Atari 800XL; BASIC cartridge and manual; limited patience
ATTIRE: Over-sized black button down; bright red cuffed pants; knock-off Air Jordans
MISSION: Create my own game
RESULT: Mission failed; rendered single line in 4 hours; high level cursing; surrender
NOTES: If only I had stuck with it
YEAR: 1991
AGE: 19
LOCATION: University of Massachusetts Amherst
RESOURCES: NES; Legend of Zelda cartridge; bong
ATTIRE: LL Bean rugby shirt; kakis
MISSION: Complete Zelda
RESULT: Mission accomplished; classes avoided; bad tone set for future
NOTES: On completion the game reset with new locations, then crashed and wiped
YEAR: 2001
AGE: 29
LOCATION: Trailer home living room
RESOURCES: Playstation; Final Fantasy 7 CD; cats
ATTIRE: Least dirty option
MISSION: Do all the things in FF7
RESULT: Mission accomplished; laundry undone; deep satisfaction
NOTES: Love me some Chocobo racing more than clean underwear
YEAR: 2005
AGE: 33
LOCATION: Trailer home kitchen
RESOURCES: Compaq Presario; Windows 98; wheat beer
ATTIRE: T-Shirt; cargo shorts
MISSION: Collect MAME ROMs of my favorite games
RESULT: Mission accomplished; laws broken
NOTES: Not proud and not sorry
YEAR: 2009
AGE: 37
LOCATION: Basement apartment
RESOURCES: Dell laptop; external monitor; ramen
ATTIRE: Same t-Shirt; same cargo shorts
MISSION: Run Quake3 at acceptable frame rate on Linux
RESULT: Mission accomplished; rail gun expertise gained
NOTES: Still have the Q3 tin box
YEAR: 2014
AGE: 42
LOCATION: Shop behind garage
RESOURCES: Caanoo portable gaming device; PC; misc cables; processed meat sticks
ATTIRE: T-shirt; shorts optional
MISSION: Load and run MAME ROMs
RESULT: Mission accomplished; accidentally smashed unrepairable screen 1 month later
NOTES: Loved this thing; I’m a moron
YEAR: 2015
AGE: 43
LOCATION: Living room
RESOURCES: PS4; Diablo 3; wife; couple kid-free hours
ATTIRE: Jammies
MISSION: Hook my wife on Diablo 3
RESULT: Mission accomplished; excess paragon levels achieved
NOTES: Good couch co-op games are awesome fun
YEAR: 2017
AGE: 45
LOCATION: Living room
RESOURCES: PS4; Rocket League; wife; ability to ignore kid
ATTIRE: Jammies
MISSION: Hook my wife on Rocket League
RESULT: Mission accomplished; still playing
NOTES: Good couch co-op games are awesome fun
YEAR: 2018
AGE: 46
LOCATION: Anywhere
RESOURCES: Nintendo Switch; life savings; wife’s blessing
ATTIRE: T-shirt; not even pretending to wear pants anymore
MISSION: Enjoy cool portable gaming system
RESULT: Mission accomplished; can’t stop playing Zelda
NOTES: The switch is awesome. I have a PS4 so not really looking for
a console but it rocks as a portable and the ability to use as a console is cool.

* DUN DUN *

I’ll Be ABAC

Remember the first time you used an admin flag to expose a special feature in your software? Pretty cool. Remember the first time you had to pee so bad during a game of hide-and-seek that you stood up behind the wood pile where you were hiding and went for it just as you were being found and the finder ran inside and told all the adults and they laughed at you which caused you to have pee anxiety for the rest of your life? Pretty not cool. Back to the point.

Admins and users, the halcyon days. Admin feature? No problem, just check the admin flag. Regular user? NO SOUP FOR YOU! Simpler times. Over the last few years I have been working on complex permissioning systems for web applications (among other things). Thankfully my Open Source software Cypht, being a sort of old school web app (but also new!), is just users and admins. And admins have very little special sauce. Easy-peasy.

However my bill-paying gig requires fine-grained access rights. In between tangents I’m going to talk about it, because like all shitty software tasks, it eventually ends up on my desk. And like all shitty software tasks, you will have to wade through some super boring domain knowledge to figure things out. We started as a team re-rolling the same system we half-ass implemented in the past, only to realize it’s not good. This led us to think there just may be a third-party solution that would ease the pain and make our system better.

In my ever so humble opinion, third-party solutions NEVER ease the cumulative pain, they only move the pain-ball around to different pain-regions. The system we landed on implements what is known as ABAC, or Attribute Based Access Control. It uses XACML, which is a … weird specification. XACML is both a protocol for enforcing access to a resource, and a system architecture. Like all good specifications it uses XML for data transfer *snort*. More importantly, it renames common operations and parts of the system with unique acronyms so nobody really knows what you are doing or even talking about.

Take this statement for example:

Not sure why the policy request from the PEP for that particular resource and action pair returned indeterminate, so I will have to inspect the PDP to see if it has a problem accessing the PIP to enrich the relevant attributes, as well as verify the request XML from the API. End of line.

This actually makes sense based on the ABAC system I’m working with (except I know that indeterminate responses mean there is no corresponding policy to evaluate the supplied resource and action, so I would never actually say this, but somebody else might, so it’s legit). Also I only say “End of line” in my head after I finish speaking.

We had high hopes the ABAC system would save us silver-bullet style from slogging through building our own. It didn’t. But it is pretty cool. In no particular order, here are some things to consider if you are thinking about implementing an ABAC system, third-party or otherwise.

  • XACML systems only issue permit/deny/WTF responses. This is important so don’t skip this bit. You DON’T ask the system “What does Jason have access to?“, you ask it “Does Jason have access to X?“. It’s a critical difference. X can be a list of things, but keep in mind the longer that list, the more time it will take the system to respond.
  • When deciding if a user can access something, XACML systems can be tied into your standard run of the mill RDMS. In the XACML world, this is called “attribute enrichment from the PIP” – where the PIP (Policy Information Point) is the RDMS.
  • I would very much like to throat punch the person who coined the phrase “attribute enrichment”
  • It’s XML all the way down peeps. The request, the response, even the policy used to say yay or nay in the ABAC system. It’s also super verbose with ample namespace usage. Good times indeed!
  • Using an XACML ABAC system does provide a nice separation of concerns. User roles and rights can still be defined in a RDMS, but they are enforced by the ABAC system. This aspect I very much like.
  • The “resources” used in policies, AKA, what you want access to, have a hierarchy. This allows for a cool fallback to a more generic policy if the specific one requested does not exist. And in the other direction, provides a mechanism to create a one-off policy for a special use case. This I also very much like.
  • Just to re-iterate – If you are looking for a system that provides a list of things a user has access to – ABAC over XACML is not that system. It can kind of sort of be shoe-horned to do that, but it’s hacky at best, and does not scale for shit.

Well there you have it. ABAC/XACML is cool, has some neat features, uses crappy formats, invents its own dialect, and overall is boring enough that I hope you never have to work with it. Next in my new series of “Esoteric software related posts that nobody could possibly find interesting“, I’m going to talk about switching out the terminal emulator I have been using for the last decade. Oh snap, I already did that one.

Either way, you have to love the “I’ll be ABAC” title pun. Pure comedic gold that one. Hasta la vista, baby!

New Year, New Terminal

I have been rocking mrxvt as my primary terminal emulator for what seems like 10 years. Coincidentally that is about the same amount of time since it was last updated. Simple, configurable, and most importantly speedy – mrxvt has been a reliable and faithful companion. For no particular reason except curiosity, and a bit of time around the holidays for tinkering, I decided to explore some other options.

To be clear, I spend the majority of my waking hours on a computer, and the majority of that at the command line, and the majority of that in vim. Swapping out the terminal part of that equation is not something I take lightly. This isn’t the first time I have ventured on this journey, but it is the first time I actually found a destination worth lingering on.

I started slogging through the old school contenders like so many times in the past, then I worked through the new-ish crop of terminals. I found some neat options, but nothing that quite fit the bill. Then I happened upon a long shot, a performance focused terminal called alacritty. By itself, allacritty did not have all the features I wanted, but paired with tmux, which it is intended to be used with, I thought it might just do the trick. Plus the project name is just to clever. Alacrity + TTY, I love it!

I have used screen in the past, and have heard of tmux, but basically had zero practical experience before the last few days. Turns out tmux is pretty sweet. Lots of knobs to twist, somewhat complicated to get setup the way I wanted, but not a vim level learning curve. Even more promising, I was able to massage tmux to behave like my custom mrxvt config. Big win for the old muscle memory.

Alacritty is a unique terminal emulator. It uses GPU accelerated rendering for speed, and has a very minimal, but smart, feature set. There are no Debian packages, so I had to compile from source. I have never built a program written in rust before, but it was as simple as apt-get installing a few pre-reqs, starting the build, and waiting for it to finish. While I was at it I built tmux from the latest git checkout, and installed the Hack font to replace the Terminus font I have been using for I don’t even know how long. I figure if I’m going new, may as well go new all the way. Well not all the way, I’m not switching to Emacs. That would be nuts!

One thing I LOVE about alacritty (aside from the speed), is the ability to map your own codes to keyboard combinations. I don’t use a lot of vim tweaks or plugins, but one thing I absolutely cannot do without is mapping “:wa” to Shift-Enter. It’s so handy! Some terminals just produce the same keycode for Enter when hitting Shift-Enter, but with allacritty I could easily setup a custom keycode that I could then map in my vimrc. Happy times!

Now I have UTF-8 support in the terminal, much improved HiDPI support for my laptop, and a slick fast terminal emulator with tabs and all the shortcuts I’m used to. It took about 2 days to tweak everything, but I’m going to use my new setup when I go back to work tomorrow and my dork excitement level is pretty high. I’m sure I will run into issues once I sink my teeth in, but I’m jazzed about starting 2018 with something new and improved (already LOVING tmux copy mode vs standard terminal scrollback (once I tweaked copy/paste)). I just nested parens, I love it when that happens!

Below are my .alacritty.yml and .tmux.conf files, and the obligatory screenshots of the before and after.

Old school


New school

.alacritty.yml (with few exceptions, this is the default alacritty config file)

# Configuration for Alacritty, the GPU enhanced terminal emulator


# Any items in the `env` entry below will be added as
# environment variables. Some entries may override variables
# set by alacritty it self.
env:
  # TERM env customization.
  #
  # If this property is not set, alacritty will set it to xterm-256color.
  #
  # Note that some xterm terminfo databases don't declare support for italics.
  # You can verify this by checking for the presence of `smso` and `sitm` in
  # `infocmp xterm-256color`.
  TERM: xterm-256color

window:
  # Window dimensions in character columns and lines
  # Falls back to size specified by window manager if set to 0x0.
  # (changes require restart)
  dimensions:
    columns: 80
    lines: 24

  # Adds this many blank pixels of padding around the window
  # Units are physical pixels; this is not DPI aware.
  # (change requires restart)
  padding:
    x: 20
    y: 20

  # Window decorations
  # Setting this to false will result in window without borders and title bar.
  decorations: false

# Display tabs using this many cells (changes require restart)
tabspaces: 8

# When true, bold text is drawn using the bright variant of colors.
draw_bold_text_with_bright_colors: true

# Font configuration (changes require restart)
#
# Important font attributes like antialiasing, subpixel aa, and hinting can be
# controlled through fontconfig. Specifically, the following attributes should
# have an effect:
#
# * hintstyle
# * antialias
# * lcdfilter
# * rgba
#
# For instance, if you wish to disable subpixel antialiasing, you might set the
# rgba property to "none". If you wish to completely disable antialiasing, you
# can set antialias to false.
#
# Please see these resources for more information on how to use fontconfig
#
# * https://wiki.archlinux.org/index.php/font_configuration#Fontconfig_configuration
# * file:///usr/share/doc/fontconfig/fontconfig-user.html
font:
  # The normal (roman) font face to use.
  normal:
    family: Hack # should be "Menlo" or something on macOS.
    # Style can be specified to pick a specific face.
    # style: Regular

  # The bold font face
  bold:
    family: monospace # should be "Menlo" or something on macOS.
    # Style can be specified to pick a specific face.
    style: Regular

  # The italic font face
  italic:
    family: monospace # should be "Menlo" or something on macOS.
    # Style can be specified to pick a specific face.
    # style: Italic

  # Point size of the font
  size: 10.0

  # Offset is the extra space around each character. offset.y can be thought of
  # as modifying the linespacing, and offset.x as modifying the letter spacing.
  offset:
    x: 0.0
    y: 0.0

  # Glyph offset determines the locations of the glyphs within their cells with
  # the default being at the bottom. Increase the x offset to move the glyph to
  # the right, increase the y offset to move the glyph upward.
  glyph_offset:
    x: 0.0
    y: 0.0

  # OS X only: use thin stroke font rendering. Thin strokes are suitable
  # for retina displays, but for non-retina you probably want this set to
  # false.
  use_thin_strokes: true

# Should display the render timer
render_timer: false

# Use custom cursor colors. If true, display the cursor in the cursor.foreground
# and cursor.background colors, otherwise invert the colors of the cursor.
custom_cursor_colors: true

# Colors (Tomorrow Night Bright)
colors:
  # Default colors
  primary:
    background: '0x000000'
    foreground: '0xeaeaea'

  # Colors the cursor will use if `custom_cursor_colors` is true
  cursor:
    text: '0x000000'
    cursor: '0x888888'

  # Normal colors
  normal:
    black:   '0x000000'
    red:     '0xd54e53'
    green:   '0xb9ca4a'
    yellow:  '0xe6c547'
    blue:    '0x7aa6da'
    magenta: '0xc397d8'
    cyan:    '0x70c0ba'
    white:   '0xffffff'

  # Bright colors
  bright:
    black:   '0x666666'
    red:     '0xff3334'
    green:   '0x9ec400'
    yellow:  '0xe7c547'
    blue:    '0x7aa6da'
    magenta: '0xb77ee0'
    cyan:    '0x54ced6'
    white:   '0xffffff'

  # Dim colors (Optional)
  dim:
    black:   '0x333333'
    red:     '0xf2777a'
    green:   '0x99cc99'
    yellow:  '0xffcc66'
    blue:    '0x6699cc'
    magenta: '0xcc99cc'
    cyan:    '0x66cccc'
    white:   '0xdddddd'

# Visual Bell
#
# Any time the BEL code is received, Alacritty "rings" the visual bell. Once
# rung, the terminal background will be set to white and transition back to the
# default background color. You can control the rate of this transition by
# setting the `duration` property (represented in milliseconds). You can also
# configure the transition function by setting the `animation` property.
#
# Possible values for `animation`
# `Ease`
# `EaseOut`
# `EaseOutSine`
# `EaseOutQuad`
# `EaseOutCubic`
# `EaseOutQuart`
# `EaseOutQuint`
# `EaseOutExpo`
# `EaseOutCirc`
# `Linear`
#
# To completely disable the visual bell, set its duration to 0.
#
visual_bell:
  animation: EaseOutExpo
  duration: 0

# Background opacity
background_opacity: 1.0

# Mouse bindings
#
# Currently doesn't support modifiers. Both the `mouse` and `action` fields must
# be specified.
#
# Values for `mouse`:
# - Middle
# - Left
# - Right
# - Numeric identifier such as `5`
#
# Values for `action`:
# - Paste
# - PasteSelection
# - Copy (TODO)
mouse_bindings:
  - { mouse: Middle, action: PasteSelection }

mouse:
  double_click: { threshold: 300 }
  triple_click: { threshold: 300 }

selection:
    semantic_escape_chars: ",│`|:\"' ()[]{}"

dynamic_title: true

hide_cursor_when_typing: true

# Style of the cursor
#
# Values for 'cursor_style':
# - Block
# - Underline
# - Beam
cursor_style: Block

# Live config reload (changes require restart)
live_config_reload: true

# Shell
#
# You can set shell.program to the path of your favorite shell, e.g. /bin/fish.
# Entries in shell.args are passed unmodified as arguments to the shell.
# shell:
#   program: /bin/bash
#   args:
#     - --login


# Key bindings
#
# Each binding is defined as an object with some properties. Most of the
# properties are optional. All of the alphabetical keys should have a letter for
# the `key` value such as `V`. Function keys are probably what you would expect
# as well (F1, F2, ..). The number keys above the main keyboard are encoded as
# `Key1`, `Key2`, etc. Keys on the number pad are encoded `Number1`, `Number2`,
# etc.  These all match the glutin::VirtualKeyCode variants.
#
# Possible values for `mods`
# `Command`, `Super` refer to the super/command/windows key
# `Control` for the control key
# `Shift` for the Shift key
# `Alt` and `Option` refer to alt/option
#
# mods may be combined with a `|`. For example, requiring control and shift
# looks like:
#
# mods: Control|Shift
#
# The parser is currently quite sensitive to whitespace and capitalization -
# capitalization must match exactly, and piped items must not have whitespace
# around them.
#
# Either an `action`, `chars`, or `command` field must be present.
#   `action` must be one of `Paste`, `PasteSelection`, `Copy`, or `Quit`.
#   `chars` writes the specified string every time that binding is activated.
#     These should generally be escape sequences, but they can be configured to
#     send arbitrary strings of bytes.
#   `command` must be a map containing a `program` string, and `args` array of
#     strings. For example:
#     - { ... , command: { program: "alacritty", args: ["-e", "vttest"] } }
#
# Want to add a binding (e.g. "PageUp") but are unsure what the X sequence
# (e.g. "\x1b[5~") is? Open another terminal (like xterm) without tmux,
# then run `showkey -a` to get the sequence associated to a key combination.
key_bindings:
  - { key: V,        mods: Control|Shift,    action: Paste               }
  - { key: C,        mods: Control|Shift,    action: Copy                }
  - { key: Q,        mods: Command, action: Quit                         }
  - { key: W,        mods: Command, action: Quit                         }
  - { key: Insert,   mods: Shift,   action: PasteSelection               }
  - { key: Key0,     mods: Control, action: ResetFontSize                }
  - { key: Equals,   mods: Control, action: IncreaseFontSize             }
  - { key: Subtract, mods: Control, action: DecreaseFontSize             }
  - { key: Home,                    chars: "\x1bOH",   mode: AppCursor   }
  - { key: Home,                    chars: "\x1b[H",   mode: ~AppCursor  }
  - { key: End,                     chars: "\x1bOF",   mode: AppCursor   }
  - { key: End,                     chars: "\x1b[F",   mode: ~AppCursor  }
  - { key: PageUp,   mods: Shift,   chars: "\x1b[5;2~"                   }
  - { key: PageUp,   mods: Control, chars: "\x1b[5;5~"                   }
  - { key: PageUp,                  chars: "\x1b[5~"                     }
  - { key: PageDown, mods: Shift,   chars: "\x1b[6;2~"                   }
  - { key: PageDown, mods: Control, chars: "\x1b[6;5~"                   }
  - { key: PageDown,                chars: "\x1b[6~"                     }
  - { key: Tab,      mods: Shift,   chars: "\x1b[Z"                      }
  - { key: Back,                    chars: "\x7f"                        }
  - { key: Back,     mods: Alt,     chars: "\x1b\x7f"                    }
  - { key: Insert,                  chars: "\x1b[2~"                     }
  - { key: Delete,                  chars: "\x1b[3~"                     }
  - { key: Left,     mods: Shift,   chars: "\x1b[1;2D"                   }
  - { key: Left,     mods: Control, chars: "\x1b[1;5D"                   }
  - { key: Left,     mods: Alt,     chars: "\x1b[1;3D"                   }
  - { key: Left,                    chars: "\x1b[D",   mode: ~AppCursor  }
  - { key: Left,                    chars: "\x1bOD",   mode: AppCursor   }
  - { key: Right,    mods: Shift,   chars: "\x1b[1;2C"                   }
  - { key: Right,    mods: Control, chars: "\x1b[1;5C"                   }
  - { key: Right,    mods: Alt,     chars: "\x1b[1;3C"                   }
  - { key: Right,                   chars: "\x1b[C",   mode: ~AppCursor  }
  - { key: Right,                   chars: "\x1bOC",   mode: AppCursor   }
  - { key: Up,       mods: Shift,   chars: "\x1b[1;2A"                   }
  - { key: Up,       mods: Control, chars: "\x1b[1;5A"                   }
  - { key: Up,       mods: Alt,     chars: "\x1b[1;3A"                   }
  - { key: Up,                      chars: "\x1b[A",   mode: ~AppCursor  }
  - { key: Up,                      chars: "\x1bOA",   mode: AppCursor   }
  - { key: Down,     mods: Shift,   chars: "\x1b[1;2B"                   }
  - { key: Down,     mods: Control, chars: "\x1b[1;5B"                   }
  - { key: Down,     mods: Alt,     chars: "\x1b[1;3B"                   }
  - { key: Down,                    chars: "\x1b[B",   mode: ~AppCursor  }
  - { key: Down,                    chars: "\x1bOB",   mode: AppCursor   }
  - { key: F1,                      chars: "\x1bOP"                      }
  - { key: F2,                      chars: "\x1bOQ"                      }
  - { key: F3,                      chars: "\x1bOR"                      }
  - { key: F4,                      chars: "\x1bOS"                      }
  - { key: F5,                      chars: "\x1b[15~"                    }
  - { key: F6,                      chars: "\x1b[17~"                    }
  - { key: F7,                      chars: "\x1b[18~"                    }
  - { key: F8,                      chars: "\x1b[19~"                    }
  - { key: F9,                      chars: "\x1b[20~"                    }
  - { key: F10,                     chars: "\x1b[21~"                    }
  - { key: F11,                     chars: "\x1b[23~"                    }
  - { key: F12,                     chars: "\x1b[24~"                    }
  - { key: F1,       mods: Shift,   chars: "\x1b[1;2P"                   }
  - { key: F2,       mods: Shift,   chars: "\x1b[1;2Q"                   }
  - { key: F3,       mods: Shift,   chars: "\x1b[1;2R"                   }
  - { key: F4,       mods: Shift,   chars: "\x1b[1;2S"                   }
  - { key: F5,       mods: Shift,   chars: "\x1b[15;2~"                  }
  - { key: F6,       mods: Shift,   chars: "\x1b[17;2~"                  }
  - { key: F7,       mods: Shift,   chars: "\x1b[18;2~"                  }
  - { key: F8,       mods: Shift,   chars: "\x1b[19;2~"                  }
  - { key: F9,       mods: Shift,   chars: "\x1b[20;2~"                  }
  - { key: F10,      mods: Shift,   chars: "\x1b[21;2~"                  }
  - { key: F11,      mods: Shift,   chars: "\x1b[23;2~"                  }
  - { key: F12,      mods: Shift,   chars: "\x1b[24;2~"                  }
  - { key: F1,       mods: Control, chars: "\x1b[1;5P"                   }
  - { key: F2,       mods: Control, chars: "\x1b[1;5Q"                   }
  - { key: F3,       mods: Control, chars: "\x1b[1;5R"                   }
  - { key: F4,       mods: Control, chars: "\x1b[1;5S"                   }
  - { key: F5,       mods: Control, chars: "\x1b[15;5~"                  }
  - { key: F6,       mods: Control, chars: "\x1b[17;5~"                  }
  - { key: F7,       mods: Control, chars: "\x1b[18;5~"                  }
  - { key: F8,       mods: Control, chars: "\x1b[19;5~"                  }
  - { key: F9,       mods: Control, chars: "\x1b[20;5~"                  }
  - { key: F10,      mods: Control, chars: "\x1b[21;5~"                  }
  - { key: F11,      mods: Control, chars: "\x1b[23;5~"                  }
  - { key: F12,      mods: Control, chars: "\x1b[24;5~"                  }
  - { key: F1,       mods: Alt,     chars: "\x1b[1;6P"                   }
  - { key: F2,       mods: Alt,     chars: "\x1b[1;6Q"                   }
  - { key: F3,       mods: Alt,     chars: "\x1b[1;6R"                   }
  - { key: F4,       mods: Alt,     chars: "\x1b[1;6S"                   }
  - { key: F5,       mods: Alt,     chars: "\x1b[15;6~"                  }
  - { key: F6,       mods: Alt,     chars: "\x1b[17;6~"                  }
  - { key: F7,       mods: Alt,     chars: "\x1b[18;6~"                  }
  - { key: F8,       mods: Alt,     chars: "\x1b[19;6~"                  }
  - { key: F9,       mods: Alt,     chars: "\x1b[20;6~"                  }
  - { key: F10,      mods: Alt,     chars: "\x1b[21;6~"                  }
  - { key: F11,      mods: Alt,     chars: "\x1b[23;6~"                  }
  - { key: F12,      mods: Alt,     chars: "\x1b[24;6~"                  }
  - { key: F1,       mods: Super,   chars: "\x1b[1;3P"                   }
  - { key: F2,       mods: Super,   chars: "\x1b[1;3Q"                   }
  - { key: F3,       mods: Super,   chars: "\x1b[1;3R"                   }
  - { key: F4,       mods: Super,   chars: "\x1b[1;3S"                   }
  - { key: F5,       mods: Super,   chars: "\x1b[15;3~"                  }
  - { key: F6,       mods: Super,   chars: "\x1b[17;3~"                  }
  - { key: F7,       mods: Super,   chars: "\x1b[18;3~"                  }
  - { key: F8,       mods: Super,   chars: "\x1b[19;3~"                  }
  - { key: F9,       mods: Super,   chars: "\x1b[20;3~"                  }
  - { key: F10,      mods: Super,   chars: "\x1b[21;3~"                  }
  - { key: F11,      mods: Super,   chars: "\x1b[23;3~"                  }
  - { key: F12,      mods: Super,   chars: "\x1b[24;3~"                  }
  - { key: Return,   mods: Shift,   chars: "\x1b[25;3~"                  }

.tmux.conf (this was mashed up from a bunch of different google-able tmux examples)

setw -g mode-keys vi
set -g mouse on
set -g terminal-overrides 'xterm*:smcup@:rmcup@'
set-option -g visual-activity off
set-option -g visual-bell off
set-option -g visual-silence off
set-window-option -g monitor-activity off
set-option -g bell-action none
set-window-option -g word-separators ' '

bind -n S-Left previous-window
bind -n S-Right next-window
bind -n S-Up select-pane -U
bind -n S-Down select-pane -D
bind -n C-h split-window -v
bind -n S-PageUp copy-mode
bind -n C-n new-window \; \
    setw -g force-height 63

bind-key -T copy-mode-vi DoubleClick1Pane \
    select-pane \; \
    send-keys -X select-word \; \
    send-keys -X copy-pipe "xclip" \

bind-key -n DoubleClick1Pane \
    select-pane \; \
    copy-mode -M \; \
    send-keys -X select-word \; \
    send-keys -X copy-pipe "xclip" \; \
    display-message "COPIED"

bind-key -T copy-mode-vi \
    y send-keys -X \
    copy-pipe-and-cancel "xclip" \; \
    display-message "COPIED"

bind-key -T copy-mode-vi \
    MouseDragEnd1Pane \
    send-keys -X \
    copy-pipe-and-cancel \
    "xclip" \; \
    display-message "COPIED"

set -g pane-border-fg black
set -g pane-active-border-fg brightred
set -g status-justify left
set -g status-bg default
set -g status-fg colour12
set -g status-interval 2
set -g message-fg black
set -g message-bg yellow
set -g message-command-fg blue
set -g message-command-bg black
setw -g mode-bg colour6
setw -g mode-fg colour0
setw -g force-height 63
setw -g window-status-format " #F#I:#W#F "
setw -g window-status-current-format " #F#I:#W#F "
setw -g window-status-format "#[fg=magenta]#[bg=black] #I #[bg=cyan]#[fg=colour8] #W "
setw -g window-status-current-format "#[bg=brightmagenta]#[fg=colour8] #I #[fg=colour8]#[bg=colour14] #W "
setw -g window-status-current-bg colour0
setw -g window-status-current-fg colour11
setw -g window-status-current-attr dim
setw -g window-status-bg green
setw -g window-status-fg black
setw -g window-status-attr reverse
set -g status-left ''
set -g default-terminal "screen-256color"
setw -g clock-mode-colour colour135
setw -g mode-attr bold
setw -g mode-fg colour17
setw -g mode-bg colour238
set -g pane-border-bg colour0
set -g pane-border-fg colour0
set -g pane-active-border-bg colour0
set -g pane-active-border-fg colour0
set -g status-position bottom
set -g status-bg colour233
set -g status-fg colour4
set -g status-attr dim
set -g status-left ''
set -g status-right ''
set -g status-right-length 50
set -g status-left-length 20
setw -g window-status-current-fg colour81
setw -g window-status-current-bg colour238
setw -g window-status-current-attr bold
setw -g window-status-current-format ' #I#[fg=colour250]:#[fg=colour255]#W#[fg=colour50]#F '
setw -g window-status-fg colour138
setw -g window-status-bg colour235
setw -g window-status-attr none
setw -g window-status-format ' #I#[fg=colour237]:#[fg=colour250]#W#[fg=colour244]#F '
setw -g window-status-bell-attr bold
setw -g window-status-bell-fg colour255
setw -g window-status-bell-bg colour1
set -g message-attr bold
set -g message-fg colour250
set -g message-bg colour234

I want to wish all unencumbered readers out there a happy New Year, and I hope this next year brings all of you good fortune. I also want to thank Farmobile (my employer), for giving us the last week off. Without it I would not have had the time to get my geek on and make this update possible. I almost can’t wait to go back to work tomorrow!

Even More Multiple Monitor Gaming On Linux

It’s been a while since I rehashed this idea as a sad attempt to drive eyeballs to my blog, which these days mainly serves to shill for my Open Source webmail project Cypht (pronounced “sift”). Not, sure, you, have, heard, of, it. You should go check it out, download it, install it, send me questions because the install is hard, really dig it once you get it working, then give me money for a security audit. Or whatever it’s no big.

I haven’t played a lot of these games extensively, many just for a few minutes to test compatibility. Mainly because one of the games in this group is consuming all my “free” time. I’m going to make you read the whole list to find out which. Just kidding, it’s called Rocket League. You should stop reading this post now and play it. Come back later and thank me.

Back already? You’re welcome! Let’s get on with the list

Wasteland 2 Directors cut

https://wasteland2.inxile-entertainment.com/
Steam tells me I have played 4 hours of this game. Having just dusted it off after a year I’m a bit concerned about my memory, because I have no idea what’s going on. It is cool though. Pretty standard point and click RPG to move your group of weirdos, examine stuff, and pick up strange I-don’t-know-how-I-will-use-that items. Fights are turned based on a hex grid with the ability to move/attack within some limitations I don’t remember.

Universe Sandbox

http://universesandbox.com/
Not a game really, but works on my rig and is included for the following reasons:

  1. It’s called Universe Sandbox, which is rad.
  2. Science is awesome, astronomy especially so.
  3. You can do really neat stuff most of which I have only seen on the menus but I did zoom into Saturn and check out the ring rotation.

Robot Roller-Derby Disco Dodgeball

http://www.82apps.com/DiscoDodgeball/
Retro first person “shooter” except it’s dodgeball. The roller derby part basically means you can’t stop moving when you want. I think there are power-ups and leveling up and you can even catch a ball thrown at you. The court has ramps and platforms, and one time I made it to the upper level before a bot nailed me instantly. I have not played it much but it’s pretty fun as far as retro disco dodgeball games go.

Niche

http://niche-game.com/
Everyone knows you have to get up to get down. This game is all about getting down. Seriously. It’s a sim where you have to furiously procreate to survive, hopefully passing on some worthwhile chromosomes. My crude description not withstanding, this is actually a really interesting (and educational) turn based game. It’s also safe for work so to speak, but you really should not be playing games at work.

Martial Law

http://store.steampowered.com/app/346150/Martial_Law/
You are alone in a seemingly abandoned and desolate photo-realistic landscape. Minutes after venturing out into a field, you are near starving. When you approach any potential resources, you are gunned down from unseen locations. This basically sums up my experience.

Madout

http://store.steampowered.com/app/352170/MadOut/
Sort of like Twisted Metal meets Mario Cart. Flashy graphics, simple physics, and easy controls. Like many others on the list, I downloaded, I configured, I played – but then moved on. I’m not sure if it’s because the game itself was lackluster or if I just did not give it a chance because I rushed through it to play Rocket League.

Distance

http://survivethedistance.com/
Another racer with nice graphics and a futuristic beat-the-track/clock type of game play. Like Madout the driving physics are pretty easy to get the hang of, on early levels anyway. Probably has lots of other features I could not be bothered to dig into during my short revisit. I did notice giant pumpkins strewn about the level that slow you down when you plow through them, so there is that.

Divinity Original Sin Advanced Edition

http://www.divinityoriginalsin.com/
Another point-click-to-move RPG with nice graphics and sort of a Diablo feel to it. I have a vague recollection that I enjoyed the 30 minutes I played months ago. I’m putting this one on the get back to it later list for sure.

Besiege

http://www.besiege.spiderlinggames.co.uk/
This is a unique game. Each level requires you to build a war machine that you let loose to destroy enemies or fortresses. There is a huge variety of components to build your machine with. Watching my creation fall apart in seconds because of my comically poor structural engineering skills is hilarious. 100% recommend this one, it’s a gem.

Rocket League

https://www.rocketleague.com/
Many of the games I like tend to be complicated, and increase in complexity as you level up and gain new abilities. Rocket League is one of those rare games with a simple concept, simple controls, playable right away, with endless room to improve. The premise is soccer (as well as other game modes) with a giant ball that you smash into with a car to move down the field. Calling it demolition derby soccer doesn’t really do it justice, but it’s accurate.

The real fun (and frustration), is combining the simple controls to blast your car into the air for “arial” hits. Trying to accurately hit the ball in the air is like trying to get 10 meters in QWOP – surprisingly difficult. You can play with bots offline, or against human opponents online (as well couch co-op with split screen). Be warned – the online community can be iffy (but you can mute the chat thank god). Another online warning – a surprising number of players are seriously good, like stupid good. Of course It’s also possible I’m just stupid bad.

Testing PHP Network Code

I work on this Open Source webmail client. I don’t think I have ever written about it here before. It’s called Cypht. It connects to services, like an IMAP, SMTP, or POP3 server. It uses the PHP function stream_socket_client to create a connection to these services, then it sends commands and reads responses with standard read/write functions like fgets and fwrite.

Recently I decided I hate myself, so I tried to build a way to unit-test this. Turns out it’s possible, and not nearly as hard as I deserve. I did bang my head around the desk area for a few days figuring it out, so not a total loss. Here is how I did it.

Step 1: Abstract low-down-no-good functions

No matter how amazingly awesome your PHP code base is, if your code actually does anything and you want comprehensive unit test coverage, you have no choice but to abstract a few built-in PHP functions that simply don’t play nice (sessions, cookies, header, curl, streams, you get the picture). I use the following pattern for this:

  • Create a class of all static methods that “wrap” the naughty functions
  • Only define that class at run time if it does not already exist
  • Change your code to call the naughty_class::function version
  • Create the same class in your unit test bootstrap, that has friendly versions of these functions (like doing nothing, or returning true or whatever)
  • Include your unit test version before the run time version when running tests.
  • Realize your wildest dreams of success and good fortune.

An example:

class NaughtyFunctions {
    /**
     * @param string $server host to connect to
     * @param integer $port port to connect to
     * @param integer $errno error number
     * @param string $errstr error string
     * @param integer $mode connection mode
     * @param object $ctx context
    */
    public static function stream_socket_client($server, $port,
        &$errno, &$errstr, $timeout, $mode, $ctx) {
        return stream_socket_client($server.':'.$port, $errno,
            $errstr, $timeout, $mode, $ctx);
    }
}

Instead of calling stream_socket_client in code, we call NaughtyFunctions::stream_socket_client with the same (similar) arguments. This pattern (or something like it) is required to make this work, so no skipping step 1. It’s also a great way to deal with PHP functions that disagree with PHPUnit, and as a way to fool tests into taking a different code path they would not normally take, like by overriding function_exists for example. Here is what Cypht uses at runtime:

https://github.com/jasonmunro/cypht/blob/master/lib/framework.php#L59-L202

Step 2: Build a stream wrapper to fake out your code

In PHP you can fake a “stream” AKA a file handle or network connection, by creating and registering a “stream wrapper“. For file operations and stateless protocols like HTTP, this is pretty simple – read until the “file” ends. But for persistent network protocols, this takes a bit of cleverness.

You need the ability to read from the stream until you reach “End Of File” (EOF). But then you need to reset the EOF status the next time you issue a command, so you can read from the stream again. There is no way (I know of) to do this from within the stream wrapper prototype, and we don’t want to alter the network code we are testing.

Thus the cleverness. Using the abstract in step 1, we can save a reference to the stream resource, and rewind it every time we send a new command, effectively resetting the EOF. Seems less clever now that I write this, but it was the most difficult part.

Here is an example of of both the NaughtyFunctions class and a stream wrapper in action:


/**
 * Generic stream wrapper. This will be extended for protocol
 * specific commands and responses.
 */
class Fake_Server {

    /* position within the response string */
    protected $position;

    /* current response string */
    protected $response = '';

    /* list of commands to responses, varies per protocol */
    public $command_responses = array();

    /* open */
    function stream_open($path, $mode, $options, &$opened) {
        $this->position = 0;
        return true;
    }

    /* read */
    function stream_read($count) {
        $this->position += strlen($this->response);
        return $this->response;
    }

    /* write */
    function stream_write($data) {
        $data = trim($data);

        /* look for and set the correct response */
        if (array_key_exists($data, $this->command_responses)) {
            $this->response =  $this->command_responses[$data];
        }

        /* request not found, so set an error value */
        else {
            $this->response = $this->error_resp($data);
        }
        /* CLEVERNESS: here we rewind the stream so we
           can read from it again */
        rewind(NaughtyFunctions::$resource);
        return (strlen($data)+2);
    }

    /* tell */
    function stream_tell() {
        return $this->position;
    }

    /* seek */
    function stream_seek($pos, $whence) {
        $this->position = 0;
        return true;
    }

    /* end of file */
    function stream_eof() {
        return $this->position >= strlen($this->response);
    }

    /* generic error */
    function error_resp($data) {
        return "ERROR\r\n";
    }
}

/**
 * IMAP specific fake server that extends the generic one
 */
class Fake_IMAP_Server extends Fake_Server {

    /* array of commands and their corresponding responses */
    public $command_responses = array(
        'A1 CAPABILITY' => "* CAPABILITY IMAP4rev1 LITERAL+ ".
            "LOGIN-REFERRALS ID ENABLE AUTH=PLAIN AUTH=CRAM-MD5\r\n",
        /* other commands and responses go here */
    );

    /* IMAP friendly error */
    function error_resp($data) {
        $bits = explode(' ', $data);
        $pre = $bits[0];
        return $pre." BAD Error in IMAP command\r\n";
    }
}

/**
 * Naughty functions wrapper to be used in unit tests. Unlike the
 * run time version, this one returns a "connection" to our fake
 * server.
 */
class NaughtyFunctions {

    /* this will hold a reference to our fake network connection */
    public static $resource = false;

    /* we can toggle this to simulate a bad connection */
    public static $no_stream = false;

    /* fake out stream_socket_client and start the wrapper */
    public static function stream_socket_client($server, $port,
        &$errno, &$errstr, $timeout, $mode, $ctx) {

        /* bad connection */
        if (self::$no_stream) {
            return false;
        }
        /* don't call twice from the same test */
        if (!in_array('foo', stream_get_wrappers(), true)) {
            stream_wrapper_register('foo', 'Fake_IMAP_Server');
        }

        /* open, save a reference to, and return the connection
           to our fake server */
        $res = fopen('foo://', 'w+');
        self::$resource = $res;
        return $res;
    }
}

Step 3: Correlate requests and responses for your protocol

Now all you have to do is map requests to the server with appropriate (or inappropriate) responses to exercise your network code from a unit test. In this case that would be adding to the $command_responses array in Fake_IMAP_Server. This is where we cross over from “cool problem solving” to “incredibly tedious unit test production”. looks like I will be receiving extra punishment after all.

Step 4. See a doctor about your wrist pain from writing all the unit tests

Cypht has about 14,000 lines of code I need to test this way. I’m about 1% through the process. I love that it can be done without standing up an IMAP/POP3/SMTP server, but my fingers hurt just thinking about it.