Skip to content

Commit

Permalink
Add item overrides, fix some network bugs.
Browse files Browse the repository at this point in the history
  • Loading branch information
phulin committed Aug 27, 2024
1 parent c798b93 commit 74c7c2a
Show file tree
Hide file tree
Showing 13 changed files with 155 additions and 116 deletions.
23 changes: 0 additions & 23 deletions client/src/api/function.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
// Needed for DataLoader.
import "setimmediate";

import DataLoader from "dataloader";
import type * as kolmafia from "kolmafia";

import { apiCall } from "./base";

export function batchFunction(
Expand All @@ -27,23 +24,3 @@ export function batchFunction(
}),
);
}

const functionsLoader = new DataLoader(batchFunction, {
batchScheduleFn: (callback) => setTimeout(callback, 50),
});

function callFunctionInternal<T>(name: string, args: unknown[]): Promise<T> {
return functionsLoader.load({ name, args }).then((value) => value as T);
}

export const call = new Proxy(
{} as {
[K in keyof typeof kolmafia]: (typeof kolmafia)[K];
},
{
get(target, property) {
if (typeof property === "symbol") return undefined;
return (...args: unknown[]) => callFunctionInternal(property, args);
},
},
);
29 changes: 18 additions & 11 deletions client/src/contexts/RefreshContext.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import {
myAdventures,
myFamiliar,
myHash,
myHp,
myMeat,
myMp,
myTurncount,
} from "kolmafia";
import React, { createContext, ReactNode, useEffect, useState } from "react";

import { call } from "../api/function";
import useInterval from "../hooks/useInterval";
import { markRemoteCallCacheDirty } from "../kolmafia/remote";

Expand All @@ -15,16 +23,15 @@ const RefreshContext = createContext({
});

async function getCharacterState() {
const [myTurncount, myMeat, myHp, myMp, myFamiliar, myAdventures] =
await Promise.all([
call.myTurncount(),
call.myMeat(),
call.myHp(),
call.myMp(),
call.myFamiliar().name,
call.myAdventures(),
]);
return { myTurncount, myMeat, myHp, myMp, myFamiliar, myAdventures };
return {
myHash: myHash(),
myTurncount: myTurncount(),
myMeat: myMeat(),
myHp: myHp(),
myMp: myMp(),
myFamiliar: myFamiliar(),
myAdventures: myAdventures(),
};
}

