Skip to content

Commit

Permalink
Add support for registering via the client.
Browse files Browse the repository at this point in the history
  • Loading branch information
Half-Shot committed Jan 16, 2025
1 parent c532485 commit 9ce475d
Show file tree
Hide file tree
Showing 5 changed files with 156 additions and 11 deletions.
1 change: 1 addition & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ jobs:
echo "VITE_BUILD_NUMBER=$((GITHUB_RUN_NUMBER))" >> $GITHUB_ENV
echo "VITE_BUILD_COMMIT=$GITHUB_SHA" >> $GITHUB_ENV
echo "VITE_DEFAULT_HOMESERVER=https://kcore.half-shot.uk" >> $GITHUB_ENV
echo "VITE_REGISTRATION_TOKEN=wg-w1-xooNi3ahzah5" >> $GITHUB_ENV
- name: Set up Node
uses: actions/setup-node@v4
with:
Expand Down
4 changes: 3 additions & 1 deletion src/frontend/components/config.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
interface WormgineClientConfiguration {
defaultHomeserver: string | null;
registrationToken: string | null;
}

const { VITE_DEFAULT_HOMESERVER } = import.meta.env;
const { VITE_DEFAULT_HOMESERVER, VITE_REGISTRATION_TOKEN } = import.meta.env;

