mirror of
https://ceregatti.org/git/daniel/dayzdockerserver.git
synced 2025-06-23 20:41:18 +00:00
Lots of layout changes.
Add placeholders for future functions. Add buttons for install, update, update mods, etc. Add bootstrap icons CDN. Remove old CSS in favor of Bootstrap classes. Add favico. Update docs.
This commit is contained in:
parent
8a8dc7f25a
commit
052304f450
6 changed files with 170 additions and 70 deletions
|
@ -18,6 +18,10 @@ This volume can get quite large. It will require at least 2G of disk space for t
|
|||
Some map mods are as large as 10G. Make sure you have that much disk space in the location where docker stores its
|
||||
volumes, usually `/var/lib/docker/volumes`.
|
||||
|
||||
## Goals
|
||||
|
||||
* Provide a turnkey DayZ server with mod support.
|
||||
|
||||
## Configure and Build
|
||||
|
||||
Ensure [Docker](https://docs.docker.com/engine/install/) and [Docker compose](https://docs.docker.com/compose/install/)
|
||||
|
@ -152,7 +156,7 @@ docker compose exec main dayzserver force
|
|||
When the server exits cleanly, i.e. exit code 0, the container also stops. Otherwise, a crash is presumed, and the server will be restarted.
|
||||
|
||||
NOTE: As DayZ Experimental 1.19, the server is known to not exit upon SIGINT when mods are installed. This makes force stopping the server
|
||||
required. This is not a clean exit, and will cause the server to restart. Manually take the server [down](#down) to stop the container.
|
||||
required. This is not a clean exit, and will cause the server to restart. Manually take the server [down](#stop) to stop the container.
|
||||
|
||||
### Workshop - Add / List / Remove / Update mods
|
||||
|
||||
|
|
BIN
web/root/favicon.png
Normal file
BIN
web/root/favicon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 81 KiB |
|
@ -1,23 +1,15 @@
|
|||
body {
|
||||
padding-top: 10px;
|
||||
padding: 10px;
|
||||
background-color: black;
|
||||
}
|
||||
.green {
|
||||
color: green;
|
||||
font-weight: bolder;
|
||||
}
|
||||
.yellow {
|
||||
color: yellow;
|
||||
font-weight: bolder;
|
||||
}
|
||||
.darkgrey {
|
||||
background-color: darkgray;
|
||||
font-weight: bolder;
|
||||
margin-bottom: 10px;
|
||||
|
||||
button {
|
||||
padding: 5px;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
.modInfo {
|
||||
background-color: aliceblue;
|
||||
th, td {
|
||||
padding-right: 10px
|
||||
}
|
||||
|
||||
.result {
|
||||
|
@ -32,7 +24,3 @@ body {
|
|||
cursor: pointer;
|
||||
text-underline: blue;
|
||||
}
|
||||
|
||||
th, td {
|
||||
padding-right: 10px
|
||||
}
|
||||
|
|
|
@ -3,9 +3,10 @@
|
|||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>DayZ Docker Server</title>
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.png">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet"
|
||||
integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="/index.css">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.3.0/font/bootstrap-icons.css"> <link rel="stylesheet" href="/index.css">
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js"
|
||||
integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
|
||||
<script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
|
||||
|
|
|
@ -1,31 +1,48 @@
|
|||
const template = `
|
||||
<div class="container-fluid">
|
||||
<div class="row jumbotron darkgrey">
|
||||
<div class="col-7">
|
||||
<div class="container-fluid bg-light">
|
||||
<div class="d-flex justify-content-between">
|
||||
<div class="text-center">
|
||||
<h1>DayZ Docker Server</h1>
|
||||
</div>
|
||||
<div class="col-3 form-control-lg">
|
||||
<div class="d-flex">
|
||||
<div class="text-center">
|
||||
<button
|
||||
@click="install"
|
||||
:class="'btn ' + (installed ? 'btn-danger' : 'btn-success')"
|
||||
>
|
||||
Install Server Files
|
||||
</button>
|
||||
<button @click="updatebase" class="btn btn-warning">Update Base</button>
|
||||
<button @click="updatemods" class="btn btn-warning">Update Mods</button>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<button @click="server" class="btn btn-primary">Server</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-control-lg">
|
||||
<form @submit="handleSubmit">
|
||||
<input name="search" placeholder="Search mods..." autofocus>
|
||||
</form>
|
||||
</div>
|
||||
<div class="col-2">
|
||||
<div>
|
||||
Server files installed: {{ installed }}
|
||||
<div class="justify-right">
|
||||
Server files installed:
|
||||
<span class="bi bi-check h2 text-success" v-if="installed"></span>
|
||||
<span class="bi bi-x h2 danger text-danger" v-else></span>
|
||||
</div>
|
||||
<div>
|
||||
Version: {{ version }}
|
||||
<div v-if="version != ''">
|
||||
Version: <span class="text-success font-weight-bold">{{ version }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="fetchError != ''"
|
||||
class="row jumbotron text-center alert alert-danger"
|
||||
class="text-center alert alert-danger"
|
||||
>
|
||||
{{ fetchError }}
|
||||
</div>
|
||||
<div class="row jumbotron darkgrey">
|
||||
<div class="col-3">
|
||||
<div class="d-flex">
|
||||
<div>
|
||||
<h2 class="text-center">Mods</h2>
|
||||
<table>
|
||||
<tr>
|
||||
|
@ -39,7 +56,7 @@ const template = `
|
|||
<td>
|
||||
<a
|
||||
target="_blank"
|
||||
:href="'https://steamcommunity.com/sharedfiles/filedetails/?id=' + mod.id"
|
||||
:href="steamURL + mod.id"
|
||||
>
|
||||
{{ mod.id }}
|
||||
</a>
|
||||
|
@ -51,42 +68,11 @@ const template = `
|
|||
</template>
|
||||
</table>
|
||||
</div>
|
||||
<div class="col-9 modInfo" v-if="searchResults != ''">
|
||||
<table>
|
||||
<tr>
|
||||
<th>Steam Link</th>
|
||||
<th>Title</th>
|
||||
<th>Size</th>
|
||||
<th>Last Updated</th>
|
||||
<th>Subscriptions</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
<tr v-for="result in searchResults">
|
||||
<td>
|
||||
<a
|
||||
target="_blank"
|
||||
:href="'https://steamcommunity.com/sharedfiles/filedetails/?id=' + result.publishedfileid"
|
||||
>
|
||||
<img data-bs-toggle="tooltip" data-bs-placement="left" :title="result.short_description" width="160" height="90" :src="result.preview_url">
|
||||
</a>
|
||||
</td>
|
||||
<td>{{ result.title }}</td>
|
||||
<td>{{ BKMG(result.file_size) }}</td>
|
||||
<td>{{ new Date(result.time_updated * 1000).toLocaleDateString("en-us") }}</td>
|
||||
<td>{{ result.lifetime_subscriptions }}</td>
|
||||
<td>
|
||||
<button v-if="mods.find(o => o.id == result.publishedfileid)" @click="removeMod(result.publishedfileid)" type="button" class="btn btn-danger">Remove</button>
|
||||
<button v-else @click="installMod(result.publishedfileid)" type="button" class="btn btn-success">Install</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="col-9 modInfo" v-if="modInfo != ''">
|
||||
<div class="text-center col-12">
|
||||
<h2>{{ modInfo.name }} mod info:</h2>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-2">
|
||||
<div v-if="modInfo != ''">
|
||||
<div>
|
||||
<h3>{{ modInfo.name }}</h3>
|
||||
<div class="d-flex">
|
||||
<div>
|
||||
<div>
|
||||
ID: {{ modInfo.id }}
|
||||
</div>
|
||||
|
@ -107,13 +93,46 @@ const template = `
|
|||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-10">
|
||||
<div>
|
||||
<div v-if="XMLInfo != ''">
|
||||
<textarea cols="120" rows="15" v-if="this.XMLInfo != ''">{{ this.XMLInfo }}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="searchResults != ''">
|
||||
<table>
|
||||
<tr>
|
||||
<th>Steam Link</th>
|
||||
<th>Title</th>
|
||||
<th>Size</th>
|
||||
<th>Last Updated</th>
|
||||
<th>Subscriptions</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
<tr v-for="result in searchResults">
|
||||
<td>
|
||||
<a
|
||||
target="_blank"
|
||||
:href="steamURL + result.publishedfileid"
|
||||
>
|
||||
<img :alt="result.short_description" data-bs-toggle="tooltip" data-bs-placement="left" :title="result.short_description" width="160" height="90" :src="result.preview_url">
|
||||
</a>
|
||||
</td>
|
||||
<td>{{ result.title }}</td>
|
||||
<td>{{ BKMG(result.file_size) }}</td>
|
||||
<td>{{ new Date(result.time_updated * 1000).toLocaleDateString("en-us") }}</td>
|
||||
<td>{{ result.lifetime_subscriptions }}</td>
|
||||
<td>
|
||||
<button v-if="mods.find(o => o.id == result.publishedfileid)" @click="removeMod(result.publishedfileid)" type="button" class="btn btn-danger">Remove</button>
|
||||
<button v-else @click="installMod(result.publishedfileid)" type="button" class="btn btn-success">Install</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
|
||||
export default {
|
||||
|
@ -126,6 +145,7 @@ export default {
|
|||
mods: [],
|
||||
modInfo: "",
|
||||
searchResults: [],
|
||||
steamURL: 'https://steamcommunity.com/sharedfiles/filedetails/?id=',
|
||||
version: "Unknown",
|
||||
XMLFile: "",
|
||||
XMLInfo: "",
|
||||
|
@ -196,6 +216,17 @@ export default {
|
|||
this.fetchError = error.message
|
||||
})
|
||||
},
|
||||
removeMod(modId) {
|
||||
fetch('/remove/' + modId)
|
||||
.then(response => response.text())
|
||||
.then(response => {
|
||||
console.log(response)
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error)
|
||||
this.fetchError = error.message
|
||||
})
|
||||
},
|
||||
BKMG(val) {
|
||||
const units = ['bytes', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']
|
||||
let l = 0, n = parseInt(val, 10) || 0
|
||||
|
|
78
web/web.js
78
web/web.js
|
@ -1,3 +1,11 @@
|
|||
/*
|
||||
A DayZ Linux server provisioning system.
|
||||
|
||||
This is the web UI for provisioning a DayZ server running under Linux.
|
||||
It manages the main container that installs and maintains the base DayZ server files
|
||||
along with all mod base files. The goal being to keep all of these centralized and consistent,
|
||||
but to also make them available for the creation of server containers.
|
||||
*/
|
||||
import express from 'express'
|
||||
import path from 'path'
|
||||
import fs from 'fs'
|
||||
|
@ -45,6 +53,49 @@ const configFiles = [
|
|||
'types.xml',
|
||||
]
|
||||
|
||||
// From https://helpthedeadreturn.wordpress.com/2019/07/17/dayz-sa-mission-file
|
||||
const allConfigFiles = {
|
||||
"db": [ // global server config and core loot economy files
|
||||
"events.xml", // dynamic events
|
||||
"globals.xml", // global settings
|
||||
"messages.xml", // server broadcast messages and shutdown
|
||||
"types.xml" // loot table
|
||||
],
|
||||
"env": [ // coordinates, static and dynamic spawns for each entity
|
||||
"cattle_territories.xml",
|
||||
"domestic_animals_territories.xml",
|
||||
"hare_territories.xml",
|
||||
"hen_territories.xml",
|
||||
"pig_territories.xml",
|
||||
"red_deer_territories.xml",
|
||||
"roe_deer_territories.xml",
|
||||
"sheep_goat_territories.xml",
|
||||
"wild_boar_territories.xml",
|
||||
"wolf_territories.xml",
|
||||
"zombie_territories.xml"
|
||||
],
|
||||
"root": [
|
||||
"cfgeconomycore.xml", // loot economy core settings and extensions
|
||||
"cfgeffectarea.json", // static contaminated area coordinates and other properties
|
||||
"cfgenvironment.xml", // includes env\* files and parameters
|
||||
"cfgeventgroups.xml", // definitions of groups of objects that spawn together in a dynamic event
|
||||
"cfgeventspawns.xml", // coordinates where events may occur
|
||||
"cfggameplay.json", // gameplay configuration settings.
|
||||
"cfgIgnoreList.xml", // list of items that won’t be loaded from the storage
|
||||
"cfglimitsdefinition.xml", // list of valid categories, tags, usageflags and valueflags
|
||||
"cfglimitsdefinitionuser.xml", // shortcut groups of usageflags and valueflags
|
||||
"cfgplayerspawnpoints.xml", // new character spawn points
|
||||
"cfgrandompresets.xml", // collection of groups of items
|
||||
"cfgspawnabletypes.xml", // loot categorization (ie hoarder) as well as set of items that spawn as cargo or as attachment on weapons, vehicles or infected.
|
||||
"cfgundergroundtriggers.json", // used for triggering light and sounds in the Livonia bunker, not used for Chernarus
|
||||
"cfgweather.xml", // weather configuration
|
||||
"init.c", // mission startup file (PC only)
|
||||
"map*.xml",
|
||||
"mapgroupproto.xml", // structures, tags, maxloot and lootpoints
|
||||
"mapgrouppos.xml" // all valid lootpoints
|
||||
]
|
||||
}
|
||||
|
||||
const config = {
|
||||
installFile: serverFiles + "/DayZServer",
|
||||
modDir: modDir + "/" + client_appid,
|
||||
|
@ -52,6 +103,13 @@ const config = {
|
|||
steamAPIKey: process.env["STEAMAPIKEY"]
|
||||
}
|
||||
|
||||
const getVersion = (installed) => {
|
||||
if(installed) {
|
||||
return "1.20.bogus"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
const getDirSize = (dirPath) => {
|
||||
let size = 0
|
||||
const files = fs.readdirSync(dirPath)
|
||||
|
@ -144,6 +202,23 @@ app.get(('/install/:modId'), (req, res) => {
|
|||
res.send(modId + " was installed")
|
||||
})
|
||||
|
||||
// Remove a mod
|
||||
app.get(('/remove/:modId'), (req, res) => {
|
||||
const modId = req.params["modId"]
|
||||
// Shell out to steamcmd, monitor the process, and display the output as it runs
|
||||
res.send(modId + " was removed")
|
||||
})
|
||||
|
||||
// Update base files
|
||||
app.get('/updatebase', (req, res) => {
|
||||
res.send("Base files were updates")
|
||||
})
|
||||
|
||||
// Update mods
|
||||
app.get('/updatemods', (req, res) => {
|
||||
res.send("Mod files were updates")
|
||||
})
|
||||
|
||||
/*
|
||||
Get the status of things:
|
||||
If the base files are installed, the version of the server, a list of mods, etc.
|
||||
|
@ -152,9 +227,10 @@ app.get('/status', (req, res) => {
|
|||
// FIXME Async/await this stuff...
|
||||
const installed = fs.existsSync(config.installFile)
|
||||
const mods = getMods()
|
||||
const version = getVersion(installed)
|
||||
const ret = {
|
||||
"installed": installed,
|
||||
"version": "1.20.bogus",
|
||||
"version": version,
|
||||
"mods": mods
|
||||
}
|
||||
res.send(ret)
|
||||
|
|
Loading…
Add table
Reference in a new issue