type CharacterState = Awaited<ReturnType<typeof getCharacterState>>;
Expand Down
20 changes: 16 additions & 4 deletions client/src/kolmafia/remote.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ function processOverrides<T>(value: T): { overrideApplied: boolean; value: T } {
value.objectType === "Location" &&
value.identifierString !== undefined
) {
const overrideValue = localStorage.getItem(value.identifierString);
const overrideValue = localStorage.getItem(
`$location[${value.identifierString}].turns_spent`,
);
if (overrideValue !== null) {
return {
overrideApplied: true,
Expand Down Expand Up @@ -98,10 +100,20 @@ export function remoteCall<T>(
if (
!ignoreOverrides &&
name === "getProperty" &&
typeof args[0] === "string"
typeof firstArg === "string"
) {
const override = localStorage.getItem(args[0]);
if (override !== null) return override as unknown as T;
const override = localStorage.getItem(firstArg);
if (override !== null) return override as T;
} else if (
!ignoreOverrides &&
name === "availableAmount" &&
typeof firstArg === "object" &&
isIdentified(firstArg)
) {
const override = localStorage.getItem(
`available_amount($item[${firstArg.identifierString}])`,
);
if (override !== null) return parseInt(override) as T;
} else if (
name === "toInt" &&
firstArg !== null &&
Expand Down
39 changes: 16 additions & 23 deletions client/src/kolmafia/singletonize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,21 +96,19 @@ export function transformPropertyNames<T>(object: T): T {
export function serialize<T>(object: T): Partial<T> {
if (Array.isArray(object)) {
return object.map((item) => serialize(item)) as T;
} else if (isPOJO(object)) {
if (isIdentified(object)) {
const result = {} as Identified<PlaceholderTypes>;
result.objectType = object.objectType;
if (object.identifierNumber && object.identifierNumber >= 0) {
result.identifierNumber = object.identifierNumber;
} else {
result.identifierString = object.identifierString;
}
return result as T;
} else if (isIdentified(object)) {
const result = {} as Identified<PlaceholderTypes>;
result.objectType = object.objectType;
if (object.identifierNumber && object.identifierNumber >= 0) {
result.identifierNumber = object.identifierNumber;
} else {
return Object.fromEntries(
Object.entries(object).map(([key, value]) => [key, serialize(value)]),
) as T;
result.identifierString = object.identifierString;
}
return result as T;
} else if (isPOJO(object)) {
return Object.fromEntries(
Object.entries(object).map(([key, value]) => [key, serialize(value)]),
) as T;
} else return object;
}

Expand All @@ -122,16 +120,11 @@ export function serialize<T>(object: T): Partial<T> {
export default function singletonize<T>(object: T): T {
if (Array.isArray(object)) {
return object.map((item) => singletonize(item)) as T;
} else if (isIdentified(object)) {
return cacheIdentified(object) as T;
} else if (isPOJO(object)) {
if (isIdentified(object)) {
return cacheIdentified(object) as T;
} else {
return Object.fromEntries(
Object.entries(object).map(([key, value]) => [
key,
singletonize(value),
]),
) as T;
}
return Object.fromEntries(
Object.entries(object).map(([key, value]) => [key, singletonize(value)]),
) as T;
} else return object;
}
108 changes: 74 additions & 34 deletions client/src/prefs/Layout.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,82 @@
import { Container, Heading, Stack, Table, Tbody } from "@chakra-ui/react";
import { Location } from "kolmafia";
import { Location, toItem } from "kolmafia";
import { KnownProperty } from "libram";
import { ChangeEvent, useCallback, useContext, useState } from "react";

import RefreshContext from "../contexts/RefreshContext";
import { remoteCall } from "../kolmafia/remote";
import items from "./items.json";
import locations from "./locations.json";
import OverrideRow from "./OverrideRow";
import preferences from "./preferences.json";
import ValidatedInput from "./ValidatedInput";

interface OverrideTableProps {
filterRegex: RegExp | null;
}

const PreferencesTable: React.FC<OverrideTableProps> = ({ filterRegex }) => (
<Table size="sm">
<Tbody>
{(preferences as KnownProperty[])
.filter((p) => !filterRegex || filterRegex.test(p))
.map((property) => (
<OverrideRow
key={property}
override={property}
current={remoteCall("getProperty", [property], "", true)}
/>
))}
</Tbody>
</Table>
);

const LocationsTable: React.FC<OverrideTableProps> = ({ filterRegex }) => (
<Table size="sm">
<Tbody>
{locations
.filter((l) => !filterRegex || filterRegex.test(l))
.map((location) => (
<OverrideRow
key={`$location[${location}].turns_spent`}
override={`$location[${location}].turns_spent`}
label={location}
current={
remoteCall<Location>(
"toLocation",
[location],
{} as Location,
true,
).turnsSpent?.toString() ?? ""
}
/>
))}
</Tbody>
</Table>
);

const ItemsTable: React.FC<OverrideTableProps> = ({ filterRegex }) => (
<Table size="sm">
<Tbody>
{items
.filter((l) => !filterRegex || filterRegex.test(l))
.map((item) => (
<OverrideRow
key={`available_amount($item[${item}])`}
override={`available_amount($item[${item}])`}
label={item}
current={remoteCall<number>(
"availableAmount",
[toItem(item)],
0,
true,
).toString()}
/>
))}
</Tbody>
</Table>
);

const Layout = () => {
useContext(RefreshContext);
const [filter, setFilter] = useState("");
Expand Down Expand Up @@ -40,44 +107,17 @@ const Layout = () => {
<Heading as="h2" size="md" textAlign="center">
Preferences
</Heading>
<Table size="sm">
<Tbody>
{(preferences as KnownProperty[])
.filter((p) => !filterRegex || filterRegex.test(p))
.map((property) => (
<OverrideRow
key={property}
override={property}
current={remoteCall("getProperty", [property], "", true)}
/>
))}
</Tbody>
</Table>
<PreferencesTable filterRegex={filterRegex} />
</Stack>
<Stack>
<Heading as="h2" size="md" textAlign="center">
Turns Spent
</Heading>
<Table size="sm">
<Tbody>
{locations
.filter((l) => !filterRegex || filterRegex.test(l))
.map((location) => (
<OverrideRow
key={location}
override={location}
current={
remoteCall<Location>(
"toLocation",
[location],
{} as Location,
true,
).turnsSpent?.toString() ?? ""
}
/>
))}
</Tbody>
</Table>
<LocationsTable filterRegex={filterRegex} />
<Heading as="h2" size="md" textAlign="center">
Items
</Heading>
<ItemsTable filterRegex={filterRegex} />
</Stack>
</Stack>
</Stack>
Expand Down
15 changes: 10 additions & 5 deletions client/src/prefs/OverrideRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,21 @@ import { Td, Text, Tr } from "@chakra-ui/react";
import { ChangeEvent, useCallback } from "react";
import React from "react";

import { Override, validityType, validValue } from "./valid";
import { validityType, validValue } from "./valid";
import ValidatedInput from "./ValidatedInput";

// override can be the name of a location, in which case it's turnsSpent there.
// override is the key in localStorage
interface OverrideRowProps {
override: Override;
label?: string;
override: string;
current: string;
}

const OverrideRow: React.FC<OverrideRowProps> = ({ override, current }) => {
const OverrideRow: React.FC<OverrideRowProps> = ({
label,
override,
current,
}) => {
const [value, setValue] = React.useState(
localStorage.getItem(override) ?? "",
);
Expand Down Expand Up @@ -42,7 +47,7 @@ const OverrideRow: React.FC<OverrideRowProps> = ({ override, current }) => {
<Tr>
<Td>
<Text textAlign="right" my="auto">
{override}
{label ?? override}
</Text>
</Td>
<Td>
Expand Down
6 changes: 6 additions & 0 deletions client/src/prefs/items.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[
"boring binder clip",
"bowling ball",
"McClusky file (complete)",
"Shore Inc. Ship Trip Scrip"
]
13 changes: 6 additions & 7 deletions client/src/prefs/valid.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,21 @@ import {
isBooleanProperty,
isNumericOrStringProperty,
isNumericProperty,
KnownProperty,
} from "libram";

import locations from "./locations.json";

export type Override = KnownProperty | (typeof locations)[number];

export type ValidityType =
| "string"
| "number"
| "boolean"
| "string | number"
| "quest";

export function validityType(override: Override): ValidityType {
if (isNumericProperty(override) || locations.includes(override)) {
export function validityType(override: string): ValidityType {
if (
isNumericProperty(override) ||
/^\$location\[.*\].turns_spent$/.test(override) ||
/$available_amount\(\$item\[.*\]\)/.test(override)
) {
return "number";
} else if (isBooleanProperty(override)) {
return "boolean";
Expand Down
2 changes: 1 addition & 1 deletion client/src/sections/quests/level11/HiddenCity.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ const Apartment = () => {
const Office = () => {
const step = questStep("questL11Business");
const haveClip =
have($item`boring binder clip`) && !have($item`McClusky file (complete)`);
have($item`boring binder clip`) || have($item`McClusky file (complete)`);
const needToUseClip =
have($item`boring binder clip`) && have($item`McClusky file (page 5)`);
const files = [
Expand Down
4 changes: 2 additions & 2 deletions client/src/sections/resources/Cartography.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ListItem } from "@chakra-ui/react";
import { myLevel, toLocation } from "kolmafia";
import { Location, myLevel } from "kolmafia";
import { $item, $skill, get, have } from "libram";

import BulletedList from "../../components/BulletedList";
Expand Down Expand Up @@ -83,7 +83,7 @@ const MapTargetItem: React.FC<MapTargetProps> = ({ target }) => {
return (
<ListItem key={target.monster}>
<MainLink
href={parentPlaceLink(toLocation(target.zone))}
href={parentPlaceLink(Location.get(target.zone))}
>{`${target.monster} @ ${target.zone}`}</MainLink>
</ListItem>
);
Expand Down
Binary file modified server/.yarn/install-state.gz
Binary file not shown.
Loading

0 comments on commit 74c7c2a

Please sign in to comment.