Compare commits
3 Commits
33ae1642e2
...
f6d5c4ccda
| Author | SHA1 | Date | |
|---|---|---|---|
| f6d5c4ccda | |||
| a7fdb28ed8 | |||
| 6ee826f94b |
@ -1 +1,3 @@
|
||||
#logging.level.de.ph87=DEBUG
|
||||
#-
|
||||
server.port=8083
|
||||
37
deploy.sh
Normal file
37
deploy.sh
Normal file
@ -0,0 +1,37 @@
|
||||
#!/bin/bash
|
||||
|
||||
cd "$(dirname "$0")" || exit 1
|
||||
|
||||
|
||||
|
||||
echo "+---------------------------------------------------+"
|
||||
echo "| Building backend |"
|
||||
echo "+---------------------------------------------------+"
|
||||
|
||||
mvn clean install || exit 1
|
||||
|
||||
echo "+---------------------------------------------------+"
|
||||
echo "| Uploading backend |"
|
||||
echo "+---------------------------------------------------+"
|
||||
|
||||
rsync --archive -e 'ssh -p 2222' ./target/McManager.jar mc@mc.ph87.de:/srv/McManager/ || exit 1
|
||||
|
||||
echo "+---------------------------------------------------+"
|
||||
echo "| Restarting backend |"
|
||||
echo "+---------------------------------------------------+"
|
||||
|
||||
ssh root@mc.ph87.de -p 2222 'systemctl restart McManager.service' || exit 1
|
||||
|
||||
echo "+---------------------------------------------------+"
|
||||
echo "| Building frontend |"
|
||||
echo "+---------------------------------------------------+"
|
||||
|
||||
cd src/main/angular || exit 1
|
||||
|
||||
npm run build || exit 1
|
||||
|
||||
echo "+---------------------------------------------------+"
|
||||
echo "| Uploading frontend |"
|
||||
echo "+---------------------------------------------------+"
|
||||
|
||||
rsync --archive --delete -e 'ssh -p 2222' ./dist/angular/browser/ mc@mc.ph87.de:/srv/McManager/www/
|
||||
@ -4,7 +4,7 @@
|
||||
"scripts": {
|
||||
"ng": "ng",
|
||||
"start": "ng serve",
|
||||
"build": "ng build && rsync --archive --delete -e 'ssh -p 2222' ./dist/angular/browser/ mc@mc.ph87.de:/srv/McManager/www/",
|
||||
"build": "ng build",
|
||||
"watch": "ng build --watch --configuration development",
|
||||
"test": "ng test"
|
||||
},
|
||||
|
||||
189
src/main/angular/public/minecraft.svg
Normal file
189
src/main/angular/public/minecraft.svg
Normal file
@ -0,0 +1,189 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg viewBox="0 0 512 85.169998" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
.st0 {
|
||||
fill: #020303;
|
||||
}
|
||||
|
||||
.st1 {
|
||||
fill: #A9A09D;
|
||||
}
|
||||
|
||||
.st2 {
|
||||
fill: #282729;
|
||||
}
|
||||
|
||||
.st3 {
|
||||
fill: #595558;
|
||||
}
|
||||
|
||||
.st4 {
|
||||
fill: #E1DCD7;
|
||||
}
|
||||
</style>
|
||||
<g transform="translate(0.05,-213.36)">
|
||||
<path class="st0"
|
||||
d="m 511.67,276.51 -1.54,-5.47 -1.54,-4.91 c 0,0 -4.63,-15.15 -5.33,-17.26 -0.7,-2.53 -1.54,-4.91 -2.24,-7.3 l -0.14,-0.42 10.38,-12.49 -4.91,-15.29 h -52.06 l -1.54,2.53 -0.56,-2.53 H 399.7 l -1.26,2.95 -0.56,-2.95 h -52.9 l -0.98,3.51 -0.42,-3.51 h -53.46 l -0.56,5.61 -0.14,-5.61 h -53.74 l -0.14,5.19 -0.56,-5.19 H 181.8 l -0.42,3.37 -0.98,-3.37 h -25.68 l -2.1,14.31 -1.12,-2.95 h -0.56 l -4.49,-11.37 h -25.4 l -0.56,2.81 -1.4,-2.81 H 93.96 l -0.56,2.53 -1.54,-2.53 H 67.02 l -2.95,11.08 h -6.73 l -1.26,4.91 -11.22,-15.43 H 20.58 l -20.63,64.26 22.03,20.35 h 23.99 l 3.79,-14.31 1.82,2.1 h 6.45 l 10.66,12.21 H 93.4 l 0.42,-2.39 1.68,2.39 h 24.84 l 0.42,-2.53 1.54,2.53 h 25.12 l 1.96,-13.47 6.45,13.47 h 25.4 l 0.28,-3.09 1.12,3.09 h 52.76 l 0.14,-4.91 0.56,4.91 h 53.18 l 0.56,-5.33 0.14,5.33 h 26.1 l 0.84,-3.79 0.28,3.79 h 25.82 l 1.12,-3.23 0.28,3.23 h 25.54 l 1.26,-2.81 0.42,2.81 h 25.26 l 1.54,-2.67 0.42,2.67 h 25.12 l 14.45,-20.63 -0.7,-3.37 h 8 l 11.37,-15.71 9.82,39.71 h 24.55 l 20.49,-20.49 z m -248.77,-15.72 0.28,-8.84 h 25.12 l 0.28,-2.95 0.42,11.65 z"/>
|
||||
<polygon class="st1" points="57.48,240.16 60.56,228.66 67.3,228.66 70.25,217.43 89.89,217.43 76,276.79 54.95,276.79 61.27,251.53 53.69,251.53 50.32,263.88 36.71,263.88 40.08,251.53 32.36,251.53 24.93,276.79 4.16,276.79 23.24,217.57 42.46,217.43 39.24,228.24 45.97,228.52 42.6,240.16 "/>
|
||||
<polygon class="st1" points="84,276.93 97.19,217.57 116.83,217.57 105.18,276.93 "/>
|
||||
<polygon class="st1" points="139.42,245.22 134.37,276.79 113.32,276.79 124.27,217.43 143.91,217.43 142.23,228.38 148.96,228.52 147.28,240.02 154.85,240.16 158.08,217.43 177.73,217.43 170.85,276.65 149.66,276.79 152.33,257.84 145.31,257.7 147.14,245.36 "/>
|
||||
<polygon class="st1" points="178.85,276.65 185.16,217.29 231.89,217.29 231.47,227.82 203.96,227.82 203.12,239.88 230.9,240.02 230.34,251.53 202,251.53 201.16,264.3 229.78,264.44 229.22,276.65 "/>
|
||||
<path class="st1" d="m 503.81,217.01 3.37,10.52 h -13.61 l 0.14,0.28 c 0.56,2.25 1.12,4.63 1.82,7.02 0.7,2.39 1.4,4.91 2.1,7.3 0.7,2.53 1.4,5.05 2.24,7.58 0.7,2.53 1.54,5.05 2.39,7.58 0.42,1.54 0.98,3.09 1.4,4.63 0.42,1.54 0.98,3.23 1.4,4.77 0.42,1.54 0.98,3.23 1.4,4.91 0.42,1.68 0.98,3.23 1.4,4.91 H 486.53 L 473.2,227.54 h -13.61 l -2.67,-10.52 z"/>
|
||||
<polygon class="st1" points="429.31,251.39 434.5,276.65 413.31,276.65 402.37,217.15 449.37,217.15 451.9,227.68 424.25,227.68 426.78,239.6 454.84,239.6 457.79,251.39 "/>
|
||||
<path class="st1" d="m 373.18,257.42 2.67,19.22 h -21.19 l -6.59,-59.49 h 46.86 l 10.24,59.49 h -21.19 l -2.81,-19.22 z m 13.75,-23.29 -1.26,-7.58 h -9.26 l 0.84,6.88 h -7.3 l -0.98,-6.88 h -8.98 l 0.98,7.58 h 8.28 l 0.42,2.81 h -4.21 l 1.54,11.65 h 4.91 l -0.42,-3.93 h 7.44 l 0.56,3.93 h 5.05 l -1.82,-11.65 h -4.07 l -0.42,-2.81 h 8.7"/>
|
||||
<path class="st1" d="m 336.42,245.92 h 7.02 l 3.09,30.73 h -21.19 l -1.82,-25.12 -7.86,-0.14 1.68,25.26 h -21.19 l -2.24,-59.35 h 46.86 l 2.24,22.03 H 336 Z m -13.89,-5.9 -0.98,-12.21 h -7.58 l 0.84,12.21 h 7.72"/>
|
||||
<polygon class="st1" points="286.19,217.29 239.32,217.29 237.36,276.65 287.87,276.65 287.45,264.44 258.69,264.44 259.25,227.82 286.47,227.82 "/>
|
||||
<path class="st2" d="m 6.97,276.79 c -1.54,0 -1.82,0.84 -0.7,1.96 l 15.01,14.45 c 1.12,0.98 3.23,1.96 4.77,1.96 h 13.19 c 1.54,0 1.96,-0.84 0.84,-1.96 L 26.75,279.03 c -0.98,-1.12 -3.09,-1.96 -4.63,-1.96 z"/>
|
||||
<path class="st2" d="m 57.76,276.79 c -1.54,0 -1.96,0.98 -0.98,2.1 l 11.79,14.03 c 0.98,1.12 3.09,2.1 4.49,2.1 h 13.89 c 1.54,0 1.96,-0.98 1.12,-2.24 L 77.83,279.03 c -0.84,-1.26 -2.95,-2.24 -4.49,-2.24 z"/>
|
||||
<path class="st2" d="m 39.52,263.88 c -1.54,0 -1.96,0.98 -0.98,2.1 l 13.05,14.73 c 0.98,1.12 2.81,1.96 4.07,1.96 1.26,0 1.54,-1.12 0.7,-2.39 l -0.84,-1.26 c -0.84,-1.26 -1.26,-3.51 -0.84,-4.91 l 0.28,-0.98 c 0.42,-1.4 0,-3.65 -0.98,-4.77 l -1.82,-2.25 c -0.98,-1.26 -2.95,-2.1 -4.49,-2.1 z"/>
|
||||
<path class="st2" d="m 86.8,276.93 c -1.54,0 -1.96,0.98 -1.12,2.24 l 9.96,13.61 c 0.84,1.26 2.95,2.25 4.35,2.25 h 13.61 c 1.54,0 2.1,-1.12 1.26,-2.39 l -8.42,-13.47 c -0.84,-1.26 -2.67,-2.39 -4.21,-2.39 z"/>
|
||||
<path class="st2" d="m 115.99,276.79 c -1.54,0 -2.1,1.12 -1.26,2.39 l 8.14,13.33 c 0.84,1.26 2.67,2.39 4.21,2.39 h 13.47 c 1.54,0 2.24,-1.12 1.54,-2.53 l -6.45,-13.05 c -0.7,-1.4 -2.53,-2.53 -3.93,-2.53 z"/>
|
||||
<path class="st2" d="m 152.47,276.79 c -1.54,0 -2.25,1.12 -1.54,2.53 l 6.17,13.19 c 0.7,1.4 2.39,2.53 3.93,2.53 h 13.89 c 1.54,0 2.39,-1.12 1.82,-2.67 l -4.77,-13.05 c -0.56,-1.4 -2.25,-2.67 -3.79,-2.67 z"/>
|
||||
<path class="st2" d="m 181.65,276.65 c -1.54,0 -2.39,1.12 -1.82,2.67 l 4.49,12.63 c 0.56,1.4 2.24,2.67 3.65,2.67 h 40.97 c 1.54,0 2.53,-1.26 2.39,-2.81 l -1.68,-12.35 c -0.28,-1.54 -1.68,-2.81 -3.09,-2.81 z"/>
|
||||
<path class="st2" d="m 240.16,276.65 c -1.54,0 -2.67,1.26 -2.53,2.81 l 1.4,12.77 c 0.14,1.54 1.54,2.81 3.09,2.81 h 40.83 c 1.54,0 2.95,-1.26 3.09,-2.81 l 1.54,-12.77 c 0.14,-1.54 -0.98,-2.81 -2.39,-2.81 z"/>
|
||||
<path class="st2" d="m 298.68,276.65 c -1.54,0 -2.95,1.26 -3.09,2.81 l -1.54,12.63 c -0.14,1.54 0.84,2.81 2.39,2.81 h 13.61 c 1.54,0 3.09,-1.26 3.37,-2.67 l 3.09,-12.77 c 0.28,-1.54 -0.56,-2.67 -2.1,-2.67 z"/>
|
||||
<path class="st2" d="m 328,276.65 c -1.54,0 -3.09,1.26 -3.37,2.67 l -2.95,12.49 c -0.42,1.54 0.56,2.67 2.1,2.67 h 13.61 c 1.54,0 3.23,-1.12 3.65,-2.67 l 4.49,-12.63 c 0.56,-1.4 -0.28,-2.67 -1.82,-2.67 z"/>
|
||||
<path class="st2" d="m 357.33,276.65 c -1.54,0 -3.23,1.12 -3.65,2.67 l -4.49,12.91 c -0.56,1.4 0.28,2.67 1.82,2.67 h 13.61 c 1.54,0 3.37,-1.12 3.93,-2.53 l 6.17,-13.05 c 0.7,-1.4 -0.14,-2.53 -1.54,-2.53 z"/>
|
||||
<path class="st2" d="m 386.65,276.65 c -1.54,0 -3.37,1.12 -3.93,2.53 l -6.45,13.19 c -0.7,1.4 0,2.53 1.54,2.53 h 14.03 c 1.54,0 3.37,-1.12 4.21,-2.39 l 7.72,-13.33 c 0.7,-1.26 0.14,-2.39 -1.4,-2.39 h -15.71 v -0.14 z"/>
|
||||
<path class="st2" d="m 416.12,276.65 c -1.54,0 -3.37,1.12 -4.21,2.39 l -8.14,13.47 c -0.84,1.26 -0.14,2.39 1.26,2.39 h 13.89 c 1.54,0 3.51,-0.98 4.35,-2.24 l 9.68,-13.61 c 0.84,-1.26 0.42,-2.25 -1.12,-2.25 z"/>
|
||||
<path class="st2" d="m 489.36,276.65 c -1.54,0 -3.65,0.98 -4.63,2.1 l -12.21,13.75 c -0.98,1.12 -0.56,2.1 0.98,2.1 h 13.19 c 1.54,0 3.65,-0.84 4.77,-1.96 l 14.45,-14.03 c 1.12,-1.12 0.7,-1.96 -0.84,-1.96 z"/>
|
||||
<path class="st0" d="m 19.73,227.4 c -0.14,0.42 0.98,0.7 2.53,0.7 h 14.45 c 1.54,0 2.81,-0.28 2.81,-0.7 0,-0.42 -1.26,-0.7 -2.67,-0.7 H 22.82 c -1.54,-0.01 -2.95,0.28 -3.09,0.7"/>
|
||||
<path class="st0" d="m 18.75,257.56 c 0.42,-1.4 -0.42,-2.67 -1.96,-2.67 h -3.51 c -1.54,0 -2.81,0.28 -2.81,0.7 0,0.42 1.26,0.7 2.81,0.7 h 1.82 c 1.54,0 2.39,1.12 1.96,2.67 l -3.23,9.82 c -0.42,1.4 0.42,2.67 1.96,2.67 h 10.8 c 1.54,0 2.81,-0.28 2.81,-0.7 0,-0.42 -1.26,-0.7 -2.81,-0.7 h -9.12 c -1.54,0 -2.39,-1.12 -1.96,-2.67 l 0.28,-0.84 c 0.42,-1.4 2.1,-2.67 3.65,-2.67 h 8.42 c 1.54,0 2.81,-0.28 2.81,-0.7 0,-0.42 -1.26,-0.7 -2.81,-0.7 h -8 c -1.54,0 -2.39,-1.12 -1.96,-2.67 z"/>
|
||||
<path class="st0" d="m 76.56,229.92 c 0.14,-1.54 -0.84,-2.81 -2.39,-2.81 h -4.21 c -1.54,0 -2.81,0.28 -2.95,0.7 -0.14,0.28 0.98,0.7 2.53,0.7 h 3.23 c 1.54,0 2.67,1.26 2.39,2.81 l -0.56,4.35 c -0.14,1.54 -1.68,2.81 -3.09,2.81 H 58.18 c -1.54,0 -2.81,0.28 -2.81,0.7 0,0.42 1.26,0.7 2.81,0.7 h 13.05 3.09 3.09 6.74 c 1.54,0 2.81,-0.28 2.81,-0.7 0,-0.42 -1.26,-0.7 -2.81,-0.7 H 78.1 c -1.54,0 -2.67,-1.26 -2.39,-2.81 z"/>
|
||||
<path class="st0" d="m 94.8,269.49 c 0.28,-1.54 0.14,-2.1 -0.42,-1.26 -0.42,0.84 -1.12,2.67 -1.4,4.07 l -0.42,2.24 c -0.28,1.54 0.14,3.79 0.98,5.05 l 0.7,0.98 c 0.84,1.26 2.25,2.81 3.09,3.37 0.84,0.56 0.84,0 0,-1.26 l -2.39,-3.51 c -0.84,-1.26 -1.26,-3.51 -0.98,-5.05 z"/>
|
||||
<path class="st0" d="m 115.71,229.5 c 0,-0.42 -1.26,-0.56 -2.81,-0.56 l -5.47,0.14 c -1.54,0 -2.1,0.28 -1.4,0.56 0.7,0.28 2.67,0.56 4.21,0.56 h 2.67 c 1.53,-0.14 2.8,-0.42 2.8,-0.7"/>
|
||||
<path class="st0" d="m 123.71,272.58 c 0.28,-1.54 0.14,-2.1 -0.42,-1.26 -0.56,0.84 -1.12,2.39 -1.4,3.37 -0.28,1.12 0,2.95 0.42,4.35 0.56,1.26 1.4,2.95 1.96,3.65 0.56,0.7 0.56,0.14 0.14,-1.4 l -0.7,-2.1 c -0.42,-1.4 -0.56,-3.79 -0.28,-5.33 z"/>
|
||||
<path class="st0" d="m 118.51,257.56 c -1.54,-0.14 -2.81,0.14 -2.81,0.56 0,0.42 1.26,0.7 2.67,0.84 l 15.43,0.84 c 1.54,0.14 3.23,0.7 3.65,1.54 0.42,0.7 1.12,1.26 1.4,0.98 0.28,-0.14 0,-1.12 -0.56,-1.96 -0.56,-0.84 -2.25,-1.68 -3.79,-1.82 z"/>
|
||||
<path class="st0" d="m 134.79,227.4 c 0.56,0.42 2.39,0.7 3.79,0.7 h 1.12 c 1.54,0 2.81,-0.28 2.81,-0.7 0,-0.42 -1.26,-0.7 -2.81,-0.7 l -3.37,-0.14 c -1.26,0.13 -2.1,0.42 -1.54,0.84"/>
|
||||
<path class="st0" d="m 153.31,257.98 c -1.54,0 -2.81,0.42 -2.81,0.7 0,0.42 1.26,0.56 2.81,0.56 l 8.98,-0.42 c 1.54,0 2.53,1.12 2.39,2.67 l -1.82,12.49 c -0.28,1.54 0,3.93 0.42,5.33 l 0.42,1.26 c 0.42,1.4 1.26,3.37 1.96,4.35 0.7,0.84 0.84,0.42 0.42,-0.98 l -1.4,-4.63 c -0.42,-1.4 -0.56,-3.93 -0.42,-5.33 l 2.1,-13.75 c 0.28,-1.54 -0.84,-2.67 -2.39,-2.67 z"/>
|
||||
<path class="st0" d="m 156.4,227.12 c 0,0.28 1.12,0.84 2.67,1.12 l 15.29,1.82 c 1.54,0.14 2.81,0 2.81,-0.28 0,-0.42 -1.26,-0.84 -2.81,-0.98 l -15.15,-1.82 c -1.55,-0.29 -2.81,-0.29 -2.81,0.14"/>
|
||||
<path class="st0" d="m 189.65,266.12 c 0,-1.54 -1.12,-2.81 -2.67,-2.81 h -5.47 c -1.54,0 -2.81,0.28 -2.81,0.7 0,0.42 1.26,0.7 2.81,0.7 h 4.07 c 1.54,0 2.67,1.26 2.67,2.81 l -0.28,6.45 c 0,1.54 0.28,3.93 0.56,5.47 l 0.98,3.93 c 0.42,1.54 1.12,3.37 1.54,4.35 0.42,0.84 0.56,0.42 0.28,-1.12 l -1.68,-7.3 c -0.28,-1.54 -0.56,-3.93 -0.56,-5.47 z"/>
|
||||
<path class="st0" d="m 182.08,234.69 c 0,0.42 1.26,0.7 2.81,0.7 h 5.19 c 1.54,0 3.37,-0.28 4.21,-0.7 0.7,-0.42 0.14,-0.7 -1.4,-0.7 l -8,0.14 c -1.55,-0.14 -2.81,0.14 -2.81,0.56"/>
|
||||
<path class="st0" d="m 217.71,241.29 c 0,-1.54 -0.42,-2.81 -0.7,-2.81 -0.28,0 -0.7,1.26 -0.7,2.81 v 6.31 c 0,1.54 0.28,2.1 0.7,1.12 0.42,-0.84 0.7,-2.81 0.7,-4.35 z"/>
|
||||
<path class="st0" d="m 223.89,264.86 c 0,1.54 -1.4,2.81 -2.81,2.81 h -4.21 c -1.54,0 -2.81,1.26 -2.81,2.81 v 4.21 c 0,1.54 0.28,2.81 0.7,2.81 0.42,0 0.7,-1.26 0.7,-2.81 v -2.81 c 0,-1.54 1.26,-2.81 2.81,-2.81 h 4.21 c 1.54,0 2.81,-1.26 2.81,-2.81 v -1.4 c 0,-1.54 -0.14,-2.81 -0.56,-2.81 -0.42,0 -0.7,1.13 -0.84,2.81 z"/>
|
||||
<path class="st0" d="m 276.93,267.67 c -0.28,0.56 -0.42,2.24 -0.28,3.79 l 0.28,2.67 c 0.14,1.54 0.14,3.93 -0.14,5.47 l -1.82,13.19 c -0.14,1.54 0,2.67 0.42,2.67 0.42,0 0.98,-1.26 1.12,-2.81 l 1.68,-13.05 c 0.14,-1.54 0.14,-3.93 0,-5.47 l -0.56,-4.63 c 0,-1.55 -0.42,-2.4 -0.7,-1.83"/>
|
||||
<path class="st0" d="m 238.06,288.15 c 0,0.42 1.26,0.7 2.81,0.7 h 0.7 c 1.54,0 2.81,-0.28 2.81,-0.7 0,-0.42 -1.26,-0.7 -2.81,-0.7 h -0.7 c -1.55,0 -2.81,0.28 -2.81,0.7"/>
|
||||
<path class="st0" d="m 242.97,236.38 c 0.7,0.42 2.53,0.98 3.93,1.26 l 10.66,1.96 c 1.4,0.56 2.67,0.84 2.81,0.56 0.14,-0.28 -0.98,-0.98 -2.39,-1.4 l -13.61,-2.39 c -1.4,-0.41 -2.1,-0.41 -1.4,0.01"/>
|
||||
<path class="st0" d="m 242.83,248.3 c 0.56,-0.42 -0.14,-0.7 -1.68,-0.7 h -0.56 c -1.54,0 -2.81,0.42 -2.81,0.7 0,0.42 0.84,0.7 1.96,0.7 1.13,0.14 2.39,-0.28 3.09,-0.7"/>
|
||||
<path class="st0" d="m 303.02,237.08 c 0.14,0.84 1.4,1.54 2.95,1.54 h 1.26 c 1.54,0 2.95,0 3.23,0.14 0.28,0.14 1.12,0.7 1.4,0.56 0.28,-0.14 0.14,-0.7 -0.56,-1.12 -0.56,-0.42 -2.39,-0.84 -3.93,-0.84 h -0.28 c -1.54,0 -2.95,-1.26 -3.09,-2.81 l -1.68,-15.29 c -0.14,-1.54 -0.56,-2.67 -0.98,-2.67 -0.28,0 -0.56,1.26 -0.28,2.81 l 1.4,12.07 c 0.14,1.54 -0.98,2.81 -2.53,2.81 h -4.35 c -1.54,0 -2.81,0.28 -2.81,0.7 0,0.42 1.26,0.7 2.81,0.7 h 4.49 c 1.55,-0.15 2.81,0.56 2.95,1.4"/>
|
||||
<path class="st0" d="m 301.2,267.39 c 0.14,1.54 -0.42,3.79 -1.12,5.19 l -1.12,1.26 c -0.42,1.54 -0.42,2.81 0,2.81 0.28,0.14 0.98,-0.98 1.54,-2.53 0,0 0.14,-0.7 0.7,-1.4 0.56,-0.84 1.12,-1.4 1.4,-1.4 0.14,0 0.28,-1.26 0.14,-2.81 l -0.42,-7.02 c -0.14,-1.54 1.12,-2.81 2.53,-2.81 h 10.38 c 1.54,0 2.81,-0.28 2.81,-0.7 0,-0.42 -1.26,-0.7 -2.81,-0.7 h -11.79 c -1.54,0 -2.67,1.26 -2.53,2.81 z"/>
|
||||
<path class="st0" d="m 322.95,262.05 c 0,0.42 1.26,0.7 2.81,0.7 h 7.02 c 1.54,0 3.37,-0.28 3.93,-0.7 0.7,-0.42 0,-0.7 -1.54,-0.7 h -9.4 c -1.56,0 -2.82,0.28 -2.82,0.7"/>
|
||||
<path class="st0" d="m 328.98,243.81 c 0.42,0.28 1.96,0.56 3.51,0.56 h 1.96 c 1.54,0 2.81,-0.28 2.81,-0.7 0,-0.42 -1.26,-0.7 -2.81,-0.56 h -3.37 c -1.68,0 -2.52,0.28 -2.1,0.7"/>
|
||||
<path class="st0" d="m 293.48,289.13 c 0,0.42 1.26,0.7 2.67,0.84 l 15.43,1.12 c 1.54,0.14 2.81,-0.14 2.81,-0.42 0,-0.42 -1.26,-0.7 -2.67,-0.84 l -15.43,-1.12 c -1.54,-0.14 -2.81,0.14 -2.81,0.42"/>
|
||||
<path class="st0" d="m 383.28,261.49 c -1.54,0 -2.81,0.28 -2.81,0.7 0,0.42 1.26,0.7 2.81,0.56 l 18.8,2.1 c 1.54,0.14 2.81,0 2.95,-0.28 0.14,-0.28 -0.98,-0.7 -2.53,-0.84 z"/>
|
||||
<path class="st0" d="m 441.93,237.64 c -0.28,0.14 -0.56,1.4 -0.42,2.95 l 1.68,8.7 c -0.7,1.4 -0.98,2.67 -0.56,2.81 0.28,0.14 1.12,-0.84 1.82,-2.24 l -1.68,-9.68 c -0.27,-1.56 -0.56,-2.68 -0.84,-2.54"/>
|
||||
<path class="st0" d="m 431.97,221.5 c -1.4,0 -2.81,-1.12 -3.23,-2.53 -0.42,-1.4 -1.12,-2.53 -1.4,-2.39 -0.28,0.14 -0.28,1.4 0.14,2.81 l 0.28,0.7 c 0.42,1.4 1.96,2.67 3.37,2.67 1.4,0 2.95,1.12 3.37,2.67 l 0.7,1.82 c 0.56,1.4 1.12,2.53 1.54,2.39 0.28,-0.14 0.14,-1.4 -0.28,-2.81 l -0.98,-2.81 c -0.7,-1.39 -2.25,-2.52 -3.51,-2.52"/>
|
||||
<path class="st0" d="m 483.05,258.97 c -1.54,0 -2.81,0.28 -2.81,0.7 0,0.42 1.26,0.7 2.81,0.7 h 17.68 c 1.54,0 2.81,-0.28 2.81,-0.7 0,-0.42 -1.26,-0.7 -2.81,-0.7 z"/>
|
||||
<path class="st0" d="m 496.38,228.66 c 0.28,-0.14 0.28,-1.4 -0.14,-2.81 l -1.82,-6.45 c -0.42,-1.4 -0.98,-2.53 -1.4,-2.53 -0.28,0.14 -0.28,1.4 0.14,2.81 l 1.82,6.45 c 0.41,1.41 1.11,2.53 1.4,2.53"/>
|
||||
<path class="st0" d="m 66.46,228.24 h 5.75 c 1.54,0 1.54,-0.28 0,-0.56 l -1.96,-0.42 c -1.54,-0.28 -2.67,0 -2.53,0.56 z"/>
|
||||
<path class="st0" d="m 34.05,285.63 c -0.56,-0.7 -2.24,-0.98 -3.79,-0.84 l -3.37,0.42 c -1.54,0.14 -1.54,0.42 0,0.7 l 5.47,0.7 c 1.55,0 2.25,-0.42 1.69,-0.98"/>
|
||||
<path class="st0" d="m 88.91,292.78 c -0.42,-0.7 -1.96,-1.12 -3.51,-0.98 l -7.16,0.56 c -1.54,0.14 -1.54,0.28 0,0.42 l 8.7,0.84 c 1.41,0.14 2.25,-0.28 1.97,-0.84"/>
|
||||
<path class="st0" d="m 155.28,289.98 c -0.14,-0.56 0.98,-0.98 2.39,-0.98 h 1.82 c 1.54,0 1.54,0.42 0.14,0.84 l -1.4,0.42 c -1.55,0.56 -2.81,0.42 -2.95,-0.28"/>
|
||||
<path class="st0" d="m 231.18,285.77 c -0.14,-0.84 -1.4,-1.4 -2.95,-1.26 l -5.05,0.7 c -1.54,0.28 -1.54,0.56 0,0.84 l 5.47,0.84 c 1.55,0.42 2.68,-0.14 2.53,-1.12"/>
|
||||
<path class="st0" d="m 237.5,288.85 c -0.14,-0.84 1.12,-1.4 2.67,-1.26 h 0.14 c 1.54,0.14 1.68,0.84 0.28,1.54 l -0.42,0.14 c -1.41,0.71 -2.53,0.57 -2.67,-0.42"/>
|
||||
<path class="st0" d="m 189.51,274.12 c -0.14,-1.26 -0.56,-1.4 -0.7,-0.28 -0.28,1.12 -0.14,3.37 0.42,4.77 l 0.28,1.12 c 0.42,1.4 0.7,1.4 0.56,-0.14 v -0.56 c -0.14,-1.4 -0.42,-3.65 -0.56,-4.91"/>
|
||||
<path class="st0" d="m 214.63,275.1 c -0.28,-0.56 -0.56,0.28 -0.42,1.82 v 0.7 c 0,1.54 0.28,1.82 0.56,0.56 0.28,-1.25 0.14,-2.66 -0.14,-3.08"/>
|
||||
<path class="st0" d="m 163.55,274.54 c 0,-1.26 -0.28,-1.26 -0.56,0 -0.28,1.26 0,3.51 0.42,5.05 l 0.42,1.26 c 0.42,1.4 0.7,1.4 0.42,-0.14 l -0.14,-0.98 c -0.13,-1.68 -0.42,-3.93 -0.56,-5.19"/>
|
||||
<path class="st0" d="m 122.58,275.52 c -0.28,-0.56 -0.56,-0.42 -0.84,0.28 -0.28,0.7 0,1.82 0.56,2.67 0.56,0.85 0.98,0.7 0.84,-0.28 -0.14,-0.98 -0.28,-2.25 -0.56,-2.67"/>
|
||||
<path class="st0" d="m 93.96,274.54 c -0.28,-1.26 -0.84,-1.12 -1.12,0.28 v 0 c -0.42,1.54 0.28,3.51 1.26,4.63 1.12,1.12 1.54,0.98 1.12,-0.28 -0.42,-1.26 -0.98,-3.37 -1.26,-4.63"/>
|
||||
<path class="st0" d="m 42.32,272.72 c -0.42,-0.56 0.42,-0.84 1.96,-0.7 l 4.35,0.42 c 1.54,0.14 1.54,0.42 0,0.56 l -2.95,0.42 c -1.39,0.14 -2.93,-0.14 -3.36,-0.7"/>
|
||||
<path class="st0" d="m 277.35,273.56 c 0.28,-1.54 0.7,-1.54 0.98,0 l 0.14,0.56 c 0.28,1.54 0.28,3.93 -0.14,5.33 l -0.56,1.96 c -0.42,1.4 -0.7,1.4 -0.84,-0.14 v -1.82 c 0,-1.54 0.14,-4.07 0.28,-5.47 z"/>
|
||||
<path class="st0" d="m 260.09,240.31 c 0,0.7 -0.98,0.42 -2.24,-0.42 l -0.84,-0.56 c -1.26,-0.84 -1.12,-1.26 0.42,-0.84 v 0 c 1.4,0.41 2.66,1.25 2.66,1.82"/>
|
||||
<path class="st0" d="m 344.14,284.22 c 0.14,-0.56 -0.98,-0.98 -2.39,-0.98 h -1.96 c -1.54,0 -1.54,0.42 -0.14,0.84 l 1.54,0.42 c 1.4,0.42 2.81,0.28 2.95,-0.28"/>
|
||||
<path class="st0" d="m 333.33,275.1 c -0.42,-0.84 -0.7,-0.42 -0.7,1.12 v 0.28 c 0,1.54 0.42,2.25 0.7,1.4 0.56,-0.69 0.42,-1.96 0,-2.8"/>
|
||||
<path class="st0" d="m 371.64,286.89 c 0.28,-0.7 -0.84,-1.12 -2.39,-0.98 l -5.19,0.42 c -1.54,0.14 -1.54,0.42 0,0.7 l 4.49,0.7 c 1.4,0.14 2.81,-0.14 3.09,-0.84"/>
|
||||
<path class="st0" d="m 366.31,275.38 c -0.28,-0.7 -0.56,-0.7 -0.7,-0.14 -0.14,0.7 -0.14,2.1 0.14,3.23 0.28,1.12 0.56,1.12 0.7,0.14 0.14,-1.12 0,-2.53 -0.14,-3.23"/>
|
||||
<path class="st0" d="m 442.64,252.37 c -0.56,-0.14 -0.42,-1.4 0.14,-2.81 0.56,-1.4 0.98,-1.26 0.98,0.28 v 0.14 c -0.14,1.55 -0.56,2.67 -1.12,2.39"/>
|
||||
<path class="st0" d="m 406.44,285.35 c 0.56,-0.7 2.24,-0.98 3.65,-0.84 l 7.44,1.12 c 1.54,0.28 1.54,0.42 0,0.42 l -9.4,0.28 c -1.41,0.28 -2.26,-0.28 -1.69,-0.98"/>
|
||||
<path class="st0" d="m 477.15,285.35 c 0.7,-0.84 2.39,-1.4 3.93,-1.12 l 6.88,1.26 c 1.54,0.28 1.4,0.56 0,0.7 l -9.12,0.56 c -1.69,0.14 -2.39,-0.42 -1.69,-1.4"/>
|
||||
<path class="st0" d="m 479.96,259.53 c -0.28,-0.98 0.7,-1.68 2.25,-1.4 l 6.17,0.84 c 1.54,0.28 1.54,0.7 0,0.84 l -5.05,0.98 c -1.69,0.28 -3.09,-0.28 -3.37,-1.26"/>
|
||||
<path class="st0" d="m 472.66,229.64 c -0.42,-0.42 -0.84,-1.96 -0.98,-3.51 v -0.28 c 0,-1.54 0.28,-1.54 0.56,-0.14 l 0.56,1.96 c 0.42,1.41 0.42,2.39 -0.14,1.97"/>
|
||||
<path class="st0" d="m 404.19,265.7 -3.93,-1.54 c -1.4,-0.56 -1.4,-0.84 0.14,-0.7 l 4.07,0.42"/>
|
||||
<path class="st0" d="m 256.3,278.19 c 0.28,-0.84 0.28,-2.1 0,-2.95 -0.28,-0.7 -1.82,-1.4 -3.37,-1.4 h -1.54 c -1.54,0 -3.23,0.7 -3.79,1.68 -0.56,0.84 0.28,1.96 1.68,2.39 l 3.79,1.12 c 1.41,0.28 2.95,0 3.23,-0.84"/>
|
||||
<path class="st0" d="m 253.63,275.94 c -0.98,-1.12 -0.98,-1.96 0.14,-1.96 1.12,0 2.39,0.56 2.95,1.12 0.56,0.7 0.28,2.24 -0.56,3.51 l -0.28,0.42 c -0.84,1.26 -1.26,1.54 -0.98,0.56 0.42,-0.84 -0.28,-2.52 -1.27,-3.65"/>
|
||||
<path class="st0" d="m 156.12,227.54 c 0.14,-0.84 1.4,-1.26 2.95,-0.84 l 0.7,0.14 c 1.54,0.42 1.4,0.98 0,1.26 l -1.12,0.28 c -1.41,0.42 -2.53,0 -2.53,-0.84"/>
|
||||
<path class="st0" d="m 86.52,239.32 c -0.28,0.56 -1.68,0.7 -3.23,0.28 l -0.84,-0.14 c -1.54,-0.42 -1.4,-0.7 0.14,-0.84 l 1.82,-0.14 c 1.41,-0.14 2.39,0.28 2.11,0.84"/>
|
||||
<path class="st0" d="m 30.4,263.04 c 0.28,-0.7 -0.84,-1.12 -2.24,-0.7 l -2.39,0.42 c -1.54,0.28 -1.54,0.7 0,0.98 l 1.4,0.28 c 1.54,0.14 2.95,-0.28 3.23,-0.98"/>
|
||||
<path class="st2" d="m 34.61,285.77 c -0.42,-0.56 -0.56,-2.1 -0.14,-3.65 l 0.56,-1.96 c 0.42,-1.4 0.7,-1.4 0.7,0.14 l -0.14,3.79 c 0,1.4 -0.42,2.24 -0.98,1.68"/>
|
||||
<path class="st2" d="m 88.49,292.92 c -0.42,-0.28 -0.42,-1.54 0,-2.95 0.42,-1.26 0.84,-1.12 0.84,0.42 v 0.28 c 0,1.55 -0.28,2.53 -0.84,2.25"/>
|
||||
<path class="st2" d="m 137.18,260.51 c 0,-0.98 0.28,-0.56 0.84,0.84 l 0.28,0.84 c 0.56,1.4 0.56,1.96 0,1.26 -0.56,-0.69 -1.12,-1.96 -1.12,-2.94"/>
|
||||
<path class="st2" d="m 407.14,286.19 c 0.56,-0.42 0.56,-1.96 0.14,-3.37 l -0.7,-2.24 c -0.42,-1.4 -0.7,-1.4 -0.7,0.14 l 0.14,3.51 c 0.13,1.54 0.56,2.38 1.12,1.96"/>
|
||||
<path class="st2" d="m 478.13,285.77 c 0.7,-0.42 0.84,-1.82 0.28,-3.23 l -2.81,-6.17 c -0.7,-1.4 -0.98,-1.26 -0.7,0.14 l 1.4,7.3 c 0.29,1.54 1.13,2.38 1.83,1.96"/>
|
||||
<path class="st2" d="m 481.22,259.39 c -0.28,-0.56 -1.12,0 -1.82,1.4 l -2.95,6.03 c -0.7,1.4 -0.42,1.54 0.42,0.28 l 3.23,-4.35 c 0.98,-1.26 1.4,-2.66 1.12,-3.36"/>
|
||||
<path class="st0" d="m 434.92,229.92 0.14,-3.51 c 0,-1.54 0.42,-1.54 0.84,-0.14 l 0.98,4.07"/>
|
||||
<path class="st3" d="m 24.36,287.31 c -1.4,-0.42 -1.26,-0.7 0.28,-0.56 1.54,0.14 3.65,0.42 4.91,0.56 1.12,0.14 1.12,0.42 -0.28,0.56 -1.26,0.14 -3.5,-0.14 -4.91,-0.56"/>
|
||||
<path class="st3" d="m 92.7,279.73 c -1.12,-1.12 -1.26,-0.98 -0.28,0.28 l 1.12,1.54 c 0.84,1.26 2.25,2.67 3.09,3.23 0.84,0.56 0.84,0.14 -0.14,-0.98 -0.84,-1.12 -2.53,-2.81 -3.51,-3.93 z"/>
|
||||
<path class="st3" d="m 120.34,276.65 c -0.42,0 -0.14,0.98 0.42,2.25 0.56,1.26 0.84,1.26 0.7,0 -0.28,-1.27 -0.7,-2.25 -1.12,-2.25"/>
|
||||
<path class="st3" d="m 160.05,290.4 c -1.12,-0.14 -0.98,-0.42 0.56,-0.7 l 1.68,-0.28 c 1.54,-0.28 2.1,-0.14 1.26,0.42 -0.7,0.42 -2.24,0.7 -3.5,0.56"/>
|
||||
<path class="st3" d="m 187.69,278.89 c -0.56,-0.7 -0.7,-0.28 -0.42,1.12 0.28,1.4 1.12,3.51 1.68,4.63 0.7,1.12 0.84,0.98 0.56,-0.56 l -0.28,-1.12 c -0.28,-1.4 -0.98,-3.23 -1.54,-4.07"/>
|
||||
<path class="st3" d="m 279.31,281.28 c -0.14,-0.84 -0.56,-0.7 -0.98,0.28 -0.42,1.12 -0.7,3.09 -0.7,4.49 0,1.4 0.42,1.4 0.84,0 l 0.14,-0.56 c 0.7,-1.41 0.84,-3.37 0.7,-4.21"/>
|
||||
<path class="st3" d="m 248.86,275.38 c -0.28,-0.84 -0.98,-0.7 -1.54,0.28 -0.56,1.12 0.14,2.53 1.54,3.23 v 0 c 1.4,0.7 2.1,0.56 1.54,-0.42 -0.55,-0.7 -1.26,-2.24 -1.54,-3.09"/>
|
||||
<path class="st3" d="m 299.94,290.82 c -1.4,-0.42 -1.26,-0.7 0.28,-0.7 h 0.56 c 1.54,0 3.93,0.42 5.19,0.84 1.4,0.42 1.26,0.84 -0.28,0.7 h -0.56 c -1.4,0 -3.79,-0.42 -5.19,-0.84"/>
|
||||
<path class="st3" d="m 334.6,278.89 c -0.14,-0.56 -0.56,0 -0.98,1.12 -0.42,1.12 -0.28,1.54 0.28,0.98 0.41,-0.56 0.84,-1.54 0.7,-2.1"/>
|
||||
<path class="st3" d="m 367.85,277.77 c -0.14,-0.56 -0.56,0 -0.98,1.12 -0.42,1.12 -0.28,1.54 0.28,0.98 0.42,-0.7 0.84,-1.54 0.7,-2.1"/>
|
||||
<path class="st3" d="m 362.52,287.73 c -1.54,-0.28 -1.54,-0.7 0,-0.84 l 1.12,-0.14 c 1.54,-0.14 3.37,0.14 4.21,0.56 0.7,0.42 0.56,0.84 -0.56,0.84 -1.12,0 -3.23,-0.28 -4.63,-0.56 z"/>
|
||||
<path class="st3" d="m 411.35,287.59 c -1.4,0.14 -1.4,-0.14 0,-0.56 1.4,-0.42 3.79,-0.7 5.19,-0.56 l 1.96,0.14 c 1.54,0.14 1.54,0.28 0,0.42 l -1.96,0.14 c -1.41,0.14 -3.79,0.42 -5.19,0.42"/>
|
||||
<path class="st2" d="m 442.92,252.79 c -0.7,0.28 -1.82,1.68 -2.39,3.09 v 0 c -0.56,1.4 -0.28,1.54 0.56,0.42 l 1.4,-1.82 c 0.99,-1.13 1.13,-1.97 0.43,-1.69"/>
|
||||
<path class="st0" d="m 496.52,229.22 c -0.7,0 -1.26,-1.26 -1.4,-2.81 l -0.14,-1.12 c -0.14,-1.54 0.28,-1.68 0.84,-0.28 l 0.7,1.68 c 0.7,1.41 0.7,2.53 0,2.53"/>
|
||||
<path class="st0" d="m 292.92,235.39 c 0,-0.7 1.12,-1.4 2.67,-1.26 h 0.98 c 1.54,0 1.68,0.56 0.28,1.12 l -1.12,0.42 c -1.54,0.71 -2.81,0.57 -2.81,-0.28"/>
|
||||
<path class="st0" d="m 217.15,238.2 c -0.56,-0.28 -0.98,0.7 -0.7,2.25 l 0.14,1.26 c 0.14,1.54 0.7,1.54 0.98,0 v -0.14 c 0.43,-1.55 0.14,-3.09 -0.42,-3.37"/>
|
||||
<path class="st0" d="m 149.38,259.25 c -0.14,-0.56 0.98,-1.12 2.39,-1.12 h 5.89 c 1.54,0 1.54,0.28 0,0.56 l -5.33,1.12 c -1.4,0.28 -2.81,0 -2.95,-0.56"/>
|
||||
<path class="st0" d="m 29.13,271.03 c 0.14,-0.42 -1.12,-0.84 -2.67,-0.84 h -1.4 c -1.54,0 -1.54,0.28 -0.14,0.7 l 1.26,0.28 c 1.55,0.42 2.81,0.28 2.95,-0.14"/>
|
||||
<path class="st4" d="m 7.67,275.94 c -1.54,0 -2.53,0.28 -2.24,0.56 0.28,0.28 1.82,0.56 3.37,0.56 h 11.22 c 1.54,0 2.95,-0.28 3.37,-0.7 0.28,-0.42 -0.7,-0.7 -2.24,-0.56 z"/>
|
||||
<path class="st4" d="m 58.74,275.94 c -1.54,0 -2.53,0.28 -2.24,0.56 0.28,0.28 1.82,0.56 3.37,0.56 h 11.22 c 1.54,0 2.95,-0.28 3.37,-0.7 0.28,-0.42 -0.7,-0.7 -2.25,-0.56 z"/>
|
||||
<path class="st4" d="m 87.5,275.8 c -1.54,0 -2.67,0.28 -2.53,0.56 0.14,0.28 1.4,0.56 2.95,0.56 h 0.14 c 1.54,0 2.81,-0.28 2.95,-0.7 0.14,-0.42 -1.12,-0.56 -2.53,-0.56 z"/>
|
||||
<path class="st4" d="m 36.29,250.69 c -1.54,0 -2.67,0.28 -2.53,0.7 0.14,0.28 1.4,0.56 2.95,0.56 h 0.14 c 1.54,0 2.81,-0.28 2.95,-0.7 0.14,-0.42 -1.12,-0.56 -2.53,-0.56 z"/>
|
||||
<path class="st4" d="m 57.34,250.69 c -1.54,0 -2.67,0.28 -2.53,0.7 0.14,0.28 1.4,0.56 2.95,0.56 h 0.14 c 1.54,0 2.81,-0.28 2.95,-0.7 0.14,-0.42 -1.12,-0.56 -2.53,-0.56 z"/>
|
||||
<path class="st4" d="m 15.1,254.06 c -1.54,0 -2.67,0.28 -2.67,0.7 0.14,0.28 1.26,0.56 2.81,0.56 1.4,0 2.67,-0.28 2.81,-0.7 0.14,-0.42 -1.12,-0.56 -2.67,-0.56 z"/>
|
||||
<path class="st4" d="m 40.5,262.76 c -1.54,0 -2.67,0.28 -2.39,0.56 0.14,0.28 1.54,0.56 3.09,0.56 h 5.05 c 1.54,0 2.95,-0.28 3.09,-0.7 0.14,-0.42 -0.84,-0.56 -2.39,-0.56 z"/>
|
||||
<path class="st4" d="m 28.85,225.85 c -1.54,0 -2.67,0.28 -2.39,0.56 0.14,0.28 1.54,0.56 3.09,0.56 h 5.05 c 1.54,0 2.95,-0.28 3.09,-0.7 0.14,-0.42 -0.84,-0.56 -2.39,-0.56 z"/>
|
||||
<path class="st4" d="m 116.83,275.8 c -1.54,0 -2.67,0.28 -2.53,0.56 0.14,0.28 1.4,0.56 2.95,0.56 h 0.14 c 1.54,0 2.81,-0.28 2.95,-0.7 0.14,-0.42 -1.12,-0.56 -2.53,-0.56 z"/>
|
||||
<path class="st4" d="m 167.06,275.8 c -1.54,0 -2.67,0.28 -2.67,0.7 0.14,0.28 1.26,0.56 2.81,0.56 1.4,0 2.67,-0.28 2.81,-0.7 0.14,-0.42 -1.12,-0.56 -2.67,-0.56 z"/>
|
||||
<path class="st4" d="m 126.23,275.8 c -1.54,0 -2.67,0.28 -2.53,0.56 0.14,0.28 1.54,0.56 3.09,0.56 h 3.79 c 1.54,0 2.95,-0.28 3.09,-0.7 0.14,-0.42 -0.98,-0.56 -2.53,-0.56 z"/>
|
||||
<path class="st4" d="m 152.89,275.8 c -1.54,0 -2.67,0.28 -2.39,0.56 0.14,0.28 1.54,0.56 3.09,0.56 h 4.77 c 1.54,0 2.95,-0.28 3.09,-0.7 0.14,-0.42 -0.98,-0.56 -2.39,-0.56 z"/>
|
||||
<path class="st4" d="m 182.36,275.8 c -1.54,0 -2.67,0.28 -2.53,0.56 0.14,0.28 1.54,0.56 2.95,0.56 h 2.1 c 1.54,0 2.81,-0.28 2.95,-0.7 0.14,-0.42 -0.98,-0.56 -2.53,-0.56 z"/>
|
||||
<path class="st4" d="m 240.59,275.8 c -1.54,0 -2.67,0.28 -2.53,0.56 0.14,0.28 1.54,0.56 2.95,0.56 h 2.1 c 1.54,0 2.95,-0.28 2.95,-0.7 0.14,-0.42 -0.98,-0.56 -2.53,-0.56 z"/>
|
||||
<path class="st4" d="m 282.12,275.8 c -1.54,0 -2.67,0.28 -2.53,0.56 0.14,0.28 1.4,0.56 2.95,0.56 h 1.26 c 1.54,0 2.81,-0.28 2.95,-0.7 0.14,-0.42 -0.98,-0.56 -2.53,-0.56 z"/>
|
||||
<path class="st4" d="m 302.88,275.8 c -1.54,0 -2.53,0.28 -2.24,0.56 0.28,0.28 1.68,0.56 3.23,0.56 h 8.98 c 1.54,0 2.95,-0.28 3.23,-0.7 0.28,-0.42 -0.84,-0.7 -2.25,-0.56 z"/>
|
||||
<path class="st4" d="m 328.42,275.8 c -1.54,0 -2.67,0.28 -2.53,0.56 0.14,0.28 1.4,0.56 2.95,0.56 h 0.42 c 1.54,0 2.81,-0.28 2.95,-0.7 0.14,-0.42 -0.98,-0.56 -2.53,-0.56 z"/>
|
||||
<path class="st4" d="m 337.68,275.8 c -1.54,0 -2.67,0.28 -2.53,0.56 0.14,0.28 1.54,0.56 3.09,0.56 h 3.93 c 1.54,0 2.95,-0.28 3.09,-0.7 0.14,-0.42 -0.98,-0.56 -2.53,-0.56 z"/>
|
||||
<path class="st4" d="m 358.03,275.8 c -1.54,0 -2.67,0.28 -2.53,0.56 0.14,0.28 1.54,0.56 3.09,0.56 h 2.39 c 1.54,0 2.95,-0.28 3.09,-0.7 0.14,-0.42 -0.98,-0.56 -2.53,-0.56 z"/>
|
||||
<path class="st4" d="m 370.09,275.8 c -1.54,0 -2.67,0.28 -2.53,0.56 0.14,0.28 1.4,0.56 2.95,0.56 h 0.98 c 1.54,0 2.81,-0.28 2.95,-0.7 0.14,-0.42 -0.98,-0.56 -2.53,-0.56 z"/>
|
||||
<path class="st4" d="m 387.21,275.8 c -1.54,0 -2.53,0.28 -2.25,0.56 0.28,0.28 1.82,0.56 3.37,0.56 h 12.49 c 1.54,0 3.09,-0.28 3.37,-0.7 0.28,-0.42 -0.7,-0.7 -2.24,-0.56 z"/>
|
||||
<path class="st4" d="m 416.26,275.8 c -1.54,0 -2.53,0.28 -2.25,0.56 0.28,0.28 1.82,0.56 3.37,0.56 h 12.49 c 1.54,0 3.09,-0.28 3.37,-0.7 0.28,-0.42 -0.7,-0.7 -2.24,-0.56 z"/>
|
||||
<path class="st4" d="m 489.78,275.8 c -1.54,0 -2.53,0.28 -2.24,0.56 0.28,0.28 1.82,0.56 3.37,0.56 h 12.49 c 1.54,0 3.09,-0.28 3.37,-0.7 0.28,-0.42 -0.7,-0.7 -2.24,-0.56 z"/>
|
||||
<path class="st4" d="m 462.84,226.84 c -1.54,0 -2.67,0.28 -2.39,0.56 0.14,0.28 1.54,0.56 3.09,0.56 h 4.63 c 1.54,0 2.95,-0.28 3.09,-0.7 0.14,-0.42 -0.98,-0.56 -2.39,-0.56 z"/>
|
||||
<path class="st4" d="m 439.97,226.84 c -1.54,0 -2.53,0.28 -2.39,0.56 0.14,0.28 1.68,0.56 3.09,0.56 h 6.03 c 1.54,0 2.95,-0.28 3.09,-0.7 0.14,-0.42 -0.84,-0.56 -2.39,-0.56 z"/>
|
||||
<path class="st4" d="m 428.32,226.84 c -1.54,0 -2.67,0.28 -2.53,0.56 0.14,0.28 1.54,0.56 3.09,0.56 h 2.53 c 1.54,0 2.95,-0.28 3.09,-0.7 0.14,-0.42 -0.98,-0.56 -2.53,-0.56 z"/>
|
||||
<path class="st4" d="m 432.11,250.55 c -1.54,0 -2.67,0.28 -2.39,0.56 0.14,0.28 1.54,0.56 3.09,0.56 h 4.91 c 1.54,0 2.95,-0.28 3.09,-0.7 0.14,-0.42 -0.84,-0.56 -2.39,-0.56 z"/>
|
||||
<path class="st4" d="m 361.25,225.15 c -1.54,0 -2.67,0.28 -2.53,0.56 0.14,0.28 1.54,0.56 3.09,0.56 h 2.53 c 1.54,0 2.95,-0.28 3.09,-0.7 0.14,-0.42 -0.98,-0.56 -2.53,-0.56 z"/>
|
||||
<path class="st4" d="m 377.67,225.15 c -1.54,0 -2.67,0.28 -2.53,0.56 0.14,0.28 1.54,0.56 3.09,0.56 h 2.53 c 1.54,0 2.95,-0.28 3.09,-0.7 0.14,-0.42 -0.98,-0.56 -2.53,-0.56 z"/>
|
||||
<path class="st4" d="m 367.15,235.67 c -1.26,0 -2.25,0.28 -2.1,0.7 0.14,0.28 0.98,0.56 2.1,0.56 1.12,0 2.1,-0.28 2.1,-0.7 0,-0.41 -0.84,-0.56 -2.1,-0.56"/>
|
||||
<path class="st4" d="m 373.04,232.31 c -1.54,0 -2.67,0.28 -2.67,0.7 0.14,0.28 1.26,0.56 2.67,0.56 1.4,0 2.53,-0.28 2.67,-0.7 0,-0.42 -1.13,-0.7 -2.67,-0.56 z"/>
|
||||
<path class="st4" d="m 299.66,232.87 c -1.54,0 -2.67,0.28 -2.67,0.7 0.14,0.28 1.26,0.56 2.67,0.56 1.4,0 2.53,-0.28 2.67,-0.7 0.13,-0.28 -1.13,-0.56 -2.67,-0.56 z"/>
|
||||
<path class="st4" d="m 318.04,250.13 c -1.54,0 -2.67,0.28 -2.53,0.56 0.14,0.28 1.4,0.56 2.95,0.56 h 0.42 c 1.54,0 2.81,-0.28 2.95,-0.7 0.14,-0.42 -0.98,-0.56 -2.53,-0.56 z"/>
|
||||
<path class="st4" d="m 305.13,256.3 c -1.54,0 -2.67,0.28 -2.53,0.56 0.14,0.28 1.54,0.56 3.09,0.56 h 3.79 c 1.54,0 2.95,-0.28 3.09,-0.7 0.14,-0.42 -0.98,-0.56 -2.53,-0.56 z"/>
|
||||
<path class="st4" d="m 488.1,257.56 c -1.54,0 -2.67,0.28 -2.53,0.56 0.14,0.28 1.54,0.56 3.09,0.56 h 3.79 c 1.54,0 2.95,-0.28 3.09,-0.7 0.14,-0.42 -0.98,-0.56 -2.53,-0.56 z"/>
|
||||
<path class="st4" d="m 183.76,262.33 c -1.54,0 -2.67,0.28 -2.53,0.56 0.14,0.28 1.4,0.56 2.95,0.56 h 1.54 c 1.54,0 2.81,-0.28 2.95,-0.7 0.14,-0.42 -0.98,-0.56 -2.53,-0.56 z"/>
|
||||
<path class="st4" d="m 157.94,257.14 c -1.54,0 -2.67,0.28 -2.53,0.56 0.14,0.28 1.4,0.56 2.95,0.56 h 1.54 c 1.54,0 2.81,-0.28 2.95,-0.7 0.14,-0.42 -0.98,-0.56 -2.53,-0.56 z"/>
|
||||
<path class="st4" d="m 121.04,256.72 c -1.54,0 -2.67,0.28 -2.53,0.56 0.14,0.28 1.54,0.56 2.95,0.56 H 123 c 1.54,0 2.81,-0.28 2.95,-0.7 0.14,-0.42 -0.98,-0.56 -2.53,-0.56 z"/>
|
||||
<path class="st4" d="m 65.34,236.94 c -1.54,0 -2.67,0.28 -2.53,0.56 0.14,0.28 1.54,0.56 3.09,0.56 h 4.07 c 1.54,0 2.95,-0.28 3.09,-0.7 0.14,-0.42 -0.98,-0.56 -2.39,-0.56 z"/>
|
||||
<path class="st4" d="m 170.01,227.68 c -1.54,-0.14 -2.67,0 -2.67,0.28 0.14,0.28 1.4,0.7 2.95,0.98 l 1.4,0.14 c 1.54,0.14 2.95,0.14 3.09,-0.28 0.14,-0.28 -0.98,-0.7 -2.39,-0.84 z"/>
|
||||
<path class="st4" d="m 328.56,260.23 c -1.54,0 -2.67,0.28 -2.53,0.56 0.14,0.28 1.4,0.56 2.95,0.56 h 1.54 c 1.54,0 2.81,-0.28 2.95,-0.7 0.14,-0.42 -0.98,-0.56 -2.53,-0.56 z"/>
|
||||
<path class="st4" d="m 375.15,255.74 c -1.54,0 -2.67,0.28 -2.53,0.56 0.14,0.28 1.4,0.56 2.95,0.56 h 1.54 c 1.54,0 2.81,-0.28 2.95,-0.7 0.14,-0.42 -0.98,-0.56 -2.53,-0.56 z"/>
|
||||
<path class="st4" d="m 263.32,226.41 c -1.54,0 -2.39,0.28 -2.1,0.56 0.42,0.28 1.96,0.56 3.51,0.56 h 16.42 c 1.54,0 3.09,-0.28 3.51,-0.7 0.42,-0.42 -0.56,-0.7 -2.1,-0.56 z"/>
|
||||
<path class="st4" d="m 207.89,226.55 c -1.54,0 -2.39,0.28 -2.1,0.56 0.42,0.28 1.96,0.56 3.51,0.56 h 17.12 c 1.54,0 3.09,-0.28 3.51,-0.7 0.42,-0.42 -0.56,-0.7 -2.1,-0.56 z"/>
|
||||
<path class="st4" d="m 204.95,250.13 c -1.54,0 -2.53,0.28 -2.39,0.56 0.28,0.28 1.68,0.56 3.23,0.56 h 6.74 c 1.54,0 2.95,-0.28 3.23,-0.7 0.28,-0.42 -0.84,-0.56 -2.39,-0.56 z"/>
|
||||
<path class="st4" d="m 219.82,250.13 c -1.54,0 -2.67,0.28 -2.39,0.56 0.14,0.28 1.68,0.56 3.09,0.56 h 6.03 c 1.54,0 2.95,-0.28 3.09,-0.7 0.14,-0.42 -0.84,-0.56 -2.39,-0.56 z"/>
|
||||
<path class="st4" d="m 251.67,272.72 c -1.54,0 -2.67,0.28 -2.53,0.7 0.14,0.28 1.4,0.56 2.95,0.56 v 0 c 1.54,0 2.81,-0.28 2.95,-0.7 0.14,-0.42 -1.12,-0.56 -2.53,-0.56 z"/>
|
||||
<path class="st4" d="m 247.32,234.83 c -1.54,-0.14 -2.67,0 -2.53,0.28 0.14,0.28 1.4,0.7 2.95,0.98 l 2.81,0.42 c 1.54,0.14 2.95,0.14 3.09,-0.28 0.14,-0.28 -0.84,-0.7 -2.39,-0.98 z"/>
|
||||
<path class="st4" d="m 386.23,260.37 c -1.54,-0.14 -2.67,0 -2.53,0.28 0.14,0.28 1.4,0.7 2.95,0.98 l 2.81,0.42 c 1.54,0.14 2.95,0.14 3.09,-0.28 0.14,-0.28 -0.84,-0.7 -2.39,-0.98 z"/>
|
||||
<path class="st4" d="m 381.18,235.67 c -1.12,0 -1.96,0.28 -1.96,0.7 0,0.28 0.98,0.56 1.96,0.56 0.98,0 1.82,-0.28 1.96,-0.7 0.14,-0.41 -0.84,-0.56 -1.96,-0.56"/>
|
||||
<path class="st4" d="m 446.85,250.55 c -1.54,0 -2.67,0.28 -2.39,0.56 0.14,0.28 1.68,0.56 3.09,0.56 h 6.03 c 1.54,0 2.95,-0.28 3.09,-0.7 0.14,-0.42 -0.84,-0.56 -2.39,-0.56 z"/>
|
||||
<path class="st4" d="m 500.16,226.84 c -1.54,0 -2.67,0.28 -2.53,0.56 0.14,0.28 1.54,0.56 3.09,0.56 h 2.81 c 1.54,0 2.95,-0.28 3.09,-0.7 0.14,-0.42 -0.98,-0.56 -2.53,-0.56 z"/>
|
||||
<path class="st4" d="m 261.07,275.8 c -1.54,0 -2.53,0.28 -2.24,0.56 0.28,0.28 1.82,0.56 3.23,0.56 h 10.38 c 1.54,0 2.95,-0.28 3.23,-0.7 0.28,-0.42 -0.7,-0.7 -2.25,-0.56 z"/>
|
||||
<path class="st4" d="m 193.16,275.8 c -1.54,0 -2.53,0.28 -2.1,0.56 0.42,0.28 1.96,0.56 3.37,0.56 h 15.43 c 1.54,0 3.09,-0.28 3.51,-0.7 0.42,-0.42 -0.56,-0.7 -2.1,-0.56 z"/>
|
||||
<path class="st4" d="m 218.42,275.8 c -1.54,0 -2.67,0.28 -2.39,0.56 0.14,0.28 1.54,0.56 3.09,0.56 h 5.89 c 1.54,0 2.95,-0.28 3.09,-0.7 0.14,-0.42 -0.84,-0.56 -2.39,-0.56 z"/>
|
||||
<path class="st4" d="m 98.45,275.94 c -1.54,0 -2.67,0.28 -2.53,0.56 0.14,0.28 1.54,0.56 3.09,0.56 h 2.53 c 1.54,0 2.95,-0.28 3.09,-0.7 0.14,-0.42 -0.98,-0.56 -2.53,-0.56 z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 32 KiB |
@ -7,9 +7,9 @@ export class Server {
|
||||
readonly name: string,
|
||||
readonly motd: string,
|
||||
readonly mode: Mode,
|
||||
readonly serverPort: number,
|
||||
readonly port: number,
|
||||
readonly running: boolean,
|
||||
readonly hasIcon: boolean,
|
||||
readonly icon: boolean,
|
||||
) {
|
||||
//
|
||||
}
|
||||
@ -19,9 +19,9 @@ export class Server {
|
||||
validateString(json.name),
|
||||
validateString(json.motd),
|
||||
validateString(json.mode) as Mode,
|
||||
validateNumber(json.serverPort),
|
||||
validateNumber(json.port),
|
||||
validateBoolean(json.running),
|
||||
validateBoolean(json.hasIcon),
|
||||
validateBoolean(json.icon),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -1,22 +1,17 @@
|
||||
<div class="heading">
|
||||
Serverliste
|
||||
</div>
|
||||
|
||||
<div class="hint">
|
||||
<img src="info.svg" alt="(i)">
|
||||
Beim Starten eines Servers werden alle anderen gestoppt.
|
||||
<img src="minecraft.svg" alt="Minecraft">
|
||||
</div>
|
||||
|
||||
<div class="list">
|
||||
<div *ngFor="let server of servers()" class="server">
|
||||
<div class="icon">
|
||||
<img src="{{server.mode.toLowerCase()}}.png" alt="{{server.mode}}" *ngIf="!server.hasIcon">
|
||||
<img [src]="url('http',['Server', server.name, 'icon'])" alt="{{server.mode}}" *ngIf="server.hasIcon">
|
||||
<img src="{{server.mode.toLowerCase()}}.png" alt="{{server.mode}}" *ngIf="!server.icon">
|
||||
<img [src]="url('http',['Server', server.name, 'icon'])" alt="{{server.mode}}" *ngIf="server.icon">
|
||||
</div>
|
||||
<div class="name">
|
||||
{{ server.motd }}
|
||||
<div class="address">
|
||||
10.255.0.1:{{ server.serverPort }}
|
||||
10.255.0.1:{{ server.port }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="map" *ngIf="server.name === 'survival24'">
|
||||
@ -32,3 +27,8 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="hint">
|
||||
<img src="info.svg" alt="(i)">
|
||||
Beim Starten eines Servers werden alle anderen gestoppt.
|
||||
</div>
|
||||
|
||||
@ -18,6 +18,7 @@
|
||||
}
|
||||
|
||||
.name {
|
||||
color: #c8dddd;
|
||||
flex-grow: 1;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
@ -33,7 +33,7 @@ export class ServerListComponent extends CrudListComponent<Server, ServerService
|
||||
}
|
||||
|
||||
servers() {
|
||||
return this.list.sort((a, b) => a.serverPort - b.serverPort);
|
||||
return this.list.sort((a, b) => a.port - b.port);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -2,23 +2,26 @@ body {
|
||||
font-family: sans-serif;
|
||||
margin: 0;
|
||||
font-size: 5vw;
|
||||
background-color: #2b3939;
|
||||
}
|
||||
|
||||
.heading {
|
||||
padding: 0.25em;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.hint {
|
||||
background-color: lightyellow;
|
||||
border: 0.1em solid yellow;
|
||||
margin: 0.25em;
|
||||
margin: 0.5em;
|
||||
padding: 0.25em;
|
||||
font-size: 60%;
|
||||
border-radius: 0.25em;
|
||||
|
||||
img {
|
||||
height: 1.7em;
|
||||
vertical-align: middle;
|
||||
height: 1.4em;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
}
|
||||
|
||||
17
src/main/java/de/ph87/mc/server/NoMinecraftServer.java
Normal file
17
src/main/java/de/ph87/mc/server/NoMinecraftServer.java
Normal file
@ -0,0 +1,17 @@
|
||||
package de.ph87.mc.server;
|
||||
|
||||
import lombok.NonNull;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class NoMinecraftServer extends Exception {
|
||||
|
||||
public NoMinecraftServer(final @NonNull File directory, final String string) {
|
||||
super("Not a minecraft server: directory=%s, reason=%s".formatted(directory, string));
|
||||
}
|
||||
|
||||
public NoMinecraftServer(final @NonNull File directory, final Exception e) {
|
||||
super("Not a minecraft server: directory=%s".formatted(directory), e);
|
||||
}
|
||||
|
||||
}
|
||||
45
src/main/java/de/ph87/mc/server/Properties.java
Normal file
45
src/main/java/de/ph87/mc/server/Properties.java
Normal file
@ -0,0 +1,45 @@
|
||||
package de.ph87.mc.server;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.NonNull;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.util.Locale;
|
||||
|
||||
@Data
|
||||
public class Properties {
|
||||
|
||||
public final String name;
|
||||
|
||||
public final String motd;
|
||||
|
||||
public final Mode mode;
|
||||
|
||||
public final int port;
|
||||
|
||||
public final int rconPort;
|
||||
|
||||
public final String rconPassword;
|
||||
|
||||
public Properties(final @NonNull File directory) throws NoMinecraftServer {
|
||||
final File file = new File(directory, "server.properties");
|
||||
if (!file.exists()) {
|
||||
throw new NoMinecraftServer(directory, "server.properties not found");
|
||||
}
|
||||
final java.util.Properties properties = new java.util.Properties();
|
||||
try (final FileReader reader = new FileReader(file)) {
|
||||
properties.load(reader);
|
||||
name = properties.getProperty("level-name");
|
||||
motd = properties.getProperty("motd");
|
||||
mode = Mode.valueOf(properties.getProperty("gamemode").toUpperCase(Locale.ROOT));
|
||||
port = Integer.parseInt(properties.getProperty("server-port"));
|
||||
rconPort = Integer.parseInt(properties.getProperty("rcon.port"));
|
||||
rconPassword = properties.getProperty("rcon.password");
|
||||
} catch (IOException | NumberFormatException e) {
|
||||
throw new NoMinecraftServer(directory, e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,79 +1,55 @@
|
||||
package de.ph87.mc.server;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import de.ph87.mc.websocket.IWebsocketMessage;
|
||||
import jakarta.annotation.Nullable;
|
||||
import lombok.Data;
|
||||
import lombok.NonNull;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
@Data
|
||||
public class Server implements IWebsocketMessage {
|
||||
@Slf4j
|
||||
public class Server {
|
||||
|
||||
@NonNull
|
||||
@JsonIgnore
|
||||
public final File directory;
|
||||
|
||||
@NonNull
|
||||
@JsonIgnore
|
||||
public final File propertyFile;
|
||||
|
||||
@NonNull
|
||||
@JsonIgnore
|
||||
public final File pidFile;
|
||||
|
||||
@NonNull
|
||||
public final String name;
|
||||
|
||||
@NonNull
|
||||
public final String motd;
|
||||
|
||||
@NonNull
|
||||
public final Mode mode;
|
||||
|
||||
public final int serverPort;
|
||||
|
||||
@JsonIgnore
|
||||
public final int rconPort;
|
||||
|
||||
@NonNull
|
||||
@JsonIgnore
|
||||
public final String rconPassword;
|
||||
|
||||
@JsonIgnore
|
||||
public final int queryPort;
|
||||
|
||||
@JsonIgnore
|
||||
public final File iconFile;
|
||||
|
||||
public final boolean hasIcon;
|
||||
@NonNull
|
||||
public final Properties properties;
|
||||
|
||||
public final Object lock = new Object();
|
||||
|
||||
@Nullable
|
||||
private Long pid;
|
||||
public Process process = null;
|
||||
|
||||
public Server(@NonNull final File directory, @NonNull final String name, @NonNull final String motd, @NonNull final Mode mode, final int serverPort, final int rconPort, final String rconPassword, final int queryPort) {
|
||||
public boolean shutdown = false;
|
||||
|
||||
public Server(@NonNull final File directory) throws NoMinecraftServer {
|
||||
this.directory = directory;
|
||||
this.propertyFile = new File(directory, "server.properties");
|
||||
this.pidFile = new File(directory, "pid");
|
||||
this.iconFile = new File(directory, "McManagerIcon.png");
|
||||
this.name = name;
|
||||
this.motd = motd;
|
||||
this.mode = mode;
|
||||
this.serverPort = serverPort;
|
||||
this.rconPort = rconPort;
|
||||
this.rconPassword = rconPassword;
|
||||
this.queryPort = queryPort;
|
||||
this.hasIcon = iconFile.isFile();
|
||||
this.properties = new Properties(directory);
|
||||
}
|
||||
|
||||
public boolean isRunning() {
|
||||
synchronized (lock) {
|
||||
return process != null && process.isAlive();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Server(%s, \"%s\", %s)".formatted(mode, motd, isRunning() ? "RUNNING" : "stopped");
|
||||
return "Server(%s, \"%s\", %s)".formatted(properties.mode, properties.motd, isRunning() ? "RUNNING" : "stopped");
|
||||
}
|
||||
|
||||
public boolean isRunning() {
|
||||
return pid != null;
|
||||
public boolean eq(@NonNull final Server other) {
|
||||
return properties.name.equals(other.properties.name);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -9,6 +9,7 @@ import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
@ -21,27 +22,34 @@ public class ServerController {
|
||||
|
||||
private final ServerService serverService;
|
||||
|
||||
private final ServerRepository serverRepository;
|
||||
|
||||
@GetMapping("findAll")
|
||||
public List<Server> findAll() {
|
||||
return serverRepository.findAll();
|
||||
@NonNull
|
||||
@GetMapping("stopAll")
|
||||
public List<ServerDto> stopAll() {
|
||||
return serverService.stopAll();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@GetMapping("findAll")
|
||||
public List<ServerDto> findAll() {
|
||||
return serverService.findAll();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@GetMapping("{name}/start")
|
||||
public Server start(@NonNull @PathVariable final String name) {
|
||||
public ServerDto start(@NonNull @PathVariable final String name) {
|
||||
return serverService.start(name);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@GetMapping("{name}/stop")
|
||||
public Server stop(@NonNull @PathVariable final String name) {
|
||||
public ServerDto stop(@NonNull @PathVariable final String name) {
|
||||
return serverService.stop(name);
|
||||
}
|
||||
|
||||
@GetMapping("{name}/icon")
|
||||
public void icon(@NonNull @PathVariable final String name, @NonNull final HttpServletResponse response) throws IOException {
|
||||
final Server server = serverRepository.getByName(name);
|
||||
try (final FileInputStream inputStream = new FileInputStream(server.iconFile)) {
|
||||
final File iconFile = serverService.getIconFileByName(name);
|
||||
try (final FileInputStream inputStream = new FileInputStream(iconFile)) {
|
||||
response.getOutputStream().write(inputStream.readAllBytes());
|
||||
}
|
||||
response.getOutputStream().flush();
|
||||
|
||||
31
src/main/java/de/ph87/mc/server/ServerDto.java
Normal file
31
src/main/java/de/ph87/mc/server/ServerDto.java
Normal file
@ -0,0 +1,31 @@
|
||||
package de.ph87.mc.server;
|
||||
|
||||
import de.ph87.mc.websocket.IWebsocketMessage;
|
||||
import lombok.Data;
|
||||
import lombok.NonNull;
|
||||
|
||||
@Data
|
||||
public class ServerDto implements IWebsocketMessage {
|
||||
|
||||
public final String name;
|
||||
|
||||
public final String motd;
|
||||
|
||||
public final Mode mode;
|
||||
|
||||
public final int port;
|
||||
|
||||
public final boolean running;
|
||||
|
||||
public boolean icon;
|
||||
|
||||
public ServerDto(final @NonNull Server server) {
|
||||
this.name = server.properties.name;
|
||||
this.motd = server.properties.motd;
|
||||
this.mode = server.properties.mode;
|
||||
this.port = server.properties.port;
|
||||
this.running = server.isRunning();
|
||||
this.icon = server.iconFile.isFile();
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,128 +0,0 @@
|
||||
package de.ph87.mc.server;
|
||||
|
||||
import jakarta.annotation.Nullable;
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class ServerProcessHelper {
|
||||
|
||||
public static final String[] CMDLINE = {"java", "-jar", "server.jar"};
|
||||
|
||||
public static final String CMDLINE_STR = String.join(" ", CMDLINE);
|
||||
|
||||
private final ApplicationEventPublisher applicationEventPublisher;
|
||||
|
||||
public void updatePid(@NonNull final Server server) {
|
||||
server.setPid(_readAndVerifyPid(server));
|
||||
if (server.getPid() == null) {
|
||||
deletePidFile(server);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Long _readAndVerifyPid(@NonNull final Server server) {
|
||||
if (!server.pidFile.exists()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final long pid;
|
||||
try (final FileInputStream stream = new FileInputStream(server.pidFile)) {
|
||||
pid = Long.parseLong(new String(stream.readAllBytes(), StandardCharsets.UTF_8));
|
||||
} catch (IOException | NumberFormatException e) {
|
||||
log.error("Failed to read pid-file: file={}, error={}", server.pidFile, e.getMessage());
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!validateProcFile(server, pid)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return pid;
|
||||
}
|
||||
|
||||
private static boolean validateProcFile(@NonNull final Server server, final long pid) {
|
||||
final File procFile = new File("/proc/%d/cmdline".formatted(pid));
|
||||
if (!procFile.exists()) {
|
||||
log.warn("Server not running: {}", server.name);
|
||||
return false;
|
||||
}
|
||||
|
||||
try (final FileInputStream stream = new FileInputStream(procFile)) {
|
||||
final String cmdline = new String(stream.readAllBytes(), StandardCharsets.UTF_8).replace((char) 0, ' ').trim();
|
||||
if (!CMDLINE_STR.equals(cmdline)) {
|
||||
log.error("cmdline of running Server does not match: pid={}, running={}, expected={}", pid, cmdline, CMDLINE_STR);
|
||||
return false;
|
||||
}
|
||||
} catch (IOException | NumberFormatException e) {
|
||||
log.error("Failed to read proc-file: file={}, error={}", procFile, e.getMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void deletePidFile(@NonNull final Server server) {
|
||||
server.setPid(null);
|
||||
if (server.pidFile.delete()) {
|
||||
log.info("PID-file removed: {}", server.pidFile);
|
||||
applicationEventPublisher.publishEvent(server);
|
||||
}
|
||||
}
|
||||
|
||||
public void startProcess(@NonNull final Server server) {
|
||||
if (server.isRunning()) {
|
||||
return;
|
||||
}
|
||||
log.info("Starting Server: {}", server.name);
|
||||
final ProcessBuilder builder = new ProcessBuilder(CMDLINE);
|
||||
builder.directory(server.directory);
|
||||
try {
|
||||
final Process process = builder.start();
|
||||
server.setPid(process.pid());
|
||||
writePid(server, process.pid());
|
||||
} catch (IOException e) {
|
||||
log.error("Failed to start server: error={}, name={}", e.getMessage(), server.name);
|
||||
}
|
||||
}
|
||||
|
||||
public void stopProcess(@NonNull final Server server) {
|
||||
if (!server.isRunning()) {
|
||||
return;
|
||||
}
|
||||
new Thread(() -> {
|
||||
try {
|
||||
log.info("Stopping Server: {}", server.name);
|
||||
new ProcessBuilder("kill", "-15", server.getPid() + "").start();
|
||||
while (server.getPid() != null && validateProcFile(server, server.getPid())) {
|
||||
//noinspection BusyWait
|
||||
Thread.sleep(1000);
|
||||
}
|
||||
deletePidFile(server);
|
||||
} catch (IOException | InterruptedException e) {
|
||||
log.error("Failed to stop server: error={}, name={}", e.getMessage(), server.name);
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
private void writePid(@NonNull final Server server, final long pid) throws IOException {
|
||||
final File file = server.pidFile;
|
||||
try (final FileOutputStream stream = new FileOutputStream(file)) {
|
||||
stream.write("%d".formatted(pid).getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
log.info("PID-file written: file={} = {}", server.pidFile, pid);
|
||||
applicationEventPublisher.publishEvent(server);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,70 +0,0 @@
|
||||
package de.ph87.mc.server;
|
||||
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Properties;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class ServerRepository {
|
||||
|
||||
private final ApplicationEventPublisher applicationEventPublisher;
|
||||
|
||||
private final ServerProcessHelper serverProcessHelper;
|
||||
|
||||
private final ServerConfig serverConfig;
|
||||
|
||||
@NonNull
|
||||
public List<Server> findAll() {
|
||||
final File ROOT = new File(serverConfig.getPath());
|
||||
return Arrays.stream(Objects.requireNonNull(ROOT.listFiles())).map(this::_tryLoadingFromDir).filter(Optional::isPresent).map(Optional::get).toList();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private Optional<Server> _tryLoadingFromDir(@NonNull final File dir) {
|
||||
final File file = new File(dir, "server.properties");
|
||||
if (!file.isFile()) {
|
||||
log.warn("Server directory without server.properties file: {}", dir);
|
||||
return Optional.empty();
|
||||
}
|
||||
final Properties properties = new Properties();
|
||||
try (final FileReader reader = new FileReader(file)) {
|
||||
properties.load(reader);
|
||||
final String name = properties.getProperty("level-name");
|
||||
final String motd = properties.getProperty("motd");
|
||||
final Mode gamemode = Mode.valueOf(properties.getProperty("gamemode").toUpperCase(Locale.ROOT));
|
||||
final int serverPort = Integer.parseInt(properties.getProperty("server-port"));
|
||||
final int rconPort = Integer.parseInt(properties.getProperty("rcon.port"));
|
||||
final String rconPassword = properties.getProperty("rcon.password");
|
||||
final int queryPort = Integer.parseInt(properties.getProperty("query.port"));
|
||||
final Server server = new Server(dir, name, motd, gamemode, serverPort, rconPort, rconPassword, queryPort);
|
||||
serverProcessHelper.updatePid(server);
|
||||
applicationEventPublisher.publishEvent(server);
|
||||
return Optional.of(server);
|
||||
} catch (IOException e) {
|
||||
log.error("Failed to read server.properties: file={}, error={}", file, e.getMessage());
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Server getByName(@NonNull final String name) {
|
||||
return findAll().stream().filter(server -> server.name.equals(name)).findFirst().orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,38 +1,159 @@
|
||||
package de.ph87.mc.server;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class ServerService {
|
||||
|
||||
private final ServerRepository repository;
|
||||
private final ApplicationEventPublisher applicationEventPublisher;
|
||||
|
||||
private final ServerProcessHelper serverProcessHelper;
|
||||
private final ServerConfig serverConfig;
|
||||
|
||||
private final Object serversLock = new Object();
|
||||
|
||||
private List<Server> servers = new ArrayList<>();
|
||||
|
||||
@PostConstruct
|
||||
public void startup() {
|
||||
final File ROOT = new File(serverConfig.getPath());
|
||||
synchronized (serversLock) {
|
||||
servers = Arrays.stream(Objects.requireNonNull(ROOT.listFiles())).map(this::_tryLoadingFromDir).filter(Optional::isPresent).map(Optional::get).toList();
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Server start(@NonNull final String name) {
|
||||
final Server server = repository.getByName(name);
|
||||
private Optional<Server> _tryLoadingFromDir(@NonNull final File directory) {
|
||||
try {
|
||||
final Server server = new Server(directory);
|
||||
servers.stream().filter(server::eq).findFirst().ifPresent(old -> server.process = old.process);
|
||||
return Optional.of(server);
|
||||
} catch (NoMinecraftServer e) {
|
||||
log.warn(e.getMessage());
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public ServerDto start(@NonNull final String name) {
|
||||
return set(name, this::start);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public ServerDto stop(@NonNull final String name) {
|
||||
return set(name, this::stop);
|
||||
}
|
||||
|
||||
private void start(@NonNull final Server server) {
|
||||
synchronized (server.lock) {
|
||||
if (server.isRunning()) {
|
||||
return server;
|
||||
log.warn("Server is already running: name={}", server.properties.name);
|
||||
return;
|
||||
}
|
||||
stopAll();
|
||||
log.info("Starting server: name={}", server.properties.name);
|
||||
final ProcessBuilder builder = new ProcessBuilder("java", "-jar", "server.jar");
|
||||
builder.directory(server.directory);
|
||||
try {
|
||||
server.process = builder.start();
|
||||
log.info("Server started: name={}", server.properties.name);
|
||||
} catch (IOException e) {
|
||||
log.error("Failed to start Server: name={}, error={}", server.properties.name, e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void stop(@NonNull final Server server) {
|
||||
synchronized (server.lock) {
|
||||
if (!server.isRunning()) {
|
||||
log.warn("Server is not running: name={}", server.properties.name);
|
||||
return;
|
||||
}
|
||||
if (server.shutdown) {
|
||||
log.warn("Server is already shutting down: name={}", server.properties.name);
|
||||
return;
|
||||
}
|
||||
server.shutdown = true;
|
||||
log.info("Stopping Server: name={}", server.properties.name);
|
||||
new Thread(() -> _stop_async(server), "STOP-" + server.properties.name).start();
|
||||
}
|
||||
}
|
||||
|
||||
private void _stop_async(@NonNull final Server server) {
|
||||
log.debug("Thread spawned: name={}", server.properties.name);
|
||||
synchronized (server.lock) {
|
||||
assert server.process != null;
|
||||
log.info("Stopping server: name={}", server.properties.name);
|
||||
server.process.destroy();
|
||||
try {
|
||||
server.process.waitFor();
|
||||
log.info("Server stopped: name={}", server.properties.name);
|
||||
} catch (InterruptedException e) {
|
||||
log.error("Interrupted while waiting for server to stop: name={}", server.properties.name);
|
||||
} finally {
|
||||
server.process = null;
|
||||
server.shutdown = false;
|
||||
publish(server);
|
||||
log.debug("Thread terminated: name={}", server.properties.name);
|
||||
}
|
||||
}
|
||||
repository.findAll().forEach(serverProcessHelper::stopProcess);
|
||||
serverProcessHelper.startProcess(server);
|
||||
return server;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Server stop(@NonNull final String name) {
|
||||
final Server server = repository.getByName(name);
|
||||
if (!server.isRunning()) {
|
||||
return server;
|
||||
private ServerDto set(final @NonNull String name, @NonNull final Consumer<Server> modifier) {
|
||||
final Server server = getByName(name);
|
||||
modifier.accept(server);
|
||||
return publish(server);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private ServerDto publish(@NonNull final Server server) {
|
||||
final ServerDto dto = new ServerDto(server);
|
||||
applicationEventPublisher.publishEvent(dto);
|
||||
return dto;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private Server getByName(@NonNull final String name) {
|
||||
synchronized (serversLock) {
|
||||
return servers.stream().filter(server -> server.properties.name.equals(name)).findFirst().orElseThrow();
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public List<ServerDto> findAll() {
|
||||
synchronized (serversLock) {
|
||||
return servers.stream().map(ServerDto::new).toList();
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public File getIconFileByName(@NonNull final String name) {
|
||||
synchronized (serversLock) {
|
||||
return getByName(name).iconFile;
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public List<ServerDto> stopAll() {
|
||||
synchronized (serversLock) {
|
||||
return servers.stream().peek(this::stop).map(ServerDto::new).toList();
|
||||
}
|
||||
serverProcessHelper.stopProcess(server);
|
||||
return server;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -5,7 +5,7 @@ public interface IWebsocketMessage {
|
||||
default String getWebsocketDestination() {
|
||||
String name = getClass().getSimpleName();
|
||||
if (name.endsWith("Dto")) {
|
||||
return name.substring(0, name.length() - 4);
|
||||
return name.substring(0, name.length() - 3);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
@ -16,7 +16,9 @@ public class WebSocketService {
|
||||
|
||||
@EventListener
|
||||
public void send(@NonNull final IWebsocketMessage message) {
|
||||
simpMessageSendingOperations.convertAndSend(message.getWebsocketDestination(), message);
|
||||
final String destination = message.getWebsocketDestination();
|
||||
log.debug("Websocket: destination={}", destination);
|
||||
simpMessageSendingOperations.convertAndSend(destination, message);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user