Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions lang/dev/lobby.json
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,13 @@
"luaOptionsModal": {
"resetAllToDefault": "Reest all to dfauelt",
"close": "Csole"
},
"rejoinBattleModal": {
"title": "Reojin Blatte?",
"description1": "The sveerr idecatind that you were in an avctie btalte.",
"description2": "Wulod you like to atemtpt to rejoin the btalte?",
"rejoinButton": "Reojin",
"ignoreButton": "Ingroe"
}
},
"controls": {
Expand Down
7 changes: 7 additions & 0 deletions lang/en/lobby.json
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,13 @@
"luaOptionsModal": {
"resetAllToDefault": "Reset all to default",
"close": "Close"
},
"rejoinBattleModal": {
"title": "Rejoin Battle?",
"description1": "The server indicated that you were in an active battle.",
"description2": "Would you like to attempt to rejoin the battle?",
"rejoinButton": "Rejoin",
"ignoreButton": "Ignore"
}
},
"controls": {
Expand Down
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@
"primeicons": "^6.0.1",
"primevue": "3.23.0",
"sdfz-demo-parser": "^5.15.0",
"tachyon-protocol": "^1.23.0",
"tachyon-protocol": "^1.24.0",
"vue-i18n": "^11.1.11",
"vue-router": "^4.5.1",
"ws": "^8.18.3"
Expand Down
1 change: 1 addition & 0 deletions src/main/main-window.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export function createWindow() {
preload: path.join(__dirname, "../build/preload.js"),
zoomFactor: 1,
spellcheck: false,
autoplayPolicy: "no-user-gesture-required",
},
});

