Initial commit: Game Night landing + Minecraft cheat sheet
Some checks failed
Build games-landing / build (push) Failing after 13s

Includes:
- Game Night landing page with health checks
- FTB StoneBlock 4 server info & command reference
- Dockerfile for nginx deployment
- Forgejo CI/CD workflow
This commit is contained in:
joshii 2026-03-17 23:31:54 +01:00
parent f812467e39
commit 5aeaf567a7
15 changed files with 998 additions and 0 deletions

View file

@ -0,0 +1,22 @@
name: Build games-landing
on:
push:
branches: [main]
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Clone repo
run: git clone --depth 1 https://git.joshuahirsig.xyz/docker/games-landing.git .
- name: Install Docker CLI
run: apt-get update -qq && apt-get install -y -qq ca-certificates curl gnupg >/dev/null && install -m 0755 -d /etc/apt/keyrings && curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc && echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian bookworm stable" > /etc/apt/sources.list.d/docker.list && apt-get update -qq && apt-get install -y -qq docker-ce-cli >/dev/null
- name: Build and push
run: |
echo "${{ secrets.REGISTRY_TOKEN }}" | docker login git.joshuahirsig.xyz -u joshii --password-stdin
docker build -t git.joshuahirsig.xyz/docker/games-landing:latest .
docker push git.joshuahirsig.xyz/docker/games-landing:latest

4
Dockerfile Normal file
View file

@ -0,0 +1,4 @@
FROM nginx:alpine
COPY . /usr/share/nginx/html/
RUN rm -f /usr/share/nginx/html/Dockerfile /usr/share/nginx/html/README.md
EXPOSE 80

7
codenames-icon.svg Normal file
View file

@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" fill="none">
<rect x="15" y="25" width="70" height="50" rx="6" stroke="#a78bfa" stroke-width="4"/>
<circle cx="50" cy="42" r="10" stroke="#a78bfa" stroke-width="3.5"/>
<path d="M35 62c0-8.3 6.7-15 15-15s15 6.7 15 15" stroke="#a78bfa" stroke-width="3.5" stroke-linecap="round"/>
<line x1="60" y1="20" x2="68" y2="12" stroke="#c4b5fd" stroke-width="3" stroke-linecap="round"/>
<circle cx="70" cy="10" r="4" fill="#c4b5fd"/>
</svg>

After

Width:  |  Height:  |  Size: 501 B

11
favicon.svg Normal file
View file

@ -0,0 +1,11 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" fill="none">
<rect x="10" y="10" width="80" height="80" rx="16" fill="#18181b"/>
<path d="M35 40h-8a4 4 0 00-4 4v4a4 4 0 004 4h8" stroke="#818cf8" stroke-width="3.5" stroke-linecap="round"/>
<path d="M65 40h8a4 4 0 014 4v4a4 4 0 01-4 4h-8" stroke="#818cf8" stroke-width="3.5" stroke-linecap="round"/>
<rect x="30" y="30" width="40" height="40" rx="8" stroke="#a78bfa" stroke-width="3.5"/>
<line x1="42" y1="45" x2="42" y2="55" stroke="#c4b5fd" stroke-width="3" stroke-linecap="round"/>
<line x1="50" y1="45" x2="50" y2="55" stroke="#c4b5fd" stroke-width="3" stroke-linecap="round"/>
<line x1="58" y1="45" x2="58" y2="55" stroke="#c4b5fd" stroke-width="3" stroke-linecap="round"/>
<circle cx="50" cy="22" r="3" fill="#818cf8"/>
<circle cx="50" cy="78" r="3" fill="#818cf8"/>
</svg>

After

Width:  |  Height:  |  Size: 861 B

151
games.json Normal file
View file

@ -0,0 +1,151 @@
{
"settings": {
"healthChecks": true,
"healthTimeoutMs": 6000
},
"games": [
{
"id": "minecraft",
"name": "FTB StoneBlock 4",
"url": "https://minecraft.joshuahirsig.xyz",
"icon": "⛏️",
"iconClass": "icon-emerald",
"description": "Minecraft Modpack — grabe dich durch Stein, baue Maschinen & meistere die Technik!",
"players": "1-20 Spieler",
"tag": "Minecraft",
"tagClass": "tag-board",
"healthEnabled": true,
"healthUrl": "https://minecraft.joshuahirsig.xyz/api/status"
},
{
"id": "codenames",
"name": "Codenames",
"url": "https://codenames.joshuahirsig.xyz",
"icon": "🕵️",
"iconClass": "icon-purple",
"description": "Finde die Agenten deines Teams anhand von Hinweisen!",
"players": "4+ Spieler",
"tag": "Teamspiel",
"tagClass": "tag-team",
"iconSvg": "/codenames-icon.svg"
},
{
"id": "scribble",
"name": "Scribble",
"url": "https://scribble.joshuahirsig.xyz",
"icon": "🎨",
"iconClass": "icon-pink",
"description": "Zeichne und errate Begriffe - wie Skribbl.io!",
"players": "3+ Spieler",
"tag": "Party",
"tagClass": "tag-party",
"iconSvg": "/scribble-icon.svg"
},
{
"id": "cards",
"name": "Massive Decks",
"url": "https://cards.joshuahirsig.xyz",
"icon": "🃏",
"iconClass": "icon-blue",
"description": "Cards Against Humanity - die besten schlechten Antworten gewinnen.",
"players": "3+ Spieler",
"tag": "Party",
"tagClass": "tag-party",
"iconSvg": "/massivedecks-icon.svg"
},
{
"id": "trivia",
"name": "Trivia",
"url": "https://trivia.joshuahirsig.xyz",
"icon": "🧠",
"iconClass": "icon-amber",
"description": "Quiz-Spiel wie Kahoot - erstelle eigene Fragen!",
"players": "2+ Spieler",
"tag": "Quiz",
"tagClass": "tag-quiz",
"iconSvg": "/trivia-icon.svg"
},
{
"id": "tabletop",
"name": "Virtual Tabletop",
"url": "https://tabletop.joshuahirsig.xyz",
"icon": "🎲",
"iconClass": "icon-emerald",
"description": "300+ Brettspiele, Kartenspiele und mehr.",
"players": "2+ Spieler",
"tag": "Brettspiel",
"tagClass": "tag-board",
"iconSvg": "/tabletop-icon.svg"
},
{
"id": "tosios",
"name": "TOSIOS",
"url": "https://shooter.joshuahirsig.xyz",
"icon": "🔫",
"iconClass": "icon-red",
"description": "Browser IO-Shooter - schnelle Multiplayer-Matches!",
"players": "2+ Spieler",
"tag": "Action",
"tagClass": "tag-strategy",
"iconSvg": "/tosios-icon.svg"
},
{
"id": "geoquiz",
"name": "Le Grand GeoQuiz",
"url": "https://geoquiz.joshuahirsig.xyz",
"icon": "🌍",
"iconClass": "icon-emerald",
"description": "Strategisches Geografie-Spiel - 8 Lander, 8 Kategorien, niedrigste Punktzahl gewinnt!",
"players": "1 Spieler",
"tag": "Quiz",
"tagClass": "tag-quiz",
"iconSvg": "/geoquiz-icon.svg"
},
{
"id": "worldguessr",
"name": "WorldGuessr",
"url": "https://worldguessr.joshuahirsig.xyz",
"icon": "🌎",
"iconClass": "icon-emerald",
"description": "GeoGuessr-Klon - errate Orte auf der ganzen Welt anhand von Street View!",
"players": "1+ Spieler",
"tag": "Geo",
"tagClass": "tag-quiz",
"iconSvg": "/worldguessr-icon.png"
},
{
"id": "pokemon-military",
"name": "Pokémon or Military?",
"url": "https://pokemon.joshuahirsig.xyz",
"icon": "⚔️",
"iconClass": "icon-red",
"description": "Ist es ein Pokémon-TCG-Set oder eine Militäroperation? Finde es heraus!",
"players": "1 Spieler",
"tag": "Quiz",
"tagClass": "tag-quiz",
"iconSvg": "/pokemon-icon.svg"
},
{
"id": "bomberman",
"name": "Bomberman (UniCT)",
"url": "https://bomberman.joshuahirsig.xyz",
"icon": "💣",
"iconClass": "icon-amber",
"description": "Klassisches Bomberman — 2-4 Spieler, private Räume, Power-ups und Chat!",
"players": "2-4 Spieler",
"tag": "Action",
"tagClass": "tag-strategy"
},
{
"id": "bomber",
"name": "Bomber (Vasin)",
"url": "https://bomber.joshuahirsig.xyz",
"icon": "💥",
"iconClass": "icon-red",
"description": "Bomberman-Klon mit Phaser.js — bis zu 3 Spieler, 2 Maps, Skill-Upgrades.",
"players": "2-3 Spieler",
"tag": "Action",
"tagClass": "tag-strategy"
}
]
}

