Skip to content

Commit

Permalink
Merge pull request #17 from halimath/feature/player_reload
Browse files Browse the repository at this point in the history
feature: player reload
  • Loading branch information
halimath authored May 5, 2024
2 parents 9b756aa + 4dc4377 commit 5a27abf
Show file tree
Hide file tree
Showing 16 changed files with 401 additions and 166 deletions.
96 changes: 30 additions & 66 deletions app/src/api/index.ts
Original file line number Diff line number Diff line change
@@ -1,38 +1,9 @@
import * as wecco from "@weccoframework/core"
import { ApiClient, Session as SessionDto } from "../../generated"
import { Message, ReplaceScene } from "../control"
import { Aspect, Gamemaster, Player, PlayerCharacter, Session } from "../models"

abstract class ApiBase {
private readonly interval: number

protected constructor(
protected readonly emit: wecco.MessageEmitter<Message>,
protected readonly apiClient: ApiClient,
readonly sessionId: string,
private readonly modelCtor: new (table: Session) => Gamemaster | PlayerCharacter,
protected readonly characterId?: string,
) {
this.requestUpdate()
this.interval = setInterval(this.requestUpdate.bind(this), 1000)
}

close() {
clearInterval(this.interval)
}

protected async requestUpdate() {
const session = await this.apiClient.session.getSession({
id: this.sessionId,
})

this.emit(new ReplaceScene(new this.modelCtor(convertTable(session, this.characterId))))
}
}
import { Aspect, Player, Session } from "../models"

const AuthTokenSessionStorageKey = "auth-token"

async function createApiClient(): Promise<ApiClient> {
export async function createApiClient(): Promise<ApiClient> {
let authToken = sessionStorage.getItem(AuthTokenSessionStorageKey)
let apiClient: ApiClient

Expand Down Expand Up @@ -60,31 +31,27 @@ async function createApiClient(): Promise<ApiClient> {
})
}

export class GamemasterApi extends ApiBase {
static async createSession(emit: wecco.MessageEmitter<Message>, title: string): Promise<GamemasterApi> {
export class GamemasterApi {
static async createSession(title: string): Promise<GamemasterApi> {
const apiClient = await createApiClient()

const sessionId = await apiClient.session.createSession({
requestBody: {
title: title,
}
})

return new GamemasterApi(emit, apiClient, sessionId)
return new GamemasterApi(apiClient, sessionId)
}

static async joinSession(emit: wecco.MessageEmitter<Message>, sessionId: string): Promise<GamemasterApi> {
const apiClient = await createApiClient()

return new GamemasterApi(emit, apiClient, sessionId)
}
constructor(
private readonly apiClient: ApiClient,
readonly sessionId: string,
) {}

private constructor(
emit: wecco.MessageEmitter<Message>,
unauthorizedApiClient: ApiClient,
sessionId: string,
) {
super(emit, unauthorizedApiClient, sessionId, Gamemaster)
async getSession(): Promise<Session> {
let dto = await this.apiClient.session.getSession({ id: this.sessionId })
return convertTable(dto)
}

async updateFatePoints(characterId: string, delta: number) {
Expand All @@ -95,7 +62,6 @@ export class GamemasterApi extends ApiBase {
fatePointsDelta: delta,
}
})
this.requestUpdate()
}

async addAspect(name: string, playerId?: string) {
Expand All @@ -115,41 +81,40 @@ export class GamemasterApi extends ApiBase {
name: name,
}
})
}

this.requestUpdate()
}
}

async removeAspect(id: string) {
await this.apiClient.session.deleteAspect({
id: this.sessionId,
aspectId: id,
})
this.requestUpdate()
})
}
}

export class PlayerCharacterApi extends ApiBase {
static async joinGame(emit: wecco.MessageEmitter<Message>, id: string, name: string): Promise<PlayerCharacterApi> {
export class PlayerCharacterApi {
static async joinGame(id: string, name: string): Promise<PlayerCharacterApi> {
const apiClient = await createApiClient()

const characterId = await apiClient.session.joinSession({
id: id,
requestBody: {
name: name,
}
})

return new PlayerCharacterApi(emit, apiClient, id, characterId)
return new PlayerCharacterApi(apiClient, id, characterId)
}

constructor(
private readonly apiClient: ApiClient,
readonly sessionId: string,
readonly characterId: string,
) {}

private constructor(
emit: wecco.MessageEmitter<Message>,
apiClient: ApiClient,
sessionId: string,
characterId: string,
) {
super(emit, apiClient, sessionId, PlayerCharacter, characterId)
async getSession(): Promise<Session> {
let dto = await this.apiClient.session.getSession({ id: this.sessionId })
return convertTable(dto, this.characterId)
}

async spendFatePoint() {
Expand All @@ -159,8 +124,7 @@ export class PlayerCharacterApi extends ApiBase {
requestBody: {
fatePointsDelta: -1,
}
})
this.requestUpdate()
})
}
}