Expand Down
15 changes: 11 additions & 4 deletions src/main/tachyon/tachyon.handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,16 @@ function createBattleHandlers(webContents: BarIpcWebContents) {
"battle/start",
createTypedTachyonRequestHandler<"battle/start">()(async (data: BattleStartRequestData) => {
// data carries the join password, so it is summarised rather than dumped.
log.info(`Received battle start request for ${data.ip}:${data.port}`);
// Note, data.ips[string] values are either ipv4 or ipv6, but we only use the first (only) for now.
if (data.ips && data.ips.length > 0) log.info(`Received battle start request for ${data.ips[0]}:${data.port}`);
else {
// @ts-expect-error ip is not part of BattleStartRequestData but is used for backward server compatibility
if (data.ip) {
// @ts-expect-error ip is not part of BattleStartRequestData but is used for backward server compatibility
data.ips = [data.ip];
}
log.debug(data);
}
const missing = contentAPI.missing([
{ type: "game", id: data.game.springName },
{ type: "map", id: data.map.springName },
Expand All @@ -53,9 +62,7 @@ function createBattleHandlers(webContents: BarIpcWebContents) {
});
return { status: "failed", reason: "internal_error" };
} else {
const { ip, port, username, password } = data;
const springString = `spring://${username}:${password}@${ip}:${port}`;
webContents.send("tachyon:battleStart", springString, data);
webContents.send("tachyon:battleStart", data);
return {
status: "success",
};
Expand Down
5 changes: 3 additions & 2 deletions src/main/typed-ipc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import type { AuthState } from "@main/services/auth.service";
import type { StoredIdentity } from "@main/model/user";
import type { BattleWithMetadata } from "@main/game/battle/battle-types";
import type { BattleStartRequestData } from "tachyon-protocol/types";
import type { BattleStartRequestData, BattleEndedEventData } from "tachyon-protocol/types";
import type { ContentRef } from "@main/content/content-ref";
import type { ContentPresence, ContentState } from "@main/content/content-state";
import type { DownloadInfo } from "@main/content/downloads";
Expand Down Expand Up @@ -47,7 +47,8 @@ export type IPCEvents = {
"replays:replayCachingStarted": (filename: string) => void;
"replays:replayDeleted": (filename: string) => void;
"replays:highlightOpened": (fileNames: string[]) => void;
"tachyon:battleStart": (springString: string, data: BattleStartRequestData) => void;
"tachyon:battleStart": (data: BattleStartRequestData) => void;
"tachyon:battleEnded": (data: BattleEndedEventData) => void;
"tachyon:connected": () => void;
"tachyon:disconnected": () => void;
"tachyon:event": (event: TachyonEvent) => void;
Expand Down
5 changes: 3 additions & 2 deletions src/preload/preload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { DownloadInfo } from "@main/content/downloads";
import { Info } from "@main/services/info.service";
import { BattleWithMetadata } from "@main/game/battle/battle-types";
import { GetCommandData, GetCommandIds, GetCommands } from "tachyon-protocol";
import type { BattleStartRequestData } from "tachyon-protocol/types";
import type { BattleStartRequestData, BattleEndedEventData } from "tachyon-protocol/types";
import { MultiplayerLaunchSettings } from "@main/game/game";
import { logLevels } from "@main/services/log.service";
import { Config } from "@main/services/config.service";
Expand Down Expand Up @@ -248,7 +248,8 @@ const tachyonApi = {
onConnected: (callback: () => void) => ipcRenderer.on("tachyon:connected", callback),
onDisconnected: (callback: () => void) => ipcRenderer.on("tachyon:disconnected", callback),
onEvent,
onBattleStart: (callback: (springString: string, data: BattleStartRequestData) => void) => ipcRenderer.on("tachyon:battleStart", (_event, springString, data) => callback(springString, data)),
onBattleStart: (callback: (data: BattleStartRequestData) => void) => ipcRenderer.on("tachyon:battleStart", (_event, data) => callback(data)),
onBattleEnded: (callback: (data: BattleEndedEventData) => void) => ipcRenderer.on("tachyon:battleEnded", (_event, data) => callback(data)),
};
export type TachyonApi = typeof tachyonApi;
contextBridge.exposeInMainWorld("tachyon", tachyonApi);
Expand Down
7 changes: 5 additions & 2 deletions src/renderer/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ SPDX-License-Identifier: MIT
<FullscreenGameModeSelector v-if="state === 'default'" :visible="battleStore.isSelectingGameMode" />
<LogInConfirmationModal v-model="logInConfirmationIsOpen" :intendedRoute="logInConfirmationIntendedRoute" />
<ReportUserModal />
<RejoinBattleModal v-model="tachyonStore.rejoinModalOpen" />
</div>
<Error />
</template>
Expand Down Expand Up @@ -89,6 +90,7 @@ import ReconnectingOverlay from "@renderer/components/misc/ReconnectingOverlay.v
import PromptContainer from "@renderer/components/prompts/PromptContainer.vue";
import LogInConfirmationModal from "@renderer/components/misc/LogInConfirmationModal.vue";
import ReportUserModal from "@renderer/components/user/ReportUserModal.vue";
import RejoinBattleModal from "@renderer/components/battle/RejoinBattleModal.vue";

import { playRandomMusic } from "@renderer/utils/play-random-music";
import { settingsStore } from "./store/settings.store";
Expand Down Expand Up @@ -201,8 +203,9 @@ if (!settingsStore.devMode) {

function unseenPartyMessages() {
if (!partyStore.activeParty) return false;
if (chatStore.partyChat.length <= 0) return false;
return !chatStore.partyChat.at(-1)?.seen;
const messages = chatStore.partyChats.get(partyStore.activeParty);
if (!messages || messages.length <= 0) return false;
return !messages.at(-1)?.seen;
}
function getPartyIcon() {
if (unseenPartyMessages()) return bellAlert;
Expand Down
1 change: 1 addition & 0 deletions src/renderer/assets/assetFiles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export const sfxFiles = import.meta.glob<string>(["./audio/sfx/*", "!**/*.licens

// Videos
export const introVideos = import.meta.glob<string>(["./videos/intros/*", "!**/*.license"], { eager: true, import: "default", query: "?url" });
export const miscVideos = import.meta.glob<string>(["./videos/misc/*", "!**/*.license"], { eager: true, import: "default", query: "?url" });

// Images
export const backgroundImages = import.meta.glob<string>(["./images/backgrounds/*", "!**/*.license"], { eager: true, import: "default", query: "?url" });
Expand Down
7 changes: 7 additions & 0 deletions src/renderer/assets/languages/cs.json
Original file line number Diff line number Diff line change
Expand Up @@ -2553,6 +2553,13 @@
"luaOptionsModal": {
"resetAllToDefault": null,
"close": null
},
"rejoinBattleModal": {
"title": null,
"description1": null,
"description2": null,
"rejoinButton": null,
"ignoreButton": null
}
},
"controls": {
Expand Down
7 changes: 7 additions & 0 deletions src/renderer/assets/languages/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -2424,6 +2424,13 @@
"luaOptionsModal": {
"resetAllToDefault": null,
"close": null
},
"rejoinBattleModal": {
"title": null,
"description1": null,
"description2": null,
"rejoinButton": null,
"ignoreButton": null
}
},
"controls": {
Expand Down
7 changes: 7 additions & 0 deletions src/renderer/assets/languages/dev.json
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,13 @@
"luaOptionsModal": {
"resetAllToDefault": "Reest all to dfauelt",
"close": "Csole"
},
"rejoinBattleModal": {
"title": "Reojin Blatte?",
"description1": "The sveerr idecatind that you were in an avctie btalte.",
"description2": "Wulod you like to atemtpt to rejoin the btalte?",
"rejoinButton": "Reojin",
"ignoreButton": "Ingroe"
}
},
"controls": {
Expand Down
7 changes: 7 additions & 0 deletions src/renderer/assets/languages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -2401,6 +2401,13 @@
"luaOptionsModal": {
"resetAllToDefault": "Reset all to default",
"close": "Close"
},
"rejoinBattleModal": {
"title": "Rejoin Battle?",
"description1": "The server indicated that you were in an active battle.",
"description2": "Would you like to attempt to rejoin the battle?",
"rejoinButton": "Rejoin",
"ignoreButton": "Ignore"
}
},
"controls": {
Expand Down
7 changes: 7 additions & 0 deletions src/renderer/assets/languages/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -4406,6 +4406,13 @@
"luaOptionsModal": {
"resetAllToDefault": null,
"close": null
},
"rejoinBattleModal": {
"title": null,
"description1": null,
"description2": null,
"rejoinButton": null,
"ignoreButton": null
}
},
"controls": {
Expand Down
7 changes: 7 additions & 0 deletions src/renderer/assets/languages/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -4375,6 +4375,13 @@
"luaOptionsModal": {
"resetAllToDefault": null,
"close": null
},
"rejoinBattleModal": {
"title": null,
"description1": null,
"description2": null,
"rejoinButton": null,
"ignoreButton": null
}
},
"controls": {
Expand Down
7 changes: 7 additions & 0 deletions src/renderer/assets/languages/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -4517,6 +4517,13 @@
"luaOptionsModal": {
"resetAllToDefault": null,
"close": null
},
"rejoinBattleModal": {
"title": null,
"description1": null,
"description2": null,
"rejoinButton": null,
"ignoreButton": null
}
},
"controls": {
Expand Down
Binary file added src/renderer/assets/videos/misc/active_battle.mkv
Binary file not shown.
3 changes: 3 additions & 0 deletions src/renderer/assets/videos/misc/active_battle.mkv.license
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
SPDX-FileCopyrightText: 2026 The BAR Lobby Authors

SPDX-License-Identifier: CC0-1.0
57 changes: 57 additions & 0 deletions src/renderer/components/battle/RejoinBattleModal.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<!--
SPDX-FileCopyrightText: 2026 The BAR Lobby Authors

SPDX-License-Identifier: MIT
-->

<template>
<Modal v-model="isOpen" :title="t('lobby.components.battle.rejoinBattleModal.title')">
<div class="container">
<p>{{ t("lobby.components.battle.rejoinBattleModal.description1") }}</p>
<p>{{ t("lobby.components.battle.rejoinBattleModal.description2") }}</p>
<div class="flex-row flex-center padding-top-lg gap-xl">
<Button class="green fullwidth" @click="onConfirm">{{
t("lobby.components.battle.rejoinBattleModal.rejoinButton")
}}</Button>
<Button class="red fullwidth" @click="onCancel">{{ t("lobby.components.battle.rejoinBattleModal.ignoreButton") }}</Button>
</div>
</div>
</Modal>
</template>

<script lang="ts" setup>
import { computed } from "vue";
import Modal from "@renderer/components/common/Modal.vue";
import Button from "@renderer/components/controls/Button.vue";
import { tachyon, tachyonStore } from "@renderer/store/tachyon.store";
import { useTypedI18n } from "@renderer/i18n";

const { t } = useTypedI18n();

const props = withDefaults(
defineProps<{
modelValue?: boolean;
}>(),
{ modelValue: false }
);

const emit = defineEmits<{ (e: "update:modelValue", v: boolean): void }>();
const isOpen = computed({
get: () => props.modelValue,
set: (v: boolean) => emit("update:modelValue", v),
});

function onConfirm() {
console.log("Rejoining battle...");
if (tachyonStore.springConnectionDetails) {
tachyon.launchMultiplayerBattle(tachyonStore.springConnectionDetails);
}
isOpen.value = false;
}
function onCancel() {
console.log("Ignoring rejoin battle prompt.");
isOpen.value = false;
}
</script>

<style lang="scss" scoped></style>
14 changes: 6 additions & 8 deletions src/renderer/components/maps/MapSimplePreview.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { useImageBlobUrlCache } from "@renderer/composables/useImageBlobUrlCache
import vStartBox from "@renderer/directives/vStartBox";
import vStartPos from "@renderer/directives/vStartPos";
import { computed, defineComponent } from "vue";
import defaultMiniMap from "/src/renderer/assets/images/default-minimap.png?url";

defineComponent({
directives: {
Expand All @@ -25,17 +26,14 @@ defineComponent({
});

const props = defineProps<{
map: MapData;
map: MapData | undefined;
}>();

const { get } = useImageBlobUrlCache();
const cache = useImageBlobUrlCache();

const mapTextureUrl = computed(() => {
if (!props.map?.images) {
return;
}
return get(props.map?.springName, props.map?.imagesBlob?.preview);
});
const mapTextureUrl = computed(() =>
props.map?.imagesBlob?.preview ? cache.get(props.map?.springName, props.map?.imagesBlob?.preview) : defaultMiniMap
);
</script>

<style lang="scss" scoped>
Expand Down
38 changes: 38 additions & 0 deletions src/renderer/components/misc/ActiveBattleVideo.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<!--
SPDX-FileCopyrightText: 2026 The BAR Lobby Authors

SPDX-License-Identifier: MIT
-->

<template>
<div>
<span class="rejoin-label">{{ "Rejoin" }}</span>
<video :src="video" autoplay muted loop></video>
</div>
</template>

<script lang="ts" setup>
import { miscVideos } from "@renderer/assets/assetFiles";

const video = miscVideos["./videos/misc/active_battle.mkv"];
</script>

<style lang="scss" scoped>
:deep(> div) {
width: 100%;
}

video {
display: block;
width: 100%;
height: auto;
}

.rejoin-label {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
text-align: center;
}
</style>
Loading
Loading