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
2 changes: 1 addition & 1 deletion .github/workflows/deploy-beta.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ jobs:
- name: Build
run: pnpm build:beta
env:
NODE_ENV: production
NODE_ENV: beta

- name: Set up SSH
run: |
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
"version": "0.1.0",
"type": "module",
"scripts": {
"start": "vite",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should add a start that give you a selection of which mode you wanna launch via prompter.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we need an extra layer of indirection for something so simple.

"start:prod": "vite --mode production",
"start:beta": "vite --mode beta",
"start:dev": "vite --mode development",
"build": "vite build",
"build:beta": "vite build --mode beta",
Expand Down
13 changes: 12 additions & 1 deletion src/constants/app-constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,19 @@ export const RUN_HISTORY_LIMIT: number = 25;
/** The number of save slots available to players. */
export const SAVE_SLOT_LIMIT: number = 5;

/** Whether the app is running in beta (or development) mode. */
/**
* `true` if running in "beta" mode via either of:
* - `pnpm start:beta` (which runs `vite --mode beta`)
* - A build created via `pnpm build:beta`
*/
export const IS_BETA = import.meta.env.MODE === "beta";

/**
* `true` if running in "development" mode via either of:
* - `pnpm start:dev` (which runs `vite --mode development`)
* - A build created via `pnpm build:dev`
*/
export const IS_DEV = import.meta.env.MODE === "development";

/** Whether the app is running in a test environment */
export const IS_TEST = import.meta.env.NODE_ENV === "test";
37 changes: 17 additions & 20 deletions src/main.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import "#app/polyfills"; // polyfills must be first
import "#app/phaser-extensions";

// Catch global errors and display them in an alert so users can report the issue.
import { IS_BETA, IS_DEV } from "#constants/app-constants";

if (IS_DEV || IS_BETA) {
document.title += " (Beta)";
}

window.onerror = (_message, _source, _lineno, _colno, error) => {
console.error(error);
// const errorString = `Received unhandled error. Open browser console and click OK to see details.\nError: ${message}\nSource: ${source}\nLine: ${lineno}\nColumn: ${colno}\nStack: ${error.stack}`;
//alert(errorString);
// Avoids logging the error a second time.
return true;
};

// Catch global promise rejections and display them in an alert so users can report the issue.
window.addEventListener("unhandledrejection", (event) => {
// const errorString = `Received unhandled promise rejection. Open browser console and click OK to see details.\nReason: ${event.reason}`;
console.error(event.reason);
//alert(errorString);
});

document.fonts.load("16px emerald").then(() => document.fonts.load("10px pkmnems"));
Expand All @@ -36,17 +36,14 @@ const startGame = async (manifest?: any) => {
}
};

fetch("/manifest.json")
.then((res) => res.json())
.then((jsonResponse) => {
startGame(jsonResponse.manifest);
})
.catch(() => {
// Manifest not found (likely local build)
startGame();
})
.finally(() => {
if (import.meta.env.MODE === "development") {
import("./dev");
}
});
try {
const json = await (await fetch("/manifest.json")).json();
await startGame(json.manifest);
} catch {
// The manifest wasn't found, likely due to running locally
await startGame();
}

if (IS_DEV) {
await import("./dev");
}
10 changes: 4 additions & 6 deletions src/phases/game-over-phase.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { api } from "#api/api";
import { clientSessionId } from "#app/account";
import { globalScene } from "#app/global-scene";
import { BYPASS_LOGIN } from "#constants/app-constants";
import { getCharVariantFromDialogue } from "#data/dialogue";
import type { PokemonSpecies } from "#data/pokemon-species";
import { AchvCategory } from "#enums/achv-category";
Expand Down Expand Up @@ -200,12 +201,9 @@ export class GameOverPhase extends BattlePhase {
});
};

/**
* Check to see if the game is running offline
* If Online, execute apiFetch as intended
* If Offline, execute offlineNewClear() only for victory, a localStorage implementation of newClear daily run checks
*/
if (!api.isLocal || api.isConnected) {
// If Online, execute `apiFetch` as intended
// If Offline, execute `offlineNewClear()` only for victory, a localStorage implementation of `newClear` daily run checks
if (!BYPASS_LOGIN || api.isConnected) {
api.savedata.session
.newclear({ slot: globalScene.sessionSlotId, isVictory: this.isVictory, clientSessionId })
.then((success) => doGameOver(success));
Expand Down
6 changes: 4 additions & 2 deletions src/phases/title-phase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { loggedInUser } from "#app/account";
import { getGameMode, getModeName } from "#app/game-mode";
import { globalScene } from "#app/global-scene";
import { Phase } from "#app/phase";
import { BYPASS_LOGIN } from "#constants/app-constants";
import { fetchDailyRunSeed, getDailyRunStarters } from "#data/daily-run";
import { GameModes } from "#enums/game-modes";
import { ModifierPoolType } from "#enums/modifier-pool-type";
Expand Down Expand Up @@ -247,8 +248,9 @@ export class TitlePhase extends Phase {
});
};

