From 8f7d00307829fdd8d6943c084891d4e5f9b41f49 Mon Sep 17 00:00:00 2001
From: Commander-lol <ljcapitanio@gmail.com>
Date: Thu, 17 Dec 2015 18:29:48 +0000
Subject: [PATCH] Initial 1.0 working version

---
 .gitignore               |   5 +-
 app.js                   |  67 +++++++++++++++
 local_modules/reroute.js |  13 +++
 local_modules/xbridge.js |  62 ++++++++++++++
 package.json             |  22 +++++
 public/chat.html         |  46 +++++++++++
 public/index.html        | 173 +++++++++++++++++++++++++++++++++++++++
 scripts/kill_redis.sh    |   1 +
 scripts/setup.sh         |  21 +++++
 scripts/xscrp.sh         |  19 +++++
 10 files changed, 428 insertions(+), 1 deletion(-)
 create mode 100644 app.js
 create mode 100644 local_modules/reroute.js
 create mode 100644 local_modules/xbridge.js
 create mode 100644 package.json
 create mode 100644 public/chat.html
 create mode 100644 public/index.html
 create mode 100644 scripts/kill_redis.sh
 create mode 100644 scripts/setup.sh
 create mode 100755 scripts/xscrp.sh

diff --git a/.gitignore b/.gitignore
index d3f11de..0833166 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,4 +2,7 @@
 # https://help.github.com/articles/ignoring-files
 # Example .gitignore files: https://github.com/github/gitignore
 /bower_components/