82
geoquiz-icon.svg Normal file
View file

@ -0,0 +1,82 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 512.001 512.001" xml:space="preserve">
<circle style="fill:#0792C7;" cx="247.935" cy="302.408" r="201.755"/>
<path style="fill:#B2E763;" d="M417.403,334.683l-48.421-56.491l16.14-48.421l-16.14-8.07l32.28-40.35
c0,0,52.324,31.93,48.421,137.191L417.403,334.683z"/>
<path style="fill:#2C4D77;" d="M134.949,302.403c0-96.168,67.306-176.564,157.367-196.794c-14.288-3.209-29.128-4.958-44.385-4.958
c-111.425,0-201.752,90.328-201.752,201.752s90.328,201.752,201.752,201.752c15.257,0,30.098-1.749,44.385-4.958
C202.255,478.969,134.949,398.572,134.949,302.403z"/>
<path style="fill:#00BA94;" d="M62.318,375.035l16.14-48.421l-32.28-40.35C46.177,286.263,43.587,356.16,62.318,375.035z"/>
<g>
<polyline style="fill:#B2E763;" points="231.791,165.212 199.51,197.492 175.299,181.352 151.089,197.492 126.879,245.913
134.949,278.193 175.299,302.403 167.23,342.754 143.019,375.035 159.159,447.665 143.019,471.876 159.159,479.945 223.72,439.595
215.65,391.175 272.141,294.333 231.791,253.982 288.281,213.632 256,165.212 "/>
<path style="fill:#B2E763;" d="M344.772,479.945l-64.561-16.14l-40.35,40.35C239.86,504.155,289.89,512.169,344.772,479.945z"/>
</g>
<path style="fill:#F4F4F4;" d="M360.912,392.7L360.912,392.7c0-16.859,12.327-31.984,29.11-33.585
c4.732-0.451,9.269,0.107,13.436,1.505c6.146-19.685,24.516-33.99,46.224-33.99c27.237,0,49.221,22.618,48.398,50.04
c-0.795,26.5-23.915,47.06-50.429,47.06H304.421h25.599C347.082,423.73,360.912,409.762,360.912,392.7z"/>
<g>
<path style="fill:#FFFFFF;" d="M449.684,326.614c-7.184,0-13.934,1.687-20.064,4.492c17.018,7.874,28.715,25.436,28.111,45.556
c-0.796,26.5-23.916,47.068-50.429,47.068h40.35c26.513,0,49.633-20.568,50.428-47.068
C498.904,349.239,476.921,326.614,449.684,326.614z"/>
<path style="fill:#FFFFFF;" d="M151.089,215.021L151.089,215.021c0-16.859-12.326-31.916-29.109-33.517
c-4.732-0.451-9.269,0.14-13.436,1.539c-6.146-19.685-24.516-33.972-46.225-33.972c-27.237,0-49.221,22.49-48.398,49.912
c0.795,26.5,23.915,46.933,50.429,46.933h143.232h-25.598C164.92,245.917,151.089,232.083,151.089,215.021z"/>
</g>
<path style="fill:#F4F4F4;" d="M54.271,198.984c-0.604-20.12,11.093-37.545,28.11-45.419c-6.129-2.806-12.881-4.493-20.063-4.493
c-27.237,0-49.221,22.49-48.398,49.912c0.795,26.5,23.915,46.933,50.429,46.933h40.35C78.186,245.917,55.066,225.485,54.271,198.984
z"/>
<path style="fill:#F60A53;" d="M333.527,22.027c-18.91-18.91-49.567-18.91-68.477,0l-17.119,17.119l-17.119-17.119
c-18.91-18.91-49.567-18.91-68.477,0c-18.91,18.909-18.91,49.567,0,68.476l85.596,85.596l64.561-64.561l21.035-21.035
C352.437,71.594,352.437,40.936,333.527,22.027z"/>
<path style="fill:#FE702D;" d="M247.931,39.146l-17.119-17.119c-18.91-18.91-49.567-18.91-68.477,0
c-18.91,18.909-18.91,49.567,0,68.476l85.596,85.596"/>
<g>
<rect x="296.509" y="254.284" style="fill:#FFFFFF;" width="14.643" height="15.689"/>
<rect x="272.452" y="327.501" style="fill:#FFFFFF;" width="15.689" height="15.689"/>
<rect x="128.11" y="310.765" style="fill:#FFFFFF;" width="14.643" height="15.689"/>
<rect x="103.007" y="335.869" style="fill:#FFFFFF;" width="15.689" height="15.689"/>
<rect x="321.613" y="440.465" style="fill:#FFFFFF;" width="14.643" height="15.689"/>
</g>
<path d="M489.889,335.908c-9.002-9.275-20.544-15.025-33.053-16.62c0.447-5.609,0.692-11.26,0.692-16.885
c0-85.77-51.979-162.272-130.603-194.205l12.149-12.149c10.628-10.627,16.481-24.756,16.481-39.785
c0-15.028-5.853-29.158-16.48-39.785C328.446,5.852,314.316,0,299.288,0s-29.158,5.852-39.785,16.48l-11.571,11.571L236.358,16.48
c-21.937-21.937-57.632-21.938-79.57,0c-10.628,10.627-16.481,24.757-16.481,39.785c0,15.029,5.853,29.158,16.48,39.785
l12.158,12.158c-26.054,10.622-49.899,26.526-69.817,46.593c-9.953-8.59-22.879-13.608-36.811-13.608
c-15.426,0-29.807,6.066-40.495,17.079C11.211,169.209,5.62,183.652,6.078,198.942c0.703,23.444,16.886,43.464,38.87,51.047
c-4.389,17.05-6.616,34.65-6.616,52.415c0,115.572,94.025,209.597,209.598,209.597c33.526,0,65.561-7.68,95.215-22.827
c26.814-13.696,50.721-33.373,69.43-57.075h35.077c31.222,0,57.362-24.634,58.269-54.914
C506.387,361.701,500.693,347.043,489.889,335.908z M441.091,319.472c-7.252,1.103-14.092,3.582-20.208,7.218l-43.054-50.229
l16.887-50.662l-13.545-6.773l24.073-30.092c23.254,32.265,36.595,71.677,36.595,113.47
C441.837,308.092,441.583,313.809,441.091,319.472z M155.996,56.264c0-10.838,4.22-21.027,11.885-28.691
c7.911-7.911,18.3-11.865,28.691-11.865c10.392,0,20.781,3.955,28.693,11.865l22.666,22.666l22.667-22.666
c7.663-7.664,17.852-11.884,28.69-11.884c10.838,0,21.028,4.22,28.692,11.884h0.001c7.664,7.664,11.884,17.852,11.884,28.691
s-4.22,21.028-11.885,28.692l-80.048,80.049l-80.049-80.049C160.217,77.292,155.996,67.103,155.996,56.264z M215.122,435.718
L155.631,472.9c-0.947-0.515-1.881-1.046-2.817-1.575l14.73-22.094l-16.085-72.377l23.114-30.817l9.504-47.517l-42.304-25.383
l-4.975-19.898h70.802v-15.689h-25.616c-12.708,0-23.049-10.223-23.049-22.789c0-4.121-0.61-8.124-1.731-11.916l18.097-12.065
l25.211,16.807l33.907-33.907l13.514,13.514l7.979-7.979l21.632,32.448l-57.845,41.318l42.622,42.622l-54.872,94.065
L215.122,435.718z M181.076,120.34l42.246,42.246l-24.812,24.812l-23.21-15.474l-25.327,16.885
c-6.587-8.423-16.271-14.198-27.247-15.244c-3.105-0.296-6.222-0.221-9.325,0.223c-1.145-2.489-2.461-4.868-3.928-7.126
C129.627,146.116,154.201,130.22,181.076,120.34z M21.762,198.471c-0.331-11.012,3.689-21.407,11.32-29.271
c7.708-7.942,18.09-12.316,29.236-12.316c17.86,0,33.428,11.426,38.737,28.432l2.388,7.649l7.597-2.55
c3.392-1.139,6.821-1.554,10.195-1.232c12.341,1.177,22.009,12.411,22.009,25.577c0,8.522,2.803,16.406,7.542,22.789H64.348
C41.513,237.549,22.407,220.019,21.762,198.471z M54.138,308.771l15.528,19.411l-8.638,25.913
C57.014,339.591,54.646,324.413,54.138,308.771z M69.07,377.284l0.69,0.23l17.49-52.47l-32.41-40.511
c0.973-10.637,2.818-21.152,5.514-31.445c1.322,0.089,2.652,0.15,3.995,0.15h56.275l7.503,30.009l38.396,23.037l-6.638,33.185
l-25.307,33.744l16.197,72.885l-11.31,16.964C108.417,442.035,83.763,412.249,69.07,377.284z M259.115,495.995l23.503-23.502
l38.368,9.592C301.276,490.135,280.56,494.797,259.115,495.995z M343.007,471.418l-65.202-16.301l-40.875,40.875
c-22.798-1.281-44.557-6.511-64.599-15.028l59.988-37.492l-8.465-50.792l58.11-99.617l-38.078-38.079l55.136-39.384l-31.799-47.698
l47.566-47.567c32.313,11.853,60.066,31.716,81.316,56.891l-0.969-0.775l-38.342,47.928l18.734,9.368l-15.393,46.179l48.516,56.601
c-4.09,4.366-7.511,9.421-10.081,15.034c-3.016-0.384-6.121-0.426-9.292-0.126c-20.305,1.937-36.21,20.177-36.21,41.526
c0,12.929-10.339,23.448-23.048,23.448h-25.146v15.689h87.186C377.954,447.736,361.296,461.092,343.007,471.418z M490.239,376.713
c-0.656,21.888-19.76,39.695-42.586,39.695h-86.637c4.86-6.539,7.74-14.66,7.74-23.448c0-13.35,9.668-24.73,22.01-25.906
c3.624-0.344,7.055,0.014,10.195,1.068l7.597,2.55l2.388-7.649c5.325-17.057,20.892-28.515,38.736-28.515
c10.952,0,21.233,4.378,28.947,12.327C486.453,354.898,490.576,365.508,490.239,376.713z"/>
<g>
<rect x="352.991" y="310.765" style="fill:#FFFFFF;" width="15.689" height="15.689"/>
<rect x="336.256" y="157.009" style="fill:#FFFFFF;" width="16.735" height="15.689"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.3 KiB

