Procházet zdrojové kódy

Merge branch 'APIv3'

Iñigo Valentin před 5 roky
rodič
revize
82ccf284b4

+ 98 - 0
application/API/v3/API_Controller.php

@@ -0,0 +1,98 @@
+<?php
+
+    /**
+     * v3 API Controller file.
+     *
+     * Provides a class to handle all posible API requests.
+     *
+     * @category Constroller
+     */
+
+    /**
+     * v3 API controller.
+     *
+     * Handles every API request.
+     *
+     * @category Controller
+     */
+    class API_Controller {
+
+        /**
+         * Constructor.
+         *
+         * Handles every request, creating the required models and selecting the
+         * view.
+         *
+         * @param mixed[] $query GET parameters of the request.
+         */
+        public function __construct($query){
+
+            // Remove host
+            array_shift($query);
+            // Remove /API/
+            array_shift($query);
+            // Remove /v3/
+            array_shift($query);
+            // API call: Use API controller
+            $method = $_SERVER["REQUEST_METHOD"];
+            if (!in_array($method, ["GET", "POST", "PUT", "DELETE"])){
+                $method = "GET";
+            }
+            if (count($query) == 0 || $query[0] == null || $query[0] == ""){
+                require_once(__DIR__ . "/main.php");
+            }
+            else{
+                $command = strtolower($query[0]);
+                // Remove /<command>/
+                array_shift($query);
+                switch ($command){
+                    case "help":
+                        require_once(__DIR__ . "/help/index.php");
+                        break;
+                    case "profile":
+                    case "run":
+                    case "logbook":
+                    case "collection":
+                    case "run":
+                    case "units":
+                        if (file_exists(__DIR__ . "/$command/$method.php")){
+                            require_once(__DIR__ . "/$command/$method.php");
+                        }
+                        else{
+                            header("HTTP/1.1 404");
+                        }
+                        break;
+                    /*case "update_profile":
+                        require_once(__DIR__ . "/update_profile.php");
+                        break;
+                    case "update_logbook":
+                        require_once(__DIR__ . "/update_logbook.php");
+                        break;
+                    case "update_collection":
+                        require_once(__DIR__ . "/update_collection.php");
+                        break;
+                    case "log_run_dungeon":
+                        require_once(__DIR__ . "/log_run_dungeon.php");
+                        break;
+                    case "log_run_dh":
+                        require_once(__DIR__ . "/log_run_dh.php");
+                        break;
+                    case "log_run_scenario":
+                        require_once(__DIR__ . "/log_run_scenario.php");
+                        break;
+                    case "log_run_toa":
+                        require_once(__DIR__ . "/log_run_toa.php");
+                        break;
+                    case "log_run_rift_dungeon":
+                        require_once(__DIR__ . "/log_run_rift_dungeon.php");
+                        break;
+                    case "units":
+                        require_once(__DIR__ . "/units.php");
+                        break;*/
+                    default:
+                        header("HTTP/1.1 404");
+                }
+            }
+        }
+    }
+?>

+ 422 - 0
application/API/v3/help/api.php

@@ -0,0 +1,422 @@
+<?php
+
+    $API = [
+        "version" => 2,
+        "category" => [
+            [ // BEGIN CATEGORY Units
+                "name" => "Units",
+                "command" => [
+                    [ // BEGIN COMMAND Units
+                        "name" => "units",
+                        "method" => [
+                            [
+                                "id" => "GET",
+                                "url" => "/units/",
+                                "description" => "Retrieves units.",
+                                "parameters" => [
+                                    [
+                                        "key" => "page",
+                                        "via" => "request",
+                                        "type" => "int",
+                                        "description" => "Page to fetch. Deaults to 1.",
+                                        "optional" => true
+                                    ],
+                                    [
+                                        "key" => "per_page",
+                                        "via" => "request",
+                                        "type" => "int",
+                                        "description" => "number of results per page. Defaults to 30, maximum is 100.",
+                                        "optional" => true
+                                    ],
+                                    [
+                                        "key" => "name",
+                                        "via" => "request",
+                                        "type" => "string",
+                                        "description" => "Filter units with this in their name. Case insensitive.",
+                                        "optional" => true
+                                    ]
+                                ],
+                                "responses" => [
+                                    [
+                                        "status" => "200",
+                                        "message" => "Success.",
+                                        "description" => null,
+                                        "example" => null,
+                                    ],
+                                ]
+                            ],
+                            [
+                                "id" => "GET",
+                                "url" => "/units/{id}/",
+                                "description" => "Retrieves a unit info.",
+                                "parameters" => [
+                                    [
+                                        "key" => "{id}",
+                                        "via" => "query",
+                                        "type" => "string",
+                                        "description" => "Unit ID.",
+                                        "optional" => false
+                                    ],
+                                ],
+                                "responses" => [
+                                    [
+                                        "status" => "200",
+                                        "message" => "Success.",
+                                        "description" => null,
+                                        "example" => null,
+                                    ],
+                                    [
+                                        "status" => "404",
+                                        "message" => "Unit not found.",
+                                        "description" => "The requested unit does not exist.",
+                                        "example" => null
+                                    ]
+                                ]
+                            ],
+                        ],
+                    ] // END COMMAND profile
+                ]
+            ], // END CATEGORY Units
+            [ // BEGIN CATEGORY Profile
+                "name" => "Profile",
+                "command" => [
+                    [ // BEGIN COMMAND UploadProfile
+                        "name" => "profile",
+                        "method" => [
+                            [
+                                "id" => "GET",
+                                "url" => "/profile/{id}/",
+                                "description" => "Retrieves player profile.",
+                                "parameters" => [
+                                    [
+                                        "key" => "{id}",
+                                        "via" => "query",
+                                        "type" => "string",
+                                        "description" => "Player ID or name.",
+                                        "optional" => false
+                                    ],
+                                    [
+                                        "key" => "key",
+                                        "via" => "body",
+                                        "type" => "string",
+                                        "description" => "API key. Required for private profiles.",
+                                        "optional" => true
+                                    ]
+                                ],
+                                "responses" => [
+                                    [
+                                        "status" => "200",
+                                        "message" => "Success.",
+                                        "description" => null,
+                                        "example" => json_encode('{"username":"IValentin","level":50,"public":"1"}', JSON_PRETTY_PRINT),
+                                    ],
+                                    [
+                                        "status" => "403",
+                                        "message" => "A list of profiles cant be retrieved. Please specify a profile ID or player name.",
+                                        "description" => "No player ID or name has been supplied. A full list of profiles is not available.",
+                                        "example" => null
+                                    ],
+                                    [
+                                        "status" => "403",
+                                        "message" => "The profile is not public.",
+                                        "description" => "The requestd player profile is private, and the API key has not been sent or it's invalid.",
+                                        "example" => null
+                                    ],
+                                    [
+                                        "status" => "404",
+                                        "message" => "Player not found.",
+                                        "description" => "The requestd player is not registered in SWDB.",
+                                        "example" => null
+                                    ]
+                                ]
+                            ],
+                            [
+                                "id" => "POST",
+                                "url" => "/profile/{id}/",
+                                "description" => "Creates a new player profile.",
+                                "parameters" => [
+                                    [
+                                        "key" => "{id}",
+                                        "via" => "query",
+                                        "type" => "string",
+                                        "description" => "Player ID.",
+                                        "optional" => false
+                                    ],
+                                    [
+                                        "key" => "email",
+                                        "via" => "body",
+                                        "type" => "string",
+                                        "description" => "User email. Must be a valid address.",
+                                        "optional" => false
+                                    ],
+                                    [
+                                        "key" => "password",
+                                        "via" => "body",
+                                        "type" => "string",
+                                        "description" => "New user password.",
+                                        "optional" => false
+                                    ],
+                                    [
+                                        "key" => "name",
+                                        "via" => "body",
+                                        "type" => "string",
+                                        "description" => "New username. If not supplied, it will default to 'player_[id]'. Will be overriden on the next PUT request.",
+                                        "optional" => true
+                                    ],
+                                    [
+                                        "key" => "public",
+                                        "via" => "body",
+                                        "type" => "int",
+                                        "description" => "1 to make the profile public, 0 to keep it private. Default to 0.",
+                                        "optional" => true
+                                    ],
+                                ],
+                                "responses" => [
+                                    [
+                                        "status" => "201",
+                                        "message" => "Created.",
+                                        "description" => null,
+                                        "example" => json_encode('{"username":"IValentin","email":example@example.com,"api_key":"0123456789012345","public":"0"}', JSON_PRETTY_PRINT),
+                                    ],
+                                    [
+                                        "status" => "400",
+                                        "message" => "User ID not received.",
+                                        "description" => "The ID of the player to create is not in the query.",
+                                        "example" => null
+                                    ],
+                                    [
+                                        "status" => "400",
+                                        "message" => "User email not received.",
+                                        "description" => "The email of the player to create has not ben passed in the request.",
+                                        "example" => null
+                                    ],
+                                    [
+                                        "status" => "400",
+                                        "message" => "Invalid email.",
+                                        "description" => "The 'mail' parameter must be a valid email address.",
+                                        "example" => null
+                                    ],
+                                    [
+                                        "status" => "400",
+                                        "message" => "User password not received.",
+                                        "description" => "The password of the player to create has not ben passed in the request.",
+                                        "example" => null
+                                    ],
+                                    [
+                                        "status" => "400",
+                                        "message" => "Existing user.",
+                                        "description" => "A player with the same ID is already registered in SWDB.",
+                                        "example" => null
+                                    ]
+                                ]
+                            ],
+                            [
+                                "id" => "PUT",
+                                "url" => "/profile/{id}/",
+                                "description" => "Updates a player profile with data received from the game.",
+                                "parameters" => [
+                                    [
+                                        "key" => "{id}",
+                                        "via" => "query",
+                                        "type" => "string",
+                                        "description" => "Player ID.",
+                                        "optional" => false
+                                    ],
+                                    [
+                                        "key" => "key",
+                                        "via" => "body",
+                                        "type" => "string",
+                                        "description" => "API key.",
+                                        "optional" => false
+                                    ],
+                                    [
+                                        "key" => "response",
+                                        "via" => "body",
+                                        "type" => "string",
+                                        "description" => "JSON received from game server on HubUserLogin command.",
+                                        "optional" => false
+                                    ]
+                                ],
+                                "responses" => [
+                                    [
+                                        "status" => "204",
+                                        "message" => "Profile imported.",
+                                        "description" => null,
+                                        "example" => null,
+                                    ],
+                                    [
+                                        "status" => "400",
+                                        "message" => "API key not received.",
+                                        "description" => "No API key was suplied in the request.",
+                                        "example" => null
+                                    ],
+                                    [
+                                        "status" => "400",
+                                        "message" => "No data received.",
+                                        "description" => "Game JSON response not received from request",
+                                        "example" => null
+                                    ],
+                                    [
+                                        "status" => "400",
+                                        "message" => "Data is no valid JSON.",
+                                        "description" => "The parameter 'response' contains information, but it's not valid JSON data.",
+                                        "example" => null
+                                    ],
+                                    [
+                                        "status" => "401",
+                                        "message" => "Invalid credentials",
+                                        "description" => "The API key is not valid for the profile being updated.",
+                                        "example" => null
+                                    ]
+                                ]
+                            ],
+                            [
+                                "id" => "DELETE",
+                                "url" => "/profile/{id}/",
+                                "description" => "Deletes a player profile.",
+                                "parameters" => [
+                                    [
+                                        "key" => "{id}",
+                                        "via" => "query",
+                                        "type" => "string",
+                                        "description" => "Player ID.",
+                                        "optional" => false
+                                    ],
+                                    [
+                                        "key" => "key",
+                                        "via" => "body",
+                                        "type" => "string",
+                                        "description" => "API key.",
+                                        "optional" => false
+                                    ]
+                                ],
+                                "responses" => [
+                                    [
+                                        "status" => "204",
+                                        "message" => "Profile deleted.",
+                                        "description" => null,
+                                        "example" => null,
+                                    ],
+                                    [
+                                        "status" => "400",
+                                        "message" => "User ID not received.",
+                                        "description" => "The ID of the player to create is not in the query.",
+                                        "example" => null
+                                    ],
+                                    [
+                                        "status" => "400",
+                                        "message" => "API key not received.",
+                                        "description" => "No API key was suplied in the request.",
+                                        "example" => null
+                                    ],
+                                    [
+                                        "status" => "401",
+                                        "message" => "Invalid credentials",
+                                        "description" => "The API key is not valid for the profile being updated.",
+                                        "example" => null
+                                    ]
+                                ]
+                            ],
+                        ],
+                    ] // END COMMAND profile
+                ]
+            ], // END CATEGORY Profile
+            [ // BEGIN CATEGORY Run
+                "name" => "Run",
+                "command" => [
+                    [ // BEGIN COMMAND run
+                        "name" => "run",
+                        "method" => [
+                            [
+                                "id" => "POST",
+                                "url" => "/run/",
+                                "description" => "Logs a game run using JSON files intercepted from the game.",
+                                "parameters" => [
+                                    [
+                                        "key" => "key",
+                                        "via" => "body",
+                                        "type" => "string",
+                                        "description" => "API key.",
+                                        "optional" => false
+                                    ],
+                                    [
+                                        "key" => "result_response",
+                                        "via" => "body",
+                                        "type" => "string",
+                                        "description" => "JSON received from game server on run result.",
+                                        "optional" => false
+                                    ],
+                                    [
+                                        "key" => "result_request",
+                                        "via" => "body",
+                                        "type" => "string",
+                                        "description" => "JSON sent to game server on run result. Required for commands: BattleDungeonResult_V2, BattleDimensionHoleDungeonResult_v2",
+                                        "optional" => true
+                                    ],
+                                    [
+                                        "key" => "start_response",
+                                        "via" => "body",
+                                        "type" => "string",
+                                        "description" => "JSON received from game server on run start. Required for commands: BattleRiftOfWorldsRaidResult, BattleScenarioResult",
+                                        "optional" => true
+                                    ],
+                                    [
+                                        "key" => "start_request",
+                                        "via" => "body",
+                                        "type" => "string",
+                                        "description" => "JSON sent to game server on run start. Required for commands: ",
+                                        "optional" => true
+                                    ],
+                                ],
+                                "responses" => [
+                                    [
+                                        "status" => "201",
+                                        "message" => "Created.",
+                                        "description" => null,
+                                        "example" => null,
+                                    ],
+                                    [
+                                        "status" => "400",
+                                        "message" => "API key not received.",
+                                        "description" => "No API key was suplied in the request.",
+                                        "example" => null
+                                    ],
+                                    [
+                                        "status" => "400",
+                                        "message" => "No result response data received.",
+                                        "description" => "JSON received from game server on run result wast receiven on the request.",
+                                        "example" => null
+                                    ],
+                                    [
+                                        "status" => "400",
+                                        "message" => "Result response data is no valid JSON.",
+                                        "description" => "The parameter 'result_response' contains information, but it's not valid JSON data.",
+                                        "example" => null
+                                    ],
+                                    [
+                                        "status" => "401",
+                                        "message" => "Invalid credentials",
+                                        "description" => "The API key is not valid for the profile being updated.",
+                                        "example" => null
+                                    ],
+                                    [
+                                        "status" => "405",
+                                        "message" => "Command not implemented.",
+                                        "description" => "The command referenced in the JSON file is not implemented.",
+                                        "example" => null
+                                    ],
+                                    [
+                                        "status" => "409",
+                                        "message" => "Run already in database.",
+                                        "description" => "The run is already in the database for the user.",
+                                        "example" => null
+                                    ]
+                                ]
+                            ],
+                        ],
+                    ] // END COMMAND Run
+                ]
+            ], // END CATEGORY Run
+        ],
+    ];
+?>

+ 487 - 0
application/API/v3/help/index.php

@@ -0,0 +1,487 @@
+<?php
+    /**
+     * API Help view.
+     *
+     * Shows API documentation.
+     *
+     * @category View
+     */
+?>
+<?php
+    include __DIR__ . "/api.php";
+?>
+<!DOCTYPE html>
+<html lang='en'>
+    <head>
+        <meta content='text/html; charset=utf-8' http-equiv='content-type'/>
+        <meta name='viewport' content='width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1'/>
+        <title>SWDB APIv3</title>
+        <link rel='shortcut icon' href='<?=URL::IMG["LOGO"]?>sw.png'/>
+        <!-- CSS -->
+        <link rel='stylesheet' type='text/css' href='<?=URL::CSS?>ui.css'/>
+        <style>
+
+            aside hr{
+                height: 0em;
+                padding: 0;
+                margin: 0 1em;
+                border: 0.01em solid #db490054;
+                border-radius: 1em;
+            }
+
+            aside h3 {
+                margin: 0.4em 0 0.2em 1em;
+                border-left: 0.5em solid #db490054;
+                border-radius: 0.2em;
+                padding-left: 0.3em;
+                background-color: #00000000;
+                transition: all .3s ease-in-out;
+            }
+
+            aside h3:hover{
+                background-color: #db490054;
+            }
+
+            aside h3 a{
+                color: #cccccc
+            }
+
+            aside h3:hover a{
+                color: #ffffff
+            }
+
+            aside ul {
+                margin: 0.2em 0;
+                list-style: none;
+            }
+
+            aside ul li{
+                border-left: 0.5em solid #db490054;
+                border-radius: 0.2em;
+                padding-left: 0.3em;
+                margin-bottom: 0.2em;
+                background-color: #00000000;
+                transition: all .3s ease-in-out;
+            }
+
+            aside ul li:hover{
+                background-color: #db490054;
+            }
+
+            aside ul li a{
+                color: #cccccc
+            }
+
+            aside ul li:hover a{
+                color: #ffffff
+            }
+
+            aside div#copyright{
+                text-align: center;
+                font-size: 70%;
+                position: absolute;
+                bottom: 1em;
+            }
+
+            section.category article{
+                margin: 1.5em;
+                padding-left: 1em;
+                padding-right: 1em;
+                text-align: left;
+            }
+
+            section.category article h3{
+                vertical-align: bottom;
+            }
+
+            section.category article h3 span.description{
+                font-style: italic;
+                font-size: 90%;
+            }
+
+            section.category article:not(:last-child){
+                padding-bottom: 1em;
+                margin-bottom: 1em;
+                border-bottom: 0.1em solid #ffffff66;
+            }
+
+            section.category article div.command span{
+                display: inline-block;
+                border-radius: 0.6em;
+                padding: 0.3em 0.5em;
+                border-width: 0.15em;
+                border-style: solid;
+                color: #ffffff;
+                font-family: monospace, monospace;
+                font-weight: bold;
+            }
+
+            section.category article div.command span.url{
+                background-color: #292b36;
+                border-color: #4b4d57;
+            }
+
+            section.category article div.command span.type{
+                text-transform: uppercase;
+            }
+
+            section.category article div.command span.type_GET {
+                background-color: #008000;
+                border-color: #22a222;
+            }
+
+            section.category article div.command span.type_PUT {
+                background-color: #e5c500;
+                border-color: #f7e722;
+            }
+
+            section.category article div.command span.type_POST {
+                background-color: #4070ec;
+                border-color: #6292fe;
+            }
+
+            section.category article div.command span.type_DELETE {
+                background-color: #ed0039;
+                border-color: #ff225b;
+            }
+
+            section.category article table{
+                border-collapse: collapse;
+                width: 80%;
+                border-radius: 0.5em;
+                margin: 0.5em auto 1em auto;
+            }
+
+            section.category article table th {
+                background-color: #331100;
+                text-align: left;
+                font-weight: bold;
+                padding: 0.5em 1em;
+                border: 0.1px solid #e0e0e0;
+            }
+
+            section.category article table td {
+                background-color: #352413;
+                vertical-align: top;
+                padding: 0.2em 0.4em;
+                border: #e0e0e0 1px solid;
+            }
+
+            section.category article table td.name {
+                font-family: monospace, monospace;
+                font-weight: bold;
+            }
+
+            section.category article table td.name span.location{
+                padding-left: 1em;
+                font-size: 80%;
+                font-style: italic;
+                color: #ffffff77;
+            }
+
+            section.category article table td.type {
+                font-family: monospace, monospace;
+                font-weight: bold;
+            }
+
+            section.category article table td.status span{
+                display: inline-block;
+                border-radius: 0.6em;
+                padding: 0.3em 0.5em;
+                border-width: 0.15em;
+                border-style: solid;
+                color: #ffffff;
+                font-family: monospace, monospace;
+                font-weight: bold;
+            }
+
+            section.category article table td.status_1 span{
+                border-color: #999999;
+                background-color: #777777;
+            }
+            section.category article table td.status_2 span{
+                background-color: #008000;
+                border-color: #22a222;
+            }
+            section.category article table td.status_3 span{
+                background-color: #4070ec;
+                border-color: #6292fe;
+            }
+            section.category article table td.status_4 span{
+                background-color: #e5c500;
+                border-color: #f7e722;
+            }
+            section.category article table td.status_5 span{
+                background-color: #ed0039;
+                border-color: #ff225b;
+            }
+
+            section.category article table td.message{
+                font-family: monospace, monospace;
+            }
+
+            section.category article table td.description span.optional {
+                font-size: 90%;
+                color: #bbbbbb;
+                font-style: italic;
+            }
+
+            section#example{
+                position: fixed;
+                display: none;
+                top: 15%;
+                left: 20%;
+                width: 60%;
+                max-width: 90em;
+                height: 70%;
+                max-height: 30em;
+                text-align: right;
+            }
+
+            section#example article{
+                text-align: right;
+            }
+
+            section#example article pre#example_content{
+                text-align: left;
+                background-color: #292b36;
+                border: 0.3em solid #4b4d57;
+                border-radius: 0.8em;
+                padding: 1em;
+                max-height: 20em;
+                overflow: scroll;
+            }
+
+        </style>
+        <!-- Javascript -->
+        <script>
+            function showExample(command, status, example){
+                document.getElementById('example_title').innerHTML = command + " HTTP status " + status + " response example";
+                document.getElementById('example_content').innerHTML = JSON.stringify(JSON.parse(example), null, 4);
+                document.getElementById('example').style.display = 'block';
+            }
+            function closeExample(command, status, example){
+                document.getElementById('example').style.display = 'none';
+            }
+        </script>
+        <!-- Meta tags -->
+        <link rel='canonical' href='<?=URL::BASE?>API/v3/help/'/>
+        <link rel='author' href='SWDB'/>
+        <link rel='publisher' href='SWDB'/>
+        <meta name='description' content='SWDB API v3 Documentation'/>
+        <meta property='og:title' content='SWDB APIv3'/>
+        <meta property='og:url' content='<?=URL::BASE?>API/v3/help/'/>
+        <meta property='og:description' content='SWDB API v3 Documentation'/>
+        <meta property='og:image' content='<?=URL::IMG["LOGO"]?>sw.png'/>
+        <meta property='og:site_name' content='SWDB'/>
+        <meta property='og:type' content='website'/>
+        <meta property='og:locale' content='en'/>
+        <meta name='twitter:card' content='summary'/>
+        <meta name='twitter:title' content='SWDB APIv3'/>
+        <meta name='twitter:description' content='SWDB API v3 Documentation'/>
+        <meta name='twitter:image' content='<?=URL::IMG["LOGO"]?>sw.png'/>
+        <meta name='twitter:url' content='<?=URL::BASE?>API/v3/help/'/>
+        <meta name='robots' content='index follow'/>
+    </head>
+    <body>
+        <header>
+            <div id='logo'>
+                <img id='logo_img' src='<?=URL::IMG["LOGO"]?>sw.png' alt=''/>
+                <h1 id='logo_text'>
+                    <span class='large'>D</span>
+                    <span class='small'>ata</span>
+                    <span class='large'>B</span>
+                    <span class='small'>ase</span>
+                </h1>
+            </div>
+        </header>
+        <aside>
+            <h2>
+                API v3 endopoints:
+            </h2>
+<?php
+            foreach ($API["category"] as $category){
+?>
+                <h3>
+                    <a href='#category_<?=$category["name"]?>'>
+                        <?=$category["name"]?>
+                    </a>
+                </h3>
+                <ul>
+<?php
+                    foreach ($category["command"] as $command){
+                        foreach ($command["method"] as $method){
+?>
+                            <li>
+                                <a href='#command_<?=$command["name"]?>-<?=$method["id"]?>-<?=htmlspecialchars($method["url"])?>' alt='<?=$command["description"]?>'>
+                                    <?=$method["id"]?> <?=htmlspecialchars($method["url"])?>
+                                </a>
+                            </li>
+<?php
+                        }
+                    }
+?>
+                </ul>
+                <hr/>
+<?php
+            }
+?>
+        </aside>
+        <main>
+<?php
+            foreach ($API["category"] as $category){
+                foreach ($category["command"] as $command){
+?>
+                    <section id='category_<?=$category["name"]?>' class='category content'>
+                    <h2>
+                        <?=$category["name"]?>
+                    </h2>
+                        <article id='command_<?=$command["name"]?>'>
+                            <h3>
+                                <?=$command["name"]?>
+                            </h3>
+<?php
+                            foreach ($command["method"] as $method){
+?>
+                                <div class='command' id='command_<?=$command["name"]?>-<?=$method["id"]?>-<?=htmlspecialchars($method["url"])?>'>
+                                    <span class='type type_<?=$method["id"]?>'>
+                                        <?=$method["id"]?>
+                                    </span>
+                                    <span class='url'>
+                                        <?=htmlspecialchars($method["url"])?>
+                                    </span>
+                                    <span class='description'>
+                                        <?=$method["description"]?>
+                                    </span>
+                                </div>
+                                <table class='params'>
+                                    <thead>
+                                        <tr>
+                                            <th class='header' colspan='3'>
+                                                Parameters
+                                            </th>
+                                        </tr>
+                                        <tr>
+                                            <th class='name'>
+                                                Parameter
+                                            </th>
+                                            <th class='type'>
+                                                Type
+                                            </th>
+                                            <th class='description'>
+                                                Description
+                                            </th>
+                                        </tr>
+                                    </thead>
+                                    <tbody>
+    <?php
+                                        foreach ($method["parameters"] as $param){
+    ?>
+                                            <tr>
+                                                <td class='name'>
+                                                    <?=$param["key"]?>
+                                                    <span class='location'>
+                                                        (<?=$param["via"]?>)
+                                                    </span>
+                                                </td>
+                                                <td class='type'>
+                                                    <?=$param["type"]?>
+                                                </td>
+                                                <td class='description'>
+                                                    <?=($param["optional"] ? "<span class='optional'>[Optional] </span>" : "")?>
+                                                    <?=$param["description"]?>
+                                                </td>
+                                            </tr>
+    <?php
+                                        }
+    ?>
+                                    </tbody>
+                                </table>
+                                <table class='responses'>
+                                    <thead>
+                                        <tr>
+                                            <th class='header' colspan='3'>
+                                                Responses
+                                            </th>
+                                        </tr>
+                                        <tr>
+                                            <th class='status'>
+                                                Status
+                                            </th>
+                                            <th class='status'>
+                                                Message
+                                            </th>
+                                            <th class='description'>
+                                                Description
+                                            </th>
+                                        </tr>
+                                    </thead>
+                                    <tbody>
+    <?php
+                                        foreach ($method["responses"] as $response){
+    ?>
+                                            <tr>
+                                                <td class='status status_<?=substr($response["status"], 0, 1)?>'>
+                                                    <span>
+                                                        <?=$response["status"]?>
+                                                    </span>
+                                                </td>
+                                                <td class='message'>
+                                                    <?=$response["message"]?>
+                                                </td>
+                                                <td class='description'>
+                                                    <?=$response["description"]?>
+    <?php
+                                                    if ($response["example"] != null){
+    ?>
+                                                        <span class='fake_a' onclick="showExample('<?=$command["name"]?>', <?=$status["status"]?>, '<?=htmlspecialchars(json_decode($status["example"]), ENT_QUOTES)?>')">
+                                                            View example.
+                                                        </span>
+    <?php
+                                                    }
+    ?>
+                                                </td>
+                                            </tr>
+    <?php
+                                        }
+    ?>
+                                    </tbody>
+                                </table>
+<?php
+                            }
+?>
+                        </article>
+                    </section>
+<?php
+                }
+?>
+                <section id='example' class='content'>
+                    <h2 id='example_title'>
+                    </h2>
+                    <article>
+                        <pre id='example_content'>
+                        </pre>
+                        <input type='button' value='Close' onClick='closeExample();'>
+                    </article>
+                </section>
+<?php
+            }
+?>
+        </main>
+        <footer>
+            <table id='footer_table'>
+                <tr>
+                    <td id='footer_left'>
+                        Version <?=APP::VERSION?>. Developed by <a href='https://inigovalentin.com'>I&ntilde;igo Valentin</a>.
+                        <br/>
+                        <br/>
+                        Source code available on <a href='https://github.com'>Github</a> under the GPLv3.
+                    </td>
+                    <td id='footer_right'>
+                        Neither I nor this tool are affiliated with or endorsed by <a href='http://com2us.com/'>Com2uS</a> in any way. The Summoners >
+                    </td>
+                </tr>
+            </table>
+        </footer>
+    </body>
+</html>

+ 17 - 0
application/API/v3/main.php

@@ -0,0 +1,17 @@
+<?php
+    header("Content-Type:application/json");
+    $links = [
+        "Archetypes" => URL::BASE . "/API/v2/archetypes",
+        "Area types" => URL::BASE . "/API/v2/area_types",
+        "Areas types" => URL::BASE . "/API/v2/areas",
+        "Buildings" => URL::BASE . "/API/v2/buildings",
+        "Decorations" => URL::BASE . "/API/v2/decorations",
+        "Areas types" => URL::BASE . "/API/v2/areas",
+        "Units" => URL::BASE . "/API/v2/units",
+        "Skills" => URL::BASE . "/API/v2/skills",
+        "Skill effects" => URL::BASE . "/API/v2/skill_efects",
+    ];
+    header("HTTP/1.1 200");
+    $json_response = json_encode($links);
+    echo $json_response;
+?>

+ 76 - 0
application/API/v3/profile/DELETE.php

