Skip to content
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
bd18fab
Signals for user/self to pass received data
Hectate Aug 23, 2026
713e673
Party state recovery
Hectate Aug 23, 2026
9718d08
lobby and matchmaking recovery
Hectate Aug 23, 2026
8833b28
Mild cleanups
Hectate Aug 23, 2026
125bcc4
battle start tweaks
Hectate Aug 23, 2026
48209e7
Wired up rejoin battle confirmation
Hectate Aug 23, 2026
dfd85d2
Watch for battle ending on user's updated event
Hectate Aug 23, 2026
f6f206d
Added in-battle UI indicator and navigation
Hectate Aug 24, 2026
bb1b838
Push user to matchmaking view
Hectate Aug 24, 2026
103833d
Some cleanups
Hectate Aug 24, 2026
7e30a44
Tweaks
Hectate Aug 24, 2026
2298db9
Fixed tests
Hectate Aug 24, 2026
b7df4fe
user.currentBattle is correct now
Hectate Aug 24, 2026
3249891
ported an old fix that was never applied
Hectate Aug 25, 2026
bf7caab
Fix party update
Hectate Aug 26, 2026
22f869f
Translation strings added
Hectate Aug 26, 2026
e251920
null/undefined state handling
Hectate Aug 26, 2026
828b6ce
Tweaks for null/undefined objects
Hectate Aug 27, 2026
5d9e156
format script run
Hectate Aug 27, 2026
aac9dad
Merge remote-tracking branch 'upstream/master' into reconnect-stores
Hectate Aug 27, 2026
1a43acf
Added mock router functions
Hectate Aug 27, 2026
2d261f2
Merge remote-tracking branch 'upstream/master' into reconnect-stores
Hectate Aug 27, 2026
a9302a6
More mocks for new tests
Hectate Aug 27, 2026
c5cc107
fixed micro-milliseconds conversion
Hectate Aug 27, 2026
81de6ae
Handled clearing battle states in more places
Hectate Aug 27, 2026
3bf4b94
Reminder for later
Hectate Aug 27, 2026
bdceffa
Tests to cover missing server implementation
Hectate Aug 27, 2026
a233f52
Fixes from review
Hectate Aug 28, 2026
2370875
more review tweaks
Hectate Aug 28, 2026
d149931
Store init guards per review
Hectate Aug 28, 2026
1ed7289
Lobby chat history preservation
Hectate Aug 28, 2026
f0dce7f
Implement requestStructured
Hectate Aug 29, 2026
0a1d607
removed comment
Hectate Aug 29, 2026
1ac480e
Swapped signals for in-store listeners
Hectate Sep 1, 2026
3733af3
Refactored chats to use maps for all types
Hectate Sep 1, 2026
3f08558
requestJoinLobby explicit parameter names
Hectate Sep 1, 2026
0dfa43f
Tachyon version bumped to 1.24.0
Hectate Sep 1, 2026
2f3fc49
Explicit "not_in_lobby" error handling
Hectate Sep 1, 2026
ee46603
Added "rejoin" text to animation
Hectate Sep 1, 2026
da45da2
Updated tachyon references
Hectate Sep 2, 2026
a84cad0
Cleaner matchmaking update, tests
Hectate Sep 2, 2026
9316e82
Temporary backwards compat
Hectate Sep 2, 2026
c55c6ec
updated IP checks
Hectate Sep 3, 2026
b03664c
More back compat fixes
Hectate Sep 3, 2026
ddf6cbf
Merge remote-tracking branch 'upstream/master' into reconnect-stores
Hectate Sep 3, 2026
1e399cd
log.info > log.debug
Hectate Sep 3, 2026
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
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
4 changes: 1 addition & 3 deletions src/main/tachyon/tachyon.handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,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
2 changes: 2 additions & 0 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
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"
Comment thread
Hectate marked this conversation as resolved.
}
},
"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() {
Comment thread
Hectate marked this conversation as resolved.
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";
Comment thread
Hectate marked this conversation as resolved.

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
24 changes: 24 additions & 0 deletions src/renderer/components/misc/ActiveBattleVideo.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<!--
SPDX-FileCopyrightText: 2026 The BAR Lobby Authors

SPDX-License-Identifier: MIT
-->

<template>
<video :src="video" autoplay muted loop></video>
</template>

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

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

<style lang="scss" scoped>
video {
max-width: 100%;
max-height: 200px;
height: auto;
object-fit: fill;
}
</style>
36 changes: 34 additions & 2 deletions src/renderer/components/navbar/NavBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@ SPDX-License-Identifier: MIT
<template>
<div class="nav" :class="{ hidden }">
<div class="logo">
<Button to="/play/menu?">
<img src="/src/renderer/assets/images/logo.svg" />
<Button @click="onLogoClick">
<div class="logo-container">
<img src="/src/renderer/assets/images/logo.svg" :class="{ dim: tachyonStore.springConnectionDetails }" />
<ActiveBattleVideo class="battle-video" v-if="tachyonStore.springConnectionDetails" />
</div>
</Button>
</div>
<div class="flex-col flex-grow">
Expand Down Expand Up @@ -126,6 +129,8 @@ import { me } from "@renderer/store/me.store";
import ServerStatus from "@renderer/components/navbar/ServerStatus.vue";
import { useLogInConfirmation } from "@renderer/composables/useLogInConfirmation";
import { chatStore } from "@renderer/store/chat.store";
import { tachyonStore } from "@renderer/store/tachyon.store";
import ActiveBattleVideo from "@renderer/components/misc/ActiveBattleVideo.vue";

defineProps<{
hidden?: boolean;
Expand Down Expand Up @@ -173,6 +178,14 @@ function minimizeWindow() {
function toggleFullscreen() {
settingsStore.fullscreen = !settingsStore.fullscreen;
}

function onLogoClick() {
if (tachyonStore.springConnectionDetails) {
tachyonStore.rejoinModalOpen = true;
} else {
router.push("/play");
}
}
Comment thread
Hectate marked this conversation as resolved.
</script>

<style lang="scss" scoped>
Expand Down Expand Up @@ -361,4 +374,23 @@ function toggleFullscreen() {
flex-grow: 1;
-webkit-app-region: drag !important;
}
.logo-container {
position: relative;
display: inline-block;
}
.logo-container img {
display: block;
// max-width: 100%;
// height: auto;
}
.battle-video {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 2;
}
.dim {
filter: brightness(0.2);
}
</style>
Loading
Loading