Dungeons & PowerShell

PowerShell ist nicht nur ein mächtiges Werkzeug für Administratoren und Entwickler auch Rollenspieler können die umfangreiche Skriptsprache nutzen. Egal ob Dungeons & Dragons, Das schwarze Auge, Pathfinder oder Splittermond, jedes System kann mit Hilfe der PowerShell als Grundlage für Würfel und Zufallsereignisse dienen.

Würfeln

Die Würfel lassen sich als Kombination aus Anzahl und Seiten darstellen, getrennt mit einem W für Würfel oder im Englischen mit d für dice. Somit ergibt 1W6 einen sechsseitigen Würfel mit den Augen von 1 bis 6. Der klassische 1W20 für D&D würfelt die Zahlen von 1 bis 20. Bei 2W4 werden zwei Würfel mit je vier Seiten gewürfelt, das Ergebnis liegt zwischen 2 und 8. PowerShell simuliert dies durch die Funktion Get-Random. Der minimale Wert ist dabei inklusive, der maximale Wert ist exklusiv! Dadurch ergeben sich die folgenden Aurufe für den 1W6 und 1W20.

Get-Random -Minimum 1 -Maximum 7
# oder
1..6 | Get-Random

Get-Random -Minimum 1 -Maximum 21
oder
1..20 | Get-Random

Mehrere Würfel lassen sich simulieren wenn für jeden Würfel ein eigenes Array erstellt wird. Aus der Gesamtmenge sind dann die Zahlen zufällig zu wählen. Wird die Count Option von Get-Random auf einem einfachen Array genutzt können die Zahlen nicht doppelt vorkommen. Die Funktion für 2W20 sieht damit wie folgt aus.

$Seiten = 20
$Anzahl = 2
(1..$Seiten)*$Anzahl | Get-Random -Count $Anzahl

Möchte man das Ergebnis aller Würfel addieren kommt Measure-Object zum Einsatz. Dabei kann optional auch ein Wert aufaddiert werden, beispielsweise um Fähigkeitsmodifikatoren einzurechnen. Das 2W20 Beispiel von oben, ergänzt um einen +4 Modifikator, also 2W20+4 sieht dann wie folgt aus.

(((1..$Seiten)*$Anzahl | Get-Random -Count $Anzahl) | Measure-Object -Sum).Sum + 4

Zum Schluss werfen wir einen Blick auf die Charaktererstellung von D&D und Pathfinder. Eine Möglichkeit der fünften Edition ist das Auswürfeln. Bei der Methode „4d6 drop lowest“ werden vier sechsseitige Würfel genutzt und der Kleinste entfernt. Dadurch entstehen Werte von 3 (sehr unwahrscheinlich) bis 18 (sehr unwahrscheinlich), im Schnitt liegen die Werte bei 9 bis 16. In die Formeln oben eingesetzt lassen sich die Attribute des Charakters somit wie folgt auswürfeln.

(1..6)*4 | Get-Random -Count 4 | Sort-Object | Select -Last 3 | Measure-Object -Sum | Select -ExpandProperty Sum

Zufallsbegegnungen

Spielleiter sind von Natur aus oft faule Zeitgenossen. Nicht umsonst erklären sich die Seitenlangen Begegnungstabellen in vielen Regelwerken. Mit Hilfe der PowerShell können solche Tabellen auch zufällig ermittelt werden. Eine Variante wäre das Speichern der Begegnungen in diversen Dateien. Exemplarisch ist in diesem Artikel aber eine statische Liste auf Basis der Tabelle „Gewölbe (für niedrige Stufen) HG 2“ aus dem Pathfinder Referenz Dokument gezeigt.

# Begegnungen definieren mit Wahrscheinlichkeit
$Begegnungen = 
@([pscustomobject]@{Min=1; Max=6; Name="1W6 Schreckensratten"},
[pscustomobject]@{Min=7; Max=12; Name="1W6 Feuerkäfer"},
[pscustomobject]@{Min=13; Max=20; Name="1W6 Menschliche Skelette"},
[pscustomobject]@{Min=21; Max=24; Name="1W4 Riesentausendfüßler"},
[pscustomobject]@{Min=25; Max=26; Name="1 Spinnenschwarm"},
[pscustomobject]@{Min=27; Max=32; Name="1W6 Menschliche Zombies"})