@@ -0,0 +1,76 @@
+<?php
+    /**
+     * Profile deleter script.
+     *
+     * Exposes an API to update an existing profile.
+     *
+     * It also saves the data to a JSON file in the data directory.
+     *
+     * Mandatory query parameters are:
+     *  - id: Player ID.
+     *
+     * Mandatory DELETE parameters are:
+     *  - key: User API key.
+     *
+     * @category API
+     */
+
+    global $db;
+
+    try{
+
+        header("Content-type: application/json; charset=utf-8");
+
+        // Get put data
+        parse_str(file_get_contents("php://input"), $_DELETE);
+
+        // Get profile ID
+        // $query heredated from API_CONTROLLER
+        if (count($query) > 0){
+            $id = $query[0];
+        }
+        else{
+            // ID is mandatory
+            header("HTTP/1.1 400 User ID not received.");
+            syslog(LOG_INFO, "[APIv3] HTTP/1.1 400 User ID not received.");
+            return 400;
+        }
+
+        // Check API key.
+        $api_key = $_DELETE["key"];
+        if ($api_key == null || $api_key == false){
+            header("HTTP/1.1 400 API key not received.");
+            syslog(LOG_INFO, "[APIv3] HTTP/1.1 400 API key not received.");
+            return 400;
+        }
+
+        // Authenticate
+        $statement = $db->prepare("SELECT COUNT(uid) AS c FROM player WHERE uid = :uid AND api_key = :api_key;");
+        $statement->bindValue(":uid", $uid);
+        $statement->bindValue(":api_key", $api_key);
+        if (1 != $statement->execute()->fetchArray(SQLITE3_ASSOC)["c"]){
+            header("HTTP/1.1 401 Invalid credentials.");
+            syslog(LOG_INFO, "[APIv3] HTTP/1.1 401 Invalid credentials.");
+            return 401;
+        }
+
+        $statement = $db->prepare("
+          DELETE FROM player
+          WHERE
+            uid = :uid AND
+            api_key, = :api_key;
+        ");
+        $statement->bindValue(":uid", $uid);
+        $statement->bindValue(":api_key", $api_key);
+        $statement->execute();
+
+        header("HTTP/1.1 204 Profile deleted.");
+        syslog(LOG_INFO, "[APIv3] HTTP/1.1 204 Profile deleted.");
+        return 204;
+    }
+    catch(Exception $e) {
+        header("HTTP/1.1 500 Unexpeced error: " . $e->getMessage());
+        syslog(LOG_ERR, "[APIv3] HTTP/1.1 500 Unexpected error: " . $e->getMessage());
+        return 500;
+    }
+?>

+ 67 - 0
application/API/v3/profile/GET.php

@@ -0,0 +1,67 @@
+<?php
+    /**
+     * Profile getter script.
+     *
+     * Exposes an API to rerieve a user profile.
+     *
+     * A lst of profiles can't be retrieved.
+     *
+     * Mandatory query parameters are:
+     *  - id: Player ID.
+     *
+     * Optional GET parameters are.
+     *  - key: User API key to retrieve a private profile.
+     *
+     * @category API
+     */
+
+    global $db;
+
+    try{
+        header("Content-type: application/json; charset=utf-8");
+
+        // Get profile ID
+        // $query heredated from API_CONTROLLER
+        if (count($query) > 1){
+            $id = $query[0];
+        }
+        else{
+            // Id is mandatory, a list can be retrieved
+            header("HTTP/1.1 403 A list of profiles cant be retrieved. Please specify a profile ID or player name.");
+            syslog(LOG_INFO, "[APIv3] HTTP/1.1 403 A list of profiles cant be retrieved. Please specify a profile ID or player name.");
+            return 403;
+        }
+
+        // Get player
+        $statement = $db->prepare("SELECT * FROM player WHERE uid = :uid OR upper(name) = upper(:uid);");
+        $statement->bindValue(":uid", $id);
+        $r = $statement->execute()->fetchArray(SQLITE3_ASSOC);
+        if ($r == null){
+            // No player found
+            header("HTTP/1.1 404 Player not found.");
+            syslog(LOG_INFO, "[APIv3] HTTP/1.1 404 Player not found.");
+            return 403;
+        }
+        if ($r["public"] != 1 && $r["key"] != filter_input(INPUT_GET, "key")){
+            header("HTTP/1.1 403 The profile is not public.");
+            syslog(LOG_INFO, "[APIv3] HTTP/1.1 403 The profile is not public.");
+            return 403;
+        }
+
+        // Build array
+        $player = [
+            "username" => $r["name"],
+            "level" => $r["level"],
+            "public" => "1"
+        ];
+        echo(json_encode($player));
+        header("HTTP/1.1 200 Success.");
+        syslog(LOG_INFO, "[APIv3] HTTP/1.1 200 Success.");
+        return 200;
+    }
+    catch(Exception $e) {
+        header("HTTP/1.1 500 Unexpeced error: " . $e->getMessage());
+        syslog(LOG_ERR, "[APIv3] HTTP/1.1 500 Unexpected error: " . $e->getMessage());
+        return 500;
+    }
+?>

+ 211 - 0
application/API/v3/profile/POST.php

@@ -0,0 +1,211 @@
+<?php
+    /**
+     * Profile creator script.
+     *
+     * Exposes an API to create a new profile.
+     *
+     * Mandatory query parameters are:
+     *  - id: Player ID.
+     *
+     * Mandatory POST parameters are:
+     *  - email: User email.
+     *  - password: User password.
+     *
+     * Optional POST parameters are:
+     *  - name: User name.
+     *  - public: 1 To make the profile public, 0 for private.
+     *
+     * @category API
+     */
+
+    global $db;
+
+    try{
+
+        header("Content-type: application/json; charset=utf-8");
+
+        // Get put data
+        parse_str(file_get_contents("php://input"), $_POST);
+
+        // Get profile ID
+        // $query heredated from API_CONTROLLER
+        if (count($query) > 0){
+            $id = $query[0];
+        }
+        else{
+            // ID is mandatory
+            header("HTTP/1.1 400 User ID not received.");
+            syslog(LOG_INFO, "[APIv3] HTTP/1.1 400 User ID not received.");
+            return 400;
+        }
+
+        // Check user mail.
+        $mail = $_POST["email"];
+        if ($mail == null || $mail == false){
+            header("HTTP/1.1 400 User email not received.");
+            syslog(LOG_INFO, "[APIv3] HTTP/1.1 400 User email not received.");
+            return 400;
+        }
+        if (!filter_var($mail, FILTER_VALIDATE_EMAIL)) {
+            header("HTTP/1.1 400 Invalid email.");
+            syslog(LOG_INFO, "[APIv3] HTTP/1.1 400 Invalid email.");
+            return 400;
+        }
+        // Check user password.
+        $password = $_POST["password"];
+        if ($password == null || $password == false){
+            header("HTTP/1.1 400 User password not received.");
+            syslog(LOG_INFO, "[APIv3] HTTP/1.1 400 User password not received.");
+            return 400;
+        }
+        // Check user name.
+        $name = $_POST["name"];
+        if ($name == null || $name == false){
+            $name = "player_" . $uid;
+        }
+        // Check user name.
+        $public = intval($_POST["public"]);
+        if ($public != 1){
+            $public = 0;
+        }
+
+        // Check if player exists
+        $statement = $db->prepare("SELECT COUNT(uid) AS c FROM player WHERE uid = :uid;");
+        $statement->bindValue(":uid", $uid);
+        if (1 != $statement->execute()->fetchArray(SQLITE3_ASSOC)["c"]){
+            header("HTTP/1.1 400 Existing user.");
+            syslog(LOG_INFO, "[APIv3] HTTP/1.1 400 Existing user.");
+            return 400;
+        }
+
+        // Prepare inputs
+        $password = hash("sha256", $password);
+        $characters = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+        $charactersLength = 16;
+        $api_key = "";
+        for ($i = 0; $i < $length; $i++) {
+            $api_key .= $characters[rand(0, $charactersLength - 1)];
+        }
+
+        // Insert
+        $statement = $db->prepare("
+          INSERT INTO player (
+            uid,
+            name,
+            mail,
+            password,
+            api_key,
+            mana,
+            crystal,
+            country,
+            lang,
+            level,
+            experience,
+            energy,
+            energy_max,
+            energy_per_min,
+            arena_energy,
+            arena_energy_max,
+            rep,
+            social_point,
+            honor_point,
+            guild_point,
+            darkportal_energy,
+            darkportal_energy_max,
+            dimension_energy,
+            dimension_energy_max,
+            costume_point,
+            costume_point_max,
+            honor_medal,
+            honor_mark,
+            event_coin,
+            storage_slots,
+            island,
+            public
+          ) VALUES (
+            :uid,
+            :name,
+            :mail,
+            :password,
+            :api_key,
+            :mana,
+            :crystal,
+            :country,
+            :lang,
+            :level,
+            :experience,
+            :energy,
+            :energy_max,
+            :energy_per_min,
+            :arena_energy,
+            :arena_energy_max,
+            :rep,
+            :social_point,
+            :honor_point,
+            :guild_point,
+            :darkportal_energy,
+            :darkportal_energy_max,
+            :dimension_energy,
+            :dimension_energy_max,
+            :costume_point,
+            :costume_point_max,
+            :honor_medal,
+            :honor_mark,
+            :event_coin,
+            :storage_slots,
+            :island,
+            :public
+          );
+        ");
+        $statement->bindValue(":uid", $uid);
+        $statement->bindValue(":name", $name);
+        $statement->bindValue(":mail", $mail);
+        $statement->bindValue(":password", $password);
+        $statement->bindValue(":api_key", $api_key);
+        $statement->bindValue(":mana", 0);
+        $statement->bindValue(":crystal", 0);
+        $statement->bindValue(":country", null);
+        $statement->bindValue(":lang", null);
+        $statement->bindValue(":level", 1);
+        $statement->bindValue(":experience", 0);
+        $statement->bindValue(":energy", 0);
+        $statement->bindValue(":energy_max", 0);
+        $statement->bindValue(":energy_per_min", 0);
+        $statement->bindValue(":arena_energy", 0);
+        $statement->bindValue(":arena_energy_max", 10);
+        $statement->bindValue(":rep", null);
+        $statement->bindValue(":social_point", 0);
+        $statement->bindValue(":honor_point", 0);
+        $statement->bindValue(":guild_point", 0);
+        $statement->bindValue(":darkportal_energy", 0);
+        $statement->bindValue(":darkportal_energy_max", 10);
+        $statement->bindValue(":dimension_energy", 0);
+        $statement->bindValue(":dimension_energy_max", 100);
+        $statement->bindValue(":costume_point", 0);
+        $statement->bindValue(":costume_point_max", 0);
+        $statement->bindValue(":honor_medal", 0);
+        $statement->bindValue(":honor_mark", 0);
+        $statement->bindValue(":event_coin", 0);
+        $statement->bindValue(":storage_slots", 0);
+        $statement->bindValue(":island_upgrade", 0);
+        $statement->bindValue(":public", $public);
+        $statement->execute();
+
+        // Build array
+        $player = [
+            "username" => $name,
+            "email" => $mail,
+            "api_key" => $api_key,
+            "public" => "1"
+        ];
+        echo(json_encode($player));
+        header("HTTP/1.1 201 Created.");
+        syslog(LOG_INFO, "[APIv3] HTTP/1.1 201 Created.");
+        return 201;
+    }
+    catch(Exception $e) {
+        header("HTTP/1.1 500 Unexpeced error: " . $e->getMessage());
+        syslog(LOG_ERR, "[APIv3] HTTP/1.1 500 Unexpected error: " . $e->getMessage());
+        return 500;
+    }
+?>

+ 1662 - 0
application/API/v3/profile/PUT.php

@@ -0,0 +1,1662 @@
+<?php
+    /**
+     * Profile uploader script.
+     *
+     * Exposes an API to update an existing profile.
+     *
+     * It also saves the data to a JSON file in the data directory.
+     *
+     * Mandatory PUT parameters are:
+     *  - response: JSON received from game server on HubUserLogin command.
+     *  - key: User API key.
+     *
+     * @category API
+     */
+
+    global $db;
+
+    /**
+     * Calculates the currrent efficiency of a rune.
+     *
+     * @param JSON $rune JSON fragment of the rune.
+     * @return int Efficiency (0-100)
+     */
+    function calculate_efficiency($rune){
+        $efficiency = 0.0;
+        $max_efficiency = 0.0;
+        $substats = [];
+        $level = $rune->{"upgrade_curr"};
+        $stars = $rune->{"class"};
+        // Ancient stars are + 10
+        if ($stars > 10){
+            $stars -= 10;
+        }
+        $list = null;
+        // Main stat
+        $main_stat = $rune->{"pri_eff"}[0];
+        switch ($main_stat){
+            case RUNE_STAT_ID::HP:
+                $list = RUNE_STAT_VALUES::HP;
+                break;
+            case RUNE_STAT_ID::HP_P:
+                $list = RUNE_STAT_VALUES::HP_P;
+                break;
+            case RUNE_STAT_ID::ATK:
+                $list = RUNE_STAT_VALUES::ATK;
+                break;
+            case RUNE_STAT_ID::ATK_P:
+                $list = RUNE_STAT_VALUES::ATK_P;
+                break;
+            case RUNE_STAT_ID::DEF:
+                $list = RUNE_STAT_VALUES::DEF;
+                break;
+            case RUNE_STAT_ID::DEF_P:
+                $list = RUNE_STAT_VALUES::DEF_P;
+                break;
+            case RUNE_STAT_ID::SPD:
+                $list = RUNE_STAT_VALUES::SPD;
+                break;
+            case RUNE_STAT_ID::CRR:
+                $list = RUNE_STAT_VALUES::CRR;
+                break;
+            case RUNE_STAT_ID::CRD:
+                $list = RUNE_STAT_VALUES::CRD;
+                break;
+            case RUNE_STAT_ID::ACC:
+                $list = RUNE_STAT_VALUES::ACC;
+                break;
+            case RUNE_STAT_ID::RES:
+                $list = RUNE_STAT_VALUES::RES;
+                break;
+        }
+        $efficiency += floatval($list[$stars][$level]) / floatval($list[6][15]);
+        // Innate stat
+        if ($rune->{"prefix_eff"}[0] > 0){
+            $innate_stat = $rune->{"pri_eff"}[0];
+            $innate_stat_value = $rune->{"pri_eff"}[1];
+            $efficiency += floatval($innate_stat_value) / (5 * floatval(RUNE_SUBSTAT_MAX_VALUES::SUBSTAT[$innate_stat][6]));
+        }
+        // Substats
+        for ($i = 0; $i <= 3; $i ++){
+            if (sizeof($rune->{"sec_eff"}) > $i){
+                $substat = $rune->{"sec_eff"}[$i][0];
+                $substat_value = $rune->{"sec_eff"}[$i][1];
+                $efficiency += floatval($substat_value) / (5 * floatval(RUNE_SUBSTAT_MAX_VALUES::SUBSTAT[$substat][6]));
+            }
+        }
+        $efficiency = $efficiency / 2.8 * 100;
+        if ($efficiency > 100){
+            $efficiency = 100;
+        }
+        return $efficiency;
+    }
+
+    /**
+     * Calculates the maximum efficiency of a rune.
+     *
+     * @param JSON $rune JSON fragment of the rune.
+     * @return int Maximum efficiency (0-100)
+     */
+    function calculate_maximum_efficiency($rune){
+        $efficiency = 0.0;
+        $max_efficiency = 0.0;
+        $substats = [];
+        $stars = $rune->{"class"};
+        // Ancient stars are + 10
+        if ($stars > 10){
+            $stars -= 10;
+        }
+        $list = null;
+        // Main stat
+        $main_stat = $rune->{"pri_eff"}[0];
+        switch ($main_stat){
+            case RUNE_STAT_ID::HP:
+                $list = RUNE_STAT_VALUES::HP;
+                break;
+            case RUNE_STAT_ID::HP_P:
+                $list = RUNE_STAT_VALUES::HP_P;
+                break;
+            case RUNE_STAT_ID::ATK:
+                $list = RUNE_STAT_VALUES::ATK;
+                break;
+            case RUNE_STAT_ID::ATK_P:
+                $list = RUNE_STAT_VALUES::ATK_P;
+                break;
+            case RUNE_STAT_ID::DEF:
+                $list = RUNE_STAT_VALUES::DEF;
+                break;
+            case RUNE_STAT_ID::DEF_P:
+                $list = RUNE_STAT_VALUES::DEF_P;
+                break;
+            case RUNE_STAT_ID::SPD:
+                $list = RUNE_STAT_VALUES::SPD;
+                break;
+            case RUNE_STAT_ID::CRR:
+                $list = RUNE_STAT_VALUES::CRR;
+                break;
+            case RUNE_STAT_ID::CRD:
+                $list = RUNE_STAT_VALUES::CRD;
+                break;
+            case RUNE_STAT_ID::ACC:
+                $list = RUNE_STAT_VALUES::ACC;
+                break;
+            case RUNE_STAT_ID::RES:
+                $list = RUNE_STAT_VALUES::RES;
+                break;
+        }
+        $efficiency += floatval($list[$stars][15]) / floatval($list[6][15]);
+        // Innate stat
+        if ($rune->{"prefix_eff"}[0] > 0){
+            $innate_stat = $rune->{"pri_eff"}[0];
+            $innate_stat_value = $rune->{"pri_eff"}[1];
+            $efficiency += floatval($innate_stat_value) / (5 * floatval(RUNE_SUBSTAT_MAX_VALUES::SUBSTAT[$innate_stat][6]));
+        }
+        // Substats
+        for ($i = 0; $i <= 3; $i ++){
+            if (sizeof($rune->{"sec_eff"}) > $i){
+                $substat = $rune->{"sec_eff"}[$i][0];
+                $efficiency += floatval(RUNE_SUBSTAT_MAX_VALUES::SUBSTAT[$substat][$stars]) / (5 * floatval(RUNE_SUBSTAT_MAX_VALUES::SUBSTAT[$substat][6]));
+            }
+        }
+        $efficiency = $efficiency / 2.8 * 100;
+        if ($efficiency > 100){
+            $efficiency = 100;
+        }
+        return $efficiency;
+    }
+    
+    /**
+     * Parses an artifact and saves it to the database.
+     *
+     * @param string $uid Owner ID.
+     * @param JSON artifact JSON fragment of the artifact.
+     */
+    function parse_artifact($uid, $artifact){
+        global $db;
+        $statement = $db->prepare("
+          INSERT INTO artifact (
+            uid,
+            id,
+            assigned_to,
+            type,
+            attribute,
+            archetype,
+            level,
+            quality,
+            original_quality,
+            value,
+            efficiency,
+            max_efficiency,
+            main_stat,
+            main_stat_value,
+            effect_1,
+            effect_1_value,
+            effect_1_enchant,
+            effect_1_grind,
+            effect_2,
+            effect_2_value,
+            effect_2_enchant,
+            effect_2_grind,
+            effect_3,
+            effect_3_value,
+            effect_3_enchant,
+            effect_3_grind,
+            effect_4,
+            effect_4_value,
+            effect_4_enchant,
+            effect_4_grind,
+            locked
+          ) VALUES (
+            :uid,
+            :id,
+            :assigned_to,
+            :type,
+            :attribute,
+            :archetype,
+            :level,
+            :quality,
+            :original_quality,
+            :value,
+            :efficiency,
+            :max_efficiency,
+            :main_stat,
+            :main_stat_value,
+            :effect_1,
+            :effect_1_value,
+            :effect_1_enchant,
+            :effect_1_grind,
+            :effect_2,
+            :effect_2_value,
+            :effect_2_enchant,
+            :effect_2_grind,
+            :effect_3,
+            :effect_3_value,
+            :effect_3_enchant,
+            :effect_3_grind,
+            :effect_4,
+            :effect_4_value,
+            :effect_4_enchant,
+            :effect_4_grind,
+            :locked
+          );
+        ");
+        $statement->bindValue(":uid", $uid);
+        $statement->bindValue(":id", $artifact->{"rid"});
+        if (intval($artifact->{"occupied_id"}) == 0){
+            $statement->bindValue(":assigned_to", null);
+        }
+        else{
+            $statement->bindValue(":assigned_to", $artifact->{"occupied_id"});
+        }
+        if (intval($artifact->{"attribute"}) == 0){
+            $statement->bindValue(":attribute", null);
+        }
+        else{
+            $statement->bindValue(":attribute", $artifact->{"attribute"});
+        }
+        if (intval($artifact->{"type"}) == 0){
+            $statement->bindValue(":type", null);
+        }
+        else{
+            $statement->bindValue(":type", $artifact->{"type"});
+        }
+        if (intval($artifact->{"unit_style"}) == 0){
+            $statement->bindValue(":archetype", null);
+        }
+        else{
+            $statement->bindValue(":archetype", $artifact->{"unit_style"});
+        }
+        $statement->bindValue(":level", $artifact->{"level"});
+        $statement->bindValue(":quality", $artifact->{"rank"});
+        $statement->bindValue(":original_quality", $artifact->{"natural_rank"});
+        $statement->bindValue(":value", 0); // Not in JSON file
+        $statement->bindValue(":locked", $artifact->{"locked"});
+        $efficiency = 0; // Not standard formula yet
+        $max_efficiency = 0; // Not standard formula yet
+        $statement->bindValue(":efficiency", $efficiency);
+        $statement->bindValue(":max_efficiency", $max_efficiency);
+        $statement->bindValue(":main_stat", $artifact->{"pri_effect"}[0]);
+        $statement->bindValue(":main_stat_value", $artifact->{"pri_effect"}[1]);
+        if (sizeof($artifact->{"sec_effects"}) > 0){
+            $statement->bindValue(":effect_1", $artifact->{"sec_effects"}[0][0]);
+            $statement->bindValue(":effect_1_value", $artifact->{"sec_effects"}[0][1]);
+            $statement->bindValue(":effect_1_enchant", $artifact->{"sec_effects"}[0][2]);
+            $statement->bindValue(":effect_1_grind", $artifact->{"sec_effects"}[0][3]);
+        }
+        else{
+            $statement->bindValue(":effect_1", null);
+            $statement->bindValue(":effect_1_value", 0);
+            $statement->bindValue(":effect_1_enchant", 0);
+            $statement->bindValue(":effect_1_grind", 0);
+        }
+        if (sizeof($artifact->{"sec_effects"}) > 1){
+            $statement->bindValue(":effect_2", $artifact->{"sec_effects"}[1][0]);
+            $statement->bindValue(":effect_2_value", $artifact->{"sec_effects"}[1][1]);
+            $statement->bindValue(":effect_2_enchant", $artifact->{"sec_effects"}[1][2]);
+            $statement->bindValue(":effect_2_grind", $artifact->{"sec_effects"}[1][3]);
+        }
+        else{
+            $statement->bindValue(":effect_2", null);
+            $statement->bindValue(":effect_2_value", 0);
+            $statement->bindValue(":effect_2_enchant", 0);
+            $statement->bindValue(":effect_2_grind", 0);
+        }
+        if (sizeof($artifact->{"sec_effects"}) > 2){
+            $statement->bindValue(":effect_3", $artifact->{"sec_effects"}[2][0]);
+            $statement->bindValue(":effect_3_value", $artifact->{"sec_effects"}[2][1]);
+            $statement->bindValue(":effect_3_enchant", $artifact->{"sec_effects"}[2][2]);
+            $statement->bindValue(":effect_3_grind", $artifact->{"sec_effects"}[2][3]);
+        }
+        else{
+            $statement->bindValue(":effect_3", null);
+            $statement->bindValue(":effect_3_value", 0);
+            $statement->bindValue(":effect_3_enchant", 0);
+            $statement->bindValue(":effect_3_grind", 0);
+        }
+        if (sizeof($artifact->{"sec_effects"}) > 3){
+            $statement->bindValue(":effect_4", $artifact->{"sec_effects"}[3][0]);
+            $statement->bindValue(":effect_4_value", $artifact->{"sec_effects"}[3][1]);
+            $statement->bindValue(":effect_4_enchant", $artifact->{"sec_effects"}[3][2]);
+            $statement->bindValue(":effect_4_grind", $artifact->{"sec_effects"}[3][3]);
+        }
+        else{
+            $statement->bindValue(":effect_4", null);
+            $statement->bindValue(":effect_4_value", 0);
+            $statement->bindValue(":effect_4_enchant", 0);
+            $statement->bindValue(":effect_4_grind", 0);
+        }
+        $statement->execute();
+    }
+
+    /**
+     * Parses a rune and saves it to the database.
+     *
+     * @param string $uid Owner ID.
+     * @param JSON rune JSON fragment of the rune.
+     */
+    function parse_rune($uid, $rune, $lock_list){
+        global $db;
+        $statement = $db->prepare("
+          INSERT INTO rune (
+            uid,
+            id,
+            assigned_to,
+            type,
+            slot,
+            stars,
+            ancient,
+            level,
+            quality,
+            original_quality,
+            value,
+            efficiency,
+            max_efficiency,
+            main_stat,
+            main_stat_value,
+            innate_stat,
+            innate_stat_value,
+            substat_1,
+            substat_1_value,
+            substat_1_enchant,
+            substat_1_grind,
+            substat_2,
+            substat_2_value,
+            substat_2_enchant,
+            substat_2_grind,
+            substat_3,
+            substat_3_value,
+            substat_3_enchant,
+            substat_3_grind,
+            substat_4,
+            substat_4_value,
+            substat_4_enchant,
+            substat_4_grind,
+            locked
+          ) VALUES (
+            :uid,
+            :id,
+            :assigned_to,
+            :type,
+            :slot,
+            :stars,
+            :ancient,
+            :level,
+            :quality,
+            :original_quality,
+            :value,
+            :efficiency,
+            :max_efficiency,
+            :main_stat,
+            :main_stat_value,
+            :innate_stat,
+            :innate_stat_value,
+            :substat_1,
+            :substat_1_value,
+            :substat_1_enchant,
+            :substat_1_grind,
+            :substat_2,
+            :substat_2_value,
+            :substat_2_enchant,
+            :substat_2_grind,
+            :substat_3,
+            :substat_3_value,
+            :substat_3_enchant,
+            :substat_3_grind,
+            :substat_4,
+            :substat_4_value,
+            :substat_4_enchant,
+            :substat_4_grind,
+            :locked
+          );
+        ");
+        $statement->bindValue(":uid", $uid);
+        $statement->bindValue(":id", $rune->{"rune_id"});
+        if (intval($rune->{"occupied_id"}) == 0){
+            $statement->bindValue(":assigned_to", null);
+        }
+        else{
+            $statement->bindValue(":assigned_to", $rune->{"occupied_id"});
+        }
+        $statement->bindValue(":type", $rune->{"set_id"});
+        $statement->bindValue(":slot", $rune->{"slot_no"});
+        $statement->bindValue(":value", $rune->{"sell_value"});
+        if ($rune->{"class"} > 10){
+            // Ancient rune
+            $statement->bindValue(":ancient", 1);
+            $statement->bindValue(":stars", intval($rune->{"class"}) - 10);
+            $statement->bindValue(":original_quality", intval($rune->{"extra"}) - 10);
+        }
+        else{
+            // Non Ancient rune
+            $statement->bindValue(":ancient", 0);
+            $statement->bindValue(":stars", $rune->{"class"});
+            $statement->bindValue(":original_quality", $rune->{"extra"});
+        }
+        $level = $rune->{"upgrade_curr"};
+        $statement->bindValue(":level", $level);
+        $quality = QUALITY_ID::NORMAL;
+        if ($level >= 12){
+            $quality = QUALITY_ID::LEGEND;
+        }
+        elseif ($level >= 9){
+            $quality = QUALITY_ID::HERO;
+        }
+        elseif ($level >= 6){
+            $quality = QUALITY_ID::RARE;
+        }
+        elseif ($level >= 3){
+            $quality = QUALITY_ID::MAGIC;
+        }
+        if ($rune->{"extra"} > $quality){
+            $quality = $rune->{"extra"};
+        }
+        $statement->bindValue(":quality", $quality);
+        $lock = 0;
+        if (in_array($rune->{"rune_id"}, $lock_list)){
+            $lock = 1;
+        }
+        $statement->bindValue(":locked", $lock);
+        $efficiency = calculate_efficiency($rune);
+        $max_efficiency = calculate_maximum_efficiency($rune);
+        $statement->bindValue(":efficiency", $efficiency);
+        $statement->bindValue(":max_efficiency", $max_efficiency);
+        $statement->bindValue(":main_stat", $rune->{"pri_eff"}[0]);
+        $statement->bindValue(":main_stat_value", $rune->{"pri_eff"}[1]);
+        if ($rune->{"prefix_eff"}[0] > 0){
+            $statement->bindValue(":innate_stat", $rune->{"prefix_eff"}[0]);
+            $statement->bindValue(":innate_stat_value", $rune->{"prefix_eff"}[1]);
+        }
+        else{
+            $statement->bindValue(":innate_stat", null);
+            $statement->bindValue(":innate_stat_value", 0);
+        }
+        if (sizeof($rune->{"sec_eff"}) > 0){
+            $statement->bindValue(":substat_1", $rune->{"sec_eff"}[0][0]);
+            $statement->bindValue(":substat_1_value", $rune->{"sec_eff"}[0][1]);
+            $statement->bindValue(":substat_1_enchant", $rune->{"sec_eff"}[0][2]);
+            $statement->bindValue(":substat_1_grind", $rune->{"sec_eff"}[0][3]);
+        }
+        else{
+            $statement->bindValue(":substat_1", null);
+            $statement->bindValue(":substat_1_value", 0);
+            $statement->bindValue(":substat_1_enchant", 0);
+            $statement->bindValue(":substat_1_grind", 0);
+        }
+        if (sizeof($rune->{"sec_eff"}) > 1){
+            $statement->bindValue(":substat_2", $rune->{"sec_eff"}[1][0]);
+            $statement->bindValue(":substat_2_value", $rune->{"sec_eff"}[1][1]);
+            $statement->bindValue(":substat_2_enchant", $rune->{"sec_eff"}[1][2]);
+            $statement->bindValue(":substat_2_grind", $rune->{"sec_eff"}[1][3]);
+        }
+        else{
+            $statement->bindValue(":substat_2", null);
+            $statement->bindValue(":substat_2_value", 0);
+            $statement->bindValue(":substat_2_enchant", 0);
+            $statement->bindValue(":substat_2_grind", 0);
+        }
+        if (sizeof($rune->{"sec_eff"}) > 2){
+            $statement->bindValue(":substat_3", $rune->{"sec_eff"}[2][0]);
+            $statement->bindValue(":substat_3_value", $rune->{"sec_eff"}[2][1]);
+            $statement->bindValue(":substat_3_enchant", $rune->{"sec_eff"}[2][2]);
+            $statement->bindValue(":substat_3_grind", $rune->{"sec_eff"}[2][3]);
+        }
+        else{
+            $statement->bindValue(":substat_3", null);
+            $statement->bindValue(":substat_3_value", 0);
+            $statement->bindValue(":substat_3_enchant", 0);
+            $statement->bindValue(":substat_3_grind", 0);
+        }
+        if (sizeof($rune->{"sec_eff"}) > 3){
+            $statement->bindValue(":substat_4", $rune->{"sec_eff"}[3][0]);
+            $statement->bindValue(":substat_4_value", $rune->{"sec_eff"}[3][1]);
+            $statement->bindValue(":substat_4_enchant", $rune->{"sec_eff"}[3][2]);
+            $statement->bindValue(":substat_4_grind", $rune->{"sec_eff"}[3][3]);
+        }
+        else{
+            $statement->bindValue(":substat_4", null);
+            $statement->bindValue(":substat_4_value", 0);
+            $statement->bindValue(":substat_4_enchant", 0);
+            $statement->bindValue(":substat_4_grind", 0);
+        }
+        $statement->execute();
+    }
+
+    /**
+     * Parses the response to the HubUserLogin command and updates profile.
+     *
+     * @param SQLiteConn $db Database connection.
+     * @param int $uid Player UID.
+     * @param JSON $json Response.
+     */
+    function parse_profile($db, $uid, $json){
+        // Save data file
+        $uname = $json->{"wizard_info"}->{"wizard_name"};
+        $dtime = (new DateTime())->format("Y-m-dTH:i:s");
+        $fname = ($_SERVER["DOCUMENT_ROOT"] . "/../data/profile_" . $uid . "_" . $uname . "_" . $dtime . ".json");
+        try{
+            file_put_contents($fname, json_encode($json));
+        }
+        catch(Exception $e) {
+            header("HTTP/1.1 500 Unable to save profile file: " . $e->getMessage());
+            syslog(LOG_INFO, "[APIv3] HTTP/1.1 500 Unable to save profile file: " . $e->getMessage());
+            return 500;
+        }
+
+        // Clear previous profile data
+        $db->query("PRAGMA foreign_keys = OFF;");
+        $statement = $db->prepare("DELETE FROM unit_skill WHERE unit IN (SELECT id FROM unit WHERE uid = :uid);");
+        $statement->bindValue(":uid", $uid);
+        $statement->execute();
+        $tables_to_clear = [
+            "scenario",
+            "defense",
+            "gw_defense",
+            "unit",
+            "rune",
+            "artifact",
+            "building",
+            "decoration",
+            "inventory",
+            "enchantment"
+        ];
+        foreach ($tables_to_clear as $table){
+            $statement = $db->prepare("DELETE FROM $table WHERE uid = :uid;");
+            $statement->bindValue(":uid", $uid);
+            $statement->execute();
+        }
+        $db->query("DELETE FROM summon_special"); # For every player, will be reloaded.
+
+        // Get rune lock list
+        $rune_lock_list = $json->{"rune_lock_list"};
+
+        // Update player data
+        $statement = $db->prepare("
+          UPDATE player
+          SET
+            name = :name AND
+            mana = :mana AND
+            crystal = :crystal AND
+            country = :country AND
+            lang = :lang AND
+            level = :level AND
+            experience = :experience AND
+            energy = :energy AND
+            energy_max = :energy_max AND
+            energy_per_min = :energy_per_min AND
+            arena_energy = :arena_energy AND
+            arena_energy_max = :arena_energy_max AND
+            rep = :rep AND
+            social_point = :social_point AND
+            honor_point = :honor_point AND
+            guild_point = :guild_point AND
+            darkportal_energy = :darkportal_energy AND
+            darkportal_energy_max = :darkportal_energy_max AND
+            dimension_energy = :dimension_energy AND
+            dimension_energy_max = :dimension_energy_max AND
+            costume_point = :costume_point AND
+            costume_point_max = :costume_point_max AND
+            honor_medal = :honor_medal AND
+            honor_mark = :honor_mark AND
+            event_coin = :event_coin AND
+            storage_slots = :storage_slots AND
+            island = :island_upgrade
+          WHERE uid = :uid;
+        ");
+        $statement->bindValue(":name", $json->{"wizard_info"}->{"wizard_name"});
+        $statement->bindValue(":mana", $json->{"wizard_info"}->{"wizard_mana"});
+        $statement->bindValue(":crystal", $json->{"wizard_info"}->{"wizard_crystal"});
+        $statement->bindValue(":country", $json->{"wizard_info"}->{"wizard_last_country"});
+        $statement->bindValue(":lang", $json->{"wizard_info"}->{"wizard_last_lang"});
+        $statement->bindValue(":level", $json->{"wizard_info"}->{"wizard_level"});
+        $statement->bindValue(":experience", $json->{"wizard_info"}->{"experience"});
+        $statement->bindValue(":energy", $json->{"wizard_info"}->{"wizard_energy"});
+        $statement->bindValue(":energy_max", $json->{"wizard_info"}->{"energy_max"});
+        $statement->bindValue(":energy_per_min", $json->{"wizard_info"}->{"energy_per_min"});
+        $statement->bindValue(":arena_energy", $json->{"wizard_info"}->{"arena_energy"});
+        $statement->bindValue(":arena_energy_max", $json->{"wizard_info"}->{"arena_energy_max"});
+        $statement->bindValue(":rep", $json->{"wizard_info"}->{"rep_unit_id"});
+        $statement->bindValue(":social_point", $json->{"wizard_info"}->{"social_point_current"});
+        $statement->bindValue(":honor_point", $json->{"wizard_info"}->{"honor_point"});
+        $statement->bindValue(":guild_point", $json->{"wizard_info"}->{"guild_point"});
+        $statement->bindValue(":darkportal_energy", $json->{"wizard_info"}->{"darkportal_energy"});
+        $statement->bindValue(":darkportal_energy_max", $json->{"wizard_info"}->{"darkportal_energy_max"});
+        $statement->bindValue(":dimension_energy", $json->{"dimension_hole_info"}->{"energy"});
+        $statement->bindValue(":dimension_energy_max", $json->{"dimension_hole_info"}->{"energy_max"});
+        $statement->bindValue(":costume_point", $json->{"wizard_info"}->{"costume_point"});
+        $statement->bindValue(":costume_point_max", $json->{"wizard_info"}->{"costume_point_max"});
+        $statement->bindValue(":honor_medal", $json->{"wizard_info"}->{"honor_medal"});
+        $statement->bindValue(":honor_mark", $json->{"wizard_info"}->{"honor_mark"});
+        $statement->bindValue(":event_coin", $json->{"wizard_info"}->{"event_coin"});
+        $statement->bindValue(":storage_slots", $json->{"unit_depository_slots"}->{"number"});
+        $island_upgrade = 0;
+        foreach ($json->{"island_info"} as $island){
+            if (intval($island->{"id"}) > 100 && intval($island->{"open"}) == 1){
+                $island_upgrade = intval($island->{"id"});
+            }
+        }
+        $statement->bindValue(":island_upgrade", $island_upgrade);
+        $statement->bindValue(":uid", $uid);
+        $statement->execute();
+
+        // Update scenarion completion
+        foreach ($json->{"scenario_list"} as $sce){
+            $statement = $db->prepare("
+              INSERT INTO scenario (
+                uid,
+                region,
+                difficulty,
+                cleared,
+                max_cleared
+              ) VALUES (
+                :uid,
+                :region,
+                :difficulty,
+                :cleared,
+                :max_cleared
+              );
+            ");
+            $statement->bindValue(":uid", $uid);
+            $statement->bindValue(":region", $sce->{"region_id"});
+            $statement->bindValue(":difficulty", $sce->{"difficulty"});
+            $statement->bindValue(":cleared", $sce->{"cleared"});
+            $max_cleared = 0;
+            foreach ($sce->{"stage_list"} as $stage){
+                if (intval($stage->{"cleared"}) == 1){
+                    $max_cleared = intval($stage->{"stage_no"});
+                }
+            }
+            $statement->bindValue(":max_cleared", $max_cleared);
+            $statement->execute();
+        }
+
+        // Parse Arena defense
+        foreach ($json->{"defense_unit_list"} as $defense){
+            $statement = $db->prepare("
+              INSERT INTO defense (
+                uid,
+                unit,
+                position
+              ) VALUES (
+                :uid,
+                :unit,
+                :position
+              );
+            ");
+            $statement->bindValue(":uid", $uid);
+            $statement->bindValue(":unit", $defense->{"unit_id"});
+            $statement->bindValue(":position", $defense->{"pos_id"});
+            $statement->execute();
+        }
+
+        // Parse GW defense
+        if ($json->{"guildwar_defense_unit_list"}[0]){
+            $position = 1;
+            foreach ($json->{"guildwar_defense_unit_list"}[0] as $defense){
+                $statement = $db->prepare("
+                  INSERT INTO gw_defense (
+                    uid,
+                    unit,
+                    position
+                  ) VALUES (
+                    :uid,
+                    :unit,
+                    :position
+                  );
+                ");
+                $statement->bindValue(":uid", $uid);
+                $statement->bindValue(":unit", $defense->{"unit_id"});
+                $statement->bindValue(":position", $position);
+                $statement->execute();
+                $position ++;
+            }
+        }
+        if ($json->{"guildwar_defense_unit_list"}[1]){
+            $position = 4;
+            foreach ($json->{"guildwar_defense_unit_list"}[1] as $defense){
+                $statement = $db->prepare("
+                  INSERT INTO gw_defense (
+                    uid,
+                    unit,
+                    position
+                  ) VALUES (
+                    :uid,
+                    :unit,
+                    :position
+                  );
+                ");
+                $statement->bindValue(":uid", $uid);
+                $statement->bindValue(":unit", $defense->{"unit_id"});
+                $statement->bindValue(":position", $position);
+                $statement->execute();
+                $position ++;
+            }
+        }
+
+        // Parse buildings
+        foreach($json->{"building_list"} as $building){
+            $statement = $db->prepare("
+              INSERT INTO building (
+                uid,
+                id,
+                building,
+                gain
+              ) VALUES (
+                :uid,
+                :id,
+                :building,
+                :gain
+              );
+            ");
+            $statement->bindValue(":uid", $uid);
+            $statement->bindValue(":id", $building->{"building_id"});
+            $statement->bindValue(":building", $building->{"building_master_id"});
+            $statement->bindValue(":gain", $building->{"gain_per_hour"});
+            $statement->execute();
+        }
+
+        // Parse decorarion
+        foreach($json->{"deco_list"} as $building){
+            $statement = $db->prepare("
+              INSERT INTO decoration (
+                uid,
+                id,
+                decoration,
+                level
+              ) VALUES (
+                :uid,
+                :id,
+                :decoration,
+                :level
+              );
+            ");
+            $statement->bindValue(":uid", $uid);
+            $statement->bindValue(":id", $building->{"deco_id"});
+            $statement->bindValue(":decoration", $building->{"master_id"});
+            $statement->bindValue(":level", $building->{"level"});
+            $statement->execute();
+        }
+
+        // Parse units
+        $lock_list = $json->{"unit_lock_list"};
+        foreach ($json->{"unit_list"} as $unit){
+            $statement = $db->prepare("
+              INSERT INTO unit (
+                uid,
+                id,
+                building,
+                unit,
+                level,
+                stars,
+                hp,
+                attack,
+                defense,
+                speed,
+                crit_rate,
+                crit_damage,
+                resistance,
+                accuracy,
+                experience,
+                exp_gained,
+                exp_gain_rate,
+                costume,
+                attribute,
+                source,
+                create_time,
+                homunculus,
+                homunculus_name,
+                lock
+              ) VALUES (
+                :uid,
+                :id,
+                :building,
+                :unit,
+                :level,
+                :stars,
+                :hp,
+                :attack,
+                :defense,
+                :speed,
+                :crit_rate,
+                :crit_damage,
+                :resistance,
+                :accuracy,
+                :experience,
+                :exp_gained,
+                :exp_gain_rate,
+                :costume,
+                :attribute,
+                :source,
+                :create_time,
+                :homunculus,
+                :homunculus_name,
+                :lock
+              );
+            ");
+            $statement->bindValue(":uid", $uid);
+            $statement->bindValue(":id", $unit->{"unit_id"});
+            $statement->bindValue(":building", $unit->{"building_id"});
+            $statement->bindValue(":unit", $unit->{"unit_master_id"});
+            $statement->bindValue(":level", $unit->{"unit_level"});
+            $statement->bindValue(":stars", $unit->{"class"});
+            $statement->bindValue(":hp", intval($unit->{"con"}) * 15); # Always x15
+            $statement->bindValue(":attack", $unit->{"atk"});
+            $statement->bindValue(":defense", $unit->{"def"});
+            $statement->bindValue(":speed", $unit->{"spd"});
+            $statement->bindValue(":crit_rate", $unit->{"critical_rate"});
+            $statement->bindValue(":crit_damage", $unit->{"critical_damage"});
+            $statement->bindValue(":resistance", $unit->{"resist"});
+            $statement->bindValue(":accuracy", $unit->{"accuracy"});
+            $statement->bindValue(":experience", $unit->{"experience"});
+            $statement->bindValue(":exp_gained", $unit->{"exp_gained"});
+            $statement->bindValue(":exp_gain_rate", $unit->{"exp_gain_rate"});
+            $statement->bindValue(":costume", $unit->{"costume_master_id"});
+            $statement->bindValue(":attribute", $unit->{"attribute"});
+            $statement->bindValue(":source", $unit->{"source"});
+            $statement->bindValue(":create_time", $unit->{"create_time"});
+            $statement->bindValue(":homunculus", $unit->{"homunculus"});
+            $statement->bindValue(":homunculus_name", $unit->{"homunculus_name"});
+            $lock = 0;
+            if (in_array($unit->{"unit_id"}, $lock_list)){
+                $lock = 1;
+            }
+            $statement->bindValue(":lock", $lock);
+            $statement->execute();
+            foreach ($unit->{"skills"} as $skill){
+                $statement = $db->prepare("
+                  INSERT INTO unit_skill (
+                    unit,
+                    skill,
+                    level
+                  ) VALUES (
+                    :unit,
+                    :skill,
+                    :level
+                  );
+                ");
+                $statement->bindValue(":unit", $unit->{"unit_id"});
+                $statement->bindValue(":skill", $skill[0]);
+                $statement->bindValue(":level", $skill[1]);
+                $statement->execute();
+            }
+        }
+
+        // Parse invetory
+        foreach ($json->{"inventory_info"} as $item){
+            $statement = $db->prepare("
+              INSERT INTO inventory (
+                uid,
+                id,
+                type,
+                amount
+              ) VALUES (
+                :uid,
+                :id,
+                :type,
+                :amount
+              );
+            ");
+            $statement->bindValue(":uid", $uid);
+            $statement->bindValue(":id", $item->{"item_master_id"});
+            $statement->bindValue(":type", $item->{"item_master_type"});
+            $statement->bindValue(":amount", $item->{"item_quantity"});
+            $statement->execute();
+        }
+        // Fix rune craft item type.
+        $db->query("UPDATE inventory SET type = 27 WHERE type = 29 AND id IN (2001, 4001, 4002, 4003, 9001, 9002, 9003, 8001);");
+
+        // Parse Summon Stone summoning list
+        // TODO: Set propper dates
+        foreach ($json->{"summon_special_info"}->{"this"} as $unit){
+            $statement = $db->prepare("
+              INSERT INTO summon_special (
+                week,
+                unit
+              ) VALUES (
+                :week,
+                :unit
+              );
+            ");
+            $statement->bindValue(":week", 0);
+            $statement->bindValue(":unit", $unit);
+            $statement->execute();
+        }
+        foreach ($json->{"summon_special_info"}->{"next"} as $unit){
+            $statement = $db->prepare("
+              INSERT INTO summon_special (
+                week,
+                unit
+              ) VALUES (
+                :week,
+                :unit
+              );
+            ");
+            $statement->bindValue(":week", 1);
+            $statement->bindValue(":unit", $unit);
+            $statement->execute();
+        }
+        foreach ($json->{"summon_special_info"}->{"third"} as $unit){
+            $statement = $db->prepare("
+              INSERT INTO summon_special (
+                week,
+                unit
+              ) VALUES (
+                :week,
+                :unit
+              );
+            ");
+            $statement->bindValue(":week", 2);
+            $statement->bindValue(":unit", $unit);
+            $statement->execute();
+        }
+        foreach ($json->{"summon_special_info"}->{"fourth"} as $unit){
+            $statement = $db->prepare("
+              INSERT INTO summon_special (
+                week,
+                unit
+              ) VALUES (
+                :week,
+                :unit
+              );
+            ");
+            $statement->bindValue(":week", 3);
+            $statement->bindValue(":unit", $unit);
+            $statement->execute();
+        }
+
+        // Parse runes
+        foreach ($json->{"runes"} as $rune){
+            parse_rune($uid, $rune, $rune_lock_list);
+        }
+        foreach ($json->{"unit_list"} as $unit){
+            foreach ($unit->{"runes"} as $rune){
+                parse_rune($uid, $rune, $rune_lock_list);
+            }
+        }
+
+        // Parse artifacts
+        foreach ($json->{"artifacts"} as $artifact){
+            parse_artifact($uid, $artifact);
+        }
+        foreach ($json->{"unit_list"} as $unit){
+            foreach ($unit->{"artifacts"} as $artifact){
+                parse_artifact($uid, $artifact);
+            }
+        }
+
+        // Parse enchantments
+        foreach ($json->{"rune_craft_item_list"} as $item){
+            $statement = $db->prepare("
+              INSERT INTO enchantment (
+                uid,
+                id,
+                type,
+                quality,
+                rune,
+                stat,
+                value,
+                amount
+              ) VALUES (
+                :uid,
+                :id,
+                :type,
+                :quality,
+                :rune,
+                :stat,
+                :value,
+                :amount
+              );
+            ");
+            $statement->bindValue(":uid", $uid);
+            $statement->bindValue(":id", $item->{"craft_item_id"});
+            $statement->bindValue(":type", $item->{"craft_type"});
+            $statement->bindValue(":value", $item->{"sell_value"});
+            $statement->bindValue(":amount", $item->{"amount"});
+            $info = str_pad(intval($item->{"craft_type_id"}), 6, "0", STR_PAD_LEFT);
+            /*
+            info : 5 or 6 digit number: RRSSQQ
+             RR: Rune (may not be padded)
+             SS: Stat
+             QQ:  Quality
+            */
+            $statement->bindValue(":quality", intval(substr($info, 4, 2)));
+            $statement->bindValue(":stat", intval(substr($info, 2, 2)));
+            $statement->bindValue(":rune", intval(substr($info, 0, 2)));
+            $statement->execute();
+        }
+
+        // Parse guild
+        if ($json->{"guild"}->{"guild_info"} != null){
+            $guild = $json->{"guild"}->{"guild_info"};
+            $statement = $db->prepare("DELETE FROM guild WHERE id = :id;");
+            $statement->bindValue(":id", $guild->{"guild_id"});
+            $statement->execute();
+            $statement = $db->prepare("DELETE FROM guild_member WHERE guild = :guild;");
+            $statement->bindValue(":guild", $guild->{"guild_id"});
+            $statement->execute();
+            $statement = $db->prepare("
+              INSERT INTO guild (
+                id,
+                name,
+                level,
+                experience,
+                recruiting,
+                members,
+                leader,
+                comment,
+                notice
+              ) VALUES (
+                :id,
+                :name,
+                :level,
+                :experience,
+                :recruiting,
+                :members,
+                :leader,
+                :comment,
+                :notice
+              );
+            ");
+            $statement->bindValue(":id",$guild->{"guild_id"});
+            $statement->bindValue(":name",$guild->{"name"});
+            $statement->bindValue(":level",$guild->{"level"});
+            $statement->bindValue(":experience",$guild->{"experience"});
+            $statement->bindValue(":recruiting",$guild->{"recruit_status"});
+            $statement->bindValue(":members",$guild->{"member_now"});
+            $statement->bindValue(":leader",$guild->{"master_wizard_id"});
+            $statement->bindValue(":comment",$guild->{"comment"});
+            $statement->bindValue(":notice",$guild->{"notice"});
+            $statement->execute();
+            foreach ($json->{"guild"}->{"guild_members"} as $member){
+                $statement = $db->prepare("
+                  INSERT INTO guild_member (
+                    id,
+                    guild,
+                    grade,
+                    name,
+                    level,
+                    rating,
+                    arena_score,
+                    joined,
+                    last_login,
+                    in_war,
+                    has_defense
+                  ) VALUES (
+                    :id,
+                    :guild,
+                    :grade,
+                    :name,
+                    :level,
+                    :rating,
+                    :arena_score,
+                    :joined,
+                    :last_login,
+                    :in_war,
+                    :has_defense
+                  );
+                ");
+                $statement->bindValue(":id", $member->{"wizard_id"});
+                $statement->bindValue(":guild", $member->{"guild_id"});
+                $statement->bindValue(":grade", $member->{"grade"});
+                $statement->bindValue(":name", $member->{"wizard_name"});
+                $statement->bindValue(":level", $member->{"wizard_level"});
+                $statement->bindValue(":rating", $member->{"rating_id"});
+                $statement->bindValue(":arena_score", $member->{"arena_score"});
+                $statement->bindValue(":joined", $member->{"join_timestamp"});
+                $statement->bindValue(":last_login", $member->{"last_login_timestamp"});
+                $in_war = 0;
+                foreach ($json->{"guildwar_member_list"} as $war){
+                    if ($war->{"wizard_id"} == $member->{"wizard_id"}){
+                        $in_war = 1;
+                        break;
+                    }
+                }
+                $has_defense = 0;
+                foreach ($json->{"guild_member_defense_list"} as $defense){
+                    if ($defense->{"wizard_id"} == $member->{"wizard_id"}){
+                        $has_defense = 1;
+                        break;
+                    }
+                }
+                $statement->bindValue(":in_war", $in_war);
+                $statement->bindValue(":has_defense", $has_defense);
+                $statement->execute();
+            }
+        }
+
+        // Update invalid teams
+        $statement = $db->prepare("
+          DELETE FROM team 
+          WHERE 
+            id IN (
+              SELECT DISTINCT team 
+              FROM team_unit 
+              WHERE unit NOT IN (
+                SELECT DISTINCT id FROM unit WHERE uid = :uid
+              )
+            ) AND
+            uid = :uid
+          ");
+        $statement->bindValue(":uid", $uid);
+        $statement->execute();
+        $db->query("
+          DELETE FROM team_unit
+          WHERE 
+            unit NOT IN (
+              SELECT DISTINCT id FROM unit
+            ) OR 
+            team NOT IN (
+              SELECT DISTINCT id FROM team
+            );"
+        );
+        // Return success
+        return "201 Created.";
+    }
+
+    /**
+     * Parses the response to the GetUnitCollection command and updates the
+     * user monster collection status.
+     *
+     * @param SQLiteConn $db Database connection.
+     * @param int $uid Player UID.
+     * @param JSON $json Response.
+     */
+    function parse_collection($db, $uid, $json){
+        // Clear previous collection data
+        $db->query("PRAGMA foreign_keys = OFF;");
+        $statement = $db->prepare("DELETE FROM collection WHERE uid = :uid;");
+        $statement->bindValue(":uid", $uid);
+        $statement->execute();
+        foreach ($json->{"collection"} as $unit){
+            $statement = $db->prepare("
+              INSERT INTO collection (uid, unit, open)
+              VALUES (:uid, :unit, :open);
+            ");
+            $statement->bindValue(":uid", $uid);
+            $statement->bindValue(":unit", $unit->{"unit_master_id"});
+            $statement->bindValue(":open", $unit->{"open"});
+            $statement->execute();
+        }
+        // Return success
+        return "201 Created.";
+    }
+
+    /**
+     * Parses the response to the GetLobbyWizardLog command and updates the
+     * user records.
+     *
+     * @param SQLiteConn $db Database connection.
+     * @param int $uid Player UID.
+     * @param JSON $json Response.
+     */
+    function parse_records($db, $uid, $json){
+
+        # Page 1: Cairos non-elemental, rift raid, rift dungeon.
+        if (intval($json->{"lobby_wizard_log"}->{"page_no"}) == 1){
+
+            // Update data in player table
+            $db->query("PRAGMA foreign_keys = OFF;");
+            $statement = $db->prepare("
+              UPDATE player SET 
+                joined = :joined,
+                top_rank_arena = :top_rank_arena,
+                top_rank_world_arena = :top_rank_world_arena,
+                top_rank_special_league = :top_rank_special_league,
+                top_rank_gw = :top_rank_gw,
+                top_rank_siege = :top_rank_siege,
+                top_rank_wboss = :top_rank_wboss,
+                top_rank_toan = :top_rank_toan,
+                top_rank_toah = :top_rank_toah
+              WHERE uid = :uid;
+            ");
+            $statement->bindValue(":joined", $json->{"lobby_wizard_log"}->{"account_create_timestamp"});
+            $statement->bindValue(":top_rank_arena", $json->{"lobby_wizard_log"}->{"pvp_best_rating_id"});
+            $statement->bindValue(":top_rank_world_arena", $json->{"lobby_wizard_log"}->{"rtpvp_rank_best_rating_id"});
+            $statement->bindValue(":top_rank_special_league", $json->{"lobby_wizard_log"}->{"rtpvp_contest_best_rating_id"});
+            $statement->bindValue(":top_rank_gw", $json->{"lobby_wizard_log"}->{"guildwar_best_rating_id"});
+            $statement->bindValue(":top_rank_siege", $json->{"lobby_wizard_log"}->{"guildsiege_best_rating_id"});
+            $statement->bindValue(":top_rank_wboss", $json->{"lobby_wizard_log"}->{"world_boss_best_rank_id"});
+            $statement->bindValue(":top_rank_toan", $json->{"lobby_wizard_log"}->{"trial_tower_normal_best_floor"});
+            $statement->bindValue(":top_rank_toah", $json->{"lobby_wizard_log"}->{"trial_tower_hard_best_floor"});
+            $statement->bindValue(":uid", $uid);
+            $statement->execute();
+
+            // Loop Cairos records
+            foreach ($json->{"lobby_wizard_log"}->{"dungeon_best_clear_info_list"} as $record){
+                // Delete prevous data
+                $statement = $db->prepare("
+                  DELETE FROM record_party
+                  WHERE 
+                    uid = :uid AND
+                    area_type = :area_type AND
+                    area = :area;
+                ");
+                $statement->bindValue(":uid", $uid);
+                $statement->bindValue(":area_type", AREA_TYPE_ID::CAIROS_DUNGEON);
+                $statement->bindValue(":area", $record->{"dungeon_id"});
+                $statement->execute();
+                $statement = $db->prepare("
+                  DELETE FROM record
+                  WHERE 
+                    uid = :uid AND
+                    area_type = :area_type AND
+                    area = :area;
+                ");
+                $statement->bindValue(":uid", $uid);
+                $statement->bindValue(":area_type", AREA_TYPE_ID::CAIROS_DUNGEON);
+                $statement->bindValue(":area", $record->{"dungeon_id"});
+                $statement->execute();
+
+                // Insert new data
+                $statement = $db->prepare("
+                  INSERT INTO record
+                    (uid, area_type, area, stage, time, score, rank)
+                  VALUES
+                    (:uid, :area_type, :area, :stage, :time, :score, :rank);
+                ");
+                $statement->bindValue(":uid", $uid);
+                $statement->bindValue(":area_type", AREA_TYPE_ID::CAIROS_DUNGEON);
+                $statement->bindValue(":area", $record->{"dungeon_id"});
+                $statement->bindValue(":area", $record->{"dungeon_id"});
+                $statement->bindValue(":stage", $record->{"stage_id"});
+                $statement->bindValue(":time", $record->{"clear_time"});
+                $statement->bindValue(":score", 0);
+                $statement->bindValue(":rank", null);
+                $statement->execute();
+
+                // Loop party
+                foreach ($record->{"my_unit_deck_list"} as $party){
+                    $statement = $db->prepare("
+                      INSERT INTO record_party
+                        (uid, area_type, area, unit, k_unit, leader, front)
+                      VALUES
+                        (:uid, :area_type, :area, :unit, :k_unit, :leader, :front);
+                    ");
+                    $statement->bindValue(":uid", $uid);
+                    $statement->bindValue(":area_type", AREA_TYPE_ID::CAIROS_DUNGEON);
+                    $statement->bindValue(":area", $record->{"dungeon_id"});
+                    $statement->bindValue(":unit", $party->{"unit_id"});
+                    $statement->bindValue(":k_unit", $party->{"unit_master_id"});
+                    $statement->bindValue(":leader", $party->{"leader"});
+                    $statement->bindValue(":front", 0);
+                    $statement->execute();
+                }
+            }
+
+            // Rift Raid record
+            if (sizeof($json->{"lobby_wizard_log"}->{"raid_best_clear_info_list"}) > 0){
+                $record = $json->{"lobby_wizard_log"}->{"raid_best_clear_info_list"}[0];
+
+                // Delete prevous data
+                $statement = $db->prepare("
+                  DELETE FROM record_party 
+                  WHERE 
+                    uid = :uid AND
+                    area_type = :area_type AND
+                    area = :area;
+                ");
+                $statement->bindValue(":uid", $uid);
+                $statement->bindValue(":area_type", AREA_TYPE_ID::RIFT_RAID);
+                $statement->bindValue(":area", $record->{"stage_id"});
+                $statement->execute();
+                $statement = $db->prepare("
+                  DELETE FROM record
+                  WHERE 
+                    uid = :uid AND
+                    area_type = :area_type AND
+                    area = :area;
+                ");
+                $statement->bindValue(":uid", $uid);
+                $statement->bindValue(":area_type", AREA_TYPE_ID::RIFT_RAID);
+                $statement->bindValue(":area", $record->{"stage_id"});
+                $statement->execute();
+
+                // Insert new data
+                $statement = $db->prepare("
+                  INSERT INTO record
+                    (uid, area_type, area, stage, time, score, rank)
+                  VALUES
+                    (:uid, :area_type, :area, :stage, :time, :score, :rank);
+                ");
+                $statement->bindValue(":uid", $uid);
+                $statement->bindValue(":area_type", AREA_TYPE_ID::RIFT_RAID);
+                $statement->bindValue(":area", $record->{"stage_id"});
+                $statement->bindValue(":stage", $record->{"stage_id"});
+                $statement->bindValue(":time", $record->{"clear_time"});
+                $statement->bindValue(":score", 0);
+                $statement->bindValue(":rank", null);
+                $statement->execute();
+
+                // Loop party
+                foreach ($record->{"my_unit_deck_list"} as $party){
+                    $statement = $db->prepare("
+                      INSERT INTO record_party
+                        (uid, area_type, area, unit, k_unit, leader, front)
+                      VALUES
+                        (:uid, :area_type, :area, :unit, :k_unit, :leader, :front);
+                    ");
+                    $statement->bindValue(":uid", $uid);
+                    $statement->bindValue(":area_type", AREA_TYPE_ID::RIFT_RAID);
+                    $statement->bindValue(":area", $record->{"stage_id"});
+                    $statement->bindValue(":unit", $party->{"unit_id"});
+                    $statement->bindValue(":k_unit", $party->{"unit_master_id"});
+                    $statement->bindValue(":leader", $party->{"leader"});
+                    if ($party->{"slot_index"} <= 4){
+                        $statement->bindValue(":front", 1);
+                    }
+                    else{
+                        $statement->bindValue(":front", 0);
+                    }
+                    $statement->execute();
+                }
+            }
+
+            // Loop Rift Dungeon records
+            foreach ( $json->{"lobby_wizard_log"}->{"rift_dungeon_best_clear_info_list"} as $record){
+
+                // Delete prevous data
+                $statement = $db->prepare("
+                  DELETE FROM record_party 
+                  WHERE 
+                    uid = :uid AND
+                    area_type = :area_type AND
+                    area = :area;
+                ");
+                $statement->bindValue(":uid", $uid);
+                $statement->bindValue(":area_type", AREA_TYPE_ID::RIFT_DUNGEON);
+                $statement->bindValue(":area", $record->{"rift_dungeon_id"});
+                $statement->execute();
+                $statement = $db->prepare("
+                  DELETE FROM record
+                  WHERE 
+                    uid = :uid AND
+                    area_type = :area_type AND
+                    area = :area;
+                ");
+                $statement->bindValue(":uid", $uid);
+                $statement->bindValue(":area_type", AREA_TYPE_ID::RIFT_DUNGEON);
+                $statement->bindValue(":area", $record->{"rift_dungeon_id"});
+                $statement->execute();
+
+                // Insert new data
+                $statement = $db->prepare("
+                  INSERT INTO record
+                    (uid, area_type, area, stage, time, score, rank)
+                  VALUES
+                    (:uid, :area_type, :area, :stage, :time, :score, :rank);
+                ");
+                $statement->bindValue(":uid", $uid);
+                $statement->bindValue(":area_type", AREA_TYPE_ID::RIFT_DUNGEON);
+                $statement->bindValue(":area", $record->{"rift_dungeon_id"});
+                $statement->bindValue(":stage", 0);
+                $statement->bindValue(":time", 0);
+                $statement->bindValue(":score", $record->{"clear_damage"});
+                switch (intval($record->{"clear_rating"})){
+                    case 2:
+                        $statement->bindValue(":rank", "D");
+                        break;
+                    case 3:
+                        $statement->bindValue(":rank", "C");
+                        break;
+                    case 4:
+                        $statement->bindValue(":rank", "B-");
+                        break;
+                    case 5:
+                        $statement->bindValue(":rank", "B");
+                        break;
+                    case 6:
+                        $statement->bindValue(":rank", "B+");
+                        break;
+                    case 7:
+                        $statement->bindValue(":rank", "A-");
+                        break;
+                    case 8:
+                        $statement->bindValue(":rank", "A");
+                        break;
+                    case 9:
+                        $statement->bindValue(":rank", "A+");
+                        break;
+                    case 10:
+                        $statement->bindValue(":rank", "S");
+                        break;
+                    case 11:
+                        $statement->bindValue(":rank", "SS");
+                        break;
+                    case 12:
+                        $statement->bindValue(":rank", "SS");
+                        break;
+                    default:
+                        $statement->bindValue(":rank", "F");
+                }
+                $statement->execute();
+
+                // Loop party
+                foreach ($record->{"my_unit_deck_list"} as $party){
+                    $statement = $db->prepare("
+                      INSERT INTO record_party
+                        (uid, area_type, area, unit, k_unit, leader, front)
+                      VALUES
+                        (:uid, :area_type, :area, :unit, :k_unit, :leader, :front);
+                    ");
+                    $statement->bindValue(":uid", $uid);
+                    $statement->bindValue(":area_type", AREA_TYPE_ID::RIFT_DUNGEON);
+                    $statement->bindValue(":area", $record->{"rift_dungeon_id"});
+                    $statement->bindValue(":unit", $party->{"unit_id"});
+                    $statement->bindValue(":k_unit", $party->{"unit_master_id"});
+                    $statement->bindValue(":leader", $party->{"leader"});
+                    if ($party->{"slot_index"} <= 4){
+                        $statement->bindValue(":front", 1);
+                    }
+                    else{
+                        $statement->bindValue(":front", 0);
+                    }
+                    $statement->execute();
+                }
+            }
+        }
+
+        # Page 2: Cairos non-elemental, rift raid, rift dungeon.
+        elseif (intval($json->{"lobby_wizard_log"}->{"page_no"} == 2)){
+            // Loop Cairos records
+            foreach ($json->{"lobby_wizard_log"}->{"dungeon_best_clear_info_list"} as $record){
+                // Delete prevous data
+                $statement = $db->prepare("
+                  DELETE FROM record_party
+                  WHERE 
+                    uid = :uid AND
+                    area_type = :area_type AND
+                    area = :area;
+                ");
+                $statement->bindValue(":uid", $uid);
+                $statement->bindValue(":area_type", AREA_TYPE_ID::CAIROS_DUNGEON);
+                $statement->bindValue(":area", $record->{"dungeon_id"});
+                $statement->execute();
+                $statement = $db->prepare("
+                  DELETE FROM record
+                  WHERE 
+                    uid = :uid AND
+                    area_type = :area_type AND
+                    area = :area;
+                ");
+                $statement->bindValue(":uid", $uid);
+                $statement->bindValue(":area_type", AREA_TYPE_ID::CAIROS_DUNGEON);
+                $statement->bindValue(":area", $record->{"dungeon_id"});
+                $statement->execute();
+
+                // Insert new data
+                $statement = $db->prepare("
+                  INSERT INTO record
+                    (uid, area_type, area, stage, time, score, rank)
+                  VALUES
+                    (:uid, :area_type, :area, :stage, :time, :score, :rank);
+                ");
+                $statement->bindValue(":uid", $uid);
+                $statement->bindValue(":area_type", AREA_TYPE_ID::CAIROS_DUNGEON);
+                $statement->bindValue(":area", $record->{"dungeon_id"});
+                $statement->bindValue(":area", $record->{"dungeon_id"});
+                $statement->bindValue(":stage", $record->{"stage_id"});
+                $statement->bindValue(":time", $record->{"clear_time"});
+                $statement->bindValue(":score", 0);
+                $statement->bindValue(":rank", null);
+                $statement->execute();
+
+                // Loop party
+                foreach ($record->{"my_unit_deck_list"} as $party){
+                    $statement = $db->prepare("
+                      INSERT INTO record_party
+                        (uid, area_type, area, unit, k_unit, leader, front)
+                      VALUES
+                        (:uid, :area_type, :area, :unit, :k_unit, :leader, :front);
+                    ");
+                    $statement->bindValue(":uid", $uid);
+                    $statement->bindValue(":area_type", AREA_TYPE_ID::CAIROS_DUNGEON);
+                    $statement->bindValue(":area", $record->{"dungeon_id"});
+                    $statement->bindValue(":unit", $party->{"unit_id"});
+                    $statement->bindValue(":k_unit", $party->{"unit_master_id"});
+                    $statement->bindValue(":leader", $party->{"leader"});
+                    $statement->bindValue(":front", 0);
+                    $statement->execute();
+                }
+            }
+        }
+        elseif (intval($json->{"lobby_wizard_log"}->{"page_no"}) == 3){
+            return "204 Page not logged (only pages 1 and 2 are needed).";
+        }
+        elseif (intval($json->{"lobby_wizard_log"}->{"page_no"}) == 4){
+            return "204 Page not logged (only pages 1 and 2 are needed).";
+        }
+        else{
+            return "400 Unknown page.";
+        }
+
+        // Return success
+        return "201 Created.";
+    }
+
+    try{
+
+        header("Content-type: application/json; charset=utf-8");
+
+        // Get put data
+        parse_str(file_get_contents("php://input"), $_PUT);
+
+        // Check API key.
+        $key = $_PUT["key"];
+        if ($key == null || $key == false){
+            header("HTTP/1.1 400 API key not received.");
+            syslog(LOG_INFO, "[APIv3] HTTP/1.1 400 API key not received.");
+            return 400;
+        }
+
+        // Check data
+        $data = $_PUT["response"];
+        if ($data == null || $data == false){
+            header("HTTP/1.1 400 No data received.");
+            syslog(LOG_INFO, "[APIv3] HTTP/1.1 400 No data received.");
+            return 400;
+        }
+
+        // Check data format.
+        $json = json_decode($data);
+        if ($json === null){
+            header("HTTP/1.1 400 Data is no valid JSON.");
+            syslog(LOG_INFO, "[APIv3] HTTP/1.1 400 Data is no valid JSON.");
+            return 400;
+        }
+
+        // Get and validate command
+        $command = $json->{"command"};
+        $accepted_commands = [
+            "HubUserLogin", // Login, full profile
+            "GetUnitCollection", // Unit collection
+            "GetLobbyWizardLog" // Records
+        ];
+        if (!in_array($command, $accepted_commands)){
+            header("HTTP/1.1 405 Command not implemented.");
+            syslog(LOG_INFO, "[APIv3] HTTP/1.1 405 Command not implemented.");
+            return 405;
+        }
+
+        // Get user info and Authenticate
+        switch ($command){
+            case "HubUserLogin":
+                $uid = $json->{"wizard_id"};
+                $statement = $db->prepare("SELECT COUNT(uid) AS c FROM player WHERE uid = :uid AND api_key = :api_key;");
+                $statement->bindValue(":uid", $uid);
+                $statement->bindValue(":api_key", $key);
+                if (1 != $statement->execute()->fetchArray(SQLITE3_ASSOC)["c"]){
+                    header("HTTP/1.1 401 Invalid credentials.");
+                    syslog(LOG_INFO, "[APIv3] HTTP/1.1 401 Invalid credentials.");
+                    return 401;
+                }
+                break;
+            case "GetUnitCollection":
+                // No identification in this JSON, get uid from databases
+                $statement = $db->prepare("SELECT uid FROM player WHERE api_key = :api_key;");
+                $statement->bindValue(":api_key", $key);
+                $r = $statement->execute()->fetchArray(SQLITE3_ASSOC);
+                if ($r){
+                    $uid = $r["uid"];
+                }
+                else{
+                    header("HTTP/1.1 401 Invalid credentials.");
+                    syslog(LOG_INFO, "[APIv3] HTTP/1.1 401 Invalid credentials.");
+                    return 401;
+                }
+                break;
+            case "GetLobbyWizardLog":
+                $uid = $json->{"lobby_wizard_log"}->{"wizard_id"};
+                $statement = $db->prepare("SELECT COUNT(uid) AS c FROM player WHERE uid = :uid AND api_key = :api_key;");
+                $statement->bindValue(":uid", $uid);
+                $statement->bindValue(":api_key", $key);
+                if (1 != $statement->execute()->fetchArray(SQLITE3_ASSOC)["c"]){
+                    header("HTTP/1.1 401 Invalid credentials.");
+                    syslog(LOG_INFO, "[APIv3] HTTP/1.1 401 Invalid credentials.");
+                    return 401;
+                }
+                break;
+        }
+        if ($command == "GetUnitCollection"){ // This command doesnt provide wizard_id
+            $statement = $db->prepare("SELECT COUNT(uid) AS c FROM player WHERE uid = :uid AND api_key = :api_key;");
+            $statement->bindValue(":uid", $uid);
+            $statement->bindValue(":api_key", $key);
+            if (1 != $statement->execute()->fetchArray(SQLITE3_ASSOC)["c"]){
+                header("HTTP/1.1 401 Invalid credentials.");
+                syslog(LOG_INFO, "[APIv3] HTTP/1.1 401 Invalid credentials.");
+                return 401;
+            }
+        }
+
+        // Run parser
+        $result = "500 Unknown Error";
+        switch($command){
+            case "HubUserLogin":
+                $result = parse_profile($db, $uid, $json);
+                break;
+            case "GetUnitCollection":
+                $result = parse_collection($db, $uid, $json);
+                break;
+            case "GetLobbyWizardLog":
+                $result = parse_records($db, $uid, $json);
+                break;
+        }
+
+        // Process result
+        $status_code = intval(substr($result, 0, 3));
+        $status_message = substr($result, 4);
+        if ($status_code == 0 || $status_code < 100 || $status_code > 599){
+            header("HTTP/1.1 500 Unknown error.");
+            syslog(LOG_ERR, "[APIv3] HTTP/1.1 500 Unknown error.");
+            return 500;
+        }
+        else{
+            header("HTTP/1.1 $status_code $status_message");
+            syslog(LOG_INFO, "[APIv3] HTTP/1.1 $status_code $status_message");
+            return $status_code;
+        }
+    }
+    catch(Exception $e) {
+        header("HTTP/1.1 500 Unexpeced error: " . $e->getMessage());
+        syslog(LOG_INFO, "[APIv3] HTTP/1.1 500 Unexpected error: " . $e->getMessage());
+        return 500;
+    }
+?>

+ 1474 - 0
application/API/v3/run/POST.php

@@ -0,0 +1,1474 @@
+<?php
+    /**
+     * Run logger script.
+     *
+     * Exposes an API to log a run.
+     *
+     * Mandatory PUT parameters are:
+     *  - result_response: JSON received from game server on run end.
+     *  - key: User API key.
+     *
+     * Optional POST parameters are:
+     *  - result_request: JSON sent to game server on run end.
+     *  - start_response: JSON received from game server on run start.
+     *  - start_request: JSON sent to game server on run start.
+     *
+     * @category API
+     */
+
+    /**
+     * Calculates the currrent efficiency of a rune.
+     *
+     * @param JSON $rune JSON fragment of the rune.
+     * @return int Efficiency (0-100)
+     */
+    function calculate_efficiency($rune){
+        $efficiency = 0.0;
+        $max_efficiency = 0.0;
+        $substats = [];
+        $level = $rune->{"upgrade_curr"};
+        $stars = $rune->{"class"};
+        // Ancient stars are + 10
+        if ($stars > 10){
+            $stars -= 10;
+        }
+        $list = null;
+        // Main stat
+        $main_stat = $rune->{"pri_eff"}[0];
+        switch ($main_stat){
+            case RUNE_STAT_ID::HP:
+                $list = RUNE_STAT_VALUES::HP;
+                break;
+            case RUNE_STAT_ID::HP_P:
+                $list = RUNE_STAT_VALUES::HP_P;
+                break;
+            case RUNE_STAT_ID::ATK:
+                $list = RUNE_STAT_VALUES::ATK;
+                break;
+            case RUNE_STAT_ID::ATK_P:
+                $list = RUNE_STAT_VALUES::ATK_P;
+                break;
+            case RUNE_STAT_ID::DEF:
+                $list = RUNE_STAT_VALUES::DEF;
+                break;
+            case RUNE_STAT_ID::DEF_P:
+                $list = RUNE_STAT_VALUES::DEF_P;
+                break;
+            case RUNE_STAT_ID::SPD:
+                $list = RUNE_STAT_VALUES::SPD;
+                break;
+            case RUNE_STAT_ID::CRR:
+                $list = RUNE_STAT_VALUES::CRR;
+                break;
+            case RUNE_STAT_ID::CRD:
+                $list = RUNE_STAT_VALUES::CRD;
+                break;
+            case RUNE_STAT_ID::ACC:
+                $list = RUNE_STAT_VALUES::ACC;
+                break;
+            case RUNE_STAT_ID::RES:
+                $list = RUNE_STAT_VALUES::RES;
+                break;
+        }
+        $efficiency += floatval($list[$stars][$level]) / floatval($list[6][15]);
+        // Innate stat
+        if ($rune->{"prefix_eff"}[0] > 0){
+            $innate_stat = $rune->{"pri_eff"}[0];
+            $innate_stat_value = $rune->{"pri_eff"}[1];
+            $efficiency += floatval($innate_stat_value) / (5 * floatval(RUNE_SUBSTAT_MAX_VALUES::SUBSTAT[$innate_stat][6]));
+        }
+        // Substats
+        for ($i = 0; $i <= 3; $i ++){
+            if (sizeof($rune->{"sec_eff"}) > $i){
+                $substat = $rune->{"sec_eff"}[$i][0];
+                $substat_value = $rune->{"sec_eff"}[$i][1];
+                $efficiency += floatval($substat_value) / (5 * floatval(RUNE_SUBSTAT_MAX_VALUES::SUBSTAT[$substat][6]));
+            }
+        }
+        $efficiency = $efficiency / 2.8 * 100;
+        if ($efficiency > 100){
+            $efficiency = 100;
+        }
+        return $efficiency;
+    }
+
+    /**
+     * Calculates the maximum efficiency of a rune.
+     *
+     * @param JSON $rune JSON fragment of the rune.
+     * @return int Maximum efficiency (0-100)
+     */
+    function calculate_maximum_efficiency($rune){
+        $efficiency = 0.0;
+        $max_efficiency = 0.0;
+        $substats = [];
+        $stars = $rune->{"class"};
+        // Ancient stars are + 10
+        if ($stars > 10){
+            $stars -= 10;
+        }
+        $list = null;
+        // Main stat
+        $main_stat = $rune->{"pri_eff"}[0];
+        switch ($main_stat){
+            case RUNE_STAT_ID::HP:
+                $list = RUNE_STAT_VALUES::HP;
+                break;
+            case RUNE_STAT_ID::HP_P:
+                $list = RUNE_STAT_VALUES::HP_P;
+                break;
+            case RUNE_STAT_ID::ATK:
+                $list = RUNE_STAT_VALUES::ATK;
+                break;
+            case RUNE_STAT_ID::ATK_P:
+                $list = RUNE_STAT_VALUES::ATK_P;
+                break;
+            case RUNE_STAT_ID::DEF:
+                $list = RUNE_STAT_VALUES::DEF;
+                break;
+            case RUNE_STAT_ID::DEF_P:
+                $list = RUNE_STAT_VALUES::DEF_P;
+                break;
+            case RUNE_STAT_ID::SPD:
+                $list = RUNE_STAT_VALUES::SPD;
+                break;
+            case RUNE_STAT_ID::CRR:
+                $list = RUNE_STAT_VALUES::CRR;
+                break;
+            case RUNE_STAT_ID::CRD:
+                $list = RUNE_STAT_VALUES::CRD;
+                break;
+            case RUNE_STAT_ID::ACC:
+                $list = RUNE_STAT_VALUES::ACC;
+                break;
+            case RUNE_STAT_ID::RES:
+                $list = RUNE_STAT_VALUES::RES;
+                break;
+        }
+        $efficiency += floatval($list[$stars][15]) / floatval($list[6][15]);
+        // Innate stat
+        if ($rune->{"prefix_eff"}[0] > 0){
+            $innate_stat = $rune->{"pri_eff"}[0];
+            $innate_stat_value = $rune->{"pri_eff"}[1];
+            $efficiency += floatval($innate_stat_value) / (5 * floatval(RUNE_SUBSTAT_MAX_VALUES::SUBSTAT[$innate_stat][6]));
+        }
+        // Substats
+        for ($i = 0; $i <= 3; $i ++){
+            if (sizeof($rune->{"sec_eff"}) > $i){
+                $substat = $rune->{"sec_eff"}[$i][0];
+                $efficiency += floatval(RUNE_SUBSTAT_MAX_VALUES::SUBSTAT[$substat][$stars]) / (5 * floatval(RUNE_SUBSTAT_MAX_VALUES::SUBSTAT[$substat][6]));
+            }
+        }
+        $efficiency = $efficiency / 2.8 * 100;
+        if ($efficiency > 100){
+            $efficiency = 100;
+        }
+        return $efficiency;
+    }
+
+    /**
+     * Parses the JSONs relateds to an Arena run and saves the data.
+     *
+     * @param SQLiteConn $db Database connection.
+     * @param int $uid Player UID.
+     * @param int $id Run ID.
+     * @param JSON[] $json List of received JSONs.
+     */
+    function log_arena_run($db, $id, $uid, $json){
+        // Only the result request and response JSONs are needed:
+        $response = $json["result_res"];
+
+        // Prepare the "run" insert.
+        $statement = $db->prepare("
+          INSERT INTO run (
+            uid,
+            id,
+            dtime,
+            area_type,
+            area,
+            stage,
+            difficulty,
+            win,
+            time,
+            mana,
+            energy,
+            crystal,
+            guild_points,
+            honor_points,
+            helper,
+            score,
+            rank
+          ) VALUES (
+            :uid,
+            :id,
+            :dtime,
+            :area_type,
+            :area,
+            :stage,
+            :difficulty,
+            :win,
+            :time,
+            :mana,
+            :energy,
+            :crystal,
+            :guild_points,
+            :honor_points,
+            :helper,
+            :score,
+            :rank
+          );
+        ");
+
+        // Bind data and insert
+        $statement->bindValue(":uid", $uid);
+        $statement->bindValue(":id", $id);
+        $statement->bindValue(":dtime", $response->{"tvaluelocal"});
+        $statement->bindValue(":area_type", AREA_TYPE_ID::ARENA);
+        $statement->bindValue(":area", AREA_TYPE_ID::ARENA);
+        $statement->bindValue(":stage", null);
+        $statement->bindValue(":difficulty", null);
+        $statement->bindValue(":win", $response->{"win_lose"});
+        $statement->bindValue(":time", 1);
+        if (array_key_exists("mana", $response->{"reward"})){
+            $statement->bindValue(":mana", $response->{"reward"}->{"mana"});
+        }
+        else{
+            $statement->bindValue(":mana", 0);
+        }
+        if (array_key_exists("energy", $response->{"reward"})){
+            $statement->bindValue(":energy", $response->{"reward"}->{"energy"});
+        }
+        else{
+            $statement->bindValue(":energy", 0);
+        }
+        if (array_key_exists("crystal", $response->{"reward"})){
+            $statement->bindValue(":crystal", $response->{"reward"}->{"crystal"});
+        }
+        else{
+            $statement->bindValue(":crystal", 0);
+        }
+        $statement->bindValue(":guild_points", 0);
+        if (array_key_exists("honor_point", $response->{"reward"})){
+            $statement->bindValue(":honor_points", $response->{"reward"}->{"honor_point"});
+        }
+        else{
+            $statement->bindValue(":honor_points", 0);
+        }
+        $statement->bindValue(":helper", 0);
+        $statement->bindValue(":score", null);
+        $statement->bindValue(":rank", null);
+        $statement->execute();
+
+        // Parse party
+        $leader = 1;
+        foreach ($response->{"unit_list"} as $unit){
+            $statement = $db->prepare("
+              INSERT INTO run_party 
+                (run, unit, k_unit, leader, front)
+              VALUES
+                (:run, :unit, :k_unit, :leader, :front);"
+            );
+            $statement->bindValue(":run", $id);
+            $statement->bindValue(":unit", $unit->{"unit_id"});
+            $statement->bindValue(":k_unit", $unit->{"unit_master_id"});
+            $statement->bindValue(":leader", $leader);
+            $leader = 0;
+            $statement->bindValue(":front", 0);
+            $statement->execute();
+        }
+
+        // Return success
+        return "201 Created.";
+    }
+
+    /**
+     * Parses the JSONs relateds to a Dimensional Rift run and saves the data.
+     *
+     * @param SQLiteConn $db Database connection.
+     * @param int $uid Player UID.
+     * @param int $id Run ID.
+     * @param JSON[] $json List of received JSONs.
+     */
+    function log_dark_portal_run($db, $id, $uid, $json){
+        // Only the result request and response JSONs are needed:
+        $response = $json["result_res"];
+
+        // Prepare the "run" insert.
+        $statement = $db->prepare("
+          INSERT INTO run (
+            uid,
+            id,
+            dtime,
+            area_type,
+            area,
+            stage,
+            difficulty,
+            win,
+            time,
+            mana,
+            energy,
+            crystal,
+            guild_points,
+            honor_points,
+            helper,
+            score,
+            rank
+          ) VALUES (
+            :uid,
+            :id,
+            :dtime,
+            :area_type,
+            :area,
+            :stage,
+            :difficulty,
+            :win,
+            :time,
+            :mana,
+            :energy,
+            :crystal,
+            :guild_points,
+            :honor_points,
+            :helper,
+            :score,
+            :rank
+          );
+        ");
+
+        // Bind data and insert
+        $statement->bindValue(":uid", $uid);
+        $statement->bindValue(":id", $id);
+        $statement->bindValue(":dtime", $response->{"tvaluelocal"});
+        $statement->bindValue(":area_type", AREA_TYPE_ID::DIMENSIONAL_RIFT);
+        // The area is the region the portal is open in.
+        $statement->bindValue(":area", $response->{"region_id"});
+        $statement->bindValue(":stage", null);
+        $statement->bindValue(":difficulty", $response->{"difficulty"});
+        $statement->bindValue(":win", $response->{"win_lose"});
+        $statement->bindValue(":time", 1);
+        if (array_key_exists("mana", $response->{"reward"})){
+            $statement->bindValue(":mana", $response->{"reward"}->{"mana"});
+        }
+        else{
+            $statement->bindValue(":mana", 0);
+        }
+        if (array_key_exists("energy", $response->{"reward"})){
+            $statement->bindValue(":energy", $response->{"reward"}->{"energy"});
+        }
+        else{
+            $statement->bindValue(":energy", 0);
+        }
+        if (array_key_exists("crystal", $response->{"reward"})){
+            $statement->bindValue(":crystal", $response->{"reward"}->{"crystal"});
+        }
+        else{
+            $statement->bindValue(":crystal", 0);
+        }
+        $statement->bindValue(":guild_points", 0);
+        $statement->bindValue(":honor_points", 0);
+        $statement->bindValue(":helper", 0);
+        $statement->bindValue(":score", null);
+        $statement->bindValue(":rank", null);
+        $statement->execute();
+
+        // Parse party
+        $leader = 1;
+        foreach ($response->{"unit_list"} as $unit){
+            $statement = $db->prepare("
+              INSERT INTO run_party 
+                (run, unit, k_unit, leader, front)
+              VALUES
+                (:run, :unit, :k_unit, :leader, :front);"
+            );
+            $statement->bindValue(":run", $id);
+            $statement->bindValue(":unit", $unit->{"unit_id"});
+            $statement->bindValue(":k_unit", $unit->{"unit_master_id"});
+            $statement->bindValue(":leader", $leader);
+            $leader = 0;
+            $statement->bindValue(":front", 0);
+            $statement->execute();
+        }
+
+        // Return success
+        return "201 Created.";
+    }
+
+    /**
+     * Parses the JSONs relateds to a scenario run and saves the data.
+     *
+     * @param SQLiteConn $db Database connection.
+     * @param int $uid Player UID.
+     * @param int $id Run ID.
+     * @param JSON[] $json List of received JSONs.
+     */
+    function log_scenario_run($db, $id, $uid, $json){
+        // Only the result request and response JSONs are needed:
+        $response = $json["result_res"];
+
+        if ($json["start_res"] === null){
+            return ("400 Start response is required to parse scenario runs.");
+        }
+        $request = $json["start_res"];
+
+        // Prepare the "run" insert.
+        $statement = $db->prepare("
+          INSERT INTO run (
+            uid,
+            id,
+            dtime,
+            area_type,
+            area,
+            stage,
+            difficulty,
+            win,
+            time,
+            mana,
+            energy,
+            crystal,
+            guild_points,
+            honor_points,
+            helper,
+            score,
+            rank
+          ) VALUES (
+            :uid,
+            :id,
+            :dtime,
+            :area_type,
+            :area,
+            :stage,
+            :difficulty,
+            :win,
+            :time,
+            :mana,
+            :energy,
+            :crystal,
+            :guild_points,
+            :honor_points,
+            :helper,
+            :score,
+            :rank
+          );
+        ");
+
+        // Bind data and insert
+        $statement->bindValue(":uid", $uid);
+        $statement->bindValue(":id", $id);
+        $statement->bindValue(":dtime", $response->{"tvaluelocal"});
+        $statement->bindValue(":area_type", AREA_TYPE_ID::SCENARIO);
+        $statement->bindValue(":area", $response->{"scenario_info"}->{"region_id"});
+        $statement->bindValue(":stage", $response->{"scenario_info"}->{"stage_no"});
+        $statement->bindValue(":difficulty", $response->{"scenario_info"}->{"difficulty"});
+        $statement->bindValue(":win", $response->{"win_lose"});
+        $statement->bindValue(":time", $response->{"clear_time"}->{"current_time"});
+        if (array_key_exists("mana", $response->{"reward"})){
+            $statement->bindValue(":mana", $response->{"reward"}->{"mana"});
+        }
+        else{
+            $statement->bindValue(":mana", 0);
+        }
+        if (array_key_exists("energy", $response->{"reward"})){
+            $statement->bindValue(":energy", $response->{"reward"}->{"energy"});
+        }
+        else{
+            $statement->bindValue(":energy", 0);
+        }
+        if (array_key_exists("crystal", $response->{"reward"})){
+            $statement->bindValue(":crystal", $response->{"reward"}->{"crystal"});
+        }
+        else{
+            $statement->bindValue(":crystal", 0);
+        }
+        $statement->bindValue(":guild_points", 0);
+        $statement->bindValue(":honor_points", 0);
+        if (array_key_exists("helper_unit_list", $request) && sizeof($request->{"helper_unit_list"}) > 0){
+            $statement->bindValue(":helper", 1);
+        }
+        else{
+            $statement->bindValue(":helper", 0);
+        }
+        $statement->bindValue(":score", null);
+        $statement->bindValue(":rank", null);
+        $statement->execute();
+
+        // Parse various types of reward
+        if (array_key_exists("crate", $response->{"reward"})){
+            $crate = $response->{"reward"}->{"crate"};
+            if (array_key_exists("unit_info", $crate)){
+                $item = $crate->{"unit_info"};
+                $statement = $db->prepare("INSERT INTO run_drop_unit (run, unit) VALUES (:run, :unit);");
+                $statement->bindValue(":run", $id);
+                $statement->bindValue(":unit", $unit->{"unit_master_id"});
+                $statement->execute();
+            }
+            if (array_key_exists("craft_stuff", $crate)){
+                $item = $crate->{"craft_stuff"};
+                $statement = $db->prepare("INSERT INTO run_drop_item (run, item, amount) VALUES (:run, :item, :amount);");
+                $statement->bindValue(":run", $id);
+                $statement->bindValue(":item", $item->{"item_master_id"});
+                $statement->bindValue(":amount", $item->{"item_quantity"});
+                $statement->execute();
+            }
+            if (array_key_exists("random_scroll", $crate)){
+                $item = $crate->{"random_scroll"};
+                $statement = $db->prepare("INSERT INTO run_drop_item (run, item, amount) VALUES (:run, :item, :amount);");
+                $statement->bindValue(":run", $id);
+                $statement->bindValue(":item", $item->{"item_master_id"});
+                $statement->bindValue(":amount", $item->{"item_quantity"});
+                $statement->execute();
+            }
+            if (array_key_exists("rune", $crate)){
+                $rune = $crate->{"rune"};
+                $statement = $db->prepare("
+                    INSERT INTO run_drop_rune (
+                    run,
+                    id,
+                    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
+                    ) VALUES (
+                    :run,
+                    :id,
+                    :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
+                    );
+                ");
+                $statement->bindValue(":run", $id);
+                $statement->bindValue(":id", $rune->{"rune_id"});
+                $statement->bindValue(":type", $rune->{"set_id"});
+                $statement->bindValue(":slot", $rune->{"slot_no"});
+                $statement->bindValue(":value", $rune->{"sell_value"});
+                if ($rune->{"class"} > 10){
+                    // Ancient rune
+                    $statement->bindValue(":ancient", 1);
+                    $statement->bindValue(":stars", intval($rune->{"class"}) - 10);
+                    $statement->bindValue(":quality", intval($rune->{"rank"}) - 10);
+                }
+                else{
+                    // Non Ancient rune
+                    $statement->bindValue(":ancient", 0);
+                    $statement->bindValue(":stars", $rune->{"class"});
+                    $statement->bindValue(":quality", $rune->{"rank"});
+                }
+                $efficiency = calculate_efficiency($rune);
+                $max_efficiency = calculate_maximum_efficiency($rune);
+                $statement->bindValue(":efficiency", $efficiency);
+                $statement->bindValue(":max_efficiency", $max_efficiency);
+                $statement->bindValue(":main_stat", $rune->{"pri_eff"}[0]);
+                $statement->bindValue(":main_stat_value", $rune->{"pri_eff"}[1]);
+                if ($rune->{"prefix_eff"}[0] > 0){
+                    $statement->bindValue(":innate_stat", $rune->{"prefix_eff"}[0]);
+                    $statement->bindValue(":innate_stat_value", $rune->{"prefix_eff"}[1]);
+                }
+                else{
+                    $statement->bindValue(":innate_stat", null);
+                    $statement->bindValue(":innate_stat_value", 0);
+                }
+                if (sizeof($rune->{"sec_eff"}) > 0){
+                    $statement->bindValue(":substat_1", $rune->{"sec_eff"}[0][0]);
+                    $statement->bindValue(":substat_1_value", $rune->{"sec_eff"}[0][1]);
+                }
+                else{
+                    $statement->bindValue(":substat_1", null);
+                    $statement->bindValue(":substat_1_value", 0);
+                }
+                if (sizeof($rune->{"sec_eff"}) > 1){
+                    $statement->bindValue(":substat_2", $rune->{"sec_eff"}[1][0]);
+                    $statement->bindValue(":substat_2_value", $rune->{"sec_eff"}[1][1]);
+                }
+                else{
+                    $statement->bindValue(":substat_2", null);
+                    $statement->bindValue(":substat_2_value", 0);
+                }
+                if (sizeof($rune->{"sec_eff"}) > 2){
+                    $statement->bindValue(":substat_3", $rune->{"sec_eff"}[2][0]);
+                    $statement->bindValue(":substat_3_value", $rune->{"sec_eff"}[2][1]);
+                }
+                else{
+                    $statement->bindValue(":substat_3", null);
+                    $statement->bindValue(":substat_3_value", 0);
+                }
+                if (sizeof($rune->{"sec_eff"}) > 3){
+                    $statement->bindValue(":substat_4", $rune->{"sec_eff"}[3][0]);
+                    $statement->bindValue(":substat_4_value", $rune->{"sec_eff"}[3][1]);
+                }
+                else{
+                    $statement->bindValue(":substat_4", null);
+                    $statement->bindValue(":substat_4_value", 0);
+                }
+                $statement->execute();
+            }
+        }
+
+        // Parse party
+        $leader = 1;
+        foreach ($response->{"unit_list"} as $unit){
+            $statement = $db->prepare("
+              INSERT INTO run_party 
+                (run, unit, k_unit, leader, front)
+              VALUES
+                (:run, :unit, :k_unit, :leader, :front);"
+            );
+            $statement->bindValue(":run", $id);
+            $statement->bindValue(":unit", $unit->{"unit_id"});
+            $statement->bindValue(":k_unit", $unit->{"unit_master_id"});
+            $statement->bindValue(":leader", $leader);
+            $leader = 0;
+            $statement->bindValue(":front", 0);
+            $statement->execute();
+        }
+
+        // Return success
+        return "201 Created.";
+    }
+
+    /**
+     * Parses the JSONs relateds to a cairos run and saves the data.
+     *
+     * @param SQLiteConn $db Database connection.
+     * @param int $uid Player UID.
+     * @param int $id Run ID.
+     * @param JSON[] $json List of received JSONs.
+     */
+    function log_rift_raid_run($db, $id, $uid, $json){
+        // Only the start and result requests JSONs are needed:
+        $result = $json["result_res"];
+        if ($json["start_res"] === null){
+            return ("400 Start response is required to parse Rift Raid runs.");
+        }
+        $start = $json["start_res"];
+
+        // Prepare the "run" insert.
+        $statement = $db->prepare("
+          INSERT INTO run (
+            uid,
+            id,
+            dtime,
+            area_type,
+            area,
+            stage,
+            difficulty,
+            win,
+            time,
+            mana,
+            energy,
+            crystal,
+            guild_points,
+            honor_points,
+            helper,
+            score,
+            rank
+          ) VALUES (
+            :uid,
+            :id,
+            :dtime,
+            :area_type,
+            :area,
+            :stage,
+            :difficulty,
+            :win,
+            :time,
+            :mana,
+            :energy,
+            :crystal,
+            :guild_points,
+            :honor_points,
+            :helper,
+            :score,
+            :rank
+          );
+        ");
+
+        // Bind data and insert
+        $statement->bindValue(":uid", $uid);
+        $statement->bindValue(":id", $id);
+        $statement->bindValue(":dtime", $result->{"tvaluelocal"});
+        $statement->bindValue(":area_type", AREA_TYPE_ID::RIFT_RAID);
+        $statement->bindValue(":area", AREA_TYPE_ID::RIFT_RAID);
+        $statement->bindValue(":stage", $start->{"battle_info"}->{"stage_id"});
+        $statement->bindValue(":difficulty", null);
+        $statement->bindValue(":win", $result->{"win_lose"});
+        if ($result->{"win_lose"} == 1){
+            $statement->bindValue(":time", $result->{"clear_time"}->{"current_time"});
+        }
+        else{
+            $statement->bindValue(":time", 1);
+        }
+        $statement->bindValue(":mana", 0); // If reward is mana, i will be updated later
+        $statement->bindValue(":energy", 0);
+        $statement->bindValue(":crystal", 0);
+        $statement->bindValue(":guild_points", 0);
+        $statement->bindValue(":honor_points", 0);
+        $statement->bindValue(":helper", 0);
+        $statement->bindValue(":score", null);
+        $statement->bindValue(":rank", null);
+        $statement->execute();
+
+        // Parse various types of reward
+        if ($result->{"win_lose"} == 1 && array_key_exists("battle_reward_list", $result)){
+            foreach ($result->{"battle_reward_list"} as $reward_list){
+                if ($reward_list->{"wizard_id"} == $uid){
+                    $reward = $reward_list->{"reward_list"}[0];
+                    $reward_type = $reward->{"item_master_type"};
+                    switch ($reward_type){
+                        case INVENTORY_TYPE_ID::MONSTER: // Unit
+                            $statement = $db->prepare("INSERT INTO run_drop_unit (run, unit) VALUES (:run, :unit);");
+                            $statement->bindValue(":run", $id);
+                            $statement->bindValue(":unit", $reward->{"item_master_id"});
+                            $statement->execute();
+                            break;
+                        case INVENTORY_TYPE_ID::SCROLL:
+                        case INVENTORY_TYPE_ID::ESSENCES:
+                            $statement = $db->prepare("INSERT INTO run_drop_item (run, item, amount) VALUES (:run, :item, :amount);");
+                            $statement->bindValue(":run", $id);
+                            $statement->bindValue(":item", $reward->{"item_master_id"});
+                            $statement->bindValue(":amount", $reward->{"item_quantity"});
+                            $statement->execute();
+                            break;
+                        case INVENTORY_TYPE_ID::ENCHANTMENT:
+                            $statement = $db->prepare("
+                              INSERT INTO run_drop_enchantment (run, id, type, quality, rune, stat, value) 
+                              VALUES (:run, :id, :type, :quality, :rune, :stat, :value);
+                            ");
+                            $statement->bindValue(":run", $id);
+                            $statement->bindValue(":id", $reward->{"runecraft_item_id"});
+                            $statement->bindValue(":type", $reward->{"runecraft_type"});
+                            $statement->bindValue(":quality", $reward->{"runecraft_rank"});
+                            $statement->bindValue(":rune", $reward->{"runecraft_set_id"});
+                            $statement->bindValue(":stat", $reward->{"runecraft_effect_id"});
+                            $statement->bindValue(":value", 0);
+                            $statement->execute();
+                            break;
+                        case 102: //: Mana
+                            $statement = $db->prepare("UPDATE run SET mana = :mana WHERE run = :run;");
+                            $statement->bindValue(":run", $id);
+                            $statement->bindValue(":id", $reward->{"runecraft_item_id"});
+                            $statement->execute();
+                            break;
+                    }
+                }
+            }
+        }
+
+        // Parse party
+        foreach ($start->{"battle_info"}->{"user_list"} as $user){
+            if ($user->{"wizard_id"} == $uid){
+                foreach ($user->{"deck_list"} as $unit){
+                    $statement = $db->prepare("
+                      INSERT INTO run_party 
+                        (run, unit, k_unit, leader, front)
+                      VALUES
+                        (:run, :unit, :k_unit, :leader, :front);"
+                    );
+                    $statement->bindValue(":run", $id);
+                    $statement->bindValue(":unit", $unit->{"unit_info"}->{"unit_id"});
+                    $statement->bindValue(":k_unit", $unit->{"unit_info"}->{"unit_master_id"});
+                    $statement->bindValue(":leader", $unit->{"leader"});
+                    if (intval($unit->{"index"}) <= 4){
+                        $statement->bindValue(":front", 1);
+                    }
+                    else{
+                        $statement->bindValue(":front", 0);
+                    }
+                    $statement->execute();
+                }
+            }
+        }
+
+        // Return success
+        return "201 Created.";
+    }
+
+    /**
+     * Parses the JSONs relateds to a cairos run and saves the data.
+     *
+     * @param SQLiteConn $db Database connection.
+     * @param int $uid Player UID.
+     * @param int $id Run ID.
+     * @param JSON[] $json List of received JSONs.
+     */
+    function log_cairos_run($db, $id, $uid, $json){
+        // Only the result request and response JSONs are needed:
+        $response = $json["result_res"];
+        if ($json["result_req"] === null){
+            return ("400 Result request is required to parse Cairos runs.");
+        }
+        $request = $json["result_req"];
+
+        // Prepare the "run" insert.
+        $statement = $db->prepare("
+          INSERT INTO run (
+            uid,
+            id,
+            dtime,
+            area_type,
+            area,
+            stage,
+            difficulty,
+            win,
+            time,
+            mana,
+            energy,
+            crystal,
+            guild_points,
+            honor_points,
+            helper,
+            score,
+            rank
+          ) VALUES (
+            :uid,
+            :id,
+            :dtime,
+            :area_type,
+            :area,
+            :stage,
+            :difficulty,
+            :win,
+            :time,
+            :mana,
+            :energy,
+            :crystal,
+            :guild_points,
+            :honor_points,
+            :helper,
+            :score,
+            :rank
+          );
+        ");
+
+        // Bind data and insert
+        $statement->bindValue(":uid", $uid);
+        $statement->bindValue(":id", $id);
+        $statement->bindValue(":dtime", $response->{"tvaluelocal"});
+        $statement->bindValue(":area_type", AREA_TYPE_ID::CAIROS_DUNGEON);
+        $statement->bindValue(":area", $request->{"dungeon_id"});
+        $statement->bindValue(":stage", $request->{"stage_id"});
+        $statement->bindValue(":difficulty", null);
+        $statement->bindValue(":win", $response->{"win_lose"});
+        $statement->bindValue(":time", $response->{"clear_time"}->{"current_time"});
+        if (array_key_exists("mana", $response->{"reward"})){
+            $statement->bindValue(":mana", $response->{"reward"}->{"mana"});
+        }
+        else{
+            $statement->bindValue(":mana", 0);
+        }
+        if (array_key_exists("energy", $response->{"reward"})){
+            $statement->bindValue(":energy", $response->{"reward"}->{"energy"});
+        }
+        else{
+            $statement->bindValue(":energy", 0);
+        }
+        if (array_key_exists("crystal", $response->{"reward"})){
+            $statement->bindValue(":crystal", $response->{"reward"}->{"crystal"});
+        }
+        else{
+            $statement->bindValue(":crystal", 0);
+        }
+        $statement->bindValue(":guild_points", 0);
+        $statement->bindValue(":honor_points", 0);
+        $statement->bindValue(":helper", 0); // TODO Where is this information??
+        $statement->bindValue(":score", null);
+        $statement->bindValue(":rank", null);
+        $statement->execute();
+
+        // Parse various types of reward
+        if (array_key_exists("changed_item_list", $response)){
+            $reward_type = $response->{"changed_item_list"}[0]->{"type"};
+            switch ($reward_type){
+                case INVENTORY_TYPE_ID::MONSTER: // Unit
+                    $statement = $db->prepare("INSERT INTO run_drop_unit (run, unit) VALUES (:run, :unit);");
+                    $statement->bindValue(":run", $id);
+                    $statement->bindValue(":unit", $response->{"changed_item_list"}[0]->{"view"}->{"item_master_id"});
+                    $statement->execute();
+                    break;
+                case INVENTORY_TYPE_ID::SCROLL:
+                case INVENTORY_TYPE_ID::ESSENCES:
+                    $statement = $db->prepare("INSERT INTO run_drop_item (run, item, amount) VALUES (:run, :item, :amount);");
+                    $statement->bindValue(":run", $id);
+                    $statement->bindValue(":item", $response->{"changed_item_list"}[0]->{"view"}->{"item_master_id"});
+                    $statement->bindValue(":amount", $response->{"changed_item_list"}[0]->{"view"}->{"item_quantity"});
+                    $statement->execute();
+                    break;
+                case INVENTORY_TYPE_ID::RUNE:
+                    $rune = $response->{"changed_item_list"}[0]->{"info"};
+                    $statement = $db->prepare("
+                      INSERT INTO run_drop_rune (
+                        run,
+                        id,
+                        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
+                      ) VALUES (
+                        :run,
+                        :id,
+                        :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
+                      );
+                    ");
+                    $statement->bindValue(":run", $id);
+                    $statement->bindValue(":id", $rune->{"rune_id"});
+                    $statement->bindValue(":type", $rune->{"set_id"});
+                    $statement->bindValue(":slot", $rune->{"slot_no"});
+                    $statement->bindValue(":value", $rune->{"sell_value"});
+                    if ($rune->{"class"} > 10){
+                        // Ancient rune
+                        $statement->bindValue(":ancient", 1);
+                        $statement->bindValue(":stars", intval($rune->{"class"}) - 10);
+                        $statement->bindValue(":quality", intval($rune->{"rank"}) - 10);
+                    }
+                    else{
+                        // Non Ancient rune
+                        $statement->bindValue(":ancient", 0);
+                        $statement->bindValue(":stars", $rune->{"class"});
+                        $statement->bindValue(":quality", $rune->{"rank"});
+                    }
+                    $efficiency = calculate_efficiency($rune);
+                    $max_efficiency = calculate_maximum_efficiency($rune);
+                    $statement->bindValue(":efficiency", $efficiency);
+                    $statement->bindValue(":max_efficiency", $max_efficiency);
+                    $statement->bindValue(":main_stat", $rune->{"pri_eff"}[0]);
+                    $statement->bindValue(":main_stat_value", $rune->{"pri_eff"}[1]);
+                    if ($rune->{"prefix_eff"}[0] > 0){
+                        $statement->bindValue(":innate_stat", $rune->{"prefix_eff"}[0]);
+                        $statement->bindValue(":innate_stat_value", $rune->{"prefix_eff"}[1]);
+                    }
+                    else{
+                        $statement->bindValue(":innate_stat", null);
+                        $statement->bindValue(":innate_stat_value", 0);
+                    }
+                    if (sizeof($rune->{"sec_eff"}) > 0){
+                        $statement->bindValue(":substat_1", $rune->{"sec_eff"}[0][0]);
+                        $statement->bindValue(":substat_1_value", $rune->{"sec_eff"}[0][1]);
+                    }
+                    else{
+                        $statement->bindValue(":substat_1", null);
+                        $statement->bindValue(":substat_1_value", 0);
+                    }
+                    if (sizeof($rune->{"sec_eff"}) > 1){
+                        $statement->bindValue(":substat_2", $rune->{"sec_eff"}[1][0]);
+                        $statement->bindValue(":substat_2_value", $rune->{"sec_eff"}[1][1]);
+                    }
+                    else{
+                        $statement->bindValue(":substat_2", null);
+                        $statement->bindValue(":substat_2_value", 0);
+                    }
+                    if (sizeof($rune->{"sec_eff"}) > 2){
+                        $statement->bindValue(":substat_3", $rune->{"sec_eff"}[2][0]);
+                        $statement->bindValue(":substat_3_value", $rune->{"sec_eff"}[2][1]);
+                    }
+                    else{
+                        $statement->bindValue(":substat_3", null);
+                        $statement->bindValue(":substat_3_value", 0);
+                    }
+                    if (sizeof($rune->{"sec_eff"}) > 3){
+                        $statement->bindValue(":substat_4", $rune->{"sec_eff"}[3][0]);
+                        $statement->bindValue(":substat_4_value", $rune->{"sec_eff"}[3][1]);
+                    }
+                    else{
+                        $statement->bindValue(":substat_4", null);
+                        $statement->bindValue(":substat_4_value", 0);
+                    }
+                    $statement->execute();
+                    break;
+            }
+        }
+
+        // Parse party
+        $leader = 1;
+        foreach ($response->{"unit_list"} as $unit){
+            $statement = $db->prepare("
+              INSERT INTO run_party 
+                (run, unit, k_unit, leader, front)
+              VALUES
+                (:run, :unit, :k_unit, :leader, :front);"
+            );
+            $statement->bindValue(":run", $id);
+            $statement->bindValue(":unit", $unit->{"unit_id"});
+            $statement->bindValue(":k_unit", $unit->{"unit_master_id"});
+            $statement->bindValue(":leader", $leader);
+            $leader = 0;
+            $statement->bindValue(":front", 0);
+            $statement->execute();
+        }
+
+        // Return success
+        return "201 Created.";
+    }
+
+    /**
+     * Parses the JSONs relateds to a Dimension Hole run and saves the data.
+     *
+     * @param SQLiteConn $db Database connection.
+     * @param int $uid Player UID.
+     * @param int $id Run ID.
+     * @param JSON[] $json List of received JSONs.
+     */
+    function log_dimension_hole_run($db, $id, $uid, $json){
+        // Only the result request and response JSONs are needed:
+        $response = $json["result_res"];
+        if ($json["result_req"] === null){
+            return ("400 Result request is required to parse Cairos runs.");
+        }
+        $request = $json["result_req"];
+
+        // Prepare the "run" insert.
+        $statement = $db->prepare("
+          INSERT INTO run (
+            uid,
+            id,
+            dtime,
+            area_type,
+            area,
+            stage,
+            difficulty,
+            win,
+            time,
+            mana,
+            energy,
+            crystal,
+            guild_points,
+            honor_points,
+            helper,
+            score,
+            rank
+          ) VALUES (
+            :uid,
+            :id,
+            :dtime,
+            :area_type,
+            :area,
+            :stage,
+            :difficulty,
+            :win,
+            :time,
+            :mana,
+            :energy,
+            :crystal,
+            :guild_points,
+            :honor_points,
+            :helper,
+            :score,
+            :rank
+          );
+        ");
+
+        // Bind data and insert
+        $statement->bindValue(":uid", $uid);
+        $statement->bindValue(":id", $id);
+        $statement->bindValue(":dtime", $response->{"tvaluelocal"});
+        $statement->bindValue(":area_type", AREA_TYPE_ID::DIMENSIONAL_HOLE);
+        $statement->bindValue(":area", $response->{"dungeon_id"});
+        $statement->bindValue(":stage", $response->{"difficulty"}); // Difficulty is stage
+        $statement->bindValue(":difficulty", null);
+        $statement->bindValue(":win", $response->{"win_lose"});
+        $statement->bindValue(":time", $response->{"clear_time"}->{"current_time"});
+        if (array_key_exists("mana", $response->{"reward"})){
+            $statement->bindValue(":mana", $response->{"reward"}->{"mana"});
+        }
+        else{
+            $statement->bindValue(":mana", 0);
+        }
+        if (array_key_exists("energy", $response->{"reward"})){
+            $statement->bindValue(":energy", $response->{"reward"}->{"energy"});
+        }
+        $statement->bindValue(":energy", 0);
+        if (array_key_exists("crystal", $response->{"reward"})){
+            $statement->bindValue(":crystal", $response->{"reward"}->{"crystal"});
+        }
+        else{
+            $statement->bindValue(":crystal", 0);
+        }
+        $statement->bindValue(":guild_points", 0);
+        $statement->bindValue(":honor_points", 0);
+        $statement->bindValue(":helper", 0); // TODO Where is this information??
+        $statement->bindValue(":score", null);
+        $statement->bindValue(":rank", null);
+        $statement->execute();
+
+        // Parse various types of reward
+        if (array_key_exists("changed_item_list", $response)){
+            foreach ($response->{"changed_item_list"} as $drop){
+                $reward_type = $drop->{"type"};
+                switch ($reward_type){
+                    case INVENTORY_TYPE_ID::CRAFT_STUFF:
+                        $statement = $db->prepare("INSERT INTO run_drop_item (run, item, amount) VALUES (:run, :item, :amount);");
+                        $statement->bindValue(":run", $id);
+                        $statement->bindValue(":item", $drop->{"view"}->{"item_master_id"});
+                        $statement->bindValue(":amount", $drop->{"view"}->{"item_quantity"});
+                        $statement->execute();
+                        break;
+                    case INVENTORY_TYPE_ID::RUNE:
+                        // TODO: Investigate with no awaken dungeons
+                        /*$rune = $response->{"changed_item_list"}[0]->{"info"};
+                        $statement = $db->prepare("
+                          INSERT INTO run_drop_rune (
+                            run,
+                            id,
+                            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
+                          ) VALUES (
+                            :run,
+                            :id,
+                            :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
+                        );
+                        ");
+                        $statement->bindValue(":run", $id);
+                        $statement->bindValue(":id", $rune->{"rune_id"});
+                        $statement->bindValue(":type", $rune->{"set_id"});
+                        $statement->bindValue(":slot", $rune->{"slot_no"});
+                        $statement->bindValue(":value", $rune->{"sell_value"});
+                        if ($rune->{"class"} > 10){
+                            // Ancient rune
+                            $statement->bindValue(":ancient", 1);
+                            $statement->bindValue(":stars", intval($rune->{"class"}) - 10);
+                            $statement->bindValue(":quality", intval($rune->{"rank"}) - 10);
+                        }
+                        else{
+                            // Non Ancient rune
+                            $statement->bindValue(":ancient", 0);
+                            $statement->bindValue(":stars", $rune->{"class"});
+                            $statement->bindValue(":quality", $rune->{"rank"});
+                        }
+                        $efficiency = calculate_efficiency($rune);
+                        $max_efficiency = calculate_maximum_efficiency($rune);
+                        $statement->bindValue(":efficiency", $efficiency);
+                        $statement->bindValue(":max_efficiency", $max_efficiency);
+                        $statement->bindValue(":main_stat", $rune->{"pri_eff"}[0]);
+                        $statement->bindValue(":main_stat_value", $rune->{"pri_eff"}[1]);
+                        if ($rune->{"prefix_eff"}[0] > 0){
+                            $statement->bindValue(":innate_stat", $rune->{"prefix_eff"}[0]);
+                            $statement->bindValue(":innate_stat_value", $rune->{"prefix_eff"}[1]);
+                        }
+                        else{
+                            $statement->bindValue(":innate_stat", null);
+                            $statement->bindValue(":innate_stat_value", 0);
+                        }
+                        if (sizeof($rune->{"sec_eff"}) > 0){
+                            $statement->bindValue(":substat_1", $rune->{"sec_eff"}[0][0]);
+                            $statement->bindValue(":substat_1_value", $rune->{"sec_eff"}[0][1]);
+                        }
+                        else{
+                            $statement->bindValue(":substat_1", null);
+                            $statement->bindValue(":substat_1_value", 0);
+                        }
+                        if (sizeof($rune->{"sec_eff"}) > 1){
+                            $statement->bindValue(":substat_2", $rune->{"sec_eff"}[1][0]);
+                            $statement->bindValue(":substat_2_value", $rune->{"sec_eff"}[1][1]);
+                        }
+                        else{
+                            $statement->bindValue(":substat_2", null);
+                            $statement->bindValue(":substat_2_value", 0);
+                        }
+                        if (sizeof($rune->{"sec_eff"}) > 2){
+                            $statement->bindValue(":substat_3", $rune->{"sec_eff"}[2][0]);
+                            $statement->bindValue(":substat_3_value", $rune->{"sec_eff"}[2][1]);
+                        }
+                        else{
+                            $statement->bindValue(":substat_3", null);
+                            $statement->bindValue(":substat_3_value", 0);
+                        }
+                        if (sizeof($rune->{"sec_eff"}) > 3){
+                            $statement->bindValue(":substat_4", $rune->{"sec_eff"}[3][0]);
+                            $statement->bindValue(":substat_4_value", $rune->{"sec_eff"}[3][1]);
+                        }
+                        else{
+                            $statement->bindValue(":substat_4", null);
+                            $statement->bindValue(":substat_4_value", 0);
+                        }
+                        $statement->execute();*/
+                        break;
+                }
+            }
+        }
+
+        // Parse party
+        $leader = 1;
+        foreach ($response->{"unit_list"} as $unit){
+            $statement = $db->prepare("
+              INSERT INTO run_party 
+                (run, unit, k_unit, leader, front)
+              VALUES
+                (:run, :unit, :k_unit, :leader, :front);"
+            );
+            $statement->bindValue(":run", $id);
+            $statement->bindValue(":unit", $unit->{"unit_id"});
+            $statement->bindValue(":k_unit", $unit->{"unit_master_id"});
+            $statement->bindValue(":leader", $leader);
+            $leader = 0;
+            $statement->bindValue(":front", 0);
+            $statement->execute();
+        }
+
+        // Return success
+        return "201 Created.";
+    }
+
+    global $db;
+
+    try{
+
+        header("Content-type: application/json; charset=utf-8");
+
+        // Get put data
+        parse_str(file_get_contents("php://input"), $_POST);
+
+        // Check API key.
+        if (!array_key_exists("key", $_POST)){
+            header("HTTP/1.1 400 API key not received.");
+            syslog(LOG_INFO, "[APIv3] HTTP/1.1 400 API key not received.");
+            return 400;
+        }
+        $key = $_POST["key"];
+
+        // Get all JSON files
+        $json = [
+            "start_req" => null,
+            "start_res" => null,
+            "result_req" => null,
+            "result_res" => null,
+        ];
+
+        // Result response - mandatory
+        if (!array_key_exists("result_response", $_POST)){
+            header("HTTP/1.1 400 No result response data received.");
+            syslog(LOG_INFO, "[APIv3] HTTP/1.1 400 No result response data received.");
+            return 400;
+        }
+        $data_json = json_decode($_POST["result_response"]);
+        if ($data_json === null){
+            header("HTTP/1.1 400 Result response data is no valid JSON.");
+            syslog(LOG_INFO, "[APIv3] HTTP/1.1 400 Result response data is no valid JSON.");
+            return 400;
+        }
+        $json["result_res"] = $data_json;
+
+        // Result request - optional
+        if (array_key_exists("result_request", $_POST)){
+            $data_raw = $_POST["result_request"];
+            if ($data_raw != null && $data_raw != false){
+                $data_json = json_decode($data_raw);
+                if ($data_json != null){
+                    $json["result_req"] = $data_json;
+                }
+            }
+        }
+        // Start result - optional
+        if (array_key_exists("start_response", $_POST)){
+            $data_raw = $_POST["start_response"];
+            if ($data_raw != null && $data_raw != false){
+                $data_json = json_decode($data_raw);
+                if ($data_json != null){
+                    $json["start_res"] = $data_json;
+                }
+            }
+        }
+        // Start result - optional
+        if (array_key_exists("start_request", $_POST)){
+            $data_raw = $_POST["start_request"];
+            if ($data_raw != null && $data_raw != false){
+                $data_json = json_decode($data_raw);
+                if ($data_json != null){
+                    $json["start_req"] = $data_json;
+                }
+            }
+        }
+
+        // Extract basic data
+        $uid = $json["result_res"]->{"wizard_info"}->{"wizard_id"};
+        $command = $json["result_res"]->{"command"};
+
+        // Authenticate
+        $statement = $db->prepare("SELECT COUNT(uid) AS c FROM player WHERE uid = :uid AND api_key = :api_key;");
+        $statement->bindValue(":uid", $uid);
+        $statement->bindValue(":api_key", $key);
+        if (1 != $statement->execute()->fetchArray(SQLITE3_ASSOC)["c"]){
+            header("HTTP/1.1 401 Invalid credentials.");
+            syslog(LOG_INFO, "[APIv3] HTTP/1.1 401 Invalid credentials.");
+            return 401;
+        }
+
+        // Check for implemented command
+        $accepted_commands = [
+            "BattleDungeonResult_V2", // Dungeon Run
+            "BattleDimensionHoleDungeonResult_v2", // Dimension Hole Run
+            "BattleRiftOfWorldsRaidResult", // Rift Raid Run
+            "BattleScenarioResult", // Scenario Run
+            "BattleArenaResult", // Arena Run
+            "BattleDarkPortalResult" // Dimensinal Rift Run
+        ];
+        if (!in_array($command, $accepted_commands)){
+            header("HTTP/1.1 405 Command not implemented.");
+            syslog(LOG_INFO, "[APIv3] HTTP/1.1 405 Command not implemented.");
+            return 405;
+        }
+
+        // Check if already in DB
+        $dtime = $json["result_res"]->{"tvaluelocal"};
+        $statement = $db->prepare("SELECT count(id) AS c FROM run WHERE uid = :uid AND dtime = :dtime;");
+        $statement->bindValue(":uid", $uid);
+        $statement->bindValue(":dtime", $dtime);
+        if (0 != $statement->execute()->fetchArray(SQLITE3_ASSOC)["c"]){
+            header("HTTP/1.1 409 Run already in database.");
+            syslog(LOG_INFO, "[APIv3] HTTP/1.1 409 Run already in database.");
+            return 409;
+        }
+
+        // Get new ID
+        $q = $db->query("SELECT max(id) + 1 AS id FROM run;");
+        $r = $q->fetchArray(SQLITE3_ASSOC);
+        if ($r){
+            $id = $r["id"];
+        }
+        else{
+            $id = 1;
+        }
+
+        // Run parsing function
+        $result = "500 Unknown Error";
+        switch($command){
+            case "BattleDungeonResult_V2":
+                $result = log_cairos_run($db, $id, $uid, $json);
+                break;
+            case "BattleDimensionHoleDungeonResult_v2":
+                $result = log_dimension_hole_run($db, $id, $uid, $json);
+                break;
+            case "BattleRiftOfWorldsRaidResult":
+                $result = log_rift_raid_run($db, $id, $uid, $json);
+                break;
+            case "BattleScenarioResult":
+                $result = log_scenario_run($db, $id, $uid, $json);
+                break;
+            case "BattleArenaResult":
+                $result = log_arena_run($db, $id, $uid, $json);
+                break;
+            case "BattleDarkPortalResult":
+                $result = log_dark_portal_run($db, $id, $uid, $json);
+                break;
+        }
+
+        // Process result
+        $status_code = intval(substr($result, 0, 3));
+        $status_message = substr($result, 4);
+        if ($status_code == 0 || $status_code < 100 || $status_code > 599){
+            header("HTTP/1.1 500 Unknown error.");
+            syslog(LOG_ERR, "[APIv3] HTTP/1.1 500 Unknown error.");
+            return 500;
+        }
+        else{
+            header("HTTP/1.1 $status_code $status_message");
+            syslog(LOG_INFO, "[APIv3] HTTP/1.1 $status_code $status_message");
+            return $status_code;
+        }
+    }
+    catch(Exception $e) {
+        header("HTTP/1.1 500 Unexpeced error: " . $e->getMessage());
+        syslog(LOG_ERR, "[APIv3] HTTP/1.1 500 Unexpected error: " . $e->getMessage());
+        return 500;
+    }
+?>

+ 108 - 0
application/API/v3/units.php

@@ -0,0 +1,108 @@
+<?php
+    /**
+     * Unit api.
+     *
+     * Exposes an API to get a list of units of a player, or a unit details.
+     * Used POST parameters are:
+     *  - key: User API key. If not, only public user's unit can be seen.
+     *
+     * @category API
+     * @magic $params URL parameters:
+     *  1: User ID
+     *  2: Unit ID (optional)
+     */
+
+    global $db;
+
+    try{
+
+        if (count($params) <= 1){
+            http_response_code(400);
+            return 400;
+        }
+        $uid = $params[1];
+        $id = null;
+
+        if (count($params) > 2){
+            $id = $params[2];
+        }
+
+        // Check API key or public profile.
+        if (isset($_POST['key'])){
+            $key = filter_input(INPUT_POST, 'key');
+        }
+        else{
+            $key = "";
+        }
+        $s = "SELECT COUNT(uid) AS c FROM player WHERE uid = $uid AND (public = 1 OR api_key = '$key');";
+        if (1 != $db->query($s)->fetchArray(SQLITE3_ASSOC)["c"]){
+            http_response_code(401);
+            return 401;
+        }
+
+        // No unit ID specified, show a list.
+        if ($id == null){
+            $units = [];
+            $s = "SELECT id, '" . URL::BASE . "API/v2/units/$uid/' || id AS uri FROM unit WHERE uid = '$uid'";
+            $q = $db->query($s);
+            $content = false;
+            while ($r = $q->fetchArray(SQLITE3_ASSOC)){
+                array_push($units, $r);
+                $content = true;
+            }
+            if ($content == true){
+                header('Content-Type: application/json');
+                echo json_encode($units);
+                http_response_code(200);
+                return 200;
+            }
+            else{
+                http_response_code(204);
+                return 204;
+            }
+        }
+
+        // Specific unit, show details.
+        else{
+            $s = "SELECT * FROM unit WHERE uid = '$uid' AND id = '$id'";
+            $q = $db->query($s);
+            if($r = $q->fetchArray(SQLITE3_ASSOC)){
+                $unit = $r;
+
+                // Runes
+                $unit["runes"] = [];
+                $s = "SELECT * FROM rune WHERE assigned_to = '" . $unit["id"] . "' ORDER BY slot;";
+                $q = $db->query($s);
+                while ($r = $q->fetchArray(SQLITE3_ASSOC)){
+                    $rune = $r;
+                    unset($rune["uid"]); # Not needed
+                    unset($rune["assigned_to"]); # Not needed
+                    array_push($unit["runes"], $rune);
+                }
+
+                // Skills
+                $unit["skills"] = [];
+                $s = "SELECT skill, level FROM unit_skill WHERE unit = '" . $unit["id"] . "';";
+                $q = $db->query($s);
+                while ($r = $q->fetchArray(SQLITE3_ASSOC)){
+                    array_push($unit["skills"], $r);
+                }
+
+                header('Content-Type: application/json');
+                echo json_encode($unit);
+                http_response_code(200);
+                return 200;
+            }
+            else{
+                http_response_code(404);
+                return 404;
+            }
+        }
+
+    }
+    catch(Exception $e) {
+        error_log("Unknown error fetching units: " . $e->getMessage());
+        http_response_code(500);
+        return 500;
+    }
+?>

+ 455 - 0
application/API/v3/units/GET.php

@@ -0,0 +1,455 @@
+<?php
+    /**
+     * Unit getter script.
+     *
+     * Exposes an API to rerieve unit info.
+     *
+     * Optional query parameters are:
+     *  - id: Unit ID.
+     *
+     * @category API
+     */
+
+    global $db;
+
+    /**
+     * Gets info on a unit.
+     *
+     * @param SQLiteConn $db Database connection.
+     * @param int $id Unit identifier
+     * @return mixed[] Content.
+     */
+    function get_unit($db, $id){
+        $result = [
+            "status" => 404,
+            "message" => "Unit not found",
+            "content" => ""
+        ];
+        $statement = $db->prepare("
+          SELECT
+            k_unit.id AS id,
+            k_unit.name AS name,
+            k_unit.family AS family,
+            k_unit.element AS element,
+            k_element.name AS element_name,
+            k_unit.archetype AS archetype,
+            k_archetype.name AS archetype_name,
+            k_unit.base_stars AS base_stars,
+            k_unit.natural_stars AS natural_stars,
+            k_unit.obtainable AS obtainable,
+            k_unit.awakens_from AS awakens_from,
+            k_unit.awakens_to AS awakens_to,
+            k_unit.homunculus AS homunculus,
+            k_unit.hp AS hp,
+            k_unit.attack AS attack,
+            k_unit.defense AS defense,
+            k_unit.speed AS speed,
+            k_unit.crit_rate AS crit_rate,
+            k_unit.crit_damage AS crit_damage,
+            k_unit.accuracy AS accuracy,
+            k_unit.resistance AS resistance,
+            k_unit.leader_skill AS leader_skill
+          FROM
+            k_unit,
+            k_element,
+            k_archetype
+          WHERE
+            k_unit.element = k_element.id AND
+            k_unit.archetype = k_archetype.id AND
+            k_unit.id = :id ;
+        ");
+        $statement->bindValue(":id", $id);
+        $q = $statement->execute();
+        $r = $q->fetchArray(SQLITE3_ASSOC);
+        if ($r){
+            $unit = [
+                "id" => $r["id"],
+                "url" => URL::BASE . "API/v3/units/" . $r["id"] . "/",
+                "name" => $r["name"],
+                "family" => $r["family"],
+                "image" => APPLICATION::img("UNIT", $r["id"]),
+                "element" => $r["element"],
+                "element_name" => $r["element_name"],
+                "archetype" => $r["archetype"],
+                "archetype_name" => $r["archetype_name"],
+                "stars" => $r["base_stars"],
+                "natural_stars" => $r["natural_stars"],
+                "obtainable" => (($r["obtainable"] == 1) ? (true) : (false)),
+                "awakens_from" => $r["awakens_from"],
+                "awakens_to" => $r["awakens_to"],
+                "homunculus" => (($r["homunculus"] == 1) ? (true) : (false)),
+                "hp" => $r["hp"],
+                "attack" => $r["attack"],
+                "defense" => $r["defense"],
+                "speed" => $r["speed"],
+                "critical_rate" => $r["crit_rate"],
+                "critical_damage" => $r["crit_damage"],
+                "accuracy" => $r["accuracy"],
+                "resistance" => $r["resistance"],
+                "leader_skill" => null,
+                "skills" => [],
+            ];
+
+            // Leader_skill
+            if ($r["leader_skill"] != null){
+                $statement_leader = $db->prepare("
+                SELECT
+                    k_leader_skill.id AS id,
+                    k_leader_skill.stat AS stat,
+                    k_stat.name AS stat_name,
+                    k_leader_skill.amount AS amount,
+                    k_leader_skill.area AS area,
+                    k_effect_area.name AS area_name,
+                    k_leader_skill.element AS element,
+                    k_element.name AS element_name
+                FROM
+                    k_leader_skill,
+                    k_stat,
+                    k_effect_area,
+                    k_element
+                WHERE
+                    k_leader_skill.id = :id AND
+                    k_leader_skill.stat = k_stat.id AND
+                    k_leader_skill.area = k_effect_area.id AND
+                    k_leader_skill.element = k_element.id;
+                ");
+                $statement_leader->bindValue(":id", $r["leader_skill"]);
+                $q_leader = $statement_leader->execute();
+                $r_leader = $q_leader->fetchArray(SQLITE3_ASSOC);
+                if ($r_leader){
+                    $description = "Increases the ";
+                    switch ($r_leader["stat"]){
+                        case STAT_ID::ATK:
+                            $description .= "attack power";
+                            break;
+                        case STAT_ID::DEF:
+                            $description .= "defense";
+                            break;
+                        case STAT_ID::HP:
+                            $description .= "HP";
+                            break;
+                        case STAT_ID::SPD:
+                            $description .= "attack speed";
+                            break;
+                        case STAT_ID::CRIT_RATE:
+                            $description .= "critical rate";
+                            break;
+                        case STAT_ID::CRIT_DMG:
+                            $description .= "critical damage";
+                            break;
+                        case STAT_ID::ACCURACY:
+                            $description .= "accuracy";
+                            break;
+                        case STAT_ID::RESIST:
+                            $description .= "resistance";
+                            break;
+                    }
+                    $description .= " of ally monsters ";
+                    switch ($r_leader["element"]){
+                        case ELEMENT_ID::WATER:
+                            $description .= " with Water attribute ";
+                            break;
+                        case ELEMENT_ID::FIRE:
+                            $description .= " with Fire attribute ";
+                            break;
+                        case ELEMENT_ID::WIND:
+                            $description .= " with Wind attribute ";
+                            break;
+                        case ELEMENT_ID::LIGHT:
+                            $description .= " with Light attribute ";
+                            break;
+                        case ELEMENT_ID::DARK:
+                            $description .= " with Dark attribute ";
+                            break;
+                    }
+                    $description = $description . "by " . $r_leader["amount"] . "%";
+                    switch ($r_leader["area"]){
+                        case EFFECT_AREA_ID::DUNGEON:
+                            $description .= " in the Dungeons";
+                            break;
+                        case EFFECT_AREA_ID::ARENA:
+                            $description .= " in the Arena";
+                            break;
+                        case EFFECT_AREA_ID::GUILD:
+                            $description .= " in Guild content";
+                            break;
+                    }
+                    $description = str_replace("  ", " ", $description) . ".";
+                    $unit["leader_skill"] = [
+                        "id" => $r_leader["id"],
+                        "stat" => $r_leader["stat"],
+                        "stat_name" => $r_leader["stat_name"],
+                        "increase" => $r_leader["amount"],
+                        "area" => $r_leader["area"],
+                        "area_name" => $r_leader["area_name"],
+                        "element" => $r_leader["element"],
+                        "element_name" => $r_leader["element_name"],
+                        "description" => $description
+                    ];
+                }
+            }
+
+            // Skill
+            $statement_skills = $db->prepare("
+              SELECT
+                k_skill.id AS id,
+                k_skill.slot AS slot,
+                k_skill.name AS name,
+                k_skill.description AS description,
+                k_skill.passive AS passive,
+                k_skill.cooltime AS cooltime,
+                k_skill.hits AS hits,
+                k_skill.aoe AS aoe,
+                k_skill.multiplier_formula AS multiplier_formula,
+                k_skill.max_level AS max_level
+              FROM
+                k_skill,
+                k_unit_skill
+              WHERE
+                k_skill.id = k_unit_skill.skill AND
+                k_unit_skill.unit = :id 
+              ORDER BY slot;
+            ");
+            $statement_skills->bindValue(":id", $id);
+            $q_skills = $statement_skills->execute();
+            while ($r_skills = $q_skills->fetchArray(SQLITE3_ASSOC)){
+                $skill = [
+                    "id" => $r_skills["id"],
+                    "slot" => $r_skills["slot"],
+                    "name" => $r_skills["name"],
+                    "description" => $r_skills["description"],
+                    "image" => APPLICATION::img("SKILL", $r_skills["id"]),
+                    "passive" => (($r_skills["passive"] == 1) ? (true) : (false)),
+                    "cooltime" => $r_skills["cooltime"],
+                    "hits" => $r_skills["hits"],
+                    "aoe" => (($r_skills["aoe"] == 1) ? (true) : (false)),
+                    "effects" => [],
+                    "max_level" => $r_skills["max_level"],
+                    "upgrades" => [],
+                    "formula" => $r_skills["multiplier_formula"]
+                ];
+                // Skill levels
+                $statement_skill_levels = $db->prepare("
+                  SELECT
+                    level,
+                    description
+                  FROM k_skill_level
+                  WHERE
+                    level > 1 AND
+                    skill = :id
+                  ORDER BY level;
+                ");
+                $statement_skill_levels->bindValue(":id", $r_skills["id"]);
+                $q_skill_levels = $statement_skill_levels->execute();
+                while ($r_skill_levels = $q_skill_levels->fetchArray(SQLITE3_ASSOC)){
+                    $level = [
+                        "level" => $r_skill_levels["level"],
+                        "description" => $r_skill_levels["description"]
+                    ];
+                    array_push($skill["upgrades"], $level);
+                }
+                // Skill efffects
+                $statement_skill_effects = $db->prepare("
+                  SELECT
+                    k_effect.id AS id,
+                    k_effect.is_buff AS is_buff,
+                    k_effect.name AS name,
+                    k_effect.description AS description
+                  FROM
+                    k_effect,
+                    k_skill_effect
+                  WHERE
+                    k_skill_effect.skill = :id AND
+                    k_skill_effect.effect = k_effect.id
+                ");
+                $statement_skill_effects->bindValue(":id", $r_skills["id"]);
+                $q_skill_effects = $statement_skill_effects->execute();
+                while ($r_skill_effects = $q_skill_effects->fetchArray(SQLITE3_ASSOC)){
+                    $effect = [
+                        "id" => $r_skill_effects["id"],
+                        "buff" => (($r_skill_effects["is_buff"] == 1) ? (true) : (false)),
+                        "name" => $r_skill_effects["name"],
+                        "description" => $r_skill_effects["description"],
+                        "image" => APPLICATION::img("EFFECT", $r_skill_effects["id"]),
+                    ];
+                    array_push($skill["effects"], $effect);
+                }
+                array_push($unit["skills"], $skill);
+            }
+
+            // Add to array
+            $result["content"] = $unit;
+            $result["status"] = 200;
+            $result["message"] = "Success.";
+        }
+        return $result;
+    }
+
+    /**
+     * Gets a list of units, paginated.
+     *
+     * @param SQLiteConn $db Database connection.
+     * @return mixed[] Content.
+     */
+    function get_unit_list($db){
+        $result = [
+            "status" => 200,
+            "message" => "Success.",
+            "content" => ""
+        ];
+
+        // Get filters
+        $parameters = "";
+        $filters = [
+            "page" => 1,
+            "per_page" => 30,
+            "name" => "",
+            "element" => "",
+            "archetype" => "",
+            "stars" => "",
+            "stars_lte" => "",
+            "stars_gte" => "",
+        ];
+
+        foreach (array_keys($filters) as $filter_key){
+            $filter = filter_input(INPUT_GET, $filter_key);
+            if ($filter != null && $filter !== false){
+                $filters[$filter_key] = $filter;
+                // Also, build the new url parameters
+                if ($filter_ley != "page"){
+                    $parameters .= ("&" . $filter_key . "=" . $filter);
+                }
+            }
+        }
+
+        // Fix some filters
+        if (intval($filters["page"]) < 1){
+            $filters["page"] = 1;
+        }
+        if (intval($filters["per_page"]) < 1){
+            $filters["per_page"] = 1;
+        }
+        elseif (intval($filters["per_page"]) > 100){
+            $filters["per_page"] = 100;
+        }
+
+        // Build the where clause
+        $where = "
+          k_unit.element = k_element.id AND
+          k_unit.archetype = k_archetype.id
+        ";
+        if (strlen($filters["name"]) > 0){
+            $where .= " AND upper(k_unit.name) LIKE upper(:name) ";
+        }
+        if (strlen($filters["element"]) > 0){
+            $where .= " AND (k_element.id = :element OR upper(k_element.name) = upper(:element)) ";
+        }
+        if (strlen($filters["archetype"]) > 0){
+            $where .= " AND (k_archetype.id = :archetype OR upper(k_archetype.name) = upper(:archetype)) ";
+        }
+        if (strlen($filters["stars"]) > 0){
+            $where .= " AND k_unit.base_stars = :stars ";
+        }
+        if (strlen($filters["stars_lte"]) > 0){
+            $where .= " AND k_unit.base_stars <= :stars_lte ";
+        }
+        if (strlen($filters["stars_gte"]) > 0){
+            $where .= " AND k_unit.base_stars >= :stars_gte ";
+        }
+
+        // Get total
+        $statement = $db->prepare("
+          SELECT count(k_unit.id) AS c
+          FROM
+            k_unit,
+            k_element,
+            k_archetype
+          WHERE $where ;
+        ");
+        $statement->bindValue(":name", "%" . $filters["name"] . "%");
+        $statement->bindValue(":element", $filters["element"]);
+        $statement->bindValue(":archetype", $filters["archetype"]);
+        $statement->bindValue(":stars", $filters["stars"]);
+        $statement->bindValue(":stars_gte", $filters["stars_gte"]);
+        $statement->bindValue(":stars_lte", $filters["stars_lte"]);
+        $total = $statement->execute()->fetchArray(SQLITE3_ASSOC)["c"];
+
+        // Selet all units
+        $statement = $db->prepare("
+          SELECT k_unit.id AS id
+          FROM
+            k_unit,
+            k_element,
+            k_archetype
+          WHERE $where
+          LIMIT :limit
+          OFFSET :offset ;
+        ");
+        $statement->bindValue(":name", "%" . $filters["name"] . "%");
+        $statement->bindValue(":element", $filters["element"]);
+        $statement->bindValue(":archetype", $filters["archetype"]);
+        $statement->bindValue(":stars", $filters["stars"]);
+        $statement->bindValue(":stars_gte", $filters["stars_gte"]);
+        $statement->bindValue(":stars_lte", $filters["stars_lte"]);
+        $statement->bindValue(":limit", $filters["per_page"]);
+        $statement->bindValue(":offset", $filters["per_page"] * ($filters["page"] - 1));
+        $q = $statement->execute();
+        $units = [];
+
+        // Loop and get unit info
+        while ($r = $q->fetchArray(SQLITE3_ASSOC)){
+            $unit = get_unit($db, $r["id"]);
+            if ($unit["status"] == 200){
+                array_push($units, $unit["content"]);
+            }
+        };
+
+        // Calculate previous and next pages
+        if ($filters["page"] == 1){
+            $url_prev = null;
+        }
+        else{
+            $url_prev = URL::BASE . "API/v3/units/?page=" . ($filters["page"] - 1) . $parameters;
+        }
+        if ($filters["page"] < ceil($total / $filters["per_page"])){
+            $url_next = URL::BASE . "API/v3/units/?page=" . ($filters["page"] + 1) . $parameters;
+        }
+        else{
+            $url_next = null;
+        }
+        $content = [
+            "count" => $total,
+            "next" => $url_next,
+            "previous" => $url_prev,
+            "results" => $units
+        ];
+        $result["content"] = $content;
+        return $result;
+    }
+
+    try{
+        header("Content-type: application/json; charset=utf-8");
+        $result = [
+            "status" => 200,
+            "message" => "Success.",
+            "content" => ""
+        ];
+        // Get profile ID
+        // $query heredated from API_CONTROLLER
+        if (strlen($query[0]) > 0){
+            $id = $query[0];
+            $result = get_unit($db, $id);
+        }
+        else{
+            $result = get_unit_list($db);
+        }
+        header("HTTP/1.1 " . $result["status"] . " " . $result["message"]);
+        echo(json_encode($result["content"]));
+        syslog(LOG_ERR, "HTTP/1.1 " . $result["status"] . " " . $result["message"]);
+        return $result["status"];
+    }
+    catch(Exception $e) {
+        header("HTTP/1.1 500 Unexpeced error: " . $e->getMessage());
+        syslog(LOG_ERR, "[APIv3] HTTP/1.1 500 Unexpected error: " . $e->getMessage());
+        return 500;
+    }

+ 1357 - 2
application/Constant.php

@@ -1019,9 +1019,9 @@
         const RAINBOWMON = 25;   # Possibly material monsters in general. Appears when wish returns a rainbowmon.
 
         /**
-         * Rune Craft.
+         * Enchantment.
          */
-        const RUNE_CRAFT = 27;
+        const ENCHANTMENT = 27;
 
         /**
          * Craft Stuff.
@@ -2763,6 +2763,1361 @@
         ];
     };
 
+    /**
+     * Rune main stat values constants.
+     *
+     * Includes the value of the main stats for type, stars, and level.
+     *
+     * @category Data
+     */
+    final class RUNE_SUBSTAT_MAX_VALUES{
+    
+        const SUBSTAT = [
+            RUNE_STAT_ID::HP => [
+                "1" => 300,
+                "2" => 525,
+                "3" => 825,
+                "4" => 1125,
+                "5" => 1500,
+                "6" => 1875
+            ],
+            RUNE_STAT_ID::HP_P => [
+                "1" => 10,
+                "2" => 15,
+                "3" => 25,
+                "4" => 30,
+                "5" => 35,
+                "6" => 40
+            ],
+            RUNE_STAT_ID::ATK => [
+                "1" => 20,
+                "2" => 25,
+                "3" => 40,
+                "4" => 50,
+                "5" => 75,
+                "6" => 100
+            ],
+            RUNE_STAT_ID::ATK_P => [
+                "1" => 10,
+                "2" => 15,
+                "3" => 25,
+                "4" => 30,
+                "5" => 35,
+                "6" => 40
+            ],
+            RUNE_STAT_ID::DEF => [
+                "1" => 20,
+                "2" => 25,
+                "3" => 40,
+                "4" => 50,
+                "5" => 75,
+                "6" => 100
+            ],
+            RUNE_STAT_ID::DEF_P => [
+                "1" => 10,
+                "2" => 15,
+                "3" => 25,
+                "4" => 30,
+                "5" => 35,
+                "6" => 40
+            ],
+            RUNE_STAT_ID::SPD => [
+                "1" => 5,
+                "2" => 10,
+                "3" => 15,
+                "4" => 20,
+                "5" => 25,
+                "6" => 30
+            ],
+            RUNE_STAT_ID::CRR => [
+                "1" => 5,
+                "2" => 10,
+                "3" => 15,
+                "4" => 20,
+                "5" => 25,
+                "6" => 30
+            ],
+            RUNE_STAT_ID::CRD => [
+                "1" => 10,
+                "2" => 15,
+                "3" => 20,
+                "4" => 25,
+                "5" => 30,
+                "6" => 35
+            ],
+            RUNE_STAT_ID::RES => [
+                "1" => 10,
+                "2" => 15,
+                "3" => 25,
+                "4" => 30,
+                "5" => 35,
+                "6" => 40
+            ],
+            RUNE_STAT_ID::ACC => [
+                "1" => 10,
+                "2" => 15,
+                "3" => 25,
+                "4" => 30,
+                "5" => 35,
+                "6" => 40
+            ]
+        ];
+
+        /**
+         * Values of the HP main stat [stars][level]
+         */
+        const HP = [
+            "1" => [
+                "0" => 40,
+                "1" => 85,
+                "2" => 130,
+                "3" => 175,
+                "4" => 220,
+                "5" => 265,
+                "6" => 310,
+                "7" => 355,
+                "8" => 400,
+                "9" => 445,
+                "10" => 490,
+                "11" => 535,
+                "12" => 580,
+                "13" => 625,
+                "14" => 670,
+                "15" => 804
+            ],
+            "2" => [
+                "0" => 70,
+                "1" => 130,
+                "2" => 190,
+                "3" => 250,
+                "4" => 310,
+                "5" => 370,
+                "6" => 430,
+                "7" => 490,
+                "8" => 550,
+                "9" => 610,
+                "10" => 670,
+                "11" => 730,
+                "12" => 790,
+                "13" => 850,
+                "14" => 910,
+                "15" => 1092
+            ],
+            "3" => [
+                "0" => 100,
+                "1" => 175,
+                "2" => 250,
+                "3" => 325,
+                "4" => 400,
+                "5" => 475,
+                "6" => 550,
+                "7" => 625,
+                "8" => 700,
+                "9" => 775,
+                "10" => 850,
+                "11" => 925,
+                "12" => 1000,
+                "13" => 1075,
+                "14" => 1150,
+                "15" => 1380
+            ],
+            "4" => [
+                "0" => 160,
+                "1" => 250,
+                "2" => 340,
+                "3" => 430,
+                "4" => 520,
+                "5" => 610,
+                "6" => 700,
+                "7" => 790,
+                "8" => 880,
+                "9" => 970,
+                "10" => 1060,
+                "11" => 1150,
+                "12" => 1240,
+                "13" => 1330,
+                "14" => 1420,
+                "15" => 1704
+            ],
+            "5" => [
+                "0" => 270,
+                "1" => 375,
+                "2" => 480,
+                "3" => 585,
+                "4" => 690,
+                "5" => 795,
+                "6" => 900,
+                "7" => 1005,
+                "8" => 1110,
+                "9" => 1215,
+                "10" => 1320,
+                "11" => 1425,
+                "12" => 1530,
+                "13" => 1635,
+                "14" => 1740,
+                "15" => 2088
+            ],
+            "6" => [
+                "0" => 360,
+                "1" => 480,
+                "2" => 600,
+                "3" => 720,
+                "4" => 840,
+                "5" => 960,
+                "6" => 1080,
+                "7" => 1200,
+                "8" => 1320,
+                "9" => 1440,
+                "10" => 1560,
+                "11" => 1680,
+                "12" => 1800,
+                "13" => 1920,
+                "14" => 2040,
+                "15" => 2448
+            ]
+        ];
+        
+        /**
+         * Values of the HP_P main stat[stars][level]
+         */
+        const HP_P = [
+            "1" => [
+                "0" => 1,
+                "1" => 2,
+                "2" => 3,
+                "3" => 4,
+                "4" => 5,
+                "5" => 6,
+                "6" => 7,
+                "7" => 8,
+                "8" => 9,
+                "9" => 10,
+                "10" => 11,
+                "11" => 12,
+                "12" => 13,
+                "13" => 14,
+                "14" => 15,
+                "15" => 18
+            ],
+            "2" => [
+                "0" => 2,
+                "1" => 3,
+                "2" => 4,
+                "3" => 5,
+                "4" => 6,
+                "5" => 7,
+                "6" => 8,
+                "7" => 9,
+                "8" => 10,
+                "9" => 11,
+                "10" => 12,
+                "11" => 13,
+                "12" => 14,
+                "13" => 15,
+                "14" => 16,
+                "15" => 19
+            ],
+            "3" => [
+                "0" => 4,
+                "1" => 6,
+                "2" => 8,
+                "3" => 10,
+                "4" => 12,
+                "5" => 14,
+                "6" => 16,
+                "7" => 18,
+                "8" => 20,
+                "9" => 22,
+                "10" => 24,
+                "11" => 26,
+                "12" => 28,
+                "13" => 30,
+                "14" => 32,
+                "15" => 38
+            ],
+            "4" => [
+                "0" => 5,
+                "1" => 7,
+                "2" => 9,
+                "3" => 11,
+                "4" => 13,
+                "5" => 16,
+                "6" => 18,
+                "7" => 20,
+                "8" => 22,
+                "9" => 24,
+                "10" => 27,
+                "11" => 29,
+                "12" => 31,
+                "13" => 33,
+                "14" => 36,
+                "15" => 43
+            ],
+            "5" => [
+                "0" => 8,
+                "1" => 10,
+                "2" => 12,
+                "3" => 15,
+                "4" => 17,
+                "5" => 20,
+                "6" => 22,
+                "7" => 24,
+                "8" => 27,
+                "9" => 29,
+                "10" => 32,
+                "11" => 34,
+                "12" => 37,
+                "13" => 40,
+                "14" => 43,
+                "15" => 51
+            ],
+            "6" => [
+                "0" => 11,
+                "1" => 14,
+                "2" => 17,
+                "3" => 20,
+                "4" => 23,
+                "5" => 26,
+                "6" => 29,
+                "7" => 32,
+                "8" => 35,
+                "9" => 38,
+                "10" => 41,
+                "11" => 44,
+                "12" => 47,
+                "13" => 50,
+                "14" => 53,
+                "15" => 63
+            ]
+        ];
+
+        /**
+         * Values of the ATK main stat[stars][level]
+         */
+        const ATK = [
+            "1" => [
+                "0" => 3,
+                "1" => 6,
+                "2" => 9,
+                "3" => 12,
+                "4" => 15,
+                "5" => 18,
+                "6" => 21,
+                "7" => 24,
+                "8" => 27,
+                "9" => 30,
+                "10" => 33,
+                "11" => 36,
+                "12" => 39,
+                "13" => 42,
+                "14" => 45,
+                "15" => 54
+            ],
+            "2" => [
+                "0" => 5,
+                "1" => 9,
+                "2" => 13,
+                "3" => 17,
+                "4" => 21,
+                "5" => 25,
+                "6" => 29,
+                "7" => 33,
+                "8" => 37,
+                "9" => 41,
+                "10" => 45,
+                "11" => 49,
+                "12" => 53,
+                "13" => 57,
+                "14" => 61,
+                "15" => 73
+            ],
+            "3" => [
+                "0" => 7,
+                "1" => 12,
+                "2" => 17,
+                "3" => 22,
+                "4" => 27,
+                "5" => 32,
+                "6" => 37,
+                "7" => 42,
+                "8" => 47,
+                "9" => 52,
+                "10" => 57,
+                "11" => 62,
+                "12" => 67,
+                "13" => 72,
+                "14" => 77,
+                "15" => 92
+            ],
+            "4" => [
+                "0" => 10,
+                "1" => 16,
+                "2" => 22,
+                "3" => 28,
+                "4" => 34,
+                "5" => 40,
+                "6" => 46,
+                "7" => 52,
+                "8" => 58,
+                "9" => 64,
+                "10" => 70,
+                "11" => 76,
+                "12" => 82,
+                "13" => 88,
+                "14" => 94,
+                "15" => 112
+            ],
+            "5" => [
+                "0" => 15,
+                "1" => 22,
+                "2" => 29,
+                "3" => 36,
+                "4" => 43,
+                "5" => 50,
+                "6" => 57,
+                "7" => 64,
+                "8" => 71,
+                "9" => 78,
+                "10" => 85,
+                "11" => 92,
+                "12" => 99,
+                "13" => 106,
+                "14" => 113,
+                "15" => 135
+            ],
+            "6" => [
+                "0" => 22,
+                "1" => 30,
+                "2" => 38,
+                "3" => 46,
+                "4" => 54,
+                "5" => 62,
+                "6" => 70,
+                "7" => 78,
+                "8" => 86,
+                "9" => 94,
+                "10" => 102,
+                "11" => 110,
+                "12" => 118,
+                "13" => 126,
+                "14" => 134,
+                "15" => 160
+            ]
+        ];
+
+        /**
+         * Values of the ATK_P main stat[stars][level]
+         */
+        const ATK_P = [
+            "1" => [
+                "0" => 1,
+                "1" => 2,
+                "2" => 3,
+                "3" => 4,
+                "4" => 5,
+                "5" => 6,
+                "6" => 7,
+                "7" => 8,
+                "8" => 9,
+                "9" => 10,
+                "10" => 11,
+                "11" => 12,
+                "12" => 13,
+                "13" => 14,
+                "14" => 15,
+                "15" => 18
+            ],
+            "2" => [
+                "0" => 2,
+                "1" => 3,
+                "2" => 4,
+                "3" => 5,
+                "4" => 6,
+                "5" => 7,
+                "6" => 8,
+                "7" => 9,
+                "8" => 10,
+                "9" => 11,
+                "10" => 12,
+                "11" => 13,
+                "12" => 14,
+                "13" => 15,
+                "14" => 16,
+                "15" => 19
+            ],
+            "3" => [
+                "0" => 4,
+                "1" => 6,
+                "2" => 8,
+                "3" => 10,
+                "4" => 12,
+                "5" => 14,
+                "6" => 16,
+                "7" => 18,
+                "8" => 20,
+                "9" => 22,
+                "10" => 24,
+                "11" => 26,
+                "12" => 28,
+                "13" => 30,
+                "14" => 32,
+                "15" => 38
+            ],
+            "4" => [
+                "0" => 5,
+                "1" => 7,
+                "2" => 9,
+                "3" => 11,
+                "4" => 13,
+                "5" => 16,
+                "6" => 18,
+                "7" => 20,
+                "8" => 22,
+                "9" => 24,
+                "10" => 27,
+                "11" => 29,
+                "12" => 31,
+                "13" => 33,
+                "14" => 36,
+                "15" => 43
+            ],
+            "5" => [
+                "0" => 8,
+                "1" => 10,
+                "2" => 12,
+                "3" => 15,
+                "4" => 17,
+                "5" => 20,
+                "6" => 22,
+                "7" => 24,
+                "8" => 27,
+                "9" => 29,
+                "10" => 32,
+                "11" => 34,
+                "12" => 37,
+                "13" => 40,
+                "14" => 43,
+                "15" => 51
+            ],
+            "6" => [
+                "0" => 11,
+                "1" => 14,
+                "2" => 17,
+                "3" => 20,
+                "4" => 23,
+                "5" => 26,
+                "6" => 29,
+                "7" => 32,
+                "8" => 35,
+                "9" => 38,
+                "10" => 41,
+                "11" => 44,
+                "12" => 47,
+                "13" => 50,
+                "14" => 53,
+                "15" => 63
+            ]
+        ];
+        
+        /**
+         * Values of the DEF main stat[stars][level]
+         */
+        const DEF = [
+            "1" => [
+                "0" => 3,
+                "1" => 6,
+                "2" => 9,
+                "3" => 12,
+                "4" => 15,
+                "5" => 18,
+                "6" => 21,
+                "7" => 24,
+                "8" => 27,
+                "9" => 30,
+                "10" => 33,
+                "11" => 36,
+                "12" => 39,
+                "13" => 42,
+                "14" => 45,
+                "15" => 54
+            ],
+            "2" => [
+                "0" => 5,
+                "1" => 9,
+                "2" => 13,
+                "3" => 17,
+                "4" => 21,
+                "5" => 25,
+                "6" => 29,
+                "7" => 33,
+                "8" => 37,
+                "9" => 41,
+                "10" => 45,
+                "11" => 49,
+                "12" => 53,
+                "13" => 57,
+                "14" => 61,
+                "15" => 73
+            ],
+            "3" => [
+                "0" => 7,
+                "1" => 12,
+                "2" => 17,
+                "3" => 22,
+                "4" => 27,
+                "5" => 32,
+                "6" => 37,
+                "7" => 42,
+                "8" => 47,
+                "9" => 52,
+                "10" => 57,
+                "11" => 62,
+                "12" => 67,
+                "13" => 72,
+                "14" => 77,
+                "15" => 92
+            ],
+            "4" => [
+                "0" => 10,
+                "1" => 16,
+                "2" => 22,
+                "3" => 28,
+                "4" => 34,
+                "5" => 40,
+                "6" => 46,
+                "7" => 52,
+                "8" => 58,
+                "9" => 64,
+                "10" => 70,
+                "11" => 76,
+                "12" => 82,
+                "13" => 88,
+                "14" => 94,
+                "15" => 112
+            ],
+            "5" => [
+                "0" => 15,
+                "1" => 22,
+                "2" => 29,
+                "3" => 36,
+                "4" => 43,
+                "5" => 50,
+                "6" => 57,
+                "7" => 64,
+                "8" => 71,
+                "9" => 78,
+                "10" => 85,
+                "11" => 92,
+                "12" => 99,
+                "13" => 106,
+                "14" => 113,
+                "15" => 135
+            ],
+            "6" => [
+                "0" => 22,
+                "1" => 30,
+                "2" => 38,
+                "3" => 46,
+                "4" => 54,
+                "5" => 62,
+                "6" => 70,
+                "7" => 78,
+                "8" => 86,
+                "9" => 94,
+                "10" => 102,
+                "11" => 110,
+                "12" => 118,
+                "13" => 126,
+                "14" => 134,
+                "15" => 160
+            ]
+        ];
+        
+        /**
+         * Values of the DEF_P main stat[stars][level]
+         */
+        const DEF_P = [
+            "1" => [
+                "0" => 1,
+                "1" => 2,
+                "2" => 3,
+                "3" => 4,
+                "4" => 5,
+                "5" => 6,
+                "6" => 7,
+                "7" => 8,
+                "8" => 9,
+                "9" => 10,
+                "10" => 11,
+                "11" => 12,
+                "12" => 13,
+                "13" => 14,
+                "14" => 15,
+                "15" => 18
+            ],
+            "2" => [
+                "0" => 2,
+                "1" => 3,
+                "2" => 4,
+                "3" => 5,
+                "4" => 6,
+                "5" => 7,
+                "6" => 8,
+                "7" => 9,
+                "8" => 10,
+                "9" => 11,
+                "10" => 12,
+                "11" => 13,
+                "12" => 14,
+                "13" => 15,
+                "14" => 16,
+                "15" => 19
+            ],
+            "3" => [
+                "0" => 4,
+                "1" => 6,
+                "2" => 8,
+                "3" => 10,
+                "4" => 12,
+                "5" => 14,
+                "6" => 16,
+                "7" => 18,
+                "8" => 20,
+                "9" => 22,
+                "10" => 24,
+                "11" => 26,
+                "12" => 28,
+                "13" => 30,
+                "14" => 32,
+                "15" => 38
+            ],
+            "4" => [
+                "0" => 5,
+                "1" => 7,
+                "2" => 9,
+                "3" => 11,
+                "4" => 13,
+                "5" => 16,
+                "6" => 18,
+                "7" => 20,
+                "8" => 22,
+                "9" => 24,
+                "10" => 27,
+                "11" => 29,
+                "12" => 31,
+                "13" => 33,
+                "14" => 36,
+                "15" => 43
+            ],
+            "5" => [
+                "0" => 8,
+                "1" => 10,
+                "2" => 12,
+                "3" => 15,
+                "4" => 17,
+                "5" => 20,
+                "6" => 22,
+                "7" => 24,
+                "8" => 27,
+                "9" => 29,
+                "10" => 32,
+                "11" => 34,
+                "12" => 37,
+                "13" => 40,
+                "14" => 43,
+                "15" => 51
+            ],
+            "6" => [
+                "0" => 11,
+                "1" => 14,
+                "2" => 17,
+                "3" => 20,
+                "4" => 23,
+                "5" => 26,
+                "6" => 29,
+                "7" => 32,
+                "8" => 35,
+                "9" => 38,
+                "10" => 41,
+                "11" => 44,
+                "12" => 47,
+                "13" => 50,
+                "14" => 53,
+                "15" => 63
+            ]
+        ];
+
+        /**
+         * Values of the SPD main stat[stars][level]
+         */
+        const SPD = [
+            "1" => [
+                "0" => 1,
+                "1" => 2,
+                "2" => 3,
+                "3" => 4,
+                "4" => 5,
+                "5" => 6,
+                "6" => 7,
+                "7" => 8,
+                "8" => 9,
+                "9" => 10,
+                "10" => 11,
+                "11" => 12,
+                "12" => 13,
+                "13" => 14,
+                "14" => 15,
+                "15" => 18
+            ],
+            "2" => [
+                "0" => 2,
+                "1" => 3,
+                "2" => 4,
+                "3" => 5,
+                "4" => 6,
+                "5" => 7,
+                "6" => 8,
+                "7" => 9,
+                "8" => 10,
+                "9" => 11,
+                "10" => 12,
+                "11" => 13,
+                "12" => 14,
+                "13" => 15,
+                "14" => 16,
+                "15" => 19
+            ],
+            "3" => [
+                "0" => 3,
+                "1" => 4,
+                "2" => 5,
+                "3" => 6,
+                "4" => 8,
+                "5" => 9,
+                "6" => 10,
+                "7" => 12,
+                "8" => 13,
+                "9" => 14,
+                "10" => 16,
+                "11" => 17,
+                "12" => 18,
+                "13" => 19,
+                "14" => 21,
+                "15" => 25
+            ],
+            "4" => [
+                "0" => 4,
+                "1" => 5,
+                "2" => 7,
+                "3" => 8,
+                "4" => 10,
+                "5" => 11,
+                "6" => 13,
+                "7" => 14,
+                "8" => 16,
+                "9" => 17,
+                "10" => 19,
+                "11" => 20,
+                "12" => 22,
+                "13" => 23,
+                "14" => 25,
+                "15" => 30
+            ],
+            "5" => [
+                "0" => 5,
+                "1" => 7,
+                "2" => 9,
+                "3" => 11,
+                "4" => 13,
+                "5" => 15,
+                "6" => 17,
+                "7" => 19,
+                "8" => 21,
+                "9" => 23,
+                "10" => 25,
+                "11" => 27,
+                "12" => 29,
+                "13" => 31,
+                "14" => 33,
+                "15" => 39
+            ],
+            "6" => [
+                "0" => 7,
+                "1" => 9,
+                "2" => 11,
+                "3" => 13,
+                "4" => 15,
+                "5" => 17,
+                "6" => 19,
+                "7" => 21,
+                "8" => 23,
+                "9" => 25,
+                "10" => 27,
+                "11" => 29,
+                "12" => 31,
+                "13" => 33,
+                "14" => 35,
+                "15" => 42
+            ]
+        ];
+
+        /**
+         * Values of the CRR main stat[stars][level]
+         */
+        const CRR = [
+            "1" => [
+                "0" => 1,
+                "1" => 2,
+                "2" => 3,
+                "3" => 4,
+                "4" => 5,
+                "5" => 6,
+                "6" => 7,
+                "7" => 8,
+                "8" => 9,
+                "9" => 10,
+                "10" => 11,
+                "11" => 12,
+                "12" => 13,
+                "13" => 14,
+                "14" => 15,
+                "15" => 18
+            ],
+            "2" => [
+                "0" => 2,
+                "1" => 3,
+                "2" => 4,
+                "3" => 5,
+                "4" => 6,
+                "5" => 7,
+                "6" => 8,
+                "7" => 9,
+                "8" => 10,
+                "9" => 11,
+                "10" => 12,
+                "11" => 13,
+                "12" => 14,
+                "13" => 15,
+                "14" => 16,
+                "15" => 19
+            ],
+            "3" => [
+                "0" => 3,
+                "1" => 5,
+                "2" => 7,
+                "3" => 9,
+                "4" => 11,
+                "5" => 13,
+                "6" => 15,
+                "7" => 17,
+                "8" => 19,
+                "9" => 21,
+                "10" => 23,
+                "11" => 25,
+                "12" => 27,
+                "13" => 29,
+                "14" => 31,
+                "15" => 37
+            ],
+            "4" => [
+                "0" => 4,
+                "1" => 6,
+                "2" => 8,
+                "3" => 11,
+                "4" => 13,
+                "5" => 15,
+                "6" => 17,
+                "7" => 19,
+                "8" => 22,
+                "9" => 24,
+                "10" => 26,
+                "11" => 28,
+                "12" => 30,
+                "13" => 33,
+                "14" => 35,
+                "15" => 41
+            ],
+            "5" => [
+                "0" => 5,
+                "1" => 7,
+                "2" => 10,
+                "3" => 12,
+                "4" => 15,
+                "5" => 17,
+                "6" => 19,
+                "7" => 22,
+                "8" => 24,
+                "9" => 27,
+                "10" => 29,
+                "11" => 31,
+                "12" => 34,
+                "13" => 36,
+                "14" => 39,
+                "15" => 47
+            ],
+            "6" => [
+                "0" => 7,
+                "1" => 10,
+                "2" => 13,
+                "3" => 16,
+                "4" => 19,
+                "5" => 22,
+                "6" => 25,
+                "7" => 28,
+                "8" => 31,
+                "9" => 34,
+                "10" => 37,
+                "11" => 40,
+                "12" => 43,
+                "13" => 46,
+                "14" => 49,
+                "15" => 58
+            ]
+        ];
+
+        /**
+         * Values of the CRD main stat[stars][level]
+         */
+        const CRD = [
+            "1" => [
+                "0" => 2,
+                "1" => 3,
+                "2" => 4,
+                "3" => 5,
+                "4" => 6,
+                "5" => 7,
+                "6" => 8,
+                "7" => 9,
+                "8" => 10,
+                "9" => 11,
+                "10" => 12,
+                "11" => 13,
+                "12" => 14,
+                "13" => 15,
+                "14" => 16,
+                "15" => 19
+            ],
+            "2" => [
+                "0" => 3,
+                "1" => 5,
+                "2" => 7,
+                "3" => 9,
+                "4" => 11,
+                "5" => 13,
+                "6" => 15,
+                "7" => 17,
+                "8" => 19,
+                "9" => 21,
+                "10" => 23,
+                "11" => 25,
+                "12" => 27,
+                "13" => 29,
+                "14" => 31,
+                "15" => 37
+            ],
+            "3" => [
+                "0" => 4,
+                "1" => 6,
+                "2" => 9,
+                "3" => 11,
+                "4" => 13,
+                "5" => 16,
+                "6" => 18,
+                "7" => 20,
+                "8" => 22,
+                "9" => 25,
+                "10" => 27,
+                "11" => 29,
+                "12" => 32,
+                "13" => 34,
+                "14" => 36,
+                "15" => 43
+            ],
+            "4" => [
+                "0" => 6,
+                "1" => 9,
+                "2" => 12,
+                "3" => 15,
+                "4" => 18,
+                "5" => 21,
+                "6" => 24,
+                "7" => 27,
+                "8" => 30,
+                "9" => 33,
+                "10" => 36,
+                "11" => 39,
+                "12" => 42,
+                "13" => 45,
+                "14" => 48,
+                "15" => 57
+            ],
+            "5" => [
+                "0" => 8,
+                "1" => 11,
+                "2" => 15,
+                "3" => 18,
+                "4" => 21,
+                "5" => 25,
+                "6" => 28,
+                "7" => 31,
+                "8" => 34,
+                "9" => 38,
+                "10" => 41,
+                "11" => 44,
+                "12" => 48,
+                "13" => 51,
+                "14" => 54,
+                "15" => 65
+            ],
+            "6" => [
+                "0" => 11,
+                "1" => 15,
+                "2" => 19,
+                "3" => 23,
+                "4" => 27,
+                "5" => 31,
+                "6" => 35,
+                "7" => 39,
+                "8" => 43,
+                "9" => 47,
+                "10" => 51,
+                "11" => 55,
+                "12" => 59,
+                "13" => 63,
+                "14" => 67,
+                "15" => 80
+            ]
+        ];
+
+        /**
+         * Values of the ACC main stat[stars][level]
+         */
+        const ACC = [
+            "1" => [
+                "0" => 1,
+                "1" => 2,
+                "2" => 3,
+                "3" => 4,
+                "4" => 5,
+                "5" => 6,
+                "6" => 7,
+                "7" => 8,
+                "8" => 9,
+                "9" => 10,
+                "10" => 11,
+                "11" => 12,
+                "12" => 13,
+                "13" => 14,
+                "14" => 15,
+                "15" => 18
+            ],
+            "2" => [
+                "0" => 2,
+                "1" => 3,
+                "2" => 4,
+                "3" => 5,
+                "4" => 6,
+                "5" => 7,
+                "6" => 8,
+                "7" => 9,
+                "8" => 10,
+                "9" => 11,
+                "10" => 12,
+                "11" => 13,
+                "12" => 14,
+                "13" => 15,
+                "14" => 16,
+                "15" => 19
+            ],
+            "3" => [
+                "0" => 4,
+                "1" => 6,
+                "2" => 8,
+                "3" => 10,
+                "4" => 12,
+                "5" => 14,
+                "6" => 16,
+                "7" => 18,
+                "8" => 20,
+                "9" => 22,
+                "10" => 24,
+                "11" => 26,
+                "12" => 28,
+                "13" => 30,
+                "14" => 32,
+                "15" => 38
+            ],
+            "4" => [
+                "0" => 6,
+                "1" => 8,
+                "2" => 10,
+                "3" => 13,
+                "4" => 15,
+                "5" => 17,
+                "6" => 19,
+                "7" => 21,
+                "8" => 24,
+                "9" => 26,
+                "10" => 28,
+                "11" => 30,
+                "12" => 32,
+                "13" => 35,
+                "14" => 37,
+                "15" => 44
+            ],
+            "5" => [
+                "0" => 9,
+                "1" => 11,
+                "2" => 14,
+                "3" => 16,
+                "4" => 19,
+                "5" => 21,
+                "6" => 23,
+                "7" => 26,
+                "8" => 28,
+                "9" => 31,
+                "10" => 33,
+                "11" => 35,
+                "12" => 38,
+                "13" => 40,
+                "14" => 43,
+                "15" => 51
+            ],
+            "6" => [
+                "0" => 12,
+                "1" => 15,
+                "2" => 18,
+                "3" => 21,
+                "4" => 24,
+                "5" => 27,
+                "6" => 30,
+                "7" => 33,
+                "8" => 36,
+                "9" => 39,
+                "10" => 42,
+                "11" => 45,
+                "12" => 48,
+                "13" => 51,
+                "14" => 54,
+                "15" => 64
+            ]
+        ];
+
+        /**
+         * Values of the RES main stat[stars][level]
+         */
+        const RES = [
+            "1" => [
+                "0" => 1,
+                "1" => 2,
+                "2" => 3,
+                "3" => 4,
+                "4" => 5,
+                "5" => 6,
+                "6" => 7,
+                "7" => 8,
+                "8" => 9,
+                "9" => 10,
+                "10" => 11,
+                "11" => 12,
+                "12" => 13,
+                "13" => 14,
+                "14" => 15,
+                "15" => 18
+            ],
+            "2" => [
+                "0" => 2,
+                "1" => 3,
+                "2" => 4,
+                "3" => 5,
+                "4" => 6,
+                "5" => 7,
+                "6" => 8,
+                "7" => 9,
+                "8" => 10,
+                "9" => 11,
+                "10" => 12,
+                "11" => 13,
+                "12" => 14,
+                "13" => 15,
+                "14" => 16,
+                "15" => 19
+            ],
+            "3" => [
+                "0" => 4,
+                "1" => 6,
+                "2" => 8,
+                "3" => 10,
+                "4" => 12,
+                "5" => 14,
+                "6" => 16,
+                "7" => 18,
+                "8" => 20,
+                "9" => 22,
+                "10" => 24,
+                "11" => 26,
+                "12" => 28,
+                "13" => 30,
+                "14" => 32,
+                "15" => 38
+            ],
+            "4" => [
+                "0" => 6,
+                "1" => 8,
+                "2" => 10,
+                "3" => 13,
+                "4" => 15,
+                "5" => 17,
+                "6" => 19,
+                "7" => 21,
+                "8" => 24,
+                "9" => 26,
+                "10" => 28,
+                "11" => 30,
+                "12" => 32,
+                "13" => 35,
+                "14" => 37,
+                "15" => 44
+            ],
+            "5" => [
+                "0" => 9,
+                "1" => 11,
+                "2" => 14,
+                "3" => 16,
+                "4" => 19,
+                "5" => 21,
+                "6" => 23,
+                "7" => 26,
+                "8" => 28,
+                "9" => 31,
+                "10" => 33,
+                "11" => 35,
+                "12" => 38,
+                "13" => 40,
+                "14" => 43,
+                "15" => 51
+            ],
+            "6" => [
+                "0" => 12,
+                "1" => 15,
+                "2" => 18,
+                "3" => 21,
+                "4" => 24,
+                "5" => 27,
+                "6" => 30,
+                "7" => 33,
+                "8" => 36,
+                "9" => 39,
+                "10" => 42,
+                "11" => 45,
+                "12" => 48,
+                "13" => 51,
+                "14" => 54,
+                "15" => 64
+            ]
+        ];
+    };
+
     /**
      * Artifact types.
      *

+ 7 - 3
application/Controller.php

@@ -87,6 +87,10 @@
                             require_once(__DIR__ . "/API/v2/API_Controller.php");
                             new API_Controller($params);
                             break;
+                        case "v3":
+                            require_once(__DIR__ . "/API/v3/API_Controller.php");
+                            new API_Controller($params);
+                            break;
                     }
                 }
                 return;
@@ -284,9 +288,9 @@
                         require_once(PATH::PAGE . "Report_Skillup_Page.php");
                         $page = new Report_Skillup_Page($pars[1]);
                     }
-                    elseif (strtoupper($pars[1]) == "RUNECRAFT"){
-                        require_once(PATH::PAGE . "Report_Runecraft_Page.php");
-                        $page = new Report_Runecraft_Page($pars[1]);
+                    elseif (strtoupper($pars[1]) == "RUNE_ENCHANTMENT"){
+                        require_once(PATH::PAGE . "Report_Rune_Enchantment_Page.php");
+                        $page = new Report_Rune_Enchantment_Page($pars[1]);
                     }
                 }
                 elseif (strtoupper($pars[0]) == "OPTIMIZER"){

+ 41 - 26
application/entity/Rune_Craft.php → application/entity/Enchantment.php

@@ -1,6 +1,6 @@
 <?php
     /**
-     * Rune craft items entity file.
+     * Enchantments items entity file.
      *
      * Creates the entity and makes it available.
      *
@@ -14,13 +14,13 @@
     require_once(PATH::ENTITY . "K_Rune_Set.php");
 
     /**
-     * Owned rune craft item.
+     * Owned enchantments.
      *
-     * Represents an object from the table 'rune_craft'.
+     * Represents an object from the table 'enchantment'.
      *
      * @category Entity
      */
-    class Rune_Craft extends Entity{
+    class Enchantment extends Entity{
 
         /**
          * @var int Owner identifier.
@@ -116,30 +116,30 @@
 
             $s = "
               SELECT
-                rune_craft.id AS id,
-                rune_craft.quality As quality,
-                rune_craft.rune AS rune,
-                rune_craft.stat AS stat,
-                rune_craft.value AS value,
-                rune_craft.amount AS amount,
-                k_rune_craft_type.name AS name,
-                k_rune_craft_type.gem AS gem,
-                k_rune_craft_type.inmemorial AS inmemorial,
-                k_rune_craft_type.ancient AS ancient,
-                k_rune_craft_range.min AS min,
-                k_rune_craft_range.max AS max
+                enchantment.id AS id,
+                enchantment.quality As quality,
+                enchantment.rune AS rune,
+                enchantment.stat AS stat,
+                enchantment.value AS value,
+                enchantment.amount AS amount,
+                k_enchantment_type.name AS name,
+                k_enchantment_type.gem AS gem,
+                k_enchantment_type.inmemorial AS inmemorial,
+                k_enchantment_type.ancient AS ancient,
+                k_enchantment_range.min AS min,
+                k_enchantment_range.max AS max
               FROM
-                rune_craft,
-                k_rune_craft_type,
-                k_rune_craft_range
+                enchantment,
+                k_enchantment_type,
+                k_enchantment_range
               WHERE
-                rune_craft.id = $id AND
-                rune_craft.type = k_rune_craft_type.id AND
-                k_rune_craft_type.gem = k_rune_craft_range.gem AND
-                k_rune_craft_type.gem = k_rune_craft_range.gem AND
-                k_rune_craft_type.ancient = k_rune_craft_range.ancient AND
-                rune_craft.quality = k_rune_craft_range.quality AND
-                rune_craft.stat = k_rune_craft_range.stat;
+                enchantment.id = $id AND
+                enchantment.type = k_enchantment_type.id AND
+                k_enchantment_type.gem = k_enchantment_range.gem AND
+                k_enchantment_type.gem = k_enchantment_range.gem AND
+                k_enchantment_type.ancient = k_enchantment_range.ancient AND
+                enchantment.quality = k_enchantment_range.quality AND
+                enchantment.stat = k_enchantment_range.stat;
             ";
             $q = $db->query($s);
             $r = $q->fetchArray(SQLITE3_ASSOC);
@@ -224,5 +224,20 @@
             }
         }
 
+        /**
+         * Calculates name variables.
+         */
+        public function refresh_names(){
+            $this->stat_name = $this->stat_name($this->stat);
+            $this->quality_name = $this->quality_name($this->quality);
+            $this->name = $this->rune->name . " " . $this->stat;
+            if ($this->inmemorial == 0){
+                $this->name = $this->rune->name . " " . $this->stat;
+            }
+            else{
+                $this->name = "Inmemorial " . $this->stat;
+            }
+        }
+
     }
 ?>

+ 21 - 4
application/entity/K_Area.php

@@ -73,6 +73,18 @@
                 $this->type = AREA_TYPE_ID::CAIROS_DUNGEON;
                 $this->name = "Hall of Heroes";
             }
+            else{
+                $s = "
+                  SELECT name
+                  FROM k_area_type
+                  WHERE id = '$type'
+                ";
+                $q = $db->query($s);
+                $r = $q->fetchArray(SQLITE3_ASSOC);
+                if ($r){
+                    $this->name = HTML::e($r["name"]);
+                }
+            }
         }
 
         /**
@@ -88,9 +100,17 @@
                 // Hall of Heroes
                 return APPLICATION::img("AREA", "HOH");
             }
+            elseif ($this->type == AREA_TYPE_ID::RIFT_RAID){
+                return APPLICATION::img("UNIT", 44007);
+            }
+            elseif ($this->type == AREA_TYPE_ID::ARENA){
+                return APPLICATION::img("AREA", "ARENA");
+            }
+            elseif ($this->type == AREA_TYPE_ID::DIMENSIONAL_RIFT){
+                return APPLICATION::img("CURRENCY", "darkportalenergy");
+            }
             elseif (
                 $this->type == AREA_TYPE_ID::SCENARIO ||
-                $this->type == AREA_TYPE_ID::ARENA ||
                 $this->type == AREA_TYPE_ID::GUILD_WAR ||
                 $this->type == AREA_TYPE_ID::GUILD_SIEGE ||
                 $this->type == AREA_TYPE_ID::WORLD_BOSS ||
@@ -159,9 +179,6 @@
                         break;
                 }
             }
-            elseif ($this->type == AREA_TYPE_ID::RIFT_RAID){
-                return APPLICATION::img("UNIT", 44007);
-            }
             elseif ($this->type == AREA_TYPE_ID::DIMENSIONAL_HOLE){
                 switch ($this->id){
                     case DUNGEON_ID::FOREST_OF_ROARING_BEASTS:

+ 1 - 1
application/entity/K_Leader_Skill.php

@@ -151,7 +151,7 @@
                     $s = $s . " in Guild content";
                     break;
             }
-            $s = $s . ".";
+            $s = str_replace("  ", " ", $s) . ".";
             return $s;
         }
 

+ 1 - 1
application/entity/Player.php

@@ -336,7 +336,7 @@
                   inventory
                 WHERE
                   inventory.uid = '$UID' AND
-                  inventory.type = " . INVENTORY_TYPE_ID::RUNE_CRAFT . " AND
+                  inventory.type = " . INVENTORY_TYPE_ID::ENCHANTMENT . " AND
                   amount > 0
                 ORDER BY inventory.id;
             ";

+ 80 - 21
application/entity/Run.php

@@ -14,6 +14,7 @@
     require_once(PATH::ENTITY . "Unit.php");
     require_once(PATH::ENTITY . "K_Unit.php");
     require_once(PATH::ENTITY . "Rune.php");
+    require_once(PATH::ENTITY . "Enchantment.php");
     require_once(PATH::ENTITY . "Inventory.php");
     require_once(PATH::ENTITY . "K_Area.php");
     require_once(PATH::ENTITY . "K_Rune_Set.php");
@@ -71,17 +72,27 @@
         /**
          * @var int Reward mana.
          */
-        public $mana;
+        public $mana = 0;
 
         /**
          * @var int Reward energy.
          */
-        public $energy;
+        public $energy = 0;
 
         /**
          * @var int Reward crystal.
          */
-        public $crystal;
+        public $crystal = 0;
+
+        /**
+         * @var int Reward Honor Points (Arena).
+         */
+        public $honor_points = 0;
+
+        /**
+         * @var int Reward Guild Points.
+         */
+        public $guild_points = 0;
 
         /**
          * @var bool Indicates if a frind/mentor helped.
@@ -146,7 +157,12 @@
         /**
          * @var \Rune[] List of Runes dropped.
          */
-        public $rune= [];
+        public $rune = [];
+
+        /**
+         * @var \Enchantment[] List of Enchantments dropped.
+         */
+        public $enchantment = [];
 
         /**
          * Constructor.
@@ -166,6 +182,7 @@
                   id,
                   uid,
                   dtime,
+                  area_type,
                   area,
                   stage,
                   difficulty,
@@ -174,6 +191,8 @@
                   mana,
                   energy,
                   crystal,
+                  honor_points,
+                  guild_points,
                   helper,
                   score,
                   rank
@@ -185,23 +204,7 @@
             $this->id = $r["id"];
             $this->uid = $r["uid"];
             $this->dtime = $r["dtime"];
-            $type = 0;
-            if (APPLICATION::valid_id("RAID_RIFT_DUNGEON_ID", $r["area"]) && $r["stage"] < 1){
-                $type = AREA_TYPE_ID::RIFT_RAID_DUNGEON;
-            }
-            elseif (APPLICATION::valid_id("SCENARIO_ID", $r["area"])){
-                $type = AREA_TYPE_ID::SCENARIO;
-            }
-            elseif (APPLICATION::valid_id("ELEMENTAL_RIFT_DUNGEON_ID", $r["area"]) && $r["stage"] < 1){
-                $type = AREA_TYPE_ID::RIFT_DUNGEON;
-            }
-            elseif (APPLICATION::valid_id("CAIROS_DUNGEON_ID", $r["area"])){
-                $type = AREA_TYPE_ID::CAIROS_DUNGEON;
-            }
-            elseif (APPLICATION::valid_id("DIMENSION_DUNGEON_ID", $r["area"])){
-                $type = AREA_TYPE_ID::DIMENSIONAL_HOLE;
-            }
-            $this->area = new K_Area($r["area"], $type);
+            $this->area = new K_Area($r["area"], $r["area_type"]);
             $this->stage = $r["stage"];
             $this->difficulty = $r["difficulty"];
             $this->win = $r["win"];
@@ -209,6 +212,8 @@
             $this->mana = $r["mana"];
             $this->energy = $r["energy"];
             $this->crystal = $r["crystal"];
+            $this->honor_points = $r["honor_points"];
+            $this->guild_points = $r["guild_points"];
             $this->helper = $r["helper"];
             $this->score = $r["score"];
             $this->rank = $r["rank"];
@@ -363,6 +368,60 @@
                 }
                 array_push($this->rune, $rune);
             }
+            $s = "
+              SELECT
+                run_drop_enchantment.id AS id,
+                type,
+                run_drop_enchantment.quality AS quality,
+                rune,
+                run_drop_enchantment.stat AS stat,
+                value,
+                k_enchantment_type.inmemorial AS inmemorial,
+                k_enchantment_type.ancient AS ancient,
+                k_enchantment_range.min AS min,
+                k_enchantment_range.max AS max
+              FROM
+                run_drop_enchantment,
+                k_enchantment_type,
+                k_enchantment_range
+              WHERE
+                run = $this->id AND
+                run_drop_enchantment.type = k_enchantment_type.id AND
+                k_enchantment_type.gem = k_enchantment_range.gem AND
+                k_enchantment_type.gem = k_enchantment_range.gem AND
+                k_enchantment_type.ancient = k_enchantment_range.ancient AND
+                run_drop_enchantment.quality = k_enchantment_range.quality AND
+                run_drop_enchantment.stat = k_enchantment_range.stat;
+              ;
+            ";
+            $q = $db->query($s);
+            while ($r = $q->fetchArray(SQLITE3_ASSOC)){
+                $enchantment = new Enchantment($r["id"]);
+                if (!isset($enchantment->id)){
+                    $enchantment->id = $r["id"];
+                    if ($r["type"] == CRAFT_ID::ENCHANT_GEM || $r["type"] == CRAFT_ID::IMMEMORIAL_GEM){
+                        $enchantment->gem = 1;
+                    }
+                    else{
+                        $enchantment->gem = 0;
+                    }
+                    $enchantment->quality = $r["quality"];
+                    $enchantment->value = 0;
+                    $enchantment->stat = $r["stat"];
+                    $enchantment->ancient = $r["ancient"];
+                    $enchantment->inmemorial = $r["inmemorial"];
+                    $enchantment->rune = new K_Rune_Set($r["rune"]);
+                    $enchantment->min = $r["min"];
+                    $enchantment->max = $r["max"];
+                    $enchantment->value = $r["value"];
+                    $enchantment->amount = 0;
+                    $enchantment->refresh_names();
+                }
+                else{
+                    $enchantment->amount = 0;
+                }
+                array_push($this->enchantment, $enchantment);
+            }
         }
 
     }

+ 24 - 22
application/helper/HTML_Helper.php

@@ -19,7 +19,7 @@
     require_once(PATH::ENTITY . "K_Decoration.php");
     require_once(PATH::ENTITY . "Rune.php");
     require_once(PATH::ENTITY . "Artifact.php");
-    require_once(PATH::ENTITY . "Rune_Craft.php");
+    require_once(PATH::ENTITY . "Enchantment.php");
     require_once(PATH::ENTITY . "Inventory.php");
 
     /**
@@ -559,16 +559,16 @@
         /**
          * Generates a grindstone or gem panel.
          * 
-         * @param Rune_Craft craft Entity to get the info from.
+         * @param Enchantment Entity to get the info from.
          * @return string HTML content for a rune panel. Empty on error.
          */
-        public static function craft_panel($craft) {
-            if (!($craft instanceof Rune_Craft)){
+        public static function enchantment_panel($enchantment) {
+            if (!($enchantment instanceof Enchantment)){
                 return "";
             }
             $html = "";
-            if ($craft->gem == 1){
-                if ($craft->inmemorial == 1){
+            if ($enchantment->gem == 1){
+                if ($enchantment->inmemorial == 1){
                     $base = URL::IMG["GRIND"] . "gem_inmemorial.png";
                 }
                 else{
@@ -576,37 +576,39 @@
                 }
             }
             else{
-                if ($craft->inmemorial == 1){
+                if ($enchantment->inmemorial == 1){
                     $base = URL::IMG["GRIND"] . "grind_inmemorial.png";
                 }
                 else{
                     $base = URL::IMG["GRIND"] . "grind.png";
                 }
             }
-            if ($craft->inmemorial == 0 && isset($craft->rune)){
-                $icon = "<img class='craft_icon' src='" . $craft->rune->get_image() . "'/>\n";
+            if ($enchantment->inmemorial == 0 && isset($enchantment->rune)){
+                $icon = "<img class='enchantment_icon' src='" . $enchantment->rune->get_image() . "'/>\n";
             }
             else{
                 $icon = "";
             }
-            if (in_array($craft->stat_name, array("CRR", "CRD", "RES", "ACC", "HP%", "ATK%", "DEF%"))){
-                $stat_name = str_replace("%", "", $craft->stat_name);
-                $stat_value = "+" . $craft->min . "~" . $craft->max . "%";
+            if (in_array($enchantment->stat_name, array("CRR", "CRD", "RES", "ACC", "HP%", "ATK%", "DEF%"))){
+                $stat_name = str_replace("%", "", $enchantment->stat_name);
+                $stat_value = "+" . $enchantment->min . "~" . $enchantment->max . "%";
             }
             else{
-                $stat_name = $craft->stat_name;
-                $stat_value = "+" . $craft->min . "~" . $craft->max;
+                $stat_name = $enchantment->stat_name;
+                $stat_value = "+" . $enchantment->min . "~" . $enchantment->max;
             }
-            $html .= "<div class='craft_panel craft_quality_" . strtolower($craft->quality_name) . " rune_ancient_" . $craft->ancient . "'>\n";
-            $html .= "<img class='craft_base' src='" . $base . "'/>\n";
+            $html .= "<div class='enchantment_panel enchantment_quality_" . strtolower($enchantment->quality_name) . " enchantment_ancient_" . $enchantment->ancient . "'>\n";
+            $html .= "<img class='enchantment_base' src='" . $base . "'/>\n";
             $html .= $icon;
-            $html .= "<span class='craft_stat'>\n";
-            $html .= "<span class='craft_stat_name'>" . $stat_name . "</span>\n";
-            $html .= "<span class='craft_stat_value'>" . $stat_value . "</span>\n";
-            $html .= "</span>\n";
-            $html .= "<span class='craft_amount'>\n";
-            $html .= "x" . $craft->amount . "\n";
+            $html .= "<span class='enchantment_stat'>\n";
+            $html .= "<span class='enchantment_stat_name'>" . $stat_name . "</span>\n";
+            $html .= "<span class='enchantment_stat_value'>" . $stat_value . "</span>\n";
             $html .= "</span>\n";
+            if ($enchantment->amount > 0){
+                $html .= "<span class='enchantment_amount'>\n";
+                $html .= "x" . $enchantment->amount . "\n";
+                $html .= "</span>\n";
+            }
             $html .= "</div>\n";
             return $html;
         }

+ 6 - 6
application/page/Enchantments_Page.php

@@ -13,7 +13,7 @@
      */
     require_once(PATH::PAGE . "Page.php");
     require_once(PATH::ENTITY . "Custom_Filter.php");
-    require_once(PATH::ENTITY . "Rune_Craft.php");
+    require_once(PATH::ENTITY . "Enchantment.php");
 
     /**
      * Enchantment list page model.
@@ -23,12 +23,12 @@
     class Enchantments_Page extends Page{
 
         /**
-         * @var \Rune_Craft[] Owned enchantment gems.
+         * @var \Enchantment[] Owned enchantment gems.
          */
         public $gems = [];
 
         /**
-         * @var \Rune_Craft[] Owned enchantment grindstones.
+         * @var \Enchantment[] Owned enchantment grindstones.
          */
         public $grindstones = [];
 
@@ -58,7 +58,7 @@
             }
             $q = $db->query($s);
             while ($r = $q->fetchArray(SQLITE3_ASSOC)){
-                $enchantment = new Rune_Craft($r["id"]);
+                $enchantment = new Enchantment($r["id"]);
                 //if ($enchantment->gem == true){
                 //   array_push($this->gems, $enchantment);
                 //}
@@ -162,7 +162,7 @@
             $s = "
               SELECT
                 id
-              FROM rune_craft
+              FROM enchantment
               WHERE uid = '$UID' AND (
                 $cond
               )
@@ -182,7 +182,7 @@
             global $UID;
             $s = "
               SELECT id
-              FROM rune_craft
+              FROM enchantment
               WHERE
                 uid = '$UID' AND
                 quality >= " . $this->filters["MIN_QUALITY"] . " AND

+ 49 - 50
application/page/Report_Runecraft_Page.php → application/page/Report_Rune_Enchantment_Page.php

@@ -13,14 +13,14 @@
     require_once(PATH::PAGE . "Page.php");
     require_once(PATH::ENTITY . "Unit.php");
     require_once(PATH::ENTITY . "Rune.php");
-    require_once(PATH::ENTITY . "Rune_Craft.php");
+    require_once(PATH::ENTITY . "Enchantment.php");
 
     /**
      * Upgradeable runes report page model.
      *
      * @category Page
      */
-    class Report_Runecraft_Page extends Page{
+    class Report_Rune_Enchantment_Page extends Page{
 
         /**
          * @var mixed[] List of available upgrades.
@@ -32,7 +32,7 @@
          *     upgrade = [
          *       [
          *         rune (Rune),
-         *         craft = [(Rune_Craft)]
+         *         enchant = [(Enchantment)]
          *       ]
          *     ]
          *   ]
@@ -100,7 +100,7 @@
          */
         public function __construct(){
             global $db;
-            $this->view = PATH::VIEW . "report_runecraft.php";
+            $this->view = PATH::VIEW . "report_rune_enchantment.php";
             $this->parse_filters();
             $s = $this->build_query();
             $q = $db->query($s);
@@ -124,7 +124,7 @@
                     ];
                     $rupgrade = [
                         "rune" => new Rune($r["rune"]),
-                        "craft" => [],
+                        "enchantment" => [],
                     ];
                 }
                 if ($r["rune"] != $prev_rune){
@@ -134,18 +134,18 @@
                     $prev_rune = $r["rune"];
                     $rupgrade = [
                         "rune" => new Rune($r["rune"]),
-                        "craft" => [],
+                        "enchantment" => [],
                     ];
                 }
-                $rune_craft = new Rune_craft($r["craft"]);
-                array_push($rupgrade["craft"], $rune_craft);
+                $enchantment= new Enchantment($r["enchantment"]);
+                array_push($rupgrade["enchantment"], $enchantment);
             }
             // Last entries
             array_push($upgrade["upgrade"], $rupgrade);
             array_push($this->upgrades, $upgrade);
-            $this->title = "Rune craft - SWDB";
-            $this->description = "Find runes that can be upgraded via crafting.";
-            $this->canonical = URL::BASE . "report/runecraft/";
+            $this->title = "Rune enchantment - SWDB";
+            $this->description = "Find runes that can be upgraded via enchantments.";
+            $this->canonical = URL::BASE . "report/rune_enchantment/";
         }
 
         /**
@@ -154,7 +154,6 @@
          * min_stars (1-6) and in_storage (on/off).
          */
         private function parse_filters(){
-            //$this->filters = FILTER::RUNECRAFT;
             // Monster
             if (isset($_GET["monster_min_stars"]) && intval($_GET["monster_min_stars"]) > 0 && intval($_GET["monster_min_stars"]) < 7){
                 $this->filters["MONSTER_MIN_STARS"] = $_GET["monster_min_stars"];
@@ -256,20 +255,20 @@
                 SELECT
                   unit.id AS unit,
                   rune.id AS rune,
-                  rune_craft.id AS craft
+                  enchantment.id AS enchantment
                 FROM
                   unit,
                   k_unit,
                   rune,
-                  rune_craft,
-                  k_rune_craft_type
+                  enchantment,
+                  k_enchantment_type
                 WHERE
                   unit.uid = '$UID' AND
                   rune.uid = '$UID' AND
-                  rune_craft.uid = '$UID' AND
+                  enchantment.uid = '$UID' AND
                   unit.id = rune.assigned_to AND
                   unit.unit = k_unit.id AND
-                  rune_craft.type = k_rune_craft_type.id AND
+                  enchantment.type = k_enchantment_type.id AND
                   unit.stars >= " . $this->filters["RUNE_MIN_STARS"] . " AND
                   unit.stars <= " . $this->filters["RUNE_MAX_STARS"] . " AND
                   -- RUNE FILTERS
@@ -286,10 +285,10 @@
                   rune.max_efficiency >= " . $this->filters["RUNE_MIN_MAX_EFFICIENCY"] . " AND
                   rune.max_efficiency <= " . $this->filters["RUNE_MAX_MAX_EFFICIENCY"] . " AND
                   (
-                    rune.type = rune_craft.rune OR 
-                    k_rune_craft_type.inmemorial = 1
+                    rune.type = enchantment.rune OR 
+                    k_enchantment_type.inmemorial = 1
                   ) AND
-                  k_rune_craft_type.ancient = rune.ancient AND
+                  k_enchantment_type.ancient = rune.ancient AND
                 ";
             if (strlen($this->filters["MONSTER_NAME"]) > 0){
                 $s = $s . " upper(k_unit.name) LIKE upper('%" . $this->filters["MONSTER_NAME"] . "%') AND ";
@@ -305,13 +304,13 @@
                 $s = $s . "-1) AND ";
             }
             $s .= "
-                rune_craft.stat <> rune.main_stat AND
-                rune_craft.stat <> rune.innate_stat AND
+                enchantment.stat <> rune.main_stat AND
+                enchantment.stat <> rune.innate_stat AND
                   (
-                    k_rune_craft_type.gem = 1 AND
+                    k_enchantment_type.gem = 1 AND
                     (
                       -- GEM FILTERS
-                      rune_craft.quality >= " . $this->filters["GEM_MIN_QUALITY"] . " AND
+                      enchantment.quality >= " . $this->filters["GEM_MIN_QUALITY"] . " AND
             ";
             if (count($this->filters["GEM_STAT_FROM"]) > 0){
                 $in = "(";
@@ -327,19 +326,19 @@
                     $in = $in . "$t, ";
                 }
                 $in = $in . "-1)";
-                $s .= " rune_craft.stat IN $in AND ";
+                $s .= " enchantment.stat IN $in AND ";
             }
             $s .= "
                       (
-                        rune_craft.stat <> rune.substat_1 AND
-                        rune_craft.stat <> rune.substat_2 AND
-                        rune_craft.stat <> rune.substat_3 AND
-                        rune_craft.stat <> rune.substat_4
+                        enchantment.stat <> rune.substat_1 AND
+                        enchantment.stat <> rune.substat_2 AND
+                        enchantment.stat <> rune.substat_3 AND
+                        enchantment.stat <> rune.substat_4
                       )
                     )
                   ) |
                   (
-                    k_rune_craft_type.gem = 0 AND
+                    k_enchantment_type.gem = 0 AND
                     (
                       -- GRINDSTONE FILTERS
             ";
@@ -349,34 +348,34 @@
                     $in = $in . "$t, ";
                 }
                 $in = $in . "-1)";
-                $s .= " rune_craft.stat IN $in AND ";
+                $s .= " enchantment.stat IN $in AND ";
             }
             $s .= "
-                      rune_craft.quality >= " . $this->filters["GRIND_MIN_QUALITY"] . " AND
+                      enchantment.quality >= " . $this->filters["GRIND_MIN_QUALITY"] . " AND
                       (
                         (
-                          rune_craft.stat = rune.substat_1 AND
-                          rune_craft.stat <> rune.substat_2 AND
-                          rune_craft.stat <> rune.substat_3 AND
-                          rune_craft.stat <> rune.substat_4
+                          enchantment.stat = rune.substat_1 AND
+                          enchantment.stat <> rune.substat_2 AND
+                          enchantment.stat <> rune.substat_3 AND
+                          enchantment.stat <> rune.substat_4
                         ) OR
                         (
-                          rune_craft.stat <> rune.substat_1 AND
-                          rune_craft.stat = rune.substat_2 AND
-                          rune_craft.stat <> rune.substat_3 AND
-                          rune_craft.stat <> rune.substat_4
+                          enchantment.stat <> rune.substat_1 AND
+                          enchantment.stat = rune.substat_2 AND
+                          enchantment.stat <> rune.substat_3 AND
+                          enchantment.stat <> rune.substat_4
                         ) OR
                         (
-                          rune_craft.stat <> rune.substat_1 AND
-                          rune_craft.stat <> rune.substat_2 AND
-                          rune_craft.stat = rune.substat_3 AND
-                          rune_craft.stat <> rune.substat_4
+                          enchantment.stat <> rune.substat_1 AND
+                          enchantment.stat <> rune.substat_2 AND
+                          enchantment.stat = rune.substat_3 AND
+                          enchantment.stat <> rune.substat_4
                         ) OR
                         (
-                          rune_craft.stat <> rune.substat_1 AND
-                          rune_craft.stat <> rune.substat_2 AND
-                          rune_craft.stat <> rune.substat_3 AND
-                          rune_craft.stat = rune.substat_4
+                          enchantment.stat <> rune.substat_1 AND
+                          enchantment.stat <> rune.substat_2 AND
+                          enchantment.stat <> rune.substat_3 AND
+                          enchantment.stat = rune.substat_4
                         )
                       )
                     )
@@ -392,8 +391,8 @@
                   rune.level DESC,
                   rune.id, 
                   rune.slot, 
-                  rune_craft.quality DESC,
-                  rune_craft.stat DESC;
+                  enchantment.quality DESC,
+                  enchantment.stat DESC;
                 ";
             return $s;
         }

binární
application/sw.sqlite


+ 1 - 1
application/view/enchantments.php

@@ -273,7 +273,7 @@
 <?php
                         }
 ?>
-                        <?=HTML::craft_panel($grindstone)?>
+                        <?=HTML::enchantment_panel($grindstone)?>
 <?php
                     }
 ?>

+ 1 - 1
application/view/report.php

@@ -75,7 +75,7 @@
                         Shows monsters with runes that can be grinded or enchanted.
                         It list the grindstones and gems that are available for each rune.
                     </p>
-                    <a class='a_button' href='/<?=$UID?>/report/runecraft/'>
+                    <a class='a_button' href='/<?=$UID?>/report/rune_enchantment/'>
                         Go
                     </a>
                 </article>

+ 6 - 6
application/view/report_runecraft.php → application/view/report_rune_enchantment.php

@@ -1,11 +1,11 @@
 <?php
     /**
-     * Rune craft report view.
+     * Rune Enchantment report view.
      *
      * Contains the view layout and ome code to present the data.
      *
      * @category View
-     * @property_read Report_Runecraft_Page $page The page model.
+     * @property_read Report_Rune_Enchantment_Page $page The page model.
      */
 ?>
 <!DOCTYPE html>
@@ -17,7 +17,7 @@
         <link rel='shortcut icon' href='<?=$page->favicon?>'/>
         <!-- CSS files -->
         <link rel='stylesheet' type='text/css' href='<?=URL::CSS?>ui.css'/>
-        <link rel='stylesheet' type='text/css' href='<?=URL::CSS?>report_runecraft.css'/>
+        <link rel='stylesheet' type='text/css' href='<?=URL::CSS?>report_rune_enchantment.css'/>
         <!-- Meta tags -->
         <link rel='canonical' href='<?=$page->canonical?>'/>
         <link rel='author' href='<?=$page->author?>'/>
@@ -102,7 +102,7 @@
             <h2>
                 Report filters
             </h2>
-            <form action='/<?=$UID?>/report/runecraft/' method='get' id='filter_runecraft' class='filter'>
+            <form action='/<?=$UID?>/report/runeenchantment/' method='get' id='filter_runeenchantment' class='filter'>
                 <table>
                     <tr>
                         <td colspan='2' class='header'>
@@ -483,9 +483,9 @@
                                 </td>
                                 <td class='upgrades'>
 <?php
-                                    foreach ($rupgrade["craft"] as $craft){
+                                    foreach ($rupgrade["enchantment"] as $enchantment){
     ?>
-                                        <?=HTML::craft_panel($craft)?>
+                                        <?=HTML::enchantment_panel($enchantment)?>
 <?php
                                     }
     ?>

+ 40 - 11
application/view/runs.php

@@ -300,7 +300,10 @@
 ?>
                                             <span class='difficulty'>
 <?php
-                                                if ($run->area->type == AREA_TYPE_ID::TARTARUS_LABYRINTH){
+                                                if (
+                                                  $run->area->type == AREA_TYPE_ID::TARTARUS_LABYRINTH ||
+                                                  $run->area->type == AREA_TYPE_ID::DIMENSIONAL_RIFT
+                                                ){
                                                     $difficulty = $run->difficulty;
                                                 }
                                                 else{
@@ -322,15 +325,15 @@
 <?php
                                     $date = date_create($run->dtime);
 ?>
-                                    <?=date_format($date, "Y/m/d H:i:s")?>
+                                    <?=date("Y/m/d H:i:s", $run->dtime);//date_format($date, "Y/m/d H:i:s")?>
                                 </td>
-                                <td class='team <?=$class_frontline?>'>
+                                <td class='team'>
 <?php
                                     if ($run->helper == 1){
                                         $enable_save = false;
 ?>
                                         <div class='monster_panel helper'>
-                                            <img class='monster' title='Friend/Menstor' src='<?=URL::IMG["CURRENCY"]?>socialpoint.png'/>
+                                            <img class='monster' title='Friend/Mentor' src='<?=URL::IMG["CURRENCY"]?>socialpoint.png'/>
                                         </div>
 <?php
                                     }
@@ -390,15 +393,17 @@
                                     }
                                     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);
+                                            if ($run->time > 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>
+                                                <?=$m?>:<?=$s?><span class='ms'>.<?=$ms?></span>
 <?php
+                                            }
                                         }
                                         else{
 ?>
@@ -432,6 +437,22 @@
                                             <img class='item' title='Crystal' src='<?=URL::IMG["CURRENCY"]?>crystal.png'/>
                                             <span class='item'><?=$run->crystal?></span>
                                         </div>
+<?php
+                                    }
+                                    if ($run->honor_points > 0){
+?>
+                                        <div class='item'>
+                                            <img class='item' title='Honor Points' src='<?=URL::IMG["CURRENCY"]?>honor.png'/>
+                                            <span class='item'><?=$run->honor_points?></span>
+                                        </div>
+<?php
+                                    }
+                                    if ($run->guild_points > 0){
+?>
+                                        <div class='item'>
+                                            <img class='item' title='Guild Points' src='<?=URL::IMG["CURRENCY"]?>guildpoint.png'/>
+                                            <span class='item'><?=$run->guild_points?></span>
+                                        </div>
 <?php
                                     }
                                     foreach ($run->unit as $unit){
@@ -480,6 +501,14 @@
                                             <img class='item' title='Rune' src='<?=URL::IMG["RUNE"]?>base.png'/>
                                             <?=HTML::rune_table($rune)?>
                                         </div>
+<?php
+                                    }
+                                    foreach ($run->enchantment as $enchantment){
+?>
+                                        <div class='item item_enchantment'>
+                                            <img class='item' title='Enchantment' src='<?=URL::IMG["GRIND"]?>grind.png'/>
+                                            <?=HTML::enchantment_panel($enchantment)?>
+                                        </div>
 <?php
                                     }
 ?>

+ 234 - 234
install_data/install_base.sql

@@ -257,7 +257,7 @@ INSERT INTO k_inventory_type VALUES(11,'Essences','');
 INSERT INTO k_inventory_type VALUES(12,'Monster Piece','');
 INSERT INTO k_inventory_type VALUES(15,'UNKNOWN','Appears in inventory_info with qty 0');
 INSERT INTO k_inventory_type VALUES(19,'Guild Monster Piece','');
-INSERT INTO k_inventory_type VALUES(27,'Rune Craft','');
+INSERT INTO k_inventory_type VALUES(27,'Enchantment','');
 INSERT INTO k_inventory_type VALUES(29,'Craft Stuff','');
 INSERT INTO k_inventory_type VALUES(61,'Enhancing Monster','');
 CREATE TABLE k_inventory(
@@ -663,28 +663,28 @@ INSERT INTO k_artifact_effect VALUES (406, '[Skill 3] Recovery +{}%');
 INSERT INTO k_artifact_effect VALUES (407, '[Skill 1] Accuracy +{}%');
 INSERT INTO k_artifact_effect VALUES (408, '[Skill 2] Accuracy +{}%');
 INSERT INTO k_artifact_effect VALUES (409, '[Skill 3] Accuracy +{}%');
-CREATE TABLE k_craft(
+CREATE TABLE k_enchantment(
     id INT NOT NULL PRIMARY KEY,
     name TEXT UNIQUE
 );
-INSERT INTO k_craft VALUES(1,'Enchant_gem');
-INSERT INTO k_craft VALUES(2,'Grindstone');
-INSERT INTO k_craft VALUES(3,'Immemorial_gem');
-INSERT INTO k_craft VALUES(4,'Immemorial_grindstone');
-CREATE TABLE k_rune_craft_type(
+INSERT INTO k_enchantment VALUES(1,'Enchant_gem');
+INSERT INTO k_enchantment VALUES(2,'Grindstone');
+INSERT INTO k_enchantment VALUES(3,'Immemorial_gem');
+INSERT INTO k_enchantment VALUES(4,'Immemorial_grindstone');
+CREATE TABLE k_enchantment_type(
     id INT NOT NULL PRIMARY KEY,
     name TEXT NOT NULL UNIQUE,
     gem INT NOT NULL CHECK(gem IN (1, 0)),
     inmemorial INT NOT NULL CHECK(inmemorial IN (1, 0)),
     ancient INT NOT NULL CHECK(ancient IN (1, 0))
 );
-INSERT INTO k_rune_craft_type VALUES(1,'Enchant Gem',1,0,0);
-INSERT INTO k_rune_craft_type VALUES(2,'Grindstone',0,0,0);
-INSERT INTO k_rune_craft_type VALUES(3,'Inmemorial Gem',1,1,0);
-INSERT INTO k_rune_craft_type VALUES(4,'Inmemorial Grindstone',0,1,0);
-INSERT INTO k_rune_craft_type VALUES(5,'Ancient Gem',1,0,1);
-INSERT INTO k_rune_craft_type VALUES(6,'Ancient Grindstone',0,0,1);
-CREATE TABLE k_rune_craft_range(
+INSERT INTO k_enchantment_type VALUES(1,'Enchant Gem',1,0,0);
+INSERT INTO k_enchantment_type VALUES(2,'Grindstone',0,0,0);
+INSERT INTO k_enchantment_type VALUES(3,'Inmemorial Gem',1,1,0);
+INSERT INTO k_enchantment_type VALUES(4,'Inmemorial Grindstone',0,1,0);
+INSERT INTO k_enchantment_type VALUES(5,'Ancient Gem',1,0,1);
+INSERT INTO k_enchantment_type VALUES(6,'Ancient Grindstone',0,0,1);
+CREATE TABLE k_enchantment_range(
     gem INT NOT NULL CHECK(gem IN (1, 0)),
     ancient INT NOT NULL CHECK(ancient IN (1, 0)),
     quality INT NOT NULL REFERENCES k_quality(id),
@@ -692,226 +692,226 @@ CREATE TABLE k_rune_craft_range(
     min INT NOT NULL CHECK(min > 0),
     max INT NOT NULL CHECK(max > 0)
 );
-INSERT INTO k_rune_craft_range VALUES(0,0,1,1,80,120);
-INSERT INTO k_rune_craft_range VALUES(0,0,2,1,100,200);
-INSERT INTO k_rune_craft_range VALUES(0,0,3,1,180,250);
-INSERT INTO k_rune_craft_range VALUES(0,0,4,1,230,450);
-INSERT INTO k_rune_craft_range VALUES(0,0,5,1,430,550);
-INSERT INTO k_rune_craft_range VALUES(0,0,1,2,1,3);
-INSERT INTO k_rune_craft_range VALUES(0,0,2,2,2,5);
-INSERT INTO k_rune_craft_range VALUES(0,0,3,2,3,6);
-INSERT INTO k_rune_craft_range VALUES(0,0,4,2,4,7);
-INSERT INTO k_rune_craft_range VALUES(0,0,5,2,5,10);
-INSERT INTO k_rune_craft_range VALUES(0,0,1,3,4,8);
-INSERT INTO k_rune_craft_range VALUES(0,0,2,3,6,12);
-INSERT INTO k_rune_craft_range VALUES(0,0,3,3,10,18);
-INSERT INTO k_rune_craft_range VALUES(0,0,4,3,12,22);
-INSERT INTO k_rune_craft_range VALUES(0,0,5,3,18,30);
-INSERT INTO k_rune_craft_range VALUES(0,0,1,4,1,3);
-INSERT INTO k_rune_craft_range VALUES(0,0,2,4,2,5);
-INSERT INTO k_rune_craft_range VALUES(0,0,3,4,3,6);
-INSERT INTO k_rune_craft_range VALUES(0,0,4,4,4,7);
-INSERT INTO k_rune_craft_range VALUES(0,0,5,4,5,10);
-INSERT INTO k_rune_craft_range VALUES(0,0,1,5,4,8);
-INSERT INTO k_rune_craft_range VALUES(0,0,2,5,6,12);
-INSERT INTO k_rune_craft_range VALUES(0,0,3,5,10,18);
-INSERT INTO k_rune_craft_range VALUES(0,0,4,5,12,22);
-INSERT INTO k_rune_craft_range VALUES(0,0,5,5,18,30);
-INSERT INTO k_rune_craft_range VALUES(0,0,1,6,1,3);
-INSERT INTO k_rune_craft_range VALUES(0,0,2,6,2,5);
-INSERT INTO k_rune_craft_range VALUES(0,0,3,6,3,6);
-INSERT INTO k_rune_craft_range VALUES(0,0,4,6,4,7);
-INSERT INTO k_rune_craft_range VALUES(0,0,5,6,5,10);
-INSERT INTO k_rune_craft_range VALUES(0,0,1,8,1,2);
-INSERT INTO k_rune_craft_range VALUES(0,0,2,8,1,2);
-INSERT INTO k_rune_craft_range VALUES(0,0,3,8,2,3);
-INSERT INTO k_rune_craft_range VALUES(0,0,4,8,3,4);
-INSERT INTO k_rune_craft_range VALUES(0,0,5,8,4,5);
-INSERT INTO k_rune_craft_range VALUES(0,0,1,9,1,2);
-INSERT INTO k_rune_craft_range VALUES(0,0,2,9,1,3);
-INSERT INTO k_rune_craft_range VALUES(0,0,3,9,2,4);
-INSERT INTO k_rune_craft_range VALUES(0,0,4,9,3,5);
-INSERT INTO k_rune_craft_range VALUES(0,0,5,9,4,6);
-INSERT INTO k_rune_craft_range VALUES(0,0,1,10,1,3);
-INSERT INTO k_rune_craft_range VALUES(0,0,2,10,2,4);
-INSERT INTO k_rune_craft_range VALUES(0,0,3,10,2,5);
-INSERT INTO k_rune_craft_range VALUES(0,0,4,10,3,5);
-INSERT INTO k_rune_craft_range VALUES(0,0,5,10,4,7);
-INSERT INTO k_rune_craft_range VALUES(0,0,1,11,1,3);
-INSERT INTO k_rune_craft_range VALUES(0,0,2,11,2,4);
-INSERT INTO k_rune_craft_range VALUES(0,0,3,11,2,5);
-INSERT INTO k_rune_craft_range VALUES(0,0,4,11,3,7);
-INSERT INTO k_rune_craft_range VALUES(0,0,5,11,4,8);
-INSERT INTO k_rune_craft_range VALUES(0,0,1,12,1,3);
-INSERT INTO k_rune_craft_range VALUES(0,0,2,12,2,4);
-INSERT INTO k_rune_craft_range VALUES(0,0,3,12,2,5);
-INSERT INTO k_rune_craft_range VALUES(0,0,4,12,3,7);
-INSERT INTO k_rune_craft_range VALUES(0,0,5,12,4,8);
-INSERT INTO k_rune_craft_range VALUES(1,0,1,1,100,150);
-INSERT INTO k_rune_craft_range VALUES(1,0,2,1,130,220);
-INSERT INTO k_rune_craft_range VALUES(1,0,3,1,200,310);
-INSERT INTO k_rune_craft_range VALUES(1,0,4,1,290,420);
-INSERT INTO k_rune_craft_range VALUES(1,0,5,1,400,580);
-INSERT INTO k_rune_craft_range VALUES(1,0,1,2,2,4);
-INSERT INTO k_rune_craft_range VALUES(1,0,2,2,3,7);
-INSERT INTO k_rune_craft_range VALUES(1,0,3,2,5,9);
-INSERT INTO k_rune_craft_range VALUES(1,0,4,2,7,11);
-INSERT INTO k_rune_craft_range VALUES(1,0,5,2,9,13);
-INSERT INTO k_rune_craft_range VALUES(1,0,1,3,8,12);
-INSERT INTO k_rune_craft_range VALUES(1,0,2,3,10,16);
-INSERT INTO k_rune_craft_range VALUES(1,0,3,3,15,23);
-INSERT INTO k_rune_craft_range VALUES(1,0,4,3,20,30);
-INSERT INTO k_rune_craft_range VALUES(1,0,5,3,28,40);
-INSERT INTO k_rune_craft_range VALUES(1,0,1,4,2,4);
-INSERT INTO k_rune_craft_range VALUES(1,0,2,4,3,7);
-INSERT INTO k_rune_craft_range VALUES(1,0,3,4,5,9);
-INSERT INTO k_rune_craft_range VALUES(1,0,4,4,7,11);
-INSERT INTO k_rune_craft_range VALUES(1,0,5,4,9,13);
-INSERT INTO k_rune_craft_range VALUES(1,0,1,5,8,12);
-INSERT INTO k_rune_craft_range VALUES(1,0,2,5,10,16);
-INSERT INTO k_rune_craft_range VALUES(1,0,3,5,15,23);
-INSERT INTO k_rune_craft_range VALUES(1,0,4,5,20,30);
-INSERT INTO k_rune_craft_range VALUES(1,0,5,5,28,40);
-INSERT INTO k_rune_craft_range VALUES(1,0,1,6,2,4);
-INSERT INTO k_rune_craft_range VALUES(1,0,2,6,3,7);
-INSERT INTO k_rune_craft_range VALUES(1,0,3,6,5,9);
-INSERT INTO k_rune_craft_range VALUES(1,0,4,6,7,11);
-INSERT INTO k_rune_craft_range VALUES(1,0,5,6,9,13);
-INSERT INTO k_rune_craft_range VALUES(1,0,1,8,1,3);
-INSERT INTO k_rune_craft_range VALUES(1,0,2,8,2,4);
-INSERT INTO k_rune_craft_range VALUES(1,0,3,8,3,6);
-INSERT INTO k_rune_craft_range VALUES(1,0,4,8,5,8);
-INSERT INTO k_rune_craft_range VALUES(1,0,5,8,7,10);
-INSERT INTO k_rune_craft_range VALUES(1,0,1,9,1,3);
-INSERT INTO k_rune_craft_range VALUES(1,0,2,9,2,4);
-INSERT INTO k_rune_craft_range VALUES(1,0,3,9,3,5);
-INSERT INTO k_rune_craft_range VALUES(1,0,4,9,4,7);
-INSERT INTO k_rune_craft_range VALUES(1,0,5,9,6,9);
-INSERT INTO k_rune_craft_range VALUES(1,0,1,10,2,4);
-INSERT INTO k_rune_craft_range VALUES(1,0,2,10,3,5);
-INSERT INTO k_rune_craft_range VALUES(1,0,3,10,4,6);
-INSERT INTO k_rune_craft_range VALUES(1,0,4,10,5,8);
-INSERT INTO k_rune_craft_range VALUES(1,0,5,10,7,10);
-INSERT INTO k_rune_craft_range VALUES(1,0,1,11,2,4);
-INSERT INTO k_rune_craft_range VALUES(1,0,2,11,3,6);
-INSERT INTO k_rune_craft_range VALUES(1,0,3,11,5,8);
-INSERT INTO k_rune_craft_range VALUES(1,0,4,11,6,9);
-INSERT INTO k_rune_craft_range VALUES(1,0,5,11,8,11);
-INSERT INTO k_rune_craft_range VALUES(1,0,1,12,2,4);
-INSERT INTO k_rune_craft_range VALUES(1,0,2,12,3,6);
-INSERT INTO k_rune_craft_range VALUES(1,0,3,12,5,8);
-INSERT INTO k_rune_craft_range VALUES(1,0,4,12,6,9);
-INSERT INTO k_rune_craft_range VALUES(1,0,5,12,8,11);
-INSERT INTO k_rune_craft_range VALUES(0,1,1,1,80,120);
-INSERT INTO k_rune_craft_range VALUES(0,1,2,1,100,200);
-INSERT INTO k_rune_craft_range VALUES(0,1,3,1,180,250);
-INSERT INTO k_rune_craft_range VALUES(0,1,4,1,230,450);
-INSERT INTO k_rune_craft_range VALUES(0,1,5,1,430,550);
-INSERT INTO k_rune_craft_range VALUES(0,1,1,2,1,3);
-INSERT INTO k_rune_craft_range VALUES(0,1,2,2,2,5);
-INSERT INTO k_rune_craft_range VALUES(0,1,3,2,3,6);
-INSERT INTO k_rune_craft_range VALUES(0,1,4,2,4,7);
-INSERT INTO k_rune_craft_range VALUES(0,1,5,2,5,10);
-INSERT INTO k_rune_craft_range VALUES(0,1,1,3,4,8);
-INSERT INTO k_rune_craft_range VALUES(0,1,2,3,6,12);
-INSERT INTO k_rune_craft_range VALUES(0,1,3,3,10,18);
-INSERT INTO k_rune_craft_range VALUES(0,1,4,3,12,22);
-INSERT INTO k_rune_craft_range VALUES(0,1,5,3,18,30);
-INSERT INTO k_rune_craft_range VALUES(0,1,1,4,1,3);
-INSERT INTO k_rune_craft_range VALUES(0,1,2,4,2,5);
-INSERT INTO k_rune_craft_range VALUES(0,1,3,4,3,6);
-INSERT INTO k_rune_craft_range VALUES(0,1,4,4,4,7);
-INSERT INTO k_rune_craft_range VALUES(0,1,5,4,5,10);
-INSERT INTO k_rune_craft_range VALUES(0,1,1,5,4,8);
-INSERT INTO k_rune_craft_range VALUES(0,1,2,5,6,12);
-INSERT INTO k_rune_craft_range VALUES(0,1,3,5,10,18);
-INSERT INTO k_rune_craft_range VALUES(0,1,4,5,12,22);
-INSERT INTO k_rune_craft_range VALUES(0,1,5,5,18,30);
-INSERT INTO k_rune_craft_range VALUES(0,1,1,6,1,3);
-INSERT INTO k_rune_craft_range VALUES(0,1,2,6,2,5);
-INSERT INTO k_rune_craft_range VALUES(0,1,3,6,3,6);
-INSERT INTO k_rune_craft_range VALUES(0,1,4,6,4,7);
-INSERT INTO k_rune_craft_range VALUES(0,1,5,6,5,10);
-INSERT INTO k_rune_craft_range VALUES(0,1,1,8,1,2);
-INSERT INTO k_rune_craft_range VALUES(0,1,2,8,1,2);
-INSERT INTO k_rune_craft_range VALUES(0,1,3,8,2,3);
-INSERT INTO k_rune_craft_range VALUES(0,1,4,8,3,4);
-INSERT INTO k_rune_craft_range VALUES(0,1,5,8,4,5);
-INSERT INTO k_rune_craft_range VALUES(0,1,1,9,1,2);
-INSERT INTO k_rune_craft_range VALUES(0,1,2,9,1,3);
-INSERT INTO k_rune_craft_range VALUES(0,1,3,9,2,4);
-INSERT INTO k_rune_craft_range VALUES(0,1,4,9,3,5);
-INSERT INTO k_rune_craft_range VALUES(0,1,5,9,4,6);
-INSERT INTO k_rune_craft_range VALUES(0,1,1,10,1,3);
-INSERT INTO k_rune_craft_range VALUES(0,1,2,10,2,4);
-INSERT INTO k_rune_craft_range VALUES(0,1,3,10,2,5);
-INSERT INTO k_rune_craft_range VALUES(0,1,4,10,3,5);
-INSERT INTO k_rune_craft_range VALUES(0,1,5,10,4,7);
-INSERT INTO k_rune_craft_range VALUES(0,1,1,11,1,3);
-INSERT INTO k_rune_craft_range VALUES(0,1,2,11,2,4);
-INSERT INTO k_rune_craft_range VALUES(0,1,3,11,2,5);
-INSERT INTO k_rune_craft_range VALUES(0,1,4,11,3,7);
-INSERT INTO k_rune_craft_range VALUES(0,1,5,11,4,8);
-INSERT INTO k_rune_craft_range VALUES(0,1,1,12,1,3);
-INSERT INTO k_rune_craft_range VALUES(0,1,2,12,2,4);
-INSERT INTO k_rune_craft_range VALUES(0,1,3,12,2,5);
-INSERT INTO k_rune_craft_range VALUES(0,1,4,12,3,7);
-INSERT INTO k_rune_craft_range VALUES(0,1,5,12,4,8);
-INSERT INTO k_rune_craft_range VALUES(1,1,1,1,100,150);
-INSERT INTO k_rune_craft_range VALUES(1,1,2,1,130,220);
-INSERT INTO k_rune_craft_range VALUES(1,1,3,1,200,310);
-INSERT INTO k_rune_craft_range VALUES(1,1,4,1,290,420);
-INSERT INTO k_rune_craft_range VALUES(1,1,5,1,400,580);
-INSERT INTO k_rune_craft_range VALUES(1,1,1,2,2,4);
-INSERT INTO k_rune_craft_range VALUES(1,1,2,2,3,7);
-INSERT INTO k_rune_craft_range VALUES(1,1,3,2,5,9);
-INSERT INTO k_rune_craft_range VALUES(1,1,4,2,7,11);
-INSERT INTO k_rune_craft_range VALUES(1,1,5,2,9,13);
-INSERT INTO k_rune_craft_range VALUES(1,1,1,3,8,12);
-INSERT INTO k_rune_craft_range VALUES(1,1,2,3,10,16);
-INSERT INTO k_rune_craft_range VALUES(1,1,3,3,15,23);
-INSERT INTO k_rune_craft_range VALUES(1,1,4,3,20,30);
-INSERT INTO k_rune_craft_range VALUES(1,1,5,3,28,40);
-INSERT INTO k_rune_craft_range VALUES(1,1,1,4,2,4);
-INSERT INTO k_rune_craft_range VALUES(1,1,2,4,3,7);
-INSERT INTO k_rune_craft_range VALUES(1,1,3,4,5,9);
-INSERT INTO k_rune_craft_range VALUES(1,1,4,4,7,11);
-INSERT INTO k_rune_craft_range VALUES(1,1,5,4,9,13);
-INSERT INTO k_rune_craft_range VALUES(1,1,1,5,8,12);
-INSERT INTO k_rune_craft_range VALUES(1,1,2,5,10,16);
-INSERT INTO k_rune_craft_range VALUES(1,1,3,5,15,23);
-INSERT INTO k_rune_craft_range VALUES(1,1,4,5,20,30);
-INSERT INTO k_rune_craft_range VALUES(1,1,5,5,28,40);
-INSERT INTO k_rune_craft_range VALUES(1,1,1,6,2,4);
-INSERT INTO k_rune_craft_range VALUES(1,1,2,6,3,7);
-INSERT INTO k_rune_craft_range VALUES(1,1,3,6,5,9);
-INSERT INTO k_rune_craft_range VALUES(1,1,4,6,7,11);
-INSERT INTO k_rune_craft_range VALUES(1,1,5,6,9,13);
-INSERT INTO k_rune_craft_range VALUES(1,1,1,8,1,3);
-INSERT INTO k_rune_craft_range VALUES(1,1,2,8,2,4);
-INSERT INTO k_rune_craft_range VALUES(1,1,3,8,3,6);
-INSERT INTO k_rune_craft_range VALUES(1,1,4,8,5,8);
-INSERT INTO k_rune_craft_range VALUES(1,1,5,8,7,10);
-INSERT INTO k_rune_craft_range VALUES(1,1,1,9,1,3);
-INSERT INTO k_rune_craft_range VALUES(1,1,2,9,2,4);
-INSERT INTO k_rune_craft_range VALUES(1,1,3,9,3,5);
-INSERT INTO k_rune_craft_range VALUES(1,1,4,9,4,7);
-INSERT INTO k_rune_craft_range VALUES(1,1,5,9,6,9);
-INSERT INTO k_rune_craft_range VALUES(1,1,1,10,2,4);
-INSERT INTO k_rune_craft_range VALUES(1,1,2,10,3,5);
-INSERT INTO k_rune_craft_range VALUES(1,1,3,10,4,6);
-INSERT INTO k_rune_craft_range VALUES(1,1,4,10,5,8);
-INSERT INTO k_rune_craft_range VALUES(1,1,5,10,7,10);
-INSERT INTO k_rune_craft_range VALUES(1,1,1,11,2,4);
-INSERT INTO k_rune_craft_range VALUES(1,1,2,11,3,6);
-INSERT INTO k_rune_craft_range VALUES(1,1,3,11,5,8);
-INSERT INTO k_rune_craft_range VALUES(1,1,4,11,6,9);
-INSERT INTO k_rune_craft_range VALUES(1,1,5,11,8,11);
-INSERT INTO k_rune_craft_range VALUES(1,1,1,12,2,4);
-INSERT INTO k_rune_craft_range VALUES(1,1,2,12,3,6);
-INSERT INTO k_rune_craft_range VALUES(1,1,3,12,5,8);
-INSERT INTO k_rune_craft_range VALUES(1,1,4,12,6,9);
-INSERT INTO k_rune_craft_range VALUES(1,1,5,12,8,11);
+INSERT INTO k_enchantment_range VALUES(0,0,1,1,80,120);
+INSERT INTO k_enchantment_range VALUES(0,0,2,1,100,200);
+INSERT INTO k_enchantment_range VALUES(0,0,3,1,180,250);
+INSERT INTO k_enchantment_range VALUES(0,0,4,1,230,450);
+INSERT INTO k_enchantment_range VALUES(0,0,5,1,430,550);
+INSERT INTO k_enchantment_range VALUES(0,0,1,2,1,3);
+INSERT INTO k_enchantment_range VALUES(0,0,2,2,2,5);
+INSERT INTO k_enchantment_range VALUES(0,0,3,2,3,6);
+INSERT INTO k_enchantment_range VALUES(0,0,4,2,4,7);
+INSERT INTO k_enchantment_range VALUES(0,0,5,2,5,10);
+INSERT INTO k_enchantment_range VALUES(0,0,1,3,4,8);
+INSERT INTO k_enchantment_range VALUES(0,0,2,3,6,12);
+INSERT INTO k_enchantment_range VALUES(0,0,3,3,10,18);
+INSERT INTO k_enchantment_range VALUES(0,0,4,3,12,22);
+INSERT INTO k_enchantment_range VALUES(0,0,5,3,18,30);
+INSERT INTO k_enchantment_range VALUES(0,0,1,4,1,3);
+INSERT INTO k_enchantment_range VALUES(0,0,2,4,2,5);
+INSERT INTO k_enchantment_range VALUES(0,0,3,4,3,6);
+INSERT INTO k_enchantment_range VALUES(0,0,4,4,4,7);
+INSERT INTO k_enchantment_range VALUES(0,0,5,4,5,10);
+INSERT INTO k_enchantment_range VALUES(0,0,1,5,4,8);
+INSERT INTO k_enchantment_range VALUES(0,0,2,5,6,12);
+INSERT INTO k_enchantment_range VALUES(0,0,3,5,10,18);
+INSERT INTO k_enchantment_range VALUES(0,0,4,5,12,22);
+INSERT INTO k_enchantment_range VALUES(0,0,5,5,18,30);
+INSERT INTO k_enchantment_range VALUES(0,0,1,6,1,3);
+INSERT INTO k_enchantment_range VALUES(0,0,2,6,2,5);
+INSERT INTO k_enchantment_range VALUES(0,0,3,6,3,6);
+INSERT INTO k_enchantment_range VALUES(0,0,4,6,4,7);
+INSERT INTO k_enchantment_range VALUES(0,0,5,6,5,10);
+INSERT INTO k_enchantment_range VALUES(0,0,1,8,1,2);
+INSERT INTO k_enchantment_range VALUES(0,0,2,8,1,2);
+INSERT INTO k_enchantment_range VALUES(0,0,3,8,2,3);
+INSERT INTO k_enchantment_range VALUES(0,0,4,8,3,4);
+INSERT INTO k_enchantment_range VALUES(0,0,5,8,4,5);
+INSERT INTO k_enchantment_range VALUES(0,0,1,9,1,2);
+INSERT INTO k_enchantment_range VALUES(0,0,2,9,1,3);
+INSERT INTO k_enchantment_range VALUES(0,0,3,9,2,4);
+INSERT INTO k_enchantment_range VALUES(0,0,4,9,3,5);
+INSERT INTO k_enchantment_range VALUES(0,0,5,9,4,6);
+INSERT INTO k_enchantment_range VALUES(0,0,1,10,1,3);
+INSERT INTO k_enchantment_range VALUES(0,0,2,10,2,4);
+INSERT INTO k_enchantment_range VALUES(0,0,3,10,2,5);
+INSERT INTO k_enchantment_range VALUES(0,0,4,10,3,5);
+INSERT INTO k_enchantment_range VALUES(0,0,5,10,4,7);
+INSERT INTO k_enchantment_range VALUES(0,0,1,11,1,3);
+INSERT INTO k_enchantment_range VALUES(0,0,2,11,2,4);
+INSERT INTO k_enchantment_range VALUES(0,0,3,11,2,5);
+INSERT INTO k_enchantment_range VALUES(0,0,4,11,3,7);
+INSERT INTO k_enchantment_range VALUES(0,0,5,11,4,8);
+INSERT INTO k_enchantment_range VALUES(0,0,1,12,1,3);
+INSERT INTO k_enchantment_range VALUES(0,0,2,12,2,4);
+INSERT INTO k_enchantment_range VALUES(0,0,3,12,2,5);
+INSERT INTO k_enchantment_range VALUES(0,0,4,12,3,7);
+INSERT INTO k_enchantment_range VALUES(0,0,5,12,4,8);
+INSERT INTO k_enchantment_range VALUES(1,0,1,1,100,150);
+INSERT INTO k_enchantment_range VALUES(1,0,2,1,130,220);
+INSERT INTO k_enchantment_range VALUES(1,0,3,1,200,310);
+INSERT INTO k_enchantment_range VALUES(1,0,4,1,290,420);
+INSERT INTO k_enchantment_range VALUES(1,0,5,1,400,580);
+INSERT INTO k_enchantment_range VALUES(1,0,1,2,2,4);
+INSERT INTO k_enchantment_range VALUES(1,0,2,2,3,7);
+INSERT INTO k_enchantment_range VALUES(1,0,3,2,5,9);
+INSERT INTO k_enchantment_range VALUES(1,0,4,2,7,11);
+INSERT INTO k_enchantment_range VALUES(1,0,5,2,9,13);
+INSERT INTO k_enchantment_range VALUES(1,0,1,3,8,12);
+INSERT INTO k_enchantment_range VALUES(1,0,2,3,10,16);
+INSERT INTO k_enchantment_range VALUES(1,0,3,3,15,23);
+INSERT INTO k_enchantment_range VALUES(1,0,4,3,20,30);
+INSERT INTO k_enchantment_range VALUES(1,0,5,3,28,40);
+INSERT INTO k_enchantment_range VALUES(1,0,1,4,2,4);
+INSERT INTO k_enchantment_range VALUES(1,0,2,4,3,7);
+INSERT INTO k_enchantment_range VALUES(1,0,3,4,5,9);
+INSERT INTO k_enchantment_range VALUES(1,0,4,4,7,11);
+INSERT INTO k_enchantment_range VALUES(1,0,5,4,9,13);
+INSERT INTO k_enchantment_range VALUES(1,0,1,5,8,12);
+INSERT INTO k_enchantment_range VALUES(1,0,2,5,10,16);
+INSERT INTO k_enchantment_range VALUES(1,0,3,5,15,23);
+INSERT INTO k_enchantment_range VALUES(1,0,4,5,20,30);
+INSERT INTO k_enchantment_range VALUES(1,0,5,5,28,40);
+INSERT INTO k_enchantment_range VALUES(1,0,1,6,2,4);
+INSERT INTO k_enchantment_range VALUES(1,0,2,6,3,7);
+INSERT INTO k_enchantment_range VALUES(1,0,3,6,5,9);
+INSERT INTO k_enchantment_range VALUES(1,0,4,6,7,11);
+INSERT INTO k_enchantment_range VALUES(1,0,5,6,9,13);
+INSERT INTO k_enchantment_range VALUES(1,0,1,8,1,3);
+INSERT INTO k_enchantment_range VALUES(1,0,2,8,2,4);
+INSERT INTO k_enchantment_range VALUES(1,0,3,8,3,6);
+INSERT INTO k_enchantment_range VALUES(1,0,4,8,5,8);
+INSERT INTO k_enchantment_range VALUES(1,0,5,8,7,10);
+INSERT INTO k_enchantment_range VALUES(1,0,1,9,1,3);
+INSERT INTO k_enchantment_range VALUES(1,0,2,9,2,4);
+INSERT INTO k_enchantment_range VALUES(1,0,3,9,3,5);
+INSERT INTO k_enchantment_range VALUES(1,0,4,9,4,7);
+INSERT INTO k_enchantment_range VALUES(1,0,5,9,6,9);
+INSERT INTO k_enchantment_range VALUES(1,0,1,10,2,4);
+INSERT INTO k_enchantment_range VALUES(1,0,2,10,3,5);
+INSERT INTO k_enchantment_range VALUES(1,0,3,10,4,6);
+INSERT INTO k_enchantment_range VALUES(1,0,4,10,5,8);
+INSERT INTO k_enchantment_range VALUES(1,0,5,10,7,10);
+INSERT INTO k_enchantment_range VALUES(1,0,1,11,2,4);
+INSERT INTO k_enchantment_range VALUES(1,0,2,11,3,6);
+INSERT INTO k_enchantment_range VALUES(1,0,3,11,5,8);
+INSERT INTO k_enchantment_range VALUES(1,0,4,11,6,9);
+INSERT INTO k_enchantment_range VALUES(1,0,5,11,8,11);
+INSERT INTO k_enchantment_range VALUES(1,0,1,12,2,4);
+INSERT INTO k_enchantment_range VALUES(1,0,2,12,3,6);
+INSERT INTO k_enchantment_range VALUES(1,0,3,12,5,8);
+INSERT INTO k_enchantment_range VALUES(1,0,4,12,6,9);
+INSERT INTO k_enchantment_range VALUES(1,0,5,12,8,11);
+INSERT INTO k_enchantment_range VALUES(0,1,1,1,80,120);
+INSERT INTO k_enchantment_range VALUES(0,1,2,1,100,200);
+INSERT INTO k_enchantment_range VALUES(0,1,3,1,180,250);
+INSERT INTO k_enchantment_range VALUES(0,1,4,1,230,450);
+INSERT INTO k_enchantment_range VALUES(0,1,5,1,430,550);
+INSERT INTO k_enchantment_range VALUES(0,1,1,2,1,3);
+INSERT INTO k_enchantment_range VALUES(0,1,2,2,2,5);
+INSERT INTO k_enchantment_range VALUES(0,1,3,2,3,6);
+INSERT INTO k_enchantment_range VALUES(0,1,4,2,4,7);
+INSERT INTO k_enchantment_range VALUES(0,1,5,2,5,10);
+INSERT INTO k_enchantment_range VALUES(0,1,1,3,4,8);
+INSERT INTO k_enchantment_range VALUES(0,1,2,3,6,12);
+INSERT INTO k_enchantment_range VALUES(0,1,3,3,10,18);
+INSERT INTO k_enchantment_range VALUES(0,1,4,3,12,22);
+INSERT INTO k_enchantment_range VALUES(0,1,5,3,18,30);
+INSERT INTO k_enchantment_range VALUES(0,1,1,4,1,3);
+INSERT INTO k_enchantment_range VALUES(0,1,2,4,2,5);
+INSERT INTO k_enchantment_range VALUES(0,1,3,4,3,6);
+INSERT INTO k_enchantment_range VALUES(0,1,4,4,4,7);
+INSERT INTO k_enchantment_range VALUES(0,1,5,4,5,10);
+INSERT INTO k_enchantment_range VALUES(0,1,1,5,4,8);
+INSERT INTO k_enchantment_range VALUES(0,1,2,5,6,12);
+INSERT INTO k_enchantment_range VALUES(0,1,3,5,10,18);
+INSERT INTO k_enchantment_range VALUES(0,1,4,5,12,22);
+INSERT INTO k_enchantment_range VALUES(0,1,5,5,18,30);
+INSERT INTO k_enchantment_range VALUES(0,1,1,6,1,3);
+INSERT INTO k_enchantment_range VALUES(0,1,2,6,2,5);
+INSERT INTO k_enchantment_range VALUES(0,1,3,6,3,6);
+INSERT INTO k_enchantment_range VALUES(0,1,4,6,4,7);
+INSERT INTO k_enchantment_range VALUES(0,1,5,6,5,10);
+INSERT INTO k_enchantment_range VALUES(0,1,1,8,1,2);
+INSERT INTO k_enchantment_range VALUES(0,1,2,8,1,2);
+INSERT INTO k_enchantment_range VALUES(0,1,3,8,2,3);
+INSERT INTO k_enchantment_range VALUES(0,1,4,8,3,4);
+INSERT INTO k_enchantment_range VALUES(0,1,5,8,4,5);
+INSERT INTO k_enchantment_range VALUES(0,1,1,9,1,2);
+INSERT INTO k_enchantment_range VALUES(0,1,2,9,1,3);
+INSERT INTO k_enchantment_range VALUES(0,1,3,9,2,4);
+INSERT INTO k_enchantment_range VALUES(0,1,4,9,3,5);
+INSERT INTO k_enchantment_range VALUES(0,1,5,9,4,6);
+INSERT INTO k_enchantment_range VALUES(0,1,1,10,1,3);
+INSERT INTO k_enchantment_range VALUES(0,1,2,10,2,4);
+INSERT INTO k_enchantment_range VALUES(0,1,3,10,2,5);
+INSERT INTO k_enchantment_range VALUES(0,1,4,10,3,5);
+INSERT INTO k_enchantment_range VALUES(0,1,5,10,4,7);
+INSERT INTO k_enchantment_range VALUES(0,1,1,11,1,3);
+INSERT INTO k_enchantment_range VALUES(0,1,2,11,2,4);
+INSERT INTO k_enchantment_range VALUES(0,1,3,11,2,5);
+INSERT INTO k_enchantment_range VALUES(0,1,4,11,3,7);
+INSERT INTO k_enchantment_range VALUES(0,1,5,11,4,8);
+INSERT INTO k_enchantment_range VALUES(0,1,1,12,1,3);
+INSERT INTO k_enchantment_range VALUES(0,1,2,12,2,4);
+INSERT INTO k_enchantment_range VALUES(0,1,3,12,2,5);
+INSERT INTO k_enchantment_range VALUES(0,1,4,12,3,7);
+INSERT INTO k_enchantment_range VALUES(0,1,5,12,4,8);
+INSERT INTO k_enchantment_range VALUES(1,1,1,1,100,150);
+INSERT INTO k_enchantment_range VALUES(1,1,2,1,130,220);
+INSERT INTO k_enchantment_range VALUES(1,1,3,1,200,310);
+INSERT INTO k_enchantment_range VALUES(1,1,4,1,290,420);
+INSERT INTO k_enchantment_range VALUES(1,1,5,1,400,580);
+INSERT INTO k_enchantment_range VALUES(1,1,1,2,2,4);
+INSERT INTO k_enchantment_range VALUES(1,1,2,2,3,7);
+INSERT INTO k_enchantment_range VALUES(1,1,3,2,5,9);
+INSERT INTO k_enchantment_range VALUES(1,1,4,2,7,11);
+INSERT INTO k_enchantment_range VALUES(1,1,5,2,9,13);
+INSERT INTO k_enchantment_range VALUES(1,1,1,3,8,12);
+INSERT INTO k_enchantment_range VALUES(1,1,2,3,10,16);
+INSERT INTO k_enchantment_range VALUES(1,1,3,3,15,23);
+INSERT INTO k_enchantment_range VALUES(1,1,4,3,20,30);
+INSERT INTO k_enchantment_range VALUES(1,1,5,3,28,40);
+INSERT INTO k_enchantment_range VALUES(1,1,1,4,2,4);
+INSERT INTO k_enchantment_range VALUES(1,1,2,4,3,7);
+INSERT INTO k_enchantment_range VALUES(1,1,3,4,5,9);
+INSERT INTO k_enchantment_range VALUES(1,1,4,4,7,11);
+INSERT INTO k_enchantment_range VALUES(1,1,5,4,9,13);
+INSERT INTO k_enchantment_range VALUES(1,1,1,5,8,12);
+INSERT INTO k_enchantment_range VALUES(1,1,2,5,10,16);
+INSERT INTO k_enchantment_range VALUES(1,1,3,5,15,23);
+INSERT INTO k_enchantment_range VALUES(1,1,4,5,20,30);
+INSERT INTO k_enchantment_range VALUES(1,1,5,5,28,40);
+INSERT INTO k_enchantment_range VALUES(1,1,1,6,2,4);
+INSERT INTO k_enchantment_range VALUES(1,1,2,6,3,7);
+INSERT INTO k_enchantment_range VALUES(1,1,3,6,5,9);
+INSERT INTO k_enchantment_range VALUES(1,1,4,6,7,11);
+INSERT INTO k_enchantment_range VALUES(1,1,5,6,9,13);
+INSERT INTO k_enchantment_range VALUES(1,1,1,8,1,3);
+INSERT INTO k_enchantment_range VALUES(1,1,2,8,2,4);
+INSERT INTO k_enchantment_range VALUES(1,1,3,8,3,6);
+INSERT INTO k_enchantment_range VALUES(1,1,4,8,5,8);
+INSERT INTO k_enchantment_range VALUES(1,1,5,8,7,10);
+INSERT INTO k_enchantment_range VALUES(1,1,1,9,1,3);
+INSERT INTO k_enchantment_range VALUES(1,1,2,9,2,4);
+INSERT INTO k_enchantment_range VALUES(1,1,3,9,3,5);
+INSERT INTO k_enchantment_range VALUES(1,1,4,9,4,7);
+INSERT INTO k_enchantment_range VALUES(1,1,5,9,6,9);
+INSERT INTO k_enchantment_range VALUES(1,1,1,10,2,4);
+INSERT INTO k_enchantment_range VALUES(1,1,2,10,3,5);
+INSERT INTO k_enchantment_range VALUES(1,1,3,10,4,6);
+INSERT INTO k_enchantment_range VALUES(1,1,4,10,5,8);
+INSERT INTO k_enchantment_range VALUES(1,1,5,10,7,10);
+INSERT INTO k_enchantment_range VALUES(1,1,1,11,2,4);
+INSERT INTO k_enchantment_range VALUES(1,1,2,11,3,6);
+INSERT INTO k_enchantment_range VALUES(1,1,3,11,5,8);
+INSERT INTO k_enchantment_range VALUES(1,1,4,11,6,9);
+INSERT INTO k_enchantment_range VALUES(1,1,5,11,8,11);
+INSERT INTO k_enchantment_range VALUES(1,1,1,12,2,4);
+INSERT INTO k_enchantment_range VALUES(1,1,2,12,3,6);
+INSERT INTO k_enchantment_range VALUES(1,1,3,12,5,8);
+INSERT INTO k_enchantment_range VALUES(1,1,4,12,6,9);
+INSERT INTO k_enchantment_range VALUES(1,1,5,12,8,11);
 CREATE TABLE k_decoration_level(
     decoration INT NOT NULL REFERENCES k_decoration(id),
     level INT NOT NULL CHECK(level BETWEEN 1 AND 10),

+ 5 - 3
install_data/install_data.sql

@@ -219,7 +219,7 @@ CREATE TABLE guild_member(
     in_war INT NOT NULL CHECK(in_war IN (0, 1)),
     has_defense INT NOT NULL CHECK(has_defense IN (0, 1))
 );
-CREATE TABLE rune_craft(
+CREATE TABLE enchantment(
     uid INT NOT NULL REFERENCES player(id),
     id INT NOT NULL PRIMARY KEY,
     type INT NOT NULL,
@@ -237,10 +237,10 @@ CREATE TABLE run_party(
     front INT NOT NULL DEFAULT 0,
     PRIMARY KEY (run, unit)
 );
-CREATE TABLE run_drop_rune_craft(
+CREATE TABLE run_drop_enchantment(
     run INT NOT NULL REFERENCES run(id),
     id INT NOT NULL PRIMARY KEY,
-    type INT NOT NULL REFERENCES k_rune_craft_type(id), -- TODO: Duplicated??
+    type INT NOT NULL REFERENCES k_enchantment(id), -- TODO: Duplicated??
     quality INT NOT NULL REFERENCES k_quality(id),
     rune INT NOT NULL REFERENCES k_rune_set(id),
     stat INT NOT NULL REFERENCES k_rune_stat(id),
@@ -286,6 +286,8 @@ CREATE TABLE run(
     mana INT NOT NULL CHECK(mana >= 0),
     energy INT NOT NULL CHECK(energy >= 0),
     crystal INT NOT NULL CHECK(crystal >= 0),
+    guild_points NOT NULL CHECK(guild_points >= 0),
+    honor_points NOT NULL CHECK(honor_points >= 0),
     helper INT NOT NULL CHECK(helper IN (0, 1)),
     score INT,
     rank TEXT

+ 0 - 0
public/css/report_runecraft.css → public/css/report_rune_enchantment.css


+ 13 - 0
public/css/runs.css

@@ -136,6 +136,7 @@ table#runs td.reward div.item span.item{
     width: 2.6em;
     text-align: right;
     text-shadow: 0 0 0.3em #000, 0 0 0.3em #000, 0 0 0.3em #000, 0 0 0.2em #000, 0 0 0.1em #000, 0 0 0.1em #000;
+    pointer-events: none;
 }
 
 table#runs td.reward div.item_rune table.rune{
@@ -150,6 +151,18 @@ table#runs td.reward div.item_rune:hover table.rune{
     display: block;
 }
 
+table#runs td.reward div.item_enchantment div.enchantment_panel{
+    display: none;
+    position: absolute;
+    right: 1.8em;
+    bottom: 0;
+    font-size: 75%;
+}
+
+table#runs td.reward div.item_enchantment:hover div.enchantment_panel{
+    display: block;
+}
+
 table#runs td.action img.save_team{
     width: 1em;
     height: 1em;

+ 19 - 19
public/css/ui.css

@@ -1073,9 +1073,9 @@ table.artifact td.details table.table_details span.quality.quality_legend{backgr
 
 
 /**
- * Craft item panel
+ * Enchantment panel
  */
-div.craft_panel{
+div.enchantment_panel{
     width: 4em;
     height: 4em;
     border: 0.1em solid #3c2929;
@@ -1084,22 +1084,22 @@ div.craft_panel{
     text-align: center;
     display: inline-block;
 }
-div.craft_panel.craft_quality_normal{ /* Normal */
+div.enchantment_panel.enchantment_quality_normal{ /* Normal */
     background-image: repeating-radial-gradient(#ffffff, #cccccc 30%, #ffffff 60%);
 }
-div.craft_panel.craft_quality_magic{ /* Magic */
+div.enchantment_panel.enchantment_quality_magic{ /* Magic */
     background-image: repeating-radial-gradient(#aaffaa, #88cc88 30%, #aaffaa 60%);
 }
-div.craft_panel.craft_quality_rare{ /* Rare */
+div.enchantment_panel.enchantment_quality_rare{ /* Rare */
     background-image: repeating-radial-gradient(#aaaaff, #8888cc 30%, #aaaaff 60%);
 }
-div.craft_panel.craft_quality_hero{ /* Hero */
+div.enchantment_panel.enchantment_quality_hero{ /* Hero */
     background-image: repeating-radial-gradient(#ffaacc, #cc88aa 30%, #ffaacc 60%);
 }
-div.craft_panel.craft_quality_legend{ /* Legend */
+div.enchantment_panel.enchantment_quality_legend{ /* Legend */
     background-image: repeating-radial-gradient(#f48200, #b76201 30%, #f48200 60%);
 }
-div.craft_panel img.craft_base{
+div.enchantment_panel img.enchantment_base{
     position: absolute;
     width: 4em;
     height: 4em;
@@ -1107,20 +1107,20 @@ div.craft_panel img.craft_base{
     top: 0.1em;
     left: 0.5em;
 }
-div.craft_panel img.craft_icon{
+div.enchantment_panel img.enchantment_icon{
     position: absolute;
     width: 1.5em;
     height: 1.5em;
     top: 0.8em;
     left: 1.25em;
 }
-div.craft_panel.craft_ancient_1 img.craft_base{filter: brightness(150%) contrast(90%) saturate(100%) drop-shadow(0 0 0.3em #ffffff) drop-shadow(0 0 0.1em #ffffff);}
-div.craft_panel.craft_quality_normal img.craft_base{filter: sepia(100%) saturate(500%) grayscale(100%);}
-div.craft_panel.craft_quality_magic img.craft_base{filter: sepia(100%) saturate(500%) hue-rotate(60deg);}
-div.craft_panel.craft_quality_rare img.craft_base{filter: sepia(100%) saturate(500%) hue-rotate(120deg);}
-div.craft_panel.craft_quality_hero img.craft_base{filter: sepia(100%) saturate(500%) hue-rotate(230deg);}
-div.craft_panel.craft_quality_legend img.craft_base{filter: sepia(100%) saturate(500%) hue-rotate(330deg);}
-div.craft_panel span.craft_stat{
+div.enchantment_panel.enchantment_ancient_1 img.enchantment_base{filter: brightness(150%) contrast(90%) saturate(100%) drop-shadow(0 0 0.3em #ffffff) drop-shadow(0 0 0.1em #ffffff);}
+div.enchantment_panel.enchantment_quality_normal img.enchantment_base{filter: sepia(100%) saturate(500%) grayscale(100%);}
+div.enchantment_panel.enchantment_quality_magic img.enchantment_base{filter: sepia(100%) saturate(500%) hue-rotate(60deg);}
+div.enchantment_panel.enchantment_quality_rare img.enchantment_base{filter: sepia(100%) saturate(500%) hue-rotate(120deg);}
+div.enchantment_panel.enchantment_quality_hero img.enchantment_base{filter: sepia(100%) saturate(500%) hue-rotate(230deg);}
+div.enchantment_panel.enchantment_quality_legend img.enchantment_base{filter: sepia(100%) saturate(500%) hue-rotate(330deg);}
+div.enchantment_panel span.enchantment_stat{
     display: block;
     width: 100%;
     text-align: center;
@@ -1130,17 +1130,17 @@ div.craft_panel span.craft_stat{
     bottom: 0;
     font-family: monospace;
 }
-div.craft_panel span.craft_stat span.craft_stat_name{
+div.enchantment_panel span.enchantment_stat span.enchantment_stat_name{
     display: block;
     margin: 0;
 }
-div.craft_panel span.craft_stat span.craft_stat_value{
+div.enchantment_panel span.enchantment_stat span.enchantment_stat_value{
     display: block;
     font-weight: bold;
     margin: 0;
 }
 
-div.craft_panel span.craft_amount{
+div.enchantment_panel span.enchantment_amount{
     display: block;
     position: absolute;
     top: 0;

+ 72 - 61
swex-plugin/swdb.js

@@ -1,14 +1,3 @@
-/**
- * 
- * List of processed commands:
- * 
- * - HubUserLogin
- * - BattleDungeonResult
- * - BattleScenarioResult
- * - BattleDimensionHoleDungeonResult
- * - BattleTrialTowerResult_v2
- * 
- */
 const dateFormat = require('/usr/local/lib/node_modules/dateformat');
 
 var querystring = require('querystring');
@@ -81,6 +70,8 @@ module.exports = {
     processCommand(command, req, res){
         // Process the command
         var apiCommand = "";
+        var method = "";
+        var params = {'key': config.Config.Plugins[this.pluginName].apiKey};
         var success = null;
         var start = false;
         switch (command){
@@ -91,55 +82,90 @@ module.exports = {
             case 'BattleDimensionHoleDungeonStart':
             case 'BattleTrialTowerStart_v2':
             case 'BattleRiftDungeonStart':
+            case 'BattleRiftOfWorldsRaidStart':
                 this.eventStartReq = req;
                 this.eventStartRes = res;
                 break;
 
             // Profile login
             case 'HubUserLogin':
-                apiCommand = "upload_profile";
-                success = "Profile uploaded sucesfully!"
+                apiCommand = "profile/";
+                method = "PUT";
+                success = "Profile uploaded sucesfully!";
+                params['response'] = JSON.stringify(res);
                 break;
             // Catalog status
             case 'GetUnitCollection':
-                apiCommand = "update_collection";
-                success = "Collection updated sucesfully!"
+                apiCommand = "profile/";
+                method = "PUT";
+                success = "Collection succesfully updated!";
+                params['response'] = JSON.stringify(res);
                 break;
             // Profile logbook
             case 'GetLobbyWizardLog':
-                apiCommand = "update_logbook";
-                success = "Logbook updated succesfully!";
+                apiCommand = "profile/";
+                method = "PUT";
+                success = "Records succesfully updated!";
+                params['response'] = JSON.stringify(res);
                 break;
-            // Battle runs
+            // Cairos Dungeon run
             case 'BattleDungeonResult_V2':
-                apiCommand = "upload_run_dungeon";
+                apiCommand = "run/";
+                method = "POST";
                 success = "Cairos run logged sucesfully!"
+                params['result_response'] = JSON.stringify(res);
+                params['result_request'] = JSON.stringify(req);
                 break;
+            // Dimension Hole run
+            case 'BattleDimensionHoleDungeonResult_v2':
+                apiCommand = "run/";
+                method = "POST";
+                success = "Dimension Hole run logged sucesfully!"
+                params['result_response'] = JSON.stringify(res);
+                params['result_request'] = JSON.stringify(req);
+                break;
+            // Rift raid run
+            case 'BattleRiftOfWorldsRaidResult':
+                apiCommand = "run/";
+                method = "POST";
+                success = "Rift Rarid run logged sucesfully!"
+                params['result_response'] = JSON.stringify(res);
+                params['start_response'] = JSON.stringify(this.eventStartRes);
+                break;
+            // Scenario run
             case 'BattleScenarioResult':
-                apiCommand = "upload_run_scenario";
+                apiCommand = "run/";
+                method = "POST";
                 success = "Scenario run logged sucesfully!"
+                params['result_response'] = JSON.stringify(res);
+                params['start_response'] = JSON.stringify(this.eventStartRes);
                 break;
-            case 'BattleDimensionHoleDungeonResult':
-                apiCommand = "upload_run_dimension";
-                success = "Dimension Hole run logged sucesfully!"
+            // Arena run
+            case 'BattleArenaResult':
+                apiCommand = "run/";
+                method = "POST";
+                success = "Arena run logged sucesfully!"
+                params['result_response'] = JSON.stringify(res);
                 break;
-            case 'BattleTrialTowerResult_v2':
-                apiCommand = "upload_run_toa";
-                success = "TOA run logged sucesfully!"
+            // Dimensional Rift run
+            case 'BattleDarkPortalResult':
+                apiCommand = "run/";
+                method = "POST";
+                success = "Dimensional Rift run logged sucesfully!"
+                params['result_response'] = JSON.stringify(res);
                 break;
-            case 'BattleRiftDungeonResult':
-                apiCommand = "upload_run_rift";
-                success = "Rift Dungeon run logged sucesfully!"
-                start = true;
+            case 'BattleTrialTowerResult_v2':
+                //command = "log_run_toa";
+                //success = "TOA run logged sucesfully!"
                 break;
         }
         if (apiCommand != ""){
-            this.command(apiCommand, req, res, start, success);
+            this.command(apiCommand, method, params, success);
         }
     },
 
     /**
-     * Calls an arbitrary command on the SWDB APIv2.
+     * Calls an arbitrary command on the SWDB APIv3.
      *
      * @param command Command nade.
      * @param req The full request data.
@@ -147,50 +173,35 @@ module.exports = {
      * @param start Indicates if event start request and response are needed.
      * @param success Optional. Message to show on success.
      */
-    command(command, req, res, start = false, success = null){
+    command(command, method, params, success = null){
 
         // 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;
-        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
-            });
-        }
+        var data = querystring.stringify(params);
         // Configure POST requets
         var post_options = {
             host: config.Config.Plugins[this.pluginName].host,
             port: config.Config.Plugins[this.pluginName].port,
-            path: '/API/v2/' + command,
-            method: 'POST',
+            path: '/API/v3/' + command,
+            method: method,
             headers: {
                 'Content-Type': 'application/x-www-form-urlencoded',
-                'Content-Length': Buffer.byteLength(post_data)
+                'Content-Length': Buffer.byteLength(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){
+        var req = http.request(post_options, (function(res) {
+            res.setEncoding('utf8');
+            res.on('data', (function (body) {
+                switch (res.statusCode){
                     case 200:
                     case 201:
-                        this.log('success', `${success} [${post_res.statusCode}]`);
+                        this.log('success', `${success} [${res.statusCode}]`);
                         break;
                     case 400:
                         this.log('error', `[HTTP/1.1 400] There were errors importing the data: ${JSON.stringify(body)}`);
@@ -202,13 +213,13 @@ module.exports = {
                         this.log('error', '[HTTP/1.1 500] The server returned an error.');
                         break;
                     default:
-                        this.log('error', `[HTTP/1.1 ${post_res.statusCode}] Unexpected error.`);
+                        this.log('error', `[HTTP/1.1 ${res.statusCode}] Unexpected error.`);
                 }
             }).bind(this));
         }).bind(this));
         // Perform the request
-        post_req.write(post_data);
-        post_req.end();
+        req.write(data);
+        req.end();
     },
 
     /**