Pārlūkot izejas kodu

Rift Dungeon runs logged. Addaptations in the runs page for the new data.

Iñigo Valentin 6 gadi atpakaļ
vecāks
revīzija
96ea046196

+ 4 - 1
application/API/v2/API_Controller.php

@@ -4,7 +4,7 @@
      * v2 API Controller file.
      *
      * Provides a class to handle all posible API requests.
-     * 
+     *
      * @category Constroller
      */
 
@@ -53,6 +53,9 @@
                     case "upload_run_toa":
                         require_once(__DIR__ . "/upload_run_toa.php");
                         break;
+                    case "upload_run_rift":
+                        require_once(__DIR__ . "/upload_run_rift.php");
+                        break;
                     case "units":
                         require_once(__DIR__ . "/units.php");
                         break;

+ 4 - 1
application/API/v2/bin/upload_run_dimension.py

@@ -157,7 +157,10 @@ def parseRun(db, data_request, data_response):
     id = cursor.fetchone()[0]
     if id == None:
         id = 1
-    insert(db, "run", (uid, id, dtime, area_type, area, stage, difficulty, win, time, mana, energy, crystal, helper))
+
+    score = None
+    rank = None
+    insert(db, "run", (uid, id, dtime, area_type, area, stage, difficulty, win, time, mana, energy, crystal, helper, score, rank))
 
     # Parse various types of reward
     crate = data_response["reward"]["crate"]

+ 4 - 1
application/API/v2/bin/upload_run_dungeon.py

@@ -158,7 +158,10 @@ def parseRun(db, data_request, data_response):
     id = cursor.fetchone()[0]
     if id == None:
         id = 1
-    insert(db, "run", (uid, id, dtime, area_type, area, stage, difficulty, win, time, mana, energy, crystal, helper))
+
+    score = None
+    rank = None
+    insert(db, "run", (uid, id, dtime, area_type, area, stage, difficulty, win, time, mana, energy, crystal, helper, score, rank))
 
     # Parse various types of reward
     crate = data_response["reward"]["crate"]

+ 274 - 0
application/API/v2/bin/upload_run_rift.py