# Maximale Wahrscheinlichkeit ermitteln und einen zufälligen Wert bestimmen
$Max = $Begegnungen | foreach { $_.Max } | Measure-Object -Maximum | Select-Object -ExpandProperty Maximum
$Auswahl = Get-Random -Minimum 1 -Maximum ([int32]$Max+1)

#Begegnung darstellen
$Begegnungen | Where-Object { $Auswahl -ge $_.Min -and $Auswahl -le $_.Max }

NPCs

NPCs können beliebig komplex aufgebaut sein. Ein einfaches Mittel ist das Definieren von verschiedenen Feldern, die sinnvoll kombiniert werden.

$Namen = "Sarah", "Annabelle", "Johanna", "Lindsey", "Elaine"
$Eigenschaften = "abenteuerlustige", "böse", "chaotische", "erfinderische", "mutige", "risikofreudige"
$Klassen = "Kriegerin", "Magierin", "Schurkin", "Adelige", "Bettlerin", "Händlerin"
"{0} ist eine {1} {2}." -f ($Namen | Get-Random), ($Eigenschaften | Get-Random), ($Klassen | Get-Random)

„Johanna ist eine mutige Kriegerin.“

 

Bilder: Pixabay, Wikipedia

PowerShell Hilfe

Es gibt sehr viele PowerShell Befehle zur Erstellung von Programmen. Einige dienen dabei aber auch der Optimierung der PowerShell selber und helfen bei der Lösung von Problemen. Die PowerShell Hilfe ist hierbei das zentrale Element. Leider funktioniert die Hilfe standardmäßig nicht immer auf allen Geräten und in der gewünschten Sprache. Hier gibt es die Abhilfe dazu.

Hilfe wird nicht aktualisiert

Auf deutschen Windows Systemen gibt es durchaus Probleme mit dem Update-Help und den Spracheinstellungen. Erhält der Benutzer die folgende Meldung, so kann dies durch ein herunterladen der englischen Sprachversion der Hilfeeinträge gelöst werden.

Problem: Die Hilfedateien für dieses Cmdlet können von „Get-Help“ auf diesem Computer nicht gefunden werden. Es wird nur ein Teil der Hilfe angezeigt.

Lösung:

Update-Help -UICulture "en-US"

Nützliche Befehle

# Listet alle Befehle auf oder zeigt das Modul für ein Command.
Get-Command
  
# Aktualisiert die Daten für Get-Help bzw. -? also dem Kernelement der PowerShell Hilfe.
Update-Help
SQLite mit der PowerShell nutzen

SQLite hat sich als kleine Bibliothek für Datei-basierte Datenbanken etabliert. Mit Hilfe der PowerShell und einem ebenso kleinen Modul sind Zugriffe auf die Datenbank einfach zu erledigen.

SQLite

_SQLite ist eine Programmbibliothek, die ein relationales Datenbanksystem enthält. SQLite unterstützt einen Großteil der im SQL-92-Standard festgelegten SQL-Sprachbefehle. […] Die SQLite-Bibliothek lässt sich direkt in entsprechende Anwendungen integrieren, sodass keine weitere Server-Software benötigt wird. Dies ist der entscheidende Unterschied zu anderen Datenbanksystemen. Durch das Einbinden der Bibliothek wird die Anwendung um Datenbankfunktionen erweitert, ohne auf externe Softwarepakete angewiesen zu sein. SQLite hat einige Besonderheiten gegenüber anderen Datenbanken: Die Bibliothek ist nur wenige hundert Kilobyte groß. Eine SQLite-Datenbank besteht aus einer einzigen Datei, die alle Tabellen, Indizes, Views, Trigger usw. enthält. Dies vereinfacht den Austausch zwischen verschiedenen Systemen, sogar zwischen Systemen mit unterschiedlichen Byte-Reihenfolgen. Jede Spalte kann Daten beliebiger Typen enthalten, erst zur Laufzeit wird nötigenfalls konvertiert. (Quelle: Wikipedia)_