// If Online, calls seed fetch from db to generate daily run. If Offline, generates a daily run based on current date.
if (!api.isLocal || api.isConnected) {
// If Online, calls seed fetch from db to generate daily run.
// If Offline, generates a daily run based on current date.
if (!BYPASS_LOGIN || api.isConnected) {
fetchDailyRunSeed()
.then((seed) => {
if (seed) {
Expand Down
12 changes: 6 additions & 6 deletions src/plugins/api/account-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,7 @@ import type {
} from "#types/api-types";
import { removeCookie, setCookie } from "#utils/app-utils";

/**
* A wrapper for the account API requests.
*/
/** A wrapper for the account API requests. */
export class AccountApi extends ApiBase {
//#region Public

Expand Down Expand Up @@ -39,7 +37,7 @@ export class AccountApi extends ApiBase {
* @param registerData The {@linkcode AccountRegisterRequest} to send
* @returns An error message if something went wrong
*/
public async register(registerData: AccountRegisterRequest) {
public async register(registerData: AccountRegisterRequest): Promise<string | null> {
try {
const response = await this.doPost("/account/register", registerData, "form-urlencoded");

Expand All @@ -60,7 +58,7 @@ export class AccountApi extends ApiBase {
* @param loginData The {@linkcode AccountLoginRequest} to send
* @returns An error message if something went wrong
*/
public async login(loginData: AccountLoginRequest) {
public async login(loginData: AccountLoginRequest): Promise<string | null> {
try {
const response = await this.doPost("/account/login", loginData, "form-urlencoded");

Expand All @@ -82,7 +80,7 @@ export class AccountApi extends ApiBase {
* Send a logout request.
* **Always** (no matter if failed or not) removes the session cookie.
*/
public async logout() {
public async logout(): Promise<void> {
try {
const response = await this.doGet("/account/logout");

Expand All @@ -95,4 +93,6 @@ export class AccountApi extends ApiBase {

removeCookie(SESSION_ID_COOKIE); // we are always clearing the cookie.
}

//#endregion
}
8 changes: 4 additions & 4 deletions src/plugins/api/admin-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export class AdminApi extends ApiBase {
* @param params The {@linkcode LinkAccountToDiscordIdRequest} to send
* @returns `null` if successful, error message if not
*/
public async linkAccountToDiscord(params: LinkAccountToDiscordIdRequest) {
public async linkAccountToDiscord(params: LinkAccountToDiscordIdRequest): Promise<string | null> {
try {
const response = await this.doPost("/admin/account/discordLink", params, "form-urlencoded");

Expand All @@ -40,7 +40,7 @@ export class AdminApi extends ApiBase {
* @param params The {@linkcode UnlinkAccountFromDiscordIdRequest} to send
* @returns `null` if successful, error message if not
*/
public async unlinkAccountFromDiscord(params: UnlinkAccountFromDiscordIdRequest) {
public async unlinkAccountFromDiscord(params: UnlinkAccountFromDiscordIdRequest): Promise<string | null> {
try {
const response = await this.doPost("/admin/account/discordUnlink", params, "form-urlencoded");

Expand All @@ -64,7 +64,7 @@ export class AdminApi extends ApiBase {
* @param params The {@linkcode LinkAccountToGoogledIdRequest} to send
* @returns `null` if successful, error message if not
*/
public async linkAccountToGoogleId(params: LinkAccountToGoogledIdRequest) {
public async linkAccountToGoogleId(params: LinkAccountToGoogledIdRequest): Promise<string | null> {
try {
const response = await this.doPost("/admin/account/googleLink", params, "form-urlencoded");

Expand All @@ -88,7 +88,7 @@ export class AdminApi extends ApiBase {
* @param params The {@linkcode UnlinkAccountFromGoogledIdRequest} to send
* @returns `null` if successful, error message if not
*/
public async unlinkAccountFromGoogleId(params: UnlinkAccountFromGoogledIdRequest) {
public async unlinkAccountFromGoogleId(params: UnlinkAccountFromGoogledIdRequest): Promise<string | null> {
try {
const response = await this.doPost("/admin/account/googleUnlink", params, "form-urlencoded");

Expand Down
10 changes: 7 additions & 3 deletions src/plugins/api/api-base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,21 @@ export abstract class ApiBase {

protected readonly base: string;

//#endregion
//#region Public

constructor(base: string) {
this.base = base;
}

//#endregion
//#region Protected

/**
* Send a GET request.
* @param path The path to send the request to.
*/
protected async doGet(path: string) {
protected async doGet(path: string): Promise<Response> {
return this.doFetch(path, { method: "GET" });
}

Expand All @@ -32,7 +34,7 @@ export abstract class ApiBase {
* @param bodyData The body-data to send.
* @param dataType The data-type of the {@linkcode bodyData}.
*/
protected async doPost<D = undefined>(path: string, bodyData?: D, dataType: DataType = "json") {
protected async doPost<D = undefined>(path: string, bodyData?: D, dataType: DataType = "json"): Promise<Response> {
let body: string | undefined;
const headers: HeadersInit = {};

Expand Down Expand Up @@ -83,11 +85,13 @@ export abstract class ApiBase {
* @param data the data to transform to {@linkcode URLSearchParams}
* @returns a {@linkcode URLSearchParams} representaton of {@linkcode data}
*/
protected toUrlSearchParams<D extends Record<string, any>>(data: D) {
protected toUrlSearchParams<D extends Record<string, any>>(data: D): URLSearchParams {
const arr = Object.entries(data)
.map(([key, value]) => [key, String(value ?? "")])
.filter(([, value]) => value !== "");

return new URLSearchParams(arr);
}

//#endregion
}
30 changes: 9 additions & 21 deletions src/plugins/api/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@ import { DailyApi } from "#api/daily-api";
import { SavedataApi } from "#api/savedata-api";
import type { TitleStatsResponse } from "#types/api-types";

/**
* A wrapper for API requests.
*/
/** A wrapper for API requests. */
class Api extends ApiBase {
//#region Fields

Expand All @@ -18,11 +16,10 @@ class Api extends ApiBase {
public readonly admin: AdminApi;
public readonly savedata: SavedataApi;

/** Wheter the hostname is 'localhost' or an IP address, and ensure a port is specified. */
private readonly _isLocal: boolean;
/** Whether the server/api is connected. By default we assume `true`. */
private _isConnected: boolean;

//#endregion
//#region Public

constructor(base: string) {
Expand All @@ -31,26 +28,17 @@ class Api extends ApiBase {
this.daily = new DailyApi(base);
this.admin = new AdminApi(base);
this.savedata = new SavedataApi(base);
this._isLocal =
((window.location.hostname === "localhost" || /^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$/.test(window.location.hostname))
&& window.location.port !== "")
|| window.location.hostname === "";
}

/** Whether the server/api is connected. By default we assume `true`. */
public get isConnected() {
public get isConnected(): boolean {
return this._isConnected;
}

/** Wheter the hostname is 'localhost' or an IP address, and ensure a port is specified. */
public get isLocal() {
return this._isLocal;
}

/**
* Request game title-stats.
*/
public async getGameTitleStats() {
public async getGameTitleStats(): Promise<TitleStatsResponse | null> {
if (!this.isConnected) {
this.printServerNotConnectedWarning();
return null;
Expand All @@ -69,7 +57,7 @@ class Api extends ApiBase {
* Unlink the currently logged in user from Discord.
* @returns `true` if unlinking was successful, `false` if not
*/
public async unlinkDiscord() {
public async unlinkDiscord(): Promise<boolean> {
if (!this.isConnected) {
this.printServerNotConnectedWarning();
return false;
Expand All @@ -92,7 +80,7 @@ class Api extends ApiBase {
* Unlink the currently logged in user from Google.
* @returns `true` if unlinking was successful, `false` if not
*/
public async unlinkGoogle() {
public async unlinkGoogle(): Promise<boolean> {
if (!this.isConnected) {
this.printServerNotConnectedWarning();
return false;
Expand All @@ -116,7 +104,7 @@ class Api extends ApiBase {
* @remarks
* We have no dedicated ping/status endpoint yet, so we ping the game title stats endpoint, but without printing any errors by default.
*/
async ping() {
public async ping(): Promise<void> {
try {
const response = await this.doGet("/game/titlestats");
const data = await response.json();
Expand All @@ -129,14 +117,14 @@ class Api extends ApiBase {
}
}
if (import.meta.env.VITE_API_DEBUG === "1") {
console.log("isLocalServerConnected:", this.isConnected);
console.log("`Api#isConnected`:", this.isConnected);
}
}

//#endregion
//#region Private

private printServerNotConnectedWarning() {
private printServerNotConnectedWarning(): void {
if (import.meta.env.VITE_API_DEBUG === "1") {
console.warn(this.ERR_SERVER_NOT_CONNECTED);
}
Expand Down
8 changes: 4 additions & 4 deletions src/plugins/api/daily-api.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
import { ApiBase } from "#api/api-base";

/**
* A wrapper for daily-run API requests.
*/
/** A wrapper for daily-run API requests. */
export class DailyApi extends ApiBase {
//#region Public

/**
* Request the daily-run seed.
* @returns The active daily-run seed as `string`.
*/
public async getSeed() {
public async getSeed(): Promise<string | null> {
try {
const response = await this.doGet("/daily/seed");
return response.text();
Expand All @@ -19,4 +17,6 @@ export class DailyApi extends ApiBase {
return null;
}
}

//#endregion
}
Loading