-/node_modules/
\ No newline at end of file
+/node_modules/
+*.log
+*.rdb
+/db
\ No newline at end of file
diff --git a/app.js b/app.js
new file mode 100644
index 0000000..a0bf1da
--- /dev/null
+++ b/app.js
@@ -0,0 +1,67 @@
+var exec = require("child_process").exec,
+    level = require("levelup"),
+    http = require("combi-server")({
+        port: 8080,
+        appname: "Pompey Plays",
+        useAnsi: true,
+        websocket: {
+            enabled: true,
+            lib: true
+        }
+    }),
+    q = require("q"),
+    reroute = require("./local_modules/reroute"),
+    path = require("path"),
+    x = require("./local_modules/xbridge"),
+
+    ni = require("os").networkInterfaces()["eth0"].reduce(function(p, c){if(c.family == "IPv4"){return c;}else{return p;}}, null);
+
+console.dir(ni);
+
+reroute.add("/", "/index.html");
+reroute.add("/log", "/chat.html");
+
+http.preListen(function() {
+    var def = q.defer();
+    setTimeout(function() {
+        console.log("WAITED 1500MS");
+        def.resolve(101);
+    }, 1500);
+    return def.promise;
+})
+
+http.postListen(function() {
+    var def = q.defer();
+    setTimeout(function() {
+        console.log("WAITED 1500MS");
+        def.resolve(101);
+    }, 1500);
+    return def.promise;
+})
+
+http.use(reroute.middleware);
+
+http.static(path.join(__dirname, "public"));
+
+http.ws.do("input:json", function(conn, data) {
+    x.do(data.key)
+    .then(function(e) {
+        conn.sendPayload("input-response", x.errs[e]);
+        http.ws.broadcast(JSON.stringify({
+            type: "chat",
+            payload: {
+                client: conn._id,
+                message: "pressed " + data.key,
+                mode: "self"
+            }
+        }))
+    })
+    .catch(function(err) {
+        conn.sendPayload("input-response", {
+            code: 400,
+            message: err.message
+        })
+    });
+})
+
+http.listen();
diff --git a/local_modules/reroute.js b/local_modules/reroute.js
new file mode 100644
index 0000000..717b9fe
--- /dev/null
+++ b/local_modules/reroute.js
@@ -0,0 +1,13 @@
+var reroutes = {};
+
+module.exports = {
+    add: function (from, to) {
+        reroutes[from] = to;
+    },
+    middleware: function(req, res, next) {
+        if (reroutes.hasOwnProperty(req.url)) {
+            req.url = reroutes[req.url];
+        }
+        return next();
+    }
+}
diff --git a/local_modules/xbridge.js b/local_modules/xbridge.js
new file mode 100644
index 0000000..067fbed
--- /dev/null
+++ b/local_modules/xbridge.js
@@ -0,0 +1,62 @@
+var errmap = {
+        "0": {
+            code: 200,
+            message: "Ok"
+        },
+        "1": {
+            code: 404,
+            message: "Retro Arch Not Found"
+        },
+        "2": {
+            code: 400,
+            message: "Invalid Input Given"
+        }
+    },
+    controlmap = {
+        gba: {
+            left: "Left",
+            right: "Right",
+            up: "Up",
+            down: "Down",
+            a: "x",
+            b: "z",
+            r: "w",
+            l: "q",
+            select: "Shift_R",
+            start: "Return"
+        }
+    },
+    curControl = "gba";
+    exec = require("child_process").exec,
+    q = require("q");
+
+module.exports = {
+    do: function(virtualKey) {
+        var def = q.defer();
+        if (!controlmap[curControl].hasOwnProperty(virtualKey)) {
+            def.reject(new Error("Invalid Virtual Keypress"));
+        } else {
+            exec("bash scripts/xscrp.sh " + controlmap[curControl][virtualKey], function(err, stdout, stderr) {
+                if (err) {
+                    def.reject(err);
+                } else {
+                    def.resolve(stdout.toString());
+                }
+            });
+        }
+        return def.promise;
+    },
+    set controls (e) {
+        if (controlmap.hasOwnProperty(e)) {
+            curControl = e;
+        } else {
+            throw new Error("Invalid Control Scheme");
+        }
+    },
+    get controls () {
+        return curControl;
+    },
+    get errs () {
+        return errmap;
+    }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..fabfada
--- /dev/null
+++ b/package.json
@@ -0,0 +1,22 @@
+{
+  "name": "pompey-plays",
+  "version": "1.0.0",
+  "private": true,
+  "description": "Pompey plays video game multiplexing unicron badassery",
+  "main": "app.js",
+  "dependencies": {
+    "combi-server": "^1.3.1",
+    "leveldown": "^1.4.3",
+    "levelup": "^1.3.1",
+    "node-ip": "^0.1.2",
+    "q": "^1.4.1"
+  },
+  "scripts": {
+    "start": "NODE_ENV=production node app",
+    "kill-redis": "bash scripts/kill_redis.sh",
+    "inst-ubi": "bash scripts/setup.sh"
+  },
+  "devDependencies": {},
+  "author": "Louis Capitanchik",
+  "license": "ISC"
+}
diff --git a/public/chat.html b/public/chat.html
new file mode 100644
index 0000000..cd34a8f
--- /dev/null
+++ b/public/chat.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <meta charset="utf-8"/>
+        <title>PP :: LOG</title>
+        <style>
+            .message {
+                padding: 0.5em;
+            }
+            .message:not(:last-child) {
+                border-bottom: 1px solid grey;
+            }
+            .message strong {
+                padding-left: 0.2em;
+                padding-right: 0.3em;
+            }
+        </style>
+    </head>
+    <body>
+        <div id="log">
+
+        </div>
+        <script src="/ws.lib" type="application/javascript"></script>
+        <script type="application/javascript">
+            var socket = new CombiSocket(),
+                logBox = document.getElementById("log");
+            socket.on("chat", function(data) {
+                console.log(data);
+                var message = document.createElement("div"),
+                    user = document.createElement("strong"),
+                    content = data.mode === "self" ? document.createElement("em") : document.createElement("span");
+
+                message.classList.add("message");
+
+                user.textContent = data.client;
+                content.textContent = data.message;
+
+                message.appendChild(user);
+                message.appendChild(content);
+
+                logBox.appendChild(message);
+                logBox.scrollIntoView(false);
+            });
+        </script>
+    </body>
+</html>
diff --git a/public/index.html b/public/index.html
new file mode 100644
index 0000000..cddecab
--- /dev/null
+++ b/public/index.html
@@ -0,0 +1,173 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <meta charset="utf-8"/>
+        <meta content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0' name='viewport' />
+        <title>Pompey Plays...</title>
+        <style>
+            .button .box {
+                fill: #c3c5c4;
+                stroke: #555a5d;
+            }
+            .button .arrow {
+                fill: #555a5d;
+            }
+            .button text {
+                font-family: sans-serif;
+                font-size: 150%;
+                fill: #555a5d;
+                text-anchor: middle;
+                -webkit-user-select: none;
+                -moz-user-select: none;
+                -ms-user-select: none;
+                user-select: none;
+            }
+            .commands .button text {
+                font-size: 125%;
+            }
+            .group {
+                position: absolute;
+                width: 50%;
+                height: 50%;
+                display: block;
+                float: left;
+                clear: both;
+            }
+            .group.letters {
+                left: 50%;
+                top: 15%;
+            }
+            .group.commands {
+                left: 25%;
+                top: 50%;
+            }
+            #a .box {
+                fill: #c92313;
+            }
+            #b .box {
+                fill: #f0ba24;
+            }
+
+            .hitbox {
+                fill: black;
+                opacity: 0;
+                transition: opacity 0.1s linear;
+            }
+            .hitbox:hover {
+                opacity: 0.3;
+            }
+
+            .content, html, body {
+                margin: 0;
+                padding: 0;
+                width: 100%;
+                max-width: 500px;
+                height: 100%;
+                max-height: 500px;
+            }
+
+        </style>
+    </head>
+    <body>
+        <div class="content">
+            <div class="group arrows">
+                <!-- LEFT ARROW -->
+                <div id="cont-left" style="position:absolute;left:0px;top:125px;height:200px;width:200px;">
+                    <svg viewbox="0, 0, 100, 100" class="button" id="left">
+                        <path class="box" d="M 50,0 L 0,50 L 50,100 L 100,50 Z" />
+                        <path class="arrow" d="M 50,10 L 60,20 L 30,50 L 60,80 L 50,90 L 10,50 Z" />
+                        <path class="hitbox" d="M 50,0 L 0,50 L 50,100 L 100,50 Z" />
+                    </svg>
+                </div>
+
+                <!-- RIGHT ARROW -->
+                <div id="cont-right" style="position:absolute;left:250px;top:125px;height:200px;width:200px;">
+                    <svg viewbox="0, 0, 100, 100" class="button" id="right">
+                        <path class="box" d="M 50,0 L 0,50 L 50,100 L 100,50 Z" />
+                        <path class="arrow" d="M 50,10 L 40,20 L 70,50 L 40,80 L 50,90 L 90,50 Z" />
+                        <path class="hitbox" d="M 50,0 L 0,50 L 50,100 L 100,50 Z" />
+                    </svg>
+                </div>
+
+                <!-- UP ARROW -->
+                <div id="cont-up" style="position:absolute;left:125px;top:0px;height:200px;width:200px;">
+                    <svg viewbox="0, 0, 100, 100" class="button" id="up">
+                        <path class="box" d="M 50,0 L 0,50 L 50,100 L 100,50 Z" />
+                        <path class="arrow" d="M 10,50 L 20,60 L 50,30 L 80,60 L 90,50 L 50,10 Z" />
+                        <path class="hitbox" d="M 50,0 L 0,50 L 50,100 L 100,50 Z" />
+                    </svg>
+                </div>
+
+                <!-- DOWN ARROW -->
+                <div id="cont-down" style="position:absolute;left:125px;top:250px;height:200px;width:200px;">
+                    <svg viewbox="0, 0, 100, 100" class="button" id="down">
+                        <path class="box" d="M 50,0 L 0,50 L 50,100 L 100,50 Z" />
+                        <path class="arrow" d="M 10,50 L 20,40 L 50,70 L 80,40 L 90,50 L 50,90 Z" />
+                        <path class="hitbox" d="M 50,0 L 0,50 L 50,100 L 100,50 Z" />
+                    </svg>
+                </div>
+            </div>
+
+            <div class="group letters">
+                <div style="position:absolute;left:125px;top:0px;width:200px;height:200px;">
+                    <svg viewbox="0, 0, 100, 100" class="button" id="a">
+                        <circle class="box" r="35" cx="50" cy="50"/>
+                        <text x="50" y="50" dy="7">A</text>
+                        <circle class="hitbox" r="35" cx="50" cy="50"/>
+                    </svg>
+                </div>
+                <div style="position:absolute;left:0px;top:125px;width:200px;height:200px;">
+                    <svg viewbox="0, 0, 100, 100" class="button" id="b">
+                        <circle class="box" r="35" cx="50" cy="50"/>
+                        <text x="50" y="50" dy="7">B</text>
+                        <circle class="hitbox" r="35" cx="50" cy="50"/>
+                    </svg>
+                </div>
+            </div>
+
+            <div class="group commands">
+                <div style="position:absolute;left:0px;top:0px;width:200px;height:200px;">
+                    <svg viewbox="0, 0, 100, 100" class="button" id="select">
+                        <rect class="box" x="1" y="30" width="98" height="40" rx="25" ry="25"/>
+                        <text x="50" y="50" dy="7">SELECT</text>
+                        <rect class="hitbox" x="0" y="30" width="100" height="40" rx="25" ry="25"/>
+                    </svg>
+                </div>
+
+                <div style="position:absolute;left:225px;top:0px;width:200px;height:200px;">
+                    <svg viewbox="0, 0, 100, 100" class="button" id="start">
+                        <rect class="box" x="1" y="30" width="98" height="40" rx="25" ry="25"/>
+                        <text x="50" y="50" dy="7">START</text>
+                        <rect class="hitbox" x="0" y="30" width="100" height="40" rx="25" ry="25"/>
+                    </svg>
+                </div>
+            </div>
+        </div>
+
+        <script src="/ws.lib" type="application/javascript"></script>
+        <script>
+            var socket = new CombiSocket(),
+                keys = [
+                    "a",
+                    "b",
+                    "left",
+                    "right",
+                    "up",
+                    "down",
+                    "select",
+                    "start"
+                ],
+                sendKey = function(key) {
+                    socket.message("input", {key: key});
+                };
+            socket.on("input-response", function(data) {
+                console.log(data);
+            });
+
+            keys.map(function(e) {
+                document.querySelector("#" + e + " .hitbox").addEventListener("click", sendKey.bind(null, e));
+            })
+
+        </script>
+    </body>
+</html>
diff --git a/scripts/kill_redis.sh b/scripts/kill_redis.sh
new file mode 100644
index 0000000..9f9a13f
--- /dev/null
+++ b/scripts/kill_redis.sh
@@ -0,0 +1 @@
+ps -aux | grep redis | head -1 | tr -s ' ' | cut -d ' ' -f 2 | xargs kill $1
diff --git a/scripts/setup.sh b/scripts/setup.sh
new file mode 100644
index 0000000..b1a34f9
--- /dev/null
+++ b/scripts/setup.sh
@@ -0,0 +1,21 @@
+#!/bin/bash
+PDIR=`pwd`
+
+if ! [ -x "$(command -v xdotool)" ]; then # RetroArch doesn't do command lines nicely, has to be bundled with xdotool check
+    sudo add-apt-repository ppa:libretro/stable
+    sudo apt-get update
+    sudo apt-get install xdotool retroarch retroarch-* libretro-*
+fi
+
+# Enable if switching levelDB to Redis
+#if ! [ -x ~/Programs/$REDISVER/src/redis-server ]; then
+#    REDISVER="redis-3.0.5"
+#    mkdir -p ~/Programs/tmp
+#    mkdir -p ~/Programs/$REDISVER
+#    cd ~/Programs/tmp
+#    wget http://download.redis.io/releases/$REDISVER.tar.gz
+#    tar xzf $REDISVER.tar.gz -C ../
+#    cd ../$REDISVER
+#    make
+#    cd $PDIR
+#fi
diff --git a/scripts/xscrp.sh b/scripts/xscrp.sh
new file mode 100755
index 0000000..0c2db0b
--- /dev/null
+++ b/scripts/xscrp.sh
@@ -0,0 +1,19 @@
+#!/bin/bash
+
+WID=`xdotool search --name RetroArch`
+VALID="_z__x__q__w__Left__Right__Up__Down__Shift_R__Return_"
+if [[ $WID == "" ]]
+then
+    echo 1
+else
+    if [[ $VALID == *"_$1_"* ]]
+    then
+        xdotool windowactivate $WID
+        xdotool windowfocus $WID
+        xdotool keydown $1
+        xdotool keyup $1
+        echo 0
+    else
+        echo 2
+    fi
+fi
-- 
GitLab