Module

Für den Zugriff auf SQLite Datenbanken gibt es eine Reihe von Modulen. Einige Kapsel sogar das komplette SQL in PowerShell Funktionsaufrufe. Für diesen Artikel reicht das PSSQLite Modul vom GitHub User RamblingCookieMonster. Das Modul ermöglicht den Zugriff auf die Datenbank mittels ein oder zwei einfachen Funktionsaufrufen.

Um .SQLite, .SQLite3 oder .db Dateien zu öffnen hilft das Programm SQLite Browser.

Das Modul kann aus GitHub runtergeladen werden oder mittels git exportiert werden. Entweder wird der Unterordner „PSSQLite“ in ein eigenes Verzeichnis gelegt oder in einen der Standard Modulverzeichnisse der PowerShell installiert. Liegt die Datei im Standardpfad:

> $Env:PSModulePath
C:\Users\xxxxxx\Documents\WindowsPowerShell\Modules;C:\Program Files\WindowsPowerShell\Modules;C:\windows\system32\WindowsPowerShell\v1.0\Modules\;C:\Program Files (x86)\Microsoft SQL Server\110\Tools\PowerShell\Modules\
> Import-Module PSSQLite

Andernfalls muss der gesamte Pfad angegeben werden:

> Import-Module "PFAD_ZUM_ORDNER\PSSQLite"

Die Hilfe des Moduls lässt sich über die bekannten Befehle ausgeben.

Get-Command -Module PSSQLite
Get-Help Invoke-SQLiteQuery -Full

SQL Kommandos ausführen

Ein wichtigstes Merkmal bei der Nutzung der Bibliotek ist das automatische Erstellen der SQLite Datei. Wurde die Datei noch nicht erstellt gibt es eine Warnung und eine leere Datenbank wird erzeugt.

$db = "PFAD_ZUM_ORDNER\db1.db" # oder .SQLite, .SQLite3
$query = "CREATE TABLE user (
  UserId INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
  Firstname NVARCHAR(250) NOT NULL,
  Lastname NVARCHAR(250) NOT NULL,
  Birthdate DATE
)"

# SQLite erstellt die Datei automatisch
Invoke-SqliteQuery -Query $query -DataSource $db

# Daten einfügen
Invoke-SqliteQuery -Query "INSERT INTO user(Firstname, Lastname, Birthdate) VALUES ('Max', 'Müller', '2000-09-01');  -DataSource $db"

Verbindungen nutzen

Möchte man mehr als nur One-Shoot Befehle ausführen bietet sich das öffnen und schließen von Verbindungen an. Dazu benötigt es nur einen weiteren Befehl. Anstelle der DataSource kann dann bei den Querys die SQLiteConnection zum Einsatz kommen.

$db = "PFAD_ZUM_ORDNER\db1.db"
$conn = New-SQLiteConnection @Verbose -DataSource $db
$conn.ConnectionString
$conn.State

Invoke-SqliteQuery -SQLiteConnection $conn -Query "SELECT * FROM user"

$conn.Close()
$conn.State

SQL-Injection umgehen mittels Parametern

_SQL-Injection bezeichnet das Ausnutzen einer Sicherheitslücke in Zusammenhang mit SQL-Datenbanken, die durch mangelnde Maskierung oder Überprüfung von Metazeichen in Benutzereingaben entsteht. Der Angreifer versucht dabei, über die Anwendung, die den Zugriff auf die Datenbank bereitstellt, eigene Datenbankbefehle einzuschleusen. Sein Ziel ist es, Daten auszuspähen, in seinem Sinne zu verändern, die Kontrolle über den Server zu erhalten oder einfach größtmöglichen Schaden anzurichten (Quelle: Wikipedia)_ Wie sieht das aber in der Praxis aus? Nehmen wir an das ein Programm Daten vom Benutzer einließt.

$vorname = Read-Host "Vorname"
$nachname = Read-Host "Nachname"
$gebdat = Read-Host "Geburtsdatum (YYYY-MM-DD)"

Würden diese Daten nun direkt in das Query eingegeben werden kann der Benutzer jede Zeichenkette zu einem SQL Befehl machen.

