Skip to content

Commit

Permalink
fix outdated mod being installed again, improve updating in general
Browse files Browse the repository at this point in the history
  • Loading branch information
Raicuparta committed Dec 14, 2023
1 parent 0128f73 commit 482760b
Show file tree
Hide file tree
Showing 8 changed files with 131 additions and 82 deletions.
2 changes: 1 addition & 1 deletion backend/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ async fn start_game_exe(game_id: &str, handle: AppHandle) -> Result {

#[tauri::command]
#[specta::specta]
async fn install_mod(mod_id: &str, game_id: &str, handle: AppHandle) -> Result {
async fn install_mod(game_id: &str, mod_id: &str, handle: AppHandle) -> Result {
let state = handle.app_state();

let mut installed_games = state.installed_games.get_data()?;
Expand Down
4 changes: 2 additions & 2 deletions frontend/api/bindings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ export function openGameFolder(gameId: string) {
return invoke()<null>("open_game_folder", { gameId })
}

export function installMod(modId: string, gameId: string) {
return invoke()<null>("install_mod", { modId,gameId })
export function installMod(gameId: string, modId: string) {
return invoke()<null>("install_mod", { gameId,modId })
}

export function uninstallMod(gameId: string, modId: string) {
Expand Down
94 changes: 94 additions & 0 deletions frontend/components/installed-games/game-mod-button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { Tooltip } from "@mantine/core";
import {
ModLoaderData,
downloadMod,
installMod,
uninstallMod,
} from "@api/bindings";
import { CommandButton } from "@components/command-button";
import { IconTool, IconTrash } from "@tabler/icons-react";
import { UnifiedMod } from "@hooks/use-unified-mods";
import { getIsOutdated } from "../../util/is-outdated";
import { OutdatedMarker } from "@components/OutdatedMarker";
import { ProcessedInstalledGame } from "@hooks/use-processed-installed-games";
import { useCallback } from "react";
import { MutedText } from "@components/muted-text";

type Props = {
readonly game: ProcessedInstalledGame;
readonly mod: UnifiedMod;
readonly modLoader: ModLoaderData;
};

export function GameModButton(props: Props) {
const installedVersion = props.game.installedModVersions[props.mod.common.id];
const isInstalledModOutdated = getIsOutdated(
installedVersion,
props.mod.remote?.latestVersion?.id,
);
const isLocalModOutdated = getIsOutdated(
props.mod.local?.manifest?.version,
props.mod.remote?.latestVersion?.id,
);
const isInstalled = Boolean(installedVersion);

const handleClick = useCallback(async () => {
if (isLocalModOutdated) {
await downloadMod(props.mod.common.id);
} else if (isInstalled && !isInstalledModOutdated) {
await uninstallMod(props.game.id, props.mod.common.id);
return;
}

await installMod(props.game.id, props.mod.common.id);
}, [
isInstalled,
isLocalModOutdated,
isInstalledModOutdated,
props.game.id,
props.mod.common.id,
]);

const versionText =
!isInstalled || isInstalledModOutdated
? props.mod.remote?.latestVersion?.id
: installedVersion;

function getActionText() {
if (isInstalledModOutdated) return "Update";
if (isInstalled) return "Uninstall";
if (props.modLoader.kind === "Installable") return "Install";
return "Run";
}

function getIcon() {
if (isInstalledModOutdated) return <OutdatedMarker />;
if (isInstalled) return <IconTrash />;
return <IconTool />;
}

return (
<Tooltip
disabled={!isInstalledModOutdated}
label="Mod outdated. Click to update."
key={props.mod.common.id}
>
<CommandButton
leftSection={getIcon()}
confirmationText={
isInstalled
? undefined
: "Attention: be careful when installing mods on multiplayer games! Anticheat can detect some mods and get you banned, even if the mods seem harmless."
}
confirmationSkipId={isInstalled ? undefined : "install-mod-confirm"}
onClick={handleClick}
>
{getActionText()} {props.mod.remote?.title ?? props.mod.common.id}
{versionText && <MutedText>({versionText})</MutedText>}
{!props.game.executable.engine && (
<MutedText>({props.mod.common.engine})</MutedText>
)}
</CommandButton>
</Tooltip>
);
}
81 changes: 10 additions & 71 deletions frontend/components/installed-games/installed-game-modal.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import { Flex, Modal, Stack, Text, Tooltip } from "@mantine/core";
import { Flex, Modal, Stack } from "@mantine/core";
import {
installMod,
openGameFolder,
openGameModsFolder,
refreshGame,
removeGame,
startGame,
startGameExe,
uninstallMod,
} from "@api/bindings";
import { useMemo } from "react";
import { ItemName } from "../item-name";
Expand All @@ -21,7 +19,6 @@ import {
IconPlayerPlay,
IconRefresh,
IconShoppingBag,
IconTool,
IconTrash,
} from "@tabler/icons-react";
import { steamCommands } from "../../util/steam";
Expand All @@ -31,11 +28,10 @@ import { modLoadersAtom } from "@hooks/use-data";
import { CommandButtonGroup } from "@components/command-button-group";
import { DebugData } from "@components/debug-data";
import { useUnifiedMods } from "@hooks/use-unified-mods";
import { isOutdated } from "../../util/is-outdated";
import { installedGamesColumns } from "./installed-games-columns";
import { TableItemDetails } from "@components/table/table-item-details";
import { OutdatedMarker } from "@components/OutdatedMarker";
import { ProcessedInstalledGame } from "@hooks/use-processed-installed-games";
import { GameModButton } from "./game-mod-button";

type Props = {
readonly game: ProcessedInstalledGame;
Expand Down Expand Up @@ -151,71 +147,14 @@ export function InstalledGameModal(props: Props) {
label={modLoader.id.toUpperCase()}
key={modLoader.id}
>
{/* TODO: these buttons could be extracted to a separate component, lots of stuff happening. */}
{modLoader.mods.map((mod) => {
const installedVersion =
props.game.installedModVersions[mod.common.id];
const outdated = isOutdated(
installedVersion,
mod.remote?.latestVersion?.id,
);

return installedVersion ? (
<Tooltip
disabled={!outdated}
label="Mod outdated. Reinstall it to update."
key={mod.common.id}
>
<CommandButton
leftSection={
outdated ? <OutdatedMarker /> : <IconTrash />
}
onClick={() =>
uninstallMod(props.game.id, mod.common.id)
}
>
Uninstall {mod.remote?.title ?? mod.common.id}{" "}
<Text
opacity={0.5}
ml="xs"
size="xs"
>
({installedVersion})
</Text>
</CommandButton>
</Tooltip>
) : (
<CommandButton
leftSection={<IconTool />}
key={mod.common.id}
confirmationText="Attention: be careful when installing mods on multiplayer games! Anticheat can detect some mods and get you banned, even if the mods seem harmless."
confirmationSkipId="install-mod-confirm"
onClick={() => installMod(mod.common.id, props.game.id)}
>
{modLoader.kind === "Installable" ? "Install" : "Run"}{" "}
{mod.remote?.title ?? mod.common.id}
{!props.game.executable.engine && (
// TODO this text to separate component, it's used in multiple places.
<Text
opacity={0.5}
ml="xs"
size="xs"
>
({mod.common.engine})
</Text>
)}
{mod.remote?.latestVersion && (
<Text
opacity={0.5}
ml="xs"
size="xs"
>
({mod.remote.latestVersion?.id})
</Text>
)}
</CommandButton>
);
})}
{modLoader.mods.map((mod) => (
<GameModButton
key={mod.common.id}
game={props.game}
mod={mod}
modLoader={modLoader}
/>
))}
</CommandButtonGroup>
),
)}
Expand Down
10 changes: 5 additions & 5 deletions frontend/components/mods/mod-version-badge.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Badge, DefaultMantineColor, Stack, Tooltip } from "@mantine/core";
import { isOutdated } from "../../util/is-outdated";
import { getIsOutdated } from "../../util/is-outdated";
import { OutdatedMarker } from "@components/OutdatedMarker";

type Props = {
Expand All @@ -9,15 +9,15 @@ type Props = {

function getColor(props: Props): DefaultMantineColor {
if (!props.localVersion) return "gray";
if (isOutdated(props.localVersion, props.remoteVersion)) return "orange";
if (getIsOutdated(props.localVersion, props.remoteVersion)) return "orange";
return "green";
}

export function ModVersionBadge(props: Props) {
const outdated = isOutdated(props.localVersion, props.remoteVersion);
const isOutdated = getIsOutdated(props.localVersion, props.remoteVersion);
return (
<Tooltip
disabled={!outdated}
disabled={!isOutdated}
label="Mod outdated. Re-download it to update."
>
<Stack
Expand All @@ -27,7 +27,7 @@ export function ModVersionBadge(props: Props) {
<Badge color={getColor(props)}>
{props.localVersion || props.remoteVersion || "-"}
</Badge>
{outdated && <OutdatedMarker />}
{isOutdated && <OutdatedMarker />}
</Stack>
</Tooltip>
);
Expand Down
16 changes: 16 additions & 0 deletions frontend/components/muted-text.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Text } from "@mantine/core";

type Props = {
readonly children: React.ReactNode;
};

export function MutedText(props: Props) {
return (
<Text
size="sm"
opacity={0.5}
>
{props.children}
</Text>
);
}
4 changes: 2 additions & 2 deletions frontend/hooks/use-processed-installed-games.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useAtomValue } from "jotai";
import { useMemo } from "react";
import { installedGamesAtom } from "./use-data";
import { useUnifiedMods } from "./use-unified-mods";
import { isOutdated } from "../util/is-outdated";
import { getIsOutdated } from "../util/is-outdated";
import { InstalledGame } from "@api/bindings";

type ProcessedInstalledGameRecord = Record<string, ProcessedInstalledGame>;
Expand All @@ -22,7 +22,7 @@ export function useProcessedInstalledGames() {
hasOutdatedMod:
Object.entries(installedGame.installedModVersions).findIndex(
([modId, installedVersion]) =>
isOutdated(
getIsOutdated(
installedVersion,
mods[modId]?.remote?.latestVersion?.id,
),
Expand Down
2 changes: 1 addition & 1 deletion frontend/util/is-outdated.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export function isOutdated(
export function getIsOutdated(
localVersion: string | undefined | null,
remoteVersion: string | undefined | null,
) {
Expand Down

0 comments on commit 482760b

Please sign in to comment.