Update for Minecraft
Using template from the PHP version of this site, from 2016/2017! $ ls -Alh -rw-r--r-- 1 chandler chandler 1.5K Jan 4 2017 index.php
This commit is contained in:
parent
8dd1b7b58d
commit
f17074d22d
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,2 +1,2 @@
|
||||||
factorio-site
|
minecraft-site
|
||||||
config.json
|
config.json
|
||||||
|
|
13
go.mod
13
go.mod
|
@ -1,8 +1,15 @@
|
||||||
module github.com/chandlerswift/factorio-site
|
module git.chandlerswift.com/chandlerswift/minecraft-site
|
||||||
|
|
||||||
go 1.14
|
go 1.21
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/james4k/rcon v0.0.0-20120923215419-8fbb8268b60a
|
github.com/dreamscached/minequery/v2 v2.4.1
|
||||||
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59
|
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59
|
||||||
)
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/google/uuid v1.3.0 // indirect
|
||||||
|
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
|
||||||
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 // indirect
|
||||||
|
golang.org/x/text v0.3.7 // indirect
|
||||||
|
)
|
||||||
|
|
12
go.sum
12
go.sum
|
@ -1,5 +1,9 @@
|
||||||
github.com/james4k/rcon v0.0.0-20120923215419-8fbb8268b60a h1:JxcWget6X/VfBMKxPIc28Jel37LGREut2fpV+ObkwJ0=
|
github.com/dreamscached/minequery/v2 v2.4.1 h1:jRx0oQ80dq6+2a01oHiSm0s3KzLs3cT+9vkAL2tLOHc=
|
||||||
github.com/james4k/rcon v0.0.0-20120923215419-8fbb8268b60a/go.mod h1:1qNVsDcmNQDsAXYfUuF/Z0rtK5eT8x9D6Pi7S3PjXAg=
|
github.com/dreamscached/minequery/v2 v2.4.1/go.mod h1:wKoUWEntOrS1jnJJfq73nkkU8cjNCCQojUHqXAtibZU=
|
||||||
|
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||||
|
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
||||||
|
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59 h1:3zb4D3T4G8jdExgVU/95+vQXfpEPiMdCaZgmGVxjNHM=
|
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59 h1:3zb4D3T4G8jdExgVU/95+vQXfpEPiMdCaZgmGVxjNHM=
|
||||||
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
@ -7,5 +11,7 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2eP
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||||
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
|
|
@ -5,18 +5,17 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"html/template"
|
"html/template"
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/james4k/rcon"
|
"github.com/dreamscached/minequery/v2"
|
||||||
"golang.org/x/crypto/acme/autocert"
|
"golang.org/x/crypto/acme/autocert"
|
||||||
)
|
)
|
||||||
|
|
||||||
type config struct {
|
type config struct {
|
||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
Content string `json:"content"`
|
Content template.HTML `json:"content"`
|
||||||
Servers []server `json:"servers"`
|
Servers []server `json:"servers"`
|
||||||
UseTLS bool `json:"useTLS"`
|
UseTLS bool `json:"useTLS"`
|
||||||
TLSHostname string `json:"tlshostname"`
|
TLSHostname string `json:"tlshostname"`
|
||||||
|
@ -25,43 +24,21 @@ type config struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type server struct {
|
type server struct {
|
||||||
Host string `json:"host"` // for display only
|
Host string `json:"host"`
|
||||||
Port int `json:"port"`
|
Port int `json:"port"`
|
||||||
RCONHost string `json:"rconhost"` // not displayed, but used to connect; leave blank for no RCON connection
|
Status *minequery.Status17
|
||||||
RCONPort int `json:"rconport"`
|
|
||||||
RCONPassword string `json:"rconpassword"`
|
|
||||||
Title string `json:"title"` // TODO: get this from RCON?
|
|
||||||
Description string `json:"description"` // TODO: get this from RCON?
|
|
||||||
Version string // Populated by RCON
|
|
||||||
Players string // Populated by RCON
|
|
||||||
rconConnection *rcon.RemoteConsole
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type pageData struct {
|
type pageData struct {
|
||||||
Title string
|
Title string
|
||||||
Content string
|
Content template.HTML
|
||||||
Servers []server
|
Servers []server
|
||||||
}
|
}
|
||||||
|
|
||||||
// rconCommand executes a command on the server, and returns the server's
|
|
||||||
// response as a string.
|
|
||||||
func (s server) rconCommand(command string) (response string, err error) {
|
|
||||||
_, err = s.rconConnection.Write(command)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
response, _, err = s.rconConnection.Read()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
||||||
// Parse config file
|
// Parse config file
|
||||||
configData, err := ioutil.ReadFile("./config.json")
|
configData, err := os.ReadFile("./config.json")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Error reading config file: %v\n", err)
|
log.Fatalf("Error reading config file: %v\n", err)
|
||||||
}
|
}
|
||||||
|
@ -91,32 +68,14 @@ func main() {
|
||||||
log.Fatalf("Error parsing HTML template: %v\n", err)
|
log.Fatalf("Error parsing HTML template: %v\n", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connect to RCON servers
|
|
||||||
for i := range config.Servers {
|
|
||||||
s := config.Servers[i]
|
|
||||||
if s.RCONHost != "" {
|
|
||||||
config.Servers[i].rconConnection, err = rcon.Dial(fmt.Sprintf("%v:%v", s.RCONHost, s.RCONPort), s.RCONPassword)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Error making RCON connection to %v: %v", s.Title, err)
|
|
||||||
}
|
|
||||||
defer config.Servers[i].rconConnection.Close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
// Update servers with current data
|
// Update servers with current data
|
||||||
for i, s := range config.Servers {
|
for i, s := range config.Servers {
|
||||||
if s.rconConnection != nil {
|
// TODO: Query instead (opportunistically?) to get mod lists, etc
|
||||||
config.Servers[i].Players, err = s.rconCommand("/players o")
|
config.Servers[i].Status, err = minequery.Ping17(s.Host, s.Port)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error executing players online command: %v\n", err)
|
log.Printf("Error querying server: %v", err)
|
||||||
}
|
|
||||||
|
|
||||||
config.Servers[i].Version, err = s.rconCommand("/version")
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Error executing version command: %v\n", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,7 +86,7 @@ func main() {
|
||||||
})
|
})
|
||||||
|
|
||||||
// Serve backup directory
|
// Serve backup directory
|
||||||
if serveBackups { // TODO: also remove HTML if disabled
|
if serveBackups { // TODO: add HTML
|
||||||
http.Handle("/backups/", http.StripPrefix("/backups/", http.FileServer(http.Dir(config.BackupDir))))
|
http.Handle("/backups/", http.StripPrefix("/backups/", http.FileServer(http.Dir(config.BackupDir))))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>{{.Title}}</title>
|
<meta charset='utf-8'>
|
||||||
|
<meta name='viewport' content='width=device-width, initial-scale=1'>
|
||||||
|
<title>ChandlerSwift.com Minecraft</title>
|
||||||
<style>
|
<style>
|
||||||
html {
|
body{margin:1em auto;max-width:40em;padding:0 .62em;font:1.2em sans-serif; line-height: 1.62em;}h1,h2,h3{line-height:1.2em;}@media print{body{max-width:none}}
|
||||||
font-family: sans-serif;
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
/* https://html.spec.whatwg.org/multipage/rendering.html#phrasing-content-3 */
|
/* https://html.spec.whatwg.org/multipage/rendering.html#phrasing-content-3 */
|
||||||
.light {
|
.light {
|
||||||
color: #888;
|
color: #888;
|
||||||
|
@ -24,26 +23,38 @@
|
||||||
color: #518;
|
color: #518;
|
||||||
}
|
}
|
||||||
:not(:hover) .optional-port {
|
:not(:hover) .optional-port {
|
||||||
color: #ccc;
|
color: #666;
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
</head>
|
<link rel="shortcut icon" href="https://chandlerswift.com/favicon.ico" />
|
||||||
<body>
|
</head>
|
||||||
|
<body>
|
||||||
|
<header>
|
||||||
<h1>{{.Title}}</h1>
|
<h1>{{.Title}}</h1>
|
||||||
<p>{{.Content}}</p>
|
<p>{{.Content}}</p>
|
||||||
<p>Backups are taken and archived periodically, and made available in the <a href="/backups">backup archive</a>.</p>
|
</header>
|
||||||
{{range .Servers}}
|
{{range .Servers}}
|
||||||
<h3>{{.Title}}</h3>
|
<section>
|
||||||
|
<h2 style="margin-bottom: 0;"><code>{{.Host}}{{if eq .Port 25565}}<span class="optional-port">:{{.Port}}</span>{{else}}:{{.Port}}{{end}}</code></h2>
|
||||||
|
{{if .Status}}
|
||||||
|
<span style="color: #666; font-style: italic;">{{.Status.Description.String}}</span>
|
||||||
<ul>
|
<ul>
|
||||||
<li><b>Description:</b> {{.Description}}</li>
|
<li>Minecraft Version: {{.Status.VersionName}}</li>
|
||||||
<li><b>Connection String:</b> {{.Host}}{{if eq .Port 34197}}<span class="optional-port">:{{.Port}}</span>{{else}}:{{.Port}}{{end}}</li>
|
<li>Players Online: {{.Status.OnlinePlayers}}/{{.Status.MaxPlayers}}
|
||||||
{{if ne .RCONHost ""}}
|
{{if gt .Status.OnlinePlayers 0}}
|
||||||
<li><b>Version:</b> {{.Version}}</li>
|
<ul>
|
||||||
<li><b>Current players:</b><br>{{.Players}}</li>
|
{{range $i, $player := .Status.SamplePlayers}}
|
||||||
|
<li>{{$player.Nickname}}</li>
|
||||||
{{end}}
|
{{end}}
|
||||||
</ul>
|
</ul>
|
||||||
{{end}}
|
{{end}}
|
||||||
<p class="light">This page maintained by chandlerswift's <a href="https://github.com/chandlerswift/factorio-site">factorio server list</a>.</p>
|
</li>
|
||||||
</body>
|
</ul>
|
||||||
|
{{else}}
|
||||||
|
<span style="color: #666;">Unable to connect to server to check status</span>
|
||||||
|
{{end}}
|
||||||
|
</section>
|
||||||
|
{{end}}
|
||||||
|
<p class="light">This page maintained by chandlerswift's <a href="https://git.chandlerswift.com/chandlerswift/minecraft-site">minecraft server list</a>.</p>
|
||||||
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
Loading…
Reference in a new issue