MonoRepo for publishing a combo of packages and applications aimed at web based multiplayer game development
Express for the API/Game Cooridinator/File Server. Games MUST register themsevlves with it
A Lit web component base for the frontend find/join game. Games SHOULD register themselves with it (TBD).
Used by the frontend and games, potentially integratable into other things like stream overlays
Lit Webcomponents used to build the frontend and other things like it, provided for stream integration as well as reuse in both the games and frontend
Shared functionality and types that are only intended to be consumed by the games/frontend/api. This MUST only contain code that is applicable in both dom and server contentexts.
Shared server side only functionality for server and games. This lib also includes express, so MUST never be served up to the frontend.
Publicly shared functionality and types that are intended to be consumed by the internal applications as well as external applications (eg custom OBS integrations)
Games should be implemented as a set of packages/artifacts that live inside of the /games/
root directory. These directories should contain a set of packages that implement (at a minimum) the game frontend artifact and the server route registration to serve those artifacts. They MAY also include packages to augment the API client (TBD) and Frontend (TBD), internally shared logic (eg for multiplayer server logic), and externally shared code/types/etc.
Package Naming Convention: @shieldmaidengames/${gameName}-game
This is the frontend "game" code. This can be whatever engine, but needs to have everything bundled into an index.html entry point that can be iframed in. The main frontend MUST supply any needed config to the game via an iframe property or callback (TBD).
This entry point must be made available via an artifacts:copy:package
yarn command in the game's package.json
, and the game's project.json
(nx) definition MUST include the artifact
and game
tags. The copy command MUST copy all required files to the server's artifact path (`/apps/server/artifacts/${gameName}).
Package Naming Convention: @shieldmaidengames/${gameName}-server
This is the package responsible for registering the game's routing with the server. For multiplayer games where a websocket server is started for the instance, that code SHOULD also be exposed here (TBD).
The server code MUST expose a function that registers a route factory via the GameServerModuleRouter
token defined in the core @shieldmaidengames/webgames-internal-server-shared
package. These factories WILL BE supplied the artifact root directory so they may properly build the router, which will be consumed by the core server.
- Request/Response
- Reserved for API interactions so as to not cause confusion between DOM/EventBridge events and API interactions
- Examples:
FindGameRequest
andFindGameResponse
for "Hey API find games that match these criteria" and "Here are matching games"
- Cue/Event
- Reserved for DOM and Game Events.
*Cue
is a request for something to happen,*Event
is a notification that something is actually happening - Event naming can also include the Before/During/After naming triad, e.g.
BeforePlayerJoinedEvent
,PlayerJoinedEvent
,AfterPlayerJoinedEvent
. - Examples:
FindGameCue
for "Please load the find game dom interface"PlayerDiedEvent
for "Hey just letting you know, this player died"
- Reserved for DOM and Game Events.
- Message
- Reserved for EventBridge events and Websocket messages. This are distinguised from DOM events as they are likely not actually derived from the
Event
base class. - EventBridge messages should be reserved for communication between systems. Shared information, such as the list of current players, that is needed in multiple elements or sections of the application should be supplied via contexts.
- Reserved for EventBridge events and Websocket messages. This are distinguised from DOM events as they are likely not actually derived from the
- API: (Request/Response)
- Long lived or multi-subject data such as game outcomes, player registration, or find game requests.
- All of these things are not subject to moment by moment flux, and should likely live in the data store.
- An useful illustration of the difference would be "Game Move made" which is a transient event and "Game outcomes including a list of all game moves made", which is a record of a full game and not subject to moment by moment changes.
- DOM: (Cue/Event)
- Requests for changes in DOM related UI state or notification of UI changes/interactions
- All Cues and Events should have a static string property
EVENT_NAME
that is used to contain and reference the event name. - Examples:
- User clicked on the start game button ->
StartGameClickEvent
() - "Timer ran out" ->
GameTimerExpiredEvent
- "Hey I'm a web component and I need my dependencies" ->
InjectionCue
- User clicked on the start game button ->
- Game: (Cue/Event)
- Requests for change in game state, or notification of a game state change
- All Cues and Events should have a static string property
EVENT_NAME
that is used to contain and reference the event name. - Notably, these may be issued through the standard DOM event pathway, but are not required to do so
- Examples:
- Player died ->
PlayerDiedEvent
- All players have affirmed (probably through a button emitting a
StartGameClickEvent
) that the game is ready to start ->BeforeStartGameEvent
,StartGameEvent
,AfterStartGameEvent
triad.
- Player died ->
- Event Bridge: (Message)
- Reserved for communication between systems, these should be used for summary or overall notification, and should not be assumed to go to a single target as multiple consumers can register to receive these messages.
- Messages must extend the
BridgeMessage
type, must strictly define the.messageName
property as one potential string OR one of a set of strings, subject data must be contained in the.messageData
property, and new message type definitions should strictly define the shape of the data. Implemented.messageName
properties should try to follow reverse domain name notation if possible. Even if the event bridge implentation does not currently allow for subscribing at different levels of granularity, it would be good to future proof in this way. - Usage examples:
- Messages between Service/Shared workers and their respective tabs
- Messages between seperated and decoupled sections of the same application.
- Examples:
- "Game goes from in progress to complete" ->
GameStateChangeMessage
{ messageName: "game.state.change.completed", sentTime: Date.now(), messageData: { priorState: GameState.InProgress, newState: GameState.Completed, gameSummary: GameSummary, gameLog: GameLog }, }
- "Game goes from in progress to complete" ->