@@ -0,0 +1,274 @@
+#!/usr/bin/python3
+
+import sqlite3
+import json
+import sys
+import os
+
+import MAPPING
+
+"""
+Reads the API KEY, that must be passed as first command line argument.
+
+:returns: Recovered API KEY.
+:raises Exception: Th KEY couldn't be red.
+"""
+def readKey():
+    try:
+        key = sys.argv[1]
+        return key
+    except Exception as e:
+        print("Error parsing API KEY: " + str(e))
+        raise
+
+"""
+Reads the JSON data, that must be passed as second command line argument.
+
+:param: index 2 for start data, 3 for result data, 4 for event start request, 5
+for event start response.
+:returns: Recovered data, in JSON format.
+:raises Exception: The data couldn't be red or converted to JSON.
+"""
+def readData(index):
+    try:
+        data = json.loads(sys.argv[index])
+        return data
+    except Exception as e:
+        print("Error parsing data: " + str(e))
+        raise
+
+"""
+Verifies that the API key matches the player data and that it exists in th DB.
+
+:param db: Connection to the database.
+:returns: Connection to the database.
+:param data: Data in json format.
+:param key: API KEY.
+:returns: True if key and player match, False otherwise.
+:raises IntegrityError: The queryes couldn't bre executed.
+"""
+def verifyKey(db, data, key):
+    print('Verifying KEY...')
+    status = False
+    try:
+        uid = data["wizard_info"]["wizard_id"]
+        cursor = db.cursor()
+        cursor.execute('SELECT count(uid) AS c FROM player WHERE uid = ? AND api_key = ?;', (uid, key))
+        if cursor.fetchone()[0] == 1:
+            status = True
+        cursor.close()
+    except sqlite3.IntegrityError as e:
+        print("Error executing statement: " + str(e))
+        raise
+    return status
+
+"""
+Opens the database file and deletes from the user tables
+
+:param name: The path to the sqlite database.
+:returns: Connection to the database.
+:raises IntegrityError: The queryes couldn't bre executed.
+:raises IOError: The sqlite file couldn't be created.
+"""
+def openDatabase():
+    kdb = os.path.dirname(os.path.realpath(sys.argv[0])) + '/../../../sw.sqlite'
+    udb = os.path.dirname(os.path.realpath(sys.argv[0])) + '/../../../../data/sw.sqlite'
+    print('Configuring database...')
+    try:
+        db = sqlite3.connect(kdb)
+        cursor = db.cursor()
+        cursor.execute('attach "' + udb + '" as data;')
+        cursor.close()
+        return db
+    except sqlite3.IntegrityError as e:
+        print("Error executing statement: " + str(e))
+        raise
+    except IOError as e:
+        print("I/O Error creating database " + name + ": " + str(e))
+        raise
+
+"""
+Inserts a row into the database.
+
+:param db: Connection to the database.
+:param table: Name of the table to insert into.
+:param values: List of values to insert.
+:raises IntegrityError: The insert query was unsuccesfull.
+"""
+def insert(db, table, values):
+    cursor = db.cursor()
+    placeholders = ''
+    for x in range(0, len(values)):
+        placeholders = placeholders + '?, '
+    placeholders = placeholders[:len(placeholders) - 2]
+    query = 'INSERT INTO ' + table + ' VALUES (' + placeholders + ')'
+    try:
+        cursor.execute(query, values)
+    except sqlite3.IntegrityError as e:
+        print('Error Inserting into ' + table + ' with values (' + str(values) + '): ' + str(e))
+        raise
+    cursor.close;
+
+"""
+Parses run data (tables run, run_party, run_drop_rune, run_drop_runecraft,
+run_drop_item, run_drop_unit, run_drop_sd, run_drop_unit_pieces,
+run_drop_shapeshifting).
+
+:param db: Sqlite database connection.
+:param data_request: JSON data of the run result request.
+:param data_response: JSON data of the run result response.
+:param data_start_request: JSON data of the run start request.
+:param data_start_response: JSON data of the run start response.
+"""
+def parseRun(db, data_request, data_response, data_start_request, data_start_response):
+    print("Parsing run...")
+    cursor = db.cursor()
+
+    # Read basic data
+    uid = data_request["wizard_id"]
+    dtime = data_response["tvaluelocal"]
+
+    # Check if run has already been inserted.
+    cursor.execute("SELECT count(id) AS c FROM run WHERE uid = ? AND dtime = ?;", (uid, dtime))
+    if (cursor.fetchone()[0] > 0):
+        print("    Run already in database. Stopping....")
+        return 409;
+
+    # We are inserting, get aditional info
+    area_type = 3 # Rift Dungeon
+    area = data_request["dungeon_id"]
+    stage = None
+    difficulty = None
+    score = data_response["total_damage"]
+    rank = "F"
+    raw_rank = data_response["rift_dungeon_box_id"]
+    win = 1
+    if raw_rank == 0:
+        rank = "F"
+        win = 0
+    elif raw_rank == 1:
+        rank = "D"
+    elif raw_rank == 2:
+        rank = "C"
+    elif raw_rank == 3:
+        rank = "B-"
+    elif raw_rank == 4:
+        rank = "B"
+    elif raw_rank == 5:
+        rank = "B+"
+    elif raw_rank == 6:
+        rank = "A-"
+    elif raw_rank == 7:
+        rank = "A"
+    elif raw_rank == 8:
+        rank = "A+"
+    elif raw_rank == 9:
+        rank = "S"
+    elif raw_rank == 10:
+        rank = "SS"
+    elif raw_rank == 11:
+        rank = "SSS"
+    # TODO
+    time = 1
+    #time = data_response["clear_time"]
+
+    # Get the new ID and insert
+    cursor.execute("SELECT max(id) + 1 AS id FROM run;")
+    id = cursor.fetchone()[0]
+    if id == None:
+        id = 1
+    mana = 0
+    energy = 0
+    crystal = 0
+    helper = 0
+    insert(db, "run", (uid, id, dtime, area_type, area, stage, difficulty, win, time, mana, energy, crystal, helper, score, rank))
+
+
+    for item in data_response["item_list"]:
+        type = item["type"]
+
+        if type == 29: # Craft material
+            insert(db, "run_drop_item", (id, item["id"], item["quantity"]))
+
+        elif type == 8: # Rune
+            rune = item["info"]
+            rune_id = rune["rune_id"]
+            rune_type = rune["set_id"]
+            slot = rune["slot_no"]
+            stars = rune["class"]
+            ancient = 0
+            quality = rune["rank"]
+            value = rune["sell_value"]
+            efficiency, max_efficiency = MAPPING.calculate_efficiency(rune)
+            main_stat = rune["pri_eff"][0]
+            main_stat_value = rune["pri_eff"][1]
+            if "prefix_eff" in rune:
+                innate_stat = rune["prefix_eff"][0]
+                innate_stat_value = rune["prefix_eff"][1]
+            else:
+                innate_stat = 0
+                innate_stat_value = 0
+            substat_1 = 0
+            substat_1_value = 0
+            substat_2 = 0
+            substat_2_value = 0
+            substat_3 = 0
+            substat_3_value = 0
+            substat_4 = 0
+            substat_4_value = 0
+            if "0" in rune["sec_eff"]:
+                substat_1 = rune["sec_eff"]["0"]["0"]
+                substat_1_value = rune["sec_eff"]["0"]["1"]
+            if "1" in rune["sec_eff"]:
+                substat_1 = rune["sec_eff"]["1"]["0"]
+                substat_1_value = rune["sec_eff"]["1"]["1"]
+            if "2" in rune["sec_eff"]:
+                substat_1 = rune["sec_eff"]["2"]["0"]
+                substat_1_value = rune["sec_eff"]["2"]["1"]
+            if "3" in rune["sec_eff"]:
+                substat_1 = rune["sec_eff"]["3"]["0"]
+                substat_1_value = rune["sec_eff"]["3"]["1"]
+            insert(db, "run_drop_rune", (id, rune_id, rune_type, slot, stars, ancient, quality, value, efficiency, max_efficiency, main_stat, main_stat_value, innate_stat, innate_stat_value, substat_1, substat_1_value, substat_2, substat_2_value, substat_3, substat_3_value, substat_4, substat_4_value))
+
+        # TODO: Grindstones and gems
+        # TODO: Unit
+
+    # Parse party
+    leader_slot = data_start_request["leader_index"]
+    for unit in data_start_request["unit_id_list"]:
+        unit_id = unit["unit_id"]
+        slot = unit["slot_index"]
+        leader = 0
+        front = 0
+        if slot == leader_slot:
+            leader = 1
+        if slot <= 3:
+            front = 1
+        cursor.execute("SELECT unit FROM unit WHERE uid = ? AND id = ?;", (uid, unit_id))
+        unit_master_id = cursor.fetchone()[0]
+        insert(db, "run_party", (id, unit_id, unit_master_id, leader, front))
+
+    db.commit()
+
+
+"""
+Begin script
+"""
+data_request = readData(2)
+data_response = readData(3)
+data_start_request = readData(4)
+data_start_response = readData(5)
+key = readKey()
+db = openDatabase()
+if verifyKey(db, data_response, key) == False:
+    print("Invalid API KEY...")
+    sys.exit(401)
+else:
+    try:
+        parseRun(db, data_request, data_response, data_start_request, data_start_response)
+    except Exception as e:
+        print("Error parsing Rift Dungeon run: " + str(e))
+        sys.exit(400)
+sys.exit(201)
+
+