# SEHR SCHLECHT!!!!!! SQL-Injection anfällig!!!!!!!!!
$query = "INSERT INTO user(Firstname, Lastname, Birthdate) VALUES ('$vorname', '$nachname', '$gebdat')"

Gibt der Benutzer als Geburtsdatum dann etwas ein wie: 2016-10-10′); DROP TABLE user; — Dann wird aus dem obigen Befehl:

INSERT INTO user(Firstname, Lastname, Birthdate) VALUES ('xxxx', 'xxxx', '2016-10-10'); DROP TABLE user; --')"

Damit erzeugt das Statement einen Datensatz und löscht direkt darauf die Tabelle. Dies ist ein sehr destruktives Beispiel. In einem weniger schädlichen Szenario können damit weitere Datensätze erzeugt werden. Der richtige Weg ist die Nutzung von Parametern. Die SQLite Bibliothek übernimmt dann die Aufgabe der korrekten Maskierung von schädlichen Eingaben.

$query = "INSERT INTO user(Firstname, Lastname, Birthdate) VALUES (@firstname, @lastname, @birthdate)"
Invoke-SqliteQuery -SQLiteConnection $Script:Connection -Query $query -SqlParameters @{
  firstname = $vorname
  lastname = $nachname
  birthdate = $gebdat
}
Software Toolchains

Ich wurde bereits des öfteren gefragt welche Software ich für meine Anwendungen, Spiele und in der täglichen Arbeit einsetze. Je nach (Betriebs-)System und Einsatzzweck habe ich diverse Programme im Einsatz. Dabei setze ich auf ein Zusammenspiel von mächtigen professionellen Werkzeugen und Open Source Software (OSS).

Windows

Softwareentwicklung

  • WinMerge – Vergleichen von Dateien und Diff erstellen
  • .NET – Das .NET Framework
  • Microsoft ASP.NET MVC – Bibliothek für Model View Control ASP.NET Webseiten
  • Microsoft SQL Server – Datenbank von Microsoft
  • Microsoft Visual Studio – Entwicklungsumgebung für C# und viele weitere Sprachen
  • TortoiseSVN – Client für Subversion
  • Java Runtime Environment – Laufzeitumgebung für Java

Webentwicklung

  • Microsoft Visual Studio – Entwicklungsumgebung für C# und viele weitere Sprachen
  • Google Chrome – Browser von Google
  • Firefox – Browser der Mozilla Foundation
  • Fiddler – HTTP debugging proxy server Anwendung
  • FileZilla – FTP Programm für Datentransfer
  • Notepad++ – Extrem mächtiger Texteditor
  • Microsoft Visual Studio Code – Extrem mächtiger Codeeditor

Bildbearbeitung

  • Paint.NET – Zeichenprogramm
  • GIMP – Umfangreiche Bildbearbeitung
  • Inkscape – Bearbeitung von Vektorgrafiken/SVC Dateien
  • Adobe Photoshop – Das wohl beste Bildbearbeitungsprogramm (Abo über Cloud Photography Plan)

Sonstiges

  • VLC – Video- und Audioplayer für alle gängigen Dateiformate
  • Audacity – Audioberbeitung
  • Spotify – Musikstreaming
  • Microsoft Office – Office Paket von Microsoft
  • OpenOffice – Freies Office Paket
  • Adobe Reader – PDF Anezige
  • MiKTeX – Compiler und Pakete für LaTeX
  • TeXnicCenter – Editor für TeX/LaTeX
  • PDFCreator – PDF Erstellung
  • Avira – Antivirenprogramm
  • KeePass 2 – Passwortverwaltung
  • FreeMind – MindMap Erstellung
  • Dropbox – Cloud Datenspeicher
  • OneDrive – Cloud Datenspeicher
  • 7-Zip – Datenkomprimierung
  • WinRAR – Datenkomprimierung
  • Skype – Telefonie und Video über das Internet
  • Thunderbird – Mailclient
  • RD Tabs – Remotedesktop mit mehreren Tabs

Linux

Raspberry Pi

  • PuTTY – SSH Client für Windows

