Skip to content

Commit

Permalink
Fix bugs with mod validation
Browse files Browse the repository at this point in the history
  • Loading branch information
FLSoz committed Mar 12, 2022
1 parent 1f44793 commit c354e54
Show file tree
Hide file tree
Showing 19 changed files with 182 additions and 236 deletions.
3 changes: 2 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ module.exports = {
'no-console': "off",
'@typescript-eslint/no-non-null-assertion': 'off',
'class-methods-use-this': 'warn',
'prefer-destructuring': 'warn'
'prefer-destructuring': 'warn',
'react/require-default-props': 'off'
},
parserOptions: {
ecmaVersion: 2020,
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "terratech-steam-mod-loader",
"productName": "ttsm",
"description": "Mod loader for TerraTech that handles Steam Workshop mod configurations",
"version": "1.0.7",
"version": "1.1.0",
"scripts": {
"build": "concurrently \"npm run build:main\" \"npm run build:renderer\"",
"build:main": "cross-env NODE_ENV=production TS_NODE_TRANSPILE_ONLY=true webpack --config ./.erb/configs/webpack.config.main.prod.ts",
Expand Down
2 changes: 1 addition & 1 deletion release/app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "terratech-steam-mod-loader",
"productName": "ttsm",
"description": "Mod loader for TerraTech that handles Steam Workshop mod configurations",
"version": "1.0.7",
"version": "1.1.0",
"main": "./dist/main/main.js",
"author": {
"name": "FLSoz",
Expand Down
28 changes: 19 additions & 9 deletions src/main/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { autoUpdater } from 'electron-updater';
import log from 'electron-log';
import fs from 'fs';

import querySteam from './steam';
import getWorkshopModDetails from './steam';
import MenuBuilder from './menu';
import { resolveHtmlPath } from './util';
import { ModConfig, Mod, ModCollection } from './model';
Expand All @@ -28,7 +28,7 @@ const isDevelopment = process.env.NODE_ENV === 'development' || process.env.DEBU

export default class AppUpdater {
constructor() {
log.transports.file.level = isDevelopment ? 'info' : 'warn';
log.transports.file.level = isDevelopment ? 'debug' : 'warn';
autoUpdater.logger = log;
autoUpdater.checkForUpdatesAndNotify();
}
Expand Down Expand Up @@ -175,8 +175,16 @@ interface PathParams {
path: string;
}

ipcMain.on('open-mod-steam', async (event, workshopID: string) => {
shell.openExternal(`steam://url/CommunityFilePage/${workshopID}`);
});

ipcMain.on('open-mod-browser', async (event, workshopID: string) => {
shell.openExternal(`https://steamcommunity.com/sharedfiles/filedetails/?id=${workshopID}`);
});

// Read raw app metadata from the given paths
ipcMain.on('read-mod-metadata', async (event, pathParams: PathParams, type, workshopID: BigInt | null) => {
ipcMain.on('read-mod-metadata', async (event, pathParams: PathParams, type, workshopID: string | null) => {
const modPath = path.join(...pathParams.prefixes, pathParams.path);
log.info(`Reading mod metadata for ${modPath}`);
fs.readdir(modPath, { withFileTypes: true }, async (err, files) => {
Expand Down Expand Up @@ -232,6 +240,7 @@ ipcMain.on('read-mod-metadata', async (event, pathParams: PathParams, type, work
// eslint-disable-next-line prefer-destructuring
config.name = matches[1];
}
log.debug(`Found file: ${file.name} under mod path ${modPath}`);
validMod = true;
}
}
Expand All @@ -240,10 +249,10 @@ ipcMain.on('read-mod-metadata', async (event, pathParams: PathParams, type, work

// augment workshop mod with data
let workshopMod: Mod | null = null;
if (workshopID) {
if (validMod && workshopID) {
potentialMod.subscribed = true;
try {
workshopMod = await querySteam(workshopID);
workshopMod = await getWorkshopModDetails(workshopID);
const steamConfig = workshopMod?.config;
if (steamConfig && config) {
const { name } = steamConfig;
Expand All @@ -259,7 +268,9 @@ ipcMain.on('read-mod-metadata', async (event, pathParams: PathParams, type, work
}
}

// log.debug(JSON.stringify(potentialMod, null, 2));
if (validMod) {
log.debug(JSON.stringify(potentialMod, null, 2));
}
event.reply('mod-metadata-results', validMod ? potentialMod : null);
}
});
Expand Down Expand Up @@ -401,9 +412,8 @@ ipcMain.handle('launch-game', async (_event, workshopID, closeOnLaunch, args) =>
});

// Handle querying steam and parsing the result for a mod page
ipcMain.handle('query-steam', async (_event, workshopID) => {
const mod = await querySteam(workshopID);
return mod;
ipcMain.handle('query-steam-subscribed', async (_event, steamID) => {
return [];
});

// Write a json file to a certain location
Expand Down
2 changes: 1 addition & 1 deletion src/main/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export interface Mod {
type: ModType;
ID: string;
UID: string;
WorkshopID: BigInt | null;
WorkshopID: string | null;
config?: ModConfig;
subscribed?: boolean;
}
Expand Down
6 changes: 4 additions & 2 deletions src/main/preload.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ const log = require('electron-log');

const validChannels = [
'game-running',
'query-steam',
'query-steam-subscribed',
'launch-game',
'read-file',
'write-file',
Expand All @@ -27,7 +27,9 @@ const validChannels = [
'read-collections-list',
'update-collection',
'select-path',
'select-path-result'
'select-path-result',
'open-mod-browser',
'open-mod-steam'
];

contextBridge.exposeInMainWorld('electron', {
Expand Down
12 changes: 5 additions & 7 deletions src/main/steam.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,16 @@ import axios from 'axios';
import { Url } from 'url';
import log from 'electron-log';
import { ModType, ModConfig, Mod } from './model';
import Steamworks from './steamworks';

interface Author {
id: string;
name: string;
}

// Parse Steam Workshop page into relevant details
function parsePage(mod: HTMLElement, workshopID: BigInt): Mod | null {
function parseModPage(mod: HTMLElement, workshopID: string): Mod | null {
const ttModSearch = mod.querySelector('.apphub_AppName');
if (ttModSearch && ttModSearch.text === 'TerraTech') {
if (ttModSearch && ttModSearch.text.toLowerCase().includes('terratech')) {
const resultMod: Mod = {
UID: `workshop:${workshopID}`,
ID: workshopID.toString(),
Expand Down Expand Up @@ -69,6 +68,7 @@ function parsePage(mod: HTMLElement, workshopID: BigInt): Mod | null {
} else {
modConfig.hasCode = true;
}
// log.debug(`Declaring workshop item ${workshopID} a mod since it has tags: ${JSON.stringify(tagsMap, null, 2)}`);
} else {
isMod = false;
}
Expand Down Expand Up @@ -244,12 +244,10 @@ function updateRate(rate: number) {

const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
// get the result from steam
export default async function querySteam(id: BigInt): Promise<Mod | null> {
export default async function getWorkshopModDetails(id: string): Promise<Mod | null> {
const response = await axios.get(`https://steamcommunity.com/sharedfiles/filedetails/?id=${id.toString()}`);
await delay(0.5);
log.debug(`Got steam results for ${id}`);
const mod: HTMLElement = parse(response.data.toString());
return parsePage(mod, id);
return parseModPage(mod, id);
}

// https://steamcommunity.com/id/flsoz/myworkshopfiles?browsefilter=mysubscriptions&sortmethod=subscriptiondate&section=items&appid=285920&requiredtags%5B0%5D=Mods&p=1&numperpage=30
24 changes: 5 additions & 19 deletions src/renderer/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,23 @@ import React, { Component } from 'react';
import { Layout } from 'antd';

import { AppState } from 'renderer/model/AppState';
import {
Outlet,
useLocation,
Location,
useNavigate,
NavigateFunction,
} from 'react-router-dom';
import { Outlet, useLocation, Location, useNavigate, NavigateFunction } from 'react-router-dom';
import MenuBar from './views/components/MenuBar';
import { Mod } from './model/Mod';
import { ModCollection } from './model/ModCollection';
import { DEFAULT_CONFIG } from './model/AppConfig';

const { Sider } = Layout;

class App extends Component<
{ location: Location; navigate: NavigateFunction },
AppState
> {
class App extends Component<{ location: Location; navigate: NavigateFunction }, AppState> {
constructor(props: { location: Location; navigate: NavigateFunction }) {
super(props);
this.state = {
config: DEFAULT_CONFIG,
userDataPath: '',
targetPathAfterLoad: '/collections/main',
mods: new Map<string, Mod>(),
workshopToModID: new Map<string, string>(),
allCollections: new Map<string, ModCollection>(),
allCollectionNames: new Set<string>(),
activeCollection: undefined,
Expand All @@ -40,7 +32,7 @@ class App extends Component<
savingConfig: false,
configErrors: {},
updateState: this.updateState.bind(this),
navigate: this.navigate.bind(this),
navigate: this.navigate.bind(this)
};
}

Expand Down Expand Up @@ -69,13 +61,7 @@ class App extends Component<
}

render() {
const {
launchingGame,
sidebarCollapsed,
savingConfig,
madeConfigEdits,
configErrors,
} = this.state;
const { launchingGame, sidebarCollapsed, savingConfig, madeConfigEdits, configErrors } = this.state;
const { location } = this.props;
return (
<div style={{ display: 'flex', width: '100%', height: '100%' }}>
Expand Down
2 changes: 1 addition & 1 deletion src/renderer/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import CollectionManagerComponent from './views/components/CollectionManagerComp
import MainCollectionComponent from './views/components/MainCollectionComponent';
import RawCollectionComponent from './views/components/RawCollectionComponent';

const rootElement = document.getElementById("root");
const rootElement = document.getElementById('root');
render(
<Router>
<Routes>
Expand Down
14 changes: 12 additions & 2 deletions src/renderer/model/Api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { Mod } from './Mod';
/* eslint-disable @typescript-eslint/ban-types */
export enum ValidChannel {
GAME_RUNNING = 'game-running',
QUERY_STEAM = 'query-steam',
QUERY_STEAM_SUBSCRIBED = 'query-steam-subscribed',
LAUNCH_GAME = 'launch-game',
WRITE_FILE = 'write-file',
READ_FILE = 'read-file',
Expand All @@ -30,7 +30,9 @@ export enum ValidChannel {
READ_COLLECTIONS = 'read-collections-list',
UPDATE_COLLECTION = 'update-collection',
SELECT_PATH = 'select-path',
SELECT_PATH_RESULT = 'select-path-result'
SELECT_PATH_RESULT = 'select-path-result',
OPEN_MOD_BROWSER = 'open-mod-browser',
OPEN_MOD_STEAM = 'open-mod-steam'
}

interface ElectronInterface {
Expand Down Expand Up @@ -233,5 +235,13 @@ class API {
renameCollection(collection: ModCollection, newName: string): Promise<boolean> {
return ipcRenderer.invoke(ValidChannel.RENAME_COLLECTION, collection, newName);
}

openModBrowser(workshopID: string) {
ipcRenderer.send(ValidChannel.OPEN_MOD_BROWSER, workshopID);
}

openModSteam(workshopID: string) {
ipcRenderer.send(ValidChannel.OPEN_MOD_STEAM, workshopID);
}
}
export const api = new API(window);
1 change: 1 addition & 0 deletions src/renderer/model/AppState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export interface AppState {
config: AppConfig;
userDataPath: string;
mods: Map<string, Mod>;
workshopToModID: Map<string, string>;
allCollections: Map<string, ModCollection>;
allCollectionNames: Set<string>;
activeCollection?: ModCollection;
Expand Down
35 changes: 24 additions & 11 deletions src/renderer/model/Mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export interface Mod {
type: ModType;
ID: string;
UID: string;
WorkshopID?: BigInt | null;
WorkshopID?: string;
config?: ModConfig;
subscribed?: boolean;
}
Expand All @@ -45,6 +45,7 @@ export interface ModData {
key: string;
uid: string;
id: string;
workshopId?: string;
type: ModType;
preview?: string;
name: string;
Expand All @@ -60,13 +61,15 @@ export interface ModData {

export function convertToModData(input: Map<string, Mod>): ModData[] {
const dependenciesMap: Map<string, Set<string>> = new Map();
const tempMap: Map<string, ModData> = new Map();
const tempMap: Map<string, ModData[]> = new Map();
const workshopMap: Map<string, string> = new Map();

[...input.values()].forEach((mod: Mod) => {
const modData: ModData = {
key: mod.ID,
uid: mod.UID,
id: mod.WorkshopID ? `${mod.WorkshopID}` : mod.ID,
id: mod.ID,
workshopId: mod.WorkshopID,
type: mod.type,
preview: mod.config?.preview,
name: mod.config && mod.config.name ? mod.config.name : mod.ID,
Expand All @@ -77,9 +80,14 @@ export function convertToModData(input: Map<string, Mod>): ModData[] {
tags: mod.config?.tags,
subscribed: mod.subscribed
};
tempMap.set(mod.ID, modData);
const duplicateMods = tempMap.get(mod.ID);
if (duplicateMods) {
duplicateMods.push(modData);
} else {
tempMap.set(mod.ID, [modData]);
}
if (mod.WorkshopID) {
workshopMap.set(mod.WorkshopID.toString(), mod.ID);
workshopMap.set(mod.WorkshopID, mod.ID);
}
if (modData.dependsOn) {
modData.dependsOn.forEach((dependency) => {
Expand All @@ -94,14 +102,19 @@ export function convertToModData(input: Map<string, Mod>): ModData[] {
});
const missingMods = [];
dependenciesMap.forEach((reliers, dependency) => {
const modData = tempMap.get(dependency);
if (modData) {
modData.isDependencyFor = [...reliers];
} else {
missingMods.push(dependency);
const modID = workshopMap.get(dependency);
if (modID) {
const duplicateMods = tempMap.get(modID);
if (duplicateMods) {
duplicateMods.forEach((modData: ModData) => {
modData.isDependencyFor = [...reliers];
});
} else {
missingMods.push(dependency);
}
}
});
return [...tempMap.values()];
return [...tempMap.values()].flat();
}

export function filterRows(rows: ModData[], searchString: string | undefined): ModData[] {
Expand Down
Loading

0 comments on commit c354e54

Please sign in to comment.