add frontend and swap whole stack to sveltekit/nodejs

This commit is contained in:
2023-08-10 13:36:04 +02:00
parent d423c41ec7
commit ad90b440f5
124 changed files with 6665 additions and 11046 deletions

20
src/routes/+layout.svelte Normal file
View File

@ -0,0 +1,20 @@
<script>
import '../app.css';
</script>
<nav class="navbar fixed top-0 bg-base-100 h-12 z-40">
<div class="navbar-start h-full">
<a class="h-full" href="/">
<img class="rounded h-full" src="/img/craftattack-logo.webp" alt="Logo" />
</a>
</div>
<div class="navbar-center flex space-x-20">
<a class="link" href="/register">Anmelden</a>
<a class="link" href="/rules">Regeln</a>
</div>
<div class="navbar-end" />
</nav>
<main class="flex min-h-[calc(100vh-64px-16px)] mt-16 mb-4">
<slot />
</main>

12
src/routes/+page.svelte Normal file
View File

@ -0,0 +1,12 @@
<script lang="ts">
import { PUBLIC_START_DATE } from '$env/static/public';
import Timer from '$lib/components/Countdown/Countdown.svelte';
</script>
<svelte:head>
<title>Craftattack</title>
</svelte:head>
<div class="w-full flex justify-center items-center">
<Timer end={Date.parse(PUBLIC_START_DATE)} />
</div>

6
src/routes/+server.ts Normal file
View File

@ -0,0 +1,6 @@
import type { RequestHandler } from '@sveltejs/kit';
import { json } from '@sveltejs/kit';
export const POST = (() => {
return json({ scc: true });
}) satisfies RequestHandler;

View File

@ -0,0 +1,3 @@
<div class="flex justify-center w-full">
<slot />
</div>

View File