+ 3 - 1
application/API/v2/bin/upload_run_scenario.py

@@ -148,7 +148,9 @@ def parseRun(db, data):
     id = cursor.fetchone()[0]
     if id == None:
         id = 1
-    insert(db, "run", (uid, id, dtime, area, stage, difficulty, win, time, mana, energy, crystal, helper))
+    score = None
+    rank = None
+    insert(db, "run", (uid, id, dtime, area_type, area, stage, difficulty, win, time, mana, energy, crystal, helper, score, rank))
     if data["shapeshifting"] > 0:
         insert(db, "run_drop_shapeshifting", (id, data["shapeshifting"]))
     if data["sd"] > 0:

+ 3 - 1
application/API/v2/bin/upload_run_toa.py

@@ -183,7 +183,9 @@ def parseRun(db, data_request, data_response):
         insert(db, "run_drop_item", (id, 8, 1))
 
     # Insert
-    insert(db, "run", (uid, id, dtime, area_type, area, stage, difficulty, win, time, mana, energy, crystal, helper))
+    score = None
+    rank = None
+    insert(db, "run", (uid, id, dtime, area_type, area, stage, difficulty, win, time, mana, energy, crystal, helper, score, rank))
 
     # Parse units
     i = 0

+ 1 - 1
application/API/v2/upload_profile.php