502
index.html Normal file
View file

@ -0,0 +1,502 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Game Night</title>
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap');
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
background: #07070f;
color: #d0d0d0;
min-height: 100vh;
overflow-x: hidden;
}
.bg {
position: fixed; inset: 0; z-index: 0; pointer-events: none;
background:
radial-gradient(ellipse 80% 60% at 15% 45%, rgba(124, 58, 237, 0.12) 0%, transparent 60%),
radial-gradient(ellipse 60% 50% at 85% 25%, rgba(59, 130, 246, 0.08) 0%, transparent 55%),
radial-gradient(ellipse 70% 50% at 50% 90%, rgba(168, 85, 247, 0.06) 0%, transparent 50%);
}
.bg::after {
content: ''; position: absolute; inset: 0;
background-image: radial-gradient(rgba(255,255,255,0.03) 1px, transparent 1px);
background-size: 32px 32px;
}
.container {
position: relative; z-index: 1;
max-width: 960px; margin: 0 auto;
padding: 3.5rem 1.5rem 4rem;
}
header { text-align: center; margin-bottom: 2.5rem; }
.logo { font-size: 3.2rem; margin-bottom: 0.3rem; filter: drop-shadow(0 0 24px rgba(139,92,246,0.3)); }
h1 {
font-size: 2.4rem; font-weight: 800; letter-spacing: -0.03em;
background: linear-gradient(135deg, #c4b5fd 0%, #818cf8 40%, #60a5fa 100%);
-webkit-background-clip: text; -webkit-text-fill-color: transparent;
background-clip: text;
}
.subtitle { color: #555; font-size: 0.9rem; margin-top: 0.4rem; }
.status-bar {
display: inline-flex; align-items: center; gap: 0.5rem;
margin-top: 1.2rem; padding: 0.45rem 1.2rem;
background: rgba(255,255,255,0.04); border: 1px solid rgba(255,255,255,0.06);
border-radius: 99px; font-size: 0.78rem; color: #777;
backdrop-filter: blur(8px);
}
.status-bar .dot {
width: 7px; height: 7px; border-radius: 50%; background: #555;
transition: all 0.4s;
}
.status-bar .dot.all-good { background: #22c55e; box-shadow: 0 0 8px rgba(34,197,94,0.5); }
.status-bar .dot.some-down { background: #f59e0b; box-shadow: 0 0 8px rgba(245,158,11,0.4); }
.games {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 1rem;
}
.game {
display: flex; flex-direction: column;
background: rgba(255,255,255,0.025);
border: 1px solid rgba(255,255,255,0.05);
border-radius: 16px; padding: 1.4rem 1.5rem;
text-decoration: none; color: inherit;
transition: all 0.35s cubic-bezier(0.4, 0, 0.2, 1);
position: relative; overflow: hidden;
opacity: 0; transform: translateY(16px);
animation: fadeUp 0.5s forwards;
}
.game { animation-delay: calc(0.05s * var(--i)); }
@keyframes fadeUp {
to { opacity: 1; transform: translateY(0); }
}
.game::before {
content: ''; position: absolute; inset: 0;
background: radial-gradient(circle at var(--mx, 50%) var(--my, 50%), rgba(139,92,246,0.08), transparent 60%);
opacity: 0; transition: opacity 0.4s;
}
.game:hover::before { opacity: 1; }
.game:hover {
transform: translateY(-6px);
border-color: rgba(139,92,246,0.25);
box-shadow: 0 24px 48px rgba(0,0,0,0.35), 0 0 0 1px rgba(139,92,246,0.1);
}
.game-top { display: flex; align-items: flex-start; gap: 0.9rem; margin-bottom: 0.8rem; }
.game-icon {
width: 52px; height: 52px; border-radius: 14px;
display: flex; align-items: center; justify-content: center;
font-size: 1.6rem; flex-shrink: 0;
position: relative;
}
.game-icon::after {
content: ''; position: absolute; inset: 0; border-radius: 14px;
border: 1px solid rgba(255,255,255,0.06);
}
.icon-purple { background: rgba(139,92,246,0.12); }
.icon-blue { background: rgba(59,130,246,0.12); }
.icon-pink { background: rgba(236,72,153,0.12); }
.icon-red { background: rgba(239,68,68,0.12); }
.icon-amber { background: rgba(245,158,11,0.12); }
.icon-emerald { background: rgba(16,185,129,0.12); }
.game-info { flex: 1; min-width: 0; }
.game h2 { font-size: 1.1rem; font-weight: 700; color: #f0f0f0; line-height: 1.3; }
.game-meta {
display: flex; align-items: center; gap: 0.6rem;
margin-top: 0.25rem; flex-wrap: wrap;
}
.health {
display: inline-flex; align-items: center; gap: 0.3rem;
font-size: 0.7rem; font-weight: 500;
}
.health-dot {
width: 6px; height: 6px; border-radius: 50%;
background: #333; transition: all 0.4s;
}
.health-dot.online {
background: #22c55e;
box-shadow: 0 0 6px rgba(34,197,94,0.6);
animation: glow 2.5s ease-in-out infinite;
}
.health-dot.offline {
background: #ef4444;
box-shadow: 0 0 4px rgba(239,68,68,0.4);
}
.health-dot.checking {
background: #666;
animation: blink 0.8s ease-in-out infinite;
}
.health-label { color: #666; }
.health-label.online { color: #4ade80; }
.health-label.offline { color: #f87171; }
@keyframes glow {
0%, 100% { box-shadow: 0 0 4px rgba(34,197,94,0.4); }
50% { box-shadow: 0 0 10px rgba(34,197,94,0.7); }
}
@keyframes blink {
0%, 100% { opacity: 0.3; }
50% { opacity: 1; }
}
.players-badge {
font-size: 0.68rem; color: #555; font-weight: 500;
}
.tag {
font-size: 0.6rem; font-weight: 600; text-transform: uppercase;
letter-spacing: 0.6px; padding: 2px 7px; border-radius: 5px;
}
.tag-team { background: rgba(139,92,246,0.1); color: #a78bfa; }
.tag-party { background: rgba(236,72,153,0.1); color: #f472b6; }
.tag-strategy { background: rgba(239,68,68,0.1); color: #f87171; }
.tag-quiz { background: rgba(245,158,11,0.1); color: #fbbf24; }
.tag-board { background: rgba(16,185,129,0.1); color: #34d399; }
.game p {
font-size: 0.82rem; color: #777; line-height: 1.55;
position: relative;
}
.game-arrow {
position: absolute; bottom: 1.2rem; right: 1.4rem;
color: #333; font-size: 1.1rem; transition: all 0.3s;
}
.game:hover .game-arrow { color: #8b5cf6; transform: translateX(3px); }
footer {
text-align: center; margin-top: 3rem;
font-size: 0.72rem; color: #333;
}
@media (max-width: 640px) {
.logo { font-size: 2.4rem; }
h1 { font-size: 1.8rem; }
.games { grid-template-columns: 1fr; }
.container { padding: 2rem 1rem 3rem; }
}
</style>
</head>
<body>
<div class="bg"></div>
<div class="container">
<header>
<div class="logo">&#127918;</div>
<h1>Game Night</h1>
<p class="subtitle">W&auml;hle ein Spiel und lade deine Freunde ein</p>
<div class="status-bar">
<span class="dot" id="summary-dot"></span>
<span id="summary-text">Services werden gepr&uuml;ft...</span>
</div>
</header>
<div class="games" id="games-grid"></div>
<footer>Hosted on joshuahirsig.xyz</footer>
</div>
<script>
var DEFAULT_SETTINGS = {
healthChecks: true,
healthTimeoutMs: 6000
};
var DEFAULT_GAMES = [
{ id: "codenames", name: "Codenames", icon: "\uD83D\uDD75\uFE0F", iconClass: "icon-purple", description: "Finde die Agenten deines Teams anhand von Hinweisen!", players: "4+ Spieler", tag: "Teamspiel", tagClass: "tag-team", url: "https://codenames.joshuahirsig.xyz" },
{ id: "scribble", name: "Scribble", icon: "\uD83C\uDFA8", iconClass: "icon-pink", description: "Zeichne und errate Begriffe \u2013 wie Skribbl.io!", players: "3+ Spieler", tag: "Party", tagClass: "tag-party", url: "https://scribble.joshuahirsig.xyz" },
{ id: "cards", name: "Massive Decks", icon: "\uD83C\uDCCF", iconClass: "icon-blue", description: "Cards Against Humanity \u2013 die besten schlechten Antworten gewinnen.", players: "3+ Spieler", tag: "Party", tagClass: "tag-party", url: "https://cards.joshuahirsig.xyz" },
{ id: "trivia", name: "Trivia", icon: "\uD83E\uDDE0", iconClass: "icon-amber", description: "Quiz-Spiel wie Kahoot \u2013 erstelle eigene Fragen!", players: "2+ Spieler", tag: "Quiz", tagClass: "tag-quiz", url: "https://trivia.joshuahirsig.xyz" },
{ id: "tabletop", name: "Virtual Tabletop", icon: "\uD83C\uDFB2", iconClass: "icon-emerald", description: "300+ Brettspiele, Kartenspiele und mehr.", players: "2+ Spieler", tag: "Brettspiel", tagClass: "tag-board", url: "https://tabletop.joshuahirsig.xyz" },
{ id: "tosios", name: "TOSIOS", icon: "\uD83D\uDD2B", iconClass: "icon-red", description: "Browser IO-Shooter \u2013 schnelle Multiplayer-Matches!", players: "2+ Spieler", tag: "Action", tagClass: "tag-strategy", url: "https://shooter.joshuahirsig.xyz" },
{ id: "geoquiz", name: "Le Grand GeoQuiz", icon: "\uD83C\uDF0D", iconClass: "icon-emerald", description: "Strategisches Geografie-Spiel \u2013 8 L\u00e4nder, 8 Kategorien, niedrigste Punktzahl gewinnt!", players: "1 Spieler", tag: "Quiz", tagClass: "tag-quiz", url: "https://geoquiz.joshuahirsig.xyz" }
];
var YAML_LOADER_URL = "https://cdn.jsdelivr.net/npm/js-yaml@4.1.0/dist/js-yaml.min.js";
var yamlLoaderPromise = null;
var grid = document.getElementById("games-grid");
function escapeHtml(value) {
return String(value)
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#39;");
}
function slugify(value) {
var slug = String(value || "")
.toLowerCase()
.replace(/[^a-z0-9]+/g, "-")
.replace(/^-+|-+$/g, "");
return slug || "game";
}
function loadScript(src) {
return new Promise(function(resolve, reject) {
var script = document.createElement("script");
script.src = src;
script.onload = resolve;
script.onerror = function() { reject(new Error("Script konnte nicht geladen werden: " + src)); };
document.head.appendChild(script);
});
}
function ensureYamlParser() {
if (window.jsyaml && typeof window.jsyaml.load === "function") {
return Promise.resolve(window.jsyaml);
}
if (!yamlLoaderPromise) {
yamlLoaderPromise = loadScript(YAML_LOADER_URL).then(function() {
if (!window.jsyaml || typeof window.jsyaml.load !== "function") {
throw new Error("YAML parser nicht verf\u00fcgbar");
}
return window.jsyaml;
});
}
return yamlLoaderPromise;
}
function normalizeGame(rawGame, index, usedIds) {
var game = rawGame && typeof rawGame === "object" ? rawGame : {};
var name = game.name ? String(game.name) : ("Game " + (index + 1));
var id = game.id ? String(game.id) : slugify(name);
if (usedIds[id]) {
var suffix = 2;
while (usedIds[id + "-" + suffix]) {
suffix++;
}
id = id + "-" + suffix;
}
usedIds[id] = true;
return {
id: id,
name: name,
icon: game.icon ? String(game.icon) : "\uD83C\uDFAE",
iconClass: game.iconClass ? String(game.iconClass) : "icon-blue",
iconSvg: game.iconSvg ? String(game.iconSvg) : "",
description: String(game.description || game.desc || ""),
players: String(game.players || ""),
tag: String(game.tag || ""),
tagClass: String(game.tagClass || "tag-team"),
url: String(game.url || "#"),
healthUrl: String(game.healthUrl || game.health_url || ""),
healthEnabled: game.healthEnabled !== false
};
}
function normalizeConfig(rawConfig) {
var configObject = Array.isArray(rawConfig)
? { games: rawConfig }
: (rawConfig && typeof rawConfig === "object" ? rawConfig : {});
var settings = Object.assign({}, DEFAULT_SETTINGS, configObject.settings || {});
var timeoutMs = Number(settings.healthTimeoutMs);
if (!Number.isFinite(timeoutMs) || timeoutMs < 1000) {
timeoutMs = DEFAULT_SETTINGS.healthTimeoutMs;
}
settings.healthChecks = settings.healthChecks !== false;
settings.healthTimeoutMs = timeoutMs;
var sourceGames = Array.isArray(configObject.games) ? configObject.games : [];
if (!sourceGames.length) {
sourceGames = DEFAULT_GAMES;
}
var usedIds = {};
var normalizedGames = sourceGames.map(function(game, index) {
return normalizeGame(game, index, usedIds);
});
return {
settings: settings,
games: normalizedGames
};
}
async function loadExternalConfig() {
var sources = [
{ path: "/games.json", format: "json" },
{ path: "/games.yaml", format: "yaml" },
{ path: "/games.yml", format: "yaml" }
];
for (var i = 0; i < sources.length; i++) {
var source = sources[i];
try {
var response = await fetch(source.path, { cache: "no-store" });
if (!response.ok) {
continue;
}
var parsed;
if (source.format === "json") {
parsed = await response.json();
} else {
var yaml = await ensureYamlParser();
parsed = yaml.load(await response.text());
}
console.info("Config geladen aus", source.path);
return normalizeConfig(parsed);
} catch (error) {
console.warn("Config konnte nicht gelesen werden:", source.path, error);
}
}
console.warn("Keine externe Config gefunden. Fallback wird verwendet.");
return normalizeConfig({ games: DEFAULT_GAMES, settings: DEFAULT_SETTINGS });
}
function buildMeta(game) {
var parts = [
'<span class="health" id="health-' + escapeHtml(game.id) + '">' +
'<span class="health-dot checking"></span>' +
'<span class="health-label">Pr\u00fcfe...</span>' +
'</span>'
];
if (game.players) {
parts.push('<span class="players-badge">' + escapeHtml(game.players) + '</span>');
}
if (game.tag) {
parts.push('<span class="tag ' + escapeHtml(game.tagClass) + '">' + escapeHtml(game.tag) + '</span>');
}
return parts.join("");
}
function renderGames(games) {
grid.innerHTML = "";
games.forEach(function(game, index) {
var card = document.createElement("a");
card.className = "game";
card.href = game.url;
card.innerHTML =
'<div class="game-top">' +
'<div class="game-icon ' + escapeHtml(game.iconClass) + '">' + (game.iconSvg ? '<img src="' + escapeHtml(game.iconSvg) + '"' + ' style="width:32px;height:32px" alt="">' : escapeHtml(game.icon)) + '</div>' +
'<div class="game-info">' +
'<h2>' + escapeHtml(game.name) + '</h2>' +
'<div class="game-meta">' + buildMeta(game) + '</div>' +
'</div>' +
'</div>' +
'<p>' + escapeHtml(game.description) + '</p>' +
'<span class="game-arrow">\u2192</span>';
card.addEventListener("mousemove", function(event) {
var rect = card.getBoundingClientRect();
card.style.setProperty("--mx", ((event.clientX - rect.left) / rect.width * 100) + "%");
card.style.setProperty("--my", ((event.clientY - rect.top) / rect.height * 100) + "%");
});
grid.appendChild(card);
});
}
function checkHealth(game, settings) {
var el = document.getElementById("health-" + game.id);
if (!el) {
return Promise.resolve({ checked: false, online: false });
}
if (!settings.healthChecks || game.healthEnabled === false) {
el.innerHTML = '<span class="health-dot"></span><span class="health-label">Kein Check</span>';
return Promise.resolve({ checked: false, online: false });
}
var targetUrl = game.healthUrl || game.url;
if (!targetUrl || targetUrl === "#") {
el.innerHTML = '<span class="health-dot"></span><span class="health-label">Kein Check</span>';
return Promise.resolve({ checked: false, online: false });
}
var ctrl = new AbortController();
var timer = setTimeout(function() { ctrl.abort(); }, settings.healthTimeoutMs);
return fetch(targetUrl, { mode: "no-cors", cache: "no-store", signal: ctrl.signal })
.then(function() {
clearTimeout(timer);
el.innerHTML = '<span class="health-dot online"></span><span class="health-label online">Online</span>';
return { checked: true, online: true };
})
.catch(function() {
clearTimeout(timer);
el.innerHTML = '<span class="health-dot offline"></span><span class="health-label offline">Offline</span>';
return { checked: true, online: false };
});
}
function updateSummary(results, totalGames) {
var dot = document.getElementById("summary-dot");
var txt = document.getElementById("summary-text");
var checked = 0;
var online = 0;
results.forEach(function(result) {
if (result.checked) {
checked++;
if (result.online) {
online++;
}
}
});
if (!totalGames) {
dot.className = "dot";
txt.textContent = "Keine Spiele konfiguriert";
return;
}
if (!checked) {
dot.className = "dot";
txt.textContent = "Health-Checks deaktiviert";
return;
}
if (online === checked) {
dot.className = "dot all-good";
txt.textContent = "Alle " + online + " Services online";
} else {
dot.className = "dot some-down";
txt.textContent = online + "/" + checked + " Services online";
}
if (checked < totalGames) {
txt.textContent += " (" + (totalGames - checked) + " ohne Check)";
}
}
(async function init() {
var config = await loadExternalConfig();
renderGames(config.games);
var healthResults = await Promise.all(
config.games.map(function(game) {
return checkHealth(game, config.settings);
})
);
updateSummary(healthResults, config.games.length);
})().catch(function(error) {
console.error("Landing konnte nicht initialisiert werden:", error);
document.getElementById("summary-text").textContent = "Fehler beim Laden";
});
</script>
</body>
</html>

4
massivedecks-icon.svg Normal file
View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512">
<path d="M273 20c-11 0-21 9-23 21l-9 88h-8L39 163c-12 2-21 15-19 27l50 283c2 12 15 21 27 19l194-34c12-3 21-15 18-27l-13-73 140 14c13 2 25-8 26-20l30-286c1-13-8-24-21-25L276 20h-3zm0 16h1l196 21c4 0 6 3 6 7l-30 286c0 4-4 7-8 6l-144-15-35-193-3-9 10-97c1-3 4-6 7-6zm47 106l-10 99 39 4c15 2 25 0 32-8 9-8 15-21 16-37 2-15-1-29-8-39-6-9-15-13-30-15zm19 20l18 1c16 2 22 14 20 35-3 22-11 32-27 31l-18-2zm-135 91l17 98-20 4-13-77-4 80-20 3-32-73 14 76-20 4-18-98 31-6 31 75 4-81z" />
</svg>

After

Width:  |  Height:  |  Size: 629 B

176
minecraft.html Normal file
View file

@ -0,0 +1,176 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>FTB StoneBlock 4 - Server Info</title>
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
<script src="https://unpkg.com/lucide@latest"></script>
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap');
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; background: #07070f; color: #d0d0d0; min-height: 100vh; }
.bg { position: fixed; inset: 0; z-index: 0; pointer-events: none; background: radial-gradient(ellipse 80% 60% at 15% 45%, rgba(16,185,129,0.12) 0%, transparent 60%), radial-gradient(ellipse 60% 50% at 85% 25%, rgba(59,130,246,0.08) 0%, transparent 55%); }
.bg::after { content: ''; position: absolute; inset: 0; background-image: radial-gradient(rgba(255,255,255,0.03) 1px, transparent 1px); background-size: 32px 32px; }
.container { position: relative; z-index: 1; max-width: 800px; margin: 0 auto; padding: 2.5rem 1.5rem 4rem; }
header { text-align: center; margin-bottom: 2rem; }
.logo { font-size: 3rem; margin-bottom: 0.3rem; color: #34d399; }
h1 { font-size: 2.2rem; font-weight: 800; letter-spacing: -0.03em; background: linear-gradient(135deg, #6ee7b7 0%, #34d399 40%, #10b981 100%); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; }
.subtitle { color: #555; font-size: 0.9rem; margin-top: 0.4rem; }
.server-info { display: flex; flex-wrap: wrap; gap: 0.8rem; justify-content: center; margin: 1.5rem 0 2rem; }
.info-badge { display: inline-flex; align-items: center; gap: 0.5rem; padding: 0.5rem 1rem; background: rgba(255,255,255,0.04); border: 1px solid rgba(255,255,255,0.06); border-radius: 99px; font-size: 0.82rem; color: #aaa; }
.info-badge strong { color: #f0f0f0; }
.info-badge svg { width: 16px; height: 16px; color: #777; flex-shrink: 0; }
.info-badge .copy-btn { background: none; border: none; color: #666; cursor: pointer; padding: 0 0.2rem; transition: color 0.2s; display: inline-flex; align-items: center; }
.info-badge .copy-btn svg { width: 14px; height: 14px; }
.info-badge .copy-btn:hover { color: #10b981; }
.download-btn { display: inline-flex; align-items: center; gap: 0.5rem; padding: 0.7rem 1.5rem; margin: 0.5rem 0.5rem 2rem; background: linear-gradient(135deg, #10b981 0%, #059669 100%); color: #fff; font-weight: 600; font-size: 0.9rem; border: none; border-radius: 12px; cursor: pointer; text-decoration: none; transition: all 0.3s; }
.download-btn:hover { transform: translateY(-2px); box-shadow: 0 8px 24px rgba(16,185,129,0.3); }
.download-btn.orange { background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%); }
.download-btn.orange:hover { box-shadow: 0 8px 24px rgba(245,158,11,0.3); }
.steps { background: rgba(255,255,255,0.025); border: 1px solid rgba(255,255,255,0.05); border-radius: 16px; padding: 1.5rem; margin-bottom: 2rem; }
.steps h2 { font-size: 1.1rem; font-weight: 700; color: #f0f0f0; margin-bottom: 1rem; display: flex; align-items: center; gap: 0.5rem; }
.steps h2 svg { width: 20px; height: 20px; color: #34d399; flex-shrink: 0; }
.steps ol { padding-left: 1.4rem; }
.steps li { margin-bottom: 0.7rem; font-size: 0.88rem; line-height: 1.6; color: #bbb; }
.steps li strong { color: #f0f0f0; }
.steps code { background: rgba(16,185,129,0.1); color: #6ee7b7; padding: 0.15rem 0.5rem; border-radius: 6px; font-size: 0.82rem; }
h2.section-title { font-size: 1.3rem; font-weight: 800; color: #f0f0f0; margin: 2rem 0 1rem; padding-bottom: 0.5rem; border-bottom: 1px solid rgba(255,255,255,0.06); display: flex; align-items: center; gap: 0.5rem; }
h2.section-title svg { width: 20px; height: 20px; color: #34d399; flex-shrink: 0; }
.cmd-grid { display: grid; gap: 0.5rem; margin-bottom: 1.5rem; }
.cmd-row { display: grid; grid-template-columns: 1fr 1.2fr; background: rgba(255,255,255,0.025); border: 1px solid rgba(255,255,255,0.04); border-radius: 10px; overflow: hidden; }
.cmd-row:hover { border-color: rgba(16,185,129,0.2); }
.cmd-row.admin { border-left: 3px solid rgba(239,68,68,0.4); }
.cmd-row.player { border-left: 3px solid rgba(34,197,94,0.4); }
.cmd-cmd { padding: 0.6rem 0.9rem; font-family: monospace; font-size: 0.78rem; color: #6ee7b7; background: rgba(16,185,129,0.04); display: flex; align-items: center; word-break: break-all; }
.cmd-desc { padding: 0.6rem 0.9rem; font-size: 0.82rem; color: #999; display: flex; align-items: center; gap: 0.5rem; }
.badge { font-size: 0.6rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.5px; padding: 2px 6px; border-radius: 4px; white-space: nowrap; }
.badge-admin { background: rgba(239,68,68,0.15); color: #f87171; }
.badge-player { background: rgba(34,197,94,0.15); color: #4ade80; }
.legend { display: flex; gap: 1.2rem; justify-content: center; margin: 1rem 0 0.5rem; font-size: 0.78rem; color: #777; }
.legend span { display: inline-flex; align-items: center; gap: 0.4rem; }
.legend-dot { width: 10px; height: 3px; border-radius: 2px; }
.legend-dot.admin { background: rgba(239,68,68,0.6); }
.legend-dot.player { background: rgba(34,197,94,0.6); }
.back-link { display: inline-flex; align-items: center; gap: 0.4rem; color: #555; font-size: 0.8rem; text-decoration: none; margin-bottom: 1.5rem; transition: color 0.2s; }
.back-link:hover { color: #10b981; }
.mc-status { display: inline-flex; align-items: center; gap: 0.4rem; padding: 0.4rem 1rem; background: rgba(255,255,255,0.04); border: 1px solid rgba(255,255,255,0.06); border-radius: 99px; font-size: 0.78rem; color: #777; margin-bottom: 1rem; }
.mc-dot { width: 8px; height: 8px; border-radius: 50%; background: #555; transition: all 0.4s; }
.mc-dot.online { background: #22c55e; box-shadow: 0 0 8px rgba(34,197,94,0.6); animation: glow 2.5s ease-in-out infinite; }
.mc-dot.offline { background: #ef4444; box-shadow: 0 0 4px rgba(239,68,68,0.4); }
@keyframes glow { 0%,100% { box-shadow: 0 0 4px rgba(34,197,94,0.4); } 50% { box-shadow: 0 0 10px rgba(34,197,94,0.7); } }
footer { text-align: center; margin-top: 2rem; font-size: 0.72rem; color: #333; }
@media (max-width: 640px) { h1 { font-size: 1.7rem; } .cmd-row { grid-template-columns: 1fr; } .container { padding: 1.5rem 1rem 3rem; } }
</style>
</head>
<body>
<div class="bg"></div>
<div class="container">
<a href="https://games.joshuahirsig.xyz" class="back-link"><i data-lucide="arrow-left" style="width:14px;height:14px"></i> Zurück zu Game Night</a>
<header>
<div class="logo"><i data-lucide="pickaxe"></i></div>
<h1>FTB StoneBlock 4</h1>
<p class="subtitle">Minecraft Modpack Server</p>
</header>
<div style="text-align:center">
<div class="mc-status"><span class="mc-dot" id="mc-dot"></span><span id="mc-status-text">Server wird geprüft...</span></div>
<div class="server-info">
<div class="info-badge"><i data-lucide="globe"></i> Server: <strong>joshuahirsig.xyz</strong> <button class="copy-btn" onclick="navigator.clipboard.writeText('joshuahirsig.xyz')" title="Kopieren"><i data-lucide="clipboard"></i></button></div>
<div class="info-badge"><i data-lucide="lock"></i> Online Mode (Mojang-Account)</div>
<div class="info-badge"><i data-lucide="users"></i> Max 20 Spieler</div>
<div class="info-badge"><i data-lucide="cpu"></i> Minecraft 1.21.1 / NeoForge</div>
</div>
<a href="https://www.curseforge.com/minecraft/modpacks/ftb-stoneblock-4" target="_blank" class="download-btn"><i data-lucide="download"></i> Modpack bei CurseForge</a>
<a href="https://www.feed-the-beast.com/modpacks/130-ftb-stoneblock-4" target="_blank" class="download-btn orange"><i data-lucide="download"></i> Modpack bei FTB App</a>
</div>
<div class="steps">
<h2><i data-lucide="rocket"></i> So kommst du auf den Server</h2>
<ol>
<li><strong>FTB App</strong> oder <strong>CurseForge App</strong> installieren</li>
<li><strong>FTB StoneBlock 4</strong> Modpack installieren und starten</li>
<li>Im Hauptmenu: <strong>Multiplayer</strong> &rarr; <strong>Server hinzufügen</strong></li>
<li>Server-Adresse eingeben: <code>joshuahirsig.xyz</code></li>
<li>Verbinden &mdash; du landest in der <strong>Lobby</strong></li>
<li>Durch ein <strong>Portal</strong> gehen und eine Base wählen (Stone, Cherry, Cave, etc.)</li>
<li>Viel Spass beim Graben</li>
</ol>
</div>
<div class="legend"><span><span class="legend-dot player"></span> Alle Spieler</span><span><span class="legend-dot admin"></span> Nur Admin (OP)</span></div>
<h2 class="section-title"><i data-lucide="home"></i> FTB Team Bases</h2>
<div class="cmd-grid">
<div class="cmd-row player"><div class="cmd-cmd">/ftbteams party create &lt;Name&gt;</div><div class="cmd-desc"><span class="badge badge-player">Alle</span> Neues Team erstellen</div></div>
<div class="cmd-row player"><div class="cmd-cmd">/ftbteams party invite &lt;Name&gt;</div><div class="cmd-desc"><span class="badge badge-player">Alle</span> Spieler ins Team einladen</div></div>
<div class="cmd-row player"><div class="cmd-cmd">/ftbteams party kick &lt;Name&gt;</div><div class="cmd-desc"><span class="badge badge-player">Alle</span> Spieler aus Team werfen</div></div>
<div class="cmd-row player"><div class="cmd-cmd">/ftbteams party leave</div><div class="cmd-desc"><span class="badge badge-player">Alle</span> Team verlassen</div></div>
<div class="cmd-row player"><div class="cmd-cmd">/ftbteambases home</div><div class="cmd-desc"><span class="badge badge-player">Alle</span> Zur eigenen Base teleportieren</div></div>
<div class="cmd-row player"><div class="cmd-cmd">/ftbteambases lobby</div><div class="cmd-desc"><span class="badge badge-player">Alle</span> Zurück zur Lobby</div></div>
</div>
<h2 class="section-title"><i data-lucide="shield"></i> Spieler-Verwaltung</h2>
<div class="cmd-grid">
<div class="cmd-row admin"><div class="cmd-cmd">/whitelist add &lt;Name&gt;</div><div class="cmd-desc"><span class="badge badge-admin">Admin</span> Spieler zur Whitelist hinzufügen</div></div>
<div class="cmd-row admin"><div class="cmd-cmd">/whitelist remove &lt;Name&gt;</div><div class="cmd-desc"><span class="badge badge-admin">Admin</span> Spieler von Whitelist entfernen</div></div>
<div class="cmd-row admin"><div class="cmd-cmd">/whitelist list</div><div class="cmd-desc"><span class="badge badge-admin">Admin</span> Alle Spieler auf der Whitelist</div></div>
<div class="cmd-row admin"><div class="cmd-cmd">/kick &lt;Name&gt; [Grund]</div><div class="cmd-desc"><span class="badge badge-admin">Admin</span> Spieler kicken</div></div>
<div class="cmd-row admin"><div class="cmd-cmd">/ban &lt;Name&gt; [Grund]</div><div class="cmd-desc"><span class="badge badge-admin">Admin</span> Spieler permanent bannen</div></div>
<div class="cmd-row admin"><div class="cmd-cmd">/pardon &lt;Name&gt;</div><div class="cmd-desc"><span class="badge badge-admin">Admin</span> Ban aufheben</div></div>
</div>
<h2 class="section-title"><i data-lucide="compass"></i> Teleport &amp; Gamemode</h2>
<div class="cmd-grid">
<div class="cmd-row admin"><div class="cmd-cmd">/tp &lt;Name&gt;</div><div class="cmd-desc"><span class="badge badge-admin">Admin</span> Zu Spieler teleportieren</div></div>
<div class="cmd-row admin"><div class="cmd-cmd">/tp &lt;Name&gt; &lt;x&gt; &lt;y&gt; &lt;z&gt;</div><div class="cmd-desc"><span class="badge badge-admin">Admin</span> Spieler zu Koordinaten teleportieren</div></div>
<div class="cmd-row admin"><div class="cmd-cmd">/gamemode survival &lt;Name&gt;</div><div class="cmd-desc"><span class="badge badge-admin">Admin</span> Überlebensmodus</div></div>
<div class="cmd-row admin"><div class="cmd-cmd">/gamemode creative &lt;Name&gt;</div><div class="cmd-desc"><span class="badge badge-admin">Admin</span> Kreativmodus</div></div>
<div class="cmd-row admin"><div class="cmd-cmd">/gamemode spectator &lt;Name&gt;</div><div class="cmd-desc"><span class="badge badge-admin">Admin</span> Zuschauermodus</div></div>
</div>
<h2 class="section-title"><i data-lucide="settings"></i> Server-Verwaltung</h2>
<div class="cmd-grid">
<div class="cmd-row admin"><div class="cmd-cmd">/difficulty easy|normal|hard</div><div class="cmd-desc"><span class="badge badge-admin">Admin</span> Schwierigkeit ändern</div></div>
<div class="cmd-row admin"><div class="cmd-cmd">/gamerule keepInventory true</div><div class="cmd-desc"><span class="badge badge-admin">Admin</span> Inventar beim Tod behalten</div></div>
<div class="cmd-row admin"><div class="cmd-cmd">/gamerule doDaylightCycle false</div><div class="cmd-desc"><span class="badge badge-admin">Admin</span> Tag/Nacht-Zyklus stoppen</div></div>
<div class="cmd-row admin"><div class="cmd-cmd">/time set day</div><div class="cmd-desc"><span class="badge badge-admin">Admin</span> Tageszeit auf Tag setzen</div></div>
<div class="cmd-row admin"><div class="cmd-cmd">/weather clear</div><div class="cmd-desc"><span class="badge badge-admin">Admin</span> Wetter auf klar setzen</div></div>
<div class="cmd-row admin"><div class="cmd-cmd">/save-all</div><div class="cmd-desc"><span class="badge badge-admin">Admin</span> Welt manuell speichern</div></div>
</div>
<h2 class="section-title"><i data-lucide="package"></i> Items &amp; Effekte</h2>
<div class="cmd-grid">
<div class="cmd-row admin"><div class="cmd-cmd">/give &lt;Name&gt; &lt;Item&gt; [Anzahl]</div><div class="cmd-desc"><span class="badge badge-admin">Admin</span> Item geben</div></div>
<div class="cmd-row admin"><div class="cmd-cmd">/clear &lt;Name&gt;</div><div class="cmd-desc"><span class="badge badge-admin">Admin</span> Inventar leeren</div></div>
<div class="cmd-row admin"><div class="cmd-cmd">/effect give &lt;Name&gt; &lt;Effekt&gt; &lt;Dauer&gt;</div><div class="cmd-desc"><span class="badge badge-admin">Admin</span> Effekt geben</div></div>
<div class="cmd-row admin"><div class="cmd-cmd">/effect clear &lt;Name&gt;</div><div class="cmd-desc"><span class="badge badge-admin">Admin</span> Alle Effekte entfernen</div></div>
<div class="cmd-row admin"><div class="cmd-cmd">/kill &lt;Name&gt;</div><div class="cmd-desc"><span class="badge badge-admin">Admin</span> Spieler/Entity töten</div></div>
</div>
<footer>Hosted on joshuahirsig.xyz</footer>
</div>
<script>
lucide.createIcons();
(function() {
var dot = document.getElementById('mc-dot');
var txt = document.getElementById('mc-status-text');
fetch('https://minecraft.joshuahirsig.xyz/api/status')
.then(function(r) { return r.json(); })
.then(function(data) {
if (data.online) {
dot.className = 'mc-dot online';
var players = data.players + '/' + data.max + ' Spieler';
txt.textContent = 'Server Online - ' + players;
txt.style.color = '#4ade80';
} else {
dot.className = 'mc-dot offline';
txt.textContent = 'Server Offline';
txt.style.color = '#f87171';
}
})
.catch(function() {
dot.className = 'mc-dot offline';
txt.textContent = 'Status unbekannt';
txt.style.color = '#f87171';
});
})();
</script>
</body>
</html>

7
pokemon-icon.svg Normal file
View file

@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
<circle cx="50" cy="50" r="48" fill="#cc1a1a" stroke="#111" stroke-width="4"/>
<rect x="2" y="47" width="96" height="6" fill="#111"/>
<circle cx="50" cy="50" r="12" fill="#111"/>
<circle cx="50" cy="50" r="8" fill="#e8e8e8" stroke="#111" stroke-width="2"/>
<text x="50" y="30" text-anchor="middle" font-size="22" font-weight="bold" fill="#fff" font-family="sans-serif"></text>
</svg>

After

Width:  |  Height:  |  Size: 460 B

8
scribble-icon.svg Normal file
View file

@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" fill="none">
<path d="M65 15l20 20-45 45-25 5 5-25z" stroke="#ec4899" stroke-width="4" stroke-linejoin="round"/>
<path d="M60 20l20 20" stroke="#ec4899" stroke-width="4" stroke-linecap="round"/>
<path d="M20 80l25-5" stroke="#ec4899" stroke-width="3" stroke-linecap="round"/>
<circle cx="30" cy="55" r="4" fill="#f9a8d4"/>
<circle cx="45" cy="40" r="3" fill="#f9a8d4"/>
<circle cx="22" cy="68" r="3" fill="#f9a8d4"/>
</svg>

After

Width:  |  Height:  |  Size: 498 B

8
tabletop-icon.svg Normal file
View file

@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" fill="none">
<rect x="15" y="15" width="70" height="70" rx="12" stroke="#10b981" stroke-width="4"/>
<circle cx="35" cy="35" r="5" fill="#6ee7b7"/>
<circle cx="65" cy="35" r="5" fill="#6ee7b7"/>
<circle cx="35" cy="65" r="5" fill="#6ee7b7"/>
<circle cx="65" cy="65" r="5" fill="#6ee7b7"/>
<circle cx="50" cy="50" r="5" fill="#6ee7b7"/>
</svg>

After

Width:  |  Height:  |  Size: 416 B

9
tosios-icon.svg Normal file
View file

@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" fill="none">
<circle cx="50" cy="50" r="35" stroke="#ef4444" stroke-width="4"/>
<circle cx="50" cy="50" r="20" stroke="#ef4444" stroke-width="3"/>
<circle cx="50" cy="50" r="5" fill="#fca5a5"/>
<line x1="50" y1="8" x2="50" y2="22" stroke="#ef4444" stroke-width="3" stroke-linecap="round"/>
<line x1="50" y1="78" x2="50" y2="92" stroke="#ef4444" stroke-width="3" stroke-linecap="round"/>
<line x1="8" y1="50" x2="22" y2="50" stroke="#ef4444" stroke-width="3" stroke-linecap="round"/>
<line x1="78" y1="50" x2="92" y2="50" stroke="#ef4444" stroke-width="3" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 663 B

7
trivia-icon.svg Normal file
View file

@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" fill="none">
<path d="M50 10C30 10 18 25 18 40c0 10 5 18 12 23v12h40V63c7-5 12-13 12-23C82 25 70 10 50 10z" stroke="#f59e0b" stroke-width="4" stroke-linejoin="round"/>
<line x1="35" y1="85" x2="65" y2="85" stroke="#f59e0b" stroke-width="4" stroke-linecap="round"/>
<line x1="38" y1="93" x2="62" y2="93" stroke="#f59e0b" stroke-width="4" stroke-linecap="round"/>
<line x1="50" y1="30" x2="50" y2="50" stroke="#fcd34d" stroke-width="3.5" stroke-linecap="round"/>
<circle cx="50" cy="57" r="2.5" fill="#fcd34d"/>
</svg>

After

Width:  |  Height:  |  Size: 589 B

BIN
worldguessr-icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB