54 Commits

Author SHA1 Message Date
a2456c8b46 Merge remote-tracking branch 'origin/master'
All checks were successful
SteamWarCI Build successful
2025-03-31 22:28:54 +02:00
0952035091 Add shop.md 2025-03-31 22:28:51 +02:00
Lixfel
9c8c02f679 Update wgs25-kampfplan.md
All checks were successful
SteamWarCI Build successful
2025-03-31 19:43:24 +02:00
Lixfel
3b5fdc57c0 Update wgs25-kampfplan.md
All checks were successful
SteamWarCI Build successful
2025-03-25 06:40:14 +01:00
Lixfel
733c63946f Update wgs25-kampfplan.md
All checks were successful
SteamWarCI Build successful
2025-03-25 06:32:35 +01:00
fd846250ab Merge pull request 'Addapted script side for new example hotkey script' (#8) from add-example-script-to-downloads into master
All checks were successful
SteamWarCI Build successful
Reviewed-on: #8
Reviewed-by: Chaoscaot <max@chaoscaot.de>
2025-03-22 10:36:18 +01:00
D4rkr34lm
17460772e9 Addapted script side for new example hotkey script
All checks were successful
SteamWarCI Build successful
2025-03-21 18:54:26 +01:00
Lixfel
9a20860072 Update wgs25-kampfplan.md
All checks were successful
SteamWarCI Build successful
2025-03-18 06:48:53 +01:00
8f51723a3b Fix Graf Spee name
All checks were successful
SteamWarCI Build successful
2025-03-14 13:49:36 +01:00
8ad2f283aa Fixup things
All checks were successful
SteamWarCI Build successful
2025-03-13 16:30:12 +01:00
39f1af8b73 Add MissileWars.md
All checks were successful
SteamWarCI Build successful
2025-03-13 16:27:48 +01:00
266c4cb4ea Add MissileWars ranking to Navbar.svelte
All checks were successful
SteamWarCI Build successful
2025-03-13 16:24:45 +01:00
f3df3c0000 Add ranked to Micro WarGear
All checks were successful
SteamWarCI Build successful
2025-03-13 16:05:26 +01:00
Lixfel
cb78fc598b Update wgs25-kampfplan.md
All checks were successful
SteamWarCI Build successful
2025-03-09 16:52:42 +01:00
ba7ecc1a8e Merge remote-tracking branch 'origin/master'
All checks were successful
SteamWarCI Build successful
2025-03-04 23:33:17 +01:00
6ea92f9383 Tracking and adding LFS artifacts 2025-03-04 23:33:00 +01:00
Lixfel
998770bf59 Update wgs25-kampfplan.md
All checks were successful
SteamWarCI Build successful
2025-03-04 07:24:58 +01:00
a231032555 Add and standardize MissileWars translations and links
All checks were successful
SteamWarCI Build successful
2025-03-02 16:21:43 +01:00
3aa3731bcb Add MissileWars mode config and leaderboard link
All checks were successful
SteamWarCI Build successful
2025-03-02 16:18:37 +01:00
5e80c95bfd Add download link and update source URL in teamserver.json
All checks were successful
SteamWarCI Build successful
2025-03-02 16:12:47 +01:00
Lixfel
09dc28b6da Update wgs25-kampfplan.md
All checks were successful
SteamWarCI Build successful
2025-03-02 15:07:14 +01:00
Lixfel
fd7cf716ca Update wgs25-kampfplan.md
All checks were successful
SteamWarCI Build successful
2025-03-01 22:08:23 +01:00
73bd6a5e96 Fix Website
All checks were successful
SteamWarCI Build successful
2025-03-01 20:07:09 +01:00
9c02cc1f4d Fix Website
All checks were successful
SteamWarCI Build successful
2025-03-01 20:04:41 +01:00
de8457fe45 Fix Website
All checks were successful
SteamWarCI Build successful
2025-03-01 20:03:04 +01:00
bccd5eb5a0 Enhance request handling with token refresh and retries
All checks were successful
SteamWarCI Build successful
2025-03-01 11:55:01 +01:00
53afe70b27 Refactor token refresh logic to streamline error handling.
All checks were successful
SteamWarCI Build successful
2025-03-01 11:34:09 +01:00
4bbdaa06a9 Refactor auth handling to improve token refresh logic
All checks were successful
SteamWarCI Build successful
2025-03-01 11:30:30 +01:00
f03867b9a7 Add retry mechanism and limit for token requests
All checks were successful
SteamWarCI Build successful
2025-03-01 11:27:06 +01:00
23e10eef0f Fix group filtering logic in FightTable.svelte
All checks were successful
SteamWarCI Build successful
2025-03-01 11:15:39 +01:00
4c72f4f26b FIx MW3 creation date
All checks were successful
SteamWarCI Build successful
2025-03-01 10:52:44 +01:00
624ba7f296 Merge pull request 'Merge branch wgs25-kampfplan' (#7) from wgs25-kampfplan into master
All checks were successful
SteamWarCI Build successful
Reviewed-on: #7
2025-03-01 10:51:19 +01:00
Lixfel
d7d20e4347 Update wgs25-kampfplan.md
All checks were successful
SteamWarCI Build successful
2025-03-01 10:50:17 +01:00
Lixfel
43bd8f4a7c Update wgs25-kampfplan.md
Some checks failed
SteamWarCI Build failed
2025-03-01 10:49:03 +01:00
Lixfel
18e8627b54 Update wgs25-kampfplan.md
Some checks failed
SteamWarCI Build failed
2025-03-01 10:42:19 +01:00
Lixfel
0efc46c7e2 Create page announcements/de/wgs25-kampfplan.md
Some checks failed
SteamWarCI Build failed
2025-03-01 09:53:50 +01:00
62fff0c0b2 Merge pull request 'Refactor authentication and implement password reset.' (#3) from develop/authv2 into master
All checks were successful
SteamWarCI Build successful
Reviewed-on: #3
Reviewed-by: Lixfel <lixfel@noreply.localhost>
2025-02-25 22:39:40 +01:00
TheBreadBeard
86b479fb28 Update missilewars-iii-eventplan.md
All checks were successful
SteamWarCI Build successful
2025-02-23 18:34:53 +01:00
Chaoscaot
489402292d Update adventskalender-schems.md
All checks were successful
SteamWarCI Build successful
2025-02-23 18:31:10 +01:00
b53ce04a75 Remove reset password functionality
All checks were successful
SteamWarCI Build successful
2025-02-23 17:23:45 +01:00
069a9973a4 Add Gitea link and icon to navbar layout
All checks were successful
SteamWarCI Build successful
2025-02-23 15:20:50 +01:00
c3410de1d7 Refactor event handling to use Promises for better efficiency.
All checks were successful
SteamWarCI Build successful
2025-02-23 12:25:56 +01:00
a23c514102 Revert "Refactor event mounts and update script management."
This reverts commit bf8110af6c.
2025-02-23 12:20:34 +01:00
bf8110af6c Refactor event mounts and update script management.
All checks were successful
SteamWarCI Build successful
2025-02-23 12:18:58 +01:00
349f71af1c Add event listener for "astro:before-swap" in slug page
All checks were successful
SteamWarCI Build successful
2025-02-23 12:14:11 +01:00
dda37127ca Use type import and update page load event handling.
All checks were successful
SteamWarCI Build successful
2025-02-23 09:59:37 +01:00
6d210eb0ff Merge pull request 'Merge branch missilewars-iii-eventplan' (#4) from missilewars-iii-eventplan into master
All checks were successful
SteamWarCI Build successful
Reviewed-on: #4
2025-02-23 09:49:04 +01:00
Chaoscaot
cfede8f299 Update missilewars-iii-eventplan.md
All checks were successful
SteamWarCI Build successful
2025-02-23 09:47:30 +01:00
TheBreadBeard
597153ed39 Update missilewars-iii-eventplan.md
All checks were successful
SteamWarCI Build successful
2025-02-23 07:43:22 +01:00
TheBreadBeard
697e903a26 Create page announcements/de/missilewars-iii-eventplan.md
Some checks failed
SteamWarCI Build failed
2025-02-23 07:21:02 +01:00
1433784369 Update auth API endpoints to remove "/v2" prefix
All checks were successful
SteamWarCI Build successful
2025-02-20 22:15:02 +01:00
2c63a33bda Refine token validation and update user stats endpoint.
All checks were successful
SteamWarCI Build successful
Extend access token validation to include a 10-second buffer to prevent potential expiry issues. Modify the user stats API call to use the base `/stats/user` endpoint for improved consistency.
2025-02-18 00:09:06 +01:00
87265e5ccc Add "Repeat Password" label to i18n and form components
All checks were successful
SteamWarCI Build successful
2025-02-17 18:32:54 +01:00
75f1a6528b Refactor authentication and implement password reset.
All checks were successful
SteamWarCI Build successful
2025-02-17 18:29:17 +01:00
41 changed files with 643 additions and 133 deletions

3
.gitattributes vendored Normal file
View File

@@ -0,0 +1,3 @@
.jpg filter=lfs diff=lfs merge=lfs -text
.png filter=lfs diff=lfs merge=lfs -text
.glb filter=lfs diff=lfs merge=lfs -text

View File

@@ -7,7 +7,6 @@ import sitemap from "@astrojs/sitemap";
import robotsTxt from "astro-robots-txt";
import path from "node:path";
import mdx from "@astrojs/mdx";
import pagefind from "astro-pagefind";
// https://astro.build/config
export default defineConfig({
@@ -22,7 +21,6 @@ export default defineConfig({
tailwind({
configFile: "./tailwind.config.cjs",
}),
pagefind(),
configureI18n(),
sitemap({
i18n: {

View File

@@ -20,6 +20,7 @@
<script lang="ts">
import {t} from "astro-i18n";
import {statsRepo} from "@repo/stats.ts";
import "@styles/table.css"
interface Props {
@@ -64,7 +65,3 @@
<p>{error.message}</p>
{/await}
<style>
@import "../styles/table.css";
</style>

View File

@@ -55,7 +55,7 @@
</tr>
</thead>
<tbody>
{#each window(event.fights.filter(f => f.group === group), rows) as fights}
{#each window(event.fights.filter(f => group === undefined ? true : f.group === group), rows) as fights}
<tr>
{#each fights as fight (fight.id)}
<td>{Intl.DateTimeFormat(astroI18n.locale, {

View File

@@ -31,8 +31,7 @@
let error: string = $state("");
async function login() {
let {tokenStore} = await import("./repo/repo.ts");
let {authRepo} = await import("./repo/auth.ts");
let {authV2Repo} = await import("./repo/authv2.ts");
if (username === "" || pw === "") {
pw = "";
error = t("login.error");
@@ -40,15 +39,14 @@
}
try {
let auth = await get(authRepo).login(username, pw);
if (auth == undefined) {
let auth = await get(authV2Repo).login(username, pw);
if (!auth) {
pw = "";
error = t("login.error");
return;
}
tokenStore.set(auth);
navigate(l("/dashboard"));
await navigate(l("/dashboard"));
} catch (e: any) {
pw = "";
error = t("login.error");

View File

@@ -23,24 +23,35 @@
import {t} from "astro-i18n";
import {l} from "../util/util";
import {onMount} from "svelte";
import {loggedIn} from "@repo/authv2.ts";
interface Props {
logo?: import('svelte').Snippet;
}
let { logo }: Props = $props();
let navbar: HTMLDivElement = $state();
let navbar = $state<HTMLDivElement>();
let searchOpen = $state(false);
let accountBtn = $state<HTMLAnchorElement>();
$effect(() => {
if ($loggedIn) {
accountBtn!.href = l("/dashboard");
} else {
accountBtn!.href = l("/login");
}
})
onMount(() => {
handleScroll();
})
function handleScroll() {
if (window.scrollY > 0) {
navbar.classList.add("before:scale-y-100");
navbar!.classList.add("before:scale-y-100");
} else {
navbar.classList.remove("before:scale-y-100");
navbar!.classList.remove("before:scale-y-100");
}
}
</script>
@@ -90,6 +101,8 @@
<a href={l("/rules/microwargear")}
class="btn btn-gray">{t("navbar.links.rules.micro")}</a>
<a href={l("/rules/streetfight")} class="btn btn-gray">{t("navbar.links.rules.sf")}</a>
<h2 class="px-2 text-gray-300">{t("navbar.links.rules.ranked")}</h2>
<a href={l("/rangliste/missilewars")} class="btn btn-gray">{t("navbar.links.ranked.mw")}</a>
</div>
</div>
<!-- TODO: Add help center
@@ -106,7 +119,7 @@
</div>
</div>
-->
<a class="btn" href={l("/login")}>
<a class="btn" href={l("/login")} bind:this={accountBtn}>
<span class="btn__text">{t("navbar.links.account")}</span>
</a>
<!--

View File

@@ -1,5 +1,5 @@
---
import {CollectionEntry} from "astro:content";
import type {CollectionEntry} from "astro:content";
import {l} from "../util/util";
import {astroI18n} from "astro-i18n";
import {Image} from "astro:assets";

View File

@@ -22,38 +22,40 @@
import wrap from "svelte-spa-router/wrap";
import Router, {replace} from "svelte-spa-router";
import {get} from "svelte/store";
import {tokenStore} from "@repo/repo";
import {loggedIn} from "@repo/authv2.ts";
const routes: RouteDefinition = {
"/": wrap({asyncComponent: () => import("./pages/Home.svelte"), conditions: detail => get(tokenStore) != ""}),
"/": wrap({asyncComponent: () => import("./pages/Home.svelte"), conditions: detail => get(loggedIn)}),
"/perms": wrap({
asyncComponent: () => import("./pages/Perms.svelte"),
conditions: detail => get(tokenStore) != ""
conditions: detail => get(loggedIn)
}),
"/login": wrap({
asyncComponent: () => import("./pages/Login.svelte"),
conditions: detail => get(tokenStore) == ""
conditions: detail => !get(loggedIn)
}),
"/event/:id": wrap({
asyncComponent: () => import("./pages/Event.svelte"),
conditions: detail => get(tokenStore) != ""
conditions: detail => get(loggedIn)
}),
"/event/:id/generate": wrap({
asyncComponent: () => import("./pages/Generate.svelte"),
conditions: detail => get(tokenStore) != ""
conditions: detail => get(loggedIn)
}),
"/edit": wrap({
asyncComponent: () => import("./pages/Edit.svelte"),
conditions: detail => get(tokenStore) != ""
conditions: detail => get(loggedIn)
}),
"/display/:event": wrap({
asyncComponent: () => import("./pages/Display.svelte"),
conditions: detail => get(tokenStore) != ""
conditions: detail => get(loggedIn)
}),
"*": wrap({asyncComponent: () => import("./pages/NotFound.svelte")})
};
function conditionsFailed(event: ConditionsFailedEvent) {
console.log(event)
if (event.detail.location === "/login") {
replace("/");
} else {

View File

@@ -22,9 +22,9 @@
import type {Player} from "@type/data.ts";
import {l} from "@utils/util.ts";
import Statistics from "./Statistics.svelte";
import {authRepo} from "@repo/auth.ts";
import {tokenStore} from "@repo/repo.ts";
import {authV2Repo} from "@repo/authv2.ts";
import Card from "@components/Card.svelte";
import {navigate} from "astro:transitions/client";
interface Props {
user: Player;
@@ -33,9 +33,8 @@
let { user }: Props = $props();
async function logout() {
await $authRepo.logout()
tokenStore.set("")
window.location.href = l("/login")
await $authV2Repo.logout();
await navigate(l("/login"));
}
</script>

View File

@@ -1,44 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2023 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {fetchWithToken, tokenStore} from "./repo.ts";
import {derived} from "svelte/store";
export class AuthRepo {
constructor(private token: string) {
}
public async login(username: string, password: string): Promise<string> {
return await fetchWithToken(this.token, "/auth/login", {
body: JSON.stringify({
username,
password,
}),
method: "POST",
}).then(value => value.json()).then(value => value.token);
}
public async logout(): Promise<void> {
await fetchWithToken(this.token, "/auth/tokens/logout", {
method: "POST",
});
}
}
export const authRepo = derived(tokenStore, ($token) => new AuthRepo($token));

View File

@@ -0,0 +1,184 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2025 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {readable, writable} from "svelte/store";
import dayjs, {type Dayjs} from "dayjs";
import {type AuthToken, AuthTokenSchema} from "@type/auth.ts";
export class AuthV2Repo {
private accessToken: string | undefined;
private accessTokenExpires: Dayjs | undefined;
private refreshToken: string | undefined;
private refreshTokenExpires: Dayjs | undefined;
constructor() {
if (typeof localStorage === "undefined") {
return;
}
this.accessToken = localStorage.getItem("sw-access-token") ?? undefined;
if (this.accessToken) {
this.accessTokenExpires = dayjs(localStorage.getItem("sw-access-token-expires") ?? "");
}
this.refreshToken = localStorage.getItem("sw-refresh-token") ?? undefined;
if (this.refreshToken) {
loggedIn.set(true);
this.refreshTokenExpires = dayjs(localStorage.getItem("sw-refresh-token-expires") ?? "");
}
}
async login(name: string, password: string) {
if (this.accessToken !== undefined || this.refreshToken !== undefined) {
throw new Error("Already logged in");
}
try {
const login = await this.request("/auth", {
method: "POST",
body: JSON.stringify({
name,
password,
keepLoggedIn: true,
}),
}).then(value => value.json()).then(value => AuthTokenSchema.parse(value));
this.setLoginState(login);
return true;
} catch (e) {
return false;
}
}
async logout() {
if (this.accessToken === undefined) {
return;
}
await this.request("/auth", {
method: "DELETE",
});
this.resetAccessToken();
this.resetRefreshToken();
}
private setLoginState(tokens: AuthToken) {
this.setAccessToken(tokens.accessToken.token, dayjs(tokens.accessToken.expires));
this.setRefreshToken(tokens.refreshToken.token, dayjs(tokens.refreshToken.expires));
loggedIn.set(true);
}
private setAccessToken(token: string, expires: Dayjs) {
this.accessToken = token;
this.accessTokenExpires = expires;
localStorage.setItem("sw-access-token", token);
localStorage.setItem("sw-access-token-expires", expires.toString());
}
private resetAccessToken() {
if (this.accessToken === undefined) {
return;
}
this.accessToken = undefined;
this.accessTokenExpires = undefined;
localStorage.removeItem("sw-access-token");
localStorage.removeItem("sw-access-token-expires");
}
private setRefreshToken(token: string, expires: Dayjs) {
this.refreshToken = token;
this.refreshTokenExpires = expires;
localStorage.setItem("sw-refresh-token", token);
localStorage.setItem("sw-refresh-token-expires", expires.toString());
}
private resetRefreshToken() {
if (this.refreshToken === undefined) {
return;
}
this.refreshToken = undefined;
this.refreshTokenExpires = undefined;
localStorage.removeItem("sw-refresh-token");
localStorage.removeItem("sw-refresh-token-expires");
loggedIn.set(false);
}
private async refresh() {
if (this.refreshToken === undefined || this.refreshTokenExpires === undefined || this.refreshTokenExpires.isBefore(dayjs().add(10, "seconds"))) {
this.resetRefreshToken();
this.resetAccessToken();
return;
}
const response = await this.requestWithToken(this.refreshToken!, "/auth", {
method: "PUT",
}).then(value => {
if (value.status === 401) {
this.resetRefreshToken();
this.resetAccessToken();
return undefined;
}
return value.json();
}).then(value => AuthTokenSchema.parse(value));
this.setLoginState(response);
}
async request(url: string, params: RequestInit = {}, retryCount: number = 0) {
if (this.accessToken !== undefined && this.accessTokenExpires !== undefined && this.accessTokenExpires.isBefore(dayjs().add(10, "seconds"))) {
await this.refresh();
}
return this.requestWithToken(this.accessToken ?? "", url, params, retryCount);
}
private async requestWithToken(token: string, url: string, params: RequestInit = {}, retryCount: number = 0): Promise<Response> {
if (retryCount >= 3) {
throw new Error("Too many retries");
}
return fetch(`${import.meta.env.PUBLIC_API_SERVER}${url}`, {...params,
headers: {
...(token !== "" ? {"Authorization": "Bearer " + (token)} : {}),
"Content-Type": "application/json", ...params.headers,
},
})
.then(async value => {
if (value.status === 401 && url !== "/auth") {
try {
await this.refresh();
// eslint-disable-next-line @typescript-eslint/no-unused-vars
} catch (_e) { /* empty */ }
return this.request(url, params, retryCount + 1);
}
return value;
});
}
}
export const loggedIn = writable(false);
export const authV2Repo = readable(new AuthV2Repo());

View File

@@ -17,31 +17,9 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {writable} from "svelte/store";
import {get, writable} from "svelte/store";
import {authV2Repo} from "@repo/authv2.ts";
export const fetchWithToken = (token: string, url: string, params: RequestInit = {}) =>
fetch(`${import.meta.env.PUBLIC_API_SERVER}${url}`, {...params,
headers: {
...(token !== "" ? {"Authorization": "Bearer " + (token)} : {}),
"Content-Type": "application/json", ...params.headers,
},
})
.then(value => {
if (value.status === 401) {
tokenStore.set("");
}
return value;
});
export const fetchWithToken = (token: string, url: string, params: RequestInit = {}) => get(authV2Repo).request(url, params);
export function getLocalStorage() {
if (typeof localStorage === "undefined") {
return {
getItem: () => "",
setItem: () => {},
};
}
return localStorage;
}
export const tokenStore = writable((getLocalStorage().getItem("sw-session") ?? ""));
tokenStore.subscribe((value) => getLocalStorage().setItem("sw-session", value));
export const tokenStore = writable("");

View File

@@ -36,7 +36,7 @@ export class StatsRepo {
}
public async getUserStats(id: string): Promise<UserStats> {
return await fetchWithToken(this.token, `/stats/user/${id}`).then(value => value.json()).then(UserStatsSchema.parse);
return await fetchWithToken(this.token, `/stats/user`).then(value => value.json()).then(UserStatsSchema.parse);
}
}

View File

@@ -0,0 +1,34 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2025 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {z} from "zod";
export const TokenSchema = z.object({
token: z.string(),
expires: z.string(),
});
export type Token = z.infer<typeof TokenSchema>;
export const AuthTokenSchema = z.object({
accessToken: TokenSchema,
refreshToken: TokenSchema,
});
export type AuthToken = z.infer<typeof AuthTokenSchema>;

View File

@@ -0,0 +1,46 @@
---
title: MissileWars III Eventplan
key: missilewars3-eventplan
description: Der Eventplan für MissileWars 3
created: 2025-02-23
tags:
- event
- missilewars
---
### Infos:
Eventleitung: TheBreadBeard
Fights werden nach Möglichkeit mit einer Pause von 10 Minuten vorverschoben.
# Gruppenphase
## Punkte aus der Gruppenphase
<group-table data-event="67"> </group-table>
### Fights
| Start | Teams |
|-------|------------|
| 16:00:00 | KT vs Borg |
| 16:00:30 | VI vs FK |
| 16:30:00 | FK vs KT |
| 16:30:30 | Hlcy vs VI |
| 17:00:00 | VI vs KT |
| 17:00:30 | Borg vs Hlcy |
| 17:30:00 | KT vs Hlcy|
| 17:30:30 | FK vs Borg |
| 18:00:00 | VI vs Borg |
| 18:00:30 | FK vs Hlcy |
## KO-Phase
| Start | Teams |
|-------|------------|
| 17:50:00 | VI vs Borg |
| 18:20:00 | Hlcy vs FK |
| 18:22:00 | VI vs FK |
| 18:44:00 | Hlcy vs Borg |
## Ergebnisse
<fight-table data-event="67" data-group="Gruppe 1"> </fight-table>

View File

@@ -0,0 +1,105 @@
---
title: WGS 2025 Kampfplan
description: WGS 2025 Kampfplan
key: wgs-2025-kampfplan
created: 2025-03-01
tags:
- event
- wargear
---
# WGS 2025 Kampfplan
### Platzierung
| Platzierung | Team | Punkte |
|-------------|-------------|--------|
| 1. | Halcyon | 7 |
| 2. | Happy-End | 6 |
| 3. | ABYSS | 4 |
| 4. | Borg | 3 |
| 5. | TheNoobTeam | 1 |
| 6. | BlackFire | 0 |
### Aktueller Spieltag
<fight-table data-event="68"></fight-table>
### Qualifikation
| 01.03.2025 | Begegnung | Ergebnis |
|------------|--------------|----------|
| 19:00 | HPY vs ABYS | HPY qualifiziert |
| 19:30 | Borg vs Hlcy | Hlcy qualifiziert |
| 20:00 | TNT vs BF | TNT qualifiziert |
| 20:30 | ABYS vs Borg | Sieg ABYS |
| 21:00 | Borg vs BF | Sieg Borg |
| 21:30 | BF vs ABYS | ABYS, Borg qualifiziert |
PL hat sich nach der Qualifikation zurückgezogen, womit BF nachgerückt ist.
### 1. Spieltag
| 08.03.2025 | Begegnung | Ergebnis |
|------------|--------------|----------|
| 19:00 | ABYS vs Borg | Sieg ABYS |
| 19:30 | Hlcy vs HPY | Sieg Hlcy |
| 20:00 | BF vs TNT | Sieg TNT |
| 20:30 | HPY vs ABYS | Sieg HPY |
| 21:00 | Borg vs BF | Sieg Borg |
| 21:30 | TNT vs Hlcy | Sieg Hlcy |
### 2. Spieltag
| 15.03.2025 | Begegnung | Ergebnis |
|------------|--------------|----------|
| 19:00 | HPY vs TNT | Sieg HPY |
| 19:30 | ABYS vs BF | Sieg ABYS |
| 20:00 | Borg vs Hlcy | Sieg Hlcy |
| 20:30 | BF vs HPY | Sieg HPY |
| 21:00 | TNT vs Borg | Sieg Borg |
| 21:30 | Hlcy vs ABYS | Sieg Hlcy |
### 3. Spieltag
| 22.03.2025 | Begegnung | Ergebnis |
|------------|-------------|----------|
| 19:00 | ABYS vs TNT | Sieg ABYS |
| 19:30 | Borg vs HPY | Sieg HPY |
| 20:00 | Hlcy vs BF | Sieg Hlcy |
| 20:30 | - | / |
| 21:00 | - | / |
| 21:30 | - | / |
### 4. Spieltag
| 29.03.2025 | Begegnung | Ergebnis |
|------------|--------------|----------|
| 19:00 | TNT vs ABYS | Sieg ABYS |
| 19:30 | HPY vs Borg | Sieg HPY |
| 20:00 | BF vs Hlcy | Sieg Hlcy |
| 20:30 | Borg vs TNT | Sieg Borg |
| 21:00 | HPY vs BF | Sieg HPY |
| 21:30 | ABYS vs Hlcy | Sieg Hlcy |
### 5. Spieltag
| 05.04.2025 | Begegnung | Ergebnis |
|------------|--------------|----------|
| 19:00 | TNT vs HPY | / |
| 19:30 | BF vs ABYS | / |
| 20:00 | Hlcy vs Borg | / |
| 20:30 | ABYS vs HPY | / |
| 21:00 | BF vs Borg | / |
| 21:30 | Hlcy vs TNT | / |
### 6. Spieltag
| 12.04.2025 | Begegnung | Ergebnis |
|------------|--------------|----------|
| 19:00 | Borg vs ABYS | / |
| 19:30 | HPY vs Hlcy | / |
| 20:00 | TNT vs BF | / |
| 20:30 | - | / |
| 21:00 | - | / |
| 21:30 | - | / |

View File

@@ -4,7 +4,8 @@
"url": {
"1.21.4": "https://git.steamwar.de/SteamWar/AdvancedScripts/releases/download/2.2.0/AdvancedScripts-2.2.0.jar",
"1.20.6": "https://git.steamwar.de/SteamWar/AdvancedScripts/releases/download/2.1.0/AdvancedScripts-2.1.0.jar",
"1.19.3": "https://git.steamwar.de/SteamWar/AdvancedScripts/releases/download/2.0.0/AdvancedScripts-2.0.0.jar"
"1.19.3": "https://git.steamwar.de/SteamWar/AdvancedScripts/releases/download/2.0.0/AdvancedScripts-2.0.0.jar",
"Hotkey script": "https://git.steamwar.de/SteamWar/SteamWar/src/branch/main/BauSystem/hotkeys.lua"
},
"sourceUrl": "https://git.steamwar.de/SteamWar/AdvancedScripts"
}

View File

@@ -2,7 +2,8 @@
"name": "SteamWarTeamserver",
"description": "Dieses Plugin ermöglicht die einfache Einbindung deines Servers in SteamWar. Wie du deinen (Team-)Server über SteamWar erreichbar machen kannst findest du hier.",
"url": {
"Info": "/teamserverintegration"
"Info": "/teamserverintegration",
"Download": "https://git.steamwar.de/SteamWar/SteamWarTeamserverIntegration/releases/download/latest/SteamWarTeamserverIntegration.jar"
},
"sourceUrl": "https://git.steamwar.de/SteamWar/SteamWarBungeeTeamserver"
"sourceUrl": "https://git.steamwar.de/SteamWar/SteamWarTeamserverIntegration"
}

View File

@@ -1,4 +1,5 @@
{
"translationKey": "microwg",
"main": false
"main": false,
"ranked": true
}

View File

@@ -0,0 +1,5 @@
{
"translationKey": "mw",
"main": false,
"ranked": true
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 922 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1000 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -0,0 +1,178 @@
---
title: Shop
description: Shop
slug: shop
slugs:
en: shop
---
# Ranks
![Lieutenant2.png](Lieutenant2.png)
| | |
|:------------------------------------|:--------:|
| 1x Monthly crate Key | GIFT |
| Toggle night vision | PERK |
| `/enderchest` access | PERK |
| 4 additional homes | PERK |
| 4 additional auction house listings | PERK |
| extra particle slot | COSMETIC |
<button>Buy now for 5€</button>\
\
\
![commander2.png](commander2.png)
| | |
|:------------------------------------|:--------:|
| 3x Monthly crate Key | GIFT |
| Toggle night vision | PERK |
| `/enderchest` access | PERK |
| 6 additional homes | PERK |
| 6 additional auction house listings | PERK |
| 2 extra particle slot | COSMETIC |
| 5 Minion Slots | PERK |
| reduced Premium shop access | PERK |
| Fireball cannon | PERK |
| 1x monthly airstrike | PERk |
| use chat color codes | COSMETIC |
| `/back` access | PERK |
| 1 invincible chunk | PERK |
<button>Buy now for 14,25€</button>\
\
\
![captain2.png](captain2.png)
| | |
|:-------------------------------------|:--------:|
| 6x Monthly crate Key | GIFT |
| Toggle night vision | PERK |
| `/enderchest` access | PERK |
| 10 additional homes | PERK |
| 10 additional auction house listings | PERK |
| 5 extra particle slot | COSMETIC |
| 10 Minion Slots | PERK |
| Premium shop access | PERK |
| Fireball cannon | PERK |
| 3x monthly airstrike | PERk |
| use chat color codes | COSMETIC |
| `/back` access | PERK |
| spectate other players | PERK |
| +27 secure clan vault slots | PERK |
| 1x monthly 3 days raid protection | PERK |
| 1 cannon moving create | GIFT |
| 10% more Steamies per € | PERK |
| 2 invincible chunk | PERK |
| 1x Monthly invincible chunk buster | GIFT |
| `/nick` access | PERK |
<button>Buy now for 27€</button>
# Keys
![key1.png](key1.png)
| 70% | 20% | 10% |
|--------------------|---------------------------|----------------------|
| Scraps Kit | Creeper Bomb | Charged Creeper Bomb |
| Animal Crate | 3x Wither Skull | Airstrike |
| Trench Axe | Totem of Dying | Crate 2 Key |
| Trench Pickaxe | Legendary Trench Axe | Wither EMP |
| Trench Shovel | Legendary Trench Pikaxe | Random Max Enchant |
| Sell Wand | Legendary Trench Shovel | Legendary Multitool |
| 2x Portal | Minion | colorcode Nametag |
| Starter Weapon Kit | Legendary Helmet | Guardian Spawner |
| 1x God Apple | Legendary Chestplate | Legendary Raid Key |
| Shulker Box | Legendary Leggings | |
| Random Enchant | Legendary Boots | |
| Common Raid Key | 6x God Apples | |
| Starter Tool Kit | 3x Throwable Creeper | |
| 10 Steamies | Zombie Spawner | |
| Endstone Crate | Witch Spawner | |
| | Supercharged Auto Crafter | |
| | Scroll of Durability | |
| | 16x Reinforced Obsidian | |
| | Zombie Villager Spawner | |
| | Scroll of Binding | |
| | Rare Raid Key | |
<button>Buy now for 230s</button>\
\
\
![key2-2.png](key2-2.png)
| 20% | 40% | 30% | 10% |
|--------------------|---------------------------|----------------------|-------------------------|
| Scraps Kit | Creeper Bomb | Charged Creeper Bomb | Cannon Moving Crate |
| Animal Crate | 3x Wither Skull | Airstrike | Invincible Chunk Buster |
| Trench Axe | Totem of Dying | Crate 2 Key | Mythic Kit |
| Trench Pickaxe | Legendary Trench Axe | Wither EMP | Mythic Raid Key |
| Trench Shovel | Legendary Trench Pikaxe | Random Max Enchant | Wither Skeleton Spawner |
| Sell Wand | Legendary Trench Shovel | Legendary Multitool | Blaze Spawner |
| 2x Portal | Minion | colorcode Nametag | Captain Rank |
| Starter Weapon Kit | Legendary Helmet | Guardian Spawner | |
| 1x God Apple | Legendary Chestplate | Legendary Raid Key | |
| Shulker Box | Legendary Leggings | | |
| Random Enchant | Legendary Boots | | |
| Common Raid Key | 6x God Apples | | |
| Starter Tool Kit | 3x Throwable Creeper | | |
| 10 Steamies | Zombie Spawner | | |
| Endstone Crate | Witch Spawner | | |
| | Supercharged Auto Crafter | | |
| | Scroll of Durability | | |
| | 16x Reinforced Obsidian | | |
| | Zombie Villager Spawner | | |
| | Scroll of Binding | | |
| | Rare Raid Key | | |
<button>Buy now for 230s</button>| Common Raid Key | 6x God Apples | |
| Starter Tool Kit | 3x Throwable Creeper | |
| 10 Steamies | Zombie Spawner | |
| Endstone Crate | Witch Spawner | |
| | Supercharged Auto Crafter | |
| | Scroll of Durability | |
| | 16x Reinforced Obsidian | |
| | Zombie Villager Spawner | |
| | Scroll of Binding | |
| | Rare Raid Key | |
<button>Buy now for 1173s</button>\
\
\
![key3-3.png](key3-2.png)
| 10% | 70% | 20% |
|---------------------------|----------------------|-------------------------|
| Creeper Bomb | Charged Creeper Bomb | Cannon Moving Crate |
| 3x Wither Skull | Airstrike | Invincible Chunk Buster |
| Totem of Dying | Crate 2 Key | Mythic Kit |
| Legendary Trench Axe | Wither EMP | Mythic Raid Key |
| Legendary Trench Pikaxe | Random Max Enchant | Wither Skeleton Spawner |
| Legendary Trench Shovel | Legendary Multitool | Blaze Spawner |
| Minion | colorcode Nametag | Captain Rank |
| Legendary Helmet | Guardian Spawner | |
| Legendary Chestplate | Legendary Raid Key | |
| Legendary Leggings | | |
| Legendary Boots | | |
| 6x God Apples | | |
| 3x Throwable Creeper | | |
| Zombie Spawner | | |
| Witch Spawner | | |
| Supercharged Auto Crafter | | |
| Scroll of Durability | | |
| 16x Reinforced Obsidian | | |
| Zombie Villager Spawner | | |
| Scroll of Binding | | |
| Rare Raid Key | | |
<button>Buy now for 5869s</button>
# Steamies
![STEAMIE.png](STEAMIE.png) 420 Steamies für 4,50€\
\
![STEAMIE2.png](STEAMIE2.png) 4269 Steamies für 45,00€\
\
![steamiebag.png](steamiebag.png) 42690 Steamies für 450,00€

Binary file not shown.

After

Width:  |  Height:  |  Size: 943 B

View File

@@ -1,9 +1,9 @@
{
"name": "Graf Spree",
"name": "Graf Spee",
"description": "A simple, lightweight description of a frostbite.",
"id": 123,
"creator": ["Test", "Test2"],
"gamemode": "warship",
"image": "../../../images/publics/grafspree/area_render.png",
"image": "../../../images/publics/grafspee/area_render.png",
"3d": false
}

View File

@@ -0,0 +1,3 @@
---
translationKey: mw
---

View File

@@ -87,7 +87,10 @@
"title": "Hilfe",
"docs": "Dokumentation"
},
"account": "Konto"
"account": "Konto",
"ranked": {
"mw": "MissileWars"
}
}
},
"wg": {
@@ -123,6 +126,9 @@
"microwg": {
"title": "MicroWarGear"
},
"mw": {
"title": "MissileWars"
},
"footer": {
"imprint": "Impressum",
"privacy": "Datenschutzerklärung",
@@ -205,7 +211,8 @@
},
"label": {
"username": "Nutzername",
"password": "Passwort"
"password": "Passwort",
"repeat": "Passwort Wiederholen"
},
"setPassword": "Wie setzte ich mein Passwort?",
"submit": "Login",

View File

@@ -149,7 +149,8 @@
},
"label": {
"username": "Username",
"password": "Password"
"password": "Password",
"repeat": "Repeat Password"
},
"setPassword": "How to set a Password",
"submit": "Login",

View File

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 1.3 MiB

View File

@@ -3,7 +3,7 @@ import {Image} from "astro:assets";
import Basic from "./Basic.astro";
import "../styles/button.css";
import localLogo from "../images/logo.png";
import {YoutubeSolid, DiscordSolid} from "flowbite-svelte-icons";
import {YoutubeSolid, DiscordSolid, FileCodeSolid} from "flowbite-svelte-icons";
import {t} from "astro-i18n";
import {l} from "../util/util";
@@ -50,6 +50,9 @@ const {title, description} = Astro.props;
<a class="flex" href="/discord">
<DiscordSolid class="mr-2"/>
Discord</a>
<a class="flex" href="https://git.steamwar.de">
<FileCodeSolid class="mr-2"/>
Gitea</a>
</div>
</div>
<span class="text-sm text-white text-center mt-1">© SteamWar.de - Made with ❤️ by Chaoscaot</span>

View File

@@ -77,7 +77,7 @@ const ogImage = await getImage({
)}
<div class={post.data.image ? "absolute bottom-8 left-2" : "mb-4"}>
<h1 class="text-4xl mb-0" transition:name={post.data.title + "-title"}>{post.data.title}</h1>
<div class="flex items-center mt-2 text-neutral-300">
<div class="flex items-center mt-2 text-neutral-800 dark:text-neutral-300">
<TagSolid class="w-4 h-4 mr-2"/>
<div transition:name={post.data.title + "-tags"}>
{post.data.tags.map(tag => (
@@ -111,15 +111,13 @@ const ogImage = await getImage({
import type {ExtendedEvent} from "@type/event";
import {mount} from "svelte";
const eventMounts: Map<string, ((ev: ExtendedEvent) => void)[]> = new Map();
const eventMounts: Map<string, Promise<ExtendedEvent>> = new Map();
class FightTableElement extends HTMLElement {
connectedCallback(): void {
if (!eventMounts.has(this.dataset["event"]!)) {
eventMounts.set(this.dataset["event"]!, []);
}
loadEvent(this.dataset["event"]!);
const rows = Number.parseInt(this.dataset["rows"]!);
eventMounts.get(this.dataset["event"]!)!.push(ev => {
eventMounts.get(this.dataset["event"]!)!.then(ev => {
mount(FightTable, {
target: this,
props: {
@@ -134,11 +132,9 @@ const ogImage = await getImage({
class GroupTableElement extends HTMLElement {
connectedCallback(): void {
if (!eventMounts.has(this.dataset["event"]!)) {
eventMounts.set(this.dataset["event"]!, []);
}
loadEvent(this.dataset["event"]!);
const rows = Number.parseInt(this.dataset["rows"]!);
eventMounts.get(this.dataset["event"]!)!.push(ev => {
eventMounts.get(this.dataset["event"]!)!.then(ev => {
mount(GroupTable, {
target: this,
props: {
@@ -154,17 +150,13 @@ const ogImage = await getImage({
customElements.define("fight-table", FightTableElement);
customElements.define("group-table", GroupTableElement);
function mountEvent() {
for (const key of eventMounts.keys()) {
get(eventRepo).getEvent(key).then(ev => {
for (const mount of eventMounts.get(key)!) {
mount(ev);
}
});
function loadEvent(id: string) {
if (!eventMounts.has(id)) {
eventMounts.set(id, get(eventRepo).getEvent(id));
}
}
mountEvent();
document.addEventListener("astro:before-swap", eventMounts.clear);
</script>
</article>
</PageLayout>

View File

@@ -8,9 +8,11 @@ import {t} from "astro-i18n";
<script>
import {l} from "../util/util";
import {navigate} from "astro:transitions/client";
import {get} from "svelte/store";
import {loggedIn} from "../components/repo/authv2";
document.addEventListener("astro:page-load", () => {
if (window.location.href.endsWith("/dashboard") || window.location.href.endsWith("/dashboard/")) {
if ((localStorage.getItem("sw-session") ?? "") === "") {
if (!get(loggedIn)) {
navigate(l("/login"), {});
}
}

View File

@@ -9,10 +9,12 @@ import BackgroundImage from "../components/BackgroundImage.astro";
<script>
import {l} from "../util/util";
import {navigate} from "astro:transitions/client";
import {loggedIn} from "../components/repo/authv2";
import {get} from "svelte/store";
document.addEventListener("astro:page-load", () => {
if (window.location.href.endsWith("/login") || window.location.href.endsWith("/login/")) {
if ((localStorage.getItem("sw-session") ?? "") !== "") {
if (get(loggedIn)) {
navigate(l("/dashboard"), {history: "replace"});
}
}

View File

@@ -44,4 +44,5 @@ const modes = await getCollection("modes", entry => entry.data.main);
</div>
</div>
</div>))}
<a href={l("/rangliste/MissileWars")}>MissileWars Rangliste</a>
</PageLayout>

View File

@@ -31,7 +31,7 @@ table {
text-align: center;
tr:nth-child(odd) {
@apply bg-neutral-800;
@apply bg-neutral-200 dark:bg-neutral-800;
}
}
}