Expand Down
96 changes: 78 additions & 18 deletions app/src/control/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as wecco from "@weccoframework/core"
import { GamemasterApi, PlayerCharacterApi } from "../api"
import { Home, Model, Notification, Scene } from "../models"
import { GamemasterApi, PlayerCharacterApi, createApiClient } from "../api"
import { GamemasterScene, HomeScene, Model, Notification, PlayerCharacterScene, Scene } from "../models"
import { m } from "../utils/i18n"

export class ReplaceScene {
Expand All @@ -25,14 +25,14 @@ export class NewSession {
constructor(public readonly title: string) { }
}

export class RejoinSession {
readonly command = "rejoin-session"
export class Rejoin {
readonly command = "rejoin"

constructor(public readonly sessionId: string) { }
}

export class JoinCharacter {
readonly command = "join-character"
export class JoinAsPlayer {
readonly command = "join-as-player"

constructor(public readonly id: string, public readonly name: string) { }
}
Expand Down Expand Up @@ -66,8 +66,8 @@ export class SessionClosed {
export type Message = ReplaceScene |
PostNotification |
NewSession |
RejoinSession |
JoinCharacter |
Rejoin |
JoinAsPlayer |
UpdatePlayerFatePoints |
SpendFatePoint |
AddAspect |
Expand All @@ -77,7 +77,38 @@ export type Message = ReplaceScene |
export class Controller {
private api: GamemasterApi | PlayerCharacterApi | null = null

async update({model, message, emit}: wecco.UpdaterContext<Model, Message>): Promise<Model | typeof wecco.NoModelChange> {
private updateInterval: number | null = null

private clearUpdate() {
if (this.updateInterval !== null) {
clearInterval(this.updateInterval)
}
}

private scheduleUpdates(emit: wecco.MessageEmitter<Message>) {
const requestAndEmitUpdate = async () => {
if (!this.api) {
return
}

const session = await this.api.getSession()

let scene: Scene

if (this.api instanceof GamemasterApi) {
scene = new GamemasterScene(session)
} else {
scene = new PlayerCharacterScene(session)
}

emit(new ReplaceScene(scene))
}

this.updateInterval = setInterval(requestAndEmitUpdate, 1000)
requestAndEmitUpdate()
}

async update({ model, message, emit }: wecco.UpdaterContext<Model, Message>): Promise<Model | typeof wecco.NoModelChange> {
switch (message.command) {
case "replace-scene":
return new Model(model.versionInfo, message.scene)
Expand All @@ -86,24 +117,34 @@ export class Controller {
return new Model(model.versionInfo, model.scene, ...message.notifications)

case "session-closed":
return new Model(model.versionInfo, new Home(), new Notification(m("tableClosed.message")))
return new Model(model.versionInfo, new HomeScene(), new Notification(m("tableClosed.message")))

case "new-session":
this.api?.close()
this.api = await GamemasterApi.createSession(emit, message.title)
this.clearUpdate()
this.api = await GamemasterApi.createSession(message.title)
history.pushState(null, "", `/session/${this.api.sessionId}`)
this.scheduleUpdates(emit)
break

case "rejoin-session":
this.api?.close()
this.api = await GamemasterApi.joinSession(emit, message.sessionId)
case "join-as-player":
this.clearUpdate()
this.api = await PlayerCharacterApi.joinGame(message.id, message.name)
history.pushState(null, "", `/session/${this.api.sessionId}`)
this.scheduleUpdates(emit)
break

case "join-character":
this.api?.close()
this.api = await PlayerCharacterApi.joinGame(emit, message.id, message.name)
case "rejoin":
this.clearUpdate()
try {
this.api = await rejoinSession(message.sessionId)
this.scheduleUpdates(emit)
} catch {
document.location.pathname = ""
return new Model(model.versionInfo, new HomeScene())
}
break


case "update-fate-points":
if (this.api instanceof GamemasterApi) {
this.api.updateFatePoints(message.playerId, message.fatePoints)
Expand Down Expand Up @@ -132,3 +173,22 @@ export class Controller {
return model.notifications.length === 0 ? wecco.NoModelChange : model.pruneNotifications()
}
}


async function rejoinSession (sessionId: string): Promise<GamemasterApi | PlayerCharacterApi> {
let apiClient = await createApiClient()

let [authInfo, session] = await Promise.all([
apiClient.authorization.getAuthenticationInfo(),
apiClient.session.getSession({ id: sessionId }),
])

if (authInfo.userId == session.ownerId) {
return new GamemasterApi(apiClient, sessionId)
} else {
console.log(session)
let characterId = session.characters.find(c => c.ownerId == authInfo.userId)?.id
console.log(characterId)
return new PlayerCharacterApi(apiClient, sessionId, characterId!)
}
}
16 changes: 8 additions & 8 deletions app/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import * as wecco from "@weccoframework/core"
import "material-icons/iconfont/material-icons.css"
import "roboto-fontface/css/roboto/roboto-fontface.css"
import { Controller, RejoinSession, ReplaceScene, Message } from "./control"
import { Controller, Message, Rejoin, ReplaceScene } from "./control"
import "./index.css"
import { Aspect, Gamemaster, Home, Model, Player, PlayerCharacter, Session, VersionInfo } from "./models"
import { Aspect, GamemasterScene, HomeScene, Model, Player, PlayerCharacterScene, Session, VersionInfo } from "./models"
import { load } from "./utils/i18n"
import { root } from "./views"

Expand All @@ -16,21 +16,21 @@ document.addEventListener("DOMContentLoaded", async () => {

const controller = new Controller()

const app = wecco.createApp<Model, Message>(() => new Model(versionInfo, new Home()), controller.update.bind(controller), root).mount("#app")
const app = wecco.createApp<Model, Message>(() => new Model(versionInfo, new HomeScene()), controller.update.bind(controller), root).mount("#app")

if (document.location.pathname.startsWith("/join/")) {
const sessionId = document.location.pathname.replace("/join/", "")
document.location.hash = ""
app.emit(new ReplaceScene(new Home(sessionId)))
app.emit(new ReplaceScene(new HomeScene(sessionId)))

} else if (document.location.pathname.startsWith("/session/")) {
} else if (document.location.pathname.startsWith("/session/")) {
const sessionId = document.location.pathname.replace("/session/", "")
app.emit(new RejoinSession(sessionId))
app.emit(new Rejoin(sessionId))

// The following branches serve as an easy way to "view" a scene in dev mode.
// TODO: Replace this with https://storybook.js.org/ or something similar.
} else if (document.location.hash.startsWith("#dev/gamemaster")) {
app.emit(new ReplaceScene(new Gamemaster(new Session(
app.emit(new ReplaceScene(new GamemasterScene(new Session(
"1",
"Test Table",
"1",
Expand All @@ -53,7 +53,7 @@ document.addEventListener("DOMContentLoaded", async () => {
]
))))
} else if (document.location.hash.startsWith("#dev/player")) {
app.emit(new ReplaceScene(new PlayerCharacter(new Session(
app.emit(new ReplaceScene(new PlayerCharacterScene(new Session(
"1",
"Test Table",
"1",
Expand Down
8 changes: 4 additions & 4 deletions app/src/models/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export class Session {
}
}

export class PlayerCharacter {
export class PlayerCharacterScene {
constructor(
public readonly session: Session,
) { }
Expand All @@ -49,17 +49,17 @@ export class PlayerCharacter {
}
}

export class Gamemaster {
export class GamemasterScene {
constructor(
public readonly session: Session,
) { }
}

export class Home {
export class HomeScene {
constructor(public readonly joinSessionId?: string) { }
}

export type Scene = Home | PlayerCharacter | Gamemaster
export type Scene = HomeScene | PlayerCharacterScene | GamemasterScene

export interface VersionInfo {
version: string
Expand Down
Loading

0 comments on commit 5a27abf

Please sign in to comment.