+ {/if}
+
+
diff --git a/src/components/admin/repo/event.ts b/src/components/admin/repo/event.ts
new file mode 100644
index 0000000..e14b6b1
--- /dev/null
+++ b/src/components/admin/repo/event.ts
@@ -0,0 +1,96 @@
+import type {ExtendedEvent, ShortEvent, SWEvent} from "../types/event.js";
+import {fetchWithToken} from "./repo.js";
+import type {Moment} from "moment";
+import {ExtendedEventSchema, ShortEventSchema, SWEventSchema} from "../types/event.js";
+import {z} from "zod";
+
+export interface CreateEvent {
+ name: string
+ start: Moment
+ end: Moment
+}
+
+export interface UpdateEvent {
+ name: string
+ start: Moment
+ end: Moment
+ deadline: Moment
+ maxTeamMembers: number
+ schemType: string | null
+ publicSchemsOnly: boolean
+ spectateSystem: boolean
+}
+
+export class EventRepo {
+ constructor(private token: string) {}
+
+ public async listEvents(): Promise {
+ const res = await fetchWithToken(this.token, "/events");
+
+ if (res.ok) {
+ return z.array(ShortEventSchema).parse(await res.json());
+ } else {
+ throw new Error("Could not fetch events: " + res.statusText);
+ }
+ }
+
+ public async getEvent(id: string): Promise {
+ const res = await fetchWithToken(this.token, `/events/${id}`);
+
+ if (res.ok) {
+ return ExtendedEventSchema.parse(await res.json());
+ } else {
+ throw new Error("Could not fetch event: " + res.statusText);
+ }
+ }
+
+ public async createEvent(event: CreateEvent): Promise {
+ const res = await fetchWithToken(this.token, "/events", {
+ method: "POST",
+ body: JSON.stringify({
+ name: event.name,
+ start: +event.start,
+ end: +event.end
+ }),
+ });
+
+ if (res.ok) {
+ return SWEventSchema.parse(await res.json());
+ } else {
+ throw new Error("Could not create event: " + res.statusText);
+ }
+ }
+
+ public async updateEvent(id: string, event: UpdateEvent): Promise {
+ const res = await fetchWithToken(this.token, `/events/${id}`, {
+ method: "PUT",
+ body: JSON.stringify({
+ name: event.name,
+ start: +event.start,
+ end: +event.end,
+ deadline: +event.deadline,
+ maxTeamMembers: event.maxTeamMembers,
+ schemType: event.schemType,
+ publicSchemsOnly: event.publicSchemsOnly,
+ spectateSystem: event.spectateSystem
+ }),
+ headers: {
+ "Content-Type": "application/json"
+ }
+ });
+
+ if (res.ok) {
+ return SWEventSchema.parse(await res.json());
+ } else {
+ throw new Error("Could not update event: " + res.statusText);
+ }
+ }
+
+ public async deleteEvent(id: string): Promise {
+ const res = await fetchWithToken(this.token, `/events/${id}`, {
+ method: "DELETE"
+ });
+
+ return res.ok;
+ }
+}
diff --git a/src/components/admin/repo/fight.ts b/src/components/admin/repo/fight.ts
new file mode 100644
index 0000000..1a6cc5e
--- /dev/null
+++ b/src/components/admin/repo/fight.ts
@@ -0,0 +1,92 @@
+import type {EventFight} from "../types/event.js";
+import {fetchWithToken} from "./repo.js";
+import type {Moment} from "moment";
+import {z} from "zod";
+import {EventFightSchema} from "../types/event.js";
+
+export interface CreateFight {
+ spielmodus: string
+ map: string
+ blueTeam: number
+ redTeam: number
+ start: Moment
+ kampfleiter: number | null
+ group: string | null
+}
+
+export interface UpdateFight {
+ spielmodus: string | null
+ map: string | null
+ blueTeam: number | null
+ redTeam: number | null
+ start: Moment | null
+ kampfleiter: number | null
+ group: string | null
+}
+
+export class FightRepo {
+ constructor(private token: string) {}
+
+ public async listFights(eventId: number): Promise {
+ const res = await fetchWithToken(this.token, `/events/${eventId}/fights`);
+
+ if (res.ok) {
+ return z.array(EventFightSchema).parse(await res.json());
+ } else {
+ throw new Error("Could not fetch fights: " + res.statusText);
+ }
+ }
+
+ public async createFight(eventId: number, fight: CreateFight): Promise {
+ let res = await fetchWithToken(this.token, `/fights`, {
+ method: "POST",
+ body: JSON.stringify({
+ event: eventId,
+ spielmodus: fight.spielmodus,
+ map: fight.map,
+ blueTeam: fight.blueTeam,
+ redTeam: fight.redTeam,
+ start: +fight.start,
+ kampfleiter: fight.kampfleiter,
+ group: fight.group
+ })
+ })
+
+ if (res.ok) {
+ return EventFightSchema.parse(await res.json());
+ } else {
+ throw new Error("Could not create fight: " + res.statusText);
+ }
+ }
+
+ public async updateFight(fightId: number, fight: UpdateFight): Promise {
+ let res = await fetchWithToken(this.token, `/fights/${fightId}`, {
+ method: "PUT",
+ body: JSON.stringify({
+ spielmodus: fight.spielmodus,
+ map: fight.map,
+ blueTeam: fight.blueTeam,
+ redTeam: fight.redTeam,
+ start: fight.start?.valueOf(),
+ kampfleiter: fight.kampfleiter,
+ group: fight.group
+ })
+ })
+
+ if (res.ok) {
+ return EventFightSchema.parse(await res.json());
+ } else {
+ throw new Error("Could not update fight: " + res.statusText);
+ }
+ }
+
+ public async deleteFight(fightId: number): Promise {
+ let res = await fetchWithToken(this.token, `/fights/${fightId}`, {
+ method: "DELETE"
+ })
+
+ if (!res.ok) {
+ throw new Error("Could not delete fight: " + res.statusText);
+ }
+ }
+}
diff --git a/src/components/admin/repo/perms.ts b/src/components/admin/repo/perms.ts
new file mode 100644
index 0000000..0f354c9
--- /dev/null
+++ b/src/components/admin/repo/perms.ts
@@ -0,0 +1,57 @@
+import type {Perms, UserPerms} from "../types/perms.js";
+import {fetchWithToken} from "./repo.js";
+import {PermsSchema, UserPermsSchema} from "../types/perms.js";
+
+export class PermsRepo {
+ constructor(private token: string) {}
+
+ public async listPerms(): Promise {
+ const res = await fetchWithToken(this.token, "/perms");
+
+ if (res.ok) {
+ return PermsSchema.parse(await res.json());
+ } else {
+ throw new Error("Could not fetch perms: " + res.statusText);
+ }
+ }
+
+ public async getPerms(userId: number): Promise {
+ const res = await fetchWithToken(this.token, `/perms/user/${userId}`);
+
+ if (res.ok) {
+ return UserPermsSchema.parse(await res.json());
+ } else {
+ throw new Error("Could not fetch perms: " + res.statusText);
+ }
+ }
+
+ public async setPrefix(userId: number, prefix: string): Promise {
+ const res = await fetchWithToken(this.token, `/perms/user/${userId}/prefix/${prefix}`, {
+ method: "PUT",
+ });
+
+ if (!res.ok) {
+ throw new Error("Could not set prefix: " + res.statusText);
+ }
+ }
+
+ public async addPerm(userId: number, perm: string): Promise {
+ const res = await fetchWithToken(this.token, `/perms/user/${userId}/${perm}`, {
+ method: "PUT",
+ });
+
+ if (!res.ok) {
+ throw new Error("Could not add perm: " + res.statusText);
+ }
+ }
+
+ public async removePerm(userId: number, perm: string): Promise {
+ const res = await fetchWithToken(this.token, `/perms/user/${userId}/${perm}`, {
+ method: "DELETE",
+ });
+
+ if (!res.ok) {
+ throw new Error("Could not remove perm: " + res.statusText);
+ }
+ }
+}
diff --git a/src/components/admin/repo/repo.ts b/src/components/admin/repo/repo.ts
new file mode 100644
index 0000000..2dbf045
--- /dev/null
+++ b/src/components/admin/repo/repo.ts
@@ -0,0 +1,15 @@
+import {derived, writable} from "svelte/store";
+import {EventRepo} from "./event.js";
+import {FightRepo} from "./fight.js";
+import {PermsRepo} from "./perms.js";
+
+export { EventRepo } from "./event.js"
+
+export const fetchWithToken = (token: string, url: string, params: RequestInit = {}) => fetch(`https://steamwar.de/eventplanner-api${url}`, {...params, headers: {"X-SW-Auth": token, "Content-Type": "application/json", ...params.headers}});
+
+export const tokenStore = writable(localStorage.getItem("sw-api-token") ?? "")
+tokenStore.subscribe((value) => localStorage.setItem("sw-api-token", value))
+
+export const eventRepo = derived(tokenStore, ($token) => new EventRepo($token))
+export const fightRepo = derived(tokenStore, ($token) => new FightRepo($token))
+export const permsRepo = derived(tokenStore, ($token) => new PermsRepo($token))
diff --git a/src/components/admin/stores/cached.ts b/src/components/admin/stores/cached.ts
new file mode 100644
index 0000000..ba88b26
--- /dev/null
+++ b/src/components/admin/stores/cached.ts
@@ -0,0 +1,63 @@
+import {readonly, writable} from "svelte/store";
+
+import type {Readable, Subscriber, Unsubscriber} from "svelte/store";
+
+export interface Cached extends Readable{
+ reload: () => void;
+}
+
+export function cached(normal: T, init: () => Promise): Cached {
+ const store = writable(normal);
+ let first = true;
+
+ const reload = () => {
+ init().then(data => {
+ store.set(data);
+ });
+ }
+
+ return {
+ ...readonly(store),
+ subscribe: (run: Subscriber, invalidate?: (value?: T) => void): Unsubscriber => {
+ if(first) {
+ first = false;
+ reload();
+ }
+ return store.subscribe(run, invalidate);
+ },
+ reload
+ };
+}
+
+export function cachedFamily(normal: K, init: (T) => Promise): (T) => Cached {
+ const stores: Map> = new Map();
+ return (arg: T) => {
+ if(stores.has(arg)) {
+ return stores.get(arg);
+ } else {
+ const store = writable(normal);
+ let first = true;
+
+ const reload = () => {
+ init(arg).then(data => {
+ store.set(data);
+ });
+ }
+
+ const cachedStore = {
+ ...readonly(store),
+ subscribe: (run: Subscriber, invalidate?: (value?: K) => void): Unsubscriber => {
+ if(first) {
+ first = false;
+ reload();
+ }
+ return store.subscribe(run, invalidate);
+ },
+ reload
+ } as Cached;
+
+ stores.set(arg, cachedStore);
+ return cachedStore;
+ }
+ }
+}
diff --git a/src/components/admin/stores/stores.ts b/src/components/admin/stores/stores.ts
new file mode 100644
index 0000000..100a837
--- /dev/null
+++ b/src/components/admin/stores/stores.ts
@@ -0,0 +1,47 @@
+import type {Player, SchematicType} from "../types/data.js";
+import {PlayerSchema} from "../types/data.js";
+import {cached, cachedFamily} from "./cached.js";
+import type {Team} from "../types/team.js";
+import {TeamSchema} from "../types/team.js";
+import {get, writable} from "svelte/store";
+import {permsRepo, tokenStore} from "../repo/repo.js";
+import {z} from "zod";
+
+export const schemTypes = cached([], () => {
+ return fetch("https://steamwar.de/eventplanner-api/data/schematicTypes", {headers: {"X-SW-Auth": get(tokenStore)}})
+ .then(res => res.json())
+})
+
+export const players = cached([], async () => {
+ const res = await fetch("https://steamwar.de/eventplanner-api/data/users", {headers: {"X-SW-Auth": get(tokenStore)}});
+ return z.array(PlayerSchema).parse(await res.json());
+})
+
+export const gamemodes = cached([], async () => {
+ const res = await fetch("https://steamwar.de/eventplanner-api/data/gamemodes", {headers: {"X-SW-Auth": get(tokenStore)}});
+ return z.array(z.string()).parse(await res.json());
+})
+
+export const maps = cachedFamily([], async (gamemode) => {
+ if (get(gamemodes).every(value => value !== gamemode)) return [];
+
+ const res = await fetch(`https://steamwar.de/eventplanner-api/data/gamemodes/${gamemode}/maps`, {headers: {"X-SW-Auth": get(tokenStore)}});
+ if (!res.ok) {
+ return [];
+ } else {
+ return res.json();
+ }
+})
+
+export const groups = cached([], async () => {
+ const res = await fetch("https://steamwar.de/eventplanner-api/data/groups", {headers: {"X-SW-Auth": get(tokenStore)}});
+ return z.array(z.string()).parse(await res.json());
+})
+
+export const teams = cached([], async () => {
+ const res = await fetch("https://steamwar.de/eventplanner-api/team", {headers: {"X-SW-Auth": get(tokenStore)}});
+ return z.array(TeamSchema).parse(await res.json());
+})
+
+export const isWide = writable(window.innerWidth >= 640);
+window.addEventListener("resize", () => isWide.set(window.innerWidth >= 640));
diff --git a/src/components/admin/types/data.ts b/src/components/admin/types/data.ts
new file mode 100644
index 0000000..a14757a
--- /dev/null
+++ b/src/components/admin/types/data.ts
@@ -0,0 +1,16 @@
+import {z} from "zod";
+
+export const SchematicTypeSchema = z.object({
+ name: z.string(),
+ db: z.string(),
+})
+
+export type SchematicType = z.infer;
+
+export const PlayerSchema = z.object({
+ id: z.number(),
+ name: z.string(),
+ uuid: z.string(),
+})
+
+export type Player = z.infer;
diff --git a/src/components/admin/types/event.ts b/src/components/admin/types/event.ts
new file mode 100644
index 0000000..523ce98
--- /dev/null
+++ b/src/components/admin/types/event.ts
@@ -0,0 +1,46 @@
+import type {Team} from "./team.js";
+import type {Player} from "./data.js";
+import {z} from "zod";
+import {TeamSchema} from "./team.js";
+import {PlayerSchema} from "./data.js";
+
+export const ShortEventSchema = z.object({
+ id: z.number(),
+ name: z.string(),
+ start: z.number(),
+ end: z.number(),
+})
+
+export type ShortEvent = z.infer;
+
+export const SWEventSchema = ShortEventSchema.extend({
+ deadline: z.number(),
+ maxTeamMembers: z.number(),
+ schemType: z.string().nullable(),
+ publicSchemsOnly: z.boolean(),
+ spectateSystem: z.boolean(),
+})
+
+export type SWEvent = z.infer;
+
+export const EventFightSchema = z.object({
+ id: z.number(),
+ spielmodus: z.string(),
+ map: z.string(),
+ blueTeam: TeamSchema,
+ redTeam: TeamSchema,
+ kampfleiter: PlayerSchema.nullable(),
+ start: z.number(),
+ ergebnis: z.number(),
+ group: z.string().nullable(),
+})
+
+export type EventFight = z.infer;
+
+export const ExtendedEventSchema = z.object({
+ event: SWEventSchema,
+ teams: z.array(TeamSchema),
+ fights: z.array(EventFightSchema),
+})
+
+export type ExtendedEvent = z.infer;
diff --git a/src/components/admin/types/perms.ts b/src/components/admin/types/perms.ts
new file mode 100644
index 0000000..6f88335
--- /dev/null
+++ b/src/components/admin/types/perms.ts
@@ -0,0 +1,23 @@
+import {z} from "zod";
+
+export const PrefixSchema = z.object({
+ name: z.string().startsWith("PREFIX_"),
+ colorCode: z.string().length(2).startsWith("§"),
+ chatPrefix: z.string()
+})
+
+export type Prefix = z.infer;
+
+export const PermsSchema = z.object({
+ perms: z.array(z.string()),
+ prefixes: z.record(PrefixSchema),
+})
+
+export type Perms = z.infer;
+
+export const UserPermsSchema = z.object({
+ prefix: PrefixSchema,
+ perms: z.array(z.string()),
+})
+
+export type UserPerms = z.infer;
diff --git a/src/components/admin/types/team.ts b/src/components/admin/types/team.ts
new file mode 100644
index 0000000..ee2b4ab
--- /dev/null
+++ b/src/components/admin/types/team.ts
@@ -0,0 +1,12 @@
+import {z} from "zod";
+
+export const TeamSchema = z.object({
+ id: z.number(),
+ name: z.string(),
+ kuerzel: z.string().min(1).max(4),
+ color: z.string().max(1),
+})
+
+export type Team = z.infer;
+
+
diff --git a/src/components/admin/util.ts b/src/components/admin/util.ts
new file mode 100644
index 0000000..d5cfc1e
--- /dev/null
+++ b/src/components/admin/util.ts
@@ -0,0 +1,49 @@
+import Color from "color";
+import type {Team} from "./types/team.js";
+
+export const capitalize = (str: string) => str.charAt(0).toUpperCase() + str.slice(1);
+
+export function colorFromTeam(team: Team): string {
+ switch (team.color) {
+ case "1":
+ return "#0000AA";
+ case "2":
+ return "#00AA00";
+ case "3":
+ return "#00AAAA";
+ case "4":
+ return "#AA0000";
+ case "5":
+ return "#AA00AA";
+ case "6":
+ return "#FFAA00";
+ case "7":
+ return "#AAAAAA";
+ case "8":
+ return "#555555";
+ case "9":
+ return "#5555FF";
+ case "a":
+ return "#55FF55";
+ case "b":
+ return "#55FFFF";
+ case "c":
+ return "#FF5555";
+ case "d":
+ return "#FF55FF";
+ case "e":
+ return "#FFFF55";
+ case "f":
+ return "#FFFFFF";
+ default:
+ return "#000000";
+ }
+}
+
+export function lighten(color: string) {
+ return brightness(color) ? Color(color).lighten(0.2).hex() : Color(color).darken(0.2).hex()
+}
+
+export function brightness(color: string) {
+ return Color(color).isLight()
+}
diff --git a/src/content/config.ts b/src/content/config.ts
new file mode 100644
index 0000000..fb699a5
--- /dev/null
+++ b/src/content/config.ts
@@ -0,0 +1,14 @@
+// @ts-ignore
+import { defineCollection, z } from 'astro:content';
+
+export const pages = defineCollection({
+ type: "content",
+ schema: z.object({
+ title: z.string().min(1).max(80),
+ description: z.string().min(1).max(120),
+ })
+})
+
+export const collections = {
+ 'pages': pages
+}
diff --git a/src/content/pages/en/faq.md b/src/content/pages/en/faq.md
new file mode 100644
index 0000000..d8b79e8
--- /dev/null
+++ b/src/content/pages/en/faq.md
@@ -0,0 +1,144 @@
+---
+title: FAQ
+description: Frequently asked questions
+slug: faq
+---
+
+# Server/ IP-Adresse
+
+#### Was ist die IP? Wie kann ich auf den Server joinen?
+Steamwar.de
+
+#### Mit welcher Version kann ich auf den Server joinen?
+SteamWar läuft auf der 1.19.4, du kannst aber mit den Versionen 1.12.x -1.19.4 joinen.
+
+#### Kann ich auch mit der Bedrock-Version joinen?
+Ja, über die IP: steamwar.de
+
+# Allgemeines
+
+#### Wo finde ich die Verhaltensrichtlinien/ Serverregeln
+Über diese Website unter „Startseite“ oder indem du hier drauf klickst
+
+# Minigames/ Fights
+
+#### Wie kann ich Kämpfe starten?
+Mit „/fight“ kannst du Kämpfe mit dem aktuellen WarGear Regelwerk und der WG Submodi starten. Du kannst aber auch die Lobby-Portale nutzen, indem du entweder durch die großen Portale am Spawn gehst, oder im Teleporter auf „Arenen“ drückst, und dann dort über die Portale eine Runde startest.
+
+#### Wie kann ich Kämpfe mit alten Regelwerken starten?
+Mit „/historic“ kannst du alle historischen Spielmodi spielen, bis zur Version 1.7.
+
+#### Wie kann ich gegen einen bestimmten Spieler kämpfen?
+Mit „/challenge“ kannst du den gewählten Spieler herausfordern. So können auch z. B. Private MissileWars Runden gestartet werden, in der jeder Spieler eingeladen werden muss, um teilnehmen zu können. Der Challenge-Befehl funktioniert nicht mit historischen Spielmodi.
+
+#### Was sind rotierende Modi?
+Für manche Modi gibt es nicht genug Nachfrage, um eine dauerhafte Aufnahme dieser in die regulär spielbaren Modi zu rechtfertigen. Diese Modi kann man daher alle paar Monate spielen und werden allgemein als rotierende Modi bezeichnet. Es steht immer ein rotierender Modus zur Verfügung, und dieser wechselt ungefähr jeden Monat. Zu den rotierenden Modi gehören z. B. MicroWarGear und MegaWarGear.
+
+#### Wie kann ich anderen Kämpfen beitreten?
+Entweder klickst du auf die entsprechende Nachricht im Chat, trittst einer Arena mit „/arena [arena]“ bei, oder folgst einem Spieler mit „/join [spieler]“
+
+#### Nach welchen Regeln muss ich meine Kampfmaschinen bauen?
+Die Regelwerke findest du hier auf der Website unter dem Menüpunkt „Spielmodi“
+
+# Bauserver
+
+#### Was ist ein Bauserver?
+Ein Bauserver ist ein privater Server, auf welchem Redstoneschaltungen, Kanonen und Schematics für unsere Spielmodi im Kreativmodus mit einer großen Liste an Hilfsmitteln gebaut werden können.
+
+#### Wie kann ich meinen Bauserver starten?
+Entweder mit „/bau“, „/b“ oder „/build“. Solltest du auf einen Bau einer anderen Spielversion wollen, benutze „/b [version]“.
+
+#### Wie lasse ich andere auf meinen Bau?
+Entweder über „/b addmember [spieler]“ oder klicke auf die Nachricht im Chat, wenn sie versuchen deinem Bau per command beizutreten. Du wirst sie erkennen, wenn sie erscheint.
+
+#### Wie trete ich anderen Baus bei?
+Entweder über „/b tp [spieler]“ oder „/join [spieler]“, wenn du weißt, dass er sich auf seinem Bau befindet. Solltest du auf den Bau einer anderen Spielversion wollen, gebe „/b tp [spieler] [version]“ ein.
+
+#### Wie gebe ich anderen Spielern Rechte?
+Für Worldrechte (Regionsverwaltung) „/b toggleworld [spieler]“
+
+Für Worldedit „/b togglewe [spieler]“
+
+#### Wie kann ich die verschiedenen Funktionen meines Baus nutzen?
+Am Anfang ist es am einfachsten, das Bau-GUI zu nutzen. Das geht entweder über „/gui“ oder indem du deine Taste zum Hand wechseln zweimal schnell hintereinander drückst (standardmäßig „F“)
+
+#### Wie schreibe ich im lokalen Chat bzw. im Bau Chat?
+„/bc [Nachricht]“, „/local [Nachricht]“ oder „+ [Nachricht]“
+
+# Schematics
+
+#### Wo kann ich meine Schematics hochladen?
+Entweder über https://steamwar.de/startseite/schematicupload/
+ Oder schicke unserem Discord Bot (Steamwar Bot#9952) die Schematic per Privatnachricht. Dafür musst du deinen Minecraft und Discord Account bei uns verknüpft haben.
+
+#### Wie kann ich meine Schematics herunterladen?
+Über die ingame Download Funktion im Schem-GUI „//schem gui“, und „//schem download [schematic]“,
+
+#### Wie kann ich meine Schematic auf die Arena bringen/ freigeben lassen?
+„//schem changetype [schemname] [Typ]“ oder wenn du dich mit den Typen nicht auskennst „//schem changetype [schemname]“. Danach muss die Schematic von einem Supporter oder Moderator geprüft werden.
+
+#### Wie erfahre ich den Grund für die Ablehnung meiner Schematic?
+Wenn du zum Zeitpunkt der Ablehnung offline warst „//schem search [schemname]“
+
+Wenn du zum Zeitpunkt der Ablehnung online bist, steht eine Nachricht im Chat.
+
+Über eine Privatnachricht vom SteamWar Discord Bot. Verbinde dafür deinen Minecraft und Discord Account (Informationen dazu unter dem Punkt „Discord“)
+
+#### Wie erstelle ich eine Schematic/ speichere ich mein WarGear, WarShip usw.?
+Gehe auf die Region mit deinem Bauwerk. Gebe nun „/select build“ ein. Stelle dich jetzt mittig vor den Baubereich (normalerweise der gelbe Glasblock), und gebe „//copy“ ein. Jetzt kannst du dein Bauwerk mit „//schem save [schemname]“ speichern.
+
+Gib „//wand“ ein, nimm das Item in die Hand und wähle mit Linksklick bspw. die untere linke Ecke des Bereichs aus, in welchem sich dein Bauwerk befindet, und wähle dann mit Rechtsklick die schräg gegenüberliegende Ecke aus. Alternativ kannst du die Ecken auch mit „//pos1“ und „//pos2“ auswählen. Dafür müssen dann die Füße deines Charakters in der jeweiligen Ecke bei Senden des Commands befinden. Jetzt sollte sich, wenn du das Item in der Hand hältst, dein Bauwerk innerhalb eines Rahmens aus Partikeln befinden. Sollte dies nicht der Fall sein, passe die Ecken an, bis sich dein Bauwerk in dem Rahmen befindet. Gehe nun an die Stelle, wo du dein Bauwerk kopieren möchtest, was üblicherweise mittig auf dem Boden vor diesem ist (der gelbe Glasblock im Boden). Gebe jetzt „//copy“ ein. Um das Ganze letztendlich zu speichern, gebe „//schem save [schemname]“ ein.
+
+#### Wie sende ich ein Public ein?
+Informiere ein entsprechendes Ratsmitglied (einsehbar auf unserem Discord Server) und füge alle Mitglieder des entsprechenden Rates auf die Schem hinzu. Danach wird der Antrag innerhalb des Rates diskutiert. Sollte der Rat dem Antrag zustimmen, muss ein Admin entscheiden, ob es Public wird. Wenn ja, wirst du aufgefordert alle wieder von der Schematic zu entfernen und es wird zur Public Schem geändert.
+
+# Discord
+
+#### Wo finde ich den Discord Server?
+[steamwar.de/discord](https://steamwar.de/discord)
+
+#### Wo finde ich den Social Media Discord Server?
+[Discord](https://discord.gg/PvXFsRvZfB)
+
+#### Wie verbinde ich meinen Discord und Minecraft Account?
+Klicke in dem Discord Channel „#「👮」regel-infos“ auf den Button „Minecraft verknüpfen“. Folge dann den von dem SteamWar Bot angewiesenen Schritten.
+
+# Support
+
+#### Wie öffne ich ein Support Ticket?
+* Auf unserem Discord-Server über den Channel „#「❓」support“. Dort kannst du Fragen stellen, Bugs und Spieler melden, sowie Features vorschlagen und Entbannungsanträge stellen.
+
+#### Wie melde ich schnell einen Bug?
+„/bug [beschreibung des bugs]“
+
+# Events
+
+#### Wie kann ich mich für ein Event anmelden?
+„/team event [eventname]“ oder, wenn du deinen Minecraft Account mit deinem Discord Accout verknüpft hast, über den Discord Channel „#「📣」events“ auf unserem Discord-Server.
+
+#### Kann ich mit zwei Accounts gleichzeitig teilnehmen?
+Nein
+
+#### Kann ich während eines Events noch Spieler in mein Team einladen?
+Nein. Während eines laufenden Events sind Neuaufnahmen in ein Team nicht möglich.
+
+# Forum
+
+#### Wo finde ich das Forum?
+Auf unserem Discord Server unter der Kategorie „Forum“
+
+# Website
+
+#### Wie melde ich mich auf der Website an?
+Tritt unserem Minecraft Server bei, und gebe „/webpw [passwort]“ ein
+
+# Teams
+
+#### Wie trete ich einem Team bei?
+Mit „/team join [Team]“. Mit „/team“ siehst du alle weiteren Commands.
+
+# Sonstiges
+
+#### Wo erhalte ich weitere Hilfe?
+Mit „/tutorial“ kannst du dir Tutorial-Welten von Spielern ansehen.
+
diff --git a/src/content/pages/en/join.md b/src/content/pages/en/join.md
new file mode 100644
index 0000000..cabb3ac
--- /dev/null
+++ b/src/content/pages/en/join.md
@@ -0,0 +1,5 @@
+---
+title: Join Now!
+description: How to join SteamWar.de
+slug: join
+---
diff --git a/src/env.d.ts b/src/env.d.ts
index 29be4a1..1e58cf7 100644
--- a/src/env.d.ts
+++ b/src/env.d.ts
@@ -1,3 +1,4 @@
+///
///
///
diff --git a/src/layouts/Basic.astro b/src/layouts/Basic.astro
index 411fd28..5d4a7b6 100644
--- a/src/layouts/Basic.astro
+++ b/src/layouts/Basic.astro
@@ -4,18 +4,19 @@ import {astroI18n} from "astro-i18n";
const { title, description } = Astro.props.frontmatter || Astro.props;
---
-
+
+
{title}
-
+
diff --git a/src/layouts/NavbarLayout.astro b/src/layouts/NavbarLayout.astro
index e89554f..77f6c5b 100644
--- a/src/layouts/NavbarLayout.astro
+++ b/src/layouts/NavbarLayout.astro
@@ -11,25 +11,61 @@ const { title } = Astro.props;
-