@@ -16,7 +16,7 @@
 
     try{
         // Check data
-        $data = filter_input(INPUT_POST, 'data');
+        $data = filter_input(INPUT_POST, 'response');
         if ($data == null || $data == false){
             http_response_code(400);
             return 400;

+ 1 - 11
application/API/v2/upload_run_dungeon.php

@@ -14,17 +14,11 @@
 
     global $db;
 
-//foreach($_POST as $k => $v) {
-//    error_log(" POST $k: $v");
-//}
-
-
     try{
         // Check data
         $data_request = filter_input(INPUT_POST, 'request');
-        $data_response= filter_input(INPUT_POST, 'response');
+        $data_response = filter_input(INPUT_POST, 'response');
         if ($data_request == null || $data_request == false || $data_response == null || $data_response == false){
-error_log("error1");
             http_response_code(400);
             return 400;
         }
@@ -40,7 +34,6 @@ error_log("error1");
         $json_request = json_decode($data_request);
         $json_response = json_decode($data_response);
         if ($json_response === null | $json_request === null){
-error_log("error2");
             http_response_code(400);
             return 400;
         }
@@ -58,7 +51,6 @@ error_log("error2");
         $cmd = __DIR__ . "/bin/upload_run_dungeon.py " . $key . " " . escapeshellarg($data_request) . " " . escapeshellarg($data_response);
         $out = [];
         $ret = 0;
-//error_log($cmd);
         try{
             exec($cmd, $out, $ret);
         }
@@ -71,7 +63,6 @@ error_log("error2");
         if ($ret != 201){
             try{
                 http_response_code($ret);
-error_log("NO 201: $ret");
                 return $ret;
             }
             catch(Exception $e) {
@@ -80,7 +71,6 @@ error_log("NO 201: $ret");
                 return 500;
             }
         }
-error_log("ACTUAL RET: " . $ret);
         // At this point, status code should be 200
         http_response_code($ret);
         return $ret;

+ 100 - 0
application/API/v2/upload_run_rift.php

@@ -0,0 +1,100 @@
+<?php
+    /**
+     * Dungeon run logger script.
+     *
+     * Exposes an API to save a run to the database.
+     * Reads post data and calls the upload_dungeon_run.py script.
+     * Mandatory POST parameters are:
+     *  - request: Intercepted game request.
+     *  - response: Intercepted game response.
+     *  - key: User API key.
+     *
+     * @category API
+     */
+
+    global $db;
+
+    try{
+        // Check data
+        $data_request = filter_input(INPUT_POST, 'request');
+        $data_response = filter_input(INPUT_POST, 'response');
+        $data_start_request = filter_input(INPUT_POST, 'start_request');
+        $data_start_response = filter_input(INPUT_POST, 'start_response');
+        if (
+          $data_request == null ||
+          $data_request == false ||
+          $data_response == null ||
+          $data_response == false ||
+          $data_start_request == null ||
+          $data_start_request == false ||
+          $data_start_response == null ||
+          $data_start_response == false
+        ){
+            http_response_code(400);
+            return 400;
+        }
+
+        // Check API key.
+        $key = filter_input(INPUT_POST, 'key');
+        if ($key == null || $key == false){
+            http_response_code(401);
+            return 401;
+        }
+
+        // Check data format.
+        $json_request = json_decode($data_request);
+        $json_response = json_decode($data_response);
+        if ($json_response === null | $json_request === null){
+            http_response_code(400);
+            return 400;
+        }
+
+        // Authenticate
+        $uid = $json_request->{"wizard_id"};
+        $uname = $json_response->{"wizard_info"}->{"wizard_name"};
+        $s = "SELECT COUNT(uid) AS c FROM player WHERE uid = $uid AND api_key = '$key';";
+        if (1 != $db->query($s)->fetchArray(SQLITE3_ASSOC)["c"]){
+            http_response_code(401);
+            return 401;
+        }
+
+        // Run dungeon run parser script
+        $cmd = __DIR__ . "/bin/upload_run_rift.py " .
+          $key . " " .
+          escapeshellarg($data_request) . " " .
+          escapeshellarg($data_response) . " " .
+          escapeshellarg($data_start_request) . " " .
+          escapeshellarg($data_start_response);
+        $out = [];
+        $ret = 0;
+        try{
+            exec($cmd, $out, $ret);
+        }
+        catch(Exception $e) {
+            error_log("Error running Rift Dungeon run script: " . $e->getMessage());
+            http_response_code(500);
+            return 500;
+        }
+
+        if ($ret != 201){
+            try{
+                error_log("Rift Dungeon run script returned an unexpected value $ret: ");
+                http_response_code($ret);
+                return $ret;
+            }
+            catch(Exception $e) {
+                error_log("Rift Dungeon run script returned an unexpected value $ret: " . $e->getMessage());
+                http_response_code(500);
+                return 500;
+            }
+        }
+        // At this point, status code should be 201
+        http_response_code($ret);
+        return $ret;
+    }
+    catch(Exception $e) {
+        error_log("Unknown error parsing Rift Dungeon run: " . $e->getMessage());
+        http_response_code(500);
+        return 500;
+    }
+?>

+ 39 - 4
application/entity/Run.php

@@ -88,6 +88,16 @@
          */
         public $helper;
 
+        /**
+         * @var int Score in battles that are rated (WB, rift dungeons...).
+         */
+        public $score;
+
+        /**
+         * @var String Rank in battles that are rated (WB, rift dungeons...).
+         */
+        public $rank;
+
         /**
          * @var \Unit[]|\K_Unit[] Uits used.
          * If the unit is still owned, it will be a instance of Unit.
@@ -95,6 +105,16 @@
          */
         public $party = [];
 
+        /**
+         * @var int ID of the leader unit.
+         */
+        public $leader;
+
+        /**
+         * @var int[] List of IDs of the frontline units.
+         */
+        public $frontline = [];
+
         /**
          * @var \Inventory[] Dropped items.
          */
@@ -154,7 +174,9 @@
                   mana,
                   energy,
                   crystal,
-                  helper
+                  helper,
+                  score,
+                  rank
                 FROM run
                 WHERE id = $id;
             ";
@@ -171,7 +193,7 @@
                 $type = AREA_TYPE_ID::SCENARIO;
             }
             elseif (APPLICATION::valid_id("ELEMENTAL_RIFT_DUNGEON_ID", $r["area"]) && $r["stage"] < 1){
-                $type = AREA_TYPE_ID::ELEMENTAL_RIFT_DUNGEON;
+                $type = AREA_TYPE_ID::RIFT_DUNGEON;
             }
             elseif (APPLICATION::valid_id("DUNGEON_ID", $r["area"])){
                 $type = AREA_TYPE_ID::CAIROS_DUNGEON;
@@ -185,13 +207,20 @@
             $this->energy = $r["energy"];
             $this->crystal = $r["crystal"];
             $this->helper = $r["helper"];
+            $this->score = $r["score"];
+            $this->rank = $r["rank"];
             $s = "
                 SELECT
                   unit,
                   k_unit,
-                  (SELECT count(id) FROM unit WHERE unit.unit = k_unit) AS owned
+                  (SELECT count(id) FROM unit WHERE unit.unit = k_unit) AS owned,
+                  leader,
+                  front
                 FROM run_party
-                WHERE run = $id;
+                WHERE run = $id
+                ORDER BY 
+                  front DESC,
+                  leader DESC;
             ";
             $q = $db->query($s);
             while ($r = $q->fetchArray(SQLITE3_ASSOC)){
@@ -201,6 +230,12 @@
                 else{
                     array_push($this->party, new K_Unit($r["k_unit"], false));
                 }
+                if ($r["leader"] == 1){
+                    $this->leader = $r["unit"];
+                }
+                if ($r["front"] == 1){
+                    array_push($this->frontline, $r["unit"]);
+                }
             }
             $s = "
                 SELECT

+ 31 - 10
application/view/runs.php

@@ -131,7 +131,7 @@
                             Team
                         </th>
                         <th>
-                            Time
+                            Time/Rank
                         </th>
                         <th>
                             Reward
@@ -189,7 +189,15 @@
                                     </div>
 <?php
                                 }
+                                $prev_front = false;
+                                $front = "undefined";
                                 foreach ($run->party as $party){
+                                    $front = in_array($party->id, $run->frontline);
+                                    if ($front != "undefined" && $prev_front != $front){
+?>
+                                        <hr class='frontline'/>
+<?php
+                                    }
                                     if ($party instanceof K_Unit){
                                         $enable_save = false;
                                         $disabled = "disabled";
@@ -212,26 +220,39 @@
                                         </a>
 <?php
                                     }
+                                    $prev_front = $front;
                                 }
 ?>
                             </td>
                             <td class='time'>
 <?php
-                                if ($run->win == 1){
-                                    $s = floor($run->time / 1000);
-                                    $m = floor($s / 60);
-                                    $s = floor($s % 60);
-                                    $ms = ($run-> time - (1000 * 60 * $m) - (1000 * 60 * $s) / 100);
-                                    $s = str_pad($s, 2, '0', STR_PAD_LEFT);
-                                    $ms = str_pad($ms, 2, '0', STR_PAD_LEFT);
+                                if ($run->area->type == AREA_TYPE_ID::RIFT_DUNGEON){
 ?>
-                                    <?=$m?>:<?=$s?><span class='ms'>.<?=$ms?></span>
+                                    <span class='rank'>
+                                        <?=$run->rank?>
+                                    </span>
+                                    <span class='score'>
+                                        <?=$run->score?>
+                                    </span>
 <?php
                                 }
                                 else{
+                                    if ($run->win == 1){
+                                        $s = floor($run->time / 1000);
+                                        $m = floor($s / 60);
+                                        $s = floor($s % 60);
+                                        $ms = ($run-> time - (1000 * 60 * $m) - (1000 * 60 * $s) / 100);
+                                        $s = str_pad($s, 2, '0', STR_PAD_LEFT);
+                                        $ms = str_pad($ms, 2, '0', STR_PAD_LEFT);
+?>
+                                        <?=$m?>:<?=$s?><span class='ms'>.<?=$ms?></span>
+<?php
+                                    }
+                                    else{
 ?>
-                                    <span class='defeated'>Defeated</span>
+                                        <span class='defeated'>Defeated</span>
 <?php
+                                    }
                                 }
 ?>
                             </td>

+ 3 - 1
install_data/install_data.sql

@@ -212,7 +212,9 @@ CREATE TABLE run(
     mana INT NOT NULL CHECK(mana >= 0),
     energy INT NOT NULL CHECK(energy >= 0),
     crystal INT NOT NULL CHECK(crystal >= 0),
-    helper INT NOT NULL CHECK(helper IN (0, 1))
+    helper INT NOT NULL CHECK(helper IN (0, 1)),
+    score INT,
+    rank TEXT
 );
 CREATE TABLE run_drop_rune(
     run INT NOT NULL REFERENCES run(id),

+ 14 - 0
public/css/runs.css

@@ -81,6 +81,20 @@ table#runs td.time span.defeated{
     text-shadow: 0 0 0.05em #ffffff;
 }
 
+table#runs td.time span.rank{
+    display: inline-block;
+    vertical-align: bottom;
+    font-size: 250%;
+    color: #ff6633;
+    text-shadow: 0 0 0.1em #000, 0 0 0.1em #000, 0 0 0.1em #000, 0 0 0.1em #000, 0 0 0.1em #000, 0 0 0.2em #ff0, 0 0 0.2em #ff0, 0 0 0.2em #ff0, 0 0 0.2em #ff0;
+    font-style: italic;
+}
+
+table#runs td.time span.rank{
+    display: inline-block;
+    vertical-align: bottom;
+}
+
 table#runs td.reward div.item{
     display: inline-block;
     width: 1.6em;

+ 69 - 245
swex-plugin/swdb.js

@@ -80,224 +80,94 @@ module.exports = {
      */
     processCommand(command, req, res){
         // Process the command
+        var apiCommand = "";
+        var success = null;
+        var start = false;
         switch (command){
-            case 'HubUserLogin':
-                this.uploadProfile(res);
-                break;
+
+            // Start events. Save in case they are needed.
             case 'BattleDungeonStart':
+            case 'BattleScenarioStart':
+            case 'BattleDimensionHoleDungeonStart':
+            case 'BattleTrialTowerStart_v2':
+            case 'BattleRiftDungeonStart':
                 this.eventStartReq = req;
                 this.eventStartRes = res;
                 break;
-            case 'BattleDungeonResult':
-                this.uploadRunDungeon(req, res);
+
+            // Profile login
+            case 'HubUserLogin':
+                apiCommand = "upload_profile";
+                success = "Profile uploaded sucesfully!"
                 break;
-            case 'BattleScenarioStart':
-                this.eventStart = req;
-                this.eventStartRes = res;
+
+            // Battle runs
+            case 'BattleDungeonResult':
+                apiCommand = "upload_run_dungeon";
+                success = "Cairos run logged sucesfully!"
                 break;
             case 'BattleScenarioResult':
-                this.uploadRunScenario(req, res);
-                break;
-            case 'BattleDimensionHoleDungeonStart':
-                this.eventStartReq = req;
-                this.eventStartRes = res;
+                apiCommand = "upload_run_scenario";
+                success = "Scenario run logged sucesfully!"
                 break;
             case 'BattleDimensionHoleDungeonResult':
-                this.uploadRunDimensionDungeon(req, res);
-                break;
-            case 'BattleTrialTowerStart_v2':
-                this.eventStart = req;
-                this.eventStartRes = res;
+                apiCommand = "upload_run_dimension";
+                success = "Dimension Hole run logged sucesfully!"
                 break;
             case 'BattleTrialTowerResult_v2':
-                this.uploadTOARun(res, req);
+                apiCommand = "upload_run_toa";
+                success = "TOA run logged sucesfully!"
+                break;
+            case 'BattleRiftDungeonResult':
+                apiCommand = "upload_run_rift";
+                success = "Rift Dungeon run logged sucesfully!"
+                start = true;
                 break;
         }
+        if (apiCommand != ""){
+            this.command(apiCommand, req, res, start, success);
+        }
     },
 
     /**
-     * Makes a call to the upload_profile API to sync user data.
-     *
-     * @param req The full request data.
-     */
-    uploadProfile(res){
-        this.log('info', 'Uploading profile to SWDB...');
-        // Generate POST data
-        var post_data = querystring.stringify({
-            'data' : JSON.stringify(res),
-            'key' : config.Config.Plugins[this.pluginName].apiKey
-        });
-        // Configure POST requets
-        var post_options = {
-            host: config.Config.Plugins[this.pluginName].host,
-            port: config.Config.Plugins[this.pluginName].port,
-            path: '/API/v2/upload_profile',
-            method: 'POST',
-            headers: {
-                'Content-Type': 'application/x-www-form-urlencoded',
-                'Content-Length': Buffer.byteLength(post_data)
-            }
-        };
-        // Set up the request
-        var post_req = http.request(post_options, (function(post_res) {
-            post_res.setEncoding('utf8');
-            post_res.on('data', (function (body) {
-                switch (post_res.statusCode){
-                    case 200:
-                        this.log('success', 'Profile successfully uploaded.');
-                        break;
-                    case 201:
-                        this.log('success', 'Profile successfully uploaded.');
-                        break;
-                    case 400:
-                        this.log('error', `There were errors importing the data: ${JSON.stringify(body)}`);
-                        break;
-                    case 404:
-                        this.log('error', 'URL not found.');
-                        break;
-                    case 500:
-                        this.log('error', 'The server returned an error.');
-                        break;
-                    default:
-                        this.log('error', `Unexpected error importing profile: ${post_res.statusCode}`);
-                }
-            }).bind(this));
-        }).bind(this));
-        // Perform the request
-        post_req.write(post_data);
-        post_req.end();
-    },
-
-    /**
-     * Makes a call to the upload_run_dungeon API to register a dungeon run.
-     *
-     * @param req The full request data.
-     * @param req The full response data.
-     */
-    uploadRunDungeon(req, res){
-        this.log('info', 'Uploading dungeon run to SWDB...')
-        // Generate POST data
-        var post_data = querystring.stringify({
-            'request': JSON.stringify(req),
-            'response': JSON.stringify(res),
-            'key' : config.Config.Plugins[this.pluginName].apiKey
-        });
-        // Configure POST requets
-        var post_options = {
-            host: config.Config.Plugins[this.pluginName].host,
-            port: config.Config.Plugins[this.pluginName].port,
-            path: '/API/v2/upload_run_dungeon',
-            method: 'POST',
-            headers: {
-                'Content-Type': 'application/x-www-form-urlencoded',
-                'Content-Length': Buffer.byteLength(post_data)
-            }
-        };
-        // Set up the request
-        var post_req = http.request(post_options, (function(post_res) {
-            post_res.setEncoding('utf8');
-            post_res.on('data', (function (body) {
-                switch (post_res.statusCode){
-                    case 200:
-                        this.log('success', 'Dungeon run successfully uploaded.');
-                        break;
-                    case 201:
-                        this.log('success', 'Dungeon run successfully uploaded.');
-                        break;
-                    case 400:
-                        this.log('error', `There were errors importing the run: ${JSON.stringify(body)}`);
-                        break;
-                    case 404:
-                        this.log('error', 'URL not found.');
-                        break;
-                    case 500:
-                        this.log('error', 'The server returned an error.');
-                        break;
-                    default:
-                        this.log('error', `Unexpected error importing run: ${post_res.statusCode}`);
-                }
-            }).bind(this));
-        }).bind(this));
-        // Perform the request
-        post_req.write(post_data);
-        post_req.end();
-    },
-
-    /**
-     * Makes a call to the upload_run_dimension API to register a Dimension Hole
-     * Dungeon run.
+     * Calls an arbitrary command on the SWDB APIv2.
      *
+     * @param command Command nade.
      * @param req The full request data.
-     * @param req The full response data.
+     * @param res The full response data.
+     * @param start Indicates if event start request and response are needed.
+     * @param success Optional. Message to show on success.
      */
-    uploadRunDimensionDungeon(req, res){
-        this.log('info', 'Uploading Dimension Hole Dungeon run to SWDB...')
-        // Generate POST data
-        var post_data = querystring.stringify({
-            'request': JSON.stringify(req),
-            'response': JSON.stringify(res),
-            'key' : config.Config.Plugins[this.pluginName].apiKey
-        });
-        // Configure POST requets
-        var post_options = {
-            host: config.Config.Plugins[this.pluginName].host,
-            port: config.Config.Plugins[this.pluginName].port,
-            path: '/API/v2/upload_run_dimension',
-            method: 'POST',
-            headers: {
-                'Content-Type': 'application/x-www-form-urlencoded',
-                'Content-Length': Buffer.byteLength(post_data)
-            }
-        };
-        // Set up the request
-        var post_req = http.request(post_options, (function(post_res) {
-            post_res.setEncoding('utf8');
-            post_res.on('data', (function (body) {
-                switch (post_res.statusCode){
-                    case 200:
-                        this.log('success', 'Dimension Hole Dungeon run successfully uploaded.');
-                        break;
-                    case 201:
-                        this.log('success', 'Dimension Hole Dungeon run successfully uploaded.');
-                        break;
-                    case 400:
-                        this.log('error', `There were errors importing the run: ${JSON.stringify(body)}`);
-                        break;
-                    case 404:
-                        this.log('error', 'URL not found.');
-                        break;
-                    case 500:
-                        this.log('error', 'The server returned an error.');
-                        break;
-                    default:
-                        this.log('error', `Unexpected error importing run: ${post_res.statusCode}`);
-                }
-            }).bind(this));
-        }).bind(this));
-        // Perform the request
-        post_req.write(post_data);
-        post_req.end();
-    },
+    command(command, req, res, start = false, success = null){
 
-    /**
-     * Makes a call to the upload_run_dungeon API to register a TOA run.
-     *
-     * @param req The full request data.
-     * @param req The full response data.
-     */
-    uploadRunTOA(req, res){
-        this.log('info', 'Uploading TOA run to SWDB...')
+        // Override success message if empty
+        if (success == null || success == ""){
+            sucess = `Command ${command} succesfully executed!`
+        }
+        this.log('debug', `Starting ${command}...`);
         // Generate POST data
-        var post_data = querystring.stringify({
-            'request': JSON.stringify(req),
-            'response': JSON.stringify(res),
-            'key' : config.Config.Plugins[this.pluginName].apiKey
-        });
+        var post_data;
+        if (start == false){
+            post_data = querystring.stringify({
+                'request' : JSON.stringify(req),
+                'response' : JSON.stringify(res),
+                'key' : config.Config.Plugins[this.pluginName].apiKey
+            });
+        }
+        else{
+            post_data = querystring.stringify({
+                'request' : JSON.stringify(req),
+                'response' : JSON.stringify(res),
+                'start_request' : JSON.stringify(this.eventStartReq),
+                'start_response' : JSON.stringify(this.eventStartRes),
+                'key' : config.Config.Plugins[this.pluginName].apiKey
+            });
+        }
         // Configure POST requets
         var post_options = {
             host: config.Config.Plugins[this.pluginName].host,
             port: config.Config.Plugins[this.pluginName].port,
-            path: '/API/v2/upload_run_toa',
+            path: '/API/v2/' + command,
             method: 'POST',
             headers: {
                 'Content-Type': 'application/x-www-form-urlencoded',
@@ -310,22 +180,20 @@ module.exports = {
             post_res.on('data', (function (body) {
                 switch (post_res.statusCode){
                     case 200:
-                        this.log('success', 'TOA run successfully uploaded.');
-                        break;
                     case 201:
-                        this.log('success', 'TOA run successfully uploaded.');
+                        this.log('success', `${success} [${post_res.statusCode}]`);
                         break;
                     case 400:
-                        this.log('error', `There were errors importing the run: ${JSON.stringify(body)}`);
+                        this.log('error', `[HTTP/1.1 400] There were errors importing the data: ${JSON.stringify(body)}`);
                         break;
                     case 404:
-                        this.log('error', 'URL not found.');
+                        this.log('error', '[HTTP/1.1 404] URL not found.');
                         break;
                     case 500:
-                        this.log('error', 'The server returned an error.');
+                        this.log('error', '[HTTP/1.1 500] The server returned an error.');
                         break;
                     default:
-                        this.log('error', `Unexpected error importing run: ${post_res.statusCode}`);
+                        this.log('error', `[HTTP/1.1 ${post_res.statusCode}] Unexpected error.`);
                 }
             }).bind(this));
         }).bind(this));
@@ -334,50 +202,6 @@ module.exports = {
         post_req.end();
     },
 
-    /**
-     * Makes a call to the upload_run_scenario API to register a scenario run.
-     *
-     * @param req The full request data.
-     * @param req The full response data.
-     */
-    uploadRunScenario(req, res){
-        this.log('info', 'Uploading scenario run to SWDB...')
-        this.request.post(
-            'upload_run_scenario',
-            {json: {
-                key: config.Config.Plugins[this.pluginName].apiKey,
-                request: req,
-                response: res
-            } },
-            //req_options,
-            (error, response, body) => {
-                if (error) {
-                    this.log('error', `Error uplading scenario run: ${error.message}`);
-                    return;
-                }
-                switch (response.statusCode){
-                    case 200:
-                        this.log('success', 'Scenario run successfully uploaded.');
-                        break;
-                    case 201:
-                        this.log('success', 'Scenario run successfully uploaded.');
-                        break;
-                    case 400:
-                        this.log('error', `There were errors importing the data: ${JSON.stringify(body)}`);
-                        break;
-                    case 404:
-                        this.log('error', 'URL not found.');
-                        break;
-                    case 500:
-                        this.log('error', 'The server returned an error.');
-                        break;
-                    default:
-                        this.log('error', `Unexpected error importing profile: ${response.statusCode}`);
-                }
-            }
-        );
-    },
-
     /**
      * Prints a message to the SWEX output and to the console.
      * 
@@ -385,11 +209,11 @@ module.exports = {
      * @param msg The message to log.
      */
     log(type, msg){
-        //if (type != "debug" || this.config.Config.Plugins[this.pluginName].debug == true){
+        if (type != "debug" || this.config.Config.Plugins[this.pluginName].debug == true){
             this.proxy.log({ type: type, source: 'plugin', name: this.pluginName, message: msg });
             pad_type = type.substring(0, 5).padEnd(5, ' ');
             pad_plugin = this.pluginName.substring(0, 4).padEnd(4, ' ');
             console.log(`[${pad_plugin}][${pad_type}] ${msg}`);
-        //}
+        }
     }
 };