Separate wild searches out by world

This commit is contained in:
Justin C. Miller
2021-09-18 18:05:06 -07:00
parent 2d507b2fad
commit 51102e6dbe
18 changed files with 195 additions and 69 deletions

View File

@@ -2,67 +2,40 @@ package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"sync"
"github.com/gorilla/mux"
"github.com/jmoiron/sqlx"
)
type apiHandler struct {
lock sync.Mutex
loader *Loader
tamedStmt *sqlx.Stmt
wildStmt *sqlx.Stmt
type api struct {
lock sync.Mutex
loader *Loader
tamedStmt *sqlx.Stmt
wildStmt *sqlx.Stmt
worldsStmt *sqlx.Stmt
}
func (ah *apiHandler) getDinos(tamed bool) ([]dinoResult, error) {
func (ah *api) getTames(w http.ResponseWriter, r *http.Request) {
ah.loader.lock.Lock()
defer ah.loader.lock.Unlock()
db := ah.loader.db
query := getWildDinos
stmt := ah.wildStmt
if tamed {
query = getTamedDinos
stmt = ah.tamedStmt
}
var err error
if stmt == nil {
stmt, err = db.Preparex(query)
if ah.tamedStmt == nil {
ah.tamedStmt, err = db.Preparex(getTamedDinos)
if err != nil {
return nil, fmt.Errorf("Database Error: %w", err)
}
if tamed {
ah.tamedStmt = stmt
} else {
ah.wildStmt = stmt
log.Printf("Error preparing statement: %s", err)
http.Error(w, "Database Error", http.StatusInternalServerError)
return
}
}
var result []dinoResult
err = stmt.Select(&result)
if err != nil {
return nil, fmt.Errorf("Database Error: %w", err)
}
return result, nil
}
func (ah *apiHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
err := r.ParseForm()
if err != nil {
log.Printf("Error parsing query: %s", err)
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
tamed := r.Form.Get("type") != "wild"
result, err := ah.getDinos(tamed)
err = ah.tamedStmt.Select(&result)
if err != nil {
log.Printf("Error loading dinos: %s", err)
http.Error(w, "Database Error", http.StatusInternalServerError)
@@ -79,21 +52,94 @@ func (ah *apiHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.Write(data)
}
func runServer(loader *Loader, addr string) {
apiHandler := &apiHandler{loader: loader}
func (ah *api) getWorlds(w http.ResponseWriter, r *http.Request) {
ah.loader.lock.Lock()
defer ah.loader.lock.Unlock()
sm := http.NewServeMux()
sm.Handle("/api/dinos", apiHandler)
db := ah.loader.db
fs := http.FileServer(http.Dir("static"))
sm.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/" {
http.Redirect(w, r, "/tamed.html", http.StatusFound)
var err error
if ah.worldsStmt == nil {
ah.worldsStmt, err = db.Preparex(getWorlds)
if err != nil {
log.Printf("Error preparing statement: %s", err)
http.Error(w, "Database Error", http.StatusInternalServerError)
return
}
fs.ServeHTTP(w, r)
}
var result []struct {
Id int `json:"id"`
Name string `json:"name"`
}
err = ah.worldsStmt.Select(&result)
if err != nil {
log.Printf("Error loading dinos: %s", err)
http.Error(w, "Database Error", http.StatusInternalServerError)
return
}
data, err := json.Marshal(result)
if err != nil {
log.Printf("Error marshalling result: %s", err)
http.Error(w, "JSON Error", http.StatusInternalServerError)
return
}
w.Write(data)
}
func (ah *api) getWild(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
worldId := vars["world"]
ah.loader.lock.Lock()
defer ah.loader.lock.Unlock()
db := ah.loader.db
var err error
if ah.wildStmt == nil {
ah.wildStmt, err = db.Preparex(getWildDinos)
if err != nil {
log.Printf("Error preparing statement: %s", err)
http.Error(w, "Database Error", http.StatusInternalServerError)
return
}
}
var result []dinoResult
err = ah.wildStmt.Select(&result, worldId)
if err != nil {
log.Printf("Error loading dinos: %s", err)
http.Error(w, "Database Error", http.StatusInternalServerError)
return
}
data, err := json.Marshal(result)
if err != nil {
log.Printf("Error marshalling result: %s", err)
http.Error(w, "JSON Error", http.StatusInternalServerError)
return
}
w.Write(data)
}
func runServer(loader *Loader, addr string) {
api := &api{loader: loader}
r := mux.NewRouter()
r.HandleFunc("/api/tames", api.getTames)
r.HandleFunc("/api/worlds", api.getWorlds)
r.HandleFunc("/api/worlds/{world}/dinos", api.getWild)
fs := http.FileServer(http.Dir("static"))
r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", fs))
r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/static/tamed.html", http.StatusFound)
})
log.Printf("Listening on: %s", addr)
log.Fatal(http.ListenAndServe(addr, loggingWrapper(sm)))
log.Fatal(http.ListenAndServe(addr, loggingWrapper(r)))
}

View File

@@ -103,5 +103,8 @@ FROM
LEFT JOIN worlds w ON d.world == w.id
LEFT JOIN classes c1 ON d.class == c1.id
WHERE
d.is_tamed = 0
d.is_tamed = 0 AND
d.world = ?
`
const getWorlds = `SELECT id, name FROM worlds`

1
go.mod
View File

@@ -4,6 +4,7 @@ go 1.16
require (
github.com/fsnotify/fsnotify v1.5.0
github.com/gorilla/mux v1.8.0
github.com/jmoiron/sqlx v1.3.4
github.com/justinian/ark v1.0.0
github.com/mattn/go-sqlite3 v1.14.8

2
go.sum
View File

@@ -4,6 +4,8 @@ github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gG
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/jmoiron/sqlx v1.3.4 h1:wv+0IJZfL5z0uZoUjlpKgHkgaFSYD+r9CfrXjEXsO7w=
github.com/jmoiron/sqlx v1.3.4/go.mod h1:2BljVx/86SuTyjE+aPYlHCTNvZrnJXghYGpNiXLBMCQ=
github.com/justinian/ark v1.0.0 h1:cD7SHtxXhNJFE+9KX/d9/Ax2o6ILcV15zquhCErRfFc=

View File

@@ -0,0 +1,6 @@
This favicon was generated using the following graphics from Twitter Twemoji:
- Graphics Title: 1f995.svg
- Graphics Author: Copyright 2020 Twitter, Inc and other contributors (https://github.com/twitter/twemoji)
- Graphics Source: https://github.com/twitter/twemoji/blob/master/assets/svg/1f995.svg
- Graphics License: CC-BY 4.0 (https://creativecommons.org/licenses/by/4.0/)

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 492 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 983 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -0,0 +1,10 @@
{
"name":"Menagerie: ARK Creature Explorer",
"short_name":"Menagerie",
"icons": [
{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},
{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],
"theme_color":"#ffffff",
"background_color":"#ffffff",
"display":"standalone"
}

View File

@@ -1,3 +1,20 @@
var populateWildLinks = function (listElement) {
$.ajax("/api/worlds", {
dataType: "json",
success: function(data, statusString, xhr) {
for (i = 0; i < data.length; i++) {
var item = document.createElement("li");
var link = document.createElement("a");
var worldName = document.createTextNode(data[i]["name"]);
link.classList.add("dropdown-item");
link.href = "wild.html#" + data[i]["id"]
link.appendChild(worldName);
item.appendChild(link);
listElement.appendChild(item);
}
}
});
}
var colorFunc = function (data, type, row, meta) {
var c = null;

View File

@@ -78,12 +78,13 @@ var columns = [
];
var tableOptions = {
"ajax": {"url":"api/dinos?type=tamed", "dataSrc":""},
"columns": tamedColumns,
"ajax": {"url":"/api/tames", "dataSrc":""},
"columns": columns,
"dom": "rtpil",
"pageLength": 50,
"scrollX": true,
"processing": true,
"language": {
"searchBuilder": {

View File

@@ -7,7 +7,7 @@ var showStats = function () {
table.columns([12, 13, 14, 15, 16, 17, 18, 19]).visible(showBase);
};
var wildColumns = [
var columns = [
{"data": "world", "title": "World", "visible": false},
{"data": "class_name", "title": "Class"},
{"data": "levels_wild", "title": "Level"},
@@ -33,13 +33,14 @@ var wildColumns = [
{"data": "speed_wild", "title": "Sp", "searchBuilderTitle": "Base Speed"}
];
var wildTableOptions = {
"ajax": {"url":"api/dinos?type=wild", "dataSrc":""},
"columns": wildColumns,
var tableOptions = {
"ajax": {"dataSrc":""},
"columns": columns,
"dom": "rtpil",
"pageLength": 50,
"scrollX": true,
"processing": true,
"language": {
"searchBuilder": {
@@ -55,7 +56,15 @@ var wildTableOptions = {
"searchBuilder": {
"columns": [0, 1, 2,
6, 7, 8, 9, 10, 11,
12, 13, 14, 15, 16, 17, 18, 19,
20, 21, 22, 23, 24, 25, 26, 27]
12, 13, 14, 15, 16, 17, 18, 19]
}
};
var setTableWorld = function (table) {
var id = window.location.hash.substr(1);
if (id.length > 0) {
table.rows({selected: true}).deselect();
url = "/api/worlds/" + id + "/dinos";
table.ajax.url(url).load();
}
};

View File

@@ -11,9 +11,9 @@
<script type="text/javascript"
src="https://cdn.datatables.net/v/bs5/jq-3.3.1/dt-1.10.25/sb-1.1.0/sl-1.3.3/datatables.min.js"></script>
<script type="text/javascript" src="colors.js"></script>
<script type="text/javascript" src="main.js"></script>
<script type="text/javascript" src="tamed.js"></script>
<script type="text/javascript" src="js/colors.js"></script>
<script type="text/javascript" src="js/main.js"></script>
<script type="text/javascript" src="js/tamed.js"></script>
<script type="text/javascript">
$(document).ready( function () {
@@ -30,6 +30,9 @@
$( '#showCurrent' ).on( 'click', showStats );
$( '#showColor' ).on( 'click', showStats );
var wilds = document.getElementById("wildDropdown");
populateWildLinks(wilds);
table.on( "select", function () {
row = table.row({"selected":true}).data();
if (row.name) {
@@ -96,7 +99,11 @@
<span class="navbar-brand">Menagerie: ARK Creature Explorer</span>
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item"><a class="nav-link active" href="#">Tamed</a></li>
<li class="nav-item"><a class="nav-link" href="wild.html">Wild</a></li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown">Wild</a>
<ul class="dropdown-menu" id="wildDropdown">
</ul>
</li>
</ul>
</div>
</nav>

View File

@@ -1,5 +1,9 @@
<html>
<head>
<link rel="apple-touch-icon" sizes="180x180" href="/static/images/icon/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/static/images/icon/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/static/images/icon/favicon-16x16.png">
<link rel="manifest" href="/static/images/icon/site.webmanifest">
<link rel="stylesheet" type="text/css"
href="https://cdn.jsdelivr.net/npm/bootswatch@5/dist/yeti/bootstrap.min.css">
@@ -11,15 +15,20 @@
<script type="text/javascript"
src="https://cdn.datatables.net/v/bs5/jq-3.3.1/dt-1.10.25/sb-1.1.0/sl-1.3.3/datatables.min.js"></script>
<script type="text/javascript" src="colors.js"></script>
<script type="text/javascript" src="main.js"></script>
<script type="text/javascript" src="wild.js"></script>
<script type="text/javascript" src="js/colors.js"></script>
<script type="text/javascript" src="js/main.js"></script>
<script type="text/javascript" src="js/wild.js"></script>
<script type="text/javascript">
$(document).ready( function () {
table = $('#dinos').DataTable(tableOptions);
table.searchBuilder.container().prependTo( $('#searchBuilderDiv') );
setTableWorld(table);
window.addEventListener("hashchange", function () {
setTableWorld(table);
}, {passive: true});
$( '#searchInput' ).on( 'keyup', function () {
table.search( this.value ).draw();
} );
@@ -27,6 +36,9 @@
$( '#showBase' ).on( 'click', showStats );
$( '#showColor' ).on( 'click', showStats );
var wilds = document.getElementById("wildDropdown");
populateWildLinks(wilds);
table.on( "select", function () {
row = table.row({"selected":true}).data();
@@ -69,6 +81,14 @@
.dinoInfo {
font-size: smaller;
}
#dinos_processing {
padding: 10px;
border: 3px solid black;
z-index: 1000;
color: #fff;
background: rgba(0.6, 0.6, 0.6, 0.25);
}
</style>
</head>
<body>
@@ -78,7 +98,11 @@
<span class="navbar-brand">Menagerie: ARK Creature Explorer</span>
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item"><a class="nav-link" href="tamed.html">Tamed</a></li>
<li class="nav-item"><a class="nav-link active" href="#">Wild</a></li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown">Wild</a>
<ul class="dropdown-menu" id="wildDropdown">
</ul>
</li>
</ul>
</div>
</nav>