Ninite

Download der wichtigsten Tools über Ninite.

Großes Paket

.NET 4.6.1, Audacity, Avira, CDBurnerXP, Chrome, Dropbox, FileZilla, Firefox, GIMP, Google Drive, Inkscape, Java 8, KeePass 2, Notepad++, OneDrive, OpenOffice, Paint.NET, PuTTY, Skype, Spotify, Steam, Thunderbird, VLC, WinMerge, WinRAR

Kleines Paket

.NET 4.6.1, 7-Zip, Chrome, Dropbox, FileZilla, Firefox, GIMP, Inkscape, Java 8, KeePass 2, Notepad++, Paint.NET, PuTTY, Spotify, VLC, WinMerge

PowerShell Game 04

Nachdem die Spielwelt für das PowerShell Game existiert, wäre es doch schön wenn sich etwas darin bewegen könnte. Üblicherweise ist das die Spielfigur. In diesem Fall ein freundlicher Smiley.

Variablen

Alle Informationen die eine Spielfigur benötigt bekommt diese nun als Attribute.

$player = New-Object PSObject
Add-Member -InputObject $player -MemberType NoteProperty -Name x -Value 1
Add-Member -InputObject $player -MemberType NoteProperty -Name y -Value 0
Add-Member -InputObject $player -MemberType NoteProperty -Name symbol -Value '☺'

Bewegung

Im GameLoop sollen die Tasten n/e/s/w (North/East/South/West) eine Bewegung der Spielfigur ermöglichen.

switch($action)
{
    'q' { $runGame = $false; }
    'e' { $player.x++ }
    'w' { $player.x-- }
    'n' { $player.y-- }
    's' { $player.y++ }
}

Neben der Bewegung ist es außerdem hilfreich wenn der Spieler das Spielfeld nicht verlassen kann.

if($player.x -lt 0) { $player.x = 0 }
if($player.x -ge $global:mapWidth ) { $player.x = $global:mapWidth -1 }
if($player.y -lt 0) { $player.y = 0 }
if($player.y -ge $global:mapHeight ) { $player.y = $global:mapHeight -1 }

Spielfeld

Beim Spielfeld wird nun vor der Ausgabe eines Feldes ermittelt ob der Spieler auf diesem Feld steht. Falls dies der Fall ist, ist das angezeigte Symbol der Smiley in der Farbe des Spielers. Der Hintergrund erhält die Farbe des ursprünglichen Feldes.

function DrawMap()
[...]
# Farbe ermitteln
    $char = $global:map[$x + $y * $global:mapWidth]
        $foregroundColor = "Black"
        $backgroundColor = "White"

# Feldspezifische Farbe und Symbol ermitteln		
    switch($char)
    {
        'E' { $foregroundColor = "Black"; $backgroundColor = "DarkRed"; $char = '^' }
        '#' { $foregroundColor = "Gray"; $backgroundColor = "DarkGray"; $char = '#' }
        'L' { $foregroundColor = "DarkGreen"; $backgroundColor = "DarkGreen"; $char = ' ' }
        'S' { $foregroundColor = "Yellow"; $backgroundColor = "Yellow"; $char = ' ' }
        'B' { $foregroundColor = "White"; $backgroundColor = "White"; $char = ' ' }
        'D' { $foregroundColor = "Black"; $backgroundColor = "Black"; $char = ' ' }
        'G' { $foregroundColor = "DarkGreen"; $backgroundColor = "Green"; $char = '*' }
        'J' { $foregroundColor = "DarkGray"; $backgroundColor = "Gray"; $char = '''' }
        'W' { $foregroundColor = "DarkBlue"; $backgroundColor = "Blue"; $char = '~' }
    }
    
# Wenn der Spieler auf dem Feld ist - Symbold und Vordergrundfarbe ersetzen		
    if($x -eq $player.x -and $y -eq $player.y)
    {
        $foregroundColor = "Blue"
        $char = $player.symbol
    }
    
# Aktuelle Symbol-/Farbkombination ausgeben
    Write-Host -NoNewline -ForegroundColor $foregroundColor -BackgroundColor $backgroundColor $char
[...]