@ -0,0 +1,23 @@
<script lang="ts">
import { fly } from 'svelte/transition';
import RegistrationComplete from './RegistrationComplete.svelte';
import Register from './Register.svelte';
let registered = false;
</script>
<svelte:head>
<title>Craftattack - Anmeldung</title>
</svelte:head>
<div class="absolute top-12 grid card w-11/12 xl:w-2/3 2xl:w-1/2 p-6 shadow-lg overflow-hidden">
{#if !registered}
<div class="col-[1] row-[1]" transition:fly={{ x: -200, duration: 300 }}>
<Register on:submit={() => (registered = true)} />
</div>
{:else}
<div class="col-[1] row-[1]" transition:fly={{ x: 200, duration: 300 }}>
<RegistrationComplete on:close={() => (registered = false)} />
</div>
{/if}
</div>

View File

@ -0,0 +1,49 @@
import {
getBedrockUuid,
getCrackedUuid,
getJavaUuid,
UserNotFoundError
} from '$lib/server/minecraft';
import { error, type RequestHandler } from '@sveltejs/kit';
import { User } from '$lib/server/database';
export const POST = (async ({ request }) => {
const data = await request.formData();
let uuid: string;
try {
// available playertypes are 'java', 'bedrock' and 'cracked'
switch (data.get('playertype')) {
case 'java':
uuid = await getJavaUuid(data.get('username') as string);
break;
case 'bedrock':
uuid = await getBedrockUuid(data.get('username') as string);
break;
case 'cracked':
uuid = getCrackedUuid(data.get('username') as string);
break;
default:
throw new Error(`invalid player type (${data.get('playertype')})`);
}
} catch (e) {
if (e instanceof UserNotFoundError) {
throw error(400, e.message);
}
console.error((e as Error).message);
return new Response();
}
await User.create({
firstname: data.get('firstname'),
lastname: data.get('lastname'),
birthday: data.get('birthday'),
telephone: data.get('telephone'),
username: data.get('username'),
playertype: data.get('playertype'),
password: data.get('password'),
uuid: uuid
});
return new Response();
}) satisfies RequestHandler;

View File

@ -0,0 +1,221 @@
<script lang="ts">
import Select from '$lib/components/Input/Select.svelte';
import Input from '$lib/components/Input/Input.svelte';
import { createEventDispatcher, onMount } from 'svelte';
const dispatch = createEventDispatcher();
// eslint-disable-next-line @typescript-eslint/no-empty-function
let checkInputs = () => {};
let playertype = 'java';
let firstnameInput: HTMLInputElement;
let lastnameInput: HTMLInputElement;
let birthdayInput: HTMLInputElement;
let usernameInput: HTMLInputElement;
let privacyInput: HTMLInputElement;
let logsInput: HTMLInputElement;
let rulesInput: HTMLInputElement;
onMount(() => {
checkInputs = () => {
let allInputs = [
firstnameInput,
lastnameInput,
birthdayInput,
usernameInput,
privacyInput,
logsInput,
rulesInput
];
if (!allInputs.every((v) => v.value || v.checked)) {
inputsInvalidMessage = 'Bitte fülle alle erforderlichen Felder aus';
} else {
inputsInvalidMessage = null;
}
};
});
async function sendRegister() {
// eslint-disable-next-line no-async-promise-executor
registerRequest = new Promise(async (resolve, reject) => {
const response = await fetch('/register', {
method: 'POST',
body: new FormData(document.forms[0])
});
if (response.ok) {
dispatch('submit', {});
resolve();
} else if (response.status < 500) {
reject(Error((await response.json()).message));
} else {
reject(Error(`${response.statusText} (${response.status})`));
}
});
}
let inputsInvalidMessage: string | null = 'Bitte fülle alle erforderlichen Felder aus';
let registerRequest: Promise<void> | null = null;
</script>
<h1 class="text-center text-3xl lg:text-5xl">Anmeldung</h1>
<form id="form" on:input={checkInputs} on:submit|preventDefault={sendRegister}>
<div class="divider">Persönliche Angaben</div>
<div class="mx-2 grid grid-cols-1 sm:grid-cols-2 gap-y-4">
<Input
id="firstname"
name="firstname"
type="text"
required={true}
bind:inputElement={firstnameInput}
>
<span slot="label">Vorname</span>
</Input>
<Input
id="lastname"
name="lastname"
type="text"
required={true}
bind:inputElement={lastnameInput}
>
<span slot="label">Nachname</span>
</Input>
<Input
id="birthday"
name="birthday"
type="date"
required={true}
bind:inputElement={birthdayInput}
>
<span slot="label">Geburtstag</span>
<span slot="notice">Die Angabe hat keine Auswirkungen auf das Spielgeschehen</span>
</Input>
<Input id="telephone" name="telephone" type="tel">
<span slot="label">Telefonnummer</span>
<p slot="notice">
Diese nutzen wir, um Dich in der Whatsapp-Gruppe zuzuordnen und kontaktieren zu können.
<br />
<b>Die Angabe ist freiwillig, hilft den Administratoren jedoch sehr!</b>
</p>
</Input>
</div>
<div class="divider">Spiel</div>
<div class="mx-2 grid grid-cols-1 sm:grid-cols-2 gap-y-4">
<Input
id="username"
name="username"
type="text"
required={true}
bind:inputElement={usernameInput}
>
<span slot="label">Minecraft-Spielername</span>
</Input>
<Select
id="playertype"
name="playertype"
label="Edition"
bind:value={playertype}
required={true}
>
<option value="java">Java Edition</option>
<option value="bedrock">Bedrock Edition</option>
<option value="cracked">Java cracked</option>
</Select>
{#if playertype === 'cracked'}
<div class="sm:col-span-2">
<Input id="password" name="password" type="password" required={true}>
<span slot="label">Passwort</span>
<span slot="notice">
Da Du cracked spielst, musst Du ein Passwort festlegen, mit welchem Du Dich auf dem
Server authentifizierst! Das Passwort wird im Klartext gespeichert und ist in deinen
Clientlogs sowie in Serverlogs für Admins sichtbar. Verwende daher ein neues Passwort,
welches Du nirgends sonst verwendest! Merke Dir das Passwort gut, ohne kannst Du Dich
nicht auf dem Server einloggen
</span>
</Input>
</div>
{/if}
</div>
<div class="divider" />
<div class="mx-2 grid gap-y-3 mb-6">
<div class="flex gap-4">
<Input
id="privacy"
name="privacy"
type="checkbox"
required={true}
bind:inputElement={privacyInput}
/>
<label for="privacy">
<span>
Ich bin mit der Speicherung meiner in der Anmeldung angegebenen, persönlichen Daten
einverstanden. Siehe <a class="link" href="https://mhsl.eu/id.html">Datenschutz</a>
</span>
<span class="text-red-700">*</span>
</label>
</div>
<div class="flex gap-4">
<Input id="logs" name="logs" type="checkbox" required={true} bind:inputElement={logsInput} />
<label for="logs">
<span>
Ich bin mit der Speicherung in Form von Logs aller meiner, beim Spielen anfallenden,
persönlichen Daten durch den Server einverstanden
</span>
<span class="text-red-700">*</span>
</label>
</div>
<div class="flex gap-4">
<Input
id="rules"
name="rules"
type="checkbox"
required={true}
bind:inputElement={rulesInput}
/>
<label for="rules">
Ich bin mit den <a class="link" href="/rules">Regeln</a> einverstanden und achte sie
<span class="text-red-700">*</span>
<br />
<p class="text-[.75rem]">
Dies betrifft jede Interaktion im Spiel und zugehörige Daten wie z.B. Chatnachrichten
welche vom Minecraft Client an den Server übermittelt werden
</p>
</label>
</div>
</div>
<div
class={inputsInvalidMessage !== null ? 'tooltip tooltip-top' : 'grid w-min'}
data-tip={inputsInvalidMessage}
>
<div class="row-[1] col-[1]">
<Input
id="submit"
type="submit"
value="Anmeldung absenden"
disabled={inputsInvalidMessage !== null || registerRequest !== null}
/>
</div>
{#key registerRequest}
{#if registerRequest}
{#await registerRequest}
<span
class="relative top-[calc(50%-12px)] left-[calc(50%-12px)] row-[1] col-[1] loading loading-ring"
/>
{:catch error}
<dialog
class="modal"
on:close={() => setTimeout(() => (registerRequest = null), 200)}
open
>
<form method="dialog" class="modal-box">
<button class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2"></button>
<h3 class="font-bold text-lg">Error</h3>
<p class="py-4">{error.message}</p>
</form>
<form method="dialog" class="modal-backdrop bg-[rgba(0,0,0,.2)]">
<button>close</button>
</form>
</dialog>
{/await}
{/if}
{/key}
</div>
</form>

View File

@ -0,0 +1,32 @@
<script lang="ts">
import { IconSolid } from 'svelte-heros-v2';
import { createEventDispatcher } from 'svelte';
import { PUBLIC_START_DATE } from '$env/static/public';
const dispatch = createEventDispatcher();
let startDayOptions: Intl.DateTimeFormatOptions = {
day: '2-digit',
month: 'long',
year: 'numeric'
};
let startTimeOptions: Intl.DateTimeFormatOptions = {
hour: '2-digit',
minute: '2-digit'
};
</script>
<div class="flex items-center h-12 mb-2">
<button class="sm:absolute btn btn-sm btn-square" on:click={() => dispatch('close')}>
<IconSolid name="chevron-left-solid" />
</button>
<h1 class="text-center text-xl sm:text-3xl m-auto">Registrierung erfolgreich</h1>
</div>
<p>
<b>Du hast dich erfolgreich für Craftattack 6 registriert</b>. Spielstart ist am {new Date(
PUBLIC_START_DATE
).toLocaleString('de-DE', startDayOptions)} um {new Date(PUBLIC_START_DATE).toLocaleString(
'de-DE',
startTimeOptions
)} Uhr.
</p>

View File

@ -0,0 +1,3 @@
<div class="mx-4 sm:mx-48">
<slot />
</div>

View File

@ -0,0 +1,121 @@
<svelte:head>
<title>Craftattack - Regeln</title>
</svelte:head>
<h1 class="text-3xl lg:text-5xl mb-4">CraftAttack 5 Regelwerk</h1>
<p>
Das Lesen der Regeln ist für alle Teilnehmer verpflichtend. Die Regeln sollen für einen
reibungslosen und strukturierte Ablauf des Projekts sorgen, weshalb das Lesen der Regeln ein
essenzieller Bestandteil für das Gelingen von CraftAttack 5 ist. Die Regeln sind wörtlich zu
verstehen und sind Grundlage für das Projekt. Zur Vereinfachung gehen sie nicht zu weit ins Detail
und deuten teils nur umfangreiche Themengebiete an. Entscheidungen werden, wenn von Spielern
angeregt, dann durch die Administratoren getroffen, die sich an den Regeln orientieren.
</p>
<ol class="p-[revert] list-decimal my-6">
<li>
Oberste Priorität hat der respektvolle und tolerante Umgang der Spieler untereinander. Der
Spielspaß, der offene Umgang miteinander und die Interaktion aller steht im Vordergrund, weshalb
Drohungen, Belästigungen oder sonstige gegenüber anderen Spielern respektlose Aktivitäten
strengstens verboten sind und auch hart geahndet werden.
</li>
<li>
Selbstverständlich sind sämtliche Inhalte (Minecraft-Namen, Skins, Chat-Nachrichten, Links,
etc.) mit sexistischen, diskriminierenden, rassistischen, pornographischen oder illegalen
Inhalten nicht erlaubt. Außerdem ist es nicht gestattet, den Chat mit Nachrichten jeglicher Art
vollzuspammen. Des Weiteren sollte der MC-Name des Spielers, der bei der Anmeldung angegeben
wird, bis zum Ende des Projekts nicht geändert werden. Das Nutzen bzw. Anmelden von
Zweitaccounts ist nicht gestattet.
</li>
<li>
Jegliche Clientmodifications, die deutliche Vorteile gegenüber anderen Spielern erbringen, sind
nicht gestattet. Alle Spieler, die kein Minecraft Vanilla spielen, sind verpflichtet ihre
Clients oder Modifications dahingehend zu überprüfen, ob durch diese entscheidende Vorteile
erlangt werden können. Solche Modifications sind zu entfernen. Das Nutzen von simplen Mini-Maps
(Draufsicht) oder Cosmetics ist selbstverständlich erlaubt. Es liegt im Allgemeinen im Interesse
der Administratoren, dass Spieler es nicht übertreiben und sich keine Vorteile schaffen, die
gegenüber anderen ungerecht sind. Dabei setzen die Administratoren auch auf eine Selbstreflexion
jedes einzelnen. Im Zweifel sind Spieler dazu angehalten, einen Administrator zu kontaktieren,
der genauer Informationen übermitteln kann.
</li>
<li>
Das Erbauen und Betreiben lag-erzeugender Maschinen, Farmen (Zero-Tick-Farmen etc.) oder andere
Bauten, die den Spielfluss stören könnten, ist verboten. Im Zweifelsfall ist eine Anfrage bei
den Administratoren erwünscht. Bei beispielsweise Farmen oder allgemeinen Redstone Schaltung
sollten diese auch nur dann aktiviert werden, wenn nötig. Außerdem sollten überdimensional große
Villager-Baukomplexe nur in Absprache mit Administratoren errichtet und betreiben werden.
Selbstverständlich ist das Erbauen von Farmen ein essenzieller Bestandteil des Projekts und
stellt auch kein Problem dar, solange die oben genannten Bedingungen eingehalten werden.
</li>
<li>
Das Verkaufen von Items ist allgemein jedem Spieler überall gestattet. Jedoch bietet es sich an
und ist wünschenswert, die Shops aller Spieler in einem Shoppingdistrict gemeinsam anzusiedeln,
um die Interaktion zu fördern. Ein Shop muss sich innerhalb des ausgewiesenen Bereiches befinden
und muss ebenso über das dort bestehende Wegenetz erreichbar sein. Der Shoppingdistrict ist
ausschließlich zum Bauen von Shops vorgesehen. Mehrere Shops zu einem bestimmten Item sind
möglich und auch erwünscht. Diebstahl im Shoppingdistrict untersteht denselben Strafen wie
allgemeiner Diebstahl. Ein angemessener Abstand der privaten Strukturen vom Shoppingdisrict ist
einzuhalten.
</li>
<li>
Das Abstecken bestimmter Gebiete ist grundsätzlich erlaubt, jedoch sind unangemessen große
Grundstücke untersagt. Das maximale Maß ist im Einzelfall zu entscheiden. Die Grenzen bereits
abgesteckter Grundstücke sind unveränderlich. Im Fall von Inaktivität oder andere Beschwerden
beziehungsweise Verstöße ist ein Administrator zu kontaktieren.
</li>
<li>
Wie bereits angedeutet, ist das Ziel ein Umgang untereinander, der für keinen negative Aspekte
beinhaltet. So ist es beispielsweise nicht gestattet, andere zu bestehlen oder ohne Erlaubnis
Bauwerke anderer zu verändern. Dabei liegt natürlich eine gewisse Toleranzbereitschaft vor und
ein Spielraum, der allerdings nicht überschritten werden darf. Dies gilt sowohl für das
Bestehlen anderer als auch für Griefing (zerstören von Bauten anderer ohne Erlaubnis etc.).
Außerdem ist das Töten anderer ohne nachvollziehbaren Grund verboten. Natürlich ist auch hier
eine Bewertung jeder einzelnen Situation notwendig, weshalb eine Verallgemeinerung hier bewusst
nicht angeführt wird. Fest steht jedoch, dass klar zwischen einem Töten aus Spaß mit geringen
Folgen und einem mehrmaligen - ja sogar permanenten Töten anderer mit schlimmeren Folgen,
unterschieden wird.
</li>
<li>
Allgemein liegt es in der Hand der Administratoren einzelne Situation zu bewerten, Strafen zu
verhängen und Entscheidungen zu treffen. Ein internes Strikesystem der Administratoren sorgt für
eine Gleichberechtigung aller Spieler. Des Weiteren erfolgt eine Absprache unter den
Administratoren, um alle Sichtweisen miteinzubringen. Wichtig ist zusätzlich, dass alle
Entscheidungen der Administratoren im Sinne des Projekts getroffen werden. Den Entscheidungen
und Anweisungen der Administratoren ist stets Folge zu leisten, wenn diese als Administrator
fungieren. Im normalen Spielbetrieb sind sie normale Mitspieler ohne spielentscheidende
Sonderrechte. So ist es nicht ihre Aufgabe überall nach dem Rechten zu sehen, sondern
Ansprechpartner zu sein, um dann nach der Vorlegung eines Problems durch einen Geschädigten die
Administratorenrolle einzunehmen und dementsprechend zu handeln. In dem Feld ist einzutragen,
wobei die Regeln trotzdem bis zum Ende gelesen werden müssen. Allgemein gilt immer der
Grundsatz, dass ein Eingriff der Administratoren nur dann erfolgt, wenn dies die Spieler auch
fordern. Solange beide Parteien zufrieden sind, passiert natürlich auch nichts. Wenn also
beispielsweise zwei Spieler ein bewusstes pvp-Duell starten, zieht das logischer Weise keine
Konsequenzen nach sich.
</li>
<li>
Jedem Teilnehmer ist es möglich sich an den Support/das Administratoren-Team zu wenden. Zu den
Administratoren gehören die Spieler, die auf dem Server mit einem Admin-Tag versehen sind. Zwei
von diesen sind außerdem Administrator der WhatsApp-Gruppe. Eine Kontaktaufnahme ist direkt auf
dem Server im Chat oder auf dem Teamspeak: „mhsl.eu“ möglich. Außerdem können sie über WhatsApp
angeschrieben werden, wenn sich z.B. gerade kein Administrator auf dem Server befindet oder bei
anderen Rückfragen. Bei Unzufriedenheit, Meldung eines Regelverstoßen, Anregungen oder Fragen
steht das Administratoren-Team allen Spielern jederzeit zu Verfügung.
</li>
<li>
Konflikte sollen grundlegend zuerst auf einer Ebene zwischen den Spielern geschlichtet werden,
bevor ein Administrator kontaktiert wird. Jeder Regelverstoß zieht unterschiedliche Folgen nach
sich, die von Ermahnungen, über Tagesbänne bis zum permanenten Bann führen können. Diese
möglichen Konsequenzen sind von allen Teilnehmern zu akzeptieren.
</li>
</ol>
<p>
Alle aufgeführten Regeln und die damit in Verbindung stehende Angaben erfolgen ohne Gewähr auf
Vollständigkeit, Richtigkeit und Aktualität. Das Durchsetzen der Regeln liegt im Ermessen der
Administratoren, die vorher in Absprache mit dem Geschädigten eine der Situation angemessene
Maßnahmen getroffen haben.
</p>
<style lang="postcss">
li {
@apply mb-2;
}
</style>