function truthyStringOrNull(value: unknown) {
if (typeof value === "string" && value) {
Expand All @@ -13,6 +14,7 @@ function truthyStringOrNull(value: unknown) {

const config: WormgineClientConfiguration = {
defaultHomeserver: truthyStringOrNull(VITE_DEFAULT_HOMESERVER),
registrationToken: truthyStringOrNull(VITE_REGISTRATION_TOKEN),
};

export default config;
94 changes: 93 additions & 1 deletion src/frontend/components/menus/online-play.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import styles from "./online-play.module.css";
import { useObservableEagerState } from "observable-hooks";
import Logger from "../../../log";
import { DefaultWeaponSchema } from "../../../weapons/schema";
import { getLocalTeamsHook } from "../../../settings";

interface Props {
client: NetGameClient | undefined;
Expand All @@ -29,6 +30,7 @@ function LoggedInView({
}) {
const [displayname, setDisplayName] = useState<string>();
const [authenticatedAvatarBlob, setAvatarBlobUrl] = useState<string>();
const [localTeams] = getLocalTeamsHook();

const createGameCallback = useCallback(() => {
client
Expand Down Expand Up @@ -98,18 +100,94 @@ function LoggedInView({
You may press the button below to create a lobby. To join a lobby, use
a URL provided by the host.
</p>
<button onClick={createGameCallback}>Create Lobby</button>
<p>
{localTeams?.length === 0 ? (
<strong>
You must have at least one local team before you can create a
lobby.
</strong>
) : null}
</p>
<button onClick={createGameCallback} disabled={!localTeams?.length}>
Create Lobby
</button>
</section>
</>
);
}

function RegistrationForm({
setClientConfig,
onBack,
}: {
setClientConfig: Props["setClientConfig"];
onBack: () => void;
}) {
const [loginInProgress, setLoginInProgress] = useState(false);
const [error, setError] = useState<string>();
const onSubmit = useCallback(
async (e: SubmitEvent) => {
e.preventDefault();
if (!config.defaultHomeserver || !config.registrationToken) {
return;
}
try {
setLoginInProgress(true);
const target = e.target as HTMLFormElement;
const username = (
target.elements.namedItem("username") as HTMLInputElement
).value;
const password = (
target.elements.namedItem("password") as HTMLInputElement
).value;
const { accessToken } = await NetGameClient.register(
config.defaultHomeserver,
config.registrationToken,
username,
password,
);
setClientConfig({
accessToken,
baseUrl: config.defaultHomeserver,
});
} catch (ex) {
setError((ex as Error).toString());
} finally {
setLoginInProgress(false);
}
},
[setClientConfig],
);

return (
<section>
<p>
You may log into an existing account below by specifying a username and
password. Future versions will allow you to create an account / login to
other servers.
</p>
{error && (
<p>
Error: <span>{error}</span>
</p>
)}
<form disabled={loginInProgress} onSubmit={onSubmit}>
<input type="text" placeholder="username" id="username"></input>
<input type="password" placeholder="password" id="password"></input>
<button type="submit">Register</button>
</form>
<button onClick={onBack}>Back to login</button>
</section>
);
}

function LoginForm({
setClientConfig,
}: {
setClientConfig: Props["setClientConfig"];
}) {
const [loginInProgress, setLoginInProgress] = useState(false);
const [showRegForm, setShowRegForm] = useState(false);
const [error, setError] = useState<string>();
const onSubmit = useCallback(
async (e: SubmitEvent) => {
Expand Down Expand Up @@ -144,6 +222,15 @@ function LoginForm({
[setClientConfig],
);

if (showRegForm) {
return (
<RegistrationForm
setClientConfig={setClientConfig}
onBack={() => setShowRegForm(false)}
></RegistrationForm>
);
}

return (
<section>
<p>
Expand All @@ -161,6 +248,11 @@ function LoginForm({
<input type="password" placeholder="password" id="password"></input>
<button type="submit">Login</button>
</form>
{config.registrationToken && (
<button onClick={() => setShowRegForm(true)}>
Register new account
</button>
)}
</section>
);
}
Expand Down
64 changes: 55 additions & 9 deletions src/net/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -282,21 +282,23 @@ export class NetGameClient extends EventEmitter {
localStorage.removeItem(WORMGINE_STORAGE_KEY_CLIENT_CONFIG);
}

public static async register(
public static async login(
homeserverUrl: string,
name: string,
username: string,
password: string,
) {
): Promise<{ accessToken: string }> {
const client = createClient({
baseUrl: homeserverUrl,
fetchFn: (input, init) => globalThis.fetch(input, init),
store: new MemoryStore({ localStorage: window.localStorage }),
});
return await client.register(name, password, null, {
type: "m.login.password",
});
const response = await client.loginWithPassword(username, password);
return { accessToken: response.access_token };
}
public static async login(

public static async register(
homeserverUrl: string,
registrationToken: string,
username: string,
password: string,
): Promise<{ accessToken: string }> {
Expand All @@ -305,8 +307,52 @@ export class NetGameClient extends EventEmitter {
fetchFn: (input, init) => globalThis.fetch(input, init),
store: new MemoryStore({ localStorage: window.localStorage }),
});
const response = await client.loginWithPassword(username, password);
return { accessToken: response.access_token };
const params: { session: string; flows: { stages: string[] }[] } =
await client.registerRequest({}).catch((ex) => ex.data);
if (
!params.flows.some((s) => s.stages[0] === "m.login.registration_token")
) {
throw Error(
"Cannot register on this host. Registration token support is not enabled",
);
}
try {
const response = await client.register(
username,
password,
params.session,
{
type: "m.login.registration_token",
token: registrationToken,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} as any,
);
if (!response.access_token) {
throw Error("Unexpected response");
}
return { accessToken: response.access_token };
} catch (ex) {
if (
(ex as MatrixError).data.completed?.includes(
"m.login.registration_token",
)
) {
const response = await client.register(
username,
password,
params.session,
{
type: "m.login.dummy",
},
);
if (!response.access_token) {
throw Error("Unexpected response");
}
return { accessToken: response.access_token };
} else {
throw ex;
}
}
}

constructor(config: NetClientConfig) {
Expand Down
4 changes: 4 additions & 0 deletions src/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ export function getGameSettings(): GameSettings {
};
}

export function getLocalTeamsHook() {
return useLocalStorageState<StoredTeam[]>(WORMGINE_STORAGE_KEY_TEAMS);
}

export function getLocalTeams(): StoredTeam[] {
const item = localStorage.getItem(WORMGINE_STORAGE_KEY_TEAMS);
if (!item) {
Expand Down

0 comments on commit 9ce475d

Please sign in to comment.